physmap 0.2.4__tar.gz → 0.2.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. {physmap-0.2.4/src/physmap.egg-info → physmap-0.2.5}/PKG-INFO +154 -109
  2. {physmap-0.2.4 → physmap-0.2.5}/README.md +151 -108
  3. {physmap-0.2.4 → physmap-0.2.5}/pyproject.toml +2 -1
  4. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/__init__.py +1 -1
  5. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/_paths.py +11 -1
  6. physmap-0.2.5/src/physmap/benchmarks/architecture_axis.py +475 -0
  7. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/benchmarks/benchmark_v0_4.py +10 -1
  8. physmap-0.2.5/src/physmap/benchmarks/home_baseline.py +372 -0
  9. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/benchmarks/registry.py +1 -1
  10. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/benchmarks/report.py +43 -2
  11. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/cli.py +60 -4
  12. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/stress_tests/lewis_reuse.py +3 -0
  13. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/substrate/loaders.py +47 -23
  14. {physmap-0.2.4 → physmap-0.2.5/src/physmap.egg-info}/PKG-INFO +154 -109
  15. {physmap-0.2.4 → physmap-0.2.5}/src/physmap.egg-info/SOURCES.txt +22 -1
  16. {physmap-0.2.4 → physmap-0.2.5}/src/physmap.egg-info/requires.txt +3 -0
  17. physmap-0.2.5/tests/test_architecture_axis.py +105 -0
  18. {physmap-0.2.4 → physmap-0.2.5}/tests/test_benchmark_reproduces.py +2 -1
  19. physmap-0.2.5/tests/test_calibration_corpus.py +295 -0
  20. physmap-0.2.5/tests/test_casper_vehicle.py +61 -0
  21. physmap-0.2.5/tests/test_closure_index.py +80 -0
  22. physmap-0.2.5/tests/test_closures_registry.py +185 -0
  23. physmap-0.2.5/tests/test_corpus_real.py +102 -0
  24. physmap-0.2.5/tests/test_corpus_resolver.py +53 -0
  25. physmap-0.2.5/tests/test_d3_pipeline.py +462 -0
  26. physmap-0.2.5/tests/test_dirker_water_middle.py +111 -0
  27. physmap-0.2.5/tests/test_evidence_corpus_validators.py +551 -0
  28. physmap-0.2.5/tests/test_home_baseline.py +154 -0
  29. physmap-0.2.5/tests/test_marineau_vehicle.py +63 -0
  30. physmap-0.2.5/tests/test_observability.py +158 -0
  31. physmap-0.2.5/tests/test_regime_observability_mapping.py +284 -0
  32. physmap-0.2.5/tests/test_resolution_semantics.py +111 -0
  33. physmap-0.2.5/tests/test_split_corpus.py +122 -0
  34. {physmap-0.2.4 → physmap-0.2.5}/tests/test_talk_package.py +2 -2
  35. physmap-0.2.5/tests/test_vehicle_config.py +182 -0
  36. physmap-0.2.5/tests/test_velazquez_sco2_middle.py +87 -0
  37. physmap-0.2.5/tests/test_weighting_heuristic.py +284 -0
  38. {physmap-0.2.4 → physmap-0.2.5}/LICENSE +0 -0
  39. {physmap-0.2.4 → physmap-0.2.5}/LICENSE-CORPUS +0 -0
  40. {physmap-0.2.4 → physmap-0.2.5}/NOTICE +0 -0
  41. {physmap-0.2.4 → physmap-0.2.5}/setup.cfg +0 -0
  42. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/applicability/__init__.py +0 -0
  43. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/applicability/fixtures.py +0 -0
  44. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/applicability/screen.py +0 -0
  45. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/baselines/__init__.py +0 -0
  46. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/benchmarks/__init__.py +0 -0
  47. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/benchmarks/benchmark_report.py +0 -0
  48. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/benchmarks/compare.py +0 -0
  49. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/closures/__init__.py +0 -0
  50. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/closures/data/__init__.py +0 -0
  51. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/closures/data/closure_index.json +0 -0
  52. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/closures/formulas.py +0 -0
  53. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/closures/geometry_classes.py +0 -0
  54. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/closures/index.py +0 -0
  55. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/closures/registry.py +0 -0
  56. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/compat/__init__.py +0 -0
  57. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/core/__init__.py +0 -0
  58. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/core/mechanism.py +0 -0
  59. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/core/signals.py +0 -0
  60. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/corpus/__init__.py +0 -0
  61. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/corpus/calibration.py +0 -0
  62. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/corpus/data/__init__.py +0 -0
  63. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/corpus/data/corpus_seed.jsonl +0 -0
  64. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/corpus/data/evidence_claims_seed.jsonl +0 -0
  65. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/corpus/data/evidence_sources_seed.jsonl +0 -0
  66. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/corpus/data/premium_coverage.json +0 -0
  67. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/corpus/evidence.py +0 -0
  68. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/explain/__init__.py +0 -0
  69. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/explain/benchmark.py +0 -0
  70. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/explain/causal.py +0 -0
  71. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/guardrail/__init__.py +0 -0
  72. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/guardrail/aggregator_observability.py +0 -0
  73. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/guardrail/classify.py +0 -0
  74. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/guardrail/configs.py +0 -0
  75. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/guardrail/corpus_regimes.py +0 -0
  76. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/guardrail/detector_conformal.py +0 -0
  77. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/guardrail/detector_density.py +0 -0
  78. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/guardrail/enums.py +0 -0
  79. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/guardrail/graph.py +0 -0
  80. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/guardrail/guardrail.py +0 -0
  81. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/guardrail/io.py +0 -0
  82. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/guardrail/regime_observability.py +0 -0
  83. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/guardrail/render.py +0 -0
  84. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/guardrail/weighting_heuristic.py +0 -0
  85. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/infra/__init__.py +0 -0
  86. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/infra/blindspot_oracle.py +0 -0
  87. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/infra/corpus_runtime.py +0 -0
  88. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/integrations/__init__.py +0 -0
  89. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/materiality/__init__.py +0 -0
  90. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/materiality/estimator.py +0 -0
  91. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/materiality/independence.py +0 -0
  92. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/materiality/surrogate_fit.py +0 -0
  93. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/observability/__init__.py +0 -0
  94. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/pipeline/__init__.py +0 -0
  95. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/pipeline/aggregators.py +0 -0
  96. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/pipeline/assessment_v06.py +0 -0
  97. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/pipeline/core.py +0 -0
  98. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/pipeline/defeasible_aggregator.py +0 -0
  99. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/pipeline/detectors.py +0 -0
  100. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/pipeline/observability.py +0 -0
  101. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/pipeline/surrogate.py +0 -0
  102. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/pipeline/validity_signal.py +0 -0
  103. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/pipeline/vehicle_spec.py +0 -0
  104. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/release.py +0 -0
  105. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/stress_tests/__init__.py +0 -0
  106. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/substrate/__init__.py +0 -0
  107. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/substrate/corpus_real.py +0 -0
  108. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/substrate/engine.py +0 -0
  109. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/substrate/forrest.py +0 -0
  110. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/substrate/naca_tn1451.py +0 -0
  111. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/substrate/naca_wpd_loader.py +0 -0
  112. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/substrate/stage1_ingest.py +0 -0
  113. {physmap-0.2.4 → physmap-0.2.5}/src/physmap/substrate/vehicle_config.py +0 -0
  114. {physmap-0.2.4 → physmap-0.2.5}/src/physmap.egg-info/dependency_links.txt +0 -0
  115. {physmap-0.2.4 → physmap-0.2.5}/src/physmap.egg-info/entry_points.txt +0 -0
  116. {physmap-0.2.4 → physmap-0.2.5}/src/physmap.egg-info/top_level.txt +0 -0
  117. {physmap-0.2.4 → physmap-0.2.5}/tests/test_applicability_screen.py +0 -0
  118. {physmap-0.2.4 → physmap-0.2.5}/tests/test_benchmark_boundaries.py +0 -0
  119. {physmap-0.2.4 → physmap-0.2.5}/tests/test_causal_materiality.py +0 -0
  120. {physmap-0.2.4 → physmap-0.2.5}/tests/test_cli_without_checkout.py +0 -0
  121. {physmap-0.2.4 → physmap-0.2.5}/tests/test_guardrail_public_api.py +0 -0
  122. {physmap-0.2.4 → physmap-0.2.5}/tests/test_no_conflation.py +0 -0
  123. {physmap-0.2.4 → physmap-0.2.5}/tests/test_no_heavy_imports.py +0 -0
  124. {physmap-0.2.4 → physmap-0.2.5}/tests/test_render_ood_wording.py +0 -0
  125. {physmap-0.2.4 → physmap-0.2.5}/tests/test_stress_test_lewis_reuse.py +0 -0
  126. {physmap-0.2.4 → physmap-0.2.5}/tests/test_surrogate_fit.py +0 -0
  127. {physmap-0.2.4 → physmap-0.2.5}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: physmap
3
- Version: 0.2.4
3
+ Version: 0.2.5
4
4
  Summary: Physics-aware credibility checks for AI surrogates in multiphysics simulation: closure validity, surrogate observability, and causal materiality.
5
5
  Author: Vishnu Vettrivel
6
6
  License-Expression: MIT
@@ -28,6 +28,8 @@ Requires-Dist: matplotlib>=3.5; extra == "experiment"
28
28
  Provides-Extra: dev
29
29
  Requires-Dist: pytest>=7.4; extra == "dev"
30
30
  Requires-Dist: pytest-xdist>=3.5; extra == "dev"
31
+ Provides-Extra: architectures
32
+ Requires-Dist: torch>=2.2; extra == "architectures"
31
33
  Dynamic: license-file
32
34
 
33
35
  # PhysMAP
@@ -36,18 +38,41 @@ Dynamic: license-file
36
38
 
37
39
  Physics-aware credibility checks for AI surrogates in multiphysics simulation.
38
40
 
39
- A surrogate trained on `(Re, Pr)` over the fully-developed region of a heated pipe is
40
- blind to `x/D`, the variable that actually governs the entrance region. So it fails
41
- there, confidently. And so does an input-based OOD detector, because it sees the
42
- same two columns the surrogate does — and in those two columns the entrance points look
43
- perfectly ordinary.
41
+ An input-based OOD detector asks whether a prediction's inputs look familiar. PhysMAP asks two
42
+ other questions: **does the physics the model relies on still apply here**, and **does a
43
+ mechanism the model left out materially change the quantity of interest?** It keeps the OOD
44
+ detectors, and adds the checks they cannot make — because what breaks a surrogate is often not
45
+ one of its inputs.
44
46
 
45
- PhysMAP reads the bound variable from the test coordinates instead of from the
46
- surrogate's inputs, checks it against the closure relation's validated range, and knows
47
- at fit time whether that variable is structurally observable to the surrogate at all.
47
+ - **Applicability assurance** — shown on the entrance region of a heated pipe (NACA TN-1451).
48
+ - **Causal materiality** — shown in a controlled model-reuse stress test on Lewis (1992)
49
+ Test 35A, the main controlled demonstration.
50
+ - **Supporting evidence** across seven published datasets, led by a hypersonic transition case
51
+ (Casper) where three different surrogate architectures give the same result.
48
52
 
49
- **That is the point of PhysMAP: it catches the failures an input-based OOD detector cannot
50
- see, because what broke the surrogate is not one of its inputs.**
53
+ No LLM is in any path. Every explanation is a deterministic rendered template.
54
+
55
+ ## Applicability assurance: the NACA x/D entrance region
56
+
57
+ ![Applicability assurance: the x/D entrance region](https://raw.githubusercontent.com/cloudronin/physmap/main/docs/talk/figures/bench_1_naca_entrance.png)
58
+
59
+ A heat-transfer surrogate that takes only `(Re, Pr)` relies on a closure — here the Gnielinski
60
+ correlation — validated for fully developed flow, from x/D = 10 on. Near a pipe's inlet, `x/D`
61
+ governs the heat transfer, and neither the surrogate nor an input-based OOD detector sees it:
62
+ in `(Re, Pr)` the entrance points look perfectly ordinary. On NACA TN-1451's Fig 10 (two-reader
63
+ digitisation), against the measurement, at the numerical-error threshold (17.5 %):
64
+
65
+ - Gnielinski is **within the threshold on 40 of 40** fully developed points.
66
+ - It **exceeds the threshold on 9 of 45** entrance points — all at x/D ≤ 5, the largest 38 % at
67
+ the inlet.
68
+ - PhysMAP identifies **all 45 entrance predictions as outside the closure's supported
69
+ applicability region**. The input-based OOD detectors fire on none.
70
+ - **The other 36 are numerically acceptable, but not physically supported by that closure.**
71
+
72
+ Numerical agreement does not by itself establish that a prediction is credibly supported.
73
+ PhysMAP reads the bound variable from the test coordinates rather than from the surrogate's
74
+ inputs, checks it against the closure's validated range, and knows at setup that `x/D` is
75
+ structurally invisible to this surrogate:
51
76
 
52
77
  ```
53
78
  verdicts: {'REJECT': 45}
@@ -58,58 +83,132 @@ rationale: Prediction relies on gnielinski-1976 beyond its validated x_over_D bo
58
83
  because they cannot observe x_over_D.
59
84
  ```
60
85
 
61
- No LLM is in any path. Every explanation is a deterministic rendered template.
86
+ The −63% is the literature's figure; on this data the largest entrance error is 38%. Run it:
87
+ [`examples/naca_entrance_region.py`](https://github.com/cloudronin/physmap/blob/main/examples/naca_entrance_region.py).
88
+
89
+ ## Causal materiality: a controlled model-reuse stress test (Lewis 35A)
90
+
91
+ Applicability asks whether the physics a prediction relies on still applies. This asks
92
+ something else: when a mechanism the surrogate never saw becomes active, does it change the
93
+ answer — and where?
62
94
 
63
- Run it yourself: [`examples/naca_entrance_region.py`](https://github.com/cloudronin/physmap/blob/main/examples/naca_entrance_region.py).
95
+ ```bash
96
+ physmap stress-test lewis-reuse
97
+ ```
64
98
 
65
- ## What PhysMAP adds to OOD detection
99
+ **The setup.** The surrogate was trained for forced convection, where gravity did not vary
100
+ and was not an input. It was then reused in vertical heated flow, where buoyancy became
101
+ material. A mixed-convection surrogate designed for this regime should include Richardson
102
+ number, Grashof number, or equivalent physical information — this test reuses one without
103
+ it, on purpose.
66
104
 
67
- ![What PhysMAP adds to input-based OOD detection, per dataset](https://raw.githubusercontent.com/cloudronin/physmap/main/docs/talk/figures/bench_3_what_physmap_adds.png)
105
+ **The claim.** PhysMAP detects when model reuse activates a physically relevant mechanism
106
+ outside the surrogate's observable input space. An input-only OOD detector cannot identify a
107
+ change absent from its input contract.
68
108
 
69
- An input-based OOD detector judges a prediction by where its inputs sit relative to the
70
- training data. It asks that well — but it cannot see a change in something that is not an
71
- input. PhysMAP adds the check it cannot make: it reads the variables the surrogate never saw,
72
- tests the physics relation behind the surrogate against its validated range, and knows at
73
- setup which of those variables the detectors can see. It keeps the OOD detectors, and
74
- overrides them only where they are structurally blind.
109
+ **The input contract.** Both the surrogate and the input-based OOD detector — the
110
+ seven-vehicle benchmark's own, unchanged — receive `Re`, `Pr` and `x_over_D`. Neither
111
+ receives gravity, `Ri`, `Gr` or heat flux. **Every visible deployment input exactly matches a
112
+ training input.**
75
113
 
76
- The seven-vehicle benchmark measures exactly that, per dataset, at the default setting (the
77
- 99th percentile):
114
+ **The result**, same visible inputs, two physical states, on Lewis's (1992) vertical-tube
115
+ experiment:
78
116
 
79
- | Dataset | Can the OOD detectors see the cause? | Wrong predictions caught only by PhysMAP | Right predictions flagged anyway |
117
+ | | surrogate error | input-based OOD scores | PhysMAP materiality |
80
118
  |---|---|---|---|
81
- | `naca_tn1451` — heated pipe, entrance region | no | 20 of 20 | 24 |
82
- | `casper_hypersonic_transition` — hypersonic transition | no | 4 of 8 | 0 |
83
- | `jin_sco2_buoyancy` — supercritical CO2, vertical tube | no | 15 of 26 | 0 |
84
- | `velazquez_sco2` — supercritical CO2, property variation | partly | 18 of 67 | 0 |
85
- | `dirker_water` — water, horizontal tube | partly | 2 of 11 | 16 |
86
- | `marineau_hypersonic_transition` — hypersonic transition | yes | 0 of 6 | 0 |
87
- | `forrest` — rectangular channel | yes | not tested: one training row | — |
88
-
89
- - **Where it helps:** when the cause of failure is hidden from the surrogate's inputs. There
90
- PhysMAP catches wrong predictions the OOD detectors miss entirely — all 20 for the pipe
91
- entrance.
92
- - **Where it doesn't:** when the cause is an input, the OOD detectors already see it and
93
- PhysMAP adds nothing. `marineau_hypersonic_transition` is the control that shows it.
94
- - **The cost:** the closure check flags anything outside a relation's validated range, even
95
- when the surrogate happens to be right — 24 false alarms for the pipe entrance, 16 for
96
- `dirker_water`.
97
-
98
- Counts are rows of each dataset, not independent cases, and are never pooled into a rate
99
- across datasets; the source values are digitised from publications. The benchmark's
100
- input-based detectors are distance-to-training and GP variance. `physmap benchmark report`
101
- prints these counts, and `physmap benchmark run` recomputes them from a clone.
102
-
103
- This is a different question from PhysMAP's causal-materiality check — whether a mechanism
104
- the surrogate never saw is large enough to matter — which is demonstrated separately below.
105
- Neither is evidence for the other.
119
+ | gravity off — the accurate control | within 0.06 % | quiet at the default setting; identical in both rows | 0 |
120
+ | gravity on — Lewis's measurement | 17–18 % downstream | quiet at the default setting; identical in both rows | up to 0.195 |
121
+
122
+ Identical OOD scores and zero-versus-0.195 materiality need no threshold. The flag threshold θ
123
+ is not yet locked, so materiality is reported as numbers, and θ = 0.10 appears only as an
124
+ illustration.
125
+
126
+ A secondary result shows specificity: when the visible operating point falls *between*
127
+ training runs, the OOD detector warns in both the accurate and the inaccurate case —
128
+ identically — while PhysMAP changes with the physical mechanism.
129
+
130
+ **What it is not.** Not a claim that OOD detectors fail in general — this one does exactly its
131
+ job. Not a suggestion to leave gravity out. Not a claim about NVIDIA PhysicsNeMo, whose OOD
132
+ and physics checks are distinct and were not run. **One run, a development demonstration:** its
133
+ stations are not independent cases, and no precision, recall or F1 is computed.
134
+
135
+ The command recomputes everything from this checkout in a few minutes, asserts the exact
136
+ input overlap and the unchanged OOD scores, and diffs itself against a committed bank — the
137
+ same contract as `physmap benchmark run`, deliberately kept a separate command because this is
138
+ a causal-materiality result. It recomputes from committed CFD-derived profiles and does not rerun
139
+ OpenFOAM. Full record:
140
+ [docs/findings/lewis-ood-head-to-head.md](https://github.com/cloudronin/physmap/blob/main/docs/findings/lewis-ood-head-to-head.md). The NAFEMS
141
+ talk package — figures, facts sheet, claims ledger, and a clean-clone reproduction record — is
142
+ in [docs/talk/](https://github.com/cloudronin/physmap/blob/main/docs/talk/README.md).
143
+
144
+
145
+ ## Supporting evidence: seven published datasets
146
+
147
+ The seven-vehicle benchmark asks, per dataset: which wrong predictions does PhysMAP flag that
148
+ the input-based detectors (distance-to-training and GP variance) miss — and was the surrogate
149
+ accurate at home, so that deployment is what created the failure?
150
+
151
+ **Casper leads.** Hypersonic transition; the cause, tunnel freestream noise, is not a surrogate
152
+ input. The surrogate is wrong on 6 of 159 home rows held out (4%), and on 8 of 8 deployed. A
153
+ Gaussian process, a DeepONet and gradient-boosted trees each pass the same home-accuracy check,
154
+ and **each leaves 4 of 8 deployment errors that only PhysMAP flags**.
155
+
156
+ **Dirker supports it.** Water in a horizontal tube; the cause, buoyancy, is partly visible to
157
+ the inputs. Wrong on 0 of 31 home rows held out, and on 11 of 60 deployed; PhysMAP flags 2 of
158
+ those 11 that the detectors miss.
159
+
160
+ ![The home baseline behind each count](https://raw.githubusercontent.com/cloudronin/physmap/main/docs/talk/figures/bench_4_home_baseline.png)
161
+
162
+ **The full matrix** — every dataset, at the default setting (the 99th percentile), with how its
163
+ home error was obtained, its error rates and its limitation:
164
+
165
+ | Dataset | Detectors see the cause? | Home error: how obtained | Home wrong | Deployed wrong | Wrong, flagged only by PhysMAP | Accurate, flagged anyway | Limitation |
166
+ |---|---|---|---|---|---|---|---|
167
+ | `casper_hypersonic_transition` — hypersonic transition | no | fitted; held out by refit (in-sample 2 of 159) | 6 of 159 (4%) | 8 of 8 (100%) | 4 of 8 | 0 | — |
168
+ | `dirker_water` — water, horizontal tube | partly | fitted; held out by refit (in-sample 0 of 31) | 0 of 31 (0%) | 11 of 60 (18%) | 2 of 11 | 16 | the cause is partly visible |
169
+ | `naca_tn1451` — heated pipe, entrance region | no | published correlation; 40 of 40 inside its range | 0 of 40 (0%) | 9 of 45 (20%) | 9 of 9 | 19 | the applicability case above, same data — not separate evidence |
170
+ | `jin_sco2_buoyancy` — supercritical CO2, vertical tube | no | published correlation; 11 of 17 inside its range | 17 of 17 (100%) | 26 of 27 (96%) | 15 of 26 | 0 | wrong almost everywhere: no home baseline |
171
+ | `velazquez_sco2` — supercritical CO2, property variation | partly | published correlation; 197 of 393 inside its range | 386 of 393 (98%) | 67 of 67 (100%) | 18 of 67 | 0 | wrong almost everywhere: no home baseline |
172
+ | `marineau_hypersonic_transition` — hypersonic transition | yes | fitted; held out by refit (in-sample 0 of 9) | 5 of 9 (56%) | 6 of 6 (100%) | 0 of 6 | 0 | the control; nine home rows are too few |
173
+ | `forrest` — rectangular channel | yes | published correlation | 0 of 1 | 4 of 4 (100%) | not tested | — | one training row; triage-grade values |
174
+
175
+ "Wrong" is each dataset's own threshold, set from measurement noise; "accurate" is a tighter
176
+ one, with a dead band between them. Home error is labelled by how it was obtained: where the
177
+ surrogate was fitted to the home rows, it is also refitted without each row in turn; a
178
+ published correlation was never fitted to them.
179
+
180
+ - **Where the evidence holds:** Casper and Dirker have a home baseline from which deployment
181
+ fails — and NACA, which is the applicability case above.
182
+ - **Where the reading stops:** Jin and Velazquez keep their counts, but their correlations are
183
+ wrong almost everywhere, at home too, so the counts cannot show that deployment created the
184
+ failure.
185
+ - **Where PhysMAP adds nothing:** Marineau, where the cause is a surrogate input and the
186
+ detectors already see it.
187
+ - **Accurate predictions flagged anyway:** PhysMAP flags every prediction outside a closure's
188
+ supported region, accurate or not — 19 for NACA, 16 for Dirker. As detection those are false
189
+ alarms; as applicability assurance, they are predictions the closure does not support.
190
+ - **The kind of model:** on Casper, three model types pass the home check and each leaves 4 of
191
+ 8 errors only PhysMAP flags. On NACA all three pass too, but its home reads repeat — 40 rows,
192
+ 5 distinct values — so leave-one-out says little there. On Jin and Velazquez none passes.
193
+
194
+ No gate decides a dataset's reading: the counts and rates are the evidence, and
195
+ `physmap benchmark report` prints each dataset's interpretation as prose. Counts are rows of
196
+ each dataset, not independent cases, never pooled into a rate, and digitised from publications.
197
+ A one-sided Fisher exact test was run after the counts were seen; it is reported as exploratory
198
+ and decides nothing.
199
+
200
+ **Bank v0.4.1.** It corrects the NACA source data: the original bank's NACA row came from an
201
+ automated read of the figure later found invalid — wrong axis calibration, points on gridline
202
+ crossings — and its "20 of 20" is withdrawn. The original bank is kept unchanged for audit, not
203
+ as evidence. See [`data/naca/CORRECTION_v0_4_1.md`](https://github.com/cloudronin/physmap/blob/main/data/naca/CORRECTION_v0_4_1.md).
106
204
 
107
205
  ## The benchmark: seven vehicles, all rerunnable
108
206
 
109
207
  ```bash
110
- physmap benchmark report # all seven outcomes and the counts above, each marked recomputed or banked
208
+ physmap benchmark report # all seven outcomes, the counts above and the home baseline
111
209
  physmap benchmark run # recomputes all seven from this checkout, diffs against the bank
112
210
  physmap benchmark coverage # what that subset does and does not cover
211
+ physmap benchmark architectures --banked # three model types per vehicle; drop --banked to retrain (PyTorch)
113
212
  ```
114
213
 
115
214
  All seven vehicles ran, all seven outcomes are reported, and **all seven now ship their
@@ -162,9 +261,9 @@ one to another.
162
261
 
163
262
  | Check | Question | Status | Evidence |
164
263
  |---|---|---|---|
165
- | **Closure validity** | Is this closure relation being applied outside the range it was calibrated on? | Shipping | the seven-vehicle benchmark, above |
166
- | **Surrogate observability** | Can the surrogate's inputs even represent the variable that governs the failure? | Shipping | the seven-vehicle benchmark, above |
167
- | **Causal materiality** | Is the out-of-range mechanism large enough to matter for the quantity of interest? | Preview — method, plus one controlled stress test | Lewis 35A, below |
264
+ | **Closure validity** | Is this closure relation being applied outside the range it was calibrated on? | Shipping | applicability assurance (NACA x/D); the seven-vehicle benchmark |
265
+ | **Surrogate observability** | Can the surrogate's inputs even represent the variable that governs the failure? | Shipping | the same |
266
+ | **Causal materiality** | Is the out-of-range mechanism large enough to matter for the quantity of interest? | Preview — method, plus one controlled stress test | Lewis 35A, the main controlled demonstration |
168
267
 
169
268
  ## What this release claims
170
269
 
@@ -192,61 +291,6 @@ not reproduction of the published numbers. The protocol defines `REPRODUCED` and
192
291
  unreachable, so the word cannot drift onto a weaker result. See
193
292
  [`protocols/`](https://github.com/cloudronin/physmap/tree/main/protocols).
194
293
 
195
- ## A separate question: causal materiality, in a controlled stress test (Lewis 35A)
196
-
197
- The benchmark asks whether PhysMAP catches failures the OOD detectors cannot see. This asks
198
- something else: when a mechanism the surrogate never saw becomes active, does it matter for the
199
- answer — and where?
200
-
201
- ```bash
202
- physmap stress-test lewis-reuse
203
- ```
204
-
205
- **The setup.** The surrogate was trained for forced convection, where gravity did not vary
206
- and was not an input. It was then reused in vertical heated flow, where buoyancy became
207
- material. A mixed-convection surrogate designed for this regime should include Richardson
208
- number, Grashof number, or equivalent physical information — this test reuses one without
209
- it, on purpose.
210
-
211
- **The claim.** PhysMAP detects when model reuse activates a physically relevant mechanism
212
- outside the surrogate's observable input space. An input-only OOD detector cannot identify a
213
- change absent from its input contract.
214
-
215
- **The input contract.** Both the surrogate and the input-based OOD detector — the
216
- seven-vehicle benchmark's own, unchanged — receive `Re`, `Pr` and `x_over_D`. Neither
217
- receives gravity, `Ri`, `Gr` or heat flux. **Every visible deployment input exactly matches a
218
- training input.**
219
-
220
- **The result**, same visible inputs, two physical states, on Lewis's (1992) vertical-tube
221
- experiment:
222
-
223
- | | surrogate error | input-based OOD scores | PhysMAP materiality |
224
- |---|---|---|---|
225
- | gravity off — the accurate control | within 0.06 % | identical in both rows | 0 |
226
- | gravity on — Lewis's measurement | 17–18 % downstream | identical in both rows | up to 0.195 |
227
-
228
- Identical OOD scores and zero-versus-0.195 materiality need no threshold. The flag threshold θ
229
- is not yet locked, so materiality is reported as numbers, and θ = 0.10 appears only as an
230
- illustration.
231
-
232
- A secondary result shows specificity: when the visible operating point falls *between*
233
- training runs, the OOD detector warns in both the accurate and the inaccurate case —
234
- identically — while PhysMAP changes with the physical mechanism.
235
-
236
- **What it is not.** Not a claim that OOD detectors fail in general — this one does exactly its
237
- job. Not a suggestion to leave gravity out. Not a claim about NVIDIA PhysicsNeMo, whose OOD
238
- and physics checks are distinct and were not run. **One run, a development demonstration:** its
239
- stations are not independent cases, and no precision, recall or F1 is computed.
240
-
241
- The command recomputes everything from this checkout in a few minutes, asserts the exact
242
- input overlap and the unchanged OOD scores, and diffs itself against a committed bank — the
243
- same contract as `physmap benchmark run`, deliberately kept a separate command because this is
244
- a causal-materiality result. It recomputes from committed CFD-derived profiles and does not rerun
245
- OpenFOAM. Full record:
246
- [docs/findings/lewis-ood-head-to-head.md](https://github.com/cloudronin/physmap/blob/main/docs/findings/lewis-ood-head-to-head.md). The NAFEMS
247
- talk package — figures, facts sheet, claims ledger, and a clean-clone reproduction record — is
248
- in [docs/talk/](https://github.com/cloudronin/physmap/blob/main/docs/talk/README.md).
249
-
250
294
  ## Install
251
295
 
252
296
  ```bash
@@ -268,7 +312,8 @@ redistribution terms and partly because it is not runtime data. From a plain
268
312
  `pip install physmap`, those commands say so in one sentence and stop.
269
313
 
270
314
  Optional extras: `[jsonld]` adds evidence export via `uofa`, `[experiment]` adds
271
- `matplotlib` for benchmark figures, `[dev]` adds the test tooling.
315
+ `matplotlib` for benchmark figures, `[architectures]` adds PyTorch for the DeepONet in
316
+ `physmap benchmark architectures`, `[dev]` adds the test tooling.
272
317
 
273
318
  ## Using the guardrail
274
319