完善 PNL00R 摩擦模型与八路回归

This commit is contained in:
huojiarong committed 2026-08-18 09:30:29 +00:00
1 parent 684d28752a
commit f725f038b6
10 files changed
+498 -98

No files matched your search

+257 -9
View File
@@ -2,8 +2,12 @@ from __future__ import annotations
import unittest
from app.simulation.components.amesim.flow.pipes import AmesimPnl00r
from app.simulation.components.amesim.flow.pipes import (
AmesimPnl0001,
AmesimPnl00r,
)
from app.simulation.components.amesim.junctions.nodes import AmesimPn3Node2
from app.simulation.components.amesim.media.mediums import AmesimHeliumPengRobinsonMedium
from app.simulation.components.experimental.storage.cylinder import Cylinder
from app.simulation.components.experimental.storage.tank import Tank
from app.simulation.core.medium import IdealGasMedium
@@ -94,12 +98,189 @@ class AmesimPnl00rComponentTests(unittest.TestCase):
self.assertGreater(above, below)
self.assertLess(reverse, 0.0)
self.assertAlmostEqual(below, -reverse, delta=abs(below) * 1.0e-9)
self.assertLess(abs(above - below), abs(above) * 0.05)
self.assertLess(abs(above - below), abs(above) * 0.10)
self.assertEqual(pipe.mass_flow(base_pressure, base_pressure), 0.0)
self.assertGreater(pipe.mass_flow(15.3e6 + 0.1, 15.3e6), 0.0)
def test_matches_amesim_pn2pipefr_laminar_baselines(self) -> None:
medium = AmesimHeliumPengRobinsonMedium()
pipe = AmesimPnl00r(
"pnl_1",
medium,
diam=0.014,
le=1.0,
rr=0.045 / 14.0,
)
samples = (
(
13_887_929.5734,
288.959589373,
13_887_887.6633,
288.959239289,
1.79075554200e-4,
838.404977868,
3.32483767035e-6,
0.0521569680484,
0.076335424633,
),
(
13_093_276.1461,
285.548411921,
13_093_238.0341,
285.546325432,
1.40922897107e-4,
665.123640745,
3.09743309373e-6,
0.0429212663992,
0.0962227112065,
),
(
11_690_210.3512,
272.892693485,
11_690_180.6138,
272.889527291,
7.10081729578e-5,
345.615999811,
2.3706370264e-6,
0.0230685613948,
0.185176612295,
),
)
def test_pressure_drop_inversion_cache_keys_all_property_inputs(self) -> None:
for p_1, T_1, p_2, T_2, mass_flow, re, cm, velocity, ff in samples:
with self.subTest(pressure=p_1):
pipe.port_1.p = p_1
pipe.port_2.p = p_2
pipe.update_stream_outflows(
{
"port_1": medium.specific_enthalpy_at_pressure(p_1, T_1),
"port_2": medium.specific_enthalpy_at_pressure(p_2, T_2),
}
)
actual_mass_flow = pipe.mass_flow(p_1, p_2)
results = pipe.component_result_values()
self.assertAlmostEqual(actual_mass_flow / mass_flow, 1.0, delta=0.005)
self.assertAlmostEqual(results["re"] / re, 1.0, delta=0.01)
self.assertAlmostEqual(results["cm"] / cm, 1.0, delta=1.0e-4)
self.assertAlmostEqual(results["v"] / velocity, 1.0, delta=0.005)
self.assertAlmostEqual(results["ff"] / ff, 1.0, delta=0.01)
def test_matches_amesim_pn2pipefr_transition_baselines(self) -> None:
medium = AmesimHeliumPengRobinsonMedium()
pipe = AmesimPnl00r(
"pnl_1",
medium,
diam=0.014,
le=1.0,
rr=0.045 / 14.0,
)
# Unchanged test_mql_1.ame, pneumatic_105, ascending transition at
# t=0.2862..0.3047 s. AMESim dm1 is positive into port 1 while this
# component's port-1-to-port-2 coordinate is negative for these rows.
samples = (
(
10_631_986.90667049,
262.71180639527233,
10_632_026.310978588,
262.7092388107747,
-2.8192909824026485e-4,
1408.0846374820405,
5.030700878807581e-6,
-0.09668817162408228,
0.04545209089983021,
),
(
10_594_171.934888396,
262.32910587014925,
10_594_213.825687861,
262.3266782251236,
-3.598950492898518e-4,
1799.2599253847663,
5.723572909300966e-6,
-0.1236749841566422,
0.03590026408283139,
),
(
10_554_721.69137201,
261.9257072357455,
10_554_766.280823693,
261.92350790162175,
-4.3973197060195834e-4,
2200.69088342098,
6.529418698258151e-6,
-0.1514267390526478,
0.03111112083131272,
),
(
10_544_953.846352266,
261.82512637406904,
10_544_999.25593169,
261.82299814935993,
-4.592053720488228e-4,
2298.7460389094877,
6.783006948981891e-6,
-0.15821443006863944,
0.03074226487938153,
),
(
10_514_807.25092133,
261.5128728347395,
10_514_855.822238008,
261.5109955483938,
-5.190048780471613e-4,
2600.199197308607,
7.798343300840328e-6,
-0.17910295287093314,
0.03166638237616583,
),
(
10_432_760.829010766,
260.6482173603332,
10_432_818.817992153,
260.6472539768074,
-6.759582533840764e-4,
3394.136683451109,
1.1253055264735043e-5,
-0.23427502517225277,
0.03839469933871734,
),
)
for p_1, T_1, p_2, T_2, mass_flow, re, cm, velocity, ff in samples:
with self.subTest(reynolds=re):
pipe.port_1.p = p_1
pipe.port_2.p = p_2
pipe.update_stream_outflows(
{
"port_1": medium.specific_enthalpy_at_pressure(p_1, T_1),
"port_2": medium.specific_enthalpy_at_pressure(p_2, T_2),
}
)
actual_mass_flow = pipe.mass_flow(p_1, p_2)
results = pipe.component_result_values()
self.assertAlmostEqual(actual_mass_flow / mass_flow, 1.0, delta=0.006)
self.assertAlmostEqual(results["re"] / re, 1.0, delta=0.012)
self.assertAlmostEqual(results["cm"] / cm, 1.0, delta=1.0e-4)
self.assertAlmostEqual(results["v"] / velocity, 1.0, delta=0.006)
self.assertAlmostEqual(results["ff"] / ff, 1.0, delta=0.012)
def test_upstream_temperature_uses_same_side_connected_stream(self) -> None:
pipe = AmesimPnl00r("pnl_1", self.medium)
h_1 = self.medium.specific_enthalpy(250.0)
h_2 = self.medium.specific_enthalpy(400.0)
pipe.update_stream_outflows({"port_1": h_1, "port_2": h_2})
self.assertAlmostEqual(pipe._port_temperature("port_1"), 250.0)
self.assertAlmostEqual(pipe._port_temperature("port_2"), 400.0)
self.assertEqual(pipe.port_1.h_outflow, h_2)
self.assertEqual(pipe.port_2.h_outflow, h_1)
def test_pn2pipefr_cache_keys_all_property_inputs(self) -> None:
pipe = AmesimPnl00r(
"pnl_1",
self.medium,
@@ -113,12 +294,12 @@ class AmesimPnl00rComponentTests(unittest.TestCase):
"port_2": self.medium.specific_enthalpy(300.0),
}
)
pipe._mass_flow_for_pressure_drop.cache_clear()
AmesimPnl0001._one_way_pn2pipefr_mass_flow.cache_clear()
first = pipe.mass_flow(501000.0, 500000.0)
after_first = pipe._mass_flow_for_pressure_drop.cache_info()
after_first = AmesimPnl0001._one_way_pn2pipefr_mass_flow.cache_info()
second = pipe.mass_flow(501000.0, 500000.0)
after_second = pipe._mass_flow_for_pressure_drop.cache_info()
after_second = AmesimPnl0001._one_way_pn2pipefr_mass_flow.cache_info()
pipe.update_flow_temperature_references(
{
"port_1": self.medium.specific_enthalpy(400.0),
@@ -126,7 +307,7 @@ class AmesimPnl00rComponentTests(unittest.TestCase):
}
)
third = pipe.mass_flow(501000.0, 500000.0)
after_temperature_change = pipe._mass_flow_for_pressure_drop.cache_info()
after_temperature_change = AmesimPnl0001._one_way_pn2pipefr_mass_flow.cache_info()
self.assertEqual(first, second)
self.assertEqual(after_first.misses, 1)
@@ -345,6 +526,10 @@ class AmesimPnl00rComponentTests(unittest.TestCase):
actual_flow,
upstream_temperature,
)
friction = pipe.friction_factor(expected_reynolds)
flow_coefficient = (
pipe.diam / (pipe.le * friction)
) ** 0.5
self.assertAlmostEqual(results["re"], expected_reynolds)
self.assertAlmostEqual(
results["v"],
@@ -354,11 +539,11 @@ class AmesimPnl00rComponentTests(unittest.TestCase):
results["cm"],
abs(actual_flow)
* upstream_temperature**0.5
/ (pipe.area * upstream_pressure),
/ (flow_coefficient * pipe.area * upstream_pressure),
)
self.assertAlmostEqual(
results["ff"],
min(pipe.friction_factor(expected_reynolds), 64_000_000.0),
min(friction, 64_000_000.0),
)
def test_friction_factor_transitions_from_laminar_to_turbulent(self) -> None:
@@ -405,6 +590,69 @@ class AmesimPnl00rComponentTests(unittest.TestCase):
delta=8.0e-5,
)
def test_friction_factor_matches_amesim_transition_sweep(self) -> None:
pipe = AmesimPnl00r(
"pnl_14mm",
self.medium,
diam=0.014,
rr=0.045 / 14.0,
)
samples = (
(1408.0846374820405, 0.04545209089983021),
(1594.9147224775163, 0.04022919421391556),
(1799.2599253847663, 0.03590026408283139),
(2001.1290261016431, 0.03286496043823008),
(2200.69088342098, 0.03111112083131272),
(2298.7460389094877, 0.03074226487938153),
(2396.165041443924, 0.030707477592933765),
(2600.199197308607, 0.03166638237616583),
(2795.6649128003255, 0.03349648426341731),
(2996.892872668051, 0.035593529418766444),
(3204.685791989844, 0.037354280559382634),
(3394.136683451109, 0.03839469933871734),
)
for reynolds, expected in samples:
with self.subTest(reynolds=reynolds):
self.assertAlmostEqual(
pipe.friction_factor(reynolds) / expected,
1.0,
delta=0.0015,
)
def test_mass_flow_is_monotone_through_transition(self) -> None:
medium = AmesimHeliumPengRobinsonMedium()
pipe = AmesimPnl00r(
"pnl_14mm",
medium,
diam=0.014,
le=1.0,
rr=0.045 / 14.0,
)
mean_pressure = 10.6e6
temperature = 262.0
enthalpy = medium.specific_enthalpy_at_pressure(mean_pressure, temperature)
pipe.update_flow_temperature_references(
{"port_1": enthalpy, "port_2": enthalpy}
)
forward: list[float] = []
reverse: list[float] = []
for index in range(221):
pressure_drop = 25.0 + index * 0.25
high = mean_pressure + 0.5 * pressure_drop
low = mean_pressure - 0.5 * pressure_drop
forward.append(pipe.mass_flow(high, low))
reverse.append(-pipe.mass_flow(low, high))
self.assertLess(pipe.reynolds_number(forward[0], temperature), 1400.0)
self.assertGreater(
pipe.reynolds_number(forward[-1], temperature), 3400.0
)
self.assertTrue(all(left < right for left, right in zip(forward, forward[1:])))
for forward_flow, reverse_flow in zip(forward, reverse):
self.assertAlmostEqual(forward_flow, reverse_flow, delta=1.0e-12)
def test_friction_factor_matches_amesim_transition_regime(self) -> None:
pipe = AmesimPnl00r(
"pnl_20mm",