Path: blob/main/src/equations/compressible_euler_multicomponent_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"""8CompressibleEulerMulticomponentEquations1D(; gammas, gas_constants)910Multicomponent version of the compressible Euler equations11```math12\frac{\partial}{\partial t}13\begin{pmatrix}14\rho v_1 \\ \rho e \\ \rho_1 \\ \rho_2 \\ \vdots \\ \rho_{n}15\end{pmatrix}16+17\frac{\partial}{\partial x}18\begin{pmatrix}19\rho v_1^2 + p \\ (\rho e +p) v_1 \\ \rho_1 v_1 \\ \rho_2 v_1 \\ \vdots \\ \rho_{n} v_120\end{pmatrix}2122=23\begin{pmatrix}240 \\ 0 \\ 0 \\ 0 \\ \vdots \\ 025\end{pmatrix}26```27for calorically perfect gas in one space dimension.28Here, ``\rho_i`` is the density of component ``i``, ``\rho=\sum_{i=1}^n\rho_i`` the sum of the individual ``\rho_i``,29``v_1`` the velocity, ``e`` the specific total energy **rather than** specific internal energy, and30```math31p = (\gamma - 1) \left( \rho e - \frac{1}{2} \rho v_1^2 \right)32```33the pressure,34```math35\gamma=\frac{\sum_{i=1}^n\rho_i C_{v,i}\gamma_i}{\sum_{i=1}^n\rho_i C_{v,i}}36```37total heat capacity ratio, ``\gamma_i`` heat capacity ratio of component ``i``,38```math39C_{v,i}=\frac{R}{\gamma_i-1}40```41specific heat capacity at constant volume of component ``i``.4243In case of more than one component, the specific heat ratios `gammas` and the gas constants44`gas_constants` should be passed as tuples, e.g., `gammas=(1.4, 1.667)`.4546The remaining variables like the specific heats at constant volume `cv` or the specific heats at47constant pressure `cp` are then calculated considering a calorically perfect gas.48"""49struct CompressibleEulerMulticomponentEquations1D{NVARS, NCOMP, RealT <: Real} <:50AbstractCompressibleEulerMulticomponentEquations{1, NVARS, NCOMP}51gammas::SVector{NCOMP, RealT}52gas_constants::SVector{NCOMP, RealT}53cv::SVector{NCOMP, RealT}54cp::SVector{NCOMP, RealT}5556function CompressibleEulerMulticomponentEquations1D{NVARS, NCOMP, RealT}(gammas::SVector{NCOMP,57RealT},58gas_constants::SVector{NCOMP,59RealT}) where {60NVARS,61NCOMP,62RealT <:63Real64}65NCOMP >= 1 ||66throw(DimensionMismatch("`gammas` and `gas_constants` have to be filled with at least one value"))6768cv = gas_constants ./ (gammas .- 1)69cp = gas_constants + gas_constants ./ (gammas .- 1)7071new(gammas, gas_constants, cv, cp)72end73end7475function CompressibleEulerMulticomponentEquations1D(; gammas, gas_constants)76_gammas = promote(gammas...)77_gas_constants = promote(gas_constants...)78RealT = promote_type(eltype(_gammas), eltype(_gas_constants),79typeof(gas_constants[1] / (gammas[1] - 1)))80__gammas = SVector(map(RealT, _gammas))81__gas_constants = SVector(map(RealT, _gas_constants))8283NVARS = length(_gammas) + 284NCOMP = length(_gammas)8586return CompressibleEulerMulticomponentEquations1D{NVARS, NCOMP, RealT}(__gammas,87__gas_constants)88end8990@inline function Base.real(::CompressibleEulerMulticomponentEquations1D{NVARS, NCOMP,91RealT}) where {92NVARS,93NCOMP,94RealT95}96RealT97end9899function varnames(::typeof(cons2cons),100equations::CompressibleEulerMulticomponentEquations1D)101cons = ("rho_v1", "rho_e")102rhos = ntuple(n -> "rho" * string(n), Val(ncomponents(equations)))103return (cons..., rhos...)104end105106function varnames(::typeof(cons2prim),107equations::CompressibleEulerMulticomponentEquations1D)108prim = ("v1", "p")109rhos = ntuple(n -> "rho" * string(n), Val(ncomponents(equations)))110return (prim..., rhos...)111end112113# Set initial conditions at physical location `x` for time `t`114115"""116initial_condition_convergence_test(x, t, equations::CompressibleEulerMulticomponentEquations1D)117118A smooth initial condition used for convergence tests in combination with119[`source_terms_convergence_test`](@ref)120(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains).121"""122function initial_condition_convergence_test(x, t,123equations::CompressibleEulerMulticomponentEquations1D)124RealT = eltype(x)125c = 2126A = convert(RealT, 0.1)127L = 2128f = 1.0f0 / L129omega = 2 * convert(RealT, pi) * f130ini = c + A * sin(omega * (x[1] - t))131132v1 = 1133134rho = ini135136# Here we compute an arbitrary number of different rhos. (one rho is double the next rho while the sum of all rhos is 1)137prim_rho = SVector{ncomponents(equations), real(equations)}(2^(i - 1) * (1 - 2) *138rho / (1 -1392^ncomponents(equations))140for i in eachcomponent(equations))141142prim1 = rho * v1143prim2 = rho^2144145prim_other = SVector(prim1, prim2)146147return vcat(prim_other, prim_rho)148end149150"""151source_terms_convergence_test(u, x, t, equations::CompressibleEulerMulticomponentEquations1D)152153Source terms used for convergence tests in combination with154[`initial_condition_convergence_test`](@ref)155(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains).156"""157@inline function source_terms_convergence_test(u, x, t,158equations::CompressibleEulerMulticomponentEquations1D)159# Same settings as in `initial_condition`160RealT = eltype(u)161c = 2162A = convert(RealT, 0.1)163L = 2164f = 1.0f0 / L165omega = 2 * convert(RealT, pi) * f166167gamma = totalgamma(u, equations)168169x1, = x170si, co = sincos((t - x1) * omega)171tmp = (-((4 * si * A - 4 * c) + 1) * (gamma - 1) * co * A * omega) / 2172173# Here we compute an arbitrary number of different rhos. (one rho is double the next rho while the sum of all rhos is 1174du_rho = SVector{ncomponents(equations), real(equations)}(0175for i in eachcomponent(equations))176177du1 = tmp178du2 = tmp179180du_other = SVector(du1, du2)181182return vcat(du_other, du_rho)183end184185"""186initial_condition_weak_blast_wave(x, t, equations::CompressibleEulerMulticomponentEquations1D)187188A for multicomponent adapted weak blast wave adapted to multicomponent and taken from189- Sebastian Hennemann, Gregor J. Gassner (2020)190A provably entropy stable subcell shock capturing approach for high order split form DG191[arXiv: 2008.12044](https://arxiv.org/abs/2008.12044)192"""193function initial_condition_weak_blast_wave(x, t,194equations::CompressibleEulerMulticomponentEquations1D)195# From Hennemann & Gassner JCP paper 2020 (Sec. 6.3)196RealT = eltype(x)197inicenter = SVector(0)198x_norm = x[1] - inicenter[1]199r = abs(x_norm)200cos_phi = x_norm > 0 ? 1 : -1201202prim_rho = SVector{ncomponents(equations), real(equations)}(r > 0.5f0 ?2032^(i - 1) * (1 - 2) /204(RealT(1) -2052^ncomponents(equations)) :2062^(i - 1) * (1 - 2) *207RealT(1.1691) /208(1 -2092^ncomponents(equations))210for i in eachcomponent(equations))211212v1 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos_phi213p = r > 0.5f0 ? one(RealT) : convert(RealT, 1.245)214215prim_other = SVector(v1, p)216217return prim2cons(vcat(prim_other, prim_rho), equations)218end219220# Calculate 1D flux for a single point221@inline function flux(u, orientation::Integer,222equations::CompressibleEulerMulticomponentEquations1D)223rho_v1, rho_e = u224225rho = density(u, equations)226227v1 = rho_v1 / rho228gamma = totalgamma(u, equations)229p = (gamma - 1) * (rho_e - 0.5f0 * rho * v1^2)230231f_rho = densities(u, v1, equations)232f1 = rho_v1 * v1 + p233f2 = (rho_e + p) * v1234235f_other = SVector(f1, f2)236237return vcat(f_other, f_rho)238end239240"""241flux_chandrashekar(u_ll, u_rr, orientation, equations::CompressibleEulerMulticomponentEquations1D)242243Entropy conserving two-point flux by244- Ayoub Gouasmi, Karthik Duraisamy (2020)245"Formulation of Entropy-Stable schemes for the multicomponent compressible Euler equations"246[arXiv:1904.00972v3](https://arxiv.org/abs/1904.00972) [math.NA] 4 Feb 2020247"""248@inline function flux_chandrashekar(u_ll, u_rr, orientation::Integer,249equations::CompressibleEulerMulticomponentEquations1D)250# Unpack left and right state251@unpack gammas, gas_constants, cv = equations252rho_v1_ll, rho_e_ll = u_ll253rho_v1_rr, rho_e_rr = u_rr254rhok_mean = SVector{ncomponents(equations), real(equations)}(ln_mean(u_ll[i + 2],255u_rr[i + 2])256for i in eachcomponent(equations))257rhok_avg = SVector{ncomponents(equations), real(equations)}(0.5f0 * (u_ll[i + 2] +258u_rr[i + 2])259for i in eachcomponent(equations))260261# Iterating over all partial densities262rho_ll = density(u_ll, equations)263rho_rr = density(u_rr, equations)264265gamma_ll = totalgamma(u_ll, equations)266gamma_rr = totalgamma(u_rr, equations)267268# extract velocities269v1_ll = rho_v1_ll / rho_ll270v1_rr = rho_v1_rr / rho_rr271v1_avg = 0.5f0 * (v1_ll + v1_rr)272v1_square = 0.5f0 * (v1_ll^2 + v1_rr^2)273v_sum = v1_avg274275RealT = eltype(u_ll)276enth = zero(RealT)277help1_ll = zero(RealT)278help1_rr = zero(RealT)279280for i in eachcomponent(equations)281enth += rhok_avg[i] * gas_constants[i]282help1_ll += u_ll[i + 2] * cv[i]283help1_rr += u_rr[i + 2] * cv[i]284end285286T_ll = (rho_e_ll - 0.5f0 * rho_ll * (v1_ll^2)) / help1_ll287T_rr = (rho_e_rr - 0.5f0 * rho_rr * (v1_rr^2)) / help1_rr288T = 0.5f0 * (1 / T_ll + 1 / T_rr)289T_log = ln_mean(1 / T_ll, 1 / T_rr)290291# Calculate fluxes depending on orientation292help1 = zero(RealT)293help2 = zero(RealT)294295f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * v1_avg296for i in eachcomponent(equations))297for i in eachcomponent(equations)298help1 += f_rho[i] * cv[i]299help2 += f_rho[i]300end301f1 = (help2) * v1_avg + enth / T302f2 = (help1) / T_log - 0.5f0 * (v1_square) * (help2) + v1_avg * f1303304f_other = SVector(f1, f2)305306return vcat(f_other, f_rho)307end308309"""310flux_ranocha(u_ll, u_rr, orientation_or_normal_direction,311equations::CompressibleEulerMulticomponentEquations1D)312313Adaption of the entropy conserving and kinetic energy preserving two-point flux by314- Hendrik Ranocha (2018)315Generalised Summation-by-Parts Operators and Entropy Stability of Numerical Methods316for Hyperbolic Balance Laws317[PhD thesis, TU Braunschweig](https://cuvillier.de/en/shop/publications/7743)318See also319- Hendrik Ranocha (2020)320Entropy Conserving and Kinetic Energy Preserving Numerical Methods for321the Euler Equations Using Summation-by-Parts Operators322[Proceedings of ICOSAHOM 2018](https://doi.org/10.1007/978-3-030-39647-3_42)323"""324@inline function flux_ranocha(u_ll, u_rr, orientation::Integer,325equations::CompressibleEulerMulticomponentEquations1D)326# Unpack left and right state327@unpack gammas, gas_constants, cv = equations328rho_v1_ll, rho_e_ll = u_ll329rho_v1_rr, rho_e_rr = u_rr330rhok_mean = SVector{ncomponents(equations), real(equations)}(ln_mean(u_ll[i + 2],331u_rr[i + 2])332for i in eachcomponent(equations))333rhok_avg = SVector{ncomponents(equations), real(equations)}(0.5f0 * (u_ll[i + 2] +334u_rr[i + 2])335for i in eachcomponent(equations))336337# Iterating over all partial densities338rho_ll = density(u_ll, equations)339rho_rr = density(u_rr, equations)340341# Calculating gamma342gamma = totalgamma(0.5f0 * (u_ll + u_rr), equations)343inv_gamma_minus_one = 1 / (gamma - 1)344345# extract velocities346v1_ll = rho_v1_ll / rho_ll347v1_rr = rho_v1_rr / rho_rr348v1_avg = 0.5f0 * (v1_ll + v1_rr)349velocity_square_avg = 0.5f0 * (v1_ll * v1_rr)350351# density flux352f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * v1_avg353for i in eachcomponent(equations))354355# helpful variables356RealT = eltype(u_ll)357f_rho_sum = zero(RealT)358help1_ll = zero(RealT)359help1_rr = zero(RealT)360enth_ll = zero(RealT)361enth_rr = zero(RealT)362for i in eachcomponent(equations)363enth_ll += u_ll[i + 2] * gas_constants[i]364enth_rr += u_rr[i + 2] * gas_constants[i]365f_rho_sum += f_rho[i]366help1_ll += u_ll[i + 2] * cv[i]367help1_rr += u_rr[i + 2] * cv[i]368end369370# temperature and pressure371T_ll = (rho_e_ll - 0.5f0 * rho_ll * (v1_ll^2)) / help1_ll372T_rr = (rho_e_rr - 0.5f0 * rho_rr * (v1_rr^2)) / help1_rr373p_ll = T_ll * enth_ll374p_rr = T_rr * enth_rr375p_avg = 0.5f0 * (p_ll + p_rr)376inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll)377378# momentum and energy flux379f1 = f_rho_sum * v1_avg + p_avg380f2 = f_rho_sum * (velocity_square_avg + inv_rho_p_mean * inv_gamma_minus_one) +3810.5f0 * (p_ll * v1_rr + p_rr * v1_ll)382f_other = SVector(f1, f2)383384return vcat(f_other, f_rho)385end386387# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation388@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,389equations::CompressibleEulerMulticomponentEquations1D)390rho_v1_ll, rho_e_ll = u_ll391rho_v1_rr, rho_e_rr = u_rr392393# Calculate primitive variables and speed of sound394rho_ll = density(u_ll, equations)395rho_rr = density(u_rr, equations)396gamma_ll = totalgamma(u_ll, equations)397gamma_rr = totalgamma(u_rr, equations)398399v_ll = rho_v1_ll / rho_ll400v_rr = rho_v1_rr / rho_rr401402p_ll = (gamma_ll - 1) * (rho_e_ll - 0.5f0 * rho_ll * v_ll^2)403p_rr = (gamma_rr - 1) * (rho_e_rr - 0.5f0 * rho_rr * v_rr^2)404c_ll = sqrt(gamma_ll * p_ll / rho_ll)405c_rr = sqrt(gamma_rr * p_rr / rho_rr)406407return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr)408end409410# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`411@inline function max_abs_speed(u_ll, u_rr, orientation::Integer,412equations::CompressibleEulerMulticomponentEquations1D)413rho_v1_ll, rho_e_ll = u_ll414rho_v1_rr, rho_e_rr = u_rr415416# Calculate primitive variables and speed of sound417rho_ll = density(u_ll, equations)418rho_rr = density(u_rr, equations)419gamma_ll = totalgamma(u_ll, equations)420gamma_rr = totalgamma(u_rr, equations)421422v_ll = rho_v1_ll / rho_ll423v_rr = rho_v1_rr / rho_rr424425p_ll = (gamma_ll - 1) * (rho_e_ll - 0.5f0 * rho_ll * v_ll^2)426p_rr = (gamma_rr - 1) * (rho_e_rr - 0.5f0 * rho_rr * v_rr^2)427c_ll = sqrt(gamma_ll * p_ll / rho_ll)428c_rr = sqrt(gamma_rr * p_rr / rho_rr)429430return max(abs(v_ll) + c_ll, abs(v_rr) + c_rr)431end432433@inline function max_abs_speeds(u,434equations::CompressibleEulerMulticomponentEquations1D)435rho_v1, rho_e = u436437rho = density(u, equations)438v1 = rho_v1 / rho439440gamma = totalgamma(u, equations)441p = (gamma - 1) * (rho_e - 0.5f0 * rho * (v1^2))442c = sqrt(gamma * p / rho)443444return (abs(v1) + c,)445end446447# Convert conservative variables to primitive448@inline function cons2prim(u, equations::CompressibleEulerMulticomponentEquations1D)449rho_v1, rho_e = u450451prim_rho = SVector{ncomponents(equations), real(equations)}(u[i + 2]452for i in eachcomponent(equations))453454rho = density(u, equations)455v1 = rho_v1 / rho456gamma = totalgamma(u, equations)457458p = (gamma - 1) * (rho_e - 0.5f0 * rho * (v1^2))459prim_other = SVector(v1, p)460461return vcat(prim_other, prim_rho)462end463464# Convert primitive to conservative variables465@inline function prim2cons(prim, equations::CompressibleEulerMulticomponentEquations1D)466@unpack cv, gammas = equations467v1, p = prim468469RealT = eltype(prim)470471cons_rho = SVector{ncomponents(equations), RealT}(prim[i + 2]472for i in eachcomponent(equations))473rho = density(prim, equations)474gamma = totalgamma(prim, equations)475476rho_v1 = rho * v1477478rho_e = p / (gamma - 1) + 0.5f0 * (rho_v1 * v1)479480cons_other = SVector{2, RealT}(rho_v1, rho_e)481482return vcat(cons_other, cons_rho)483end484485# Convert conservative variables to entropy486@inline function cons2entropy(u, equations::CompressibleEulerMulticomponentEquations1D)487@unpack cv, gammas, gas_constants = equations488489rho_v1, rho_e = u490491rho = density(u, equations)492493RealT = eltype(u)494help1 = zero(RealT)495gas_constant = zero(RealT)496for i in eachcomponent(equations)497help1 += u[i + 2] * cv[i]498gas_constant += gas_constants[i] * (u[i + 2] / rho)499end500501v1 = rho_v1 / rho502v_square = v1^2503gamma = totalgamma(u, equations)504505p = (gamma - 1) * (rho_e - 0.5f0 * rho * v_square)506s = log(p) - gamma * log(rho) - log(gas_constant)507rho_p = rho / p508T = (rho_e - 0.5f0 * rho * v_square) / (help1)509510entrop_rho = SVector{ncomponents(equations), real(equations)}((cv[i] *511(1 - log(T)) +512gas_constants[i] *513(1 + log(u[i + 2])) -514v1^2 / (2 * T))515for i in eachcomponent(equations))516517w1 = gas_constant * v1 * rho_p518w2 = gas_constant * (-rho_p)519520entrop_other = SVector(w1, w2)521522return vcat(entrop_other, entrop_rho)523end524525# Convert entropy variables to conservative variables526@inline function entropy2cons(w, equations::CompressibleEulerMulticomponentEquations1D)527@unpack gammas, gas_constants, cv, cp = equations528T = -1 / w[2]529v1 = w[1] * T530cons_rho = SVector{ncomponents(equations), real(equations)}(exp(1 /531gas_constants[i] *532(-cv[i] *533log(-w[2]) -534cp[i] + w[i + 2] -5350.5f0 * w[1]^2 /536w[2]))537for i in eachcomponent(equations))538539RealT = eltype(w)540rho = zero(RealT)541help1 = zero(RealT)542help2 = zero(RealT)543p = zero(RealT)544for i in eachcomponent(equations)545rho += cons_rho[i]546help1 += cons_rho[i] * cv[i] * gammas[i]547help2 += cons_rho[i] * cv[i]548p += cons_rho[i] * gas_constants[i] * T549end550u1 = rho * v1551gamma = help1 / help2552u2 = p / (gamma - 1) + 0.5f0 * rho * v1^2553cons_other = SVector(u1, u2)554return vcat(cons_other, cons_rho)555end556557@inline function total_entropy(u, equations::CompressibleEulerMulticomponentEquations1D)558@unpack cv, gammas, gas_constants = equations559rho_v1, rho_e = u560rho = density(u, equations)561T = temperature(u, equations)562563RealT = eltype(u)564total_entropy = zero(RealT)565for i in eachcomponent(equations)566total_entropy -= u[i + 2] * (cv[i] * log(T) - gas_constants[i] * log(u[i + 2]))567end568569return total_entropy570end571572@inline function temperature(u, equations::CompressibleEulerMulticomponentEquations1D)573@unpack cv, gammas, gas_constants = equations574575rho_v1, rho_e = u576577rho = density(u, equations)578579RealT = eltype(u)580help1 = zero(RealT)581582for i in eachcomponent(equations)583help1 += u[i + 2] * cv[i]584end585586v1 = rho_v1 / rho587v_square = v1^2588T = (rho_e - 0.5f0 * rho * v_square) / help1589590return T591end592593"""594totalgamma(u, equations::CompressibleEulerMulticomponentEquations1D)595596Function that calculates the total gamma out of all partial gammas using the597partial density fractions as well as the partial specific heats at constant volume.598"""599@inline function totalgamma(u, equations::CompressibleEulerMulticomponentEquations1D)600@unpack cv, gammas = equations601602RealT = eltype(u)603help1 = zero(RealT)604help2 = zero(RealT)605606for i in eachcomponent(equations)607help1 += u[i + 2] * cv[i] * gammas[i]608help2 += u[i + 2] * cv[i]609end610611return help1 / help2612end613614@inline function pressure(u, equations::CompressibleEulerMulticomponentEquations1D)615rho_v1, rho_e = u616617rho = density(u, equations)618gamma = totalgamma(u, equations)619620p = (gamma - 1) * (rho_e - 0.5f0 * (rho_v1^2) / rho)621622return p623end624625@inline function density(u, equations::CompressibleEulerMulticomponentEquations1D)626RealT = eltype(u)627rho = zero(RealT)628629for i in eachcomponent(equations)630rho += u[i + 2]631end632633return rho634end635636@inline function densities(u, v, equations::CompressibleEulerMulticomponentEquations1D)637return SVector{ncomponents(equations), real(equations)}(u[i + 2] * v638for i in eachcomponent(equations))639end640641@inline function velocity(u, equations::CompressibleEulerMulticomponentEquations1D)642rho = density(u, equations)643v1 = u[1] / rho644return v1645end646end # @muladd647648649