Files
CelerisLab/docs/fixed_three_level_multipatch.md
T
ClaudeandClaude Opus 5.5 ec9b3d721c P1 (12/12): one FixedPatchStepper construction and initialize API
Removes the legacy entry points now that every caller is migrated:
FixedPatchStepper(cfg, *, context, refinements, coarse_boundary,
level_blocks, level_objects, level_jets, observe_*, explosion) and
initialize(*level_g). Deleted: positional bounds, fine_block, base_block,
fine_objects, jets, child, child_block, child_objects, child_jets,
child_rectangles_in_parent, initialize(coarse_g/fine_g/child_g),
_legacy_levels and _initial_levels. Docstring, README and docs describe
the level lists. Compatibility views (patch, fine_boundary, ...) and the
PeriodicPatchStepper alias are unchanged (separate follow-up).

Error -> rule table (legacy keyword check -> level-list rule now firing):
- several patches without fine_block/base_block -> _check_levels
  'several patches require fine_block and base_block' (unchanged rule)
- base_block without fine_block -> 'a blocked level requires blocked
  refined levels below it'
- nested child without all blocks -> 'three or more levels must all be
  blocked' / 'a blocked level requires ...'
- fine_objects/jets with a nested child ('only on L2') -> 'bodies/Jets are
  supported only on the finest level of a nested hierarchy'
- child_objects/child_jets without a nested child -> 'level_objects/
  level_jets needs one entry per level'
- scalar child with several L1 patches ('scalar child requires one parent
  patch') -> hierarchy 'a scalar child requires a scalar parent'
- child vs child_rectangles_in_parent mutually exclusive -> no level-list
  equivalent (cannot be expressed); covered by the new explicit
  RemovedLegacyAPI test: removed keywords and positional bounds raise
  TypeError before any context use, initialize has only *level_g.
The InitialLevels test of the removed adapter is replaced by that test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 02:02:29 +00:00

4.4 KiB

Fixed three-level rectangle unions

This page describes the legacy three-level entry point of FixedPatchStepper, with rectangle unions on either adjacent pair. The refinements level-list entry point also supports arbitrary depth; three or more levels require all-blocked storage and bodies/Jets only on the finest level. This is not general AMR: there is no per-region timestep, moving geometry, automatic dynamic refinement or L1 body support in the three-level form. All regions share one current/spare buffer pair per level. The existing schedule advances L0 once, L1 twice and L2 four times per base step, with aggregate transfers. No CUDA kernel changes are involved.

Explicit coordinate frames

Keep the existing scalar child=(x0,y0,x1,y1) for a single scalar parent patch. Its coordinates retain their original meaning: half-open integer indices in the allocated L1 window, including its ghost band.

A rectangle list as the second refinements entry (formerly the keyword child_rectangles_in_parent=[...]) accepts a scalar or a set parent and interprets every rectangle in the actual allocated L1 array, including ghost bands and holes. It never infers region-local coordinates. The physical root cell edge corresponding to an L1 index (x,y) is parent.fine_origin + (x,y)/2:

  • A scalar L1 parent has a shifted window origin.
  • A set L1 parent has origin (0,0) and public shape (2*ny,2*nx).
  • A child set has public shape twice the allocated L1 shape. Its root origin is the L1 allocated origin, even when the parent is a scalar window.

For a 16x16 root, scalar parent (4,4,12,12) and scalar child (8,8,16,16) are physically equivalent to parent set [(4,4,12,12)] with child set [(12,12,20,20)]. A child set on the scalar parent instead uses [(8,8,16,16)]. Reusing the same numbers after changing the parent frame can move the physical region.

Both adjacent pairs are built from their unions, without a fixed rectangle-gap rule. Adjacent, overlapping and concave unions are accepted only when their actual transfer dependencies close. The whole child union must preserve inherited ACTIVE coverage, uncovered ACTIVE donors/receivers, surviving pulls, and the upstream restriction footprint. Sparse holes remain unowned; inherited INNER remains stream-only and OUTER remains invalid. Block sizes must divide their allocated level grids. Sparse storage owns every grid-aligned tile intersecting the union plus its four-cell ghost band; rectangle and support edges need not align with tile edges.

Example

run = FixedPatchStepper(
    cfg, context=ctx,
    refinements=[[(4, 4, 20, 28), (24, 4, 44, 28)], [(16, 16, 32, 40), (56, 16, 80, 40)]],
    level_blocks=[(4, 4)] * 3,
)

This example uses a 48x32 root. initialize and download use three Q-major dense arrays. Public arrays and masks still scale with the full allocated extent; sparse device buffers do not remove that host-memory limit.

Stationary whole-circle L2 bodies and Jets (level_objects[2], level_jets[2]) use root edge coordinates and a single L2 boundary object. Body IDs are physical object-list positions, not region IDs or caller obj_id. Complete boundary stencils must avoid the transfer footprint. Change a child Jet with set_jet_amplitude(index, value, level=2); L1 bodies/Jets remain unsupported.

Verification

CPU geometry, table, pre-CUDA rejection, schedule and cleanup tests:

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

tests/unit/test_union_refinement.py covers union dependencies and tile ownership; tests/unit/test_hierarchy.py covers composed geometry through four levels. tests/unit/test_raw_exchange.py checks the four-level host schedule with CUDA mocks. These establish topology/control-flow coverage, not numerical qualification at every depth or interface shape.

Explicit GPU gates are in tests/validation/multigrid/test_refinement_three_level_multipatch_gpu.py. They cover physical-frame equivalence, twins, asymmetric refinement, multiple children, reversed order, independently varied aligned layouts, public/raw masks and two L2 bodies with static/zero/driven/switched Jets. Snapshots are 0/1/2/8. CPU mocks do not execute CUDA and do not establish numerical accuracy. GPU equivalence gates supplement the separately frozen independent dense three-level numerical replay; passing either alone is not a conservation, performance or physical-accuracy claim.