254 lines
8.6 KiB
Python
254 lines
8.6 KiB
Python
from __future__ import annotations
|
|
|
|
import unittest
|
|
|
|
from app.simulation.examples.test_mql.primitives.pneumatic import (
|
|
HELIUM_PNEUMATIC_GAS,
|
|
AmesimPneumaticGas,
|
|
AmesimPneumaticOrifice,
|
|
AmesimPneumaticVolume,
|
|
AmesimVariablePneumaticVolume,
|
|
cm3_to_m3,
|
|
compressible_orifice_mass_flow,
|
|
diameter_mm_to_area_m2,
|
|
kg_to_g,
|
|
liters_to_m3,
|
|
m3_to_cm3,
|
|
mm2_to_m2,
|
|
)
|
|
|
|
|
|
class AmesimPneumaticComponentsTest(unittest.TestCase):
|
|
def test_unit_conversions(self) -> None:
|
|
self.assertAlmostEqual(liters_to_m3(15.0), 0.015)
|
|
self.assertAlmostEqual(m3_to_cm3(0.015), 15000.0)
|
|
self.assertAlmostEqual(cm3_to_m3(15000.0), 0.015)
|
|
self.assertAlmostEqual(kg_to_g(1.25), 1250.0)
|
|
self.assertAlmostEqual(mm2_to_m2(78.5), 78.5e-6)
|
|
self.assertAlmostEqual(diameter_mm_to_area_m2(10.0), 7.853981633974483e-5)
|
|
|
|
def test_reference_enthalpy_uses_amesim_operating_temperature_baseline(self) -> None:
|
|
gas = AmesimPneumaticGas(cp=5193.0, cv=3116.0)
|
|
|
|
self.assertAlmostEqual(gas.specific_reference_enthalpy(298.15), 0.0)
|
|
self.assertAlmostEqual(
|
|
gas.specific_reference_enthalpy(290.2036),
|
|
5193.0 * (290.2036 - 298.15),
|
|
)
|
|
self.assertAlmostEqual(
|
|
gas.reference_temperature_from_specific_enthalpy(
|
|
gas.specific_reference_enthalpy(287.7322),
|
|
),
|
|
287.7322,
|
|
)
|
|
|
|
def test_pressure_reference_enthalpy_includes_real_gas_departure(self) -> None:
|
|
gas = AmesimPneumaticGas(cp=5193.0, cv=3116.0)
|
|
|
|
ideal_reference_h = gas.specific_reference_enthalpy(287.7322)
|
|
pressure_reference_h = gas.pressure_reference_enthalpy(13_839_965.0, 287.7322)
|
|
|
|
self.assertAlmostEqual(ideal_reference_h, -54099.6354, delta=0.001)
|
|
self.assertAlmostEqual(
|
|
pressure_reference_h - ideal_reference_h,
|
|
11936.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:
|
|
gas = AmesimPneumaticGas(cp=0.0, cv=3116.0)
|
|
|
|
with self.assertRaisesRegex(ValueError, "cp must be positive"):
|
|
gas.reference_temperature_from_specific_enthalpy(42.0)
|
|
|
|
def test_volume_initial_state_matches_requested_pressure_temperature(self) -> None:
|
|
volume = AmesimPneumaticVolume.from_liters(
|
|
name="pn_general_chamber",
|
|
volume_liters=57.0,
|
|
p0=15.3e6,
|
|
T0=293.15,
|
|
)
|
|
props = volume.properties()
|
|
|
|
self.assertAlmostEqual(props.p, 15.3e6, delta=15.3e6 * 1.0e-12)
|
|
self.assertAlmostEqual(props.T, 293.15)
|
|
self.assertGreater(props.rho, 20.0)
|
|
self.assertLess(props.rho, 30.0)
|
|
|
|
def test_volume_reports_amesim_style_observables(self) -> None:
|
|
volume = AmesimPneumaticVolume.from_liters(
|
|
name="pn_general_chamber",
|
|
volume_liters=57.0,
|
|
p0=15.3e6,
|
|
T0=293.15,
|
|
)
|
|
|
|
self.assertAlmostEqual(volume.volume_cm3(), 57000.0)
|
|
self.assertAlmostEqual(
|
|
volume.pressure_gauge_pa(reference_pressure_pa=101_300.0),
|
|
15198700.0,
|
|
)
|
|
self.assertGreater(volume.gas_mass_g(), 1300.0)
|
|
|
|
def test_volume_balances_two_connections_with_stream_enthalpy(self) -> None:
|
|
volume = AmesimPneumaticVolume.from_liters(
|
|
name="pn_general_chamber",
|
|
volume_liters=57.0,
|
|
p0=15.3e6,
|
|
T0=293.15,
|
|
)
|
|
internal_h = volume.properties().h
|
|
|
|
derivative = volume.derivatives_from_two_connections(
|
|
port_a_m_flow=0.2,
|
|
connected_h_a=internal_h + 1000.0,
|
|
port_b_m_flow=-0.1,
|
|
connected_h_b=internal_h - 1000.0,
|
|
internal_h=internal_h,
|
|
)
|
|
|
|
self.assertAlmostEqual(derivative.m, 0.1)
|
|
self.assertAlmostEqual(
|
|
derivative.U,
|
|
0.2 * (internal_h + 1000.0) - 0.1 * internal_h,
|
|
)
|
|
self.assertAlmostEqual(volume.port_a.p, volume.port_b.p)
|
|
|
|
def test_volume_applies_amesim_heat_exchange_to_energy_derivative(self) -> None:
|
|
volume = AmesimPneumaticVolume.from_liters(
|
|
name="heated_chamber",
|
|
volume_liters=15.0,
|
|
p0=100000.0,
|
|
T0=300.0,
|
|
heat_transfer_coefficient=1500.0,
|
|
heat_transfer_area=0.7,
|
|
external_temperature_k=293.15,
|
|
)
|
|
props = volume.properties()
|
|
|
|
derivative = volume.derivatives_from_two_connections(
|
|
port_a_m_flow=0.0,
|
|
connected_h_a=props.h,
|
|
port_b_m_flow=0.0,
|
|
connected_h_b=props.h,
|
|
internal_h=props.h,
|
|
)
|
|
|
|
self.assertAlmostEqual(
|
|
derivative.U,
|
|
1500.0 * 0.7 * (293.15 - 300.0),
|
|
)
|
|
|
|
def test_variable_volume_tracks_external_volume(self) -> None:
|
|
volume = AmesimVariablePneumaticVolume.from_liters(
|
|
name="pn_c1_8",
|
|
dead_volume_liters=15.0,
|
|
p0=100000.0,
|
|
T0=293.15,
|
|
)
|
|
|
|
self.assertAlmostEqual(volume.volume_cm3(), 15000.0)
|
|
volume.set_external_volume_m3(cm3_to_m3(34242.54636512914))
|
|
self.assertAlmostEqual(volume.volume_cm3(), 49242.54636512914)
|
|
|
|
def test_variable_volume_boundary_work_uses_external_volume_rate(self) -> None:
|
|
volume = AmesimVariablePneumaticVolume.from_liters(
|
|
name="pn_c1_8",
|
|
dead_volume_liters=15.0,
|
|
p0=100000.0,
|
|
T0=293.15,
|
|
)
|
|
props = volume.properties()
|
|
volume.set_external_volume_m3(0.0, external_volume_rate_m3_s=2.0e-6)
|
|
|
|
derivative = volume.derivatives_from_two_connections(
|
|
port_a_m_flow=0.0,
|
|
connected_h_a=props.h,
|
|
port_b_m_flow=0.0,
|
|
connected_h_b=props.h,
|
|
internal_h=props.h,
|
|
)
|
|
|
|
self.assertAlmostEqual(derivative.m, 0.0)
|
|
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:
|
|
orifice = AmesimPneumaticOrifice.from_mm2(
|
|
name="pn_orifice_18",
|
|
area_mm2=78.5,
|
|
flow_coefficient=0.9,
|
|
)
|
|
|
|
forward = orifice.mass_flow(15.3e6, 1.0e6, 293.15)
|
|
reverse = orifice.mass_flow(1.0e6, 15.3e6, 293.15)
|
|
|
|
self.assertGreater(forward, 0.0)
|
|
self.assertAlmostEqual(reverse, -forward)
|
|
self.assertEqual(orifice.mass_flow(1.0e6, 1.0e6, 293.15), 0.0)
|
|
|
|
def test_choked_flow_is_independent_of_lower_downstream_pressure(self) -> None:
|
|
base = compressible_orifice_mass_flow(
|
|
upstream_pressure=15.3e6,
|
|
downstream_pressure=1.0e6,
|
|
upstream_temperature=293.15,
|
|
area=78.5e-6,
|
|
flow_coefficient=0.9,
|
|
gas=HELIUM_PNEUMATIC_GAS,
|
|
)
|
|
lower_back_pressure = compressible_orifice_mass_flow(
|
|
upstream_pressure=15.3e6,
|
|
downstream_pressure=0.1e6,
|
|
upstream_temperature=293.15,
|
|
area=78.5e-6,
|
|
flow_coefficient=0.9,
|
|
gas=HELIUM_PNEUMATIC_GAS,
|
|
)
|
|
|
|
self.assertGreater(base, 0.0)
|
|
self.assertAlmostEqual(lower_back_pressure, base)
|
|
|
|
def test_subcritical_flow_decreases_as_back_pressure_rises(self) -> None:
|
|
low_back_pressure = compressible_orifice_mass_flow(
|
|
upstream_pressure=1.0e6,
|
|
downstream_pressure=0.6e6,
|
|
upstream_temperature=293.15,
|
|
area=78.5e-6,
|
|
flow_coefficient=0.9,
|
|
)
|
|
high_back_pressure = compressible_orifice_mass_flow(
|
|
upstream_pressure=1.0e6,
|
|
downstream_pressure=0.9e6,
|
|
upstream_temperature=293.15,
|
|
area=78.5e-6,
|
|
flow_coefficient=0.9,
|
|
)
|
|
|
|
self.assertGreater(low_back_pressure, high_back_pressure)
|
|
self.assertGreater(high_back_pressure, 0.0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|