Air-source heat pump (ASHP — space conditioning)

ASHP conditions a building zone (heating + cooling) rather than charging a DHW tank. The refrigerant cycle and outdoor-coil source side are shared with ASHPB; what differs is the load side — a zone energy balance instead of a tank.

Overview

The class is tmhp.AirSourceHeatPump. Use it when the heat pump’s job is space conditioning rather than DHW production.

Base usage

from tmhp import AirSourceHeatPump

ashp = AirSourceHeatPump(ref="R32")

# See API reference below for the full constructor and
# analyze_steady / analyze_dynamic signatures.

Source-side mechanics

Identical to ASHPB — outdoor coil with variable-speed fan, ε-NTU air-side heat exchanger.

Sink-side mechanics

A zone temperature / load proxy stands in for the building. The heat pump’s condenser duty serves whatever space-heating or cooling load the caller supplies; there is no tank energy balance.

API reference

Air source heat pump — physics-based cycle model with indoor unit.

Resolves a vapour-compression refrigerant cycle coupled to an outdoor-air heat exchanger and an indoor-air heat exchanger. Supports both cooling (Q_r_iu > 0) and heating (Q_r_iu < 0) modes. The indoor load Q_r_iu is imposed externally each timestep.

At each time step the model finds the minimum-power operating point (compressor + indoor fan + outdoor fan) via bounded 2-D optimisation over the evaporator and condenser approach temperature differences.

Architecture mirrors AirSourceHeatPumpBoiler — uses the same shared utility functions (calc_ref_state, calc_HX_perf_for_target_heat, calc_fan_power_from_dV_fan) and the same postprocess_exergy() pattern, but replaces the tank energy balance with direct air-side heat exchange at the indoor unit.

class tmhp.air_source_heat_pump.AirSourceHeatPump(ref='R32', V_disp_cmp=0.0001, eta_cmp_isen=None, eta_cmp_vol=None, eta_cmp_mech=0.855, dT_superheat=3.0, dT_subcool=3.0, UA_cond_design=None, UA_evap_design=None, dV_ou_fan_a_design=None, dP_ou_fan_design=60.0, A_cross_ou=None, eta_ou_fan_design=0.6, dV_iu_fan_a_design=None, dP_iu_fan_design=60.0, A_cross_iu=None, eta_iu_fan_design=0.6, hp_capacity=4000.0, T_a_room=27.0, vsd_coeffs_ou=None, vsd_coeffs_iu=None)[source]

Bases: object

Air source heat pump with indoor-unit air heat exchange.

The refrigerant cycle is resolved via CoolProp with user-specified superheat / subcool margins. A bounded 2-D optimiser minimises total electrical input (E_cmp + E_iu_fan + E_ou_fan) over the evaporator and condenser approach temperatures.

Parameters:
  • ref (str)

  • V_disp_cmp (float)

  • eta_cmp_isen (Union[float, Callable, None])

  • eta_cmp_vol (Union[float, Callable, None])

  • eta_cmp_mech (float | Callable)

  • dT_superheat (float)

  • dT_subcool (float)

  • UA_cond_design (Optional[float])

  • UA_evap_design (Optional[float])

  • dV_ou_fan_a_design (Optional[float])

  • dP_ou_fan_design (float)

  • A_cross_ou (Optional[float])

  • eta_ou_fan_design (float)

  • dV_iu_fan_a_design (Optional[float])

  • dP_iu_fan_design (float)

  • A_cross_iu (Optional[float])

  • eta_iu_fan_design (float)

  • hp_capacity (float)

  • T_a_room (float)

  • vsd_coeffs_ou (Optional[dict])

  • vsd_coeffs_iu (Optional[dict])

__init__(ref='R32', V_disp_cmp=0.0001, eta_cmp_isen=None, eta_cmp_vol=None, eta_cmp_mech=0.855, dT_superheat=3.0, dT_subcool=3.0, UA_cond_design=None, UA_evap_design=None, dV_ou_fan_a_design=None, dP_ou_fan_design=60.0, A_cross_ou=None, eta_ou_fan_design=0.6, dV_iu_fan_a_design=None, dP_iu_fan_design=60.0, A_cross_iu=None, eta_iu_fan_design=0.6, hp_capacity=4000.0, T_a_room=27.0, vsd_coeffs_ou=None, vsd_coeffs_iu=None)[source]
Parameters:
  • ref (str)

  • V_disp_cmp (float)

  • eta_cmp_isen (Union[float, Callable, None])

  • eta_cmp_vol (Union[float, Callable, None])

  • eta_cmp_mech (float | Callable)

  • dT_superheat (float)

  • dT_subcool (float)

  • UA_cond_design (Optional[float])

  • UA_evap_design (Optional[float])

  • dV_ou_fan_a_design (Optional[float])

  • dP_ou_fan_design (float)

  • A_cross_ou (Optional[float])

  • eta_ou_fan_design (float)

  • dV_iu_fan_a_design (Optional[float])

  • dP_iu_fan_design (float)

  • A_cross_iu (Optional[float])

  • eta_iu_fan_design (float)

  • hp_capacity (float)

  • T_a_room (float)

  • vsd_coeffs_ou (Optional[dict])

  • vsd_coeffs_iu (Optional[dict])

analyze_steady(Q_r_iu, T0, T_a_room=None, *, return_dict=True, postprocess=True, verbose=True)[source]

Run a steady-state performance snapshot.

Parameters:
  • Q_r_iu (float) – Indoor thermal load [W]. >0 cooling, <0 heating, 0 off.

  • T0 (float) – Dead-state / outdoor-air temperature [°C].

  • T_a_room (float | None) – Room air temperature [°C]. Uses constructor default if None.

  • return_dict (bool) – If True return dict; else single-row DataFrame.

  • postprocess (bool) – If True, apply postprocess_exergy to the output.

  • verbose (bool) – If True, print warnings upon convergence failure.

Returns:

Cycle state plus diagnostic flags.

Two keys are useful for branching:

  • "converged" (bool) — True only when the inner HX optimisation and the SciPy optimiser both succeeded.

  • "failure_reason" (str) — one of "none", "cycle_invalid" (the refrigerant cycle itself was infeasible), "hx_not_converged" (cycle OK but the HX residual exceeded tolerance), or "optimizer_failed" (SciPy reported success=False).

ASHP triggers an off-mode fallback for any of the non-"none" reasons — E_cmp [W] will be 0 and the COP keys will be NaN in that case. Treat failure_reason != "none" as “do not trust the numbers”.

Return type:

dict | pd.DataFrame

analyze_dynamic(simulation_period_sec, dt_s, Q_r_iu_schedule, T0_schedule, T_a_room_schedule=None, result_save_csv_path=None)[source]

Run a time-stepping dynamic simulation.

Parameters:
  • simulation_period_sec (int) – Total simulation duration [s].

  • dt_s (int) – Time step size [s].

  • Q_r_iu_schedule (array-like) – Indoor thermal load per step [W].

  • T0_schedule (array-like) – Outdoor temperature per step [°C].

  • T_a_room_schedule (array-like | None) – Room air temperature per step [°C]. If None, uses constructor default.

  • result_save_csv_path (str | None) – Optional CSV output path.

Returns:

Per-timestep result DataFrame.

Return type:

pd.DataFrame

postprocess_exergy(df)[source]

Compute ASHP-specific exergy variables.

Mirrors AirSourceHeatPumpBoiler.postprocess_exergy() with adaptations for indoor-unit air exchange.

Pipeline:

  1. Refrigerant state-point exergy (CoolProp)

  2. Electricity = exergy (compressor, IU fan, OU fan)

  3. Air exergy (outdoor unit + indoor unit)

  4. HX Carnot exergy (condenser, evaporator)

  5. Component-level exergy destruction

  6. Exergetic efficiency metrics

Parameters:

df (DataFrame)

Return type:

DataFrame