Source code for pyro.compressible.riemann

import numpy as np
from numba import njit

import pyro.mesh.array_indexer as ai
from pyro.util import msg


[docs] @njit(cache=True) def riemann_cgf(idir, ng, idens, ixmom, iymom, iener, irhoX, nspec, lower_solid, upper_solid, gamma, U_l, U_r): r""" Solve riemann shock tube problem for a general equation of state using the method of Colella, Glaz, and Ferguson. See Almgren et al. 2010 (the CASTRO paper) for details. The Riemann problem for the Euler's equation produces 4 regions, separated by the three characteristics (u - cs, u, u + cs):: u - cs t u u + cs \ ^ . / \ *L | . *R / \ | . / \ | . / L \ | . / R \ | . / \ |. / \|./ ----------+----------------> x We care about the solution on the axis. The basic idea is to use estimates of the wave speeds to figure out which region we are in, and: use jump conditions to evaluate the state there. Only density jumps across the u characteristic. All primitive variables jump across the other two. Special attention is needed if a rarefaction spans the axis. Parameters ---------- idir : int Are we predicting to the edges in the x-direction (1) or y-direction (2)? ng : int The number of ghost cells nspec : int The number of species idens, ixmom, iymom, iener, irhoX : int The indices of the density, x-momentum, y-momentum, internal energy density and species partial densities in the conserved state vector. lower_solid, upper_solid : int Are we at lower or upper solid boundaries? gamma : float Adiabatic index U_l, U_r : ndarray Conserved state on the left and right cell edges. Returns ------- out : ndarray Conserved states. """ # pylint: disable=unused-variable qx, qy, nvar = U_l.shape U_out = np.zeros((qx, qy, nvar)) smallc = 1.e-10 smallrho = 1.e-10 smallp = 1.e-10 nx = qx - 2 * ng ny = qy - 2 * ng ilo = ng ihi = ng + nx jlo = ng jhi = ng + ny for i in range(ilo - 1, ihi + 1): for j in range(jlo - 1, jhi + 1): # primitive variable states rho_l = U_l[i, j, idens] # un = normal velocity; ut = transverse velocity if idir == 1: un_l = U_l[i, j, ixmom] / rho_l ut_l = U_l[i, j, iymom] / rho_l else: un_l = U_l[i, j, iymom] / rho_l ut_l = U_l[i, j, ixmom] / rho_l rhoe_l = U_l[i, j, iener] - 0.5 * rho_l * (un_l**2 + ut_l**2) p_l = rhoe_l * (gamma - 1.0) p_l = max(p_l, smallp) rho_r = U_r[i, j, idens] if idir == 1: un_r = U_r[i, j, ixmom] / rho_r ut_r = U_r[i, j, iymom] / rho_r else: un_r = U_r[i, j, iymom] / rho_r ut_r = U_r[i, j, ixmom] / rho_r rhoe_r = U_r[i, j, iener] - 0.5 * rho_r * (un_r**2 + ut_r**2) p_r = rhoe_r * (gamma - 1.0) p_r = max(p_r, smallp) # define the Lagrangian sound speed W_l = max(smallrho * smallc, np.sqrt(gamma * p_l * rho_l)) W_r = max(smallrho * smallc, np.sqrt(gamma * p_r * rho_r)) # and the regular sound speeds c_l = max(smallc, np.sqrt(gamma * p_l / rho_l)) c_r = max(smallc, np.sqrt(gamma * p_r / rho_r)) # define the star states pstar = (W_l * p_r + W_r * p_l + W_l * W_r * (un_l - un_r)) / (W_l + W_r) pstar = max(pstar, smallp) ustar = (W_l * un_l + W_r * un_r + (p_l - p_r)) / (W_l + W_r) # now compute the remaining state to the left and right # of the contact (in the star region) rhostar_l = rho_l + (pstar - p_l) / c_l**2 rhostar_r = rho_r + (pstar - p_r) / c_r**2 rhoestar_l = rhoe_l + \ (pstar - p_l) * (rhoe_l / rho_l + p_l / rho_l) / c_l**2 rhoestar_r = rhoe_r + \ (pstar - p_r) * (rhoe_r / rho_r + p_r / rho_r) / c_r**2 cstar_l = max(smallc, np.sqrt(gamma * pstar / rhostar_l)) cstar_r = max(smallc, np.sqrt(gamma * pstar / rhostar_r)) # figure out which state we are in, based on the location of # the waves if ustar > 0.0: # contact is moving to the right, we need to understand # the L and *L states # Note: transverse velocity only jumps across contact ut_state = ut_l # define eigenvalues lambda_l = un_l - c_l lambdastar_l = ustar - cstar_l if pstar > p_l: # the wave is a shock -- find the shock speed sigma = (lambda_l + lambdastar_l) / 2.0 if sigma > 0.0: # shock is moving to the right -- solution is L state rho_state = rho_l un_state = un_l p_state = p_l rhoe_state = rhoe_l else: # solution is *L state rho_state = rhostar_l un_state = ustar p_state = pstar rhoe_state = rhoestar_l else: # the wave is a rarefaction if lambda_l < 0.0 and lambdastar_l < 0.0: # rarefaction fan is moving to the left -- solution is # *L state rho_state = rhostar_l un_state = ustar p_state = pstar rhoe_state = rhoestar_l elif lambda_l > 0.0 and lambdastar_l > 0.0: # rarefaction fan is moving to the right -- solution is # L state rho_state = rho_l un_state = un_l p_state = p_l rhoe_state = rhoe_l else: # rarefaction spans x/t = 0 -- interpolate alpha = lambda_l / (lambda_l - lambdastar_l) rho_state = alpha * rhostar_l + (1.0 - alpha) * rho_l un_state = alpha * ustar + (1.0 - alpha) * un_l p_state = alpha * pstar + (1.0 - alpha) * p_l rhoe_state = alpha * rhoestar_l + \ (1.0 - alpha) * rhoe_l elif ustar < 0: # contact moving left, we need to understand the R and *R # states # Note: transverse velocity only jumps across contact ut_state = ut_r # define eigenvalues lambda_r = un_r + c_r lambdastar_r = ustar + cstar_r if pstar > p_r: # the wave if a shock -- find the shock speed sigma = (lambda_r + lambdastar_r) / 2.0 if sigma > 0.0: # shock is moving to the right -- solution is *R state rho_state = rhostar_r un_state = ustar p_state = pstar rhoe_state = rhoestar_r else: # solution is R state rho_state = rho_r un_state = un_r p_state = p_r rhoe_state = rhoe_r else: # the wave is a rarefaction if lambda_r < 0.0 and lambdastar_r < 0.0: # rarefaction fan is moving to the left -- solution is # R state rho_state = rho_r un_state = un_r p_state = p_r rhoe_state = rhoe_r elif lambda_r > 0.0 and lambdastar_r > 0.0: # rarefaction fan is moving to the right -- solution is # *R state rho_state = rhostar_r un_state = ustar p_state = pstar rhoe_state = rhoestar_r else: # rarefaction spans x/t = 0 -- interpolate alpha = lambda_r / (lambda_r - lambdastar_r) rho_state = alpha * rhostar_r + (1.0 - alpha) * rho_r un_state = alpha * ustar + (1.0 - alpha) * un_r p_state = alpha * pstar + (1.0 - alpha) * p_r rhoe_state = alpha * rhoestar_r + \ (1.0 - alpha) * rhoe_r else: # ustar == 0 rho_state = 0.5 * (rhostar_l + rhostar_r) un_state = ustar ut_state = 0.5 * (ut_l + ut_r) p_state = pstar rhoe_state = 0.5 * (rhoestar_l + rhoestar_r) # species now if nspec > 0: if ustar > 0.0: xn = U_l[i, j, irhoX:irhoX + nspec] / U_l[i, j, idens] elif ustar < 0.0: xn = U_r[i, j, irhoX:irhoX + nspec] / U_r[i, j, idens] else: xn = 0.5 * (U_l[i, j, irhoX:irhoX + nspec] / U_l[i, j, idens] + U_r[i, j, irhoX:irhoX + nspec] / U_r[i, j, idens]) # are we on a solid boundary? if idir == 1: if i == ilo and lower_solid == 1: un_state = 0.0 if i == ihi + 1 and upper_solid == 1: un_state = 0.0 elif idir == 2: if j == jlo and lower_solid == 1: un_state = 0.0 if j == jhi + 1 and upper_solid == 1: un_state = 0.0 # update conserved state U_out[i, j, idens] = rho_state if idir == 1: U_out[i, j, ixmom] = rho_state * un_state U_out[i, j, iymom] = rho_state * ut_state else: U_out[i, j, ixmom] = rho_state * ut_state U_out[i, j, iymom] = rho_state * un_state U_out[i, j, iener] = rhoe_state + \ 0.5 * rho_state * (un_state**2 + ut_state**2) if nspec > 0: U_out[i, j, irhoX:irhoX + nspec] = xn * rho_state return U_out
[docs] @njit(cache=True) def riemann_prim(idir, ng, irho, iu, iv, ip, iX, nspec, lower_solid, upper_solid, gamma, q_l, q_r): r""" this is like riemann_cgf, except that it works on a primitive variable input state and returns the primitive variable interface state Solve riemann shock tube problem for a general equation of state using the method of Colella, Glaz, and Ferguson. See Almgren et al. 2010 (the CASTRO paper) for details. The Riemann problem for the Euler's equation produces 4 regions, separated by the three characteristics :math:`(u - cs, u, u + cs)`:: u - cs t u u + cs \ ^ . / \ *L | . *R / \ | . / \ | . / L \ | . / R \ | . / \ |. / \|./ ----------+----------------> x We care about the solution on the axis. The basic idea is to use estimates of the wave speeds to figure out which region we are in, and: use jump conditions to evaluate the state there. Only density jumps across the :math:`u` characteristic. All primitive variables jump across the other two. Special attention is needed if a rarefaction spans the axis. Parameters ---------- idir : int Are we predicting to the edges in the x-direction (1) or y-direction (2)? ng : int The number of ghost cells nspec : int The number of species irho, iu, iv, ip, iX : int The indices of the density, x-velocity, y-velocity, pressure and species fractions in the state vector. lower_solid, upper_solid : int Are we at lower or upper solid boundaries? gamma : float Adiabatic index q_l, q_r : ndarray Primitive state on the left and right cell edges. Returns ------- out : ndarray Primitive flux """ qx, qy, nvar = q_l.shape q_int = np.zeros((qx, qy, nvar)) smallc = 1.e-10 smallrho = 1.e-10 smallp = 1.e-10 nx = qx - 2 * ng ny = qy - 2 * ng ilo = ng ihi = ng + nx jlo = ng jhi = ng + ny for i in range(ilo - 1, ihi + 1): for j in range(jlo - 1, jhi + 1): # primitive variable states rho_l = q_l[i, j, irho] # un = normal velocity; ut = transverse velocity if idir == 1: un_l = q_l[i, j, iu] ut_l = q_l[i, j, iv] else: un_l = q_l[i, j, iv] ut_l = q_l[i, j, iu] p_l = q_l[i, j, ip] p_l = max(p_l, smallp) rho_r = q_r[i, j, irho] if idir == 1: un_r = q_r[i, j, iu] ut_r = q_r[i, j, iv] else: un_r = q_r[i, j, iv] ut_r = q_r[i, j, iu] p_r = q_r[i, j, ip] p_r = max(p_r, smallp) # define the Lagrangian sound speed rho_l = max(smallrho, rho_l) rho_r = max(smallrho, rho_r) W_l = max(smallrho * smallc, np.sqrt(gamma * p_l * rho_l)) W_r = max(smallrho * smallc, np.sqrt(gamma * p_r * rho_r)) # and the regular sound speeds c_l = max(smallc, np.sqrt(gamma * p_l / rho_l)) c_r = max(smallc, np.sqrt(gamma * p_r / rho_r)) # define the star states pstar = (W_l * p_r + W_r * p_l + W_l * W_r * (un_l - un_r)) / (W_l + W_r) pstar = max(pstar, smallp) ustar = (W_l * un_l + W_r * un_r + (p_l - p_r)) / (W_l + W_r) # now compute the remaining state to the left and right # of the contact (in the star region) rhostar_l = rho_l + (pstar - p_l) / c_l**2 rhostar_r = rho_r + (pstar - p_r) / c_r**2 cstar_l = max(smallc, np.sqrt(gamma * pstar / rhostar_l)) cstar_r = max(smallc, np.sqrt(gamma * pstar / rhostar_r)) # figure out which state we are in, based on the location of # the waves if ustar > 0.0: # contact is moving to the right, we need to understand # the L and *L states # Note: transverse velocity only jumps across contact ut_state = ut_l # define eigenvalues lambda_l = un_l - c_l lambdastar_l = ustar - cstar_l if pstar > p_l: # the wave is a shock -- find the shock speed sigma = (lambda_l + lambdastar_l) / 2.0 if sigma > 0.0: # shock is moving to the right -- solution is L state rho_state = rho_l un_state = un_l p_state = p_l else: # solution is *L state rho_state = rhostar_l un_state = ustar p_state = pstar else: # the wave is a rarefaction if lambda_l < 0.0 and lambdastar_l < 0.0: # rarefaction fan is moving to the left -- solution is # *L state rho_state = rhostar_l un_state = ustar p_state = pstar elif lambda_l > 0.0 and lambdastar_l > 0.0: # rarefaction fan is moving to the right -- solution is # L state rho_state = rho_l un_state = un_l p_state = p_l else: # rarefaction spans x/t = 0 -- interpolate alpha = lambda_l / (lambda_l - lambdastar_l) rho_state = alpha * rhostar_l + (1.0 - alpha) * rho_l un_state = alpha * ustar + (1.0 - alpha) * un_l p_state = alpha * pstar + (1.0 - alpha) * p_l elif ustar < 0: # contact moving left, we need to understand the R and *R # states # Note: transverse velocity only jumps across contact ut_state = ut_r # define eigenvalues lambda_r = un_r + c_r lambdastar_r = ustar + cstar_r if pstar > p_r: # the wave if a shock -- find the shock speed sigma = (lambda_r + lambdastar_r) / 2.0 if sigma > 0.0: # shock is moving to the right -- solution is *R state rho_state = rhostar_r un_state = ustar p_state = pstar else: # solution is R state rho_state = rho_r un_state = un_r p_state = p_r else: # the wave is a rarefaction if lambda_r < 0.0 and lambdastar_r < 0.0: # rarefaction fan is moving to the left -- solution is # R state rho_state = rho_r un_state = un_r p_state = p_r elif lambda_r > 0.0 and lambdastar_r > 0.0: # rarefaction fan is moving to the right -- solution is # *R state rho_state = rhostar_r un_state = ustar p_state = pstar else: # rarefaction spans x/t = 0 -- interpolate alpha = lambda_r / (lambda_r - lambdastar_r) rho_state = alpha * rhostar_r + (1.0 - alpha) * rho_r un_state = alpha * ustar + (1.0 - alpha) * un_r p_state = alpha * pstar + (1.0 - alpha) * p_r else: # ustar == 0 rho_state = 0.5 * (rhostar_l + rhostar_r) un_state = ustar ut_state = 0.5 * (ut_l + ut_r) p_state = pstar # species now if nspec > 0: if ustar > 0.0: xn = q_l[i, j, iX:iX + nspec] elif ustar < 0.0: xn = q_r[i, j, iX:iX + nspec] else: xn = 0.5 * (q_l[i, j, iX:iX + nspec] + q_r[i, j, iX:iX + nspec]) # are we on a solid boundary? if idir == 1: if i == ilo and lower_solid == 1: un_state = 0.0 if i == ihi + 1 and upper_solid == 1: un_state = 0.0 elif idir == 2: if j == jlo and lower_solid == 1: un_state = 0.0 if j == jhi + 1 and upper_solid == 1: un_state = 0.0 q_int[i, j, irho] = rho_state if idir == 1: q_int[i, j, iu] = un_state q_int[i, j, iv] = ut_state else: q_int[i, j, iu] = ut_state q_int[i, j, iv] = un_state q_int[i, j, ip] = p_state if nspec > 0: q_int[i, j, iX:iX + nspec] = xn return q_int
[docs] @njit(cache=True) def estimate_wave_speed(rho_l, u_l, p_l, c_l, rho_r, u_r, p_r, c_r, gamma): # Estimate the star quantities -- use one of three methods to # do this -- the primitive variable Riemann solver, the two # shock approximation, or the two rarefaction approximation. # Pick the method based on the pressure states at the # interface. p_max = max(p_l, p_r) p_min = min(p_l, p_r) Q = p_max / p_min rho_avg = 0.5 * (rho_l + rho_r) c_avg = 0.5 * (c_l + c_r) # primitive variable Riemann solver (Toro, 9.3) factor = rho_avg * c_avg pstar = 0.5 * (p_l + p_r) + 0.5 * (u_l - u_r) * factor ustar = 0.5 * (u_l + u_r) + 0.5 * (p_l - p_r) / factor if Q > 2 and (pstar < p_min or pstar > p_max): # use a more accurate Riemann solver for the estimate here if pstar < p_min: # 2-rarefaction Riemann solver z = (gamma - 1.0) / (2.0 * gamma) p_lr = (p_l / p_r)**z ustar = (p_lr * u_l / c_l + u_r / c_r + 2.0 * (p_lr - 1.0) / (gamma - 1.0)) / \ (p_lr / c_l + 1.0 / c_r) pstar = 0.5 * (p_l * (1.0 + (gamma - 1.0) * (u_l - ustar) / (2.0 * c_l))**(1.0 / z) + p_r * (1.0 + (gamma - 1.0) * (ustar - u_r) / (2.0 * c_r))**(1.0 / z)) else: # 2-shock Riemann solver A_r = 2.0 / ((gamma + 1.0) * rho_r) B_r = p_r * (gamma - 1.0) / (gamma + 1.0) A_l = 2.0 / ((gamma + 1.0) * rho_l) B_l = p_l * (gamma - 1.0) / (gamma + 1.0) # guess of the pressure p_guess = max(0.0, pstar) g_l = np.sqrt(A_l / (p_guess + B_l)) g_r = np.sqrt(A_r / (p_guess + B_r)) pstar = (g_l * p_l + g_r * p_r - (u_r - u_l)) / (g_l + g_r) ustar = 0.5 * (u_l + u_r) + \ 0.5 * ((pstar - p_r) * g_r - (pstar - p_l) * g_l) # estimate the nonlinear wave speeds if pstar <= p_l: # rarefaction S_l = u_l - c_l else: # shock S_l = u_l - c_l * np.sqrt(1.0 + ((gamma + 1.0) / (2.0 * gamma)) * (pstar / p_l - 1.0)) if pstar <= p_r: # rarefaction S_r = u_r + c_r else: # shock S_r = u_r + c_r * np.sqrt(1.0 + ((gamma + 1.0) / (2.0 / gamma)) * (pstar / p_r - 1.0)) return S_l, S_r
[docs] @njit(cache=True) def riemann_hllc(idir, ng, idens, ixmom, iymom, iener, irhoX, nspec, lower_solid, upper_solid, # pylint: disable=unused-argument gamma, U_l, U_r): r""" This is the HLLC Riemann solver. The implementation follows directly out of Toro's book. Note: this does not handle the transonic rarefaction. Parameters ---------- idir : int Are we predicting to the edges in the x-direction (1) or y-direction (2)? ng : int The number of ghost cells nspec : int The number of species idens, ixmom, iymom, iener, irhoX : int The indices of the density, x-momentum, y-momentum, internal energy density and species partial densities in the conserved state vector. lower_solid, upper_solid : int Are we at lower or upper solid boundaries? gamma : float Adiabatic index U_l, U_r : ndarray Conserved state on the left and right cell edges. Returns ------- out : ndarray Conserved flux """ # Only Cartesian2d is supported in HLLC coord_type = 0 qx, qy, nvar = U_l.shape F = np.zeros((qx, qy, nvar)) smallc = 1.e-10 smallp = 1.e-10 U_state = np.zeros(nvar) nx = qx - 2 * ng ny = qy - 2 * ng ilo = ng ihi = ng + nx jlo = ng jhi = ng + ny for i in range(ilo - 1, ihi + 1): for j in range(jlo - 1, jhi + 1): # primitive variable states rho_l = U_l[i, j, idens] # un = normal velocity; ut = transverse velocity if idir == 1: un_l = U_l[i, j, ixmom] / rho_l ut_l = U_l[i, j, iymom] / rho_l else: un_l = U_l[i, j, iymom] / rho_l ut_l = U_l[i, j, ixmom] / rho_l rhoe_l = U_l[i, j, iener] - 0.5 * rho_l * (un_l**2 + ut_l**2) p_l = rhoe_l * (gamma - 1.0) p_l = max(p_l, smallp) rho_r = U_r[i, j, idens] if idir == 1: un_r = U_r[i, j, ixmom] / rho_r ut_r = U_r[i, j, iymom] / rho_r else: un_r = U_r[i, j, iymom] / rho_r ut_r = U_r[i, j, ixmom] / rho_r rhoe_r = U_r[i, j, iener] - 0.5 * rho_r * (un_r**2 + ut_r**2) p_r = rhoe_r * (gamma - 1.0) p_r = max(p_r, smallp) # compute the sound speeds c_l = max(smallc, np.sqrt(gamma * p_l / rho_l)) c_r = max(smallc, np.sqrt(gamma * p_r / rho_r)) S_l, S_r = estimate_wave_speed(rho_l, un_l, p_l, c_l, rho_r, un_r, p_r, c_r, gamma) # We could just take S_c = u_star as the estimate for the # contact speed, but we can actually do this more accurately # by using the Rankine-Hugonoit jump conditions across each # of the waves (see Toro 10.58, Batten et al. SIAM # J. Sci. and Stat. Comp., 18:1553 (1997) S_c = (p_r - p_l + rho_l * un_l * (S_l - un_l) - rho_r * un_r * (S_r - un_r)) / \ (rho_l * (S_l - un_l) - rho_r * (S_r - un_r)) # figure out which region we are in and compute the state and # the interface fluxes using the HLLC Riemann solver if S_r <= 0.0: # R region U_state[:] = U_r[i, j, :] F[i, j, :] = consFlux(idir, coord_type, gamma, idens, ixmom, iymom, iener, irhoX, nspec, U_state) elif S_c <= 0.0 < S_r: # R* region HLLCfactor = rho_r * (S_r - un_r) / (S_r - S_c) U_state[idens] = HLLCfactor if idir == 1: U_state[ixmom] = HLLCfactor * S_c U_state[iymom] = HLLCfactor * ut_r else: U_state[ixmom] = HLLCfactor * ut_r U_state[iymom] = HLLCfactor * S_c U_state[iener] = HLLCfactor * (U_r[i, j, iener] / rho_r + (S_c - un_r) * (S_c + p_r / (rho_r * (S_r - un_r)))) # species if nspec > 0: U_state[irhoX:irhoX + nspec] = HLLCfactor * \ U_r[i, j, irhoX:irhoX + nspec] / rho_r # find the flux on the right interface F[i, j, :] = consFlux(idir, coord_type, gamma, idens, ixmom, iymom, iener, irhoX, nspec, U_r[i, j, :]) # correct the flux F[i, j, :] = F[i, j, :] + S_r * (U_state[:] - U_r[i, j, :]) elif S_l < 0.0 < S_c: # L* region HLLCfactor = rho_l * (S_l - un_l) / (S_l - S_c) U_state[idens] = HLLCfactor if idir == 1: U_state[ixmom] = HLLCfactor * S_c U_state[iymom] = HLLCfactor * ut_l else: U_state[ixmom] = HLLCfactor * ut_l U_state[iymom] = HLLCfactor * S_c U_state[iener] = HLLCfactor * (U_l[i, j, iener] / rho_l + (S_c - un_l) * (S_c + p_l / (rho_l * (S_l - un_l)))) # species if nspec > 0: U_state[irhoX:irhoX + nspec] = HLLCfactor * \ U_l[i, j, irhoX:irhoX + nspec] / rho_l # find the flux on the left interface F[i, j, :] = consFlux(idir, coord_type, gamma, idens, ixmom, iymom, iener, irhoX, nspec, U_l[i, j, :]) # correct the flux F[i, j, :] = F[i, j, :] + S_l * (U_state[:] - U_l[i, j, :]) else: # L region U_state[:] = U_l[i, j, :] F[i, j, :] = consFlux(idir, coord_type, gamma, idens, ixmom, iymom, iener, irhoX, nspec, U_state) # we should deal with solid boundaries somehow here return F
[docs] @njit(cache=True) def riemann_hllc_lowspeed(idir, ng, idens, ixmom, iymom, iener, irhoX, nspec, lower_solid, upper_solid, # pylint: disable=unused-argument gamma, U_l, U_r): r""" This is the HLLC Riemann solver based on Toro (2009) alternate formulation (Eqs. 10.43 and 10.44) and the low Mach number asymptotic fix of Minoshima & Miyoshi (2021). It is also based on the Quokka implementation. Parameters ---------- idir : int Are we predicting to the edges in the x-direction (1) or y-direction (2)? ng : int The number of ghost cells nspec : int The number of species idens, ixmom, iymom, iener, irhoX : int The indices of the density, x-momentum, y-momentum, internal energy density and species partial densities in the conserved state vector. lower_solid, upper_solid : int Are we at lower or upper solid boundaries? gamma : float Adiabatic index U_l, U_r : ndarray Conserved state on the left and right cell edges. Returns ------- out : ndarray Conserved flux """ # Only Cartesian2d is supported in HLLC coord_type = 0 qx, qy, nvar = U_l.shape F = np.zeros((qx, qy, nvar)) smallc = 1.e-10 smallp = 1.e-10 nx = qx - 2 * ng ny = qy - 2 * ng ilo = ng ihi = ng + nx jlo = ng jhi = ng + ny for i in range(ilo - 1, ihi + 1): for j in range(jlo - 1, jhi + 1): D_star = np.zeros(nvar) # primitive variable states rho_l = U_l[i, j, idens] # un = normal velocity; ut = transverse velocity iun = -1 if idir == 1: un_l = U_l[i, j, ixmom] / rho_l ut_l = U_l[i, j, iymom] / rho_l iun = ixmom else: un_l = U_l[i, j, iymom] / rho_l ut_l = U_l[i, j, ixmom] / rho_l iun = iymom rhoe_l = U_l[i, j, iener] - 0.5 * rho_l * (un_l**2 + ut_l**2) p_l = rhoe_l * (gamma - 1.0) p_l = max(p_l, smallp) rho_r = U_r[i, j, idens] if idir == 1: un_r = U_r[i, j, ixmom] / rho_r ut_r = U_r[i, j, iymom] / rho_r else: un_r = U_r[i, j, iymom] / rho_r ut_r = U_r[i, j, ixmom] / rho_r rhoe_r = U_r[i, j, iener] - 0.5 * rho_r * (un_r**2 + ut_r**2) p_r = rhoe_r * (gamma - 1.0) p_r = max(p_r, smallp) # compute the sound speeds c_l = max(smallc, np.sqrt(gamma * p_l / rho_l)) c_r = max(smallc, np.sqrt(gamma * p_r / rho_r)) S_l, S_r = estimate_wave_speed(rho_l, un_l, p_l, c_l, rho_r, un_r, p_r, c_r, gamma) # We could just take S_c = u_star as the estimate for the # contact speed, but we can actually do this more accurately # by using the Rankine-Hugonoit jump conditions across each # of the waves (see Toro 2009 Eq, 10.37, Batten et al. SIAM # J. Sci. and Stat. Comp., 18:1553 (1997) S_c = (p_r - p_l + rho_l * un_l * (S_l - un_l) - rho_r * un_r * (S_r - un_r)) / \ (rho_l * (S_l - un_l) - rho_r * (S_r - un_r)) # D* is used to control the pressure addition to the star flux D_star[iun] = 1.0 D_star[iener] = S_c # compute the fluxes corresponding to the left and right states U_state_l = U_l[i, j, :] F_l = consFlux(idir, coord_type, gamma, idens, ixmom, iymom, iener, irhoX, nspec, U_state_l) U_state_r = U_r[i, j, :] F_r = consFlux(idir, coord_type, gamma, idens, ixmom, iymom, iener, irhoX, nspec, U_state_r) # compute the star pressure with the low Mach correction # Minoshima & Miyoshi (2021) Eq. 23. This is actually averaging # the left and right pressures (see also Toro 2009 Eq. 10.42) vmag_l = np.sqrt(un_l**2 + ut_l**2) vmag_r = np.sqrt(un_r**2 + ut_r**2) cs_max = max(c_l, c_r) chi = min(1.0, max(vmag_l, vmag_r) / cs_max) phi = chi * (2.0 - chi) pstar_lr = 0.5 * (p_l + p_r) + \ 0.5 * phi * (rho_l * (S_l - un_l) * (S_c - un_l) + rho_r * (S_r - un_r) * (S_c - un_r)) # figure out which region we are in and compute the state and # the interface fluxes using the HLLC Riemann solver if S_r <= 0.0: # R region F[i, j, :] = F_r[:] elif S_c <= 0.0 < S_r: # R* region F[i, j, :] = (S_c * (S_r * U_state_r - F_r) + S_r * pstar_lr * D_star) / (S_r - S_c) elif S_l < 0.0 < S_c: # L* region F[i, j, :] = (S_c * (S_l * U_state_l - F_l) + S_l * pstar_lr * D_star) / (S_l - S_c) else: # L region F[i, j, :] = F_l[:] # we should deal with solid boundaries somehow here return F
[docs] def riemann_flux(idir, U_l, U_r, my_data, rp, ivars, lower_solid, upper_solid, tc, return_cons=False): """ This is the general interface that constructs the unsplit fluxes through the idir (1 for x, 2 for y) interfaces using the left and right conserved states by using the riemann solver. Parameters ---------- U_l, U_r: ndarray, ndarray Conserved states in the left and right interface my_data : CellCenterData2d object The data object containing the grid and advective scalar that we are advecting. rp : RuntimeParameters object The runtime parameters for the simulation ivars : Variables object The Variables object that tells us which indices refer to which variables lower_solid, upper_solid : int Are we at lower or upper solid boundaries? tc : TimerCollection object The timers we are using to profile return_cons: Boolean If we don't use HLLC Riemann solver, do we also return conserved states? Returns ------- F : ndarray Fluxes in x or y direction Optionally: U: ndarray Conserved states in x or y direction """ tm_riem = tc.timer("riemann") tm_riem.begin() myg = my_data.grid riemann_method = rp.get_param("compressible.riemann") gamma = rp.get_param("eos.gamma") riemann_solvers = {"HLLC": riemann_hllc, "HLLC_lm": riemann_hllc_lowspeed, "CGF": riemann_cgf} if riemann_method not in riemann_solvers: msg.fail("ERROR: Riemann solver undefined") riemannFunc = riemann_solvers[riemann_method] # This returns Flux in idir direction if we use HLLC # and conserved states otherwise _u = riemannFunc(idir, myg.ng, ivars.idens, ivars.ixmom, ivars.iymom, ivars.iener, ivars.irhox, ivars.naux, lower_solid, upper_solid, gamma, U_l, U_r) # If riemann_method is not HLLC, then construct flux using conserved states if riemann_method not in ["HLLC", "HLLC_lm"]: _f = consFlux(idir, myg.coord_type, gamma, ivars.idens, ivars.ixmom, ivars.iymom, ivars.iener, ivars.irhox, ivars.naux, _u) else: # If riemann_method is HLLC, then its already flux _f = _u F = ai.ArrayIndexer(d=_f, grid=myg) tm_riem.end() if riemann_method not in ["HLLC", "HLLC_lm"] and return_cons: U = ai.ArrayIndexer(d=_u, grid=myg) return F, U return F
[docs] @njit(cache=True) def consFlux(idir, coord_type, gamma, idens, ixmom, iymom, iener, irhoX, nspec, U_state): r""" Calculate the conservative flux. Parameters ---------- idir : int Are we predicting to the edges in the x-direction (1) or y-direction (2)? gamma : float Adiabatic index idens, ixmom, iymom, iener, irhoX : int The indices of the density, x-momentum, y-momentum, internal energy density and species partial densities in the conserved state vector. nspec : int The number of species U_state : ndarray Conserved state vector. Returns ------- out : ndarray Conserved flux """ F = np.zeros_like(U_state) if U_state.ndim == 1: u = 0.0 v = 0.0 if U_state[idens] != 0.0: u = U_state[ixmom] / U_state[idens] v = U_state[iymom] / U_state[idens] else: u = np.zeros_like(U_state[..., ixmom]) v = np.zeros_like(U_state[..., iymom]) for i in range(U_state.shape[0]): for j in range(U_state.shape[1]): if U_state[i, j, idens] != 0.0: u[i, j] = U_state[i, j, ixmom] / U_state[i, j, idens] v[i, j] = U_state[i, j, iymom] / U_state[i, j, idens] p = (U_state[..., iener] - 0.5 * U_state[..., idens] * (u * u + v * v)) * (gamma - 1.0) if idir == 1: F[..., idens] = U_state[..., idens] * u F[..., ixmom] = U_state[..., ixmom] * u # if Cartesian2d, then add pressure to xmom flux if coord_type == 0: F[..., ixmom] += p F[..., iymom] = U_state[..., iymom] * u F[..., iener] = (U_state[..., iener] + p) * u if nspec > 0: F[..., irhoX:irhoX + nspec] = U_state[..., irhoX:irhoX + nspec] * u else: F[..., idens] = U_state[..., idens] * v F[..., ixmom] = U_state[..., ixmom] * v F[..., iymom] = U_state[..., iymom] * v # if Cartesian2d, then add pressure to ymom flux if coord_type == 0: F[..., iymom] += p F[..., iener] = (U_state[..., iener] + p) * v if nspec > 0: F[..., irhoX:irhoX + nspec] = U_state[..., irhoX:irhoX + nspec] * v return F