ryujin 2.1.1 revision f751393c604e228afd3e8516e9e236ec609caae0
limiter.template.h
Go to the documentation of this file.
1//
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3// Copyright (C) 2020 - 2024 by the ryujin authors
4//
5
6#pragma once
7
8#include "limiter.h"
9
10namespace ryujin
11{
12 namespace EulerAEOS
13 {
14 template <int dim, typename Number>
15 std::tuple<Number, bool>
17 const state_type &U,
18 const state_type &P,
19 const Number t_min /* = Number(0.) */,
20 const Number t_max /* = Number(1.) */)
21 {
22 const auto view = hyperbolic_system.view<dim, Number>();
23
24 bool success = true;
25 Number t_r = t_max;
26
27 constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();
28 const auto small = view.vacuum_state_relaxation_small();
29 const auto large = view.vacuum_state_relaxation_large();
30 const ScalarNumber relax_small = ScalarNumber(1. + small * eps);
31 const ScalarNumber relax = ScalarNumber(1. + large * eps);
32
33 /*
34 * First limit the density rho.
35 *
36 * See [Guermond, Nazarov, Popov, Thomas] (4.8):
37 */
38
39 {
40 const auto &rho_U = view.density(U);
41 const auto &rho_P = view.density(P);
42
43 const auto &rho_min = std::get<0>(bounds);
44 const auto &rho_max = std::get<1>(bounds);
45
46 /*
47 * Verify that rho_U is within bounds. This property might be
48 * violated for relative CFL numbers larger than 1.
49 */
50 const auto test_min = view.filter_vacuum_density(
51 std::max(Number(0.), rho_U - relax * rho_max));
52 const auto test_max = view.filter_vacuum_density(
53 std::max(Number(0.), rho_min - relax * rho_U));
54 if (!(test_min == Number(0.) && test_max == Number(0.))) {
55#ifdef DEBUG_OUTPUT
56 std::cout << std::fixed << std::setprecision(16);
57 std::cout << "Bounds violation: low-order density (critical)!"
58 << "\n\t\trho min: " << rho_min
59 << "\n\t\trho min (delta): "
60 << negative_part(rho_U - rho_min)
61 << "\n\t\trho: " << rho_U
62 << "\n\t\trho max (delta): "
63 << positive_part(rho_U - rho_max)
64 << "\n\t\trho max: " << rho_max << "\n"
65 << std::endl;
66#endif
67 success = false;
68 }
69
70 const Number denominator =
71 ScalarNumber(1.) / (std::abs(rho_P) + eps * rho_max);
72
73 t_r = dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(
74 rho_max,
75 rho_U + t_r * rho_P,
76 /*
77 * rho_P is positive.
78 *
79 * Note: Do not take an absolute value here. If we are out of
80 * bounds we have to ensure that t_r is set to t_min.
81 */
82 (rho_max - rho_U) * denominator,
83 t_r);
84
85 t_r = dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(
86 rho_U + t_r * rho_P,
87 rho_min,
88 /*
89 * rho_P is negative.
90 *
91 * Note: Do not take an absolute value here. If we are out of
92 * bounds we have to ensure that t_r is set to t_min.
93 */
94 (rho_U - rho_min) * denominator,
95 t_r);
96
97 /*
98 * Ensure that t_min <= t <= t_max. This might not be the case if
99 * rho_U is outside the interval [rho_min, rho_max]. Furthermore,
100 * the quotient we take above is prone to numerical cancellation in
101 * particular in the second pass of the limiter when rho_P might be
102 * small.
103 */
104 t_r = std::min(t_r, t_max);
105 t_r = std::max(t_r, t_min);
106
107#ifdef CHECK_BOUNDS
108 /*
109 * Verify that the new state is within bounds:
110 */
111 const auto rho_new = view.density(U + t_r * P);
112 const auto test_new_min = view.filter_vacuum_density(
113 std::max(Number(0.), rho_new - relax * rho_max));
114 const auto test_new_max = view.filter_vacuum_density(
115 std::max(Number(0.), rho_min - relax * rho_new));
116 if (!(test_new_min == Number(0.) && test_new_max == Number(0.))) {
117#ifdef DEBUG_OUTPUT
118 std::cout << std::fixed << std::setprecision(16);
119 std::cout << "Bounds violation: high-order density!"
120 << "\n\t\trho min: " << rho_min
121 << "\n\t\trho min (delta): "
122 << negative_part(rho_new - rho_min)
123 << "\n\t\trho: " << rho_new
124 << "\n\t\trho max (delta): "
125 << positive_part(rho_new - rho_max)
126 << "\n\t\trho max: " << rho_max << "\n"
127 << std::endl;
128#endif
129 success = false;
130 }
131#endif
132 }
133
134 /*
135 * Then limit the specific entropy:
136 *
137 * See [Guermond, Nazarov, Popov, Thomas], Section 4.6 + Section 5.1:
138 */
139
140 Number t_l = t_min; // good state
141
142 const auto &gamma = std::get<3>(bounds) /* = gamma_min*/;
143 const Number gm1 = gamma - Number(1.);
144
145 const auto interpolation_b = view.eos_interpolation_b();
146
147 {
148 /*
149 * Prepare a quadratic Newton method:
150 *
151 * Given initial limiter values t_l and t_r with psi(t_l) > 0 and
152 * psi(t_r) < 0 we try to find t^\ast with psi(t^\ast) \approx 0.
153 *
154 * FIXME -- Need to change comment
155 *
156 * Here, psi is a 3-convex function obtained by scaling the specific
157 * entropy s:
158 *
159 * psi = \rho ^ {\gamma + 1} s
160 *
161 * (s in turn was defined as s =\varepsilon \rho ^{-\gamma}, where
162 * \varepsilon = (\rho e) is the internal energy.)
163 */
164
165 const auto &s_min = std::get<2>(bounds);
166
167 for (unsigned int n = 0; n < parameters.newton_max_iterations(); ++n) {
168
169 const auto U_r = U + t_r * P;
170 const auto rho_r = view.density(U_r);
171 const auto rho_r_gamma = ryujin::pow(rho_r, gamma);
172 const auto rho_e_r = view.internal_energy(U_r);
173 const auto covolume_r = Number(1.) - interpolation_b * rho_r;
174
175 auto psi_r =
176 relax_small * rho_r * rho_e_r -
177 s_min * rho_r * rho_r_gamma * ryujin::pow(covolume_r, -gm1);
178
179#ifndef CHECK_BOUNDS
180 /*
181 * If psi_r > 0 the right state is fine, force returning t_r by
182 * setting t_l = t_r:
183 */
184 t_l = dealii::compare_and_apply_mask<
185 dealii::SIMDComparison::greater_than>(
186 psi_r, Number(0.), t_r, t_l);
187
188 /*
189 * If we have set t_l = t_r everywhere then all states state U_r
190 * with t_r obey the specific entropy inequality and we can
191 * break.
192 *
193 * This is a very important optimization: Only for 1 in (25 to
194 * 50) cases do we actually need to limit on the specific entropy
195 * because one of the right states failed. So we can skip
196 * constructing the left state U_l, which is expensive.
197 *
198 * This implies unfortunately that we might not accurately report
199 * whether the low_order update U itself obeyed bounds because
200 * U_r = U + t_r * P pushed us back into bounds. We thus skip
201 * this shortcut if `CHECK_BOUNDS` is set.
202 */
203 if (t_l == t_r)
204 break;
205#endif
206
207#ifdef DEBUG_OUTPUT_LIMITER
208 if (n == 0) {
209 std::cout << std::endl;
210 std::cout << std::fixed << std::setprecision(16);
211 std::cout << "t_l: (start) " << t_l << std::endl;
212 std::cout << "t_r: (start) " << t_r << std::endl;
213 }
214#endif
215
216 const auto U_l = U + t_l * P;
217 const auto rho_l = view.density(U_l);
218 const auto rho_l_gamma = ryujin::pow(rho_l, gamma);
219 const auto rho_e_l = view.internal_energy(U_l);
220 const auto covolume_l = Number(1.) - interpolation_b * rho_l;
221
222 auto psi_l =
223 relax_small * rho_l * rho_e_l -
224 s_min * rho_l * rho_l_gamma * ryujin::pow(covolume_l, -gm1);
225
226 /*
227 * Verify that the left state is within bounds. This property might
228 * be violated for relative CFL numbers larger than 1.
229 */
230 const auto lower_bound = (ScalarNumber(1.) - relax) * s_min * rho_l *
231 rho_l_gamma * ryujin::pow(covolume_l, -gm1);
232 if (n == 0 &&
233 !(std::min(Number(0.), psi_l - lower_bound) == Number(0.))) {
234#ifdef DEBUG_OUTPUT
235 std::cout << std::fixed << std::setprecision(16);
236 std::cout
237 << "Bounds violation: low-order specific entropy (critical)!\n";
238 std::cout << "\t\tPsi left: 0 <= " << psi_l << "\n" << std::endl;
239#endif
240 success = false;
241 }
242
243#ifdef CHECK_BOUNDS
244 /*
245 * If psi_r > 0 the right state is fine, force returning t_r by
246 * setting t_l = t_r:
247 */
248 t_l = dealii::compare_and_apply_mask<
249 dealii::SIMDComparison::greater_than>(
250 psi_r, Number(0.), t_r, t_l);
251#endif
252
253 /*
254 * Break if the window between t_l and t_r is within the prescribed
255 * tolerance:
256 */
257 const Number tolerance(parameters.newton_tolerance());
258 if (std::max(Number(0.), t_r - t_l - tolerance) == Number(0.))
259 break;
260
261 /* We got unlucky and have to perform a Newton step: */
262
263 const auto drho = view.density(P);
264 const auto drho_e_l = view.internal_energy_derivative(U_l) * P;
265 const auto drho_e_r = view.internal_energy_derivative(U_r) * P;
266
267 const auto extra_term_l =
268 s_min * ryujin::pow(rho_l / covolume_l, gamma) *
269 (covolume_l + gamma - interpolation_b * rho_l);
270 const auto extra_term_r =
271 s_min * ryujin::pow(rho_r / covolume_r, gamma) *
272 (covolume_r + gamma - interpolation_b * rho_r);
273
274 const auto dpsi_l =
275 rho_l * drho_e_l + (rho_e_l - extra_term_l) * drho;
276 const auto dpsi_r =
277 rho_r * drho_e_r + (rho_e_r - extra_term_r) * drho;
278
280 t_l, t_r, psi_l, psi_r, dpsi_l, dpsi_r, Number(-1.));
281
282#ifdef DEBUG_OUTPUT_LIMITER
283 std::cout << "psi_l: " << psi_l << std::endl;
284 std::cout << "psi_r: " << psi_r << std::endl;
285 std::cout << "dpsi_l: " << dpsi_l << std::endl;
286 std::cout << "dpsi_r: " << dpsi_r << std::endl;
287 std::cout << "t_l: ( " << n << " ) " << t_l << std::endl;
288 std::cout << "t_r: ( " << n << " ) " << t_r << std::endl;
289#endif
290 }
291
292#ifdef CHECK_BOUNDS
293 /*
294 * Verify that the new state is within bounds:
295 */
296 {
297 const auto U_new = U + t_l * P;
298
299 const auto rho_new = view.density(U_new);
300 const auto rho_new_gamma = ryujin::pow(rho_new, gamma);
301 const auto rho_e_new = view.internal_energy(U_new);
302 const auto covolume_new = Number(1.) - interpolation_b * rho_new;
303
304 const auto psi_new =
305 relax_small * rho_new * rho_e_new -
306 s_min * rho_new * rho_new_gamma * ryujin::pow(covolume_new, -gm1);
307
308 const auto lower_bound = (ScalarNumber(1.) - relax) * s_min *
309 rho_new * rho_new_gamma *
310 ryujin::pow(covolume_new, -gm1);
311
312 const bool e_valid = std::min(Number(0.), rho_e_new) == Number(0.);
313 const bool psi_valid =
314 std::min(Number(0.), psi_new - lower_bound) == Number(0.);
315
316 if (!e_valid || !psi_valid) {
317#ifdef DEBUG_OUTPUT
318 std::cout << std::fixed << std::setprecision(16);
319 std::cout << "Bounds violation: high-order specific entropy!\n";
320 std::cout << "\t\trho e: 0 <= " << rho_e_new << "\n";
321 std::cout << "\t\tPsi: 0 <= " << psi_new << "\n" << std::endl;
322#endif
323 success = false;
324 }
325 }
326#endif
327 }
328
329 return {t_l, success};
330 }
331
332 } // namespace EulerAEOS
333} // namespace ryujin
typename View::state_type state_type
Definition: limiter.h:113
typename View::ScalarNumber ScalarNumber
Definition: limiter.h:134
std::tuple< Number, bool > limit(const Bounds &bounds, const state_type &U, const state_type &P, const Number t_min=Number(0.), const Number t_max=Number(1.))
std::array< Number, n_bounds > Bounds
Definition: limiter.h:168
DEAL_II_ALWAYS_INLINE void quadratic_newton_step(Number &p_1, Number &p_2, const Number phi_p_1, const Number phi_p_2, const Number dphi_p_1, const Number dphi_p_2, const Number sign=Number(1.0))
Definition: newton.h:39
T pow(const T x, const T b)
DEAL_II_ALWAYS_INLINE Number negative_part(const Number number)
Definition: simd.h:123
DEAL_II_ALWAYS_INLINE Number positive_part(const Number number)
Definition: simd.h:111