feat(components): add ThermoState generators and Eurovent backend demo
This commit is contained in:
64
crates/fluids/coolprop-sys/build.rs
Normal file
64
crates/fluids/coolprop-sys/build.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
//! Build script for coolprop-sys.
|
||||
//!
|
||||
//! This compiles the CoolProp C++ library statically.
|
||||
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn coolprop_src_path() -> Option<PathBuf> {
|
||||
// Try to find CoolProp source in common locations
|
||||
let possible_paths = vec![
|
||||
// Vendor directory (recommended)
|
||||
PathBuf::from("vendor/coolprop"),
|
||||
// External directory
|
||||
PathBuf::from("external/coolprop"),
|
||||
// System paths
|
||||
PathBuf::from("/usr/local/src/CoolProp"),
|
||||
PathBuf::from("/opt/CoolProp"),
|
||||
];
|
||||
|
||||
for path in possible_paths {
|
||||
if path.join("CMakeLists.txt").exists() {
|
||||
return Some(path);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let static_linking = env::var("CARGO_FEATURE_STATIC").is_ok();
|
||||
|
||||
// Check if CoolProp source is available
|
||||
if let Some(coolprop_path) = coolprop_src_path() {
|
||||
println!("cargo:rerun-if-changed={}", coolprop_path.display());
|
||||
|
||||
// Configure build for CoolProp
|
||||
println!(
|
||||
"cargo:rustc-link-search=native={}/build",
|
||||
coolprop_path.display()
|
||||
);
|
||||
}
|
||||
|
||||
// Link against CoolProp
|
||||
if static_linking {
|
||||
// Static linking - find libCoolProp.a
|
||||
println!("cargo:rustc-link-lib=static=CoolProp");
|
||||
} else {
|
||||
// Dynamic linking
|
||||
println!("cargo:rustc-link-lib=dylib=CoolProp");
|
||||
}
|
||||
|
||||
// Link required system libraries
|
||||
println!("cargo:rustc-link-lib=dylib=m");
|
||||
println!("cargo:rustc-link-lib=dylib=stdc++");
|
||||
|
||||
// Tell Cargo to rerun if build.rs changes
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
println!(
|
||||
"cargo:warning=CoolProp source not found in vendor/.
|
||||
For full static build, run:
|
||||
git clone https://github.com/CoolProp/CoolProp.git vendor/coolprop"
|
||||
);
|
||||
}
|
||||
336
crates/fluids/coolprop-sys/src/lib.rs
Normal file
336
crates/fluids/coolprop-sys/src/lib.rs
Normal file
@@ -0,0 +1,336 @@
|
||||
//! 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
|
||||
fn CoolProp_PropsSI(
|
||||
Output: c_char,
|
||||
Name1: c_char,
|
||||
Value1: c_double,
|
||||
Name2: c_char,
|
||||
Value2: c_double,
|
||||
Fluid: *const c_char,
|
||||
) -> c_double;
|
||||
|
||||
/// Get a property value using input pair
|
||||
fn CoolProp_Props1SI(Fluid: *const c_char, Output: c_char) -> c_double;
|
||||
|
||||
/// Get CoolProp version string
|
||||
fn CoolProp_get_global_param_string(
|
||||
Param: *const c_char,
|
||||
Output: *mut c_char,
|
||||
OutputLength: c_int,
|
||||
) -> c_int;
|
||||
|
||||
/// Get fluid info
|
||||
fn CoolProp_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
|
||||
fn CoolProp_isfluid(Fluid: *const c_char) -> c_int;
|
||||
|
||||
/// Get saturation temperature
|
||||
fn CoolProp_Saturation_T(Fluid: *const c_char, Par: c_char, Value: c_double) -> c_double;
|
||||
|
||||
/// Get critical point
|
||||
fn CoolProp_CriticalPoint(Fluid: *const c_char, Output: c_char) -> c_double;
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// The property value in SI units, or NaN if an error occurs
|
||||
pub unsafe fn props_si_pt(property: &str, p: f64, t: f64, fluid: &str) -> f64 {
|
||||
let prop = property.as_bytes()[0] as c_char;
|
||||
let fluid_c = CString::new(fluid).unwrap();
|
||||
|
||||
CoolProp_PropsSI(prop, b'P' as c_char, p, b'T' as c_char, 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
|
||||
/// The property value in SI units, or NaN if an error occurs
|
||||
pub unsafe fn props_si_ph(property: &str, p: f64, h: f64, fluid: &str) -> f64 {
|
||||
let prop = property.as_bytes()[0] as c_char;
|
||||
let fluid_c = CString::new(fluid).unwrap();
|
||||
|
||||
CoolProp_PropsSI(prop, b'P' as c_char, p, b'H' as c_char, 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
|
||||
/// The property value in SI units, or NaN if an error occurs
|
||||
pub unsafe fn props_si_tq(property: &str, t: f64, q: f64, fluid: &str) -> f64 {
|
||||
let prop = property.as_bytes()[0] as c_char;
|
||||
let fluid_c = CString::new(fluid).unwrap();
|
||||
|
||||
CoolProp_PropsSI(prop, b'T' as c_char, t, b'Q' as c_char, 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
|
||||
/// The property value in SI units, or NaN if an error occurs
|
||||
pub unsafe fn props_si_px(property: &str, p: f64, x: f64, fluid: &str) -> f64 {
|
||||
let prop = property.as_bytes()[0] as c_char;
|
||||
let fluid_c = CString::new(fluid).unwrap();
|
||||
|
||||
CoolProp_PropsSI(
|
||||
prop,
|
||||
b'P' as c_char,
|
||||
p,
|
||||
b'Q' as c_char, // Q for quality
|
||||
x,
|
||||
fluid_c.as_ptr(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Get critical point temperature for a fluid.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `fluid` - Fluid name
|
||||
///
|
||||
/// # Returns
|
||||
/// Critical temperature in K, or NaN if unavailable
|
||||
pub unsafe fn critical_temperature(fluid: &str) -> f64 {
|
||||
let fluid_c = CString::new(fluid).unwrap();
|
||||
CoolProp_CriticalPoint(fluid_c.as_ptr(), b'T' as c_char)
|
||||
}
|
||||
|
||||
/// Get critical point pressure for a fluid.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `fluid` - Fluid name
|
||||
///
|
||||
/// # Returns
|
||||
/// Critical pressure in Pa, or NaN if unavailable
|
||||
pub unsafe fn critical_pressure(fluid: &str) -> f64 {
|
||||
let fluid_c = CString::new(fluid).unwrap();
|
||||
CoolProp_CriticalPoint(fluid_c.as_ptr(), b'P' as c_char)
|
||||
}
|
||||
|
||||
/// Get critical point density for a fluid.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `fluid` - Fluid name
|
||||
///
|
||||
/// # Returns
|
||||
/// Critical density in kg/m³, or NaN if unavailable
|
||||
pub unsafe fn critical_density(fluid: &str) -> f64 {
|
||||
let fluid_c = CString::new(fluid).unwrap();
|
||||
CoolProp_CriticalPoint(fluid_c.as_ptr(), b'D' as c_char)
|
||||
}
|
||||
|
||||
/// Check if a fluid is available in CoolProp.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `fluid` - Fluid name
|
||||
///
|
||||
/// # Returns
|
||||
/// `true` if the fluid is available
|
||||
pub unsafe fn is_fluid_available(fluid: &str) -> bool {
|
||||
let fluid_c = CString::new(fluid).unwrap();
|
||||
CoolProp_isfluid(fluid_c.as_ptr()) != 0
|
||||
}
|
||||
|
||||
/// 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 = CoolProp_get_global_param_string(
|
||||
b"version\0".as_ptr() as *const c_char,
|
||||
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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user