220 lines
8.0 KiB
Python
220 lines
8.0 KiB
Python
from __future__ import annotations
|
|
|
|
import unittest
|
|
|
|
from app.simulation.components.amesim.mechanical.translational import (
|
|
AmesimF000,
|
|
AmesimForc,
|
|
AmesimMecmas21,
|
|
AmesimLstp00a,
|
|
AmesimLmechn1,
|
|
)
|
|
from app.simulation.core.medium import IdealGasMedium
|
|
from app.simulation.registry import COMPONENT_MODEL_REGISTRY
|
|
|
|
|
|
class AmesimMechanicalPublicComponentTests(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.medium = IdealGasMedium()
|
|
|
|
def test_mecmas21_catalog_ports_follow_amesim_icon_sides(self) -> None:
|
|
self.assertEqual(
|
|
tuple((port.name, port.side) for port in AmesimMecmas21.DISPLAY.ports),
|
|
(("port_2", "left"), ("port_1", "right")),
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_forc_direction_only_accepts_positive_or_negative_one(self) -> None:
|
|
definition = AmesimForc.PARAMETERS[0]
|
|
|
|
self.assertEqual(AmesimForc.MODEL_VERSION, "0.2.0")
|
|
self.assertEqual(definition.name, "direction")
|
|
self.assertEqual(definition.default, 1.0)
|
|
self.assertEqual(
|
|
tuple((option.value, option.label) for option in definition.options),
|
|
((1.0, "正向"), (-1.0, "反向")),
|
|
)
|
|
with self.assertRaisesRegex(ValueError, "must be one of 1, -1"):
|
|
AmesimForc("invalid_force", direction=0.0)
|
|
|
|
|
|
|
|
def test_mecmas21_choice_metadata_preserves_amesim_codes(self) -> None:
|
|
definitions = {
|
|
definition.name: definition
|
|
for definition in AmesimMecmas21.PARAMETERS
|
|
}
|
|
expected_options = {
|
|
"useFriction": ((1.0, "否"), (2.0, "是")),
|
|
"stoptype": (
|
|
(1.0, "理想限位"),
|
|
(2.0, "弹性限位"),
|
|
(3.0, "恢复碰撞"),
|
|
(4.0, "无限位"),
|
|
),
|
|
"discContactOption": (
|
|
(1.0, "允许负接触力"),
|
|
(2.0, "不允许负接触力"),
|
|
),
|
|
"strib": ((1.0, "否"), (2.0, "是")),
|
|
"frictionType": ((1.0, "简单"), (2.0, "高级")),
|
|
}
|
|
self.assertEqual(definitions["useFriction"].default, 2.0)
|
|
self.assertEqual(definitions["stoptype"].default, 1.0)
|
|
|
|
for name, options in expected_options.items():
|
|
with self.subTest(name=name):
|
|
definition = definitions[name]
|
|
self.assertEqual(definition.editor, "choice")
|
|
self.assertEqual(
|
|
tuple((option.value, option.label) for option in definition.options),
|
|
options,
|
|
)
|
|
self.assertIn("尚未实现", definitions["frictionType"].description)
|
|
self.assertIn("尚未实现", definitions["strib"].description)
|
|
self.assertEqual(definitions["theta"].label, "倾角(度)")
|
|
self.assertIn("+90°", definitions["theta"].description)
|
|
self.assertIn("-90°", definitions["theta"].description)
|
|
self.assertIn("重力分量", definitions["theta"].description)
|
|
self.assertEqual(AmesimMecmas21.MODEL_VERSION, "0.2.0")
|
|
|
|
def test_mecmas21_visibility_matches_amesim_parameter_dependencies(self) -> None:
|
|
definitions = {
|
|
definition.name: definition
|
|
for definition in AmesimMecmas21.PARAMETERS
|
|
}
|
|
expected_visibility = {
|
|
"fstick": (
|
|
("stoptype", (1.0, 2.0, 4.0)),
|
|
("useFriction", (2.0,)),
|
|
),
|
|
"fcoul": (
|
|
("stoptype", (1.0, 2.0, 4.0)),
|
|
("useFriction", (2.0,)),
|
|
),
|
|
"rvisc": (("useFriction", (2.0,)),),
|
|
"wind": (("useFriction", (2.0,)),),
|
|
"dvel": (
|
|
("stoptype", (1.0, 2.0, 4.0)),
|
|
("useFriction", (2.0,)),
|
|
("frictionType", (2.0,)),
|
|
),
|
|
"restdvel": (("stoptype", (3.0,)),),
|
|
"restcoeff": (("stoptype", (3.0,)),),
|
|
"astrib": (
|
|
("stoptype", (1.0, 2.0, 4.0)),
|
|
("useFriction", (2.0,)),
|
|
("frictionType", (2.0,)),
|
|
("strib", (2.0,)),
|
|
),
|
|
"xmin": (("stoptype", (1.0, 2.0, 3.0)),),
|
|
"Kbmin": (("stoptype", (2.0,)),),
|
|
"Dbmin": (("stoptype", (2.0,)),),
|
|
"Pdmin": (("stoptype", (2.0,)),),
|
|
"xmax": (("stoptype", (1.0, 2.0, 3.0)),),
|
|
"Kbmax": (("stoptype", (2.0,)),),
|
|
"Dbmax": (("stoptype", (2.0,)),),
|
|
"Pdmax": (("stoptype", (2.0,)),),
|
|
"discContactOption": (("stoptype", (2.0,)),),
|
|
"strib": (
|
|
("stoptype", (1.0, 2.0, 4.0)),
|
|
("useFriction", (2.0,)),
|
|
("frictionType", (2.0,)),
|
|
),
|
|
"frictionType": (
|
|
("stoptype", (1.0, 2.0, 4.0)),
|
|
("useFriction", (2.0,)),
|
|
),
|
|
}
|
|
|
|
for name, expected in expected_visibility.items():
|
|
with self.subTest(name=name):
|
|
self.assertEqual(
|
|
tuple(
|
|
(condition.parameter, condition.values)
|
|
for condition in definitions[name].visible_when
|
|
),
|
|
expected,
|
|
)
|
|
for name in ("mass", "theta", "useFriction", "stoptype", "v0", "x0"):
|
|
with self.subTest(always_visible=name):
|
|
self.assertEqual(definitions[name].visible_when, ())
|
|
|
|
|
|
def test_lmechn1_defaults_to_two_right_ports(self) -> None:
|
|
node = AmesimLmechn1("node_default", self.medium)
|
|
|
|
self.assertEqual(node.v1, 2)
|
|
self.assertEqual(node.active_ports, ("port_1", "port_2", "port_3"))
|
|
|
|
def test_lmechn1_registry_rejects_fractional_integer_options(self) -> None:
|
|
with self.assertRaisesRegex(ValueError, "v1 must be an integer"):
|
|
COMPONENT_MODEL_REGISTRY["amesim_lmechn1"].create(
|
|
"node_1",
|
|
self.medium,
|
|
{"v1": 2.5},
|
|
)
|
|
|
|
def test_lmechn1_supports_twenty_right_ports(self) -> None:
|
|
node = AmesimLmechn1("node_20", self.medium, v1=20.0, sum=1.0)
|
|
|
|
self.assertEqual(
|
|
node.active_ports,
|
|
tuple(f"port_{index}" for index in range(1, 22)),
|
|
)
|
|
with self.assertRaisesRegex(ValueError, "must be at most 20"):
|
|
AmesimLmechn1("node_21", self.medium, v1=21.0, sum=1.0)
|
|
|
|
|
|
|
|
def test_mecmas21_registry_rejects_fractional_choice_value(self) -> None:
|
|
with self.assertRaisesRegex(ValueError, "must be one of"):
|
|
COMPONENT_MODEL_REGISTRY["amesim_mecmas21"].create(
|
|
"mass_1",
|
|
self.medium,
|
|
{"useFriction": 0.5},
|
|
)
|
|
|
|
def test_mecmas21_registry_rejects_unknown_integer_options(self) -> None:
|
|
invalid_options = {
|
|
"useFriction": 0.0,
|
|
"stoptype": 0.0,
|
|
"discContactOption": 3.0,
|
|
"strib": 0.0,
|
|
"frictionType": 3.0,
|
|
}
|
|
|
|
for name, value in invalid_options.items():
|
|
with self.subTest(name=name):
|
|
with self.assertRaisesRegex(ValueError, "must be one of"):
|
|
COMPONENT_MODEL_REGISTRY["amesim_mecmas21"].create(
|
|
"mass_1",
|
|
self.medium,
|
|
{name: value},
|
|
)
|
|
|
|
def test_lstp00a_registry_rejects_invalid_choice_values(self) -> None:
|
|
invalid_options = {
|
|
"stiffmode": (0.0, 1.5, 3.0),
|
|
"discContactOption": (0.0, 1.5, 3.0),
|
|
}
|
|
|
|
for name, values in invalid_options.items():
|
|
for value in values:
|
|
with self.subTest(name=name, value=value):
|
|
with self.assertRaisesRegex(
|
|
ValueError,
|
|
"must be (?:one of|at least|at most)",
|
|
):
|
|
COMPONENT_MODEL_REGISTRY["amesim_lstp00a"].create(
|
|
"contact_1",
|
|
self.medium,
|
|
{name: value},
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|