# Synchronized leaf G remap and manual continuation 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. The CPU helper itself adds no CUDA, scheduler, allocator, boundary handling, collision or ghost refill. A bounded runtime adapter is described below. ```python 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 are handled only by the explicit adapter below. 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. ```sh PYTHONPATH=src python -m unittest discover -s tests/unit -p 'test_synchronized_remap.py' -v ``` ## Explicit runtime continuation `replacement = current.remap_child(rectangles)` returns a new ready `FixedPatchStepper` at the **same absolute step_count**. It supports only body-free periodic three-level runs whose existing L2 uses `child_rectangles_in_parent`. Scalar or union L1 parents are supported; scalar `child=` is deliberately rejected. L0/L1, relaxation and block sizes stay fixed. No tagger, scheduler, in-place replacement, or general rollback is provided. The method holds the stepper lock, validates geometry, synchronizes its stream, and downloads the actual current owned leaves. It certifies postcollision G and completed 1:2:4 counts internally, applies the CPU helper, rebuilds all geometry/transfer/kind/halo buffers, and uploads both current and spare buffers. No old halo is reused. The regular step schedule fills transit ghosts/halos before use. `initialize` itself still resets the count to zero. The old stepper remains ready, unchanged and independently usable. The caller owns **both** objects and must close both before closing their shared CUDA context. Candidate construction or upload failure releases only candidate resources; an actual old-device synchronization/read failure marks the old stepper failed, consistent with its existing failure semantics. ```python replacement = current.remap_child([(20, 16, 36, 40)]) # Explicitly choose the returned continuation only after success. current.close() current = replacement current.step() # advances the preserved absolute count by one ``` CPU lifecycle mocks cover fresh current-buffer reads, exact transfer, separate ownership, time preservation, ordinary initialization reset, unsupported states and geometry, and candidate/source failure cleanup. GPU tests are predeclared in `tests/validation/multigrid/test_runtime_child_remap_gpu.py`: identity next step bitwise, shifted A→B→A with two intervals between changes, uniform moving and nonequilibrium states, exact CPU remap and overlap, both buffers reset, finite leaves, exact masks and absolute counts. They were not run on the CPU host. The separate independent FP64 capture/replay gate is max population error **3e-6**, with exact masks; remap mass/momentum allow FP32 roundoff. Per-population conservation is not an advancement target under collision.