"""CPU-only contract tests for run_dfg_re20. Not CUDA or DFG acceptance evidence.""" import sys from pathlib import Path import unittest sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "validation")) import run_dfg_re20 as v # noqa: E402 import run_refined_dfg as rr # noqa: E402 h, _ = rr.helpers(rr.DEFAULT_SOURCE) def rows(values, tstar_end=v.TSTAR_MAX): n = int(round(tstar_end / v.SAMPLE_TSTAR)) return [dict(tstar=(i + 1) * v.SAMPLE_TSTAR, **values(i)) for i in range(n)] class Re20Contract(unittest.TestCase): def test_lattice_parameters_and_bounds(self): root, uniform = v.lattice_parameters(40), v.lattice_parameters(80) self.assertAlmostEqual(root["tau"], 0.56, places=12) self.assertAlmostEqual(uniform["tau"], 0.62, places=12) self.assertEqual((root["steps"], root["interval"]), (240000, 2000)) self.assertEqual((uniform["steps"], uniform["interval"]), (480000, 4000)) self.assertAlmostEqual(v.plan()["refined_fine_tau"], 0.62, places=12) def test_config_only_changes_viscosity(self): base = v._re100_config(h, 40, "SRT", "none") cfg, body, p = v.config(h, 40, "SRT") self.assertAlmostEqual(cfg["physics"]["viscosity"], 0.01 * 40 / 20, places=15) self.assertEqual((p["re"], p["viscosity"]), (20.0, cfg["physics"]["viscosity"])) base[0]["physics"]["viscosity"] = cfg["physics"]["viscosity"] self.assertEqual(base[0], cfg) self.assertEqual(base[1], body) with self.assertRaisesRegex(ValueError, "no Jets"): v.config(h, 40, "MRT", "zero") def test_units(self): s = v.sample([2.0, -0.5], 4, 40, 3.0) den = rr.SPEED**2 * 40 / 2 self.assertAlmostEqual(s["cd"], 2.0 / 4 / den) self.assertAlmostEqual(s["cl"], -0.5 / 4 / den) self.assertAlmostEqual(s["p_diff"], 3.0 * 0.04) self.assertEqual(s["dp_scaled"], 3.0) def test_steady_and_verdict(self): inside = dict(cd=5.58, cl=0.0106, p_diff=0.1174) out = v.summarize(rows(lambda i: inside)) self.assertEqual((out["status"], out["steady"], out["window_samples"]), ("WITHIN_INTERVALS", True, 21)) self.assertTrue(out["cd_positive"]) self.assertAlmostEqual(out["edge_distance"]["cl"], 0.0002) self.assertTrue(all(out["verdict"].values())) low = v.summarize(rows(lambda i: dict(inside, cl=0.0100))) self.assertEqual(low["status"], "OUTSIDE_INTERVALS") self.assertFalse(low["verdict"]["cl"]) def test_slow_drift_gives_no_verdict(self): # Range 2e-5 passes (limit 6e-5); slope * window 2e-5 exceeds 1.2e-5. out = v.summarize(rows(lambda i: dict(cd=5.58, cl=0.0106 + 1e-6 * i, p_diff=0.1174))) self.assertEqual((out["status"], out["verdict"]), ("NOT_STEADY", None)) self.assertLessEqual(out["ranges"]["cl"], out["range_limits"]["cl"]) self.assertGreater(out["drifts"]["cl"], out["slope_limits"]["cl"]) def test_noise_range(self): quiet = v.summarize(rows(lambda i: dict(cd=5.58, cl=0.0106 + 2e-5 * (i % 2), p_diff=0.1174))) self.assertTrue(quiet["steady"]) # within the range limit, no trend noisy = v.summarize(rows(lambda i: dict(cd=5.58, cl=0.0106 + 1e-4 * (i % 2), p_diff=0.1174))) self.assertEqual(noisy["status"], "NOT_STEADY") self.assertGreater(noisy["ranges"]["cl"], noisy["range_limits"]["cl"]) def test_incomplete_run_gives_no_verdict(self): out = v.summarize(rows(lambda i: dict(cd=5.58, cl=0.0106, p_diff=0.1174), tstar_end=30)) self.assertEqual((out["status"], out["verdict"]), ("INCOMPLETE", None)) class Re20Provenance(unittest.TestCase): def test_records_current_checkout(self): record = v.provenance(rr.DEFAULT_SOURCE) self.assertEqual(record["source_mode"], "current_checkout") self.assertEqual(len(record["checkout_revision"]), 40) self.assertEqual(len(record["src_tree"]), 40) self.assertEqual(len(record["runner_sha256"]), 64) self.assertIn("checkout_dirty", record) if __name__ == "__main__": unittest.main()