physmap 0.2.0__py3-none-any.whl
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.
- physmap/__init__.py +61 -0
- physmap/_paths.py +69 -0
- physmap/applicability/__init__.py +0 -0
- physmap/applicability/fixtures.py +83 -0
- physmap/applicability/screen.py +99 -0
- physmap/baselines/__init__.py +0 -0
- physmap/benchmarks/__init__.py +0 -0
- physmap/benchmarks/benchmark_report.py +405 -0
- physmap/benchmarks/benchmark_v0_4.py +424 -0
- physmap/benchmarks/compare.py +149 -0
- physmap/benchmarks/registry.py +217 -0
- physmap/benchmarks/report.py +224 -0
- physmap/cli.py +301 -0
- physmap/closures/__init__.py +48 -0
- physmap/closures/data/__init__.py +7 -0
- physmap/closures/data/closure_index.json +2997 -0
- physmap/closures/formulas.py +213 -0
- physmap/closures/geometry_classes.py +109 -0
- physmap/closures/index.py +393 -0
- physmap/closures/registry.py +313 -0
- physmap/compat/__init__.py +0 -0
- physmap/core/__init__.py +0 -0
- physmap/core/mechanism.py +69 -0
- physmap/core/signals.py +50 -0
- physmap/corpus/__init__.py +12 -0
- physmap/corpus/calibration.py +543 -0
- physmap/corpus/data/__init__.py +12 -0
- physmap/corpus/data/corpus_seed.jsonl +15 -0
- physmap/corpus/data/evidence_claims_seed.jsonl +21 -0
- physmap/corpus/data/evidence_sources_seed.jsonl +8 -0
- physmap/corpus/data/premium_coverage.json +60 -0
- physmap/corpus/evidence.py +871 -0
- physmap/explain/__init__.py +0 -0
- physmap/explain/benchmark.py +101 -0
- physmap/explain/causal.py +82 -0
- physmap/guardrail/__init__.py +38 -0
- physmap/guardrail/aggregator_observability.py +187 -0
- physmap/guardrail/classify.py +147 -0
- physmap/guardrail/configs.py +120 -0
- physmap/guardrail/corpus_regimes.py +208 -0
- physmap/guardrail/detector_conformal.py +129 -0
- physmap/guardrail/detector_density.py +74 -0
- physmap/guardrail/enums.py +69 -0
- physmap/guardrail/graph.py +73 -0
- physmap/guardrail/guardrail.py +606 -0
- physmap/guardrail/io.py +201 -0
- physmap/guardrail/regime_observability.py +519 -0
- physmap/guardrail/render.py +159 -0
- physmap/guardrail/weighting_heuristic.py +216 -0
- physmap/infra/__init__.py +23 -0
- physmap/infra/blindspot_oracle.py +356 -0
- physmap/infra/corpus_runtime.py +275 -0
- physmap/integrations/__init__.py +0 -0
- physmap/materiality/__init__.py +0 -0
- physmap/materiality/estimator.py +239 -0
- physmap/materiality/independence.py +92 -0
- physmap/materiality/surrogate_fit.py +293 -0
- physmap/observability/__init__.py +0 -0
- physmap/pipeline/__init__.py +58 -0
- physmap/pipeline/aggregators.py +199 -0
- physmap/pipeline/assessment_v06.py +509 -0
- physmap/pipeline/core.py +442 -0
- physmap/pipeline/defeasible_aggregator.py +324 -0
- physmap/pipeline/detectors.py +309 -0
- physmap/pipeline/observability.py +430 -0
- physmap/pipeline/surrogate.py +251 -0
- physmap/pipeline/validity_signal.py +273 -0
- physmap/pipeline/vehicle_spec.py +287 -0
- physmap/release.py +81 -0
- physmap/stress_tests/__init__.py +9 -0
- physmap/stress_tests/lewis_reuse.py +517 -0
- physmap/substrate/__init__.py +28 -0
- physmap/substrate/corpus_real.py +206 -0
- physmap/substrate/engine.py +209 -0
- physmap/substrate/forrest.py +249 -0
- physmap/substrate/loaders.py +2176 -0
- physmap/substrate/naca_tn1451.py +379 -0
- physmap/substrate/naca_wpd_loader.py +187 -0
- physmap/substrate/stage1_ingest.py +187 -0
- physmap/substrate/vehicle_config.py +407 -0
- physmap-0.2.0.dist-info/METADATA +270 -0
- physmap-0.2.0.dist-info/RECORD +88 -0
- physmap-0.2.0.dist-info/WHEEL +5 -0
- physmap-0.2.0.dist-info/entry_points.txt +2 -0
- physmap-0.2.0.dist-info/licenses/LICENSE +21 -0
- physmap-0.2.0.dist-info/licenses/LICENSE-CORPUS +469 -0
- physmap-0.2.0.dist-info/licenses/NOTICE +77 -0
- physmap-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"""Pure Nu(...) closure formulas, extracted from substrate files.
|
|
2
|
+
|
|
3
|
+
Each function takes keyword-only inputs (so the substrate engine can pass an
|
|
4
|
+
input bag and each closure picks what it needs) and returns Nu as a numpy
|
|
5
|
+
array. Formulas are byte-faithful copies of the originals — DO NOT alter
|
|
6
|
+
the math; the byte-for-byte substrate-migration gate compares numerical
|
|
7
|
+
output exactly.
|
|
8
|
+
|
|
9
|
+
Inputs that may appear in the kwarg bag (not every closure uses every one):
|
|
10
|
+
Re Reynolds number (turbulent internal flow; or Re_x for flat plate)
|
|
11
|
+
Pr Prandtl number
|
|
12
|
+
Ra Rayleigh number (natural convection)
|
|
13
|
+
Nu_forced pre-computed forced-convection Nu (for Churchill blend)
|
|
14
|
+
Nu_natural pre-computed natural-convection Nu (for Churchill blend)
|
|
15
|
+
mu_ratio μ_bulk / μ_wall (Sieder-Tate viscosity correction)
|
|
16
|
+
heating bool — Dittus-Boelter n=0.4 (heating) vs 0.3 (cooling)
|
|
17
|
+
n Churchill blend exponent
|
|
18
|
+
|
|
19
|
+
Each closure declares the kwargs it uses in `registry.ClosureEntry.required_inputs`;
|
|
20
|
+
the substrate engine dispatches on that to populate only what each formula needs.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import numpy as np
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# ── internal-flow closures (Re, Pr) ─────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
def modified_sparrow_cur_nu(*, Re: np.ndarray, Pr: np.ndarray) -> np.ndarray:
|
|
31
|
+
"""Modified Sparrow-Cur (Forrest 2014 Eq. 7): Nu = 0.036·Re^0.76·Pr^(1/3).
|
|
32
|
+
|
|
33
|
+
Geometry-matched primary closure for one-sided heated narrow rectangular
|
|
34
|
+
mini-channels with high aspect ratio (alpha* < 0.1). Validated bounds:
|
|
35
|
+
Re in [10000, 70000], Pr in [2.2, 5.4]. Corpus entry:
|
|
36
|
+
modified-sparrow-cur-asym-narrow-rect-channel-2014 (status: extrapolated).
|
|
37
|
+
|
|
38
|
+
Original Sparrow & Cur (1982): Nu = 0.0464·Re^0.76 at fixed Pr=2.5,
|
|
39
|
+
isothermal wall, alpha*=0.0556, Re 10000-45000. Forrest 2014 modification:
|
|
40
|
+
Chilton-Colburn analogy gives explicit Pr^(1/3); coefficient adjusted
|
|
41
|
+
0.0464 -> 0.036 consistent with Pr^(1/3) at Pr=2.5; wall BC extended
|
|
42
|
+
from isothermal to uniform heat flux; Re extended empirically to 70000.
|
|
43
|
+
"""
|
|
44
|
+
Re = np.asarray(Re, dtype=float)
|
|
45
|
+
Pr = np.asarray(Pr, dtype=float)
|
|
46
|
+
return 0.036 * Re ** 0.76 * Pr ** (1.0 / 3.0)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def gnielinski_nu(*, Re: np.ndarray, Pr: np.ndarray) -> np.ndarray:
|
|
50
|
+
"""Gnielinski (1976): Nu_D = (f/8)(Re-1000)Pr / (1 + 12.7*sqrt(f/8)*(Pr^(2/3)-1))
|
|
51
|
+
where f = (0.790*ln(Re) - 1.64)^(-2) is Petukhov's friction factor.
|
|
52
|
+
|
|
53
|
+
Geometry: circular pipe. Validated Re in [3000, 5e6], Pr in [0.5, 2000]
|
|
54
|
+
per the corpus calibration entry gnielinski-1976.
|
|
55
|
+
"""
|
|
56
|
+
Re = np.asarray(Re, dtype=float)
|
|
57
|
+
Pr = np.asarray(Pr, dtype=float)
|
|
58
|
+
f = (0.790 * np.log(np.maximum(Re, 1.0)) - 1.64) ** (-2.0)
|
|
59
|
+
num = (f / 8.0) * (Re - 1000.0) * Pr
|
|
60
|
+
den = 1.0 + 12.7 * np.sqrt(f / 8.0) * (Pr ** (2.0 / 3.0) - 1.0)
|
|
61
|
+
return num / den
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def dittus_boelter_nu(*, Re: np.ndarray, Pr: np.ndarray,
|
|
65
|
+
heating: bool = True) -> np.ndarray:
|
|
66
|
+
"""Dittus-Boelter (McAdams form, 1930): Nu_D = 0.023 Re^0.8 Pr^n.
|
|
67
|
+
|
|
68
|
+
n = 0.4 for heating (fluid heated by wall), n = 0.3 for cooling.
|
|
69
|
+
Geometry: circular pipe. Validated Re in [10000, 1.2e6], Pr in [0.7, 160]
|
|
70
|
+
per the corpus calibration entry dittus-boelter-1930.
|
|
71
|
+
"""
|
|
72
|
+
Re = np.asarray(Re, dtype=float)
|
|
73
|
+
Pr = np.asarray(Pr, dtype=float)
|
|
74
|
+
n = 0.4 if heating else 0.3
|
|
75
|
+
return 0.023 * Re ** 0.8 * Pr ** n
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def petukhov_nu(*, Re: np.ndarray, Pr: np.ndarray) -> np.ndarray:
|
|
79
|
+
"""Petukhov (1970): Nu = (f/8)*Re*Pr / [1.07 + 12.7*sqrt(f/8)*(Pr^(2/3)-1)]
|
|
80
|
+
where f = (1.82*log10(Re) - 1.64)^(-2).
|
|
81
|
+
|
|
82
|
+
Geometry: circular pipe. NOT IN the calibration corpus (corpus has
|
|
83
|
+
Gnielinski/D-B/Sieder-Tate as the canonical turbulent-circular-pipe
|
|
84
|
+
triple); kept in the registry as reference-only for Forrest's
|
|
85
|
+
Table-4 cross-comparison.
|
|
86
|
+
"""
|
|
87
|
+
Re = np.asarray(Re, dtype=float)
|
|
88
|
+
Pr = np.asarray(Pr, dtype=float)
|
|
89
|
+
f = (1.82 * np.log10(np.maximum(Re, 1.0)) - 1.64) ** (-2.0)
|
|
90
|
+
num = (f / 8.0) * Re * Pr
|
|
91
|
+
den = 1.07 + 12.7 * np.sqrt(f / 8.0) * (Pr ** (2.0 / 3.0) - 1.0)
|
|
92
|
+
return num / den
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def sieder_tate_nu(*, Re: np.ndarray, Pr: np.ndarray,
|
|
96
|
+
mu_ratio: np.ndarray | float = 1.0) -> np.ndarray:
|
|
97
|
+
"""Sieder-Tate (1936): Nu_D = 0.027 Re^0.8 Pr^(1/3) (mu_b/mu_w)^0.14.
|
|
98
|
+
|
|
99
|
+
mu_ratio defaults to 1.0 (no viscosity correction); the substrate
|
|
100
|
+
engine may supply a row-level value if available. Geometry: circular pipe.
|
|
101
|
+
"""
|
|
102
|
+
Re = np.asarray(Re, dtype=float)
|
|
103
|
+
Pr = np.asarray(Pr, dtype=float)
|
|
104
|
+
return 0.027 * Re ** 0.8 * Pr ** (1.0 / 3.0) * mu_ratio ** 0.14
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# ── external flat-plate / vertical-plate closures ───────────────────────────
|
|
108
|
+
|
|
109
|
+
def pohlhausen_forced_local_nu(*, Re: np.ndarray, Pr: np.ndarray) -> np.ndarray:
|
|
110
|
+
"""Pohlhausen local laminar forced flat-plate: Nu_x = 0.332 Re_x^(1/2) Pr^(1/3).
|
|
111
|
+
|
|
112
|
+
LOCAL form (at position x); the length-averaged form is 0.664 Re_L^(1/2) Pr^(1/3).
|
|
113
|
+
The Lance & Smith substrate computes LOCAL Nu at each measurement position
|
|
114
|
+
for like-with-like comparison against the measured local q". Geometry:
|
|
115
|
+
flat_plate_external_forced. Validated Re in [0, 5e5], Pr in [0.6, 50].
|
|
116
|
+
Corpus entry: blasius-pohlhausen-flat-plate-forced-1921.
|
|
117
|
+
|
|
118
|
+
The kwarg is named `Re` (not `Re_x`) for uniform-bag dispatch; the substrate
|
|
119
|
+
engine populates Re with the local Re_x value at each row.
|
|
120
|
+
"""
|
|
121
|
+
Re = np.asarray(Re, dtype=float)
|
|
122
|
+
Pr = np.asarray(Pr, dtype=float)
|
|
123
|
+
return 0.332 * np.sqrt(np.maximum(Re, 0.0)) * Pr ** (1.0 / 3.0)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def mcadams_natural_local_nu(*, Ra: np.ndarray) -> np.ndarray:
|
|
127
|
+
"""McAdams local laminar natural vertical-plate: Nu_x = 0.59 Ra_x^(1/4).
|
|
128
|
+
|
|
129
|
+
Validated Ra in [1e4, 1e9], Pr in [0.6, 7] (Pr enters via Ra=Gr*Pr).
|
|
130
|
+
Geometry: vertical_plate_external_natural. Corpus entry:
|
|
131
|
+
mcadams-vertical-plate-natural-1954.
|
|
132
|
+
"""
|
|
133
|
+
Ra = np.asarray(Ra, dtype=float)
|
|
134
|
+
return 0.59 * np.maximum(Ra, 0.0) ** 0.25
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# ── composite / blender closures ────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
def churchill_mixed_nu(*, Nu_forced: np.ndarray, Nu_natural: np.ndarray,
|
|
140
|
+
n: float = 3.0) -> np.ndarray:
|
|
141
|
+
"""Churchill mixed-convection blend (assisting flow): Nu_M^n = Nu_F^n + Nu_N^n.
|
|
142
|
+
|
|
143
|
+
n=3 for assisting flow (standard Churchill exponent). Geometry:
|
|
144
|
+
flat_plate_external_mixed. NOT IN the calibration corpus today; kept in
|
|
145
|
+
the registry as the Lance & Smith matched-prediction surrogate.
|
|
146
|
+
"""
|
|
147
|
+
Nu_F = np.asarray(Nu_forced, float)
|
|
148
|
+
Nu_N = np.asarray(Nu_natural, float)
|
|
149
|
+
return (Nu_F ** n + Nu_N ** n) ** (1.0 / n)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def aung_worku_mixed_nu(*, Re: np.ndarray, Pr: np.ndarray) -> np.ndarray:
|
|
153
|
+
"""Aung & Worku (1986) mixed forced-natural convection — registered as a
|
|
154
|
+
CORPUS-VALIDITY ANCHOR ONLY, not as an executable predictor.
|
|
155
|
+
|
|
156
|
+
This closure exists in the registry so the substrate engine's geometry-match
|
|
157
|
+
invariant and matched-closure lookup resolve for the Stage-3 MIDDLE vehicle
|
|
158
|
+
(Dirker/Meyer/Reid water tube). main's regime framework maps
|
|
159
|
+
MIXED_CONVECTION_HORIZONTAL_TUBE -> this closure; it carries the
|
|
160
|
+
`richardson_number` validity bound (Ri in [0.1, 10]) that the vehicle's
|
|
161
|
+
closure-validity detector reads directly from the corpus. The vehicle's
|
|
162
|
+
surrogate-of-record is a data-driven GP fit (loader
|
|
163
|
+
`dirker_water_richardson_bands`), NOT this correlation, so the Nu formula is
|
|
164
|
+
deliberately unimplemented (the original is a vertical-channel Eq-13 family
|
|
165
|
+
form; porting it to the horizontal-tube geometry is out of scope and unused).
|
|
166
|
+
Raises loudly if ever dispatched as a predictor.
|
|
167
|
+
"""
|
|
168
|
+
raise NotImplementedError(
|
|
169
|
+
"aung-worku-mixed-convection-1986 is a corpus-validity anchor "
|
|
170
|
+
"(richardson_number bound) only; it has no executable Nu predictor. The "
|
|
171
|
+
"dirker_water Stage-3 vehicle uses a data-driven GP surrogate, not this "
|
|
172
|
+
"correlation."
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def pate_freestream_noise_bound(**kwargs) -> np.ndarray:
|
|
177
|
+
"""Pate-Stainback freestream-disturbance transition boundary — CORPUS-
|
|
178
|
+
VALIDITY ANCHOR ONLY, not an executable predictor.
|
|
179
|
+
|
|
180
|
+
Registered so the substrate engine's geometry-match invariant + matched-
|
|
181
|
+
closure lookup resolve for the aerospace Casper vehicle (quiet-vs-noisy
|
|
182
|
+
hypersonic transition). It carries the freestream_noise_rms_pitot_pct
|
|
183
|
+
validity bound (conventional tunnels validated for RMS Pitot >= ~0.5%);
|
|
184
|
+
the QUIET deploy points (~0.05%) fall below it and the closure-validity
|
|
185
|
+
detector fires. Casper's surrogate-of-record is a GP transition model
|
|
186
|
+
(loader `casper_hypersonic_transition`), so no Nu/rms formula is needed.
|
|
187
|
+
Raises loudly if ever dispatched as a predictor.
|
|
188
|
+
"""
|
|
189
|
+
raise NotImplementedError(
|
|
190
|
+
"pate-stainback-freestream-noise-hypersonic-1980 is a corpus-validity "
|
|
191
|
+
"anchor (freestream_noise_rms_pitot_pct bound) only; it has no executable "
|
|
192
|
+
"predictor. The Casper vehicle uses a GP surrogate."
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def marineau_entropy_shock_bound(**kwargs) -> np.ndarray:
|
|
197
|
+
"""Marineau entropy-layer / shock-interaction transition boundary — CORPUS-
|
|
198
|
+
VALIDITY ANCHOR ONLY, not an executable predictor.
|
|
199
|
+
|
|
200
|
+
Registered so the geometry-match invariant + matched-closure lookup resolve
|
|
201
|
+
for the aerospace Marineau vehicle (bluntness negative control). It carries
|
|
202
|
+
the entropy_layer_shock_ratio (S_T/X_SW) validity bound (>= 0.1 for the
|
|
203
|
+
e^N / 2nd-mode regime); the large-bluntness deploy points fall below it and
|
|
204
|
+
the closure-validity detector fires — but the steelman baseline ALSO fires
|
|
205
|
+
(nose radius is a surrogate input), so PhysMAP correctly declines a clean
|
|
206
|
+
differentiator. Marineau's surrogate-of-record is a data-driven fit (loader
|
|
207
|
+
`marineau_hypersonic_transition`). Raises loudly if dispatched as a predictor.
|
|
208
|
+
"""
|
|
209
|
+
raise NotImplementedError(
|
|
210
|
+
"marineau-entropy-layer-shock-interaction-2014 is a corpus-validity "
|
|
211
|
+
"anchor (entropy_layer_shock_ratio bound) only; it has no executable "
|
|
212
|
+
"predictor. The Marineau vehicle uses a data-driven surrogate."
|
|
213
|
+
)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Controlled vocabulary for closure / vehicle geometry classes.
|
|
2
|
+
|
|
3
|
+
The geometry-match invariant in `substrate_engine.build_substrate(...)` is an
|
|
4
|
+
exact-string comparison between `ClosureEntry.geometry_class` and
|
|
5
|
+
`VehicleConfig.geometry.class_`. Both sides MUST draw from this vocabulary.
|
|
6
|
+
String constants (not Enum) so YAML serialization is trivial.
|
|
7
|
+
|
|
8
|
+
A geometry class describes WHAT KIND of flow/heat-transfer configuration a
|
|
9
|
+
closure was derived for OR a vehicle realizes. Mismatch between the matched
|
|
10
|
+
closure's geometry class and the vehicle's geometry class is exactly the
|
|
11
|
+
Sparrow-Cur / Lance & Smith trap that motivates the invariant.
|
|
12
|
+
|
|
13
|
+
NOTE: geometry is INTENTIONALLY absent from `corpus.jsonl` (gate 6 of the
|
|
14
|
+
calibration corpus schema forbids geometry in `physics_coordinates`).
|
|
15
|
+
The registry is the place where geometry meets the executable formula.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from typing import Literal
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Forrest mini-channel — Modified Sparrow-Cur was derived for this exact
|
|
22
|
+
# configuration (one-sided heated, high aspect ratio rectangular).
|
|
23
|
+
NARROW_RECT_CHANNEL_ONE_SIDED = "narrow_rect_channel_one_sided"
|
|
24
|
+
|
|
25
|
+
# Generic circular pipe — Gnielinski, Dittus-Boelter, Petukhov, Sieder-Tate
|
|
26
|
+
# are circular-pipe correlations. Used as REFERENCE closures on other
|
|
27
|
+
# geometries (Forrest, Lance & Smith) for Table-4 cross-comparison.
|
|
28
|
+
CIRCULAR_PIPE = "circular_pipe"
|
|
29
|
+
|
|
30
|
+
# Mudhafar smooth micro-tubes (50-950 μm inner diameter). Geometrically a
|
|
31
|
+
# sub-class of circular_pipe, but kept distinct because micro-scale physics
|
|
32
|
+
# may diverge from macro-scale circular-pipe closures.
|
|
33
|
+
CIRCULAR_MICRO_TUBE_SMOOTH = "circular_micro_tube_smooth"
|
|
34
|
+
|
|
35
|
+
# Mudhafar rough micro-tubes (Ra 5.3-60 μm). Roughness puts the flow outside
|
|
36
|
+
# all standard smooth-pipe correlations.
|
|
37
|
+
CIRCULAR_PIPE_ROUGH = "circular_pipe_rough"
|
|
38
|
+
|
|
39
|
+
# Lance & Smith / Pohlhausen — external flat-plate laminar forced.
|
|
40
|
+
FLAT_PLATE_EXTERNAL_FORCED = "flat_plate_external_forced"
|
|
41
|
+
|
|
42
|
+
# Lance & Smith / McAdams — vertical-plate laminar natural convection.
|
|
43
|
+
VERTICAL_PLATE_EXTERNAL_NATURAL = "vertical_plate_external_natural"
|
|
44
|
+
|
|
45
|
+
# Lance & Smith / Churchill — mixed-convection assisting flow on flat plate.
|
|
46
|
+
# This is the matched geometry for the L&S substrate.
|
|
47
|
+
FLAT_PLATE_EXTERNAL_MIXED = "flat_plate_external_mixed"
|
|
48
|
+
|
|
49
|
+
# NACA TN-1451 — circular pipe with entrance-region effects (x/D matters).
|
|
50
|
+
# Per v0.2 steelman, NACA's matched closure is Gnielinski (circular_pipe),
|
|
51
|
+
# applied to this geometry as the documented practitioner habit (aggregate
|
|
52
|
+
# HE surrogates omit x/D). The Hausen multiplicative correction lives in
|
|
53
|
+
# the substrate as a justification signal; it's not a closure family.
|
|
54
|
+
CIRCULAR_PIPE_ENTRANCE_REGION = "circular_pipe_entrance_region"
|
|
55
|
+
|
|
56
|
+
# Casper hypersonic sharp cone — boundary-layer transition driven by tunnel
|
|
57
|
+
# freestream disturbance (Pate-Stainback noise validity boundary). Aerospace
|
|
58
|
+
# domain; matched closure carries the freestream-noise validity bound.
|
|
59
|
+
HYPERSONIC_SHARP_CONE = "hypersonic_sharp_cone"
|
|
60
|
+
|
|
61
|
+
# Marineau hypersonic blunt cone — transition driven by entropy-layer / shock
|
|
62
|
+
# interaction (nose bluntness). Aerospace domain; matched closure carries the
|
|
63
|
+
# entropy-layer/shock-interaction (S_T/X_SW) validity bound.
|
|
64
|
+
HYPERSONIC_BLUNT_CONE = "hypersonic_blunt_cone"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
GeometryClass = Literal[
|
|
68
|
+
"narrow_rect_channel_one_sided",
|
|
69
|
+
"circular_pipe",
|
|
70
|
+
"circular_micro_tube_smooth",
|
|
71
|
+
"circular_pipe_rough",
|
|
72
|
+
"flat_plate_external_forced",
|
|
73
|
+
"vertical_plate_external_natural",
|
|
74
|
+
"flat_plate_external_mixed",
|
|
75
|
+
"circular_pipe_entrance_region",
|
|
76
|
+
"hypersonic_sharp_cone",
|
|
77
|
+
"hypersonic_blunt_cone",
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
ALL_GEOMETRY_CLASSES: frozenset[str] = frozenset({
|
|
82
|
+
NARROW_RECT_CHANNEL_ONE_SIDED,
|
|
83
|
+
CIRCULAR_PIPE,
|
|
84
|
+
CIRCULAR_MICRO_TUBE_SMOOTH,
|
|
85
|
+
CIRCULAR_PIPE_ROUGH,
|
|
86
|
+
FLAT_PLATE_EXTERNAL_FORCED,
|
|
87
|
+
VERTICAL_PLATE_EXTERNAL_NATURAL,
|
|
88
|
+
FLAT_PLATE_EXTERNAL_MIXED,
|
|
89
|
+
CIRCULAR_PIPE_ENTRANCE_REGION,
|
|
90
|
+
HYPERSONIC_SHARP_CONE,
|
|
91
|
+
HYPERSONIC_BLUNT_CONE,
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def assert_known_geometry(geometry_class: str) -> None:
|
|
96
|
+
"""Raise ValueError if the string is not in the controlled vocabulary.
|
|
97
|
+
|
|
98
|
+
Both ClosureEntry construction and VehicleConfig loading call this to fail
|
|
99
|
+
fast on typos (cheap defense against the silent-string-mismatch class of
|
|
100
|
+
bug the invariant is designed to catch).
|
|
101
|
+
"""
|
|
102
|
+
if geometry_class not in ALL_GEOMETRY_CLASSES:
|
|
103
|
+
raise ValueError(
|
|
104
|
+
f"Unknown geometry_class {geometry_class!r}. "
|
|
105
|
+
f"Must be one of: {sorted(ALL_GEOMETRY_CLASSES)}. "
|
|
106
|
+
f"Add it to physmap/closures/geometry_classes.py if it's a new "
|
|
107
|
+
f"configuration; do NOT silently accept unknown strings — the "
|
|
108
|
+
f"geometry-match invariant compares strings exactly."
|
|
109
|
+
)
|