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