Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/equations/lattice_boltzmann_3d.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
LatticeBoltzmannEquations3D(; Ma, Re, collision_op=collision_bgk,
10
c=1, L=1, rho0=1, u0=nothing, nu=nothing)
11
12
The Lattice-Boltzmann equations
13
```math
14
\partial_t u_\alpha + v_{\alpha,1} \partial_1 u_\alpha + v_{\alpha,2} \partial_2 u_\alpha + v_{\alpha,3} \partial_3 u_\alpha = 0
15
```
16
in three space dimensions for the D3Q27 scheme.
17
18
The characteristic Mach number and Reynolds numbers are specified as `Ma` and `Re`. By the
19
default, the collision operator `collision_op` is set to the BGK model. `c`, `L`, and `rho0`
20
specify the mean thermal molecular velocity, the characteristic length, and the reference density,
21
respectively. They can usually be left to the default values. If desired, instead of the Mach
22
number, one can set the macroscopic reference velocity `u0` directly (`Ma` needs to be set to
23
`nothing` in this case). Likewise, instead of the Reynolds number one can specify the kinematic
24
viscosity `nu` directly (in this case, `Re` needs to be set to `nothing`).
25
26
27
The twenty-seven discrete velocity directions of the D3Q27 scheme are sorted as follows [4]:
28
* plane at `z = -1`:
29
```
30
24 17 21 y
31
┌───┼───┐ │
32
│ │ │
33
10 ┼ 6 ┼ 15 ──── x
34
│ │ ╱
35
└───┼───┘ ╱
36
20 12 26 z
37
```
38
* plane at `z = 0`:
39
```
40
14 3 7 y
41
┌───┼───┐ │
42
│ │ │
43
2 ┼ 27 ┼ 1 ──── x
44
│ │ ╱
45
└───┼───┘ ╱
46
8 4 13 z
47
```
48
* plane at `z = +1`:
49
```
50
25 11 19 y
51
┌───┼───┐ │
52
│ │ │
53
16 ┼ 5 ┼ 9 ──── x
54
│ │ ╱
55
└───┼───┘ ╱
56
22 18 23 z
57
```
58
Note that usually the velocities are numbered from `0` to `26`, where `0` corresponds to the zero
59
velocity. Due to Julia using 1-based indexing, here we use indices from `1` to `27`, where `1`
60
through `26` correspond to the velocity directions in [4] and `27` is the zero velocity.
61
62
The corresponding opposite directions are:
63
* 1 ←→ 2
64
* 2 ←→ 1
65
* 3 ←→ 4
66
* 4 ←→ 3
67
* 5 ←→ 6
68
* 6 ←→ 5
69
* 7 ←→ 8
70
* 8 ←→ 7
71
* 9 ←→ 10
72
* 10 ←→ 9
73
* 11 ←→ 12
74
* 12 ←→ 11
75
* 13 ←→ 14
76
* 14 ←→ 13
77
* 15 ←→ 16
78
* 16 ←→ 15
79
* 17 ←→ 18
80
* 18 ←→ 17
81
* 19 ←→ 20
82
* 20 ←→ 19
83
* 21 ←→ 22
84
* 22 ←→ 21
85
* 23 ←→ 24
86
* 24 ←→ 23
87
* 25 ←→ 26
88
* 26 ←→ 25
89
* 27 ←→ 27
90
91
The main sources for the base implementation were
92
1. Misun Min, Taehun Lee, **A spectral-element discontinuous Galerkin lattice Boltzmann method for
93
nearly incompressible flows**, Journal of Computational Physics 230(1), 2011
94
[doi:10.1016/j.jcp.2010.09.024](https://doi.org/10.1016/j.jcp.2010.09.024)
95
2. Karsten Golly, **Anwendung der Lattice-Boltzmann Discontinuous Galerkin Spectral Element Method
96
(LB-DGSEM) auf laminare und turbulente nahezu inkompressible Strömungen im dreidimensionalen
97
Raum**, Master Thesis, University of Cologne, 2018.
98
3. Dieter Hänel, **Molekulare Gasdynamik**, Springer-Verlag Berlin Heidelberg, 2004
99
[doi:10.1007/3-540-35047-0](https://doi.org/10.1007/3-540-35047-0)
100
4. Timm Krüger et al., **The Lattice Boltzmann Method**, Springer International Publishing, 2017
101
[doi:10.1007/978-3-319-44649-3](https://doi.org/10.1007/978-3-319-44649-3)
102
"""
103
struct LatticeBoltzmannEquations3D{RealT <: Real, CollisionOp} <:
104
AbstractLatticeBoltzmannEquations{3, 27}
105
c::RealT # mean thermal molecular velocity
106
c_s::RealT # isothermal speed of sound
107
rho0::RealT # macroscopic reference density
108
109
Ma::RealT # characteristic Mach number
110
u0::RealT # macroscopic reference velocity
111
112
Re::RealT # characteristic Reynolds number
113
L::RealT # reference length
114
nu::RealT # kinematic viscosity
115
116
weights::SVector{27, RealT} # weighting factors for the equilibrium distribution
117
v_alpha1::SVector{27, RealT} # discrete molecular velocity components in x-direction
118
v_alpha2::SVector{27, RealT} # discrete molecular velocity components in y-direction
119
v_alpha3::SVector{27, RealT} # discrete molecular velocity components in z-direction
120
121
collision_op::CollisionOp # collision operator for the collision kernel
122
end
123
124
function LatticeBoltzmannEquations3D(; Ma, Re, collision_op = collision_bgk,
125
c = 1, L = 1, rho0 = 1, u0 = nothing, nu = nothing)
126
# Sanity check that exactly one of Ma, u0 is not `nothing`
127
if isnothing(Ma) && isnothing(u0)
128
error("Mach number `Ma` and reference speed `u0` may not both be `nothing`")
129
elseif !isnothing(Ma) && !isnothing(u0)
130
error("Mach number `Ma` and reference speed `u0` may not both be set")
131
end
132
133
# Sanity check that exactly one of Re, nu is not `nothing`
134
if isnothing(Re) && isnothing(nu)
135
error("Reynolds number `Re` and visocsity `nu` may not both be `nothing`")
136
elseif !isnothing(Re) && !isnothing(nu)
137
error("Reynolds number `Re` and visocsity `nu` may not both be set")
138
end
139
140
# Calculate isothermal speed of sound
141
# The relation between the isothermal speed of sound `c_s` and the mean thermal molecular velocity
142
# `c` depends on the used phase space discretization, and is valid for D3Q27 (and others). For
143
# details, see, e.g., [3] in the docstring above.
144
# c_s = c / sqrt(3)
145
146
# Calculate missing quantities
147
if isnothing(Ma)
148
RealT = eltype(u0)
149
c_s = c / sqrt(convert(RealT, 3))
150
Ma = u0 / c_s
151
elseif isnothing(u0)
152
RealT = eltype(Ma)
153
c_s = c / sqrt(convert(RealT, 3))
154
u0 = Ma * c_s
155
end
156
if isnothing(Re)
157
Re = u0 * L / nu
158
elseif isnothing(nu)
159
nu = u0 * L / Re
160
end
161
162
# Promote to common data type
163
Ma, Re, c, L, rho0, u0, nu = promote(Ma, Re, c, L, rho0, u0, nu)
164
165
# Source for weights and speeds: [4] in docstring above
166
weights = SVector{27, RealT}(2 / 27, 2 / 27, 2 / 27, 2 / 27, 2 / 27, 2 / 27, 1 / 54,
167
1 / 54,
168
1 / 54,
169
1 / 54, 1 / 54, 1 / 54, 1 / 54, 1 / 54, 1 / 54, 1 / 54,
170
1 / 54,
171
1 / 54,
172
1 / 216, 1 / 216, 1 / 216, 1 / 216, 1 / 216, 1 / 216,
173
1 / 216,
174
1 / 216, 8 / 27)
175
v_alpha1 = SVector{27, RealT}(c, -c, 0, 0, 0, 0, c, -c, c,
176
-c, 0, 0, c, -c, c, -c, 0, 0,
177
c, -c, c, -c, c, -c, -c, c, 0)
178
v_alpha2 = SVector{27, RealT}(0, 0, c, -c, 0, 0, c, -c, 0,
179
0, c, -c, -c, c, 0, 0, c, -c,
180
c, -c, c, -c, -c, c, c, -c, 0)
181
v_alpha3 = SVector{27, RealT}(0, 0, 0, 0, c, -c, 0, 0, c,
182
-c, c, -c, 0, 0, -c, c, -c, c,
183
c, -c, -c, c, c, -c, c, -c, 0)
184
185
LatticeBoltzmannEquations3D(c, c_s, rho0, Ma, u0, Re, L, nu,
186
weights, v_alpha1, v_alpha2, v_alpha3,
187
collision_op)
188
end
189
190
function varnames(::typeof(cons2cons), equations::LatticeBoltzmannEquations3D)
191
ntuple(v -> "pdf" * string(v), Val(nvariables(equations)))
192
end
193
function varnames(::typeof(cons2prim), equations::LatticeBoltzmannEquations3D)
194
varnames(cons2cons, equations)
195
end
196
197
"""
198
cons2macroscopic(u, equations::LatticeBoltzmannEquations3D)
199
200
Convert the conservative variables `u` (the particle distribution functions)
201
to the macroscopic variables (density, velocity_1, velocity_2, velocity_3, pressure).
202
"""
203
@inline function cons2macroscopic(u, equations::LatticeBoltzmannEquations3D)
204
rho = density(u, equations)
205
v1, v2, v3 = velocity(u, equations)
206
p = pressure(u, equations)
207
return SVector(rho, v1, v2, v3, p)
208
end
209
function varnames(::typeof(cons2macroscopic), ::LatticeBoltzmannEquations3D)
210
("rho", "v1", "v2", "v3", "p")
211
end
212
213
# Set initial conditions at physical location `x` for time `t`
214
"""
215
initial_condition_constant(x, t, equations::LatticeBoltzmannEquations3D)
216
217
A constant initial condition to test free-stream preservation.
218
"""
219
function initial_condition_constant(x, t, equations::LatticeBoltzmannEquations3D)
220
@unpack u0 = equations
221
222
RealT = eltype(x)
223
rho = convert(RealT, pi)
224
v1 = u0
225
v2 = u0
226
v3 = u0
227
228
return equilibrium_distribution(rho, v1, v2, v3, equations)
229
end
230
231
# Calculate 1D flux for a single point
232
@inline function flux(u, orientation::Integer, equations::LatticeBoltzmannEquations3D)
233
if orientation == 1 # x-direction
234
v_alpha = equations.v_alpha1
235
elseif orientation == 2 # y-direction
236
v_alpha = equations.v_alpha2
237
else # z-direction
238
v_alpha = equations.v_alpha3
239
end
240
return v_alpha .* u
241
end
242
243
"""
244
flux_godunov(u_ll, u_rr, orientation,
245
equations::LatticeBoltzmannEquations3D)
246
247
Godunov (upwind) flux for the 3D Lattice-Boltzmann equations.
248
"""
249
@inline function flux_godunov(u_ll, u_rr, orientation::Integer,
250
equations::LatticeBoltzmannEquations3D)
251
if orientation == 1 # x-direction
252
v_alpha = equations.v_alpha1
253
elseif orientation == 2 # y-direction
254
v_alpha = equations.v_alpha2
255
else # z-direction
256
v_alpha = equations.v_alpha3
257
end
258
return 0.5f0 * (v_alpha .* (u_ll + u_rr) - abs.(v_alpha) .* (u_rr - u_ll))
259
end
260
261
"""
262
density(p::Real, equations::LatticeBoltzmannEquations3D)
263
density(u, equations::LatticeBoltzmannEquations3D)
264
265
Calculate the macroscopic density from the pressure `p` or the particle distribution functions `u`.
266
"""
267
@inline density(p::Real, equations::LatticeBoltzmannEquations3D) = p / equations.c_s^2
268
@inline density(u, equations::LatticeBoltzmannEquations3D) = sum(u)
269
270
"""
271
velocity(u, orientation, equations::LatticeBoltzmannEquations3D)
272
273
Calculate the macroscopic velocity for the given `orientation` (1 -> x, 2 -> y, 3 -> z) from the
274
particle distribution functions `u`.
275
"""
276
@inline function velocity(u, orientation::Integer,
277
equations::LatticeBoltzmannEquations3D)
278
if orientation == 1 # x-direction
279
v_alpha = equations.v_alpha1
280
elseif orientation == 2 # y-direction
281
v_alpha = equations.v_alpha2
282
else # z-direction
283
v_alpha = equations.v_alpha3
284
end
285
286
return dot(v_alpha, u) / density(u, equations)
287
end
288
289
"""
290
velocity(u, equations::LatticeBoltzmannEquations3D)
291
292
Calculate the macroscopic velocity vector from the particle distribution functions `u`.
293
"""
294
@inline function velocity(u, equations::LatticeBoltzmannEquations3D)
295
@unpack v_alpha1, v_alpha2, v_alpha3 = equations
296
rho = density(u, equations)
297
298
return SVector(dot(v_alpha1, u) / rho,
299
dot(v_alpha2, u) / rho,
300
dot(v_alpha3, u) / rho)
301
end
302
303
"""
304
pressure(rho::Real, equations::LatticeBoltzmannEquations3D)
305
pressure(u, equations::LatticeBoltzmannEquations3D)
306
307
Calculate the macroscopic pressure from the density `rho` or the particle distribution functions
308
`u`.
309
"""
310
@inline function pressure(rho::Real, equations::LatticeBoltzmannEquations3D)
311
return rho * equations.c_s^2
312
end
313
@inline function pressure(u, equations::LatticeBoltzmannEquations3D)
314
pressure(density(u, equations), equations)
315
end
316
317
"""
318
equilibrium_distribution(alpha, rho, v1, v2, v3, equations::LatticeBoltzmannEquations3D)
319
320
Calculate the local equilibrium distribution for the distribution function with index `alpha` and
321
given the macroscopic state defined by `rho`, `v1`, `v2`, `v3`.
322
"""
323
@inline function equilibrium_distribution(alpha, rho, v1, v2, v3,
324
equations::LatticeBoltzmannEquations3D)
325
@unpack weights, c_s, v_alpha1, v_alpha2, v_alpha3 = equations
326
327
va_v = v_alpha1[alpha] * v1 + v_alpha2[alpha] * v2 + v_alpha3[alpha] * v3
328
cs_squared = c_s^2
329
v_squared = v1^2 + v2^2 + v3^2
330
331
return weights[alpha] * rho *
332
(1 + va_v / cs_squared
333
+ va_v^2 / (2 * cs_squared^2)
334
-
335
v_squared / (2 * cs_squared))
336
end
337
338
@inline function equilibrium_distribution(rho, v1, v2, v3,
339
equations::LatticeBoltzmannEquations3D)
340
return SVector(equilibrium_distribution(1, rho, v1, v2, v3, equations),
341
equilibrium_distribution(2, rho, v1, v2, v3, equations),
342
equilibrium_distribution(3, rho, v1, v2, v3, equations),
343
equilibrium_distribution(4, rho, v1, v2, v3, equations),
344
equilibrium_distribution(5, rho, v1, v2, v3, equations),
345
equilibrium_distribution(6, rho, v1, v2, v3, equations),
346
equilibrium_distribution(7, rho, v1, v2, v3, equations),
347
equilibrium_distribution(8, rho, v1, v2, v3, equations),
348
equilibrium_distribution(9, rho, v1, v2, v3, equations),
349
equilibrium_distribution(10, rho, v1, v2, v3, equations),
350
equilibrium_distribution(11, rho, v1, v2, v3, equations),
351
equilibrium_distribution(12, rho, v1, v2, v3, equations),
352
equilibrium_distribution(13, rho, v1, v2, v3, equations),
353
equilibrium_distribution(14, rho, v1, v2, v3, equations),
354
equilibrium_distribution(15, rho, v1, v2, v3, equations),
355
equilibrium_distribution(16, rho, v1, v2, v3, equations),
356
equilibrium_distribution(17, rho, v1, v2, v3, equations),
357
equilibrium_distribution(18, rho, v1, v2, v3, equations),
358
equilibrium_distribution(19, rho, v1, v2, v3, equations),
359
equilibrium_distribution(20, rho, v1, v2, v3, equations),
360
equilibrium_distribution(21, rho, v1, v2, v3, equations),
361
equilibrium_distribution(22, rho, v1, v2, v3, equations),
362
equilibrium_distribution(23, rho, v1, v2, v3, equations),
363
equilibrium_distribution(24, rho, v1, v2, v3, equations),
364
equilibrium_distribution(25, rho, v1, v2, v3, equations),
365
equilibrium_distribution(26, rho, v1, v2, v3, equations),
366
equilibrium_distribution(27, rho, v1, v2, v3, equations))
367
end
368
369
function equilibrium_distribution(u, equations::LatticeBoltzmannEquations3D)
370
rho = density(u, equations)
371
v1, v2, v3 = velocity(u, equations)
372
373
return equilibrium_distribution(rho, v1, v2, v3, equations)
374
end
375
376
"""
377
collision_bgk(u, dt, equations::LatticeBoltzmannEquations3D)
378
379
Collision operator for the Bhatnagar, Gross, and Krook (BGK) model.
380
"""
381
@inline function collision_bgk(u, dt, equations::LatticeBoltzmannEquations3D)
382
@unpack c_s, nu = equations
383
tau = nu / (c_s^2 * dt)
384
return -(u - equilibrium_distribution(u, equations)) / (tau + 0.5f0)
385
end
386
387
@inline have_constant_speed(::LatticeBoltzmannEquations3D) = True()
388
389
@inline function max_abs_speeds(equations::LatticeBoltzmannEquations3D)
390
@unpack c = equations
391
392
return c, c, c
393
end
394
395
# Convert conservative variables to primitive
396
@inline cons2prim(u, equations::LatticeBoltzmannEquations3D) = u
397
398
# Convert conservative variables to entropy variables
399
@inline cons2entropy(u, equations::LatticeBoltzmannEquations3D) = u
400
401
# Calculate kinetic energy for a conservative state `u`
402
@inline function energy_kinetic(u, equations::LatticeBoltzmannEquations3D)
403
rho = density(u, equations)
404
v1, v2, v3 = velocity(u, equations)
405
406
return 0.5f0 * (v1^2 + v2^2 + v3^2) / rho / equations.rho0
407
end
408
409
# Calculate nondimensionalized kinetic energy for a conservative state `u`
410
@inline function energy_kinetic_nondimensional(u,
411
equations::LatticeBoltzmannEquations3D)
412
return energy_kinetic(u, equations) / equations.u0^2
413
end
414
end # @muladd
415
416