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