IB-LBM Fluid Node#
Module: mime.nodes.environment.lbm.fluid_node
Class: IBLBMFluidNode
Stability: experimental
Algorithm ID: MIME-NODE-010
Version: 1.0.0
Verification Mode: Mode 2 (Independent)
Summary#
3D immersed-boundary lattice Boltzmann (IB-LBM) fluid solver for
confined microrobot flows. Wraps the existing D3Q19 LBM utilities
(d3q19.py, bounce_back.py, helix_geometry.py) as a MADDENING
SimulationNode so the volumetric flow can be coupled into a
node-graph with rigid-body and magnetic-actuation nodes via edges.
The pipe wall uses simple bounce-back; the UMR body uses simple
bounce-back or Bouzidi interpolated bounce-back depending on
use_bouzidi.
Governing Equations#
D3Q19 BGK lattice Boltzmann equation:
with macroscopic moments
and the equilibrium expansion to \(O(u^2)\) in the lattice speed.
Body coupling uses momentum-exchange [@Ladd1994] on the post-streaming populations along boundary links, giving force and torque
Discretization#
D3Q19 stencil; BGK collision with relaxation time \(\tau\).
Two-pass bounce-back per step: static pipe wall first, then the rotating UMR body. The body mask is rebuilt each step from the SDF at the current rotation angle so the wall geometry stays differentiable in pose.
Bouzidi interpolated bounce-back [@Bouzidi2001] uses sparse SDF \(q\)-values along each missing link;
max_boundary_links_per_diris pre-sized at construction to avoid silent truncation insidejnp.nonzero.
Implementation Mapping#
Equation Term |
Implementation |
Notes |
|---|---|---|
BGK collision + streaming |
|
|
Equilibrium \(f_i^{\text{eq}}\) |
|
|
Pipe wall bounce-back |
|
First pass |
Bouzidi IBB on UMR |
|
Second pass |
Sparse \(q\)-values from SDF |
|
|
Momentum-exchange force |
|
|
Momentum-exchange torque |
|
|
LBM→SI conversion |
|
Edge transform |
Assumptions and Simplifications#
Incompressible flow: \(\mathrm{Ma} \ll 1\) at every lattice node (validated for \(\mathrm{Ma}_{\text{tip}} \le 0.1\)).
Newtonian fluid; viscosity derived from \(\tau\).
Rigid body, single rotation axis (z by default — only
body_angular_velocity[2]is consumed).Stencil node:
halo_width()returns{0: 1, 1: 1, 2: 1}(D3Q19 streaming reads one neighbour per spatial axis). This marks the node non-pointwise — it runs on a single device by default, and shards across devices when wrapped in MADDENING v0.2.1’sShardedStencilNode(see Multi-GPU sharding below). See Node API migration.The static pipe-wall mask is constructed once at init; only the dynamic body mask is rebuilt per step.
Validated Physical Regimes#
Parameter |
Verified Range |
Notes |
|---|---|---|
\(\mathrm{Ma}_{\text{tip}}\) |
0 – 0.1 |
Tip Mach number bound for incompressibility |
\(\tau\) |
0.55 – 1.5 |
BGK relaxation stability window |
\(\mathrm{Re}_{\text{rot}}\) |
0 – ~30 |
Tested across helical-UMR replication cases |
Known Limitations and Failure Modes#
Bouzidi interpolated bounce-back is not yet supported on the multi-GPU sharded path (it needs per-slab SDF q-value recomputation); the node guards
use_bouzidi=Trueunder sharding with a clearNotImplementedError. Simple bounce-back shards. Single-device runs support both.Per-step \(q\)-value recomputation costs ~0.1 s at \(192^3\) on H100.
First step triggers JAX compilation (30–60 s at \(192^3\)).
If
max_boundary_links_per_diris undersized,jnp.nonzerosilently truncates boundary links and accuracy degrades without error. The node uses a 1.5× margin over the at-init worst-case count.Diffuse IB kernels (Peskin delta) would create direction-dependent velocity transfer errors at clinical frequencies; this node uses bounce-back instead.
Stability Conditions#
\(\tau \ge 0.55\) to keep BGK stable for the discrete equilibrium.
\(\mathrm{Ma} \le 0.1\) for the incompressible-limit expansion to hold pointwise.
Multi-GPU sharding#
Added in version v0.2: Pencil decomposition across devices via MADDENING v0.2.1’s
ShardedStencilNode.
Construct the node with multigpu_shard_axis=<spatial axis> and wrap it:
from maddening.cloud.multigpu.device_mesh import create_device_mesh
from maddening.cloud.multigpu.sharded_node import ShardedStencilNode
node = IBLBMFluidNode(..., multigpu_shard_axis=2)
mesh = create_device_mesh(shape=(4,))
sharded = ShardedStencilNode(node, mesh, axis_map={"devices": 2},
boundary="periodic")
The sharded update_padded collides on the halo-padded slab, streams with
the slice-based d3q19.stream_padded, recomputes the pipe + UMR missing-link
masks per slab (bounce_back.compute_missing_mask_sharded — periodic roll on
the full axes, halo-slice on the shard axis), rebuilds the UMR body and
rotation-velocity field on the slab’s global coordinates (an origin
offset on the geometry helpers and the momentum-exchange torque), and returns
drag_force / drag_torque as per-slab partial sums that the wrapper
lax.psums (declared via domain_integral_fields()). Only the pipe wall is a
sharded StaticArray (it is axis-aligned (nx, ny, nz)); the missing-link
masks are recomputed per slab.
This is bit-identical to the single-device step under jit, validated on a
4-device CPU virtual-device mesh in
tests/verification/test_lbm_sharded_contract.py. Bouzidi IBB on the sharded
path is deferred (see Known Limitations).
State Variables#
Field |
Shape |
Units |
Description |
|---|---|---|---|
f |
(nx, ny, nz, 19) |
lattice |
D3Q19 populations (the only spatial state) |
body_angle |
() |
rad |
Accumulated body rotation |
drag_force |
(3,) |
lattice |
Momentum-exchange force — a domain-integral output, not evolving state ( |
drag_torque |
(3,) |
lattice |
Momentum-exchange torque — domain-integral output |
The pipe wall and its missing-link mask are not state: the pipe wall is a
non-evolving static_data array and the UMR mask is recomputed from
body_angle each step. state_fields() is therefore ["f", "body_angle"].
Parameters#
Parameter |
Type |
Default |
Units |
Description |
|---|---|---|---|---|
nx, ny, nz |
int |
— |
cells |
Lattice dimensions |
tau |
float |
— |
— |
BGK relaxation time |
vessel_radius_lu |
float |
— |
lattice |
Pipe radius in lattice units |
body_geometry_params |
dict |
— |
lattice |
|
use_bouzidi |
bool |
False |
— |
Interpolated bounce-back on body (not on the sharded path) |
dx_physical |
float |
1.0 |
m |
Physical lattice spacing (for SI conversion) |
multigpu_shard_axis |
int | None |
None |
— |
Spatial axis (0/1/2) to decompose across devices; set when wrapping in |
Boundary Inputs#
Field |
Shape |
Default |
Coupling Type |
Description |
|---|---|---|---|---|
body_angular_velocity |
(3,) |
zeros |
replacive |
Angular velocity [rad/step] in lattice units |
body_orientation |
(4,) |
(1,0,0,0) |
replacive |
Body orientation quaternion |
Boundary Fluxes (outputs)#
Field |
Shape |
Units |
Description |
|---|---|---|---|
drag_force |
(3,) |
lattice |
Momentum-exchange force (convert via edge) |
drag_torque |
(3,) |
lattice |
Momentum-exchange torque (convert via edge) |
MIME-Specific Sections#
Anatomical Operating Context#
Compartment |
Flow Regime |
Re Range |
Viscosity Range |
|---|---|---|---|
Iliac artery (confined UMR) |
quasi-stagnant |
0 – 0.1 |
3 – 4 mPa·s |
Clinical Relevance#
LBM gives the full volumetric flow that drives pulsatile drag and near-wall lubrication on the UMR. In the hybrid solver, LBM provides the background velocity that the Stokeslet BEM uses as its Schwarz boundary condition — the LBM does not discretise the body itself, which sidesteps Mach-number and IB-kernel pathologies at high rotation rates.
References#
[@Ladd1994] Ladd (1994). Numerical simulations of particulate suspensions via a discretized Boltzmann equation. J. Fluid Mech. 271, 285–309.
[@Bouzidi2001] Bouzidi, Firdaouss & Lallemand (2001). Momentum transfer of a Boltzmann-lattice fluid with boundaries. Phys. Fluids 13(11).
Verification Evidence#
MIME-VER-lbm-001: Poiseuille flow in cylinder (analytical)
MIME-VER-lbm-002: Womersley unsteady pipe flow
MIME-VER-lbm-003: Rotating-sphere torque vs. Stokes formula
Unit tests:
tests/nodes/lbm/Multi-GPU bit-compat:
tests/verification/test_lbm_sharded_contract.py
Changelog#
Version |
Date |
Change |
|---|---|---|
1.0.0 |
2026-02-18 |
Initial implementation — D3Q19 BGK + Bouzidi IBB |
1.1.0 |
2026-05-31 |
v0.2: |