The ASHP bundle sizes the heat pump to the dwelling's design heat loss 🟥

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-07-03 10:26:35 +00:00
parent 039df7ae87
commit df38dda7b0
2 changed files with 32 additions and 3 deletions

View file

@ -335,17 +335,23 @@ def recommend_heating(
products: ProductRepository,
restrictions: PlanningRestrictions = PlanningRestrictions(),
considered_measures: Optional[frozenset[MeasureType]] = None,
*,
design_heat_loss_kw: Optional[float] = None,
) -> Optional[Recommendation]:
"""Return a "Heating & Hot Water" Recommendation of competing whole-system
bundles for the dwelling, else None when no bundle is eligible. ASHP is
additionally gated by the Property's planning protections (ADR-0024)."""
additionally gated by the Property's planning protections (ADR-0024).
``design_heat_loss_kw`` (the calculator's SAP design heat loss) sizes the
ASHP to the dwelling so SAP's Appendix-N PSR efficiency reads near its peak
(ADR-0049); when omitted, sizing falls back to the floor-area proxy."""
options: list[MeasureOption] = []
hhr_option = _hhr_storage_option(epc, products)
if hhr_option is not None:
options.append(hhr_option)
ashp_option = _ashp_option(epc, products, restrictions)
ashp_option = _ashp_option(epc, products, restrictions, design_heat_loss_kw)
if ashp_option is not None:
options.append(ashp_option)
@ -730,9 +736,11 @@ def _ashp_option(
epc: EpcPropertyData,
products: ProductRepository,
restrictions: PlanningRestrictions,
design_heat_loss_kw: Optional[float] = None,
) -> Optional[MeasureOption]:
"""The air-source heat-pump bundle, offered for any non-flat house/bungalow
that is not listed/heritage and not already a heat pump."""
that is not listed/heritage and not already a heat pump. The pump is sized to
``design_heat_loss_kw`` (ADR-0049), falling back to the floor-area proxy."""
if not _ashp_eligible(epc, restrictions):
return None
# Cost is composed per-dwelling from the rate sheet (ADR-0025), not the

View file

@ -791,3 +791,24 @@ def test_ashp_sizing_caps_at_the_largest_pump_for_a_high_heat_loss_dwelling() ->
# Assert — capped at the 12 kW rung, not left undersized at 5 kW.
assert pcdb_id == 110281
def test_ashp_bundle_sizes_the_pump_to_the_design_heat_loss() -> None:
# A high-heat-loss house (17.5 kW) must get a pump sized to it — the capped
# 12 kW aroTHERM plus (PCDB 110281) — so SAP's Appendix-N PSR efficiency reads
# near its peak, not the fixed 5 kW unit (110257) whose PSR collapses.
# Arrange
baseline: EpcPropertyData = _gas_boiler_house()
# Act
recommendation: Recommendation | None = recommend_heating(
baseline, _StubProducts(), design_heat_loss_kw=17.5
)
# Assert — the ASHP overlay carries the sized 12 kW record, not the fixed 5 kW.
assert recommendation is not None
ashp = next(
o for o in recommendation.options if o.measure_type == "air_source_heat_pump"
)
assert ashp.overlay.heating is not None
assert ashp.overlay.heating.main_heating_index_number == 110281