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.
This commit is contained in:
2026-10-21 11:07:29 +08:00
parent 18f018befe
commit 580fcf6525
3 changed files with 45 additions and 42 deletions
+27 -26
View File
@@ -125,8 +125,8 @@ with CudaContext(0) as ctx, FixedPatchStepper(cfg, context=ctx, refinements=[(8,
Scope and limits: Scope and limits:
- The examples below use D2Q9, SRT, FP32, double buffer, unshifted, no LES or - 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 forcing. Fixed 2:1 hierarchies use level lists of arbitrary depth; two- and
legacy two/three-level forms are shown below. three-level examples below use that same constructor.
- `coarse_boundary="periodic"` (default) or `"slot_reservoir"` (bottom slot, top - `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. 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)`), - 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 inside the union's active region with
its boundary stencil clear of transfer cells; a jet may not select links of two 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. 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 ```python
cfg = LBMConfig(nx=32, ny=16, viscosity=.1) 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; coarse_g, fine_g = run.download() # coarse: NaN on every covered cell;
# fine: NaN on every non-ACTIVE cell # fine: NaN on every non-ACTIVE cell
``` ```
- Legacy three-level scalar chain: add `child=(x0, y0, x1, y1)` in - Three-level nested windows: pass two scalar rectangles in `refinements` —
**L1 window cells** (the patch's fine grid including its 4-cell ghost band) and L1 in L0 cells, L2 in **L1 window cells** (the parent's fine grid including its
all three `level_blocks` entries. L2 must sit inside L1's 4-cell ghost band) — plus all three `level_blocks` entries. L2 must sit inside
ACTIVE cells, away from L1's ghost band and transfer footprint (checked before L1's ACTIVE cells, away from L1's ghost band and transfer footprint (checked
any GPU allocation). `initialize` and `download` take/return three arrays; each before any GPU allocation). `initialize` and `download` take/return three
level is NaN on its non-ACTIVE (and child-covered) cells. One coarse step = 2 L1 arrays; each level is NaN on its non-ACTIVE (and child-covered) cells. One
steps = 4 L2 steps. One L1 patch only. Optional stationary circles and Jets coarse step = 2 L1 steps = 4 L2 steps. One L1 window only in this scalar
live on L2 via `level_objects[2]` and `level_jets[2]`; L1 bodies/Jets retain chain. Optional stationary circles and Jets live on L2 via `level_objects[2]`
their L1 meaning and must be empty in this mode. All object and Jet geometry and `level_jets[2]`; L1 bodies/Jets retain their L1 meaning and must be empty
uses the **L0 edge frame**, not the child bounds' L1 window frame. L2 has in this mode. All object and Jet geometry uses the **L0 edge frame**, not the
dx=dt=0.25, so prescribed lattice velocity is unchanged. Each whole body and child bounds' L1 window frame. L2 has dx=dt=0.25, so prescribed lattice
its BFL stencil must be inside L2 ACTIVE cells, outside the transfer footprint. velocity is unchanged. Each whole body and its BFL stencil must be inside L2
A Jet may select one physical body across multiple blocks, never two bodies. ACTIVE cells, outside the transfer footprint. A Jet may select one physical
body across multiple blocks, never two bodies.
```python ```python
from CelerisLab.body.geometry.circle import CircleGeometry from CelerisLab.body.geometry.circle import CircleGeometry
@@ -199,16 +200,16 @@ Scope and limits:
run.step(4) run.step(4)
coarse_g, l1_g, l2_g = run.download() # solids/covered/nonleaves are NaN 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` - Level lists (the only constructor): `refinements[l]` describes level `l+1`
integer cells; use a rectangle list for a union, or a scalar rectangle for a in allocated level-`l` integer cells; use a rectangle list for a union, or a
single window (a scalar child requires a scalar parent). `level_blocks`, scalar rectangle for a single window (a scalar child requires a scalar
`level_objects` and `level_jets` each have one entry per level, including L0. parent). `level_blocks`, `level_objects` and `level_jets` each have one entry
These lists are mutually exclusive with the legacy patch keywords. Three or per level, including L0. Three or more levels require all-blocked storage and
more levels require all-blocked storage and bodies/Jets only on the finest bodies/Jets only on the finest level. Every adjacent pair must preserve
level. Every adjacent pair must preserve inherited ACTIVE coverage, valid inherited ACTIVE coverage, valid donors/receivers, surviving pulls and the
donors/receivers, surviving pulls and the upstream transfer footprint. upstream transfer footprint. Level `l` takes `2**l` steps per base interval.
Level `l` takes `2**l` steps per base interval. This describes implemented This describes implemented topology and scheduling, not accuracy or
topology and scheduling, not accuracy or convergence qualification. convergence qualification.
- Blocked body levels can enable `observe_raw_exchange=True` and call - Blocked body levels can enable `observe_raw_exchange=True` and call
`read_raw_exchange(body_id, level=...)` after a completed base interval. `read_raw_exchange(body_id, level=...)` after a completed base interval.
It reduces unique cut-link observations into wall/aperture/total force, It reduces unique cut-link observations into wall/aperture/total force,
+11 -11
View File
@@ -1,8 +1,8 @@
# Fixed three-level rectangle unions # Fixed three-level rectangle unions
This page describes the legacy three-level entry point of `FixedPatchStepper`, This page describes three-level `FixedPatchStepper` setups built from the
with rectangle unions on either adjacent pair. The `refinements` level-list `refinements` level list, with rectangle unions on either adjacent pair. The
entry point also supports arbitrary depth; three or more levels require same constructor supports arbitrary depth; three or more levels require
all-blocked storage and bodies/Jets only on the finest level. This is not 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 general AMR: there is no per-region timestep, moving geometry, automatic
dynamic refinement or L1 body support in the three-level form. dynamic refinement or L1 body support in the three-level form.
@@ -12,15 +12,15 @@ No CUDA kernel changes are involved.
## Explicit coordinate frames ## Explicit coordinate frames
Keep the existing scalar `child=(x0,y0,x1,y1)` for a single scalar parent patch. For a single scalar parent patch, pass a scalar rectangle as `refinements[1]`:
Its coordinates retain their original meaning: half-open integer indices in the `(x0,y0,x1,y1)` in half-open integer indices of the allocated L1 window,
allocated L1 window, including its ghost band. including its ghost band.
A rectangle list as the second `refinements` entry (formerly the keyword A rectangle list as `refinements[1]` accepts a scalar or a set parent and
`child_rectangles_in_parent=[...]`) accepts a scalar or a set parent and interprets every rectangle in interprets every rectangle in the actual allocated L1 array, including ghost
the actual allocated L1 array, including ghost bands and holes. It never infers bands and holes. It never infers region-local coordinates. The physical root
region-local coordinates. The physical root cell edge corresponding to an L1 cell edge corresponding to an L1 index `(x,y)` is
index `(x,y)` is `parent.fine_origin + (x,y)/2`: `parent.fine_origin + (x,y)/2`:
- A scalar L1 parent has a shifted window origin. - 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 set L1 parent has origin `(0,0)` and public shape `(2*ny,2*nx)`.
+7 -5
View File
@@ -75,13 +75,13 @@ def _check_levels(refinements, blocks, objects, jets):
raise ValueError(f"{name} needs one entry per level ({depth})") raise ValueError(f"{name} needs one entry per level ({depth})")
for level, block in enumerate(blocks): for level, block in enumerate(blocks):
if block is not None and len(block) != 2: 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]: if objects[0] or jets[0]:
raise ValueError("base-level bodies/Jets are not supported") 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)): 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") 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): 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 depth > 2:
if any(block is None for block in blocks): if any(block is None for block in blocks):
raise ValueError("three or more levels must all be blocked") 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, Nested levels: a child set's coordinates index the allocated parent array,
including ghost bands and holes: root edge = parent fine_origin + index / 2 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 (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 origin). A scalar child requires a scalar parent. Rectangle unions impose no
in its own parent-cell units. All regions share one buffer pair per level fixed gap rule: adjacent, overlapping and concave unions are accepted when
and the fixed schedule of 2**l hops per base step at level l. Bodies/Jets are supported only on the 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 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 lattice velocity); every whole circle and its BFL stencil must fit strictly
away from that level's transfer footprint. away from that level's transfer footprint.