ryujin 2.1.1 revision 9dcb748690310d6a540ebb8b066d1a0834fc7604
riemann_solver.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 <compile_time_options.h>
9
10#include "riemann_solver.h"
11
12#include <newton.h>
13#include <simd.h>
14
15// #define DEBUG_RIEMANN_SOLVER
16
17namespace ryujin
18{
19 namespace Euler
20 {
21 template <int dim, typename Number>
22 DEAL_II_ALWAYS_INLINE inline Number
24 const Number p_star) const
25 {
26 const auto &[rho, u, p, a] = riemann_data;
27 const auto gamma = hyperbolic_system.gamma();
28
29 const Number Az = ScalarNumber(2.) / (rho * (gamma + Number(1.)));
30 const Number Bz =
31 (gamma - ScalarNumber(1.)) / (gamma + ScalarNumber(1.)) * p;
32 const Number radicand = Az / (p_star + Bz);
33 const Number true_value = (p_star - p) * std::sqrt(radicand);
34
35 const auto exponent =
36 ScalarNumber(0.5) * (gamma - ScalarNumber(1.)) / gamma;
37 const Number factor = ryujin::pow(p_star / p, exponent) - Number(1.);
38 const auto false_value =
39 ScalarNumber(2.) * a * factor / (gamma - ScalarNumber(1.));
40
41 return dealii::compare_and_apply_mask<
42 dealii::SIMDComparison::greater_than_or_equal>(
43 p_star, p, true_value, false_value);
44 }
45
46
47 template <int dim, typename Number>
48 DEAL_II_ALWAYS_INLINE inline Number
50 const primitive_type &riemann_data_j,
51 const Number p_in) const
52 {
53 const Number &u_i = riemann_data_i[1];
54 const Number &u_j = riemann_data_j[1];
55
56 return f(riemann_data_i, p_in) + f(riemann_data_j, p_in) + u_j - u_i;
57 }
58
59
60 /*
61 * The approximate Riemann solver is based on a function phi(p) that is
62 * montone increasing in p, concave down and whose (weak) third
63 * derivative is non-negative and locally bounded [1, p. 912]. Because we
64 * actually do not perform any iteration for computing our wavespeed
65 * estimate we can get away by only implementing a specialized variant of
66 * the phi function that computes phi(p_max). It inlines the
67 * implementation of the "f" function and eliminates all unnecessary
68 * branches in "f".
69 *
70 * Cost: 0x pow, 2x division, 2x sqrt
71 */
72 template <int dim, typename Number>
73 DEAL_II_ALWAYS_INLINE inline Number
75 const primitive_type &riemann_data_i,
76 const primitive_type &riemann_data_j) const
77 {
78 const auto &[rho_i, u_i, p_i, a_i] = riemann_data_i;
79 const auto &[rho_j, u_j, p_j, a_j] = riemann_data_j;
80
81 const Number p_max = std::max(p_i, p_j);
82
83 const auto gamma = hyperbolic_system.gamma();
84
85 const Number radicand_inverse_i = ScalarNumber(0.5) * rho_i *
86 ((gamma + ScalarNumber(1.)) * p_max +
87 (gamma - ScalarNumber(1.)) * p_i);
88
89 const Number value_i = (p_max - p_i) / std::sqrt(radicand_inverse_i);
90
91 const Number radicand_inverse_j = ScalarNumber(0.5) * rho_j *
92 ((gamma + ScalarNumber(1.)) * p_max +
93 (gamma - ScalarNumber(1.)) * p_j);
94
95 const Number value_j = (p_max - p_j) / std::sqrt(radicand_inverse_j);
96
97 return value_i + value_j + u_j - u_i;
98 }
99
100
101 /*
102 * Next we construct approximations for the two extreme wave speeds of
103 * the Riemann fan [1, p. 912, (3.7) + (3.8)] and compute an upper bound
104 * lambda_max of the maximal wave speed:
105 */
106
107
108 /*
109 * see [1], page 912, (3.7)
110 *
111 * Cost: 0x pow, 1x division, 1x sqrt
112 */
113 template <int dim, typename Number>
114 DEAL_II_ALWAYS_INLINE inline Number
116 const primitive_type &riemann_data, const Number p_star) const
117 {
118 const auto &[rho, u, p, a] = riemann_data;
119 const auto inv_p = ScalarNumber(1.0) / p;
120
121 const auto gamma = hyperbolic_system.gamma();
122 const auto gamma_inverse = hyperbolic_system.gamma_inverse();
123 const auto factor =
124 (gamma + ScalarNumber(1.0)) * ScalarNumber(0.5) * gamma_inverse;
125 const Number tmp = positive_part((p_star - p) * inv_p);
126
127 return u - a * std::sqrt(ScalarNumber(1.0) + factor * tmp);
128 }
129
130
131 /*
132 * see [1], page 912, (3.8)
133 *
134 * Cost: 0x pow, 1x division, 1x sqrt
135 */
136 template <int dim, typename Number>
137 DEAL_II_ALWAYS_INLINE inline Number
139 const primitive_type &primitive_state, const Number p_star) const
140 {
141 const auto &[rho, u, p, a] = primitive_state;
142 const auto inv_p = ScalarNumber(1.0) / p;
143
144 const auto gamma = hyperbolic_system.gamma();
145 const auto gamma_inverse = hyperbolic_system.gamma_inverse();
146 const Number factor =
147 (gamma + ScalarNumber(1.0)) * ScalarNumber(0.5) * gamma_inverse;
148 const Number tmp = positive_part((p_star - p) * inv_p);
149 return u + a * std::sqrt(Number(1.0) + factor * tmp);
150 }
151
152
153 /*
154 * For two given primitive states <code>riemann_data_i</code> and
155 * <code>riemann_data_j</code>, and a guess p_2, compute an upper bound
156 * for lambda.
157 *
158 * This is the same lambda_max as computed by compute_gap. The function
159 * simply avoids a number of unnecessary computations (in case we do
160 * not need to know the gap).
161 *
162 * Cost: 0x pow, 2x division, 2x sqrt
163 */
164 template <int dim, typename Number>
165 DEAL_II_ALWAYS_INLINE inline Number
167 const primitive_type &riemann_data_i,
168 const primitive_type &riemann_data_j,
169 const Number p_star) const
170 {
171 const Number nu_11 = lambda1_minus(riemann_data_i, p_star);
172 const Number nu_32 = lambda3_plus(riemann_data_j, p_star);
173
174 return std::max(positive_part(nu_32), negative_part(nu_11));
175 }
176
177
178 /*
179 * Two-rarefaction approximation to p_star computed for two primitive
180 * states <code>riemann_data_i</code> and <code>riemann_data_j</code>.
181 *
182 * See [1], page 914, (4.3)
183 *
184 * Cost: 2x pow, 2x division, 0x sqrt
185 */
186 template <int dim, typename Number>
187 DEAL_II_ALWAYS_INLINE inline Number
189 const primitive_type &riemann_data_i,
190 const primitive_type &riemann_data_j) const
191 {
192 const auto &[rho_i, u_i, p_i, a_i] = riemann_data_i;
193 const auto &[rho_j, u_j, p_j, a_j] = riemann_data_j;
194 const auto inv_p_j = ScalarNumber(1.) / p_j;
195
196 /*
197 * Nota bene (cf. [1, (4.3)]):
198 * a_Z^0 * sqrt(1 - b * rho_Z) = a_Z * (1 - b * rho_Z)
199 * We have computed a_Z already, so we are simply going to use this
200 * identity below:
201 */
202
203 const auto &gamma = hyperbolic_system.gamma();
204 const auto factor = (gamma - ScalarNumber(1.)) * ScalarNumber(0.5);
205
206 /*
207 * Nota bene (cf. [1, (3.6)]: The condition "numerator > 0" is the
208 * well-known non-vacuum condition. In case we encounter numerator <= 0
209 * then p_star = 0 is the correct pressure to compute the wave speed.
210 * Therefore, all we have to do is to take the positive part of the
211 * expression:
212 */
213 const auto &gamma_inverse = hyperbolic_system.gamma_inverse();
214 const auto &gm1_inverse = hyperbolic_system.gamma_minus_one_inverse();
215
216 const Number numerator = positive_part(a_i + a_j - factor * (u_j - u_i));
217 const Number denominator =
218 a_i * ryujin::pow(p_i * inv_p_j, -factor * gamma_inverse) + a_j;
219
220 const auto exponent = ScalarNumber(2.0) * gamma * gm1_inverse;
221
222 const auto p_1_tilde =
223 p_j * ryujin::pow(numerator / denominator, exponent);
224
225#ifdef DEBUG_RIEMANN_SOLVER
226 std::cout << "p_star_two_rarefaction = " << p_1_tilde << std::endl;
227#endif
228 return p_1_tilde;
229 }
230
231
232 /*
233 * Failsafe approximation to p_star computed for two primitive
234 * states <code>riemann_data_i</code> and <code>riemann_data_j</code>.
235 *
236 * See [1], page 914, (4.3)
237 *
238 * Cost: 2x pow, 2x division, 0x sqrt
239 */
240 template <int dim, typename Number>
241 DEAL_II_ALWAYS_INLINE inline Number
243 const primitive_type &riemann_data_i,
244 const primitive_type &riemann_data_j) const
245 {
246 const auto &[rho_i, u_i, p_i, a_i] = riemann_data_i;
247 const auto &[rho_j, u_j, p_j, a_j] = riemann_data_j;
248
249 const auto &gamma = hyperbolic_system.gamma();
250
251 /*
252 * Compute (5.11) formula for \tilde p_2^\ast:
253 *
254 * Cost: 0x pow, 3x division, 3x sqrt
255 */
256
257 const Number p_max = std::max(p_i, p_j);
258
259 Number radicand_i = ScalarNumber(2.) * p_max;
260 radicand_i /=
261 rho_i * ((gamma + Number(1.)) * p_max + (gamma - Number(1.)) * p_i);
262
263 const Number x_i = std::sqrt(radicand_i);
264
265 Number radicand_j = ScalarNumber(2.) * p_max;
266 radicand_j /=
267 rho_j * ((gamma + Number(1.)) * p_max + (gamma - Number(1.)) * p_j);
268
269 const Number x_j = std::sqrt(radicand_j);
270
271 const Number a = x_i + x_j;
272 const Number b = u_j - u_i;
273 const Number c = -p_i * x_i - p_j * x_j;
274
275 const Number base = (-b + std::sqrt(b * b - ScalarNumber(4.) * a * c)) /
276 (ScalarNumber(2.) * a);
277 const Number p_2_tilde = base * base;
278
279#ifdef DEBUG_RIEMANN_SOLVER
280 std::cout << "p_star_failsafe = " << p_2_tilde << std::endl;
281#endif
282 return p_2_tilde;
283 }
284
285
286 template <int dim, typename Number>
288 const primitive_type &riemann_data_i,
289 const primitive_type &riemann_data_j) const
290 {
291 /*
292 * For exactly solving the Riemann problem we need to start with a
293 * good upper and lower bound, p_1 <= p_star <= p_2, for finding
294 * phi(p_star) == 0. This implies that we have to ensure that
295 * phi(p_2) >= 0 and phi(p_1) <= 0.
296 *
297 * Instead of solving the Riemann problem exactly, however we will
298 * simply use the upper bound p_2 (with p_2 >= p_star) to compute
299 * lambda_max and return the estimate.
300 *
301 * We will use three candidates, p_min, p_max and the two rarefaction
302 * approximation p_star_tilde. We have (up to round-off errors) that
303 * phi(p_star_tilde) >= 0. So this is a safe upper bound, it might
304 * just be too large.
305 *
306 * Depending on the sign of phi(p_max) we select the following ranges:
307 *
308 * phi(p_max) < 0:
309 * p_1 <- p_max and p_2 <- p_star_tilde
310 *
311 * phi(p_max) >= 0:
312 * p_1 <- p_min and p_2 <- min(p_max, p_star_tilde)
313 *
314 * Nota bene:
315 *
316 * - The special case phi(p_max) == 0 as discussed in [1] is already
317 * contained in the second condition.
318 *
319 * - In principle, we would have to treat the case phi(p_min) > 0 as
320 * well. This corresponds to two expansion waves and a good
321 * estimate for the wavespeed is obtained by simply computing
322 * lambda_max with p_2 = 0.
323 *
324 * However, it turns out that numerically in this case we will
325 * have
326 *
327 * 0 < p_star <= p_star_tilde <= p_min <= p_max.
328 *
329 * So it is sufficient to end up with p_2 = p_star_tilde (!!) to
330 * compute the exact same wave speed as for p_2 = 0.
331 *
332 * Note: If for some reason p_star should be computed exactly,
333 * then p_1 has to be set to zero. This can be done efficiently by
334 * simply checking for p_2 < p_1 and setting p_1 <- 0 if
335 * necessary.
336 */
337
338 const auto &[rho_i, u_i, p_i, a_i] = riemann_data_i;
339 const auto &[rho_j, u_j, p_j, a_j] = riemann_data_j;
340
341#ifdef DEBUG_RIEMANN_SOLVER
342 std::cout << "rho_left: " << rho_i << std::endl;
343 std::cout << "u_left: " << u_i << std::endl;
344 std::cout << "p_left: " << p_i << std::endl;
345 std::cout << "a_left: " << a_i << std::endl;
346 std::cout << "rho_right: " << rho_j << std::endl;
347 std::cout << "u_right: " << u_j << std::endl;
348 std::cout << "p_right: " << p_j << std::endl;
349 std::cout << "a_right: " << a_j << std::endl;
350#endif
351
352 const Number p_max = std::max(p_i, p_j);
353
354 const Number rarefaction =
355 p_star_two_rarefaction(riemann_data_i, riemann_data_j);
356 const Number failsafe = p_star_failsafe(riemann_data_i, riemann_data_j);
357 const Number p_star_tilde = std::min(rarefaction, failsafe);
358
359 const Number phi_p_max = phi_of_p_max(riemann_data_i, riemann_data_j);
360
361 const Number p_2 =
362 dealii::compare_and_apply_mask<dealii::SIMDComparison::less_than>(
363 phi_p_max,
364 Number(0.),
365 p_star_tilde,
366 std::min(p_max, p_star_tilde));
367
368#ifdef DEBUG_RIEMANN_SOLVER
369 std::cout << " p^*_tilde = " << p_2 << "\n";
370 std::cout << " phi(p_*_t) = "
371 << phi(riemann_data_i, riemann_data_j, p_2) << "\n";
372 std::cout << "-> lambda_max = "
373 << compute_lambda(riemann_data_i, riemann_data_j, p_2)
374 << std::endl;
375#endif
376
377 return compute_lambda(riemann_data_i, riemann_data_j, p_2);
378 }
379
380 } // namespace Euler
381} // namespace ryujin
Number phi_of_p_max(const primitive_type &riemann_data_i, const primitive_type &riemann_data_j) const
Number compute_lambda(const primitive_type &riemann_data_i, const primitive_type &riemann_data_j, const Number p_star) const
Number lambda1_minus(const primitive_type &riemann_data, const Number p_star) const
Number p_star_two_rarefaction(const primitive_type &riemann_data_i, const primitive_type &riemann_data_j) const
Number compute(const primitive_type &riemann_data_i, const primitive_type &riemann_data_j) const
std::array< Number, riemann_data_size > primitive_type
Number p_star_failsafe(const primitive_type &riemann_data_i, const primitive_type &riemann_data_j) const
typename HyperbolicSystemView::ScalarNumber ScalarNumber
Number lambda3_plus(const primitive_type &primitive_state, const Number p_star) const
std::array< Number, riemann_data_size > primitive_type
typename get_value_type< Number >::type ScalarNumber
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