ExaDG
Loading...
Searching...
No Matches
inverse_mass_operator.h
1/* ______________________________________________________________________
2 *
3 * ExaDG - High-Order Discontinuous Galerkin for the Exa-Scale
4 *
5 * Copyright (C) 2021 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_INVERSE_MASS_OPERATOR_H_
23#define EXADG_OPERATORS_INVERSE_MASS_OPERATOR_H_
24
25// deal.II
26#include <deal.II/fe/fe_data.h>
27#include <deal.II/lac/la_parallel_vector.h>
28#include <deal.II/matrix_free/operators.h>
29
30// ExaDG
31#include <exadg/grid/grid_data.h>
32#include <exadg/matrix_free/integrators.h>
33#include <exadg/operators/inverse_mass_parameters.h>
34#include <exadg/operators/mass_operator.h>
35#include <exadg/operators/variable_coefficients.h>
36#include <exadg/solvers_and_preconditioners/preconditioners/block_jacobi_preconditioner.h>
37#include <exadg/solvers_and_preconditioners/preconditioners/jacobi_preconditioner.h>
38#include <exadg/solvers_and_preconditioners/preconditioners/preconditioner_amg.h>
39
40namespace ExaDG
41{
42template<typename Number>
43struct InverseMassOperatorData
44{
45 InverseMassOperatorData()
46 : dof_index(0),
47 quad_index(0),
48 coefficient_is_variable(false),
49 consider_inverse_coefficient(false),
50 variable_coefficients(nullptr)
51 {
52 }
53
54 // Get optimal in the sense of (most likely) fastest implementation type of the inverse mass
55 // operator depending on the approximation space.
56 template<int dim>
57 static InverseMassType
58 get_optimal_inverse_mass_type(dealii::FiniteElement<dim> const & fe)
59 {
60 if(fe.conforms(dealii::FiniteElementData<dim>::L2))
61 {
62 if(fe.reference_cell().is_hyper_cube())
63 {
64 return InverseMassType::MatrixfreeOperator;
65 }
66 else
67 {
68 return InverseMassType::ElementwiseKrylovSolver;
69 }
70 }
71 else
72 {
73 return InverseMassType::GlobalKrylovSolver;
74 }
75 }
76
77 // Parameters referring to dealii::MatrixFree
78 unsigned int dof_index;
79 unsigned int quad_index;
80
81 InverseMassParameters parameters;
82
83 // Enable variable coefficients.
84 bool coefficient_is_variable;
85
86 // Consider the regular form of the coefficient (1) or its inverse (2):
87 // (1) : (u_h , v_h * c)_Omega
88 // (2) : (u_h , v_h / c)_Omega
89 bool consider_inverse_coefficient;
90
91 VariableCoefficients<dealii::VectorizedArray<Number>> const * variable_coefficients;
92};
93
94template<int dim, int n_components, typename Number>
95class InverseMassOperator
96{
97private:
98 typedef dealii::LinearAlgebra::distributed::Vector<Number> VectorType;
99
100 typedef InverseMassOperator<dim, n_components, Number> This;
101
102 typedef CellIntegrator<dim, n_components, Number> Integrator;
103
104 // use a template parameter of -1 to select the precompiled version of this operator
105 typedef dealii::MatrixFreeOperators::CellwiseInverseMassMatrix<dim, -1, n_components, Number>
106 InverseMassAsMatrixFreeOperator;
107
108 typedef std::pair<unsigned int, unsigned int> Range;
109
110public:
111 InverseMassOperator() : matrix_free(nullptr), dof_index(0), quad_index(0)
112 {
113 }
114
115 unsigned int
116 get_n_iter_global_last() const
117 {
118 return this->n_iter_global_last;
119 }
120
121 void
122 initialize(dealii::MatrixFree<dim, Number> const & matrix_free_in,
123 InverseMassOperatorData<Number> const inverse_mass_operator_data,
124 dealii::AffineConstraints<Number> const * constraints = nullptr)
125 {
126 this->matrix_free = &matrix_free_in;
127 dof_index = inverse_mass_operator_data.dof_index;
128 quad_index = inverse_mass_operator_data.quad_index;
129
130 data = inverse_mass_operator_data.parameters;
131
132 coefficient_is_variable = inverse_mass_operator_data.coefficient_is_variable;
133 consider_inverse_coefficient = inverse_mass_operator_data.consider_inverse_coefficient;
134 variable_coefficients = inverse_mass_operator_data.variable_coefficients;
135
136 // Variable coefficients only implemented for the matrix-free operator.
137 AssertThrow(not coefficient_is_variable or variable_coefficients != nullptr,
138 dealii::ExcMessage("Pointer to variable coefficients not set properly."));
139
140 dealii::FiniteElement<dim> const & fe = matrix_free->get_dof_handler(dof_index).get_fe();
141
142 // Some implementation variants of the inverse mass operator are based on assumptions on the
143 // discretization and more efficient choices are available for tensor-product elements or
144 // L2-conforming spaces.
145 if(data.implementation_type == InverseMassType::MatrixfreeOperator)
146 {
147 AssertThrow(
148 fe.base_element(0).dofs_per_cell == dealii::Utilities::pow(fe.degree + 1, dim),
149 dealii::ExcMessage(
150 "The matrix-free cell-wise inverse mass operator is currently only available for isotropic tensor-product elements."));
151
152 AssertThrow(
153 this->matrix_free->get_shape_info(0, quad_index).data[0].n_q_points_1d == fe.degree + 1,
154 dealii::ExcMessage(
155 "The matrix-free cell-wise inverse mass operator is currently only available if n_q_points_1d = n_nodes_1d."));
156
157 AssertThrow(
158 fe.conforms(dealii::FiniteElementData<dim>::L2),
159 dealii::ExcMessage(
160 "The matrix-free cell-wise inverse mass operator is only available for L2-conforming elements."));
161 }
162 else
163 {
164 // Setup MassOperator as underlying operator for cell-wise direct/iterative inverse or global
165 // solve.
166 MassOperatorData<dim, Number> mass_operator_data;
167 mass_operator_data.dof_index = dof_index;
168 mass_operator_data.quad_index = quad_index;
169 mass_operator_data.coefficient_is_variable = coefficient_is_variable;
170 mass_operator_data.variable_coefficients = variable_coefficients;
171 mass_operator_data.consider_inverse_coefficient = consider_inverse_coefficient;
172
173 if(data.implementation_type == InverseMassType::ElementwiseKrylovSolver)
174 {
175 mass_operator_data.implement_block_diagonal_preconditioner_matrix_free = true;
176 mass_operator_data.solver_block_diagonal = Elementwise::Solver::CG;
177 if(inverse_mass_operator_data.parameters.preconditioner == PreconditionerMass::None)
178 {
179 mass_operator_data.preconditioner_block_diagonal = Elementwise::Preconditioner::None;
180 }
181 else if(inverse_mass_operator_data.parameters.preconditioner ==
182 PreconditionerMass::PointJacobi)
183 {
184 mass_operator_data.preconditioner_block_diagonal =
185 Elementwise::Preconditioner::PointJacobi;
186 }
187 mass_operator_data.solver_data_block_diagonal =
188 inverse_mass_operator_data.parameters.solver_data;
189 }
190
191 // Use constraints if provided.
192 if(constraints == nullptr)
193 {
194 dealii::AffineConstraints<Number> dummy_constraints;
195 dummy_constraints.close();
196 mass_operator.initialize(*matrix_free, dummy_constraints, mass_operator_data);
197 }
198 else
199 {
200 mass_operator.initialize(*matrix_free, *constraints, mass_operator_data);
201 }
202
203 if(data.implementation_type == InverseMassType::ElementwiseKrylovSolver or
204 data.implementation_type == InverseMassType::BlockMatrices)
205 {
206 // Non-L2-conforming elements are asserted here because the cell-wise inverse neglecting
207 // cell-coupling terms is only an approximation of the inverse mass matrix.
208 AssertThrow(
209 fe.conforms(dealii::FiniteElementData<dim>::L2),
210 dealii::ExcMessage(
211 "The cell-wise inverse mass operator is only available for L2-conforming elements."));
212
213 block_jacobi_preconditioner =
214 std::make_shared<BlockJacobiPreconditioner<MassOperator<dim, n_components, Number>>>(
215 mass_operator, true /* initialize_preconditioner */);
216 }
217 else if(data.implementation_type == InverseMassType::GlobalKrylovSolver)
218 {
219 Krylov::SolverDataCG solver_data;
220 solver_data.max_iter = inverse_mass_operator_data.parameters.solver_data.max_iter;
221 solver_data.solver_tolerance_abs =
222 inverse_mass_operator_data.parameters.solver_data.abs_tol;
223 solver_data.solver_tolerance_rel =
224 inverse_mass_operator_data.parameters.solver_data.rel_tol;
225
226 solver_data.use_preconditioner =
227 inverse_mass_operator_data.parameters.preconditioner != PreconditionerMass::None;
228 if(inverse_mass_operator_data.parameters.preconditioner == PreconditionerMass::None)
229 {
230 // no setup required.
231 }
232 else if(inverse_mass_operator_data.parameters.preconditioner ==
233 PreconditionerMass::PointJacobi)
234 {
235 global_preconditioner =
236 std::make_shared<JacobiPreconditioner<MassOperator<dim, n_components, Number>>>(
237 mass_operator, true /* initialize_preconditioner */);
238 }
239 else if(inverse_mass_operator_data.parameters.preconditioner ==
240 PreconditionerMass::BlockJacobi)
241 {
242 global_preconditioner =
243 std::make_shared<BlockJacobiPreconditioner<MassOperator<dim, n_components, Number>>>(
244 mass_operator, true /* initialize_preconditioner */);
245 }
246 else if(inverse_mass_operator_data.parameters.preconditioner == PreconditionerMass::AMG)
247 {
248 global_preconditioner =
249 std::make_shared<PreconditionerAMG<MassOperator<dim, n_components, Number>, Number>>(
250 mass_operator,
251 true /* initialize_preconditioner */,
252 inverse_mass_operator_data.parameters.amg_data);
253 }
254 else
255 {
256 AssertThrow(false, dealii::ExcMessage("This `PreconditionerMass` is not implemented."));
257 }
258
259 global_solver = std::make_shared<Krylov::SolverCG<MassOperator<dim, n_components, Number>,
261 VectorType>>(mass_operator,
262 *global_preconditioner,
263 solver_data);
264 }
265 else
266 {
267 AssertThrow(false,
268 dealii::ExcMessage("The specified `InverseMassType` is not implemented."));
269 }
270 }
271 }
272
277 void
279 {
280 if(data.implementation_type == InverseMassType::MatrixfreeOperator)
281 {
282 // no updates needed as long as the MatrixFree object is up-to-date (which is not the
283 // responsibility of the present class).
284 }
285 else if(data.implementation_type == InverseMassType::ElementwiseKrylovSolver or
286 data.implementation_type == InverseMassType::BlockMatrices)
287 {
288 // the mass operator does not need to be updated as long as the MatrixFree object is
289 // up-to-date (which is not the responsibility of the present class).
290
291 // update the matrix-based components of the block-Jacobi preconditioner
292 block_jacobi_preconditioner->update();
293 }
294 else if(data.implementation_type == InverseMassType::GlobalKrylovSolver)
295 {
296 global_preconditioner->update();
297 }
298 else
299 {
300 AssertThrow(false, dealii::ExcMessage("The specified InverseMassType is not implemented."));
301 }
302 }
303
304 // dst = M^-1 * src
305 void
306 apply(VectorType & dst, VectorType const & src) const
307 {
308 if(data.implementation_type == InverseMassType::GlobalKrylovSolver)
309 {
310 AssertThrow(global_solver.get() != 0,
311 dealii::ExcMessage("Global mass solver has not been initialized."));
312 this->n_iter_global_last = global_solver->solve(dst, src);
313 }
314 else
315 {
316 dst.zero_out_ghost_values();
317
318 if(data.implementation_type == InverseMassType::MatrixfreeOperator)
319 {
320 matrix_free->cell_loop(&This::cell_loop_matrix_free_operator, this, dst, src);
321 }
322 else // ElementwiseKrylovSolver or BlockMatrices
323 {
324 AssertThrow(block_jacobi_preconditioner.get() != 0,
325 dealii::ExcMessage(
326 "Cell-wise iterative/direct block-Jacobi solver has not been initialized."));
327 block_jacobi_preconditioner->vmult(dst, src);
328 }
329 }
330 }
331
332 // dst = scaling_factor * (M^-1 * src)
333 void
334 apply_scale(VectorType & dst, double const scaling_factor, VectorType const & src) const
335 {
336 if(data.implementation_type == InverseMassType::MatrixfreeOperator)
337 {
338 // In the InverseMassType::MatrixfreeOperator case we can avoid
339 // streaming the vector from memory twice.
340
341 // ghost have to be zeroed out before MatrixFree::cell_loop().
342 dst.zero_out_ghost_values();
343
344 matrix_free->cell_loop(
345 &This::cell_loop_matrix_free_operator,
346 this,
347 dst,
348 src,
349 /*operation before cell operation*/ {}, /*operation after cell operation*/
350 [&](const unsigned int start_range, const unsigned int end_range) {
351 for(unsigned int i = start_range; i < end_range; ++i)
352 dst.local_element(i) *= scaling_factor;
353 },
354 dof_index);
355 }
356 else
357 {
358 apply(dst, src);
359 dst *= scaling_factor;
360 }
361 }
362
363
364private:
365 void
366 cell_loop_matrix_free_operator(dealii::MatrixFree<dim, Number> const &,
367 VectorType & dst,
368 VectorType const & src,
369 Range const & cell_range) const
370 {
371 Integrator integrator(*matrix_free, dof_index, quad_index);
372 InverseMassAsMatrixFreeOperator inverse_mass(integrator);
373
374 if(coefficient_is_variable)
375 {
376 for(unsigned int cell = cell_range.first; cell < cell_range.second; ++cell)
377 {
378 integrator.reinit(cell);
379 integrator.read_dof_values(src, 0);
380
381 dealii::AlignedVector<dealii::VectorizedArray<Number>> inverse_JxW_times_coefficient(
382 integrator.n_q_points);
383 inverse_mass.fill_inverse_JxW_values(inverse_JxW_times_coefficient);
384
385 if(consider_inverse_coefficient)
386 {
387 // Consider a mass matrix of the form
388 // (u_h , v_h / c)_Omega
389 // hence fill the vector with (J / c)^-1 = c/J
390 for(unsigned int q = 0; q < integrator.n_q_points; ++q)
391 {
392 inverse_JxW_times_coefficient[q] *=
393 this->variable_coefficients->get_coefficient_cell(cell, q);
394 }
395 }
396 else
397 {
398 // Consider a mass matrix of the form
399 // (u_h , v_h * c)_Omega
400 // hence fill the vector with inv(J * c) = 1/(J * c)
401 for(unsigned int q = 0; q < integrator.n_q_points; ++q)
402 {
403 inverse_JxW_times_coefficient[q] /=
404 this->variable_coefficients->get_coefficient_cell(cell, q);
405 }
406 }
407
408 inverse_mass.apply(inverse_JxW_times_coefficient,
409 n_components,
410 integrator.begin_dof_values(),
411 integrator.begin_dof_values());
412
413 integrator.set_dof_values(dst, 0);
414 }
415 }
416 else
417 {
418 for(unsigned int cell = cell_range.first; cell < cell_range.second; ++cell)
419 {
420 integrator.reinit(cell);
421 integrator.read_dof_values(src, 0);
422
423 inverse_mass.apply(integrator.begin_dof_values(), integrator.begin_dof_values());
424
425 integrator.set_dof_values(dst, 0);
426 }
427 }
428 }
429
430 dealii::MatrixFree<dim, Number> const * matrix_free;
431
432 unsigned int dof_index, quad_index;
433
434 InverseMassParameters data;
435
436 // Variable coefficients not managed by this class.
437 bool coefficient_is_variable;
438 bool consider_inverse_coefficient;
439
440 VariableCoefficients<dealii::VectorizedArray<Number>> const * variable_coefficients;
441
442 // Solver and preconditioner for solving a global linear system of equations for all degrees of
443 // freedom.
444 std::shared_ptr<PreconditionerBase<Number>> global_preconditioner;
445 std::shared_ptr<Krylov::SolverBase<VectorType>> global_solver;
446
447 // Iterations needed in global Krylov solver at last inverse application.
448 mutable unsigned int n_iter_global_last = dealii::numbers::invalid_unsigned_int;
449
450 // Block-Jacobi preconditioner used as cell-wise inverse for L2-conforming spaces. In this case,
451 // the mass matrix is block-diagonal and a block-Jacobi preconditioner inverts the mass operator
452 // exactly (up to solver tolerances). The implementation of the block-Jacobi preconditioner can be
453 // matrix-based or matrix-free, depending on the parameters specified.
454 std::shared_ptr<BlockJacobiPreconditioner<MassOperator<dim, n_components, Number>>>
455 block_jacobi_preconditioner;
456
457 // MassOperator as underlying operator for the cell-wise or global iterative solves.
458 MassOperator<dim, n_components, Number> mass_operator;
459};
460
461} // namespace ExaDG
462
463#endif /* EXADG_OPERATORS_INVERSE_MASS_OPERATOR_H_ */
void update()
Definition inverse_mass_operator.h:278
Definition preconditioner_base.h:35
Definition variable_coefficients.h:40
Definition driver.cpp:33
Definition inverse_mass_operator.h:44
Definition inverse_mass_parameters.h:56
Definition iterative_solvers_dealii_wrapper.h:92
Definition mass_operator.h:35