Skip to content
cfd-lab:~/es/posts/2026-06-26-mrt-lbm-momen…● online

Traducción del original en coreano escrito por el autor. Leer el original (한국어)

NOTE #084DAY FRI CFD기법DATE 2026.06.26UPDATED 2026.10.19READ 6 min read#LBM#MRT#Lattice-Boltzmann#Collision-Operator#Taylor-Green

Sobrevivir al régimen de baja viscosidad donde BGK explota — Colisión MRT en Lattice Boltzmann

Subir solo s_nu a 1.99 lleva la viscosidad hacia 0. Las barras que quedan, s_e y s_q, deciden si aparece el tablero.

Mismo retículo, mismo equilibrio. Uno gira. El otro se rompe en un tablero. BGK empuja las nueve distribuciones con un único tiempo de relajación. MRT (Multiple Relaxation Time, tiempo de relajación múltiple) las pasa a momentos y empuja cada uno a su propio ritmo.

Al llevar la barra sνs_\nu hasta 1.99, el número de viscosidad cae hacia 0. Las barras ses_e y sqs_q se quedan donde estaban.

D2Q9 moment-space relaxation rates · S = diag(s₀…s₈)
0.51.01.52.0ρe1.64ε1.54jxqx1.70jyqy1.70pxx1.20pxy1.20
shear viscosity ν = cs²(1/s_ν − 1/2) = 0.1111 (lattice units)
✓ all rates in (0, 2) — linearly stable region

Las barras conservadas ρ\rho, jxj_x, jyj_y siguen en cero. BGK ata todas las barras a un solo número. Ese nudo es el sitio del tablero a baja viscosidad.

La debilidad de relajarlo todo en un solo compás#

El método de lattice Boltzmann (LBM) repite dos pasos: las funciones de distribución fif_i se desplazan por el retículo (streaming) y, en cada punto, relajan hacia el equilibrio (collision). La colisión BGK más común se escribe como

fi(x+ci, t+1)=fi(x,t)−1τ(fi−fieq)f_i(\mathbf{x}+\mathbf{c}_i,\, t+1) = f_i(\mathbf{x}, t) - \frac{1}{\tau}\bigl(f_i - f_i^{\text{eq}}\bigr)

donde fif_i es la distribución a lo largo de la velocidad ci\mathbf{c}_i, τ\tau es el tiempo de relajación y fieqf_i^{\text{eq}} es el equilibrio local. La viscosidad queda fijada solo por τ\tau.

ν=cs2(τ−12),cs2=13\nu = c_s^2\left(\tau - \tfrac{1}{2}\right), \qquad c_s^2 = \tfrac{1}{3}

El problema empieza aquí. Para bajar la viscosidad (subir el número de Reynolds) hay que acercar τ\tau a 1/21/2. Pero cuando τ→1/2\tau \to 1/2, los modos de alto orden que nada tienen que ver con la viscosidad apenas relajan y recorren el retículo. Cuando esos modos crecen cerca del límite de resolución de la malla, surge una oscilación de tablero y la simulación diverge. En BGK, la perilla de la viscosidad y la de la estabilidad están atadas al mismo mango.

Mirar la colisión en el espacio de momentos#

No se relajan las nueve distribuciones tal como están. Primero se transforman en momentos con significado físico y después se relaja. Los momentos son combinaciones lineales de las distribuciones.

m=M f\mathbf{m} = M\,\mathbf{f}

Aquí f=(f0,…,f8)⊤\mathbf{f} = (f_0,\dots,f_8)^\top es el vector de distribuciones, MM es la matriz de transformación 9×99\times 9 y m\mathbf{m} es el vector de momentos. La colisión relaja hacia los momentos de equilibrio meq\mathbf{m}^{\text{eq}} en el espacio de momentos y luego regresa al espacio de velocidades.

f∗=f−M−1 S (Mf−meq)\mathbf{f}^{*} = \mathbf{f} - M^{-1}\,S\,\bigl(M\mathbf{f} - \mathbf{m}^{\text{eq}}\bigr)

S=diag(s0,…,s8)S = \mathrm{diag}(s_0,\dots,s_8) es la matriz diagonal con las tasas de relajación por momento. Si todos los sis_i se fijan al mismo valor 1/τ1/\tau, esta expresión vuelve exactamente a BGK. MRT no hace más que resolver esas entradas diagonales momento por momento.

Los nueve momentos de D2Q9 y la matriz de transformación M#

En el retículo D2Q9 (dos dimensiones, nueve velocidades) los momentos son estos nueve: densidad ρ\rho, energía ee, energía al cuadrado ε\varepsilon, momento jx,jyj_x, j_y, flujo de energía qx,qyq_x, q_y y los tipo tensión pxx,pxyp_{xx}, p_{xy}.

Entre ellos, ρ,jx,jy\rho, j_x, j_y son cantidades conservadas que la colisión no altera, así que sus tasas de relajación se fijan en s=0s=0. Los otros seis son las perillas ajustables. La matriz de transformación estándar de Lallemand–Luo se construye con filas ortogonales, por lo que la inversa es simplemente la transpuesta dividida por las normas de fila.

Los momentos de equilibrio cierran solo con densidad y momento.

eeq=−2ρ+3(jx2+jy2),εeq=ρ−3(jx2+jy2)qxeq=−jx,qyeq=−jypxxeq=jx2−jy2,pxyeq=jxjy\begin{aligned} e^{\text{eq}} &= -2\rho + 3(j_x^2 + j_y^2), &\quad \varepsilon^{\text{eq}} &= \rho - 3(j_x^2 + j_y^2) \\ q_x^{\text{eq}} &= -j_x, &\quad q_y^{\text{eq}} &= -j_y \\ p_{xx}^{\text{eq}} &= j_x^2 - j_y^2, &\quad p_{xy}^{\text{eq}} &= j_x j_y \end{aligned}

Aquí jx=ρuxj_x = \rho u_x, jy=ρuyj_y = \rho u_y son las componentes del momento. El equilibrio de los momentos de tensión pxx,pxyp_{xx}, p_{xy} se acopla a los gradientes de velocidad, así que sus tasas de relajación fijan la viscosidad.

Una tasa distinta por momento — grados de libertad ajenos a la viscosidad#

Quien fija la viscosidad de corte es la tasa de relajación de los momentos de tensión, sνs_\nu.

ν=cs2(1sν−12)\nu = c_s^2\left(\frac{1}{s_\nu} - \frac{1}{2}\right)

Las otras tasas ses_e (volumétrica), sεs_\varepsilon y sqs_q (flujo de energía) no entran en la viscosidad. Son libres. Aunque sνs_\nu se pegue a 22, los modos de alto orden pueden quedarse en la banda estable 1.5∼1.81.5\sim1.8. Lo que BGK ataba a una sola perilla se separa aquí.

La viscosidad por defecto del listado es ν=0.004\nu=0.004. Entonces sν=1/(3ν+1/2)s_\nu = 1/(3\nu + 1/2), de modo que 3×0.004+0.5=0.5123\times 0.004 + 0.5 = 0.512 y sν=1/0.512=1.953125s_\nu = 1/0.512 = 1.953125. Al llevar la barra a 1.99, ν=(1/3)(1/1.99−1/2)\nu = (1/3)(1/1.99 - 1/2). Restar 0.5 de 1/1.99≈0.50251/1.99 \approx 0.5025 y dividir por 3 deja ν≈8.4×10−4\nu \approx 8.4\times 10^{-4}. Cerca de cero, no cero. A esa viscosidad BGK también empuja ses_e, sεs_\varepsilon y sqs_q hasta 1.99. El MRT del listado fija esos tres sitios en 1.641.64, 1.541.54 y 1.71.7. El vector que corre es [0, 1.64, 1.54, 0, 1.7, 0, 1.7, s_nu, s_nu], así que los únicos ceros son los tres sitios conservados.

Los momentos de equilibrio se cierran solo con densidad y momento porque la distribución ya se cortó a orden bajo. Ese corte está en Maxwell–Boltzmann dentro de nueve velocidades.

Colisión en el espacio de momentos, streaming en el de velocidades#

El flujo del algoritmo es el siguiente.

  1. Calcular las magnitudes macroscópicas ρ,ux,uy\rho, u_x, u_y a partir de fif_i
  2. Transformar a momentos con m=Mf\mathbf{m} = M\mathbf{f}
  3. Calcular los momentos de equilibrio meq\mathbf{m}^{\text{eq}} (a partir de densidad y momento)
  4. Relajar por momento: ma←ma−sa(ma−maeq)m_a \leftarrow m_a - s_a(m_a - m_a^{\text{eq}})
  5. Restaurar al espacio de velocidades con f∗=M−1m\mathbf{f}^{*} = M^{-1}\mathbf{m}
  6. Streaming: mover fi∗f_i^{*} al vecino a lo largo de ci\mathbf{c}_i
  7. Aplicar condiciones de contorno y volver al paso 1

Solo la colisión ocurre en el espacio de momentos; el streaming transcurre en el espacio de velocidades como siempre. El costo adicional es apenas dos productos de matrices 9×99\times 9 por celda.

Python — Medir el decaimiento viscoso con un vórtice de Taylor–Green#

Para la verificación usamos un vórtice de Taylor–Green. Este flujo tiene una solución exacta de decaimiento viscoso, así que se puede contrastar la implementación con números. La energía cinética debe decaer como exp⁡(−4νk2t)\exp(-4\nu k^2 t), con kk el número de onda del vórtice.

import numpy as np
 
# Retículo D2Q9
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])
 
# Matriz de transformacion Lallemand-Luo (filas: rho,e,eps,jx,qx,jy,qy,pxx,pxy)
M = np.array([
    [ 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [-4,-1,-1,-1,-1, 2, 2, 2, 2],
    [ 4,-2,-2,-2,-2, 1, 1, 1, 1],
    [ 0, 1, 0,-1, 0, 1,-1,-1, 1],
    [ 0,-2, 0, 2, 0, 1,-1,-1, 1],
    [ 0, 0, 1, 0,-1, 1, 1,-1,-1],
    [ 0, 0,-2, 0, 2, 1, 1,-1,-1],
    [ 0, 1,-1, 1,-1, 0, 0, 0, 0],
    [ 0, 0, 0, 0, 0, 1,-1, 1,-1],
], dtype=float)
Minv = np.linalg.inv(M)
 
def d2q9_equilibrium(rho, ux, uy):
    eq = np.zeros((9,) + rho.shape)
    for i in range(9):
        cu = cx[i] * ux + cy[i] * uy
        eq[i] = w[i] * rho * (1 + 3*cu + 4.5*cu**2 - 1.5*(ux**2 + uy**2))
    return eq
 
def relaxation_rates(nu):
    s_nu = 1.0 / (3*nu + 0.5)          # la tasa que fija la viscosidad
    # conservados (rho,jx,jy)=0, modos de alto orden fijos en la banda estable
    return np.array([0, 1.64, 1.54, 0, 1.7, 0, 1.7, s_nu, s_nu])
 
def taylor_green_init(N, U0):
    x = np.arange(N)
    X, Y = np.meshgrid(x, x, indexing='ij')
    k = 2*np.pi / N
    ux = -U0 * np.cos(k*X) * np.sin(k*Y)
    uy =  U0 * np.sin(k*X) * np.cos(k*Y)
    rho = 1 - 0.75 * U0**2 * (np.cos(2*k*X) + np.cos(2*k*Y))
    return d2q9_equilibrium(rho, ux, uy), k
 
def macros(f):
    rho = f.sum(axis=0)
    ux = (cx[:, None, None] * f).sum(axis=0) / rho
    uy = (cy[:, None, None] * f).sum(axis=0) / rho
    return rho, ux, uy
 
def mrt_collide(f, s):
    rho, ux, uy = macros(f)
    jx, jy = rho*ux, rho*uy
    m = np.tensordot(M, f, axes=([1], [0]))        # al espacio de momentos
    meq = np.zeros_like(m)
    meq[0] = rho
    meq[1] = -2*rho + 3*(jx**2 + jy**2)
    meq[2] = rho - 3*(jx**2 + jy**2)
    meq[3], meq[4] = jx, -jx
    meq[5], meq[6] = jy, -jy
    meq[7] = jx**2 - jy**2
    meq[8] = jx*jy
    m -= s[:, None, None] * (m - meq)               # relajacion por momento
    return np.tensordot(Minv, m, axes=([1], [0]))    # de vuelta al espacio de velocidades
 
def stream(f):
    for i in range(9):
        f[i] = np.roll(f[i], (cx[i], cy[i]), axis=(0, 1))
    return f
 
def run_mrt_lbm(N=64, nu=0.004, U0=0.04, steps=2000):
    f, k = taylor_green_init(N, U0)
    s = relaxation_rates(nu)
    e0 = None
    for t in range(steps):
        f = mrt_collide(f, s)
        f = stream(f)
        if t % 400 == 0:
            _, ux, uy = macros(f)
            E = np.mean(ux**2 + uy**2)
            e0 = E if e0 is None else e0
            print(f"t={t:5d}  KE/KE0={E/e0:.4f}  analytic={np.exp(-4*nu*k**2*t):.4f}")
 
run_mrt_lbm()

El valor analítico que el print pone al lado de la energía cinética es exp⁡(−4νk2t)\exp(-4\nu k^2 t). Los valores por defecto son N=64N=64, ν=0.004\nu=0.004, k=2π/Nk=2\pi/N, así que 4νk2=0.016 (π/32)2≈1.54×10−44\nu k^2 = 0.016\,(\pi/32)^2 \approx 1.54\times 10^{-4}. En t=400t=400 el exponente vale cerca de −0.0617-0.0617 y el factor cerca de 0.940. En t=1600t=1600 queda cerca de −0.247-0.247 y el factor cerca de 0.781. Con range(2000) el último tt impreso es 1600. Coincidir a dos decimales es comparar ese factor con la energía cinética numérica. BGK en el mismo retículo y la misma ν\nu diverge antes, a una viscosidad más baja.

BGK y MRT lado a lado al mismo Re#

El control de viscosidad frena el decaimiento. El botón BGK/MRT decide si el retículo aguanta con esa misma ν\nu.

mode: MRT
step: 0
KE / KE₀: 1.000
analytic e⁻⁴ᵛᵏ²ᵗ: 1.000
Color = vorticity (red ↺ / blue ↻). Taylor–Green vortex on a 40×40 periodic lattice.

Al bajar la viscosidad hasta cerca de 0.0020.002 y dejar BGK, un patrón rugoso invade el retículo y aparece "diverged". Al pasar a MRT con la misma viscosidad y reiniciar, el vórtice decae con suavidad. Con ν=0.002\nu=0.002, sν=1/(3×0.002+0.5)=1/0.506≈1.976s_\nu=1/(3\times 0.002+0.5)=1/0.506\approx 1.976. BGK empuja también los modos altos a ese número. Los 1.641.64, 1.541.54 y 1.71.7 del listado se quedan al lado.

Dejar los modos fantasma cuando s_ν se acerca a 2#

MRT solo separa la perilla de la viscosidad sνs_\nu de las perillas de estabilidad ses_e y sqs_q. Copiar sν=1.953s_\nu=1.953 de ν=0.004\nu=0.004 a todos los modos es BGK. Dejar 1.641.64, 1.541.54 y 1.71.7 mientras solo sube sνs_\nu es MRT. El costo extra son dos productos 9×99\times 9 por celda.

Sin esa separación, un Taylor–Green de baja viscosidad muere en tablero. Otro camino para frenar la misma escapada es un teorema H dentro de la colisión. La comprobación del decaimiento sigue siendo la línea exp⁡(−4νk2t)\exp(-4\nu k^2 t).

Comparte si te resultó útil.