接入AMESim help文档与PNVO流量诊断
This commit is contained in:
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.
@@ -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,
|
||||||
*,
|
*,
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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"))
|
||||||
|
|
||||||
|
|||||||
@@ -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"]
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user