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>
3.7 KiB
3.7 KiB
Device state lifecycle (uniform Simulation, FixedPatchStepper)
State is grouped by what a change invalidates. This file describes the code as
it is; it adds no framework. Line references are to candidate 0f8fdbc3.
| Class | Contents | How it changes | Owner rule |
|---|---|---|---|
| 1. Module and its globals | Compile-time (visible to the compiler, fixed per module): config.h macros incl. NX/NY, COLLISION_MODEL, BLOCK_ROOT_BC, WALL_*. Runtime per-module globals (not visible to the compiler; written by host code): __constant__ d_params, d_w, d_cx, d_cy, d_block_channel, d_block_wall; plus bound kernel handles |
Globals are written once by the owner right after load_module. Uniform grid: Simulation._recompile (construction, body-topology sync) loads a new module and re-uploads; update_runtime_params rewrites d_params (omega, force, n_objects) between runs. Steppers: never rewritten after construction, so they are immutable for the instance's lifetime although they are runtime values. The blocked slot/reservoir root reads its slot from the d_block_wall constant (the former dense route used WALL_* macros; both were bit-identical in the parity gates). |
Every owner loads its own module (compiler.load_module = module_from_file, no cache). A loaded module is never shared between live owners: globals are per module, so sharing would share mutable d_params. |
| 2. Geometry, layout, tables | flags, curved-link tables, block layouts and halo pairs, transfer (explode/coalesce) tables, block kinds | Immutable for the life of an instance. Uniform grid: rebuilt by body sync. FixedPatchStepper: a geometry change is a new instance (remap_child), committed only after a successful transfer. |
No in-place table edits. |
| 3. Controls and dependent boundary columns | Jet amplitude -> per-link surface_velocity; body actions (uniform grid) |
Between steps, without a table or module rebuild (set_jet_amplitude). |
Input (index, level, amplitude) is validated before any device write; invalid input changes nothing and the instance stays usable. The host copy commits only after the upload returned. Any error after validation leaves the device column undefined and fails the instance: FixedPatchStepper status failed, Simulation._failed; every later device-touching public method (Simulation: stream, set_jet_amplitude, initialize, sync_bodies, run/step, read_surface_exchange, read_body(s), get_macroscopic, get_ddf, update_runtime_params, snapshot, restore, save/load checkpoint) raises; cached host readers and host-side staging (e.g. get_flags, add_body/remove_body before a sync) do not touch the device and stay callable. close() still releases resources and stays retryable. |
| 4. Per-step state | population double buffers and their parity, raw-exchange ledgers, observations | Device-only during step(n) / run(n); buffer swaps are host-handle swaps. |
Valid on the host only after the single wait at the end of the call. |
Storage routes are separate concerns:
- Original uniform grid (
uniform.cu,Simulation): whole-domainNX/NY. - Blocked levels (
blocked.cu; everyFixedPatchStepperlevel, including the single level ofrefinements=[]):NX/NYare one block window including halo. - Dense refinement levels (
refinement.cu) were removed:level_blocksis required and every refinement level is blocked.
A single-rectangle refinement (FixedPatch2D) is stored in its own fine
window frame (FixedPatchStepper.__init__,
BlockLayout2D.build(*pair.fine_shape, ...)). Only a rectangle set
(FixedPatchSet2D) uses the level-wide frame (2*ny, 2*nx), whose
fine_layout requires that full grid to be divisible by the tile
(refinement.py:240-252).