test(D1): exchange, Jet and fine-surface checks run on all-blocked storage
- test_exchange_window and test_raw_exchange_gpu fine(): dense root becomes a (12, 8) blocked root (fine(None) stays only for the dense reference test that D2 retires); - jets: G1/G3 compare blocked layouts against the FINE_BLOCKS[0] reference (blocked root); the dense run is a separate leg D2 removes; G2 legacy actuation vs Jet on the blocked reference; CPU fixtures use a blocked root; - test_refinement_blocked_fine.raw reads every level through its layout; - test_fine_surface_gpu: new test_fine_surface_blocked_matches_uniform_four_hops (fine ROI after each of four hops vs the uniform 48^2 solver with the same tolerances, blowing/suction mass sign from raw exchange, zero actuation == passive bit for bit); the cross-interface donor rejection passes tiles. The dense hook/ledger/audit probes in test_fine_surface_four_hops and the dense SoA fault tests stay until D2 retires them by name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
452eaf32de
commit
104f74632f
@@ -279,6 +279,61 @@ def test_fine_surface_four_hops(speed):
|
||||
np.testing.assert_array_equal(got, wanted)
|
||||
|
||||
|
||||
def run_blocked_patch(speed):
|
||||
"""All-blocked run (whole-level tiles) of the same case; fine ROI after each fine hop."""
|
||||
cfg = LBMConfig(nx=24, ny=24, viscosity=.05, velocity=0, rho=1)
|
||||
source = object_spec(speed)
|
||||
with CudaContext(0) as context:
|
||||
with implementation.FixedPatchStepper(cfg, context=context, refinements=[BOUNDS],
|
||||
level_blocks=[(24, 24), (40, 40)],
|
||||
level_objects=[(), (source,)],
|
||||
observe_raw_exchange=True) as backend:
|
||||
c, f = backend._levels
|
||||
flags = backend.fine_boundary.flags.reshape(40, 40)
|
||||
active = np.zeros((40, 40), bool)
|
||||
active[4:-4, 4:-4] = True
|
||||
active &= ((flags & FLUID) != 0) & ((flags & SOLID) == 0)
|
||||
yy, xx = np.mgrid[:40, :40]
|
||||
roi = active & ((xx-20)**2+(yy-20)**2 <= 9**2)
|
||||
backend.initialize(rest((24, 24)), rest((40, 40)))
|
||||
traces, original = [], f.kernel
|
||||
|
||||
def fine(*args, **kwargs):
|
||||
original(*args, **kwargs)
|
||||
backend._stream.synchronize()
|
||||
advanced = f.layout.to_dense(read(args[1], f.layout.blocked_shape))
|
||||
assert np.isfinite(advanced[:, active]).all()
|
||||
assert np.isnan(advanced[:, (flags & SOLID) != 0]).all()
|
||||
traces.append(advanced[:, roi].copy())
|
||||
|
||||
with patch.object(f, "kernel", fine):
|
||||
backend.step(2)
|
||||
assert len(traces) == 4 and backend.step_count == 2
|
||||
mass = backend.read_raw_exchange(0)["total"]["mass_into_fluid"]
|
||||
return traces, roi, backend.download(), float(mass)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("speed", [0., .002, -.002])
|
||||
def test_fine_surface_blocked_matches_uniform_four_hops(speed):
|
||||
"""Blocked storage: fine ROI after each of four hops against the uniform 48^2 solver
|
||||
(same tolerances as the dense test); blowing/suction drives mass with its sign;
|
||||
zero actuation equals the passive circle bit for bit."""
|
||||
actual, roi, final, mass = run_blocked_patch(speed)
|
||||
reference = uniform_reference(speed, roi)
|
||||
for got, wanted in zip(actual, reference):
|
||||
np.testing.assert_allclose(got, wanted, atol=3e-6, rtol=0)
|
||||
np.testing.assert_allclose(MOMENTS@got.astype(float), MOMENTS@wanted.astype(float),
|
||||
atol=2e-5, rtol=0)
|
||||
if speed:
|
||||
assert speed*mass > 0
|
||||
else:
|
||||
passive, passive_roi, passive_final, _ = run_blocked_patch(None)
|
||||
np.testing.assert_array_equal(passive_roi, roi)
|
||||
for got, wanted in zip(actual+list(final), passive+list(passive_final)):
|
||||
np.testing.assert_array_equal(got, wanted)
|
||||
print(f"blocked speed={speed} four hops vs uniform PASS mass_into_fluid={mass:+.9g}", flush=True)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("missing", ["surface_velocity_gpu", "link_exchange_gpu"])
|
||||
def test_missing_surface_device_buffer_rejects_before_launch(missing):
|
||||
backend = implementation.FixedPatchStepper.__new__(implementation.FixedPatchStepper)
|
||||
@@ -328,6 +383,7 @@ def test_cross_interface_donor_rejects_before_cuda():
|
||||
with patch.object(cuda, "mem_alloc") as allocate:
|
||||
with pytest.raises(ValueError, match="donor crosses"):
|
||||
implementation.FixedPatchStepper(cfg, context=context, refinements=[BOUNDS],
|
||||
level_blocks=[(24, 24), (40, 40)],
|
||||
level_objects=[(), (source,)])
|
||||
context.activate.assert_not_called()
|
||||
allocate.assert_not_called()
|
||||
|
||||
Reference in New Issue
Block a user