ryujin 2.1.1 revision ee5cbcbf2346c1299c942d0e1f13b46449973c18
Loading...
Searching...
No Matches
wave_speed_estimator.template.h
Go to the documentation of this file.
1//
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3// Copyright (C) 2023 - 2026 by the ryujin authors
4//
5
6#pragma once
7
8#include <compile_time_options.h>
9
11
12#include <simd.h>
13
14#include <random>
15
16// #define DEBUG_WAVE_SPEED_ESTIMATOR
17
18namespace ryujin
19{
20 namespace ScalarConservation
21 {
22 template <int dim, typename Number>
24 const Number &u_i,
25 const Number &u_j,
26 const precomputed_type &prec_i,
27 const precomputed_type &prec_j,
28 const dealii::Tensor<1, dim, Number> &n_ij) const
29 {
30 /* Project all fluxes to 1D: */
31 const Number f_i = view_.construct_flux_tensor(prec_i) * n_ij;
32 const Number f_j = view_.construct_flux_tensor(prec_j) * n_ij;
33 const Number df_i = view_.construct_flux_gradient_tensor(prec_i) * n_ij;
34 const Number df_j = view_.construct_flux_gradient_tensor(prec_j) * n_ij;
35
36 const auto h2 = Number(2. * view_.derivative_approximation_delta());
37
38#ifdef DEBUG_WAVE_SPEED_ESTIMATOR
39 std::cout << "\nu_i = " << u_i << std::endl;
40 std::cout << "u_j = " << u_j << std::endl;
41 std::cout << "f_i = " << f_i << std::endl;
42 std::cout << "f_j = " << f_j << std::endl;
43 std::cout << "df_i = " << df_i << std::endl;
44 std::cout << "df_j = " << df_j << std::endl;
45#endif
46
47 /*
48 * The Roe average with a regularization based on $h$ which is the
49 * step size used for the central difference approximation of f'(u).
50 *
51 * The regularization max(|u_i - u_j|, 2 * h) ensures that the
52 * quotient approximates the derivative f'( (u_i + u_j)/2 ) to the
53 * same precision that we use to compute f'(u_i) and f'(u_j) in the
54 * FunctionParser (via a central difference approximation).
55 *
56 * This implies that in contrast to the actual limit of the
57 * difference quotient we will approach 0 as |u_j - u_i| goes to
58 * zero. We fix this by taking the maximum with our approximation of
59 * f'(u_i) and f'(u_j) further down below.
60 */
61
62 auto lambda_max = std::abs(f_i - f_j) / std::max(std::abs(u_i - u_j), h2);
63#ifdef DEBUG_WAVE_SPEED_ESTIMATOR
64 std::cout << " Roe average = " << lambda_max << std::endl;
65#endif
66
67 constexpr auto gte = dealii::SIMDComparison::greater_than_or_equal;
68
69 if (wave_speed_estimator_.use_greedy_wavespeed()) {
70 /*
71 * In case of a greedy estimate we make sure that we always use the
72 * Roe average and only fall back to the derivative approximation
73 * when u_i and u_j are close to each other within 2h:
74 */
75 lambda_max = dealii::compare_and_apply_mask<gte>(
76 std::abs(u_i - u_j),
77 h2,
78 lambda_max,
79 /* Approximate derivative in centerpoint: */
80 std::abs(ScalarNumber(0.5) * (df_i + df_j)));
81#ifdef DEBUG_WAVE_SPEED_ESTIMATOR
82 std::cout << " interpolated = "
83 << std::abs(ScalarNumber(0.5) * (df_i + df_j)) << std::endl;
84#endif
85
86 } else {
87 /*
88 * Always take the maximum with |f'(u_i)| and |f'(u_j)|.
89 *
90 * For convex fluxes this implies that lambda_max is indeed the
91 * maximal wavespeed of the system. See Example 79.17 in reference
92 * @cite ErnGuermond2021.
93 */
94 lambda_max = std::max(lambda_max, std::abs(df_i));
95 lambda_max = std::max(lambda_max, std::abs(df_j));
96#ifdef DEBUG_WAVE_SPEED_ESTIMATOR
97 std::cout << " left derivative = " << std::abs(df_i) << std::endl;
98 std::cout << " right derivative = " << std::abs(df_j) << std::endl;
99#endif
100 }
101
102 /*
103 * Thread-local helper lambda to generate a random number in [0,1]:
104 */
105
106 thread_local static const auto draw = []() {
107 static std::random_device random_device;
108 static auto generator = std::default_random_engine(random_device());
109 static std::uniform_real_distribution<ScalarNumber> dist(0., 1.);
110
111 if constexpr (std::is_same_v<ScalarNumber, Number>) {
112 /*
113 * Scalar quantity:
114 */
115 return dist(generator);
116
117 } else {
118 /*
119 * Populate a vectorized array:
120 */
121 Number result;
122 for (unsigned int s = 0; s < Number::size(); ++s)
123 result[s] = dist(generator);
124 return result;
125 }
126 };
127
128 /*
129 * Helper functions for enforcing entropy inequalities:
130 */
131
132 const auto enforce_entropy = [&](const Number &k) {
133 const Number f_k = view_.flux_function(k) * n_ij;
134
135#ifdef DEBUG_WAVE_SPEED_ESTIMATOR
136 std::cout << "k = " << k << std::endl;
137 std::cout << "f_k = " << f_k << std::endl;
138#endif
139
140 const Number eta_i = view_.kruzkov_entropy(k, u_i);
141 const Number q_i =
142 view_.kruzkov_entropy_derivative(k, u_i) * (f_i - f_k);
143
144 const Number eta_j = view_.kruzkov_entropy(k, u_j);
145 const Number q_j =
146 view_.kruzkov_entropy_derivative(k, u_j) * (f_j - f_k);
147
148 const Number a = u_i + u_j - ScalarNumber(2.) * k;
149 const Number b = f_j - f_i;
150 const Number c = eta_i + eta_j;
151 const Number d = q_j - q_i;
152
153 /*
154 * FIXME: Ordinarily, lambda_left and lambda_right would be
155 * computed without taking the absolute value of the numerator.
156 * (The denominator is - in the absence of rounding errors - always
157 * nonnegative. The numerator has a sign.)
158 * But empirically it turns out that taking the absolute value and
159 * letting both estimates participate in the maximal wavespeed
160 * estimate helps a lot.
161 */
162 const Number lambda_left = std::abs(d + b) / (std::abs(c + a) + h2);
163 const Number lambda_right = std::abs(d - b) / (std::abs(c - a) + h2);
164
165#ifdef DEBUG_WAVE_SPEED_ESTIMATOR
166 std::cout << " left wavespeed = " << lambda_left << std::endl;
167 std::cout << " right wavespeed = " << lambda_right << std::endl;
168#endif
169 lambda_max = std::max(lambda_max, lambda_left);
170 lambda_max = std::max(lambda_max, lambda_right);
171 };
172
173
174 if (wave_speed_estimator_.use_averaged_entropy()) {
175 const Number k = ScalarNumber(0.5) * (u_i + u_j);
176 enforce_entropy(k);
177 }
178
179 const unsigned int n_entropies = wave_speed_estimator_.random_entropies();
180 for (unsigned int i = 0; i < n_entropies; ++i) {
181 const Number factor = draw();
182 const Number k = factor * u_i + (Number(1.) - factor) * u_j;
183 enforce_entropy(k);
184 }
185
186#ifdef DEBUG_WAVE_SPEED_ESTIMATOR
187 std::cout << "-> lambda_max = " << lambda_max << std::endl;
188#endif
189 return lambda_max;
190 }
191
192
193 template <int dim, typename Number>
194 DEAL_II_ALWAYS_INLINE inline Number
196 const PrecomputedVectorView &pv,
197 const state_type &U_i,
198 const state_type &U_j,
199 const unsigned int i,
200 const unsigned int *js,
201 const dealii::Tensor<1, dim, Number> &n_ij) const
202 {
203 using pst = typename View::precomputed_type;
204
205 const auto u_i = view_.state(U_i);
206 const auto u_j = view_.state(U_j);
207
208 const auto prec_i = pv.template read_tensor<Number, pst>(i);
209 const auto prec_j = pv.template read_tensor<Number, pst>(js);
210
211 return compute(u_i, u_j, prec_i, prec_j, n_ij);
212 }
213
214 } // namespace ScalarConservation
215} // namespace ryujin
std::array< Number, n_precomputed_values > precomputed_type
typename View::PrecomputedVectorView PrecomputedVectorView
Number compute(const Number &u_i, const Number &u_j, const precomputed_type &prec_i, const precomputed_type &prec_j, const dealii::Tensor< 1, dim, Number > &n_ij) const