diff --git a/domain/sap10_calculator/rdsap/cert_to_inputs.py b/domain/sap10_calculator/rdsap/cert_to_inputs.py index d6e2db76..efc2d20a 100644 --- a/domain/sap10_calculator/rdsap/cert_to_inputs.py +++ b/domain/sap10_calculator/rdsap/cert_to_inputs.py @@ -3638,6 +3638,24 @@ def cert_to_inputs( main_category=water_main.main_heating_category if water_main is not None else None, main_fuel=_main_fuel_code(water_main), ) + # SAP 10.2 Table 4c row "No boiler interlock — regular boiler: + # DHW −5%" (PDF p.169). RdSAP §3 (PDF p.57) defines boiler + # interlock as "Assumed present if there is a room thermostat and + # (for stored hot water systems heated by the boiler) a cylinder + # thermostat. Otherwise not interlocked." A combi-fed cylinder + # routes the boiler as a regular boiler for the DHW circuit (the + # combi's instantaneous-DHW capability is bypassed), so the + # regular-boiler row applies. Note c): the adjustment caps at + # −5pp (no thermostatic control and no boiler interlock do not + # accumulate). Cert 000565 (cylinder lodged + cyl-stat absent + + # WHC 914 to PCDB combi Main 2) closes 79% → 74% — matches + # worksheet (217)m exactly. + if ( + epc.has_hot_water_cylinder + and epc.sap_heating.cylinder_thermostat != "Y" + and water_pcdb_main is not None + ): + water_eff -= 0.05 # SAP 10.2 Appendix N3.6 + N3.7(a) — when an HP cert lodges a PCDB # Table 362 record, the cascade replaces the Table 4a defaults with # APM-interpolated η_space and η_water at the dwelling's PSR. diff --git a/domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py b/domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py index be6ca923..21aa00ef 100644 --- a/domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py +++ b/domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py @@ -1802,6 +1802,65 @@ def test_cert_with_hot_water_cylinder_computes_primary_loss_59m_from_sap_table_3 ) +def test_table_4c_no_boiler_interlock_applies_minus_5_dhw_adjustment_when_cylinder_lodged_without_thermostat() -> None: + """SAP 10.2 Table 4c (PDF p.169-170) "Efficiency adjustments": + + (2) Efficiency adjustment due to control system Space DHW + No boiler interlock - regular boiler -5 -5 + No boiler interlock - combi -5 0 + + Note c): "These do not accumulate as no thermostatic control or + presence of a bypass means that there is no boiler interlock." + + RdSAP 10 §3 (PDF p.57) "Boiler interlock" definition: + Assumed present if there is a room thermostat and (for stored + hot water systems heated by the boiler) a cylinder thermostat. + Otherwise not interlocked. + + A boiler feeding a hot-water cylinder is in the "stored hot water + systems heated by the boiler" category — when no cylinder + thermostat is lodged, boiler interlock is absent and Table 4c + applies -5 percentage points to the DHW seasonal efficiency. This + holds regardless of whether the boiler is a combi or regular type; + in a combi-fed-cylinder configuration the combi acts as a regular + boiler for the DHW circuit (instantaneous DHW capability is + bypassed by the cylinder routing), so the regular-boiler row of + Table 4c (-5%) applies. + + Cert 000565 (ASHP Main 1 + Vaillant PCDB 15100 combi Main 2 + WHC + 914 + 160 L cylinder + cylinder thermostat absent) reproduces the + pattern. PCDB summer η = 79.0; worksheet (217)m = 74.0; the + cascade was returning 79% (PCDB summer unchanged), driving the + -238 kWh HW pin over-shoot that survived S0380.79. + + Predicted HW closure: 2778.72 / 0.74 = 3754.99 vs worksheet + 3755.03 — within 0.04 kWh. + """ + # Arrange — use the real cert 000565 fixture (Elmhurst extractor + + # mapper) so the (62)m demand cascade is the worksheet-pinned + # tuple and only the (217)m efficiency step is under test. + from domain.sap10_calculator.worksheet.tests._elmhurst_worksheet_000565 import ( + build_epc as build_cert_000565, + ) + from domain.sap10_calculator.calculator import calculate_sap_from_inputs + epc = build_cert_000565() + + # Act + result = calculate_sap_from_inputs(cert_to_inputs(epc)) + + # Assert — cert 000565 worksheet line (219) sum = 3755.0288 (HW + # fuel kWh/yr). Pre-fix: 3517.37 (off by −238 from (64)/0.79). + # With Table 4c -5% applied: (64)/0.74 = 2778.72/0.74 = 3754.99, + # closing to worksheet 3755.03 within 0.04 kWh. + expected_hw_kwh = 3755.0288 + assert abs(result.hot_water_kwh_per_yr - expected_hw_kwh) < 0.1, ( + f"cert 000565 hot_water_kwh_per_yr: got " + f"{result.hot_water_kwh_per_yr!r}, want {expected_hw_kwh!r} per " + f"SAP 10.2 Table 4c (No boiler interlock — regular boiler: " + f"DHW −5%); RdSAP §3 (no cylinder thermostat → no boiler interlock)" + ) + + def test_cylinder_storage_loss_applies_57m_solar_adjustment_per_sap_4_line_7693() -> None: """SAP 10.2 §4 line 7693 (PDF p.137):