From 580fcf6525c345d86b4bbb35ccda28b56959427d Mon Sep 17 00:00:00 2001 From: Grok Date: Wed, 21 Oct 2026 11:07:29 +0800 Subject: [PATCH] docs(P1): align wording with level-list API only Drop legacy constructor prose (child=/mutual exclusion), retarget README and fixed_three_level_multipatch examples to refinements / level_blocks, and fix stepper error/doc strings that still named fine_block/base_block or a fixed gap >= 4 (unions use dependency closure). Wording only; no behavior change. --- README.md | 53 ++++++++++++------------ docs/fixed_three_level_multipatch.md | 22 +++++----- src/CelerisLab/lbm/refinement_stepper.py | 12 +++--- 3 files changed, 45 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index a63b329..4a579a1 100644 --- a/README.md +++ b/README.md @@ -125,8 +125,8 @@ with CudaContext(0) as ctx, FixedPatchStepper(cfg, context=ctx, refinements=[(8, Scope and limits: - The examples below use D2Q9, SRT, FP32, double buffer, unshifted, no LES or - forcing. Fixed 2:1 hierarchies can use level lists of arbitrary depth; the - legacy two/three-level forms are shown below. + forcing. Fixed 2:1 hierarchies use level lists of arbitrary depth; two- and + three-level examples below use that same constructor. - `coarse_boundary="periodic"` (default) or `"slot_reservoir"` (bottom slot, top reservoir; patch needs `y0 >= 4`, `y1 <= ny-4`). Periodic mode ignores the wall/slot config fields. - Object centers/radii use the coarse **cell-edge** frame (cell `I` spans `[I, I+1)`), @@ -151,7 +151,7 @@ Scope and limits: inside the union's active region with its boundary stencil clear of transfer cells; a jet may not select links of two different bodies, even across regions. Wall-touching patches remain unsupported. - For nesting, use the level lists or legacy child forms below. + For nesting, add further `refinements` entries as shown below. ```python cfg = LBMConfig(nx=32, ny=16, viscosity=.1) @@ -165,19 +165,20 @@ Scope and limits: coarse_g, fine_g = run.download() # coarse: NaN on every covered cell; # fine: NaN on every non-ACTIVE cell ``` -- Legacy three-level scalar chain: add `child=(x0, y0, x1, y1)` in - **L1 window cells** (the patch's fine grid including its 4-cell ghost band) and - all three `level_blocks` entries. L2 must sit inside L1's - ACTIVE cells, away from L1's ghost band and transfer footprint (checked before - any GPU allocation). `initialize` and `download` take/return three arrays; each - level is NaN on its non-ACTIVE (and child-covered) cells. One coarse step = 2 L1 - steps = 4 L2 steps. One L1 patch only. Optional stationary circles and Jets - live on L2 via `level_objects[2]` and `level_jets[2]`; L1 bodies/Jets retain - their L1 meaning and must be empty in this mode. All object and Jet geometry - uses the **L0 edge frame**, not the child bounds' L1 window frame. L2 has - dx=dt=0.25, so prescribed lattice velocity is unchanged. Each whole body and - its BFL stencil must be inside L2 ACTIVE cells, outside the transfer footprint. - A Jet may select one physical body across multiple blocks, never two bodies. +- Three-level nested windows: pass two scalar rectangles in `refinements` — + L1 in L0 cells, L2 in **L1 window cells** (the parent's fine grid including its + 4-cell ghost band) — plus all three `level_blocks` entries. L2 must sit inside + L1's ACTIVE cells, away from L1's ghost band and transfer footprint (checked + before any GPU allocation). `initialize` and `download` take/return three + arrays; each level is NaN on its non-ACTIVE (and child-covered) cells. One + coarse step = 2 L1 steps = 4 L2 steps. One L1 window only in this scalar + chain. Optional stationary circles and Jets live on L2 via `level_objects[2]` + and `level_jets[2]`; L1 bodies/Jets retain their L1 meaning and must be empty + in this mode. All object and Jet geometry uses the **L0 edge frame**, not the + child bounds' L1 window frame. L2 has dx=dt=0.25, so prescribed lattice + velocity is unchanged. Each whole body and its BFL stencil must be inside L2 + ACTIVE cells, outside the transfer footprint. A Jet may select one physical + body across multiple blocks, never two bodies. ```python from CelerisLab.body.geometry.circle import CircleGeometry @@ -199,16 +200,16 @@ Scope and limits: run.step(4) coarse_g, l1_g, l2_g = run.download() # solids/covered/nonleaves are NaN ``` -- Level lists: `refinements[l]` describes level `l+1` in allocated level-`l` - integer cells; use a rectangle list for a union, or a scalar rectangle for a - single window (a scalar child requires a scalar parent). `level_blocks`, - `level_objects` and `level_jets` each have one entry per level, including L0. - These lists are mutually exclusive with the legacy patch keywords. Three or - more levels require all-blocked storage and bodies/Jets only on the finest - level. Every adjacent pair must preserve inherited ACTIVE coverage, valid - donors/receivers, surviving pulls and the upstream transfer footprint. - Level `l` takes `2**l` steps per base interval. This describes implemented - topology and scheduling, not accuracy or convergence qualification. +- Level lists (the only constructor): `refinements[l]` describes level `l+1` + in allocated level-`l` integer cells; use a rectangle list for a union, or a + scalar rectangle for a single window (a scalar child requires a scalar + parent). `level_blocks`, `level_objects` and `level_jets` each have one entry + per level, including L0. Three or more levels require all-blocked storage and + bodies/Jets only on the finest level. Every adjacent pair must preserve + inherited ACTIVE coverage, valid donors/receivers, surviving pulls and the + upstream transfer footprint. Level `l` takes `2**l` steps per base interval. + This describes implemented topology and scheduling, not accuracy or + convergence qualification. - Blocked body levels can enable `observe_raw_exchange=True` and call `read_raw_exchange(body_id, level=...)` after a completed base interval. It reduces unique cut-link observations into wall/aperture/total force, diff --git a/docs/fixed_three_level_multipatch.md b/docs/fixed_three_level_multipatch.md index 2472c9c..1fddbdb 100644 --- a/docs/fixed_three_level_multipatch.md +++ b/docs/fixed_three_level_multipatch.md @@ -1,8 +1,8 @@ # 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 +This page describes three-level `FixedPatchStepper` setups built from the +`refinements` level list, with rectangle unions on either adjacent pair. The +same constructor 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. @@ -12,15 +12,15 @@ 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. +For a single scalar parent patch, pass a scalar rectangle as `refinements[1]`: +`(x0,y0,x1,y1)` in half-open integer indices of 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 rectangle list as `refinements[1]` 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)`. diff --git a/src/CelerisLab/lbm/refinement_stepper.py b/src/CelerisLab/lbm/refinement_stepper.py index 86310ce..4d719aa 100644 --- a/src/CelerisLab/lbm/refinement_stepper.py +++ b/src/CelerisLab/lbm/refinement_stepper.py @@ -75,13 +75,13 @@ def _check_levels(refinements, blocks, objects, jets): raise ValueError(f"{name} needs one entry per level ({depth})") for level, block in enumerate(blocks): if block is not None and len(block) != 2: - raise ValueError(f"{_level_name(level)}_block must be (by, bx)") + raise ValueError(f"level_blocks[{level}] must be (by, bx)") if objects[0] or jets[0]: raise ValueError("base-level bodies/Jets are not supported") if any(blocks[level - 1] is not None and blocks[level] is None for level in range(1, depth)): raise ValueError("a blocked level requires blocked refined levels below it") if not is_rectangle(refinements[0]) and (blocks[0] is None or blocks[1] is None): - raise ValueError("several patches require fine_block and base_block") + raise ValueError("several L1 patches require level_blocks[0] and level_blocks[1]") if depth > 2: if any(block is None for block in blocks): raise ValueError("three or more levels must all be blocked") @@ -149,9 +149,11 @@ class FixedPatchStepper: Nested levels: a child set's coordinates index the allocated parent array, including ghost bands and holes: root edge = parent fine_origin + index / 2 (zero origin for a parent set; a scalar parent keeps its shifted window - origin). A scalar child requires a scalar parent. Each pair requires gap >= 4 - in its own parent-cell units. All regions share one buffer pair per level - and the fixed schedule of 2**l hops per base step at level l. Bodies/Jets are supported only on the + origin). A scalar child requires a scalar parent. Rectangle unions impose no + fixed gap rule: adjacent, overlapping and concave unions are accepted when + their actual transfer dependencies close (FixedPatchSet2D / + nest_patch_set). All regions share one buffer pair per level and the fixed + schedule of 2**l hops per base step at level l. Bodies/Jets are supported only on the finest level of a nested hierarchy (L2 dx=dt=.25 in root units, unchanged lattice velocity); every whole circle and its BFL stencil must fit strictly away from that level's transfer footprint.