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