154 lines
7.5 KiB
Python
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())
|