Skip to content

Commit 24ebe35

Browse files
committed
the exact Riemann solver
1 parent cdb82e6 commit 24ebe35

File tree

1 file changed

+162
-0
lines changed

1 file changed

+162
-0
lines changed

riemann_exact.py

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# solve the Riemann problem for a gamma-law gas
2+
3+
import numpy as np
4+
import scipy.optimize as optimize
5+
6+
class State(object):
7+
""" a simple object to hold a primitive variable state """
8+
9+
def __init__(self, p=1.0, u=0.0, rho=1.0):
10+
self.p = p
11+
self.u = u
12+
self.rho = rho
13+
14+
def __str__(self):
15+
return "rho: {}; u: {}; p: {}".format(self.rho, self.u, self.p)
16+
17+
class RiemannProblem(object):
18+
""" a class to define a Riemann problem. It takes a left
19+
and right state. Note: we assume a constant gamma """
20+
21+
def __init__(self, left_state, right_state, gamma=1.4):
22+
self.left = left_state
23+
self.right = right_state
24+
self.gamma = gamma
25+
26+
self.ustar = None
27+
self.pstar = None
28+
29+
def u_hugoniot(self, p, side):
30+
"""define the Hugoniot curve, u(p)."""
31+
32+
if side == "left":
33+
state = self.left
34+
s = 1.0
35+
elif side == "right":
36+
state = self.right
37+
s = -1.0
38+
39+
c = np.sqrt(self.gamma*state.p/state.rho)
40+
41+
if p < state.p:
42+
# rarefaction
43+
u = state.u + s*(2.0*c/(self.gamma-1.0))* \
44+
(1.0 - (p/state.p)**((self.gamma-1.0)/(2.0*self.gamma)))
45+
else:
46+
# shock
47+
beta = (self.gamma+1.0)/(self.gamma-1.0)
48+
u = state.u + s*(2.0*c/np.sqrt(2.0*self.gamma*(self.gamma-1.0)))* \
49+
(1.0 - p/state.p)/np.sqrt(1.0 + beta*p/state.p)
50+
51+
return u
52+
53+
def find_star_state(self, p_min=0.001, p_max=1000.0):
54+
""" root find the Hugoniot curve to find ustar, pstar """
55+
56+
# we need to root-find on
57+
self.pstar = optimize.brentq(
58+
lambda p: self.u_hugoniot(p, "left") - self.u_hugoniot(p, "right"),
59+
p_min, p_max)
60+
self.ustar = self.u_hugoniot(self.pstar, "left")
61+
62+
63+
def shock_solution(self, sgn, state):
64+
"""return the interface solution considering a shock"""
65+
66+
p_ratio = self.pstar/state.p
67+
c = np.sqrt(self.gamma*state.p/state.rho)
68+
69+
# Toro, eq. 4.52 / 4.59
70+
S = state.u + sgn*c*np.sqrt(0.5*(self.gamma + 1.0)/self.gamma*p_ratio +
71+
0.5*(self.gamma - 1.0)/self.gamma)
72+
73+
# are we to the left or right of the shock?
74+
if (self.ustar < 0 and S < 0) or (self.ustar > 0 and S > 0):
75+
# R/L region
76+
solution = state
77+
else:
78+
# * region -- get rhostar from Toro, eq. 4.50 / 4.57
79+
gam_fac = (self.gamma - 1.0)/(self.gamma + 1.0)
80+
rhostar = state.rho * (p_ratio + gam_fac)/(gam_fac * p_ratio + 1.0)
81+
solution = State(rho=rhostar, u=self.ustar, p=self.pstar)
82+
83+
return solution
84+
85+
def rarefaction_solution(self, sgn, state):
86+
"""return the interface solution considering a rarefaction wave"""
87+
88+
# find the speed of the head and tail of the rarefaction fan
89+
90+
# isentropic (Toro eq. 4.54 / 4.61)
91+
p_ratio = self.pstar/state.p
92+
c = np.sqrt(self.gamma*state.p/state.rho)
93+
cstar = c*p_ratio**((self.gamma-1.0)/(2*self.gamma))
94+
95+
lambda_head = state.u + sgn*c
96+
lambda_tail = self.ustar + sgn*cstar
97+
98+
if (sgn > 0 and lambda_head < 0) or (sgn < 0 and lambda_head > 0):
99+
# R/L region
100+
solution = state
101+
102+
elif (sgn > 0 and lambda_tail > 0) or (sgn < 0 and lambda_tail < 0):
103+
# * region, we use the isentropic density (Toro 4.53 / 4.60)
104+
solution = State(rho = state.rho*p_ratio**(1.0/self.gamma),
105+
u = self.ustar, p = self.pstar)
106+
107+
else:
108+
# we are in the fan -- Toro 4.56 / 4.63
109+
rho = state.rho * (2/(self.gamma + 1.0) -
110+
sgn*gam_fac*state.u/c)**(2.0/(self.gamma-1.0))
111+
u = 2.0/(self.gamma + 1.0) * ( -sgn*c + 0.5*(self.gamma - 1.0)*state.u)
112+
p = state.p * (2/(self.gamma + 1.0) -
113+
sgn*gam_fac*state.u/c)**(2.0*self.gamma/(self.gamma-1.0))
114+
solution = State(rho=rho, u=u, p=p)
115+
116+
return solution
117+
118+
def sample_solution(self):
119+
"""given the star state (ustar, pstar), find the state on the interface"""
120+
121+
gam_fac = (self.gamma - 1.0)/(self.gamma + 1.0)
122+
123+
if self.ustar < 0:
124+
# we are in the R* or R region
125+
state = self.right
126+
sgn = 1.0
127+
else:
128+
# we are in the L* or L region
129+
state = self.left
130+
sgn = -1.0
131+
132+
# is the non-contact wave a shock or rarefaction?
133+
if self.pstar > state.p:
134+
# compression! we are a shock
135+
solution = self.shock_solution(sgn, state)
136+
137+
else:
138+
# rarefaction
139+
solution = self.rarefaction_solution(sgn, state)
140+
141+
return solution
142+
143+
def cons_flux(state, v):
144+
""" given an interface state, return the conservative flux"""
145+
flux = np.zeros((v.nvar), dtype=np.float64)
146+
147+
flux[v.urho] = state.rho * state.u
148+
flux[v.umx] = flux[v.urho] * state.u + state.p
149+
flux[v.uener] = (0.5 * state.rho * state.u**2 +
150+
state.p/(v.gamma - 1.0) + state.p) * state.u
151+
return flux
152+
153+
if __name__ == "__main__":
154+
155+
q_l = State(rho=1.0, u=0.0, p=1.0)
156+
q_r = State(rho=0.125, u=0.0, p=0.1)
157+
158+
rp = RiemannProblem(q_l, q_r, gamma=1.4)
159+
160+
rp.find_star_state()
161+
q_int = rp.sample_solution()
162+
print(q_int)

0 commit comments

Comments
 (0)