Path: blob/main/src/equations/compressible_euler_multicomponent_2d.jl
5586 views
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).1# Since these FMAs can increase the performance of many numerical algorithms,2# we need to opt-in explicitly.3# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.4@muladd begin5#! format: noindent67@doc raw"""8CompressibleEulerMulticomponentEquations2D(; gammas, gas_constants)910Multicomponent version of the compressible Euler equations11```math12\frac{\partial}{\partial t}13\begin{pmatrix}14\rho v_1 \\ \rho v_2 \\ \rho e_{\text{total}} \\ \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 v_1 v_2 \\ ( \rho e_{\text{total}} +p) v_1 \\ \rho_1 v_1 \\ \rho_2 v_1 \\ \vdots \\ \rho_{n} v_120\end{pmatrix}21+22\frac{\partial}{\partial y}23\begin{pmatrix}24\rho v_1 v_2 \\ \rho v_2^2 + p \\ ( \rho e_{\text{total}} +p) v_2 \\ \rho_1 v_2 \\ \rho_2 v_2 \\ \vdots \\ \rho_{n} v_225\end{pmatrix}26=27\begin{pmatrix}280 \\ 0 \\ 0 \\ 0 \\ 0 \\ \vdots \\ 029\end{pmatrix}30```31for calorically perfect gas in two space dimensions.32Here, ``\rho_i`` is the density of component ``i``, ``\rho=\sum_{i=1}^n\rho_i`` the sum of the individual ``\rho_i``,33``v_1``, ``v_2`` the velocities, ``e_{\text{total}}`` the specific total energy, and34```math35p = (\gamma - 1) \left( \rho e - \frac{1}{2} \rho (v_1^2 + v_2^2) \right)36```37the pressure,38```math39\gamma=\frac{\sum_{i=1}^n\rho_i C_{v,i}\gamma_i}{\sum_{i=1}^n\rho_i C_{v,i}}40```41total heat capacity ratio, ``\gamma_i`` heat capacity ratio of component ``i``,42```math43C_{v,i}=\frac{R}{\gamma_i-1}44```45specific heat capacity at constant volume of component ``i``.4647In case of more than one component, the specific heat ratios `gammas` and the gas constants48`gas_constants` in [kJ/(kg*K)] should be passed as tuples, e.g., `gammas=(1.4, 1.667)`.4950The remaining variables like the specific heats at constant volume `cv` or the specific heats at51constant pressure `cp` are then calculated considering a calorically perfect gas.52"""53struct CompressibleEulerMulticomponentEquations2D{NVARS, NCOMP, RealT <: Real} <:54AbstractCompressibleEulerMulticomponentEquations{2, NVARS, NCOMP}55gammas::SVector{NCOMP, RealT}56gas_constants::SVector{NCOMP, RealT}57cv::SVector{NCOMP, RealT}58cp::SVector{NCOMP, RealT}5960function CompressibleEulerMulticomponentEquations2D{NVARS, NCOMP, RealT}(gammas::SVector{NCOMP,61RealT},62gas_constants::SVector{NCOMP,63RealT}) where {64NVARS,65NCOMP,66RealT <:67Real68}69NCOMP >= 1 ||70throw(DimensionMismatch("`gammas` and `gas_constants` have to be filled with at least one value"))7172cv = gas_constants ./ (gammas .- 1)73cp = gas_constants + gas_constants ./ (gammas .- 1)7475return new(gammas, gas_constants, cv, cp)76end77end7879function CompressibleEulerMulticomponentEquations2D(; gammas, gas_constants)80_gammas = promote(gammas...)81_gas_constants = promote(gas_constants...)82RealT = promote_type(eltype(_gammas), eltype(_gas_constants),83typeof(gas_constants[1] / (gammas[1] - 1)))84__gammas = SVector(map(RealT, _gammas))85__gas_constants = SVector(map(RealT, _gas_constants))8687NVARS = length(_gammas) + 388NCOMP = length(_gammas)8990return CompressibleEulerMulticomponentEquations2D{NVARS, NCOMP, RealT}(__gammas,91__gas_constants)92end9394@inline function Base.real(::CompressibleEulerMulticomponentEquations2D{NVARS, NCOMP,95RealT}) where {96NVARS,97NCOMP,98RealT99}100return RealT101end102103function varnames(::typeof(cons2cons),104equations::CompressibleEulerMulticomponentEquations2D)105cons = ("rho_v1", "rho_v2", "rho_e_total")106rhos = ntuple(n -> "rho" * string(n), Val(ncomponents(equations)))107return (cons..., rhos...)108end109110function varnames(::typeof(cons2prim),111equations::CompressibleEulerMulticomponentEquations2D)112prim = ("v1", "v2", "p")113rhos = ntuple(n -> "rho" * string(n), Val(ncomponents(equations)))114return (prim..., rhos...)115end116117# Set initial conditions at physical location `x` for time `t`118119"""120initial_condition_convergence_test(x, t, equations::CompressibleEulerMulticomponentEquations2D)121122A smooth initial condition used for convergence tests in combination with123[`source_terms_convergence_test`](@ref)124(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains).125"""126function initial_condition_convergence_test(x, t,127equations::CompressibleEulerMulticomponentEquations2D)128RealT = eltype(x)129c = 2130A = convert(RealT, 0.1)131L = 2132f = 1.0f0 / L133omega = 2 * convert(RealT, pi) * f134ini = c + A * sin(omega * (x[1] + x[2] - t))135136v1 = 1137v2 = 1138139rho = ini140141# Here we compute an arbitrary number of different rhos. (one rho is double the next rho while the sum of all rhos is 1)142prim_rho = SVector{ncomponents(equations), real(equations)}(2^(i - 1) * (1 - 2) *143rho / (1 -1442^ncomponents(equations))145for i in eachcomponent(equations))146147prim1 = rho * v1148prim2 = rho * v2149prim3 = rho^2150151prim_other = SVector(prim1, prim2, prim3)152153return vcat(prim_other, prim_rho)154end155156"""157source_terms_convergence_test(u, x, t, equations::CompressibleEulerMulticomponentEquations2D)158159Source terms used for convergence tests in combination with160[`initial_condition_convergence_test`](@ref)161(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains).162163References for the method of manufactured solutions (MMS):164- Kambiz Salari and Patrick Knupp (2000)165Code Verification by the Method of Manufactured Solutions166[DOI: 10.2172/759450](https://doi.org/10.2172/759450)167- Patrick J. Roache (2002)168Code Verification by the Method of Manufactured Solutions169[DOI: 10.1115/1.1436090](https://doi.org/10.1115/1.1436090)170"""171@inline function source_terms_convergence_test(u, x, t,172equations::CompressibleEulerMulticomponentEquations2D)173# Same settings as in `initial_condition`174RealT = eltype(u)175c = 2176A = convert(RealT, 0.1)177L = 2178f = 1.0f0 / L179omega = 2 * convert(RealT, pi) * f180181gamma = totalgamma(u, equations)182183x1, x2 = x184si, co = sincos((x1 + x2 - t) * omega)185tmp1 = co * A * omega186tmp2 = si * A187tmp3 = gamma - 1188tmp4 = (2 * c - 1) * tmp3189tmp5 = (2 * tmp2 * gamma - 2 * tmp2 + tmp4 + 1) * tmp1190tmp6 = tmp2 + c191192# Here we compute an arbitrary number of different rhos. (one rho is double the next rho while the sum of all rhos is 1193du_rho = SVector{ncomponents(equations), real(equations)}(2^(i - 1) * (1 - 2) *194tmp1 / (1 -1952^ncomponents(equations))196for i in eachcomponent(equations))197198du1 = tmp5199du2 = tmp5200du3 = 2 * ((tmp6 - 1) * tmp3 + tmp6 * gamma) * tmp1201202du_other = SVector(du1, du2, du3)203204return vcat(du_other, du_rho)205end206207"""208initial_condition_weak_blast_wave(x, t, equations::CompressibleEulerMulticomponentEquations2D)209210A for multicomponent adapted weak blast wave taken from211- Sebastian Hennemann, Gregor J. Gassner (2020)212A provably entropy stable subcell shock capturing approach for high order split form DG213[arXiv: 2008.12044](https://arxiv.org/abs/2008.12044)214"""215function initial_condition_weak_blast_wave(x, t,216equations::CompressibleEulerMulticomponentEquations2D)217# From Hennemann & Gassner JCP paper 2020 (Sec. 6.3)218# Set up polar coordinates219RealT = eltype(x)220inicenter = SVector(0, 0)221x_norm = x[1] - inicenter[1]222y_norm = x[2] - inicenter[2]223r = sqrt(x_norm^2 + y_norm^2)224phi = atan(y_norm, x_norm)225sin_phi, cos_phi = sincos(phi)226227prim_rho = SVector{ncomponents(equations), real(equations)}(r > 0.5f0 ?2282^(i - 1) * (1 - 2) /229(RealT(1) -2302^ncomponents(equations)) :2312^(i - 1) * (1 - 2) *232RealT(1.1691) /233(1 -2342^ncomponents(equations))235for i in eachcomponent(equations))236237v1 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos_phi238v2 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * sin_phi239p = r > 0.5f0 ? one(RealT) : convert(RealT, 1.245)240241prim_other = SVector(v1, v2, p)242243return prim2cons(vcat(prim_other, prim_rho), equations)244end245246"""247boundary_condition_slip_wall(u_inner, normal_direction, x, t, surface_flux_function,248equations::CompressibleEulerMulticomponentEquations2D)249250Determine the boundary numerical surface flux for a slip wall condition.251Imposes a zero normal velocity at the wall.252Density is taken from the internal solution state and pressure is computed as an253exact solution of a 1D Riemann problem. Further details about this boundary state254are available in the paper:255- J. J. W. van der Vegt and H. van der Ven (2002)256Slip flow boundary conditions in discontinuous Galerkin discretizations of257the Euler equations of gas dynamics258[PDF](https://reports.nlr.nl/bitstream/handle/10921/692/TP-2002-300.pdf?sequence=1)259260Details about the 1D pressure Riemann solution can be found in Section 6.3.3 of the book261- Eleuterio F. Toro (2009)262Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction2633rd edition264[DOI: 10.1007/b79761](https://doi.org/10.1007/b79761)265266Should be used together with [`UnstructuredMesh2D`](@ref), [`P4estMesh`](@ref), or [`T8codeMesh`](@ref).267"""268@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector,269x, t,270surface_flux_function,271equations::CompressibleEulerMulticomponentEquations2D)272norm_ = norm(normal_direction)273# Normalize the vector without using `normalize` since we need to multiply by the `norm_` later274normal = normal_direction / norm_275276# rotate the internal solution state277u_local = rotate_to_x(u_inner, normal, equations)278279# compute the primitive variables280(v_normal, v_tangent, p_local, rhos_local...) = cons2prim(u_local, equations)281282rho_local = sum(rhos_local)283gamma = totalgamma(u_inner, equations)284285# Get the solution of the pressure Riemann problem286# See Section 6.3.3 of287# Eleuterio F. Toro (2009)288# Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction289# [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761)290if v_normal <= 0291sound_speed = sqrt(gamma * p_local / rho_local) # local sound speed292p_star = p_local *293(1 + 0.5f0 * (gamma - 1) * v_normal / sound_speed)^(2 *294gamma /295(gamma - 1.0f0))296else # v_normal > 0297A = 2 / ((gamma + 1) * rho_local)298B = p_local * (gamma - 1) / (gamma + 1)299p_star = p_local +3000.5f0 * v_normal / A *301(v_normal + sqrt(v_normal^2 + 4 * A * (p_local + B)))302end303304# For the slip wall we directly set the flux as the normal velocity is zero305return vcat(SVector(p_star * normal_direction[1],306p_star * normal_direction[2],3070.0f0),308zero(SVector{ncomponents(equations), eltype(u_inner)}))309end310311"""312boundary_condition_slip_wall(u_inner, orientation, direction, x, t,313surface_flux_function, equations::CompressibleEulerMulticomponentEquations2D)314315Should be used together with [`TreeMesh`](@ref).316"""317@inline function boundary_condition_slip_wall(u_inner, orientation,318direction, x, t,319surface_flux_function,320equations::CompressibleEulerMulticomponentEquations2D)321# get the appropriate normal vector from the orientation322RealT = eltype(u_inner)323if orientation == 1324normal_direction = SVector(one(RealT), zero(RealT))325else # orientation == 2326normal_direction = SVector(zero(RealT), one(RealT))327end328329# compute and return the flux using `boundary_condition_slip_wall` routine below330return boundary_condition_slip_wall(u_inner, normal_direction, direction,331x, t, surface_flux_function, equations)332end333334"""335boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector,336direction, x, t,337surface_flux_function, equations::CompressibleEulerMulticomponentEquations2D)338339Should be used together with [`StructuredMesh`](@ref).340"""341@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector,342direction, x, t,343surface_flux_function,344equations::CompressibleEulerMulticomponentEquations2D)345# flip sign of normal to make it outward pointing, then flip the sign of the normal flux back346# to be inward pointing on the -x and -y sides due to the orientation convention used by StructuredMesh347if isodd(direction)348boundary_flux = -boundary_condition_slip_wall(u_inner, -normal_direction,349x, t, surface_flux_function,350equations)351else352boundary_flux = boundary_condition_slip_wall(u_inner, normal_direction,353x, t, surface_flux_function,354equations)355end356357return boundary_flux358end359360# Calculate 1D flux for a single point361@inline function flux(u, orientation::Integer,362equations::CompressibleEulerMulticomponentEquations2D)363rho_v1, rho_v2, rho_e_total = u364365rho = density(u, equations)366367v1 = rho_v1 / rho368v2 = rho_v2 / rho369gamma = totalgamma(u, equations)370p = (gamma - 1) * (rho_e_total - 0.5f0 * rho * (v1^2 + v2^2))371372if orientation == 1373f_rho = densities(u, v1, equations)374f1 = rho_v1 * v1 + p375f2 = rho_v2 * v1376f3 = (rho_e_total + p) * v1377else378f_rho = densities(u, v2, equations)379f1 = rho_v1 * v2380f2 = rho_v2 * v2 + p381f3 = (rho_e_total + p) * v2382end383384f_other = SVector(f1, f2, f3)385386return vcat(f_other, f_rho)387end388389# Calculate 1D flux for a single point390@inline function flux(u, normal_direction::AbstractVector,391equations::CompressibleEulerMulticomponentEquations2D)392rho_v1, rho_v2, rho_e_total = u393394rho = density(u, equations)395396v1 = rho_v1 / rho397v2 = rho_v2 / rho398v_normal = v1 * normal_direction[1] + v2 * normal_direction[2]399gamma = totalgamma(u, equations)400p = (gamma - 1) * (rho_e_total - 0.5f0 * rho * (v1^2 + v2^2))401402f_rho = densities(u, v_normal, equations)403f1 = rho_v1 * v_normal + p * normal_direction[1]404f2 = rho_v2 * v_normal + p * normal_direction[2]405f3 = (rho_e_total + p) * v_normal406407f_other = SVector(f1, f2, f3)408409return vcat(f_other, f_rho)410end411412"""413flux_chandrashekar(u_ll, u_rr, orientation, equations::CompressibleEulerMulticomponentEquations2D)414415Adaption of the entropy conserving two-point flux by416- Ayoub Gouasmi, Karthik Duraisamy (2020)417"Formulation of Entropy-Stable schemes for the multicomponent compressible Euler equations"418[arXiv:1904.00972v3](https://arxiv.org/abs/1904.00972) [math.NA] 4 Feb 2020419"""420@inline function flux_chandrashekar(u_ll, u_rr, orientation::Integer,421equations::CompressibleEulerMulticomponentEquations2D)422# Unpack left and right state423@unpack gammas, gas_constants, cv = equations424rho_v1_ll, rho_v2_ll, rho_e_total_ll = u_ll425rho_v1_rr, rho_v2_rr, rho_e_total_rr = u_rr426rhok_mean = SVector{ncomponents(equations), real(equations)}(ln_mean(u_ll[i + 3],427u_rr[i + 3])428for i in eachcomponent(equations))429rhok_avg = SVector{ncomponents(equations), real(equations)}(0.5f0 * (u_ll[i + 3] +430u_rr[i + 3])431for i in eachcomponent(equations))432433# Iterating over all partial densities434rho_ll = density(u_ll, equations)435rho_rr = density(u_rr, equations)436437# extract velocities438v1_ll = rho_v1_ll / rho_ll439v2_ll = rho_v2_ll / rho_ll440v1_rr = rho_v1_rr / rho_rr441v2_rr = rho_v2_rr / rho_rr442v1_avg = 0.5f0 * (v1_ll + v1_rr)443v2_avg = 0.5f0 * (v2_ll + v2_rr)444v1_square = 0.5f0 * (v1_ll^2 + v1_rr^2)445v2_square = 0.5f0 * (v2_ll^2 + v2_rr^2)446v_sum = v1_avg + v2_avg447448RealT = eltype(u_ll)449enth = zero(RealT)450help1_ll = zero(RealT)451help1_rr = zero(RealT)452453for i in eachcomponent(equations)454enth += rhok_avg[i] * gas_constants[i]455help1_ll += u_ll[i + 3] * cv[i]456help1_rr += u_rr[i + 3] * cv[i]457end458459T_ll = (rho_e_total_ll - 0.5f0 * rho_ll * (v1_ll^2 + v2_ll^2)) / help1_ll460T_rr = (rho_e_total_rr - 0.5f0 * rho_rr * (v1_rr^2 + v2_rr^2)) / help1_rr461T = 0.5f0 * (1 / T_ll + 1 / T_rr)462T_log = ln_mean(1 / T_ll, 1 / T_rr)463464# Calculate fluxes depending on orientation465help1 = zero(RealT)466help2 = zero(RealT)467if orientation == 1468f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * v1_avg469for i in eachcomponent(equations))470for i in eachcomponent(equations)471help1 += f_rho[i] * cv[i]472help2 += f_rho[i]473end474f1 = (help2) * v1_avg + enth / T475f2 = (help2) * v2_avg476f3 = (help1) / T_log - 0.5f0 * (v1_square + v2_square) * (help2) + v1_avg * f1 +477v2_avg * f2478else479f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * v2_avg480for i in eachcomponent(equations))481for i in eachcomponent(equations)482help1 += f_rho[i] * cv[i]483help2 += f_rho[i]484end485f1 = (help2) * v1_avg486f2 = (help2) * v2_avg + enth / T487f3 = (help1) / T_log - 0.5f0 * (v1_square + v2_square) * (help2) + v1_avg * f1 +488v2_avg * f2489end490f_other = SVector(f1, f2, f3)491492return vcat(f_other, f_rho)493end494495"""496flux_ranocha(u_ll, u_rr, orientation_or_normal_direction,497equations::CompressibleEulerMulticomponentEquations2D)498499Adaption of the entropy conserving and kinetic energy preserving two-point flux by500- Hendrik Ranocha (2018)501Generalised Summation-by-Parts Operators and Entropy Stability of Numerical Methods502for Hyperbolic Balance Laws503[PhD thesis, TU Braunschweig](https://cuvillier.de/en/shop/publications/7743)504See also505- Hendrik Ranocha (2020)506Entropy Conserving and Kinetic Energy Preserving Numerical Methods for507the Euler Equations Using Summation-by-Parts Operators508[Proceedings of ICOSAHOM 2018](https://doi.org/10.1007/978-3-030-39647-3_42)509"""510@inline function flux_ranocha(u_ll, u_rr, orientation::Integer,511equations::CompressibleEulerMulticomponentEquations2D)512# Unpack left and right state513@unpack gammas, gas_constants, cv = equations514rho_v1_ll, rho_v2_ll, rho_e_total_ll = u_ll515rho_v1_rr, rho_v2_rr, rho_e_total_rr = u_rr516rhok_mean = SVector{ncomponents(equations), real(equations)}(ln_mean(u_ll[i + 3],517u_rr[i + 3])518for i in eachcomponent(equations))519rhok_avg = SVector{ncomponents(equations), real(equations)}(0.5f0 * (u_ll[i + 3] +520u_rr[i + 3])521for i in eachcomponent(equations))522523# Iterating over all partial densities524rho_ll = density(u_ll, equations)525rho_rr = density(u_rr, equations)526527# Calculating gamma528gamma = totalgamma(0.5f0 * (u_ll + u_rr), equations)529inv_gamma_minus_one = 1 / (gamma - 1)530531# extract velocities532v1_ll = rho_v1_ll / rho_ll533v1_rr = rho_v1_rr / rho_rr534v1_avg = 0.5f0 * (v1_ll + v1_rr)535v2_ll = rho_v2_ll / rho_ll536v2_rr = rho_v2_rr / rho_rr537v2_avg = 0.5f0 * (v2_ll + v2_rr)538velocity_square_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr)539540# helpful variables541RealT = eltype(u_ll)542help1_ll = zero(RealT)543help1_rr = zero(RealT)544enth_ll = zero(RealT)545enth_rr = zero(RealT)546for i in eachcomponent(equations)547enth_ll += u_ll[i + 3] * gas_constants[i]548enth_rr += u_rr[i + 3] * gas_constants[i]549help1_ll += u_ll[i + 3] * cv[i]550help1_rr += u_rr[i + 3] * cv[i]551end552553# temperature and pressure554T_ll = (rho_e_total_ll - 0.5f0 * rho_ll * (v1_ll^2 + v2_ll^2)) / help1_ll555T_rr = (rho_e_total_rr - 0.5f0 * rho_rr * (v1_rr^2 + v2_rr^2)) / help1_rr556p_ll = T_ll * enth_ll557p_rr = T_rr * enth_rr558p_avg = 0.5f0 * (p_ll + p_rr)559inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll)560561f_rho_sum = zero(RealT)562if orientation == 1563f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * v1_avg564for i in eachcomponent(equations))565for i in eachcomponent(equations)566f_rho_sum += f_rho[i]567end568f1 = f_rho_sum * v1_avg + p_avg569f2 = f_rho_sum * v2_avg570f3 = f_rho_sum * (velocity_square_avg + inv_rho_p_mean * inv_gamma_minus_one) +5710.5f0 * (p_ll * v1_rr + p_rr * v1_ll)572else573f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * v2_avg574for i in eachcomponent(equations))575for i in eachcomponent(equations)576f_rho_sum += f_rho[i]577end578f1 = f_rho_sum * v1_avg579f2 = f_rho_sum * v2_avg + p_avg580f3 = f_rho_sum * (velocity_square_avg + inv_rho_p_mean * inv_gamma_minus_one) +5810.5f0 * (p_ll * v2_rr + p_rr * v2_ll)582end583584# momentum and energy flux585f_other = SVector(f1, f2, f3)586587return vcat(f_other, f_rho)588end589590@inline function flux_ranocha(u_ll, u_rr, normal_direction::AbstractVector,591equations::CompressibleEulerMulticomponentEquations2D)592# Unpack left and right state593@unpack gammas, gas_constants, cv = equations594rho_v1_ll, rho_v2_ll, rho_e_total_ll = u_ll595rho_v1_rr, rho_v2_rr, rho_e_total_rr = u_rr596rhok_mean = SVector{ncomponents(equations), real(equations)}(ln_mean(u_ll[i + 3],597u_rr[i + 3])598for i in eachcomponent(equations))599rhok_avg = SVector{ncomponents(equations), real(equations)}(0.5f0 * (u_ll[i + 3] +600u_rr[i + 3])601for i in eachcomponent(equations))602603# Iterating over all partial densities604rho_ll = density(u_ll, equations)605rho_rr = density(u_rr, equations)606607# Calculating gamma608gamma = totalgamma(0.5f0 * (u_ll + u_rr), equations)609inv_gamma_minus_one = 1 / (gamma - 1)610611# extract velocities612v1_ll = rho_v1_ll / rho_ll613v1_rr = rho_v1_rr / rho_rr614v1_avg = 0.5f0 * (v1_ll + v1_rr)615v2_ll = rho_v2_ll / rho_ll616v2_rr = rho_v2_rr / rho_rr617v2_avg = 0.5f0 * (v2_ll + v2_rr)618velocity_square_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr)619v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2]620v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2]621622# helpful variables623RealT = eltype(u_ll)624help1_ll = zero(RealT)625help1_rr = zero(RealT)626enth_ll = zero(RealT)627enth_rr = zero(RealT)628for i in eachcomponent(equations)629enth_ll += u_ll[i + 3] * gas_constants[i]630enth_rr += u_rr[i + 3] * gas_constants[i]631help1_ll += u_ll[i + 3] * cv[i]632help1_rr += u_rr[i + 3] * cv[i]633end634635# temperature and pressure636T_ll = (rho_e_total_ll - 0.5f0 * rho_ll * (v1_ll^2 + v2_ll^2)) / help1_ll637T_rr = (rho_e_total_rr - 0.5f0 * rho_rr * (v1_rr^2 + v2_rr^2)) / help1_rr638p_ll = T_ll * enth_ll639p_rr = T_rr * enth_rr640p_avg = 0.5f0 * (p_ll + p_rr)641inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll)642643f_rho_sum = zero(RealT)644f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * 0.5f0 *645(v_dot_n_ll + v_dot_n_rr)646for i in eachcomponent(equations))647for i in eachcomponent(equations)648f_rho_sum += f_rho[i]649end650f1 = f_rho_sum * v1_avg + p_avg * normal_direction[1]651f2 = f_rho_sum * v2_avg + p_avg * normal_direction[2]652f3 = f_rho_sum * (velocity_square_avg + inv_rho_p_mean * inv_gamma_minus_one) +6530.5f0 * (p_ll * v_dot_n_rr + p_rr * v_dot_n_ll)654655# momentum and energy flux656f_other = SVector(f1, f2, f3)657658return vcat(f_other, f_rho)659end660661# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation662@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,663equations::CompressibleEulerMulticomponentEquations2D)664rho_v1_ll, rho_v2_ll, rho_e_total_ll = u_ll665rho_v1_rr, rho_v2_rr, rho_e_total_rr = u_rr666667# Get the density and gas gamma668rho_ll = density(u_ll, equations)669rho_rr = density(u_rr, equations)670gamma_ll = totalgamma(u_ll, equations)671gamma_rr = totalgamma(u_rr, equations)672673# Get the velocities based on direction674if orientation == 1675v_ll = rho_v1_ll / rho_ll676v_rr = rho_v1_rr / rho_rr677else # orientation == 2678v_ll = rho_v2_ll / rho_ll679v_rr = rho_v2_rr / rho_rr680end681682# Compute the sound speeds on the left and right683p_ll = (gamma_ll - 1) *684(rho_e_total_ll - 0.5f0 * (rho_v1_ll^2 + rho_v2_ll^2) / rho_ll)685c_ll = sqrt(gamma_ll * p_ll / rho_ll)686p_rr = (gamma_rr - 1) *687(rho_e_total_rr - 0.5f0 * (rho_v1_rr^2 + rho_v2_rr^2) / rho_rr)688c_rr = sqrt(gamma_rr * p_rr / rho_rr)689690return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr)691end692693# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation694@inline function max_abs_speed_naive(u_ll::SVector{N, T}, u_rr::SVector{N, T},695normal_direction::SVector{2, T},696equations::CompressibleEulerMulticomponentEquations2D) where {697N,698T <:699AbstractFloat700}701# Unpack conservative variables702rho_v1_ll, rho_v2_ll, rho_e_total_ll = u_ll703rho_v1_rr, rho_v2_rr, rho_e_total_rr = u_rr704705# Get densities and gammas706rho_ll = T(density(u_ll, equations))707rho_rr = T(density(u_rr, equations))708gamma_ll = T(totalgamma(u_ll, equations))709gamma_rr = T(totalgamma(u_rr, equations))710711# Velocity components712v_ll_vec = SVector(rho_v1_ll / rho_ll, rho_v2_ll / rho_ll)713v_rr_vec = SVector(rho_v1_rr / rho_rr, rho_v2_rr / rho_rr)714715# Project velocities onto the direction normal_direction.716v_ll = dot(v_ll_vec, normal_direction)717v_rr = dot(v_rr_vec, normal_direction)718719# Compute pressures720p_ll = (gamma_ll - one(T)) *721(rho_e_total_ll - 0.5f0 * dot(v_ll_vec, v_ll_vec) * rho_ll)722p_rr = (gamma_rr - one(T)) *723(rho_e_total_rr - 0.5f0 * dot(v_rr_vec, v_rr_vec) * rho_rr)724725# Sound speeds726c_ll = sqrt(gamma_ll * p_ll / rho_ll)727c_rr = sqrt(gamma_rr * p_rr / rho_rr)728729return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr)730end731732# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`733@inline function max_abs_speed(u_ll, u_rr, orientation::Integer,734equations::CompressibleEulerMulticomponentEquations2D)735rho_v1_ll, rho_v2_ll, rho_e_total_ll = u_ll736rho_v1_rr, rho_v2_rr, rho_e_total_rr = u_rr737738# Get the density and gas gamma739rho_ll = density(u_ll, equations)740rho_rr = density(u_rr, equations)741gamma_ll = totalgamma(u_ll, equations)742gamma_rr = totalgamma(u_rr, equations)743744# Get the velocities based on direction745if orientation == 1746v_ll = rho_v1_ll / rho_ll747v_rr = rho_v1_rr / rho_rr748else # orientation == 2749v_ll = rho_v2_ll / rho_ll750v_rr = rho_v2_rr / rho_rr751end752753# Compute the sound speeds on the left and right754p_ll = (gamma_ll - 1) *755(rho_e_total_ll - 0.5f0 * (rho_v1_ll^2 + rho_v2_ll^2) / rho_ll)756c_ll = sqrt(gamma_ll * p_ll / rho_ll)757p_rr = (gamma_rr - 1) *758(rho_e_total_rr - 0.5f0 * (rho_v1_rr^2 + rho_v2_rr^2) / rho_rr)759c_rr = sqrt(gamma_rr * p_rr / rho_rr)760761return max(abs(v_ll) + c_ll, abs(v_rr) + c_rr)762end763764@inline function max_abs_speeds(u,765equations::CompressibleEulerMulticomponentEquations2D)766rho_v1, rho_v2, rho_e_total = u767768rho = density(u, equations)769v1 = rho_v1 / rho770v2 = rho_v2 / rho771772gamma = totalgamma(u, equations)773p = (gamma - 1) * (rho_e_total - 0.5f0 * rho * (v1^2 + v2^2))774c = sqrt(gamma * p / rho)775776return (abs(v1) + c, abs(v2) + c)777end778779@inline function rotate_to_x(u, normal_vector,780equations::CompressibleEulerMulticomponentEquations2D)781# cos and sin of the angle between the x-axis and the normalized normal_vector are782# the normalized vector's x and y coordinates respectively (see unit circle).783c = normal_vector[1]784s = normal_vector[2]785786# Apply the 2D rotation matrix with normal and tangent directions of the form787# [ n_1 n_2 0 0;788# t_1 t_2 0 0;789# 0 0 1 0790# 0 0 0 1]791# where t_1 = -n_2 and t_2 = n_1792793densities = @view u[4:end]794return SVector(c * u[1] + s * u[2],795-s * u[1] + c * u[2],796u[3],797densities...)798end799800# Called inside `FluxRotated` in `numerical_fluxes.jl` so the direction801# has been normalized prior to this back-rotation of the state vector802@inline function rotate_from_x(u, normal_vector,803equations::CompressibleEulerMulticomponentEquations2D)804# cos and sin of the angle between the x-axis and the normalized normal_vector are805# the normalized vector's x and y coordinates respectively (see unit circle).806c = normal_vector[1]807s = normal_vector[2]808809# Apply the 2D back-rotation matrix with normal and tangent directions of the form810# [ n_1 t_1 0 0;811# n_2 t_2 0 0;812# 0 0 1 0;813# 0 0 0 1 ]814# where t_1 = -n_2 and t_2 = n_1815816densities = @view u[4:end]817return SVector(c * u[1] - s * u[2],818s * u[1] + c * u[2],819u[3],820densities...)821end822823# Convert conservative variables to primitive824@inline function cons2prim(u, equations::CompressibleEulerMulticomponentEquations2D)825rho_v1, rho_v2, rho_e_total = u826827prim_rho = SVector{ncomponents(equations), real(equations)}(u[i + 3]828for i in eachcomponent(equations))829830rho = density(u, equations)831v1 = rho_v1 / rho832v2 = rho_v2 / rho833gamma = totalgamma(u, equations)834p = (gamma - 1) * (rho_e_total - 0.5f0 * rho * (v1^2 + v2^2))835prim_other = SVector(v1, v2, p)836837return vcat(prim_other, prim_rho)838end839840# Convert conservative variables to entropy841@inline function cons2entropy(u, equations::CompressibleEulerMulticomponentEquations2D)842@unpack cv, gammas, gas_constants = equations843rho_v1, rho_v2, rho_e_total = u844845rho = density(u, equations)846847# Multicomponent stuff848RealT = eltype(u)849help1 = zero(RealT)850gas_constant = zero(RealT)851for i in eachcomponent(equations)852help1 += u[i + 3] * cv[i]853gas_constant += gas_constants[i] * (u[i + 3] / rho)854end855856v1 = rho_v1 / rho857v2 = rho_v2 / rho858v_square = v1^2 + v2^2859gamma = totalgamma(u, equations)860861p = (gamma - 1) * (rho_e_total - 0.5f0 * rho * v_square)862s = log(p) - gamma * log(rho) - log(gas_constant)863rho_p = rho / p864T = (rho_e_total - 0.5f0 * rho * v_square) / (help1)865866entrop_rho = SVector{ncomponents(equations), real(equations)}((cv[i] *867(1 - log(T)) +868gas_constants[i] *869(1 + log(u[i + 3])) -870v_square / (2 * T))871for i in eachcomponent(equations))872873w1 = gas_constant * v1 * rho_p874w2 = gas_constant * v2 * rho_p875w3 = gas_constant * (-rho_p)876877entrop_other = SVector(w1, w2, w3)878879return vcat(entrop_other, entrop_rho)880end881882# Convert entropy variables to conservative variables883@inline function entropy2cons(w, equations::CompressibleEulerMulticomponentEquations2D)884@unpack gammas, gas_constants, cp, cv = equations885T = -1 / w[3]886v1 = w[1] * T887v2 = w[2] * T888v_squared = v1^2 + v2^2889cons_rho = SVector{ncomponents(equations), real(equations)}(exp((w[i + 3] -890cv[i] *891(1 - log(T)) +892v_squared /893(2 * T)) /894gas_constants[i] -8951)896for i in eachcomponent(equations))897898RealT = eltype(w)899rho = zero(RealT)900help1 = zero(RealT)901help2 = zero(RealT)902p = zero(RealT)903for i in eachcomponent(equations)904rho += cons_rho[i]905help1 += cons_rho[i] * cv[i] * gammas[i]906help2 += cons_rho[i] * cv[i]907p += cons_rho[i] * gas_constants[i] * T908end909u1 = rho * v1910u2 = rho * v2911gamma = help1 / help2912u3 = p / (gamma - 1) + 0.5f0 * rho * v_squared913cons_other = SVector(u1, u2, u3)914return vcat(cons_other, cons_rho)915end916917# Convert primitive to conservative variables918@inline function prim2cons(prim, equations::CompressibleEulerMulticomponentEquations2D)919@unpack cv, gammas = equations920v1, v2, p = prim921922cons_rho = SVector{ncomponents(equations), real(equations)}(prim[i + 3]923for i in eachcomponent(equations))924rho = density(prim, equations)925gamma = totalgamma(prim, equations)926927rho_v1 = rho * v1928rho_v2 = rho * v2929rho_e_total = p / (gamma - 1) + 0.5f0 * (rho_v1 * v1 + rho_v2 * v2)930931cons_other = SVector(rho_v1, rho_v2, rho_e_total)932933return vcat(cons_other, cons_rho)934end935936@inline function total_entropy(u, equations::CompressibleEulerMulticomponentEquations2D)937@unpack cv, gammas, gas_constants = equations938rho = density(u, equations)939T = temperature(u, equations)940941RealT = eltype(u)942total_entropy = zero(RealT)943for i in eachcomponent(equations)944total_entropy -= u[i + 3] * (cv[i] * log(T) - gas_constants[i] * log(u[i + 3]))945end946947return total_entropy948end949950@inline function temperature(u, equations::CompressibleEulerMulticomponentEquations2D)951@unpack cv, gammas, gas_constants = equations952953rho_v1, rho_v2, rho_e_total = u954955rho = density(u, equations)956RealT = eltype(u)957help1 = zero(RealT)958959for i in eachcomponent(equations)960help1 += u[i + 3] * cv[i]961end962963v1 = rho_v1 / rho964v2 = rho_v2 / rho965v_square = v1^2 + v2^2966T = (rho_e_total - 0.5f0 * rho * v_square) / help1967968return T969end970971"""972totalgamma(u, equations::CompressibleEulerMulticomponentEquations2D)973974Function that calculates the total gamma out of all partial gammas using the975partial density fractions as well as the partial specific heats at constant volume.976"""977@inline function totalgamma(u, equations::CompressibleEulerMulticomponentEquations2D)978@unpack cv, gammas = equations979980RealT = eltype(u)981help1 = zero(RealT)982help2 = zero(RealT)983984for i in eachcomponent(equations)985help1 += u[i + 3] * cv[i] * gammas[i]986help2 += u[i + 3] * cv[i]987end988989return help1 / help2990end991992@inline function pressure(u, equations::CompressibleEulerMulticomponentEquations2D)993rho_v1, rho_v2, rho_e_total = u994995rho = density(u, equations)996gamma = totalgamma(u, equations)997998p = (gamma - 1) * (rho_e_total - 0.5f0 * (rho_v1^2 + rho_v2^2) / rho)9991000return p1001end10021003@inline function density_pressure(u,1004equations::CompressibleEulerMulticomponentEquations2D)1005rho_v1, rho_v2, rho_e_total = u10061007rho = density(u, equations)1008gamma = totalgamma(u, equations)1009rho_times_p = (gamma - 1) * (rho * rho_e_total - 0.5f0 * (rho_v1^2 + rho_v2^2))10101011return rho_times_p1012end10131014@inline function density(u, equations::CompressibleEulerMulticomponentEquations2D)1015RealT = eltype(u)1016rho = zero(RealT)10171018for i in eachcomponent(equations)1019rho += u[i + 3]1020end10211022return rho1023end10241025@inline function densities(u, v, equations::CompressibleEulerMulticomponentEquations2D)1026return SVector{ncomponents(equations), real(equations)}(u[i + 3] * v1027for i in eachcomponent(equations))1028end10291030@inline function velocity(u, equations::CompressibleEulerMulticomponentEquations2D)1031rho = density(u, equations)1032v1 = u[1] / rho1033v2 = u[2] / rho1034return SVector(v1, v2)1035end10361037@inline function velocity(u, orientation::Int,1038equations::CompressibleEulerMulticomponentEquations2D)1039rho = density(u, equations)1040v = u[orientation] / rho1041return v1042end1043end # @muladd104410451046