A physics-based Python library for heat pump simulation
Refrigerant-agnostic · operating-condition-agnostic · first-principles from the cycle up
Documentation · Sister project: Energy-Exergy Analysis Engine
tmhp is a Python library of thermodynamic cycle models for air-source, ground-source, and water-source heat pumps. The models cover domestic hot water (DHW), space heating, and space cooling.
Every model solves the same closed refrigerant cycle from first principles at every time step — no manufacturer-specific curve fits, no per-unit recalibration. Swap the refrigerant, change the source side, or move the operating point, and the same code path produces a coherent answer.
In one line: a refrigerant-agnostic, condition-agnostic heat pump model — one library, many systems.
Most building-energy simulators (EnergyPlus, TRNSYS, and friends) model a heat pump as an empirical curve fit against the manufacturer's catalogue. That is cheap and accurate inside the calibration envelope, but it carries structural limits:
| Curve-fit models | This library |
|---|---|
| Tied to the operating range of the original test data | Predictive across the full refrigerant envelope |
| Refrigerant is baked into the coefficients | Any CoolProp-supported refrigerant, swappable at runtime |
| Refrigerant state is hidden | Full thermodynamic state at every cycle node |
| Requires re-fitting for every new unit | One model class, parameterized by geometry & components |
You pay for it with a few extra parameters and a slightly more expensive time step. What you get in return is a model you can trust outside its calibration range — across refrigerants, operating envelopes, and system topologies that no single catalogue covers.
Shared cycle architecture — bold blocks are reused across ASHPB, GSHPB, WSHPB, ASHP, and GSHP. Open the interactive version →
Reference ASHPB topology — outdoor unit, refrigerant loop, hot water tank, mixing valve.
Each time step solves a closed refrigerant cycle coupled to the surrounding system (tank, building, ground loop, …). The condenser duty is given; the evaporating temperature is the free variable, picked by minimizing compressor power. The cycle closes on a physical optimum, not on fitted coefficients.
| Sub-model | Method |
|---|---|
| Refrigerant state points | CoolProp (REFPROP-grade equation of state) |
| Compressor work | Isentropic + volumetric + mechanical efficiency |
| Condenser / evaporator | ε-NTU (effectiveness-NTU) heat exchanger model |
| Outdoor unit fan | ASHRAE 90.1-style variable-speed-drive (VSD) power curve + air-side ε-NTU |
| Ground heat exchanger | g-function (ground thermal response) via pygfunction |
| PV / solar thermal | pvlib-driven irradiance & power |
| Cycle closure | Internal minimization → optimal evaporating temperature |
| Plotting backend | dartwork-mpl — thin matplotlib utility layer |
The same refrigerant cycle is reused across every system model. What varies between models is composed along three independent axes:
- Environmental medium — air, ground, or water. Acts as the heat source in heating mode and the heat sink in cooling mode; the same loop, with the direction of heat flow reversed.
- Demand side — what the system has to deliver: a domestic-hot-water tank, a space-heating load, or a space-cooling load.
- Auxiliary subsystems — parallel energy contributors that augment (not replace) the cycle: solar thermal collectors (STC) preheat the tank, photovoltaics (PV) offset compressor and fan electricity, and an energy storage system (ESS) buffers surplus PV generation.
Each concrete model in the next section is a fixed combination of these three axes.
Requires Python ≥ 3.10 and the uv package manager.
git clone https://github.com/bet-lab/tmhp.git
cd tmhp
uv syncThat's it — uv sync reads pyproject.toml and resolves every dependency against the committed uv.lock.
Runtime dependencies pulled in automatically:
- CoolProp · NumPy · SciPy · pandas · Matplotlib
- pvlib (PV / solar thermal subsystems) · pygfunction (g-function borehole) · tqdm (progress bars)
- dartwork-mpl — a thin Matplotlib styling layer used by the Mollier-diagram plotters; pulled from the upstream Git repo via
[tool.uv.sources]since it has no PyPI release.
Optional dev / docs tooling lives behind PEP 735 dependency groups, so the runtime install stays lean:
uv sync --group dev # ruff, mypy, pytest, pytest-cov
uv sync --group docs # sphinx + shibuya theme + authoring / UX extensionsSee the installation guide for the full per-group breakdown and the CI-equivalent --locked workflow.
from tmhp import AirSourceHeatPumpBoiler
# Build a model — the refrigerant is a constructor argument (default: R134a)
ashpb = AirSourceHeatPumpBoiler(ref="R32")
# Steady state: tank at 55 °C, ambient at 5 °C, target condenser duty 8 kW
result = ashpb.analyze_steady(
T_tank_w=55.0,
T0=5.0,
Q_ref_cond=8_000.0,
)
print(f"COP (refrigerant) : {result['cop_ref [-]']:.2f}")
print(f"COP (system) : {result['cop_sys [-]']:.2f}")
print(f"Heating capacity : {result['Q_ref_cond [W]'] / 1e3:.2f} kW")
print(f"Compressor power : {result['E_cmp [W]'] / 1e3:.2f} kW")
print(f"Evap sat. temp. : {result['T_ref_evap_sat [°C]']:.1f} °C")
print(f"Cond sat. temp. : {result['T_ref_cond_sat_v [°C]']:.1f} °C")Swap the refrigerant by changing one argument — no recalibration, no manufacturer data:
ashpb_r290 = AirSourceHeatPumpBoiler(ref="R290") # propane
ashpb_r744 = AirSourceHeatPumpBoiler(ref="R744") # CO₂
ashpb_r410 = AirSourceHeatPumpBoiler(ref="R410A")import numpy as np
from tmhp import AirSourceHeatPumpBoiler
ashpb = AirSourceHeatPumpBoiler(ref="R32")
simulation_period_sec = 24 * 3600
dt_s = 60
n_steps = simulation_period_sec // dt_s
dhw_usage_schedule = np.zeros(n_steps) # m³/s per step
T0_schedule = np.full(n_steps, 5.0) # outdoor °C per step
df = ashpb.analyze_dynamic(
simulation_period_sec = simulation_period_sec,
dt_s = dt_s,
T_tank_w_init_C = 50.0,
dhw_usage_schedule = dhw_usage_schedule,
T0_schedule = T0_schedule,
)
# df is a pandas DataFrame with the same keys as analyze_steady, per time step.Air-source heat pump boilers (ASHPB)
| Class | Description |
|---|---|
AirSourceHeatPumpBoiler |
Core ASHPB — refrigerant cycle + storage tank |
ASHPB_STC_preheat |
+ Solar thermal collector preheat |
ASHPB_STC_tank |
+ STC with stratified tank |
ASHPB_PV_ESS |
+ PV + Energy Storage System |
Ground-source heat pump boilers (GSHPB)
| Class | Description |
|---|---|
GroundSourceHeatPumpBoiler |
Core GSHPB with g-function borehole model |
GSHPB_STC_preheat |
+ STC preheat |
GSHPB_STC_tank |
+ STC with stratified tank |
GSHPB_PV_ESS |
+ PV + Energy Storage System |
Water-source heat pump boiler (WSHPB)
| Class | Description |
|---|---|
WaterSourceHeatPumpBoiler |
Dynamic WSHPB model |
Space-conditioning heat pumps
| Class | Description |
|---|---|
AirSourceHeatPump |
ASHP — heating & cooling |
GroundSourceHeatPump |
GSHP — heating & cooling |
Supporting modules
| Module | Purpose |
|---|---|
refrigerant.py |
CoolProp state-point helpers |
thermodynamics.py |
Cycle analysis — COP, compression ratio, isentropic efficiency |
heat_transfer.py |
ε-NTU heat exchanger calculations |
hx_fan.py |
Air-side fan & heat-exchanger model |
g_function.py |
Borehole g-function (pygfunction) |
weather.py |
Outdoor air temperature & weather utilities |
dhw.py |
Domestic hot water demand profiles |
cop.py |
COP correlations |
enex_functions.py |
Energy / exergy helpers |
dynamic_context.py |
Per-step simulation state |
subsystems.py |
Subsystem composition (STC / PV / UV) |
simulation_summary.py |
Stdout summary tables |
visualization.py |
Plotting facade |
mollier_diagram.py |
T-h / P-h / T-s plots |
uv_treatment.py |
UV treatment subsystem |
calc_util.py |
Unit conversions |
constants.py |
Physical constants |
AirSourceHeatPumpBoiler has been benchmarked against the Samsung EHS Mono HT Quiet R32 14 kW unit (Technical Data Book PDF) across 15 operating points —
Per-point comparison (catalogue conditions and target values follow Table 1 of the KJACR 2026 paper; predicted values come from re-running the released code via scripts/validation/samsung_ehs_parity.py):
| 1 | 40 | −10 | 13.45 | 2.30 | 2.37 | 0.07 | 3.0 % |
| 2 | 40 | 2 | 12.42 | 3.04 | 3.83 | 0.79 | 25.8 % |
| 3 | 40 | 12 | 14.65 | 5.07 | 4.67 | 0.40 | 7.9 % |
| 4 | 40 | 20 | 15.69 | 6.48 | 5.65 | 0.83 | 12.8 % |
| 5 | 40 | 30 | 16.98 | 7.68 | 7.43 | 0.25 | 3.2 % |
| 6 | 50 | −10 | 13.89 | 2.00 | 1.84 | 0.16 | 7.8 % |
| 7 | 50 | 2 | 13.27 | 2.56 | 3.04 | 0.48 | 18.9 % |
| 8 | 50 | 12 | 14.76 | 3.86 | 3.71 | 0.15 | 3.9 % |
| 9 | 50 | 20 | 15.97 | 4.78 | 4.34 | 0.44 | 9.2 % |
| 10 | 50 | 30 | 17.48 | 5.95 | 5.37 | 0.58 | 9.8 % |
| 11 | 65 | −10 | 13.97 | 1.73 | 1.42 | 0.31 | 17.7 % |
| 12 | 65 | 2 | 13.71 | 2.04 | 2.37 | 0.33 | 16.1 % |
| 13 | 65 | 12 | 16.38 | 2.84 | 2.73 | 0.11 | 3.7 % |
| 14 | 65 | 20 | 17.48 | 3.34 | 3.17 | 0.17 | 5.1 % |
| 15 | 65 | 30 | 18.84 | 4.04 | 3.79 | 0.25 | 6.1 % |
| Mean | 0.35 | 10.1 % |
Notation
- TLWT — Leaving Water Temperature, the manufacturer's catalogue reference. The model's tank water temperature is set 2.5 K below TLWT for TLWT ≤ 60 °C and 5 K below for TLWT > 60 °C, per the paper's EWT/LWT offset.
- T0 — outdoor (dead-state) air temperature.
- Qref,cond — target condenser heat rate.
- COP — system Coefficient of Performance, Qref,cond / (Ecmp + Efan).
- AE — Absolute Error, |COPpred − COPtarget|.
- APE — Absolute Percentage Error, (AE / COPtarget) × 100 %.
- MAE / MAPE — mean AE / APE across the 15 points.
The parity plot and the table above are regenerated by scripts/validation/samsung_ehs_parity.py, so anyone can reproduce the comparison from source.
Scope. Only
AirSourceHeatPumpBoilerhas been quantitatively validated against catalogue data. The other system classes (GroundSourceHeatPumpBoiler,WaterSourceHeatPumpBoiler,AirSourceHeatPump,GroundSourceHeatPump, and the subsystem-augmented variants) share the same refrigerant-cycle core and pass smoke tests on representative operating points, but they have not yet been benchmarked against unit-specific data.
📄 Jo, H. & Choi, W. "Thermodynamic Modeling of Refrigerant Cycle in an Air-Source Heat Pump Boiler and Performance Validation", KJACR (2026, in press).
📘 Samsung Electronics, EHS Mono HT Quiet R32 Technical Data Book (2024) — PDF
The full documentation — getting-started guide, concept pages, tutorials, API reference, and validation report — lives at https://bet-lab.github.io/tmhp/.
If you're new to the library, start with the getting-started guide for a three-step path from uv sync to your first dynamic simulation.
Project layout
tmhp/
├── src/tmhp/ # Importable package
│ ├── __init__.py # Public re-exports
│ │
│ ├── air_source_heat_pump.py # ASHP (space conditioning)
│ ├── air_source_heat_pump_boiler.py # ASHPB core
│ ├── ashpb_stc_preheat.py
│ ├── ashpb_stc_tank.py
│ ├── ashpb_pv_ess.py
│ │
│ ├── ground_source_heat_pump.py # GSHP (space conditioning)
│ ├── ground_source_heat_pump_boiler.py # GSHPB core
│ ├── gshpb_stc_preheat.py
│ ├── gshpb_stc_tank.py
│ ├── gshpb_pv_ess.py
│ │
│ ├── water_source_heat_pump_boiler.py # WSHPB core
│ │
│ ├── refrigerant.py # CoolProp helpers
│ ├── thermodynamics.py # Cycle analysis
│ ├── heat_transfer.py # ε-NTU
│ ├── hx_fan.py # Air-side fan & heat-exchanger model
│ ├── g_function.py # Borehole g-function
│ ├── weather.py
│ ├── dhw.py
│ ├── cop.py
│ ├── enex_functions.py
│ ├── dynamic_context.py
│ ├── subsystems.py
│ ├── simulation_summary.py
│ ├── visualization.py
│ ├── mollier_diagram.py
│ ├── uv_treatment.py
│ ├── calc_util.py
│ └── constants.py
│
├── docs/ # Sphinx documentation
├── tests/ # Unit / smoke tests
├── pyproject.toml
├── uv.lock
└── README.md
If you use this library in academic work, please cite the validation paper:
@article{Jo2026Thermodynamic,
title = {Thermodynamic Modeling of Refrigerant Cycle in an Air-Source
Heat Pump Boiler and Performance Validation},
author = {Jo, Habin and Choi, Wonjun},
journal = {Korean Journal of Air-Conditioning and Refrigeration Engineering},
year = {2026},
note = {in press}
}- Sister project: Energy-Exergy Analysis Engine — an energy / exergy analysis library developed in parallel by the same team. It consumes simulation output from
tmhp(or any other source) and computes the second-law balance; the two projects ship as separate packages.
MIT License © 2025 betlab (Habin Jo, Wonjun Choi). See LICENSE for the full text.
