Path: blob/main/src/equations/compressible_euler_1d.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"""8CompressibleEulerEquations1D(gamma)910The compressible Euler equations11```math12\frac{\partial}{\partial t}13\begin{pmatrix}14\rho \\ \rho v_1 \\ \rho e15\end{pmatrix}16+17\frac{\partial}{\partial x}18\begin{pmatrix}19\rho v_1 \\ \rho v_1^2 + p \\ (\rho e +p) v_120\end{pmatrix}21=22\begin{pmatrix}230 \\ 0 \\ 024\end{pmatrix}25```26for an ideal gas with ratio of specific heats `gamma` in one space dimension.27Here, ``\rho`` is the density, ``v_1`` the velocity, ``e`` the specific total energy **rather than** specific internal energy, and28```math29p = (\gamma - 1) \left( \rho e - \frac{1}{2} \rho v_1^2 \right)30```31the pressure.32"""33struct CompressibleEulerEquations1D{RealT <: Real} <:34AbstractCompressibleEulerEquations{1, 3}35gamma::RealT # ratio of specific heats36inv_gamma_minus_one::RealT # = inv(gamma - 1); can be used to write slow divisions as fast multiplications3738function CompressibleEulerEquations1D(gamma)39γ, inv_gamma_minus_one = promote(gamma, inv(gamma - 1))40new{typeof(γ)}(γ, inv_gamma_minus_one)41end42end4344function varnames(::typeof(cons2cons), ::CompressibleEulerEquations1D)45("rho", "rho_v1", "rho_e")46end47varnames(::typeof(cons2prim), ::CompressibleEulerEquations1D) = ("rho", "v1", "p")4849"""50initial_condition_constant(x, t, equations::CompressibleEulerEquations1D)5152A constant initial condition to test free-stream preservation.53"""54function initial_condition_constant(x, t, equations::CompressibleEulerEquations1D)55RealT = eltype(x)56rho = 157rho_v1 = convert(RealT, 0.1)58rho_e = 1059return SVector(rho, rho_v1, rho_e)60end6162"""63initial_condition_convergence_test(x, t, equations::CompressibleEulerEquations1D)6465A smooth initial condition used for convergence tests in combination with66[`source_terms_convergence_test`](@ref)67(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains).68"""69function initial_condition_convergence_test(x, t,70equations::CompressibleEulerEquations1D)71RealT = eltype(x)72c = 273A = convert(RealT, 0.1)74L = 275f = 1.0f0 / L76ω = 2 * convert(RealT, pi) * f77ini = c + A * sin(ω * (x[1] - t))7879rho = ini80rho_v1 = ini81rho_e = ini^28283return SVector(rho, rho_v1, rho_e)84end8586"""87source_terms_convergence_test(u, x, t, equations::CompressibleEulerEquations1D)8889Source terms used for convergence tests in combination with90[`initial_condition_convergence_test`](@ref)91(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains).92"""93@inline function source_terms_convergence_test(u, x, t,94equations::CompressibleEulerEquations1D)95# Same settings as in `initial_condition`96RealT = eltype(u)97c = 298A = convert(RealT, 0.1)99L = 2100f = 1.0f0 / L101ω = 2 * convert(RealT, pi) * f102γ = equations.gamma103104x1, = x105106si, co = sincos(ω * (x1 - t))107rho = c + A * si108rho_x = ω * A * co109110# Note that d/dt rho = -d/dx rho.111# This yields du2 = du3 = d/dx p (derivative of pressure).112# Other terms vanish because of v = 1.113du1 = 0114du2 = rho_x * (2 * rho - 0.5f0) * (γ - 1)115du3 = du2116117return SVector(du1, du2, du3)118end119120"""121initial_condition_density_wave(x, t, equations::CompressibleEulerEquations1D)122123A sine wave in the density with constant velocity and pressure; reduces the124compressible Euler equations to the linear advection equations.125This setup is the test case for stability of EC fluxes from paper126- Gregor J. Gassner, Magnus Svärd, Florian J. Hindenlang (2020)127Stability issues of entropy-stable and/or split-form high-order schemes128[arXiv: 2007.09026](https://arxiv.org/abs/2007.09026)129with the following parameters130- domain [-1, 1]131- mesh = 4x4132- polydeg = 5133"""134function initial_condition_density_wave(x, t, equations::CompressibleEulerEquations1D)135RealT = eltype(x)136v1 = convert(RealT, 0.1)137rho = 1 + convert(RealT, 0.98) * sinpi(2 * (x[1] - t * v1))138rho_v1 = rho * v1139p = 20140rho_e = p / (equations.gamma - 1) + 0.5f0 * rho * v1^2141return SVector(rho, rho_v1, rho_e)142end143144"""145initial_condition_weak_blast_wave(x, t, equations::CompressibleEulerEquations1D)146147A weak blast wave taken from148- Sebastian Hennemann, Gregor J. Gassner (2020)149A provably entropy stable subcell shock capturing approach for high order split form DG150[arXiv: 2008.12044](https://arxiv.org/abs/2008.12044)151"""152function initial_condition_weak_blast_wave(x, t,153equations::CompressibleEulerEquations1D)154# From Hennemann & Gassner JCP paper 2020 (Sec. 6.3)155# Set up polar coordinates156RealT = eltype(x)157inicenter = SVector(0)158x_norm = x[1] - inicenter[1]159r = abs(x_norm)160# The following code is equivalent to161# phi = atan(0.0, x_norm)162# cos_phi = cos(phi)163# in 1D but faster164cos_phi = x_norm > 0 ? 1 : -1165166# Calculate primitive variables167rho = r > 0.5f0 ? one(RealT) : convert(RealT, 1.1691)168v1 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos_phi169p = r > 0.5f0 ? one(RealT) : convert(RealT, 1.245)170171return prim2cons(SVector(rho, v1, p), equations)172end173174"""175initial_condition_eoc_test_coupled_euler_gravity(x, t, equations::CompressibleEulerEquations1D)176177One dimensional variant of the setup used for convergence tests of the Euler equations178with self-gravity from179- Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020)180A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics181[arXiv: 2008.10593](https://arxiv.org/abs/2008.10593)182!!! note183There is no additional source term necessary for the manufactured solution in one184spatial dimension. Thus, [`source_terms_eoc_test_coupled_euler_gravity`](@ref) is not185present there.186"""187function initial_condition_eoc_test_coupled_euler_gravity(x, t,188equations::CompressibleEulerEquations1D)189# OBS! this assumes that γ = 2 other manufactured source terms are incorrect190if equations.gamma != 2191error("adiabatic constant must be 2 for the coupling convergence test")192end193RealT = eltype(x)194c = 2195A = convert(RealT, 0.1)196ini = c + A * sinpi(x[1] - t)197G = 1 # gravitational constant198199rho = ini200v1 = 1201p = 2 * ini^2 * G / convert(RealT, pi) # * 2 / ndims, but ndims==1 here202203return prim2cons(SVector(rho, v1, p), equations)204end205206"""207boundary_condition_slip_wall(u_inner, orientation, direction, x, t,208surface_flux_function, equations::CompressibleEulerEquations1D)209Determine the boundary numerical surface flux for a slip wall condition.210Imposes a zero normal velocity at the wall.211Density is taken from the internal solution state and pressure is computed as an212exact solution of a 1D Riemann problem. Further details about this boundary state213are available in the paper:214- J. J. W. van der Vegt and H. van der Ven (2002)215Slip flow boundary conditions in discontinuous Galerkin discretizations of216the Euler equations of gas dynamics217[PDF](https://reports.nlr.nl/bitstream/handle/10921/692/TP-2002-300.pdf?sequence=1)218219Should be used together with [`TreeMesh`](@ref).220"""221@inline function boundary_condition_slip_wall(u_inner, orientation,222direction, x, t,223surface_flux_function,224equations::CompressibleEulerEquations1D)225# compute the primitive variables226rho_local, v_normal, p_local = cons2prim(u_inner, equations)227228if isodd(direction) # flip sign of normal to make it outward pointing229v_normal *= -1230end231232# Get the solution of the pressure Riemann problem233# See Section 6.3.3 of234# Eleuterio F. Toro (2009)235# Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction236# [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761)237if v_normal <= 0238sound_speed = sqrt(equations.gamma * p_local / rho_local) # local sound speed239p_star = p_local *240(1 + 0.5f0 * (equations.gamma - 1) * v_normal / sound_speed)^(2 *241equations.gamma *242equations.inv_gamma_minus_one)243else # v_normal > 0244A = 2 / ((equations.gamma + 1) * rho_local)245B = p_local * (equations.gamma - 1) / (equations.gamma + 1)246p_star = p_local +2470.5f0 * v_normal / A *248(v_normal + sqrt(v_normal^2 + 4 * A * (p_local + B)))249end250251# For the slip wall we directly set the flux as the normal velocity is zero252return SVector(0, p_star, 0)253end254255# Calculate 1D flux for a single point256@inline function flux(u, orientation::Integer, equations::CompressibleEulerEquations1D)257rho, rho_v1, rho_e = u258v1 = rho_v1 / rho259p = (equations.gamma - 1) * (rho_e - 0.5f0 * rho_v1 * v1)260# Ignore orientation since it is always "1" in 1D261f1 = rho_v1262f2 = rho_v1 * v1 + p263f3 = (rho_e + p) * v1264return SVector(f1, f2, f3)265end266267"""268flux_shima_etal(u_ll, u_rr, orientation, equations::CompressibleEulerEquations1D)269270This flux is is a modification of the original kinetic energy preserving two-point flux by271- Yuichi Kuya, Kosuke Totani and Soshi Kawai (2018)272Kinetic energy and entropy preserving schemes for compressible flows273by split convective forms274[DOI: 10.1016/j.jcp.2018.08.058](https://doi.org/10.1016/j.jcp.2018.08.058)275276The modification is in the energy flux to guarantee pressure equilibrium and was developed by277- Nao Shima, Yuichi Kuya, Yoshiharu Tamaki, Soshi Kawai (JCP 2020)278Preventing spurious pressure oscillations in split convective form discretizations for279compressible flows280[DOI: 10.1016/j.jcp.2020.110060](https://doi.org/10.1016/j.jcp.2020.110060)281"""282@inline function flux_shima_etal(u_ll, u_rr, orientation::Integer,283equations::CompressibleEulerEquations1D)284# Unpack left and right state285rho_ll, v1_ll, p_ll = cons2prim(u_ll, equations)286rho_rr, v1_rr, p_rr = cons2prim(u_rr, equations)287288# Average each factor of products in flux289rho_avg = 0.5f0 * (rho_ll + rho_rr)290v1_avg = 0.5f0 * (v1_ll + v1_rr)291p_avg = 0.5f0 * (p_ll + p_rr)292kin_avg = 0.5f0 * (v1_ll * v1_rr)293294# Calculate fluxes295# Ignore orientation since it is always "1" in 1D296pv1_avg = 0.5f0 * (p_ll * v1_rr + p_rr * v1_ll)297f1 = rho_avg * v1_avg298f2 = f1 * v1_avg + p_avg299f3 = p_avg * v1_avg * equations.inv_gamma_minus_one + f1 * kin_avg + pv1_avg300301return SVector(f1, f2, f3)302end303304"""305flux_kennedy_gruber(u_ll, u_rr, orientation, equations::CompressibleEulerEquations1D)306307Kinetic energy preserving two-point flux by308- Kennedy and Gruber (2008)309Reduced aliasing formulations of the convective terms within the310Navier-Stokes equations for a compressible fluid311[DOI: 10.1016/j.jcp.2007.09.020](https://doi.org/10.1016/j.jcp.2007.09.020)312"""313@inline function flux_kennedy_gruber(u_ll, u_rr, orientation::Integer,314equations::CompressibleEulerEquations1D)315# Unpack left and right state316rho_e_ll = last(u_ll)317rho_e_rr = last(u_rr)318rho_ll, v1_ll, p_ll = cons2prim(u_ll, equations)319rho_rr, v1_rr, p_rr = cons2prim(u_rr, equations)320321# Average each factor of products in flux322rho_avg = 0.5f0 * (rho_ll + rho_rr)323v1_avg = 0.5f0 * (v1_ll + v1_rr)324p_avg = 0.5f0 * (p_ll + p_rr)325e_avg = 0.5f0 * (rho_e_ll / rho_ll + rho_e_rr / rho_rr)326327# Ignore orientation since it is always "1" in 1D328f1 = rho_avg * v1_avg329f2 = rho_avg * v1_avg * v1_avg + p_avg330f3 = (rho_avg * e_avg + p_avg) * v1_avg331332return SVector(f1, f2, f3)333end334335"""336flux_chandrashekar(u_ll, u_rr, orientation, equations::CompressibleEulerEquations1D)337338Entropy conserving two-point flux by339- Chandrashekar (2013)340Kinetic Energy Preserving and Entropy Stable Finite Volume Schemes341for Compressible Euler and Navier-Stokes Equations342[DOI: 10.4208/cicp.170712.010313a](https://doi.org/10.4208/cicp.170712.010313a)343"""344@inline function flux_chandrashekar(u_ll, u_rr, orientation::Integer,345equations::CompressibleEulerEquations1D)346# Unpack left and right state347rho_ll, v1_ll, p_ll = cons2prim(u_ll, equations)348rho_rr, v1_rr, p_rr = cons2prim(u_rr, equations)349beta_ll = 0.5f0 * rho_ll / p_ll350beta_rr = 0.5f0 * rho_rr / p_rr351specific_kin_ll = 0.5f0 * (v1_ll^2)352specific_kin_rr = 0.5f0 * (v1_rr^2)353354# Compute the necessary mean values355rho_avg = 0.5f0 * (rho_ll + rho_rr)356rho_mean = ln_mean(rho_ll, rho_rr)357beta_mean = ln_mean(beta_ll, beta_rr)358beta_avg = 0.5f0 * (beta_ll + beta_rr)359v1_avg = 0.5f0 * (v1_ll + v1_rr)360p_mean = 0.5f0 * rho_avg / beta_avg361velocity_square_avg = specific_kin_ll + specific_kin_rr362363# Calculate fluxes364# Ignore orientation since it is always "1" in 1D365f1 = rho_mean * v1_avg366f2 = f1 * v1_avg + p_mean367f3 = f1 * 0.5f0 * (1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) +368f2 * v1_avg369370return SVector(f1, f2, f3)371end372373"""374flux_ranocha(u_ll, u_rr, orientation_or_normal_direction, equations::CompressibleEulerEquations1D)375376Entropy conserving and kinetic energy preserving two-point flux by377- Hendrik Ranocha (2018)378Generalised Summation-by-Parts Operators and Entropy Stability of Numerical Methods379for Hyperbolic Balance Laws380[PhD thesis, TU Braunschweig](https://cuvillier.de/en/shop/publications/7743)381See also382- Hendrik Ranocha (2020)383Entropy Conserving and Kinetic Energy Preserving Numerical Methods for384the Euler Equations Using Summation-by-Parts Operators385[Proceedings of ICOSAHOM 2018](https://doi.org/10.1007/978-3-030-39647-3_42)386"""387@inline function flux_ranocha(u_ll, u_rr, orientation::Integer,388equations::CompressibleEulerEquations1D)389# Unpack left and right state390rho_ll, v1_ll, p_ll = cons2prim(u_ll, equations)391rho_rr, v1_rr, p_rr = cons2prim(u_rr, equations)392393# Compute the necessary mean values394rho_mean = ln_mean(rho_ll, rho_rr)395# Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)`396# in exact arithmetic since397# log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ)398# = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ)399inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll)400v1_avg = 0.5f0 * (v1_ll + v1_rr)401p_avg = 0.5f0 * (p_ll + p_rr)402velocity_square_avg = 0.5f0 * (v1_ll * v1_rr)403404# Calculate fluxes405# Ignore orientation since it is always "1" in 1D406f1 = rho_mean * v1_avg407f2 = f1 * v1_avg + p_avg408f3 = f1 * (velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) +4090.5f0 * (p_ll * v1_rr + p_rr * v1_ll)410411return SVector(f1, f2, f3)412end413414# While `normal_direction` isn't strictly necessary in 1D, certain solvers assume that415# the normal component is incorporated into the numerical flux.416#417# See `flux(u, normal_direction::AbstractVector, equations::AbstractEquations{1})` for a418# similar implementation.419@inline function flux_ranocha(u_ll, u_rr, normal_direction::AbstractVector,420equations::CompressibleEulerEquations1D)421return normal_direction[1] * flux_ranocha(u_ll, u_rr, 1, equations)422end423424"""425splitting_steger_warming(u, orientation::Integer,426equations::CompressibleEulerEquations1D)427splitting_steger_warming(u, which::Union{Val{:minus}, Val{:plus}}428orientation::Integer,429equations::CompressibleEulerEquations1D)430431Splitting of the compressible Euler flux of Steger and Warming.432433Returns a tuple of the fluxes "minus" (associated with waves going into the434negative axis direction) and "plus" (associated with waves going into the435positive axis direction). If only one of the fluxes is required, use the436function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`.437438!!! warning "Experimental implementation (upwind SBP)"439This is an experimental feature and may change in future releases.440441## References442443- Joseph L. Steger and R. F. Warming (1979)444Flux Vector Splitting of the Inviscid Gasdynamic Equations445With Application to Finite Difference Methods446[NASA Technical Memorandum](https://ntrs.nasa.gov/api/citations/19790020779/downloads/19790020779.pdf)447"""448@inline function splitting_steger_warming(u, orientation::Integer,449equations::CompressibleEulerEquations1D)450fm = splitting_steger_warming(u, Val{:minus}(), orientation, equations)451fp = splitting_steger_warming(u, Val{:plus}(), orientation, equations)452return fm, fp453end454455@inline function splitting_steger_warming(u, ::Val{:plus}, orientation::Integer,456equations::CompressibleEulerEquations1D)457rho, rho_v1, rho_e = u458v1 = rho_v1 / rho459p = (equations.gamma - 1) * (rho_e - 0.5f0 * rho_v1 * v1)460a = sqrt(equations.gamma * p / rho)461462lambda1 = v1463lambda2 = v1 + a464lambda3 = v1 - a465466lambda1_p = positive_part(lambda1) # Same as (lambda_i + abs(lambda_i)) / 2, but faster :)467lambda2_p = positive_part(lambda2)468lambda3_p = positive_part(lambda3)469470alpha_p = 2 * (equations.gamma - 1) * lambda1_p + lambda2_p + lambda3_p471472rho_2gamma = 0.5f0 * rho / equations.gamma473f1p = rho_2gamma * alpha_p474f2p = rho_2gamma * (alpha_p * v1 + a * (lambda2_p - lambda3_p))475f3p = rho_2gamma * (alpha_p * 0.5f0 * v1^2 + a * v1 * (lambda2_p - lambda3_p)476+ a^2 * (lambda2_p + lambda3_p) * equations.inv_gamma_minus_one)477478return SVector(f1p, f2p, f3p)479end480481@inline function splitting_steger_warming(u, ::Val{:minus}, orientation::Integer,482equations::CompressibleEulerEquations1D)483rho, rho_v1, rho_e = u484v1 = rho_v1 / rho485p = (equations.gamma - 1) * (rho_e - 0.5f0 * rho_v1 * v1)486a = sqrt(equations.gamma * p / rho)487488lambda1 = v1489lambda2 = v1 + a490lambda3 = v1 - a491492lambda1_m = negative_part(lambda1) # Same as (lambda_i - abs(lambda_i)) / 2, but faster :)493lambda2_m = negative_part(lambda2)494lambda3_m = negative_part(lambda3)495496alpha_m = 2 * (equations.gamma - 1) * lambda1_m + lambda2_m + lambda3_m497498rho_2gamma = 0.5f0 * rho / equations.gamma499f1m = rho_2gamma * alpha_m500f2m = rho_2gamma * (alpha_m * v1 + a * (lambda2_m - lambda3_m))501f3m = rho_2gamma * (alpha_m * 0.5f0 * v1^2 + a * v1 * (lambda2_m - lambda3_m)502+ a^2 * (lambda2_m + lambda3_m) * equations.inv_gamma_minus_one)503504return SVector(f1m, f2m, f3m)505end506507"""508splitting_vanleer_haenel(u, orientation::Integer,509equations::CompressibleEulerEquations1D)510splitting_vanleer_haenel(u, which::Union{Val{:minus}, Val{:plus}}511orientation::Integer,512equations::CompressibleEulerEquations1D)513514Splitting of the compressible Euler flux from van Leer. This splitting further515contains a reformulation due to Hänel et al. where the energy flux uses the516enthalpy. The pressure splitting is independent from the splitting of the517convective terms. As such there are many pressure splittings suggested across518the literature. We implement the 'p4' variant suggested by Liou and Steffen as519it proved the most robust in practice.520521Returns a tuple of the fluxes "minus" (associated with waves going into the522negative axis direction) and "plus" (associated with waves going into the523positive axis direction). If only one of the fluxes is required, use the524function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`.525526!!! warning "Experimental implementation (upwind SBP)"527This is an experimental feature and may change in future releases.528529## References530531- Bram van Leer (1982)532Flux-Vector Splitting for the Euler Equation533[DOI: 10.1007/978-3-642-60543-7_5](https://doi.org/10.1007/978-3-642-60543-7_5)534- D. Hänel, R. Schwane and G. Seider (1987)535On the accuracy of upwind schemes for the solution of the Navier-Stokes equations536[DOI: 10.2514/6.1987-1105](https://doi.org/10.2514/6.1987-1105)537- Meng-Sing Liou and Chris J. Steffen, Jr. (1991)538High-Order Polynomial Expansions (HOPE) for Flux-Vector Splitting539[NASA Technical Memorandum](https://ntrs.nasa.gov/citations/19910016425)540"""541@inline function splitting_vanleer_haenel(u, orientation::Integer,542equations::CompressibleEulerEquations1D)543fm = splitting_vanleer_haenel(u, Val{:minus}(), orientation, equations)544fp = splitting_vanleer_haenel(u, Val{:plus}(), orientation, equations)545return fm, fp546end547548@inline function splitting_vanleer_haenel(u, ::Val{:plus}, orientation::Integer,549equations::CompressibleEulerEquations1D)550rho, rho_v1, rho_e = u551v1 = rho_v1 / rho552p = (equations.gamma - 1) * (rho_e - 0.5f0 * rho_v1 * v1)553554# sound speed and enthalpy555a = sqrt(equations.gamma * p / rho)556H = (rho_e + p) / rho557558# signed Mach number559M = v1 / a560561p_plus = 0.5f0 * (1 + equations.gamma * M) * p562563f1p = 0.25f0 * rho * a * (M + 1)^2564f2p = f1p * v1 + p_plus565f3p = f1p * H566567return SVector(f1p, f2p, f3p)568end569570@inline function splitting_vanleer_haenel(u, ::Val{:minus}, orientation::Integer,571equations::CompressibleEulerEquations1D)572rho, rho_v1, rho_e = u573v1 = rho_v1 / rho574p = (equations.gamma - 1) * (rho_e - 0.5f0 * rho_v1 * v1)575576# sound speed and enthalpy577a = sqrt(equations.gamma * p / rho)578H = (rho_e + p) / rho579580# signed Mach number581M = v1 / a582583p_minus = 0.5f0 * (1 - equations.gamma * M) * p584585f1m = -0.25f0 * rho * a * (M - 1)^2586f2m = f1m * v1 + p_minus587f3m = f1m * H588589return SVector(f1m, f2m, f3m)590end591592# TODO: FD593# This splitting is interesting because it can handle the "el diablo" wave594# for long time runs. Computing the eigenvalues of the operator we see595# J = jacobian_ad_forward(semi);596# lamb = eigvals(J);597# maximum(real.(lamb))598# 2.1411031631522748e-6599# So the instability of this splitting is very weak. However, the 2D variant600# of this splitting on "el diablo" still crashes early. Can we learn anything601# from the design of this splitting?602"""603splitting_coirier_vanleer(u, orientation::Integer,604equations::CompressibleEulerEquations1D)605splitting_coirier_vanleer(u, which::Union{Val{:minus}, Val{:plus}}606orientation::Integer,607equations::CompressibleEulerEquations1D)608609Splitting of the compressible Euler flux from Coirier and van Leer.610The splitting has correction terms in the pressure splitting as well as611the mass and energy flux components. The motivation for these corrections612are to handle flows at the low Mach number limit.613614Returns a tuple of the fluxes "minus" (associated with waves going into the615negative axis direction) and "plus" (associated with waves going into the616positive axis direction). If only one of the fluxes is required, use the617function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`.618619!!! warning "Experimental implementation (upwind SBP)"620This is an experimental feature and may change in future releases.621622## References623624- William Coirier and Bram van Leer (1991)625Numerical flux formulas for the Euler and Navier-Stokes equations.626II - Progress in flux-vector splitting627[DOI: 10.2514/6.1991-1566](https://doi.org/10.2514/6.1991-1566)628"""629@inline function splitting_coirier_vanleer(u, orientation::Integer,630equations::CompressibleEulerEquations1D)631fm = splitting_coirier_vanleer(u, Val{:minus}(), orientation, equations)632fp = splitting_coirier_vanleer(u, Val{:plus}(), orientation, equations)633return fm, fp634end635636@inline function splitting_coirier_vanleer(u, ::Val{:plus}, orientation::Integer,637equations::CompressibleEulerEquations1D)638rho, rho_v1, rho_e = u639v1 = rho_v1 / rho640p = (equations.gamma - 1) * (rho_e - 0.5f0 * rho_v1 * v1)641642# sound speed and enthalpy643a = sqrt(equations.gamma * p / rho)644H = (rho_e + p) / rho645646# signed Mach number647M = v1 / a648649P = 2650mu = 1651nu = 0.75f0652omega = 2 # adjusted from suggested value of 1.5653654p_plus = 0.25f0 * ((M + 1)^2 * (2 - M) - nu * M * (M^2 - 1)^P) * p655656f1p = 0.25f0 * rho * a * ((M + 1)^2 - mu * (M^2 - 1)^P)657f2p = f1p * v1 + p_plus658f3p = f1p * H - omega * rho * a^3 * M^2 * (M^2 - 1)^2659660return SVector(f1p, f2p, f3p)661end662663@inline function splitting_coirier_vanleer(u, ::Val{:minus}, orientation::Integer,664equations::CompressibleEulerEquations1D)665rho, rho_v1, rho_e = u666v1 = rho_v1 / rho667p = (equations.gamma - 1) * (rho_e - 0.5f0 * rho_v1 * v1)668669# sound speed and enthalpy670a = sqrt(equations.gamma * p / rho)671H = (rho_e + p) / rho672673# signed Mach number674M = v1 / a675676P = 2677mu = 1678nu = 0.75f0679omega = 2 # adjusted from suggested value of 1.5680681p_minus = 0.25f0 * ((M - 1)^2 * (2 + M) + nu * M * (M^2 - 1)^P) * p682683f1m = -0.25f0 * rho * a * ((M - 1)^2 - mu * (M^2 - 1)^P)684f2m = f1m * v1 + p_minus685f3m = f1m * H + omega * rho * a^3 * M^2 * (M^2 - 1)^2686687return SVector(f1m, f2m, f3m)688end689690# Calculate estimates for maximum wave speed for local Lax-Friedrichs-type dissipation as the691# maximum velocity magnitude plus the maximum speed of sound692@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,693equations::CompressibleEulerEquations1D)694rho_ll, rho_v1_ll, rho_e_ll = u_ll695rho_rr, rho_v1_rr, rho_e_rr = u_rr696697# Calculate primitive variables and speed of sound698v1_ll = rho_v1_ll / rho_ll699v_mag_ll = abs(v1_ll)700p_ll = (equations.gamma - 1) * (rho_e_ll - 0.5f0 * rho_ll * v_mag_ll^2)701c_ll = sqrt(equations.gamma * p_ll / rho_ll)702v1_rr = rho_v1_rr / rho_rr703v_mag_rr = abs(v1_rr)704p_rr = (equations.gamma - 1) * (rho_e_rr - 0.5f0 * rho_rr * v_mag_rr^2)705c_rr = sqrt(equations.gamma * p_rr / rho_rr)706707return max(v_mag_ll, v_mag_rr) + max(c_ll, c_rr)708end709710@inline function (dissipation::DissipationMatrixWintersEtal)(u_ll, u_rr,711normal_direction::AbstractVector,712equations::CompressibleEulerEquations1D)713gamma = equations.gamma714715norm_ = norm(normal_direction)716unit_normal_direction = normal_direction / norm_717718rho_ll, v1_ll, p_ll = cons2prim(u_ll, equations)719rho_rr, v1_rr, p_rr = cons2prim(u_rr, equations)720721b_ll = rho_ll / (2 * p_ll)722b_rr = rho_rr / (2 * p_rr)723724rho_log = ln_mean(rho_ll, rho_rr)725b_log = ln_mean(b_ll, b_rr)726v1_avg = 0.5f0 * (v1_ll + v1_rr)727p_avg = 0.5f0 * (rho_ll + rho_rr) / (b_ll + b_rr) # 2 * b_avg = b_ll + b_rr728v1_squared_bar = v1_ll * v1_rr729h_bar = gamma / (2 * b_log * (gamma - 1)) + 0.5f0 * v1_squared_bar730c_bar = sqrt(gamma * p_avg / rho_log)731732v1_minus_c = v1_avg - c_bar * unit_normal_direction[1]733v1_plus_c = v1_avg + c_bar * unit_normal_direction[1]734v_avg_normal = v1_avg * unit_normal_direction[1]735736entropy_vars_jump = cons2entropy(u_rr, equations) - cons2entropy(u_ll, equations)737738lambda_1 = abs(v_avg_normal - c_bar) * rho_log / (2 * gamma)739lambda_2 = abs(v_avg_normal) * rho_log * (gamma - 1) / gamma740lambda_3 = abs(v_avg_normal + c_bar) * rho_log / (2 * gamma)741lambda_4 = abs(v_avg_normal) * p_avg742743entropy_var_rho_jump, entropy_var_rho_v1_jump, entropy_var_rho_e_jump = entropy_vars_jump744745w1 = lambda_1 * (entropy_var_rho_jump + v1_minus_c * entropy_var_rho_v1_jump +746(h_bar - c_bar * v_avg_normal) * entropy_var_rho_e_jump)747w2 = lambda_2 * (entropy_var_rho_jump + v1_avg * entropy_var_rho_v1_jump +748v1_squared_bar / 2 * entropy_var_rho_e_jump)749w3 = lambda_3 * (entropy_var_rho_jump + v1_plus_c * entropy_var_rho_v1_jump +750(h_bar + c_bar * v_avg_normal) * entropy_var_rho_e_jump)751752dissipation_rho = w1 + w2 + w3753754dissipation_rho_v1 = (w1 * v1_minus_c +755w2 * v1_avg +756w3 * v1_plus_c)757758dissipation_rhoe = (w1 * (h_bar - c_bar * v_avg_normal) +759w2 * 0.5f0 * v1_squared_bar +760w3 * (h_bar + c_bar * v_avg_normal) +761lambda_4 *762(entropy_var_rho_e_jump * (v1_avg * v1_avg - v_avg_normal^2)))763764return -0.5f0 * SVector(dissipation_rho, dissipation_rho_v1, dissipation_rhoe) *765norm_766end767768# Calculate estimates for minimum and maximum wave speeds for HLL-type fluxes769@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer,770equations::CompressibleEulerEquations1D)771rho_ll, v1_ll, p_ll = cons2prim(u_ll, equations)772rho_rr, v1_rr, p_rr = cons2prim(u_rr, equations)773774λ_min = v1_ll - sqrt(equations.gamma * p_ll / rho_ll)775λ_max = v1_rr + sqrt(equations.gamma * p_rr / rho_rr)776777return λ_min, λ_max778end779780# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`781@inline function max_abs_speed(u_ll, u_rr, orientation::Integer,782equations::CompressibleEulerEquations1D)783rho_ll, rho_v1_ll, rho_e_ll = u_ll784rho_rr, rho_v1_rr, rho_e_rr = u_rr785786# Calculate primitive variables and speed of sound787v1_ll = rho_v1_ll / rho_ll788v_mag_ll = abs(v1_ll)789p_ll = (equations.gamma - 1) * (rho_e_ll - 0.5f0 * rho_ll * v_mag_ll^2)790c_ll = sqrt(equations.gamma * p_ll / rho_ll)791v1_rr = rho_v1_rr / rho_rr792v_mag_rr = abs(v1_rr)793p_rr = (equations.gamma - 1) * (rho_e_rr - 0.5f0 * rho_rr * v_mag_rr^2)794c_rr = sqrt(equations.gamma * p_rr / rho_rr)795796return max(v_mag_ll + c_ll, v_mag_rr + c_rr)797end798799# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes800@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,801equations::CompressibleEulerEquations1D)802rho_ll, v1_ll, p_ll = cons2prim(u_ll, equations)803rho_rr, v1_rr, p_rr = cons2prim(u_rr, equations)804805c_ll = sqrt(equations.gamma * p_ll / rho_ll)806c_rr = sqrt(equations.gamma * p_rr / rho_rr)807808λ_min = min(v1_ll - c_ll, v1_rr - c_rr)809λ_max = max(v1_ll + c_ll, v1_rr + c_rr)810811return λ_min, λ_max812end813814"""815flux_hllc(u_ll, u_rr, orientation, equations::CompressibleEulerEquations1D)816817Computes the HLLC flux (HLL with Contact) for compressible Euler equations developed by E.F. Toro818[Lecture slides](http://www.prague-sum.com/download/2012/Toro_2-HLLC-RiemannSolver.pdf)819Signal speeds: [DOI: 10.1137/S1064827593260140](https://doi.org/10.1137/S1064827593260140)820"""821function flux_hllc(u_ll, u_rr, orientation::Integer,822equations::CompressibleEulerEquations1D)823# Calculate primitive variables and speed of sound824rho_ll, rho_v1_ll, rho_e_ll = u_ll825rho_rr, rho_v1_rr, rho_e_rr = u_rr826827v1_ll = rho_v1_ll / rho_ll828e_ll = rho_e_ll / rho_ll829p_ll = (equations.gamma - 1) * (rho_e_ll - 0.5f0 * rho_ll * v1_ll^2)830c_ll = sqrt(equations.gamma * p_ll / rho_ll)831832v1_rr = rho_v1_rr / rho_rr833e_rr = rho_e_rr / rho_rr834p_rr = (equations.gamma - 1) * (rho_e_rr - 0.5f0 * rho_rr * v1_rr^2)835c_rr = sqrt(equations.gamma * p_rr / rho_rr)836837# Obtain left and right fluxes838f_ll = flux(u_ll, orientation, equations)839f_rr = flux(u_rr, orientation, equations)840841# Compute Roe averages842sqrt_rho_ll = sqrt(rho_ll)843sqrt_rho_rr = sqrt(rho_rr)844sum_sqrt_rho = sqrt_rho_ll + sqrt_rho_rr845vel_L = v1_ll846vel_R = v1_rr847vel_roe = (sqrt_rho_ll * vel_L + sqrt_rho_rr * vel_R) / sum_sqrt_rho848ekin_roe = 0.5f0 * vel_roe^2849H_ll = (rho_e_ll + p_ll) / rho_ll850H_rr = (rho_e_rr + p_rr) / rho_rr851H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) / sum_sqrt_rho852c_roe = sqrt((equations.gamma - 1) * (H_roe - ekin_roe))853854Ssl = min(vel_L - c_ll, vel_roe - c_roe)855Ssr = max(vel_R + c_rr, vel_roe + c_roe)856sMu_L = Ssl - vel_L857sMu_R = Ssr - vel_R858if Ssl >= 0859f1 = f_ll[1]860f2 = f_ll[2]861f3 = f_ll[3]862elseif Ssr <= 0863f1 = f_rr[1]864f2 = f_rr[2]865f3 = f_rr[3]866else867SStar = (p_rr - p_ll + rho_ll * vel_L * sMu_L - rho_rr * vel_R * sMu_R) /868(rho_ll * sMu_L - rho_rr * sMu_R)869if Ssl <= 0 <= SStar870densStar = rho_ll * sMu_L / (Ssl - SStar)871enerStar = e_ll + (SStar - vel_L) * (SStar + p_ll / (rho_ll * sMu_L))872UStar1 = densStar873UStar2 = densStar * SStar874UStar3 = densStar * enerStar875876f1 = f_ll[1] + Ssl * (UStar1 - rho_ll)877f2 = f_ll[2] + Ssl * (UStar2 - rho_v1_ll)878f3 = f_ll[3] + Ssl * (UStar3 - rho_e_ll)879else880densStar = rho_rr * sMu_R / (Ssr - SStar)881enerStar = e_rr + (SStar - vel_R) * (SStar + p_rr / (rho_rr * sMu_R))882UStar1 = densStar883UStar2 = densStar * SStar884UStar3 = densStar * enerStar885886#end887f1 = f_rr[1] + Ssr * (UStar1 - rho_rr)888f2 = f_rr[2] + Ssr * (UStar2 - rho_v1_rr)889f3 = f_rr[3] + Ssr * (UStar3 - rho_e_rr)890end891end892return SVector(f1, f2, f3)893end894895# While `normal_direction` isn't strictly necessary in 1D, certain solvers assume that896# the normal component is incorporated into the numerical flux.897#898# The HLLC flux along a 1D "normal" can be evaluated by scaling the velocity/momentum by899# the normal for the 1D HLLC flux, then scaling the resulting momentum flux again.900# Moreover, the 2D HLLC flux reduces to this if the normal vector is [n, 0].901function flux_hllc(u_ll, u_rr, normal_direction::AbstractVector,902equations::CompressibleEulerEquations1D)903norm_ = abs(normal_direction[1])904normal_direction_unit = normal_direction[1] * inv(norm_)905906# scale the momentum by the normal direction907f = flux_hllc(SVector(u_ll[1], normal_direction_unit * u_ll[2], u_ll[3]),908SVector(u_rr[1], normal_direction_unit * u_rr[2], u_rr[3]), 1,909equations)910911# rescale the momentum flux by the normal direction and normalize912return SVector(f[1], normal_direction_unit * f[2], f[3]) * norm_913end914915"""916min_max_speed_einfeldt(u_ll, u_rr, orientation, equations::CompressibleEulerEquations1D)917918Computes the HLLE (Harten-Lax-van Leer-Einfeldt) flux for the compressible Euler equations.919Special estimates of the signal velocites and linearization of the Riemann problem developed920by Einfeldt to ensure that the internal energy and density remain positive during the computation921of the numerical flux.922923Original publication:924- Bernd Einfeldt (1988)925On Godunov-type methods for gas dynamics.926[DOI: 10.1137/0725021](https://doi.org/10.1137/0725021)927928Compactly summarized:929- Siddhartha Mishra, Ulrik Skre Fjordholm and Rémi Abgrall930Numerical methods for conservation laws and related equations.931[Link](https://metaphor.ethz.ch/x/2019/hs/401-4671-00L/literature/mishra_hyperbolic_pdes.pdf)932"""933@inline function min_max_speed_einfeldt(u_ll, u_rr, orientation::Integer,934equations::CompressibleEulerEquations1D)935# Calculate primitive variables, enthalpy and speed of sound936rho_ll, v_ll, p_ll = cons2prim(u_ll, equations)937rho_rr, v_rr, p_rr = cons2prim(u_rr, equations)938939# `u_ll[3]` is total energy `rho_e_ll` on the left940H_ll = (u_ll[3] + p_ll) / rho_ll941c_ll = sqrt(equations.gamma * p_ll / rho_ll)942943# `u_rr[3]` is total energy `rho_e_rr` on the right944H_rr = (u_rr[3] + p_rr) / rho_rr945c_rr = sqrt(equations.gamma * p_rr / rho_rr)946947# Compute Roe averages948sqrt_rho_ll = sqrt(rho_ll)949sqrt_rho_rr = sqrt(rho_rr)950inv_sum_sqrt_rho = inv(sqrt_rho_ll + sqrt_rho_rr)951952v_roe = (sqrt_rho_ll * v_ll + sqrt_rho_rr * v_rr) * inv_sum_sqrt_rho953v_roe_mag = v_roe^2954955H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) * inv_sum_sqrt_rho956c_roe = sqrt((equations.gamma - 1) * (H_roe - 0.5f0 * v_roe_mag))957958# Compute convenience constant for positivity preservation, see959# https://doi.org/10.1016/0021-9991(91)90211-3960beta = sqrt(0.5f0 * (equations.gamma - 1) / equations.gamma)961962# Estimate the edges of the Riemann fan (with positivity conservation)963SsL = min(v_roe - c_roe, v_ll - beta * c_ll, 0)964SsR = max(v_roe + c_roe, v_rr + beta * c_rr, 0)965966return SsL, SsR967end968969@inline function max_abs_speeds(u, equations::CompressibleEulerEquations1D)970rho, rho_v1, rho_e = u971v1 = rho_v1 / rho972p = (equations.gamma - 1) * (rho_e - 0.5f0 * rho * v1^2)973c = sqrt(equations.gamma * p / rho)974975return (abs(v1) + c,)976end977978# Convert conservative variables to primitive979@inline function cons2prim(u, equations::CompressibleEulerEquations1D)980rho, rho_v1, rho_e = u981982v1 = rho_v1 / rho983p = (equations.gamma - 1) * (rho_e - 0.5f0 * rho_v1 * v1)984985return SVector(rho, v1, p)986end987988# Convert conservative variables to entropy989@inline function cons2entropy(u, equations::CompressibleEulerEquations1D)990rho, rho_v1, rho_e = u991992v1 = rho_v1 / rho993v_square = v1^2994p = (equations.gamma - 1) * (rho_e - 0.5f0 * rho * v_square)995s = log(p) - equations.gamma * log(rho)996rho_p = rho / p997998w1 = (equations.gamma - s) * equations.inv_gamma_minus_one -9990.5f0 * rho_p * v_square1000w2 = rho_p * v11001w3 = -rho_p10021003return SVector(w1, w2, w3)1004end10051006@inline function entropy2cons(w, equations::CompressibleEulerEquations1D)1007# See Hughes, Franca, Mallet (1986) A new finite element formulation for CFD1008# [DOI: 10.1016/0045-7825(86)90127-1](https://doi.org/10.1016/0045-7825(86)90127-1)1009@unpack gamma = equations10101011# convert to entropy `-rho * s` used by Hughes, France, Mallet (1986)1012# instead of `-rho * s / (gamma - 1)`1013V1, V2, V5 = w .* (gamma - 1)10141015# specific entropy, eq. (53)1016s = gamma - V1 + 0.5f0 * (V2^2) / V510171018# eq. (52)1019energy_internal = ((gamma - 1) / (-V5)^gamma)^(equations.inv_gamma_minus_one) *1020exp(-s * equations.inv_gamma_minus_one)10211022# eq. (51)1023rho = -V5 * energy_internal1024rho_v1 = V2 * energy_internal1025rho_e = (1 - 0.5f0 * (V2^2) / V5) * energy_internal1026return SVector(rho, rho_v1, rho_e)1027end10281029# Convert primitive to conservative variables1030@inline function prim2cons(prim, equations::CompressibleEulerEquations1D)1031rho, v1, p = prim1032rho_v1 = rho * v11033rho_e = p * equations.inv_gamma_minus_one + 0.5f0 * (rho_v1 * v1)1034return SVector(rho, rho_v1, rho_e)1035end10361037@inline function density(u, equations::CompressibleEulerEquations1D)1038rho = u[1]1039return rho1040end10411042@inline function velocity(u, orientation_or_normal,1043equations::CompressibleEulerEquations1D)1044return velocity(u, equations)1045end10461047@inline function velocity(u, equations::CompressibleEulerEquations1D)1048rho = u[1]1049v1 = u[2] / rho1050return v11051end10521053@inline function pressure(u, equations::CompressibleEulerEquations1D)1054rho, rho_v1, rho_e = u1055p = (equations.gamma - 1) * (rho_e - 0.5f0 * (rho_v1^2) / rho)1056return p1057end10581059@inline function density_pressure(u, equations::CompressibleEulerEquations1D)1060rho, rho_v1, rho_e = u1061rho_times_p = (equations.gamma - 1) * (rho * rho_e - 0.5f0 * (rho_v1^2))1062return rho_times_p1063end10641065# Calculate thermodynamic entropy for a conservative state `cons`1066@inline function entropy_thermodynamic(cons, equations::CompressibleEulerEquations1D)1067# Pressure1068p = (equations.gamma - 1) * (cons[3] - 0.5f0 * (cons[2]^2) / cons[1])10691070# Thermodynamic entropy1071s = log(p) - equations.gamma * log(cons[1])10721073return s1074end10751076# Calculate mathematical entropy for a conservative state `cons`1077@inline function entropy_math(cons, equations::CompressibleEulerEquations1D)1078# Mathematical entropy1079S = -entropy_thermodynamic(cons, equations) * cons[1] *1080equations.inv_gamma_minus_one10811082return S1083end10841085# Default entropy is the mathematical entropy1086@inline function entropy(cons, equations::CompressibleEulerEquations1D)1087entropy_math(cons, equations)1088end10891090# Calculate total energy for a conservative state `cons`1091@inline energy_total(cons, ::CompressibleEulerEquations1D) = cons[3]10921093# Calculate kinetic energy for a conservative state `cons`1094@inline function energy_kinetic(cons, equations::CompressibleEulerEquations1D)1095return 0.5f0 * (cons[2]^2) / cons[1]1096end10971098# Calculate internal energy for a conservative state `cons`1099@inline function energy_internal(cons, equations::CompressibleEulerEquations1D)1100return energy_total(cons, equations) - energy_kinetic(cons, equations)1101end1102end # @muladd110311041105