Peano
Loading...
Searching...
No Matches
riemann.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2
3"""!
4This is a standalone Riemann solver, based on the exact Riemann solver that is
5part of Shadowfax (https://github.com/AstroUGent/shadowfax), CMacIonize
6(https://github.com/bwvdnbro/CMacIonize) and SWIFT.
7
8It is based on the description given in Toro, E., Riemann Solvers and
9Numerical Methods for Fluid Dynamics, 3rd edition (Springer, 2009).
10
11This file should not be run directly (it just runs some unit tests if this is
12done). Instead, include it in another script as follows:
13import riemannsolver
14(make sure the folder containing the file is part of the system PATH).
15
16Once included, you can create a RiemannSolver object for an ideal gas with a
17given polytropic index (e.g. 5/3):
18solver = riemannsolver.RiemannSolver(5./3.)
19This object has a member function solve which can be used to solve the Riemann
20problem with a given left and right state:
21rhosol, usol, psol, flag = solver.solver(rhoL, uL, pL, rhoR, uR, pR)
22rhosol, rhoL, and rhoR are the densities of respectively the solution, the
23left state, and the right state.
24usol, uL, and uR are the fluid velocities
25psol, pL, and pR are the pressures
26flag is an extra return variable that is set to -1, 1, or 0 if respectively
27the left state, the right state, or a vacuum state was sampled.
28
29By default, the Riemann solution is sampled for a reference velocity dxdt = 0.
30Alternatively, you can sample the solution for another reference velocity
31dxdt = x / t by providing dxdt as an extra argument to solve().
32"""
33
34import numpy as np
35
36
38 """!
39 @brief Exact Riemann solver.
40 """
41
42 def __init__(self, gamma):
43 r"""!
44 @brief Constructor.
45
46 @param gamma Adiabatic index \f$\gamma{}\f$.
47 """
48
49 if gamma <= 1.0:
50 print("The adiabatic index needs to be larger than 1!")
51 exit()
52
53 self._gamma = gamma
54
55
57 self._gp1d2g = 0.5 * (gamma + 1.0) / gamma
58 # gamma minus 1 divided by 2 gamma
59 self._gm1d2g = 0.5 * (gamma - 1.0) / gamma
60 # gamma minus 1 divided by gamma plus 1
61 self._gm1dgp1 = (gamma - 1.0) / (gamma + 1.0)
62 # two divided by gamma plus 1
63 self._tdgp1 = 2.0 / (gamma + 1.0)
64 # two divided by gamma minus 1
65 self._tdgm1 = 2.0 / (gamma - 1.0)
66 # gamma minus 1 divided by 2
67 self._gm1d2 = 0.5 * (gamma - 1.0)
68 # two times gamma divided by gamma minus 1
69 self._tgdgm1 = 2.0 * gamma / (gamma - 1.0)
70 # gamma inverse
71 self._ginv = 1.0 / gamma
72
73 def get_soundspeed(self, rho, P):
74 """!
75 @brief Get the soundspeed corresponding to the given density and pressure.
76
77 @param rho Density value.
78 @param P Pressure value.
79 @return Soundspeed.
80 """
81 if rho > 0.0:
82 return np.sqrt(self._gamma * P / rho)
83 else:
84 return 0.0
85
86 def fb(self, rho, P, a, Pstar):
87 """!
88 @brief Riemann fL or fR function.
89
90 @param rho Density of the left or right state.
91 @param P Pressure of the left or right state.
92 @param a Soundspeed of the left or right state.
93 @param Pstar (Temporary) pressure of the middle state.
94 @return Value of the fL or fR function.
95 """
96 if Pstar > P:
97 A = self._tdgp1 / rho
98 B = self._gm1dgp1 * P
99 fval = (Pstar - P) * np.sqrt(A / (Pstar + B))
100 else:
101 fval = self._tdgm1 * a * ((Pstar / P) ** (self._gm1d2g) - 1.0)
102
103 return fval
104
105 def f(self, rhoL, uL, PL, aL, rhoR, uR, PR, aR, Pstar):
106 """!
107 @brief Riemann f function.
108
109 @param rhoL Density of the left state.
110 @param uL Velocity of the left state.
111 @param PL Pressure of the left state.
112 @param aL Soundspeed of the left state.
113 @param rhoR Density of the right state.
114 @param uR Velocity of the right state.
115 @param PR Pressure of the right state.
116 @param aR Soundspeed of the right state.
117 @param Pstar (Temporary) pressure of the middle state.
118 @return Value of the Riemann f function.
119 """
120 return self.fb(rhoL, PL, aL, Pstar) + self.fb(rhoR, PR, aR, Pstar) + (uR - uL)
121
122 def fprimeb(self, rho, P, a, Pstar):
123 """!
124 @brief Derivative of the Riemann fL or fR function.
125
126 @param rho Density of the left or right state.
127 @param P Pressure of the left or right state.
128 @param a Soundspeed of the left or right state.
129 @param Pstar (Temporary) pressure of the middle state.
130 @return Value of the derivative of the Riemann fL or fR function.
131 """
132 if Pstar > P:
133 A = self._tdgp1 / rho
134 B = self._gm1dgp1 * P
135 fval = (1.0 - 0.5 * (Pstar - P) / (B + Pstar)) * np.sqrt(A / (Pstar + B))
136 else:
137 fval = 1.0 / (rho * a) * (Pstar / P) ** (-self._gp1d2g)
138
139 return fval
140
141 def fprime(self, rhoL, PL, aL, rhoR, PR, aR, Pstar):
142 """!
143 @brief Derivative of the Riemann f function.
144
145 @param rhoL Density of the left state.
146 @param PL Pressure of the left state.
147 @param aL Soundspeed of the left state.
148 @param rhoR Density of the right state.
149 @param PR Pressure of the right state.
150 @param aR Soundspeed of the right state.
151 @param Pstar (Temporary) pressure of the middle state.
152 @return Value of the derivative of the Riemann f function.
153 """
154 return self.fprimeb(rhoL, PL, aL, Pstar) + self.fprimeb(rhoR, PR, aR, Pstar)
155
156 def gb(self, rho, P, Pstar):
157 """!
158 @brief Riemann gL or gR function.
159
160 @param rho Density of the left or right state.
161 @param P Pressure of the left or right state.
162 @param Pstar (Temporary) pressure in the middle state.
163 @return Value of the gL or gR function.
164 """
165 A = self._tdgp1 / rho
166 B = self._gm1dgp1 * P
167 return np.sqrt(A / (Pstar + B))
168
169 def guess_P(self, rhoL, uL, PL, aL, rhoR, uR, PR, aR):
170 """!
171 @brief Get an initial guess for the pressure in the middle state.
172
173 @param rhoL Left state density.
174 @param uL Left state velocity.
175 @param PL Left state pressure.
176 @param aL Left state soundspeed.
177 @param rhoR Right state density.
178 @param uR Right state velocity.
179 @param PR Right state pressure.
180 @param aR Right state soundspeed.
181 @return Initial guess for the pressure in the middle state.
182 """
183 Pmin = min(PL, PR)
184 Pmax = max(PL, PR)
185 qmax = Pmax / Pmin
186 Ppv = 0.5 * (PL + PR) - 0.125 * (uR - uL) * (PL + PR) * (aL + aR)
187 Ppv = max(5.0e-9 * (PL + PR), Ppv)
188 if qmax <= 2 and Pmin <= Ppv and Ppv <= Pmax:
189 Pguess = Ppv
190 else:
191 if Ppv < Pmin:
192 # two rarefactions
193 Pguess = (
194 (aL + aR - self._gm1d2 * (uR - uL))
195 / (aL / (PL**self._gm1d2g) + aR / (PR**self._gm1d2g))
196 ) ** self._tgdgm1
197 else:
198 # two shocks
199 gL = self.gb(rhoL, PL, Ppv)
200 gR = self.gb(rhoR, PR, Ppv)
201 Pguess = (gL * PL + gR * PR - uR + uL) / (gL + gR)
202
203 # Toro: "Not that approximate solutions may predict, incorrectly, a
204 # negative value for pressure (...). Thus in order to avoid negative guess
205 # values we introduce the small positive constant _tolerance"
206 # (tolerance is 1.e-8 in this case)
207 Pguess = max(5.0e-9 * (PL + PR), Pguess)
208 return Pguess
209
210 def solve_brent(self, rhoL, uL, PL, aL, rhoR, uR, PR, aR, Plow, Phigh):
211 """!
212 @brief Find the pressure of the middle state by using Brent's method.
213
214 @param rhoL Density of the left state.
215 @param uL Velocity of the left state.
216 @param PL Pressure of the left state.
217 @param aL Soundspeed of the left state.
218 @param rhoR Density of the right state.
219 @param uR Velocity of the right state.
220 @param PR Pressure of the right state.
221 @param aR Soundspeed of the right state.
222 @param Plow Lower bound guess for the pressure of the middle state.
223 @param Phigh Higher bound guess for the pressure of the middle state.
224 @return Pressure of the middle state, with a 1.e-8 relative error precision.
225 """
226 a = Plow
227 b = Phigh
228 c = 0.0
229 d = 1.0e230
230
231 fa = self.f(rhoL, uL, PL, aL, rhoR, uR, PR, aR, a)
232 fb = self.f(rhoL, uL, PL, aL, rhoR, uR, PR, aR, b)
233 fc = 0.0
234
235 s = 0.0
236 fs = 0.0
237
238 if fa * fb > 0.0:
239 print("Equal sign function values provided to solve_brent!")
240 exit()
241
242 # if |f(a)| < |f(b)| then swap (a,b) end if
243 if abs(fa) < abs(fb):
244 tmp = a
245 a = b
246 b = tmp
247 tmp = fa
248 fa = fb
249 fb = tmp
250
251 c = a
252 fc = fa
253 mflag = True
254
255 while (not fb == 0.0) and (abs(a - b) > 5.0e-9 * (a + b)):
256 if (not fa == fc) and (not fb == fc):
257 # Inverse quadratic interpolation
258 s = (
259 a * fb * fc / (fa - fb) / (fa - fc)
260 + b * fa * fc / (fb - fa) / (fb - fc)
261 + c * fa * fb / (fc - fa) / (fc - fb)
262 )
263 else:
264 # Secant Rule
265 s = b - fb * (b - a) / (fb - fa)
266
267 tmp2 = 0.25 * (3.0 * a + b)
268 if (
269 (not (((s > tmp2) and (s < b)) or ((s < tmp2) and (s > b))))
270 or (mflag and (abs(s - b) >= 0.5 * abs(b - c)))
271 or ((not mflag) and (abs(s - b) >= 0.5 * abs(c - d)))
272 or (mflag and (abs(b - c) < 5.0e-9 * (b + c)))
273 or ((not mflag) and (abs(c - d) < 5.0e-9 * (c + d)))
274 ):
275 s = 0.5 * (a + b)
276 mflag = True
277 else:
278 mflag = False
279 fs = self.f(rhoL, uL, PL, aL, rhoR, uR, PR, aR, s)
280 d = c
281 c = b
282 fc = fb
283 if fa * fs < 0.0:
284 b = s
285 fb = fs
286 else:
287 a = s
288 fa = fs
289
290 # if |f(a)| < |f(b)| then swap (a,b) end if
291 if abs(fa) < abs(fb):
292 tmp = a
293 a = b
294 b = tmp
295 tmp = fa
296 fa = fb
297 fb = tmp
298
299 return b
300
302 self, rhoR, uR, PR, aR, ustar, Pstar, dxdt=np.array([0.0])
303 ):
304 """!
305 @brief Sample the Riemann problem solution for a position in the right
306 shock wave regime.
307
308 @param rhoR Density of the right state.
309 @param uR Velocity of the right state.
310 @param PR Pressure of the right state.
311 @param aR Soundspeed of the right state.
312 @param ustar Velocity of the middle state.
313 @param Pstar Pressure of the middle state.
314 @param rhosol Density solution.
315 @param usol Velocity solution.
316 @param Psol Pressure solution.
317 @param dxdt Point in velocity space where we want to sample the solution.
318 @return Density, velocity and pressure solution.
319 """
320 rhosol = np.zeros(dxdt.shape)
321 usol = np.zeros(dxdt.shape)
322 Psol = np.zeros(dxdt.shape)
323
324 # variable used twice below
325 PdPR = Pstar / PR
326 # get the shock speed
327 SR = uR + aR * np.sqrt(self._gp1d2g * PdPR + self._gm1d2g)
328 middle_state = SR > dxdt
329 right_state = ~middle_state
330
331 rhosol[middle_state] = (
332 rhoR * (PdPR + self._gm1dgp1) / (self._gm1dgp1 * PdPR + 1.0)
333 )
334 usol[middle_state] = ustar
335 Psol[middle_state] = Pstar
336
337 rhosol[right_state] = rhoR
338 usol[right_state] = uR
339 Psol[right_state] = PR
340
341 return rhosol, usol, Psol
342
344 self, rhoR, uR, PR, aR, ustar, Pstar, dxdt=np.array([0.0])
345 ):
346 """!
347 @brief Sample the Riemann problem solution for a position in the right
348 rarefaction wave regime.
349
350 @param rhoR Density of the right state.
351 @param uR Velocity of the right state.
352 @param PR Pressure of the right state.
353 @param aR Soundspeed of the right state.
354 @param ustar Velocity of the middle state.
355 @param Pstar Pressure of the middle state.
356 @param dxdt Point in velocity space where we want to sample the solution.
357 @return Density, velocity and pressure solution.
358 """
359 rhosol = np.zeros(dxdt.shape)
360 usol = np.zeros(dxdt.shape)
361 Psol = np.zeros(dxdt.shape)
362
363 # get the velocity of the head of the rarefaction wave
364 SHR = uR + aR
365 rarefaction_regime = SHR > dxdt
366 right_state = ~rarefaction_regime
367
369 PdPR = Pstar / PR
370 # get the velocity of the tail of the rarefaction wave
371 STR = ustar + aR * PdPR**self._gm1d2g
372 middle_state = rarefaction_regime & (STR > dxdt)
373 rarefaction_fan = rarefaction_regime & (~middle_state)
374
375 rhosol[middle_state] = rhoR * PdPR**self._ginv
376 usol[middle_state] = ustar
377 Psol[middle_state] = Pstar
378
380 base = self._tdgp1 - self._gm1dgp1 * (uR - dxdt[rarefaction_fan]) / aR
381 rhosol[rarefaction_fan] = rhoR * base**self._tdgm1
382 usol[rarefaction_fan] = self._tdgp1 * (
383 -aR + self._gm1d2 * uR + dxdt[rarefaction_fan]
384 )
385 Psol[rarefaction_fan] = PR * base**self._tgdgm1
386
387 rhosol[right_state] = rhoR
388 usol[right_state] = uR
389 Psol[right_state] = PR
390
391 return rhosol, usol, Psol
392
393 def sample_right_state(self, rhoR, uR, PR, aR, ustar, Pstar, dxdt=np.array([0.0])):
394 """!
395 @brief Sample the Riemann problem solution in the right state regime.
396
397 @param rhoR Density of the right state.
398 @param uR Velocity of the right state.
399 @param PR Pressure of the right state.
400 @param aR Soundspeed of the right state.
401 @param ustar Velocity of the middle state.
402 @param Pstar Pressure of the middle state.
403 @param dxdt Point in velocity space where we want to sample the solution.
404 @return Density, velocity and pressure solution.
405 """
406 if Pstar > PR:
407
408 rhosol, usol, Psol = self.sample_right_shock_wave(
409 rhoR, uR, PR, aR, ustar, Pstar, dxdt
410 )
411 else:
412
413 rhosol, usol, Psol = self.sample_right_rarefaction_wave(
414 rhoR, uR, PR, aR, ustar, Pstar, dxdt
415 )
416
417 return rhosol, usol, Psol
418
420 self, rhoL, uL, PL, aL, ustar, Pstar, dxdt=np.array([0.0])
421 ):
422 """!
423 @brief Sample the Riemann problem solution for a position in the left shock
424 wave regime.
425
426 @param rhoL Density of the left state.
427 @param uL Velocity of the left state.
428 @param PL Pressure of the left state.
429 @param aL Soundspeed of the left state.
430 @param ustar Velocity of the middle state.
431 @param Pstar Pressure of the middle state.
432 @param dxdt Point in velocity space where we want to sample the solution.
433 @return Density, velocity and pressure solution.
434 """
435 rhosol = np.zeros(dxdt.shape)
436 usol = np.zeros(dxdt.shape)
437 Psol = np.zeros(dxdt.shape)
438
439 # variable used twice below
440 PdPL = Pstar / PL
441 # get the shock speed
442 SL = uL - aL * np.sqrt(self._gp1d2g * PdPL + self._gm1d2g)
443 middle_state = SL < dxdt
444 left_state = ~middle_state
445 if np.any(middle_state):
446
447 rhosol[middle_state] = (
448 rhoL * (PdPL + self._gm1dgp1) / (self._gm1dgp1 * PdPL + 1.0)
449 )
450 usol[middle_state] = ustar
451 Psol[middle_state] = Pstar
452 if np.any(left_state):
453
454 rhosol[left_state] = rhoL
455 usol[left_state] = uL
456 Psol[left_state] = PL
457
458 return rhosol, usol, Psol
459
461 self, rhoL, uL, PL, aL, ustar, Pstar, dxdt=np.array([0.0])
462 ):
463 """!
464 @brief Sample the Riemann problem solution for a position in the left
465 rarefaction wave regime.
466
467 @param rhoL Density of the left state.
468 @param uL Velocity of the left state.
469 @param PL Pressure of the left state.
470 @param aL Soundspeed of the left state.
471 @param ustar Velocity of the middle state.
472 @param Pstar Pressure of the middle state.
473 @param dxdt Point in velocity space where we want to sample the solution.
474 @return Density, velocity and pressure solution.
475 """
476 rhosol = np.zeros(dxdt.shape)
477 usol = np.zeros(dxdt.shape)
478 Psol = np.zeros(dxdt.shape)
479
480 # get the velocity of the head of the rarefaction wave
481 SHL = uL - aL
482 rarefaction_wave = SHL < dxdt
483 left_state = ~rarefaction_wave
484 if np.any(rarefaction_wave):
485
487 PdPL = Pstar / PL
488 # get the velocity of the tail of the rarefaction wave
489 STL = ustar - aL * PdPL**self._gm1d2g
490 rarefaction_fan = rarefaction_wave & (STL > dxdt)
491 middle_state = rarefaction_wave & (~rarefaction_fan)
492 if np.any(rarefaction_fan):
493
495 base = self._tdgp1 + self._gm1dgp1 * (uL - dxdt[rarefaction_fan]) / aL
496 rhosol[rarefaction_fan] = rhoL * base**self._tdgm1
497 usol[rarefaction_fan] = self._tdgp1 * (
498 aL + self._gm1d2 * uL + dxdt[rarefaction_fan]
499 )
500 Psol[rarefaction_fan] = PL * base**self._tgdgm1
501 if np.any(middle_state):
502
503 rhosol[middle_state] = rhoL * PdPL**self._ginv
504 usol[middle_state] = ustar
505 Psol[middle_state] = Pstar
506 if np.any(left_state):
507
508 rhosol[left_state] = rhoL
509 usol[left_state] = uL
510 Psol[left_state] = PL
511
512 return rhosol, usol, Psol
513
514 def sample_left_state(self, rhoL, uL, PL, aL, ustar, Pstar, dxdt=np.array([0.0])):
515 """!
516 @brief Sample the Riemann problem solution in the left state regime.
517
518 @param rhoL Density of the left state.
519 @param uL Velocity of the left state.
520 @param PL Pressure of the left state.
521 @param aL Soundspeed of the left state.
522 @param ustar Velocity of the middle state.
523 @param Pstar Pressure of the middle state.
524 @param dxdt Point in velocity space where we want to sample the solution.
525 @return Density, velocity and pressure solution.
526 """
527 if Pstar > PL:
528
529 rhosol, usol, Psol = self.sample_left_shock_wave(
530 rhoL, uL, PL, aL, ustar, Pstar, dxdt
531 )
532 else:
533
534 rhosol, usol, Psol = self.sample_left_rarefaction_wave(
535 rhoL, uL, PL, aL, ustar, Pstar, dxdt
536 )
537
538 return rhosol, usol, Psol
539
540 def sample_right_vacuum(self, rhoL, uL, PL, aL, dxdt=np.array([0.0])):
541 """!
542 @brief Sample the vacuum Riemann problem if the right state is a vacuum.
543
544 @param rhoL Density of the left state.
545 @param uL Velocity of the left state.
546 @param PL Pressure of the left state.
547 @param aL Soundspeed of the left state.
548 @param dxdt Point in velocity space where we want to sample the solution.
549 @return Density, velocity and pressure solution, and a flag indicating
550 wether the left state (-1), the right state (1), or a vacuum state (0) was
551 sampled.
552 """
553 rhosol = np.zeros(dxdt.shape)
554 usol = np.zeros(dxdt.shape)
555 Psol = np.zeros(dxdt.shape)
556 flagsol = np.zeros(dxdt.shape, dtype=np.int32)
557
558 vacuum_regime = uL - aL < dxdt
559 left_state = ~vacuum_regime
560 if np.any(vacuum_regime):
561
563 SL = uL + self._tdgm1 * aL
564 rarefaction_wave = vacuum_regime & (SL > dxdt)
565 if np.any(rarefaction_wave):
566
568 base = self._tdgp1 + self._gm1dgp1 * (uL - dxdt[rarefaction_wave]) / aL
569 rhosol[rarefaction_wave] = rhoL * base**self._tdgm1
570 usol[rarefaction_wave] = self._tdgp1 * (
571 aL + self._gm1d2 * uL + dxdt[rarefaction_wave]
572 )
573 Psol[rarefaction_wave] = PL * base**self._tgdgm1
574 flagsol[rarefaction_wave] = -1
575
577 if np.any(left_state):
578
579 rhosol[left_state] = rhoL
580 usol[left_state] = uL
581 Psol[left_state] = PL
582 flagsol[left_state] = -1
583
584 return rhosol, usol, Psol, flagsol
585
586 def sample_left_vacuum(self, rhoR, uR, PR, aR, dxdt=np.array([0.0])):
587 """!
588 @brief Sample the vacuum Riemann problem if the left state is a vacuum.
589
590 @param rhoR Density of the right state.
591 @param uR Velocity of the right state.
592 @param PR Pressure of the right state.
593 @param aR Soundspeed of the right state.
594 @param dxdt Point in velocity space where we want to sample the solution.
595 @return Density, velocity and pressure solution, and a flag indicating
596 wether the left state (-1), the right state (1), or a vacuum state (0) was
597 sampled.
598 """
599 rhosol = np.zeros(dxdt.shape)
600 usol = np.zeros(dxdt.shape)
601 Psol = np.zeros(dxdt.shape)
602 flagsol = np.zeros(dxdt.shape, dtype=np.int32)
603
604 vacuum_regime = dxdt < uR + aR
605 right_state = ~vacuum_regime
606 if np.any(vacuum_regime):
607
609 SR = uR - self._tdgm1 * aR
610 rarefaction_wave = vacuum_regime & (SR < dxdt)
611 if np.any(rarefaction_wave):
612
614 base = self._tdgp1 - self._gm1dgp1 * (uR - dxdt[rarefaction_wave]) / aR
615 rhosol[rarefaction_wave] = rhoR * base**self._tdgm1
616 usol[rarefaction_wave] = self._tdgp1 * (
617 -aR + self._tdgm1 * uR + dxdt[rarefaction_wave]
618 )
619 Psol[rarefaction_wave] = PR * base**self._tgdgm1
620 flagsol[rarefaction_wave] = 1
621
624 if np.any(right_state):
625 rhosol[right_state] = rhoR
626 usol[right_state] = uR
627 Psol[right_state] = PR
628 flagsol[right_state] = 1
629
630 return rhosol, usol, Psol, flagsol
631
632 def sample_vacuum_generation(self, rhoL, uL, PL, aL, rhoR, uR, PR, aR, dxdt):
633 """!
634 @brief Sample the vacuum Riemann problem in the case vacuum is generated in
635 between the left and right state.
636
637 @param rhoL Density of the left state.
638 @param uL Velocity of the left state.
639 @param PL Pressure of the left state.
640 @param aL Soundspeed of the left state.
641 @param rhoR Density of the right state.
642 @param uR Velocity of the right state.
643 @param PR Pressure of the right state.
644 @param aR Soundspeed of the right state.
645 @param dxdt Point in velocity space where we want to sample the solution.
646 @return Density, velocity and pressure solution, and a flag indicating
647 wether the left state (-1), the right state (1), or a vacuum state (0) was
648 sampled.
649 """
650 rhosol = np.zeros(dxdt.shape)
651 usol = np.zeros(dxdt.shape)
652 Psol = np.zeros(dxdt.shape)
653 flagsol = np.zeros(dxdt.shape, dtype=np.int32)
654
655 # get the speeds of the left and right rarefaction waves
656 SR = uR - self._tdgm1 * aR
657 SL = uL + self._tdgm1 * aL
658 vacuum = (SR > dxdt) & (SL < dxdt)
659 rarefaction_regime = ~vacuum
660
662 if np.any(rarefaction_regime):
663
664 right_rarefaction_regime = rarefaction_regime & (SL < dxdt)
665 left_rarefaction_regime = rarefaction_regime & (~right_rarefaction_regime)
666 if np.any(right_rarefaction_regime):
667
668 right_rarefaction = right_rarefaction_regime & (dxdt < uR + aR)
669 right_state = right_rarefaction_regime & (~right_rarefaction)
670 if np.any(right_rarefaction):
671
673 base = (
674 self._tdgp1
675 - self._gm1dgp1 * (uR - dxdt[right_rarefaction]) / aR
676 )
677 rhosol[right_rarefaction] = rhoR * base**self._tdgm1
678 usol[right_rarefaction] = self._tdgp1 * (
679 -aR + self._tdgm1 * uR + dxdt[right_rarefaction]
680 )
681 Psol[right_rarefaction] = PR * base**self._tgdgm1
682 if np.any(right_state):
683
684 rhosol[right_state] = rhoR
685 usol[right_state] = uR
686 Psol[right_state] = PR
687 flagsol[right_rarefaction_regime] = 1
688 if np.any(left_rarefaction_regime):
689
690 left_rarefaction = left_rarefaction_regime & (dxdt > uL - aL)
691 left_state = left_rarefaction_regime & (~left_rarefaction)
692 if np.any(left_rarefaction):
693
695 base = (
696 self._tdgp1 + self._gm1dgp1 * (uL - dxdt[left_rarefaction]) / aL
697 )
698 rhosol[left_rarefaction] = rhoL * base**self._tdgm1
699 usol[left_rarefaction] = self._tdgp1 * (
700 aL + self._tdgm1 * uL + dxdt[left_rarefaction]
701 )
702 Psol[left_rarefaction] = PL * base**self._tgdgm1
703 if np.any(left_state):
704
705 rhosol[left_state] = rhoL
706 usol[left_state] = uL
707 Psol[left_state] = PL
708 flagsol[left_rarefaction_regime] = -1
709
710 return rhosol, usol, Psol, flagsol
711
712 def solve_vacuum(self, rhoL, uL, PL, aL, rhoR, uR, PR, aR, dxdt=np.array([0.0])):
713 """!
714 @brief Vacuum Riemann solver.
715
716 This solver is called when one or both states have a zero density, or when
717 the vacuum generation condition is satisfied (meaning vacuum is generated
718 in the middle state, although strictly speaking there is no "middle"
719 state if vacuum is involved).
720
721 @param rhoL Density of the left state.
722 @param uL Velocity of the left state.
723 @param PL Pressure of the left state.
724 @param aL Soundspeed of the left state.
725 @param rhoR Density of the right state.
726 @param uR Velocity of the right state.
727 @param PR Pressure of the right state.
728 @param aR Soundspeed of the right state.
729 @param dxdt Point in velocity space where we want to sample the solution.
730 @return Density, velocity and pressure solution, and a flag indicating
731 wether the left state (-1), the right state (1), or a vacuum state (0) was
732 sampled.
733 """
734 # if both states are vacuum, the solution is also vacuum
735 if rhoL == 0.0 and rhoR == 0.0:
736 rhosol = np.zeros(dxdt.shape)
737 usol = np.zeros(dxdt.shape)
738 Psol = np.zeros(dxdt.shape)
739 flagsol = np.zeros(dxdt.shape, dtype=np.int32)
740 return rhosol, usol, Psol, flagsol
741
742 if rhoR == 0.0:
743
744 return self.sample_right_vacuum(rhoL, uL, PL, aL, dxdt)
745 else:
746 if rhoL == 0.0:
747
748 return self.sample_left_vacuum(rhoR, uR, PR, aR, dxdt)
749 else:
750
751 return self.sample_vacuum_generation(
752 rhoL, uL, PL, aL, rhoR, uR, PR, aR, dxdt
753 )
754
755 def solve(self, rhoL, uL, PL, rhoR, uR, PR, dxdt=np.array([0.0])):
756 """!
757 @brief Solve the Riemann problem with the given left and right state.
758
759 @param rhoL Left state density.
760 @param uL Left state velocity.
761 @param PL Left state pressure.
762 @param rhoR Right state density.
763 @param uR Right state velocity.
764 @param PR Right state pressure.
765 @param dxdt Point in velocity space where we want to sample the solution.
766 @return Density, velocity and pressure solution, and a flag indicating
767 wether the left state (-1), the right state (1), or a vacuum state (0) was
768 sampled.
769 """
770
771 # get the soundspeeds
772 aL = self.get_soundspeed(rhoL, PL)
773 aR = self.get_soundspeed(rhoR, PR)
774
775 # handle vacuum
776 if rhoL == 0.0 or rhoR == 0.0:
777 return self.solve_vacuum(rhoL, uL, PL, aL, rhoR, uR, PR, aR, dxdt)
778
779 # handle vacuum generation
780 if self._tdgm1 * (aL + aR) <= uR - uL:
781 return self.solve_vacuum(rhoL, uL, PL, aL, rhoR, uR, PR, aR, dxdt)
782
783 # find the pressure and velocity in the middle state
784 # since this is an exact Riemann solver, this is an iterative process,
785 # whereby we basically find the root of a function (the Riemann f function
786 # defined above)
787 # we start by using a Newton-Raphson method, since we do not have an
788 # interval in which the function changes sign
789 # however, as soon as we have such an interval, we switch to a much more
790 # robust root finding method (Brent's method). We do this because the
791 # Newton-Raphson method in some cases can overshoot and return a negative
792 # pressure, for which the Riemann f function is not defined. Brent's method
793 # will never stroll outside of the initial interval in which the function
794 # changes sign.
795 Pstar = 0.0
796 Pguess = self.guess_P(rhoL, uL, PL, aL, rhoR, uR, PR, aR)
797 # we only store this variable to store the sign of the function for pressure
798 # zero
799 # we need to find a larger pressure for which this sign changes to have an
800 # interval where we can use Brent's method
801 fPstar = self.f(rhoL, uL, PL, aL, rhoR, uR, PR, aR, Pstar)
802 fPguess = self.f(rhoL, uL, PL, aL, rhoR, uR, PR, aR, Pguess)
803 if fPstar * fPguess >= 0.0:
804 # Newton-Raphson until convergence or until usable interval is found to
805 # use Brent's method
806 while abs(Pstar - Pguess) > 5.0e-9 * (Pstar + Pguess) and fPguess < 0.0:
807 Pstar = Pguess
808 Pguess = Pguess - fPguess / self.fprime(
809 rhoL, PL, aL, rhoR, PR, aR, Pguess
810 )
811 fPguess = self.f(rhoL, uL, PL, aL, rhoR, uR, PR, aR, Pguess)
812
813 # As soon as there is a suitable interval: use Brent's method
814 if abs(Pstar - Pguess) > 5.0e-9 * (Pstar + Pguess) and fPguess > 0.0:
815 Pstar = self.solve_brent(rhoL, uL, PL, aL, rhoR, uR, PR, aR, Pstar, Pguess)
816 else:
817 Pstar = Pguess
818
819 # the middle state velocity is fixed once the middle state pressure is known
820 ustar = 0.5 * (uL + uR) + 0.5 * (
821 self.fb(rhoR, PR, aR, Pstar) - self.fb(rhoL, PL, aL, Pstar)
822 )
823
824 # we now have solved the Riemann problem: we have the left, middle and
825 # right state, and this completely fixes the solution
826 # we just need to sample the solution for x/t.
827 rhosol = np.zeros(dxdt.shape)
828 usol = np.zeros(dxdt.shape)
829 Psol = np.zeros(dxdt.shape)
830 flagsol = np.zeros(dxdt.shape, dtype=np.int32)
831
832 right_state = ustar < dxdt
833 left_state = ~right_state
834
835 if np.any(right_state):
836 (
837 rhosol[right_state],
838 usol[right_state],
839 Psol[right_state],
840 ) = self.sample_right_state(
841 rhoR, uR, PR, aR, ustar, Pstar, dxdt[right_state]
842 )
843 flagsol[right_state] = 1
844 if np.any(left_state):
845 (
846 rhosol[left_state],
847 usol[left_state],
848 Psol[left_state],
849 ) = self.sample_left_state(rhoL, uL, PL, aL, ustar, Pstar, dxdt[left_state])
850 flagsol[left_state] = -1
851
852 return rhosol, usol, Psol, flagsol
853
854 def solve_middle_state(self, rhoL, uL, PL, rhoR, uR, PR):
855 """!
856 @brief Solve the Riemann problem with the given left and right state for
857 the velocity and pressure (but do not sample the solution).
858
859 @param rhoL Left state density.
860 @param uL Left state velocity.
861 @param PL Left state pressure.
862 @param rhoR Right state density.
863 @param uR Right state velocity.
864 @param PR Right state pressure.
865 @return Velocity and pressure in the middle region.
866 """
867
868 # get the soundspeeds
869 aL = self.get_soundspeed(rhoL, PL)
870 aR = self.get_soundspeed(rhoR, PR)
871
872 # handle vacuum
873 if rhoL == 0.0 or rhoR == 0.0:
874 print("Vacuum not handled yet!")
875 exit()
876
877 # handle vacuum generation
878 if self._tdgm1 * (aL + aR) <= uR - uL:
879 print("Vacuum not handled yet!")
880 exit()
881
882 # find the pressure and velocity in the middle state
883 # since this is an exact Riemann solver, this is an iterative process,
884 # whereby we basically find the root of a function (the Riemann f function
885 # defined above)
886 # we start by using a Newton-Raphson method, since we do not have an
887 # interval in which the function changes sign
888 # however, as soon as we have such an interval, we switch to a much more
889 # robust root finding method (Brent's method). We do this because the
890 # Newton-Raphson method in some cases can overshoot and return a negative
891 # pressure, for which the Riemann f function is not defined. Brent's method
892 # will never stroll outside of the initial interval in which the function
893 # changes sign.
894 Pstar = 0.0
895 Pguess = self.guess_P(rhoL, uL, PL, aL, rhoR, uR, PR, aR)
896 # we only store this variable to store the sign of the function for pressure
897 # zero
898 # we need to find a larger pressure for which this sign changes to have an
899 # interval where we can use Brent's method
900 fPstar = self.f(rhoL, uL, PL, aL, rhoR, uR, PR, aR, Pstar)
901 fPguess = self.f(rhoL, uL, PL, aL, rhoR, uR, PR, aR, Pguess)
902 if fPstar * fPguess >= 0.0:
903 # Newton-Raphson until convergence or until usable interval is found to
904 # use Brent's method
905 while abs(Pstar - Pguess) > 5.0e-9 * (Pstar + Pguess) and fPguess < 0.0:
906 Pstar = Pguess
907 Pguess = Pguess - fPguess / self.fprime(
908 rhoL, PL, aL, rhoR, PR, aR, Pguess
909 )
910 fPguess = self.f(rhoL, uL, PL, aL, rhoR, uR, PR, aR, Pguess)
911
912 # As soon as there is a suitable interval: use Brent's method
913 if abs(Pstar - Pguess) > 5.0e-9 * (Pstar + Pguess) and fPguess > 0.0:
914 Pstar = self.solve_brent(rhoL, uL, PL, aL, rhoR, uR, PR, aR, Pstar, Pguess)
915 else:
916 Pstar = Pguess
917
918 # the middle state velocity is fixed once the middle state pressure is known
919 ustar = 0.5 * (uL + uR) + 0.5 * (
920 self.fb(rhoR, PR, aR, Pstar) - self.fb(rhoL, PL, aL, Pstar)
921 )
922
923 return ustar, Pstar
sample_left_rarefaction_wave(self, rhoL, uL, PL, aL, ustar, Pstar, dxdt=np.array([0.0]))
Sample the Riemann problem solution for a position in the left rarefaction wave regime.
Definition riemann.py:462
sample_right_shock_wave(self, rhoR, uR, PR, aR, ustar, Pstar, dxdt=np.array([0.0]))
Sample the Riemann problem solution for a position in the right shock wave regime.
Definition riemann.py:303
solve_vacuum(self, rhoL, uL, PL, aL, rhoR, uR, PR, aR, dxdt=np.array([0.0]))
Vacuum Riemann solver.
Definition riemann.py:712
fb(self, rho, P, a, Pstar)
Riemann fL or fR function.
Definition riemann.py:86
sample_left_vacuum(self, rhoR, uR, PR, aR, dxdt=np.array([0.0]))
Sample the vacuum Riemann problem if the left state is a vacuum.
Definition riemann.py:586
f(self, rhoL, uL, PL, aL, rhoR, uR, PR, aR, Pstar)
Riemann f function.
Definition riemann.py:105
sample_right_rarefaction_wave(self, rhoR, uR, PR, aR, ustar, Pstar, dxdt=np.array([0.0]))
Sample the Riemann problem solution for a position in the right rarefaction wave regime.
Definition riemann.py:345
__init__(self, gamma)
Constructor.
Definition riemann.py:42
solve_brent(self, rhoL, uL, PL, aL, rhoR, uR, PR, aR, Plow, Phigh)
Find the pressure of the middle state by using Brent's method.
Definition riemann.py:210
sample_right_state(self, rhoR, uR, PR, aR, ustar, Pstar, dxdt=np.array([0.0]))
Sample the Riemann problem solution in the right state regime.
Definition riemann.py:393
sample_right_vacuum(self, rhoL, uL, PL, aL, dxdt=np.array([0.0]))
Sample the vacuum Riemann problem if the right state is a vacuum.
Definition riemann.py:540
sample_left_state(self, rhoL, uL, PL, aL, ustar, Pstar, dxdt=np.array([0.0]))
Sample the Riemann problem solution in the left state regime.
Definition riemann.py:514
fprime(self, rhoL, PL, aL, rhoR, PR, aR, Pstar)
Derivative of the Riemann f function.
Definition riemann.py:141
solve(self, rhoL, uL, PL, rhoR, uR, PR, dxdt=np.array([0.0]))
Solve the Riemann problem with the given left and right state.
Definition riemann.py:755
sample_vacuum_generation(self, rhoL, uL, PL, aL, rhoR, uR, PR, aR, dxdt)
Sample the vacuum Riemann problem in the case vacuum is generated in between the left and right state...
Definition riemann.py:632
sample_left_shock_wave(self, rhoL, uL, PL, aL, ustar, Pstar, dxdt=np.array([0.0]))
Sample the Riemann problem solution for a position in the left shock wave regime.
Definition riemann.py:421
gb(self, rho, P, Pstar)
Riemann gL or gR function.
Definition riemann.py:156
solve_middle_state(self, rhoL, uL, PL, rhoR, uR, PR)
Solve the Riemann problem with the given left and right state for the velocity and pressure (but do n...
Definition riemann.py:854
get_soundspeed(self, rho, P)
Get the soundspeed corresponding to the given density and pressure.
Definition riemann.py:73
guess_P(self, rhoL, uL, PL, aL, rhoR, uR, PR, aR)
Get an initial guess for the pressure in the middle state.
Definition riemann.py:169
_gp1d2g
related quantities: gamma plus 1 divided by 2 gamma
Definition riemann.py:57
fprimeb(self, rho, P, a, Pstar)
Derivative of the Riemann fL or fR function.
Definition riemann.py:122