对齐Amesim氦气PR物性与PNVO流量
This commit is contained in:
1 parent
18d9802f03
commit
046aa49814
13 files changed
+730
-73
No files matched your search
@@ -4,7 +4,11 @@ from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import ClassVar
|
||||
|
||||
from app.simulation.core.medium import GasMedium, IdealGasMedium
|
||||
from app.simulation.core.medium import (
|
||||
GasMedium,
|
||||
IdealGasMedium,
|
||||
ThermodynamicProperties,
|
||||
)
|
||||
from app.simulation.core.peng_robinson import HELIUM_PR, PengRobinsonFluid
|
||||
|
||||
|
||||
@@ -36,18 +40,19 @@ class AmesimHeliumPengRobinsonMedium(IdealGasMedium):
|
||||
"""AMESim helium with a Peng-Robinson mechanical equation of state.
|
||||
|
||||
The pressure-density-temperature relation is evaluated by the shared
|
||||
``HELIUM_PR`` fluid. The first public AMESim port keeps the committed
|
||||
constant-heat-capacity caloric model so it can be consumed through the
|
||||
same :class:`GasMedium` contract as ideal-gas air.
|
||||
``HELIUM_PR`` fluid. The caloric reference follows the constant NASA
|
||||
polynomial from Simcenter Amesim 2404 ``helium_cp_h_s.data``.
|
||||
"""
|
||||
|
||||
SUBSTANCE_ID: ClassVar[str] = "helium"
|
||||
PROPERTY_METHOD_ID: ClassVar[str] = "peng_robinson"
|
||||
fluid: ClassVar[PengRobinsonFluid] = HELIUM_PR
|
||||
nasa_cp_over_R: ClassVar[float] = 2.5
|
||||
nasa_enthalpy_constant_K: ClassVar[float] = -745.375
|
||||
|
||||
name: str = "AMESimHeliumPengRobinson"
|
||||
R_gas: float = HELIUM_PR.specific_gas_constant
|
||||
cp_ref: float = 5193.0
|
||||
cp_ref: float = nasa_cp_over_R * HELIUM_PR.specific_gas_constant
|
||||
T_ref: float = 293.15
|
||||
cp_slope: float = 0.0
|
||||
viscosity_ref: float = 1.96e-5
|
||||
@@ -56,7 +61,7 @@ class AmesimHeliumPengRobinsonMedium(IdealGasMedium):
|
||||
|
||||
@property
|
||||
def cv(self) -> float:
|
||||
return 3116.0
|
||||
return (self.nasa_cp_over_R - 1.0) * self.R_gas
|
||||
|
||||
def cv_at_temperature(self, T: float) -> float:
|
||||
del T
|
||||
@@ -65,11 +70,201 @@ class AmesimHeliumPengRobinsonMedium(IdealGasMedium):
|
||||
def density(self, p: float, T: float) -> float:
|
||||
return self.fluid.density(p, T)
|
||||
|
||||
def _real_heat_capacities(
|
||||
self,
|
||||
p: float,
|
||||
T: float,
|
||||
) -> tuple[float, float, float, float, float]:
|
||||
density = self.density(p, T)
|
||||
pressure_density_derivative = (
|
||||
self.fluid.pressure_density_derivative_at_temperature(
|
||||
T,
|
||||
density,
|
||||
)
|
||||
)
|
||||
pressure_temperature_derivative = (
|
||||
self.fluid.pressure_temperature_derivative_at_density(
|
||||
T,
|
||||
density,
|
||||
)
|
||||
)
|
||||
cv = (
|
||||
self.cv_at_temperature(T)
|
||||
+ self.fluid.residual_isochoric_heat_capacity_at_density(T, density)
|
||||
)
|
||||
cp = (
|
||||
cv
|
||||
+ T
|
||||
* pressure_temperature_derivative
|
||||
* pressure_temperature_derivative
|
||||
/ (density * density * pressure_density_derivative)
|
||||
)
|
||||
if cp <= 0.0 or cv <= 0.0:
|
||||
raise ValueError("Real-gas heat capacities must be positive.")
|
||||
return (
|
||||
cp,
|
||||
cv,
|
||||
density,
|
||||
pressure_density_derivative,
|
||||
pressure_temperature_derivative,
|
||||
)
|
||||
|
||||
def _local_isentropic_density_pressure_factor(
|
||||
self,
|
||||
p: float,
|
||||
T: float,
|
||||
) -> tuple[float, float]:
|
||||
cp, cv, density, pressure_density_derivative, pressure_temperature_derivative = (
|
||||
self._real_heat_capacities(p, T)
|
||||
)
|
||||
heat_capacity_ratio = cp / cv
|
||||
factor = p / (
|
||||
density * pressure_density_derivative * heat_capacity_ratio
|
||||
)
|
||||
exponent = (
|
||||
p
|
||||
* (heat_capacity_ratio - 1.0)
|
||||
/ (
|
||||
heat_capacity_ratio
|
||||
* T
|
||||
* pressure_temperature_derivative
|
||||
)
|
||||
)
|
||||
return factor, exponent
|
||||
|
||||
def isentropic_density_pressure_factor(
|
||||
self,
|
||||
p: float,
|
||||
T: float,
|
||||
downstream_pressure: float | None = None,
|
||||
) -> float:
|
||||
upstream_factor, isentropic_temperature_exponent = (
|
||||
self._local_isentropic_density_pressure_factor(p, T)
|
||||
)
|
||||
if downstream_pressure is None or downstream_pressure >= p:
|
||||
return upstream_factor
|
||||
|
||||
pressure_ratio = max(downstream_pressure / p, 1.0e-12)
|
||||
isentropic_temperature = max(
|
||||
T * pressure_ratio**isentropic_temperature_exponent,
|
||||
2.2,
|
||||
)
|
||||
downstream_factor, _unused_exponent = (
|
||||
self._local_isentropic_density_pressure_factor(
|
||||
max(downstream_pressure, 1.0),
|
||||
isentropic_temperature,
|
||||
)
|
||||
)
|
||||
# AMESim 2404 saggs_ evaluates the local factor at the upstream
|
||||
# state and at an approximate isentropic downstream state.
|
||||
return 0.5 * (upstream_factor + downstream_factor)
|
||||
|
||||
def pressure(self, m: float, T: float, V: float) -> float:
|
||||
if V <= 0.0:
|
||||
raise ValueError("Volume must stay positive.")
|
||||
return self.fluid.pressure_from_density(T, m / V)
|
||||
|
||||
def specific_internal_energy(self, T: float) -> float:
|
||||
return self.R_gas * (
|
||||
(self.nasa_cp_over_R - 1.0) * T
|
||||
+ self.nasa_enthalpy_constant_K
|
||||
)
|
||||
|
||||
def specific_internal_energy_at_pressure(self, p: float, T: float) -> float:
|
||||
density = self.density(p, T)
|
||||
return (
|
||||
self.specific_internal_energy(T)
|
||||
+ self.fluid.residual_specific_internal_energy_at_density(T, density)
|
||||
)
|
||||
|
||||
def specific_enthalpy(self, T: float) -> float:
|
||||
return self.R_gas * (
|
||||
self.nasa_cp_over_R * T
|
||||
+ self.nasa_enthalpy_constant_K
|
||||
)
|
||||
|
||||
def specific_enthalpy_at_pressure(self, p: float, T: float) -> float:
|
||||
return self.specific_enthalpy(T) + self.fluid.residual_specific_enthalpy(p, T)
|
||||
|
||||
def temperature_from_internal_energy(self, u: float) -> float:
|
||||
return (
|
||||
u / self.R_gas - self.nasa_enthalpy_constant_K
|
||||
) / (self.nasa_cp_over_R - 1.0)
|
||||
|
||||
def temperature_from_enthalpy(self, h: float) -> float:
|
||||
return (
|
||||
h / self.R_gas - self.nasa_enthalpy_constant_K
|
||||
) / self.nasa_cp_over_R
|
||||
|
||||
def temperature_from_pressure_enthalpy(self, p: float, h: float) -> float:
|
||||
temperature = max(self.temperature_from_enthalpy(h), 2.2)
|
||||
for _iteration in range(16):
|
||||
residual_enthalpy = self.fluid.residual_specific_enthalpy(p, temperature)
|
||||
next_temperature = max(
|
||||
self.temperature_from_enthalpy(h - residual_enthalpy),
|
||||
2.2,
|
||||
)
|
||||
if abs(next_temperature - temperature) <= 1.0e-10 * max(
|
||||
temperature,
|
||||
1.0,
|
||||
):
|
||||
return next_temperature
|
||||
temperature = next_temperature
|
||||
return temperature
|
||||
|
||||
def temperature_from_mass_internal_energy(self, m: float, U: float) -> float:
|
||||
if m <= 0.0:
|
||||
raise ValueError("Mass must stay positive when recovering temperature.")
|
||||
return self.temperature_from_internal_energy(U / m)
|
||||
|
||||
def properties_from_mU(
|
||||
self,
|
||||
m: float,
|
||||
U: float,
|
||||
V: float,
|
||||
) -> ThermodynamicProperties:
|
||||
if m <= 0.0:
|
||||
raise ValueError("Mass must stay positive when recovering temperature.")
|
||||
if V <= 0.0:
|
||||
raise ValueError("Volume must stay positive.")
|
||||
density = m / V
|
||||
target_internal_energy = U / m
|
||||
temperature = max(
|
||||
self.temperature_from_internal_energy(target_internal_energy),
|
||||
2.2,
|
||||
)
|
||||
for _iteration in range(16):
|
||||
residual_internal_energy = (
|
||||
self.fluid.residual_specific_internal_energy_at_density(
|
||||
temperature,
|
||||
density,
|
||||
)
|
||||
)
|
||||
next_temperature = max(
|
||||
self.temperature_from_internal_energy(
|
||||
target_internal_energy - residual_internal_energy
|
||||
),
|
||||
2.2,
|
||||
)
|
||||
if abs(next_temperature - temperature) <= 1.0e-10 * max(
|
||||
temperature,
|
||||
1.0,
|
||||
):
|
||||
temperature = next_temperature
|
||||
break
|
||||
temperature = next_temperature
|
||||
pressure = self.fluid.pressure_from_density(temperature, density)
|
||||
return ThermodynamicProperties(
|
||||
p=pressure,
|
||||
T=temperature,
|
||||
rho=density,
|
||||
u=target_internal_energy,
|
||||
h=self.specific_enthalpy_at_pressure(
|
||||
pressure,
|
||||
temperature,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AmesimGasPropertyModelSpec:
|
||||
|
||||
Reference in new issue
Block a user