完善建模交互、组件图标与系统协议

This commit is contained in:
ljz committed 2026-08-15 17:40:18 +08:00
1 parent 456c29b3b6
commit 6572defaa4
66 files changed
+10067 -4163

No files matched your search

@@ -81,12 +81,29 @@ class AmesimForc(AlgebraicComponent):
"""AMESim FORC signal-to-force converter."""
MODEL_TYPE = "amesim_forc"
MODEL_VERSION = "0.1.0"
MODEL_VERSION = "0.2.0"
PORTS = (
PortDefinition.signal("res", nominal_role="input"),
PortDefinition.mechanical_translational("port_2"),
)
PARAMETERS = ()
PARAMETERS = (
ParameterDefinition(
"direction",
1.0,
label="力方向",
quantity="dimensionless",
unit="",
editor="choice",
options=(
ParameterOption(1.0, "正向"),
ParameterOption(-1.0, "反向"),
),
description=(
"显式控制输入信号相对于机械端口正方向的力符号;"
"图标旋转和镜像不会改变该参数。"
),
),
)
RESULT_VARIABLES = (
ResultVariableDefinition("force", "输出力", "force", "N", "signal", 10),
)
@@ -102,12 +119,12 @@ class AmesimForc(AlgebraicComponent):
order=20,
)
def __init__(self, name: str) -> None:
def __init__(self, name: str, *, direction: float = 1.0) -> None:
super().__init__(name=name)
self.set_parameter_values({})
self.set_parameter_values({"direction": direction})
self.direction = float(direction)
self.res = self.register_declared_port("res")
self.port_2 = self.register_declared_port("port_2")
self._orientation_sign = 1.0
@classmethod
def create(
@@ -117,20 +134,11 @@ class AmesimForc(AlgebraicComponent):
medium: IdealGasMedium,
parameters: Mapping[str, float],
) -> "AmesimForc":
return cls(name=name)
def apply_layout_transform(self, *, rotation: int, mirrored: bool) -> None:
"""Apply the AMESim icon direction to the signed force output."""
normalized_rotation = int(rotation) % 360
if normalized_rotation not in {0, 90, 180, 270}:
raise ValueError("FORC rotation must be a multiple of 90 degrees.")
direction = -1.0 if normalized_rotation in {180, 270} else 1.0
self._orientation_sign = -direction if mirrored else direction
return cls(name=name, direction=parameters["direction"])
@property
def output_force(self) -> float:
return float(self.res.signal)
return self.direction * float(self.res.signal)
def pressure_flow_equation_residuals(self) -> tuple[EquationResidual, ...]:
return (
@@ -141,7 +149,7 @@ class AmesimForc(AlgebraicComponent):
relation="constitutive",
variables=(f"{self.name}.port_2.f", f"{self.name}.res.signal"),
role="flow",
value=self.port_2.f + self._orientation_sign * self.output_force,
value=self.port_2.f + self.output_force,
),
)
@@ -366,7 +374,7 @@ class AmesimMecmas21(DynamicComponent):
),
ParameterDefinition(
"useFriction",
1.0,
2.0,
label="启用摩擦",
quantity="dimensionless",
unit="",
@@ -383,7 +391,7 @@ class AmesimMecmas21(DynamicComponent):
),
ParameterDefinition(
"stoptype",
4.0,
1.0,
label="限位类型",
quantity="dimensionless",
unit="",
@@ -1031,14 +1039,33 @@ class AmesimLmechn1(AlgebraicComponent):
"""AMESim LMECHN1 first public dynamic linear mechanical node."""
MODEL_TYPE = "amesim_lmechn1"
MODEL_VERSION = "0.1.0"
MODEL_VERSION = "0.2.0"
PORTS = tuple(
PortDefinition.mechanical_translational(f"port_{index}")
for index in range(1, 10)
for index in range(1, 22)
)
PARAMETERS = (
ParameterDefinition("v1", 8.0, label="右侧端口数", quantity="dimensionless", unit="", minimum=1.0, maximum=8.0),
ParameterDefinition("sum", 1.0, label="节点求和模式", quantity="dimensionless", unit="", minimum=0.0),
ParameterDefinition(
"v1",
2.0,
label="右侧端口数",
quantity="dimensionless",
unit="",
minimum=1.0,
maximum=20.0,
description="设置工作区中显示的右侧机械端口数量,最多 20 个。",
),
ParameterDefinition(
"sum",
1.0,
label="节点求和模式",
quantity="dimensionless",
unit="",
editor="choice",
options=(
ParameterOption(1.0, "各端口力代数和为零(标准节点)"),
),
),
)
RESULT_VARIABLES = (
ResultVariableDefinition("tforce", "节点合力", "force", "N", "derived", 10),
@@ -1049,13 +1076,16 @@ class AmesimLmechn1(AlgebraicComponent):
category_id="mechanical",
symbol="amesim_lmechn1",
ports=tuple(
[PortDisplaySpec(f"port_{index}", "left", order=index * 10) for index in range(1, 9)]
+ [PortDisplaySpec("port_9", "right", order=90)]
[
PortDisplaySpec(f"port_{index}", "right", order=index * 10)
for index in range(1, 21)
]
+ [PortDisplaySpec("port_21", "left", order=210)]
),
order=50,
)
def __init__(self, name: str, medium: IdealGasMedium, *, v1: float = 8.0, sum: float = 1.0) -> None:
def __init__(self, name: str, medium: IdealGasMedium, *, v1: float = 2.0, sum: float = 1.0) -> None:
super().__init__(name=name)
self.set_parameter_values({"v1": v1, "sum": sum})
self.v1 = int(v1)
@@ -1078,21 +1108,30 @@ class AmesimLmechn1(AlgebraicComponent):
@property
def active_ports(self) -> tuple[str, ...]:
return tuple(f"port_{index}" for index in range(1, self.v1 + 1)) + ("port_9",)
return tuple(f"port_{index}" for index in range(1, self.v1 + 2))
@property
def reference_port_name(self) -> str:
return f"port_{self.v1 + 1}"
@property
def required_connection_ports(self) -> tuple[str, ...]:
return self.active_ports
@property
def total_force(self) -> float:
# AMESim's ``tforce`` is the force transmitted by the summed branch
# ports (1..v1). Port 9 is the balancing/common port and is excluded
# from that reported value.
# ports (1..v1). The final active port is the balancing/common port and
# is excluded from that reported value.
return sum(self.get_port(port_name).f for port_name in self.active_ports[:-1])
@property
def force_balance(self) -> float:
return self.total_force + self.port_9.f
return self.total_force + self.get_port(self.reference_port_name).f
def pressure_flow_equation_residuals(self) -> tuple[EquationResidual, ...]:
reference = self.port_9
reference_name = self.reference_port_name
reference = self.get_port(reference_name)
residuals: list[EquationResidual] = []
for port_name in self.active_ports[:-1]:
port = self.get_port(port_name)
@@ -1102,7 +1141,7 @@ class AmesimLmechn1(AlgebraicComponent):
owner="component",
owner_id=self.name,
relation="equal",
variables=(f"{self.name}.{port_name}.x", f"{self.name}.port_9.x"),
variables=(f"{self.name}.{port_name}.x", f"{self.name}.{reference_name}.x"),
role="effort",
value=port.x - reference.x,
)
@@ -1113,7 +1152,7 @@ class AmesimLmechn1(AlgebraicComponent):
owner="component",
owner_id=self.name,
relation="equal",
variables=(f"{self.name}.{port_name}.v", f"{self.name}.port_9.v"),
variables=(f"{self.name}.{port_name}.v", f"{self.name}.{reference_name}.v"),
role="effort",
value=port.v - reference.v,
)
@@ -1129,6 +1168,39 @@ class AmesimLmechn1(AlgebraicComponent):
value=self.force_balance,
)
)
for definition in self.PORTS[self.v1 + 1 :]:
port = self.get_port(definition.name)
residuals.extend(
(
EquationResidual(
id=f"{self.name}:{definition.name}_inactive_x",
owner="component",
owner_id=self.name,
relation="constitutive",
variables=(f"{self.name}.{definition.name}.x",),
role="effort",
value=port.x,
),
EquationResidual(
id=f"{self.name}:{definition.name}_inactive_v",
owner="component",
owner_id=self.name,
relation="constitutive",
variables=(f"{self.name}.{definition.name}.v",),
role="effort",
value=port.v,
),
EquationResidual(
id=f"{self.name}:{definition.name}_inactive_force",
owner="component",
owner_id=self.name,
relation="constitutive",
variables=(f"{self.name}.{definition.name}.f",),
role="flow",
value=port.f,
),
)
)
return tuple(residuals)
def component_result_values(self) -> Mapping[str, float]:
@@ -1,12 +1,3 @@
"""Temporary component library used to validate the model authoring contract."""
from app.simulation.components.experimental.library import LIBRARY
# Compatibility aliases for code written before the v1 library manifest.
LIBRARY_ID = LIBRARY.id
LIBRARY_LABEL = LIBRARY.label
LIBRARY_VERSION = LIBRARY.version
LIBRARY_ORDER = LIBRARY.order
LIBRARY_SOURCE_PACKAGE = LIBRARY.source_package
LIBRARY_TEMPORARY = LIBRARY.temporary