Files
Entropyk/crates/fluids/src/backend.rs

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)))
}
}