完善建模交互、组件图标与系统协议
This commit is contained in:
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
|
||||
Reference in new issue
Block a user