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