8.6 KiB
src coherence stage (from main 914906e)
Owner direction 2026-10-28: unified style over historical compatibility, inside src.
No numerical change is intended. Stages after 5f69eba: 65339ab (one boundary/Jet record,
host preparation helpers, pair accessors), c5a3cdb (block_collide families), 442979b
(exchange metadata on the boundary record). All three received parent/candidate
generated-config and host/GPU equality checks. Only the collision slice ran a new PTX
matrix (a4fc46d, whose src is identical to c5a3cdb); 65339ab and 442979b reused prior
PTX evidence after unchanged CUDA/compiler and generated-input checks. In the collision
matrix, only the two channel-root SRT modules changed PTX (omega load/clamp moved);
their parent/candidate outputs matched exactly in the exercised cases.
Conventions (defined once, in lbm.hierarchy.Hierarchy)
- Level
l = 0 .. depth-1(0 = root). Pairhierarchy.pairs[l-1]builds levell. - Array shapes are
(ny, nx); rectangles, origins and centers are(x, y). - Coarse/fine name the two members (parent/child) of ANY pair, not absolute L0/L1:
fine_shape,fine_origin,prepare_fine_boundary,blocked_fine_tables,blocked_coarse_tables. - Refined levels (l >= 1) of FixedPatchStepper place bodies/Jets in the root EDGE frame
(root cell
i=[i, i+1)). UniformSimulationobjects AND the static circles of a single-level (depth 1) FixedPatchStepper use the integer-NODE frame. Nothing converts between them. This concerns input placement only: within the EDGE frame, refined levels still project root-edge coordinates to fine-local cell indices with the half-cell shift (prepare_fine_boundary:(v - o)/dx - .5; hits back viafine_hits_to_edge_frameorigin + dx*(hit + .5)), usingedge_frame/fine_originbelow. Hierarchy.edge_frame(l)->((ox, oy), h): cell(i, j)of levell's allocated array (ghost band and holes included) covers[o + h*i, o + h*(i+1)). A scalar FixedPatch2D window carries the half-coarse-cell ghost shiftbounds - HALO/2infine_origin; a set's level-wide grid has origin 0. Level 1 boundaries are built from the root config (the wall-aware fields live there); deeper ones from the parent frame.FixedPatch2D.fine_level_offset(wasfine_level_origin) is an array INDEX offset(y, x), not an(x, y)origin.
FixedPatchStepper migration map
Historical: left column = names at or after 914906e that no longer exist; right column =
the current name.
| removed / changed | replacement |
|---|---|
patch, child_patch |
hierarchy.pairs[0], hierarchy.pairs[1] |
base_layout, fine_layout, child_layout (stepper properties) |
layouts[0], layouts[1], layouts[2] (FixedPatchSet2D.fine_layout(block) is unchanged) |
fine_boundary, child_boundary |
boundaries[1], boundaries[2] (host geometry; None = no bodies/Jets) |
_jets |
jets[1] |
_boundaries[l] (dict from L1, level keys), _level_boundaries (b4eacf7), _device_boundaries (65339ab) |
_boundaries[l]: the one combined per-level _BlockedBoundary record (list from L0, None at L0 unless single-level static circles): host geometry .host, .tables, .jets, .jet_cache, exchange .spacing/.centers, device handles |
_fine_tables, _actuated, _jet_cache |
_boundaries[1].tables / .actuated / .jet_cache |
_level_jets (private) |
_boundaries[l].jets (public jets[l] is derived) |
_exchange_spacing[l], _exchange_centers[l] (private) |
_boundaries[l].spacing (h_l, root units) / .centers (root edge frame); only levels with a boundary report exchange |
_level_blocks (private) |
_layouts[l].block |
_remap_cfg (private) |
_root_cfg |
refinement_tables.blocked_base_tables (also via refinement_stepper) |
refinement_tables.blocked_coarse_tables (import from refinement_tables; the stepper no longer re-exports) |
_fine_leaf, _child_leaf |
leaf[1], leaf[2] (copies) |
_middle_kind |
hierarchy.kinds[1] |
_boundary_hosts (private) |
public boundaries[l] = _boundaries[l].host (host geometry only; the private record is the combined one) |
remap_child(rects, child_objects=...) |
remap_finest(rects, objects=...) |
set_jet_amplitude(i, a) (implicit level 1) |
set_jet_amplitude(i, a, level=l) (required) |
read_raw_exchange(b) / read_exchange_window(b) (implicit level 1) |
read_raw_exchange(b, level=l) / read_exchange_window(b, level=l) (required) |
_level_name(l) "base/fine/child" messages |
"level l" |
refinement.MIN_PATCH_GAP |
deleted (no gap policy exists) |
params.cuh HaloPlan, collide_trt_no_force |
deleted (zero references) |
Kept: step_count, constructor keywords (refinements, level_blocks, level_objects,
level_jets, coarse_boundary, explosion), Simulation.set_jet_amplitude(i, a) (one level),
RemapTopology.from_config(..., child_objects=, child_jets=) (internal remap helper; "child" =
the replaced level relative to its parent).
Responsibilities
- Construction:
_check_levels(inputs); host preparation, numpy only, before any device work:_level_layouts,_kernel_configs(per-level kernel configs),_host_boundaries,_prepare_blocked_boundary,_fluid_leaves;Hierarchy.edge_frame(geometry frames); then_planned_bytes()(memory preflight) and_build_level/_build_pair/_upload_boundary(device). A construction failure closes the partial instance. Observation reads share_observed_boundary+_summarize. - Pair accessors:
rectangles(a scalar FixedPatch2D gives(bounds,)) andfine_level_offset((0, 0)for a set) on both pair types; the types and their storage frames stay distinct. - Per-level state, by lifecycle:
- Fixed plan structure (built once in
__init__, read by_planned_bytes()before any allocation, readable afterclose()through the public views):_layouts,_level_cfgs,_leaf,_state, and in each_boundaries[l]record the host geometry.host, link tables,.jet_cache,.spacing,.centers. - Mutable boundary data in the same record:
.jetsand.tables["surface_velocity"], changed only byset_jet_amplitude(host prepare -> device write -> commit; an uncertain write fails the instance); device handles (.flags,.device,.kernel, ledgers) are set by_upload_boundaryand cleared byclose(). - Runtime:
_levels(_Level) holds compiled modules, kernels, the two population buffers (swapped per step) and transfer/kind handles. It is created only after the memory preflight and cleared onclose();.layout/.boundaryreference the plan for_hop. Folding the plan into_Levelwould mix both lifecycles in one record.
- Fixed plan structure (built once in
- Blocked kernels:
operators/block_collision.cuh block_collideis the one collision extension point ofBlockStepandBlockChannelStep. It computes rho/u once and reads no runtime force (d_params.fx/fy). Two compile-time families: uniform-equivalent (MRT on any level, SRT on the channel rootBLOCK_ROOT_BC == 2): clamped omega,collide_mrt_no_forceorcollide_srtwith its zero Guo term; and level SRT (BLOCK_ROOT_BC0/1): unclamped omega,collide_srt_no_force, with the shifted direct-momentum feq underUSE_DDF_SHIFTING. The uniform path keepscollide_dispatch(runtime force, LES, TRT). explosion="projected_linear"stays an optional capability;"constant"stays default.
Not done / open
- Two per-level config derivations stay distinct on purpose:
Hierarchy.configs(_middle_config, wall fields reset on every refined level, geometry validation) vs.FixedPatchStepper._kernel_configs(reset only on level 1 of a slot_reservoir root; generates each level's config.h). Folding them needs a config.h/PTX diff gate. - No single-
Simulationfacade over FixedPatchStepper: the two own different state models (uniform node frame, body sync, forcing, checkpoint vs. fixed blocked hierarchy, edge frame, remap-as-new-instance). A facade would only forward calls; no concrete need found. - Deferred, each needing its own evidence first: sharing the channel-root cell/donor selector
with OneStep (the closure functions are already shared; a shared selector changes uniform
signatures and needs a uniform PTX gate); merging FixedPatch2D into FixedPatchSet2D
(different origin, ghost band, layout size and addressing; needs a geometry/parity table and
would need tiles that are not grid-aligned); one
clamp_omegahelper for uniform and blocked paths (it lives in the hash-pinned LES headerturbulence_smag.cuh). - Tests, examples and validation callers are migrated separately (pinned runners stay frozen).