Path: blob/main/src/equations/lattice_boltzmann_3d.jl
2055 views
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).1# Since these FMAs can increase the performance of many numerical algorithms,2# we need to opt-in explicitly.3# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.4@muladd begin5#! format: noindent67@doc raw"""8LatticeBoltzmannEquations3D(; Ma, Re, collision_op=collision_bgk,9c=1, L=1, rho0=1, u0=nothing, nu=nothing)1011The Lattice-Boltzmann equations12```math13\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 = 014```15in three space dimensions for the D3Q27 scheme.1617The characteristic Mach number and Reynolds numbers are specified as `Ma` and `Re`. By the18default, the collision operator `collision_op` is set to the BGK model. `c`, `L`, and `rho0`19specify the mean thermal molecular velocity, the characteristic length, and the reference density,20respectively. They can usually be left to the default values. If desired, instead of the Mach21number, one can set the macroscopic reference velocity `u0` directly (`Ma` needs to be set to22`nothing` in this case). Likewise, instead of the Reynolds number one can specify the kinematic23viscosity `nu` directly (in this case, `Re` needs to be set to `nothing`).242526The twenty-seven discrete velocity directions of the D3Q27 scheme are sorted as follows [4]:27* plane at `z = -1`:28```2924 17 21 y30┌───┼───┐ │31│ │ │3210 ┼ 6 ┼ 15 ──── x33│ │ ╱34└───┼───┘ ╱3520 12 26 z36```37* plane at `z = 0`:38```3914 3 7 y40┌───┼───┐ │41│ │ │422 ┼ 27 ┼ 1 ──── x43│ │ ╱44└───┼───┘ ╱458 4 13 z46```47* plane at `z = +1`:48```4925 11 19 y50┌───┼───┐ │51│ │ │5216 ┼ 5 ┼ 9 ──── x53│ │ ╱54└───┼───┘ ╱5522 18 23 z56```57Note that usually the velocities are numbered from `0` to `26`, where `0` corresponds to the zero58velocity. Due to Julia using 1-based indexing, here we use indices from `1` to `27`, where `1`59through `26` correspond to the velocity directions in [4] and `27` is the zero velocity.6061The corresponding opposite directions are:62* 1 ←→ 263* 2 ←→ 164* 3 ←→ 465* 4 ←→ 366* 5 ←→ 667* 6 ←→ 568* 7 ←→ 869* 8 ←→ 770* 9 ←→ 1071* 10 ←→ 972* 11 ←→ 1273* 12 ←→ 1174* 13 ←→ 1475* 14 ←→ 1376* 15 ←→ 1677* 16 ←→ 1578* 17 ←→ 1879* 18 ←→ 1780* 19 ←→ 2081* 20 ←→ 1982* 21 ←→ 2283* 22 ←→ 2184* 23 ←→ 2485* 24 ←→ 2386* 25 ←→ 2687* 26 ←→ 2588* 27 ←→ 278990The main sources for the base implementation were911. Misun Min, Taehun Lee, **A spectral-element discontinuous Galerkin lattice Boltzmann method for92nearly incompressible flows**, Journal of Computational Physics 230(1), 201193[doi:10.1016/j.jcp.2010.09.024](https://doi.org/10.1016/j.jcp.2010.09.024)942. Karsten Golly, **Anwendung der Lattice-Boltzmann Discontinuous Galerkin Spectral Element Method95(LB-DGSEM) auf laminare und turbulente nahezu inkompressible Strömungen im dreidimensionalen96Raum**, Master Thesis, University of Cologne, 2018.973. Dieter Hänel, **Molekulare Gasdynamik**, Springer-Verlag Berlin Heidelberg, 200498[doi:10.1007/3-540-35047-0](https://doi.org/10.1007/3-540-35047-0)994. Timm Krüger et al., **The Lattice Boltzmann Method**, Springer International Publishing, 2017100[doi:10.1007/978-3-319-44649-3](https://doi.org/10.1007/978-3-319-44649-3)101"""102struct LatticeBoltzmannEquations3D{RealT <: Real, CollisionOp} <:103AbstractLatticeBoltzmannEquations{3, 27}104c::RealT # mean thermal molecular velocity105c_s::RealT # isothermal speed of sound106rho0::RealT # macroscopic reference density107108Ma::RealT # characteristic Mach number109u0::RealT # macroscopic reference velocity110111Re::RealT # characteristic Reynolds number112L::RealT # reference length113nu::RealT # kinematic viscosity114115weights::SVector{27, RealT} # weighting factors for the equilibrium distribution116v_alpha1::SVector{27, RealT} # discrete molecular velocity components in x-direction117v_alpha2::SVector{27, RealT} # discrete molecular velocity components in y-direction118v_alpha3::SVector{27, RealT} # discrete molecular velocity components in z-direction119120collision_op::CollisionOp # collision operator for the collision kernel121end122123function LatticeBoltzmannEquations3D(; Ma, Re, collision_op = collision_bgk,124c = 1, L = 1, rho0 = 1, u0 = nothing, nu = nothing)125# Sanity check that exactly one of Ma, u0 is not `nothing`126if isnothing(Ma) && isnothing(u0)127error("Mach number `Ma` and reference speed `u0` may not both be `nothing`")128elseif !isnothing(Ma) && !isnothing(u0)129error("Mach number `Ma` and reference speed `u0` may not both be set")130end131132# Sanity check that exactly one of Re, nu is not `nothing`133if isnothing(Re) && isnothing(nu)134error("Reynolds number `Re` and visocsity `nu` may not both be `nothing`")135elseif !isnothing(Re) && !isnothing(nu)136error("Reynolds number `Re` and visocsity `nu` may not both be set")137end138139# Calculate isothermal speed of sound140# The relation between the isothermal speed of sound `c_s` and the mean thermal molecular velocity141# `c` depends on the used phase space discretization, and is valid for D3Q27 (and others). For142# details, see, e.g., [3] in the docstring above.143# c_s = c / sqrt(3)144145# Calculate missing quantities146if isnothing(Ma)147RealT = eltype(u0)148c_s = c / sqrt(convert(RealT, 3))149Ma = u0 / c_s150elseif isnothing(u0)151RealT = eltype(Ma)152c_s = c / sqrt(convert(RealT, 3))153u0 = Ma * c_s154end155if isnothing(Re)156Re = u0 * L / nu157elseif isnothing(nu)158nu = u0 * L / Re159end160161# Promote to common data type162Ma, Re, c, L, rho0, u0, nu = promote(Ma, Re, c, L, rho0, u0, nu)163164# Source for weights and speeds: [4] in docstring above165weights = SVector{27, RealT}(2 / 27, 2 / 27, 2 / 27, 2 / 27, 2 / 27, 2 / 27, 1 / 54,1661 / 54,1671 / 54,1681 / 54, 1 / 54, 1 / 54, 1 / 54, 1 / 54, 1 / 54, 1 / 54,1691 / 54,1701 / 54,1711 / 216, 1 / 216, 1 / 216, 1 / 216, 1 / 216, 1 / 216,1721 / 216,1731 / 216, 8 / 27)174v_alpha1 = SVector{27, RealT}(c, -c, 0, 0, 0, 0, c, -c, c,175-c, 0, 0, c, -c, c, -c, 0, 0,176c, -c, c, -c, c, -c, -c, c, 0)177v_alpha2 = SVector{27, RealT}(0, 0, c, -c, 0, 0, c, -c, 0,1780, c, -c, -c, c, 0, 0, c, -c,179c, -c, c, -c, -c, c, c, -c, 0)180v_alpha3 = SVector{27, RealT}(0, 0, 0, 0, c, -c, 0, 0, c,181-c, c, -c, 0, 0, -c, c, -c, c,182c, -c, -c, c, c, -c, c, -c, 0)183184LatticeBoltzmannEquations3D(c, c_s, rho0, Ma, u0, Re, L, nu,185weights, v_alpha1, v_alpha2, v_alpha3,186collision_op)187end188189function varnames(::typeof(cons2cons), equations::LatticeBoltzmannEquations3D)190ntuple(v -> "pdf" * string(v), Val(nvariables(equations)))191end192function varnames(::typeof(cons2prim), equations::LatticeBoltzmannEquations3D)193varnames(cons2cons, equations)194end195196"""197cons2macroscopic(u, equations::LatticeBoltzmannEquations3D)198199Convert the conservative variables `u` (the particle distribution functions)200to the macroscopic variables (density, velocity_1, velocity_2, velocity_3, pressure).201"""202@inline function cons2macroscopic(u, equations::LatticeBoltzmannEquations3D)203rho = density(u, equations)204v1, v2, v3 = velocity(u, equations)205p = pressure(u, equations)206return SVector(rho, v1, v2, v3, p)207end208function varnames(::typeof(cons2macroscopic), ::LatticeBoltzmannEquations3D)209("rho", "v1", "v2", "v3", "p")210end211212# Set initial conditions at physical location `x` for time `t`213"""214initial_condition_constant(x, t, equations::LatticeBoltzmannEquations3D)215216A constant initial condition to test free-stream preservation.217"""218function initial_condition_constant(x, t, equations::LatticeBoltzmannEquations3D)219@unpack u0 = equations220221RealT = eltype(x)222rho = convert(RealT, pi)223v1 = u0224v2 = u0225v3 = u0226227return equilibrium_distribution(rho, v1, v2, v3, equations)228end229230# Calculate 1D flux for a single point231@inline function flux(u, orientation::Integer, equations::LatticeBoltzmannEquations3D)232if orientation == 1 # x-direction233v_alpha = equations.v_alpha1234elseif orientation == 2 # y-direction235v_alpha = equations.v_alpha2236else # z-direction237v_alpha = equations.v_alpha3238end239return v_alpha .* u240end241242"""243flux_godunov(u_ll, u_rr, orientation,244equations::LatticeBoltzmannEquations3D)245246Godunov (upwind) flux for the 3D Lattice-Boltzmann equations.247"""248@inline function flux_godunov(u_ll, u_rr, orientation::Integer,249equations::LatticeBoltzmannEquations3D)250if orientation == 1 # x-direction251v_alpha = equations.v_alpha1252elseif orientation == 2 # y-direction253v_alpha = equations.v_alpha2254else # z-direction255v_alpha = equations.v_alpha3256end257return 0.5f0 * (v_alpha .* (u_ll + u_rr) - abs.(v_alpha) .* (u_rr - u_ll))258end259260"""261density(p::Real, equations::LatticeBoltzmannEquations3D)262density(u, equations::LatticeBoltzmannEquations3D)263264Calculate the macroscopic density from the pressure `p` or the particle distribution functions `u`.265"""266@inline density(p::Real, equations::LatticeBoltzmannEquations3D) = p / equations.c_s^2267@inline density(u, equations::LatticeBoltzmannEquations3D) = sum(u)268269"""270velocity(u, orientation, equations::LatticeBoltzmannEquations3D)271272Calculate the macroscopic velocity for the given `orientation` (1 -> x, 2 -> y, 3 -> z) from the273particle distribution functions `u`.274"""275@inline function velocity(u, orientation::Integer,276equations::LatticeBoltzmannEquations3D)277if orientation == 1 # x-direction278v_alpha = equations.v_alpha1279elseif orientation == 2 # y-direction280v_alpha = equations.v_alpha2281else # z-direction282v_alpha = equations.v_alpha3283end284285return dot(v_alpha, u) / density(u, equations)286end287288"""289velocity(u, equations::LatticeBoltzmannEquations3D)290291Calculate the macroscopic velocity vector from the particle distribution functions `u`.292"""293@inline function velocity(u, equations::LatticeBoltzmannEquations3D)294@unpack v_alpha1, v_alpha2, v_alpha3 = equations295rho = density(u, equations)296297return SVector(dot(v_alpha1, u) / rho,298dot(v_alpha2, u) / rho,299dot(v_alpha3, u) / rho)300end301302"""303pressure(rho::Real, equations::LatticeBoltzmannEquations3D)304pressure(u, equations::LatticeBoltzmannEquations3D)305306Calculate the macroscopic pressure from the density `rho` or the particle distribution functions307`u`.308"""309@inline function pressure(rho::Real, equations::LatticeBoltzmannEquations3D)310return rho * equations.c_s^2311end312@inline function pressure(u, equations::LatticeBoltzmannEquations3D)313pressure(density(u, equations), equations)314end315316"""317equilibrium_distribution(alpha, rho, v1, v2, v3, equations::LatticeBoltzmannEquations3D)318319Calculate the local equilibrium distribution for the distribution function with index `alpha` and320given the macroscopic state defined by `rho`, `v1`, `v2`, `v3`.321"""322@inline function equilibrium_distribution(alpha, rho, v1, v2, v3,323equations::LatticeBoltzmannEquations3D)324@unpack weights, c_s, v_alpha1, v_alpha2, v_alpha3 = equations325326va_v = v_alpha1[alpha] * v1 + v_alpha2[alpha] * v2 + v_alpha3[alpha] * v3327cs_squared = c_s^2328v_squared = v1^2 + v2^2 + v3^2329330return weights[alpha] * rho *331(1 + va_v / cs_squared332+ va_v^2 / (2 * cs_squared^2)333-334v_squared / (2 * cs_squared))335end336337@inline function equilibrium_distribution(rho, v1, v2, v3,338equations::LatticeBoltzmannEquations3D)339return SVector(equilibrium_distribution(1, rho, v1, v2, v3, equations),340equilibrium_distribution(2, rho, v1, v2, v3, equations),341equilibrium_distribution(3, rho, v1, v2, v3, equations),342equilibrium_distribution(4, rho, v1, v2, v3, equations),343equilibrium_distribution(5, rho, v1, v2, v3, equations),344equilibrium_distribution(6, rho, v1, v2, v3, equations),345equilibrium_distribution(7, rho, v1, v2, v3, equations),346equilibrium_distribution(8, rho, v1, v2, v3, equations),347equilibrium_distribution(9, rho, v1, v2, v3, equations),348equilibrium_distribution(10, rho, v1, v2, v3, equations),349equilibrium_distribution(11, rho, v1, v2, v3, equations),350equilibrium_distribution(12, rho, v1, v2, v3, equations),351equilibrium_distribution(13, rho, v1, v2, v3, equations),352equilibrium_distribution(14, rho, v1, v2, v3, equations),353equilibrium_distribution(15, rho, v1, v2, v3, equations),354equilibrium_distribution(16, rho, v1, v2, v3, equations),355equilibrium_distribution(17, rho, v1, v2, v3, equations),356equilibrium_distribution(18, rho, v1, v2, v3, equations),357equilibrium_distribution(19, rho, v1, v2, v3, equations),358equilibrium_distribution(20, rho, v1, v2, v3, equations),359equilibrium_distribution(21, rho, v1, v2, v3, equations),360equilibrium_distribution(22, rho, v1, v2, v3, equations),361equilibrium_distribution(23, rho, v1, v2, v3, equations),362equilibrium_distribution(24, rho, v1, v2, v3, equations),363equilibrium_distribution(25, rho, v1, v2, v3, equations),364equilibrium_distribution(26, rho, v1, v2, v3, equations),365equilibrium_distribution(27, rho, v1, v2, v3, equations))366end367368function equilibrium_distribution(u, equations::LatticeBoltzmannEquations3D)369rho = density(u, equations)370v1, v2, v3 = velocity(u, equations)371372return equilibrium_distribution(rho, v1, v2, v3, equations)373end374375"""376collision_bgk(u, dt, equations::LatticeBoltzmannEquations3D)377378Collision operator for the Bhatnagar, Gross, and Krook (BGK) model.379"""380@inline function collision_bgk(u, dt, equations::LatticeBoltzmannEquations3D)381@unpack c_s, nu = equations382tau = nu / (c_s^2 * dt)383return -(u - equilibrium_distribution(u, equations)) / (tau + 0.5f0)384end385386@inline have_constant_speed(::LatticeBoltzmannEquations3D) = True()387388@inline function max_abs_speeds(equations::LatticeBoltzmannEquations3D)389@unpack c = equations390391return c, c, c392end393394# Convert conservative variables to primitive395@inline cons2prim(u, equations::LatticeBoltzmannEquations3D) = u396397# Convert conservative variables to entropy variables398@inline cons2entropy(u, equations::LatticeBoltzmannEquations3D) = u399400# Calculate kinetic energy for a conservative state `u`401@inline function energy_kinetic(u, equations::LatticeBoltzmannEquations3D)402rho = density(u, equations)403v1, v2, v3 = velocity(u, equations)404405return 0.5f0 * (v1^2 + v2^2 + v3^2) / rho / equations.rho0406end407408# Calculate nondimensionalized kinetic energy for a conservative state `u`409@inline function energy_kinetic_nondimensional(u,410equations::LatticeBoltzmannEquations3D)411return energy_kinetic(u, equations) / equations.u0^2412end413end # @muladd414415416