feat: integrate AMESim media models and editor UI

This commit is contained in:
ljz committed 2026-07-31 23:36:58 +08:00
1 parent 3e4f466114
commit e7177ab03e
181 files changed
+7042 -25270

No files matched your search

@@ -3,6 +3,10 @@ from __future__ import annotations
from collections.abc import Mapping
from math import isclose, sqrt
from app.simulation.components.amesim.gases import (
AMESIM_GAS_INDEX_PARAMETER,
normalize_amesim_gas_index,
)
from app.simulation.core.base import AlgebraicComponent
from app.simulation.core.catalog import ComponentDisplaySpec, PortDisplaySpec
from app.simulation.core.equations import EquationResidual
@@ -10,7 +14,7 @@ from app.simulation.core.metadata import (
ParameterDefinition,
ResultVariableDefinition,
)
from app.simulation.core.medium import IdealGasMedium
from app.simulation.core.medium import GasMedium
from app.simulation.core.ports import PortDefinition
@@ -29,6 +33,7 @@ class AmesimPnor001(AlgebraicComponent):
PortDefinition.pneumatic("port_2", nominal_role="bidirectional"),
)
PARAMETERS = (
AMESIM_GAS_INDEX_PARAMETER,
ParameterDefinition(
"cq",
0.72,
@@ -37,6 +42,7 @@ class AmesimPnor001(AlgebraicComponent):
unit="",
minimum=1.0e-10,
maximum=1.0,
description="孔口实际质量流量相对于理想可压缩流量的无量纲修正系数。",
),
ParameterDefinition(
"area",
@@ -46,6 +52,7 @@ class AmesimPnor001(AlgebraicComponent):
unit="m2",
minimum=0.0,
maximum=1.0,
description="选择 Cq/面积方式时用于流量计算的有效孔口面积。",
),
ParameterDefinition(
"Cv",
@@ -54,6 +61,7 @@ class AmesimPnor001(AlgebraicComponent):
quantity="dimensionless",
unit="",
minimum=0.0,
description="选择 Cv 方式时使用的英制流量系数。",
),
ParameterDefinition(
"Kv",
@@ -62,15 +70,7 @@ class AmesimPnor001(AlgebraicComponent):
quantity="dimensionless",
unit="",
minimum=0.0,
),
ParameterDefinition(
"gi",
1.0,
label="气体类型索引",
quantity="dimensionless",
unit="",
minimum=1.0,
maximum=99.0,
description="选择 Kv 方式时使用的公制流量系数。",
),
ParameterDefinition(
"flowset",
@@ -80,6 +80,7 @@ class AmesimPnor001(AlgebraicComponent):
unit="",
minimum=1.0,
maximum=3.0,
description="流量参数方式:1 使用 Cq 和面积,2 使用 Cv,3 使用 Kv。",
),
)
RESULT_VARIABLES = (
@@ -115,7 +116,7 @@ class AmesimPnor001(AlgebraicComponent):
def __init__(
self,
name: str,
medium: IdealGasMedium,
medium: GasMedium,
*,
cq: float = 0.72,
area: float = 5.0e-6,
@@ -140,7 +141,7 @@ class AmesimPnor001(AlgebraicComponent):
self.area = float(area)
self.Cv = float(Cv)
self.Kv = float(Kv)
self.gi = self._integer_parameter("gi", gi)
self.gi = normalize_amesim_gas_index(gi)
self.flowset = self._integer_parameter("flowset", flowset)
if self.flowset not in {1, 2, 3}:
raise ValueError("PNOR001 flowset must be 1, 2, or 3.")
@@ -163,7 +164,7 @@ class AmesimPnor001(AlgebraicComponent):
cls,
*,
name: str,
medium: IdealGasMedium,
medium: GasMedium,
parameters: Mapping[str, float],
) -> AmesimPnor001:
return cls(
@@ -206,7 +207,7 @@ class AmesimPnor001(AlgebraicComponent):
def _upstream_temperature(self, port_name: str) -> float:
port = self.get_port(port_name)
if port.h_outflow > 0.0:
return max(port.h_outflow / self.medium.cp_ref, 1.0)
return max(self.medium.temperature_from_enthalpy(port.h_outflow), 1.0)
return self.medium.T_ref
def mass_flow(self, p_1: float, p_2: float) -> float:
@@ -321,6 +322,7 @@ class AmesimPnvo001FixedOpening(AlgebraicComponent):
PortDefinition.pneumatic("port_3", nominal_role="bidirectional"),
)
PARAMETERS = (
AMESIM_GAS_INDEX_PARAMETER,
ParameterDefinition(
"cq",
0.72,
@@ -329,6 +331,7 @@ class AmesimPnvo001FixedOpening(AlgebraicComponent):
unit="",
minimum=1.0e-10,
maximum=1.0,
description="孔口实际质量流量相对于理想可压缩流量的无量纲修正系数。",
),
ParameterDefinition(
"area0",
@@ -338,6 +341,7 @@ class AmesimPnvo001FixedOpening(AlgebraicComponent):
unit="m2",
minimum=0.0,
maximum=1.0,
description="阀门完全开启时的最大有效孔口面积。",
),
ParameterDefinition(
"Cv",
@@ -346,6 +350,7 @@ class AmesimPnvo001FixedOpening(AlgebraicComponent):
quantity="dimensionless",
unit="",
minimum=0.0,
description="选择 Cv 方式时使用的最大英制流量系数。",
),
ParameterDefinition(
"Kv",
@@ -354,15 +359,7 @@ class AmesimPnvo001FixedOpening(AlgebraicComponent):
quantity="dimensionless",
unit="",
minimum=0.0,
),
ParameterDefinition(
"gi",
1.0,
label="气体类型索引",
quantity="dimensionless",
unit="",
minimum=1.0,
maximum=99.0,
description="选择 Kv 方式时使用的最大公制流量系数。",
),
ParameterDefinition(
"flowset",
@@ -372,6 +369,7 @@ class AmesimPnvo001FixedOpening(AlgebraicComponent):
unit="",
minimum=1.0,
maximum=3.0,
description="流量参数方式:1 使用 Cq 和面积,2 使用 Cv,3 使用 Kv。",
),
ParameterDefinition(
"opening",
@@ -381,6 +379,7 @@ class AmesimPnvo001FixedOpening(AlgebraicComponent):
unit="",
minimum=0.0,
maximum=1.0,
description="固定的归一化阀门开度;0 表示关闭,1 表示完全开启。",
),
)
RESULT_VARIABLES = (
@@ -424,7 +423,7 @@ class AmesimPnvo001FixedOpening(AlgebraicComponent):
def __init__(
self,
name: str,
medium: IdealGasMedium,
medium: GasMedium,
*,
cq: float = 0.72,
area0: float = 5.0e-6,
@@ -451,7 +450,7 @@ class AmesimPnvo001FixedOpening(AlgebraicComponent):
self.area0 = float(area0)
self.Cv = float(Cv)
self.Kv = float(Kv)
self.gi = self._integer_parameter("gi", gi)
self.gi = normalize_amesim_gas_index(gi)
self.flowset = self._integer_parameter("flowset", flowset)
if self.flowset not in {1, 2, 3}:
raise ValueError("PNVO001 fixed-opening flowset must be 1, 2, or 3.")
@@ -475,7 +474,7 @@ class AmesimPnvo001FixedOpening(AlgebraicComponent):
cls,
*,
name: str,
medium: IdealGasMedium,
medium: GasMedium,
parameters: Mapping[str, float],
) -> AmesimPnvo001FixedOpening:
return cls(
@@ -509,7 +508,7 @@ class AmesimPnvo001FixedOpening(AlgebraicComponent):
def _upstream_temperature(self, port_name: str) -> float:
port = self.get_port(port_name)
if port.h_outflow > 0.0:
return max(port.h_outflow / self.medium.cp_ref, 1.0)
return max(self.medium.temperature_from_enthalpy(port.h_outflow), 1.0)
return self.medium.T_ref
def mass_flow(self, p_2: float, p_3: float) -> float:
@@ -621,11 +620,11 @@ class AmesimPnvo001SignalOpening(AmesimPnvo001FixedOpening):
PortDefinition.pneumatic("port_3", nominal_role="bidirectional"),
)
PARAMETERS = (
AMESIM_GAS_INDEX_PARAMETER,
ParameterDefinition("cq", 0.72, label="流量系数 Cq", quantity="dimensionless", unit="", minimum=1.0e-10, maximum=1.0),
ParameterDefinition("area0", 5.0e-6, label="最大孔口面积", quantity="area", unit="m2", minimum=0.0, maximum=1.0),
ParameterDefinition("Cv", 0.5, label="最大流量系数 Cv", quantity="dimensionless", unit="", minimum=0.0),
ParameterDefinition("Kv", 0.4, label="最大流量系数 Kv", quantity="dimensionless", unit="", minimum=0.0),
ParameterDefinition("gi", 1.0, label="气体类型索引", quantity="dimensionless", unit="", minimum=1.0, maximum=99.0),
ParameterDefinition("flowset", 1.0, label="流量系数设置", quantity="dimensionless", unit="", minimum=1.0, maximum=3.0),
ParameterDefinition("opening0", 1.0, label="初始开度", quantity="dimensionless", unit="", minimum=0.0, maximum=1.0),
)
@@ -646,7 +645,7 @@ class AmesimPnvo001SignalOpening(AmesimPnvo001FixedOpening):
def __init__(
self,
name: str,
medium: IdealGasMedium,
medium: GasMedium,
*,
cq: float = 0.72,
area0: float = 5.0e-6,
@@ -673,7 +672,7 @@ class AmesimPnvo001SignalOpening(AmesimPnvo001FixedOpening):
self.area0 = float(area0)
self.Cv = float(Cv)
self.Kv = float(Kv)
self.gi = self._integer_parameter("gi", gi)
self.gi = normalize_amesim_gas_index(gi)
self.flowset = self._integer_parameter("flowset", flowset)
if self.flowset not in {1, 2, 3}:
raise ValueError("PNVO001 signal-opening flowset must be 1, 2, or 3.")
@@ -691,7 +690,7 @@ class AmesimPnvo001SignalOpening(AmesimPnvo001FixedOpening):
cls,
*,
name: str,
medium: IdealGasMedium,
medium: GasMedium,
parameters: Mapping[str, float],
) -> "AmesimPnvo001SignalOpening":
return cls(name=name, medium=medium, **dict(parameters))
+38 -51
View File
@@ -3,6 +3,10 @@ from __future__ import annotations
from collections.abc import Mapping
from math import isclose, log10, pi, sqrt
from app.simulation.components.amesim.gases import (
AMESIM_GAS_INDEX_PARAMETER,
normalize_amesim_gas_index,
)
from app.simulation.core.base import AlgebraicComponent, DynamicComponent, ThermodynamicVolumeComponent
from app.simulation.core.catalog import ComponentDisplaySpec, PortDisplaySpec
from app.simulation.core.equations import EquationResidual
@@ -11,7 +15,7 @@ from app.simulation.core.metadata import (
ResultVariableDefinition,
THERMODYNAMIC_VOLUME_RESULT_VARIABLES,
)
from app.simulation.core.medium import IdealGasMedium, ThermodynamicProperties
from app.simulation.core.medium import GasMedium, ThermodynamicProperties
from app.simulation.core.ports import PortDefinition
from app.simulation.core.state import VolumeState
@@ -31,6 +35,7 @@ class AmesimPnl00r(AlgebraicComponent):
PortDefinition.pneumatic("port_2", nominal_role="bidirectional"),
)
PARAMETERS = (
AMESIM_GAS_INDEX_PARAMETER,
ParameterDefinition(
"diam",
0.01,
@@ -39,6 +44,7 @@ class AmesimPnl00r(AlgebraicComponent):
unit="m",
minimum=0.0,
minimum_exclusive=True,
description="管路的有效内径,用于计算流通面积和摩擦压降。",
),
ParameterDefinition(
"le",
@@ -48,6 +54,7 @@ class AmesimPnl00r(AlgebraicComponent):
unit="m",
minimum=0.0,
minimum_exclusive=True,
description="参与摩擦压降计算的管路有效长度。",
),
ParameterDefinition(
"rr",
@@ -57,15 +64,7 @@ class AmesimPnl00r(AlgebraicComponent):
unit="",
minimum=0.0,
maximum=0.1,
),
ParameterDefinition(
"gi",
1.0,
label="气体类型索引",
quantity="dimensionless",
unit="",
minimum=1.0,
maximum=99.0,
description="管壁绝对粗糙度与管径之比,用于计算 Darcy 摩擦因子。",
),
)
RESULT_VARIABLES = (
@@ -117,7 +116,7 @@ class AmesimPnl00r(AlgebraicComponent):
def __init__(
self,
name: str,
medium: IdealGasMedium,
medium: GasMedium,
*,
diam: float = 0.01,
le: float = 1.0,
@@ -130,7 +129,7 @@ class AmesimPnl00r(AlgebraicComponent):
self.diam = float(diam)
self.le = float(le)
self.rr = float(rr)
self.gi = self._integer_parameter("gi", gi)
self.gi = normalize_amesim_gas_index(gi)
self.area = pi * self.diam * self.diam / 4.0
initial_h = medium.specific_enthalpy(medium.T_ref)
@@ -151,7 +150,7 @@ class AmesimPnl00r(AlgebraicComponent):
cls,
*,
name: str,
medium: IdealGasMedium,
medium: GasMedium,
parameters: Mapping[str, float],
) -> AmesimPnl00r:
return cls(
@@ -166,22 +165,11 @@ class AmesimPnl00r(AlgebraicComponent):
def _port_temperature(self, port_name: str) -> float:
port = self.get_port(port_name)
if port.h_outflow > 0.0:
return max(port.h_outflow / self.medium.cp_ref, 1.0)
return max(self.medium.temperature_from_enthalpy(port.h_outflow), 1.0)
return self.medium.T_ref
@staticmethod
def _dynamic_viscosity(temperature_k: float) -> float:
if temperature_k <= 0.0:
raise ValueError("temperature_k must be positive")
reference_temperature = 293.15
reference_viscosity = 1.82e-5
sutherland_constant = 110.4
return (
reference_viscosity
* (temperature_k / reference_temperature) ** 1.5
* (reference_temperature + sutherland_constant)
/ (temperature_k + sutherland_constant)
)
def _dynamic_viscosity(self, temperature_k: float) -> float:
return self.medium.dynamic_viscosity(temperature_k)
def reynolds_number(self, mass_flow: float, temperature: float) -> float:
viscosity = self._dynamic_viscosity(temperature)
@@ -322,6 +310,7 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
PortDefinition.pneumatic("port_2", nominal_role="bidirectional"),
)
PARAMETERS = (
AMESIM_GAS_INDEX_PARAMETER,
ParameterDefinition(
"diam",
0.01,
@@ -330,6 +319,7 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
unit="m",
minimum=0.0,
minimum_exclusive=True,
description="管路的有效内径,用于计算流通面积、储气容积和摩擦压降。",
),
ParameterDefinition(
"le",
@@ -339,6 +329,7 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
unit="m",
minimum=0.0,
minimum_exclusive=True,
description="管路的有效长度,用于计算储气容积、换热面积和摩擦压降。",
),
ParameterDefinition(
"rr",
@@ -348,6 +339,7 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
unit="",
minimum=0.0,
maximum=0.1,
description="管壁绝对粗糙度与管径之比,用于计算 Darcy 摩擦因子。",
),
ParameterDefinition(
"k",
@@ -358,6 +350,7 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
minimum=0.0,
minimum_exclusive=True,
maximum=2.0,
description="AMESim 管路热力学配置使用的多方指数。",
),
ParameterDefinition(
"kth",
@@ -366,6 +359,7 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
quantity="heat_transfer_coefficient",
unit="W/(m2*K)",
minimum=0.0,
description="管内气体与外部环境之间的对流换热系数。",
),
ParameterDefinition(
"extemp",
@@ -375,15 +369,7 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
unit="K",
minimum=0.0,
minimum_exclusive=True,
),
ParameterDefinition(
"gi",
1.0,
label="气体类型索引",
quantity="dimensionless",
unit="",
minimum=1.0,
maximum=99.0,
description="管路外部环境的绝对温度,用于计算换热功率。",
),
ParameterDefinition(
"mode",
@@ -393,6 +379,7 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
unit="",
minimum=1.0,
maximum=2.0,
description="热模型选择:1 为绝热,2 为按换热系数计算环境换热。",
),
ParameterDefinition(
"p0",
@@ -402,6 +389,7 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
unit="Pa",
minimum=0.0,
minimum_exclusive=True,
description="仿真开始时管内气体的绝对压力。",
),
ParameterDefinition(
"T0",
@@ -411,6 +399,7 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
unit="K",
minimum=0.0,
minimum_exclusive=True,
description="仿真开始时管内气体的绝对温度。",
),
)
RESULT_VARIABLES = THERMODYNAMIC_VOLUME_RESULT_VARIABLES + (
@@ -462,7 +451,7 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
def __init__(
self,
name: str,
medium: IdealGasMedium,
medium: GasMedium,
*,
diam: float = 0.01,
le: float = 1.0,
@@ -497,14 +486,14 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
self.k = float(k)
self.kth = float(kth)
self.extemp = float(extemp)
self.gi = self._integer_parameter("gi", gi)
self.gi = normalize_amesim_gas_index(gi)
self.mode = self._integer_parameter("mode", mode)
self.p0 = float(p0)
self.T0 = float(T0)
self.area = pi * self.diam * self.diam / 4.0
self.volume = self.area * self.le
self.exchange_area = pi * self.diam * self.le
m0 = self.p0 * self.volume / (medium.R_gas * self.T0)
m0 = medium.density(self.p0, self.T0) * self.volume
U0 = m0 * medium.specific_internal_energy(self.T0)
self.state = VolumeState(m=m0, U=U0)
initial_h = medium.specific_enthalpy(self.T0)
@@ -527,7 +516,7 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
cls,
*,
name: str,
medium: IdealGasMedium,
medium: GasMedium,
parameters: Mapping[str, float],
) -> "AmesimPnl0001":
return cls(
@@ -566,9 +555,8 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
return 0.0
return self.kth * self.exchange_area * (self.extemp - temperature)
@staticmethod
def _dynamic_viscosity(temperature_k: float) -> float:
return AmesimPnl00r._dynamic_viscosity(temperature_k)
def _dynamic_viscosity(self, temperature_k: float) -> float:
return self.medium.dynamic_viscosity(temperature_k)
def reynolds_number(self, mass_flow: float, temperature: float) -> float:
viscosity = self._dynamic_viscosity(temperature)
@@ -745,7 +733,7 @@ class AmesimPnl0002(AmesimPnl0001):
cls,
*,
name: str,
medium: IdealGasMedium,
medium: GasMedium,
parameters: Mapping[str, float],
) -> "AmesimPnl0002":
return cls(
@@ -943,7 +931,7 @@ class AmesimPnl0003(DynamicComponent):
def __init__(
self,
name: str,
medium: IdealGasMedium,
medium: GasMedium,
*,
diam: float = 0.01,
le: float = 1.0,
@@ -982,7 +970,7 @@ class AmesimPnl0003(DynamicComponent):
self.k = float(k)
self.kth = float(kth)
self.extemp = float(extemp)
self.gi = AmesimPnl0001._integer_parameter("gi", gi)
self.gi = normalize_amesim_gas_index(gi)
self.mode = AmesimPnl0001._integer_parameter("mode", mode)
self.area = pi * self.diam * self.diam / 4.0
self.volume = self.area * self.le
@@ -1004,13 +992,13 @@ class AmesimPnl0003(DynamicComponent):
cls,
*,
name: str,
medium: IdealGasMedium,
medium: GasMedium,
parameters: Mapping[str, float],
) -> "AmesimPnl0003":
return cls(name=name, medium=medium, **dict(parameters))
def _initial_state(self, pressure: float, temperature: float) -> VolumeState:
mass = pressure * self.compliance_volume / (self.medium.R_gas * temperature)
mass = self.medium.density(pressure, temperature) * self.compliance_volume
return VolumeState(m=mass, U=mass * self.medium.specific_internal_energy(temperature))
def get_state_vector(self) -> list[float]:
@@ -1040,9 +1028,8 @@ class AmesimPnl0003(DynamicComponent):
def refresh_thermodynamic_ports(self) -> tuple[ThermodynamicProperties, ThermodynamicProperties]:
return self.properties_1(), self.properties_2()
@staticmethod
def _dynamic_viscosity(temperature_k: float) -> float:
return AmesimPnl00r._dynamic_viscosity(temperature_k)
def _dynamic_viscosity(self, temperature_k: float) -> float:
return self.medium.dynamic_viscosity(temperature_k)
def reynolds_number(self, mass_flow: float, temperature: float) -> float:
viscosity = self._dynamic_viscosity(temperature)