对齐AMESim管阻孔口与储气耦合
This commit is contained in:
1 parent
7671418582
commit
6abcc220de
14 files changed
+817
-65
No files matched your search
@@ -51,13 +51,12 @@ _PNVO001_FLOW_COEFFICIENT_GROUP = ParameterGroupDisplaySpec(
|
||||
class AmesimPnor001(AlgebraicComponent):
|
||||
"""AMESim PNOR001 constant-flow-coefficient pneumatic orifice.
|
||||
|
||||
This public component preserves the PNOR001 catalog/XML contract and uses a
|
||||
finite bidirectional compressible-orifice approximation. The Siemens
|
||||
`pn2rcqfix_` details remain a later calibration target.
|
||||
This public component preserves the PNOR001 catalog/XML contract and uses
|
||||
real-gas pressure-ratio flow with AMESim-style near-equal-pressure smoothing.
|
||||
"""
|
||||
|
||||
MODEL_TYPE = "amesim_pnor001"
|
||||
MODEL_VERSION = "0.2.0"
|
||||
MODEL_VERSION = "0.3.0"
|
||||
PORTS = (
|
||||
PortDefinition.pneumatic("port_1", nominal_role="bidirectional"),
|
||||
PortDefinition.pneumatic("port_2", nominal_role="bidirectional"),
|
||||
@@ -188,6 +187,7 @@ class AmesimPnor001(AlgebraicComponent):
|
||||
self.port_1.h_outflow = initial_h
|
||||
self.port_2 = self.register_declared_port("port_2")
|
||||
self.port_2.h_outflow = initial_h
|
||||
self._connected_h: dict[str, float] = {}
|
||||
|
||||
@staticmethod
|
||||
def _integer_parameter(name: str, value: float) -> int:
|
||||
@@ -243,16 +243,113 @@ class AmesimPnor001(AlgebraicComponent):
|
||||
|
||||
def _upstream_temperature(self, port_name: str) -> float:
|
||||
port = self.get_port(port_name)
|
||||
inlet_h = self._connected_h.get(port_name, port.h_outflow)
|
||||
return max(
|
||||
self.medium.temperature_from_pressure_enthalpy(
|
||||
max(port.p, 1.0),
|
||||
port.h_outflow,
|
||||
inlet_h,
|
||||
),
|
||||
1.0,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _subsonic_mass_flow_parameter(
|
||||
*,
|
||||
pressure_ratio: float,
|
||||
gamma_s: float,
|
||||
density: float,
|
||||
upstream_temperature: float,
|
||||
upstream_pressure: float,
|
||||
) -> float:
|
||||
expansion = (
|
||||
pressure_ratio ** (2.0 * gamma_s)
|
||||
- pressure_ratio ** (1.0 + gamma_s)
|
||||
)
|
||||
return sqrt(
|
||||
max(
|
||||
2.0
|
||||
/ (1.0 - gamma_s)
|
||||
* density
|
||||
* upstream_temperature
|
||||
/ upstream_pressure
|
||||
* expansion,
|
||||
0.0,
|
||||
)
|
||||
)
|
||||
|
||||
def _one_way_flow_characteristics(
|
||||
self,
|
||||
*,
|
||||
upstream_pressure: float,
|
||||
downstream_pressure: float,
|
||||
upstream_temperature: float,
|
||||
) -> tuple[float, float]:
|
||||
p_up = max(upstream_pressure, 1.0)
|
||||
p_down = max(min(downstream_pressure, p_up), 0.0)
|
||||
T_up = max(upstream_temperature, 1.0)
|
||||
gamma_s = self.medium.isentropic_density_pressure_factor(
|
||||
p_up,
|
||||
T_up,
|
||||
p_down,
|
||||
)
|
||||
gamma_s = min(max(gamma_s, 1.0e-9), 1.0 - 1.0e-9)
|
||||
density = max(self.medium.density(p_up, T_up), 1.0e-12)
|
||||
pressure_ratio = max(p_down / p_up, 0.0)
|
||||
critical_ratio = (2.0 * gamma_s / (gamma_s + 1.0)) ** (
|
||||
1.0 / (1.0 - gamma_s)
|
||||
)
|
||||
if pressure_ratio <= critical_ratio:
|
||||
effective_pressure_ratio = critical_ratio
|
||||
mass_flow_parameter = (
|
||||
sqrt(2.0 / (1.0 + gamma_s) * density * T_up / p_up)
|
||||
* (2.0 * gamma_s / (gamma_s + 1.0))
|
||||
** (gamma_s / (1.0 - gamma_s))
|
||||
)
|
||||
gas_velocity = sqrt(2.0 / (1.0 + gamma_s) * p_up / density)
|
||||
else:
|
||||
effective_pressure_ratio = pressure_ratio
|
||||
mass_flow_parameter = self._subsonic_mass_flow_parameter(
|
||||
pressure_ratio=pressure_ratio,
|
||||
gamma_s=gamma_s,
|
||||
density=density,
|
||||
upstream_temperature=T_up,
|
||||
upstream_pressure=p_up,
|
||||
)
|
||||
gas_velocity = sqrt(
|
||||
max(
|
||||
2.0
|
||||
/ (1.0 - gamma_s)
|
||||
* p_up
|
||||
/ density
|
||||
* (1.0 - pressure_ratio ** (1.0 - gamma_s)),
|
||||
0.0,
|
||||
)
|
||||
)
|
||||
|
||||
reference = self._subsonic_mass_flow_parameter(
|
||||
pressure_ratio=_PN_PRESSURE_RATIO_ACCURACY,
|
||||
gamma_s=gamma_s,
|
||||
density=density,
|
||||
upstream_temperature=T_up,
|
||||
upstream_pressure=p_up,
|
||||
)
|
||||
if mass_flow_parameter > 0.0 and reference > 0.0:
|
||||
argument = (
|
||||
_PN_LAMINAR_SMOOTHING_GAIN
|
||||
* abs(mass_flow_parameter / reference)
|
||||
* log(effective_pressure_ratio)
|
||||
/ log(_PN_PRESSURE_RATIO_ACCURACY)
|
||||
)
|
||||
smoothing_factor = tanh(max(argument, 0.0))
|
||||
mass_flow_parameter *= smoothing_factor
|
||||
gas_velocity *= smoothing_factor
|
||||
return mass_flow_parameter, gas_velocity
|
||||
|
||||
def mass_flow(self, p_1: float, p_2: float) -> float:
|
||||
if p_1 == p_2 or self.effective_area == 0.0:
|
||||
if (
|
||||
isclose(p_1, p_2, rel_tol=0.0, abs_tol=1.0e-8)
|
||||
or self.effective_area == 0.0
|
||||
):
|
||||
return 0.0
|
||||
if p_1 > p_2:
|
||||
return self._one_way_mass_flow(
|
||||
@@ -274,43 +371,41 @@ class AmesimPnor001(AlgebraicComponent):
|
||||
upstream_temperature: float,
|
||||
) -> float:
|
||||
p_up = max(upstream_pressure, 1.0)
|
||||
p_down = max(min(downstream_pressure, p_up), 0.0)
|
||||
T_up = max(upstream_temperature, 1.0)
|
||||
gamma = max(self.medium.gamma, 1.000001)
|
||||
pressure_ratio = max(p_down / p_up, 0.0)
|
||||
critical_ratio = (2.0 / (gamma + 1.0)) ** (gamma / (gamma - 1.0))
|
||||
if pressure_ratio <= critical_ratio:
|
||||
flow_factor = sqrt(gamma / (self.medium.R_gas * T_up)) * (
|
||||
2.0 / (gamma + 1.0)
|
||||
) ** ((gamma + 1.0) / (2.0 * (gamma - 1.0)))
|
||||
else:
|
||||
expansion = pressure_ratio ** (2.0 / gamma) - pressure_ratio ** (
|
||||
(gamma + 1.0) / gamma
|
||||
)
|
||||
flow_factor = sqrt(
|
||||
max(
|
||||
2.0
|
||||
* gamma
|
||||
* expansion
|
||||
/ (self.medium.R_gas * T_up * (gamma - 1.0)),
|
||||
0.0,
|
||||
)
|
||||
)
|
||||
return self.effective_cq * self.effective_area * p_up * flow_factor
|
||||
mass_flow_parameter, _ = self._one_way_flow_characteristics(
|
||||
upstream_pressure=p_up,
|
||||
downstream_pressure=downstream_pressure,
|
||||
upstream_temperature=T_up,
|
||||
)
|
||||
return (
|
||||
self.effective_cq
|
||||
* self.effective_area
|
||||
* p_up
|
||||
* mass_flow_parameter
|
||||
/ sqrt(T_up)
|
||||
)
|
||||
|
||||
def component_result_values(self) -> Mapping[str, float]:
|
||||
p_1 = max(self.port_1.p, 1.0)
|
||||
p_2 = max(self.port_2.p, 1.0)
|
||||
m_flow = abs(self.mass_flow(self.port_1.p, self.port_2.p))
|
||||
upstream_pressure = max(p_1, p_2)
|
||||
upstream_temperature = self._upstream_temperature(
|
||||
"port_1" if p_1 >= p_2 else "port_2"
|
||||
if p_1 >= p_2:
|
||||
upstream_port_name = "port_1"
|
||||
upstream_pressure = p_1
|
||||
downstream_pressure = p_2
|
||||
flow_direction = 1.0
|
||||
else:
|
||||
upstream_port_name = "port_2"
|
||||
upstream_pressure = p_2
|
||||
downstream_pressure = p_1
|
||||
flow_direction = -1.0
|
||||
mass_flow_parameter, gas_velocity = self._one_way_flow_characteristics(
|
||||
upstream_pressure=upstream_pressure,
|
||||
downstream_pressure=downstream_pressure,
|
||||
upstream_temperature=self._upstream_temperature(upstream_port_name),
|
||||
)
|
||||
density = max(self.medium.density(upstream_pressure, upstream_temperature), 1.0e-12)
|
||||
area = max(self.effective_area, 1.0e-18)
|
||||
return {
|
||||
"cm": m_flow / (self.effective_cq * area * upstream_pressure),
|
||||
"gasvel": m_flow / (density * area),
|
||||
"cm": mass_flow_parameter,
|
||||
"gasvel": flow_direction * gas_velocity,
|
||||
}
|
||||
|
||||
def pressure_flow_equation_residuals(self) -> tuple[EquationResidual, ...]:
|
||||
@@ -344,6 +439,7 @@ class AmesimPnor001(AlgebraicComponent):
|
||||
)
|
||||
|
||||
def update_stream_outflows(self, connected_h: Mapping[str, float]) -> None:
|
||||
self._connected_h = dict(connected_h)
|
||||
self.port_1.h_outflow = connected_h["port_2"]
|
||||
self.port_2.h_outflow = connected_h["port_1"]
|
||||
|
||||
@@ -593,7 +689,10 @@ class AmesimPnvo001FixedOpening(AlgebraicComponent):
|
||||
)
|
||||
|
||||
def mass_flow(self, p_2: float, p_3: float) -> float:
|
||||
if p_2 == p_3 or self.effective_area == 0.0:
|
||||
if (
|
||||
isclose(p_2, p_3, rel_tol=1.0e-7, abs_tol=1.0e-9)
|
||||
or self.effective_area == 0.0
|
||||
):
|
||||
return 0.0
|
||||
if p_2 > p_3:
|
||||
return self._one_way_mass_flow(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from math import isclose, log10, pi, sqrt
|
||||
from math import isclose, log, log10, pi, sqrt, tanh
|
||||
|
||||
from app.simulation.components.amesim.gases import (
|
||||
AMESIM_GAS_INDEX_PARAMETER,
|
||||
@@ -246,7 +246,7 @@ class AmesimPnl00r(AlgebraicComponent):
|
||||
while self.darcy_pressure_drop(upper, density=density, temperature=temperature) < pressure_drop:
|
||||
upper *= 10.0
|
||||
if upper > 1.0e3:
|
||||
raise ValueError("unable to bracket PNL00R resistance flow")
|
||||
return 1.0e3
|
||||
lower = 0.0
|
||||
for _ in range(48):
|
||||
middle = 0.5 * (lower + upper)
|
||||
@@ -257,7 +257,7 @@ class AmesimPnl00r(AlgebraicComponent):
|
||||
return 0.5 * (lower + upper)
|
||||
|
||||
def mass_flow(self, p_1: float, p_2: float) -> float:
|
||||
if p_1 == p_2:
|
||||
if isclose(p_1, p_2, rel_tol=1.0e-7, abs_tol=1.0e-9):
|
||||
return 0.0
|
||||
pressure_difference = p_1 - p_2
|
||||
upstream_pressure = max(p_1, p_2, 1.0)
|
||||
@@ -645,7 +645,7 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
|
||||
) < pressure_drop:
|
||||
upper *= 10.0
|
||||
if upper > 1.0e3:
|
||||
raise ValueError("unable to bracket PNL0001 resistance flow")
|
||||
return 1.0e3
|
||||
lower = 0.0
|
||||
for _ in range(48):
|
||||
middle = 0.5 * (lower + upper)
|
||||
@@ -659,16 +659,126 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
|
||||
upper = middle
|
||||
return 0.5 * (lower + upper)
|
||||
|
||||
def _one_way_pn2pipefr_mass_flow(
|
||||
self,
|
||||
*,
|
||||
upstream_pressure: float,
|
||||
downstream_pressure: float,
|
||||
upstream_temperature: float,
|
||||
resistance_length: float,
|
||||
) -> float:
|
||||
"""AMESim pn2pipefr-style compressible friction flow."""
|
||||
|
||||
p_up = max(float(upstream_pressure), 1.0)
|
||||
p_down = max(min(float(downstream_pressure), p_up), 0.0)
|
||||
T_up = max(float(upstream_temperature), 1.0)
|
||||
if resistance_length <= 0.0:
|
||||
raise ValueError("Pipe resistance length must be positive.")
|
||||
|
||||
gamma_s = self.medium.isentropic_density_pressure_factor(
|
||||
p_up,
|
||||
T_up,
|
||||
p_down,
|
||||
)
|
||||
gamma_s = min(max(gamma_s, 1.0e-9), 1.0 - 1.0e-9)
|
||||
density = max(self.medium.density(p_up, T_up), 1.0e-12)
|
||||
pressure_ratio = max(p_down / p_up, 0.0)
|
||||
critical_ratio = (2.0 * gamma_s / (gamma_s + 1.0)) ** (
|
||||
1.0 / (1.0 - gamma_s)
|
||||
)
|
||||
|
||||
def mass_flow_parameter(ratio: float) -> float:
|
||||
if ratio <= critical_ratio:
|
||||
value = (
|
||||
sqrt(2.0 / (1.0 + gamma_s) * density * T_up / p_up)
|
||||
* (2.0 * gamma_s / (gamma_s + 1.0))
|
||||
** (gamma_s / (1.0 - gamma_s))
|
||||
)
|
||||
effective_ratio = critical_ratio
|
||||
else:
|
||||
expansion = ratio ** (2.0 * gamma_s) - ratio ** (1.0 + gamma_s)
|
||||
value = sqrt(
|
||||
max(
|
||||
2.0
|
||||
/ (1.0 - gamma_s)
|
||||
* density
|
||||
* T_up
|
||||
/ p_up
|
||||
* expansion,
|
||||
0.0,
|
||||
)
|
||||
)
|
||||
effective_ratio = ratio
|
||||
|
||||
accuracy = 0.9999
|
||||
reference_expansion = (
|
||||
accuracy ** (2.0 * gamma_s)
|
||||
- accuracy ** (1.0 + gamma_s)
|
||||
)
|
||||
reference = sqrt(
|
||||
max(
|
||||
2.0
|
||||
/ (1.0 - gamma_s)
|
||||
* density
|
||||
* T_up
|
||||
/ p_up
|
||||
* reference_expansion,
|
||||
0.0,
|
||||
)
|
||||
)
|
||||
if value > 0.0 and reference > 0.0:
|
||||
smoothing_argument = (
|
||||
12.0
|
||||
* abs(value / reference)
|
||||
* log(effective_ratio)
|
||||
/ log(accuracy)
|
||||
)
|
||||
value *= tanh(max(smoothing_argument, 0.0))
|
||||
return value
|
||||
|
||||
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)
|
||||
)
|
||||
return (
|
||||
flow_coefficient
|
||||
* self.area
|
||||
* p_up
|
||||
* mass_flow_parameter(pressure_ratio)
|
||||
/ sqrt(T_up)
|
||||
)
|
||||
|
||||
flow_coefficient = sqrt(self.diam / (resistance_length * 0.02))
|
||||
magnitude = (
|
||||
flow_coefficient
|
||||
* self.area
|
||||
* p_up
|
||||
* mass_flow_parameter(pressure_ratio)
|
||||
/ sqrt(T_up)
|
||||
)
|
||||
for _iteration in range(16):
|
||||
next_magnitude = target_flow(magnitude)
|
||||
if abs(next_magnitude - magnitude) <= max(
|
||||
1.0e-12,
|
||||
abs(magnitude) * 1.0e-9,
|
||||
):
|
||||
return next_magnitude
|
||||
magnitude = 0.5 * (magnitude + next_magnitude)
|
||||
return magnitude
|
||||
|
||||
def mass_flow(self, p_1: float, p_2: float, temperature: float) -> float:
|
||||
if p_1 == p_2:
|
||||
if isclose(p_1, p_2, rel_tol=0.0, abs_tol=1.0e-8):
|
||||
return 0.0
|
||||
pressure_difference = p_1 - p_2
|
||||
upstream_pressure = max(p_1, p_2, 1.0)
|
||||
density = max(self.medium.density(upstream_pressure, temperature), 1.0e-12)
|
||||
magnitude = self._mass_flow_for_pressure_drop(
|
||||
abs(pressure_difference),
|
||||
density=density,
|
||||
temperature=temperature,
|
||||
upstream_temperature = max(float(temperature), 1.0)
|
||||
resistance_length = getattr(self, "resistance_length", self.le)
|
||||
magnitude = self._one_way_pn2pipefr_mass_flow(
|
||||
upstream_pressure=max(p_1, p_2),
|
||||
downstream_pressure=min(p_1, p_2),
|
||||
upstream_temperature=upstream_temperature,
|
||||
resistance_length=resistance_length,
|
||||
)
|
||||
return magnitude if pressure_difference > 0.0 else -magnitude
|
||||
|
||||
@@ -1115,7 +1225,7 @@ class AmesimPnl0003(DynamicComponent):
|
||||
while self.darcy_pressure_drop(upper, density=density, temperature=temperature) < pressure_drop:
|
||||
upper *= 10.0
|
||||
if upper > 1.0e3:
|
||||
raise ValueError("unable to bracket PNL0003 resistance flow")
|
||||
return 1.0e3
|
||||
lower = 0.0
|
||||
for _ in range(48):
|
||||
middle = 0.5 * (lower + upper)
|
||||
@@ -1129,7 +1239,7 @@ class AmesimPnl0003(DynamicComponent):
|
||||
port_1 = self._properties(self.state_1)
|
||||
port_2 = self._properties(self.state_2)
|
||||
pressure_difference = port_1.p - port_2.p
|
||||
if pressure_difference == 0.0:
|
||||
if isclose(port_1.p, port_2.p, rel_tol=1.0e-7, abs_tol=1.0e-9):
|
||||
return 0.0
|
||||
upstream = port_1 if pressure_difference > 0.0 else port_2
|
||||
magnitude = self._mass_flow_for_pressure_drop(
|
||||
|
||||
Reference in new issue
Block a user