Isolated System

Fluid property calls

2026-04-01

In this post, I’ll tell you how to calculate fluid properties in Matlab, Python and Julia.

I use Arch at home, AlmaLinux at work, and also have Windows 11 running on a work laptop. I won’t share every possible combo of OS and programming language because the article length would balloon out of control, but I’ll give some combinations that I use. Also, I’ll call them ‘folders’ rather than ‘directories’ because I came from a Windows background and that’s what I’m used to.

Refprop and Coolprop are both one-stop shops for addressing all of your fluid property needs. They abstract away the messy world of equations of state and leave you with a consistent interface for every kind of fluid: pure (like water), pseudo-pure (R-410A), predefined mixtures (like air), and even concoctions that are products of twisted imaginations. Refprop is proprietary and sold by NIST, whereas Coolprop is open-source, though made by the same people who made Refprop, so you can often treat them as interchangeable (with some differences explained later in this article).

Now, a disclaimer before I start. I’m paid (a generous Swiss salary) to be a cooling engineer and I enjoy playing around with this stuff in my free time, which is why I paid for a personal-use Refprop license, a personal-use XProps license, and have actually bought Matlab (I plan to pay for WinRAR next). If you would rather not, feel free to refer to just the CoolProp stuff below, and stick to Python and Julia.

Let’s install these first, then I’ll tell you how to use them and how they compare to each other. While I’m teaching you the wrappers, we will always calculate properties for CO2 under the following conditions: 20 bar pressure, 250 kJ/kg enthalpy. In each case, we expect a density of 142.10 kg/m3. The units won’t always be bar and kJ/kg though.

Refprop

There are two versions of Refprop, v9 and v10. There are significant changes in terms of how you call them, and which wrappers are supported. Globally, the instructions can be found on NIST’s Github page. Below is basically my experience following these instructions

Python

This one is on Linux because it is significantly easier to install on Windows.

You first need to have Refprop itself available on Linux before you can call it in Python. To do this, you need to get a copy of the Refprop’s installation file, something like NIST2310.EXE. Then:

  1. Install Wine: sudo pacman -S wine wine-tricks
  2. Install Refprop: wine NIST2310.EXE. This should install Refprop in your home folder. In my case, I could see the Refpropf files here: /home/vb/.wine/drive_c/Program Files (x86)/REFPROP/
  3. We now need a Linux-equivalent of a dll file. These are called ‘Shared Object’ files in linuxland. Specifically, we need librefprop.so. Start by creating a folder called ‘refprop’ in your home folder (to keep things clean) mkdir refprop && cd refprop.
  4. Recursively clone the NIST Refprop-cmake library git clone --recursive https://github.com/usnistgov/REFPROP-cmake.git
  5. From the wine REFPROP installation folder, copy the FORTRAN folder and paste it into the REFPROP-cmake folder you just downloaded: cp -r "/home/vb/.wine/drive_c/Program Files (x86)/REFPROP/FORTRAN" /home/vb/refprop/REFPROP-cmake/ (This might need to change if you have different folders)
  6. Make a build directory inside REFPROP-cmake mkdir build && cd build
  7. Now, you’re ready to make the files. First, configure the build: cmake .. -DCMAKE_BUILD_TYPE=Release
  8. Then, actually do the making: cmake --build . In my case, the build errored out before reaching completion, but just after having created librefprop.so, so job done.
  9. Copy this librefprop.so into the REFPROP .wine folder.

Now, in Python you should be able to do this:

import os
from ctREFPROP.ctREFPROP import REFPROPFunctionLibrary

os.environ['RPPREFIX'] = '/home/vb/.wine/drive_c/Program Files (x86)/REFPROP/'
RP = REFPROPFunctionLibrary(os.environ['RPPREFIX'])
RP.SETPATHdll(os.environ['RPPREFIX'])
r = RP.REFPROPdll("CO2","PH","D",RP.MASS_BASE_SI,0,0,20e5,250e3,[1.0]+[0.0]*19)
print(r.Output[0])  # Should be 142.10

Congratulations!

Matlab

Note, first, that Matlab themselves support calling Refprop and CoolProp both from a common interface. You can find the files and the instructions on Matlab File Exchange. This gets you a getFluidProperty function that will handle property calls for you.

Refprop 9 is now legacy, but it had first-class Matlab support. It uses the refpropm function call. Because I’ve never had luck running Matlab on Linux, all the instructions that follow are for Windows.

You’ll need five files:

You can find them in the Legacy folder of the Refprop Wrappers repository. They also seem to be available on NIST’s website, but those might be outdated versions.

You also need to download the refprop.h file.

Copy all these files into the Refprop installation folder (C:\Program Files (x86)\REFPROP\). refpropm.m must be placed in the REFPROP installation folder in Program Files, not in the Matlab path or anything.

Then, in Matlab, select “Add Path with Subfolders” and add the REFPROP folder. To test out whether this is working, type in an example command:

>> refpropm('D','P',20e2,'H',250e3,'CO2')  % Pressure is kPa in refpropm

ans =

  142.1041

With Refprop 10, the only blessed way of calling its from Matlab is by calling it via Python. The developers grew tired of supporting different Matlab versions and abandoned support for it.

I used Anaconda to install Python on my machine. With it, I opened the Anaconda Prompt and asked pip to install Refprop: pip install -U ctREFPROP. This worked for me, no trouble. If this doesn’t work for you, NIST recommend the wheel, which is “a ctypes based interface to Refprop that you can get from PYPI”. If you’re a Matlab person, PYPI is a repository of software for Python, aka something like an App store. A ‘ctypes’ interface means that the original code is written in C/C++ and is buried in a DLL. The interface just acts as a communicator between Python and the library. Finally, a ‘wheel’ is how you install Python packages that may not be in Pip or PYPI. The legacy method of packaging used to be eggs. Now it’s wheels.

Now, with ctREFPROP installed, we go into Matlab and follow the sample code that NIST recommend. They want you to specify the path to Refprop as thus: RP = py.ctREFPROP.ctREFPROP.REFPROPFunctionLibrary('C:\Program Files (x86)\REFPROP')

This didn’t work for me since there are multiple DLLs in my Refprop installation folder due to the aforementioned Refprop 9 tweak.

Error using ctREFPROP>REFPROPFunctionLibrary (line 159)
Python Error: ValueError: Too many loadable shared
libraries were found in the folder "C:\Program Files
(x86)\REFPROP"; obtained libraries were: ['C:\\Program
Files (x86)\\REFPROP\\REFPRP64.DLL', 'C:\\Program Files
(x86)\\REFPROP\\REFPRP64_thunk_pcwin64.dll'].  You must
provide an absolute path to the shared library you would
like to load

In particular, there is the REFPRP64.dll and the REFPRP64_thunk_pcwin64.dll. This is because I have Refprop 9 also installed. What we want is the former, so the modification is simple: RP = py.ctREFPROP.ctREFPROP.REFPROPFunctionLibrary('C:\Program Files (x86)\REFPROP\REFPRP64.dll') aka we link to it directly. This lead to a successful loading of the library.

Now, let’s see how to calculate things. The sample code is rather obscure:

>> MOLSI = RP.GETENUMdll(int8(0),'MOLAR BASE SI').iEnum;
>> iMass = int8(0); % 0: molar fractions; 1: mass fractions
>> iFlag = int8(0); % 0: don't call SATSPLN; 1: call SATSPLN
>> z = {1.0};
>> r = RP.REFPROPdll('Water','PQ','T',MOLSI,iMass,iFlag,101325,0.0,z);
>> o = double(r.Outputs);
>> o(1)
>> 373.1243

All of this is incredibly fiddly. 6 commands to get the density is obviously onerous, but c’est la vie.

Julia

Once again, you have to call Python from Julia.

First, install Pycall: ] add PyCall.

Typing the ] has taken you to the package management mode. After the installation, backspace out of it.

Next, we need to build PyCall. You could just build it the first time with Pkg.build("PyCall"), but my Python Refprop is not system-wide. Rather, it is installed to a specific venv to keep the system Python cleaner. I therefore need to link this Python call of Julia to a specific venv. So I do this instead:

ENV["PYTHON"] = "/home/vb/python/.venv/bin/python"
using Pkg
Pkg.build("PyCall")
using PyCall

I don’t have to do this after the first time. Next, we’re ready to call Refprop:

using PyCall

refprop = pyimport("ctREFPROP.ctREFPROP")
rp = refprop.REFPROPFunctionLibrary("/home/vb/.wine/drive_c/Program Files (x86)/REFPROP/")
rp.SETPATHdll("/home/vb/.wine/drive_c/Program Files (x86)/REFPROP/")

iUnits = rp.GETENUMdll(0, "MASS BASE SI")[1]
r = rp.REFPROPdll("CO2", "PH", "D", iUnits, 0, 0, 20e5, 250e3, [1.0])
density = r[2][1]
println("$density")  # Should be 142.1041...

CoolProp

Python

This is the easiest of the lot since the principal developers are all Python experts.

pip install CoolProp should work. Then try the following:

from CoolProp.CoolProp import PropsSI
print(PropsSI('D','P',20e5,'H',250e3,'CO2')

Julia

Again, it is trivial to get CoolProp running in Julia. Do add CoolProp in the package management mode. That should be all that you need.

using CoolProp
PropsSI("D","P",20e5,"H",250e3,"CO2")

Should spit out 142.1041.

Matlab

There are two ways to call CoolProp. The first (and honestly easier) method is to call the Python wrapper from within Matlab. The second is to load the dll library using Matlab, but then you only get access to the Low-Level Interface (see further into the article) of CoolProp, which is harder to understand intuitively, although ultimately more detailed and faster (the Python call is slow as molasses).

You should first make Python available to call through Matlab. It’s best if Python is already in your path. Then, you could always try pip install CoolProp, but this didn’t work on my machine. Despite trying all manner of things like installing a local copy instead of for all users, fiddling with PATH variables etc, the CoolProp install would always fail. Ultimately, having Anaconda handle my Python installation worked out a treat. Inelegant, but necessary in an imperfect world. Here are the steps for that:

  1. Get Anaconda (The install file is nearly half a gigabyte.)
  2. Install it.
  3. Open ‘Anaconda Prompt’ from your Start menu.
  4. Run this command to install CoolProp: conda install -c conda-forge coolprop.
  5. Find out where the pythonw.exe file of Anaconda is installed. In my case, it’s at C:\Users\<username>\anaconda3\pythonw.exe.
  6. Next, we need to tell Matlab how to run Python. Use the command pyversion('path/to/pythonw.exe'). The w at the end is for ensuring that a terminal window doesn’t open up when python is run, although I think just “python.exe” should also work fine.
  7. Run the command pyversion(), aka the same as before but this time without any arguments. If it outputs something like below, you’ve succeeded:
>> pyversion()

       version: '3.8'
    executable: 'C:\Users\<username>\anaconda3\pythonw.exe'
       library: 'C:\Users\<username>\anaconda3\python38.dll'
          home: 'C:\Users\<username>\anaconda3'
      isloaded: 0
  1. Let’s see if CoolProp works from Matlab now. Hopefully you get the same answer as me:
py.CoolProp.CoolProp.PropsSI('D','P',20e5,'H',250e3,'CO2')

ans =

   142.1041789...

If yes, then, congratulations. CoolProp now runs on your Matlab installation. Calling Python through Anaconda rather than natively likely has a performance penalty, but you signed up for it anyway when calling Python through Matlab, so who gives a damn.

Octave

Didn’t try it on Windows. For Arch, I followed the official instructions.

First, I downloaded COolProp from github: git clone https://github.com/CoolProp/CoolProp --recursive

Next, I needed six for Python: sudo pacman -S python-six. I tried to use my trusty ‘scientific computing’ venv to install six to not corrupt system python, but that wasn’t working, so installed it from Arch’s repos instead. You might need your own luck with your OS.

Next, I did the cmake:

mkdir -p build && cd build
cmake .. -DCOOLPROP_OCTAVE_MODULE=ON -DBUILD_TESTING=ON -DCMAKE_CXX_STANDARD=11
cmake --build .

This all seemed to work well. I then copied the file to octave’s path:

sudo cp CoolProp.oct /usr/share/octave/$(octave-config -p VERSION)/m/

To run it, then, I opened up octave in my terminal and tried to call it

octave:1> CoolProp
octave:2> PropsSI('D','P',20e5,'H',250e3,'CO2')

But, this would cause octave to freeze. Ctrl+C wouldn’t bring the prompt back, nor would Ctrl+U or Ctrl+D.

What followed was a long wild goose chase, expertly solved by Claude. Here’s what it said:

Recent versions of CoolProp added “superancillary” functions — precomputed Chebyshev polynomial expansions that approximate saturation properties without iterative solving. They are parsed and loaded into memory on first use.

The hang is likely a static initialization deadlock: when the SWIG oct module loads in Octave, it triggers C++ static initializers that try to parse the superancillary JSON data for all fluids simultaneously, and something in that process deadlocks under Octave’s threading model. It never completes, so the process hangs forever and ignores signals.

Setting COOLPROP_DISABLE_SUPERANCILLARIES_ENTIRELY=1 skips that entire parsing step, so initialization completes normally and calls proceed using the traditional iterative solvers instead.

To make it permanent, add this to ~/.config/fish/config.fish: set -x COOLPROP_DISABLE_SUPERANCILLARIES_ENTIRELY 1

And, by jove, it was right. I set set -Ux COOLPROP_DISABLE_SUPERANCILLARIES_ENTIRELY 1 to make it a permanent change. Then, the octave commands above started working again.

octave:1> CoolProp
octave:2> PropsSI('D','P',20e5,'H',250e3,'CO2')
CoolProp: superancillaries have been disabled because the COOLPROP_DISABLE_SUPERANCILLARIES_ENTIRELY environment variable has been defined
ans = 142.10

LibreOffice

Follow the official instructions and it should all work out, he says again. I had to do Step 5 (“Install CoolProp” button) to get it to work.

JavaScript

This is pretty easy. If you follow the link on the official instructions you can download the .js and .wasm files. Then you need to embed them into your html file. Instead of writing a bunch of repetitious instructions, I thought I’d show you a demo of the thing in action. The javascript code is embedded in the file so you can View Source if you want to copy it for yourself: CoolProp Demo

High-level interface comparison

Right, with all of this complicated installation behind us, let’s briefly stop by something not very complicated, before diving once again into complicated things.

The high-level interfaces are how you would call Refprop and CoolProp from your programming language without diving into the underlying static or dynamic libraries. These calls are pretty similar between both libraries, as this pseudo-code illustrates:

ref_prop = refpropm('D','P',20e5,'H',250e3,'CO2')
cool_prop = PropsSI('D','P',20e5,'H',250e3,'CO2')

There are, though, some crucial differences between the two libraries:

All in all, six of one, half a dozen of the other. Or, horses for courses. Or, the right tool for the job. Or, it is good to have both installed.

At any rate, the high level interfaces aren’t quite sufficient for our uses because when we are calling Python from Julia/Matlab for instances, we only see the low level interface.

Low-level interface

This is what the high level interfaces call in the backend anyway, and what we were using above for calculating the fluid properties when calling Refprop Python through Matlab/Julia for example.

Some day in the future, when I have more patience, I will describe the low level interfaces in more detail.

Enthalpy reference states

One final thing I want to discuss is the concept of reference states. Enthalpy, internal energy and entropy are relative things. We define what is our zero point (or another point), and all other values are calculated relative to it. Commonly used reference states are:

  1. Enthalpy/Entropy = 0 at saturated liquid at Normal Boiling Point (NBP)
  2. Enthalpy/Entropy = 0 at saturated liquid at -40°C (ASHRAE)
  3. Enthalpy = 200 kJ/kg, Entropy = 1 kJ/(kg-K) for saturated liquid at 0°C (IIR)

Of course, some heretics disregard all these standards and just go their own way.

Fortunately for me, though, Refprop and Coolprop both use the same reference states for CO2, which is the third one above.