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