diff --git a/PythonModels/components/amesim_pneumatic_line.py b/PythonModels/components/amesim_pneumatic_line.py index ab9c92d..bcd4b16 100644 --- a/PythonModels/components/amesim_pneumatic_line.py +++ b/PythonModels/components/amesim_pneumatic_line.py @@ -6,6 +6,7 @@ from math import log10, pi, sqrt from PythonModels.components.amesim_pneumatic import ( HELIUM_PNEUMATIC_GAS, AmesimPneumaticGas, + compressible_orifice_mass_flow, diameter_mm_to_area_m2, ) from PythonModels.core.base import AlgebraicComponent, DynamicComponent @@ -60,6 +61,61 @@ class _DarcyPipeResistanceMixin: upper = middle return 0.5 * (lower + upper) + def pn2pipefr_mass_flow( + self, + *, + port_1_pressure_pa: float, + port_1_temperature_k: float, + port_2_pressure_pa: float, + port_2_temperature_k: float, + length: float | None = None, + ) -> float: + pressure_difference = port_1_pressure_pa - port_2_pressure_pa + if pressure_difference == 0.0: + return 0.0 + upstream_pressure = max(port_1_pressure_pa, port_2_pressure_pa) + downstream_pressure = min(port_1_pressure_pa, port_2_pressure_pa) + upstream_temperature = ( + port_1_temperature_k + if pressure_difference > 0.0 + else port_2_temperature_k + ) + resistance_length = self.length if length is None else length + if resistance_length <= 0.0: + raise ValueError("length must be positive") + + def target_flow(mass_flow_kg_s: float) -> float: + reynolds = self._reynolds_number(mass_flow_kg_s, upstream_temperature) + friction_factor = self._friction_factor(reynolds) + flow_coefficient = sqrt( + self.diameter / (resistance_length * friction_factor) + ) + return compressible_orifice_mass_flow( + upstream_pressure=upstream_pressure, + downstream_pressure=downstream_pressure, + upstream_temperature=upstream_temperature, + area=self.area, + flow_coefficient=flow_coefficient, + gas=self.gas, + ) + + flow_coefficient = sqrt(self.diameter / (resistance_length * 0.02)) + magnitude = compressible_orifice_mass_flow( + upstream_pressure=upstream_pressure, + downstream_pressure=downstream_pressure, + upstream_temperature=upstream_temperature, + area=self.area, + flow_coefficient=flow_coefficient, + gas=self.gas, + ) + for _ in range(12): + next_magnitude = target_flow(magnitude) + if abs(next_magnitude - magnitude) <= max(1.0e-12, abs(magnitude) * 1.0e-9): + magnitude = next_magnitude + break + magnitude = 0.5 * (magnitude + next_magnitude) + return magnitude if pressure_difference > 0.0 else -magnitude + def _darcy_pressure_drop( self, mass_flow_kg_s: float, diff --git a/PythonModels/scripts/run_test_mql_full_state_comparison.py b/PythonModels/scripts/run_test_mql_full_state_comparison.py index 8f438c9..42b86fe 100644 --- a/PythonModels/scripts/run_test_mql_full_state_comparison.py +++ b/PythonModels/scripts/run_test_mql_full_state_comparison.py @@ -280,6 +280,8 @@ class TestMqlPnl0001PressureLossCalibrationDiagnostic: amesim_pressure_drop_pa: float current_darcy_pressure_drop_pa: float pressure_drop_multiplier: float + candidate_pn2pipefr_dm1_g_s: float + candidate_pn2pipefr_to_amesim_dm1_ratio: 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 @@ -580,6 +582,7 @@ def _pnl0001_pressure_loss_calibration_diagnostic( 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}") + amesim_chamber_temperature_k = amesim_value(f"temp@{chamber_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 @@ -601,6 +604,25 @@ def _pnl0001_pressure_loss_calibration_diagnostic( pressure_drop_multiplier = ( float("inf") if amesim_pressure_drop_pa > 0.0 else 1.0 ) + candidate_port1_to_port2_kg_s = line.pn2pipefr_mass_flow( + port_1_pressure_pa=( + amesim_chamber_gauge_pressure_pa + AMESIM_REFERENCE_PRESSURE_PA + ), + port_1_temperature_k=amesim_chamber_temperature_k, + port_2_pressure_pa=( + amesim_line_gauge_pressure_pa + AMESIM_REFERENCE_PRESSURE_PA + ), + port_2_temperature_k=amesim_line_temperature_k, + ) + candidate_pn2pipefr_dm1_g_s = -candidate_port1_to_port2_kg_s * 1.0e3 + if amesim_dm1_g_s != 0.0: + candidate_pn2pipefr_to_amesim_dm1_ratio = ( + candidate_pn2pipefr_dm1_g_s / amesim_dm1_g_s + ) + else: + candidate_pn2pipefr_to_amesim_dm1_ratio = ( + float("inf") if candidate_pn2pipefr_dm1_g_s != 0.0 else 1.0 + ) amesim_linear_conductance = _pnl0001_linear_conductance( dm1_g_s=amesim_dm1_g_s, temperature_k=amesim_line_temperature_k, @@ -634,6 +656,10 @@ def _pnl0001_pressure_loss_calibration_diagnostic( amesim_pressure_drop_pa=amesim_pressure_drop_pa, current_darcy_pressure_drop_pa=current_darcy_pressure_drop_pa, pressure_drop_multiplier=pressure_drop_multiplier, + candidate_pn2pipefr_dm1_g_s=candidate_pn2pipefr_dm1_g_s, + candidate_pn2pipefr_to_amesim_dm1_ratio=( + candidate_pn2pipefr_to_amesim_dm1_ratio + ), amesim_linear_conductance_kg_s_sqrt_k_per_pa=( amesim_linear_conductance ), @@ -984,6 +1010,10 @@ def format_test_mql_pnvo_event_window_summary( 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"candidate_pn2pipefr_dm1=" + f"{pressure_loss.candidate_pn2pipefr_dm1_g_s}, " + f"candidate_pn2pipefr_ratio=" + f"{pressure_loss.candidate_pn2pipefr_to_amesim_dm1_ratio}, " f"amesim_linear_k=" f"{pressure_loss.amesim_linear_conductance_kg_s_sqrt_k_per_pa}, " f"python_linear_k=" diff --git a/tests/test_amesim_pnl0001_pipe.py b/tests/test_amesim_pnl0001_pipe.py index 1bf4c27..dc79fc4 100644 --- a/tests/test_amesim_pnl0001_pipe.py +++ b/tests/test_amesim_pnl0001_pipe.py @@ -39,6 +39,24 @@ class AmesimPnl0001PipeTests(unittest.TestCase): self.assertLess(reverse, 0.0) self.assertAlmostEqual(abs(forward), abs(reverse), delta=abs(forward) * 0.01) + def test_pn2pipefr_candidate_flow_follows_pressure_gradient(self) -> None: + forward = self.pipe.pn2pipefr_mass_flow( + port_1_pressure_pa=15.31e6, + port_1_temperature_k=293.15, + port_2_pressure_pa=15.3e6, + port_2_temperature_k=293.15, + ) + reverse = self.pipe.pn2pipefr_mass_flow( + port_1_pressure_pa=15.29e6, + port_1_temperature_k=293.15, + port_2_pressure_pa=15.3e6, + port_2_temperature_k=293.15, + ) + + self.assertGreater(forward, 0.0) + self.assertLess(reverse, 0.0) + 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", diff --git a/tests/test_run_test_mql_full_state_comparison.py b/tests/test_run_test_mql_full_state_comparison.py index 3b19c65..ce1cd42 100644 --- a/tests/test_run_test_mql_full_state_comparison.py +++ b/tests/test_run_test_mql_full_state_comparison.py @@ -257,6 +257,8 @@ class RunTestMqlFullStateComparisonScriptTests(unittest.TestCase): amesim_pressure_drop_pa=1297629.6, current_darcy_pressure_drop_pa=422633.2, pressure_drop_multiplier=3.07, + candidate_pn2pipefr_dm1_g_s=424.2, + candidate_pn2pipefr_to_amesim_dm1_ratio=1.01, 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, @@ -319,6 +321,8 @@ class RunTestMqlFullStateComparisonScriptTests(unittest.TestCase): self.assertIn("pressure_loss@pneumatic_69", summary) self.assertIn("cm=0.0159", summary) self.assertIn("dp_multiplier=3.07", summary) + self.assertIn("candidate_pn2pipefr_dm1=424.2", summary) + self.assertIn("candidate_pn2pipefr_ratio=1.01", 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)