Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/equations/linearized_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
LinearizedEulerEquations2D(v_mean_global, c_mean_global, rho_mean_global)
10
11
Linearized Euler equations in two space dimensions. The equations are given by
12
```math
13
\partial_t
14
\begin{pmatrix}
15
\rho' \\ v_1' \\ v_2' \\ p'
16
\end{pmatrix}
17
+
18
\partial_x
19
\begin{pmatrix}
20
\bar{\rho} v_1' + \bar{v_1} \rho ' \\ \bar{v_1} v_1' + \frac{p'}{\bar{\rho}} \\ \bar{v_1} v_2' \\ \bar{v_1} p' + c^2 \bar{\rho} v_1'
21
\end{pmatrix}
22
+
23
\partial_y
24
\begin{pmatrix}
25
\bar{\rho} v_2' + \bar{v_2} \rho ' \\ \bar{v_2} v_1' \\ \bar{v_2} v_2' + \frac{p'}{\bar{\rho}} \\ \bar{v_2} p' + c^2 \bar{\rho} v_2'
26
\end{pmatrix}
27
=
28
\begin{pmatrix}
29
0 \\ 0 \\ 0 \\ 0
30
\end{pmatrix}
31
```
32
The bar ``\bar{(\cdot)}`` indicates uniform mean flow variables and ``c`` is the speed of sound.
33
The unknowns are the acoustic velocities ``v' = (v_1', v_2')``, the pressure ``p'`` and the density ``\rho'``.
34
"""
35
struct LinearizedEulerEquations2D{RealT <: Real} <:
36
AbstractLinearizedEulerEquations{2, 4}
37
v_mean_global::SVector{2, RealT}
38
c_mean_global::RealT
39
rho_mean_global::RealT
40
end
41
42
function LinearizedEulerEquations2D(v_mean_global::NTuple{2, <:Real},
43
c_mean_global::Real, rho_mean_global::Real)
44
if rho_mean_global < 0
45
throw(ArgumentError("rho_mean_global must be non-negative"))
46
elseif c_mean_global < 0
47
throw(ArgumentError("c_mean_global must be non-negative"))
48
end
49
50
return LinearizedEulerEquations2D(SVector(v_mean_global), c_mean_global,
51
rho_mean_global)
52
end
53
54
function LinearizedEulerEquations2D(; v_mean_global::NTuple{2, <:Real},
55
c_mean_global::Real, rho_mean_global::Real)
56
return LinearizedEulerEquations2D(v_mean_global, c_mean_global,
57
rho_mean_global)
58
end
59
60
function varnames(::typeof(cons2cons), ::LinearizedEulerEquations2D)
61
("rho_prime", "v1_prime", "v2_prime", "p_prime")
62
end
63
function varnames(::typeof(cons2prim), ::LinearizedEulerEquations2D)
64
("rho_prime", "v1_prime", "v2_prime", "p_prime")
65
end
66
67
"""
68
initial_condition_convergence_test(x, t, equations::LinearizedEulerEquations2D)
69
70
A smooth initial condition used for convergence tests.
71
"""
72
function initial_condition_convergence_test(x, t, equations::LinearizedEulerEquations2D)
73
rho_prime = -cospi(2 * t) * (sinpi(2 * x[1]) + sinpi(2 * x[2]))
74
v1_prime = sinpi(2 * t) * cospi(2 * x[1])
75
v2_prime = sinpi(2 * t) * cospi(2 * x[2])
76
p_prime = rho_prime
77
78
return SVector(rho_prime, v1_prime, v2_prime, p_prime)
79
end
80
81
"""
82
boundary_condition_wall(u_inner, orientation, direction, x, t, surface_flux_function,
83
equations::LinearizedEulerEquations2D)
84
85
Boundary conditions for a solid wall.
86
"""
87
function boundary_condition_wall(u_inner, orientation, direction, x, t,
88
surface_flux_function,
89
equations::LinearizedEulerEquations2D)
90
# Boundary state is equal to the inner state except for the velocity. For boundaries
91
# in the -x/+x direction, we multiply the velocity in the x direction by -1.
92
# Similarly, for boundaries in the -y/+y direction, we multiply the velocity in the
93
# y direction by -1
94
if direction in (1, 2) # x direction
95
u_boundary = SVector(u_inner[1], -u_inner[2], u_inner[3], u_inner[4])
96
else # y direction
97
u_boundary = SVector(u_inner[1], u_inner[2], -u_inner[3], u_inner[4])
98
end
99
100
# Calculate boundary flux
101
if iseven(direction) # u_inner is "left" of boundary, u_boundary is "right" of boundary
102
flux = surface_flux_function(u_inner, u_boundary, orientation, equations)
103
else # u_boundary is "left" of boundary, u_inner is "right" of boundary
104
flux = surface_flux_function(u_boundary, u_inner, orientation, equations)
105
end
106
107
return flux
108
end
109
110
# Calculate 1D flux for a single point
111
@inline function flux(u, orientation::Integer, equations::LinearizedEulerEquations2D)
112
@unpack v_mean_global, c_mean_global, rho_mean_global = equations
113
rho_prime, v1_prime, v2_prime, p_prime = u
114
if orientation == 1
115
f1 = v_mean_global[1] * rho_prime + rho_mean_global * v1_prime
116
f2 = v_mean_global[1] * v1_prime + p_prime / rho_mean_global
117
f3 = v_mean_global[1] * v2_prime
118
f4 = v_mean_global[1] * p_prime + c_mean_global^2 * rho_mean_global * v1_prime
119
else
120
f1 = v_mean_global[2] * rho_prime + rho_mean_global * v2_prime
121
f2 = v_mean_global[2] * v1_prime
122
f3 = v_mean_global[2] * v2_prime + p_prime / rho_mean_global
123
f4 = v_mean_global[2] * p_prime + c_mean_global^2 * rho_mean_global * v2_prime
124
end
125
126
return SVector(f1, f2, f3, f4)
127
end
128
129
# Calculate 1D flux for a single point
130
@inline function flux(u, normal_direction::AbstractVector,
131
equations::LinearizedEulerEquations2D)
132
@unpack v_mean_global, c_mean_global, rho_mean_global = equations
133
rho_prime, v1_prime, v2_prime, p_prime = u
134
135
v_mean_normal = v_mean_global[1] * normal_direction[1] +
136
v_mean_global[2] * normal_direction[2]
137
v_prime_normal = v1_prime * normal_direction[1] + v2_prime * normal_direction[2]
138
139
f1 = v_mean_normal * rho_prime + rho_mean_global * v_prime_normal
140
f2 = v_mean_normal * v1_prime + normal_direction[1] * p_prime / rho_mean_global
141
f3 = v_mean_normal * v2_prime + normal_direction[2] * p_prime / rho_mean_global
142
f4 = v_mean_normal * p_prime + c_mean_global^2 * rho_mean_global * v_prime_normal
143
144
return SVector(f1, f2, f3, f4)
145
end
146
147
@inline have_constant_speed(::LinearizedEulerEquations2D) = True()
148
149
@inline function max_abs_speeds(equations::LinearizedEulerEquations2D)
150
@unpack v_mean_global, c_mean_global = equations
151
return abs(v_mean_global[1]) + c_mean_global, abs(v_mean_global[2]) + c_mean_global
152
end
153
154
@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,
155
equations::LinearizedEulerEquations2D)
156
@unpack v_mean_global, c_mean_global = equations
157
if orientation == 1
158
return abs(v_mean_global[1]) + c_mean_global
159
else # orientation == 2
160
return abs(v_mean_global[2]) + c_mean_global
161
end
162
end
163
164
@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,
165
equations::LinearizedEulerEquations2D)
166
@unpack v_mean_global, c_mean_global = equations
167
v_mean_normal = normal_direction[1] * v_mean_global[1] +
168
normal_direction[2] * v_mean_global[2]
169
return abs(v_mean_normal) + c_mean_global * norm(normal_direction)
170
end
171
172
@doc raw"""
173
flux_godunov(u_ll, u_rr, orientation_or_normal_direction,
174
equations::LinearizedEulerEquations2D)
175
176
An upwind flux for the linearized Euler equations based on diagonalization of the physical
177
flux matrix. Given the physical flux ``Au``, ``A=T \Lambda T^{-1}`` with
178
``\Lambda`` being a diagonal matrix that holds the eigenvalues of ``A``, decompose
179
``\Lambda = \Lambda^+ + \Lambda^-`` where ``\Lambda^+`` and ``\Lambda^-`` are diagonal
180
matrices holding the positive and negative eigenvalues of ``A``, respectively. Then for
181
left and right states ``u_L, u_R``, the numerical flux calculated by this function is given
182
by ``A^+ u_L + A^- u_R`` where ``A^{\pm} = T \Lambda^{\pm} T^{-1}``.
183
184
The diagonalization of the flux matrix can be found in
185
- R. F. Warming, Richard M. Beam and B. J. Hyett (1975)
186
Diagonalization and simultaneous symmetrization of the gas-dynamic matrices
187
[DOI: 10.1090/S0025-5718-1975-0388967-5](https://doi.org/10.1090/S0025-5718-1975-0388967-5)
188
"""
189
@inline function flux_godunov(u_ll, u_rr, orientation::Integer,
190
equations::LinearizedEulerEquations2D)
191
@unpack v_mean_global, rho_mean_global, c_mean_global = equations
192
v1_mean = v_mean_global[1]
193
v2_mean = v_mean_global[2]
194
195
rho_prime_ll, v1_prime_ll, v2_prime_ll, p_prime_ll = u_ll
196
rho_prime_rr, v1_prime_rr, v2_prime_rr, p_prime_rr = u_rr
197
198
if orientation == 1
199
# Eigenvalues of the flux matrix
200
lambda1 = v1_mean
201
lambda2 = v1_mean - c_mean_global
202
lambda3 = v1_mean + c_mean_global
203
204
lambda1_p = positive_part(lambda1)
205
lambda2_p = positive_part(lambda2)
206
lambda3_p = positive_part(lambda3)
207
lambda2p3_half_p = 0.5f0 * (lambda2_p + lambda3_p)
208
lambda3m2_half_p = 0.5f0 * (lambda3_p - lambda2_p)
209
210
lambda1_m = negative_part(lambda1)
211
lambda2_m = negative_part(lambda2)
212
lambda3_m = negative_part(lambda3)
213
lambda2p3_half_m = 0.5f0 * (lambda2_m + lambda3_m)
214
lambda3m2_half_m = 0.5f0 * (lambda3_m - lambda2_m)
215
216
f1p = (lambda1_p * rho_prime_ll +
217
lambda3m2_half_p / c_mean_global * rho_mean_global * v1_prime_ll +
218
(lambda2p3_half_p - lambda1_p) / c_mean_global^2 * p_prime_ll)
219
f2p = (lambda2p3_half_p * v1_prime_ll +
220
lambda3m2_half_p / c_mean_global * p_prime_ll / rho_mean_global)
221
f3p = lambda1_p * v2_prime_ll
222
f4p = (lambda3m2_half_p * c_mean_global * rho_mean_global * v1_prime_ll +
223
lambda2p3_half_p * p_prime_ll)
224
225
f1m = (lambda1_m * rho_prime_rr +
226
lambda3m2_half_m / c_mean_global * rho_mean_global * v1_prime_rr +
227
(lambda2p3_half_m - lambda1_m) / c_mean_global^2 * p_prime_rr)
228
f2m = (lambda2p3_half_m * v1_prime_rr +
229
lambda3m2_half_m / c_mean_global * p_prime_rr / rho_mean_global)
230
f3m = lambda1_m * v2_prime_rr
231
f4m = (lambda3m2_half_m * c_mean_global * rho_mean_global * v1_prime_rr +
232
lambda2p3_half_m * p_prime_rr)
233
234
f1 = f1p + f1m
235
f2 = f2p + f2m
236
f3 = f3p + f3m
237
f4 = f4p + f4m
238
else # orientation == 2
239
# Eigenvalues of the flux matrix
240
lambda1 = v2_mean
241
lambda2 = v2_mean - c_mean_global
242
lambda3 = v2_mean + c_mean_global
243
244
lambda1_p = positive_part(lambda1)
245
lambda2_p = positive_part(lambda2)
246
lambda3_p = positive_part(lambda3)
247
lambda2p3_half_p = 0.5f0 * (lambda2_p + lambda3_p)
248
lambda3m2_half_p = 0.5f0 * (lambda3_p - lambda2_p)
249
250
lambda1_m = negative_part(lambda1)
251
lambda2_m = negative_part(lambda2)
252
lambda3_m = negative_part(lambda3)
253
lambda2p3_half_m = 0.5f0 * (lambda2_m + lambda3_m)
254
lambda3m2_half_m = 0.5f0 * (lambda3_m - lambda2_m)
255
256
f1p = (lambda1_p * rho_prime_ll +
257
lambda3m2_half_p / c_mean_global * rho_mean_global * v2_prime_ll +
258
(lambda2p3_half_p - lambda1_p) / c_mean_global^2 * p_prime_ll)
259
f2p = lambda1_p * v1_prime_ll
260
f3p = (lambda2p3_half_p * v2_prime_ll +
261
lambda3m2_half_p / c_mean_global * p_prime_ll / rho_mean_global)
262
f4p = (lambda3m2_half_p * c_mean_global * rho_mean_global * v2_prime_ll +
263
lambda2p3_half_p * p_prime_ll)
264
265
f1m = (lambda1_m * rho_prime_rr +
266
lambda3m2_half_m / c_mean_global * rho_mean_global * v2_prime_rr +
267
(lambda2p3_half_m - lambda1_m) / c_mean_global^2 * p_prime_rr)
268
f2m = lambda1_m * v1_prime_rr
269
f3m = (lambda2p3_half_m * v2_prime_rr +
270
lambda3m2_half_m / c_mean_global * p_prime_rr / rho_mean_global)
271
f4m = (lambda3m2_half_m * c_mean_global * rho_mean_global * v2_prime_rr +
272
lambda2p3_half_m * p_prime_rr)
273
274
f1 = f1p + f1m
275
f2 = f2p + f2m
276
f3 = f3p + f3m
277
f4 = f4p + f4m
278
end
279
280
return SVector(f1, f2, f3, f4)
281
end
282
283
@inline function flux_godunov(u_ll, u_rr, normal_direction::AbstractVector,
284
equations::LinearizedEulerEquations2D)
285
@unpack v_mean_global, rho_mean_global, c_mean_global = equations
286
rho_prime_ll, v1_prime_ll, v2_prime_ll, p_prime_ll = u_ll
287
rho_prime_rr, v1_prime_rr, v2_prime_rr, p_prime_rr = u_rr
288
289
# Do not use `normalize` since we use `norm_` later to scale the eigenvalues
290
norm_ = norm(normal_direction)
291
normal_vector = normal_direction / norm_
292
293
# Use normalized vector here, scaling is applied via eigenvalues of the flux matrix
294
v_mean_normal = v_mean_global[1] * normal_vector[1] +
295
v_mean_global[2] * normal_vector[2]
296
v_prime_normal_ll = v1_prime_ll * normal_vector[1] + v2_prime_ll * normal_vector[2]
297
v_prime_normal_rr = v1_prime_rr * normal_vector[1] + v2_prime_rr * normal_vector[2]
298
299
# Eigenvalues of the flux matrix
300
lambda1 = v_mean_normal * norm_
301
lambda2 = (v_mean_normal - c_mean_global) * norm_
302
lambda3 = (v_mean_normal + c_mean_global) * norm_
303
304
lambda1_p = positive_part(lambda1)
305
lambda2_p = positive_part(lambda2)
306
lambda3_p = positive_part(lambda3)
307
lambda2p3_half_p = 0.5f0 * (lambda2_p + lambda3_p)
308
lambda3m2_half_p = 0.5f0 * (lambda3_p - lambda2_p)
309
310
lambda1_m = negative_part(lambda1)
311
lambda2_m = negative_part(lambda2)
312
lambda3_m = negative_part(lambda3)
313
lambda2p3_half_m = 0.5f0 * (lambda2_m + lambda3_m)
314
lambda3m2_half_m = 0.5f0 * (lambda3_m - lambda2_m)
315
316
f1p = (lambda1_p * rho_prime_ll +
317
lambda3m2_half_p / c_mean_global * rho_mean_global * v_prime_normal_ll +
318
(lambda2p3_half_p - lambda1_p) / c_mean_global^2 * p_prime_ll)
319
f2p = (((lambda1_p * normal_vector[2]^2 +
320
lambda2p3_half_p * normal_vector[1]^2) * v1_prime_ll +
321
(lambda2p3_half_p - lambda1_p) * prod(normal_vector) * v2_prime_ll) +
322
lambda3m2_half_p / c_mean_global * normal_vector[1] * p_prime_ll /
323
rho_mean_global)
324
f3p = (((lambda1_p * normal_vector[1]^2 +
325
lambda2p3_half_p * normal_vector[2]^2) * v2_prime_ll +
326
(lambda2p3_half_p - lambda1_p) * prod(normal_vector) * v1_prime_ll) +
327
lambda3m2_half_p / c_mean_global * normal_vector[2] * p_prime_ll /
328
rho_mean_global)
329
f4p = (lambda3m2_half_p * c_mean_global * rho_mean_global * v_prime_normal_ll +
330
lambda2p3_half_p * p_prime_ll)
331
332
f1m = (lambda1_m * rho_prime_rr +
333
lambda3m2_half_m / c_mean_global * rho_mean_global * v_prime_normal_rr +
334
(lambda2p3_half_m - lambda1_m) / c_mean_global^2 * p_prime_rr)
335
f2m = (((lambda1_m * normal_vector[2]^2 +
336
lambda2p3_half_m * normal_vector[1]^2) * v1_prime_rr +
337
(lambda2p3_half_m - lambda1_m) * prod(normal_vector) * v2_prime_rr) +
338
lambda3m2_half_m / c_mean_global * normal_vector[1] * p_prime_rr /
339
rho_mean_global)
340
f3m = (((lambda1_m * normal_vector[1]^2 +
341
lambda2p3_half_m * normal_vector[2]^2) * v2_prime_rr +
342
(lambda2p3_half_m - lambda1_m) * prod(normal_vector) * v1_prime_rr) +
343
lambda3m2_half_m / c_mean_global * normal_vector[2] * p_prime_rr /
344
rho_mean_global)
345
f4m = (lambda3m2_half_m * c_mean_global * rho_mean_global * v_prime_normal_rr +
346
lambda2p3_half_m * p_prime_rr)
347
348
f1 = f1p + f1m
349
f2 = f2p + f2m
350
f3 = f3p + f3m
351
f4 = f4p + f4m
352
353
return SVector(f1, f2, f3, f4)
354
end
355
356
# Calculate estimate for minimum and maximum wave speeds for HLL-type fluxes
357
@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer,
358
equations::LinearizedEulerEquations2D)
359
min_max_speed_davis(u_ll, u_rr, orientation, equations)
360
end
361
362
@inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,
363
equations::LinearizedEulerEquations2D)
364
min_max_speed_davis(u_ll, u_rr, normal_direction, equations)
365
end
366
367
# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
368
@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,
369
equations::LinearizedEulerEquations2D)
370
@unpack v_mean_global, c_mean_global = equations
371
372
λ_min = v_mean_global[orientation] - c_mean_global
373
λ_max = v_mean_global[orientation] + c_mean_global
374
375
return λ_min, λ_max
376
end
377
378
@inline function min_max_speed_davis(u_ll, u_rr, normal_direction::AbstractVector,
379
equations::LinearizedEulerEquations2D)
380
@unpack v_mean_global, c_mean_global = equations
381
382
norm_ = norm(normal_direction)
383
384
v_normal = v_mean_global[1] * normal_direction[1] +
385
v_mean_global[2] * normal_direction[2]
386
387
# The v_normals are already scaled by the norm
388
λ_min = v_normal - c_mean_global * norm_
389
λ_max = v_normal + c_mean_global * norm_
390
391
return λ_min, λ_max
392
end
393
394
# Convert conservative variables to primitive
395
@inline cons2prim(u, equations::LinearizedEulerEquations2D) = u
396
@inline cons2entropy(u, ::LinearizedEulerEquations2D) = u
397
end # muladd
398
399