Path: blob/main/src/equations/ideal_glm_mhd_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"""8IdealGlmMhdMulticomponentEquations2D(; gammas, gas_constants)910The ideal compressible multicomponent GLM-MHD equations11```math12\frac{\partial}{\partial t}13\begin{pmatrix}14\rho \mathbf{v} \\ \rho e_{\text{total}} \\ \mathbf{B} \\ \psi \\ \rho_1 \\ \vdots \\ \rho_{n}15\end{pmatrix}16+17\nabla \cdot18\begin{pmatrix}19\rho (\mathbf{v} \otimes \mathbf{v}) + (p + \frac{1}{2} \Vert \mathbf{B} \Vert_2 ^2) \underline{I} - \mathbf{B} \otimes \mathbf{B} \\20\mathbf{v} (\frac{1}{2} \rho \Vert \mathbf{v} \Vert_2 ^2 + \frac{\gamma p}{\gamma - 1} + \Vert \mathbf{B} \Vert_2 ^2) - \mathbf{B} (\mathbf{v} \cdot \mathbf{B}) + c_h \psi \mathbf{B} \\21\mathbf{v} \otimes \mathbf{B} - \mathbf{B} \otimes \mathbf{v} + c_h \psi \underline{I} \\22c_h \mathbf{B} \\ \rho_1 \mathbf{v} \\ \vdots \\ \rho_{n} \mathbf{v}23\end{pmatrix}24+25(\nabla \cdot \mathbf{B})26\begin{pmatrix}27\mathbf{B} \\ \mathbf{v} \cdot \mathbf{B} \\ \mathbf{v} \\ 0 \\ 0 \\ \vdots \\ 028\end{pmatrix}29+30(\nabla \psi) \cdot31\begin{pmatrix}320 \\ \mathbf{v} \cdot \psi \\ 0 \\ \mathbf{v} \\ \mathbf{0} \\ \vdots \\ \mathbf{0}33\end{pmatrix}34=35\begin{pmatrix}36\mathbf{0} \\ 0 \\ \mathbf{0} \\ 0 \\ 0 \\ \vdots \\ 037\end{pmatrix}38```39for calorically perfect gases in two space dimensions.40Here, ``\rho_i`` is the density of component ``i``, ``\rho=\sum_{i=1}^n\rho_i`` the sum of the individual ``\rho_i``,41``\mathbf{v}`` the velocity, ``\mathbf{B}`` the magnetic field, ``c_h`` the hyperbolic divergence cleaning speed,42``\psi`` the generalized Lagrangian Multiplier (GLM),43``e_{\text{total}}`` the specific total energy, and44```math45p = (\gamma - 1) \left( \rho e_{\text{total}} - \frac{1}{2} \rho \Vert \mathbf{v} \Vert_2 ^2 - \frac{1}{2} \Vert \mathbf{B} \Vert_2 ^2 - \frac{1}{2} \psi^2 \right)46```47the pressure,48```math49\gamma=\frac{\sum_{i=1}^n\rho_i C_{v,i}\gamma_i}{\sum_{i=1}^n\rho_i C_{v,i}}50```51total heat capacity ratio, ``\gamma_i`` heat capacity ratio of component ``i``,52```math53C_{v,i}=\frac{R_i}{\gamma_i-1}54```55specific heat capacity at constant volume of component ``i`` and ``\underline{I}`` the ``3\times 3`` identity matrix.5657In case of more than one component, the specific heat ratios `gammas` and the gas constants58`gas_constants` should be passed as tuples, e.g., `gammas = (1.4, 1.667)`.5960The remaining variables like the specific heats at constant volume `cv` or the specific heats at61constant pressure `cp` are then calculated considering a calorically perfect gas.62"""63struct IdealGlmMhdMulticomponentEquations2D{NVARS, NCOMP, RealT <: Real} <:64AbstractIdealGlmMhdMulticomponentEquations{2, NVARS, NCOMP}65gammas::SVector{NCOMP, RealT}66gas_constants::SVector{NCOMP, RealT}67cv::SVector{NCOMP, RealT}68cp::SVector{NCOMP, RealT}69c_h::RealT # GLM cleaning speed7071function IdealGlmMhdMulticomponentEquations2D{NVARS, NCOMP, RealT}(gammas::SVector{NCOMP,72RealT},73gas_constants::SVector{NCOMP,74RealT},75c_h::RealT) where {76NVARS,77NCOMP,78RealT <:79Real80}81NCOMP >= 1 ||82throw(DimensionMismatch("`gammas` and `gas_constants` have to be filled with at least one value"))8384cv = gas_constants ./ (gammas .- 1)85cp = gas_constants + gas_constants ./ (gammas .- 1)8687return new(gammas, gas_constants, cv, cp, c_h)88end89end9091function IdealGlmMhdMulticomponentEquations2D(; gammas, gas_constants)92_gammas = promote(gammas...)93_gas_constants = promote(gas_constants...)94RealT = promote_type(eltype(_gammas), eltype(_gas_constants))95__gammas = SVector(map(RealT, _gammas))96__gas_constants = SVector(map(RealT, _gas_constants))9798c_h = convert(RealT, NaN)99100NVARS = length(_gammas) + 8101NCOMP = length(_gammas)102103return IdealGlmMhdMulticomponentEquations2D{NVARS, NCOMP, RealT}(__gammas,104__gas_constants,105c_h)106end107108# Outer constructor for `@reset` works correctly109function IdealGlmMhdMulticomponentEquations2D(gammas, gas_constants, cv, cp, c_h)110_gammas = promote(gammas...)111_gas_constants = promote(gas_constants...)112RealT = promote_type(eltype(_gammas), eltype(_gas_constants))113__gammas = SVector(map(RealT, _gammas))114__gas_constants = SVector(map(RealT, _gas_constants))115116c_h = convert(RealT, c_h)117118NVARS = length(_gammas) + 8119NCOMP = length(_gammas)120121return IdealGlmMhdMulticomponentEquations2D{NVARS, NCOMP, RealT}(__gammas,122__gas_constants,123c_h)124end125126@inline function Base.real(::IdealGlmMhdMulticomponentEquations2D{NVARS, NCOMP, RealT}) where {127NVARS,128NCOMP,129RealT130}131return RealT132end133134have_nonconservative_terms(::IdealGlmMhdMulticomponentEquations2D) = True()135136function varnames(::typeof(cons2cons), equations::IdealGlmMhdMulticomponentEquations2D)137cons = ("rho_v1", "rho_v2", "rho_v3", "rho_e_total", "B1", "B2", "B3", "psi")138rhos = ntuple(n -> "rho" * string(n), Val(ncomponents(equations)))139return (cons..., rhos...)140end141142function varnames(::typeof(cons2prim), equations::IdealGlmMhdMulticomponentEquations2D)143prim = ("v1", "v2", "v3", "p", "B1", "B2", "B3", "psi")144rhos = ntuple(n -> "rho" * string(n), Val(ncomponents(equations)))145return (prim..., rhos...)146end147148function default_analysis_integrals(::IdealGlmMhdMulticomponentEquations2D)149return (entropy_timederivative, Val(:l2_divb), Val(:linf_divb))150end151152# Helper function to extract the magnetic field vector from the conservative variables153magnetic_field(u, equations::IdealGlmMhdMulticomponentEquations2D) = SVector(u[5], u[6],154u[7])155156"""157initial_condition_convergence_test(x, t, equations::IdealGlmMhdMulticomponentEquations2D)158159An Alfvén wave as smooth initial condition used for convergence tests.160"""161function initial_condition_convergence_test(x, t,162equations::IdealGlmMhdMulticomponentEquations2D)163# smooth Alfvén wave test from Derigs et al. FLASH (2016)164# domain must be set to [0, 1/cos(α)] x [0, 1/sin(α)], γ = 5/3165RealT = eltype(x)166alpha = 0.25f0 * convert(RealT, pi)167x_perp = x[1] * cos(alpha) + x[2] * sin(alpha)168B_perp = convert(RealT, 0.1) * sinpi(2 * x_perp)169rho = one(RealT)170prim_rho = SVector{ncomponents(equations), real(equations)}(2^(i - 1) * (1 - 2) *171rho / (1 -1722^ncomponents(equations))173for i in eachcomponent(equations))174175v1 = -B_perp * sin(alpha)176v2 = B_perp * cos(alpha)177v3 = convert(RealT, 0.1) * cospi(2 * x_perp)178p = convert(RealT, 0.1)179B1 = cos(alpha) + v1180B2 = sin(alpha) + v2181B3 = v3182psi = 0183prim_other = SVector(v1, v2, v3, p, B1, B2, B3, psi)184185return prim2cons(vcat(prim_other, prim_rho), equations)186end187188"""189initial_condition_weak_blast_wave(x, t, equations::IdealGlmMhdMulticomponentEquations2D)190191A weak blast wave adapted from192- Sebastian Hennemann, Gregor J. Gassner (2020)193A provably entropy stable subcell shock capturing approach for high order split form DG194[arXiv: 2008.12044](https://arxiv.org/abs/2008.12044)195"""196function initial_condition_weak_blast_wave(x, t,197equations::IdealGlmMhdMulticomponentEquations2D)198# Adapted MHD version of the weak blast wave from Hennemann & Gassner JCP paper 2020 (Sec. 6.3)199# Same discontinuity in the velocities but with magnetic fields200# Set up polar coordinates201RealT = eltype(x)202inicenter = SVector(0, 0)203x_norm = x[1] - inicenter[1]204y_norm = x[2] - inicenter[2]205r = sqrt(x_norm^2 + y_norm^2)206phi = atan(y_norm, x_norm)207sin_phi, cos_phi = sincos(phi)208209# We initialize each species with a fraction of the total density `rho`, such210# that the sum of the densities is `rho := density(prim, equations)`. The density of211# a species is double the density of the next species.212prim_rho = SVector{ncomponents(equations), real(equations)}(r > 0.5f0 ?2132^(i - 1) * (1 - 2) /214(RealT(1) -2152^ncomponents(equations)) :2162^(i - 1) * (1 - 2) *217RealT(1.1691) /218(1 -2192^ncomponents(equations))220for i in eachcomponent(equations))221222v1 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos_phi223v2 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * sin_phi224p = r > 0.5f0 ? one(RealT) : convert(RealT, 1.245)225226prim_other = SVector(v1, v2, 0, p, 1, 1, 1, 0)227228return prim2cons(vcat(prim_other, prim_rho), equations)229end230231# Calculate 1D flux for a single point232@inline function flux(u, orientation::Integer,233equations::IdealGlmMhdMulticomponentEquations2D)234rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = u235@unpack c_h = equations236237rho = density(u, equations)238239v1 = rho_v1 / rho240v2 = rho_v2 / rho241v3 = rho_v3 / rho242kin_en = 0.5f0 * rho * (v1^2 + v2^2 + v3^2)243mag_en = 0.5f0 * (B1^2 + B2^2 + B3^2)244gamma = totalgamma(u, equations)245p = (gamma - 1) * (rho_e_total - kin_en - mag_en - 0.5f0 * psi^2)246247if orientation == 1248f_rho = densities(u, v1, equations)249f1 = rho_v1 * v1 + p + mag_en - B1^2250f2 = rho_v1 * v2 - B1 * B2251f3 = rho_v1 * v3 - B1 * B3252f4 = (kin_en + gamma * p / (gamma - 1) + 2 * mag_en) * v1 -253B1 * (v1 * B1 + v2 * B2 + v3 * B3) + c_h * psi * B1254f5 = c_h * psi255f6 = v1 * B2 - v2 * B1256f7 = v1 * B3 - v3 * B1257f8 = c_h * B1258else # orientation == 2259f_rho = densities(u, v2, equations)260f1 = rho_v2 * v1 - B1 * B2261f2 = rho_v2 * v2 + p + mag_en - B2^2262f3 = rho_v2 * v3 - B2 * B3263f4 = (kin_en + gamma * p / (gamma - 1) + 2 * mag_en) * v2 -264B2 * (v1 * B1 + v2 * B2 + v3 * B3) + c_h * psi * B2265f5 = v2 * B1 - v1 * B2266f6 = c_h * psi267f7 = v2 * B3 - v3 * B2268f8 = c_h * B2269end270271f_other = SVector(f1, f2, f3, f4, f5, f6, f7, f8)272273return vcat(f_other, f_rho)274end275276"""277flux_nonconservative_powell(u_ll, u_rr, orientation::Integer,278equations::IdealGlmMhdMulticomponentEquations2D)279280Non-symmetric two-point flux discretizing the nonconservative (source) term of281Powell and the Galilean nonconservative term associated with the GLM multiplier282of the [`IdealGlmMhdMulticomponentEquations2D`](@ref).283284## References285- Marvin Bohm, Andrew R.Winters, Gregor J. Gassner, Dominik Derigs,286Florian Hindenlang, Joachim Saur287An entropy stable nodal discontinuous Galerkin method for the resistive MHD288equations. Part I: Theory and numerical verification289[DOI: 10.1016/j.jcp.2018.06.027](https://doi.org/10.1016/j.jcp.2018.06.027)290"""291@inline function flux_nonconservative_powell(u_ll, u_rr, orientation::Integer,292equations::IdealGlmMhdMulticomponentEquations2D)293rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll294rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr295296rho_ll = density(u_ll, equations)297298v1_ll = rho_v1_ll / rho_ll299v2_ll = rho_v2_ll / rho_ll300v3_ll = rho_v3_ll / rho_ll301v_dot_B_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll302303# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)304# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})305# Note that the order of conserved variables is changed compared to the306# standard GLM MHD equations, i.e., the densities are moved to the end307# Here, we compute the non-density components at first and append zero density308# components afterwards309zero_densities = SVector{ncomponents(equations), real(equations)}(ntuple(_ -> zero(real(equations)),310Val(ncomponents(equations))))311if orientation == 1312f = SVector(B1_ll * B1_rr,313B2_ll * B1_rr,314B3_ll * B1_rr,315v_dot_B_ll * B1_rr + v1_ll * psi_ll * psi_rr,316v1_ll * B1_rr,317v2_ll * B1_rr,318v3_ll * B1_rr,319v1_ll * psi_rr)320else # orientation == 2321f = SVector(B1_ll * B2_rr,322B2_ll * B2_rr,323B3_ll * B2_rr,324v_dot_B_ll * B2_rr + v2_ll * psi_ll * psi_rr,325v1_ll * B2_rr,326v2_ll * B2_rr,327v3_ll * B2_rr,328v2_ll * psi_rr)329end330331return vcat(f, zero_densities)332end333334"""335flux_derigs_etal(u_ll, u_rr, orientation, equations::IdealGlmMhdMulticomponentEquations2D)336337Entropy conserving two-point flux adapted by338- Derigs et al. (2018)339Ideal GLM-MHD: About the entropy consistent nine-wave magnetic field340divergence diminishing ideal magnetohydrodynamics equations for multicomponent341[DOI: 10.1016/j.jcp.2018.03.002](https://doi.org/10.1016/j.jcp.2018.03.002)342"""343function flux_derigs_etal(u_ll, u_rr, orientation::Integer,344equations::IdealGlmMhdMulticomponentEquations2D)345# Unpack left and right states to get velocities, pressure, and inverse temperature (called beta)346rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll347rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr348@unpack gammas, gas_constants, cv, c_h = equations349350rho_ll = density(u_ll, equations)351rho_rr = density(u_rr, equations)352353gamma_ll = totalgamma(u_ll, equations)354gamma_rr = totalgamma(u_rr, equations)355356rhok_mean = SVector{ncomponents(equations), real(equations)}(ln_mean(u_ll[i + 8],357u_rr[i + 8])358for i in eachcomponent(equations))359rhok_avg = SVector{ncomponents(equations), real(equations)}(0.5f0 * (u_ll[i + 8] +360u_rr[i + 8])361for i in eachcomponent(equations))362363v1_ll = rho_v1_ll / rho_ll364v2_ll = rho_v2_ll / rho_ll365v3_ll = rho_v3_ll / rho_ll366v1_rr = rho_v1_rr / rho_rr367v2_rr = rho_v2_rr / rho_rr368v3_rr = rho_v3_rr / rho_rr369v1_sq = 0.5f0 * (v1_ll^2 + v1_rr^2)370v2_sq = 0.5f0 * (v2_ll^2 + v2_rr^2)371v3_sq = 0.5f0 * (v3_ll^2 + v3_rr^2)372v_sq = v1_sq + v2_sq + v3_sq373B1_sq = 0.5f0 * (B1_ll^2 + B1_rr^2)374B2_sq = 0.5f0 * (B2_ll^2 + B2_rr^2)375B3_sq = 0.5f0 * (B3_ll^2 + B3_rr^2)376B_sq = B1_sq + B2_sq + B3_sq377vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^2378vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^2379mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2380mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2381# for convenience store v⋅B382vel_dot_mag_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll383vel_dot_mag_rr = v1_rr * B1_rr + v2_rr * B2_rr + v3_rr * B3_rr384385# Compute the necessary mean values needed for either direction386v1_avg = 0.5f0 * (v1_ll + v1_rr)387v2_avg = 0.5f0 * (v2_ll + v2_rr)388v3_avg = 0.5f0 * (v3_ll + v3_rr)389v_sum = v1_avg + v2_avg + v3_avg390B1_avg = 0.5f0 * (B1_ll + B1_rr)391B2_avg = 0.5f0 * (B2_ll + B2_rr)392B3_avg = 0.5f0 * (B3_ll + B3_rr)393psi_avg = 0.5f0 * (psi_ll + psi_rr)394vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)395mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)396vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)397398RealT = eltype(u_ll)399enth = zero(RealT)400help1_ll = zero(RealT)401help1_rr = zero(RealT)402403for i in eachcomponent(equations)404enth += rhok_avg[i] * gas_constants[i]405help1_ll += u_ll[i + 8] * cv[i]406help1_rr += u_rr[i + 8] * cv[i]407end408409T_ll = (rho_e_total_ll - 0.5f0 * rho_ll * (vel_norm_ll) - 0.5f0 * mag_norm_ll -4100.5f0 * psi_ll^2) / help1_ll411T_rr = (rho_e_total_rr - 0.5f0 * rho_rr * (vel_norm_rr) - 0.5f0 * mag_norm_rr -4120.5f0 * psi_rr^2) / help1_rr413T = 0.5f0 * (1 / T_ll + 1 / T_rr)414T_log = ln_mean(1 / T_ll, 1 / T_rr)415416# Calculate fluxes depending on orientation with specific direction averages417help1 = zero(RealT)418help2 = zero(RealT)419if orientation == 1420f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * v1_avg421for i in eachcomponent(equations))422for i in eachcomponent(equations)423help1 += f_rho[i] * cv[i]424help2 += f_rho[i]425end426f1 = help2 * v1_avg + enth / T + 0.5f0 * mag_norm_avg - B1_avg * B1_avg427f2 = help2 * v2_avg - B1_avg * B2_avg428f3 = help2 * v3_avg - B1_avg * B3_avg429f5 = c_h * psi_avg430f6 = v1_avg * B2_avg - v2_avg * B1_avg431f7 = v1_avg * B3_avg - v3_avg * B1_avg432f8 = c_h * B1_avg433# total energy flux is complicated and involves the previous eight components434psi_B1_avg = 0.5f0 * (B1_ll * psi_ll + B1_rr * psi_rr)435v1_mag_avg = 0.5f0 * (v1_ll * mag_norm_ll + v1_rr * mag_norm_rr)436437f4 = (help1 / T_log) - 0.5f0 * (vel_norm_avg) * (help2) + f1 * v1_avg +438f2 * v2_avg + f3 * v3_avg +439f5 * B1_avg + f6 * B2_avg + f7 * B3_avg + f8 * psi_avg -4400.5f0 * v1_mag_avg +441B1_avg * vel_dot_mag_avg - c_h * psi_B1_avg442443else444f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * v2_avg445for i in eachcomponent(equations))446for i in eachcomponent(equations)447help1 += f_rho[i] * cv[i]448help2 += f_rho[i]449end450f1 = help2 * v1_avg - B1_avg * B2_avg451f2 = help2 * v2_avg + enth / T + 0.5f0 * mag_norm_avg - B2_avg * B2_avg452f3 = help2 * v3_avg - B2_avg * B3_avg453f5 = v2_avg * B1_avg - v1_avg * B2_avg454f6 = c_h * psi_avg455f7 = v2_avg * B3_avg - v3_avg * B2_avg456f8 = c_h * B2_avg457458# total energy flux is complicated and involves the previous eight components459psi_B2_avg = 0.5f0 * (B2_ll * psi_ll + B2_rr * psi_rr)460v2_mag_avg = 0.5f0 * (v2_ll * mag_norm_ll + v2_rr * mag_norm_rr)461462f4 = (help1 / T_log) - 0.5f0 * (vel_norm_avg) * (help2) + f1 * v1_avg +463f2 * v2_avg + f3 * v3_avg +464f5 * B1_avg + f6 * B2_avg + f7 * B3_avg + f8 * psi_avg -4650.5f0 * v2_mag_avg +466B2_avg * vel_dot_mag_avg - c_h * psi_B2_avg467end468469f_other = SVector(f1, f2, f3, f4, f5, f6, f7, f8)470471return vcat(f_other, f_rho)472end473474"""475flux_hindenlang_gassner(u_ll, u_rr, orientation_or_normal_direction,476equations::IdealGlmMhdMulticomponentEquations2D)477478Adaption of the entropy conserving and kinetic energy preserving two-point flux of479Hindenlang (2019), extending [`flux_ranocha`](@ref) to the MHD equations.480## References481- Florian Hindenlang, Gregor Gassner (2019)482A new entropy conservative two-point flux for ideal MHD equations derived from483first principles.484Presented at HONOM 2019: European workshop on high order numerical methods485for evolutionary PDEs, theory and applications486- Hendrik Ranocha (2018)487Generalised Summation-by-Parts Operators and Entropy Stability of Numerical Methods488for Hyperbolic Balance Laws489[PhD thesis, TU Braunschweig](https://cuvillier.de/en/shop/publications/7743)490- Hendrik Ranocha (2020)491Entropy Conserving and Kinetic Energy Preserving Numerical Methods for492the Euler Equations Using Summation-by-Parts Operators493[Proceedings of ICOSAHOM 2018](https://doi.org/10.1007/978-3-030-39647-3_42)494"""495@inline function flux_hindenlang_gassner(u_ll, u_rr, orientation::Integer,496equations::IdealGlmMhdMulticomponentEquations2D)497# Unpack left and right states498v1_ll, v2_ll, v3_ll, p_ll, B1_ll, B2_ll, B3_ll, psi_ll = cons2prim(u_ll, equations)499v1_rr, v2_rr, v3_rr, p_rr, B1_rr, B2_rr, B3_rr, psi_rr = cons2prim(u_rr, equations)500501rho_ll = density(u_ll, equations)502rho_rr = density(u_rr, equations)503504# Compute the necessary mean values needed for either direction505# Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)`506# in exact arithmetic since507# log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ)508# = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ)509inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll)510v1_avg = 0.5f0 * (v1_ll + v1_rr)511v2_avg = 0.5f0 * (v2_ll + v2_rr)512v3_avg = 0.5f0 * (v3_ll + v3_rr)513p_avg = 0.5f0 * (p_ll + p_rr)514psi_avg = 0.5f0 * (psi_ll + psi_rr)515velocity_square_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr)516magnetic_square_avg = 0.5f0 * (B1_ll * B1_rr + B2_ll * B2_rr + B3_ll * B3_rr)517518inv_gamma_minus_one = 1 / (totalgamma(0.5f0 * (u_ll + u_rr), equations) - 1)519520rhok_mean = SVector{ncomponents(equations), real(equations)}(ln_mean(u_ll[i + 8],521u_rr[i + 8])522for i in eachcomponent(equations))523rhok_avg = SVector{ncomponents(equations), real(equations)}(0.5f0 * (u_ll[i + 8] +524u_rr[i + 8])525for i in eachcomponent(equations))526527RealT = eltype(u_ll)528if orientation == 1529f1 = zero(RealT)530f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * v1_avg531for i in eachcomponent(equations))532for i in eachcomponent(equations)533f1 += f_rho[i]534end535536# Calculate fluxes depending on orientation with specific direction averages537f2 = f1 * v1_avg + p_avg + magnetic_square_avg -5380.5f0 * (B1_ll * B1_rr + B1_rr * B1_ll)539f3 = f1 * v2_avg - 0.5f0 * (B1_ll * B2_rr + B1_rr * B2_ll)540f4 = f1 * v3_avg - 0.5f0 * (B1_ll * B3_rr + B1_rr * B3_ll)541# f5 below542f6 = f6 = equations.c_h * psi_avg543f7 = 0.5f0 * (v1_ll * B2_ll - v2_ll * B1_ll + v1_rr * B2_rr - v2_rr * B1_rr)544f8 = 0.5f0 * (v1_ll * B3_ll - v3_ll * B1_ll + v1_rr * B3_rr - v3_rr * B1_rr)545f9 = equations.c_h * 0.5f0 * (B1_ll + B1_rr)546# total energy flux is complicated and involves the previous components547f5 = (f1 * (velocity_square_avg + inv_rho_p_mean * inv_gamma_minus_one)548+5490.5f0 * (+p_ll * v1_rr + p_rr * v1_ll550+ (v1_ll * B2_ll * B2_rr + v1_rr * B2_rr * B2_ll)551+ (v1_ll * B3_ll * B3_rr + v1_rr * B3_rr * B3_ll)552-553(v2_ll * B1_ll * B2_rr + v2_rr * B1_rr * B2_ll)554-555(v3_ll * B1_ll * B3_rr + v3_rr * B1_rr * B3_ll)556+557equations.c_h * (B1_ll * psi_rr + B1_rr * psi_ll)))558else559f1 = zero(RealT)560f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * v2_avg561for i in eachcomponent(equations))562for i in eachcomponent(equations)563f1 += f_rho[i]564end565566# Calculate fluxes depending on orientation with specific direction averages567f2 = f1 * v1_avg - 0.5f0 * (B2_ll * B1_rr + B2_rr * B1_ll)568f3 = f1 * v2_avg + p_avg + magnetic_square_avg -5690.5f0 * (B2_ll * B2_rr + B2_rr * B2_ll)570f4 = f1 * v3_avg - 0.5f0 * (B2_ll * B3_rr + B2_rr * B3_ll)571#f5 below572f6 = 0.5f0 * (v2_ll * B1_ll - v1_ll * B2_ll + v2_rr * B1_rr - v1_rr * B2_rr)573f7 = equations.c_h * psi_avg574f8 = 0.5f0 * (v2_ll * B3_ll - v3_ll * B2_ll + v2_rr * B3_rr - v3_rr * B2_rr)575f9 = equations.c_h * 0.5f0 * (B2_ll + B2_rr)576# total energy flux is complicated and involves the previous components577f5 = (f1 * (velocity_square_avg + inv_rho_p_mean * inv_gamma_minus_one)578+5790.5f0 * (+p_ll * v2_rr + p_rr * v2_ll580+ (v2_ll * B1_ll * B1_rr + v2_rr * B1_rr * B1_ll)581+ (v2_ll * B3_ll * B3_rr + v2_rr * B3_rr * B3_ll)582-583(v1_ll * B2_ll * B1_rr + v1_rr * B2_rr * B1_ll)584-585(v3_ll * B2_ll * B3_rr + v3_rr * B2_rr * B3_ll)586+587equations.c_h * (B2_ll * psi_rr + B2_rr * psi_ll)))588end589590f_other = SVector(f2, f3, f4, f5, f6, f7, f8, f9)591592return vcat(f_other, f_rho)593end594595# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation596@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,597equations::IdealGlmMhdMulticomponentEquations2D)598rho_v1_ll, rho_v2_ll, _ = u_ll599rho_v1_rr, rho_v2_rr, _ = u_rr600601rho_ll = density(u_ll, equations)602rho_rr = density(u_rr, equations)603604# Calculate velocities and fast magnetoacoustic wave speeds605if orientation == 1606v_ll = rho_v1_ll / rho_ll607v_rr = rho_v1_rr / rho_rr608else # orientation == 2609v_ll = rho_v2_ll / rho_ll610v_rr = rho_v2_rr / rho_rr611end612cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)613cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)614615return max(abs(v_ll), abs(v_rr)) + max(cf_ll, cf_rr)616end617618# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`619@inline function max_abs_speed(u_ll, u_rr, orientation::Integer,620equations::IdealGlmMhdMulticomponentEquations2D)621rho_v1_ll, rho_v2_ll, _ = u_ll622rho_v1_rr, rho_v2_rr, _ = u_rr623624rho_ll = density(u_ll, equations)625rho_rr = density(u_rr, equations)626627# Calculate velocities and fast magnetoacoustic wave speeds628if orientation == 1629v_ll = rho_v1_ll / rho_ll630v_rr = rho_v1_rr / rho_rr631else # orientation == 2632v_ll = rho_v2_ll / rho_ll633v_rr = rho_v2_rr / rho_rr634end635cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)636cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)637638return max(abs(v_ll) + cf_ll, abs(v_rr) + cf_rr)639end640641@inline function max_abs_speeds(u, equations::IdealGlmMhdMulticomponentEquations2D)642rho_v1, rho_v2, _ = u643644rho = density(u, equations)645646v1 = rho_v1 / rho647v2 = rho_v2 / rho648649cf_x_direction = calc_fast_wavespeed(u, 1, equations)650cf_y_direction = calc_fast_wavespeed(u, 2, equations)651652return (abs(v1) + cf_x_direction, abs(v2) + cf_y_direction)653end654655@doc raw"""656pressure(u, equations::IdealGlmMhdMulticomponentEquations2D)657658Computes the pressure for an ideal equation of state with659isentropic exponent/adiabatic index ``\gamma`` from the conserved variables `u`.660```math661\begin{aligned}662p &= (\gamma - 1) \left( \rho e_{\text{total}} - \rho e_{\text{kinetic}} - \rho e_{\text{magnetic}} - \frac{1}{2}\psi^2 \right) \\663&= (\gamma - 1) \left( \rho e_{\text{total}} - \frac{1}{2}664\left[\rho \Vert v \Vert_2^2 + \Vert B \Vert_2^2 + \psi^2 \right] \right)665\end{aligned}666```667"""668@inline function pressure(u, equations::IdealGlmMhdMulticomponentEquations2D)669rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = u670rho = density(u, equations)671gamma = totalgamma(u, equations)672p = (gamma - 1) * (rho_e_total -6730.5f0 *674((rho_v1^2 + rho_v2^2 + rho_v3^2) / rho +675B1^2 + B2^2 + B3^2 + psi^2))676return p677end678679@inline function density_pressure(u, equations::IdealGlmMhdMulticomponentEquations2D)680rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = u681rho = density(u, equations)682gamma = totalgamma(u, equations)683rho_times_p = (gamma - 1) * (rho * rho_e_total -6840.5f0 * (rho_v1^2 + rho_v2^2 + rho_v3^2 +685rho * (B1^2 + B2^2 + B3^2 + psi^2)))686return rho_times_p687end688689# Convert conservative variables to primitive690function cons2prim(u, equations::IdealGlmMhdMulticomponentEquations2D)691rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = u692693prim_rho = SVector{ncomponents(equations), real(equations)}(u[i + 8]694for i in eachcomponent(equations))695rho = density(u, equations)696697v1 = rho_v1 / rho698v2 = rho_v2 / rho699v3 = rho_v3 / rho700701gamma = totalgamma(u, equations)702703p = (gamma - 1) *704(rho_e_total - 0.5f0 * rho * (v1^2 + v2^2 + v3^2) -7050.5f0 * (B1^2 + B2^2 + B3^2) -7060.5f0 * psi^2)707prim_other = SVector(v1, v2, v3, p, B1, B2, B3, psi)708709return vcat(prim_other, prim_rho)710end711712# Convert conservative variables to entropy713@inline function cons2entropy(u, equations::IdealGlmMhdMulticomponentEquations2D)714rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = u715@unpack cv, gammas, gas_constants = equations716717rho = density(u, equations)718719v1 = rho_v1 / rho720v2 = rho_v2 / rho721v3 = rho_v3 / rho722v_square = v1^2 + v2^2 + v3^2723gamma = totalgamma(u, equations)724p = (gamma - 1) *725(rho_e_total - 0.5f0 * rho * v_square - 0.5f0 * (B1^2 + B2^2 + B3^2) -7260.5f0 * psi^2)727s = log(p) - gamma * log(rho)728rho_p = rho / p729730# Multicomponent stuff731help1 = zero(v1)732733for i in eachcomponent(equations)734help1 += u[i + 8] * cv[i]735end736737T = (rho_e_total - 0.5f0 * rho * v_square - 0.5f0 * (B1^2 + B2^2 + B3^2) -7380.5f0 * psi^2) /739(help1)740741entrop_rho = SVector{ncomponents(equations), real(equations)}(-1 *742(cv[i] * log(T) -743gas_constants[i] *744log(u[i + 8])) +745gas_constants[i] +746cv[i] -747(v_square / (2 * T))748for i in eachcomponent(equations))749750w1 = v1 / T751w2 = v2 / T752w3 = v3 / T753w4 = -1 / T754w5 = B1 / T755w6 = B2 / T756w7 = B3 / T757w8 = psi / T758759entrop_other = SVector(w1, w2, w3, w4, w5, w6, w7, w8)760761return vcat(entrop_other, entrop_rho)762end763764# Convert primitive to conservative variables765@inline function prim2cons(prim, equations::IdealGlmMhdMulticomponentEquations2D)766v1, v2, v3, p, B1, B2, B3, psi = prim767768cons_rho = SVector{ncomponents(equations), real(equations)}(prim[i + 8]769for i in eachcomponent(equations))770rho = density(prim, equations)771772rho_v1 = rho * v1773rho_v2 = rho * v2774rho_v3 = rho * v3775776gamma = totalgamma(prim, equations)777rho_e_total = p / (gamma - 1) + 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3) +7780.5f0 * (B1^2 + B2^2 + B3^2) + 0.5f0 * psi^2779780cons_other = SVector(rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3,781psi)782783return vcat(cons_other, cons_rho)784end785786# Compute the fastest wave speed for ideal MHD equations: c_f, the fast magnetoacoustic eigenvalue787@inline function calc_fast_wavespeed(cons, direction,788equations::IdealGlmMhdMulticomponentEquations2D)789rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = cons790rho = density(cons, equations)791v1 = rho_v1 / rho792v2 = rho_v2 / rho793v3 = rho_v3 / rho794v_mag = sqrt(v1^2 + v2^2 + v3^2)795gamma = totalgamma(cons, equations)796p = (gamma - 1) *797(rho_e_total - 0.5f0 * rho * v_mag^2 - 0.5f0 * (B1^2 + B2^2 + B3^2) -7980.5f0 * psi^2)799a_square = gamma * p / rho800sqrt_rho = sqrt(rho)801b1 = B1 / sqrt_rho802b2 = B2 / sqrt_rho803b3 = B3 / sqrt_rho804b_square = b1^2 + b2^2 + b3^2805if direction == 1 # x-direction806c_f = sqrt(0.5f0 * (a_square + b_square) +8070.5f0 * sqrt((a_square + b_square)^2 - 4 * a_square * b1^2))808else809c_f = sqrt(0.5f0 * (a_square + b_square) +8100.5f0 * sqrt((a_square + b_square)^2 - 4 * a_square * b2^2))811end812return c_f813end814815@inline function density(u, equations::IdealGlmMhdMulticomponentEquations2D)816RealT = eltype(u)817rho = zero(RealT)818819for i in eachcomponent(equations)820rho += u[i + 8]821end822823return rho824end825826@inline function totalgamma(u, equations::IdealGlmMhdMulticomponentEquations2D)827@unpack cv, gammas = equations828829RealT = eltype(u)830help1 = zero(RealT)831help2 = zero(RealT)832833for i in eachcomponent(equations)834help1 += u[i + 8] * cv[i] * gammas[i]835help2 += u[i + 8] * cv[i]836end837838return help1 / help2839end840841@inline function densities(u, v, equations::IdealGlmMhdMulticomponentEquations2D)842return SVector{ncomponents(equations), real(equations)}(u[i + 8] * v843for i in eachcomponent(equations))844end845end # @muladd846847848