feat: integrate AMESim media models and editor UI
This commit is contained in:
1 parent
3e4f466114
commit
e7177ab03e
181 files changed
+7042
-25270
No files matched your search
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Protocol
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -12,6 +13,53 @@ class ThermodynamicProperties:
|
||||
h: float
|
||||
|
||||
|
||||
class GasMedium(Protocol):
|
||||
"""Thermodynamic contract required by pneumatic components.
|
||||
|
||||
``IdealGasMedium`` is the default implementation. Keeping the component
|
||||
boundary structural allows a later helium/Peng-Robinson implementation to
|
||||
be registered without changing every AMESim component constructor.
|
||||
"""
|
||||
|
||||
name: str
|
||||
R_gas: float
|
||||
cp_ref: float
|
||||
T_ref: float
|
||||
|
||||
@property
|
||||
def cv(self) -> float: ...
|
||||
|
||||
@property
|
||||
def gamma(self) -> float: ...
|
||||
|
||||
def cp_at_temperature(self, T: float) -> float: ...
|
||||
|
||||
def cv_at_temperature(self, T: float) -> float: ...
|
||||
|
||||
def density(self, p: float, T: float) -> float: ...
|
||||
|
||||
def dynamic_viscosity(self, T: float) -> float: ...
|
||||
|
||||
def specific_internal_energy(self, T: float) -> float: ...
|
||||
|
||||
def specific_enthalpy(self, T: float) -> float: ...
|
||||
|
||||
def temperature_from_internal_energy(self, u: float) -> float: ...
|
||||
|
||||
def temperature_from_enthalpy(self, h: float) -> float: ...
|
||||
|
||||
def temperature_from_mass_internal_energy(self, m: float, U: float) -> float: ...
|
||||
|
||||
def pressure(self, m: float, T: float, V: float) -> float: ...
|
||||
|
||||
def properties_from_mU(
|
||||
self,
|
||||
m: float,
|
||||
U: float,
|
||||
V: float,
|
||||
) -> ThermodynamicProperties: ...
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class IdealGasMedium:
|
||||
"""Temperature-dependent ideal-gas air approximation.
|
||||
@@ -27,6 +75,9 @@ class IdealGasMedium:
|
||||
cp_ref: float = 1005.0
|
||||
T_ref: float = 300.0
|
||||
cp_slope: float = 0.0
|
||||
viscosity_ref: float = 1.82e-5
|
||||
viscosity_T_ref: float = 293.15
|
||||
sutherland_constant: float = 110.4
|
||||
|
||||
@property
|
||||
def cv(self) -> float:
|
||||
@@ -45,6 +96,18 @@ class IdealGasMedium:
|
||||
def density(self, p: float, T: float) -> float:
|
||||
return p / (self.R_gas * T)
|
||||
|
||||
def dynamic_viscosity(self, T: float) -> float:
|
||||
"""Return dynamic viscosity using the default air Sutherland law."""
|
||||
|
||||
if T <= 0.0:
|
||||
raise ValueError("Temperature must be positive.")
|
||||
return (
|
||||
self.viscosity_ref
|
||||
* (T / self.viscosity_T_ref) ** 1.5
|
||||
* (self.viscosity_T_ref + self.sutherland_constant)
|
||||
/ (T + self.sutherland_constant)
|
||||
)
|
||||
|
||||
def specific_internal_energy(self, T: float) -> float:
|
||||
delta_T = T - self.T_ref
|
||||
return (
|
||||
@@ -77,6 +140,22 @@ class IdealGasMedium:
|
||||
delta_T = positive_root if abs(positive_root) <= abs(negative_root) else negative_root
|
||||
return self.T_ref + delta_T
|
||||
|
||||
def temperature_from_enthalpy(self, h: float) -> float:
|
||||
reference_enthalpy = self.cp_ref * self.T_ref
|
||||
delta_h = h - reference_enthalpy
|
||||
|
||||
if abs(self.cp_slope) <= 1e-15:
|
||||
return self.T_ref + delta_h / self.cp_ref
|
||||
|
||||
a = 0.5 * self.cp_slope
|
||||
b = self.cp_ref
|
||||
c = -delta_h
|
||||
discriminant = max(b * b - 4.0 * a * c, 0.0)
|
||||
positive_root = (-b + discriminant**0.5) / (2.0 * a)
|
||||
negative_root = (-b - discriminant**0.5) / (2.0 * a)
|
||||
delta_T = positive_root if abs(positive_root) <= abs(negative_root) else negative_root
|
||||
return self.T_ref + delta_T
|
||||
|
||||
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.")
|
||||
|
||||
Reference in new issue
Block a user