167 lines
6.4 KiB
Rust
167 lines
6.4 KiB
Rust
//! Fluid backend trait and implementations.
|
|
//!
|
|
//! This module defines the core `FluidBackend` trait that abstracts the source
|
|
//! of thermodynamic property data, allowing the solver to switch between different
|
|
//! backends (CoolProp, tabular data, mock for testing).
|
|
|
|
use crate::errors::FluidResult;
|
|
use crate::mixture::Mixture;
|
|
use crate::types::{CriticalPoint, FluidId, Phase, Property, FluidState, ThermoState};
|
|
use entropyk_core::{Pressure, Temperature};
|
|
|
|
/// Trait for fluid property backends.
|
|
///
|
|
/// Implementors must provide methods to query thermodynamic properties
|
|
/// for various fluids. This allows the solver to work with different
|
|
/// property sources (CoolProp, tabular data, mock data for testing).
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// use entropyk_fluids::{FluidBackend, FluidId, Property, FluidState, ThermoState, FluidError, FluidResult, CriticalPoint};
|
|
///
|
|
/// struct MyBackend;
|
|
/// impl FluidBackend for MyBackend {
|
|
/// fn property(&self, _fluid: FluidId, _property: Property, _state: FluidState) -> FluidResult<f64> {
|
|
/// Ok(1.0)
|
|
/// }
|
|
/// fn critical_point(&self, fluid: FluidId) -> FluidResult<CriticalPoint> {
|
|
/// Err(FluidError::NoCriticalPoint { fluid: fluid.0 })
|
|
/// }
|
|
/// fn is_fluid_available(&self, _fluid: &FluidId) -> bool { false }
|
|
/// fn phase(&self, _fluid: FluidId, _state: FluidState) -> FluidResult<entropyk_fluids::Phase> {
|
|
/// Ok(entropyk_fluids::Phase::Unknown)
|
|
/// }
|
|
/// fn full_state(&self, _fluid: FluidId, _p: entropyk_core::Pressure, _h: entropyk_core::Enthalpy) -> FluidResult<ThermoState> {
|
|
/// Err(FluidError::UnsupportedProperty { property: "full_state".to_string() })
|
|
/// }
|
|
/// fn list_fluids(&self) -> Vec<FluidId> { vec![] }
|
|
/// }
|
|
/// ```
|
|
pub trait FluidBackend: Send + Sync {
|
|
/// Query a thermodynamic property for a fluid at a given state.
|
|
///
|
|
/// # Arguments
|
|
/// * `fluid` - The fluid identifier (e.g., "R134a", "CO2")
|
|
/// * `property` - The property to query
|
|
/// * `state` - The thermodynamic state specification
|
|
///
|
|
/// # Returns
|
|
/// The property value in SI units, or an error if the property
|
|
/// cannot be computed (unknown fluid, invalid state, etc.)
|
|
fn property(&self, fluid: FluidId, property: Property, state: FluidState) -> FluidResult<f64>;
|
|
|
|
/// Compute the complete thermodynamic state of a fluid at a given pressure and enthalpy.
|
|
///
|
|
/// This method is intended to be implemented by backends capable of natively calculating
|
|
/// all key parameters (phase, saturation temperatures, qualities, limits) without the user
|
|
/// needing to query them individually.
|
|
///
|
|
/// # Arguments
|
|
/// * `fluid` - The fluid identifier
|
|
/// * `p` - The absolute pressure
|
|
/// * `h` - The specific enthalpy
|
|
///
|
|
/// # Returns
|
|
/// The comprehensive `ThermoState` Snapshot, or an Error.
|
|
fn full_state(&self, fluid: FluidId, p: Pressure, h: entropyk_core::Enthalpy) -> FluidResult<ThermoState>;
|
|
|
|
/// Get critical point data for a fluid.
|
|
///
|
|
/// # Arguments
|
|
/// * `fluid` - The fluid identifier
|
|
///
|
|
/// # Returns
|
|
/// The critical point (Tc, Pc, density), or an error if not available
|
|
fn critical_point(&self, fluid: FluidId) -> FluidResult<CriticalPoint>;
|
|
|
|
/// Check if a fluid is available in this backend.
|
|
///
|
|
/// # Arguments
|
|
/// * `fluid` - The fluid identifier
|
|
///
|
|
/// # Returns
|
|
/// `true` if the fluid is available, `false` otherwise
|
|
fn is_fluid_available(&self, fluid: &FluidId) -> bool;
|
|
|
|
/// Get the phase of a fluid at a given state.
|
|
///
|
|
/// # Arguments
|
|
/// * `fluid` - The fluid identifier
|
|
/// * `state` - The thermodynamic state
|
|
///
|
|
/// # Returns
|
|
/// The phase (Liquid, Vapor, TwoPhase, etc.)
|
|
fn phase(&self, fluid: FluidId, state: FluidState) -> FluidResult<Phase>;
|
|
|
|
/// List all available fluids in this backend.
|
|
fn list_fluids(&self) -> Vec<FluidId>;
|
|
|
|
/// Calculate the bubble point temperature for a mixture at given pressure.
|
|
///
|
|
/// The bubble point is the temperature at which a liquid mixture begins to boil
|
|
/// (saturated liquid temperature).
|
|
///
|
|
/// # Arguments
|
|
/// * `pressure` - The pressure in Pa
|
|
/// * `mixture` - The mixture composition
|
|
///
|
|
/// # Returns
|
|
/// The bubble point temperature in Kelvin
|
|
fn bubble_point(&self, _pressure: Pressure, _mixture: &Mixture) -> FluidResult<Temperature> {
|
|
Err(crate::errors::FluidError::UnsupportedProperty {
|
|
property: "Bubble point calculation not supported by this backend".to_string(),
|
|
})
|
|
}
|
|
|
|
/// Calculate the dew point temperature for a mixture at given pressure.
|
|
///
|
|
/// The dew point is the temperature at which a vapor mixture begins to condense
|
|
/// (saturated vapor temperature).
|
|
///
|
|
/// # Arguments
|
|
/// * `pressure` - The pressure in Pa
|
|
/// * `mixture` - The mixture composition
|
|
///
|
|
/// # Returns
|
|
/// The dew point temperature in Kelvin
|
|
fn dew_point(&self, _pressure: Pressure, _mixture: &Mixture) -> FluidResult<Temperature> {
|
|
Err(crate::errors::FluidError::UnsupportedProperty {
|
|
property: "Dew point calculation not supported by this backend".to_string(),
|
|
})
|
|
}
|
|
|
|
/// Calculate the temperature glide for a mixture at given pressure.
|
|
///
|
|
/// Temperature glide is the difference between dew point and bubble point
|
|
/// temperatures: T_glide = T_dew - T_bubble.
|
|
/// This is non-zero for zeotropic mixtures and zero for azeotropes/pure fluids.
|
|
///
|
|
/// # Arguments
|
|
/// * `pressure` - The pressure in Pa
|
|
/// * `mixture` - The mixture composition
|
|
///
|
|
/// # Returns
|
|
/// The temperature glide in Kelvin
|
|
fn temperature_glide(&self, pressure: Pressure, mixture: &Mixture) -> FluidResult<f64> {
|
|
let t_bubble = self.bubble_point(pressure, mixture)?;
|
|
let t_dew = self.dew_point(pressure, mixture)?;
|
|
Ok(t_dew.to_kelvin() - t_bubble.to_kelvin())
|
|
}
|
|
|
|
/// Check if a mixture is supported by this backend.
|
|
///
|
|
/// # Arguments
|
|
/// * `mixture` - The mixture to check
|
|
///
|
|
/// # Returns
|
|
/// `true` if the mixture is supported, `false` otherwise
|
|
fn is_mixture_supported(&self, mixture: &Mixture) -> bool {
|
|
// Default implementation: check if all components are available
|
|
mixture
|
|
.components()
|
|
.iter()
|
|
.all(|c| self.is_fluid_available(&FluidId::new(c)))
|
|
}
|
|
}
|