Path: blob/main/src/equations/ideal_glm_mhd_multiion_2d.jl
5586 views
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).1# Since these FMAs can increase the performance of many numerical algorithms,2# we need to opt-in explicitly.3# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.4@muladd begin5#! format: noindent67@doc raw"""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"""84struct 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"))123124return new(gammas, charge_to_mass, gas_constants, molar_masses,125ion_ion_collision_constants,126ion_electron_collision_constants, electron_pressure,127electron_temperature,128c_h)129end130end131132function IdealGlmMhdMultiIonEquations2D(; gammas, charge_to_mass,133gas_constants = zero(SVector{length(gammas),134eltype(gammas)}),135molar_masses = zero(SVector{length(gammas),136eltype(gammas)}),137ion_ion_collision_constants = zeros(eltype(gammas),138length(gammas),139length(gammas)),140ion_electron_collision_constants = zero(SVector{length(gammas),141eltype(gammas)}),142electron_pressure = electron_pressure_zero,143electron_temperature = electron_pressure_zero,144initial_c_h = convert(eltype(gammas), NaN))145_gammas = promote(gammas...)146_charge_to_mass = promote(charge_to_mass...)147_gas_constants = promote(gas_constants...)148_molar_masses = promote(molar_masses...)149_ion_electron_collision_constants = promote(ion_electron_collision_constants...)150RealT = promote_type(eltype(_gammas), eltype(_charge_to_mass),151eltype(_gas_constants), eltype(_molar_masses),152eltype(ion_ion_collision_constants),153eltype(_ion_electron_collision_constants))154__gammas = SVector(map(RealT, _gammas))155__charge_to_mass = SVector(map(RealT, _charge_to_mass))156__gas_constants = SVector(map(RealT, _gas_constants))157__molar_masses = SVector(map(RealT, _molar_masses))158__ion_ion_collision_constants = map(RealT, ion_ion_collision_constants)159__ion_electron_collision_constants = SVector(map(RealT,160_ion_electron_collision_constants))161162NVARS = length(_gammas) * 5 + 4163NCOMP = length(_gammas)164165return IdealGlmMhdMultiIonEquations2D{NVARS, NCOMP, RealT,166typeof(electron_pressure),167typeof(electron_temperature)}(__gammas,168__charge_to_mass,169__gas_constants,170__molar_masses,171__ion_ion_collision_constants,172__ion_electron_collision_constants,173electron_pressure,174electron_temperature,175initial_c_h)176end177178# Outer constructor for `@reset` works correctly179function IdealGlmMhdMultiIonEquations2D(gammas, charge_to_mass, gas_constants,180molar_masses, ion_ion_collision_constants,181ion_electron_collision_constants,182electron_pressure,183electron_temperature,184c_h)185return IdealGlmMhdMultiIonEquations2D(gammas = gammas,186charge_to_mass = charge_to_mass,187gas_constants = gas_constants,188molar_masses = molar_masses,189ion_ion_collision_constants = ion_ion_collision_constants,190ion_electron_collision_constants = ion_electron_collision_constants,191electron_pressure = electron_pressure,192electron_temperature = electron_temperature,193initial_c_h = c_h)194end195196@inline function Base.real(::IdealGlmMhdMultiIonEquations2D{NVARS, NCOMP, RealT}) where {197NVARS,198NCOMP,199RealT200}201return RealT202end203204"""205initial_condition_weak_blast_wave(x, t, equations::IdealGlmMhdMultiIonEquations2D)206207A weak blast wave (adapted to multi-ion MHD) from208- Hennemann, S., Rueda-Ramírez, A. M., Hindenlang, F. J., & Gassner, G. J. (2021). A provably entropy209stable subcell shock capturing approach for high order split form DG for the compressible Euler equations.210Journal of Computational Physics, 426, 109935. [arXiv: 2008.12044](https://arxiv.org/abs/2008.12044).211[DOI: 10.1016/j.jcp.2020.109935](https://doi.org/10.1016/j.jcp.2020.109935)212"""213function initial_condition_weak_blast_wave(x, t,214equations::IdealGlmMhdMultiIonEquations2D)215# Adapted MHD version of the weak blast wave from Hennemann & Gassner JCP paper 2020 (Sec. 6.3)216# Same discontinuity in the velocities but with magnetic fields217# Set up polar coordinates218RealT = eltype(x)219inicenter = (0, 0)220x_norm = x[1] - inicenter[1]221y_norm = x[2] - inicenter[2]222r = sqrt(x_norm^2 + y_norm^2)223phi = atan(y_norm, x_norm)224225# Calculate primitive variables226rho = r > 0.5f0 ? one(RealT) : convert(RealT, 1.1691)227v1 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos(phi)228v2 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * sin(phi)229p = r > 0.5f0 ? one(RealT) : convert(RealT, 1.245)230231prim = zero(MVector{nvariables(equations), RealT})232prim[1] = 1233prim[2] = 1234prim[3] = 1235for k in eachcomponent(equations)236# We initialize each species with a fraction of the total density `rho`, such237# that the sum of the densities is `rho := density(prim, equations)`. The density of238# a species is double the density of the next species.239fraction = 2^(k - 1) * (1 - 2) / (1 - 2^ncomponents(equations))240set_component!(prim, k,241fraction * rho, v1,242v2, 0, p, equations)243end244245return prim2cons(SVector(prim), equations)246end247248# 2D flux of the multi-ion GLM-MHD equations in the direction `orientation`249@inline function flux(u, orientation::Integer,250equations::IdealGlmMhdMultiIonEquations2D)251B1, B2, B3 = magnetic_field(u, equations)252psi = divergence_cleaning_field(u, equations)253254v1_plus, v2_plus, v3_plus, vk1_plus, vk2_plus, vk3_plus = charge_averaged_velocities(u,255equations)256257mag_en = 0.5f0 * (B1^2 + B2^2 + B3^2)258div_clean_energy = 0.5f0 * psi^2259260f = zero(MVector{nvariables(equations), eltype(u)})261262if orientation == 1263f[1] = equations.c_h * psi264f[2] = v1_plus * B2 - v2_plus * B1265f[3] = v1_plus * B3 - v3_plus * B1266267for k in eachcomponent(equations)268rho, rho_v1, rho_v2, rho_v3, rho_e_total = get_component(k, u, equations)269rho_inv = 1 / rho270v1 = rho_v1 * rho_inv271v2 = rho_v2 * rho_inv272v3 = rho_v3 * rho_inv273kin_en = 0.5f0 * rho * (v1^2 + v2^2 + v3^2)274275gamma = equations.gammas[k]276p = (gamma - 1) * (rho_e_total - kin_en - mag_en - div_clean_energy)277278f1 = rho_v1279f2 = rho_v1 * v1 + p280f3 = rho_v1 * v2281f4 = rho_v1 * v3282f5 = (kin_en + gamma * p / (gamma - 1)) * v1 + 2 * mag_en * vk1_plus[k] -283B1 * (vk1_plus[k] * B1 + vk2_plus[k] * B2 + vk3_plus[k] * B3) +284equations.c_h * psi * B1285286set_component!(f, k, f1, f2, f3, f4, f5, equations)287end288f[end] = equations.c_h * B1289else #if orientation == 2290f[1] = v2_plus * B1 - v1_plus * B2291f[2] = equations.c_h * psi292f[3] = v2_plus * B3 - v3_plus * B2293294for k in eachcomponent(equations)295rho, rho_v1, rho_v2, rho_v3, rho_e_total = get_component(k, u, equations)296rho_inv = 1 / rho297v1 = rho_v1 * rho_inv298v2 = rho_v2 * rho_inv299v3 = rho_v3 * rho_inv300kin_en = 0.5f0 * rho * (v1^2 + v2^2 + v3^2)301302gamma = equations.gammas[k]303p = (gamma - 1) * (rho_e_total - kin_en - mag_en - div_clean_energy)304305f1 = rho_v2306f2 = rho_v2 * v1307f3 = rho_v2 * v2 + p308f4 = rho_v2 * v3309f5 = (kin_en + gamma * p / (gamma - 1)) * v2 + 2 * mag_en * vk2_plus[k] -310B2 * (vk1_plus[k] * B1 + vk2_plus[k] * B2 + vk3_plus[k] * B3) +311equations.c_h * psi * B2312313set_component!(f, k, f1, f2, f3, f4, f5, equations)314end315f[end] = equations.c_h * B2316end317318return SVector(f)319end320321@inline function flux(u, normal_direction::AbstractVector,322equations::IdealGlmMhdMultiIonEquations2D)323B1, B2, B3 = magnetic_field(u, equations)324psi = divergence_cleaning_field(u, equations)325326v1_plus, v2_plus, v3_plus, vk1_plus, vk2_plus,327vk3_plus = charge_averaged_velocities(u,328equations)329330mag_en = 0.5f0 * (B1^2 + B2^2 + B3^2)331div_clean_energy = 0.5f0 * psi^2332333f = zero(MVector{nvariables(equations), eltype(u)})334335f[1] = equations.c_h * psi * normal_direction[1] +336(v2_plus * B1 - v1_plus * B2) * normal_direction[2]337f[2] = (v1_plus * B2 - v2_plus * B1) * normal_direction[1] +338equations.c_h * psi * normal_direction[2]339f[3] = (v1_plus * B3 - v3_plus * B1) * normal_direction[1] +340(v2_plus * B3 - v3_plus * B2) * normal_direction[2]341342for k in eachcomponent(equations)343rho, rho_v1, rho_v2, rho_v3, rho_e_total = get_component(k, u, equations)344rho_inv = 1 / rho345v1 = rho_v1 * rho_inv346v2 = rho_v2 * rho_inv347v3 = rho_v3 * rho_inv348kin_en = 0.5f0 * rho * (v1^2 + v2^2 + v3^2)349350gamma = equations.gammas[k]351p = (gamma - 1) * (rho_e_total - kin_en - mag_en - div_clean_energy)352353v_normal = v1 * normal_direction[1] + v2 * normal_direction[2]354rho_v_normal = rho * v_normal355356f1 = rho_v_normal357f2 = rho_v_normal * v1 + p * normal_direction[1]358f3 = rho_v_normal * v2 + p * normal_direction[2]359f4 = rho_v_normal * v3360f5 = ((kin_en + gamma * p / (gamma - 1)) * v1 + 2 * mag_en * vk1_plus[k] -361B1 * (vk1_plus[k] * B1 + vk2_plus[k] * B2 + vk3_plus[k] * B3) +362equations.c_h * psi * B1) * normal_direction[1] +363((kin_en + gamma * p / (gamma - 1)) * v2 + 2 * mag_en * vk2_plus[k] -364B2 * (vk1_plus[k] * B1 + vk2_plus[k] * B2 + vk3_plus[k] * B3) +365equations.c_h * psi * B2) * normal_direction[2]366367set_component!(f, k, f1, f2, f3, f4, f5, equations)368end369f[end] = equations.c_h * (B1 * normal_direction[1] + B2 * normal_direction[2])370371return SVector(f)372end373374"""375flux_nonconservative_ruedaramirez_etal(u_ll, u_rr,376orientation_or_normal_direction,377equations::IdealGlmMhdMultiIonEquations2D)378379Entropy-conserving non-conservative two-point "flux" as described in380- A. Rueda-Ramírez, A. Sikstel, G. Gassner, An Entropy-Stable Discontinuous Galerkin Discretization381of the Ideal Multi-Ion Magnetohydrodynamics System (2024). Journal of Computational Physics.382[DOI: 10.1016/j.jcp.2024.113655](https://doi.org/10.1016/j.jcp.2024.113655).383384!!! info "Usage and Scaling of Non-Conservative Fluxes in Trixi.jl"385The non-conservative fluxes derived in the reference above are written as the product386of local and symmetric parts and are meant to be used in the same way as the conservative387fluxes (i.e., flux + flux_noncons in both volume and surface integrals). In this routine,388the fluxes are multiplied by 2 because the non-conservative fluxes are always multiplied389by 0.5 whenever they are used in the Trixi code.390391The term is composed of four individual non-conservative terms:3921. The Godunov-Powell term, which arises for plasmas with non-vanishing magnetic field divergence, and393is evaluated as a function of the charge-averaged velocity.3942. The Lorentz-force term, which becomes a conservative term in the limit of one ion species for vanishing395electron pressure gradients.3963. The "multi-ion" term, which vanishes in the limit of one ion species.3974. The GLM term, which is needed for Galilean invariance.398"""399@inline function flux_nonconservative_ruedaramirez_etal(u_ll, u_rr,400orientation::Integer,401equations::IdealGlmMhdMultiIonEquations2D)402@unpack charge_to_mass = equations403# Unpack left and right states to get the magnetic field404B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)405B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)406psi_ll = divergence_cleaning_field(u_ll, equations)407psi_rr = divergence_cleaning_field(u_rr, equations)408409# Compute important averages410B1_avg = 0.5f0 * (B1_ll + B1_rr)411B2_avg = 0.5f0 * (B2_ll + B2_rr)412B3_avg = 0.5f0 * (B3_ll + B3_rr)413mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2414mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2415mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)416psi_avg = 0.5f0 * (psi_ll + psi_rr)417418# Mean electron pressure419pe_ll = equations.electron_pressure(u_ll, equations)420pe_rr = equations.electron_pressure(u_rr, equations)421pe_mean = 0.5f0 * (pe_ll + pe_rr)422423# Compute charge ratio of u_ll424charge_ratio_ll = zero(MVector{ncomponents(equations), eltype(u_ll)})425total_electron_charge = zero(eltype(u_ll))426for k in eachcomponent(equations)427rho_k = u_ll[3 + (k - 1) * 5 + 1]428charge_ratio_ll[k] = rho_k * charge_to_mass[k]429total_electron_charge += charge_ratio_ll[k]430end431charge_ratio_ll ./= total_electron_charge432433# Compute auxiliary variables434v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll,435vk3_plus_ll = charge_averaged_velocities(u_ll,436equations)437v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr,438vk3_plus_rr = charge_averaged_velocities(u_rr,439equations)440441f = zero(MVector{nvariables(equations), eltype(u_ll)})442443if orientation == 1444# Entries of Godunov-Powell term for induction equation (multiply by 2 because the non-conservative flux is445# multiplied by 0.5 whenever it's used in the Trixi code)446f[1] = 2 * v1_plus_ll * B1_avg447f[2] = 2 * v2_plus_ll * B1_avg448f[3] = 2 * v3_plus_ll * B1_avg449450for k in eachcomponent(equations)451# Compute term Lorentz term452f2 = charge_ratio_ll[k] * (0.5f0 * mag_norm_avg - B1_avg * B1_avg + pe_mean)453f3 = charge_ratio_ll[k] * (-B1_avg * B2_avg)454f4 = charge_ratio_ll[k] * (-B1_avg * B3_avg)455f5 = vk1_plus_ll[k] * pe_mean456457# Compute multi-ion term (vanishes for NCOMP==1)458vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]459vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]460vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]461vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]462vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]463vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]464vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)465vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)466vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)467f5 += (B2_ll * (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) +468B3_ll * (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg))469470# Compute Godunov-Powell term471f2 += charge_ratio_ll[k] * B1_ll * B1_avg472f3 += charge_ratio_ll[k] * B2_ll * B1_avg473f4 += charge_ratio_ll[k] * B3_ll * B1_avg474f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *475B1_avg476477# Compute GLM term for the energy478f5 += v1_plus_ll * psi_ll * psi_avg479480# Add to the flux vector (multiply by 2 because the non-conservative flux is481# multiplied by 0.5 whenever it's used in the Trixi code)482set_component!(f, k, 0, 2 * f2, 2 * f3, 2 * f4, 2 * f5,483equations)484end485# Compute GLM term for psi (multiply by 2 because the non-conservative flux is486# multiplied by 0.5 whenever it's used in the Trixi code)487f[end] = 2 * v1_plus_ll * psi_avg488489else #if orientation == 2490# Entries of Godunov-Powell term for induction equation (multiply by 2 because the non-conservative flux is491# multiplied by 0.5 whenever it's used in the Trixi code)492f[1] = 2 * v1_plus_ll * B2_avg493f[2] = 2 * v2_plus_ll * B2_avg494f[3] = 2 * v3_plus_ll * B2_avg495496for k in eachcomponent(equations)497# Compute term Lorentz term498f2 = charge_ratio_ll[k] * (-B2_avg * B1_avg)499f3 = charge_ratio_ll[k] *500(-B2_avg * B2_avg + 0.5f0 * mag_norm_avg + pe_mean)501f4 = charge_ratio_ll[k] * (-B2_avg * B3_avg)502f5 = vk2_plus_ll[k] * pe_mean503504# Compute multi-ion term (vanishes for NCOMP==1)505vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]506vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]507vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]508vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]509vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]510vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]511vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)512vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)513vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)514f5 += (B1_ll * (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) +515B3_ll * (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg))516517# Compute Godunov-Powell term518f2 += charge_ratio_ll[k] * B1_ll * B2_avg519f3 += charge_ratio_ll[k] * B2_ll * B2_avg520f4 += charge_ratio_ll[k] * B3_ll * B2_avg521f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *522B2_avg523524# Compute GLM term for the energy525f5 += v2_plus_ll * psi_ll * psi_avg526527# Add to the flux vector (multiply by 2 because the non-conservative flux is528# multiplied by 0.5 whenever it's used in the Trixi code)529set_component!(f, k, 0, 2 * f2, 2 * f3, 2 * f4, 2 * f5,530equations)531end532# Compute GLM term for psi (multiply by 2 because the non-conservative flux is533# multiplied by 0.5 whenever it's used in the Trixi code)534f[end] = 2 * v2_plus_ll * psi_avg535end536537return SVector(f)538end539540@inline function flux_nonconservative_ruedaramirez_etal(u_ll, u_rr,541normal_direction::AbstractVector,542equations::IdealGlmMhdMultiIonEquations2D)543@unpack charge_to_mass = equations544# Unpack left and right states to get the magnetic field545B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)546B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)547psi_ll = divergence_cleaning_field(u_ll, equations)548psi_rr = divergence_cleaning_field(u_rr, equations)549B_dot_n_ll = B1_ll * normal_direction[1] +550B2_ll * normal_direction[2]551B_dot_n_rr = B1_rr * normal_direction[1] +552B2_rr * normal_direction[2]553B_dot_n_avg = 0.5f0 * (B_dot_n_ll + B_dot_n_rr)554555# Compute important averages556B1_avg = 0.5f0 * (B1_ll + B1_rr)557B2_avg = 0.5f0 * (B2_ll + B2_rr)558B3_avg = 0.5f0 * (B3_ll + B3_rr)559mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2560mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2561mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)562psi_avg = 0.5f0 * (psi_ll + psi_rr)563564# Mean electron pressure565pe_ll = equations.electron_pressure(u_ll, equations)566pe_rr = equations.electron_pressure(u_rr, equations)567pe_mean = 0.5f0 * (pe_ll + pe_rr)568569# Compute charge ratio of u_ll570charge_ratio_ll = zero(MVector{ncomponents(equations), eltype(u_ll)})571total_electron_charge = zero(eltype(u_ll))572for k in eachcomponent(equations)573rho_k = u_ll[3 + (k - 1) * 5 + 1] # Extract densities from conserved variable vector574charge_ratio_ll[k] = rho_k * charge_to_mass[k]575total_electron_charge += charge_ratio_ll[k]576end577charge_ratio_ll ./= total_electron_charge578579# Compute auxiliary variables580v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll,581vk3_plus_ll = charge_averaged_velocities(u_ll,582equations)583v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr,584vk3_plus_rr = charge_averaged_velocities(u_rr,585equations)586v_plus_dot_n_ll = (v1_plus_ll * normal_direction[1] +587v2_plus_ll * normal_direction[2])588f = zero(MVector{nvariables(equations), eltype(u_ll)})589590# Entries of Godunov-Powell term for induction equation (multiply by 2 because the non-conservative flux is591# multiplied by 0.5 whenever it's used in the Trixi code)592f[1] = 2 * v1_plus_ll * B_dot_n_avg593f[2] = 2 * v2_plus_ll * B_dot_n_avg594f[3] = 2 * v3_plus_ll * B_dot_n_avg595596for k in eachcomponent(equations)597# Compute term Lorentz term598f2 = charge_ratio_ll[k] *599((0.5f0 * mag_norm_avg + pe_mean) * normal_direction[1] -600B_dot_n_avg * B1_avg)601f3 = charge_ratio_ll[k] *602((0.5f0 * mag_norm_avg + pe_mean) * normal_direction[2] -603B_dot_n_avg * B2_avg)604f4 = charge_ratio_ll[k] *605(-B_dot_n_avg * B3_avg)606f5 = (vk1_plus_ll[k] * normal_direction[1] +607vk2_plus_ll[k] * normal_direction[2]) * pe_mean608609# Compute multi-ion term (vanishes for NCOMP==1)610vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]611vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]612vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]613vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]614vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]615vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]616vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)617vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)618vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)619f5 += ((B2_ll * (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) +620B3_ll * (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg)) *621normal_direction[1] +622(B1_ll * (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) +623B3_ll * (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg)) *624normal_direction[2])625626# Compute Godunov-Powell term627f2 += charge_ratio_ll[k] * B1_ll * B_dot_n_avg628f3 += charge_ratio_ll[k] * B2_ll * B_dot_n_avg629f4 += charge_ratio_ll[k] * B3_ll * B_dot_n_avg630f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *631B_dot_n_avg632633# Compute GLM term for the energy634f5 += v_plus_dot_n_ll * psi_ll * psi_avg635636# Add to the flux vector (multiply by 2 because the non-conservative flux is637# multiplied by 0.5 whenever it's used in the Trixi code)638set_component!(f, k, 0, 2 * f2, 2 * f3, 2 * f4, 2 * f5,639equations)640end641# Compute GLM term for psi (multiply by 2 because the non-conservative flux is642# multiplied by 0.5 whenever it's used in the Trixi code)643f[end] = 2 * v_plus_dot_n_ll * psi_avg644645return SVector(f)646end647648"""649flux_nonconservative_central(u_ll, u_rr, orientation::Integer,650equations::IdealGlmMhdMultiIonEquations2D)651652Central non-conservative two-point "flux", where the symmetric parts are computed with standard averages.653The use of this term together with [`flux_central`](@ref)654with [`VolumeIntegralFluxDifferencing`](@ref) yields a "standard"655(weak-form) DGSEM discretization of the multi-ion GLM-MHD system. This flux can also be used to construct a656standard local Lax-Friedrichs flux using `surface_flux = (flux_lax_friedrichs, flux_nonconservative_central)`.657658!!! info "Usage and Scaling of Non-Conservative Fluxes in Trixi"659The central non-conservative fluxes implemented in this function are written as the product660of local and symmetric parts, where the symmetric part is a standard average. These fluxes661are meant to be used in the same way as the conservative fluxes (i.e., flux + flux_noncons662in both volume and surface integrals). In this routine, the fluxes are multiplied by 2 because663the non-conservative fluxes are always multiplied by 0.5 whenever they are used in the Trixi code.664665The term is composed of four individual non-conservative terms:6661. The Godunov-Powell term, which arises for plasmas with non-vanishing magnetic field divergence, and667is evaluated as a function of the charge-averaged velocity.6682. The Lorentz-force term, which becomes a conservative term in the limit of one ion species for vanishing669electron pressure gradients.6703. The "multi-ion" term, which vanishes in the limit of one ion species.6714. The GLM term, which is needed for Galilean invariance.672"""673@inline function flux_nonconservative_central(u_ll, u_rr, orientation::Integer,674equations::IdealGlmMhdMultiIonEquations2D)675@unpack charge_to_mass = equations676# Unpack left and right states to get the magnetic field677B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)678B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)679psi_ll = divergence_cleaning_field(u_ll, equations)680psi_rr = divergence_cleaning_field(u_rr, equations)681682# Compute important averages683mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2684mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2685686# Electron pressure687pe_ll = equations.electron_pressure(u_ll, equations)688pe_rr = equations.electron_pressure(u_rr, equations)689690# Compute charge ratio of u_ll691charge_ratio_ll = zero(MVector{ncomponents(equations), eltype(u_ll)})692total_electron_charge = zero(real(equations))693for k in eachcomponent(equations)694rho_k = u_ll[3 + (k - 1) * 5 + 1]695charge_ratio_ll[k] = rho_k * charge_to_mass[k]696total_electron_charge += charge_ratio_ll[k]697end698charge_ratio_ll ./= total_electron_charge699700# Compute auxiliary variables701v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll,702vk3_plus_ll = charge_averaged_velocities(u_ll,703equations)704v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr,705vk3_plus_rr = charge_averaged_velocities(u_rr,706equations)707708f = zero(MVector{nvariables(equations), eltype(u_ll)})709710if orientation == 1711# Entries of Godunov-Powell term for induction equation712f[1] = v1_plus_ll * (B1_ll + B1_rr)713f[2] = v2_plus_ll * (B1_ll + B1_rr)714f[3] = v3_plus_ll * (B1_ll + B1_rr)715for k in eachcomponent(equations)716# Compute Lorentz term717f2 = charge_ratio_ll[k] * ((0.5f0 * mag_norm_ll - B1_ll * B1_ll + pe_ll) +718(0.5f0 * mag_norm_rr - B1_rr * B1_rr + pe_rr))719f3 = charge_ratio_ll[k] * ((-B1_ll * B2_ll) + (-B1_rr * B2_rr))720f4 = charge_ratio_ll[k] * ((-B1_ll * B3_ll) + (-B1_rr * B3_rr))721f5 = vk1_plus_ll[k] * (pe_ll + pe_rr)722723# Compute multi-ion term, which vanishes for NCOMP==1724vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]725vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]726vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]727vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]728vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]729vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]730f5 += (B2_ll * ((vk1_minus_ll * B2_ll - vk2_minus_ll * B1_ll) +731(vk1_minus_rr * B2_rr - vk2_minus_rr * B1_rr)) +732B3_ll * ((vk1_minus_ll * B3_ll - vk3_minus_ll * B1_ll) +733(vk1_minus_rr * B3_rr - vk3_minus_rr * B1_rr)))734735# Compute Godunov-Powell term736f2 += charge_ratio_ll[k] * B1_ll * (B1_ll + B1_rr)737f3 += charge_ratio_ll[k] * B2_ll * (B1_ll + B1_rr)738f4 += charge_ratio_ll[k] * B3_ll * (B1_ll + B1_rr)739f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *740(B1_ll + B1_rr)741742# Compute GLM term for the energy743f5 += v1_plus_ll * psi_ll * (psi_ll + psi_rr)744745# Append to the flux vector746set_component!(f, k, 0, f2, f3, f4, f5, equations)747end748# Compute GLM term for psi749f[end] = v1_plus_ll * (psi_ll + psi_rr)750751else #if orientation == 2752# Entries of Godunov-Powell term for induction equation753f[1] = v1_plus_ll * (B2_ll + B2_rr)754f[2] = v2_plus_ll * (B2_ll + B2_rr)755f[3] = v3_plus_ll * (B2_ll + B2_rr)756757for k in eachcomponent(equations)758# Compute Lorentz term759f2 = charge_ratio_ll[k] * ((-B2_ll * B1_ll) + (-B2_rr * B1_rr))760f3 = charge_ratio_ll[k] * ((-B2_ll * B2_ll + 0.5f0 * mag_norm_ll + pe_ll) +761(-B2_rr * B2_rr + 0.5f0 * mag_norm_rr + pe_rr))762f4 = charge_ratio_ll[k] * ((-B2_ll * B3_ll) + (-B2_rr * B3_rr))763f5 = vk2_plus_ll[k] * (pe_ll + pe_rr)764765# Compute multi-ion term (vanishes for NCOMP==1)766vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]767vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]768vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]769vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]770vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]771vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]772f5 += (B1_ll * ((vk2_minus_ll * B1_ll - vk1_minus_ll * B2_ll) +773(vk2_minus_rr * B1_rr - vk1_minus_rr * B2_rr)) +774B3_ll * ((vk2_minus_ll * B3_ll - vk3_minus_ll * B2_ll) +775(vk2_minus_rr * B3_rr - vk3_minus_rr * B2_rr)))776777# Compute Godunov-Powell term778f2 += charge_ratio_ll[k] * B1_ll * (B2_ll + B2_rr)779f3 += charge_ratio_ll[k] * B2_ll * (B2_ll + B2_rr)780f4 += charge_ratio_ll[k] * B3_ll * (B2_ll + B2_rr)781f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *782(B2_ll + B2_rr)783784# Compute GLM term for the energy785f5 += v2_plus_ll * psi_ll * (psi_ll + psi_rr)786787# Append to the flux vector788set_component!(f, k, 0, f2, f3, f4, f5, equations)789end790# Compute GLM term for psi791f[end] = v2_plus_ll * (psi_ll + psi_rr)792end793794return SVector(f)795end796797@inline function flux_nonconservative_central(u_ll, u_rr,798normal_direction::AbstractVector,799equations::IdealGlmMhdMultiIonEquations2D)800@unpack charge_to_mass = equations801# Unpack left and right states to get the magnetic field802B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)803B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)804psi_ll = divergence_cleaning_field(u_ll, equations)805psi_rr = divergence_cleaning_field(u_rr, equations)806807# Compute important averages808mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2809mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2810811# Electron pressure812pe_ll = equations.electron_pressure(u_ll, equations)813pe_rr = equations.electron_pressure(u_rr, equations)814815# Compute charge ratio of u_ll816charge_ratio_ll = zero(MVector{ncomponents(equations), eltype(u_ll)})817total_electron_charge = zero(real(equations))818for k in eachcomponent(equations)819rho_k = u_ll[3 + (k - 1) * 5 + 1]820charge_ratio_ll[k] = rho_k * charge_to_mass[k]821total_electron_charge += charge_ratio_ll[k]822end823charge_ratio_ll ./= total_electron_charge824825# Compute auxiliary variables826v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll,827vk3_plus_ll = charge_averaged_velocities(u_ll,828equations)829v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr,830vk3_plus_rr = charge_averaged_velocities(u_rr,831equations)832833f = zero(MVector{nvariables(equations), eltype(u_ll)})834835# Compute B_dot_n, v_dot_B_ll, and psi terms once (they are constant for all species)836B1_sum = B1_ll + B1_rr837B2_sum = B2_ll + B2_rr838B_dot_n = B1_sum * normal_direction[1] + B2_sum * normal_direction[2]839v_dot_B_ll = v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll840psi_sum = psi_ll + psi_rr841842# Entries of Godunov-Powell term for induction equation843f[1] = v1_plus_ll * B_dot_n844f[2] = v2_plus_ll * B_dot_n845f[3] = v3_plus_ll * B_dot_n846847for k in eachcomponent(equations)848# Compute Lorentz term849f2 = charge_ratio_ll[k] *850((0.5f0 * mag_norm_ll - B1_ll * B1_ll + pe_ll) +851(0.5f0 * mag_norm_rr - B1_rr * B1_rr + pe_rr)) * normal_direction[1] +852charge_ratio_ll[k] * ((-B2_ll * B1_ll) + (-B2_rr * B1_rr)) *853normal_direction[2]854f3 = charge_ratio_ll[k] * ((-B1_ll * B2_ll) + (-B1_rr * B2_rr)) *855normal_direction[1] +856charge_ratio_ll[k] *857((-B2_ll * B2_ll + 0.5f0 * mag_norm_ll + pe_ll) +858(-B2_rr * B2_rr + 0.5f0 * mag_norm_rr + pe_rr)) * normal_direction[2]859f4 = charge_ratio_ll[k] * ((-B1_ll * B3_ll) + (-B1_rr * B3_rr)) *860normal_direction[1] +861charge_ratio_ll[k] * ((-B2_ll * B3_ll) + (-B2_rr * B3_rr)) *862normal_direction[2]863f5 = vk1_plus_ll[k] * (pe_ll + pe_rr) * normal_direction[1] +864vk2_plus_ll[k] * (pe_ll + pe_rr) * normal_direction[2]865866# Compute multi-ion term, which vanishes for NCOMP==1867vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]868vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]869vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]870vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]871vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]872vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]873f5 += (B2_ll * ((vk1_minus_ll * B2_ll - vk2_minus_ll * B1_ll) +874(vk1_minus_rr * B2_rr - vk2_minus_rr * B1_rr)) +875B3_ll * ((vk1_minus_ll * B3_ll - vk3_minus_ll * B1_ll) +876(vk1_minus_rr * B3_rr - vk3_minus_rr * B1_rr))) * normal_direction[1]877f5 += (B1_ll * ((vk2_minus_ll * B1_ll - vk1_minus_ll * B2_ll) +878(vk2_minus_rr * B1_rr - vk1_minus_rr * B2_rr)) +879B3_ll * ((vk2_minus_ll * B3_ll - vk3_minus_ll * B2_ll) +880(vk2_minus_rr * B3_rr - vk3_minus_rr * B2_rr))) * normal_direction[2]881882# Compute Godunov-Powell term883f2 += charge_ratio_ll[k] * B1_ll * B_dot_n884f3 += charge_ratio_ll[k] * B2_ll * B_dot_n885f4 += charge_ratio_ll[k] * B3_ll * B_dot_n886f5 += v_dot_B_ll * B_dot_n887888# Compute GLM term for the energy889f5 += (v1_plus_ll * normal_direction[1] + v2_plus_ll * normal_direction[2]) *890psi_ll * psi_sum891892# Append to the flux vector893set_component!(f, k, 0, f2, f3, f4, f5, equations)894end895# Compute GLM term for psi896f[end] = (v1_plus_ll * normal_direction[1] + v2_plus_ll * normal_direction[2]) *897psi_sum898899return SVector(f)900end901902"""903flux_ruedaramirez_etal(u_ll, u_rr, orientation, equations::IdealGlmMhdMultiIonEquations2D)904905Entropy conserving two-point flux for the multi-ion GLM-MHD equations from906- A. Rueda-Ramírez, A. Sikstel, G. Gassner, An Entropy-Stable Discontinuous Galerkin Discretization907of the Ideal Multi-Ion Magnetohydrodynamics System (2024). Journal of Computational Physics.908[DOI: 10.1016/j.jcp.2024.113655](https://doi.org/10.1016/j.jcp.2024.113655).909910This flux (together with the MHD non-conservative term) is consistent in the case of one ion species with the flux of:911- Derigs et al. (2018). Ideal GLM-MHD: About the entropy consistent nine-wave magnetic field912divergence diminishing ideal magnetohydrodynamics equations for multi-ion913[DOI: 10.1016/j.jcp.2018.03.002](https://doi.org/10.1016/j.jcp.2018.03.002)914"""915function flux_ruedaramirez_etal(u_ll, u_rr, orientation::Integer,916equations::IdealGlmMhdMultiIonEquations2D)917@unpack gammas = equations918# Unpack left and right states to get the magnetic field919B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)920B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)921psi_ll = divergence_cleaning_field(u_ll, equations)922psi_rr = divergence_cleaning_field(u_rr, equations)923924v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll,925vk3_plus_ll = charge_averaged_velocities(u_ll,926equations)927v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr,928vk3_plus_rr = charge_averaged_velocities(u_rr,929equations)930931f = zero(MVector{nvariables(equations), eltype(u_ll)})932933# Compute averages for global variables934v1_plus_avg = 0.5f0 * (v1_plus_ll + v1_plus_rr)935v2_plus_avg = 0.5f0 * (v2_plus_ll + v2_plus_rr)936v3_plus_avg = 0.5f0 * (v3_plus_ll + v3_plus_rr)937B1_avg = 0.5f0 * (B1_ll + B1_rr)938B2_avg = 0.5f0 * (B2_ll + B2_rr)939B3_avg = 0.5f0 * (B3_ll + B3_rr)940mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2941mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2942mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)943psi_avg = 0.5f0 * (psi_ll + psi_rr)944945if orientation == 1946psi_B1_avg = 0.5f0 * (B1_ll * psi_ll + B1_rr * psi_rr)947948# Magnetic field components from f^MHD949f6 = equations.c_h * psi_avg950f7 = v1_plus_avg * B2_avg - v2_plus_avg * B1_avg951f8 = v1_plus_avg * B3_avg - v3_plus_avg * B1_avg952f9 = equations.c_h * B1_avg953954# Start building the flux955f[1] = f6956f[2] = f7957f[3] = f8958f[end] = f9959960# Iterate over all components961for k in eachcomponent(equations)962# Unpack left and right states963rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll = get_component(k,964u_ll,965equations)966rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr = get_component(k,967u_rr,968equations)969rho_inv_ll = 1 / rho_ll970v1_ll = rho_v1_ll * rho_inv_ll971v2_ll = rho_v2_ll * rho_inv_ll972v3_ll = rho_v3_ll * rho_inv_ll973rho_inv_rr = 1 / rho_rr974v1_rr = rho_v1_rr * rho_inv_rr975v2_rr = rho_v2_rr * rho_inv_rr976v3_rr = rho_v3_rr * rho_inv_rr977vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^2978vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^2979980p_ll = (gammas[k] - 1) *981(rho_e_total_ll - 0.5f0 * rho_ll * vel_norm_ll -9820.5f0 * mag_norm_ll -9830.5f0 * psi_ll^2)984p_rr = (gammas[k] - 1) *985(rho_e_total_rr - 0.5f0 * rho_rr * vel_norm_rr -9860.5f0 * mag_norm_rr -9870.5f0 * psi_rr^2)988beta_ll = 0.5f0 * rho_ll / p_ll989beta_rr = 0.5f0 * rho_rr / p_rr990# for convenience store vk_plus⋅B991vel_dot_mag_ll = vk1_plus_ll[k] * B1_ll + vk2_plus_ll[k] * B2_ll +992vk3_plus_ll[k] * B3_ll993vel_dot_mag_rr = vk1_plus_rr[k] * B1_rr + vk2_plus_rr[k] * B2_rr +994vk3_plus_rr[k] * B3_rr995996# Compute the necessary mean values needed for either direction997rho_avg = 0.5f0 * (rho_ll + rho_rr)998rho_mean = ln_mean(rho_ll, rho_rr)999beta_mean = ln_mean(beta_ll, beta_rr)1000beta_avg = 0.5f0 * (beta_ll + beta_rr)1001p_mean = 0.5f0 * rho_avg / beta_avg1002v1_avg = 0.5f0 * (v1_ll + v1_rr)1003v2_avg = 0.5f0 * (v2_ll + v2_rr)1004v3_avg = 0.5f0 * (v3_ll + v3_rr)1005vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)1006vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)1007vk1_plus_avg = 0.5f0 * (vk1_plus_ll[k] + vk1_plus_rr[k])1008vk2_plus_avg = 0.5f0 * (vk2_plus_ll[k] + vk2_plus_rr[k])1009vk3_plus_avg = 0.5f0 * (vk3_plus_ll[k] + vk3_plus_rr[k])1010# v_minus1011vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]1012vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]1013vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]1014vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]1015vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]1016vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]1017vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)1018vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)1019vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)10201021# Fill the fluxes for the mass and momentum equations1022f1 = rho_mean * v1_avg1023f2 = f1 * v1_avg + p_mean1024f3 = f1 * v2_avg1025f4 = f1 * v3_avg10261027# total energy flux is complicated and involves the previous eight components1028v1_plus_mag_avg = 0.5f0 * (vk1_plus_ll[k] * mag_norm_ll +1029vk1_plus_rr[k] * mag_norm_rr)1030# Euler part1031f5 = f1 * 0.5f0 * (1 / (gammas[k] - 1) / beta_mean - vel_norm_avg) +1032f2 * v1_avg + f3 * v2_avg + f4 * v3_avg1033# MHD part1034f5 += (f6 * B1_avg + f7 * B2_avg + f8 * B3_avg - 0.5f0 * v1_plus_mag_avg +1035B1_avg * vel_dot_mag_avg # Same terms as in Derigs (but with v_plus)1036+ f9 * psi_avg - equations.c_h * psi_B1_avg # GLM term1037+10380.5f0 * vk1_plus_avg * mag_norm_avg -1039vk1_plus_avg * B1_avg * B1_avg - vk2_plus_avg * B1_avg * B2_avg -1040vk3_plus_avg * B1_avg * B3_avg # Additional terms related to the Lorentz non-conservative term (momentum eqs)1041-1042B2_avg * (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) -1043B3_avg * (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg)) # Terms related to the multi-ion non-conservative term (induction equation!)10441045set_component!(f, k, f1, f2, f3, f4, f5, equations)1046end1047else #if orientation == 21048psi_B2_avg = 0.5f0 * (B2_ll * psi_ll + B2_rr * psi_rr)10491050# Magnetic field components from f^MHD1051f6 = v2_plus_avg * B1_avg - v1_plus_avg * B2_avg1052f7 = equations.c_h * psi_avg1053f8 = v2_plus_avg * B3_avg - v3_plus_avg * B2_avg1054f9 = equations.c_h * B2_avg10551056# Start building the flux1057f[1] = f61058f[2] = f71059f[3] = f81060f[end] = f910611062# Iterate over all components1063for k in eachcomponent(equations)1064# Unpack left and right states1065rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll = get_component(k,1066u_ll,1067equations)1068rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr = get_component(k,1069u_rr,1070equations)10711072rho_inv_ll = 1 / rho_ll1073v1_ll = rho_v1_ll * rho_inv_ll1074v2_ll = rho_v2_ll * rho_inv_ll1075v3_ll = rho_v3_ll * rho_inv_ll1076rho_inv_rr = 1 / rho_rr1077v1_rr = rho_v1_rr * rho_inv_rr1078v2_rr = rho_v2_rr * rho_inv_rr1079v3_rr = rho_v3_rr * rho_inv_rr1080vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^21081vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^210821083p_ll = (gammas[k] - 1) *1084(rho_e_total_ll - 0.5f0 * rho_ll * vel_norm_ll -10850.5f0 * mag_norm_ll -10860.5f0 * psi_ll^2)1087p_rr = (gammas[k] - 1) *1088(rho_e_total_rr - 0.5f0 * rho_rr * vel_norm_rr -10890.5f0 * mag_norm_rr -10900.5f0 * psi_rr^2)1091beta_ll = 0.5f0 * rho_ll / p_ll1092beta_rr = 0.5f0 * rho_rr / p_rr1093# for convenience store vk_plus⋅B1094vel_dot_mag_ll = vk1_plus_ll[k] * B1_ll + vk2_plus_ll[k] * B2_ll +1095vk3_plus_ll[k] * B3_ll1096vel_dot_mag_rr = vk1_plus_rr[k] * B1_rr + vk2_plus_rr[k] * B2_rr +1097vk3_plus_rr[k] * B3_rr10981099# Compute the necessary mean values needed for either direction1100rho_avg = 0.5f0 * (rho_ll + rho_rr)1101rho_mean = ln_mean(rho_ll, rho_rr)1102beta_mean = ln_mean(beta_ll, beta_rr)1103beta_avg = 0.5f0 * (beta_ll + beta_rr)1104p_mean = 0.5f0 * rho_avg / beta_avg1105v1_avg = 0.5f0 * (v1_ll + v1_rr)1106v2_avg = 0.5f0 * (v2_ll + v2_rr)1107v3_avg = 0.5f0 * (v3_ll + v3_rr)1108vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)1109vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)1110vk1_plus_avg = 0.5f0 * (vk1_plus_ll[k] + vk1_plus_rr[k])1111vk2_plus_avg = 0.5f0 * (vk2_plus_ll[k] + vk2_plus_rr[k])1112vk3_plus_avg = 0.5f0 * (vk3_plus_ll[k] + vk3_plus_rr[k])1113# v_minus1114vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]1115vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]1116vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]1117vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]1118vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]1119vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]1120vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)1121vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)1122vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)11231124# Fill the fluxes for the mass and momentum equations1125f1 = rho_mean * v2_avg1126f2 = f1 * v1_avg1127f3 = f1 * v2_avg + p_mean1128f4 = f1 * v3_avg11291130# total energy flux is complicated and involves the previous eight components1131v2_plus_mag_avg = 0.5f0 * (vk2_plus_ll[k] * mag_norm_ll +1132vk2_plus_rr[k] * mag_norm_rr)1133# Euler part1134f5 = f1 * 0.5f0 * (1 / (gammas[k] - 1) / beta_mean - vel_norm_avg) +1135f2 * v1_avg + f3 * v2_avg + f4 * v3_avg1136# MHD part1137f5 += (f6 * B1_avg + f7 * B2_avg + f8 * B3_avg - 0.5f0 * v2_plus_mag_avg +1138B2_avg * vel_dot_mag_avg # Same terms as in Derigs (but with v_plus)1139+ f9 * psi_avg - equations.c_h * psi_B2_avg # GLM term1140+11410.5f0 * vk2_plus_avg * mag_norm_avg -1142vk1_plus_avg * B2_avg * B1_avg - vk2_plus_avg * B2_avg * B2_avg -1143vk3_plus_avg * B2_avg * B3_avg # Additional terms related to the Lorentz non-conservative term (momentum eqs)1144-1145B1_avg * (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) -1146B3_avg * (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg)) # Terms related to the multi-ion non-conservative term (induction equation!)11471148set_component!(f, k, f1, f2, f3, f4, f5, equations)1149end1150end11511152return SVector(f)1153end11541155function flux_ruedaramirez_etal(u_ll, u_rr, normal_direction::AbstractVector,1156equations::IdealGlmMhdMultiIonEquations2D)1157@unpack gammas = equations1158# Unpack left and right states to get the magnetic field1159B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)1160B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)1161psi_ll = divergence_cleaning_field(u_ll, equations)1162psi_rr = divergence_cleaning_field(u_rr, equations)11631164v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll,1165vk3_plus_ll = charge_averaged_velocities(u_ll,1166equations)1167v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr,1168vk3_plus_rr = charge_averaged_velocities(u_rr,1169equations)11701171f = zero(MVector{nvariables(equations), eltype(u_ll)})11721173# Compute averages for global variables1174v1_plus_avg = 0.5f0 * (v1_plus_ll + v1_plus_rr)1175v2_plus_avg = 0.5f0 * (v2_plus_ll + v2_plus_rr)1176v3_plus_avg = 0.5f0 * (v3_plus_ll + v3_plus_rr)1177B1_avg = 0.5f0 * (B1_ll + B1_rr)1178B2_avg = 0.5f0 * (B2_ll + B2_rr)1179B3_avg = 0.5f0 * (B3_ll + B3_rr)1180mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^21181mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^21182mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)1183psi_avg = 0.5f0 * (psi_ll + psi_rr)11841185psi_B1_avg = 0.5f0 * (B1_ll * psi_ll + B1_rr * psi_rr)1186psi_B2_avg = 0.5f0 * (B2_ll * psi_ll + B2_rr * psi_rr)11871188# Compute B_dot_n_avg and psi_B_dot_n_avg once (they are constant for all species)1189B_dot_n_avg = B1_avg * normal_direction[1] + B2_avg * normal_direction[2]1190psi_B_dot_n_avg = psi_B1_avg * normal_direction[1] +1191psi_B2_avg * normal_direction[2]11921193# Magnetic field components from f^MHD1194f6 = (equations.c_h * psi_avg * normal_direction[1] +1195(v2_plus_avg * B1_avg - v1_plus_avg * B2_avg) * normal_direction[2])1196f7 = ((v1_plus_avg * B2_avg - v2_plus_avg * B1_avg) * normal_direction[1] +1197equations.c_h * psi_avg * normal_direction[2])1198f8 = ((v1_plus_avg * B3_avg - v3_plus_avg * B1_avg) * normal_direction[1] +1199(v2_plus_avg * B3_avg - v3_plus_avg * B2_avg) * normal_direction[2])1200f9 = (equations.c_h * B1_avg * normal_direction[1] +1201equations.c_h * B2_avg * normal_direction[2])12021203# Start building the flux1204f[1] = f61205f[2] = f71206f[3] = f81207f[end] = f912081209# Iterate over all components1210for k in eachcomponent(equations)1211# Unpack left and right states1212rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll = get_component(k, u_ll,1213equations)1214rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr = get_component(k, u_rr,1215equations)1216rho_inv_ll = 1 / rho_ll1217v1_ll = rho_v1_ll * rho_inv_ll1218v2_ll = rho_v2_ll * rho_inv_ll1219v3_ll = rho_v3_ll * rho_inv_ll1220rho_inv_rr = 1 / rho_rr1221v1_rr = rho_v1_rr * rho_inv_rr1222v2_rr = rho_v2_rr * rho_inv_rr1223v3_rr = rho_v3_rr * rho_inv_rr1224vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^21225vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^212261227p_ll = (gammas[k] - 1) *1228(rho_e_total_ll - 0.5f0 * rho_ll * vel_norm_ll - 0.5f0 * mag_norm_ll -12290.5f0 * psi_ll^2)1230p_rr = (gammas[k] - 1) *1231(rho_e_total_rr - 0.5f0 * rho_rr * vel_norm_rr - 0.5f0 * mag_norm_rr -12320.5f0 * psi_rr^2)1233beta_ll = 0.5f0 * rho_ll / p_ll1234beta_rr = 0.5f0 * rho_rr / p_rr1235# for convenience store vk_plus⋅B1236vel_dot_mag_ll = vk1_plus_ll[k] * B1_ll + vk2_plus_ll[k] * B2_ll +1237vk3_plus_ll[k] * B3_ll1238vel_dot_mag_rr = vk1_plus_rr[k] * B1_rr + vk2_plus_rr[k] * B2_rr +1239vk3_plus_rr[k] * B3_rr12401241# Compute the necessary mean values needed for either direction1242rho_avg = 0.5f0 * (rho_ll + rho_rr)1243rho_mean = ln_mean(rho_ll, rho_rr)1244beta_mean = ln_mean(beta_ll, beta_rr)1245beta_avg = 0.5f0 * (beta_ll + beta_rr)1246p_mean = 0.5f0 * rho_avg / beta_avg1247v1_avg = 0.5f0 * (v1_ll + v1_rr)1248v2_avg = 0.5f0 * (v2_ll + v2_rr)1249v3_avg = 0.5f0 * (v3_ll + v3_rr)1250vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)1251vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)1252vk1_plus_avg = 0.5f0 * (vk1_plus_ll[k] + vk1_plus_rr[k])1253vk2_plus_avg = 0.5f0 * (vk2_plus_ll[k] + vk2_plus_rr[k])1254vk3_plus_avg = 0.5f0 * (vk3_plus_ll[k] + vk3_plus_rr[k])1255# v_minus1256vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]1257vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]1258vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]1259vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]1260vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]1261vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]1262vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)1263vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)1264vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)12651266# Fill the fluxes for the mass and momentum equations1267f1 = rho_mean * (v1_avg * normal_direction[1] + v2_avg * normal_direction[2])1268f2 = f1 * v1_avg + p_mean * normal_direction[1]1269f3 = f1 * v2_avg + p_mean * normal_direction[2]1270f4 = f1 * v3_avg12711272# total energy flux is complicated and involves the previous eight components1273vk1_plus_mag_avg = 0.5f0 * (vk1_plus_ll[k] * mag_norm_ll +1274vk1_plus_rr[k] * mag_norm_rr)1275vk2_plus_mag_avg = 0.5f0 * (vk2_plus_ll[k] * mag_norm_ll +1276vk2_plus_rr[k] * mag_norm_rr)1277# Euler part1278f5 = f1 * 0.5f0 * (1 / (gammas[k] - 1) / beta_mean - vel_norm_avg) +1279f2 * v1_avg + f3 * v2_avg + f4 * v3_avg1280# MHD part1281f5 += (f6 * B1_avg + f7 * B2_avg + f8 * B3_avg -12820.5f0 * vk1_plus_mag_avg * normal_direction[1] -12830.5f0 * vk2_plus_mag_avg * normal_direction[2] +1284B_dot_n_avg * vel_dot_mag_avg # Same terms as in Derigs (but with v_plus)1285+ f9 * psi_avg -1286equations.c_h * psi_B_dot_n_avg # GLM term1287+12880.5f0 *1289(vk1_plus_avg * normal_direction[1] + vk2_plus_avg * normal_direction[2]) *1290mag_norm_avg -1291vk1_plus_avg * B_dot_n_avg * B1_avg -1292vk2_plus_avg * B_dot_n_avg * B2_avg -1293vk3_plus_avg * B_dot_n_avg * B3_avg # Additional terms related to the Lorentz non-conservative term (momentum eqs)1294-1295B2_avg * (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) *1296normal_direction[1] -1297B3_avg * (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg) *1298normal_direction[1] -1299B1_avg * (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) *1300normal_direction[2] -1301B3_avg * (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg) *1302normal_direction[2]) # Terms related to the multi-ion non-conservative term (induction equation!)13031304set_component!(f, k, f1, f2, f3, f4, f5, equations)1305end13061307return SVector(f)1308end13091310# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation1311# This routine approximates the maximum wave speed as sum of the maximum ion velocity1312# for all species and the maximum magnetosonic speed.1313@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,1314equations::IdealGlmMhdMultiIonEquations2D)1315# Calculate fast magnetoacoustic wave speeds1316# left1317cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)1318# right1319cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)13201321# Calculate velocities1322v_ll = zero(eltype(u_ll))1323v_rr = zero(eltype(u_rr))1324if orientation == 11325for k in eachcomponent(equations)1326rho, rho_v1, _ = get_component(k, u_ll, equations)1327v_ll = max(v_ll, abs(rho_v1 / rho))1328rho, rho_v1, _ = get_component(k, u_rr, equations)1329v_rr = max(v_rr, abs(rho_v1 / rho))1330end1331else #if orientation == 21332for k in eachcomponent(equations)1333rho, rho_v1, rho_v2, _ = get_component(k, u_ll, equations)1334v_ll = max(v_ll, abs(rho_v2 / rho))1335rho, rho_v1, rho_v2, _ = get_component(k, u_rr, equations)1336v_rr = max(v_rr, abs(rho_v2 / rho))1337end1338end13391340return max(abs(v_ll), abs(v_rr)) + max(cf_ll, cf_rr)1341end13421343@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,1344equations::IdealGlmMhdMultiIonEquations2D)1345# Calculate fast magnetoacoustic wave speeds1346# left1347cf_ll = calc_fast_wavespeed(u_ll, normal_direction, equations)1348# right1349cf_rr = calc_fast_wavespeed(u_rr, normal_direction, equations)13501351# Calculate velocities1352v_ll = zero(eltype(u_ll))1353v_rr = zero(eltype(u_rr))1354for k in eachcomponent(equations)1355rho, rho_v1, rho_v2, _ = get_component(k, u_ll, equations)1356v_ll = max(v_ll,1357abs((rho_v1 * normal_direction[1] + rho_v2 * normal_direction[2]) /1358rho))1359rho, rho_v1, rho_v2, _ = get_component(k, u_rr, equations)1360v_rr = max(v_rr,1361abs((rho_v1 * normal_direction[1] + rho_v2 * normal_direction[2]) /1362rho))1363end13641365return max(abs(v_ll), abs(v_rr)) + max(cf_ll, cf_rr)1366end13671368# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`1369@inline function max_abs_speed(u_ll, u_rr, orientation::Integer,1370equations::IdealGlmMhdMultiIonEquations2D)1371# Calculate fast magnetoacoustic wave speeds1372# left1373cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)1374# right1375cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)13761377# Calculate velocities1378v_ll = zero(eltype(u_ll))1379v_rr = zero(eltype(u_rr))1380if orientation == 11381for k in eachcomponent(equations)1382rho, rho_v1, _ = get_component(k, u_ll, equations)1383v_ll = max(v_ll, abs(rho_v1 / rho))1384rho, rho_v1, _ = get_component(k, u_rr, equations)1385v_rr = max(v_rr, abs(rho_v1 / rho))1386end1387else #if orientation == 21388for k in eachcomponent(equations)1389rho, rho_v1, rho_v2, _ = get_component(k, u_ll, equations)1390v_ll = max(v_ll, abs(rho_v2 / rho))1391rho, rho_v1, rho_v2, _ = get_component(k, u_rr, equations)1392v_rr = max(v_rr, abs(rho_v2 / rho))1393end1394end13951396return max(abs(v_ll) + cf_ll, abs(v_rr) + cf_rr)1397end13981399@inline function max_abs_speeds(u, equations::IdealGlmMhdMultiIonEquations2D)1400v1 = zero(real(equations))1401v2 = zero(real(equations))1402for k in eachcomponent(equations)1403rho, rho_v1, rho_v2, _ = get_component(k, u, equations)1404v1 = max(v1, abs(rho_v1 / rho))1405v2 = max(v2, abs(rho_v2 / rho))1406end14071408cf_x_direction = calc_fast_wavespeed(u, 1, equations)1409cf_y_direction = calc_fast_wavespeed(u, 2, equations)14101411return (abs(v1) + cf_x_direction, abs(v2) + cf_y_direction)1412end14131414# Compute the fastest wave speed for ideal multi-ion GLM-MHD equations: c_f, the fast1415# magnetoacoustic eigenvalue. This routine computes the fast magnetosonic speed for each ion1416# species using the single-fluid MHD expressions and approximates the multi-ion c_f as1417# the maximum of these individual magnetosonic speeds.1418@inline function calc_fast_wavespeed(cons, orientation::Integer,1419equations::IdealGlmMhdMultiIonEquations2D)1420B1, B2, B3 = magnetic_field(cons, equations)1421psi = divergence_cleaning_field(cons, equations)14221423c_f = zero(real(equations))1424for k in eachcomponent(equations)1425rho, rho_v1, rho_v2, rho_v3, rho_e_total = get_component(k, cons, equations)14261427rho_inv = 1 / rho1428v1 = rho_v1 * rho_inv1429v2 = rho_v2 * rho_inv1430v3 = rho_v3 * rho_inv1431gamma = equations.gammas[k]1432p = (gamma - 1) *1433(rho_e_total - 0.5f0 * rho * (v1^2 + v2^2 + v3^2) -14340.5f0 * (B1^2 + B2^2 + B3^2) -14350.5f0 * psi^2)1436a_square = gamma * p * rho_inv1437inv_sqrt_rho = 1 / sqrt(rho)14381439b1 = B1 * inv_sqrt_rho1440b2 = B2 * inv_sqrt_rho1441b3 = B3 * inv_sqrt_rho1442b_square = b1^2 + b2^2 + b3^214431444if orientation == 11445c_f = max(c_f,1446sqrt(0.5f0 * (a_square + b_square) +14470.5f0 *1448sqrt((a_square + b_square)^2 - 4 * a_square * b1^2)))1449else #if orientation == 21450c_f = max(c_f,1451sqrt(0.5f0 * (a_square + b_square) +14520.5f0 *1453sqrt((a_square + b_square)^2 - 4 * a_square * b2^2)))1454end1455end14561457return c_f1458end14591460@inline function calc_fast_wavespeed(cons, normal_direction::AbstractVector,1461equations::IdealGlmMhdMultiIonEquations2D)1462B1, B2, B3 = magnetic_field(cons, equations)1463psi = divergence_cleaning_field(cons, equations)14641465norm_squared = (normal_direction[1]^2 + normal_direction[2]^2)14661467c_f = zero(real(equations))1468for k in eachcomponent(equations)1469rho, rho_v1, rho_v2, rho_v3, rho_e_total = get_component(k, cons, equations)14701471rho_inv = 1 / rho1472v1 = rho_v1 * rho_inv1473v2 = rho_v2 * rho_inv1474v3 = rho_v3 * rho_inv1475gamma = equations.gammas[k]1476p = (gamma - 1) *1477(rho_e_total - 0.5f0 * rho * (v1^2 + v2^2 + v3^2) -14780.5f0 * (B1^2 + B2^2 + B3^2) -14790.5f0 * psi^2)1480a_square = gamma * p * rho_inv1481inv_sqrt_rho = 1 / sqrt(rho)14821483b1 = B1 * inv_sqrt_rho1484b2 = B2 * inv_sqrt_rho1485b3 = B3 * inv_sqrt_rho1486b_square = b1^2 + b2^2 + b3^21487# Properly normalize the magnetic field projection onto the unit normal1488b_dot_n_squared = (b1 * normal_direction[1] + b2 * normal_direction[2])^2 /1489norm_squared14901491c_f = max(c_f,1492sqrt((0.5f0 * (a_square + b_square) +14930.5f0 *1494sqrt((a_square + b_square)^2 - 4 * a_square * b_dot_n_squared)) *1495norm_squared))1496end14971498return c_f1499end1500end # @muladd150115021503