ExaDG
Loading...
Searching...
No Matches
solution_projection_between_triangulations.h
1/* ______________________________________________________________________
2 *
3 * ExaDG - High-Order Discontinuous Galerkin for the Exa-Scale
4 *
5 * Copyright (C) 2025 by the ExaDG authors
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 * ______________________________________________________________________
20 */
21
22#ifndef EXADG_OPERATORS_SOLUTION_PROJECTION_BETWEEN_TRIANGULATIONS_H_
23#define EXADG_OPERATORS_SOLUTION_PROJECTION_BETWEEN_TRIANGULATIONS_H_
24
25// C/C++
26#include <algorithm>
27
28// deal.II
29#include <deal.II/base/conditional_ostream.h>
30#include <deal.II/base/mpi_remote_point_evaluation.h>
31#include <deal.II/matrix_free/matrix_free.h>
32#include <deal.II/numerics/vector_tools.h>
33
34// ExaDG
35#include <exadg/matrix_free/integrators.h>
36#include <exadg/matrix_free/matrix_free_data.h>
37#include <exadg/operators/inverse_mass_operator.h>
38#include <exadg/operators/inverse_mass_parameters.h>
39#include <exadg/operators/mapping_flags.h>
40#include <exadg/operators/quadrature.h>
41#include <exadg/postprocessor/write_output.h>
42
43namespace ExaDG
44{
45namespace GridToGridProjection
46{
47// Parameters controlling the grid-to-grid projection.
48template<int dim>
49struct GridToGridProjectionData
50{
51 GridToGridProjectionData()
52 : rpe_data(),
53 solver_data(),
54 preconditioner(PreconditionerMass::PointJacobi),
55 inverse_mass_type(InverseMassType::MatrixfreeOperator),
56 additional_quadrature_points(1),
57 is_test(false)
58 {
59 }
60
61 void
62 print(dealii::ConditionalOStream const & pcout) const
63 {
64 print_parameter(pcout, "RPE tolerance", rpe_data.tolerance);
65 print_parameter(pcout, "RPE enforce unique mapping", rpe_data.enforce_unique_mapping);
66 print_parameter(pcout, "RPE rtree level", rpe_data.rtree_level);
67 }
68
69 typename dealii::Utilities::MPI::RemotePointEvaluation<dim>::AdditionalData::AdditionalData
70 rpe_data;
71 SolverData solver_data;
72 PreconditionerMass preconditioner;
73 InverseMassType inverse_mass_type;
74
75 // Number of additional integration points used for sampling the source grid.
76 // The default `additional_quadrature_points = 1` considers `fe_degree + 1` quadrature points in
77 // 1D using the `fe_degree` of the target grid's finite element.
78 unsigned int additional_quadrature_points;
79
80 // Toggle iteration count and timing output.
81 bool is_test;
82};
83
88template<int dim, int n_components, typename Number>
89std::vector<dealii::Point<dim>>
90collect_integration_points(
91 dealii::MatrixFree<dim, Number, dealii::VectorizedArray<Number>> const & matrix_free,
92 unsigned int const dof_index,
93 unsigned int const quad_index)
94{
95 CellIntegrator<dim, n_components, Number> fe_eval(matrix_free, dof_index, quad_index);
96
97 // Conservative estimate for the number of points.
98 std::vector<dealii::Point<dim>> integration_points;
99 integration_points.reserve(
100 matrix_free.get_dof_handler(dof_index).get_triangulation().n_active_cells() *
101 fe_eval.n_q_points);
102
103 for(unsigned int cell_batch_idx = 0; cell_batch_idx < matrix_free.n_cell_batches();
104 ++cell_batch_idx)
105 {
106 fe_eval.reinit(cell_batch_idx);
107 for(const unsigned int q : fe_eval.quadrature_point_indices())
108 {
109 dealii::Point<dim, dealii::VectorizedArray<Number>> const cell_batch_points =
110 fe_eval.quadrature_point(q);
111 for(unsigned int i = 0; i < dealii::VectorizedArray<Number>::size(); ++i)
112 {
113 dealii::Point<dim> p;
114 for(unsigned int d = 0; d < dim; ++d)
115 {
116 p[d] = cell_batch_points[d][i];
117 }
118 integration_points.push_back(p);
119 }
120 }
121 }
122
123 return integration_points;
124}
125
130template<int dim, int n_components, typename Number, typename VectorType>
131VectorType
132assemble_projection_rhs(
133 dealii::MatrixFree<dim, Number, dealii::VectorizedArray<Number>> const & matrix_free,
134 std::vector<
135 typename dealii::FEPointEvaluation<n_components, dim, dim, Number>::value_type> const &
136 values_source_in_q_points_target,
137 unsigned int const dof_index,
138 unsigned int const quad_index)
139{
140 VectorType system_rhs;
141 matrix_free.initialize_dof_vector(system_rhs, dof_index);
142
143 CellIntegrator<dim, n_components, Number> fe_eval(matrix_free, dof_index, quad_index);
144
145 unsigned int idx_q_point = 0;
146
147 for(unsigned int cell_batch_idx = 0; cell_batch_idx < matrix_free.n_cell_batches();
148 ++cell_batch_idx)
149 {
150 fe_eval.reinit(cell_batch_idx);
151 for(unsigned int const q : fe_eval.quadrature_point_indices())
152 {
153 dealii::Tensor<1, n_components, dealii::VectorizedArray<Number>> tmp;
154
155 for(unsigned int i = 0; i < dealii::VectorizedArray<Number>::size(); ++i)
156 {
157 typename dealii::FEPointEvaluation<n_components, dim, dim, Number>::value_type const
158 values = values_source_in_q_points_target[idx_q_point];
159
160 // Increment index into `values_source_in_q_points_target`, meaning that the sequence of
161 // function values in integration points need to match the particular sequence here.
162 ++idx_q_point;
163
164 if constexpr(n_components == 1)
165 {
166 tmp[0][i] = values;
167 }
168 else
169 {
170 for(unsigned int c = 0; c < n_components; ++c)
171 {
172 tmp[c][i] = values[c];
173 }
174 }
175 }
176
177 fe_eval.submit_value(tmp, q);
178 }
179 fe_eval.integrate(dealii::EvaluationFlags::values);
180 fe_eval.distribute_local_to_global(system_rhs);
181 }
182 system_rhs.compress(dealii::VectorOperation::add);
183
184 return system_rhs;
185}
186
191template<int dim, typename Number, int n_components, typename VectorType>
192void
193project_vectors(
194 std::vector<VectorType *> const & source_vectors,
195 dealii::DoFHandler<dim> const & source_dof_handler,
196 std::shared_ptr<dealii::Mapping<dim> const> const & source_mapping,
197 std::vector<VectorType *> const & target_vectors,
198 dealii::MatrixFree<dim, Number, dealii::VectorizedArray<Number>> const & target_matrix_free,
199 unsigned int const dof_index,
200 unsigned int const quad_index,
202{
203 // Setup inverse mass operator.
204 InverseMassOperatorData<Number> inverse_mass_operator_data;
205 inverse_mass_operator_data.dof_index = dof_index;
206 inverse_mass_operator_data.quad_index = quad_index;
207 inverse_mass_operator_data.parameters.preconditioner = PreconditionerMass::PointJacobi;
208 inverse_mass_operator_data.parameters.solver_data = data.solver_data;
209 inverse_mass_operator_data.parameters.implementation_type = data.inverse_mass_type;
210
211 InverseMassOperator<dim, n_components, Number> inverse_mass_operator;
212 inverse_mass_operator.initialize(target_matrix_free, inverse_mass_operator_data);
213
214 dealii::Utilities::MPI::RemotePointEvaluation<dim> rpe_source(data.rpe_data);
215
216 // The sequence of integration points follows from the sequence of points as encountered during
217 // cell batch loop.
218 std::vector<dealii::Point<dim>> integration_points_target =
219 collect_integration_points<dim, n_components, Number>(target_matrix_free,
220 dof_index,
221 quad_index);
222
223 rpe_source.reinit(integration_points_target,
224 source_dof_handler.get_triangulation(),
225 *source_mapping);
226
227 if(not rpe_source.all_points_found())
228 {
229 write_points_in_dummy_triangulation(
230 integration_points_target, "./", "all_points", 0, source_dof_handler.get_mpi_communicator());
231
232 std::vector<dealii::Point<dim>> points_not_found;
233 points_not_found.reserve(integration_points_target.size());
234 for(unsigned int i = 0; i < integration_points_target.size(); ++i)
235 {
236 if(not rpe_source.point_found(i))
237 {
238 points_not_found.push_back(integration_points_target[i]);
239 }
240 }
241
242 write_points_in_dummy_triangulation(
243 points_not_found, "./", "points_not_found", 0, source_dof_handler.get_mpi_communicator());
244
245 AssertThrow(rpe_source.all_points_found(),
246 dealii::ExcMessage(
247 "Could not interpolate source grid vector in target grid. "
248 "Points exported to `./all_points.pvtu` and `./points_not_found.pvtu`"));
249 }
250
251 // Loop over vectors and project.
252 for(unsigned int i = 0; i < target_vectors.size(); ++i)
253 {
254 // Evaluate the source vector at the target integration points.
255 VectorType const & source_vector = *source_vectors.at(i);
256 source_vector.update_ghost_values();
257
258 std::vector<
259 typename dealii::FEPointEvaluation<n_components, dim, dim, Number>::value_type> const
260 values_source_in_q_points_target = dealii::VectorTools::point_values<n_components>(
261 rpe_source, source_dof_handler, source_vector, dealii::VectorTools::EvaluationFlags::avg);
262
263 // Assemble right hand side vector for the projection.
264 VectorType system_rhs = assemble_projection_rhs<dim, n_components, Number, VectorType>(
265 target_matrix_free, values_source_in_q_points_target, dof_index, quad_index);
266
267 // Solve linear system starting from zero initial guess.
268 VectorType sol;
269 sol.reinit(system_rhs, false /* omit_zeroing_entries */);
270
271 inverse_mass_operator.apply(sol, system_rhs);
272
273 // Copy solution to target vector.
274 *target_vectors[i] = sol;
275 }
276}
277
283template<int dim, typename Number, typename VectorType>
284void
285do_grid_to_grid_projection(
286 std::vector<std::vector<VectorType *>> const & source_vectors_per_dof_handler,
287 std::vector<dealii::DoFHandler<dim> const *> const & source_dof_handlers,
288 std::shared_ptr<dealii::Mapping<dim> const> const & source_mapping,
289 std::vector<std::vector<VectorType *>> & target_vectors_per_dof_handler,
290 std::vector<dealii::DoFHandler<dim> const *> const & target_dof_handlers,
291 dealii::MatrixFree<dim, Number, dealii::VectorizedArray<Number>> const & matrix_free,
293{
294 // Check input dimensions.
295 AssertThrow(source_vectors_per_dof_handler.size() == source_dof_handlers.size(),
296 dealii::ExcMessage("First dimension of source vector of vectors "
297 "has to match source DoFHandler count."));
298 AssertThrow(target_vectors_per_dof_handler.size() == target_dof_handlers.size(),
299 dealii::ExcMessage("First dimension of target vector of vectors "
300 "has to match target DoFHandler count."));
301 AssertThrow(source_dof_handlers.size() == target_dof_handlers.size(),
302 dealii::ExcMessage("Target and source DoFHandler counts have to match"));
303 AssertThrow(source_vectors_per_dof_handler.size() > 0,
304 dealii::ExcMessage("Vector of source vectors empty."));
305 for(unsigned int i = 0; i < source_vectors_per_dof_handler.size(); ++i)
306 {
307 AssertThrow(source_vectors_per_dof_handler[i].size() ==
308 target_vectors_per_dof_handler.at(i).size(),
309 dealii::ExcMessage("Vectors of source and target vectors need to have same size."));
310 }
311
312 // Project vectors per `dealii::DoFHandler`.
313 for(unsigned int i = 0; i < target_dof_handlers.size(); ++i)
314 {
315 unsigned int const n_components = target_dof_handlers[i]->get_fe().n_components();
316 if(n_components == 1)
317 {
318 project_vectors<dim, Number, 1 /* n_components */, VectorType>(
319 source_vectors_per_dof_handler.at(i),
320 *source_dof_handlers.at(i),
321 source_mapping,
322 target_vectors_per_dof_handler.at(i),
323 matrix_free,
324 i /* dof_index */,
325 i /* quad_index */,
326 data);
327 }
328 else if(n_components == dim)
329 {
330 project_vectors<dim, Number, dim /* n_components */, VectorType>(
331 source_vectors_per_dof_handler.at(i),
332 *source_dof_handlers.at(i),
333 source_mapping,
334 target_vectors_per_dof_handler.at(i),
335 matrix_free,
336 i /* dof_index */,
337 i /* quad_index */,
338 data);
339 }
340 else if(n_components == dim + 2)
341 {
342 project_vectors<dim, Number, dim + 2 /* n_components */, VectorType>(
343 source_vectors_per_dof_handler.at(i),
344 *source_dof_handlers.at(i),
345 source_mapping,
346 target_vectors_per_dof_handler.at(i),
347 matrix_free,
348 i /* dof_index */,
349 i /* quad_index */,
350 data);
351 }
352 else
353 {
354 AssertThrow(n_components == 1 or n_components == dim,
355 dealii::ExcMessage("The requested number of components is not"
356 "supported in `grid_to_grid_projection()`."));
357 }
358 }
359}
360
364template<int dim, typename Number, typename VectorType>
365void
366grid_to_grid_projection(
367 std::vector<std::vector<VectorType *>> const & source_vectors_per_dof_handler,
368 std::vector<dealii::DoFHandler<dim> const *> const & source_dof_handlers,
369 std::shared_ptr<dealii::Mapping<dim> const> const & source_mapping,
370 std::vector<std::vector<VectorType *>> & target_vectors_per_dof_handler,
371 std::vector<dealii::DoFHandler<dim> const *> const & target_dof_handlers,
372 std::shared_ptr<dealii::Mapping<dim> const> const & target_mapping,
374{
375 // Setup a single `dealii::MatrixFree` object with multiple `dealii::DoFHandler`s.
376 MatrixFreeData<dim, Number> matrix_free_data;
377
378 MappingFlags mapping_flags;
379 mapping_flags.cells =
380 dealii::update_quadrature_points | dealii::update_values | dealii::update_JxW_values;
381 matrix_free_data.append_mapping_flags(mapping_flags);
382
383 dealii::AffineConstraints<Number> empty_constraints;
384 empty_constraints.clear();
385 empty_constraints.close();
386 for(unsigned int i = 0; i < target_dof_handlers.size(); ++i)
387 {
388 matrix_free_data.insert_dof_handler(target_dof_handlers[i], std::to_string(i));
389 matrix_free_data.insert_constraint(&empty_constraints, std::to_string(i));
390
391 ElementType element_type = get_element_type(target_dof_handlers[i]->get_triangulation());
392
393 std::shared_ptr<dealii::Quadrature<dim>> quadrature = create_quadrature<dim>(
394 element_type, target_dof_handlers[i]->get_fe().degree + data.additional_quadrature_points);
395
396 matrix_free_data.insert_quadrature(*quadrature, std::to_string(i));
397 }
398
399 dealii::MatrixFree<dim, Number, dealii::VectorizedArray<Number>> matrix_free;
400 matrix_free.reinit(*target_mapping,
401 matrix_free_data.get_dof_handler_vector(),
402 matrix_free_data.get_constraint_vector(),
403 matrix_free_data.get_quadrature_vector(),
404 matrix_free_data.data);
405
406 do_grid_to_grid_projection<dim, Number, VectorType>(source_vectors_per_dof_handler,
407 source_dof_handlers,
408 source_mapping,
409 target_vectors_per_dof_handler,
410 target_dof_handlers,
411 matrix_free,
412 data);
413}
414
415} // namespace GridToGridProjection
416} // namespace ExaDG
417
418#endif /* EXADG_OPERATORS_SOLUTION_PROJECTION_BETWEEN_TRIANGULATIONS_H_ */
Definition driver.cpp:33
std::shared_ptr< dealii::Quadrature< dim > > create_quadrature(ElementType const &element_type, unsigned int const n_q_points_1d)
Definition quadrature.h:40
ElementType get_element_type(dealii::Triangulation< dim > const &tria)
Definition grid_data.h:70
Definition solution_projection_between_triangulations.h:50
Definition solver_data.h:34