refprop-rs 0.3.0

Safe Rust bindings for NIST REFPROP – thermodynamic & transport properties of refrigerants, pure fluids, and mixtures
Documentation
use refprop::{ParallelFluid, UnitSystem};
use std::time::Instant;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // ── Create a parallel pool ────────────────────────────────────────
    //
    // The pool automatically:
    //   1. Detects the number of CPU cores
    //   2. Copies the REFPROP DLL/SO once per core into a temp directory
    //   3. Loads each copy as an independent instance
    //
    // Each instance has its own Fortran global state, enabling true
    // parallel computation with zero lock contention.

    let pool = ParallelFluid::with_units("R134A", UnitSystem::engineering())?;
    println!(
        "Parallel pool created with {} workers\n",
        pool.worker_count()
    );

    // ── Single-point queries (same API as Fluid) ──────────────────────

    let p = pool.get("P", "T", 0.0, "Q", 100.0)?;
    println!("R134A Psat(0 °C) = {p:.4} bar");

    let crit = pool.critical_point()?;
    println!(
        "Critical point: {:.2} °C, {:.2} bar\n",
        crit.temperature, crit.pressure
    );

    // ── Batch parallel computation ────────────────────────────────────
    //
    // Compute density at 10 000 (T, P) points in parallel.
    // Work is automatically partitioned across all workers.

    let n = 10_000;
    let temps: Vec<f64> = (0..n).map(|i| -20.0 + i as f64 * 0.01).collect();
    let press: Vec<f64> = vec![10.0; n];

    let t0 = Instant::now();
    let densities = pool.par_get("D", "T", &temps, "P", &press)?;
    let elapsed = t0.elapsed();

    println!("par_get: {n} TP flash calculations");
    println!("  elapsed: {:.2?}", elapsed);
    println!(
        "  throughput: {:.0} calls/s",
        n as f64 / elapsed.as_secs_f64()
    );
    println!("  first: D({:.1} °C, 10 bar) = {:.4} kg/m³", temps[0], densities[0]);
    println!(
        "  last:  D({:.1} °C, 10 bar) = {:.4} kg/m³\n",
        temps[n - 1],
        densities[n - 1]
    );

    // ── Batch flash returning full ThermoProp structs ─────────────────

    let inputs: Vec<(f64, f64)> = temps[..100]
        .iter()
        .zip(&press[..100])
        .map(|(&t, &p)| (t, p))
        .collect();

    let results = pool.par_props_tp(&inputs);
    let ok_count = results.iter().filter(|r| r.is_ok()).count();
    println!("par_props_tp: {ok_count}/{} succeeded", inputs.len());

    if let Ok(first) = &results[0] {
        println!(
            "  T={:.2} °C  P={:.2} bar  D={:.4} kg/m³  H={:.2} kJ/kg",
            first.temperature, first.pressure, first.density, first.enthalpy
        );
    }

    // ── Parallel mixture ──────────────────────────────────────────────

    let mix_pool = ParallelFluid::mixture_with_units(
        &[("R32", 0.5), ("R125", 0.5)],
        UnitSystem::engineering(),
    )?;

    let mix_temps: Vec<f64> = (-20..=40).map(|t| t as f64).collect();
    let mix_q: Vec<f64> = vec![100.0; mix_temps.len()];

    let pressures = mix_pool.par_get("P", "T", &mix_temps, "Q", &mix_q)?;
    println!("\nR410A-like mixture saturation curve:");
    for (t, p) in mix_temps.iter().step_by(10).zip(pressures.iter().step_by(10)) {
        println!("  T = {t:6.1} °C  ->  Psat = {p:.4} bar");
    }

    Ok(())
}