114 discretization_->selected_geometry().update_dof_handler(*dof_handler_cg_);
115 discretization_->selected_geometry().update_dof_handler(*dof_handler_dg_);
117 dof_handler_cg_->distribute_dofs(discretization_->finite_element_cg());
118 dof_handler_dg_->distribute_dofs(discretization_->finite_element_dg());
122 template <
int dim,
typename Number>
125 auto &dof_handler = this->dof_handler();
126 const IndexSet &locally_owned = dof_handler.locally_owned_dofs();
127 n_locally_owned_ = locally_owned.n_elements();
133 DoFRenumbering::Cuthill_McKee(dof_handler);
144 mpi_ensemble_.ensemble_communicator(),
155 create_constraints_and_sparsity_pattern();
157 dof_handler, sparsity_pattern_,
warp_size);
172 mpi_ensemble_.ensemble_communicator(),
185 template <
int dim,
typename Number>
186 void OfflineData<dim, Number>::create_constraints_and_sparsity_pattern()
194 const auto populate_affine_constraints =
195 [&](
const auto &dof_handler,
auto &affine_constraints) {
196 const auto locally_relevant =
197 DoFTools::extract_locally_relevant_dofs(dof_handler);
199 const IndexSet &locally_owned = dof_handler.locally_owned_dofs();
200 affine_constraints.reinit(locally_owned, locally_relevant);
201 DoFTools::make_hanging_node_constraints(dof_handler,
209 const auto &periodic_faces =
210 discretization_->triangulation().get_periodic_face_map();
212 for (
const auto &[left, value] : periodic_faces) {
213 const auto &[right, orientation] = value;
215 typename DoFHandler<dim>::cell_iterator dof_cell_left(
216 &left.first->get_triangulation(),
221 typename DoFHandler<dim>::cell_iterator dof_cell_right(
222 &right.first->get_triangulation(),
223 right.first->level(),
224 right.first->index(),
227 if constexpr (std::is_same_v<Number, double>) {
228 DoFTools::make_periodicity_constraints(
229 dof_cell_left->face(left.second),
230 dof_cell_right->face(right.second),
235 AssertThrow(
false, dealii::ExcNotImplemented());
240 affine_constraints.close();
245 const std::vector<IndexSet> &locally_owned_dofs =
246 Utilities::MPI::all_gather(
247 mpi_ensemble_.ensemble_communicator(),
248 dof_handler.locally_owned_dofs());
249 const IndexSet locally_active =
250 dealii::DoFTools::extract_locally_active_dofs(dof_handler);
251 Assert(affine_constraints.is_consistent_in_parallel(
254 mpi_ensemble_.ensemble_communicator(),
261 populate_affine_constraints(*dof_handler_cg_, affine_constraints_cg_);
263 populate_affine_constraints(*dof_handler_dg_, affine_constraints_dg_);
269 const auto &dof_handler = this->dof_handler();
270 const auto &affine_constraints = this->affine_constraints();
271 const IndexSet &locally_owned = dof_handler.locally_owned_dofs();
272 Assert(n_locally_owned_ == locally_owned.n_elements(),
273 dealii::ExcInternalError());
275 const auto locally_relevant =
276 DoFTools::extract_locally_relevant_dofs(dof_handler);
278 sparsity_pattern_.reinit(
279 dof_handler.n_dofs(), dof_handler.n_dofs(), locally_relevant);
281 if (discretization_->have_discontinuous_ansatz()) {
286 dof_handler, sparsity_pattern_, affine_constraints,
false);
291 DoFTools::make_sparsity_pattern(
292 dof_handler, sparsity_pattern_, affine_constraints,
false);
301 SparsityTools::distribute_sparsity_pattern(
304 mpi_ensemble_.ensemble_communicator(),
313 template <
int dim,
typename Number>
314 void OfflineData<dim, Number>::ensure_simd_stride_consistency()
316 auto &dof_handler = this->dof_handler();
322 const auto consistent_stride_range [[maybe_unused]] = [&]() {
323 const IndexSet &locally_owned = dof_handler.locally_owned_dofs();
324 const auto offset = n_locally_owned_ != 0 ? *locally_owned.begin() : 0;
326 unsigned int warp_row_length = 0;
328 for (; i < n_locally_internal_; ++i) {
330 warp_row_length = sparsity_pattern_.row_length(offset + i);
332 if (warp_row_length != sparsity_pattern_.row_length(offset + i)) {
343 const auto mpi_allreduce_logical_or = [&](
const bool local_value) {
344 std::function<bool(
const bool &,
const bool &)> comparator =
345 [](
const bool &left,
const bool &right) ->
bool {
346 return left || right;
348 return Utilities::MPI::all_reduce(
349 local_value, mpi_ensemble_.ensemble_communicator(), comparator);
360 const auto &affine_constraints = this->affine_constraints();
361 if (mpi_allreduce_logical_or(affine_constraints.n_constraints() > 0)) {
362 if (mpi_allreduce_logical_or(
363 consistent_stride_range() != n_locally_internal_)) {
370 dof_handler, sparsity_pattern_, n_locally_internal_,
warp_size);
371 create_constraints_and_sparsity_pattern();
372 n_locally_internal_ = consistent_stride_range();
381 Assert(consistent_stride_range() == n_locally_internal_,
382 dealii::ExcInternalError());
394 template <
int dim,
typename Number>
395 void OfflineData<dim, Number>::create_partitioner_and_simd_sparsity(
396 const unsigned int problem_dimension,
397 const unsigned int n_precomputed_values)
399 const auto &dof_handler = this->dof_handler();
400 const auto &affine_constraints = this->affine_constraints();
401 const IndexSet &locally_owned = dof_handler.locally_owned_dofs();
402 Assert(n_locally_owned_ == locally_owned.n_elements(),
403 dealii::ExcInternalError());
405 auto locally_relevant =
406 DoFTools::extract_locally_relevant_dofs(dof_handler);
409 IndexSet additional_dofs(dof_handler.n_dofs());
410 for (
auto &entry : sparsity_pattern_)
411 if (!locally_relevant.is_element(entry.column())) {
412 Assert(locally_owned.is_element(entry.row()), ExcInternalError());
413 additional_dofs.add_index(entry.column());
415 additional_dofs.compress();
416 locally_relevant.add_indices(additional_dofs);
417 locally_relevant.compress();
420 n_locally_relevant_ = locally_relevant.n_elements();
422 scalar_partitioner_ = std::make_shared<dealii::Utilities::MPI::Partitioner>(
423 locally_owned, locally_relevant, mpi_ensemble_.ensemble_communicator());
426 scalar_partitioner_, problem_dimension);
429 scalar_partitioner_, n_precomputed_values);
434 const auto mpi_allreduce_logical_or = [&](
const bool local_value) {
435 std::function<bool(
const bool &,
const bool &)> comparator =
436 [](
const bool &left,
const bool &right) ->
bool {
437 return left || right;
439 return Utilities::MPI::all_reduce(
440 local_value, mpi_ensemble_.ensemble_communicator(), comparator);
449 if (mpi_allreduce_logical_or(affine_constraints.n_constraints() > 0)) {
453 n_export_indices_ = 0;
454 for (
const auto &it : scalar_partitioner_->import_indices())
455 if (it.second <= n_locally_internal_)
456 n_export_indices_ = std::
max(n_export_indices_, it.second);
464 unsigned int control = 0;
465 for (
const auto &it : scalar_partitioner_->import_indices())
466 if (it.second <= n_locally_internal_)
467 control = std::
max(control, it.second);
469 Assert(control <= n_export_indices_, ExcInternalError());
470 Assert(n_export_indices_ <= n_locally_internal_, ExcInternalError());
480 sparsity_pattern_simd_.reinit(n_locally_internal_,
488 template <
int dim,
typename Number>
489 void OfflineData<dim, Number>::create_matrices()
492 std::cout <<
"OfflineData<dim, Number>::create_matrices()" << std::endl;
504 mass_matrix_.reinit(sparsity_pattern_simd_, policy);
505 if (discretization_->have_discontinuous_ansatz())
506 mass_matrix_inverse_.reinit(sparsity_pattern_simd_, policy);
508 lumped_mass_matrix_.reinit_with_scalar_partitioner(scalar_partitioner_,
510 lumped_mass_matrix_inverse_.reinit_with_scalar_partitioner(
511 scalar_partitioner_, policy);
513 betaij_matrix_.reinit(sparsity_pattern_simd_, policy);
514 cij_matrix_.reinit(sparsity_pattern_simd_, policy);
515 if (discretization_->have_discontinuous_ansatz())
516 incidence_matrix_.reinit(sparsity_pattern_simd_, policy);
522 const auto &dof_handler = this->dof_handler();
523 const auto &affine_constraints = this->affine_constraints();
525 measure_of_omega_ = 0.;
528 const auto local_assemble_system = [&](
const auto &cell,
533 auto &is_locally_owned = copy.is_locally_owned_;
534 auto &local_dof_indices = copy.local_dof_indices_;
535 auto &neighbor_local_dof_indices = copy.neighbor_local_dof_indices_;
537 auto &cell_mass_matrix = copy.cell_mass_matrix_;
538 auto &cell_mass_matrix_inverse = copy.cell_mass_matrix_inverse_;
539 auto &cell_betaij_matrix = copy.cell_betaij_matrix_;
540 auto &cell_cij_matrix = copy.cell_cij_matrix_;
541 auto &interface_cij_matrix = copy.interface_cij_matrix_;
542 auto &cell_measure = copy.cell_measure_;
544 auto &hp_fe_values = scratch.hp_fe_values_;
545 auto &hp_fe_face_values = scratch.hp_fe_face_values_;
546 auto &hp_fe_neighbor_face_values = scratch.hp_fe_neighbor_face_values_;
548 is_locally_owned = cell->is_locally_owned();
549 if (!is_locally_owned)
552 const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();
554 cell_mass_matrix.reinit(dofs_per_cell, dofs_per_cell);
555 cell_betaij_matrix.reinit(dofs_per_cell, dofs_per_cell);
556 for (
auto &matrix : cell_cij_matrix)
557 matrix.reinit(dofs_per_cell, dofs_per_cell);
558 if (discretization_->have_discontinuous_ansatz()) {
559 cell_mass_matrix_inverse.reinit(dofs_per_cell, dofs_per_cell);
562 hp_fe_values.reinit(cell);
563 const auto &fe_values = hp_fe_values.get_present_fe_values();
565 local_dof_indices.resize(dofs_per_cell);
566 cell->get_dof_indices(local_dof_indices);
570 cell_mass_matrix = 0.;
571 cell_betaij_matrix = 0.;
572 for (
auto &matrix : cell_cij_matrix)
574 if (discretization_->have_discontinuous_ansatz()) {
575 cell_mass_matrix_inverse = 0.;
579 for (
unsigned int q : fe_values.quadrature_point_indices()) {
580 const auto JxW = fe_values.JxW(q);
583 if (dofs_per_cell != 0 && cell->is_locally_owned())
584 cell_measure += Number(JxW);
586 for (
unsigned int j : fe_values.dof_indices()) {
587 const auto value_JxW = fe_values.shape_value(j, q) * JxW;
588 const auto grad_JxW = fe_values.shape_grad(j, q) * JxW;
590 for (
unsigned int i : fe_values.dof_indices()) {
591 const auto value = fe_values.shape_value(i, q);
592 const auto grad = fe_values.shape_grad(i, q);
594 cell_mass_matrix(i, j) += Number(value * value_JxW);
595 cell_betaij_matrix(i, j) += Number(grad * grad_JxW);
596 for (
unsigned int d = 0; d < dim; ++d)
597 cell_cij_matrix[d](i, j) += Number((value * grad_JxW)[d]);
607 if (!discretization_->have_discontinuous_ansatz())
610 for (
const auto f_index : cell->face_indices()) {
611 const auto &face = cell->face(f_index);
614 const bool has_neighbor =
615 !face->at_boundary() || cell->has_periodic_neighbor(f_index);
619 neighbor_local_dof_indices[f_index].resize(0);
624 const auto neighbor_cell = cell->neighbor_or_periodic_neighbor(f_index);
625 if (neighbor_cell->is_artificial()) {
628 neighbor_local_dof_indices[f_index].resize(0);
633 const bool neighbor_cell_has_fe_nothing =
634 treat_fe_nothing_as_boundary_ &&
635 (
dynamic_cast<const dealii::FE_Nothing<dim> *
>(
636 &neighbor_cell->get_fe()) !=
nullptr);
637 if (neighbor_cell_has_fe_nothing) {
640 neighbor_local_dof_indices[f_index].resize(0);
644 hp_fe_face_values.reinit(cell, f_index);
645 const auto &fe_face_values = hp_fe_face_values.get_present_fe_values();
649 for (
unsigned int q : fe_face_values.quadrature_point_indices()) {
650 const auto JxW = fe_face_values.JxW(q);
651 const auto &normal = fe_face_values.get_normal_vectors()[q];
653 for (
unsigned int j : fe_face_values.dof_indices()) {
654 const auto value_JxW = fe_face_values.shape_value(j, q) * JxW;
656 for (
unsigned int i : fe_face_values.dof_indices()) {
657 const auto value = fe_face_values.shape_value(i, q);
659 for (
unsigned int d = 0; d < dim; ++d)
660 cell_cij_matrix[d](i, j) -=
661 Number(0.5 * normal[d] * value * value_JxW);
668 const unsigned int f_index_neighbor =
669 cell->has_periodic_neighbor(f_index)
670 ? cell->periodic_neighbor_of_periodic_neighbor(f_index)
671 : cell->neighbor_of_neighbor(f_index);
673 const unsigned int neighbor_dofs_per_cell =
674 neighbor_cell->get_fe().n_dofs_per_cell();
675 neighbor_local_dof_indices[f_index].resize(neighbor_dofs_per_cell);
676 neighbor_cell->get_dof_indices(neighbor_local_dof_indices[f_index]);
678 for (
unsigned int k = 0; k < dim; ++k) {
679 interface_cij_matrix[f_index][k].reinit(dofs_per_cell,
680 neighbor_dofs_per_cell);
681 interface_cij_matrix[f_index][k] = 0.;
684 hp_fe_neighbor_face_values.reinit(neighbor_cell, f_index_neighbor);
685 const auto &fe_neighbor_face_values =
686 hp_fe_neighbor_face_values.get_present_fe_values();
688 for (
unsigned int q : fe_face_values.quadrature_point_indices()) {
689 const auto JxW = fe_face_values.JxW(q);
690 const auto &normal = fe_face_values.get_normal_vectors()[q];
693 for (
unsigned int j : fe_neighbor_face_values.dof_indices()) {
694 const auto value_JxW =
695 fe_neighbor_face_values.shape_value(j, q) * JxW;
697 for (
unsigned int i : fe_face_values.dof_indices()) {
698 const auto value = fe_face_values.shape_value(i, q);
700 for (
unsigned int d = 0; d < dim; ++d)
701 interface_cij_matrix[f_index][d](i, j) +=
702 Number(0.5 * normal[d] * value * value_JxW);
712 if (discretization_->have_discontinuous_ansatz()) {
714 if (!cell_mass_matrix_inverse.empty())
715 cell_mass_matrix_inverse.invert(cell_mass_matrix);
719 const auto copy_local_to_global = [&](
const auto ©) {
720 const auto &is_locally_owned = copy.is_locally_owned_;
721 const auto &dof_indices = copy.local_dof_indices_;
722 const auto &neighbor_dof_indices = copy.neighbor_local_dof_indices_;
723 const auto &cell_mass_matrix = copy.cell_mass_matrix_;
724 const auto &cell_mass_matrix_inverse = copy.cell_mass_matrix_inverse_;
725 const auto &cell_cij_matrix = copy.cell_cij_matrix_;
726 const auto &interface_cij_matrix = copy.interface_cij_matrix_;
727 const auto &cell_betaij_matrix = copy.cell_betaij_matrix_;
728 const auto &cell_measure = copy.cell_measure_;
730 if (!is_locally_owned)
734 cell_mass_matrix, dof_indices, affine_constraints, mass_matrix_);
737 cell_cij_matrix, dof_indices, affine_constraints, cij_matrix_);
743 if (dof_indices.size() != 0) {
744 for (
unsigned int f_index = 0; f_index < copy.n_faces; ++f_index) {
745 if (neighbor_dof_indices[f_index].size() != 0) {
748 neighbor_dof_indices[f_index],
756 cell_betaij_matrix, dof_indices, affine_constraints, betaij_matrix_);
758 if (discretization_->have_discontinuous_ansatz())
762 mass_matrix_inverse_);
764 measure_of_omega_ += cell_measure;
767 WorkStream::run(dof_handler.begin_active(),
769 local_assemble_system,
770 copy_local_to_global,
771 AssemblyScratchData<dim>(*discretization_),
772 AssemblyCopyData<dim, Number>());
774 mass_matrix_.view().compress(VectorOperation::add);
775 mass_matrix_.view().update_ghost_rows();
776 cij_matrix_.view().compress(VectorOperation::add);
777 cij_matrix_.view().update_ghost_rows();
778 betaij_matrix_.view().compress(VectorOperation::add);
779 betaij_matrix_.view().update_ghost_rows();
780 if (discretization_->have_discontinuous_ansatz()) {
781 mass_matrix_inverse_.view().compress(VectorOperation::add);
782 mass_matrix_inverse_.view().update_ghost_rows();
785 measure_of_omega_ = Utilities::MPI::sum(
786 measure_of_omega_, mpi_ensemble_.ensemble_communicator());
793 const auto sparsity_simd_view = sparsity_pattern_simd_.view();
794 const auto mass_matrix_view = mass_matrix_.view();
795 const auto lumped_mass_matrix_view = lumped_mass_matrix_.view();
796 const auto lumped_mass_matrix_inverse_view =
797 lumped_mass_matrix_inverse_.view();
799 const auto body = [&](
auto sentinel,
unsigned int i) {
800 using T =
decltype(sentinel);
801 constexpr unsigned int stride_size = get_stride_size<T>;
804 const unsigned int row_length = sparsity_simd_view.row_length(i);
810 const unsigned int *js = sparsity_simd_view.columns(i);
811 for (
unsigned int col_idx = 0; col_idx < row_length;
812 ++col_idx, js += stride_size) {
814 const auto m_ij = mass_matrix_view.template read_entry<T>(i, col_idx);
818 lumped_mass_matrix_view.template write_entry<T>(m_i, i);
819 lumped_mass_matrix_inverse_view.
820 template write_entry<T>(Number(1.) / m_i, i);
823 cpu_simd_loop<Number>(
"", body, 0, n_locally_internal_, n_locally_owned_);
825 lumped_mass_matrix_view.update_ghost_values();
826 lumped_mass_matrix_inverse_view.update_ghost_values();
833 if (discretization_->have_discontinuous_ansatz()) {
834 const auto lumped_mass_matrix_view = lumped_mass_matrix_.view();
837 const auto local_assemble_system = [&](
const auto &cell,
842 auto &is_locally_owned = copy.is_locally_owned_;
843 auto &local_dof_indices = copy.local_dof_indices_;
844 auto &neighbor_local_dof_indices = copy.neighbor_local_dof_indices_;
845 auto &interface_incidence_matrix = copy.interface_incidence_matrix_;
846 auto &hp_fe_face_values_nodal = scratch.hp_fe_face_values_nodal_;
847 auto &hp_fe_neighbor_face_values_nodal =
848 scratch.hp_fe_neighbor_face_values_nodal_;
850 is_locally_owned = cell->is_locally_owned();
851 if (!is_locally_owned)
854 const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();
856 for (
auto &matrix : interface_incidence_matrix)
857 matrix.reinit(dofs_per_cell, dofs_per_cell);
859 local_dof_indices.resize(dofs_per_cell);
860 cell->get_dof_indices(local_dof_indices);
863 for (
auto &matrix : interface_incidence_matrix)
866 for (
const auto f_index : cell->face_indices()) {
867 const auto &face = cell->face(f_index);
870 const bool has_neighbor =
871 !face->at_boundary() || cell->has_periodic_neighbor(f_index);
875 neighbor_local_dof_indices[f_index].resize(0);
880 const auto neighbor_cell =
881 cell->neighbor_or_periodic_neighbor(f_index);
882 if (neighbor_cell->is_artificial()) {
885 neighbor_local_dof_indices[f_index].resize(0);
890 const bool neighbor_cell_has_fe_nothing =
891 treat_fe_nothing_as_boundary_ &&
892 (
dynamic_cast<const dealii::FE_Nothing<dim> *
>(
893 &neighbor_cell->get_fe()) !=
nullptr);
894 if (neighbor_cell_has_fe_nothing) {
895 neighbor_local_dof_indices[f_index].resize(0);
899 const unsigned int neighbor_dofs_per_cell =
900 neighbor_cell->get_fe().n_dofs_per_cell();
901 neighbor_local_dof_indices[f_index].resize(neighbor_dofs_per_cell);
902 neighbor_cell->get_dof_indices(neighbor_local_dof_indices[f_index]);
904 const unsigned int f_index_neighbor =
905 cell->has_periodic_neighbor(f_index)
906 ? cell->periodic_neighbor_of_periodic_neighbor(f_index)
907 : cell->neighbor_of_neighbor(f_index);
909 hp_fe_face_values_nodal.reinit(cell, f_index);
910 const auto &fe_face_values_nodal =
911 hp_fe_face_values_nodal.get_present_fe_values();
912 hp_fe_neighbor_face_values_nodal.reinit(neighbor_cell,
914 const auto &fe_neighbor_face_values_nodal =
915 hp_fe_neighbor_face_values_nodal.get_present_fe_values();
919 for (
unsigned int q :
920 fe_face_values_nodal.quadrature_point_indices()) {
922 for (
unsigned int j : fe_neighbor_face_values_nodal.dof_indices()) {
923 const auto v_j = fe_neighbor_face_values_nodal.shape_value(j, q);
924 for (
unsigned int i : fe_face_values_nodal.dof_indices()) {
925 const auto v_i = fe_face_values_nodal.shape_value(i, q);
926 constexpr auto eps = std::numeric_limits<Number>::epsilon();
927 if (std::abs(v_i * v_j) > 100. * eps) {
928 const auto &ansatz = discretization_->ansatz();
930 const auto global_i = local_dof_indices[i];
931 const auto global_j = neighbor_local_dof_indices[f_index][j];
933 scalar_partitioner_->global_to_local(global_i);
935 scalar_partitioner_->global_to_local(global_j);
936 const auto m_i = lumped_mass_matrix_view.read_entry(local_i);
937 const auto m_j = lumped_mass_matrix_view.read_entry(local_j);
939 Number(0.5) * (m_i + m_j) / measure_of_omega_;
950 r_ij = std::pow(hd_ij, incidence_relaxation_even_ / dim);
957 r_ij = std::pow(hd_ij, incidence_relaxation_odd_ / dim);
960 interface_incidence_matrix[f_index](i, j) += r_ij;
968 const auto copy_local_to_global = [&](
const auto ©) {
969 const auto &is_locally_owned = copy.is_locally_owned_;
970 const auto &dof_indices = copy.local_dof_indices_;
971 const auto &neighbor_dof_indices = copy.neighbor_local_dof_indices_;
972 const auto &interface_incidence_matrix =
973 copy.interface_incidence_matrix_;
975 if (!is_locally_owned)
982 if (dof_indices.size() != 0) {
983 for (
unsigned int f_index = 0; f_index < copy.n_faces; ++f_index) {
984 if (neighbor_dof_indices[f_index].size() != 0) {
987 neighbor_dof_indices[f_index],
995 WorkStream::run(dof_handler.begin_active(),
997 local_assemble_system,
998 copy_local_to_global,
999 AssemblyScratchData<dim>(*discretization_),
1000 AssemblyCopyData<dim, Number>());
1002 incidence_matrix_.view().compress(VectorOperation::add);
1009 boundary_map_ = construct_boundary_map(
1010 dof_handler.begin_active(), dof_handler.end(), *scalar_partitioner_);
1021 std::vector<unsigned int> boundary_indices;
1022 std::map<unsigned int, unsigned int> position_of_index;
1024 boundary_slots_.clear();
1025 boundary_slots_.reserve(boundary_map_.size());
1027 for (
const auto &entry : boundary_map_) {
1028 const auto index = std::get<0>(entry);
1029 const auto [it, inserted] =
1030 position_of_index.try_emplace(index, boundary_indices.size());
1032 boundary_indices.push_back(index);
1033 boundary_slots_.push_back(it->second);
1036 boundary_indices_.reinit(boundary_indices.size(),
1038 std::copy(boundary_indices.begin(),
1039 boundary_indices.end(),
1040 boundary_indices_.view());
1042 const auto coupling_boundary_pairs = collect_coupling_boundary_pairs(
1043 dof_handler.begin_active(), dof_handler.end(), *scalar_partitioner_);
1045 coupling_boundary_pairs_.reinit(coupling_boundary_pairs.size(),
1047 std::copy(coupling_boundary_pairs.begin(),
1048 coupling_boundary_pairs.end(),
1049 coupling_boundary_pairs_.view());
1052#ifdef DEBUG_SYMMETRY_CHECK
1057 const auto lumped_mass_matrix_view = lumped_mass_matrix_.view();
1059 double total_mass = 0.;
1060 for (
unsigned int i = 0; i < n_locally_owned_; ++i)
1061 total_mass += lumped_mass_matrix_view.read_entry(i);
1063 Utilities::MPI::sum(total_mass, mpi_ensemble_.ensemble_communicator());
1065 Assert(std::abs(measure_of_omega_ - total_mass) <
1066 1.e-12 * measure_of_omega_,
1068 "Total mass differs from the measure of the domain."));
1074 const auto sparsity_simd_view = sparsity_pattern_simd_.view();
1075 const auto mass_matrix_view = mass_matrix_.view();
1076 const auto cij_matrix_view = cij_matrix_.view();
1078 for (
unsigned int i = 0; i < n_locally_owned_; ++i) {
1080 const unsigned int row_length = sparsity_simd_view.row_length(i);
1081 if (row_length == 1)
1084 auto sum = mass_matrix_view.read_entry(i, 0) -
1085 lumped_mass_matrix_view.read_entry(i);
1088 const unsigned int stride_size = sparsity_simd_view.stride_of_row(i);
1089 const unsigned int *js = sparsity_simd_view.columns(i);
1090 for (
unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {
1091 const auto j = js[col_idx * stride_size];
1092 Assert(j < n_locally_relevant_, dealii::ExcInternalError());
1094 const auto m_ij = mass_matrix_view.read_entry(i, col_idx);
1095 if (discretization_->have_discontinuous_ansatz()) {
1098 Assert(std::abs(m_ij) > -1.e-12, dealii::ExcInternalError());
1100 Assert(std::abs(m_ij) > 1.e-12, dealii::ExcInternalError());
1104 const auto m_ji = mass_matrix_view.read_transposed_entry(i, col_idx);
1105 if (std::abs(m_ij - m_ji) >= 1.e-12) {
1107 std::stringstream ss;
1108 ss <<
"m_ij matrix is not symmetric: " << m_ij <<
" <-> " << m_ji;
1109 Assert(
false, dealii::ExcMessage(ss.str()));
1113 Assert(std::abs(sum) < 1.e-12, dealii::ExcInternalError());
1120 for (
unsigned int i = 0; i < n_locally_owned_; ++i) {
1122 const unsigned int row_length = sparsity_simd_view.row_length(i);
1123 if (row_length == 1)
1126 auto sum = cij_matrix_view.read_tensor(i, 0);
1129 const unsigned int stride_size = sparsity_simd_view.stride_of_row(i);
1130 const unsigned int *js = sparsity_simd_view.columns(i);
1131 for (
unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {
1132 const auto j = js[col_idx * stride_size];
1133 Assert(j < n_locally_relevant_, dealii::ExcInternalError());
1135 const auto c_ij = cij_matrix_view.read_tensor(i, col_idx);
1136 Assert(c_ij.norm() > 1.e-12, dealii::ExcInternalError());
1139 const auto c_ji = cij_matrix_view.read_transposed_tensor(i, col_idx);
1140 if ((c_ij + c_ji).norm() >= 1.e-12) {
1144 const CouplingDescription coupling{i, col_idx, j};
1145 const auto *begin = coupling_boundary_pairs_.view();
1146 const auto *end = begin + coupling_boundary_pairs_.size();
1147 if (std::find(begin, end, coupling) == end) {
1148 std::stringstream ss;
1149 ss <<
"c_ij matrix is not anti-symmetric: " << c_ij <<
" <-> "
1151 Assert(
false, dealii::ExcMessage(ss.str()));
1156 Assert(sum.norm() < 1.e-12, dealii::ExcInternalError());
1162 template <
int dim,
typename Number>
1163 void OfflineData<dim, Number>::create_multigrid_data()
1166 std::cout <<
"OfflineData<dim, Number>::create_multigrid_data()"
1170 Assert(!dof_handler_cg_->has_hp_capabilities(), dealii::ExcInternalError());
1171 Assert(!dof_handler_dg_->has_hp_capabilities(), dealii::ExcInternalError());
1173 dof_handler_cg_->distribute_mg_dofs();
1174 dof_handler_dg_->distribute_mg_dofs();
1178 auto &dof_handler = this->dof_handler();
1180 const auto n_levels = dof_handler.get_triangulation().n_global_levels();
1182 AffineConstraints<float> level_constraints;
1185 level_boundary_map_.resize(n_levels);
1186 level_lumped_mass_matrix_.resize(n_levels);
1188 for (
unsigned int level = 0; level < n_levels; ++level) {
1191 const auto relevant_dofs =
1192 dealii::DoFTools::extract_locally_relevant_level_dofs(dof_handler,
1195 const auto partitioner = std::make_shared<Utilities::MPI::Partitioner>(
1196 dof_handler.locally_owned_mg_dofs(level),
1198 mpi_ensemble_.ensemble_communicator());
1199 level_lumped_mass_matrix_[level].reinit(partitioner);
1200 std::vector<types::global_dof_index> dof_indices(
1201 dof_handler.get_fe().dofs_per_cell);
1202 dealii::Vector<Number> mass_values(dof_handler.get_fe().dofs_per_cell);
1203 dealii::hp::FEValues<dim> hp_fe_values(discretization_->mapping(),
1204 discretization_->finite_element(),
1205 discretization_->quadrature(),
1206 update_values | update_JxW_values);
1207 for (
const auto &cell : dof_handler.cell_iterators_on_level(level))
1210 if (cell->is_locally_owned_on_level()) {
1211 hp_fe_values.reinit(cell);
1212 const auto &fe_values = hp_fe_values.get_present_fe_values();
1213 for (
unsigned int i = 0; i < mass_values.size(); ++i) {
1215 for (
unsigned int q = 0; q < fe_values.n_quadrature_points; ++q)
1216 sum += fe_values.shape_value(i, q) * fe_values.JxW(q);
1217 mass_values(i) = sum;
1219 cell->get_mg_dof_indices(dof_indices);
1220 level_constraints.distribute_local_to_global(
1221 mass_values, dof_indices, level_lumped_mass_matrix_[level]);
1223 level_lumped_mass_matrix_[level].compress(VectorOperation::add);
1227 level_boundary_map_[level] = construct_boundary_map(
1228 dof_handler.begin_mg(level), dof_handler.end_mg(level), *partitioner);
1233 template <
int dim,
typename Number>
1234 template <
typename ITERATOR1,
typename ITERATOR2>
1236 const ITERATOR1 &begin,
1237 const ITERATOR2 &end,
1238 const Utilities::MPI::Partitioner &partitioner)
const -> BoundaryMap
1241 std::cout <<
"OfflineData<dim, Number>::construct_boundary_map()"
1245 const auto sparsity_simd_view = sparsity_pattern_simd_.view();
1251 using BoundaryData = std::tuple<dealii::Tensor<1, dim, Number> ,
1254 dealii::types::boundary_id ,
1255 dealii::Point<dim>> ;
1256 std::multimap<unsigned int, BoundaryData> preliminary_map;
1258 std::vector<dealii::types::global_dof_index> local_dof_indices;
1260 dealii::hp::FEFaceValues<dim> hp_fe_face_values(
1261 discretization_->mapping(),
1262 discretization_->finite_element(),
1263 discretization_->face_quadrature(),
1264 dealii::update_normal_vectors | dealii::update_values |
1265 dealii::update_JxW_values);
1267 for (
auto cell = begin; cell != end; ++cell) {
1275 if ((cell->is_active() && cell->is_artificial()) ||
1276 (!cell->is_active() && cell->is_artificial_on_level()))
1279 const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();
1280 const auto &support_points = cell->get_fe().get_unit_support_points();
1282 local_dof_indices.resize(dofs_per_cell);
1283 cell->get_active_or_mg_dof_indices(local_dof_indices);
1285 for (
auto f_index : cell->face_indices()) {
1286 const auto face = cell->face(f_index);
1287 auto id = face->boundary_id();
1300 const bool neighbor_cell_has_fe_nothing =
1301 treat_fe_nothing_as_boundary_ &&
1302 !face->at_boundary() &&
1303 cell->neighbor(f_index)->is_active() &&
1304 !cell->neighbor(f_index)->is_artificial() &&
1305 (
dynamic_cast<const dealii::FE_Nothing<dim> *
>(
1306 &cell->neighbor(f_index)->get_fe()) !=
nullptr);
1308 if (neighbor_cell_has_fe_nothing) {
1313 id = cell->neighbor(f_index)->material_id();
1315 }
else if (!face->at_boundary()) {
1320 hp_fe_face_values.reinit(cell, f_index);
1321 const auto &fe_face_values = hp_fe_face_values.get_present_fe_values();
1322 const auto &mapping =
1323 hp_fe_face_values.get_mapping_collection()[cell->active_fe_index()];
1325 for (
unsigned int j : fe_face_values.dof_indices()) {
1326 if (!cell->get_fe().has_support_on_face(j, f_index))
1329 Number boundary_mass = 0.;
1330 dealii::Tensor<1, dim, Number> normal;
1332 for (
unsigned int q : fe_face_values.quadrature_point_indices()) {
1333 const auto JxW = fe_face_values.JxW(q);
1334 const auto phi_i = fe_face_values.shape_value(j, q);
1336 boundary_mass += phi_i * JxW;
1337 normal += phi_i * fe_face_values.normal_vector(q) * JxW;
1347 if (std::abs(boundary_mass) == 0.)
1350 const auto global_index = local_dof_indices[j];
1351 const auto index = partitioner.global_to_local(global_index);
1354 if (index >= n_locally_owned_)
1358 const unsigned int row_length = sparsity_simd_view.row_length(index);
1359 if (row_length == 1)
1362 Point<dim> position =
1363 mapping.transform_unit_to_real_cell(cell, support_points[j]);
1369 preliminary_map.insert(
1370 {index, {normal, boundary_mass, boundary_mass, id, position}});
1386 std::multimap<unsigned int, BoundaryData> filtered_map;
1387 std::set<dealii::types::global_dof_index> boundary_dofs;
1388 for (
auto entry : preliminary_map) {
1389 bool inserted =
false;
1390 const auto range = filtered_map.equal_range(entry.first);
1391 for (
auto it = range.first; it != range.second; ++it) {
1396 new_point] = entry.second;
1397 auto &[normal, normal_mass, boundary_mass, id, point] = it->second;
1402 Assert(point.distance(new_point) < 1.0e-14, dealii::ExcInternalError());
1404 if (normal * new_normal / normal.norm() / new_normal.norm() > 0.50) {
1409 normal += new_normal;
1410 boundary_mass += new_boundary_mass;
1414 }
else if constexpr (dim == 2) {
1430 filtered_map.insert(entry);
1437 BoundaryMap boundary_map;
1439 std::begin(filtered_map),
1440 std::end(filtered_map),
1441 std::back_inserter(boundary_map),
1443 auto index = it.first;
1444 const auto &[normal, normal_mass, boundary_mass, id, point] =
1447 const auto new_normal_mass =
1448 normal.norm() + std::numeric_limits<Number>::epsilon();
1449 const auto new_normal = normal / new_normal_mass;
1451 return {index, new_normal, new_normal_mass, boundary_mass, id, point};
1454 return boundary_map;
1458 template <
int dim,
typename Number>
1459 template <
typename ITERATOR1,
typename ITERATOR2>
1461 const ITERATOR1 &begin,
1462 const ITERATOR2 &end,
1463 const Utilities::MPI::Partitioner &partitioner)
const
1464 -> CouplingBoundaryPairs
1467 std::cout <<
"OfflineData<dim, Number>::collect_coupling_boundary_pairs()"
1477 std::set<unsigned int> locally_relevant_boundary_indices;
1479 std::vector<dealii::types::global_dof_index> local_dof_indices;
1481 for (
auto cell = begin; cell != end; ++cell) {
1484 if (cell->is_artificial())
1487 const auto &finite_element = cell->get_fe();
1488 const unsigned int dofs_per_cell = finite_element.dofs_per_cell;
1489 local_dof_indices.resize(dofs_per_cell);
1490 cell->get_active_or_mg_dof_indices(local_dof_indices);
1492 for (
auto f_index : cell->face_indices()) {
1493 const auto face = cell->face(f_index);
1494 const auto id = face->boundary_id();
1503 const bool neighbor_cell_has_fe_nothing =
1504 treat_fe_nothing_as_boundary_ &&
1505 !face->at_boundary() &&
1506 cell->neighbor(f_index)->is_active() &&
1507 !cell->neighbor(f_index)->is_artificial() &&
1508 (
dynamic_cast<const dealii::FE_Nothing<dim> *
>(
1509 &cell->neighbor(f_index)->get_fe()) !=
nullptr);
1511 if (!neighbor_cell_has_fe_nothing && !face->at_boundary())
1514 for (
unsigned int j = 0; j < dofs_per_cell; ++j) {
1516 if (!cell->get_fe().has_support_on_face(j, f_index))
1519 const auto global_index = local_dof_indices[j];
1520 const auto index = partitioner.global_to_local(global_index);
1523 if (index >= n_locally_relevant_)
1526 locally_relevant_boundary_indices.insert(index);
1535 const auto sparsity_simd_view = sparsity_pattern_simd_.view();
1537 CouplingBoundaryPairs result;
1539 for (
const auto i : locally_relevant_boundary_indices) {
1542 if (i >= n_locally_owned_)
1545 const unsigned int row_length = sparsity_simd_view.row_length(i);
1548 if (row_length == 1)
1551 const unsigned int stride_size = sparsity_simd_view.stride_of_row(i);
1552 const unsigned int *js = sparsity_simd_view.columns(i);
1554 for (
unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {
1555 const auto j = js[col_idx * stride_size];
1557 if (locally_relevant_boundary_indices.count(j) != 0) {
1558 result.push_back({i, col_idx, j});