Path: blob/main/src/equations/compressible_euler_3d.jl
2055 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 e15\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 +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 +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 +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`` the specific total energy **rather than** specific internal energy, and39```math40p = (\gamma - 1) \left( \rho e - \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))51new{typeof(γ)}(γ, inv_gamma_minus_one)52end53end5455function varnames(::typeof(cons2cons), ::CompressibleEulerEquations3D)56("rho", "rho_v1", "rho_v2", "rho_v3", "rho_e")57end58function varnames(::typeof(cons2prim), ::CompressibleEulerEquations3D)59("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 = 1075return SVector(rho, rho_v1, rho_v2, rho_v3, rho_e)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 = ini^299100return SVector(rho, rho_v1, rho_v2, rho_v3, rho_e)101end102103"""104source_terms_convergence_test(u, x, t, equations::CompressibleEulerEquations3D)105106Source terms used for convergence tests in combination with107[`initial_condition_convergence_test`](@ref).108"""109@inline function source_terms_convergence_test(u, x, t,110equations::CompressibleEulerEquations3D)111# Same settings as in `initial_condition`112RealT = eltype(u)113c = 2114A = convert(RealT, 0.1)115L = 2116f = 1.0f0 / L117ω = 2 * convert(RealT, pi) * f118γ = equations.gamma119120x1, x2, x3 = x121si, co = sincos(ω * (x1 + x2 + x3 - t))122rho = c + A * si123rho_x = ω * A * co124# Note that d/dt rho = -d/dx rho = -d/dy rho = - d/dz rho.125126tmp = (2 * rho - 1.5f0) * (γ - 1)127128du1 = 2 * rho_x129du2 = rho_x * (2 + tmp)130du3 = du2131du4 = du2132du5 = rho_x * (4 * rho + 3 * tmp)133134return SVector(du1, du2, du3, du4, du5)135end136137"""138initial_condition_weak_blast_wave(x, t, equations::CompressibleEulerEquations3D)139140A weak blast wave taken from141- Sebastian Hennemann, Gregor J. Gassner (2020)142A provably entropy stable subcell shock capturing approach for high order split form DG143[arXiv: 2008.12044](https://arxiv.org/abs/2008.12044)144"""145function initial_condition_weak_blast_wave(x, t,146equations::CompressibleEulerEquations3D)147# From Hennemann & Gassner JCP paper 2020 (Sec. 6.3)148# Set up spherical coordinates149RealT = eltype(x)150inicenter = (0, 0, 0)151x_norm = x[1] - inicenter[1]152y_norm = x[2] - inicenter[2]153z_norm = x[3] - inicenter[3]154r = sqrt(x_norm^2 + y_norm^2 + z_norm^2)155phi = atan(y_norm, x_norm)156theta = iszero(r) ? zero(RealT) : acos(z_norm / r)157158# Calculate primitive variables159rho = r > 0.5f0 ? one(RealT) : convert(RealT, 1.1691)160v1 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos(phi) * sin(theta)161v2 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * sin(phi) * sin(theta)162v3 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos(theta)163p = r > 0.5f0 ? one(RealT) : convert(RealT, 1.245)164165return prim2cons(SVector(rho, v1, v2, v3, p), equations)166end167168"""169initial_condition_eoc_test_coupled_euler_gravity(x, t, equations::CompressibleEulerEquations3D)170171Setup used for convergence tests of the Euler equations with self-gravity used in172- Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020)173A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics174[arXiv: 2008.10593](https://arxiv.org/abs/2008.10593)175in combination with [`source_terms_eoc_test_coupled_euler_gravity`](@ref)176or [`source_terms_eoc_test_euler`](@ref).177"""178function initial_condition_eoc_test_coupled_euler_gravity(x, t,179equations::CompressibleEulerEquations3D)180# OBS! this assumes that γ = 2 other manufactured source terms are incorrect181if equations.gamma != 2182error("adiabatic constant must be 2 for the coupling convergence test")183end184RealT = eltype(x)185c = 2186A = convert(RealT, 0.1)187ini = c + A * sinpi(x[1] + x[2] + x[3] - t)188G = 1 # gravitational constant189190rho = ini191v1 = 1192v2 = 1193v3 = 1194p = ini^2 * G * 2 / (3 * convert(RealT, pi)) # "3" is the number of spatial dimensions195196return prim2cons(SVector(rho, v1, v2, v3, p), equations)197end198199"""200source_terms_eoc_test_coupled_euler_gravity(u, x, t, equations::CompressibleEulerEquations3D)201202Setup used for convergence tests of the Euler equations with self-gravity used in203- Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020)204A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics205[arXiv: 2008.10593](https://arxiv.org/abs/2008.10593)206in combination with [`initial_condition_eoc_test_coupled_euler_gravity`](@ref).207"""208@inline function source_terms_eoc_test_coupled_euler_gravity(u, x, t,209equations::CompressibleEulerEquations3D)210# Same settings as in `initial_condition_eoc_test_coupled_euler_gravity`211RealT = eltype(u)212c = 2213A = convert(RealT, 0.1)214G = 1 # gravitational constant, must match coupling solver215C_grav = -4 * G / (3 * convert(RealT, pi)) # "3" is the number of spatial dimensions # 2D: -2.0*G/pi216217x1, x2, x3 = x218si, co = sincospi(x1 + x2 + x3 - t)219rhox = A * convert(RealT, pi) * co220rho = c + A * si221222# In "2 * rhox", the "2" is "number of spatial dimensions minus one"223du1 = 2 * rhox224du2 = 2 * rhox225du3 = 2 * rhox226du4 = 2 * rhox227du5 = 2 * rhox * (1.5f0 - C_grav * rho) # "3" in "3/2" is the number of spatial dimensions228229return SVector(du1, du2, du3, du4, du5)230end231232"""233source_terms_eoc_test_euler(u, x, t, equations::CompressibleEulerEquations3D)234235Setup used for convergence tests of the Euler equations with self-gravity used in236- Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020)237A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics238[arXiv: 2008.10593](https://arxiv.org/abs/2008.10593)239in combination with [`initial_condition_eoc_test_coupled_euler_gravity`](@ref).240241!!! note242This method is to be used for testing pure Euler simulations with analytic self-gravity.243If you intend to do coupled Euler-gravity simulations, you need to use244[`source_terms_eoc_test_coupled_euler_gravity`](@ref) instead.245"""246function source_terms_eoc_test_euler(u, x, t, equations::CompressibleEulerEquations3D)247# Same settings as in `initial_condition_eoc_test_coupled_euler_gravity`248RealT = eltype(u)249c = 2250A = convert(RealT, 0.1)251G = 1252C_grav = -4 * G / (3 * convert(RealT, pi)) # "3" is the number of spatial dimensions253254x1, x2, x3 = x255si, co = sincospi(x1 + x2 + x3 - t)256rhox = A * convert(RealT, pi) * co257rho = c + A * si258259du1 = rhox * 2260du2 = rhox * (2 - C_grav * rho)261du3 = rhox * (2 - C_grav * rho)262du4 = rhox * (2 - C_grav * rho)263du5 = rhox * (3 - 5 * C_grav * rho)264265return SVector(du1, du2, du3, du4, du5)266end267268"""269boundary_condition_slip_wall(u_inner, normal_direction, x, t, surface_flux_function,270equations::CompressibleEulerEquations3D)271272Determine the boundary numerical surface flux for a slip wall condition.273Imposes a zero normal velocity at the wall.274Density is taken from the internal solution state and pressure is computed as an275exact solution of a 1D Riemann problem. Further details about this boundary state276are available in the paper:277- J. J. W. van der Vegt and H. van der Ven (2002)278Slip flow boundary conditions in discontinuous Galerkin discretizations of279the Euler equations of gas dynamics280[PDF](https://reports.nlr.nl/bitstream/handle/10921/692/TP-2002-300.pdf?sequence=1)281282Details about the 1D pressure Riemann solution can be found in Section 6.3.3 of the book283- Eleuterio F. Toro (2009)284Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction2853rd edition286[DOI: 10.1007/b79761](https://doi.org/10.1007/b79761)287288Should be used together with [`P4estMesh`](@ref) or [`T8codeMesh`](@ref).289"""290@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector,291x, t,292surface_flux_function,293equations::CompressibleEulerEquations3D)294norm_ = norm(normal_direction)295# Normalize the vector without using `normalize` since we need to multiply by the `norm_` later296normal = normal_direction / norm_297298# Some vector that can't be identical to normal_vector (unless normal_vector == 0)299tangent1 = SVector(normal_direction[2], normal_direction[3], -normal_direction[1])300# Orthogonal projection301tangent1 -= dot(normal, tangent1) * normal302tangent1 = normalize(tangent1)303304# Third orthogonal vector305tangent2 = normalize(cross(normal_direction, tangent1))306307# rotate the internal solution state308u_local = rotate_to_x(u_inner, normal, tangent1, tangent2, equations)309310# compute the primitive variables311rho_local, v_normal, v_tangent1, v_tangent2, p_local = cons2prim(u_local, equations)312313# Get the solution of the pressure Riemann problem314# See Section 6.3.3 of315# Eleuterio F. Toro (2009)316# Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction317# [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761)318if v_normal <= 0319sound_speed = sqrt(equations.gamma * p_local / rho_local) # local sound speed320p_star = p_local *321(1 + 0.5f0 * (equations.gamma - 1) * v_normal / sound_speed)^(2 *322equations.gamma *323equations.inv_gamma_minus_one)324else # v_normal > 0325A = 2 / ((equations.gamma + 1) * rho_local)326B = p_local * (equations.gamma - 1) / (equations.gamma + 1)327p_star = p_local +3280.5f0 * v_normal / A *329(v_normal + sqrt(v_normal^2 + 4 * A * (p_local + B)))330end331332# For the slip wall we directly set the flux as the normal velocity is zero333return SVector(0,334p_star * normal[1],335p_star * normal[2],336p_star * normal[3],3370) * norm_338end339340"""341boundary_condition_slip_wall(u_inner, orientation, direction, x, t,342surface_flux_function, equations::CompressibleEulerEquations3D)343344Should be used together with [`TreeMesh`](@ref).345"""346@inline function boundary_condition_slip_wall(u_inner, orientation,347direction, x, t,348surface_flux_function,349equations::CompressibleEulerEquations3D)350# get the appropriate normal vector from the orientation351RealT = eltype(u_inner)352if orientation == 1353normal_direction = SVector(one(RealT), zero(RealT), zero(RealT))354elseif orientation == 2355normal_direction = SVector(zero(RealT), one(RealT), zero(RealT))356else # orientation == 3357normal_direction = SVector(zero(RealT), zero(RealT), one(RealT))358end359360# compute and return the flux using `boundary_condition_slip_wall` routine below361return boundary_condition_slip_wall(u_inner, normal_direction, direction,362x, t, surface_flux_function, equations)363end364365"""366boundary_condition_slip_wall(u_inner, normal_direction, direction, x, t,367surface_flux_function, equations::CompressibleEulerEquations3D)368369Should be used together with [`StructuredMesh`](@ref).370"""371@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector,372direction, x, t,373surface_flux_function,374equations::CompressibleEulerEquations3D)375# flip sign of normal to make it outward pointing, then flip the sign of the normal flux back376# to be inward pointing on the -x, -y, and -z sides due to the orientation convention used by StructuredMesh377if isodd(direction)378boundary_flux = -boundary_condition_slip_wall(u_inner, -normal_direction,379x, t, surface_flux_function,380equations)381else382boundary_flux = boundary_condition_slip_wall(u_inner, normal_direction,383x, t, surface_flux_function,384equations)385end386387return boundary_flux388end389390# Calculate 1D flux for a single point391@inline function flux(u, orientation::Integer, equations::CompressibleEulerEquations3D)392rho, rho_v1, rho_v2, rho_v3, rho_e = u393v1 = rho_v1 / rho394v2 = rho_v2 / rho395v3 = rho_v3 / rho396p = (equations.gamma - 1) *397(rho_e - 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3))398if orientation == 1399f1 = rho_v1400f2 = rho_v1 * v1 + p401f3 = rho_v1 * v2402f4 = rho_v1 * v3403f5 = (rho_e + p) * v1404elseif orientation == 2405f1 = rho_v2406f2 = rho_v2 * v1407f3 = rho_v2 * v2 + p408f4 = rho_v2 * v3409f5 = (rho_e + p) * v2410else411f1 = rho_v3412f2 = rho_v3 * v1413f3 = rho_v3 * v2414f4 = rho_v3 * v3 + p415f5 = (rho_e + p) * v3416end417return SVector(f1, f2, f3, f4, f5)418end419420@inline function flux(u, normal_direction::AbstractVector,421equations::CompressibleEulerEquations3D)422rho_e = last(u)423rho, v1, v2, v3, p = cons2prim(u, equations)424425v_normal = v1 * normal_direction[1] + v2 * normal_direction[2] +426v3 * normal_direction[3]427rho_v_normal = rho * v_normal428f1 = rho_v_normal429f2 = rho_v_normal * v1 + p * normal_direction[1]430f3 = rho_v_normal * v2 + p * normal_direction[2]431f4 = rho_v_normal * v3 + p * normal_direction[3]432f5 = (rho_e + p) * v_normal433return SVector(f1, f2, f3, f4, f5)434end435436"""437flux_shima_etal(u_ll, u_rr, orientation_or_normal_direction,438equations::CompressibleEulerEquations3D)439440This flux is is a modification of the original kinetic energy preserving two-point flux by441- Yuichi Kuya, Kosuke Totani and Soshi Kawai (2018)442Kinetic energy and entropy preserving schemes for compressible flows443by split convective forms444[DOI: 10.1016/j.jcp.2018.08.058](https://doi.org/10.1016/j.jcp.2018.08.058)445446The modification is in the energy flux to guarantee pressure equilibrium and was developed by447- Nao Shima, Yuichi Kuya, Yoshiharu Tamaki, Soshi Kawai (JCP 2020)448Preventing spurious pressure oscillations in split convective form discretizations for449compressible flows450[DOI: 10.1016/j.jcp.2020.110060](https://doi.org/10.1016/j.jcp.2020.110060)451"""452@inline function flux_shima_etal(u_ll, u_rr, orientation::Integer,453equations::CompressibleEulerEquations3D)454# Unpack left and right state455rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)456rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)457458# Average each factor of products in flux459rho_avg = 0.5f0 * (rho_ll + rho_rr)460v1_avg = 0.5f0 * (v1_ll + v1_rr)461v2_avg = 0.5f0 * (v2_ll + v2_rr)462v3_avg = 0.5f0 * (v3_ll + v3_rr)463p_avg = 0.5f0 * (p_ll + p_rr)464kin_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr)465466# Calculate fluxes depending on orientation467if orientation == 1468pv1_avg = 0.5f0 * (p_ll * v1_rr + p_rr * v1_ll)469f1 = rho_avg * v1_avg470f2 = f1 * v1_avg + p_avg471f3 = f1 * v2_avg472f4 = f1 * v3_avg473f5 = p_avg * v1_avg * equations.inv_gamma_minus_one + f1 * kin_avg + pv1_avg474elseif orientation == 2475pv2_avg = 0.5f0 * (p_ll * v2_rr + p_rr * v2_ll)476f1 = rho_avg * v2_avg477f2 = f1 * v1_avg478f3 = f1 * v2_avg + p_avg479f4 = f1 * v3_avg480f5 = p_avg * v2_avg * equations.inv_gamma_minus_one + f1 * kin_avg + pv2_avg481else482pv3_avg = 0.5f0 * (p_ll * v3_rr + p_rr * v3_ll)483f1 = rho_avg * v3_avg484f2 = f1 * v1_avg485f3 = f1 * v2_avg486f4 = f1 * v3_avg + p_avg487f5 = p_avg * v3_avg * equations.inv_gamma_minus_one + f1 * kin_avg + pv3_avg488end489490return SVector(f1, f2, f3, f4, f5)491end492493@inline function flux_shima_etal(u_ll, u_rr, normal_direction::AbstractVector,494equations::CompressibleEulerEquations3D)495# Unpack left and right state496rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)497rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)498v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +499v3_ll * normal_direction[3]500v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +501v3_rr * normal_direction[3]502503# Average each factor of products in flux504rho_avg = 0.5f0 * (rho_ll + rho_rr)505v1_avg = 0.5f0 * (v1_ll + v1_rr)506v2_avg = 0.5f0 * (v2_ll + v2_rr)507v3_avg = 0.5f0 * (v3_ll + v3_rr)508v_dot_n_avg = 0.5f0 * (v_dot_n_ll + v_dot_n_rr)509p_avg = 0.5f0 * (p_ll + p_rr)510velocity_square_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr)511512# Calculate fluxes depending on normal_direction513f1 = rho_avg * v_dot_n_avg514f2 = f1 * v1_avg + p_avg * normal_direction[1]515f3 = f1 * v2_avg + p_avg * normal_direction[2]516f4 = f1 * v3_avg + p_avg * normal_direction[3]517f5 = (f1 * velocity_square_avg +518p_avg * v_dot_n_avg * equations.inv_gamma_minus_one519+ 0.5f0 * (p_ll * v_dot_n_rr + p_rr * v_dot_n_ll))520521return SVector(f1, f2, f3, f4, f5)522end523524"""525flux_kennedy_gruber(u_ll, u_rr, orientation_or_normal_direction,526equations::CompressibleEulerEquations3D)527528Kinetic energy preserving two-point flux by529- Kennedy and Gruber (2008)530Reduced aliasing formulations of the convective terms within the531Navier-Stokes equations for a compressible fluid532[DOI: 10.1016/j.jcp.2007.09.020](https://doi.org/10.1016/j.jcp.2007.09.020)533"""534@inline function flux_kennedy_gruber(u_ll, u_rr, orientation::Integer,535equations::CompressibleEulerEquations3D)536# Unpack left and right state537rho_e_ll = last(u_ll)538rho_e_rr = last(u_rr)539rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)540rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)541542# Average each factor of products in flux543rho_avg = 0.5f0 * (rho_ll + rho_rr)544v1_avg = 0.5f0 * (v1_ll + v1_rr)545v2_avg = 0.5f0 * (v2_ll + v2_rr)546v3_avg = 0.5f0 * (v3_ll + v3_rr)547p_avg = 0.5f0 * (p_ll + p_rr)548e_avg = 0.5f0 * (rho_e_ll / rho_ll + rho_e_rr / rho_rr)549550# Calculate fluxes depending on orientation551if orientation == 1552f1 = rho_avg * v1_avg553f2 = f1 * v1_avg + p_avg554f3 = f1 * v2_avg555f4 = f1 * v3_avg556f5 = (rho_avg * e_avg + p_avg) * v1_avg557elseif orientation == 2558f1 = rho_avg * v2_avg559f2 = f1 * v1_avg560f3 = f1 * v2_avg + p_avg561f4 = f1 * v3_avg562f5 = (rho_avg * e_avg + p_avg) * v2_avg563else564f1 = rho_avg * v3_avg565f2 = f1 * v1_avg566f3 = f1 * v2_avg567f4 = f1 * v3_avg + p_avg568f5 = (rho_avg * e_avg + p_avg) * v3_avg569end570571return SVector(f1, f2, f3, f4, f5)572end573574@inline function flux_kennedy_gruber(u_ll, u_rr, normal_direction::AbstractVector,575equations::CompressibleEulerEquations3D)576# Unpack left and right state577rho_e_ll = last(u_ll)578rho_e_rr = last(u_rr)579rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)580rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)581582# Average each factor of products in flux583rho_avg = 0.5f0 * (rho_ll + rho_rr)584v1_avg = 0.5f0 * (v1_ll + v1_rr)585v2_avg = 0.5f0 * (v2_ll + v2_rr)586v3_avg = 0.5f0 * (v3_ll + v3_rr)587p_avg = 0.5f0 * (p_ll + p_rr)588e_avg = 0.5f0 * (rho_e_ll / rho_ll + rho_e_rr / rho_rr)589590v_dot_n_avg = v1_avg * normal_direction[1] + v2_avg * normal_direction[2] +591v3_avg * normal_direction[3]592p_avg = 0.5f0 * (p_ll + p_rr)593e_avg = 0.5f0 * (rho_e_ll / rho_ll + rho_e_rr / rho_rr)594595# Calculate fluxes depending on normal_direction596f1 = rho_avg * v_dot_n_avg597f2 = f1 * v1_avg + p_avg * normal_direction[1]598f3 = f1 * v2_avg + p_avg * normal_direction[2]599f4 = f1 * v3_avg + p_avg * normal_direction[3]600f5 = f1 * e_avg + p_avg * v_dot_n_avg601602return SVector(f1, f2, f3, f4, f5)603end604605"""606flux_chandrashekar(u_ll, u_rr, orientation_or_normal_direction, equations::CompressibleEulerEquations3D)607608Entropy conserving two-point flux by609- Chandrashekar (2013)610Kinetic Energy Preserving and Entropy Stable Finite Volume Schemes611for Compressible Euler and Navier-Stokes Equations612[DOI: 10.4208/cicp.170712.010313a](https://doi.org/10.4208/cicp.170712.010313a)613"""614@inline function flux_chandrashekar(u_ll, u_rr, orientation::Integer,615equations::CompressibleEulerEquations3D)616# Unpack left and right state617rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)618rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)619620beta_ll = 0.5f0 * rho_ll / p_ll621beta_rr = 0.5f0 * rho_rr / p_rr622specific_kin_ll = 0.5f0 * (v1_ll^2 + v2_ll^2 + v3_ll^2)623specific_kin_rr = 0.5f0 * (v1_rr^2 + v2_rr^2 + v3_rr^2)624625# Compute the necessary mean values626rho_avg = 0.5f0 * (rho_ll + rho_rr)627rho_mean = ln_mean(rho_ll, rho_rr)628beta_mean = ln_mean(beta_ll, beta_rr)629beta_avg = 0.5f0 * (beta_ll + beta_rr)630v1_avg = 0.5f0 * (v1_ll + v1_rr)631v2_avg = 0.5f0 * (v2_ll + v2_rr)632v3_avg = 0.5f0 * (v3_ll + v3_rr)633p_mean = 0.5f0 * rho_avg / beta_avg634velocity_square_avg = specific_kin_ll + specific_kin_rr635636# Calculate fluxes depending on orientation637if orientation == 1638f1 = rho_mean * v1_avg639f2 = f1 * v1_avg + p_mean640f3 = f1 * v2_avg641f4 = f1 * v3_avg642f5 = f1 * 0.5f0 *643(1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) +644f2 * v1_avg + f3 * v2_avg + f4 * v3_avg645elseif orientation == 2646f1 = rho_mean * v2_avg647f2 = f1 * v1_avg648f3 = f1 * v2_avg + p_mean649f4 = f1 * v3_avg650f5 = f1 * 0.5f0 *651(1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) +652f2 * v1_avg + f3 * v2_avg + f4 * v3_avg653else654f1 = rho_mean * v3_avg655f2 = f1 * v1_avg656f3 = f1 * v2_avg657f4 = f1 * v3_avg + p_mean658f5 = f1 * 0.5f0 *659(1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) +660f2 * v1_avg + f3 * v2_avg + f4 * v3_avg661end662663return SVector(f1, f2, f3, f4, f5)664end665666@inline function flux_chandrashekar(u_ll, u_rr, normal_direction::AbstractVector,667equations::CompressibleEulerEquations3D)668# Unpack left and right state669rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)670rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)671672v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +673v3_ll * normal_direction[3]674v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +675v3_rr * normal_direction[3]676677beta_ll = 0.5f0 * rho_ll / p_ll678beta_rr = 0.5f0 * rho_rr / p_rr679specific_kin_ll = 0.5f0 * (v1_ll^2 + v2_ll^2 + v3_ll^2)680specific_kin_rr = 0.5f0 * (v1_rr^2 + v2_rr^2 + v3_rr^2)681682# Compute the necessary mean values683rho_avg = 0.5f0 * (rho_ll + rho_rr)684rho_mean = ln_mean(rho_ll, rho_rr)685beta_mean = ln_mean(beta_ll, beta_rr)686beta_avg = 0.5f0 * (beta_ll + beta_rr)687v1_avg = 0.5f0 * (v1_ll + v1_rr)688v2_avg = 0.5f0 * (v2_ll + v2_rr)689v3_avg = 0.5f0 * (v3_ll + v3_rr)690p_mean = 0.5f0 * rho_avg / beta_avg691velocity_square_avg = specific_kin_ll + specific_kin_rr692693# Multiply with average of normal velocities694f1 = rho_mean * 0.5f0 * (v_dot_n_ll + v_dot_n_rr)695f2 = f1 * v1_avg + p_mean * normal_direction[1]696f3 = f1 * v2_avg + p_mean * normal_direction[2]697f4 = f1 * v3_avg + p_mean * normal_direction[3]698f5 = f1 * 0.5f0 * (1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) +699f2 * v1_avg + f3 * v2_avg + f4 * v3_avg700701return SVector(f1, f2, f3, f4, f5)702end703704"""705flux_ranocha(u_ll, u_rr, orientation_or_normal_direction,706equations::CompressibleEulerEquations3D)707708Entropy conserving and kinetic energy preserving two-point flux by709- Hendrik Ranocha (2018)710Generalised Summation-by-Parts Operators and Entropy Stability of Numerical Methods711for Hyperbolic Balance Laws712[PhD thesis, TU Braunschweig](https://cuvillier.de/en/shop/publications/7743)713See also714- Hendrik Ranocha (2020)715Entropy Conserving and Kinetic Energy Preserving Numerical Methods for716the Euler Equations Using Summation-by-Parts Operators717[Proceedings of ICOSAHOM 2018](https://doi.org/10.1007/978-3-030-39647-3_42)718"""719@inline function flux_ranocha(u_ll, u_rr, orientation::Integer,720equations::CompressibleEulerEquations3D)721# Unpack left and right state722rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)723rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)724725# Compute the necessary mean values726rho_mean = ln_mean(rho_ll, rho_rr)727# Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)`728# in exact arithmetic since729# log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ)730# = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ)731inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll)732v1_avg = 0.5f0 * (v1_ll + v1_rr)733v2_avg = 0.5f0 * (v2_ll + v2_rr)734v3_avg = 0.5f0 * (v3_ll + v3_rr)735p_avg = 0.5f0 * (p_ll + p_rr)736velocity_square_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr)737738# Calculate fluxes depending on orientation739if orientation == 1740f1 = rho_mean * v1_avg741f2 = f1 * v1_avg + p_avg742f3 = f1 * v2_avg743f4 = f1 * v3_avg744f5 = f1 *745(velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) +7460.5f0 * (p_ll * v1_rr + p_rr * v1_ll)747elseif orientation == 2748f1 = rho_mean * v2_avg749f2 = f1 * v1_avg750f3 = f1 * v2_avg + p_avg751f4 = f1 * v3_avg752f5 = f1 *753(velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) +7540.5f0 * (p_ll * v2_rr + p_rr * v2_ll)755else # orientation == 3756f1 = rho_mean * v3_avg757f2 = f1 * v1_avg758f3 = f1 * v2_avg759f4 = f1 * v3_avg + p_avg760f5 = f1 *761(velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) +7620.5f0 * (p_ll * v3_rr + p_rr * v3_ll)763end764765return SVector(f1, f2, f3, f4, f5)766end767768@inline function flux_ranocha(u_ll, u_rr, normal_direction::AbstractVector,769equations::CompressibleEulerEquations3D)770# Unpack left and right state771rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)772rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)773v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +774v3_ll * normal_direction[3]775v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +776v3_rr * normal_direction[3]777778# Compute the necessary mean values779rho_mean = ln_mean(rho_ll, rho_rr)780# Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)`781# in exact arithmetic since782# log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ)783# = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ)784inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll)785v1_avg = 0.5f0 * (v1_ll + v1_rr)786v2_avg = 0.5f0 * (v2_ll + v2_rr)787v3_avg = 0.5f0 * (v3_ll + v3_rr)788p_avg = 0.5f0 * (p_ll + p_rr)789velocity_square_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr)790791# Calculate fluxes depending on normal_direction792f1 = rho_mean * 0.5f0 * (v_dot_n_ll + v_dot_n_rr)793f2 = f1 * v1_avg + p_avg * normal_direction[1]794f3 = f1 * v2_avg + p_avg * normal_direction[2]795f4 = f1 * v3_avg + p_avg * normal_direction[3]796f5 = (f1 * (velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one)797+7980.5f0 * (p_ll * v_dot_n_rr + p_rr * v_dot_n_ll))799800return SVector(f1, f2, f3, f4, f5)801end802803"""804splitting_steger_warming(u, orientation::Integer,805equations::CompressibleEulerEquations3D)806splitting_steger_warming(u, which::Union{Val{:minus}, Val{:plus}}807orientation::Integer,808equations::CompressibleEulerEquations3D)809810Splitting of the compressible Euler flux of Steger and Warming.811812Returns a tuple of the fluxes "minus" (associated with waves going into the813negative axis direction) and "plus" (associated with waves going into the814positive axis direction). If only one of the fluxes is required, use the815function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`.816817!!! warning "Experimental implementation (upwind SBP)"818This is an experimental feature and may change in future releases.819820## References821822- Joseph L. Steger and R. F. Warming (1979)823Flux Vector Splitting of the Inviscid Gasdynamic Equations824With Application to Finite Difference Methods825[NASA Technical Memorandum](https://ntrs.nasa.gov/api/citations/19790020779/downloads/19790020779.pdf)826"""827@inline function splitting_steger_warming(u, orientation::Integer,828equations::CompressibleEulerEquations3D)829fm = splitting_steger_warming(u, Val{:minus}(), orientation, equations)830fp = splitting_steger_warming(u, Val{:plus}(), orientation, equations)831return fm, fp832end833834@inline function splitting_steger_warming(u, ::Val{:plus}, orientation::Integer,835equations::CompressibleEulerEquations3D)836rho, rho_v1, rho_v2, rho_v3, rho_e = u837v1 = rho_v1 / rho838v2 = rho_v2 / rho839v3 = rho_v3 / rho840p = (equations.gamma - 1) *841(rho_e - 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3))842a = sqrt(equations.gamma * p / rho)843844if orientation == 1845lambda1 = v1846lambda2 = v1 + a847lambda3 = v1 - a848849lambda1_p = positive_part(lambda1) # Same as (lambda_i + abs(lambda_i)) / 2, but faster :)850lambda2_p = positive_part(lambda2)851lambda3_p = positive_part(lambda3)852853alpha_p = 2 * (equations.gamma - 1) * lambda1_p + lambda2_p + lambda3_p854855rho_2gamma = 0.5f0 * rho / equations.gamma856f1p = rho_2gamma * alpha_p857f2p = rho_2gamma * (alpha_p * v1 + a * (lambda2_p - lambda3_p))858f3p = rho_2gamma * alpha_p * v2859f4p = rho_2gamma * alpha_p * v3860f5p = rho_2gamma *861(alpha_p * 0.5f0 * (v1^2 + v2^2 + v3^2) +862a * v1 *863(lambda2_p - lambda3_p)864+ a^2 * (lambda2_p + lambda3_p) * equations.inv_gamma_minus_one)865elseif orientation == 2866lambda1 = v2867lambda2 = v2 + a868lambda3 = v2 - a869870lambda1_p = positive_part(lambda1) # Same as (lambda_i + abs(lambda_i)) / 2, but faster :)871lambda2_p = positive_part(lambda2)872lambda3_p = positive_part(lambda3)873874alpha_p = 2 * (equations.gamma - 1) * lambda1_p + lambda2_p + lambda3_p875876rho_2gamma = 0.5f0 * rho / equations.gamma877f1p = rho_2gamma * alpha_p878f2p = rho_2gamma * alpha_p * v1879f3p = rho_2gamma * (alpha_p * v2 + a * (lambda2_p - lambda3_p))880f4p = rho_2gamma * alpha_p * v3881f5p = rho_2gamma *882(alpha_p * 0.5f0 * (v1^2 + v2^2 + v3^2) +883a * v2 *884(lambda2_p - lambda3_p)885+ a^2 * (lambda2_p + lambda3_p) * equations.inv_gamma_minus_one)886else # orientation == 3887lambda1 = v3888lambda2 = v3 + a889lambda3 = v3 - a890891lambda1_p = positive_part(lambda1) # Same as (lambda_i + abs(lambda_i)) / 2, but faster :)892lambda2_p = positive_part(lambda2)893lambda3_p = positive_part(lambda3)894895alpha_p = 2 * (equations.gamma - 1) * lambda1_p + lambda2_p + lambda3_p896897rho_2gamma = 0.5f0 * rho / equations.gamma898f1p = rho_2gamma * alpha_p899f2p = rho_2gamma * alpha_p * v1900f3p = rho_2gamma * alpha_p * v2901f4p = rho_2gamma * (alpha_p * v3 + a * (lambda2_p - lambda3_p))902f5p = rho_2gamma *903(alpha_p * 0.5f0 * (v1^2 + v2^2 + v3^2) +904a * v3 *905(lambda2_p - lambda3_p)906+ a^2 * (lambda2_p + lambda3_p) * equations.inv_gamma_minus_one)907end908return SVector(f1p, f2p, f3p, f4p, f5p)909end910911@inline function splitting_steger_warming(u, ::Val{:minus}, orientation::Integer,912equations::CompressibleEulerEquations3D)913rho, rho_v1, rho_v2, rho_v3, rho_e = u914v1 = rho_v1 / rho915v2 = rho_v2 / rho916v3 = rho_v3 / rho917p = (equations.gamma - 1) *918(rho_e - 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3))919a = sqrt(equations.gamma * p / rho)920921if orientation == 1922lambda1 = v1923lambda2 = v1 + a924lambda3 = v1 - a925926lambda1_m = negative_part(lambda1) # Same as (lambda_i - abs(lambda_i)) / 2, but faster :)927lambda2_m = negative_part(lambda2)928lambda3_m = negative_part(lambda3)929930alpha_m = 2 * (equations.gamma - 1) * lambda1_m + lambda2_m + lambda3_m931932rho_2gamma = 0.5f0 * rho / equations.gamma933f1m = rho_2gamma * alpha_m934f2m = rho_2gamma * (alpha_m * v1 + a * (lambda2_m - lambda3_m))935f3m = rho_2gamma * alpha_m * v2936f4m = rho_2gamma * alpha_m * v3937f5m = rho_2gamma *938(alpha_m * 0.5f0 * (v1^2 + v2^2 + v3^2) +939a * v1 *940(lambda2_m - lambda3_m)941+ a^2 * (lambda2_m + lambda3_m) * equations.inv_gamma_minus_one)942elseif orientation == 2943lambda1 = v2944lambda2 = v2 + a945lambda3 = v2 - a946947lambda1_m = negative_part(lambda1) # Same as (lambda_i - abs(lambda_i)) / 2, but faster :)948lambda2_m = negative_part(lambda2)949lambda3_m = negative_part(lambda3)950951alpha_m = 2 * (equations.gamma - 1) * lambda1_m + lambda2_m + lambda3_m952953rho_2gamma = 0.5f0 * rho / equations.gamma954f1m = rho_2gamma * alpha_m955f2m = rho_2gamma * alpha_m * v1956f3m = rho_2gamma * (alpha_m * v2 + a * (lambda2_m - lambda3_m))957f4m = rho_2gamma * alpha_m * v3958f5m = rho_2gamma *959(alpha_m * 0.5f0 * (v1^2 + v2^2 + v3^2) +960a * v2 *961(lambda2_m - lambda3_m)962+ a^2 * (lambda2_m + lambda3_m) * equations.inv_gamma_minus_one)963else # orientation == 3964lambda1 = v3965lambda2 = v3 + a966lambda3 = v3 - a967968lambda1_m = negative_part(lambda1) # Same as (lambda_i - abs(lambda_i)) / 2, but faster :)969lambda2_m = negative_part(lambda2)970lambda3_m = negative_part(lambda3)971972alpha_m = 2 * (equations.gamma - 1) * lambda1_m + lambda2_m + lambda3_m973974rho_2gamma = 0.5f0 * rho / equations.gamma975f1m = rho_2gamma * alpha_m976f2m = rho_2gamma * alpha_m * v1977f3m = rho_2gamma * alpha_m * v2978f4m = rho_2gamma * (alpha_m * v3 + a * (lambda2_m - lambda3_m))979f5m = rho_2gamma *980(alpha_m * 0.5f0 * (v1^2 + v2^2 + v3^2) +981a * v3 *982(lambda2_m - lambda3_m)983+ a^2 * (lambda2_m + lambda3_m) * equations.inv_gamma_minus_one)984end985return SVector(f1m, f2m, f3m, f4m, f5m)986end987988"""989FluxLMARS(c)(u_ll, u_rr, orientation_or_normal_direction,990equations::CompressibleEulerEquations3D)991992Low Mach number approximate Riemann solver (LMARS) for atmospheric flows using993an estimate `c` of the speed of sound.994995References:996- Xi Chen et al. (2013)997A Control-Volume Model of the Compressible Euler Equations with a Vertical998Lagrangian Coordinate999[DOI: 10.1175/MWR-D-12-00129.1](https://doi.org/10.1175/mwr-d-12-00129.1)1000"""1001@inline function (flux_lmars::FluxLMARS)(u_ll, u_rr, orientation::Integer,1002equations::CompressibleEulerEquations3D)1003c = flux_lmars.speed_of_sound10041005# Unpack left and right state1006rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1007rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)10081009if orientation == 11010v_ll = v1_ll1011v_rr = v1_rr1012elseif orientation == 21013v_ll = v2_ll1014v_rr = v2_rr1015else # orientation == 31016v_ll = v3_ll1017v_rr = v3_rr1018end10191020rho = 0.5f0 * (rho_ll + rho_rr)1021p = 0.5f0 * (p_ll + p_rr) - 0.5f0 * c * rho * (v_rr - v_ll)1022v = 0.5f0 * (v_ll + v_rr) - 1 / (2 * c * rho) * (p_rr - p_ll)10231024# We treat the energy term analogous to the potential temperature term in the paper by1025# Chen et al., i.e. we use p_ll and p_rr, and not p1026if v >= 01027f1, f2, f3, f4, f5 = v * u_ll1028f5 = f5 + p_ll * v1029else1030f1, f2, f3, f4, f5 = v * u_rr1031f5 = f5 + p_rr * v1032end10331034if orientation == 11035f2 += p1036elseif orientation == 21037f3 += p1038else # orientation == 31039f4 += p1040end10411042return SVector(f1, f2, f3, f4, f5)1043end10441045@inline function (flux_lmars::FluxLMARS)(u_ll, u_rr, normal_direction::AbstractVector,1046equations::CompressibleEulerEquations3D)1047c = flux_lmars.speed_of_sound10481049# Unpack left and right state1050rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1051rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)10521053v_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +1054v3_ll * normal_direction[3]1055v_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +1056v3_rr * normal_direction[3]10571058# Note that this is the same as computing v_ll and v_rr with a normalized normal vector1059# and then multiplying v by `norm_` again, but this version is slightly faster.1060norm_ = norm(normal_direction)10611062rho = 0.5f0 * (rho_ll + rho_rr)1063p = 0.5f0 * (p_ll + p_rr) - 0.5f0 * c * rho * (v_rr - v_ll) / norm_1064v = 0.5f0 * (v_ll + v_rr) - 1 / (2 * c * rho) * (p_rr - p_ll) * norm_10651066# We treat the energy term analogous to the potential temperature term in the paper by1067# Chen et al., i.e. we use p_ll and p_rr, and not p1068if v >= 01069f1, f2, f3, f4, f5 = v * u_ll1070f5 = f5 + p_ll * v1071else1072f1, f2, f3, f4, f5 = v * u_rr1073f5 = f5 + p_rr * v1074end10751076return SVector(f1,1077f2 + p * normal_direction[1],1078f3 + p * normal_direction[2],1079f4 + p * normal_direction[3],1080f5)1081end10821083# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation as the1084# maximum velocity magnitude plus the maximum speed of sound1085@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,1086equations::CompressibleEulerEquations3D)1087rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1088rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)10891090# Get the velocity value in the appropriate direction1091if orientation == 11092v_ll = v1_ll1093v_rr = v1_rr1094elseif orientation == 21095v_ll = v2_ll1096v_rr = v2_rr1097else # orientation == 31098v_ll = v3_ll1099v_rr = v3_rr1100end1101# Calculate sound speeds1102c_ll = sqrt(equations.gamma * p_ll / rho_ll)1103c_rr = sqrt(equations.gamma * p_rr / rho_rr)11041105return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr)1106end11071108@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,1109equations::CompressibleEulerEquations3D)1110rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1111rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)11121113# Calculate normal velocities and sound speed1114# left1115v_ll = (v1_ll * normal_direction[1]1116+ v2_ll * normal_direction[2]1117+ v3_ll * normal_direction[3])1118c_ll = sqrt(equations.gamma * p_ll / rho_ll)1119# right1120v_rr = (v1_rr * normal_direction[1]1121+ v2_rr * normal_direction[2]1122+ v3_rr * normal_direction[3])1123c_rr = sqrt(equations.gamma * p_rr / rho_rr)11241125return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) * norm(normal_direction)1126end11271128# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`1129@inline function max_abs_speed(u_ll, u_rr, orientation::Integer,1130equations::CompressibleEulerEquations3D)1131rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1132rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)11331134# Get the velocity value in the appropriate direction1135if orientation == 11136v_ll = v1_ll1137v_rr = v1_rr1138elseif orientation == 21139v_ll = v2_ll1140v_rr = v2_rr1141else # orientation == 31142v_ll = v3_ll1143v_rr = v3_rr1144end1145# Calculate sound speeds1146c_ll = sqrt(equations.gamma * p_ll / rho_ll)1147c_rr = sqrt(equations.gamma * p_rr / rho_rr)11481149return max(abs(v_ll) + c_ll, abs(v_rr) + c_rr)1150end11511152# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`1153@inline function max_abs_speed(u_ll, u_rr, normal_direction::AbstractVector,1154equations::CompressibleEulerEquations3D)1155rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1156rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)11571158# Calculate normal velocities and sound speeds1159# left1160v_ll = (v1_ll * normal_direction[1]1161+ v2_ll * normal_direction[2]1162+ v3_ll * normal_direction[3])1163c_ll = sqrt(equations.gamma * p_ll / rho_ll)1164# right1165v_rr = (v1_rr * normal_direction[1]1166+ v2_rr * normal_direction[2]1167+ v3_rr * normal_direction[3])1168c_rr = sqrt(equations.gamma * p_rr / rho_rr)11691170norm_ = norm(normal_direction)1171return max(abs(v_ll) + c_ll * norm_, abs(v_rr) + c_rr * norm_)1172end11731174# Calculate estimates for minimum and maximum wave speeds for HLL-type fluxes1175@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer,1176equations::CompressibleEulerEquations3D)1177rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1178rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)11791180if orientation == 1 # x-direction1181λ_min = v1_ll - sqrt(equations.gamma * p_ll / rho_ll)1182λ_max = v1_rr + sqrt(equations.gamma * p_rr / rho_rr)1183elseif orientation == 2 # y-direction1184λ_min = v2_ll - sqrt(equations.gamma * p_ll / rho_ll)1185λ_max = v2_rr + sqrt(equations.gamma * p_rr / rho_rr)1186else # z-direction1187λ_min = v3_ll - sqrt(equations.gamma * p_ll / rho_ll)1188λ_max = v3_rr + sqrt(equations.gamma * p_rr / rho_rr)1189end11901191return λ_min, λ_max1192end11931194@inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,1195equations::CompressibleEulerEquations3D)1196rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1197rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)11981199v_normal_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +1200v3_ll * normal_direction[3]1201v_normal_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +1202v3_rr * normal_direction[3]12031204norm_ = norm(normal_direction)1205# The v_normals are already scaled by the norm1206λ_min = v_normal_ll - sqrt(equations.gamma * p_ll / rho_ll) * norm_1207λ_max = v_normal_rr + sqrt(equations.gamma * p_rr / rho_rr) * norm_12081209return λ_min, λ_max1210end12111212# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes1213@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,1214equations::CompressibleEulerEquations3D)1215rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1216rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)12171218c_ll = sqrt(equations.gamma * p_ll / rho_ll)1219c_rr = sqrt(equations.gamma * p_rr / rho_rr)12201221if orientation == 1 # x-direction1222λ_min = min(v1_ll - c_ll, v1_rr - c_rr)1223λ_max = max(v1_ll + c_ll, v1_rr + c_rr)1224elseif orientation == 2 # y-direction1225λ_min = min(v2_ll - c_ll, v2_rr - c_rr)1226λ_max = max(v2_ll + c_ll, v2_rr + c_rr)1227else # z-direction1228λ_min = min(v3_ll - c_ll, v3_rr - c_rr)1229λ_max = max(v3_ll + c_ll, v3_rr + c_rr)1230end12311232return λ_min, λ_max1233end12341235# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes1236@inline function min_max_speed_davis(u_ll, u_rr, normal_direction::AbstractVector,1237equations::CompressibleEulerEquations3D)1238rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1239rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)12401241norm_ = norm(normal_direction)12421243c_ll = sqrt(equations.gamma * p_ll / rho_ll) * norm_1244c_rr = sqrt(equations.gamma * p_rr / rho_rr) * norm_12451246v_normal_ll = v1_ll * normal_direction[1] +1247v2_ll * normal_direction[2] +1248v3_ll * normal_direction[3]1249v_normal_rr = v1_rr * normal_direction[1] +1250v2_rr * normal_direction[2] +1251v3_rr * normal_direction[3]12521253# The v_normals are already scaled by the norm1254λ_min = min(v_normal_ll - c_ll, v_normal_rr - c_rr)1255λ_max = max(v_normal_ll + c_ll, v_normal_rr + c_rr)12561257return λ_min, λ_max1258end12591260# Rotate normal vector to x-axis; normal, tangent1 and tangent2 need to be orthonormal1261# Called inside `FluxRotated` in `numerical_fluxes.jl` so the directions1262# has been normalized prior to this rotation of the state vector1263@inline function rotate_to_x(u, normal_vector, tangent1, tangent2,1264equations::CompressibleEulerEquations3D)1265# Multiply with [ 1 0 0 0 0;1266# 0 ― normal_vector ― 0;1267# 0 ― tangent1 ― 0;1268# 0 ― tangent2 ― 0;1269# 0 0 0 0 1 ]1270return SVector(u[1],1271normal_vector[1] * u[2] + normal_vector[2] * u[3] +1272normal_vector[3] * u[4],1273tangent1[1] * u[2] + tangent1[2] * u[3] + tangent1[3] * u[4],1274tangent2[1] * u[2] + tangent2[2] * u[3] + tangent2[3] * u[4],1275u[5])1276end12771278@inline function (dissipation::DissipationMatrixWintersEtal)(u_ll, u_rr,1279normal_direction::AbstractVector,1280equations::CompressibleEulerEquations3D)1281(; gamma) = equations12821283# Step 1:1284# Rotate solution into the appropriate direction12851286norm_ = norm(normal_direction)1287# Normalize the vector without using `normalize` since we need to multiply by the `norm_` later1288normal_vector = normal_direction / norm_12891290# Some vector that can't be identical to normal_vector (unless normal_vector == 0)1291tangent1 = SVector(normal_direction[2], normal_direction[3], -normal_direction[1])1292# Orthogonal projection1293tangent1 -= dot(normal_vector, tangent1) * normal_vector1294tangent1 = normalize(tangent1)12951296# Third orthogonal vector1297tangent2 = normalize(cross(normal_direction, tangent1))12981299u_ll_rotated = rotate_to_x(u_ll, normal_vector, tangent1, tangent2, equations)1300u_rr_rotated = rotate_to_x(u_rr, normal_vector, tangent1, tangent2, equations)13011302# Step 2:1303# Compute the averages using the rotated variables1304rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll_rotated, equations)1305rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr_rotated, equations)13061307b_ll = rho_ll / (2 * p_ll)1308b_rr = rho_rr / (2 * p_rr)13091310rho_log = ln_mean(rho_ll, rho_rr)1311b_log = ln_mean(b_ll, b_rr)1312v1_avg = 0.5f0 * (v1_ll + v1_rr)1313v2_avg = 0.5f0 * (v2_ll + v2_rr)1314v3_avg = 0.5f0 * (v3_ll + v3_rr)1315p_avg = 0.5f0 * (rho_ll + rho_rr) / (b_ll + b_rr)1316v_squared_bar = v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr1317h_bar = gamma / (2 * b_log * (gamma - 1)) + 0.5f0 * v_squared_bar1318c_bar = sqrt(gamma * p_avg / rho_log)13191320# Step 3:1321# Build the dissipation term as given in Appendix A of the paper1322# - A. R. Winters, D. Derigs, G. Gassner, S. Walch, A uniquely defined entropy stable matrix dissipation operator1323# for high Mach number ideal MHD and compressible Euler simulations (2017). Journal of Computational Physics.1324# [DOI: 10.1016/j.jcp.2016.12.006](https://doi.org/10.1016/j.jcp.2016.12.006).13251326# Get entropy variables jump in the rotated variables1327w_jump = cons2entropy(u_rr_rotated, equations) -1328cons2entropy(u_ll_rotated, equations)13291330# Entries of the diagonal scaling matrix where D = ABS(\Lambda)T1331lambda_1 = abs(v1_avg - c_bar) * rho_log / (2 * gamma)1332lambda_2 = abs(v1_avg) * rho_log * (gamma - 1) / gamma1333lambda_3 = abs(v1_avg) * p_avg # scaled repeated eigenvalue in the tangential direction1334lambda_5 = abs(v1_avg + c_bar) * rho_log / (2 * gamma)1335D = SVector(lambda_1, lambda_2, lambda_3, lambda_3, lambda_5)13361337# Entries of the right eigenvector matrix (others have already been precomputed)1338r21 = v1_avg - c_bar1339r25 = v1_avg + c_bar1340r51 = h_bar - v1_avg * c_bar1341r52 = 0.5f0 * v_squared_bar1342r55 = h_bar + v1_avg * c_bar13431344# Build R and transpose of R matrices1345R = @SMatrix [[1;; 1;; 0;; 0;; 1];1346[r21;; v1_avg;; 0;; 0;; r25];1347[v2_avg;; v2_avg;; 1;; 0;; v2_avg];1348[v3_avg;; v3_avg;; 0;; 1;; v3_avg];1349[r51;; r52;; v2_avg;; v3_avg;; r55]]13501351RT = @SMatrix [[1;; r21;; v2_avg;; v3_avg;; r51];1352[1;; v1_avg;; v2_avg;; v3_avg;; r52];1353[0;; 0;; 1;; 0;; v2_avg];1354[0;; 0;; 0;; 1;; v3_avg];1355[1;; r25;; v2_avg;; v3_avg;; r55]]13561357# Compute the dissipation term R * D * R^T * [[w]] from right-to-left13581359# First comes R^T * [[w]]1360diss = RT * w_jump1361# Next multiply with the eigenvalues and Barth scaling1362diss = D .* diss1363# Finally apply the remaining eigenvector matrix1364diss = R * diss13651366# Step 4:1367# Do not forget to backrotate and then return with proper normalization scaling1368return -0.5f0 * rotate_from_x(diss, normal_vector, tangent1, tangent2, equations) *1369norm_1370end13711372# Rotate x-axis to normal vector; normal, tangent1 and tangent2 need to be orthonormal1373# Called inside `FluxRotated` in `numerical_fluxes.jl` so the directions1374# has been normalized prior to this back-rotation of the state vector1375@inline function rotate_from_x(u, normal_vector, tangent1, tangent2,1376equations::CompressibleEulerEquations3D)1377# Multiply with [ 1 0 0 0 0;1378# 0 | | | 0;1379# 0 normal_vector tangent1 tangent2 0;1380# 0 | | | 0;1381# 0 0 0 0 1 ]1382return SVector(u[1],1383normal_vector[1] * u[2] + tangent1[1] * u[3] + tangent2[1] * u[4],1384normal_vector[2] * u[2] + tangent1[2] * u[3] + tangent2[2] * u[4],1385normal_vector[3] * u[2] + tangent1[3] * u[3] + tangent2[3] * u[4],1386u[5])1387end13881389"""1390flux_hllc(u_ll, u_rr, orientation_or_normal_direction, equations::CompressibleEulerEquations3D)13911392Computes the HLLC flux (HLL with Contact) for compressible Euler equations developed by E.F. Toro1393[Lecture slides](http://www.prague-sum.com/download/2012/Toro_2-HLLC-RiemannSolver.pdf)1394Signal speeds: [DOI: 10.1137/S1064827593260140](https://doi.org/10.1137/S1064827593260140)1395"""1396function flux_hllc(u_ll, u_rr, orientation::Integer,1397equations::CompressibleEulerEquations3D)1398# Calculate primitive variables and speed of sound1399rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll = u_ll1400rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr = u_rr14011402v1_ll = rho_v1_ll / rho_ll1403v2_ll = rho_v2_ll / rho_ll1404v3_ll = rho_v3_ll / rho_ll1405e_ll = rho_e_ll / rho_ll1406p_ll = (equations.gamma - 1) *1407(rho_e_ll - 0.5f0 * rho_ll * (v1_ll^2 + v2_ll^2 + v3_ll^2))1408c_ll = sqrt(equations.gamma * p_ll / rho_ll)14091410v1_rr = rho_v1_rr / rho_rr1411v2_rr = rho_v2_rr / rho_rr1412v3_rr = rho_v3_rr / rho_rr1413e_rr = rho_e_rr / rho_rr1414p_rr = (equations.gamma - 1) *1415(rho_e_rr - 0.5f0 * rho_rr * (v1_rr^2 + v2_rr^2 + v3_rr^2))1416c_rr = sqrt(equations.gamma * p_rr / rho_rr)14171418# Obtain left and right fluxes1419f_ll = flux(u_ll, orientation, equations)1420f_rr = flux(u_rr, orientation, equations)14211422# Compute Roe averages1423sqrt_rho_ll = sqrt(rho_ll)1424sqrt_rho_rr = sqrt(rho_rr)1425sum_sqrt_rho = sqrt_rho_ll + sqrt_rho_rr1426if orientation == 1 # x-direction1427vel_L = v1_ll1428vel_R = v1_rr1429elseif orientation == 2 # y-direction1430vel_L = v2_ll1431vel_R = v2_rr1432else # z-direction1433vel_L = v3_ll1434vel_R = v3_rr1435end1436vel_roe = (sqrt_rho_ll * vel_L + sqrt_rho_rr * vel_R) / sum_sqrt_rho1437v1_roe = sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr1438v2_roe = sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr1439v3_roe = sqrt_rho_ll * v3_ll + sqrt_rho_rr * v3_rr1440vel_roe_mag = (v1_roe^2 + v2_roe^2 + v3_roe^2) / sum_sqrt_rho^21441H_ll = (rho_e_ll + p_ll) / rho_ll1442H_rr = (rho_e_rr + p_rr) / rho_rr1443H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) / sum_sqrt_rho1444c_roe = sqrt((equations.gamma - 1) * (H_roe - 0.5f0 * vel_roe_mag))1445Ssl = min(vel_L - c_ll, vel_roe - c_roe)1446Ssr = max(vel_R + c_rr, vel_roe + c_roe)1447sMu_L = Ssl - vel_L1448sMu_R = Ssr - vel_R14491450if Ssl >= 01451f1 = f_ll[1]1452f2 = f_ll[2]1453f3 = f_ll[3]1454f4 = f_ll[4]1455f5 = f_ll[5]1456elseif Ssr <= 01457f1 = f_rr[1]1458f2 = f_rr[2]1459f3 = f_rr[3]1460f4 = f_rr[4]1461f5 = f_rr[5]1462else1463SStar = (p_rr - p_ll + rho_ll * vel_L * sMu_L - rho_rr * vel_R * sMu_R) /1464(rho_ll * sMu_L - rho_rr * sMu_R)1465if Ssl <= 0 <= SStar1466densStar = rho_ll * sMu_L / (Ssl - SStar)1467enerStar = e_ll + (SStar - vel_L) * (SStar + p_ll / (rho_ll * sMu_L))1468UStar1 = densStar1469UStar5 = densStar * enerStar1470if orientation == 1 # x-direction1471UStar2 = densStar * SStar1472UStar3 = densStar * v2_ll1473UStar4 = densStar * v3_ll1474elseif orientation == 2 # y-direction1475UStar2 = densStar * v1_ll1476UStar3 = densStar * SStar1477UStar4 = densStar * v3_ll1478else # z-direction1479UStar2 = densStar * v1_ll1480UStar3 = densStar * v2_ll1481UStar4 = densStar * SStar1482end1483f1 = f_ll[1] + Ssl * (UStar1 - rho_ll)1484f2 = f_ll[2] + Ssl * (UStar2 - rho_v1_ll)1485f3 = f_ll[3] + Ssl * (UStar3 - rho_v2_ll)1486f4 = f_ll[4] + Ssl * (UStar4 - rho_v3_ll)1487f5 = f_ll[5] + Ssl * (UStar5 - rho_e_ll)1488else1489densStar = rho_rr * sMu_R / (Ssr - SStar)1490enerStar = e_rr + (SStar - vel_R) * (SStar + p_rr / (rho_rr * sMu_R))1491UStar1 = densStar1492UStar5 = densStar * enerStar1493if orientation == 1 # x-direction1494UStar2 = densStar * SStar1495UStar3 = densStar * v2_rr1496UStar4 = densStar * v3_rr1497elseif orientation == 2 # y-direction1498UStar2 = densStar * v1_rr1499UStar3 = densStar * SStar1500UStar4 = densStar * v3_rr1501else # z-direction1502UStar2 = densStar * v1_rr1503UStar3 = densStar * v2_rr1504UStar4 = densStar * SStar1505end1506f1 = f_rr[1] + Ssr * (UStar1 - rho_rr)1507f2 = f_rr[2] + Ssr * (UStar2 - rho_v1_rr)1508f3 = f_rr[3] + Ssr * (UStar3 - rho_v2_rr)1509f4 = f_rr[4] + Ssr * (UStar4 - rho_v3_rr)1510f5 = f_rr[5] + Ssr * (UStar5 - rho_e_rr)1511end1512end1513return SVector(f1, f2, f3, f4, f5)1514end15151516function flux_hllc(u_ll, u_rr, normal_direction::AbstractVector,1517equations::CompressibleEulerEquations3D)1518# Calculate primitive variables and speed of sound1519rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1520rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)15211522v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +1523v3_ll * normal_direction[3]1524v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +1525v3_rr * normal_direction[3]15261527norm_ = norm(normal_direction)1528norm_sq = norm_ * norm_1529inv_norm_sq = inv(norm_sq)15301531c_ll = sqrt(equations.gamma * p_ll / rho_ll) * norm_1532c_rr = sqrt(equations.gamma * p_rr / rho_rr) * norm_15331534# Obtain left and right fluxes1535f_ll = flux(u_ll, normal_direction, equations)1536f_rr = flux(u_rr, normal_direction, equations)15371538# Compute Roe averages1539sqrt_rho_ll = sqrt(rho_ll)1540sqrt_rho_rr = sqrt(rho_rr)1541sum_sqrt_rho = sqrt_rho_ll + sqrt_rho_rr15421543v1_roe = (sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr) / sum_sqrt_rho1544v2_roe = (sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr) / sum_sqrt_rho1545v3_roe = (sqrt_rho_ll * v3_ll + sqrt_rho_rr * v3_rr) / sum_sqrt_rho1546vel_roe = v1_roe * normal_direction[1] + v2_roe * normal_direction[2] +1547v3_roe * normal_direction[3]1548vel_roe_mag = v1_roe^2 + v2_roe^2 + v3_roe^215491550e_ll = u_ll[5] / rho_ll1551e_rr = u_rr[5] / rho_rr15521553H_ll = (u_ll[5] + p_ll) / rho_ll1554H_rr = (u_rr[5] + p_rr) / rho_rr15551556H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) / sum_sqrt_rho1557c_roe = sqrt((equations.gamma - 1) * (H_roe - 0.5f0 * vel_roe_mag)) * norm_15581559Ssl = min(v_dot_n_ll - c_ll, vel_roe - c_roe)1560Ssr = max(v_dot_n_rr + c_rr, vel_roe + c_roe)1561sMu_L = Ssl - v_dot_n_ll1562sMu_R = Ssr - v_dot_n_rr15631564if Ssl >= 01565f1 = f_ll[1]1566f2 = f_ll[2]1567f3 = f_ll[3]1568f4 = f_ll[4]1569f5 = f_ll[5]1570elseif Ssr <= 01571f1 = f_rr[1]1572f2 = f_rr[2]1573f3 = f_rr[3]1574f4 = f_rr[4]1575f5 = f_rr[5]1576else1577SStar = (rho_ll * v_dot_n_ll * sMu_L - rho_rr * v_dot_n_rr * sMu_R +1578(p_rr - p_ll) * norm_sq) / (rho_ll * sMu_L - rho_rr * sMu_R)1579if Ssl <= 0 <= SStar1580densStar = rho_ll * sMu_L / (Ssl - SStar)1581enerStar = e_ll +1582(SStar - v_dot_n_ll) *1583(SStar * inv_norm_sq + p_ll / (rho_ll * sMu_L))1584UStar1 = densStar1585UStar2 = densStar *1586(v1_ll + (SStar - v_dot_n_ll) * normal_direction[1] * inv_norm_sq)1587UStar3 = densStar *1588(v2_ll + (SStar - v_dot_n_ll) * normal_direction[2] * inv_norm_sq)1589UStar4 = densStar *1590(v3_ll + (SStar - v_dot_n_ll) * normal_direction[3] * inv_norm_sq)1591UStar5 = densStar * enerStar1592f1 = f_ll[1] + Ssl * (UStar1 - u_ll[1])1593f2 = f_ll[2] + Ssl * (UStar2 - u_ll[2])1594f3 = f_ll[3] + Ssl * (UStar3 - u_ll[3])1595f4 = f_ll[4] + Ssl * (UStar4 - u_ll[4])1596f5 = f_ll[5] + Ssl * (UStar5 - u_ll[5])1597else1598densStar = rho_rr * sMu_R / (Ssr - SStar)1599enerStar = e_rr +1600(SStar - v_dot_n_rr) *1601(SStar * inv_norm_sq + p_rr / (rho_rr * sMu_R))1602UStar1 = densStar1603UStar2 = densStar *1604(v1_rr + (SStar - v_dot_n_rr) * normal_direction[1] * inv_norm_sq)1605UStar3 = densStar *1606(v2_rr + (SStar - v_dot_n_rr) * normal_direction[2] * inv_norm_sq)1607UStar4 = densStar *1608(v3_rr + (SStar - v_dot_n_rr) * normal_direction[3] * inv_norm_sq)1609UStar5 = densStar * enerStar1610f1 = f_rr[1] + Ssr * (UStar1 - u_rr[1])1611f2 = f_rr[2] + Ssr * (UStar2 - u_rr[2])1612f3 = f_rr[3] + Ssr * (UStar3 - u_rr[3])1613f4 = f_rr[4] + Ssr * (UStar4 - u_rr[4])1614f5 = f_rr[5] + Ssr * (UStar5 - u_rr[5])1615end1616end1617return SVector(f1, f2, f3, f4, f5)1618end16191620"""1621min_max_speed_einfeldt(u_ll, u_rr, orientation, equations::CompressibleEulerEquations3D)16221623Computes the HLLE (Harten-Lax-van Leer-Einfeldt) flux for the compressible Euler equations.1624Special estimates of the signal velocites and linearization of the Riemann problem developed1625by Einfeldt to ensure that the internal energy and density remain positive during the computation1626of the numerical flux.16271628- Bernd Einfeldt (1988)1629On Godunov-type methods for gas dynamics.1630[DOI: 10.1137/0725021](https://doi.org/10.1137/0725021)1631- Bernd Einfeldt, Claus-Dieter Munz, Philip L. Roe and Björn Sjögreen (1991)1632On Godunov-type methods near low densities.1633[DOI: 10.1016/0021-9991(91)90211-3](https://doi.org/10.1016/0021-9991(91)90211-3)1634"""1635@inline function min_max_speed_einfeldt(u_ll, u_rr, orientation::Integer,1636equations::CompressibleEulerEquations3D)1637# Calculate primitive variables, enthalpy and speed of sound1638rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1639rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)16401641# `u_ll[5]` is total energy `rho_e_ll` on the left1642H_ll = (u_ll[5] + p_ll) / rho_ll1643c_ll = sqrt(equations.gamma * p_ll / rho_ll)16441645# `u_rr[5]` is total energy `rho_e_rr` on the right1646H_rr = (u_rr[5] + p_rr) / rho_rr1647c_rr = sqrt(equations.gamma * p_rr / rho_rr)16481649# Compute Roe averages1650sqrt_rho_ll = sqrt(rho_ll)1651sqrt_rho_rr = sqrt(rho_rr)1652inv_sum_sqrt_rho = inv(sqrt_rho_ll + sqrt_rho_rr)16531654v1_roe = (sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr) * inv_sum_sqrt_rho1655v2_roe = (sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr) * inv_sum_sqrt_rho1656v3_roe = (sqrt_rho_ll * v3_ll + sqrt_rho_rr * v3_rr) * inv_sum_sqrt_rho1657v_roe_mag = v1_roe^2 + v2_roe^2 + v3_roe^216581659H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) * inv_sum_sqrt_rho1660c_roe = sqrt((equations.gamma - 1) * (H_roe - 0.5f0 * v_roe_mag))16611662# Compute convenience constant for positivity preservation, see1663# https://doi.org/10.1016/0021-9991(91)90211-31664beta = sqrt(0.5f0 * (equations.gamma - 1) / equations.gamma)16651666# Estimate the edges of the Riemann fan (with positivity conservation)1667if orientation == 1 # x-direction1668SsL = min(v1_roe - c_roe, v1_ll - beta * c_ll, 0)1669SsR = max(v1_roe + c_roe, v1_rr + beta * c_rr, 0)1670elseif orientation == 2 # y-direction1671SsL = min(v2_roe - c_roe, v2_ll - beta * c_ll, 0)1672SsR = max(v2_roe + c_roe, v2_rr + beta * c_rr, 0)1673else # z-direction1674SsL = min(v3_roe - c_roe, v3_ll - beta * c_ll, 0)1675SsR = max(v3_roe + c_roe, v3_rr + beta * c_rr, 0)1676end16771678return SsL, SsR1679end16801681"""1682min_max_speed_einfeldt(u_ll, u_rr, normal_direction, equations::CompressibleEulerEquations3D)16831684Computes the HLLE (Harten-Lax-van Leer-Einfeldt) flux for the compressible Euler equations.1685Special estimates of the signal velocites and linearization of the Riemann problem developed1686by Einfeldt to ensure that the internal energy and density remain positive during the computation1687of the numerical flux.16881689- Bernd Einfeldt (1988)1690On Godunov-type methods for gas dynamics.1691[DOI: 10.1137/0725021](https://doi.org/10.1137/0725021)1692- Bernd Einfeldt, Claus-Dieter Munz, Philip L. Roe and Björn Sjögreen (1991)1693On Godunov-type methods near low densities.1694[DOI: 10.1016/0021-9991(91)90211-3](https://doi.org/10.1016/0021-9991(91)90211-3)1695"""1696@inline function min_max_speed_einfeldt(u_ll, u_rr, normal_direction::AbstractVector,1697equations::CompressibleEulerEquations3D)1698# Calculate primitive variables, enthalpy and speed of sound1699rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)1700rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)17011702v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +1703v3_ll * normal_direction[3]1704v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +1705v3_rr * normal_direction[3]17061707norm_ = norm(normal_direction)17081709# `u_ll[5]` is total energy `rho_e_ll` on the left1710H_ll = (u_ll[5] + p_ll) / rho_ll1711c_ll = sqrt(equations.gamma * p_ll / rho_ll) * norm_17121713# `u_rr[5]` is total energy `rho_e_rr` on the right1714H_rr = (u_rr[5] + p_rr) / rho_rr1715c_rr = sqrt(equations.gamma * p_rr / rho_rr) * norm_17161717# Compute Roe averages1718sqrt_rho_ll = sqrt(rho_ll)1719sqrt_rho_rr = sqrt(rho_rr)1720inv_sum_sqrt_rho = inv(sqrt_rho_ll + sqrt_rho_rr)17211722v1_roe = (sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr) * inv_sum_sqrt_rho1723v2_roe = (sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr) * inv_sum_sqrt_rho1724v3_roe = (sqrt_rho_ll * v3_ll + sqrt_rho_rr * v3_rr) * inv_sum_sqrt_rho1725v_roe = v1_roe * normal_direction[1] + v2_roe * normal_direction[2] +1726v3_roe * normal_direction[3]1727v_roe_mag = v1_roe^2 + v2_roe^2 + v3_roe^217281729H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) * inv_sum_sqrt_rho1730c_roe = sqrt((equations.gamma - 1) * (H_roe - 0.5f0 * v_roe_mag)) * norm_17311732# Compute convenience constant for positivity preservation, see1733# https://doi.org/10.1016/0021-9991(91)90211-31734beta = sqrt(0.5f0 * (equations.gamma - 1) / equations.gamma)17351736# Estimate the edges of the Riemann fan (with positivity conservation)1737SsL = min(v_roe - c_roe, v_dot_n_ll - beta * c_ll, 0)1738SsR = max(v_roe + c_roe, v_dot_n_rr + beta * c_rr, 0)17391740return SsL, SsR1741end17421743@inline function max_abs_speeds(u, equations::CompressibleEulerEquations3D)1744rho, v1, v2, v3, p = cons2prim(u, equations)1745c = sqrt(equations.gamma * p / rho)17461747return abs(v1) + c, abs(v2) + c, abs(v3) + c1748end17491750# Convert conservative variables to primitive1751@inline function cons2prim(u, equations::CompressibleEulerEquations3D)1752rho, rho_v1, rho_v2, rho_v3, rho_e = u17531754v1 = rho_v1 / rho1755v2 = rho_v2 / rho1756v3 = rho_v3 / rho1757p = (equations.gamma - 1) *1758(rho_e - 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3))17591760return SVector(rho, v1, v2, v3, p)1761end17621763# Convert conservative variables to entropy1764@inline function cons2entropy(u, equations::CompressibleEulerEquations3D)1765rho, rho_v1, rho_v2, rho_v3, rho_e = u17661767v1 = rho_v1 / rho1768v2 = rho_v2 / rho1769v3 = rho_v3 / rho1770v_square = v1^2 + v2^2 + v3^21771p = (equations.gamma - 1) * (rho_e - 0.5f0 * rho * v_square)1772s = log(p) - equations.gamma * log(rho)1773rho_p = rho / p17741775w1 = (equations.gamma - s) * equations.inv_gamma_minus_one -17760.5f0 * rho_p * v_square1777w2 = rho_p * v11778w3 = rho_p * v21779w4 = rho_p * v31780w5 = -rho_p17811782return SVector(w1, w2, w3, w4, w5)1783end17841785@inline function entropy2cons(w, equations::CompressibleEulerEquations3D)1786# See Hughes, Franca, Mallet (1986) A new finite element formulation for CFD1787# [DOI: 10.1016/0045-7825(86)90127-1](https://doi.org/10.1016/0045-7825(86)90127-1)1788@unpack gamma = equations17891790# convert to entropy `-rho * s` used by Hughes, France, Mallet (1986)1791# instead of `-rho * s / (gamma - 1)`1792V1, V2, V3, V4, V5 = w .* (gamma - 1)17931794# s = specific entropy, eq. (53)1795V_square = V2^2 + V3^2 + V4^21796s = gamma - V1 + V_square / (2 * V5)17971798# eq. (52)1799rho_iota = ((gamma - 1) / (-V5)^gamma)^(equations.inv_gamma_minus_one) *1800exp(-s * equations.inv_gamma_minus_one)18011802# eq. (51)1803rho = -rho_iota * V51804rho_v1 = rho_iota * V21805rho_v2 = rho_iota * V31806rho_v3 = rho_iota * V41807rho_e = rho_iota * (1 - V_square / (2 * V5))1808return SVector(rho, rho_v1, rho_v2, rho_v3, rho_e)1809end18101811# Convert primitive to conservative variables1812@inline function prim2cons(prim, equations::CompressibleEulerEquations3D)1813rho, v1, v2, v3, p = prim1814rho_v1 = rho * v11815rho_v2 = rho * v21816rho_v3 = rho * v31817rho_e = p * equations.inv_gamma_minus_one +18180.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3)1819return SVector(rho, rho_v1, rho_v2, rho_v3, rho_e)1820end18211822@inline function density(u, equations::CompressibleEulerEquations3D)1823rho = u[1]1824return rho1825end18261827@inline function velocity(u, equations::CompressibleEulerEquations3D)1828rho = u[1]1829v1 = u[2] / rho1830v2 = u[3] / rho1831v3 = u[4] / rho1832return SVector(v1, v2, v3)1833end18341835@inline function velocity(u, orientation::Int, equations::CompressibleEulerEquations3D)1836rho = u[1]1837v = u[orientation + 1] / rho1838return v1839end18401841@inline function pressure(u, equations::CompressibleEulerEquations3D)1842rho, rho_v1, rho_v2, rho_v3, rho_e = u1843p = (equations.gamma - 1) * (rho_e - 0.5f0 * (rho_v1^2 + rho_v2^2 + rho_v3^2) / rho)1844return p1845end18461847@inline function density_pressure(u, equations::CompressibleEulerEquations3D)1848rho, rho_v1, rho_v2, rho_v3, rho_e = u1849rho_times_p = (equations.gamma - 1) *1850(rho * rho_e - 0.5f0 * (rho_v1^2 + rho_v2^2 + rho_v3^2))1851return rho_times_p1852end18531854# Calculate thermodynamic entropy for a conservative state `u`1855@inline function entropy_thermodynamic(u, equations::CompressibleEulerEquations3D)1856rho, _ = u1857p = pressure(u, equations)18581859# Thermodynamic entropy1860s = log(p) - equations.gamma * log(rho)18611862return s1863end18641865# Calculate mathematical entropy for a conservative state `cons`1866@inline function entropy_math(cons, equations::CompressibleEulerEquations3D)1867S = -entropy_thermodynamic(cons, equations) * cons[1] *1868equations.inv_gamma_minus_one1869# Mathematical entropy18701871return S1872end18731874# Default entropy is the mathematical entropy1875@inline function entropy(cons, equations::CompressibleEulerEquations3D)1876entropy_math(cons, equations)1877end18781879# Calculate total energy for a conservative state `cons`1880@inline energy_total(cons, ::CompressibleEulerEquations3D) = cons[5]18811882# Calculate kinetic energy for a conservative state `cons`1883@inline function energy_kinetic(u, equations::CompressibleEulerEquations3D)1884rho, rho_v1, rho_v2, rho_v3, _ = u1885return 0.5f0 * (rho_v1^2 + rho_v2^2 + rho_v3^2) / rho1886end18871888# Calculate internal energy for a conservative state `cons`1889@inline function energy_internal(cons, equations::CompressibleEulerEquations3D)1890return energy_total(cons, equations) - energy_kinetic(cons, equations)1891end1892end # @muladd189318941895