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
5586 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
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
return new(gammas, charge_to_mass, gas_constants, molar_masses,
126
ion_ion_collision_constants,
127
ion_electron_collision_constants, electron_pressure,
128
electron_temperature,
129
c_h)
130
end
131
end
132
133
function IdealGlmMhdMultiIonEquations2D(; gammas, charge_to_mass,
134
gas_constants = zero(SVector{length(gammas),
135
eltype(gammas)}),
136
molar_masses = zero(SVector{length(gammas),
137
eltype(gammas)}),
138
ion_ion_collision_constants = zeros(eltype(gammas),
139
length(gammas),
140
length(gammas)),
141
ion_electron_collision_constants = zero(SVector{length(gammas),
142
eltype(gammas)}),
143
electron_pressure = electron_pressure_zero,
144
electron_temperature = electron_pressure_zero,
145
initial_c_h = convert(eltype(gammas), NaN))
146
_gammas = promote(gammas...)
147
_charge_to_mass = promote(charge_to_mass...)
148
_gas_constants = promote(gas_constants...)
149
_molar_masses = promote(molar_masses...)
150
_ion_electron_collision_constants = promote(ion_electron_collision_constants...)
151
RealT = promote_type(eltype(_gammas), eltype(_charge_to_mass),
152
eltype(_gas_constants), eltype(_molar_masses),
153
eltype(ion_ion_collision_constants),
154
eltype(_ion_electron_collision_constants))
155
__gammas = SVector(map(RealT, _gammas))
156
__charge_to_mass = SVector(map(RealT, _charge_to_mass))
157
__gas_constants = SVector(map(RealT, _gas_constants))
158
__molar_masses = SVector(map(RealT, _molar_masses))
159
__ion_ion_collision_constants = map(RealT, ion_ion_collision_constants)
160
__ion_electron_collision_constants = SVector(map(RealT,
161
_ion_electron_collision_constants))
162
163
NVARS = length(_gammas) * 5 + 4
164
NCOMP = length(_gammas)
165
166
return IdealGlmMhdMultiIonEquations2D{NVARS, NCOMP, RealT,
167
typeof(electron_pressure),
168
typeof(electron_temperature)}(__gammas,
169
__charge_to_mass,
170
__gas_constants,
171
__molar_masses,
172
__ion_ion_collision_constants,
173
__ion_electron_collision_constants,
174
electron_pressure,
175
electron_temperature,
176
initial_c_h)
177
end
178
179
# Outer constructor for `@reset` works correctly
180
function IdealGlmMhdMultiIonEquations2D(gammas, charge_to_mass, gas_constants,
181
molar_masses, ion_ion_collision_constants,
182
ion_electron_collision_constants,
183
electron_pressure,
184
electron_temperature,
185
c_h)
186
return IdealGlmMhdMultiIonEquations2D(gammas = gammas,
187
charge_to_mass = charge_to_mass,
188
gas_constants = gas_constants,
189
molar_masses = molar_masses,
190
ion_ion_collision_constants = ion_ion_collision_constants,
191
ion_electron_collision_constants = ion_electron_collision_constants,
192
electron_pressure = electron_pressure,
193
electron_temperature = electron_temperature,
194
initial_c_h = c_h)
195
end
196
197
@inline function Base.real(::IdealGlmMhdMultiIonEquations2D{NVARS, NCOMP, RealT}) where {
198
NVARS,
199
NCOMP,
200
RealT
201
}
202
return RealT
203
end
204
205
"""
206
initial_condition_weak_blast_wave(x, t, equations::IdealGlmMhdMultiIonEquations2D)
207
208
A weak blast wave (adapted to multi-ion MHD) from
209
- Hennemann, S., Rueda-Ramírez, A. M., Hindenlang, F. J., & Gassner, G. J. (2021). A provably entropy
210
stable subcell shock capturing approach for high order split form DG for the compressible Euler equations.
211
Journal of Computational Physics, 426, 109935. [arXiv: 2008.12044](https://arxiv.org/abs/2008.12044).
212
[DOI: 10.1016/j.jcp.2020.109935](https://doi.org/10.1016/j.jcp.2020.109935)
213
"""
214
function initial_condition_weak_blast_wave(x, t,
215
equations::IdealGlmMhdMultiIonEquations2D)
216
# Adapted MHD version of the weak blast wave from Hennemann & Gassner JCP paper 2020 (Sec. 6.3)
217
# Same discontinuity in the velocities but with magnetic fields
218
# Set up polar coordinates
219
RealT = eltype(x)
220
inicenter = (0, 0)
221
x_norm = x[1] - inicenter[1]
222
y_norm = x[2] - inicenter[2]
223
r = sqrt(x_norm^2 + y_norm^2)
224
phi = atan(y_norm, x_norm)
225
226
# Calculate primitive variables
227
rho = r > 0.5f0 ? one(RealT) : convert(RealT, 1.1691)
228
v1 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos(phi)
229
v2 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * sin(phi)
230
p = r > 0.5f0 ? one(RealT) : convert(RealT, 1.245)
231
232
prim = zero(MVector{nvariables(equations), RealT})
233
prim[1] = 1
234
prim[2] = 1
235
prim[3] = 1
236
for k in eachcomponent(equations)
237
# We initialize each species with a fraction of the total density `rho`, such
238
# that the sum of the densities is `rho := density(prim, equations)`. The density of
239
# a species is double the density of the next species.
240
fraction = 2^(k - 1) * (1 - 2) / (1 - 2^ncomponents(equations))
241
set_component!(prim, k,
242
fraction * rho, v1,
243
v2, 0, p, equations)
244
end
245
246
return prim2cons(SVector(prim), equations)
247
end
248
249
# 2D flux of the multi-ion GLM-MHD equations in the direction `orientation`
250
@inline function flux(u, orientation::Integer,
251
equations::IdealGlmMhdMultiIonEquations2D)
252
B1, B2, B3 = magnetic_field(u, equations)
253
psi = divergence_cleaning_field(u, equations)
254
255
v1_plus, v2_plus, v3_plus, vk1_plus, vk2_plus, vk3_plus = charge_averaged_velocities(u,
256
equations)
257
258
mag_en = 0.5f0 * (B1^2 + B2^2 + B3^2)
259
div_clean_energy = 0.5f0 * psi^2
260
261
f = zero(MVector{nvariables(equations), eltype(u)})
262
263
if orientation == 1
264
f[1] = equations.c_h * psi
265
f[2] = v1_plus * B2 - v2_plus * B1
266
f[3] = v1_plus * B3 - v3_plus * B1
267
268
for k in eachcomponent(equations)
269
rho, rho_v1, rho_v2, rho_v3, rho_e_total = get_component(k, u, equations)
270
rho_inv = 1 / rho
271
v1 = rho_v1 * rho_inv
272
v2 = rho_v2 * rho_inv
273
v3 = rho_v3 * rho_inv
274
kin_en = 0.5f0 * rho * (v1^2 + v2^2 + v3^2)
275
276
gamma = equations.gammas[k]
277
p = (gamma - 1) * (rho_e_total - kin_en - mag_en - div_clean_energy)
278
279
f1 = rho_v1
280
f2 = rho_v1 * v1 + p
281
f3 = rho_v1 * v2
282
f4 = rho_v1 * v3
283
f5 = (kin_en + gamma * p / (gamma - 1)) * v1 + 2 * mag_en * vk1_plus[k] -
284
B1 * (vk1_plus[k] * B1 + vk2_plus[k] * B2 + vk3_plus[k] * B3) +
285
equations.c_h * psi * B1
286
287
set_component!(f, k, f1, f2, f3, f4, f5, equations)
288
end
289
f[end] = equations.c_h * B1
290
else #if orientation == 2
291
f[1] = v2_plus * B1 - v1_plus * B2
292
f[2] = equations.c_h * psi
293
f[3] = v2_plus * B3 - v3_plus * B2
294
295
for k in eachcomponent(equations)
296
rho, rho_v1, rho_v2, rho_v3, rho_e_total = get_component(k, u, equations)
297
rho_inv = 1 / rho
298
v1 = rho_v1 * rho_inv
299
v2 = rho_v2 * rho_inv
300
v3 = rho_v3 * rho_inv
301
kin_en = 0.5f0 * rho * (v1^2 + v2^2 + v3^2)
302
303
gamma = equations.gammas[k]
304
p = (gamma - 1) * (rho_e_total - kin_en - mag_en - div_clean_energy)
305
306
f1 = rho_v2
307
f2 = rho_v2 * v1
308
f3 = rho_v2 * v2 + p
309
f4 = rho_v2 * v3
310
f5 = (kin_en + gamma * p / (gamma - 1)) * v2 + 2 * mag_en * vk2_plus[k] -
311
B2 * (vk1_plus[k] * B1 + vk2_plus[k] * B2 + vk3_plus[k] * B3) +
312
equations.c_h * psi * B2
313
314
set_component!(f, k, f1, f2, f3, f4, f5, equations)
315
end
316
f[end] = equations.c_h * B2
317
end
318
319
return SVector(f)
320
end
321
322
@inline function flux(u, normal_direction::AbstractVector,
323
equations::IdealGlmMhdMultiIonEquations2D)
324
B1, B2, B3 = magnetic_field(u, equations)
325
psi = divergence_cleaning_field(u, equations)
326
327
v1_plus, v2_plus, v3_plus, vk1_plus, vk2_plus,
328
vk3_plus = charge_averaged_velocities(u,
329
equations)
330
331
mag_en = 0.5f0 * (B1^2 + B2^2 + B3^2)
332
div_clean_energy = 0.5f0 * psi^2
333
334
f = zero(MVector{nvariables(equations), eltype(u)})
335
336
f[1] = equations.c_h * psi * normal_direction[1] +
337
(v2_plus * B1 - v1_plus * B2) * normal_direction[2]
338
f[2] = (v1_plus * B2 - v2_plus * B1) * normal_direction[1] +
339
equations.c_h * psi * normal_direction[2]
340
f[3] = (v1_plus * B3 - v3_plus * B1) * normal_direction[1] +
341
(v2_plus * B3 - v3_plus * B2) * normal_direction[2]
342
343
for k in eachcomponent(equations)
344
rho, rho_v1, rho_v2, rho_v3, rho_e_total = get_component(k, u, equations)
345
rho_inv = 1 / rho
346
v1 = rho_v1 * rho_inv
347
v2 = rho_v2 * rho_inv
348
v3 = rho_v3 * rho_inv
349
kin_en = 0.5f0 * rho * (v1^2 + v2^2 + v3^2)
350
351
gamma = equations.gammas[k]
352
p = (gamma - 1) * (rho_e_total - kin_en - mag_en - div_clean_energy)
353
354
v_normal = v1 * normal_direction[1] + v2 * normal_direction[2]
355
rho_v_normal = rho * v_normal
356
357
f1 = rho_v_normal
358
f2 = rho_v_normal * v1 + p * normal_direction[1]
359
f3 = rho_v_normal * v2 + p * normal_direction[2]
360
f4 = rho_v_normal * v3
361
f5 = ((kin_en + gamma * p / (gamma - 1)) * v1 + 2 * mag_en * vk1_plus[k] -
362
B1 * (vk1_plus[k] * B1 + vk2_plus[k] * B2 + vk3_plus[k] * B3) +
363
equations.c_h * psi * B1) * normal_direction[1] +
364
((kin_en + gamma * p / (gamma - 1)) * v2 + 2 * mag_en * vk2_plus[k] -
365
B2 * (vk1_plus[k] * B1 + vk2_plus[k] * B2 + vk3_plus[k] * B3) +
366
equations.c_h * psi * B2) * normal_direction[2]
367
368
set_component!(f, k, f1, f2, f3, f4, f5, equations)
369
end
370
f[end] = equations.c_h * (B1 * normal_direction[1] + B2 * normal_direction[2])
371
372
return SVector(f)
373
end
374
375
"""
376
flux_nonconservative_ruedaramirez_etal(u_ll, u_rr,
377
orientation_or_normal_direction,
378
equations::IdealGlmMhdMultiIonEquations2D)
379
380
Entropy-conserving non-conservative two-point "flux" as described in
381
- A. Rueda-Ramírez, A. Sikstel, G. Gassner, An Entropy-Stable Discontinuous Galerkin Discretization
382
of the Ideal Multi-Ion Magnetohydrodynamics System (2024). Journal of Computational Physics.
383
[DOI: 10.1016/j.jcp.2024.113655](https://doi.org/10.1016/j.jcp.2024.113655).
384
385
!!! info "Usage and Scaling of Non-Conservative Fluxes in Trixi.jl"
386
The non-conservative fluxes derived in the reference above are written as the product
387
of local and symmetric parts and are meant to be used in the same way as the conservative
388
fluxes (i.e., flux + flux_noncons in both volume and surface integrals). In this routine,
389
the fluxes are multiplied by 2 because the non-conservative fluxes are always multiplied
390
by 0.5 whenever they are used in the Trixi code.
391
392
The term is composed of four individual non-conservative terms:
393
1. The Godunov-Powell term, which arises for plasmas with non-vanishing magnetic field divergence, and
394
is evaluated as a function of the charge-averaged velocity.
395
2. The Lorentz-force term, which becomes a conservative term in the limit of one ion species for vanishing
396
electron pressure gradients.
397
3. The "multi-ion" term, which vanishes in the limit of one ion species.
398
4. The GLM term, which is needed for Galilean invariance.
399
"""
400
@inline function flux_nonconservative_ruedaramirez_etal(u_ll, u_rr,
401
orientation::Integer,
402
equations::IdealGlmMhdMultiIonEquations2D)
403
@unpack charge_to_mass = equations
404
# Unpack left and right states to get the magnetic field
405
B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)
406
B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)
407
psi_ll = divergence_cleaning_field(u_ll, equations)
408
psi_rr = divergence_cleaning_field(u_rr, equations)
409
410
# Compute important averages
411
B1_avg = 0.5f0 * (B1_ll + B1_rr)
412
B2_avg = 0.5f0 * (B2_ll + B2_rr)
413
B3_avg = 0.5f0 * (B3_ll + B3_rr)
414
mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2
415
mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2
416
mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)
417
psi_avg = 0.5f0 * (psi_ll + psi_rr)
418
419
# Mean electron pressure
420
pe_ll = equations.electron_pressure(u_ll, equations)
421
pe_rr = equations.electron_pressure(u_rr, equations)
422
pe_mean = 0.5f0 * (pe_ll + pe_rr)
423
424
# Compute charge ratio of u_ll
425
charge_ratio_ll = zero(MVector{ncomponents(equations), eltype(u_ll)})
426
total_electron_charge = zero(eltype(u_ll))
427
for k in eachcomponent(equations)
428
rho_k = u_ll[3 + (k - 1) * 5 + 1]
429
charge_ratio_ll[k] = rho_k * charge_to_mass[k]
430
total_electron_charge += charge_ratio_ll[k]
431
end
432
charge_ratio_ll ./= total_electron_charge
433
434
# Compute auxiliary variables
435
v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll,
436
vk3_plus_ll = charge_averaged_velocities(u_ll,
437
equations)
438
v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr,
439
vk3_plus_rr = charge_averaged_velocities(u_rr,
440
equations)
441
442
f = zero(MVector{nvariables(equations), eltype(u_ll)})
443
444
if orientation == 1
445
# Entries of Godunov-Powell term for induction equation (multiply by 2 because the non-conservative flux is
446
# multiplied by 0.5 whenever it's used in the Trixi code)
447
f[1] = 2 * v1_plus_ll * B1_avg
448
f[2] = 2 * v2_plus_ll * B1_avg
449
f[3] = 2 * v3_plus_ll * B1_avg
450
451
for k in eachcomponent(equations)
452
# Compute term Lorentz term
453
f2 = charge_ratio_ll[k] * (0.5f0 * mag_norm_avg - B1_avg * B1_avg + pe_mean)
454
f3 = charge_ratio_ll[k] * (-B1_avg * B2_avg)
455
f4 = charge_ratio_ll[k] * (-B1_avg * B3_avg)
456
f5 = vk1_plus_ll[k] * pe_mean
457
458
# Compute multi-ion term (vanishes for NCOMP==1)
459
vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]
460
vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]
461
vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]
462
vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]
463
vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]
464
vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]
465
vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)
466
vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)
467
vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)
468
f5 += (B2_ll * (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) +
469
B3_ll * (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg))
470
471
# Compute Godunov-Powell term
472
f2 += charge_ratio_ll[k] * B1_ll * B1_avg
473
f3 += charge_ratio_ll[k] * B2_ll * B1_avg
474
f4 += charge_ratio_ll[k] * B3_ll * B1_avg
475
f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *
476
B1_avg
477
478
# Compute GLM term for the energy
479
f5 += v1_plus_ll * psi_ll * psi_avg
480
481
# Add to the flux vector (multiply by 2 because the non-conservative flux is
482
# multiplied by 0.5 whenever it's used in the Trixi code)
483
set_component!(f, k, 0, 2 * f2, 2 * f3, 2 * f4, 2 * f5,
484
equations)
485
end
486
# Compute GLM term for psi (multiply by 2 because the non-conservative flux is
487
# multiplied by 0.5 whenever it's used in the Trixi code)
488
f[end] = 2 * v1_plus_ll * psi_avg
489
490
else #if orientation == 2
491
# Entries of Godunov-Powell term for induction equation (multiply by 2 because the non-conservative flux is
492
# multiplied by 0.5 whenever it's used in the Trixi code)
493
f[1] = 2 * v1_plus_ll * B2_avg
494
f[2] = 2 * v2_plus_ll * B2_avg
495
f[3] = 2 * v3_plus_ll * B2_avg
496
497
for k in eachcomponent(equations)
498
# Compute term Lorentz term
499
f2 = charge_ratio_ll[k] * (-B2_avg * B1_avg)
500
f3 = charge_ratio_ll[k] *
501
(-B2_avg * B2_avg + 0.5f0 * mag_norm_avg + pe_mean)
502
f4 = charge_ratio_ll[k] * (-B2_avg * B3_avg)
503
f5 = vk2_plus_ll[k] * pe_mean
504
505
# Compute multi-ion term (vanishes for NCOMP==1)
506
vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]
507
vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]
508
vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]
509
vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]
510
vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]
511
vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]
512
vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)
513
vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)
514
vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)
515
f5 += (B1_ll * (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) +
516
B3_ll * (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg))
517
518
# Compute Godunov-Powell term
519
f2 += charge_ratio_ll[k] * B1_ll * B2_avg
520
f3 += charge_ratio_ll[k] * B2_ll * B2_avg
521
f4 += charge_ratio_ll[k] * B3_ll * B2_avg
522
f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *
523
B2_avg
524
525
# Compute GLM term for the energy
526
f5 += v2_plus_ll * psi_ll * psi_avg
527
528
# Add to the flux vector (multiply by 2 because the non-conservative flux is
529
# multiplied by 0.5 whenever it's used in the Trixi code)
530
set_component!(f, k, 0, 2 * f2, 2 * f3, 2 * f4, 2 * f5,
531
equations)
532
end
533
# Compute GLM term for psi (multiply by 2 because the non-conservative flux is
534
# multiplied by 0.5 whenever it's used in the Trixi code)
535
f[end] = 2 * v2_plus_ll * psi_avg
536
end
537
538
return SVector(f)
539
end
540
541
@inline function flux_nonconservative_ruedaramirez_etal(u_ll, u_rr,
542
normal_direction::AbstractVector,
543
equations::IdealGlmMhdMultiIonEquations2D)
544
@unpack charge_to_mass = equations
545
# Unpack left and right states to get the magnetic field
546
B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)
547
B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)
548
psi_ll = divergence_cleaning_field(u_ll, equations)
549
psi_rr = divergence_cleaning_field(u_rr, equations)
550
B_dot_n_ll = B1_ll * normal_direction[1] +
551
B2_ll * normal_direction[2]
552
B_dot_n_rr = B1_rr * normal_direction[1] +
553
B2_rr * normal_direction[2]
554
B_dot_n_avg = 0.5f0 * (B_dot_n_ll + B_dot_n_rr)
555
556
# Compute important averages
557
B1_avg = 0.5f0 * (B1_ll + B1_rr)
558
B2_avg = 0.5f0 * (B2_ll + B2_rr)
559
B3_avg = 0.5f0 * (B3_ll + B3_rr)
560
mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2
561
mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2
562
mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)
563
psi_avg = 0.5f0 * (psi_ll + psi_rr)
564
565
# Mean electron pressure
566
pe_ll = equations.electron_pressure(u_ll, equations)
567
pe_rr = equations.electron_pressure(u_rr, equations)
568
pe_mean = 0.5f0 * (pe_ll + pe_rr)
569
570
# Compute charge ratio of u_ll
571
charge_ratio_ll = zero(MVector{ncomponents(equations), eltype(u_ll)})
572
total_electron_charge = zero(eltype(u_ll))
573
for k in eachcomponent(equations)
574
rho_k = u_ll[3 + (k - 1) * 5 + 1] # Extract densities from conserved variable vector
575
charge_ratio_ll[k] = rho_k * charge_to_mass[k]
576
total_electron_charge += charge_ratio_ll[k]
577
end
578
charge_ratio_ll ./= total_electron_charge
579
580
# Compute auxiliary variables
581
v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll,
582
vk3_plus_ll = charge_averaged_velocities(u_ll,
583
equations)
584
v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr,
585
vk3_plus_rr = charge_averaged_velocities(u_rr,
586
equations)
587
v_plus_dot_n_ll = (v1_plus_ll * normal_direction[1] +
588
v2_plus_ll * normal_direction[2])
589
f = zero(MVector{nvariables(equations), eltype(u_ll)})
590
591
# Entries of Godunov-Powell term for induction equation (multiply by 2 because the non-conservative flux is
592
# multiplied by 0.5 whenever it's used in the Trixi code)
593
f[1] = 2 * v1_plus_ll * B_dot_n_avg
594
f[2] = 2 * v2_plus_ll * B_dot_n_avg
595
f[3] = 2 * v3_plus_ll * B_dot_n_avg
596
597
for k in eachcomponent(equations)
598
# Compute term Lorentz term
599
f2 = charge_ratio_ll[k] *
600
((0.5f0 * mag_norm_avg + pe_mean) * normal_direction[1] -
601
B_dot_n_avg * B1_avg)
602
f3 = charge_ratio_ll[k] *
603
((0.5f0 * mag_norm_avg + pe_mean) * normal_direction[2] -
604
B_dot_n_avg * B2_avg)
605
f4 = charge_ratio_ll[k] *
606
(-B_dot_n_avg * B3_avg)
607
f5 = (vk1_plus_ll[k] * normal_direction[1] +
608
vk2_plus_ll[k] * normal_direction[2]) * pe_mean
609
610
# Compute multi-ion term (vanishes for NCOMP==1)
611
vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]
612
vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]
613
vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]
614
vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]
615
vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]
616
vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]
617
vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)
618
vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)
619
vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)
620
f5 += ((B2_ll * (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) +
621
B3_ll * (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg)) *
622
normal_direction[1] +
623
(B1_ll * (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) +
624
B3_ll * (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg)) *
625
normal_direction[2])
626
627
# Compute Godunov-Powell term
628
f2 += charge_ratio_ll[k] * B1_ll * B_dot_n_avg
629
f3 += charge_ratio_ll[k] * B2_ll * B_dot_n_avg
630
f4 += charge_ratio_ll[k] * B3_ll * B_dot_n_avg
631
f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *
632
B_dot_n_avg
633
634
# Compute GLM term for the energy
635
f5 += v_plus_dot_n_ll * psi_ll * psi_avg
636
637
# Add to the flux vector (multiply by 2 because the non-conservative flux is
638
# multiplied by 0.5 whenever it's used in the Trixi code)
639
set_component!(f, k, 0, 2 * f2, 2 * f3, 2 * f4, 2 * f5,
640
equations)
641
end
642
# Compute GLM term for psi (multiply by 2 because the non-conservative flux is
643
# multiplied by 0.5 whenever it's used in the Trixi code)
644
f[end] = 2 * v_plus_dot_n_ll * psi_avg
645
646
return SVector(f)
647
end
648
649
"""
650
flux_nonconservative_central(u_ll, u_rr, orientation::Integer,
651
equations::IdealGlmMhdMultiIonEquations2D)
652
653
Central non-conservative two-point "flux", where the symmetric parts are computed with standard averages.
654
The use of this term together with [`flux_central`](@ref)
655
with [`VolumeIntegralFluxDifferencing`](@ref) yields a "standard"
656
(weak-form) DGSEM discretization of the multi-ion GLM-MHD system. This flux can also be used to construct a
657
standard local Lax-Friedrichs flux using `surface_flux = (flux_lax_friedrichs, flux_nonconservative_central)`.
658
659
!!! info "Usage and Scaling of Non-Conservative Fluxes in Trixi"
660
The central non-conservative fluxes implemented in this function are written as the product
661
of local and symmetric parts, where the symmetric part is a standard average. These fluxes
662
are meant to be used in the same way as the conservative fluxes (i.e., flux + flux_noncons
663
in both volume and surface integrals). In this routine, the fluxes are multiplied by 2 because
664
the non-conservative fluxes are always multiplied by 0.5 whenever they are used in the Trixi code.
665
666
The term is composed of four individual non-conservative terms:
667
1. The Godunov-Powell term, which arises for plasmas with non-vanishing magnetic field divergence, and
668
is evaluated as a function of the charge-averaged velocity.
669
2. The Lorentz-force term, which becomes a conservative term in the limit of one ion species for vanishing
670
electron pressure gradients.
671
3. The "multi-ion" term, which vanishes in the limit of one ion species.
672
4. The GLM term, which is needed for Galilean invariance.
673
"""
674
@inline function flux_nonconservative_central(u_ll, u_rr, orientation::Integer,
675
equations::IdealGlmMhdMultiIonEquations2D)
676
@unpack charge_to_mass = equations
677
# Unpack left and right states to get the magnetic field
678
B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)
679
B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)
680
psi_ll = divergence_cleaning_field(u_ll, equations)
681
psi_rr = divergence_cleaning_field(u_rr, equations)
682
683
# Compute important averages
684
mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2
685
mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2
686
687
# Electron pressure
688
pe_ll = equations.electron_pressure(u_ll, equations)
689
pe_rr = equations.electron_pressure(u_rr, equations)
690
691
# Compute charge ratio of u_ll
692
charge_ratio_ll = zero(MVector{ncomponents(equations), eltype(u_ll)})
693
total_electron_charge = zero(real(equations))
694
for k in eachcomponent(equations)
695
rho_k = u_ll[3 + (k - 1) * 5 + 1]
696
charge_ratio_ll[k] = rho_k * charge_to_mass[k]
697
total_electron_charge += charge_ratio_ll[k]
698
end
699
charge_ratio_ll ./= total_electron_charge
700
701
# Compute auxiliary variables
702
v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll,
703
vk3_plus_ll = charge_averaged_velocities(u_ll,
704
equations)
705
v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr,
706
vk3_plus_rr = charge_averaged_velocities(u_rr,
707
equations)
708
709
f = zero(MVector{nvariables(equations), eltype(u_ll)})
710
711
if orientation == 1
712
# Entries of Godunov-Powell term for induction equation
713
f[1] = v1_plus_ll * (B1_ll + B1_rr)
714
f[2] = v2_plus_ll * (B1_ll + B1_rr)
715
f[3] = v3_plus_ll * (B1_ll + B1_rr)
716
for k in eachcomponent(equations)
717
# Compute Lorentz term
718
f2 = charge_ratio_ll[k] * ((0.5f0 * mag_norm_ll - B1_ll * B1_ll + pe_ll) +
719
(0.5f0 * mag_norm_rr - B1_rr * B1_rr + pe_rr))
720
f3 = charge_ratio_ll[k] * ((-B1_ll * B2_ll) + (-B1_rr * B2_rr))
721
f4 = charge_ratio_ll[k] * ((-B1_ll * B3_ll) + (-B1_rr * B3_rr))
722
f5 = vk1_plus_ll[k] * (pe_ll + pe_rr)
723
724
# Compute multi-ion term, which vanishes for NCOMP==1
725
vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]
726
vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]
727
vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]
728
vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]
729
vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]
730
vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]
731
f5 += (B2_ll * ((vk1_minus_ll * B2_ll - vk2_minus_ll * B1_ll) +
732
(vk1_minus_rr * B2_rr - vk2_minus_rr * B1_rr)) +
733
B3_ll * ((vk1_minus_ll * B3_ll - vk3_minus_ll * B1_ll) +
734
(vk1_minus_rr * B3_rr - vk3_minus_rr * B1_rr)))
735
736
# Compute Godunov-Powell term
737
f2 += charge_ratio_ll[k] * B1_ll * (B1_ll + B1_rr)
738
f3 += charge_ratio_ll[k] * B2_ll * (B1_ll + B1_rr)
739
f4 += charge_ratio_ll[k] * B3_ll * (B1_ll + B1_rr)
740
f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *
741
(B1_ll + B1_rr)
742
743
# Compute GLM term for the energy
744
f5 += v1_plus_ll * psi_ll * (psi_ll + psi_rr)
745
746
# Append to the flux vector
747
set_component!(f, k, 0, f2, f3, f4, f5, equations)
748
end
749
# Compute GLM term for psi
750
f[end] = v1_plus_ll * (psi_ll + psi_rr)
751
752
else #if orientation == 2
753
# Entries of Godunov-Powell term for induction equation
754
f[1] = v1_plus_ll * (B2_ll + B2_rr)
755
f[2] = v2_plus_ll * (B2_ll + B2_rr)
756
f[3] = v3_plus_ll * (B2_ll + B2_rr)
757
758
for k in eachcomponent(equations)
759
# Compute Lorentz term
760
f2 = charge_ratio_ll[k] * ((-B2_ll * B1_ll) + (-B2_rr * B1_rr))
761
f3 = charge_ratio_ll[k] * ((-B2_ll * B2_ll + 0.5f0 * mag_norm_ll + pe_ll) +
762
(-B2_rr * B2_rr + 0.5f0 * mag_norm_rr + pe_rr))
763
f4 = charge_ratio_ll[k] * ((-B2_ll * B3_ll) + (-B2_rr * B3_rr))
764
f5 = vk2_plus_ll[k] * (pe_ll + pe_rr)
765
766
# Compute multi-ion term (vanishes for NCOMP==1)
767
vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]
768
vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]
769
vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]
770
vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]
771
vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]
772
vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]
773
f5 += (B1_ll * ((vk2_minus_ll * B1_ll - vk1_minus_ll * B2_ll) +
774
(vk2_minus_rr * B1_rr - vk1_minus_rr * B2_rr)) +
775
B3_ll * ((vk2_minus_ll * B3_ll - vk3_minus_ll * B2_ll) +
776
(vk2_minus_rr * B3_rr - vk3_minus_rr * B2_rr)))
777
778
# Compute Godunov-Powell term
779
f2 += charge_ratio_ll[k] * B1_ll * (B2_ll + B2_rr)
780
f3 += charge_ratio_ll[k] * B2_ll * (B2_ll + B2_rr)
781
f4 += charge_ratio_ll[k] * B3_ll * (B2_ll + B2_rr)
782
f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) *
783
(B2_ll + B2_rr)
784
785
# Compute GLM term for the energy
786
f5 += v2_plus_ll * psi_ll * (psi_ll + psi_rr)
787
788
# Append to the flux vector
789
set_component!(f, k, 0, f2, f3, f4, f5, equations)
790
end
791
# Compute GLM term for psi
792
f[end] = v2_plus_ll * (psi_ll + psi_rr)
793
end
794
795
return SVector(f)
796
end
797
798
@inline function flux_nonconservative_central(u_ll, u_rr,
799
normal_direction::AbstractVector,
800
equations::IdealGlmMhdMultiIonEquations2D)
801
@unpack charge_to_mass = equations
802
# Unpack left and right states to get the magnetic field
803
B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)
804
B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)
805
psi_ll = divergence_cleaning_field(u_ll, equations)
806
psi_rr = divergence_cleaning_field(u_rr, equations)
807
808
# Compute important averages
809
mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2
810
mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2
811
812
# Electron pressure
813
pe_ll = equations.electron_pressure(u_ll, equations)
814
pe_rr = equations.electron_pressure(u_rr, equations)
815
816
# Compute charge ratio of u_ll
817
charge_ratio_ll = zero(MVector{ncomponents(equations), eltype(u_ll)})
818
total_electron_charge = zero(real(equations))
819
for k in eachcomponent(equations)
820
rho_k = u_ll[3 + (k - 1) * 5 + 1]
821
charge_ratio_ll[k] = rho_k * charge_to_mass[k]
822
total_electron_charge += charge_ratio_ll[k]
823
end
824
charge_ratio_ll ./= total_electron_charge
825
826
# Compute auxiliary variables
827
v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll,
828
vk3_plus_ll = charge_averaged_velocities(u_ll,
829
equations)
830
v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr,
831
vk3_plus_rr = charge_averaged_velocities(u_rr,
832
equations)
833
834
f = zero(MVector{nvariables(equations), eltype(u_ll)})
835
836
# Compute B_dot_n, v_dot_B_ll, and psi terms once (they are constant for all species)
837
B1_sum = B1_ll + B1_rr
838
B2_sum = B2_ll + B2_rr
839
B_dot_n = B1_sum * normal_direction[1] + B2_sum * normal_direction[2]
840
v_dot_B_ll = v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll
841
psi_sum = psi_ll + psi_rr
842
843
# Entries of Godunov-Powell term for induction equation
844
f[1] = v1_plus_ll * B_dot_n
845
f[2] = v2_plus_ll * B_dot_n
846
f[3] = v3_plus_ll * B_dot_n
847
848
for k in eachcomponent(equations)
849
# Compute Lorentz term
850
f2 = charge_ratio_ll[k] *
851
((0.5f0 * mag_norm_ll - B1_ll * B1_ll + pe_ll) +
852
(0.5f0 * mag_norm_rr - B1_rr * B1_rr + pe_rr)) * normal_direction[1] +
853
charge_ratio_ll[k] * ((-B2_ll * B1_ll) + (-B2_rr * B1_rr)) *
854
normal_direction[2]
855
f3 = charge_ratio_ll[k] * ((-B1_ll * B2_ll) + (-B1_rr * B2_rr)) *
856
normal_direction[1] +
857
charge_ratio_ll[k] *
858
((-B2_ll * B2_ll + 0.5f0 * mag_norm_ll + pe_ll) +
859
(-B2_rr * B2_rr + 0.5f0 * mag_norm_rr + pe_rr)) * normal_direction[2]
860
f4 = charge_ratio_ll[k] * ((-B1_ll * B3_ll) + (-B1_rr * B3_rr)) *
861
normal_direction[1] +
862
charge_ratio_ll[k] * ((-B2_ll * B3_ll) + (-B2_rr * B3_rr)) *
863
normal_direction[2]
864
f5 = vk1_plus_ll[k] * (pe_ll + pe_rr) * normal_direction[1] +
865
vk2_plus_ll[k] * (pe_ll + pe_rr) * normal_direction[2]
866
867
# Compute multi-ion term, which vanishes for NCOMP==1
868
vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]
869
vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]
870
vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]
871
vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]
872
vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]
873
vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]
874
f5 += (B2_ll * ((vk1_minus_ll * B2_ll - vk2_minus_ll * B1_ll) +
875
(vk1_minus_rr * B2_rr - vk2_minus_rr * B1_rr)) +
876
B3_ll * ((vk1_minus_ll * B3_ll - vk3_minus_ll * B1_ll) +
877
(vk1_minus_rr * B3_rr - vk3_minus_rr * B1_rr))) * normal_direction[1]
878
f5 += (B1_ll * ((vk2_minus_ll * B1_ll - vk1_minus_ll * B2_ll) +
879
(vk2_minus_rr * B1_rr - vk1_minus_rr * B2_rr)) +
880
B3_ll * ((vk2_minus_ll * B3_ll - vk3_minus_ll * B2_ll) +
881
(vk2_minus_rr * B3_rr - vk3_minus_rr * B2_rr))) * normal_direction[2]
882
883
# Compute Godunov-Powell term
884
f2 += charge_ratio_ll[k] * B1_ll * B_dot_n
885
f3 += charge_ratio_ll[k] * B2_ll * B_dot_n
886
f4 += charge_ratio_ll[k] * B3_ll * B_dot_n
887
f5 += v_dot_B_ll * B_dot_n
888
889
# Compute GLM term for the energy
890
f5 += (v1_plus_ll * normal_direction[1] + v2_plus_ll * normal_direction[2]) *
891
psi_ll * psi_sum
892
893
# Append to the flux vector
894
set_component!(f, k, 0, f2, f3, f4, f5, equations)
895
end
896
# Compute GLM term for psi
897
f[end] = (v1_plus_ll * normal_direction[1] + v2_plus_ll * normal_direction[2]) *
898
psi_sum
899
900
return SVector(f)
901
end
902
903
"""
904
flux_ruedaramirez_etal(u_ll, u_rr, orientation, equations::IdealGlmMhdMultiIonEquations2D)
905
906
Entropy conserving two-point flux for the multi-ion GLM-MHD equations from
907
- A. Rueda-Ramírez, A. Sikstel, G. Gassner, An Entropy-Stable Discontinuous Galerkin Discretization
908
of the Ideal Multi-Ion Magnetohydrodynamics System (2024). Journal of Computational Physics.
909
[DOI: 10.1016/j.jcp.2024.113655](https://doi.org/10.1016/j.jcp.2024.113655).
910
911
This flux (together with the MHD non-conservative term) is consistent in the case of one ion species with the flux of:
912
- Derigs et al. (2018). Ideal GLM-MHD: About the entropy consistent nine-wave magnetic field
913
divergence diminishing ideal magnetohydrodynamics equations for multi-ion
914
[DOI: 10.1016/j.jcp.2018.03.002](https://doi.org/10.1016/j.jcp.2018.03.002)
915
"""
916
function flux_ruedaramirez_etal(u_ll, u_rr, orientation::Integer,
917
equations::IdealGlmMhdMultiIonEquations2D)
918
@unpack gammas = equations
919
# Unpack left and right states to get the magnetic field
920
B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)
921
B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)
922
psi_ll = divergence_cleaning_field(u_ll, equations)
923
psi_rr = divergence_cleaning_field(u_rr, equations)
924
925
v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll,
926
vk3_plus_ll = charge_averaged_velocities(u_ll,
927
equations)
928
v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr,
929
vk3_plus_rr = charge_averaged_velocities(u_rr,
930
equations)
931
932
f = zero(MVector{nvariables(equations), eltype(u_ll)})
933
934
# Compute averages for global variables
935
v1_plus_avg = 0.5f0 * (v1_plus_ll + v1_plus_rr)
936
v2_plus_avg = 0.5f0 * (v2_plus_ll + v2_plus_rr)
937
v3_plus_avg = 0.5f0 * (v3_plus_ll + v3_plus_rr)
938
B1_avg = 0.5f0 * (B1_ll + B1_rr)
939
B2_avg = 0.5f0 * (B2_ll + B2_rr)
940
B3_avg = 0.5f0 * (B3_ll + B3_rr)
941
mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2
942
mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2
943
mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)
944
psi_avg = 0.5f0 * (psi_ll + psi_rr)
945
946
if orientation == 1
947
psi_B1_avg = 0.5f0 * (B1_ll * psi_ll + B1_rr * psi_rr)
948
949
# Magnetic field components from f^MHD
950
f6 = equations.c_h * psi_avg
951
f7 = v1_plus_avg * B2_avg - v2_plus_avg * B1_avg
952
f8 = v1_plus_avg * B3_avg - v3_plus_avg * B1_avg
953
f9 = equations.c_h * B1_avg
954
955
# Start building the flux
956
f[1] = f6
957
f[2] = f7
958
f[3] = f8
959
f[end] = f9
960
961
# Iterate over all components
962
for k in eachcomponent(equations)
963
# Unpack left and right states
964
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll = get_component(k,
965
u_ll,
966
equations)
967
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr = get_component(k,
968
u_rr,
969
equations)
970
rho_inv_ll = 1 / rho_ll
971
v1_ll = rho_v1_ll * rho_inv_ll
972
v2_ll = rho_v2_ll * rho_inv_ll
973
v3_ll = rho_v3_ll * rho_inv_ll
974
rho_inv_rr = 1 / rho_rr
975
v1_rr = rho_v1_rr * rho_inv_rr
976
v2_rr = rho_v2_rr * rho_inv_rr
977
v3_rr = rho_v3_rr * rho_inv_rr
978
vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^2
979
vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^2
980
981
p_ll = (gammas[k] - 1) *
982
(rho_e_total_ll - 0.5f0 * rho_ll * vel_norm_ll -
983
0.5f0 * mag_norm_ll -
984
0.5f0 * psi_ll^2)
985
p_rr = (gammas[k] - 1) *
986
(rho_e_total_rr - 0.5f0 * rho_rr * vel_norm_rr -
987
0.5f0 * mag_norm_rr -
988
0.5f0 * psi_rr^2)
989
beta_ll = 0.5f0 * rho_ll / p_ll
990
beta_rr = 0.5f0 * rho_rr / p_rr
991
# for convenience store vk_plus⋅B
992
vel_dot_mag_ll = vk1_plus_ll[k] * B1_ll + vk2_plus_ll[k] * B2_ll +
993
vk3_plus_ll[k] * B3_ll
994
vel_dot_mag_rr = vk1_plus_rr[k] * B1_rr + vk2_plus_rr[k] * B2_rr +
995
vk3_plus_rr[k] * B3_rr
996
997
# Compute the necessary mean values needed for either direction
998
rho_avg = 0.5f0 * (rho_ll + rho_rr)
999
rho_mean = ln_mean(rho_ll, rho_rr)
1000
beta_mean = ln_mean(beta_ll, beta_rr)
1001
beta_avg = 0.5f0 * (beta_ll + beta_rr)
1002
p_mean = 0.5f0 * rho_avg / beta_avg
1003
v1_avg = 0.5f0 * (v1_ll + v1_rr)
1004
v2_avg = 0.5f0 * (v2_ll + v2_rr)
1005
v3_avg = 0.5f0 * (v3_ll + v3_rr)
1006
vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)
1007
vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)
1008
vk1_plus_avg = 0.5f0 * (vk1_plus_ll[k] + vk1_plus_rr[k])
1009
vk2_plus_avg = 0.5f0 * (vk2_plus_ll[k] + vk2_plus_rr[k])
1010
vk3_plus_avg = 0.5f0 * (vk3_plus_ll[k] + vk3_plus_rr[k])
1011
# v_minus
1012
vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]
1013
vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]
1014
vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]
1015
vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]
1016
vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]
1017
vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]
1018
vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)
1019
vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)
1020
vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)
1021
1022
# Fill the fluxes for the mass and momentum equations
1023
f1 = rho_mean * v1_avg
1024
f2 = f1 * v1_avg + p_mean
1025
f3 = f1 * v2_avg
1026
f4 = f1 * v3_avg
1027
1028
# total energy flux is complicated and involves the previous eight components
1029
v1_plus_mag_avg = 0.5f0 * (vk1_plus_ll[k] * mag_norm_ll +
1030
vk1_plus_rr[k] * mag_norm_rr)
1031
# Euler part
1032
f5 = f1 * 0.5f0 * (1 / (gammas[k] - 1) / beta_mean - vel_norm_avg) +
1033
f2 * v1_avg + f3 * v2_avg + f4 * v3_avg
1034
# MHD part
1035
f5 += (f6 * B1_avg + f7 * B2_avg + f8 * B3_avg - 0.5f0 * v1_plus_mag_avg +
1036
B1_avg * vel_dot_mag_avg # Same terms as in Derigs (but with v_plus)
1037
+ f9 * psi_avg - equations.c_h * psi_B1_avg # GLM term
1038
+
1039
0.5f0 * vk1_plus_avg * mag_norm_avg -
1040
vk1_plus_avg * B1_avg * B1_avg - vk2_plus_avg * B1_avg * B2_avg -
1041
vk3_plus_avg * B1_avg * B3_avg # Additional terms related to the Lorentz non-conservative term (momentum eqs)
1042
-
1043
B2_avg * (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) -
1044
B3_avg * (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg)) # Terms related to the multi-ion non-conservative term (induction equation!)
1045
1046
set_component!(f, k, f1, f2, f3, f4, f5, equations)
1047
end
1048
else #if orientation == 2
1049
psi_B2_avg = 0.5f0 * (B2_ll * psi_ll + B2_rr * psi_rr)
1050
1051
# Magnetic field components from f^MHD
1052
f6 = v2_plus_avg * B1_avg - v1_plus_avg * B2_avg
1053
f7 = equations.c_h * psi_avg
1054
f8 = v2_plus_avg * B3_avg - v3_plus_avg * B2_avg
1055
f9 = equations.c_h * B2_avg
1056
1057
# Start building the flux
1058
f[1] = f6
1059
f[2] = f7
1060
f[3] = f8
1061
f[end] = f9
1062
1063
# Iterate over all components
1064
for k in eachcomponent(equations)
1065
# Unpack left and right states
1066
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll = get_component(k,
1067
u_ll,
1068
equations)
1069
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr = get_component(k,
1070
u_rr,
1071
equations)
1072
1073
rho_inv_ll = 1 / rho_ll
1074
v1_ll = rho_v1_ll * rho_inv_ll
1075
v2_ll = rho_v2_ll * rho_inv_ll
1076
v3_ll = rho_v3_ll * rho_inv_ll
1077
rho_inv_rr = 1 / rho_rr
1078
v1_rr = rho_v1_rr * rho_inv_rr
1079
v2_rr = rho_v2_rr * rho_inv_rr
1080
v3_rr = rho_v3_rr * rho_inv_rr
1081
vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^2
1082
vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^2
1083
1084
p_ll = (gammas[k] - 1) *
1085
(rho_e_total_ll - 0.5f0 * rho_ll * vel_norm_ll -
1086
0.5f0 * mag_norm_ll -
1087
0.5f0 * psi_ll^2)
1088
p_rr = (gammas[k] - 1) *
1089
(rho_e_total_rr - 0.5f0 * rho_rr * vel_norm_rr -
1090
0.5f0 * mag_norm_rr -
1091
0.5f0 * psi_rr^2)
1092
beta_ll = 0.5f0 * rho_ll / p_ll
1093
beta_rr = 0.5f0 * rho_rr / p_rr
1094
# for convenience store vk_plus⋅B
1095
vel_dot_mag_ll = vk1_plus_ll[k] * B1_ll + vk2_plus_ll[k] * B2_ll +
1096
vk3_plus_ll[k] * B3_ll
1097
vel_dot_mag_rr = vk1_plus_rr[k] * B1_rr + vk2_plus_rr[k] * B2_rr +
1098
vk3_plus_rr[k] * B3_rr
1099
1100
# Compute the necessary mean values needed for either direction
1101
rho_avg = 0.5f0 * (rho_ll + rho_rr)
1102
rho_mean = ln_mean(rho_ll, rho_rr)
1103
beta_mean = ln_mean(beta_ll, beta_rr)
1104
beta_avg = 0.5f0 * (beta_ll + beta_rr)
1105
p_mean = 0.5f0 * rho_avg / beta_avg
1106
v1_avg = 0.5f0 * (v1_ll + v1_rr)
1107
v2_avg = 0.5f0 * (v2_ll + v2_rr)
1108
v3_avg = 0.5f0 * (v3_ll + v3_rr)
1109
vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)
1110
vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)
1111
vk1_plus_avg = 0.5f0 * (vk1_plus_ll[k] + vk1_plus_rr[k])
1112
vk2_plus_avg = 0.5f0 * (vk2_plus_ll[k] + vk2_plus_rr[k])
1113
vk3_plus_avg = 0.5f0 * (vk3_plus_ll[k] + vk3_plus_rr[k])
1114
# v_minus
1115
vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]
1116
vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]
1117
vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]
1118
vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]
1119
vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]
1120
vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]
1121
vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)
1122
vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)
1123
vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)
1124
1125
# Fill the fluxes for the mass and momentum equations
1126
f1 = rho_mean * v2_avg
1127
f2 = f1 * v1_avg
1128
f3 = f1 * v2_avg + p_mean
1129
f4 = f1 * v3_avg
1130
1131
# total energy flux is complicated and involves the previous eight components
1132
v2_plus_mag_avg = 0.5f0 * (vk2_plus_ll[k] * mag_norm_ll +
1133
vk2_plus_rr[k] * mag_norm_rr)
1134
# Euler part
1135
f5 = f1 * 0.5f0 * (1 / (gammas[k] - 1) / beta_mean - vel_norm_avg) +
1136
f2 * v1_avg + f3 * v2_avg + f4 * v3_avg
1137
# MHD part
1138
f5 += (f6 * B1_avg + f7 * B2_avg + f8 * B3_avg - 0.5f0 * v2_plus_mag_avg +
1139
B2_avg * vel_dot_mag_avg # Same terms as in Derigs (but with v_plus)
1140
+ f9 * psi_avg - equations.c_h * psi_B2_avg # GLM term
1141
+
1142
0.5f0 * vk2_plus_avg * mag_norm_avg -
1143
vk1_plus_avg * B2_avg * B1_avg - vk2_plus_avg * B2_avg * B2_avg -
1144
vk3_plus_avg * B2_avg * B3_avg # Additional terms related to the Lorentz non-conservative term (momentum eqs)
1145
-
1146
B1_avg * (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) -
1147
B3_avg * (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg)) # Terms related to the multi-ion non-conservative term (induction equation!)
1148
1149
set_component!(f, k, f1, f2, f3, f4, f5, equations)
1150
end
1151
end
1152
1153
return SVector(f)
1154
end
1155
1156
function flux_ruedaramirez_etal(u_ll, u_rr, normal_direction::AbstractVector,
1157
equations::IdealGlmMhdMultiIonEquations2D)
1158
@unpack gammas = equations
1159
# Unpack left and right states to get the magnetic field
1160
B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)
1161
B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)
1162
psi_ll = divergence_cleaning_field(u_ll, equations)
1163
psi_rr = divergence_cleaning_field(u_rr, equations)
1164
1165
v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll,
1166
vk3_plus_ll = charge_averaged_velocities(u_ll,
1167
equations)
1168
v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr,
1169
vk3_plus_rr = charge_averaged_velocities(u_rr,
1170
equations)
1171
1172
f = zero(MVector{nvariables(equations), eltype(u_ll)})
1173
1174
# Compute averages for global variables
1175
v1_plus_avg = 0.5f0 * (v1_plus_ll + v1_plus_rr)
1176
v2_plus_avg = 0.5f0 * (v2_plus_ll + v2_plus_rr)
1177
v3_plus_avg = 0.5f0 * (v3_plus_ll + v3_plus_rr)
1178
B1_avg = 0.5f0 * (B1_ll + B1_rr)
1179
B2_avg = 0.5f0 * (B2_ll + B2_rr)
1180
B3_avg = 0.5f0 * (B3_ll + B3_rr)
1181
mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2
1182
mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2
1183
mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)
1184
psi_avg = 0.5f0 * (psi_ll + psi_rr)
1185
1186
psi_B1_avg = 0.5f0 * (B1_ll * psi_ll + B1_rr * psi_rr)
1187
psi_B2_avg = 0.5f0 * (B2_ll * psi_ll + B2_rr * psi_rr)
1188
1189
# Compute B_dot_n_avg and psi_B_dot_n_avg once (they are constant for all species)
1190
B_dot_n_avg = B1_avg * normal_direction[1] + B2_avg * normal_direction[2]
1191
psi_B_dot_n_avg = psi_B1_avg * normal_direction[1] +
1192
psi_B2_avg * normal_direction[2]
1193
1194
# Magnetic field components from f^MHD
1195
f6 = (equations.c_h * psi_avg * normal_direction[1] +
1196
(v2_plus_avg * B1_avg - v1_plus_avg * B2_avg) * normal_direction[2])
1197
f7 = ((v1_plus_avg * B2_avg - v2_plus_avg * B1_avg) * normal_direction[1] +
1198
equations.c_h * psi_avg * normal_direction[2])
1199
f8 = ((v1_plus_avg * B3_avg - v3_plus_avg * B1_avg) * normal_direction[1] +
1200
(v2_plus_avg * B3_avg - v3_plus_avg * B2_avg) * normal_direction[2])
1201
f9 = (equations.c_h * B1_avg * normal_direction[1] +
1202
equations.c_h * B2_avg * normal_direction[2])
1203
1204
# Start building the flux
1205
f[1] = f6
1206
f[2] = f7
1207
f[3] = f8
1208
f[end] = f9
1209
1210
# Iterate over all components
1211
for k in eachcomponent(equations)
1212
# Unpack left and right states
1213
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll = get_component(k, u_ll,
1214
equations)
1215
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr = get_component(k, u_rr,
1216
equations)
1217
rho_inv_ll = 1 / rho_ll
1218
v1_ll = rho_v1_ll * rho_inv_ll
1219
v2_ll = rho_v2_ll * rho_inv_ll
1220
v3_ll = rho_v3_ll * rho_inv_ll
1221
rho_inv_rr = 1 / rho_rr
1222
v1_rr = rho_v1_rr * rho_inv_rr
1223
v2_rr = rho_v2_rr * rho_inv_rr
1224
v3_rr = rho_v3_rr * rho_inv_rr
1225
vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^2
1226
vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^2
1227
1228
p_ll = (gammas[k] - 1) *
1229
(rho_e_total_ll - 0.5f0 * rho_ll * vel_norm_ll - 0.5f0 * mag_norm_ll -
1230
0.5f0 * psi_ll^2)
1231
p_rr = (gammas[k] - 1) *
1232
(rho_e_total_rr - 0.5f0 * rho_rr * vel_norm_rr - 0.5f0 * mag_norm_rr -
1233
0.5f0 * psi_rr^2)
1234
beta_ll = 0.5f0 * rho_ll / p_ll
1235
beta_rr = 0.5f0 * rho_rr / p_rr
1236
# for convenience store vk_plus⋅B
1237
vel_dot_mag_ll = vk1_plus_ll[k] * B1_ll + vk2_plus_ll[k] * B2_ll +
1238
vk3_plus_ll[k] * B3_ll
1239
vel_dot_mag_rr = vk1_plus_rr[k] * B1_rr + vk2_plus_rr[k] * B2_rr +
1240
vk3_plus_rr[k] * B3_rr
1241
1242
# Compute the necessary mean values needed for either direction
1243
rho_avg = 0.5f0 * (rho_ll + rho_rr)
1244
rho_mean = ln_mean(rho_ll, rho_rr)
1245
beta_mean = ln_mean(beta_ll, beta_rr)
1246
beta_avg = 0.5f0 * (beta_ll + beta_rr)
1247
p_mean = 0.5f0 * rho_avg / beta_avg
1248
v1_avg = 0.5f0 * (v1_ll + v1_rr)
1249
v2_avg = 0.5f0 * (v2_ll + v2_rr)
1250
v3_avg = 0.5f0 * (v3_ll + v3_rr)
1251
vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)
1252
vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)
1253
vk1_plus_avg = 0.5f0 * (vk1_plus_ll[k] + vk1_plus_rr[k])
1254
vk2_plus_avg = 0.5f0 * (vk2_plus_ll[k] + vk2_plus_rr[k])
1255
vk3_plus_avg = 0.5f0 * (vk3_plus_ll[k] + vk3_plus_rr[k])
1256
# v_minus
1257
vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k]
1258
vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k]
1259
vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k]
1260
vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k]
1261
vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k]
1262
vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k]
1263
vk1_minus_avg = 0.5f0 * (vk1_minus_ll + vk1_minus_rr)
1264
vk2_minus_avg = 0.5f0 * (vk2_minus_ll + vk2_minus_rr)
1265
vk3_minus_avg = 0.5f0 * (vk3_minus_ll + vk3_minus_rr)
1266
1267
# Fill the fluxes for the mass and momentum equations
1268
f1 = rho_mean * (v1_avg * normal_direction[1] + v2_avg * normal_direction[2])
1269
f2 = f1 * v1_avg + p_mean * normal_direction[1]
1270
f3 = f1 * v2_avg + p_mean * normal_direction[2]
1271
f4 = f1 * v3_avg
1272
1273
# total energy flux is complicated and involves the previous eight components
1274
vk1_plus_mag_avg = 0.5f0 * (vk1_plus_ll[k] * mag_norm_ll +
1275
vk1_plus_rr[k] * mag_norm_rr)
1276
vk2_plus_mag_avg = 0.5f0 * (vk2_plus_ll[k] * mag_norm_ll +
1277
vk2_plus_rr[k] * mag_norm_rr)
1278
# Euler part
1279
f5 = f1 * 0.5f0 * (1 / (gammas[k] - 1) / beta_mean - vel_norm_avg) +
1280
f2 * v1_avg + f3 * v2_avg + f4 * v3_avg
1281
# MHD part
1282
f5 += (f6 * B1_avg + f7 * B2_avg + f8 * B3_avg -
1283
0.5f0 * vk1_plus_mag_avg * normal_direction[1] -
1284
0.5f0 * vk2_plus_mag_avg * normal_direction[2] +
1285
B_dot_n_avg * vel_dot_mag_avg # Same terms as in Derigs (but with v_plus)
1286
+ f9 * psi_avg -
1287
equations.c_h * psi_B_dot_n_avg # GLM term
1288
+
1289
0.5f0 *
1290
(vk1_plus_avg * normal_direction[1] + vk2_plus_avg * normal_direction[2]) *
1291
mag_norm_avg -
1292
vk1_plus_avg * B_dot_n_avg * B1_avg -
1293
vk2_plus_avg * B_dot_n_avg * B2_avg -
1294
vk3_plus_avg * B_dot_n_avg * B3_avg # Additional terms related to the Lorentz non-conservative term (momentum eqs)
1295
-
1296
B2_avg * (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) *
1297
normal_direction[1] -
1298
B3_avg * (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg) *
1299
normal_direction[1] -
1300
B1_avg * (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) *
1301
normal_direction[2] -
1302
B3_avg * (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg) *
1303
normal_direction[2]) # Terms related to the multi-ion non-conservative term (induction equation!)
1304
1305
set_component!(f, k, f1, f2, f3, f4, f5, equations)
1306
end
1307
1308
return SVector(f)
1309
end
1310
1311
# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation
1312
# This routine approximates the maximum wave speed as sum of the maximum ion velocity
1313
# for all species and the maximum magnetosonic speed.
1314
@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,
1315
equations::IdealGlmMhdMultiIonEquations2D)
1316
# Calculate fast magnetoacoustic wave speeds
1317
# left
1318
cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)
1319
# right
1320
cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)
1321
1322
# Calculate velocities
1323
v_ll = zero(eltype(u_ll))
1324
v_rr = zero(eltype(u_rr))
1325
if orientation == 1
1326
for k in eachcomponent(equations)
1327
rho, rho_v1, _ = get_component(k, u_ll, equations)
1328
v_ll = max(v_ll, abs(rho_v1 / rho))
1329
rho, rho_v1, _ = get_component(k, u_rr, equations)
1330
v_rr = max(v_rr, abs(rho_v1 / rho))
1331
end
1332
else #if orientation == 2
1333
for k in eachcomponent(equations)
1334
rho, rho_v1, rho_v2, _ = get_component(k, u_ll, equations)
1335
v_ll = max(v_ll, abs(rho_v2 / rho))
1336
rho, rho_v1, rho_v2, _ = get_component(k, u_rr, equations)
1337
v_rr = max(v_rr, abs(rho_v2 / rho))
1338
end
1339
end
1340
1341
return max(abs(v_ll), abs(v_rr)) + max(cf_ll, cf_rr)
1342
end
1343
1344
@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,
1345
equations::IdealGlmMhdMultiIonEquations2D)
1346
# Calculate fast magnetoacoustic wave speeds
1347
# left
1348
cf_ll = calc_fast_wavespeed(u_ll, normal_direction, equations)
1349
# right
1350
cf_rr = calc_fast_wavespeed(u_rr, normal_direction, equations)
1351
1352
# Calculate velocities
1353
v_ll = zero(eltype(u_ll))
1354
v_rr = zero(eltype(u_rr))
1355
for k in eachcomponent(equations)
1356
rho, rho_v1, rho_v2, _ = get_component(k, u_ll, equations)
1357
v_ll = max(v_ll,
1358
abs((rho_v1 * normal_direction[1] + rho_v2 * normal_direction[2]) /
1359
rho))
1360
rho, rho_v1, rho_v2, _ = get_component(k, u_rr, equations)
1361
v_rr = max(v_rr,
1362
abs((rho_v1 * normal_direction[1] + rho_v2 * normal_direction[2]) /
1363
rho))
1364
end
1365
1366
return max(abs(v_ll), abs(v_rr)) + max(cf_ll, cf_rr)
1367
end
1368
1369
# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`
1370
@inline function max_abs_speed(u_ll, u_rr, orientation::Integer,
1371
equations::IdealGlmMhdMultiIonEquations2D)
1372
# Calculate fast magnetoacoustic wave speeds
1373
# left
1374
cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)
1375
# right
1376
cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)
1377
1378
# Calculate velocities
1379
v_ll = zero(eltype(u_ll))
1380
v_rr = zero(eltype(u_rr))
1381
if orientation == 1
1382
for k in eachcomponent(equations)
1383
rho, rho_v1, _ = get_component(k, u_ll, equations)
1384
v_ll = max(v_ll, abs(rho_v1 / rho))
1385
rho, rho_v1, _ = get_component(k, u_rr, equations)
1386
v_rr = max(v_rr, abs(rho_v1 / rho))
1387
end
1388
else #if orientation == 2
1389
for k in eachcomponent(equations)
1390
rho, rho_v1, rho_v2, _ = get_component(k, u_ll, equations)
1391
v_ll = max(v_ll, abs(rho_v2 / rho))
1392
rho, rho_v1, rho_v2, _ = get_component(k, u_rr, equations)
1393
v_rr = max(v_rr, abs(rho_v2 / rho))
1394
end
1395
end
1396
1397
return max(abs(v_ll) + cf_ll, abs(v_rr) + cf_rr)
1398
end
1399
1400
@inline function max_abs_speeds(u, equations::IdealGlmMhdMultiIonEquations2D)
1401
v1 = zero(real(equations))
1402
v2 = zero(real(equations))
1403
for k in eachcomponent(equations)
1404
rho, rho_v1, rho_v2, _ = get_component(k, u, equations)
1405
v1 = max(v1, abs(rho_v1 / rho))
1406
v2 = max(v2, abs(rho_v2 / rho))
1407
end
1408
1409
cf_x_direction = calc_fast_wavespeed(u, 1, equations)
1410
cf_y_direction = calc_fast_wavespeed(u, 2, equations)
1411
1412
return (abs(v1) + cf_x_direction, abs(v2) + cf_y_direction)
1413
end
1414
1415
# Compute the fastest wave speed for ideal multi-ion GLM-MHD equations: c_f, the fast
1416
# magnetoacoustic eigenvalue. This routine computes the fast magnetosonic speed for each ion
1417
# species using the single-fluid MHD expressions and approximates the multi-ion c_f as
1418
# the maximum of these individual magnetosonic speeds.
1419
@inline function calc_fast_wavespeed(cons, orientation::Integer,
1420
equations::IdealGlmMhdMultiIonEquations2D)
1421
B1, B2, B3 = magnetic_field(cons, equations)
1422
psi = divergence_cleaning_field(cons, equations)
1423
1424
c_f = zero(real(equations))
1425
for k in eachcomponent(equations)
1426
rho, rho_v1, rho_v2, rho_v3, rho_e_total = get_component(k, cons, equations)
1427
1428
rho_inv = 1 / rho
1429
v1 = rho_v1 * rho_inv
1430
v2 = rho_v2 * rho_inv
1431
v3 = rho_v3 * rho_inv
1432
gamma = equations.gammas[k]
1433
p = (gamma - 1) *
1434
(rho_e_total - 0.5f0 * rho * (v1^2 + v2^2 + v3^2) -
1435
0.5f0 * (B1^2 + B2^2 + B3^2) -
1436
0.5f0 * psi^2)
1437
a_square = gamma * p * rho_inv
1438
inv_sqrt_rho = 1 / sqrt(rho)
1439
1440
b1 = B1 * inv_sqrt_rho
1441
b2 = B2 * inv_sqrt_rho
1442
b3 = B3 * inv_sqrt_rho
1443
b_square = b1^2 + b2^2 + b3^2
1444
1445
if orientation == 1
1446
c_f = max(c_f,
1447
sqrt(0.5f0 * (a_square + b_square) +
1448
0.5f0 *
1449
sqrt((a_square + b_square)^2 - 4 * a_square * b1^2)))
1450
else #if orientation == 2
1451
c_f = max(c_f,
1452
sqrt(0.5f0 * (a_square + b_square) +
1453
0.5f0 *
1454
sqrt((a_square + b_square)^2 - 4 * a_square * b2^2)))
1455
end
1456
end
1457
1458
return c_f
1459
end
1460
1461
@inline function calc_fast_wavespeed(cons, normal_direction::AbstractVector,
1462
equations::IdealGlmMhdMultiIonEquations2D)
1463
B1, B2, B3 = magnetic_field(cons, equations)
1464
psi = divergence_cleaning_field(cons, equations)
1465
1466
norm_squared = (normal_direction[1]^2 + normal_direction[2]^2)
1467
1468
c_f = zero(real(equations))
1469
for k in eachcomponent(equations)
1470
rho, rho_v1, rho_v2, rho_v3, rho_e_total = get_component(k, cons, equations)
1471
1472
rho_inv = 1 / rho
1473
v1 = rho_v1 * rho_inv
1474
v2 = rho_v2 * rho_inv
1475
v3 = rho_v3 * rho_inv
1476
gamma = equations.gammas[k]
1477
p = (gamma - 1) *
1478
(rho_e_total - 0.5f0 * rho * (v1^2 + v2^2 + v3^2) -
1479
0.5f0 * (B1^2 + B2^2 + B3^2) -
1480
0.5f0 * psi^2)
1481
a_square = gamma * p * rho_inv
1482
inv_sqrt_rho = 1 / sqrt(rho)
1483
1484
b1 = B1 * inv_sqrt_rho
1485
b2 = B2 * inv_sqrt_rho
1486
b3 = B3 * inv_sqrt_rho
1487
b_square = b1^2 + b2^2 + b3^2
1488
# Properly normalize the magnetic field projection onto the unit normal
1489
b_dot_n_squared = (b1 * normal_direction[1] + b2 * normal_direction[2])^2 /
1490
norm_squared
1491
1492
c_f = max(c_f,
1493
sqrt((0.5f0 * (a_square + b_square) +
1494
0.5f0 *
1495
sqrt((a_square + b_square)^2 - 4 * a_square * b_dot_n_squared)) *
1496
norm_squared))
1497
end
1498
1499
return c_f
1500
end
1501
end # @muladd
1502
1503