Skip to content

Commit a7532c4

Browse files
authored
add an implementation of an HLLC solver with low Mach corrections (#282)
this follows Minioshima & Miyoshi (2021) to get the correct asymptotic pressure behavior at low Mach numbers. This was inspired by Quokka's implementation. Note: at the moment, this does not work with characteristic tracing, so it should be used with `compressible_rk`.
1 parent d058c7b commit a7532c4

File tree

1 file changed

+252
-93
lines changed

1 file changed

+252
-93
lines changed

pyro/compressible/riemann.py

Lines changed: 252 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,91 @@ def riemann_prim(idir, ng,
593593
return q_int
594594

595595

596+
@njit(cache=True)
597+
def estimate_wave_speed(rho_l, u_l, p_l, c_l,
598+
rho_r, u_r, p_r, c_r,
599+
gamma):
600+
601+
# Estimate the star quantities -- use one of three methods to
602+
# do this -- the primitive variable Riemann solver, the two
603+
# shock approximation, or the two rarefaction approximation.
604+
# Pick the method based on the pressure states at the
605+
# interface.
606+
607+
p_max = max(p_l, p_r)
608+
p_min = min(p_l, p_r)
609+
610+
Q = p_max / p_min
611+
612+
rho_avg = 0.5 * (rho_l + rho_r)
613+
c_avg = 0.5 * (c_l + c_r)
614+
615+
# primitive variable Riemann solver (Toro, 9.3)
616+
factor = rho_avg * c_avg
617+
618+
pstar = 0.5 * (p_l + p_r) + 0.5 * (u_l - u_r) * factor
619+
ustar = 0.5 * (u_l + u_r) + 0.5 * (p_l - p_r) / factor
620+
621+
if Q > 2 and (pstar < p_min or pstar > p_max):
622+
623+
# use a more accurate Riemann solver for the estimate here
624+
625+
if pstar < p_min:
626+
627+
# 2-rarefaction Riemann solver
628+
z = (gamma - 1.0) / (2.0 * gamma)
629+
p_lr = (p_l / p_r)**z
630+
631+
ustar = (p_lr * u_l / c_l + u_r / c_r +
632+
2.0 * (p_lr - 1.0) / (gamma - 1.0)) / \
633+
(p_lr / c_l + 1.0 / c_r)
634+
635+
pstar = 0.5 * (p_l * (1.0 + (gamma - 1.0) * (u_l - ustar) /
636+
(2.0 * c_l))**(1.0 / z) +
637+
p_r * (1.0 + (gamma - 1.0) * (ustar - u_r) /
638+
(2.0 * c_r))**(1.0 / z))
639+
640+
else:
641+
642+
# 2-shock Riemann solver
643+
A_r = 2.0 / ((gamma + 1.0) * rho_r)
644+
B_r = p_r * (gamma - 1.0) / (gamma + 1.0)
645+
646+
A_l = 2.0 / ((gamma + 1.0) * rho_l)
647+
B_l = p_l * (gamma - 1.0) / (gamma + 1.0)
648+
649+
# guess of the pressure
650+
p_guess = max(0.0, pstar)
651+
652+
g_l = np.sqrt(A_l / (p_guess + B_l))
653+
g_r = np.sqrt(A_r / (p_guess + B_r))
654+
655+
pstar = (g_l * p_l + g_r * p_r - (u_r - u_l)) / (g_l + g_r)
656+
657+
ustar = 0.5 * (u_l + u_r) + \
658+
0.5 * ((pstar - p_r) * g_r - (pstar - p_l) * g_l)
659+
660+
# estimate the nonlinear wave speeds
661+
662+
if pstar <= p_l:
663+
# rarefaction
664+
S_l = u_l - c_l
665+
else:
666+
# shock
667+
S_l = u_l - c_l * np.sqrt(1.0 + ((gamma + 1.0) / (2.0 * gamma)) *
668+
(pstar / p_l - 1.0))
669+
670+
if pstar <= p_r:
671+
# rarefaction
672+
S_r = u_r + c_r
673+
else:
674+
# shock
675+
S_r = u_r + c_r * np.sqrt(1.0 + ((gamma + 1.0) / (2.0 / gamma)) *
676+
(pstar / p_r - 1.0))
677+
678+
return S_l, S_r
679+
680+
596681
@njit(cache=True)
597682
def riemann_hllc(idir, ng,
598683
idens, ixmom, iymom, iener, irhoX, nspec,
@@ -683,96 +768,8 @@ def riemann_hllc(idir, ng,
683768
c_l = max(smallc, np.sqrt(gamma * p_l / rho_l))
684769
c_r = max(smallc, np.sqrt(gamma * p_r / rho_r))
685770

686-
# Estimate the star quantities -- use one of three methods to
687-
# do this -- the primitive variable Riemann solver, the two
688-
# shock approximation, or the two rarefaction approximation.
689-
# Pick the method based on the pressure states at the
690-
# interface.
691-
692-
p_max = max(p_l, p_r)
693-
p_min = min(p_l, p_r)
694-
695-
Q = p_max / p_min
696-
697-
rho_avg = 0.5 * (rho_l + rho_r)
698-
c_avg = 0.5 * (c_l + c_r)
699-
700-
# primitive variable Riemann solver (Toro, 9.3)
701-
factor = rho_avg * c_avg
702-
# factor2 = rho_avg / c_avg
703-
704-
pstar = 0.5 * (p_l + p_r) + 0.5 * (un_l - un_r) * factor
705-
ustar = 0.5 * (un_l + un_r) + 0.5 * (p_l - p_r) / factor
706-
707-
# rhostar_l = rho_l + (un_l - ustar) * factor2
708-
# rhostar_r = rho_r + (ustar - un_r) * factor2
709-
710-
if Q > 2 and (pstar < p_min or pstar > p_max):
711-
712-
# use a more accurate Riemann solver for the estimate here
713-
714-
if pstar < p_min:
715-
716-
# 2-rarefaction Riemann solver
717-
z = (gamma - 1.0) / (2.0 * gamma)
718-
p_lr = (p_l / p_r)**z
719-
720-
ustar = (p_lr * un_l / c_l + un_r / c_r +
721-
2.0 * (p_lr - 1.0) / (gamma - 1.0)) / \
722-
(p_lr / c_l + 1.0 / c_r)
723-
724-
pstar = 0.5 * (p_l * (1.0 + (gamma - 1.0) * (un_l - ustar) /
725-
(2.0 * c_l))**(1.0 / z) +
726-
p_r * (1.0 + (gamma - 1.0) * (ustar - un_r) /
727-
(2.0 * c_r))**(1.0 / z))
728-
729-
# rhostar_l = rho_l * (pstar / p_l)**(1.0 / gamma)
730-
# rhostar_r = rho_r * (pstar / p_r)**(1.0 / gamma)
731-
732-
else:
733-
734-
# 2-shock Riemann solver
735-
A_r = 2.0 / ((gamma + 1.0) * rho_r)
736-
B_r = p_r * (gamma - 1.0) / (gamma + 1.0)
737-
738-
A_l = 2.0 / ((gamma + 1.0) * rho_l)
739-
B_l = p_l * (gamma - 1.0) / (gamma + 1.0)
740-
741-
# guess of the pressure
742-
p_guess = max(0.0, pstar)
743-
744-
g_l = np.sqrt(A_l / (p_guess + B_l))
745-
g_r = np.sqrt(A_r / (p_guess + B_r))
746-
747-
pstar = (g_l * p_l + g_r * p_r -
748-
(un_r - un_l)) / (g_l + g_r)
749-
750-
ustar = 0.5 * (un_l + un_r) + \
751-
0.5 * ((pstar - p_r) * g_r - (pstar - p_l) * g_l)
752-
753-
# rhostar_l = rho_l * (pstar / p_l + (gamma - 1.0) / (gamma + 1.0)) / \
754-
# ((gamma - 1.0) / (gamma + 1.0) * (pstar / p_l) + 1.0)
755-
#
756-
# rhostar_r = rho_r * (pstar / p_r + (gamma - 1.0) / (gamma + 1.0)) / \
757-
# ((gamma - 1.0) / (gamma + 1.0) * (pstar / p_r) + 1.0)
758-
759-
# estimate the nonlinear wave speeds
760-
761-
if pstar <= p_l:
762-
# rarefaction
763-
S_l = un_l - c_l
764-
else:
765-
# shock
766-
S_l = un_l - c_l * np.sqrt(1.0 + ((gamma + 1.0) / (2.0 * gamma)) *
767-
(pstar / p_l - 1.0))
768-
769-
if pstar <= p_r:
770-
# rarefaction
771-
S_r = un_r + c_r
772-
else:
773-
# shock
774-
S_r = un_r + c_r * np.sqrt(1.0 + ((gamma + 1.0) / (2.0 / gamma)) *
775-
(pstar / p_r - 1.0))
771+
S_l, S_r = estimate_wave_speed(rho_l, un_l, p_l, c_l,
772+
rho_r, un_r, p_r, c_r, gamma)
776773

777774
# We could just take S_c = u_star as the estimate for the
778775
# contact speed, but we can actually do this more accurately
@@ -863,6 +860,166 @@ def riemann_hllc(idir, ng,
863860
return F
864861

865862

863+
@njit(cache=True)
864+
def riemann_hllc_lowspeed(idir, ng,
865+
idens, ixmom, iymom, iener, irhoX, nspec,
866+
lower_solid, upper_solid, # pylint: disable=unused-argument
867+
gamma, U_l, U_r):
868+
r"""
869+
This is the HLLC Riemann solver based on Toro (2009) alternate formulation
870+
(Eqs. 10.43 and 10.44) and the low Mach number asymptotic fix of
871+
Minoshima & Miyoshi (2021). It is also based on the Quokka implementation.
872+
873+
Parameters
874+
----------
875+
idir : int
876+
Are we predicting to the edges in the x-direction (1) or y-direction (2)?
877+
ng : int
878+
The number of ghost cells
879+
nspec : int
880+
The number of species
881+
idens, ixmom, iymom, iener, irhoX : int
882+
The indices of the density, x-momentum, y-momentum, internal energy density
883+
and species partial densities in the conserved state vector.
884+
lower_solid, upper_solid : int
885+
Are we at lower or upper solid boundaries?
886+
gamma : float
887+
Adiabatic index
888+
U_l, U_r : ndarray
889+
Conserved state on the left and right cell edges.
890+
891+
Returns
892+
-------
893+
out : ndarray
894+
Conserved flux
895+
"""
896+
897+
# Only Cartesian2d is supported in HLLC
898+
coord_type = 0
899+
900+
qx, qy, nvar = U_l.shape
901+
902+
F = np.zeros((qx, qy, nvar))
903+
904+
smallc = 1.e-10
905+
smallp = 1.e-10
906+
907+
nx = qx - 2 * ng
908+
ny = qy - 2 * ng
909+
ilo = ng
910+
ihi = ng + nx
911+
jlo = ng
912+
jhi = ng + ny
913+
914+
for i in range(ilo - 1, ihi + 1):
915+
for j in range(jlo - 1, jhi + 1):
916+
917+
D_star = np.zeros(nvar)
918+
919+
# primitive variable states
920+
rho_l = U_l[i, j, idens]
921+
922+
# un = normal velocity; ut = transverse velocity
923+
iun = -1
924+
if idir == 1:
925+
un_l = U_l[i, j, ixmom] / rho_l
926+
ut_l = U_l[i, j, iymom] / rho_l
927+
iun = ixmom
928+
else:
929+
un_l = U_l[i, j, iymom] / rho_l
930+
ut_l = U_l[i, j, ixmom] / rho_l
931+
iun = iymom
932+
933+
rhoe_l = U_l[i, j, iener] - 0.5 * rho_l * (un_l**2 + ut_l**2)
934+
935+
p_l = rhoe_l * (gamma - 1.0)
936+
p_l = max(p_l, smallp)
937+
938+
rho_r = U_r[i, j, idens]
939+
940+
if idir == 1:
941+
un_r = U_r[i, j, ixmom] / rho_r
942+
ut_r = U_r[i, j, iymom] / rho_r
943+
else:
944+
un_r = U_r[i, j, iymom] / rho_r
945+
ut_r = U_r[i, j, ixmom] / rho_r
946+
947+
rhoe_r = U_r[i, j, iener] - 0.5 * rho_r * (un_r**2 + ut_r**2)
948+
949+
p_r = rhoe_r * (gamma - 1.0)
950+
p_r = max(p_r, smallp)
951+
952+
# compute the sound speeds
953+
c_l = max(smallc, np.sqrt(gamma * p_l / rho_l))
954+
c_r = max(smallc, np.sqrt(gamma * p_r / rho_r))
955+
956+
S_l, S_r = estimate_wave_speed(rho_l, un_l, p_l, c_l,
957+
rho_r, un_r, p_r, c_r, gamma)
958+
959+
# We could just take S_c = u_star as the estimate for the
960+
# contact speed, but we can actually do this more accurately
961+
# by using the Rankine-Hugonoit jump conditions across each
962+
# of the waves (see Toro 2009 Eq, 10.37, Batten et al. SIAM
963+
# J. Sci. and Stat. Comp., 18:1553 (1997)
964+
965+
S_c = (p_r - p_l +
966+
rho_l * un_l * (S_l - un_l) -
967+
rho_r * un_r * (S_r - un_r)) / \
968+
(rho_l * (S_l - un_l) - rho_r * (S_r - un_r))
969+
970+
# D* is used to control the pressure addition to the star flux
971+
D_star[iun] = 1.0
972+
D_star[iener] = S_c
973+
974+
# compute the fluxes corresponding to the left and right states
975+
976+
U_state_l = U_l[i, j, :]
977+
F_l = consFlux(idir, coord_type, gamma,
978+
idens, ixmom, iymom, iener, irhoX, nspec,
979+
U_state_l)
980+
981+
U_state_r = U_r[i, j, :]
982+
F_r = consFlux(idir, coord_type, gamma,
983+
idens, ixmom, iymom, iener, irhoX, nspec,
984+
U_state_r)
985+
986+
# compute the star pressure with the low Mach correction
987+
# Minoshima & Miyoshi (2021) Eq. 23. This is actually averaging
988+
# the left and right pressures (see also Toro 2009 Eq. 10.42)
989+
990+
vmag_l = np.sqrt(un_l**2 + ut_l**2)
991+
vmag_r = np.sqrt(un_r**2 + ut_r**2)
992+
993+
cs_max = max(c_l, c_r)
994+
chi = min(1.0, max(vmag_l, vmag_r) / cs_max)
995+
phi = chi * (2.0 - chi)
996+
pstar_lr = 0.5 * (p_l + p_r) + \
997+
0.5 * phi * (rho_l * (S_l - un_l) * (S_c - un_l) +
998+
rho_r * (S_r - un_r) * (S_c - un_r))
999+
1000+
# figure out which region we are in and compute the state and
1001+
# the interface fluxes using the HLLC Riemann solver
1002+
if S_r <= 0.0:
1003+
# R region
1004+
F[i, j, :] = F_r[:]
1005+
1006+
elif S_c <= 0.0 < S_r:
1007+
# R* region
1008+
F[i, j, :] = (S_c * (S_r * U_state_r - F_r) + S_r * pstar_lr * D_star) / (S_r - S_c)
1009+
1010+
elif S_l < 0.0 < S_c:
1011+
# L* region
1012+
F[i, j, :] = (S_c * (S_l * U_state_l - F_l) + S_l * pstar_lr * D_star) / (S_l - S_c)
1013+
1014+
else:
1015+
# L region
1016+
F[i, j, :] = F_l[:]
1017+
1018+
# we should deal with solid boundaries somehow here
1019+
1020+
return F
1021+
1022+
8661023
def riemann_flux(idir, U_l, U_r, my_data, rp, ivars,
8671024
lower_solid, upper_solid, tc, return_cons=False):
8681025
"""
@@ -907,7 +1064,9 @@ def riemann_flux(idir, U_l, U_r, my_data, rp, ivars,
9071064
riemann_method = rp.get_param("compressible.riemann")
9081065
gamma = rp.get_param("eos.gamma")
9091066

910-
riemann_solvers = {"HLLC": riemann_hllc, "CGF": riemann_cgf}
1067+
riemann_solvers = {"HLLC": riemann_hllc,
1068+
"HLLC_lm": riemann_hllc_lowspeed,
1069+
"CGF": riemann_cgf}
9111070

9121071
if riemann_method not in riemann_solvers:
9131072
msg.fail("ERROR: Riemann solver undefined")
@@ -923,7 +1082,7 @@ def riemann_flux(idir, U_l, U_r, my_data, rp, ivars,
9231082
gamma, U_l, U_r)
9241083

9251084
# If riemann_method is not HLLC, then construct flux using conserved states
926-
if riemann_method != "HLLC":
1085+
if riemann_method not in ["HLLC", "HLLC_lm"]:
9271086
_f = consFlux(idir, myg.coord_type, gamma,
9281087
ivars.idens, ivars.ixmom, ivars.iymom,
9291088
ivars.iener, ivars.irhox, ivars.naux,
@@ -935,7 +1094,7 @@ def riemann_flux(idir, U_l, U_r, my_data, rp, ivars,
9351094
F = ai.ArrayIndexer(d=_f, grid=myg)
9361095
tm_riem.end()
9371096

938-
if riemann_method != "HLLC" and return_cons:
1097+
if riemann_method not in ["HLLC", "HLLC_lm"] and return_cons:
9391098
U = ai.ArrayIndexer(d=_u, grid=myg)
9401099
return F, U
9411100

0 commit comments

Comments
 (0)