ryujin 2.1.1 revision 46bf70e400e423a8ffffe8300887eeb35b8dfb2c
mesh_adaptor.template.h
Go to the documentation of this file.
1//
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3// Copyright (C) 2024 by the ryujin authors
4//
5
6#pragma once
7
8#include "mesh_adaptor.h"
9#include "mpi_ensemble.h"
10#include "openmp.h"
12#include "simd.h"
13
14#include <deal.II/base/array_view.h>
15#include <deal.II/distributed/grid_refinement.h>
16
17#include <boost/container/small_vector.hpp>
18
19namespace ryujin
20{
21 template <typename Description, int dim, typename Number>
23 const MPIEnsemble &mpi_ensemble,
24 const OfflineData<dim, Number> &offline_data,
25 const HyperbolicSystem &hyperbolic_system,
26 const ParabolicSystem &parabolic_system,
27 const InitialPrecomputedVector &initial_precomputed,
28 const ScalarVector &alpha,
29 const std::string &subsection /*= "MeshAdaptor"*/)
30 : ParameterAcceptor(subsection)
31 , mpi_ensemble_(mpi_ensemble)
32 , offline_data_(&offline_data)
33 , hyperbolic_system_(&hyperbolic_system)
34 , parabolic_system_(&parabolic_system)
35 , initial_precomputed_(initial_precomputed)
36 , alpha_(alpha)
37 , need_mesh_adaptation_(false)
38 {
39 adaptation_strategy_ = AdaptationStrategy::smoothness_indicators;
40 add_parameter("adaptation strategy",
41 adaptation_strategy_,
42 "The chosen adaptation strategy. Possible values are: global "
43 "refinement, random adaptation, smoothness indicators");
44
45 marking_strategy_ = MarkingStrategy::fixed_threshold;
46 add_parameter(
47 "marking strategy",
48 marking_strategy_,
49 "The chosen marking strategy. Possible values are: fixed threshold.");
50
51 time_point_selection_strategy_ =
53 add_parameter("time point selection strategy",
54 time_point_selection_strategy_,
55 "The chosen time point selection strategy. Possible values "
56 "are: fixed time points, simulation cycle");
57
58 /* Options for various adaptation strategies: */
59 enter_subsection("adaptation strategies");
60 random_adaptation_mersenne_twister_seed_ = 42u;
61 add_parameter("random adaptation: mersenne_twister_seed",
62 random_adaptation_mersenne_twister_seed_,
63 "Seed for 64bit Mersenne Twister used for random refinement");
64
65 add_parameter(
66 "smoothness indicators: quantities",
67 smoothness_selected_quantities_,
68 "List of conserved, primitive or precomputed quantities that will be "
69 "used for constructing the smoothness indicator.");
70
71 smoothness_local_global_ratio_ = 0.5;
72 add_parameter(
73 "smoothness indicators: local global ratio",
74 smoothness_local_global_ratio_,
75 "Ratio between local and global denominator value used for normalizing "
76 "the smoothness indicator. A value of 1 indicates pure global "
77 "normalization, a value of 0 indicates pure local normalization.");
78
79 smoothness_min_cutoff_ = 0.0;
80 add_parameter("smoothness indicators: min cutoff",
81 smoothness_min_cutoff_,
82 "minimal cutoff for the smoothness indicator: values below "
83 "this threshold will be set to the cutoff value.");
84
85 smoothness_max_cutoff_ = 1.0e16;
86 add_parameter("smoothness indicators: max cutoff",
87 smoothness_max_cutoff_,
88 "minimal cutoff for the smoothness indicator: values above "
89 "this threshold will be set to the cutoff value.");
90
91 smoothness_widen_stencil_ = 15;
92 add_parameter(
93 "smoothness indicators: stencil size",
94 smoothness_widen_stencil_,
95 "Number of layers to widen the smoothness indicator stencil.");
96 leave_subsection();
97
98 /* Options for various marking strategies: */
99
100 enter_subsection("marking strategies");
101
102 coarsening_threshold_ = 0.25;
103 add_parameter(
104 "coarsening threshold",
105 coarsening_threshold_,
106 "Marking: normalized or absolute threshold for selecting cells for "
107 "coarsening (used in \"fixed threshhold\" marking strategy).");
108
109 refinement_threshold_ = 0.75;
110 add_parameter(
111 "refinement threshold",
112 refinement_threshold_,
113 "Marking: normalized or absolute threshold for selecting cells for "
114 "refinement (used in \"fixed threshold\" marking strategy).");
115
116 absolute_threshold_ = false;
117 add_parameter("absolute threshold",
118 absolute_threshold_,
119 "Marking: if set to true use an absolute threshold for the "
120 "\"refinement threshold\" and \"coarsening threshold\" "
121 "values instead of a relative one. If this parameter is set "
122 "to false then the smoothness indicator is normalized into "
123 "the number range of [0., 1] and the threshold parameters "
124 "are also expected to be a value in this interval.");
125
126 min_refinement_level_ = 0;
127 add_parameter("minimal refinement level",
128 min_refinement_level_,
129 "Marking: minimal refinement level of cells that will be "
130 "maintained while coarsening cells.");
131
132 max_refinement_level_ = 1000;
133 add_parameter("maximal refinement level",
134 max_refinement_level_,
135 "Marking: maximal refinement level of cells that will be "
136 "maintained while refininig cells.");
137 leave_subsection();
138
139 /* Options for various time point selection strategies: */
140
141 enter_subsection("time point selection strategies");
142 adaptation_time_points_ = {};
143 add_parameter("fixed time points",
144 adaptation_time_points_,
145 "List of time points in (simulation) time at which we will "
146 "perform a mesh adaptation cycle.");
148 adaptation_cycle_interval_ = 10;
149 add_parameter("simulation cycle: interval",
150 adaptation_cycle_interval_,
151 "The nth simulation cycle at which we will "
152 "perform mesh adapation.");
153 leave_subsection();
155 const auto call_back = [this] {
156 /* Initialize Mersenne Twister with configured seed: */
157 mersenne_twister_.seed(random_adaptation_mersenne_twister_seed_);
158 };
159
160 call_back();
161 ParameterAcceptor::parse_parameters_call_back.connect(call_back);
162 }
163
164
165 template <typename Description, int dim, typename Number>
167 {
168#ifdef DEBUG_OUTPUT
169 std::cout << "MeshAdaptor<dim, Number>::prepare()" << std::endl;
170#endif
171
172 if (time_point_selection_strategy_ ==
174 /* Remove outdated refinement timestamps: */
175 const auto new_end = std::remove_if(
176 adaptation_time_points_.begin(),
177 adaptation_time_points_.end(),
178 [&](const Number &t_refinement) { return (t > t_refinement); });
179 adaptation_time_points_.erase(new_end, adaptation_time_points_.end());
180 }
181
183 {"alpha"}, smoothness_selected_quantities_);
184
185 /* toggle mesh adaptation flag to off. */
186 need_mesh_adaptation_ = false;
187 }
188
189
190 template <typename Description, int dim, typename Number>
192 const StateVector &state_vector) const
193 {
194 const auto &affine_constraints = offline_data_->affine_constraints();
195 const unsigned int n_internal = offline_data_->n_locally_internal();
196 const unsigned int n_owned = offline_data_->n_locally_owned();
197 const auto &sparsity_simd = offline_data_->sparsity_pattern_simd();
198 const auto &betaij_matrix = offline_data_->betaij_matrix();
199 using VA = dealii::VectorizedArray<Number>;
200
201 /*
202 * Extract selected quantities:
203 */
204
205 auto quantities =
207 *offline_data_,
208 *hyperbolic_system_,
209 state_vector,
210 initial_precomputed_,
211 {"alpha"},
212 {alpha_},
213 smoothness_selected_quantities_);
214
215 for (auto &it : quantities) {
216 it.update_ghost_values();
217 affine_constraints.distribute(it);
218 it.update_ghost_values();
219 }
220
221 /*
222 * Set up temporary vectors:
223 */
224
225 const unsigned int n_entries = quantities.size();
226 const auto &scalar_partitioner = offline_data_->scalar_partitioner();
227
228 std::vector<ScalarVector> numerator(n_entries);
229 std::vector<ScalarVector> denominator(std::max(1u, n_entries));
230 for (auto &it : numerator)
231 it.reinit(scalar_partitioner);
232 for (auto &it : denominator)
233 it.reinit(scalar_partitioner);
234
235 /*
236 * Commpute numerators and denominators for the smoothness indicators:
237 */
238
240
241 auto loop = [&](auto sentinel, unsigned int left, unsigned int right) {
242 using T = decltype(sentinel);
243 unsigned int stride_size = get_stride_size<T>;
244
246 for (unsigned int i = left; i < right; i += stride_size) {
247
248 /* Skip constrained degrees of freedom: */
249 const unsigned int row_length = sparsity_simd.row_length(i);
250 if (row_length == 1)
251 continue;
252
253 boost::container::small_vector<T, 10> value_i(n_entries, T(0.));
254 for (unsigned int k = 0; k < n_entries; ++k) {
255 value_i[k] = get_entry<T>(quantities[k], i);
256 }
257
258 boost::container::small_vector<T, 10> numerator_i(n_entries, T(0.));
259 boost::container::small_vector<T, 10> denominator_i(n_entries, T(0.));
260
261 const unsigned int *js = sparsity_simd.columns(i);
262 for (unsigned int col_idx = 0; col_idx < row_length;
263 ++col_idx, js += stride_size) {
264
265 /* Skip diagonal. */
266 if (col_idx == 0)
267 continue;
268
269 const auto beta_ij = betaij_matrix.template get_entry<T>(i, col_idx);
270
271 for (unsigned int k = 0; k < n_entries; ++k) {
272 const auto value_j_k = get_entry<T>(quantities[k], js);
273 numerator_i[k] += beta_ij * (value_j_k - value_i[k]);
274 denominator_i[k] +=
275 std::abs(beta_ij) * //
276 std::max(std::abs(value_j_k), std::abs(value_i[k]));
277 }
278
279 for (unsigned int k = 0; k < n_entries; ++k) {
280 write_entry<T>(numerator[k], numerator_i[k], i);
281 write_entry<T>(denominator[k], denominator_i[k], i);
282 }
283 }
284 }
285 };
286
287 /* Parallel non-vectorized loop: */
288 loop(Number(), n_internal, n_owned);
289 /* Parallel vectorized SIMD loop: */
290 loop(VA(), 0, n_internal);
291
293
294 /*
295 * Normalize and populate smoothness_indicators_ vector:
296 */
297
298 smoothness_indicators_.reinit(scalar_partitioner);
299
300 constexpr Number eps = std::numeric_limits<Number>::epsilon();
301
302 std::vector<Number> denominator_global_maximum(n_entries);
303 for (unsigned int k = 0; k < n_entries; ++k) {
304 denominator_global_maximum[k] = dealii::Utilities::MPI::max(
305 denominator[k].linfty_norm(), mpi_ensemble_.ensemble_communicator());
306
307 denominator_global_maximum[k] =
308 std::max(denominator_global_maximum[k], eps);
309 }
310
312
313 auto loop = [&](auto sentinel, unsigned int left, unsigned int right) {
314 using T = decltype(sentinel);
315 unsigned int stride_size = get_stride_size<T>;
316
318 for (unsigned int i = left; i < right; i += stride_size) {
319
320 /* Skip constrained degrees of freedom: */
321 const unsigned int row_length = sparsity_simd.row_length(i);
322 if (row_length == 1)
323 continue;
324
325 auto alpha_i = T(0.);
326 for (unsigned int k = 0; k < n_entries; ++k) {
327 const auto numerator_i = get_entry<T>(numerator[k], i);
328 const auto denominator_i = get_entry<T>(denominator[k], i);
329
330 auto denominator =
331 (Number(1.) - smoothness_local_global_ratio_) * denominator_i +
332 smoothness_local_global_ratio_ * denominator_global_maximum[k];
333 denominator = std::max(T(eps), denominator);
334
335 alpha_i += std::abs(numerator_i) / denominator;
336 }
337
338 alpha_i = std::min(alpha_i, T(smoothness_max_cutoff_));
339 alpha_i = std::max(alpha_i, T(smoothness_min_cutoff_));
340 write_entry<T>(smoothness_indicators_, alpha_i, i);
341 }
342 };
343
344 /* Parallel non-vectorized loop: */
345 loop(Number(), n_internal, n_owned);
346 /* Parallel vectorized SIMD loop: */
347 loop(VA(), 0, n_internal);
348
350
351 /*
352 * Widen indicators over stencil via max() operator:
353 */
354
355 for (unsigned int cycle = 0; cycle < smoothness_widen_stencil_; ++cycle) {
356 smoothness_indicators_.update_ghost_values();
357
359
360 auto loop = [&](auto sentinel, unsigned int left, unsigned int right) {
361 using ScalarNumber = typename get_value_type<Number>::type;
362
363 using T = decltype(sentinel);
364 unsigned int stride_size = get_stride_size<T>;
365
366 /* Stored thread locally: */
367
369 for (unsigned int i = left; i < right; i += stride_size) {
370
371 /* Skip constrained degrees of freedom: */
372 const unsigned int row_length = sparsity_simd.row_length(i);
373 if (row_length == 1)
374 continue;
375
376 auto alpha_i = get_entry<T>(smoothness_indicators_, i);
377
378 const unsigned int *js = sparsity_simd.columns(i);
379 for (unsigned int col_idx = 0; col_idx < row_length;
380 ++col_idx, js += stride_size) {
381
382 /* Skip diagonal. */
383 if (col_idx == 0)
384 continue;
385
386 const auto alpha_j = get_entry<T>(smoothness_indicators_, js);
387
388 alpha_i = std::max(alpha_i, alpha_j);
389 }
390
391 write_entry<T>(/*SIC!*/ denominator[0], alpha_i, i);
392 }
393 };
394
395 /* Parallel non-vectorized loop: */
396 loop(Number(), n_internal, n_owned);
397 /* Parallel vectorized SIMD loop: */
398 loop(VA(), 0, n_internal);
399
401
402 smoothness_indicators_ = /*SIC!*/ denominator[0];
403 }
404
405 smoothness_indicators_.update_ghost_values();
406 affine_constraints.distribute(smoothness_indicators_);
407 smoothness_indicators_.update_ghost_values();
408 }
409
410
411 template <typename Description, int dim, typename Number>
413 const StateVector &state_vector, const Number t, unsigned int cycle)
414 {
415#ifdef DEBUG_OUTPUT
416 std::cout << "MeshAdaptor<dim, Number>::analyze()" << std::endl;
417#endif
418
419 /*
420 * Decide whether we perform an adaptation cycle with the chosen time
421 * point selection strategy:
422 */
423
424 switch (time_point_selection_strategy_) {
426 /* Remove all refinement points from the vector that lie in the past: */
427 const auto new_end = std::remove_if( //
428 adaptation_time_points_.begin(),
429 adaptation_time_points_.end(),
430 [&](const Number &t_refinement) {
431 if (t < t_refinement)
432 return false;
433 need_mesh_adaptation_ = true;
434 return true;
435 });
436 adaptation_time_points_.erase(new_end, adaptation_time_points_.end());
437 } break;
438
440 /* check whether we reached a cycle interval: */
441 if (cycle % adaptation_cycle_interval_ == 0)
442 need_mesh_adaptation_ = true;
443 } break;
444
445 default:
446 AssertThrow(false, dealii::ExcInternalError());
447 __builtin_trap();
448 }
449
450 if (!need_mesh_adaptation_)
451 return;
452
453 /*
454 * Some adaptation strategies require us to prepare some internal
455 * data fields:
456 */
457
458 switch (adaptation_strategy_) {
460 /* do nothing */
461 break;
462
464 /* do nothing */
465 break;
466
468 compute_smoothness_indicators(state_vector);
469 break;
470 }
471
472 default:
473 AssertThrow(false, dealii::ExcInternalError());
474 __builtin_trap();
475 }
476 }
477
478
479 template <typename Description, int dim, typename Number>
482 {
483 std::generate(std::begin(indicators_), std::end(indicators_), [&]() {
484 static std::uniform_real_distribution<double> distribution(0.0, 10.0);
485 return distribution(mersenne_twister_);
486 });
487 }
488
489
490 template <typename Description, int dim, typename Number>
491 void MeshAdaptor<Description, dim, Number>::
492 populate_cell_indicators_from_smoothness_indicators() const
493 {
494 const auto &scalar_partitioner = offline_data_->scalar_partitioner();
495
496 /*
497 * Distribute to cells by taking a cell-wise average:
498 */
499
500 std::vector<dealii::types::global_dof_index> local_dof_indices;
501
502 const auto &dof_handler = offline_data_->dof_handler();
503 for (const auto &cell : dof_handler.active_cell_iterators()) {
504 if (!cell->is_locally_owned())
505 continue;
506
507 const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();
508 const auto scale = Number(1. / dofs_per_cell);
509 local_dof_indices.resize(dofs_per_cell);
510 cell->get_dof_indices(local_dof_indices);
511
512 auto alpha_cell = Number(0.);
513
514 for (unsigned int i = 0; i < dofs_per_cell; ++i) {
515 const auto global_i = local_dof_indices[i];
516 const auto local_i = scalar_partitioner->global_to_local(global_i);
517 auto alpha_i = get_entry<Number>(smoothness_indicators_, local_i);
518 alpha_cell += alpha_i;
519 }
520 alpha_cell *= scale;
521
522 indicators_[cell->active_cell_index()] = static_cast<float>(alpha_cell);
523 }
524 }
525
526
527 template <typename Description, int dim, typename Number>
530 dealii::Triangulation<dim> &triangulation [[maybe_unused]]) const
531 {
532 auto &discretization [[maybe_unused]] = offline_data_->discretization();
533 Assert(&triangulation == &discretization.triangulation(),
534 dealii::ExcInternalError());
535
536#if !DEAL_II_VERSION_GTE(9, 6, 0)
537 AssertThrow(
538 false,
539 dealii::ExcMessage(
540 "The MeshAdaptor class needs deal.II version 9.6.0 or newer"));
541
542#else
543
544 /*
545 * Compute cell indicators with the chosen adaptation strategy:
546 */
547
548 switch (adaptation_strategy_) {
550 /* Simply mark all cells for refinement and return: */
551 for (auto &cell : triangulation.active_cell_iterators())
552 cell->set_refine_flag();
553 return;
554 } break;
555
557 indicators_.reinit(triangulation.n_active_cells());
558 populate_cell_indicators_with_random_values();
559 } break;
560
562 indicators_.reinit(triangulation.n_active_cells());
563 populate_cell_indicators_from_smoothness_indicators();
564 } break;
565
566 default:
567 AssertThrow(false, dealii::ExcInternalError());
568 __builtin_trap();
569 }
570
571 /*
572 * Mark cells with chosen marking strategy:
573 */
574
575 switch (marking_strategy_) {
577
578 float inv_denominator = 1.f;
579 float bias = 0.f;
580
581 if (!absolute_threshold_) {
582 /*
583 * Normalize indicators to the interval [0., 1.]
584 */
585
586 float minimum = std::numeric_limits<float>::max();
587 float maximum = 0.f;
588 for (const auto &cell : triangulation.active_cell_iterators()) {
589 if (!cell->is_locally_owned())
590 continue;
591 const auto indicator = indicators_[cell->active_cell_index()];
592 minimum = std::min(minimum, indicator);
593 maximum = std::max(maximum, indicator);
594 }
595 minimum = dealii::Utilities::MPI::min(
596 minimum, mpi_ensemble_.ensemble_communicator());
597 maximum = dealii::Utilities::MPI::max(
598 maximum, mpi_ensemble_.ensemble_communicator());
599
600 constexpr float eps = std::numeric_limits<float>::epsilon();
601 // Ensure that if minimum == maximum we end up with 0.5 everywhere
602 inv_denominator = 1.f / (maximum - minimum + 10.f * eps);
603 bias = (minimum + 5.f * eps) * inv_denominator;
604 }
605
606 /*
607 * And mark all cells according to threshold:
608 */
609
610 for (const auto &cell : triangulation.active_cell_iterators()) {
611 if (!cell->is_locally_owned())
612 continue;
613
614 auto indicator = indicators_[cell->active_cell_index()];
615 indicator = indicator * inv_denominator - bias;
616 if (indicator < coarsening_threshold_)
617 cell->set_coarsen_flag();
618 else if (indicator > refinement_threshold_)
619 cell->set_refine_flag();
620 }
621 } break;
622
623 default:
624 AssertThrow(false, dealii::ExcInternalError());
625 __builtin_trap();
626 }
627
628 /*
629 * Constrain refinement and coarsening to maximum and minimum
630 * refinement levels:
631 */
632
633 if (triangulation.n_levels() > max_refinement_level_)
634 for (const auto &cell :
635 triangulation.active_cell_iterators_on_level(max_refinement_level_))
636 cell->clear_refine_flag();
637
638 for (const auto &cell :
639 triangulation.active_cell_iterators_on_level(min_refinement_level_))
640 cell->clear_coarsen_flag();
641#endif
642 }
643} // namespace ryujin
void compute_smoothness_indicators(const StateVector &state_vector) const
typename View::StateVector StateVector
Definition: mesh_adaptor.h:122
Vectors::ScalarVector< Number > ScalarVector
Definition: mesh_adaptor.h:124
MeshAdaptor(const MPIEnsemble &mpi_ensemble, const OfflineData< dim, Number > &offline_data, const HyperbolicSystem &hyperbolic_system, const ParabolicSystem &parabolic_system, const InitialPrecomputedVector &initial_precomputed, const ScalarVector &alpha, const std::string &subsection="/MeshAdaptor")
void analyze(const StateVector &state_vector, const Number t, unsigned int cycle)
typename View::InitialPrecomputedVector InitialPrecomputedVector
Definition: mesh_adaptor.h:123
typename Description::ParabolicSystem ParabolicSystem
Definition: mesh_adaptor.h:115
typename Description::HyperbolicSystem HyperbolicSystem
Definition: mesh_adaptor.h:114
void mark_cells_for_coarsening_and_refinement(dealii::Triangulation< dim > &triangulation) const
void prepare(const Number t)
#define RYUJIN_PARALLEL_REGION_BEGIN
Definition: openmp.h:54
#define RYUJIN_OMP_FOR
Definition: openmp.h:70
#define RYUJIN_PARALLEL_REGION_END
Definition: openmp.h:63
static std::vector< ScalarVector > extract(const OfflineData< dim, Number > &offline_data, const HyperbolicSystem &hyperbolic_system, const StateVector &state_vector, const InitialPrecomputedVector &initial_precomputed, const std::vector< std::string > &additional_names, const std::vector< std::reference_wrapper< const ScalarVector > > &additional_vectors, const std::vector< std::string > &selected)
static void check(const std::vector< std::string > &additional_names, const std::vector< std::string > &selected)