Files
CelerisLab/tests/validation/experimental_full9/test_dfg.py
T

154 lines
7.5 KiB
Python

"""Focused host checks for the uniform experimental DFG pilot."""
from copy import deepcopy
import importlib.util
import json
from pathlib import Path
import subprocess
import sys
from tempfile import TemporaryDirectory
import unittest
sys.path.insert(0, str(Path(__file__).resolve().parent))
import run_dfg as r
def test_factory_and_default():
cfg, body, p, manifest = r.configuration(r.ROOT)
r.configuration(r.ROOT)
import run_refined_dfg as runner
h, _ = runner.helpers(r.ROOT)
old, old_body, old_p = runner.config(h, 80)
assert old['method']['inlet']['scheme'] == 'zou_he_local'
old['method']['inlet']['scheme'] = r.VARIANT
assert cfg == old and body == old_body and p == old_p
assert len(body['objects']) == 1 and p['diameter'] == 80
assert cfg == runner.config(h, 80, r.VARIANT)[0]
assert manifest['commit'] == '926ca27407c26ca11283282a6e53249925725376'
def receipt():
_, _, _, m = r.configuration(r.ROOT)
expected = r.identity(m)
rows = [dict(step=i*4000, tstar=i*.5, tstar_force=i*.5-.25,
rho_min=.99, rho_max=1.01, max_mach=.03,
cd=3., cl=.1, dp=2., mass=.8, flux_in=.41, flux_out=.41)
for i in range(1, 11)]
return expected, dict(**expected, case='uniform', mode='pilot', status='pilot_complete',
healthy=True, steps=40000, executed_steps=40000, history=rows,
calibration_batches_seconds=[.001]*5)
def test_receipts():
expected, result = receipt()
r.verify_pilot(result, expected)
for key, value in [('collision', 'MRT'), ('experimental_variant', 'zou_he_local'),
('observation_mode', 'first_health_pair'), ('control', 'without_cylinder'),
('numerical_source_commit', 'other'), ('case', 'refined'),
('steps', 39999), ('executed_steps', 39999), ('healthy', False)]:
bad = deepcopy(result); bad[key] = value
with unittest.TestCase().assertRaises(ValueError): r.verify_pilot(bad, expected)
for change in ('nan', 'missing', 'time', 'count'):
bad = deepcopy(result)
if change == 'nan': bad['history'][-1]['tstar'] = float('nan')
if change == 'missing': del bad['history'][0]['cd']
if change == 'time': bad['history'][0]['tstar_force'] = .5
if change == 'count': bad['history'].pop()
with unittest.TestCase().assertRaises(ValueError): r.verify_pilot(bad, expected)
def test_accuracy_requires_reference():
assert not r.accuracy_pass(dict(healthy=True, periodic=True))
assert r.accuracy_pass(dict(healthy=True, periodic=True,
reference_comparison=dict(reference_agreement=True)))
assert not r.accuracy_pass(dict(healthy=True, periodic=False,
reference_comparison=dict(reference_agreement=True)))
def test_preflight_and_source_guards():
with TemporaryDirectory() as temp:
subprocess.run([sys.executable, str(Path(r.__file__)), '--output', temp], check=True)
out = json.loads((Path(temp)/'preflight.json').read_text())
assert out['max_steps'] == 40000 and out['observation_width_tstar'] == .5
assert not out['executed'] and not out['accuracy_qualified']
assert out['wall_seconds'] == 180 and out['control'] == 'with_cylinder'
with unittest.TestCase().assertRaises(ValueError): r.configuration(r.ROOT.parent)
with unittest.TestCase().assertRaisesRegex(ValueError, 'Pinned source mismatch'):
r.diagnostic.source_config(r.ROOT, Path(r.__file__).with_name('source_manifest.json'))
def test_shared_source_semantics():
r.configuration(r.ROOT)
import run_refined_dfg as runner
text = Path(runner.__file__).read_text()
assert 'sparse_pilot=False' in text
assert '(a.mode == "pilot" and not sparse_pilot)' in text
assert 'impulse += sim.read_force(0).astype(np.float64) * batch' in text
assert 'tstar_force=(begin + n / 2) * SPEED / p["diameter"]' in text
assert 'cd=float(impulse[0] / n / den)' in text
assert 'mass = float(rho[fluid].sum()) * p["dx"] ** 2' in text
assert 'executed_steps += batch' in text
assert 'First unhealthy observation; onset between observations is unknown' in text
def test_accounted_calibration_and_failure_progress():
import numpy as np
import types
from unittest.mock import patch
r.configuration(r.ROOT)
import run_refined_dfg as runner
h, periodic = runner.helpers(r.ROOT)
_, _, p = runner.config(h, 80)
count = [0]
fail = [False]
class Simulation:
def __init__(self, *args, **kwargs):
self.field = types.SimpleNamespace(flag=np.ones(p['nx']*p['ny'], np.uint8))
def __enter__(self): return self
def __exit__(self, *args): pass
def initialize(self): count[0] = 0
def run(self, n): count[0] += n
def read_force(self, body): return np.array([.004, .002])
def get_macroscopic(self):
shape = (p['ny'], p['nx'])
return dict(rho=np.full(shape, float('nan') if fail[0] else 1.),
ux=np.full(shape, .01), uy=np.zeros(shape))
modules = {
'CelerisLab.simulation': types.SimpleNamespace(Simulation=Simulation),
'CelerisLab.cuda.context': types.SimpleNamespace(CudaContext=object),
'CelerisLab.lbm.refinement_stepper': types.SimpleNamespace(FixedPatchStepper=object),
}
with patch.dict(sys.modules, modules), patch.object(h, 'pressure_probes', return_value=dict(dp=2.)):
with TemporaryDirectory() as temp:
args = types.SimpleNamespace(source=r.ROOT, output=Path(temp), mode='pilot', deadline=float('inf'))
progress = {}
result = runner.run_case(args, h, periodic, 'uniform', inlet_scheme=r.VARIANT,
sparse_pilot=True, progress=progress)
assert count[0] == 40000 and result['executed_steps'] == 40000
assert len(result['history']) == 10 and len(result['calibration_batches_seconds']) == 5
assert abs(result['history'][0]['cd'] - 1.) < 1e-14
assert result['history'][0]['tstar_force'] == .25
fail[0] = True
with TemporaryDirectory() as temp:
args.output = Path(temp)
result = runner.run_case(args, h, periodic, 'uniform', inlet_scheme=r.VARIANT,
sparse_pilot=True, progress=progress)
assert result['executed_steps'] == 4000 and result['steps'] == 0
assert result['first_observed_failure_step'] == 4000 and result['last_observed_step'] == 0
fail[0] = False
with TemporaryDirectory() as temp:
args.output = Path(temp)
def deadline(_):
if count[0] >= 500: raise TimeoutError('test deadline')
count[0] = 0
with patch.object(runner, 'check_deadline', side_effect=deadline):
with unittest.TestCase().assertRaises(TimeoutError):
runner.run_case(args, h, periodic, 'uniform', inlet_scheme=r.VARIANT,
sparse_pilot=True, progress=progress)
assert progress['executed_steps'] == 500 and progress['last_observed_step'] == 0
assert len(progress['calibration_batches_seconds']) == 5
if __name__ == '__main__':
result = unittest.TextTestRunner(verbosity=2).run(unittest.TestSuite(
unittest.FunctionTestCase(fn) for name, fn in sorted(globals().items()) if name.startswith('test_')))
raise SystemExit(not result.wasSuccessful())