Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/equations/compressible_euler_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
CompressibleEulerEquations3D(gamma)
10
11
The compressible Euler equations
12
```math
13
\frac{\partial}{\partial t}
14
\begin{pmatrix}
15
\rho \\ \rho v_1 \\ \rho v_2 \\ \rho v_3 \\ \rho e_{\text{total}}
16
\end{pmatrix}
17
+
18
\frac{\partial}{\partial x}
19
\begin{pmatrix}
20
\rho v_1 \\ \rho v_1^2 + p \\ \rho v_1 v_2 \\ \rho v_1 v_3 \\ ( \rho e_{\text{total}} +p) v_1
21
\end{pmatrix}
22
+
23
\frac{\partial}{\partial y}
24
\begin{pmatrix}
25
\rho v_2 \\ \rho v_1 v_2 \\ \rho v_2^2 + p \\ \rho v_1 v_3 \\ ( \rho e_{\text{total}} +p) v_2
26
\end{pmatrix}
27
+
28
\frac{\partial}{\partial z}
29
\begin{pmatrix}
30
\rho v_3 \\ \rho v_1 v_3 \\ \rho v_2 v_3 \\ \rho v_3^2 + p \\ ( \rho e_{\text{total}} +p) v_3
31
\end{pmatrix}
32
=
33
\begin{pmatrix}
34
0 \\ 0 \\ 0 \\ 0 \\ 0
35
\end{pmatrix}
36
```
37
for an ideal gas with ratio of specific heats `gamma`
38
in three space dimensions.
39
Here, ``\rho`` is the density, ``v_1``, ``v_2``, ``v_3`` the velocities, ``e_{\text{total}}`` the specific total energy, and
40
```math
41
p = (\gamma - 1) \left( \rho e_{\text{total}} - \frac{1}{2} \rho (v_1^2+v_2^2+v_3^2) \right)
42
```
43
the pressure.
44
"""
45
struct CompressibleEulerEquations3D{RealT <: Real} <:
46
AbstractCompressibleEulerEquations{3, 5}
47
gamma::RealT # ratio of specific heats
48
inv_gamma_minus_one::RealT # = inv(gamma - 1); can be used to write slow divisions as fast multiplications
49
50
function CompressibleEulerEquations3D(gamma)
51
γ, inv_gamma_minus_one = promote(gamma, inv(gamma - 1))
52
return new{typeof(γ)}(γ, inv_gamma_minus_one)
53
end
54
end
55
56
function varnames(::typeof(cons2cons), ::CompressibleEulerEquations3D)
57
return ("rho", "rho_v1", "rho_v2", "rho_v3", "rho_e_total")
58
end
59
function varnames(::typeof(cons2prim), ::CompressibleEulerEquations3D)
60
return ("rho", "v1", "v2", "v3", "p")
61
end
62
63
# Set initial conditions at physical location `x` for time `t`
64
"""
65
initial_condition_constant(x, t, equations::CompressibleEulerEquations3D)
66
67
A constant initial condition to test free-stream preservation.
68
"""
69
function initial_condition_constant(x, t, equations::CompressibleEulerEquations3D)
70
RealT = eltype(x)
71
rho = 1
72
rho_v1 = convert(RealT, 0.1)
73
rho_v2 = convert(RealT, -0.2)
74
rho_v3 = convert(RealT, 0.7)
75
rho_e_total = 10
76
return SVector(rho, rho_v1, rho_v2, rho_v3, rho_e_total)
77
end
78
79
"""
80
initial_condition_convergence_test(x, t, equations::CompressibleEulerEquations3D)
81
82
A smooth initial condition used for convergence tests in combination with
83
[`source_terms_convergence_test`](@ref).
84
"""
85
function initial_condition_convergence_test(x, t,
86
equations::CompressibleEulerEquations3D)
87
RealT = eltype(x)
88
c = 2
89
A = convert(RealT, 0.1)
90
L = 2
91
f = 1.0f0 / L
92
ω = 2 * convert(RealT, pi) * f
93
ini = c + A * sin(ω * (x[1] + x[2] + x[3] - t))
94
95
rho = ini
96
rho_v1 = ini
97
rho_v2 = ini
98
rho_v3 = ini
99
rho_e_total = ini^2
100
101
return SVector(rho, rho_v1, rho_v2, rho_v3, rho_e_total)
102
end
103
104
"""
105
source_terms_convergence_test(u, x, t, equations::CompressibleEulerEquations3D)
106
107
Source terms used for convergence tests in combination with
108
[`initial_condition_convergence_test`](@ref).
109
110
References for the method of manufactured solutions (MMS):
111
- Kambiz Salari and Patrick Knupp (2000)
112
Code Verification by the Method of Manufactured Solutions
113
[DOI: 10.2172/759450](https://doi.org/10.2172/759450)
114
- Patrick J. Roache (2002)
115
Code Verification by the Method of Manufactured Solutions
116
[DOI: 10.1115/1.1436090](https://doi.org/10.1115/1.1436090)
117
"""
118
@inline function source_terms_convergence_test(u, x, t,
119
equations::CompressibleEulerEquations3D)
120
# Same settings as in `initial_condition`
121
RealT = eltype(u)
122
c = 2
123
A = convert(RealT, 0.1)
124
L = 2
125
f = 1.0f0 / L
126
ω = 2 * convert(RealT, pi) * f
127
γ = equations.gamma
128
129
x1, x2, x3 = x
130
si, co = sincos(ω * (x1 + x2 + x3 - t))
131
rho = c + A * si
132
rho_x = ω * A * co
133
# Note that d/dt rho = -d/dx rho = -d/dy rho = - d/dz rho.
134
135
tmp = (2 * rho - 1.5f0) * (γ - 1)
136
137
du1 = 2 * rho_x
138
du2 = rho_x * (2 + tmp)
139
du3 = du2
140
du4 = du2
141
du5 = rho_x * (4 * rho + 3 * tmp)
142
143
return SVector(du1, du2, du3, du4, du5)
144
end
145
146
"""
147
initial_condition_weak_blast_wave(x, t, equations::CompressibleEulerEquations3D)
148
149
A weak blast wave taken from
150
- Sebastian Hennemann, Gregor J. Gassner (2020)
151
A provably entropy stable subcell shock capturing approach for high order split form DG
152
[arXiv: 2008.12044](https://arxiv.org/abs/2008.12044)
153
"""
154
function initial_condition_weak_blast_wave(x, t,
155
equations::CompressibleEulerEquations3D)
156
# From Hennemann & Gassner JCP paper 2020 (Sec. 6.3)
157
# Set up spherical coordinates
158
RealT = eltype(x)
159
inicenter = (0, 0, 0)
160
x_norm = x[1] - inicenter[1]
161
y_norm = x[2] - inicenter[2]
162
z_norm = x[3] - inicenter[3]
163
r = sqrt(x_norm^2 + y_norm^2 + z_norm^2)
164
phi = atan(y_norm, x_norm)
165
theta = iszero(r) ? zero(RealT) : acos(z_norm / r)
166
167
# Calculate primitive variables
168
rho = r > 0.5f0 ? one(RealT) : convert(RealT, 1.1691)
169
v1 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos(phi) * sin(theta)
170
v2 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * sin(phi) * sin(theta)
171
v3 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos(theta)
172
p = r > 0.5f0 ? one(RealT) : convert(RealT, 1.245)
173
174
return prim2cons(SVector(rho, v1, v2, v3, p), equations)
175
end
176
177
"""
178
initial_condition_eoc_test_coupled_euler_gravity(x, t, equations::CompressibleEulerEquations3D)
179
180
Setup used for convergence tests of the Euler equations with self-gravity used in
181
- Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020)
182
A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics
183
[arXiv: 2008.10593](https://arxiv.org/abs/2008.10593)
184
in combination with [`source_terms_eoc_test_coupled_euler_gravity`](@ref)
185
or [`source_terms_eoc_test_euler`](@ref).
186
"""
187
function initial_condition_eoc_test_coupled_euler_gravity(x, t,
188
equations::CompressibleEulerEquations3D)
189
# OBS! this assumes that γ = 2 other manufactured source terms are incorrect
190
if equations.gamma != 2
191
error("adiabatic constant must be 2 for the coupling convergence test")
192
end
193
RealT = eltype(x)
194
c = 2
195
A = convert(RealT, 0.1)
196
ini = c + A * sinpi(x[1] + x[2] + x[3] - t)
197
G = 1 # gravitational constant
198
199
rho = ini
200
v1 = 1
201
v2 = 1
202
v3 = 1
203
p = ini^2 * G * 2 / (3 * convert(RealT, pi)) # "3" is the number of spatial dimensions
204
205
return prim2cons(SVector(rho, v1, v2, v3, p), equations)
206
end
207
208
"""
209
source_terms_eoc_test_coupled_euler_gravity(u, x, t, equations::CompressibleEulerEquations3D)
210
211
Setup used for convergence tests of the Euler equations with self-gravity used in
212
- Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020)
213
A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics
214
[arXiv: 2008.10593](https://arxiv.org/abs/2008.10593)
215
in combination with [`initial_condition_eoc_test_coupled_euler_gravity`](@ref).
216
"""
217
@inline function source_terms_eoc_test_coupled_euler_gravity(u, x, t,
218
equations::CompressibleEulerEquations3D)
219
# Same settings as in `initial_condition_eoc_test_coupled_euler_gravity`
220
RealT = eltype(u)
221
c = 2
222
A = convert(RealT, 0.1)
223
G = 1 # gravitational constant, must match coupling solver
224
C_grav = -4 * G / (3 * convert(RealT, pi)) # "3" is the number of spatial dimensions # 2D: -2.0*G/pi
225
226
x1, x2, x3 = x
227
si, co = sincospi(x1 + x2 + x3 - t)
228
rhox = A * convert(RealT, pi) * co
229
rho = c + A * si
230
231
# In "2 * rhox", the "2" is "number of spatial dimensions minus one"
232
du1 = 2 * rhox
233
du2 = 2 * rhox
234
du3 = 2 * rhox
235
du4 = 2 * rhox
236
du5 = 2 * rhox * (1.5f0 - C_grav * rho) # "3" in "3/2" is the number of spatial dimensions
237
238
return SVector(du1, du2, du3, du4, du5)
239
end
240
241
"""
242
source_terms_eoc_test_euler(u, x, t, equations::CompressibleEulerEquations3D)
243
244
Setup used for convergence tests of the Euler equations with self-gravity used in
245
- Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020)
246
A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics
247
[arXiv: 2008.10593](https://arxiv.org/abs/2008.10593)
248
in combination with [`initial_condition_eoc_test_coupled_euler_gravity`](@ref).
249
250
!!! note
251
This method is to be used for testing pure Euler simulations with analytic self-gravity.
252
If you intend to do coupled Euler-gravity simulations, you need to use
253
[`source_terms_eoc_test_coupled_euler_gravity`](@ref) instead.
254
"""
255
function source_terms_eoc_test_euler(u, x, t, equations::CompressibleEulerEquations3D)
256
# Same settings as in `initial_condition_eoc_test_coupled_euler_gravity`
257
RealT = eltype(u)
258
c = 2
259
A = convert(RealT, 0.1)
260
G = 1
261
C_grav = -4 * G / (3 * convert(RealT, pi)) # "3" is the number of spatial dimensions
262
263
x1, x2, x3 = x
264
si, co = sincospi(x1 + x2 + x3 - t)
265
rhox = A * convert(RealT, pi) * co
266
rho = c + A * si
267
268
du1 = rhox * 2
269
du2 = rhox * (2 - C_grav * rho)
270
du3 = rhox * (2 - C_grav * rho)
271
du4 = rhox * (2 - C_grav * rho)
272
du5 = rhox * (3 - 5 * C_grav * rho)
273
274
return SVector(du1, du2, du3, du4, du5)
275
end
276
277
"""
278
boundary_condition_slip_wall(u_inner, normal_direction, x, t, surface_flux_function,
279
equations::CompressibleEulerEquations3D)
280
281
Determine the boundary numerical surface flux for a slip wall condition.
282
Imposes a zero normal velocity at the wall.
283
Density is taken from the internal solution state and pressure is computed as an
284
exact solution of a 1D Riemann problem. Further details about this boundary state
285
are available in the paper:
286
- J. J. W. van der Vegt and H. van der Ven (2002)
287
Slip flow boundary conditions in discontinuous Galerkin discretizations of
288
the Euler equations of gas dynamics
289
[PDF](https://reports.nlr.nl/bitstream/handle/10921/692/TP-2002-300.pdf?sequence=1)
290
291
Details about the 1D pressure Riemann solution can be found in Section 6.3.3 of the book
292
- Eleuterio F. Toro (2009)
293
Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction
294
3rd edition
295
[DOI: 10.1007/b79761](https://doi.org/10.1007/b79761)
296
297
Should be used together with [`P4estMesh`](@ref) or [`T8codeMesh`](@ref).
298
"""
299
@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector,
300
x, t,
301
surface_flux_function,
302
equations::CompressibleEulerEquations3D)
303
norm_ = norm(normal_direction)
304
# Normalize the vector without using `normalize` since we need to multiply by the `norm_` later
305
normal = normal_direction / norm_
306
307
# Some vector that can't be identical to normal_vector (unless normal_vector == 0)
308
tangent1 = SVector(normal_direction[2], normal_direction[3], -normal_direction[1])
309
# Orthogonal projection
310
tangent1 -= dot(normal, tangent1) * normal
311
tangent1 = normalize(tangent1)
312
313
# Third orthogonal vector
314
tangent2 = normalize(cross(normal_direction, tangent1))
315
316
# rotate the internal solution state
317
u_local = rotate_to_x(u_inner, normal, tangent1, tangent2, equations)
318
319
# compute the primitive variables
320
rho_local, v_normal, v_tangent1, v_tangent2, p_local = cons2prim(u_local, equations)
321
322
# Get the solution of the pressure Riemann problem
323
# See Section 6.3.3 of
324
# Eleuterio F. Toro (2009)
325
# Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction
326
# [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761)
327
if v_normal <= 0
328
sound_speed = sqrt(equations.gamma * p_local / rho_local) # local sound speed
329
p_star = p_local *
330
(1 + 0.5f0 * (equations.gamma - 1) * v_normal / sound_speed)^(2 *
331
equations.gamma *
332
equations.inv_gamma_minus_one)
333
else # v_normal > 0
334
A = 2 / ((equations.gamma + 1) * rho_local)
335
B = p_local * (equations.gamma - 1) / (equations.gamma + 1)
336
p_star = p_local +
337
0.5f0 * v_normal / A *
338
(v_normal + sqrt(v_normal^2 + 4 * A * (p_local + B)))
339
end
340
341
# For the slip wall we directly set the flux as the normal velocity is zero
342
return SVector(0,
343
p_star * normal[1],
344
p_star * normal[2],
345
p_star * normal[3],
346
0) * norm_
347
end
348
349
"""
350
boundary_condition_slip_wall(u_inner, orientation, direction, x, t,
351
surface_flux_function, equations::CompressibleEulerEquations3D)
352
353
Should be used together with [`TreeMesh`](@ref).
354
"""
355
@inline function boundary_condition_slip_wall(u_inner, orientation,
356
direction, x, t,
357
surface_flux_function,
358
equations::CompressibleEulerEquations3D)
359
# get the appropriate normal vector from the orientation
360
RealT = eltype(u_inner)
361
if orientation == 1
362
normal_direction = SVector(one(RealT), zero(RealT), zero(RealT))
363
elseif orientation == 2
364
normal_direction = SVector(zero(RealT), one(RealT), zero(RealT))
365
else # orientation == 3
366
normal_direction = SVector(zero(RealT), zero(RealT), one(RealT))
367
end
368
369
# compute and return the flux using `boundary_condition_slip_wall` routine below
370
return boundary_condition_slip_wall(u_inner, normal_direction, direction,
371
x, t, surface_flux_function, equations)
372
end
373
374
"""
375
boundary_condition_slip_wall(u_inner, normal_direction, direction, x, t,
376
surface_flux_function, equations::CompressibleEulerEquations3D)
377
378
Should be used together with [`StructuredMesh`](@ref).
379
"""
380
@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector,
381
direction, x, t,
382
surface_flux_function,
383
equations::CompressibleEulerEquations3D)
384
# flip sign of normal to make it outward pointing, then flip the sign of the normal flux back
385
# to be inward pointing on the -x, -y, and -z sides due to the orientation convention used by StructuredMesh
386
if isodd(direction)
387
boundary_flux = -boundary_condition_slip_wall(u_inner, -normal_direction,
388
x, t, surface_flux_function,
389
equations)
390
else
391
boundary_flux = boundary_condition_slip_wall(u_inner, normal_direction,
392
x, t, surface_flux_function,
393
equations)
394
end
395
396
return boundary_flux
397
end
398
399
# Calculate 1D flux for a single point
400
@inline function flux(u, orientation::Integer, equations::CompressibleEulerEquations3D)
401
rho, rho_v1, rho_v2, rho_v3, rho_e_total = u
402
v1 = rho_v1 / rho
403
v2 = rho_v2 / rho
404
v3 = rho_v3 / rho
405
p = (equations.gamma - 1) *
406
(rho_e_total - 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3))
407
if orientation == 1
408
f1 = rho_v1
409
f2 = rho_v1 * v1 + p
410
f3 = rho_v1 * v2
411
f4 = rho_v1 * v3
412
f5 = (rho_e_total + p) * v1
413
elseif orientation == 2
414
f1 = rho_v2
415
f2 = rho_v2 * v1
416
f3 = rho_v2 * v2 + p
417
f4 = rho_v2 * v3
418
f5 = (rho_e_total + p) * v2
419
else
420
f1 = rho_v3
421
f2 = rho_v3 * v1
422
f3 = rho_v3 * v2
423
f4 = rho_v3 * v3 + p
424
f5 = (rho_e_total + p) * v3
425
end
426
return SVector(f1, f2, f3, f4, f5)
427
end
428
429
@inline function flux(u, normal_direction::AbstractVector,
430
equations::CompressibleEulerEquations3D)
431
rho_e_total = last(u)
432
rho, v1, v2, v3, p = cons2prim(u, equations)
433
434
v_normal = v1 * normal_direction[1] + v2 * normal_direction[2] +
435
v3 * normal_direction[3]
436
rho_v_normal = rho * v_normal
437
f1 = rho_v_normal
438
f2 = rho_v_normal * v1 + p * normal_direction[1]
439
f3 = rho_v_normal * v2 + p * normal_direction[2]
440
f4 = rho_v_normal * v3 + p * normal_direction[3]
441
f5 = (rho_e_total + p) * v_normal
442
return SVector(f1, f2, f3, f4, f5)
443
end
444
445
"""
446
flux_shima_etal(u_ll, u_rr, orientation_or_normal_direction,
447
equations::CompressibleEulerEquations3D)
448
449
This flux is is a modification of the original kinetic energy preserving two-point flux by
450
- Yuichi Kuya, Kosuke Totani and Soshi Kawai (2018)
451
Kinetic energy and entropy preserving schemes for compressible flows
452
by split convective forms
453
[DOI: 10.1016/j.jcp.2018.08.058](https://doi.org/10.1016/j.jcp.2018.08.058)
454
455
The modification is in the energy flux to guarantee pressure equilibrium and was developed by
456
- Nao Shima, Yuichi Kuya, Yoshiharu Tamaki, Soshi Kawai (JCP 2020)
457
Preventing spurious pressure oscillations in split convective form discretizations for
458
compressible flows
459
[DOI: 10.1016/j.jcp.2020.110060](https://doi.org/10.1016/j.jcp.2020.110060)
460
"""
461
@inline function flux_shima_etal(u_ll, u_rr, orientation::Integer,
462
equations::CompressibleEulerEquations3D)
463
# Unpack left and right state
464
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
465
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
466
467
# Average each factor of products in flux
468
rho_avg = 0.5f0 * (rho_ll + rho_rr)
469
v1_avg = 0.5f0 * (v1_ll + v1_rr)
470
v2_avg = 0.5f0 * (v2_ll + v2_rr)
471
v3_avg = 0.5f0 * (v3_ll + v3_rr)
472
p_avg = 0.5f0 * (p_ll + p_rr)
473
kin_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr)
474
475
# Calculate fluxes depending on orientation
476
if orientation == 1
477
pv1_avg = 0.5f0 * (p_ll * v1_rr + p_rr * v1_ll)
478
f1 = rho_avg * v1_avg
479
f2 = f1 * v1_avg + p_avg
480
f3 = f1 * v2_avg
481
f4 = f1 * v3_avg
482
f5 = p_avg * v1_avg * equations.inv_gamma_minus_one + f1 * kin_avg + pv1_avg
483
elseif orientation == 2
484
pv2_avg = 0.5f0 * (p_ll * v2_rr + p_rr * v2_ll)
485
f1 = rho_avg * v2_avg
486
f2 = f1 * v1_avg
487
f3 = f1 * v2_avg + p_avg
488
f4 = f1 * v3_avg
489
f5 = p_avg * v2_avg * equations.inv_gamma_minus_one + f1 * kin_avg + pv2_avg
490
else
491
pv3_avg = 0.5f0 * (p_ll * v3_rr + p_rr * v3_ll)
492
f1 = rho_avg * v3_avg
493
f2 = f1 * v1_avg
494
f3 = f1 * v2_avg
495
f4 = f1 * v3_avg + p_avg
496
f5 = p_avg * v3_avg * equations.inv_gamma_minus_one + f1 * kin_avg + pv3_avg
497
end
498
499
return SVector(f1, f2, f3, f4, f5)
500
end
501
502
@inline function flux_shima_etal(u_ll, u_rr, normal_direction::AbstractVector,
503
equations::CompressibleEulerEquations3D)
504
# Unpack left and right state
505
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
506
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
507
v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +
508
v3_ll * normal_direction[3]
509
v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +
510
v3_rr * normal_direction[3]
511
512
# Average each factor of products in flux
513
rho_avg = 0.5f0 * (rho_ll + rho_rr)
514
v1_avg = 0.5f0 * (v1_ll + v1_rr)
515
v2_avg = 0.5f0 * (v2_ll + v2_rr)
516
v3_avg = 0.5f0 * (v3_ll + v3_rr)
517
v_dot_n_avg = 0.5f0 * (v_dot_n_ll + v_dot_n_rr)
518
p_avg = 0.5f0 * (p_ll + p_rr)
519
velocity_square_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr)
520
521
# Calculate fluxes depending on normal_direction
522
f1 = rho_avg * v_dot_n_avg
523
f2 = f1 * v1_avg + p_avg * normal_direction[1]
524
f3 = f1 * v2_avg + p_avg * normal_direction[2]
525
f4 = f1 * v3_avg + p_avg * normal_direction[3]
526
f5 = (f1 * velocity_square_avg +
527
p_avg * v_dot_n_avg * equations.inv_gamma_minus_one
528
+ 0.5f0 * (p_ll * v_dot_n_rr + p_rr * v_dot_n_ll))
529
530
return SVector(f1, f2, f3, f4, f5)
531
end
532
533
"""
534
flux_kennedy_gruber(u_ll, u_rr, orientation_or_normal_direction,
535
equations::CompressibleEulerEquations3D)
536
537
Kinetic energy preserving two-point flux by
538
- Kennedy and Gruber (2008)
539
Reduced aliasing formulations of the convective terms within the
540
Navier-Stokes equations for a compressible fluid
541
[DOI: 10.1016/j.jcp.2007.09.020](https://doi.org/10.1016/j.jcp.2007.09.020)
542
"""
543
@inline function flux_kennedy_gruber(u_ll, u_rr, orientation::Integer,
544
equations::CompressibleEulerEquations3D)
545
# Unpack left and right state
546
rho_e_total_ll = last(u_ll)
547
rho_e_total_rr = last(u_rr)
548
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
549
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
550
551
# Average each factor of products in flux
552
rho_avg = 0.5f0 * (rho_ll + rho_rr)
553
v1_avg = 0.5f0 * (v1_ll + v1_rr)
554
v2_avg = 0.5f0 * (v2_ll + v2_rr)
555
v3_avg = 0.5f0 * (v3_ll + v3_rr)
556
p_avg = 0.5f0 * (p_ll + p_rr)
557
e_avg = 0.5f0 * (rho_e_total_ll / rho_ll + rho_e_total_rr / rho_rr)
558
559
# Calculate fluxes depending on orientation
560
if orientation == 1
561
f1 = rho_avg * v1_avg
562
f2 = f1 * v1_avg + p_avg
563
f3 = f1 * v2_avg
564
f4 = f1 * v3_avg
565
f5 = (rho_avg * e_avg + p_avg) * v1_avg
566
elseif orientation == 2
567
f1 = rho_avg * v2_avg
568
f2 = f1 * v1_avg
569
f3 = f1 * v2_avg + p_avg
570
f4 = f1 * v3_avg
571
f5 = (rho_avg * e_avg + p_avg) * v2_avg
572
else
573
f1 = rho_avg * v3_avg
574
f2 = f1 * v1_avg
575
f3 = f1 * v2_avg
576
f4 = f1 * v3_avg + p_avg
577
f5 = (rho_avg * e_avg + p_avg) * v3_avg
578
end
579
580
return SVector(f1, f2, f3, f4, f5)
581
end
582
583
@inline function flux_kennedy_gruber(u_ll, u_rr, normal_direction::AbstractVector,
584
equations::CompressibleEulerEquations3D)
585
# Unpack left and right state
586
rho_e_total_ll = last(u_ll)
587
rho_e_total_rr = last(u_rr)
588
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
589
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
590
591
# Average each factor of products in flux
592
rho_avg = 0.5f0 * (rho_ll + rho_rr)
593
v1_avg = 0.5f0 * (v1_ll + v1_rr)
594
v2_avg = 0.5f0 * (v2_ll + v2_rr)
595
v3_avg = 0.5f0 * (v3_ll + v3_rr)
596
p_avg = 0.5f0 * (p_ll + p_rr)
597
e_avg = 0.5f0 * (rho_e_total_ll / rho_ll + rho_e_total_rr / rho_rr)
598
599
v_dot_n_avg = v1_avg * normal_direction[1] + v2_avg * normal_direction[2] +
600
v3_avg * normal_direction[3]
601
p_avg = 0.5f0 * (p_ll + p_rr)
602
e_avg = 0.5f0 * (rho_e_total_ll / rho_ll + rho_e_total_rr / rho_rr)
603
604
# Calculate fluxes depending on normal_direction
605
f1 = rho_avg * v_dot_n_avg
606
f2 = f1 * v1_avg + p_avg * normal_direction[1]
607
f3 = f1 * v2_avg + p_avg * normal_direction[2]
608
f4 = f1 * v3_avg + p_avg * normal_direction[3]
609
f5 = f1 * e_avg + p_avg * v_dot_n_avg
610
611
return SVector(f1, f2, f3, f4, f5)
612
end
613
614
"""
615
flux_chandrashekar(u_ll, u_rr, orientation_or_normal_direction, equations::CompressibleEulerEquations3D)
616
617
Entropy conserving two-point flux by
618
- Chandrashekar (2013)
619
Kinetic Energy Preserving and Entropy Stable Finite Volume Schemes
620
for Compressible Euler and Navier-Stokes Equations
621
[DOI: 10.4208/cicp.170712.010313a](https://doi.org/10.4208/cicp.170712.010313a)
622
"""
623
@inline function flux_chandrashekar(u_ll, u_rr, orientation::Integer,
624
equations::CompressibleEulerEquations3D)
625
# Unpack left and right state
626
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
627
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
628
629
beta_ll = 0.5f0 * rho_ll / p_ll
630
beta_rr = 0.5f0 * rho_rr / p_rr
631
specific_kin_ll = 0.5f0 * (v1_ll^2 + v2_ll^2 + v3_ll^2)
632
specific_kin_rr = 0.5f0 * (v1_rr^2 + v2_rr^2 + v3_rr^2)
633
634
# Compute the necessary mean values
635
rho_avg = 0.5f0 * (rho_ll + rho_rr)
636
rho_mean = ln_mean(rho_ll, rho_rr)
637
beta_mean = ln_mean(beta_ll, beta_rr)
638
beta_avg = 0.5f0 * (beta_ll + beta_rr)
639
v1_avg = 0.5f0 * (v1_ll + v1_rr)
640
v2_avg = 0.5f0 * (v2_ll + v2_rr)
641
v3_avg = 0.5f0 * (v3_ll + v3_rr)
642
p_mean = 0.5f0 * rho_avg / beta_avg
643
velocity_square_avg = specific_kin_ll + specific_kin_rr
644
645
# Calculate fluxes depending on orientation
646
if orientation == 1
647
f1 = rho_mean * v1_avg
648
f2 = f1 * v1_avg + p_mean
649
f3 = f1 * v2_avg
650
f4 = f1 * v3_avg
651
f5 = f1 * 0.5f0 *
652
(1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) +
653
f2 * v1_avg + f3 * v2_avg + f4 * v3_avg
654
elseif orientation == 2
655
f1 = rho_mean * v2_avg
656
f2 = f1 * v1_avg
657
f3 = f1 * v2_avg + p_mean
658
f4 = f1 * v3_avg
659
f5 = f1 * 0.5f0 *
660
(1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) +
661
f2 * v1_avg + f3 * v2_avg + f4 * v3_avg
662
else
663
f1 = rho_mean * v3_avg
664
f2 = f1 * v1_avg
665
f3 = f1 * v2_avg
666
f4 = f1 * v3_avg + p_mean
667
f5 = f1 * 0.5f0 *
668
(1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) +
669
f2 * v1_avg + f3 * v2_avg + f4 * v3_avg
670
end
671
672
return SVector(f1, f2, f3, f4, f5)
673
end
674
675
@inline function flux_chandrashekar(u_ll, u_rr, normal_direction::AbstractVector,
676
equations::CompressibleEulerEquations3D)
677
# Unpack left and right state
678
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
679
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
680
681
v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +
682
v3_ll * normal_direction[3]
683
v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +
684
v3_rr * normal_direction[3]
685
686
beta_ll = 0.5f0 * rho_ll / p_ll
687
beta_rr = 0.5f0 * rho_rr / p_rr
688
specific_kin_ll = 0.5f0 * (v1_ll^2 + v2_ll^2 + v3_ll^2)
689
specific_kin_rr = 0.5f0 * (v1_rr^2 + v2_rr^2 + v3_rr^2)
690
691
# Compute the necessary mean values
692
rho_avg = 0.5f0 * (rho_ll + rho_rr)
693
rho_mean = ln_mean(rho_ll, rho_rr)
694
beta_mean = ln_mean(beta_ll, beta_rr)
695
beta_avg = 0.5f0 * (beta_ll + beta_rr)
696
v1_avg = 0.5f0 * (v1_ll + v1_rr)
697
v2_avg = 0.5f0 * (v2_ll + v2_rr)
698
v3_avg = 0.5f0 * (v3_ll + v3_rr)
699
p_mean = 0.5f0 * rho_avg / beta_avg
700
velocity_square_avg = specific_kin_ll + specific_kin_rr
701
702
# Multiply with average of normal velocities
703
f1 = rho_mean * 0.5f0 * (v_dot_n_ll + v_dot_n_rr)
704
f2 = f1 * v1_avg + p_mean * normal_direction[1]
705
f3 = f1 * v2_avg + p_mean * normal_direction[2]
706
f4 = f1 * v3_avg + p_mean * normal_direction[3]
707
f5 = f1 * 0.5f0 * (1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) +
708
f2 * v1_avg + f3 * v2_avg + f4 * v3_avg
709
710
return SVector(f1, f2, f3, f4, f5)
711
end
712
713
"""
714
flux_ranocha(u_ll, u_rr, orientation_or_normal_direction,
715
equations::CompressibleEulerEquations3D)
716
717
Entropy conserving and kinetic energy preserving two-point flux by
718
- Hendrik Ranocha (2018)
719
Generalised Summation-by-Parts Operators and Entropy Stability of Numerical Methods
720
for Hyperbolic Balance Laws
721
[PhD thesis, TU Braunschweig](https://cuvillier.de/en/shop/publications/7743)
722
See also
723
- Hendrik Ranocha (2020)
724
Entropy Conserving and Kinetic Energy Preserving Numerical Methods for
725
the Euler Equations Using Summation-by-Parts Operators
726
[Proceedings of ICOSAHOM 2018](https://doi.org/10.1007/978-3-030-39647-3_42)
727
"""
728
@inline function flux_ranocha(u_ll, u_rr, orientation::Integer,
729
equations::CompressibleEulerEquations3D)
730
# Unpack left and right state
731
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
732
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
733
734
# Compute the necessary mean values
735
rho_mean = ln_mean(rho_ll, rho_rr)
736
# Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)`
737
# in exact arithmetic since
738
# log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ)
739
# = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ)
740
inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll)
741
v1_avg = 0.5f0 * (v1_ll + v1_rr)
742
v2_avg = 0.5f0 * (v2_ll + v2_rr)
743
v3_avg = 0.5f0 * (v3_ll + v3_rr)
744
p_avg = 0.5f0 * (p_ll + p_rr)
745
velocity_square_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr)
746
747
# Calculate fluxes depending on orientation
748
if orientation == 1
749
f1 = rho_mean * v1_avg
750
f2 = f1 * v1_avg + p_avg
751
f3 = f1 * v2_avg
752
f4 = f1 * v3_avg
753
f5 = f1 *
754
(velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) +
755
0.5f0 * (p_ll * v1_rr + p_rr * v1_ll)
756
elseif orientation == 2
757
f1 = rho_mean * v2_avg
758
f2 = f1 * v1_avg
759
f3 = f1 * v2_avg + p_avg
760
f4 = f1 * v3_avg
761
f5 = f1 *
762
(velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) +
763
0.5f0 * (p_ll * v2_rr + p_rr * v2_ll)
764
else # orientation == 3
765
f1 = rho_mean * v3_avg
766
f2 = f1 * v1_avg
767
f3 = f1 * v2_avg
768
f4 = f1 * v3_avg + p_avg
769
f5 = f1 *
770
(velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) +
771
0.5f0 * (p_ll * v3_rr + p_rr * v3_ll)
772
end
773
774
return SVector(f1, f2, f3, f4, f5)
775
end
776
777
@inline function flux_ranocha(u_ll, u_rr, normal_direction::AbstractVector,
778
equations::CompressibleEulerEquations3D)
779
# Unpack left and right state
780
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
781
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
782
v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +
783
v3_ll * normal_direction[3]
784
v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +
785
v3_rr * normal_direction[3]
786
787
# Compute the necessary mean values
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
velocity_square_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr)
799
800
# Calculate fluxes depending on normal_direction
801
f1 = rho_mean * 0.5f0 * (v_dot_n_ll + v_dot_n_rr)
802
f2 = f1 * v1_avg + p_avg * normal_direction[1]
803
f3 = f1 * v2_avg + p_avg * normal_direction[2]
804
f4 = f1 * v3_avg + p_avg * normal_direction[3]
805
f5 = (f1 * (velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one)
806
+
807
0.5f0 * (p_ll * v_dot_n_rr + p_rr * v_dot_n_ll))
808
809
return SVector(f1, f2, f3, f4, f5)
810
end
811
812
"""
813
splitting_steger_warming(u, orientation::Integer,
814
equations::CompressibleEulerEquations3D)
815
splitting_steger_warming(u, which::Union{Val{:minus}, Val{:plus}}
816
orientation::Integer,
817
equations::CompressibleEulerEquations3D)
818
819
Splitting of the compressible Euler flux of Steger and Warming.
820
821
Returns a tuple of the fluxes "minus" (associated with waves going into the
822
negative axis direction) and "plus" (associated with waves going into the
823
positive axis direction). If only one of the fluxes is required, use the
824
function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`.
825
826
!!! warning "Experimental implementation (upwind SBP)"
827
This is an experimental feature and may change in future releases.
828
829
## References
830
831
- Joseph L. Steger and R. F. Warming (1979)
832
Flux Vector Splitting of the Inviscid Gasdynamic Equations
833
With Application to Finite Difference Methods
834
[NASA Technical Memorandum](https://ntrs.nasa.gov/api/citations/19790020779/downloads/19790020779.pdf)
835
"""
836
@inline function splitting_steger_warming(u, orientation::Integer,
837
equations::CompressibleEulerEquations3D)
838
fm = splitting_steger_warming(u, Val{:minus}(), orientation, equations)
839
fp = splitting_steger_warming(u, Val{:plus}(), orientation, equations)
840
return fm, fp
841
end
842
843
@inline function splitting_steger_warming(u, ::Val{:plus}, orientation::Integer,
844
equations::CompressibleEulerEquations3D)
845
rho, rho_v1, rho_v2, rho_v3, rho_e_total = u
846
v1 = rho_v1 / rho
847
v2 = rho_v2 / rho
848
v3 = rho_v3 / rho
849
p = (equations.gamma - 1) *
850
(rho_e_total - 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3))
851
a = sqrt(equations.gamma * p / rho)
852
853
if orientation == 1
854
lambda1 = v1
855
lambda2 = v1 + a
856
lambda3 = v1 - a
857
858
lambda1_p = positive_part(lambda1) # Same as (lambda_i + abs(lambda_i)) / 2, but faster :)
859
lambda2_p = positive_part(lambda2)
860
lambda3_p = positive_part(lambda3)
861
862
alpha_p = 2 * (equations.gamma - 1) * lambda1_p + lambda2_p + lambda3_p
863
864
rho_2gamma = 0.5f0 * rho / equations.gamma
865
f1p = rho_2gamma * alpha_p
866
f2p = rho_2gamma * (alpha_p * v1 + a * (lambda2_p - lambda3_p))
867
f3p = rho_2gamma * alpha_p * v2
868
f4p = rho_2gamma * alpha_p * v3
869
f5p = rho_2gamma *
870
(alpha_p * 0.5f0 * (v1^2 + v2^2 + v3^2) +
871
a * v1 *
872
(lambda2_p - lambda3_p)
873
+ a^2 * (lambda2_p + lambda3_p) * equations.inv_gamma_minus_one)
874
elseif orientation == 2
875
lambda1 = v2
876
lambda2 = v2 + a
877
lambda3 = v2 - a
878
879
lambda1_p = positive_part(lambda1) # Same as (lambda_i + abs(lambda_i)) / 2, but faster :)
880
lambda2_p = positive_part(lambda2)
881
lambda3_p = positive_part(lambda3)
882
883
alpha_p = 2 * (equations.gamma - 1) * lambda1_p + lambda2_p + lambda3_p
884
885
rho_2gamma = 0.5f0 * rho / equations.gamma
886
f1p = rho_2gamma * alpha_p
887
f2p = rho_2gamma * alpha_p * v1
888
f3p = rho_2gamma * (alpha_p * v2 + a * (lambda2_p - lambda3_p))
889
f4p = rho_2gamma * alpha_p * v3
890
f5p = rho_2gamma *
891
(alpha_p * 0.5f0 * (v1^2 + v2^2 + v3^2) +
892
a * v2 *
893
(lambda2_p - lambda3_p)
894
+ a^2 * (lambda2_p + lambda3_p) * equations.inv_gamma_minus_one)
895
else # orientation == 3
896
lambda1 = v3
897
lambda2 = v3 + a
898
lambda3 = v3 - a
899
900
lambda1_p = positive_part(lambda1) # Same as (lambda_i + abs(lambda_i)) / 2, but faster :)
901
lambda2_p = positive_part(lambda2)
902
lambda3_p = positive_part(lambda3)
903
904
alpha_p = 2 * (equations.gamma - 1) * lambda1_p + lambda2_p + lambda3_p
905
906
rho_2gamma = 0.5f0 * rho / equations.gamma
907
f1p = rho_2gamma * alpha_p
908
f2p = rho_2gamma * alpha_p * v1
909
f3p = rho_2gamma * alpha_p * v2
910
f4p = rho_2gamma * (alpha_p * v3 + a * (lambda2_p - lambda3_p))
911
f5p = rho_2gamma *
912
(alpha_p * 0.5f0 * (v1^2 + v2^2 + v3^2) +
913
a * v3 *
914
(lambda2_p - lambda3_p)
915
+ a^2 * (lambda2_p + lambda3_p) * equations.inv_gamma_minus_one)
916
end
917
return SVector(f1p, f2p, f3p, f4p, f5p)
918
end
919
920
@inline function splitting_steger_warming(u, ::Val{:minus}, orientation::Integer,
921
equations::CompressibleEulerEquations3D)
922
rho, rho_v1, rho_v2, rho_v3, rho_e_total = u
923
v1 = rho_v1 / rho
924
v2 = rho_v2 / rho
925
v3 = rho_v3 / rho
926
p = (equations.gamma - 1) *
927
(rho_e_total - 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3))
928
a = sqrt(equations.gamma * p / rho)
929
930
if orientation == 1
931
lambda1 = v1
932
lambda2 = v1 + a
933
lambda3 = v1 - a
934
935
lambda1_m = negative_part(lambda1) # Same as (lambda_i - abs(lambda_i)) / 2, but faster :)
936
lambda2_m = negative_part(lambda2)
937
lambda3_m = negative_part(lambda3)
938
939
alpha_m = 2 * (equations.gamma - 1) * lambda1_m + lambda2_m + lambda3_m
940
941
rho_2gamma = 0.5f0 * rho / equations.gamma
942
f1m = rho_2gamma * alpha_m
943
f2m = rho_2gamma * (alpha_m * v1 + a * (lambda2_m - lambda3_m))
944
f3m = rho_2gamma * alpha_m * v2
945
f4m = rho_2gamma * alpha_m * v3
946
f5m = rho_2gamma *
947
(alpha_m * 0.5f0 * (v1^2 + v2^2 + v3^2) +
948
a * v1 *
949
(lambda2_m - lambda3_m)
950
+ a^2 * (lambda2_m + lambda3_m) * equations.inv_gamma_minus_one)
951
elseif orientation == 2
952
lambda1 = v2
953
lambda2 = v2 + a
954
lambda3 = v2 - a
955
956
lambda1_m = negative_part(lambda1) # Same as (lambda_i - abs(lambda_i)) / 2, but faster :)
957
lambda2_m = negative_part(lambda2)
958
lambda3_m = negative_part(lambda3)
959
960
alpha_m = 2 * (equations.gamma - 1) * lambda1_m + lambda2_m + lambda3_m
961
962
rho_2gamma = 0.5f0 * rho / equations.gamma
963
f1m = rho_2gamma * alpha_m
964
f2m = rho_2gamma * alpha_m * v1
965
f3m = rho_2gamma * (alpha_m * v2 + a * (lambda2_m - lambda3_m))
966
f4m = rho_2gamma * alpha_m * v3
967
f5m = rho_2gamma *
968
(alpha_m * 0.5f0 * (v1^2 + v2^2 + v3^2) +
969
a * v2 *
970
(lambda2_m - lambda3_m)
971
+ a^2 * (lambda2_m + lambda3_m) * equations.inv_gamma_minus_one)
972
else # orientation == 3
973
lambda1 = v3
974
lambda2 = v3 + a
975
lambda3 = v3 - a
976
977
lambda1_m = negative_part(lambda1) # Same as (lambda_i - abs(lambda_i)) / 2, but faster :)
978
lambda2_m = negative_part(lambda2)
979
lambda3_m = negative_part(lambda3)
980
981
alpha_m = 2 * (equations.gamma - 1) * lambda1_m + lambda2_m + lambda3_m
982
983
rho_2gamma = 0.5f0 * rho / equations.gamma
984
f1m = rho_2gamma * alpha_m
985
f2m = rho_2gamma * alpha_m * v1
986
f3m = rho_2gamma * alpha_m * v2
987
f4m = rho_2gamma * (alpha_m * v3 + a * (lambda2_m - lambda3_m))
988
f5m = rho_2gamma *
989
(alpha_m * 0.5f0 * (v1^2 + v2^2 + v3^2) +
990
a * v3 *
991
(lambda2_m - lambda3_m)
992
+ a^2 * (lambda2_m + lambda3_m) * equations.inv_gamma_minus_one)
993
end
994
return SVector(f1m, f2m, f3m, f4m, f5m)
995
end
996
997
"""
998
FluxLMARS(c)(u_ll, u_rr, orientation_or_normal_direction,
999
equations::CompressibleEulerEquations3D)
1000
1001
Low Mach number approximate Riemann solver (LMARS) for atmospheric flows using
1002
an estimate `c` of the speed of sound.
1003
1004
References:
1005
- Xi Chen et al. (2013)
1006
A Control-Volume Model of the Compressible Euler Equations with a Vertical
1007
Lagrangian Coordinate
1008
[DOI: 10.1175/MWR-D-12-00129.1](https://doi.org/10.1175/mwr-d-12-00129.1)
1009
"""
1010
@inline function (flux_lmars::FluxLMARS)(u_ll, u_rr, orientation::Integer,
1011
equations::CompressibleEulerEquations3D)
1012
c = flux_lmars.speed_of_sound
1013
1014
# Unpack left and right state
1015
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
1016
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
1017
1018
if orientation == 1
1019
v_ll = v1_ll
1020
v_rr = v1_rr
1021
elseif orientation == 2
1022
v_ll = v2_ll
1023
v_rr = v2_rr
1024
else # orientation == 3
1025
v_ll = v3_ll
1026
v_rr = v3_rr
1027
end
1028
1029
rho = 0.5f0 * (rho_ll + rho_rr)
1030
p = 0.5f0 * (p_ll + p_rr) - 0.5f0 * c * rho * (v_rr - v_ll)
1031
v = 0.5f0 * (v_ll + v_rr) - 1 / (2 * c * rho) * (p_rr - p_ll)
1032
1033
# We treat the energy term analogous to the potential temperature term in the paper by
1034
# Chen et al., i.e. we use p_ll and p_rr, and not p
1035
if v >= 0
1036
f1, f2, f3, f4, f5 = v * u_ll
1037
f5 = f5 + p_ll * v
1038
else
1039
f1, f2, f3, f4, f5 = v * u_rr
1040
f5 = f5 + p_rr * v
1041
end
1042
1043
if orientation == 1
1044
f2 += p
1045
elseif orientation == 2
1046
f3 += p
1047
else # orientation == 3
1048
f4 += p
1049
end
1050
1051
return SVector(f1, f2, f3, f4, f5)
1052
end
1053
1054
@inline function (flux_lmars::FluxLMARS)(u_ll, u_rr, normal_direction::AbstractVector,
1055
equations::CompressibleEulerEquations3D)
1056
c = flux_lmars.speed_of_sound
1057
1058
# Unpack left and right state
1059
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
1060
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
1061
1062
v_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +
1063
v3_ll * normal_direction[3]
1064
v_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +
1065
v3_rr * normal_direction[3]
1066
1067
# Note that this is the same as computing v_ll and v_rr with a normalized normal vector
1068
# and then multiplying v by `norm_` again, but this version is slightly faster.
1069
norm_ = norm(normal_direction)
1070
1071
rho = 0.5f0 * (rho_ll + rho_rr)
1072
p = 0.5f0 * (p_ll + p_rr) - 0.5f0 * c * rho * (v_rr - v_ll) / norm_
1073
v = 0.5f0 * (v_ll + v_rr) - 1 / (2 * c * rho) * (p_rr - p_ll) * norm_
1074
1075
# We treat the energy term analogous to the potential temperature term in the paper by
1076
# Chen et al., i.e. we use p_ll and p_rr, and not p
1077
if v >= 0
1078
f1, f2, f3, f4, f5 = v * u_ll
1079
f5 = f5 + p_ll * v
1080
else
1081
f1, f2, f3, f4, f5 = v * u_rr
1082
f5 = f5 + p_rr * v
1083
end
1084
1085
return SVector(f1,
1086
f2 + p * normal_direction[1],
1087
f3 + p * normal_direction[2],
1088
f4 + p * normal_direction[3],
1089
f5)
1090
end
1091
1092
# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation as the
1093
# maximum velocity magnitude plus the maximum speed of sound
1094
@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,
1095
equations::CompressibleEulerEquations3D)
1096
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
1097
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
1098
1099
# Get the velocity value in the appropriate direction
1100
if orientation == 1
1101
v_ll = v1_ll
1102
v_rr = v1_rr
1103
elseif orientation == 2
1104
v_ll = v2_ll
1105
v_rr = v2_rr
1106
else # orientation == 3
1107
v_ll = v3_ll
1108
v_rr = v3_rr
1109
end
1110
# Calculate sound speeds
1111
c_ll = sqrt(equations.gamma * p_ll / rho_ll)
1112
c_rr = sqrt(equations.gamma * p_rr / rho_rr)
1113
1114
return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr)
1115
end
1116
1117
@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,
1118
equations::CompressibleEulerEquations3D)
1119
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
1120
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
1121
1122
# Calculate normal velocities and sound speed
1123
# left
1124
v_ll = (v1_ll * normal_direction[1]
1125
+ v2_ll * normal_direction[2]
1126
+ v3_ll * normal_direction[3])
1127
c_ll = sqrt(equations.gamma * p_ll / rho_ll)
1128
# right
1129
v_rr = (v1_rr * normal_direction[1]
1130
+ v2_rr * normal_direction[2]
1131
+ v3_rr * normal_direction[3])
1132
c_rr = sqrt(equations.gamma * p_rr / rho_rr)
1133
1134
return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) * norm(normal_direction)
1135
end
1136
1137
# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`
1138
@inline function max_abs_speed(u_ll, u_rr, orientation::Integer,
1139
equations::CompressibleEulerEquations3D)
1140
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
1141
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
1142
1143
# Get the velocity value in the appropriate direction
1144
if orientation == 1
1145
v_ll = v1_ll
1146
v_rr = v1_rr
1147
elseif orientation == 2
1148
v_ll = v2_ll
1149
v_rr = v2_rr
1150
else # orientation == 3
1151
v_ll = v3_ll
1152
v_rr = v3_rr
1153
end
1154
# Calculate sound speeds
1155
c_ll = sqrt(equations.gamma * p_ll / rho_ll)
1156
c_rr = sqrt(equations.gamma * p_rr / rho_rr)
1157
1158
return max(abs(v_ll) + c_ll, abs(v_rr) + c_rr)
1159
end
1160
1161
# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`
1162
@inline function max_abs_speed(u_ll, u_rr, normal_direction::AbstractVector,
1163
equations::CompressibleEulerEquations3D)
1164
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
1165
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
1166
1167
# Calculate normal velocities and sound speeds
1168
# left
1169
v_ll = (v1_ll * normal_direction[1]
1170
+ v2_ll * normal_direction[2]
1171
+ v3_ll * normal_direction[3])
1172
c_ll = sqrt(equations.gamma * p_ll / rho_ll)
1173
# right
1174
v_rr = (v1_rr * normal_direction[1]
1175
+ v2_rr * normal_direction[2]
1176
+ v3_rr * normal_direction[3])
1177
c_rr = sqrt(equations.gamma * p_rr / rho_rr)
1178
1179
norm_ = norm(normal_direction)
1180
return max(abs(v_ll) + c_ll * norm_, abs(v_rr) + c_rr * norm_)
1181
end
1182
1183
# Calculate estimates for minimum and maximum wave speeds for HLL-type fluxes
1184
@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer,
1185
equations::CompressibleEulerEquations3D)
1186
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
1187
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
1188
1189
if orientation == 1 # x-direction
1190
λ_min = v1_ll - sqrt(equations.gamma * p_ll / rho_ll)
1191
λ_max = v1_rr + sqrt(equations.gamma * p_rr / rho_rr)
1192
elseif orientation == 2 # y-direction
1193
λ_min = v2_ll - sqrt(equations.gamma * p_ll / rho_ll)
1194
λ_max = v2_rr + sqrt(equations.gamma * p_rr / rho_rr)
1195
else # z-direction
1196
λ_min = v3_ll - sqrt(equations.gamma * p_ll / rho_ll)
1197
λ_max = v3_rr + sqrt(equations.gamma * p_rr / rho_rr)
1198
end
1199
1200
return λ_min, λ_max
1201
end
1202
1203
@inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,
1204
equations::CompressibleEulerEquations3D)
1205
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
1206
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
1207
1208
v_normal_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +
1209
v3_ll * normal_direction[3]
1210
v_normal_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +
1211
v3_rr * normal_direction[3]
1212
1213
norm_ = norm(normal_direction)
1214
# The v_normals are already scaled by the norm
1215
λ_min = v_normal_ll - sqrt(equations.gamma * p_ll / rho_ll) * norm_
1216
λ_max = v_normal_rr + sqrt(equations.gamma * p_rr / rho_rr) * norm_
1217
1218
return λ_min, λ_max
1219
end
1220
1221
# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
1222
@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,
1223
equations::CompressibleEulerEquations3D)
1224
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
1225
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
1226
1227
c_ll = sqrt(equations.gamma * p_ll / rho_ll)
1228
c_rr = sqrt(equations.gamma * p_rr / rho_rr)
1229
1230
if orientation == 1 # x-direction
1231
λ_min = min(v1_ll - c_ll, v1_rr - c_rr)
1232
λ_max = max(v1_ll + c_ll, v1_rr + c_rr)
1233
elseif orientation == 2 # y-direction
1234
λ_min = min(v2_ll - c_ll, v2_rr - c_rr)
1235
λ_max = max(v2_ll + c_ll, v2_rr + c_rr)
1236
else # z-direction
1237
λ_min = min(v3_ll - c_ll, v3_rr - c_rr)
1238
λ_max = max(v3_ll + c_ll, v3_rr + c_rr)
1239
end
1240
1241
return λ_min, λ_max
1242
end
1243
1244
# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
1245
@inline function min_max_speed_davis(u_ll, u_rr, normal_direction::AbstractVector,
1246
equations::CompressibleEulerEquations3D)
1247
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
1248
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
1249
1250
norm_ = norm(normal_direction)
1251
1252
c_ll = sqrt(equations.gamma * p_ll / rho_ll) * norm_
1253
c_rr = sqrt(equations.gamma * p_rr / rho_rr) * norm_
1254
1255
v_normal_ll = v1_ll * normal_direction[1] +
1256
v2_ll * normal_direction[2] +
1257
v3_ll * normal_direction[3]
1258
v_normal_rr = v1_rr * normal_direction[1] +
1259
v2_rr * normal_direction[2] +
1260
v3_rr * normal_direction[3]
1261
1262
# The v_normals are already scaled by the norm
1263
λ_min = min(v_normal_ll - c_ll, v_normal_rr - c_rr)
1264
λ_max = max(v_normal_ll + c_ll, v_normal_rr + c_rr)
1265
1266
return λ_min, λ_max
1267
end
1268
1269
# Rotate normal vector to x-axis; normal, tangent1 and tangent2 need to be orthonormal
1270
# Called inside `FluxRotated` in `numerical_fluxes.jl` so the directions
1271
# has been normalized prior to this rotation of the state vector
1272
@inline function rotate_to_x(u, normal_vector, tangent1, tangent2,
1273
equations::CompressibleEulerEquations3D)
1274
# Multiply with [ 1 0 0 0 0;
1275
# 0 ― normal_vector ― 0;
1276
# 0 ― tangent1 ― 0;
1277
# 0 ― tangent2 ― 0;
1278
# 0 0 0 0 1 ]
1279
return SVector(u[1],
1280
normal_vector[1] * u[2] + normal_vector[2] * u[3] +
1281
normal_vector[3] * u[4],
1282
tangent1[1] * u[2] + tangent1[2] * u[3] + tangent1[3] * u[4],
1283
tangent2[1] * u[2] + tangent2[2] * u[3] + tangent2[3] * u[4],
1284
u[5])
1285
end
1286
1287
@inline function (dissipation::DissipationMatrixWintersEtal)(u_ll, u_rr,
1288
normal_direction::AbstractVector,
1289
equations::CompressibleEulerEquations3D)
1290
(; gamma) = equations
1291
1292
# Step 1:
1293
# Rotate solution into the appropriate direction
1294
1295
norm_ = norm(normal_direction)
1296
# Normalize the vector without using `normalize` since we need to multiply by the `norm_` later
1297
normal_vector = normal_direction / norm_
1298
1299
# Some vector that can't be identical to normal_vector (unless normal_vector == 0)
1300
tangent1 = SVector(normal_direction[2], normal_direction[3], -normal_direction[1])
1301
# Orthogonal projection
1302
tangent1 -= dot(normal_vector, tangent1) * normal_vector
1303
tangent1 = normalize(tangent1)
1304
1305
# Third orthogonal vector
1306
tangent2 = normalize(cross(normal_direction, tangent1))
1307
1308
u_ll_rotated = rotate_to_x(u_ll, normal_vector, tangent1, tangent2, equations)
1309
u_rr_rotated = rotate_to_x(u_rr, normal_vector, tangent1, tangent2, equations)
1310
1311
# Step 2:
1312
# Compute the averages using the rotated variables
1313
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll_rotated, equations)
1314
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr_rotated, equations)
1315
1316
b_ll = rho_ll / (2 * p_ll)
1317
b_rr = rho_rr / (2 * p_rr)
1318
1319
rho_log = ln_mean(rho_ll, rho_rr)
1320
b_log = ln_mean(b_ll, b_rr)
1321
v1_avg = 0.5f0 * (v1_ll + v1_rr)
1322
v2_avg = 0.5f0 * (v2_ll + v2_rr)
1323
v3_avg = 0.5f0 * (v3_ll + v3_rr)
1324
p_avg = 0.5f0 * (rho_ll + rho_rr) / (b_ll + b_rr)
1325
v_squared_bar = v1_ll * v1_rr + v2_ll * v2_rr + v3_ll * v3_rr
1326
h_bar = gamma / (2 * b_log * (gamma - 1)) + 0.5f0 * v_squared_bar
1327
c_bar = sqrt(gamma * p_avg / rho_log)
1328
1329
# Step 3:
1330
# Build the dissipation term as given in Appendix A of the paper
1331
# - A. R. Winters, D. Derigs, G. Gassner, S. Walch, A uniquely defined entropy stable matrix dissipation operator
1332
# for high Mach number ideal MHD and compressible Euler simulations (2017). Journal of Computational Physics.
1333
# [DOI: 10.1016/j.jcp.2016.12.006](https://doi.org/10.1016/j.jcp.2016.12.006).
1334
1335
# Get entropy variables jump in the rotated variables
1336
w_jump = cons2entropy(u_rr_rotated, equations) -
1337
cons2entropy(u_ll_rotated, equations)
1338
1339
# Entries of the diagonal scaling matrix where D = ABS(\Lambda)T
1340
lambda_1 = abs(v1_avg - c_bar) * rho_log / (2 * gamma)
1341
lambda_2 = abs(v1_avg) * rho_log * (gamma - 1) / gamma
1342
lambda_3 = abs(v1_avg) * p_avg # scaled repeated eigenvalue in the tangential direction
1343
lambda_5 = abs(v1_avg + c_bar) * rho_log / (2 * gamma)
1344
D = SVector(lambda_1, lambda_2, lambda_3, lambda_3, lambda_5)
1345
1346
# Entries of the right eigenvector matrix (others have already been precomputed)
1347
r21 = v1_avg - c_bar
1348
r25 = v1_avg + c_bar
1349
r51 = h_bar - v1_avg * c_bar
1350
r52 = 0.5f0 * v_squared_bar
1351
r55 = h_bar + v1_avg * c_bar
1352
1353
# Build R and transpose of R matrices
1354
R = @SMatrix [[1;; 1;; 0;; 0;; 1];
1355
[r21;; v1_avg;; 0;; 0;; r25];
1356
[v2_avg;; v2_avg;; 1;; 0;; v2_avg];
1357
[v3_avg;; v3_avg;; 0;; 1;; v3_avg];
1358
[r51;; r52;; v2_avg;; v3_avg;; r55]]
1359
1360
RT = @SMatrix [[1;; r21;; v2_avg;; v3_avg;; r51];
1361
[1;; v1_avg;; v2_avg;; v3_avg;; r52];
1362
[0;; 0;; 1;; 0;; v2_avg];
1363
[0;; 0;; 0;; 1;; v3_avg];
1364
[1;; r25;; v2_avg;; v3_avg;; r55]]
1365
1366
# Compute the dissipation term R * D * R^T * [[w]] from right-to-left
1367
1368
# First comes R^T * [[w]]
1369
diss = RT * w_jump
1370
# Next multiply with the eigenvalues and Barth scaling
1371
diss = D .* diss
1372
# Finally apply the remaining eigenvector matrix
1373
diss = R * diss
1374
1375
# Step 4:
1376
# Do not forget to backrotate and then return with proper normalization scaling
1377
return -0.5f0 * rotate_from_x(diss, normal_vector, tangent1, tangent2, equations) *
1378
norm_
1379
end
1380
1381
# Rotate x-axis to normal vector; normal, tangent1 and tangent2 need to be orthonormal
1382
# Called inside `FluxRotated` in `numerical_fluxes.jl` so the directions
1383
# has been normalized prior to this back-rotation of the state vector
1384
@inline function rotate_from_x(u, normal_vector, tangent1, tangent2,
1385
equations::CompressibleEulerEquations3D)
1386
# Multiply with [ 1 0 0 0 0;
1387
# 0 | | | 0;
1388
# 0 normal_vector tangent1 tangent2 0;
1389
# 0 | | | 0;
1390
# 0 0 0 0 1 ]
1391
return SVector(u[1],
1392
normal_vector[1] * u[2] + tangent1[1] * u[3] + tangent2[1] * u[4],
1393
normal_vector[2] * u[2] + tangent1[2] * u[3] + tangent2[2] * u[4],
1394
normal_vector[3] * u[2] + tangent1[3] * u[3] + tangent2[3] * u[4],
1395
u[5])
1396
end
1397
1398
"""
1399
flux_hllc(u_ll, u_rr, orientation_or_normal_direction, equations::CompressibleEulerEquations3D)
1400
1401
Computes the HLLC flux (HLL with Contact) for compressible Euler equations developed by E.F. Toro
1402
[Lecture slides](http://www.prague-sum.com/download/2012/Toro_2-HLLC-RiemannSolver.pdf)
1403
Signal speeds: [DOI: 10.1137/S1064827593260140](https://doi.org/10.1137/S1064827593260140)
1404
"""
1405
function flux_hllc(u_ll, u_rr, orientation::Integer,
1406
equations::CompressibleEulerEquations3D)
1407
# Calculate primitive variables and speed of sound
1408
rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, rho_e_total_ll = u_ll
1409
rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, rho_e_total_rr = u_rr
1410
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
e_ll = rho_e_total_ll / rho_ll
1415
p_ll = (equations.gamma - 1) *
1416
(rho_e_total_ll - 0.5f0 * rho_ll * (v1_ll^2 + v2_ll^2 + v3_ll^2))
1417
c_ll = sqrt(equations.gamma * p_ll / rho_ll)
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
e_rr = rho_e_total_rr / rho_rr
1423
p_rr = (equations.gamma - 1) *
1424
(rho_e_total_rr - 0.5f0 * rho_rr * (v1_rr^2 + v2_rr^2 + v3_rr^2))
1425
c_rr = sqrt(equations.gamma * p_rr / rho_rr)
1426
1427
# Obtain left and right fluxes
1428
f_ll = flux(u_ll, orientation, equations)
1429
f_rr = flux(u_rr, orientation, equations)
1430
1431
# Compute Roe averages
1432
sqrt_rho_ll = sqrt(rho_ll)
1433
sqrt_rho_rr = sqrt(rho_rr)
1434
sum_sqrt_rho = sqrt_rho_ll + sqrt_rho_rr
1435
if orientation == 1 # x-direction
1436
vel_L = v1_ll
1437
vel_R = v1_rr
1438
elseif orientation == 2 # y-direction
1439
vel_L = v2_ll
1440
vel_R = v2_rr
1441
else # z-direction
1442
vel_L = v3_ll
1443
vel_R = v3_rr
1444
end
1445
vel_roe = (sqrt_rho_ll * vel_L + sqrt_rho_rr * vel_R) / sum_sqrt_rho
1446
v1_roe = sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr
1447
v2_roe = sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr
1448
v3_roe = sqrt_rho_ll * v3_ll + sqrt_rho_rr * v3_rr
1449
vel_roe_mag = (v1_roe^2 + v2_roe^2 + v3_roe^2) / sum_sqrt_rho^2
1450
H_ll = (rho_e_total_ll + p_ll) / rho_ll
1451
H_rr = (rho_e_total_rr + p_rr) / rho_rr
1452
H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) / sum_sqrt_rho
1453
c_roe = sqrt((equations.gamma - 1) * (H_roe - 0.5f0 * vel_roe_mag))
1454
Ssl = min(vel_L - c_ll, vel_roe - c_roe)
1455
Ssr = max(vel_R + c_rr, vel_roe + c_roe)
1456
sMu_L = Ssl - vel_L
1457
sMu_R = Ssr - vel_R
1458
1459
if Ssl >= 0
1460
f1 = f_ll[1]
1461
f2 = f_ll[2]
1462
f3 = f_ll[3]
1463
f4 = f_ll[4]
1464
f5 = f_ll[5]
1465
elseif Ssr <= 0
1466
f1 = f_rr[1]
1467
f2 = f_rr[2]
1468
f3 = f_rr[3]
1469
f4 = f_rr[4]
1470
f5 = f_rr[5]
1471
else
1472
SStar = (p_rr - p_ll + rho_ll * vel_L * sMu_L - rho_rr * vel_R * sMu_R) /
1473
(rho_ll * sMu_L - rho_rr * sMu_R)
1474
if Ssl <= 0 <= SStar
1475
densStar = rho_ll * sMu_L / (Ssl - SStar)
1476
enerStar = e_ll + (SStar - vel_L) * (SStar + p_ll / (rho_ll * sMu_L))
1477
UStar1 = densStar
1478
UStar5 = densStar * enerStar
1479
if orientation == 1 # x-direction
1480
UStar2 = densStar * SStar
1481
UStar3 = densStar * v2_ll
1482
UStar4 = densStar * v3_ll
1483
elseif orientation == 2 # y-direction
1484
UStar2 = densStar * v1_ll
1485
UStar3 = densStar * SStar
1486
UStar4 = densStar * v3_ll
1487
else # z-direction
1488
UStar2 = densStar * v1_ll
1489
UStar3 = densStar * v2_ll
1490
UStar4 = densStar * SStar
1491
end
1492
f1 = f_ll[1] + Ssl * (UStar1 - rho_ll)
1493
f2 = f_ll[2] + Ssl * (UStar2 - rho_v1_ll)
1494
f3 = f_ll[3] + Ssl * (UStar3 - rho_v2_ll)
1495
f4 = f_ll[4] + Ssl * (UStar4 - rho_v3_ll)
1496
f5 = f_ll[5] + Ssl * (UStar5 - rho_e_total_ll)
1497
else
1498
densStar = rho_rr * sMu_R / (Ssr - SStar)
1499
enerStar = e_rr + (SStar - vel_R) * (SStar + p_rr / (rho_rr * sMu_R))
1500
UStar1 = densStar
1501
UStar5 = densStar * enerStar
1502
if orientation == 1 # x-direction
1503
UStar2 = densStar * SStar
1504
UStar3 = densStar * v2_rr
1505
UStar4 = densStar * v3_rr
1506
elseif orientation == 2 # y-direction
1507
UStar2 = densStar * v1_rr
1508
UStar3 = densStar * SStar
1509
UStar4 = densStar * v3_rr
1510
else # z-direction
1511
UStar2 = densStar * v1_rr
1512
UStar3 = densStar * v2_rr
1513
UStar4 = densStar * SStar
1514
end
1515
f1 = f_rr[1] + Ssr * (UStar1 - rho_rr)
1516
f2 = f_rr[2] + Ssr * (UStar2 - rho_v1_rr)
1517
f3 = f_rr[3] + Ssr * (UStar3 - rho_v2_rr)
1518
f4 = f_rr[4] + Ssr * (UStar4 - rho_v3_rr)
1519
f5 = f_rr[5] + Ssr * (UStar5 - rho_e_total_rr)
1520
end
1521
end
1522
return SVector(f1, f2, f3, f4, f5)
1523
end
1524
1525
function flux_hllc(u_ll, u_rr, normal_direction::AbstractVector,
1526
equations::CompressibleEulerEquations3D)
1527
# Calculate primitive variables and speed of sound
1528
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
1529
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
1530
1531
v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +
1532
v3_ll * normal_direction[3]
1533
v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +
1534
v3_rr * normal_direction[3]
1535
1536
norm_ = norm(normal_direction)
1537
norm_sq = norm_ * norm_
1538
inv_norm_sq = inv(norm_sq)
1539
1540
c_ll = sqrt(equations.gamma * p_ll / rho_ll) * norm_
1541
c_rr = sqrt(equations.gamma * p_rr / rho_rr) * norm_
1542
1543
# Obtain left and right fluxes
1544
f_ll = flux(u_ll, normal_direction, equations)
1545
f_rr = flux(u_rr, normal_direction, equations)
1546
1547
# Compute Roe averages
1548
sqrt_rho_ll = sqrt(rho_ll)
1549
sqrt_rho_rr = sqrt(rho_rr)
1550
sum_sqrt_rho = sqrt_rho_ll + sqrt_rho_rr
1551
1552
v1_roe = (sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr) / sum_sqrt_rho
1553
v2_roe = (sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr) / sum_sqrt_rho
1554
v3_roe = (sqrt_rho_ll * v3_ll + sqrt_rho_rr * v3_rr) / sum_sqrt_rho
1555
vel_roe = v1_roe * normal_direction[1] + v2_roe * normal_direction[2] +
1556
v3_roe * normal_direction[3]
1557
vel_roe_mag = v1_roe^2 + v2_roe^2 + v3_roe^2
1558
1559
e_ll = u_ll[5] / rho_ll
1560
e_rr = u_rr[5] / rho_rr
1561
1562
H_ll = (u_ll[5] + p_ll) / rho_ll
1563
H_rr = (u_rr[5] + p_rr) / rho_rr
1564
1565
H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) / sum_sqrt_rho
1566
c_roe = sqrt((equations.gamma - 1) * (H_roe - 0.5f0 * vel_roe_mag)) * norm_
1567
1568
Ssl = min(v_dot_n_ll - c_ll, vel_roe - c_roe)
1569
Ssr = max(v_dot_n_rr + c_rr, vel_roe + c_roe)
1570
sMu_L = Ssl - v_dot_n_ll
1571
sMu_R = Ssr - v_dot_n_rr
1572
1573
if Ssl >= 0
1574
f1 = f_ll[1]
1575
f2 = f_ll[2]
1576
f3 = f_ll[3]
1577
f4 = f_ll[4]
1578
f5 = f_ll[5]
1579
elseif Ssr <= 0
1580
f1 = f_rr[1]
1581
f2 = f_rr[2]
1582
f3 = f_rr[3]
1583
f4 = f_rr[4]
1584
f5 = f_rr[5]
1585
else
1586
SStar = (rho_ll * v_dot_n_ll * sMu_L - rho_rr * v_dot_n_rr * sMu_R +
1587
(p_rr - p_ll) * norm_sq) / (rho_ll * sMu_L - rho_rr * sMu_R)
1588
if Ssl <= 0 <= SStar
1589
densStar = rho_ll * sMu_L / (Ssl - SStar)
1590
enerStar = e_ll +
1591
(SStar - v_dot_n_ll) *
1592
(SStar * inv_norm_sq + p_ll / (rho_ll * sMu_L))
1593
UStar1 = densStar
1594
UStar2 = densStar *
1595
(v1_ll + (SStar - v_dot_n_ll) * normal_direction[1] * inv_norm_sq)
1596
UStar3 = densStar *
1597
(v2_ll + (SStar - v_dot_n_ll) * normal_direction[2] * inv_norm_sq)
1598
UStar4 = densStar *
1599
(v3_ll + (SStar - v_dot_n_ll) * normal_direction[3] * inv_norm_sq)
1600
UStar5 = densStar * enerStar
1601
f1 = f_ll[1] + Ssl * (UStar1 - u_ll[1])
1602
f2 = f_ll[2] + Ssl * (UStar2 - u_ll[2])
1603
f3 = f_ll[3] + Ssl * (UStar3 - u_ll[3])
1604
f4 = f_ll[4] + Ssl * (UStar4 - u_ll[4])
1605
f5 = f_ll[5] + Ssl * (UStar5 - u_ll[5])
1606
else
1607
densStar = rho_rr * sMu_R / (Ssr - SStar)
1608
enerStar = e_rr +
1609
(SStar - v_dot_n_rr) *
1610
(SStar * inv_norm_sq + p_rr / (rho_rr * sMu_R))
1611
UStar1 = densStar
1612
UStar2 = densStar *
1613
(v1_rr + (SStar - v_dot_n_rr) * normal_direction[1] * inv_norm_sq)
1614
UStar3 = densStar *
1615
(v2_rr + (SStar - v_dot_n_rr) * normal_direction[2] * inv_norm_sq)
1616
UStar4 = densStar *
1617
(v3_rr + (SStar - v_dot_n_rr) * normal_direction[3] * inv_norm_sq)
1618
UStar5 = densStar * enerStar
1619
f1 = f_rr[1] + Ssr * (UStar1 - u_rr[1])
1620
f2 = f_rr[2] + Ssr * (UStar2 - u_rr[2])
1621
f3 = f_rr[3] + Ssr * (UStar3 - u_rr[3])
1622
f4 = f_rr[4] + Ssr * (UStar4 - u_rr[4])
1623
f5 = f_rr[5] + Ssr * (UStar5 - u_rr[5])
1624
end
1625
end
1626
return SVector(f1, f2, f3, f4, f5)
1627
end
1628
1629
"""
1630
min_max_speed_einfeldt(u_ll, u_rr, orientation, equations::CompressibleEulerEquations3D)
1631
1632
Computes the HLLE (Harten-Lax-van Leer-Einfeldt) flux for the compressible Euler equations.
1633
Special estimates of the signal velocites and linearization of the Riemann problem developed
1634
by Einfeldt to ensure that the internal energy and density remain positive during the computation
1635
of the numerical flux.
1636
1637
- Bernd Einfeldt (1988)
1638
On Godunov-type methods for gas dynamics.
1639
[DOI: 10.1137/0725021](https://doi.org/10.1137/0725021)
1640
- Bernd Einfeldt, Claus-Dieter Munz, Philip L. Roe and Björn Sjögreen (1991)
1641
On Godunov-type methods near low densities.
1642
[DOI: 10.1016/0021-9991(91)90211-3](https://doi.org/10.1016/0021-9991(91)90211-3)
1643
"""
1644
@inline function min_max_speed_einfeldt(u_ll, u_rr, orientation::Integer,
1645
equations::CompressibleEulerEquations3D)
1646
# Calculate primitive variables, enthalpy and speed of sound
1647
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
1648
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
1649
1650
# `u_ll[5]` is total energy `rho_e_total_ll` on the left
1651
H_ll = (u_ll[5] + p_ll) / rho_ll
1652
c_ll = sqrt(equations.gamma * p_ll / rho_ll)
1653
1654
# `u_rr[5]` is total energy `rho_e_total_rr` on the right
1655
H_rr = (u_rr[5] + p_rr) / rho_rr
1656
c_rr = sqrt(equations.gamma * p_rr / rho_rr)
1657
1658
# Compute Roe averages
1659
sqrt_rho_ll = sqrt(rho_ll)
1660
sqrt_rho_rr = sqrt(rho_rr)
1661
inv_sum_sqrt_rho = inv(sqrt_rho_ll + sqrt_rho_rr)
1662
1663
v1_roe = (sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr) * inv_sum_sqrt_rho
1664
v2_roe = (sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr) * inv_sum_sqrt_rho
1665
v3_roe = (sqrt_rho_ll * v3_ll + sqrt_rho_rr * v3_rr) * inv_sum_sqrt_rho
1666
v_roe_mag = v1_roe^2 + v2_roe^2 + v3_roe^2
1667
1668
H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) * inv_sum_sqrt_rho
1669
c_roe = sqrt((equations.gamma - 1) * (H_roe - 0.5f0 * v_roe_mag))
1670
1671
# Compute convenience constant for positivity preservation, see
1672
# https://doi.org/10.1016/0021-9991(91)90211-3
1673
beta = sqrt(0.5f0 * (equations.gamma - 1) / equations.gamma)
1674
1675
# Estimate the edges of the Riemann fan (with positivity conservation)
1676
if orientation == 1 # x-direction
1677
SsL = min(v1_roe - c_roe, v1_ll - beta * c_ll, 0)
1678
SsR = max(v1_roe + c_roe, v1_rr + beta * c_rr, 0)
1679
elseif orientation == 2 # y-direction
1680
SsL = min(v2_roe - c_roe, v2_ll - beta * c_ll, 0)
1681
SsR = max(v2_roe + c_roe, v2_rr + beta * c_rr, 0)
1682
else # z-direction
1683
SsL = min(v3_roe - c_roe, v3_ll - beta * c_ll, 0)
1684
SsR = max(v3_roe + c_roe, v3_rr + beta * c_rr, 0)
1685
end
1686
1687
return SsL, SsR
1688
end
1689
1690
"""
1691
min_max_speed_einfeldt(u_ll, u_rr, normal_direction, equations::CompressibleEulerEquations3D)
1692
1693
Computes the HLLE (Harten-Lax-van Leer-Einfeldt) flux for the compressible Euler equations.
1694
Special estimates of the signal velocites and linearization of the Riemann problem developed
1695
by Einfeldt to ensure that the internal energy and density remain positive during the computation
1696
of the numerical flux.
1697
1698
- Bernd Einfeldt (1988)
1699
On Godunov-type methods for gas dynamics.
1700
[DOI: 10.1137/0725021](https://doi.org/10.1137/0725021)
1701
- Bernd Einfeldt, Claus-Dieter Munz, Philip L. Roe and Björn Sjögreen (1991)
1702
On Godunov-type methods near low densities.
1703
[DOI: 10.1016/0021-9991(91)90211-3](https://doi.org/10.1016/0021-9991(91)90211-3)
1704
"""
1705
@inline function min_max_speed_einfeldt(u_ll, u_rr, normal_direction::AbstractVector,
1706
equations::CompressibleEulerEquations3D)
1707
# Calculate primitive variables, enthalpy and speed of sound
1708
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
1709
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
1710
1711
v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] +
1712
v3_ll * normal_direction[3]
1713
v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] +
1714
v3_rr * normal_direction[3]
1715
1716
norm_ = norm(normal_direction)
1717
1718
# `u_ll[5]` is total energy `rho_e_total_ll` on the left
1719
H_ll = (u_ll[5] + p_ll) / rho_ll
1720
c_ll = sqrt(equations.gamma * p_ll / rho_ll) * norm_
1721
1722
# `u_rr[5]` is total energy `rho_e_total_rr` on the right
1723
H_rr = (u_rr[5] + p_rr) / rho_rr
1724
c_rr = sqrt(equations.gamma * p_rr / rho_rr) * norm_
1725
1726
# Compute Roe averages
1727
sqrt_rho_ll = sqrt(rho_ll)
1728
sqrt_rho_rr = sqrt(rho_rr)
1729
inv_sum_sqrt_rho = inv(sqrt_rho_ll + sqrt_rho_rr)
1730
1731
v1_roe = (sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr) * inv_sum_sqrt_rho
1732
v2_roe = (sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr) * inv_sum_sqrt_rho
1733
v3_roe = (sqrt_rho_ll * v3_ll + sqrt_rho_rr * v3_rr) * inv_sum_sqrt_rho
1734
v_roe = v1_roe * normal_direction[1] + v2_roe * normal_direction[2] +
1735
v3_roe * normal_direction[3]
1736
v_roe_mag = v1_roe^2 + v2_roe^2 + v3_roe^2
1737
1738
H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) * inv_sum_sqrt_rho
1739
c_roe = sqrt((equations.gamma - 1) * (H_roe - 0.5f0 * v_roe_mag)) * norm_
1740
1741
# Compute convenience constant for positivity preservation, see
1742
# https://doi.org/10.1016/0021-9991(91)90211-3
1743
beta = sqrt(0.5f0 * (equations.gamma - 1) / equations.gamma)
1744
1745
# Estimate the edges of the Riemann fan (with positivity conservation)
1746
SsL = min(v_roe - c_roe, v_dot_n_ll - beta * c_ll, 0)
1747
SsR = max(v_roe + c_roe, v_dot_n_rr + beta * c_rr, 0)
1748
1749
return SsL, SsR
1750
end
1751
1752
@inline function max_abs_speeds(u, equations::CompressibleEulerEquations3D)
1753
rho, v1, v2, v3, p = cons2prim(u, equations)
1754
c = sqrt(equations.gamma * p / rho)
1755
1756
return abs(v1) + c, abs(v2) + c, abs(v3) + c
1757
end
1758
1759
# Convert conservative variables to primitive
1760
@inline function cons2prim(u, equations::CompressibleEulerEquations3D)
1761
rho, rho_v1, rho_v2, rho_v3, rho_e_total = u
1762
1763
v1 = rho_v1 / rho
1764
v2 = rho_v2 / rho
1765
v3 = rho_v3 / rho
1766
p = (equations.gamma - 1) *
1767
(rho_e_total - 0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3))
1768
1769
return SVector(rho, v1, v2, v3, p)
1770
end
1771
1772
# Convert conservative variables to entropy
1773
@inline function cons2entropy(u, equations::CompressibleEulerEquations3D)
1774
rho, rho_v1, rho_v2, rho_v3, rho_e_total = u
1775
1776
v1 = rho_v1 / rho
1777
v2 = rho_v2 / rho
1778
v3 = rho_v3 / rho
1779
v_square = v1^2 + v2^2 + v3^2
1780
p = (equations.gamma - 1) * (rho_e_total - 0.5f0 * rho * v_square)
1781
s = log(p) - equations.gamma * log(rho)
1782
rho_p = rho / p
1783
1784
w1 = (equations.gamma - s) * equations.inv_gamma_minus_one -
1785
0.5f0 * rho_p * v_square
1786
w2 = rho_p * v1
1787
w3 = rho_p * v2
1788
w4 = rho_p * v3
1789
w5 = -rho_p
1790
1791
return SVector(w1, w2, w3, w4, w5)
1792
end
1793
1794
# Transformation from conservative variables u to entropy vector ds_0/du,
1795
# using the modified specific entropy of Guermond et al. (2019): s_0 = p * rho^(-gamma) / (gamma-1).
1796
# Note: This is *not* the "conventional" specific entropy s = ln(p / rho^(gamma)).
1797
@inline function cons2entropy_guermond_etal(u, equations::CompressibleEulerEquations3D)
1798
rho, rho_v1, rho_v2, rho_v3, rho_e_total = u
1799
1800
inv_rho = 1 / rho
1801
v_square_rho = (rho_v1^2 + rho_v2^2 + rho_v3^2) * inv_rho
1802
inv_rho_gammap1 = inv_rho^(equations.gamma + 1)
1803
1804
# The derivative vector for the modified specific entropy of Guermond et al.
1805
w1 = inv_rho_gammap1 *
1806
(0.5f0 * (equations.gamma + 1) * v_square_rho - equations.gamma * rho_e_total)
1807
w2 = -rho_v1 * inv_rho_gammap1
1808
w3 = -rho_v2 * inv_rho_gammap1
1809
w4 = -rho_v3 * inv_rho_gammap1
1810
w5 = inv_rho^equations.gamma
1811
1812
return SVector(w1, w2, w3, w4, w5)
1813
end
1814
1815
@inline function entropy2cons(w, equations::CompressibleEulerEquations3D)
1816
# See Hughes, Franca, Mallet (1986) A new finite element formulation for CFD
1817
# [DOI: 10.1016/0045-7825(86)90127-1](https://doi.org/10.1016/0045-7825(86)90127-1)
1818
@unpack gamma = equations
1819
1820
# convert to entropy `-rho * s` used by Hughes, France, Mallet (1986)
1821
# instead of `-rho * s / (gamma - 1)`
1822
V1, V2, V3, V4, V5 = w .* (gamma - 1)
1823
1824
# s = specific entropy, eq. (53)
1825
V_square = V2^2 + V3^2 + V4^2
1826
s = gamma - V1 + V_square / (2 * V5)
1827
1828
# eq. (52)
1829
rho_iota = ((gamma - 1) / (-V5)^gamma)^(equations.inv_gamma_minus_one) *
1830
exp(-s * equations.inv_gamma_minus_one)
1831
1832
# eq. (51)
1833
rho = -rho_iota * V5
1834
rho_v1 = rho_iota * V2
1835
rho_v2 = rho_iota * V3
1836
rho_v3 = rho_iota * V4
1837
rho_e_total = rho_iota * (1 - V_square / (2 * V5))
1838
return SVector(rho, rho_v1, rho_v2, rho_v3, rho_e_total)
1839
end
1840
1841
# Convert primitive to conservative variables
1842
@inline function prim2cons(prim, equations::CompressibleEulerEquations3D)
1843
rho, v1, v2, v3, p = prim
1844
rho_v1 = rho * v1
1845
rho_v2 = rho * v2
1846
rho_v3 = rho * v3
1847
rho_e_total = p * equations.inv_gamma_minus_one +
1848
0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3)
1849
return SVector(rho, rho_v1, rho_v2, rho_v3, rho_e_total)
1850
end
1851
1852
@inline function density(u, equations::CompressibleEulerEquations3D)
1853
rho = u[1]
1854
return rho
1855
end
1856
1857
@inline function velocity(u, equations::CompressibleEulerEquations3D)
1858
rho = u[1]
1859
v1 = u[2] / rho
1860
v2 = u[3] / rho
1861
v3 = u[4] / rho
1862
return SVector(v1, v2, v3)
1863
end
1864
1865
@inline function velocity(u, orientation::Int, equations::CompressibleEulerEquations3D)
1866
rho = u[1]
1867
v = u[orientation + 1] / rho
1868
return v
1869
end
1870
1871
@inline function pressure(u, equations::CompressibleEulerEquations3D)
1872
rho, rho_v1, rho_v2, rho_v3, rho_e_total = u
1873
p = (equations.gamma - 1) *
1874
(rho_e_total - 0.5f0 * (rho_v1^2 + rho_v2^2 + rho_v3^2) / rho)
1875
return p
1876
end
1877
1878
@inline function density_pressure(u, equations::CompressibleEulerEquations3D)
1879
rho, rho_v1, rho_v2, rho_v3, rho_e_total = u
1880
rho_times_p = (equations.gamma - 1) *
1881
(rho * rho_e_total - 0.5f0 * (rho_v1^2 + rho_v2^2 + rho_v3^2))
1882
return rho_times_p
1883
end
1884
1885
# Calculate thermodynamic entropy for a conservative state `u`
1886
@inline function entropy_thermodynamic(u, equations::CompressibleEulerEquations3D)
1887
rho, _ = u
1888
p = pressure(u, equations)
1889
1890
# Thermodynamic entropy
1891
s = log(p) - equations.gamma * log(rho)
1892
1893
return s
1894
end
1895
1896
# Calculate mathematical entropy for a conservative state `cons`
1897
@inline function entropy_math(cons, equations::CompressibleEulerEquations3D)
1898
S = -entropy_thermodynamic(cons, equations) * cons[1] *
1899
equations.inv_gamma_minus_one
1900
# Mathematical entropy
1901
1902
return S
1903
end
1904
1905
@doc raw"""
1906
entropy_guermond_etal(u, equations::CompressibleEulerEquations3D)
1907
1908
Calculate the modified specific entropy of Guermond et al. (2019):
1909
```math
1910
s_0 = p * \rho^{-\gamma} / (\gamma-1).
1911
```
1912
Note: This is *not* the "conventional" specific entropy ``s = ln(p / \rho^\gamma)``.
1913
- Guermond at al. (2019)
1914
Invariant domain preserving discretization-independent schemes and convex limiting for hyperbolic systems.
1915
[DOI: 10.1016/j.cma.2018.11.036](https://doi.org/10.1016/j.cma.2018.11.036)
1916
"""
1917
@inline function entropy_guermond_etal(u, equations::CompressibleEulerEquations3D)
1918
rho, rho_v1, rho_v2, rho_v3, rho_e_total = u
1919
1920
# Modified specific entropy from Guermond et al. (2019)
1921
s = (rho_e_total - 0.5f0 * (rho_v1^2 + rho_v2^2 + rho_v3^2) / rho) *
1922
(1 / rho)^equations.gamma
1923
1924
return s
1925
end
1926
1927
# Transformation from conservative variables u to d(s)/d(u)
1928
@inline function gradient_conservative(::typeof(entropy_guermond_etal),
1929
u, equations::CompressibleEulerEquations3D)
1930
return cons2entropy_guermond_etal(u, equations)
1931
end
1932
1933
# Default entropy is the mathematical entropy
1934
@inline function entropy(cons, equations::CompressibleEulerEquations3D)
1935
return entropy_math(cons, equations)
1936
end
1937
1938
# Calculate total energy for a conservative state `cons`
1939
@inline energy_total(cons, ::CompressibleEulerEquations3D) = cons[5]
1940
1941
# Calculate kinetic energy for a conservative state `cons`
1942
@inline function energy_kinetic(u, equations::CompressibleEulerEquations3D)
1943
rho, rho_v1, rho_v2, rho_v3, _ = u
1944
return 0.5f0 * (rho_v1^2 + rho_v2^2 + rho_v3^2) / rho
1945
end
1946
1947
# Calculate internal energy for a conservative state `cons`
1948
@inline function energy_internal(cons, equations::CompressibleEulerEquations3D)
1949
return energy_total(cons, equations) - energy_kinetic(cons, equations)
1950
end
1951
1952
@inline function entropy_potential(u, orientation::Int,
1953
equations::CompressibleEulerEquations3D)
1954
if orientation == 1
1955
return u[2]
1956
elseif orientation == 2
1957
return u[3]
1958
else # orientation == 3
1959
return u[4]
1960
end
1961
end
1962
# Version for non-Cartesian meshes, i.e., everything but `TreeMesh`es.
1963
@inline function entropy_potential(u, normal_direction::AbstractVector,
1964
equations::CompressibleEulerEquations3D)
1965
return u[2] * normal_direction[1] +
1966
u[3] * normal_direction[2] +
1967
u[4] * normal_direction[3]
1968
end
1969
1970
# State validation for Newton-bisection method of subcell IDP limiting
1971
@inline function Base.isvalid(u, equations::CompressibleEulerEquations3D)
1972
if u[1] <= 0 || pressure(u, equations) <= 0
1973
return false
1974
end
1975
return true
1976
end
1977
end # @muladd
1978
1979