From 0102ff313a6c033ae069de5a27f0331dbbd1e903 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 18 May 2026 16:49:11 +0000 Subject: [PATCH] slice S-B18: SAP rating uses UK-average weather (region 0), not cert region MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SAP 10.2 Appendix U explicit rule: "Calculations for fabric energy efficiency (FEE), regulation compliance (TER and DER, TPER and DPER) and for ratings (SAP rating and environmental impact rating) are done with UK average weather. Other calculations (such as for energy use and costs on EPCs) are done using local weather." Our calculator was using the cert's region_code for everything. Spec mandates region 0 (UK average) for rating outputs. Net MAE neutral on the 100-cert sample (most certs sit close to UK average) and on the 300-cert sample but it's spec-correct, and aligns with what the cert assessor's SAP rating actually computes. Found by switching from probe-driven to worksheet-driven iteration — per user suggestion this is the more efficient mode once the easy wins from probe-driven have been extracted. 100-cert: MAE 4.39 (unchanged) 300-cert: MAE 5.44 Co-Authored-By: Claude Opus 4.7 --- .../src/domain/sap/rdsap/cert_to_inputs.py | 22 ++++++++++--------- .../sap/rdsap/tests/test_cert_to_inputs.py | 17 ++++++++------ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/packages/domain/src/domain/sap/rdsap/cert_to_inputs.py b/packages/domain/src/domain/sap/rdsap/cert_to_inputs.py index 7a473041..cffbd2d3 100644 --- a/packages/domain/src/domain/sap/rdsap/cert_to_inputs.py +++ b/packages/domain/src/domain/sap/rdsap/cert_to_inputs.py @@ -214,16 +214,18 @@ def _dwelling_exposure(dwelling_type: Optional[str]) -> DwellingExposure: def _region_index(region_code: Optional[str]) -> int: - """Coerce EpcPropertyData.region_code (str) to the integer Appendix U - region index. Out-of-range or unparseable → 0 (UK average).""" - if region_code is None: - return 0 - try: - idx = int(region_code) - except (TypeError, ValueError): - return 0 - if 0 <= idx <= 21: - return idx + """SAP rating must be computed with UK-average weather per Appendix U: + "Calculations for fabric energy efficiency (FEE), regulation compliance + (TER and DER, TPER and DPER) and for ratings (SAP rating and environmental + impact rating) are done with UK average weather. Other calculations (such + as for energy use and costs on EPCs) are done using local weather." + + Since our calculator's primary output is the SAP rating, we always return + region 0 (UK average) regardless of the cert's actual region_code. A + future slice may add a `compute_local_weather` flag to also produce the + energy-use kWh totals at local weather. + """ + _ = region_code return 0 diff --git a/packages/domain/src/domain/sap/rdsap/tests/test_cert_to_inputs.py b/packages/domain/src/domain/sap/rdsap/tests/test_cert_to_inputs.py index 9bd27a9c..2915dde0 100644 --- a/packages/domain/src/domain/sap/rdsap/tests/test_cert_to_inputs.py +++ b/packages/domain/src/domain/sap/rdsap/tests/test_cert_to_inputs.py @@ -95,12 +95,15 @@ def test_minimal_cert_round_trips_through_calculator_and_returns_sap_result() -> assert result.total_fuel_cost_gbp > 0 -def test_region_code_string_is_translated_to_appendix_u_region_index() -> None: - # Arrange — `region_code` on EpcPropertyData is a string ("1"="Thames", - # "20"="Shetland"); the mapper must coerce it to the integer index - # Appendix U expects, falling back to 0 (UK average) when missing. +def test_calculator_always_uses_uk_average_weather_for_rating() -> None: + # Arrange — SAP 10.2 Appendix U explicitly states: "Calculations for + # ratings (SAP rating and environmental impact rating) are done with + # UK average weather". The mapper must therefore always set region=0 + # regardless of the cert's lodged region_code; a future slice may + # add a separate local-weather path for the energy-use display + # numbers that EPC software shows alongside the rating. base = _typical_semi_detached_epc() - thames = base + thames = base # region_code="1" no_region = make_minimal_sap10_epc( total_floor_area_m2=_TYPICAL_TFA_M2, habitable_rooms_count=4, @@ -113,8 +116,8 @@ def test_region_code_string_is_translated_to_appendix_u_region_index() -> None: inputs_thames = cert_to_inputs(thames) inputs_default = cert_to_inputs(no_region) - # Assert - assert inputs_thames.region == 1 + # Assert — both collapse to UK average (region 0) for rating purposes. + assert inputs_thames.region == 0 assert inputs_default.region == 0