Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/equations/compressible_euler_multicomponent_2d.jl
5586 views
1
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
2
# Since these FMAs can increase the performance of many numerical algorithms,
3
# we need to opt-in explicitly.
4
# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
5
@muladd begin
6
#! format: noindent
7
8
@doc raw"""
9
CompressibleEulerMulticomponentEquations2D(; gammas, gas_constants)
10
11
Multicomponent version of the compressible Euler equations
12
```math
13
\frac{\partial}{\partial t}
14
\begin{pmatrix}
15
\rho v_1 \\ \rho v_2 \\ \rho e_{\text{total}} \\ \rho_1 \\ \rho_2 \\ \vdots \\ \rho_{n}
16
\end{pmatrix}
17
+
18
\frac{\partial}{\partial x}
19
\begin{pmatrix}
20
\rho v_1^2 + p \\ \rho v_1 v_2 \\ ( \rho e_{\text{total}} +p) v_1 \\ \rho_1 v_1 \\ \rho_2 v_1 \\ \vdots \\ \rho_{n} v_1
21
\end{pmatrix}
22
+
23
\frac{\partial}{\partial y}
24
\begin{pmatrix}
25
\rho v_1 v_2 \\ \rho v_2^2 + p \\ ( \rho e_{\text{total}} +p) v_2 \\ \rho_1 v_2 \\ \rho_2 v_2 \\ \vdots \\ \rho_{n} v_2
26
\end{pmatrix}
27
=
28
\begin{pmatrix}
29
0 \\ 0 \\ 0 \\ 0 \\ 0 \\ \vdots \\ 0
30
\end{pmatrix}
31
```
32
for calorically perfect gas in two space dimensions.
33
Here, ``\rho_i`` is the density of component ``i``, ``\rho=\sum_{i=1}^n\rho_i`` the sum of the individual ``\rho_i``,
34
``v_1``, ``v_2`` the velocities, ``e_{\text{total}}`` the specific total 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
```math
40
\gamma=\frac{\sum_{i=1}^n\rho_i C_{v,i}\gamma_i}{\sum_{i=1}^n\rho_i C_{v,i}}
41
```
42
total heat capacity ratio, ``\gamma_i`` heat capacity ratio of component ``i``,
43
```math
44
C_{v,i}=\frac{R}{\gamma_i-1}
45
```
46
specific heat capacity at constant volume of component ``i``.
47
48
In case of more than one component, the specific heat ratios `gammas` and the gas constants
49
`gas_constants` in [kJ/(kg*K)] should be passed as tuples, e.g., `gammas=(1.4, 1.667)`.
50
51
The remaining variables like the specific heats at constant volume `cv` or the specific heats at
52
constant pressure `cp` are then calculated considering a calorically perfect gas.
53
"""
54
struct CompressibleEulerMulticomponentEquations2D{NVARS, NCOMP, RealT <: Real} <:
55
AbstractCompressibleEulerMulticomponentEquations{2, NVARS, NCOMP}
56
gammas::SVector{NCOMP, RealT}
57
gas_constants::SVector{NCOMP, RealT}
58
cv::SVector{NCOMP, RealT}
59
cp::SVector{NCOMP, RealT}
60
61
function CompressibleEulerMulticomponentEquations2D{NVARS, NCOMP, RealT}(gammas::SVector{NCOMP,
62
RealT},
63
gas_constants::SVector{NCOMP,
64
RealT}) where {
65
NVARS,
66
NCOMP,
67
RealT <:
68
Real
69
}
70
NCOMP >= 1 ||
71
throw(DimensionMismatch("`gammas` and `gas_constants` have to be filled with at least one value"))
72
73
cv = gas_constants ./ (gammas .- 1)
74
cp = gas_constants + gas_constants ./ (gammas .- 1)
75
76
return new(gammas, gas_constants, cv, cp)
77
end
78
end
79
80
function CompressibleEulerMulticomponentEquations2D(; gammas, gas_constants)
81
_gammas = promote(gammas...)
82
_gas_constants = promote(gas_constants...)
83
RealT = promote_type(eltype(_gammas), eltype(_gas_constants),
84
typeof(gas_constants[1] / (gammas[1] - 1)))
85
__gammas = SVector(map(RealT, _gammas))
86
__gas_constants = SVector(map(RealT, _gas_constants))
87
88
NVARS = length(_gammas) + 3
89
NCOMP = length(_gammas)
90
91
return CompressibleEulerMulticomponentEquations2D{NVARS, NCOMP, RealT}(__gammas,
92
__gas_constants)
93
end
94
95
@inline function Base.real(::CompressibleEulerMulticomponentEquations2D{NVARS, NCOMP,
96
RealT}) where {
97
NVARS,
98
NCOMP,
99
RealT
100
}
101
return RealT
102
end
103
104
function varnames(::typeof(cons2cons),
105
equations::CompressibleEulerMulticomponentEquations2D)
106
cons = ("rho_v1", "rho_v2", "rho_e_total")
107
rhos = ntuple(n -> "rho" * string(n), Val(ncomponents(equations)))
108
return (cons..., rhos...)
109
end
110
111
function varnames(::typeof(cons2prim),
112
equations::CompressibleEulerMulticomponentEquations2D)
113
prim = ("v1", "v2", "p")
114
rhos = ntuple(n -> "rho" * string(n), Val(ncomponents(equations)))
115
return (prim..., rhos...)
116
end
117
118
# Set initial conditions at physical location `x` for time `t`
119
120
"""
121
initial_condition_convergence_test(x, t, equations::CompressibleEulerMulticomponentEquations2D)
122
123
A smooth initial condition used for convergence tests in combination with
124
[`source_terms_convergence_test`](@ref)
125
(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains).
126
"""
127
function initial_condition_convergence_test(x, t,
128
equations::CompressibleEulerMulticomponentEquations2D)
129
RealT = eltype(x)
130
c = 2
131
A = convert(RealT, 0.1)
132
L = 2
133
f = 1.0f0 / L
134
omega = 2 * convert(RealT, pi) * f
135
ini = c + A * sin(omega * (x[1] + x[2] - t))
136
137
v1 = 1
138
v2 = 1
139
140
rho = ini
141
142
# Here we compute an arbitrary number of different rhos. (one rho is double the next rho while the sum of all rhos is 1)
143
prim_rho = SVector{ncomponents(equations), real(equations)}(2^(i - 1) * (1 - 2) *
144
rho / (1 -
145
2^ncomponents(equations))
146
for i in eachcomponent(equations))
147
148
prim1 = rho * v1
149
prim2 = rho * v2
150
prim3 = rho^2
151
152
prim_other = SVector(prim1, prim2, prim3)
153
154
return vcat(prim_other, prim_rho)
155
end
156
157
"""
158
source_terms_convergence_test(u, x, t, equations::CompressibleEulerMulticomponentEquations2D)
159
160
Source terms used for convergence tests in combination with
161
[`initial_condition_convergence_test`](@ref)
162
(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains).
163
164
References for the method of manufactured solutions (MMS):
165
- Kambiz Salari and Patrick Knupp (2000)
166
Code Verification by the Method of Manufactured Solutions
167
[DOI: 10.2172/759450](https://doi.org/10.2172/759450)
168
- Patrick J. Roache (2002)
169
Code Verification by the Method of Manufactured Solutions
170
[DOI: 10.1115/1.1436090](https://doi.org/10.1115/1.1436090)
171
"""
172
@inline function source_terms_convergence_test(u, x, t,
173
equations::CompressibleEulerMulticomponentEquations2D)
174
# Same settings as in `initial_condition`
175
RealT = eltype(u)
176
c = 2
177
A = convert(RealT, 0.1)
178
L = 2
179
f = 1.0f0 / L
180
omega = 2 * convert(RealT, pi) * f
181
182
gamma = totalgamma(u, equations)
183
184
x1, x2 = x
185
si, co = sincos((x1 + x2 - t) * omega)
186
tmp1 = co * A * omega
187
tmp2 = si * A
188
tmp3 = gamma - 1
189
tmp4 = (2 * c - 1) * tmp3
190
tmp5 = (2 * tmp2 * gamma - 2 * tmp2 + tmp4 + 1) * tmp1
191
tmp6 = tmp2 + c
192
193
# Here we compute an arbitrary number of different rhos. (one rho is double the next rho while the sum of all rhos is 1
194
du_rho = SVector{ncomponents(equations), real(equations)}(2^(i - 1) * (1 - 2) *
195
tmp1 / (1 -
196
2^ncomponents(equations))
197
for i in eachcomponent(equations))
198
199
du1 = tmp5
200
du2 = tmp5
201
du3 = 2 * ((tmp6 - 1) * tmp3 + tmp6 * gamma) * tmp1
202
203
du_other = SVector(du1, du2, du3)
204
205
return vcat(du_other, du_rho)
206
end
207
208
"""
209
initial_condition_weak_blast_wave(x, t, equations::CompressibleEulerMulticomponentEquations2D)
210
211
A for multicomponent adapted weak blast wave taken from
212
- Sebastian Hennemann, Gregor J. Gassner (2020)
213
A provably entropy stable subcell shock capturing approach for high order split form DG
214
[arXiv: 2008.12044](https://arxiv.org/abs/2008.12044)
215
"""
216
function initial_condition_weak_blast_wave(x, t,
217
equations::CompressibleEulerMulticomponentEquations2D)
218
# From Hennemann & Gassner JCP paper 2020 (Sec. 6.3)
219
# Set up polar coordinates
220
RealT = eltype(x)
221
inicenter = SVector(0, 0)
222
x_norm = x[1] - inicenter[1]
223
y_norm = x[2] - inicenter[2]
224
r = sqrt(x_norm^2 + y_norm^2)
225
phi = atan(y_norm, x_norm)
226
sin_phi, cos_phi = sincos(phi)
227
228
prim_rho = SVector{ncomponents(equations), real(equations)}(r > 0.5f0 ?
229
2^(i - 1) * (1 - 2) /
230
(RealT(1) -
231
2^ncomponents(equations)) :
232
2^(i - 1) * (1 - 2) *
233
RealT(1.1691) /
234
(1 -
235
2^ncomponents(equations))
236
for i in eachcomponent(equations))
237
238
v1 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos_phi
239
v2 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * sin_phi
240
p = r > 0.5f0 ? one(RealT) : convert(RealT, 1.245)
241
242
prim_other = SVector(v1, v2, p)
243
244
return prim2cons(vcat(prim_other, prim_rho), equations)
245
end
246
247
"""
248
boundary_condition_slip_wall(u_inner, normal_direction, x, t, surface_flux_function,
249
equations::CompressibleEulerMulticomponentEquations2D)
250
251
Determine the boundary numerical surface flux for a slip wall condition.
252
Imposes a zero normal velocity at the wall.
253
Density is taken from the internal solution state and pressure is computed as an
254
exact solution of a 1D Riemann problem. Further details about this boundary state
255
are available in the paper:
256
- J. J. W. van der Vegt and H. van der Ven (2002)
257
Slip flow boundary conditions in discontinuous Galerkin discretizations of
258
the Euler equations of gas dynamics
259
[PDF](https://reports.nlr.nl/bitstream/handle/10921/692/TP-2002-300.pdf?sequence=1)
260
261
Details about the 1D pressure Riemann solution can be found in Section 6.3.3 of the book
262
- Eleuterio F. Toro (2009)
263
Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction
264
3rd edition
265
[DOI: 10.1007/b79761](https://doi.org/10.1007/b79761)
266
267
Should be used together with [`UnstructuredMesh2D`](@ref), [`P4estMesh`](@ref), or [`T8codeMesh`](@ref).
268
"""
269
@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector,
270
x, t,
271
surface_flux_function,
272
equations::CompressibleEulerMulticomponentEquations2D)
273
norm_ = norm(normal_direction)
274
# Normalize the vector without using `normalize` since we need to multiply by the `norm_` later
275
normal = normal_direction / norm_
276
277
# rotate the internal solution state
278
u_local = rotate_to_x(u_inner, normal, equations)
279
280
# compute the primitive variables
281
(v_normal, v_tangent, p_local, rhos_local...) = cons2prim(u_local, equations)
282
283
rho_local = sum(rhos_local)
284
gamma = totalgamma(u_inner, equations)
285
286
# Get the solution of the pressure Riemann problem
287
# See Section 6.3.3 of
288
# Eleuterio F. Toro (2009)
289
# Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction
290
# [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761)
291
if v_normal <= 0
292
sound_speed = sqrt(gamma * p_local / rho_local) # local sound speed
293
p_star = p_local *
294
(1 + 0.5f0 * (gamma - 1) * v_normal / sound_speed)^(2 *
295
gamma /
296
(gamma - 1.0f0))
297
else # v_normal > 0
298
A = 2 / ((gamma + 1) * rho_local)
299
B = p_local * (gamma - 1) / (gamma + 1)
300
p_star = p_local +
301
0.5f0 * v_normal / A *
302
(v_normal + sqrt(v_normal^2 + 4 * A * (p_local + B)))
303
end
304
305
# For the slip wall we directly set the flux as the normal velocity is zero
306
return vcat(SVector(p_star * normal_direction[1],
307
p_star * normal_direction[2],
308
0.0f0),
309
zero(SVector{ncomponents(equations), eltype(u_inner)}))
310
end
311
312
"""
313
boundary_condition_slip_wall(u_inner, orientation, direction, x, t,
314
surface_flux_function, equations::CompressibleEulerMulticomponentEquations2D)
315
316
Should be used together with [`TreeMesh`](@ref).
317
"""
318
@inline function boundary_condition_slip_wall(u_inner, orientation,
319
direction, x, t,
320
surface_flux_function,
321
equations::CompressibleEulerMulticomponentEquations2D)
322
# get the appropriate normal vector from the orientation
323
RealT = eltype(u_inner)
324
if orientation == 1
325
normal_direction = SVector(one(RealT), zero(RealT))
326
else # orientation == 2
327
normal_direction = SVector(zero(RealT), one(RealT))
328
end
329
330
# compute and return the flux using `boundary_condition_slip_wall` routine below
331
return boundary_condition_slip_wall(u_inner, normal_direction, direction,
332
x, t, surface_flux_function, equations)
333
end
334
335
"""
336
boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector,
337
direction, x, t,
338
surface_flux_function, equations::CompressibleEulerMulticomponentEquations2D)
339
340
Should be used together with [`StructuredMesh`](@ref).
341
"""
342
@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector,
343
direction, x, t,
344
surface_flux_function,
345
equations::CompressibleEulerMulticomponentEquations2D)
346
# flip sign of normal to make it outward pointing, then flip the sign of the normal flux back
347
# to be inward pointing on the -x and -y sides due to the orientation convention used by StructuredMesh
348
if isodd(direction)
349
boundary_flux = -boundary_condition_slip_wall(u_inner, -normal_direction,
350
x, t, surface_flux_function,
351
equations)
352
else
353
boundary_flux = boundary_condition_slip_wall(u_inner, normal_direction,
354
x, t, surface_flux_function,
355
equations)
356
end
357
358
return boundary_flux
359
end
360
361
# Calculate 1D flux for a single point
362
@inline function flux(u, orientation::Integer,
363
equations::CompressibleEulerMulticomponentEquations2D)
364
rho_v1, rho_v2, rho_e_total = u
365
366
rho = density(u, equations)
367
368
v1 = rho_v1 / rho
369
v2 = rho_v2 / rho
370
gamma = totalgamma(u, equations)
371
p = (gamma - 1) * (rho_e_total - 0.5f0 * rho * (v1^2 + v2^2))
372
373
if orientation == 1
374
f_rho = densities(u, v1, equations)
375
f1 = rho_v1 * v1 + p
376
f2 = rho_v2 * v1
377
f3 = (rho_e_total + p) * v1
378
else
379
f_rho = densities(u, v2, equations)
380
f1 = rho_v1 * v2
381
f2 = rho_v2 * v2 + p
382
f3 = (rho_e_total + p) * v2
383
end
384
385
f_other = SVector(f1, f2, f3)
386
387
return vcat(f_other, f_rho)
388
end
389
390
# Calculate 1D flux for a single point
391
@inline function flux(u, normal_direction::AbstractVector,
392
equations::CompressibleEulerMulticomponentEquations2D)
393
rho_v1, rho_v2, rho_e_total = u
394
395
rho = density(u, equations)
396
397
v1 = rho_v1 / rho
398
v2 = rho_v2 / rho
399
v_normal = v1 * normal_direction[1] + v2 * normal_direction[2]
400
gamma = totalgamma(u, equations)
401
p = (gamma - 1) * (rho_e_total - 0.5f0 * rho * (v1^2 + v2^2))
402
403
f_rho = densities(u, v_normal, equations)
404
f1 = rho_v1 * v_normal + p * normal_direction[1]
405
f2 = rho_v2 * v_normal + p * normal_direction[2]
406
f3 = (rho_e_total + p) * v_normal
407
408
f_other = SVector(f1, f2, f3)
409
410
return vcat(f_other, f_rho)
411
end
412
413
"""
414
flux_chandrashekar(u_ll, u_rr, orientation, equations::CompressibleEulerMulticomponentEquations2D)
415
416
Adaption of the entropy conserving two-point flux by
417
- Ayoub Gouasmi, Karthik Duraisamy (2020)
418
"Formulation of Entropy-Stable schemes for the multicomponent compressible Euler equations"
419
[arXiv:1904.00972v3](https://arxiv.org/abs/1904.00972) [math.NA] 4 Feb 2020
420
"""
421
@inline function flux_chandrashekar(u_ll, u_rr, orientation::Integer,
422
equations::CompressibleEulerMulticomponentEquations2D)
423
# Unpack left and right state
424
@unpack gammas, gas_constants, cv = equations
425
rho_v1_ll, rho_v2_ll, rho_e_total_ll = u_ll
426
rho_v1_rr, rho_v2_rr, rho_e_total_rr = u_rr
427
rhok_mean = SVector{ncomponents(equations), real(equations)}(ln_mean(u_ll[i + 3],
428
u_rr[i + 3])
429
for i in eachcomponent(equations))
430
rhok_avg = SVector{ncomponents(equations), real(equations)}(0.5f0 * (u_ll[i + 3] +
431
u_rr[i + 3])
432
for i in eachcomponent(equations))
433
434
# Iterating over all partial densities
435
rho_ll = density(u_ll, equations)
436
rho_rr = density(u_rr, equations)
437
438
# extract velocities
439
v1_ll = rho_v1_ll / rho_ll
440
v2_ll = rho_v2_ll / rho_ll
441
v1_rr = rho_v1_rr / rho_rr
442
v2_rr = rho_v2_rr / rho_rr
443
v1_avg = 0.5f0 * (v1_ll + v1_rr)
444
v2_avg = 0.5f0 * (v2_ll + v2_rr)
445
v1_square = 0.5f0 * (v1_ll^2 + v1_rr^2)
446
v2_square = 0.5f0 * (v2_ll^2 + v2_rr^2)
447
v_sum = v1_avg + v2_avg
448
449
RealT = eltype(u_ll)
450
enth = zero(RealT)
451
help1_ll = zero(RealT)
452
help1_rr = zero(RealT)
453
454
for i in eachcomponent(equations)
455
enth += rhok_avg[i] * gas_constants[i]
456
help1_ll += u_ll[i + 3] * cv[i]
457
help1_rr += u_rr[i + 3] * cv[i]
458
end
459
460
T_ll = (rho_e_total_ll - 0.5f0 * rho_ll * (v1_ll^2 + v2_ll^2)) / help1_ll
461
T_rr = (rho_e_total_rr - 0.5f0 * rho_rr * (v1_rr^2 + v2_rr^2)) / help1_rr
462
T = 0.5f0 * (1 / T_ll + 1 / T_rr)
463
T_log = ln_mean(1 / T_ll, 1 / T_rr)
464
465
# Calculate fluxes depending on orientation
466
help1 = zero(RealT)
467
help2 = zero(RealT)
468
if orientation == 1
469
f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * v1_avg
470
for i in eachcomponent(equations))
471
for i in eachcomponent(equations)
472
help1 += f_rho[i] * cv[i]
473
help2 += f_rho[i]
474
end
475
f1 = (help2) * v1_avg + enth / T
476
f2 = (help2) * v2_avg
477
f3 = (help1) / T_log - 0.5f0 * (v1_square + v2_square) * (help2) + v1_avg * f1 +
478
v2_avg * f2
479
else
480
f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * v2_avg
481
for i in eachcomponent(equations))
482
for i in eachcomponent(equations)
483
help1 += f_rho[i] * cv[i]
484
help2 += f_rho[i]
485
end
486
f1 = (help2) * v1_avg
487
f2 = (help2) * v2_avg + enth / T
488
f3 = (help1) / T_log - 0.5f0 * (v1_square + v2_square) * (help2) + v1_avg * f1 +
489
v2_avg * f2
490
end
491
f_other = SVector(f1, f2, f3)
492
493
return vcat(f_other, f_rho)
494
end
495
496
"""
497
flux_ranocha(u_ll, u_rr, orientation_or_normal_direction,
498
equations::CompressibleEulerMulticomponentEquations2D)
499
500
Adaption of the entropy conserving and kinetic energy preserving two-point flux by
501
- Hendrik Ranocha (2018)
502
Generalised Summation-by-Parts Operators and Entropy Stability of Numerical Methods
503
for Hyperbolic Balance Laws
504
[PhD thesis, TU Braunschweig](https://cuvillier.de/en/shop/publications/7743)
505
See also
506
- Hendrik Ranocha (2020)
507
Entropy Conserving and Kinetic Energy Preserving Numerical Methods for
508
the Euler Equations Using Summation-by-Parts Operators
509
[Proceedings of ICOSAHOM 2018](https://doi.org/10.1007/978-3-030-39647-3_42)
510
"""
511
@inline function flux_ranocha(u_ll, u_rr, orientation::Integer,
512
equations::CompressibleEulerMulticomponentEquations2D)
513
# Unpack left and right state
514
@unpack gammas, gas_constants, cv = equations
515
rho_v1_ll, rho_v2_ll, rho_e_total_ll = u_ll
516
rho_v1_rr, rho_v2_rr, rho_e_total_rr = u_rr
517
rhok_mean = SVector{ncomponents(equations), real(equations)}(ln_mean(u_ll[i + 3],
518
u_rr[i + 3])
519
for i in eachcomponent(equations))
520
rhok_avg = SVector{ncomponents(equations), real(equations)}(0.5f0 * (u_ll[i + 3] +
521
u_rr[i + 3])
522
for i in eachcomponent(equations))
523
524
# Iterating over all partial densities
525
rho_ll = density(u_ll, equations)
526
rho_rr = density(u_rr, equations)
527
528
# Calculating gamma
529
gamma = totalgamma(0.5f0 * (u_ll + u_rr), equations)
530
inv_gamma_minus_one = 1 / (gamma - 1)
531
532
# extract velocities
533
v1_ll = rho_v1_ll / rho_ll
534
v1_rr = rho_v1_rr / rho_rr
535
v1_avg = 0.5f0 * (v1_ll + v1_rr)
536
v2_ll = rho_v2_ll / rho_ll
537
v2_rr = rho_v2_rr / rho_rr
538
v2_avg = 0.5f0 * (v2_ll + v2_rr)
539
velocity_square_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr)
540
541
# helpful variables
542
RealT = eltype(u_ll)
543
help1_ll = zero(RealT)
544
help1_rr = zero(RealT)
545
enth_ll = zero(RealT)
546
enth_rr = zero(RealT)
547
for i in eachcomponent(equations)
548
enth_ll += u_ll[i + 3] * gas_constants[i]
549
enth_rr += u_rr[i + 3] * gas_constants[i]
550
help1_ll += u_ll[i + 3] * cv[i]
551
help1_rr += u_rr[i + 3] * cv[i]
552
end
553
554
# temperature and pressure
555
T_ll = (rho_e_total_ll - 0.5f0 * rho_ll * (v1_ll^2 + v2_ll^2)) / help1_ll
556
T_rr = (rho_e_total_rr - 0.5f0 * rho_rr * (v1_rr^2 + v2_rr^2)) / help1_rr
557
p_ll = T_ll * enth_ll
558
p_rr = T_rr * enth_rr
559
p_avg = 0.5f0 * (p_ll + p_rr)
560
inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll)
561
562
f_rho_sum = zero(RealT)
563
if orientation == 1
564
f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * v1_avg
565
for i in eachcomponent(equations))
566
for i in eachcomponent(equations)
567
f_rho_sum += f_rho[i]
568
end
569
f1 = f_rho_sum * v1_avg + p_avg
570
f2 = f_rho_sum * v2_avg
571
f3 = f_rho_sum * (velocity_square_avg + inv_rho_p_mean * inv_gamma_minus_one) +
572
0.5f0 * (p_ll * v1_rr + p_rr * v1_ll)
573
else
574
f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * v2_avg
575
for i in eachcomponent(equations))
576
for i in eachcomponent(equations)
577
f_rho_sum += f_rho[i]
578
end
579
f1 = f_rho_sum * v1_avg
580
f2 = f_rho_sum * v2_avg + p_avg
581
f3 = f_rho_sum * (velocity_square_avg + inv_rho_p_mean * inv_gamma_minus_one) +
582
0.5f0 * (p_ll * v2_rr + p_rr * v2_ll)
583
end
584
585
# momentum and energy flux
586
f_other = SVector(f1, f2, f3)
587
588
return vcat(f_other, f_rho)
589
end
590
591
@inline function flux_ranocha(u_ll, u_rr, normal_direction::AbstractVector,
592
equations::CompressibleEulerMulticomponentEquations2D)
593
# Unpack left and right state
594
@unpack gammas, gas_constants, cv = equations
595
rho_v1_ll, rho_v2_ll, rho_e_total_ll = u_ll
596
rho_v1_rr, rho_v2_rr, rho_e_total_rr = u_rr
597
rhok_mean = SVector{ncomponents(equations), real(equations)}(ln_mean(u_ll[i + 3],
598
u_rr[i + 3])
599
for i in eachcomponent(equations))
600
rhok_avg = SVector{ncomponents(equations), real(equations)}(0.5f0 * (u_ll[i + 3] +
601
u_rr[i + 3])
602
for i in eachcomponent(equations))
603
604
# Iterating over all partial densities
605
rho_ll = density(u_ll, equations)
606
rho_rr = density(u_rr, equations)
607
608
# Calculating gamma
609
gamma = totalgamma(0.5f0 * (u_ll + u_rr), equations)
610
inv_gamma_minus_one = 1 / (gamma - 1)
611
612
# extract velocities
613
v1_ll = rho_v1_ll / rho_ll
614
v1_rr = rho_v1_rr / rho_rr
615
v1_avg = 0.5f0 * (v1_ll + v1_rr)
616
v2_ll = rho_v2_ll / rho_ll
617
v2_rr = rho_v2_rr / rho_rr
618
v2_avg = 0.5f0 * (v2_ll + v2_rr)
619
velocity_square_avg = 0.5f0 * (v1_ll * v1_rr + v2_ll * v2_rr)
620
v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2]
621
v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2]
622
623
# helpful variables
624
RealT = eltype(u_ll)
625
help1_ll = zero(RealT)
626
help1_rr = zero(RealT)
627
enth_ll = zero(RealT)
628
enth_rr = zero(RealT)
629
for i in eachcomponent(equations)
630
enth_ll += u_ll[i + 3] * gas_constants[i]
631
enth_rr += u_rr[i + 3] * gas_constants[i]
632
help1_ll += u_ll[i + 3] * cv[i]
633
help1_rr += u_rr[i + 3] * cv[i]
634
end
635
636
# temperature and pressure
637
T_ll = (rho_e_total_ll - 0.5f0 * rho_ll * (v1_ll^2 + v2_ll^2)) / help1_ll
638
T_rr = (rho_e_total_rr - 0.5f0 * rho_rr * (v1_rr^2 + v2_rr^2)) / help1_rr
639
p_ll = T_ll * enth_ll
640
p_rr = T_rr * enth_rr
641
p_avg = 0.5f0 * (p_ll + p_rr)
642
inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll)
643
644
f_rho_sum = zero(RealT)
645
f_rho = SVector{ncomponents(equations), real(equations)}(rhok_mean[i] * 0.5f0 *
646
(v_dot_n_ll + v_dot_n_rr)
647
for i in eachcomponent(equations))
648
for i in eachcomponent(equations)
649
f_rho_sum += f_rho[i]
650
end
651
f1 = f_rho_sum * v1_avg + p_avg * normal_direction[1]
652
f2 = f_rho_sum * v2_avg + p_avg * normal_direction[2]
653
f3 = f_rho_sum * (velocity_square_avg + inv_rho_p_mean * inv_gamma_minus_one) +
654
0.5f0 * (p_ll * v_dot_n_rr + p_rr * v_dot_n_ll)
655
656
# momentum and energy flux
657
f_other = SVector(f1, f2, f3)
658
659
return vcat(f_other, f_rho)
660
end
661
662
# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation
663
@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,
664
equations::CompressibleEulerMulticomponentEquations2D)
665
rho_v1_ll, rho_v2_ll, rho_e_total_ll = u_ll
666
rho_v1_rr, rho_v2_rr, rho_e_total_rr = u_rr
667
668
# Get the density and gas gamma
669
rho_ll = density(u_ll, equations)
670
rho_rr = density(u_rr, equations)
671
gamma_ll = totalgamma(u_ll, equations)
672
gamma_rr = totalgamma(u_rr, equations)
673
674
# Get the velocities based on direction
675
if orientation == 1
676
v_ll = rho_v1_ll / rho_ll
677
v_rr = rho_v1_rr / rho_rr
678
else # orientation == 2
679
v_ll = rho_v2_ll / rho_ll
680
v_rr = rho_v2_rr / rho_rr
681
end
682
683
# Compute the sound speeds on the left and right
684
p_ll = (gamma_ll - 1) *
685
(rho_e_total_ll - 0.5f0 * (rho_v1_ll^2 + rho_v2_ll^2) / rho_ll)
686
c_ll = sqrt(gamma_ll * p_ll / rho_ll)
687
p_rr = (gamma_rr - 1) *
688
(rho_e_total_rr - 0.5f0 * (rho_v1_rr^2 + rho_v2_rr^2) / rho_rr)
689
c_rr = sqrt(gamma_rr * p_rr / rho_rr)
690
691
return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr)
692
end
693
694
# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation
695
@inline function max_abs_speed_naive(u_ll::SVector{N, T}, u_rr::SVector{N, T},
696
normal_direction::SVector{2, T},
697
equations::CompressibleEulerMulticomponentEquations2D) where {
698
N,
699
T <:
700
AbstractFloat
701
}
702
# Unpack conservative variables
703
rho_v1_ll, rho_v2_ll, rho_e_total_ll = u_ll
704
rho_v1_rr, rho_v2_rr, rho_e_total_rr = u_rr
705
706
# Get densities and gammas
707
rho_ll = T(density(u_ll, equations))
708
rho_rr = T(density(u_rr, equations))
709
gamma_ll = T(totalgamma(u_ll, equations))
710
gamma_rr = T(totalgamma(u_rr, equations))
711
712
# Velocity components
713
v_ll_vec = SVector(rho_v1_ll / rho_ll, rho_v2_ll / rho_ll)
714
v_rr_vec = SVector(rho_v1_rr / rho_rr, rho_v2_rr / rho_rr)
715
716
# Project velocities onto the direction normal_direction.
717
v_ll = dot(v_ll_vec, normal_direction)
718
v_rr = dot(v_rr_vec, normal_direction)
719
720
# Compute pressures
721
p_ll = (gamma_ll - one(T)) *
722
(rho_e_total_ll - 0.5f0 * dot(v_ll_vec, v_ll_vec) * rho_ll)
723
p_rr = (gamma_rr - one(T)) *
724
(rho_e_total_rr - 0.5f0 * dot(v_rr_vec, v_rr_vec) * rho_rr)
725
726
# Sound speeds
727
c_ll = sqrt(gamma_ll * p_ll / rho_ll)
728
c_rr = sqrt(gamma_rr * p_rr / rho_rr)
729
730
return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr)
731
end
732
733
# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`
734
@inline function max_abs_speed(u_ll, u_rr, orientation::Integer,
735
equations::CompressibleEulerMulticomponentEquations2D)
736
rho_v1_ll, rho_v2_ll, rho_e_total_ll = u_ll
737
rho_v1_rr, rho_v2_rr, rho_e_total_rr = u_rr
738
739
# Get the density and gas gamma
740
rho_ll = density(u_ll, equations)
741
rho_rr = density(u_rr, equations)
742
gamma_ll = totalgamma(u_ll, equations)
743
gamma_rr = totalgamma(u_rr, equations)
744
745
# Get the velocities based on direction
746
if orientation == 1
747
v_ll = rho_v1_ll / rho_ll
748
v_rr = rho_v1_rr / rho_rr
749
else # orientation == 2
750
v_ll = rho_v2_ll / rho_ll
751
v_rr = rho_v2_rr / rho_rr
752
end
753
754
# Compute the sound speeds on the left and right
755
p_ll = (gamma_ll - 1) *
756
(rho_e_total_ll - 0.5f0 * (rho_v1_ll^2 + rho_v2_ll^2) / rho_ll)
757
c_ll = sqrt(gamma_ll * p_ll / rho_ll)
758
p_rr = (gamma_rr - 1) *
759
(rho_e_total_rr - 0.5f0 * (rho_v1_rr^2 + rho_v2_rr^2) / rho_rr)
760
c_rr = sqrt(gamma_rr * p_rr / rho_rr)
761
762
return max(abs(v_ll) + c_ll, abs(v_rr) + c_rr)
763
end
764
765
@inline function max_abs_speeds(u,
766
equations::CompressibleEulerMulticomponentEquations2D)
767
rho_v1, rho_v2, rho_e_total = u
768
769
rho = density(u, equations)
770
v1 = rho_v1 / rho
771
v2 = rho_v2 / rho
772
773
gamma = totalgamma(u, equations)
774
p = (gamma - 1) * (rho_e_total - 0.5f0 * rho * (v1^2 + v2^2))
775
c = sqrt(gamma * p / rho)
776
777
return (abs(v1) + c, abs(v2) + c)
778
end
779
780
@inline function rotate_to_x(u, normal_vector,
781
equations::CompressibleEulerMulticomponentEquations2D)
782
# cos and sin of the angle between the x-axis and the normalized normal_vector are
783
# the normalized vector's x and y coordinates respectively (see unit circle).
784
c = normal_vector[1]
785
s = normal_vector[2]
786
787
# Apply the 2D rotation matrix with normal and tangent directions of the form
788
# [ n_1 n_2 0 0;
789
# t_1 t_2 0 0;
790
# 0 0 1 0
791
# 0 0 0 1]
792
# where t_1 = -n_2 and t_2 = n_1
793
794
densities = @view u[4:end]
795
return SVector(c * u[1] + s * u[2],
796
-s * u[1] + c * u[2],
797
u[3],
798
densities...)
799
end
800
801
# Called inside `FluxRotated` in `numerical_fluxes.jl` so the direction
802
# has been normalized prior to this back-rotation of the state vector
803
@inline function rotate_from_x(u, normal_vector,
804
equations::CompressibleEulerMulticomponentEquations2D)
805
# cos and sin of the angle between the x-axis and the normalized normal_vector are
806
# the normalized vector's x and y coordinates respectively (see unit circle).
807
c = normal_vector[1]
808
s = normal_vector[2]
809
810
# Apply the 2D back-rotation matrix with normal and tangent directions of the form
811
# [ n_1 t_1 0 0;
812
# n_2 t_2 0 0;
813
# 0 0 1 0;
814
# 0 0 0 1 ]
815
# where t_1 = -n_2 and t_2 = n_1
816
817
densities = @view u[4:end]
818
return SVector(c * u[1] - s * u[2],
819
s * u[1] + c * u[2],
820
u[3],
821
densities...)
822
end
823
824
# Convert conservative variables to primitive
825
@inline function cons2prim(u, equations::CompressibleEulerMulticomponentEquations2D)
826
rho_v1, rho_v2, rho_e_total = u
827
828
prim_rho = SVector{ncomponents(equations), real(equations)}(u[i + 3]
829
for i in eachcomponent(equations))
830
831
rho = density(u, equations)
832
v1 = rho_v1 / rho
833
v2 = rho_v2 / rho
834
gamma = totalgamma(u, equations)
835
p = (gamma - 1) * (rho_e_total - 0.5f0 * rho * (v1^2 + v2^2))
836
prim_other = SVector(v1, v2, p)
837
838
return vcat(prim_other, prim_rho)
839
end
840
841
# Convert conservative variables to entropy
842
@inline function cons2entropy(u, equations::CompressibleEulerMulticomponentEquations2D)
843
@unpack cv, gammas, gas_constants = equations
844
rho_v1, rho_v2, rho_e_total = u
845
846
rho = density(u, equations)
847
848
# Multicomponent stuff
849
RealT = eltype(u)
850
help1 = zero(RealT)
851
gas_constant = zero(RealT)
852
for i in eachcomponent(equations)
853
help1 += u[i + 3] * cv[i]
854
gas_constant += gas_constants[i] * (u[i + 3] / rho)
855
end
856
857
v1 = rho_v1 / rho
858
v2 = rho_v2 / rho
859
v_square = v1^2 + v2^2
860
gamma = totalgamma(u, equations)
861
862
p = (gamma - 1) * (rho_e_total - 0.5f0 * rho * v_square)
863
s = log(p) - gamma * log(rho) - log(gas_constant)
864
rho_p = rho / p
865
T = (rho_e_total - 0.5f0 * rho * v_square) / (help1)
866
867
entrop_rho = SVector{ncomponents(equations), real(equations)}((cv[i] *
868
(1 - log(T)) +
869
gas_constants[i] *
870
(1 + log(u[i + 3])) -
871
v_square / (2 * T))
872
for i in eachcomponent(equations))
873
874
w1 = gas_constant * v1 * rho_p
875
w2 = gas_constant * v2 * rho_p
876
w3 = gas_constant * (-rho_p)
877
878
entrop_other = SVector(w1, w2, w3)
879
880
return vcat(entrop_other, entrop_rho)
881
end
882
883
# Convert entropy variables to conservative variables
884
@inline function entropy2cons(w, equations::CompressibleEulerMulticomponentEquations2D)
885
@unpack gammas, gas_constants, cp, cv = equations
886
T = -1 / w[3]
887
v1 = w[1] * T
888
v2 = w[2] * T
889
v_squared = v1^2 + v2^2
890
cons_rho = SVector{ncomponents(equations), real(equations)}(exp((w[i + 3] -
891
cv[i] *
892
(1 - log(T)) +
893
v_squared /
894
(2 * T)) /
895
gas_constants[i] -
896
1)
897
for i in eachcomponent(equations))
898
899
RealT = eltype(w)
900
rho = zero(RealT)
901
help1 = zero(RealT)
902
help2 = zero(RealT)
903
p = zero(RealT)
904
for i in eachcomponent(equations)
905
rho += cons_rho[i]
906
help1 += cons_rho[i] * cv[i] * gammas[i]
907
help2 += cons_rho[i] * cv[i]
908
p += cons_rho[i] * gas_constants[i] * T
909
end
910
u1 = rho * v1
911
u2 = rho * v2
912
gamma = help1 / help2
913
u3 = p / (gamma - 1) + 0.5f0 * rho * v_squared
914
cons_other = SVector(u1, u2, u3)
915
return vcat(cons_other, cons_rho)
916
end
917
918
# Convert primitive to conservative variables
919
@inline function prim2cons(prim, equations::CompressibleEulerMulticomponentEquations2D)
920
@unpack cv, gammas = equations
921
v1, v2, p = prim
922
923
cons_rho = SVector{ncomponents(equations), real(equations)}(prim[i + 3]
924
for i in eachcomponent(equations))
925
rho = density(prim, equations)
926
gamma = totalgamma(prim, equations)
927
928
rho_v1 = rho * v1
929
rho_v2 = rho * v2
930
rho_e_total = p / (gamma - 1) + 0.5f0 * (rho_v1 * v1 + rho_v2 * v2)
931
932
cons_other = SVector(rho_v1, rho_v2, rho_e_total)
933
934
return vcat(cons_other, cons_rho)
935
end
936
937
@inline function total_entropy(u, equations::CompressibleEulerMulticomponentEquations2D)
938
@unpack cv, gammas, gas_constants = equations
939
rho = density(u, equations)
940
T = temperature(u, equations)
941
942
RealT = eltype(u)
943
total_entropy = zero(RealT)
944
for i in eachcomponent(equations)
945
total_entropy -= u[i + 3] * (cv[i] * log(T) - gas_constants[i] * log(u[i + 3]))
946
end
947
948
return total_entropy
949
end
950
951
@inline function temperature(u, equations::CompressibleEulerMulticomponentEquations2D)
952
@unpack cv, gammas, gas_constants = equations
953
954
rho_v1, rho_v2, rho_e_total = u
955
956
rho = density(u, equations)
957
RealT = eltype(u)
958
help1 = zero(RealT)
959
960
for i in eachcomponent(equations)
961
help1 += u[i + 3] * cv[i]
962
end
963
964
v1 = rho_v1 / rho
965
v2 = rho_v2 / rho
966
v_square = v1^2 + v2^2
967
T = (rho_e_total - 0.5f0 * rho * v_square) / help1
968
969
return T
970
end
971
972
"""
973
totalgamma(u, equations::CompressibleEulerMulticomponentEquations2D)
974
975
Function that calculates the total gamma out of all partial gammas using the
976
partial density fractions as well as the partial specific heats at constant volume.
977
"""
978
@inline function totalgamma(u, equations::CompressibleEulerMulticomponentEquations2D)
979
@unpack cv, gammas = equations
980
981
RealT = eltype(u)
982
help1 = zero(RealT)
983
help2 = zero(RealT)
984
985
for i in eachcomponent(equations)
986
help1 += u[i + 3] * cv[i] * gammas[i]
987
help2 += u[i + 3] * cv[i]
988
end
989
990
return help1 / help2
991
end
992
993
@inline function pressure(u, equations::CompressibleEulerMulticomponentEquations2D)
994
rho_v1, rho_v2, rho_e_total = u
995
996
rho = density(u, equations)
997
gamma = totalgamma(u, equations)
998
999
p = (gamma - 1) * (rho_e_total - 0.5f0 * (rho_v1^2 + rho_v2^2) / rho)
1000
1001
return p
1002
end
1003
1004
@inline function density_pressure(u,
1005
equations::CompressibleEulerMulticomponentEquations2D)
1006
rho_v1, rho_v2, rho_e_total = u
1007
1008
rho = density(u, equations)
1009
gamma = totalgamma(u, equations)
1010
rho_times_p = (gamma - 1) * (rho * rho_e_total - 0.5f0 * (rho_v1^2 + rho_v2^2))
1011
1012
return rho_times_p
1013
end
1014
1015
@inline function density(u, equations::CompressibleEulerMulticomponentEquations2D)
1016
RealT = eltype(u)
1017
rho = zero(RealT)
1018
1019
for i in eachcomponent(equations)
1020
rho += u[i + 3]
1021
end
1022
1023
return rho
1024
end
1025
1026
@inline function densities(u, v, equations::CompressibleEulerMulticomponentEquations2D)
1027
return SVector{ncomponents(equations), real(equations)}(u[i + 3] * v
1028
for i in eachcomponent(equations))
1029
end
1030
1031
@inline function velocity(u, equations::CompressibleEulerMulticomponentEquations2D)
1032
rho = density(u, equations)
1033
v1 = u[1] / rho
1034
v2 = u[2] / rho
1035
return SVector(v1, v2)
1036
end
1037
1038
@inline function velocity(u, orientation::Int,
1039
equations::CompressibleEulerMulticomponentEquations2D)
1040
rho = density(u, equations)
1041
v = u[orientation] / rho
1042
return v
1043
end
1044
end # @muladd
1045
1046