Path: blob/main/src/equations/lattice_boltzmann_2d.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"""8LatticeBoltzmannEquations2D(; 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 = 014```15in two space dimensions for the D2Q9 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 nine discrete velocity directions of the D2Q9 scheme are sorted as follows [4]:27```286 2 5 y29┌───┼───┐ │30│ │ │313 ┼ 9 ┼ 1 ──── x32│ │ ╱33└───┼───┘ ╱347 4 8 z35```36Note that usually the velocities are numbered from `0` to `8`, where `0` corresponds to the zero37velocity. Due to Julia using 1-based indexing, here we use indices from `1` to `9`, where `1`38through `8` correspond to the velocity directions in [4] and `9` is the zero velocity.3940The corresponding opposite directions are:41* 1 ←→ 342* 2 ←→ 443* 3 ←→ 144* 4 ←→ 245* 5 ←→ 746* 6 ←→ 847* 7 ←→ 548* 8 ←→ 649* 9 ←→ 95051The main sources for the base implementation were521. Misun Min, Taehun Lee, **A spectral-element discontinuous Galerkin lattice Boltzmann method for53nearly incompressible flows**, Journal of Computational Physics 230(1), 201154[doi:10.1016/j.jcp.2010.09.024](https://doi.org/10.1016/j.jcp.2010.09.024)552. Karsten Golly, **Anwendung der Lattice-Boltzmann Discontinuous Galerkin Spectral Element Method56(LB-DGSEM) auf laminare und turbulente nahezu inkompressible Strömungen im dreidimensionalen57Raum**, Master Thesis, University of Cologne, 2018.583. Dieter Hänel, **Molekulare Gasdynamik**, Springer-Verlag Berlin Heidelberg, 200459[doi:10.1007/3-540-35047-0](https://doi.org/10.1007/3-540-35047-0)604. Timm Krüger et al., **The Lattice Boltzmann Method**, Springer International Publishing, 201761[doi:10.1007/978-3-319-44649-3](https://doi.org/10.1007/978-3-319-44649-3)62"""63struct LatticeBoltzmannEquations2D{RealT <: Real, CollisionOp} <:64AbstractLatticeBoltzmannEquations{2, 9}65c::RealT # mean thermal molecular velocity66c_s::RealT # isothermal speed of sound67rho0::RealT # macroscopic reference density6869Ma::RealT # characteristic Mach number70u0::RealT # macroscopic reference velocity7172Re::RealT # characteristic Reynolds number73L::RealT # reference length74nu::RealT # kinematic viscosity7576weights::SVector{9, RealT} # weighting factors for the equilibrium distribution77v_alpha1::SVector{9, RealT} # discrete molecular velocity components in x-direction78v_alpha2::SVector{9, RealT} # discrete molecular velocity components in y-direction7980collision_op::CollisionOp # collision operator for the collision kernel81end8283function LatticeBoltzmannEquations2D(; Ma, Re, collision_op = collision_bgk,84c = 1, L = 1, rho0 = 1, u0 = nothing, nu = nothing)85# Sanity check that exactly one of Ma, u0 is not `nothing`86if isnothing(Ma) && isnothing(u0)87error("Mach number `Ma` and reference speed `u0` may not both be `nothing`")88elseif !isnothing(Ma) && !isnothing(u0)89error("Mach number `Ma` and reference speed `u0` may not both be set")90end9192# Sanity check that exactly one of Re, nu is not `nothing`93if isnothing(Re) && isnothing(nu)94error("Reynolds number `Re` and visocsity `nu` may not both be `nothing`")95elseif !isnothing(Re) && !isnothing(nu)96error("Reynolds number `Re` and visocsity `nu` may not both be set")97end9899# Calculate isothermal speed of sound100# The relation between the isothermal speed of sound `c_s` and the mean thermal molecular velocity101# `c` depends on the used phase space discretization, and is valid for D2Q9 (and others). For102# details, see, e.g., [3] in the docstring above.103# c_s = c / sqrt(3)104105# Calculate missing quantities106if isnothing(Ma)107RealT = eltype(u0)108c_s = c / sqrt(convert(RealT, 3))109Ma = u0 / c_s110elseif isnothing(u0)111RealT = eltype(Ma)112c_s = c / sqrt(convert(RealT, 3))113u0 = Ma * c_s114end115if isnothing(Re)116Re = u0 * L / nu117elseif isnothing(nu)118nu = u0 * L / Re119end120121# Promote to common data type122Ma, Re, c, L, rho0, u0, nu = promote(Ma, Re, c, L, rho0, u0, nu)123124# Source for weights and speeds: [4] in the docstring above125weights = SVector{9, RealT}(1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 36, 1 / 36, 1 / 36,1261 / 36, 4 / 9)127v_alpha1 = SVector{9, RealT}(c, 0, -c, 0, c, -c, -c, c, 0)128v_alpha2 = SVector{9, RealT}(0, c, 0, -c, c, c, -c, -c, 0)129130LatticeBoltzmannEquations2D(c, c_s, rho0, Ma, u0, Re, L, nu,131weights, v_alpha1, v_alpha2,132collision_op)133end134135function varnames(::typeof(cons2cons), equations::LatticeBoltzmannEquations2D)136ntuple(v -> "pdf" * string(v), nvariables(equations))137end138function varnames(::typeof(cons2prim), equations::LatticeBoltzmannEquations2D)139varnames(cons2cons, equations)140end141142"""143cons2macroscopic(u, equations::LatticeBoltzmannEquations2D)144145Convert the conservative variables `u` (the particle distribution functions)146to the macroscopic variables (density, velocity_1, velocity_2, pressure).147"""148@inline function cons2macroscopic(u, equations::LatticeBoltzmannEquations2D)149rho = density(u, equations)150v1, v2 = velocity(u, equations)151p = pressure(u, equations)152return SVector(rho, v1, v2, p)153end154function varnames(::typeof(cons2macroscopic), ::LatticeBoltzmannEquations2D)155("rho", "v1", "v2", "p")156end157158# Set initial conditions at physical location `x` for time `t`159"""160initial_condition_constant(x, t, equations::LatticeBoltzmannEquations2D)161162A constant initial condition to test free-stream preservation.163"""164function initial_condition_constant(x, t, equations::LatticeBoltzmannEquations2D)165@unpack u0 = equations166167RealT = eltype(x)168rho = convert(RealT, pi)169v1 = u0170v2 = u0171172return equilibrium_distribution(rho, v1, v2, equations)173end174175"""176boundary_condition_noslip_wall(u_inner, orientation, direction, x, t,177surface_flux_function,178equations::LatticeBoltzmannEquations2D)179180No-slip wall boundary condition using the bounce-back approach.181"""182@inline function boundary_condition_noslip_wall(u_inner, orientation, direction, x, t,183surface_flux_function,184equations::LatticeBoltzmannEquations2D)185# For LBM no-slip wall boundary conditions, we set the boundary state to186# - the inner state for outgoing particle distribution functions187# - the *opposite* inner state for all other particle distribution functions188# See the list of (opposite) directions in the docstring of `LatticeBoltzmannEquations2D`.189if direction == 1 # boundary in -x direction190pdf1 = u_inner[3]191pdf2 = u_inner[4]192pdf3 = u_inner[3] # outgoing193pdf4 = u_inner[2]194pdf5 = u_inner[7]195pdf6 = u_inner[6] # outgoing196pdf7 = u_inner[7] # outgoing197pdf8 = u_inner[6]198pdf9 = u_inner[9]199elseif direction == 2 # boundary in +x direction200pdf1 = u_inner[1] # outgoing201pdf2 = u_inner[4]202pdf3 = u_inner[1]203pdf4 = u_inner[2]204pdf5 = u_inner[5] # outgoing205pdf6 = u_inner[8]206pdf7 = u_inner[5]207pdf8 = u_inner[8] # outgoing208pdf9 = u_inner[9]209elseif direction == 3 # boundary in -y direction210pdf1 = u_inner[3]211pdf2 = u_inner[4]212pdf3 = u_inner[1]213pdf4 = u_inner[4] # outgoing214pdf5 = u_inner[7]215pdf6 = u_inner[8]216pdf7 = u_inner[7] # outgoing217pdf8 = u_inner[8] # outgoing218pdf9 = u_inner[9]219else # boundary in +y direction220pdf1 = u_inner[3]221pdf2 = u_inner[2] # outgoing222pdf3 = u_inner[1]223pdf4 = u_inner[2]224pdf5 = u_inner[5] # outgoing225pdf6 = u_inner[6] # outgoing226pdf7 = u_inner[5]227pdf8 = u_inner[6]228pdf9 = u_inner[9]229end230u_boundary = SVector(pdf1, pdf2, pdf3, pdf4, pdf5, pdf6, pdf7, pdf8, pdf9)231232# Calculate boundary flux233if iseven(direction) # u_inner is "left" of boundary, u_boundary is "right" of boundary234flux = surface_flux_function(u_inner, u_boundary, orientation, equations)235else # u_boundary is "left" of boundary, u_inner is "right" of boundary236flux = surface_flux_function(u_boundary, u_inner, orientation, equations)237end238239return flux240end241242# Calculate 1D flux for a single point243@inline function flux(u, orientation::Integer, equations::LatticeBoltzmannEquations2D)244if orientation == 1245v_alpha = equations.v_alpha1246else247v_alpha = equations.v_alpha2248end249return v_alpha .* u250end251252"""253flux_godunov(u_ll, u_rr, orientation,254equations::LatticeBoltzmannEquations2D)255256Godunov (upwind) flux for the 2D Lattice-Boltzmann equations.257"""258@inline function flux_godunov(u_ll, u_rr, orientation::Integer,259equations::LatticeBoltzmannEquations2D)260if orientation == 1261v_alpha = equations.v_alpha1262else263v_alpha = equations.v_alpha2264end265return 0.5f0 * (v_alpha .* (u_ll + u_rr) - abs.(v_alpha) .* (u_rr - u_ll))266end267268"""269density(p::Real, equations::LatticeBoltzmannEquations2D)270density(u, equations::LatticeBoltzmannEquations2D)271272Calculate the macroscopic density from the pressure `p` or the particle distribution functions `u`.273"""274@inline density(p::Real, equations::LatticeBoltzmannEquations2D) = p / equations.c_s^2275@inline density(u, equations::LatticeBoltzmannEquations2D) = sum(u)276277"""278velocity(u, orientation, equations::LatticeBoltzmannEquations2D)279280Calculate the macroscopic velocity for the given `orientation` (1 -> x, 2 -> y) from the281particle distribution functions `u`.282"""283@inline function velocity(u, orientation::Integer,284equations::LatticeBoltzmannEquations2D)285if orientation == 1286v_alpha = equations.v_alpha1287else288v_alpha = equations.v_alpha2289end290291return dot(v_alpha, u) / density(u, equations)292end293294"""295velocity(u, equations::LatticeBoltzmannEquations2D)296297Calculate the macroscopic velocity vector from the particle distribution functions `u`.298"""299@inline function velocity(u, equations::LatticeBoltzmannEquations2D)300@unpack v_alpha1, v_alpha2 = equations301rho = density(u, equations)302303return SVector(dot(v_alpha1, u) / rho,304dot(v_alpha2, u) / rho)305end306307"""308pressure(rho::Real, equations::LatticeBoltzmannEquations2D)309pressure(u, equations::LatticeBoltzmannEquations2D)310311Calculate the macroscopic pressure from the density `rho` or the particle distribution functions312`u`.313"""314@inline function pressure(rho::Real, equations::LatticeBoltzmannEquations2D)315return rho * equations.c_s^2316end317@inline function pressure(u, equations::LatticeBoltzmannEquations2D)318pressure(density(u, equations), equations)319end320321"""322equilibrium_distribution(alpha, rho, v1, v2, equations::LatticeBoltzmannEquations2D)323324Calculate the local equilibrium distribution for the distribution function with index `alpha` and325given the macroscopic state defined by `rho`, `v1`, `v2`.326"""327@inline function equilibrium_distribution(alpha, rho, v1, v2,328equations::LatticeBoltzmannEquations2D)329@unpack weights, c_s, v_alpha1, v_alpha2 = equations330331va_v = v_alpha1[alpha] * v1 + v_alpha2[alpha] * v2332cs_squared = c_s^2333v_squared = v1^2 + v2^2334335return weights[alpha] * rho *336(1 + va_v / cs_squared337+ va_v^2 / (2 * cs_squared^2)338-339v_squared / (2 * cs_squared))340end341342@inline function equilibrium_distribution(rho, v1, v2,343equations::LatticeBoltzmannEquations2D)344return SVector(equilibrium_distribution(1, rho, v1, v2, equations),345equilibrium_distribution(2, rho, v1, v2, equations),346equilibrium_distribution(3, rho, v1, v2, equations),347equilibrium_distribution(4, rho, v1, v2, equations),348equilibrium_distribution(5, rho, v1, v2, equations),349equilibrium_distribution(6, rho, v1, v2, equations),350equilibrium_distribution(7, rho, v1, v2, equations),351equilibrium_distribution(8, rho, v1, v2, equations),352equilibrium_distribution(9, rho, v1, v2, equations))353end354355function equilibrium_distribution(u, equations::LatticeBoltzmannEquations2D)356rho = density(u, equations)357v1, v2 = velocity(u, equations)358359return equilibrium_distribution(rho, v1, v2, equations)360end361362"""363collision_bgk(u, dt, equations::LatticeBoltzmannEquations2D)364365Collision operator for the Bhatnagar, Gross, and Krook (BGK) model.366"""367@inline function collision_bgk(u, dt, equations::LatticeBoltzmannEquations2D)368@unpack c_s, nu = equations369tau = nu / (c_s^2 * dt)370return -(u - equilibrium_distribution(u, equations)) / (tau + 0.5f0)371end372373@inline have_constant_speed(::LatticeBoltzmannEquations2D) = True()374375@inline function max_abs_speeds(equations::LatticeBoltzmannEquations2D)376@unpack c = equations377378return c, c379end380381# Convert conservative variables to primitive382@inline cons2prim(u, equations::LatticeBoltzmannEquations2D) = u383384# Convert conservative variables to entropy variables385@inline cons2entropy(u, equations::LatticeBoltzmannEquations2D) = u386end # @muladd387388389