Path: blob/main/src/equations/ideal_glm_mhd_multiion_3d.jl
2055 views
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).1# Since these FMAs can increase the performance of many numerical algorithms,2# we need to opt-in explicitly.3# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.4@muladd begin5#! format: noindent67@doc raw"""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"""40mutable struct 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"))5859new(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}95RealT96end9798"""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 = 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 - 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 = 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 - 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 = 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 - 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 = 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 - 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_ll = get_component(k, u_ll,878equations)879rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr = get_component(k, u_rr,880equations)881882rho_inv_ll = 1 / rho_ll883v1_ll = rho_v1_ll * rho_inv_ll884v2_ll = rho_v2_ll * rho_inv_ll885v3_ll = rho_v3_ll * rho_inv_ll886rho_inv_rr = 1 / rho_rr887v1_rr = rho_v1_rr * rho_inv_rr888v2_rr = rho_v2_rr * rho_inv_rr889v3_rr = rho_v3_rr * rho_inv_rr890vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^2891vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^2892893p_ll = (gammas[k] - 1) *894(rho_e_ll - 0.5f0 * rho_ll * vel_norm_ll - 0.5f0 * mag_norm_ll -8950.5f0 * psi_ll^2)896p_rr = (gammas[k] - 1) *897(rho_e_rr - 0.5f0 * rho_rr * vel_norm_rr - 0.5f0 * mag_norm_rr -8980.5f0 * psi_rr^2)899beta_ll = 0.5f0 * rho_ll / p_ll900beta_rr = 0.5f0 * rho_rr / p_rr901# for convenience store vk_plus⋅B902vel_dot_mag_ll = vk1_plus_ll[k] * B1_ll + vk2_plus_ll[k] * B2_ll +903vk3_plus_ll[k] * B3_ll904vel_dot_mag_rr = vk1_plus_rr[k] * B1_rr + vk2_plus_rr[k] * B2_rr +905vk3_plus_rr[k] * B3_rr906907# Compute the necessary mean values needed for either direction908rho_avg = 0.5f0 * (rho_ll + rho_rr)909rho_mean = ln_mean(rho_ll, rho_rr)910beta_mean = ln_mean(beta_ll, beta_rr)911beta_avg = 0.5f0 * (beta_ll + beta_rr)912p_mean = 0.5f0 * rho_avg / beta_avg913v1_avg = 0.5f0 * (v1_ll + v1_rr)914v2_avg = 0.5f0 * (v2_ll + v2_rr)915v3_avg = 0.5f0 * (v3_ll + v3_rr)916vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)917vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)918vk1_plus_avg = 0.5f0 * (vk1_plus_ll[k] + vk1_plus_rr[k])919vk2_plus_avg = 0.5f0 * (vk2_plus_ll[k] + vk2_plus_rr[k])920vk3_plus_avg = 0.5f0 * (vk3_plus_ll[k] + vk3_plus_rr[k])921# v_minus922vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]923vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]924vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]925vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]926vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]927vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]928vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)929vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)930vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)931932# Fill the fluxes for the mass and momentum equations933f1 = rho_mean * v1_avg934f2 = f1 * v1_avg + p_mean935f3 = f1 * v2_avg936f4 = f1 * v3_avg937938# total energy flux is complicated and involves the previous eight components939v1_plus_mag_avg = 0.5f0 * (vk1_plus_ll[k] * mag_norm_ll +940vk1_plus_rr[k] * mag_norm_rr)941# Euler part942f5 = f1 * 0.5f0 * (1 / (gammas[k] - 1) / beta_mean - vel_norm_avg) +943f2 * v1_avg + f3 * v2_avg + f4 * v3_avg944# MHD part945f5 += (f6 * B1_avg + f7 * B2_avg + f8 * B3_avg - 0.5f0 * v1_plus_mag_avg +946B1_avg * vel_dot_mag_avg # Same terms as in Derigs (but with v_plus)947+ f9 * psi_avg - equations.c_h * psi_B1_avg # GLM term948+9490.5f0 * vk1_plus_avg * mag_norm_avg -950vk1_plus_avg * B1_avg * B1_avg - vk2_plus_avg * B1_avg * B2_avg -951vk3_plus_avg * B1_avg * B3_avg # Additional terms related to the Lorentz non-conservative term (momentum eqs)952-953B2_avg * (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) -954B3_avg * (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg)) # Terms related to the multi-ion non-conservative term (induction equation!)955956set_component!(f, k, f1, f2, f3, f4, f5, equations)957end958elseif orientation == 2959psi_B2_avg = 0.5f0 * (B2_ll * psi_ll + B2_rr * psi_rr)960961# Magnetic field components from f^MHD962f6 = v2_plus_avg * B1_avg - v1_plus_avg * B2_avg963f7 = equations.c_h * psi_avg964f8 = v2_plus_avg * B3_avg - v3_plus_avg * B2_avg965f9 = equations.c_h * B2_avg966967# Start building the flux968f[1] = f6969f[2] = f7970f[3] = f8971f[end] = f9972973# Iterate over all components974for k in eachcomponent(equations)975# Unpack left and right states976rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll = get_component(k, u_ll,977equations)978rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr = get_component(k, u_rr,979equations)980981rho_inv_ll = 1 / rho_ll982v1_ll = rho_v1_ll * rho_inv_ll983v2_ll = rho_v2_ll * rho_inv_ll984v3_ll = rho_v3_ll * rho_inv_ll985rho_inv_rr = 1 / rho_rr986v1_rr = rho_v1_rr * rho_inv_rr987v2_rr = rho_v2_rr * rho_inv_rr988v3_rr = rho_v3_rr * rho_inv_rr989vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^2990vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^2991992p_ll = (gammas[k] - 1) *993(rho_e_ll - 0.5f0 * rho_ll * vel_norm_ll - 0.5f0 * mag_norm_ll -9940.5f0 * psi_ll^2)995p_rr = (gammas[k] - 1) *996(rho_e_rr - 0.5f0 * rho_rr * vel_norm_rr - 0.5f0 * mag_norm_rr -9970.5f0 * psi_rr^2)998beta_ll = 0.5f0 * rho_ll / p_ll999beta_rr = 0.5f0 * rho_rr / p_rr1000# for convenience store vk_plus⋅B1001vel_dot_mag_ll = vk1_plus_ll[k] * B1_ll + vk2_plus_ll[k] * B2_ll +1002vk3_plus_ll[k] * B3_ll1003vel_dot_mag_rr = vk1_plus_rr[k] * B1_rr + vk2_plus_rr[k] * B2_rr +1004vk3_plus_rr[k] * B3_rr10051006# Compute the necessary mean values needed for either direction1007rho_avg = 0.5f0 * (rho_ll + rho_rr)1008rho_mean = ln_mean(rho_ll, rho_rr)1009beta_mean = ln_mean(beta_ll, beta_rr)1010beta_avg = 0.5f0 * (beta_ll + beta_rr)1011p_mean = 0.5f0 * rho_avg / beta_avg1012v1_avg = 0.5f0 * (v1_ll + v1_rr)1013v2_avg = 0.5f0 * (v2_ll + v2_rr)1014v3_avg = 0.5f0 * (v3_ll + v3_rr)1015vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)1016vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)1017vk1_plus_avg = 0.5f0 * (vk1_plus_ll[k] + vk1_plus_rr[k])1018vk2_plus_avg = 0.5f0 * (vk2_plus_ll[k] + vk2_plus_rr[k])1019vk3_plus_avg = 0.5f0 * (vk3_plus_ll[k] + vk3_plus_rr[k])1020# v_minus1021vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]1022vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]1023vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]1024vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]1025vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]1026vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]1027vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)1028vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)1029vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)10301031# Fill the fluxes for the mass and momentum equations1032f1 = rho_mean * v2_avg1033f2 = f1 * v1_avg1034f3 = f1 * v2_avg + p_mean1035f4 = f1 * v3_avg10361037# total energy flux is complicated and involves the previous eight components1038v2_plus_mag_avg = 0.5f0 * (vk2_plus_ll[k] * mag_norm_ll +1039vk2_plus_rr[k] * mag_norm_rr)1040# Euler part1041f5 = f1 * 0.5f0 * (1 / (gammas[k] - 1) / beta_mean - vel_norm_avg) +1042f2 * v1_avg + f3 * v2_avg + f4 * v3_avg1043# MHD part1044f5 += (f6 * B1_avg + f7 * B2_avg + f8 * B3_avg - 0.5f0 * v2_plus_mag_avg +1045B2_avg * vel_dot_mag_avg # Same terms as in Derigs (but with v_plus)1046+ f9 * psi_avg - equations.c_h * psi_B2_avg # GLM term1047+10480.5f0 * vk2_plus_avg * mag_norm_avg -1049vk1_plus_avg * B2_avg * B1_avg - vk2_plus_avg * B2_avg * B2_avg -1050vk3_plus_avg * B2_avg * B3_avg # Additional terms related to the Lorentz non-conservative term (momentum eqs)1051-1052B1_avg * (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) -1053B3_avg * (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg)) # Terms related to the multi-ion non-conservative term (induction equation!)10541055set_component!(f, k, f1, f2, f3, f4, f5, equations)1056end1057else #if orientation == 31058psi_B3_avg = 0.5f0 * (B3_ll * psi_ll + B3_rr * psi_rr)10591060# Magnetic field components from f^MHD1061f6 = v3_plus_avg * B1_avg - v1_plus_avg * B3_avg1062f7 = v3_plus_avg * B2_avg - v2_plus_avg * B3_avg1063f8 = equations.c_h * psi_avg1064f9 = equations.c_h * B3_avg10651066# Start building the flux1067f[1] = f61068f[2] = f71069f[3] = f81070f[end] = f910711072# Iterate over all components1073for k in eachcomponent(equations)1074# Unpack left and right states1075rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll = get_component(k, u_ll,1076equations)1077rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr = get_component(k, u_rr,1078equations)10791080rho_inv_ll = 1 / rho_ll1081v1_ll = rho_v1_ll * rho_inv_ll1082v2_ll = rho_v2_ll * rho_inv_ll1083v3_ll = rho_v3_ll * rho_inv_ll1084rho_inv_rr = 1 / rho_rr1085v1_rr = rho_v1_rr * rho_inv_rr1086v2_rr = rho_v2_rr * rho_inv_rr1087v3_rr = rho_v3_rr * rho_inv_rr1088vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^21089vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^210901091p_ll = (gammas[k] - 1) *1092(rho_e_ll - 0.5f0 * rho_ll * vel_norm_ll - 0.5f0 * mag_norm_ll -10930.5f0 * psi_ll^2)1094p_rr = (gammas[k] - 1) *1095(rho_e_rr - 0.5f0 * rho_rr * vel_norm_rr - 0.5f0 * mag_norm_rr -10960.5f0 * psi_rr^2)1097beta_ll = 0.5f0 * rho_ll / p_ll1098beta_rr = 0.5f0 * rho_rr / p_rr1099# for convenience store vk_plus⋅B1100vel_dot_mag_ll = vk1_plus_ll[k] * B1_ll + vk2_plus_ll[k] * B2_ll +1101vk3_plus_ll[k] * B3_ll1102vel_dot_mag_rr = vk1_plus_rr[k] * B1_rr + vk2_plus_rr[k] * B2_rr +1103vk3_plus_rr[k] * B3_rr11041105# Compute the necessary mean values needed for either direction1106rho_avg = 0.5f0 * (rho_ll + rho_rr)1107rho_mean = ln_mean(rho_ll, rho_rr)1108beta_mean = ln_mean(beta_ll, beta_rr)1109beta_avg = 0.5f0 * (beta_ll + beta_rr)1110p_mean = 0.5f0 * rho_avg / beta_avg1111v1_avg = 0.5f0 * (v1_ll + v1_rr)1112v2_avg = 0.5f0 * (v2_ll + v2_rr)1113v3_avg = 0.5f0 * (v3_ll + v3_rr)1114vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)1115vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)1116vk1_plus_avg = 0.5f0 * (vk1_plus_ll[k] + vk1_plus_rr[k])1117vk2_plus_avg = 0.5f0 * (vk2_plus_ll[k] + vk2_plus_rr[k])1118vk3_plus_avg = 0.5f0 * (vk3_plus_ll[k] + vk3_plus_rr[k])1119# v_minus1120vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]1121vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]1122vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]1123vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]1124vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]1125vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]1126vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)1127vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)1128vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)11291130# Fill the fluxes for the mass and momentum equations1131f1 = rho_mean * v3_avg1132f2 = f1 * v1_avg1133f3 = f1 * v2_avg1134f4 = f1 * v3_avg + p_mean11351136# total energy flux is complicated and involves the previous eight components1137v3_plus_mag_avg = 0.5f0 * (vk3_plus_ll[k] * mag_norm_ll +1138vk3_plus_rr[k] * mag_norm_rr)1139# Euler part1140f5 = f1 * 0.5f0 * (1 / (gammas[k] - 1) / beta_mean - vel_norm_avg) +1141f2 * v1_avg + f3 * v2_avg + f4 * v3_avg1142# MHD part1143f5 += (f6 * B1_avg + f7 * B2_avg + f8 * B3_avg - 0.5f0 * v3_plus_mag_avg +1144B3_avg * vel_dot_mag_avg # Same terms as in Derigs (but with v_plus)1145+ f9 * psi_avg - equations.c_h * psi_B3_avg # GLM term1146+11470.5f0 * vk3_plus_avg * mag_norm_avg -1148vk1_plus_avg * B3_avg * B1_avg - vk2_plus_avg * B3_avg * B2_avg -1149vk3_plus_avg * B3_avg * B3_avg # Additional terms related to the Lorentz non-conservative term (momentum eqs)1150-1151B1_avg * (vk3_minus_avg * B1_avg - vk1_minus_avg * B3_avg) -1152B2_avg * (vk3_minus_avg * B2_avg - vk2_minus_avg * B3_avg)) # Terms related to the multi-ion non-conservative term (induction equation!)11531154set_component!(f, k, f1, f2, f3, f4, f5, equations)1155end1156end11571158return SVector(f)1159end11601161function flux_ruedaramirez_etal(u_ll, u_rr, normal_direction::AbstractVector,1162equations::IdealGlmMhdMultiIonEquations3D)1163@unpack gammas = equations1164# Unpack left and right states to get the magnetic field1165B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)1166B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)1167psi_ll = divergence_cleaning_field(u_ll, equations)1168psi_rr = divergence_cleaning_field(u_rr, equations)1169B_dot_n_ll = B1_ll * normal_direction[1] +1170B2_ll * normal_direction[2] +1171B3_ll * normal_direction[3]1172B_dot_n_rr = B1_rr * normal_direction[1] +1173B2_rr * normal_direction[2] +1174B3_rr * normal_direction[3]1175B_dot_n_avg = 0.5f0 * (B_dot_n_ll + B_dot_n_rr)11761177v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll, vk3_plus_ll = charge_averaged_velocities(u_ll,1178equations)1179v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr, vk3_plus_rr = charge_averaged_velocities(u_rr,1180equations)11811182f = zero(MVector{nvariables(equations), eltype(u_ll)})11831184# Compute averages for global variables1185v1_plus_avg = 0.5f0 * (v1_plus_ll + v1_plus_rr)1186v2_plus_avg = 0.5f0 * (v2_plus_ll + v2_plus_rr)1187v3_plus_avg = 0.5f0 * (v3_plus_ll + v3_plus_rr)1188B1_avg = 0.5f0 * (B1_ll + B1_rr)1189B2_avg = 0.5f0 * (B2_ll + B2_rr)1190B3_avg = 0.5f0 * (B3_ll + B3_rr)1191mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^21192mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^21193mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)1194psi_avg = 0.5f0 * (psi_ll + psi_rr)1195# Averages that depend on normal direction1196psi_B_dot_n_avg = 0.5f0 *1197((B1_ll * psi_ll + B1_rr * psi_rr) * normal_direction[1] +1198(B2_ll * psi_ll + B2_rr * psi_rr) * normal_direction[2] +1199(B3_ll * psi_ll + B3_rr * psi_rr) * normal_direction[3])12001201# Magnetic field components from f^MHD (we store them in f6..f9 for consistency with single-fluid MHD)1202f6 = ((equations.c_h * psi_avg) * normal_direction[1] +1203(v2_plus_avg * B1_avg - v1_plus_avg * B2_avg) * normal_direction[2] +1204(v3_plus_avg * B1_avg - v1_plus_avg * B3_avg) * normal_direction[3])1205f7 = ((v1_plus_avg * B2_avg - v2_plus_avg * B1_avg) * normal_direction[1] +1206(equations.c_h * psi_avg) * normal_direction[2] +1207(v3_plus_avg * B2_avg - v2_plus_avg * B3_avg) * normal_direction[3])1208f8 = ((v1_plus_avg * B3_avg - v3_plus_avg * B1_avg) * normal_direction[1] +1209(v2_plus_avg * B3_avg - v3_plus_avg * B2_avg) * normal_direction[2] +1210(equations.c_h * psi_avg) * normal_direction[3])1211f9 = equations.c_h * B_dot_n_avg12121213# Start building the flux1214f[1] = f61215f[2] = f71216f[3] = f81217f[end] = f912181219# Iterate over all components1220for k in eachcomponent(equations)1221# Unpack left and right states1222rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll = get_component(k, u_ll,1223equations)1224rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr = get_component(k, u_rr,1225equations)12261227rho_inv_ll = 1 / rho_ll1228v1_ll = rho_v1_ll * rho_inv_ll1229v2_ll = rho_v2_ll * rho_inv_ll1230v3_ll = rho_v3_ll * rho_inv_ll1231rho_inv_rr = 1 / rho_rr1232v1_rr = rho_v1_rr * rho_inv_rr1233v2_rr = rho_v2_rr * rho_inv_rr1234v3_rr = rho_v3_rr * rho_inv_rr1235vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^21236vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^212371238p_ll = (gammas[k] - 1) *1239(rho_e_ll - 0.5f0 * rho_ll * vel_norm_ll - 0.5f0 * mag_norm_ll -12400.5f0 * psi_ll^2)1241p_rr = (gammas[k] - 1) *1242(rho_e_rr - 0.5f0 * rho_rr * vel_norm_rr - 0.5f0 * mag_norm_rr -12430.5f0 * psi_rr^2)1244beta_ll = 0.5f0 * rho_ll / p_ll1245beta_rr = 0.5f0 * rho_rr / p_rr12461247# for convenience store vk_plus⋅B1248vel_dot_mag_ll = vk1_plus_ll[k] * B1_ll + vk2_plus_ll[k] * B2_ll +1249vk3_plus_ll[k] * B3_ll1250vel_dot_mag_rr = vk1_plus_rr[k] * B1_rr + vk2_plus_rr[k] * B2_rr +1251vk3_plus_rr[k] * B3_rr12521253# Compute the necessary mean values needed for either direction1254rho_avg = 0.5f0 * (rho_ll + rho_rr)1255rho_mean = ln_mean(rho_ll, rho_rr)1256beta_mean = ln_mean(beta_ll, beta_rr)1257beta_avg = 0.5f0 * (beta_ll + beta_rr)1258p_mean = 0.5f0 * rho_avg / beta_avg1259v1_avg = 0.5f0 * (v1_ll + v1_rr)1260v2_avg = 0.5f0 * (v2_ll + v2_rr)1261v3_avg = 0.5f0 * (v3_ll + v3_rr)1262vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)1263vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)1264vk1_plus_avg = 0.5f0 * (vk1_plus_ll[k] + vk1_plus_rr[k])1265vk2_plus_avg = 0.5f0 * (vk2_plus_ll[k] + vk2_plus_rr[k])1266vk3_plus_avg = 0.5f0 * (vk3_plus_ll[k] + vk3_plus_rr[k])1267# v_minus1268vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]1269vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]1270vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]1271vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]1272vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]1273vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]1274vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)1275vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)1276vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)1277# Compute terms that depend on normal direction1278v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +1279v3_ll * normal_direction[3]1280v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +1281v3_rr * normal_direction[3]1282vk_plus_mag_avg_dot_n = 0.5f0 * (normal_direction[1] *1283(vk1_plus_ll[k] * mag_norm_ll +1284vk1_plus_rr[k] * mag_norm_rr) +1285normal_direction[2] *1286(vk2_plus_ll[k] * mag_norm_ll +1287vk2_plus_rr[k] * mag_norm_rr) +1288normal_direction[3] *1289(vk3_plus_ll[k] * mag_norm_ll +1290vk3_plus_rr[k] * mag_norm_rr))1291vk_plus_dot_n_avg = vk1_plus_avg * normal_direction[1] +1292vk2_plus_avg * normal_direction[2] +1293vk3_plus_avg * normal_direction[3]12941295# Fill the fluxes for the mass and momentum equations1296f1 = rho_mean * 0.5f0 * (v_dot_n_ll + v_dot_n_rr)1297f2 = f1 * v1_avg + p_mean * normal_direction[1]1298f3 = f1 * v2_avg + p_mean * normal_direction[2]1299f4 = f1 * v3_avg + p_mean * normal_direction[3]13001301# total energy flux is complicated and involves the previous eight components1302# Euler part1303f5 = f1 * 0.5f0 * (1 / (gammas[k] - 1) / beta_mean - vel_norm_avg) +1304f2 * v1_avg + f3 * v2_avg + f4 * v3_avg1305# MHD part1306f5 += (f6 * B1_avg + f7 * B2_avg + f8 * B3_avg - 0.5f0 * vk_plus_mag_avg_dot_n +1307B_dot_n_avg * vel_dot_mag_avg # Same terms as in Derigs (but with v_plus)1308+ f9 * psi_avg - equations.c_h * psi_B_dot_n_avg # GLM terms1309+13100.5f0 * vk_plus_dot_n_avg * mag_norm_avg -1311vk1_plus_avg * B_dot_n_avg * B1_avg -1312vk2_plus_avg * B_dot_n_avg * B2_avg -1313vk3_plus_avg * B_dot_n_avg * B3_avg) # Additional terms related to the Lorentz non-conservative term (momentum eqs)13141315# Curl terms related to the multi-ion non-conservative term that depend on vk_minus1316# These terms vanish in the limit of one ion species and come from the induction equation!1317f5 -= (normal_direction[1] *1318(B2_avg * (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) +1319B3_avg * (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg)) +1320normal_direction[2] *1321(B1_avg * (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) +1322B3_avg * (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg)) +1323normal_direction[3] *1324(B1_avg * (vk3_minus_avg * B1_avg - vk1_minus_avg * B3_avg) +1325B2_avg * (vk3_minus_avg * B2_avg - vk2_minus_avg * B3_avg)))13261327set_component!(f, k, f1, f2, f3, f4, f5, equations)1328end13291330return SVector(f)1331end13321333# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation1334# This routine approximates the maximum wave speed as sum of the maximum ion velocity1335# for all species and the maximum magnetosonic speed.1336@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,1337equations::IdealGlmMhdMultiIonEquations3D)1338# Calculate fast magnetoacoustic wave speeds1339# left1340cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)1341# right1342cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)13431344# Calculate velocities1345v_ll = zero(eltype(u_ll))1346v_rr = zero(eltype(u_rr))1347if orientation == 11348for k in eachcomponent(equations)1349rho, rho_v1, _ = get_component(k, u_ll, equations)1350v_ll = max(v_ll, abs(rho_v1 / rho))1351rho, rho_v1, _ = get_component(k, u_rr, equations)1352v_rr = max(v_rr, abs(rho_v1 / rho))1353end1354elseif orientation == 21355for k in eachcomponent(equations)1356rho, rho_v1, rho_v2, _ = get_component(k, u_ll, equations)1357v_ll = max(v_ll, abs(rho_v2 / rho))1358rho, rho_v1, rho_v2, _ = get_component(k, u_rr, equations)1359v_rr = max(v_rr, abs(rho_v2 / rho))1360end1361else #if orientation == 31362for k in eachcomponent(equations)1363rho, rho_v1, rho_v2, rho_v3, _ = get_component(k, u_ll, equations)1364v_ll = max(v_ll, abs(rho_v3 / rho))1365rho, rho_v1, rho_v2, rho_v3, _ = get_component(k, u_rr, equations)1366v_rr = max(v_rr, abs(rho_v3 / rho))1367end1368end13691370return max(abs(v_ll), abs(v_rr)) + max(cf_ll, cf_rr)1371end13721373@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,1374equations::IdealGlmMhdMultiIonEquations3D)1375# Calculate fast magnetoacoustic wave speeds1376# left1377cf_ll = calc_fast_wavespeed(u_ll, normal_direction, equations)1378# right1379cf_rr = calc_fast_wavespeed(u_rr, normal_direction, equations)13801381# Calculate velocities1382v_ll = zero(eltype(u_ll))1383v_rr = zero(eltype(u_rr))1384for k in eachcomponent(equations)1385# Left state1386rho, rho_v1, rho_v2, rho_v3, _ = get_component(k, u_ll, equations)1387inv_rho = 1 / rho1388v_ll = max(v_ll,1389abs(rho_v1 * inv_rho * normal_direction[1] +1390rho_v2 * inv_rho * normal_direction[2] +1391rho_v3 * inv_rho * normal_direction[3]))1392# Right state1393rho, rho_v1, rho_v2, rho_v3, _ = get_component(k, u_rr, equations)1394inv_rho = 1 / rho1395v_rr = max(v_rr,1396abs(rho_v1 * inv_rho * normal_direction[1] +1397rho_v2 * inv_rho * normal_direction[2] +1398rho_v3 * inv_rho * normal_direction[3]))1399end14001401return max(abs(v_ll), abs(v_rr)) + max(cf_ll, cf_rr)1402end14031404# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`1405@inline function max_abs_speed(u_ll, u_rr, orientation::Integer,1406equations::IdealGlmMhdMultiIonEquations3D)1407# Calculate fast magnetoacoustic wave speeds1408# left1409cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)1410# right1411cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)14121413# Calculate velocities1414v_ll = zero(eltype(u_ll))1415v_rr = zero(eltype(u_rr))1416if orientation == 11417for k in eachcomponent(equations)1418rho, rho_v1, _ = get_component(k, u_ll, equations)1419v_ll = max(v_ll, abs(rho_v1 / rho))1420rho, rho_v1, _ = get_component(k, u_rr, equations)1421v_rr = max(v_rr, abs(rho_v1 / rho))1422end1423elseif orientation == 21424for k in eachcomponent(equations)1425rho, rho_v1, rho_v2, _ = get_component(k, u_ll, equations)1426v_ll = max(v_ll, abs(rho_v2 / rho))1427rho, rho_v1, rho_v2, _ = get_component(k, u_rr, equations)1428v_rr = max(v_rr, abs(rho_v2 / rho))1429end1430else #if orientation == 31431for k in eachcomponent(equations)1432rho, rho_v1, rho_v2, rho_v3, _ = get_component(k, u_ll, equations)1433v_ll = max(v_ll, abs(rho_v3 / rho))1434rho, rho_v1, rho_v2, rho_v3, _ = get_component(k, u_rr, equations)1435v_rr = max(v_rr, abs(rho_v3 / rho))1436end1437end14381439return max(abs(v_ll) + cf_ll, abs(v_rr) + cf_rr)1440end14411442# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`1443@inline function max_abs_speed(u_ll, u_rr, normal_direction::AbstractVector,1444equations::IdealGlmMhdMultiIonEquations3D)1445# Calculate fast magnetoacoustic wave speeds1446# left1447cf_ll = calc_fast_wavespeed(u_ll, normal_direction, equations)1448# right1449cf_rr = calc_fast_wavespeed(u_rr, normal_direction, equations)14501451# Calculate velocities1452v_ll = zero(eltype(u_ll))1453v_rr = zero(eltype(u_rr))1454for k in eachcomponent(equations)1455# Left state1456rho, rho_v1, rho_v2, rho_v3, _ = get_component(k, u_ll, equations)1457inv_rho = 1 / rho1458v_ll = max(v_ll,1459abs(rho_v1 * inv_rho * normal_direction[1] +1460rho_v2 * inv_rho * normal_direction[2] +1461rho_v3 * inv_rho * normal_direction[3]))1462# Right state1463rho, rho_v1, rho_v2, rho_v3, _ = get_component(k, u_rr, equations)1464inv_rho = 1 / rho1465v_rr = max(v_rr,1466abs(rho_v1 * inv_rho * normal_direction[1] +1467rho_v2 * inv_rho * normal_direction[2] +1468rho_v3 * inv_rho * normal_direction[3]))1469end14701471return max(abs(v_ll) + cf_ll, abs(v_rr) + cf_rr)1472end14731474@inline function max_abs_speeds(u, equations::IdealGlmMhdMultiIonEquations3D)1475v1 = zero(real(equations))1476v2 = zero(real(equations))1477v3 = zero(real(equations))1478for k in eachcomponent(equations)1479rho, rho_v1, rho_v2, rho_v3, _ = get_component(k, u, equations)1480rho_inv = 1 / rho1481v1 = max(v1, abs(rho_v1 * rho_inv))1482v2 = max(v2, abs(rho_v2 * rho_inv))1483v3 = max(v3, abs(rho_v3 * rho_inv))1484end14851486cf_x_direction = calc_fast_wavespeed(u, 1, equations)1487cf_y_direction = calc_fast_wavespeed(u, 2, equations)1488cf_z_direction = calc_fast_wavespeed(u, 3, equations)14891490return (abs(v1) + cf_x_direction, abs(v2) + cf_y_direction,1491abs(v3) + cf_z_direction)1492end14931494# Compute the fastest wave speed for ideal multi-ion GLM-MHD equations: c_f, the fast1495# magnetoacoustic eigenvalue. This routine computes the fast magnetosonic speed for each ion1496# species using the single-fluid MHD expressions and approximates the multi-ion c_f as1497# the maximum of these individual magnetosonic speeds.1498@inline function calc_fast_wavespeed(cons, orientation::Integer,1499equations::IdealGlmMhdMultiIonEquations3D)1500B1, B2, B3 = magnetic_field(cons, equations)1501psi = divergence_cleaning_field(cons, equations)15021503c_f = zero(real(equations))1504for k in eachcomponent(equations)1505rho, rho_v1, rho_v2, rho_v3, rho_e = get_component(k, cons, equations)15061507rho_inv = 1 / rho1508v1 = rho_v1 * rho_inv1509v2 = rho_v2 * rho_inv1510v3 = rho_v3 * rho_inv1511gamma = equations.gammas[k]1512p = (gamma - 1) *1513(rho_e - 0.5f0 * rho * (v1^2 + v2^2 + v3^2) - 0.5f0 * (B1^2 + B2^2 + B3^2) -15140.5f0 * psi^2)1515a_square = gamma * p * rho_inv1516inv_sqrt_rho = 1 / sqrt(rho)15171518b1 = B1 * inv_sqrt_rho1519b2 = B2 * inv_sqrt_rho1520b3 = B3 * inv_sqrt_rho1521b_square = b1^2 + b2^2 + b3^215221523if orientation == 11524c_f = max(c_f,1525sqrt(0.5f0 * (a_square + b_square) +15260.5f0 *1527sqrt((a_square + b_square)^2 - 4 * a_square * b1^2)))1528elseif orientation == 21529c_f = max(c_f,1530sqrt(0.5f0 * (a_square + b_square) +15310.5f0 *1532sqrt((a_square + b_square)^2 - 4 * a_square * b2^2)))1533else #if orientation == 31534c_f = max(c_f,1535sqrt(0.5f0 * (a_square + b_square) +15360.5f0 *1537sqrt((a_square + b_square)^2 - 4 * a_square * b3^2)))1538end1539end15401541return c_f1542end15431544@inline function calc_fast_wavespeed(cons, normal_direction::AbstractVector,1545equations::IdealGlmMhdMultiIonEquations3D)1546B1, B2, B3 = magnetic_field(cons, equations)1547psi = divergence_cleaning_field(cons, equations)15481549norm_squared = (normal_direction[1] * normal_direction[1] +1550normal_direction[2] * normal_direction[2] +1551normal_direction[3] * normal_direction[3])15521553c_f = zero(real(equations))1554for k in eachcomponent(equations)1555rho, rho_v1, rho_v2, rho_v3, rho_e = get_component(k, cons, equations)15561557rho_inv = 1 / rho1558v1 = rho_v1 * rho_inv1559v2 = rho_v2 * rho_inv1560v3 = rho_v3 * rho_inv1561gamma = equations.gammas[k]1562p = (gamma - 1) *1563(rho_e - 0.5f0 * rho * (v1^2 + v2^2 + v3^2) - 0.5f0 * (B1^2 + B2^2 + B3^2) -15640.5f0 * psi^2)1565a_square = gamma * p * rho_inv1566inv_sqrt_rho = 1 / sqrt(rho)15671568b1 = B1 * inv_sqrt_rho1569b2 = B2 * inv_sqrt_rho1570b3 = B3 * inv_sqrt_rho1571b_square = b1^2 + b2^2 + b3^21572b_dot_n_squared = (b1 * normal_direction[1] +1573b2 * normal_direction[2] +1574b3 * normal_direction[3])^2 / norm_squared15751576c_f = max(c_f,1577sqrt((0.5f0 * (a_square + b_square) +15780.5f0 * sqrt((a_square + b_square)^2 -15794 * a_square * b_dot_n_squared)) *1580norm_squared))1581end15821583return c_f1584end1585end # @muladd158615871588