Both public examples move off the dense refinement route, inside the scope
proven by the dense/blocked parity gate (1573942: two-level unshifted SRT,
periodic or slot/reservoir root, one circle/actuation or body-free):
- fixed_patch_jet: LEVEL_BLOCKS = [(12, 8), (8, 8)] (48x48 root, 56x40
fine window), recorded in its report config;
- fixed_patch_circle: --base-block (default 12 8) and --fine-block
(default 16 20); the dense 'omit --fine-block' option is gone.
Host test test_example_tiles: each example's tiles (read from the example
source without importing it) build every layout and table up to the first
CUDA call, for the circle in both coarse boundary modes.
No src change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
66 lines
3.2 KiB
Python
66 lines
3.2 KiB
Python
"""The public examples run all-blocked: their level tiles must reach the device unchanged.
|
|
|
|
Host only: a context whose activation raises proves construction (layouts, tables,
|
|
boundary checks) succeeded up to the first CUDA call.
|
|
"""
|
|
import ast
|
|
from dataclasses import replace
|
|
from pathlib import Path
|
|
import unittest
|
|
from unittest.mock import Mock
|
|
|
|
from CelerisLab.body.geometry.circle import CircleGeometry
|
|
from CelerisLab.body.objects import SimObject
|
|
from CelerisLab.config import LBMConfig
|
|
from CelerisLab.lbm.refinement_stepper import FixedPatchStepper
|
|
|
|
EXAMPLES = Path(__file__).resolve().parents[2] / "examples"
|
|
|
|
|
|
def literal(name, constant):
|
|
"""Module-level literal from an example, read without importing it (no pycuda)."""
|
|
tree = ast.parse((EXAMPLES / name).read_text())
|
|
for node in tree.body:
|
|
if isinstance(node, ast.Assign):
|
|
for target in node.targets:
|
|
names = target.elts if isinstance(target, ast.Tuple) else [target]
|
|
for i, t in enumerate(names):
|
|
if isinstance(t, ast.Name) and t.id == constant:
|
|
value = node.value.elts[i] if isinstance(target, ast.Tuple) else node.value
|
|
return ast.literal_eval(value)
|
|
raise KeyError(constant)
|
|
|
|
|
|
def forbidden():
|
|
return Mock(activate=Mock(side_effect=AssertionError("CUDA reached")), sm_arch="sm_70")
|
|
|
|
|
|
class ExampleTiles(unittest.TestCase):
|
|
def test_fixed_patch_jet_tiles(self):
|
|
bounds, blocks = literal("fixed_patch_jet.py", "BOUNDS"), literal("fixed_patch_jet.py", "LEVEL_BLOCKS")
|
|
width, amplitude = literal("fixed_patch_jet.py", "WIDTH"), literal("fixed_patch_jet.py", "AMPLITUDE")
|
|
cfg = LBMConfig(nx=48, ny=48, viscosity=.1, velocity=0., wall_slot_width=width,
|
|
wall_normal_velocity=amplitude, y_wall_bc="on_site_velocity",
|
|
top_wall_bc="reservoir")
|
|
with self.assertRaisesRegex(AssertionError, "CUDA reached"):
|
|
FixedPatchStepper(cfg, context=forbidden(), coarse_boundary="slot_reservoir",
|
|
refinements=[bounds], level_blocks=[tuple(b) for b in blocks])
|
|
|
|
def test_fixed_patch_circle_default_tiles(self):
|
|
bounds = literal("fixed_patch_circle.py", "BOUNDS")
|
|
nx, ny = literal("fixed_patch_circle.py", "NX"), literal("fixed_patch_circle.py", "NY")
|
|
center, radius = literal("fixed_patch_circle.py", "CENTER"), literal("fixed_patch_circle.py", "RADIUS")
|
|
body = SimObject(0, geometry=CircleGeometry(*center, radius), center=center, radius=radius)
|
|
periodic = LBMConfig(nx=nx, ny=ny, viscosity=.1)
|
|
slot = replace(periodic, velocity=0, wall_normal_velocity=.001, wall_slot_width=8,
|
|
y_wall_bc="on_site_velocity", top_wall_bc="reservoir")
|
|
for mode, cfg in (("periodic", periodic), ("slot_reservoir", slot)):
|
|
with self.subTest(mode=mode), self.assertRaisesRegex(AssertionError, "CUDA reached"):
|
|
FixedPatchStepper(cfg, context=forbidden(), coarse_boundary=mode,
|
|
refinements=[bounds], level_blocks=[(12, 8), (16, 20)],
|
|
level_objects=[(), (body,)])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|