补齐test_mql机械观测覆盖
This commit is contained in:
1 parent
f6871aefac
commit
c84a809f77
2 files changed
+203
-2
No files matched your search
@@ -50,6 +50,26 @@ class TestMqlElasticEndstopObservation:
|
||||
stiffness_n_m: float
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TestMqlForceSourceObservation:
|
||||
time: float
|
||||
force_n: float
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TestMqlForceConnectorObservation:
|
||||
time: float
|
||||
force_n: float
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TestMqlMechanicalNodeObservation:
|
||||
time: float
|
||||
velocities_m_s: dict[int, float]
|
||||
displacements_m: dict[int, float]
|
||||
total_force_n: float
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TestMqlPistonObservationBinding:
|
||||
alias: str
|
||||
@@ -125,15 +145,71 @@ class TestMqlElasticEndstopObservationBinding:
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TestMqlForceSourceObservationBinding:
|
||||
alias: str
|
||||
force_path: str
|
||||
|
||||
def observation_at(self, results: AmesimResults, index: int) -> TestMqlForceSourceObservation:
|
||||
return TestMqlForceSourceObservation(
|
||||
time=results.times[index],
|
||||
force_n=results.series(self.force_path)[index],
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TestMqlForceConnectorObservationBinding:
|
||||
alias: str
|
||||
force_path: str
|
||||
|
||||
def observation_at(self, results: AmesimResults, index: int) -> TestMqlForceConnectorObservation:
|
||||
return TestMqlForceConnectorObservation(
|
||||
time=results.times[index],
|
||||
force_n=results.series(self.force_path)[index],
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TestMqlMechanicalNodeObservationBinding:
|
||||
alias: str
|
||||
velocity_paths_by_port: dict[int, str]
|
||||
displacement_paths_by_port: dict[int, str]
|
||||
total_force_path: str
|
||||
|
||||
def observation_at(self, results: AmesimResults, index: int) -> TestMqlMechanicalNodeObservation:
|
||||
return TestMqlMechanicalNodeObservation(
|
||||
time=results.times[index],
|
||||
velocities_m_s={
|
||||
port: results.series(path)[index]
|
||||
for port, path in self.velocity_paths_by_port.items()
|
||||
},
|
||||
displacements_m={
|
||||
port: results.series(path)[index]
|
||||
for port, path in self.displacement_paths_by_port.items()
|
||||
},
|
||||
total_force_n=results.series(self.total_force_path)[index],
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TestMqlMechanicalObservationCatalog:
|
||||
pistons: dict[str, TestMqlPistonObservationBinding]
|
||||
masses: dict[str, TestMqlMassEndstopObservationBinding]
|
||||
elastic_endstops: dict[str, TestMqlElasticEndstopObservationBinding]
|
||||
zero_force_sources: dict[str, TestMqlForceSourceObservationBinding]
|
||||
force_connectors: dict[str, TestMqlForceConnectorObservationBinding]
|
||||
mechanical_nodes: dict[str, TestMqlMechanicalNodeObservationBinding]
|
||||
|
||||
@property
|
||||
def binding_count(self) -> int:
|
||||
return len(self.pistons) + len(self.masses) + len(self.elastic_endstops)
|
||||
return (
|
||||
len(self.pistons)
|
||||
+ len(self.masses)
|
||||
+ len(self.elastic_endstops)
|
||||
+ len(self.zero_force_sources)
|
||||
+ len(self.force_connectors)
|
||||
+ len(self.mechanical_nodes)
|
||||
)
|
||||
|
||||
|
||||
def build_test_mql_mechanical_observation_catalog(
|
||||
@@ -160,6 +236,18 @@ def build_test_mql_mechanical_observation_catalog(
|
||||
alias: _build_elastic_endstop_binding(alias, variable_catalog)
|
||||
for alias in mechanical_assembly.elastic_endstops
|
||||
},
|
||||
zero_force_sources={
|
||||
alias: _build_zero_force_source_binding(alias, variable_catalog)
|
||||
for alias in mechanical_assembly.zero_force_sources
|
||||
},
|
||||
force_connectors={
|
||||
alias: _build_force_connector_binding(alias, variable_catalog)
|
||||
for alias in mechanical_assembly.force_connectors
|
||||
},
|
||||
mechanical_nodes={
|
||||
alias: _build_mechanical_node_binding(alias, variable_catalog)
|
||||
for alias in mechanical_assembly.mechanical_nodes
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -217,6 +305,54 @@ def _build_elastic_endstop_binding(
|
||||
)
|
||||
|
||||
|
||||
def _build_zero_force_source_binding(
|
||||
alias: str,
|
||||
variable_catalog: TestMqlVariableCatalog,
|
||||
) -> TestMqlForceSourceObservationBinding:
|
||||
variables = _owner_variables(variable_catalog, alias)
|
||||
return TestMqlForceSourceObservationBinding(
|
||||
alias=alias,
|
||||
force_path=_required_path(variables, "fzero", expected_units="N"),
|
||||
)
|
||||
|
||||
|
||||
def _build_force_connector_binding(
|
||||
alias: str,
|
||||
variable_catalog: TestMqlVariableCatalog,
|
||||
) -> TestMqlForceConnectorObservationBinding:
|
||||
variables = _owner_variables(variable_catalog, alias)
|
||||
return TestMqlForceConnectorObservationBinding(
|
||||
alias=alias,
|
||||
force_path=_required_path(variables, "force", expected_units="N"),
|
||||
)
|
||||
|
||||
|
||||
def _build_mechanical_node_binding(
|
||||
alias: str,
|
||||
variable_catalog: TestMqlVariableCatalog,
|
||||
) -> TestMqlMechanicalNodeObservationBinding:
|
||||
variables = _owner_variables(variable_catalog, alias)
|
||||
velocity_paths_by_port = {}
|
||||
displacement_paths_by_port = {}
|
||||
for port in range(1, 9):
|
||||
velocity_paths_by_port[port] = _required_path(
|
||||
variables,
|
||||
f"p{port}__vt",
|
||||
expected_units="m/s",
|
||||
)
|
||||
displacement_paths_by_port[port] = _required_path(
|
||||
variables,
|
||||
f"p{port}__xt",
|
||||
expected_units="m",
|
||||
)
|
||||
return TestMqlMechanicalNodeObservationBinding(
|
||||
alias=alias,
|
||||
velocity_paths_by_port=velocity_paths_by_port,
|
||||
displacement_paths_by_port=displacement_paths_by_port,
|
||||
total_force_path=_required_path(variables, "tforce", expected_units="N"),
|
||||
)
|
||||
|
||||
|
||||
def _owner_variables(
|
||||
variable_catalog: TestMqlVariableCatalog,
|
||||
alias: str,
|
||||
|
||||
@@ -23,7 +23,10 @@ class TestMqlMechanicalObservationCatalogTests(unittest.TestCase):
|
||||
self.assertEqual(len(self.catalog.pistons), 8)
|
||||
self.assertEqual(len(self.catalog.masses), 10)
|
||||
self.assertEqual(len(self.catalog.elastic_endstops), 8)
|
||||
self.assertEqual(self.catalog.binding_count, 26)
|
||||
self.assertEqual(len(self.catalog.zero_force_sources), 16)
|
||||
self.assertEqual(len(self.catalog.force_connectors), 2)
|
||||
self.assertEqual(len(self.catalog.mechanical_nodes), 2)
|
||||
self.assertEqual(self.catalog.binding_count, 46)
|
||||
|
||||
def test_piston_binding_exposes_amesim_data_paths(self) -> None:
|
||||
binding = self.catalog.pistons["pn_brp2_8"]
|
||||
@@ -119,6 +122,68 @@ class TestMqlMechanicalObservationCatalogTests(unittest.TestCase):
|
||||
1.0e-9,
|
||||
)
|
||||
|
||||
def test_zero_force_source_observations_are_zero(self) -> None:
|
||||
for binding in self.catalog.zero_force_sources.values():
|
||||
force = self.amesim_results.series(binding.force_path)
|
||||
with self.subTest(alias=binding.alias):
|
||||
self.assertEqual(binding.force_path, f"fzero@{binding.alias}")
|
||||
self.assertEqual(min(force), 0.0)
|
||||
self.assertEqual(max(force), 0.0)
|
||||
self.assertEqual(binding.observation_at(self.amesim_results, -1).force_n, 0.0)
|
||||
|
||||
def test_force_connector_observations_match_amesim_samples(self) -> None:
|
||||
force_1 = self.catalog.force_connectors["forcecon_1"].observation_at(
|
||||
self.amesim_results,
|
||||
-1,
|
||||
)
|
||||
force_2 = self.catalog.force_connectors["forcecon_2"].observation_at(
|
||||
self.amesim_results,
|
||||
-1,
|
||||
)
|
||||
|
||||
self.assertEqual(self.catalog.force_connectors["forcecon_1"].force_path, "force@forcecon_1")
|
||||
self.assertEqual(self.catalog.force_connectors["forcecon_2"].force_path, "force@forcecon_2")
|
||||
self.assertAlmostEqual(force_1.force_n, 49000.0)
|
||||
self.assertAlmostEqual(force_2.force_n, 0.0)
|
||||
self.assertAlmostEqual(
|
||||
self.amesim_results.series("force@forcecon_1")[0],
|
||||
1.0e17,
|
||||
)
|
||||
self.assertAlmostEqual(
|
||||
self.amesim_results.series("force@forcecon_2")[0],
|
||||
1.0e12,
|
||||
)
|
||||
|
||||
def test_mechanical_node_bindings_expose_all_ports(self) -> None:
|
||||
node = self.catalog.mechanical_nodes["dynamic_mechanical_node_alternative_2"]
|
||||
|
||||
self.assertEqual(sorted(node.velocity_paths_by_port), list(range(1, 9)))
|
||||
self.assertEqual(sorted(node.displacement_paths_by_port), list(range(1, 9)))
|
||||
self.assertEqual(node.velocity_paths_by_port[1], "p1__vt@dynamic_mechanical_node_alternative_2")
|
||||
self.assertEqual(node.displacement_paths_by_port[8], "p8__xt@dynamic_mechanical_node_alternative_2")
|
||||
self.assertEqual(node.total_force_path, "tforce@dynamic_mechanical_node_alternative_2")
|
||||
|
||||
def test_mechanical_node_observations_match_amesim_final_samples(self) -> None:
|
||||
front = self.catalog.mechanical_nodes["dynamic_mechanical_node_alternative_2"].observation_at(
|
||||
self.amesim_results,
|
||||
-1,
|
||||
)
|
||||
rear = self.catalog.mechanical_nodes["dynamic_mechanical_node_alternative_3"].observation_at(
|
||||
self.amesim_results,
|
||||
-1,
|
||||
)
|
||||
|
||||
self.assertEqual(set(front.velocities_m_s), set(range(1, 9)))
|
||||
self.assertEqual(set(front.displacements_m), set(range(1, 9)))
|
||||
self.assertAlmostEqual(front.velocities_m_s[1], -0.0)
|
||||
self.assertAlmostEqual(front.displacements_m[1], -0.3700000000000001)
|
||||
self.assertAlmostEqual(front.displacements_m[8], -0.3700000000000001)
|
||||
self.assertAlmostEqual(front.total_force_n, 1083272.2763054767)
|
||||
self.assertAlmostEqual(rear.velocities_m_s[1], 0.0)
|
||||
self.assertAlmostEqual(rear.displacements_m[1], -0.72)
|
||||
self.assertAlmostEqual(rear.displacements_m[8], -0.72)
|
||||
self.assertAlmostEqual(rear.total_force_n, 1083272.2762592242)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user