Path: blob/main/src/equations/ideal_glm_mhd_multiion_3d.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"""8IdealGlmMhdMultiIonEquations3D(; gammas, charge_to_mass,9electron_pressure = electron_pressure_zero,10initial_c_h = NaN)1112The ideal compressible multi-ion MHD equations in three space dimensions augmented with a13generalized Langange multipliers (GLM) divergence-cleaning technique. This is a14multi-species variant of the ideal GLM-MHD equations for calorically perfect plasmas15with independent momentum and energy equations for each ion species. This implementation16assumes that the equations are non-dimensionalized, such that the vacuum permeability is ``\mu_0 = 1``.1718In case of more than one ion species, the specific heat capacity ratios `gammas` and the charge-to-mass19ratios `charge_to_mass` should be passed as tuples, e.g., `gammas=(1.4, 1.667)`.2021The argument `electron_pressure` can be used to pass a function that computes the electron22pressure as a function of the state `u` with the signature `electron_pressure(u, equations)`.23By default, the electron pressure is zero.2425The argument `initial_c_h` can be used to set the GLM divergence-cleaning speed. Note that26`initial_c_h = 0` deactivates the divergence cleaning. The callback [`GlmSpeedCallback`](@ref)27can be used to adjust the GLM divergence-cleaning speed according to the time-step size.2829References:30- G. Toth, A. Glocer, Y. Ma, D. Najib, Multi-Ion Magnetohydrodynamics 429 (2010). Numerical31Modeling of Space Plasma Flows, 213–218.32- A. Rueda-Ramírez, A. Sikstel, G. Gassner, An Entropy-Stable Discontinuous Galerkin Discretization33of the Ideal Multi-Ion Magnetohydrodynamics System (2024). Journal of Computational Physics.34[DOI: 10.1016/j.jcp.2024.113655](https://doi.org/10.1016/j.jcp.2024.113655).3536!!! info "The multi-ion GLM-MHD equations require source terms"37In case of more than one ion species, the multi-ion GLM-MHD equations should ALWAYS be used38with [`source_terms_lorentz`](@ref).39"""40struct IdealGlmMhdMultiIonEquations3D{NVARS, NCOMP, RealT <: Real,41ElectronPressure} <:42AbstractIdealGlmMhdMultiIonEquations{3, NVARS, NCOMP}43gammas::SVector{NCOMP, RealT} # Heat capacity ratios44charge_to_mass::SVector{NCOMP, RealT} # Charge to mass ratios45electron_pressure::ElectronPressure # Function to compute the electron pressure46c_h::RealT # GLM cleaning speed47function IdealGlmMhdMultiIonEquations3D{NVARS, NCOMP, RealT,48ElectronPressure}(gammas49::SVector{NCOMP, RealT},50charge_to_mass51::SVector{NCOMP, RealT},52electron_pressure53::ElectronPressure,54c_h::RealT) where55{NVARS, NCOMP, RealT <: Real, ElectronPressure}56NCOMP >= 1 ||57throw(DimensionMismatch("`gammas` and `charge_to_mass` have to be filled with at least one value"))5859return new(gammas, charge_to_mass, electron_pressure, c_h)60end61end6263function IdealGlmMhdMultiIonEquations3D(; gammas, charge_to_mass,64electron_pressure = electron_pressure_zero,65initial_c_h = convert(eltype(gammas), NaN))66_gammas = promote(gammas...)67_charge_to_mass = promote(charge_to_mass...)68RealT = promote_type(eltype(_gammas), eltype(_charge_to_mass))69__gammas = SVector(map(RealT, _gammas))70__charge_to_mass = SVector(map(RealT, _charge_to_mass))7172NVARS = length(_gammas) * 5 + 473NCOMP = length(_gammas)7475return IdealGlmMhdMultiIonEquations3D{NVARS, NCOMP, RealT,76typeof(electron_pressure)}(__gammas,77__charge_to_mass,78electron_pressure,79initial_c_h)80end8182# Outer constructor for `@reset` works correctly83function IdealGlmMhdMultiIonEquations3D(gammas, charge_to_mass, electron_pressure, c_h)84return IdealGlmMhdMultiIonEquations3D(gammas = gammas,85charge_to_mass = charge_to_mass,86electron_pressure = electron_pressure,87initial_c_h = c_h)88end8990@inline function Base.real(::IdealGlmMhdMultiIonEquations3D{NVARS, NCOMP, RealT}) where {91NVARS,92NCOMP,93RealT94}95return RealT96end9798"""99initial_condition_weak_blast_wave(x, t, equations::IdealGlmMhdMultiIonEquations3D)100101A weak blast wave (adapted to multi-ion MHD) from102- Hennemann, S., Rueda-Ramírez, A. M., Hindenlang, F. J., & Gassner, G. J. (2021). A provably entropy103stable subcell shock capturing approach for high order split form DG for the compressible Euler equations.104Journal of Computational Physics, 426, 109935. [arXiv: 2008.12044](https://arxiv.org/abs/2008.12044).105[DOI: 10.1016/j.jcp.2020.109935](https://doi.org/10.1016/j.jcp.2020.109935)106"""107function initial_condition_weak_blast_wave(x, t,108equations::IdealGlmMhdMultiIonEquations3D)109# Adapted MHD version of the weak blast wave from Hennemann & Gassner JCP paper 2020 (Sec. 6.3)110# Same discontinuity in the velocities but with magnetic fields111RealT = eltype(x)112# Set up polar coordinates113inicenter = (0, 0, 0)114x_norm = x[1] - inicenter[1]115y_norm = x[2] - inicenter[2]116z_norm = x[3] - inicenter[3]117r = sqrt(x_norm^2 + y_norm^2 + z_norm^2)118phi = atan(y_norm, x_norm)119theta = iszero(r) ? zero(RealT) : acos(z_norm / r)120121# Calculate primitive variables122rho = r > 0.5f0 ? one(RealT) : convert(RealT, 1.1691)123v1 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos(phi) * sin(theta)124v2 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * sin(phi) * sin(theta)125v3 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos(theta)126p = r > 0.5f0 ? one(RealT) : convert(RealT, 1.245)127128prim = zero(MVector{nvariables(equations), real(equations)})129prim[1] = 1130prim[2] = 1131prim[3] = 1132133for k in eachcomponent(equations)134# We initialize each species with a fraction of the total density `rho`, such135# that the sum of the densities is `rho := density(prim, equations)`. The density of136# a species is double the density of the next species.137fraction = 2^(k - 1) * (1 - 2) / (1 - 2^ncomponents(equations))138set_component!(prim, k,139fraction * rho, v1,140v2, v3, p, equations)141end142143return prim2cons(SVector(prim), equations)144end145146# 3D flux of the multi-ion GLM-MHD equations in the direction `orientation`147@inline function flux(u, orientation::Integer,148equations::IdealGlmMhdMultiIonEquations3D)149B1, B2, B3 = magnetic_field(u, equations)150psi = divergence_cleaning_field(u, equations)151152v1_plus, v2_plus, v3_plus, vk1_plus, vk2_plus, vk3_plus = charge_averaged_velocities(u,153equations)154155mag_en = 0.5f0 * (B1^2 + B2^2 + B3^2)156div_clean_energy = 0.5f0 * psi^2157158f = zero(MVector{nvariables(equations), eltype(u)})159160if orientation == 1161f[1] = equations.c_h * psi162f[2] = v1_plus * B2 - v2_plus * B1163f[3] = v1_plus * B3 - v3_plus * B1164165for k in eachcomponent(equations)166rho, rho_v1, rho_v2, rho_v3, rho_e_total = get_component(k, u, equations)167rho_inv = 1 / rho168v1 = rho_v1 * rho_inv169v2 = rho_v2 * rho_inv170v3 = rho_v3 * rho_inv171kin_en = 0.5f0 * rho * (v1^2 + v2^2 + v3^2)172173gamma = equations.gammas[k]174p = (gamma - 1) * (rho_e_total - kin_en - mag_en - div_clean_energy)175176f1 = rho_v1177f2 = rho_v1 * v1 + p178f3 = rho_v1 * v2179f4 = rho_v1 * v3180f5 = (kin_en + gamma * p / (gamma - 1)) * v1 + 2 * mag_en * vk1_plus[k] -181B1 * (vk1_plus[k] * B1 + vk2_plus[k] * B2 + vk3_plus[k] * B3) +182equations.c_h * psi * B1183184set_component!(f, k, f1, f2, f3, f4, f5, equations)185end186f[end] = equations.c_h * B1187elseif orientation == 2188f[1] = v2_plus * B1 - v1_plus * B2189f[2] = equations.c_h * psi190f[3] = v2_plus * B3 - v3_plus * B2191192for k in eachcomponent(equations)193rho, rho_v1, rho_v2, rho_v3, rho_e_total = get_component(k, u, equations)194rho_inv = 1 / rho195v1 = rho_v1 * rho_inv196v2 = rho_v2 * rho_inv197v3 = rho_v3 * rho_inv198kin_en = 0.5f0 * rho * (v1^2 + v2^2 + v3^2)199200gamma = equations.gammas[k]201p = (gamma - 1) * (rho_e_total - kin_en - mag_en - div_clean_energy)202203f1 = rho_v2204f2 = rho_v2 * v1205f3 = rho_v2 * v2 + p206f4 = rho_v2 * v3207f5 = (kin_en + gamma * p / (gamma - 1)) * v2 + 2 * mag_en * vk2_plus[k] -208B2 * (vk1_plus[k] * B1 + vk2_plus[k] * B2 + vk3_plus[k] * B3) +209equations.c_h * psi * B2210211set_component!(f, k, f1, f2, f3, f4, f5, equations)212end213f[end] = equations.c_h * B2214else #if orientation == 3215f[1] = v3_plus * B1 - v1_plus * B3216f[2] = v3_plus * B2 - v2_plus * B3217f[3] = equations.c_h * psi218219for k in eachcomponent(equations)220rho, rho_v1, rho_v2, rho_v3, rho_e_total = get_component(k, u, equations)221rho_inv = 1 / rho222v1 = rho_v1 * rho_inv223v2 = rho_v2 * rho_inv224v3 = rho_v3 * rho_inv225kin_en = 0.5f0 * rho * (v1^2 + v2^2 + v3^2)226227gamma = equations.gammas[k]228p = (gamma - 1) * (rho_e_total - kin_en - mag_en - div_clean_energy)229230f1 = rho_v3231f2 = rho_v3 * v1232f3 = rho_v3 * v2233f4 = rho_v3 * v3 + p234f5 = (kin_en + gamma * p / (gamma - 1)) * v3 + 2 * mag_en * vk3_plus[k] -235B3 * (vk1_plus[k] * B1 + vk2_plus[k] * B2 + vk3_plus[k] * B3) +236equations.c_h * psi * B3237238set_component!(f, k, f1, f2, f3, f4, f5, equations)239end240f[end] = equations.c_h * B3241end242243return SVector(f)244end245246# Calculate 1D flux for a single point in the normal direction247# Note, this directional vector is not normalized248@inline function flux(u, normal_direction::AbstractVector,249equations::IdealGlmMhdMultiIonEquations3D)250B1, B2, B3 = magnetic_field(u, equations)251psi = divergence_cleaning_field(u, equations)252253v1_plus, v2_plus, v3_plus, vk1_plus, vk2_plus, vk3_plus = charge_averaged_velocities(u,254equations)255256mag_en = 0.5f0 * (B1^2 + B2^2 + B3^2)257div_clean_energy = 0.5f0 * psi^2258B_normal = B1 * normal_direction[1] + B2 * normal_direction[2] +259B3 * normal_direction[3]260261f = zero(MVector{nvariables(equations), eltype(u)})262263f[1] = (equations.c_h * psi * normal_direction[1] +264(v2_plus * B1 - v1_plus * B2) * normal_direction[2] +265(v3_plus * B1 - v1_plus * B3) * normal_direction[3])266f[2] = ((v1_plus * B2 - v2_plus * B1) * normal_direction[1] +267equations.c_h * psi * normal_direction[2] +268(v3_plus * B2 - v2_plus * B3) * normal_direction[3])269f[3] = ((v1_plus * B3 - v3_plus * B1) * normal_direction[1] +270(v2_plus * B3 - v3_plus * B2) * normal_direction[2] +271equations.c_h * psi * normal_direction[3])272273for k in eachcomponent(equations)274rho, rho_v1, rho_v2, rho_v3, rho_e_total = get_component(k, u, equations)275rho_inv = 1 / rho276v1 = rho_v1 * rho_inv277v2 = rho_v2 * rho_inv278v3 = rho_v3 * rho_inv279kin_en = 0.5f0 * rho * (v1^2 + v2^2 + v3^2)280v_normal = v1 * normal_direction[1] + v2 * normal_direction[2] +281v3 * normal_direction[3]282rho_v_normal = rho * v_normal283vk_plus_normal = vk1_plus[k] * normal_direction[1] +284vk2_plus[k] * normal_direction[2] +285vk3_plus[k] * normal_direction[3]286287gamma = equations.gammas[k]288p = (gamma - 1) * (rho_e_total - kin_en - mag_en - div_clean_energy)289290f1 = rho_v_normal291f2 = rho_v_normal * v1 + p * normal_direction[1]292f3 = rho_v_normal * v2 + p * normal_direction[2]293f4 = rho_v_normal * v3 + p * normal_direction[3]294f5 = (kin_en + gamma * p / (gamma - 1)) * v_normal +2952 * mag_en * vk_plus_normal -296B_normal * (vk1_plus[k] * B1 + vk2_plus[k] * B2 + vk3_plus[k] * B3) +297equations.c_h * psi * B_normal298299set_component!(f, k, f1, f2, f3, f4, f5, equations)300end301f[end] = equations.c_h * B_normal302303return SVector(f)304end305306"""307flux_nonconservative_ruedaramirez_etal(u_ll, u_rr,308orientation_or_normal_direction,309equations::IdealGlmMhdMultiIonEquations3D)310311Entropy-conserving non-conservative two-point "flux" as described in312- A. Rueda-Ramírez, A. Sikstel, G. Gassner, An Entropy-Stable Discontinuous Galerkin Discretization313of the Ideal Multi-Ion Magnetohydrodynamics System (2024). Journal of Computational Physics.314[DOI: 10.1016/j.jcp.2024.113655](https://doi.org/10.1016/j.jcp.2024.113655).315316!!! info "Usage and Scaling of Non-Conservative Fluxes in Trixi.jl"317The non-conservative fluxes derived in the reference above are written as the product318of local and symmetric parts and are meant to be used in the same way as the conservative319fluxes (i.e., flux + flux_noncons in both volume and surface integrals). In this routine,320the fluxes are multiplied by 2 because the non-conservative fluxes are always multiplied321by 0.5 whenever they are used in the Trixi.jl code.322323The term is composed of four individual non-conservative terms:3241. The Godunov-Powell term, which arises for plasmas with non-vanishing magnetic field divergence, and325is evaluated as a function of the charge-averaged velocity.3262. The Lorentz-force term, which becomes a conservative term in the limit of one ion species for vanishing327electron pressure gradients.3283. The "multi-ion" term, which vanishes in the limit of one ion species.3294. The GLM term, which is needed for Galilean invariance.330"""331@inline function flux_nonconservative_ruedaramirez_etal(u_ll, u_rr,332orientation::Integer,333equations::IdealGlmMhdMultiIonEquations3D)334@unpack charge_to_mass = equations335# Unpack left and right states to get the magnetic field336B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)337B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)338psi_ll = divergence_cleaning_field(u_ll, equations)339psi_rr = divergence_cleaning_field(u_rr, equations)340341# Compute important averages342B1_avg = 0.5f0 * (B1_ll + B1_rr)343B2_avg = 0.5f0 * (B2_ll + B2_rr)344B3_avg = 0.5f0 * (B3_ll + B3_rr)345mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2346mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2347mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)348psi_avg = 0.5f0 * (psi_ll + psi_rr)349350# Mean electron pressure351pe_ll = equations.electron_pressure(u_ll, equations)352pe_rr = equations.electron_pressure(u_rr, equations)353pe_mean = 0.5f0 * (pe_ll + pe_rr)354355# Compute charge ratio of u_ll356charge_ratio_ll = zero(MVector{ncomponents(equations), eltype(u_ll)})357total_electron_charge = zero(eltype(u_ll))358for k in eachcomponent(equations)359rho_k = u_ll[3 + (k - 1) * 5 + 1]360charge_ratio_ll[k] = rho_k * charge_to_mass[k]361total_electron_charge += charge_ratio_ll[k]362end363charge_ratio_ll ./= total_electron_charge364365# Compute auxiliary variables366v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll, vk3_plus_ll = charge_averaged_velocities(u_ll,367equations)368v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr, vk3_plus_rr = charge_averaged_velocities(u_rr,369equations)370371f = zero(MVector{nvariables(equations), eltype(u_ll)})372373if orientation == 1374# Entries of Godunov-Powell term for induction equation (multiply by 2 because the non-conservative flux is375# multiplied by 0.5 whenever it's used in the Trixi.jl code)376f[1] = 2 * v1_plus_ll * B1_avg377f[2] = 2 * v2_plus_ll * B1_avg378f[3] = 2 * v3_plus_ll * B1_avg379380for k in eachcomponent(equations)381# Compute term Lorentz term382f2 = charge_ratio_ll[k] * (0.5f0 * mag_norm_avg - B1_avg * B1_avg + pe_mean)383f3 = charge_ratio_ll[k] * (-B1_avg * B2_avg)384f4 = charge_ratio_ll[k] * (-B1_avg * B3_avg)385f5 = vk1_plus_ll[k] * pe_mean386387# Compute multi-ion term (vanishes for NCOMP==1)388vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]389vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]390vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]391vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]392vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]393vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]394vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)395vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)396vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)397f5 += (B2_ll * (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) +398B3_ll * (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg))399400# Compute Godunov-Powell term401f2 += charge_ratio_ll[k] * B1_ll * B1_avg402f3 += charge_ratio_ll[k] * B2_ll * B1_avg403f4 += charge_ratio_ll[k] * B3_ll * B1_avg404f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *405B1_avg406407# Compute GLM term for the energy408f5 += v1_plus_ll * psi_ll * psi_avg409410# Add to the flux vector (multiply by 2 because the non-conservative flux is411# multiplied by 0.5 whenever it's used in the Trixi code)412set_component!(f, k, 0, 2 * f2, 2 * f3, 2 * f4, 2 * f5,413equations)414end415# Compute GLM term for psi (multiply by 2 because the non-conservative flux is416# multiplied by 0.5 whenever it's used in the Trixi.jl code)417f[end] = 2 * v1_plus_ll * psi_avg418419elseif orientation == 2420# Entries of Godunov-Powell term for induction equation (multiply by 2 because the non-conservative flux is421# multiplied by 0.5 whenever it's used in the Trixi.jl code)422f[1] = 2 * v1_plus_ll * B2_avg423f[2] = 2 * v2_plus_ll * B2_avg424f[3] = 2 * v3_plus_ll * B2_avg425426for k in eachcomponent(equations)427# Compute term Lorentz term428f2 = charge_ratio_ll[k] * (-B2_avg * B1_avg)429f3 = charge_ratio_ll[k] *430(-B2_avg * B2_avg + 0.5f0 * mag_norm_avg + pe_mean)431f4 = charge_ratio_ll[k] * (-B2_avg * B3_avg)432f5 = vk2_plus_ll[k] * pe_mean433434# Compute multi-ion term (vanishes for NCOMP==1)435vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]436vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]437vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]438vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]439vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]440vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]441vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)442vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)443vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)444f5 += (B1_ll * (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) +445B3_ll * (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg))446447# Compute Godunov-Powell term448f2 += charge_ratio_ll[k] * B1_ll * B2_avg449f3 += charge_ratio_ll[k] * B2_ll * B2_avg450f4 += charge_ratio_ll[k] * B3_ll * B2_avg451f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *452B2_avg453454# Compute GLM term for the energy455f5 += v2_plus_ll * psi_ll * psi_avg456457# Add to the flux vector (multiply by 2 because the non-conservative flux is458# multiplied by 0.5 whenever it's used in the Trixi.jl code)459set_component!(f, k, 0, 2 * f2, 2 * f3, 2 * f4, 2 * f5,460equations)461end462# Compute GLM term for psi (multiply by 2 because the non-conservative flux is463# multiplied by 0.5 whenever it's used in the Trixi.jl code)464f[end] = 2 * v2_plus_ll * psi_avg465else #if orientation == 3466# Entries of Godunov-Powell term for induction equation (multiply by 2 because the non-conservative flux is467# multiplied by 0.5 whenever it's used in the Trixi.jl code)468f[1] = 2 * v1_plus_ll * B3_avg469f[2] = 2 * v2_plus_ll * B3_avg470f[3] = 2 * v3_plus_ll * B3_avg471472for k in eachcomponent(equations)473# Compute term Lorentz term474f2 = charge_ratio_ll[k] * (-B3_avg * B1_avg)475f3 = charge_ratio_ll[k] * (-B3_avg * B2_avg)476f4 = charge_ratio_ll[k] *477(-B3_avg * B3_avg + 0.5f0 * mag_norm_avg + pe_mean)478f5 = vk3_plus_ll[k] * pe_mean479480# Compute multi-ion term (vanishes for NCOMP==1)481vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]482vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]483vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]484vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]485vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]486vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]487vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)488vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)489vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)490f5 += (B1_ll * (vk3_minus_avg * B1_avg - vk1_minus_avg * B3_avg) +491B2_ll * (vk3_minus_avg * B2_avg - vk2_minus_avg * B3_avg))492493# Compute Godunov-Powell term494f2 += charge_ratio_ll[k] * B1_ll * B3_avg495f3 += charge_ratio_ll[k] * B2_ll * B3_avg496f4 += charge_ratio_ll[k] * B3_ll * B3_avg497f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *498B3_avg499500# Compute GLM term for the energy501f5 += v3_plus_ll * psi_ll * psi_avg502503# Add to the flux vector (multiply by 2 because the non-conservative flux is504# multiplied by 0.5 whenever it's used in the Trixi.jl code)505set_component!(f, k, 0, 2 * f2, 2 * f3, 2 * f4, 2 * f5,506equations)507end508# Compute GLM term for psi (multiply by 2 because the non-conservative flux is509# multiplied by 0.5 whenever it's used in the Trixi.jl code)510f[end] = 2 * v3_plus_ll * psi_avg511end512513return SVector(f)514end515516@inline function flux_nonconservative_ruedaramirez_etal(u_ll, u_rr,517normal_direction::AbstractVector,518equations::IdealGlmMhdMultiIonEquations3D)519@unpack charge_to_mass = equations520# Unpack left and right states to get the magnetic field521B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)522B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)523psi_ll = divergence_cleaning_field(u_ll, equations)524psi_rr = divergence_cleaning_field(u_rr, equations)525B_dot_n_ll = B1_ll * normal_direction[1] +526B2_ll * normal_direction[2] +527B3_ll * normal_direction[3]528B_dot_n_rr = B1_rr * normal_direction[1] +529B2_rr * normal_direction[2] +530B3_rr * normal_direction[3]531B_dot_n_avg = 0.5f0 * (B_dot_n_ll + B_dot_n_rr)532533# Compute important averages534B1_avg = 0.5f0 * (B1_ll + B1_rr)535B2_avg = 0.5f0 * (B2_ll + B2_rr)536B3_avg = 0.5f0 * (B3_ll + B3_rr)537mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2538mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2539mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)540psi_avg = 0.5f0 * (psi_ll + psi_rr)541542# Mean electron pressure543pe_ll = equations.electron_pressure(u_ll, equations)544pe_rr = equations.electron_pressure(u_rr, equations)545pe_mean = 0.5f0 * (pe_ll + pe_rr)546547# Compute charge ratio of u_ll548charge_ratio_ll = zero(MVector{ncomponents(equations), eltype(u_ll)})549total_electron_charge = zero(eltype(u_ll))550for k in eachcomponent(equations)551rho_k = u_ll[3 + (k - 1) * 5 + 1] # Extract densities from conserved variable vector552charge_ratio_ll[k] = rho_k * charge_to_mass[k]553total_electron_charge += charge_ratio_ll[k]554end555charge_ratio_ll ./= total_electron_charge556557# Compute auxiliary variables558v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll, vk3_plus_ll = charge_averaged_velocities(u_ll,559equations)560v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr, vk3_plus_rr = charge_averaged_velocities(u_rr,561equations)562v_plus_dot_n_ll = (v1_plus_ll * normal_direction[1] +563v2_plus_ll * normal_direction[2] +564v3_plus_ll * normal_direction[3])565f = zero(MVector{nvariables(equations), eltype(u_ll)})566567# Entries of Godunov-Powell term for induction equation (multiply by 2 because the non-conservative flux is568# multiplied by 0.5 whenever it's used in the Trixi code)569f[1] = 2 * v1_plus_ll * B_dot_n_avg570f[2] = 2 * v2_plus_ll * B_dot_n_avg571f[3] = 2 * v3_plus_ll * B_dot_n_avg572573for k in eachcomponent(equations)574# Compute term Lorentz term575f2 = charge_ratio_ll[k] *576((0.5f0 * mag_norm_avg + pe_mean) * normal_direction[1] -577B_dot_n_avg * B1_avg)578f3 = charge_ratio_ll[k] *579((0.5f0 * mag_norm_avg + pe_mean) * normal_direction[2] -580B_dot_n_avg * B2_avg)581f4 = charge_ratio_ll[k] *582((0.5f0 * mag_norm_avg + pe_mean) * normal_direction[3] -583B_dot_n_avg * B3_avg)584f5 = (vk1_plus_ll[k] * normal_direction[1] +585vk2_plus_ll[k] * normal_direction[2] +586vk3_plus_ll[k] * normal_direction[3]) * pe_mean587588# Compute multi-ion term (vanishes for NCOMP==1)589vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]590vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]591vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]592vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]593vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]594vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]595vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)596vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)597vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)598f5 += ((B2_ll * (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) +599B3_ll * (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg)) *600normal_direction[1] +601(B1_ll * (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) +602B3_ll * (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg)) *603normal_direction[2] +604(B1_ll * (vk3_minus_avg * B1_avg - vk1_minus_avg * B3_avg) +605B2_ll * (vk3_minus_avg * B2_avg - vk2_minus_avg * B3_avg)) *606normal_direction[3])607608# Compute Godunov-Powell term609f2 += charge_ratio_ll[k] * B1_ll * B_dot_n_avg610f3 += charge_ratio_ll[k] * B2_ll * B_dot_n_avg611f4 += charge_ratio_ll[k] * B3_ll * B_dot_n_avg612f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *613B_dot_n_avg614615# Compute GLM term for the energy616f5 += v_plus_dot_n_ll * psi_ll * psi_avg617618# Add to the flux vector (multiply by 2 because the non-conservative flux is619# multiplied by 0.5 whenever it's used in the Trixi code)620set_component!(f, k, 0, 2 * f2, 2 * f3, 2 * f4, 2 * f5,621equations)622end623# Compute GLM term for psi (multiply by 2 because the non-conservative flux is624# multiplied by 0.5 whenever it's used in the Trixi code)625f[end] = 2 * v_plus_dot_n_ll * psi_avg626627return SVector(f)628end629630"""631flux_nonconservative_central(u_ll, u_rr, orientation::Integer,632equations::IdealGlmMhdMultiIonEquations3D)633634Central non-conservative two-point "flux", where the symmetric parts are computed with standard averages.635The use of this term together with [`flux_central`](@ref)636with [`VolumeIntegralFluxDifferencing`](@ref) yields a "standard"637(weak-form) DGSEM discretization of the multi-ion GLM-MHD system. This flux can also be used to construct a638standard local Lax-Friedrichs flux using `surface_flux = (flux_lax_friedrichs, flux_nonconservative_central)`.639640!!! info "Usage and Scaling of Non-Conservative Fluxes in Trixi.jl"641The central non-conservative fluxes implemented in this function are written as the product642of local and symmetric parts, where the symmetric part is a standard average. These fluxes643are meant to be used in the same way as the conservative fluxes (i.e., flux + flux_noncons644in both volume and surface integrals). In this routine, the fluxes are multiplied by 2 because645the non-conservative fluxes are always multiplied by 0.5 whenever they are used in the Trixi.jl code.646647The term is composed of four individual non-conservative terms:6481. The Godunov-Powell term, which arises for plasmas with non-vanishing magnetic field divergence, and649is evaluated as a function of the charge-averaged velocity.6502. The Lorentz-force term, which becomes a conservative term in the limit of one ion species for vanishing651electron pressure gradients.6523. The "multi-ion" term, which vanishes in the limit of one ion species.6534. The GLM term, which is needed for Galilean invariance.654"""655@inline function flux_nonconservative_central(u_ll, u_rr, orientation::Integer,656equations::IdealGlmMhdMultiIonEquations3D)657@unpack charge_to_mass = equations658# Unpack left and right states to get the magnetic field659B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)660B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)661psi_ll = divergence_cleaning_field(u_ll, equations)662psi_rr = divergence_cleaning_field(u_rr, equations)663664# Compute important averages665mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2666mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2667668# Electron pressure669pe_ll = equations.electron_pressure(u_ll, equations)670pe_rr = equations.electron_pressure(u_rr, equations)671672# Compute charge ratio of u_ll673charge_ratio_ll = zero(MVector{ncomponents(equations), eltype(u_ll)})674total_electron_charge = zero(real(equations))675for k in eachcomponent(equations)676rho_k = u_ll[3 + (k - 1) * 5 + 1]677charge_ratio_ll[k] = rho_k * charge_to_mass[k]678total_electron_charge += charge_ratio_ll[k]679end680charge_ratio_ll ./= total_electron_charge681682# Compute auxiliary variables683v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll, vk3_plus_ll = charge_averaged_velocities(u_ll,684equations)685v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr, vk3_plus_rr = charge_averaged_velocities(u_rr,686equations)687688f = zero(MVector{nvariables(equations), eltype(u_ll)})689690if orientation == 1691# Entries of Godunov-Powell term for induction equation692f[1] = v1_plus_ll * (B1_ll + B1_rr)693f[2] = v2_plus_ll * (B1_ll + B1_rr)694f[3] = v3_plus_ll * (B1_ll + B1_rr)695for k in eachcomponent(equations)696# Compute Lorentz term697f2 = charge_ratio_ll[k] * ((0.5f0 * mag_norm_ll - B1_ll * B1_ll + pe_ll) +698(0.5f0 * mag_norm_rr - B1_rr * B1_rr + pe_rr))699f3 = charge_ratio_ll[k] * ((-B1_ll * B2_ll) + (-B1_rr * B2_rr))700f4 = charge_ratio_ll[k] * ((-B1_ll * B3_ll) + (-B1_rr * B3_rr))701f5 = vk1_plus_ll[k] * (pe_ll + pe_rr)702703# Compute multi-ion term, which vanishes for NCOMP==1704vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]705vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]706vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]707vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]708vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]709vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]710f5 += (B2_ll * ((vk1_minus_ll * B2_ll - vk2_minus_ll * B1_ll) +711(vk1_minus_rr * B2_rr - vk2_minus_rr * B1_rr)) +712B3_ll * ((vk1_minus_ll * B3_ll - vk3_minus_ll * B1_ll) +713(vk1_minus_rr * B3_rr - vk3_minus_rr * B1_rr)))714715# Compute Godunov-Powell term716f2 += charge_ratio_ll[k] * B1_ll * (B1_ll + B1_rr)717f3 += charge_ratio_ll[k] * B2_ll * (B1_ll + B1_rr)718f4 += charge_ratio_ll[k] * B3_ll * (B1_ll + B1_rr)719f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *720(B1_ll + B1_rr)721722# Compute GLM term for the energy723f5 += v1_plus_ll * psi_ll * (psi_ll + psi_rr)724725# Append to the flux vector726set_component!(f, k, 0, f2, f3, f4, f5, equations)727end728# Compute GLM term for psi729f[end] = v1_plus_ll * (psi_ll + psi_rr)730731elseif orientation == 2732# Entries of Godunov-Powell term for induction equation733f[1] = v1_plus_ll * (B2_ll + B2_rr)734f[2] = v2_plus_ll * (B2_ll + B2_rr)735f[3] = v3_plus_ll * (B2_ll + B2_rr)736737for k in eachcomponent(equations)738# Compute Lorentz term739f2 = charge_ratio_ll[k] * ((-B2_ll * B1_ll) + (-B2_rr * B1_rr))740f3 = charge_ratio_ll[k] * ((-B2_ll * B2_ll + 0.5f0 * mag_norm_ll + pe_ll) +741(-B2_rr * B2_rr + 0.5f0 * mag_norm_rr + pe_rr))742f4 = charge_ratio_ll[k] * ((-B2_ll * B3_ll) + (-B2_rr * B3_rr))743f5 = vk2_plus_ll[k] * (pe_ll + pe_rr)744745# Compute multi-ion term (vanishes for NCOMP==1)746vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]747vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]748vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]749vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]750vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]751vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]752f5 += (B1_ll * ((vk2_minus_ll * B1_ll - vk1_minus_ll * B2_ll) +753(vk2_minus_rr * B1_rr - vk1_minus_rr * B2_rr)) +754B3_ll * ((vk2_minus_ll * B3_ll - vk3_minus_ll * B2_ll) +755(vk2_minus_rr * B3_rr - vk3_minus_rr * B2_rr)))756757# Compute Godunov-Powell term758f2 += charge_ratio_ll[k] * B1_ll * (B2_ll + B2_rr)759f3 += charge_ratio_ll[k] * B2_ll * (B2_ll + B2_rr)760f4 += charge_ratio_ll[k] * B3_ll * (B2_ll + B2_rr)761f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *762(B2_ll + B2_rr)763764# Compute GLM term for the energy765f5 += v2_plus_ll * psi_ll * (psi_ll + psi_rr)766767# Append to the flux vector768set_component!(f, k, 0, f2, f3, f4, f5, equations)769end770# Compute GLM term for psi771f[end] = v2_plus_ll * (psi_ll + psi_rr)772else #if orientation == 3773# Entries of Godunov-Powell term for induction equation774f[1] = v1_plus_ll * (B3_ll + B3_rr)775f[2] = v2_plus_ll * (B3_ll + B3_rr)776f[3] = v3_plus_ll * (B3_ll + B3_rr)777778for k in eachcomponent(equations)779# Compute Lorentz term780f2 = charge_ratio_ll[k] * ((-B3_ll * B1_ll) + (-B3_rr * B1_rr))781f3 = charge_ratio_ll[k] * ((-B3_ll * B2_ll) + (-B3_rr * B2_rr))782f4 = charge_ratio_ll[k] * ((-B3_ll * B3_ll + 0.5f0 * mag_norm_ll + pe_ll) +783(-B3_rr * B3_rr + 0.5f0 * mag_norm_rr + pe_rr))784f5 = vk3_plus_ll[k] * (pe_ll + pe_rr)785786# Compute multi-ion term (vanishes for NCOMP==1)787vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]788vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]789vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]790vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]791vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]792vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]793f5 += (B1_ll * ((vk3_minus_ll * B1_ll - vk1_minus_ll * B3_ll) +794(vk3_minus_rr * B1_rr - vk1_minus_rr * B3_rr)) +795B2_ll * ((vk3_minus_ll * B2_ll - vk2_minus_ll * B3_ll) +796(vk3_minus_rr * B2_rr - vk2_minus_rr * B3_rr)))797798# Compute Godunov-Powell term799f2 += charge_ratio_ll[k] * B1_ll * (B3_ll + B3_rr)800f3 += charge_ratio_ll[k] * B2_ll * (B3_ll + B3_rr)801f4 += charge_ratio_ll[k] * B3_ll * (B3_ll + B3_rr)802f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *803(B3_ll + B3_rr)804805# Compute GLM term for the energy806f5 += v3_plus_ll * psi_ll * (psi_ll + psi_rr)807808# Append to the flux vector809set_component!(f, k, 0, f2, f3, f4, f5, equations)810end811# Compute GLM term for psi812f[end] = v3_plus_ll * (psi_ll + psi_rr)813end814815return SVector(f)816end817818"""819flux_ruedaramirez_etal(u_ll, u_rr, orientation, equations::IdealGlmMhdMultiIonEquations3D)820821Entropy conserving two-point flux for the multi-ion GLM-MHD equations from822- A. Rueda-Ramírez, A. Sikstel, G. Gassner, An Entropy-Stable Discontinuous Galerkin Discretization823of the Ideal Multi-Ion Magnetohydrodynamics System (2024). Journal of Computational Physics.824[DOI: 10.1016/j.jcp.2024.113655](https://doi.org/10.1016/j.jcp.2024.113655).825826This flux (together with the MHD non-conservative term) is consistent in the case of one ion species with the flux of:827- Derigs et al. (2018). Ideal GLM-MHD: About the entropy consistent nine-wave magnetic field828divergence diminishing ideal magnetohydrodynamics equations for multi-ion829[DOI: 10.1016/j.jcp.2018.03.002](https://doi.org/10.1016/j.jcp.2018.03.002)830"""831function flux_ruedaramirez_etal(u_ll, u_rr, orientation::Integer,832equations::IdealGlmMhdMultiIonEquations3D)833@unpack gammas = equations834# Unpack left and right states to get the magnetic field835B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)836B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)837psi_ll = divergence_cleaning_field(u_ll, equations)838psi_rr = divergence_cleaning_field(u_rr, equations)839840v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll, vk3_plus_ll = charge_averaged_velocities(u_ll,841equations)842v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr, vk3_plus_rr = charge_averaged_velocities(u_rr,843equations)844845f = zero(MVector{nvariables(equations), eltype(u_ll)})846847# Compute averages for global variables848v1_plus_avg = 0.5f0 * (v1_plus_ll + v1_plus_rr)849v2_plus_avg = 0.5f0 * (v2_plus_ll + v2_plus_rr)850v3_plus_avg = 0.5f0 * (v3_plus_ll + v3_plus_rr)851B1_avg = 0.5f0 * (B1_ll + B1_rr)852B2_avg = 0.5f0 * (B2_ll + B2_rr)853B3_avg = 0.5f0 * (B3_ll + B3_rr)854mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2855mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2856mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)857psi_avg = 0.5f0 * (psi_ll + psi_rr)858859if orientation == 1860psi_B1_avg = 0.5f0 * (B1_ll * psi_ll + B1_rr * psi_rr)861862# Magnetic field components from f^MHD863f6 = equations.c_h * psi_avg864f7 = v1_plus_avg * B2_avg - v2_plus_avg * B1_avg865f8 = v1_plus_avg * B3_avg - v3_plus_avg * B1_avg866f9 = equations.c_h * B1_avg867868# Start building the flux869f[1] = f6870f[2] = f7871f[3] = f8872f[end] = f9873874# Iterate over all components875for k in eachcomponent(equations)876# Unpack left and right states877rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll = get_component(k,878u_ll,879equations)880rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr = get_component(k,881u_rr,882equations)883884rho_inv_ll = 1 / rho_ll885v1_ll = rho_v1_ll * rho_inv_ll886v2_ll = rho_v2_ll * rho_inv_ll887v3_ll = rho_v3_ll * rho_inv_ll888rho_inv_rr = 1 / rho_rr889v1_rr = rho_v1_rr * rho_inv_rr890v2_rr = rho_v2_rr * rho_inv_rr891v3_rr = rho_v3_rr * rho_inv_rr892vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^2893vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^2894895p_ll = (gammas[k] - 1) *896(rho_e_total_ll - 0.5f0 * rho_ll * vel_norm_ll -8970.5f0 * mag_norm_ll -8980.5f0 * psi_ll^2)899p_rr = (gammas[k] - 1) *900(rho_e_total_rr - 0.5f0 * rho_rr * vel_norm_rr -9010.5f0 * mag_norm_rr -9020.5f0 * psi_rr^2)903beta_ll = 0.5f0 * rho_ll / p_ll904beta_rr = 0.5f0 * rho_rr / p_rr905# for convenience store vk_plus⋅B906vel_dot_mag_ll = vk1_plus_ll[k] * B1_ll + vk2_plus_ll[k] * B2_ll +907vk3_plus_ll[k] * B3_ll908vel_dot_mag_rr = vk1_plus_rr[k] * B1_rr + vk2_plus_rr[k] * B2_rr +909vk3_plus_rr[k] * B3_rr910911# Compute the necessary mean values needed for either direction912rho_avg = 0.5f0 * (rho_ll + rho_rr)913rho_mean = ln_mean(rho_ll, rho_rr)914beta_mean = ln_mean(beta_ll, beta_rr)915beta_avg = 0.5f0 * (beta_ll + beta_rr)916p_mean = 0.5f0 * rho_avg / beta_avg917v1_avg = 0.5f0 * (v1_ll + v1_rr)918v2_avg = 0.5f0 * (v2_ll + v2_rr)919v3_avg = 0.5f0 * (v3_ll + v3_rr)920vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)921vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)922vk1_plus_avg = 0.5f0 * (vk1_plus_ll[k] + vk1_plus_rr[k])923vk2_plus_avg = 0.5f0 * (vk2_plus_ll[k] + vk2_plus_rr[k])924vk3_plus_avg = 0.5f0 * (vk3_plus_ll[k] + vk3_plus_rr[k])925# v_minus926vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]927vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]928vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]929vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]930vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]931vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]932vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)933vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)934vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)935936# Fill the fluxes for the mass and momentum equations937f1 = rho_mean * v1_avg938f2 = f1 * v1_avg + p_mean939f3 = f1 * v2_avg940f4 = f1 * v3_avg941942# total energy flux is complicated and involves the previous eight components943v1_plus_mag_avg = 0.5f0 * (vk1_plus_ll[k] * mag_norm_ll +944vk1_plus_rr[k] * mag_norm_rr)945# Euler part946f5 = f1 * 0.5f0 * (1 / (gammas[k] - 1) / beta_mean - vel_norm_avg) +947f2 * v1_avg + f3 * v2_avg + f4 * v3_avg948# MHD part949f5 += (f6 * B1_avg + f7 * B2_avg + f8 * B3_avg - 0.5f0 * v1_plus_mag_avg +950B1_avg * vel_dot_mag_avg # Same terms as in Derigs (but with v_plus)951+ f9 * psi_avg - equations.c_h * psi_B1_avg # GLM term952+9530.5f0 * vk1_plus_avg * mag_norm_avg -954vk1_plus_avg * B1_avg * B1_avg - vk2_plus_avg * B1_avg * B2_avg -955vk3_plus_avg * B1_avg * B3_avg # Additional terms related to the Lorentz non-conservative term (momentum eqs)956-957B2_avg * (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) -958B3_avg * (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg)) # Terms related to the multi-ion non-conservative term (induction equation!)959960set_component!(f, k, f1, f2, f3, f4, f5, equations)961end962elseif orientation == 2963psi_B2_avg = 0.5f0 * (B2_ll * psi_ll + B2_rr * psi_rr)964965# Magnetic field components from f^MHD966f6 = v2_plus_avg * B1_avg - v1_plus_avg * B2_avg967f7 = equations.c_h * psi_avg968f8 = v2_plus_avg * B3_avg - v3_plus_avg * B2_avg969f9 = equations.c_h * B2_avg970971# Start building the flux972f[1] = f6973f[2] = f7974f[3] = f8975f[end] = f9976977# Iterate over all components978for k in eachcomponent(equations)979# Unpack left and right states980rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll = get_component(k,981u_ll,982equations)983rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr = get_component(k,984u_rr,985equations)986987rho_inv_ll = 1 / rho_ll988v1_ll = rho_v1_ll * rho_inv_ll989v2_ll = rho_v2_ll * rho_inv_ll990v3_ll = rho_v3_ll * rho_inv_ll991rho_inv_rr = 1 / rho_rr992v1_rr = rho_v1_rr * rho_inv_rr993v2_rr = rho_v2_rr * rho_inv_rr994v3_rr = rho_v3_rr * rho_inv_rr995vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^2996vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^2997998p_ll = (gammas[k] - 1) *999(rho_e_total_ll - 0.5f0 * rho_ll * vel_norm_ll -10000.5f0 * mag_norm_ll -10010.5f0 * psi_ll^2)1002p_rr = (gammas[k] - 1) *1003(rho_e_total_rr - 0.5f0 * rho_rr * vel_norm_rr -10040.5f0 * mag_norm_rr -10050.5f0 * psi_rr^2)1006beta_ll = 0.5f0 * rho_ll / p_ll1007beta_rr = 0.5f0 * rho_rr / p_rr1008# for convenience store vk_plus⋅B1009vel_dot_mag_ll = vk1_plus_ll[k] * B1_ll + vk2_plus_ll[k] * B2_ll +1010vk3_plus_ll[k] * B3_ll1011vel_dot_mag_rr = vk1_plus_rr[k] * B1_rr + vk2_plus_rr[k] * B2_rr +1012vk3_plus_rr[k] * B3_rr10131014# Compute the necessary mean values needed for either direction1015rho_avg = 0.5f0 * (rho_ll + rho_rr)1016rho_mean = ln_mean(rho_ll, rho_rr)1017beta_mean = ln_mean(beta_ll, beta_rr)1018beta_avg = 0.5f0 * (beta_ll + beta_rr)1019p_mean = 0.5f0 * rho_avg / beta_avg1020v1_avg = 0.5f0 * (v1_ll + v1_rr)1021v2_avg = 0.5f0 * (v2_ll + v2_rr)1022v3_avg = 0.5f0 * (v3_ll + v3_rr)1023vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)1024vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)1025vk1_plus_avg = 0.5f0 * (vk1_plus_ll[k] + vk1_plus_rr[k])1026vk2_plus_avg = 0.5f0 * (vk2_plus_ll[k] + vk2_plus_rr[k])1027vk3_plus_avg = 0.5f0 * (vk3_plus_ll[k] + vk3_plus_rr[k])1028# v_minus1029vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]1030vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]1031vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]1032vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]1033vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]1034vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]1035vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)1036vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)1037vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)10381039# Fill the fluxes for the mass and momentum equations1040f1 = rho_mean * v2_avg1041f2 = f1 * v1_avg1042f3 = f1 * v2_avg + p_mean1043f4 = f1 * v3_avg10441045# total energy flux is complicated and involves the previous eight components1046v2_plus_mag_avg = 0.5f0 * (vk2_plus_ll[k] * mag_norm_ll +1047vk2_plus_rr[k] * mag_norm_rr)1048# Euler part1049f5 = f1 * 0.5f0 * (1 / (gammas[k] - 1) / beta_mean - vel_norm_avg) +1050f2 * v1_avg + f3 * v2_avg + f4 * v3_avg1051# MHD part1052f5 += (f6 * B1_avg + f7 * B2_avg + f8 * B3_avg - 0.5f0 * v2_plus_mag_avg +1053B2_avg * vel_dot_mag_avg # Same terms as in Derigs (but with v_plus)1054+ f9 * psi_avg - equations.c_h * psi_B2_avg # GLM term1055+10560.5f0 * vk2_plus_avg * mag_norm_avg -1057vk1_plus_avg * B2_avg * B1_avg - vk2_plus_avg * B2_avg * B2_avg -1058vk3_plus_avg * B2_avg * B3_avg # Additional terms related to the Lorentz non-conservative term (momentum eqs)1059-1060B1_avg * (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) -1061B3_avg * (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg)) # Terms related to the multi-ion non-conservative term (induction equation!)10621063set_component!(f, k, f1, f2, f3, f4, f5, equations)1064end1065else #if orientation == 31066psi_B3_avg = 0.5f0 * (B3_ll * psi_ll + B3_rr * psi_rr)10671068# Magnetic field components from f^MHD1069f6 = v3_plus_avg * B1_avg - v1_plus_avg * B3_avg1070f7 = v3_plus_avg * B2_avg - v2_plus_avg * B3_avg1071f8 = equations.c_h * psi_avg1072f9 = equations.c_h * B3_avg10731074# Start building the flux1075f[1] = f61076f[2] = f71077f[3] = f81078f[end] = f910791080# Iterate over all components1081for k in eachcomponent(equations)1082# Unpack left and right states1083rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll = get_component(k,1084u_ll,1085equations)1086rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr = get_component(k,1087u_rr,1088equations)10891090rho_inv_ll = 1 / rho_ll1091v1_ll = rho_v1_ll * rho_inv_ll1092v2_ll = rho_v2_ll * rho_inv_ll1093v3_ll = rho_v3_ll * rho_inv_ll1094rho_inv_rr = 1 / rho_rr1095v1_rr = rho_v1_rr * rho_inv_rr1096v2_rr = rho_v2_rr * rho_inv_rr1097v3_rr = rho_v3_rr * rho_inv_rr1098vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^21099vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^211001101p_ll = (gammas[k] - 1) *1102(rho_e_total_ll - 0.5f0 * rho_ll * vel_norm_ll -11030.5f0 * mag_norm_ll -11040.5f0 * psi_ll^2)1105p_rr = (gammas[k] - 1) *1106(rho_e_total_rr - 0.5f0 * rho_rr * vel_norm_rr -11070.5f0 * mag_norm_rr -11080.5f0 * psi_rr^2)1109beta_ll = 0.5f0 * rho_ll / p_ll1110beta_rr = 0.5f0 * rho_rr / p_rr1111# for convenience store vk_plus⋅B1112vel_dot_mag_ll = vk1_plus_ll[k] * B1_ll + vk2_plus_ll[k] * B2_ll +1113vk3_plus_ll[k] * B3_ll1114vel_dot_mag_rr = vk1_plus_rr[k] * B1_rr + vk2_plus_rr[k] * B2_rr +1115vk3_plus_rr[k] * B3_rr11161117# Compute the necessary mean values needed for either direction1118rho_avg = 0.5f0 * (rho_ll + rho_rr)1119rho_mean = ln_mean(rho_ll, rho_rr)1120beta_mean = ln_mean(beta_ll, beta_rr)1121beta_avg = 0.5f0 * (beta_ll + beta_rr)1122p_mean = 0.5f0 * rho_avg / beta_avg1123v1_avg = 0.5f0 * (v1_ll + v1_rr)1124v2_avg = 0.5f0 * (v2_ll + v2_rr)1125v3_avg = 0.5f0 * (v3_ll + v3_rr)1126vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)1127vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)1128vk1_plus_avg = 0.5f0 * (vk1_plus_ll[k] + vk1_plus_rr[k])1129vk2_plus_avg = 0.5f0 * (vk2_plus_ll[k] + vk2_plus_rr[k])1130vk3_plus_avg = 0.5f0 * (vk3_plus_ll[k] + vk3_plus_rr[k])1131# v_minus1132vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]1133vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]1134vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]1135vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]1136vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]1137vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]1138vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)1139vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)1140vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)11411142# Fill the fluxes for the mass and momentum equations1143f1 = rho_mean * v3_avg1144f2 = f1 * v1_avg1145f3 = f1 * v2_avg1146f4 = f1 * v3_avg + p_mean11471148# total energy flux is complicated and involves the previous eight components1149v3_plus_mag_avg = 0.5f0 * (vk3_plus_ll[k] * mag_norm_ll +1150vk3_plus_rr[k] * mag_norm_rr)1151# Euler part1152f5 = f1 * 0.5f0 * (1 / (gammas[k] - 1) / beta_mean - vel_norm_avg) +1153f2 * v1_avg + f3 * v2_avg + f4 * v3_avg1154# MHD part1155f5 += (f6 * B1_avg + f7 * B2_avg + f8 * B3_avg - 0.5f0 * v3_plus_mag_avg +1156B3_avg * vel_dot_mag_avg # Same terms as in Derigs (but with v_plus)1157+ f9 * psi_avg - equations.c_h * psi_B3_avg # GLM term1158+11590.5f0 * vk3_plus_avg * mag_norm_avg -1160vk1_plus_avg * B3_avg * B1_avg - vk2_plus_avg * B3_avg * B2_avg -1161vk3_plus_avg * B3_avg * B3_avg # Additional terms related to the Lorentz non-conservative term (momentum eqs)1162-1163B1_avg * (vk3_minus_avg * B1_avg - vk1_minus_avg * B3_avg) -1164B2_avg * (vk3_minus_avg * B2_avg - vk2_minus_avg * B3_avg)) # Terms related to the multi-ion non-conservative term (induction equation!)11651166set_component!(f, k, f1, f2, f3, f4, f5, equations)1167end1168end11691170return SVector(f)1171end11721173function flux_ruedaramirez_etal(u_ll, u_rr, normal_direction::AbstractVector,1174equations::IdealGlmMhdMultiIonEquations3D)1175@unpack gammas = equations1176# Unpack left and right states to get the magnetic field1177B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)1178B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)1179psi_ll = divergence_cleaning_field(u_ll, equations)1180psi_rr = divergence_cleaning_field(u_rr, equations)1181B_dot_n_ll = B1_ll * normal_direction[1] +1182B2_ll * normal_direction[2] +1183B3_ll * normal_direction[3]1184B_dot_n_rr = B1_rr * normal_direction[1] +1185B2_rr * normal_direction[2] +1186B3_rr * normal_direction[3]1187B_dot_n_avg = 0.5f0 * (B_dot_n_ll + B_dot_n_rr)11881189v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll, vk3_plus_ll = charge_averaged_velocities(u_ll,1190equations)1191v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr, vk3_plus_rr = charge_averaged_velocities(u_rr,1192equations)11931194f = zero(MVector{nvariables(equations), eltype(u_ll)})11951196# Compute averages for global variables1197v1_plus_avg = 0.5f0 * (v1_plus_ll + v1_plus_rr)1198v2_plus_avg = 0.5f0 * (v2_plus_ll + v2_plus_rr)1199v3_plus_avg = 0.5f0 * (v3_plus_ll + v3_plus_rr)1200B1_avg = 0.5f0 * (B1_ll + B1_rr)1201B2_avg = 0.5f0 * (B2_ll + B2_rr)1202B3_avg = 0.5f0 * (B3_ll + B3_rr)1203mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^21204mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^21205mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)1206psi_avg = 0.5f0 * (psi_ll + psi_rr)1207# Averages that depend on normal direction1208psi_B_dot_n_avg = 0.5f0 *1209((B1_ll * psi_ll + B1_rr * psi_rr) * normal_direction[1] +1210(B2_ll * psi_ll + B2_rr * psi_rr) * normal_direction[2] +1211(B3_ll * psi_ll + B3_rr * psi_rr) * normal_direction[3])12121213# Magnetic field components from f^MHD (we store them in f6..f9 for consistency with single-fluid MHD)1214f6 = ((equations.c_h * psi_avg) * normal_direction[1] +1215(v2_plus_avg * B1_avg - v1_plus_avg * B2_avg) * normal_direction[2] +1216(v3_plus_avg * B1_avg - v1_plus_avg * B3_avg) * normal_direction[3])1217f7 = ((v1_plus_avg * B2_avg - v2_plus_avg * B1_avg) * normal_direction[1] +1218(equations.c_h * psi_avg) * normal_direction[2] +1219(v3_plus_avg * B2_avg - v2_plus_avg * B3_avg) * normal_direction[3])1220f8 = ((v1_plus_avg * B3_avg - v3_plus_avg * B1_avg) * normal_direction[1] +1221(v2_plus_avg * B3_avg - v3_plus_avg * B2_avg) * normal_direction[2] +1222(equations.c_h * psi_avg) * normal_direction[3])1223f9 = equations.c_h * B_dot_n_avg12241225# Start building the flux1226f[1] = f61227f[2] = f71228f[3] = f81229f[end] = f912301231# Iterate over all components1232for k in eachcomponent(equations)1233# Unpack left and right states1234rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll = get_component(k, u_ll,1235equations)1236rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr = get_component(k, u_rr,1237equations)12381239rho_inv_ll = 1 / rho_ll1240v1_ll = rho_v1_ll * rho_inv_ll1241v2_ll = rho_v2_ll * rho_inv_ll1242v3_ll = rho_v3_ll * rho_inv_ll1243rho_inv_rr = 1 / rho_rr1244v1_rr = rho_v1_rr * rho_inv_rr1245v2_rr = rho_v2_rr * rho_inv_rr1246v3_rr = rho_v3_rr * rho_inv_rr1247vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^21248vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^212491250p_ll = (gammas[k] - 1) *1251(rho_e_total_ll - 0.5f0 * rho_ll * vel_norm_ll - 0.5f0 * mag_norm_ll -12520.5f0 * psi_ll^2)1253p_rr = (gammas[k] - 1) *1254(rho_e_total_rr - 0.5f0 * rho_rr * vel_norm_rr - 0.5f0 * mag_norm_rr -12550.5f0 * psi_rr^2)1256beta_ll = 0.5f0 * rho_ll / p_ll1257beta_rr = 0.5f0 * rho_rr / p_rr12581259# for convenience store vk_plus⋅B1260vel_dot_mag_ll = vk1_plus_ll[k] * B1_ll + vk2_plus_ll[k] * B2_ll +1261vk3_plus_ll[k] * B3_ll1262vel_dot_mag_rr = vk1_plus_rr[k] * B1_rr + vk2_plus_rr[k] * B2_rr +1263vk3_plus_rr[k] * B3_rr12641265# Compute the necessary mean values needed for either direction1266rho_avg = 0.5f0 * (rho_ll + rho_rr)1267rho_mean = ln_mean(rho_ll, rho_rr)1268beta_mean = ln_mean(beta_ll, beta_rr)1269beta_avg = 0.5f0 * (beta_ll + beta_rr)1270p_mean = 0.5f0 * rho_avg / beta_avg1271v1_avg = 0.5f0 * (v1_ll + v1_rr)1272v2_avg = 0.5f0 * (v2_ll + v2_rr)1273v3_avg = 0.5f0 * (v3_ll + v3_rr)1274vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)1275vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)1276vk1_plus_avg = 0.5f0 * (vk1_plus_ll[k] + vk1_plus_rr[k])1277vk2_plus_avg = 0.5f0 * (vk2_plus_ll[k] + vk2_plus_rr[k])1278vk3_plus_avg = 0.5f0 * (vk3_plus_ll[k] + vk3_plus_rr[k])1279# v_minus1280vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]1281vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]1282vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]1283vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]1284vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]1285vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]1286vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)1287vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)1288vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)1289# Compute terms that depend on normal direction1290v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +1291v3_ll * normal_direction[3]1292v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +1293v3_rr * normal_direction[3]1294vk_plus_mag_avg_dot_n = 0.5f0 * (normal_direction[1] *1295(vk1_plus_ll[k] * mag_norm_ll +1296vk1_plus_rr[k] * mag_norm_rr) +1297normal_direction[2] *1298(vk2_plus_ll[k] * mag_norm_ll +1299vk2_plus_rr[k] * mag_norm_rr) +1300normal_direction[3] *1301(vk3_plus_ll[k] * mag_norm_ll +1302vk3_plus_rr[k] * mag_norm_rr))1303vk_plus_dot_n_avg = vk1_plus_avg * normal_direction[1] +1304vk2_plus_avg * normal_direction[2] +1305vk3_plus_avg * normal_direction[3]13061307# Fill the fluxes for the mass and momentum equations1308f1 = rho_mean * 0.5f0 * (v_dot_n_ll + v_dot_n_rr)1309f2 = f1 * v1_avg + p_mean * normal_direction[1]1310f3 = f1 * v2_avg + p_mean * normal_direction[2]1311f4 = f1 * v3_avg + p_mean * normal_direction[3]13121313# total energy flux is complicated and involves the previous eight components1314# Euler part1315f5 = f1 * 0.5f0 * (1 / (gammas[k] - 1) / beta_mean - vel_norm_avg) +1316f2 * v1_avg + f3 * v2_avg + f4 * v3_avg1317# MHD part1318f5 += (f6 * B1_avg + f7 * B2_avg + f8 * B3_avg - 0.5f0 * vk_plus_mag_avg_dot_n +1319B_dot_n_avg * vel_dot_mag_avg # Same terms as in Derigs (but with v_plus)1320+ f9 * psi_avg - equations.c_h * psi_B_dot_n_avg # GLM terms1321+13220.5f0 * vk_plus_dot_n_avg * mag_norm_avg -1323vk1_plus_avg * B_dot_n_avg * B1_avg -1324vk2_plus_avg * B_dot_n_avg * B2_avg -1325vk3_plus_avg * B_dot_n_avg * B3_avg) # Additional terms related to the Lorentz non-conservative term (momentum eqs)13261327# Curl terms related to the multi-ion non-conservative term that depend on vk_minus1328# These terms vanish in the limit of one ion species and come from the induction equation!1329f5 -= (normal_direction[1] *1330(B2_avg * (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) +1331B3_avg * (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg)) +1332normal_direction[2] *1333(B1_avg * (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) +1334B3_avg * (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg)) +1335normal_direction[3] *1336(B1_avg * (vk3_minus_avg * B1_avg - vk1_minus_avg * B3_avg) +1337B2_avg * (vk3_minus_avg * B2_avg - vk2_minus_avg * B3_avg)))13381339set_component!(f, k, f1, f2, f3, f4, f5, equations)1340end13411342return SVector(f)1343end13441345# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation1346# This routine approximates the maximum wave speed as sum of the maximum ion velocity1347# for all species and the maximum magnetosonic speed.1348@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,1349equations::IdealGlmMhdMultiIonEquations3D)1350# Calculate fast magnetoacoustic wave speeds1351# left1352cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)1353# right1354cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)13551356# Calculate velocities1357v_ll = zero(eltype(u_ll))1358v_rr = zero(eltype(u_rr))1359if orientation == 11360for k in eachcomponent(equations)1361rho, rho_v1, _ = get_component(k, u_ll, equations)1362v_ll = max(v_ll, abs(rho_v1 / rho))1363rho, rho_v1, _ = get_component(k, u_rr, equations)1364v_rr = max(v_rr, abs(rho_v1 / rho))1365end1366elseif orientation == 21367for k in eachcomponent(equations)1368rho, rho_v1, rho_v2, _ = get_component(k, u_ll, equations)1369v_ll = max(v_ll, abs(rho_v2 / rho))1370rho, rho_v1, rho_v2, _ = get_component(k, u_rr, equations)1371v_rr = max(v_rr, abs(rho_v2 / rho))1372end1373else #if orientation == 31374for k in eachcomponent(equations)1375rho, rho_v1, rho_v2, rho_v3, _ = get_component(k, u_ll, equations)1376v_ll = max(v_ll, abs(rho_v3 / rho))1377rho, rho_v1, rho_v2, rho_v3, _ = get_component(k, u_rr, equations)1378v_rr = max(v_rr, abs(rho_v3 / rho))1379end1380end13811382return max(abs(v_ll), abs(v_rr)) + max(cf_ll, cf_rr)1383end13841385@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,1386equations::IdealGlmMhdMultiIonEquations3D)1387# Calculate fast magnetoacoustic wave speeds1388# left1389cf_ll = calc_fast_wavespeed(u_ll, normal_direction, equations)1390# right1391cf_rr = calc_fast_wavespeed(u_rr, normal_direction, equations)13921393# Calculate velocities1394v_ll = zero(eltype(u_ll))1395v_rr = zero(eltype(u_rr))1396for k in eachcomponent(equations)1397# Left state1398rho, rho_v1, rho_v2, rho_v3, _ = get_component(k, u_ll, equations)1399inv_rho = 1 / rho1400v_ll = max(v_ll,1401abs(rho_v1 * inv_rho * normal_direction[1] +1402rho_v2 * inv_rho * normal_direction[2] +1403rho_v3 * inv_rho * normal_direction[3]))1404# Right state1405rho, rho_v1, rho_v2, rho_v3, _ = get_component(k, u_rr, equations)1406inv_rho = 1 / rho1407v_rr = max(v_rr,1408abs(rho_v1 * inv_rho * normal_direction[1] +1409rho_v2 * inv_rho * normal_direction[2] +1410rho_v3 * inv_rho * normal_direction[3]))1411end14121413return max(abs(v_ll), abs(v_rr)) + max(cf_ll, cf_rr)1414end14151416# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`1417@inline function max_abs_speed(u_ll, u_rr, orientation::Integer,1418equations::IdealGlmMhdMultiIonEquations3D)1419# Calculate fast magnetoacoustic wave speeds1420# left1421cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)1422# right1423cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)14241425# Calculate velocities1426v_ll = zero(eltype(u_ll))1427v_rr = zero(eltype(u_rr))1428if orientation == 11429for k in eachcomponent(equations)1430rho, rho_v1, _ = get_component(k, u_ll, equations)1431v_ll = max(v_ll, abs(rho_v1 / rho))1432rho, rho_v1, _ = get_component(k, u_rr, equations)1433v_rr = max(v_rr, abs(rho_v1 / rho))1434end1435elseif orientation == 21436for k in eachcomponent(equations)1437rho, rho_v1, rho_v2, _ = get_component(k, u_ll, equations)1438v_ll = max(v_ll, abs(rho_v2 / rho))1439rho, rho_v1, rho_v2, _ = get_component(k, u_rr, equations)1440v_rr = max(v_rr, abs(rho_v2 / rho))1441end1442else #if orientation == 31443for k in eachcomponent(equations)1444rho, rho_v1, rho_v2, rho_v3, _ = get_component(k, u_ll, equations)1445v_ll = max(v_ll, abs(rho_v3 / rho))1446rho, rho_v1, rho_v2, rho_v3, _ = get_component(k, u_rr, equations)1447v_rr = max(v_rr, abs(rho_v3 / rho))1448end1449end14501451return max(abs(v_ll) + cf_ll, abs(v_rr) + cf_rr)1452end14531454# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`1455@inline function max_abs_speed(u_ll, u_rr, normal_direction::AbstractVector,1456equations::IdealGlmMhdMultiIonEquations3D)1457# Calculate fast magnetoacoustic wave speeds1458# left1459cf_ll = calc_fast_wavespeed(u_ll, normal_direction, equations)1460# right1461cf_rr = calc_fast_wavespeed(u_rr, normal_direction, equations)14621463# Calculate velocities1464v_ll = zero(eltype(u_ll))1465v_rr = zero(eltype(u_rr))1466for k in eachcomponent(equations)1467# Left state1468rho, rho_v1, rho_v2, rho_v3, _ = get_component(k, u_ll, equations)1469inv_rho = 1 / rho1470v_ll = max(v_ll,1471abs(rho_v1 * inv_rho * normal_direction[1] +1472rho_v2 * inv_rho * normal_direction[2] +1473rho_v3 * inv_rho * normal_direction[3]))1474# Right state1475rho, rho_v1, rho_v2, rho_v3, _ = get_component(k, u_rr, equations)1476inv_rho = 1 / rho1477v_rr = max(v_rr,1478abs(rho_v1 * inv_rho * normal_direction[1] +1479rho_v2 * inv_rho * normal_direction[2] +1480rho_v3 * inv_rho * normal_direction[3]))1481end14821483return max(abs(v_ll) + cf_ll, abs(v_rr) + cf_rr)1484end14851486@inline function max_abs_speeds(u, equations::IdealGlmMhdMultiIonEquations3D)1487v1 = zero(real(equations))1488v2 = zero(real(equations))1489v3 = zero(real(equations))1490for k in eachcomponent(equations)1491rho, rho_v1, rho_v2, rho_v3, _ = get_component(k, u, equations)1492rho_inv = 1 / rho1493v1 = max(v1, abs(rho_v1 * rho_inv))1494v2 = max(v2, abs(rho_v2 * rho_inv))1495v3 = max(v3, abs(rho_v3 * rho_inv))1496end14971498cf_x_direction = calc_fast_wavespeed(u, 1, equations)1499cf_y_direction = calc_fast_wavespeed(u, 2, equations)1500cf_z_direction = calc_fast_wavespeed(u, 3, equations)15011502return (abs(v1) + cf_x_direction, abs(v2) + cf_y_direction,1503abs(v3) + cf_z_direction)1504end15051506# Compute the fastest wave speed for ideal multi-ion GLM-MHD equations: c_f, the fast1507# magnetoacoustic eigenvalue. This routine computes the fast magnetosonic speed for each ion1508# species using the single-fluid MHD expressions and approximates the multi-ion c_f as1509# the maximum of these individual magnetosonic speeds.1510@inline function calc_fast_wavespeed(cons, orientation::Integer,1511equations::IdealGlmMhdMultiIonEquations3D)1512B1, B2, B3 = magnetic_field(cons, equations)1513psi = divergence_cleaning_field(cons, equations)15141515c_f = zero(real(equations))1516for k in eachcomponent(equations)1517rho, rho_v1, rho_v2, rho_v3, rho_e_total = get_component(k, cons, equations)15181519rho_inv = 1 / rho1520v1 = rho_v1 * rho_inv1521v2 = rho_v2 * rho_inv1522v3 = rho_v3 * rho_inv1523gamma = equations.gammas[k]1524p = (gamma - 1) *1525(rho_e_total - 0.5f0 * rho * (v1^2 + v2^2 + v3^2) -15260.5f0 * (B1^2 + B2^2 + B3^2) -15270.5f0 * psi^2)1528a_square = gamma * p * rho_inv1529inv_sqrt_rho = 1 / sqrt(rho)15301531b1 = B1 * inv_sqrt_rho1532b2 = B2 * inv_sqrt_rho1533b3 = B3 * inv_sqrt_rho1534b_square = b1^2 + b2^2 + b3^215351536if orientation == 11537c_f = max(c_f,1538sqrt(0.5f0 * (a_square + b_square) +15390.5f0 *1540sqrt((a_square + b_square)^2 - 4 * a_square * b1^2)))1541elseif orientation == 21542c_f = max(c_f,1543sqrt(0.5f0 * (a_square + b_square) +15440.5f0 *1545sqrt((a_square + b_square)^2 - 4 * a_square * b2^2)))1546else #if orientation == 31547c_f = max(c_f,1548sqrt(0.5f0 * (a_square + b_square) +15490.5f0 *1550sqrt((a_square + b_square)^2 - 4 * a_square * b3^2)))1551end1552end15531554return c_f1555end15561557@inline function calc_fast_wavespeed(cons, normal_direction::AbstractVector,1558equations::IdealGlmMhdMultiIonEquations3D)1559B1, B2, B3 = magnetic_field(cons, equations)1560psi = divergence_cleaning_field(cons, equations)15611562norm_squared = (normal_direction[1] * normal_direction[1] +1563normal_direction[2] * normal_direction[2] +1564normal_direction[3] * normal_direction[3])15651566c_f = zero(real(equations))1567for k in eachcomponent(equations)1568rho, rho_v1, rho_v2, rho_v3, rho_e_total = get_component(k, cons, equations)15691570rho_inv = 1 / rho1571v1 = rho_v1 * rho_inv1572v2 = rho_v2 * rho_inv1573v3 = rho_v3 * rho_inv1574gamma = equations.gammas[k]1575p = (gamma - 1) *1576(rho_e_total - 0.5f0 * rho * (v1^2 + v2^2 + v3^2) -15770.5f0 * (B1^2 + B2^2 + B3^2) -15780.5f0 * psi^2)1579a_square = gamma * p * rho_inv1580inv_sqrt_rho = 1 / sqrt(rho)15811582b1 = B1 * inv_sqrt_rho1583b2 = B2 * inv_sqrt_rho1584b3 = B3 * inv_sqrt_rho1585b_square = b1^2 + b2^2 + b3^21586b_dot_n_squared = (b1 * normal_direction[1] +1587b2 * normal_direction[2] +1588b3 * normal_direction[3])^2 / norm_squared15891590c_f = max(c_f,1591sqrt((0.5f0 * (a_square + b_square) +15920.5f0 * sqrt((a_square + b_square)^2 -15934 * a_square * b_dot_n_squared)) *1594norm_squared))1595end15961597return c_f1598end1599end # @muladd160016011602