Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/equations/polytropic_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
PolytropicEulerEquations2D(gamma, kappa)
10
11
The polytropic Euler equations
12
```math
13
\frac{\partial}{\partial t}
14
\begin{pmatrix}
15
\rho \\ \rho v_1 \\ \rho v_2
16
\end{pmatrix}
17
+
18
\frac{\partial}{\partial x}
19
\begin{pmatrix}
20
\rho v_1 \\ \rho v_1^2 + \kappa\rho^\gamma \\ \rho v_1 v_2
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 + \kappa\rho^\gamma
26
\end{pmatrix}
27
=
28
\begin{pmatrix}
29
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 and ``v_1`` and`v_2` the velocities and
35
```math
36
p = \kappa\rho^\gamma
37
```
38
the pressure, which we replaced using this relation.
39
"""
40
struct PolytropicEulerEquations2D{RealT <: Real} <:
41
AbstractPolytropicEulerEquations{2, 3}
42
gamma::RealT # ratio of specific heats
43
kappa::RealT # fluid scaling factor
44
45
function PolytropicEulerEquations2D(gamma, kappa)
46
gamma_, kappa_ = promote(gamma, kappa)
47
new{typeof(gamma_)}(gamma_, kappa_)
48
end
49
end
50
51
function varnames(::typeof(cons2cons), ::PolytropicEulerEquations2D)
52
("rho", "rho_v1", "rho_v2")
53
end
54
varnames(::typeof(cons2prim), ::PolytropicEulerEquations2D) = ("rho", "v1", "v2")
55
56
"""
57
initial_condition_convergence_test(x, t, equations::PolytropicEulerEquations2D)
58
59
Manufactured smooth initial condition used for convergence tests
60
in combination with [`source_terms_convergence_test`](@ref).
61
"""
62
function initial_condition_convergence_test(x, t, equations::PolytropicEulerEquations2D)
63
# manufactured initial condition from Winters (2019) [0.1007/s10543-019-00789-w]
64
# domain must be set to [0, 1] x [0, 1]
65
h = 8 + cospi(2 * x[1]) * sinpi(2 * x[2]) * cospi(2 * t)
66
67
return SVector(h, h / 2, 3 * h / 2)
68
end
69
70
"""
71
source_terms_convergence_test(u, x, t, equations::PolytropicEulerEquations2D)
72
73
Source terms used for convergence tests in combination with
74
[`initial_condition_convergence_test`](@ref).
75
"""
76
@inline function source_terms_convergence_test(u, x, t,
77
equations::PolytropicEulerEquations2D)
78
# Residual from Winters (2019) [0.1007/s10543-019-00789-w] eq. (5.2).
79
RealT = eltype(u)
80
h = 8 + cospi(2 * x[1]) * sinpi(2 * x[2]) * cospi(2 * t)
81
h_t = -2 * convert(RealT, pi) * cospi(2 * x[1]) * sinpi(2 * x[2]) * sinpi(2 * t)
82
h_x = -2 * convert(RealT, pi) * sinpi(2 * x[1]) * sinpi(2 * x[2]) * cospi(2 * t)
83
h_y = 2 * convert(RealT, pi) * cospi(2 * x[1]) * cospi(2 * x[2]) * cospi(2 * t)
84
85
rho_x = h_x
86
rho_y = h_y
87
88
b = equations.kappa * equations.gamma * h^(equations.gamma - 1)
89
90
r_1 = h_t + h_x / 2 + 3 * h_y / 2
91
r_2 = h_t / 2 + h_x / 4 + b * rho_x + 3 * h_y / 4
92
r_3 = 3 * h_t / 2 + 3 * h_x / 4 + 9 * h_y / 4 + b * rho_y
93
94
return SVector(r_1, r_2, r_3)
95
end
96
97
"""
98
initial_condition_weak_blast_wave(x, t, equations::PolytropicEulerEquations2D)
99
100
A weak blast wave adapted from
101
- Sebastian Hennemann, Gregor J. Gassner (2020)
102
A provably entropy stable subcell shock capturing approach for high order split form DG
103
[arXiv: 2008.12044](https://arxiv.org/abs/2008.12044)
104
"""
105
function initial_condition_weak_blast_wave(x, t, equations::PolytropicEulerEquations2D)
106
# Adapted MHD version of the weak blast wave from Hennemann & Gassner JCP paper 2020 (Sec. 6.3)
107
# Set up polar coordinates
108
inicenter = (0, 0)
109
x_norm = x[1] - inicenter[1]
110
y_norm = x[2] - inicenter[2]
111
r = sqrt(x_norm^2 + y_norm^2)
112
phi = atan(y_norm, x_norm)
113
114
# Calculate primitive variables
115
RealT = eltype(x)
116
rho = r > 0.5f0 ? one(RealT) : convert(RealT, 1.1691)
117
v1 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * cos(phi)
118
v2 = r > 0.5f0 ? zero(RealT) : convert(RealT, 0.1882) * sin(phi)
119
120
return prim2cons(SVector(rho, v1, v2), equations)
121
end
122
123
# Calculate 1D flux for a single point in the normal direction
124
# Note, this directional vector is not normalized
125
@inline function flux(u, normal_direction::AbstractVector,
126
equations::PolytropicEulerEquations2D)
127
rho, v1, v2 = cons2prim(u, equations)
128
p = pressure(u, equations)
129
130
v_normal = v1 * normal_direction[1] + v2 * normal_direction[2]
131
rho_v_normal = rho * v_normal
132
f1 = rho_v_normal
133
f2 = rho_v_normal * v1 + p * normal_direction[1]
134
f3 = rho_v_normal * v2 + p * normal_direction[2]
135
return SVector(f1, f2, f3)
136
end
137
138
# Calculate 1D flux for a single point
139
@inline function flux(u, orientation::Integer, equations::PolytropicEulerEquations2D)
140
_, v1, v2 = cons2prim(u, equations)
141
p = pressure(u, equations)
142
143
rho_v1 = u[2]
144
rho_v2 = u[3]
145
146
if orientation == 1
147
f1 = rho_v1
148
f2 = rho_v1 * v1 + p
149
f3 = rho_v1 * v2
150
else
151
f1 = rho_v2
152
f2 = rho_v2 * v1
153
f3 = rho_v2 * v2 + p
154
end
155
return SVector(f1, f2, f3)
156
end
157
158
"""
159
flux_winters_etal(u_ll, u_rr, orientation_or_normal_direction,
160
equations::PolytropicEulerEquations2D)
161
162
Entropy conserving two-point flux for isothermal or polytropic gases.
163
Requires a special weighted Stolarsky mean for the evaluation of the density
164
denoted here as `stolarsky_mean`. Note, for isothermal gases where `gamma = 1`
165
this `stolarsky_mean` becomes the [`ln_mean`](@ref).
166
167
For details see Section 3.2 of the following reference
168
- Andrew R. Winters, Christof Czernik, Moritz B. Schily & Gregor J. Gassner (2020)
169
Entropy stable numerical approximations for the isothermal and polytropic
170
Euler equations
171
[DOI: 10.1007/s10543-019-00789-w](https://doi.org/10.1007/s10543-019-00789-w)
172
"""
173
@inline function flux_winters_etal(u_ll, u_rr, normal_direction::AbstractVector,
174
equations::PolytropicEulerEquations2D)
175
# Unpack left and right state
176
rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations)
177
rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations)
178
p_ll = equations.kappa * rho_ll^equations.gamma
179
p_rr = equations.kappa * rho_rr^equations.gamma
180
v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2]
181
v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2]
182
183
# Compute the necessary mean values
184
if equations.gamma == 1 # isothermal gas
185
rho_mean = ln_mean(rho_ll, rho_rr)
186
else # equations.gamma > 1 # polytropic gas
187
rho_mean = stolarsky_mean(rho_ll, rho_rr, equations.gamma)
188
end
189
v1_avg = 0.5f0 * (v1_ll + v1_rr)
190
v2_avg = 0.5f0 * (v2_ll + v2_rr)
191
p_avg = 0.5f0 * (p_ll + p_rr)
192
193
# Calculate fluxes depending on normal_direction
194
f1 = rho_mean * 0.5f0 * (v_dot_n_ll + v_dot_n_rr)
195
f2 = f1 * v1_avg + p_avg * normal_direction[1]
196
f3 = f1 * v2_avg + p_avg * normal_direction[2]
197
198
return SVector(f1, f2, f3)
199
end
200
201
@inline function flux_winters_etal(u_ll, u_rr, orientation::Integer,
202
equations::PolytropicEulerEquations2D)
203
# Unpack left and right state
204
rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations)
205
rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations)
206
p_ll = equations.kappa * rho_ll^equations.gamma
207
p_rr = equations.kappa * rho_rr^equations.gamma
208
209
# Compute the necessary mean values
210
if equations.gamma == 1 # isothermal gas
211
rho_mean = ln_mean(rho_ll, rho_rr)
212
else # equations.gamma > 1 # polytropic gas
213
rho_mean = stolarsky_mean(rho_ll, rho_rr, equations.gamma)
214
end
215
v1_avg = 0.5f0 * (v1_ll + v1_rr)
216
v2_avg = 0.5f0 * (v2_ll + v2_rr)
217
p_avg = 0.5f0 * (p_ll + p_rr)
218
219
if orientation == 1 # x-direction
220
f1 = rho_mean * 0.5f0 * (v1_ll + v1_rr)
221
f2 = f1 * v1_avg + p_avg
222
f3 = f1 * v2_avg
223
else # y-direction
224
f1 = rho_mean * 0.5f0 * (v2_ll + v2_rr)
225
f2 = f1 * v1_avg
226
f3 = f1 * v2_avg + p_avg
227
end
228
229
return SVector(f1, f2, f3)
230
end
231
232
@inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,
233
equations::PolytropicEulerEquations2D)
234
rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations)
235
rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations)
236
p_ll = equations.kappa * rho_ll^equations.gamma
237
p_rr = equations.kappa * rho_rr^equations.gamma
238
239
v_normal_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2]
240
v_normal_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2]
241
242
norm_ = norm(normal_direction)
243
# The v_normals are already scaled by the norm
244
lambda_min = v_normal_ll - sqrt(equations.gamma * p_ll / rho_ll) * norm_
245
lambda_max = v_normal_rr + sqrt(equations.gamma * p_rr / rho_rr) * norm_
246
247
return lambda_min, lambda_max
248
end
249
250
# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
251
@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,
252
equations::PolytropicEulerEquations2D)
253
rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations)
254
rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations)
255
# Pressure for polytropic Euler
256
p_ll = equations.kappa * rho_ll^equations.gamma
257
p_rr = equations.kappa * rho_rr^equations.gamma
258
259
c_ll = sqrt(equations.gamma * p_ll / rho_ll)
260
c_rr = sqrt(equations.gamma * p_rr / rho_rr)
261
262
if orientation == 1 # x-direction
263
λ_min = min(v1_ll - c_ll, v1_rr - c_rr)
264
λ_max = max(v1_ll + c_ll, v1_rr + c_rr)
265
else # y-direction
266
λ_min = min(v2_ll - c_ll, v2_rr - c_rr)
267
λ_max = max(v2_ll + c_ll, v2_rr + c_rr)
268
end
269
270
return λ_min, λ_max
271
end
272
273
# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
274
@inline function min_max_speed_davis(u_ll, u_rr, normal_direction::AbstractVector,
275
equations::PolytropicEulerEquations2D)
276
rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations)
277
rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations)
278
# Pressure for polytropic Euler
279
p_ll = equations.kappa * rho_ll^equations.gamma
280
p_rr = equations.kappa * rho_rr^equations.gamma
281
282
norm_ = norm(normal_direction)
283
284
c_ll = sqrt(equations.gamma * p_ll / rho_ll) * norm_
285
c_rr = sqrt(equations.gamma * p_rr / rho_rr) * norm_
286
287
v_normal_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2]
288
v_normal_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2]
289
290
# The v_normals are already scaled by the norm
291
λ_min = min(v_normal_ll - c_ll, v_normal_rr - c_rr)
292
λ_max = max(v_normal_ll + c_ll, v_normal_rr + c_rr)
293
294
return λ_min, λ_max
295
end
296
297
@inline function max_abs_speeds(u, equations::PolytropicEulerEquations2D)
298
rho, v1, v2 = cons2prim(u, equations)
299
c = sqrt(equations.gamma * equations.kappa * rho^(equations.gamma - 1))
300
301
return abs(v1) + c, abs(v2) + c
302
end
303
304
# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation as the
305
# maximum velocity magnitude plus the maximum speed of sound
306
@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,
307
equations::PolytropicEulerEquations2D)
308
rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations)
309
rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations)
310
311
# Get the velocity value in the appropriate direction
312
if orientation == 1
313
v_ll = v1_ll
314
v_rr = v1_rr
315
else # orientation == 2
316
v_ll = v2_ll
317
v_rr = v2_rr
318
end
319
# Calculate sound speeds (we have p = kappa * rho^gamma)
320
c_ll = sqrt(equations.gamma * equations.kappa * rho_ll^(equations.gamma - 1))
321
c_rr = sqrt(equations.gamma * equations.kappa * rho_rr^(equations.gamma - 1))
322
323
return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr)
324
end
325
326
@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,
327
equations::PolytropicEulerEquations2D)
328
rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations)
329
rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations)
330
331
# Calculate normal velocities and sound speed (we have p = kappa * rho^gamma)
332
# left
333
v_ll = (v1_ll * normal_direction[1] +
334
v2_ll * normal_direction[2])
335
c_ll = sqrt(equations.gamma * equations.kappa * rho_ll^(equations.gamma - 1))
336
# right
337
v_rr = (v1_rr * normal_direction[1] +
338
v2_rr * normal_direction[2])
339
c_rr = sqrt(equations.gamma * equations.kappa * rho_rr^(equations.gamma - 1))
340
341
return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) * norm(normal_direction)
342
end
343
344
# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`
345
@inline function max_abs_speed(u_ll, u_rr, orientation::Integer,
346
equations::PolytropicEulerEquations2D)
347
rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations)
348
rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations)
349
350
# Get the velocity value in the appropriate direction
351
if orientation == 1
352
v_ll = v1_ll
353
v_rr = v1_rr
354
else # orientation == 2
355
v_ll = v2_ll
356
v_rr = v2_rr
357
end
358
# Calculate sound speeds (we have p = kappa * rho^gamma)
359
c_ll = sqrt(equations.gamma * equations.kappa * rho_ll^(equations.gamma - 1))
360
c_rr = sqrt(equations.gamma * equations.kappa * rho_rr^(equations.gamma - 1))
361
362
return max(abs(v_ll) + c_ll, abs(v_rr) + c_rr)
363
end
364
365
# Less "cautious", i.e., less overestimating `λ_max` compared to `max_abs_speed_naive`
366
@inline function max_abs_speed(u_ll, u_rr, normal_direction::AbstractVector,
367
equations::PolytropicEulerEquations2D)
368
rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations)
369
rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations)
370
371
# Calculate normal velocities and sound speed (we have p = kappa * rho^gamma)
372
# left
373
v_ll = (v1_ll * normal_direction[1] +
374
v2_ll * normal_direction[2])
375
c_ll = sqrt(equations.gamma * equations.kappa * rho_ll^(equations.gamma - 1))
376
# right
377
v_rr = (v1_rr * normal_direction[1] +
378
v2_rr * normal_direction[2])
379
c_rr = sqrt(equations.gamma * equations.kappa * rho_rr^(equations.gamma - 1))
380
381
norm_ = norm(normal_direction)
382
return max(abs(v_ll) + c_ll * norm_, abs(v_rr) + c_rr * norm_)
383
end
384
385
# Convert conservative variables to primitive
386
@inline function cons2prim(u, equations::PolytropicEulerEquations2D)
387
rho, rho_v1, rho_v2 = u
388
389
v1 = rho_v1 / rho
390
v2 = rho_v2 / rho
391
392
return SVector(rho, v1, v2)
393
end
394
395
# Convert conservative variables to entropy
396
@inline function cons2entropy(u, equations::PolytropicEulerEquations2D)
397
rho, rho_v1, rho_v2 = u
398
399
v1 = rho_v1 / rho
400
v2 = rho_v2 / rho
401
v_square = v1^2 + v2^2
402
p = pressure(u, equations)
403
# Form of the internal energy depends on gas type
404
if equations.gamma == 1 # isothermal gas
405
internal_energy = equations.kappa * log(rho)
406
else # equations.gamma > 1 # polytropic gas
407
internal_energy = equations.kappa * rho^(equations.gamma - 1) /
408
(equations.gamma - 1)
409
end
410
411
w1 = internal_energy + p / rho - 0.5f0 * v_square
412
w2 = v1
413
w3 = v2
414
415
return SVector(w1, w2, w3)
416
end
417
418
# Convert primitive to conservative variables
419
@inline function prim2cons(prim, equations::PolytropicEulerEquations2D)
420
rho, v1, v2 = prim
421
rho_v1 = rho * v1
422
rho_v2 = rho * v2
423
return SVector(rho, rho_v1, rho_v2)
424
end
425
426
@inline function density(u, equations::PolytropicEulerEquations2D)
427
rho = u[1]
428
return rho
429
end
430
431
@inline function velocity(u, equations::PolytropicEulerEquations2D)
432
rho = u[1]
433
v1 = u[2] / rho
434
v2 = u[3] / rho
435
return SVector(v1, v2)
436
end
437
438
@inline function velocity(u, orientation::Int, equations::PolytropicEulerEquations2D)
439
rho = u[1]
440
v = u[orientation + 1] / rho
441
return v
442
end
443
444
@inline function pressure(u, equations::PolytropicEulerEquations2D)
445
rho, _, _ = u
446
p = equations.kappa * rho^equations.gamma
447
return p
448
end
449
end # @muladd
450
451