同步远端 PNL0003 诊断和大采样网格能力,语义合并活动感知的 60 秒真停滞判定与旧后端 15 分钟兼容兜底。 纳管热路径优化、15 单元运行证据、浏览器与 API 报告,并补充北京时间更新日志和遗留问题。
190 lines
5.5 KiB
Python
190 lines
5.5 KiB
Python
from __future__ import annotations
|
|
|
|
from collections.abc import Mapping
|
|
from math import pi
|
|
|
|
from app.simulation.core.base import AlgebraicComponent
|
|
from app.simulation.core.catalog import ComponentDisplaySpec, PortDisplaySpec
|
|
from app.simulation.core.equations import EquationResidual
|
|
from app.simulation.core.metadata import ParameterDefinition
|
|
from app.simulation.core.medium import IdealGasMedium
|
|
from app.simulation.core.ports import PortDefinition
|
|
|
|
|
|
class ResistivePipe(AlgebraicComponent):
|
|
"""Quasi-steady Darcy resistance used by topology-driven simulation."""
|
|
|
|
MODEL_TYPE = "pipe"
|
|
MODEL_VERSION = "1.0.0"
|
|
PRESSURE_FLOW_DEPENDS_ON_STREAM = False
|
|
PRESSURE_FLOW_EXACT_SUM_TO_ZERO_EQUATION_SUFFIXES = frozenset(
|
|
("mass_flow_balance",)
|
|
)
|
|
PORTS = (
|
|
PortDefinition.pneumatic("port_a", nominal_role="inlet"),
|
|
PortDefinition.pneumatic("port_b", nominal_role="outlet"),
|
|
)
|
|
PARAMETERS = (
|
|
ParameterDefinition(
|
|
"length",
|
|
5.0,
|
|
label="长度",
|
|
quantity="length",
|
|
unit="m",
|
|
minimum=0.0,
|
|
minimum_exclusive=True,
|
|
),
|
|
ParameterDefinition(
|
|
"diameter",
|
|
0.02,
|
|
label="直径",
|
|
quantity="length",
|
|
unit="m",
|
|
minimum=0.0,
|
|
minimum_exclusive=True,
|
|
),
|
|
ParameterDefinition(
|
|
"lambda_darcy",
|
|
0.02,
|
|
label="摩阻系数",
|
|
minimum=0.0,
|
|
),
|
|
ParameterDefinition(
|
|
"p0",
|
|
1e5,
|
|
label="初始压力",
|
|
quantity="pressure",
|
|
unit="Pa",
|
|
minimum=0.0,
|
|
minimum_exclusive=True,
|
|
),
|
|
ParameterDefinition(
|
|
"T0",
|
|
300.0,
|
|
label="初始温度",
|
|
quantity="temperature",
|
|
unit="K",
|
|
minimum=0.0,
|
|
minimum_exclusive=True,
|
|
),
|
|
)
|
|
RESULT_VARIABLES = ()
|
|
DISPLAY = ComponentDisplaySpec(
|
|
label="管段",
|
|
library_id="experimental",
|
|
category_id="flow",
|
|
symbol="pipe",
|
|
ports=(
|
|
PortDisplaySpec("port_a", "left", order=10),
|
|
PortDisplaySpec("port_b", "right", order=20),
|
|
),
|
|
order=30,
|
|
)
|
|
|
|
def __init__(
|
|
self,
|
|
name: str,
|
|
medium: IdealGasMedium,
|
|
L: float = 5.0,
|
|
D: float = 0.02,
|
|
lambda_darcy: float = 0.02,
|
|
p0: float = 1e5,
|
|
T0: float = 300.0,
|
|
) -> None:
|
|
super().__init__(name=name)
|
|
self.set_parameter_values(
|
|
{
|
|
"length": L,
|
|
"diameter": D,
|
|
"lambda_darcy": lambda_darcy,
|
|
"p0": p0,
|
|
"T0": T0,
|
|
}
|
|
)
|
|
self.medium = medium
|
|
self.L = L
|
|
self.D = D
|
|
self.lambda_darcy = lambda_darcy
|
|
self.p0 = p0
|
|
self.T0 = T0
|
|
self.area = pi * D * D / 4.0
|
|
initial_h = medium.specific_enthalpy(T0)
|
|
|
|
self.port_a = self.register_declared_port("port_a")
|
|
self.port_a.p = p0
|
|
self.port_a.h_outflow = initial_h
|
|
|
|
self.port_b = self.register_declared_port("port_b")
|
|
self.port_b.p = p0
|
|
self.port_b.h_outflow = initial_h
|
|
|
|
@classmethod
|
|
def create(
|
|
cls,
|
|
*,
|
|
name: str,
|
|
medium: IdealGasMedium,
|
|
parameters: Mapping[str, float],
|
|
) -> ResistivePipe:
|
|
return cls(
|
|
name=name,
|
|
medium=medium,
|
|
L=parameters["length"],
|
|
D=parameters["diameter"],
|
|
lambda_darcy=parameters["lambda_darcy"],
|
|
p0=parameters["p0"],
|
|
T0=parameters["T0"],
|
|
)
|
|
|
|
def pressure_drop(self, m_flow_a: float, p_a: float, p_b: float) -> float:
|
|
average_pressure = max(0.5 * (p_a + p_b), 1.0)
|
|
density = max(self.medium.density(average_pressure, self.T0), 1e-12)
|
|
resistance = self.lambda_darcy * (self.L / self.D)
|
|
return (
|
|
resistance
|
|
* m_flow_a
|
|
* abs(m_flow_a)
|
|
/ (2.0 * density * self.area * self.area)
|
|
)
|
|
|
|
def pressure_flow_equation_residuals(self) -> tuple[EquationResidual, ...]:
|
|
return (
|
|
EquationResidual(
|
|
id=f"{self.name}:mass_flow_balance",
|
|
owner="component",
|
|
owner_id=self.name,
|
|
relation="sumToZero",
|
|
variables=(
|
|
f"{self.name}.port_a.m_flow",
|
|
f"{self.name}.port_b.m_flow",
|
|
),
|
|
role="flow",
|
|
value=self.port_a.m_flow + self.port_b.m_flow,
|
|
),
|
|
EquationResidual(
|
|
id=f"{self.name}:darcy_pressure_loss",
|
|
owner="component",
|
|
owner_id=self.name,
|
|
relation="constitutive",
|
|
variables=(
|
|
f"{self.name}.port_a.p",
|
|
f"{self.name}.port_b.p",
|
|
f"{self.name}.port_a.m_flow",
|
|
),
|
|
role="effort",
|
|
value=(
|
|
self.port_a.p
|
|
- self.port_b.p
|
|
- self.pressure_drop(
|
|
self.port_a.m_flow,
|
|
self.port_a.p,
|
|
self.port_b.p,
|
|
)
|
|
),
|
|
),
|
|
)
|
|
|
|
def update_stream_outflows(self, connected_h: Mapping[str, float]) -> None:
|
|
self.port_a.h_outflow = connected_h["port_b"]
|
|
self.port_b.h_outflow = connected_h["port_a"]
|