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
modeargument 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 choosesT_evap_KandT_cond_Kaccordingly 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 indf(produced bycalc_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 enthalpyh_ref_* [J/kg], entropys_ref_* [J/(kg·K)], and mass-flowm_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] (default101325).
- Returns:
dfwith 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 = Efor all electricity-consumption columns.The function searches for columns matching the pattern
E_xxx [W]and creates correspondingX_xxx [W]columns.- Parameters:
df (
DataFrame) – DataFrame withE_xxx [W]columns.- Returns:
dfwithX_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:EquationFitmodel (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,inin 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 ratioP_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:
Nonewhenpr_min <= pr <= pr_max(feasible); otherwisePR_BELOW_MINorPR_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_refSwept 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 conditionA 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_ratedThe speed at which the model delivers
hp_capacityat the rating condition, with every efficiency evaluated at that actual shaft speed.m_dot_ref_ratedThe 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
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:
objectShared 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 needsm_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_Care 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:
objectSecondary side of one refrigerant heat exchanger at the rating point.
fluidis"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(orUAfor 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:
objectRating point: mode, both secondary sides and the standard it follows.
- mode: Literal['heating', 'cooling']¶
- 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:
objectSelf-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:
ValueErrorRated capacity cannot be reached at the rating condition.
- Parameters:
message (
str)
- reason = 'reference_capacity_inconsistent'¶
- tmhp.reference_state.rating_condition(family, *, source, load, default_mode, overrides=None)[source]¶
Resolve a family’s rating condition, applying caller overrides.
source/loadbuild the model’s own heat-exchanger side for a mode and an inlet temperature (rated UA, reference flow).overridesis a completeRatingCondition, or a mapping with any ofmode,source_T_C,load_T_Candstandard. Changingmodeswitches 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:
- 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=Nonethe speed deliveringhp_capacityis solved and becomes the rated speed. With an explicitrps_ratedthe same state is solved with the model’s efficiencies as given, som_dot_ref_ratedcan still be derived;rps_rateditself 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: