physmap 0.2.3__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.3/src/physmap.egg-info → physmap-0.2.5}/PKG-INFO +160 -111
  2. {physmap-0.2.3 → physmap-0.2.5}/README.md +157 -110
  3. {physmap-0.2.3 → physmap-0.2.5}/pyproject.toml +2 -1
  4. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/__init__.py +1 -1
  5. {physmap-0.2.3 → 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.3 → 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.3 → physmap-0.2.5}/src/physmap/benchmarks/registry.py +1 -1
  10. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/benchmarks/report.py +43 -2
  11. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/cli.py +60 -4
  12. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/stress_tests/lewis_reuse.py +3 -0
  13. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/substrate/loaders.py +47 -23
  14. {physmap-0.2.3 → physmap-0.2.5/src/physmap.egg-info}/PKG-INFO +160 -111
  15. {physmap-0.2.3 → physmap-0.2.5}/src/physmap.egg-info/SOURCES.txt +22 -1
  16. {physmap-0.2.3 → 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.3 → 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.3 → 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.3 → physmap-0.2.5}/LICENSE +0 -0
  39. {physmap-0.2.3 → physmap-0.2.5}/LICENSE-CORPUS +0 -0
  40. {physmap-0.2.3 → physmap-0.2.5}/NOTICE +0 -0
  41. {physmap-0.2.3 → physmap-0.2.5}/setup.cfg +0 -0
  42. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/applicability/__init__.py +0 -0
  43. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/applicability/fixtures.py +0 -0
  44. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/applicability/screen.py +0 -0
  45. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/baselines/__init__.py +0 -0
  46. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/benchmarks/__init__.py +0 -0
  47. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/benchmarks/benchmark_report.py +0 -0
  48. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/benchmarks/compare.py +0 -0
  49. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/closures/__init__.py +0 -0
  50. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/closures/data/__init__.py +0 -0
  51. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/closures/data/closure_index.json +0 -0
  52. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/closures/formulas.py +0 -0
  53. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/closures/geometry_classes.py +0 -0
  54. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/closures/index.py +0 -0
  55. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/closures/registry.py +0 -0
  56. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/compat/__init__.py +0 -0
  57. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/core/__init__.py +0 -0
  58. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/core/mechanism.py +0 -0
  59. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/core/signals.py +0 -0
  60. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/corpus/__init__.py +0 -0
  61. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/corpus/calibration.py +0 -0
  62. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/corpus/data/__init__.py +0 -0
  63. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/corpus/data/corpus_seed.jsonl +0 -0
  64. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/corpus/data/evidence_claims_seed.jsonl +0 -0
  65. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/corpus/data/evidence_sources_seed.jsonl +0 -0
  66. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/corpus/data/premium_coverage.json +0 -0
  67. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/corpus/evidence.py +0 -0
  68. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/explain/__init__.py +0 -0
  69. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/explain/benchmark.py +0 -0
  70. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/explain/causal.py +0 -0
  71. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/guardrail/__init__.py +0 -0
  72. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/guardrail/aggregator_observability.py +0 -0
  73. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/guardrail/classify.py +0 -0
  74. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/guardrail/configs.py +0 -0
  75. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/guardrail/corpus_regimes.py +0 -0
  76. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/guardrail/detector_conformal.py +0 -0
  77. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/guardrail/detector_density.py +0 -0
  78. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/guardrail/enums.py +0 -0
  79. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/guardrail/graph.py +0 -0
  80. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/guardrail/guardrail.py +0 -0
  81. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/guardrail/io.py +0 -0
  82. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/guardrail/regime_observability.py +0 -0
  83. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/guardrail/render.py +0 -0
  84. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/guardrail/weighting_heuristic.py +0 -0
  85. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/infra/__init__.py +0 -0
  86. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/infra/blindspot_oracle.py +0 -0
  87. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/infra/corpus_runtime.py +0 -0
  88. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/integrations/__init__.py +0 -0
  89. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/materiality/__init__.py +0 -0
  90. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/materiality/estimator.py +0 -0
  91. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/materiality/independence.py +0 -0
  92. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/materiality/surrogate_fit.py +0 -0
  93. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/observability/__init__.py +0 -0
  94. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/pipeline/__init__.py +0 -0
  95. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/pipeline/aggregators.py +0 -0
  96. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/pipeline/assessment_v06.py +0 -0
  97. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/pipeline/core.py +0 -0
  98. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/pipeline/defeasible_aggregator.py +0 -0
  99. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/pipeline/detectors.py +0 -0
  100. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/pipeline/observability.py +0 -0
  101. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/pipeline/surrogate.py +0 -0
  102. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/pipeline/validity_signal.py +0 -0
  103. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/pipeline/vehicle_spec.py +0 -0
  104. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/release.py +0 -0
  105. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/stress_tests/__init__.py +0 -0
  106. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/substrate/__init__.py +0 -0
  107. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/substrate/corpus_real.py +0 -0
  108. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/substrate/engine.py +0 -0
  109. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/substrate/forrest.py +0 -0
  110. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/substrate/naca_tn1451.py +0 -0
  111. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/substrate/naca_wpd_loader.py +0 -0
  112. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/substrate/stage1_ingest.py +0 -0
  113. {physmap-0.2.3 → physmap-0.2.5}/src/physmap/substrate/vehicle_config.py +0 -0
  114. {physmap-0.2.3 → physmap-0.2.5}/src/physmap.egg-info/dependency_links.txt +0 -0
  115. {physmap-0.2.3 → physmap-0.2.5}/src/physmap.egg-info/entry_points.txt +0 -0
  116. {physmap-0.2.3 → physmap-0.2.5}/src/physmap.egg-info/top_level.txt +0 -0
  117. {physmap-0.2.3 → physmap-0.2.5}/tests/test_applicability_screen.py +0 -0
  118. {physmap-0.2.3 → physmap-0.2.5}/tests/test_benchmark_boundaries.py +0 -0
  119. {physmap-0.2.3 → physmap-0.2.5}/tests/test_causal_materiality.py +0 -0
  120. {physmap-0.2.3 → physmap-0.2.5}/tests/test_cli_without_checkout.py +0 -0
  121. {physmap-0.2.3 → physmap-0.2.5}/tests/test_guardrail_public_api.py +0 -0
  122. {physmap-0.2.3 → physmap-0.2.5}/tests/test_no_conflation.py +0 -0
  123. {physmap-0.2.3 → physmap-0.2.5}/tests/test_no_heavy_imports.py +0 -0
  124. {physmap-0.2.3 → physmap-0.2.5}/tests/test_render_ood_wording.py +0 -0
  125. {physmap-0.2.3 → physmap-0.2.5}/tests/test_stress_test_lewis_reuse.py +0 -0
  126. {physmap-0.2.3 → physmap-0.2.5}/tests/test_surrogate_fit.py +0 -0
  127. {physmap-0.2.3 → 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.3
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,24 +28,51 @@ 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
34
36
 
37
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.22950742.svg)](https://doi.org/10.5281/zenodo.22950742)
38
+
35
39
  Physics-aware credibility checks for AI surrogates in multiphysics simulation.
36
40
 
37
- A surrogate trained on `(Re, Pr)` over the fully-developed region of a heated pipe is
38
- blind to `x/D`, the variable that actually governs the entrance region. So it fails
39
- there, confidently. And so does an input-based OOD detector, because it sees the
40
- same two columns the surrogate does — and in those two columns the entrance points look
41
- 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.
46
+
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.
52
+
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 %):
42
64
 
43
- PhysMAP reads the bound variable from the test coordinates instead of from the
44
- surrogate's inputs, checks it against the closure relation's validated range, and knows
45
- at fit time whether that variable is structurally observable to the surrogate at all.
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.**
46
71
 
47
- **That is the point of PhysMAP: it catches the failures an input-based OOD detector cannot
48
- see, because what broke the surrogate is not one of its inputs.**
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:
49
76
 
50
77
  ```
51
78
  verdicts: {'REJECT': 45}
@@ -56,58 +83,132 @@ rationale: Prediction relies on gnielinski-1976 beyond its validated x_over_D bo
56
83
  because they cannot observe x_over_D.
57
84
  ```
58
85
 
59
- 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)
60
90
 
61
- Run it yourself: [`examples/naca_entrance_region.py`](https://github.com/cloudronin/physmap/blob/main/examples/naca_entrance_region.py).
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?
94
+
95
+ ```bash
96
+ physmap stress-test lewis-reuse
97
+ ```
62
98
 
63
- ## 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.
64
104
 
65
- ![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.
66
108
 
67
- An input-based OOD detector judges a prediction by where its inputs sit relative to the
68
- training data. It asks that well — but it cannot see a change in something that is not an
69
- input. PhysMAP adds the check it cannot make: it reads the variables the surrogate never saw,
70
- tests the physics relation behind the surrogate against its validated range, and knows at
71
- setup which of those variables the detectors can see. It keeps the OOD detectors, and
72
- 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.**
73
113
 
74
- The seven-vehicle benchmark measures exactly that, per dataset, at the default setting (the
75
- 99th percentile):
114
+ **The result**, same visible inputs, two physical states, on Lewis's (1992) vertical-tube
115
+ experiment:
76
116
 
77
- | 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 |
78
118
  |---|---|---|---|
79
- | `naca_tn1451` — heated pipe, entrance region | no | 20 of 20 | 24 |
80
- | `casper_hypersonic_transition` — hypersonic transition | no | 4 of 8 | 0 |
81
- | `jin_sco2_buoyancy` — supercritical CO2, vertical tube | no | 15 of 26 | 0 |
82
- | `velazquez_sco2` — supercritical CO2, property variation | partly | 18 of 67 | 0 |
83
- | `dirker_water` — water, horizontal tube | partly | 2 of 11 | 16 |
84
- | `marineau_hypersonic_transition` — hypersonic transition | yes | 0 of 6 | 0 |
85
- | `forrest` — rectangular channel | yes | not tested: one training row | — |
86
-
87
- - **Where it helps:** when the cause of failure is hidden from the surrogate's inputs. There
88
- PhysMAP catches wrong predictions the OOD detectors miss entirely — all 20 for the pipe
89
- entrance.
90
- - **Where it doesn't:** when the cause is an input, the OOD detectors already see it and
91
- PhysMAP adds nothing. `marineau_hypersonic_transition` is the control that shows it.
92
- - **The cost:** the closure check flags anything outside a relation's validated range, even
93
- when the surrogate happens to be right — 24 false alarms for the pipe entrance, 16 for
94
- `dirker_water`.
95
-
96
- Counts are rows of each dataset, not independent cases, and are never pooled into a rate
97
- across datasets; the source values are digitised from publications. The benchmark's
98
- input-based detectors are distance-to-training and GP variance. `physmap benchmark report`
99
- prints these counts, and `physmap benchmark run` recomputes them from a clone.
100
-
101
- This is a different question from PhysMAP's causal-materiality check — whether a mechanism
102
- the surrogate never saw is large enough to matter — which is demonstrated separately below.
103
- 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).
104
204
 
105
205
  ## The benchmark: seven vehicles, all rerunnable
106
206
 
107
207
  ```bash
108
- 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
109
209
  physmap benchmark run # recomputes all seven from this checkout, diffs against the bank
110
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)
111
212
  ```
112
213
 
113
214
  All seven vehicles ran, all seven outcomes are reported, and **all seven now ship their
@@ -160,9 +261,9 @@ one to another.
160
261
 
161
262
  | Check | Question | Status | Evidence |
162
263
  |---|---|---|---|
163
- | **Closure validity** | Is this closure relation being applied outside the range it was calibrated on? | Shipping | the seven-vehicle benchmark, above |
164
- | **Surrogate observability** | Can the surrogate's inputs even represent the variable that governs the failure? | Shipping | the seven-vehicle benchmark, above |
165
- | **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 |
166
267
 
167
268
  ## What this release claims
168
269
 
@@ -190,61 +291,6 @@ not reproduction of the published numbers. The protocol defines `REPRODUCED` and
190
291
  unreachable, so the word cannot drift onto a weaker result. See
191
292
  [`protocols/`](https://github.com/cloudronin/physmap/tree/main/protocols).
192
293
 
193
- ## A separate question: causal materiality, in a controlled stress test (Lewis 35A)
194
-
195
- The benchmark asks whether PhysMAP catches failures the OOD detectors cannot see. This asks
196
- something else: when a mechanism the surrogate never saw becomes active, does it matter for the
197
- answer — and where?
198
-
199
- ```bash
200
- physmap stress-test lewis-reuse
201
- ```
202
-
203
- **The setup.** The surrogate was trained for forced convection, where gravity did not vary
204
- and was not an input. It was then reused in vertical heated flow, where buoyancy became
205
- material. A mixed-convection surrogate designed for this regime should include Richardson
206
- number, Grashof number, or equivalent physical information — this test reuses one without
207
- it, on purpose.
208
-
209
- **The claim.** PhysMAP detects when model reuse activates a physically relevant mechanism
210
- outside the surrogate's observable input space. An input-only OOD detector cannot identify a
211
- change absent from its input contract.
212
-
213
- **The input contract.** Both the surrogate and the input-based OOD detector — the
214
- seven-vehicle benchmark's own, unchanged — receive `Re`, `Pr` and `x_over_D`. Neither
215
- receives gravity, `Ri`, `Gr` or heat flux. **Every visible deployment input exactly matches a
216
- training input.**
217
-
218
- **The result**, same visible inputs, two physical states, on Lewis's (1992) vertical-tube
219
- experiment:
220
-
221
- | | surrogate error | input-based OOD scores | PhysMAP materiality |
222
- |---|---|---|---|
223
- | gravity off — the accurate control | within 0.06 % | identical in both rows | 0 |
224
- | gravity on — Lewis's measurement | 17–18 % downstream | identical in both rows | up to 0.195 |
225
-
226
- Identical OOD scores and zero-versus-0.195 materiality need no threshold. The flag threshold θ
227
- is not yet locked, so materiality is reported as numbers, and θ = 0.10 appears only as an
228
- illustration.
229
-
230
- A secondary result shows specificity: when the visible operating point falls *between*
231
- training runs, the OOD detector warns in both the accurate and the inaccurate case —
232
- identically — while PhysMAP changes with the physical mechanism.
233
-
234
- **What it is not.** Not a claim that OOD detectors fail in general — this one does exactly its
235
- job. Not a suggestion to leave gravity out. Not a claim about NVIDIA PhysicsNeMo, whose OOD
236
- and physics checks are distinct and were not run. **One run, a development demonstration:** its
237
- stations are not independent cases, and no precision, recall or F1 is computed.
238
-
239
- The command recomputes everything from this checkout in a few minutes, asserts the exact
240
- input overlap and the unchanged OOD scores, and diffs itself against a committed bank — the
241
- same contract as `physmap benchmark run`, deliberately kept a separate command because this is
242
- a causal-materiality result. It recomputes from committed CFD-derived profiles and does not rerun
243
- OpenFOAM. Full record:
244
- [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
245
- talk package — figures, facts sheet, claims ledger, and a clean-clone reproduction record — is
246
- in [docs/talk/](https://github.com/cloudronin/physmap/blob/main/docs/talk/README.md).
247
-
248
294
  ## Install
249
295
 
250
296
  ```bash
@@ -266,7 +312,8 @@ redistribution terms and partly because it is not runtime data. From a plain
266
312
  `pip install physmap`, those commands say so in one sentence and stop.
267
313
 
268
314
  Optional extras: `[jsonld]` adds evidence export via `uofa`, `[experiment]` adds
269
- `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.
270
317
 
271
318
  ## Using the guardrail
272
319
 
@@ -357,8 +404,10 @@ No source PDF is redistributed. Papers are cited, not copied.
357
404
 
358
405
  ## Citing
359
406
 
360
- [`CITATION.cff`](https://github.com/cloudronin/physmap/blob/main/CITATION.cff). Attribution is required by CC BY when you redistribute or
361
- build on the data.
407
+ Cite the software by its DOI, [10.5281/zenodo.22950742](https://doi.org/10.5281/zenodo.22950742). That DOI always points to
408
+ the latest version; each version also has its own DOI, listed on the Zenodo record.
409
+ [`CITATION.cff`](https://github.com/cloudronin/physmap/blob/main/CITATION.cff) holds the full metadata. Attribution is required by
410
+ CC BY when you redistribute or build on the data.
362
411
 
363
412
  ## Tests
364
413