Path: blob/main/src/equations/ideal_glm_mhd_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"""8IdealGlmMhdEquations2D(gamma)910The ideal compressible GLM-MHD equations for an ideal gas with ratio of11specific heats `gamma` in two space dimensions.12"""13struct IdealGlmMhdEquations2D{RealT <: Real} <:14AbstractIdealGlmMhdEquations{2, 9}15gamma::RealT # ratio of specific heats16inv_gamma_minus_one::RealT # = inv(gamma - 1); can be used to write slow divisions as fast multiplications17c_h::RealT # GLM cleaning speed1819function IdealGlmMhdEquations2D(gamma, c_h)20γ, inv_gamma_minus_one, c_h = promote(gamma, inv(gamma - 1), c_h)21new{typeof(γ)}(γ, inv_gamma_minus_one, c_h)22end23end2425function IdealGlmMhdEquations2D(gamma; initial_c_h = convert(typeof(gamma), NaN))26# Use `promote` to ensure that `gamma` and `initial_c_h` have the same type27IdealGlmMhdEquations2D(promote(gamma, initial_c_h)...)28end2930# Outer constructor for `@reset` works correctly31function IdealGlmMhdEquations2D(gamma, inv_gamma_minus_one, c_h)32IdealGlmMhdEquations2D(gamma, c_h)33end3435have_nonconservative_terms(::IdealGlmMhdEquations2D) = True()3637function varnames(::typeof(cons2cons), ::IdealGlmMhdEquations2D)38("rho", "rho_v1", "rho_v2", "rho_v3", "rho_e", "B1", "B2", "B3", "psi")39end40function varnames(::typeof(cons2prim), ::IdealGlmMhdEquations2D)41("rho", "v1", "v2", "v3", "p", "B1", "B2", "B3", "psi")42end43function default_analysis_integrals(::IdealGlmMhdEquations2D)44(entropy_timederivative, Val(:l2_divb), Val(:linf_divb))45end4647# Helper function to extract the magnetic field vector from the conservative variables48magnetic_field(u, equations::IdealGlmMhdEquations2D) = SVector(u[6], u[7], u[8])4950# Set initial conditions at physical location `x` for time `t`51"""52initial_condition_constant(x, t, equations::IdealGlmMhdEquations2D)5354A constant initial condition to test free-stream preservation.55"""56function initial_condition_constant(x, t, equations::IdealGlmMhdEquations2D)57RealT = eltype(x)58rho = 159rho_v1 = convert(RealT, 0.1)60rho_v2 = -convert(RealT, 0.2)61rho_v3 = -0.5f062rho_e = 5063B1 = 364B2 = -convert(RealT, 1.2)65B3 = 0.5f066psi = 067return SVector(rho, rho_v1, rho_v2, rho_v3, rho_e, B1, B2, B3, psi)68end6970"""71initial_condition_convergence_test(x, t, equations::IdealGlmMhdEquations2D)7273An Alfvén wave as smooth initial condition used for convergence tests.74See for reference section 4.2 in75- Dominik Derigs, Andrew R. Winters, Gregor J. Gassner, and Stefanie Walch (2016)76A novel high-order, entropy stable, 3D AMR MHD solver with guaranteed positive pressure77[DOI: 10.1016/j.jcp.2016.04.048](https://doi.org/10.1016/j.jcp.2016.04.048)78"""79function initial_condition_convergence_test(x, t, equations::IdealGlmMhdEquations2D)80# smooth Alfvén wave test from Derigs et al. (2016)81# domain must be set to [0, 1/cos(α)] x [0, 1/sin(α)], γ = 5/382RealT = eltype(x)83alpha = 0.25f0 * convert(RealT, pi)84x_perp = x[1] * cos(alpha) + x[2] * sin(alpha)85B_perp = convert(RealT, 0.1) * sinpi(2 * (x_perp + t))86rho = 187v1 = -B_perp * sin(alpha)88v2 = B_perp * cos(alpha)89v3 = convert(RealT, 0.1) * cospi(2 * (x_perp + t))90p = convert(RealT, 0.1)91B1 = cos(alpha) + v192B2 = sin(alpha) + v293B3 = v394psi = 095return prim2cons(SVector(rho, v1, v2, v3, p, B1, B2, B3, psi), equations)96end9798"""99initial_condition_weak_blast_wave(x, t, equations::IdealGlmMhdEquations2D)100101A weak blast wave adapted from102- Sebastian Hennemann, Gregor J. Gassner (2020)103A provably entropy stable subcell shock capturing approach for high order split form DG104[arXiv: 2008.12044](https://arxiv.org/abs/2008.12044)105"""106function initial_condition_weak_blast_wave(x, t, equations::IdealGlmMhdEquations2D)107# Adapted MHD version of the weak blast wave from Hennemann & Gassner JCP paper 2020 (Sec. 6.3)108# Same discontinuity in the velocities but with magnetic fields109# Set up polar coordinates110RealT = eltype(x)111inicenter = (0, 0)112x_norm = x[1] - inicenter[1]113y_norm = x[2] - inicenter[2]114r = sqrt(x_norm^2 + y_norm^2)115phi = atan(y_norm, x_norm)116117# Calculate primitive variables118rho = r > 0.5f0 ? one(RealT) : convert(RealT, 1.1691)119v1 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos(phi)120v2 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * sin(phi)121p = r > 0.5f0 ? one(RealT) : convert(RealT, 1.245)122123return prim2cons(SVector(rho, v1, v2, 0, p, 1, 1, 1, 0), equations)124end125126# Pre-defined source terms should be implemented as127# function source_terms_WHATEVER(u, x, t, equations::IdealGlmMhdEquations2D)128129# Calculate 1D flux for a single point130@inline function flux(u, orientation::Integer, equations::IdealGlmMhdEquations2D)131rho, rho_v1, rho_v2, rho_v3, rho_e, B1, B2, B3, psi = u132v1 = rho_v1 / rho133v2 = rho_v2 / rho134v3 = rho_v3 / rho135kin_en = 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3)136mag_en = 0.5f0 * (B1 * B1 + B2 * B2 + B3 * B3)137p_over_gamma_minus_one = (rho_e - kin_en - mag_en - 0.5f0 * psi^2)138p = (equations.gamma - 1) * p_over_gamma_minus_one139if orientation == 1140f1 = rho_v1141f2 = rho_v1 * v1 + p + mag_en - B1^2142f3 = rho_v1 * v2 - B1 * B2143f4 = rho_v1 * v3 - B1 * B3144f5 = (kin_en + equations.gamma * p_over_gamma_minus_one + 2 * mag_en) * v1 -145B1 * (v1 * B1 + v2 * B2 + v3 * B3) + equations.c_h * psi * B1146f6 = equations.c_h * psi147f7 = v1 * B2 - v2 * B1148f8 = v1 * B3 - v3 * B1149f9 = equations.c_h * B1150else #if orientation == 2151f1 = rho_v2152f2 = rho_v2 * v1 - B2 * B1153f3 = rho_v2 * v2 + p + mag_en - B2^2154f4 = rho_v2 * v3 - B2 * B3155f5 = (kin_en + equations.gamma * p_over_gamma_minus_one + 2 * mag_en) * v2 -156B2 * (v1 * B1 + v2 * B2 + v3 * B3) + equations.c_h * psi * B2157f6 = v2 * B1 - v1 * B2158f7 = equations.c_h * psi159f8 = v2 * B3 - v3 * B2160f9 = equations.c_h * B2161end162163return SVector(f1, f2, f3, f4, f5, f6, f7, f8, f9)164end165166# Calculate 1D flux for a single point in the normal direction167# Note, this directional vector is not normalized168@inline function flux(u, normal_direction::AbstractVector,169equations::IdealGlmMhdEquations2D)170rho, rho_v1, rho_v2, rho_v3, rho_e, B1, B2, B3, psi = u171v1 = rho_v1 / rho172v2 = rho_v2 / rho173v3 = rho_v3 / rho174kin_en = 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3)175mag_en = 0.5f0 * (B1 * B1 + B2 * B2 + B3 * B3)176p_over_gamma_minus_one = (rho_e - kin_en - mag_en - 0.5f0 * psi^2)177p = (equations.gamma - 1) * p_over_gamma_minus_one178179v_normal = v1 * normal_direction[1] + v2 * normal_direction[2]180B_normal = B1 * normal_direction[1] + B2 * normal_direction[2]181rho_v_normal = rho * v_normal182183f1 = rho_v_normal184f2 = rho_v_normal * v1 - B1 * B_normal + (p + mag_en) * normal_direction[1]185f3 = rho_v_normal * v2 - B2 * B_normal + (p + mag_en) * normal_direction[2]186f4 = rho_v_normal * v3 - B3 * B_normal187f5 = ((kin_en + equations.gamma * p_over_gamma_minus_one + 2 * mag_en) * v_normal188-189B_normal * (v1 * B1 + v2 * B2 + v3 * B3) + equations.c_h * psi * B_normal)190f6 = equations.c_h * psi * normal_direction[1] +191(v2 * B1 - v1 * B2) * normal_direction[2]192f7 = equations.c_h * psi * normal_direction[2] +193(v1 * B2 - v2 * B1) * normal_direction[1]194f8 = v_normal * B3 - v3 * B_normal195f9 = equations.c_h * B_normal196197return SVector(f1, f2, f3, f4, f5, f6, f7, f8, f9)198end199200"""201flux_nonconservative_powell(u_ll, u_rr, orientation::Integer,202equations::IdealGlmMhdEquations2D)203flux_nonconservative_powell(u_ll, u_rr,204normal_direction::AbstractVector,205equations::IdealGlmMhdEquations2D)206207Non-symmetric two-point flux discretizing the nonconservative (source) term of208Powell and the Galilean nonconservative term associated with the GLM multiplier209of the [`IdealGlmMhdEquations2D`](@ref).210211On curvilinear meshes, the implementation differs from the reference since we use the averaged212normal direction for the metrics dealiasing.213214## References215- Marvin Bohm, Andrew R.Winters, Gregor J. Gassner, Dominik Derigs,216Florian Hindenlang, Joachim Saur217An entropy stable nodal discontinuous Galerkin method for the resistive MHD218equations. Part I: Theory and numerical verification219[DOI: 10.1016/j.jcp.2018.06.027](https://doi.org/10.1016/j.jcp.2018.06.027)220"""221@inline function flux_nonconservative_powell(u_ll, u_rr, orientation::Integer,222equations::IdealGlmMhdEquations2D)223rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll224rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr225226v1_ll = rho_v1_ll / rho_ll227v2_ll = rho_v2_ll / rho_ll228v3_ll = rho_v3_ll / rho_ll229v_dot_B_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll230231# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)232# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})233if orientation == 1234f = SVector(0,235B1_ll * B1_rr,236B2_ll * B1_rr,237B3_ll * B1_rr,238v_dot_B_ll * B1_rr + v1_ll * psi_ll * psi_rr,239v1_ll * B1_rr,240v2_ll * B1_rr,241v3_ll * B1_rr,242v1_ll * psi_rr)243else # orientation == 2244f = SVector(0,245B1_ll * B2_rr,246B2_ll * B2_rr,247B3_ll * B2_rr,248v_dot_B_ll * B2_rr + v2_ll * psi_ll * psi_rr,249v1_ll * B2_rr,250v2_ll * B2_rr,251v3_ll * B2_rr,252v2_ll * psi_rr)253end254255return f256end257258@inline function flux_nonconservative_powell(u_ll, u_rr,259normal_direction::AbstractVector,260equations::IdealGlmMhdEquations2D)261rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll262rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr263264v1_ll = rho_v1_ll / rho_ll265v2_ll = rho_v2_ll / rho_ll266v3_ll = rho_v3_ll / rho_ll267v_dot_B_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll268269v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2]270B_dot_n_rr = B1_rr * normal_direction[1] +271B2_rr * normal_direction[2]272273# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)274# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})275f = SVector(0,276B1_ll * B_dot_n_rr,277B2_ll * B_dot_n_rr,278B3_ll * B_dot_n_rr,279v_dot_B_ll * B_dot_n_rr + v_dot_n_ll * psi_ll * psi_rr,280v1_ll * B_dot_n_rr,281v2_ll * B_dot_n_rr,282v3_ll * B_dot_n_rr,283v_dot_n_ll * psi_rr)284285return f286end287288# For `VolumeIntegralSubcellLimiting` the nonconservative flux is created as a callable struct to289# enable dispatch on the type of the nonconservative term (symmetric / jump).290struct FluxNonConservativePowellLocalSymmetric <:291FluxNonConservative{NonConservativeSymmetric()}292end293294n_nonconservative_terms(::FluxNonConservativePowellLocalSymmetric) = 2295296const flux_nonconservative_powell_local_symmetric = FluxNonConservativePowellLocalSymmetric()297298"""299flux_nonconservative_powell_local_symmetric(u_ll, u_rr,300orientation::Integer,301equations::IdealGlmMhdEquations2D)302flux_nonconservative_powell_local_symmetric(u_ll, u_rr,303normal_direction::AbstractVector,304equations::IdealGlmMhdEquations2D)305306Non-symmetric two-point flux discretizing the nonconservative (source) term of307Powell and the Galilean nonconservative term associated with the GLM multiplier308of the [`IdealGlmMhdEquations2D`](@ref).309310This implementation uses a non-conservative term that can be written as the product311of local and symmetric parts. It is equivalent to the non-conservative flux of Bohm312et al. [`flux_nonconservative_powell`](@ref) for conforming meshes but it yields different313results on non-conforming meshes(!). On curvilinear meshes this formulation applies the314local normal direction compared to the averaged one used in [`flux_nonconservative_powell`](@ref).315316The two other flux functions with the same name return either the local317or symmetric portion of the non-conservative flux based on the type of the318nonconservative_type argument, employing multiple dispatch. They are used to319compute the subcell fluxes in dg_2d_subcell_limiters.jl.320321## References322- Rueda-Ramírez, Gassner (2023). A Flux-Differencing Formula for Split-Form Summation By Parts323Discretizations of Non-Conservative Systems. https://arxiv.org/pdf/2211.14009.pdf.324"""325@inline function (noncons_flux::FluxNonConservativePowellLocalSymmetric)(u_ll, u_rr,326orientation::Integer,327equations::IdealGlmMhdEquations2D)328rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll329rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr330331v1_ll = rho_v1_ll / rho_ll332v2_ll = rho_v2_ll / rho_ll333v3_ll = rho_v3_ll / rho_ll334v_dot_B_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll335336# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)337# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})338psi_avg = (psi_ll + psi_rr) #* 0.5 # The flux is already multiplied by 0.5 wherever it is used in the code339if orientation == 1340B1_avg = (B1_ll + B1_rr) #* 0.5 # The flux is already multiplied by 0.5 wherever it is used in the code341f = SVector(0,342B1_ll * B1_avg,343B2_ll * B1_avg,344B3_ll * B1_avg,345v_dot_B_ll * B1_avg + v1_ll * psi_ll * psi_avg,346v1_ll * B1_avg,347v2_ll * B1_avg,348v3_ll * B1_avg,349v1_ll * psi_avg)350else # orientation == 2351B2_avg = (B2_ll + B2_rr) #* 0.5 # The flux is already multiplied by 0.5 wherever it is used in the code352f = SVector(0,353B1_ll * B2_avg,354B2_ll * B2_avg,355B3_ll * B2_avg,356v_dot_B_ll * B2_avg + v2_ll * psi_ll * psi_avg,357v1_ll * B2_avg,358v2_ll * B2_avg,359v3_ll * B2_avg,360v2_ll * psi_avg)361end362363return f364end365366@inline function (noncons_flux::FluxNonConservativePowellLocalSymmetric)(u_ll, u_rr,367normal_direction::AbstractVector,368equations::IdealGlmMhdEquations2D)369rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll370rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr371372v1_ll = rho_v1_ll / rho_ll373v2_ll = rho_v2_ll / rho_ll374v3_ll = rho_v3_ll / rho_ll375v_dot_B_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll376377# The factor 0.5 of the averages can be omitted since it is already applied when this378# function is called.379psi_avg = (psi_ll + psi_rr)380B1_avg = (B1_ll + B1_rr)381B2_avg = (B2_ll + B2_rr)382383B_dot_n_avg = B1_avg * normal_direction[1] + B2_avg * normal_direction[2]384v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2]385386# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)387# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})388f = SVector(0,389B1_ll * B_dot_n_avg,390B2_ll * B_dot_n_avg,391B3_ll * B_dot_n_avg,392v_dot_B_ll * B_dot_n_avg + v_dot_n_ll * psi_ll * psi_avg,393v1_ll * B_dot_n_avg,394v2_ll * B_dot_n_avg,395v3_ll * B_dot_n_avg,396v_dot_n_ll * psi_avg)397398return f399end400401"""402flux_nonconservative_powell_local_symmetric(u_ll, orientation::Integer,403equations::IdealGlmMhdEquations2D,404nonconservative_type::NonConservativeLocal,405nonconservative_term::Integer)406flux_nonconservative_powell_local_symmetric(u_ll, normal_direction_ll::AbstractVector,407equations::IdealGlmMhdEquations2D,408nonconservative_type::NonConservativeLocal,409nonconservative_term::Integer)410411Local part of the Powell and GLM non-conservative terms. Needed for the calculation of412the non-conservative staggered "fluxes" for subcell limiting. See, e.g.,413- Rueda-Ramírez, Gassner (2023). A Flux-Differencing Formula for Split-Form Summation By Parts414Discretizations of Non-Conservative Systems. https://arxiv.org/pdf/2211.14009.pdf.415This function is used to compute the subcell fluxes in dg_2d_subcell_limiters.jl.416"""417@inline function (noncons_flux::FluxNonConservativePowellLocalSymmetric)(u_ll,418orientation::Integer,419equations::IdealGlmMhdEquations2D,420nonconservative_type::NonConservativeLocal,421nonconservative_term::Integer)422rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll423424if nonconservative_term == 1425# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)426v1_ll = rho_v1_ll / rho_ll427v2_ll = rho_v2_ll / rho_ll428v3_ll = rho_v3_ll / rho_ll429v_dot_B_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll430f = SVector(0,431B1_ll,432B2_ll,433B3_ll,434v_dot_B_ll,435v1_ll,436v2_ll,437v3_ll,4380)439else #nonconservative_term ==2440# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})441if orientation == 1442v1_ll = rho_v1_ll / rho_ll443f = SVector(0,4440,4450,4460,447v1_ll * psi_ll,4480,4490,4500,451v1_ll)452else #orientation == 2453v2_ll = rho_v2_ll / rho_ll454f = SVector(0,4550,4560,4570,458v2_ll * psi_ll,4590,4600,4610,462v2_ll)463end464end465return f466end467468@inline function (noncons_flux::FluxNonConservativePowellLocalSymmetric)(u_ll,469normal_direction_ll::AbstractVector,470equations::IdealGlmMhdEquations2D,471nonconservative_type::NonConservativeLocal,472nonconservative_term::Integer)473rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll474475if nonconservative_term == 1476# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)477v1_ll = rho_v1_ll / rho_ll478v2_ll = rho_v2_ll / rho_ll479v3_ll = rho_v3_ll / rho_ll480v_dot_B_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll481482f = SVector(0,483B1_ll,484B2_ll,485B3_ll,486v_dot_B_ll,487v1_ll,488v2_ll,489v3_ll,4900)491else # nonconservative_term == 2492# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})493v1_ll = rho_v1_ll / rho_ll494v2_ll = rho_v2_ll / rho_ll495v_dot_n_ll = v1_ll * normal_direction_ll[1] + v2_ll * normal_direction_ll[2]496497f = SVector(0,4980,4990,5000,501v_dot_n_ll * psi_ll,5020,5030,5040,505v_dot_n_ll)506end507return f508end509510"""511flux_nonconservative_powell_local_symmetric(u_ll, orientation::Integer,512equations::IdealGlmMhdEquations2D,513nonconservative_type::NonConservativeSymmetric,514nonconservative_term::Integer)515flux_nonconservative_powell_local_symmetric(u_ll, normal_direction_avg::AbstractVector,516equations::IdealGlmMhdEquations2D,517nonconservative_type::NonConservativeSymmetric,518nonconservative_term::Integer)519520Symmetric part of the Powell and GLM non-conservative terms. Needed for the calculation of521the non-conservative staggered "fluxes" for subcell limiting. See, e.g.,522- Rueda-Ramírez, Gassner (2023). A Flux-Differencing Formula for Split-Form Summation By Parts523Discretizations of Non-Conservative Systems. https://arxiv.org/pdf/2211.14009.pdf.524This function is used to compute the subcell fluxes in dg_2d_subcell_limiters.jl.525"""526@inline function (noncons_flux::FluxNonConservativePowellLocalSymmetric)(u_ll, u_rr,527orientation::Integer,528equations::IdealGlmMhdEquations2D,529nonconservative_type::NonConservativeSymmetric,530nonconservative_term::Integer)531rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll532rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr533534if nonconservative_term == 1535# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)536if orientation == 1537B1_avg = (B1_ll + B1_rr)#* 0.5 # The flux is already multiplied by 0.5 wherever it is used in the code538f = SVector(0,539B1_avg,540B1_avg,541B1_avg,542B1_avg,543B1_avg,544B1_avg,545B1_avg,5460)547else # orientation == 2548B2_avg = (B2_ll + B2_rr)#* 0.5 # The flux is already multiplied by 0.5 wherever it is used in the code549f = SVector(0,550B2_avg,551B2_avg,552B2_avg,553B2_avg,554B2_avg,555B2_avg,556B2_avg,5570)558end559else #nonconservative_term == 2560# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})561psi_avg = (psi_ll + psi_rr)#* 0.5 # The flux is already multiplied by 0.5 wherever it is used in the code562f = SVector(0,5630,5640,5650,566psi_avg,5670,5680,5690,570psi_avg)571end572573return f574end575576@inline function (noncons_flux::FluxNonConservativePowellLocalSymmetric)(u_ll, u_rr,577normal_direction_avg::AbstractVector,578equations::IdealGlmMhdEquations2D,579nonconservative_type::NonConservativeSymmetric,580nonconservative_term::Integer)581rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll582rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr583584if nonconservative_term == 1585# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)586# The factor 0.5 of the average can be omitted since it is already applied when this587# function is called.588B_dot_n_avg = (B1_ll + B1_rr) * normal_direction_avg[1] +589(B2_ll + B2_rr) * normal_direction_avg[2]590f = SVector(0,591B_dot_n_avg,592B_dot_n_avg,593B_dot_n_avg,594B_dot_n_avg,595B_dot_n_avg,596B_dot_n_avg,597B_dot_n_avg,5980)599else # nonconservative_term == 2600# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})601# The factor 0.5 of the average can be omitted since it is already applied when this602# function is called.603psi_avg = (psi_ll + psi_rr)604f = SVector(0,6050,6060,6070,608psi_avg,6090,6100,6110,612psi_avg)613end614615return f616end617618# For `VolumeIntegralSubcellLimiting` the nonconservative flux is created as a callable struct to619# enable dispatch on the type of the nonconservative term (symmetric / jump).620struct FluxNonConservativePowellLocalJump <:621FluxNonConservative{NonConservativeJump()}622end623624n_nonconservative_terms(::FluxNonConservativePowellLocalJump) = 2625626const flux_nonconservative_powell_local_jump = FluxNonConservativePowellLocalJump()627628"""629flux_nonconservative_powell_local_jump(u_ll, u_rr,630orientation::Integer,631equations::IdealGlmMhdEquations2D)632flux_nonconservative_powell_local_jump(u_ll, u_rr,633normal_direction::AbstractVector,634equations::IdealGlmMhdEquations2D)635636Non-symmetric two-point flux discretizing the nonconservative (source) term of637Powell and the Galilean nonconservative term associated with the GLM multiplier638of the [`IdealGlmMhdEquations2D`](@ref).639640This implementation uses a non-conservative term that can be written as the product641of local and jump parts.642643The two other flux functions with the same name return either the local644or jump portion of the non-conservative flux based on the type of the645nonconservative_type argument, employing multiple dispatch. They are used to646compute the subcell fluxes in dg_2d_subcell_limiters.jl.647648## References649- Rueda-Ramírez, Gassner (2023). A Flux-Differencing Formula for Split-Form Summation By Parts650Discretizations of Non-Conservative Systems. https://arxiv.org/pdf/2211.14009.pdf.651"""652@inline function (noncons_flux::FluxNonConservativePowellLocalJump)(u_ll, u_rr,653orientation::Integer,654equations::IdealGlmMhdEquations2D)655rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll656rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr657658v1_ll = rho_v1_ll / rho_ll659v2_ll = rho_v2_ll / rho_ll660v3_ll = rho_v3_ll / rho_ll661v_dot_B_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll662663# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)664# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})665psi_jump = psi_rr - psi_ll666if orientation == 1667B1_jump = B1_rr - B1_ll # The flux is already multiplied by 0.5 wherever it is used in the code668f = SVector(0,669B1_ll * B1_jump,670B2_ll * B1_jump,671B3_ll * B1_jump,672v_dot_B_ll * B1_jump + v1_ll * psi_ll * psi_jump,673v1_ll * B1_jump,674v2_ll * B1_jump,675v3_ll * B1_jump,676v1_ll * psi_jump)677else # orientation == 2678B2_jump = B2_rr - B2_ll # The flux is already multiplied by 0.5 wherever it is used in the code679f = SVector(0,680B1_ll * B2_jump,681B2_ll * B2_jump,682B3_ll * B2_jump,683v_dot_B_ll * B2_jump + v2_ll * psi_ll * psi_jump,684v1_ll * B2_jump,685v2_ll * B2_jump,686v3_ll * B2_jump,687v2_ll * psi_jump)688end689690return f691end692693@inline function (noncons_flux::FluxNonConservativePowellLocalJump)(u_ll, u_rr,694normal_direction::AbstractVector,695equations::IdealGlmMhdEquations2D)696rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll697rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr698699v1_ll = rho_v1_ll / rho_ll700v2_ll = rho_v2_ll / rho_ll701v3_ll = rho_v3_ll / rho_ll702v_dot_B_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll703704psi_jump = psi_rr - psi_ll705B1_jump = B1_rr - B1_ll706B2_jump = B2_rr - B2_ll707708B_dot_n_jump = B1_jump * normal_direction[1] + B2_jump * normal_direction[2]709v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2]710711# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)712# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})713f = SVector(0,714B1_ll * B_dot_n_jump,715B2_ll * B_dot_n_jump,716B3_ll * B_dot_n_jump,717v_dot_B_ll * B_dot_n_jump + v_dot_n_ll * psi_ll * psi_jump,718v1_ll * B_dot_n_jump,719v2_ll * B_dot_n_jump,720v3_ll * B_dot_n_jump,721v_dot_n_ll * psi_jump)722723return f724end725726"""727flux_nonconservative_powell_local_jump(u_ll, orientation::Integer,728equations::IdealGlmMhdEquations2D,729nonconservative_type::NonConservativeLocal,730nonconservative_term::Integer)731flux_nonconservative_powell_local_jump(u_ll, normal_direction_ll::AbstractVector,732equations::IdealGlmMhdEquations2D,733nonconservative_type::NonConservativeLocal,734nonconservative_term::Integer)735736Local part of the Powell and GLM non-conservative terms. Needed for the calculation of737the non-conservative staggered "fluxes" for subcell limiting.738This function is used to compute the subcell fluxes in dg_2d_subcell_limiters.jl.739740## References741- Rueda-Ramírez, Gassner (2023). A Flux-Differencing Formula for Split-Form Summation By Parts742Discretizations of Non-Conservative Systems. https://arxiv.org/pdf/2211.14009.pdf.743"""744@inline function (noncons_flux::FluxNonConservativePowellLocalJump)(u_ll,745orientation::Integer,746equations::IdealGlmMhdEquations2D,747nonconservative_type::NonConservativeLocal,748nonconservative_term::Integer)749rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll750if nonconservative_term == 1751# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)752v1_ll = rho_v1_ll / rho_ll753v2_ll = rho_v2_ll / rho_ll754v3_ll = rho_v3_ll / rho_ll755v_dot_B_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll756f = SVector(0,757B1_ll,758B2_ll,759B3_ll,760v_dot_B_ll,761v1_ll,762v2_ll,763v3_ll,7640)765else #nonconservative_term ==2766# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})767if orientation == 1768v1_ll = rho_v1_ll / rho_ll769f = SVector(0,7700,7710,7720,773v1_ll * psi_ll,7740,7750,7760,777v1_ll)778else #orientation == 2779v2_ll = rho_v2_ll / rho_ll780f = SVector(0,7810,7820,7830,784v2_ll * psi_ll,7850,7860,7870,788v2_ll)789end790end791return f792end793794@inline function (noncons_flux::FluxNonConservativePowellLocalJump)(u_ll,795normal_direction_ll::AbstractVector,796equations::IdealGlmMhdEquations2D,797nonconservative_type::NonConservativeLocal,798nonconservative_term::Integer)799rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll800801if nonconservative_term == 1802# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)803v1_ll = rho_v1_ll / rho_ll804v2_ll = rho_v2_ll / rho_ll805v3_ll = rho_v3_ll / rho_ll806v_dot_B_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll807808f = SVector(0,809B1_ll,810B2_ll,811B3_ll,812v_dot_B_ll,813v1_ll,814v2_ll,815v3_ll,8160)817else # nonconservative_term == 2818# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})819v1_ll = rho_v1_ll / rho_ll820v2_ll = rho_v2_ll / rho_ll821v_dot_n_ll = v1_ll * normal_direction_ll[1] + v2_ll * normal_direction_ll[2]822823f = SVector(0,8240,8250,8260,827v_dot_n_ll * psi_ll,8280,8290,8300,831v_dot_n_ll)832end833return f834end835836"""837flux_nonconservative_powell_local_jump(u_ll, orientation::Integer,838equations::IdealGlmMhdEquations2D,839nonconservative_type::NonConservativeJump,840nonconservative_term::Integer)841flux_nonconservative_powell_local_jump(u_ll, normal_direction_avg::AbstractVector,842equations::IdealGlmMhdEquations2D,843nonconservative_type::NonConservativeJump,844nonconservative_term::Integer)845846Jump part of the Powell and GLM non-conservative terms. Needed for the calculation of847the non-conservative staggered "fluxes" for subcell limiting.848This function is used to compute the subcell fluxes in dg_2d_subcell_limiters.jl.849850## References851- Rueda-Ramírez, Gassner (2023). A Flux-Differencing Formula for Split-Form Summation By Parts852Discretizations of Non-Conservative Systems. https://arxiv.org/pdf/2211.14009.pdf.853"""854@inline function (noncons_flux::FluxNonConservativePowellLocalJump)(u_ll, u_rr,855orientation::Integer,856equations::IdealGlmMhdEquations2D,857nonconservative_type::NonConservativeJump,858nonconservative_term::Integer)859rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll860rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr861862if nonconservative_term == 1863# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)864if orientation == 1865B1_jump = B1_rr - B1_ll # The flux is already multiplied by 0.5 wherever it is used in the code866f = SVector(0,867B1_jump,868B1_jump,869B1_jump,870B1_jump,871B1_jump,872B1_jump,873B1_jump,8740)875else # orientation == 2876B2_jump = B2_rr - B2_ll # The flux is already multiplied by 0.5 wherever it is used in the code877f = SVector(0,878B2_jump,879B2_jump,880B2_jump,881B2_jump,882B2_jump,883B2_jump,884B2_jump,8850)886end887else #nonconservative_term == 2888# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})889psi_jump = psi_rr - psi_ll # The flux is already multiplied by 0.5 wherever it is used in the code890f = SVector(0,8910,8920,8930,894psi_jump,8950,8960,8970,898psi_jump)899end900901return f902end903904@inline function (noncons_flux::FluxNonConservativePowellLocalJump)(u_ll, u_rr,905normal_direction_avg::AbstractVector,906equations::IdealGlmMhdEquations2D,907nonconservative_type::NonConservativeJump,908nonconservative_term::Integer)909rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll910rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr911912if nonconservative_term == 1913# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)914B1_jump = B1_rr - B1_ll915B2_jump = B2_rr - B2_ll916B_dot_n_jump = B1_jump * normal_direction_avg[1] +917B2_jump * normal_direction_avg[2]918f = SVector(0,919B_dot_n_jump,920B_dot_n_jump,921B_dot_n_jump,922B_dot_n_jump,923B_dot_n_jump,924B_dot_n_jump,925B_dot_n_jump,9260)927else # nonconservative_term == 2928# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})929psi_jump = (psi_rr - psi_ll)930f = SVector(0,9310,9320,9330,934psi_jump,9350,9360,9370,938psi_jump)939end940941return f942end943944"""945flux_derigs_etal(u_ll, u_rr, orientation, equations::IdealGlmMhdEquations2D)946947Entropy conserving two-point flux by948- Derigs et al. (2018)949Ideal GLM-MHD: About the entropy consistent nine-wave magnetic field950divergence diminishing ideal magnetohydrodynamics equations951[DOI: 10.1016/j.jcp.2018.03.002](https://doi.org/10.1016/j.jcp.2018.03.002)952"""953function flux_derigs_etal(u_ll, u_rr, orientation::Integer,954equations::IdealGlmMhdEquations2D)955# Unpack left and right states to get velocities, pressure, and inverse temperature (called beta)956rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll957rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr958959v1_ll = rho_v1_ll / rho_ll960v2_ll = rho_v2_ll / rho_ll961v3_ll = rho_v3_ll / rho_ll962v1_rr = rho_v1_rr / rho_rr963v2_rr = rho_v2_rr / rho_rr964v3_rr = rho_v3_rr / rho_rr965vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^2966vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^2967mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2968mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2969p_ll = (equations.gamma - 1) *970(rho_e_ll - 0.5f0 * rho_ll * vel_norm_ll - 0.5f0 * mag_norm_ll -9710.5f0 * psi_ll^2)972p_rr = (equations.gamma - 1) *973(rho_e_rr - 0.5f0 * rho_rr * vel_norm_rr - 0.5f0 * mag_norm_rr -9740.5f0 * psi_rr^2)975beta_ll = 0.5f0 * rho_ll / p_ll976beta_rr = 0.5f0 * rho_rr / p_rr977# for convenience store v⋅B978vel_dot_mag_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll979vel_dot_mag_rr = v1_rr * B1_rr + v2_rr * B2_rr + v3_rr * B3_rr980981# Compute the necessary mean values needed for either direction982rho_avg = 0.5f0 * (rho_ll + rho_rr)983rho_mean = ln_mean(rho_ll, rho_rr)984beta_mean = ln_mean(beta_ll, beta_rr)985beta_avg = 0.5f0 * (beta_ll + beta_rr)986v1_avg = 0.5f0 * (v1_ll + v1_rr)987v2_avg = 0.5f0 * (v2_ll + v2_rr)988v3_avg = 0.5f0 * (v3_ll + v3_rr)989p_mean = 0.5f0 * rho_avg / beta_avg990B1_avg = 0.5f0 * (B1_ll + B1_rr)991B2_avg = 0.5f0 * (B2_ll + B2_rr)992B3_avg = 0.5f0 * (B3_ll + B3_rr)993psi_avg = 0.5f0 * (psi_ll + psi_rr)994vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)995mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)996vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)997998# Calculate fluxes depending on orientation with specific direction averages999if orientation == 11000f1 = rho_mean * v1_avg1001f2 = f1 * v1_avg + p_mean + 0.5f0 * mag_norm_avg - B1_avg * B1_avg1002f3 = f1 * v2_avg - B1_avg * B2_avg1003f4 = f1 * v3_avg - B1_avg * B3_avg1004f6 = equations.c_h * psi_avg1005f7 = v1_avg * B2_avg - v2_avg * B1_avg1006f8 = v1_avg * B3_avg - v3_avg * B1_avg1007f9 = equations.c_h * B1_avg1008# total energy flux is complicated and involves the previous eight components1009psi_B1_avg = 0.5f0 * (B1_ll * psi_ll + B1_rr * psi_rr)1010v1_mag_avg = 0.5f0 * (v1_ll * mag_norm_ll + v1_rr * mag_norm_rr)1011f5 = (f1 * 0.5f0 * (1 / (equations.gamma - 1) / beta_mean - vel_norm_avg) +1012f2 * v1_avg + f3 * v2_avg +1013f4 * v3_avg + f6 * B1_avg + f7 * B2_avg + f8 * B3_avg + f9 * psi_avg -10140.5f0 * v1_mag_avg +1015B1_avg * vel_dot_mag_avg - equations.c_h * psi_B1_avg)1016else1017f1 = rho_mean * v2_avg1018f2 = f1 * v1_avg - B1_avg * B2_avg1019f3 = f1 * v2_avg + p_mean + 0.5f0 * mag_norm_avg - B2_avg * B2_avg1020f4 = f1 * v3_avg - B2_avg * B3_avg1021f6 = v2_avg * B1_avg - v1_avg * B2_avg1022f7 = equations.c_h * psi_avg1023f8 = v2_avg * B3_avg - v3_avg * B2_avg1024f9 = equations.c_h * B2_avg1025# total energy flux is complicated and involves the previous eight components1026psi_B2_avg = 0.5f0 * (B2_ll * psi_ll + B2_rr * psi_rr)1027v2_mag_avg = 0.5f0 * (v2_ll * mag_norm_ll + v2_rr * mag_norm_rr)1028f5 = (f1 * 0.5f0 * (1 / (equations.gamma - 1) / beta_mean - vel_norm_avg) +1029f2 * v1_avg + f3 * v2_avg +1030f4 * v3_avg + f6 * B1_avg + f7 * B2_avg + f8 * B3_avg + f9 * psi_avg -10310.5f0 * v2_mag_avg +1032B2_avg * vel_dot_mag_avg - equations.c_h * psi_B2_avg)1033end10341035return SVector(f1, f2, f3, f4, f5, f6, f7, f8, f9)1036end10371038"""1039flux_hindenlang_gassner(u_ll, u_rr, orientation_or_normal_direction,1040equations::IdealGlmMhdEquations2D)10411042Entropy conserving and kinetic energy preserving two-point flux of1043Hindenlang and Gassner (2019), extending [`flux_ranocha`](@ref) to the MHD equations.10441045## References1046- Florian Hindenlang, Gregor Gassner (2019)1047A new entropy conservative two-point flux for ideal MHD equations derived from1048first principles.1049Presented at HONOM 2019: European workshop on high order numerical methods1050for evolutionary PDEs, theory and applications1051- Hendrik Ranocha (2018)1052Generalised Summation-by-Parts Operators and Entropy Stability of Numerical Methods1053for Hyperbolic Balance Laws1054[PhD thesis, TU Braunschweig](https://cuvillier.de/en/shop/publications/7743)1055- Hendrik Ranocha (2020)1056Entropy Conserving and Kinetic Energy Preserving Numerical Methods for1057the Euler Equations Using Summation-by-Parts Operators1058[Proceedings of ICOSAHOM 2018](https://doi.org/10.1007/978-3-030-39647-3_42)1059"""1060@inline function flux_hindenlang_gassner(u_ll, u_rr, orientation::Integer,1061equations::IdealGlmMhdEquations2D)1062# Unpack left and right states1063rho_ll, v1_ll, v2_ll, v3_ll, p_ll, B1_ll, B2_ll, B3_ll, psi_ll = cons2prim(u_ll,1064equations)1065rho_rr, v1_rr, v2_rr, v3_rr, p_rr, B1_rr, B2_rr, B3_rr, psi_rr = cons2prim(u_rr,1066equations)10671068# Compute the necessary mean values needed for either direction1069rho_mean = ln_mean(rho_ll, rho_rr)1070# Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)`1071# in exact arithmetic since1072# log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ)1073# = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ)1074inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll)1075v1_avg = 0.5f0 * (v1_ll + v1_rr)1076v2_avg = 0.5f0 * (v2_ll + v2_rr)1077v3_avg = 0.5f0 * (v3_ll + v3_rr)1078p_avg = 0.5f0 * (p_ll + p_rr)1079psi_avg = 0.5f0 * (psi_ll + psi_rr)1080velocity_square_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr)1081magnetic_square_avg = 0.5f0 * (B1_ll * B1_rr + B2_ll * B2_rr + B3_ll * B3_rr)10821083# Calculate fluxes depending on orientation with specific direction averages1084if orientation == 11085f1 = rho_mean * v1_avg1086f2 = f1 * v1_avg + p_avg + magnetic_square_avg -10870.5f0 * (B1_ll * B1_rr + B1_rr * B1_ll)1088f3 = f1 * v2_avg - 0.5f0 * (B1_ll * B2_rr + B1_rr * B2_ll)1089f4 = f1 * v3_avg - 0.5f0 * (B1_ll * B3_rr + B1_rr * B3_ll)1090#f5 below1091f6 = equations.c_h * psi_avg1092f7 = 0.5f0 * (v1_ll * B2_ll - v2_ll * B1_ll + v1_rr * B2_rr - v2_rr * B1_rr)1093f8 = 0.5f0 * (v1_ll * B3_ll - v3_ll * B1_ll + v1_rr * B3_rr - v3_rr * B1_rr)1094f9 = equations.c_h * 0.5f0 * (B1_ll + B1_rr)1095# total energy flux is complicated and involves the previous components1096f5 = (f1 *1097(velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one)1098+10990.5f0 * (+p_ll * v1_rr + p_rr * v1_ll1100+ (v1_ll * B2_ll * B2_rr + v1_rr * B2_rr * B2_ll)1101+ (v1_ll * B3_ll * B3_rr + v1_rr * B3_rr * B3_ll)1102-1103(v2_ll * B1_ll * B2_rr + v2_rr * B1_rr * B2_ll)1104-1105(v3_ll * B1_ll * B3_rr + v3_rr * B1_rr * B3_ll)1106+1107equations.c_h * (B1_ll * psi_rr + B1_rr * psi_ll)))1108else # orientation == 21109f1 = rho_mean * v2_avg1110f2 = f1 * v1_avg - 0.5f0 * (B2_ll * B1_rr + B2_rr * B1_ll)1111f3 = f1 * v2_avg + p_avg + magnetic_square_avg -11120.5f0 * (B2_ll * B2_rr + B2_rr * B2_ll)1113f4 = f1 * v3_avg - 0.5f0 * (B2_ll * B3_rr + B2_rr * B3_ll)1114#f5 below1115f6 = 0.5f0 * (v2_ll * B1_ll - v1_ll * B2_ll + v2_rr * B1_rr - v1_rr * B2_rr)1116f7 = equations.c_h * psi_avg1117f8 = 0.5f0 * (v2_ll * B3_ll - v3_ll * B2_ll + v2_rr * B3_rr - v3_rr * B2_rr)1118f9 = equations.c_h * 0.5f0 * (B2_ll + B2_rr)1119# total energy flux is complicated and involves the previous components1120f5 = (f1 *1121(velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one)1122+11230.5f0 * (+p_ll * v2_rr + p_rr * v2_ll1124+ (v2_ll * B1_ll * B1_rr + v2_rr * B1_rr * B1_ll)1125+ (v2_ll * B3_ll * B3_rr + v2_rr * B3_rr * B3_ll)1126-1127(v1_ll * B2_ll * B1_rr + v1_rr * B2_rr * B1_ll)1128-1129(v3_ll * B2_ll * B3_rr + v3_rr * B2_rr * B3_ll)1130+1131equations.c_h * (B2_ll * psi_rr + B2_rr * psi_ll)))1132end11331134return SVector(f1, f2, f3, f4, f5, f6, f7, f8, f9)1135end11361137@inline function flux_hindenlang_gassner(u_ll, u_rr, normal_direction::AbstractVector,1138equations::IdealGlmMhdEquations2D)1139# Unpack left and right states1140rho_ll, v1_ll, v2_ll, v3_ll, p_ll, B1_ll, B2_ll, B3_ll, psi_ll = cons2prim(u_ll,1141equations)1142rho_rr, v1_rr, v2_rr, v3_rr, p_rr, B1_rr, B2_rr, B3_rr, psi_rr = cons2prim(u_rr,1143equations)1144v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2]1145v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2]1146B_dot_n_ll = B1_ll * normal_direction[1] + B2_ll * normal_direction[2]1147B_dot_n_rr = B1_rr * normal_direction[1] + B2_rr * normal_direction[2]11481149# Compute the necessary mean values needed for either direction1150rho_mean = ln_mean(rho_ll, rho_rr)1151# Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)`1152# in exact arithmetic since1153# log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ)1154# = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ)1155inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll)1156v1_avg = 0.5f0 * (v1_ll + v1_rr)1157v2_avg = 0.5f0 * (v2_ll + v2_rr)1158v3_avg = 0.5f0 * (v3_ll + v3_rr)1159p_avg = 0.5f0 * (p_ll + p_rr)1160psi_avg = 0.5f0 * (psi_ll + psi_rr)1161velocity_square_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr)1162magnetic_square_avg = 0.5f0 * (B1_ll * B1_rr + B2_ll * B2_rr + B3_ll * B3_rr)11631164# Calculate fluxes depending on normal_direction1165f1 = rho_mean * 0.5f0 * (v_dot_n_ll + v_dot_n_rr)1166f2 = (f1 * v1_avg + (p_avg + magnetic_square_avg) * normal_direction[1]1167-11680.5f0 * (B_dot_n_ll * B1_rr + B_dot_n_rr * B1_ll))1169f3 = (f1 * v2_avg + (p_avg + magnetic_square_avg) * normal_direction[2]1170-11710.5f0 * (B_dot_n_ll * B2_rr + B_dot_n_rr * B2_ll))1172f4 = (f1 * v3_avg1173-11740.5f0 * (B_dot_n_ll * B3_rr + B_dot_n_rr * B3_ll))1175#f5 below1176f6 = (equations.c_h * psi_avg * normal_direction[1]1177+11780.5f0 * (v_dot_n_ll * B1_ll - v1_ll * B_dot_n_ll +1179v_dot_n_rr * B1_rr - v1_rr * B_dot_n_rr))1180f7 = (equations.c_h * psi_avg * normal_direction[2]1181+11820.5f0 * (v_dot_n_ll * B2_ll - v2_ll * B_dot_n_ll +1183v_dot_n_rr * B2_rr - v2_rr * B_dot_n_rr))1184f8 = +0.5f0 * (v_dot_n_ll * B3_ll - v3_ll * B_dot_n_ll +1185v_dot_n_rr * B3_rr - v3_rr * B_dot_n_rr)1186f9 = equations.c_h * 0.5f0 * (B_dot_n_ll + B_dot_n_rr)1187# total energy flux is complicated and involves the previous components1188f5 = (f1 * (velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one)1189+11900.5f0 * (+p_ll * v_dot_n_rr + p_rr * v_dot_n_ll1191+ (v_dot_n_ll * B1_ll * B1_rr + v_dot_n_rr * B1_rr * B1_ll)1192+ (v_dot_n_ll * B2_ll * B2_rr + v_dot_n_rr * B2_rr * B2_ll)1193+ (v_dot_n_ll * B3_ll * B3_rr + v_dot_n_rr * B3_rr * B3_ll)1194-1195(v1_ll * B_dot_n_ll * B1_rr + v1_rr * B_dot_n_rr * B1_ll)1196-1197(v2_ll * B_dot_n_ll * B2_rr + v2_rr * B_dot_n_rr * B2_ll)1198-1199(v3_ll * B_dot_n_ll * B3_rr + v3_rr * B_dot_n_rr * B3_ll)1200+1201equations.c_h * (B_dot_n_ll * psi_rr + B_dot_n_rr * psi_ll)))12021203return SVector(f1, f2, f3, f4, f5, f6, f7, f8, f9)1204end12051206# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation1207@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,1208equations::IdealGlmMhdEquations2D)1209rho_ll, rho_v1_ll, rho_v2_ll, _ = u_ll1210rho_rr, rho_v1_rr, rho_v2_rr, _ = u_rr12111212# Calculate the left/right velocities and fast magnetoacoustic wave speeds1213if orientation == 11214v_ll = rho_v1_ll / rho_ll1215v_rr = rho_v1_rr / rho_rr1216else # orientation == 21217v_ll = rho_v2_ll / rho_ll1218v_rr = rho_v2_rr / rho_rr1219end1220cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)1221cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)12221223return max(abs(v_ll), abs(v_rr)) + max(cf_ll, cf_rr)1224end12251226@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,1227equations::IdealGlmMhdEquations2D)1228# return max(v_mag_ll, v_mag_rr) + max(cf_ll, cf_rr)1229rho_ll, rho_v1_ll, rho_v2_ll, _ = u_ll1230rho_rr, rho_v1_rr, rho_v2_rr, _ = u_rr12311232# Calculate normal velocities and fast magnetoacoustic wave speeds1233# left1234v1_ll = rho_v1_ll / rho_ll1235v2_ll = rho_v2_ll / rho_ll1236v_ll = (v1_ll * normal_direction[1]1237+1238v2_ll * normal_direction[2])1239cf_ll = calc_fast_wavespeed(u_ll, normal_direction, equations)1240# right1241v1_rr = rho_v1_rr / rho_rr1242v2_rr = rho_v2_rr / rho_rr1243v_rr = (v1_rr * normal_direction[1]1244+1245v2_rr * normal_direction[2])1246cf_rr = calc_fast_wavespeed(u_rr, normal_direction, equations)12471248# wave speeds already scaled by norm(normal_direction) in [`calc_fast_wavespeed`](@ref)1249return max(abs(v_ll), abs(v_rr)) + max(cf_ll, cf_rr)1250end12511252# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`1253@inline function max_abs_speed(u_ll, u_rr, orientation::Integer,1254equations::IdealGlmMhdEquations2D)1255rho_ll, rho_v1_ll, rho_v2_ll, _ = u_ll1256rho_rr, rho_v1_rr, rho_v2_rr, _ = u_rr12571258# Calculate the left/right velocities and fast magnetoacoustic wave speeds1259if orientation == 11260v_ll = rho_v1_ll / rho_ll1261v_rr = rho_v1_rr / rho_rr1262else # orientation == 21263v_ll = rho_v2_ll / rho_ll1264v_rr = rho_v2_rr / rho_rr1265end1266cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)1267cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)12681269return max(abs(v_ll) + cf_ll, abs(v_rr) + cf_rr)1270end12711272# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`1273@inline function max_abs_speed(u_ll, u_rr, normal_direction::AbstractVector,1274equations::IdealGlmMhdEquations2D)1275# return max(v_mag_ll, v_mag_rr) + max(cf_ll, cf_rr)1276rho_ll, rho_v1_ll, rho_v2_ll, _ = u_ll1277rho_rr, rho_v1_rr, rho_v2_rr, _ = u_rr12781279# Calculate normal velocities and fast magnetoacoustic wave speeds1280# left1281v1_ll = rho_v1_ll / rho_ll1282v2_ll = rho_v2_ll / rho_ll1283v_ll = (v1_ll * normal_direction[1]1284+1285v2_ll * normal_direction[2])1286cf_ll = calc_fast_wavespeed(u_ll, normal_direction, equations)1287# right1288v1_rr = rho_v1_rr / rho_rr1289v2_rr = rho_v2_rr / rho_rr1290v_rr = (v1_rr * normal_direction[1]1291+1292v2_rr * normal_direction[2])1293cf_rr = calc_fast_wavespeed(u_rr, normal_direction, equations)12941295# wave speeds already scaled by norm(normal_direction) in [`calc_fast_wavespeed`](@ref)1296return max(abs(v_ll) + cf_ll, abs(v_rr) + cf_rr)1297end12981299# Calculate estimate for minimum and maximum wave speeds for HLL-type fluxes1300@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer,1301equations::IdealGlmMhdEquations2D)1302rho_ll, rho_v1_ll, rho_v2_ll, _ = u_ll1303rho_rr, rho_v1_rr, rho_v2_rr, _ = u_rr13041305# Calculate primitive velocity variables1306v1_ll = rho_v1_ll / rho_ll1307v2_ll = rho_v2_ll / rho_ll13081309v1_rr = rho_v1_rr / rho_rr1310v2_rr = rho_v2_rr / rho_rr13111312# Approximate the left-most and right-most eigenvalues in the Riemann fan1313if orientation == 1 # x-direction1314λ_min = v1_ll - calc_fast_wavespeed(u_ll, orientation, equations)1315λ_max = v1_rr + calc_fast_wavespeed(u_rr, orientation, equations)1316else # y-direction1317λ_min = v2_ll - calc_fast_wavespeed(u_ll, orientation, equations)1318λ_max = v2_rr + calc_fast_wavespeed(u_rr, orientation, equations)1319end13201321return λ_min, λ_max1322end13231324@inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,1325equations::IdealGlmMhdEquations2D)1326rho_ll, rho_v1_ll, rho_v2_ll, _ = u_ll1327rho_rr, rho_v1_rr, rho_v2_rr, _ = u_rr13281329# Calculate primitive velocity variables1330v1_ll = rho_v1_ll / rho_ll1331v2_ll = rho_v2_ll / rho_ll13321333v1_rr = rho_v1_rr / rho_rr1334v2_rr = rho_v2_rr / rho_rr13351336v_normal_ll = (v1_ll * normal_direction[1] + v2_ll * normal_direction[2])1337v_normal_rr = (v1_rr * normal_direction[1] + v2_rr * normal_direction[2])13381339c_f_ll = calc_fast_wavespeed(u_ll, normal_direction, equations)1340c_f_rr = calc_fast_wavespeed(u_rr, normal_direction, equations)13411342# Estimate the min/max eigenvalues in the normal direction1343λ_min = min(v_normal_ll - c_f_ll, v_normal_rr - c_f_rr)1344λ_max = max(v_normal_rr + c_f_rr, v_normal_rr + c_f_rr)13451346return λ_min, λ_max1347end13481349# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes1350@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,1351equations::IdealGlmMhdEquations2D)1352rho_ll, rho_v1_ll, rho_v2_ll, _ = u_ll1353rho_rr, rho_v1_rr, rho_v2_rr, _ = u_rr13541355# Calculate primitive velocity variables1356v1_ll = rho_v1_ll / rho_ll1357v2_ll = rho_v2_ll / rho_ll13581359v1_rr = rho_v1_rr / rho_rr1360v2_rr = rho_v2_rr / rho_rr13611362# Approximate the left-most and right-most eigenvalues in the Riemann fan1363if orientation == 1 # x-direction1364c_f_ll = calc_fast_wavespeed(u_ll, orientation, equations)1365c_f_rr = calc_fast_wavespeed(u_rr, orientation, equations)13661367λ_min = min(v1_ll - c_f_ll, v1_rr - c_f_rr)1368λ_max = max(v1_ll + c_f_ll, v1_rr + c_f_rr)1369else # y-direction1370c_f_ll = calc_fast_wavespeed(u_ll, orientation, equations)1371c_f_rr = calc_fast_wavespeed(u_rr, orientation, equations)13721373λ_min = min(v2_ll - c_f_ll, v2_rr - c_f_rr)1374λ_max = max(v2_ll + c_f_ll, v1_rr + c_f_rr)1375end13761377return λ_min, λ_max1378end13791380# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes1381@inline function min_max_speed_davis(u_ll, u_rr, normal_direction::AbstractVector,1382equations::IdealGlmMhdEquations2D)1383rho_ll, rho_v1_ll, rho_v2_ll, _ = u_ll1384rho_rr, rho_v1_rr, rho_v2_rr, _ = u_rr13851386# Calculate primitive velocity variables1387v1_ll = rho_v1_ll / rho_ll1388v2_ll = rho_v2_ll / rho_ll13891390v1_rr = rho_v1_rr / rho_rr1391v2_rr = rho_v2_rr / rho_rr13921393v_normal_ll = (v1_ll * normal_direction[1] + v2_ll * normal_direction[2])1394v_normal_rr = (v1_rr * normal_direction[1] + v2_rr * normal_direction[2])13951396c_f_ll = calc_fast_wavespeed(u_ll, normal_direction, equations)1397c_f_rr = calc_fast_wavespeed(u_rr, normal_direction, equations)13981399# Estimate the min/max eigenvalues in the normal direction1400λ_min = min(v_normal_ll - c_f_ll, v_normal_rr - c_f_rr)1401λ_max = max(v_normal_ll + c_f_ll, v_normal_rr + c_f_rr)14021403return λ_min, λ_max1404end14051406"""1407min_max_speed_einfeldt(u_ll, u_rr, orientation::Integer, equations::IdealGlmMhdEquations2D)14081409Calculate minimum and maximum wave speeds for HLL-type fluxes as in1410- Li (2005)1411An HLLC Riemann solver for magneto-hydrodynamics1412[DOI: 10.1016/j.jcp.2004.08.020](https://doi.org/10.1016/j.jcp.2004.08.020).1413"""1414@inline function min_max_speed_einfeldt(u_ll, u_rr, orientation::Integer,1415equations::IdealGlmMhdEquations2D)1416rho_ll, rho_v1_ll, rho_v2_ll, _ = u_ll1417rho_rr, rho_v1_rr, rho_v2_rr, _ = u_rr14181419# Calculate primitive velocity variables1420v1_ll = rho_v1_ll / rho_ll1421v2_ll = rho_v2_ll / rho_ll14221423v1_rr = rho_v1_rr / rho_rr1424v2_rr = rho_v2_rr / rho_rr14251426# Approximate the left-most and right-most eigenvalues in the Riemann fan1427if orientation == 1 # x-direction1428c_f_ll = calc_fast_wavespeed(u_ll, orientation, equations)1429c_f_rr = calc_fast_wavespeed(u_rr, orientation, equations)1430vel_roe, c_f_roe = calc_fast_wavespeed_roe(u_ll, u_rr, orientation, equations)1431λ_min = min(v1_ll - c_f_ll, vel_roe - c_f_roe)1432λ_max = max(v1_rr + c_f_rr, vel_roe + c_f_roe)1433else # y-direction1434c_f_ll = calc_fast_wavespeed(u_ll, orientation, equations)1435c_f_rr = calc_fast_wavespeed(u_rr, orientation, equations)1436vel_roe, c_f_roe = calc_fast_wavespeed_roe(u_ll, u_rr, orientation, equations)1437λ_min = min(v2_ll - c_f_ll, vel_roe - c_f_roe)1438λ_max = max(v2_rr + c_f_rr, vel_roe + c_f_roe)1439end14401441return λ_min, λ_max1442end14431444@inline function min_max_speed_einfeldt(u_ll, u_rr, normal_direction::AbstractVector,1445equations::IdealGlmMhdEquations2D)1446rho_ll, rho_v1_ll, rho_v2_ll, _ = u_ll1447rho_rr, rho_v1_rr, rho_v2_rr, _ = u_rr14481449# Calculate primitive velocity variables1450v1_ll = rho_v1_ll / rho_ll1451v2_ll = rho_v2_ll / rho_ll14521453v1_rr = rho_v1_rr / rho_rr1454v2_rr = rho_v2_rr / rho_rr14551456v_normal_ll = (v1_ll * normal_direction[1] + v2_ll * normal_direction[2])1457v_normal_rr = (v1_rr * normal_direction[1] + v2_rr * normal_direction[2])14581459c_f_ll = calc_fast_wavespeed(u_ll, normal_direction, equations)1460c_f_rr = calc_fast_wavespeed(u_rr, normal_direction, equations)1461v_roe, c_f_roe = calc_fast_wavespeed_roe(u_ll, u_rr, normal_direction, equations)14621463# Estimate the min/max eigenvalues in the normal direction1464λ_min = min(v_normal_ll - c_f_ll, v_roe - c_f_roe)1465λ_max = max(v_normal_rr + c_f_rr, v_roe + c_f_roe)14661467return λ_min, λ_max1468end14691470# Called inside `FluxRotated` in `numerical_fluxes.jl` so the direction1471# has been normalized prior to this rotation of the state vector1472@inline function rotate_to_x(u, normal_vector, equations::IdealGlmMhdEquations2D)1473# cos and sin of the angle between the x-axis and the normalized normal_vector are1474# the normalized vector's x and y coordinates respectively (see unit circle).1475c = normal_vector[1]1476s = normal_vector[2]14771478# Apply the 2D rotation matrix with normal and tangent directions of the form1479# [ 1 0 0 0 0 0 0 0 0;1480# 0 n_1 n_2 0 0 0 0 0 0;1481# 0 t_1 t_2 0 0 0 0 0 0;1482# 0 0 0 1 0 0 0 0 0;1483# 0 0 0 0 1 0 0 0 0;1484# 0 0 0 0 0 n_1 n_2 0 0;1485# 0 0 0 0 0 t_1 t_2 0 0;1486# 0 0 0 0 0 0 0 1 0;1487# 0 0 0 0 0 0 0 0 1 ]1488# where t_1 = -n_2 and t_2 = n_1.1489# Note for IdealGlmMhdEquations2D only the velocities and magnetic field variables rotate14901491return SVector(u[1],1492c * u[2] + s * u[3],1493-s * u[2] + c * u[3],1494u[4],1495u[5],1496c * u[6] + s * u[7],1497-s * u[6] + c * u[7],1498u[8],1499u[9])1500end15011502# Called inside `FluxRotated` in `numerical_fluxes.jl` so the direction1503# has been normalized prior to this back-rotation of the state vector1504@inline function rotate_from_x(u, normal_vector, equations::IdealGlmMhdEquations2D)1505# cos and sin of the angle between the x-axis and the normalized normal_vector are1506# the normalized vector's x and y coordinates respectively (see unit circle).1507c = normal_vector[1]1508s = normal_vector[2]15091510# Apply the 2D back-rotation matrix with normal and tangent directions of the form1511# [ 1 0 0 0 0 0 0 0 0;1512# 0 n_1 t_1 0 0 0 0 0 0;1513# 0 n_2 t_2 0 0 0 0 0 0;1514# 0 0 0 1 0 0 0 0 0;1515# 0 0 0 0 1 0 0 0 0;1516# 0 0 0 0 0 n_1 t_1 0 0;1517# 0 0 0 0 0 n_2 t_2 0 0;1518# 0 0 0 0 0 0 0 1 0;1519# 0 0 0 0 0 0 0 0 1 ]1520# where t_1 = -n_2 and t_2 = n_1.1521# Note for IdealGlmMhdEquations2D the velocities and magnetic field variables back-rotate15221523return SVector(u[1],1524c * u[2] - s * u[3],1525s * u[2] + c * u[3],1526u[4],1527u[5],1528c * u[6] - s * u[7],1529s * u[6] + c * u[7],1530u[8],1531u[9])1532end15331534@inline function max_abs_speeds(u, equations::IdealGlmMhdEquations2D)1535rho, rho_v1, rho_v2, rho_v3, _ = u1536v1 = rho_v1 / rho1537v2 = rho_v2 / rho1538v3 = rho_v3 / rho1539cf_x_direction = calc_fast_wavespeed(u, 1, equations)1540cf_y_direction = calc_fast_wavespeed(u, 2, equations)15411542return abs(v1) + cf_x_direction, abs(v2) + cf_y_direction1543end15441545# Convert conservative variables to primitive1546@inline function cons2prim(u, equations::IdealGlmMhdEquations2D)1547rho, rho_v1, rho_v2, rho_v3, rho_e, B1, B2, B3, psi = u15481549v1 = rho_v1 / rho1550v2 = rho_v2 / rho1551v3 = rho_v3 / rho1552p = (equations.gamma - 1) * (rho_e -15530.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v31554+ B1 * B1 + B2 * B2 + B3 * B31555+ psi * psi))15561557return SVector(rho, v1, v2, v3, p, B1, B2, B3, psi)1558end15591560# Convert conservative variables to entropy variables1561@inline function cons2entropy(u, equations::IdealGlmMhdEquations2D)1562rho, rho_v1, rho_v2, rho_v3, rho_e, B1, B2, B3, psi = u15631564v1 = rho_v1 / rho1565v2 = rho_v2 / rho1566v3 = rho_v3 / rho1567v_square = v1^2 + v2^2 + v3^21568p = (equations.gamma - 1) *1569(rho_e - 0.5f0 * rho * v_square - 0.5f0 * (B1^2 + B2^2 + B3^2) - 0.5f0 * psi^2)1570s = log(p) - equations.gamma * log(rho)1571rho_p = rho / p15721573w1 = (equations.gamma - s) * equations.inv_gamma_minus_one -15740.5f0 * rho_p * v_square1575w2 = rho_p * v11576w3 = rho_p * v21577w4 = rho_p * v31578w5 = -rho_p1579w6 = rho_p * B11580w7 = rho_p * B21581w8 = rho_p * B31582w9 = rho_p * psi15831584return SVector(w1, w2, w3, w4, w5, w6, w7, w8, w9)1585end15861587# Convert entropy variables to conservative variables1588@inline function entropy2cons(w, equations::IdealGlmMhdEquations2D)1589w1, w2, w3, w4, w5, w6, w7, w8, w9 = w15901591v1 = -w2 / w51592v2 = -w3 / w51593v3 = -w4 / w515941595B1 = -w6 / w51596B2 = -w7 / w51597B3 = -w8 / w51598psi = -w9 / w515991600# This imitates what is done for compressible Euler 3D `entropy2cons`: we convert from1601# the entropy variables for `-rho * s / (gamma - 1)` to the entropy variables for the entropy1602# `-rho * s` used by Hughes, Franca, Mallet (1986).1603@unpack gamma = equations1604V1, V2, V3, V4, V5 = SVector(w1, w2, w3, w4, w5) * (gamma - 1)1605s = gamma - V1 + (V2^2 + V3^2 + V4^2) / (2 * V5)1606rho_iota = ((gamma - 1) / (-V5)^gamma)^(equations.inv_gamma_minus_one) *1607exp(-s * equations.inv_gamma_minus_one)1608rho = -rho_iota * V51609p = -rho / w516101611return prim2cons(SVector(rho, v1, v2, v3, p, B1, B2, B3, psi), equations)1612end16131614# Convert primitive to conservative variables1615@inline function prim2cons(prim, equations::IdealGlmMhdEquations2D)1616rho, v1, v2, v3, p, B1, B2, B3, psi = prim16171618rho_v1 = rho * v11619rho_v2 = rho * v21620rho_v3 = rho * v31621rho_e = p * equations.inv_gamma_minus_one +16220.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3) +16230.5f0 * (B1^2 + B2^2 + B3^2) + 0.5f0 * psi^216241625return SVector(rho, rho_v1, rho_v2, rho_v3, rho_e, B1, B2, B3, psi)1626end16271628@inline function density(u, equations::IdealGlmMhdEquations2D)1629rho, rho_v1, rho_v2, rho_v3, rho_e, B1, B2, B3, psi = u1630return rho1631end16321633@inline function velocity(u, equations::IdealGlmMhdEquations2D)1634rho = u[1]1635v1 = u[2] / rho1636v2 = u[3] / rho1637v3 = u[4] / rho1638return SVector(v1, v2, v3)1639end16401641@inline function velocity(u, orientation::Int, equations::IdealGlmMhdEquations2D)1642rho = u[1]1643v = u[orientation + 1] / rho1644return v1645end16461647@inline function pressure(u, equations::IdealGlmMhdEquations2D)1648rho, rho_v1, rho_v2, rho_v3, rho_e, B1, B2, B3, psi = u1649p = (equations.gamma - 1) * (rho_e - 0.5f0 * (rho_v1^2 + rho_v2^2 + rho_v3^2) / rho1650-16510.5f0 * (B1^2 + B2^2 + B3^2)1652-16530.5f0 * psi^2)1654return p1655end16561657# Transformation from conservative variables u to d(p)/d(u)1658@inline function gradient_conservative(::typeof(pressure),1659u, equations::IdealGlmMhdEquations2D)1660rho, rho_v1, rho_v2, rho_v3, rho_e, B1, B2, B3, psi = u16611662v1 = rho_v1 / rho1663v2 = rho_v2 / rho1664v3 = rho_v3 / rho1665v_square = v1^2 + v2^2 + v3^216661667return (equations.gamma - 1) *1668SVector(0.5f0 * v_square, -v1, -v2, -v3, 1, -B1, -B2, -B3, -psi)1669end16701671@inline function density_pressure(u, equations::IdealGlmMhdEquations2D)1672rho, rho_v1, rho_v2, rho_v3, rho_e, B1, B2, B3, psi = u1673p = (equations.gamma - 1) * (rho_e - 0.5f0 * (rho_v1^2 + rho_v2^2 + rho_v3^2) / rho1674-16750.5f0 * (B1^2 + B2^2 + B3^2)1676-16770.5f0 * psi^2)1678return rho * p1679end16801681# Compute the fastest wave speed for ideal MHD equations: c_f, the fast magnetoacoustic eigenvalue1682@inline function calc_fast_wavespeed(cons, orientation::Integer,1683equations::IdealGlmMhdEquations2D)1684rho, rho_v1, rho_v2, rho_v3, rho_e, B1, B2, B3, psi = cons1685v1 = rho_v1 / rho1686v2 = rho_v2 / rho1687v3 = rho_v3 / rho1688kin_en = 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3)1689mag_en = 0.5f0 * (B1 * B1 + B2 * B2 + B3 * B3)1690p = (equations.gamma - 1) * (rho_e - kin_en - mag_en - 0.5f0 * psi^2)1691a_square = equations.gamma * p / rho1692sqrt_rho = sqrt(rho)1693b1 = B1 / sqrt_rho1694b2 = B2 / sqrt_rho1695b3 = B3 / sqrt_rho1696b_square = b1 * b1 + b2 * b2 + b3 * b31697if orientation == 1 # x-direction1698c_f = sqrt(0.5f0 * (a_square + b_square) +16990.5f0 * sqrt((a_square + b_square)^2 - 4 * a_square * b1^2))1700else1701c_f = sqrt(0.5f0 * (a_square + b_square) +17020.5f0 * sqrt((a_square + b_square)^2 - 4 * a_square * b2^2))1703end1704return c_f1705end17061707@inline function calc_fast_wavespeed(cons, normal_direction::AbstractVector,1708equations::IdealGlmMhdEquations2D)1709rho, rho_v1, rho_v2, rho_v3, rho_e, B1, B2, B3, psi = cons1710v1 = rho_v1 / rho1711v2 = rho_v2 / rho1712v3 = rho_v3 / rho1713kin_en = 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3)1714mag_en = 0.5f0 * (B1 * B1 + B2 * B2 + B3 * B3)1715p = (equations.gamma - 1) * (rho_e - kin_en - mag_en - 0.5f0 * psi^2)1716a_square = equations.gamma * p / rho1717sqrt_rho = sqrt(rho)1718b1 = B1 / sqrt_rho1719b2 = B2 / sqrt_rho1720b3 = B3 / sqrt_rho1721b_square = b1 * b1 + b2 * b2 + b3 * b31722norm_squared = (normal_direction[1] * normal_direction[1] +1723normal_direction[2] * normal_direction[2])1724b_dot_n_squared = (b1 * normal_direction[1] +1725b2 * normal_direction[2])^2 / norm_squared17261727c_f = sqrt((0.5f0 * (a_square + b_square) +17280.5f0 * sqrt((a_square + b_square)^2 - 4 * a_square * b_dot_n_squared)) *1729norm_squared)1730return c_f1731end17321733"""1734calc_fast_wavespeed_roe(u_ll, u_rr, orientation_or_normal_direction, equations::IdealGlmMhdEquations2D)17351736Compute the fast magnetoacoustic wave speed using Roe averages1737as given by1738- Cargo and Gallice (1997)1739Roe Matrices for Ideal MHD and Systematic Construction1740of Roe Matrices for Systems of Conservation Laws1741[DOI: 10.1006/jcph.1997.5773](https://doi.org/10.1006/jcph.1997.5773)1742"""1743@inline function calc_fast_wavespeed_roe(u_ll, u_rr, orientation::Integer,1744equations::IdealGlmMhdEquations2D)1745rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll1746rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr17471748# Calculate primitive variables1749v1_ll = rho_v1_ll / rho_ll1750v2_ll = rho_v2_ll / rho_ll1751v3_ll = rho_v3_ll / rho_ll1752kin_en_ll = 0.5f0 * (rho_v1_ll * v1_ll + rho_v2_ll * v2_ll + rho_v3_ll * v3_ll)1753mag_norm_ll = B1_ll * B1_ll + B2_ll * B2_ll + B3_ll * B3_ll1754p_ll = (equations.gamma - 1) *1755(rho_e_ll - kin_en_ll - 0.5f0 * mag_norm_ll - 0.5f0 * psi_ll^2)17561757v1_rr = rho_v1_rr / rho_rr1758v2_rr = rho_v2_rr / rho_rr1759v3_rr = rho_v3_rr / rho_rr1760kin_en_rr = 0.5f0 * (rho_v1_rr * v1_rr + rho_v2_rr * v2_rr + rho_v3_rr * v3_rr)1761mag_norm_rr = B1_rr * B1_rr + B2_rr * B2_rr + B3_rr * B3_rr1762p_rr = (equations.gamma - 1) *1763(rho_e_rr - kin_en_rr - 0.5f0 * mag_norm_rr - 0.5f0 * psi_rr^2)17641765# compute total pressure which is thermal + magnetic pressures1766p_total_ll = p_ll + 0.5f0 * mag_norm_ll1767p_total_rr = p_rr + 0.5f0 * mag_norm_rr17681769# compute the Roe density averages1770sqrt_rho_ll = sqrt(rho_ll)1771sqrt_rho_rr = sqrt(rho_rr)1772inv_sqrt_rho_add = 1 / (sqrt_rho_ll + sqrt_rho_rr)1773inv_sqrt_rho_prod = 1 / (sqrt_rho_ll * sqrt_rho_rr)1774rho_ll_roe = sqrt_rho_ll * inv_sqrt_rho_add1775rho_rr_roe = sqrt_rho_rr * inv_sqrt_rho_add1776# Roe averages1777# velocities and magnetic fields1778v1_roe = v1_ll * rho_ll_roe + v1_rr * rho_rr_roe1779v2_roe = v2_ll * rho_ll_roe + v2_rr * rho_rr_roe1780v3_roe = v3_ll * rho_ll_roe + v3_rr * rho_rr_roe1781B1_roe = B1_ll * rho_ll_roe + B1_rr * rho_rr_roe1782B2_roe = B2_ll * rho_ll_roe + B2_rr * rho_rr_roe1783B3_roe = B3_ll * rho_ll_roe + B3_rr * rho_rr_roe1784# enthalpy1785H_ll = (rho_e_ll + p_total_ll) / rho_ll1786H_rr = (rho_e_rr + p_total_rr) / rho_rr1787H_roe = H_ll * rho_ll_roe + H_rr * rho_rr_roe1788# temporary variable see equation (4.12) in Cargo and Gallice1789X = 0.5f0 * ((B1_ll - B1_rr)^2 + (B2_ll - B2_rr)^2 + (B3_ll - B3_rr)^2) *1790inv_sqrt_rho_add^21791# averaged components needed to compute c_f, the fast magnetoacoustic wave speed1792b_square_roe = (B1_roe^2 + B2_roe^2 + B3_roe^2) * inv_sqrt_rho_prod # scaled magnectic sum1793a_square_roe = ((2 - equations.gamma) * X +1794(equations.gamma - 1) *1795(H_roe - 0.5f0 * (v1_roe^2 + v2_roe^2 + v3_roe^2) -1796b_square_roe)) # acoustic speed1797# finally compute the average wave speed and set the output velocity (depends on orientation)1798if orientation == 1 # x-direction1799c_a_roe = B1_roe^2 * inv_sqrt_rho_prod # (squared) Alfvén wave speed1800a_star_roe = sqrt((a_square_roe + b_square_roe)^2 -18014 * a_square_roe * c_a_roe)1802c_f_roe = sqrt(0.5f0 * (a_square_roe + b_square_roe + a_star_roe))1803vel_out_roe = v1_roe1804else # y-direction1805c_a_roe = B2_roe^2 * inv_sqrt_rho_prod # (squared) Alfvén wave speed1806a_star_roe = sqrt((a_square_roe + b_square_roe)^2 -18074 * a_square_roe * c_a_roe)1808c_f_roe = sqrt(0.5f0 * (a_square_roe + b_square_roe + a_star_roe))1809vel_out_roe = v2_roe1810end18111812return vel_out_roe, c_f_roe1813end18141815@inline function calc_fast_wavespeed_roe(u_ll, u_rr, normal_direction::AbstractVector,1816equations::IdealGlmMhdEquations2D)1817rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll1818rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr18191820# Calculate primitive variables1821v1_ll = rho_v1_ll / rho_ll1822v2_ll = rho_v2_ll / rho_ll1823v3_ll = rho_v3_ll / rho_ll1824kin_en_ll = 0.5f0 * (rho_v1_ll * v1_ll + rho_v2_ll * v2_ll + rho_v3_ll * v3_ll)1825mag_norm_ll = B1_ll * B1_ll + B2_ll * B2_ll + B3_ll * B3_ll1826p_ll = (equations.gamma - 1) *1827(rho_e_ll - kin_en_ll - 0.5f0 * mag_norm_ll - 0.5f0 * psi_ll^2)18281829v1_rr = rho_v1_rr / rho_rr1830v2_rr = rho_v2_rr / rho_rr1831v3_rr = rho_v3_rr / rho_rr1832kin_en_rr = 0.5f0 * (rho_v1_rr * v1_rr + rho_v2_rr * v2_rr + rho_v3_rr * v3_rr)1833mag_norm_rr = B1_rr * B1_rr + B2_rr * B2_rr + B3_rr * B3_rr1834p_rr = (equations.gamma - 1) *1835(rho_e_rr - kin_en_rr - 0.5f0 * mag_norm_rr - 0.5f0 * psi_rr^2)18361837# compute total pressure which is thermal + magnetic pressures1838p_total_ll = p_ll + 0.5f0 * mag_norm_ll1839p_total_rr = p_rr + 0.5f0 * mag_norm_rr18401841# compute the Roe density averages1842sqrt_rho_ll = sqrt(rho_ll)1843sqrt_rho_rr = sqrt(rho_rr)1844inv_sqrt_rho_add = 1 / (sqrt_rho_ll + sqrt_rho_rr)1845inv_sqrt_rho_prod = 1 / (sqrt_rho_ll * sqrt_rho_rr)1846rho_ll_roe = sqrt_rho_ll * inv_sqrt_rho_add1847rho_rr_roe = sqrt_rho_rr * inv_sqrt_rho_add1848# Roe averages1849# velocities and magnetic fields1850v1_roe = v1_ll * rho_ll_roe + v1_rr * rho_rr_roe1851v2_roe = v2_ll * rho_ll_roe + v2_rr * rho_rr_roe1852v3_roe = v3_ll * rho_ll_roe + v3_rr * rho_rr_roe1853B1_roe = B1_ll * rho_ll_roe + B1_rr * rho_rr_roe1854B2_roe = B2_ll * rho_ll_roe + B2_rr * rho_rr_roe1855B3_roe = B3_ll * rho_ll_roe + B3_rr * rho_rr_roe1856# enthalpy1857H_ll = (rho_e_ll + p_total_ll) / rho_ll1858H_rr = (rho_e_rr + p_total_rr) / rho_rr1859H_roe = H_ll * rho_ll_roe + H_rr * rho_rr_roe1860# temporary variable see equation (4.12) in Cargo and Gallice1861X = 0.5f0 * ((B1_ll - B1_rr)^2 + (B2_ll - B2_rr)^2 + (B3_ll - B3_rr)^2) *1862inv_sqrt_rho_add^21863# averaged components needed to compute c_f, the fast magnetoacoustic wave speed1864b_square_roe = (B1_roe^2 + B2_roe^2 + B3_roe^2) * inv_sqrt_rho_prod # scaled magnectic sum1865a_square_roe = ((2 - equations.gamma) * X +1866(equations.gamma - 1) *1867(H_roe - 0.5f0 * (v1_roe^2 + v2_roe^2 + v3_roe^2) -1868b_square_roe)) # acoustic speed18691870# finally compute the average wave speed and set the output velocity (depends on orientation)1871norm_squared = (normal_direction[1] * normal_direction[1] +1872normal_direction[2] * normal_direction[2])1873B_roe_dot_n_squared = (B1_roe * normal_direction[1] +1874B2_roe * normal_direction[2])^2 / norm_squared18751876c_a_roe = B_roe_dot_n_squared * inv_sqrt_rho_prod # (squared) Alfvén wave speed1877a_star_roe = sqrt((a_square_roe + b_square_roe)^2 - 4 * a_square_roe * c_a_roe)1878c_f_roe = sqrt(0.5f0 * (a_square_roe + b_square_roe + a_star_roe) * norm_squared)1879vel_out_roe = (v1_roe * normal_direction[1] +1880v2_roe * normal_direction[2])18811882return vel_out_roe, c_f_roe1883end18841885# Calculate thermodynamic entropy for a conservative state `cons`1886@inline function entropy_thermodynamic(cons, equations::IdealGlmMhdEquations2D)1887# Pressure1888p = (equations.gamma - 1) *1889(cons[5] - 0.5f0 * (cons[2]^2 + cons[3]^2 + cons[4]^2) / cons[1]1890-18910.5f0 * (cons[6]^2 + cons[7]^2 + cons[8]^2)1892-18930.5f0 * cons[9]^2)18941895# Thermodynamic entropy1896s = log(p) - equations.gamma * log(cons[1])18971898return s1899end19001901# Calculate mathematical entropy for a conservative state `cons`1902@inline function entropy_math(cons, equations::IdealGlmMhdEquations2D)1903S = -entropy_thermodynamic(cons, equations) * cons[1] *1904equations.inv_gamma_minus_one19051906return S1907end19081909# Default entropy is the mathematical entropy1910@inline entropy(cons, equations::IdealGlmMhdEquations2D) = entropy_math(cons, equations)19111912# Calculate total energy for a conservative state `cons`1913@inline energy_total(cons, ::IdealGlmMhdEquations2D) = cons[5]19141915# Calculate kinetic energy for a conservative state `cons`1916@inline function energy_kinetic(cons, equations::IdealGlmMhdEquations2D)1917return 0.5f0 * (cons[2]^2 + cons[3]^2 + cons[4]^2) / cons[1]1918end19191920# Calculate the magnetic energy for a conservative state `cons'.1921# OBS! For non-dinmensional form of the ideal MHD magnetic pressure ≡ magnetic energy1922@inline function energy_magnetic(cons, ::IdealGlmMhdEquations2D)1923return 0.5f0 * (cons[6]^2 + cons[7]^2 + cons[8]^2)1924end19251926# Calculate internal energy for a conservative state `cons`1927@inline function energy_internal(cons, equations::IdealGlmMhdEquations2D)1928return (energy_total(cons, equations)1929-1930energy_kinetic(cons, equations)1931-1932energy_magnetic(cons, equations)1933-1934cons[9]^2 / 2)1935end19361937# State validation for Newton-bisection method of subcell IDP limiting1938@inline function Base.isvalid(u, equations::IdealGlmMhdEquations2D)1939p = pressure(u, equations)1940if u[1] <= 0 || p <= 01941return false1942end1943return true1944end19451946# Calculate the cross helicity (\vec{v}⋅\vec{B}) for a conservative state `cons'1947@inline function cross_helicity(cons, ::IdealGlmMhdEquations2D)1948return (cons[2] * cons[6] + cons[3] * cons[7] + cons[4] * cons[8]) / cons[1]1949end1950end # @muladd195119521953