test: cover TRT restart and actual-rate refinement phase
Integrate reviewed test changes from 7c1517de819741907fb945ea0ab7864734fa3856 and 340ba4e1dc6386581839c0b28c8fe8a1f4aeb70b unchanged. Document bounded 2D implementation, evidence and open gates; production source and historical manifests are unchanged.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
# Bounded 2D implementation and validation scope
|
||||
|
||||
This inventory uses dev `8403956a9afc0b0bb89265cec374392013d89016` as its
|
||||
production source checkpoint. The accompanying tests add coverage; their presence
|
||||
is not a passing GPU receipt. Historical campaigns retain their original source
|
||||
identities and acceptance gates.
|
||||
|
||||
## Implementation boundaries
|
||||
|
||||
- Uniform D2Q9 supports SRT, TRT and MRT. An available collision selector does not
|
||||
qualify every wall, inlet/outlet, forcing, storage or LES combination.
|
||||
- Fixed refinement supports SRT/MRT, FP32 and double-buffer streaming; TRT and LES
|
||||
are rejected. Shifted refinement requires all-blocked storage. Refined channel
|
||||
boundaries have narrower configuration guards than uniform boundaries.
|
||||
- Uniform checkpoints support compatible same-method continuation. Changing
|
||||
collision or LES parameters is a different evolution, not exact continuation.
|
||||
Jet checkpoints and refined checkpoints are unsupported; synchronized remapping
|
||||
is not persistent restart.
|
||||
- Keep these conclusions 2D. Local D3Q19 operator tests do not establish a supported
|
||||
3D Simulation runtime.
|
||||
|
||||
## Evidence and remaining gaps
|
||||
|
||||
| Area | Available evidence | Boundary of the conclusion |
|
||||
|---|---|---|
|
||||
| MRT cylinder flow and observation windows | Full matched uniform/refined MRT DFG and the window-ledger campaign passed their frozen periodicity, reference and matched-comparison gates. The window campaign used `185a919`; dev adds only a docstring correction. | This does not qualify other collision/boundary combinations or every refinement interface. Timing improvements are not additional accuracy evidence. |
|
||||
| Uniform forced channel | Six Poiseuille GPU cases on `8403956`: SRT/TRT/MRT at wall-normal resolutions H16/H32 passed. | This is the tested analytic channel configuration, not open-boundary or all-layout qualification. |
|
||||
| Uniform restart | Ten SRT/MRT checkpoint GPU tests passed on `8403956`. | The new representative TRT same-method double-buffer continuation test still requires its own GPU receipt; no LES, Jet or refined checkpoint claim follows. |
|
||||
| Taylor–Green refinement | Independent topology checks for the exact TG setup are available. The companion phase regression checks the actual lattice-rate configuration. | The physical interface-accuracy gate remains open. Topology and phase setup checks do not establish convergence or close that gate. |
|
||||
| Porous Couette and annulus | Prior campaign evidence exists and must remain associated with its original setup and source. | The reconstructed annulus is a validation helper, not qualification of a public boundary-condition API. Do not infer absence of evidence from a missing current rerun. |
|
||||
| LES and other combinations | Separate scalar and uniform-flow normalization evidence exists for the reviewed temporary correction. | That correction is not integrated into this source checkpoint. Do not transfer its acceptance to unchanged production, or to TRT/MRT LES, forcing, EsoPull, objects or refinement. |
|
||||
|
||||
Historical uniform SRT cylinder-startup and inlet-rounding findings remain separate
|
||||
from the passed MRT DFG result. Raw exchange/window consistency is a measurement
|
||||
contract, not a general stress or force-accuracy certification. Existing source-pinned
|
||||
runners may intentionally reject a newer checkout; never silently repin old receipts.
|
||||
|
||||
## Small next batch
|
||||
|
||||
1. Cross-review the added TRT checkpoint and actual-rate TG phase tests, then run
|
||||
their applicable checks with exact checkout/import receipts. Preserve the
|
||||
existing SRT checkpoint cases and numerical criteria.
|
||||
2. Run the single added TRT checkpoint GPU case with the same method on both sides
|
||||
of save/load. Record its result separately from the ten existing SRT/MRT passes.
|
||||
3. Continue the existing TG accuracy investigation using its actual fine-level
|
||||
rates and the established analytic convention. Keep the accuracy gate open
|
||||
until the relevant physical comparison passes.
|
||||
|
||||
No repeat of the accepted full MRT DFG or Cartesian expansion across unsupported
|
||||
combinations is required by this document. Main integration remains a separate
|
||||
review of complete, bounded feature scope.
|
||||
@@ -65,9 +65,14 @@ def test_shifted_topology_add_remove_preserves_cache_and_finite_flow(streaming,p
|
||||
assert np.max(macro['rho'][fluid]) < 1.5
|
||||
|
||||
|
||||
@pytest.mark.parametrize('streaming',['double_buffer','esopull'])
|
||||
def test_checkpoint_exact_continuation_and_controls(streaming,tmp_path):
|
||||
with make_sim(nx=48,ny=24,streaming=streaming,shifted=True) as sim:
|
||||
@pytest.mark.parametrize('collision,streaming', [
|
||||
pytest.param('SRT', 'double_buffer', id='double_buffer'),
|
||||
pytest.param('SRT', 'esopull', id='esopull'),
|
||||
pytest.param('TRT', 'double_buffer', id='trt-double_buffer'),
|
||||
])
|
||||
def test_checkpoint_exact_continuation_and_controls(collision,streaming,tmp_path):
|
||||
# Same collision throughout save/load/continue; this is not a method-switch restart.
|
||||
with make_sim(nx=48,ny=24,streaming=streaming,shifted=True,collision=collision) as sim:
|
||||
sim.add_body('circle',center=(14,12),radius=3)
|
||||
sim.add_body('force_region',center=(30,12),radius=2)
|
||||
sim.initialize()
|
||||
|
||||
@@ -7,6 +7,7 @@ At most eight coarse steps per case. No files, plots or flow arrays are saved.
|
||||
import hashlib
|
||||
import importlib.util
|
||||
from contextlib import nullcontext
|
||||
from dataclasses import replace
|
||||
from pathlib import Path
|
||||
import resource
|
||||
import sys
|
||||
@@ -21,7 +22,7 @@ import CelerisLab
|
||||
from CelerisLab.config import LBMConfig
|
||||
from CelerisLab.cuda import compiler_v2 as compiler
|
||||
from CelerisLab.cuda.context import CudaContext
|
||||
from CelerisLab.lbm.refinement_stepper import PeriodicPatchStepper
|
||||
from CelerisLab.lbm.refinement_stepper import FixedPatchStepper, PeriodicPatchStepper
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
POP_ATOL = 3e-6
|
||||
@@ -60,6 +61,69 @@ def global_array(module, name, shape):
|
||||
|
||||
|
||||
class CoupledGPU(unittest.TestCase):
|
||||
def test_actual_tg_rates_one_interval(self):
|
||||
"""Algebraic rate/phase check, not a new physical Taylor--Green benchmark.
|
||||
|
||||
The N64/Re100 TG rates are exercised on the existing small patch with
|
||||
a nonequilibrium state. One interval checks g=C(f), including the
|
||||
shifted blocked path; no accuracy gate or production source changes.
|
||||
"""
|
||||
self.assertEqual(Path(CelerisLab.__file__).resolve(), ROOT / "src/CelerisLab/__init__.py")
|
||||
self.assertEqual(Path(compiler._KERNEL_DIR).resolve(), ROOT / "src/CelerisLab/lbm/kernels")
|
||||
spec = replace(ref.make_spec(collision="srt"),
|
||||
tau_coarse=.5576, tau_fine=.6152)
|
||||
maps = ref.build_maps(spec)
|
||||
state = ref.make_state(spec, velocity=(.03, -.02))
|
||||
rng = np.random.default_rng(1)
|
||||
state = ref.State(*(a * (1 + 1e-3 * rng.standard_normal(a.shape))
|
||||
for a in (state.coarse_f, state.fine_f)))
|
||||
initial = postcollision(state, spec, maps)
|
||||
next_state, _ = ref.advance_coarse_once(spec, maps, state)
|
||||
expected = postcollision(next_state, spec, maps)
|
||||
arrivals = (next_state.coarse_f, next_state.fine_f)
|
||||
|
||||
cases = (("dense_unshifted", False, {}),
|
||||
("blocked_shifted", True,
|
||||
dict(base_block=(4, 4), fine_block=(6, 6))))
|
||||
with CudaContext(0) as context:
|
||||
for label, shifted, storage in cases:
|
||||
with self.subTest(storage=label):
|
||||
cfg = LBMConfig(nx=16, ny=16, viscosity=.0192,
|
||||
ddf_shifting=shifted)
|
||||
with FixedPatchStepper(cfg, (4, 4, 12, 12), context=context,
|
||||
**storage) as backend:
|
||||
self.assertAlmostEqual(backend.patch.tau_coarse, spec.tau_coarse)
|
||||
self.assertAlmostEqual(backend.patch.tau_fine, spec.tau_fine)
|
||||
self.assertEqual(tuple(level.shape for level in backend._levels),
|
||||
((9, 16, 16), (9, 24, 24)))
|
||||
# Physical FP64 g is encoded by initialize before FP32 rounding.
|
||||
backend.initialize(*initial)
|
||||
before = totals(backend.download(), maps)
|
||||
backend.step(1)
|
||||
actual = backend.download()
|
||||
self.assertEqual(backend.step_count, 1)
|
||||
backend.patch.check_synchronization(
|
||||
1, next_state.tick, phase="postcollision_g")
|
||||
for got, want, arrival in zip(actual, expected, arrivals):
|
||||
np.testing.assert_array_equal(np.isnan(got), np.isnan(want))
|
||||
np.testing.assert_allclose(got, want, rtol=0, atol=POP_ATOL,
|
||||
equal_nan=True)
|
||||
# A raw arrival-f comparison must fail on BOTH levels.
|
||||
# Equilibrium-only data would make this control vacuous.
|
||||
with self.assertRaises(AssertionError):
|
||||
np.testing.assert_allclose(got, arrival, rtol=0,
|
||||
atol=POP_ATOL, equal_nan=True)
|
||||
np.testing.assert_allclose(totals(actual, maps), before,
|
||||
rtol=0, atol=MOMENT_ATOL)
|
||||
np.testing.assert_allclose(totals(actual, maps),
|
||||
totals(expected, maps),
|
||||
rtol=0, atol=MOMENT_ATOL)
|
||||
error = max(float(np.nanmax(np.abs(a - e)))
|
||||
for a, e in zip(actual, expected))
|
||||
print(f"actual-TG-rate algebraic check: {label} steps=1 "
|
||||
f"tau=.5576/.6152 max_population={error:.9g}; "
|
||||
"wrong-phase controls PASS", flush=True)
|
||||
|
||||
def test_cleanup_attempts_every_allocation(self):
|
||||
backend = PeriodicPatchStepper.__new__(PeriodicPatchStepper)
|
||||
backend.ctx = Mock(activate=nullcontext)
|
||||
|
||||
Reference in New Issue
Block a user