362 lines
11 KiB
Rust
362 lines
11 KiB
Rust
//! FFI bindings to CoolProp C++ library.
|
|
//!
|
|
//! This module provides low-level FFI bindings to the CoolProp library.
|
|
//! All functions are unsafe and require proper error handling.
|
|
|
|
#![allow(dead_code)]
|
|
|
|
use libc::{c_char, c_double, c_int};
|
|
use std::ffi::CString;
|
|
|
|
/// Error codes returned by CoolProp
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
#[repr(i32)]
|
|
pub enum CoolPropError {
|
|
/// No error occurred
|
|
NoError = 0,
|
|
/// Input error code
|
|
InputError = 1,
|
|
/// Library not loaded
|
|
LibraryNotLoaded = 2,
|
|
/// Unknown property value
|
|
UnknownPropertyValue = 3,
|
|
/// Unknown fluid
|
|
UnknownFluid = 4,
|
|
/// Unknown parameter
|
|
UnknownParameter = 5,
|
|
/// Not implemented
|
|
NotImplemented = 6,
|
|
/// Invalid number of parameters
|
|
InvalidNumber = 7,
|
|
/// Could not load library
|
|
CouldNotLoadLibrary = 8,
|
|
/// Invalid fluid pair
|
|
InvalidFluidPair = 9,
|
|
/// Version mismatch
|
|
VersionMismatch = 10,
|
|
/// Internal error
|
|
InternalError = 11,
|
|
}
|
|
|
|
impl CoolPropError {
|
|
/// Convert CoolProp error code to Rust result
|
|
pub fn from_code(code: i32) -> Result<(), CoolPropError> {
|
|
match code {
|
|
0 => Ok(()),
|
|
_ => Err(match code {
|
|
1 => CoolPropError::InputError,
|
|
2 => CoolPropError::LibraryNotLoaded,
|
|
3 => CoolPropError::UnknownPropertyValue,
|
|
4 => CoolPropError::UnknownFluid,
|
|
5 => CoolPropError::UnknownParameter,
|
|
6 => CoolPropError::NotImplemented,
|
|
7 => CoolPropError::InvalidNumber,
|
|
8 => CoolPropError::CouldNotLoadLibrary,
|
|
9 => CoolPropError::InvalidFluidPair,
|
|
10 => CoolPropError::VersionMismatch,
|
|
_ => CoolPropError::InternalError,
|
|
}),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Output parameters for CoolProp
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
#[repr(i32)]
|
|
pub enum CoolPropParam {
|
|
/// Nothing
|
|
Nothing = 0,
|
|
/// Pressure [Pa]
|
|
Pressure = 1,
|
|
/// Temperature [K]
|
|
Temperature = 2,
|
|
/// Density [kg/m³]
|
|
Density = 3,
|
|
/// Specific enthalpy [J/kg]
|
|
Enthalpy = 4,
|
|
/// Specific entropy [J/kg/K]
|
|
Entropy = 5,
|
|
/// Specific internal energy [J/kg]
|
|
InternalEnergy = 6,
|
|
/// Specific heat at constant pressure [J/kg/K]
|
|
Cv = 7,
|
|
/// Specific heat at constant pressure [J/kg/K]
|
|
Cp = 8,
|
|
/// Quality [-]
|
|
Quality = 9,
|
|
/// Viscosity [Pa·s]
|
|
Viscosity = 10,
|
|
/// Thermal conductivity [W/m/K]
|
|
Conductivity = 11,
|
|
/// Surface tension [N/m]
|
|
SurfaceTension = 12,
|
|
/// Prandtl number [-]
|
|
Prandtl = 13,
|
|
}
|
|
|
|
/// Input parameters for CoolProp
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
#[repr(i32)]
|
|
pub enum CoolPropInputPair {
|
|
/// No input
|
|
None = 0,
|
|
/// Pressure & Temperature
|
|
PT = 1,
|
|
/// Pressure & Density
|
|
PD = 2,
|
|
/// Pressure & Enthalpy
|
|
PH = 3,
|
|
/// Pressure & Entropy
|
|
PS = 4,
|
|
/// Pressure & Internal Energy
|
|
PU = 5,
|
|
/// Temperature & Density
|
|
TD = 6,
|
|
/// Temperature & Enthalpy
|
|
TH = 7,
|
|
/// Temperature & Entropy
|
|
TS = 8,
|
|
/// Temperature & Internal Energy
|
|
TU = 9,
|
|
/// Enthalpy & Entropy
|
|
HS = 10,
|
|
/// Density & Internal Energy
|
|
DU = 11,
|
|
/// Pressure & Quality
|
|
PQ = 12,
|
|
/// Temperature & Quality
|
|
TQ = 13,
|
|
}
|
|
|
|
// CoolProp C functions
|
|
extern "C" {
|
|
/// Get a property value using pressure and temperature
|
|
/// Get a property value using pressure and temperature
|
|
#[cfg_attr(target_os = "macos", link_name = "\x01__Z7PropsSIPKcS0_dS0_dS0_")]
|
|
#[cfg_attr(not(target_os = "macos"), link_name = "_Z7PropsSIPKcS0_dS0_dS0_")]
|
|
fn PropsSI(
|
|
Output: *const c_char,
|
|
Name1: *const c_char,
|
|
Value1: c_double,
|
|
Name2: *const c_char,
|
|
Value2: c_double,
|
|
Fluid: *const c_char,
|
|
) -> c_double;
|
|
|
|
/// Get a property value using input pair
|
|
#[cfg_attr(target_os = "macos", link_name = "\x01__Z8Props1SIPKcS0_")]
|
|
#[cfg_attr(not(target_os = "macos"), link_name = "_Z8Props1SIPKcS0_")]
|
|
fn Props1SI(Fluid: *const c_char, Output: *const c_char) -> c_double;
|
|
|
|
/// Get CoolProp version string
|
|
#[cfg_attr(target_os = "macos", link_name = "\x01__Z23get_global_param_stringPKcPci")]
|
|
#[cfg_attr(not(target_os = "macos"), link_name = "get_global_param_string")]
|
|
fn get_global_param_string(
|
|
Param: *const c_char,
|
|
Output: *mut c_char,
|
|
OutputLength: c_int,
|
|
) -> c_int;
|
|
|
|
/// Get fluid info
|
|
#[cfg_attr(target_os = "macos", link_name = "\x01__Z22get_fluid_param_stringPKcS0_Pci")]
|
|
#[cfg_attr(not(target_os = "macos"), link_name = "get_fluid_param_string")]
|
|
fn get_fluid_param_string(
|
|
Fluid: *const c_char,
|
|
Param: *const c_char,
|
|
Output: *mut c_char,
|
|
OutputLength: c_int,
|
|
) -> c_int;
|
|
|
|
// Check if fluid exists
|
|
// CoolProp doesn't have a direct C isfluid function. We usually just try to fetch a string or param or we can map it downstream
|
|
// But let's see if we can just dummy it or use get_fluid_param_string
|
|
|
|
// There is no C CriticalPoint, it's just Props1SI("Tcrit", "Water")
|
|
|
|
}
|
|
|
|
/// Get a thermodynamic property using pressure and temperature.
|
|
///
|
|
/// # Arguments
|
|
/// * `property` - The property to retrieve (e.g., "D" for density, "H" for enthalpy)
|
|
/// * `p` - Pressure in Pa
|
|
/// * `t` - Temperature in K
|
|
/// * `fluid` - Fluid name (e.g., "R134a")
|
|
///
|
|
/// # Returns
|
|
/// # Safety
|
|
/// This function calls the CoolProp C++ library and passes a CString pointer.
|
|
/// The caller must ensure the fluid string is properly null-terminated if needed and valid.
|
|
pub unsafe fn props_si_pt(property: &str, p: f64, t: f64, fluid: &str) -> f64 {
|
|
let prop_c = std::ffi::CString::new(property).unwrap();
|
|
let fluid_c = CString::new(fluid).unwrap();
|
|
|
|
PropsSI(prop_c.as_ptr(), c"P".as_ptr(), p, c"T".as_ptr(), t, fluid_c.as_ptr())
|
|
}
|
|
|
|
/// Get a thermodynamic property using pressure and enthalpy.
|
|
///
|
|
/// # Arguments
|
|
/// * `property` - The property to retrieve
|
|
/// * `p` - Pressure in Pa
|
|
/// * `h` - Specific enthalpy in J/kg
|
|
/// * `fluid` - Fluid name
|
|
///
|
|
/// # Returns
|
|
/// # Safety
|
|
/// This function calls the CoolProp C++ library and passes a CString pointer.
|
|
/// The caller must ensure the fluid string is valid.
|
|
pub unsafe fn props_si_ph(property: &str, p: f64, h: f64, fluid: &str) -> f64 {
|
|
let prop_c = std::ffi::CString::new(property).unwrap();
|
|
let fluid_c = CString::new(fluid).unwrap();
|
|
|
|
PropsSI(prop_c.as_ptr(), c"P".as_ptr(), p, c"H".as_ptr(), h, fluid_c.as_ptr())
|
|
}
|
|
|
|
/// Get a thermodynamic property using temperature and quality (saturation).
|
|
///
|
|
/// # Arguments
|
|
/// * `property` - The property to retrieve (D, H, S, P, etc.)
|
|
/// * `t` - Temperature in K
|
|
/// * `q` - Quality (0 = saturated liquid, 1 = saturated vapor)
|
|
/// * `fluid` - Fluid name
|
|
///
|
|
/// # Returns
|
|
/// # Safety
|
|
/// This function calls the CoolProp C++ library and passes a CString pointer.
|
|
/// The caller must ensure the fluid string is valid.
|
|
pub unsafe fn props_si_tq(property: &str, t: f64, q: f64, fluid: &str) -> f64 {
|
|
let prop_c = std::ffi::CString::new(property).unwrap();
|
|
let fluid_c = CString::new(fluid).unwrap();
|
|
|
|
PropsSI(prop_c.as_ptr(), c"T".as_ptr(), t, c"Q".as_ptr(), q, fluid_c.as_ptr())
|
|
}
|
|
|
|
/// Get a thermodynamic property using pressure and quality.
|
|
///
|
|
/// # Arguments
|
|
/// * `property` - The property to retrieve
|
|
/// * `p` - Pressure in Pa
|
|
/// * `x` - Quality (0-1)
|
|
/// * `fluid` - Fluid name
|
|
///
|
|
/// # Returns
|
|
/// # Safety
|
|
/// This function calls the CoolProp C++ library and passes a CString pointer.
|
|
/// The caller must ensure the fluid string is valid.
|
|
pub unsafe fn props_si_px(property: &str, p: f64, x: f64, fluid: &str) -> f64 {
|
|
let prop_c = std::ffi::CString::new(property).unwrap();
|
|
let fluid_c = CString::new(fluid).unwrap();
|
|
|
|
PropsSI(
|
|
prop_c.as_ptr(),
|
|
c"P".as_ptr(),
|
|
p,
|
|
c"Q".as_ptr(), // Q for quality
|
|
x,
|
|
fluid_c.as_ptr(),
|
|
)
|
|
}
|
|
|
|
/// Get critical point temperature for a fluid.
|
|
///
|
|
/// # Arguments
|
|
/// * `fluid` - Fluid name
|
|
///
|
|
/// # Returns
|
|
/// # Safety
|
|
/// This function calls the CoolProp C++ library and passes a CString pointer.
|
|
/// The caller must ensure the fluid string is valid.
|
|
pub unsafe fn critical_temperature(fluid: &str) -> f64 {
|
|
let fluid_c = CString::new(fluid).unwrap();
|
|
Props1SI(fluid_c.as_ptr(), c"Tcrit".as_ptr())
|
|
}
|
|
|
|
/// Get critical point pressure for a fluid.
|
|
///
|
|
/// # Arguments
|
|
/// * `fluid` - Fluid name
|
|
///
|
|
/// # Returns
|
|
/// # Safety
|
|
/// This function calls the CoolProp C++ library and passes a CString pointer.
|
|
/// The caller must ensure the fluid string is valid.
|
|
pub unsafe fn critical_pressure(fluid: &str) -> f64 {
|
|
let fluid_c = CString::new(fluid).unwrap();
|
|
Props1SI(fluid_c.as_ptr(), c"pcrit".as_ptr())
|
|
}
|
|
|
|
/// Get critical point density for a fluid.
|
|
///
|
|
/// # Arguments
|
|
/// * `fluid` - Fluid name
|
|
///
|
|
/// # Returns
|
|
/// # Safety
|
|
/// This function calls the CoolProp C++ library and passes a CString pointer.
|
|
/// The caller must ensure the fluid string is valid.
|
|
pub unsafe fn critical_density(fluid: &str) -> f64 {
|
|
let fluid_c = CString::new(fluid).unwrap();
|
|
Props1SI(fluid_c.as_ptr(), c"rhocrit".as_ptr())
|
|
}
|
|
|
|
/// Check if a fluid is available in CoolProp.
|
|
///
|
|
/// # Arguments
|
|
/// * `fluid` - Fluid name
|
|
///
|
|
/// # Returns
|
|
/// # Safety
|
|
/// This function calls the CoolProp C++ library and passes a CString pointer.
|
|
/// The caller must ensure the fluid string is valid.
|
|
pub unsafe fn is_fluid_available(fluid: &str) -> bool {
|
|
let fluid_c = CString::new(fluid).unwrap();
|
|
// CoolProp C API does not expose isfluid, so we try fetching a property
|
|
let res = Props1SI(fluid_c.as_ptr(), c"Tcrit".as_ptr());
|
|
if res.is_finite() && res != 0.0 { true } else { false }
|
|
}
|
|
|
|
/// Get CoolProp version string.
|
|
///
|
|
/// # Returns
|
|
/// Version string (e.g., "6.14.0")
|
|
pub fn get_version() -> String {
|
|
unsafe {
|
|
let mut buffer = vec![0u8; 32];
|
|
let result = get_global_param_string(
|
|
c"version".as_ptr(),
|
|
buffer.as_mut_ptr() as *mut c_char,
|
|
buffer.len() as c_int,
|
|
);
|
|
|
|
if result == 0 {
|
|
String::from_utf8_lossy(&buffer)
|
|
.trim_end_matches('\0')
|
|
.to_string()
|
|
} else {
|
|
String::from("Unknown")
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_version() {
|
|
let version = get_version();
|
|
assert!(!version.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn test_fluid_available() {
|
|
// Test some common refrigerants
|
|
unsafe {
|
|
assert!(is_fluid_available("R134a"));
|
|
assert!(is_fluid_available("R410A"));
|
|
assert!(is_fluid_available("CO2"));
|
|
}
|
|
}
|
|
}
|