Example/docs closure fixes
- README: close the Manual stream control code fence before the new section; snippet now builds rest-equilibrium inputs (runnable as shown). - Example: diagnostics use the known fluid-leaf masks (uncovered coarse minus slot-mode wall rows; fine ACTIVE minus solids) and raise on any non-finite population or rho <= 0 there instead of filtering by finiteness. No solver changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5304525e39
commit
749f103ea8
2 files changed
+30
-8
No files matched your search
@@ -90,6 +90,7 @@ sim.run(100, stream=stream, upload_act=False, sync_obs=False)
|
|||||||
# Later: sync and read
|
# Later: sync and read
|
||||||
stream.synchronize()
|
stream.synchronize()
|
||||||
obs = sim.read_bodies(stream=stream) # sync already done, just read pinned buffer
|
obs = sim.read_bodies(stream=stream) # sync already done, just read pinned buffer
|
||||||
|
```
|
||||||
|
|
||||||
### Fixed 2:1 refinement patch (experimental backend)
|
### Fixed 2:1 refinement patch (experimental backend)
|
||||||
|
|
||||||
@@ -99,10 +100,12 @@ normal surface velocity (blowing/suction). It is a standalone backend, not part
|
|||||||
of `Simulation`. Runnable demo: `examples/fixed_patch_circle.py`.
|
of `Simulation`. Runnable demo: `examples/fixed_patch_circle.py`.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
import numpy as np
|
||||||
from CelerisLab.body.geometry.circle import CircleGeometry
|
from CelerisLab.body.geometry.circle import CircleGeometry
|
||||||
from CelerisLab.body.objects import SimObject, SurfaceActuation
|
from CelerisLab.body.objects import SimObject, SurfaceActuation
|
||||||
from CelerisLab.config import LBMConfig
|
from CelerisLab.config import LBMConfig
|
||||||
from CelerisLab.cuda.context import CudaContext
|
from CelerisLab.cuda.context import CudaContext
|
||||||
|
from CelerisLab.lbm.equilibrium import compute_feq_d2q9
|
||||||
from CelerisLab.lbm.refinement_stepper import FixedPatchStepper
|
from CelerisLab.lbm.refinement_stepper import FixedPatchStepper
|
||||||
|
|
||||||
cfg = LBMConfig(nx=32, ny=24, viscosity=.1)
|
cfg = LBMConfig(nx=32, ny=24, viscosity=.1)
|
||||||
@@ -110,7 +113,10 @@ body = SimObject(0, geometry=CircleGeometry(16.0, 12.0, 2.15), center=(16.0, 12.
|
|||||||
surface_actuation=SurfaceActuation(0.7, 0.85, 0.002)) # angle, half-width, speed
|
surface_actuation=SurfaceActuation(0.7, 0.85, 0.002)) # angle, half-width, speed
|
||||||
with CudaContext(0) as ctx, FixedPatchStepper(cfg, (8, 6, 24, 18), context=ctx,
|
with CudaContext(0) as ctx, FixedPatchStepper(cfg, (8, 6, 24, 18), context=ctx,
|
||||||
fine_objects=[body], fine_block=(16, 20)) as run:
|
fine_objects=[body], fine_block=(16, 20)) as run:
|
||||||
run.initialize(coarse_g, fine_g) # (9, ny, nx) postcollision populations per level
|
rest = np.asarray(compute_feq_d2q9(1.0, 0.0, 0.0), np.float32)[:, None, None]
|
||||||
|
# (9, ny, nx) postcollision populations per level; non-leaf cells are discarded.
|
||||||
|
run.initialize(np.broadcast_to(rest, (9, *run.patch.coarse_shape)),
|
||||||
|
np.broadcast_to(rest, (9, *run.patch.fine_shape)))
|
||||||
run.step(128) # coarse steps; fine level takes two per step
|
run.step(128) # coarse steps; fine level takes two per step
|
||||||
coarse_g, fine_g = run.download() # NaN on covered, ghost and solid cells
|
coarse_g, fine_g = run.download() # NaN on covered, ghost and solid cells
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -19,8 +19,9 @@ from CelerisLab.body.geometry.circle import CircleGeometry
|
|||||||
from CelerisLab.body.objects import SimObject, SurfaceActuation
|
from CelerisLab.body.objects import SimObject, SurfaceActuation
|
||||||
from CelerisLab.config import LBMConfig
|
from CelerisLab.config import LBMConfig
|
||||||
from CelerisLab.cuda.context import CudaContext
|
from CelerisLab.cuda.context import CudaContext
|
||||||
from CelerisLab.lbm.descriptors import D2Q9_EX, D2Q9_EY
|
from CelerisLab.lbm.descriptors import D2Q9_EX, D2Q9_EY, SOLID
|
||||||
from CelerisLab.lbm.equilibrium import compute_feq_d2q9
|
from CelerisLab.lbm.equilibrium import compute_feq_d2q9
|
||||||
|
from CelerisLab.lbm.refinement import ACTIVE
|
||||||
from CelerisLab.lbm.refinement_stepper import FixedPatchStepper
|
from CelerisLab.lbm.refinement_stepper import FixedPatchStepper
|
||||||
|
|
||||||
NX, NY, BOUNDS = 32, 24, (8, 6, 24, 18) # coarse cells; patch is half-open (x0, y0, x1, y1)
|
NX, NY, BOUNDS = 32, 24, (8, 6, 24, 18) # coarse cells; patch is half-open (x0, y0, x1, y1)
|
||||||
@@ -35,15 +36,29 @@ def config(mode):
|
|||||||
wall_slot_width=8, y_wall_bc="on_site_velocity", top_wall_bc="reservoir")
|
wall_slot_width=8, y_wall_bc="on_site_velocity", top_wall_bc="reservoir")
|
||||||
|
|
||||||
|
|
||||||
def leaf_diagnostics(coarse, fine):
|
def leaf_masks(run, mode):
|
||||||
"""Fluid leaves only (downloads are NaN on covered/ghost/solid cells).
|
"""Known fluid leaves: uncovered coarse cells (minus wall rows in slot mode)
|
||||||
|
and fine ACTIVE cells outside the body."""
|
||||||
|
p = run.patch
|
||||||
|
x0, y0, x1, y1 = p.bounds
|
||||||
|
coarse = np.ones(p.coarse_shape, bool)
|
||||||
|
coarse[y0:y1, x0:x1] = False
|
||||||
|
if mode == "slot_reservoir":
|
||||||
|
coarse[[0, -1]] = False
|
||||||
|
solid = (run.fine_boundary.flags.reshape(p.fine_shape) & SOLID) != 0
|
||||||
|
return coarse, (p.fine_kind == ACTIVE) & ~solid
|
||||||
|
|
||||||
|
|
||||||
|
def leaf_diagnostics(coarse, fine, masks):
|
||||||
|
"""Fluid leaves only; a non-finite population or rho <= 0 there is an error.
|
||||||
|
|
||||||
Mass counts a fine cell as 1/4 of a coarse cell (2D, 2:1)."""
|
Mass counts a fine cell as 1/4 of a coarse cell (2D, 2:1)."""
|
||||||
out = {}
|
out = {}
|
||||||
for name, g, area in (("coarse", coarse, 1.0), ("fine", fine, .25)):
|
for name, g, leaf, area in (("coarse", coarse, masks[0], 1.0), ("fine", fine, masks[1], .25)):
|
||||||
leaf = np.isfinite(g).all(axis=0)
|
|
||||||
f = g[:, leaf].astype(np.float64)
|
f = g[:, leaf].astype(np.float64)
|
||||||
rho = f.sum(axis=0)
|
rho = f.sum(axis=0)
|
||||||
|
if not np.isfinite(f).all() or (rho <= 0).any():
|
||||||
|
raise RuntimeError(f"non-finite population or non-positive density on a {name} fluid leaf")
|
||||||
ux = (np.array(D2Q9_EX)[:, None] * f).sum(axis=0) / rho
|
ux = (np.array(D2Q9_EX)[:, None] * f).sum(axis=0) / rho
|
||||||
uy = (np.array(D2Q9_EY)[:, None] * f).sum(axis=0) / rho
|
uy = (np.array(D2Q9_EY)[:, None] * f).sum(axis=0) / rho
|
||||||
out[name] = (area * rho.sum(), rho.min(), rho.max(), np.hypot(ux, uy).max())
|
out[name] = (area * rho.sum(), rho.min(), rho.max(), np.hypot(ux, uy).max())
|
||||||
@@ -83,11 +98,12 @@ def main():
|
|||||||
storage = f"blocked {fine_block}" if fine_block else "dense"
|
storage = f"blocked {fine_block}" if fine_block else "dense"
|
||||||
print(f"mode={args.mode} fine storage={storage} speed={args.speed:+g} "
|
print(f"mode={args.mode} fine storage={storage} speed={args.speed:+g} "
|
||||||
f"(fine allocation {p.fine_shape[0]}x{p.fine_shape[1]}, fine origin {p.fine_origin})")
|
f"(fine allocation {p.fine_shape[0]}x{p.fine_shape[1]}, fine origin {p.fine_origin})")
|
||||||
start = leaf_diagnostics(*run.download())
|
masks = leaf_masks(run, args.mode)
|
||||||
|
start = leaf_diagnostics(*run.download(), masks)
|
||||||
for _ in range(0, args.steps, 32):
|
for _ in range(0, args.steps, 32):
|
||||||
run.step(min(32, args.steps - run.step_count))
|
run.step(min(32, args.steps - run.step_count))
|
||||||
coarse, fine = run.download() # synchronized postcollision G, NaN on non-leaves
|
coarse, fine = run.download() # synchronized postcollision G, NaN on non-leaves
|
||||||
d = leaf_diagnostics(coarse, fine)
|
d = leaf_diagnostics(coarse, fine, masks)
|
||||||
mass = d["coarse"][0] + d["fine"][0]
|
mass = d["coarse"][0] + d["fine"][0]
|
||||||
rho_lo = min(d["coarse"][1], d["fine"][1])
|
rho_lo = min(d["coarse"][1], d["fine"][1])
|
||||||
rho_hi = max(d["coarse"][2], d["fine"][2])
|
rho_hi = max(d["coarse"][2], d["fine"][2])
|
||||||
|
|||||||
Reference in new issue
Block a user