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