Fix 11 pre-existing test failures from the absorbed PR

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 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-06-02 15:09:59 +00:00 committed by Jun-te Kim
parent beae12aee8
commit a5d889e143
2 changed files with 47 additions and 35 deletions

View file

@ -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

View file

@ -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.643.48,
heating oil 4.947.64, std electricity 16.4913.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.643.48, std
electricity 16.4913.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)