ryujin 2.1.1 revision b04fb4aba7bd97ddfa5994a6b4b6e8586eb15db2
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 HyperbolicDescription, int dim, typename Number>
23 const MPIEnsemble &mpi_ensemble,
24 const OfflineData<dim, Number> &offline_data,
25 const HyperbolicSystem &hyperbolic_system,
26 const Interface::ParabolicSystem &parabolic_system,
27 const MultiComponentVector<Number> &initial_precomputed,
28 const ScalarVector<Number> &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 }
166
167 template <typename HyperbolicDescription, 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 HyperbolicDescription, int dim, typename Number>
193 const StateVector<Number> &state_vector,
194 const Number t,
195 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 HyperbolicDescription, 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 HyperbolicDescription, int dim, typename Number>
273 void MeshAdaptor<HyperbolicDescription, 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 =
285 smoothness_indicators_.template view<1>();
286
287 const auto &dof_handler = offline_data_->dof_handler();
288 for (const auto &cell : dof_handler.active_cell_iterators()) {
289 if (!cell->is_locally_owned())
290 continue;
291
292 const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();
293 const auto scale = Number(1. / dofs_per_cell);
294 local_dof_indices.resize(dofs_per_cell);
295 cell->get_dof_indices(local_dof_indices);
296
297 auto alpha_cell = Number(0.);
298
299 for (unsigned int i = 0; i < dofs_per_cell; ++i) {
300 const auto global_i = local_dof_indices[i];
301 const auto local_i = scalar_partitioner->global_to_local(global_i);
302 auto alpha_i =
303 smoothness_indicators_view.template read_entry<Number>(local_i);
304 alpha_cell += alpha_i;
305 }
306 alpha_cell *= scale;
307
308 indicators_[cell->active_cell_index()] = static_cast<float>(alpha_cell);
309 }
310 }
311
312
313 template <typename HyperbolicDescription, int dim, typename Number>
316 dealii::Triangulation<dim> &triangulation [[maybe_unused]]) const
317 {
318 auto &discretization [[maybe_unused]] = offline_data_->discretization();
319 Assert(&triangulation == &discretization.triangulation(),
320 dealii::ExcInternalError());
321
322 /*
323 * Compute cell indicators with the chosen adaptation strategy:
324 */
325
326 switch (adaptation_strategy_) {
328 /* Simply mark all cells for refinement and return: */
329 for (auto &cell : triangulation.active_cell_iterators())
330 cell->set_refine_flag();
331 return;
332 } break;
333
335 indicators_.reinit(triangulation.n_active_cells());
336 populate_cell_indicators_with_random_values();
337 } break;
338
340 indicators_.reinit(triangulation.n_active_cells());
341 populate_cell_indicators_from_smoothness_indicators();
342 } break;
343
344 default:
345 AssertThrow(false, dealii::ExcInternalError());
346 __builtin_trap();
347 }
348
349 /*
350 * Mark cells with chosen marking strategy:
351 */
352
353 switch (marking_strategy_) {
355
356 float inv_denominator = 1.f;
357 float bias = 0.f;
358
359 if (!absolute_threshold_) {
360 /*
361 * Normalize indicators to the interval [0., 1.]
362 */
363
364 float minimum = std::numeric_limits<float>::max();
365 float maximum = 0.f;
366 for (const auto &cell : triangulation.active_cell_iterators()) {
367 if (!cell->is_locally_owned())
368 continue;
369 const auto indicator = indicators_[cell->active_cell_index()];
370 minimum = std::min(minimum, indicator);
371 maximum = std::max(maximum, indicator);
372 }
373 minimum = dealii::Utilities::MPI::min(
374 minimum, mpi_ensemble_.ensemble_communicator());
375 maximum = dealii::Utilities::MPI::max(
376 maximum, mpi_ensemble_.ensemble_communicator());
377
378 constexpr float eps = std::numeric_limits<float>::epsilon();
379 // Ensure that if minimum == maximum we end up with 0.5 everywhere
380 inv_denominator = 1.f / (maximum - minimum + 10.f * eps);
381 bias = (minimum + 5.f * eps) * inv_denominator;
382 }
383
384 /*
385 * And mark all cells according to threshold:
386 */
387
388 for (const auto &cell : triangulation.active_cell_iterators()) {
389 if (!cell->is_locally_owned())
390 continue;
391
392 auto indicator = indicators_[cell->active_cell_index()];
393 indicator = indicator * inv_denominator - bias;
394 if (indicator < coarsening_threshold_)
395 cell->set_coarsen_flag();
396 else if (indicator > refinement_threshold_)
397 cell->set_refine_flag();
398 }
399 } break;
400
401 default:
402 AssertThrow(false, dealii::ExcInternalError());
403 __builtin_trap();
404 }
405
406 /*
407 * Constrain refinement and coarsening to maximum and minimum
408 * refinement levels:
409 */
410
411 if (triangulation.n_levels() > max_refinement_level_)
412 for (const auto &cell :
413 triangulation.active_cell_iterators_on_level(max_refinement_level_))
414 cell->clear_refine_flag();
415
416 for (const auto &cell :
417 triangulation.active_cell_iterators_on_level(min_refinement_level_))
418 cell->clear_coarsen_flag();
419 }
420
421
422 template <typename HyperbolicDescription, int dim, typename Number>
425 const StateVector<Number> &state_vector) const
426 {
427 const auto &affine_constraints = offline_data_->affine_constraints();
428 const unsigned int n_internal = offline_data_->n_locally_internal();
429 const unsigned int n_owned = offline_data_->n_locally_owned();
430 const auto sparsity_simd_view =
431 offline_data_->sparsity_pattern_simd().view();
432 const auto betaij_matrix_view = offline_data_->betaij_matrix().view();
433 using VA = dealii::VectorizedArray<Number>;
434
435 /*
436 * Extract selected quantities:
437 */
438
439 selected_components_extractor_.prepare_extraction(state_vector);
440 auto quantities = selected_components_extractor_.view().extract();
441
442 for (auto &it : quantities) {
443 it.update_ghost_values();
444 affine_constraints.distribute(it);
445 it.update_ghost_values();
446 }
447
448 /*
449 * Set up temporary vectors:
450 */
451
452 const unsigned int n_entries = quantities.size();
453 const auto &scalar_partitioner = offline_data_->scalar_partitioner();
454
455 /*
456 * Ensure we have at least one entry in numerator and denominator
457 * available. We use those for temporary storage.
458 */
459 std::vector<ScalarHostVector<Number>> numerator(std::max(1u, n_entries));
460 std::vector<ScalarHostVector<Number>> denominator(std::max(1u, n_entries));
461 for (auto &it : numerator)
462 it.reinit(scalar_partitioner);
463 for (auto &it : denominator)
464 it.reinit(scalar_partitioner);
465
466 /*
467 * Commpute numerators and denominators for the smoothness indicators:
468 */
469
470 const auto body = [&](auto sentinel, unsigned int i) {
471 using T = decltype(sentinel);
472 const unsigned int stride_size = sparsity_simd_view.stride_of_row(i);
473
474 /* Skip constrained degrees of freedom: */
475 const unsigned int row_length = sparsity_simd_view.row_length(i);
476 if (row_length == 1)
477 return;
478
479 boost::container::small_vector<T, 10> value_i(n_entries, T(0.));
480 for (unsigned int k = 0; k < n_entries; ++k) {
481 value_i[k] = read_entry<T>(quantities[k], i);
482 }
483
484 boost::container::small_vector<T, 10> numerator_i(n_entries, T(0.));
485 boost::container::small_vector<T, 10> denominator_i(n_entries, T(0.));
486
487 const unsigned int *js = sparsity_simd_view.columns(i);
488 for (unsigned int col_idx = 0; col_idx < row_length;
489 ++col_idx, js += stride_size) {
490
491 /* Skip diagonal. */
492 if (col_idx == 0)
493 continue;
494
495 const auto beta_ij =
496 betaij_matrix_view.template read_entry<T>(i, col_idx);
497
498 for (unsigned int k = 0; k < n_entries; ++k) {
499 const auto value_j_k = read_entry<T>(quantities[k], js);
500 numerator_i[k] += beta_ij * (value_j_k - value_i[k]);
501 denominator_i[k] +=
502 std::abs(beta_ij) * //
503 std::max(std::abs(value_j_k), std::abs(value_i[k]));
504 }
505
506 for (unsigned int k = 0; k < n_entries; ++k) {
507 write_entry<T>(numerator[k], numerator_i[k], i);
508 write_entry<T>(denominator[k], denominator_i[k], i);
509 }
510 }
511 };
512
513 cpu_simd_loop<Number>("mesh_adaptor_1", body, 0, n_internal, n_owned);
514
515 /*
516 * Sum up and normalize the indicators and store the result in numerator[0]:
517 */
518
519 constexpr Number eps = std::numeric_limits<Number>::epsilon();
520
521 std::vector<Number> denominator_global_maximum(n_entries);
522 for (unsigned int k = 0; k < n_entries; ++k) {
523 denominator_global_maximum[k] = dealii::Utilities::MPI::max(
524 denominator[k].linfty_norm(), mpi_ensemble_.ensemble_communicator());
525
526 denominator_global_maximum[k] =
527 std::max(denominator_global_maximum[k], eps);
528 }
529
530 const auto body_normalize = [&](auto sentinel, unsigned int i) {
531 using T = decltype(sentinel);
532
533 /* Skip constrained degrees of freedom: */
534 const unsigned int row_length = sparsity_simd_view.row_length(i);
535 if (row_length == 1)
536 return;
537
538 auto alpha_i = T(0.);
539 for (unsigned int k = 0; k < n_entries; ++k) {
540 const auto numerator_i = read_entry<T>(numerator[k], i);
541 const auto denominator_i = read_entry<T>(denominator[k], i);
542
543 auto denominator =
544 (Number(1.) - smoothness_local_global_ratio_) * denominator_i +
545 smoothness_local_global_ratio_ * denominator_global_maximum[k];
546 denominator = std::max(T(eps), denominator);
547
548 alpha_i += std::abs(numerator_i) / denominator;
549 }
550
551 alpha_i = std::min(alpha_i, T(smoothness_max_cutoff_));
552 alpha_i = std::max(alpha_i, T(smoothness_min_cutoff_));
553 write_entry<T>(/*SIC!*/ numerator[0], alpha_i, i);
554 };
555
556 cpu_simd_loop<Number>(
557 "mesh_adaptor_2", body_normalize, 0, n_internal, n_owned);
558
559 /*
560 * Widen indicators over stencil via max() operator:
561 */
562
563 const auto body_widen = [&](auto sentinel, unsigned int i) {
564 using T = decltype(sentinel);
565 const unsigned int stride_size = sparsity_simd_view.stride_of_row(i);
566
567 /* Skip constrained degrees of freedom: */
568 const unsigned int row_length = sparsity_simd_view.row_length(i);
569 if (row_length == 1)
570 return;
571
572 auto alpha_i = read_entry<T>(numerator[0], i);
573
574 const unsigned int *js = sparsity_simd_view.columns(i);
575 for (unsigned int col_idx = 0; col_idx < row_length;
576 ++col_idx, js += stride_size) {
577
578 /* Skip diagonal. */
579 if (col_idx == 0)
580 continue;
581
582 const auto alpha_j = read_entry<T>(numerator[0], js);
583
584 alpha_i = std::max(alpha_i, alpha_j);
585 }
586
587 write_entry<T>(/*SIC!*/ denominator[0], alpha_i, i);
588 };
589
590 for (unsigned int cycle = 0; cycle < smoothness_widen_stencil_; ++cycle) {
591 numerator[0].update_ghost_values();
592 cpu_simd_loop<Number>(
593 "mesh_adaptor_3", body_widen, 0, n_internal, n_owned);
594 numerator[0] = /*SIC!*/ denominator[0];
595 }
596
597 numerator[0].update_ghost_values();
598 affine_constraints.distribute(numerator[0]);
599
600 /*
601 * Insert result into smoothness_indicators_:
602 */
603
604 smoothness_indicators_.reinit_with_scalar_partitioner(scalar_partitioner,
605 1);
606 const auto smoothness_indicators_view =
607 smoothness_indicators_.template view<1>();
608 smoothness_indicators_view.insert_component(numerator[0], 0);
609 smoothness_indicators_view.update_ghost_values();
610 }
611} // namespace ryujin
void mark_cells_for_coarsening_and_refinement(dealii::Triangulation< dim > &triangulation) const
void compute_smoothness_indicators(const StateVector< Number > &state_vector) const
void analyze(const StateVector< Number > &state_vector, const Number t, unsigned int cycle)
MeshAdaptor(const MPIEnsemble &mpi_ensemble, const OfflineData< dim, Number > &offline_data, const HyperbolicSystem &hyperbolic_system, const Interface::ParabolicSystem &parabolic_system, const MultiComponentVector< Number > &initial_precomputed, const ScalarVector< Number > &alpha, const std::string &subsection="/MeshAdaptor")
typename HyperbolicDescription::HyperbolicSystem HyperbolicSystem
void prepare(const Number t)