Air-source heat pump boiler (ASHPB)

The ASHPB family pairs the shared refrigerant cycle with an outdoor-coil source side and a DHW tank sink side. This is the default first stop and the model the Getting Started flow uses.

Overview

ASHPB solves the closed refrigerant cycle every step against an outdoor coil (ε-NTU air-side) and a tank-coupled condenser. The class is tmhp.AirSourceHeatPumpBoiler. Three composed variants extend it with subsystems:

  • tmhp.ASHPB_STC_preheat — STC heats the mains feed before it reaches the tank.

  • tmhp.ASHPB_STC_tank — STC charges a separate top node of a stratified tank.

  • tmhp.ASHPB_PV_ESS — PV generation + battery storage routes electricity to the compressor and auxiliaries before drawing grid.

Base usage

from tmhp import AirSourceHeatPumpBoiler

ashpb = AirSourceHeatPumpBoiler(ref="R32")

# Steady-state snapshot
result = ashpb.analyze_steady(
    T_tank_w=55.0,    # tank water [°C]
    T0=7.0,           # outdoor air [°C]
    Q_ref_cond=8_000, # target condenser duty [W]
)

# Time-stepping dynamic run — see Getting Started for full schedule
# construction.
# df = ashpb.analyze_dynamic(...)

Source-side mechanics

ASHPB models its outdoor coil as a variable-speed fan coupled to an ε-NTU heat exchanger. Fan electrical power follows an ASHRAE 90.1-style cubic-with-speed curve; coil ε is recomputed each step from the resolved refrigerant mass flow.

Sink-side mechanics

The sink is a single-node DHW tank (with optional stratification on the _STC_tank variant). The tank energy balance is solved implicitly per step with fsolve, jointly with the refrigerant cycle.

Composed variants

The standalone ASHPB: outdoor coil ε-NTU evaporator, R32 cycle, DHW tank charge.

from tmhp import AirSourceHeatPumpBoiler

ashpb = AirSourceHeatPumpBoiler(ref="R32")
result = ashpb.analyze_steady(
    T_tank_w=55.0,
    T0=7.0,
    Q_ref_cond=8_000,
)

Adds a flat-plate STC that preheats mains water entering the tank. Reduces the tank-charge duty the heat pump has to deliver.

from tmhp import ASHPB_STC_preheat
from tmhp.subsystems import SolarThermalCollector

stc = SolarThermalCollector(A_stc=4.0, stc_tilt=35.0, stc_azimuth=180.0)
model = ASHPB_STC_preheat(stc=stc, ref="R32")

STC charges a separate top node of a stratified tank; the heat pump charges the bottom. Top-of-tank water is drawn first.

from tmhp import ASHPB_STC_tank
from tmhp.subsystems import SolarThermalCollector

stc = SolarThermalCollector(A_stc=4.0, stc_tilt=35.0, stc_azimuth=180.0)
model = ASHPB_STC_tank(stc=stc, ref="R32")

Photovoltaic generation + ESS preferentially feeds the compressor and auxiliaries.

from tmhp import ASHPB_PV_ESS
from tmhp.subsystems import EnergyStorageSystem, PhotovoltaicSystem

model = ASHPB_PV_ESS(
    pv=PhotovoltaicSystem(),
    ess=EnergyStorageSystem(),
    ref="R32",
)

STC preheat

A SolarThermalCollector heats the cold mains water before it reaches the DHW tank, so the heat pump sees pre-heated water during the preheat window.

import numpy as np

from tmhp import ASHPB_STC_preheat
from tmhp.subsystems import SolarThermalCollector

stc = SolarThermalCollector(
    A_stc=4.0,             # 4 m² collector area
    stc_tilt=35.0,
    stc_azimuth=180.0,
)

model = ASHPB_STC_preheat(stc=stc, ref="R32")

Drive it the same way as base ASHPB, adding irradiance schedules (I_DN_schedule, I_dH_schedule) — both in W/m² per step:

dt_s          = 60
n_steps       = 24 * 3600 // dt_s
hour_of_day   = np.arange(n_steps) / 60.0

# Crude clear-sky irradiance: bell from 06:00 to 18:00.
day_window    = (hour_of_day >= 6.0) & (hour_of_day <= 18.0)
sun_shape     = np.sin(np.pi * (hour_of_day - 6.0) / 12.0)
I_DN          = np.where(day_window, 800.0 * sun_shape, 0.0)
I_dH          = np.where(day_window, 100.0 * sun_shape, 0.0)

T0  = np.full(n_steps, 5.0)
dhw = np.zeros(n_steps)

df = model.analyze_dynamic(
    simulation_period_sec = n_steps * dt_s,
    dt_s                  = dt_s,
    T_tank_w_init_C       = 50.0,
    dhw_usage_schedule    = dhw,
    T0_schedule           = T0,
    I_DN_schedule         = I_DN,
    I_dH_schedule         = I_dH,
)

To quantify the contribution, run the same schedules through a base AirSourceHeatPumpBoiler and difference the daily compressor energy:

from tmhp import AirSourceHeatPumpBoiler

base = AirSourceHeatPumpBoiler(ref="R32").analyze_dynamic(
    simulation_period_sec = n_steps * dt_s,
    dt_s                  = dt_s,
    T_tank_w_init_C       = 50.0,
    dhw_usage_schedule    = dhw,
    T0_schedule           = T0,
)

def daily_kwh(s, dt_s=dt_s):
    return float(s.sum()) * dt_s / 3.6e6

saving = daily_kwh(base["E_cmp [W]"]) - daily_kwh(df["E_cmp [W]"])
print(f"STC preheat saving: {saving:.2f} kWh/day")
class tmhp.ASHPB_STC_preheat(*, stc, **kwargs)[source]

Bases: AirSourceHeatPumpBoiler

ASHPB + SolarThermalCollector in mains_preheat placement.

Physical configuration

The STC preheats mains cold water before it enters the storage tank. The raised inlet temperature (T_tank_w_in_override_K) reduces the thermal load on the heat pump compressor.

Orchestration responsibility

This class owns all simulation logic for the STC:

  • _run_subsystems: activation probe + calc_performance() → sets T_tank_w_in_override_K when active

  • _augment_results: result column assembly (uses pump outlet temperature directly; no re-evaluation at solved tank temp)

  • _postprocess: STC exergy calculation. Tank boundary corrections are not applied because the preheated inlet temperature is already reflected in the core X_tank_w_in [W] column (X_in_tank_add = 0).

type stc:

SolarThermalCollector

param stc:

Pure physics engine. No mode constraint required.

type stc:

SolarThermalCollector

type **kwargs:

param **kwargs:

Forwarded to AirSourceHeatPumpBoiler.

raises TypeError:

If stc is not a SolarThermalCollector instance.

__init__(*, stc, **kwargs)[source]
Parameters:

stc (SolarThermalCollector)

STC with stratified tank

class tmhp.ASHPB_STC_tank(*, stc, **kwargs)[source]

Bases: AirSourceHeatPumpBoiler

ASHPB + SolarThermalCollector in tank_circuit placement.

Physical configuration

The STC collector loop is connected directly to the storage tank. The STC draws water from the tank, heats it via solar energy, and returns it through the pump. The STC is activated only when the collector outlet temperature exceeds the current tank temperature.

Orchestration responsibility

This class owns all simulation logic for the STC:

  • _run_subsystems: activation probe + calc_performance()

  • _augment_results: result column assembly (re-evaluates at solved tank temperature for accuracy)

  • _postprocess: STC exergy calculation and tank boundary correction (X_tot, Xc_tank)

type stc:

SolarThermalCollector

param stc:

Pure physics engine. No mode constraint required.

type stc:

SolarThermalCollector

type **kwargs:

param **kwargs:

Forwarded to AirSourceHeatPumpBoiler.

raises TypeError:

If stc is not a SolarThermalCollector instance.

__init__(*, stc, **kwargs)[source]
Parameters:

stc (SolarThermalCollector)

PV + ESS

PhotovoltaicSystem generation feeds the compressor and auxiliaries; an EnergyStorageSystem buffers midday surplus for evening load; grid import covers whatever the two cannot supply.

from tmhp import ASHPB_PV_ESS
from tmhp.subsystems import EnergyStorageSystem, PhotovoltaicSystem

model = ASHPB_PV_ESS(
    pv  = PhotovoltaicSystem(),
    ess = EnergyStorageSystem(),
    ref = "R32",
)

# Pass I_DN_schedule + I_dH_schedule to analyze_dynamic exactly
# as you would for ASHPB_STC_preheat.
Two-panel daily energy balance for the PV + ESS scenario. Panel (a) is the timeseries of PV generation, HP load, and grid import. Panel (b) is the stacked-bar daily ledger of where PV ended up and where HP load came from.

24-hour ASHPB_PV_ESS run with a clear-sky irradiance profile and default PhotovoltaicSystem / EnergyStorageSystem sizes. Panel (b) makes the sizing tradeoff readable: shrinking the ESS column on the left would push more of the right-hand bar from “PV” to “Grid”. Generated by scripts/visualization/pv_ess_energy_balance.py.

class tmhp.ASHPB_PV_ESS(*, pv, ess=None, eta_inv=0.95, T_inv_K=313.15, **kwargs)[source]

Bases: AirSourceHeatPumpBoiler

ASHPB scenario where the heat pump is supplied by PV + ESS + Grid.

The PV/ESS routing is resolved synchronously inside _augment_results after the HP compressor load is known. No 1-step lag: the PV energy is allocated to the exact HP load produced in the same timestep.

Parameters:
  • pv (PhotovoltaicSystem) – Pure-physics PV + charge-controller model.

  • ess (EnergyStorageSystem) – Pure-physics battery model with charge() / discharge().

  • eta_inv (float) – Inverter DC→AC efficiency [–].

  • T_inv_K (float) – Inverter temperature for entropy calculation [K].

  • **kwargs – Forwarded to AirSourceHeatPumpBoiler.

__init__(*, pv, ess=None, eta_inv=0.95, T_inv_K=313.15, **kwargs)[source]
Parameters:

API reference

Integrated System Model: Air Source Heat Pump Boiler (ASHPB).

This system class orchestrates the dynamic interaction between distinct thermodynamic sub-components to simulate the overall heating performance. While implemented as an integrated model, its physical calculations represent the behavior of:

  1. Refrigerant Cycle (Vapor-Compression): Evaluates thermodynamic states using CoolProp, enforcing superheat/subcool margins.

  2. Heat Pump Compressor: Models the compression process using isentropic and volumetric efficiencies to compute the actual discharge enthalpy and mass flow rate. The compressor power is determined from the enthalpy difference and the mass flow rate.

  3. Expansion Valve: Modeled as an isenthalpic expansion device (constant enthalpy) that throttles the refrigerant from the condensing pressure down to the evaporating pressure.

  4. Heat Exchangers (Condenser & Evaporator):

    • Condenser: Placed inside the tank (hydronic), utilizing a static overall heat transfer coefficient (UA_cond_design).

    • Evaporator: Air-coupled outdoor unit, utilizing a dynamic overall heat transfer coefficient (UA_evap) that scales non-linearly with fan airflow (Colburn j-factor analogy).

  5. Thermal Storage Tank: Modeled with lumped-capacitance and DHW mixing logic.

At each time step, the model finds the minimum-power operating point (compressor + fan) via bounded 1-D optimisation (Brent’s method) over the evaporator approach temperature difference.

Note

See the project paper (KJACR 2025) for the underlying refrigerant-cycle theory and component-level modelling assumptions of this system.

Optional sub-components (injected via constructor): - SolarThermalCollector — tank-circuit or mains-preheat placement - (future) PVPanel — photovoltaic integration

Tank-level management and UV disinfection are built-in features configured through constructor parameters.

class tmhp.air_source_heat_pump_boiler.AirSourceHeatPumpBoiler(ref='R134a', V_disp_cmp=0.0002, eta_cmp_isen=None, eta_cmp_vol=None, eta_cmp_mech=0.855, dT_superheat=5.0, dT_subcool=5.0, UA_cond_design=None, UA_evap_design=None, n_evap=0.65, dV_ou_fan_a_design=None, dP_ou_fan_design=60.0, A_cross_ou=None, eta_ou_fan_design=0.6, T_tank_w_upper_bound=65.0, T_tank_w_lower_bound=60.0, T_mix_w_out=40.0, T_sup_w=15.0, hp_capacity=15000.0, dV_mix_w_out_max=0.0045, r0=0.2, H=1.2, x_shell=0.005, x_ins=0.05, k_shell=25, k_ins=0.03, h_o=15, tank_always_full=True, tank_level_lower_bound=0.5, tank_level_upper_bound=1.0, dV_tank_w_in_refill=0.001, prevent_simultaneous_flow=False, hp_on_schedule=None, stc=None, pv=None, uv=None, vsd_coeffs_ou=None)[source]

Bases: object

Air source heat pump boiler with outdoor-air evaporator.

The refrigerant cycle is resolved via CoolProp with user-specified superheat / subcool margins. The condenser approach temperature is determined analytically (dT_ref_cond = Q_cond_target / UA_cond), and a bounded 1-D optimiser (Brent’s method) minimises total electrical input (E_cmp + E_ou_fan) over the evaporator approach.

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])

  • n_evap (float)

  • dV_ou_fan_a_design (Optional[float])

  • dP_ou_fan_design (float)

  • A_cross_ou (Optional[float])

  • eta_ou_fan_design (float)

  • T_tank_w_upper_bound (float)

  • T_tank_w_lower_bound (float)

  • T_mix_w_out (float)

  • T_sup_w (float)

  • hp_capacity (float)

  • dV_mix_w_out_max (float)

  • r0 (float)

  • H (float)

  • x_shell (float)

  • x_ins (float)

  • k_shell (float)

  • k_ins (float)

  • h_o (float)

  • tank_always_full (bool)

  • tank_level_lower_bound (float)

  • tank_level_upper_bound (float)

  • dV_tank_w_in_refill (float)

  • prevent_simultaneous_flow (bool)

  • hp_on_schedule (Optional[list[tuple[float, float]]])

  • stc (Optional[SolarThermalCollector])

  • pv (Optional[PhotovoltaicSystem])

  • vsd_coeffs_ou (Optional[dict])

__init__(ref='R134a', V_disp_cmp=0.0002, eta_cmp_isen=None, eta_cmp_vol=None, eta_cmp_mech=0.855, dT_superheat=5.0, dT_subcool=5.0, UA_cond_design=None, UA_evap_design=None, n_evap=0.65, dV_ou_fan_a_design=None, dP_ou_fan_design=60.0, A_cross_ou=None, eta_ou_fan_design=0.6, T_tank_w_upper_bound=65.0, T_tank_w_lower_bound=60.0, T_mix_w_out=40.0, T_sup_w=15.0, hp_capacity=15000.0, dV_mix_w_out_max=0.0045, r0=0.2, H=1.2, x_shell=0.005, x_ins=0.05, k_shell=25, k_ins=0.03, h_o=15, tank_always_full=True, tank_level_lower_bound=0.5, tank_level_upper_bound=1.0, dV_tank_w_in_refill=0.001, prevent_simultaneous_flow=False, hp_on_schedule=None, stc=None, pv=None, uv=None, vsd_coeffs_ou=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])

  • n_evap (float)

  • dV_ou_fan_a_design (Optional[float])

  • dP_ou_fan_design (float)

  • A_cross_ou (Optional[float])

  • eta_ou_fan_design (float)

  • T_tank_w_upper_bound (float)

  • T_tank_w_lower_bound (float)

  • T_mix_w_out (float)

  • T_sup_w (float)

  • hp_capacity (float)

  • dV_mix_w_out_max (float)

  • r0 (float)

  • H (float)

  • x_shell (float)

  • x_ins (float)

  • k_shell (float)

  • k_ins (float)

  • h_o (float)

  • tank_always_full (bool)

  • tank_level_lower_bound (float)

  • tank_level_upper_bound (float)

  • dV_tank_w_in_refill (float)

  • prevent_simultaneous_flow (bool)

  • hp_on_schedule (Optional[list[tuple[float, float]]])

  • stc (Optional[SolarThermalCollector])

  • pv (Optional[PhotovoltaicSystem])

  • vsd_coeffs_ou (Optional[dict])

analyze_steady(T_tank_w, T0, Q_ref_cond, *, return_dict=True)[source]

Run a steady-state performance snapshot.

Evaluates the refrigerant cycle at a given operating point (T_tank_w, T0, Q_ref_cond) without solving the tank energy balance or tracking dynamic flows.

Parameters:
  • T_tank_w (float) – Tank water temperature [°C] — treated as a given input.

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

  • Q_ref_cond (float) – Target condenser heat rate [W].

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

Returns:

Cycle state plus diagnostic flags.

Two keys are useful for branching:

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

  • "failure_reason" (str) — one of "none", "cycle_invalid", "hx_not_converged", or "optimizer_failed".

ASHPB returns the cycle numbers (E_cmp, Q_ref_cond, …) whenever _calc_state produced a dict at all. A failure_reason of "hx_not_converged" therefore does not invalidate the result: it only means the HX residual exceeded tolerance and the converged flag is False. Off-mode fallback (E_cmp=0) only occurs when the cycle itself was infeasible.

Return type:

dict | pd.DataFrame

analyze_dynamic(simulation_period_sec, dt_s, T_tank_w_init_C, dhw_usage_schedule, T0_schedule, I_DN_schedule=None, I_dH_schedule=None, T_sup_w_schedule=None, tank_level_init=1.0, result_save_csv_path=None)[source]

Run a time-stepping dynamic simulation.

Fully implicit scheme: fsolve solves for [T_next, level_next] each timestep.

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

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

  • T_tank_w_init_C (float) – Initial tank temperature [°C].

  • dhw_usage_schedule (np.ndarray) – DHW volumetric flow rate per step [m³/s].

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

  • I_DN_schedule (array-like | None) – Direct-normal irradiance per step [W/m²].

  • I_dH_schedule (array-like | None) – Diffuse-horizontal irradiance [W/m²].

  • T_sup_w_schedule (array-like | None) – Mains water supply temperature per step [°C]. If None, the constructor value T_sup_w is used for every step (backward-compatible).

  • tank_level_init (float) – Initial fractional tank level (0–1).

  • 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.

Owns the full HP exergy topology:

  1. Refrigerant state-point exergy (CoolProp)

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

  3. Air exergy (outdoor unit)

  4. Heat exchanger Carnot exergy (condenser, evaporator)

  5. Water exergy (tank inlet/outlet, mixing valve)

  6. Heat loss exergy, tank stored exergy

  7. Subsystem exergy via calc_exergy() protocol

  8. Component-level exergy destruction

  9. Exergetic efficiency metrics

Parameters:

df (pd.DataFrame) – Result DataFrame from analyze_dynamic().

Returns:

DataFrame with exergy columns appended.

Return type:

pd.DataFrame

ASHPB with SolarThermalCollector — mains_preheat placement.

Phase 3 restructuring: all simulation orchestration logic (activation probe, result assembly, exergy calculation) is implemented directly in this class. SolarThermalCollector is used purely as a physics engine (calc_performance()), with no dependency on step(), assemble_results(), or calc_exergy().

Note

See the project paper for the orchestration logic and energy/exergy boundaries used by hybrid (STC + HP) systems.

Usage

from enex_analysis import SolarThermalCollector
from enex_analysis.ASHPB_STC_preheat import ASHPB_STC_preheat

stc = SolarThermalCollector(A_stc=4.0)
model = ASHPB_STC_preheat(
    stc=stc,
    ref="R134a",
    hp_capacity=15_000.0,
    T_tank_w_lower_bound=55.0,
    T_tank_w_upper_bound=65.0,
    T_mix_w_out=42.0,
)
df = model.analyze_dynamic(...)
class tmhp.ashpb_stc_preheat.ASHPB_STC_preheat(*, stc, **kwargs)[source]

Bases: AirSourceHeatPumpBoiler

ASHPB + SolarThermalCollector in mains_preheat placement.

Physical configuration

The STC preheats mains cold water before it enters the storage tank. The raised inlet temperature (T_tank_w_in_override_K) reduces the thermal load on the heat pump compressor.

Orchestration responsibility

This class owns all simulation logic for the STC:

  • _run_subsystems: activation probe + calc_performance() → sets T_tank_w_in_override_K when active

  • _augment_results: result column assembly (uses pump outlet temperature directly; no re-evaluation at solved tank temp)

  • _postprocess: STC exergy calculation. Tank boundary corrections are not applied because the preheated inlet temperature is already reflected in the core X_tank_w_in [W] column (X_in_tank_add = 0).

type stc:

SolarThermalCollector

param stc:

Pure physics engine. No mode constraint required.

type stc:

SolarThermalCollector

type **kwargs:

param **kwargs:

Forwarded to AirSourceHeatPumpBoiler.

raises TypeError:

If stc is not a SolarThermalCollector instance.

__init__(*, stc, **kwargs)[source]
Parameters:

stc (SolarThermalCollector)

ASHPB with SolarThermalCollector — tank_circuit placement.

Phase 3 restructuring: all simulation orchestration logic (activation probe, result assembly, exergy calculation) is implemented directly in this class. SolarThermalCollector is used purely as a physics engine (calc_performance()), with no dependency on step(), assemble_results(), or calc_exergy().

Note

See the project paper for the orchestration logic and energy/exergy boundaries used by hybrid (STC + HP, stratified tank) systems.

Usage

from enex_analysis import SolarThermalCollector
from enex_analysis.ASHPB_STC_tank import ASHPB_STC_tank

stc = SolarThermalCollector(A_stc=4.0)
model = ASHPB_STC_tank(
    stc=stc,
    ref="R134a",
    hp_capacity=15_000.0,
    T_tank_w_lower_bound=55.0,
    T_tank_w_upper_bound=65.0,
    T_mix_w_out=42.0,
)
df = model.analyze_dynamic(...)
class tmhp.ashpb_stc_tank.ASHPB_STC_tank(*, stc, **kwargs)[source]

Bases: AirSourceHeatPumpBoiler

ASHPB + SolarThermalCollector in tank_circuit placement.

Physical configuration

The STC collector loop is connected directly to the storage tank. The STC draws water from the tank, heats it via solar energy, and returns it through the pump. The STC is activated only when the collector outlet temperature exceeds the current tank temperature.

Orchestration responsibility

This class owns all simulation logic for the STC:

  • _run_subsystems: activation probe + calc_performance()

  • _augment_results: result column assembly (re-evaluates at solved tank temperature for accuracy)

  • _postprocess: STC exergy calculation and tank boundary correction (X_tot, Xc_tank)

type stc:

SolarThermalCollector

param stc:

Pure physics engine. No mode constraint required.

type stc:

SolarThermalCollector

type **kwargs:

param **kwargs:

Forwarded to AirSourceHeatPumpBoiler.

raises TypeError:

If stc is not a SolarThermalCollector instance.

__init__(*, stc, **kwargs)[source]
Parameters:

stc (SolarThermalCollector)

ASHPB Scenario: Heat Pump driven by PV + ESS with Grid/Dump integration.

Energy routing (all logic lives here, subsystems are pure physics):

  1. PV generation → pv.calc_performance()

  2. DC routing: - PV surplus → ess.charge(); leftover → dump - PV deficit → ess.discharge(); leftover → grid import

  3. Inverter conversion loss applied to DC supply

  4. Grid import covers any remaining AC shortfall

Note

See the project paper for the orchestration logic and energy/exergy boundaries used by hybrid (PV + ESS + HP) systems.

class tmhp.ashpb_pv_ess.ASHPB_PV_ESS(*, pv, ess=None, eta_inv=0.95, T_inv_K=313.15, **kwargs)[source]

Bases: AirSourceHeatPumpBoiler

ASHPB scenario where the heat pump is supplied by PV + ESS + Grid.

The PV/ESS routing is resolved synchronously inside _augment_results after the HP compressor load is known. No 1-step lag: the PV energy is allocated to the exact HP load produced in the same timestep.

Parameters:
  • pv (PhotovoltaicSystem) – Pure-physics PV + charge-controller model.

  • ess (EnergyStorageSystem) – Pure-physics battery model with charge() / discharge().

  • eta_inv (float) – Inverter DC→AC efficiency [–].

  • T_inv_K (float) – Inverter temperature for entropy calculation [K].

  • **kwargs – Forwarded to AirSourceHeatPumpBoiler.

__init__(*, pv, ess=None, eta_inv=0.95, T_inv_K=313.15, **kwargs)[source]
Parameters:

Validation

ASHPB has been benchmarked against the Samsung EHS Mono HT Quiet R32 14 kW catalogue across 15 operating points. See Validation for the parity plot and per-point comparison.