修正动态管与阀门诊断输出

This commit is contained in:
huojiarong committed 2026-08-18 10:24:29 +00:00
1 parent f725f038b6
commit 53f8601fec
6 files changed
+219 -16

No files matched your search

@@ -35,6 +35,7 @@ _FLOWSET_USES_CV = (ParameterCondition("flowset", (2.0,)),)
_FLOWSET_USES_KV = (ParameterCondition("flowset", (3.0,)),) _FLOWSET_USES_KV = (ParameterCondition("flowset", (3.0,)),)
_PN_PRESSURE_RATIO_ACCURACY = 0.9999 _PN_PRESSURE_RATIO_ACCURACY = 0.9999
_PN_LAMINAR_SMOOTHING_GAIN = 12.0 _PN_LAMINAR_SMOOTHING_GAIN = 12.0
_PNVO001_CLOSED_OPENING_ABS_TOL = 1.0e-12
_PNOR001_FLOW_COEFFICIENT_GROUP = ParameterGroupDisplaySpec( _PNOR001_FLOW_COEFFICIENT_GROUP = ParameterGroupDisplaySpec(
id="flow_coefficient", id="flow_coefficient",
label="流量系数", label="流量系数",
@@ -835,6 +836,16 @@ class AmesimPnvo001FixedOpening(AlgebraicComponent):
downstream_pressure=downstream_pressure, downstream_pressure=downstream_pressure,
upstream_temperature=upstream_temperature, upstream_temperature=upstream_temperature,
) )
# AMESim reports no vena-contracta velocity while the valve is closed.
# Signal propagation around a step can leave a round-off-sized opening,
# so apply the same numerical-zero convention to this diagnostic only.
if isclose(
self.opening,
0.0,
rel_tol=0.0,
abs_tol=_PNVO001_CLOSED_OPENING_ABS_TOL,
):
gas_velocity = 0.0
return { return {
"xv": self.opening, "xv": self.opening,
"cm": mass_flow_parameter, "cm": mass_flow_parameter,
+55 -15
View File
@@ -75,6 +75,31 @@ def _reported_friction_factor(value: float) -> float:
return min(float(value), _MAX_REPORTED_FRICTION_FACTOR) return min(float(value), _MAX_REPORTED_FRICTION_FACTOR)
def _bare_pipe_mass_flow_parameter(
mass_flow: float,
*,
area: float,
diameter: float,
upstream_pressure: float,
upstream_temperature: float,
resistance_length: float,
friction_factor: float,
) -> float:
"""Return Amesim's ``Cm`` without the pipe flow coefficient ``Cq``."""
flow_coefficient = sqrt(
diameter / (resistance_length * friction_factor)
)
return (
abs(mass_flow)
* sqrt(upstream_temperature)
/ max(
flow_coefficient * area * upstream_pressure,
1.0e-18,
)
)
_DYNAMIC_PIPE_POLYTROPIC_MODE = ParameterCondition("mode", (1.0,)) _DYNAMIC_PIPE_POLYTROPIC_MODE = ParameterCondition("mode", (1.0,))
_DYNAMIC_PIPE_HEAT_EXCHANGE_MODE = ParameterCondition("mode", (2.0,)) _DYNAMIC_PIPE_HEAT_EXCHANGE_MODE = ParameterCondition("mode", (2.0,))
_DYNAMIC_PIPE_PARAMETER_GROUPS = ( _DYNAMIC_PIPE_PARAMETER_GROUPS = (
@@ -1014,6 +1039,7 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
upstream_pressure = max(self.port_1.p, props.p, 1.0) upstream_pressure = max(self.port_1.p, props.p, 1.0)
density = max(self.medium.density(upstream_pressure, props.T), 1.0e-12) density = max(self.medium.density(upstream_pressure, props.T), 1.0e-12)
reynolds = self.reynolds_number(flow, props.T) reynolds = self.reynolds_number(flow, props.T)
friction = self.friction_factor(reynolds)
return { return {
"m": self.state.m, "m": self.state.m,
"U": self.state.U, "U": self.state.U,
@@ -1023,13 +1049,17 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
"u": props.u, "u": props.u,
"h": props.h, "h": props.h,
"re": reynolds, "re": reynolds,
"cm": ( "cm": _bare_pipe_mass_flow_parameter(
abs(flow) flow,
* sqrt(props.T) area=self.area,
/ max(self.area * upstream_pressure, 1.0e-18) diameter=self.diam,
upstream_pressure=upstream_pressure,
upstream_temperature=props.T,
resistance_length=self.le,
friction_factor=friction,
), ),
"v": flow / (density * self.area), "v": flow / (density * self.area),
"ff": _reported_friction_factor(self.friction_factor(reynolds)), "ff": _reported_friction_factor(friction),
} }
def pressure_flow_equation_values(self) -> tuple[float, ...]: def pressure_flow_equation_values(self) -> tuple[float, ...]:
@@ -1344,16 +1374,21 @@ class AmesimPnl0002(AmesimPnl0001):
1.0e-12, 1.0e-12,
) )
reynolds = self.reynolds_number(flow, upstream_temperature) reynolds = self.reynolds_number(flow, upstream_temperature)
friction = self.friction_factor(reynolds)
resistance_diagnostics.append( resistance_diagnostics.append(
( (
reynolds, reynolds,
( _bare_pipe_mass_flow_parameter(
abs(flow) flow,
* sqrt(upstream_temperature) area=self.area,
/ max(self.area * upstream_pressure, 1.0e-18) diameter=self.diam,
upstream_pressure=upstream_pressure,
upstream_temperature=upstream_temperature,
resistance_length=self.resistance_length,
friction_factor=friction,
), ),
abs(flow) / (density * self.area), abs(flow) / (density * self.area),
self.friction_factor(reynolds), friction,
) )
) )
reynolds, cm, velocity, friction = ( reynolds, cm, velocity, friction = (
@@ -1699,6 +1734,7 @@ class AmesimPnl0003(DynamicComponent):
center_flow = self.resistance_mass_flow() center_flow = self.resistance_mass_flow()
upstream = port_1 if center_flow >= 0.0 else port_2 upstream = port_1 if center_flow >= 0.0 else port_2
reynolds = self.reynolds_number(center_flow, upstream.T) reynolds = self.reynolds_number(center_flow, upstream.T)
friction = self.friction_factor(reynolds)
return { return {
"m1": self.state_1.m, "m1": self.state_1.m,
"U1": self.state_1.U, "U1": self.state_1.U,
@@ -1716,13 +1752,17 @@ class AmesimPnl0003(DynamicComponent):
"h2": port_2.h, "h2": port_2.h,
"dmctr": center_flow, "dmctr": center_flow,
"re": reynolds, "re": reynolds,
"cm": ( "cm": _bare_pipe_mass_flow_parameter(
abs(center_flow) center_flow,
* sqrt(upstream.T) area=self.area,
/ max(self.area * max(port_1.p, port_2.p, 1.0), 1.0e-18) diameter=self.diam,
upstream_pressure=max(port_1.p, port_2.p, 1.0),
upstream_temperature=upstream.T,
resistance_length=self.le,
friction_factor=friction,
), ),
"v": center_flow / (max(upstream.rho, 1.0e-12) * self.area), "v": center_flow / (max(upstream.rho, 1.0e-12) * self.area),
"ff": _reported_friction_factor(self.friction_factor(reynolds)), "ff": _reported_friction_factor(friction),
} }
def pressure_flow_equation_values(self) -> tuple[float, ...]: def pressure_flow_equation_values(self) -> tuple[float, ...]:
+28
View File
@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
import unittest import unittest
from math import sqrt
from app.simulation.components.amesim.flow.pipes import AmesimPnl0001 from app.simulation.components.amesim.flow.pipes import AmesimPnl0001
from app.simulation.core.medium import IdealGasMedium from app.simulation.core.medium import IdealGasMedium
@@ -148,6 +149,33 @@ class AmesimPnl0001ComponentTests(unittest.TestCase):
{"m", "U", "p", "T", "rho", "u", "h", "re", "cm", "v", "ff"}, {"m", "U", "p", "T", "rho", "u", "h", "re", "cm", "v", "ff"},
) )
def test_result_cm_excludes_pipe_flow_coefficient(self) -> None:
pipe = AmesimPnl0001(
"pnl_1",
self.medium,
diam=0.014,
le=0.3,
rr=0.045 / 14.0,
p0=2.5e6,
T0=290.0,
)
pipe.port_1.p = 1.6e6
props = pipe.properties()
flow = pipe.mass_flow(pipe.port_1.p, props.p, props.T)
reynolds = pipe.reynolds_number(flow, props.T)
friction = pipe.friction_factor(reynolds)
raw_cq_cm = (
abs(flow)
* sqrt(props.T)
/ (pipe.area * max(pipe.port_1.p, props.p))
)
flow_coefficient = sqrt(pipe.diam / (pipe.le * friction))
actual = pipe.component_result_values()["cm"]
self.assertAlmostEqual(actual, raw_cq_cm / flow_coefficient)
self.assertNotAlmostEqual(actual, raw_cq_cm)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
+104 -1
View File
@@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
import unittest import unittest
from math import log10 from math import log10, sqrt
from app.simulation.components.amesim.flow.pipes import AmesimPnl0002, AmesimPnl0003 from app.simulation.components.amesim.flow.pipes import AmesimPnl0002, AmesimPnl0003
from app.simulation.core.medium import IdealGasMedium from app.simulation.core.medium import IdealGasMedium
@@ -200,6 +200,79 @@ class AmesimPnl0002ComponentTests(unittest.TestCase):
self.assertAlmostEqual(derivative[0], 0.3) self.assertAlmostEqual(derivative[0], 0.3)
self.assertAlmostEqual(derivative[1], 0.3 * props.h) self.assertAlmostEqual(derivative[1], 0.3 * props.h)
def test_result_cm_averages_bare_parameter_for_each_resistance(self) -> None:
pipe = AmesimPnl0002(
"pnl_2",
self.medium,
diam=0.02,
le=2.0,
rr=0.045 / 20.0,
p0=100000.0,
T0=350.0,
)
center = pipe.properties()
pipe.port_1.p = 130000.0
pipe.port_2.p = 80000.0
pipe.update_flow_temperature_references(
{
"port_1": self.medium.specific_enthalpy_at_pressure(
pipe.port_1.p,
250.0,
),
"port_2": self.medium.specific_enthalpy_at_pressure(
pipe.port_2.p,
450.0,
),
}
)
raw_parameters: list[float] = []
bare_parameters: list[float] = []
frictions: list[float] = []
for port_name, port in (
("port_1", pipe.port_1),
("port_2", pipe.port_2),
):
flow = pipe.port_mass_flow(
port.p,
center.p,
center.T,
port_name=port_name,
)
if flow >= 0.0:
upstream_pressure = port.p
upstream_temperature = pipe.medium.temperature_from_pressure_enthalpy(
port.p,
pipe._connected_h[port_name],
)
else:
upstream_pressure = center.p
upstream_temperature = center.T
reynolds = pipe.reynolds_number(flow, upstream_temperature)
friction = pipe.friction_factor(reynolds)
raw = (
abs(flow)
* sqrt(upstream_temperature)
/ (pipe.area * upstream_pressure)
)
flow_coefficient = sqrt(
pipe.diam / (pipe.resistance_length * friction)
)
raw_parameters.append(raw)
bare_parameters.append(raw / flow_coefficient)
frictions.append(friction)
expected = sum(bare_parameters) / 2.0
collapsed = (sum(raw_parameters) / 2.0) / sqrt(
pipe.diam
/ (pipe.resistance_length * (sum(frictions) / 2.0))
)
actual = pipe.component_result_values()["cm"]
self.assertAlmostEqual(actual, expected)
self.assertGreater(abs(actual - collapsed), 1.0e-8)
class AmesimPnl0003ComponentTests(unittest.TestCase): class AmesimPnl0003ComponentTests(unittest.TestCase):
def setUp(self) -> None: def setUp(self) -> None:
@@ -389,6 +462,36 @@ class AmesimPnl0003ComponentTests(unittest.TestCase):
self.assertIn("dmctr", values) self.assertIn("dmctr", values)
self.assertIn("re", values) self.assertIn("re", values)
def test_result_cm_excludes_center_resistance_flow_coefficient(self) -> None:
pipe = AmesimPnl0003(
"pnl_3",
self.medium,
diam=0.02,
le=0.3,
rr=0.045 / 20.0,
p1_0=10.7e6,
T1_0=263.4,
p2_0=10.68e6,
T2_0=263.42,
)
port_1 = pipe.properties_1()
port_2 = pipe.properties_2()
flow = pipe.resistance_mass_flow()
upstream = port_1 if flow >= 0.0 else port_2
reynolds = pipe.reynolds_number(flow, upstream.T)
friction = pipe.friction_factor(reynolds)
raw_cq_cm = (
abs(flow)
* sqrt(upstream.T)
/ (pipe.area * max(port_1.p, port_2.p))
)
flow_coefficient = sqrt(pipe.diam / (pipe.le * friction))
actual = pipe.component_result_values()["cm"]
self.assertAlmostEqual(actual, raw_cq_cm / flow_coefficient)
self.assertNotAlmostEqual(actual, raw_cq_cm)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
@@ -54,6 +54,25 @@ class AmesimPnvo001FixedOpeningComponentTests(unittest.TestCase):
self.assertEqual(valve.mass_flow(500000.0, 100000.0), 0.0) self.assertEqual(valve.mass_flow(500000.0, 100000.0), 0.0)
def test_closed_opening_only_zeroes_reported_gas_velocity(self) -> None:
valve = AmesimPnvo001FixedOpening("valve_1", self.medium, opening=0.0)
valve.port_2.p = 500000.0
valve.port_3.p = 100000.0
closed = valve.component_result_values()
self.assertGreater(closed["cm"], 0.0)
self.assertEqual(closed["gasvel"], 0.0)
valve.opening = 7.0e-15
roundoff_closed = valve.component_result_values()
self.assertEqual(roundoff_closed["gasvel"], 0.0)
self.assertEqual(roundoff_closed["cm"], closed["cm"])
valve.opening = 1.0e-9
genuinely_open = valve.component_result_values()
self.assertNotEqual(genuinely_open["gasvel"], 0.0)
self.assertEqual(genuinely_open["cm"], closed["cm"])
def test_mass_flow_is_bidirectional_by_pressure_order(self) -> None: def test_mass_flow_is_bidirectional_by_pressure_order(self) -> None:
valve = AmesimPnvo001FixedOpening("valve_1", self.medium, opening=0.5) valve = AmesimPnvo001FixedOpening("valve_1", self.medium, opening=0.5)
+2
View File
@@ -244,6 +244,8 @@ class AmesimPnvo001SignalXmlTests(unittest.TestCase):
self.assertEqual(result["series"]["step_1.out.signal"][at_event], 1.0) self.assertEqual(result["series"]["step_1.out.signal"][at_event], 1.0)
self.assertEqual(result["series"]["valve_1.xv"][before_event], 0.0) self.assertEqual(result["series"]["valve_1.xv"][before_event], 0.0)
self.assertEqual(result["series"]["valve_1.xv"][at_event], 1.0) self.assertEqual(result["series"]["valve_1.xv"][at_event], 1.0)
self.assertEqual(result["series"]["valve_1.gasvel"][before_event], 0.0)
self.assertNotEqual(result["series"]["valve_1.gasvel"][at_event], 0.0)
self.assertGreater( self.assertGreater(
result["series"]["valve_1.port_2.m_flow"][at_event], result["series"]["valve_1.port_2.m_flow"][at_event],
0.45, 0.45,