整合求解器活动监控与步长回归证据

同步远端 PNL0003 诊断和大采样网格能力,语义合并活动感知的 60 秒真停滞判定与旧后端 15 分钟兼容兜底。

纳管热路径优化、15 单元运行证据、浏览器与 API 报告,并补充北京时间更新日志和遗留问题。
This commit is contained in:
lujingze committed 2026-08-19 16:24:31 +00:00
1 parent c19cf77aee
commit e18399c022
46 files changed
+181589 -170

No files matched your search

@@ -60,6 +60,9 @@ class AmesimPnor001(AlgebraicComponent):
MODEL_TYPE = "amesim_pnor001"
MODEL_VERSION = "0.3.0"
PRESSURE_FLOW_DEPENDS_ON_STREAM = True
PRESSURE_FLOW_EXACT_SUM_TO_ZERO_EQUATION_SUFFIXES = frozenset(
("mass_flow_balance",)
)
PORTS = (
PortDefinition.pneumatic("port_1", nominal_role="bidirectional"),
PortDefinition.pneumatic("port_2", nominal_role="bidirectional"),
@@ -465,6 +468,9 @@ class AmesimPnvo001FixedOpening(AlgebraicComponent):
MODEL_TYPE = "amesim_pnvo001_fixed"
MODEL_VERSION = "0.2.0"
PRESSURE_FLOW_DEPENDS_ON_STREAM = True
PRESSURE_FLOW_EXACT_SUM_TO_ZERO_EQUATION_SUFFIXES = frozenset(
("mass_flow_balance",)
)
PORTS = (
PortDefinition.pneumatic("port_2", nominal_role="bidirectional"),
PortDefinition.pneumatic("port_3", nominal_role="bidirectional"),
@@ -900,6 +906,10 @@ class AmesimPnvo001SignalOpening(AmesimPnvo001FixedOpening):
MODEL_TYPE = "amesim_pnvo001"
MODEL_VERSION = "0.2.0"
PRESSURE_FLOW_DEPENDS_ON_STREAM = True
# Repeat the exact-sum promise on this concrete subclass deliberately.
PRESSURE_FLOW_EXACT_SUM_TO_ZERO_EQUATION_SUFFIXES = frozenset(
("mass_flow_balance",)
)
PORTS = (
PortDefinition.signal("res", nominal_role="input"),
PortDefinition.pneumatic("port_2", nominal_role="bidirectional"),
+106 -14
View File
@@ -1,6 +1,6 @@
from __future__ import annotations
from collections.abc import Mapping, Sequence
from collections.abc import Callable, Mapping, Sequence
from dataclasses import dataclass
from functools import lru_cache
from math import isclose, isfinite, log, log10, pi, sqrt, tanh
@@ -71,6 +71,47 @@ _PN2PIPEFR_TRANSITION_SHARPNESS = 8.37293695
_PN2PIPEFR_ANALYTIC_LAMINAR_MAX_REYNOLDS = 1000.0
def _pn2pipefr_friction_factor_with_precomputed_fully_rough(
reynolds_number: float,
*,
relative_roughness: float,
fully_rough: float | None,
) -> float:
"""Evaluate the built-in friction law with its rr-only term supplied."""
if reynolds_number <= 0.0:
return 64_000_000.0
laminar = 64.0 / reynolds_number
if reynolds_number <= _PN2PIPEFR_TRANSITION_START_REYNOLDS:
return laminar
# Keep this arithmetic in the same order as AmesimPnl00r.friction_factor.
# The specialized fixed-point path only moves the rr-only logarithm out of
# the iteration; subclasses continue to use the public virtual method.
smooth_turbulent = 1.0 / (
-1.8 * log10(6.9 / reynolds_number)
) ** 2
if relative_roughness <= 0.0:
turbulent = smooth_turbulent
else:
assert fully_rough is not None
roughness_reynolds = reynolds_number * relative_roughness
roughness_reynolds_squared = roughness_reynolds * roughness_reynolds
roughness_weight = roughness_reynolds_squared / (
roughness_reynolds_squared + 180.0 * 180.0
)
turbulent = smooth_turbulent + roughness_weight * (
fully_rough - smooth_turbulent
)
transition_coordinate = (
(reynolds_number - _PN2PIPEFR_TRANSITION_START_REYNOLDS)
/ _PN2PIPEFR_TRANSITION_SCALE_REYNOLDS
)
transition_power = transition_coordinate**_PN2PIPEFR_TRANSITION_SHARPNESS
transition_weight = transition_power / (1.0 + transition_power)
return laminar + transition_weight * (turbulent - laminar)
def _reported_friction_factor(value: float) -> float:
return min(float(value), _MAX_REPORTED_FRICTION_FACTOR)
@@ -123,6 +164,9 @@ class AmesimPnl00r(AlgebraicComponent):
MODEL_TYPE = "amesim_pnl00r"
MODEL_VERSION = "0.3.0"
PRESSURE_FLOW_DEPENDS_ON_STREAM = True
PRESSURE_FLOW_EXACT_SUM_TO_ZERO_EQUATION_SUFFIXES = frozenset(
("mass_flow_balance",)
)
PORTS = (
PortDefinition.pneumatic("port_1", nominal_role="bidirectional"),
PortDefinition.pneumatic("port_2", nominal_role="bidirectional"),
@@ -876,11 +920,11 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
value *= tanh(max(smoothing_argument, 0.0))
return value
pressure_ratio_flow_parameter = mass_flow_parameter(pressure_ratio)
if analytic_laminar:
flow_parameter = mass_flow_parameter(pressure_ratio)
viscosity = self._dynamic_viscosity(T_up)
laminar_mass_flow = (
self.area * p_up * flow_parameter
self.area * p_up * pressure_ratio_flow_parameter
) ** 2 / (16.0 * pi * viscosity * resistance_length * T_up)
if (
self.reynolds_number(laminar_mass_flow, T_up)
@@ -888,27 +932,58 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
):
return laminar_mass_flow
sqrt_temperature = sqrt(T_up)
compiled_reynolds = (
type(self).reynolds_number is AmesimPnl0001.reynolds_number
and type(self)._dynamic_viscosity is AmesimPnl0001._dynamic_viscosity
)
reynolds_denominator = (
pi * self.diam * self._dynamic_viscosity(T_up)
if max_iterations > 0 and compiled_reynolds
else None
)
compiled_friction_factor = type(self) in (
AmesimPnl00r,
AmesimPnl0001,
AmesimPnl0002,
)
fully_rough = (
1.0 / (-2.0 * log10(self.rr / 3.7)) ** 2
if max_iterations > 0
and compiled_friction_factor
and self.rr > 0.0
else None
)
def target_flow(mass_flow: float) -> float:
reynolds = self.reynolds_number(mass_flow, T_up)
friction = self.friction_factor(reynolds)
flow_coefficient = sqrt(
self.diam / (resistance_length * friction)
reynolds = (
4.0 * abs(mass_flow) / reynolds_denominator
if reynolds_denominator is not None
else self.reynolds_number(mass_flow, T_up)
)
friction = (
_pn2pipefr_friction_factor_with_precomputed_fully_rough(
reynolds,
relative_roughness=self.rr,
fully_rough=fully_rough,
)
if compiled_friction_factor
else self.friction_factor(reynolds)
)
return (
flow_coefficient
sqrt(self.diam / (resistance_length * friction))
* self.area
* p_up
* mass_flow_parameter(pressure_ratio)
/ sqrt(T_up)
* pressure_ratio_flow_parameter
/ sqrt_temperature
)
flow_coefficient = sqrt(self.diam / (resistance_length * 0.02))
magnitude = (
flow_coefficient
sqrt(self.diam / (resistance_length * 0.02))
* self.area
* p_up
* mass_flow_parameter(pressure_ratio)
/ sqrt(T_up)
* pressure_ratio_flow_parameter
/ sqrt_temperature
)
for _iteration in range(max_iterations):
next_magnitude = target_flow(magnitude)
@@ -1069,6 +1144,23 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
self.port_1.m_flow - self.mass_flow(self.port_1.p, props.p, props.T),
)
def pressure_flow_equation_value_readers(
self,
) -> Mapping[str, Callable[[], float]]:
"""Expose the independently evaluable state-pressure residual."""
def pressure_state_residual() -> float:
props = self.medium.properties_from_mU(
self.state.m,
self.state.U,
self.volume,
)
return self.port_2.p - props.p
return {
f"{self.name}:port_2_pressure_state": pressure_state_residual,
}
def pressure_flow_equation_residuals(self) -> tuple[EquationResidual, ...]:
props = self.medium.properties_from_mU(self.state.m, self.state.U, self.volume)
return (
@@ -163,6 +163,9 @@ class AmesimPn3Node2(_AmesimPneumaticNode):
MODEL_TYPE = "amesim_pn3node2"
MODEL_VERSION = "0.3.0"
PRESSURE_FLOW_DEPENDS_ON_STREAM = False
PRESSURE_FLOW_EXACT_SUM_TO_ZERO_EQUATION_SUFFIXES = frozenset(
("mass_flow_balance",)
)
PORTS = (
PortDefinition.pneumatic("port_1", nominal_role="bidirectional"),
PortDefinition.pneumatic("port_2", nominal_role="bidirectional"),
@@ -200,6 +203,9 @@ class AmesimP4Node2(_AmesimPneumaticNode):
MODEL_TYPE = "amesim_p4node2"
MODEL_VERSION = "0.3.0"
PRESSURE_FLOW_DEPENDS_ON_STREAM = False
PRESSURE_FLOW_EXACT_SUM_TO_ZERO_EQUATION_SUFFIXES = frozenset(
("mass_flow_balance",)
)
PORTS = (
PortDefinition.pneumatic("port_1", nominal_role="bidirectional"),
PortDefinition.pneumatic("port_2", nominal_role="bidirectional"),
@@ -1386,6 +1386,9 @@ class AmesimLmechn1(AlgebraicComponent):
MODEL_TYPE = "amesim_lmechn1"
MODEL_VERSION = "0.2.0"
PRESSURE_FLOW_EXACT_SUM_TO_ZERO_EQUATION_SUFFIXES = frozenset(
("force_balance",)
)
PORTS = tuple(
PortDefinition.mechanical_translational(f"port_{index}")
for index in range(1, 22)
@@ -17,6 +17,9 @@ class Orifice(AlgebraicComponent):
MODEL_TYPE = "orifice"
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"),
@@ -116,4 +119,3 @@ class Orifice(AlgebraicComponent):
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"]
@@ -17,6 +17,9 @@ class ResistivePipe(AlgebraicComponent):
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"),
@@ -15,6 +15,9 @@ class Tee(AlgebraicComponent):
MODEL_TYPE = "tee"
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_in", nominal_role="bidirectional"),
PortDefinition.pneumatic("port_out1", nominal_role="bidirectional"),