Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae53fade13 | ||
|
|
358bed1962 |
No files matched your search
@@ -117,8 +117,8 @@ with CudaContext(0) as ctx, FixedPatchStepper(cfg, context=ctx, refinements=[(8,
|
|||||||
level_objects=[(), (body,)]) as run:
|
level_objects=[(), (body,)]) as run:
|
||||||
rest = np.asarray(compute_feq_d2q9(1.0, 0.0, 0.0), np.float32)[:, None, None]
|
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.
|
# (9, ny, nx) postcollision populations per level; non-leaf cells are discarded.
|
||||||
run.initialize(np.broadcast_to(rest, (9, *run.hierarchy.pairs[0].coarse_shape)),
|
run.initialize(np.broadcast_to(rest, (9, *run.patch.coarse_shape)),
|
||||||
np.broadcast_to(rest, (9, *run.hierarchy.pairs[0].fine_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
|
||||||
```
|
```
|
||||||
@@ -143,7 +143,7 @@ Scope and limits:
|
|||||||
from their union; adjacent, overlapping and concave unions are accepted when
|
from their union; adjacent, overlapping and concave unions are accepted when
|
||||||
their actual dependencies close. There is no fixed rectangle-gap rule.
|
their actual dependencies close. There is no fixed rectangle-gap rule.
|
||||||
The fine level is then one level-wide grid `(2*ny, 2*nx)`:
|
The fine level is then one level-wide grid `(2*ny, 2*nx)`:
|
||||||
`run.hierarchy.pairs[0].fine_shape` gives it, `initialize`/`download` use it, and only the
|
`run.patch.fine_shape` gives it, `initialize`/`download` use it, and only the
|
||||||
grid-aligned tiles intersecting the union plus its four-cell ghost band are stored.
|
grid-aligned tiles intersecting the union plus its four-cell ghost band are stored.
|
||||||
Block sizes must divide the level-wide grid; rectangle edges need not align
|
Block sizes must divide the level-wide grid; rectangle edges need not align
|
||||||
with tiles. Order of rectangles does not matter. L1 bodies/Jets (`level_objects[1]`/`level_jets[1]`) work
|
with tiles. Order of rectangles does not matter. L1 bodies/Jets (`level_objects[1]`/`level_jets[1]`) work
|
||||||
@@ -159,8 +159,8 @@ Scope and limits:
|
|||||||
with CudaContext(0) as ctx, FixedPatchStepper(
|
with CudaContext(0) as ctx, FixedPatchStepper(
|
||||||
cfg, context=ctx, refinements=[((4, 4, 12, 12), (16, 4, 24, 12))],
|
cfg, context=ctx, refinements=[((4, 4, 12, 12), (16, 4, 24, 12))],
|
||||||
level_blocks=[(4, 4), (4, 4)]) as run:
|
level_blocks=[(4, 4), (4, 4)]) as run:
|
||||||
run.initialize(np.broadcast_to(rest, (9, *run.hierarchy.pairs[0].coarse_shape)),
|
run.initialize(np.broadcast_to(rest, (9, *run.patch.coarse_shape)),
|
||||||
np.broadcast_to(rest, (9, *run.hierarchy.pairs[0].fine_shape))) # (9, 32, 64)
|
np.broadcast_to(rest, (9, *run.patch.fine_shape))) # (9, 32, 64)
|
||||||
run.step(32)
|
run.step(32)
|
||||||
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
|
||||||
@@ -193,10 +193,10 @@ Scope and limits:
|
|||||||
cfg, context=ctx, refinements=[(4, 4, 12, 12), (8, 8, 16, 16)],
|
cfg, context=ctx, refinements=[(4, 4, 12, 12), (8, 8, 16, 16)],
|
||||||
level_blocks=[(4, 4)] * 3, level_objects=[(), (), (body,)],
|
level_blocks=[(4, 4)] * 3, level_objects=[(), (), (body,)],
|
||||||
level_jets=[(), (), (jet,)]) as run:
|
level_jets=[(), (), (jet,)]) as run:
|
||||||
shapes = (run.hierarchy.pairs[0].coarse_shape, run.hierarchy.pairs[0].fine_shape, run.hierarchy.pairs[1].fine_shape)
|
shapes = (run.patch.coarse_shape, run.patch.fine_shape, run.child_patch.fine_shape)
|
||||||
run.initialize(*(np.broadcast_to(rest, (9, *s)) for s in shapes)) # 16², 24², 24²
|
run.initialize(*(np.broadcast_to(rest, (9, *s)) for s in shapes)) # 16², 24², 24²
|
||||||
run.step(4)
|
run.step(4)
|
||||||
run.set_jet_amplitude(0, -.002, level=2) # explicit level selects the driven level
|
run.set_jet_amplitude(0, -.002, level=2) # default level=1 still addresses L1
|
||||||
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
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -8,8 +8,16 @@ pending a GPU run).
|
|||||||
|
|
||||||
- Level `l = 0 .. depth-1` (0 = root). Pair `hierarchy.pairs[l-1]` builds level `l`.
|
- Level `l = 0 .. depth-1` (0 = root). Pair `hierarchy.pairs[l-1]` builds level `l`.
|
||||||
- Array shapes are `(ny, nx)`; rectangles, origins and centers are `(x, y)`.
|
- Array shapes are `(ny, nx)`; rectangles, origins and centers are `(x, y)`.
|
||||||
- FixedPatchStepper bodies/Jets use the root EDGE frame (root cell `i` = `[i, i+1)`);
|
- Coarse/fine name the two members (parent/child) of ANY pair, not absolute L0/L1:
|
||||||
uniform `Simulation` objects use the integer-NODE frame. Nothing converts between them.
|
`fine_shape`, `fine_origin`, `prepare_fine_boundary`, `blocked_fine_tables`,
|
||||||
|
`blocked_coarse_tables`.
|
||||||
|
- Refined levels (l >= 1) of FixedPatchStepper place bodies/Jets in the root EDGE frame
|
||||||
|
(root cell `i` = `[i, i+1)`). Uniform `Simulation` objects AND the static circles of a
|
||||||
|
single-level (depth 1) FixedPatchStepper use the integer-NODE frame. Nothing converts
|
||||||
|
between them. This concerns input placement only: within the EDGE frame, refined
|
||||||
|
levels still project root-edge coordinates to fine-local cell indices with the
|
||||||
|
half-cell shift (`prepare_fine_boundary`: `(v - o)/dx - .5`; hits back via `fine_hits_to_edge_frame`
|
||||||
|
`origin + dx*(hit + .5)`), using `edge_frame`/`fine_origin` below.
|
||||||
- `Hierarchy.edge_frame(l)` -> `((ox, oy), h)`: cell `(i, j)` of level `l`'s allocated
|
- `Hierarchy.edge_frame(l)` -> `((ox, oy), h)`: cell `(i, j)` of level `l`'s allocated
|
||||||
array (ghost band and holes included) covers `[o + h*i, o + h*(i+1))`. A scalar
|
array (ghost band and holes included) covers `[o + h*i, o + h*(i+1))`. A scalar
|
||||||
FixedPatch2D window carries the half-coarse-cell ghost shift `bounds - HALO/2` in
|
FixedPatch2D window carries the half-coarse-cell ghost shift `bounds - HALO/2` in
|
||||||
@@ -26,8 +34,11 @@ pending a GPU run).
|
|||||||
| `base_layout`, `fine_layout`, `child_layout` (stepper properties) | `layouts[0]`, `layouts[1]`, `layouts[2]` (`FixedPatchSet2D.fine_layout(block)` is unchanged) |
|
| `base_layout`, `fine_layout`, `child_layout` (stepper properties) | `layouts[0]`, `layouts[1]`, `layouts[2]` (`FixedPatchSet2D.fine_layout(block)` is unchanged) |
|
||||||
| `fine_boundary`, `child_boundary` | `boundaries[1]`, `boundaries[2]` (host geometry; `None` = no bodies/Jets) |
|
| `fine_boundary`, `child_boundary` | `boundaries[1]`, `boundaries[2]` (host geometry; `None` = no bodies/Jets) |
|
||||||
| `_jets` | `jets[1]` |
|
| `_jets` | `jets[1]` |
|
||||||
| `_boundaries[l]` (dict from L1, level keys) | `_level_boundaries[l]` (list from L0, `None` at L0 unless single-level static circles) |
|
| `_boundaries[l]` (dict from L1, level keys) | `_device_boundaries[l]` (list from L0, `None` at L0 unless single-level static circles) |
|
||||||
| `_fine_tables`, `_actuated`, `_jet_cache` | `_level_boundaries[1].tables` / `.actuated` / `.jet_cache` |
|
| `_level_boundaries` (private, b4eacf7) | `_device_boundaries` |
|
||||||
|
| `_fine_tables`, `_actuated`, `_jet_cache` | `_device_boundaries[1].tables` / `.actuated` / `.jet_cache` |
|
||||||
|
| `_remap_cfg` (private) | `_root_cfg` |
|
||||||
|
| `refinement_tables.blocked_base_tables` (also via `refinement_stepper`) | `refinement_tables.blocked_coarse_tables` (import from `refinement_tables`; the stepper no longer re-exports) |
|
||||||
| `_fine_leaf`, `_child_leaf` | `leaf[1]`, `leaf[2]` (copies) |
|
| `_fine_leaf`, `_child_leaf` | `leaf[1]`, `leaf[2]` (copies) |
|
||||||
| `_middle_kind` | `hierarchy.kinds[1]` |
|
| `_middle_kind` | `hierarchy.kinds[1]` |
|
||||||
| `_boundary_hosts` (private) | `_boundaries` (private list behind `boundaries`) |
|
| `_boundary_hosts` (private) | `_boundaries` (private list behind `boundaries`) |
|
||||||
@@ -56,6 +67,10 @@ the replaced level relative to its parent).
|
|||||||
|
|
||||||
## Not done / open
|
## Not done / open
|
||||||
|
|
||||||
|
- Two per-level config derivations stay distinct on purpose: `Hierarchy.configs`
|
||||||
|
(`_middle_config`, wall fields reset on every refined level, geometry validation) vs.
|
||||||
|
`FixedPatchStepper._kernel_configs` (reset only on level 1 of a slot_reservoir root;
|
||||||
|
generates each level's config.h). Folding them needs a config.h/PTX diff gate.
|
||||||
- No single-`Simulation` facade over FixedPatchStepper: the two own different state models
|
- No single-`Simulation` facade over FixedPatchStepper: the two own different state models
|
||||||
(uniform node frame, body sync, forcing, checkpoint vs. immutable blocked hierarchy, edge
|
(uniform node frame, body sync, forcing, checkpoint vs. immutable blocked hierarchy, edge
|
||||||
frame, remap-as-new-instance). A facade would only forward calls; no concrete need found.
|
frame, remap-as-new-instance). A facade would only forward calls; no concrete need found.
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ coverage and two-hop BFL transfer-footprint checks still apply. This is manual
|
|||||||
remapping, not adaptive body tracking or runtime body editing.
|
remapping, not adaptive body tracking or runtime body editing.
|
||||||
|
|
||||||
Supply all original root edge-frame definitions in their construction order.
|
Supply all original root edge-frame definitions in their construction order.
|
||||||
Do not pass `current.boundaries[2].objects`: those are projected fine-local copies.
|
Do not pass `current.child_boundary.objects`: those are projected fine-local copies.
|
||||||
There is no retained mutable original-object registry. Before reading device
|
There is no retained mutable original-object registry. Before reading device
|
||||||
state, remapping reprojects the supplied definition onto the old topology and
|
state, remapping reprojects the supplied definition onto the old topology and
|
||||||
compares exact ordered circle centers/radii, flags, physical cut links, body
|
compares exact ordered circle centers/radii, flags, physical cut links, body
|
||||||
|
|||||||
@@ -40,13 +40,13 @@ def config(mode):
|
|||||||
def leaf_masks(run, mode):
|
def leaf_masks(run, mode):
|
||||||
"""Known fluid leaves: uncovered coarse cells (minus wall rows in slot mode)
|
"""Known fluid leaves: uncovered coarse cells (minus wall rows in slot mode)
|
||||||
and fine ACTIVE cells outside the body."""
|
and fine ACTIVE cells outside the body."""
|
||||||
p = run.hierarchy.pairs[0]
|
p = run.patch
|
||||||
x0, y0, x1, y1 = p.bounds
|
x0, y0, x1, y1 = p.bounds
|
||||||
coarse = np.ones(p.coarse_shape, bool)
|
coarse = np.ones(p.coarse_shape, bool)
|
||||||
coarse[y0:y1, x0:x1] = False
|
coarse[y0:y1, x0:x1] = False
|
||||||
if mode == "slot_reservoir":
|
if mode == "slot_reservoir":
|
||||||
coarse[[0, -1]] = False
|
coarse[[0, -1]] = False
|
||||||
solid = (run.boundaries[1].flags.reshape(p.fine_shape) & SOLID) != 0
|
solid = (run.fine_boundary.flags.reshape(p.fine_shape) & SOLID) != 0
|
||||||
return coarse, (p.fine_kind == ACTIVE) & ~solid
|
return coarse, (p.fine_kind == ACTIVE) & ~solid
|
||||||
|
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ def main():
|
|||||||
cfg, context=context, coarse_boundary=args.mode, refinements=[BOUNDS],
|
cfg, context=context, coarse_boundary=args.mode, refinements=[BOUNDS],
|
||||||
level_blocks=[tuple(args.base_block), tuple(args.fine_block)],
|
level_blocks=[tuple(args.base_block), tuple(args.fine_block)],
|
||||||
level_objects=[(), (body,)]) as run:
|
level_objects=[(), (body,)]) as run:
|
||||||
p = run.hierarchy.pairs[0]
|
p = run.patch
|
||||||
# Rest equilibrium is already postcollision G, so no initial collision is
|
# Rest equilibrium is already postcollision G, so no initial collision is
|
||||||
# needed. Non-leaf entries (covered, ghost, solid) are discarded by initialize.
|
# needed. Non-leaf entries (covered, ghost, solid) are discarded by initialize.
|
||||||
rest = np.asarray(compute_feq_d2q9(1.0, 0.0, 0.0), np.float32)[:, None, None]
|
rest = np.asarray(compute_feq_d2q9(1.0, 0.0, 0.0), np.float32)[:, None, None]
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ def main():
|
|||||||
with CudaContext(0) as context, FixedPatchStepper(cfg, context=context,
|
with CudaContext(0) as context, FixedPatchStepper(cfg, context=context,
|
||||||
coarse_boundary='slot_reservoir', refinements=[BOUNDS],
|
coarse_boundary='slot_reservoir', refinements=[BOUNDS],
|
||||||
level_blocks=LEVEL_BLOCKS) as run:
|
level_blocks=LEVEL_BLOCKS) as run:
|
||||||
p = run.hierarchy.pairs[0]
|
p = run.patch
|
||||||
masks = [np.ones(p.coarse_shape, bool), p.fine_kind == ACTIVE]
|
masks = [np.ones(p.coarse_shape, bool), p.fine_kind == ACTIVE]
|
||||||
masks[0][[0, -1]] = False
|
masks[0][[0, -1]] = False
|
||||||
masks[0][4:28, 16:32] = False
|
masks[0][4:28, 16:32] = False
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ def main():
|
|||||||
store_precision="FP32", ddf_shifting=False, les_enabled=False)
|
store_precision="FP32", ddf_shifting=False, les_enabled=False)
|
||||||
with CudaContext(0) as context, FixedPatchStepper(cfg, context=context, refinements=[(4, 4, 12, 12)],
|
with CudaContext(0) as context, FixedPatchStepper(cfg, context=context, refinements=[(4, 4, 12, 12)],
|
||||||
level_blocks=[(16, 16), (24, 24)]) as run:
|
level_blocks=[(16, 16), (24, 24)]) as run:
|
||||||
p = run.hierarchy.pairs[0]
|
p = run.patch
|
||||||
coarse = shear_equilibrium(p.coarse_shape, (0, 0), 1.0, cfg.ny)
|
coarse = shear_equilibrium(p.coarse_shape, (0, 0), 1.0, cfg.ny)
|
||||||
fine = shear_equilibrium(p.fine_shape, p.fine_origin, .5, cfg.ny)
|
fine = shear_equilibrium(p.fine_shape, p.fine_origin, .5, cfg.ny)
|
||||||
coarse_leaf = np.ones(p.coarse_shape, dtype=bool)
|
coarse_leaf = np.ones(p.coarse_shape, dtype=bool)
|
||||||
|
|||||||
@@ -26,8 +26,15 @@ class Hierarchy:
|
|||||||
Index conventions (the single place they are defined):
|
Index conventions (the single place they are defined):
|
||||||
* level l = 0 (root) .. depth-1; pair index p = l-1 builds level l from level p;
|
* level l = 0 (root) .. depth-1; pair index p = l-1 builds level l from level p;
|
||||||
* array shapes are (ny, nx); rectangles, origins and centers are (x, y);
|
* array shapes are (ny, nx); rectangles, origins and centers are (x, y);
|
||||||
* the root EDGE frame puts root cell i at [i, i+1), centers at i+.5 (bodies and
|
* a pair's two members are its COARSE (parent) and FINE (child) level, for every
|
||||||
Jets of FixedPatchStepper); uniform Simulation objects use the integer-NODE frame;
|
pair, not absolute L0/L1 (fine_shape, fine_origin, blocked_coarse_tables, ...);
|
||||||
|
* refined levels (l >= 1) of FixedPatchStepper place bodies and Jets in the root
|
||||||
|
EDGE frame: root cell i covers [i, i+1), center i+.5. Uniform Simulation objects
|
||||||
|
and the static circles of a single-level (depth 1) FixedPatchStepper use the
|
||||||
|
integer-NODE frame (CircleGeometry.build_flag_mask at integer (x, y)). No
|
||||||
|
NODE<->EDGE input conversion is applied anywhere; inside the EDGE frame, refined
|
||||||
|
boundaries still project root-edge coordinates to fine-local cell indices with
|
||||||
|
the half-cell shift ((v - o)/h - .5, see prepare_fine_boundary);
|
||||||
* cell (i, j) of level l's allocated array (ghost band and holes included) covers
|
* cell (i, j) of level l's allocated array (ghost band and holes included) covers
|
||||||
[o + h*i, o + h*(i+1)) with (o, h) = edge_frame(l); level 1 is built from the root
|
[o + h*i, o + h*(i+1)) with (o, h) = edge_frame(l); level 1 is built from the root
|
||||||
config directly, deeper levels from their parent frame (see edge_frame).
|
config directly, deeper levels from their parent frame (see edge_frame).
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ from .channel import channel_masks, validate_channel_config, validate_channel_tr
|
|||||||
from ..body.jets import jet_link_velocity
|
from ..body.jets import jet_link_velocity
|
||||||
from .hierarchy import build_hierarchy
|
from .hierarchy import build_hierarchy
|
||||||
from .refinement import ACTIVE, HALO, FixedPatch2D, FixedPatchSet2D, prepare_fine_boundary
|
from .refinement import ACTIVE, HALO, FixedPatch2D, FixedPatchSet2D, prepare_fine_boundary
|
||||||
from .refinement_tables import (blocked_base_tables, blocked_fine_tables, # noqa: F401 (re-export)
|
from .refinement_tables import (blocked_coarse_tables, blocked_fine_tables,
|
||||||
blocked_kind, projected_explosion_tables, _prepare_blocked_boundary,
|
blocked_kind, projected_explosion_tables, _prepare_blocked_boundary,
|
||||||
_BlockedBoundary,
|
_BlockedBoundary,
|
||||||
BLOCKED_BOUNDARY_DEVICE_COLUMNS)
|
BLOCKED_BOUNDARY_DEVICE_COLUMNS)
|
||||||
@@ -66,8 +66,12 @@ class _Level:
|
|||||||
def _kernel_configs(cfg, pairs, wall_aware):
|
def _kernel_configs(cfg, pairs, wall_aware):
|
||||||
"""Per-level kernel LBMConfig (window shape, viscosity doubling per level).
|
"""Per-level kernel LBMConfig (window shape, viscosity doubling per level).
|
||||||
|
|
||||||
Geometry configs live in Hierarchy.configs; level 1 of a slot_reservoir root
|
Deliberately distinct from Hierarchy.configs (refinement._middle_config), which
|
||||||
replaces the root wall fields by the internal bounce-back/velocity ghost setup.
|
resets the wall fields on EVERY refined level for geometry validation. Here only
|
||||||
|
level 1 of a slot_reservoir root resets them (internal bounce-back/velocity ghost
|
||||||
|
setup); other levels inherit their parent's fields. These configs generate each
|
||||||
|
level's compiled config.h, so folding the two derivations is a numerical-parity
|
||||||
|
change (config.h/PTX diff gate), not a style one.
|
||||||
"""
|
"""
|
||||||
configs = [replace(cfg)]
|
configs = [replace(cfg)]
|
||||||
for index, pair in enumerate(pairs):
|
for index, pair in enumerate(pairs):
|
||||||
@@ -119,7 +123,8 @@ class FixedPatchStepper:
|
|||||||
supported).
|
supported).
|
||||||
``refinements=[]`` is one blocked level: periodic SRT root without LES,
|
``refinements=[]`` is one blocked level: periodic SRT root without LES,
|
||||||
Jets or exchange observation; its ``level_objects[0]`` are stationary
|
Jets or exchange observation; its ``level_objects[0]`` are stationary
|
||||||
passive circles (static BFL).
|
passive circles (static BFL) in the integer-NODE frame, like Simulation
|
||||||
|
objects (see Hierarchy for frames).
|
||||||
|
|
||||||
``coarse_boundary`` is ``periodic`` (default) or ``slot_reservoir``:
|
``coarse_boundary`` is ``periodic`` (default) or ``slot_reservoir``:
|
||||||
a bottom on-site slot and top fixed-density reservoir from ``cfg``.
|
a bottom on-site slot and top fixed-density reservoir from ``cfg``.
|
||||||
@@ -131,7 +136,7 @@ class FixedPatchStepper:
|
|||||||
Periodic mode does not apply the wall/slot boundary fields in ``cfg``.
|
Periodic mode does not apply the wall/slot boundary fields in ``cfg``.
|
||||||
Outer walls remain coarse; fine halo donors must be ordinary fluid.
|
Outer walls remain coarse; fine halo donors must be ordinary fluid.
|
||||||
|
|
||||||
Bodies are fixed circles in root (L0) physical coordinates; velocities are
|
Bodies of refined levels are fixed circles in root (L0) physical coordinates; velocities are
|
||||||
in root lattice units (dx=dt=1). Independent local copies are projected at
|
in root lattice units (dx=dt=1). Independent local copies are projected at
|
||||||
fine cell centers; their complete stencil must be active and disjoint from
|
fine cell centers; their complete stencil must be active and disjoint from
|
||||||
the actual two-hop transfer footprint. These use the edge/cell-center frame
|
the actual two-hop transfer footprint. These use the edge/cell-center frame
|
||||||
@@ -258,7 +263,7 @@ class FixedPatchStepper:
|
|||||||
raise ValueError(f"level {level} blocks exceed launch limits")
|
raise ValueError(f"level {level} blocks exceed launch limits")
|
||||||
self._layouts.append(layout)
|
self._layouts.append(layout)
|
||||||
if self._channel:
|
if self._channel:
|
||||||
base = blocked_base_tables(pairs[0], self._layouts[0], np.argsort(pairs[0].coalesce_coarse))
|
base = blocked_coarse_tables(pairs[0], self._layouts[0], np.argsort(pairs[0].coalesce_coarse))
|
||||||
validate_channel_transfers(pairs[0], self._layouts[0], base[1])
|
validate_channel_transfers(pairs[0], self._layouts[0], base[1])
|
||||||
self._level_cfgs = _kernel_configs(cfg, pairs, self._wall_aware)
|
self._level_cfgs = _kernel_configs(cfg, pairs, self._wall_aware)
|
||||||
# Host boundary geometry per level in the root edge frame (Hierarchy.edge_frame).
|
# Host boundary geometry per level in the root edge frame (Hierarchy.edge_frame).
|
||||||
@@ -294,7 +299,7 @@ class FixedPatchStepper:
|
|||||||
if depth == 1 and level_objects[0]:
|
if depth == 1 and level_objects[0]:
|
||||||
tables = static_circle_tables(self._layouts[0], level_objects[0])
|
tables = static_circle_tables(self._layouts[0], level_objects[0])
|
||||||
root = _BlockedBoundary(None, tables, (), False)
|
root = _BlockedBoundary(None, tables, (), False)
|
||||||
self._level_boundaries = [root] + [
|
self._device_boundaries = [root] + [
|
||||||
_prepare_blocked_boundary(self._boundaries[level], self._layouts[level],
|
_prepare_blocked_boundary(self._boundaries[level], self._layouts[level],
|
||||||
self._level_cfgs[level], level_jets[level],
|
self._level_cfgs[level], level_jets[level],
|
||||||
_shifted_blocked=self._ddf_shifting)
|
_shifted_blocked=self._ddf_shifting)
|
||||||
@@ -310,7 +315,7 @@ class FixedPatchStepper:
|
|||||||
if self._channel:
|
if self._channel:
|
||||||
self._leaf[0] &= channel_masks((cfg.ny, cfg.nx))[0]
|
self._leaf[0] &= channel_masks((cfg.ny, cfg.nx))[0]
|
||||||
# Private constructor inputs for remap_finest.
|
# Private constructor inputs for remap_finest.
|
||||||
self._remap_cfg = replace(cfg)
|
self._root_cfg = replace(cfg)
|
||||||
self._level_blocks = level_blocks
|
self._level_blocks = level_blocks
|
||||||
self.ctx = context
|
self.ctx = context
|
||||||
self._lock = RLock()
|
self._lock = RLock()
|
||||||
@@ -366,7 +371,7 @@ class FixedPatchStepper:
|
|||||||
for index, layout in enumerate(self._layouts):
|
for index, layout in enumerate(self._layouts):
|
||||||
total += 2 * 4 * int(np.prod(layout.blocked_shape))
|
total += 2 * 4 * int(np.prod(layout.blocked_shape))
|
||||||
total += layout.halo_dst.nbytes + layout.halo_src.nbytes
|
total += layout.halo_dst.nbytes + layout.halo_src.nbytes
|
||||||
b = self._level_boundaries[index]
|
b = self._device_boundaries[index]
|
||||||
if b is not None:
|
if b is not None:
|
||||||
total += sum(np.asarray(v).nbytes for k, v in b.tables.items()
|
total += sum(np.asarray(v).nbytes for k, v in b.tables.items()
|
||||||
if k == "flags" or k in BLOCKED_BOUNDARY_DEVICE_COLUMNS)
|
if k == "flags" or k in BLOCKED_BOUNDARY_DEVICE_COLUMNS)
|
||||||
@@ -389,19 +394,19 @@ class FixedPatchStepper:
|
|||||||
grid = ((config.nx + self._block[0] - 1) // self._block[0], config.ny, layout.nblocks)
|
grid = ((config.nx + self._block[0] - 1) // self._block[0], config.ny, layout.nblocks)
|
||||||
level = _Level(shape, module, self._allocate(size), self._allocate(size),
|
level = _Level(shape, module, self._allocate(size), self._allocate(size),
|
||||||
module.get_function(name), grid, layout)
|
module.get_function(name), grid, layout)
|
||||||
level.boundary = self._level_boundaries[index]
|
level.boundary = self._device_boundaries[index]
|
||||||
level.halo_copy = module.get_function("BlockHaloCopy")
|
level.halo_copy = module.get_function("BlockHaloCopy")
|
||||||
if len(layout.halo_dst):
|
if len(layout.halo_dst):
|
||||||
level.halo_dst = self._upload(layout.halo_dst)
|
level.halo_dst = self._upload(layout.halo_dst)
|
||||||
level.halo_src = self._upload(layout.halo_src)
|
level.halo_src = self._upload(layout.halo_src)
|
||||||
if index == 0 and self._channel:
|
if index == 0 and self._channel:
|
||||||
cfg = self._remap_cfg
|
cfg = self._root_cfg
|
||||||
ptr, size = module.get_global("d_block_channel")
|
ptr, size = module.get_global("d_block_channel")
|
||||||
if size != struct.calcsize("=IIf"):
|
if size != struct.calcsize("=IIf"):
|
||||||
raise RuntimeError("Block channel host/device layout mismatch")
|
raise RuntimeError("Block channel host/device layout mismatch")
|
||||||
cuda.memcpy_htod(ptr, struct.pack("=IIf", cfg.nx, cfg.ny, cfg.velocity))
|
cuda.memcpy_htod(ptr, struct.pack("=IIf", cfg.nx, cfg.ny, cfg.velocity))
|
||||||
if index == 0 and self._wall_aware:
|
if index == 0 and self._wall_aware:
|
||||||
cfg = self._remap_cfg
|
cfg = self._root_cfg
|
||||||
ptr, size = module.get_global("d_block_wall")
|
ptr, size = module.get_global("d_block_wall")
|
||||||
if size != struct.calcsize("=IIIf"):
|
if size != struct.calcsize("=IIIf"):
|
||||||
raise RuntimeError("Block wall host/device layout mismatch")
|
raise RuntimeError("Block wall host/device layout mismatch")
|
||||||
@@ -415,11 +420,11 @@ class FixedPatchStepper:
|
|||||||
order = np.argsort(pair.coalesce_coarse)
|
order = np.argsort(pair.coalesce_coarse)
|
||||||
ghosts, children, child_kind = blocked_fine_tables(pair, child.layout, order)
|
ghosts, children, child_kind = blocked_fine_tables(pair, child.layout, order)
|
||||||
if index == 0:
|
if index == 0:
|
||||||
donors, targets, parent_kind = blocked_base_tables(pair, parent.layout, order,
|
donors, targets, parent_kind = blocked_coarse_tables(pair, parent.layout, order,
|
||||||
self._wall_aware)
|
self._wall_aware)
|
||||||
else:
|
else:
|
||||||
# Nested pairs keep inherited INNER/OUTER and sparse holes of the parent.
|
# Nested pairs keep inherited INNER/OUTER and sparse holes of the parent.
|
||||||
donors, targets, parent_kind = blocked_base_tables(
|
donors, targets, parent_kind = blocked_coarse_tables(
|
||||||
pair, parent.layout, order, composed_kind=self.hierarchy.kinds[index])
|
pair, parent.layout, order, composed_kind=self.hierarchy.kinds[index])
|
||||||
result = _Pair(child.module.get_function("BlockExplode"),
|
result = _Pair(child.module.get_function("BlockExplode"),
|
||||||
parent.module.get_function("BlockCoalesce"),
|
parent.module.get_function("BlockCoalesce"),
|
||||||
@@ -552,12 +557,12 @@ class FixedPatchStepper:
|
|||||||
if (self._wall_aware or self._channel or self.hierarchy.depth != 3
|
if (self._wall_aware or self._channel or self.hierarchy.depth != 3
|
||||||
or not isinstance(pairs[1], FixedPatchSet2D)
|
or not isinstance(pairs[1], FixedPatchSet2D)
|
||||||
or self._boundaries[1] is not None
|
or self._boundaries[1] is not None
|
||||||
or any(b is not None for b in self._level_boundaries[:2])):
|
or any(b is not None for b in self._device_boundaries[:2])):
|
||||||
raise ValueError("remap_finest requires a periodic three-level hierarchy with a "
|
raise ValueError("remap_finest requires a periodic three-level hierarchy with a "
|
||||||
"level-2 set and no level-0/1 bodies")
|
"level-2 set and no level-0/1 bodies")
|
||||||
cfg = self._remap_cfg
|
cfg = self._root_cfg
|
||||||
objects = tuple(objects)
|
objects = tuple(objects)
|
||||||
actual = self._level_boundaries[2]
|
actual = self._device_boundaries[2]
|
||||||
jets = () if actual is None else actual.jets
|
jets = () if actual is None else actual.jets
|
||||||
old = RemapTopology.from_config(cfg, pairs[0], pairs[1].bounds,
|
old = RemapTopology.from_config(cfg, pairs[0], pairs[1].bounds,
|
||||||
child_objects=objects, child_jets=jets,
|
child_objects=objects, child_jets=jets,
|
||||||
@@ -628,7 +633,7 @@ class FixedPatchStepper:
|
|||||||
if (isinstance(level, bool) or not isinstance(level, Integral)
|
if (isinstance(level, bool) or not isinstance(level, Integral)
|
||||||
or not 1 <= level < self.hierarchy.depth):
|
or not 1 <= level < self.hierarchy.depth):
|
||||||
raise ValueError("Jet level must name an existing refined level (1 .. depth-1)")
|
raise ValueError("Jet level must name an existing refined level (1 .. depth-1)")
|
||||||
boundary = self._level_boundaries[level]
|
boundary = self._device_boundaries[level]
|
||||||
jets = list(self._level_jets[level])
|
jets = list(self._level_jets[level])
|
||||||
jets[index] = jets[index].with_amplitude(amplitude) # validates first
|
jets[index] = jets[index].with_amplitude(amplitude) # validates first
|
||||||
velocity = None
|
velocity = None
|
||||||
@@ -735,7 +740,7 @@ class FixedPatchStepper:
|
|||||||
self._exchange_valid = False
|
self._exchange_valid = False
|
||||||
for _ in range(n):
|
for _ in range(n):
|
||||||
if self._observe_raw_exchange: # stream-ordered: holds the last interval only
|
if self._observe_raw_exchange: # stream-ordered: holds the last interval only
|
||||||
for b in self._level_boundaries:
|
for b in self._device_boundaries:
|
||||||
if b is not None:
|
if b is not None:
|
||||||
cuda.memset_d8_async(b.interval_integral, 0,
|
cuda.memset_d8_async(b.interval_integral, 0,
|
||||||
len(b.tables["cell"]) * 32, self._stream)
|
len(b.tables["cell"]) * 32, self._stream)
|
||||||
@@ -758,7 +763,7 @@ class FixedPatchStepper:
|
|||||||
raise RuntimeError("window exchange observation is disabled")
|
raise RuntimeError("window exchange observation is disabled")
|
||||||
self._window_valid = False
|
self._window_valid = False
|
||||||
try:
|
try:
|
||||||
for b in self._level_boundaries:
|
for b in self._device_boundaries:
|
||||||
if b is not None and b.window_integral is not None:
|
if b is not None and b.window_integral is not None:
|
||||||
cuda.memset_d8(b.window_integral, 0, len(b.tables["cell"]) * 32)
|
cuda.memset_d8(b.window_integral, 0, len(b.tables["cell"]) * 32)
|
||||||
except BaseException:
|
except BaseException:
|
||||||
@@ -771,7 +776,7 @@ class FixedPatchStepper:
|
|||||||
if (isinstance(level, bool) or not isinstance(level, Integral)
|
if (isinstance(level, bool) or not isinstance(level, Integral)
|
||||||
or not 0 <= level < len(self._levels)):
|
or not 0 <= level < len(self._levels)):
|
||||||
raise ValueError("level must name an existing level")
|
raise ValueError("level must name an existing level")
|
||||||
b = self._level_boundaries[level]
|
b = self._device_boundaries[level]
|
||||||
if (b is None or isinstance(body_id, bool) or not isinstance(body_id, Integral)
|
if (b is None or isinstance(body_id, bool) or not isinstance(body_id, Integral)
|
||||||
or not 0 <= body_id < len(b.host.objects)):
|
or not 0 <= body_id < len(b.host.objects)):
|
||||||
raise ValueError("body_id must name a body on this level")
|
raise ValueError("body_id must name a body on this level")
|
||||||
@@ -868,7 +873,7 @@ class FixedPatchStepper:
|
|||||||
self._stream.synchronize()
|
self._stream.synchronize()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
error = exc
|
error = exc
|
||||||
for b in getattr(self, "_level_boundaries", ()):
|
for b in getattr(self, "_device_boundaries", ()):
|
||||||
if b is not None:
|
if b is not None:
|
||||||
b.flags = b.device = b.kernel = None
|
b.flags = b.device = b.kernel = None
|
||||||
b.last_raw = b.interval_integral = b.accumulate = None
|
b.last_raw = b.interval_integral = b.accumulate = None
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ def blocked_kind(layout, dense_kind):
|
|||||||
kind[owner[owned]] = np.asarray(dense_kind).ravel()[owned]
|
kind[owner[owned]] = np.asarray(dense_kind).ravel()[owned]
|
||||||
return kind
|
return kind
|
||||||
|
|
||||||
def blocked_base_tables(patch, layout, order, wall_aware=False, *, composed_kind=None):
|
def blocked_coarse_tables(patch, layout, order, wall_aware=False, *, composed_kind=None):
|
||||||
"""Parent donor cells and receiver-local arrival slots, in transfer row order.
|
"""Parent donor cells and receiver-local arrival slots, in transfer row order.
|
||||||
|
|
||||||
Stage arrivals only after child reads and parent halo gather, immediately
|
Stage arrivals only after child reads and parent halo gather, immediately
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ inline float __fdiv_rn(float a,float b){return a/b;}
|
|||||||
#include "step/one_step_double.cu"
|
#include "step/one_step_double.cu"
|
||||||
#else
|
#else
|
||||||
#include "core/cell_kind.cuh"
|
#include "core/cell_kind.cuh"
|
||||||
#include "operators/block_collision.cuh"
|
|
||||||
#include "step/block_channel.cu"
|
#include "step/block_channel.cu"
|
||||||
#endif
|
#endif
|
||||||
int main(){
|
int main(){
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from CelerisLab.config import LBMConfig
|
|||||||
from CelerisLab.lbm.blocks import BlockLayout2D
|
from CelerisLab.lbm.blocks import BlockLayout2D
|
||||||
from CelerisLab.lbm.channel import channel_masks, validate_channel_config, validate_channel_transfers
|
from CelerisLab.lbm.channel import channel_masks, validate_channel_config, validate_channel_transfers
|
||||||
from CelerisLab.lbm.refinement import FixedPatch2D
|
from CelerisLab.lbm.refinement import FixedPatch2D
|
||||||
from CelerisLab.lbm.refinement_tables import blocked_base_tables
|
from CelerisLab.lbm.refinement_tables import blocked_coarse_tables
|
||||||
from .test_numerical_operators import compile_run
|
from .test_numerical_operators import compile_run
|
||||||
|
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ def transfer(bounds, block=(4, 6)):
|
|||||||
cfg = LBMConfig(nx=24, ny=20, viscosity=.1)
|
cfg = LBMConfig(nx=24, ny=20, viscosity=.1)
|
||||||
pair = FixedPatch2D.from_config(cfg, bounds)
|
pair = FixedPatch2D.from_config(cfg, bounds)
|
||||||
layout = BlockLayout2D.build(20, 24, *block)
|
layout = BlockLayout2D.build(20, 24, *block)
|
||||||
targets = blocked_base_tables(pair, layout, np.argsort(pair.coalesce_coarse))[1]
|
targets = blocked_coarse_tables(pair, layout, np.argsort(pair.coalesce_coarse))[1]
|
||||||
return pair, layout, targets
|
return pair, layout, targets
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -102,8 +102,8 @@ def test_simulation_before_initialize_commits_host_only(monkeypatch):
|
|||||||
|
|
||||||
|
|
||||||
def stepper_state(run):
|
def stepper_state(run):
|
||||||
boundary = run._level_boundaries[2]
|
boundary = run._device_boundaries[2]
|
||||||
return boundary.tables["surface_velocity"].copy(), boundary.jets, run.jets[2]
|
return boundary.tables["surface_velocity"].copy(), boundary.jets, run._level_jets[2]
|
||||||
|
|
||||||
|
|
||||||
def test_stepper_invalid_input_writes_nothing_and_stays_ready():
|
def test_stepper_invalid_input_writes_nothing_and_stays_ready():
|
||||||
@@ -116,11 +116,11 @@ def test_stepper_invalid_input_writes_nothing_and_stays_ready():
|
|||||||
with pytest.raises(error):
|
with pytest.raises(error):
|
||||||
run.set_jet_amplitude(*args, **kw)
|
run.set_jet_amplitude(*args, **kw)
|
||||||
module.cuda.memcpy_htod.assert_not_called()
|
module.cuda.memcpy_htod.assert_not_called()
|
||||||
np.testing.assert_array_equal(run._level_boundaries[2].tables["surface_velocity"], velocity)
|
np.testing.assert_array_equal(run._device_boundaries[2].tables["surface_velocity"], velocity)
|
||||||
assert (run._level_boundaries[2].jets, run.jets[2]) == (jets, level_jets)
|
assert (run._device_boundaries[2].jets, run._level_jets[2]) == (jets, level_jets)
|
||||||
assert run._status == "ready"
|
assert run._status == "ready"
|
||||||
run.set_jet_amplitude(0, .017, level=2)
|
run.set_jet_amplitude(0, .017, level=2)
|
||||||
assert run.jets[2][0].amplitude == .017
|
assert run._level_jets[2][0].amplitude == .017
|
||||||
finally:
|
finally:
|
||||||
run.close()
|
run.close()
|
||||||
|
|
||||||
@@ -136,8 +136,8 @@ def test_stepper_device_failure_keeps_host_and_blocks_reuse(where):
|
|||||||
run._stream.synchronize.side_effect = RuntimeError("upload failed")
|
run._stream.synchronize.side_effect = RuntimeError("upload failed")
|
||||||
with pytest.raises(RuntimeError, match="upload failed"):
|
with pytest.raises(RuntimeError, match="upload failed"):
|
||||||
run.set_jet_amplitude(0, .017, level=2)
|
run.set_jet_amplitude(0, .017, level=2)
|
||||||
np.testing.assert_array_equal(run._level_boundaries[2].tables["surface_velocity"], velocity)
|
np.testing.assert_array_equal(run._device_boundaries[2].tables["surface_velocity"], velocity)
|
||||||
assert (run._level_boundaries[2].jets, run.jets[2]) == (jets, level_jets)
|
assert (run._device_boundaries[2].jets, run._level_jets[2]) == (jets, level_jets)
|
||||||
assert run._status == "failed"
|
assert run._status == "failed"
|
||||||
module.cuda.memcpy_htod.side_effect = None
|
module.cuda.memcpy_htod.side_effect = None
|
||||||
run._stream.synchronize.side_effect = None
|
run._stream.synchronize.side_effect = None
|
||||||
|
|||||||
@@ -94,13 +94,13 @@ class MultiplePinnedRemap(unittest.TestCase):
|
|||||||
run.set_jet_amplitude(0, .017, level=2)
|
run.set_jet_amplitude(0, .017, level=2)
|
||||||
run.set_jet_amplitude(1, -.011, level=2)
|
run.set_jet_amplitude(1, -.011, level=2)
|
||||||
before = run.download()
|
before = run.download()
|
||||||
clone = run.remap_finest((PINS+MOVED+ADDED)[::-1], objects=bodies)
|
clone = run.remap_child((PINS+MOVED+ADDED)[::-1], child_objects=bodies)
|
||||||
try:
|
try:
|
||||||
self.assertEqual(tuple(j.amplitude for j in clone._level_boundaries[2].jets), (.017, -.011))
|
self.assertEqual(tuple(j.amplitude for j in clone._boundaries[2].jets), (.017, -.011))
|
||||||
np.testing.assert_array_equal(clone._level_boundaries[2].tables['surface_velocity'],
|
np.testing.assert_array_equal(clone._boundaries[2].tables['surface_velocity'],
|
||||||
run._level_boundaries[2].tables['surface_velocity'])
|
run._boundaries[2].tables['surface_velocity'])
|
||||||
clone.set_jet_amplitude(1, .005, level=2)
|
clone.set_jet_amplitude(1, .005, level=2)
|
||||||
self.assertEqual(run._level_boundaries[2].jets[1].amplitude, -.011)
|
self.assertEqual(run._boundaries[2].jets[1].amplitude, -.011)
|
||||||
clone.step()
|
clone.step()
|
||||||
self.assertEqual((run.step_count, clone.step_count), (7, 8))
|
self.assertEqual((run.step_count, clone.step_count), (7, 8))
|
||||||
for got, want in zip(run.download(), before):
|
for got, want in zip(run.download(), before):
|
||||||
@@ -116,7 +116,7 @@ class MultiplePinnedRemap(unittest.TestCase):
|
|||||||
start = len(allocations)
|
start = len(allocations)
|
||||||
module.cuda.memcpy_dtoh.reset_mock()
|
module.cuda.memcpy_dtoh.reset_mock()
|
||||||
with self.assertRaisesRegex(ValueError, 'boundary'):
|
with self.assertRaisesRegex(ValueError, 'boundary'):
|
||||||
run.remap_finest(PINS+FLUID, objects=changed)
|
run.remap_child(PINS+FLUID, child_objects=changed)
|
||||||
self.assertEqual(len(allocations), start)
|
self.assertEqual(len(allocations), start)
|
||||||
module.cuda.memcpy_dtoh.assert_not_called()
|
module.cuda.memcpy_dtoh.assert_not_called()
|
||||||
with self.assertRaisesRegex(ValueError, 'pinned'):
|
with self.assertRaisesRegex(ValueError, 'pinned'):
|
||||||
@@ -128,7 +128,7 @@ class MultiplePinnedRemap(unittest.TestCase):
|
|||||||
cases = [(PINS+FLUID, bodies[:1]), (PINS+FLUID, bodies[::-1]),
|
cases = [(PINS+FLUID, bodies[:1]), (PINS+FLUID, bodies[::-1]),
|
||||||
(PINS+FLUID, (bodies[0], body(center=(32.1, 10.)))),
|
(PINS+FLUID, (bodies[0], body(center=(32.1, 10.)))),
|
||||||
(PINS+FLUID, (bodies[0], body(SurfaceActuation(0., .7, 0.), (32., 10.)))),
|
(PINS+FLUID, (bodies[0], body(SurfaceActuation(0., .7, 0.), (32., 10.)))),
|
||||||
(PINS+FLUID, run.boundaries[2].objects)]
|
(PINS+FLUID, run.child_boundary.objects)]
|
||||||
for index in range(2):
|
for index in range(2):
|
||||||
changed = list(PINS+FLUID)
|
changed = list(PINS+FLUID)
|
||||||
x0, y0, x1, y1 = changed[index]
|
x0, y0, x1, y1 = changed[index]
|
||||||
@@ -141,7 +141,7 @@ class MultiplePinnedRemap(unittest.TestCase):
|
|||||||
run._stream.synchronize.reset_mock()
|
run._stream.synchronize.reset_mock()
|
||||||
for rects, supplied in cases:
|
for rects, supplied in cases:
|
||||||
with self.subTest(rects=rects), self.assertRaises(ValueError):
|
with self.subTest(rects=rects), self.assertRaises(ValueError):
|
||||||
run.remap_finest(rects, objects=supplied)
|
run.remap_child(rects, child_objects=supplied)
|
||||||
self.assertEqual(len(allocations), start)
|
self.assertEqual(len(allocations), start)
|
||||||
module.cuda.memcpy_dtoh.assert_not_called()
|
module.cuda.memcpy_dtoh.assert_not_called()
|
||||||
run._stream.synchronize.assert_not_called()
|
run._stream.synchronize.assert_not_called()
|
||||||
|
|||||||
@@ -61,11 +61,11 @@ class PinnedBodyRemap(unittest.TestCase):
|
|||||||
run,module,allocations,storage,objects=ready()
|
run,module,allocations,storage,objects=ready()
|
||||||
run.set_jet_amplitude(0,.017,level=2)
|
run.set_jet_amplitude(0,.017,level=2)
|
||||||
before=run.download()
|
before=run.download()
|
||||||
clone=run.remap_finest(B,objects=objects)
|
clone=run.remap_child(B,child_objects=objects)
|
||||||
self.assertEqual(clone._level_boundaries[2].jets[0].amplitude,.017)
|
self.assertEqual(clone._boundaries[2].jets[0].amplitude,.017)
|
||||||
np.testing.assert_array_equal(clone._level_boundaries[2].tables['surface_velocity'],
|
np.testing.assert_array_equal(clone._boundaries[2].tables['surface_velocity'],
|
||||||
run._level_boundaries[2].tables['surface_velocity'])
|
run._boundaries[2].tables['surface_velocity'])
|
||||||
self.assertTrue(same_physical_boundary(run.boundaries[2],clone.boundaries[2]))
|
self.assertTrue(same_physical_boundary(run.child_boundary,clone.child_boundary))
|
||||||
clone.step()
|
clone.step()
|
||||||
self.assertEqual((run.step_count,clone.step_count),(7,8))
|
self.assertEqual((run.step_count,clone.step_count),(7,8))
|
||||||
for got,want in zip(run.download(),before): np.testing.assert_array_equal(got,want)
|
for got,want in zip(run.download(),before): np.testing.assert_array_equal(got,want)
|
||||||
@@ -73,22 +73,22 @@ class PinnedBodyRemap(unittest.TestCase):
|
|||||||
|
|
||||||
def test_static_body_without_actuation(self):
|
def test_static_body_without_actuation(self):
|
||||||
run,module,allocations,storage,objects=ready(jets=())
|
run,module,allocations,storage,objects=ready(jets=())
|
||||||
clone=run.remap_finest(A,objects=objects)
|
clone=run.remap_child(A,child_objects=objects)
|
||||||
self.assertFalse(clone._level_boundaries[2].actuated)
|
self.assertFalse(clone._boundaries[2].actuated)
|
||||||
self.assertNotIn('surface_velocity',clone._level_boundaries[2].tables)
|
self.assertNotIn('surface_velocity',clone._boundaries[2].tables)
|
||||||
for a,b in zip(run.download(),clone.download()): np.testing.assert_array_equal(a,b)
|
for a,b in zip(run.download(),clone.download()): np.testing.assert_array_equal(a,b)
|
||||||
clone.close();run.close()
|
clone.close();run.close()
|
||||||
|
|
||||||
def test_reject_before_device_reads_or_allocation(self):
|
def test_reject_before_device_reads_or_allocation(self):
|
||||||
run,module,allocations,storage,objects=ready()
|
run,module,allocations,storage,objects=ready()
|
||||||
start=len(allocations)
|
start=len(allocations)
|
||||||
cases=[(B,()),(B,run.boundaries[2].objects),(B,(body(center=(12.1,14.)),)),
|
cases=[(B,()),(B,run.child_boundary.objects),(B,(body(center=(12.1,14.)),)),
|
||||||
(((24,16,36,40),B[1]),objects),(((16,28,32,40),B[1]),objects),
|
(((24,16,36,40),B[1]),objects),(((16,28,32,40),B[1]),objects),
|
||||||
((B[1],),objects)]
|
((B[1],),objects)]
|
||||||
module.cuda.memcpy_dtoh.reset_mock()
|
module.cuda.memcpy_dtoh.reset_mock()
|
||||||
for rects,objs in cases:
|
for rects,objs in cases:
|
||||||
with self.subTest(rects=rects),self.assertRaises(ValueError):
|
with self.subTest(rects=rects),self.assertRaises(ValueError):
|
||||||
run.remap_finest(rects,objects=objs)
|
run.remap_child(rects,child_objects=objs)
|
||||||
self.assertEqual(len(allocations),start)
|
self.assertEqual(len(allocations),start)
|
||||||
module.cuda.memcpy_dtoh.assert_not_called()
|
module.cuda.memcpy_dtoh.assert_not_called()
|
||||||
self.assertEqual((run._status,run.step_count),('ready',7))
|
self.assertEqual((run._status,run.step_count),('ready',7))
|
||||||
@@ -100,9 +100,9 @@ class PinnedBodyRemap(unittest.TestCase):
|
|||||||
(body(SurfaceActuation(0.,.7,speed)),),())
|
(body(SurfaceActuation(0.,.7,speed)),),())
|
||||||
for spec in (SurfaceActuation(0.,.7,.003),SurfaceActuation(2.,.7,speed)):
|
for spec in (SurfaceActuation(0.,.7,.003),SurfaceActuation(2.,.7,speed)):
|
||||||
module.cuda.memcpy_dtoh.reset_mock()
|
module.cuda.memcpy_dtoh.reset_mock()
|
||||||
with self.assertRaises(ValueError): run.remap_finest(B,objects=(body(spec),))
|
with self.assertRaises(ValueError): run.remap_child(B,child_objects=(body(spec),))
|
||||||
module.cuda.memcpy_dtoh.assert_not_called()
|
module.cuda.memcpy_dtoh.assert_not_called()
|
||||||
clone=run.remap_finest(B,objects=objects)
|
clone=run.remap_child(B,child_objects=objects)
|
||||||
clone.close();run.close()
|
clone.close();run.close()
|
||||||
|
|
||||||
def test_allocation_failure_keeps_old_ready_and_count(self):
|
def test_allocation_failure_keeps_old_ready_and_count(self):
|
||||||
@@ -117,7 +117,7 @@ class PinnedBodyRemap(unittest.TestCase):
|
|||||||
return allocate(size)
|
return allocate(size)
|
||||||
with patch.object(module.cuda,'mem_alloc',side_effect=fail):
|
with patch.object(module.cuda,'mem_alloc',side_effect=fail):
|
||||||
with self.assertRaisesRegex(RuntimeError,'allocation'):
|
with self.assertRaisesRegex(RuntimeError,'allocation'):
|
||||||
run.remap_finest(B,objects=objects)
|
run.remap_child(B,child_objects=objects)
|
||||||
self.assertEqual((run._status,run.step_count),('ready',7))
|
self.assertEqual((run._status,run.step_count),('ready',7))
|
||||||
for got,want in zip(run.download(),before): np.testing.assert_array_equal(got,want)
|
for got,want in zip(run.download(),before): np.testing.assert_array_equal(got,want)
|
||||||
for a in saved: a.free.assert_not_called()
|
for a in saved: a.free.assert_not_called()
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ class ProjectedPairDispatch(unittest.TestCase):
|
|||||||
run, host, _, _, storage = backend(refinements=FOUR[:2], explosion=explosion)
|
run, host, _, _, storage = backend(refinements=FOUR[:2], explosion=explosion)
|
||||||
run._step_count = 7
|
run._step_count = 7
|
||||||
old = run.download()
|
old = run.download()
|
||||||
clone = run.remap_finest(FOUR[1])
|
clone = run.remap_child(FOUR[1])
|
||||||
self.assertEqual(clone._explosion, explosion)
|
self.assertEqual(clone._explosion, explosion)
|
||||||
self.assertEqual(clone.step_count, 7)
|
self.assertEqual(clone.step_count, 7)
|
||||||
for pair in clone._pairs:
|
for pair in clone._pairs:
|
||||||
@@ -182,7 +182,7 @@ class ProjectedPairDispatch(unittest.TestCase):
|
|||||||
refinements=FOUR[:2], explosion='projected_linear')
|
refinements=FOUR[:2], explosion='projected_linear')
|
||||||
run._step_count = 3
|
run._step_count = 3
|
||||||
before = run.download()
|
before = run.download()
|
||||||
clone = run.remap_finest(changed)
|
clone = run.remap_child(changed)
|
||||||
self.assertEqual(clone._explosion, 'projected_linear')
|
self.assertEqual(clone._explosion, 'projected_linear')
|
||||||
self.assertEqual(clone.hierarchy.pairs[1].bounds, tuple(changed))
|
self.assertEqual(clone.hierarchy.pairs[1].bounds, tuple(changed))
|
||||||
self.assertNotEqual(clone._pairs[1].n_ghosts, run._pairs[1].n_ghosts)
|
self.assertNotEqual(clone._pairs[1].n_ghosts, run._pairs[1].n_ghosts)
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ class RawExchange(unittest.TestCase):
|
|||||||
|
|
||||||
def test_four_level_schedule_publication_and_fixed_storage(self):
|
def test_four_level_schedule_publication_and_fixed_storage(self):
|
||||||
run, host, calls, arrays = backend()
|
run, host, calls, arrays = backend()
|
||||||
b = run._level_boundaries[3]
|
b = run._device_boundaries[3]
|
||||||
rows = len(b.tables['cell'])
|
rows = len(b.tables['cell'])
|
||||||
self.assertEqual(b.last_raw.size, rows * 16)
|
self.assertEqual(b.last_raw.size, rows * 16)
|
||||||
self.assertEqual(b.interval_integral.size, rows * 32)
|
self.assertEqual(b.interval_integral.size, rows * 32)
|
||||||
@@ -128,7 +128,7 @@ class RawExchange(unittest.TestCase):
|
|||||||
|
|
||||||
def test_disabled_has_nulls_and_no_observation_allocation_or_launch(self):
|
def test_disabled_has_nulls_and_no_observation_allocation_or_launch(self):
|
||||||
run, host, calls, _ = backend(False)
|
run, host, calls, _ = backend(False)
|
||||||
b = run._level_boundaries[3]
|
b = run._device_boundaries[3]
|
||||||
self.assertIs(b.last_raw, b.interval_integral)
|
self.assertIs(b.last_raw, b.interval_integral)
|
||||||
self.assertIs(b.interval_integral, None)
|
self.assertIs(b.interval_integral, None)
|
||||||
run.step()
|
run.step()
|
||||||
|
|||||||
@@ -524,11 +524,6 @@ def test_public_jet_reversal_boundary_and_windows():
|
|||||||
assert r.jet_interval(handle, "reverse", switch + 80, 80, diameter) == (80, None)
|
assert r.jet_interval(handle, "reverse", switch + 80, 80, diameter) == (80, None)
|
||||||
assert r.jet_interval(handle, "fixed", switch, 80, diameter) == (80, None)
|
assert r.jet_interval(handle, "fixed", switch, 80, diameter) == (80, None)
|
||||||
assert not calls
|
assert not calls
|
||||||
refined_calls = []
|
|
||||||
refined = SimpleNamespace(set_jet_amplitude=lambda i, a, *, level: refined_calls.append((i, a, level)))
|
|
||||||
n, event = r.jet_interval(refined, "reverse", switch, 80, diameter, level=1)
|
|
||||||
assert n == 80 and event["step"] == switch and event["tstar"] == 300
|
|
||||||
assert refined_calls == [(0, -.001, 1), (1, .001, 1)]
|
|
||||||
rows = [dict(tstar=t, tstar_force=t - .5, cd=2.0, cl=-.1) for t in range(251, 306)]
|
rows = [dict(tstar=t, tstar_force=t - .5, cd=2.0, cl=-.1) for t in range(251, 306)]
|
||||||
windows = r.jet_diagnostics(rows, "reverse")
|
windows = r.jet_diagnostics(rows, "reverse")
|
||||||
assert len(windows) == 6 and all(w["complete"] for w in windows)
|
assert len(windows) == 6 and all(w["complete"] for w in windows)
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ class FixedPatchSetTests(unittest.TestCase):
|
|||||||
self.assertEqual((fy, fx), (32, 48))
|
self.assertEqual((fy, fx), (32, 48))
|
||||||
rows = cells = 0
|
rows = cells = 0
|
||||||
for p in s.patches:
|
for p in s.patches:
|
||||||
y0, x0 = p.fine_level_offset
|
y0, x0 = p.fine_level_origin
|
||||||
ly, lx = p.fine_shape
|
ly, lx = p.fine_shape
|
||||||
np.testing.assert_array_equal(s.fine_kind[y0:y0 + ly, x0:x0 + lx], p.fine_kind)
|
np.testing.assert_array_equal(s.fine_kind[y0:y0 + ly, x0:x0 + lx], p.fine_kind)
|
||||||
gy, gx = np.divmod(p.ghost_fine.astype(np.int64), lx)
|
gy, gx = np.divmod(p.ghost_fine.astype(np.int64), lx)
|
||||||
@@ -224,7 +224,7 @@ class FixedPatchSetTests(unittest.TestCase):
|
|||||||
layout = s.fine_layout((4, 4))
|
layout = s.fine_layout((4, 4))
|
||||||
support = np.zeros(s.fine_shape, bool)
|
support = np.zeros(s.fine_shape, bool)
|
||||||
for p in s.patches:
|
for p in s.patches:
|
||||||
y0, x0 = p.fine_level_offset
|
y0, x0 = p.fine_level_origin
|
||||||
support[y0:y0 + p.fine_shape[0], x0:x0 + p.fine_shape[1]] = True
|
support[y0:y0 + p.fine_shape[0], x0:x0 + p.fine_shape[1]] = True
|
||||||
np.testing.assert_array_equal(layout.owned, support)
|
np.testing.assert_array_equal(layout.owned, support)
|
||||||
self.assertTrue(layout.owned.ravel()[s.ghost_fine.astype(np.intp)].all())
|
self.assertTrue(layout.owned.ravel()[s.ghost_fine.astype(np.intp)].all())
|
||||||
|
|||||||
@@ -31,10 +31,7 @@ class RefinementMRTHost(unittest.TestCase):
|
|||||||
"compute_feq(rho, ux, uy, feq);",
|
"compute_feq(rho, ux, uy, feq);",
|
||||||
"collide_srt_no_force(f, feq, d_params.omega);"):
|
"collide_srt_no_force(f, feq, d_params.omega);"):
|
||||||
self.assertIn(text, block_collision)
|
self.assertIn(text, block_collision)
|
||||||
self.assertIn("collide_srt(f, feq, forcing, omega);", block_collision)
|
self.assertIn("collide_srt(f, feq, forcing, omega);", channel)
|
||||||
self.assertIn("#elif BLOCK_ROOT_BC == 2", block_collision)
|
|
||||||
self.assertEqual(channel.count("block_collide(f);"), 1)
|
|
||||||
self.assertNotIn("collide_mrt_unforced(f);", channel)
|
|
||||||
for source in (blocks,):
|
for source in (blocks,):
|
||||||
self.assertIn("(COLLISION_MODEL != 0 && COLLISION_MODEL != 2)", source)
|
self.assertIn("(COLLISION_MODEL != 0 && COLLISION_MODEL != 2)", source)
|
||||||
self.assertIn("USE_LES", source)
|
self.assertIn("USE_LES", source)
|
||||||
@@ -43,7 +40,7 @@ class RefinementMRTHost(unittest.TestCase):
|
|||||||
# One shared unforced MRT body; each blocked/patch path calls it exactly once.
|
# One shared unforced MRT body; each blocked/patch path calls it exactly once.
|
||||||
mrt = (KERNELS / "operators/collision_mrt.cuh").read_text()
|
mrt = (KERNELS / "operators/collision_mrt.cuh").read_text()
|
||||||
self.assertEqual(mrt.count("void collide_mrt_unforced("), 1)
|
self.assertEqual(mrt.count("void collide_mrt_unforced("), 1)
|
||||||
for source in (blocks + "\n" + block_collision, channel + "\n" + block_collision):
|
for source in (blocks + "\n" + block_collision, channel):
|
||||||
self.assertEqual(source.count("collide_mrt_unforced(f);"), 1)
|
self.assertEqual(source.count("collide_mrt_unforced(f);"), 1)
|
||||||
self.assertNotIn("collide_mrt_no_force(", source)
|
self.assertNotIn("collide_mrt_no_force(", source)
|
||||||
self.assertNotIn("compute_rho_u(f, rho, ux, uy);\n collide_mrt", source)
|
self.assertNotIn("compute_rho_u(f, rho, ux, uy);\n collide_mrt", source)
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ class Gate(unittest.TestCase):
|
|||||||
def test_sparse_parent_tables_preserve_inherited_kinds(self):
|
def test_sparse_parent_tables_preserve_inherited_kinds(self):
|
||||||
child,kind=r.nest_patch_set(CFG,self.parent,CHILDREN)
|
child,kind=r.nest_patch_set(CFG,self.parent,CHILDREN)
|
||||||
layout=self.parent.fine_layout((4,4));order=np.argsort(child.coalesce_coarse)
|
layout=self.parent.fine_layout((4,4));order=np.argsort(child.coalesce_coarse)
|
||||||
donors,targets,bkind=host.blocked_base_tables(child,layout,order,composed_kind=kind)
|
donors,targets,bkind=host.blocked_coarse_tables(child,layout,order,composed_kind=kind)
|
||||||
self.assertTrue((donors < layout.hole).all())
|
self.assertTrue((donors < layout.hole).all())
|
||||||
self.assertTrue((targets < np.prod(layout.blocked_shape)).all())
|
self.assertTrue((targets < np.prod(layout.blocked_shape)).all())
|
||||||
np.testing.assert_array_equal(bkind[layout.owner[layout.owned]],kind[layout.owned])
|
np.testing.assert_array_equal(bkind[layout.owner[layout.owned]],kind[layout.owned])
|
||||||
@@ -158,10 +158,10 @@ class Gate(unittest.TestCase):
|
|||||||
hole=int(np.flatnonzero(~layout.owned.ravel())[0])
|
hole=int(np.flatnonzero(~layout.owned.ravel())[0])
|
||||||
bad=child.ghost_coarse.copy();bad[0]=hole
|
bad=child.ghost_coarse.copy();bad[0]=hole
|
||||||
with self.assertRaisesRegex(ValueError,'owned uncovered'):
|
with self.assertRaisesRegex(ValueError,'owned uncovered'):
|
||||||
host.blocked_base_tables(replace(child,ghost_coarse=bad),layout,order,composed_kind=kind)
|
host.blocked_coarse_tables(replace(child,ghost_coarse=bad),layout,order,composed_kind=kind)
|
||||||
bad=child.coalesce_coarse.copy();bad[0]=hole
|
bad=child.coalesce_coarse.copy();bad[0]=hole
|
||||||
with self.assertRaisesRegex(ValueError,'owned uncovered'):
|
with self.assertRaisesRegex(ValueError,'owned uncovered'):
|
||||||
host.blocked_base_tables(replace(child,coalesce_coarse=bad),layout,order,composed_kind=kind)
|
host.blocked_coarse_tables(replace(child,coalesce_coarse=bad),layout,order,composed_kind=kind)
|
||||||
|
|
||||||
|
|
||||||
def bodies_and_jets(speed=.002):
|
def bodies_and_jets(speed=.002):
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class RuntimeChildRemap(unittest.TestCase):
|
|||||||
saved = {key: value.copy() for key, value in storage.items()}
|
saved = {key: value.copy() for key, value in storage.items()}
|
||||||
old_allocations = list(allocations)
|
old_allocations = list(allocations)
|
||||||
module.cuda.memcpy_dtoh.reset_mock()
|
module.cuda.memcpy_dtoh.reset_mock()
|
||||||
clone = run.remap_finest(A)
|
clone = run.remap_child(A)
|
||||||
self.assertEqual(module.cuda.memcpy_dtoh.call_count, 3)
|
self.assertEqual(module.cuda.memcpy_dtoh.call_count, 3)
|
||||||
for a, b in zip(clone.download(), before):
|
for a, b in zip(clone.download(), before):
|
||||||
np.testing.assert_array_equal(a, b)
|
np.testing.assert_array_equal(a, b)
|
||||||
@@ -48,7 +48,7 @@ class RuntimeChildRemap(unittest.TestCase):
|
|||||||
run, module, allocations, storage = ready_backend()
|
run, module, allocations, storage = ready_backend()
|
||||||
before = run.download()
|
before = run.download()
|
||||||
expected = remap(topology(A), topology(B), before).g
|
expected = remap(topology(A), topology(B), before).g
|
||||||
clone = run.remap_finest(B)
|
clone = run.remap_child(B)
|
||||||
for level, want, got in zip(clone._levels, expected, clone.download()):
|
for level, want, got in zip(clone._levels, expected, clone.download()):
|
||||||
np.testing.assert_array_equal(got, want)
|
np.testing.assert_array_equal(got, want)
|
||||||
for ptr in (level.current, level.spare):
|
for ptr in (level.current, level.spare):
|
||||||
@@ -65,17 +65,17 @@ class RuntimeChildRemap(unittest.TestCase):
|
|||||||
module.cuda.memcpy_dtoh.reset_mock()
|
module.cuda.memcpy_dtoh.reset_mock()
|
||||||
for status in ('new', 'failed', 'closed', 'advancing'):
|
for status in ('new', 'failed', 'closed', 'advancing'):
|
||||||
run._status = status
|
run._status = status
|
||||||
with self.assertRaises(RuntimeError): run.remap_finest(B)
|
with self.assertRaises(RuntimeError): run.remap_child(B)
|
||||||
run._status = 'ready'
|
run._status = 'ready'
|
||||||
for bad in ([], [(40,16,48,40)], [(16,16,32,24),(16,27,32,55)]):
|
for bad in ([], [(40,16,48,40)], [(16,16,32,24),(16,27,32,55)]):
|
||||||
with self.assertRaises(ValueError): run.remap_finest(bad)
|
with self.assertRaises(ValueError): run.remap_child(bad)
|
||||||
hosts, pairs = run._boundaries, run.hierarchy.pairs
|
hosts, pairs = run._boundary_hosts, run.hierarchy.pairs
|
||||||
for attribute, value in (('_wall_aware', True),
|
for attribute, value in (('_wall_aware', True),
|
||||||
('_boundaries', [*hosts[:2], object()]), # child boundary
|
('_boundary_hosts', [*hosts[:2], object()]), # child boundary
|
||||||
('_boundaries', [hosts[0], object(), hosts[2]]), # fine boundary
|
('_boundary_hosts', [hosts[0], object(), hosts[2]]), # fine boundary
|
||||||
('hierarchy', replace(run.hierarchy, pairs=(pairs[0], None)))):
|
('hierarchy', replace(run.hierarchy, pairs=(pairs[0], None)))):
|
||||||
with patch.object(run, attribute, value):
|
with patch.object(run, attribute, value):
|
||||||
with self.assertRaises(ValueError): run.remap_finest(B)
|
with self.assertRaises(ValueError): run.remap_child(B)
|
||||||
self.assertEqual(len(allocations), start)
|
self.assertEqual(len(allocations), start)
|
||||||
module.cuda.memcpy_dtoh.assert_not_called()
|
module.cuda.memcpy_dtoh.assert_not_called()
|
||||||
run.close()
|
run.close()
|
||||||
@@ -102,7 +102,7 @@ class RuntimeChildRemap(unittest.TestCase):
|
|||||||
initialize(candidate, *arrays)
|
initialize(candidate, *arrays)
|
||||||
operation = patch.object(type(run), 'initialize', fail)
|
operation = patch.object(type(run), 'initialize', fail)
|
||||||
with operation, self.assertRaisesRegex(RuntimeError, 'candidate'):
|
with operation, self.assertRaisesRegex(RuntimeError, 'candidate'):
|
||||||
run.remap_finest(B)
|
run.remap_child(B)
|
||||||
self.assertEqual((run._status, run.step_count), ('ready', 7))
|
self.assertEqual((run._status, run.step_count), ('ready', 7))
|
||||||
for a, b in zip(run.download(), before): np.testing.assert_array_equal(a, b)
|
for a, b in zip(run.download(), before): np.testing.assert_array_equal(a, b)
|
||||||
for allocation in old_allocations: allocation.free.assert_not_called()
|
for allocation in old_allocations: allocation.free.assert_not_called()
|
||||||
@@ -116,7 +116,7 @@ class RuntimeChildRemap(unittest.TestCase):
|
|||||||
target = run._stream.synchronize if operation == 'sync' else module.cuda.memcpy_dtoh
|
target = run._stream.synchronize if operation == 'sync' else module.cuda.memcpy_dtoh
|
||||||
target.side_effect = RuntimeError('old device failure')
|
target.side_effect = RuntimeError('old device failure')
|
||||||
with self.assertRaisesRegex(RuntimeError, 'old device'):
|
with self.assertRaisesRegex(RuntimeError, 'old device'):
|
||||||
run.remap_finest(B)
|
run.remap_child(B)
|
||||||
self.assertEqual(run._status, 'failed')
|
self.assertEqual(run._status, 'failed')
|
||||||
self.assertEqual(len(allocations), start)
|
self.assertEqual(len(allocations), start)
|
||||||
target.side_effect = None
|
target.side_effect = None
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class ShiftedJets(unittest.TestCase):
|
|||||||
def test_switch_upload_only_then_raw_remap_keeps_live_velocity(self):
|
def test_switch_upload_only_then_raw_remap_keeps_live_velocity(self):
|
||||||
run,module,allocations,storage,objects=ready();clone=None
|
run,module,allocations,storage,objects=ready();clone=None
|
||||||
try:
|
try:
|
||||||
boundary=run._level_boundaries[2]
|
boundary=run._device_boundaries[2]
|
||||||
before=run._download_raw()
|
before=run._download_raw()
|
||||||
original=boundary.tables['surface_velocity'].copy()
|
original=boundary.tables['surface_velocity'].copy()
|
||||||
module.cuda.memcpy_htod.reset_mock()
|
module.cuda.memcpy_htod.reset_mock()
|
||||||
@@ -77,13 +77,13 @@ class ShiftedJets(unittest.TestCase):
|
|||||||
np.testing.assert_array_equal(boundary.tables['surface_velocity'],-original)
|
np.testing.assert_array_equal(boundary.tables['surface_velocity'],-original)
|
||||||
self.assertEqual(run.step_count,7)
|
self.assertEqual(run.step_count,7)
|
||||||
for a,b in zip(before.values,run._download_raw().values):self.assertEqual(a.tobytes(),b.tobytes())
|
for a,b in zip(before.values,run._download_raw().values):self.assertEqual(a.tobytes(),b.tobytes())
|
||||||
clone=run.remap_finest(B,objects=objects)
|
clone=run.remap_child(B,child_objects=objects)
|
||||||
self.assertEqual(clone.jets[2][0].amplitude,-jet().amplitude)
|
self.assertEqual(clone._level_jets[2][0].amplitude,-jet().amplitude)
|
||||||
np.testing.assert_array_equal(clone._level_boundaries[2].tables['surface_velocity'],
|
np.testing.assert_array_equal(clone._device_boundaries[2].tables['surface_velocity'],
|
||||||
boundary.tables['surface_velocity'])
|
boundary.tables['surface_velocity'])
|
||||||
self.assertEqual(clone.step_count,7)
|
self.assertEqual(clone.step_count,7)
|
||||||
saved=run._download_raw()
|
saved=run._download_raw()
|
||||||
with self.assertRaises(ValueError):run.remap_finest(B,objects=(body(center=(12.1,14.)),))
|
with self.assertRaises(ValueError):run.remap_child(B,child_objects=(body(center=(12.1,14.)),))
|
||||||
for a,b in zip(saved.values,run._download_raw().values):self.assertEqual(a.tobytes(),b.tobytes())
|
for a,b in zip(saved.values,run._download_raw().values):self.assertEqual(a.tobytes(),b.tobytes())
|
||||||
finally:
|
finally:
|
||||||
if clone is not None:clone.close()
|
if clone is not None:clone.close()
|
||||||
@@ -92,12 +92,12 @@ class ShiftedJets(unittest.TestCase):
|
|||||||
def test_invalid_amplitude_does_not_upload_or_commit(self):
|
def test_invalid_amplitude_does_not_upload_or_commit(self):
|
||||||
run,module,_,_,_=ready()
|
run,module,_,_,_=ready()
|
||||||
try:
|
try:
|
||||||
before=run._level_boundaries[2].tables['surface_velocity'].copy()
|
before=run._device_boundaries[2].tables['surface_velocity'].copy()
|
||||||
module.cuda.memcpy_htod.reset_mock()
|
module.cuda.memcpy_htod.reset_mock()
|
||||||
for amplitude in (float('nan'),float('inf'),1.):
|
for amplitude in (float('nan'),float('inf'),1.):
|
||||||
with self.assertRaises(ValueError):run.set_jet_amplitude(0,amplitude,level=2)
|
with self.assertRaises(ValueError):run.set_jet_amplitude(0,amplitude,level=2)
|
||||||
module.cuda.memcpy_htod.assert_not_called()
|
module.cuda.memcpy_htod.assert_not_called()
|
||||||
np.testing.assert_array_equal(before,run._level_boundaries[2].tables['surface_velocity'])
|
np.testing.assert_array_equal(before,run._device_boundaries[2].tables['surface_velocity'])
|
||||||
finally:run.close()
|
finally:run.close()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ class ShiftedStorage(unittest.TestCase):
|
|||||||
before = run._download_raw()
|
before = run._download_raw()
|
||||||
expected = remap(topology(A),topology(B),before.values)
|
expected = remap(topology(A),topology(B),before.values)
|
||||||
with patch.object(run,'download',side_effect=AssertionError('public decode used')):
|
with patch.object(run,'download',side_effect=AssertionError('public decode used')):
|
||||||
clone = run.remap_finest(B)
|
clone = run.remap_child(B)
|
||||||
self.assertEqual((run.step_count,clone.step_count),(7,7))
|
self.assertEqual((run.step_count,clone.step_count),(7,7))
|
||||||
self.assertFalse(clone._exchange_valid)
|
self.assertFalse(clone._exchange_valid)
|
||||||
for level, want, got in zip(clone._levels,expected.g,clone._download_raw().values):
|
for level, want, got in zip(clone._levels,expected.g,clone._download_raw().values):
|
||||||
@@ -114,7 +114,7 @@ class ShiftedStorage(unittest.TestCase):
|
|||||||
physical = tuple(np.broadcast_to(_WEIGHTS, level.shape).copy() for level in run._levels)
|
physical = tuple(np.broadcast_to(_WEIGHTS, level.shape).copy() for level in run._levels)
|
||||||
for a in physical: a[1] += 2**-35
|
for a in physical: a[1] += 2**-35
|
||||||
run.initialize(*physical)
|
run.initialize(*physical)
|
||||||
for a,mask in zip(run._download_raw().values,run.leaf):
|
for a,mask in zip(run._download_raw().values,run._leaf):
|
||||||
self.assertTrue((a[1,mask] == np.float32(2**-35)).all())
|
self.assertTrue((a[1,mask] == np.float32(2**-35)).all())
|
||||||
self.assertEqual(run.step_count,0)
|
self.assertEqual(run.step_count,0)
|
||||||
module.cuda.memcpy_htod.reset_mock()
|
module.cuda.memcpy_htod.reset_mock()
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class SingleLevel(unittest.TestCase):
|
|||||||
|
|
||||||
def test_circle_solids_are_not_state(self):
|
def test_circle_solids_are_not_state(self):
|
||||||
run, _, _ = backend((circle(),))
|
run, _, _ = backend((circle(),))
|
||||||
solid = run._level_boundaries[0].tables["solid"]
|
solid = run._device_boundaries[0].tables["solid"]
|
||||||
self.assertTrue(solid.any())
|
self.assertTrue(solid.any())
|
||||||
np.testing.assert_array_equal(run._state[0], ~solid)
|
np.testing.assert_array_equal(run._state[0], ~solid)
|
||||||
|
|
||||||
|
|||||||
@@ -35,10 +35,10 @@ class UnionBodySupport(unittest.TestCase):
|
|||||||
run,module,allocations,storage,objects=ready()
|
run,module,allocations,storage,objects=ready()
|
||||||
try:
|
try:
|
||||||
run.set_jet_amplitude(0,.017,level=2)
|
run.set_jet_amplitude(0,.017,level=2)
|
||||||
clone=run.remap_finest(((16,16,24,40),(24,16,32,40),A[1]),objects=objects)
|
clone=run.remap_child(((16,16,24,40),(24,16,32,40),A[1]),child_objects=objects)
|
||||||
try:
|
try:
|
||||||
self.assertEqual(clone._level_boundaries[2].jets[0].amplitude,.017)
|
self.assertEqual(clone._boundaries[2].jets[0].amplitude,.017)
|
||||||
self.assertTrue(same_physical_boundary(run.boundaries[2],clone.boundaries[2]))
|
self.assertTrue(same_physical_boundary(run.child_boundary,clone.child_boundary))
|
||||||
for a,b in zip(run.download(),clone.download()):np.testing.assert_array_equal(a,b)
|
for a,b in zip(run.download(),clone.download()):np.testing.assert_array_equal(a,b)
|
||||||
finally:clone.close()
|
finally:clone.close()
|
||||||
finally:run.close()
|
finally:run.close()
|
||||||
@@ -40,7 +40,7 @@ class Equivalence(unittest.TestCase):
|
|||||||
table_sets(FixedPatchSet2D.from_config(CFG, [(8, 8, 20, 22)])))
|
table_sets(FixedPatchSet2D.from_config(CFG, [(8, 8, 20, 22)])))
|
||||||
|
|
||||||
def test_separated_rectangles_reproduce_current_tables(self):
|
def test_separated_rectangles_reproduce_current_tables(self):
|
||||||
rects = [(4, 4, 10, 10), (16, 16, 26, 26)] # separated rectangles; union dependencies determine legality
|
rects = [(4, 4, 10, 10), (16, 16, 26, 26)] # gap 6 >= MIN_PATCH_GAP
|
||||||
self.assertEqual(table_sets(union(*rects)),
|
self.assertEqual(table_sets(union(*rects)),
|
||||||
table_sets(FixedPatchSet2D.from_config(CFG, rects)))
|
table_sets(FixedPatchSet2D.from_config(CFG, rects)))
|
||||||
|
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ class SingleLevelGPU(unittest.TestCase):
|
|||||||
with CudaContext(0) as context:
|
with CudaContext(0) as context:
|
||||||
for split in SPLITS:
|
for split in SPLITS:
|
||||||
with single(context=context, block=split, objects=[circle()]) as backend:
|
with single(context=context, block=split, objects=[circle()]) as backend:
|
||||||
tables = backend._level_boundaries[0].tables
|
tables = backend._device_boundaries[0].tables
|
||||||
for key, values, dtype in (("q", [l.q for l in links], np.float32),
|
for key, values, dtype in (("q", [l.q for l in links], np.float32),
|
||||||
("dir", [l.dir for l in links], np.uint8),
|
("dir", [l.dir for l in links], np.uint8),
|
||||||
("fallback", [l.fallback for l in links], np.uint8)):
|
("fallback", [l.fallback for l in links], np.uint8)):
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ class ChannelRootGPU(unittest.TestCase):
|
|||||||
arrays = [state(*level.shape[1:]) for level in backend._levels]
|
arrays = [state(*level.shape[1:]) for level in backend._levels]
|
||||||
backend.initialize(*arrays)
|
backend.initialize(*arrays)
|
||||||
backend.step(4)
|
backend.step(4)
|
||||||
if bodies: backend.set_jet_amplitude(0, -.001, level=1)
|
if bodies: backend.set_jet_amplitude(0, -.001)
|
||||||
backend.step(4)
|
backend.step(4)
|
||||||
raw = backend._download_raw()
|
raw = backend._download_raw()
|
||||||
physical = backend.download()
|
physical = backend.download()
|
||||||
@@ -128,7 +128,7 @@ class ChannelRootGPU(unittest.TestCase):
|
|||||||
else:
|
else:
|
||||||
for a,b in zip(reference, raw.values): np.testing.assert_array_equal(a,b)
|
for a,b in zip(reference, raw.values): np.testing.assert_array_equal(a,b)
|
||||||
with self.assertRaisesRegex(ValueError, "periodic"):
|
with self.assertRaisesRegex(ValueError, "periodic"):
|
||||||
backend.remap_finest([])
|
backend.remap_child([])
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__": unittest.main()
|
if __name__ == "__main__": unittest.main()
|
||||||
@@ -59,44 +59,44 @@ class ExchangeWindowCPU(unittest.TestCase):
|
|||||||
stack.enter_context(patch.object(cuda, "memcpy_dtoh",
|
stack.enter_context(patch.object(cuda, "memcpy_dtoh",
|
||||||
side_effect=lambda out, src: out.fill(0)))
|
side_effect=lambda out, src: out.fill(0)))
|
||||||
backend = build(m)
|
backend = build(m)
|
||||||
b = backend._level_boundaries[1]
|
b = backend._device_boundaries[1]
|
||||||
self.assertIsNotNone(b.window_integral)
|
self.assertIsNotNone(b.window_integral)
|
||||||
with self.assertRaises(RuntimeError):
|
with self.assertRaises(RuntimeError):
|
||||||
backend.start_exchange_window() # not initialized
|
backend.start_exchange_window() # not initialized
|
||||||
arrays = [np.full(level.shape, 1 / 9, np.float32) for level in backend._levels]
|
arrays = [np.full(level.shape, 1 / 9, np.float32) for level in backend._levels]
|
||||||
backend.initialize(*arrays)
|
backend.initialize(*arrays)
|
||||||
with self.assertRaisesRegex(RuntimeError, "no valid exchange window"):
|
with self.assertRaisesRegex(RuntimeError, "no valid exchange window"):
|
||||||
backend.read_exchange_window(0, level=1)
|
backend.read_exchange_window(0)
|
||||||
backend.step(1)
|
backend.step(1)
|
||||||
self.assertEqual(len(accumulations(m)), 2) # closed window: interval ledger only
|
self.assertEqual(len(accumulations(m)), 2) # closed window: interval ledger only
|
||||||
backend.start_exchange_window()
|
backend.start_exchange_window()
|
||||||
with self.assertRaisesRegex(RuntimeError, "at least one completed"):
|
with self.assertRaisesRegex(RuntimeError, "at least one completed"):
|
||||||
backend.read_exchange_window(0, level=1)
|
backend.read_exchange_window(0)
|
||||||
m["calls"].clear()
|
m["calls"].clear()
|
||||||
backend.step(2)
|
backend.step(2)
|
||||||
calls = accumulations(m)
|
calls = accumulations(m)
|
||||||
self.assertEqual(len(calls), 8) # two fine hops x two ledgers x two base steps
|
self.assertEqual(len(calls), 8) # two fine hops x two ledgers x two base steps
|
||||||
self.assertEqual({id(c[1][1]) for c in calls},
|
self.assertEqual({id(c[1][1]) for c in calls},
|
||||||
{id(b.interval_integral), id(b.window_integral)})
|
{id(b.interval_integral), id(b.window_integral)})
|
||||||
out = backend.read_exchange_window(0, level=1)
|
out = backend.read_exchange_window(0)
|
||||||
self.assertEqual((out["time_start"], out["time_end"], out["sample_count"]), (1., 3, 4))
|
self.assertEqual((out["time_start"], out["time_end"], out["sample_count"]), (1., 3, 4))
|
||||||
self.assertEqual(backend.read_exchange_window(0, level=1)["time_end"], 3) # non-destructive
|
self.assertEqual(backend.read_exchange_window(0)["time_end"], 3) # non-destructive
|
||||||
backend.initialize(*arrays) # invalidates
|
backend.initialize(*arrays) # invalidates
|
||||||
with self.assertRaisesRegex(RuntimeError, "no valid exchange window"):
|
with self.assertRaisesRegex(RuntimeError, "no valid exchange window"):
|
||||||
backend.read_exchange_window(0, level=1)
|
backend.read_exchange_window(0)
|
||||||
backend.start_exchange_window()
|
backend.start_exchange_window()
|
||||||
with patch.object(backend, "_hop", side_effect=RuntimeError("injected")):
|
with patch.object(backend, "_hop", side_effect=RuntimeError("injected")):
|
||||||
with self.assertRaises(RuntimeError):
|
with self.assertRaises(RuntimeError):
|
||||||
backend.step(1)
|
backend.step(1)
|
||||||
self.assertFalse(backend._window_valid)
|
self.assertFalse(backend._window_valid)
|
||||||
with self.assertRaises(RuntimeError):
|
with self.assertRaises(RuntimeError):
|
||||||
backend.read_exchange_window(0, level=1) # failed status
|
backend.read_exchange_window(0) # failed status
|
||||||
|
|
||||||
def test_disabled_default_has_no_window_cost(self):
|
def test_disabled_default_has_no_window_cost(self):
|
||||||
with mocked_backend() as m, ExitStack() as stack:
|
with mocked_backend() as m, ExitStack() as stack:
|
||||||
stack.enter_context(patch.object(cuda, "memset_d8_async"))
|
stack.enter_context(patch.object(cuda, "memset_d8_async"))
|
||||||
backend = build(m, window=False)
|
backend = build(m, window=False)
|
||||||
self.assertIsNone(backend._level_boundaries[1].window_integral)
|
self.assertIsNone(backend._device_boundaries[1].window_integral)
|
||||||
backend.initialize(*[np.full(l.shape, 1 / 9, np.float32) for l in backend._levels])
|
backend.initialize(*[np.full(l.shape, 1 / 9, np.float32) for l in backend._levels])
|
||||||
backend.step(1)
|
backend.step(1)
|
||||||
self.assertEqual(len(accumulations(m)), 2)
|
self.assertEqual(len(accumulations(m)), 2)
|
||||||
@@ -120,7 +120,7 @@ class ExchangeWindowGPU(unittest.TestCase):
|
|||||||
windowed.step(2)
|
windowed.step(2)
|
||||||
plain.step(2)
|
plain.step(2)
|
||||||
windowed.start_exchange_window()
|
windowed.start_exchange_window()
|
||||||
b = windowed._level_boundaries[1]
|
b = windowed._device_boundaries[1]
|
||||||
count = len(b.tables["cell"])
|
count = len(b.tables["cell"])
|
||||||
factors = np.array([.25, .25, .125, .25]) # h*dt, h*h*dt, h*h for level-1 h=.5
|
factors = np.array([.25, .25, .125, .25]) # h*dt, h*h*dt, h*h for level-1 h=.5
|
||||||
scale = np.zeros(4)
|
scale = np.zeros(4)
|
||||||
@@ -140,12 +140,12 @@ class ExchangeWindowGPU(unittest.TestCase):
|
|||||||
summed = {part: [0., 0., 0., 0.] for part in ("total", "wall", "aperture")}
|
summed = {part: [0., 0., 0., 0.] for part in ("total", "wall", "aperture")}
|
||||||
steps = 0
|
steps = 0
|
||||||
for speed in (0., .002, .002, -.001, 0., 0.): # amplitude updates stay in-window
|
for speed in (0., .002, .002, -.001, 0., 0.): # amplitude updates stay in-window
|
||||||
windowed.set_jet_amplitude(0, speed, level=1)
|
windowed.set_jet_amplitude(0, speed)
|
||||||
plain.set_jet_amplitude(0, speed, level=1)
|
plain.set_jet_amplitude(0, speed)
|
||||||
windowed.step(1)
|
windowed.step(1)
|
||||||
plain.step(1)
|
plain.step(1)
|
||||||
steps += 1
|
steps += 1
|
||||||
obs = windowed.read_raw_exchange(0, level=1)
|
obs = windowed.read_raw_exchange(0)
|
||||||
for part in summed:
|
for part in summed:
|
||||||
o = obs[part]
|
o = obs[part]
|
||||||
summed[part] = [summed[part][0] + o["impulse"][0], summed[part][1] + o["impulse"][1],
|
summed[part] = [summed[part][0] + o["impulse"][0], summed[part][1] + o["impulse"][1],
|
||||||
@@ -153,7 +153,7 @@ class ExchangeWindowGPU(unittest.TestCase):
|
|||||||
summed[part][3] + o["mass_into_fluid"]]
|
summed[part][3] + o["mass_into_fluid"]]
|
||||||
for a, e in zip(windowed._download_raw().values, plain._download_raw().values):
|
for a, e in zip(windowed._download_raw().values, plain._download_raw().values):
|
||||||
same_bits(self, a, e)
|
same_bits(self, a, e)
|
||||||
out = windowed.read_exchange_window(0, level=1)
|
out = windowed.read_exchange_window(0)
|
||||||
self.assertEqual((out["time_end"] - out["time_start"], out["sample_count"]),
|
self.assertEqual((out["time_end"] - out["time_start"], out["sample_count"]),
|
||||||
(steps, 2 * steps))
|
(steps, 2 * steps))
|
||||||
self.assertEqual(substeps, 2 * steps)
|
self.assertEqual(substeps, 2 * steps)
|
||||||
@@ -165,11 +165,11 @@ class ExchangeWindowGPU(unittest.TestCase):
|
|||||||
diff = np.abs(got - np.array(summed[part]))
|
diff = np.abs(got - np.array(summed[part]))
|
||||||
self.assertTrue((diff <= bound).all(), f"{part}: {diff} > {bound}")
|
self.assertTrue((diff <= bound).all(), f"{part}: {diff} > {bound}")
|
||||||
np.testing.assert_array_equal(o["force"], o["impulse"] / steps)
|
np.testing.assert_array_equal(o["force"], o["impulse"] / steps)
|
||||||
again = windowed.read_exchange_window(0, level=1)
|
again = windowed.read_exchange_window(0)
|
||||||
np.testing.assert_array_equal(again["total"]["impulse"], out["total"]["impulse"])
|
np.testing.assert_array_equal(again["total"]["impulse"], out["total"]["impulse"])
|
||||||
windowed.initialize(*arrays)
|
windowed.initialize(*arrays)
|
||||||
with self.assertRaises(RuntimeError):
|
with self.assertRaises(RuntimeError):
|
||||||
windowed.read_exchange_window(0, level=1)
|
windowed.read_exchange_window(0)
|
||||||
|
|
||||||
|
|
||||||
def audit_substeps(stepper, b, count, record):
|
def audit_substeps(stepper, b, count, record):
|
||||||
@@ -212,7 +212,7 @@ class ExchangeWindowShiftedMRTGPU(unittest.TestCase):
|
|||||||
run.step(2)
|
run.step(2)
|
||||||
if run is not plain:
|
if run is not plain:
|
||||||
run.start_exchange_window()
|
run.start_exchange_window()
|
||||||
b = windowed._level_boundaries[1]
|
b = windowed._device_boundaries[1]
|
||||||
count = len(b.tables["cell"])
|
count = len(b.tables["cell"])
|
||||||
record = dict(scale=np.zeros(4), substeps=0)
|
record = dict(scale=np.zeros(4), substeps=0)
|
||||||
audit_substeps(windowed, b, count, record)
|
audit_substeps(windowed, b, count, record)
|
||||||
@@ -220,7 +220,7 @@ class ExchangeWindowShiftedMRTGPU(unittest.TestCase):
|
|||||||
for _ in range(steps):
|
for _ in range(steps):
|
||||||
windowed.step(1)
|
windowed.step(1)
|
||||||
plain.step(1)
|
plain.step(1)
|
||||||
summed += components(windowed.read_raw_exchange(0, level=1))
|
summed += components(windowed.read_raw_exchange(0))
|
||||||
for a, e in zip(windowed._download_raw().values, plain._download_raw().values):
|
for a, e in zip(windowed._download_raw().values, plain._download_raw().values):
|
||||||
same_bits(self, a, e)
|
same_bits(self, a, e)
|
||||||
split.step(steps) # one call: same ledger arithmetic as five step(1) calls
|
split.step(steps) # one call: same ledger arithmetic as five step(1) calls
|
||||||
@@ -228,31 +228,31 @@ class ExchangeWindowShiftedMRTGPU(unittest.TestCase):
|
|||||||
same_bits(self, a, e)
|
same_bits(self, a, e)
|
||||||
rows = [np.empty((count, 4), np.float64) for _ in range(2)]
|
rows = [np.empty((count, 4), np.float64) for _ in range(2)]
|
||||||
cuda.memcpy_dtoh(rows[0], b.window_integral)
|
cuda.memcpy_dtoh(rows[0], b.window_integral)
|
||||||
cuda.memcpy_dtoh(rows[1], split._level_boundaries[1].window_integral)
|
cuda.memcpy_dtoh(rows[1], split._device_boundaries[1].window_integral)
|
||||||
np.testing.assert_array_equal(rows[0].view(np.uint64), rows[1].view(np.uint64))
|
np.testing.assert_array_equal(rows[0].view(np.uint64), rows[1].view(np.uint64))
|
||||||
|
|
||||||
out = windowed.read_exchange_window(0, level=1)
|
out = windowed.read_exchange_window(0)
|
||||||
self.assertEqual(record["substeps"], 2 * steps)
|
self.assertEqual(record["substeps"], 2 * steps)
|
||||||
self.assertEqual((out["time_end"] - out["time_start"], out["sample_count"]),
|
self.assertEqual((out["time_end"] - out["time_start"], out["sample_count"]),
|
||||||
(steps, 2 * steps))
|
(steps, 2 * steps))
|
||||||
bound = 4 * (count * record["substeps"]) * EPS * record["scale"] + 1e-300
|
bound = 4 * (count * record["substeps"]) * EPS * record["scale"] + 1e-300
|
||||||
diff = np.abs(components(out) - summed)
|
diff = np.abs(components(out) - summed)
|
||||||
self.assertTrue((diff <= bound).all(), f"{diff} > {bound}")
|
self.assertTrue((diff <= bound).all(), f"{diff} > {bound}")
|
||||||
np.testing.assert_array_equal(split.read_exchange_window(0, level=1)["total"]["impulse"],
|
np.testing.assert_array_equal(split.read_exchange_window(0)["total"]["impulse"],
|
||||||
out["total"]["impulse"])
|
out["total"]["impulse"])
|
||||||
|
|
||||||
windowed.step(0) # no interval: window and its duration unchanged
|
windowed.step(0) # no interval: window and its duration unchanged
|
||||||
again = windowed.read_exchange_window(0, level=1)
|
again = windowed.read_exchange_window(0)
|
||||||
self.assertEqual(again["time_end"], out["time_end"])
|
self.assertEqual(again["time_end"], out["time_end"])
|
||||||
np.testing.assert_array_equal(again["total"]["impulse"], out["total"]["impulse"])
|
np.testing.assert_array_equal(again["total"]["impulse"], out["total"]["impulse"])
|
||||||
|
|
||||||
windowed.start_exchange_window() # reset after a window
|
windowed.start_exchange_window() # reset after a window
|
||||||
with self.assertRaisesRegex(RuntimeError, "at least one completed"):
|
with self.assertRaisesRegex(RuntimeError, "at least one completed"):
|
||||||
windowed.read_exchange_window(0, level=1)
|
windowed.read_exchange_window(0)
|
||||||
record.update(scale=np.zeros(4), substeps=0)
|
record.update(scale=np.zeros(4), substeps=0)
|
||||||
windowed.step(1)
|
windowed.step(1)
|
||||||
single = components(windowed.read_raw_exchange(0, level=1))
|
single = components(windowed.read_raw_exchange(0))
|
||||||
reset = windowed.read_exchange_window(0, level=1)
|
reset = windowed.read_exchange_window(0)
|
||||||
self.assertEqual(reset["time_end"] - reset["time_start"], 1)
|
self.assertEqual(reset["time_end"] - reset["time_start"], 1)
|
||||||
bound = 4 * (count * record["substeps"]) * EPS * record["scale"] + 1e-300
|
bound = 4 * (count * record["substeps"]) * EPS * record["scale"] + 1e-300
|
||||||
self.assertTrue((np.abs(components(reset) - single) <= bound).all())
|
self.assertTrue((np.abs(components(reset) - single) <= bound).all())
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ def run_blocked_patch(speed):
|
|||||||
level_objects=[(), (source,)],
|
level_objects=[(), (source,)],
|
||||||
observe_raw_exchange=True) as backend:
|
observe_raw_exchange=True) as backend:
|
||||||
c, f = backend._levels
|
c, f = backend._levels
|
||||||
flags = backend.boundaries[1].flags.reshape(40, 40)
|
flags = backend.fine_boundary.flags.reshape(40, 40)
|
||||||
active = np.zeros((40, 40), bool)
|
active = np.zeros((40, 40), bool)
|
||||||
active[4:-4, 4:-4] = True
|
active[4:-4, 4:-4] = True
|
||||||
active &= ((flags & FLUID) != 0) & ((flags & SOLID) == 0)
|
active &= ((flags & FLUID) != 0) & ((flags & SOLID) == 0)
|
||||||
@@ -101,7 +101,7 @@ def run_blocked_patch(speed):
|
|||||||
with patch.object(f, "kernel", fine):
|
with patch.object(f, "kernel", fine):
|
||||||
backend.step(2)
|
backend.step(2)
|
||||||
assert len(traces) == 4 and backend.step_count == 2
|
assert len(traces) == 4 and backend.step_count == 2
|
||||||
mass = backend.read_raw_exchange(0, level=1)["total"]["mass_into_fluid"]
|
mass = backend.read_raw_exchange(0)["total"]["mass_into_fluid"]
|
||||||
return traces, roi, backend.download(), float(mass)
|
return traces, roi, backend.download(), float(mass)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ def make(context):
|
|||||||
|
|
||||||
def topology(run, rectangles, objects):
|
def topology(run, rectangles, objects):
|
||||||
return RemapTopology.from_config(
|
return RemapTopology.from_config(
|
||||||
CFG, run.hierarchy.pairs[0], rectangles, child_objects=objects,
|
CFG, run.patch, rectangles, child_objects=objects,
|
||||||
child_jets=run._level_boundaries[2].jets,
|
child_jets=run._boundaries[2].jets,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -74,8 +74,8 @@ class MultiplePinnedRemapGPU(unittest.TestCase):
|
|||||||
def change_amplitudes(self, run, amplitudes):
|
def change_amplitudes(self, run, amplitudes):
|
||||||
# Each jet selects a different body. Updating one must preserve the
|
# Each jet selects a different body. Updating one must preserve the
|
||||||
# other body's current per-link velocities, including its driven links.
|
# other body's current per-link velocities, including its driven links.
|
||||||
boundary = run._level_boundaries[2]
|
boundary = run._boundaries[2]
|
||||||
body_ids = run.boundaries[2].curved_host[3]
|
body_ids = run.child_boundary.curved_host[3]
|
||||||
for index, amplitude in enumerate(amplitudes):
|
for index, amplitude in enumerate(amplitudes):
|
||||||
before = boundary.tables['surface_velocity'].copy()
|
before = boundary.tables['surface_velocity'].copy()
|
||||||
run.set_jet_amplitude(index, amplitude, level=2)
|
run.set_jet_amplitude(index, amplitude, level=2)
|
||||||
@@ -87,11 +87,11 @@ class MultiplePinnedRemapGPU(unittest.TestCase):
|
|||||||
|
|
||||||
def check_boundary(self, old, new, mesh, amplitudes):
|
def check_boundary(self, old, new, mesh, amplitudes):
|
||||||
self.assertEqual(mesh.pinned_rectangles, PINS)
|
self.assertEqual(mesh.pinned_rectangles, PINS)
|
||||||
self.assertTrue(same_physical_boundary(old.boundaries[2], new.boundaries[2]))
|
self.assertTrue(same_physical_boundary(old.child_boundary, new.child_boundary))
|
||||||
self.assertTrue(same_physical_boundary(new.boundaries[2], mesh.boundary))
|
self.assertTrue(same_physical_boundary(new.child_boundary, mesh.boundary))
|
||||||
# Compare physical cut-link rows, never layout-dependent blocked addresses.
|
# Compare physical cut-link rows, never layout-dependent blocked addresses.
|
||||||
for run in (old, new):
|
for run in (old, new):
|
||||||
boundary = run._level_boundaries[2]
|
boundary = run._boundaries[2]
|
||||||
self.assertEqual(tuple(jet.amplitude for jet in boundary.jets), amplitudes)
|
self.assertEqual(tuple(jet.amplitude for jet in boundary.jets), amplitudes)
|
||||||
exact(self, boundary.tables['surface_velocity'], mesh.surface_velocity,
|
exact(self, boundary.tables['surface_velocity'], mesh.surface_velocity,
|
||||||
'latest physical per-link velocities')
|
'latest physical per-link velocities')
|
||||||
@@ -107,7 +107,7 @@ class MultiplePinnedRemapGPU(unittest.TestCase):
|
|||||||
before, count = old.download(), old.step_count
|
before, count = old.download(), old.step_count
|
||||||
mesh = topology(old, START, objects)
|
mesh = topology(old, START, objects)
|
||||||
# Input order is irrelevant even with several pins and fluid regions.
|
# Input order is irrelevant even with several pins and fluid regions.
|
||||||
new = owned.enter_context(old.remap_finest(START[::-1], objects=objects))
|
new = owned.enter_context(old.remap_child(START[::-1], child_objects=objects))
|
||||||
self.assertIsNot(old, new)
|
self.assertIsNot(old, new)
|
||||||
self.assertEqual((old.step_count, new.step_count), (count, count))
|
self.assertEqual((old.step_count, new.step_count), (count, count))
|
||||||
self.check_boundary(old, new, mesh, amplitudes)
|
self.check_boundary(old, new, mesh, amplitudes)
|
||||||
@@ -133,7 +133,7 @@ class MultiplePinnedRemapGPU(unittest.TestCase):
|
|||||||
previous = current
|
previous = current
|
||||||
self.change_amplitudes(previous, amplitudes)
|
self.change_amplitudes(previous, amplitudes)
|
||||||
before, count = previous.download(), previous.step_count
|
before, count = previous.download(), previous.step_count
|
||||||
old_mesh = topology(previous, previous.hierarchy.pairs[1].bounds, objects)
|
old_mesh = topology(previous, previous.child_patch.bounds, objects)
|
||||||
new_mesh = topology(previous, rectangles, objects)
|
new_mesh = topology(previous, rectangles, objects)
|
||||||
expected = remap_synchronized(
|
expected = remap_synchronized(
|
||||||
old_mesh, new_mesh, before, base_count=count,
|
old_mesh, new_mesh, before, base_count=count,
|
||||||
@@ -141,7 +141,7 @@ class MultiplePinnedRemapGPU(unittest.TestCase):
|
|||||||
phase='postcollision_g',
|
phase='postcollision_g',
|
||||||
).g
|
).g
|
||||||
current = owned.enter_context(
|
current = owned.enter_context(
|
||||||
previous.remap_finest(rectangles, objects=objects))
|
previous.remap_child(rectangles, child_objects=objects))
|
||||||
self.assertIsNot(previous, current)
|
self.assertIsNot(previous, current)
|
||||||
self.assertEqual((previous.step_count, current.step_count), (count, count))
|
self.assertEqual((previous.step_count, current.step_count), (count, count))
|
||||||
self.check_boundary(previous, current, new_mesh, amplitudes)
|
self.check_boundary(previous, current, new_mesh, amplitudes)
|
||||||
|
|||||||
@@ -36,12 +36,12 @@ class PinnedBodyRemapGPU(unittest.TestCase):
|
|||||||
|
|
||||||
def check_boundary(self, before, after):
|
def check_boundary(self, before, after):
|
||||||
for name in ('flags','boundary_cells','curved_hits'):
|
for name in ('flags','boundary_cells','curved_hits'):
|
||||||
exact(self,getattr(after.boundaries[2],name),getattr(before.boundaries[2],name),name)
|
exact(self,getattr(after.child_boundary,name),getattr(before.child_boundary,name),name)
|
||||||
for a,b in zip(after.boundaries[2].curved_host,before.boundaries[2].curved_host):
|
for a,b in zip(after.child_boundary.curved_host,before.child_boundary.curved_host):
|
||||||
exact(self,a,b,'physical cut links')
|
exact(self,a,b,'physical cut links')
|
||||||
exact(self,after._level_boundaries[2].tables['surface_velocity'],
|
exact(self,after._boundaries[2].tables['surface_velocity'],
|
||||||
before._level_boundaries[2].tables['surface_velocity'],'current per-link velocities')
|
before._boundaries[2].tables['surface_velocity'],'current per-link velocities')
|
||||||
self.assertEqual(after._level_boundaries[2].jets[0].amplitude,.017)
|
self.assertEqual(after._boundaries[2].jets[0].amplitude,.017)
|
||||||
|
|
||||||
def test_noop_next_step_bitwise(self):
|
def test_noop_next_step_bitwise(self):
|
||||||
with CudaContext(0) as ctx, ExitStack() as owned:
|
with CudaContext(0) as ctx, ExitStack() as owned:
|
||||||
@@ -49,7 +49,7 @@ class PinnedBodyRemapGPU(unittest.TestCase):
|
|||||||
old.initialize(*initial(old,False));old.step(2)
|
old.initialize(*initial(old,False));old.step(2)
|
||||||
old.set_jet_amplitude(0,.017,level=2)
|
old.set_jet_amplitude(0,.017,level=2)
|
||||||
before=old.download()
|
before=old.download()
|
||||||
new=owned.enter_context(old.remap_finest(A,objects=objects))
|
new=owned.enter_context(old.remap_child(A,child_objects=objects))
|
||||||
self.check_boundary(old,new);self.check_reset(new,before)
|
self.check_boundary(old,new);self.check_reset(new,before)
|
||||||
old.step();new.step()
|
old.step();new.step()
|
||||||
for a,b in zip(new.download(),old.download()): exact(self,a,b,'no-op next step bitwise')
|
for a,b in zip(new.download(),old.download()): exact(self,a,b,'no-op next step bitwise')
|
||||||
@@ -63,14 +63,14 @@ class PinnedBodyRemapGPU(unittest.TestCase):
|
|||||||
current.set_jet_amplitude(0,.017,level=2)
|
current.set_jet_amplitude(0,.017,level=2)
|
||||||
for rects in (B,A):
|
for rects in (B,A):
|
||||||
previous=current;before=previous.download();count=previous.step_count
|
previous=current;before=previous.download();count=previous.step_count
|
||||||
jets=previous._level_boundaries[2].jets
|
jets=previous._boundaries[2].jets
|
||||||
old=RemapTopology.from_config(CFG,previous.hierarchy.pairs[0],previous.hierarchy.pairs[1].bounds,
|
old=RemapTopology.from_config(CFG,previous.patch,previous.child_patch.bounds,
|
||||||
child_objects=objects,child_jets=jets)
|
child_objects=objects,child_jets=jets)
|
||||||
new=RemapTopology.from_config(CFG,previous.hierarchy.pairs[0],rects,
|
new=RemapTopology.from_config(CFG,previous.patch,rects,
|
||||||
child_objects=objects,child_jets=jets)
|
child_objects=objects,child_jets=jets)
|
||||||
expected=remap_synchronized(old,new,before,base_count=count,
|
expected=remap_synchronized(old,new,before,base_count=count,
|
||||||
level_steps=(count,2*count,4*count),phase='postcollision_g').g
|
level_steps=(count,2*count,4*count),phase='postcollision_g').g
|
||||||
current=owned.enter_context(previous.remap_finest(rects,objects=objects))
|
current=owned.enter_context(previous.remap_child(rects,child_objects=objects))
|
||||||
self.check_boundary(previous,current);self.check_reset(current,expected)
|
self.check_boundary(previous,current);self.check_reset(current,expected)
|
||||||
for a,b,om,nm in zip(current.download(),before,old.leaf_masks,new.leaf_masks):
|
for a,b,om,nm in zip(current.download(),before,old.leaf_masks,new.leaf_masks):
|
||||||
exact(self,a[:,om & nm],b[:,om & nm],'physical-fluid overlap')
|
exact(self,a[:,om & nm],b[:,om & nm],'physical-fluid overlap')
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ def census(run, index, buffer):
|
|||||||
layout, b = level.layout, level.boundary
|
layout, b = level.layout, level.boundary
|
||||||
g = download(buffer, layout.blocked_shape)
|
g = download(buffer, layout.blocked_shape)
|
||||||
dense = layout.to_dense(g)
|
dense = layout.to_dense(g)
|
||||||
leaf = run.leaf[index]
|
leaf = run._leaf[index]
|
||||||
ny, nx = leaf.shape
|
ny, nx = leaf.shape
|
||||||
solid = (b.host.flags.reshape(ny, nx) & SOLID) != 0
|
solid = (b.host.flags.reshape(ny, nx) & SOLID) != 0
|
||||||
y, x = np.nonzero(leaf)
|
y, x = np.nonzero(leaf)
|
||||||
@@ -105,7 +105,7 @@ class RawExchangeGPU(unittest.TestCase):
|
|||||||
arrays = [state(l.shape) for l in measured._levels]
|
arrays = [state(l.shape) for l in measured._levels]
|
||||||
measured.initialize(*arrays)
|
measured.initialize(*arrays)
|
||||||
plain.initialize(*arrays)
|
plain.initialize(*arrays)
|
||||||
b = measured._level_boundaries[1]
|
b = measured._device_boundaries[1]
|
||||||
count = len(b.tables['cell'])
|
count = len(b.tables['cell'])
|
||||||
kernel = measured._levels[1].kernel
|
kernel = measured._levels[1].kernel
|
||||||
samples = []
|
samples = []
|
||||||
@@ -119,7 +119,7 @@ class RawExchangeGPU(unittest.TestCase):
|
|||||||
measured._stream.synchronize()
|
measured._stream.synchronize()
|
||||||
after = measured._levels[1].layout.to_dense(
|
after = measured._levels[1].layout.to_dense(
|
||||||
download(dst, measured._levels[1].layout.blocked_shape))
|
download(dst, measured._levels[1].layout.blocked_shape))
|
||||||
change = totals(after, measured.leaf[1])-before
|
change = totals(after, measured._leaf[1])-before
|
||||||
expected = external + np.array([-raw[:, 0].astype(float).sum(),
|
expected = external + np.array([-raw[:, 0].astype(float).sum(),
|
||||||
-raw[:, 1].astype(float).sum(),
|
-raw[:, 1].astype(float).sum(),
|
||||||
raw[:, 3].astype(float).sum()])
|
raw[:, 3].astype(float).sum()])
|
||||||
@@ -127,8 +127,8 @@ class RawExchangeGPU(unittest.TestCase):
|
|||||||
samples.append(raw.copy())
|
samples.append(raw.copy())
|
||||||
measured._levels[1].kernel = audited_step
|
measured._levels[1].kernel = audited_step
|
||||||
for speed in (0., .002, -.001, 0.):
|
for speed in (0., .002, -.001, 0.):
|
||||||
measured.set_jet_amplitude(0, speed, level=1)
|
measured.set_jet_amplitude(0, speed)
|
||||||
plain.set_jet_amplitude(0, speed, level=1)
|
plain.set_jet_amplitude(0, speed)
|
||||||
samples.clear()
|
samples.clear()
|
||||||
measured.step()
|
measured.step()
|
||||||
plain.step()
|
plain.step()
|
||||||
@@ -139,11 +139,11 @@ class RawExchangeGPU(unittest.TestCase):
|
|||||||
np.testing.assert_array_equal(integral, expected)
|
np.testing.assert_array_equal(integral, expected)
|
||||||
last = download(b.last_raw, (count, 4))
|
last = download(b.last_raw, (count, 4))
|
||||||
np.testing.assert_array_equal(last, samples[-1])
|
np.testing.assert_array_equal(last, samples[-1])
|
||||||
obs = measured.read_raw_exchange(0, level=1)
|
obs = measured.read_raw_exchange(0)
|
||||||
assert obs['sample_count'] == 2
|
assert obs['sample_count'] == 2
|
||||||
assert b.tables['aperture'].any()
|
assert b.tables['aperture'].any()
|
||||||
measured.step(0)
|
measured.step(0)
|
||||||
np.testing.assert_array_equal(measured.read_raw_exchange(0, level=1)['total']['impulse'],
|
np.testing.assert_array_equal(measured.read_raw_exchange(0)['total']['impulse'],
|
||||||
obs['total']['impulse'])
|
obs['total']['impulse'])
|
||||||
|
|
||||||
def test_depth_four_interval_storage_and_sampling(self):
|
def test_depth_four_interval_storage_and_sampling(self):
|
||||||
@@ -158,7 +158,7 @@ class RawExchangeGPU(unittest.TestCase):
|
|||||||
FixedPatchStepper(cfg, context=ctx, **settings) as plain:
|
FixedPatchStepper(cfg, context=ctx, **settings) as plain:
|
||||||
arrays = [state(l.shape, ux=0.) for l in run._levels]
|
arrays = [state(l.shape, ux=0.) for l in run._levels]
|
||||||
run.initialize(*arrays); plain.initialize(*arrays)
|
run.initialize(*arrays); plain.initialize(*arrays)
|
||||||
b = run._level_boundaries[3]
|
b = run._device_boundaries[3]
|
||||||
count = len(b.tables['cell'])
|
count = len(b.tables['cell'])
|
||||||
kernel = run._levels[3].kernel
|
kernel = run._levels[3].kernel
|
||||||
samples, independent_samples = [], []
|
samples, independent_samples = [], []
|
||||||
@@ -171,7 +171,7 @@ class RawExchangeGPU(unittest.TestCase):
|
|||||||
run._stream.synchronize()
|
run._stream.synchronize()
|
||||||
after = run._levels[3].layout.to_dense(
|
after = run._levels[3].layout.to_dense(
|
||||||
download(dst, run._levels[3].layout.blocked_shape))
|
download(dst, run._levels[3].layout.blocked_shape))
|
||||||
change = totals(after, run.leaf[3])-before
|
change = totals(after, run._leaf[3])-before
|
||||||
expected = external + np.array([-independent[:, 0].sum(),
|
expected = external + np.array([-independent[:, 0].sum(),
|
||||||
-independent[:, 1].sum(), independent[:, 3].sum()])
|
-independent[:, 1].sum(), independent[:, 3].sum()])
|
||||||
np.testing.assert_allclose(change, expected, rtol=2e-4, atol=8e-5)
|
np.testing.assert_allclose(change, expected, rtol=2e-4, atol=8e-5)
|
||||||
@@ -213,7 +213,7 @@ class RawExchangeGPU(unittest.TestCase):
|
|||||||
FixedPatchStepper(CFG, context=ctx, **fine((16, 20)), observe_raw_exchange=True) as amr:
|
FixedPatchStepper(CFG, context=ctx, **fine((16, 20)), observe_raw_exchange=True) as amr:
|
||||||
amr.initialize(*(state(l.shape) for l in amr._levels))
|
amr.initialize(*(state(l.shape) for l in amr._levels))
|
||||||
uniform.initialize(state((9, 48, 64)))
|
uniform.initialize(state((9, 48, 64)))
|
||||||
b = amr._level_boundaries[1]
|
b = amr._device_boundaries[1]
|
||||||
count = len(b.tables['cell'])
|
count = len(b.tables['cell'])
|
||||||
links = obj.geometry.build_cut_links(64, 48)
|
links = obj.geometry.build_cut_links(64, 48)
|
||||||
np.testing.assert_array_equal([v.dir for v in links], b.tables['dir'])
|
np.testing.assert_array_equal([v.dir for v in links], b.tables['dir'])
|
||||||
@@ -221,7 +221,7 @@ class RawExchangeGPU(unittest.TestCase):
|
|||||||
# Same fine-grid force arms after translation to the same center: the
|
# Same fine-grid force arms after translation to the same center: the
|
||||||
# single level's static BFL gets the AMR Jet column and force arms; the raw
|
# single level's static BFL gets the AMR Jet column and force arms; the raw
|
||||||
# output pointer is substituted at launch (no stepper-side accumulation).
|
# output pointer is substituted at launch (no stepper-side accumulation).
|
||||||
root = uniform._level_boundaries[0]
|
root = uniform._device_boundaries[0]
|
||||||
velocity = uniform._upload(b.tables['surface_velocity'])
|
velocity = uniform._upload(b.tables['surface_velocity'])
|
||||||
root.device.update(surface_velocity=velocity, rx=uniform._upload(b.tables['rx']),
|
root.device.update(surface_velocity=velocity, rx=uniform._upload(b.tables['rx']),
|
||||||
ry=uniform._upload(b.tables['ry']))
|
ry=uniform._upload(b.tables['ry']))
|
||||||
@@ -234,7 +234,7 @@ class RawExchangeGPU(unittest.TestCase):
|
|||||||
samples.append(download(raw, (count, 4)))
|
samples.append(download(raw, (count, 4)))
|
||||||
root.kernel = measured_boundary
|
root.kernel = measured_boundary
|
||||||
for speed in (0., .002, -.001, 0.):
|
for speed in (0., .002, -.001, 0.):
|
||||||
amr.set_jet_amplitude(0, speed, level=1)
|
amr.set_jet_amplitude(0, speed)
|
||||||
cuda.memcpy_htod(velocity, b.tables['surface_velocity'])
|
cuda.memcpy_htod(velocity, b.tables['surface_velocity'])
|
||||||
samples.clear()
|
samples.clear()
|
||||||
amr.step(); uniform.step(2)
|
amr.step(); uniform.step(2)
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ from CelerisLab.lbm import refinement_stepper
|
|||||||
from CelerisLab.lbm.blocks import BlockLayout2D
|
from CelerisLab.lbm.blocks import BlockLayout2D
|
||||||
from CelerisLab.lbm.descriptors import D2Q9_EX, D2Q9_EY
|
from CelerisLab.lbm.descriptors import D2Q9_EX, D2Q9_EY
|
||||||
from CelerisLab.lbm.refinement import ACTIVE, OUTER_GHOST, FixedPatch2D
|
from CelerisLab.lbm.refinement import ACTIVE, OUTER_GHOST, FixedPatch2D
|
||||||
from CelerisLab.lbm.refinement_stepper import FixedPatchStepper, blocked_base_tables
|
from CelerisLab.lbm.refinement_stepper import FixedPatchStepper
|
||||||
|
from CelerisLab.lbm.refinement_tables import blocked_coarse_tables
|
||||||
from test_refinement_blocked_fine import NX, NY, BOUNDS, config, state, circle, actuation
|
from test_refinement_blocked_fine import NX, NY, BOUNDS, config, state, circle, actuation
|
||||||
|
|
||||||
BASE_BLOCKS = ((24, 32), (12, 8), (3, 4), (1, 1))
|
BASE_BLOCKS = ((24, 32), (12, 8), (3, 4), (1, 1))
|
||||||
@@ -108,7 +109,7 @@ class BlockedBaseCPU(unittest.TestCase):
|
|||||||
leaves[[0, -1]] = False
|
leaves[[0, -1]] = False
|
||||||
for by, bx in BASE_BLOCKS:
|
for by, bx in BASE_BLOCKS:
|
||||||
layout = BlockLayout2D.build(NY, NX, by, bx, periodic=(True, mode == "periodic"))
|
layout = BlockLayout2D.build(NY, NX, by, bx, periodic=(True, mode == "periodic"))
|
||||||
donors, targets, kind = blocked_base_tables(p, layout, order, mode != "periodic")
|
donors, targets, kind = blocked_coarse_tables(p, layout, order, mode != "periodic")
|
||||||
ly, lx = layout.local_shape
|
ly, lx = layout.local_shape
|
||||||
L = ly * lx
|
L = ly * lx
|
||||||
expected, source_cells = [], []
|
expected, source_cells = [], []
|
||||||
@@ -138,7 +139,7 @@ class BlockedBaseCPU(unittest.TestCase):
|
|||||||
p = FixedPatch2D.from_config(config("periodic"), BOUNDS)
|
p = FixedPatch2D.from_config(config("periodic"), BOUNDS)
|
||||||
order = np.argsort(p.coalesce_coarse)
|
order = np.argsort(p.coalesce_coarse)
|
||||||
layout = BlockLayout2D.build(NY, NX, 3, 4)
|
layout = BlockLayout2D.build(NY, NX, 3, 4)
|
||||||
donors, targets, _ = blocked_base_tables(p, layout, order)
|
donors, targets, _ = blocked_coarse_tables(p, layout, order)
|
||||||
dense = np.arange(9 * NY * NX, dtype=np.float32).reshape(9, NY, NX)
|
dense = np.arange(9 * NY * NX, dtype=np.float32).reshape(9, NY, NX)
|
||||||
initial = layout.to_blocked(dense)
|
initial = layout.to_blocked(dense)
|
||||||
arrivals = np.arange(len(targets), dtype=np.float32) + 10000
|
arrivals = np.arange(len(targets), dtype=np.float32) + 10000
|
||||||
@@ -178,7 +179,7 @@ class BlockedBaseCPU(unittest.TestCase):
|
|||||||
# With one parent block, all eight grazing sources are owner cores.
|
# With one parent block, all eight grazing sources are owner cores.
|
||||||
# Each is read for four fine ghost children: late explosion corrupts32.
|
# Each is read for four fine ghost children: late explosion corrupts32.
|
||||||
single = BlockLayout2D.build(NY, NX, NY, NX)
|
single = BlockLayout2D.build(NY, NX, NY, NX)
|
||||||
donors, targets, _ = blocked_base_tables(p, single, order)
|
donors, targets, _ = blocked_coarse_tables(p, single, order)
|
||||||
initial = single.to_blocked(dense)
|
initial = single.to_blocked(dense)
|
||||||
staged = initial.copy()
|
staged = initial.copy()
|
||||||
staged.ravel()[targets.astype(np.intp)] = arrivals
|
staged.ravel()[targets.astype(np.intp)] = arrivals
|
||||||
@@ -204,7 +205,7 @@ class BlockedBaseCPU(unittest.TestCase):
|
|||||||
level_blocks=[(3, 4), FINE_BLOCK],
|
level_blocks=[(3, 4), FINE_BLOCK],
|
||||||
level_objects=[(), (circle(actuation(.002)),)])
|
level_objects=[(), (circle(actuation(.002)),)])
|
||||||
try:
|
try:
|
||||||
backend.initialize(state(NY, NX, 1), state(*backend.hierarchy.pairs[0].fine_shape, 2))
|
backend.initialize(state(NY, NX, 1), state(*backend.patch.fine_shape, 2))
|
||||||
coarse, fine = backend._levels
|
coarse, fine = backend._levels
|
||||||
current, spare, fine_current = coarse.current, coarse.spare, fine.current
|
current, spare, fine_current = coarse.current, coarse.spare, fine.current
|
||||||
backend.step(2)
|
backend.step(2)
|
||||||
@@ -255,7 +256,7 @@ class BlockedBaseCPU(unittest.TestCase):
|
|||||||
with mocked_backend() as m:
|
with mocked_backend() as m:
|
||||||
backend = FixedPatchStepper(config("periodic"), context=m["context"], refinements=[BOUNDS],
|
backend = FixedPatchStepper(config("periodic"), context=m["context"], refinements=[BOUNDS],
|
||||||
level_blocks=[(3, 4), FINE_BLOCK])
|
level_blocks=[(3, 4), FINE_BLOCK])
|
||||||
backend.initialize(state(NY, NX, 1), state(*backend.hierarchy.pairs[0].fine_shape, 2))
|
backend.initialize(state(NY, NX, 1), state(*backend.patch.fine_shape, 2))
|
||||||
m["functions"]["BlockCoalesce"].side_effect = RuntimeError("injected parent stage failure")
|
m["functions"]["BlockCoalesce"].side_effect = RuntimeError("injected parent stage failure")
|
||||||
current = backend._levels[0].current
|
current = backend._levels[0].current
|
||||||
with self.assertRaisesRegex(RuntimeError, "injected parent stage"):
|
with self.assertRaisesRegex(RuntimeError, "injected parent stage"):
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ from CelerisLab.lbm.descriptors import D2Q9_EX, D2Q9_EY
|
|||||||
from CelerisLab.lbm.descriptors import SOLID
|
from CelerisLab.lbm.descriptors import SOLID
|
||||||
from CelerisLab.lbm.refinement import (ACTIVE, INNER_GHOST, OUTER_GHOST, FixedPatch2D,
|
from CelerisLab.lbm.refinement import (ACTIVE, INNER_GHOST, OUTER_GHOST, FixedPatch2D,
|
||||||
prepare_fine_boundary)
|
prepare_fine_boundary)
|
||||||
from CelerisLab.lbm.refinement_stepper import FixedPatchStepper, blocked_fine_tables
|
from CelerisLab.lbm.refinement_stepper import FixedPatchStepper
|
||||||
|
from CelerisLab.lbm.refinement_tables import blocked_fine_tables
|
||||||
|
|
||||||
NX, NY, BOUNDS, STEPS = 32, 24, (8, 6, 24, 18), 8
|
NX, NY, BOUNDS, STEPS = 32, 24, (8, 6, 24, 18), 8
|
||||||
FINE_BLOCKS = ((32, 40), (16, 20), (16, 10)) # fine window 32x40: 1x1, 2x2, 4x2
|
FINE_BLOCKS = ((32, 40), (16, 20), (16, 10)) # fine window 32x40: 1x1, 2x2, 4x2
|
||||||
@@ -125,7 +126,7 @@ class BlockedFineCPU(unittest.TestCase):
|
|||||||
patch.object(cuda, "mem_alloc", side_effect=alloc), patch.object(cuda, "memcpy_htod"):
|
patch.object(cuda, "mem_alloc", side_effect=alloc), patch.object(cuda, "memcpy_htod"):
|
||||||
backend = FixedPatchStepper(config("periodic"), context=context, refinements=[BOUNDS],
|
backend = FixedPatchStepper(config("periodic"), context=context, refinements=[BOUNDS],
|
||||||
level_blocks=[(12, 8), FINE_BLOCKS[0]])
|
level_blocks=[(12, 8), FINE_BLOCKS[0]])
|
||||||
self.assertEqual(len(backend.layouts[1].halo_dst), 0)
|
self.assertEqual(len(backend.fine_layout.halo_dst), 0)
|
||||||
self.assertIsNone(backend._levels[1].halo_dst)
|
self.assertIsNone(backend._levels[1].halo_dst)
|
||||||
backend._gather_halo(backend._levels[1], Mock()) # no launch without pairs
|
backend._gather_halo(backend._levels[1], Mock()) # no launch without pairs
|
||||||
module.get_function.return_value.assert_not_called()
|
module.get_function.return_value.assert_not_called()
|
||||||
@@ -178,7 +179,7 @@ class BlockedFineCPU(unittest.TestCase):
|
|||||||
backend = FixedPatchStepper(config("periodic"), context=context, refinements=[BOUNDS],
|
backend = FixedPatchStepper(config("periodic"), context=context, refinements=[BOUNDS],
|
||||||
level_blocks=[(12, 8), FINE_BLOCKS[1]],
|
level_blocks=[(12, 8), FINE_BLOCKS[1]],
|
||||||
level_objects=[(), tuple(objects)])
|
level_objects=[(), tuple(objects)])
|
||||||
backend.initialize(state(NY, NX, 1), state(*backend.hierarchy.pairs[0].fine_shape, 2))
|
backend.initialize(state(NY, NX, 1), state(*backend.patch.fine_shape, 2))
|
||||||
return backend, calls
|
return backend, calls
|
||||||
|
|
||||||
def test_same_buffer_halo_bfl_step_launch_order(self):
|
def test_same_buffer_halo_bfl_step_launch_order(self):
|
||||||
@@ -186,8 +187,8 @@ class BlockedFineCPU(unittest.TestCase):
|
|||||||
backend, calls = self.mocked_backend([circle(spec)])
|
backend, calls = self.mocked_backend([circle(spec)])
|
||||||
c, f = backend._levels
|
c, f = backend._levels
|
||||||
coarse, cur, spare = c.current, f.current, f.spare
|
coarse, cur, spare = c.current, f.current, f.spare
|
||||||
flags, kind = backend._level_boundaries[1].flags, backend._levels[1].kind
|
flags, kind = backend._device_boundaries[1].flags, backend._levels[1].kind
|
||||||
velocity = backend._level_boundaries[1].device.get("surface_velocity")
|
velocity = backend._device_boundaries[1].device.get("surface_velocity")
|
||||||
self.assertEqual(velocity is None, spec is None)
|
self.assertEqual(velocity is None, spec is None)
|
||||||
backend.step(1)
|
backend.step(1)
|
||||||
backend.close()
|
backend.close()
|
||||||
@@ -212,8 +213,8 @@ class BlockedFineCPU(unittest.TestCase):
|
|||||||
def test_actuation_without_velocity_buffer_fails_before_bfl(self):
|
def test_actuation_without_velocity_buffer_fails_before_bfl(self):
|
||||||
for speed in (0.0, .002): # any spec, even zero amplitude
|
for speed in (0.0, .002): # any spec, even zero amplitude
|
||||||
backend, calls = self.mocked_backend([circle(actuation(speed))])
|
backend, calls = self.mocked_backend([circle(actuation(speed))])
|
||||||
self.assertIsNotNone(backend._level_boundaries[1].tables["surface_velocity"])
|
self.assertIsNotNone(backend._fine_tables["surface_velocity"])
|
||||||
del backend._level_boundaries[1].device["surface_velocity"]
|
del backend._device_boundaries[1].device["surface_velocity"]
|
||||||
with self.assertRaisesRegex(RuntimeError, "Surface velocity device buffer is missing"):
|
with self.assertRaisesRegex(RuntimeError, "Surface velocity device buffer is missing"):
|
||||||
backend.step(1)
|
backend.step(1)
|
||||||
names = [name for name, _ in calls]
|
names = [name for name, _ in calls]
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ class NormalBuildGPU(unittest.TestCase):
|
|||||||
if not (x0 <= sx < x1 and y0 <= sy < y1):
|
if not (x0 <= sx < x1 and y0 <= sy < y1):
|
||||||
corners.add(target)
|
corners.add(target)
|
||||||
self.assertEqual(len(corners), 8)
|
self.assertEqual(len(corners), 8)
|
||||||
self.assertEqual(set(backend.hierarchy.pairs[0].coalesce_coarse.tolist()), targets)
|
self.assertEqual(set(backend.patch.coalesce_coarse.tolist()), targets)
|
||||||
backend.initialize(*initial)
|
backend.initialize(*initial)
|
||||||
before = discrete_mass(initial)
|
before = discrete_mass(initial)
|
||||||
for step in range(1, 9):
|
for step in range(1, 9):
|
||||||
@@ -383,8 +383,8 @@ class NormalBuildGPU(unittest.TestCase):
|
|||||||
expected = scalar_interval(*[a.astype(np.float64) for a in initial], bounds)
|
expected = scalar_interval(*[a.astype(np.float64) for a in initial], bounds)
|
||||||
with CudaContext(0) as context, FixedPatchStepper(cfg, context=context, refinements=[bounds],
|
with CudaContext(0) as context, FixedPatchStepper(cfg, context=context, refinements=[bounds],
|
||||||
level_blocks=[(13, 17), (14, 16)]) as backend:
|
level_blocks=[(13, 17), (14, 16)]) as backend:
|
||||||
self.assertEqual(backend.hierarchy.pairs[0].fine_shape, (14, 16))
|
self.assertEqual(backend.patch.fine_shape, (14, 16))
|
||||||
p = backend.hierarchy.pairs[0]
|
p = backend.patch
|
||||||
allocated = 2 * 4 * sum(int(np.prod(level.layout.blocked_shape)) for level in backend._levels) \
|
allocated = 2 * 4 * sum(int(np.prod(level.layout.blocked_shape)) for level in backend._levels) \
|
||||||
+ sum(a.nbytes for a in (p.ghost_fine, p.ghost_coarse, p.coalesce_coarse, p.coalesce_fine))
|
+ sum(a.nbytes for a in (p.ghost_fine, p.ghost_coarse, p.coalesce_coarse, p.coalesce_fine))
|
||||||
self.assertLess(allocated, 128*1024)
|
self.assertLess(allocated, 128*1024)
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ class CoupledGPU(unittest.TestCase):
|
|||||||
ddf_shifting=shifted)
|
ddf_shifting=shifted)
|
||||||
with FixedPatchStepper(cfg, context=context, refinements=[(4, 4, 12, 12)],
|
with FixedPatchStepper(cfg, context=context, refinements=[(4, 4, 12, 12)],
|
||||||
**storage) as backend:
|
**storage) as backend:
|
||||||
self.assertAlmostEqual(backend.hierarchy.pairs[0].tau_coarse, spec.tau_coarse)
|
self.assertAlmostEqual(backend.patch.tau_coarse, spec.tau_coarse)
|
||||||
self.assertAlmostEqual(backend.hierarchy.pairs[0].tau_fine, spec.tau_fine)
|
self.assertAlmostEqual(backend.patch.tau_fine, spec.tau_fine)
|
||||||
self.assertEqual(tuple(level.shape for level in backend._levels),
|
self.assertEqual(tuple(level.shape for level in backend._levels),
|
||||||
((9, 16, 16), (9, 24, 24)))
|
((9, 16, 16), (9, 24, 24)))
|
||||||
# Physical FP64 g is encoded by initialize before FP32 rounding.
|
# Physical FP64 g is encoded by initialize before FP32 rounding.
|
||||||
@@ -101,7 +101,7 @@ class CoupledGPU(unittest.TestCase):
|
|||||||
backend.step(1)
|
backend.step(1)
|
||||||
actual = backend.download()
|
actual = backend.download()
|
||||||
self.assertEqual(backend.step_count, 1)
|
self.assertEqual(backend.step_count, 1)
|
||||||
backend.hierarchy.pairs[0].check_synchronization(
|
backend.patch.check_synchronization(
|
||||||
1, next_state.tick, phase="postcollision_g")
|
1, next_state.tick, phase="postcollision_g")
|
||||||
for got, want, arrival in zip(actual, expected, arrivals):
|
for got, want, arrival in zip(actual, expected, arrivals):
|
||||||
np.testing.assert_array_equal(np.isnan(got), np.isnan(want))
|
np.testing.assert_array_equal(np.isnan(got), np.isnan(want))
|
||||||
@@ -165,13 +165,13 @@ class CoupledGPU(unittest.TestCase):
|
|||||||
with self.assertRaisesRegex(RuntimeError, "failed"):
|
with self.assertRaisesRegex(RuntimeError, "failed"):
|
||||||
operation()
|
operation()
|
||||||
raw = level_storage(fine)
|
raw = level_storage(fine)
|
||||||
self.assertTrue(np.isfinite(raw.ravel()[backend.hierarchy.pairs[0].coalesce_fine]).all())
|
self.assertTrue(np.isfinite(raw.ravel()[backend.patch.coalesce_fine]).all())
|
||||||
self.assertFalse(context.is_closed)
|
self.assertFalse(context.is_closed)
|
||||||
print("partial failure: two fine hops completed, no coarse step published; cleanup PASS", flush=True)
|
print("partial failure: two fine hops completed, no coarse step published; cleanup PASS", flush=True)
|
||||||
|
|
||||||
def eight_steps(self, backend, spec, maps):
|
def eight_steps(self, backend, spec, maps):
|
||||||
"""Rest and seeded nonequilibrium, 8 steps against the scalar reference."""
|
"""Rest and seeded nonequilibrium, 8 steps against the scalar reference."""
|
||||||
p = backend.hierarchy.pairs[0]
|
p = backend.patch
|
||||||
for case in ("rest", "nonequilibrium_seed1"):
|
for case in ("rest", "nonequilibrium_seed1"):
|
||||||
with self.subTest(case=case):
|
with self.subTest(case=case):
|
||||||
state = ref.make_state(spec, velocity=(0, 0) if case == "rest" else (.03, -.02))
|
state = ref.make_state(spec, velocity=(0, 0) if case == "rest" else (.03, -.02))
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ class JetStepperCPU(unittest.TestCase):
|
|||||||
C = (16.0, 12.0)
|
C = (16.0, 12.0)
|
||||||
whole = Jet(Sector(C, .1, math.pi, 3.), Radial(C, inward=False), amplitude=0.)
|
whole = Jet(Sector(C, .1, math.pi, 3.), Radial(C, inward=False), amplitude=0.)
|
||||||
backend, _ = mocked([circle()], [whole], FINE_BLOCKS[1])
|
backend, _ = mocked([circle()], [whole], FINE_BLOCKS[1])
|
||||||
_, jet_id = backend._level_boundaries[1].jet_cache
|
_, jet_id = backend._jet_cache
|
||||||
self.assertGreater(len(np.unique(backend._level_boundaries[1].tables["block"][jet_id == 0])), 1)
|
self.assertGreater(len(np.unique(backend._fine_tables["block"][jet_id == 0])), 1)
|
||||||
backend.close()
|
backend.close()
|
||||||
|
|
||||||
def test_fine_ownership_uses_remapped_physical_owner(self):
|
def test_fine_ownership_uses_remapped_physical_owner(self):
|
||||||
@@ -76,10 +76,10 @@ class JetStepperCPU(unittest.TestCase):
|
|||||||
source = SimObject(99, geometry=CircleGeometry(*C, 2.15), center=C, radius=2.15)
|
source = SimObject(99, geometry=CircleGeometry(*C, 2.15), center=C, radius=2.15)
|
||||||
whole = Jet(Sector(C, .1, math.pi, 3.), Radial(C, inward=False), amplitude=0.)
|
whole = Jet(Sector(C, .1, math.pi, 3.), Radial(C, inward=False), amplitude=0.)
|
||||||
backend, _ = mocked([source], [whole], FINE_BLOCKS[1])
|
backend, _ = mocked([source], [whole], FINE_BLOCKS[1])
|
||||||
_, jet_id = backend._level_boundaries[1].jet_cache
|
_, jet_id = backend._jet_cache
|
||||||
owners = backend.boundaries[1].curved_host[3][jet_id == 0]
|
owners = backend.fine_boundary.curved_host[3][jet_id == 0]
|
||||||
self.assertEqual(np.unique(owners).tolist(), [0])
|
self.assertEqual(np.unique(owners).tolist(), [0])
|
||||||
self.assertGreater(len(np.unique(backend._level_boundaries[1].tables["block"][jet_id == 0])), 1)
|
self.assertGreater(len(np.unique(backend._fine_tables["block"][jet_id == 0])), 1)
|
||||||
self.assertEqual(source.obj_id, 99)
|
self.assertEqual(source.obj_id, 99)
|
||||||
backend.close()
|
backend.close()
|
||||||
cfg = config("periodic")
|
cfg = config("periodic")
|
||||||
@@ -97,8 +97,8 @@ class JetStepperCPU(unittest.TestCase):
|
|||||||
def test_blocked_jet_velocity_matches_legacy_rows(self):
|
def test_blocked_jet_velocity_matches_legacy_rows(self):
|
||||||
legacy, _ = mocked([circle(actuation(SPEED))], (), FINE_BLOCKS[1])
|
legacy, _ = mocked([circle(actuation(SPEED))], (), FINE_BLOCKS[1])
|
||||||
driven, _ = mocked([circle()], [jet()], FINE_BLOCKS[1])
|
driven, _ = mocked([circle()], [jet()], FINE_BLOCKS[1])
|
||||||
a = legacy._level_boundaries[1].tables["surface_velocity"]
|
a = legacy._fine_tables["surface_velocity"]
|
||||||
b = driven._level_boundaries[1].tables["surface_velocity"]
|
b = driven._fine_tables["surface_velocity"]
|
||||||
diff = np.abs(a - b).max()
|
diff = np.abs(a - b).max()
|
||||||
print(f"fine jet vs legacy velocity rows: max |dU| = {diff:.3e} (exact={diff == 0})")
|
print(f"fine jet vs legacy velocity rows: max |dU| = {diff:.3e} (exact={diff == 0})")
|
||||||
self.assertLessEqual(diff, 4 * np.finfo(np.float32).eps * SPEED)
|
self.assertLessEqual(diff, 4 * np.finfo(np.float32).eps * SPEED)
|
||||||
@@ -106,20 +106,20 @@ class JetStepperCPU(unittest.TestCase):
|
|||||||
|
|
||||||
def test_blocked_amplitude_update_rewrites_only_velocity_column(self):
|
def test_blocked_amplitude_update_rewrites_only_velocity_column(self):
|
||||||
backend, htod = mocked([circle()], [jet()], FINE_BLOCKS[1])
|
backend, htod = mocked([circle()], [jet()], FINE_BLOCKS[1])
|
||||||
before = backend._level_boundaries[1].tables["surface_velocity"].copy()
|
before = backend._fine_tables["surface_velocity"].copy()
|
||||||
device = backend._level_boundaries[1].device["surface_velocity"]
|
device = backend._device_boundaries[1].device["surface_velocity"]
|
||||||
with patch.object(cuda, "memcpy_htod", htod):
|
with patch.object(cuda, "memcpy_htod", htod):
|
||||||
htod.reset_mock()
|
htod.reset_mock()
|
||||||
backend.set_jet_amplitude(0, -SPEED, level=1)
|
backend.set_jet_amplitude(0, -SPEED)
|
||||||
(ptr, sent), _ = htod.call_args
|
(ptr, sent), _ = htod.call_args
|
||||||
self.assertEqual(htod.call_count, 1)
|
self.assertEqual(htod.call_count, 1)
|
||||||
self.assertIs(ptr, device)
|
self.assertIs(ptr, device)
|
||||||
np.testing.assert_allclose(sent, -before, rtol=1e-6, atol=0)
|
np.testing.assert_allclose(sent, -before, rtol=1e-6, atol=0)
|
||||||
np.testing.assert_array_equal(backend._level_boundaries[1].tables["surface_velocity"], sent)
|
np.testing.assert_array_equal(backend._fine_tables["surface_velocity"], sent)
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
backend.set_jet_amplitude(0, 1.5, level=1)
|
backend.set_jet_amplitude(0, 1.5)
|
||||||
self.assertEqual(htod.call_count, 1)
|
self.assertEqual(htod.call_count, 1)
|
||||||
self.assertEqual(backend.jets[1][0].amplitude, -SPEED)
|
self.assertEqual(backend._jets[0].amplitude, -SPEED)
|
||||||
backend.close()
|
backend.close()
|
||||||
|
|
||||||
def test_copy_failure_marks_failed_and_keeps_host_amplitude(self):
|
def test_copy_failure_marks_failed_and_keeps_host_amplitude(self):
|
||||||
@@ -128,24 +128,24 @@ class JetStepperCPU(unittest.TestCase):
|
|||||||
status = backend._status
|
status = backend._status
|
||||||
for bad, error in ((1.5, ValueError), (float("nan"), ValueError)):
|
for bad, error in ((1.5, ValueError), (float("nan"), ValueError)):
|
||||||
with self.assertRaises(error):
|
with self.assertRaises(error):
|
||||||
backend.set_jet_amplitude(0, bad, level=1)
|
backend.set_jet_amplitude(0, bad)
|
||||||
self.assertEqual(backend._status, status) # invalid input stays usable
|
self.assertEqual(backend._status, status) # invalid input stays usable
|
||||||
with self.assertRaises(IndexError):
|
with self.assertRaises(IndexError):
|
||||||
backend.set_jet_amplitude(1, -SPEED, level=1)
|
backend.set_jet_amplitude(1, -SPEED)
|
||||||
self.assertEqual(backend._status, status)
|
self.assertEqual(backend._status, status)
|
||||||
before = backend._level_boundaries[1].tables["surface_velocity"].copy()
|
before = backend._fine_tables["surface_velocity"].copy()
|
||||||
boom = Mock(side_effect=RuntimeError("copy failed"))
|
boom = Mock(side_effect=RuntimeError("copy failed"))
|
||||||
with patch.object(cuda, "memcpy_htod", boom), patch.object(cuda, "memcpy_htod_async", boom), \
|
with patch.object(cuda, "memcpy_htod", boom), patch.object(cuda, "memcpy_htod_async", boom), \
|
||||||
patch.object(cuda, "pagelocked_empty", side_effect=lambda shape, dtype: np.empty(shape, dtype)):
|
patch.object(cuda, "pagelocked_empty", side_effect=lambda shape, dtype: np.empty(shape, dtype)):
|
||||||
with self.assertRaisesRegex(RuntimeError, "copy failed"):
|
with self.assertRaisesRegex(RuntimeError, "copy failed"):
|
||||||
backend.set_jet_amplitude(0, -SPEED, level=1)
|
backend.set_jet_amplitude(0, -SPEED)
|
||||||
self.assertEqual(backend._status, "failed", fine_block)
|
self.assertEqual(backend._status, "failed", fine_block)
|
||||||
self.assertEqual(backend.jets[1][0].amplitude, SPEED)
|
self.assertEqual(backend._jets[0].amplitude, SPEED)
|
||||||
np.testing.assert_array_equal(backend._level_boundaries[1].tables["surface_velocity"], before)
|
np.testing.assert_array_equal(backend._fine_tables["surface_velocity"], before)
|
||||||
with self.assertRaisesRegex(RuntimeError, "failed"):
|
with self.assertRaisesRegex(RuntimeError, "failed"):
|
||||||
backend.step(1)
|
backend.step(1)
|
||||||
with self.assertRaisesRegex(RuntimeError, "failed"):
|
with self.assertRaisesRegex(RuntimeError, "failed"):
|
||||||
backend.set_jet_amplitude(0, -SPEED, level=1)
|
backend.set_jet_amplitude(0, -SPEED)
|
||||||
backend.close()
|
backend.close()
|
||||||
|
|
||||||
def test_jet_overlapping_legacy_aperture_rejected_before_cuda(self):
|
def test_jet_overlapping_legacy_aperture_rejected_before_cuda(self):
|
||||||
@@ -170,7 +170,7 @@ def run(mode, fine_block, *, objects, jets, switch=None):
|
|||||||
backend.step(STEPS)
|
backend.step(STEPS)
|
||||||
else:
|
else:
|
||||||
backend.step(STEPS // 2)
|
backend.step(STEPS // 2)
|
||||||
backend.set_jet_amplitude(0, switch, level=1)
|
backend.set_jet_amplitude(0, switch)
|
||||||
backend.step(STEPS - STEPS // 2)
|
backend.step(STEPS - STEPS // 2)
|
||||||
return backend.download(), raw(backend)
|
return backend.download(), raw(backend)
|
||||||
|
|
||||||
|
|||||||
@@ -36,14 +36,14 @@ def level_fine(patch_set, windows):
|
|||||||
"""Level-wide fine array with each patch window filled from `windows`."""
|
"""Level-wide fine array with each patch window filled from `windows`."""
|
||||||
out = np.full((9, *patch_set.fine_shape), np.nan, np.float32)
|
out = np.full((9, *patch_set.fine_shape), np.nan, np.float32)
|
||||||
for p, w in zip(patch_set.patches, windows):
|
for p, w in zip(patch_set.patches, windows):
|
||||||
y0, x0 = p.fine_level_offset
|
y0, x0 = p.fine_level_origin
|
||||||
out[:, y0:y0 + p.fine_shape[0], x0:x0 + p.fine_shape[1]] = w
|
out[:, y0:y0 + p.fine_shape[0], x0:x0 + p.fine_shape[1]] = w
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
def window(patch_set, index, fine):
|
def window(patch_set, index, fine):
|
||||||
p = patch_set.patches[index]
|
p = patch_set.patches[index]
|
||||||
y0, x0 = p.fine_level_offset
|
y0, x0 = p.fine_level_origin
|
||||||
return fine[:, y0:y0 + p.fine_shape[0], x0:x0 + p.fine_shape[1]]
|
return fine[:, y0:y0 + p.fine_shape[0], x0:x0 + p.fine_shape[1]]
|
||||||
|
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ class MultipatchBodiesGPU(unittest.TestCase):
|
|||||||
for step in self.STEPS:
|
for step in self.STEPS:
|
||||||
if switch is not None and step == 8:
|
if switch is not None and step == 8:
|
||||||
for index, amplitude in switch:
|
for index, amplitude in switch:
|
||||||
backend.set_jet_amplitude(index, amplitude, level=1)
|
backend.set_jet_amplitude(index, amplitude)
|
||||||
backend.step(step - backend.step_count)
|
backend.step(step - backend.step_count)
|
||||||
out.append(backend.download() + raw(backend))
|
out.append(backend.download() + raw(backend))
|
||||||
return out
|
return out
|
||||||
|
|||||||
@@ -128,10 +128,8 @@ class ThreeLevelCPU(unittest.TestCase):
|
|||||||
run = mocked_child()
|
run = mocked_child()
|
||||||
b = run._levels[2].boundary
|
b = run._levels[2].boundary
|
||||||
before = b.tables["surface_velocity"].copy()
|
before = b.tables["surface_velocity"].copy()
|
||||||
with self.assertRaises(TypeError):
|
|
||||||
run.set_jet_amplitude(0, -.002) # level is required
|
|
||||||
with self.assertRaises(IndexError):
|
with self.assertRaises(IndexError):
|
||||||
run.set_jet_amplitude(0, -.002, level=1) # level 1 has no Jets
|
run.set_jet_amplitude(0, -.002) # default stays L1
|
||||||
for level in (0, 3, True):
|
for level in (0, 3, True):
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
run.set_jet_amplitude(0, -.002, level=level)
|
run.set_jet_amplitude(0, -.002, level=level)
|
||||||
@@ -140,7 +138,7 @@ class ThreeLevelCPU(unittest.TestCase):
|
|||||||
self.assertEqual(upload.call_count, 1)
|
self.assertEqual(upload.call_count, 1)
|
||||||
self.assertIs(upload.call_args.args[0], b.device["surface_velocity"])
|
self.assertIs(upload.call_args.args[0], b.device["surface_velocity"])
|
||||||
np.testing.assert_array_equal(b.tables["surface_velocity"], -before)
|
np.testing.assert_array_equal(b.tables["surface_velocity"], -before)
|
||||||
self.assertEqual(run.jets[1], ())
|
self.assertEqual(run._jets, ())
|
||||||
with patch.object(cuda, "memcpy_htod", side_effect=RuntimeError("copy failed")):
|
with patch.object(cuda, "memcpy_htod", side_effect=RuntimeError("copy failed")):
|
||||||
with self.assertRaisesRegex(RuntimeError, "copy failed"):
|
with self.assertRaisesRegex(RuntimeError, "copy failed"):
|
||||||
run.set_jet_amplitude(0, .002, level=2)
|
run.set_jet_amplitude(0, .002, level=2)
|
||||||
@@ -152,17 +150,17 @@ class ThreeLevelCPU(unittest.TestCase):
|
|||||||
def test_child_public_masks_include_solids(self):
|
def test_child_public_masks_include_solids(self):
|
||||||
from CelerisLab.lbm.descriptors import SOLID
|
from CelerisLab.lbm.descriptors import SOLID
|
||||||
run = mocked_child()
|
run = mocked_child()
|
||||||
solid = run.boundaries[2].flags.reshape(run.hierarchy.pairs[1].fine_shape) & SOLID != 0
|
solid = run.child_boundary.flags.reshape(run.child_patch.fine_shape) & SOLID != 0
|
||||||
self.assertTrue(solid.any())
|
self.assertTrue(solid.any())
|
||||||
self.assertFalse(run.leaf[2][solid].any())
|
self.assertFalse(run._child_leaf[solid].any())
|
||||||
arrays = [np.ones(level.shape, np.float32) for level in run._levels]
|
arrays = [np.ones(level.shape, np.float32) for level in run._levels]
|
||||||
run._mask_nonleaves(*arrays)
|
run._mask_nonleaves(*arrays)
|
||||||
self.assertTrue(np.isnan(arrays[2][:, solid]).all())
|
self.assertTrue(np.isnan(arrays[2][:, solid]).all())
|
||||||
with patch.object(cuda, "memcpy_htod") as upload:
|
with patch.object(cuda, "memcpy_htod") as upload:
|
||||||
run.initialize(*arrays)
|
run.initialize(*arrays)
|
||||||
sent = upload.call_args_list[-1].args[1]
|
sent = upload.call_args_list[-1].args[1]
|
||||||
dense = run.layouts[2].to_dense(sent)
|
dense = run.child_layout.to_dense(sent)
|
||||||
self.assertTrue(np.isnan(dense[:, ~run.leaf[2]]).all())
|
self.assertTrue(np.isnan(dense[:, ~run._child_leaf]).all())
|
||||||
run.close()
|
run.close()
|
||||||
|
|
||||||
|
|
||||||
@@ -171,7 +169,7 @@ class ThreeLevelGPU(unittest.TestCase):
|
|||||||
from CelerisLab.cuda.context import CudaContext
|
from CelerisLab.cuda.context import CudaContext
|
||||||
feq = np.asarray(compute_feq_d2q9(1.0, .03, -.02), np.float32)[:, None, None]
|
feq = np.asarray(compute_feq_d2q9(1.0, .03, -.02), np.float32)[:, None, None]
|
||||||
with CudaContext(0) as ctx, FixedPatchStepper(CFG, context=ctx, **KW) as run:
|
with CudaContext(0) as ctx, FixedPatchStepper(CFG, context=ctx, **KW) as run:
|
||||||
shapes = ((16, 16), run.hierarchy.pairs[0].fine_shape, run.hierarchy.pairs[1].fine_shape)
|
shapes = ((16, 16), run.patch.fine_shape, run.child_patch.fine_shape)
|
||||||
run.initialize(*(np.broadcast_to(feq, (9, *s)).copy() for s in shapes))
|
run.initialize(*(np.broadcast_to(feq, (9, *s)).copy() for s in shapes))
|
||||||
run.step(8)
|
run.step(8)
|
||||||
self.assertEqual(run.step_count, 8)
|
self.assertEqual(run.step_count, 8)
|
||||||
@@ -184,7 +182,7 @@ class ThreeLevelGPU(unittest.TestCase):
|
|||||||
def test_nonequilibrium_masks_and_fresh_middle_feeds(self):
|
def test_nonequilibrium_masks_and_fresh_middle_feeds(self):
|
||||||
from CelerisLab.cuda.context import CudaContext
|
from CelerisLab.cuda.context import CudaContext
|
||||||
with CudaContext(0) as ctx, FixedPatchStepper(CFG, context=ctx, **KW) as run:
|
with CudaContext(0) as ctx, FixedPatchStepper(CFG, context=ctx, **KW) as run:
|
||||||
p1, p2 = run.hierarchy.pairs[0], run.hierarchy.pairs[1]
|
p1, p2 = run.patch, run.child_patch
|
||||||
run.initialize(state(16, 16, 1), state(*p1.fine_shape, 2), state(*p2.fine_shape, 3))
|
run.initialize(state(16, 16, 1), state(*p1.fine_shape, 2), state(*p2.fine_shape, 3))
|
||||||
f = run._levels[1]
|
f = run._levels[1]
|
||||||
feeds, original = [], run._advance
|
feeds, original = [], run._advance
|
||||||
@@ -197,7 +195,7 @@ class ThreeLevelGPU(unittest.TestCase):
|
|||||||
run.step(1)
|
run.step(1)
|
||||||
self.assertEqual(feeds, [cur, spare]) # current G, then the new G of hop 1
|
self.assertEqual(feeds, [cur, spare]) # current G, then the new G of hop 1
|
||||||
run.step(7)
|
run.step(7)
|
||||||
middle = run.hierarchy.kinds[1]
|
middle = run._middle_kind
|
||||||
for g, leaf in zip(run.download(), (~p1.covered, middle == ACTIVE, p2.fine_kind == ACTIVE)):
|
for g, leaf in zip(run.download(), (~p1.covered, middle == ACTIVE, p2.fine_kind == ACTIVE)):
|
||||||
self.assertTrue(np.isfinite(g[:, leaf]).all())
|
self.assertTrue(np.isfinite(g[:, leaf]).all())
|
||||||
self.assertTrue(np.isnan(g[:, ~leaf]).all())
|
self.assertTrue(np.isnan(g[:, ~leaf]).all())
|
||||||
@@ -229,15 +227,15 @@ class ThreeLevelBodyGPU(unittest.TestCase):
|
|||||||
if stop == 4 and switch:
|
if stop == 4 and switch:
|
||||||
run.set_jet_amplitude(0, -.002, level=2)
|
run.set_jet_amplitude(0, -.002, level=2)
|
||||||
public, stored = run.download(), raw(run)
|
public, stored = run.download(), raw(run)
|
||||||
leaves = [~run.hierarchy.pairs[0].covered, run.leaf[1], run.leaf[2]]
|
leaves = [~run.patch.covered, run._fine_leaf, run._child_leaf]
|
||||||
if mode != "periodic":
|
if mode != "periodic":
|
||||||
leaves[0][[0, -1]] = False
|
leaves[0][[0, -1]] = False
|
||||||
for g, mask in zip(public, leaves):
|
for g, mask in zip(public, leaves):
|
||||||
self.assertTrue(np.isfinite(g[:, mask]).all())
|
self.assertTrue(np.isfinite(g[:, mask]).all())
|
||||||
self.assertTrue(np.isnan(g[:, ~mask]).all())
|
self.assertTrue(np.isnan(g[:, ~mask]).all())
|
||||||
invalid = [run.hierarchy.pairs[0].covered.copy(), run.hierarchy.kinds[1] == OUTER_GHOST,
|
invalid = [run.patch.covered.copy(), run._middle_kind == OUTER_GHOST,
|
||||||
(run.hierarchy.pairs[1].fine_kind == OUTER_GHOST) |
|
(run.child_patch.fine_kind == OUTER_GHOST) |
|
||||||
((run.boundaries[2].flags.reshape(run.hierarchy.pairs[1].fine_shape) & SOLID) != 0)]
|
((run.child_boundary.flags.reshape(run.child_patch.fine_shape) & SOLID) != 0)]
|
||||||
if mode != "periodic":
|
if mode != "periodic":
|
||||||
invalid[0][[0, -1]] = True
|
invalid[0][[0, -1]] = True
|
||||||
for g, mask in zip(stored, invalid):
|
for g, mask in zip(stored, invalid):
|
||||||
|
|||||||
@@ -54,9 +54,9 @@ def jets(speed):
|
|||||||
|
|
||||||
|
|
||||||
def frames(run):
|
def frames(run):
|
||||||
middle_origin = np.asarray(run.hierarchy.pairs[0].fine_origin)
|
middle_origin = np.asarray(run.patch.fine_origin)
|
||||||
return ((np.zeros(2), 1.), (middle_origin, .5),
|
return ((np.zeros(2), 1.), (middle_origin, .5),
|
||||||
(middle_origin + .5*np.asarray(run.hierarchy.pairs[1].fine_origin), .25))
|
(middle_origin + .5*np.asarray(run.child_patch.fine_origin), .25))
|
||||||
|
|
||||||
|
|
||||||
def physical_state(shape, origin, spacing):
|
def physical_state(shape, origin, spacing):
|
||||||
@@ -92,15 +92,15 @@ class ThreeLevelMultipatchGPU(unittest.TestCase):
|
|||||||
for index in range(len(drive)):
|
for index in range(len(drive)):
|
||||||
run.set_jet_amplitude(index, -.002, level=2)
|
run.set_jet_amplitude(index, -.002, level=2)
|
||||||
public, stored = run.download(), raw(run)
|
public, stored = run.download(), raw(run)
|
||||||
masks = [~run.hierarchy.pairs[0].covered, run.leaf[1], run.leaf[2]]
|
masks = [~run.patch.covered, run._fine_leaf, run._child_leaf]
|
||||||
if mode != 'periodic':
|
if mode != 'periodic':
|
||||||
masks[0][[0, -1]] = False
|
masks[0][[0, -1]] = False
|
||||||
invalid = [run.hierarchy.pairs[0].covered.copy(), run.hierarchy.kinds[1] == OUTER_GHOST,
|
invalid = [run.patch.covered.copy(), run._middle_kind == OUTER_GHOST,
|
||||||
run.hierarchy.pairs[1].fine_kind == OUTER_GHOST]
|
run.child_patch.fine_kind == OUTER_GHOST]
|
||||||
if mode != 'periodic':
|
if mode != 'periodic':
|
||||||
invalid[0][[0, -1]] = True
|
invalid[0][[0, -1]] = True
|
||||||
if run.boundaries[2] is not None:
|
if run.child_boundary is not None:
|
||||||
invalid[2] |= (run.boundaries[2].flags.reshape(run.hierarchy.pairs[1].fine_shape) & SOLID) != 0
|
invalid[2] |= (run.child_boundary.flags.reshape(run.child_patch.fine_shape) & SOLID) != 0
|
||||||
for level, (g, mask, dense, bad) in enumerate(zip(public, masks, stored, invalid)):
|
for level, (g, mask, dense, bad) in enumerate(zip(public, masks, stored, invalid)):
|
||||||
label = f'{mode} stop={stop} level={level}'
|
label = f'{mode} stop={stop} level={level}'
|
||||||
self.assertTrue(np.isfinite(g[:, mask]).all(), label)
|
self.assertTrue(np.isfinite(g[:, mask]).all(), label)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ def initial(run, uniform):
|
|||||||
|
|
||||||
class RuntimeChildRemapGPU(unittest.TestCase):
|
class RuntimeChildRemapGPU(unittest.TestCase):
|
||||||
def check_public(self, run):
|
def check_public(self, run):
|
||||||
for array, leaf in zip(run.download(), (~run.hierarchy.pairs[0].covered, run.leaf[1], run.leaf[2])):
|
for array, leaf in zip(run.download(), (~run.patch.covered, run._fine_leaf, run._child_leaf)):
|
||||||
self.assertTrue(np.isfinite(array[:, leaf]).all())
|
self.assertTrue(np.isfinite(array[:, leaf]).all())
|
||||||
self.assertTrue(np.isnan(array[:, ~leaf]).all())
|
self.assertTrue(np.isnan(array[:, ~leaf]).all())
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ class RuntimeChildRemapGPU(unittest.TestCase):
|
|||||||
old.initialize(*initial(old, False))
|
old.initialize(*initial(old, False))
|
||||||
old.step(2)
|
old.step(2)
|
||||||
before = old.download()
|
before = old.download()
|
||||||
new = owned.enter_context(old.remap_finest(A))
|
new = owned.enter_context(old.remap_child(A))
|
||||||
self.assertEqual((old.step_count, new.step_count), (2, 2))
|
self.assertEqual((old.step_count, new.step_count), (2, 2))
|
||||||
self.check_reset(new, before)
|
self.check_reset(new, before)
|
||||||
for got, want in zip(old.download(), before): exact(self, got, want, 'old unchanged')
|
for got, want in zip(old.download(), before): exact(self, got, want, 'old unchanged')
|
||||||
@@ -75,12 +75,12 @@ class RuntimeChildRemapGPU(unittest.TestCase):
|
|||||||
current.step(2)
|
current.step(2)
|
||||||
for rectangles in (B, A):
|
for rectangles in (B, A):
|
||||||
before, count = current.download(), current.step_count
|
before, count = current.download(), current.step_count
|
||||||
old_mesh = RemapTopology.from_config(CFG, current.hierarchy.pairs[0], current.hierarchy.pairs[1].bounds)
|
old_mesh = RemapTopology.from_config(CFG, current.patch, current.child_patch.bounds)
|
||||||
new_mesh = RemapTopology.from_config(CFG, current.hierarchy.pairs[0], rectangles)
|
new_mesh = RemapTopology.from_config(CFG, current.patch, rectangles)
|
||||||
expected = remap_synchronized(old_mesh, new_mesh, before, base_count=count,
|
expected = remap_synchronized(old_mesh, new_mesh, before, base_count=count,
|
||||||
level_steps=(count, 2*count, 4*count), phase='postcollision_g').g
|
level_steps=(count, 2*count, 4*count), phase='postcollision_g').g
|
||||||
previous = current
|
previous = current
|
||||||
current = owned.enter_context(previous.remap_finest(rectangles))
|
current = owned.enter_context(previous.remap_child(rectangles))
|
||||||
self.assertEqual(current.step_count, count)
|
self.assertEqual(current.step_count, count)
|
||||||
self.check_reset(current, expected)
|
self.check_reset(current, expected)
|
||||||
for got, want, old_g, old_leaf, new_leaf in zip(
|
for got, want, old_g, old_leaf, new_leaf in zip(
|
||||||
|
|||||||
@@ -8,17 +8,14 @@ from contextlib import ExitStack
|
|||||||
import unittest
|
import unittest
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from CelerisLab.cuda.context import CudaContext
|
from CelerisLab.cuda.context import CudaContext
|
||||||
from CelerisLab.lbm.refinement_stepper import FixedPatchStepper
|
from research.shear_refinement.example import (CHILDREN, make, initialize, observe,
|
||||||
from research.shear_refinement.example import (CFG, PARENTS, CHILDREN, initialize,
|
topology, objects)
|
||||||
observe, topology, objects)
|
|
||||||
|
|
||||||
|
|
||||||
class ShearRefinementGPU(unittest.TestCase):
|
class ShearRefinementGPU(unittest.TestCase):
|
||||||
def test_measured_growth_matches_explicit_manual_continuation(self):
|
def test_measured_growth_matches_explicit_manual_continuation(self):
|
||||||
with CudaContext(0) as ctx, ExitStack() as owned:
|
with CudaContext(0) as ctx, ExitStack() as owned:
|
||||||
old = owned.enter_context(FixedPatchStepper(
|
old = owned.enter_context(make(ctx))
|
||||||
CFG, context=ctx, refinements=[PARENTS, CHILDREN],
|
|
||||||
level_blocks=[(4, 4)] * 3, level_objects=[(), (), objects()]))
|
|
||||||
initialize(old)
|
initialize(old)
|
||||||
old.step(2)
|
old.step(2)
|
||||||
before = old.download()
|
before = old.download()
|
||||||
@@ -28,10 +25,10 @@ class ShearRefinementGPU(unittest.TestCase):
|
|||||||
self.assertGreater(report['requested'], 0)
|
self.assertGreater(report['requested'], 0)
|
||||||
self.assertEqual(report['corridor_unknown'], 0)
|
self.assertEqual(report['corridor_unknown'], 0)
|
||||||
self.assertEqual(report['corridor_unavailable'], 0)
|
self.assertEqual(report['corridor_unavailable'], 0)
|
||||||
auto = owned.enter_context(old.remap_finest(chosen, objects=objects()))
|
auto = owned.enter_context(old.remap_child(chosen, child_objects=objects()))
|
||||||
explicit = owned.enter_context(old.remap_finest(manual, objects=objects()))
|
explicit = owned.enter_context(old.remap_child(manual, child_objects=objects()))
|
||||||
for run in (auto, explicit):
|
for run in (auto, explicit):
|
||||||
self.assertEqual(run.hierarchy.pairs[1].bounds[0], CHILDREN[0])
|
self.assertEqual(run.child_patch.bounds[0], CHILDREN[0])
|
||||||
self.assertEqual(run.step_count, 2)
|
self.assertEqual(run.step_count, 2)
|
||||||
run.step(2)
|
run.step(2)
|
||||||
for a, b, leaf in zip(auto.download(), explicit.download(), topology(manual).leaf_masks):
|
for a, b, leaf in zip(auto.download(), explicit.download(), topology(manual).leaf_masks):
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ def census(run,index,buffer):
|
|||||||
"""Independent physical link/face census of actual staged receiver windows."""
|
"""Independent physical link/face census of actual staged receiver windows."""
|
||||||
level=run._levels[index];layout=level.layout;b=level.boundary
|
level=run._levels[index];layout=level.layout;b=level.boundary
|
||||||
stored=download(buffer,layout.blocked_shape);dense=layout.to_dense(stored)
|
stored=download(buffer,layout.blocked_shape);dense=layout.to_dense(stored)
|
||||||
leaf=run.leaf[index];ny,nx=leaf.shape
|
leaf=run._leaf[index];ny,nx=leaf.shape
|
||||||
solid=(b.host.flags.reshape(ny,nx)&SOLID)!=0
|
solid=(b.host.flags.reshape(ny,nx)&SOLID)!=0
|
||||||
y,x=np.nonzero(leaf);owner=layout.owner[y,x].astype(np.intp)
|
y,x=np.nonzero(leaf);owner=layout.owner[y,x].astype(np.intp)
|
||||||
cells=int(np.prod(layout.local_shape));block,cell=np.divmod(owner,cells)
|
cells=int(np.prod(layout.local_shape));block,cell=np.divmod(owner,cells)
|
||||||
@@ -93,7 +93,7 @@ class Ledger:
|
|||||||
self.max_row=max(self.max_row,float(abs(raw.astype(float)-independent).max()))
|
self.max_row=max(self.max_row,float(abs(raw.astype(float)-independent).max()))
|
||||||
self.original(src,dst,*args,**kwargs);run._stream.synchronize()
|
self.original(src,dst,*args,**kwargs);run._stream.synchronize()
|
||||||
after=level.layout.to_dense(download(dst,level.layout.blocked_shape))
|
after=level.layout.to_dense(download(dst,level.layout.blocked_shape))
|
||||||
change=raw_totals(after,run.leaf[index])-before
|
change=raw_totals(after,run._leaf[index])-before
|
||||||
expected=external+np.array([-independent[:,0].sum(),-independent[:,1].sum(),independent[:,3].sum()])
|
expected=external+np.array([-independent[:,0].sum(),-independent[:,1].sum(),independent[:,3].sum()])
|
||||||
np.testing.assert_allclose(change,expected,rtol=2e-4,atol=8e-5)
|
np.testing.assert_allclose(change,expected,rtol=2e-4,atol=8e-5)
|
||||||
self.max_balance=max(self.max_balance,float(abs(change-expected).max()))
|
self.max_balance=max(self.max_balance,float(abs(change-expected).max()))
|
||||||
@@ -102,7 +102,7 @@ class Ledger:
|
|||||||
|
|
||||||
def step(self):
|
def step(self):
|
||||||
run=self.run;self.samples.clear();run.step()
|
run=self.run;self.samples.clear();run.step()
|
||||||
b=run._level_boundaries[self.index];h=run._exchange_spacing[self.index]
|
b=run._device_boundaries[self.index];h=run._exchange_spacing[self.index]
|
||||||
self.test.assertEqual(len(self.samples),2**self.index)
|
self.test.assertEqual(len(self.samples),2**self.index)
|
||||||
expected=np.zeros_like(self.samples[0],dtype=np.float64)
|
expected=np.zeros_like(self.samples[0],dtype=np.float64)
|
||||||
weights=np.array([h*h,h*h,h*h*h,h*h])
|
weights=np.array([h*h,h*h,h*h*h,h*h])
|
||||||
@@ -113,7 +113,7 @@ class Ledger:
|
|||||||
for name in ('impulse','angular_impulse','mass_into_fluid'):
|
for name in ('impulse','angular_impulse','mass_into_fluid'):
|
||||||
np.testing.assert_allclose(obs['total'][name],np.asarray(obs['wall'][name])+obs['aperture'][name],rtol=0,atol=2e-13)
|
np.testing.assert_allclose(obs['total'][name],np.asarray(obs['wall'][name])+obs['aperture'][name],rtol=0,atol=2e-13)
|
||||||
self.test.assertEqual(obs['sample_count'],2**self.index)
|
self.test.assertEqual(obs['sample_count'],2**self.index)
|
||||||
for a,leaf in zip(run._download_raw().values,run.leaf):
|
for a,leaf in zip(run._download_raw().values,run._leaf):
|
||||||
self.test.assertTrue(np.isfinite(a[:,leaf]).all())
|
self.test.assertTrue(np.isfinite(a[:,leaf]).all())
|
||||||
self.test.assertTrue((1+a[:,leaf].sum(axis=0,dtype=np.float64)>0).all())
|
self.test.assertTrue((1+a[:,leaf].sum(axis=0,dtype=np.float64)>0).all())
|
||||||
return obs
|
return obs
|
||||||
@@ -140,10 +140,10 @@ class ShiftedJetsGPU(unittest.TestCase):
|
|||||||
level_objects=[(),(obj,)],level_jets=[(),(whole,)],observe_raw_exchange=True) as run:
|
level_objects=[(),(obj,)],level_jets=[(),(whole,)],observe_raw_exchange=True) as run:
|
||||||
run._initialize_raw(initial(run,rho,u=.013,v=0.))
|
run._initialize_raw(initial(run,rho,u=.013,v=0.))
|
||||||
expected=run._download_raw();ledger=Ledger(self,run,1)
|
expected=run._download_raw();ledger=Ledger(self,run,1)
|
||||||
self.assertTrue(run._level_boundaries[1].tables['aperture'].all())
|
self.assertTrue(run._device_boundaries[1].tables['aperture'].all())
|
||||||
for _ in range(4):ledger.step()
|
for _ in range(4):ledger.step()
|
||||||
error=max(float(abs(a[:,leaf]-b[:,leaf]).max()) for a,b,leaf in
|
error=max(float(abs(a[:,leaf]-b[:,leaf]).max()) for a,b,leaf in
|
||||||
zip(run._download_raw().values,expected.values,run.leaf))
|
zip(run._download_raw().values,expected.values,run._leaf))
|
||||||
self.assertLessEqual(error,1e-6) # inherited uniform-state cap
|
self.assertLessEqual(error,1e-6) # inherited uniform-state cap
|
||||||
print(f'matched whole-circle rho={rho}: max_abs_raw_state={error:.9g}',flush=True)
|
print(f'matched whole-circle rho={rho}: max_abs_raw_state={error:.9g}',flush=True)
|
||||||
ledger.report(f'matched whole-circle rho={rho}')
|
ledger.report(f'matched whole-circle rho={rho}')
|
||||||
@@ -161,7 +161,7 @@ class ShiftedJetsGPU(unittest.TestCase):
|
|||||||
ledger=Ledger(self,measured,1)
|
ledger=Ledger(self,measured,1)
|
||||||
for speed in SPEEDS:
|
for speed in SPEEDS:
|
||||||
before=measured._download_raw();count=measured.step_count
|
before=measured._download_raw();count=measured.step_count
|
||||||
measured.set_jet_amplitude(0,speed, level=1);plain.set_jet_amplitude(0,speed, level=1)
|
measured.set_jet_amplitude(0,speed);plain.set_jet_amplitude(0,speed)
|
||||||
self.exact_raw(before,measured._download_raw());self.assertEqual(measured.step_count,count)
|
self.exact_raw(before,measured._download_raw());self.assertEqual(measured.step_count,count)
|
||||||
ledger.step();plain.step()
|
ledger.step();plain.step()
|
||||||
self.exact_raw(measured._download_raw(),plain._download_raw())
|
self.exact_raw(measured._download_raw(),plain._download_raw())
|
||||||
@@ -193,15 +193,15 @@ class ShiftedJetsGPU(unittest.TestCase):
|
|||||||
old._initialize_raw(initial(old,.99));ledger=Ledger(self,old,2)
|
old._initialize_raw(initial(old,.99));ledger=Ledger(self,old,2)
|
||||||
for speed in (.002,-.001):old.set_jet_amplitude(0,speed,level=2);ledger.step()
|
for speed in (.002,-.001):old.set_jet_amplitude(0,speed,level=2);ledger.step()
|
||||||
before=old._download_raw();count=old.step_count
|
before=old._download_raw();count=old.step_count
|
||||||
topology=lambda rects:RemapTopology.from_config(cfg,old.hierarchy.pairs[0],rects,child_objects=(obj,),
|
topology=lambda rects:RemapTopology.from_config(cfg,old.patch,rects,child_objects=(obj,),
|
||||||
child_jets=old.jets[2],_shifted_blocked=True)
|
child_jets=old._level_jets[2],_shifted_blocked=True)
|
||||||
expected=_remap_stored(topology(a),topology(b),before,shifted=True,base_count=count,
|
expected=_remap_stored(topology(a),topology(b),before,shifted=True,base_count=count,
|
||||||
level_steps=(count,2*count,4*count),phase='postcollision_g')
|
level_steps=(count,2*count,4*count),phase='postcollision_g')
|
||||||
new=owned.enter_context(old.remap_finest(b,objects=(obj,)))
|
new=owned.enter_context(old.remap_child(b,child_objects=(obj,)))
|
||||||
self.exact_raw(before,old._download_raw())
|
self.exact_raw(before,old._download_raw())
|
||||||
self.exact_raw(_StoredPopulations(expected.g,True),new._download_raw())
|
self.exact_raw(_StoredPopulations(expected.g,True),new._download_raw())
|
||||||
self.assertEqual(new.jets[2][0].amplitude,-.001)
|
self.assertEqual(new._level_jets[2][0].amplitude,-.001)
|
||||||
np.testing.assert_array_equal(new._level_boundaries[2].tables['surface_velocity'],old._level_boundaries[2].tables['surface_velocity'])
|
np.testing.assert_array_equal(new._device_boundaries[2].tables['surface_velocity'],old._device_boundaries[2].tables['surface_velocity'])
|
||||||
newledger=Ledger(self,new,2);ledger.step();newledger.step()
|
newledger=Ledger(self,new,2);ledger.step();newledger.step()
|
||||||
self.assertEqual((old.step_count,new.step_count),(count+1,count+1))
|
self.assertEqual((old.step_count,new.step_count),(count+1,count+1))
|
||||||
ledger.report('L2 source');newledger.report('L2 remapped')
|
ledger.report('L2 source');newledger.report('L2 remapped')
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class ShiftedRemapGPU(unittest.TestCase):
|
|||||||
def check_finite(self,run):
|
def check_finite(self,run):
|
||||||
raw=run._download_raw()
|
raw=run._download_raw()
|
||||||
self.assertTrue(raw.shifted)
|
self.assertTrue(raw.shifted)
|
||||||
for a,leaf in zip(raw.values,run.leaf):
|
for a,leaf in zip(raw.values,run._leaf):
|
||||||
self.assertTrue(np.isfinite(a[:,leaf]).all())
|
self.assertTrue(np.isfinite(a[:,leaf]).all())
|
||||||
self.assertTrue(np.isnan(a[:,~leaf]).all())
|
self.assertTrue(np.isnan(a[:,~leaf]).all())
|
||||||
self.assertTrue((1+a[:,leaf].sum(axis=0,dtype=np.float64)>0).all())
|
self.assertTrue((1+a[:,leaf].sum(axis=0,dtype=np.float64)>0).all())
|
||||||
@@ -71,7 +71,7 @@ class ShiftedRemapGPU(unittest.TestCase):
|
|||||||
old=owned.enter_context(make(ctx))
|
old=owned.enter_context(make(ctx))
|
||||||
old._initialize_raw(initial(old));old.step(2)
|
old._initialize_raw(initial(old));old.step(2)
|
||||||
before=old._download_raw()
|
before=old._download_raw()
|
||||||
new=owned.enter_context(old.remap_finest(A))
|
new=owned.enter_context(old.remap_child(A))
|
||||||
self.check_reset(new,before.values)
|
self.check_reset(new,before.values)
|
||||||
self.snapshot_equal(old._download_raw(),before,'old unchanged')
|
self.snapshot_equal(old._download_raw(),before,'old unchanged')
|
||||||
self.snapshot_equal(new._download_raw(),before,'identity raw copy')
|
self.snapshot_equal(new._download_raw(),before,'identity raw copy')
|
||||||
@@ -85,11 +85,11 @@ class ShiftedRemapGPU(unittest.TestCase):
|
|||||||
old=owned.enter_context(make(ctx))
|
old=owned.enter_context(make(ctx))
|
||||||
old._initialize_raw(initial(old));old.step(2)
|
old._initialize_raw(initial(old));old.step(2)
|
||||||
before=old._download_raw();count=old.step_count
|
before=old._download_raw();count=old.step_count
|
||||||
old_mesh=RemapTopology.from_config(CFG,old.hierarchy.pairs[0],A)
|
old_mesh=RemapTopology.from_config(CFG,old.patch,A)
|
||||||
new_mesh=RemapTopology.from_config(CFG,old.hierarchy.pairs[0],B)
|
new_mesh=RemapTopology.from_config(CFG,old.patch,B)
|
||||||
expected=_remap_stored(old_mesh,new_mesh,before,shifted=True,
|
expected=_remap_stored(old_mesh,new_mesh,before,shifted=True,
|
||||||
base_count=count,level_steps=(count,2*count,4*count),phase='postcollision_g')
|
base_count=count,level_steps=(count,2*count,4*count),phase='postcollision_g')
|
||||||
new=owned.enter_context(old.remap_finest(B))
|
new=owned.enter_context(old.remap_child(B))
|
||||||
self.check_reset(new,expected.g)
|
self.check_reset(new,expected.g)
|
||||||
self.snapshot_equal(new._download_raw(),_StoredPopulations(expected.g,True),'host raw fill')
|
self.snapshot_equal(new._download_raw(),_StoredPopulations(expected.g,True),'host raw fill')
|
||||||
for a,b,om,nm in zip(before.values,new._download_raw().values,old_mesh.leaf_masks,new_mesh.leaf_masks):
|
for a,b,om,nm in zip(before.values,new._download_raw().values,old_mesh.leaf_masks,new_mesh.leaf_masks):
|
||||||
@@ -103,7 +103,7 @@ class ShiftedRemapGPU(unittest.TestCase):
|
|||||||
with CudaContext(0) as ctx,make(ctx) as run:
|
with CudaContext(0) as ctx,make(ctx) as run:
|
||||||
run._initialize_raw(initial(run));run.step(2)
|
run._initialize_raw(initial(run));run.step(2)
|
||||||
before=run._download_raw()
|
before=run._download_raw()
|
||||||
with self.assertRaises(ValueError):run.remap_finest([])
|
with self.assertRaises(ValueError):run.remap_child([])
|
||||||
self.snapshot_equal(run._download_raw(),before,'rejection preserves source')
|
self.snapshot_equal(run._download_raw(),before,'rejection preserves source')
|
||||||
self.assertEqual(run.step_count,2)
|
self.assertEqual(run.step_count,2)
|
||||||
run.step();self.check_finite(run)
|
run.step();self.check_finite(run)
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ def public_jets(center, radius, scenario):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def jet_interval(handle, scenario, begin, n, diameter, *, level=None):
|
def jet_interval(handle, scenario, begin, n, diameter, level=None):
|
||||||
"""Apply a reversal only at a completed interval boundary, before advancing."""
|
"""Apply a reversal only at a completed interval boundary, before advancing."""
|
||||||
if scenario != "none":
|
if scenario != "none":
|
||||||
boundaries = [round(t * diameter / SPEED) for t in (250, 260, 270, 280, 290, 300, 305)]
|
boundaries = [round(t * diameter / SPEED) for t in (250, 260, 270, 280, 290, 300, 305)]
|
||||||
@@ -92,9 +92,10 @@ def jet_interval(handle, scenario, begin, n, diameter, *, level=None):
|
|||||||
if begin < switch:
|
if begin < switch:
|
||||||
return min(n, switch - begin), None
|
return min(n, switch - begin), None
|
||||||
if begin == switch:
|
if begin == switch:
|
||||||
target = {} if level is None else dict(level=level)
|
# FixedPatchStepper needs an explicit Jet level; Simulation has one level.
|
||||||
handle.set_jet_amplitude(0, -JET_PEAK, **target)
|
kw = {} if level is None else {"level": level}
|
||||||
handle.set_jet_amplitude(1, JET_PEAK, **target)
|
handle.set_jet_amplitude(0, -JET_PEAK, **kw)
|
||||||
|
handle.set_jet_amplitude(1, JET_PEAK, **kw)
|
||||||
return n, dict(step=begin, tstar=300.0, amplitudes=[-JET_PEAK, JET_PEAK])
|
return n, dict(step=begin, tstar=300.0, amplitudes=[-JET_PEAK, JET_PEAK])
|
||||||
return n, None
|
return n, None
|
||||||
|
|
||||||
@@ -257,7 +258,7 @@ def host_geometry(source, h, collision="SRT", jet_scenario="none", placement="wi
|
|||||||
validate_channel_config,
|
validate_channel_config,
|
||||||
validate_channel_transfers,
|
validate_channel_transfers,
|
||||||
)
|
)
|
||||||
from CelerisLab.lbm.refinement_tables import blocked_base_tables
|
from CelerisLab.lbm import refinement_tables
|
||||||
from CelerisLab.body.geometry.circle import CircleGeometry
|
from CelerisLab.body.geometry.circle import CircleGeometry
|
||||||
from CelerisLab.body.objects import SimObject
|
from CelerisLab.body.objects import SimObject
|
||||||
|
|
||||||
@@ -269,7 +270,11 @@ def host_geometry(source, h, collision="SRT", jet_scenario="none", placement="wi
|
|||||||
pair = FixedPatch2D.from_config(cfg, PLACEMENTS[placement])
|
pair = FixedPatch2D.from_config(cfg, PLACEMENTS[placement])
|
||||||
layout = BlockLayout2D.build(cfg.ny, cfg.nx, *BASE_BLOCK)
|
layout = BlockLayout2D.build(cfg.ny, cfg.nx, *BASE_BLOCK)
|
||||||
validate_channel_config(cfg, [BASE_BLOCK, FINE_BLOCK])
|
validate_channel_config(cfg, [BASE_BLOCK, FINE_BLOCK])
|
||||||
targets = blocked_base_tables(pair, layout, np.argsort(pair.coalesce_coarse))[1]
|
# Historical pinned source (jet_scenario none) names it blocked_base_tables;
|
||||||
|
# the current checkout (public Jet scenarios) names it blocked_coarse_tables.
|
||||||
|
coarse_tables = (refinement_tables.blocked_base_tables if jet_scenario == "none"
|
||||||
|
else refinement_tables.blocked_coarse_tables)
|
||||||
|
targets = coarse_tables(pair, layout, np.argsort(pair.coalesce_coarse))[1]
|
||||||
validate_channel_transfers(pair, layout, targets)
|
validate_channel_transfers(pair, layout, targets)
|
||||||
BlockLayout2D.build(*pair.fine_shape, *FINE_BLOCK, periodic=(False, False))
|
BlockLayout2D.build(*pair.fine_shape, *FINE_BLOCK, periodic=(False, False))
|
||||||
body = SimObject(
|
body = SimObject(
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ def run_variant(backend, variant, n_intervals, deadline):
|
|||||||
else:
|
else:
|
||||||
for _ in range(INTERVAL):
|
for _ in range(INTERVAL):
|
||||||
backend.step(1)
|
backend.step(1)
|
||||||
totals.append(components(backend.read_exchange_window(0, level=1)))
|
totals.append(components(backend.read_exchange_window(0)))
|
||||||
t.check_deadline(deadline)
|
t.check_deadline(deadline)
|
||||||
return np.array(totals), (np.array(scales) if scales else None)
|
return np.array(totals), (np.array(scales) if scales else None)
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user