diff --git a/PythonModels/systems/test_mql_mechanical.py b/PythonModels/systems/test_mql_mechanical.py index 38d9271..9c52ee1 100644 --- a/PythonModels/systems/test_mql_mechanical.py +++ b/PythonModels/systems/test_mql_mechanical.py @@ -153,9 +153,17 @@ class TestMqlMechanicalMassState: return [self.velocity_m_s, self.displacement_m] +@dataclass(frozen=True) +class TestMqlMechanicalNodeKinematics: + alias: str + velocities_m_s: dict[int, float] + displacements_m: dict[int, float] + + @dataclass(frozen=True) class TestMqlMechanicalMassSnapshot: states: tuple[TestMqlMechanicalMassState, ...] + node_kinematics_by_alias: dict[str, TestMqlMechanicalNodeKinematics] @property def state_count(self) -> int: @@ -186,7 +194,30 @@ class TestMqlMechanicalMassClosure: ) for index, alias in enumerate(self.mass_aliases) ) - return TestMqlMechanicalMassSnapshot(states=states) + return TestMqlMechanicalMassSnapshot( + states=states, + node_kinematics_by_alias=self._node_kinematics_by_alias(states), + ) + + def _node_kinematics_by_alias( + self, + states: tuple[TestMqlMechanicalMassState, ...], + ) -> dict[str, TestMqlMechanicalNodeKinematics]: + state_by_alias = {state.alias: state for state in states} + front = state_by_alias["mass_friction_endstops_18"] + rear = state_by_alias["mass_friction_endstops_19"] + return { + "dynamic_mechanical_node_alternative_2": TestMqlMechanicalNodeKinematics( + alias="dynamic_mechanical_node_alternative_2", + velocities_m_s={port: -front.velocity_m_s for port in range(1, 9)}, + displacements_m={port: -front.displacement_m for port in range(1, 9)}, + ), + "dynamic_mechanical_node_alternative_3": TestMqlMechanicalNodeKinematics( + alias="dynamic_mechanical_node_alternative_3", + velocities_m_s={port: rear.velocity_m_s for port in range(1, 9)}, + displacements_m={port: rear.displacement_m for port in range(1, 9)}, + ), + } def rhs( self, diff --git a/tests/test_test_mql_mechanical.py b/tests/test_test_mql_mechanical.py index 9831a73..5e1da5f 100644 --- a/tests/test_test_mql_mechanical.py +++ b/tests/test_test_mql_mechanical.py @@ -63,6 +63,31 @@ class TestMqlMechanicalAssemblyTests(unittest.TestCase): for index, mass_state in enumerate(snapshot.states): self.assertAlmostEqual(rhs[2 * index + 1], mass_state.velocity_m_s) + def test_mechanical_mass_snapshot_exposes_lmechan1_node_kinematics(self) -> None: + closure = TestMqlMechanicalMassClosure(self.assembly) + state = closure.initial_state_vector() + state[16] = 0.2 + state[17] = 0.37 + state[18] = -0.3 + state[19] = -0.72 + + snapshot = closure.snapshot(state) + front = snapshot.node_kinematics_by_alias[ + "dynamic_mechanical_node_alternative_2" + ] + rear = snapshot.node_kinematics_by_alias[ + "dynamic_mechanical_node_alternative_3" + ] + + self.assertEqual(set(front.velocities_m_s), set(range(1, 9))) + self.assertEqual(set(front.displacements_m), set(range(1, 9))) + self.assertEqual(set(rear.velocities_m_s), set(range(1, 9))) + self.assertEqual(set(rear.displacements_m), set(range(1, 9))) + self.assertAlmostEqual(front.velocities_m_s[1], -0.2) + self.assertAlmostEqual(front.displacements_m[8], -0.37) + self.assertAlmostEqual(rear.velocities_m_s[1], -0.3) + self.assertAlmostEqual(rear.displacements_m[8], -0.72) + def test_system_simulates_mechanical_mass_state_closure(self) -> None: system = TestMqlSystem()