SRT keeps its two explicit arithmetic variants (level: unclamped no-force,
shifted direct-momentum feq; channel: uniform OneStep arithmetic), selected
at compile time by BLOCK_ROOT_BC; text moved verbatim, PTX parity pending.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A set_jet_amplitude failure while creating the internal stream marked the
Simulation failed, but the public stream property still retried
cuda.Stream(). Guard it like the other device-touching methods and add a
regression test (no retry, close still retryable). Lifecycle wording now
names the guarded device-touching methods; cached readers and host staging
stay callable. compatibility.md records the caller-visible fail-closed change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Simulation.set_jet_amplitude validated before upload and committed host
state after it, but after an uncertain device write nothing stopped reuse.
Record the failure in Simulation._failed and refuse every public device
call except close() (FixedPatchStepper already uses status 'failed').
Invalid input stays side-effect free and retryable. Adds host fault tests
for both owners and states the rule in docs/lifecycle.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deleted: kernels/refinement.cu and step/patch_step.cu (PatchExplode,
PatchFineStep, PatchCoarseStep, the CELERIS_REFINEMENT_AUDIT counters);
in FixedPatchStepper the dense branches of _build_level/_build_pair/_hop,
_apply_fine_boundary, the fine-level CurvedLinkSoA wiring (_fine_curved,
_fine_flags, _curved, _link_tables, its set_jet_amplitude/initialize/close
paths) and the now-vacuous 'requires all-blocked' guards (shifted,
projected, channel, raw exchange, several L1 rectangles, depth >= 3).
level_blocks is required: one tile (by, bx) of positive integers per level,
else ValueError before any device work naming the whole-level tile
((ny, nx) at the root, the fine window shape for one rectangle) as always
valid. No automatic tile choice. Removed route: None in level_blocks
(dense or mixed dense/blocked storage); parity of D/D, D/B and B/B is
recorded at 1573942 and 2469af7.
Kept: the uniform-grid solver (uniform.cu, Simulation, CurvedLinkSoA,
CurvedBoundaryKernel), FixedPatch2D, pinned historical runners.
Docs: compiler entries, BlockExplode comment, lifecycle, supported_settings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Restores the PeriodicBlockStepper safeguard dropped in 8e1feff, for all
FixedPatchStepper levels: both G buffers, halo pairs and boundary columns
of every level are compared with cuda.mem_get_info() before the stream,
any module or any buffer exists, raising MemoryError('level buffers exceed
free device memory'). Transfer tables are not counted (lower bound).
Tests: the single-level MemoryError case is back beside the param-layout
check (no stream, no allocation); host test_single_level covers depth 1
and depth 2 (no stream, build or allocation) and the wait failure after a
good step (count stays 1). Driver-mocking fixtures report ample free memory.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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 at 1318fc2 (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 the 1318fc2 record).
- 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>
Review follow-ups for S1 (f764d68):
- a single level starts its spare buffer NaN, as PeriodicBlockStepper does,
so stale halos cannot hide (multilevel initialization unchanged);
- projected explosion is rejected for a single level;
- new tests/unit/test_single_level.py: NaN spare after initialize, per-step
launch order halo -> BFL (circles only) -> BlockStep with a null kind and
one wait per step(n), circle solids excluded from the state mask;
- the GPU bitwise comparison also covers shifted storage.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A zero-refinement FixedPatchStepper is the PeriodicBlockStepper capability:
one doubly periodic blocked SRT level (no LES, Jets or exchange
observation) whose level_objects[0] are stationary passive circles. The
circle tables come from the same checks, now blocks.static_circle_tables
(moved from block_stepper, pycuda-free); BFL, halo and BlockStep launches
are the shared helpers, with an all-ACTIVE (null) kind. Base-level bodies
stay rejected for depth >= 2. PeriodicBlockStepper and its callers are
unchanged (deleted only after S1's GPU result).
Tests: host contract rejections before CUDA for the single level; GPU
bitwise comparison against PeriodicBlockStepper for every split, with and
without the circle (the S2 gate).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The package already exposed it as CelerisLab.cuda.compiler; the module now
has that name and every tip caller, doc and current-tree test imports it
directly (no alias).
Historical-revision tooling is not edited: the Taylor-Green harness
(run_case.py, hash-pinned check_projected_pairs.py, assess receipts) only
runs against a135252, whose package has cuda/compiler_v2.py, and the J
campaign runners and dated docs describe past trees. Its two current-tree
tests now import that package from git (historical_package) instead of the
tip, and the kernel-root test first checks the accepted case.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PeriodicBlockStepper and FixedPatchStepper each built the blocked level's
kernel config (block window NX/NY, neutralized wall/slot/inlet fields,
omega/tau check) and each spelled out the raw BlockHaloCopy and
BlockCurvedBoundary argument lists. These now live once in lbm/blocks.py
(level_config, launch_halo_copy, launch_curved_boundary), so a kernel ABI
change has one host site. Both owners keep their own lifecycle; no new
class, no behaviour or default change. PeriodicBlockStepper now checks its
level config before any device work (same errors). Dense refinement levels
keep their own config check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kernel_v2.cu -> uniform.cu (whole-domain NX/NY, Simulation) and
blocks.cu -> blocked.cu (one block window, every blocked level), with
InitTubeFlow_v2 -> InitTubeFlow. All live callers, include comments,
CONFIG.md, lifecycle doc and current-tree tests move together; no alias.
refinement.cu keeps its name until the dense-route decision.
Historical-revision tooling that names the old files (LES source pins,
timing/J campaign runners, TG run_case, supported_settings citations at
888c8038, development notes) is unchanged on purpose.
No arithmetic change; the PTX symbol InitTubeFlow_v2 is renamed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
generate_config/compile_kernel/load_module also recorded the last build
directory and PTX in ContextVars so that bare compile_kernel() and
load_module() calls worked: a second, implicit interface beside the
explicit build_dir/ptx_path one used by every owner. compile_kernel now
requires build_dir= and load_module requires the PTX path.
Build contract tests use explicit paths; they still check per-build
header/output isolation, interleaved generation, path escapes, refusal of a
removed snapshot, and that a failed compile keeps the previous PTX and
leaves no partial output. No kernel, numerical or launch change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Simulation, PeriodicBlockStepper and FixedPatchStepper levels each repeated
generate_config -> compile_kernel -> load_module -> cleanup and the
d_params pack/upload. They now call compiler.build_module(cfg, entry,
arch=..., n_objects=..., block_root_bc=...) and compiler.set_params.
Simulation no longer keeps its build directory alive until close (nothing
read it after loading). Removes the unused compile_kernel_v2 alias.
Host fakes and lifecycle tests follow the new seam; the close-retry case
for build-directory cleanup goes with the removed cleanup step.
No kernel, numerical or launch change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The views and the PeriodicPatchStepper alias stay for the existing tests
and docs (about 130 references); the comment no longer says REMOVE AFTER.
No code change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
refinement.cu keeps its header, includes, the curved_double.cu entry
block, the #error guard and the optional CELERIS_REFINEMENT_AUDIT
counters, then includes step/patch_step.cu, which holds patch_collide,
PatchExplode, PatchFineStep and PatchCoarseStep moved verbatim (checked
against the previous refinement.cu) in the same order.
test_refinement_mrt reads refinement.cu + step/patch_step.cu; its
assertions are unchanged. The audit-build tests include refinement.cu
from the build root, so step/patch_step.cu still resolves.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
blocks.cu keeps its header, includes and #error guards and then includes
the kernels, as kernel_v2.cu does:
- step/block_transfer.cu: BlockHaloCopy, BlockExplode,
BlockExplodeProjected, BlockCoalesce
- step/block_boundary.cu: BlockCurvedBoundary, BlockAccumulateExchange
- step/block_step.cu: BlockStep (rejects BLOCK_ROOT_BC == 2)
- step/block_channel.cu: unchanged (mode 2 only)
Kernel text moved verbatim (checked against the previous blocks.cu) and in
the same definition order, so the translation unit is the same sequence of
kernels. The two source-reading tests follow the move:
test_projected_explosion reads step/block_transfer.cu, test_refinement_mrt
reads blocks.cu + step/block_step.cu; their assertions are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Horsea's gates showed block_ddf/block_slot change real PTX in
BlockHaloCopy, BlockExplode and BlockExplodeProjected (6/10 modules;
BlockExplodeProjected 38 -> 40 registers) although the results stayed
exact. The stack's bar is byte-identical PTX for naming-only steps, so
those three kernels return to their K3 expressions, the helpers leave
core/layout.cuh, and test_projected_explosion goes back to its K3 form.
fill_invalid stays (its kernels were byte-identical). K4 vs K3 is now
only the NaN-fill helper.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
operators/helpers.cuh already had bounce_back_swap (the uniform steps use
it). It moves verbatim, #pragma unroll included, into its own
operators/bounce_back_swap.cuh, which helpers.cuh includes; the channel
root includes that header and calls it. bounce_back_swap_d2q9 from the
previous commit is removed. bounce_back.cuh keeps only the half-way rows
helper.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- boundary/bounce_back.cuh: apply_wall_bb_d2q9_rows(y, top_row, ...) is
the existing half-way y-wall correction with an explicit top row;
apply_wall_bb_d2q9_y_pull keeps its signature and calls it with NY - 2.
bounce_back_swap_d2q9(f) names the full opposite-pair swap.
- streaming/pull_double_buffer.cuh: load_node(fi, n, f) loads one node's
stored populations.
- step/block_channel.cu uses all three instead of its inline copies: the
wall swap, the outlet donor gather, and rows 1 / ny-2 (now one call with
the physical top row; same y == 1 / y == top_row order and loads).
Same statements, branch order, indices and arithmetic.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- core/layout.cuh: block_ddf(f, block_cell[, cells]) returns the start of a
block's populations and block_slot(block_cell, cells, q) the offset of one
population; BlockHaloCopy, BlockExplode and BlockExplodeProjected use them
instead of repeating (c / cells) * NQ * cells [+ q * cells + c % cells].
BlockExplode keeps its loop-hoisted parent base; types stay uint64_t /
unsigned long; the expressions are the same ones, now named.
- core/cell_kind.cuh: fill_invalid(f) writes the NaN marker; BlockStep,
BlockChannelStep and PatchFineStep call it instead of three copies of the
loop.
No change to operation order, types, launches or arithmetic.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- core/cell_kind.cuh holds KIND_ACTIVE/INNER/OUTER (was a constexpr in
blocks.cu that step/block_channel.cu and the channel_root.cpp shim
relied on or redefined); blocks.cu, block_channel.cu and the shim now
include it.
- blocks.cu checks the lattice/model #error guard before the
BLOCK_ROOT_BC guard and mode includes, so an unsupported build reports
the real reason first.
- operators/block_collision.cuh includes what it uses (params, macro,
SRT/MRT collision) instead of relying on blocks.cu's include order.
Preprocessor-only: same kernels, same statements, same arithmetic.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
surface_link_velocity already runs jet_link_shapes; CurvedLinkSoA.
assign_surface and _prepare_blocked_boundary then ran it again with the
same jets and hits to fill the host jet cache. A private _surface_links
returns (velocity, aperture, shape, jet_id) from the one call; both
callers use it. surface_link_velocity keeps its two-value public
contract (now a thin wrapper). Same values, same validation order, same
cache contents; host geometry preparation only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop legacy constructor prose (child=/mutual exclusion), retarget
README and fixed_three_level_multipatch examples to refinements /
level_blocks, and fix stepper error/doc strings that still named
fine_block/base_block or a fixed gap >= 4 (unions use dependency
closure). Wording only; no behavior change.
Removes the legacy entry points now that every caller is migrated:
FixedPatchStepper(cfg, *, context, refinements, coarse_boundary,
level_blocks, level_objects, level_jets, observe_*, explosion) and
initialize(*level_g). Deleted: positional bounds, fine_block, base_block,
fine_objects, jets, child, child_block, child_objects, child_jets,
child_rectangles_in_parent, initialize(coarse_g/fine_g/child_g),
_legacy_levels and _initial_levels. Docstring, README and docs describe
the level lists. Compatibility views (patch, fine_boundary, ...) and the
PeriodicPatchStepper alias are unchanged (separate follow-up).
Error -> rule table (legacy keyword check -> level-list rule now firing):
- several patches without fine_block/base_block -> _check_levels
'several patches require fine_block and base_block' (unchanged rule)
- base_block without fine_block -> 'a blocked level requires blocked
refined levels below it'
- nested child without all blocks -> 'three or more levels must all be
blocked' / 'a blocked level requires ...'
- fine_objects/jets with a nested child ('only on L2') -> 'bodies/Jets are
supported only on the finest level of a nested hierarchy'
- child_objects/child_jets without a nested child -> 'level_objects/
level_jets needs one entry per level'
- scalar child with several L1 patches ('scalar child requires one parent
patch') -> hierarchy 'a scalar child requires a scalar parent'
- child vs child_rectangles_in_parent mutually exclusive -> no level-list
equivalent (cannot be expressed); covered by the new explicit
RemovedLegacyAPI test: removed keywords and positional bounds raise
TypeError before any context use, initialize has only *level_g.
The InitialLevels test of the removed adapter is replaced by that test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The only in-library caller of the legacy constructor keywords now passes
refinements=[L1 bounds, L2 set], level_blocks=[base, fine, child],
level_objects=[(), (), child_objects], level_jets=[(), (), jets] -- the
exact lists _legacy_levels produced for it. Legacy keywords stay until
every caller is migrated (final P1 commit).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review follow-up (non-blocking items): a launch failure on the second of
three steps fails closed without the batch wait, keeps step_count at its
entry value and blocks download; the docstring now states, as for
FixedPatchStepper, that async faults surface at the single wait.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Same change as the reviewed FixedPatchStepper end-sync (cherry-picked
ad1d82c+512dcc9 above): the n steps are enqueued in order and the host
waits once; step(0) launches nothing; step_count advances after the wait
and keeps its entry value on failure. Launches, buffer roles and bits are
those of n step(1) calls. Mocked-driver host test covers the single
wait, launch/role equality with n step(1), step(0) and failure.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Enqueue all n base intervals in order on the one stream and synchronize
once before returning (step(0): no launch, no wait). step_count, the raw
exchange ledger and an open window are published only after that wait;
any failure leaves step_count at its entry value and invalidates both
ledgers. Async device errors are no longer attributed to an interval;
the docstring says to use step(1) loops to localize one. Launch order and
buffers are unchanged. Adds a mocked-driver host test of the contract.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
BlockStep (slot-reservoir wall rows) and BlockChannelStep computed the
global cell of a block-window cell with the same four lines.
block_global_xy (core/layout.cuh, 2D, __forceinline__) now holds them;
both kernels call it with their own domain width. Same integer
operations: no numerical, ABI or scheduling change. The slot-reservoir
kernel layout is unchanged (no split).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
BlockStep, BlockChannelStep and refinement.cu patch_collide each carried
the same compute_rho_u -> omega clamp -> collide_mrt_no_force sequence.
collide_mrt_unforced (collision_mrt.cuh, __forceinline__) now holds it
once; the three sites call it. Same operations in the same order: no
numerical, ABI or SRT change. The host source contract now requires one
helper definition and exactly one call per file, and forbids the old
inline sequence.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FixedPatchStepper(observe_window_exchange=True), which requires
observe_raw_exchange, allocates a second per-link FP64 ledger per body
level. While a window is open, the unchanged BlockAccumulateExchange
kernel also accumulates into it after each fine hop. Small API:
start_exchange_window() zeroes and opens at the current completed step;
read_exchange_window(body_id, level=) is non-destructive and reports
impulse with rates over the integer window duration. initialize/
_initialize_raw and step failure invalidate; remap builds a new instance
with no open window; Jet amplitude updates may stay in-window. Default
disabled: no allocation and no extra launch; last_substep/last_base_step
unchanged. summarize_exchange gains window='window' with duration.
Host contract tests (mocked driver) and a GPU gate against summed
per-base reads with a reordering-bound FP64 tolerance.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Additive compile-time COLLISION_MODEL == 2 branches in BlockStep,
BlockChannelStep and patch_collide repeat the uniform collide_dispatch
MRT sequence (zero forcing, omega clamp, collide_mrt_no_force). SRT
statements, including the shifted retained-delta/direct-momentum path,
are unchanged under the alternate branch. Guards accept SRT or MRT only;
TRT, LES, non-FP32, non-double-buffer and shifted dense/mixed refinement
stay rejected. Transfers, subcycling, BFL, raw ledgers, channel closures,
shifted-Jet SRT guard and PeriodicBlockStepper are untouched. Existing
MRT operator, shifted representation and fixed kinetic rates are reused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Jet gains a last field condition='velocity'. 'pressure' raises
NotImplementedError in __post_init__ before profile/amplitude validation, so a
pressure jet never reaches surface_link_velocity, tables or kernels; unknown
values raise ValueError. Amplitude stays a lattice velocity (not a flow rate);
no pressure units, field reuse, tables or kernel changes are introduced.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit fbf6f1d122)