fix: align PNL0003 Reynolds diagnostic with AMESim

This commit is contained in:
huojiarong committed 2026-08-19 10:40:57 +00:00
1 parent 60b743dd81
commit 27f9f4add8
5 files changed
+100 -3

No files matched your search

+12 -1
View File
@@ -1667,6 +1667,16 @@ class AmesimPnl0003(DynamicComponent):
viscosity = self._dynamic_viscosity(temperature) viscosity = self._dynamic_viscosity(temperature)
return 4.0 * abs(mass_flow) / (pi * self.diam * viscosity) return 4.0 * abs(mass_flow) / (pi * self.diam * viscosity)
def reported_reynolds_number(
self,
mass_flow: float,
temperature: float,
) -> float:
"""Return AMESim's derived re without changing pipe dynamics."""
viscosity = self.medium.diagnostic_dynamic_viscosity(temperature)
return 4.0 * abs(mass_flow) / (pi * self.diam * viscosity)
def friction_factor(self, reynolds_number: float) -> float: def friction_factor(self, reynolds_number: float) -> float:
return AmesimPnl00r.friction_factor(self, reynolds_number) return AmesimPnl00r.friction_factor(self, reynolds_number)
@@ -1734,6 +1744,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)
reported_reynolds = self.reported_reynolds_number(center_flow, upstream.T)
friction = self.friction_factor(reynolds) friction = self.friction_factor(reynolds)
return { return {
"m1": self.state_1.m, "m1": self.state_1.m,
@@ -1751,7 +1762,7 @@ class AmesimPnl0003(DynamicComponent):
"u2": port_2.u, "u2": port_2.u,
"h2": port_2.h, "h2": port_2.h,
"dmctr": center_flow, "dmctr": center_flow,
"re": reynolds, "re": reported_reynolds,
"cm": _bare_pipe_mass_flow_parameter( "cm": _bare_pipe_mass_flow_parameter(
center_flow, center_flow,
area=self.area, area=self.area,
@@ -2,7 +2,7 @@ from __future__ import annotations
from collections.abc import Callable, Sequence from collections.abc import Callable, Sequence
from dataclasses import dataclass from dataclasses import dataclass
from math import isfinite from math import exp, isfinite, log
from typing import ClassVar from typing import ClassVar
from app.simulation.core.errors import RecoverableTrialStateError from app.simulation.core.errors import RecoverableTrialStateError
@@ -55,6 +55,12 @@ class AmesimHeliumPengRobinsonMedium(IdealGasMedium):
fluid: ClassVar[PengRobinsonFluid] = HELIUM_PR fluid: ClassVar[PengRobinsonFluid] = HELIUM_PR
nasa_cp_over_R: ClassVar[float] = 2.5 nasa_cp_over_R: ClassVar[float] = 2.5
nasa_enthalpy_constant_K: ClassVar[float] = -745.375 nasa_enthalpy_constant_K: ClassVar[float] = -745.375
nasa_viscosity_coefficients: ClassVar[tuple[float, float, float, float]] = (
0.7501594,
35.76324,
-2212.129,
0.9212635,
)
name: str = "AMESimHeliumPengRobinson" name: str = "AMESimHeliumPengRobinson"
R_gas: float = HELIUM_PR.specific_gas_constant R_gas: float = HELIUM_PR.specific_gas_constant
@@ -73,6 +79,19 @@ class AmesimHeliumPengRobinsonMedium(IdealGasMedium):
del T del T
return self.cv return self.cv
def diagnostic_dynamic_viscosity(self, T: float) -> float:
"""Return the AMESim NASA-table viscosity used by pipe diagnostics.
pn2pipefr reports Reynolds number with sagum viscosity. Keep this
separate from dynamic_viscosity so matching that diagnostic cannot
alter the already-validated pipe flow or friction dynamics.
"""
if T <= 0.0:
raise ValueError("Temperature must be positive.")
a, b, c, d = self.nasa_viscosity_coefficients
return 1.0e-7 * exp(a * log(T) + b / T + c / (T * T) + d)
@profile_property("density") @profile_property("density")
@cache_property_calculation("density") @cache_property_calculation("density")
def density(self, p: float, T: float) -> float: def density(self, p: float, T: float) -> float:
+12
View File
@@ -81,6 +81,8 @@ class GasMedium(Protocol):
def dynamic_viscosity(self, T: float) -> float: ... def dynamic_viscosity(self, T: float) -> float: ...
def diagnostic_dynamic_viscosity(self, T: float) -> float: ...
def specific_internal_energy(self, T: float) -> float: ... def specific_internal_energy(self, T: float) -> float: ...
def specific_internal_energy_at_pressure(self, p: float, T: float) -> float: ... def specific_internal_energy_at_pressure(self, p: float, T: float) -> float: ...
@@ -182,6 +184,16 @@ class IdealGasMedium:
/ (T + self.sutherland_constant) / (T + self.sutherland_constant)
) )
def diagnostic_dynamic_viscosity(self, T: float) -> float:
"""Return the viscosity convention used by derived diagnostics.
Most media use the same transport property for dynamics and reported
diagnostics. Reference-library media may override this without
changing a calibrated constitutive flow relation.
"""
return self.dynamic_viscosity(T)
@profile_property("specific_internal_energy") @profile_property("specific_internal_energy")
def specific_internal_energy(self, T: float) -> float: def specific_internal_energy(self, T: float) -> float:
delta_T = T - self.T_ref delta_T = T - self.T_ref
+25
View File
@@ -72,8 +72,33 @@ class AmesimHeliumPengRobinsonMediumTests(unittest.TestCase):
temperature, temperature,
) )
self.assertAlmostEqual(medium.dynamic_viscosity(293.15), 1.96e-5) self.assertAlmostEqual(medium.dynamic_viscosity(293.15), 1.96e-5)
self.assertAlmostEqual(
medium.diagnostic_dynamic_viscosity(293.15),
1.9616132203938165e-5,
)
self.assertEqual(medium.sutherland_constant, 79.4) self.assertEqual(medium.sutherland_constant, 79.4)
def test_pipe_diagnostic_viscosity_reproduces_amesim_nasa_table(self) -> None:
medium = AmesimHeliumPengRobinsonMedium()
self.assertEqual(
medium.nasa_viscosity_coefficients,
(0.7501594, 35.76324, -2212.129, 0.9212635),
)
self.assertAlmostEqual(
medium.diagnostic_dynamic_viscosity(233.92077861710192),
1.683135986478913e-5,
delta=1.0e-19,
)
self.assertNotAlmostEqual(
medium.diagnostic_dynamic_viscosity(233.92077861710192),
medium.dynamic_viscosity(233.92077861710192),
delta=1.0e-8,
)
with self.assertRaises(ValueError):
medium.diagnostic_dynamic_viscosity(0.0)
def test_pressure_transport_enthalpy_includes_peng_robinson_departure( def test_pressure_transport_enthalpy_includes_peng_robinson_departure(
self, self,
) -> None: ) -> None:
+31 -1
View File
@@ -1,9 +1,12 @@
from __future__ import annotations from __future__ import annotations
import unittest import unittest
from math import log10, sqrt from math import log10, pi, sqrt
from app.simulation.components.amesim.flow.pipes import AmesimPnl0002, AmesimPnl0003 from app.simulation.components.amesim.flow.pipes import AmesimPnl0002, AmesimPnl0003
from app.simulation.components.amesim.media.mediums import (
AmesimHeliumPengRobinsonMedium,
)
from app.simulation.core.medium import IdealGasMedium from app.simulation.core.medium import IdealGasMedium
from app.simulation.registry import COMPONENT_MODEL_REGISTRY from app.simulation.registry import COMPONENT_MODEL_REGISTRY
@@ -462,6 +465,33 @@ class AmesimPnl0003ComponentTests(unittest.TestCase):
self.assertIn("dmctr", values) self.assertIn("dmctr", values)
self.assertIn("re", values) self.assertIn("re", values)
def test_reported_reynolds_uses_amesim_helium_nasa_viscosity_only(self) -> None:
medium = AmesimHeliumPengRobinsonMedium()
pipe = AmesimPnl0003("pnl_3", medium, diam=0.02)
mass_flow = 0.04555349965141288
upstream_temperature = 233.92077861710192
# Frozen AMESim dense row: t=0.81 s, branch 2, D=0.02 m.
expected_amesim_reynolds = 172298.96343252173
reported = pipe.reported_reynolds_number(
mass_flow,
upstream_temperature,
)
dynamic = pipe.reynolds_number(mass_flow, upstream_temperature)
self.assertAlmostEqual(reported, expected_amesim_reynolds, delta=1.0e-9)
self.assertNotAlmostEqual(reported, dynamic, delta=1.0)
self.assertAlmostEqual(
dynamic,
4.0
* abs(mass_flow)
/ (
pi
* pipe.diam
* medium.dynamic_viscosity(upstream_temperature)
),
)
def test_result_cm_excludes_center_resistance_flow_coefficient(self) -> None: def test_result_cm_excludes_center_resistance_flow_coefficient(self) -> None:
pipe = AmesimPnl0003( pipe = AmesimPnl0003(
"pnl_3", "pnl_3",