Path: blob/main/src/equations/ideal_glm_mhd_multiion_2d.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"""8IdealGlmMhdMultiIonEquations2D(; gammas, charge_to_mass,9gas_constants = zero(SVector{length(gammas),10eltype(gammas)}),11molar_masses = zero(SVector{length(gammas),12eltype(gammas)}),13ion_ion_collision_constants = zeros(eltype(gammas),14length(gammas),15length(gammas)),16ion_electron_collision_constants = zero(SVector{length(gammas),17eltype(gammas)}),18electron_pressure = electron_pressure_zero,19electron_temperature = electron_pressure_zero,20initial_c_h = NaN)2122The ideal compressible multi-ion MHD equations in two space dimensions augmented with a23generalized Langange multipliers (GLM) divergence-cleaning technique. This is a24multi-species variant of the ideal GLM-MHD equations for calorically perfect plasmas25with independent momentum and energy equations for each ion species. This implementation26assumes that the equations are non-dimensionalized such, that the vacuum permeability is ``\mu_0 = 1``.2728In case of more than one ion species, the specific heat capacity ratios `gammas` and the charge-to-mass29ratios `charge_to_mass` should be passed as tuples, e.g., `gammas=(1.4, 1.667)`.3031The ion-ion and ion-electron collision source terms can be computed using the functions32[`source_terms_collision_ion_ion`](@ref) and [`source_terms_collision_ion_electron`](@ref), respectively.3334For ion-ion collision terms, the optional keyword arguments `gas_constants`, `molar_masses`, and `ion_ion_collision_constants`35must be provided. For ion-electron collision terms, the optional keyword arguments `gas_constants`, `molar_masses`,36`ion_electron_collision_constants`, and `electron_temperature` are required.3738- **`gas_constants`** and **`molar_masses`** are tuples containing the gas constant and molar mass of each39ion species, respectively. The **molar masses** can be provided in any unit system, as they are only used to40compute ratios and are independent of the other arguments.4142- **`ion_ion_collision_constants`** is a symmetric matrix that contains coefficients to compute the collision43frequencies between pairs of ion species. For example, `ion_ion_collision_constants[2, 3]` contains the collision44coefficient for collisions between the ion species 2 and the ion species 3. These constants are derived using the kinetic45theory of gases (see, e.g., *Schunk & Nagy, 2000*). They are related to the collision coefficients ``B_{st}`` listed46in Table 4.3 of *Schunk & Nagy (2000)*, but are scaled by the molecular mass of ion species ``t`` (i.e.,47`ion_ion_collision_constants[2, 3] = ` ``B_{st}/m_{t}``) and must be provided in consistent physical units48(Schunk & Nagy use ``cm^3 K^{3/2} / s``).49See [`source_terms_collision_ion_ion`](@ref) for more details on how these constants are used to compute the collision50frequencies.5152- **`ion_electron_collision_constants`** is a tuple containing coefficients to compute the ion-electron collision frequency53for each ion species. They correspond to the collision coefficients `B_{se}` divided by the elementary charge.54The ion-electron collision frequencies can also be computed using the kinetic theory55of gases (see, e.g., *Schunk & Nagy, 2000*). See [`source_terms_collision_ion_electron`](@ref) for more details on how these56constants are used to compute the collision frequencies.5758- **`electron_temperature`** is a function with the signature `electron_temperature(u, equations)` that can be used59compute the electron temperature as a function of the state `u`. The electron temperature is relevant for the computation60of the ion-electron collision source terms.6162The argument `electron_pressure` can be used to pass a function that computes the electron63pressure as a function of the state `u` with the signature `electron_pressure(u, equations)`.64The gradient of the electron pressure is relevant for the computation of the Lorentz flux65and non-conservative term. By default, the electron pressure is zero.6667The argument `initial_c_h` can be used to set the GLM divergence-cleaning speed. Note that68`initial_c_h = 0` deactivates the divergence cleaning. The callback [`GlmSpeedCallback`](@ref)69can be used to adjust the GLM divergence-cleaning speed according to the time-step size.7071References:72- G. Toth, A. Glocer, Y. Ma, D. Najib, Multi-Ion Magnetohydrodynamics 429 (2010). Numerical73Modeling of Space Plasma Flows, 213–218. [Bib Code: Code: 2010ASPC..429..213T](https://adsabs.harvard.edu/full/2010ASPC..429..213T).74- A. Rueda-Ramírez, A. Sikstel, G. Gassner, An Entropy-Stable Discontinuous Galerkin Discretization75of the Ideal Multi-Ion Magnetohydrodynamics System (2024). Journal of Computational Physics.76[DOI: 10.1016/j.jcp.2024.113655](https://doi.org/10.1016/j.jcp.2024.113655).77- Schunk, R. W., & Nagy, A. F. (2000). Ionospheres: Physics, plasma physics, and chemistry.78Cambridge university press. [DOI: 10.1017/CBO9780511635342](https://doi.org/10.1017/CBO9780511635342).7980!!! info "The multi-ion GLM-MHD equations require source terms"81In case of more than one ion species, the multi-ion GLM-MHD equations should ALWAYS be used82with [`source_terms_lorentz`](@ref).83"""84mutable struct IdealGlmMhdMultiIonEquations2D{NVARS, NCOMP, RealT <: Real,85ElectronPressure, ElectronTemperature} <:86AbstractIdealGlmMhdMultiIonEquations{2, NVARS, NCOMP}87gammas::SVector{NCOMP, RealT} # Heat capacity ratios88charge_to_mass::SVector{NCOMP, RealT} # Charge to mass ratios89gas_constants::SVector{NCOMP, RealT} # Specific gas constants90molar_masses::SVector{NCOMP, RealT} # Molar masses (can be provided in any units as they are only used to compute ratios)91ion_ion_collision_constants::Array{RealT, 2} # Symmetric matrix of collision frequency coefficients92ion_electron_collision_constants::SVector{NCOMP, RealT} # Constants for the ion-electron collision frequencies. The collision frequency is obtained as constant * (e * n_e) / T_e^1.593electron_pressure::ElectronPressure # Function to compute the electron pressure94electron_temperature::ElectronTemperature # Function to compute the electron temperature95c_h::RealT # GLM cleaning speed9697function IdealGlmMhdMultiIonEquations2D{NVARS, NCOMP, RealT, ElectronPressure,98ElectronTemperature}(gammas99::SVector{NCOMP,100RealT},101charge_to_mass102::SVector{NCOMP,103RealT},104gas_constants105::SVector{NCOMP,106RealT},107molar_masses108::SVector{NCOMP,109RealT},110ion_ion_collision_constants111::Array{RealT, 2},112ion_electron_collision_constants113::SVector{NCOMP,114RealT},115electron_pressure116::ElectronPressure,117electron_temperature118::ElectronTemperature,119c_h::RealT) where120{NVARS, NCOMP, RealT <: Real, ElectronPressure, ElectronTemperature}121NCOMP >= 1 ||122throw(DimensionMismatch("`gammas` and `charge_to_mass` have to be filled with at least one value"))123124new(gammas, charge_to_mass, gas_constants, molar_masses,125ion_ion_collision_constants,126ion_electron_collision_constants, electron_pressure, electron_temperature,127c_h)128end129end130131function IdealGlmMhdMultiIonEquations2D(; gammas, charge_to_mass,132gas_constants = zero(SVector{length(gammas),133eltype(gammas)}),134molar_masses = zero(SVector{length(gammas),135eltype(gammas)}),136ion_ion_collision_constants = zeros(eltype(gammas),137length(gammas),138length(gammas)),139ion_electron_collision_constants = zero(SVector{length(gammas),140eltype(gammas)}),141electron_pressure = electron_pressure_zero,142electron_temperature = electron_pressure_zero,143initial_c_h = convert(eltype(gammas), NaN))144_gammas = promote(gammas...)145_charge_to_mass = promote(charge_to_mass...)146_gas_constants = promote(gas_constants...)147_molar_masses = promote(molar_masses...)148_ion_electron_collision_constants = promote(ion_electron_collision_constants...)149RealT = promote_type(eltype(_gammas), eltype(_charge_to_mass),150eltype(_gas_constants), eltype(_molar_masses),151eltype(ion_ion_collision_constants),152eltype(_ion_electron_collision_constants))153__gammas = SVector(map(RealT, _gammas))154__charge_to_mass = SVector(map(RealT, _charge_to_mass))155__gas_constants = SVector(map(RealT, _gas_constants))156__molar_masses = SVector(map(RealT, _molar_masses))157__ion_ion_collision_constants = map(RealT, ion_ion_collision_constants)158__ion_electron_collision_constants = SVector(map(RealT,159_ion_electron_collision_constants))160161NVARS = length(_gammas) * 5 + 4162NCOMP = length(_gammas)163164return IdealGlmMhdMultiIonEquations2D{NVARS, NCOMP, RealT,165typeof(electron_pressure),166typeof(electron_temperature)}(__gammas,167__charge_to_mass,168__gas_constants,169__molar_masses,170__ion_ion_collision_constants,171__ion_electron_collision_constants,172electron_pressure,173electron_temperature,174initial_c_h)175end176177# Outer constructor for `@reset` works correctly178function IdealGlmMhdMultiIonEquations2D(gammas, charge_to_mass, gas_constants,179molar_masses, ion_ion_collision_constants,180ion_electron_collision_constants,181electron_pressure,182electron_temperature,183c_h)184return IdealGlmMhdMultiIonEquations2D(gammas = gammas,185charge_to_mass = charge_to_mass,186gas_constants = gas_constants,187molar_masses = molar_masses,188ion_ion_collision_constants = ion_ion_collision_constants,189ion_electron_collision_constants = ion_electron_collision_constants,190electron_pressure = electron_pressure,191electron_temperature = electron_temperature,192initial_c_h = c_h)193end194195@inline function Base.real(::IdealGlmMhdMultiIonEquations2D{NVARS, NCOMP, RealT}) where {196NVARS,197NCOMP,198RealT199}200RealT201end202203"""204initial_condition_weak_blast_wave(x, t, equations::IdealGlmMhdMultiIonEquations2D)205206A weak blast wave (adapted to multi-ion MHD) from207- Hennemann, S., Rueda-Ramírez, A. M., Hindenlang, F. J., & Gassner, G. J. (2021). A provably entropy208stable subcell shock capturing approach for high order split form DG for the compressible Euler equations.209Journal of Computational Physics, 426, 109935. [arXiv: 2008.12044](https://arxiv.org/abs/2008.12044).210[DOI: 10.1016/j.jcp.2020.109935](https://doi.org/10.1016/j.jcp.2020.109935)211"""212function initial_condition_weak_blast_wave(x, t,213equations::IdealGlmMhdMultiIonEquations2D)214# Adapted MHD version of the weak blast wave from Hennemann & Gassner JCP paper 2020 (Sec. 6.3)215# Same discontinuity in the velocities but with magnetic fields216# Set up polar coordinates217RealT = eltype(x)218inicenter = (0, 0)219x_norm = x[1] - inicenter[1]220y_norm = x[2] - inicenter[2]221r = sqrt(x_norm^2 + y_norm^2)222phi = atan(y_norm, x_norm)223224# Calculate primitive variables225rho = r > 0.5f0 ? one(RealT) : convert(RealT, 1.1691)226v1 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos(phi)227v2 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * sin(phi)228p = r > 0.5f0 ? one(RealT) : convert(RealT, 1.245)229230prim = zero(MVector{nvariables(equations), RealT})231prim[1] = 1232prim[2] = 1233prim[3] = 1234for k in eachcomponent(equations)235# We initialize each species with a fraction of the total density `rho`, such236# that the sum of the densities is `rho := density(prim, equations)`. The density of237# a species is double the density of the next species.238fraction = 2^(k - 1) * (1 - 2) / (1 - 2^ncomponents(equations))239set_component!(prim, k,240fraction * rho, v1,241v2, 0, p, equations)242end243244return prim2cons(SVector(prim), equations)245end246247# 2D flux of the multi-ion GLM-MHD equations in the direction `orientation`248@inline function flux(u, orientation::Integer,249equations::IdealGlmMhdMultiIonEquations2D)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^2258259f = zero(MVector{nvariables(equations), eltype(u)})260261if orientation == 1262f[1] = equations.c_h * psi263f[2] = v1_plus * B2 - v2_plus * B1264f[3] = v1_plus * B3 - v3_plus * B1265266for k in eachcomponent(equations)267rho, rho_v1, rho_v2, rho_v3, rho_e = get_component(k, u, equations)268rho_inv = 1 / rho269v1 = rho_v1 * rho_inv270v2 = rho_v2 * rho_inv271v3 = rho_v3 * rho_inv272kin_en = 0.5f0 * rho * (v1^2 + v2^2 + v3^2)273274gamma = equations.gammas[k]275p = (gamma - 1) * (rho_e - kin_en - mag_en - div_clean_energy)276277f1 = rho_v1278f2 = rho_v1 * v1 + p279f3 = rho_v1 * v2280f4 = rho_v1 * v3281f5 = (kin_en + gamma * p / (gamma - 1)) * v1 + 2 * mag_en * vk1_plus[k] -282B1 * (vk1_plus[k] * B1 + vk2_plus[k] * B2 + vk3_plus[k] * B3) +283equations.c_h * psi * B1284285set_component!(f, k, f1, f2, f3, f4, f5, equations)286end287f[end] = equations.c_h * B1288else #if orientation == 2289f[1] = v2_plus * B1 - v1_plus * B2290f[2] = equations.c_h * psi291f[3] = v2_plus * B3 - v3_plus * B2292293for k in eachcomponent(equations)294rho, rho_v1, rho_v2, rho_v3, rho_e = get_component(k, u, equations)295rho_inv = 1 / rho296v1 = rho_v1 * rho_inv297v2 = rho_v2 * rho_inv298v3 = rho_v3 * rho_inv299kin_en = 0.5f0 * rho * (v1^2 + v2^2 + v3^2)300301gamma = equations.gammas[k]302p = (gamma - 1) * (rho_e - kin_en - mag_en - div_clean_energy)303304f1 = rho_v2305f2 = rho_v2 * v1306f3 = rho_v2 * v2 + p307f4 = rho_v2 * v3308f5 = (kin_en + gamma * p / (gamma - 1)) * v2 + 2 * mag_en * vk2_plus[k] -309B2 * (vk1_plus[k] * B1 + vk2_plus[k] * B2 + vk3_plus[k] * B3) +310equations.c_h * psi * B2311312set_component!(f, k, f1, f2, f3, f4, f5, equations)313end314f[end] = equations.c_h * B2315end316317return SVector(f)318end319320"""321flux_nonconservative_ruedaramirez_etal(u_ll, u_rr,322orientation::Integer,323equations::IdealGlmMhdMultiIonEquations2D)324325Entropy-conserving non-conservative two-point "flux" as described in326- A. Rueda-Ramírez, A. Sikstel, G. Gassner, An Entropy-Stable Discontinuous Galerkin Discretization327of the Ideal Multi-Ion Magnetohydrodynamics System (2024). Journal of Computational Physics.328[DOI: 10.1016/j.jcp.2024.113655](https://doi.org/10.1016/j.jcp.2024.113655).329330!!! info "Usage and Scaling of Non-Conservative Fluxes in Trixi.jl"331The non-conservative fluxes derived in the reference above are written as the product332of local and symmetric parts and are meant to be used in the same way as the conservative333fluxes (i.e., flux + flux_noncons in both volume and surface integrals). In this routine,334the fluxes are multiplied by 2 because the non-conservative fluxes are always multiplied335by 0.5 whenever they are used in the Trixi code.336337The term is composed of four individual non-conservative terms:3381. The Godunov-Powell term, which arises for plasmas with non-vanishing magnetic field divergence, and339is evaluated as a function of the charge-averaged velocity.3402. The Lorentz-force term, which becomes a conservative term in the limit of one ion species for vanishing341electron pressure gradients.3423. The "multi-ion" term, which vanishes in the limit of one ion species.3434. The GLM term, which is needed for Galilean invariance.344"""345@inline function flux_nonconservative_ruedaramirez_etal(u_ll, u_rr,346orientation::Integer,347equations::IdealGlmMhdMultiIonEquations2D)348@unpack charge_to_mass = equations349# Unpack left and right states to get the magnetic field350B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)351B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)352psi_ll = divergence_cleaning_field(u_ll, equations)353psi_rr = divergence_cleaning_field(u_rr, equations)354355# Compute important averages356B1_avg = 0.5f0 * (B1_ll + B1_rr)357B2_avg = 0.5f0 * (B2_ll + B2_rr)358B3_avg = 0.5f0 * (B3_ll + B3_rr)359mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2360mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2361mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)362psi_avg = 0.5f0 * (psi_ll + psi_rr)363364# Mean electron pressure365pe_ll = equations.electron_pressure(u_ll, equations)366pe_rr = equations.electron_pressure(u_rr, equations)367pe_mean = 0.5f0 * (pe_ll + pe_rr)368369# Compute charge ratio of u_ll370charge_ratio_ll = zero(MVector{ncomponents(equations), eltype(u_ll)})371total_electron_charge = zero(eltype(u_ll))372for k in eachcomponent(equations)373rho_k = u_ll[3 + (k - 1) * 5 + 1]374charge_ratio_ll[k] = rho_k * charge_to_mass[k]375total_electron_charge += charge_ratio_ll[k]376end377charge_ratio_ll ./= total_electron_charge378379# Compute auxiliary variables380v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll, vk3_plus_ll = charge_averaged_velocities(u_ll,381equations)382v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr, vk3_plus_rr = charge_averaged_velocities(u_rr,383equations)384385f = zero(MVector{nvariables(equations), eltype(u_ll)})386387if orientation == 1388# Entries of Godunov-Powell term for induction equation (multiply by 2 because the non-conservative flux is389# multiplied by 0.5 whenever it's used in the Trixi code)390f[1] = 2 * v1_plus_ll * B1_avg391f[2] = 2 * v2_plus_ll * B1_avg392f[3] = 2 * v3_plus_ll * B1_avg393394for k in eachcomponent(equations)395# Compute term Lorentz term396f2 = charge_ratio_ll[k] * (0.5f0 * mag_norm_avg - B1_avg * B1_avg + pe_mean)397f3 = charge_ratio_ll[k] * (-B1_avg * B2_avg)398f4 = charge_ratio_ll[k] * (-B1_avg * B3_avg)399f5 = vk1_plus_ll[k] * pe_mean400401# Compute multi-ion term (vanishes for NCOMP==1)402vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]403vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]404vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]405vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]406vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]407vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]408vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)409vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)410vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)411f5 += (B2_ll * (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) +412B3_ll * (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg))413414# Compute Godunov-Powell term415f2 += charge_ratio_ll[k] * B1_ll * B1_avg416f3 += charge_ratio_ll[k] * B2_ll * B1_avg417f4 += charge_ratio_ll[k] * B3_ll * B1_avg418f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *419B1_avg420421# Compute GLM term for the energy422f5 += v1_plus_ll * psi_ll * psi_avg423424# Add to the flux vector (multiply by 2 because the non-conservative flux is425# multiplied by 0.5 whenever it's used in the Trixi code)426set_component!(f, k, 0, 2 * f2, 2 * f3, 2 * f4, 2 * f5,427equations)428end429# Compute GLM term for psi (multiply by 2 because the non-conservative flux is430# multiplied by 0.5 whenever it's used in the Trixi code)431f[end] = 2 * v1_plus_ll * psi_avg432433else #if orientation == 2434# Entries of Godunov-Powell term for induction equation (multiply by 2 because the non-conservative flux is435# multiplied by 0.5 whenever it's used in the Trixi code)436f[1] = 2 * v1_plus_ll * B2_avg437f[2] = 2 * v2_plus_ll * B2_avg438f[3] = 2 * v3_plus_ll * B2_avg439440for k in eachcomponent(equations)441# Compute term Lorentz term442f2 = charge_ratio_ll[k] * (-B2_avg * B1_avg)443f3 = charge_ratio_ll[k] *444(-B2_avg * B2_avg + 0.5f0 * mag_norm_avg + pe_mean)445f4 = charge_ratio_ll[k] * (-B2_avg * B3_avg)446f5 = vk2_plus_ll[k] * pe_mean447448# Compute multi-ion term (vanishes for NCOMP==1)449vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]450vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]451vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]452vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]453vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]454vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]455vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)456vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)457vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)458f5 += (B1_ll * (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) +459B3_ll * (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg))460461# Compute Godunov-Powell term462f2 += charge_ratio_ll[k] * B1_ll * B2_avg463f3 += charge_ratio_ll[k] * B2_ll * B2_avg464f4 += charge_ratio_ll[k] * B3_ll * B2_avg465f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *466B2_avg467468# Compute GLM term for the energy469f5 += v2_plus_ll * psi_ll * psi_avg470471# Add to the flux vector (multiply by 2 because the non-conservative flux is472# multiplied by 0.5 whenever it's used in the Trixi code)473set_component!(f, k, 0, 2 * f2, 2 * f3, 2 * f4, 2 * f5,474equations)475end476# Compute GLM term for psi (multiply by 2 because the non-conservative flux is477# multiplied by 0.5 whenever it's used in the Trixi code)478f[end] = 2 * v2_plus_ll * psi_avg479end480481return SVector(f)482end483484"""485flux_nonconservative_central(u_ll, u_rr, orientation::Integer,486equations::IdealGlmMhdMultiIonEquations2D)487488Central non-conservative two-point "flux", where the symmetric parts are computed with standard averages.489The use of this term together with [`flux_central`](@ref)490with [`VolumeIntegralFluxDifferencing`](@ref) yields a "standard"491(weak-form) DGSEM discretization of the multi-ion GLM-MHD system. This flux can also be used to construct a492standard local Lax-Friedrichs flux using `surface_flux = (flux_lax_friedrichs, flux_nonconservative_central)`.493494!!! info "Usage and Scaling of Non-Conservative Fluxes in Trixi"495The central non-conservative fluxes implemented in this function are written as the product496of local and symmetric parts, where the symmetric part is a standard average. These fluxes497are meant to be used in the same way as the conservative fluxes (i.e., flux + flux_noncons498in both volume and surface integrals). In this routine, the fluxes are multiplied by 2 because499the non-conservative fluxes are always multiplied by 0.5 whenever they are used in the Trixi code.500501The term is composed of four individual non-conservative terms:5021. The Godunov-Powell term, which arises for plasmas with non-vanishing magnetic field divergence, and503is evaluated as a function of the charge-averaged velocity.5042. The Lorentz-force term, which becomes a conservative term in the limit of one ion species for vanishing505electron pressure gradients.5063. The "multi-ion" term, which vanishes in the limit of one ion species.5074. The GLM term, which is needed for Galilean invariance.508"""509@inline function flux_nonconservative_central(u_ll, u_rr, orientation::Integer,510equations::IdealGlmMhdMultiIonEquations2D)511@unpack charge_to_mass = equations512# Unpack left and right states to get the magnetic field513B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)514B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)515psi_ll = divergence_cleaning_field(u_ll, equations)516psi_rr = divergence_cleaning_field(u_rr, equations)517518# Compute important averages519mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2520mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2521522# Electron pressure523pe_ll = equations.electron_pressure(u_ll, equations)524pe_rr = equations.electron_pressure(u_rr, equations)525526# Compute charge ratio of u_ll527charge_ratio_ll = zero(MVector{ncomponents(equations), eltype(u_ll)})528total_electron_charge = zero(real(equations))529for k in eachcomponent(equations)530rho_k = u_ll[3 + (k - 1) * 5 + 1]531charge_ratio_ll[k] = rho_k * charge_to_mass[k]532total_electron_charge += charge_ratio_ll[k]533end534charge_ratio_ll ./= total_electron_charge535536# Compute auxiliary variables537v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll, vk3_plus_ll = charge_averaged_velocities(u_ll,538equations)539v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr, vk3_plus_rr = charge_averaged_velocities(u_rr,540equations)541542f = zero(MVector{nvariables(equations), eltype(u_ll)})543544if orientation == 1545# Entries of Godunov-Powell term for induction equation546f[1] = v1_plus_ll * (B1_ll + B1_rr)547f[2] = v2_plus_ll * (B1_ll + B1_rr)548f[3] = v3_plus_ll * (B1_ll + B1_rr)549for k in eachcomponent(equations)550# Compute Lorentz term551f2 = charge_ratio_ll[k] * ((0.5f0 * mag_norm_ll - B1_ll * B1_ll + pe_ll) +552(0.5f0 * mag_norm_rr - B1_rr * B1_rr + pe_rr))553f3 = charge_ratio_ll[k] * ((-B1_ll * B2_ll) + (-B1_rr * B2_rr))554f4 = charge_ratio_ll[k] * ((-B1_ll * B3_ll) + (-B1_rr * B3_rr))555f5 = vk1_plus_ll[k] * (pe_ll + pe_rr)556557# Compute multi-ion term, which vanishes for NCOMP==1558vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]559vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]560vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]561vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]562vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]563vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]564f5 += (B2_ll * ((vk1_minus_ll * B2_ll - vk2_minus_ll * B1_ll) +565(vk1_minus_rr * B2_rr - vk2_minus_rr * B1_rr)) +566B3_ll * ((vk1_minus_ll * B3_ll - vk3_minus_ll * B1_ll) +567(vk1_minus_rr * B3_rr - vk3_minus_rr * B1_rr)))568569# Compute Godunov-Powell term570f2 += charge_ratio_ll[k] * B1_ll * (B1_ll + B1_rr)571f3 += charge_ratio_ll[k] * B2_ll * (B1_ll + B1_rr)572f4 += charge_ratio_ll[k] * B3_ll * (B1_ll + B1_rr)573f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *574(B1_ll + B1_rr)575576# Compute GLM term for the energy577f5 += v1_plus_ll * psi_ll * (psi_ll + psi_rr)578579# Append to the flux vector580set_component!(f, k, 0, f2, f3, f4, f5, equations)581end582# Compute GLM term for psi583f[end] = v1_plus_ll * (psi_ll + psi_rr)584585else #if orientation == 2586# Entries of Godunov-Powell term for induction equation587f[1] = v1_plus_ll * (B2_ll + B2_rr)588f[2] = v2_plus_ll * (B2_ll + B2_rr)589f[3] = v3_plus_ll * (B2_ll + B2_rr)590591for k in eachcomponent(equations)592# Compute Lorentz term593f2 = charge_ratio_ll[k] * ((-B2_ll * B1_ll) + (-B2_rr * B1_rr))594f3 = charge_ratio_ll[k] * ((-B2_ll * B2_ll + 0.5f0 * mag_norm_ll + pe_ll) +595(-B2_rr * B2_rr + 0.5f0 * mag_norm_rr + pe_rr))596f4 = charge_ratio_ll[k] * ((-B2_ll * B3_ll) + (-B2_rr * B3_rr))597f5 = vk2_plus_ll[k] * (pe_ll + pe_rr)598599# Compute multi-ion term (vanishes for NCOMP==1)600vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]601vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]602vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]603vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]604vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]605vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]606f5 += (B1_ll * ((vk2_minus_ll * B1_ll - vk1_minus_ll * B2_ll) +607(vk2_minus_rr * B1_rr - vk1_minus_rr * B2_rr)) +608B3_ll * ((vk2_minus_ll * B3_ll - vk3_minus_ll * B2_ll) +609(vk2_minus_rr * B3_rr - vk3_minus_rr * B2_rr)))610611# Compute Godunov-Powell term612f2 += charge_ratio_ll[k] * B1_ll * (B2_ll + B2_rr)613f3 += charge_ratio_ll[k] * B2_ll * (B2_ll + B2_rr)614f4 += charge_ratio_ll[k] * B3_ll * (B2_ll + B2_rr)615f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *616(B2_ll + B2_rr)617618# Compute GLM term for the energy619f5 += v2_plus_ll * psi_ll * (psi_ll + psi_rr)620621# Append to the flux vector622set_component!(f, k, 0, f2, f3, f4, f5, equations)623end624# Compute GLM term for psi625f[end] = v2_plus_ll * (psi_ll + psi_rr)626end627628return SVector(f)629end630631"""632flux_ruedaramirez_etal(u_ll, u_rr, orientation, equations::IdealGlmMhdMultiIonEquations2D)633634Entropy conserving two-point flux for the multi-ion GLM-MHD equations from635- A. Rueda-Ramírez, A. Sikstel, G. Gassner, An Entropy-Stable Discontinuous Galerkin Discretization636of the Ideal Multi-Ion Magnetohydrodynamics System (2024). Journal of Computational Physics.637[DOI: 10.1016/j.jcp.2024.113655](https://doi.org/10.1016/j.jcp.2024.113655).638639This flux (together with the MHD non-conservative term) is consistent in the case of one ion species with the flux of:640- Derigs et al. (2018). Ideal GLM-MHD: About the entropy consistent nine-wave magnetic field641divergence diminishing ideal magnetohydrodynamics equations for multi-ion642[DOI: 10.1016/j.jcp.2018.03.002](https://doi.org/10.1016/j.jcp.2018.03.002)643"""644function flux_ruedaramirez_etal(u_ll, u_rr, orientation::Integer,645equations::IdealGlmMhdMultiIonEquations2D)646@unpack gammas = equations647# Unpack left and right states to get the magnetic field648B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)649B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)650psi_ll = divergence_cleaning_field(u_ll, equations)651psi_rr = divergence_cleaning_field(u_rr, equations)652653v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll, vk3_plus_ll = charge_averaged_velocities(u_ll,654equations)655v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr, vk3_plus_rr = charge_averaged_velocities(u_rr,656equations)657658f = zero(MVector{nvariables(equations), eltype(u_ll)})659660# Compute averages for global variables661v1_plus_avg = 0.5f0 * (v1_plus_ll + v1_plus_rr)662v2_plus_avg = 0.5f0 * (v2_plus_ll + v2_plus_rr)663v3_plus_avg = 0.5f0 * (v3_plus_ll + v3_plus_rr)664B1_avg = 0.5f0 * (B1_ll + B1_rr)665B2_avg = 0.5f0 * (B2_ll + B2_rr)666B3_avg = 0.5f0 * (B3_ll + B3_rr)667mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2668mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2669mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)670psi_avg = 0.5f0 * (psi_ll + psi_rr)671672if orientation == 1673psi_B1_avg = 0.5f0 * (B1_ll * psi_ll + B1_rr * psi_rr)674675# Magnetic field components from f^MHD676f6 = equations.c_h * psi_avg677f7 = v1_plus_avg * B2_avg - v2_plus_avg * B1_avg678f8 = v1_plus_avg * B3_avg - v3_plus_avg * B1_avg679f9 = equations.c_h * B1_avg680681# Start building the flux682f[1] = f6683f[2] = f7684f[3] = f8685f[end] = f9686687# Iterate over all components688for k in eachcomponent(equations)689# Unpack left and right states690rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll = get_component(k, u_ll,691equations)692rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr = get_component(k, u_rr,693equations)694rho_inv_ll = 1 / rho_ll695v1_ll = rho_v1_ll * rho_inv_ll696v2_ll = rho_v2_ll * rho_inv_ll697v3_ll = rho_v3_ll * rho_inv_ll698rho_inv_rr = 1 / rho_rr699v1_rr = rho_v1_rr * rho_inv_rr700v2_rr = rho_v2_rr * rho_inv_rr701v3_rr = rho_v3_rr * rho_inv_rr702vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^2703vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^2704705p_ll = (gammas[k] - 1) *706(rho_e_ll - 0.5f0 * rho_ll * vel_norm_ll - 0.5f0 * mag_norm_ll -7070.5f0 * psi_ll^2)708p_rr = (gammas[k] - 1) *709(rho_e_rr - 0.5f0 * rho_rr * vel_norm_rr - 0.5f0 * mag_norm_rr -7100.5f0 * psi_rr^2)711beta_ll = 0.5f0 * rho_ll / p_ll712beta_rr = 0.5f0 * rho_rr / p_rr713# for convenience store vk_plus⋅B714vel_dot_mag_ll = vk1_plus_ll[k] * B1_ll + vk2_plus_ll[k] * B2_ll +715vk3_plus_ll[k] * B3_ll716vel_dot_mag_rr = vk1_plus_rr[k] * B1_rr + vk2_plus_rr[k] * B2_rr +717vk3_plus_rr[k] * B3_rr718719# Compute the necessary mean values needed for either direction720rho_avg = 0.5f0 * (rho_ll + rho_rr)721rho_mean = ln_mean(rho_ll, rho_rr)722beta_mean = ln_mean(beta_ll, beta_rr)723beta_avg = 0.5f0 * (beta_ll + beta_rr)724p_mean = 0.5f0 * rho_avg / beta_avg725v1_avg = 0.5f0 * (v1_ll + v1_rr)726v2_avg = 0.5f0 * (v2_ll + v2_rr)727v3_avg = 0.5f0 * (v3_ll + v3_rr)728vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)729vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)730vk1_plus_avg = 0.5f0 * (vk1_plus_ll[k] + vk1_plus_rr[k])731vk2_plus_avg = 0.5f0 * (vk2_plus_ll[k] + vk2_plus_rr[k])732vk3_plus_avg = 0.5f0 * (vk3_plus_ll[k] + vk3_plus_rr[k])733# v_minus734vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]735vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]736vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]737vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]738vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]739vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]740vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)741vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)742vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)743744# Fill the fluxes for the mass and momentum equations745f1 = rho_mean * v1_avg746f2 = f1 * v1_avg + p_mean747f3 = f1 * v2_avg748f4 = f1 * v3_avg749750# total energy flux is complicated and involves the previous eight components751v1_plus_mag_avg = 0.5f0 * (vk1_plus_ll[k] * mag_norm_ll +752vk1_plus_rr[k] * mag_norm_rr)753# Euler part754f5 = f1 * 0.5f0 * (1 / (gammas[k] - 1) / beta_mean - vel_norm_avg) +755f2 * v1_avg + f3 * v2_avg + f4 * v3_avg756# MHD part757f5 += (f6 * B1_avg + f7 * B2_avg + f8 * B3_avg - 0.5f0 * v1_plus_mag_avg +758B1_avg * vel_dot_mag_avg # Same terms as in Derigs (but with v_plus)759+ f9 * psi_avg - equations.c_h * psi_B1_avg # GLM term760+7610.5f0 * vk1_plus_avg * mag_norm_avg -762vk1_plus_avg * B1_avg * B1_avg - vk2_plus_avg * B1_avg * B2_avg -763vk3_plus_avg * B1_avg * B3_avg # Additional terms related to the Lorentz non-conservative term (momentum eqs)764-765B2_avg * (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) -766B3_avg * (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg)) # Terms related to the multi-ion non-conservative term (induction equation!)767768set_component!(f, k, f1, f2, f3, f4, f5, equations)769end770else #if orientation == 2771psi_B2_avg = 0.5f0 * (B2_ll * psi_ll + B2_rr * psi_rr)772773# Magnetic field components from f^MHD774f6 = v2_plus_avg * B1_avg - v1_plus_avg * B2_avg775f7 = equations.c_h * psi_avg776f8 = v2_plus_avg * B3_avg - v3_plus_avg * B2_avg777f9 = equations.c_h * B2_avg778779# Start building the flux780f[1] = f6781f[2] = f7782f[3] = f8783f[end] = f9784785# Iterate over all components786for k in eachcomponent(equations)787# Unpack left and right states788rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll = get_component(k, u_ll,789equations)790rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr = get_component(k, u_rr,791equations)792793rho_inv_ll = 1 / rho_ll794v1_ll = rho_v1_ll * rho_inv_ll795v2_ll = rho_v2_ll * rho_inv_ll796v3_ll = rho_v3_ll * rho_inv_ll797rho_inv_rr = 1 / rho_rr798v1_rr = rho_v1_rr * rho_inv_rr799v2_rr = rho_v2_rr * rho_inv_rr800v3_rr = rho_v3_rr * rho_inv_rr801vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^2802vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^2803804p_ll = (gammas[k] - 1) *805(rho_e_ll - 0.5f0 * rho_ll * vel_norm_ll - 0.5f0 * mag_norm_ll -8060.5f0 * psi_ll^2)807p_rr = (gammas[k] - 1) *808(rho_e_rr - 0.5f0 * rho_rr * vel_norm_rr - 0.5f0 * mag_norm_rr -8090.5f0 * psi_rr^2)810beta_ll = 0.5f0 * rho_ll / p_ll811beta_rr = 0.5f0 * rho_rr / p_rr812# for convenience store vk_plus⋅B813vel_dot_mag_ll = vk1_plus_ll[k] * B1_ll + vk2_plus_ll[k] * B2_ll +814vk3_plus_ll[k] * B3_ll815vel_dot_mag_rr = vk1_plus_rr[k] * B1_rr + vk2_plus_rr[k] * B2_rr +816vk3_plus_rr[k] * B3_rr817818# Compute the necessary mean values needed for either direction819rho_avg = 0.5f0 * (rho_ll + rho_rr)820rho_mean = ln_mean(rho_ll, rho_rr)821beta_mean = ln_mean(beta_ll, beta_rr)822beta_avg = 0.5f0 * (beta_ll + beta_rr)823p_mean = 0.5f0 * rho_avg / beta_avg824v1_avg = 0.5f0 * (v1_ll + v1_rr)825v2_avg = 0.5f0 * (v2_ll + v2_rr)826v3_avg = 0.5f0 * (v3_ll + v3_rr)827vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)828vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)829vk1_plus_avg = 0.5f0 * (vk1_plus_ll[k] + vk1_plus_rr[k])830vk2_plus_avg = 0.5f0 * (vk2_plus_ll[k] + vk2_plus_rr[k])831vk3_plus_avg = 0.5f0 * (vk3_plus_ll[k] + vk3_plus_rr[k])832# v_minus833vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]834vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]835vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]836vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]837vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]838vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]839vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)840vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)841vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)842843# Fill the fluxes for the mass and momentum equations844f1 = rho_mean * v2_avg845f2 = f1 * v1_avg846f3 = f1 * v2_avg + p_mean847f4 = f1 * v3_avg848849# total energy flux is complicated and involves the previous eight components850v2_plus_mag_avg = 0.5f0 * (vk2_plus_ll[k] * mag_norm_ll +851vk2_plus_rr[k] * mag_norm_rr)852# Euler part853f5 = f1 * 0.5f0 * (1 / (gammas[k] - 1) / beta_mean - vel_norm_avg) +854f2 * v1_avg + f3 * v2_avg + f4 * v3_avg855# MHD part856f5 += (f6 * B1_avg + f7 * B2_avg + f8 * B3_avg - 0.5f0 * v2_plus_mag_avg +857B2_avg * vel_dot_mag_avg # Same terms as in Derigs (but with v_plus)858+ f9 * psi_avg - equations.c_h * psi_B2_avg # GLM term859+8600.5f0 * vk2_plus_avg * mag_norm_avg -861vk1_plus_avg * B2_avg * B1_avg - vk2_plus_avg * B2_avg * B2_avg -862vk3_plus_avg * B2_avg * B3_avg # Additional terms related to the Lorentz non-conservative term (momentum eqs)863-864B1_avg * (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) -865B3_avg * (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg)) # Terms related to the multi-ion non-conservative term (induction equation!)866867set_component!(f, k, f1, f2, f3, f4, f5, equations)868end869end870871return SVector(f)872end873874# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation875# This routine approximates the maximum wave speed as sum of the maximum ion velocity876# for all species and the maximum magnetosonic speed.877@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,878equations::IdealGlmMhdMultiIonEquations2D)879# Calculate fast magnetoacoustic wave speeds880# left881cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)882# right883cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)884885# Calculate velocities886v_ll = zero(eltype(u_ll))887v_rr = zero(eltype(u_rr))888if orientation == 1889for k in eachcomponent(equations)890rho, rho_v1, _ = get_component(k, u_ll, equations)891v_ll = max(v_ll, abs(rho_v1 / rho))892rho, rho_v1, _ = get_component(k, u_rr, equations)893v_rr = max(v_rr, abs(rho_v1 / rho))894end895else #if orientation == 2896for k in eachcomponent(equations)897rho, rho_v1, rho_v2, _ = get_component(k, u_ll, equations)898v_ll = max(v_ll, abs(rho_v2 / rho))899rho, rho_v1, rho_v2, _ = get_component(k, u_rr, equations)900v_rr = max(v_rr, abs(rho_v2 / rho))901end902end903904return max(abs(v_ll), abs(v_rr)) + max(cf_ll, cf_rr)905end906907# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`908@inline function max_abs_speed(u_ll, u_rr, orientation::Integer,909equations::IdealGlmMhdMultiIonEquations2D)910# Calculate fast magnetoacoustic wave speeds911# left912cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)913# right914cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)915916# Calculate velocities917v_ll = zero(eltype(u_ll))918v_rr = zero(eltype(u_rr))919if orientation == 1920for k in eachcomponent(equations)921rho, rho_v1, _ = get_component(k, u_ll, equations)922v_ll = max(v_ll, abs(rho_v1 / rho))923rho, rho_v1, _ = get_component(k, u_rr, equations)924v_rr = max(v_rr, abs(rho_v1 / rho))925end926else #if orientation == 2927for k in eachcomponent(equations)928rho, rho_v1, rho_v2, _ = get_component(k, u_ll, equations)929v_ll = max(v_ll, abs(rho_v2 / rho))930rho, rho_v1, rho_v2, _ = get_component(k, u_rr, equations)931v_rr = max(v_rr, abs(rho_v2 / rho))932end933end934935return max(abs(v_ll) + cf_ll, abs(v_rr) + cf_rr)936end937938@inline function max_abs_speeds(u, equations::IdealGlmMhdMultiIonEquations2D)939v1 = zero(real(equations))940v2 = zero(real(equations))941for k in eachcomponent(equations)942rho, rho_v1, rho_v2, _ = get_component(k, u, equations)943v1 = max(v1, abs(rho_v1 / rho))944v2 = max(v2, abs(rho_v2 / rho))945end946947cf_x_direction = calc_fast_wavespeed(u, 1, equations)948cf_y_direction = calc_fast_wavespeed(u, 2, equations)949950return (abs(v1) + cf_x_direction, abs(v2) + cf_y_direction)951end952953# Compute the fastest wave speed for ideal multi-ion GLM-MHD equations: c_f, the fast954# magnetoacoustic eigenvalue. This routine computes the fast magnetosonic speed for each ion955# species using the single-fluid MHD expressions and approximates the multi-ion c_f as956# the maximum of these individual magnetosonic speeds.957@inline function calc_fast_wavespeed(cons, orientation::Integer,958equations::IdealGlmMhdMultiIonEquations2D)959B1, B2, B3 = magnetic_field(cons, equations)960psi = divergence_cleaning_field(cons, equations)961962c_f = zero(real(equations))963for k in eachcomponent(equations)964rho, rho_v1, rho_v2, rho_v3, rho_e = get_component(k, cons, equations)965966rho_inv = 1 / rho967v1 = rho_v1 * rho_inv968v2 = rho_v2 * rho_inv969v3 = rho_v3 * rho_inv970gamma = equations.gammas[k]971p = (gamma - 1) *972(rho_e - 0.5f0 * rho * (v1^2 + v2^2 + v3^2) - 0.5f0 * (B1^2 + B2^2 + B3^2) -9730.5f0 * psi^2)974a_square = gamma * p * rho_inv975inv_sqrt_rho = 1 / sqrt(rho)976977b1 = B1 * inv_sqrt_rho978b2 = B2 * inv_sqrt_rho979b3 = B3 * inv_sqrt_rho980b_square = b1^2 + b2^2 + b3^2981982if orientation == 1983c_f = max(c_f,984sqrt(0.5f0 * (a_square + b_square) +9850.5f0 *986sqrt((a_square + b_square)^2 - 4 * a_square * b1^2)))987else #if orientation == 2988c_f = max(c_f,989sqrt(0.5f0 * (a_square + b_square) +9900.5f0 *991sqrt((a_square + b_square)^2 - 4 * a_square * b2^2)))992end993end994995return c_f996end997end # @muladd9989991000