Unit-Aware Edge Transforms#
Module: maddening.core.transforms_unit
Stability: stable
Overview#
When coupling nodes that use different physical unit systems (e.g. an LBM fluid solver in lattice units coupled to a rigid body in SI), the edge between them must apply a unit conversion. MADDENING supports this via:
Unit annotations on edges and boundary specs (documentation/validation).
Transform factories that produce JAX-traceable conversion callables.
Unit Annotations#
On edges#
gm.add_edge(
"fluid", "body", "drag_force", "force",
transform=lbm_to_si_force(dx=1e-3, dt=1e-6, rho=1060.0),
source_units="lattice",
target_units="N",
)
On nodes#
Nodes declare expected units on their boundary inputs:
def boundary_input_spec(self):
return {
"force": BoundaryInputSpec(
shape=(3,), coupling_type="additive",
expected_units="N",
),
}
And on flux outputs:
def boundary_flux_spec(self):
return {
"drag_force": BoundaryFluxSpec(
shape=(3,), output_units="lattice",
),
}
Compile-time validation#
GraphManager.validate() checks two conditions:
If an edge declares
target_unitsthat differs from the target node’sexpected_units, a warning is emitted.If an edge has
source_unitsdifferent fromexpected_unitsand notransformis set, a warning is emitted.
These are warnings, not errors – they do not block compilation.
LBM Lattice Unit Conversions#
LBM operates in lattice units where $\Delta x = \Delta t_{\text{LBM}} = 1$. Physical conversion requires three parameters:
Parameter |
Symbol |
Description |
|---|---|---|
|
$\Delta x$ |
Physical lattice spacing [m] |
|
$\Delta t$ |
Physical timestep per LBM step [s] |
|
$\rho_0$ |
Reference fluid density [kg/m$^3$] |
Conversion factors#
Quantity |
Factor (multiply LBM to get SI) |
Factory |
|---|---|---|
Length |
$\Delta x$ |
|
Velocity |
$\Delta x / \Delta t$ |
|
Force |
$\rho_0 , \Delta x^4 / \Delta t^2$ |
|
Torque |
$\rho_0 , \Delta x^5 / \Delta t^2$ |
|
Pressure |
$\rho_0 , (\Delta x / \Delta t)^2$ |
|
Usage#
from maddening.core.transforms import lbm_to_si_force, lbm_to_si_torque
# Parameters for a specific LBM setup
dx = 10e-6 # 10 um lattice spacing
dt = 1e-7 # 100 ns per LBM step
rho = 1060.0 # blood density kg/m^3
# Create transform callables (pure JAX functions)
force_transform = lbm_to_si_force(dx, dt, rho)
torque_transform = lbm_to_si_torque(dx, dt, rho)
# Use on edges
gm.add_edge("fluid", "body", "drag_force", "force",
transform=force_transform,
source_units="lattice", target_units="N")
gm.add_edge("fluid", "body", "drag_torque", "torque",
transform=torque_transform,
source_units="lattice", target_units="N*m")
The returned callables are:
JAX-traceable: work inside
jax.jit,jax.lax.scan,jax.lax.fori_loopDifferentiable:
jax.gradflows through them (they are scalar multiplications)vmap-compatible: work with
jax.vmapfor parameter sweeps
Optional registration#
If you need named transforms for USD serialization, register them manually:
from maddening.core.transforms import register_transform
register_transform("my_force_lbm_to_si")(force_transform)
MIME Follow-on#
When MADDENING’s unit-aware edges are merged, IBLBMFluidNode in MIME
should be updated to:
Declare
expected_units="lattice"on boundary inputs (angular velocity, orientation).Override
boundary_flux_spec()to declareoutput_units="lattice"ondrag_forceanddrag_torque.Edges connecting
IBLBMFluidNodetoRigidBodyNodeshould carrylbm_to_si_force/lbm_to_si_torquetransforms with appropriatesource_units="lattice"andtarget_units="N"/target_units="N*m".