同步仿真框架并接入AMESim气动组件

This commit is contained in:
huojiarong committed 2026-07-30 03:53:10 +00:00
1 parent 420bafeb4e
commit db4bdb4b70
109 files changed
+26920 -420

No files matched your search

@@ -0,0 +1 @@
"""AMESim pneumatic storage components."""
@@ -0,0 +1,250 @@
from __future__ import annotations
from collections.abc import Mapping
from math import isclose
from app.simulation.core.base import ThermodynamicVolumeComponent
from app.simulation.core.catalog import ComponentDisplaySpec, PortDisplaySpec
from app.simulation.core.equations import EquationResidual
from app.simulation.core.metadata import (
ParameterDefinition,
THERMODYNAMIC_VOLUME_RESULT_VARIABLES,
)
from app.simulation.core.medium import IdealGasMedium, ThermodynamicProperties
from app.simulation.core.ports import PortDefinition
from app.simulation.core.state import VolumeState
class AmesimPnch023(ThermodynamicVolumeComponent):
"""AMESim PNCH023 simple pneumatic chamber with heat exchange.
The AMESim submodel owns pressure and temperature states and exposes two
pneumatic flow ports. This public component maps those states onto the
framework's mass/internal-energy volume state and keeps the AMESim
heat-transfer contract `kth * sth * (extemp - T)`.
"""
MODEL_TYPE = "amesim_pnch023"
MODEL_VERSION = "0.1.0"
PORTS = (
PortDefinition.pneumatic("port_1", nominal_role="bidirectional"),
PortDefinition.pneumatic("port_2", nominal_role="bidirectional"),
)
PARAMETERS = (
ParameterDefinition(
"cvol",
0.057,
label="气室容积",
quantity="volume",
unit="m3",
minimum=0.0,
minimum_exclusive=True,
),
ParameterDefinition(
"kth",
0.0,
label="换热系数",
quantity="heat_transfer_coefficient",
unit="W/(m2*K)",
minimum=0.0,
),
ParameterDefinition(
"sth",
0.1,
label="换热面积",
quantity="area",
unit="m2",
minimum=0.0,
),
ParameterDefinition(
"extemp",
293.15,
label="外部温度",
quantity="temperature",
unit="K",
minimum=0.0,
minimum_exclusive=True,
),
ParameterDefinition(
"gi",
1.0,
label="气体类型索引",
quantity="dimensionless",
unit="",
minimum=1.0,
maximum=99.0,
),
ParameterDefinition(
"p0",
100000.0,
label="初始压力",
quantity="pressure",
unit="Pa",
minimum=0.0,
minimum_exclusive=True,
),
ParameterDefinition(
"T0",
293.15,
label="初始温度",
quantity="temperature",
unit="K",
minimum=0.0,
minimum_exclusive=True,
),
)
RESULT_VARIABLES = THERMODYNAMIC_VOLUME_RESULT_VARIABLES
DISPLAY = ComponentDisplaySpec(
label="PNCH023 固定容积气室",
library_id="amesim",
category_id="storage",
symbol="tank",
ports=(
PortDisplaySpec("port_1", "left", order=10),
PortDisplaySpec("port_2", "right", order=20),
),
order=10,
)
def __init__(
self,
name: str,
medium: IdealGasMedium,
*,
cvol: float = 0.057,
kth: float = 0.0,
sth: float = 0.1,
extemp: float = 293.15,
gi: float = 1.0,
p0: float = 100000.0,
T0: float = 293.15,
) -> None:
super().__init__(name=name)
self.set_parameter_values(
{
"cvol": cvol,
"kth": kth,
"sth": sth,
"extemp": extemp,
"gi": gi,
"p0": p0,
"T0": T0,
}
)
self.medium = medium
self.cvol = float(cvol)
self.kth = float(kth)
self.sth = float(sth)
self.extemp = float(extemp)
self.gi = self._integer_parameter("gi", gi)
self.p0 = float(p0)
self.T0 = float(T0)
m0 = self.p0 * self.cvol / (medium.R_gas * self.T0)
U0 = m0 * medium.specific_internal_energy(self.T0)
self.state = VolumeState(m=m0, U=U0)
initial_h = medium.specific_enthalpy(self.T0)
self.port_1 = self.register_declared_port("port_1")
self.port_1.p = self.p0
self.port_1.h_outflow = initial_h
self.port_2 = self.register_declared_port("port_2")
self.port_2.p = self.p0
self.port_2.h_outflow = initial_h
@staticmethod
def _integer_parameter(name: str, value: float) -> int:
rounded = round(value)
if not isclose(value, rounded, rel_tol=0.0, abs_tol=1.0e-12):
raise ValueError(f"PNCH023 parameter {name} must be an integer value.")
return int(rounded)
@classmethod
def create(
cls,
*,
name: str,
medium: IdealGasMedium,
parameters: Mapping[str, float],
) -> AmesimPnch023:
return cls(
name=name,
medium=medium,
cvol=parameters["cvol"],
kth=parameters["kth"],
sth=parameters["sth"],
extemp=parameters["extemp"],
gi=parameters["gi"],
p0=parameters["p0"],
T0=parameters["T0"],
)
def get_state_vector(self) -> list[float]:
return self.state.as_vector()
def set_state_vector(self, values: list[float]) -> None:
self.state = VolumeState.from_vector(values)
def properties(self) -> ThermodynamicProperties:
props = self.medium.properties_from_mU(self.state.m, self.state.U, self.cvol)
self.port_1.p = props.p
self.port_1.h_outflow = props.h
self.port_2.p = props.p
self.port_2.h_outflow = props.h
return props
def refresh_thermodynamic_ports(self) -> ThermodynamicProperties:
return self.properties()
def thermal_energy_flow_w(self, temperature: float) -> float:
return self.kth * self.sth * (self.extemp - temperature)
def state_derivative_from_ports(
self,
connected_h: Mapping[str, float],
) -> list[float]:
props = self.properties()
inlet_h_1 = self.connection_inlet_enthalpy(
port_m_flow=self.port_1.m_flow,
connected_h=connected_h["port_1"],
internal_h=props.h,
)
inlet_h_2 = self.connection_inlet_enthalpy(
port_m_flow=self.port_2.m_flow,
connected_h=connected_h["port_2"],
internal_h=props.h,
)
derivative = VolumeState(
m=self.port_1.m_flow + self.port_2.m_flow,
U=(
self.port_1.m_flow * inlet_h_1
+ self.port_2.m_flow * inlet_h_2
+ self.thermal_energy_flow_w(props.T)
),
)
return derivative.as_vector()
def pressure_flow_equation_residuals(self) -> tuple[EquationResidual, ...]:
pressure = self.medium.properties_from_mU(
self.state.m,
self.state.U,
self.cvol,
).p
return (
EquationResidual(
id=f"{self.name}:port_1_pressure_state",
owner="component",
owner_id=self.name,
relation="state",
variables=(f"{self.name}.port_1.p", f"{self.name}.state"),
role="effort",
value=self.port_1.p - pressure,
),
EquationResidual(
id=f"{self.name}:port_2_pressure_state",
owner="component",
owner_id=self.name,
relation="state",
variables=(f"{self.name}.port_2.p", f"{self.name}.state"),
role="effort",
value=self.port_2.p - pressure,
),
)