接入AMESim help文档与PNVO流量诊断

This commit is contained in:
huojiarong committed 2026-07-22 08:56:04 +00:00
1 parent f0f8f40c7a
commit 84d1675292
17 files changed
+450 -7

No files matched your search

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2 -1
View File
@@ -332,7 +332,8 @@ class AmesimPneumaticOrifice(AlgebraicComponent):
@property @property
def effective_area(self) -> float: def effective_area(self) -> float:
return self.area * max(self.opening, 0.0) opening = min(max(self.opening, 0.0), 1.0)
return self.area * opening
def mass_flow(self, p_a: float, p_b: float, upstream_temperature: float) -> float: def mass_flow(self, p_a: float, p_b: float, upstream_temperature: float) -> float:
if p_a == p_b or self.effective_area == 0.0 or self.flow_coefficient == 0.0: if p_a == p_b or self.effective_area == 0.0 or self.flow_coefficient == 0.0:
@@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from math import log10, pi from math import log10, pi, sqrt
from PythonModels.components.amesim_pneumatic import ( from PythonModels.components.amesim_pneumatic import (
HELIUM_PNEUMATIC_GAS, HELIUM_PNEUMATIC_GAS,
@@ -112,10 +112,11 @@ class AmesimPnl0001Pipe(_DarcyPipeResistanceMixin, DynamicComponent):
resistance. Both connection mass flows use the PythonModels convention: resistance. Both connection mass flows use the PythonModels convention:
positive values enter the pipe storage. positive values enter the pipe storage.
AMESim's proprietary pressure-loss calibration is not available in the AMESim's proprietary ``pn2pipefr`` utility is represented by an
archive. This implementation therefore uses an explicit Darcy-Weisbach optional calibrated linear conductance when a model-specific baseline
law while preserving the real geometry, state count, mass/energy balance, supports it; otherwise the component falls back to an auditable
heat-transfer parameter, and observable diagnostics. Darcy-Weisbach law. Both paths preserve the real geometry, state count,
mass/energy balance, heat-transfer parameter, and observable diagnostics.
""" """
def __init__( def __init__(
@@ -128,6 +129,7 @@ class AmesimPnl0001Pipe(_DarcyPipeResistanceMixin, DynamicComponent):
polytropic_constant: float = 1.35, polytropic_constant: float = 1.35,
heat_transfer_coefficient: float = 0.0, heat_transfer_coefficient: float = 0.0,
external_temperature_k: float = 293.15, external_temperature_k: float = 293.15,
calibrated_linear_conductance: float | None = None,
gas: AmesimPneumaticGas = HELIUM_PNEUMATIC_GAS, gas: AmesimPneumaticGas = HELIUM_PNEUMATIC_GAS,
p0: float = 101_325.0, p0: float = 101_325.0,
T0: float = 293.15, T0: float = 293.15,
@@ -144,6 +146,11 @@ class AmesimPnl0001Pipe(_DarcyPipeResistanceMixin, DynamicComponent):
raise ValueError("heat_transfer_coefficient must be non-negative") raise ValueError("heat_transfer_coefficient must be non-negative")
if external_temperature_k <= 0.0: if external_temperature_k <= 0.0:
raise ValueError("external_temperature_k must be positive") raise ValueError("external_temperature_k must be positive")
if (
calibrated_linear_conductance is not None
and calibrated_linear_conductance <= 0.0
):
raise ValueError("calibrated_linear_conductance must be positive")
super().__init__(name=name) super().__init__(name=name)
self.diameter = diameter_mm * 1.0e-3 self.diameter = diameter_mm * 1.0e-3
@@ -152,6 +159,7 @@ class AmesimPnl0001Pipe(_DarcyPipeResistanceMixin, DynamicComponent):
self.polytropic_constant = polytropic_constant self.polytropic_constant = polytropic_constant
self.heat_transfer_coefficient = heat_transfer_coefficient self.heat_transfer_coefficient = heat_transfer_coefficient
self.external_temperature = external_temperature_k self.external_temperature = external_temperature_k
self.calibrated_linear_conductance = calibrated_linear_conductance
self.gas = gas self.gas = gas
self.area = diameter_mm_to_area_m2(diameter_mm) self.area = diameter_mm_to_area_m2(diameter_mm)
self.volume = self.area * self.length self.volume = self.area * self.length
@@ -210,6 +218,12 @@ class AmesimPnl0001Pipe(_DarcyPipeResistanceMixin, DynamicComponent):
pressure_difference = port_1_pressure_pa - internal.p pressure_difference = port_1_pressure_pa - internal.p
if pressure_difference == 0.0: if pressure_difference == 0.0:
return 0.0 return 0.0
if self.calibrated_linear_conductance is not None:
return (
self.calibrated_linear_conductance
* pressure_difference
/ sqrt(internal.T)
)
upstream_pressure = max(port_1_pressure_pa, internal.p) upstream_pressure = max(port_1_pressure_pa, internal.p)
upstream_temperature = ( upstream_temperature = (
port_1_temperature_k if pressure_difference > 0.0 else internal.T port_1_temperature_k if pressure_difference > 0.0 else internal.T
@@ -246,6 +260,24 @@ class AmesimPnl0001Pipe(_DarcyPipeResistanceMixin, DynamicComponent):
pressure_drop_pa=pressure_drop, pressure_drop_pa=pressure_drop,
) )
def darcy_pressure_drop_for_state(
self,
*,
mass_flow_kg_s: float,
pressure_pa: float,
temperature_k: float,
) -> float:
if pressure_pa <= 0.0:
raise ValueError("pressure_pa must be positive")
if temperature_k <= 0.0:
raise ValueError("temperature_k must be positive")
density = self.gas.density(pressure_pa, temperature_k)
return self._darcy_pressure_drop(
mass_flow_kg_s,
density=density,
temperature=temperature_k,
)
def derivatives_from_connections( def derivatives_from_connections(
self, self,
*, *,
@@ -3,7 +3,7 @@ from __future__ import annotations
import argparse import argparse
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import UTC, datetime from datetime import UTC, datetime
from math import nextafter from math import nextafter, sqrt
from pathlib import Path from pathlib import Path
from PythonModels.core.solver import SolveIVPConfig, integrate_ode from PythonModels.core.solver import SolveIVPConfig, integrate_ode
@@ -27,6 +27,7 @@ from PythonModels.systems.test_mql import (
TestMqlSimulationResult, TestMqlSimulationResult,
TestMqlSystem, TestMqlSystem,
) )
from PythonModels.systems.test_mql_pneumatic import AMESIM_REFERENCE_PRESSURE_PA
DEFAULT_FULL_STATE_COMPARISON_DATA_PATHS = ( DEFAULT_FULL_STATE_COMPARISON_DATA_PATHS = (
@@ -265,6 +266,45 @@ class TestMqlPnvoEventWindowSegmentDiagnostic:
message: str message: str
@dataclass(frozen=True)
class TestMqlPnl0001PressureLossCalibrationDiagnostic:
line_alias: str
chamber_alias: str
time_s: float
amesim_cm: float
amesim_dm1_g_s: float
amesim_mass_flow_magnitude_kg_s: float
amesim_line_gauge_pressure_pa: float
amesim_chamber_gauge_pressure_pa: float
amesim_line_temperature_k: float
amesim_pressure_drop_pa: float
current_darcy_pressure_drop_pa: float
pressure_drop_multiplier: float
amesim_linear_conductance_kg_s_sqrt_k_per_pa: float
python_linear_conductance_kg_s_sqrt_k_per_pa: float
python_to_amesim_linear_conductance_ratio: float
@dataclass(frozen=True)
class TestMqlPnvoFlowParameterDiagnostic:
orifice_alias: str
time_s: float
amesim_cm: float
amesim_dm2_g_s: float
amesim_opening: float
amesim_gas_velocity_m_s: float
python_opening: float
python_flow_coefficient: float
python_effective_area_m2: float
python_line_pressure_pa: float
python_boundary_pressure_pa: float
python_upstream_pressure_pa: float
python_upstream_temperature_k: float
python_mass_flow_kg_s: float
python_cm: float
python_to_amesim_cm_ratio: float
@dataclass(frozen=True) @dataclass(frozen=True)
class TestMqlPnvoEventWindowSampleDiagnostic: class TestMqlPnvoEventWindowSampleDiagnostic:
time_s: float time_s: float
@@ -274,6 +314,12 @@ class TestMqlPnvoEventWindowSampleDiagnostic:
pnl0001_rhs_diagnostics: tuple[TestMqlPnl0001LineRhsDiagnostic, ...] = field( pnl0001_rhs_diagnostics: tuple[TestMqlPnl0001LineRhsDiagnostic, ...] = field(
default_factory=tuple default_factory=tuple
) )
pnl0001_pressure_loss_diagnostics: tuple[
TestMqlPnl0001PressureLossCalibrationDiagnostic, ...
] = field(default_factory=tuple)
pnvo_flow_parameter_diagnostics: tuple[
TestMqlPnvoFlowParameterDiagnostic, ...
] = field(default_factory=tuple)
def abs_error(self, data_path: str) -> float: def abs_error(self, data_path: str) -> float:
return abs( return abs(
@@ -509,6 +555,166 @@ def format_test_mql_pnvo_event_boundary_summary(
return "\n".join(lines) + "\n" return "\n".join(lines) + "\n"
def _pnl0001_pressure_loss_calibration_diagnostic(
*,
closure: object,
amesim_results: AmesimResults,
python_values_by_data_path: dict[str, float],
line_alias: str,
chamber_alias: str,
time_s: float,
) -> TestMqlPnl0001PressureLossCalibrationDiagnostic:
if line_alias != "pneumatic_69" or chamber_alias != "pn_c1_8":
raise KeyError(f"Unsupported PNL0001 pressure-loss diagnostic: {line_alias}")
line = closure.pneumatic_closure.components.p4_port3_remote_primary_line
def amesim_value(data_path: str) -> float:
return interpolate_series_value(
amesim_results.times,
amesim_results.series(data_path),
time_s,
)
amesim_cm = amesim_value(f"cm@{line_alias}")
amesim_dm1_g_s = amesim_value(f"dm1@{line_alias}")
amesim_line_gauge_pressure_pa = amesim_value(f"p2@{line_alias}")
amesim_chamber_gauge_pressure_pa = amesim_value(f"press@{chamber_alias}")
amesim_line_temperature_k = amesim_value(f"t2@{line_alias}")
mass_flow_magnitude_kg_s = abs(amesim_dm1_g_s) * 1.0e-3
amesim_pressure_drop_pa = abs(
amesim_line_gauge_pressure_pa - amesim_chamber_gauge_pressure_pa
)
current_darcy_pressure_drop_pa = abs(
line.darcy_pressure_drop_for_state(
mass_flow_kg_s=mass_flow_magnitude_kg_s,
pressure_pa=(
amesim_line_gauge_pressure_pa + AMESIM_REFERENCE_PRESSURE_PA
),
temperature_k=amesim_line_temperature_k,
)
)
if current_darcy_pressure_drop_pa > 0.0:
pressure_drop_multiplier = (
amesim_pressure_drop_pa / current_darcy_pressure_drop_pa
)
else:
pressure_drop_multiplier = (
float("inf") if amesim_pressure_drop_pa > 0.0 else 1.0
)
amesim_linear_conductance = _pnl0001_linear_conductance(
dm1_g_s=amesim_dm1_g_s,
temperature_k=amesim_line_temperature_k,
pressure_drop_pa=amesim_pressure_drop_pa,
)
python_pressure_drop_pa = abs(
python_values_by_data_path[f"p2@{line_alias}"]
- python_values_by_data_path[f"press@{chamber_alias}"]
)
python_linear_conductance = _pnl0001_linear_conductance(
dm1_g_s=python_values_by_data_path[f"dm1@{line_alias}"],
temperature_k=python_values_by_data_path[f"t2@{line_alias}"],
pressure_drop_pa=python_pressure_drop_pa,
)
if amesim_linear_conductance > 0.0:
conductance_ratio = python_linear_conductance / amesim_linear_conductance
else:
conductance_ratio = (
float("inf") if python_linear_conductance > 0.0 else 1.0
)
return TestMqlPnl0001PressureLossCalibrationDiagnostic(
line_alias=line_alias,
chamber_alias=chamber_alias,
time_s=time_s,
amesim_cm=amesim_cm,
amesim_dm1_g_s=amesim_dm1_g_s,
amesim_mass_flow_magnitude_kg_s=mass_flow_magnitude_kg_s,
amesim_line_gauge_pressure_pa=amesim_line_gauge_pressure_pa,
amesim_chamber_gauge_pressure_pa=amesim_chamber_gauge_pressure_pa,
amesim_line_temperature_k=amesim_line_temperature_k,
amesim_pressure_drop_pa=amesim_pressure_drop_pa,
current_darcy_pressure_drop_pa=current_darcy_pressure_drop_pa,
pressure_drop_multiplier=pressure_drop_multiplier,
amesim_linear_conductance_kg_s_sqrt_k_per_pa=(
amesim_linear_conductance
),
python_linear_conductance_kg_s_sqrt_k_per_pa=(
python_linear_conductance
),
python_to_amesim_linear_conductance_ratio=conductance_ratio,
)
def _pnl0001_linear_conductance(
*,
dm1_g_s: float,
temperature_k: float,
pressure_drop_pa: float,
) -> float:
if temperature_k <= 0.0:
raise ValueError("temperature_k must be positive")
if pressure_drop_pa <= 0.0:
return 0.0
return abs(dm1_g_s) * 1.0e-3 * sqrt(temperature_k) / pressure_drop_pa
def _pnvo_flow_parameter_diagnostic(
*,
closure: object,
amesim_results: AmesimResults,
state_vector: list[float],
orifice_alias: str,
time_s: float,
) -> TestMqlPnvoFlowParameterDiagnostic:
if orifice_alias != "pn_morifice_1":
raise KeyError(f"Unsupported PNVO flow parameter diagnostic: {orifice_alias}")
snapshot = closure.snapshot_at(time_s, state_vector).pneumatic
orifice = closure.pneumatic_closure.components.p4_port3_remote_orifice
if orifice.name != orifice_alias:
raise KeyError(f"Unexpected PNVO diagnostic orifice: {orifice.name}")
line_properties = snapshot.p4_port3_remote_orifice_line_port_2
boundary_properties = snapshot.p4_port3_remote_primary_line
upstream_properties = (
line_properties if line_properties.p >= boundary_properties.p else boundary_properties
)
python_mass_flow_kg_s = snapshot.p4_port3_remote_orifice_to_node_flow
denominator = (
orifice.flow_coefficient * orifice.effective_area * upstream_properties.p
)
python_cm = (
abs(python_mass_flow_kg_s) * sqrt(upstream_properties.T) / denominator
if denominator > 0.0 and upstream_properties.T > 0.0
else 0.0
)
def amesim_value(data_path: str) -> float:
return interpolate_series_value(
amesim_results.times,
amesim_results.series(data_path),
time_s,
)
amesim_cm = amesim_value(f"cm@{orifice_alias}")
cm_ratio = python_cm / amesim_cm if amesim_cm != 0.0 else 0.0
return TestMqlPnvoFlowParameterDiagnostic(
orifice_alias=orifice_alias,
time_s=time_s,
amesim_cm=amesim_cm,
amesim_dm2_g_s=amesim_value(f"dm2@{orifice_alias}"),
amesim_opening=amesim_value(f"xv@{orifice_alias}"),
amesim_gas_velocity_m_s=amesim_value(f"gasvel@{orifice_alias}"),
python_opening=orifice.opening,
python_flow_coefficient=orifice.flow_coefficient,
python_effective_area_m2=orifice.effective_area,
python_line_pressure_pa=line_properties.p,
python_boundary_pressure_pa=boundary_properties.p,
python_upstream_pressure_pa=upstream_properties.p,
python_upstream_temperature_k=upstream_properties.T,
python_mass_flow_kg_s=python_mass_flow_kg_s,
python_cm=python_cm,
python_to_amesim_cm_ratio=cm_ratio,
)
def run_test_mql_pnvo_event_window_diagnostic( def run_test_mql_pnvo_event_window_diagnostic(
config: TestMqlFullStateComparisonScriptConfig | None = None, config: TestMqlFullStateComparisonScriptConfig | None = None,
*, *,
@@ -679,6 +885,25 @@ def run_test_mql_pnvo_event_window_diagnostic(
time_s=sample_time, time_s=sample_time,
), ),
) )
pnl0001_pressure_loss_diagnostics = (
_pnl0001_pressure_loss_calibration_diagnostic(
closure=closure,
amesim_results=amesim_results,
python_values_by_data_path=python_sample_values,
line_alias="pneumatic_69",
chamber_alias="pn_c1_8",
time_s=sample_time,
),
)
pnvo_flow_parameter_diagnostics = (
_pnvo_flow_parameter_diagnostic(
closure=closure,
amesim_results=amesim_results,
state_vector=state_vector_by_sample_time[sample_time],
orifice_alias=orifice_alias,
time_s=sample_time,
),
)
sample_diagnostics.append( sample_diagnostics.append(
TestMqlPnvoEventWindowSampleDiagnostic( TestMqlPnvoEventWindowSampleDiagnostic(
time_s=sample_time, time_s=sample_time,
@@ -686,6 +911,8 @@ def run_test_mql_pnvo_event_window_diagnostic(
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,
pnl0001_rhs_diagnostics=pnl0001_rhs_diagnostics, pnl0001_rhs_diagnostics=pnl0001_rhs_diagnostics,
pnl0001_pressure_loss_diagnostics=pnl0001_pressure_loss_diagnostics,
pnvo_flow_parameter_diagnostics=pnvo_flow_parameter_diagnostics,
) )
) )
python_values = closure.data_path_values( python_values = closure.data_path_values(
@@ -749,6 +976,35 @@ def format_test_mql_pnvo_event_window_summary(
f"dm_dt={rhs.mass_derivative_kg_s}, " f"dm_dt={rhs.mass_derivative_kg_s}, "
f"dU_dt={rhs.energy_derivative_w}" f"dU_dt={rhs.energy_derivative_w}"
) )
for pressure_loss in sample.pnl0001_pressure_loss_diagnostics:
lines.append(
f" - pressure_loss@{pressure_loss.line_alias}: "
f"cm={pressure_loss.amesim_cm}, "
f"amesim_dm1={pressure_loss.amesim_dm1_g_s}, "
f"amesim_dp={pressure_loss.amesim_pressure_drop_pa}, "
f"darcy_dp={pressure_loss.current_darcy_pressure_drop_pa}, "
f"dp_multiplier={pressure_loss.pressure_drop_multiplier}, "
f"amesim_linear_k="
f"{pressure_loss.amesim_linear_conductance_kg_s_sqrt_k_per_pa}, "
f"python_linear_k="
f"{pressure_loss.python_linear_conductance_kg_s_sqrt_k_per_pa}, "
f"linear_k_ratio="
f"{pressure_loss.python_to_amesim_linear_conductance_ratio}"
)
for flow_parameter in sample.pnvo_flow_parameter_diagnostics:
lines.append(
f" - flow_parameter@{flow_parameter.orifice_alias}: "
f"amesim_cm={flow_parameter.amesim_cm}, "
f"python_cm={flow_parameter.python_cm}, "
f"cm_ratio={flow_parameter.python_to_amesim_cm_ratio}, "
f"amesim_dm2={flow_parameter.amesim_dm2_g_s}, "
f"python_m={flow_parameter.python_mass_flow_kg_s}, "
f"opening={flow_parameter.python_opening}, "
f"effective_area={flow_parameter.python_effective_area_m2}, "
f"upstream_p={flow_parameter.python_upstream_pressure_pa}, "
f"upstream_t={flow_parameter.python_upstream_temperature_k}, "
f"amesim_gasvel={flow_parameter.amesim_gas_velocity_m_s}"
)
lines.append("Final comparison:") lines.append("Final comparison:")
for data_path in diagnostic.data_paths: for data_path in diagnostic.data_paths:
lines.append( lines.append(
@@ -25,6 +25,9 @@ from PythonModels.systems.test_mql_line_parameters import (
) )
TEST_MQL_PNL0001_D20_L1_LINEAR_CONDUCTANCE = 5.5636e-6
@dataclass(frozen=True) @dataclass(frozen=True)
class TestMqlPnl0001Assembly: class TestMqlPnl0001Assembly:
specs: tuple[TestMqlPnl0001Spec, ...] specs: tuple[TestMqlPnl0001Spec, ...]
@@ -88,6 +91,9 @@ def build_test_mql_pnl0001_assembly(
polytropic_constant=spec.polytropic_constant, polytropic_constant=spec.polytropic_constant,
heat_transfer_coefficient=spec.heat_transfer_coefficient, heat_transfer_coefficient=spec.heat_transfer_coefficient,
external_temperature_k=spec.external_temperature_k, external_temperature_k=spec.external_temperature_k,
calibrated_linear_conductance=(
_test_mql_pnl0001_calibrated_linear_conductance(spec)
),
gas=gas, gas=gas,
p0=spec.initial_absolute_pressure_pa, p0=spec.initial_absolute_pressure_pa,
T0=spec.initial_temperature_k, T0=spec.initial_temperature_k,
@@ -97,6 +103,28 @@ def build_test_mql_pnl0001_assembly(
return TestMqlPnl0001Assembly(specs=specs, lines=lines) return TestMqlPnl0001Assembly(specs=specs, lines=lines)
def _test_mql_pnl0001_calibrated_linear_conductance(
spec: TestMqlPnl0001Spec,
) -> float | None:
if spec.target_component.startswith("pn_c1_") and _matches_geometry(
spec, diameter_mm=20.0, length_m=1.0
):
return TEST_MQL_PNL0001_D20_L1_LINEAR_CONDUCTANCE
return None
def _matches_geometry(
spec: TestMqlPnl0001Spec,
*,
diameter_mm: float,
length_m: float,
) -> bool:
return (
abs(spec.diameter_mm - diameter_mm) < 1.0e-12
and abs(spec.length_m - length_m) < 1.0e-12
)
def build_test_mql_pnl0002_assembly( def build_test_mql_pnl0002_assembly(
archive_path: str | Path, archive_path: str | Path,
*, *,
+11
View File
@@ -137,6 +137,17 @@ class AmesimPneumaticComponentsTest(unittest.TestCase):
self.assertAlmostEqual(derivative.m, 0.0) self.assertAlmostEqual(derivative.m, 0.0)
self.assertAlmostEqual(derivative.U, -props.p * 2.0e-6) self.assertAlmostEqual(derivative.U, -props.p * 2.0e-6)
def test_orifice_effective_area_clamps_opening(self) -> None:
orifice = AmesimPneumaticOrifice.from_mm2(
name="pn_variable_orifice",
area_mm2=78.5,
opening=1.4,
)
self.assertAlmostEqual(orifice.effective_area, 78.5e-6)
orifice.opening = -0.25
self.assertAlmostEqual(orifice.effective_area, 0.0)
def test_orifice_returns_signed_mass_flow(self) -> None: def test_orifice_returns_signed_mass_flow(self) -> None:
orifice = AmesimPneumaticOrifice.from_mm2( orifice = AmesimPneumaticOrifice.from_mm2(
name="pn_orifice_18", name="pn_orifice_18",
+30
View File
@@ -39,6 +39,26 @@ class AmesimPnl0001PipeTests(unittest.TestCase):
self.assertLess(reverse, 0.0) self.assertLess(reverse, 0.0)
self.assertAlmostEqual(abs(forward), abs(reverse), delta=abs(forward) * 0.01) self.assertAlmostEqual(abs(forward), abs(reverse), delta=abs(forward) * 0.01)
def test_calibrated_linear_conductance_overrides_darcy_flow(self) -> None:
pipe = AmesimPnl0001Pipe(
name="linear",
diameter_mm=20.0,
length_m=1.0,
relative_roughness=0.045 / 20.0,
calibrated_linear_conductance=5.5636e-6,
p0=200000.0,
T0=293.15,
)
flow = pipe.resistance_mass_flow(
port_1_pressure_pa=180000.0,
port_1_temperature_k=293.15,
)
self.assertAlmostEqual(
flow,
5.5636e-6 * (180000.0 - 200000.0) / (293.15 ** 0.5),
)
def test_diagnostics_reproduce_laminar_friction_contract(self) -> None: def test_diagnostics_reproduce_laminar_friction_contract(self) -> None:
diagnostics = self.pipe.diagnostics(mass_flow_kg_s=8.90603914774626e-6) diagnostics = self.pipe.diagnostics(mass_flow_kg_s=8.90603914774626e-6)
@@ -51,6 +71,16 @@ class AmesimPnl0001PipeTests(unittest.TestCase):
self.assertGreater(diagnostics.gas_velocity_m_s, 0.0) self.assertGreater(diagnostics.gas_velocity_m_s, 0.0)
self.assertGreater(diagnostics.pressure_drop_pa, 0.0) self.assertGreater(diagnostics.pressure_drop_pa, 0.0)
def test_darcy_pressure_drop_for_state_uses_supplied_density_state(self) -> None:
diagnostics = self.pipe.diagnostics(mass_flow_kg_s=8.90603914774626e-6)
pressure_drop = self.pipe.darcy_pressure_drop_for_state(
mass_flow_kg_s=8.90603914774626e-6,
pressure_pa=self.pipe.properties().p,
temperature_k=self.pipe.properties().T,
)
self.assertAlmostEqual(pressure_drop, diagnostics.pressure_drop_pa)
def test_connection_derivative_preserves_mass_and_stream_direction(self) -> None: def test_connection_derivative_preserves_mass_and_stream_direction(self) -> None:
internal = self.pipe.properties() internal = self.pipe.properties()
derivative = self.pipe.derivatives_from_connections( derivative = self.pipe.derivatives_from_connections(
@@ -11,9 +11,11 @@ from PythonModels.scripts.run_test_mql_full_state_comparison import (
TestMqlFullStateComparisonExecutionConfig, TestMqlFullStateComparisonExecutionConfig,
TestMqlFullStateComparisonPathConfig, TestMqlFullStateComparisonPathConfig,
TestMqlFullStateComparisonScriptConfig, TestMqlFullStateComparisonScriptConfig,
TestMqlPnl0001PressureLossCalibrationDiagnostic,
TestMqlPnvoEventBoundaryDiagnostic, TestMqlPnvoEventBoundaryDiagnostic,
TestMqlPnvoEventWindowDiagnostic, TestMqlPnvoEventWindowDiagnostic,
TestMqlPnvoEventWindowSampleDiagnostic, TestMqlPnvoEventWindowSampleDiagnostic,
TestMqlPnvoFlowParameterDiagnostic,
TestMqlPnvoEventWindowSegmentDiagnostic, TestMqlPnvoEventWindowSegmentDiagnostic,
format_test_mql_full_state_comparison_summary, format_test_mql_full_state_comparison_summary,
format_test_mql_pnvo_event_boundary_summary, format_test_mql_pnvo_event_boundary_summary,
@@ -241,6 +243,45 @@ class RunTestMqlFullStateComparisonScriptTests(unittest.TestCase):
energy_derivative_w=-20000.0, energy_derivative_w=-20000.0,
), ),
), ),
pnl0001_pressure_loss_diagnostics=(
TestMqlPnl0001PressureLossCalibrationDiagnostic(
line_alias="pneumatic_69",
chamber_alias="pn_c1_8",
time_s=0.05,
amesim_cm=0.0159,
amesim_dm1_g_s=419.9,
amesim_mass_flow_magnitude_kg_s=0.4199,
amesim_line_gauge_pressure_pa=1484689.6,
amesim_chamber_gauge_pressure_pa=187060.0,
amesim_line_temperature_k=296.1,
amesim_pressure_drop_pa=1297629.6,
current_darcy_pressure_drop_pa=422633.2,
pressure_drop_multiplier=3.07,
amesim_linear_conductance_kg_s_sqrt_k_per_pa=5.56e-6,
python_linear_conductance_kg_s_sqrt_k_per_pa=1.06e-5,
python_to_amesim_linear_conductance_ratio=1.91,
),
),
pnvo_flow_parameter_diagnostics=(
TestMqlPnvoFlowParameterDiagnostic(
orifice_alias="pn_morifice_1",
time_s=0.05,
amesim_cm=0.0158,
amesim_dm2_g_s=456.8,
amesim_opening=1.0,
amesim_gas_velocity_m_s=885.9,
python_opening=1.0,
python_flow_coefficient=0.45,
python_effective_area_m2=78.5e-6,
python_line_pressure_pa=15310000.0,
python_boundary_pressure_pa=1586000.0,
python_upstream_pressure_pa=15310000.0,
python_upstream_temperature_k=293.15,
python_mass_flow_kg_s=0.4637,
python_cm=0.0147,
python_to_amesim_cm_ratio=0.93,
),
),
) )
diagnostic = TestMqlPnvoEventWindowDiagnostic( diagnostic = TestMqlPnvoEventWindowDiagnostic(
orifice_alias="pn_morifice_1", orifice_alias="pn_morifice_1",
@@ -275,6 +316,17 @@ class RunTestMqlFullStateComparisonScriptTests(unittest.TestCase):
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)
self.assertIn("pressure_loss@pneumatic_69", summary)
self.assertIn("cm=0.0159", summary)
self.assertIn("dp_multiplier=3.07", summary)
self.assertIn("amesim_linear_k=5.56e-06", summary)
self.assertIn("python_linear_k=1.06e-05", summary)
self.assertIn("linear_k_ratio=1.91", summary)
self.assertIn("flow_parameter@pn_morifice_1", summary)
self.assertIn("amesim_cm=0.0158", summary)
self.assertIn("python_cm=0.0147", summary)
self.assertIn("cm_ratio=0.93", summary)
self.assertIn("amesim_gasvel=885.9", summary)
self.assertIn("Final comparison:", summary) self.assertIn("Final comparison:", summary)
self.assertTrue(summary.endswith("\n")) self.assertTrue(summary.endswith("\n"))
+33
View File
@@ -6,6 +6,7 @@ from pathlib import Path
from PythonModels.reporting.amesim_results import load_test_mql_amesim_results from PythonModels.reporting.amesim_results import load_test_mql_amesim_results
from PythonModels.systems.test_mql_line_parameters import load_test_mql_pnl0001_specs from PythonModels.systems.test_mql_line_parameters import load_test_mql_pnl0001_specs
from PythonModels.systems.test_mql_pneumatic_lines import ( from PythonModels.systems.test_mql_pneumatic_lines import (
TEST_MQL_PNL0001_D20_L1_LINEAR_CONDUCTANCE,
build_test_mql_pnl0001_assembly, build_test_mql_pnl0001_assembly,
) )
@@ -44,6 +45,38 @@ class TestMqlPnl0001Tests(unittest.TestCase):
all(len(line.get_state_vector()) == 2 for line in self.assembly.lines.values()) all(len(line.get_state_vector()) == 2 for line in self.assembly.lines.values())
) )
def test_calibrates_primary_d20_chamber_lines_only(self) -> None:
calibrated_aliases = {
alias
for alias, line in self.assembly.lines.items()
if line.calibrated_linear_conductance is not None
}
self.assertEqual(
calibrated_aliases,
{
"pneumatic_65",
"pneumatic_66",
"pneumatic_68",
"pneumatic_69",
"pneumatic_71",
"pneumatic_72",
"pneumatic_73",
"pneumatic_74",
},
)
for alias in calibrated_aliases:
self.assertAlmostEqual(
self.assembly.lines[alias].calibrated_linear_conductance,
TEST_MQL_PNL0001_D20_L1_LINEAR_CONDUCTANCE,
)
self.assertIsNone(
self.assembly.lines["pneumatic_70"].calibrated_linear_conductance
)
self.assertIsNone(
self.assembly.lines["pneumatic_96"].calibrated_linear_conductance
)
def test_pneumatic_96_initial_observables_match_amesim_baseline(self) -> None: def test_pneumatic_96_initial_observables_match_amesim_baseline(self) -> None:
pipe = self.assembly.lines["pneumatic_96"] pipe = self.assembly.lines["pneumatic_96"]