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_3d.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
IdealGlmMhdEquations3D(gamma)
10
11
The ideal compressible GLM-MHD equations
12
```math
13
\frac{\partial}{\partial t}
14
\begin{pmatrix}
15
\rho \\ \rho \mathbf{v} \\ \rho e_{\text{total}} \\ \mathbf{B} \\ \psi
16
\end{pmatrix}
17
+
18
\nabla \cdot
19
\begin{pmatrix}
20
\rho \mathbf{v} \\ \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}
24
\end{pmatrix}
25
+
26
(\nabla \cdot \mathbf{B})
27
\begin{pmatrix}
28
0 \\ \mathbf{B} \\ \mathbf{v} \cdot \mathbf{B} \\ \mathbf{v} \\ 0
29
\end{pmatrix}
30
+
31
(\nabla \psi) \cdot
32
\begin{pmatrix}
33
\mathbf{0} \\ 0 \\ \mathbf{v} \cdot \psi \\ 0 \\ \mathbf{v}
34
\end{pmatrix}
35
=
36
\begin{pmatrix}
37
0 \\ \mathbf{0} \\ 0 \\ \mathbf{0} \\ 0
38
\end{pmatrix}
39
```
40
for an ideal gas in three space dimensions.
41
Here, ``\mathbf{v}`` is the velocity, ``\mathbf{B}`` the magnetic field, ``c_h`` the hyperbolic divergence cleaning speed,
42
``\psi`` the generalized Lagrangian Multiplier (GLM),
43
``e_{\text{total}}`` the specific total energy, and
44
```math
45
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)
46
```
47
the pressure, ``\gamma`` the total heat capacity ratio and ``\underline{I}`` the ``3\times 3`` identity matrix.
48
"""
49
struct IdealGlmMhdEquations3D{RealT <: Real} <:
50
AbstractIdealGlmMhdEquations{3, 9}
51
gamma::RealT # ratio of specific heats
52
inv_gamma_minus_one::RealT # = inv(gamma - 1); can be used to write slow divisions as fast multiplications
53
c_h::RealT # GLM cleaning speed
54
55
function IdealGlmMhdEquations3D(gamma, c_h)
56
γ, inv_gamma_minus_one, c_h = promote(gamma, inv(gamma - 1), c_h)
57
return new{typeof(γ)}(γ, inv_gamma_minus_one, c_h)
58
end
59
end
60
61
function IdealGlmMhdEquations3D(gamma; initial_c_h = convert(typeof(gamma), NaN))
62
# Use `promote` to ensure that `gamma` and `initial_c_h` have the same type
63
return IdealGlmMhdEquations3D(promote(gamma, initial_c_h)...)
64
end
65
66
# Outer constructor for `@reset` works correctly
67
function IdealGlmMhdEquations3D(gamma, inv_gamma_minus_one, c_h)
68
return IdealGlmMhdEquations3D(gamma, c_h)
69
end
70
71
have_nonconservative_terms(::IdealGlmMhdEquations3D) = True()
72
function varnames(::typeof(cons2cons), ::IdealGlmMhdEquations3D)
73
return ("rho", "rho_v1", "rho_v2", "rho_v3", "rho_e_total", "B1", "B2", "B3", "psi")
74
end
75
function varnames(::typeof(cons2prim), ::IdealGlmMhdEquations3D)
76
return ("rho", "v1", "v2", "v3", "p", "B1", "B2", "B3", "psi")
77
end
78
function default_analysis_integrals(::IdealGlmMhdEquations3D)
79
return (entropy_timederivative, Val(:l2_divb), Val(:linf_divb))
80
end
81
82
# Helper function to extract the magnetic field vector from the conservative variables
83
magnetic_field(u, equations::IdealGlmMhdEquations3D) = SVector(u[6], u[7], u[8])
84
85
# Set initial conditions at physical location `x` for time `t`
86
"""
87
initial_condition_constant(x, t, equations::IdealGlmMhdEquations3D)
88
89
A constant initial condition to test free-stream preservation.
90
"""
91
function initial_condition_constant(x, t, equations::IdealGlmMhdEquations3D)
92
RealT = eltype(x)
93
rho = 1
94
rho_v1 = convert(RealT, 0.1)
95
rho_v2 = -convert(RealT, 0.2)
96
rho_v3 = -0.5f0
97
rho_e_total = 50
98
B1 = 3
99
B2 = -convert(RealT, 1.2)
100
B3 = 0.5f0
101
psi = 0
102
return SVector(rho, rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi)
103
end
104
105
"""
106
initial_condition_convergence_test(x, t, equations::IdealGlmMhdEquations3D)
107
108
An Alfvén wave as smooth initial condition used for convergence tests.
109
See for reference section 5.1 in
110
- Christoph Altmann (2012)
111
Explicit Discontinuous Galerkin Methods for Magnetohydrodynamics
112
[DOI: 10.18419/opus-3895](http://dx.doi.org/10.18419/opus-3895)
113
"""
114
function initial_condition_convergence_test(x, t, equations::IdealGlmMhdEquations3D)
115
# Alfvén wave in three space dimensions
116
# domain must be set to [-1, 1]^3, γ = 5/3
117
RealT = eltype(x)
118
p = 1
119
omega = 2 * convert(RealT, pi) # may be multiplied by frequency
120
# r: length-variable = length of computational domain
121
r = convert(RealT, 2)
122
# e: epsilon = 0.2
123
e = convert(RealT, 0.2)
124
nx = 1 / sqrt(r^2 + 1)
125
ny = r / sqrt(r^2 + 1)
126
sqr = 1
127
Va = omega / (ny * sqr)
128
phi_alv = omega / ny * (nx * (x[1] - 0.5f0 * r) + ny * (x[2] - 0.5f0 * r)) - Va * t
129
130
rho = 1
131
v1 = -e * ny * cos(phi_alv) / rho
132
v2 = e * nx * cos(phi_alv) / rho
133
v3 = e * sin(phi_alv) / rho
134
B1 = nx - rho * v1 * sqr
135
B2 = ny - rho * v2 * sqr
136
B3 = -rho * v3 * sqr
137
psi = 0
138
139
return prim2cons(SVector(rho, v1, v2, v3, p, B1, B2, B3, psi), equations)
140
end
141
142
"""
143
initial_condition_weak_blast_wave(x, t, equations::IdealGlmMhdEquations3D)
144
145
A weak blast wave adapted from
146
- Sebastian Hennemann, Gregor J. Gassner (2020)
147
A provably entropy stable subcell shock capturing approach for high order split form DG
148
[arXiv: 2008.12044](https://arxiv.org/abs/2008.12044)
149
"""
150
function initial_condition_weak_blast_wave(x, t, equations::IdealGlmMhdEquations3D)
151
# Adapted MHD version of the weak blast wave from Hennemann & Gassner JCP paper 2020 (Sec. 6.3)
152
# Same discontinuity in the velocities but with magnetic fields
153
# Set up polar coordinates
154
RealT = eltype(x)
155
inicenter = (0, 0, 0)
156
x_norm = x[1] - inicenter[1]
157
y_norm = x[2] - inicenter[2]
158
z_norm = x[3] - inicenter[3]
159
r = sqrt(x_norm^2 + y_norm^2 + z_norm^2)
160
phi = atan(y_norm, x_norm)
161
theta = iszero(r) ? zero(RealT) : acos(z_norm / r)
162
163
# Calculate primitive variables
164
rho = r > 0.5f0 ? one(RealT) : convert(RealT, 1.1691)
165
v1 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos(phi) * sin(theta)
166
v2 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * sin(phi) * sin(theta)
167
v3 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos(theta)
168
p = r > 0.5f0 ? one(RealT) : convert(RealT, 1.245)
169
170
return prim2cons(SVector(rho, v1, v2, v3, p, 1, 1, 1, 0), equations)
171
end
172
173
# Pre-defined source terms should be implemented as
174
# function source_terms_WHATEVER(u, x, t, equations::IdealGlmMhdEquations3D)
175
176
# Calculate 1D flux for a single point
177
@inline function flux(u, orientation::Integer, equations::IdealGlmMhdEquations3D)
178
rho, rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = u
179
v1 = rho_v1 / rho
180
v2 = rho_v2 / rho
181
v3 = rho_v3 / rho
182
kin_en = 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3)
183
mag_en = 0.5f0 * (B1 * B1 + B2 * B2 + B3 * B3)
184
p_over_gamma_minus_one = (rho_e_total - kin_en - mag_en - 0.5f0 * psi^2)
185
p = (equations.gamma - 1) * p_over_gamma_minus_one
186
if orientation == 1
187
f1 = rho_v1
188
f2 = rho_v1 * v1 + p + mag_en - B1^2
189
f3 = rho_v1 * v2 - B1 * B2
190
f4 = rho_v1 * v3 - B1 * B3
191
f5 = (kin_en + equations.gamma * p_over_gamma_minus_one + 2 * mag_en) * v1 -
192
B1 * (v1 * B1 + v2 * B2 + v3 * B3) + equations.c_h * psi * B1
193
f6 = equations.c_h * psi
194
f7 = v1 * B2 - v2 * B1
195
f8 = v1 * B3 - v3 * B1
196
f9 = equations.c_h * B1
197
elseif orientation == 2
198
f1 = rho_v2
199
f2 = rho_v2 * v1 - B2 * B1
200
f3 = rho_v2 * v2 + p + mag_en - B2^2
201
f4 = rho_v2 * v3 - B2 * B3
202
f5 = (kin_en + equations.gamma * p_over_gamma_minus_one + 2 * mag_en) * v2 -
203
B2 * (v1 * B1 + v2 * B2 + v3 * B3) + equations.c_h * psi * B2
204
f6 = v2 * B1 - v1 * B2
205
f7 = equations.c_h * psi
206
f8 = v2 * B3 - v3 * B2
207
f9 = equations.c_h * B2
208
else
209
f1 = rho_v3
210
f2 = rho_v3 * v1 - B3 * B1
211
f3 = rho_v3 * v2 - B3 * B2
212
f4 = rho_v3 * v3 + p + mag_en - B3^2
213
f5 = (kin_en + equations.gamma * p_over_gamma_minus_one + 2 * mag_en) * v3 -
214
B3 * (v1 * B1 + v2 * B2 + v3 * B3) + equations.c_h * psi * B3
215
f6 = v3 * B1 - v1 * B3
216
f7 = v3 * B2 - v2 * B3
217
f8 = equations.c_h * psi
218
f9 = equations.c_h * B3
219
end
220
221
return SVector(f1, f2, f3, f4, f5, f6, f7, f8, f9)
222
end
223
224
# Calculate 1D flux for a single point in the normal direction
225
# Note, this directional vector is not normalized
226
@inline function flux(u, normal_direction::AbstractVector,
227
equations::IdealGlmMhdEquations3D)
228
rho, rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = u
229
v1 = rho_v1 / rho
230
v2 = rho_v2 / rho
231
v3 = rho_v3 / rho
232
kin_en = 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3)
233
mag_en = 0.5f0 * (B1 * B1 + B2 * B2 + B3 * B3)
234
p_over_gamma_minus_one = (rho_e_total - kin_en - mag_en - 0.5f0 * psi^2)
235
p = (equations.gamma - 1) * p_over_gamma_minus_one
236
237
v_normal = v1 * normal_direction[1] + v2 * normal_direction[2] +
238
v3 * normal_direction[3]
239
B_normal = B1 * normal_direction[1] + B2 * normal_direction[2] +
240
B3 * normal_direction[3]
241
rho_v_normal = rho * v_normal
242
243
f1 = rho_v_normal
244
f2 = rho_v_normal * v1 - B1 * B_normal + (p + mag_en) * normal_direction[1]
245
f3 = rho_v_normal * v2 - B2 * B_normal + (p + mag_en) * normal_direction[2]
246
f4 = rho_v_normal * v3 - B3 * B_normal + (p + mag_en) * normal_direction[3]
247
f5 = ((kin_en + equations.gamma * p_over_gamma_minus_one + 2 * mag_en) * v_normal
248
-
249
B_normal * (v1 * B1 + v2 * B2 + v3 * B3) + equations.c_h * psi * B_normal)
250
f6 = (equations.c_h * psi * normal_direction[1] +
251
(v2 * B1 - v1 * B2) * normal_direction[2] +
252
(v3 * B1 - v1 * B3) * normal_direction[3])
253
f7 = ((v1 * B2 - v2 * B1) * normal_direction[1] +
254
equations.c_h * psi * normal_direction[2] +
255
(v3 * B2 - v2 * B3) * normal_direction[3])
256
f8 = ((v1 * B3 - v3 * B1) * normal_direction[1] +
257
(v2 * B3 - v3 * B2) * normal_direction[2] +
258
equations.c_h * psi * normal_direction[3])
259
f9 = equations.c_h * B_normal
260
261
return SVector(f1, f2, f3, f4, f5, f6, f7, f8, f9)
262
end
263
264
"""
265
flux_nonconservative_powell(u_ll, u_rr, orientation::Integer,
266
equations::IdealGlmMhdEquations3D)
267
flux_nonconservative_powell(u_ll, u_rr,
268
normal_direction::AbstractVector,
269
equations::IdealGlmMhdEquations3D)
270
271
Non-symmetric two-point flux discretizing the nonconservative (source) term of
272
Powell and the Galilean nonconservative term associated with the GLM multiplier
273
of the [`IdealGlmMhdEquations3D`](@ref).
274
275
On curvilinear meshes, the implementation differs from the reference since we use the averaged
276
normal direction for the metrics dealiasing.
277
278
## References
279
- Marvin Bohm, Andrew R.Winters, Gregor J. Gassner, Dominik Derigs,
280
Florian Hindenlang, Joachim Saur
281
An entropy stable nodal discontinuous Galerkin method for the resistive MHD
282
equations. Part I: Theory and numerical verification
283
[DOI: 10.1016/j.jcp.2018.06.027](https://doi.org/10.1016/j.jcp.2018.06.027)
284
"""
285
@inline function flux_nonconservative_powell(u_ll, u_rr, orientation::Integer,
286
equations::IdealGlmMhdEquations3D)
287
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll
288
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr
289
290
v1_ll = rho_v1_ll / rho_ll
291
v2_ll = rho_v2_ll / rho_ll
292
v3_ll = rho_v3_ll / rho_ll
293
v_dot_B_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll
294
295
# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)
296
# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2,3}, 0, 0, 0, v_{1,2,3})
297
if orientation == 1
298
f = SVector(0,
299
B1_ll * B1_rr,
300
B2_ll * B1_rr,
301
B3_ll * B1_rr,
302
v_dot_B_ll * B1_rr + v1_ll * psi_ll * psi_rr,
303
v1_ll * B1_rr,
304
v2_ll * B1_rr,
305
v3_ll * B1_rr,
306
v1_ll * psi_rr)
307
elseif orientation == 2
308
f = SVector(0,
309
B1_ll * B2_rr,
310
B2_ll * B2_rr,
311
B3_ll * B2_rr,
312
v_dot_B_ll * B2_rr + v2_ll * psi_ll * psi_rr,
313
v1_ll * B2_rr,
314
v2_ll * B2_rr,
315
v3_ll * B2_rr,
316
v2_ll * psi_rr)
317
else # orientation == 3
318
f = SVector(0,
319
B1_ll * B3_rr,
320
B2_ll * B3_rr,
321
B3_ll * B3_rr,
322
v_dot_B_ll * B3_rr + v3_ll * psi_ll * psi_rr,
323
v1_ll * B3_rr,
324
v2_ll * B3_rr,
325
v3_ll * B3_rr,
326
v3_ll * psi_rr)
327
end
328
329
return f
330
end
331
332
@inline function flux_nonconservative_powell(u_ll, u_rr,
333
normal_direction::AbstractVector,
334
equations::IdealGlmMhdEquations3D)
335
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll
336
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr
337
338
v1_ll = rho_v1_ll / rho_ll
339
v2_ll = rho_v2_ll / rho_ll
340
v3_ll = rho_v3_ll / rho_ll
341
v_dot_B_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll
342
343
v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +
344
v3_ll * normal_direction[3]
345
B_dot_n_rr = B1_rr * normal_direction[1] +
346
B2_rr * normal_direction[2] +
347
B3_rr * normal_direction[3]
348
349
# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)
350
# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2,3}, 0, 0, 0, v_{1,2,3})
351
f = SVector(0,
352
B1_ll * B_dot_n_rr,
353
B2_ll * B_dot_n_rr,
354
B3_ll * B_dot_n_rr,
355
v_dot_B_ll * B_dot_n_rr + v_dot_n_ll * psi_ll * psi_rr,
356
v1_ll * B_dot_n_rr,
357
v2_ll * B_dot_n_rr,
358
v3_ll * B_dot_n_rr,
359
v_dot_n_ll * psi_rr)
360
361
return f
362
end
363
364
# For `VolumeIntegralSubcellLimiting` the nonconservative flux is created as a callable struct to
365
# enable dispatch on the type of the nonconservative term (symmetric / jump).
366
"""
367
flux_nonconservative_powell_local_symmetric(u_ll, u_rr,
368
normal_direction::AbstractVector,
369
equations::IdealGlmMhdEquations3D)
370
371
Non-symmetric two-point flux discretizing the nonconservative (source) term of
372
Powell and the Galilean nonconservative term associated with the GLM multiplier
373
of the [`IdealGlmMhdEquations3D`](@ref).
374
375
This implementation uses a non-conservative term that can be written as the product
376
of local and symmetric parts. It is equivalent to the non-conservative flux of Bohm
377
et al. [`flux_nonconservative_powell`](@ref) for conforming meshes but it yields different
378
results on non-conforming meshes(!). On curvilinear meshes this formulation applies the
379
local normal direction compared to the averaged one used in [`flux_nonconservative_powell`](@ref).
380
381
The two other flux functions with the same name return either the local
382
or symmetric portion of the non-conservative flux based on the type of the
383
nonconservative_type argument, employing multiple dispatch. They are used to
384
compute the subcell fluxes in dg_3d_subcell_limiters.jl.
385
386
## References
387
- Rueda-Ramírez, Gassner (2023). A Flux-Differencing Formula for Split-Form Summation By Parts
388
Discretizations of Non-Conservative Systems.
389
[DOI: 10.1016/j.jcp.2023.112607](https://doi.org/10.1016/j.jcp.2023.112607).
390
[arXiv: 2211.14009](https://doi.org/10.48550/arXiv.2211.14009).
391
"""
392
@inline function (noncons_flux::FluxNonConservativePowellLocalSymmetric)(u_ll, u_rr,
393
normal_direction::AbstractVector,
394
equations::IdealGlmMhdEquations3D)
395
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll
396
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr
397
398
v1_ll = rho_v1_ll / rho_ll
399
v2_ll = rho_v2_ll / rho_ll
400
v3_ll = rho_v3_ll / rho_ll
401
v_dot_B_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll
402
403
# The factor 0.5 of the averages can be omitted since it is already applied when this
404
# function is called.
405
psi_avg = (psi_ll + psi_rr)
406
B1_avg = (B1_ll + B1_rr)
407
B2_avg = (B2_ll + B2_rr)
408
B3_avg = (B3_ll + B3_rr)
409
410
B_dot_n_avg = B1_avg * normal_direction[1] + B2_avg * normal_direction[2] +
411
B3_avg * normal_direction[3]
412
v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +
413
v3_ll * normal_direction[3]
414
415
# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)
416
# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2,3}, 0, 0, 0, v_{1,2,3})
417
f = SVector(0,
418
B1_ll * B_dot_n_avg,
419
B2_ll * B_dot_n_avg,
420
B3_ll * B_dot_n_avg,
421
v_dot_B_ll * B_dot_n_avg + v_dot_n_ll * psi_ll * psi_avg,
422
v1_ll * B_dot_n_avg,
423
v2_ll * B_dot_n_avg,
424
v3_ll * B_dot_n_avg,
425
v_dot_n_ll * psi_avg)
426
427
return f
428
end
429
430
"""
431
flux_nonconservative_powell_local_symmetric(u_ll, normal_direction_ll::AbstractVector,
432
equations::IdealGlmMhdEquations3D,
433
nonconservative_type::NonConservativeLocal,
434
nonconservative_term::Integer)
435
436
Local part of the Powell and GLM non-conservative terms. Needed for the calculation of
437
the non-conservative staggered "fluxes" for subcell limiting. See, e.g.,
438
- Rueda-Ramírez, Gassner (2023). A Flux-Differencing Formula for Split-Form Summation By Parts
439
Discretizations of Non-Conservative Systems.
440
[DOI: 10.1016/j.jcp.2023.112607](https://doi.org/10.1016/j.jcp.2023.112607).
441
[arXiv: 2211.14009](https://doi.org/10.48550/arXiv.2211.14009).
442
443
This function is used to compute the subcell fluxes in dg_3d_subcell_limiters.jl.
444
"""
445
@inline function (noncons_flux::FluxNonConservativePowellLocalSymmetric)(u_ll,
446
normal_direction_ll::AbstractVector,
447
equations::IdealGlmMhdEquations3D,
448
nonconservative_type::NonConservativeLocal,
449
nonconservative_term::Integer)
450
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll
451
452
if nonconservative_term == 1
453
# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)
454
v1_ll = rho_v1_ll / rho_ll
455
v2_ll = rho_v2_ll / rho_ll
456
v3_ll = rho_v3_ll / rho_ll
457
v_dot_B_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll
458
459
f = SVector(0,
460
B1_ll,
461
B2_ll,
462
B3_ll,
463
v_dot_B_ll,
464
v1_ll,
465
v2_ll,
466
v3_ll,
467
0)
468
else # nonconservative_term == 2
469
# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})
470
v1_ll = rho_v1_ll / rho_ll
471
v2_ll = rho_v2_ll / rho_ll
472
v3_ll = rho_v3_ll / rho_ll
473
v_dot_n_ll = v1_ll * normal_direction_ll[1] + v2_ll * normal_direction_ll[2] +
474
v3_ll * normal_direction_ll[3]
475
476
f = SVector(0,
477
0,
478
0,
479
0,
480
v_dot_n_ll * psi_ll,
481
0,
482
0,
483
0,
484
v_dot_n_ll)
485
end
486
return f
487
end
488
489
"""
490
flux_nonconservative_powell_local_symmetric(u_ll, normal_direction_avg::AbstractVector,
491
equations::IdealGlmMhdEquations3D,
492
nonconservative_type::NonConservativeSymmetric,
493
nonconservative_term::Integer)
494
495
Symmetric part of the Powell and GLM non-conservative terms. Needed for the calculation of
496
the non-conservative staggered "fluxes" for subcell limiting. See, e.g.,
497
- Rueda-Ramírez, Gassner (2023). A Flux-Differencing Formula for Split-Form Summation By Parts
498
Discretizations of Non-Conservative Systems.
499
[DOI: 10.1016/j.jcp.2023.112607](https://doi.org/10.1016/j.jcp.2023.112607).
500
[arXiv: 2211.14009](https://doi.org/10.48550/arXiv.2211.14009).
501
502
This function is used to compute the subcell fluxes in dg_3d_subcell_limiters.jl.
503
"""
504
@inline function (noncons_flux::FluxNonConservativePowellLocalSymmetric)(u_ll, u_rr,
505
normal_direction_avg::AbstractVector,
506
equations::IdealGlmMhdEquations3D,
507
nonconservative_type::NonConservativeSymmetric,
508
nonconservative_term::Integer)
509
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll
510
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr
511
512
if nonconservative_term == 1
513
# Powell nonconservative term: (0, B_1, B_2, B_3, v⋅B, v_1, v_2, v_3, 0)
514
# The factor 0.5 of the average can be omitted since it is already applied when this
515
# function is called.
516
B_dot_n_avg = ((B1_ll + B1_rr) * normal_direction_avg[1] +
517
(B2_ll + B2_rr) * normal_direction_avg[2] +
518
(B3_ll + B3_rr) * normal_direction_avg[3])
519
f = SVector(0,
520
B_dot_n_avg,
521
B_dot_n_avg,
522
B_dot_n_avg,
523
B_dot_n_avg,
524
B_dot_n_avg,
525
B_dot_n_avg,
526
B_dot_n_avg,
527
0)
528
else # nonconservative_term == 2
529
# Galilean nonconservative term: (0, 0, 0, 0, ψ v_{1,2}, 0, 0, 0, v_{1,2})
530
# The factor 0.5 of the average can be omitted since it is already applied when this
531
# function is called.
532
psi_avg = (psi_ll + psi_rr)
533
f = SVector(0,
534
0,
535
0,
536
0,
537
psi_avg,
538
0,
539
0,
540
0,
541
psi_avg)
542
end
543
544
return f
545
end
546
547
"""
548
flux_derigs_etal(u_ll, u_rr, orientation, equations::IdealGlmMhdEquations3D)
549
550
Entropy conserving two-point flux by
551
- Derigs et al. (2018)
552
Ideal GLM-MHD: About the entropy consistent nine-wave magnetic field
553
divergence diminishing ideal magnetohydrodynamics equations
554
[DOI: 10.1016/j.jcp.2018.03.002](https://doi.org/10.1016/j.jcp.2018.03.002)
555
"""
556
function flux_derigs_etal(u_ll, u_rr, orientation::Integer,
557
equations::IdealGlmMhdEquations3D)
558
# Unpack left and right states to get velocities, pressure, and inverse temperature (called beta)
559
rho_ll, v1_ll, v2_ll, v3_ll, p_ll, B1_ll, B2_ll, B3_ll, psi_ll = cons2prim(u_ll,
560
equations)
561
rho_rr, v1_rr, v2_rr, v3_rr, p_rr, B1_rr, B2_rr, B3_rr, psi_rr = cons2prim(u_rr,
562
equations)
563
564
vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^2
565
vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^2
566
mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2
567
mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2
568
beta_ll = 0.5f0 * rho_ll / p_ll
569
beta_rr = 0.5f0 * rho_rr / p_rr
570
# for convenience store v⋅B
571
vel_dot_mag_ll = v1_ll * B1_ll + v2_ll * B2_ll + v3_ll * B3_ll
572
vel_dot_mag_rr = v1_rr * B1_rr + v2_rr * B2_rr + v3_rr * B3_rr
573
574
# Compute the necessary mean values needed for either direction
575
rho_avg = 0.5f0 * (rho_ll + rho_rr)
576
rho_mean = ln_mean(rho_ll, rho_rr)
577
beta_mean = ln_mean(beta_ll, beta_rr)
578
beta_avg = 0.5f0 * (beta_ll + beta_rr)
579
v1_avg = 0.5f0 * (v1_ll + v1_rr)
580
v2_avg = 0.5f0 * (v2_ll + v2_rr)
581
v3_avg = 0.5f0 * (v3_ll + v3_rr)
582
p_mean = 0.5f0 * rho_avg / beta_avg
583
B1_avg = 0.5f0 * (B1_ll + B1_rr)
584
B2_avg = 0.5f0 * (B2_ll + B2_rr)
585
B3_avg = 0.5f0 * (B3_ll + B3_rr)
586
psi_avg = 0.5f0 * (psi_ll + psi_rr)
587
vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)
588
mag_norm_avg = 0.5f0 * (mag_norm_ll + mag_norm_rr)
589
vel_dot_mag_avg = 0.5f0 * (vel_dot_mag_ll + vel_dot_mag_rr)
590
591
# Calculate fluxes depending on orientation with specific direction averages
592
if orientation == 1
593
f1 = rho_mean * v1_avg
594
f2 = f1 * v1_avg + p_mean + 0.5f0 * mag_norm_avg - B1_avg * B1_avg
595
f3 = f1 * v2_avg - B1_avg * B2_avg
596
f4 = f1 * v3_avg - B1_avg * B3_avg
597
f6 = equations.c_h * psi_avg
598
f7 = v1_avg * B2_avg - v2_avg * B1_avg
599
f8 = v1_avg * B3_avg - v3_avg * B1_avg
600
f9 = equations.c_h * B1_avg
601
# total energy flux is complicated and involves the previous eight components
602
psi_B1_avg = 0.5f0 * (B1_ll * psi_ll + B1_rr * psi_rr)
603
v1_mag_avg = 0.5f0 * (v1_ll * mag_norm_ll + v1_rr * mag_norm_rr)
604
f5 = (f1 * 0.5f0 * (1 / (equations.gamma - 1) / beta_mean - vel_norm_avg) +
605
f2 * v1_avg + f3 * v2_avg +
606
f4 * v3_avg + f6 * B1_avg + f7 * B2_avg + f8 * B3_avg + f9 * psi_avg -
607
0.5f0 * v1_mag_avg +
608
B1_avg * vel_dot_mag_avg - equations.c_h * psi_B1_avg)
609
elseif orientation == 2
610
f1 = rho_mean * v2_avg
611
f2 = f1 * v1_avg - B2_avg * B1_avg
612
f3 = f1 * v2_avg + p_mean + 0.5f0 * mag_norm_avg - B2_avg * B2_avg
613
f4 = f1 * v3_avg - B2_avg * B3_avg
614
f6 = v2_avg * B1_avg - v1_avg * B2_avg
615
f7 = equations.c_h * psi_avg
616
f8 = v2_avg * B3_avg - v3_avg * B2_avg
617
f9 = equations.c_h * B2_avg
618
# total energy flux is complicated and involves the previous eight components
619
psi_B2_avg = 0.5f0 * (B2_ll * psi_ll + B2_rr * psi_rr)
620
v2_mag_avg = 0.5f0 * (v2_ll * mag_norm_ll + v2_rr * mag_norm_rr)
621
f5 = (f1 * 0.5f0 * (1 / (equations.gamma - 1) / beta_mean - vel_norm_avg) +
622
f2 * v1_avg + f3 * v2_avg +
623
f4 * v3_avg + f6 * B1_avg + f7 * B2_avg + f8 * B3_avg + f9 * psi_avg -
624
0.5f0 * v2_mag_avg +
625
B2_avg * vel_dot_mag_avg - equations.c_h * psi_B2_avg)
626
else
627
f1 = rho_mean * v3_avg
628
f2 = f1 * v1_avg - B3_avg * B1_avg
629
f3 = f1 * v2_avg - B3_avg * B2_avg
630
f4 = f1 * v3_avg + p_mean + 0.5f0 * mag_norm_avg - B3_avg * B3_avg
631
f6 = v3_avg * B1_avg - v1_avg * B3_avg
632
f7 = v3_avg * B2_avg - v2_avg * B3_avg
633
f8 = equations.c_h * psi_avg
634
f9 = equations.c_h * B3_avg
635
# total energy flux is complicated and involves the previous eight components
636
psi_B3_avg = 0.5f0 * (B3_ll * psi_ll + B3_rr * psi_rr)
637
v3_mag_avg = 0.5f0 * (v3_ll * mag_norm_ll + v3_rr * mag_norm_rr)
638
f5 = (f1 * 0.5f0 * (1 / (equations.gamma - 1) / beta_mean - vel_norm_avg) +
639
f2 * v1_avg + f3 * v2_avg +
640
f4 * v3_avg + f6 * B1_avg + f7 * B2_avg + f8 * B3_avg + f9 * psi_avg -
641
0.5f0 * v3_mag_avg +
642
B3_avg * vel_dot_mag_avg - equations.c_h * psi_B3_avg)
643
end
644
645
return SVector(f1, f2, f3, f4, f5, f6, f7, f8, f9)
646
end
647
648
"""
649
flux_hindenlang_gassner(u_ll, u_rr, orientation_or_normal_direction,
650
equations::IdealGlmMhdEquations3D)
651
652
Entropy conserving and kinetic energy preserving two-point flux of
653
Hindenlang and Gassner (2019), extending [`flux_ranocha`](@ref) to the MHD equations.
654
655
## References
656
- Florian Hindenlang, Gregor Gassner (2019)
657
A new entropy conservative two-point flux for ideal MHD equations derived from
658
first principles.
659
Presented at HONOM 2019: European workshop on high order numerical methods
660
for evolutionary PDEs, theory and applications
661
- Hendrik Ranocha (2018)
662
Generalised Summation-by-Parts Operators and Entropy Stability of Numerical Methods
663
for Hyperbolic Balance Laws
664
[PhD thesis, TU Braunschweig](https://cuvillier.de/en/shop/publications/7743)
665
- Hendrik Ranocha (2020)
666
Entropy Conserving and Kinetic Energy Preserving Numerical Methods for
667
the Euler Equations Using Summation-by-Parts Operators
668
[Proceedings of ICOSAHOM 2018](https://doi.org/10.1007/978-3-030-39647-3_42)
669
"""
670
@inline function flux_hindenlang_gassner(u_ll, u_rr, orientation::Integer,
671
equations::IdealGlmMhdEquations3D)
672
# Unpack left and right states
673
rho_ll, v1_ll, v2_ll, v3_ll, p_ll, B1_ll, B2_ll, B3_ll, psi_ll = cons2prim(u_ll,
674
equations)
675
rho_rr, v1_rr, v2_rr, v3_rr, p_rr, B1_rr, B2_rr, B3_rr, psi_rr = cons2prim(u_rr,
676
equations)
677
678
# Compute the necessary mean values needed for either direction
679
rho_mean = ln_mean(rho_ll, rho_rr)
680
# Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)`
681
# in exact arithmetic since
682
# log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ)
683
# = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ)
684
inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll)
685
v1_avg = 0.5f0 * (v1_ll + v1_rr)
686
v2_avg = 0.5f0 * (v2_ll + v2_rr)
687
v3_avg = 0.5f0 * (v3_ll + v3_rr)
688
p_avg = 0.5f0 * (p_ll + p_rr)
689
psi_avg = 0.5f0 * (psi_ll + psi_rr)
690
velocity_square_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr)
691
magnetic_square_avg = 0.5f0 * (B1_ll * B1_rr + B2_ll * B2_rr + B3_ll * B3_rr)
692
693
# Calculate fluxes depending on orientation with specific direction averages
694
if orientation == 1
695
f1 = rho_mean * v1_avg
696
f2 = f1 * v1_avg + p_avg + magnetic_square_avg -
697
0.5f0 * (B1_ll * B1_rr + B1_rr * B1_ll)
698
f3 = f1 * v2_avg - 0.5f0 * (B1_ll * B2_rr + B1_rr * B2_ll)
699
f4 = f1 * v3_avg - 0.5f0 * (B1_ll * B3_rr + B1_rr * B3_ll)
700
#f5 below
701
f6 = equations.c_h * psi_avg
702
f7 = 0.5f0 * (v1_ll * B2_ll - v2_ll * B1_ll + v1_rr * B2_rr - v2_rr * B1_rr)
703
f8 = 0.5f0 * (v1_ll * B3_ll - v3_ll * B1_ll + v1_rr * B3_rr - v3_rr * B1_rr)
704
f9 = equations.c_h * 0.5f0 * (B1_ll + B1_rr)
705
# total energy flux is complicated and involves the previous components
706
f5 = (f1 *
707
(velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one)
708
+
709
0.5f0 * (+p_ll * v1_rr + p_rr * v1_ll
710
+ (v1_ll * B2_ll * B2_rr + v1_rr * B2_rr * B2_ll)
711
+ (v1_ll * B3_ll * B3_rr + v1_rr * B3_rr * B3_ll)
712
-
713
(v2_ll * B1_ll * B2_rr + v2_rr * B1_rr * B2_ll)
714
-
715
(v3_ll * B1_ll * B3_rr + v3_rr * B1_rr * B3_ll)
716
+
717
equations.c_h * (B1_ll * psi_rr + B1_rr * psi_ll)))
718
elseif orientation == 2
719
f1 = rho_mean * v2_avg
720
f2 = f1 * v1_avg - 0.5f0 * (B2_ll * B1_rr + B2_rr * B1_ll)
721
f3 = f1 * v2_avg + p_avg + magnetic_square_avg -
722
0.5f0 * (B2_ll * B2_rr + B2_rr * B2_ll)
723
f4 = f1 * v3_avg - 0.5f0 * (B2_ll * B3_rr + B2_rr * B3_ll)
724
#f5 below
725
f6 = 0.5f0 * (v2_ll * B1_ll - v1_ll * B2_ll + v2_rr * B1_rr - v1_rr * B2_rr)
726
f7 = equations.c_h * psi_avg
727
f8 = 0.5f0 * (v2_ll * B3_ll - v3_ll * B2_ll + v2_rr * B3_rr - v3_rr * B2_rr)
728
f9 = equations.c_h * 0.5f0 * (B2_ll + B2_rr)
729
# total energy flux is complicated and involves the previous components
730
f5 = (f1 *
731
(velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one)
732
+
733
0.5f0 * (+p_ll * v2_rr + p_rr * v2_ll
734
+ (v2_ll * B1_ll * B1_rr + v2_rr * B1_rr * B1_ll)
735
+ (v2_ll * B3_ll * B3_rr + v2_rr * B3_rr * B3_ll)
736
-
737
(v1_ll * B2_ll * B1_rr + v1_rr * B2_rr * B1_ll)
738
-
739
(v3_ll * B2_ll * B3_rr + v3_rr * B2_rr * B3_ll)
740
+
741
equations.c_h * (B2_ll * psi_rr + B2_rr * psi_ll)))
742
else # orientation == 3
743
f1 = rho_mean * v3_avg
744
f2 = f1 * v1_avg - 0.5f0 * (B3_ll * B1_rr + B3_rr * B1_ll)
745
f3 = f1 * v2_avg - 0.5f0 * (B3_ll * B2_rr + B3_rr * B2_ll)
746
f4 = f1 * v3_avg + p_avg + magnetic_square_avg -
747
0.5f0 * (B3_ll * B3_rr + B3_rr * B3_ll)
748
#f5 below
749
f6 = 0.5f0 * (v3_ll * B1_ll - v1_ll * B3_ll + v3_rr * B1_rr - v1_rr * B3_rr)
750
f7 = 0.5f0 * (v3_ll * B2_ll - v2_ll * B3_ll + v3_rr * B2_rr - v2_rr * B3_rr)
751
f8 = equations.c_h * psi_avg
752
f9 = equations.c_h * 0.5f0 * (B3_ll + B3_rr)
753
# total energy flux is complicated and involves the previous components
754
f5 = (f1 *
755
(velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one)
756
+
757
0.5f0 * (+p_ll * v3_rr + p_rr * v3_ll
758
+ (v3_ll * B1_ll * B1_rr + v3_rr * B1_rr * B1_ll)
759
+ (v3_ll * B2_ll * B2_rr + v3_rr * B2_rr * B2_ll)
760
-
761
(v1_ll * B3_ll * B1_rr + v1_rr * B3_rr * B1_ll)
762
-
763
(v2_ll * B3_ll * B2_rr + v2_rr * B3_rr * B2_ll)
764
+
765
equations.c_h * (B3_ll * psi_rr + B3_rr * psi_ll)))
766
end
767
768
return SVector(f1, f2, f3, f4, f5, f6, f7, f8, f9)
769
end
770
771
@inline function flux_hindenlang_gassner(u_ll, u_rr, normal_direction::AbstractVector,
772
equations::IdealGlmMhdEquations3D)
773
# Unpack left and right states
774
rho_ll, v1_ll, v2_ll, v3_ll, p_ll, B1_ll, B2_ll, B3_ll, psi_ll = cons2prim(u_ll,
775
equations)
776
rho_rr, v1_rr, v2_rr, v3_rr, p_rr, B1_rr, B2_rr, B3_rr, psi_rr = cons2prim(u_rr,
777
equations)
778
v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +
779
v3_ll * normal_direction[3]
780
v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +
781
v3_rr * normal_direction[3]
782
B_dot_n_ll = B1_ll * normal_direction[1] + B2_ll * normal_direction[2] +
783
B3_ll * normal_direction[3]
784
B_dot_n_rr = B1_rr * normal_direction[1] + B2_rr * normal_direction[2] +
785
B3_rr * normal_direction[3]
786
787
# Compute the necessary mean values needed for either direction
788
rho_mean = ln_mean(rho_ll, rho_rr)
789
# Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)`
790
# in exact arithmetic since
791
# log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ)
792
# = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ)
793
inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll)
794
v1_avg = 0.5f0 * (v1_ll + v1_rr)
795
v2_avg = 0.5f0 * (v2_ll + v2_rr)
796
v3_avg = 0.5f0 * (v3_ll + v3_rr)
797
p_avg = 0.5f0 * (p_ll + p_rr)
798
psi_avg = 0.5f0 * (psi_ll + psi_rr)
799
velocity_square_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr)
800
magnetic_square_avg = 0.5f0 * (B1_ll * B1_rr + B2_ll * B2_rr + B3_ll * B3_rr)
801
802
# Calculate fluxes depending on normal_direction
803
f1 = rho_mean * 0.5f0 * (v_dot_n_ll + v_dot_n_rr)
804
f2 = (f1 * v1_avg + (p_avg + magnetic_square_avg) * normal_direction[1]
805
-
806
0.5f0 * (B_dot_n_ll * B1_rr + B_dot_n_rr * B1_ll))
807
f3 = (f1 * v2_avg + (p_avg + magnetic_square_avg) * normal_direction[2]
808
-
809
0.5f0 * (B_dot_n_ll * B2_rr + B_dot_n_rr * B2_ll))
810
f4 = (f1 * v3_avg + (p_avg + magnetic_square_avg) * normal_direction[3]
811
-
812
0.5f0 * (B_dot_n_ll * B3_rr + B_dot_n_rr * B3_ll))
813
#f5 below
814
f6 = (equations.c_h * psi_avg * normal_direction[1]
815
+
816
0.5f0 * (v_dot_n_ll * B1_ll - v1_ll * B_dot_n_ll +
817
v_dot_n_rr * B1_rr - v1_rr * B_dot_n_rr))
818
f7 = (equations.c_h * psi_avg * normal_direction[2]
819
+
820
0.5f0 * (v_dot_n_ll * B2_ll - v2_ll * B_dot_n_ll +
821
v_dot_n_rr * B2_rr - v2_rr * B_dot_n_rr))
822
f8 = (equations.c_h * psi_avg * normal_direction[3]
823
+
824
0.5f0 * (v_dot_n_ll * B3_ll - v3_ll * B_dot_n_ll +
825
v_dot_n_rr * B3_rr - v3_rr * B_dot_n_rr))
826
f9 = equations.c_h * 0.5f0 * (B_dot_n_ll + B_dot_n_rr)
827
# total energy flux is complicated and involves the previous components
828
f5 = (f1 * (velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one)
829
+
830
0.5f0 * (+p_ll * v_dot_n_rr + p_rr * v_dot_n_ll
831
+ (v_dot_n_ll * B1_ll * B1_rr + v_dot_n_rr * B1_rr * B1_ll)
832
+ (v_dot_n_ll * B2_ll * B2_rr + v_dot_n_rr * B2_rr * B2_ll)
833
+ (v_dot_n_ll * B3_ll * B3_rr + v_dot_n_rr * B3_rr * B3_ll)
834
-
835
(v1_ll * B_dot_n_ll * B1_rr + v1_rr * B_dot_n_rr * B1_ll)
836
-
837
(v2_ll * B_dot_n_ll * B2_rr + v2_rr * B_dot_n_rr * B2_ll)
838
-
839
(v3_ll * B_dot_n_ll * B3_rr + v3_rr * B_dot_n_rr * B3_ll)
840
+
841
equations.c_h * (B_dot_n_ll * psi_rr + B_dot_n_rr * psi_ll)))
842
843
return SVector(f1, f2, f3, f4, f5, f6, f7, f8, f9)
844
end
845
846
# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation
847
@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,
848
equations::IdealGlmMhdEquations3D)
849
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, _ = u_ll
850
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, _ = u_rr
851
852
# Calculate the left/right velocities and fast magnetoacoustic wave speeds
853
if orientation == 1
854
v_ll = rho_v1_ll / rho_ll
855
v_rr = rho_v1_rr / rho_rr
856
elseif orientation == 2
857
v_ll = rho_v2_ll / rho_ll
858
v_rr = rho_v2_rr / rho_rr
859
else # orientation == 3
860
v_ll = rho_v3_ll / rho_ll
861
v_rr = rho_v3_rr / rho_rr
862
end
863
cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)
864
cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)
865
866
return max(abs(v_ll), abs(v_rr)) + max(cf_ll, cf_rr)
867
end
868
869
@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,
870
equations::IdealGlmMhdEquations3D)
871
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, _ = u_ll
872
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, _ = u_rr
873
874
# Calculate normal velocities and fast magnetoacoustic wave speeds
875
# left
876
v1_ll = rho_v1_ll / rho_ll
877
v2_ll = rho_v2_ll / rho_ll
878
v3_ll = rho_v3_ll / rho_ll
879
v_ll = (v1_ll * normal_direction[1]
880
+ v2_ll * normal_direction[2]
881
+ v3_ll * normal_direction[3])
882
cf_ll = calc_fast_wavespeed(u_ll, normal_direction, equations)
883
# right
884
v1_rr = rho_v1_rr / rho_rr
885
v2_rr = rho_v2_rr / rho_rr
886
v3_rr = rho_v3_rr / rho_rr
887
v_rr = (v1_rr * normal_direction[1]
888
+ v2_rr * normal_direction[2]
889
+ v3_rr * normal_direction[3])
890
cf_rr = calc_fast_wavespeed(u_rr, normal_direction, equations)
891
892
# wave speeds already scaled by norm(normal_direction) in [`calc_fast_wavespeed`](@ref)
893
return max(abs(v_ll), abs(v_rr)) + max(cf_ll, cf_rr)
894
end
895
896
# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`
897
@inline function max_abs_speed(u_ll, u_rr, orientation::Integer,
898
equations::IdealGlmMhdEquations3D)
899
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, _ = u_ll
900
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, _ = u_rr
901
902
# Calculate the left/right velocities and fast magnetoacoustic wave speeds
903
if orientation == 1
904
v_ll = rho_v1_ll / rho_ll
905
v_rr = rho_v1_rr / rho_rr
906
elseif orientation == 2
907
v_ll = rho_v2_ll / rho_ll
908
v_rr = rho_v2_rr / rho_rr
909
else # orientation == 3
910
v_ll = rho_v3_ll / rho_ll
911
v_rr = rho_v3_rr / rho_rr
912
end
913
cf_ll = calc_fast_wavespeed(u_ll, orientation, equations)
914
cf_rr = calc_fast_wavespeed(u_rr, orientation, equations)
915
916
return max(abs(v_ll) + cf_ll, abs(v_rr) + cf_rr)
917
end
918
919
# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`
920
@inline function max_abs_speed(u_ll, u_rr, normal_direction::AbstractVector,
921
equations::IdealGlmMhdEquations3D)
922
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, _ = u_ll
923
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, _ = u_rr
924
925
# Calculate normal velocities and fast magnetoacoustic wave speeds
926
# left
927
v1_ll = rho_v1_ll / rho_ll
928
v2_ll = rho_v2_ll / rho_ll
929
v3_ll = rho_v3_ll / rho_ll
930
v_ll = (v1_ll * normal_direction[1]
931
+ v2_ll * normal_direction[2]
932
+ v3_ll * normal_direction[3])
933
cf_ll = calc_fast_wavespeed(u_ll, normal_direction, equations)
934
# right
935
v1_rr = rho_v1_rr / rho_rr
936
v2_rr = rho_v2_rr / rho_rr
937
v3_rr = rho_v3_rr / rho_rr
938
v_rr = (v1_rr * normal_direction[1]
939
+ v2_rr * normal_direction[2]
940
+ v3_rr * normal_direction[3])
941
cf_rr = calc_fast_wavespeed(u_rr, normal_direction, equations)
942
943
# wave speeds already scaled by norm(normal_direction) in [`calc_fast_wavespeed`](@ref)
944
return max(abs(v_ll) + cf_ll, abs(v_rr) + cf_rr)
945
end
946
947
# Calculate estimate for minimum and maximum wave speeds for HLL-type fluxes
948
@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer,
949
equations::IdealGlmMhdEquations3D)
950
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, _ = u_ll
951
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, _ = u_rr
952
953
# Calculate primitive variables and speed of sound
954
v1_ll = rho_v1_ll / rho_ll
955
v2_ll = rho_v2_ll / rho_ll
956
v3_ll = rho_v3_ll / rho_ll
957
958
v1_rr = rho_v1_rr / rho_rr
959
v2_rr = rho_v2_rr / rho_rr
960
v3_rr = rho_v3_rr / rho_rr
961
962
# Approximate the left-most and right-most eigenvalues in the Riemann fan
963
if orientation == 1 # x-direction
964
λ_min = v1_ll - calc_fast_wavespeed(u_ll, orientation, equations)
965
λ_max = v1_rr + calc_fast_wavespeed(u_rr, orientation, equations)
966
elseif orientation == 2 # y-direction
967
λ_min = v2_ll - calc_fast_wavespeed(u_ll, orientation, equations)
968
λ_max = v2_rr + calc_fast_wavespeed(u_rr, orientation, equations)
969
else # z-direction
970
λ_min = v3_ll - calc_fast_wavespeed(u_ll, orientation, equations)
971
λ_max = v3_rr + calc_fast_wavespeed(u_rr, orientation, equations)
972
end
973
974
return λ_min, λ_max
975
end
976
977
@inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,
978
equations::IdealGlmMhdEquations3D)
979
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, _ = u_ll
980
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, _ = u_rr
981
982
# Calculate primitive velocity variables
983
v1_ll = rho_v1_ll / rho_ll
984
v2_ll = rho_v2_ll / rho_ll
985
v3_ll = rho_v3_ll / rho_ll
986
987
v1_rr = rho_v1_rr / rho_rr
988
v2_rr = rho_v2_rr / rho_rr
989
v3_rr = rho_v3_rr / rho_rr
990
991
v_normal_ll = (v1_ll * normal_direction[1] +
992
v2_ll * normal_direction[2] +
993
v3_ll * normal_direction[3])
994
v_normal_rr = (v1_rr * normal_direction[1] +
995
v2_rr * normal_direction[2] +
996
v3_rr * normal_direction[3])
997
998
# Estimate the min/max eigenvalues in the normal direction
999
λ_min = v_normal_ll - calc_fast_wavespeed(u_ll, normal_direction, equations)
1000
λ_max = v_normal_rr + calc_fast_wavespeed(u_rr, normal_direction, equations)
1001
1002
return λ_min, λ_max
1003
end
1004
1005
# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
1006
@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,
1007
equations::IdealGlmMhdEquations3D)
1008
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, _ = u_ll
1009
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, _ = u_rr
1010
1011
# Calculate primitive variables and speed of sound
1012
v1_ll = rho_v1_ll / rho_ll
1013
v2_ll = rho_v2_ll / rho_ll
1014
v3_ll = rho_v3_ll / rho_ll
1015
1016
v1_rr = rho_v1_rr / rho_rr
1017
v2_rr = rho_v2_rr / rho_rr
1018
v3_rr = rho_v3_rr / rho_rr
1019
1020
# Approximate the left-most and right-most eigenvalues in the Riemann fan
1021
if orientation == 1 # x-direction
1022
c_f_ll = calc_fast_wavespeed(u_ll, orientation, equations)
1023
c_f_rr = calc_fast_wavespeed(u_rr, orientation, equations)
1024
1025
λ_min = min(v1_ll - c_f_ll, v1_rr - c_f_rr)
1026
λ_max = max(v1_ll + c_f_ll, v1_rr + c_f_rr)
1027
elseif orientation == 2 # y-direction
1028
c_f_ll = calc_fast_wavespeed(u_ll, orientation, equations)
1029
c_f_rr = calc_fast_wavespeed(u_rr, orientation, equations)
1030
1031
λ_min = min(v2_ll - c_f_ll, v2_rr - c_f_rr)
1032
λ_max = max(v2_ll + c_f_ll, v2_rr + c_f_rr)
1033
else # z-direction
1034
c_f_ll = calc_fast_wavespeed(u_ll, orientation, equations)
1035
c_f_rr = calc_fast_wavespeed(u_rr, orientation, equations)
1036
1037
λ_min = min(v3_ll - c_f_ll, v3_rr - c_f_rr)
1038
λ_max = max(v3_ll + c_f_ll, v3_rr + c_f_rr)
1039
end
1040
1041
return λ_min, λ_max
1042
end
1043
1044
# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
1045
@inline function min_max_speed_davis(u_ll, u_rr, normal_direction::AbstractVector,
1046
equations::IdealGlmMhdEquations3D)
1047
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, _ = u_ll
1048
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, _ = u_rr
1049
1050
# Calculate primitive velocity variables
1051
v1_ll = rho_v1_ll / rho_ll
1052
v2_ll = rho_v2_ll / rho_ll
1053
v3_ll = rho_v3_ll / rho_ll
1054
1055
v1_rr = rho_v1_rr / rho_rr
1056
v2_rr = rho_v2_rr / rho_rr
1057
v3_rr = rho_v3_rr / rho_rr
1058
1059
v_normal_ll = (v1_ll * normal_direction[1] +
1060
v2_ll * normal_direction[2] +
1061
v3_ll * normal_direction[3])
1062
v_normal_rr = (v1_rr * normal_direction[1] +
1063
v2_rr * normal_direction[2] +
1064
v3_rr * normal_direction[3])
1065
1066
c_f_ll = calc_fast_wavespeed(u_ll, normal_direction, equations)
1067
c_f_rr = calc_fast_wavespeed(u_rr, normal_direction, equations)
1068
1069
# Estimate the min/max eigenvalues in the normal direction
1070
λ_min = min(v_normal_ll - c_f_ll, v_normal_rr - c_f_rr)
1071
λ_max = max(v_normal_ll + c_f_ll, v_normal_rr + c_f_rr)
1072
1073
return λ_min, λ_max
1074
end
1075
1076
"""
1077
min_max_speed_einfeldt(u_ll, u_rr, orientation_or_normal_direction, equations::IdealGlmMhdEquations3D)
1078
1079
Calculate minimum and maximum wave speeds for HLL-type fluxes as in
1080
- Li (2005)
1081
An HLLC Riemann solver for magneto-hydrodynamics
1082
[DOI: 10.1016/j.jcp.2004.08.020](https://doi.org/10.1016/j.jcp.2004.08.020)
1083
"""
1084
@inline function min_max_speed_einfeldt(u_ll, u_rr, orientation::Integer,
1085
equations::IdealGlmMhdEquations3D)
1086
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, _ = u_ll
1087
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, _ = u_rr
1088
1089
# Calculate primitive variables and speed of sound
1090
v1_ll = rho_v1_ll / rho_ll
1091
v2_ll = rho_v2_ll / rho_ll
1092
v3_ll = rho_v3_ll / rho_ll
1093
1094
v1_rr = rho_v1_rr / rho_rr
1095
v2_rr = rho_v2_rr / rho_rr
1096
v3_rr = rho_v3_rr / rho_rr
1097
1098
# Approximate the left-most and right-most eigenvalues in the Riemann fan
1099
if orientation == 1 # x-direction
1100
c_f_ll = calc_fast_wavespeed(u_ll, orientation, equations)
1101
c_f_rr = calc_fast_wavespeed(u_rr, orientation, equations)
1102
vel_roe, c_f_roe = calc_fast_wavespeed_roe(u_ll, u_rr, orientation, equations)
1103
λ_min = min(v1_ll - c_f_ll, vel_roe - c_f_roe)
1104
λ_max = max(v1_rr + c_f_rr, vel_roe + c_f_roe)
1105
elseif orientation == 2 # y-direction
1106
c_f_ll = calc_fast_wavespeed(u_ll, orientation, equations)
1107
c_f_rr = calc_fast_wavespeed(u_rr, orientation, equations)
1108
vel_roe, c_f_roe = calc_fast_wavespeed_roe(u_ll, u_rr, orientation, equations)
1109
λ_min = min(v2_ll - c_f_ll, vel_roe - c_f_roe)
1110
λ_max = max(v2_rr + c_f_rr, vel_roe + c_f_roe)
1111
else # z-direction
1112
c_f_ll = calc_fast_wavespeed(u_ll, orientation, equations)
1113
c_f_rr = calc_fast_wavespeed(u_rr, orientation, equations)
1114
vel_roe, c_f_roe = calc_fast_wavespeed_roe(u_ll, u_rr, orientation, equations)
1115
λ_min = min(v3_ll - c_f_ll, vel_roe - c_f_roe)
1116
λ_max = max(v3_rr + c_f_rr, vel_roe + c_f_roe)
1117
end
1118
1119
return λ_min, λ_max
1120
end
1121
1122
@inline function min_max_speed_einfeldt(u_ll, u_rr, normal_direction::AbstractVector,
1123
equations::IdealGlmMhdEquations3D)
1124
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, _ = u_ll
1125
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, _ = u_rr
1126
1127
# Calculate primitive velocity variables
1128
v1_ll = rho_v1_ll / rho_ll
1129
v2_ll = rho_v2_ll / rho_ll
1130
v3_ll = rho_v3_ll / rho_ll
1131
1132
v1_rr = rho_v1_rr / rho_rr
1133
v2_rr = rho_v2_rr / rho_rr
1134
v3_rr = rho_v3_rr / rho_rr
1135
1136
v_normal_ll = (v1_ll * normal_direction[1] +
1137
v2_ll * normal_direction[2] +
1138
v3_ll * normal_direction[3])
1139
v_normal_rr = (v1_rr * normal_direction[1] +
1140
v2_rr * normal_direction[2] +
1141
v3_rr * normal_direction[3])
1142
1143
c_f_ll = calc_fast_wavespeed(u_ll, normal_direction, equations)
1144
c_f_rr = calc_fast_wavespeed(u_rr, normal_direction, equations)
1145
v_roe, c_f_roe = calc_fast_wavespeed_roe(u_ll, u_rr, normal_direction, equations)
1146
1147
# Estimate the min/max eigenvalues in the normal direction
1148
λ_min = min(v_normal_ll - c_f_ll, v_roe - c_f_roe)
1149
λ_max = max(v_normal_rr + c_f_rr, v_roe + c_f_roe)
1150
1151
return λ_min, λ_max
1152
end
1153
1154
# Rotate normal vector to x-axis; normal, tangent1 and tangent2 need to be orthonormal
1155
# Called inside `FluxRotated` in `numerical_fluxes.jl` so the directions
1156
# has been normalized prior to this rotation of the state vector
1157
# Note, for ideal GLM-MHD only the velocities and magnetic field variables rotate
1158
@inline function rotate_to_x(u, normal_vector, tangent1, tangent2,
1159
equations::IdealGlmMhdEquations3D)
1160
# Multiply with [ 1 0 0 0 0 0 0 0 0;
1161
# 0 ― normal_vector ― 0 0 0 0 0;
1162
# 0 ― tangent1 ― 0 0 0 0 0;
1163
# 0 ― tangent2 ― 0 0 0 0 0;
1164
# 0 0 0 0 1 0 0 0 0;
1165
# 0 0 0 0 0 ― normal_vector ― 0;
1166
# 0 0 0 0 0 ― tangent1 ― 0;
1167
# 0 0 0 0 0 ― tangent2 ― 0;
1168
# 0 0 0 0 0 0 0 0 1 ]
1169
return SVector(u[1],
1170
normal_vector[1] * u[2] + normal_vector[2] * u[3] +
1171
normal_vector[3] * u[4],
1172
tangent1[1] * u[2] + tangent1[2] * u[3] + tangent1[3] * u[4],
1173
tangent2[1] * u[2] + tangent2[2] * u[3] + tangent2[3] * u[4],
1174
u[5],
1175
normal_vector[1] * u[6] + normal_vector[2] * u[7] +
1176
normal_vector[3] * u[8],
1177
tangent1[1] * u[6] + tangent1[2] * u[7] + tangent1[3] * u[8],
1178
tangent2[1] * u[6] + tangent2[2] * u[7] + tangent2[3] * u[8],
1179
u[9])
1180
end
1181
1182
# Rotate x-axis to normal vector; normal, tangent1 and tangent2 need to be orthonormal
1183
# Called inside `FluxRotated` in `numerical_fluxes.jl` so the directions
1184
# has been normalized prior to this back-rotation of the state vector
1185
# Note, for ideal GLM-MHD only the velocities and magnetic field variables back-rotate
1186
@inline function rotate_from_x(u, normal_vector, tangent1, tangent2,
1187
equations::IdealGlmMhdEquations3D)
1188
# Multiply with [ 1 0 0 0 0 0 0 0 0;
1189
# 0 | | | 0 0 0 0 0;
1190
# 0 normal_vector tangent1 tangent2 0 0 0 0 0;
1191
# 0 | | | 0 0 0 0 0;
1192
# 0 0 0 0 1 0 0 0 0;
1193
# 0 0 0 0 0 | | | 0;
1194
# 0 0 0 0 0 normal_vector tangent1 tangent2 0;
1195
# 0 0 0 0 0 | | | 0;
1196
# 0 0 0 0 0 0 0 0 1 ]
1197
return SVector(u[1],
1198
normal_vector[1] * u[2] + tangent1[1] * u[3] + tangent2[1] * u[4],
1199
normal_vector[2] * u[2] + tangent1[2] * u[3] + tangent2[2] * u[4],
1200
normal_vector[3] * u[2] + tangent1[3] * u[3] + tangent2[3] * u[4],
1201
u[5],
1202
normal_vector[1] * u[6] + tangent1[1] * u[7] + tangent2[1] * u[8],
1203
normal_vector[2] * u[6] + tangent1[2] * u[7] + tangent2[2] * u[8],
1204
normal_vector[3] * u[6] + tangent1[3] * u[7] + tangent2[3] * u[8],
1205
u[9])
1206
end
1207
1208
@inline function max_abs_speeds(u, equations::IdealGlmMhdEquations3D)
1209
rho, rho_v1, rho_v2, rho_v3, _ = u
1210
v1 = rho_v1 / rho
1211
v2 = rho_v2 / rho
1212
v3 = rho_v3 / rho
1213
cf_x_direction = calc_fast_wavespeed(u, 1, equations)
1214
cf_y_direction = calc_fast_wavespeed(u, 2, equations)
1215
cf_z_direction = calc_fast_wavespeed(u, 3, equations)
1216
1217
return abs(v1) + cf_x_direction, abs(v2) + cf_y_direction, abs(v3) + cf_z_direction
1218
end
1219
1220
# Convert conservative variables to primitive
1221
@inline function cons2prim(u, equations::IdealGlmMhdEquations3D)
1222
rho, rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = u
1223
1224
v1 = rho_v1 / rho
1225
v2 = rho_v2 / rho
1226
v3 = rho_v3 / rho
1227
p = (equations.gamma - 1) * (rho_e_total -
1228
0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3
1229
+ B1 * B1 + B2 * B2 + B3 * B3
1230
+ psi * psi))
1231
1232
return SVector(rho, v1, v2, v3, p, B1, B2, B3, psi)
1233
end
1234
1235
# Convert conservative variables to entropy
1236
@inline function cons2entropy(u, equations::IdealGlmMhdEquations3D)
1237
rho, rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = u
1238
v1 = rho_v1 / rho
1239
v2 = rho_v2 / rho
1240
v3 = rho_v3 / rho
1241
v_square = v1^2 + v2^2 + v3^2
1242
p = (equations.gamma - 1) *
1243
(rho_e_total - 0.5f0 * rho * v_square - 0.5f0 * (B1^2 + B2^2 + B3^2) -
1244
0.5f0 * psi^2)
1245
s = log(p) - equations.gamma * log(rho)
1246
rho_p = rho / p
1247
1248
w1 = (equations.gamma - s) * equations.inv_gamma_minus_one -
1249
0.5f0 * rho_p * v_square
1250
w2 = rho_p * v1
1251
w3 = rho_p * v2
1252
w4 = rho_p * v3
1253
w5 = -rho_p
1254
w6 = rho_p * B1
1255
w7 = rho_p * B2
1256
w8 = rho_p * B3
1257
w9 = rho_p * psi
1258
1259
return SVector(w1, w2, w3, w4, w5, w6, w7, w8, w9)
1260
end
1261
1262
# Convert primitive to conservative variables
1263
@inline function prim2cons(prim, equations::IdealGlmMhdEquations3D)
1264
rho, v1, v2, v3, p, B1, B2, B3, psi = prim
1265
1266
rho_v1 = rho * v1
1267
rho_v2 = rho * v2
1268
rho_v3 = rho * v3
1269
rho_e_total = p * equations.inv_gamma_minus_one +
1270
0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3) +
1271
0.5f0 * (B1^2 + B2^2 + B3^2) + 0.5f0 * psi^2
1272
1273
return SVector(rho, rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi)
1274
end
1275
1276
@inline function density(u, equations::IdealGlmMhdEquations3D)
1277
rho = u[1]
1278
return rho
1279
end
1280
1281
@inline function velocity(u, equations::IdealGlmMhdEquations3D)
1282
rho = u[1]
1283
v1 = u[2] / rho
1284
v2 = u[3] / rho
1285
v3 = u[4] / rho
1286
return SVector(v1, v2, v3)
1287
end
1288
1289
@inline function velocity(u, orientation::Int, equations::IdealGlmMhdEquations3D)
1290
rho = u[1]
1291
v = u[orientation + 1] / rho
1292
return v
1293
end
1294
1295
@doc raw"""
1296
pressure(u, equations::IdealGlmMhdEquations3D)
1297
1298
Computes the pressure for an ideal equation of state with
1299
isentropic exponent/adiabatic index ``\gamma`` from the conserved variables `u`.
1300
```math
1301
\begin{aligned}
1302
p &= (\gamma - 1) \left( \rho e_{\text{total}} - \rho e_{\text{kinetic}} - \rho e_{\text{magnetic}} - \frac{1}{2} \psi^2 \right) \\
1303
&= (\gamma - 1) \left( \rho e - \frac{1}{2}
1304
\left[\rho \Vert v \Vert_2^2 + \Vert B \Vert_2^2 + \psi^2 \right] \right)
1305
\end{aligned}
1306
```
1307
"""
1308
@inline function pressure(u, equations::IdealGlmMhdEquations3D)
1309
rho, rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = u
1310
p = (equations.gamma - 1) * (rho_e_total -
1311
0.5f0 *
1312
((rho_v1^2 + rho_v2^2 + rho_v3^2) / rho +
1313
(B1^2 + B2^2 + B3^2) + psi^2))
1314
return p
1315
end
1316
1317
# Transformation from conservative variables u to d(p)/d(u)
1318
@inline function gradient_conservative(::typeof(pressure),
1319
u, equations::IdealGlmMhdEquations3D)
1320
rho, rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = u
1321
1322
v1 = rho_v1 / rho
1323
v2 = rho_v2 / rho
1324
v3 = rho_v3 / rho
1325
v_square = v1^2 + v2^2 + v3^2
1326
1327
return (equations.gamma - 1) *
1328
SVector(0.5f0 * v_square, -v1, -v2, -v3, 1, -B1, -B2, -B3, -psi)
1329
end
1330
1331
@inline function density_pressure(u, equations::IdealGlmMhdEquations3D)
1332
rho, rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = u
1333
rho_times_p = (equations.gamma - 1) * (rho * rho_e_total -
1334
0.5f0 * (rho_v1^2 + rho_v2^2 + rho_v3^2 +
1335
rho * (B1^2 + B2^2 + B3^2 + psi^2)))
1336
return rho_times_p
1337
end
1338
1339
# Compute the fastest wave speed for ideal MHD equations: c_f, the fast magnetoacoustic eigenvalue
1340
@inline function calc_fast_wavespeed(cons, orientation::Integer,
1341
equations::IdealGlmMhdEquations3D)
1342
rho, rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = cons
1343
v1 = rho_v1 / rho
1344
v2 = rho_v2 / rho
1345
v3 = rho_v3 / rho
1346
kin_en = 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3)
1347
mag_en = 0.5f0 * (B1 * B1 + B2 * B2 + B3 * B3)
1348
p = (equations.gamma - 1) * (rho_e_total - kin_en - mag_en - 0.5f0 * psi^2)
1349
a_square = equations.gamma * p / rho
1350
sqrt_rho = sqrt(rho)
1351
b1 = B1 / sqrt_rho
1352
b2 = B2 / sqrt_rho
1353
b3 = B3 / sqrt_rho
1354
b_square = b1 * b1 + b2 * b2 + b3 * b3
1355
if orientation == 1 # x-direction
1356
c_f = sqrt(0.5f0 * (a_square + b_square) +
1357
0.5f0 * sqrt((a_square + b_square)^2 - 4 * a_square * b1^2))
1358
elseif orientation == 2 # y-direction
1359
c_f = sqrt(0.5f0 * (a_square + b_square) +
1360
0.5f0 * sqrt((a_square + b_square)^2 - 4 * a_square * b2^2))
1361
else # z-direction
1362
c_f = sqrt(0.5f0 * (a_square + b_square) +
1363
0.5f0 * sqrt((a_square + b_square)^2 - 4 * a_square * b3^2))
1364
end
1365
return c_f
1366
end
1367
1368
@inline function calc_fast_wavespeed(cons, normal_direction::AbstractVector,
1369
equations::IdealGlmMhdEquations3D)
1370
rho, rho_v1, rho_v2, rho_v3, rho_e_total, B1, B2, B3, psi = cons
1371
v1 = rho_v1 / rho
1372
v2 = rho_v2 / rho
1373
v3 = rho_v3 / rho
1374
kin_en = 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3)
1375
mag_en = 0.5f0 * (B1 * B1 + B2 * B2 + B3 * B3)
1376
p = (equations.gamma - 1) * (rho_e_total - kin_en - mag_en - 0.5f0 * psi^2)
1377
a_square = equations.gamma * p / rho
1378
sqrt_rho = sqrt(rho)
1379
b1 = B1 / sqrt_rho
1380
b2 = B2 / sqrt_rho
1381
b3 = B3 / sqrt_rho
1382
b_square = b1 * b1 + b2 * b2 + b3 * b3
1383
norm_squared = (normal_direction[1] * normal_direction[1] +
1384
normal_direction[2] * normal_direction[2] +
1385
normal_direction[3] * normal_direction[3])
1386
b_dot_n_squared = (b1 * normal_direction[1] +
1387
b2 * normal_direction[2] +
1388
b3 * normal_direction[3])^2 / norm_squared
1389
1390
c_f = sqrt((0.5f0 * (a_square + b_square) +
1391
0.5f0 * sqrt((a_square + b_square)^2 - 4 * a_square * b_dot_n_squared)) *
1392
norm_squared)
1393
return c_f
1394
end
1395
1396
"""
1397
calc_fast_wavespeed_roe(u_ll, u_rr, orientation_or_normal_direction, equations::IdealGlmMhdEquations3D)
1398
1399
Compute the fast magnetoacoustic wave speed using Roe averages as given by
1400
- Cargo and Gallice (1997)
1401
Roe Matrices for Ideal MHD and Systematic Construction
1402
of Roe Matrices for Systems of Conservation Laws
1403
[DOI: 10.1006/jcph.1997.5773](https://doi.org/10.1006/jcph.1997.5773)
1404
"""
1405
@inline function calc_fast_wavespeed_roe(u_ll, u_rr, orientation::Integer,
1406
equations::IdealGlmMhdEquations3D)
1407
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll
1408
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr
1409
1410
# Calculate primitive variables
1411
v1_ll = rho_v1_ll / rho_ll
1412
v2_ll = rho_v2_ll / rho_ll
1413
v3_ll = rho_v3_ll / rho_ll
1414
kin_en_ll = 0.5f0 * (rho_v1_ll * v1_ll + rho_v2_ll * v2_ll + rho_v3_ll * v3_ll)
1415
mag_norm_ll = B1_ll * B1_ll + B2_ll * B2_ll + B3_ll * B3_ll
1416
p_ll = (equations.gamma - 1) *
1417
(rho_e_total_ll - kin_en_ll - 0.5f0 * mag_norm_ll - 0.5f0 * psi_ll^2)
1418
1419
v1_rr = rho_v1_rr / rho_rr
1420
v2_rr = rho_v2_rr / rho_rr
1421
v3_rr = rho_v3_rr / rho_rr
1422
kin_en_rr = 0.5f0 * (rho_v1_rr * v1_rr + rho_v2_rr * v2_rr + rho_v3_rr * v3_rr)
1423
mag_norm_rr = B1_rr * B1_rr + B2_rr * B2_rr + B3_rr * B3_rr
1424
p_rr = (equations.gamma - 1) *
1425
(rho_e_total_rr - kin_en_rr - 0.5f0 * mag_norm_rr - 0.5f0 * psi_rr^2)
1426
1427
# compute total pressure which is thermal + magnetic pressures
1428
p_total_ll = p_ll + 0.5f0 * mag_norm_ll
1429
p_total_rr = p_rr + 0.5f0 * mag_norm_rr
1430
1431
# compute the Roe density averages
1432
sqrt_rho_ll = sqrt(rho_ll)
1433
sqrt_rho_rr = sqrt(rho_rr)
1434
inv_sqrt_rho_add = 1 / (sqrt_rho_ll + sqrt_rho_rr)
1435
inv_sqrt_rho_prod = 1 / (sqrt_rho_ll * sqrt_rho_rr)
1436
rho_ll_roe = sqrt_rho_ll * inv_sqrt_rho_add
1437
rho_rr_roe = sqrt_rho_rr * inv_sqrt_rho_add
1438
# Roe averages
1439
# velocities and magnetic fields
1440
v1_roe = v1_ll * rho_ll_roe + v1_rr * rho_rr_roe
1441
v2_roe = v2_ll * rho_ll_roe + v2_rr * rho_rr_roe
1442
v3_roe = v3_ll * rho_ll_roe + v3_rr * rho_rr_roe
1443
B1_roe = B1_ll * rho_ll_roe + B1_rr * rho_rr_roe
1444
B2_roe = B2_ll * rho_ll_roe + B2_rr * rho_rr_roe
1445
B3_roe = B3_ll * rho_ll_roe + B3_rr * rho_rr_roe
1446
# enthalpy
1447
H_ll = (rho_e_total_ll + p_total_ll) / rho_ll
1448
H_rr = (rho_e_total_rr + p_total_rr) / rho_rr
1449
H_roe = H_ll * rho_ll_roe + H_rr * rho_rr_roe
1450
# temporary variable see equation (4.12) in Cargo and Gallice
1451
X = 0.5f0 * ((B1_ll - B1_rr)^2 + (B2_ll - B2_rr)^2 + (B3_ll - B3_rr)^2) *
1452
inv_sqrt_rho_add^2
1453
# averaged components needed to compute c_f, the fast magnetoacoustic wave speed
1454
b_square_roe = (B1_roe^2 + B2_roe^2 + B3_roe^2) * inv_sqrt_rho_prod # scaled magnectic sum
1455
a_square_roe = ((2 - equations.gamma) * X +
1456
(equations.gamma - 1) *
1457
(H_roe - 0.5f0 * (v1_roe^2 + v2_roe^2 + v3_roe^2) -
1458
b_square_roe)) # acoustic speed
1459
# finally compute the average wave speed and set the output velocity (depends on orientation)
1460
if orientation == 1 # x-direction
1461
c_a_roe = B1_roe^2 * inv_sqrt_rho_prod # (squared) Alfvén wave speed
1462
a_star_roe = sqrt((a_square_roe + b_square_roe)^2 -
1463
4 * a_square_roe * c_a_roe)
1464
c_f_roe = sqrt(0.5f0 * (a_square_roe + b_square_roe + a_star_roe))
1465
vel_out_roe = v1_roe
1466
elseif orientation == 2 # y-direction
1467
c_a_roe = B2_roe^2 * inv_sqrt_rho_prod # (squared) Alfvén wave speed
1468
a_star_roe = sqrt((a_square_roe + b_square_roe)^2 -
1469
4 * a_square_roe * c_a_roe)
1470
c_f_roe = sqrt(0.5f0 * (a_square_roe + b_square_roe + a_star_roe))
1471
vel_out_roe = v2_roe
1472
else # z-direction
1473
c_a_roe = B3_roe^2 * inv_sqrt_rho_prod # (squared) Alfvén wave speed
1474
a_star_roe = sqrt((a_square_roe + b_square_roe)^2 -
1475
4 * a_square_roe * c_a_roe)
1476
c_f_roe = sqrt(0.5f0 * (a_square_roe + b_square_roe + a_star_roe))
1477
vel_out_roe = v3_roe
1478
end
1479
1480
return vel_out_roe, c_f_roe
1481
end
1482
1483
@inline function calc_fast_wavespeed_roe(u_ll, u_rr, normal_direction::AbstractVector,
1484
equations::IdealGlmMhdEquations3D)
1485
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll, B1_ll, B2_ll, B3_ll, psi_ll = u_ll
1486
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr, B1_rr, B2_rr, B3_rr, psi_rr = u_rr
1487
1488
# Calculate primitive variables
1489
v1_ll = rho_v1_ll / rho_ll
1490
v2_ll = rho_v2_ll / rho_ll
1491
v3_ll = rho_v3_ll / rho_ll
1492
kin_en_ll = 0.5f0 * (rho_v1_ll * v1_ll + rho_v2_ll * v2_ll + rho_v3_ll * v3_ll)
1493
mag_norm_ll = B1_ll * B1_ll + B2_ll * B2_ll + B3_ll * B3_ll
1494
p_ll = (equations.gamma - 1) *
1495
(rho_e_total_ll - kin_en_ll - 0.5f0 * mag_norm_ll - 0.5f0 * psi_ll^2)
1496
1497
v1_rr = rho_v1_rr / rho_rr
1498
v2_rr = rho_v2_rr / rho_rr
1499
v3_rr = rho_v3_rr / rho_rr
1500
kin_en_rr = 0.5f0 * (rho_v1_rr * v1_rr + rho_v2_rr * v2_rr + rho_v3_rr * v3_rr)
1501
mag_norm_rr = B1_rr * B1_rr + B2_rr * B2_rr + B3_rr * B3_rr
1502
p_rr = (equations.gamma - 1) *
1503
(rho_e_total_rr - kin_en_rr - 0.5f0 * mag_norm_rr - 0.5f0 * psi_rr^2)
1504
1505
# compute total pressure which is thermal + magnetic pressures
1506
p_total_ll = p_ll + 0.5f0 * mag_norm_ll
1507
p_total_rr = p_rr + 0.5f0 * mag_norm_rr
1508
1509
# compute the Roe density averages
1510
sqrt_rho_ll = sqrt(rho_ll)
1511
sqrt_rho_rr = sqrt(rho_rr)
1512
inv_sqrt_rho_add = 1 / (sqrt_rho_ll + sqrt_rho_rr)
1513
inv_sqrt_rho_prod = 1 / (sqrt_rho_ll * sqrt_rho_rr)
1514
rho_ll_roe = sqrt_rho_ll * inv_sqrt_rho_add
1515
rho_rr_roe = sqrt_rho_rr * inv_sqrt_rho_add
1516
# Roe averages
1517
# velocities and magnetic fields
1518
v1_roe = v1_ll * rho_ll_roe + v1_rr * rho_rr_roe
1519
v2_roe = v2_ll * rho_ll_roe + v2_rr * rho_rr_roe
1520
v3_roe = v3_ll * rho_ll_roe + v3_rr * rho_rr_roe
1521
B1_roe = B1_ll * rho_ll_roe + B1_rr * rho_rr_roe
1522
B2_roe = B2_ll * rho_ll_roe + B2_rr * rho_rr_roe
1523
B3_roe = B3_ll * rho_ll_roe + B3_rr * rho_rr_roe
1524
# enthalpy
1525
H_ll = (rho_e_total_ll + p_total_ll) / rho_ll
1526
H_rr = (rho_e_total_rr + p_total_rr) / rho_rr
1527
H_roe = H_ll * rho_ll_roe + H_rr * rho_rr_roe
1528
# temporary variable see equation (4.12) in Cargo and Gallice
1529
X = 0.5f0 * ((B1_ll - B1_rr)^2 + (B2_ll - B2_rr)^2 + (B3_ll - B3_rr)^2) *
1530
inv_sqrt_rho_add^2
1531
# averaged components needed to compute c_f, the fast magnetoacoustic wave speed
1532
b_square_roe = (B1_roe^2 + B2_roe^2 + B3_roe^2) * inv_sqrt_rho_prod # scaled magnectic sum
1533
a_square_roe = ((2 - equations.gamma) * X +
1534
(equations.gamma - 1) *
1535
(H_roe - 0.5f0 * (v1_roe^2 + v2_roe^2 + v3_roe^2) -
1536
b_square_roe)) # acoustic speed
1537
1538
# finally compute the average wave speed and set the output velocity (depends on orientation)
1539
norm_squared = (normal_direction[1] * normal_direction[1] +
1540
normal_direction[2] * normal_direction[2] +
1541
normal_direction[3] * normal_direction[3])
1542
B_roe_dot_n_squared = (B1_roe * normal_direction[1] +
1543
B2_roe * normal_direction[2] +
1544
B3_roe * normal_direction[3])^2 / norm_squared
1545
1546
c_a_roe = B_roe_dot_n_squared * inv_sqrt_rho_prod # (squared) Alfvén wave speed
1547
a_star_roe = sqrt((a_square_roe + b_square_roe)^2 - 4 * a_square_roe * c_a_roe)
1548
c_f_roe = sqrt(0.5f0 * (a_square_roe + b_square_roe + a_star_roe) * norm_squared)
1549
vel_out_roe = (v1_roe * normal_direction[1] +
1550
v2_roe * normal_direction[2] +
1551
v3_roe * normal_direction[3])
1552
1553
return vel_out_roe, c_f_roe
1554
end
1555
1556
# Calculate thermodynamic entropy for a conservative state `cons`
1557
@inline function entropy_thermodynamic(cons, equations::IdealGlmMhdEquations3D)
1558
# Pressure
1559
p = (equations.gamma - 1) *
1560
(cons[5] - 0.5f0 * (cons[2]^2 + cons[3]^2 + cons[4]^2) / cons[1]
1561
-
1562
0.5f0 * (cons[6]^2 + cons[7]^2 + cons[8]^2)
1563
-
1564
0.5f0 * cons[9]^2)
1565
1566
# Thermodynamic entropy
1567
s = log(p) - equations.gamma * log(cons[1])
1568
1569
return s
1570
end
1571
1572
# Calculate mathematical entropy for a conservative state `cons`
1573
@inline function entropy_math(cons, equations::IdealGlmMhdEquations3D)
1574
S = -entropy_thermodynamic(cons, equations) * cons[1] *
1575
equations.inv_gamma_minus_one
1576
1577
return S
1578
end
1579
1580
# Default entropy is the mathematical entropy
1581
@inline entropy(cons, equations::IdealGlmMhdEquations3D) = entropy_math(cons, equations)
1582
1583
# Calculate total energy for a conservative state `cons`
1584
@inline energy_total(cons, ::IdealGlmMhdEquations3D) = cons[5]
1585
1586
# Calculate kinetic energy for a conservative state `cons`
1587
@inline function energy_kinetic(cons, equations::IdealGlmMhdEquations3D)
1588
return 0.5f0 * (cons[2]^2 + cons[3]^2 + cons[4]^2) / cons[1]
1589
end
1590
1591
# Calculate the magnetic energy for a conservative state `cons`.
1592
# OBS! For non-dinmensional form of the ideal MHD magnetic pressure ≡ magnetic energy
1593
@inline function energy_magnetic(cons, ::IdealGlmMhdEquations3D)
1594
return 0.5f0 * (cons[6]^2 + cons[7]^2 + cons[8]^2)
1595
end
1596
1597
# Calculate internal energy for a conservative state `cons`
1598
@inline function energy_internal(cons, equations::IdealGlmMhdEquations3D)
1599
return (energy_total(cons, equations)
1600
-
1601
energy_kinetic(cons, equations)
1602
-
1603
energy_magnetic(cons, equations)
1604
-
1605
cons[9]^2 / 2)
1606
end
1607
1608
# State validation for Newton-bisection method of subcell IDP limiting
1609
@inline function Base.isvalid(u, equations::IdealGlmMhdEquations3D)
1610
if u[1] <= 0 || pressure(u, equations) <= 0
1611
return false
1612
end
1613
return true
1614
end
1615
1616
# Calculate the cross helicity (\vec{v}⋅\vec{B}) for a conservative state `cons`
1617
@inline function cross_helicity(cons, ::IdealGlmMhdEquations3D)
1618
return (cons[2] * cons[6] + cons[3] * cons[7] + cons[4] * cons[8]) / cons[1]
1619
end
1620
end # @muladd
1621
1622