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_multicomponent_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
IdealGlmMhdMulticomponentEquations2D(; gammas, gas_constants)
10
11
The ideal compressible multicomponent GLM-MHD equations
12
```math
13
\frac{\partial}{\partial t}
14
\begin{pmatrix}
15
\rho \mathbf{v} \\ \rho e_{\text{total}} \\ \mathbf{B} \\ \psi \\ \rho_1 \\ \vdots \\ \rho_{n}
16
\end{pmatrix}
17
+
18
\nabla \cdot
19
\begin{pmatrix}
20
\rho (\mathbf{v} \otimes \mathbf{v}) + (p + \frac{1}{2} \Vert \mathbf{B} \Vert_2 ^2) \underline{I} - \mathbf{B} \otimes \mathbf{B} \\
21
\mathbf{v} (\frac{1}{2} \rho \Vert \mathbf{v} \Vert_2 ^2 + \frac{\gamma p}{\gamma - 1} + \Vert \mathbf{B} \Vert_2 ^2) - \mathbf{B} (\mathbf{v} \cdot \mathbf{B}) + c_h \psi \mathbf{B} \\
22
\mathbf{v} \otimes \mathbf{B} - \mathbf{B} \otimes \mathbf{v} + c_h \psi \underline{I} \\
23
c_h \mathbf{B} \\ \rho_1 \mathbf{v} \\ \vdots \\ \rho_{n} \mathbf{v}
24
\end{pmatrix}
25
+
26
(\nabla \cdot \mathbf{B})
27
\begin{pmatrix}
28
\mathbf{B} \\ \mathbf{v} \cdot \mathbf{B} \\ \mathbf{v} \\ 0 \\ 0 \\ \vdots \\ 0
29
\end{pmatrix}
30
+
31
(\nabla \psi) \cdot
32
\begin{pmatrix}
33
0 \\ \mathbf{v} \cdot \psi \\ 0 \\ \mathbf{v} \\ \mathbf{0} \\ \vdots \\ \mathbf{0}
34
\end{pmatrix}
35
=
36
\begin{pmatrix}
37
\mathbf{0} \\ 0 \\ \mathbf{0} \\ 0 \\ 0 \\ \vdots \\ 0
38
\end{pmatrix}
39
```
40
for calorically perfect gases in two space dimensions.
41
Here, ``\rho_i`` is the density of component ``i``, ``\rho=\sum_{i=1}^n\rho_i`` the sum of the individual ``\rho_i``,
42
``\mathbf{v}`` the velocity, ``\mathbf{B}`` the magnetic field, ``c_h`` the hyperbolic divergence cleaning speed,
43
``\psi`` the generalized Lagrangian Multiplier (GLM),
44
``e_{\text{total}}`` the specific total energy, and
45
```math
46
p = (\gamma - 1) \left( \rho e_{\text{total}} - \frac{1}{2} \rho \Vert \mathbf{v} \Vert_2 ^2 - \frac{1}{2} \Vert \mathbf{B} \Vert_2 ^2 - \frac{1}{2} \psi^2 \right)
47
```
48
the pressure,
49
```math
50
\gamma=\frac{\sum_{i=1}^n\rho_i C_{v,i}\gamma_i}{\sum_{i=1}^n\rho_i C_{v,i}}
51
```
52
total heat capacity ratio, ``\gamma_i`` heat capacity ratio of component ``i``,
53
```math
54
C_{v,i}=\frac{R_i}{\gamma_i-1}
55
```
56
specific heat capacity at constant volume of component ``i`` and ``\underline{I}`` the ``3\times 3`` identity matrix.
57
58
In case of more than one component, the specific heat ratios `gammas` and the gas constants
59
`gas_constants` should be passed as tuples, e.g., `gammas = (1.4, 1.667)`.
60
61
The remaining variables like the specific heats at constant volume `cv` or the specific heats at
62
constant pressure `cp` are then calculated considering a calorically perfect gas.
63
"""
64
struct IdealGlmMhdMulticomponentEquations2D{NVARS, NCOMP, RealT <: Real} <:
65
AbstractIdealGlmMhdMulticomponentEquations{2, NVARS, NCOMP}
66
gammas::SVector{NCOMP, RealT}
67
gas_constants::SVector{NCOMP, RealT}
68
cv::SVector{NCOMP, RealT}
69
cp::SVector{NCOMP, RealT}
70
c_h::RealT # GLM cleaning speed
71
72
function IdealGlmMhdMulticomponentEquations2D{NVARS, NCOMP, RealT}(gammas::SVector{NCOMP,
73
RealT},
74
gas_constants::SVector{NCOMP,
75
RealT},
76
c_h::RealT) where {
77
NVARS,
78
NCOMP,
79
RealT <:
80
Real
81
}
82
NCOMP >= 1 ||
83
throw(DimensionMismatch("`gammas` and `gas_constants` have to be filled with at least one value"))
84
85
cv = gas_constants ./ (gammas .- 1)
86
cp = gas_constants + gas_constants ./ (gammas .- 1)
87
88
return new(gammas, gas_constants, cv, cp, c_h)
89
end
90
end
91
92
function IdealGlmMhdMulticomponentEquations2D(; gammas, gas_constants)
93
_gammas = promote(gammas...)
94
_gas_constants = promote(gas_constants...)
95
RealT = promote_type(eltype(_gammas), eltype(_gas_constants))
96
__gammas = SVector(map(RealT, _gammas))
97
__gas_constants = SVector(map(RealT, _gas_constants))
98
99
c_h = convert(RealT, NaN)
100
101
NVARS = length(_gammas) + 8
102
NCOMP = length(_gammas)
103
104
return IdealGlmMhdMulticomponentEquations2D{NVARS, NCOMP, RealT}(__gammas,
105
__gas_constants,
106
c_h)
107
end
108
109
# Outer constructor for `@reset` works correctly
110
function IdealGlmMhdMulticomponentEquations2D(gammas, gas_constants, cv, cp, c_h)
111
_gammas = promote(gammas...)
112
_gas_constants = promote(gas_constants...)
113
RealT = promote_type(eltype(_gammas), eltype(_gas_constants))
114
__gammas = SVector(map(RealT, _gammas))
115
__gas_constants = SVector(map(RealT, _gas_constants))
116
117
c_h = convert(RealT, c_h)
118
119
NVARS = length(_gammas) + 8
120
NCOMP = length(_gammas)
121
122
return IdealGlmMhdMulticomponentEquations2D{NVARS, NCOMP, RealT}(__gammas,
123
__gas_constants,
124
c_h)
125
end
126
127
@inline function Base.real(::IdealGlmMhdMulticomponentEquations2D{NVARS, NCOMP, RealT}) where {
128
NVARS,
129
NCOMP,
130
RealT
131
}
132
return RealT
133
end
134
135
have_nonconservative_terms(::IdealGlmMhdMulticomponentEquations2D) = True()
136
137
function varnames(::typeof(cons2cons), equations::IdealGlmMhdMulticomponentEquations2D)
138
cons = ("rho_v1", "rho_v2", "rho_v3", "rho_e_total", "B1", "B2", "B3", "psi")
139
rhos = ntuple(n -> "rho" * string(n), Val(ncomponents(equations)))
140
return (cons..., rhos...)
141
end
142
143
function varnames(::typeof(cons2prim), equations::IdealGlmMhdMulticomponentEquations2D)
144
prim = ("v1", "v2", "v3", "p", "B1", "B2", "B3", "psi")
145
rhos = ntuple(n -> "rho" * string(n), Val(ncomponents(equations)))
146
return (prim..., rhos...)
147
end
148
149
function default_analysis_integrals(::IdealGlmMhdMulticomponentEquations2D)
150
return (entropy_timederivative, Val(:l2_divb), Val(:linf_divb))
151
end
152
153
# Helper function to extract the magnetic field vector from the conservative variables
154
magnetic_field(u, equations::IdealGlmMhdMulticomponentEquations2D) = SVector(u[5], u[6],
155
u[7])
156
157
"""
158
initial_condition_convergence_test(x, t, equations::IdealGlmMhdMulticomponentEquations2D)
159
160
An Alfvén wave as smooth initial condition used for convergence tests.
161
"""
162
function initial_condition_convergence_test(x, t,
163
equations::IdealGlmMhdMulticomponentEquations2D)
164
# smooth Alfvén wave test from Derigs et al. FLASH (2016)
165
# domain must be set to [0, 1/cos(α)] x [0, 1/sin(α)], γ = 5/3
166
RealT = eltype(x)
167
alpha = 0.25f0 * convert(RealT, pi)
168
x_perp = x[1] * cos(alpha) + x[2] * sin(alpha)
169
B_perp = convert(RealT, 0.1) * sinpi(2 * x_perp)
170
rho = one(RealT)
171
prim_rho = SVector{ncomponents(equations), real(equations)}(2^(i - 1) * (1 - 2) *
172
rho / (1 -
173
2^ncomponents(equations))
174
for i in eachcomponent(equations))
175
176
v1 = -B_perp * sin(alpha)
177
v2 = B_perp * cos(alpha)
178
v3 = convert(RealT, 0.1) * cospi(2 * x_perp)
179
p = convert(RealT, 0.1)
180
B1 = cos(alpha) + v1
181
B2 = sin(alpha) + v2
182
B3 = v3
183
psi = 0
184
prim_other = SVector(v1, v2, v3, p, B1, B2, B3, psi)
185
186
return prim2cons(vcat(prim_other, prim_rho), equations)
187
end
188
189
"""
190
initial_condition_weak_blast_wave(x, t, equations::IdealGlmMhdMulticomponentEquations2D)
191
192
A weak blast wave adapted from
193
- Sebastian Hennemann, Gregor J. Gassner (2020)
194
A provably entropy stable subcell shock capturing approach for high order split form DG
195
[arXiv: 2008.12044](https://arxiv.org/abs/2008.12044)
196
"""
197
function initial_condition_weak_blast_wave(x, t,
198
equations::IdealGlmMhdMulticomponentEquations2D)
199
# Adapted MHD version of the weak blast wave from Hennemann & Gassner JCP paper 2020 (Sec. 6.3)
200
# Same discontinuity in the velocities but with magnetic fields
201
# Set up polar coordinates
202
RealT = eltype(x)
203
inicenter = SVector(0, 0)
204
x_norm = x[1] - inicenter[1]
205
y_norm = x[2] - inicenter[2]
206
r = sqrt(x_norm^2 + y_norm^2)
207
phi = atan(y_norm, x_norm)
208
sin_phi, cos_phi = sincos(phi)
209
210
# We initialize each species with a fraction of the total density `rho`, such
211
# that the sum of the densities is `rho := density(prim, equations)`. The density of
212
# a species is double the density of the next species.
213
prim_rho = SVector{ncomponents(equations), real(equations)}(r > 0.5f0 ?
214
2^(i - 1) * (1 - 2) /
215
(RealT(1) -
216
2^ncomponents(equations)) :
217
2^(i - 1) * (1 - 2) *
218
RealT(1.1691) /
219
(1 -
220
2^ncomponents(equations))
221
for i in eachcomponent(equations))
222
223
v1 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos_phi
224
v2 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * sin_phi
225
p = r > 0.5f0 ? one(RealT) : convert(RealT, 1.245)
226
227
prim_other = SVector(v1, v2, 0, p, 1, 1, 1, 0)
228
229
return prim2cons(vcat(prim_other, prim_rho), equations)
230
end
231
232
# Calculate 1D flux for a single point
233
@inline function flux(u, orientation::Integer,
234
equations::IdealGlmMhdMulticomponentEquations2D)
235
rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = u
236
@unpack c_h = equations
237
238
rho = density(u, equations)
239
240
v1 = rho_v1 / rho
241
v2 = rho_v2 / rho
242
v3 = rho_v3 / rho
243
kin_en = 0.5f0 * rho * (v1^2 + v2^2 + v3^2)
244
mag_en = 0.5f0 * (B1^2 + B2^2 + B3^2)
245
gamma = totalgamma(u, equations)
246
p = (gamma - 1) * (rho_e_total - kin_en - mag_en - 0.5f0 * psi^2)
247
248
if orientation == 1
249
f_rho = densities(u, v1, equations)
250
f1 = rho_v1 * v1 + p + mag_en - B1^2
251
f2 = rho_v1 * v2 - B1 * B2
252
f3 = rho_v1 * v3 - B1 * B3
253
f4 = (kin_en + gamma * p / (gamma - 1) + 2 * mag_en) * v1 -
254
B1 * (v1 * B1 + v2 * B2 + v3 * B3) + c_h * psi * B1
255
f5 = c_h * psi
256
f6 = v1 * B2 - v2 * B1
257
f7 = v1 * B3 - v3 * B1
258
f8 = c_h * B1
259
else # orientation == 2
260
f_rho = densities(u, v2, equations)
261
f1 = rho_v2 * v1 - B1 * B2
262
f2 = rho_v2 * v2 + p + mag_en - B2^2
263
f3 = rho_v2 * v3 - B2 * B3
264
f4 = (kin_en + gamma * p / (gamma - 1) + 2 * mag_en) * v2 -
265
B2 * (v1 * B1 + v2 * B2 + v3 * B3) + c_h * psi * B2
266
f5 = v2 * B1 - v1 * B2
267
f6 = c_h * psi
268
f7 = v2 * B3 - v3 * B2
269
f8 = c_h * B2
270
end
271
272
f_other = SVector(f1, f2, f3, f4, f5, f6, f7, f8)
273
274
return vcat(f_other, f_rho)
275
end
276
277
"""
278
flux_nonconservative_powell(u_ll, u_rr, orientation::Integer,
279
equations::IdealGlmMhdMulticomponentEquations2D)
280
281
Non-symmetric two-point flux discretizing the nonconservative (source) term of
282
Powell and the Galilean nonconservative term associated with the GLM multiplier
283
of the [`IdealGlmMhdMulticomponentEquations2D`](@ref).
284
285
## References
286
- Marvin Bohm, Andrew R.Winters, Gregor J. Gassner, Dominik Derigs,
287
Florian Hindenlang, Joachim Saur
288
An entropy stable nodal discontinuous Galerkin method for the resistive MHD
289
equations. Part I: Theory and numerical verification
290
[DOI: 10.1016/j.jcp.2018.06.027](https://doi.org/10.1016/j.jcp.2018.06.027)
291
"""
292
@inline function flux_nonconservative_powell(u_ll, u_rr, orientation::Integer,
293
equations::IdealGlmMhdMulticomponentEquations2D)
294
rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll
295
rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr
296
297
rho_ll = density(u_ll, equations)
298
299
v1_ll = rho_v1_ll / rho_ll
300
v2_ll = rho_v2_ll / rho_ll
301
v3_ll = rho_v3_ll / rho_ll
302
v_dot_B_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll
303
304
# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)
305
# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})
306
# Note that the order of conserved variables is changed compared to the
307
# standard GLM MHD equations, i.e., the densities are moved to the end
308
# Here, we compute the non-density components at first and append zero density
309
# components afterwards
310
zero_densities = SVector{ncomponents(equations), real(equations)}(ntuple(_ -> zero(real(equations)),
311
Val(ncomponents(equations))))
312
if orientation == 1
313
f = SVector(B1_ll * B1_rr,
314
B2_ll * B1_rr,
315
B3_ll * B1_rr,
316
v_dot_B_ll * B1_rr + v1_ll * psi_ll * psi_rr,
317
v1_ll * B1_rr,
318
v2_ll * B1_rr,
319
v3_ll * B1_rr,
320
v1_ll * psi_rr)
321
else # orientation == 2
322
f = SVector(B1_ll * B2_rr,
323
B2_ll * B2_rr,
324
B3_ll * B2_rr,
325
v_dot_B_ll * B2_rr + v2_ll * psi_ll * psi_rr,
326
v1_ll * B2_rr,
327
v2_ll * B2_rr,
328
v3_ll * B2_rr,
329
v2_ll * psi_rr)
330
end
331
332
return vcat(f, zero_densities)
333
end
334
335
"""
336
flux_derigs_etal(u_ll, u_rr, orientation, equations::IdealGlmMhdMulticomponentEquations2D)
337
338
Entropy conserving two-point flux adapted by
339
- Derigs et al. (2018)
340
Ideal GLM-MHD: About the entropy consistent nine-wave magnetic field
341
divergence diminishing ideal magnetohydrodynamics equations for multicomponent
342
[DOI: 10.1016/j.jcp.2018.03.002](https://doi.org/10.1016/j.jcp.2018.03.002)
343
"""
344
function flux_derigs_etal(u_ll, u_rr, orientation::Integer,
345
equations::IdealGlmMhdMulticomponentEquations2D)
346
# Unpack left and right states to get velocities, pressure, and inverse temperature (called beta)
347
rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll
348
rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr
349
@unpack gammas, gas_constants, cv, c_h = equations
350
351
rho_ll = density(u_ll, equations)
352
rho_rr = density(u_rr, equations)
353
354
gamma_ll = totalgamma(u_ll, equations)
355
gamma_rr = totalgamma(u_rr, equations)
356
357
rhok_mean = SVector{ncomponents(equations), real(equations)}(ln_mean(u_ll[i + 8],
358
u_rr[i + 8])
359
for i in eachcomponent(equations))
360
rhok_avg = SVector{ncomponents(equations), real(equations)}(0.5f0 * (u_ll[i + 8] +
361
u_rr[i + 8])
362
for i in eachcomponent(equations))
363
364
v1_ll = rho_v1_ll / rho_ll
365
v2_ll = rho_v2_ll / rho_ll
366
v3_ll = rho_v3_ll / rho_ll
367
v1_rr = rho_v1_rr / rho_rr
368
v2_rr = rho_v2_rr / rho_rr
369
v3_rr = rho_v3_rr / rho_rr
370
v1_sq = 0.5f0 * (v1_ll^2 + v1_rr^2)
371
v2_sq = 0.5f0 * (v2_ll^2 + v2_rr^2)
372
v3_sq = 0.5f0 * (v3_ll^2 + v3_rr^2)
373
v_sq = v1_sq + v2_sq + v3_sq
374
B1_sq = 0.5f0 * (B1_ll^2 + B1_rr^2)
375
B2_sq = 0.5f0 * (B2_ll^2 + B2_rr^2)
376
B3_sq = 0.5f0 * (B3_ll^2 + B3_rr^2)
377
B_sq = B1_sq + B2_sq + B3_sq
378
vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^2
379
vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^2
380
mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2
381
mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2
382
# for convenience store v⋅B
383
vel_dot_mag_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll
384
vel_dot_mag_rr = v1_rr * B1_rr + v2_rr * B2_rr + v3_rr * B3_rr
385
386
# Compute the necessary mean values needed for either direction
387
v1_avg = 0.5f0 * (v1_ll + v1_rr)
388
v2_avg = 0.5f0 * (v2_ll + v2_rr)
389
v3_avg = 0.5f0 * (v3_ll + v3_rr)
390
v_sum = v1_avg + v2_avg + v3_avg
391
B1_avg = 0.5f0 * (B1_ll + B1_rr)
392
B2_avg = 0.5f0 * (B2_ll + B2_rr)
393
B3_avg = 0.5f0 * (B3_ll + B3_rr)
394
psi_avg = 0.5f0 * (psi_ll + psi_rr)
395
vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)
396
mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)
397
vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)
398
399
RealT = eltype(u_ll)
400
enth = zero(RealT)
401
help1_ll = zero(RealT)
402
help1_rr = zero(RealT)
403
404
for i in eachcomponent(equations)
405
enth += rhok_avg[i] * gas_constants[i]
406
help1_ll += u_ll[i + 8] * cv[i]
407
help1_rr += u_rr[i + 8] * cv[i]
408
end
409
410
T_ll = (rho_e_total_ll - 0.5f0 * rho_ll * (vel_norm_ll) - 0.5f0 * mag_norm_ll -
411
0.5f0 * psi_ll^2) / help1_ll
412
T_rr = (rho_e_total_rr - 0.5f0 * rho_rr * (vel_norm_rr) - 0.5f0 * mag_norm_rr -
413
0.5f0 * psi_rr^2) / help1_rr
414
T = 0.5f0 * (1 / T_ll + 1 / T_rr)
415
T_log = ln_mean(1 / T_ll, 1 / T_rr)
416
417
# Calculate fluxes depending on orientation with specific direction averages
418
help1 = zero(RealT)
419
help2 = zero(RealT)
420
if orientation == 1
421
f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * v1_avg
422
for i in eachcomponent(equations))
423
for i in eachcomponent(equations)
424
help1 += f_rho[i] * cv[i]
425
help2 += f_rho[i]
426
end
427
f1 = help2 * v1_avg + enth / T + 0.5f0 * mag_norm_avg - B1_avg * B1_avg
428
f2 = help2 * v2_avg - B1_avg * B2_avg
429
f3 = help2 * v3_avg - B1_avg * B3_avg
430
f5 = c_h * psi_avg
431
f6 = v1_avg * B2_avg - v2_avg * B1_avg
432
f7 = v1_avg * B3_avg - v3_avg * B1_avg
433
f8 = c_h * B1_avg
434
# total energy flux is complicated and involves the previous eight components
435
psi_B1_avg = 0.5f0 * (B1_ll * psi_ll + B1_rr * psi_rr)
436
v1_mag_avg = 0.5f0 * (v1_ll * mag_norm_ll + v1_rr * mag_norm_rr)
437
438
f4 = (help1 / T_log) - 0.5f0 * (vel_norm_avg) * (help2) + f1 * v1_avg +
439
f2 * v2_avg + f3 * v3_avg +
440
f5 * B1_avg + f6 * B2_avg + f7 * B3_avg + f8 * psi_avg -
441
0.5f0 * v1_mag_avg +
442
B1_avg * vel_dot_mag_avg - c_h * psi_B1_avg
443
444
else
445
f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * v2_avg
446
for i in eachcomponent(equations))
447
for i in eachcomponent(equations)
448
help1 += f_rho[i] * cv[i]
449
help2 += f_rho[i]
450
end
451
f1 = help2 * v1_avg - B1_avg * B2_avg
452
f2 = help2 * v2_avg + enth / T + 0.5f0 * mag_norm_avg - B2_avg * B2_avg
453
f3 = help2 * v3_avg - B2_avg * B3_avg
454
f5 = v2_avg * B1_avg - v1_avg * B2_avg
455
f6 = c_h * psi_avg
456
f7 = v2_avg * B3_avg - v3_avg * B2_avg
457
f8 = c_h * B2_avg
458
459
# total energy flux is complicated and involves the previous eight components
460
psi_B2_avg = 0.5f0 * (B2_ll * psi_ll + B2_rr * psi_rr)
461
v2_mag_avg = 0.5f0 * (v2_ll * mag_norm_ll + v2_rr * mag_norm_rr)
462
463
f4 = (help1 / T_log) - 0.5f0 * (vel_norm_avg) * (help2) + f1 * v1_avg +
464
f2 * v2_avg + f3 * v3_avg +
465
f5 * B1_avg + f6 * B2_avg + f7 * B3_avg + f8 * psi_avg -
466
0.5f0 * v2_mag_avg +
467
B2_avg * vel_dot_mag_avg - c_h * psi_B2_avg
468
end
469
470
f_other = SVector(f1, f2, f3, f4, f5, f6, f7, f8)
471
472
return vcat(f_other, f_rho)
473
end
474
475
"""
476
flux_hindenlang_gassner(u_ll, u_rr, orientation_or_normal_direction,
477
equations::IdealGlmMhdMulticomponentEquations2D)
478
479
Adaption of the entropy conserving and kinetic energy preserving two-point flux of
480
Hindenlang (2019), extending [`flux_ranocha`](@ref) to the MHD equations.
481
## References
482
- Florian Hindenlang, Gregor Gassner (2019)
483
A new entropy conservative two-point flux for ideal MHD equations derived from
484
first principles.
485
Presented at HONOM 2019: European workshop on high order numerical methods
486
for evolutionary PDEs, theory and applications
487
- Hendrik Ranocha (2018)
488
Generalised Summation-by-Parts Operators and Entropy Stability of Numerical Methods
489
for Hyperbolic Balance Laws
490
[PhD thesis, TU Braunschweig](https://cuvillier.de/en/shop/publications/7743)
491
- Hendrik Ranocha (2020)
492
Entropy Conserving and Kinetic Energy Preserving Numerical Methods for
493
the Euler Equations Using Summation-by-Parts Operators
494
[Proceedings of ICOSAHOM 2018](https://doi.org/10.1007/978-3-030-39647-3_42)
495
"""
496
@inline function flux_hindenlang_gassner(u_ll, u_rr, orientation::Integer,
497
equations::IdealGlmMhdMulticomponentEquations2D)
498
# Unpack left and right states
499
v1_ll, v2_ll, v3_ll, p_ll, B1_ll, B2_ll, B3_ll, psi_ll = cons2prim(u_ll, equations)
500
v1_rr, v2_rr, v3_rr, p_rr, B1_rr, B2_rr, B3_rr, psi_rr = cons2prim(u_rr, equations)
501
502
rho_ll = density(u_ll, equations)
503
rho_rr = density(u_rr, equations)
504
505
# Compute the necessary mean values needed for either direction
506
# Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)`
507
# in exact arithmetic since
508
# log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ)
509
# = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ)
510
inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll)
511
v1_avg = 0.5f0 * (v1_ll + v1_rr)
512
v2_avg = 0.5f0 * (v2_ll + v2_rr)
513
v3_avg = 0.5f0 * (v3_ll + v3_rr)
514
p_avg = 0.5f0 * (p_ll + p_rr)
515
psi_avg = 0.5f0 * (psi_ll + psi_rr)
516
velocity_square_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr)
517
magnetic_square_avg = 0.5f0 * (B1_ll * B1_rr + B2_ll * B2_rr + B3_ll * B3_rr)
518
519
inv_gamma_minus_one = 1 / (totalgamma(0.5f0 * (u_ll + u_rr), equations) - 1)
520
521
rhok_mean = SVector{ncomponents(equations), real(equations)}(ln_mean(u_ll[i + 8],
522
u_rr[i + 8])
523
for i in eachcomponent(equations))
524
rhok_avg = SVector{ncomponents(equations), real(equations)}(0.5f0 * (u_ll[i + 8] +
525
u_rr[i + 8])
526
for i in eachcomponent(equations))
527
528
RealT = eltype(u_ll)
529
if orientation == 1
530
f1 = zero(RealT)
531
f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * v1_avg
532
for i in eachcomponent(equations))
533
for i in eachcomponent(equations)
534
f1 += f_rho[i]
535
end
536
537
# Calculate fluxes depending on orientation with specific direction averages
538
f2 = f1 * v1_avg + p_avg + magnetic_square_avg -
539
0.5f0 * (B1_ll * B1_rr + B1_rr * B1_ll)
540
f3 = f1 * v2_avg - 0.5f0 * (B1_ll * B2_rr + B1_rr * B2_ll)
541
f4 = f1 * v3_avg - 0.5f0 * (B1_ll * B3_rr + B1_rr * B3_ll)
542
# f5 below
543
f6 = f6 = equations.c_h * psi_avg
544
f7 = 0.5f0 * (v1_ll * B2_ll - v2_ll * B1_ll + v1_rr * B2_rr - v2_rr * B1_rr)
545
f8 = 0.5f0 * (v1_ll * B3_ll - v3_ll * B1_ll + v1_rr * B3_rr - v3_rr * B1_rr)
546
f9 = equations.c_h * 0.5f0 * (B1_ll + B1_rr)
547
# total energy flux is complicated and involves the previous components
548
f5 = (f1 * (velocity_square_avg + inv_rho_p_mean * inv_gamma_minus_one)
549
+
550
0.5f0 * (+p_ll * v1_rr + p_rr * v1_ll
551
+ (v1_ll * B2_ll * B2_rr + v1_rr * B2_rr * B2_ll)
552
+ (v1_ll * B3_ll * B3_rr + v1_rr * B3_rr * B3_ll)
553
-
554
(v2_ll * B1_ll * B2_rr + v2_rr * B1_rr * B2_ll)
555
-
556
(v3_ll * B1_ll * B3_rr + v3_rr * B1_rr * B3_ll)
557
+
558
equations.c_h * (B1_ll * psi_rr + B1_rr * psi_ll)))
559
else
560
f1 = zero(RealT)
561
f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * v2_avg
562
for i in eachcomponent(equations))
563
for i in eachcomponent(equations)
564
f1 += f_rho[i]
565
end
566
567
# Calculate fluxes depending on orientation with specific direction averages
568
f2 = f1 * v1_avg - 0.5f0 * (B2_ll * B1_rr + B2_rr * B1_ll)
569
f3 = f1 * v2_avg + p_avg + magnetic_square_avg -
570
0.5f0 * (B2_ll * B2_rr + B2_rr * B2_ll)
571
f4 = f1 * v3_avg - 0.5f0 * (B2_ll * B3_rr + B2_rr * B3_ll)
572
#f5 below
573
f6 = 0.5f0 * (v2_ll * B1_ll - v1_ll * B2_ll + v2_rr * B1_rr - v1_rr * B2_rr)
574
f7 = equations.c_h * psi_avg
575
f8 = 0.5f0 * (v2_ll * B3_ll - v3_ll * B2_ll + v2_rr * B3_rr - v3_rr * B2_rr)
576
f9 = equations.c_h * 0.5f0 * (B2_ll + B2_rr)
577
# total energy flux is complicated and involves the previous components
578
f5 = (f1 * (velocity_square_avg + inv_rho_p_mean * inv_gamma_minus_one)
579
+
580
0.5f0 * (+p_ll * v2_rr + p_rr * v2_ll
581
+ (v2_ll * B1_ll * B1_rr + v2_rr * B1_rr * B1_ll)
582
+ (v2_ll * B3_ll * B3_rr + v2_rr * B3_rr * B3_ll)
583
-
584
(v1_ll * B2_ll * B1_rr + v1_rr * B2_rr * B1_ll)
585
-
586
(v3_ll * B2_ll * B3_rr + v3_rr * B2_rr * B3_ll)
587
+
588
equations.c_h * (B2_ll * psi_rr + B2_rr * psi_ll)))
589
end
590
591
f_other = SVector(f2, f3, f4, f5, f6, f7, f8, f9)
592
593
return vcat(f_other, f_rho)
594
end
595
596
# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation
597
@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,
598
equations::IdealGlmMhdMulticomponentEquations2D)
599
rho_v1_ll, rho_v2_ll, _ = u_ll
600
rho_v1_rr, rho_v2_rr, _ = u_rr
601
602
rho_ll = density(u_ll, equations)
603
rho_rr = density(u_rr, equations)
604
605
# Calculate velocities and fast magnetoacoustic wave speeds
606
if orientation == 1
607
v_ll = rho_v1_ll / rho_ll
608
v_rr = rho_v1_rr / rho_rr
609
else # orientation == 2
610
v_ll = rho_v2_ll / rho_ll
611
v_rr = rho_v2_rr / rho_rr
612
end
613
cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)
614
cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)
615
616
return max(abs(v_ll), abs(v_rr)) + max(cf_ll, cf_rr)
617
end
618
619
# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`
620
@inline function max_abs_speed(u_ll, u_rr, orientation::Integer,
621
equations::IdealGlmMhdMulticomponentEquations2D)
622
rho_v1_ll, rho_v2_ll, _ = u_ll
623
rho_v1_rr, rho_v2_rr, _ = u_rr
624
625
rho_ll = density(u_ll, equations)
626
rho_rr = density(u_rr, equations)
627
628
# Calculate velocities and fast magnetoacoustic wave speeds
629
if orientation == 1
630
v_ll = rho_v1_ll / rho_ll
631
v_rr = rho_v1_rr / rho_rr
632
else # orientation == 2
633
v_ll = rho_v2_ll / rho_ll
634
v_rr = rho_v2_rr / rho_rr
635
end
636
cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)
637
cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)
638
639
return max(abs(v_ll) + cf_ll, abs(v_rr) + cf_rr)
640
end
641
642
@inline function max_abs_speeds(u, equations::IdealGlmMhdMulticomponentEquations2D)
643
rho_v1, rho_v2, _ = u
644
645
rho = density(u, equations)
646
647
v1 = rho_v1 / rho
648
v2 = rho_v2 / rho
649
650
cf_x_direction = calc_fast_wavespeed(u, 1, equations)
651
cf_y_direction = calc_fast_wavespeed(u, 2, equations)
652
653
return (abs(v1) + cf_x_direction, abs(v2) + cf_y_direction)
654
end
655
656
@doc raw"""
657
pressure(u, equations::IdealGlmMhdMulticomponentEquations2D)
658
659
Computes the pressure for an ideal equation of state with
660
isentropic exponent/adiabatic index ``\gamma`` from the conserved variables `u`.
661
```math
662
\begin{aligned}
663
p &= (\gamma - 1) \left( \rho e_{\text{total}} - \rho e_{\text{kinetic}} - \rho e_{\text{magnetic}} - \frac{1}{2}\psi^2 \right) \\
664
&= (\gamma - 1) \left( \rho e_{\text{total}} - \frac{1}{2}
665
\left[\rho \Vert v \Vert_2^2 + \Vert B \Vert_2^2 + \psi^2 \right] \right)
666
\end{aligned}
667
```
668
"""
669
@inline function pressure(u, equations::IdealGlmMhdMulticomponentEquations2D)
670
rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = u
671
rho = density(u, equations)
672
gamma = totalgamma(u, equations)
673
p = (gamma - 1) * (rho_e_total -
674
0.5f0 *
675
((rho_v1^2 + rho_v2^2 + rho_v3^2) / rho +
676
B1^2 + B2^2 + B3^2 + psi^2))
677
return p
678
end
679
680
@inline function density_pressure(u, equations::IdealGlmMhdMulticomponentEquations2D)
681
rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = u
682
rho = density(u, equations)
683
gamma = totalgamma(u, equations)
684
rho_times_p = (gamma - 1) * (rho * rho_e_total -
685
0.5f0 * (rho_v1^2 + rho_v2^2 + rho_v3^2 +
686
rho * (B1^2 + B2^2 + B3^2 + psi^2)))
687
return rho_times_p
688
end
689
690
# Convert conservative variables to primitive
691
function cons2prim(u, equations::IdealGlmMhdMulticomponentEquations2D)
692
rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = u
693
694
prim_rho = SVector{ncomponents(equations), real(equations)}(u[i + 8]
695
for i in eachcomponent(equations))
696
rho = density(u, equations)
697
698
v1 = rho_v1 / rho
699
v2 = rho_v2 / rho
700
v3 = rho_v3 / rho
701
702
gamma = totalgamma(u, equations)
703
704
p = (gamma - 1) *
705
(rho_e_total - 0.5f0 * rho * (v1^2 + v2^2 + v3^2) -
706
0.5f0 * (B1^2 + B2^2 + B3^2) -
707
0.5f0 * psi^2)
708
prim_other = SVector(v1, v2, v3, p, B1, B2, B3, psi)
709
710
return vcat(prim_other, prim_rho)
711
end
712
713
# Convert conservative variables to entropy
714
@inline function cons2entropy(u, equations::IdealGlmMhdMulticomponentEquations2D)
715
rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = u
716
@unpack cv, gammas, gas_constants = equations
717
718
rho = density(u, equations)
719
720
v1 = rho_v1 / rho
721
v2 = rho_v2 / rho
722
v3 = rho_v3 / rho
723
v_square = v1^2 + v2^2 + v3^2
724
gamma = totalgamma(u, equations)
725
p = (gamma - 1) *
726
(rho_e_total - 0.5f0 * rho * v_square - 0.5f0 * (B1^2 + B2^2 + B3^2) -
727
0.5f0 * psi^2)
728
s = log(p) - gamma * log(rho)
729
rho_p = rho / p
730
731
# Multicomponent stuff
732
help1 = zero(v1)
733
734
for i in eachcomponent(equations)
735
help1 += u[i + 8] * cv[i]
736
end
737
738
T = (rho_e_total - 0.5f0 * rho * v_square - 0.5f0 * (B1^2 + B2^2 + B3^2) -
739
0.5f0 * psi^2) /
740
(help1)
741
742
entrop_rho = SVector{ncomponents(equations), real(equations)}(-1 *
743
(cv[i] * log(T) -
744
gas_constants[i] *
745
log(u[i + 8])) +
746
gas_constants[i] +
747
cv[i] -
748
(v_square / (2 * T))
749
for i in eachcomponent(equations))
750
751
w1 = v1 / T
752
w2 = v2 / T
753
w3 = v3 / T
754
w4 = -1 / T
755
w5 = B1 / T
756
w6 = B2 / T
757
w7 = B3 / T
758
w8 = psi / T
759
760
entrop_other = SVector(w1, w2, w3, w4, w5, w6, w7, w8)
761
762
return vcat(entrop_other, entrop_rho)
763
end
764
765
# Convert primitive to conservative variables
766
@inline function prim2cons(prim, equations::IdealGlmMhdMulticomponentEquations2D)
767
v1, v2, v3, p, B1, B2, B3, psi = prim
768
769
cons_rho = SVector{ncomponents(equations), real(equations)}(prim[i + 8]
770
for i in eachcomponent(equations))
771
rho = density(prim, equations)
772
773
rho_v1 = rho * v1
774
rho_v2 = rho * v2
775
rho_v3 = rho * v3
776
777
gamma = totalgamma(prim, equations)
778
rho_e_total = p / (gamma - 1) + 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3) +
779
0.5f0 * (B1^2 + B2^2 + B3^2) + 0.5f0 * psi^2
780
781
cons_other = SVector(rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3,
782
psi)
783
784
return vcat(cons_other, cons_rho)
785
end
786
787
# Compute the fastest wave speed for ideal MHD equations: c_f, the fast magnetoacoustic eigenvalue
788
@inline function calc_fast_wavespeed(cons, direction,
789
equations::IdealGlmMhdMulticomponentEquations2D)
790
rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = cons
791
rho = density(cons, equations)
792
v1 = rho_v1 / rho
793
v2 = rho_v2 / rho
794
v3 = rho_v3 / rho
795
v_mag = sqrt(v1^2 + v2^2 + v3^2)
796
gamma = totalgamma(cons, equations)
797
p = (gamma - 1) *
798
(rho_e_total - 0.5f0 * rho * v_mag^2 - 0.5f0 * (B1^2 + B2^2 + B3^2) -
799
0.5f0 * psi^2)
800
a_square = gamma * p / rho
801
sqrt_rho = sqrt(rho)
802
b1 = B1 / sqrt_rho
803
b2 = B2 / sqrt_rho
804
b3 = B3 / sqrt_rho
805
b_square = b1^2 + b2^2 + b3^2
806
if direction == 1 # x-direction
807
c_f = sqrt(0.5f0 * (a_square + b_square) +
808
0.5f0 * sqrt((a_square + b_square)^2 - 4 * a_square * b1^2))
809
else
810
c_f = sqrt(0.5f0 * (a_square + b_square) +
811
0.5f0 * sqrt((a_square + b_square)^2 - 4 * a_square * b2^2))
812
end
813
return c_f
814
end
815
816
@inline function density(u, equations::IdealGlmMhdMulticomponentEquations2D)
817
RealT = eltype(u)
818
rho = zero(RealT)
819
820
for i in eachcomponent(equations)
821
rho += u[i + 8]
822
end
823
824
return rho
825
end
826
827
@inline function totalgamma(u, equations::IdealGlmMhdMulticomponentEquations2D)
828
@unpack cv, gammas = equations
829
830
RealT = eltype(u)
831
help1 = zero(RealT)
832
help2 = zero(RealT)
833
834
for i in eachcomponent(equations)
835
help1 += u[i + 8] * cv[i] * gammas[i]
836
help2 += u[i + 8] * cv[i]
837
end
838
839
return help1 / help2
840
end
841
842
@inline function densities(u, v, equations::IdealGlmMhdMulticomponentEquations2D)
843
return SVector{ncomponents(equations), real(equations)}(u[i + 8] * v
844
for i in eachcomponent(equations))
845
end
846
end # @muladd
847
848