From eb5f52e9c480ae269bf9c34c1307a07500698308 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Thu, 16 Jul 2026 18:36:35 +0000 Subject: [PATCH] =?UTF-8?q?Bill=20community-heating=20DHW=20at=20the=20net?= =?UTF-8?q?work=20fuel=20rate=20=F0=9F=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- .../rdsap/test_cert_to_inputs.py | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py index a59aef297..d1c31bace 100644 --- a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py +++ b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py @@ -45,7 +45,11 @@ from domain.sap10_ml.tests._fixtures import ( make_sap_heating, make_window, ) -from domain.sap10_calculator.calculator import Sap10Calculator, SapResult +from domain.sap10_calculator.calculator import ( + Sap10Calculator, + SapResult, + calculate_sap_from_inputs, +) from domain.sap10_calculator.exceptions import ( MissingMainFuelType, UnmappedSapCode, @@ -69,6 +73,8 @@ from domain.sap10_calculator.rdsap.cert_to_inputs import ( _TABLE_4G_DEFAULT_MEV_SFP_W_PER_L_PER_S, # pyright: ignore[reportPrivateUsage] dimensions_from_cert, fuel_cost_section_from_cert, + _fuel_cost_gbp_per_kwh, # pyright: ignore[reportPrivateUsage] + RDSAP_10_TABLE_32_PRICES, # pyright: ignore[reportPrivateUsage] _table_12_factor_fuel_code, # pyright: ignore[reportPrivateUsage] _table_12a_system_for_main, # pyright: ignore[reportPrivateUsage] _is_electric_main, # pyright: ignore[reportPrivateUsage] @@ -252,6 +258,50 @@ def test_thermal_mass_wall_code_8_is_park_home_only_when_property_type_says_so() assert abs(tmp_system_built - 250.0) <= 1e-9 +def test_community_heating_dhw_bills_at_network_rate_not_lodged_fuel() -> None: + # Arrange — a community heat-network main (Table 4a code 301, cat 6) whose + # DHW is heated by the network (WHC 901, "from the main system") but whose + # lodged HW fuel is the Elmhurst "Mains gas" (26) placeholder. SAP 10.2 §C3 + # bills network heat — space AND water — at the network's own Table-12 rate, + # so the DHW must inherit the main's heat-network rate, not the lodged fuel's. + main = MainHeatingDetail( + has_fghrs=False, + main_fuel_type=20, # mains gas (community) + heat_emitter_type=1, + emitter_temperature=1, + main_heating_control=2306, + main_heating_category=6, + sap_main_heating_code=301, + ) + epc = make_minimal_sap10_epc( + total_floor_area_m2=_TYPICAL_TFA_M2, + habitable_rooms_count=4, + country_code="ENG", + sap_building_parts=[make_building_part(construction_age_band="E")], + sap_heating=make_sap_heating( + main_heating_details=[main], + water_heating_code=901, # DHW from the main (heat network) + water_heating_fuel=26, # Elmhurst "Mains gas" placeholder + ), + ) + + # Act + section = fuel_cost_section_from_cert(epc) + + # Assert — total DHW cost is priced at the main's heat-network rate, NOT the + # lodged mains-gas rate. Network fuel is single-rate, so (245)+(246)+(247) = + # HW kWh x network rate. + assert section is not None + hw_kwh = calculate_sap_from_inputs(cert_to_inputs(epc)).hot_water_kwh_per_yr + total_water_cost_gbp = ( + section.water_high_rate_cost_gbp + + section.water_low_rate_cost_gbp + + section.water_other_fuel_cost_gbp + ) + network_rate = _fuel_cost_gbp_per_kwh(main, RDSAP_10_TABLE_32_PRICES) + assert total_water_cost_gbp == pytest.approx(hw_kwh * network_rate, rel=1e-6) + + def test_heat_network_main_applies_table12c_dlf_to_main_heating_efficiency() -> None: # Arrange — heat-network main heating (Table 4a code 301 = community # heating with CHP/boilers; main_heating_category=6). Cert age band