Update project structure and configurations
This commit is contained in:
@@ -12,7 +12,7 @@ name = "entropyk"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[features]
|
||||
default = ["coolprop"]
|
||||
default = []
|
||||
coolprop = ["entropyk-fluids/coolprop"]
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -555,7 +555,17 @@ pub struct PyFlowSource {
|
||||
impl PyFlowSource {
|
||||
#[new]
|
||||
#[pyo3(signature = (pressure_pa=101325.0, temperature_k=300.0, fluid="Water"))]
|
||||
fn new(pressure_pa: f64, temperature_k: f64, fluid: &str) -> PyResult<Self> {
|
||||
fn new(py: Python<'_>, pressure_pa: f64, temperature_k: f64, fluid: &str) -> PyResult<Self> {
|
||||
let warnings = py.import_bound("warnings")?;
|
||||
warnings.call_method1(
|
||||
"warn",
|
||||
(
|
||||
"FlowSource is deprecated. Use RefrigerantSource, BrineSource, or AirSource instead.",
|
||||
py.get_type_bound::<pyo3::exceptions::PyDeprecationWarning>(),
|
||||
2,
|
||||
),
|
||||
)?;
|
||||
|
||||
if pressure_pa <= 0.0 {
|
||||
return Err(PyValueError::new_err("pressure_pa must be positive"));
|
||||
}
|
||||
@@ -600,8 +610,17 @@ pub struct PyFlowSink;
|
||||
#[pymethods]
|
||||
impl PyFlowSink {
|
||||
#[new]
|
||||
fn new() -> Self {
|
||||
PyFlowSink
|
||||
fn new(py: Python<'_>) -> PyResult<Self> {
|
||||
let warnings = py.import_bound("warnings")?;
|
||||
warnings.call_method1(
|
||||
"warn",
|
||||
(
|
||||
"FlowSink is deprecated. Use RefrigerantSink, BrineSink, or AirSink instead.",
|
||||
py.get_type_bound::<pyo3::exceptions::PyDeprecationWarning>(),
|
||||
2,
|
||||
),
|
||||
)?;
|
||||
Ok(PyFlowSink)
|
||||
}
|
||||
|
||||
fn __repr__(&self) -> String {
|
||||
@@ -657,6 +676,250 @@ impl PyOperationalState {
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Refrigerant Boundary Conditions
|
||||
// =============================================================================
|
||||
|
||||
/// A boundary condition representing a refrigerant mass flow source.
|
||||
#[pyclass(name = "RefrigerantSource", module = "entropyk")]
|
||||
#[derive(Clone)]
|
||||
pub struct PyRefrigerantSource {
|
||||
pub(crate) fluid: String,
|
||||
pub(crate) p_set_pa: f64,
|
||||
pub(crate) quality: f64,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyRefrigerantSource {
|
||||
#[new]
|
||||
#[pyo3(signature = (fluid="R410A", pressure_pa=101325.0, quality=1.0))]
|
||||
fn new(fluid: &str, pressure_pa: f64, quality: f64) -> PyResult<Self> {
|
||||
if pressure_pa <= 0.0 {
|
||||
return Err(PyValueError::new_err("pressure_pa must be positive"));
|
||||
}
|
||||
if !(0.0..=1.0).contains(&quality) {
|
||||
return Err(PyValueError::new_err("quality must be between 0.0 and 1.0"));
|
||||
}
|
||||
Ok(PyRefrigerantSource {
|
||||
fluid: fluid.to_string(),
|
||||
p_set_pa: pressure_pa,
|
||||
quality,
|
||||
})
|
||||
}
|
||||
|
||||
fn __repr__(&self) -> String {
|
||||
format!(
|
||||
"RefrigerantSource(fluid={}, P={:.0} Pa, q={:.2})",
|
||||
self.fluid, self.p_set_pa, self.quality
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PyRefrigerantSource {
|
||||
pub(crate) fn build(&self) -> Box<dyn Component> {
|
||||
Box::new(entropyk_components::PyRefrigerantSourceReal::new(&self.fluid, self.p_set_pa, self.quality))
|
||||
}
|
||||
}
|
||||
|
||||
/// A boundary condition representing a refrigerant mass flow sink.
|
||||
#[pyclass(name = "RefrigerantSink", module = "entropyk")]
|
||||
#[derive(Clone)]
|
||||
pub struct PyRefrigerantSink {
|
||||
pub(crate) fluid: String,
|
||||
pub(crate) p_back_pa: f64,
|
||||
pub(crate) quality_opt: Option<f64>,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyRefrigerantSink {
|
||||
#[new]
|
||||
#[pyo3(signature = (fluid="R410A", p_back_pa=101325.0, quality=None))]
|
||||
fn new(fluid: &str, p_back_pa: f64, quality: Option<f64>) -> PyResult<Self> {
|
||||
if p_back_pa <= 0.0 {
|
||||
return Err(PyValueError::new_err("p_back_pa must be positive"));
|
||||
}
|
||||
if let Some(q) = quality {
|
||||
if !(0.0..=1.0).contains(&q) {
|
||||
return Err(PyValueError::new_err("quality must be between 0.0 and 1.0"));
|
||||
}
|
||||
}
|
||||
Ok(PyRefrigerantSink {
|
||||
fluid: fluid.to_string(),
|
||||
p_back_pa,
|
||||
quality_opt: quality,
|
||||
})
|
||||
}
|
||||
|
||||
fn __repr__(&self) -> String {
|
||||
format!(
|
||||
"RefrigerantSink(fluid={}, P_back={:.0} Pa, q={:?})",
|
||||
self.fluid, self.p_back_pa, self.quality_opt
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PyRefrigerantSink {
|
||||
pub(crate) fn build(&self) -> Box<dyn Component> {
|
||||
Box::new(entropyk_components::PyRefrigerantSinkReal::new(&self.fluid, self.p_back_pa, self.quality_opt))
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Brine Boundary Conditions
|
||||
// =============================================================================
|
||||
|
||||
/// A boundary condition representing a brine mass flow source.
|
||||
#[pyclass(name = "BrineSource", module = "entropyk")]
|
||||
#[derive(Clone)]
|
||||
pub struct PyBrineSource {
|
||||
pub(crate) fluid: String,
|
||||
pub(crate) concentration: f64,
|
||||
pub(crate) temperature_k: f64,
|
||||
pub(crate) pressure_pa: f64,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyBrineSource {
|
||||
#[new]
|
||||
#[pyo3(signature = (fluid="Water", concentration=0.0, temperature_k=300.0, pressure_pa=101325.0))]
|
||||
fn new(fluid: &str, concentration: f64, temperature_k: f64, pressure_pa: f64) -> PyResult<Self> {
|
||||
if pressure_pa <= 0.0 {
|
||||
return Err(PyValueError::new_err("pressure_pa must be positive"));
|
||||
}
|
||||
if temperature_k <= 0.0 {
|
||||
return Err(PyValueError::new_err("temperature_k must be positive"));
|
||||
}
|
||||
if !(0.0..=1.0).contains(&concentration) {
|
||||
return Err(PyValueError::new_err("concentration must be between 0.0 and 1.0"));
|
||||
}
|
||||
Ok(PyBrineSource {
|
||||
fluid: fluid.to_string(),
|
||||
concentration,
|
||||
temperature_k,
|
||||
pressure_pa,
|
||||
})
|
||||
}
|
||||
|
||||
fn __repr__(&self) -> String {
|
||||
format!(
|
||||
"BrineSource(fluid={}, c={:.2}, T={:.1} K, P={:.0} Pa)",
|
||||
self.fluid, self.concentration, self.temperature_k, self.pressure_pa
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PyBrineSource {
|
||||
pub(crate) fn build(&self) -> Box<dyn Component> {
|
||||
Box::new(entropyk_components::PyBrineSourceReal::new(&self.fluid, self.concentration, self.temperature_k, self.pressure_pa))
|
||||
}
|
||||
}
|
||||
|
||||
/// A boundary condition representing a brine mass flow sink.
|
||||
#[pyclass(name = "BrineSink", module = "entropyk")]
|
||||
#[derive(Clone)]
|
||||
pub struct PyBrineSink {
|
||||
pub(crate) p_back_pa: f64,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyBrineSink {
|
||||
#[new]
|
||||
#[pyo3(signature = (p_back_pa=101325.0))]
|
||||
fn new(p_back_pa: f64) -> PyResult<Self> {
|
||||
if p_back_pa <= 0.0 {
|
||||
return Err(PyValueError::new_err("p_back_pa must be positive"));
|
||||
}
|
||||
Ok(PyBrineSink { p_back_pa })
|
||||
}
|
||||
|
||||
fn __repr__(&self) -> String {
|
||||
format!("BrineSink(P_back={:.0} Pa)", self.p_back_pa)
|
||||
}
|
||||
}
|
||||
|
||||
impl PyBrineSink {
|
||||
pub(crate) fn build(&self) -> Box<dyn Component> {
|
||||
Box::new(entropyk_components::PyBrineSinkReal::new(self.p_back_pa))
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Air Boundary Conditions
|
||||
// =============================================================================
|
||||
|
||||
/// A boundary condition representing an air mass flow source.
|
||||
#[pyclass(name = "AirSource", module = "entropyk")]
|
||||
#[derive(Clone)]
|
||||
pub struct PyAirSource {
|
||||
pub(crate) temperature_k: f64,
|
||||
pub(crate) relative_humidity: f64,
|
||||
pub(crate) pressure_pa: f64,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyAirSource {
|
||||
#[new]
|
||||
#[pyo3(signature = (temperature_k=300.0, relative_humidity=0.5, pressure_pa=101325.0))]
|
||||
fn new(temperature_k: f64, relative_humidity: f64, pressure_pa: f64) -> PyResult<Self> {
|
||||
if pressure_pa <= 0.0 {
|
||||
return Err(PyValueError::new_err("pressure_pa must be positive"));
|
||||
}
|
||||
if temperature_k <= 0.0 {
|
||||
return Err(PyValueError::new_err("temperature_k must be positive"));
|
||||
}
|
||||
if !(0.0..=1.0).contains(&relative_humidity) {
|
||||
return Err(PyValueError::new_err("relative_humidity must be between 0.0 and 1.0"));
|
||||
}
|
||||
Ok(PyAirSource {
|
||||
temperature_k,
|
||||
relative_humidity,
|
||||
pressure_pa,
|
||||
})
|
||||
}
|
||||
|
||||
fn __repr__(&self) -> String {
|
||||
format!(
|
||||
"AirSource(T={:.1} K, RH={:.2}, P={:.0} Pa)",
|
||||
self.temperature_k, self.relative_humidity, self.pressure_pa
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PyAirSource {
|
||||
pub(crate) fn build(&self) -> Box<dyn Component> {
|
||||
Box::new(entropyk_components::PyAirSourceReal::new(self.temperature_k, self.relative_humidity, self.pressure_pa))
|
||||
}
|
||||
}
|
||||
|
||||
/// A boundary condition representing an air mass flow sink.
|
||||
#[pyclass(name = "AirSink", module = "entropyk")]
|
||||
#[derive(Clone)]
|
||||
pub struct PyAirSink {
|
||||
pub(crate) p_back_pa: f64,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyAirSink {
|
||||
#[new]
|
||||
#[pyo3(signature = (p_back_pa=101325.0))]
|
||||
fn new(p_back_pa: f64) -> PyResult<Self> {
|
||||
if p_back_pa <= 0.0 {
|
||||
return Err(PyValueError::new_err("p_back_pa must be positive"));
|
||||
}
|
||||
Ok(PyAirSink { p_back_pa })
|
||||
}
|
||||
|
||||
fn __repr__(&self) -> String {
|
||||
format!("AirSink(P_back={:.0} Pa)", self.p_back_pa)
|
||||
}
|
||||
}
|
||||
|
||||
impl PyAirSink {
|
||||
pub(crate) fn build(&self) -> Box<dyn Component> {
|
||||
Box::new(entropyk_components::PyAirSinkReal::new(self.p_back_pa))
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Component enum for type-erasure
|
||||
// =============================================================================
|
||||
@@ -676,6 +939,12 @@ pub(crate) enum AnyPyComponent {
|
||||
FlowMerger(PyFlowMerger),
|
||||
FlowSource(PyFlowSource),
|
||||
FlowSink(PyFlowSink),
|
||||
RefrigerantSource(PyRefrigerantSource),
|
||||
RefrigerantSink(PyRefrigerantSink),
|
||||
BrineSource(PyBrineSource),
|
||||
BrineSink(PyBrineSink),
|
||||
AirSource(PyAirSource),
|
||||
AirSink(PyAirSink),
|
||||
}
|
||||
|
||||
impl AnyPyComponent {
|
||||
@@ -694,6 +963,12 @@ impl AnyPyComponent {
|
||||
AnyPyComponent::FlowMerger(c) => c.build(),
|
||||
AnyPyComponent::FlowSource(c) => c.build(),
|
||||
AnyPyComponent::FlowSink(c) => c.build(),
|
||||
AnyPyComponent::RefrigerantSource(c) => c.build(),
|
||||
AnyPyComponent::RefrigerantSink(c) => c.build(),
|
||||
AnyPyComponent::BrineSource(c) => c.build(),
|
||||
AnyPyComponent::BrineSink(c) => c.build(),
|
||||
AnyPyComponent::AirSource(c) => c.build(),
|
||||
AnyPyComponent::AirSink(c) => c.build(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,10 @@ fn entropyk(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_class::<types::PyTemperature>()?;
|
||||
m.add_class::<types::PyEnthalpy>()?;
|
||||
m.add_class::<types::PyMassFlow>()?;
|
||||
m.add_class::<types::PyConcentration>()?;
|
||||
m.add_class::<types::PyVolumeFlow>()?;
|
||||
m.add_class::<types::PyRelativeHumidity>()?;
|
||||
m.add_class::<types::PyVaporQuality>()?;
|
||||
|
||||
// Components
|
||||
m.add_class::<components::PyCompressor>()?;
|
||||
@@ -35,6 +39,12 @@ fn entropyk(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_class::<components::PyFlowMerger>()?;
|
||||
m.add_class::<components::PyFlowSource>()?;
|
||||
m.add_class::<components::PyFlowSink>()?;
|
||||
m.add_class::<components::PyRefrigerantSource>()?;
|
||||
m.add_class::<components::PyRefrigerantSink>()?;
|
||||
m.add_class::<components::PyBrineSource>()?;
|
||||
m.add_class::<components::PyBrineSink>()?;
|
||||
m.add_class::<components::PyAirSource>()?;
|
||||
m.add_class::<components::PyAirSink>()?;
|
||||
m.add_class::<components::PyOperationalState>()?;
|
||||
|
||||
// Solver
|
||||
|
||||
@@ -282,8 +282,26 @@ fn extract_component(obj: &Bound<'_, PyAny>) -> PyResult<AnyPyComponent> {
|
||||
if let Ok(c) = obj.extract::<crate::components::PyFlowSink>() {
|
||||
return Ok(AnyPyComponent::FlowSink(c));
|
||||
}
|
||||
if let Ok(c) = obj.extract::<crate::components::PyRefrigerantSource>() {
|
||||
return Ok(AnyPyComponent::RefrigerantSource(c));
|
||||
}
|
||||
if let Ok(c) = obj.extract::<crate::components::PyRefrigerantSink>() {
|
||||
return Ok(AnyPyComponent::RefrigerantSink(c));
|
||||
}
|
||||
if let Ok(c) = obj.extract::<crate::components::PyBrineSource>() {
|
||||
return Ok(AnyPyComponent::BrineSource(c));
|
||||
}
|
||||
if let Ok(c) = obj.extract::<crate::components::PyBrineSink>() {
|
||||
return Ok(AnyPyComponent::BrineSink(c));
|
||||
}
|
||||
if let Ok(c) = obj.extract::<crate::components::PyAirSource>() {
|
||||
return Ok(AnyPyComponent::AirSource(c));
|
||||
}
|
||||
if let Ok(c) = obj.extract::<crate::components::PyAirSink>() {
|
||||
return Ok(AnyPyComponent::AirSink(c));
|
||||
}
|
||||
Err(PyValueError::new_err(
|
||||
"Expected a component (Compressor, Condenser, Evaporator, ExpansionValve, Pipe, Pump, Fan, Economizer, FlowSplitter, FlowMerger, FlowSource, FlowSink)",
|
||||
"Expected a component (Compressor, Condenser, Evaporator, ExpansionValve, Pipe, Pump, Fan, Economizer, FlowSplitter, FlowMerger, FlowSource, FlowSink, RefrigerantSource, RefrigerantSink, BrineSource, BrineSink, AirSource, AirSink)",
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -344,3 +344,239 @@ impl PyMassFlow {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// Concentration
|
||||
// =============================================================================
|
||||
|
||||
#[pyclass(name = "Concentration", module = "entropyk")]
|
||||
#[derive(Clone)]
|
||||
pub struct PyConcentration {
|
||||
pub(crate) inner: entropyk::Concentration,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyConcentration {
|
||||
#[new]
|
||||
fn new(value: f64) -> PyResult<Self> {
|
||||
if !(0.0..=1.0).contains(&value) {
|
||||
return Err(pyo3::exceptions::PyValueError::new_err("Value must be between 0.0 and 1.0"));
|
||||
}
|
||||
Ok(PyConcentration {
|
||||
inner: entropyk::Concentration::from_fraction(value),
|
||||
})
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn value(&self) -> f64 {
|
||||
self.inner.to_fraction()
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
fn from_percent(value: f64) -> Self {
|
||||
PyConcentration {
|
||||
inner: entropyk::Concentration::from_percent(value),
|
||||
}
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
fn from_fraction(value: f64) -> Self {
|
||||
PyConcentration {
|
||||
inner: entropyk::Concentration::from_fraction(value),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_percent(&self) -> f64 {
|
||||
self.inner.to_percent()
|
||||
}
|
||||
|
||||
fn to_fraction(&self) -> f64 {
|
||||
self.inner.to_fraction()
|
||||
}
|
||||
|
||||
fn to_mass_fraction(&self) -> f64 {
|
||||
self.inner.to_fraction()
|
||||
}
|
||||
|
||||
fn __repr__(&self) -> String {
|
||||
format!("Concentration({:.2}%)", self.inner.to_percent())
|
||||
}
|
||||
|
||||
fn __float__(&self) -> f64 {
|
||||
self.inner.to_fraction()
|
||||
}
|
||||
|
||||
fn __eq__(&self, other: &PyConcentration) -> bool {
|
||||
(self.inner.0 - other.inner.0).abs() < 1e-10
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// VolumeFlow
|
||||
// =============================================================================
|
||||
|
||||
#[pyclass(name = "VolumeFlow", module = "entropyk")]
|
||||
#[derive(Clone)]
|
||||
pub struct PyVolumeFlow {
|
||||
pub(crate) inner: entropyk::VolumeFlow,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyVolumeFlow {
|
||||
#[new]
|
||||
fn new(value: f64) -> PyResult<Self> {
|
||||
if value < 0.0 {
|
||||
return Err(pyo3::exceptions::PyValueError::new_err("Value cannot be negative"));
|
||||
}
|
||||
Ok(PyVolumeFlow { inner: entropyk::VolumeFlow::from_m3_per_s(value) })
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn value(&self) -> f64 {
|
||||
self.inner.to_m3_per_s()
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
fn from_m3_per_s(value: f64) -> Self {
|
||||
PyVolumeFlow { inner: entropyk::VolumeFlow::from_m3_per_s(value) }
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
fn from_l_per_min(value: f64) -> Self {
|
||||
PyVolumeFlow { inner: entropyk::VolumeFlow::from_l_per_min(value) }
|
||||
}
|
||||
|
||||
fn to_m3_per_s(&self) -> f64 {
|
||||
self.inner.to_m3_per_s()
|
||||
}
|
||||
|
||||
fn to_l_per_min(&self) -> f64 {
|
||||
self.inner.to_l_per_min()
|
||||
}
|
||||
|
||||
fn __repr__(&self) -> String {
|
||||
format!("VolumeFlow({:.6} m³/s)", self.inner.to_m3_per_s())
|
||||
}
|
||||
|
||||
fn __float__(&self) -> f64 {
|
||||
self.inner.to_m3_per_s()
|
||||
}
|
||||
|
||||
fn __eq__(&self, other: &PyVolumeFlow) -> bool {
|
||||
(self.inner.0 - other.inner.0).abs() < 1e-15
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// RelativeHumidity
|
||||
// =============================================================================
|
||||
|
||||
#[pyclass(name = "RelativeHumidity", module = "entropyk")]
|
||||
#[derive(Clone)]
|
||||
pub struct PyRelativeHumidity {
|
||||
pub(crate) inner: entropyk::RelativeHumidity,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyRelativeHumidity {
|
||||
#[new]
|
||||
fn new(value: f64) -> PyResult<Self> {
|
||||
if !(0.0..=1.0).contains(&value) {
|
||||
return Err(pyo3::exceptions::PyValueError::new_err("Value must be between 0.0 and 1.0"));
|
||||
}
|
||||
Ok(PyRelativeHumidity { inner: entropyk::RelativeHumidity::from_fraction(value) })
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn value(&self) -> f64 {
|
||||
self.inner.to_fraction()
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
fn from_percent(value: f64) -> Self {
|
||||
PyRelativeHumidity { inner: entropyk::RelativeHumidity::from_percent(value) }
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
fn from_fraction(value: f64) -> Self {
|
||||
PyRelativeHumidity { inner: entropyk::RelativeHumidity::from_fraction(value) }
|
||||
}
|
||||
|
||||
fn to_percent(&self) -> f64 {
|
||||
self.inner.to_percent()
|
||||
}
|
||||
|
||||
fn to_fraction(&self) -> f64 {
|
||||
self.inner.to_fraction()
|
||||
}
|
||||
|
||||
fn __repr__(&self) -> String {
|
||||
format!("RelativeHumidity({:.2}%)", self.inner.to_percent())
|
||||
}
|
||||
|
||||
fn __float__(&self) -> f64 {
|
||||
self.inner.to_fraction()
|
||||
}
|
||||
|
||||
fn __eq__(&self, other: &PyRelativeHumidity) -> bool {
|
||||
(self.inner.0 - other.inner.0).abs() < 1e-10
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// VaporQuality
|
||||
// =============================================================================
|
||||
|
||||
#[pyclass(name = "VaporQuality", module = "entropyk")]
|
||||
#[derive(Clone)]
|
||||
pub struct PyVaporQuality {
|
||||
pub(crate) inner: entropyk::VaporQuality,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyVaporQuality {
|
||||
#[new]
|
||||
fn new(value: f64) -> PyResult<Self> {
|
||||
if !(0.0..=1.0).contains(&value) {
|
||||
return Err(pyo3::exceptions::PyValueError::new_err("Value must be between 0.0 and 1.0"));
|
||||
}
|
||||
Ok(PyVaporQuality { inner: entropyk::VaporQuality::from_fraction(value) })
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn value(&self) -> f64 {
|
||||
self.inner.to_fraction()
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
fn from_fraction(value: f64) -> Self {
|
||||
PyVaporQuality { inner: entropyk::VaporQuality::from_fraction(value) }
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
fn from_percent(value: f64) -> Self {
|
||||
PyVaporQuality { inner: entropyk::VaporQuality::from_percent(value) }
|
||||
}
|
||||
|
||||
fn to_fraction(&self) -> f64 {
|
||||
self.inner.to_fraction()
|
||||
}
|
||||
|
||||
fn to_percent(&self) -> f64 {
|
||||
self.inner.to_percent()
|
||||
}
|
||||
|
||||
fn __repr__(&self) -> String {
|
||||
format!("VaporQuality({:.2}%)", self.inner.to_percent())
|
||||
}
|
||||
|
||||
fn __float__(&self) -> f64 {
|
||||
self.inner.to_fraction()
|
||||
}
|
||||
|
||||
fn __eq__(&self, other: &PyVaporQuality) -> bool {
|
||||
(self.inner.0 - other.inner.0).abs() < 1e-10
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
180
bindings/python/src/types_appended.rs
Normal file
180
bindings/python/src/types_appended.rs
Normal file
@@ -0,0 +1,180 @@
|
||||
// =============================================================================
|
||||
// Concentration
|
||||
// =============================================================================
|
||||
|
||||
#[pyclass(name = "Concentration", module = "entropyk")]
|
||||
#[derive(Clone)]
|
||||
pub struct PyConcentration {
|
||||
pub(crate) inner: entropyk::Concentration,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyConcentration {
|
||||
#[staticmethod]
|
||||
fn from_percent(value: f64) -> Self {
|
||||
PyConcentration {
|
||||
inner: entropyk::Concentration::from_percent(value),
|
||||
}
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
fn from_fraction(value: f64) -> Self {
|
||||
PyConcentration {
|
||||
inner: entropyk::Concentration::from_fraction(value),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_percent(&self) -> f64 {
|
||||
self.inner.to_percent()
|
||||
}
|
||||
|
||||
fn to_fraction(&self) -> f64 {
|
||||
self.inner.to_fraction()
|
||||
}
|
||||
|
||||
fn to_mass_fraction(&self) -> f64 {
|
||||
self.inner.to_fraction()
|
||||
}
|
||||
|
||||
fn __repr__(&self) -> String {
|
||||
format!("Concentration({:.2}%)", self.inner.to_percent())
|
||||
}
|
||||
|
||||
fn __float__(&self) -> f64 {
|
||||
self.inner.to_fraction()
|
||||
}
|
||||
|
||||
fn __eq__(&self, other: &PyConcentration) -> bool {
|
||||
(self.inner.0 - other.inner.0).abs() < 1e-10
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// VolumeFlow
|
||||
// =============================================================================
|
||||
|
||||
#[pyclass(name = "VolumeFlow", module = "entropyk")]
|
||||
#[derive(Clone)]
|
||||
pub struct PyVolumeFlow {
|
||||
pub(crate) inner: entropyk::VolumeFlow,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyVolumeFlow {
|
||||
#[staticmethod]
|
||||
fn from_m3_per_s(value: f64) -> Self {
|
||||
PyVolumeFlow { inner: entropyk::VolumeFlow::from_m3_per_s(value) }
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
fn from_l_per_min(value: f64) -> Self {
|
||||
PyVolumeFlow { inner: entropyk::VolumeFlow::from_l_per_min(value) }
|
||||
}
|
||||
|
||||
fn to_m3_per_s(&self) -> f64 {
|
||||
self.inner.to_m3_per_s()
|
||||
}
|
||||
|
||||
fn to_l_per_min(&self) -> f64 {
|
||||
self.inner.to_l_per_min()
|
||||
}
|
||||
|
||||
fn __repr__(&self) -> String {
|
||||
format!("VolumeFlow({:.6} m³/s)", self.inner.to_m3_per_s())
|
||||
}
|
||||
|
||||
fn __float__(&self) -> f64 {
|
||||
self.inner.to_m3_per_s()
|
||||
}
|
||||
|
||||
fn __eq__(&self, other: &PyVolumeFlow) -> bool {
|
||||
(self.inner.0 - other.inner.0).abs() < 1e-15
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// RelativeHumidity
|
||||
// =============================================================================
|
||||
|
||||
#[pyclass(name = "RelativeHumidity", module = "entropyk")]
|
||||
#[derive(Clone)]
|
||||
pub struct PyRelativeHumidity {
|
||||
pub(crate) inner: entropyk::RelativeHumidity,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyRelativeHumidity {
|
||||
#[staticmethod]
|
||||
fn from_percent(value: f64) -> Self {
|
||||
PyRelativeHumidity { inner: entropyk::RelativeHumidity::from_percent(value) }
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
fn from_fraction(value: f64) -> Self {
|
||||
PyRelativeHumidity { inner: entropyk::RelativeHumidity::from_fraction(value) }
|
||||
}
|
||||
|
||||
fn to_percent(&self) -> f64 {
|
||||
self.inner.to_percent()
|
||||
}
|
||||
|
||||
fn to_fraction(&self) -> f64 {
|
||||
self.inner.to_fraction()
|
||||
}
|
||||
|
||||
fn __repr__(&self) -> String {
|
||||
format!("RelativeHumidity({:.2}%)", self.inner.to_percent())
|
||||
}
|
||||
|
||||
fn __float__(&self) -> f64 {
|
||||
self.inner.to_fraction()
|
||||
}
|
||||
|
||||
fn __eq__(&self, other: &PyRelativeHumidity) -> bool {
|
||||
(self.inner.0 - other.inner.0).abs() < 1e-10
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// VaporQuality
|
||||
// =============================================================================
|
||||
|
||||
#[pyclass(name = "VaporQuality", module = "entropyk")]
|
||||
#[derive(Clone)]
|
||||
pub struct PyVaporQuality {
|
||||
pub(crate) inner: entropyk::VaporQuality,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyVaporQuality {
|
||||
#[staticmethod]
|
||||
fn from_fraction(value: f64) -> Self {
|
||||
PyVaporQuality { inner: entropyk::VaporQuality::from_fraction(value) }
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
fn from_percent(value: f64) -> Self {
|
||||
PyVaporQuality { inner: entropyk::VaporQuality::from_percent(value) }
|
||||
}
|
||||
|
||||
fn to_fraction(&self) -> f64 {
|
||||
self.inner.to_fraction()
|
||||
}
|
||||
|
||||
fn to_percent(&self) -> f64 {
|
||||
self.inner.to_percent()
|
||||
}
|
||||
|
||||
fn __repr__(&self) -> String {
|
||||
format!("VaporQuality({:.2}%)", self.inner.to_percent())
|
||||
}
|
||||
|
||||
fn __float__(&self) -> f64 {
|
||||
self.inner.to_fraction()
|
||||
}
|
||||
|
||||
fn __eq__(&self, other: &PyVaporQuality) -> bool {
|
||||
(self.inner.0 - other.inner.0).abs() < 1e-10
|
||||
}
|
||||
}
|
||||
|
||||
347
bindings/python/src/types_clean.rs
Normal file
347
bindings/python/src/types_clean.rs
Normal file
@@ -0,0 +1,347 @@
|
||||
//! Python wrappers for Entropyk core physical types.
|
||||
//!
|
||||
//! Each wrapper holds the inner Rust NewType and exposes Pythonic constructors
|
||||
//! with keyword arguments (e.g., `Pressure(bar=1.0)`) plus unit conversion methods.
|
||||
|
||||
use pyo3::exceptions::PyValueError;
|
||||
use pyo3::prelude::*;
|
||||
|
||||
// =============================================================================
|
||||
// Pressure
|
||||
// =============================================================================
|
||||
|
||||
/// Pressure in Pascals (Pa).
|
||||
///
|
||||
/// Construct with one of: ``pa``, ``bar``, ``kpa``, ``psi``.
|
||||
///
|
||||
/// Example::
|
||||
///
|
||||
/// p = Pressure(bar=1.0)
|
||||
/// print(p.to_bar()) # 1.0
|
||||
/// print(float(p)) # 100000.0
|
||||
#[pyclass(name = "Pressure", module = "entropyk")]
|
||||
#[derive(Clone)]
|
||||
pub struct PyPressure {
|
||||
pub(crate) inner: entropyk::Pressure,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyPressure {
|
||||
/// Create a Pressure. Specify exactly one of: ``pa``, ``bar``, ``kpa``, ``psi``.
|
||||
#[new]
|
||||
#[pyo3(signature = (pa=None, bar=None, kpa=None, psi=None))]
|
||||
fn new(
|
||||
pa: Option<f64>,
|
||||
bar: Option<f64>,
|
||||
kpa: Option<f64>,
|
||||
psi: Option<f64>,
|
||||
) -> PyResult<Self> {
|
||||
let value = match (pa, bar, kpa, psi) {
|
||||
(Some(v), None, None, None) => v,
|
||||
(None, Some(v), None, None) => v * 100_000.0,
|
||||
(None, None, Some(v), None) => v * 1_000.0,
|
||||
(None, None, None, Some(v)) => v * 6894.75729,
|
||||
_ => {
|
||||
return Err(PyValueError::new_err(
|
||||
"Specify exactly one of: pa, bar, kpa, psi",
|
||||
))
|
||||
}
|
||||
};
|
||||
Ok(PyPressure {
|
||||
inner: entropyk::Pressure(value),
|
||||
})
|
||||
}
|
||||
|
||||
/// Value in Pascals.
|
||||
fn to_pascals(&self) -> f64 {
|
||||
self.inner.to_pascals()
|
||||
}
|
||||
|
||||
/// Value in bar.
|
||||
fn to_bar(&self) -> f64 {
|
||||
self.inner.to_bar()
|
||||
}
|
||||
|
||||
/// Value in kPa.
|
||||
fn to_kpa(&self) -> f64 {
|
||||
self.inner.0 / 1_000.0
|
||||
}
|
||||
|
||||
/// Value in PSI.
|
||||
fn to_psi(&self) -> f64 {
|
||||
self.inner.to_psi()
|
||||
}
|
||||
|
||||
fn __repr__(&self) -> String {
|
||||
format!(
|
||||
"Pressure({:.2} Pa = {:.4} bar)",
|
||||
self.inner.0,
|
||||
self.inner.0 / 100_000.0
|
||||
)
|
||||
}
|
||||
|
||||
fn __str__(&self) -> String {
|
||||
format!("{:.2} Pa", self.inner.0)
|
||||
}
|
||||
|
||||
fn __float__(&self) -> f64 {
|
||||
self.inner.0
|
||||
}
|
||||
|
||||
fn __eq__(&self, other: &PyPressure) -> bool {
|
||||
(self.inner.0 - other.inner.0).abs() < 1e-10
|
||||
}
|
||||
|
||||
fn __add__(&self, other: &PyPressure) -> PyPressure {
|
||||
PyPressure {
|
||||
inner: self.inner + other.inner,
|
||||
}
|
||||
}
|
||||
|
||||
fn __sub__(&self, other: &PyPressure) -> PyPressure {
|
||||
PyPressure {
|
||||
inner: self.inner - other.inner,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Temperature
|
||||
// =============================================================================
|
||||
|
||||
/// Temperature in Kelvin (K).
|
||||
///
|
||||
/// Construct with one of: ``kelvin``, ``celsius``, ``fahrenheit``.
|
||||
///
|
||||
/// Example::
|
||||
///
|
||||
/// t = Temperature(celsius=25.0)
|
||||
/// print(t.to_kelvin()) # 298.15
|
||||
/// print(t.to_celsius()) # 25.0
|
||||
#[pyclass(name = "Temperature", module = "entropyk")]
|
||||
#[derive(Clone)]
|
||||
pub struct PyTemperature {
|
||||
pub(crate) inner: entropyk::Temperature,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyTemperature {
|
||||
/// Create a Temperature. Specify exactly one of: ``kelvin``, ``celsius``, ``fahrenheit``.
|
||||
#[new]
|
||||
#[pyo3(signature = (kelvin=None, celsius=None, fahrenheit=None))]
|
||||
fn new(kelvin: Option<f64>, celsius: Option<f64>, fahrenheit: Option<f64>) -> PyResult<Self> {
|
||||
let inner = match (kelvin, celsius, fahrenheit) {
|
||||
(Some(v), None, None) => entropyk::Temperature::from_kelvin(v),
|
||||
(None, Some(v), None) => entropyk::Temperature::from_celsius(v),
|
||||
(None, None, Some(v)) => entropyk::Temperature::from_fahrenheit(v),
|
||||
_ => {
|
||||
return Err(PyValueError::new_err(
|
||||
"Specify exactly one of: kelvin, celsius, fahrenheit",
|
||||
))
|
||||
}
|
||||
};
|
||||
Ok(PyTemperature { inner })
|
||||
}
|
||||
|
||||
/// Value in Kelvin.
|
||||
fn to_kelvin(&self) -> f64 {
|
||||
self.inner.to_kelvin()
|
||||
}
|
||||
|
||||
/// Value in Celsius.
|
||||
fn to_celsius(&self) -> f64 {
|
||||
self.inner.to_celsius()
|
||||
}
|
||||
|
||||
/// Value in Fahrenheit.
|
||||
fn to_fahrenheit(&self) -> f64 {
|
||||
self.inner.to_fahrenheit()
|
||||
}
|
||||
|
||||
fn __repr__(&self) -> String {
|
||||
format!(
|
||||
"Temperature({:.2} K = {:.2} °C)",
|
||||
self.inner.0,
|
||||
self.inner.0 - 273.15
|
||||
)
|
||||
}
|
||||
|
||||
fn __str__(&self) -> String {
|
||||
format!("{:.2} K", self.inner.0)
|
||||
}
|
||||
|
||||
fn __float__(&self) -> f64 {
|
||||
self.inner.0
|
||||
}
|
||||
|
||||
fn __eq__(&self, other: &PyTemperature) -> bool {
|
||||
(self.inner.0 - other.inner.0).abs() < 1e-10
|
||||
}
|
||||
|
||||
fn __add__(&self, other: &PyTemperature) -> PyTemperature {
|
||||
PyTemperature {
|
||||
inner: entropyk::Temperature(self.inner.0 + other.inner.0),
|
||||
}
|
||||
}
|
||||
|
||||
fn __sub__(&self, other: &PyTemperature) -> PyTemperature {
|
||||
PyTemperature {
|
||||
inner: entropyk::Temperature(self.inner.0 - other.inner.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Enthalpy
|
||||
// =============================================================================
|
||||
|
||||
/// Specific enthalpy in J/kg.
|
||||
///
|
||||
/// Construct with one of: ``j_per_kg``, ``kj_per_kg``.
|
||||
///
|
||||
/// Example::
|
||||
///
|
||||
/// h = Enthalpy(kj_per_kg=250.0)
|
||||
/// print(h.to_kj_per_kg()) # 250.0
|
||||
#[pyclass(name = "Enthalpy", module = "entropyk")]
|
||||
#[derive(Clone)]
|
||||
pub struct PyEnthalpy {
|
||||
pub(crate) inner: entropyk::Enthalpy,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyEnthalpy {
|
||||
/// Create an Enthalpy. Specify exactly one of: ``j_per_kg``, ``kj_per_kg``.
|
||||
#[new]
|
||||
#[pyo3(signature = (j_per_kg=None, kj_per_kg=None))]
|
||||
fn new(j_per_kg: Option<f64>, kj_per_kg: Option<f64>) -> PyResult<Self> {
|
||||
let inner = match (j_per_kg, kj_per_kg) {
|
||||
(Some(v), None) => entropyk::Enthalpy::from_joules_per_kg(v),
|
||||
(None, Some(v)) => entropyk::Enthalpy::from_kilojoules_per_kg(v),
|
||||
_ => {
|
||||
return Err(PyValueError::new_err(
|
||||
"Specify exactly one of: j_per_kg, kj_per_kg",
|
||||
))
|
||||
}
|
||||
};
|
||||
Ok(PyEnthalpy { inner })
|
||||
}
|
||||
|
||||
/// Value in J/kg.
|
||||
fn to_j_per_kg(&self) -> f64 {
|
||||
self.inner.to_joules_per_kg()
|
||||
}
|
||||
|
||||
/// Value in kJ/kg.
|
||||
fn to_kj_per_kg(&self) -> f64 {
|
||||
self.inner.to_kilojoules_per_kg()
|
||||
}
|
||||
|
||||
fn __repr__(&self) -> String {
|
||||
format!(
|
||||
"Enthalpy({:.2} J/kg = {:.2} kJ/kg)",
|
||||
self.inner.0,
|
||||
self.inner.0 / 1_000.0
|
||||
)
|
||||
}
|
||||
|
||||
fn __str__(&self) -> String {
|
||||
format!("{:.2} J/kg", self.inner.0)
|
||||
}
|
||||
|
||||
fn __float__(&self) -> f64 {
|
||||
self.inner.0
|
||||
}
|
||||
|
||||
fn __eq__(&self, other: &PyEnthalpy) -> bool {
|
||||
(self.inner.0 - other.inner.0).abs() < 1e-10
|
||||
}
|
||||
|
||||
fn __add__(&self, other: &PyEnthalpy) -> PyEnthalpy {
|
||||
PyEnthalpy {
|
||||
inner: entropyk::Enthalpy(self.inner.0 + other.inner.0),
|
||||
}
|
||||
}
|
||||
|
||||
fn __sub__(&self, other: &PyEnthalpy) -> PyEnthalpy {
|
||||
PyEnthalpy {
|
||||
inner: entropyk::Enthalpy(self.inner.0 - other.inner.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// MassFlow
|
||||
// =============================================================================
|
||||
|
||||
/// Mass flow rate in kg/s.
|
||||
///
|
||||
/// Construct with one of: ``kg_per_s``, ``g_per_s``.
|
||||
///
|
||||
/// Example::
|
||||
///
|
||||
/// m = MassFlow(kg_per_s=0.5)
|
||||
/// print(m.to_g_per_s()) # 500.0
|
||||
#[pyclass(name = "MassFlow", module = "entropyk")]
|
||||
#[derive(Clone)]
|
||||
pub struct PyMassFlow {
|
||||
pub(crate) inner: entropyk::MassFlow,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyMassFlow {
|
||||
/// Create a MassFlow. Specify exactly one of: ``kg_per_s``, ``g_per_s``.
|
||||
#[new]
|
||||
#[pyo3(signature = (kg_per_s=None, g_per_s=None))]
|
||||
fn new(kg_per_s: Option<f64>, g_per_s: Option<f64>) -> PyResult<Self> {
|
||||
let inner = match (kg_per_s, g_per_s) {
|
||||
(Some(v), None) => entropyk::MassFlow::from_kg_per_s(v),
|
||||
(None, Some(v)) => entropyk::MassFlow::from_grams_per_s(v),
|
||||
_ => {
|
||||
return Err(PyValueError::new_err(
|
||||
"Specify exactly one of: kg_per_s, g_per_s",
|
||||
))
|
||||
}
|
||||
};
|
||||
Ok(PyMassFlow { inner })
|
||||
}
|
||||
|
||||
/// Value in kg/s.
|
||||
fn to_kg_per_s(&self) -> f64 {
|
||||
self.inner.to_kg_per_s()
|
||||
}
|
||||
|
||||
/// Value in g/s.
|
||||
fn to_g_per_s(&self) -> f64 {
|
||||
self.inner.to_grams_per_s()
|
||||
}
|
||||
|
||||
fn __repr__(&self) -> String {
|
||||
format!("MassFlow({:.6} kg/s)", self.inner.0)
|
||||
}
|
||||
|
||||
fn __str__(&self) -> String {
|
||||
format!("{:.6} kg/s", self.inner.0)
|
||||
}
|
||||
|
||||
fn __float__(&self) -> f64 {
|
||||
self.inner.0
|
||||
}
|
||||
|
||||
fn __eq__(&self, other: &PyMassFlow) -> bool {
|
||||
(self.inner.0 - other.inner.0).abs() < 1e-15
|
||||
}
|
||||
|
||||
fn __add__(&self, other: &PyMassFlow) -> PyMassFlow {
|
||||
PyMassFlow {
|
||||
inner: entropyk::MassFlow(self.inner.0 + other.inner.0),
|
||||
}
|
||||
}
|
||||
|
||||
fn __sub__(&self, other: &PyMassFlow) -> PyMassFlow {
|
||||
PyMassFlow {
|
||||
inner: entropyk::MassFlow(self.inner.0 - other.inner.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
bindings/python/tests/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
bindings/python/tests/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
93
bindings/python/tests/test_boundary.py
Normal file
93
bindings/python/tests/test_boundary.py
Normal file
@@ -0,0 +1,93 @@
|
||||
import pytest
|
||||
import warnings
|
||||
from entropyk import (
|
||||
Concentration,
|
||||
VolumeFlow,
|
||||
RelativeHumidity,
|
||||
VaporQuality,
|
||||
RefrigerantSource,
|
||||
RefrigerantSink,
|
||||
BrineSource,
|
||||
BrineSink,
|
||||
AirSource,
|
||||
AirSink,
|
||||
FlowSource,
|
||||
FlowSink,
|
||||
System,
|
||||
)
|
||||
|
||||
def test_physical_types_instantiation():
|
||||
"""Test instantiation and constraints of new physical types."""
|
||||
c = Concentration(0.3)
|
||||
assert c.value == 0.3
|
||||
with pytest.raises(ValueError):
|
||||
Concentration(1.5)
|
||||
|
||||
vf = VolumeFlow(0.1)
|
||||
assert vf.value == 0.1
|
||||
with pytest.raises(ValueError):
|
||||
VolumeFlow(-0.1)
|
||||
|
||||
rh = RelativeHumidity(0.5)
|
||||
assert rh.value == 0.5
|
||||
with pytest.raises(ValueError):
|
||||
RelativeHumidity(-0.1)
|
||||
|
||||
vq = VaporQuality(0.8)
|
||||
assert vq.value == 0.8
|
||||
with pytest.raises(ValueError):
|
||||
VaporQuality(1.1)
|
||||
|
||||
def test_refrigerant_boundary():
|
||||
"""Test RefrigerantSource and RefrigerantSink instantiation."""
|
||||
source = RefrigerantSource(fluid="R134a", pressure_pa=200000.0, quality=0.5)
|
||||
sink = RefrigerantSink(fluid="R134a", p_back_pa=100000.0, quality=1.0)
|
||||
|
||||
assert "R134a" in repr(source)
|
||||
assert "0.50" in repr(source)
|
||||
assert "R134a" in repr(sink)
|
||||
|
||||
sys = System()
|
||||
sys.add_component(source)
|
||||
sys.add_component(sink)
|
||||
|
||||
def test_brine_boundary():
|
||||
"""Test BrineSource and BrineSink instantiation."""
|
||||
source = BrineSource(fluid="EthyleneGlycol", concentration=0.3, temperature_k=280.0, pressure_pa=200000.0)
|
||||
sink = BrineSink(p_back_pa=150000.0)
|
||||
|
||||
assert "EthyleneGlycol" in repr(source)
|
||||
assert "0.30" in repr(source)
|
||||
assert "150000" in repr(sink)
|
||||
|
||||
sys = System()
|
||||
sys.add_component(source)
|
||||
sys.add_component(sink)
|
||||
|
||||
def test_air_boundary():
|
||||
"""Test AirSource and AirSink instantiation."""
|
||||
source = AirSource(temperature_k=293.15, relative_humidity=0.5, pressure_pa=101325.0)
|
||||
sink = AirSink(p_back_pa=101325.0)
|
||||
|
||||
assert "293.1" in repr(source)
|
||||
assert "0.50" in repr(source)
|
||||
|
||||
sys = System()
|
||||
sys.add_component(source)
|
||||
sys.add_component(sink)
|
||||
|
||||
def test_deprecated_flow_boundary():
|
||||
"""Test that FlowSource and FlowSink raise deprecation warnings."""
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
FlowSource(pressure_pa=100000.0, temperature_k=300.0, fluid="Water")
|
||||
assert len(w) == 1
|
||||
assert issubclass(w[-1].category, DeprecationWarning)
|
||||
assert "deprecated" in str(w[-1].message).lower()
|
||||
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
FlowSink()
|
||||
assert len(w) == 1
|
||||
assert issubclass(w[-1].category, DeprecationWarning)
|
||||
assert "deprecated" in str(w[-1].message).lower()
|
||||
Reference in New Issue
Block a user