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>
Both public examples move off the dense refinement route, inside the scope
proven by the dense/blocked parity gate (1573942: two-level unshifted SRT,
periodic or slot/reservoir root, one circle/actuation or body-free):
- fixed_patch_jet: LEVEL_BLOCKS = [(12, 8), (8, 8)] (48x48 root, 56x40
fine window), recorded in its report config;
- fixed_patch_circle: --base-block (default 12 8) and --fine-block
(default 16 20); the dense 'omit --fine-block' option is gone.
Host test test_example_tiles: each example's tiles (read from the example
source without importing it) build every layout and table up to the first
CUDA call, for the circle in both coarse boundary modes.
No src change.
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>
Fault injection with the existing host fakes for FixedPatchStepper(
refinements=[]): an initialize upload failure, a BlockHaloCopy,
BlockCurvedBoundary or BlockStep launch failure, and a failing final
stream wait each raise, leave the instance failed (step/download refused)
and keep the completed step count; a failed launch never reaches the wait.
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>
36ad523 made a manifest file missing from the checkout a 'Source mismatch',
which also broke the tests' manifest-selection helper: it reused
verify_source with hashing neutralised but still read the (renamed) files.
run_refined_dfg.source_manifest(collision, observation_mode) now owns the
frozen-manifest selection; verify_source uses it, then verifies the
checkout. The test helper calls source_manifest directly; source identity
stays owned by test_source_pin. Same errors, manifests and pins.
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>
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>
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>
K4 made BlockExplodeProjected address storage through block_ddf and
block_slot (core/layout.cuh). This host test compiles the kernel text
extracted from blocks.cu, so it now also extracts those two helper
definitions from core/layout.cuh (device qualifiers -> inline) instead of
failing to compile. Assertions and tolerances unchanged.
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>
Manifests and receipts unchanged. Each of the 8 group-A identity tests now
extracts its manifest's recorded commit and verifies hashes there instead of
against the current checkout. Tamper assertions and runner source refusals are
kept; missing recorded history is an explicit failure, not a skip or repin.
Test-side only; no production, manifest or pin change. Group A identity
checks (test_source_pin and the jets.py pins) are unchanged and stay
failing as recorded.
B test_refined_dfg: receipt/plan fixtures load the frozen manifest JSON
that verify_source returns for a matching tree (frozen_manifest), and
the two manifest-selection assertions call verify_source with only its
working-tree hash comparison neutralised (selected_manifest), so the
malformed/collision/observation-mode/checkout receipt checks run again.
test_source_pin remains the single identity check.
C test_channel_root: int(owner)-1 (uint64-int became float on NumPy 1);
test_shifted_storage: FP32 NaN fill so the arrays stay FP32. Both reach
the intended immutable/representation/FP32/density checks.
D test_block_stepper fake gets _ddf_shifting; test_fine_surface_gpu sets
_boundary_hosts/_level_jets instead of the read-only fine_boundary view;
test_refinement_followup fake_backend sets _layouts/_ddf_shifting/
_level_cfgs instead of the read-only fine_layout view (as reviewed in
32da2d73).
E test_kernel_contracts_reject_before_upload: an inconsistent pair tau is
now first rejected by the hierarchy chain check; the test asserts that
rejection, then bypasses _check_chain to prove the per-level omega/tau
guard still rejects, and asserts no mem_alloc/memcpy_htod in either case.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
test_shifted_jets, test_shifted_storage, test_raw_exchange and
test_channel_root pass refinements/level_* lists; examples
fixed_patch_jet and fixed_periodic_patch pass refinements=[BOUNDS]
instead of the positional bounds. Assertions kept; the shifted-storage
case base_block-without-fine_block now expects the level-list rule
'blocked refined levels below' (same constraint).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
KW (legacy base/fine/child blocks + child_rectangles_in_parent) becomes
levels(): refinements=[PARENTS, CHILDREN], level_blocks, level_objects,
level_jets. Every negative case is kept and expressed per level (child
rectangles, per-level blocks); the L1-body case now expects the level-list
rule's message 'only on the finest level' instead of the legacy keyword
message 'only on L2' (same constraint).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The module-level import of CelerisLab.lbm.block_stepper loaded
pycuda.driver into the shared test process, unlike the other CPU host
tests (which load mocked copies via AST). find_spec gives the source path
without the import. Test content and assertions unchanged.
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, 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>
Supersedes 71221837. Samples every t* 0.5 (interval-mean forces), trailing
t* 50..60 window (21 samples); range <= 10 % and |slope*window| <= 2 % of
each official band width. Verdict on the window mean with the distance to
the nearest band edge. Our engineering rule, frozen before any run.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 869d60d78e)
Samples every 0.5 s at synchronized root steps; trailing 5 s window (11
samples); range and |slope*window| each at most 1 % of the official band
width. States the fluid-on-cylinder force sign, reports cd_positive,
defaults to the refined case and uses the review's label.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 71221837f9)
Reuses the pinned Re100 runner and helpers unchanged; sets only the
lattice viscosity for Re 20. Reports Cd, Cl and p_diff (dp * 0.04 for
U = 0.2 m/s), a steady rule frozen before any run (window ranges at most
a tenth of each official interval width) and no interval verdict when
not steady. Declares the pressure-NEQ outlet and Zou-He inlet deviations.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 9b8ff90609)
A (per-base read-and-sum per 80-step interval), W (window + step(80) +
one read) and W1 (window + 80 x step(1)) over equal simulated duration
from the same fresh state and warm-up, 3 interleaved repeats. Bitwise
populations and per-interval total impulses (documented engineering
tolerance) are compared outside timing; health checks untimed. Records
candidate source identity (revision, dirty flag, file hashes) and does not
use or modify frozen refined-DFG manifests/results.
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>
Simulation.get_ddf() is flat (9*ny*nx); view it as (Q, cells) before
applying the fluid mask, with a flat-DDF regression. Label A/E as
runner-like force-read workloads (values discarded, no aggregation or
validation; no macro/probe/CSV cost; not benchmark throughput) and split
status lines into CONSTRUCTED and WARM_READY. Test/doc only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Untimed physical-leaf/fluid health after warm-up and after each timed
window using the existing runner limits (finite macros, rho in [0.5,1.5],
max Mach <= 0.2); refined masks as in run_refined_dfg. Parity comparator
rejects unequal level counts and any infinity; matching NaN stays allowed
only as a storage mask. Regression tests for both. No numerical gate or
production change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Explicit stream synchronization before/after each timed loop (refined
FixedPatchStepper stream, uniform Simulation stream, in their contexts);
required --max-wall-seconds outer timeout checked between variants and in
per-step loops; untimed finite leaf/fluid health checks; force notes for
B/C/E' (no host reads; final base interval only; whole-window average with
different summation order). Parity stays A/B/C vs A and E/E' vs E only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>