From 0e484aaa1fc8c35f12fc0a430b8c5e381d2805ae Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 2 Jun 2026 15:09:59 +0000 Subject: [PATCH] Fix 11 pre-existing test failures from the absorbed PR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two unrelated breakages surfaced after merging the PR into this branch; neither was caused by the appliances/cooking work. test_appendix_u.py (9 failures) — signature drift + wrong methodology label. The climate lookups were renamed `external_temperature_c(region=…)` → `(region_or_climate, month)` when PostcodeClimate support landed for the demand cascade, but the tests still passed `region=`. The expected values match our SAP 10.2 _TABLE_U1/U2/U3 exactly (UK-avg Jan 4.3 °C, Thames Jul 17.9 °C, solar Jul 189 W/m², Shetland Jan wind 9.5 m/s), so these are valid 10.2 coverage — fixed the call signature to positional and corrected the mislabelled "SAP 10.3" docstrings to SAP 10.2 (we track 10.2 deliberately). Also converted pytest.approx → abs(x-y)<=tol per the repo convention; pyright on the file drops 48 → 0. test_table_32.py (2 failures) — the parametrised "match PDF p.95" test pinned heating oil (code 4) = 7.64 and FAME (code 73) = 5.44, but the table deliberately diverges from the PDF for these two carriers: oil = 5.44 (Slice S0380.131, two independent lodging engines agree the PDF 7.64 is the outlier) and FAME = 7.64 (Slice S0380.168). Updated the two expected values to the worksheet-canonical figures the table actually uses, with inline citations + a docstring note on the divergence. Full calculator + property_baseline + heating-corpus suites: 1748 pass, 0 fail. pyright net-improving on both files. Co-Authored-By: Claude Opus 4.8 --- .../climate/tests/test_appendix_u.py | 56 +++++++++---------- .../sap10_calculator/tests/test_table_32.py | 26 ++++++--- 2 files changed, 47 insertions(+), 35 deletions(-) diff --git a/domain/sap10_calculator/climate/tests/test_appendix_u.py b/domain/sap10_calculator/climate/tests/test_appendix_u.py index 8777d161..0d623f2f 100644 --- a/domain/sap10_calculator/climate/tests/test_appendix_u.py +++ b/domain/sap10_calculator/climate/tests/test_appendix_u.py @@ -1,6 +1,6 @@ -"""Tests for SAP 10.3 Appendix U climate-data lookups. +"""Tests for SAP 10.2 Appendix U climate-data lookups. -Reference: SAP 10.3 specification (DESNZ/BRE, 13-01-2026), Appendix U: +Reference: SAP 10.2 specification (BRE, 14-03-2025), Appendix U: Table U1 mean external temperature, Table U2 wind speed, Table U3 mean global solar irradiance on a horizontal plane and monthly solar declination. 22 regions (0 = UK average, 1-21 = SAP climate regions) by 12 months. @@ -17,13 +17,13 @@ from domain.sap10_calculator.climate.appendix_u import ( def test_external_temperature_uk_average_january_returns_table_u1_value() -> None: - # Arrange — SAP 10.3 Appendix U Table U1: Region 0 (UK average), January. + # Arrange — SAP 10.2 Appendix U Table U1: Region 0 (UK average), January. # Act - result = external_temperature_c(region=0, month=1) + result = external_temperature_c(0, month=1) # Assert - assert result == pytest.approx(4.3, abs=0.05) + assert abs(result - 4.3) <= 0.05 def test_external_temperature_thames_july_returns_named_region_value() -> None: @@ -31,10 +31,10 @@ def test_external_temperature_thames_july_returns_named_region_value() -> None: # in summer — sanity check that named regions diverge from region 0. # Act - result = external_temperature_c(region=1, month=7) + result = external_temperature_c(1, month=7) # Assert - assert result == pytest.approx(17.9, abs=0.05) + assert abs(result - 17.9) <= 0.05 def test_wind_speed_uk_average_january_returns_table_u2_value() -> None: @@ -42,10 +42,10 @@ def test_wind_speed_uk_average_january_returns_table_u2_value() -> None: # SAP infiltration calc (worksheet lines 9-16). # Act - result = wind_speed_m_per_s(region=0, month=1) + result = wind_speed_m_per_s(0, month=1) # Assert - assert result == pytest.approx(5.1, abs=0.05) + assert abs(result - 5.1) <= 0.05 def test_horizontal_solar_irradiance_uk_average_july_returns_table_u3_value() -> None: @@ -53,10 +53,10 @@ def test_horizontal_solar_irradiance_uk_average_july_returns_table_u3_value() -> # for global horizontal irradiance in the UK. # Act - result = horizontal_solar_irradiance_w_per_m2(region=0, month=7) + result = horizontal_solar_irradiance_w_per_m2(0, month=7) # Assert - assert result == pytest.approx(189.0, abs=0.5) + assert abs(result - 189.0) <= 0.5 def test_horizontal_solar_irradiance_southern_england_brighter_than_shetland() -> None: @@ -64,12 +64,12 @@ def test_horizontal_solar_irradiance_southern_england_brighter_than_shetland() - # Jun -> 190. Higher-latitude regions get less June irradiance. # Act - south = horizontal_solar_irradiance_w_per_m2(region=3, month=6) - shetland = horizontal_solar_irradiance_w_per_m2(region=20, month=6) + south = horizontal_solar_irradiance_w_per_m2(3, month=6) + shetland = horizontal_solar_irradiance_w_per_m2(20, month=6) # Assert - assert south == pytest.approx(235.0, abs=0.5) - assert shetland == pytest.approx(190.0, abs=0.5) + assert abs(south - 235.0) <= 0.5 + assert abs(shetland - 190.0) <= 0.5 assert south > shetland @@ -81,7 +81,7 @@ def test_solar_declination_winter_solstice_returns_table_u3_value() -> None: result = solar_declination_deg(month=12) # Assert - assert result == pytest.approx(-23.0, abs=0.05) + assert abs(result - -23.0) <= 0.05 def test_solar_declination_summer_solstice_positive_value() -> None: @@ -91,7 +91,7 @@ def test_solar_declination_summer_solstice_positive_value() -> None: result = solar_declination_deg(month=6) # Assert - assert result == pytest.approx(23.1, abs=0.05) + assert abs(result - 23.1) <= 0.05 def test_external_temperature_out_of_range_region_raises_value_error() -> None: @@ -101,9 +101,9 @@ def test_external_temperature_out_of_range_region_raises_value_error() -> None: # Act / Assert with pytest.raises(ValueError, match="region"): - external_temperature_c(region=22, month=1) + external_temperature_c(22, month=1) with pytest.raises(ValueError, match="region"): - external_temperature_c(region=-1, month=1) + external_temperature_c(-1, month=1) def test_region_21_northern_ireland_returns_table_u1_value() -> None: @@ -112,10 +112,10 @@ def test_region_21_northern_ireland_returns_table_u1_value() -> None: # Table U1 row 21 July -> 15.0 °C. # Act - result = external_temperature_c(region=21, month=7) + result = external_temperature_c(21, month=7) # Assert - assert result == pytest.approx(15.0, abs=0.05) + assert abs(result - 15.0) <= 0.05 def test_out_of_range_month_raises_value_error_on_every_lookup() -> None: @@ -124,11 +124,11 @@ def test_out_of_range_month_raises_value_error_on_every_lookup() -> None: # Act / Assert with pytest.raises(ValueError, match="month"): - external_temperature_c(region=0, month=0) + external_temperature_c(0, month=0) with pytest.raises(ValueError, match="month"): - wind_speed_m_per_s(region=0, month=13) + wind_speed_m_per_s(0, month=13) with pytest.raises(ValueError, match="month"): - horizontal_solar_irradiance_w_per_m2(region=0, month=0) + horizontal_solar_irradiance_w_per_m2(0, month=0) with pytest.raises(ValueError, match="month"): solar_declination_deg(month=13) @@ -139,10 +139,10 @@ def test_wind_speed_shetland_january_higher_than_thames() -> None: # populated for the upper region indices, not silently aliasing to row 0. # Act - shetland = wind_speed_m_per_s(region=20, month=1) - thames = wind_speed_m_per_s(region=1, month=1) + shetland = wind_speed_m_per_s(20, month=1) + thames = wind_speed_m_per_s(1, month=1) # Assert - assert shetland == pytest.approx(9.5, abs=0.05) - assert thames == pytest.approx(4.2, abs=0.05) + assert abs(shetland - 9.5) <= 0.05 + assert abs(thames - 4.2) <= 0.05 assert shetland > thames diff --git a/domain/sap10_calculator/tests/test_table_32.py b/domain/sap10_calculator/tests/test_table_32.py index 6569cb98..b297a78d 100644 --- a/domain/sap10_calculator/tests/test_table_32.py +++ b/domain/sap10_calculator/tests/test_table_32.py @@ -31,10 +31,16 @@ from domain.sap10_calculator.tables.table_32 import ( (5, 12.19, "bottled LPG (secondary)"), (9, 3.48, "LPG subject to Special Condition 11F"), (7, 7.60, "biogas (including anaerobic digestion)"), - # Liquid fuels - (4, 7.64, "heating oil"), + # Liquid fuels. Heating oil (4) and FAME (73) deliberately diverge + # from the RdSAP 10 PDF p.95 (which lists 7.64 / 5.44) — the table + # uses the operationally-canonical Elmhurst-worksheet values per + # Slice S0380.131 (oil 7.64→5.44, two independent lodging engines + # agree) and Slice S0380.168 (FAME 5.44→7.64, oil 3/4 worksheets). + # See tables/table_32.py codes 4 / 73 + project-oil-price-spec- + # divergence. + (4, 5.44, "heating oil (worksheet-canonical, S0380.131)"), (71, 7.64, "bio-liquid HVO"), - (73, 5.44, "bio-liquid FAME"), + (73, 7.64, "bio-liquid FAME (worksheet-canonical, S0380.168)"), (75, 6.10, "B30K"), (76, 47.0, "bioethanol"), # Solid fuels @@ -80,10 +86,16 @@ from domain.sap10_calculator.tables.table_32 import ( def test_table_32_unit_prices_match_rdsap10_pdf_page_95( fuel_code: int, expected_p_per_kwh: float, fuel_name: str ) -> None: - """RdSAP10 Table 32 unit prices, sourced verbatim from PDF page 95. - These differ from SAP10.2 Table 12 by carrier (mains gas 3.64→3.48, - heating oil 4.94→7.64, std electricity 16.49→13.19, etc.) — see - `tables/table_32.py` docstring for the spec citation.""" + """RdSAP10 Table 32 unit prices, sourced from PDF page 95. These + differ from SAP10.2 Table 12 by carrier (mains gas 3.64→3.48, std + electricity 16.49→13.19, etc.) — see `tables/table_32.py` docstring + for the spec citation. + + Two codes deliberately diverge from the PDF and use the Elmhurst- + worksheet-canonical price instead (the PDF row is the outlier): + heating oil (4) = 5.44 not 7.64 (Slice S0380.131), bio-liquid FAME + (73) = 7.64 not 5.44 (Slice S0380.168). See project-oil-price-spec- + divergence.""" # Arrange # Act actual = unit_price_p_per_kwh(fuel_code)