ryujin 2.1.1 revision b04fb4aba7bd97ddfa5994a6b4b6e8586eb15db2
Loading...
Searching...
No Matches
laplace_operator.h
Go to the documentation of this file.
1//
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3// Copyright (C) 2026 by the ryujin authors
4//
5
6#pragma once
7
8#include <deal.II/base/config.h>
9#include <loop.h>
10#include <observer_pointer.h>
11#include <offline_data.h>
12#include <simd.h>
13
14#include <deal.II/base/vectorization.h>
15#include <deal.II/dofs/dof_tools.h>
16#include <deal.II/lac/diagonal_matrix.h>
17#include <deal.II/lac/la_parallel_block_vector.h>
18#include <deal.II/lac/precondition.h>
19#include <deal.II/matrix_free/fe_evaluation.h>
20#include <deal.II/matrix_free/matrix_free.h>
21#include <deal.II/matrix_free/tools.h>
22#include <deal.II/multigrid/mg_base.h>
23#include <deal.II/multigrid/mg_coarse.h>
24#include <deal.II/multigrid/mg_matrix.h>
25#include <deal.II/multigrid/mg_smoother.h>
26#include <deal.II/multigrid/mg_transfer_matrix_free.h>
27#include <deal.II/multigrid/multigrid.h>
28
29namespace ryujin
30{
31 template <int dim, typename Number, typename Number2>
32 DEAL_II_ALWAYS_INLINE inline dealii::Tensor<1, dim, Number> apply_B_n(
33 const dealii::Tensor<1, (dim == 2 ? 1 : dim), Number> &magnetic_field,
34 const Number2 theta_tau,
35 const dealii::Tensor<1, dim, Number> &velocity)
36 {
37 if constexpr (dim == 1) {
38 return velocity;
39
40 } else if constexpr (dim == 2) {
41 return velocity -
42 theta_tau * magnetic_field[0] * cross_product_2d(velocity);
43
44 } else {
45 return velocity - theta_tau * cross_product_3d(velocity, magnetic_field);
46 }
47 }
48
49
50 template <int dim, typename Number, typename Number2>
51 DEAL_II_ALWAYS_INLINE inline dealii::Tensor<1, dim, Number> apply_B_n_inverse(
52 const dealii::Tensor<1, (dim == 2 ? 1 : dim), Number> &magnetic_field,
53 const Number2 &theta_tau,
54 const dealii::Tensor<1, dim, Number> &velocity)
55 {
56 const auto denominator =
57 Number(1.) + theta_tau * theta_tau * magnetic_field.norm_square();
58
59 if constexpr (dim == 1) {
60 return velocity;
61
62 } else if constexpr (dim == 2) {
63 const auto numerator =
64 velocity + theta_tau * magnetic_field[0] * cross_product_2d(velocity);
65 return numerator / denominator;
66
67 } else {
68 const auto numerator =
69 velocity + theta_tau * cross_product_3d(velocity, magnetic_field) +
70 theta_tau * theta_tau * (velocity * magnetic_field) * magnetic_field;
71 return numerator / denominator;
72 }
73 }
74
75
76#ifndef DOXYGEN
77 template <typename T, typename... Args>
78 void create(std::unique_ptr<T> &ptr, Args &&...args)
79 {
80 ptr = std::make_unique<T>(args...);
81 }
82#endif
83
84
91 template <int dim, typename Number>
92 class LaplaceOperator : public dealii::EnableObserverPointer
93 {
94 public:
95 // FIXME: refactor
96 static constexpr unsigned int order_fe = 1;
97 static constexpr unsigned int order_quad = 2;
98
99 LaplaceOperator() = default;
100
101 void initialize(const dealii::MatrixFree<dim, Number> &matrix_free)
102 {
103 matrix_free_ = &matrix_free;
104 }
105
106 dealii::types::global_dof_index m() const
107 {
108 return matrix_free_->get_vector_partitioner(0)->size();
109 }
110
111 Number el(const unsigned int, const unsigned int) const
112 {
113 Assert(false, dealii::ExcNotImplemented());
114 return Number();
115 }
116
118 const ScalarHostVector<Number> &src) const
119 {
120 Assert(dst.get_partitioner() == src.get_partitioner(),
121 dealii::ExcMessage("src and dst have 2 different partitioners"));
122
123 using namespace dealii;
124
125 const auto body = [this](const auto &data,
126 auto &dst,
127 const auto &src,
128 const auto range) {
129 FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number> fee(
130 data, /*CG*/ 0, /*full quadrature*/ 0);
131
132 for (unsigned int cell = range.first; cell < range.second; ++cell) {
133 fee.reinit(cell);
134 fee.read_dof_values(src);
135 apply_local_operator(fee);
136 fee.distribute_local_to_global(dst);
137 }
138 };
139
140 matrix_free_->template cell_loop<ScalarHostVector<Number>,
142 body, dst, src, /*zero destination*/ true);
143 }
144
146 const ScalarHostVector<Number> &src) const
147 {
148 vmult(dst, src);
149 }
150
152 dealii::DiagonalMatrix<ScalarHostVector<Number>> &diagonal_matrix) const
153 {
154 ScalarHostVector<Number> &diagonal_vector = diagonal_matrix.get_vector();
155 matrix_free_->initialize_dof_vector(diagonal_vector, /*CG*/ 0);
156
157 dealii::MatrixFreeTools::compute_diagonal(
158 *matrix_free_,
159 diagonal_vector,
160 &LaplaceOperator::template apply_local_operator<
161 dealii::FEEvaluation<dim, -1, 0, 1, Number>>,
162 this,
163 0,
164 1);
165
166 /* invert diagonal matrix: */
167
168 const auto n_owned_cg =
169 diagonal_vector.get_partitioner()->locally_owned_size();
170
171 const auto body_invert = [&](auto sentinel, const unsigned int i) {
172 constexpr Number eps = std::numeric_limits<Number>::epsilon();
173 using T = decltype(sentinel);
174 const auto m_i = read_entry<T>(diagonal_vector, i);
175 constexpr auto gt = dealii::SIMDComparison::greater_than;
176 const auto m_i_inv = dealii::compare_and_apply_mask<gt>(
177 std::abs(m_i), T(eps), Number(1.) / m_i, T(1.));
178 write_entry<T>(diagonal_vector, m_i_inv, i);
179 };
180 cpu_simd_loop<Number>("", body_invert, 0, n_owned_cg, n_owned_cg);
181 }
182
183 private:
184 const dealii::MatrixFree<dim, Number> *matrix_free_;
185
186 template <typename Evaluator>
187 void apply_local_operator(Evaluator &eval) const
188 {
189 eval.evaluate(dealii::EvaluationFlags::gradients);
190 for (const unsigned int q : eval.quadrature_point_indices())
191 eval.submit_gradient(eval.get_gradient(q), q);
192 eval.integrate(dealii::EvaluationFlags::gradients);
193 }
194 };
195
196
203 template <int dim, typename Number>
204 class UpdateOperator : public dealii::EnableObserverPointer
205 {
206 public:
207 // FIXME: refactor
208 static constexpr unsigned int order_fe = 1;
209 static constexpr unsigned int order_quad = 2;
210
212 dealii::LinearAlgebra::distributed::BlockVector<Number>;
213
214 UpdateOperator() = default;
215
216 void initialize(const dealii::MatrixFree<dim, Number> &matrix_free,
217 const ScalarHostVector<Number> &density,
218 const BlockHostVector &magnetic_field)
219 {
220 matrix_free_ = &matrix_free;
221 density_ = &density;
222 magnetic_field_ = &magnetic_field;
223
224 theta_tau_ = Number(0.);
225 alpha_ = Number(0.);
226 }
227
228 dealii::types::global_dof_index m() const
229 {
230 return matrix_free_->get_vector_partitioner(0)->size();
231 }
232
233 Number el(const unsigned int, const unsigned int) const
234 {
235 Assert(false, dealii::ExcNotImplemented());
236 return Number();
237 }
238
239 void set_theta_tau(const Number theta_tau) const
240 {
241 theta_tau_ = theta_tau;
242 }
243
244 void set_alpha(const Number alpha) const
245 {
246 alpha_ = alpha;
247 }
248
250 const ScalarHostVector<Number> &src) const
251 {
252 Assert(dst.get_partitioner() == src.get_partitioner(),
253 dealii::ExcMessage("src and dst have 2 different partitioners"));
254
255 using namespace dealii;
256
257 const auto body_laplace = [](const auto &data,
258 auto &dst,
259 const auto &src,
260 const auto range) {
261 FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number> fee(
262 data, /*CG*/ 0, /*full quadrature*/ 0);
263
264 for (unsigned int cell = range.first; cell < range.second; ++cell) {
265 fee.reinit(cell);
266 fee.gather_evaluate(src, dealii::EvaluationFlags::gradients);
267 for (unsigned int q = 0; q < fee.n_q_points; ++q)
268 fee.submit_gradient(fee.get_gradient(q), q);
269 fee.integrate_scatter(dealii::EvaluationFlags::gradients, dst);
270 }
271 };
272
273 matrix_free_->template cell_loop<ScalarHostVector<Number>,
275 body_laplace, dst, src, /*zero destination*/ true);
276
277 const auto body_velocity = [this](const auto &data,
278 auto &dst,
279 const auto &src,
280 const auto range) {
281 FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number> fee(
282 data, /*CG*/ 0, /*lumped quadrature*/ 1);
283 FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number>
284 fee_density(data, /*hyperbolic*/ 1, /*lumped quadrature*/ 1);
285 FEEvaluation<dim,
286 order_fe,
288 /*components*/ (dim == 2 ? 1 : dim),
289 Number>
290 fee_magnetic(data, /*hyperbolic*/ 1, /*lumped quadrature*/ 1);
291
292 for (unsigned int cell = range.first; cell < range.second; ++cell) {
293 fee.reinit(cell);
294 fee_density.reinit(cell);
295 fee_magnetic.reinit(cell);
296
297 fee.gather_evaluate(src, dealii::EvaluationFlags::gradients);
298 fee_density.gather_evaluate(*density_,
299 dealii::EvaluationFlags::values);
300 fee_magnetic.gather_evaluate(*magnetic_field_,
301 dealii::EvaluationFlags::values);
302
303 for (unsigned int q = 0; q < fee.n_q_points; ++q) {
304 const auto grad_phi = fee.get_gradient(q);
305 auto density = fee_density.get_value(q);
306 dealii::Tensor<1, (dim == 2 ? 1 : dim), decltype(density)>
307 magnetic_field;
308 if constexpr (dim == 2) {
309 magnetic_field[0] = fee_magnetic.get_value(q);
310 } else if constexpr (dim == 3) {
311 magnetic_field = fee_magnetic.get_value(q);
312 }
313
314 const auto B_n_inverse_grad_phi =
315 apply_B_n_inverse(magnetic_field, theta_tau_, grad_phi);
316 const auto result = theta_tau_ * theta_tau_ * alpha_ * density *
317 B_n_inverse_grad_phi;
318 fee.submit_gradient(result, q);
319 }
320 fee.integrate_scatter(dealii::EvaluationFlags::gradients, dst);
321 }
322 };
323
324 matrix_free_->template cell_loop<ScalarHostVector<Number>,
326 body_velocity, dst, src, /*zero destination*/ false);
327 }
328
330 const ScalarHostVector<Number> &src) const
331 {
332 vmult(dst, src);
333 }
334
335 private:
336 const dealii::MatrixFree<dim, Number> *matrix_free_;
337 const ScalarHostVector<Number> *density_;
338 const BlockHostVector *magnetic_field_;
339
340 mutable Number theta_tau_;
341 mutable Number alpha_;
342 };
343
344
351 template <int dim, typename Number>
352 class MGTransfer : public dealii::MGTransferMatrixFree<dim, Number>
353 {
354 public:
355 void build(const dealii::DoFHandler<dim> &dof_handler,
356 const dealii::MGConstrainedDoFs &mg_constrained_dofs,
357 const dealii::MGLevelObject<dealii::MatrixFree<dim, Number>>
358 &matrix_free)
359 {
360 dealii::MGTransferMatrixFree<dim, Number>::initialize_constraints(
361 mg_constrained_dofs);
362
363 /*
364 * Hand the level partitioners of our MatrixFree objects to the
365 * transfer: copy_to_mg() reinitializes the multigrid level vectors
366 * with these partitioners.
367 */
368
369 const auto n_levels = dof_handler.get_triangulation().n_global_levels();
370 std::vector<std::shared_ptr<const dealii::Utilities::MPI::Partitioner>>
371 partitioners(n_levels);
372
373 for (unsigned int level = matrix_free.min_level();
374 level <= matrix_free.max_level();
375 ++level) {
377 matrix_free[level].initialize_dof_vector(vector, /*CG*/ 0);
378 partitioners[level] = vector.get_partitioner();
379 }
380
381 dealii::MGTransferMatrixFree<dim, Number>::build(dof_handler,
382 partitioners);
383 }
384 };
385
386
393 template <int dim, typename Number>
394 class MGSmoother : public dealii::EnableObserverPointer
395 {
396 public:
397 // FIXME: refactor
398 static constexpr unsigned int order_fe = 1;
399 static constexpr unsigned int order_quad = 2;
400
402 dealii::PreconditionChebyshev<LaplaceOperator<dim, float>,
404
405 MGSmoother() = default;
406
420
421 void initialize(const OfflineData<dim, Number> &offline_data,
422 const std::set<dealii::types::boundary_id> boundary_ids,
423 const MultigridParameters parameters)
424 {
425 using namespace dealii;
426
427 /*
428 * Set up multigrid operators and data structures:
429 */
430
431 const auto &discretization = offline_data.discretization();
432 const auto &triangulation = discretization.triangulation();
433 const unsigned int n_levels = triangulation.n_global_levels();
434 const unsigned int min_level =
435 std::min(parameters.gmg_min_level, n_levels - 1);
436 MGLevelObject<IndexSet> relevant_sets(0, n_levels - 1);
437
438 const auto &dof_handler = offline_data.dof_handler_cg();
439 for (unsigned int level = 0; level < n_levels; ++level) {
440 relevant_sets[level] =
441 dealii::DoFTools::extract_locally_relevant_level_dofs( //
442 dof_handler,
443 level);
444 }
445
446 // First index CG, second index hyperbolic ansatz
447 std::vector<const dealii::DoFHandler<dim> *> dof_handlers = {
448 &dof_handler, &offline_data.dof_handler()};
449
450 mg_constrained_dofs_.initialize(dof_handler, relevant_sets);
451 /* FIXME: handle periodic boundary conditions and hanging nodes... */
452 if (!boundary_ids.empty())
453 mg_constrained_dofs_.make_zero_boundary_constraints( //
454 dof_handler,
455 boundary_ids);
456
457 // First index full quadrature, second index lumped quadrature
458 std::vector<dealii::Quadrature<1>> quadratures = {
459 discretization.quadrature_1d()[0],
460 discretization.nodal_quadrature_1d()[0]};
461
462 typename MatrixFree<dim, float>::AdditionalData additional_data_level;
463 additional_data_level.tasks_parallel_scheme =
464 MatrixFree<dim, float>::AdditionalData::none;
465 level_matrix_free_.resize(min_level, n_levels - 1);
466
467 for (unsigned int level = min_level; level < n_levels; ++level) {
468 additional_data_level.mg_level = level;
469
470 AffineConstraints<float> level_constraints(relevant_sets[level],
471 relevant_sets[level]);
472
473 if (!boundary_ids.empty()) {
474 level_constraints.add_lines(
475 mg_constrained_dofs_.get_boundary_indices(level));
476 level_constraints.merge(
477 mg_constrained_dofs_.get_level_constraints(level));
478 }
479 level_constraints.close();
480
481 AffineConstraints<float> dummy;
482 dummy.close();
483 std::vector<const dealii::AffineConstraints<float> *>
484 level_constraints_list = {&level_constraints, &dummy};
485
486 level_matrix_free_[level].reinit(discretization.mapping(),
487 dof_handlers,
488 level_constraints_list,
489 quadratures,
490 additional_data_level);
491 }
492
493 mg_transfer_.build(dof_handler, mg_constrained_dofs_, level_matrix_free_);
494
495 level_laplace_matrices_.resize(level_matrix_free_.min_level(),
496 level_matrix_free_.max_level());
497
498 MGLevelObject<typename Preconditioner::AdditionalData> smoother_data(
499 level_matrix_free_.min_level(), level_matrix_free_.max_level());
500
501 for (unsigned int level = level_matrix_free_.min_level();
502 level <= level_matrix_free_.max_level();
503 ++level) {
504
505 level_laplace_matrices_[level].initialize(level_matrix_free_[level]);
506 smoother_data[level].preconditioner =
507 std::make_shared<dealii::DiagonalMatrix<ScalarHostVector<float>>>();
508 level_laplace_matrices_[level].compute_diagonal(
509 *smoother_data[level].preconditioner);
510
511 if (boundary_ids.empty()) {
512 smoother_data[level].eigenvalue_algorithm =
513 dealii::internal::EigenvalueAlgorithm::power_iteration;
514 }
515
516 if (level == level_matrix_free_.min_level()) {
517 smoother_data[level].degree = numbers::invalid_unsigned_int;
518 smoother_data[level].eig_cg_n_iterations = 500;
519 smoother_data[level].smoothing_range = 1e-3;
520 } else {
521 smoother_data[level].degree = parameters.gmg_smoother_degree;
522 smoother_data[level].eig_cg_n_iterations =
523 parameters.gmg_smoother_n_cg_iter;
524 smoother_data[level].smoothing_range = parameters.gmg_smoother_range;
525 if (parameters.gmg_smoother_n_cg_iter == 0)
526 smoother_data[level].max_eigenvalue =
527 parameters.gmg_smoother_max_eig;
528 }
529 }
530
531 relaxation_.initialize(level_laplace_matrices_, smoother_data);
532
533 /*
534 * Set up coarse solver:
535 */
536
537 create(coarse_solver_control_, 10000, parameters.gmg_coarse_tolerance);
538 create(coarse_solver_data_);
539 create(coarse_solver_, *coarse_solver_control_, *coarse_solver_data_);
540 create(coarse_preconditioner_);
541 level_laplace_matrices_[level_laplace_matrices_.min_level()]
542 .compute_diagonal(*coarse_preconditioner_);
543 create(coarse_grid_solver_,
544 *coarse_solver_,
545 level_laplace_matrices_[level_laplace_matrices_.min_level()],
546 *coarse_preconditioner_);
547
548 /*
549 * Set up preconditioner:
550 */
551
552 create(mg_matrix_, level_laplace_matrices_);
553 create(mg_,
554 *mg_matrix_,
555 *coarse_grid_solver_,
556 mg_transfer_,
557 relaxation_,
558 relaxation_,
559 level_laplace_matrices_.min_level(),
560 level_laplace_matrices_.max_level());
561 create(preconditioner_, dof_handler, *mg_, mg_transfer_);
562 }
563
565 const ScalarHostVector<Number> &src) const
566 {
567 Assert(dst.get_partitioner() == src.get_partitioner(),
568 dealii::ExcMessage("src and dst have 2 different partitioners"));
569 preconditioner_->vmult(dst, src);
570 }
571
573 const ScalarHostVector<Number> &src) const
574 {
575 vmult(dst, src);
576 }
577
578 private:
583
584 dealii::MGConstrainedDoFs mg_constrained_dofs_;
585 dealii::MGLevelObject<dealii::MatrixFree<dim, float>> level_matrix_free_;
586 MGTransfer<dim, float> mg_transfer_;
587 dealii::MGLevelObject<LaplaceOperator<dim, float>> level_laplace_matrices_;
588
589 dealii::mg::SmootherRelaxation<Preconditioner, ScalarHostVector<float>>
590 relaxation_;
591
592 std::unique_ptr<dealii::SolverControl> coarse_solver_control_;
593 using CAD =
594 typename dealii::SolverCG<ScalarHostVector<float>>::AdditionalData;
595 std::unique_ptr<CAD> coarse_solver_data_;
596 std::unique_ptr<dealii::SolverCG<ScalarHostVector<float>>> coarse_solver_;
597 std::unique_ptr<dealii::DiagonalMatrix<ScalarHostVector<float>>>
598 coarse_preconditioner_;
599 using MGCGIS = dealii::MGCoarseGridIterativeSolver<
601 dealii::SolverCG<ScalarHostVector<float>>,
603 dealii::DiagonalMatrix<ScalarHostVector<float>>>;
604 std::unique_ptr<MGCGIS> coarse_grid_solver_;
605
606 std::unique_ptr<dealii::mg::Matrix<ScalarHostVector<float>>> mg_matrix_;
607 std::unique_ptr<dealii::Multigrid<ScalarHostVector<float>>> mg_;
608 std::unique_ptr<dealii::PreconditionMG<dim,
611 preconditioner_;
613 };
614
615} /* namespace ryujin */
void compute_diagonal(dealii::DiagonalMatrix< ScalarHostVector< Number > > &diagonal_matrix) const
Number el(const unsigned int, const unsigned int) const
dealii::types::global_dof_index m() const
void initialize(const dealii::MatrixFree< dim, Number > &matrix_free)
static constexpr unsigned int order_quad
void Tvmult(ScalarHostVector< Number > &dst, const ScalarHostVector< Number > &src) const
static constexpr unsigned int order_fe
void vmult(ScalarHostVector< Number > &dst, const ScalarHostVector< Number > &src) const
static constexpr unsigned int order_fe
MGSmoother()=default
void vmult(ScalarHostVector< Number > &dst, const ScalarHostVector< Number > &src) const
static constexpr unsigned int order_quad
void Tvmult(ScalarHostVector< Number > &dst, const ScalarHostVector< Number > &src) const
void initialize(const OfflineData< dim, Number > &offline_data, const std::set< dealii::types::boundary_id > boundary_ids, const MultigridParameters parameters)
dealii::PreconditionChebyshev< LaplaceOperator< dim, float >, ScalarHostVector< float > > Preconditioner
void build(const dealii::DoFHandler< dim > &dof_handler, const dealii::MGConstrainedDoFs &mg_constrained_dofs, const dealii::MGLevelObject< dealii::MatrixFree< dim, Number > > &matrix_free)
const dealii::DoFHandler< dim > & dof_handler() const
const auto & discretization() const
const auto & dof_handler_cg() const
void set_theta_tau(const Number theta_tau) const
void set_alpha(const Number alpha) const
static constexpr unsigned int order_fe
void Tvmult(ScalarHostVector< Number > &dst, const ScalarHostVector< Number > &src) const
static constexpr unsigned int order_quad
void initialize(const dealii::MatrixFree< dim, Number > &matrix_free, const ScalarHostVector< Number > &density, const BlockHostVector &magnetic_field)
dealii::types::global_dof_index m() const
dealii::LinearAlgebra::distributed::BlockVector< Number > BlockHostVector
Number el(const unsigned int, const unsigned int) const
void vmult(ScalarHostVector< Number > &dst, const ScalarHostVector< Number > &src) const
DEAL_II_ALWAYS_INLINE dealii::Tensor< 1, dim, Number > apply_B_n(const dealii::Tensor< 1,(dim==2 ? 1 :dim), Number > &magnetic_field, const Number2 theta_tau, const dealii::Tensor< 1, dim, Number > &velocity)
dealii::LinearAlgebra::distributed::Vector< Number > ScalarHostVector
DEAL_II_ALWAYS_INLINE dealii::Tensor< 1, dim, Number > apply_B_n_inverse(const dealii::Tensor< 1,(dim==2 ? 1 :dim), Number > &magnetic_field, const Number2 &theta_tau, const dealii::Tensor< 1, dim, Number > &velocity)