ryujin 2.1.1 revision 71cdc42292164f8095c0bd62c4ea75ebcfac58fa
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
100
101 LaplaceOperator() = default;
102
103 void initialize(const dealii::MatrixFree<dim, Number> &matrix_free)
104 {
105 matrix_free_ = &matrix_free;
106 }
107
108 dealii::types::global_dof_index m() const
109 {
110 return matrix_free_->get_vector_partitioner(0)->size();
111 }
112
113 Number el(const unsigned int, const unsigned int) const
114 {
115 Assert(false, dealii::ExcNotImplemented());
116 return Number();
117 }
118
119 void vmult(ScalarHostVector &dst, const ScalarHostVector &src) const
120 {
121 Assert(dst.get_partitioner() == src.get_partitioner(),
122 dealii::ExcMessage("src and dst have 2 different partitioners"));
123
124 using namespace dealii;
125
126 const auto body = [this](const auto &data,
127 auto &dst,
128 const auto &src,
129 const auto range) {
130 FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number> fee(
131 data, /*CG*/ 0, /*full quadrature*/ 0);
132
133 for (unsigned int cell = range.first; cell < range.second; ++cell) {
134 fee.reinit(cell);
135 fee.read_dof_values(src);
136 apply_local_operator(fee);
137 fee.distribute_local_to_global(dst);
138 }
139 };
140
141 matrix_free_->template cell_loop<ScalarHostVector, ScalarHostVector>(
142 body, dst, src, /*zero destination*/ true);
143 }
144
145 void Tvmult(ScalarHostVector &dst, const ScalarHostVector &src) const
146 {
147 vmult(dst, src);
148 }
149
151 dealii::DiagonalMatrix<ScalarHostVector> &diagonal_matrix) const
152 {
153 ScalarHostVector &diagonal_vector = diagonal_matrix.get_vector();
154 matrix_free_->initialize_dof_vector(diagonal_vector, /*CG*/ 0);
155
156 dealii::MatrixFreeTools::compute_diagonal(
157 *matrix_free_,
158 diagonal_vector,
159 &LaplaceOperator::template apply_local_operator<
160 dealii::FEEvaluation<dim, -1, 0, 1, Number>>,
161 this,
162 0,
163 1);
164
165 /* invert diagonal matrix: */
166
167 const auto n_owned_cg =
168 diagonal_vector.get_partitioner()->locally_owned_size();
169
170 const auto body_invert = [&](auto sentinel, const unsigned int i) {
171 constexpr Number eps = std::numeric_limits<Number>::epsilon();
172 using T = decltype(sentinel);
173 const auto m_i = read_entry<T>(diagonal_vector, i);
174 constexpr auto gt = dealii::SIMDComparison::greater_than;
175 const auto m_i_inv = dealii::compare_and_apply_mask<gt>(
176 std::abs(m_i), T(eps), Number(1.) / m_i, T(1.));
177 write_entry<T>(diagonal_vector, m_i_inv, i);
178 };
179 cpu_simd_loop<Number>("", body_invert, 0, n_owned_cg, n_owned_cg);
180 }
181
182 private:
183 const dealii::MatrixFree<dim, Number> *matrix_free_;
184
185 template <typename Evaluator>
186 void apply_local_operator(Evaluator &eval) const
187 {
188 eval.evaluate(dealii::EvaluationFlags::gradients);
189 for (const unsigned int q : eval.quadrature_point_indices())
190 eval.submit_gradient(eval.get_gradient(q), q);
191 eval.integrate(dealii::EvaluationFlags::gradients);
192 }
193 };
194
195
202 template <int dim, typename Number>
203 class UpdateOperator : public dealii::EnableObserverPointer
204 {
205 public:
206 // FIXME: refactor
207 static constexpr unsigned int order_fe = 1;
208 static constexpr unsigned int order_quad = 2;
209
212 dealii::LinearAlgebra::distributed::BlockVector<Number>;
213
214 UpdateOperator() = default;
215
216 void initialize(const dealii::MatrixFree<dim, Number> &matrix_free,
217 const ScalarHostVector &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
249 void vmult(ScalarHostVector &dst, const ScalarHostVector &src) const
250 {
251 Assert(dst.get_partitioner() == src.get_partitioner(),
252 dealii::ExcMessage("src and dst have 2 different partitioners"));
253
254 using namespace dealii;
255
256 const auto body_laplace = [](const auto &data,
257 auto &dst,
258 const auto &src,
259 const auto range) {
260 FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number> fee(
261 data, /*CG*/ 0, /*full quadrature*/ 0);
262
263 for (unsigned int cell = range.first; cell < range.second; ++cell) {
264 fee.reinit(cell);
265 fee.gather_evaluate(src, dealii::EvaluationFlags::gradients);
266 for (unsigned int q = 0; q < fee.n_q_points; ++q)
267 fee.submit_gradient(fee.get_gradient(q), q);
268 fee.integrate_scatter(dealii::EvaluationFlags::gradients, dst);
269 }
270 };
271
272 matrix_free_->template cell_loop<ScalarHostVector, ScalarHostVector>(
273 body_laplace, dst, src, /*zero destination*/ true);
274
275 const auto body_velocity = [this](const auto &data,
276 auto &dst,
277 const auto &src,
278 const auto range) {
279 FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number> fee(
280 data, /*CG*/ 0, /*lumped quadrature*/ 1);
281 FEEvaluation<dim, order_fe, order_quad, /*components*/ 1, Number>
282 fee_density(data, /*hyperbolic*/ 1, /*lumped quadrature*/ 1);
283 FEEvaluation<dim,
284 order_fe,
286 /*components*/ (dim == 2 ? 1 : dim),
287 Number>
288 fee_magnetic(data, /*hyperbolic*/ 1, /*lumped quadrature*/ 1);
289
290 for (unsigned int cell = range.first; cell < range.second; ++cell) {
291 fee.reinit(cell);
292 fee_density.reinit(cell);
293 fee_magnetic.reinit(cell);
294
295 fee.gather_evaluate(src, dealii::EvaluationFlags::gradients);
296 fee_density.gather_evaluate(*density_,
297 dealii::EvaluationFlags::values);
298 fee_magnetic.gather_evaluate(*magnetic_field_,
299 dealii::EvaluationFlags::values);
300
301 for (unsigned int q = 0; q < fee.n_q_points; ++q) {
302 const auto grad_phi = fee.get_gradient(q);
303 auto density = fee_density.get_value(q);
304 dealii::Tensor<1, (dim == 2 ? 1 : dim), decltype(density)>
305 magnetic_field;
306 if constexpr (dim == 2) {
307 magnetic_field[0] = fee_magnetic.get_value(q);
308 } else if constexpr (dim == 3) {
309 magnetic_field = fee_magnetic.get_value(q);
310 }
311
312 const auto B_n_inverse_grad_phi =
313 apply_B_n_inverse(magnetic_field, theta_tau_, grad_phi);
314 const auto result = theta_tau_ * theta_tau_ * alpha_ * density *
315 B_n_inverse_grad_phi;
316 fee.submit_gradient(result, q);
317 }
318 fee.integrate_scatter(dealii::EvaluationFlags::gradients, dst);
319 }
320 };
321
322 matrix_free_->template cell_loop<ScalarHostVector, ScalarHostVector>(
323 body_velocity, dst, src, /*zero destination*/ false);
324 }
325
326 void Tvmult(ScalarHostVector &dst, const ScalarHostVector &src) const
327 {
328 vmult(dst, src);
329 }
330
331 private:
332 const dealii::MatrixFree<dim, Number> *matrix_free_;
333 const ScalarHostVector *density_;
334 const BlockHostVector *magnetic_field_;
335
336 mutable Number theta_tau_;
337 mutable Number alpha_;
338 };
339
340
347 template <int dim, typename Number>
348 class MGTransfer : public dealii::MGTransferMatrixFree<dim, Number>
349 {
350 public:
351 void build(const dealii::DoFHandler<dim> &dof_handler,
352 const dealii::MGConstrainedDoFs &mg_constrained_dofs,
353 const dealii::MGLevelObject<dealii::MatrixFree<dim, Number>>
354 &matrix_free)
355 {
356 dealii::MGTransferMatrixFree<dim, Number>::initialize_constraints(
357 mg_constrained_dofs);
358
359 /*
360 * Hand the level partitioners of our MatrixFree objects to the
361 * transfer: copy_to_mg() reinitializes the multigrid level vectors
362 * with these partitioners.
363 */
364
365 const auto n_levels = dof_handler.get_triangulation().n_global_levels();
366 std::vector<std::shared_ptr<const dealii::Utilities::MPI::Partitioner>>
367 partitioners(n_levels);
368
369 for (unsigned int level = matrix_free.min_level();
370 level <= matrix_free.max_level();
371 ++level) {
373 matrix_free[level].initialize_dof_vector(vector, /*CG*/ 0);
374 partitioners[level] = vector.get_partitioner();
375 }
376
377 dealii::MGTransferMatrixFree<dim, Number>::build(dof_handler,
378 partitioners);
379 }
380 };
381
382
389 template <int dim, typename Number>
390 class MGSmoother : public dealii::EnableObserverPointer
391 {
392 public:
393 // FIXME: refactor
394 static constexpr unsigned int order_fe = 1;
395 static constexpr unsigned int order_quad = 2;
396
399
401 dealii::PreconditionChebyshev<LaplaceOperator<dim, float>,
403
404 MGSmoother() = default;
405
419
420 void initialize(const OfflineData<dim, Number> &offline_data,
421 const std::set<dealii::types::boundary_id> boundary_ids,
422 const MultigridParameters parameters)
423 {
424 using namespace dealii;
425
426 /*
427 * Set up multigrid operators and data structures:
428 */
429
430 const auto &discretization = offline_data.discretization();
431 const auto &triangulation = discretization.triangulation();
432 const unsigned int n_levels = triangulation.n_global_levels();
433 const unsigned int min_level =
434 std::min(parameters.gmg_min_level, n_levels - 1);
435 MGLevelObject<IndexSet> relevant_sets(0, n_levels - 1);
436
437 const auto &dof_handler = offline_data.dof_handler_cg();
438 for (unsigned int level = 0; level < n_levels; ++level) {
439 relevant_sets[level] =
440 dealii::DoFTools::extract_locally_relevant_level_dofs( //
441 dof_handler,
442 level);
443 }
444
445 // First index CG, second index hyperbolic ansatz
446 std::vector<const dealii::DoFHandler<dim> *> dof_handlers = {
447 &dof_handler, &offline_data.dof_handler()};
448
449 mg_constrained_dofs_.initialize(dof_handler, relevant_sets);
450 /* FIXME: handle periodic boundary conditions and hanging nodes... */
451 if (!boundary_ids.empty())
452 mg_constrained_dofs_.make_zero_boundary_constraints( //
453 dof_handler,
454 boundary_ids);
455
456 // First index full quadrature, second index lumped quadrature
457 std::vector<dealii::Quadrature<1>> quadratures = {
458 discretization.quadrature_1d()[0],
459 discretization.nodal_quadrature_1d()[0]};
460
461 typename MatrixFree<dim, float>::AdditionalData additional_data_level;
462 additional_data_level.tasks_parallel_scheme =
463 MatrixFree<dim, float>::AdditionalData::none;
464 level_matrix_free_.resize(min_level, n_levels - 1);
465
466 for (unsigned int level = min_level; level < n_levels; ++level) {
467 additional_data_level.mg_level = level;
468
469 AffineConstraints<float> level_constraints(relevant_sets[level],
470 relevant_sets[level]);
471
472 if (!boundary_ids.empty()) {
473 level_constraints.add_lines(
474 mg_constrained_dofs_.get_boundary_indices(level));
475 level_constraints.merge(
476 mg_constrained_dofs_.get_level_constraints(level));
477 }
478 level_constraints.close();
479
480 AffineConstraints<float> dummy;
481 dummy.close();
482 std::vector<const dealii::AffineConstraints<float> *>
483 level_constraints_list = {&level_constraints, &dummy};
484
485 level_matrix_free_[level].reinit(discretization.mapping(),
486 dof_handlers,
487 level_constraints_list,
488 quadratures,
489 additional_data_level);
490 }
491
492 mg_transfer_.build(dof_handler, mg_constrained_dofs_, level_matrix_free_);
493
494 level_laplace_matrices_.resize(level_matrix_free_.min_level(),
495 level_matrix_free_.max_level());
496
497 MGLevelObject<typename Preconditioner::AdditionalData> smoother_data(
498 level_matrix_free_.min_level(), level_matrix_free_.max_level());
499
500 for (unsigned int level = level_matrix_free_.min_level();
501 level <= level_matrix_free_.max_level();
502 ++level) {
503
504 level_laplace_matrices_[level].initialize(level_matrix_free_[level]);
505 smoother_data[level].preconditioner =
506 std::make_shared<dealii::DiagonalMatrix<ScalarHostVectorFloat>>();
507 level_laplace_matrices_[level].compute_diagonal(
508 *smoother_data[level].preconditioner);
509
510 if (boundary_ids.empty()) {
511 smoother_data[level].eigenvalue_algorithm =
512 dealii::internal::EigenvalueAlgorithm::power_iteration;
513 }
514
515 if (level == level_matrix_free_.min_level()) {
516 smoother_data[level].degree = numbers::invalid_unsigned_int;
517 smoother_data[level].eig_cg_n_iterations = 500;
518 smoother_data[level].smoothing_range = 1e-3;
519 } else {
520 smoother_data[level].degree = parameters.gmg_smoother_degree;
521 smoother_data[level].eig_cg_n_iterations =
522 parameters.gmg_smoother_n_cg_iter;
523 smoother_data[level].smoothing_range = parameters.gmg_smoother_range;
524 if (parameters.gmg_smoother_n_cg_iter == 0)
525 smoother_data[level].max_eigenvalue =
526 parameters.gmg_smoother_max_eig;
527 }
528 }
529
530 relaxation_.initialize(level_laplace_matrices_, smoother_data);
531
532 /*
533 * Set up coarse solver:
534 */
535
536 create(coarse_solver_control_, 10000, parameters.gmg_coarse_tolerance);
537 create(coarse_solver_data_);
538 create(coarse_solver_, *coarse_solver_control_, *coarse_solver_data_);
539 create(coarse_preconditioner_);
540 level_laplace_matrices_[level_laplace_matrices_.min_level()]
541 .compute_diagonal(*coarse_preconditioner_);
542 create(coarse_grid_solver_,
543 *coarse_solver_,
544 level_laplace_matrices_[level_laplace_matrices_.min_level()],
545 *coarse_preconditioner_);
546
547 /*
548 * Set up preconditioner:
549 */
550
551 create(mg_matrix_, level_laplace_matrices_);
552 create(mg_,
553 *mg_matrix_,
554 *coarse_grid_solver_,
555 mg_transfer_,
556 relaxation_,
557 relaxation_,
558 level_laplace_matrices_.min_level(),
559 level_laplace_matrices_.max_level());
560 create(preconditioner_, dof_handler, *mg_, mg_transfer_);
561 }
562
563 void vmult(ScalarHostVector &dst, const ScalarHostVector &src) const
564 {
565 Assert(dst.get_partitioner() == src.get_partitioner(),
566 dealii::ExcMessage("src and dst have 2 different partitioners"));
567 preconditioner_->vmult(dst, src);
568 }
569
570 void Tvmult(ScalarHostVector &dst, const ScalarHostVector &src) const
571 {
572 vmult(dst, src);
573 }
574
575 private:
580
581 dealii::MGConstrainedDoFs mg_constrained_dofs_;
582 dealii::MGLevelObject<dealii::MatrixFree<dim, float>> level_matrix_free_;
583 MGTransfer<dim, float> mg_transfer_;
584 dealii::MGLevelObject<LaplaceOperator<dim, float>> level_laplace_matrices_;
585
586 dealii::mg::SmootherRelaxation<Preconditioner, ScalarHostVectorFloat>
587 relaxation_;
588
589 std::unique_ptr<dealii::SolverControl> coarse_solver_control_;
590 using CAD =
591 typename dealii::SolverCG<ScalarHostVectorFloat>::AdditionalData;
592 std::unique_ptr<CAD> coarse_solver_data_;
593 std::unique_ptr<dealii::SolverCG<ScalarHostVectorFloat>> coarse_solver_;
594 std::unique_ptr<dealii::DiagonalMatrix<ScalarHostVectorFloat>>
595 coarse_preconditioner_;
596 using MGCGIS = dealii::MGCoarseGridIterativeSolver<
598 dealii::SolverCG<ScalarHostVectorFloat>,
600 dealii::DiagonalMatrix<ScalarHostVectorFloat>>;
601 std::unique_ptr<MGCGIS> coarse_grid_solver_;
602
603 std::unique_ptr<dealii::mg::Matrix<ScalarHostVectorFloat>> mg_matrix_;
604 std::unique_ptr<dealii::Multigrid<ScalarHostVectorFloat>> mg_;
605 std::unique_ptr<dealii::PreconditionMG<dim,
608 preconditioner_;
610 };
611
612} /* namespace ryujin */
void Tvmult(ScalarHostVector &dst, const ScalarHostVector &src) const
Number el(const unsigned int, const unsigned int) const
dealii::types::global_dof_index m() const
void vmult(ScalarHostVector &dst, const ScalarHostVector &src) const
void initialize(const dealii::MatrixFree< dim, Number > &matrix_free)
void compute_diagonal(dealii::DiagonalMatrix< ScalarHostVector > &diagonal_matrix) const
static constexpr unsigned int order_quad
Vectors::ScalarHostVector< Number > ScalarHostVector
static constexpr unsigned int order_fe
static constexpr unsigned int order_fe
Vectors::ScalarHostVector< float > ScalarHostVectorFloat
MGSmoother()=default
Vectors::ScalarHostVector< Number > ScalarHostVector
void vmult(ScalarHostVector &dst, const ScalarHostVector &src) const
static constexpr unsigned int order_quad
dealii::PreconditionChebyshev< LaplaceOperator< dim, float >, ScalarHostVectorFloat > Preconditioner
void Tvmult(ScalarHostVector &dst, const ScalarHostVector &src) const
void initialize(const OfflineData< dim, Number > &offline_data, const std::set< dealii::types::boundary_id > boundary_ids, const MultigridParameters parameters)
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
static constexpr unsigned int order_quad
void Tvmult(ScalarHostVector &dst, const ScalarHostVector &src) const
Vectors::ScalarHostVector< Number > ScalarHostVector
void vmult(ScalarHostVector &dst, const ScalarHostVector &src) const
dealii::types::global_dof_index m() const
void initialize(const dealii::MatrixFree< dim, Number > &matrix_free, const ScalarHostVector &density, const BlockHostVector &magnetic_field)
dealii::LinearAlgebra::distributed::BlockVector< Number > BlockHostVector
Number el(const unsigned int, const unsigned int) const
dealii::LinearAlgebra::distributed::Vector< Number > ScalarHostVector
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)
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)