Skip to content
cfd-lab:~/zh/posts/2026-05-25-lbm-equilibri…● online

本文译自作者的韩语原文。 阅读原文(한국어)

NOTE #052DAY MON CFD기법DATE 2026.05.25UPDATED 2026.10.19READ 5 min read#CFD#LBM#Lattice-Boltzmann#Kinetic-Theory#Hermite-Polynomial

九支箭里的Maxwell–Boltzmann — LBM平衡分布为何是多项式

代码里向东的偏移停在速度0.0891、马赫数约0.154。N=2多项式被要求守住的格子速度大约到0.173。

格子气在1986年倒了。每格粒子不是0就是1,噪声靠平均也抹不掉。1992年,那些整数换成了实数分布 fif_i。噪声没了。问题还在。九个实数凭什么代替无穷维的Maxwell–Boltzmann。

拨动图上的 drift uu 和截断阶 NN,实线的Maxwell–Boltzmann会和虚线多项式叠在一起,或者分开。

Maxwell–Boltzmann과 그 Hermite 절단의 비교. 표의 세 모멘트(밀도·운동량·에너지)가 같아지는 차수가 바로 LBM이 멈추는 자리다.
N=2면 ρ·ρu·에너지 세 모멘트가 정확히 일치한다. u가 |u|/c_s ≈ 1을 넘어가면 절단오차가 폭주하며 격자가 깨진다 — LBM이 저속(낮은 마하수)에 묶이는 이유.

N=2N=2 且 u=0.4u=0.4 时,密度、动量、能量叠在一起。uu 超过1,只有虚线离开实线。马赫上限 ∣u∣/cs≲0.3|\mathbf{u}|/c_s \lesssim 0.3 并不是这个分开的位置。数字留到后面再除。

从整数到实数 — 1986年LGCA之死与LBM的诞生#

LGCA(Lattice Gas Cellular Automaton)是正方格子、六个方向、一条碰撞规则。宏观平均回到Navier–Stokes。布尔粒子(0或1)把统计噪声放大了。把时间平均拉长,噪声会死,想看的瞬态(transient)也一起死。

McNamara和Zanetti在每个节点放上实数分布 fi(x,t)f_i(\mathbf{x}, t)。fif_i 是时刻 tt、位置 x\mathbf{x} 上沿方向 ci\mathbf{c}_i 运动的粒子期望个数。一步分成两截。碰撞(collision),然后是传播(streaming)。

fi(x+ciΔt,t+Δt)=fi(x,t)−fi(x,t)−fieq(ρ,u)τf_i(\mathbf{x} + \mathbf{c}_i \Delta t, t + \Delta t) = f_i(\mathbf{x}, t) - \frac{f_i(\mathbf{x}, t) - f_i^{eq}(\rho, \mathbf{u})}{\tau}

τ\tau 是松弛时间(relaxation time)。fieqf_i^{eq} 是平衡分布。碰撞按 τ\tau 的尺度削掉非平衡部分,streaming把这个分布推过一格。九个数够用,是因为 fieqf_i^{eq} 的形状。

Maxwell–Boltzmann怀里的定时炸弹#

连续动理论里的平衡是Maxwell–Boltzmann。

fMB(c)=ρ (2πRT)−D/2exp⁡ ⁣(−(c−u)22RT)f^{MB}(\mathbf{c}) = \rho \, (2\pi R T)^{-D/2} \exp\!\left( -\frac{(\mathbf{c} - \mathbf{u})^2}{2 R T} \right)

c\mathbf{c} 是分子速度,u\mathbf{u} 是宏观速度,DD 是维数,TT 是温度,RR 是气体常数。炸弹在于 c\mathbf{c} 是连续的。只留九个或十九个方向,就得把这个分布削薄。

把高斯积分换成矩形和,会失败。Navier–Stokes要的矩,ρ\rho、ρu\rho\mathbf{u}、动量张量 ρuu\rho\mathbf{u}\mathbf{u}、能量,和格子对不上。守恒一破,LBM就停在那里。

留下来的办法是展开。把Maxwell–Boltzmann展成正交多项式,只留需要的矩。那族多项式是Hermite。

Hermite多项式把分布切成碎片#

概率论意义下的Hermite多项式(probabilist Hermite polynomial)Hen(c)He_n(c) 在标准高斯权重 w(c)=(2π)−1/2e−c2/2w(c)=(2\pi)^{-1/2}e^{-c^2/2} 下正交。

∫Hen(c) Hem(c) w(c) dc=n! δnm\int He_n(c)\, He_m(c)\, w(c)\, dc = n!\, \delta_{nm}

前几项是 He0=1He_0=1、He1=cHe_1=c、He2=c2−1He_2=c^2-1、He3=c3−3cHe_3=c^3-3c。把一维平衡取成等温,也就是 RT=1RT=1,生成函数给出下面这一式。

fMB(c)=w(c) ρexp⁡ ⁣(uc−u22)=w(c) ρ∑n=0∞unn!Hen(c)f^{MB}(c) = w(c)\, \rho \exp\!\left( u c - \frac{u^2}{2} \right) = w(c)\, \rho \sum_{n=0}^{\infty} \frac{u^n}{n!} He_n(c)

n=0n=0 是静止的高斯。n=1n=1 是小的 drift。n=2n=2 修正平均动能。更高的 nn 是不对称的尾巴。LBM把这个和在有限阶截断。

等温展开里 RT=1RT=1,热速度也是1。图上的 uu 已经除以这个热速度。写成上限的0.3,就是这把尺子上的 ∣u∣|u|。u=0.4u=0.4 比上限高出0.1,N=2N=2 的前三个矩仍然重合。虚线分开的 ∣u∣>1|u|>1 已经过了马赫1。0.3和1不是同一次失败。

级数的 n=3n=3 项是 (u3/6) He3(c)(u^3/6)\,He_3(c),He3=c3−3cHe_3=c^3-3c。只看系数,u=0.3u=0.3 时 0.33/6=0.027/6=0.00450.3^3/6=0.027/6=0.0045。u=0.4u=0.4 时 0.43/6=0.064/6≈0.01070.4^3/6=0.064/6\approx 0.0107,大约是上限处的2.37倍。u=1u=1 时 1/6≈0.16671/6\approx 0.1667,大约是0.0045的37倍。这个比就是 13/0.33=1/0.0271^3/0.3^3=1/0.027。N=2N=2 把这一项整个丢掉。所以 u=0.4u=0.4 时密度、动量、能量仍然重合,丢掉的三阶大小却已经超过上限值的两倍。虚线分开的 u=1u=1,是这个系数爬到0.17附近的地方。马赫上限0.3并不是曲线明显离开的那个点。它是三阶系数越过0.0045时事先画下的一条线。

二阶截断的含义 — 被保住的矩

一阶粘性Navier–Stokes要的是三层。ρ\rho、ρu\rho\mathbf{u},以及动量通量 Παβ=ρuαuβ+pδαβ\Pi_{\alpha\beta}=\rho u_\alpha u_\beta+p\delta_{\alpha\beta}。ρuu\rho\mathbf{u}\mathbf{u} 是 u2u^2,所以分布里必须有 u2u^2 项。

级数在 N=2N=2 截断。

feq(c)=w(c) ρ[1+uc+u22(c2−1)]f^{eq}(c) = w(c)\, \rho \left[ 1 + uc + \frac{u^2}{2}(c^2 - 1) \right]

这是平衡分布的连续形式。N=3N=3 及以上进热通量。标准等温LBM不留它们。丢掉的项同时留下等温假设和马赫数0.3的上限。把那个三阶加回去,等温这个假设会先破。热通量是该项搬走的那一块,所以停在二阶的代码,没法用这个多项式接一个温度在流动的问题。马赫上限和等温假设是同一次截断的两面。就算把速度放在0.173以下,同一张网格上放一面热壁和一面冷壁,这个平衡也不运热。

D2Q9是如何造出来的#

把连续积分 ∫feq(c) ϕ(c) dc\int f^{eq}(c)\,\phi(c)\,dc 换成格子求和 ∑iwifieqϕ(ci)\sum_i w_i f_i^{eq}\phi(\mathbf{c}_i),需要求积(quadrature)。要对到 NN 阶,把该阶多项式精确积出来就够了。

二维里要对到 u2u^2,高斯权重下的五阶多项式必须精确。动量张量会造出 c2×u2c^2\times u^2。三点Gauss–Hermite求积负责这个积分。

一维节点是 c∈{−3,0,+3}c\in\{-\sqrt{3},0,+\sqrt{3}\},权重是 {1/6,2/3,1/6}\{1/6,2/3,1/6\}。这三个根为什么落在那里,把六阶矩折进九个速度的Hermite求积另外在数。二维张量积是 3×3=93\times 3=9。这就是D2Q9。换到格子单位,cs=1/3c_s=1/\sqrt{3},速度落在整数坐标 (0,0),(±1,0),(0,±1),(±1,±1)(0,0),(\pm 1,0),(0,\pm 1),(\pm 1,\pm 1) 上。权重 wiw_i 是把高斯因子吸进Gauss–Hermite权重之后的数。

编号ci\mathbf{c}_iwiw_i
0(0,0)(0, 0)4/94/9
1–4(±1,0),(0,±1)(\pm 1, 0), (0, \pm 1)1/91/9
5–8(±1,±1)(\pm 1, \pm 1)1/361/36

格子上的平衡,是连续形式代入 cs2=1/3c_s^2=1/3 之后的结果。

fieq=wi ρ[1+3(ci⋅u)+92(ci⋅u)2−32 u⋅u]f_i^{eq} = w_i\, \rho \left[ 1 + 3 (\mathbf{c}_i \cdot \mathbf{u}) + \frac{9}{2}(\mathbf{c}_i \cdot \mathbf{u})^2 - \frac{3}{2}\, \mathbf{u} \cdot \mathbf{u} \right]

1/cs2=31/c_s^2=3,1/(2cs4)=9/21/(2c_s^4)=9/2,1/(2cs2)=3/21/(2c_s^2)=3/2。式子里的3、9/29/2、3/23/2 就是这三个数。u=0\mathbf{u}=0 时括号等于1,fieq=wiρf_i^{eq}=w_i\rho。权重相加是 4/9+4⋅(1/9)+4⋅(1/36)=14/9+4\cdot(1/9)+4\cdot(1/36)=1。静止质量的 4/94/9 留在0号方向上。表里的三个权重,是一维权重 {1/6, 2/3, 1/6}\{1/6,\ 2/3,\ 1/6\} 的乘积。静止方向是 (2/3)×(2/3)=4/9(2/3)\times(2/3)=4/9。轴向是 (2/3)×(1/6)=1/9(2/3)\times(1/6)=1/9。对角是 (1/6)×(1/6)=1/36(1/6)\times(1/6)=1/36。四个轴向加四个对角,和再次闭上,4/9+4⋅(1/9)+4⋅(1/36)=14/9+4\cdot(1/9)+4\cdot(1/36)=1。九这个数目和三个分母,出自同一次张量积。

这一行就是等温LBM代码里的平衡。它是多项式,里面关着Maxwell–Boltzmann的前三个矩。

Python — 一次BGK碰撞#

一个节点上的一步碰撞。

import numpy as np
 
cx = np.array([0, 1, 0, -1, 0, 1, -1, -1, 1])
cy = np.array([0, 0, 1, 0, -1, 1, 1, -1, -1])
w  = np.array([4/9, 1/9, 1/9, 1/9, 1/9, 1/36, 1/36, 1/36, 1/36])
 
def equilibrium(rho, ux, uy):
    """D2Q9平衡 — 9个多项式"""
    ciu = cx * ux + cy * uy            # 点积
    usq = ux * ux + uy * uy
    return w * rho * (1 + 3 * ciu + 4.5 * ciu**2 - 1.5 * usq)
 
def bgk_collision(f, tau):
    rho = f.sum()
    ux  = (cx * f).sum() / rho
    uy  = (cy * f).sum() / rho
    feq = equilibrium(rho, ux, uy)
    return f - (f - feq) / tau, rho, ux, uy
 
# 非平衡初始分布
f = w * 1.0
f[1] += 0.05; f[3] -= 0.04          # 略向东偏
 
for step in range(20):
    f, rho, ux, uy = bgk_collision(f, tau=0.8)
    err = np.linalg.norm(f - equilibrium(rho, ux, uy))
    print(f"step {step:2d}: rho={rho:.4f}, u=({ux:+.4f},{uy:+.4f}), ||delta||={err:.2e}")
D2Q9 격자 위에서 9개 분포 함수가 BGK 충돌을 통해 평형으로 끌려가는 과정. 노란 화살표는 거시 속도 u, 가는 화살표는 각 격자 방향 c_i의 분포 크기.
τ→0.5에 가까울수록 한 스텝에 평형으로 끌려가지만, 점성 ν = c_s²(τ−1/2)도 작아져 안정성이 흔들린다.

初始分布从 f=wf=w 出发,给 f1f_1 加上0.05,从 f3f_3 减去0.04。质量是 1+0.05−0.04=1.011+0.05-0.04=1.01。xx 方向动量是 1⋅0.05+(−1)⋅(−0.04)=0.091\cdot 0.05+(-1)\cdot(-0.04)=0.09。所以 ux=0.09/1.01≈0.0891u_x=0.09/1.01\approx 0.0891,uy=0u_y=0。除以 cs=1/3c_s=1/\sqrt{3},马赫数约为0.154。上限0.3对应的格子速度是 0.3/3≈0.1730.3/\sqrt{3}\approx 0.173,0.0891在它下面。文中说大约二十步之后残差降到 10−610^{-6} 以下,指的就是贴到这个被守恒的 (1.01, 0.0891)(1.01,\ 0.0891) 上的平衡。碰撞每一步留下的只有 ρ\rho 和 ρu\rho\mathbf{u}。

把 τ\tau 拨向0.55,残差一两步就没了。粘性是 ν=cs2(τ−1/2)\nu=c_s^2(\tau-1/2)。代码里的 τ=0.8\tau=0.8 给出 ν=(1/3)⋅0.3=0.1\nu=(1/3)\cdot 0.3=0.1。τ=0.55\tau=0.55 给出 ν=(1/3)⋅0.05=1/60≈0.0167\nu=(1/3)\cdot 0.05=1/60\approx 0.0167,是代码粘性的六分之一。再把 τ\tau 推到 1/21/2,粘性变成0,格子失稳。把 τ\tau 加大,粘性上升,被拉向平衡的速度变慢。非平衡部分每一步乘上 1−1/τ1-1/\tau。代码里的 τ=0.8\tau=0.8 给出 1/τ=1.251/\tau=1.25,倍率就是 −0.25-0.25。符号会翻过来,这是过松弛。绝对值 0.250.25 乘20次是 0.2520≈9.1×10−130.25^{20}\approx 9.1\times 10^{-13}。偏差就算是1的量级,20步之后也远低于 10−610^{-6}。文中把20步当作 10−610^{-6} 的刻度,单凭这个倍率已经留了余量。τ=1\tau=1 时倍率为0,一步就落到平衡上。代码里的0.8,是从那一步平衡挪开一格的值。

过了马赫数0.3,就不要信这个多项式#

N=2N=2 只对准 ρ\rho、ρu\rho\mathbf{u} 和动量通量。再往上的都丢了。格子速度一过大约0.173,丢掉的项就不再小。把 τ\tau 降下,让粘性从 0.10.1 变到 1/601/60,平衡多项式本身不动。把速度抬过上限,和只把粘性降下,是两次不同的失败。

静止参考系里调好的粘性,参考系横移之后还在不在,这个式子答不了。被截掉的三阶拿着那个答案。参考系一动,缺失的三阶矩会不会改掉粘性仍然在这个多项式外面。

如果对您有帮助,请分享。