simulation: keep the owner context active through moving diagnostics readback
moving_diagnostics is context_bound, so stream synchronization and the DTOH copy run inside one owner activation. Tests: owner stays active through sync and download; launch order moving -> static curved -> OneStep with a real static link count and the buffer swap between steps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e25828968b
commit
b920a303a4
@@ -226,6 +226,7 @@ class Simulation:
|
|||||||
from .body.motion import MovingTables
|
from .body.motion import MovingTables
|
||||||
return MovingTables.upload([(obj.motion, obj.obj_id) for obj in moving])
|
return MovingTables.upload([(obj.motion, obj.obj_id) for obj in moving])
|
||||||
|
|
||||||
|
@context_bound
|
||||||
def moving_diagnostics(self) -> np.ndarray:
|
def moving_diagnostics(self) -> np.ndarray:
|
||||||
"""Per translating circle (in body order): cme_x, cme_y, wen_x, wen_y, link_dm,
|
"""Per translating circle (in body order): cme_x, cme_y, wen_x, wen_y, link_dm,
|
||||||
n_links accumulated since obs were last zeroed (diagnostics only)."""
|
n_links accumulated since obs were last zeroed (diagnostics only)."""
|
||||||
|
|||||||
@@ -261,21 +261,61 @@ def test_moving_tables_layout_and_attachment(monkeypatch):
|
|||||||
assert sim.stepper.moving is None and sim.stepper.blocked is not None
|
assert sim.stepper.moving is None and sim.stepper.blocked is not None
|
||||||
|
|
||||||
|
|
||||||
|
class CountingOwner(Owner):
|
||||||
|
"""Owner double that counts nested activations like CudaContext."""
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.active = 0
|
||||||
|
|
||||||
|
def activate(self):
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def scope():
|
||||||
|
self.active += 1
|
||||||
|
try:
|
||||||
|
yield self
|
||||||
|
finally:
|
||||||
|
self.active -= 1
|
||||||
|
return scope()
|
||||||
|
|
||||||
|
|
||||||
|
def test_public_diagnostics_keep_owner_active_through_sync_and_download(monkeypatch):
|
||||||
|
sim = make_simulation(monkeypatch)
|
||||||
|
sim.ctx, sim._failed = CountingOwner(), None
|
||||||
|
seen = []
|
||||||
|
sim._stream = SimpleNamespace(synchronize=lambda: seen.append(("sync", sim.ctx.active)))
|
||||||
|
expected = np.arange(6, dtype=np.float32).reshape(1, 6)
|
||||||
|
|
||||||
|
def download():
|
||||||
|
seen.append(("download", sim.ctx.active))
|
||||||
|
return expected
|
||||||
|
sim.stepper = SimpleNamespace(moving=SimpleNamespace(download_diagnostics=download))
|
||||||
|
assert sim.moving_diagnostics() is expected
|
||||||
|
assert [name for name, _ in seen] == ["sync", "download"]
|
||||||
|
assert all(active > 0 for _, active in seen) and sim.ctx.active == 0
|
||||||
|
|
||||||
|
|
||||||
def test_stepper_launches_moving_links_before_static_curved():
|
def test_stepper_launches_moving_links_before_static_curved():
|
||||||
from CelerisLab.lbm.stepper import LBMStepper
|
from CelerisLab.lbm.stepper import LBMStepper
|
||||||
launches = []
|
launches = []
|
||||||
module = SimpleNamespace(get_function=lambda name: lambda *a, **k: launches.append((name, a, k)))
|
module = SimpleNamespace(get_function=lambda name: lambda *a, **k: launches.append((name, a, k)))
|
||||||
cfg = SimpleNamespace(streaming="double_buffer", threads_per_block=128, is_d2q9=True,
|
cfg = SimpleNamespace(streaming="double_buffer", threads_per_block=128, is_d2q9=True,
|
||||||
nx=64, ny=32, nz=1)
|
nx=64, ny=32, nz=1)
|
||||||
field = SimpleNamespace(ddf_gpu="ddf", temp_gpu="temp", flag_gpu="flag", n_curved=0,
|
curved = SimpleNamespace(**{f"{name}_gpu": name for name in (
|
||||||
invalidate_host_ddf_cache=lambda: None)
|
"fluid_idx", "dir", "q", "rx", "ry", "rz", "fallback_class", "body_id")},
|
||||||
|
surface_velocity=None, surface_velocity_gpu=None, link_exchange_gpu=None)
|
||||||
|
field = SimpleNamespace(ddf_gpu="ddf", temp_gpu="temp", flag_gpu="flag", n_curved=5,
|
||||||
|
curved=curved, invalidate_host_ddf_cache=lambda: None)
|
||||||
stepper = LBMStepper(field, module, cfg)
|
stepper = LBMStepper(field, module, cfg)
|
||||||
stepper._launch_force_region = stepper._launch_sensor = lambda *a, **k: None
|
stepper._launch_force_region = stepper._launch_sensor = lambda *a, **k: None
|
||||||
stepper.moving = SimpleNamespace(band_gpu="band", geom_gpu="geom", diag_gpu="diag",
|
stepper.moving = SimpleNamespace(band_gpu="band", geom_gpu="geom", diag_gpu="diag",
|
||||||
slot_debug_gpu=None, n_bodies=1, n_slots=300)
|
slot_debug_gpu=None, n_bodies=1, n_slots=300)
|
||||||
stepper.step(2, action_gpu="act", obs_gpu="obs")
|
stepper.step(2, action_gpu="act", obs_gpu="obs")
|
||||||
names = [name for name, _, _ in launches]
|
names = [name for name, _, _ in launches]
|
||||||
assert names == ["MovingCircleLinks", "OneStep"] * 2 and stepper.step_count == 2
|
assert names == ["MovingCircleLinks", "CurvedBoundaryKernel", "OneStep"] * 2
|
||||||
|
assert stepper.step_count == 2
|
||||||
|
assert launches[1][1][0] == "ddf" and launches[3][1][0] == "temp" # swap between steps
|
||||||
args, kw = launches[0][1], launches[0][2]
|
args, kw = launches[0][1], launches[0][2]
|
||||||
assert args[:4] == ("ddf", "flag", "band", "geom") and int(args[5]) == 300
|
assert args[:4] == ("ddf", "flag", "band", "geom") and int(args[5]) == 300
|
||||||
assert kw["grid"] == (3, 1, 1) and kw["block"] == (128, 1, 1)
|
assert kw["grid"] == (3, 1, 1) and kw["block"] == (128, 1, 1)
|
||||||
|
|||||||
Reference in New Issue
Block a user