3.9 KiB
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