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);
207 if (discretization_->triangulation().has_hanging_nodes())
208 DoFTools::make_hanging_node_constraints(dof_handler,
216 const auto &periodic_faces =
217 discretization_->triangulation().get_periodic_face_map();
219 for (
const auto &[left, value] : periodic_faces) {
220 const auto &[right, orientation] = value;
222 typename DoFHandler<dim>::cell_iterator dof_cell_left(
223 &left.first->get_triangulation(),
228 typename DoFHandler<dim>::cell_iterator dof_cell_right(
229 &right.first->get_triangulation(),
230 right.first->level(),
231 right.first->index(),
234 if constexpr (std::is_same_v<Number, double>) {
235 DoFTools::make_periodicity_constraints(
236 dof_cell_left->face(left.second),
237 dof_cell_right->face(right.second),
242 AssertThrow(
false, dealii::ExcNotImplemented());
247 affine_constraints.close();
252 const std::vector<IndexSet> &locally_owned_dofs =
253 Utilities::MPI::all_gather(
254 mpi_ensemble_.ensemble_communicator(),
255 dof_handler.locally_owned_dofs());
256 const IndexSet locally_active =
257 dealii::DoFTools::extract_locally_active_dofs(dof_handler);
258 Assert(affine_constraints.is_consistent_in_parallel(
261 mpi_ensemble_.ensemble_communicator(),
268 populate_affine_constraints(*dof_handler_cg_, affine_constraints_cg_);
270 populate_affine_constraints(*dof_handler_dg_, affine_constraints_dg_);
276 const auto &dof_handler = this->dof_handler();
277 const auto &affine_constraints = this->affine_constraints();
278 const IndexSet &locally_owned = dof_handler.locally_owned_dofs();
279 Assert(n_locally_owned_ == locally_owned.n_elements(),
280 dealii::ExcInternalError());
282 const auto locally_relevant =
283 DoFTools::extract_locally_relevant_dofs(dof_handler);
285 sparsity_pattern_.reinit(
286 dof_handler.n_dofs(), dof_handler.n_dofs(), locally_relevant);
288 if (discretization_->have_discontinuous_ansatz()) {
293 dof_handler, sparsity_pattern_, affine_constraints,
false);
298 DoFTools::make_sparsity_pattern(
299 dof_handler, sparsity_pattern_, affine_constraints,
false);
308 SparsityTools::distribute_sparsity_pattern(
311 mpi_ensemble_.ensemble_communicator(),
320 template <
int dim,
typename Number>
321 void OfflineData<dim, Number>::ensure_simd_stride_consistency()
323 auto &dof_handler = this->dof_handler();
329 const auto consistent_stride_range [[maybe_unused]] = [&]() {
330 const IndexSet &locally_owned = dof_handler.locally_owned_dofs();
331 const auto offset = n_locally_owned_ != 0 ? *locally_owned.begin() : 0;
333 unsigned int warp_row_length = 0;
335 for (; i < n_locally_internal_; ++i) {
337 warp_row_length = sparsity_pattern_.row_length(offset + i);
339 if (warp_row_length != sparsity_pattern_.row_length(offset + i)) {
350 const auto mpi_allreduce_logical_or = [&](
const bool local_value) {
351 std::function<bool(
const bool &,
const bool &)> comparator =
352 [](
const bool &left,
const bool &right) ->
bool {
353 return left || right;
355 return Utilities::MPI::all_reduce(
356 local_value, mpi_ensemble_.ensemble_communicator(), comparator);
367 const auto &affine_constraints = this->affine_constraints();
368 if (mpi_allreduce_logical_or(affine_constraints.n_constraints() > 0)) {
369 if (mpi_allreduce_logical_or(
370 consistent_stride_range() != n_locally_internal_)) {
377 dof_handler, sparsity_pattern_, n_locally_internal_,
warp_size);
378 create_constraints_and_sparsity_pattern();
379 n_locally_internal_ = consistent_stride_range();
388 Assert(consistent_stride_range() == n_locally_internal_,
389 dealii::ExcInternalError());
401 template <
int dim,
typename Number>
402 void OfflineData<dim, Number>::create_partitioner_and_simd_sparsity(
403 const unsigned int problem_dimension,
404 const unsigned int n_precomputed_values)
406 const auto &dof_handler = this->dof_handler();
407 const auto &affine_constraints = this->affine_constraints();
408 const IndexSet &locally_owned = dof_handler.locally_owned_dofs();
409 Assert(n_locally_owned_ == locally_owned.n_elements(),
410 dealii::ExcInternalError());
412 auto locally_relevant =
413 DoFTools::extract_locally_relevant_dofs(dof_handler);
416 IndexSet additional_dofs(dof_handler.n_dofs());
417 for (
auto &entry : sparsity_pattern_)
418 if (!locally_relevant.is_element(entry.column())) {
419 Assert(locally_owned.is_element(entry.row()), ExcInternalError());
420 additional_dofs.add_index(entry.column());
422 additional_dofs.compress();
423 locally_relevant.add_indices(additional_dofs);
424 locally_relevant.compress();
427 n_locally_relevant_ = locally_relevant.n_elements();
429 scalar_partitioner_ = std::make_shared<dealii::Utilities::MPI::Partitioner>(
430 locally_owned, locally_relevant, mpi_ensemble_.ensemble_communicator());
433 scalar_partitioner_, problem_dimension);
436 scalar_partitioner_, n_precomputed_values);
441 const auto mpi_allreduce_logical_or = [&](
const bool local_value) {
442 std::function<bool(
const bool &,
const bool &)> comparator =
443 [](
const bool &left,
const bool &right) ->
bool {
444 return left || right;
446 return Utilities::MPI::all_reduce(
447 local_value, mpi_ensemble_.ensemble_communicator(), comparator);
456 if (mpi_allreduce_logical_or(affine_constraints.n_constraints() > 0)) {
460 n_export_indices_ = 0;
461 for (
const auto &it : scalar_partitioner_->import_indices())
462 if (it.second <= n_locally_internal_)
463 n_export_indices_ = std::
max(n_export_indices_, it.second);
471 unsigned int control = 0;
472 for (
const auto &it : scalar_partitioner_->import_indices())
473 if (it.second <= n_locally_internal_)
474 control = std::
max(control, it.second);
476 Assert(control <= n_export_indices_, ExcInternalError());
477 Assert(n_export_indices_ <= n_locally_internal_, ExcInternalError());
487 sparsity_pattern_simd_.reinit(n_locally_internal_,
495 template <
int dim,
typename Number>
496 void OfflineData<dim, Number>::create_matrices()
499 std::cout <<
"OfflineData<dim, Number>::create_matrices()" << std::endl;
511 mass_matrix_.reinit(sparsity_pattern_simd_, policy);
512 if (discretization_->have_discontinuous_ansatz())
513 mass_matrix_inverse_.reinit(sparsity_pattern_simd_, policy);
515 lumped_mass_matrix_.reinit_with_scalar_partitioner(scalar_partitioner_,
517 lumped_mass_matrix_inverse_.reinit_with_scalar_partitioner(
518 scalar_partitioner_, policy);
520 betaij_matrix_.reinit(sparsity_pattern_simd_, policy);
521 cij_matrix_.reinit(sparsity_pattern_simd_, policy);
522 if (discretization_->have_discontinuous_ansatz())
523 incidence_matrix_.reinit(sparsity_pattern_simd_, policy);
529 const auto &dof_handler = this->dof_handler();
530 const auto &affine_constraints = this->affine_constraints();
532 measure_of_omega_ = 0.;
535 const auto local_assemble_system = [&](
const auto &cell,
540 auto &is_locally_owned = copy.is_locally_owned_;
541 auto &local_dof_indices = copy.local_dof_indices_;
542 auto &neighbor_local_dof_indices = copy.neighbor_local_dof_indices_;
544 auto &cell_mass_matrix = copy.cell_mass_matrix_;
545 auto &cell_mass_matrix_inverse = copy.cell_mass_matrix_inverse_;
546 auto &cell_betaij_matrix = copy.cell_betaij_matrix_;
547 auto &cell_cij_matrix = copy.cell_cij_matrix_;
548 auto &interface_cij_matrix = copy.interface_cij_matrix_;
549 auto &cell_measure = copy.cell_measure_;
551 auto &hp_fe_values = scratch.hp_fe_values_;
552 auto &hp_fe_face_values = scratch.hp_fe_face_values_;
553 auto &hp_fe_neighbor_face_values = scratch.hp_fe_neighbor_face_values_;
555 is_locally_owned = cell->is_locally_owned();
556 if (!is_locally_owned)
559 const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();
561 cell_mass_matrix.reinit(dofs_per_cell, dofs_per_cell);
562 cell_betaij_matrix.reinit(dofs_per_cell, dofs_per_cell);
563 for (
auto &matrix : cell_cij_matrix)
564 matrix.reinit(dofs_per_cell, dofs_per_cell);
565 if (discretization_->have_discontinuous_ansatz()) {
566 cell_mass_matrix_inverse.reinit(dofs_per_cell, dofs_per_cell);
569 hp_fe_values.reinit(cell);
570 const auto &fe_values = hp_fe_values.get_present_fe_values();
572 local_dof_indices.resize(dofs_per_cell);
573 cell->get_dof_indices(local_dof_indices);
577 cell_mass_matrix = 0.;
578 cell_betaij_matrix = 0.;
579 for (
auto &matrix : cell_cij_matrix)
581 if (discretization_->have_discontinuous_ansatz()) {
582 cell_mass_matrix_inverse = 0.;
586 for (
unsigned int q : fe_values.quadrature_point_indices()) {
587 const auto JxW = fe_values.JxW(q);
590 if (dofs_per_cell != 0 && cell->is_locally_owned())
591 cell_measure += Number(JxW);
593 for (
unsigned int j : fe_values.dof_indices()) {
594 const auto value_JxW = fe_values.shape_value(j, q) * JxW;
595 const auto grad_JxW = fe_values.shape_grad(j, q) * JxW;
597 for (
unsigned int i : fe_values.dof_indices()) {
598 const auto value = fe_values.shape_value(i, q);
599 const auto grad = fe_values.shape_grad(i, q);
601 cell_mass_matrix(i, j) += Number(value * value_JxW);
602 cell_betaij_matrix(i, j) += Number(grad * grad_JxW);
603 for (
unsigned int d = 0; d < dim; ++d)
604 cell_cij_matrix[d](i, j) += Number((value * grad_JxW)[d]);
614 if (!discretization_->have_discontinuous_ansatz())
617 for (
const auto f_index : cell->face_indices()) {
618 const auto &face = cell->face(f_index);
621 const bool has_neighbor =
622 !face->at_boundary() || cell->has_periodic_neighbor(f_index);
626 neighbor_local_dof_indices[f_index].resize(0);
631 const auto neighbor_cell = cell->neighbor_or_periodic_neighbor(f_index);
632 if (neighbor_cell->is_artificial()) {
635 neighbor_local_dof_indices[f_index].resize(0);
640 const bool neighbor_cell_has_fe_nothing =
641 treat_fe_nothing_as_boundary_ &&
642 (
dynamic_cast<const dealii::FE_Nothing<dim> *
>(
643 &neighbor_cell->get_fe()) !=
nullptr);
644 if (neighbor_cell_has_fe_nothing) {
647 neighbor_local_dof_indices[f_index].resize(0);
651 hp_fe_face_values.reinit(cell, f_index);
652 const auto &fe_face_values = hp_fe_face_values.get_present_fe_values();
656 for (
unsigned int q : fe_face_values.quadrature_point_indices()) {
657 const auto JxW = fe_face_values.JxW(q);
658 const auto &normal = fe_face_values.get_normal_vectors()[q];
660 for (
unsigned int j : fe_face_values.dof_indices()) {
661 const auto value_JxW = fe_face_values.shape_value(j, q) * JxW;
663 for (
unsigned int i : fe_face_values.dof_indices()) {
664 const auto value = fe_face_values.shape_value(i, q);
666 for (
unsigned int d = 0; d < dim; ++d)
667 cell_cij_matrix[d](i, j) -=
668 Number(0.5 * normal[d] * value * value_JxW);
675 const unsigned int f_index_neighbor =
676 cell->has_periodic_neighbor(f_index)
677 ? cell->periodic_neighbor_of_periodic_neighbor(f_index)
678 : cell->neighbor_of_neighbor(f_index);
680 const unsigned int neighbor_dofs_per_cell =
681 neighbor_cell->get_fe().n_dofs_per_cell();
682 neighbor_local_dof_indices[f_index].resize(neighbor_dofs_per_cell);
683 neighbor_cell->get_dof_indices(neighbor_local_dof_indices[f_index]);
685 for (
unsigned int k = 0; k < dim; ++k) {
686 interface_cij_matrix[f_index][k].reinit(dofs_per_cell,
687 neighbor_dofs_per_cell);
688 interface_cij_matrix[f_index][k] = 0.;
691 hp_fe_neighbor_face_values.reinit(neighbor_cell, f_index_neighbor);
692 const auto &fe_neighbor_face_values =
693 hp_fe_neighbor_face_values.get_present_fe_values();
695 for (
unsigned int q : fe_face_values.quadrature_point_indices()) {
696 const auto JxW = fe_face_values.JxW(q);
697 const auto &normal = fe_face_values.get_normal_vectors()[q];
700 for (
unsigned int j : fe_neighbor_face_values.dof_indices()) {
701 const auto value_JxW =
702 fe_neighbor_face_values.shape_value(j, q) * JxW;
704 for (
unsigned int i : fe_face_values.dof_indices()) {
705 const auto value = fe_face_values.shape_value(i, q);
707 for (
unsigned int d = 0; d < dim; ++d)
708 interface_cij_matrix[f_index][d](i, j) +=
709 Number(0.5 * normal[d] * value * value_JxW);
719 if (discretization_->have_discontinuous_ansatz()) {
721 if (!cell_mass_matrix_inverse.empty())
722 cell_mass_matrix_inverse.invert(cell_mass_matrix);
726 const auto copy_local_to_global = [&](
const auto ©) {
727 const auto &is_locally_owned = copy.is_locally_owned_;
728 const auto &dof_indices = copy.local_dof_indices_;
729 const auto &neighbor_dof_indices = copy.neighbor_local_dof_indices_;
730 const auto &cell_mass_matrix = copy.cell_mass_matrix_;
731 const auto &cell_mass_matrix_inverse = copy.cell_mass_matrix_inverse_;
732 const auto &cell_cij_matrix = copy.cell_cij_matrix_;
733 const auto &interface_cij_matrix = copy.interface_cij_matrix_;
734 const auto &cell_betaij_matrix = copy.cell_betaij_matrix_;
735 const auto &cell_measure = copy.cell_measure_;
737 if (!is_locally_owned)
741 cell_mass_matrix, dof_indices, affine_constraints, mass_matrix_);
744 cell_cij_matrix, dof_indices, affine_constraints, cij_matrix_);
750 if (dof_indices.size() != 0) {
751 for (
unsigned int f_index = 0; f_index < copy.n_faces; ++f_index) {
752 if (neighbor_dof_indices[f_index].size() != 0) {
755 neighbor_dof_indices[f_index],
763 cell_betaij_matrix, dof_indices, affine_constraints, betaij_matrix_);
765 if (discretization_->have_discontinuous_ansatz())
769 mass_matrix_inverse_);
771 measure_of_omega_ += cell_measure;
774 WorkStream::run(dof_handler.begin_active(),
776 local_assemble_system,
777 copy_local_to_global,
778 AssemblyScratchData<dim>(*discretization_),
779 AssemblyCopyData<dim, Number>());
781 mass_matrix_.view().compress(VectorOperation::add);
782 mass_matrix_.view().update_ghost_rows();
783 cij_matrix_.view().compress(VectorOperation::add);
784 cij_matrix_.view().update_ghost_rows();
785 betaij_matrix_.view().compress(VectorOperation::add);
786 betaij_matrix_.view().update_ghost_rows();
787 if (discretization_->have_discontinuous_ansatz()) {
788 mass_matrix_inverse_.view().compress(VectorOperation::add);
789 mass_matrix_inverse_.view().update_ghost_rows();
792 measure_of_omega_ = Utilities::MPI::sum(
793 measure_of_omega_, mpi_ensemble_.ensemble_communicator());
800 const auto sparsity_simd_view = sparsity_pattern_simd_.view();
801 const auto mass_matrix_view = mass_matrix_.view();
802 const auto lumped_mass_matrix_view = lumped_mass_matrix_.view();
803 const auto lumped_mass_matrix_inverse_view =
804 lumped_mass_matrix_inverse_.view();
806 const auto body = [&](
auto sentinel,
unsigned int i) {
807 using T =
decltype(sentinel);
808 constexpr unsigned int stride_size = get_stride_size<T>;
811 const unsigned int row_length = sparsity_simd_view.row_length(i);
817 const unsigned int *js = sparsity_simd_view.columns(i);
818 for (
unsigned int col_idx = 0; col_idx < row_length;
819 ++col_idx, js += stride_size) {
821 const auto m_ij = mass_matrix_view.template read_entry<T>(i, col_idx);
825 lumped_mass_matrix_view.template write_entry<T>(m_i, i);
826 lumped_mass_matrix_inverse_view.
827 template write_entry<T>(Number(1.) / m_i, i);
830 cpu_simd_loop<Number>(
"", body, 0, n_locally_internal_, n_locally_owned_);
832 lumped_mass_matrix_view.update_ghost_values();
833 lumped_mass_matrix_inverse_view.update_ghost_values();
840 if (discretization_->have_discontinuous_ansatz()) {
841 const auto lumped_mass_matrix_view = lumped_mass_matrix_.view();
844 const auto local_assemble_system = [&](
const auto &cell,
849 auto &is_locally_owned = copy.is_locally_owned_;
850 auto &local_dof_indices = copy.local_dof_indices_;
851 auto &neighbor_local_dof_indices = copy.neighbor_local_dof_indices_;
852 auto &interface_incidence_matrix = copy.interface_incidence_matrix_;
853 auto &hp_fe_face_values_nodal = scratch.hp_fe_face_values_nodal_;
854 auto &hp_fe_neighbor_face_values_nodal =
855 scratch.hp_fe_neighbor_face_values_nodal_;
857 is_locally_owned = cell->is_locally_owned();
858 if (!is_locally_owned)
861 const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();
863 for (
auto &matrix : interface_incidence_matrix)
864 matrix.reinit(dofs_per_cell, dofs_per_cell);
866 local_dof_indices.resize(dofs_per_cell);
867 cell->get_dof_indices(local_dof_indices);
870 for (
auto &matrix : interface_incidence_matrix)
873 for (
const auto f_index : cell->face_indices()) {
874 const auto &face = cell->face(f_index);
877 const bool has_neighbor =
878 !face->at_boundary() || cell->has_periodic_neighbor(f_index);
882 neighbor_local_dof_indices[f_index].resize(0);
887 const auto neighbor_cell =
888 cell->neighbor_or_periodic_neighbor(f_index);
889 if (neighbor_cell->is_artificial()) {
892 neighbor_local_dof_indices[f_index].resize(0);
897 const bool neighbor_cell_has_fe_nothing =
898 treat_fe_nothing_as_boundary_ &&
899 (
dynamic_cast<const dealii::FE_Nothing<dim> *
>(
900 &neighbor_cell->get_fe()) !=
nullptr);
901 if (neighbor_cell_has_fe_nothing) {
902 neighbor_local_dof_indices[f_index].resize(0);
906 const unsigned int neighbor_dofs_per_cell =
907 neighbor_cell->get_fe().n_dofs_per_cell();
908 neighbor_local_dof_indices[f_index].resize(neighbor_dofs_per_cell);
909 neighbor_cell->get_dof_indices(neighbor_local_dof_indices[f_index]);
911 const unsigned int f_index_neighbor =
912 cell->has_periodic_neighbor(f_index)
913 ? cell->periodic_neighbor_of_periodic_neighbor(f_index)
914 : cell->neighbor_of_neighbor(f_index);
916 hp_fe_face_values_nodal.reinit(cell, f_index);
917 const auto &fe_face_values_nodal =
918 hp_fe_face_values_nodal.get_present_fe_values();
919 hp_fe_neighbor_face_values_nodal.reinit(neighbor_cell,
921 const auto &fe_neighbor_face_values_nodal =
922 hp_fe_neighbor_face_values_nodal.get_present_fe_values();
926 for (
unsigned int q :
927 fe_face_values_nodal.quadrature_point_indices()) {
929 for (
unsigned int j : fe_neighbor_face_values_nodal.dof_indices()) {
930 const auto v_j = fe_neighbor_face_values_nodal.shape_value(j, q);
931 for (
unsigned int i : fe_face_values_nodal.dof_indices()) {
932 const auto v_i = fe_face_values_nodal.shape_value(i, q);
933 constexpr auto eps = std::numeric_limits<Number>::epsilon();
934 if (std::abs(v_i * v_j) > 100. * eps) {
935 const auto &ansatz = discretization_->ansatz();
937 const auto global_i = local_dof_indices[i];
938 const auto global_j = neighbor_local_dof_indices[f_index][j];
940 scalar_partitioner_->global_to_local(global_i);
942 scalar_partitioner_->global_to_local(global_j);
943 const auto m_i = lumped_mass_matrix_view.read_entry(local_i);
944 const auto m_j = lumped_mass_matrix_view.read_entry(local_j);
946 Number(0.5) * (m_i + m_j) / measure_of_omega_;
957 r_ij = std::pow(hd_ij, incidence_relaxation_even_ / dim);
964 r_ij = std::pow(hd_ij, incidence_relaxation_odd_ / dim);
967 interface_incidence_matrix[f_index](i, j) += r_ij;
975 const auto copy_local_to_global = [&](
const auto ©) {
976 const auto &is_locally_owned = copy.is_locally_owned_;
977 const auto &dof_indices = copy.local_dof_indices_;
978 const auto &neighbor_dof_indices = copy.neighbor_local_dof_indices_;
979 const auto &interface_incidence_matrix =
980 copy.interface_incidence_matrix_;
982 if (!is_locally_owned)
989 if (dof_indices.size() != 0) {
990 for (
unsigned int f_index = 0; f_index < copy.n_faces; ++f_index) {
991 if (neighbor_dof_indices[f_index].size() != 0) {
994 neighbor_dof_indices[f_index],
1002 WorkStream::run(dof_handler.begin_active(),
1004 local_assemble_system,
1005 copy_local_to_global,
1006 AssemblyScratchData<dim>(*discretization_),
1007 AssemblyCopyData<dim, Number>());
1009 incidence_matrix_.view().compress(VectorOperation::add);
1016 boundary_map_ = construct_boundary_map(
1017 dof_handler.begin_active(), dof_handler.end(), *scalar_partitioner_);
1028 std::vector<unsigned int> boundary_indices;
1029 std::map<unsigned int, unsigned int> position_of_index;
1031 boundary_slots_.clear();
1032 boundary_slots_.reserve(boundary_map_.size());
1034 for (
const auto &entry : boundary_map_) {
1035 const auto index = std::get<0>(entry);
1036 const auto [it, inserted] =
1037 position_of_index.try_emplace(index, boundary_indices.size());
1039 boundary_indices.push_back(index);
1040 boundary_slots_.push_back(it->second);
1043 boundary_indices_.reinit(boundary_indices.size(),
1045 std::copy(boundary_indices.begin(),
1046 boundary_indices.end(),
1047 boundary_indices_.view());
1049 const auto coupling_boundary_pairs = collect_coupling_boundary_pairs(
1050 dof_handler.begin_active(), dof_handler.end(), *scalar_partitioner_);
1052 coupling_boundary_pairs_.reinit(coupling_boundary_pairs.size(),
1054 std::copy(coupling_boundary_pairs.begin(),
1055 coupling_boundary_pairs.end(),
1056 coupling_boundary_pairs_.view());
1059#ifdef DEBUG_SYMMETRY_CHECK
1064 const auto lumped_mass_matrix_view = lumped_mass_matrix_.view();
1066 double total_mass = 0.;
1067 for (
unsigned int i = 0; i < n_locally_owned_; ++i)
1068 total_mass += lumped_mass_matrix_view.read_entry(i);
1070 Utilities::MPI::sum(total_mass, mpi_ensemble_.ensemble_communicator());
1072 Assert(std::abs(measure_of_omega_ - total_mass) <
1073 1.e-12 * measure_of_omega_,
1075 "Total mass differs from the measure of the domain."));
1081 const auto sparsity_simd_view = sparsity_pattern_simd_.view();
1082 const auto mass_matrix_view = mass_matrix_.view();
1083 const auto cij_matrix_view = cij_matrix_.view();
1085 for (
unsigned int i = 0; i < n_locally_owned_; ++i) {
1087 const unsigned int row_length = sparsity_simd_view.row_length(i);
1088 if (row_length == 1)
1091 auto sum = mass_matrix_view.read_entry(i, 0) -
1092 lumped_mass_matrix_view.read_entry(i);
1095 const unsigned int stride_size = sparsity_simd_view.stride_of_row(i);
1096 const unsigned int *js = sparsity_simd_view.columns(i);
1097 for (
unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {
1098 const auto j = js[col_idx * stride_size];
1099 Assert(j < n_locally_relevant_, dealii::ExcInternalError());
1101 const auto m_ij = mass_matrix_view.read_entry(i, col_idx);
1102 if (discretization_->have_discontinuous_ansatz()) {
1105 Assert(std::abs(m_ij) > -1.e-12, dealii::ExcInternalError());
1107 Assert(std::abs(m_ij) > 1.e-12, dealii::ExcInternalError());
1111 const auto m_ji = mass_matrix_view.read_transposed_entry(i, col_idx);
1112 if (std::abs(m_ij - m_ji) >= 1.e-12) {
1114 std::stringstream ss;
1115 ss <<
"m_ij matrix is not symmetric: " << m_ij <<
" <-> " << m_ji;
1116 Assert(
false, dealii::ExcMessage(ss.str()));
1120 Assert(std::abs(sum) < 1.e-12, dealii::ExcInternalError());
1127 for (
unsigned int i = 0; i < n_locally_owned_; ++i) {
1129 const unsigned int row_length = sparsity_simd_view.row_length(i);
1130 if (row_length == 1)
1133 auto sum = cij_matrix_view.read_tensor(i, 0);
1136 const unsigned int stride_size = sparsity_simd_view.stride_of_row(i);
1137 const unsigned int *js = sparsity_simd_view.columns(i);
1138 for (
unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {
1139 const auto j = js[col_idx * stride_size];
1140 Assert(j < n_locally_relevant_, dealii::ExcInternalError());
1142 const auto c_ij = cij_matrix_view.read_tensor(i, col_idx);
1143 Assert(c_ij.norm() > 1.e-12, dealii::ExcInternalError());
1146 const auto c_ji = cij_matrix_view.read_transposed_tensor(i, col_idx);
1147 if ((c_ij + c_ji).norm() >= 1.e-12) {
1151 const CouplingDescription coupling{i, col_idx, j};
1152 const auto *begin = coupling_boundary_pairs_.view();
1153 const auto *end = begin + coupling_boundary_pairs_.size();
1154 if (std::find(begin, end, coupling) == end) {
1155 std::stringstream ss;
1156 ss <<
"c_ij matrix is not anti-symmetric: " << c_ij <<
" <-> "
1158 Assert(
false, dealii::ExcMessage(ss.str()));
1163 Assert(sum.norm() < 1.e-12, dealii::ExcInternalError());
1169 template <
int dim,
typename Number>
1170 void OfflineData<dim, Number>::create_multigrid_data()
1173 std::cout <<
"OfflineData<dim, Number>::create_multigrid_data()"
1177 Assert(!dof_handler_cg_->has_hp_capabilities(), dealii::ExcInternalError());
1178 Assert(!dof_handler_dg_->has_hp_capabilities(), dealii::ExcInternalError());
1180 dof_handler_cg_->distribute_mg_dofs();
1181 dof_handler_dg_->distribute_mg_dofs();
1185 auto &dof_handler = this->dof_handler();
1187 const auto n_levels = dof_handler.get_triangulation().n_global_levels();
1189 AffineConstraints<float> level_constraints;
1192 level_boundary_map_.resize(n_levels);
1193 level_lumped_mass_matrix_.resize(n_levels);
1195 for (
unsigned int level = 0; level < n_levels; ++level) {
1198 const auto relevant_dofs =
1199 dealii::DoFTools::extract_locally_relevant_level_dofs(dof_handler,
1202 const auto partitioner = std::make_shared<Utilities::MPI::Partitioner>(
1203 dof_handler.locally_owned_mg_dofs(level),
1205 mpi_ensemble_.ensemble_communicator());
1206 level_lumped_mass_matrix_[level].reinit(partitioner);
1207 std::vector<types::global_dof_index> dof_indices(
1208 dof_handler.get_fe().dofs_per_cell);
1209 dealii::Vector<Number> mass_values(dof_handler.get_fe().dofs_per_cell);
1210 dealii::hp::FEValues<dim> hp_fe_values(discretization_->mapping(),
1211 discretization_->finite_element(),
1212 discretization_->quadrature(),
1213 update_values | update_JxW_values);
1214 for (
const auto &cell : dof_handler.cell_iterators_on_level(level))
1217 if (cell->is_locally_owned_on_level()) {
1218 hp_fe_values.reinit(cell);
1219 const auto &fe_values = hp_fe_values.get_present_fe_values();
1220 for (
unsigned int i = 0; i < mass_values.size(); ++i) {
1222 for (
unsigned int q = 0; q < fe_values.n_quadrature_points; ++q)
1223 sum += fe_values.shape_value(i, q) * fe_values.JxW(q);
1224 mass_values(i) = sum;
1226 cell->get_mg_dof_indices(dof_indices);
1227 level_constraints.distribute_local_to_global(
1228 mass_values, dof_indices, level_lumped_mass_matrix_[level]);
1230 level_lumped_mass_matrix_[level].compress(VectorOperation::add);
1234 level_boundary_map_[level] = construct_boundary_map(
1235 dof_handler.begin_mg(level), dof_handler.end_mg(level), *partitioner);
1240 template <
int dim,
typename Number>
1241 template <
typename ITERATOR1,
typename ITERATOR2>
1243 const ITERATOR1 &begin,
1244 const ITERATOR2 &end,
1245 const Utilities::MPI::Partitioner &partitioner)
const -> BoundaryMap
1248 std::cout <<
"OfflineData<dim, Number>::construct_boundary_map()"
1252 const auto sparsity_simd_view = sparsity_pattern_simd_.view();
1258 using BoundaryData = std::tuple<dealii::Tensor<1, dim, Number> ,
1261 dealii::types::boundary_id ,
1262 dealii::Point<dim>> ;
1263 std::multimap<unsigned int, BoundaryData> preliminary_map;
1265 std::vector<dealii::types::global_dof_index> local_dof_indices;
1267 dealii::hp::FEFaceValues<dim> hp_fe_face_values(
1268 discretization_->mapping(),
1269 discretization_->finite_element(),
1270 discretization_->face_quadrature(),
1271 dealii::update_normal_vectors | dealii::update_values |
1272 dealii::update_JxW_values);
1274 for (
auto cell = begin; cell != end; ++cell) {
1282 if ((cell->is_active() && cell->is_artificial()) ||
1283 (!cell->is_active() && cell->is_artificial_on_level()))
1286 const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();
1287 const auto &support_points = cell->get_fe().get_unit_support_points();
1289 local_dof_indices.resize(dofs_per_cell);
1290 cell->get_active_or_mg_dof_indices(local_dof_indices);
1292 for (
auto f_index : cell->face_indices()) {
1293 const auto face = cell->face(f_index);
1294 auto id = face->boundary_id();
1307 const bool neighbor_cell_has_fe_nothing =
1308 treat_fe_nothing_as_boundary_ &&
1309 !face->at_boundary() &&
1310 cell->neighbor(f_index)->is_active() &&
1311 !cell->neighbor(f_index)->is_artificial() &&
1312 (
dynamic_cast<const dealii::FE_Nothing<dim> *
>(
1313 &cell->neighbor(f_index)->get_fe()) !=
nullptr);
1315 if (neighbor_cell_has_fe_nothing) {
1320 id = cell->neighbor(f_index)->material_id();
1322 }
else if (!face->at_boundary()) {
1327 hp_fe_face_values.reinit(cell, f_index);
1328 const auto &fe_face_values = hp_fe_face_values.get_present_fe_values();
1329 const auto &mapping =
1330 hp_fe_face_values.get_mapping_collection()[cell->active_fe_index()];
1332 for (
unsigned int j : fe_face_values.dof_indices()) {
1333 if (!cell->get_fe().has_support_on_face(j, f_index))
1336 Number boundary_mass = 0.;
1337 dealii::Tensor<1, dim, Number> normal;
1339 for (
unsigned int q : fe_face_values.quadrature_point_indices()) {
1340 const auto JxW = fe_face_values.JxW(q);
1341 const auto phi_i = fe_face_values.shape_value(j, q);
1343 boundary_mass += phi_i * JxW;
1344 normal += phi_i * fe_face_values.normal_vector(q) * JxW;
1354 if (std::abs(boundary_mass) == 0.)
1357 const auto global_index = local_dof_indices[j];
1358 const auto index = partitioner.global_to_local(global_index);
1361 if (index >= n_locally_owned_)
1365 const unsigned int row_length = sparsity_simd_view.row_length(index);
1366 if (row_length == 1)
1369 Point<dim> position =
1370 mapping.transform_unit_to_real_cell(cell, support_points[j]);
1376 preliminary_map.insert(
1377 {index, {normal, boundary_mass, boundary_mass, id, position}});
1393 std::multimap<unsigned int, BoundaryData> filtered_map;
1394 std::set<dealii::types::global_dof_index> boundary_dofs;
1395 for (
auto entry : preliminary_map) {
1396 bool inserted =
false;
1397 const auto range = filtered_map.equal_range(entry.first);
1398 for (
auto it = range.first; it != range.second; ++it) {
1403 new_point] = entry.second;
1404 auto &[normal, normal_mass, boundary_mass, id, point] = it->second;
1409 Assert(point.distance(new_point) < 1.0e-14, dealii::ExcInternalError());
1411 if (normal * new_normal / normal.norm() / new_normal.norm() > 0.50) {
1416 normal += new_normal;
1417 boundary_mass += new_boundary_mass;
1421 }
else if constexpr (dim == 2) {
1437 filtered_map.insert(entry);
1444 BoundaryMap boundary_map;
1446 std::begin(filtered_map),
1447 std::end(filtered_map),
1448 std::back_inserter(boundary_map),
1450 auto index = it.first;
1451 const auto &[normal, normal_mass, boundary_mass, id, point] =
1454 const auto new_normal_mass =
1455 normal.norm() + std::numeric_limits<Number>::epsilon();
1456 const auto new_normal = normal / new_normal_mass;
1458 return {index, new_normal, new_normal_mass, boundary_mass, id, point};
1461 return boundary_map;
1465 template <
int dim,
typename Number>
1466 template <
typename ITERATOR1,
typename ITERATOR2>
1468 const ITERATOR1 &begin,
1469 const ITERATOR2 &end,
1470 const Utilities::MPI::Partitioner &partitioner)
const
1471 -> CouplingBoundaryPairs
1474 std::cout <<
"OfflineData<dim, Number>::collect_coupling_boundary_pairs()"
1484 std::set<unsigned int> locally_relevant_boundary_indices;
1486 std::vector<dealii::types::global_dof_index> local_dof_indices;
1488 for (
auto cell = begin; cell != end; ++cell) {
1491 if (cell->is_artificial())
1494 const auto &finite_element = cell->get_fe();
1495 const unsigned int dofs_per_cell = finite_element.dofs_per_cell;
1496 local_dof_indices.resize(dofs_per_cell);
1497 cell->get_active_or_mg_dof_indices(local_dof_indices);
1499 for (
auto f_index : cell->face_indices()) {
1500 const auto face = cell->face(f_index);
1501 const auto id = face->boundary_id();
1510 const bool neighbor_cell_has_fe_nothing =
1511 treat_fe_nothing_as_boundary_ &&
1512 !face->at_boundary() &&
1513 cell->neighbor(f_index)->is_active() &&
1514 !cell->neighbor(f_index)->is_artificial() &&
1515 (
dynamic_cast<const dealii::FE_Nothing<dim> *
>(
1516 &cell->neighbor(f_index)->get_fe()) !=
nullptr);
1518 if (!neighbor_cell_has_fe_nothing && !face->at_boundary())
1521 for (
unsigned int j = 0; j < dofs_per_cell; ++j) {
1523 if (!cell->get_fe().has_support_on_face(j, f_index))
1526 const auto global_index = local_dof_indices[j];
1527 const auto index = partitioner.global_to_local(global_index);
1530 if (index >= n_locally_relevant_)
1533 locally_relevant_boundary_indices.insert(index);
1542 const auto sparsity_simd_view = sparsity_pattern_simd_.view();
1544 CouplingBoundaryPairs result;
1546 for (
const auto i : locally_relevant_boundary_indices) {
1549 if (i >= n_locally_owned_)
1552 const unsigned int row_length = sparsity_simd_view.row_length(i);
1555 if (row_length == 1)
1558 const unsigned int stride_size = sparsity_simd_view.stride_of_row(i);
1559 const unsigned int *js = sparsity_simd_view.columns(i);
1561 for (
unsigned int col_idx = 1; col_idx < row_length; ++col_idx) {
1562 const auto j = js[col_idx * stride_size];
1564 if (locally_relevant_boundary_indices.count(j) != 0) {
1565 result.push_back({i, col_idx, j});