Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/equations/ideal_glm_mhd_multiion_2d.jl
2055 views
1
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
2
# Since these FMAs can increase the performance of many numerical algorithms,
3
# we need to opt-in explicitly.
4
# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
5
@muladd begin
6
#! format: noindent
7
8
@doc raw"""
9
IdealGlmMhdMultiIonEquations2D(; gammas, charge_to_mass,
10
gas_constants = zero(SVector{length(gammas),
11
eltype(gammas)}),
12
molar_masses = zero(SVector{length(gammas),
13
eltype(gammas)}),
14
ion_ion_collision_constants = zeros(eltype(gammas),
15
length(gammas),
16
length(gammas)),
17
ion_electron_collision_constants = zero(SVector{length(gammas),
18
eltype(gammas)}),
19
electron_pressure = electron_pressure_zero,
20
electron_temperature = electron_pressure_zero,
21
initial_c_h = NaN)
22
23
The ideal compressible multi-ion MHD equations in two space dimensions augmented with a
24
generalized Langange multipliers (GLM) divergence-cleaning technique. This is a
25
multi-species variant of the ideal GLM-MHD equations for calorically perfect plasmas
26
with independent momentum and energy equations for each ion species. This implementation
27
assumes that the equations are non-dimensionalized such, that the vacuum permeability is ``\mu_0 = 1``.
28
29
In case of more than one ion species, the specific heat capacity ratios `gammas` and the charge-to-mass
30
ratios `charge_to_mass` should be passed as tuples, e.g., `gammas=(1.4, 1.667)`.
31
32
The ion-ion and ion-electron collision source terms can be computed using the functions
33
[`source_terms_collision_ion_ion`](@ref) and [`source_terms_collision_ion_electron`](@ref), respectively.
34
35
For ion-ion collision terms, the optional keyword arguments `gas_constants`, `molar_masses`, and `ion_ion_collision_constants`
36
must be provided. For ion-electron collision terms, the optional keyword arguments `gas_constants`, `molar_masses`,
37
`ion_electron_collision_constants`, and `electron_temperature` are required.
38
39
- **`gas_constants`** and **`molar_masses`** are tuples containing the gas constant and molar mass of each
40
ion species, respectively. The **molar masses** can be provided in any unit system, as they are only used to
41
compute ratios and are independent of the other arguments.
42
43
- **`ion_ion_collision_constants`** is a symmetric matrix that contains coefficients to compute the collision
44
frequencies between pairs of ion species. For example, `ion_ion_collision_constants[2, 3]` contains the collision
45
coefficient for collisions between the ion species 2 and the ion species 3. These constants are derived using the kinetic
46
theory of gases (see, e.g., *Schunk & Nagy, 2000*). They are related to the collision coefficients ``B_{st}`` listed
47
in Table 4.3 of *Schunk & Nagy (2000)*, but are scaled by the molecular mass of ion species ``t`` (i.e.,
48
`ion_ion_collision_constants[2, 3] = ` ``B_{st}/m_{t}``) and must be provided in consistent physical units
49
(Schunk & Nagy use ``cm^3 K^{3/2} / s``).
50
See [`source_terms_collision_ion_ion`](@ref) for more details on how these constants are used to compute the collision
51
frequencies.
52
53
- **`ion_electron_collision_constants`** is a tuple containing coefficients to compute the ion-electron collision frequency
54
for each ion species. They correspond to the collision coefficients `B_{se}` divided by the elementary charge.
55
The ion-electron collision frequencies can also be computed using the kinetic theory
56
of gases (see, e.g., *Schunk & Nagy, 2000*). See [`source_terms_collision_ion_electron`](@ref) for more details on how these
57
constants are used to compute the collision frequencies.
58
59
- **`electron_temperature`** is a function with the signature `electron_temperature(u, equations)` that can be used
60
compute the electron temperature as a function of the state `u`. The electron temperature is relevant for the computation
61
of the ion-electron collision source terms.
62
63
The argument `electron_pressure` can be used to pass a function that computes the electron
64
pressure as a function of the state `u` with the signature `electron_pressure(u, equations)`.
65
The gradient of the electron pressure is relevant for the computation of the Lorentz flux
66
and non-conservative term. By default, the electron pressure is zero.
67
68
The argument `initial_c_h` can be used to set the GLM divergence-cleaning speed. Note that
69
`initial_c_h = 0` deactivates the divergence cleaning. The callback [`GlmSpeedCallback`](@ref)
70
can be used to adjust the GLM divergence-cleaning speed according to the time-step size.
71
72
References:
73
- G. Toth, A. Glocer, Y. Ma, D. Najib, Multi-Ion Magnetohydrodynamics 429 (2010). Numerical
74
Modeling of Space Plasma Flows, 213–218. [Bib Code: Code: 2010ASPC..429..213T](https://adsabs.harvard.edu/full/2010ASPC..429..213T).
75
- A. Rueda-Ramírez, A. Sikstel, G. Gassner, An Entropy-Stable Discontinuous Galerkin Discretization
76
of the Ideal Multi-Ion Magnetohydrodynamics System (2024). Journal of Computational Physics.
77
[DOI: 10.1016/j.jcp.2024.113655](https://doi.org/10.1016/j.jcp.2024.113655).
78
- Schunk, R. W., & Nagy, A. F. (2000). Ionospheres: Physics, plasma physics, and chemistry.
79
Cambridge university press. [DOI: 10.1017/CBO9780511635342](https://doi.org/10.1017/CBO9780511635342).
80
81
!!! info "The multi-ion GLM-MHD equations require source terms"
82
In case of more than one ion species, the multi-ion GLM-MHD equations should ALWAYS be used
83
with [`source_terms_lorentz`](@ref).
84
"""
85
mutable struct IdealGlmMhdMultiIonEquations2D{NVARS, NCOMP, RealT <: Real,
86
ElectronPressure, ElectronTemperature} <:
87
AbstractIdealGlmMhdMultiIonEquations{2, NVARS, NCOMP}
88
gammas::SVector{NCOMP, RealT} # Heat capacity ratios
89
charge_to_mass::SVector{NCOMP, RealT} # Charge to mass ratios
90
gas_constants::SVector{NCOMP, RealT} # Specific gas constants
91
molar_masses::SVector{NCOMP, RealT} # Molar masses (can be provided in any units as they are only used to compute ratios)
92
ion_ion_collision_constants::Array{RealT, 2} # Symmetric matrix of collision frequency coefficients
93
ion_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.5
94
electron_pressure::ElectronPressure # Function to compute the electron pressure
95
electron_temperature::ElectronTemperature # Function to compute the electron temperature
96
c_h::RealT # GLM cleaning speed
97
98
function IdealGlmMhdMultiIonEquations2D{NVARS, NCOMP, RealT, ElectronPressure,
99
ElectronTemperature}(gammas
100
::SVector{NCOMP,
101
RealT},
102
charge_to_mass
103
::SVector{NCOMP,
104
RealT},
105
gas_constants
106
::SVector{NCOMP,
107
RealT},
108
molar_masses
109
::SVector{NCOMP,
110
RealT},
111
ion_ion_collision_constants
112
::Array{RealT, 2},
113
ion_electron_collision_constants
114
::SVector{NCOMP,
115
RealT},
116
electron_pressure
117
::ElectronPressure,
118
electron_temperature
119
::ElectronTemperature,
120
c_h::RealT) where
121
{NVARS, NCOMP, RealT <: Real, ElectronPressure, ElectronTemperature}
122
NCOMP >= 1 ||
123
throw(DimensionMismatch("`gammas` and `charge_to_mass` have to be filled with at least one value"))
124
125
new(gammas, charge_to_mass, gas_constants, molar_masses,
126
ion_ion_collision_constants,
127
ion_electron_collision_constants, electron_pressure, electron_temperature,
128
c_h)
129
end
130
end
131
132
function IdealGlmMhdMultiIonEquations2D(; gammas, charge_to_mass,
133
gas_constants = zero(SVector{length(gammas),
134
eltype(gammas)}),
135
molar_masses = zero(SVector{length(gammas),
136
eltype(gammas)}),
137
ion_ion_collision_constants = zeros(eltype(gammas),
138
length(gammas),
139
length(gammas)),
140
ion_electron_collision_constants = zero(SVector{length(gammas),
141
eltype(gammas)}),
142
electron_pressure = electron_pressure_zero,
143
electron_temperature = electron_pressure_zero,
144
initial_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...)
150
RealT = promote_type(eltype(_gammas), eltype(_charge_to_mass),
151
eltype(_gas_constants), eltype(_molar_masses),
152
eltype(ion_ion_collision_constants),
153
eltype(_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))
161
162
NVARS = length(_gammas) * 5 + 4
163
NCOMP = length(_gammas)
164
165
return IdealGlmMhdMultiIonEquations2D{NVARS, NCOMP, RealT,
166
typeof(electron_pressure),
167
typeof(electron_temperature)}(__gammas,
168
__charge_to_mass,
169
__gas_constants,
170
__molar_masses,
171
__ion_ion_collision_constants,
172
__ion_electron_collision_constants,
173
electron_pressure,
174
electron_temperature,
175
initial_c_h)
176
end
177
178
# Outer constructor for `@reset` works correctly
179
function IdealGlmMhdMultiIonEquations2D(gammas, charge_to_mass, gas_constants,
180
molar_masses, ion_ion_collision_constants,
181
ion_electron_collision_constants,
182
electron_pressure,
183
electron_temperature,
184
c_h)
185
return IdealGlmMhdMultiIonEquations2D(gammas = gammas,
186
charge_to_mass = charge_to_mass,
187
gas_constants = gas_constants,
188
molar_masses = molar_masses,
189
ion_ion_collision_constants = ion_ion_collision_constants,
190
ion_electron_collision_constants = ion_electron_collision_constants,
191
electron_pressure = electron_pressure,
192
electron_temperature = electron_temperature,
193
initial_c_h = c_h)
194
end
195
196
@inline function Base.real(::IdealGlmMhdMultiIonEquations2D{NVARS, NCOMP, RealT}) where {
197
NVARS,
198
NCOMP,
199
RealT
200
}
201
RealT
202
end
203
204
"""
205
initial_condition_weak_blast_wave(x, t, equations::IdealGlmMhdMultiIonEquations2D)
206
207
A weak blast wave (adapted to multi-ion MHD) from
208
- Hennemann, S., Rueda-Ramírez, A. M., Hindenlang, F. J., & Gassner, G. J. (2021). A provably entropy
209
stable subcell shock capturing approach for high order split form DG for the compressible Euler equations.
210
Journal 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
"""
213
function initial_condition_weak_blast_wave(x, t,
214
equations::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 fields
217
# Set up polar coordinates
218
RealT = eltype(x)
219
inicenter = (0, 0)
220
x_norm = x[1] - inicenter[1]
221
y_norm = x[2] - inicenter[2]
222
r = sqrt(x_norm^2 + y_norm^2)
223
phi = atan(y_norm, x_norm)
224
225
# Calculate primitive variables
226
rho = r > 0.5f0 ? one(RealT) : convert(RealT, 1.1691)
227
v1 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos(phi)
228
v2 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * sin(phi)
229
p = r > 0.5f0 ? one(RealT) : convert(RealT, 1.245)
230
231
prim = zero(MVector{nvariables(equations), RealT})
232
prim[1] = 1
233
prim[2] = 1
234
prim[3] = 1
235
for k in eachcomponent(equations)
236
# We initialize each species with a fraction of the total density `rho`, such
237
# that the sum of the densities is `rho := density(prim, equations)`. The density of
238
# a species is double the density of the next species.
239
fraction = 2^(k - 1) * (1 - 2) / (1 - 2^ncomponents(equations))
240
set_component!(prim, k,
241
fraction * rho, v1,
242
v2, 0, p, equations)
243
end
244
245
return prim2cons(SVector(prim), equations)
246
end
247
248
# 2D flux of the multi-ion GLM-MHD equations in the direction `orientation`
249
@inline function flux(u, orientation::Integer,
250
equations::IdealGlmMhdMultiIonEquations2D)
251
B1, B2, B3 = magnetic_field(u, equations)
252
psi = divergence_cleaning_field(u, equations)
253
254
v1_plus, v2_plus, v3_plus, vk1_plus, vk2_plus, vk3_plus = charge_averaged_velocities(u,
255
equations)
256
257
mag_en = 0.5f0 * (B1^2 + B2^2 + B3^2)
258
div_clean_energy = 0.5f0 * psi^2
259
260
f = zero(MVector{nvariables(equations), eltype(u)})
261
262
if orientation == 1
263
f[1] = equations.c_h * psi
264
f[2] = v1_plus * B2 - v2_plus * B1
265
f[3] = v1_plus * B3 - v3_plus * B1
266
267
for k in eachcomponent(equations)
268
rho, rho_v1, rho_v2, rho_v3, rho_e = get_component(k, u, equations)
269
rho_inv = 1 / rho
270
v1 = rho_v1 * rho_inv
271
v2 = rho_v2 * rho_inv
272
v3 = rho_v3 * rho_inv
273
kin_en = 0.5f0 * rho * (v1^2 + v2^2 + v3^2)
274
275
gamma = equations.gammas[k]
276
p = (gamma - 1) * (rho_e - kin_en - mag_en - div_clean_energy)
277
278
f1 = rho_v1
279
f2 = rho_v1 * v1 + p
280
f3 = rho_v1 * v2
281
f4 = rho_v1 * v3
282
f5 = (kin_en + gamma * p / (gamma - 1)) * v1 + 2 * mag_en * vk1_plus[k] -
283
B1 * (vk1_plus[k] * B1 + vk2_plus[k] * B2 + vk3_plus[k] * B3) +
284
equations.c_h * psi * B1
285
286
set_component!(f, k, f1, f2, f3, f4, f5, equations)
287
end
288
f[end] = equations.c_h * B1
289
else #if orientation == 2
290
f[1] = v2_plus * B1 - v1_plus * B2
291
f[2] = equations.c_h * psi
292
f[3] = v2_plus * B3 - v3_plus * B2
293
294
for k in eachcomponent(equations)
295
rho, rho_v1, rho_v2, rho_v3, rho_e = get_component(k, u, equations)
296
rho_inv = 1 / rho
297
v1 = rho_v1 * rho_inv
298
v2 = rho_v2 * rho_inv
299
v3 = rho_v3 * rho_inv
300
kin_en = 0.5f0 * rho * (v1^2 + v2^2 + v3^2)
301
302
gamma = equations.gammas[k]
303
p = (gamma - 1) * (rho_e - kin_en - mag_en - div_clean_energy)
304
305
f1 = rho_v2
306
f2 = rho_v2 * v1
307
f3 = rho_v2 * v2 + p
308
f4 = rho_v2 * v3
309
f5 = (kin_en + gamma * p / (gamma - 1)) * v2 + 2 * mag_en * vk2_plus[k] -
310
B2 * (vk1_plus[k] * B1 + vk2_plus[k] * B2 + vk3_plus[k] * B3) +
311
equations.c_h * psi * B2
312
313
set_component!(f, k, f1, f2, f3, f4, f5, equations)
314
end
315
f[end] = equations.c_h * B2
316
end
317
318
return SVector(f)
319
end
320
321
"""
322
flux_nonconservative_ruedaramirez_etal(u_ll, u_rr,
323
orientation::Integer,
324
equations::IdealGlmMhdMultiIonEquations2D)
325
326
Entropy-conserving non-conservative two-point "flux" as described in
327
- A. Rueda-Ramírez, A. Sikstel, G. Gassner, An Entropy-Stable Discontinuous Galerkin Discretization
328
of the Ideal Multi-Ion Magnetohydrodynamics System (2024). Journal of Computational Physics.
329
[DOI: 10.1016/j.jcp.2024.113655](https://doi.org/10.1016/j.jcp.2024.113655).
330
331
!!! info "Usage and Scaling of Non-Conservative Fluxes in Trixi.jl"
332
The non-conservative fluxes derived in the reference above are written as the product
333
of local and symmetric parts and are meant to be used in the same way as the conservative
334
fluxes (i.e., flux + flux_noncons in both volume and surface integrals). In this routine,
335
the fluxes are multiplied by 2 because the non-conservative fluxes are always multiplied
336
by 0.5 whenever they are used in the Trixi code.
337
338
The term is composed of four individual non-conservative terms:
339
1. The Godunov-Powell term, which arises for plasmas with non-vanishing magnetic field divergence, and
340
is evaluated as a function of the charge-averaged velocity.
341
2. The Lorentz-force term, which becomes a conservative term in the limit of one ion species for vanishing
342
electron pressure gradients.
343
3. The "multi-ion" term, which vanishes in the limit of one ion species.
344
4. The GLM term, which is needed for Galilean invariance.
345
"""
346
@inline function flux_nonconservative_ruedaramirez_etal(u_ll, u_rr,
347
orientation::Integer,
348
equations::IdealGlmMhdMultiIonEquations2D)
349
@unpack charge_to_mass = equations
350
# Unpack left and right states to get the magnetic field
351
B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)
352
B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)
353
psi_ll = divergence_cleaning_field(u_ll, equations)
354
psi_rr = divergence_cleaning_field(u_rr, equations)
355
356
# Compute important averages
357
B1_avg = 0.5f0 * (B1_ll + B1_rr)
358
B2_avg = 0.5f0 * (B2_ll + B2_rr)
359
B3_avg = 0.5f0 * (B3_ll + B3_rr)
360
mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2
361
mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2
362
mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)
363
psi_avg = 0.5f0 * (psi_ll + psi_rr)
364
365
# Mean electron pressure
366
pe_ll = equations.electron_pressure(u_ll, equations)
367
pe_rr = equations.electron_pressure(u_rr, equations)
368
pe_mean = 0.5f0 * (pe_ll + pe_rr)
369
370
# Compute charge ratio of u_ll
371
charge_ratio_ll = zero(MVector{ncomponents(equations), eltype(u_ll)})
372
total_electron_charge = zero(eltype(u_ll))
373
for k in eachcomponent(equations)
374
rho_k = u_ll[3 + (k - 1) * 5 + 1]
375
charge_ratio_ll[k] = rho_k * charge_to_mass[k]
376
total_electron_charge += charge_ratio_ll[k]
377
end
378
charge_ratio_ll ./= total_electron_charge
379
380
# Compute auxiliary variables
381
v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll, vk3_plus_ll = charge_averaged_velocities(u_ll,
382
equations)
383
v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr, vk3_plus_rr = charge_averaged_velocities(u_rr,
384
equations)
385
386
f = zero(MVector{nvariables(equations), eltype(u_ll)})
387
388
if orientation == 1
389
# Entries of Godunov-Powell term for induction equation (multiply by 2 because the non-conservative flux is
390
# multiplied by 0.5 whenever it's used in the Trixi code)
391
f[1] = 2 * v1_plus_ll * B1_avg
392
f[2] = 2 * v2_plus_ll * B1_avg
393
f[3] = 2 * v3_plus_ll * B1_avg
394
395
for k in eachcomponent(equations)
396
# Compute term Lorentz term
397
f2 = charge_ratio_ll[k] * (0.5f0 * mag_norm_avg - B1_avg * B1_avg + pe_mean)
398
f3 = charge_ratio_ll[k] * (-B1_avg * B2_avg)
399
f4 = charge_ratio_ll[k] * (-B1_avg * B3_avg)
400
f5 = vk1_plus_ll[k] * pe_mean
401
402
# Compute multi-ion term (vanishes for NCOMP==1)
403
vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]
404
vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]
405
vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]
406
vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]
407
vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]
408
vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]
409
vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)
410
vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)
411
vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)
412
f5 += (B2_ll * (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) +
413
B3_ll * (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg))
414
415
# Compute Godunov-Powell term
416
f2 += charge_ratio_ll[k] * B1_ll * B1_avg
417
f3 += charge_ratio_ll[k] * B2_ll * B1_avg
418
f4 += charge_ratio_ll[k] * B3_ll * B1_avg
419
f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *
420
B1_avg
421
422
# Compute GLM term for the energy
423
f5 += v1_plus_ll * psi_ll * psi_avg
424
425
# Add to the flux vector (multiply by 2 because the non-conservative flux is
426
# multiplied by 0.5 whenever it's used in the Trixi code)
427
set_component!(f, k, 0, 2 * f2, 2 * f3, 2 * f4, 2 * f5,
428
equations)
429
end
430
# Compute GLM term for psi (multiply by 2 because the non-conservative flux is
431
# multiplied by 0.5 whenever it's used in the Trixi code)
432
f[end] = 2 * v1_plus_ll * psi_avg
433
434
else #if orientation == 2
435
# Entries of Godunov-Powell term for induction equation (multiply by 2 because the non-conservative flux is
436
# multiplied by 0.5 whenever it's used in the Trixi code)
437
f[1] = 2 * v1_plus_ll * B2_avg
438
f[2] = 2 * v2_plus_ll * B2_avg
439
f[3] = 2 * v3_plus_ll * B2_avg
440
441
for k in eachcomponent(equations)
442
# Compute term Lorentz term
443
f2 = charge_ratio_ll[k] * (-B2_avg * B1_avg)
444
f3 = charge_ratio_ll[k] *
445
(-B2_avg * B2_avg + 0.5f0 * mag_norm_avg + pe_mean)
446
f4 = charge_ratio_ll[k] * (-B2_avg * B3_avg)
447
f5 = vk2_plus_ll[k] * pe_mean
448
449
# Compute multi-ion term (vanishes for NCOMP==1)
450
vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]
451
vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]
452
vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]
453
vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]
454
vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]
455
vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]
456
vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)
457
vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)
458
vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)
459
f5 += (B1_ll * (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) +
460
B3_ll * (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg))
461
462
# Compute Godunov-Powell term
463
f2 += charge_ratio_ll[k] * B1_ll * B2_avg
464
f3 += charge_ratio_ll[k] * B2_ll * B2_avg
465
f4 += charge_ratio_ll[k] * B3_ll * B2_avg
466
f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *
467
B2_avg
468
469
# Compute GLM term for the energy
470
f5 += v2_plus_ll * psi_ll * psi_avg
471
472
# Add to the flux vector (multiply by 2 because the non-conservative flux is
473
# multiplied by 0.5 whenever it's used in the Trixi code)
474
set_component!(f, k, 0, 2 * f2, 2 * f3, 2 * f4, 2 * f5,
475
equations)
476
end
477
# Compute GLM term for psi (multiply by 2 because the non-conservative flux is
478
# multiplied by 0.5 whenever it's used in the Trixi code)
479
f[end] = 2 * v2_plus_ll * psi_avg
480
end
481
482
return SVector(f)
483
end
484
485
"""
486
flux_nonconservative_central(u_ll, u_rr, orientation::Integer,
487
equations::IdealGlmMhdMultiIonEquations2D)
488
489
Central non-conservative two-point "flux", where the symmetric parts are computed with standard averages.
490
The use of this term together with [`flux_central`](@ref)
491
with [`VolumeIntegralFluxDifferencing`](@ref) yields a "standard"
492
(weak-form) DGSEM discretization of the multi-ion GLM-MHD system. This flux can also be used to construct a
493
standard local Lax-Friedrichs flux using `surface_flux = (flux_lax_friedrichs, flux_nonconservative_central)`.
494
495
!!! info "Usage and Scaling of Non-Conservative Fluxes in Trixi"
496
The central non-conservative fluxes implemented in this function are written as the product
497
of local and symmetric parts, where the symmetric part is a standard average. These fluxes
498
are meant to be used in the same way as the conservative fluxes (i.e., flux + flux_noncons
499
in both volume and surface integrals). In this routine, the fluxes are multiplied by 2 because
500
the non-conservative fluxes are always multiplied by 0.5 whenever they are used in the Trixi code.
501
502
The term is composed of four individual non-conservative terms:
503
1. The Godunov-Powell term, which arises for plasmas with non-vanishing magnetic field divergence, and
504
is evaluated as a function of the charge-averaged velocity.
505
2. The Lorentz-force term, which becomes a conservative term in the limit of one ion species for vanishing
506
electron pressure gradients.
507
3. The "multi-ion" term, which vanishes in the limit of one ion species.
508
4. The GLM term, which is needed for Galilean invariance.
509
"""
510
@inline function flux_nonconservative_central(u_ll, u_rr, orientation::Integer,
511
equations::IdealGlmMhdMultiIonEquations2D)
512
@unpack charge_to_mass = equations
513
# Unpack left and right states to get the magnetic field
514
B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)
515
B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)
516
psi_ll = divergence_cleaning_field(u_ll, equations)
517
psi_rr = divergence_cleaning_field(u_rr, equations)
518
519
# Compute important averages
520
mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2
521
mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2
522
523
# Electron pressure
524
pe_ll = equations.electron_pressure(u_ll, equations)
525
pe_rr = equations.electron_pressure(u_rr, equations)
526
527
# Compute charge ratio of u_ll
528
charge_ratio_ll = zero(MVector{ncomponents(equations), eltype(u_ll)})
529
total_electron_charge = zero(real(equations))
530
for k in eachcomponent(equations)
531
rho_k = u_ll[3 + (k - 1) * 5 + 1]
532
charge_ratio_ll[k] = rho_k * charge_to_mass[k]
533
total_electron_charge += charge_ratio_ll[k]
534
end
535
charge_ratio_ll ./= total_electron_charge
536
537
# Compute auxiliary variables
538
v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll, vk3_plus_ll = charge_averaged_velocities(u_ll,
539
equations)
540
v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr, vk3_plus_rr = charge_averaged_velocities(u_rr,
541
equations)
542
543
f = zero(MVector{nvariables(equations), eltype(u_ll)})
544
545
if orientation == 1
546
# Entries of Godunov-Powell term for induction equation
547
f[1] = v1_plus_ll * (B1_ll + B1_rr)
548
f[2] = v2_plus_ll * (B1_ll + B1_rr)
549
f[3] = v3_plus_ll * (B1_ll + B1_rr)
550
for k in eachcomponent(equations)
551
# Compute Lorentz term
552
f2 = charge_ratio_ll[k] * ((0.5f0 * mag_norm_ll - B1_ll * B1_ll + pe_ll) +
553
(0.5f0 * mag_norm_rr - B1_rr * B1_rr + pe_rr))
554
f3 = charge_ratio_ll[k] * ((-B1_ll * B2_ll) + (-B1_rr * B2_rr))
555
f4 = charge_ratio_ll[k] * ((-B1_ll * B3_ll) + (-B1_rr * B3_rr))
556
f5 = vk1_plus_ll[k] * (pe_ll + pe_rr)
557
558
# Compute multi-ion term, which vanishes for NCOMP==1
559
vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]
560
vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]
561
vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]
562
vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]
563
vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]
564
vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]
565
f5 += (B2_ll * ((vk1_minus_ll * B2_ll - vk2_minus_ll * B1_ll) +
566
(vk1_minus_rr * B2_rr - vk2_minus_rr * B1_rr)) +
567
B3_ll * ((vk1_minus_ll * B3_ll - vk3_minus_ll * B1_ll) +
568
(vk1_minus_rr * B3_rr - vk3_minus_rr * B1_rr)))
569
570
# Compute Godunov-Powell term
571
f2 += charge_ratio_ll[k] * B1_ll * (B1_ll + B1_rr)
572
f3 += charge_ratio_ll[k] * B2_ll * (B1_ll + B1_rr)
573
f4 += charge_ratio_ll[k] * B3_ll * (B1_ll + B1_rr)
574
f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *
575
(B1_ll + B1_rr)
576
577
# Compute GLM term for the energy
578
f5 += v1_plus_ll * psi_ll * (psi_ll + psi_rr)
579
580
# Append to the flux vector
581
set_component!(f, k, 0, f2, f3, f4, f5, equations)
582
end
583
# Compute GLM term for psi
584
f[end] = v1_plus_ll * (psi_ll + psi_rr)
585
586
else #if orientation == 2
587
# Entries of Godunov-Powell term for induction equation
588
f[1] = v1_plus_ll * (B2_ll + B2_rr)
589
f[2] = v2_plus_ll * (B2_ll + B2_rr)
590
f[3] = v3_plus_ll * (B2_ll + B2_rr)
591
592
for k in eachcomponent(equations)
593
# Compute Lorentz term
594
f2 = charge_ratio_ll[k] * ((-B2_ll * B1_ll) + (-B2_rr * B1_rr))
595
f3 = charge_ratio_ll[k] * ((-B2_ll * B2_ll + 0.5f0 * mag_norm_ll + pe_ll) +
596
(-B2_rr * B2_rr + 0.5f0 * mag_norm_rr + pe_rr))
597
f4 = charge_ratio_ll[k] * ((-B2_ll * B3_ll) + (-B2_rr * B3_rr))
598
f5 = vk2_plus_ll[k] * (pe_ll + pe_rr)
599
600
# Compute multi-ion term (vanishes for NCOMP==1)
601
vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]
602
vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]
603
vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]
604
vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]
605
vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]
606
vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]
607
f5 += (B1_ll * ((vk2_minus_ll * B1_ll - vk1_minus_ll * B2_ll) +
608
(vk2_minus_rr * B1_rr - vk1_minus_rr * B2_rr)) +
609
B3_ll * ((vk2_minus_ll * B3_ll - vk3_minus_ll * B2_ll) +
610
(vk2_minus_rr * B3_rr - vk3_minus_rr * B2_rr)))
611
612
# Compute Godunov-Powell term
613
f2 += charge_ratio_ll[k] * B1_ll * (B2_ll + B2_rr)
614
f3 += charge_ratio_ll[k] * B2_ll * (B2_ll + B2_rr)
615
f4 += charge_ratio_ll[k] * B3_ll * (B2_ll + B2_rr)
616
f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *
617
(B2_ll + B2_rr)
618
619
# Compute GLM term for the energy
620
f5 += v2_plus_ll * psi_ll * (psi_ll + psi_rr)
621
622
# Append to the flux vector
623
set_component!(f, k, 0, f2, f3, f4, f5, equations)
624
end
625
# Compute GLM term for psi
626
f[end] = v2_plus_ll * (psi_ll + psi_rr)
627
end
628
629
return SVector(f)
630
end
631
632
"""
633
flux_ruedaramirez_etal(u_ll, u_rr, orientation, equations::IdealGlmMhdMultiIonEquations2D)
634
635
Entropy conserving two-point flux for the multi-ion GLM-MHD equations from
636
- A. Rueda-Ramírez, A. Sikstel, G. Gassner, An Entropy-Stable Discontinuous Galerkin Discretization
637
of the Ideal Multi-Ion Magnetohydrodynamics System (2024). Journal of Computational Physics.
638
[DOI: 10.1016/j.jcp.2024.113655](https://doi.org/10.1016/j.jcp.2024.113655).
639
640
This flux (together with the MHD non-conservative term) is consistent in the case of one ion species with the flux of:
641
- Derigs et al. (2018). Ideal GLM-MHD: About the entropy consistent nine-wave magnetic field
642
divergence diminishing ideal magnetohydrodynamics equations for multi-ion
643
[DOI: 10.1016/j.jcp.2018.03.002](https://doi.org/10.1016/j.jcp.2018.03.002)
644
"""
645
function flux_ruedaramirez_etal(u_ll, u_rr, orientation::Integer,
646
equations::IdealGlmMhdMultiIonEquations2D)
647
@unpack gammas = equations
648
# Unpack left and right states to get the magnetic field
649
B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)
650
B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)
651
psi_ll = divergence_cleaning_field(u_ll, equations)
652
psi_rr = divergence_cleaning_field(u_rr, equations)
653
654
v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll, vk3_plus_ll = charge_averaged_velocities(u_ll,
655
equations)
656
v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr, vk3_plus_rr = charge_averaged_velocities(u_rr,
657
equations)
658
659
f = zero(MVector{nvariables(equations), eltype(u_ll)})
660
661
# Compute averages for global variables
662
v1_plus_avg = 0.5f0 * (v1_plus_ll + v1_plus_rr)
663
v2_plus_avg = 0.5f0 * (v2_plus_ll + v2_plus_rr)
664
v3_plus_avg = 0.5f0 * (v3_plus_ll + v3_plus_rr)
665
B1_avg = 0.5f0 * (B1_ll + B1_rr)
666
B2_avg = 0.5f0 * (B2_ll + B2_rr)
667
B3_avg = 0.5f0 * (B3_ll + B3_rr)
668
mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2
669
mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2
670
mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)
671
psi_avg = 0.5f0 * (psi_ll + psi_rr)
672
673
if orientation == 1
674
psi_B1_avg = 0.5f0 * (B1_ll * psi_ll + B1_rr * psi_rr)
675
676
# Magnetic field components from f^MHD
677
f6 = equations.c_h * psi_avg
678
f7 = v1_plus_avg * B2_avg - v2_plus_avg * B1_avg
679
f8 = v1_plus_avg * B3_avg - v3_plus_avg * B1_avg
680
f9 = equations.c_h * B1_avg
681
682
# Start building the flux
683
f[1] = f6
684
f[2] = f7
685
f[3] = f8
686
f[end] = f9
687
688
# Iterate over all components
689
for k in eachcomponent(equations)
690
# Unpack left and right states
691
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll = get_component(k, u_ll,
692
equations)
693
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr = get_component(k, u_rr,
694
equations)
695
rho_inv_ll = 1 / rho_ll
696
v1_ll = rho_v1_ll * rho_inv_ll
697
v2_ll = rho_v2_ll * rho_inv_ll
698
v3_ll = rho_v3_ll * rho_inv_ll
699
rho_inv_rr = 1 / rho_rr
700
v1_rr = rho_v1_rr * rho_inv_rr
701
v2_rr = rho_v2_rr * rho_inv_rr
702
v3_rr = rho_v3_rr * rho_inv_rr
703
vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^2
704
vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^2
705
706
p_ll = (gammas[k] - 1) *
707
(rho_e_ll - 0.5f0 * rho_ll * vel_norm_ll - 0.5f0 * mag_norm_ll -
708
0.5f0 * psi_ll^2)
709
p_rr = (gammas[k] - 1) *
710
(rho_e_rr - 0.5f0 * rho_rr * vel_norm_rr - 0.5f0 * mag_norm_rr -
711
0.5f0 * psi_rr^2)
712
beta_ll = 0.5f0 * rho_ll / p_ll
713
beta_rr = 0.5f0 * rho_rr / p_rr
714
# for convenience store vk_plus⋅B
715
vel_dot_mag_ll = vk1_plus_ll[k] * B1_ll + vk2_plus_ll[k] * B2_ll +
716
vk3_plus_ll[k] * B3_ll
717
vel_dot_mag_rr = vk1_plus_rr[k] * B1_rr + vk2_plus_rr[k] * B2_rr +
718
vk3_plus_rr[k] * B3_rr
719
720
# Compute the necessary mean values needed for either direction
721
rho_avg = 0.5f0 * (rho_ll + rho_rr)
722
rho_mean = ln_mean(rho_ll, rho_rr)
723
beta_mean = ln_mean(beta_ll, beta_rr)
724
beta_avg = 0.5f0 * (beta_ll + beta_rr)
725
p_mean = 0.5f0 * rho_avg / beta_avg
726
v1_avg = 0.5f0 * (v1_ll + v1_rr)
727
v2_avg = 0.5f0 * (v2_ll + v2_rr)
728
v3_avg = 0.5f0 * (v3_ll + v3_rr)
729
vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)
730
vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)
731
vk1_plus_avg = 0.5f0 * (vk1_plus_ll[k] + vk1_plus_rr[k])
732
vk2_plus_avg = 0.5f0 * (vk2_plus_ll[k] + vk2_plus_rr[k])
733
vk3_plus_avg = 0.5f0 * (vk3_plus_ll[k] + vk3_plus_rr[k])
734
# v_minus
735
vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]
736
vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]
737
vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]
738
vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]
739
vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]
740
vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]
741
vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)
742
vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)
743
vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)
744
745
# Fill the fluxes for the mass and momentum equations
746
f1 = rho_mean * v1_avg
747
f2 = f1 * v1_avg + p_mean
748
f3 = f1 * v2_avg
749
f4 = f1 * v3_avg
750
751
# total energy flux is complicated and involves the previous eight components
752
v1_plus_mag_avg = 0.5f0 * (vk1_plus_ll[k] * mag_norm_ll +
753
vk1_plus_rr[k] * mag_norm_rr)
754
# Euler part
755
f5 = f1 * 0.5f0 * (1 / (gammas[k] - 1) / beta_mean - vel_norm_avg) +
756
f2 * v1_avg + f3 * v2_avg + f4 * v3_avg
757
# MHD part
758
f5 += (f6 * B1_avg + f7 * B2_avg + f8 * B3_avg - 0.5f0 * v1_plus_mag_avg +
759
B1_avg * vel_dot_mag_avg # Same terms as in Derigs (but with v_plus)
760
+ f9 * psi_avg - equations.c_h * psi_B1_avg # GLM term
761
+
762
0.5f0 * vk1_plus_avg * mag_norm_avg -
763
vk1_plus_avg * B1_avg * B1_avg - vk2_plus_avg * B1_avg * B2_avg -
764
vk3_plus_avg * B1_avg * B3_avg # Additional terms related to the Lorentz non-conservative term (momentum eqs)
765
-
766
B2_avg * (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) -
767
B3_avg * (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg)) # Terms related to the multi-ion non-conservative term (induction equation!)
768
769
set_component!(f, k, f1, f2, f3, f4, f5, equations)
770
end
771
else #if orientation == 2
772
psi_B2_avg = 0.5f0 * (B2_ll * psi_ll + B2_rr * psi_rr)
773
774
# Magnetic field components from f^MHD
775
f6 = v2_plus_avg * B1_avg - v1_plus_avg * B2_avg
776
f7 = equations.c_h * psi_avg
777
f8 = v2_plus_avg * B3_avg - v3_plus_avg * B2_avg
778
f9 = equations.c_h * B2_avg
779
780
# Start building the flux
781
f[1] = f6
782
f[2] = f7
783
f[3] = f8
784
f[end] = f9
785
786
# Iterate over all components
787
for k in eachcomponent(equations)
788
# Unpack left and right states
789
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_ll = get_component(k, u_ll,
790
equations)
791
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_rr = get_component(k, u_rr,
792
equations)
793
794
rho_inv_ll = 1 / rho_ll
795
v1_ll = rho_v1_ll * rho_inv_ll
796
v2_ll = rho_v2_ll * rho_inv_ll
797
v3_ll = rho_v3_ll * rho_inv_ll
798
rho_inv_rr = 1 / rho_rr
799
v1_rr = rho_v1_rr * rho_inv_rr
800
v2_rr = rho_v2_rr * rho_inv_rr
801
v3_rr = rho_v3_rr * rho_inv_rr
802
vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^2
803
vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^2
804
805
p_ll = (gammas[k] - 1) *
806
(rho_e_ll - 0.5f0 * rho_ll * vel_norm_ll - 0.5f0 * mag_norm_ll -
807
0.5f0 * psi_ll^2)
808
p_rr = (gammas[k] - 1) *
809
(rho_e_rr - 0.5f0 * rho_rr * vel_norm_rr - 0.5f0 * mag_norm_rr -
810
0.5f0 * psi_rr^2)
811
beta_ll = 0.5f0 * rho_ll / p_ll
812
beta_rr = 0.5f0 * rho_rr / p_rr
813
# for convenience store vk_plus⋅B
814
vel_dot_mag_ll = vk1_plus_ll[k] * B1_ll + vk2_plus_ll[k] * B2_ll +
815
vk3_plus_ll[k] * B3_ll
816
vel_dot_mag_rr = vk1_plus_rr[k] * B1_rr + vk2_plus_rr[k] * B2_rr +
817
vk3_plus_rr[k] * B3_rr
818
819
# Compute the necessary mean values needed for either direction
820
rho_avg = 0.5f0 * (rho_ll + rho_rr)
821
rho_mean = ln_mean(rho_ll, rho_rr)
822
beta_mean = ln_mean(beta_ll, beta_rr)
823
beta_avg = 0.5f0 * (beta_ll + beta_rr)
824
p_mean = 0.5f0 * rho_avg / beta_avg
825
v1_avg = 0.5f0 * (v1_ll + v1_rr)
826
v2_avg = 0.5f0 * (v2_ll + v2_rr)
827
v3_avg = 0.5f0 * (v3_ll + v3_rr)
828
vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)
829
vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)
830
vk1_plus_avg = 0.5f0 * (vk1_plus_ll[k] + vk1_plus_rr[k])
831
vk2_plus_avg = 0.5f0 * (vk2_plus_ll[k] + vk2_plus_rr[k])
832
vk3_plus_avg = 0.5f0 * (vk3_plus_ll[k] + vk3_plus_rr[k])
833
# v_minus
834
vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]
835
vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]
836
vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]
837
vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]
838
vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]
839
vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]
840
vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)
841
vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)
842
vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)
843
844
# Fill the fluxes for the mass and momentum equations
845
f1 = rho_mean * v2_avg
846
f2 = f1 * v1_avg
847
f3 = f1 * v2_avg + p_mean
848
f4 = f1 * v3_avg
849
850
# total energy flux is complicated and involves the previous eight components
851
v2_plus_mag_avg = 0.5f0 * (vk2_plus_ll[k] * mag_norm_ll +
852
vk2_plus_rr[k] * mag_norm_rr)
853
# Euler part
854
f5 = f1 * 0.5f0 * (1 / (gammas[k] - 1) / beta_mean - vel_norm_avg) +
855
f2 * v1_avg + f3 * v2_avg + f4 * v3_avg
856
# MHD part
857
f5 += (f6 * B1_avg + f7 * B2_avg + f8 * B3_avg - 0.5f0 * v2_plus_mag_avg +
858
B2_avg * vel_dot_mag_avg # Same terms as in Derigs (but with v_plus)
859
+ f9 * psi_avg - equations.c_h * psi_B2_avg # GLM term
860
+
861
0.5f0 * vk2_plus_avg * mag_norm_avg -
862
vk1_plus_avg * B2_avg * B1_avg - vk2_plus_avg * B2_avg * B2_avg -
863
vk3_plus_avg * B2_avg * B3_avg # Additional terms related to the Lorentz non-conservative term (momentum eqs)
864
-
865
B1_avg * (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) -
866
B3_avg * (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg)) # Terms related to the multi-ion non-conservative term (induction equation!)
867
868
set_component!(f, k, f1, f2, f3, f4, f5, equations)
869
end
870
end
871
872
return SVector(f)
873
end
874
875
# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation
876
# This routine approximates the maximum wave speed as sum of the maximum ion velocity
877
# for all species and the maximum magnetosonic speed.
878
@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,
879
equations::IdealGlmMhdMultiIonEquations2D)
880
# Calculate fast magnetoacoustic wave speeds
881
# left
882
cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)
883
# right
884
cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)
885
886
# Calculate velocities
887
v_ll = zero(eltype(u_ll))
888
v_rr = zero(eltype(u_rr))
889
if orientation == 1
890
for k in eachcomponent(equations)
891
rho, rho_v1, _ = get_component(k, u_ll, equations)
892
v_ll = max(v_ll, abs(rho_v1 / rho))
893
rho, rho_v1, _ = get_component(k, u_rr, equations)
894
v_rr = max(v_rr, abs(rho_v1 / rho))
895
end
896
else #if orientation == 2
897
for k in eachcomponent(equations)
898
rho, rho_v1, rho_v2, _ = get_component(k, u_ll, equations)
899
v_ll = max(v_ll, abs(rho_v2 / rho))
900
rho, rho_v1, rho_v2, _ = get_component(k, u_rr, equations)
901
v_rr = max(v_rr, abs(rho_v2 / rho))
902
end
903
end
904
905
return max(abs(v_ll), abs(v_rr)) + max(cf_ll, cf_rr)
906
end
907
908
# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`
909
@inline function max_abs_speed(u_ll, u_rr, orientation::Integer,
910
equations::IdealGlmMhdMultiIonEquations2D)
911
# Calculate fast magnetoacoustic wave speeds
912
# left
913
cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)
914
# right
915
cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)
916
917
# Calculate velocities
918
v_ll = zero(eltype(u_ll))
919
v_rr = zero(eltype(u_rr))
920
if orientation == 1
921
for k in eachcomponent(equations)
922
rho, rho_v1, _ = get_component(k, u_ll, equations)
923
v_ll = max(v_ll, abs(rho_v1 / rho))
924
rho, rho_v1, _ = get_component(k, u_rr, equations)
925
v_rr = max(v_rr, abs(rho_v1 / rho))
926
end
927
else #if orientation == 2
928
for k in eachcomponent(equations)
929
rho, rho_v1, rho_v2, _ = get_component(k, u_ll, equations)
930
v_ll = max(v_ll, abs(rho_v2 / rho))
931
rho, rho_v1, rho_v2, _ = get_component(k, u_rr, equations)
932
v_rr = max(v_rr, abs(rho_v2 / rho))
933
end
934
end
935
936
return max(abs(v_ll) + cf_ll, abs(v_rr) + cf_rr)
937
end
938
939
@inline function max_abs_speeds(u, equations::IdealGlmMhdMultiIonEquations2D)
940
v1 = zero(real(equations))
941
v2 = zero(real(equations))
942
for k in eachcomponent(equations)
943
rho, rho_v1, rho_v2, _ = get_component(k, u, equations)
944
v1 = max(v1, abs(rho_v1 / rho))
945
v2 = max(v2, abs(rho_v2 / rho))
946
end
947
948
cf_x_direction = calc_fast_wavespeed(u, 1, equations)
949
cf_y_direction = calc_fast_wavespeed(u, 2, equations)
950
951
return (abs(v1) + cf_x_direction, abs(v2) + cf_y_direction)
952
end
953
954
# Compute the fastest wave speed for ideal multi-ion GLM-MHD equations: c_f, the fast
955
# magnetoacoustic eigenvalue. This routine computes the fast magnetosonic speed for each ion
956
# species using the single-fluid MHD expressions and approximates the multi-ion c_f as
957
# the maximum of these individual magnetosonic speeds.
958
@inline function calc_fast_wavespeed(cons, orientation::Integer,
959
equations::IdealGlmMhdMultiIonEquations2D)
960
B1, B2, B3 = magnetic_field(cons, equations)
961
psi = divergence_cleaning_field(cons, equations)
962
963
c_f = zero(real(equations))
964
for k in eachcomponent(equations)
965
rho, rho_v1, rho_v2, rho_v3, rho_e = get_component(k, cons, equations)
966
967
rho_inv = 1 / rho
968
v1 = rho_v1 * rho_inv
969
v2 = rho_v2 * rho_inv
970
v3 = rho_v3 * rho_inv
971
gamma = equations.gammas[k]
972
p = (gamma - 1) *
973
(rho_e - 0.5f0 * rho * (v1^2 + v2^2 + v3^2) - 0.5f0 * (B1^2 + B2^2 + B3^2) -
974
0.5f0 * psi^2)
975
a_square = gamma * p * rho_inv
976
inv_sqrt_rho = 1 / sqrt(rho)
977
978
b1 = B1 * inv_sqrt_rho
979
b2 = B2 * inv_sqrt_rho
980
b3 = B3 * inv_sqrt_rho
981
b_square = b1^2 + b2^2 + b3^2
982
983
if orientation == 1
984
c_f = max(c_f,
985
sqrt(0.5f0 * (a_square + b_square) +
986
0.5f0 *
987
sqrt((a_square + b_square)^2 - 4 * a_square * b1^2)))
988
else #if orientation == 2
989
c_f = max(c_f,
990
sqrt(0.5f0 * (a_square + b_square) +
991
0.5f0 *
992
sqrt((a_square + b_square)^2 - 4 * a_square * b2^2)))
993
end
994
end
995
996
return c_f
997
end
998
end # @muladd
999
1000