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