修正test_mql事件窗口插值诊断
This commit is contained in:
1 parent
331435e78a
commit
01b606df30
5 files changed
+103
-3
No files matched your search
@@ -70,6 +70,24 @@ class AmesimPneumaticGas:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def pressure_transport_enthalpy(
|
||||||
|
self,
|
||||||
|
pressure: float,
|
||||||
|
temperature: float,
|
||||||
|
reference_pressure: float = 101_300.0,
|
||||||
|
reference_temperature: float = 298.15,
|
||||||
|
) -> float:
|
||||||
|
"""Convert AMESim reference enthalpy to the absolute-energy state basis."""
|
||||||
|
return (
|
||||||
|
self.pressure_reference_enthalpy(
|
||||||
|
pressure,
|
||||||
|
temperature,
|
||||||
|
reference_pressure,
|
||||||
|
reference_temperature,
|
||||||
|
)
|
||||||
|
+ self.cp * reference_temperature
|
||||||
|
)
|
||||||
|
|
||||||
def temperature_from_internal_energy(self, specific_internal_energy: float) -> float:
|
def temperature_from_internal_energy(self, specific_internal_energy: float) -> float:
|
||||||
if self.cv <= 0.0:
|
if self.cv <= 0.0:
|
||||||
raise ValueError("cv must be positive.")
|
raise ValueError("cv must be positive.")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
from bisect import bisect_left
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
from math import nextafter, sqrt
|
from math import nextafter, sqrt
|
||||||
@@ -492,6 +493,8 @@ class TestMqlPnvoEventWindowSampleDiagnostic:
|
|||||||
data_paths: tuple[str, ...]
|
data_paths: tuple[str, ...]
|
||||||
python_values_by_data_path: dict[str, float]
|
python_values_by_data_path: dict[str, float]
|
||||||
amesim_values_by_data_path: dict[str, float]
|
amesim_values_by_data_path: dict[str, float]
|
||||||
|
amesim_sample_left_time_s: float
|
||||||
|
amesim_sample_right_time_s: float
|
||||||
pnl0001_rhs_diagnostics: tuple[TestMqlPnl0001LineRhsDiagnostic, ...] = field(
|
pnl0001_rhs_diagnostics: tuple[TestMqlPnl0001LineRhsDiagnostic, ...] = field(
|
||||||
default_factory=tuple
|
default_factory=tuple
|
||||||
)
|
)
|
||||||
@@ -517,6 +520,10 @@ class TestMqlPnvoEventWindowSampleDiagnostic:
|
|||||||
TestMqlPnch012EnergyEquationDiagnostic, ...
|
TestMqlPnch012EnergyEquationDiagnostic, ...
|
||||||
] = field(default_factory=tuple)
|
] = field(default_factory=tuple)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def amesim_values_are_interpolated(self) -> bool:
|
||||||
|
return self.amesim_sample_left_time_s != self.amesim_sample_right_time_s
|
||||||
|
|
||||||
def abs_error(self, data_path: str) -> float:
|
def abs_error(self, data_path: str) -> float:
|
||||||
return abs(
|
return abs(
|
||||||
self.python_values_by_data_path[data_path]
|
self.python_values_by_data_path[data_path]
|
||||||
@@ -929,6 +936,27 @@ def _series_one_sided_differences_at(
|
|||||||
return backward, forward
|
return backward, forward
|
||||||
|
|
||||||
|
|
||||||
|
def _series_sample_bracket(
|
||||||
|
*,
|
||||||
|
times: tuple[float, ...] | list[float],
|
||||||
|
time_s: float,
|
||||||
|
) -> tuple[float, float]:
|
||||||
|
if not times:
|
||||||
|
raise ValueError("at least one sample time is required")
|
||||||
|
index = bisect_left(times, time_s)
|
||||||
|
tolerance = 1.0e-12 * max(1.0, abs(time_s))
|
||||||
|
for candidate_index in (index - 1, index):
|
||||||
|
if 0 <= candidate_index < len(times):
|
||||||
|
candidate_time = times[candidate_index]
|
||||||
|
if abs(candidate_time - time_s) <= tolerance:
|
||||||
|
return candidate_time, candidate_time
|
||||||
|
if index <= 0:
|
||||||
|
return times[0], times[0]
|
||||||
|
if index >= len(times):
|
||||||
|
return times[-1], times[-1]
|
||||||
|
return times[index - 1], times[index]
|
||||||
|
|
||||||
|
|
||||||
def _reference_enthalpy_flow_for_node_port(
|
def _reference_enthalpy_flow_for_node_port(
|
||||||
*,
|
*,
|
||||||
gas: AmesimPneumaticGas,
|
gas: AmesimPneumaticGas,
|
||||||
@@ -2157,12 +2185,18 @@ def run_test_mql_pnvo_event_window_diagnostic(
|
|||||||
time_s=sample_time,
|
time_s=sample_time,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
(
|
||||||
|
amesim_sample_left_time_s,
|
||||||
|
amesim_sample_right_time_s,
|
||||||
|
) = _series_sample_bracket(times=amesim_results.times, time_s=sample_time)
|
||||||
sample_diagnostics.append(
|
sample_diagnostics.append(
|
||||||
TestMqlPnvoEventWindowSampleDiagnostic(
|
TestMqlPnvoEventWindowSampleDiagnostic(
|
||||||
time_s=sample_time,
|
time_s=sample_time,
|
||||||
data_paths=data_paths,
|
data_paths=data_paths,
|
||||||
python_values_by_data_path=python_sample_values,
|
python_values_by_data_path=python_sample_values,
|
||||||
amesim_values_by_data_path=amesim_sample_values,
|
amesim_values_by_data_path=amesim_sample_values,
|
||||||
|
amesim_sample_left_time_s=amesim_sample_left_time_s,
|
||||||
|
amesim_sample_right_time_s=amesim_sample_right_time_s,
|
||||||
pnl0001_rhs_diagnostics=pnl0001_rhs_diagnostics,
|
pnl0001_rhs_diagnostics=pnl0001_rhs_diagnostics,
|
||||||
pnl0001_pressure_loss_diagnostics=pnl0001_pressure_loss_diagnostics,
|
pnl0001_pressure_loss_diagnostics=pnl0001_pressure_loss_diagnostics,
|
||||||
pnvo_flow_parameter_diagnostics=pnvo_flow_parameter_diagnostics,
|
pnvo_flow_parameter_diagnostics=pnvo_flow_parameter_diagnostics,
|
||||||
@@ -2224,7 +2258,17 @@ def format_test_mql_pnvo_event_window_summary(
|
|||||||
if diagnostic.sample_diagnostics:
|
if diagnostic.sample_diagnostics:
|
||||||
lines.append("Sample comparisons:")
|
lines.append("Sample comparisons:")
|
||||||
for sample in diagnostic.sample_diagnostics:
|
for sample in diagnostic.sample_diagnostics:
|
||||||
lines.append(f" t={sample.time_s}")
|
if sample.amesim_values_are_interpolated:
|
||||||
|
amesim_sample_source = (
|
||||||
|
"linear-interpolation@"
|
||||||
|
f"{sample.amesim_sample_left_time_s}"
|
||||||
|
f"..{sample.amesim_sample_right_time_s}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
amesim_sample_source = f"exact@{sample.amesim_sample_left_time_s}"
|
||||||
|
lines.append(
|
||||||
|
f" t={sample.time_s} (amesim={amesim_sample_source})"
|
||||||
|
)
|
||||||
for data_path in sample.data_paths:
|
for data_path in sample.data_paths:
|
||||||
lines.append(
|
lines.append(
|
||||||
f" - {data_path}: "
|
f" - {data_path}: "
|
||||||
|
|||||||
@@ -55,6 +55,17 @@ class AmesimPneumaticComponentsTest(unittest.TestCase):
|
|||||||
delta=0.1,
|
delta=0.1,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_pressure_transport_enthalpy_restores_absolute_energy_offset(self) -> None:
|
||||||
|
gas = HELIUM_PNEUMATIC_GAS
|
||||||
|
pressure = 13_839_965.0
|
||||||
|
temperature = 287.7322
|
||||||
|
|
||||||
|
self.assertAlmostEqual(
|
||||||
|
gas.pressure_transport_enthalpy(pressure, temperature),
|
||||||
|
gas.pressure_reference_enthalpy(pressure, temperature)
|
||||||
|
+ gas.cp * 298.15,
|
||||||
|
)
|
||||||
|
|
||||||
def test_reference_temperature_from_enthalpy_rejects_non_positive_cp(self) -> None:
|
def test_reference_temperature_from_enthalpy_rejects_non_positive_cp(self) -> None:
|
||||||
gas = AmesimPneumaticGas(cp=0.0, cv=3116.0)
|
gas = AmesimPneumaticGas(cp=0.0, cv=3116.0)
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ from PythonModels.scripts.run_test_mql_full_state_comparison import (
|
|||||||
format_test_mql_pnvo_event_boundary_summary,
|
format_test_mql_pnvo_event_boundary_summary,
|
||||||
format_test_mql_pnvo_event_window_summary,
|
format_test_mql_pnvo_event_window_summary,
|
||||||
run_test_mql_full_state_comparison,
|
run_test_mql_full_state_comparison,
|
||||||
|
_series_sample_bracket,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -34,6 +35,23 @@ TEST_MQL_AME = REPO_ROOT / "AmesimModels" / "test_mql.ame"
|
|||||||
|
|
||||||
|
|
||||||
class RunTestMqlFullStateComparisonScriptTests(unittest.TestCase):
|
class RunTestMqlFullStateComparisonScriptTests(unittest.TestCase):
|
||||||
|
def test_series_sample_bracket_distinguishes_exact_and_interpolated_times(self) -> None:
|
||||||
|
times = (0.04000000000000009, 0.050000000000000114)
|
||||||
|
|
||||||
|
exact_left, exact_right = _series_sample_bracket(
|
||||||
|
times=times,
|
||||||
|
time_s=0.04,
|
||||||
|
)
|
||||||
|
interpolated_left, interpolated_right = _series_sample_bracket(
|
||||||
|
times=times,
|
||||||
|
time_s=0.041,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(exact_left, exact_right)
|
||||||
|
self.assertAlmostEqual(exact_left, 0.04)
|
||||||
|
self.assertAlmostEqual(interpolated_left, 0.04)
|
||||||
|
self.assertAlmostEqual(interpolated_right, 0.05)
|
||||||
|
|
||||||
def test_full_state_script_writes_summary_and_comparison_csv(self) -> None:
|
def test_full_state_script_writes_summary_and_comparison_csv(self) -> None:
|
||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
output_dir = Path(tmpdir)
|
output_dir = Path(tmpdir)
|
||||||
@@ -231,6 +249,8 @@ class RunTestMqlFullStateComparisonScriptTests(unittest.TestCase):
|
|||||||
"xv@pn_morifice_1": 1.0,
|
"xv@pn_morifice_1": 1.0,
|
||||||
"dm2@pn_morifice_1": 456.8,
|
"dm2@pn_morifice_1": 456.8,
|
||||||
},
|
},
|
||||||
|
amesim_sample_left_time_s=0.05,
|
||||||
|
amesim_sample_right_time_s=0.05,
|
||||||
pnl0001_rhs_diagnostics=(
|
pnl0001_rhs_diagnostics=(
|
||||||
TestMqlPnl0001LineRhsDiagnostic(
|
TestMqlPnl0001LineRhsDiagnostic(
|
||||||
line_alias="pneumatic_69",
|
line_alias="pneumatic_69",
|
||||||
@@ -497,7 +517,8 @@ class RunTestMqlFullStateComparisonScriptTests(unittest.TestCase):
|
|||||||
self.assertAlmostEqual(sample.abs_error("press@pn_c1_8"), 32067.9)
|
self.assertAlmostEqual(sample.abs_error("press@pn_c1_8"), 32067.9)
|
||||||
self.assertIn("PNVO event window diagnostic (pn_morifice_1)", summary)
|
self.assertIn("PNVO event window diagnostic (pn_morifice_1)", summary)
|
||||||
self.assertIn("Sample comparisons:", summary)
|
self.assertIn("Sample comparisons:", summary)
|
||||||
self.assertIn("t=0.05", summary)
|
self.assertFalse(sample.amesim_values_are_interpolated)
|
||||||
|
self.assertIn("t=0.05 (amesim=exact@0.05)", summary)
|
||||||
self.assertIn("rhs=8154", summary)
|
self.assertIn("rhs=8154", summary)
|
||||||
self.assertIn("rhs@pneumatic_69", summary)
|
self.assertIn("rhs@pneumatic_69", summary)
|
||||||
self.assertIn("dm_dt=-0.0222", summary)
|
self.assertIn("dm_dt=-0.0222", summary)
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ from PythonModels.reporting.test_mql_orifice_observations import (
|
|||||||
G_PER_S_TO_KG_PER_S,
|
G_PER_S_TO_KG_PER_S,
|
||||||
build_test_mql_orifice_observation_catalog,
|
build_test_mql_orifice_observation_catalog,
|
||||||
)
|
)
|
||||||
|
from PythonModels.systems.test_mql_pneumatic import (
|
||||||
|
TEST_MQL_PNVO001_FLOW_COEFFICIENT_MULTIPLIER,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
@@ -55,7 +58,10 @@ class TestMqlOrificeObservationCatalogTests(unittest.TestCase):
|
|||||||
last = binding.observation_at(self.amesim_results, -1)
|
last = binding.observation_at(self.amesim_results, -1)
|
||||||
|
|
||||||
self.assertAlmostEqual(binding.nominal_area_m2, 78.5e-6)
|
self.assertAlmostEqual(binding.nominal_area_m2, 78.5e-6)
|
||||||
self.assertAlmostEqual(binding.flow_coefficient, 0.45)
|
self.assertAlmostEqual(
|
||||||
|
binding.flow_coefficient,
|
||||||
|
0.45 * TEST_MQL_PNVO001_FLOW_COEFFICIENT_MULTIPLIER,
|
||||||
|
)
|
||||||
self.assertEqual(first.opening, 0.0)
|
self.assertEqual(first.opening, 0.0)
|
||||||
self.assertEqual(first.effective_area_m2, 0.0)
|
self.assertEqual(first.effective_area_m2, 0.0)
|
||||||
self.assertEqual(last.opening, 1.0)
|
self.assertEqual(last.opening, 1.0)
|
||||||
|
|||||||
Reference in new issue
Block a user