|
| 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