109 std::cout <<
"SolutionTransfer<HyperbolicDescription, dim, "
110 "Number>::prepare_projection()"
114 copy_to_memory_space<dealii::MemorySpace::Host>(
120 auto &triangulation = discretization_->triangulation();
122 Assert(this->handle_ == dealii::numbers::invalid_unsigned_int,
124 "You can only add one solution per SolutionTransfer object."));
126 for (
const auto &U_dynamic_k : old_state_vector.dynamic_state)
127 U_dynamic_k.deal_ii_vector().update_ghost_values();
135 this->handle_ = triangulation.register_data_attach(
136 [
this, &old_state_vector](
const auto cell,
137 const dealii::CellStatus status) {
138 const auto &dof_handler = offline_data_->dof_handler();
139 const auto dof_cell =
typename dealii::DoFHandler<dim>::cell_iterator(
140 &cell->get_triangulation(),
145 const auto &scalar_partitioner = offline_data_->scalar_partitioner();
147 const auto &U = old_state_vector.state;
148 const auto &U_dynamic = old_state_vector.dynamic_state;
150 const auto precomputed_view =
151 old_state_vector.precomputed
152 .template view<n_precomputed_values>();
154 const auto limiter_view = limiter_.template view<dim, Number>();
160 const auto n_dofs_per_cell = dof_cell->get_fe().n_dofs_per_cell();
161 std::vector<state_type> state_values(n_dofs_per_cell);
164 case dealii::CellStatus::cell_will_persist:
166 case dealii::CellStatus::cell_will_be_refined: {
172 Assert(dof_cell->is_active(), dealii::ExcInternalError());
173 std::vector<dealii::types::global_dof_index> dof_indices(
175 dof_cell->get_dof_indices(dof_indices);
178 std::begin(dof_indices),
179 std::end(dof_indices),
180 std::begin(state_values),
181 [&](
const auto global_i) {
return read_tensor(U, global_i); });
184 case dealii::CellStatus::children_will_be_coarsened: {
191 Assert(dof_cell->has_children(), dealii::ExcInternalError());
193 const auto &discretization = offline_data_->discretization();
194 const auto index = dof_cell->active_fe_index();
195 const auto &finite_element = discretization.finite_element()[index];
196 const auto &mapping = discretization.mapping()[index];
197 const auto &quadrature = discretization.quadrature()[index];
199 dealii::FEValues<dim> fe_values(
203 dealii::update_values | dealii::update_JxW_values |
204 dealii::update_quadrature_points);
206 const auto polynomial_space =
207 dealii::internal::FEPointEvaluation::get_polynomial_space(
210 std::vector<dealii::Point<dim, Number>> unit_points(
216 std::vector<dealii::Point<dim>> unit_points_temp(
217 std::is_same_v<Number, float> ? quadrature.size() : 0);
221 std::vector<state_type> state_values_quad(quadrature.size());
222 std::vector<state_type> local_rhs(n_dofs_per_cell);
224 std::vector<dealii::types::global_dof_index> dof_indices(
229 for (
unsigned int child = 0; child < dof_cell->n_children();
231 const auto child_cell = dof_cell->child(child);
233 Assert(child_cell->is_active(), dealii::ExcInternalError());
234 Assert(dof_cell->active_fe_index() ==
235 child_cell->active_fe_index(),
236 dealii::ExcMessage(
"SolutionTransfer: projection between "
237 "different FE space is not set up."));
239 fe_values.reinit(child_cell);
241 if constexpr (std::is_same_v<Number, float>) {
242 mapping.transform_points_real_to_unit_cell(
244 fe_values.get_quadrature_points(),
246 std::transform(std::begin(unit_points_temp),
247 std::end(unit_points_temp),
248 std::begin(unit_points),
249 [](
const auto &x) {
return x; });
251 mapping.transform_points_real_to_unit_cell(
252 dof_cell, fe_values.get_quadrature_points(), unit_points);
255 child_cell->get_dof_indices(dof_indices);
259 std::begin(dof_indices) != std::end(dof_indices)) {
260 const auto global_i = dof_indices[0];
261 const auto U_i = read_tensor(U, global_i);
263 scalar_partitioner->global_to_local(global_i);
264 bounds = limiter_view.projection_bounds_from_state(
265 precomputed_view, local_i, U_i);
268 for (
auto &it : state_values_quad)
271 for (
unsigned int i = 0; i < n_dofs_per_cell; ++i) {
272 const auto global_i = dof_indices[i];
273 const auto U_i = read_tensor(U, global_i);
275 scalar_partitioner->global_to_local(global_i);
276 const auto bounds_i = limiter_view.projection_bounds_from_state(
277 precomputed_view, local_i, U_i);
278 bounds = limiter_view.combine_bounds(bounds, bounds_i);
280 for (
unsigned int q = 0; q < quadrature.size(); ++q) {
281 state_values_quad[q] += U_i * fe_values.shape_value(i, q);
285 for (
unsigned int q = 0; q < quadrature.size(); ++q)
286 state_values_quad[q] *= fe_values.JxW(q);
288 for (
unsigned int q = 0; q < quadrature.size(); ++q) {
289 const unsigned int n_shapes = polynomial_space.size();
290 AssertIndexRange(n_shapes, 10);
291 dealii::ndarray<Number, 10, 2, dim> shapes;
293 std::array<Number, dim> point;
294 for (
unsigned int d = 0; d < dim; ++d)
295 point[d] = unit_points[q][d];
296 for (
unsigned int i = 0; i < n_shapes; ++i)
297 polynomial_space[i].values_of_array(point, 1, &shapes[i][0]);
299 Assert(finite_element.degree == 1, dealii::ExcNotImplemented());
307 state_values_quad[q],
316 fe_values.reinit(dof_cell);
318 dealii::FullMatrix<double> mass_inverse(n_dofs_per_cell,
320 dealii::Vector<double> lumped_mass(n_dofs_per_cell);
321 dealii::Vector<double> lumped_mass_inverse(n_dofs_per_cell);
323 auto total_mass = Number(0.);
324 for (
unsigned int i = 0; i < n_dofs_per_cell; ++i) {
325 for (
unsigned int j = 0; j < n_dofs_per_cell; ++j) {
327 for (
unsigned int q = 0; q < quadrature.size(); ++q)
328 sum += fe_values.shape_value(i, q) *
329 fe_values.shape_value(j, q) * fe_values.JxW(q);
330 mass_inverse(i, j) = sum;
331 lumped_mass(i) += sum;
333 lumped_mass_inverse(i) = Number(1.) / lumped_mass(i);
334 total_mass += lumped_mass(i);
336 mass_inverse.gauss_jordan();
340 bounds = limiter_view.fully_relax_bounds(bounds, total_mass);
342 std::vector<state_type> pij_matrix(n_dofs_per_cell *
344 dealii::FullMatrix<Number> lij_matrix(n_dofs_per_cell,
347 const auto kappa_inverse = Number(n_dofs_per_cell);
348 const auto kappa = Number(1.) / kappa_inverse;
350 for (
unsigned int i = 0; i < n_dofs_per_cell; ++i) {
351 const state_type U_i = lumped_mass_inverse(i) * local_rhs[i];
352 state_values[i] = U_i;
354 for (
unsigned int j = 0; j < n_dofs_per_cell; ++j) {
355 const auto kronecker_ij = Number(i == j ? 1. : 0.);
357 lumped_mass(i) * mass_inverse(i, j) - kronecker_ij;
359 lumped_mass(j) * mass_inverse(i, j) - kronecker_ij;
360 const auto P_ij = kappa_inverse * lumped_mass_inverse(i) *
361 (b_ij * local_rhs[j] - b_ji * local_rhs[i]);
362 pij_matrix[n_dofs_per_cell * i + j] = P_ij;
368 const auto n_iterations = limiter_view.iterations();
369 for (
unsigned int pass = 0; pass < n_iterations; ++pass) {
371 for (
unsigned int i = 0; i < n_dofs_per_cell; ++i) {
372 const auto &U_i = state_values[i];
374 for (
unsigned int j = 0; j < n_dofs_per_cell; ++j) {
375 const auto &P_ij = pij_matrix[n_dofs_per_cell * i + j];
376 const auto &[l_ij, check] =
377 limiter_view.limit(bounds, U_i, P_ij);
378 lij_matrix(i, j) = l_ij;
382 for (
unsigned int i = 0; i < n_dofs_per_cell; ++i) {
383 auto &U_i = state_values[i];
385 for (
unsigned int j = 0; j < n_dofs_per_cell; ++j) {
387 std::min(lij_matrix(i, j), lij_matrix(j, i));
388 auto &P_ij = pij_matrix[n_dofs_per_cell * i + j];
389 U_i += kappa * l_ij * P_ij;
393#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK
395 hyperbolic_system_->template view<dim, Number>();
397 view.is_admissible(U_i),
399 "Error: inadmissible state encountered in "
400 "register_data_attach / children_will_be_coarsened"));
406 case dealii::CellStatus::cell_invalid:
407 Assert(
false, dealii::ExcInternalError());
417 std::vector<Number> dynamic_values;
419 if (!U_dynamic.empty()) {
420 const auto dof_cell_cg =
421 typename dealii::DoFHandler<dim>::cell_iterator(
422 &cell->get_triangulation(),
425 &offline_data_->dof_handler_cg());
427 const auto n_dofs_per_cell_cg =
428 dof_cell_cg->get_fe().n_dofs_per_cell();
429 dealii::Vector<Number> local_values(n_dofs_per_cell_cg);
431 for (
const auto &U_k : U_dynamic) {
432 dof_cell_cg->get_interpolated_dof_values(U_k.deal_ii_vector(),
434 dynamic_values.insert(std::end(dynamic_values),
435 std::begin(local_values),
436 std::end(local_values));
440 return pack_state_values(state_values, dynamic_values);
452 <<
"SolutionTransfer<HyperbolicDescription, dim, Number>::project()"
456 move_to_memory_space<dealii::MemorySpace::Host>(
462 auto &triangulation = discretization_->triangulation();
465 this->handle_ != dealii::numbers::invalid_unsigned_int,
467 "Cannot project() a state vector without valid handle. "
468 "prepare_projection() or set_handle() have to be called first."));
470 const auto &scalar_partitioner = offline_data_->scalar_partitioner();
471 const auto &affine_constraints = offline_data_->affine_constraints();
472 const auto n_locally_owned = offline_data_->n_locally_owned();
475 projected_mass.reinit(offline_data_->scalar_partitioner());
478 offline_data_->hyperbolic_vector_partitioner(), problem_dimension);
486 std::map<std::tuple<
unsigned int ,
unsigned int >,
state_type>
488 std::map<
unsigned int ,
Bounds> bounds_map;
491 kappa.reinit(offline_data_->scalar_partitioner());
504 auto &U_dynamic = new_state_vector.dynamic_state;
505 for (
auto &U_dynamic_k : U_dynamic)
506 U_dynamic_k.deal_ii_vector().zero_out_ghost_values();
508 triangulation.notify_ready_to_unpack(
510 [
this, &projected_mass, &projected_state, &U_dynamic](
512 const dealii::CellStatus status,
513 const auto &data_range) {
514 const auto &dof_handler = offline_data_->dof_handler();
515 const auto dof_cell =
typename dealii::DoFHandler<dim>::cell_iterator(
516 &cell->get_triangulation(),
525 const auto n_dofs_per_cell = dof_cell->get_fe().n_dofs_per_cell();
526 std::vector<dealii::types::global_dof_index> dof_indices(
529 const auto [state_values, dynamic_values] =
530 unpack_state_values<state_type, Number>(data_range,
533 Assert(dynamic_values.empty() == U_dynamic.empty(),
534 dealii::ExcMessage(
"The attached data does not match the "
535 "dynamic state of the state vector."));
543 if (!U_dynamic.empty()) {
544 const auto dof_cell_cg =
545 typename dealii::DoFHandler<dim>::cell_iterator(
546 &cell->get_triangulation(),
549 &offline_data_->dof_handler_cg());
551 const auto n_dofs_per_cell_cg =
552 dof_cell_cg->get_fe().n_dofs_per_cell();
554 Assert(dynamic_values.size() ==
555 U_dynamic.size() * n_dofs_per_cell_cg,
557 "The attached data has " +
558 std::to_string(dynamic_values.size()) +
559 " dynamic state values for this cell, but " +
560 std::to_string(U_dynamic.size()) +
561 " dynamic state components with " +
562 std::to_string(n_dofs_per_cell_cg) +
563 " degrees of freedom per cell require " +
564 std::to_string(U_dynamic.size() * n_dofs_per_cell_cg) +
567 dealii::Vector<Number> local_values(n_dofs_per_cell_cg);
568 for (std::size_t k = 0; k < U_dynamic.size(); ++k) {
569 std::copy_n(std::begin(dynamic_values) + k * n_dofs_per_cell_cg,
571 std::begin(local_values));
572 dof_cell_cg->set_dof_values_by_interpolation(
573 local_values, U_dynamic[k].deal_ii_vector());
578 case dealii::CellStatus::cell_will_persist:
580 case dealii::CellStatus::children_will_be_coarsened: {
586 Assert(dof_cell->is_active(), dealii::ExcInternalError());
587 dof_cell->get_dof_indices(dof_indices);
589 const auto &discretization = offline_data_->discretization();
590 const auto index = dof_cell->active_fe_index();
591 const auto &finite_element = discretization.finite_element()[index];
592 const auto &mapping = discretization.mapping()[index];
593 const auto &quadrature = discretization.quadrature()[index];
595 dealii::FEValues<dim> fe_values(mapping,
598 dealii::update_values |
599 dealii::update_JxW_values);
601 fe_values.reinit(dof_cell);
603 dealii::Vector<double> mi(n_dofs_per_cell);
604 for (
unsigned int i = 0; i < n_dofs_per_cell; ++i) {
606 for (
unsigned int q = 0; q < quadrature.size(); ++q)
607 sum += fe_values.shape_value(i, q) * fe_values.JxW(q);
611 for (
unsigned int i = 0; i < n_dofs_per_cell; ++i) {
612 const auto global_i = dof_indices[i];
613 add_tensor(projected_state, mi(i) * state_values[i], global_i);
614 projected_mass(global_i) += mi(i);
619 case dealii::CellStatus::cell_will_be_refined: {
625 Assert(dof_cell->has_children(), dealii::ExcInternalError());
627 const auto &discretization = offline_data_->discretization();
628 const auto index = dof_cell->active_fe_index();
629 const auto &finite_element = discretization.finite_element()[index];
630 const auto &mapping = discretization.mapping()[index];
631 const auto &quadrature = discretization.quadrature()[index];
633 dealii::FEValues<dim> fe_values(
637 dealii::update_values | dealii::update_JxW_values |
638 dealii::update_quadrature_points);
640 const auto polynomial_space =
641 dealii::internal::FEPointEvaluation::get_polynomial_space(
643 std::vector<dealii::Point<dim, Number>> unit_points(
649 std::vector<dealii::Point<dim>> unit_points_temp(
650 std::is_same_v<Number, float> ? quadrature.size() : 0);
652 dealii::FullMatrix<double> mass_inverse(n_dofs_per_cell,
654 dealii::Vector<double> lumped_mass(n_dofs_per_cell);
655 std::vector<state_type> local_rhs(n_dofs_per_cell);
657 for (
unsigned int child = 0; child < dof_cell->n_children();
659 const auto child_cell = dof_cell->child(child);
661 Assert(child_cell->is_active(), dealii::ExcInternalError());
662 Assert(dof_cell->active_fe_index() ==
663 child_cell->active_fe_index(),
664 dealii::ExcMessage(
"SolutionTransfer: projection between "
665 "different FE space is not set up."));
667 child_cell->get_dof_indices(dof_indices);
671 fe_values.reinit(child_cell);
673 if constexpr (std::is_same_v<Number, float>) {
674 mapping.transform_points_real_to_unit_cell(
676 fe_values.get_quadrature_points(),
678 std::transform(std::begin(unit_points_temp),
679 std::end(unit_points_temp),
680 std::begin(unit_points),
681 [](
const auto &x) {
return x; });
683 mapping.transform_points_real_to_unit_cell(
684 dof_cell, fe_values.get_quadrature_points(), unit_points);
687 for (
auto &it : local_rhs)
690 for (
unsigned int q = 0; q < quadrature.size(); ++q) {
691 Assert(finite_element.degree == 1, dealii::ExcNotImplemented());
693 dealii::internal::evaluate_tensor_product_value(
695 make_const_array_view(state_values),
698 coefficient *= fe_values.JxW(q);
700 for (
unsigned int i = 0; i < n_dofs_per_cell; ++i)
701 local_rhs[i] += coefficient * fe_values.shape_value(i, q);
706 mass_inverse = Number(0.);
707 lumped_mass = Number(0.);
708 for (
unsigned int i = 0; i < n_dofs_per_cell; ++i) {
709 for (
unsigned int j = 0; j < n_dofs_per_cell; ++j) {
711 for (
unsigned int q = 0; q < quadrature.size(); ++q)
712 sum += fe_values.shape_value(i, q) *
713 fe_values.shape_value(j, q) * fe_values.JxW(q);
714 mass_inverse(i, j) = sum;
715 lumped_mass(i) += sum;
718 mass_inverse.gauss_jordan();
722 for (
unsigned int i = 0; i < n_dofs_per_cell; ++i) {
724 for (
unsigned int j = 0; j < n_dofs_per_cell; ++j) {
725 U_i += mass_inverse(i, j) * local_rhs[j];
728#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK
730 hyperbolic_system_->template view<dim, Number>();
731 AssertThrow(view.is_admissible(U_i),
733 "Error: inadmissible state encountered in "
734 "ready_to_unpack / cell_will_be_refined"));
736 const auto global_i = dof_indices[i];
737 add_tensor(projected_state, lumped_mass(i) * U_i, global_i);
738 projected_mass(global_i) += lumped_mass(i);
744 case dealii::CellStatus::cell_invalid:
745 Assert(
false, dealii::ExcInternalError());
756 for (
auto &U_dynamic_k : U_dynamic) {
757 auto &vector = U_dynamic_k.deal_ii_vector();
758 vector.compress(dealii::VectorOperation::insert);
759 offline_data_->affine_constraints_cg().distribute(vector);
760 vector.update_ghost_values();
763 const auto projected_state_view =
764 projected_state.template view<problem_dimension>();
766 projected_mass.compress(dealii::VectorOperation::add);
767 projected_state_view.compress(dealii::VectorOperation::add);
783 const auto new_U_view =
784 new_state_vector.state.template view<problem_dimension>();
790 const auto update_new_state_vector = [&]() {
791 for (
unsigned int local_i = 0; local_i < n_locally_owned; ++local_i) {
793 const auto mU_i = projected_state_view.read_tensor(local_i);
794 const auto m_i = projected_mass.local_element(local_i);
796#ifdef DEBUG_EXPENSIVE_BOUNDS_CHECK
797 const auto view = hyperbolic_system_->template view<dim, Number>();
799 view.is_admissible(mU_i / m_i),
800 dealii::ExcMessage(
"Error: inadmissible state encountered in "
801 "update_new_state_vector()"));
804 new_U_view.write_tensor(mU_i / m_i, local_i);
806 new_U_view.update_ghost_values();
809 update_new_state_vector();
811 const auto precomputed_view =
812 new_state_vector.precomputed.template view<n_precomputed_values>();
815 const auto update_precomputed_values = [&]() {
816 new_U_view.update_ghost_values();
817 hyperbolic_system_->fill_precomputed_values(
818 *offline_data_, new_state_vector,
false);
819 precomputed_view.update_ghost_values();
822 update_precomputed_values();
824 const auto limiter_view = limiter_.template view<dim, Number>();
837 for (
const auto &line : affine_constraints.get_lines()) {
838 const auto global_i = line.index;
839 const auto local_i = scalar_partitioner->global_to_local(global_i);
842 if (local_i >= n_locally_owned)
846 const auto m_i_star = projected_mass.local_element(local_i);
847 const auto U_i_star =
848 projected_state_view.read_tensor(local_i) / m_i_star;
850 auto &bounds = bounds_map[local_i];
851 bounds = limiter_view.projection_bounds_from_state(
852 precomputed_view, local_i, U_i_star);
856 for (
const auto &[global_k, c_k] : line.entries) {
857 const auto local_k = scalar_partitioner->global_to_local(global_k);
858 U_i_interp += c_k * new_U_view.read_tensor(local_k);
862 for (
const auto &[global_k, c_k] : line.entries) {
863 const auto local_k = scalar_partitioner->global_to_local(global_k);
864 const auto U_k = new_U_view.read_tensor(local_k);
866 const auto bounds_k = limiter_view.projection_bounds_from_state(
867 precomputed_view, local_k, U_k);
868 bounds = limiter_view.combine_bounds(bounds, bounds_k);
870 projected_state_view.add_tensor(c_k * m_i_star * U_i_star, local_k);
871 projected_mass.local_element(local_k) += c_k * m_i_star;
873 kappa.local_element(local_k) += Number(1.);
874 pik_matrix[{local_i, local_k}] = c_k * m_i_star * (U_k - U_i_interp);
879 projected_mass.compress(dealii::VectorOperation::add);
880 projected_state_view.compress(dealii::VectorOperation::add);
881 kappa.compress(dealii::VectorOperation::add);
882 update_new_state_vector();
885 projected_mass.update_ghost_values();
886 kappa.update_ghost_values();
890 const auto n_iterations = limiter_view.iterations();
891 for (
unsigned int pass = 0; pass < n_iterations; ++pass) {
894 update_precomputed_values();
896 for (
const auto &line : affine_constraints.get_lines()) {
897 const auto global_i = line.index;
898 const auto local_i = scalar_partitioner->global_to_local(global_i);
901 if (local_i >= n_locally_owned)
917 auto &bounds = bounds_map[local_i];
918 auto total_mass = Number(0.);
919 for (
const auto &[global_k, c_k] : line.entries) {
920 const auto local_k = scalar_partitioner->global_to_local(global_k);
921 const auto U_k = new_U_view.read_tensor(local_k);
922 const auto bounds_k = limiter_view.projection_bounds_from_state(
923 precomputed_view, local_k, U_k);
924 bounds = limiter_view.combine_bounds(bounds, bounds_k);
926 const auto m_k = projected_mass.local_element(local_k);
933 const auto relaxed_bounds =
934 limiter_view.fully_relax_bounds(bounds, total_mass);
938 for (
const auto &[global_k, c_k] : line.entries) {
939 const auto local_k = scalar_partitioner->global_to_local(global_k);
940 const auto kappa_k = kappa.local_element(local_k);
941 const auto m_k = projected_mass.local_element(local_k);
942 const auto U_k = new_U_view.read_tensor(local_k);
943 const auto P_ik = pik_matrix[{local_i, local_k}] * kappa_k / m_k;
945 const auto &[l_k, check] =
946 limiter_view.limit(relaxed_bounds, U_k, P_ik);
947 l = std::min(l, l_k);
952 for (
const auto &[global_k, c_k] : line.entries) {
953 const auto local_k = scalar_partitioner->global_to_local(global_k);
954 auto &mP_ik = pik_matrix[{local_i, local_k}];
955 projected_state_view.add_tensor(l * mP_ik, local_k);
961 projected_state_view.compress(dealii::VectorOperation::add);
962 update_new_state_vector();
966 for (
unsigned int local_i = 0; local_i < n_locally_owned; ++local_i) {
967 const auto global_i = scalar_partitioner->local_to_global(local_i);
968 if (affine_constraints.is_constrained(global_i))
969 new_U_view.write_tensor(
state_type{}, local_i);
971 new_U_view.update_ghost_values();
973#ifdef DEBUG_SYMMETRY_CHECK
977 const auto &lumped_mass_matrix = offline_data_->lumped_mass_matrix();
978 for (
unsigned int local_i = 0; local_i < n_locally_owned; ++local_i) {
979 const auto global_i = scalar_partitioner->local_to_global(local_i);
980 if (affine_constraints.is_constrained(global_i))
983 const auto m_i = projected_mass.local_element(local_i);
984 const auto m_i_reference = lumped_mass_matrix.view().read_entry(local_i);
985 Assert(std::abs(m_i - m_i_reference) < 1.e-10,
987 "SolutionTransfer::projection(): something went wrong. Final "
988 "masses do not agree with those computed in OfflineData."));