Refrigerant & thermodynamics

CoolProp-backed state-point helpers, cycle-level analysis (compression ratio, isentropic efficiency, COP), compressor operating-envelope guards, and COP correlations.

Refrigerant state points

Refrigerant cycle calculations and optimization.

tmhp.refrigerant.calc_ref_state(T_evap_K, T_cond_K, refrigerant, eta_cmp_isen, mode='heating', dT_superheat=0.0, dT_subcool=0.0, is_active=True, rps=None)[source]

Compute the four thermodynamic state points of the refrigerant cycle.

The vapour-compression cycle has four canonical state points:

  • State 1 (cmp_in): compressor inlet — low-pressure superheated vapour at the evaporator outlet.

  • State 2 (cmp_out): compressor outlet — high-pressure superheated vapour at the condenser inlet.

  • State 3 (exp_in): expansion-valve inlet — high-pressure subcooled liquid at the condenser outlet.

  • State 4 (exp_out): expansion-valve outlet — low-pressure two-phase mixture at the evaporator inlet.

Keys in the returned dict are always assigned by the physical compressor / expander ports; the mode argument is preserved verbatim under the "mode" key but does not change the key naming.

Note

Whether a given heat exchanger acts as the evaporator or the condenser in heating versus cooling mode is decided by the caller (_calc_state), which chooses T_evap_K and T_cond_K accordingly before calling this function.

Parameters:
  • T_evap_K (float)

  • T_cond_K (float)

  • refrigerant (str)

  • eta_cmp_isen (float | Callable)

  • mode (str)

  • dT_superheat (float)

  • dT_subcool (float)

  • is_active (bool)

  • rps (float | None)

Return type:

dict[str, Any]

tmhp.refrigerant.create_lmtd_constraints()[source]

Create LMTD-based constraint functions for cycle optimization.

Optimization requires that the heat transfer calculated by LMTD matches the heat transferred by the refrigerant cycle.

Returns:

Tuple of constraint functions (constraint_tank, constraint_hx).

Return type:

tuple[Any, Any]

tmhp.refrigerant.find_ref_loop_optimal_operation(simulator_func, refrigerant, load_W, initial_guess, bounds, constraint_funcs=None)[source]

Find the optimal operation point for the refrigerant loop.

Minimizes compressor power while satisfying target load and LMTD constraints.

Parameters:
  • simulator_func (Any) – Function that takes [dT_ref_HX, dT_ref_tank] and returns a perf dict.

  • refrigerant (str) – Refrigerant name.

  • load_W (float) – Target heat load [W].

  • initial_guess (list[float]) – Initial guess for [dT_evap, dT_cond].

  • bounds (list[tuple[float, float]]) – Bounds for [dT_evap, dT_cond].

  • constraint_funcs (list[Any] | None) – List of constraint functions. Each takes perf and returns a value that should be 0.

Returns:

Optimal performance dictionary, or None if optimization fails.

Return type:

dict[str, Any] | None

Cycle analysis

Thermodynamic property and exergy calculations.

tmhp.thermodynamics.calc_energy_flow(G, T, T0)[source]

Calculate energy flow rate.

Parameters:
  • G (float or pd.Series) – Heat capacity flow rate (mass_flow * Cp) [W/K].

  • T (float or pd.Series) – Current temperature [K].

  • T0 (float or pd.Series) – Reference/dead state temperature [K].

Returns:

Energy flow rate [W].

Return type:

float or pd.Series

tmhp.thermodynamics.calc_exergy_flow(G, T, T0)[source]

Calculate exergy flow rate.

Parameters:
  • G (float or pd.Series) – Heat capacity flow rate (mass_flow * Cp) [W/K].

  • T (float or pd.Series) – Current temperature [K].

  • T0 (float or pd.Series) – Reference/dead state temperature [K].

Returns:

Exergy flow rate [W].

Return type:

float or pd.Series

tmhp.thermodynamics.calc_refrigerant_exergy(df, ref, T0_K, P0=101325)[source]

Calculate refrigerant state-point exergy using pre-computed properties.

Uses the entropy (s_ref_*) and enthalpy (h_ref_*) columns already present in df (produced by calc_ref_state) to compute specific exergy and exergy flow rate for each refrigerant state point.

Dead-state properties (h0, s0) are evaluated at (T0, P0) for the given refrigerant using CoolProp (vectorized via unique T0 values).

Parameters:
  • df (DataFrame) – DataFrame containing pre-computed enthalpy h_ref_* [J/kg], entropy s_ref_* [J/(kg·K)], and mass-flow m_dot_ref [kg/s] columns.

  • ref (str) – CoolProp refrigerant identifier (e.g. 'R410A').

  • T0_K (Series) – Dead-state (environment) temperature per row [K].

  • P0 (float) – Dead-state pressure [Pa] (default 101325).

Returns:

df with columns added per state point: x_ref_{name} [J/kg], X_ref_{name} [W].

Return type:

DataFrame

Notes

  • Exergy equation: x = (h − h0) − T0·(s − s0) [J/kg]

  • Exergy flow: X = ṁ · x [W]

  • Rows with NaN enthalpy/entropy propagate NaN naturally.

tmhp.thermodynamics.convert_electricity_to_exergy(df)[source]

Copy all electricity columns (E_*) to exergy columns (X_*).

Electrical energy is 100 %% pure exergy, so X = E for all electricity-consumption columns.

The function searches for columns matching the pattern E_xxx [W] and creates corresponding X_xxx [W] columns.

Parameters:

df (DataFrame) – DataFrame with E_xxx [W] columns.

Returns:

df with X_xxx [W] columns added.

Return type:

DataFrame

tmhp.thermodynamics.generate_entropy_exergy_term(fluid, T, P, Q, T0, P0, phase='gas')[source]

Calculate entropy, enthalpy, and exergy.

Parameters:
  • fluid (str) – Fluid name.

  • T (float) – Temperature [K].

  • P (float) – Pressure [Pa].

  • Q (float) – Quality (0 to 1).

  • T0 (float) – Dead state temperature [K].

  • P0 (float) – Dead state pressure [Pa].

  • phase (Literal['gas', 'liquid', 'twophase']) – Fluid phase. Default is ‘gas’.

Returns:

Entropy [J/kg-K], Enthalpy [J/kg], Exergy [J/kg].

Return type:

tuple[float, float, float]

COP correlations

Heat pump Coefficient of Performance (COP) models.

Provides empirical COP correlations for: - Air source heat pumps (cooling and heating modes) - Ground source heat pumps (modified Carnot model)

tmhp.cop.calc_ASHP_cooling_COP(T_a_int_out, T_a_ext_in, Q_r_int, Q_r_max, COP_ref)[source]

Calculate the Coefficient of Performance (COP) for an Air Source Heat Pump (ASHP) in cooling mode.

Reference: https://publications.ibpsa.org/proceedings/bs/2023/papers/bs2023_1118.pdf

Parameters:
  • T_a_int_out (float) – Indoor air temperature [K]

  • T_a_ext_in (float) – Outdoor air temperature [K]

  • Q_r_int (float) – Indoor heat load [W]

  • Q_r_max (float) – Maximum cooling capacity [W]

  • COP_ref (float) – Reference COP at standard conditions

Returns:

COP value

Return type:

float

Note

COP is calculated based on: - PLR: Part Load Ratio - EIR: Energy input to cooling output ratio

tmhp.cop.calc_ASHP_heating_COP(T0, Q_r_int, Q_r_max)[source]

Calculate the Coefficient of Performance (COP) for an Air Source Heat Pump (ASHP) in heating mode.

Reference: https://www.mdpi.com/2071-1050/15/3/1880

Parameters:
  • T0 (float) – Environmental temperature [K]

  • Q_r_int (float) – Indoor heat load [W]

  • Q_r_max (float) – Maximum heating capacity [W]

Returns:

COP value

Return type:

float

Note

COP is calculated based on PLR (Part Load Ratio).

tmhp.cop.calc_GSHP_COP(T_a_iu_in_K, T_f_out_K, dV_a_ratio, mode)[source]

Calculate COP for a Ground Source Heat Pump.

Uses the EnergyPlus Coil:*:WaterToAir:EquationFit model (Equation-Fit Method).

References

EnergyPlus Engineering Reference, Ch. 16.5.10.2 (Eq. 16.412–16.418, Tang 2005). Dataset: TCH072_GLHP (ClimateMaster 6-ton, Ground Loop HP), source: EnergyPlus/datasets/WaterToAirHeatPumps.idf.

Rated conditions (from IDF comment, 15% methanol antifreeze):

  • Cooling: 70.29 kBtu/h @ 77 °F (25 °C) entering water, EER = 14.35

  • Heating: 56.14 kBtu/h @ 32 °F (0 °C) entering water, COP = 3.42

Parameters:
  • T_a_iu_in_K (float) – Indoor air inlet dry-bulb temperature [K] (= T_a_room). For cooling, internally converted to wet-bulb (T_wb) via CoolProp assuming RH = 50%.

  • T_f_out_K (float) – Source-side inlet water temperature [K] — water returning from the borehole to the heat pump (T_w,in in EnergyPlus notation).

  • dV_a_ratio (float) – Load-side air volumetric flow ratio: V_a / V_a_ref [-] V_a_ref = Q_rated / (rho_a * c_a * 10K) [m³/s]

  • mode (str) – “cooling” or “heating”

  • Units

  • -----

  • Temperatures (K)

  • rates (Flow)

  • Heat/Power (W)

Returns:

COP (dimensionless). Polynomial model is unconditionally stable— no ValueError on temperature inversion.

Return type:

float

Compressor envelope

Compressor operating-envelope guard (pressure-ratio based).

The physically primary limit on a vapour-compression cycle is the compressor pressure ratio PR = P_cond / P_evap, not a fixed temperature lift: the lift required to reach a given PR maps non-linearly through the refrigerant saturation curve (Clausius–Clapeyron) and therefore differs by refrigerant and operating level. A fixed minimum lift (e.g. 20 K) over- or under-states the true physical minimum depending on conditions; a PR floor is transferable.

This module provides the shared envelope decision used by every heat-pump model so the floor/ceiling logic and reason codes stay consistent. Each model owns its own PR_cycle_min / PR_cycle_max attributes (user-configurable) and calls check_pr_envelope(); the caller then clamps the floor (project the cycle onto PR_cycle_min for a continuous low-lift transition) and rejects the ceiling (outside the single-stage envelope).

Physical rationale: scroll axial-loading / built-in volume ratio, oil-feed pressure differential, and isentropic-efficiency collapse all place the floor near PR ~ 1.5 (1.3–2.0 band). For the ceiling, the AC-scroll hardware self-unload limit sits near PR ~ 11:1 and the single-stage modelling boundary near PR ~ 7--8; the heat-pump models nonetheless default PR_cycle_max to 20 as a deliberately generous sanity bound so that legitimate high-lift domestic-hot-water operation (condensing at 55–75 degC from a 0–12 degC source) is admitted while still rejecting non-physical pressure ratios (Cuevas & Lebrun 2009; Bertsch & Groll 2008; Gayeski et al. 2011).

tmhp.compressor_envelope.check_pr_envelope(pr, pr_min, pr_max)[source]

Classify a compressor pressure ratio against the operating envelope.

Parameters:
  • pr (float) – Compressor pressure ratio P_cond / P_evap (dimensionless, >= 1).

  • pr_min (float) – Lower envelope bound (floor). Below this the scrolls unload / oil-feed differential and isentropic efficiency collapse.

  • pr_max (float) – Upper envelope bound (ceiling). Above this a single-stage cycle is no longer representative.

Returns:

None when pr_min <= pr <= pr_max (feasible); otherwise PR_BELOW_MIN or PR_ABOVE_MAX.

Return type:

str | None

tmhp.compressor_envelope.PR_BELOW_MIN = 'pr_below_min'

pressure ratio is below PR_cycle_min (floor -> clamp).

Type:

Reason code

tmhp.compressor_envelope.PR_ABOVE_MAX = 'pr_above_max'

pressure ratio is above PR_cycle_max (ceiling -> reject).

Type:

Reason code

Compressor reference state

Compressor reference state solved from nominal capacity.

BITZER compressor efficiencies use absolute shaft speed in rev/s. Models normalise the ground heat exchanger refrigerant-side UA by a rated mass flow (m_dot_ref / m_dot_ref_rated). Both denominators belong to one physical point – the reference state – and this module computes that point from the same inputs every model already requires:

hp_capacity -> V_cmp_ref -> rating condition -> rps_rated -> m_dot_ref_rated
V_cmp_ref

Swept displacement. When the caller gives none, the models use tmhp.compressor_speed.default_displacement(), a capacity-scaled reduced-order default, not an identification of a real compressor.

rating condition

A standard (or manufacturer/user) rating point for the model family: mode, source- and load-side inlet temperatures, the model’s reference secondary flows and its rated UA values. It is independent of whatever operating condition is simulated afterwards.

rps_rated

The speed at which the model delivers hp_capacity at the rating condition, with every efficiency evaluated at that actual shaft speed.

m_dot_ref_rated

The refrigerant mass flow of that same state.

Capacity-only initialization constructs a representative compressor; it does not identify the exact manufacturer compressor.

Solution method

At the rating condition the duty on the rated side (load side) is known, so the rated-side saturation temperature follows from the rated-UA heat exchanger directly. For a trial duty on the other side, the other saturation temperature follows the same way; the cycle at those two temperatures then fixes the speed through the bounded root

\[Q_{model}(rps) - Q_{rated} = 0\]

with efficiencies evaluated at the actual bounded shaft speed (or the caller’s own callables). Heating is parameterized by speed, closing source mass flow first and then the rated condenser duty; this does not assume a monotone condenser-duty curve. Variable ground-HX UA is not used here – it needs m_dot_ref_rated, which is what is being solved for – so both heat exchangers use their rated UA at their reference flow.

A rated capacity that needs a speed outside [rps_min, rps_max] – or a rating point the subcritical cycle or the rated heat exchangers cannot reach – is reported as REFERENCE_CAPACITY_INCONSISTENT instead of being clamped onto a bound: a clamped speed would deliver a different capacity and silently redefine the machine. The pressure-ratio envelope is an operating limit and is not imposed on the rating point; its pressure ratio is reported.

tmhp.reference_state.REFERENCE_CAPACITY_INCONSISTENT = 'reference_capacity_inconsistent'

the rated capacity is not reachable inside the speed envelope (or by the subcritical cycle / rated HX) at the rating condition.

Type:

Failure reason

class tmhp.reference_state.ReferenceStateMixin[source]

Bases: object

Shared reference-state initialization for compressor-based models.

A model sets _RATING_FAMILY / _RATING_DEFAULT_MODE, implements _rating_side(), and calls _initialize_reference_state() once its refrigerant, displacement, rated UA values, reference flows and speed limits are known – and before anything that needs m_dot_ref_rated (variable ground-HX UA) is configured.

ref: str
V_cmp_ref: float
hp_capacity: float
dT_superheat: float
dT_subcool: float
dT_hx_min: float
rps_min: float
rps_max: float
tmhp.reference_state.RATING_STANDARDS: dict[str, dict[str, RatingStandard]] = {'ASHP': {'cooling': RatingStandard(source_T_C=35.0, load_T_C=27.0, standard='ISO 5151:2017 T1 cooling (outdoor 35 °C DB, indoor 27 °C DB)'), 'heating': RatingStandard(source_T_C=7.0, load_T_C=20.0, standard='ISO 5151:2017 H1 heating (outdoor 7 °C DB, indoor 20 °C DB)')}, 'ASHPB': {'heating': RatingStandard(source_T_C=7.0, load_T_C=55.0, standard='EN 14511-2:2022 A7/W55 (outdoor air 7 °C DB, water outlet 55 °C)')}, 'GSHP': {'cooling': RatingStandard(source_T_C=25.0, load_T_C=27.0, standard='ISO 13256-1:2021 ground-loop cooling (liquid entering 25 °C, indoor 27 °C DB)'), 'heating': RatingStandard(source_T_C=0.0, load_T_C=20.0, standard='ISO 13256-1:2021 ground-loop heating (liquid entering 0 °C, indoor 20 °C DB)')}, 'GSHPB': {'heating': RatingStandard(source_T_C=0.0, load_T_C=55.0, standard='EN 14511-2:2022 B0/W55 (brine inlet 0 °C, water outlet 55 °C)')}, 'WSHPB': {'heating': RatingStandard(source_T_C=10.0, load_T_C=55.0, standard='EN 14511-2:2022 W10/W55 (water inlet 10 °C, water outlet 55 °C)')}}

Standard rating points per model family and mode.

source_T_C / load_T_C are the secondary-fluid inlet temperatures. TMHP’s air coils are dry (sensible only), so wet-bulb conditions of the standards are not used. The boiler models’ condenser is immersed in a lumped tank; the tank temperature is set to the standard’s water outlet temperature, the conservative end of the condenser water range.

class tmhp.reference_state.HXSide(fluid, T_in_C, UA, volume_flow=None)[source]

Bases: object

Secondary side of one refrigerant heat exchanger at the rating point.

fluid is "air" or "water" for a single-phase stream crossing a constant-saturation-temperature surface (Q = eps C dT, eps = 1 - exp(-UA / C)), or "tank" for the boiler condenser immersed in a lumped tank (Q = UA dT), as in the models’ own cycles.

Parameters:
  • fluid (Literal['air', 'water', 'tank'])

  • T_in_C (float)

  • UA (float)

  • volume_flow (float | None)

fluid: Literal['air', 'water', 'tank']
T_in_C: float
UA: float
volume_flow: float | None = None
property conductance: float

Duty per kelvin of approach, eps C (or UA for a tank) [W/K].

__init__(fluid, T_in_C, UA, volume_flow=None)
Parameters:
  • fluid (Literal['air', 'water', 'tank'])

  • T_in_C (float)

  • UA (float)

  • volume_flow (float | None)

class tmhp.reference_state.RatingCondition(mode, source, load, standard)[source]

Bases: object

Rating point: mode, both secondary sides and the standard it follows.

Parameters:
  • mode (Literal['heating', 'cooling'])

  • source (HXSide)

  • load (HXSide)

  • standard (str)

mode: Literal['heating', 'cooling']
source: HXSide
load: HXSide
standard: str
__init__(mode, source, load, standard)
Parameters:
  • mode (Literal['heating', 'cooling'])

  • source (HXSide)

  • load (HXSide)

  • standard (str)

class tmhp.reference_state.ReferenceState(rps_rated, m_dot_ref_rated, condition, hp_capacity, V_cmp_ref, T_evap_sat_C, T_cond_sat_C, pressure_ratio, Q_cond, Q_evap, E_cmp_ref, E_cmp, eta_cmp_isen, eta_cmp_vol, eta_cmp, rps_rated_solved=True)[source]

Bases: object

Self-consistent compressor reference state.

Parameters:
  • rps_rated (float)

  • m_dot_ref_rated (float)

  • condition (RatingCondition)

  • hp_capacity (float)

  • V_cmp_ref (float)

  • T_evap_sat_C (float)

  • T_cond_sat_C (float)

  • pressure_ratio (float)

  • Q_cond (float)

  • Q_evap (float)

  • E_cmp_ref (float)

  • E_cmp (float)

  • eta_cmp_isen (float)

  • eta_cmp_vol (float)

  • eta_cmp (float)

  • rps_rated_solved (bool)

rps_rated: float
m_dot_ref_rated: float
condition: RatingCondition
hp_capacity: float
V_cmp_ref: float
T_evap_sat_C: float
T_cond_sat_C: float
pressure_ratio: float
Q_cond: float
Q_evap: float
E_cmp_ref: float
E_cmp: float
eta_cmp_isen: float
eta_cmp_vol: float
eta_cmp: float
rps_rated_solved: bool = True
property cop: float

Load-side duty over compressor electrical input at the reference state.

__init__(rps_rated, m_dot_ref_rated, condition, hp_capacity, V_cmp_ref, T_evap_sat_C, T_cond_sat_C, pressure_ratio, Q_cond, Q_evap, E_cmp_ref, E_cmp, eta_cmp_isen, eta_cmp_vol, eta_cmp, rps_rated_solved=True)
Parameters:
  • rps_rated (float)

  • m_dot_ref_rated (float)

  • condition (RatingCondition)

  • hp_capacity (float)

  • V_cmp_ref (float)

  • T_evap_sat_C (float)

  • T_cond_sat_C (float)

  • pressure_ratio (float)

  • Q_cond (float)

  • Q_evap (float)

  • E_cmp_ref (float)

  • E_cmp (float)

  • eta_cmp_isen (float)

  • eta_cmp_vol (float)

  • eta_cmp (float)

  • rps_rated_solved (bool)

exception tmhp.reference_state.ReferenceStateError(message)[source]

Bases: ValueError

Rated capacity cannot be reached at the rating condition.

Parameters:

message (str)

reason = 'reference_capacity_inconsistent'
__init__(message)[source]
Parameters:

message (str)

tmhp.reference_state.rating_condition(family, *, source, load, default_mode, overrides=None)[source]

Resolve a family’s rating condition, applying caller overrides.

source / load build the model’s own heat-exchanger side for a mode and an inlet temperature (rated UA, reference flow). overrides is a complete RatingCondition, or a mapping with any of mode, source_T_C, load_T_C and standard. Changing mode switches the defaults to that mode’s standard point.

Parameters:
  • family (str)

  • source (Callable[[Literal['heating', 'cooling'], float], HXSide])

  • load (Callable[[Literal['heating', 'cooling'], float], HXSide])

  • default_mode (Literal['heating', 'cooling'])

  • overrides (Mapping[str, Any] | RatingCondition | None)

Return type:

RatingCondition

tmhp.reference_state.solve_reference_state(*, ref, V_cmp_ref, hp_capacity, condition, eta_cmp_isen, eta_cmp_vol, eta_cmp, dT_superheat, dT_subcool, dT_hx_min, rps_min, rps_max, rps_rated=None, cache_key=None)[source]

Solve the reference state at condition.

With rps_rated=None the speed delivering hp_capacity is solved and becomes the rated speed. With an explicit rps_rated the same state is solved with the model’s efficiencies as given, so m_dot_ref_rated can still be derived; rps_rated itself is then left as supplied.

cache_key (hashable, describing the efficiency models) enables a process-wide cache, so identical constructions solve once.

Parameters:
  • ref (str)

  • V_cmp_ref (float)

  • hp_capacity (float)

  • condition (RatingCondition)

  • eta_cmp_isen (float | Callable)

  • eta_cmp_vol (float | Callable)

  • eta_cmp (float | Callable)

  • dT_superheat (float)

  • dT_subcool (float)

  • dT_hx_min (float)

  • rps_min (float)

  • rps_max (float)

  • rps_rated (float | None)

  • cache_key (tuple | None)

Return type:

ReferenceState