mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
fix(water): don't apply heat-pump water SCOP to a separate immersion (SAP N3.7a)
When a heat-pump cert lodges a PCDB Table 362 record, the APM override set BOTH the space efficiency (N3.6) and the water efficiency (N3.7a) from the heat pump unconditionally. But the PCDB η_water applies only when the DHW is heated BY the heat pump (water-heating code "from main": 901/902/914). A separate electric immersion (WHC 903) heats the water at 100% regardless of the space system, so applying the HP's water SCOP (187.5% × 0.6 in-use = 112.5%) under-counted the immersion's hot-water fuel. Gate the η_water override on the DHW-from-main codes; a separate immersion keeps its own 100% efficiency. Space η_space still always uses the APM value (the heat pump is the space main). Worksheet-validated to 1e-4 on simulated case 45 (HP space + WHC-903 immersion): water fuel (62) 1893.57 -> 2130.2639, total cost (255) 619.7433, CO2 692.13 — all matching the P960 exactly; SAP 60.53 -> rounds to the worksheet's 61. RdSAP-21.0.1 corpus unchanged (no HP+WHC903 certs in it). Pinned in test_cert_to_inputs (immersion fuel is main-independent). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9b0c590bf8
commit
72ef0f0e7b
2 changed files with 68 additions and 1 deletions
|
|
@ -7216,7 +7216,21 @@ def cert_to_inputs(
|
||||||
epc=epc,
|
epc=epc,
|
||||||
)
|
)
|
||||||
if apm_efficiencies is not None:
|
if apm_efficiencies is not None:
|
||||||
eff, water_eff = apm_efficiencies
|
# η_space (N3.6) always replaces the Table 4a default — the heat
|
||||||
|
# pump is the space main. η_water (N3.7a) applies ONLY when the DHW
|
||||||
|
# is actually heated by that main (WHC "from main": 901/902/914). A
|
||||||
|
# separate electric immersion (WHC 903) or other independent DHW
|
||||||
|
# source keeps its own water efficiency (immersion = 100%), not the
|
||||||
|
# HP's water SCOP — otherwise a HP-space + immersion-DHW dwelling
|
||||||
|
# under-counts its hot-water fuel (case 45: water 2130 -> 1894 kWh,
|
||||||
|
# +1.5 SAP, because 187.5% × 0.6 in-use = 112.5% was applied where
|
||||||
|
# the worksheet (216) uses 100%).
|
||||||
|
eff, apm_water_eff = apm_efficiencies
|
||||||
|
if (
|
||||||
|
epc.sap_heating.water_heating_code
|
||||||
|
in _WATER_INHERIT_FROM_MAIN_CODES
|
||||||
|
):
|
||||||
|
water_eff = apm_water_eff
|
||||||
if (
|
if (
|
||||||
_is_heat_network_main(main)
|
_is_heat_network_main(main)
|
||||||
and epc.sap_heating.water_heating_code in _WATER_INHERIT_FROM_MAIN_CODES
|
and epc.sap_heating.water_heating_code in _WATER_INHERIT_FROM_MAIN_CODES
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ from datatypes.epc.domain.epc_property_data import (
|
||||||
from domain.sap10_ml.tests._fixtures import (
|
from domain.sap10_ml.tests._fixtures import (
|
||||||
make_building_part,
|
make_building_part,
|
||||||
make_floor_dimension,
|
make_floor_dimension,
|
||||||
|
make_main_heating_detail,
|
||||||
make_minimal_sap10_epc,
|
make_minimal_sap10_epc,
|
||||||
make_sap_heating,
|
make_sap_heating,
|
||||||
make_window,
|
make_window,
|
||||||
|
|
@ -7581,3 +7582,55 @@ def test_index_less_mev_applies_table_4g_note_3_default_data_iuf() -> None:
|
||||||
# 2.5x the raw-0.8 value, not the raw default.
|
# 2.5x the raw-0.8 value, not the raw default.
|
||||||
assert fan_kwh > 0.0
|
assert fan_kwh > 0.0
|
||||||
assert abs(fan_kwh - expected) <= 1e-9
|
assert abs(fan_kwh - expected) <= 1e-9
|
||||||
|
|
||||||
|
|
||||||
|
def test_heat_pump_water_scop_not_applied_to_separate_immersion_dhw() -> None:
|
||||||
|
# Arrange — SAP 10.2 Appendix N3.7(a): a heat pump's PCDB water
|
||||||
|
# efficiency (η_water) applies to the DHW ONLY when the cylinder is
|
||||||
|
# heated BY the heat pump. A separate electric immersion (WHC 903) heats
|
||||||
|
# the water at 100% regardless of the space-heating system, so the HP's
|
||||||
|
# water SCOP must NOT leak onto it. Invariant: a WHC-903 immersion's
|
||||||
|
# hot-water fuel is INDEPENDENT of the main — a heat-pump main and a gas-
|
||||||
|
# boiler main yield the SAME immersion fuel (both 100%, no primary loss).
|
||||||
|
# Before the fix the APM override set η_water = 187.5% × 0.6 in-use =
|
||||||
|
# 112.5% on the HP cert, under-counting its immersion fuel. Worksheet-
|
||||||
|
# validated on simulated case 45: water (62) = 2130.26 kWh at η_water=100%,
|
||||||
|
# not 2130.26 / 1.125 = 1893.57.
|
||||||
|
hp_main = make_main_heating_detail(
|
||||||
|
main_fuel_type=29, # electricity
|
||||||
|
heat_emitter_type=1,
|
||||||
|
main_heating_category=4, # heat pump
|
||||||
|
main_heating_index_number=100053, # PCDB Table 362 ASHP (ECODAN 5 kW)
|
||||||
|
main_heating_data_source=1,
|
||||||
|
)
|
||||||
|
boiler_main = make_main_heating_detail(
|
||||||
|
main_fuel_type=26, # mains gas
|
||||||
|
heat_emitter_type=1,
|
||||||
|
main_heating_category=2, # gas boiler
|
||||||
|
sap_main_heating_code=102,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _immersion_epc(main: MainHeatingDetail) -> EpcPropertyData:
|
||||||
|
return make_minimal_sap10_epc(
|
||||||
|
total_floor_area_m2=_TYPICAL_TFA_M2,
|
||||||
|
habitable_rooms_count=4,
|
||||||
|
country_code="ENG",
|
||||||
|
has_hot_water_cylinder=True,
|
||||||
|
sap_heating=make_sap_heating(
|
||||||
|
main_heating_details=[main],
|
||||||
|
water_heating_code=903, # separate electric immersion
|
||||||
|
water_heating_fuel=30, # standard electricity
|
||||||
|
cylinder_size=2,
|
||||||
|
cylinder_insulation_type=1,
|
||||||
|
cylinder_insulation_thickness_mm=25,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act
|
||||||
|
hp_fuel = cert_to_inputs(_immersion_epc(hp_main)).hot_water_kwh_per_yr
|
||||||
|
boiler_fuel = cert_to_inputs(_immersion_epc(boiler_main)).hot_water_kwh_per_yr
|
||||||
|
|
||||||
|
# Assert — the immersion DHW fuel is identical whether the space main is a
|
||||||
|
# heat pump or a gas boiler (the HP water SCOP does not apply to it).
|
||||||
|
assert hp_fuel > 0.0
|
||||||
|
assert abs(hp_fuel - boiler_fuel) <= 1e-6
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue