# thermo: pure component properties and phase equilibrium in pure Python

> A thermodynamics library for chemical engineers that loads constant properties for compounds, builds equations of state and correlation models from them, and runs flash calculations for mixtures, with a two interface design that goes from one line for a pure substance to a full VL flash for a mixture.

**CalebBell/thermo** — Thermodynamics and Phase Equilibrium component of Chemical Engineering Design Library (ChEDL)

- Repository: https://github.com/CalebBell/thermo
- Stars: 795 · Forks: 145
- Language: Python
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/calebbell-thermo

## Two interfaces, and the simple one is a single line

The library is described as open-source software for engineers, scientists, technicians and anyone trying to understand the universe in more detail. It facilitates the retrieval of constants of chemicals, the calculation of temperature and pressure dependent chemical properties, both thermodynamic and transport, and the calculation of the same for chemical mixtures including phase equilibria using various models.

The documentation presents this as two levels. The simple interface is built around a `Chemical` object, and the example is short enough to be the whole story:

```python
from thermo.chemical import Chemical
tol = Chemical('toluene')
tol.Tm, tol.Tb, tol.Tc
(179.2, 383.75, 591.75)
tol.rho, tol.Cp, tol.k, tol.mu
(862.238, 1706.07, 0.13034, 0.0005522)
```

Those four values are the melting point, boiling point and critical temperature, then density, heat capacity, thermal conductivity and viscosity. Getting the melting, boiling and critical points of toluene, plus its density, heat capacity, thermal conductivity and viscosity, from one constructor call is the demonstration that the property database and the correlation models are wired together for you.

The design constraint behind the simple interface is stated in the documentation: the library is designed around base SI units only, for development convenience, and all chemicals default to 298.15 K and 101325 Pa on creation unless specified. All constant properties are loaded on the creation of a `Chemical` instance.

The package is `thermo` on PyPI, MIT licensed, requires Python 3.9 or newer, and is classified as alpha in the package metadata, with classifiers for CPython and PyPy across Python 3.9 through 3.14 and for Windows, macOS and the POSIX platforms including Linux and BSD.

## The rigorous interface builds an equation of state from scratch

The rigorous interface is the part that matters for anything beyond a lookup, and its example walks through building a flash calculation for pure decane with the Peng-Robinson equation of state.

The sequence is deliberate. Load the constant properties and the correlation properties for the compound, then configure a liquid phase object and a gas phase object from the same equation of state, then create a flasher that can produce one gas and one liquid phase, and finally run the flash:

```python
from thermo import ChemicalConstantsPackage, PRMIX, CEOSLiquid, CEOSGas, FlashPureVLS
constants, correlations = ChemicalConstantsPackage.from_IDs(['decane'])
eos_kwargs = dict(Tcs=constants.Tcs, Pcs=constants.Pcs, omegas=constants.omegas)
liquid = CEOSLiquid(PRMIX, HeatCapacityGases=correlations.HeatCapacityGases, eos_kwargs=eos_kwargs)
gas = CEOSGas(PRMIX, HeatCapacityGases=correlations.HeatCapacityGases, eos_kwargs=eos_kwargs)
flasher = FlashPureVLS(constants, correlations, gas=gas, liquids=[liquid], solids=[])
res = flasher.flash(T=300, P=1e5)
```

The important detail is `eos_kwargs`. The critical temperatures, critical pressures and acentric factors come from the constants package, and they are what make the Peng-Robinson model describe that specific fluid. Change the equation of state and you change the model, but the constants still come from the same data package.

The result object then exposes a wide set of derived properties. Molar and mass enthalpy, entropy, heat capacities, molar volume, molar and mass density, isobaric expansion coefficient, isothermal compressibility, the Joule Thomson coefficient, and the speed of sound in both molar and mass units. The documentation gives the worked numbers for the 300 K and 1 bar flash, which is a useful check that your own run reproduces the same thing.

## Mixture flash needs interaction parameters, and they come separately

The second rigorous example is a two-phase flash for a mixture of methane, ethane and nitrogen, and it exposes the part of the workflow that surprises people.

The constants and properties are loaded for the three components, but the binary interaction parameters are fetched separately, from an interaction parameter database:

```python
from thermo import ChemicalConstantsPackage, CEOSGas, CEOSLiquid, PRMIX, FlashVL
from thermo.interaction_parameters import IPDB
constants, properties = ChemicalConstantsPackage.from_IDs(['methane', 'ethane', 'nitrogen'])
kijs = IPDB.get_ip_asymmetric_matrix('ChemSep PR', constants.CASs, 'kij')
```

The `IPDB` call returns a matrix of binary interaction parameters, and that matrix is then passed into the equation of state configuration alongside the critical properties. The parameters come from a named source, `ChemSep PR` in this case, which is worth noting: the choice of parameter set is a modelling decision with real consequences for mixture results, and it is not something the library picks for you.

With the parameters in hand, the mixture flash works the same way as the pure component case. A `FlashVL` is created with the liquid and gas objects, then flashed at a temperature and pressure with a composition. The documentation then shows the same flasher being driven by different specifications, which is the property that makes it useful in a process calculation loop:

```python
zs = [0.965, 0.018, 0.017]
PT = flasher.flash(T=110.0, P=1e5, zs=zs)
PT.VF, PT.gas.zs, PT.liquid0.zs
(0.10365, [0.881788, 2.6758e-05, 0.11818], [0.97462, 0.02007, 0.005298])
```

There is also an N-phase flash algorithm, `FlashVLN`, and the documentation is explicit that there are no solid models implemented in this interface at this time.

## A library that is one component of a larger design library

The package description calls thermo the Chemical properties component of Chemical Engineering Design Library, ChEDL, and the package metadata says the same thing with the words chemical properties component.

That framing tells you something about the intended scope. This is not a standalone all-in-one chemical engineering package, it is one layer of a set, and the other layers are what a full process design workflow needs. The repository is by Caleb Bell, who is also the author of the related components.

The repository layout supports the picture of a serious numerical library. There is a `thermo/` package directory, a `tests/` directory, a `benchmarks/` directory with an `asv.conf.json` configuration for the airspeed velocity benchmarking framework and an `.asv/` directory, a `docs/` directory for the Sphinx documentation, a `dev/` directory, a `conftest.py` at the root, and an `environment.yml` for conda environments. There is a `Justfile` rather than a Makefile, a `.pre-commit-config.yaml`, a `.pylintrc`, and a `requirements_security.txt` for dependency security scanning.

The build is not a plain setuptools one. The pyproject.toml declares a build backend of `backend` with a `backend-path` pointing at `_custom_build/`, and the build requirements include `python-minifier` alongside setuptools and wheel, so the packaging step is custom rather than delegated to the default setuptools behaviour.

The `justfile` in particular signals the shape of the working environment, and the presence of `benchmarks/` with the asv configuration tells you the numerical performance is treated as something to measure over time rather than assumed.

## What thermo does not cover

Three boundaries are concrete.

The first is the solid phase. The documentation states plainly that there are no solid models implemented in the flash interface at this time, and the flasher is constructed with an explicit `solids=[]` argument in the pure component example, which shows the parameter exists for the interface even though the models behind it are not there. For any calculation involving a solid phase you need another tool.

The second is that accuracy is inherited from the underlying data, and the library does not remove that uncertainty. The pure component constants come from a database, and the correlation models for heat capacity, density, viscosity, thermal conductivity and surface tension are literature correlations fitted over stated ranges. Outside those ranges a correlation can degrade quietly, and the library will not warn you. This is the normal bargain of a property package and it is worth stating plainly: a wrong constant or a correlation applied outside its range produces a plausible number rather than an error.

The third is that mixture results depend on choices you have to make. The binary interaction parameters come from a named source, the equation of state is yours to pick, and the flash specification can be given in several forms. Each of those is a modelling decision, and the library provides the machinery rather than the judgement.

The alternative to name is a commercial process simulation package with its own property databases and built-in models. The difference in approach is that such a package arrives with a validated, opinionated model set and a user interface, while thermo gives you the models, the data access and the flash algorithms in Python, where you can inspect and change every part of the calculation. For a research workflow, a scriptable pipeline or an embedded calculation inside a larger simulation, that difference usually decides it.

## Conclusion

Adopt thermo if you are doing chemical engineering calculations in Python and want pure component data, equations of state and flash calculations in one dependency rather than a spreadsheet plus a property package. Do not adopt it if you need solid phase models, because the documentation states there are no solid models implemented in the flash interface at this time. Verify first which property database backs the constants, since the values are only as good as the data source, and check the interaction parameters you use, because the binary interaction parameters drive mixture results and are supplied separately from the pure component data.

## FAQ

### What is the thermo Python library?

It is the thermodynamics and phase equilibrium component of the Chemical Engineering Design Library, providing retrieval of chemical constants, calculation of temperature and pressure dependent thermodynamic and transport properties, and phase equilibrium calculations for mixtures. It is MIT licensed and requires Python 3.9 or newer.

### How do I install thermo?

From PyPI with `pip install thermo`, or from conda-forge with `conda install -c conda-forge thermo`. For the git version, run `git clone git://github.com/CalebBell/thermo.git`.

### How do I get a property such as the boiling point of toluene?

Create a Chemical from thermo.chemical with the compound name, for example `tol = Chemical('toluene')`, and read the attributes such as tol.Tm, tol.Tb, tol.Tc, tol.rho, tol.Cp, tol.k and tol.mu. All chemicals default to 298.15 K and 101325 Pa unless specified, and the library works in base SI units.

### What do I need beyond the pure component data to flash a mixture?

Binary interaction parameters, which are fetched separately through the interaction parameter database, for example `IPDB.get_ip_asymmetric_matrix('ChemSep PR', constants.CASs, 'kij')`, and are then passed into the equation of state configuration along with the critical properties.

### Does thermo support solid phases in flash calculations?

Not at this time. The documentation states that there are no solid models implemented in this interface, and the pure component flasher is created with an empty solids list.

## Sources

- [CalebBell/thermo on GitHub](https://github.com/CalebBell/thermo)
- [Issues](https://github.com/CalebBell/thermo/issues)
- [License: MIT](https://github.com/CalebBell/thermo/blob/master/LICENSE)
- [README](https://github.com/CalebBell/thermo/blob/master/README.md)
- [Releases](https://github.com/CalebBell/thermo/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/calebbell-thermo
