diff --git a/PythonModels/systems/test_mql.py b/PythonModels/systems/test_mql.py index 8e80c59..a8635ad 100644 --- a/PythonModels/systems/test_mql.py +++ b/PythonModels/systems/test_mql.py @@ -3866,6 +3866,17 @@ _PISTON_FORCE_BINDINGS = ( ("mass_friction_endstops_17", "pn_brp2_15", "p4_port3_far_primary_chamber"), ) +_ELASTIC_ENDSTOP_FORCE_BINDINGS = ( + ("mass_friction_endstops_10", "mass_friction_endstops_18", "elasticendstop_8"), + ("mass_friction_endstops_11", "mass_friction_endstops_18", "elasticendstop_9"), + ("mass_friction_endstops_12", "mass_friction_endstops_18", "elasticendstop_10"), + ("mass_friction_endstops_13", "mass_friction_endstops_18", "elasticendstop_11"), + ("mass_friction_endstops_14", "mass_friction_endstops_18", "elasticendstop_12"), + ("mass_friction_endstops_15", "mass_friction_endstops_18", "elasticendstop_13"), + ("mass_friction_endstops_16", "mass_friction_endstops_18", "elasticendstop_14"), + ("mass_friction_endstops_17", "mass_friction_endstops_18", "elasticendstop_15"), +) + class TestMqlFullStateClosure: def __init__(self, *, pneumatic_closure: object, mechanical_closure: object) -> None: @@ -3903,15 +3914,23 @@ class TestMqlFullStateClosure: def rhs(self, state_vector: list[float]) -> list[float]: pneumatic_state, mechanical_state = self._split_state(list(state_vector)) pneumatic_snapshot = self.pneumatic_closure.snapshot(pneumatic_state) + mechanical_snapshot = self.mechanical_closure.snapshot(mechanical_state) return [ *self.pneumatic_closure.rhs(pneumatic_state), *self.mechanical_closure.rhs( mechanical_state, - force_by_mass_alias=self._piston_force_by_mass_alias(pneumatic_snapshot), + force_by_mass_alias=self._force_by_mass_alias( + pneumatic_snapshot, + mechanical_snapshot, + ), ), ] - def _piston_force_by_mass_alias(self, pneumatic_snapshot: object) -> dict[str, float]: + def _force_by_mass_alias( + self, + pneumatic_snapshot: object, + mechanical_snapshot: object, + ) -> dict[str, float]: from PythonModels.systems.test_mql_pneumatic import pressure_to_amesim_gauge_pa force_by_mass_alias: dict[str, float] = {} @@ -3921,6 +3940,22 @@ class TestMqlFullStateClosure: force_by_mass_alias[mass_alias] = ( pressure_to_amesim_gauge_pa(chamber.p) * piston.annulus_area_m2 ) + + state_by_alias = {state.alias: state for state in mechanical_snapshot.states} + for mass_alias, support_mass_alias, endstop_alias in _ELASTIC_ENDSTOP_FORCE_BINDINGS: + mass_state = state_by_alias[mass_alias] + support_state = state_by_alias[support_mass_alias] + endstop = self.mechanical_closure.assembly.elastic_endstops[ + endstop_alias + ].endstop() + gap_mm = (support_state.displacement_m - mass_state.displacement_m) * 1.0e3 + penetration_velocity_m_s = ( + mass_state.velocity_m_s - support_state.velocity_m_s + ) + force_by_mass_alias[mass_alias] = ( + force_by_mass_alias.get(mass_alias, 0.0) + - endstop.contact_force(gap_mm, penetration_velocity_m_s) + ) return force_by_mass_alias diff --git a/tests/test_test_mql_pnl0001_segment.py b/tests/test_test_mql_pnl0001_segment.py index a8f942f..5ed27ec 100644 --- a/tests/test_test_mql_pnl0001_segment.py +++ b/tests/test_test_mql_pnl0001_segment.py @@ -554,6 +554,30 @@ class TestMqlPn3P4NodeChamberSegmentTests(unittest.TestCase): ) self.assertAlmostEqual(rhs[112], -0.8167936695810917) + def test_full_state_closure_applies_elastic_endstop_force_to_small_masses(self) -> None: + closure = self.system.full_state_closure_from_spec( + self.spec, + inlet_node_pressure_pa=15.31e6, + resistance_boundary_pressure_pa=15.29e6, + ) + state = closure.initial_state_vector() + state[113] = 1.0e-3 + + rhs = closure.rhs(state) + initial_rhs = closure.rhs(closure.initial_state_vector()) + endstop = closure.mechanical_closure.assembly.elastic_endstops[ + "elasticendstop_8" + ].endstop() + expected_contact_force = endstop.contact_force(-1.0, 0.0) + mass = closure.mechanical_closure.assembly.masses[ + "mass_friction_endstops_10" + ].mass_kg + + self.assertAlmostEqual( + rhs[112], + initial_rhs[112] - expected_contact_force / mass, + ) + def test_simulates_full_132_state_closure(self) -> None: solution = self.system.simulate_full_state_from_spec( self.spec,