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