The second owner of one blocked level goes. Its capability is the zero-refinement FixedPatchStepper added in f764d68/985db14 (periodic SRT level, stationary passive circles, shifted storage, NaN spare, single wait), which matched it array for array on GPU at1318fc2(3 splits x +-circle x shifted/unshifted) and passes the host lifecycle and fault contracts. API change for callers: PeriodicBlockStepper(cfg, BlockLayout2D.build(ny, nx, by, bx), context=c, objects=o) -> FixedPatchStepper(cfg, context=c, refinements=[], level_blocks=[(by, bx)], level_objects=[tuple(o)]) download() returns a 1-tuple; static-circle tables are _level_boundaries[0].tables (was .boundary); no free-memory preflight. - lbm/block_stepper.py deleted; MAX_GRID_DIM lives in refinement_stepper. - tests/validation/multigrid/test_block_stepper.py: same frozen GPU gates (split identity, FP64 oracle, circle across seams) and pre-CUDA rejection cases on the single level; the old-vs-new bitwise test is retired with the old class (its result is the1318fc2record). - tests/unit/test_block_step_sync.py folded into test_single_level.py (step(n) vs n x step(1) roles and one wait, step(0), mid-batch launch failure), which also gains bad-state rejection before upload, download failure and activation-failure retry on close. - test_shifted_storage and test_raw_exchange_gpu use the single level; the raw-exchange reference feeds the AMR Jet column and raw ledger through the root level boundary instead of patching a private kernel field. - Pinned historical runners (taylor_green/run_case.py, run_jet_circle_cv.py) are unchanged: they only run on trees that still ship PeriodicBlockStepper. No alias. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7.9 KiB
Supported settings: 2D Jet + fixed multilevel refinement (candidate 888c8038)
Every entry names the guard that enforces it (file and function or kernel guard,
at source revision 888c80381c) and, where one
exists, an existing test that exercises it. A setting not listed here is not
claimed. Observations at the end are tied to the exact case in which they were
measured; they are not general bounds.
Lattice, storage, collision
| Setting | Supported | Guard |
|---|---|---|
| Dimension / lattice | 2D D2Q9 only (3D refinement is future work) | lbm/refinement.py FixedPatch2D.from_config; #error guards in lbm/kernels/blocks.cu and lbm/kernels/refinement.cu |
| Precision / streaming | FP32 storage, double buffer | same guards |
| Collision on refinement levels | SRT or MRT; TRT and LES rejected | lbm/refinement.py FixedPatch2D.from_config (re-checked for fine boundaries in prepare_fine_boundary) |
| Shifted (ddf_shifting) storage | only when every level is blocked; dense or mixed rejected | lbm/refinement_stepper.py FixedPatchStepper.__init__, FixedPatchStepper._check_storage; #error guard in lbm/kernels/refinement.cu (dense patch kernel unshifted only) |
| Fine relaxation | 1/tau_fine must lie inside the configured omega guards | lbm/refinement.py FixedPatch2D.from_config |
Single blocked level FixedPatchStepper(refinements=[], level_blocks=[(by, bx)], level_objects=[circles]) (replaces PeriodicBlockStepper after 888c8038) |
doubly periodic, SRT only, no LES/Jets/projection/exchange observation, stationary passive circles; download() returns a 1-tuple |
lbm/refinement_stepper.py _check_levels, FixedPatchStepper.__init__; lbm/blocks.py static_circle_tables |
Tests: tests/unit/test_refinement_mrt.py, tests/validation/multigrid/test_refinement_mrt_gpu.py,
tests/unit/test_shifted_storage.py, tests/unit/test_shifted_block_operators.py.
Refinement hierarchy (FixedPatchStepper(cfg, *, context, refinements, ...))
| Setting | Supported | Guard |
|---|---|---|
| Depth | at least one refinement level; one entry per level in level_blocks/level_objects/level_jets |
lbm/refinement_stepper.py _check_levels |
| Single L1 rectangle | two-coarse-cell margin to the domain edge | lbm/refinement.py FixedPatch2D.from_config |
| Several L1 rectangles (union) | require level_blocks[0] and level_blocks[1]; transfers must close (dependency checks, no fixed gap rule) |
lbm/refinement_stepper.py _check_levels; lbm/refinement.py _check_parent_pull_closure |
| Nested levels | depth >= 3 must be all blocked; a blocked level requires blocked levels below; a scalar child needs a scalar parent | lbm/refinement_stepper.py _check_levels; lbm/hierarchy.py build_hierarchy |
| Nested transfer support | child only over inherited ACTIVE cells, donors/receivers uncovered ACTIVE, no child over the parent restriction footprint | lbm/refinement.py _nested_kind |
| Tile shape | positive (by, bx); the level allocation must be divisible by the tile (no partial tiles yet) |
lbm/refinement_stepper.py _check_levels, lbm/refinement.py FixedPatchSet2D.fine_layout (shape); lbm/blocks.py BlockLayout2D.build (divisibility) |
| Coarse boundary | periodic, slot_reservoir or channel |
lbm/refinement_stepper.py FixedPatchStepper.__init__ |
slot_reservoir |
on-site velocity bottom slot, reservoir top, velocity 0, 2 <= slot width < nx, patch rows 4..ny-4 | lbm/refinement_stepper.py FixedPatchStepper.__init__; lbm/refinement.py prepare_fine_boundary |
channel |
all levels blocked; Zou-He inlet, pressure-NEQ outlet, stationary bounce-back walls; transfers away from the closure | lbm/channel.py validate_channel_config, validate_channel_transfers |
| Explosion | constant (default); projected_linear only periodic, all-blocked SRT, no bodies/Jets, no LES (experimental) |
lbm/refinement_stepper.py FixedPatchStepper.__init__ |
L2 runtime remap (remap_child) |
periodic three-level L2 sets only, no L0/L1 bodies, identical pinned boundary | lbm/refinement_stepper.py FixedPatchStepper.remap_child |
Tests: tests/unit/test_refinement_three_level_multipatch.py, tests/validation/multigrid/test_refinement_multipatch.py,
test_refinement_three_level.py, test_refinement_three_level_multipatch_gpu.py, test_refinement_blocked_base.py,
test_refinement_blocked_fine.py, test_channel_root_gpu.py, tests/unit/test_channel_root.py,
tests/unit/test_projected_pair_stepper.py, test_runtime_child_remap_gpu.py, test_pinned_body_remap_gpu.py.
Bodies and Jets on refined levels
| Setting | Supported | Guard |
|---|---|---|
| Bodies | stationary circles without sensor/force-region roles, entirely inside the patch union, fully resolved, disjoint, stencil away from the two-hop transfer footprint | lbm/refinement.py prepare_fine_boundary |
| Level | not on L0; only on the finest level of a nested hierarchy | lbm/refinement_stepper.py _check_levels |
| Jet condition | velocity only; pressure is reserved and raises NotImplementedError |
body/jets.py Jet.__post_init__ |
| Jet geometry | each Jet selects resolved links of exactly one stationary body; no Jet/Jet or Jet/SurfaceActuation overlap; abs(amplitude) < 1 lattice | body/jets.py Jet.__post_init__, jet_link_shapes, _surface_links |
| Jets with shifted storage | only in the all-blocked stepper with SRT and no LES | body/jets.py _surface_links |
| SurfaceActuation | unshifted storage only | body/objects.py SimObject.validate_surface_actuation |
| Raw boundary exchange (force) | blocked body levels only; the window ledger requires observe_raw_exchange |
lbm/refinement_stepper.py FixedPatchStepper.__init__ |
| Checkpoint with Jets | refused | simulation.py Simulation.save_checkpoint |
Raw exchange is the cut-link momentum reaction (wall + aperture), not a validated nozzle thrust or stress load. Jet amplitude is a lattice velocity, not a flow rate.
Tests: tests/unit/test_jets.py, test_jets_review.py, test_shifted_jets.py, test_raw_exchange.py,
tests/validation/multigrid/test_refinement_jets.py, test_shifted_jets_gpu.py, test_raw_exchange_gpu.py,
test_exchange_window.py, test_fine_surface_gpu.py.
Observations from specific tested cases (not general bounds)
- SRT channel root vs uniform OneStep, strict-parity test
tests/validation/multigrid/test_channel_root_gpu.py::ChannelRootGPU::test_uniform_matched_root: in one of its 18 sub-cases (2 storage modes x 3 profile/clamp/damp settings x 3 blocks; which one is not recorded), 1 of the 4320 compared values differs, max abs 2.33e-10, max rel 1.77e-7 (about 2 ULP). The test remains failed; its cause and any general bound are unresolved. - MRT root parity test
tests/validation/multigrid/test_refinement_mrt_gpu.py::RefinementMRTGPU::test_uniform_matched_mrt_root(12 sub-cases: 2 storage modes x 2 profiles x 3 blocks, default outlet settings) passed bitwise in the Horsea focused GPU run at K17813334(2026-10-21). - Periodic Taylor-Green Re 100, measured at
36ffc8b1(V3 matched pair, N128 depth 2): every per-sample screen passed; the constant explosion's aggregate worst velocity L2, 0.004418, exceeds its preregistered 0.002 budget, while projected_linear passed that budget. - Curved-wall pressure (pilot harness, exact-q analytic annulus, tau 0.8): the coarse swirl r1 case exceeded its terminal density-gain cap (1.105e-4 vs 1e-4), so the frozen 9-case matrix is NOT_ESTABLISHED. No validated fix exists. This is a pilot harness, not production code.
- DFG 2D-1 Re 20 (V4, SRT, t* 60, pressure-NEQ outlet and local Zou-He inlet): refined and uniform runs were NOT_STEADY under the frozen steady rule, so no interval verdict was given.
Not supported in this release
3D, dynamic/automatic AMR (tagging, time-varying hierarchies beyond remap_child),
moving bodies on refined levels, pressure Jets, Jet checkpoints, LES or TRT on refined
levels, partial (non-dividing) tiles.