stepper: bind moving-circle kernels only for D2Q9 double buffer

Both kernels are compiled under DIM == 2, but LBMStepper requested them for
every double-buffer module, so stationary D3Q19 double-buffer construction
would fail at get_function. Moving circles already reject D3Q19 and EsoPull
at initialize. Stale latch comment updated. Parametrized CPU test with a
module double that lacks the symbols.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-10-31 01:23:54 +00:00
co-authored by Claude Opus 5.5
parent 0d6df6e6b8
commit 353a7b03c4
2 changed files with 23 additions and 3 deletions
+5 -3
View File
@@ -21,7 +21,7 @@ class LBMStepper:
self._esopull = (cfg.streaming == "esopull")
# Set by the owner when a step would be wrong (translating circles
# excluded from the packed links before the moving kernels exist).
# without attached moving tables, a pending target, or a failed step).
self.blocked: str | None = None
self.moving = None # MovingTables attached by the owner (translating circles)
@@ -39,8 +39,10 @@ class LBMStepper:
self.apply_esopull_curved_fn = module.get_function("ApplyEsoPullCurvedKernel")
self.sensor_fn = module.get_function("SensorKernel")
self.force_region_fn = module.get_function("ForceRegionKernel")
self.moving_fn = None if self._esopull else module.get_function("MovingCircleLinks")
self.moving_cells_fn = None if self._esopull else module.get_function("MovingCircleCells")
# Moving-circle kernels exist only in D2Q9 double-buffer modules.
moving = not self._esopull and cfg.is_d2q9
self.moving_fn = module.get_function("MovingCircleLinks") if moving else None
self.moving_cells_fn = module.get_function("MovingCircleCells") if moving else None
tpb = cfg.threads_per_block
self.block = (tpb, 1, 1)
+18
View File
@@ -310,6 +310,24 @@ def test_zero_motion_target_is_consumed_by_run_one(monkeypatch):
assert ("step", 3, False) in writes
@pytest.mark.parametrize("streaming,d2q9,bound", [("double_buffer", True, True),
("double_buffer", False, False),
("esopull", True, False)])
def test_moving_kernels_bound_only_for_d2q9_double_buffer(monkeypatch, streaming, d2q9, bound):
from CelerisLab.lbm import stepper as stepper_module
monkeypatch.setattr(stepper_module.cuda, "mem_alloc", lambda n: object())
moving = {"MovingCircleLinks", "MovingCircleCells"}
def get_function(name):
if name in moving and not (d2q9 and streaming == "double_buffer"):
raise KeyError(name) # absent from D3Q19 / EsoPull modules
return lambda *a, **k: None
cfg = SimpleNamespace(streaming=streaming, threads_per_block=128, is_d2q9=d2q9,
nx=16, ny=8, nz=4, nq=9 if d2q9 else 19)
stepper = stepper_module.LBMStepper(SimpleNamespace(), SimpleNamespace(get_function=get_function), cfg)
assert (stepper.moving_fn is not None) == bound and (stepper.moving_cells_fn is not None) == bound
def test_moving_diagnostics_empty_shape_matches_layout(monkeypatch):
from CelerisLab.body.motion import MovingTables
sim = make_simulation(monkeypatch)