Files
CelerisLab/tests/unit/test_planar_velocity.py
ClaudeandClaude Opus 5.5 d318911221 cuda: compiler_v2 module becomes compiler
The package already exposed it as CelerisLab.cuda.compiler; the module now
has that name and every tip caller, doc and current-tree test imports it
directly (no alias).

Historical-revision tooling is not edited: the Taylor-Green harness
(run_case.py, hash-pinned check_projected_pairs.py, assess receipts) only
runs against a135252, whose package has cuda/compiler_v2.py, and the J
campaign runners and dated docs describe past trees. Its two current-tree
tests now import that package from git (historical_package) instead of the
tip, and the kernel-root test first checks the accepted case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 19:15:50 +00:00

96 lines
4.3 KiB
Python

"""J1 actual-header moments and bounded host capability contracts."""
import json
from pathlib import Path
from types import SimpleNamespace
import h5py
import numpy as np
import pytest
import CelerisLab
from CelerisLab.config import LBMConfig, load_lbm_config
from CelerisLab.cuda import compiler
from CelerisLab.common.checkpoint import save_checkpoint, load_checkpoint
from .test_numerical_operators import compile_run
from .test_state_regressions import device, make_field, manager
def test_planar_helper_moments_and_known_populations(tmp_path):
root = Path(__file__).resolve().parents[2]
assert Path(CelerisLab.__file__).resolve() == root/'src/CelerisLab/__init__.py'
assert Path(compiler.kernel_path('uniform.cu')).resolve() == root/'src/CelerisLab/lbm/kernels/uniform.cu'
lines = compile_run(tmp_path, 'planar_velocity.cpp', dict(WALL_NORMAL_VELOCITY='0.0f'))
cx = np.array([0,1,-1,0,0,1,-1,1,-1])
cy = np.array([0,0,0,1,-1,1,-1,-1,1])
w = np.array([4/9]+[1/9]*4+[1/36]*4)
for start in range(0, len(lines), 3):
top, rho, u, v, neq = np.fromstring(lines[start], sep=' ')
before, after = [np.fromstring(s, sep=' ') for s in lines[start+1:start+3]]
known = cy != (-1 if top else 1)
np.testing.assert_array_equal(after[known], before[known])
density = after.sum()
np.testing.assert_allclose([after@cx, after@cy], density*np.array([u,v]), atol=3e-7, rtol=0)
if not neq:
cu = cx*u+cy*v
expected = w*rho*(1+3*cu+4.5*cu**2-1.5*(u*u+v*v))
np.testing.assert_allclose(after, expected, atol=3e-7, rtol=0)
assert abs(density-rho) <= 3e-7
@pytest.mark.parametrize('override', [
dict(collision='TRT'), dict(collision='MRT'), dict(streaming='esopull'),
dict(store_precision='FP16S'), dict(ddf_shifting=True), dict(les_enabled=True),
dict(lattice_model='D3Q19'), dict(wall_normal_velocity=1.),
dict(wall_normal_velocity=float('nan')),
])
def test_planar_unsupported_configuration(override):
with pytest.raises(ValueError):
LBMConfig(y_wall_bc='on_site_velocity', **override).validate_runtime()
def test_planar_config_roundtrip_and_fixed_targets(tmp_path):
path = Path(__file__).resolve().parents[2]/'src/CelerisLab/configs/config_lbm.json'
data = json.loads(path.read_text())
data['method'].update(y_wall_bc='on_site_velocity', wall_normal_velocity=-.01,
collision='SRT', streaming='double_buffer',
store_precision='FP32', ddf_shifting=False)
data['method']['les']['enabled'] = False
path = tmp_path/'config.json'
path.write_text(json.dumps(data))
cfg = load_lbm_config(str(path))
assert float(cfg.to_macros()['WALL_NORMAL_VELOCITY'][:-1]) == -.01
with pytest.raises(ValueError, match='object-free'):
compiler.generate_config(cfg, n_objects=1, build_root=tmp_path)
before = cfg.allocation_signature()
cfg.wall_normal_velocity = .01
assert cfg.allocation_signature() != before
def test_planar_force_rejected_before_state_change(device):
field = make_field(device, y_wall_bc='on_site_velocity')
before, writes = field._runtime_params.copy(), device.writes
with pytest.raises(ValueError, match='body forces'):
field.update_params(fx=.001)
assert field._runtime_params == before and device.writes == writes
@pytest.mark.parametrize('damage', ['target', 'mode', 'force'])
def test_planar_checkpoint_contract_before_writes(device, tmp_path, damage):
field = make_field(device, y_wall_bc='on_site_velocity')
bodies = manager(field)
bodies.sync_to_gpu(field)
path = save_checkpoint(field, SimpleNamespace(step_count=3), field.cfg, bodies,
str(tmp_path/'plane.h5'))
with h5py.File(path, 'a') as hf:
if damage == 'force':
hf.attrs['runtime_params_json'] = '{"fx":0.001}'
else:
cfg = json.loads(hf.attrs['config_json'])
cfg['wall_normal_velocity' if damage == 'target' else 'y_wall_bc'] = (
.01 if damage == 'target' else 'bounce_back')
hf.attrs['config_json'] = json.dumps(cfg)
writes = device.writes
with pytest.raises(ValueError):
load_checkpoint(path, field, SimpleNamespace(_step_count=0), field.cfg, bodies)
assert device.writes == writes