Shamrock 2025.10.0
Astrophysical Code
Loading...
Searching...
No Matches
exact.hpp
Go to the documentation of this file.
1// -------------------------------------------------------//
2//
3// SHAMROCK code for hydrodynamics
4// Copyright (c) 2021-2026 Timothée David--Cléris <tim.shamrock@proton.me>
5// SPDX-License-Identifier: CeCILL Free Software License Agreement v2.1
6// Shamrock is licensed under the CeCILL 2.1 License, see LICENSE for more information
7//
8// -------------------------------------------------------//
9
10#pragma once
11
27
28#include "shambackends/math.hpp"
29#include "shambackends/sycl.hpp"
31
32namespace shammodels::gsph::riemann {
33
39 template<class Tscal>
40 struct ExactState {
41 Tscal v;
42 Tscal p;
43 Tscal r;
44 };
45
55 template<class Tscal>
56 inline Tscal exact_v_xc_shock(Tscal LR, Tscal p2, Tscal p1, Tscal r1, Tscal gamma) {
57 const Tscal inv_gp1 = Tscal{1} / (gamma + Tscal{1});
58 const Tscal A = Tscal{2} * inv_gp1 / r1;
59 // Toro (2009) eq. 4.7: B_K = p_K*(gamma-1)/(gamma+1) -- pressure-dimensioned.
60 const Tscal B = p1 * (gamma - Tscal{1}) * inv_gp1;
61 return Tscal{-1} * LR * (p2 - p1) * sycl::sqrt(A / (p2 + B));
62 }
63
73 template<class Tscal>
74 inline Tscal exact_v_xc_rarefaction(Tscal LR, Tscal p2, Tscal p1, Tscal r1, Tscal gamma) {
75 const Tscal cs1 = sycl::sqrt(gamma * p1 / r1);
76 return Tscal{-1} * LR * Tscal{2} * cs1 / (gamma - Tscal{1})
77 * (sycl::pow(p2 / p1, Tscal{0.5} * (gamma - Tscal{1}) / gamma) - Tscal{1});
78 }
79
80 template<class Tscal>
81 inline Tscal exact_v_lr_ss(
82 Tscal pS, ExactState<Tscal> left, ExactState<Tscal> right, Tscal gamma) {
83 return exact_v_xc_shock(Tscal{-1}, pS, left.p, left.r, gamma)
84 - exact_v_xc_shock(Tscal{1}, pS, right.p, right.r, gamma);
85 }
86
87 template<class Tscal>
88 inline Tscal exact_v_lr_rs(
89 Tscal pS, ExactState<Tscal> left, ExactState<Tscal> right, Tscal gamma) {
90 return exact_v_xc_rarefaction(Tscal{-1}, pS, left.p, left.r, gamma)
91 - exact_v_xc_shock(Tscal{1}, pS, right.p, right.r, gamma);
92 }
93
94 template<class Tscal>
95 inline Tscal exact_v_lr_sr(
96 Tscal pS, ExactState<Tscal> left, ExactState<Tscal> right, Tscal gamma) {
97 return exact_v_xc_shock(Tscal{-1}, pS, left.p, left.r, gamma)
98 - exact_v_xc_rarefaction(Tscal{1}, pS, right.p, right.r, gamma);
99 }
100
101 template<class Tscal>
102 inline Tscal exact_v_lr_rr(
103 Tscal pS, ExactState<Tscal> left, ExactState<Tscal> right, Tscal gamma) {
104 return exact_v_xc_rarefaction(Tscal{-1}, pS, left.p, left.r, gamma)
105 - exact_v_xc_rarefaction(Tscal{1}, pS, right.p, right.r, gamma);
106 }
107
112 template<class Tscal>
114 ExactState<Tscal> left, ExactState<Tscal> right, Tscal gamma) {
115 const Tscal v_lr_0 = left.v - right.v;
116 if (left.p > right.p) {
117 if (v_lr_0 > exact_v_lr_ss(left.p, left, right, gamma)) {
118 return 11;
119 } else if (v_lr_0 > exact_v_lr_rs(right.p, left, right, gamma)) {
120 return 21;
121 } else {
122 return 22;
123 }
124 } else {
125 if (v_lr_0 > exact_v_lr_ss(right.p, left, right, gamma)) {
126 return 11;
127 } else if (v_lr_0 > exact_v_lr_sr(left.p, left, right, gamma)) {
128 return 12;
129 } else {
130 return 22;
131 }
132 }
133 }
134
143 template<class Tscal, class ResidualFn>
145 Tscal posi, Tscal nega, Tscal v_lr_0, Tscal tol, u32 max_iter, ResidualFn residual) {
146 constexpr Tscal eps = Tscal{1e-16};
147
148 Tscal half = Tscal{0};
149 Tscal bis2 = residual(posi) - v_lr_0;
150 for (u32 i = 0; i < max_iter; ++i) {
151 const Tscal bis1 = bis2;
152 half = Tscal{0.5} * (posi + nega);
153 bis2 = residual(half) - v_lr_0;
154
155 if (sycl::fmax(sycl::fabs(bis2), sycl::fabs(bis2 - bis1) / (sycl::fabs(bis1) + eps))
156 < tol
157 || bis2 == Tscal{0}) {
158 break;
159 }
160
161 if (bis2 > Tscal{0}) {
162 posi = half;
163 } else {
164 nega = half;
165 }
166 }
167 return half;
168 }
169
173 template<class Tscal>
174 inline Tscal exact_bisection_ss(
175 ExactState<Tscal> left, ExactState<Tscal> right, Tscal gamma, Tscal tol, u32 max_iter) {
176 constexpr Tscal scale_up = Tscal{1.00001};
177 constexpr Tscal scale_down = Tscal{0.99999};
178
179 const Tscal v_lr_0 = left.v - right.v;
180
181 // Search for an upper bound where v_lr_ss(posi) - v_lr_0 > 0. v_lr_ss(p) is
182 // monotonically increasing and unbounded in p, so this always succeeds
183 // within a handful of iterations for any physical input; the loop bound
184 // and fallback below only guard against a degenerate/non-physical state.
185 Tscal posi = (left.p + right.p) * scale_up;
186 bool bracketed = false;
187 for (u32 i = 0; i < 300; ++i) {
188 if (exact_v_lr_ss(posi, left, right, gamma) - v_lr_0 > Tscal{0}) {
189 bracketed = true;
190 break;
191 }
192 posi *= Tscal{10};
193 }
194 if (!bracketed) {
195 posi = sycl::fmax(left.p, right.p);
196 }
197 const Tscal nega = sycl::fmin(left.p, right.p) * scale_down;
198
199 return exact_bisection_generic(posi, nega, v_lr_0, tol, max_iter, [&](Tscal p) {
200 return exact_v_lr_ss(p, left, right, gamma);
201 });
202 }
203
207 template<class Tscal>
208 inline Tscal exact_bisection_rs(
209 ExactState<Tscal> left, ExactState<Tscal> right, Tscal gamma, Tscal tol, u32 max_iter) {
210 constexpr Tscal scale_up = Tscal{1.00001};
211 constexpr Tscal scale_down = Tscal{0.99999};
212
213 const Tscal v_lr_0 = left.v - right.v;
214 const Tscal posi = left.p * scale_up;
215 const Tscal nega = right.p * scale_down;
216
217 return exact_bisection_generic(posi, nega, v_lr_0, tol, max_iter, [&](Tscal p) {
218 return exact_v_lr_rs(p, left, right, gamma);
219 });
220 }
221
225 template<class Tscal>
226 inline Tscal exact_bisection_sr(
227 ExactState<Tscal> left, ExactState<Tscal> right, Tscal gamma, Tscal tol, u32 max_iter) {
228 constexpr Tscal scale_up = Tscal{1.00001};
229 constexpr Tscal scale_down = Tscal{0.99999};
230
231 const Tscal v_lr_0 = left.v - right.v;
232 const Tscal posi = right.p * scale_up;
233 const Tscal nega = left.p * scale_down;
234
235 return exact_bisection_generic(posi, nega, v_lr_0, tol, max_iter, [&](Tscal p) {
236 return exact_v_lr_sr(p, left, right, gamma);
237 });
238 }
239
243 template<class Tscal>
244 inline Tscal exact_bisection_rr(
245 ExactState<Tscal> left, ExactState<Tscal> right, Tscal gamma, Tscal tol, u32 max_iter) {
246 constexpr Tscal scale_up = Tscal{1.00001};
247
248 const Tscal v_lr_0 = left.v - right.v;
249 const Tscal posi = sycl::fmin(left.p, right.p) * scale_up;
250 const Tscal nega = Tscal{0};
251
252 return exact_bisection_generic(posi, nega, v_lr_0, tol, max_iter, [&](Tscal p) {
253 return exact_v_lr_rr(p, left, right, gamma);
254 });
255 }
256
280 template<class Tscal>
282 Tscal u_L,
283 Tscal rho_L,
284 Tscal p_L,
285 Tscal u_R,
286 Tscal rho_R,
287 Tscal p_R,
288 Tscal gamma,
289 Tscal tol = Tscal{1.0e-8},
290 u32 max_iter = 100) {
291
292 RiemannResult<Tscal> result;
293
294 const Tscal smallp = Tscal{1.0e-25};
295 const Tscal smallrho = Tscal{1.0e-25};
296
297 if (rho_L < smallrho || rho_R < smallrho || p_L < smallp || p_R < smallp) {
298 result.p_star = sycl::fmax(smallp, Tscal{0.5} * (p_L + p_R));
299 result.v_star = Tscal{0.5} * (u_L + u_R);
300 return result;
301 }
302
303 ExactState<Tscal> left{u_L, p_L, rho_L};
304 ExactState<Tscal> right{u_R, p_R, rho_R};
305
306 // Note: unlike the reference implementation this is ported from, we do
307 // NOT special-case left.p == right.p here. That shortcut assumes a
308 // uniform (non-evolving) state whenever pressures match, which is wrong
309 // when velocities differ (e.g. the classic "123 problem" double
310 // rarefaction has p_L == p_R with strongly diverging velocities). None
311 // of the shock/rarefaction relations below divide by (p2 - p1), so
312 // there is no numerical singularity to guard against at p_L == p_R.
313 const i32 wave_pattern = exact_judge_wave_pattern(left, right, gamma);
314
315 Tscal p_star;
316 if (wave_pattern == 11) {
317 p_star = exact_bisection_ss(left, right, gamma, tol, max_iter);
318 } else if (wave_pattern == 21) {
319 p_star = exact_bisection_rs(left, right, gamma, tol, max_iter);
320 } else if (wave_pattern == 12) {
321 p_star = exact_bisection_sr(left, right, gamma, tol, max_iter);
322 } else {
323 p_star = exact_bisection_rr(left, right, gamma, tol, max_iter);
324 }
325 p_star = sycl::fmax(p_star, smallp);
326
327 const Tscal ave_v = Tscal{0.5} * (left.v + right.v);
328 Tscal v_star;
329 if (wave_pattern == 11) {
330 v_star = ave_v
331 - Tscal{0.5}
332 * (exact_v_xc_shock(Tscal{-1}, p_star, left.p, left.r, gamma)
333 + exact_v_xc_shock(Tscal{1}, p_star, right.p, right.r, gamma));
334 } else if (wave_pattern == 21) {
335 v_star = ave_v
336 - Tscal{0.5}
337 * (exact_v_xc_rarefaction(Tscal{-1}, p_star, left.p, left.r, gamma)
338 + exact_v_xc_shock(Tscal{1}, p_star, right.p, right.r, gamma));
339 } else if (wave_pattern == 12) {
340 v_star = ave_v
341 - Tscal{0.5}
342 * (exact_v_xc_shock(Tscal{-1}, p_star, left.p, left.r, gamma)
343 + exact_v_xc_rarefaction(Tscal{1}, p_star, right.p, right.r, gamma));
344 } else {
345 v_star = ave_v
346 - Tscal{0.5}
347 * (exact_v_xc_rarefaction(Tscal{-1}, p_star, left.p, left.r, gamma)
348 + exact_v_xc_rarefaction(Tscal{1}, p_star, right.p, right.r, gamma));
349 }
350
351 result.p_star = p_star;
352 result.v_star = v_star;
353 return result;
354 }
355
356} // namespace shammodels::gsph::riemann
std::uint32_t u32
32 bit unsigned integer
std::int32_t i32
32 bit integer
Tscal exact_bisection_rs(ExactState< Tscal > left, ExactState< Tscal > right, Tscal gamma, Tscal tol, u32 max_iter)
Bisection solve for the rarefaction/shock (21) wave pattern.
Definition exact.hpp:208
i32 exact_judge_wave_pattern(ExactState< Tscal > left, ExactState< Tscal > right, Tscal gamma)
Wave pattern codes: 11=shock/shock, 21=rarefaction/shock, 12=shock/rarefaction, 22=rarefaction/rarefa...
Definition exact.hpp:113
Tscal exact_v_xc_shock(Tscal LR, Tscal p2, Tscal p1, Tscal r1, Tscal gamma)
Relative velocity jump across a shock wave (Rankine-Hugoniot).
Definition exact.hpp:56
Tscal exact_bisection_generic(Tscal posi, Tscal nega, Tscal v_lr_0, Tscal tol, u32 max_iter, ResidualFn residual)
Shared bisection loop for a single wave-pattern's residual function.
Definition exact.hpp:144
Tscal exact_bisection_rr(ExactState< Tscal > left, ExactState< Tscal > right, Tscal gamma, Tscal tol, u32 max_iter)
Bisection solve for the rarefaction/rarefaction (22) wave pattern.
Definition exact.hpp:244
Tscal exact_bisection_ss(ExactState< Tscal > left, ExactState< Tscal > right, Tscal gamma, Tscal tol, u32 max_iter)
Bisection solve for the shock/shock (11) wave pattern.
Definition exact.hpp:174
Tscal exact_bisection_sr(ExactState< Tscal > left, ExactState< Tscal > right, Tscal gamma, Tscal tol, u32 max_iter)
Bisection solve for the shock/rarefaction (12) wave pattern.
Definition exact.hpp:226
Tscal exact_v_xc_rarefaction(Tscal LR, Tscal p2, Tscal p1, Tscal r1, Tscal gamma)
Relative velocity jump across a rarefaction wave (isentropic relation).
Definition exact.hpp:74
RiemannResult< Tscal > exact_solver(Tscal u_L, Tscal rho_L, Tscal p_L, Tscal u_R, Tscal rho_R, Tscal p_R, Tscal gamma, Tscal tol=Tscal{1.0e-8}, u32 max_iter=100)
Exact Riemann solver for the 1D Euler equations (ideal gas).
Definition exact.hpp:281
Iterative Riemann solver for GSPH (van Leer 1997).
Left/right primitive state for the exact solver (velocity along pair axis).
Definition exact.hpp:40
Tscal v
Velocity along the pair axis.
Definition exact.hpp:41