Path: blob/main/src/equations/compressible_euler_quasi_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"""8CompressibleEulerEquationsQuasi1D(gamma)910The quasi-1d compressible Euler equations (see Chan et al. [DOI: 10.48550/arXiv.2307.12089](https://doi.org/10.48550/arXiv.2307.12089) for details)11```math12\frac{\partial}{\partial t}13\begin{pmatrix}14a \rho \\ a \rho v_1 \\ a e15\end{pmatrix}16+17\frac{\partial}{\partial x}18\begin{pmatrix}19a \rho v_1 \\ a \rho v_1^2 \\ a v_1 (e +p)20\end{pmatrix}21+22a \frac{\partial}{\partial x}23\begin{pmatrix}240 \\ p \\ 025\end{pmatrix}26=27\begin{pmatrix}280 \\ 0 \\ 029\end{pmatrix}30```31for an ideal gas with ratio of specific heats `gamma` in one space dimension.32Here, ``\rho`` is the density, ``v_1`` the velocity, ``e`` the specific total energy **rather than** specific internal energy,33``a`` the (possibly) variable nozzle width, and34```math35p = (\gamma - 1) \left( e - \frac{1}{2} \rho v_1^2 \right)36```37the pressure.3839The nozzle width function ``a(x)`` is set inside the initial condition routine40for a particular problem setup. To test the conservative form of the compressible Euler equations one can set the41nozzle width variable ``a`` to one.4243In addition to the unknowns, Trixi.jl currently stores the nozzle width values at the approximation points44despite being fixed in time.45This affects the implementation and use of these equations in various ways:46* The flux values corresponding to the nozzle width must be zero.47* The nozzle width values must be included when defining initial conditions, boundary conditions or48source terms.49* [`AnalysisCallback`](@ref) analyzes this variable.50* Trixi.jl's visualization tools will visualize the nozzle width by default.51"""52struct CompressibleEulerEquationsQuasi1D{RealT <: Real} <:53AbstractCompressibleEulerEquations{1, 4}54gamma::RealT # ratio of specific heats55inv_gamma_minus_one::RealT # = inv(gamma - 1); can be used to write slow divisions as fast multiplications5657function CompressibleEulerEquationsQuasi1D(gamma)58γ, inv_gamma_minus_one = promote(gamma, inv(gamma - 1))59new{typeof(γ)}(γ, inv_gamma_minus_one)60end61end6263have_nonconservative_terms(::CompressibleEulerEquationsQuasi1D) = True()64function varnames(::typeof(cons2cons), ::CompressibleEulerEquationsQuasi1D)65("a_rho", "a_rho_v1", "a_e", "a")66end67function varnames(::typeof(cons2prim), ::CompressibleEulerEquationsQuasi1D)68("rho", "v1", "p", "a")69end7071"""72initial_condition_convergence_test(x, t, equations::CompressibleEulerEquationsQuasi1D)7374A smooth initial condition used for convergence tests in combination with75[`source_terms_convergence_test`](@ref)76(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains).77"""78function initial_condition_convergence_test(x, t,79equations::CompressibleEulerEquationsQuasi1D)80RealT = eltype(x)81c = 282A = convert(RealT, 0.1)83L = 284f = 1.0f0 / L85ω = 2 * convert(RealT, pi) * f86ini = c + A * sin(ω * (x[1] - t))8788rho = ini89v1 = 190e = ini^2 / rho91p = (equations.gamma - 1) * (e - 0.5f0 * rho * v1^2)92a = 1.5f0 - 0.5f0 * cospi(x[1])9394return prim2cons(SVector(rho, v1, p, a), equations)95end9697"""98source_terms_convergence_test(u, x, t, equations::CompressibleEulerEquationsQuasi1D)99100Source terms used for convergence tests in combination with101[`initial_condition_convergence_test`](@ref)102(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains).103104This manufactured solution source term is specifically designed for the mozzle width 'a(x) = 1.5 - 0.5 * cos(x[1] * pi)'105as defined in [`initial_condition_convergence_test`](@ref).106"""107@inline function source_terms_convergence_test(u, x, t,108equations::CompressibleEulerEquationsQuasi1D)109# Same settings as in `initial_condition_convergence_test`.110# Derivatives calculated with ForwardDiff.jl111RealT = eltype(u)112c = 2113A = convert(RealT, 0.1)114L = 2115f = 1.0f0 / L116ω = 2 * convert(RealT, pi) * f117x1, = x118ini(x1, t) = c + A * sin(ω * (x1 - t))119120rho(x1, t) = ini(x1, t)121v1(x1, t) = 1122e(x1, t) = ini(x1, t)^2 / rho(x1, t)123p1(x1, t) = (equations.gamma - 1) * (e(x1, t) - 0.5f0 * rho(x1, t) * v1(x1, t)^2)124a(x1, t) = 1.5f0 - 0.5f0 * cospi(x1)125126arho(x1, t) = a(x1, t) * rho(x1, t)127arhou(x1, t) = arho(x1, t) * v1(x1, t)128aE(x1, t) = a(x1, t) * e(x1, t)129130darho_dt(x1, t) = ForwardDiff.derivative(t -> arho(x1, t), t)131darhou_dx(x1, t) = ForwardDiff.derivative(x1 -> arhou(x1, t), x1)132133arhouu(x1, t) = arhou(x1, t) * v1(x1, t)134darhou_dt(x1, t) = ForwardDiff.derivative(t -> arhou(x1, t), t)135darhouu_dx(x1, t) = ForwardDiff.derivative(x1 -> arhouu(x1, t), x1)136dp1_dx(x1, t) = ForwardDiff.derivative(x1 -> p1(x1, t), x1)137138auEp(x1, t) = a(x1, t) * v1(x1, t) * (e(x1, t) + p1(x1, t))139daE_dt(x1, t) = ForwardDiff.derivative(t -> aE(x1, t), t)140dauEp_dx(x1, t) = ForwardDiff.derivative(x1 -> auEp(x1, t), x1)141142du1 = darho_dt(x1, t) + darhou_dx(x1, t)143du2 = darhou_dt(x1, t) + darhouu_dx(x1, t) + a(x1, t) * dp1_dx(x1, t)144du3 = daE_dt(x1, t) + dauEp_dx(x1, t)145146return SVector(du1, du2, du3, 0)147end148149# Calculate 1D flux for a single point150@inline function flux(u, orientation::Integer,151equations::CompressibleEulerEquationsQuasi1D)152a_rho, a_rho_v1, a_e, a = u153rho, v1, p, a = cons2prim(u, equations)154e = a_e / a155156# Ignore orientation since it is always "1" in 1D157f1 = a_rho_v1158f2 = a_rho_v1 * v1159f3 = a * v1 * (e + p)160161return SVector(f1, f2, f3, 0)162end163164"""165flux_nonconservative_chan_etal(u_ll, u_rr, orientation::Integer,166equations::CompressibleEulerEquationsQuasi1D)167flux_nonconservative_chan_etal(u_ll, u_rr, normal_direction,168equations::CompressibleEulerEquationsQuasi1D)169flux_nonconservative_chan_etal(u_ll, u_rr, normal_ll, normal_rr,170equations::CompressibleEulerEquationsQuasi1D)171172Non-symmetric two-point volume flux discretizing the nonconservative (source) term173that contains the gradient of the pressure [`CompressibleEulerEquationsQuasi1D`](@ref)174and the nozzle width.175176Further details are available in the paper:177- Jesse Chan, Khemraj Shukla, Xinhui Wu, Ruofeng Liu, Prani Nalluri (2023)178High order entropy stable schemes for the quasi-one-dimensional179shallow water and compressible Euler equations180[DOI: 10.48550/arXiv.2307.12089](https://doi.org/10.48550/arXiv.2307.12089)181"""182@inline function flux_nonconservative_chan_etal(u_ll, u_rr, orientation::Integer,183equations::CompressibleEulerEquationsQuasi1D)184#Variables185_, _, p_ll, a_ll = cons2prim(u_ll, equations)186_, _, p_rr, _ = cons2prim(u_rr, equations)187188# For flux differencing using non-conservative terms, we return the189# non-conservative flux scaled by 2. This cancels with a factor of 0.5190# in the arithmetic average of {p}.191p_avg = p_ll + p_rr192193return SVector(0, a_ll * p_avg, 0, 0)194end195196# While `normal_direction` isn't strictly necessary in 1D, certain solvers assume that197# the normal component is incorporated into the numerical flux.198#199# See `flux(u, normal_direction::AbstractVector, equations::AbstractEquations{1})` for a200# similar implementation.201@inline function flux_nonconservative_chan_etal(u_ll, u_rr,202normal_direction::AbstractVector,203equations::CompressibleEulerEquationsQuasi1D)204return normal_direction[1] *205flux_nonconservative_chan_etal(u_ll, u_rr, 1, equations)206end207208@inline function flux_nonconservative_chan_etal(u_ll, u_rr,209normal_ll::AbstractVector,210normal_rr::AbstractVector,211equations::CompressibleEulerEquationsQuasi1D)212# normal_ll should be equal to normal_rr in 1D213return flux_nonconservative_chan_etal(u_ll, u_rr, normal_ll, equations)214end215216"""217@inline function flux_chan_etal(u_ll, u_rr, orientation::Integer,218equations::CompressibleEulerEquationsQuasi1D)219220Conservative (symmetric) part of the entropy conservative flux for quasi 1D compressible Euler equations split form.221This flux is a generalization of [`flux_ranocha`](@ref) for [`CompressibleEulerEquations1D`](@ref).222Further details are available in the paper:223- Jesse Chan, Khemraj Shukla, Xinhui Wu, Ruofeng Liu, Prani Nalluri (2023)224High order entropy stable schemes for the quasi-one-dimensional225shallow water and compressible Euler equations226[DOI: 10.48550/arXiv.2307.12089](https://doi.org/10.48550/arXiv.2307.12089)227"""228@inline function flux_chan_etal(u_ll, u_rr, orientation::Integer,229equations::CompressibleEulerEquationsQuasi1D)230# Unpack left and right state231rho_ll, v1_ll, p_ll, a_ll = cons2prim(u_ll, equations)232rho_rr, v1_rr, p_rr, a_rr = cons2prim(u_rr, equations)233234# Compute the necessary mean values235rho_mean = ln_mean(rho_ll, rho_rr)236# Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)`237# in exact arithmetic since238# log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ)239# = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ)240inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll)241v1_avg = 0.5f0 * (v1_ll + v1_rr)242a_v1_avg = 0.5f0 * (a_ll * v1_ll + a_rr * v1_rr)243p_avg = 0.5f0 * (p_ll + p_rr)244velocity_square_avg = 0.5f0 * (v1_ll * v1_rr)245246# Calculate fluxes247# Ignore orientation since it is always "1" in 1D248f1 = rho_mean * a_v1_avg249f2 = rho_mean * a_v1_avg * v1_avg250f3 = f1 * (velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) +2510.5f0 * (p_ll * a_rr * v1_rr + p_rr * a_ll * v1_ll)252253return SVector(f1, f2, f3, 0)254end255256# While `normal_direction` isn't strictly necessary in 1D, certain solvers assume that257# the normal component is incorporated into the numerical flux.258#259# See `flux(u, normal_direction::AbstractVector, equations::AbstractEquations{1})` for a260# similar implementation.261@inline function flux_chan_etal(u_ll, u_rr, normal_direction::AbstractVector,262equations::CompressibleEulerEquationsQuasi1D)263return normal_direction[1] * flux_chan_etal(u_ll, u_rr, 1, equations)264end265266# Calculate estimates for maximum wave speed for local Lax-Friedrichs-type dissipation as the267# maximum velocity magnitude plus the maximum speed of sound268@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,269equations::CompressibleEulerEquationsQuasi1D)270a_rho_ll, a_rho_v1_ll, a_e_ll, a_ll = u_ll271a_rho_rr, a_rho_v1_rr, a_e_rr, a_rr = u_rr272273# Calculate primitive variables and speed of sound274rho_ll = a_rho_ll / a_ll275e_ll = a_e_ll / a_ll276v1_ll = a_rho_v1_ll / a_rho_ll277v_mag_ll = abs(v1_ll)278p_ll = (equations.gamma - 1) * (e_ll - 0.5f0 * rho_ll * v_mag_ll^2)279c_ll = sqrt(equations.gamma * p_ll / rho_ll)280rho_rr = a_rho_rr / a_rr281e_rr = a_e_rr / a_rr282v1_rr = a_rho_v1_rr / a_rho_rr283v_mag_rr = abs(v1_rr)284p_rr = (equations.gamma - 1) * (e_rr - 0.5f0 * rho_rr * v_mag_rr^2)285c_rr = sqrt(equations.gamma * p_rr / rho_rr)286287return max(v_mag_ll, v_mag_rr) + max(c_ll, c_rr)288end289290# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`291@inline function max_abs_speed(u_ll, u_rr, orientation::Integer,292equations::CompressibleEulerEquationsQuasi1D)293a_rho_ll, a_rho_v1_ll, a_e_ll, a_ll = u_ll294a_rho_rr, a_rho_v1_rr, a_e_rr, a_rr = u_rr295296# Calculate primitive variables and speed of sound297rho_ll = a_rho_ll / a_ll298e_ll = a_e_ll / a_ll299v1_ll = a_rho_v1_ll / a_rho_ll300v_mag_ll = abs(v1_ll)301p_ll = (equations.gamma - 1) * (e_ll - 0.5f0 * rho_ll * v_mag_ll^2)302c_ll = sqrt(equations.gamma * p_ll / rho_ll)303rho_rr = a_rho_rr / a_rr304e_rr = a_e_rr / a_rr305v1_rr = a_rho_v1_rr / a_rho_rr306v_mag_rr = abs(v1_rr)307p_rr = (equations.gamma - 1) * (e_rr - 0.5f0 * rho_rr * v_mag_rr^2)308c_rr = sqrt(equations.gamma * p_rr / rho_rr)309310return max(v_mag_ll + c_ll, v_mag_rr + c_rr)311end312313@inline function max_abs_speeds(u, equations::CompressibleEulerEquationsQuasi1D)314a_rho, a_rho_v1, a_e, a = u315rho = a_rho / a316v1 = a_rho_v1 / a_rho317e = a_e / a318p = (equations.gamma - 1) * (e - 0.5f0 * rho * v1^2)319c = sqrt(equations.gamma * p / rho)320321return (abs(v1) + c,)322end323324# Convert conservative variables to primitive. We use the convention that the primitive325# variables for the quasi-1D equations are `(rho, v1, p)` (i.e., the same as the primitive326# variables for `CompressibleEulerEquations1D`)327@inline function cons2prim(u, equations::CompressibleEulerEquationsQuasi1D)328a_rho, a_rho_v1, a_e, a = u329q = cons2prim(SVector(a_rho, a_rho_v1, a_e) / a,330CompressibleEulerEquations1D(equations.gamma))331332return SVector(q[1], q[2], q[3], a)333end334335# The entropy for the quasi-1D compressible Euler equations is the entropy for the336# 1D compressible Euler equations scaled by the channel width `a`.337@inline function entropy(u, equations::CompressibleEulerEquationsQuasi1D)338a_rho, a_rho_v1, a_e, a = u339return a * entropy(SVector(a_rho, a_rho_v1, a_e) / a,340CompressibleEulerEquations1D(equations.gamma))341end342343# Convert conservative variables to entropy. The entropy variables for the344# quasi-1D compressible Euler equations are identical to the entropy variables345# for the standard Euler equations for an appropriate definition of `entropy`.346@inline function cons2entropy(u, equations::CompressibleEulerEquationsQuasi1D)347a_rho, a_rho_v1, a_e, a = u348w = cons2entropy(SVector(a_rho, a_rho_v1, a_e) / a,349CompressibleEulerEquations1D(equations.gamma))350351# we follow the convention for other spatially-varying equations and return the spatially352# varying coefficient `a` as the final entropy variable.353return SVector(w[1], w[2], w[3], a)354end355356@inline function entropy2cons(w, equations::CompressibleEulerEquationsQuasi1D)357w_rho, w_rho_v1, w_rho_e, a = w358u = entropy2cons(SVector(w_rho, w_rho_v1, w_rho_e),359CompressibleEulerEquations1D(equations.gamma))360return SVector(a * u[1], a * u[2], a * u[3], a)361end362363# Convert primitive to conservative variables364@inline function prim2cons(u, equations::CompressibleEulerEquationsQuasi1D)365rho, v1, p, a = u366q = prim2cons(u, CompressibleEulerEquations1D(equations.gamma))367368return SVector(a * q[1], a * q[2], a * q[3], a)369end370371@inline function density(u, equations::CompressibleEulerEquationsQuasi1D)372a_rho, _, _, a = u373rho = a_rho / a374return rho375end376377@inline function pressure(u, equations::CompressibleEulerEquationsQuasi1D)378a_rho, a_rho_v1, a_e, a = u379return pressure(SVector(a_rho, a_rho_v1, a_e) / a,380CompressibleEulerEquations1D(equations.gamma))381end382383@inline function density_pressure(u, equations::CompressibleEulerEquationsQuasi1D)384a_rho, a_rho_v1, a_e, a = u385return density_pressure(SVector(a_rho, a_rho_v1, a_e) / a,386CompressibleEulerEquations1D(equations.gamma))387end388end # @muladd389390391