Refrigerants and CoolProp

“Refrigerant-agnostic” is one of the headline properties of tmhp — and this is the page that makes it concrete. The refrigerant is a constructor argument on every model (ref="<name>"); state queries route through CoolProp, which carries the equation-of-state heavy lifting. Below: which refrigerants work out of the box, where the cycle assumptions break, and how to read CoolProp-related failures.

The contract

Any refrigerant CoolProp supports as a pure fluid or a mixture that CoolProp can resolve via its built-in alias table is usable out of the box:

AirSourceHeatPumpBoiler(ref="R32")        # difluoromethane
AirSourceHeatPumpBoiler(ref="R290")       # propane
AirSourceHeatPumpBoiler(ref="R410A")      # mixture
AirSourceHeatPumpBoiler(ref="R134a")
AirSourceHeatPumpBoiler(ref="R744")       # CO₂ (see below)
AirSourceHeatPumpBoiler(ref="R600a")      # isobutane

CoolProp returns REFPROP-grade equation-of-state values, so the result is competitive with a calibrated catalogue model regardless of which refrigerant you pick — no per-refrigerant re-tuning required.

Subcritical operation (R32, R290, R410A, R134a, …)

The cycle assumes a subcritical condensation pressure: condenser inlet is superheated vapour, condenser outlet is subcooled liquid, and the saturation dome is the relevant region for state-point calculations.

For refrigerants whose critical temperature is comfortably above typical condenser water temperatures (R32: ~78 °C critical, R290: ~97 °C, R134a: ~101 °C), this assumption holds across the entire DHW operating envelope.

Supercritical / transcritical operation (R744)

R744 (CO₂) has a critical temperature near 31 °C. For DHW heating (condenser water at 50–65 °C) the high side runs above the critical point — a transcritical cycle. The current model is written against a subcritical-condenser assumption, so R744 will still solve but the results are best interpreted as a sanity-check, not a fully transcritical model.

If you need a faithful transcritical model, a future cycle path that treats the gas-cooler explicitly is the right place to add it. The cycle-closure interface (_optimize_operation) is designed so that swapping the high-side block in is a localised change.

Mapping CoolProp errors to failure_reason

CoolProp raises when state queries fall outside the EOS envelope — for example, asking for a saturation pressure above the critical point, or asking for a property at a state that crossed into two-phase by accident. These show up inside tmhp as:

  • failure_reason == "cycle_invalid" when the cycle couldn’t produce a coherent state at all (often the EOS itself rejected a query).

  • failure_reason == "hx_not_converged" when the cycle produced a state but the HX iteration drifted because a property lookup was unstable near a phase boundary.

In both cases the relevant lever is the operating point: either move T_tank_w / T0 / Q_ref_cond away from the EOS edge, or oversize the heat exchanger so the iteration doesn’t push so hard. See failure_reason semantics for the branching pattern.

Why CoolProp specifically?

  • Coverage — every common refrigerant plus most blends, with the same call shape.

  • Quality — REFPROP-grade EOS for the major fluids, validated against the same reference data that commercial property packages use.

  • License — MIT, redistributable.

  • Cost — pure-Python wrapper over a C++ core, fast enough that 100 k state queries per simulation second are not the bottleneck.

For state-point helpers and the small wrapper layer this library adds, see Refrigerant & thermodynamics.