refprop-rs 0.3.1

Safe Rust bindings for NIST REFPROP – thermodynamic & transport properties of refrigerants, pure fluids, and mixtures
Documentation

refprop-rs

Safe Rust bindings for NIST REFPROP -- thermodynamic and transport properties of refrigerants, pure fluids, and mixtures.

Features

  • Pure fluids -- Fluid::new("R134A"), Fluid::new("CO2"), ...
  • Predefined mixtures -- Fluid::new("R410A") (auto-loaded from .MIX files)
  • Custom mixtures -- Fluid::mixture(&[("R32", 0.5), ("R125", 0.5)])
  • CoolProp-style get() -- fluid.get("D", "T", 0.0, "Q", 1.0)
  • Configurable units -- work in °C + bar + kg/m³ + kJ/kg, or K + kPa, or any mix
  • Flash calculations -- TP, TD, TH, TS, TQ, PD, PH, PS, PQ, DH, DS, HS
  • Saturation, transport, critical point, fluid info
  • Thread-safe -- global mutex with automatic fluid re-setup
  • Parallel computation (opt-in) -- ParallelFluid duplicates the DLL/SO per CPU core for true multi-threaded throughput via rayon
  • FluidApi trait -- common interface for Fluid and ParallelFluid, write generic code that works with either backend
  • Dynamic loading -- no compile-time linking, just point to your REFPROP installation

Prerequisites

A licensed REFPROP installation (v9.1 or v10).

Installation

Add to your Cargo.toml:

[dependencies]

refprop-rs = { git = "https://github.com/math-dev-24/refprop-rs" }

To enable parallel computation (optional):

[dependencies]

refprop-rs = { git = "https://github.com/math-dev-24/refprop-rs", features = ["parallel"] }

The library name is refprop, so you import it as:

use refprop::{Fluid, UnitSystem};

// With the `parallel` feature:
use refprop::{ParallelFluid, UnitSystem};

Configuration

Tell the library where REFPROP is installed. Create a .env file at the project root (or set the environment variable directly):

REFPROP_PATH='C:\Program Files (x86)\REFPROP'

The library also checks standard install locations automatically.

Quick start

Engineering units (°C, bar, kg/m³, kJ/kg)

use refprop::{Fluid, UnitSystem};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let co2 = Fluid::with_units("CO2", UnitSystem::engineering())?;

    // Everything is in °C, bar, kg/m³, kJ/kg -- no manual conversion!
    let p = co2.get("P", "T", -5.0, "Q", 1.0)?;
    println!("Psat(-5 °C) = {p:.2} bar");

    let d = co2.get("D", "T", -5.0, "Q", 1.0)?;
    println!("D_vap(-5 °C) = {d:.2} kg/m³");

    let h = co2.get("H", "T", -5.0, "Q", 1.0)?;
    println!("H_vap(-5 °C) = {h:.2} kJ/kg");

    Ok(())
}

REFPROP native units (K, kPa, mol/L, J/mol)

use refprop::Fluid;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let r134a = Fluid::new("R134A")?;

    let d = r134a.get("D", "T", 273.15, "Q", 1.0)?;
    println!("density = {d:.6} mol/L");

    let props = r134a.props_tp(300.0, 500.0)?;
    println!("{props}");

    Ok(())
}

Unit system

Choose your preferred units at construction time. All inputs and outputs are automatically converted.

Presets

Preset T P D H S Viscosity Conductivity
UnitSystem::refprop() K kPa mol/L J/mol J/(mol·K) µPa·s W/(m·K)
UnitSystem::engineering() °C bar kg/m³ kJ/kg kJ/(kg·K) µPa·s W/(m·K)
UnitSystem::si() K Pa kg/m³ J/kg J/(kg·K) Pa·s W/(m·K)

Custom builder

Pick only the units you care about; the rest stay at REFPROP defaults:

use refprop::{Fluid, UnitSystem, TempUnit, PressUnit};

let units = UnitSystem::new()
    .temperature(TempUnit::Celsius)
    .pressure(PressUnit::Bar);
    // density stays mol/L, energy stays J/mol, etc.

let fluid = Fluid::with_units("R134A", units)?;
let sat = fluid.saturation_t(0.0)?;  // 0 °C directly
println!("Psat = {:.2} bar", sat.pressure);

Available unit choices

Property Options
Temperature Kelvin, Celsius, Fahrenheit
Pressure KPa, Bar, MPa, Pa, Atm, Psi
Density MolPerL, KgPerM3
Energy/Enthalpy JPerMol, KJPerKg, JPerKg
Entropy/Cv/Cp JPerMolK, KJPerKgK, JPerKgK
Viscosity MicroPaS, MilliPaS, PaS
Conductivity WPerMK, MilliWPerMK

Mixtures

use refprop::{Fluid, UnitSystem};

// Predefined mixture (from .MIX file)
let r410a = Fluid::with_units("R410A", UnitSystem::engineering())?;

// Custom composition
let r454c = Fluid::mixture_with_units(
    &[("R32", 0.215), ("R1234YF", 0.785)],
    UnitSystem::engineering(),
)?;

let p = r454c.get("P", "T", 0.0, "Q", 0.0)?;
println!("R454C Psat(0 °C) = {p:.2} bar");

get() -- generic property lookup

// get(output, key1, val1, key2, val2) -> f64
let density = fluid.get("D", "T", 25.0, "P", 10.0)?;

Input pairs (order-independent)

Pair Description
T, P Temperature + Pressure
P, H Pressure + Enthalpy
P, S Pressure + Entropy
T, Q Temperature + Quality
P, Q Pressure + Quality
T, D Temperature + Density
T, H Temperature + Enthalpy
T, S Temperature + Entropy
P, D Pressure + Density
D, H Density + Enthalpy
D, S Density + Entropy
H, S Enthalpy + Entropy

Output keys

Key Property
T Temperature
P Pressure
D Density
H Enthalpy
S Entropy
Q Quality (vapor frac.)
Cv Heat capacity (v)
Cp Heat capacity (p)
W Speed of sound
E Internal energy
ETA Dynamic viscosity
TCX Thermal conductivity

Units depend on the UnitSystem you chose at construction time.

Flash & saturation methods

All methods respect the configured unit system.

let props = fluid.props_tp(25.0, 10.0)?;   // TP flash
let props = fluid.props_ph(10.0, 250.0)?;  // PH flash
let props = fluid.props_ps(10.0, 1.2)?;    // PS flash
let props = fluid.props_tq(0.0, 1.0)?;     // TQ flash (saturation)
let props = fluid.props_pq(5.0, 0.0)?;     // PQ flash (saturation)
let props = fluid.props_td(50.0, 30.0)?;   // TD flash
let props = fluid.props_th(50.0, 280.0)?;  // TH flash
let props = fluid.props_ts(50.0, 1.1)?;    // TS flash
let props = fluid.props_pd(5.0, 30.0)?;    // PD flash
let props = fluid.props_dh(30.0, 280.0)?;  // DH flash
let props = fluid.props_ds(30.0, 1.1)?;    // DS flash
let props = fluid.props_hs(280.0, 1.1)?;   // HS flash

let sat = fluid.saturation_t(0.0)?;        // saturation at T
let sat = fluid.saturation_p(5.0)?;        // saturation at P

let crit = fluid.critical_point()?;        // Tc, Pc, Dc
let trn  = fluid.transport(25.0, d)?;      // viscosity, conductivity
let info = fluid.info()?;                  // molar mass, Ttrp, Tnbp, ...

FluidApi trait -- generic code

Fluid and ParallelFluid both implement the FluidApi trait, so you can write functions that work with either backend:

use refprop::{FluidApi, Result};

fn density(f: &impl FluidApi, t: f64, p: f64) -> Result<f64> {
    f.get("D", "T", t, "P", p)
}

fn report(f: &dyn FluidApi) -> Result<()> {
    let cp = f.critical_point()?;
    println!("Tc = {:.2}, Pc = {:.2}", cp.temperature, cp.pressure);

    let sat = f.saturation_t(0.0)?;
    println!("Psat(0) = {:.2}", sat.pressure);

    let info = f.info()?;
    println!("M = {:.2} g/mol", info.molar_mass);
    Ok(())
}

The trait includes all single-point methods: get, all flash variants (props_tp, props_ph, ..., props_hs), saturation_t, saturation_p, transport, critical_point, info, and converter.

Parallel-only methods (par_get, par_props_*, worker_count) remain exclusive to ParallelFluid.

Parallel computation

Feature gate: parallel -- adds rayon and num_cpus as dependencies.

REFPROP (the Fortran DLL) uses global internal state, making it impossible to call from multiple threads simultaneously. ParallelFluid works around this by copying the shared library once per CPU core into a temporary directory. Each copy is loaded independently, giving it its own Fortran state. Work is then distributed across these isolated instances via rayon.

How it works

  1. Detects the number of logical CPU cores (or use a custom count)
  2. Copies the DLL/SO/dylib N times into a temp directory
  3. Loads each copy as an independent RefpropLibrary
  4. Distributes batch computations across workers via rayon
  5. Cleans up temp files automatically on Drop

Quick example

use refprop::{ParallelFluid, UnitSystem};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Auto-detects CPU cores, copies the DLL N times
    let pool = ParallelFluid::with_units("R134A", UnitSystem::engineering())?;
    println!("Workers: {}", pool.worker_count());

    // 10 000 (T, P) → density computations in parallel
    let temps: Vec<f64> = (0..10_000).map(|i| -20.0 + i as f64 * 0.01).collect();
    let press: Vec<f64> = vec![10.0; 10_000];
    let densities = pool.par_get("D", "T", &temps, "P", &press)?;

    // Also works for batch flash calculations
    let inputs: Vec<(f64, f64)> = temps.iter().zip(&press).map(|(&t, &p)| (t, p)).collect();
    let props = pool.par_props_tp(&inputs); // Vec<Result<ThermoProp>>

    // Single-point API identical to Fluid
    let p = pool.get("P", "T", 0.0, "Q", 100.0)?;

    // Mixtures too
    let mix = ParallelFluid::mixture_with_units(
        &[("R32", 0.5), ("R125", 0.5)],
        UnitSystem::engineering(),
    )?;
    Ok(())
}

Constructors

Constructor Description
ParallelFluid::new(name) Pure/predefined mix, REFPROP units, auto core count
ParallelFluid::with_units(name, units) Custom units, auto core count
ParallelFluid::with_workers(name, n) REFPROP units, explicit worker count
ParallelFluid::with_units_and_workers(name, units, n) Full control
ParallelFluid::mixture(comps) Custom mixture, REFPROP units
ParallelFluid::mixture_with_units(comps, units) Custom mixture, custom units
ParallelFluid::mixture_with_units_and_workers(comps, units, n) Full control

Batch methods

Method Input Output
par_get(out, k1, &v1s, k2, &v2s) Two &[f64] slices Result<Vec<f64>>
par_props_tp(&[(T, P)]) &[(f64, f64)] Vec<Result<ThermoProp>>
par_props_ph(&[(P, H)]) &[(f64, f64)] Vec<Result<ThermoProp>>
par_props_ps(&[(P, S)]) &[(f64, f64)] Vec<Result<ThermoProp>>
par_props_tq(&[(T, Q)]) &[(f64, f64)] Vec<Result<ThermoProp>>
par_props_pq(&[(P, Q)]) &[(f64, f64)] Vec<Result<ThermoProp>>

All single-point methods from Fluid (get, props_tp, critical_point, etc.) are also available on ParallelFluid.

Platform support

OS Library files detected
Windows 64-bit REFPRP64.DLL, REFPROP.DLL, refprop.dll
Windows 32-bit REFPROP.DLL, refprop.dll
Linux librefprop.so, libREFPROP.so
macOS librefprop.dylib, libREFPROP.dylib

Project structure

refprop-rs/
├── Cargo.toml              single crate: refprop-rs
├── src/
│   ├── lib.rs              public API & re-exports
│   ├── fluid.rs            Fluid struct (high-level API)
│   ├── pool.rs             ParallelFluid (feature = "parallel")
│   ├── traits.rs           FluidApi trait (common interface)
│   ├── converter.rs        UnitSystem + Converter
│   ├── sys.rs              low-level FFI (libloading)
│   ├── error.rs            error types
│   ├── properties.rs       result structs
│   └── backend/
│       └── refprop.rs      REFPROP backend (flash, sat, etc.)
└── examples/
    ├── demo.rs             engineering units showcase
    ├── simple.rs           pure fluid, native units
    ├── mixture.rs          predefined & custom mixtures
    └── parallel.rs         parallel batch computation
Module Role
sys Dynamic DLL loading + raw FFI function wrappers
converter UnitSystem + Converter (unit conversion)
traits FluidApi trait -- common interface for generic code
fluid High-level API: Fluid, get(), flash, units
pool ParallelFluid -- DLL pool + rayon batch methods
backend::refprop Core REFPROP calls, global & isolated state management

License

MIT