完善 PNL00R 摩擦模型与八路回归
This commit is contained in:
1 parent
684d28752a
commit
f725f038b6
10 files changed
+492
-92
No files matched your search
@@ -54,6 +54,22 @@ class Pnl0001DerivativeLinearization:
|
||||
|
||||
_MAX_REPORTED_FRICTION_FACTOR = 64_000_000.0
|
||||
|
||||
# ``pn2pipefr`` blends the laminar and turbulent friction branches over a
|
||||
# much wider Reynolds range than the former hard 2300..4000 interpolation.
|
||||
# These constants are a uniform-Re least-squares fit to the unchanged
|
||||
# Simcenter Amesim 2404 test_mql result for PNL00R/PNL0001/PNL0003 at both
|
||||
# rr=0.045/14 and rr=0.045/20. The shifted Hill exponent is greater than 2,
|
||||
# so its zero-valued join to the laminar branch is C2 continuous.
|
||||
_PN2PIPEFR_TRANSITION_START_REYNOLDS = 89.96829989
|
||||
_PN2PIPEFR_TRANSITION_SCALE_REYNOLDS = 2741.96700831
|
||||
_PN2PIPEFR_TRANSITION_SHARPNESS = 8.37293695
|
||||
|
||||
# PNL00R keeps the exact closed-form laminar solve where the fitted
|
||||
# transition contribution is below four parts per million. Extending this
|
||||
# shortcut to Re=2300 bypassed the real AMESim transition and introduced a
|
||||
# solver-visible branch point.
|
||||
_PN2PIPEFR_ANALYTIC_LAMINAR_MAX_REYNOLDS = 1000.0
|
||||
|
||||
|
||||
def _reported_friction_factor(value: float) -> float:
|
||||
return min(float(value), _MAX_REPORTED_FRICTION_FACTOR)
|
||||
@@ -75,8 +91,8 @@ class AmesimPnl00r(AlgebraicComponent):
|
||||
"""AMESim PNL00R pneumatic pipe friction resistance.
|
||||
|
||||
The public model exposes the AMESim PNL00R catalog/XML contract and uses
|
||||
an auditable Darcy-Weisbach resistance with Reynolds/roughness-dependent
|
||||
friction. Exact `pn2pipefr_` parity is left for the later model tuning pass.
|
||||
the `pn2pipefr` compressible gas relation with a Reynolds/roughness-
|
||||
dependent equivalent flow coefficient.
|
||||
"""
|
||||
|
||||
MODEL_TYPE = "amesim_pnl00r"
|
||||
@@ -244,7 +260,7 @@ class AmesimPnl00r(AlgebraicComponent):
|
||||
if reynolds_number <= 0.0:
|
||||
return 64_000_000.0
|
||||
laminar = 64.0 / reynolds_number
|
||||
if reynolds_number <= 2300.0:
|
||||
if reynolds_number <= _PN2PIPEFR_TRANSITION_START_REYNOLDS:
|
||||
return laminar
|
||||
|
||||
# pn2pipefr does not apply the fully rough correction at every
|
||||
@@ -274,10 +290,13 @@ class AmesimPnl00r(AlgebraicComponent):
|
||||
turbulent = smooth_turbulent + roughness_weight * (
|
||||
fully_rough - smooth_turbulent
|
||||
)
|
||||
if reynolds_number >= 4000.0:
|
||||
return turbulent
|
||||
fraction = (reynolds_number - 2300.0) / 1700.0
|
||||
return laminar + fraction**0.58 * (turbulent - laminar)
|
||||
transition_coordinate = (
|
||||
(reynolds_number - _PN2PIPEFR_TRANSITION_START_REYNOLDS)
|
||||
/ _PN2PIPEFR_TRANSITION_SCALE_REYNOLDS
|
||||
)
|
||||
transition_power = transition_coordinate**_PN2PIPEFR_TRANSITION_SHARPNESS
|
||||
transition_weight = transition_power / (1.0 + transition_power)
|
||||
return laminar + transition_weight * (turbulent - laminar)
|
||||
|
||||
def darcy_pressure_drop(
|
||||
self,
|
||||
@@ -329,13 +348,17 @@ class AmesimPnl00r(AlgebraicComponent):
|
||||
if isclose(p_1, p_2, rel_tol=0.0, abs_tol=1.0e-8):
|
||||
return 0.0
|
||||
pressure_difference = p_1 - p_2
|
||||
upstream_pressure = max(p_1, p_2, 1.0)
|
||||
upstream_temperature = self._port_temperature("port_1" if pressure_difference > 0.0 else "port_2")
|
||||
density = max(self.medium.density(upstream_pressure, upstream_temperature), 1.0e-12)
|
||||
magnitude = self._mass_flow_for_pressure_drop(
|
||||
abs(pressure_difference),
|
||||
density=density,
|
||||
temperature=upstream_temperature,
|
||||
upstream_temperature = self._port_temperature(
|
||||
"port_1" if pressure_difference > 0.0 else "port_2"
|
||||
)
|
||||
magnitude = AmesimPnl0001._one_way_pn2pipefr_mass_flow(
|
||||
self,
|
||||
upstream_pressure=max(p_1, p_2),
|
||||
downstream_pressure=min(p_1, p_2),
|
||||
upstream_temperature=upstream_temperature,
|
||||
resistance_length=self.le,
|
||||
max_iterations=64,
|
||||
analytic_laminar=True,
|
||||
)
|
||||
return magnitude if pressure_difference > 0.0 else -magnitude
|
||||
|
||||
@@ -347,17 +370,19 @@ class AmesimPnl00r(AlgebraicComponent):
|
||||
)
|
||||
density = max(self.medium.density(upstream_pressure, upstream_temperature), 1.0e-12)
|
||||
reynolds = self.reynolds_number(m_flow, upstream_temperature)
|
||||
friction = self.friction_factor(reynolds)
|
||||
flow_coefficient = sqrt(self.diam / (self.le * friction))
|
||||
velocity = m_flow / (density * self.area)
|
||||
cm = (
|
||||
abs(m_flow)
|
||||
* sqrt(upstream_temperature)
|
||||
/ max(self.area * upstream_pressure, 1.0e-18)
|
||||
/ max(flow_coefficient * self.area * upstream_pressure, 1.0e-18)
|
||||
)
|
||||
return {
|
||||
"re": reynolds,
|
||||
"cm": cm,
|
||||
"v": velocity,
|
||||
"ff": _reported_friction_factor(self.friction_factor(reynolds)),
|
||||
"ff": _reported_friction_factor(friction),
|
||||
}
|
||||
|
||||
def pressure_flow_equation_values(self) -> tuple[float, ...]:
|
||||
@@ -754,6 +779,8 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
|
||||
downstream_pressure: float,
|
||||
upstream_temperature: float,
|
||||
resistance_length: float,
|
||||
max_iterations: int = 16,
|
||||
analytic_laminar: bool = False,
|
||||
) -> float:
|
||||
"""AMESim pn2pipefr-style compressible friction flow."""
|
||||
|
||||
@@ -824,6 +851,18 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
|
||||
value *= tanh(max(smoothing_argument, 0.0))
|
||||
return value
|
||||
|
||||
if analytic_laminar:
|
||||
flow_parameter = mass_flow_parameter(pressure_ratio)
|
||||
viscosity = self._dynamic_viscosity(T_up)
|
||||
laminar_mass_flow = (
|
||||
self.area * p_up * flow_parameter
|
||||
) ** 2 / (16.0 * pi * viscosity * resistance_length * T_up)
|
||||
if (
|
||||
self.reynolds_number(laminar_mass_flow, T_up)
|
||||
<= _PN2PIPEFR_ANALYTIC_LAMINAR_MAX_REYNOLDS
|
||||
):
|
||||
return laminar_mass_flow
|
||||
|
||||
def target_flow(mass_flow: float) -> float:
|
||||
reynolds = self.reynolds_number(mass_flow, T_up)
|
||||
friction = self.friction_factor(reynolds)
|
||||
@@ -846,7 +885,7 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
|
||||
* mass_flow_parameter(pressure_ratio)
|
||||
/ sqrt(T_up)
|
||||
)
|
||||
for _iteration in range(16):
|
||||
for _iteration in range(max_iterations):
|
||||
next_magnitude = target_flow(magnitude)
|
||||
if abs(next_magnitude - magnitude) <= max(
|
||||
1.0e-12,
|
||||
|
||||
@@ -241,8 +241,9 @@ class ResistancePnl0001SeriesBinding:
|
||||
resistance_port: str
|
||||
resistance_other_port: str
|
||||
positive_flow_port: str
|
||||
pipe: AmesimPnl0001
|
||||
pipe: AmesimPnl00r | AmesimPnl0001
|
||||
pipe_port: str
|
||||
pipe_other_port: str | None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -2338,24 +2339,31 @@ class PressureFlowSolver:
|
||||
) -> tuple[ResistancePnl0001SeriesBinding, ...]:
|
||||
bindings: list[ResistancePnl0001SeriesBinding] = []
|
||||
resistance_types = (AmesimPnor001, AmesimPnvo001FixedOpening)
|
||||
pipe_types = (AmesimPnl00r, AmesimPnl0001)
|
||||
for connection in self.network.connections:
|
||||
first_endpoint, second_endpoint = connection.endpoints
|
||||
first = self.network.components[first_endpoint.component]
|
||||
second = self.network.components[second_endpoint.component]
|
||||
if isinstance(first, resistance_types) and isinstance(
|
||||
second, AmesimPnl0001
|
||||
second, pipe_types
|
||||
):
|
||||
resistance, resistance_port = first, first_endpoint.port
|
||||
pipe, pipe_port = second, second_endpoint.port
|
||||
elif isinstance(second, resistance_types) and isinstance(
|
||||
first, AmesimPnl0001
|
||||
first, pipe_types
|
||||
):
|
||||
resistance, resistance_port = second, second_endpoint.port
|
||||
pipe, pipe_port = first, first_endpoint.port
|
||||
else:
|
||||
continue
|
||||
if isinstance(pipe, AmesimPnl0001):
|
||||
if isinstance(pipe, AmesimPnl0002) or pipe_port != "port_1":
|
||||
continue
|
||||
pipe_other_port = None
|
||||
else:
|
||||
pipe_other_port = (
|
||||
"port_2" if pipe_port == "port_1" else "port_1"
|
||||
)
|
||||
if isinstance(resistance, AmesimPnor001):
|
||||
positive_flow_port, negative_flow_port = "port_1", "port_2"
|
||||
else:
|
||||
@@ -2374,12 +2382,13 @@ class PressureFlowSolver:
|
||||
positive_flow_port=positive_flow_port,
|
||||
pipe=pipe,
|
||||
pipe_port=pipe_port,
|
||||
pipe_other_port=pipe_other_port,
|
||||
)
|
||||
)
|
||||
return tuple(bindings)
|
||||
|
||||
def _seed_resistance_pnl0001_series_pressures(self) -> None:
|
||||
"""Causalize pressure between an orifice/valve and a PNL0001 R port."""
|
||||
"""Causalize pressure between an orifice/valve and a PNL pipe."""
|
||||
|
||||
from scipy.optimize import brentq
|
||||
|
||||
@@ -2390,7 +2399,14 @@ class PressureFlowSolver:
|
||||
pipe_port = binding.pipe_port
|
||||
|
||||
pressure_a = resistance.get_port(binding.resistance_other_port).p
|
||||
pressure_b = pipe.properties().p
|
||||
if isinstance(pipe, AmesimPnl00r):
|
||||
assert binding.pipe_other_port is not None
|
||||
pressure_b = pipe.get_port(binding.pipe_other_port).p
|
||||
pipe_temperature = None
|
||||
else:
|
||||
pipe_properties = pipe.properties()
|
||||
pressure_b = pipe_properties.p
|
||||
pipe_temperature = pipe_properties.T
|
||||
lower = min(pressure_a, pressure_b)
|
||||
upper = max(pressure_a, pressure_b)
|
||||
|
||||
@@ -2405,10 +2421,24 @@ class PressureFlowSolver:
|
||||
pressure_a,
|
||||
intermediate_pressure,
|
||||
)
|
||||
if isinstance(pipe, AmesimPnl00r):
|
||||
pipe.get_port(pipe_port).p = intermediate_pressure
|
||||
if pipe_port == "port_1":
|
||||
pipe_flow_into_connection = pipe.mass_flow(
|
||||
intermediate_pressure,
|
||||
pressure_b,
|
||||
pipe.properties().T,
|
||||
)
|
||||
else:
|
||||
pipe_flow_into_connection = -pipe.mass_flow(
|
||||
pressure_b,
|
||||
intermediate_pressure,
|
||||
)
|
||||
else:
|
||||
assert pipe_temperature is not None
|
||||
pipe_flow_into_connection = pipe.mass_flow(
|
||||
intermediate_pressure,
|
||||
pressure_b,
|
||||
pipe_temperature,
|
||||
)
|
||||
return resistance_flow_into_connection + pipe_flow_into_connection
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
| `PNVO001` | 8 | 信号调制气动孔口 | 第一版公开 | `amesim_pnvo001`,`flow` | 已接入名为 `res` 的标量信号输入端口,可由 `amesim_step0` 驱动开度;精确事件语义和 AMESim baseline 仍留后续修模。固定开度变体 `amesim_pnvo001_fixed` 继续保留。 |
|
||||
| `PN3NODE2` | 8 | 三端气动节点,压力温度由 port 2 固定 | 第一版公开 | `amesim_pn3node2`,`junctions` | 已接入三端等压、流量守恒基础版;AMESim port 2 参考温压语义和 stream 混合仍需单独测试。 |
|
||||
| `P4NODE2` | 8 | 四端气动节点,压力温度由 port 2 固定 | 第一版公开 | `amesim_p4node2`,`junctions` | 已接入四端等压、流量守恒基础版;仍需复核 port 2 参考温压和多支路混合。 |
|
||||
| `PNL00R` | 4 | 管路纯阻性摩擦段 | 第一版公开 | `amesim_pnl00r`,`flow` | 已接入准稳态阻性管公开契约;仍需按 AMESim `PNL00R` 参数和摩擦公式复核。 |
|
||||
| `PNL00R` | 4 | 管路纯阻性摩擦段 | 第一版公开 | `amesim_pnl00r`,`flow` | 已按 AMESim `pn2pipefr` 接入可压缩 `Cm/Cq` 关系、同侧上游温度和裸 `Cm` 诊断;使用未修改 AMESim 结果包直接保存的 `Re/ff` 标定 Re≈1400–3400 的 shifted-Hill C2 过渡曲线,并通过固定点、单调性和第八路 `0–0.81 s` 回归。 |
|
||||
| `PNL0001` | 20 | C-R 动态管路 | 第一版公开 | `amesim_pnl0001`,`flow` | 已按公开契约接入两状态管内容积 + port 1 摩擦残差 + mode 2 换热项;仍需后续按 AMESim baseline 复核 `pn2pipefr_` 和 mode 1 多方语义。 |
|
||||
| `PNL0002` | 8 | R-C-R 动态管路 | 第一版公开 | `amesim_pnl0002`,`flow` | 已按公开契约接入中心两状态容积 + 两端半长摩擦残差 + mode 2 换热项;仍需后续按 AMESim baseline 复核 `pn2pipefr_` 和 mode 1 多方语义。 |
|
||||
| `PNL0003` | 8 | C-R-C 动态管路 | 第一版公开 | `amesim_pnl0003`,`flow` | 已按公开契约接入两端四状态容积 + 中心摩擦流 + mode 2 换热项;大压差动态闭合和 AMESim baseline 误差仍留后续修模。 |
|
||||
@@ -89,6 +89,6 @@
|
||||
## 下一步执行建议
|
||||
|
||||
1. 以 `app.simulation.components.amesim.library` 的 22 个模型为公开清单唯一来源。
|
||||
2. 优先校准 `PNCH023 / PNOR001 / PN3NODE2 / P4NODE2 / PNL00R` 与动态管路的 AMESim baseline 误差。
|
||||
2. 优先校准 `PNCH023 / PNOR001 / PN3NODE2 / P4NODE2` 与动态管路的 AMESim baseline 误差;`PNL00R` 保持现有层流、过渡区固定点和单调性回归。
|
||||
3. 每次调整模型都同步补充目录校验、参数边界、System XML 编译和最小仿真测试。
|
||||
4. 用完整或代表性的 `test_mql` 画布校准 `PNRP17 + PNCH012` 的压力、力、位移和容积轨迹,并补齐事件边界语义。
|
||||
@@ -1,23 +1,23 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "test_mql_8-progressive-v2",
|
||||
"description": "Primary generic-solver regression target for the renamed 0.01 s-grid authoritative inputs, with a short smoke gate before staged extension.",
|
||||
"id": "test_mql_8-progressive-v3",
|
||||
"description": "Primary generic-solver regression target for the restored eight-branch 0.005 s-grid authoritative inputs, with a short smoke gate before staged extension.",
|
||||
"source": {
|
||||
"path": "tests/data/test-mql-8.xml",
|
||||
"sha256": "170463d65d074da01f0f9e9dab730b3815c94c1cc80b5190ec2e3fe623da74d3",
|
||||
"bytes": 101013,
|
||||
"sha256": "6eb2753a15af66fd8e91c169c4587e66b0ace2164c08c2cb23a67aab5fd09164",
|
||||
"bytes": 104109,
|
||||
"xmlIsAuthoritative": true,
|
||||
"companionProject": {
|
||||
"path": "tests/data/test-mql-8.json",
|
||||
"sha256": "258c50ee4850baa72fb7c2cc24536d0a631fc6a7f1fa6cedb7b6eea7c857cbaa",
|
||||
"bytes": 264675,
|
||||
"sha256": "8d7a263691a9f09f39c92aba19ae1350911dc66b4904e98bae5f20592c5a3fcd",
|
||||
"bytes": 272591,
|
||||
"executionInput": false
|
||||
},
|
||||
"simulation": {
|
||||
"tStart": 0.0,
|
||||
"tStop": 0.2,
|
||||
"sampleStep": 0.01,
|
||||
"maxStep": 0.01,
|
||||
"tStop": 5.0,
|
||||
"sampleStep": 0.005,
|
||||
"maxStep": 0.005,
|
||||
"method": "BDF"
|
||||
}
|
||||
},
|
||||
@@ -37,22 +37,38 @@
|
||||
"status": "historicalOnly",
|
||||
"compatibleWithCurrentSource": false,
|
||||
"reason": "This deferred-stage decision was calculated from the former 0.002 s source report and is not a runtime prediction for the renamed v2 source."
|
||||
},
|
||||
{
|
||||
"path": "tests/baselines/simulation/test_mql_8/runs/2026-08-17-production-v2-0.2.json",
|
||||
"sha256": "93baac975413703bebe940308ccd0de5dbd753f7c1c4fe015907315f112618a5",
|
||||
"sourceXmlSha256": "170463d65d074da01f0f9e9dab730b3815c94c1cc80b5190ec2e3fe623da74d3",
|
||||
"status": "historicalOnly",
|
||||
"compatibleWithCurrentSource": false,
|
||||
"reason": "This accepted 0.2 s report predates the four restored PNL0001 branches and cannot validate the v3 source."
|
||||
},
|
||||
{
|
||||
"path": "tests/baselines/simulation/test_mql_8/runs/2026-08-17-production-v2-extension-decision.json",
|
||||
"sha256": "c2dd8acfaae71e9c5f2eaef35d529dd1ce1d420b48703faff3d111678496b671",
|
||||
"sourceXmlSha256": "170463d65d074da01f0f9e9dab730b3815c94c1cc80b5190ec2e3fe623da74d3",
|
||||
"status": "historicalOnly",
|
||||
"compatibleWithCurrentSource": false,
|
||||
"reason": "This runtime extension decision was derived from the v2 source and is retained only as historical evidence."
|
||||
}
|
||||
],
|
||||
"structure": {
|
||||
"componentCount": 152,
|
||||
"connectionCount": 174,
|
||||
"dynamicComponentCount": 54,
|
||||
"stateCount": 124,
|
||||
"resultVariableCount": 1716,
|
||||
"pressureFlowUnknownCount": 760,
|
||||
"pressureFlowEquationCount": 760,
|
||||
"componentCount": 156,
|
||||
"connectionCount": 178,
|
||||
"dynamicComponentCount": 58,
|
||||
"stateCount": 132,
|
||||
"resultVariableCount": 1784,
|
||||
"pressureFlowUnknownCount": 776,
|
||||
"pressureFlowEquationCount": 776,
|
||||
"pressureFlowIsSquare": true,
|
||||
"logicalEffortCoordinateCount": 112,
|
||||
"eliminatedEffortAliasCount": 320,
|
||||
"canonicalCoordinateCount": 440,
|
||||
"compatibilityScatterCount": 760,
|
||||
"jacobianNonzeroCount": 3296,
|
||||
"logicalEffortCoordinateCount": 116,
|
||||
"eliminatedEffortAliasCount": 324,
|
||||
"canonicalCoordinateCount": 452,
|
||||
"compatibilityScatterCount": 776,
|
||||
"jacobianNonzeroCount": 3280,
|
||||
"jacobianColorGroupCount": 52,
|
||||
"hasMechanicalStateEvents": true
|
||||
},
|
||||
@@ -74,7 +90,7 @@
|
||||
"productionEquivalentOutput": false
|
||||
},
|
||||
"production": {
|
||||
"description": "Acceptance lane: changes only tStop and preserves the source 0.01 s output and internal-step grid.",
|
||||
"description": "Acceptance lane: changes only tStop and preserves the source 0.005 s output and internal-step grid.",
|
||||
"samplingMode": "source",
|
||||
"maxStepMode": "source",
|
||||
"instrumentationMode": "standard",
|
||||
@@ -105,13 +121,7 @@
|
||||
"expectedSignalEventTimes": [
|
||||
0.04
|
||||
],
|
||||
"expectedMechanicalTransitionTimes": [],
|
||||
"goldens": {
|
||||
"production": {
|
||||
"path": "tests/baselines/simulation/test_mql_8/goldens/production-0.2s-v1.json",
|
||||
"sha256": "99fa7b3631a89f59f86551847175f3701a1567d8d0bd10d87545734f17515d72"
|
||||
}
|
||||
}
|
||||
"expectedMechanicalTransitionTimes": []
|
||||
},
|
||||
"1s": {
|
||||
"stopTime": 1.0,
|
||||
@@ -178,7 +188,7 @@
|
||||
"eventTimeAbsoluteToleranceSeconds": 0.00002,
|
||||
"signalEventTimeAbsoluteToleranceSeconds": 1e-12,
|
||||
"mechanicalTransitionTimesAvailable": true,
|
||||
"note": "Physical state checkpoints/events and the value-free output-shape contract are separate. The production 0.2 s variant has an approved state golden; longer horizons and critical algebraic projections remain pending."
|
||||
"note": "The restored v3 source currently enforces structure, finite series, residuals, checkpoints, and events. Its v2 state golden is historical-only; a new reviewed v3 state golden remains pending."
|
||||
},
|
||||
"execution": {
|
||||
"causalExecutorV2Default": true,
|
||||
|
||||
@@ -2,8 +2,12 @@ from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from app.simulation.components.amesim.flow.pipes import AmesimPnl00r
|
||||
from app.simulation.components.amesim.flow.pipes import (
|
||||
AmesimPnl0001,
|
||||
AmesimPnl00r,
|
||||
)
|
||||
from app.simulation.components.amesim.junctions.nodes import AmesimPn3Node2
|
||||
from app.simulation.components.amesim.media.mediums import AmesimHeliumPengRobinsonMedium
|
||||
from app.simulation.components.experimental.storage.cylinder import Cylinder
|
||||
from app.simulation.components.experimental.storage.tank import Tank
|
||||
from app.simulation.core.medium import IdealGasMedium
|
||||
@@ -94,12 +98,189 @@ class AmesimPnl00rComponentTests(unittest.TestCase):
|
||||
self.assertGreater(above, below)
|
||||
self.assertLess(reverse, 0.0)
|
||||
self.assertAlmostEqual(below, -reverse, delta=abs(below) * 1.0e-9)
|
||||
self.assertLess(abs(above - below), abs(above) * 0.05)
|
||||
self.assertLess(abs(above - below), abs(above) * 0.10)
|
||||
self.assertEqual(pipe.mass_flow(base_pressure, base_pressure), 0.0)
|
||||
self.assertGreater(pipe.mass_flow(15.3e6 + 0.1, 15.3e6), 0.0)
|
||||
|
||||
def test_matches_amesim_pn2pipefr_laminar_baselines(self) -> None:
|
||||
medium = AmesimHeliumPengRobinsonMedium()
|
||||
pipe = AmesimPnl00r(
|
||||
"pnl_1",
|
||||
medium,
|
||||
diam=0.014,
|
||||
le=1.0,
|
||||
rr=0.045 / 14.0,
|
||||
)
|
||||
samples = (
|
||||
(
|
||||
13_887_929.5734,
|
||||
288.959589373,
|
||||
13_887_887.6633,
|
||||
288.959239289,
|
||||
1.79075554200e-4,
|
||||
838.404977868,
|
||||
3.32483767035e-6,
|
||||
0.0521569680484,
|
||||
0.076335424633,
|
||||
),
|
||||
(
|
||||
13_093_276.1461,
|
||||
285.548411921,
|
||||
13_093_238.0341,
|
||||
285.546325432,
|
||||
1.40922897107e-4,
|
||||
665.123640745,
|
||||
3.09743309373e-6,
|
||||
0.0429212663992,
|
||||
0.0962227112065,
|
||||
),
|
||||
(
|
||||
11_690_210.3512,
|
||||
272.892693485,
|
||||
11_690_180.6138,
|
||||
272.889527291,
|
||||
7.10081729578e-5,
|
||||
345.615999811,
|
||||
2.3706370264e-6,
|
||||
0.0230685613948,
|
||||
0.185176612295,
|
||||
),
|
||||
)
|
||||
|
||||
def test_pressure_drop_inversion_cache_keys_all_property_inputs(self) -> None:
|
||||
for p_1, T_1, p_2, T_2, mass_flow, re, cm, velocity, ff in samples:
|
||||
with self.subTest(pressure=p_1):
|
||||
pipe.port_1.p = p_1
|
||||
pipe.port_2.p = p_2
|
||||
pipe.update_stream_outflows(
|
||||
{
|
||||
"port_1": medium.specific_enthalpy_at_pressure(p_1, T_1),
|
||||
"port_2": medium.specific_enthalpy_at_pressure(p_2, T_2),
|
||||
}
|
||||
)
|
||||
|
||||
actual_mass_flow = pipe.mass_flow(p_1, p_2)
|
||||
results = pipe.component_result_values()
|
||||
|
||||
self.assertAlmostEqual(actual_mass_flow / mass_flow, 1.0, delta=0.005)
|
||||
self.assertAlmostEqual(results["re"] / re, 1.0, delta=0.01)
|
||||
self.assertAlmostEqual(results["cm"] / cm, 1.0, delta=1.0e-4)
|
||||
self.assertAlmostEqual(results["v"] / velocity, 1.0, delta=0.005)
|
||||
self.assertAlmostEqual(results["ff"] / ff, 1.0, delta=0.01)
|
||||
|
||||
def test_matches_amesim_pn2pipefr_transition_baselines(self) -> None:
|
||||
medium = AmesimHeliumPengRobinsonMedium()
|
||||
pipe = AmesimPnl00r(
|
||||
"pnl_1",
|
||||
medium,
|
||||
diam=0.014,
|
||||
le=1.0,
|
||||
rr=0.045 / 14.0,
|
||||
)
|
||||
# Unchanged test_mql_1.ame, pneumatic_105, ascending transition at
|
||||
# t=0.2862..0.3047 s. AMESim dm1 is positive into port 1 while this
|
||||
# component's port-1-to-port-2 coordinate is negative for these rows.
|
||||
samples = (
|
||||
(
|
||||
10_631_986.90667049,
|
||||
262.71180639527233,
|
||||
10_632_026.310978588,
|
||||
262.7092388107747,
|
||||
-2.8192909824026485e-4,
|
||||
1408.0846374820405,
|
||||
5.030700878807581e-6,
|
||||
-0.09668817162408228,
|
||||
0.04545209089983021,
|
||||
),
|
||||
(
|
||||
10_594_171.934888396,
|
||||
262.32910587014925,
|
||||
10_594_213.825687861,
|
||||
262.3266782251236,
|
||||
-3.598950492898518e-4,
|
||||
1799.2599253847663,
|
||||
5.723572909300966e-6,
|
||||
-0.1236749841566422,
|
||||
0.03590026408283139,
|
||||
),
|
||||
(
|
||||
10_554_721.69137201,
|
||||
261.9257072357455,
|
||||
10_554_766.280823693,
|
||||
261.92350790162175,
|
||||
-4.3973197060195834e-4,
|
||||
2200.69088342098,
|
||||
6.529418698258151e-6,
|
||||
-0.1514267390526478,
|
||||
0.03111112083131272,
|
||||
),
|
||||
(
|
||||
10_544_953.846352266,
|
||||
261.82512637406904,
|
||||
10_544_999.25593169,
|
||||
261.82299814935993,
|
||||
-4.592053720488228e-4,
|
||||
2298.7460389094877,
|
||||
6.783006948981891e-6,
|
||||
-0.15821443006863944,
|
||||
0.03074226487938153,
|
||||
),
|
||||
(
|
||||
10_514_807.25092133,
|
||||
261.5128728347395,
|
||||
10_514_855.822238008,
|
||||
261.5109955483938,
|
||||
-5.190048780471613e-4,
|
||||
2600.199197308607,
|
||||
7.798343300840328e-6,
|
||||
-0.17910295287093314,
|
||||
0.03166638237616583,
|
||||
),
|
||||
(
|
||||
10_432_760.829010766,
|
||||
260.6482173603332,
|
||||
10_432_818.817992153,
|
||||
260.6472539768074,
|
||||
-6.759582533840764e-4,
|
||||
3394.136683451109,
|
||||
1.1253055264735043e-5,
|
||||
-0.23427502517225277,
|
||||
0.03839469933871734,
|
||||
),
|
||||
)
|
||||
|
||||
for p_1, T_1, p_2, T_2, mass_flow, re, cm, velocity, ff in samples:
|
||||
with self.subTest(reynolds=re):
|
||||
pipe.port_1.p = p_1
|
||||
pipe.port_2.p = p_2
|
||||
pipe.update_stream_outflows(
|
||||
{
|
||||
"port_1": medium.specific_enthalpy_at_pressure(p_1, T_1),
|
||||
"port_2": medium.specific_enthalpy_at_pressure(p_2, T_2),
|
||||
}
|
||||
)
|
||||
|
||||
actual_mass_flow = pipe.mass_flow(p_1, p_2)
|
||||
results = pipe.component_result_values()
|
||||
|
||||
self.assertAlmostEqual(actual_mass_flow / mass_flow, 1.0, delta=0.006)
|
||||
self.assertAlmostEqual(results["re"] / re, 1.0, delta=0.012)
|
||||
self.assertAlmostEqual(results["cm"] / cm, 1.0, delta=1.0e-4)
|
||||
self.assertAlmostEqual(results["v"] / velocity, 1.0, delta=0.006)
|
||||
self.assertAlmostEqual(results["ff"] / ff, 1.0, delta=0.012)
|
||||
|
||||
def test_upstream_temperature_uses_same_side_connected_stream(self) -> None:
|
||||
pipe = AmesimPnl00r("pnl_1", self.medium)
|
||||
h_1 = self.medium.specific_enthalpy(250.0)
|
||||
h_2 = self.medium.specific_enthalpy(400.0)
|
||||
|
||||
pipe.update_stream_outflows({"port_1": h_1, "port_2": h_2})
|
||||
|
||||
self.assertAlmostEqual(pipe._port_temperature("port_1"), 250.0)
|
||||
self.assertAlmostEqual(pipe._port_temperature("port_2"), 400.0)
|
||||
self.assertEqual(pipe.port_1.h_outflow, h_2)
|
||||
self.assertEqual(pipe.port_2.h_outflow, h_1)
|
||||
|
||||
def test_pn2pipefr_cache_keys_all_property_inputs(self) -> None:
|
||||
pipe = AmesimPnl00r(
|
||||
"pnl_1",
|
||||
self.medium,
|
||||
@@ -113,12 +294,12 @@ class AmesimPnl00rComponentTests(unittest.TestCase):
|
||||
"port_2": self.medium.specific_enthalpy(300.0),
|
||||
}
|
||||
)
|
||||
pipe._mass_flow_for_pressure_drop.cache_clear()
|
||||
AmesimPnl0001._one_way_pn2pipefr_mass_flow.cache_clear()
|
||||
|
||||
first = pipe.mass_flow(501000.0, 500000.0)
|
||||
after_first = pipe._mass_flow_for_pressure_drop.cache_info()
|
||||
after_first = AmesimPnl0001._one_way_pn2pipefr_mass_flow.cache_info()
|
||||
second = pipe.mass_flow(501000.0, 500000.0)
|
||||
after_second = pipe._mass_flow_for_pressure_drop.cache_info()
|
||||
after_second = AmesimPnl0001._one_way_pn2pipefr_mass_flow.cache_info()
|
||||
pipe.update_flow_temperature_references(
|
||||
{
|
||||
"port_1": self.medium.specific_enthalpy(400.0),
|
||||
@@ -126,7 +307,7 @@ class AmesimPnl00rComponentTests(unittest.TestCase):
|
||||
}
|
||||
)
|
||||
third = pipe.mass_flow(501000.0, 500000.0)
|
||||
after_temperature_change = pipe._mass_flow_for_pressure_drop.cache_info()
|
||||
after_temperature_change = AmesimPnl0001._one_way_pn2pipefr_mass_flow.cache_info()
|
||||
|
||||
self.assertEqual(first, second)
|
||||
self.assertEqual(after_first.misses, 1)
|
||||
@@ -345,6 +526,10 @@ class AmesimPnl00rComponentTests(unittest.TestCase):
|
||||
actual_flow,
|
||||
upstream_temperature,
|
||||
)
|
||||
friction = pipe.friction_factor(expected_reynolds)
|
||||
flow_coefficient = (
|
||||
pipe.diam / (pipe.le * friction)
|
||||
) ** 0.5
|
||||
self.assertAlmostEqual(results["re"], expected_reynolds)
|
||||
self.assertAlmostEqual(
|
||||
results["v"],
|
||||
@@ -354,11 +539,11 @@ class AmesimPnl00rComponentTests(unittest.TestCase):
|
||||
results["cm"],
|
||||
abs(actual_flow)
|
||||
* upstream_temperature**0.5
|
||||
/ (pipe.area * upstream_pressure),
|
||||
/ (flow_coefficient * pipe.area * upstream_pressure),
|
||||
)
|
||||
self.assertAlmostEqual(
|
||||
results["ff"],
|
||||
min(pipe.friction_factor(expected_reynolds), 64_000_000.0),
|
||||
min(friction, 64_000_000.0),
|
||||
)
|
||||
|
||||
def test_friction_factor_transitions_from_laminar_to_turbulent(self) -> None:
|
||||
@@ -405,6 +590,69 @@ class AmesimPnl00rComponentTests(unittest.TestCase):
|
||||
delta=8.0e-5,
|
||||
)
|
||||
|
||||
def test_friction_factor_matches_amesim_transition_sweep(self) -> None:
|
||||
pipe = AmesimPnl00r(
|
||||
"pnl_14mm",
|
||||
self.medium,
|
||||
diam=0.014,
|
||||
rr=0.045 / 14.0,
|
||||
)
|
||||
samples = (
|
||||
(1408.0846374820405, 0.04545209089983021),
|
||||
(1594.9147224775163, 0.04022919421391556),
|
||||
(1799.2599253847663, 0.03590026408283139),
|
||||
(2001.1290261016431, 0.03286496043823008),
|
||||
(2200.69088342098, 0.03111112083131272),
|
||||
(2298.7460389094877, 0.03074226487938153),
|
||||
(2396.165041443924, 0.030707477592933765),
|
||||
(2600.199197308607, 0.03166638237616583),
|
||||
(2795.6649128003255, 0.03349648426341731),
|
||||
(2996.892872668051, 0.035593529418766444),
|
||||
(3204.685791989844, 0.037354280559382634),
|
||||
(3394.136683451109, 0.03839469933871734),
|
||||
)
|
||||
|
||||
for reynolds, expected in samples:
|
||||
with self.subTest(reynolds=reynolds):
|
||||
self.assertAlmostEqual(
|
||||
pipe.friction_factor(reynolds) / expected,
|
||||
1.0,
|
||||
delta=0.0015,
|
||||
)
|
||||
|
||||
def test_mass_flow_is_monotone_through_transition(self) -> None:
|
||||
medium = AmesimHeliumPengRobinsonMedium()
|
||||
pipe = AmesimPnl00r(
|
||||
"pnl_14mm",
|
||||
medium,
|
||||
diam=0.014,
|
||||
le=1.0,
|
||||
rr=0.045 / 14.0,
|
||||
)
|
||||
mean_pressure = 10.6e6
|
||||
temperature = 262.0
|
||||
enthalpy = medium.specific_enthalpy_at_pressure(mean_pressure, temperature)
|
||||
pipe.update_flow_temperature_references(
|
||||
{"port_1": enthalpy, "port_2": enthalpy}
|
||||
)
|
||||
|
||||
forward: list[float] = []
|
||||
reverse: list[float] = []
|
||||
for index in range(221):
|
||||
pressure_drop = 25.0 + index * 0.25
|
||||
high = mean_pressure + 0.5 * pressure_drop
|
||||
low = mean_pressure - 0.5 * pressure_drop
|
||||
forward.append(pipe.mass_flow(high, low))
|
||||
reverse.append(-pipe.mass_flow(low, high))
|
||||
|
||||
self.assertLess(pipe.reynolds_number(forward[0], temperature), 1400.0)
|
||||
self.assertGreater(
|
||||
pipe.reynolds_number(forward[-1], temperature), 3400.0
|
||||
)
|
||||
self.assertTrue(all(left < right for left, right in zip(forward, forward[1:])))
|
||||
for forward_flow, reverse_flow in zip(forward, reverse):
|
||||
self.assertAlmostEqual(forward_flow, reverse_flow, delta=1.0e-12)
|
||||
|
||||
def test_friction_factor_matches_amesim_transition_regime(self) -> None:
|
||||
pipe = AmesimPnl00r(
|
||||
"pnl_20mm",
|
||||
|
||||
@@ -125,12 +125,12 @@ class RegressionManifestTests(unittest.TestCase):
|
||||
self.assertTrue(manifest["source"]["xmlIsAuthoritative"])
|
||||
self.assertEqual(
|
||||
manifest["source"]["sha256"],
|
||||
"170463d65d074da01f0f9e9dab730b3815c94c1cc80b5190ec2e3fe623da74d3",
|
||||
"6eb2753a15af66fd8e91c169c4587e66b0ace2164c08c2cb23a67aab5fd09164",
|
||||
)
|
||||
self.assertEqual(Path(manifest["_sourcePath"]).name, "test-mql-8.xml")
|
||||
self.assertEqual(
|
||||
manifest["source"]["companionProject"]["sha256"],
|
||||
"258c50ee4850baa72fb7c2cc24536d0a631fc6a7f1fa6cedb7b6eea7c857cbaa",
|
||||
"8d7a263691a9f09f39c92aba19ae1350911dc66b4904e98bae5f20592c5a3fcd",
|
||||
)
|
||||
self.assertFalse(
|
||||
manifest["source"]["companionProject"]["executionInput"]
|
||||
@@ -142,6 +142,10 @@ class RegressionManifestTests(unittest.TestCase):
|
||||
self.assertEqual(manifest["lanes"]["production"]["samplingMode"], "source")
|
||||
self.assertEqual(manifest["lanes"]["solver-only"]["sampleStep"], 0.02)
|
||||
self.assertEqual(manifest["lanes"]["solver-only"]["maxStep"], 0.05)
|
||||
self.assertEqual(manifest["historicalReports"][2]["status"], "historicalOnly")
|
||||
self.assertFalse(
|
||||
manifest["historicalReports"][2]["compatibleWithCurrentSource"]
|
||||
)
|
||||
|
||||
def test_in_memory_derivative_does_not_change_authoritative_xml(self) -> None:
|
||||
manifest = load_regression_manifest(DEFAULT_MANIFEST_PATH)
|
||||
@@ -155,9 +159,9 @@ class RegressionManifestTests(unittest.TestCase):
|
||||
max_step=0.005,
|
||||
)
|
||||
|
||||
self.assertEqual(source_simulation_config(before)["tStop"], 0.2)
|
||||
self.assertEqual(source_simulation_config(before)["sampleStep"], 0.01)
|
||||
self.assertEqual(source_simulation_config(before)["maxStep"], 0.01)
|
||||
self.assertEqual(source_simulation_config(before)["tStop"], 5.0)
|
||||
self.assertEqual(source_simulation_config(before)["sampleStep"], 0.005)
|
||||
self.assertEqual(source_simulation_config(before)["maxStep"], 0.005)
|
||||
self.assertEqual(source_simulation_config(derived)["tStop"], 1.0)
|
||||
self.assertEqual(source_simulation_config(derived)["sampleStep"], 0.02)
|
||||
self.assertEqual(source_simulation_config(derived)["maxStep"], 0.005)
|
||||
@@ -581,7 +585,7 @@ class ProgressiveSuiteTests(unittest.TestCase):
|
||||
|
||||
self.assertEqual(
|
||||
seen,
|
||||
[("solver-only", 0.02, 0.05), ("production", 0.01, 0.01)],
|
||||
[("solver-only", 0.02, 0.05), ("production", 0.005, 0.005)],
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -222,15 +222,15 @@ class TargetCausalNumericIRStructureTests(unittest.TestCase):
|
||||
assert self.compilation.ir is not None
|
||||
program = self.compilation.ir.program
|
||||
|
||||
self.assertEqual(len(program.compatibility_slots), 760)
|
||||
self.assertEqual(len(program.canonical_slots), 440)
|
||||
self.assertEqual(program.effort_group_count, 112)
|
||||
self.assertEqual(program.flow_assignment_count, 328)
|
||||
self.assertEqual(program.effort_scatter_count, 432)
|
||||
self.assertEqual(program.eliminated_effort_replica_count, 320)
|
||||
self.assertEqual(len(program.compatibility_slots), 776)
|
||||
self.assertEqual(len(program.canonical_slots), 452)
|
||||
self.assertEqual(program.effort_group_count, 116)
|
||||
self.assertEqual(program.flow_assignment_count, 336)
|
||||
self.assertEqual(program.effort_scatter_count, 440)
|
||||
self.assertEqual(program.eliminated_effort_replica_count, 324)
|
||||
self.assertEqual(
|
||||
tuple(slot.slot for slot in program.canonical_slots),
|
||||
tuple(range(440)),
|
||||
tuple(range(452)),
|
||||
)
|
||||
|
||||
def test_target_batches_efforts_and_preserves_flow_stage_layout(self) -> None:
|
||||
@@ -240,8 +240,8 @@ class TargetCausalNumericIRStructureTests(unittest.TestCase):
|
||||
stage for stage in program.effort_stages if stage.variable == "p"
|
||||
)
|
||||
|
||||
self.assertEqual(len(pressure_stage.operations), 72)
|
||||
self.assertEqual(len(pressure_stage.evaluations), 36)
|
||||
self.assertEqual(len(pressure_stage.operations), 76)
|
||||
self.assertEqual(len(pressure_stage.evaluations), 40)
|
||||
self.assertTrue(
|
||||
all(
|
||||
evaluation.opcode
|
||||
@@ -251,7 +251,7 @@ class TargetCausalNumericIRStructureTests(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(
|
||||
[len(stage.target_slots) for stage in program.flow_stages],
|
||||
[110, 130, 49, 33, 5, 1],
|
||||
[114, 134, 49, 33, 5, 1],
|
||||
)
|
||||
|
||||
def test_target_reference_execution_matches_all_compatibility_slots(self) -> None:
|
||||
|
||||
@@ -7,6 +7,7 @@ from unittest.mock import patch
|
||||
from app.simulation.components.amesim.boundary.sources import AmesimPnpl01
|
||||
from app.simulation.components.amesim.flow.orifices import (
|
||||
AmesimPnor001,
|
||||
AmesimPnvo001FixedOpening,
|
||||
AmesimPnvo001SignalOpening,
|
||||
)
|
||||
from app.simulation.components.amesim.flow.pipes import AmesimPnl00r
|
||||
@@ -110,6 +111,62 @@ class PressureFlowSolverInitializationTests(unittest.TestCase):
|
||||
self.assertAlmostEqual(valve.port_2.p, pipe.port_1.p)
|
||||
self.assertAlmostEqual(-valve.port_2.m_flow, pipe.port_1.m_flow)
|
||||
|
||||
def test_pnvo_pnl00r_series_pressure_is_seeded_by_flow_balance(self) -> None:
|
||||
medium = IdealGasMedium()
|
||||
source = Cylinder(
|
||||
"source",
|
||||
medium,
|
||||
V=0.01,
|
||||
p0=150_000.0,
|
||||
T0=300.0,
|
||||
)
|
||||
valve = AmesimPnvo001FixedOpening(
|
||||
"valve",
|
||||
medium,
|
||||
opening=0.5,
|
||||
)
|
||||
pipe = AmesimPnl00r(
|
||||
"pipe",
|
||||
medium,
|
||||
diam=0.02,
|
||||
le=1.0,
|
||||
rr=1.0e-5,
|
||||
)
|
||||
sink = Tank(
|
||||
"sink",
|
||||
medium,
|
||||
V=0.1,
|
||||
p0=100_000.0,
|
||||
T0=300.0,
|
||||
)
|
||||
network = SimulationNetwork("pnvo-pnl00r-series")
|
||||
for component in (source, valve, pipe, sink):
|
||||
network.add_component(component)
|
||||
network.connect("source", "port_b", "valve", "port_2")
|
||||
network.connect("valve", "port_3", "pipe", "port_1")
|
||||
network.connect("pipe", "port_2", "sink", "port_a")
|
||||
source.refresh_thermodynamic_ports()
|
||||
sink.refresh_thermodynamic_ports()
|
||||
|
||||
solver = PressureFlowSolver(network)
|
||||
result = solver.solve()
|
||||
|
||||
self.assertTrue(result.success)
|
||||
self.assertEqual(result.evaluations, 0)
|
||||
self.assertGreater(valve.port_3.p, pipe.port_2.p)
|
||||
self.assertLess(valve.port_3.p, source.port_b.p)
|
||||
self.assertAlmostEqual(valve.port_3.p, pipe.port_1.p)
|
||||
self.assertAlmostEqual(
|
||||
-valve.port_3.m_flow,
|
||||
pipe.port_1.m_flow,
|
||||
delta=1.0e-10,
|
||||
)
|
||||
self.assertFalse(solver.causal_fast_path_eligible)
|
||||
self.assertEqual(
|
||||
solver.causal_execution_diagnostics()["fallbackReason"],
|
||||
"specialSeriesPressureSeed",
|
||||
)
|
||||
|
||||
def test_dead_ended_pnl00r_is_seeded_at_zero_flow_pressure(self) -> None:
|
||||
medium = IdealGasMedium()
|
||||
pipe = AmesimPnl00r("resistance", medium)
|
||||
|
||||
@@ -165,9 +165,9 @@ class SupportedPistonTangentCompilerTests(unittest.TestCase):
|
||||
order=0,
|
||||
)
|
||||
|
||||
self.assertEqual(pattern.nnz, 3296)
|
||||
self.assertEqual(columns_only.nnz, 2464)
|
||||
self.assertEqual(len(active_columns), 108)
|
||||
self.assertEqual(pattern.nnz, 3280)
|
||||
self.assertEqual(columns_only.nnz, 2448)
|
||||
self.assertEqual(len(active_columns), 116)
|
||||
self.assertEqual(int(groups.max(initial=-1)) + 1, 36)
|
||||
|
||||
jacobian = SparseSecantJacobian(
|
||||
@@ -182,10 +182,10 @@ class SupportedPistonTangentCompilerTests(unittest.TestCase):
|
||||
self.assertEqual(diagnostics["originalColorGroupCount"], 52)
|
||||
self.assertEqual(diagnostics["remainingColorGroupCount"], 36)
|
||||
self.assertEqual(diagnostics["exactColumnCount"], 16)
|
||||
self.assertEqual(diagnostics["finiteDifferenceColumnCount"], 108)
|
||||
self.assertEqual(diagnostics["finiteDifferenceColumnCount"], 116)
|
||||
self.assertEqual(
|
||||
jacobian._remaining_finite_difference_sparsity.nnz,
|
||||
2176,
|
||||
2160,
|
||||
)
|
||||
|
||||
def test_partial_unsupported_branch_fails_with_stable_reason(self) -> None:
|
||||
|
||||
@@ -127,12 +127,15 @@ class TestMql8StaticRegressionTests(unittest.TestCase):
|
||||
case_id,
|
||||
)
|
||||
|
||||
def test_approved_production_golden_replays_its_source_report(self) -> None:
|
||||
def test_historical_v2_golden_replays_its_source_report(self) -> None:
|
||||
repository_root = Path(self.manifest["_repositoryRoot"])
|
||||
reference = self.manifest["variants"]["0.2s"]["goldens"]["production"]
|
||||
golden_path = (
|
||||
repository_root
|
||||
/ "tests/baselines/simulation/test_mql_8/goldens/production-0.2s-v1.json"
|
||||
)
|
||||
golden = load_regression_golden(
|
||||
repository_root / reference["path"],
|
||||
expected_sha256=reference["sha256"],
|
||||
golden_path,
|
||||
expected_sha256="99fa7b3631a89f59f86551847175f3701a1567d8d0bd10d87545734f17515d72",
|
||||
repository_root=repository_root,
|
||||
)
|
||||
source_report = golden["provenance"]["sourceReport"]
|
||||
@@ -142,12 +145,16 @@ class TestMql8StaticRegressionTests(unittest.TestCase):
|
||||
case = next(item for item in report["cases"] if item["caseId"] == "0.2s")
|
||||
audit = evaluate_regression_golden(case["worker"]["summary"], golden)
|
||||
|
||||
self.assertNotEqual(
|
||||
golden["sourceXmlSha256"],
|
||||
self.manifest["source"]["sha256"],
|
||||
)
|
||||
self.assertTrue(audit["passed"], audit)
|
||||
self.assertEqual(audit["comparedValueCount"], 402)
|
||||
self.assertEqual(audit["maxAbsoluteError"], 0.0)
|
||||
self.assertEqual(audit["maxToleranceRatio"], 0.0)
|
||||
|
||||
def test_extension_decision_is_bound_to_the_current_report_and_budget(self) -> None:
|
||||
def test_historical_v2_extension_decision_remains_auditable(self) -> None:
|
||||
repository_root = Path(self.manifest["_repositoryRoot"])
|
||||
runs = repository_root / "tests/baselines/simulation/test_mql_8/runs"
|
||||
decision = json.loads(
|
||||
@@ -164,6 +171,10 @@ class TestMql8StaticRegressionTests(unittest.TestCase):
|
||||
first_decision = decision["decisions"][0]
|
||||
|
||||
self.assertEqual(
|
||||
decision["sourceXmlSha256"],
|
||||
"170463d65d074da01f0f9e9dab730b3815c94c1cc80b5190ec2e3fe623da74d3",
|
||||
)
|
||||
self.assertNotEqual(
|
||||
decision["sourceXmlSha256"], self.manifest["source"]["sha256"]
|
||||
)
|
||||
self.assertEqual(
|
||||
@@ -191,7 +202,7 @@ class TestMql8StaticRegressionTests(unittest.TestCase):
|
||||
self.assertEqual(first_decision["outcome"], "deferred")
|
||||
self.assertTrue(decision["simulationWasNotStartedForDeferredCases"])
|
||||
|
||||
def test_periodic_main_lane_exercises_the_approved_production_golden(self) -> None:
|
||||
def test_periodic_main_lane_uses_the_current_production_source(self) -> None:
|
||||
workflow = (
|
||||
Path(self.manifest["_repositoryRoot"])
|
||||
/ ".github/workflows/solver-regression.yml"
|
||||
@@ -199,11 +210,12 @@ class TestMql8StaticRegressionTests(unittest.TestCase):
|
||||
|
||||
self.assertIn("default: production", workflow)
|
||||
self.assertIn("inputs.lane || 'production'", workflow)
|
||||
self.assertNotIn("goldens", self.manifest["variants"]["0.2s"])
|
||||
|
||||
|
||||
@unittest.skipUnless(
|
||||
os.getenv(LONG_RUN_ENVIRONMENT, "").strip().lower() in {"1", "true", "yes"},
|
||||
f"Set {LONG_RUN_ENVIRONMENT}=1 to run bounded 0.01/0.2/1/5/10 s integration.",
|
||||
f"Set {LONG_RUN_ENVIRONMENT}=1 to run bounded 0.005/0.2/1/5/10 s integration.",
|
||||
)
|
||||
class TestMql8ProgressiveLongRegressionTests(unittest.TestCase):
|
||||
@classmethod
|
||||
|
||||
Reference in new issue
Block a user