ryujin 2.1.1 revision 9dcb748690310d6a540ebb8b066d1a0834fc7604
limiter.template.h
Go to the documentation of this file.
1//
2// SPDX-License-Identifier: MIT
3// Copyright (C) 2020 - 2023 by the ryujin authors
4//
5
6#pragma once
7
8#include "limiter.h"
9
10namespace ryujin
11{
12 namespace ShallowWater
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 bool success = true;
23
24 Number t_l = t_min;
25 Number t_r = t_max;
26
27 constexpr ScalarNumber min = std::numeric_limits<ScalarNumber>::min();
28 constexpr ScalarNumber eps = std::numeric_limits<ScalarNumber>::epsilon();
29 const ScalarNumber relax_small = ScalarNumber(
30 1. + hyperbolic_system.dry_state_relaxation_sharp() * eps); // FIXME
31 const ScalarNumber relax =
32 ScalarNumber(1. + hyperbolic_system.dry_state_relaxation_mollified() *
33 eps); // FIXME
34
35 /*
36 * We first limit the water_depth h.
37 *
38 * See [Guermond et al, 2021] (5.7).
39 */
40 {
41 auto h_U = hyperbolic_system.water_depth(U);
42 const auto &h_P = hyperbolic_system.water_depth(P);
43 const auto &h_min = std::get<0>(bounds);
44 const auto &h_max = std::get<1>(bounds);
45
46 const auto test_min = hyperbolic_system.filter_dry_water_depth(
47 std::max(Number(0.), h_U - relax * h_max));
48 const auto test_max = hyperbolic_system.filter_dry_water_depth(
49 std::max(Number(0.), h_min - relax * h_U));
50
51 if (!(test_min == Number(0.) && test_max == Number(0.))) {
52#ifdef DEBUG_OUTPUT
53 std::cout << std::fixed << std::setprecision(16);
54 std::cout << "Bounds violation: low-order water depth (critical)!\n";
55 std::cout << "\t\th min: " << h_min << "\n";
56 std::cout << "\t\th: " << h_U << "\n";
57 std::cout << "\t\th max: " << h_max << "\n" << std::endl;
58#endif
59 success = false;
60 }
61
62 const Number denominator =
63 h_P / std::max(std::abs(h_P * h_P), Number(100. * min));
64 constexpr auto lte = dealii::SIMDComparison::less_than_or_equal;
65
66 t_r = dealii::compare_and_apply_mask<lte>(h_max,
67 h_U + t_r * h_P,
68 positive_part(h_max - h_U) *
69 denominator,
70 t_r);
71
72 t_r = dealii::compare_and_apply_mask<lte>(h_U + t_r * h_P,
73 h_min,
74 positive_part(h_min - h_U) *
75 denominator,
76 t_r);
77
78 /* Box back into bounds: */
79 t_r = std::min(t_r, t_max);
80 t_r = std::max(t_r, t_min);
81
82 /*
83 * Enforce strict limiting on dry states:
84 */
85 t_r = dealii::compare_and_apply_mask<dealii::SIMDComparison::equal>(
86 hyperbolic_system.filter_dry_water_depth(h_U),
87 Number(0.),
88 t_l,
89 t_r);
90
91#ifdef CHECK_BOUNDS
92 /*
93 * Verify that the new state is within bounds:
94 */
95 const auto h_new = hyperbolic_system.water_depth(U + t_r * P);
96 const auto test_new_min = hyperbolic_system.filter_dry_water_depth(
97 std::max(Number(0.), h_new - relax * h_max));
98 const auto test_new_max = hyperbolic_system.filter_dry_water_depth(
99 std::max(Number(0.), h_min - relax * h_new));
100
101 if (!(test_new_min == Number(0.) && test_new_max == Number(0.))) {
102#ifdef DEBUG_OUTPUT
103 std::cout << std::fixed << std::setprecision(30);
104 std::cout << "Bounds violation: high-order water depth!\n";
105 std::cout << "\t\th min: " << h_min << "\n";
106 std::cout << "\t\th: " << h_new << "\n";
107 std::cout << "\t\th max: " << h_max << "\n";
108 std::cout << "\t\th_U: " << h_U << "\n";
109 std::cout << "\t\th_P: " << h_P << "\n";
110 std::cout << "\t\tt_r: " << t_r << "\n" << std::endl;
111#endif
112 success = false;
113 }
114#endif
115 }
116
117 /*
118 * Then limit the (negative) kinetic energy:
119 *
120 * See [Guermond et al, 2021] (5.10).
121 *
122 * Given initial limiter values t_l and t_r with psi(t_l) > 0 and
123 * psi(t_r) < 0 we try to find t^\ast with psi(t^\ast) \approx 0.
124 *
125 * Here, psi is the function:
126 *
127 * psi = h KE_i^max - 1/2 |q|^2
128 */
129
130 {
131 const auto &kin_max = std::get<2>(bounds);
132
133 /* We first check if t_r is a good state */
134
135 const auto U_r = U + t_r * P;
136 const auto h_r = hyperbolic_system.water_depth(U_r);
137 const auto q_r = hyperbolic_system.momentum(U_r);
138
139 const auto psi_r =
140 relax_small * h_r * kin_max - ScalarNumber(0.5) * q_r.norm_square();
141
142 /*
143 * If psi_r > 0 the right state is fine, force returning t_r by
144 * setting t_l = t_r:
145 */
146 t_l = dealii::compare_and_apply_mask<
147 dealii::SIMDComparison::greater_than>(psi_r, Number(0.), t_r, t_l);
148
149 /* If we have set t_l = t_r everywhere we can return: */
150 if (t_l == t_r)
151 return {t_l, success};
152
153#ifdef DEBUG_OUTPUT_LIMITER
154 {
155 std::cout << std::endl;
156 std::cout << std::fixed << std::setprecision(16);
157 std::cout << "t_l: (start) " << t_l << std::endl;
158 std::cout << "t_r: (start) " << t_r << std::endl;
159 }
160#endif
161
162 const auto U_l = U + t_l * P;
163 const auto h_l = hyperbolic_system.water_depth(U_l);
164 const auto q_l = hyperbolic_system.momentum(U_l);
165
166 const auto psi_l =
167 relax_small * h_l * kin_max - ScalarNumber(0.5) * q_l.norm_square();
168
169 /*
170 * Verify that the left state is within bounds. This property might
171 * be violated for relative CFL numbers larger than 1.
172 *
173 * We use a non-scaled eps here to force the lower_bound to be
174 * negative so that we do not accidentally trigger in "perfect" dry
175 * states with h_l equal to zero.
176 */
177 const auto filtered_h_l = hyperbolic_system.filter_dry_water_depth(h_l);
178 const auto lower_bound =
179 (ScalarNumber(1.) - relax) * filtered_h_l * kin_max - eps;
180 if (!(std::min(Number(0.), psi_l - lower_bound + min) == Number(0.))) {
181#ifdef DEBUG_OUTPUT
182 std::cout << std::fixed << std::setprecision(16);
183 std::cout
184 << "Bounds violation: low-order kinetic energy (critical)!\n";
185 std::cout << "\t\tPsi left: 0 <= " << psi_l << "\n" << std::endl;
186#endif
187 success = false;
188 }
189
190 /*
191 * Return if the window between t_l and t_r is within the prescribed
192 * tolerance:
193 */
194 if (std::max(Number(0.), t_r - t_l - newton_tolerance) == Number(0.))
195 return {t_l, success};
196
197 /*
198 * If the bound is not satisfied, we need to find the root of a
199 * quadratic function:
200 *
201 * psi(t) = (h_U + t h_P) kin_max
202 * - 1/2 (|q_U|^2 + 2(q_U * q_P) t + |q_P|^2 t^2)
203 *
204 * d_psi(t) = h_P kin_max - (q_U * q_P) - |q_P|^2 t
205 *
206 * We can compute the root of this function efficiently by using our
207 * standard quadratic_newton_step() function that will use the points
208 * [p1, p1, p2] as well as [p1, p2, p2] to construct two quadratic
209 * polynomials to compute new candiates for the bounds [t_l, t_r]. In
210 * case of a quadratic function psi(t) both polynomials will coincide
211 * so that (up to round-off error) t_l = t_r.
212 */
213
214 const auto &h_P = hyperbolic_system.water_depth(P);
215 const auto &q_U = hyperbolic_system.momentum(U);
216 const auto &q_P = hyperbolic_system.momentum(P);
217
218 const auto dpsi_l = h_P * kin_max - (q_U * q_P) - q_P * q_P * t_l;
219 const auto dpsi_r = h_P * kin_max - (q_U * q_P) - q_P * q_P * t_r;
220
222 t_l, t_r, psi_l, psi_r, dpsi_l, dpsi_r, Number(-1.));
223
224#ifdef DEBUG_OUTPUT_LIMITER
225 if (std::max(Number(0.), psi_r + Number(eps)) == Number(0.)) {
226 std::cout << "psi_l: " << psi_l << std::endl;
227 std::cout << "psi_r: " << psi_r << std::endl;
228 std::cout << "dpsi_l: " << dpsi_l << std::endl;
229 std::cout << "dpsi_r: " << dpsi_r << std::endl;
230 std::cout << "t_l: (end) " << t_l << std::endl;
231 std::cout << "t_r: (end) " << t_r << std::endl;
232 }
233#endif
234
235#ifdef CHECK_BOUNDS
236 /*
237 * Verify that the new state is within bounds:
238 */
239 {
240 const auto U_new = U + t_l * P;
241 const auto h_new = hyperbolic_system.water_depth(U_new);
242 const auto q_new = hyperbolic_system.momentum(U_new);
243
244 const auto psi_new = relax_small * h_new * kin_max -
245 ScalarNumber(0.5) * q_new.norm_square();
246
247 const auto lower_bound =
248 (ScalarNumber(1.) - relax) * h_new * kin_max - eps;
249
250 const bool psi_valid =
251 std::min(Number(0.), psi_new - lower_bound + min) == Number(0.);
252 if (!psi_valid) {
253#ifdef DEBUG_OUTPUT
254 std::cout << std::fixed << std::setprecision(16);
255 std::cout << "High-order bounds violation!\n";
256 std::cout << "\t\tDepth = " << h_new << "\n";
257 std::cout << "\t\tPsi: 0 <= " << psi_new << "\n" << std::endl;
258#endif
259 success = false;
260 }
261 }
262#endif
263 }
264
265 return {t_l, success};
266 }
267
268 } // namespace ShallowWater
269} // namespace ryujin
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:89
typename get_value_type< Number >::type ScalarNumber
Definition: limiter.h:59
HyperbolicSystem::state_type< dim, Number > state_type
Definition: limiter.h:37
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
DEAL_II_ALWAYS_INLINE Number positive_part(const Number number)
Definition: simd.h:111