Translated from the author's Korean original. Read the original (한국어)
Nine Arrows of Maxwell–Boltzmann — Why LBM's Equilibrium Is a Polynomial
The code's eastward bias sits at ux = 0.0891, Mach about 0.154. The N=2 polynomial is only asked to hold up to a lattice speed near 0.173.
Lattice gas died in 1986. Occupancy was zero or one, and the noise would not average out. In 1992 those integers became a real distribution . The noise went away. The question stayed. How do nine real numbers stand in for infinite-dimensional Maxwell–Boltzmann?
Move drift and truncation order and the solid Maxwell–Boltzmann curve either sits on the dashed polynomial or pulls away from it.
At and , density, momentum, and energy lie on top of each other. Past only the dashed curve leaves. The Mach ceiling is not that peel-off. The numbers come later.
From integers to reals — LGCA died in 1986 and LBM was born#
LGCA, the lattice gas cellular automaton, was a square lattice, six directions, one collision rule. The macroscopic average came back as Navier–Stokes. Boolean particles (zero or one) grew statistical noise. A longer time average kills the noise and the transient with it.
McNamara and Zanetti put a real distribution on every node. is the expected count of particles at position and time moving along . One step splits in two. Collision, then streaming.
is the relaxation time. is the equilibrium distribution. Collision shaves the non-equilibrium part on a scale, and streaming shifts that distribution by one cell. Nine numbers are enough only because of the shape of .
Maxwell–Boltzmann carries a time bomb#
In continuum kinetic theory the equilibrium is Maxwell–Boltzmann.
is the molecular velocity, the macroscopic speed, the dimension, the temperature, the gas constant. The bomb is that is continuous. Keeping nine or nineteen directions means thinning this distribution.
A rectangle-rule stand-in for the Gaussian integral fails. The moments Navier–Stokes asks for — , , the momentum tensor , energy — miss the lattice sum. Break conservation and LBM stops there.
The way through is a series. Expand Maxwell–Boltzmann in orthogonal polynomials and keep only the moments you need. Those polynomials are Hermite.
Hermite polynomials chop the distribution into pieces#
The probabilist Hermite polynomials are orthogonal under the standard Gaussian weight .
The first few are , , , . Set the one-dimensional equilibrium to isothermal, , and the generating function gives this.
is a Gaussian at rest. is a small drift. corrects the mean kinetic energy. Higher are asymmetric tails. LBM cuts the sum at a finite order.
In that isothermal expansion , so the thermal speed is 1. The on the figure is already divided by that speed. The quoted ceiling, 0.3, is on this scale. sits 0.1 above the ceiling, and the first three moments at still match. The peel-off at is past Mach 1. The 0.3 and the 1 are not the same failure.
The term is , with . The coefficient alone is at . At it is , about 2.37 times the ceiling value. At it is , about 37 times 0.0045. That ratio is . throws the whole term away. So the density, momentum, and energy can still match at while the discarded cubic is already more than twice its size at the ceiling. The peel-off at is where that coefficient has climbed to about 0.17. The Mach ceiling at 0.3 is not the point where the curve visibly leaves. It is a line drawn in advance, at the moment the cubic coefficient passes 0.0045.
What second-order truncation buys you#
First-order viscous Navier–Stokes needs three layers. , , and the momentum flux . Because is , the distribution has to carry a term.
Cut the series at .
That is the continuous equilibrium. and above feed the heat flux. Standard isothermal LBM does not keep them. The discarded terms are what leave both the isothermal assumption and the Mach 0.3 ceiling. Put the cubic back and the isothermal assumption breaks first. Heat flux is the piece that term carries, so a code stopped at second order cannot take a problem where temperature flows and still use this polynomial. The Mach ceiling and the isothermal assumption are two faces of one cut. Even with speed held under 0.173, a hot wall and a cold wall on the same mesh get no heat transport from this equilibrium.
How D2Q9 was built#
Turning the integral into the lattice sum needs a quadrature. Matching through order means integrating polynomials of that order exactly.
In two dimensions, accuracy through means exactness for fifth-order polynomials under the Gaussian weight. The momentum tensor produces a term. Three-point Gauss–Hermite does that integral.
The one-dimensional nodes are , with weights . Why those three roots sit where they do is counted in the Hermite quadrature that folded a sixth moment down onto nine speeds. The two-dimensional tensor product is . That is D2Q9. In lattice units , and the velocities land on integer coordinates . The weights are the Gauss–Hermite weights with the Gaussian factor absorbed.
| Index | ||
|---|---|---|
| 0 | ||
| 1–4 | ||
| 5–8 |
On-lattice equilibrium is the continuous form with substituted.
, , . The 3, the , and the in the polynomial are those three numbers. At the bracket is 1 and . The weights sum to . Four ninths of a fluid at rest sits on direction 0. The three table weights are products of the one-dimensional set . Rest is . An axis direction is . A diagonal is . Four axes and four diagonals close the sum again, . The count of nine and the three denominators come out of the same tensor product.
That line is the equilibrium in an isothermal LBM code. A polynomial, with the first three moments of Maxwell–Boltzmann locked inside it.
Python — one BGK collision step#
One collision step, one node.
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 equilibrium — nine polynomials"""
ciu = cx * ux + cy * uy # dot product
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
# off-equilibrium initial state
f = w * 1.0
f[1] += 0.05; f[3] -= 0.04 # slight eastward bias
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}")The initial state starts from , adds 0.05 to , and subtracts 0.04 from . Mass is . The -momentum is . So and . Divide by and the Mach number is about 0.154. The lattice speed at the 0.3 ceiling is , and 0.0891 is under it. The residual the run drives below in about twenty steps is the approach to equilibrium on this conserved pair . Each collision keeps only and .
Drop toward 0.55 and the residual is gone in a step or two. Viscosity is . At the code's , . At , , one sixth of the code value. Push all the way to and viscosity hits zero. The lattice goes unstable. Raise and viscosity grows while the pull toward equilibrium slows down. One step multiplies the non-equilibrium part by . At the code's , , so the factor is . The sign flips. That is over-relaxation. Twenty factors of give . A deviation of order 1 is already far under after twenty steps. Taking twenty steps as the mark for is generous on this factor alone. At the factor is 0 and the state lands on equilibrium in one step. The code's 0.8 stands one notch off that single-step equilibrium.
Past Mach 0.3, do not trust this polynomial#
matches , , and the momentum flux. Everything above that was dropped. Once the lattice speed passes about 0.173, the dropped terms are no longer small. Lowering so viscosity falls from to does not change the equilibrium polynomial. Raising the speed through the ceiling and lowering the viscosity are different failures.
Whether a viscosity tuned in a rest frame survives a shift of that frame is not answered here. The discarded cubic term holds that answer. Whether the missing third moment changes viscosity once the frame moves still sits outside this polynomial.
Related
Share if you found it helpful.