Path: blob/main/src/equations/compressible_euler_3d.jl
5586 views
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).1# Since these FMAs can increase the performance of many numerical algorithms,2# we need to opt-in explicitly.3# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.4@muladd begin5#! format: noindent67@doc raw"""8CompressibleEulerEquations3D(gamma)910The compressible Euler equations11```math12\frac{\partial}{\partial t}13\begin{pmatrix}14\rho \\ \rho v_1 \\ \rho v_2 \\ \rho v_3 \\ \rho e_{\text{total}}15\end{pmatrix}16+17\frac{\partial}{\partial x}18\begin{pmatrix}19\rho v_1 \\ \rho v_1^2 + p \\ \rho v_1 v_2 \\ \rho v_1 v_3 \\ ( \rho e_{\text{total}} +p) v_120\end{pmatrix}21+22\frac{\partial}{\partial y}23\begin{pmatrix}24\rho v_2 \\ \rho v_1 v_2 \\ \rho v_2^2 + p \\ \rho v_1 v_3 \\ ( \rho e_{\text{total}} +p) v_225\end{pmatrix}26+27\frac{\partial}{\partial z}28\begin{pmatrix}29\rho v_3 \\ \rho v_1 v_3 \\ \rho v_2 v_3 \\ \rho v_3^2 + p \\ ( \rho e_{\text{total}} +p) v_330\end{pmatrix}31=32\begin{pmatrix}330 \\ 0 \\ 0 \\ 0 \\ 034\end{pmatrix}35```36for an ideal gas with ratio of specific heats `gamma`37in three space dimensions.38Here, ``\rho`` is the density, ``v_1``, ``v_2``, ``v_3`` the velocities, ``e_{\text{total}}`` the specific total energy, and39```math40p = (\gamma - 1) \left( \rho e_{\text{total}} - \frac{1}{2} \rho (v_1^2+v_2^2+v_3^2) \right)41```42the pressure.43"""44struct CompressibleEulerEquations3D{RealT <: Real} <:45AbstractCompressibleEulerEquations{3, 5}46gamma::RealT # ratio of specific heats47inv_gamma_minus_one::RealT # = inv(gamma - 1); can be used to write slow divisions as fast multiplications4849function CompressibleEulerEquations3D(gamma)50γ, inv_gamma_minus_one = promote(gamma, inv(gamma - 1))51return new{typeof(γ)}(γ, inv_gamma_minus_one)52end53end5455function varnames(::typeof(cons2cons), ::CompressibleEulerEquations3D)56return ("rho", "rho_v1", "rho_v2", "rho_v3", "rho_e_total")57end58function varnames(::typeof(cons2prim), ::CompressibleEulerEquations3D)59return ("rho", "v1", "v2", "v3", "p")60end6162# Set initial conditions at physical location `x` for time `t`63"""64initial_condition_constant(x, t, equations::CompressibleEulerEquations3D)6566A constant initial condition to test free-stream preservation.67"""68function initial_condition_constant(x, t, equations::CompressibleEulerEquations3D)69RealT = eltype(x)70rho = 171rho_v1 = convert(RealT, 0.1)72rho_v2 = convert(RealT, -0.2)73rho_v3 = convert(RealT, 0.7)74rho_e_total = 1075return SVector(rho, rho_v1, rho_v2, rho_v3, rho_e_total)76end7778"""79initial_condition_convergence_test(x, t, equations::CompressibleEulerEquations3D)8081A smooth initial condition used for convergence tests in combination with82[`source_terms_convergence_test`](@ref).83"""84function initial_condition_convergence_test(x, t,85equations::CompressibleEulerEquations3D)86RealT = eltype(x)87c = 288A = convert(RealT, 0.1)89L = 290f = 1.0f0 / L91ω = 2 * convert(RealT, pi) * f92ini = c + A * sin(ω * (x[1] + x[2] + x[3] - t))9394rho = ini95rho_v1 = ini96rho_v2 = ini97rho_v3 = ini98rho_e_total = ini^299100return SVector(rho, rho_v1, rho_v2, rho_v3, rho_e_total)101end102103"""104source_terms_convergence_test(u, x, t, equations::CompressibleEulerEquations3D)105106Source terms used for convergence tests in combination with107[`initial_condition_convergence_test`](@ref).108109References for the method of manufactured solutions (MMS):110- Kambiz Salari and Patrick Knupp (2000)111Code Verification by the Method of Manufactured Solutions112[DOI: 10.2172/759450](https://doi.org/10.2172/759450)113- Patrick J. Roache (2002)114Code Verification by the Method of Manufactured Solutions115[DOI: 10.1115/1.1436090](https://doi.org/10.1115/1.1436090)116"""117@inline function source_terms_convergence_test(u, x, t,118equations::CompressibleEulerEquations3D)119# Same settings as in `initial_condition`120RealT = eltype(u)121c = 2122A = convert(RealT, 0.1)123L = 2124f = 1.0f0 / L125ω = 2 * convert(RealT, pi) * f126γ = equations.gamma127128x1, x2, x3 = x129si, co = sincos(ω * (x1 + x2 + x3 - t))130rho = c + A * si131rho_x = ω * A * co132# Note that d/dt rho = -d/dx rho = -d/dy rho = - d/dz rho.133134tmp = (2 * rho - 1.5f0) * (γ - 1)135136du1 = 2 * rho_x137du2 = rho_x * (2 + tmp)138du3 = du2139du4 = du2140du5 = rho_x * (4 * rho + 3 * tmp)141142return SVector(du1, du2, du3, du4, du5)143end144145"""146initial_condition_weak_blast_wave(x, t, equations::CompressibleEulerEquations3D)147148A weak blast wave taken from149- Sebastian Hennemann, Gregor J. Gassner (2020)150A provably entropy stable subcell shock capturing approach for high order split form DG151[arXiv: 2008.12044](https://arxiv.org/abs/2008.12044)152"""153function initial_condition_weak_blast_wave(x, t,154equations::CompressibleEulerEquations3D)155# From Hennemann & Gassner JCP paper 2020 (Sec. 6.3)156# Set up spherical coordinates157RealT = eltype(x)158inicenter = (0, 0, 0)159x_norm = x[1] - inicenter[1]160y_norm = x[2] - inicenter[2]161z_norm = x[3] - inicenter[3]162r = sqrt(x_norm^2 + y_norm^2 + z_norm^2)163phi = atan(y_norm, x_norm)164theta = iszero(r) ? zero(RealT) : acos(z_norm / r)165166# Calculate primitive variables167rho = r > 0.5f0 ? one(RealT) : convert(RealT, 1.1691)168v1 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos(phi) * sin(theta)169v2 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * sin(phi) * sin(theta)170v3 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos(theta)171p = r > 0.5f0 ? one(RealT) : convert(RealT, 1.245)172173return prim2cons(SVector(rho, v1, v2, v3, p), equations)174end175176"""177initial_condition_eoc_test_coupled_euler_gravity(x, t, equations::CompressibleEulerEquations3D)178179Setup used for convergence tests of the Euler equations with self-gravity used in180- Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020)181A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics182[arXiv: 2008.10593](https://arxiv.org/abs/2008.10593)183in combination with [`source_terms_eoc_test_coupled_euler_gravity`](@ref)184or [`source_terms_eoc_test_euler`](@ref).185"""186function initial_condition_eoc_test_coupled_euler_gravity(x, t,187equations::CompressibleEulerEquations3D)188# OBS! this assumes that γ = 2 other manufactured source terms are incorrect189if equations.gamma != 2190error("adiabatic constant must be 2 for the coupling convergence test")191end192RealT = eltype(x)193c = 2194A = convert(RealT, 0.1)195ini = c + A * sinpi(x[1] + x[2] + x[3] - t)196G = 1 # gravitational constant197198rho = ini199v1 = 1200v2 = 1201v3 = 1202p = ini^2 * G * 2 / (3 * convert(RealT, pi)) # "3" is the number of spatial dimensions203204return prim2cons(SVector(rho, v1, v2, v3, p), equations)205end206207"""208source_terms_eoc_test_coupled_euler_gravity(u, x, t, equations::CompressibleEulerEquations3D)209210Setup used for convergence tests of the Euler equations with self-gravity used in211- Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020)212A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics213[arXiv: 2008.10593](https://arxiv.org/abs/2008.10593)214in combination with [`initial_condition_eoc_test_coupled_euler_gravity`](@ref).215"""216@inline function source_terms_eoc_test_coupled_euler_gravity(u, x, t,217equations::CompressibleEulerEquations3D)218# Same settings as in `initial_condition_eoc_test_coupled_euler_gravity`219RealT = eltype(u)220c = 2221A = convert(RealT, 0.1)222G = 1 # gravitational constant, must match coupling solver223C_grav = -4 * G / (3 * convert(RealT, pi)) # "3" is the number of spatial dimensions # 2D: -2.0*G/pi224225x1, x2, x3 = x226si, co = sincospi(x1 + x2 + x3 - t)227rhox = A * convert(RealT, pi) * co228rho = c + A * si229230# In "2 * rhox", the "2" is "number of spatial dimensions minus one"231du1 = 2 * rhox232du2 = 2 * rhox233du3 = 2 * rhox234du4 = 2 * rhox235du5 = 2 * rhox * (1.5f0 - C_grav * rho) # "3" in "3/2" is the number of spatial dimensions236237return SVector(du1, du2, du3, du4, du5)238end239240"""241source_terms_eoc_test_euler(u, x, t, equations::CompressibleEulerEquations3D)242243Setup used for convergence tests of the Euler equations with self-gravity used in244- Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020)245A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics246[arXiv: 2008.10593](https://arxiv.org/abs/2008.10593)247in combination with [`initial_condition_eoc_test_coupled_euler_gravity`](@ref).248249!!! note250This method is to be used for testing pure Euler simulations with analytic self-gravity.251If you intend to do coupled Euler-gravity simulations, you need to use252[`source_terms_eoc_test_coupled_euler_gravity`](@ref) instead.253"""254function source_terms_eoc_test_euler(u, x, t, equations::CompressibleEulerEquations3D)255# Same settings as in `initial_condition_eoc_test_coupled_euler_gravity`256RealT = eltype(u)257c = 2258A = convert(RealT, 0.1)259G = 1260C_grav = -4 * G / (3 * convert(RealT, pi)) # "3" is the number of spatial dimensions261262x1, x2, x3 = x263si, co = sincospi(x1 + x2 + x3 - t)264rhox = A * convert(RealT, pi) * co265rho = c + A * si266267du1 = rhox * 2268du2 = rhox * (2 - C_grav * rho)269du3 = rhox * (2 - C_grav * rho)270du4 = rhox * (2 - C_grav * rho)271du5 = rhox * (3 - 5 * C_grav * rho)272273return SVector(du1, du2, du3, du4, du5)274end275276"""277boundary_condition_slip_wall(u_inner, normal_direction, x, t, surface_flux_function,278equations::CompressibleEulerEquations3D)279280Determine the boundary numerical surface flux for a slip wall condition.281Imposes a zero normal velocity at the wall.282Density is taken from the internal solution state and pressure is computed as an283exact solution of a 1D Riemann problem. Further details about this boundary state284are available in the paper:285- J. J. W. van der Vegt and H. van der Ven (2002)286Slip flow boundary conditions in discontinuous Galerkin discretizations of287the Euler equations of gas dynamics288[PDF](https://reports.nlr.nl/bitstream/handle/10921/692/TP-2002-300.pdf?sequence=1)289290Details about the 1D pressure Riemann solution can be found in Section 6.3.3 of the book291- Eleuterio F. Toro (2009)292Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction2933rd edition294[DOI: 10.1007/b79761](https://doi.org/10.1007/b79761)295296Should be used together with [`P4estMesh`](@ref) or [`T8codeMesh`](@ref).297"""298@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector,299x, t,300surface_flux_function,301equations::CompressibleEulerEquations3D)302norm_ = norm(normal_direction)303# Normalize the vector without using `normalize` since we need to multiply by the `norm_` later304normal = normal_direction / norm_305306# Some vector that can't be identical to normal_vector (unless normal_vector == 0)307tangent1 = SVector(normal_direction[2], normal_direction[3], -normal_direction[1])308# Orthogonal projection309tangent1 -= dot(normal, tangent1) * normal310tangent1 = normalize(tangent1)311312# Third orthogonal vector313tangent2 = normalize(cross(normal_direction, tangent1))314315# rotate the internal solution state316u_local = rotate_to_x(u_inner, normal, tangent1, tangent2, equations)317318# compute the primitive variables319rho_local, v_normal, v_tangent1, v_tangent2, p_local = cons2prim(u_local, equations)320321# Get the solution of the pressure Riemann problem322# See Section 6.3.3 of323# Eleuterio F. Toro (2009)324# Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction325# [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761)326if v_normal <= 0327sound_speed = sqrt(equations.gamma * p_local / rho_local) # local sound speed328p_star = p_local *329(1 + 0.5f0 * (equations.gamma - 1) * v_normal / sound_speed)^(2 *330equations.gamma *331equations.inv_gamma_minus_one)332else # v_normal > 0333A = 2 / ((equations.gamma + 1) * rho_local)334B = p_local * (equations.gamma - 1) / (equations.gamma + 1)335p_star = p_local +3360.5f0 * v_normal / A *337(v_normal + sqrt(v_normal^2 + 4 * A * (p_local + B)))338end339340# For the slip wall we directly set the flux as the normal velocity is zero341return SVector(0,342p_star * normal[1],343p_star * normal[2],344p_star * normal[3],3450) * norm_346end347348"""349boundary_condition_slip_wall(u_inner, orientation, direction, x, t,350surface_flux_function, equations::CompressibleEulerEquations3D)351352Should be used together with [`TreeMesh`](@ref).353"""354@inline function boundary_condition_slip_wall(u_inner, orientation,355direction, x, t,356surface_flux_function,357equations::CompressibleEulerEquations3D)358# get the appropriate normal vector from the orientation359RealT = eltype(u_inner)360if orientation == 1361normal_direction = SVector(one(RealT), zero(RealT), zero(RealT))362elseif orientation == 2363normal_direction = SVector(zero(RealT), one(RealT), zero(RealT))364else # orientation == 3365normal_direction = SVector(zero(RealT), zero(RealT), one(RealT))366end367368# compute and return the flux using `boundary_condition_slip_wall` routine below369return boundary_condition_slip_wall(u_inner, normal_direction, direction,370x, t, surface_flux_function, equations)371end372373"""374boundary_condition_slip_wall(u_inner, normal_direction, direction, x, t,375surface_flux_function, equations::CompressibleEulerEquations3D)376377Should be used together with [`StructuredMesh`](@ref).378"""379@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector,380direction, x, t,381surface_flux_function,382equations::CompressibleEulerEquations3D)383# flip sign of normal to make it outward pointing, then flip the sign of the normal flux back384# to be inward pointing on the -x, -y, and -z sides due to the orientation convention used by StructuredMesh385if isodd(direction)386boundary_flux = -boundary_condition_slip_wall(u_inner, -normal_direction,387x, t, surface_flux_function,388equations)389else390boundary_flux = boundary_condition_slip_wall(u_inner, normal_direction,391x, t, surface_flux_function,392equations)393end394395return boundary_flux396end397398# Calculate 1D flux for a single point399@inline function flux(u, orientation::Integer, equations::CompressibleEulerEquations3D)400rho, rho_v1, rho_v2, rho_v3, rho_e_total = u401v1 = rho_v1 / rho402v2 = rho_v2 / rho403v3 = rho_v3 / rho404p = (equations.gamma - 1) *405(rho_e_total - 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3))406if orientation == 1407f1 = rho_v1408f2 = rho_v1 * v1 + p409f3 = rho_v1 * v2410f4 = rho_v1 * v3411f5 = (rho_e_total + p) * v1412elseif orientation == 2413f1 = rho_v2414f2 = rho_v2 * v1415f3 = rho_v2 * v2 + p416f4 = rho_v2 * v3417f5 = (rho_e_total + p) * v2418else419f1 = rho_v3420f2 = rho_v3 * v1421f3 = rho_v3 * v2422f4 = rho_v3 * v3 + p423f5 = (rho_e_total + p) * v3424end425return SVector(f1, f2, f3, f4, f5)426end427428@inline function flux(u, normal_direction::AbstractVector,429equations::CompressibleEulerEquations3D)430rho_e_total = last(u)431rho, v1, v2, v3, p = cons2prim(u, equations)432433v_normal = v1 * normal_direction[1] + v2 * normal_direction[2] +434v3 * normal_direction[3]435rho_v_normal = rho * v_normal436f1 = rho_v_normal437f2 = rho_v_normal * v1 + p * normal_direction[1]438f3 = rho_v_normal * v2 + p * normal_direction[2]439f4 = rho_v_normal * v3 + p * normal_direction[3]440f5 = (rho_e_total + p) * v_normal441return SVector(f1, f2, f3, f4, f5)442end443444"""445flux_shima_etal(u_ll, u_rr, orientation_or_normal_direction,446equations::CompressibleEulerEquations3D)447448This flux is is a modification of the original kinetic energy preserving two-point flux by449- Yuichi Kuya, Kosuke Totani and Soshi Kawai (2018)450Kinetic energy and entropy preserving schemes for compressible flows451by split convective forms452[DOI: 10.1016/j.jcp.2018.08.058](https://doi.org/10.1016/j.jcp.2018.08.058)453454The modification is in the energy flux to guarantee pressure equilibrium and was developed by455- Nao Shima, Yuichi Kuya, Yoshiharu Tamaki, Soshi Kawai (JCP 2020)456Preventing spurious pressure oscillations in split convective form discretizations for457compressible flows458[DOI: 10.1016/j.jcp.2020.110060](https://doi.org/10.1016/j.jcp.2020.110060)459"""460@inline function flux_shima_etal(u_ll, u_rr, orientation::Integer,461equations::CompressibleEulerEquations3D)462# Unpack left and right state463rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)464rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)465466# Average each factor of products in flux467rho_avg = 0.5f0 * (rho_ll + rho_rr)468v1_avg = 0.5f0 * (v1_ll + v1_rr)469v2_avg = 0.5f0 * (v2_ll + v2_rr)470v3_avg = 0.5f0 * (v3_ll + v3_rr)471p_avg = 0.5f0 * (p_ll + p_rr)472kin_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr)473474# Calculate fluxes depending on orientation475if orientation == 1476pv1_avg = 0.5f0 * (p_ll * v1_rr + p_rr * v1_ll)477f1 = rho_avg * v1_avg478f2 = f1 * v1_avg + p_avg479f3 = f1 * v2_avg480f4 = f1 * v3_avg481f5 = p_avg * v1_avg * equations.inv_gamma_minus_one + f1 * kin_avg + pv1_avg482elseif orientation == 2483pv2_avg = 0.5f0 * (p_ll * v2_rr + p_rr * v2_ll)484f1 = rho_avg * v2_avg485f2 = f1 * v1_avg486f3 = f1 * v2_avg + p_avg487f4 = f1 * v3_avg488f5 = p_avg * v2_avg * equations.inv_gamma_minus_one + f1 * kin_avg + pv2_avg489else490pv3_avg = 0.5f0 * (p_ll * v3_rr + p_rr * v3_ll)491f1 = rho_avg * v3_avg492f2 = f1 * v1_avg493f3 = f1 * v2_avg494f4 = f1 * v3_avg + p_avg495f5 = p_avg * v3_avg * equations.inv_gamma_minus_one + f1 * kin_avg + pv3_avg496end497498return SVector(f1, f2, f3, f4, f5)499end500501@inline function flux_shima_etal(u_ll, u_rr, normal_direction::AbstractVector,502equations::CompressibleEulerEquations3D)503# Unpack left and right state504rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)505rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)506v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +507v3_ll * normal_direction[3]508v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +509v3_rr * normal_direction[3]510511# Average each factor of products in flux512rho_avg = 0.5f0 * (rho_ll + rho_rr)513v1_avg = 0.5f0 * (v1_ll + v1_rr)514v2_avg = 0.5f0 * (v2_ll + v2_rr)515v3_avg = 0.5f0 * (v3_ll + v3_rr)516v_dot_n_avg = 0.5f0 * (v_dot_n_ll + v_dot_n_rr)517p_avg = 0.5f0 * (p_ll + p_rr)518velocity_square_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr)519520# Calculate fluxes depending on normal_direction521f1 = rho_avg * v_dot_n_avg522f2 = f1 * v1_avg + p_avg * normal_direction[1]523f3 = f1 * v2_avg + p_avg * normal_direction[2]524f4 = f1 * v3_avg + p_avg * normal_direction[3]525f5 = (f1 * velocity_square_avg +526p_avg * v_dot_n_avg * equations.inv_gamma_minus_one527+ 0.5f0 * (p_ll * v_dot_n_rr + p_rr * v_dot_n_ll))528529return SVector(f1, f2, f3, f4, f5)530end531532"""533flux_kennedy_gruber(u_ll, u_rr, orientation_or_normal_direction,534equations::CompressibleEulerEquations3D)535536Kinetic energy preserving two-point flux by537- Kennedy and Gruber (2008)538Reduced aliasing formulations of the convective terms within the539Navier-Stokes equations for a compressible fluid540[DOI: 10.1016/j.jcp.2007.09.020](https://doi.org/10.1016/j.jcp.2007.09.020)541"""542@inline function flux_kennedy_gruber(u_ll, u_rr, orientation::Integer,543equations::CompressibleEulerEquations3D)544# Unpack left and right state545rho_e_total_ll = last(u_ll)546rho_e_total_rr = last(u_rr)547rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)548rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)549550# Average each factor of products in flux551rho_avg = 0.5f0 * (rho_ll + rho_rr)552v1_avg = 0.5f0 * (v1_ll + v1_rr)553v2_avg = 0.5f0 * (v2_ll + v2_rr)554v3_avg = 0.5f0 * (v3_ll + v3_rr)555p_avg = 0.5f0 * (p_ll + p_rr)556e_avg = 0.5f0 * (rho_e_total_ll / rho_ll + rho_e_total_rr / rho_rr)557558# Calculate fluxes depending on orientation559if orientation == 1560f1 = rho_avg * v1_avg561f2 = f1 * v1_avg + p_avg562f3 = f1 * v2_avg563f4 = f1 * v3_avg564f5 = (rho_avg * e_avg + p_avg) * v1_avg565elseif orientation == 2566f1 = rho_avg * v2_avg567f2 = f1 * v1_avg568f3 = f1 * v2_avg + p_avg569f4 = f1 * v3_avg570f5 = (rho_avg * e_avg + p_avg) * v2_avg571else572f1 = rho_avg * v3_avg573f2 = f1 * v1_avg574f3 = f1 * v2_avg575f4 = f1 * v3_avg + p_avg576f5 = (rho_avg * e_avg + p_avg) * v3_avg577end578579return SVector(f1, f2, f3, f4, f5)580end581582@inline function flux_kennedy_gruber(u_ll, u_rr, normal_direction::AbstractVector,583equations::CompressibleEulerEquations3D)584# Unpack left and right state585rho_e_total_ll = last(u_ll)586rho_e_total_rr = last(u_rr)587rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)588rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)589590# Average each factor of products in flux591rho_avg = 0.5f0 * (rho_ll + rho_rr)592v1_avg = 0.5f0 * (v1_ll + v1_rr)593v2_avg = 0.5f0 * (v2_ll + v2_rr)594v3_avg = 0.5f0 * (v3_ll + v3_rr)595p_avg = 0.5f0 * (p_ll + p_rr)596e_avg = 0.5f0 * (rho_e_total_ll / rho_ll + rho_e_total_rr / rho_rr)597598v_dot_n_avg = v1_avg * normal_direction[1] + v2_avg * normal_direction[2] +599v3_avg * normal_direction[3]600p_avg = 0.5f0 * (p_ll + p_rr)601e_avg = 0.5f0 * (rho_e_total_ll / rho_ll + rho_e_total_rr / rho_rr)602603# Calculate fluxes depending on normal_direction604f1 = rho_avg * v_dot_n_avg605f2 = f1 * v1_avg + p_avg * normal_direction[1]606f3 = f1 * v2_avg + p_avg * normal_direction[2]607f4 = f1 * v3_avg + p_avg * normal_direction[3]608f5 = f1 * e_avg + p_avg * v_dot_n_avg609610return SVector(f1, f2, f3, f4, f5)611end612613"""614flux_chandrashekar(u_ll, u_rr, orientation_or_normal_direction, equations::CompressibleEulerEquations3D)615616Entropy conserving two-point flux by617- Chandrashekar (2013)618Kinetic Energy Preserving and Entropy Stable Finite Volume Schemes619for Compressible Euler and Navier-Stokes Equations620[DOI: 10.4208/cicp.170712.010313a](https://doi.org/10.4208/cicp.170712.010313a)621"""622@inline function flux_chandrashekar(u_ll, u_rr, orientation::Integer,623equations::CompressibleEulerEquations3D)624# Unpack left and right state625rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)626rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)627628beta_ll = 0.5f0 * rho_ll / p_ll629beta_rr = 0.5f0 * rho_rr / p_rr630specific_kin_ll = 0.5f0 * (v1_ll^2 + v2_ll^2 + v3_ll^2)631specific_kin_rr = 0.5f0 * (v1_rr^2 + v2_rr^2 + v3_rr^2)632633# Compute the necessary mean values634rho_avg = 0.5f0 * (rho_ll + rho_rr)635rho_mean = ln_mean(rho_ll, rho_rr)636beta_mean = ln_mean(beta_ll, beta_rr)637beta_avg = 0.5f0 * (beta_ll + beta_rr)638v1_avg = 0.5f0 * (v1_ll + v1_rr)639v2_avg = 0.5f0 * (v2_ll + v2_rr)640v3_avg = 0.5f0 * (v3_ll + v3_rr)641p_mean = 0.5f0 * rho_avg / beta_avg642velocity_square_avg = specific_kin_ll + specific_kin_rr643644# Calculate fluxes depending on orientation645if orientation == 1646f1 = rho_mean * v1_avg647f2 = f1 * v1_avg + p_mean648f3 = f1 * v2_avg649f4 = f1 * v3_avg650f5 = f1 * 0.5f0 *651(1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) +652f2 * v1_avg + f3 * v2_avg + f4 * v3_avg653elseif orientation == 2654f1 = rho_mean * v2_avg655f2 = f1 * v1_avg656f3 = f1 * v2_avg + p_mean657f4 = f1 * v3_avg658f5 = f1 * 0.5f0 *659(1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) +660f2 * v1_avg + f3 * v2_avg + f4 * v3_avg661else662f1 = rho_mean * v3_avg663f2 = f1 * v1_avg664f3 = f1 * v2_avg665f4 = f1 * v3_avg + p_mean666f5 = f1 * 0.5f0 *667(1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) +668f2 * v1_avg + f3 * v2_avg + f4 * v3_avg669end670671return SVector(f1, f2, f3, f4, f5)672end673674@inline function flux_chandrashekar(u_ll, u_rr, normal_direction::AbstractVector,675equations::CompressibleEulerEquations3D)676# Unpack left and right state677rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)678rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)679680v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +681v3_ll * normal_direction[3]682v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +683v3_rr * normal_direction[3]684685beta_ll = 0.5f0 * rho_ll / p_ll686beta_rr = 0.5f0 * rho_rr / p_rr687specific_kin_ll = 0.5f0 * (v1_ll^2 + v2_ll^2 + v3_ll^2)688specific_kin_rr = 0.5f0 * (v1_rr^2 + v2_rr^2 + v3_rr^2)689690# Compute the necessary mean values691rho_avg = 0.5f0 * (rho_ll + rho_rr)692rho_mean = ln_mean(rho_ll, rho_rr)693beta_mean = ln_mean(beta_ll, beta_rr)694beta_avg = 0.5f0 * (beta_ll + beta_rr)695v1_avg = 0.5f0 * (v1_ll + v1_rr)696v2_avg = 0.5f0 * (v2_ll + v2_rr)697v3_avg = 0.5f0 * (v3_ll + v3_rr)698p_mean = 0.5f0 * rho_avg / beta_avg699velocity_square_avg = specific_kin_ll + specific_kin_rr700701# Multiply with average of normal velocities702f1 = rho_mean * 0.5f0 * (v_dot_n_ll + v_dot_n_rr)703f2 = f1 * v1_avg + p_mean * normal_direction[1]704f3 = f1 * v2_avg + p_mean * normal_direction[2]705f4 = f1 * v3_avg + p_mean * normal_direction[3]706f5 = f1 * 0.5f0 * (1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) +707f2 * v1_avg + f3 * v2_avg + f4 * v3_avg708709return SVector(f1, f2, f3, f4, f5)710end711712"""713flux_ranocha(u_ll, u_rr, orientation_or_normal_direction,714equations::CompressibleEulerEquations3D)715716Entropy conserving and kinetic energy preserving two-point flux by717- Hendrik Ranocha (2018)718Generalised Summation-by-Parts Operators and Entropy Stability of Numerical Methods719for Hyperbolic Balance Laws720[PhD thesis, TU Braunschweig](https://cuvillier.de/en/shop/publications/7743)721See also722- Hendrik Ranocha (2020)723Entropy Conserving and Kinetic Energy Preserving Numerical Methods for724the Euler Equations Using Summation-by-Parts Operators725[Proceedings of ICOSAHOM 2018](https://doi.org/10.1007/978-3-030-39647-3_42)726"""727@inline function flux_ranocha(u_ll, u_rr, orientation::Integer,728equations::CompressibleEulerEquations3D)729# Unpack left and right state730rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)731rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)732733# Compute the necessary mean values734rho_mean = ln_mean(rho_ll, rho_rr)735# Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)`736# in exact arithmetic since737# log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ)738# = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ)739inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll)740v1_avg = 0.5f0 * (v1_ll + v1_rr)741v2_avg = 0.5f0 * (v2_ll + v2_rr)742v3_avg = 0.5f0 * (v3_ll + v3_rr)743p_avg = 0.5f0 * (p_ll + p_rr)744velocity_square_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr)745746# Calculate fluxes depending on orientation747if orientation == 1748f1 = rho_mean * v1_avg749f2 = f1 * v1_avg + p_avg750f3 = f1 * v2_avg751f4 = f1 * v3_avg752f5 = f1 *753(velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) +7540.5f0 * (p_ll * v1_rr + p_rr * v1_ll)755elseif orientation == 2756f1 = rho_mean * v2_avg757f2 = f1 * v1_avg758f3 = f1 * v2_avg + p_avg759f4 = f1 * v3_avg760f5 = f1 *761(velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) +7620.5f0 * (p_ll * v2_rr + p_rr * v2_ll)763else # orientation == 3764f1 = rho_mean * v3_avg765f2 = f1 * v1_avg766f3 = f1 * v2_avg767f4 = f1 * v3_avg + p_avg768f5 = f1 *769(velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) +7700.5f0 * (p_ll * v3_rr + p_rr * v3_ll)771end772773return SVector(f1, f2, f3, f4, f5)774end775776@inline function flux_ranocha(u_ll, u_rr, normal_direction::AbstractVector,777equations::CompressibleEulerEquations3D)778# Unpack left and right state779rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)780rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)781v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +782v3_ll * normal_direction[3]783v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +784v3_rr * normal_direction[3]785786# Compute the necessary mean values787rho_mean = ln_mean(rho_ll, rho_rr)788# Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)`789# in exact arithmetic since790# log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ)791# = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ)792inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll)793v1_avg = 0.5f0 * (v1_ll + v1_rr)794v2_avg = 0.5f0 * (v2_ll + v2_rr)795v3_avg = 0.5f0 * (v3_ll + v3_rr)796p_avg = 0.5f0 * (p_ll + p_rr)797velocity_square_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr)798799# Calculate fluxes depending on normal_direction800f1 = rho_mean * 0.5f0 * (v_dot_n_ll + v_dot_n_rr)801f2 = f1 * v1_avg + p_avg * normal_direction[1]802f3 = f1 * v2_avg + p_avg * normal_direction[2]803f4 = f1 * v3_avg + p_avg * normal_direction[3]804f5 = (f1 * (velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one)805+8060.5f0 * (p_ll * v_dot_n_rr + p_rr * v_dot_n_ll))807808return SVector(f1, f2, f3, f4, f5)809end810811"""812splitting_steger_warming(u, orientation::Integer,813equations::CompressibleEulerEquations3D)814splitting_steger_warming(u, which::Union{Val{:minus}, Val{:plus}}815orientation::Integer,816equations::CompressibleEulerEquations3D)817818Splitting of the compressible Euler flux of Steger and Warming.819820Returns a tuple of the fluxes "minus" (associated with waves going into the821negative axis direction) and "plus" (associated with waves going into the822positive axis direction). If only one of the fluxes is required, use the823function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`.824825!!! warning "Experimental implementation (upwind SBP)"826This is an experimental feature and may change in future releases.827828## References829830- Joseph L. Steger and R. F. Warming (1979)831Flux Vector Splitting of the Inviscid Gasdynamic Equations832With Application to Finite Difference Methods833[NASA Technical Memorandum](https://ntrs.nasa.gov/api/citations/19790020779/downloads/19790020779.pdf)834"""835@inline function splitting_steger_warming(u, orientation::Integer,836equations::CompressibleEulerEquations3D)837fm = splitting_steger_warming(u, Val{:minus}(), orientation, equations)838fp = splitting_steger_warming(u, Val{:plus}(), orientation, equations)839return fm, fp840end841842@inline function splitting_steger_warming(u, ::Val{:plus}, orientation::Integer,843equations::CompressibleEulerEquations3D)844rho, rho_v1, rho_v2, rho_v3, rho_e_total = u845v1 = rho_v1 / rho846v2 = rho_v2 / rho847v3 = rho_v3 / rho848p = (equations.gamma - 1) *849(rho_e_total - 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3))850a = sqrt(equations.gamma * p / rho)851852if orientation == 1853lambda1 = v1854lambda2 = v1 + a855lambda3 = v1 - a856857lambda1_p = positive_part(lambda1) # Same as (lambda_i + abs(lambda_i)) / 2, but faster :)858lambda2_p = positive_part(lambda2)859lambda3_p = positive_part(lambda3)860861alpha_p = 2 * (equations.gamma - 1) * lambda1_p + lambda2_p + lambda3_p862863rho_2gamma = 0.5f0 * rho / equations.gamma864f1p = rho_2gamma * alpha_p865f2p = rho_2gamma * (alpha_p * v1 + a * (lambda2_p - lambda3_p))866f3p = rho_2gamma * alpha_p * v2867f4p = rho_2gamma * alpha_p * v3868f5p = rho_2gamma *869(alpha_p * 0.5f0 * (v1^2 + v2^2 + v3^2) +870a * v1 *871(lambda2_p - lambda3_p)872+ a^2 * (lambda2_p + lambda3_p) * equations.inv_gamma_minus_one)873elseif orientation == 2874lambda1 = v2875lambda2 = v2 + a876lambda3 = v2 - a877878lambda1_p = positive_part(lambda1) # Same as (lambda_i + abs(lambda_i)) / 2, but faster :)879lambda2_p = positive_part(lambda2)880lambda3_p = positive_part(lambda3)881882alpha_p = 2 * (equations.gamma - 1) * lambda1_p + lambda2_p + lambda3_p883884rho_2gamma = 0.5f0 * rho / equations.gamma885f1p = rho_2gamma * alpha_p886f2p = rho_2gamma * alpha_p * v1887f3p = rho_2gamma * (alpha_p * v2 + a * (lambda2_p - lambda3_p))888f4p = rho_2gamma * alpha_p * v3889f5p = rho_2gamma *890(alpha_p * 0.5f0 * (v1^2 + v2^2 + v3^2) +891a * v2 *892(lambda2_p - lambda3_p)893+ a^2 * (lambda2_p + lambda3_p) * equations.inv_gamma_minus_one)894else # orientation == 3895lambda1 = v3896lambda2 = v3 + a897lambda3 = v3 - a898899lambda1_p = positive_part(lambda1) # Same as (lambda_i + abs(lambda_i)) / 2, but faster :)900lambda2_p = positive_part(lambda2)901lambda3_p = positive_part(lambda3)902903alpha_p = 2 * (equations.gamma - 1) * lambda1_p + lambda2_p + lambda3_p904905rho_2gamma = 0.5f0 * rho / equations.gamma906f1p = rho_2gamma * alpha_p907f2p = rho_2gamma * alpha_p * v1908f3p = rho_2gamma * alpha_p * v2909f4p = rho_2gamma * (alpha_p * v3 + a * (lambda2_p - lambda3_p))910f5p = rho_2gamma *911(alpha_p * 0.5f0 * (v1^2 + v2^2 + v3^2) +912a * v3 *913(lambda2_p - lambda3_p)914+ a^2 * (lambda2_p + lambda3_p) * equations.inv_gamma_minus_one)915end916return SVector(f1p, f2p, f3p, f4p, f5p)917end918919@inline function splitting_steger_warming(u, ::Val{:minus}, orientation::Integer,920equations::CompressibleEulerEquations3D)921rho, rho_v1, rho_v2, rho_v3, rho_e_total = u922v1 = rho_v1 / rho923v2 = rho_v2 / rho924v3 = rho_v3 / rho925p = (equations.gamma - 1) *926(rho_e_total - 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3))927a = sqrt(equations.gamma * p / rho)928929if orientation == 1930lambda1 = v1931lambda2 = v1 + a932lambda3 = v1 - a933934lambda1_m = negative_part(lambda1) # Same as (lambda_i - abs(lambda_i)) / 2, but faster :)935lambda2_m = negative_part(lambda2)936lambda3_m = negative_part(lambda3)937938alpha_m = 2 * (equations.gamma - 1) * lambda1_m + lambda2_m + lambda3_m939940rho_2gamma = 0.5f0 * rho / equations.gamma941f1m = rho_2gamma * alpha_m942f2m = rho_2gamma * (alpha_m * v1 + a * (lambda2_m - lambda3_m))943f3m = rho_2gamma * alpha_m * v2944f4m = rho_2gamma * alpha_m * v3945f5m = rho_2gamma *946(alpha_m * 0.5f0 * (v1^2 + v2^2 + v3^2) +947a * v1 *948(lambda2_m - lambda3_m)949+ a^2 * (lambda2_m + lambda3_m) * equations.inv_gamma_minus_one)950elseif orientation == 2951lambda1 = v2952lambda2 = v2 + a953lambda3 = v2 - a954955lambda1_m = negative_part(lambda1) # Same as (lambda_i - abs(lambda_i)) / 2, but faster :)956lambda2_m = negative_part(lambda2)957lambda3_m = negative_part(lambda3)958959alpha_m = 2 * (equations.gamma - 1) * lambda1_m + lambda2_m + lambda3_m960961rho_2gamma = 0.5f0 * rho / equations.gamma962f1m = rho_2gamma * alpha_m963f2m = rho_2gamma * alpha_m * v1964f3m = rho_2gamma * (alpha_m * v2 + a * (lambda2_m - lambda3_m))965f4m = rho_2gamma * alpha_m * v3966f5m = rho_2gamma *967(alpha_m * 0.5f0 * (v1^2 + v2^2 + v3^2) +968a * v2 *969(lambda2_m - lambda3_m)970+ a^2 * (lambda2_m + lambda3_m) * equations.inv_gamma_minus_one)971else # orientation == 3972lambda1 = v3973lambda2 = v3 + a974lambda3 = v3 - a975976lambda1_m = negative_part(lambda1) # Same as (lambda_i - abs(lambda_i)) / 2, but faster :)977lambda2_m = negative_part(lambda2)978lambda3_m = negative_part(lambda3)979980alpha_m = 2 * (equations.gamma - 1) * lambda1_m + lambda2_m + lambda3_m981982rho_2gamma = 0.5f0 * rho / equations.gamma983f1m = rho_2gamma * alpha_m984f2m = rho_2gamma * alpha_m * v1985f3m = rho_2gamma * alpha_m * v2986f4m = rho_2gamma * (alpha_m * v3 + a * (lambda2_m - lambda3_m))987f5m = rho_2gamma *988(alpha_m * 0.5f0 * (v1^2 + v2^2 + v3^2) +989a * v3 *990(lambda2_m - lambda3_m)991+ a^2 * (lambda2_m + lambda3_m) * equations.inv_gamma_minus_one)992end993return SVector(f1m, f2m, f3m, f4m, f5m)994end995996"""997FluxLMARS(c)(u_ll, u_rr, orientation_or_normal_direction,998equations::CompressibleEulerEquations3D)9991000Low Mach number approximate Riemann solver (LMARS) for atmospheric flows using1001an estimate `c` of the speed of sound.10021003References:1004- Xi Chen et al. (2013)1005A Control-Volume Model of the Compressible Euler Equations with a Vertical1006Lagrangian Coordinate1007[DOI: 10.1175/MWR-D-12-00129.1](https://doi.org/10.1175/mwr-d-12-00129.1)1008"""1009@inline function (flux_lmars::FluxLMARS)(u_ll, u_rr, orientation::Integer,1010equations::CompressibleEulerEquations3D)1011c = flux_lmars.speed_of_sound10121013# Unpack left and right state1014rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1015rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)10161017if orientation == 11018v_ll = v1_ll1019v_rr = v1_rr1020elseif orientation == 21021v_ll = v2_ll1022v_rr = v2_rr1023else # orientation == 31024v_ll = v3_ll1025v_rr = v3_rr1026end10271028rho = 0.5f0 * (rho_ll + rho_rr)1029p = 0.5f0 * (p_ll + p_rr) - 0.5f0 * c * rho * (v_rr - v_ll)1030v = 0.5f0 * (v_ll + v_rr) - 1 / (2 * c * rho) * (p_rr - p_ll)10311032# We treat the energy term analogous to the potential temperature term in the paper by1033# Chen et al., i.e. we use p_ll and p_rr, and not p1034if v >= 01035f1, f2, f3, f4, f5 = v * u_ll1036f5 = f5 + p_ll * v1037else1038f1, f2, f3, f4, f5 = v * u_rr1039f5 = f5 + p_rr * v1040end10411042if orientation == 11043f2 += p1044elseif orientation == 21045f3 += p1046else # orientation == 31047f4 += p1048end10491050return SVector(f1, f2, f3, f4, f5)1051end10521053@inline function (flux_lmars::FluxLMARS)(u_ll, u_rr, normal_direction::AbstractVector,1054equations::CompressibleEulerEquations3D)1055c = flux_lmars.speed_of_sound10561057# Unpack left and right state1058rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1059rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)10601061v_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +1062v3_ll * normal_direction[3]1063v_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +1064v3_rr * normal_direction[3]10651066# Note that this is the same as computing v_ll and v_rr with a normalized normal vector1067# and then multiplying v by `norm_` again, but this version is slightly faster.1068norm_ = norm(normal_direction)10691070rho = 0.5f0 * (rho_ll + rho_rr)1071p = 0.5f0 * (p_ll + p_rr) - 0.5f0 * c * rho * (v_rr - v_ll) / norm_1072v = 0.5f0 * (v_ll + v_rr) - 1 / (2 * c * rho) * (p_rr - p_ll) * norm_10731074# We treat the energy term analogous to the potential temperature term in the paper by1075# Chen et al., i.e. we use p_ll and p_rr, and not p1076if v >= 01077f1, f2, f3, f4, f5 = v * u_ll1078f5 = f5 + p_ll * v1079else1080f1, f2, f3, f4, f5 = v * u_rr1081f5 = f5 + p_rr * v1082end10831084return SVector(f1,1085f2 + p * normal_direction[1],1086f3 + p * normal_direction[2],1087f4 + p * normal_direction[3],1088f5)1089end10901091# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation as the1092# maximum velocity magnitude plus the maximum speed of sound1093@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,1094equations::CompressibleEulerEquations3D)1095rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1096rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)10971098# Get the velocity value in the appropriate direction1099if orientation == 11100v_ll = v1_ll1101v_rr = v1_rr1102elseif orientation == 21103v_ll = v2_ll1104v_rr = v2_rr1105else # orientation == 31106v_ll = v3_ll1107v_rr = v3_rr1108end1109# Calculate sound speeds1110c_ll = sqrt(equations.gamma * p_ll / rho_ll)1111c_rr = sqrt(equations.gamma * p_rr / rho_rr)11121113return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr)1114end11151116@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,1117equations::CompressibleEulerEquations3D)1118rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1119rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)11201121# Calculate normal velocities and sound speed1122# left1123v_ll = (v1_ll * normal_direction[1]1124+ v2_ll * normal_direction[2]1125+ v3_ll * normal_direction[3])1126c_ll = sqrt(equations.gamma * p_ll / rho_ll)1127# right1128v_rr = (v1_rr * normal_direction[1]1129+ v2_rr * normal_direction[2]1130+ v3_rr * normal_direction[3])1131c_rr = sqrt(equations.gamma * p_rr / rho_rr)11321133return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) * norm(normal_direction)1134end11351136# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`1137@inline function max_abs_speed(u_ll, u_rr, orientation::Integer,1138equations::CompressibleEulerEquations3D)1139rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1140rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)11411142# Get the velocity value in the appropriate direction1143if orientation == 11144v_ll = v1_ll1145v_rr = v1_rr1146elseif orientation == 21147v_ll = v2_ll1148v_rr = v2_rr1149else # orientation == 31150v_ll = v3_ll1151v_rr = v3_rr1152end1153# Calculate sound speeds1154c_ll = sqrt(equations.gamma * p_ll / rho_ll)1155c_rr = sqrt(equations.gamma * p_rr / rho_rr)11561157return max(abs(v_ll) + c_ll, abs(v_rr) + c_rr)1158end11591160# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`1161@inline function max_abs_speed(u_ll, u_rr, normal_direction::AbstractVector,1162equations::CompressibleEulerEquations3D)1163rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1164rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)11651166# Calculate normal velocities and sound speeds1167# left1168v_ll = (v1_ll * normal_direction[1]1169+ v2_ll * normal_direction[2]1170+ v3_ll * normal_direction[3])1171c_ll = sqrt(equations.gamma * p_ll / rho_ll)1172# right1173v_rr = (v1_rr * normal_direction[1]1174+ v2_rr * normal_direction[2]1175+ v3_rr * normal_direction[3])1176c_rr = sqrt(equations.gamma * p_rr / rho_rr)11771178norm_ = norm(normal_direction)1179return max(abs(v_ll) + c_ll * norm_, abs(v_rr) + c_rr * norm_)1180end11811182# Calculate estimates for minimum and maximum wave speeds for HLL-type fluxes1183@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer,1184equations::CompressibleEulerEquations3D)1185rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1186rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)11871188if orientation == 1 # x-direction1189λ_min = v1_ll - sqrt(equations.gamma * p_ll / rho_ll)1190λ_max = v1_rr + sqrt(equations.gamma * p_rr / rho_rr)1191elseif orientation == 2 # y-direction1192λ_min = v2_ll - sqrt(equations.gamma * p_ll / rho_ll)1193λ_max = v2_rr + sqrt(equations.gamma * p_rr / rho_rr)1194else # z-direction1195λ_min = v3_ll - sqrt(equations.gamma * p_ll / rho_ll)1196λ_max = v3_rr + sqrt(equations.gamma * p_rr / rho_rr)1197end11981199return λ_min, λ_max1200end12011202@inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,1203equations::CompressibleEulerEquations3D)1204rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1205rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)12061207v_normal_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +1208v3_ll * normal_direction[3]1209v_normal_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +1210v3_rr * normal_direction[3]12111212norm_ = norm(normal_direction)1213# The v_normals are already scaled by the norm1214λ_min = v_normal_ll - sqrt(equations.gamma * p_ll / rho_ll) * norm_1215λ_max = v_normal_rr + sqrt(equations.gamma * p_rr / rho_rr) * norm_12161217return λ_min, λ_max1218end12191220# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes1221@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,1222equations::CompressibleEulerEquations3D)1223rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1224rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)12251226c_ll = sqrt(equations.gamma * p_ll / rho_ll)1227c_rr = sqrt(equations.gamma * p_rr / rho_rr)12281229if orientation == 1 # x-direction1230λ_min = min(v1_ll - c_ll, v1_rr - c_rr)1231λ_max = max(v1_ll + c_ll, v1_rr + c_rr)1232elseif orientation == 2 # y-direction1233λ_min = min(v2_ll - c_ll, v2_rr - c_rr)1234λ_max = max(v2_ll + c_ll, v2_rr + c_rr)1235else # z-direction1236λ_min = min(v3_ll - c_ll, v3_rr - c_rr)1237λ_max = max(v3_ll + c_ll, v3_rr + c_rr)1238end12391240return λ_min, λ_max1241end12421243# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes1244@inline function min_max_speed_davis(u_ll, u_rr, normal_direction::AbstractVector,1245equations::CompressibleEulerEquations3D)1246rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1247rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)12481249norm_ = norm(normal_direction)12501251c_ll = sqrt(equations.gamma * p_ll / rho_ll) * norm_1252c_rr = sqrt(equations.gamma * p_rr / rho_rr) * norm_12531254v_normal_ll = v1_ll * normal_direction[1] +1255v2_ll * normal_direction[2] +1256v3_ll * normal_direction[3]1257v_normal_rr = v1_rr * normal_direction[1] +1258v2_rr * normal_direction[2] +1259v3_rr * normal_direction[3]12601261# The v_normals are already scaled by the norm1262λ_min = min(v_normal_ll - c_ll, v_normal_rr - c_rr)1263λ_max = max(v_normal_ll + c_ll, v_normal_rr + c_rr)12641265return λ_min, λ_max1266end12671268# Rotate normal vector to x-axis; normal, tangent1 and tangent2 need to be orthonormal1269# Called inside `FluxRotated` in `numerical_fluxes.jl` so the directions1270# has been normalized prior to this rotation of the state vector1271@inline function rotate_to_x(u, normal_vector, tangent1, tangent2,1272equations::CompressibleEulerEquations3D)1273# Multiply with [ 1 0 0 0 0;1274# 0 ― normal_vector ― 0;1275# 0 ― tangent1 ― 0;1276# 0 ― tangent2 ― 0;1277# 0 0 0 0 1 ]1278return SVector(u[1],1279normal_vector[1] * u[2] + normal_vector[2] * u[3] +1280normal_vector[3] * u[4],1281tangent1[1] * u[2] + tangent1[2] * u[3] + tangent1[3] * u[4],1282tangent2[1] * u[2] + tangent2[2] * u[3] + tangent2[3] * u[4],1283u[5])1284end12851286@inline function (dissipation::DissipationMatrixWintersEtal)(u_ll, u_rr,1287normal_direction::AbstractVector,1288equations::CompressibleEulerEquations3D)1289(; gamma) = equations12901291# Step 1:1292# Rotate solution into the appropriate direction12931294norm_ = norm(normal_direction)1295# Normalize the vector without using `normalize` since we need to multiply by the `norm_` later1296normal_vector = normal_direction / norm_12971298# Some vector that can't be identical to normal_vector (unless normal_vector == 0)1299tangent1 = SVector(normal_direction[2], normal_direction[3], -normal_direction[1])1300# Orthogonal projection1301tangent1 -= dot(normal_vector, tangent1) * normal_vector1302tangent1 = normalize(tangent1)13031304# Third orthogonal vector1305tangent2 = normalize(cross(normal_direction, tangent1))13061307u_ll_rotated = rotate_to_x(u_ll, normal_vector, tangent1, tangent2, equations)1308u_rr_rotated = rotate_to_x(u_rr, normal_vector, tangent1, tangent2, equations)13091310# Step 2:1311# Compute the averages using the rotated variables1312rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll_rotated, equations)1313rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr_rotated, equations)13141315b_ll = rho_ll / (2 * p_ll)1316b_rr = rho_rr / (2 * p_rr)13171318rho_log = ln_mean(rho_ll, rho_rr)1319b_log = ln_mean(b_ll, b_rr)1320v1_avg = 0.5f0 * (v1_ll + v1_rr)1321v2_avg = 0.5f0 * (v2_ll + v2_rr)1322v3_avg = 0.5f0 * (v3_ll + v3_rr)1323p_avg = 0.5f0 * (rho_ll + rho_rr) / (b_ll + b_rr)1324v_squared_bar = v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr1325h_bar = gamma / (2 * b_log * (gamma - 1)) + 0.5f0 * v_squared_bar1326c_bar = sqrt(gamma * p_avg / rho_log)13271328# Step 3:1329# Build the dissipation term as given in Appendix A of the paper1330# - A. R. Winters, D. Derigs, G. Gassner, S. Walch, A uniquely defined entropy stable matrix dissipation operator1331# for high Mach number ideal MHD and compressible Euler simulations (2017). Journal of Computational Physics.1332# [DOI: 10.1016/j.jcp.2016.12.006](https://doi.org/10.1016/j.jcp.2016.12.006).13331334# Get entropy variables jump in the rotated variables1335w_jump = cons2entropy(u_rr_rotated, equations) -1336cons2entropy(u_ll_rotated, equations)13371338# Entries of the diagonal scaling matrix where D = ABS(\Lambda)T1339lambda_1 = abs(v1_avg - c_bar) * rho_log / (2 * gamma)1340lambda_2 = abs(v1_avg) * rho_log * (gamma - 1) / gamma1341lambda_3 = abs(v1_avg) * p_avg # scaled repeated eigenvalue in the tangential direction1342lambda_5 = abs(v1_avg + c_bar) * rho_log / (2 * gamma)1343D = SVector(lambda_1, lambda_2, lambda_3, lambda_3, lambda_5)13441345# Entries of the right eigenvector matrix (others have already been precomputed)1346r21 = v1_avg - c_bar1347r25 = v1_avg + c_bar1348r51 = h_bar - v1_avg * c_bar1349r52 = 0.5f0 * v_squared_bar1350r55 = h_bar + v1_avg * c_bar13511352# Build R and transpose of R matrices1353R = @SMatrix [[1;; 1;; 0;; 0;; 1];1354[r21;; v1_avg;; 0;; 0;; r25];1355[v2_avg;; v2_avg;; 1;; 0;; v2_avg];1356[v3_avg;; v3_avg;; 0;; 1;; v3_avg];1357[r51;; r52;; v2_avg;; v3_avg;; r55]]13581359RT = @SMatrix [[1;; r21;; v2_avg;; v3_avg;; r51];1360[1;; v1_avg;; v2_avg;; v3_avg;; r52];1361[0;; 0;; 1;; 0;; v2_avg];1362[0;; 0;; 0;; 1;; v3_avg];1363[1;; r25;; v2_avg;; v3_avg;; r55]]13641365# Compute the dissipation term R * D * R^T * [[w]] from right-to-left13661367# First comes R^T * [[w]]1368diss = RT * w_jump1369# Next multiply with the eigenvalues and Barth scaling1370diss = D .* diss1371# Finally apply the remaining eigenvector matrix1372diss = R * diss13731374# Step 4:1375# Do not forget to backrotate and then return with proper normalization scaling1376return -0.5f0 * rotate_from_x(diss, normal_vector, tangent1, tangent2, equations) *1377norm_1378end13791380# Rotate x-axis to normal vector; normal, tangent1 and tangent2 need to be orthonormal1381# Called inside `FluxRotated` in `numerical_fluxes.jl` so the directions1382# has been normalized prior to this back-rotation of the state vector1383@inline function rotate_from_x(u, normal_vector, tangent1, tangent2,1384equations::CompressibleEulerEquations3D)1385# Multiply with [ 1 0 0 0 0;1386# 0 | | | 0;1387# 0 normal_vector tangent1 tangent2 0;1388# 0 | | | 0;1389# 0 0 0 0 1 ]1390return SVector(u[1],1391normal_vector[1] * u[2] + tangent1[1] * u[3] + tangent2[1] * u[4],1392normal_vector[2] * u[2] + tangent1[2] * u[3] + tangent2[2] * u[4],1393normal_vector[3] * u[2] + tangent1[3] * u[3] + tangent2[3] * u[4],1394u[5])1395end13961397"""1398flux_hllc(u_ll, u_rr, orientation_or_normal_direction, equations::CompressibleEulerEquations3D)13991400Computes the HLLC flux (HLL with Contact) for compressible Euler equations developed by E.F. Toro1401[Lecture slides](http://www.prague-sum.com/download/2012/Toro_2-HLLC-RiemannSolver.pdf)1402Signal speeds: [DOI: 10.1137/S1064827593260140](https://doi.org/10.1137/S1064827593260140)1403"""1404function flux_hllc(u_ll, u_rr, orientation::Integer,1405equations::CompressibleEulerEquations3D)1406# Calculate primitive variables and speed of sound1407rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll = u_ll1408rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr = u_rr14091410v1_ll = rho_v1_ll / rho_ll1411v2_ll = rho_v2_ll / rho_ll1412v3_ll = rho_v3_ll / rho_ll1413e_ll = rho_e_total_ll / rho_ll1414p_ll = (equations.gamma - 1) *1415(rho_e_total_ll - 0.5f0 * rho_ll * (v1_ll^2 + v2_ll^2 + v3_ll^2))1416c_ll = sqrt(equations.gamma * p_ll / rho_ll)14171418v1_rr = rho_v1_rr / rho_rr1419v2_rr = rho_v2_rr / rho_rr1420v3_rr = rho_v3_rr / rho_rr1421e_rr = rho_e_total_rr / rho_rr1422p_rr = (equations.gamma - 1) *1423(rho_e_total_rr - 0.5f0 * rho_rr * (v1_rr^2 + v2_rr^2 + v3_rr^2))1424c_rr = sqrt(equations.gamma * p_rr / rho_rr)14251426# Obtain left and right fluxes1427f_ll = flux(u_ll, orientation, equations)1428f_rr = flux(u_rr, orientation, equations)14291430# Compute Roe averages1431sqrt_rho_ll = sqrt(rho_ll)1432sqrt_rho_rr = sqrt(rho_rr)1433sum_sqrt_rho = sqrt_rho_ll + sqrt_rho_rr1434if orientation == 1 # x-direction1435vel_L = v1_ll1436vel_R = v1_rr1437elseif orientation == 2 # y-direction1438vel_L = v2_ll1439vel_R = v2_rr1440else # z-direction1441vel_L = v3_ll1442vel_R = v3_rr1443end1444vel_roe = (sqrt_rho_ll * vel_L + sqrt_rho_rr * vel_R) / sum_sqrt_rho1445v1_roe = sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr1446v2_roe = sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr1447v3_roe = sqrt_rho_ll * v3_ll + sqrt_rho_rr * v3_rr1448vel_roe_mag = (v1_roe^2 + v2_roe^2 + v3_roe^2) / sum_sqrt_rho^21449H_ll = (rho_e_total_ll + p_ll) / rho_ll1450H_rr = (rho_e_total_rr + p_rr) / rho_rr1451H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) / sum_sqrt_rho1452c_roe = sqrt((equations.gamma - 1) * (H_roe - 0.5f0 * vel_roe_mag))1453Ssl = min(vel_L - c_ll, vel_roe - c_roe)1454Ssr = max(vel_R + c_rr, vel_roe + c_roe)1455sMu_L = Ssl - vel_L1456sMu_R = Ssr - vel_R14571458if Ssl >= 01459f1 = f_ll[1]1460f2 = f_ll[2]1461f3 = f_ll[3]1462f4 = f_ll[4]1463f5 = f_ll[5]1464elseif Ssr <= 01465f1 = f_rr[1]1466f2 = f_rr[2]1467f3 = f_rr[3]1468f4 = f_rr[4]1469f5 = f_rr[5]1470else1471SStar = (p_rr - p_ll + rho_ll * vel_L * sMu_L - rho_rr * vel_R * sMu_R) /1472(rho_ll * sMu_L - rho_rr * sMu_R)1473if Ssl <= 0 <= SStar1474densStar = rho_ll * sMu_L / (Ssl - SStar)1475enerStar = e_ll + (SStar - vel_L) * (SStar + p_ll / (rho_ll * sMu_L))1476UStar1 = densStar1477UStar5 = densStar * enerStar1478if orientation == 1 # x-direction1479UStar2 = densStar * SStar1480UStar3 = densStar * v2_ll1481UStar4 = densStar * v3_ll1482elseif orientation == 2 # y-direction1483UStar2 = densStar * v1_ll1484UStar3 = densStar * SStar1485UStar4 = densStar * v3_ll1486else # z-direction1487UStar2 = densStar * v1_ll1488UStar3 = densStar * v2_ll1489UStar4 = densStar * SStar1490end1491f1 = f_ll[1] + Ssl * (UStar1 - rho_ll)1492f2 = f_ll[2] + Ssl * (UStar2 - rho_v1_ll)1493f3 = f_ll[3] + Ssl * (UStar3 - rho_v2_ll)1494f4 = f_ll[4] + Ssl * (UStar4 - rho_v3_ll)1495f5 = f_ll[5] + Ssl * (UStar5 - rho_e_total_ll)1496else1497densStar = rho_rr * sMu_R / (Ssr - SStar)1498enerStar = e_rr + (SStar - vel_R) * (SStar + p_rr / (rho_rr * sMu_R))1499UStar1 = densStar1500UStar5 = densStar * enerStar1501if orientation == 1 # x-direction1502UStar2 = densStar * SStar1503UStar3 = densStar * v2_rr1504UStar4 = densStar * v3_rr1505elseif orientation == 2 # y-direction1506UStar2 = densStar * v1_rr1507UStar3 = densStar * SStar1508UStar4 = densStar * v3_rr1509else # z-direction1510UStar2 = densStar * v1_rr1511UStar3 = densStar * v2_rr1512UStar4 = densStar * SStar1513end1514f1 = f_rr[1] + Ssr * (UStar1 - rho_rr)1515f2 = f_rr[2] + Ssr * (UStar2 - rho_v1_rr)1516f3 = f_rr[3] + Ssr * (UStar3 - rho_v2_rr)1517f4 = f_rr[4] + Ssr * (UStar4 - rho_v3_rr)1518f5 = f_rr[5] + Ssr * (UStar5 - rho_e_total_rr)1519end1520end1521return SVector(f1, f2, f3, f4, f5)1522end15231524function flux_hllc(u_ll, u_rr, normal_direction::AbstractVector,1525equations::CompressibleEulerEquations3D)1526# Calculate primitive variables and speed of sound1527rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1528rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)15291530v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +1531v3_ll * normal_direction[3]1532v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +1533v3_rr * normal_direction[3]15341535norm_ = norm(normal_direction)1536norm_sq = norm_ * norm_1537inv_norm_sq = inv(norm_sq)15381539c_ll = sqrt(equations.gamma * p_ll / rho_ll) * norm_1540c_rr = sqrt(equations.gamma * p_rr / rho_rr) * norm_15411542# Obtain left and right fluxes1543f_ll = flux(u_ll, normal_direction, equations)1544f_rr = flux(u_rr, normal_direction, equations)15451546# Compute Roe averages1547sqrt_rho_ll = sqrt(rho_ll)1548sqrt_rho_rr = sqrt(rho_rr)1549sum_sqrt_rho = sqrt_rho_ll + sqrt_rho_rr15501551v1_roe = (sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr) / sum_sqrt_rho1552v2_roe = (sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr) / sum_sqrt_rho1553v3_roe = (sqrt_rho_ll * v3_ll + sqrt_rho_rr * v3_rr) / sum_sqrt_rho1554vel_roe = v1_roe * normal_direction[1] + v2_roe * normal_direction[2] +1555v3_roe * normal_direction[3]1556vel_roe_mag = v1_roe^2 + v2_roe^2 + v3_roe^215571558e_ll = u_ll[5] / rho_ll1559e_rr = u_rr[5] / rho_rr15601561H_ll = (u_ll[5] + p_ll) / rho_ll1562H_rr = (u_rr[5] + p_rr) / rho_rr15631564H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) / sum_sqrt_rho1565c_roe = sqrt((equations.gamma - 1) * (H_roe - 0.5f0 * vel_roe_mag)) * norm_15661567Ssl = min(v_dot_n_ll - c_ll, vel_roe - c_roe)1568Ssr = max(v_dot_n_rr + c_rr, vel_roe + c_roe)1569sMu_L = Ssl - v_dot_n_ll1570sMu_R = Ssr - v_dot_n_rr15711572if Ssl >= 01573f1 = f_ll[1]1574f2 = f_ll[2]1575f3 = f_ll[3]1576f4 = f_ll[4]1577f5 = f_ll[5]1578elseif Ssr <= 01579f1 = f_rr[1]1580f2 = f_rr[2]1581f3 = f_rr[3]1582f4 = f_rr[4]1583f5 = f_rr[5]1584else1585SStar = (rho_ll * v_dot_n_ll * sMu_L - rho_rr * v_dot_n_rr * sMu_R +1586(p_rr - p_ll) * norm_sq) / (rho_ll * sMu_L - rho_rr * sMu_R)1587if Ssl <= 0 <= SStar1588densStar = rho_ll * sMu_L / (Ssl - SStar)1589enerStar = e_ll +1590(SStar - v_dot_n_ll) *1591(SStar * inv_norm_sq + p_ll / (rho_ll * sMu_L))1592UStar1 = densStar1593UStar2 = densStar *1594(v1_ll + (SStar - v_dot_n_ll) * normal_direction[1] * inv_norm_sq)1595UStar3 = densStar *1596(v2_ll + (SStar - v_dot_n_ll) * normal_direction[2] * inv_norm_sq)1597UStar4 = densStar *1598(v3_ll + (SStar - v_dot_n_ll) * normal_direction[3] * inv_norm_sq)1599UStar5 = densStar * enerStar1600f1 = f_ll[1] + Ssl * (UStar1 - u_ll[1])1601f2 = f_ll[2] + Ssl * (UStar2 - u_ll[2])1602f3 = f_ll[3] + Ssl * (UStar3 - u_ll[3])1603f4 = f_ll[4] + Ssl * (UStar4 - u_ll[4])1604f5 = f_ll[5] + Ssl * (UStar5 - u_ll[5])1605else1606densStar = rho_rr * sMu_R / (Ssr - SStar)1607enerStar = e_rr +1608(SStar - v_dot_n_rr) *1609(SStar * inv_norm_sq + p_rr / (rho_rr * sMu_R))1610UStar1 = densStar1611UStar2 = densStar *1612(v1_rr + (SStar - v_dot_n_rr) * normal_direction[1] * inv_norm_sq)1613UStar3 = densStar *1614(v2_rr + (SStar - v_dot_n_rr) * normal_direction[2] * inv_norm_sq)1615UStar4 = densStar *1616(v3_rr + (SStar - v_dot_n_rr) * normal_direction[3] * inv_norm_sq)1617UStar5 = densStar * enerStar1618f1 = f_rr[1] + Ssr * (UStar1 - u_rr[1])1619f2 = f_rr[2] + Ssr * (UStar2 - u_rr[2])1620f3 = f_rr[3] + Ssr * (UStar3 - u_rr[3])1621f4 = f_rr[4] + Ssr * (UStar4 - u_rr[4])1622f5 = f_rr[5] + Ssr * (UStar5 - u_rr[5])1623end1624end1625return SVector(f1, f2, f3, f4, f5)1626end16271628"""1629min_max_speed_einfeldt(u_ll, u_rr, orientation, equations::CompressibleEulerEquations3D)16301631Computes the HLLE (Harten-Lax-van Leer-Einfeldt) flux for the compressible Euler equations.1632Special estimates of the signal velocites and linearization of the Riemann problem developed1633by Einfeldt to ensure that the internal energy and density remain positive during the computation1634of the numerical flux.16351636- Bernd Einfeldt (1988)1637On Godunov-type methods for gas dynamics.1638[DOI: 10.1137/0725021](https://doi.org/10.1137/0725021)1639- Bernd Einfeldt, Claus-Dieter Munz, Philip L. Roe and Björn Sjögreen (1991)1640On Godunov-type methods near low densities.1641[DOI: 10.1016/0021-9991(91)90211-3](https://doi.org/10.1016/0021-9991(91)90211-3)1642"""1643@inline function min_max_speed_einfeldt(u_ll, u_rr, orientation::Integer,1644equations::CompressibleEulerEquations3D)1645# Calculate primitive variables, enthalpy and speed of sound1646rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1647rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)16481649# `u_ll[5]` is total energy `rho_e_total_ll` on the left1650H_ll = (u_ll[5] + p_ll) / rho_ll1651c_ll = sqrt(equations.gamma * p_ll / rho_ll)16521653# `u_rr[5]` is total energy `rho_e_total_rr` on the right1654H_rr = (u_rr[5] + p_rr) / rho_rr1655c_rr = sqrt(equations.gamma * p_rr / rho_rr)16561657# Compute Roe averages1658sqrt_rho_ll = sqrt(rho_ll)1659sqrt_rho_rr = sqrt(rho_rr)1660inv_sum_sqrt_rho = inv(sqrt_rho_ll + sqrt_rho_rr)16611662v1_roe = (sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr) * inv_sum_sqrt_rho1663v2_roe = (sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr) * inv_sum_sqrt_rho1664v3_roe = (sqrt_rho_ll * v3_ll + sqrt_rho_rr * v3_rr) * inv_sum_sqrt_rho1665v_roe_mag = v1_roe^2 + v2_roe^2 + v3_roe^216661667H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) * inv_sum_sqrt_rho1668c_roe = sqrt((equations.gamma - 1) * (H_roe - 0.5f0 * v_roe_mag))16691670# Compute convenience constant for positivity preservation, see1671# https://doi.org/10.1016/0021-9991(91)90211-31672beta = sqrt(0.5f0 * (equations.gamma - 1) / equations.gamma)16731674# Estimate the edges of the Riemann fan (with positivity conservation)1675if orientation == 1 # x-direction1676SsL = min(v1_roe - c_roe, v1_ll - beta * c_ll, 0)1677SsR = max(v1_roe + c_roe, v1_rr + beta * c_rr, 0)1678elseif orientation == 2 # y-direction1679SsL = min(v2_roe - c_roe, v2_ll - beta * c_ll, 0)1680SsR = max(v2_roe + c_roe, v2_rr + beta * c_rr, 0)1681else # z-direction1682SsL = min(v3_roe - c_roe, v3_ll - beta * c_ll, 0)1683SsR = max(v3_roe + c_roe, v3_rr + beta * c_rr, 0)1684end16851686return SsL, SsR1687end16881689"""1690min_max_speed_einfeldt(u_ll, u_rr, normal_direction, equations::CompressibleEulerEquations3D)16911692Computes the HLLE (Harten-Lax-van Leer-Einfeldt) flux for the compressible Euler equations.1693Special estimates of the signal velocites and linearization of the Riemann problem developed1694by Einfeldt to ensure that the internal energy and density remain positive during the computation1695of the numerical flux.16961697- Bernd Einfeldt (1988)1698On Godunov-type methods for gas dynamics.1699[DOI: 10.1137/0725021](https://doi.org/10.1137/0725021)1700- Bernd Einfeldt, Claus-Dieter Munz, Philip L. Roe and Björn Sjögreen (1991)1701On Godunov-type methods near low densities.1702[DOI: 10.1016/0021-9991(91)90211-3](https://doi.org/10.1016/0021-9991(91)90211-3)1703"""1704@inline function min_max_speed_einfeldt(u_ll, u_rr, normal_direction::AbstractVector,1705equations::CompressibleEulerEquations3D)1706# Calculate primitive variables, enthalpy and speed of sound1707rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1708rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)17091710v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +1711v3_ll * normal_direction[3]1712v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +1713v3_rr * normal_direction[3]17141715norm_ = norm(normal_direction)17161717# `u_ll[5]` is total energy `rho_e_total_ll` on the left1718H_ll = (u_ll[5] + p_ll) / rho_ll1719c_ll = sqrt(equations.gamma * p_ll / rho_ll) * norm_17201721# `u_rr[5]` is total energy `rho_e_total_rr` on the right1722H_rr = (u_rr[5] + p_rr) / rho_rr1723c_rr = sqrt(equations.gamma * p_rr / rho_rr) * norm_17241725# Compute Roe averages1726sqrt_rho_ll = sqrt(rho_ll)1727sqrt_rho_rr = sqrt(rho_rr)1728inv_sum_sqrt_rho = inv(sqrt_rho_ll + sqrt_rho_rr)17291730v1_roe = (sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr) * inv_sum_sqrt_rho1731v2_roe = (sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr) * inv_sum_sqrt_rho1732v3_roe = (sqrt_rho_ll * v3_ll + sqrt_rho_rr * v3_rr) * inv_sum_sqrt_rho1733v_roe = v1_roe * normal_direction[1] + v2_roe * normal_direction[2] +1734v3_roe * normal_direction[3]1735v_roe_mag = v1_roe^2 + v2_roe^2 + v3_roe^217361737H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) * inv_sum_sqrt_rho1738c_roe = sqrt((equations.gamma - 1) * (H_roe - 0.5f0 * v_roe_mag)) * norm_17391740# Compute convenience constant for positivity preservation, see1741# https://doi.org/10.1016/0021-9991(91)90211-31742beta = sqrt(0.5f0 * (equations.gamma - 1) / equations.gamma)17431744# Estimate the edges of the Riemann fan (with positivity conservation)1745SsL = min(v_roe - c_roe, v_dot_n_ll - beta * c_ll, 0)1746SsR = max(v_roe + c_roe, v_dot_n_rr + beta * c_rr, 0)17471748return SsL, SsR1749end17501751@inline function max_abs_speeds(u, equations::CompressibleEulerEquations3D)1752rho, v1, v2, v3, p = cons2prim(u, equations)1753c = sqrt(equations.gamma * p / rho)17541755return abs(v1) + c, abs(v2) + c, abs(v3) + c1756end17571758# Convert conservative variables to primitive1759@inline function cons2prim(u, equations::CompressibleEulerEquations3D)1760rho, rho_v1, rho_v2, rho_v3, rho_e_total = u17611762v1 = rho_v1 / rho1763v2 = rho_v2 / rho1764v3 = rho_v3 / rho1765p = (equations.gamma - 1) *1766(rho_e_total - 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3))17671768return SVector(rho, v1, v2, v3, p)1769end17701771# Convert conservative variables to entropy1772@inline function cons2entropy(u, equations::CompressibleEulerEquations3D)1773rho, rho_v1, rho_v2, rho_v3, rho_e_total = u17741775v1 = rho_v1 / rho1776v2 = rho_v2 / rho1777v3 = rho_v3 / rho1778v_square = v1^2 + v2^2 + v3^21779p = (equations.gamma - 1) * (rho_e_total - 0.5f0 * rho * v_square)1780s = log(p) - equations.gamma * log(rho)1781rho_p = rho / p17821783w1 = (equations.gamma - s) * equations.inv_gamma_minus_one -17840.5f0 * rho_p * v_square1785w2 = rho_p * v11786w3 = rho_p * v21787w4 = rho_p * v31788w5 = -rho_p17891790return SVector(w1, w2, w3, w4, w5)1791end17921793# Transformation from conservative variables u to entropy vector ds_0/du,1794# using the modified specific entropy of Guermond et al. (2019): s_0 = p * rho^(-gamma) / (gamma-1).1795# Note: This is *not* the "conventional" specific entropy s = ln(p / rho^(gamma)).1796@inline function cons2entropy_guermond_etal(u, equations::CompressibleEulerEquations3D)1797rho, rho_v1, rho_v2, rho_v3, rho_e_total = u17981799inv_rho = 1 / rho1800v_square_rho = (rho_v1^2 + rho_v2^2 + rho_v3^2) * inv_rho1801inv_rho_gammap1 = inv_rho^(equations.gamma + 1)18021803# The derivative vector for the modified specific entropy of Guermond et al.1804w1 = inv_rho_gammap1 *1805(0.5f0 * (equations.gamma + 1) * v_square_rho - equations.gamma * rho_e_total)1806w2 = -rho_v1 * inv_rho_gammap11807w3 = -rho_v2 * inv_rho_gammap11808w4 = -rho_v3 * inv_rho_gammap11809w5 = inv_rho^equations.gamma18101811return SVector(w1, w2, w3, w4, w5)1812end18131814@inline function entropy2cons(w, equations::CompressibleEulerEquations3D)1815# See Hughes, Franca, Mallet (1986) A new finite element formulation for CFD1816# [DOI: 10.1016/0045-7825(86)90127-1](https://doi.org/10.1016/0045-7825(86)90127-1)1817@unpack gamma = equations18181819# convert to entropy `-rho * s` used by Hughes, France, Mallet (1986)1820# instead of `-rho * s / (gamma - 1)`1821V1, V2, V3, V4, V5 = w .* (gamma - 1)18221823# s = specific entropy, eq. (53)1824V_square = V2^2 + V3^2 + V4^21825s = gamma - V1 + V_square / (2 * V5)18261827# eq. (52)1828rho_iota = ((gamma - 1) / (-V5)^gamma)^(equations.inv_gamma_minus_one) *1829exp(-s * equations.inv_gamma_minus_one)18301831# eq. (51)1832rho = -rho_iota * V51833rho_v1 = rho_iota * V21834rho_v2 = rho_iota * V31835rho_v3 = rho_iota * V41836rho_e_total = rho_iota * (1 - V_square / (2 * V5))1837return SVector(rho, rho_v1, rho_v2, rho_v3, rho_e_total)1838end18391840# Convert primitive to conservative variables1841@inline function prim2cons(prim, equations::CompressibleEulerEquations3D)1842rho, v1, v2, v3, p = prim1843rho_v1 = rho * v11844rho_v2 = rho * v21845rho_v3 = rho * v31846rho_e_total = p * equations.inv_gamma_minus_one +18470.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3)1848return SVector(rho, rho_v1, rho_v2, rho_v3, rho_e_total)1849end18501851@inline function density(u, equations::CompressibleEulerEquations3D)1852rho = u[1]1853return rho1854end18551856@inline function velocity(u, equations::CompressibleEulerEquations3D)1857rho = u[1]1858v1 = u[2] / rho1859v2 = u[3] / rho1860v3 = u[4] / rho1861return SVector(v1, v2, v3)1862end18631864@inline function velocity(u, orientation::Int, equations::CompressibleEulerEquations3D)1865rho = u[1]1866v = u[orientation + 1] / rho1867return v1868end18691870@inline function pressure(u, equations::CompressibleEulerEquations3D)1871rho, rho_v1, rho_v2, rho_v3, rho_e_total = u1872p = (equations.gamma - 1) *1873(rho_e_total - 0.5f0 * (rho_v1^2 + rho_v2^2 + rho_v3^2) / rho)1874return p1875end18761877@inline function density_pressure(u, equations::CompressibleEulerEquations3D)1878rho, rho_v1, rho_v2, rho_v3, rho_e_total = u1879rho_times_p = (equations.gamma - 1) *1880(rho * rho_e_total - 0.5f0 * (rho_v1^2 + rho_v2^2 + rho_v3^2))1881return rho_times_p1882end18831884# Calculate thermodynamic entropy for a conservative state `u`1885@inline function entropy_thermodynamic(u, equations::CompressibleEulerEquations3D)1886rho, _ = u1887p = pressure(u, equations)18881889# Thermodynamic entropy1890s = log(p) - equations.gamma * log(rho)18911892return s1893end18941895# Calculate mathematical entropy for a conservative state `cons`1896@inline function entropy_math(cons, equations::CompressibleEulerEquations3D)1897S = -entropy_thermodynamic(cons, equations) * cons[1] *1898equations.inv_gamma_minus_one1899# Mathematical entropy19001901return S1902end19031904@doc raw"""1905entropy_guermond_etal(u, equations::CompressibleEulerEquations3D)19061907Calculate the modified specific entropy of Guermond et al. (2019):1908```math1909s_0 = p * \rho^{-\gamma} / (\gamma-1).1910```1911Note: This is *not* the "conventional" specific entropy ``s = ln(p / \rho^\gamma)``.1912- Guermond at al. (2019)1913Invariant domain preserving discretization-independent schemes and convex limiting for hyperbolic systems.1914[DOI: 10.1016/j.cma.2018.11.036](https://doi.org/10.1016/j.cma.2018.11.036)1915"""1916@inline function entropy_guermond_etal(u, equations::CompressibleEulerEquations3D)1917rho, rho_v1, rho_v2, rho_v3, rho_e_total = u19181919# Modified specific entropy from Guermond et al. (2019)1920s = (rho_e_total - 0.5f0 * (rho_v1^2 + rho_v2^2 + rho_v3^2) / rho) *1921(1 / rho)^equations.gamma19221923return s1924end19251926# Transformation from conservative variables u to d(s)/d(u)1927@inline function gradient_conservative(::typeof(entropy_guermond_etal),1928u, equations::CompressibleEulerEquations3D)1929return cons2entropy_guermond_etal(u, equations)1930end19311932# Default entropy is the mathematical entropy1933@inline function entropy(cons, equations::CompressibleEulerEquations3D)1934return entropy_math(cons, equations)1935end19361937# Calculate total energy for a conservative state `cons`1938@inline energy_total(cons, ::CompressibleEulerEquations3D) = cons[5]19391940# Calculate kinetic energy for a conservative state `cons`1941@inline function energy_kinetic(u, equations::CompressibleEulerEquations3D)1942rho, rho_v1, rho_v2, rho_v3, _ = u1943return 0.5f0 * (rho_v1^2 + rho_v2^2 + rho_v3^2) / rho1944end19451946# Calculate internal energy for a conservative state `cons`1947@inline function energy_internal(cons, equations::CompressibleEulerEquations3D)1948return energy_total(cons, equations) - energy_kinetic(cons, equations)1949end19501951@inline function entropy_potential(u, orientation::Int,1952equations::CompressibleEulerEquations3D)1953if orientation == 11954return u[2]1955elseif orientation == 21956return u[3]1957else # orientation == 31958return u[4]1959end1960end1961# Version for non-Cartesian meshes, i.e., everything but `TreeMesh`es.1962@inline function entropy_potential(u, normal_direction::AbstractVector,1963equations::CompressibleEulerEquations3D)1964return u[2] * normal_direction[1] +1965u[3] * normal_direction[2] +1966u[4] * normal_direction[3]1967end19681969# State validation for Newton-bisection method of subcell IDP limiting1970@inline function Base.isvalid(u, equations::CompressibleEulerEquations3D)1971if u[1] <= 0 || pressure(u, equations) <= 01972return false1973end1974return true1975end1976end # @muladd197719781979