diff --git a/experiments/summarize_density_order.py b/experiments/summarize_density_order.py index 5caab77..7ead56a 100644 --- a/experiments/summarize_density_order.py +++ b/experiments/summarize_density_order.py @@ -83,6 +83,20 @@ def density_metrics(case, key): "interval_negative_enthalpy_witnesses": witness_h.get("negative_enthalpy_samples")} +def check_non_density_layouts(records): + reference = {} + for (campaign, name), (_, (_, mesh, case)) in records.items(): + model = case["n"], case["J"] + layout = {key: value for key, value in case["block_sizes"].items() + if key not in ("value/density", "residual/barotropic_closure")} + current = mesh, layout + if model in reference: + require(current == reference[model], + f"Non-density layout or mesh changed for (n,J)={model}: {campaign}/{name}") + else: + reference[model] = current + + def build_rows(audits): """Deduplicate overlapping audited case records, never average repeat runs.""" records = {} @@ -97,6 +111,9 @@ def build_rows(audits): require(records[key][1] == payload, "Overlapping audit records disagree: " + str(key)) else: records[key] = audit_path, payload + # Separate passing audits may never have seen the other density orders. + # Enforce their shared non-density discretization before deriving ratios. + check_non_density_layouts(records) frozen, coupled = [], [] for (campaign, name), (audit_path, (commit, mesh, case)) in sorted(records.items()): common = {"audit_json": audit_path, "campaign": campaign, "case": name, "producer_commit": commit, @@ -298,6 +315,21 @@ def plot_figure(frozen, coupled, output): class Tests(unittest.TestCase): + def test_cross_audit_non_density_layout(self): + def case(density_size, gravity_size=23): + return {"n": 1.5, "J": 0., "block_sizes": {"value/density": density_size, + "residual/barotropic_closure": density_size, "value/gravity_potential": gravity_size, + "residual/poisson_balance": gravity_size}} + records = {("q2", "sample"): ("audit_q2.json", ("commit2", "mesh", case(27))), + ("q3", "sample"): ("audit_q3.json", ("commit3", "mesh", case(64)))} + check_non_density_layouts(records) + records[("q3", "sample")] = ("audit_q3.json", ("commit3", "mesh", case(64, 24))) + with self.assertRaisesRegex(ValueError, "Non-density layout"): + check_non_density_layouts(records) + records[("q3", "sample")] = ("audit_q3.json", ("commit3", "changed_mesh", case(64))) + with self.assertRaisesRegex(ValueError, "mesh changed"): + check_non_density_layouts(records) + def test_uncertain_plot_observation_breaks_trend(self): row = {"negative_mass": 1e-30, "negative_classification": "uncertain_witnesses_only"} plotted, note = plot_observation(row, "negative_mass", negative=True) @@ -413,6 +445,7 @@ def main(): summary = {"schema": "density_order_summary_v1", "generator_sha256": digest(Path(__file__)), "input_audit_sha256": {str(path.resolve()): digest(path) for path in args.audits}, "scope": "Presentation of audited observations only; no campaign files reopened and no independent FEM work.", + "cross_order_layout_check": "Mesh identity and all non-density block names/sizes match for every repeated (n,J), including records loaded from separate audits.", "notes": ["Frozen controls are fixed-q26 L2 projections; EOS error norms use separate unsliced q30.", "Negative-mass denominator is the absolute measured density mass, not a substituted exact target mass.", "Uncertainty scales are reconstruction indicators, not rigorous bounds; zero observed negativity is not a global positivity certificate.",