Files
CelerisLab/docs/synchronized_leaf_remap.md
T

3.9 KiB
Raw Blame History

Synchronized leaf G remap (CPU only)

This is a CPU state-transfer component, not a runtime adaptive solver. It changes only a separated L2 rectangle set in a three-level, body-free, periodic hierarchy. L0/L1 topology, relaxation parameters, direction order, physical frame and completed time remain unchanged. Existing geometry checks validate child support/nesting and separation. No CUDA, scheduler, allocator, boundary handling, collision, ghost refill or runtime installation is added.

from CelerisLab.lbm.refinement import FixedPatchSet2D
from CelerisLab.lbm.synchronized_remap import RemapTopology, remap_synchronized

# cfg: D2Q9/SRT/FP32/unshifted/double_buffer, nz=1, no LES
parent = FixedPatchSet2D.from_config(cfg, [(4, 4, 20, 28), (24, 4, 44, 28)])
old = RemapTopology.from_config(cfg, parent, [(16, 16, 32, 40)])
new = RemapTopology.from_config(cfg, parent, [(20, 16, 36, 40)])
candidate = remap_synchronized(
    old, new, current_g, base_count=7, level_steps=(7, 14, 28),
    phase="postcollision_g", origin=(0., 0.), spacing=1.,
)

The example has a 48×32 root; both child supports allow 4×4 blocks. Child bounds use the parent's allocated L1 coordinates, not rectangle-local indices. A scalar FixedPatch2D parent is also supported: its shifted L1 window origin is retained. Physical lower edges are origin + spacing*(x,y) on L0, origin + spacing*(parent.fine_origin + (x,y)/2) on L1 and origin + spacing*(parent.fine_origin + (x,y)/4) on L2.

Factory options coarse_boundary and objects must declare "periodic" and an empty body collection (all levels). LBMConfig's channel settings do not select this operator's boundary mode. Geometry metadata is factory-created; manual mutation/forging of metadata is unsupported. Caller certifies the actual state has no bodies/forcing, that all device work and old readers are quiescent, and that inputs are the current owned-leaf postcollision G. Count/phase stamps check consistency, not freshness or active-buffer identity.

current_g is three NumPy Q-major arrays (9,ny,nx). For blocked inputs, pass layouts=(base_layout, fine_layout, child_layout); each optional None entry denotes dense input. Every leaf must have an owner. Only owner cores are read, never redundant block halos. Mixed precision is rejected. FP64 is accepted for CPU reference tests; this does not enable FP64 runtime storage. Outputs candidate.g are separate dense arrays regardless of input layout. Memory scales with public grid extents, not only sparse allocated blocks.

Same-level leaf overlap is copied bitwise in physical coordinates. Newly refined L2 copies all nine values from its valid old L1 parent. Newly exposed L1 receives the complete four-child mean, including q0. Covered parents, ghosts and holes never serve as donors; NaN/Inf outside leaves is ignored, while nonfinite required leaves or nonpositive density reject the operation. Restriction uses FP64 quarter-weight accumulation and rounds to the input dtype. New nonleaves are NaN. Source arrays/metadata are not changed.

candidate retains base_count, level_steps, phase, origin, spacing and new topology.leaf_masks. Existing FixedPatchStepper.initialize resets the counter: using it is not a continuation adapter. This component neither publishes a candidate nor retires old state. Runtime table rebuild/halo refill and correct first-step scheduling remain separate integration work.

Tests accumulate leaf-area integrals in FP64 for each population, mass and momentum. The declared absolute tolerance is 2e-13 * sum(abs(old population integrals)) for FP64, 2e-7 * scale for FP32, accounting for reduction and restriction rounding (not a solver accuracy bound). Coarse→fine→coarse copy round-trips preserve state; coarsening arbitrary fine structure loses it and is not invertible. No kinetic accuracy or stability claim follows.

PYTHONPATH=src python -m unittest discover -s tests/unit -p 'test_synchronized_remap.py' -v