feat(overlay): pin oil A/B bands from accredited PCDB (completes A/B matrix)

Oil A = (90.6, 82.8) [PCDB 17292 Firebird Enviromax Blue Supreme], B = (88.0, 80.2)
[PCDB 10498 Worcester Greenstar Danesmoor], applied to oil regular (127) + combi
(130). No separate oil Elmhurst build: the gas builds proved Elmhurst's worksheet
(206)/(217) for a database boiler == its PCDB winter/summer read verbatim (SAP 10.2
App D2.1), so the PCDB record is the accredited value. Corpus unmoved; 68 overlay
tests green (ADR-0068). Full A/B matrix (gas/LPG/biogas + oil) now live.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-07-29 19:07:57 +00:00
parent 063eb17a89
commit 9760b38238
2 changed files with 23 additions and 17 deletions

View file

@ -297,14 +297,23 @@ _BAND_TO_TABLE_4B_CODE: dict[tuple[int, BoilerEfficiencyBand], int] = {
# to both gas regular (102) and combi (104). (Enabling A moves ~14.6k A-rated homes
# up ~+13 SAP — flagged to stakeholders; some cross a band → Recs/eligibility.)
#
# OIL (124132): still pending — no oil slot yet, so an oil A/B boiler keeps its
# condensing code default (127/130). Oil A/B is a tiny population; a follow-up can
# pin it from an oil PCDB product or an oil Elmhurst build. No invented numbers ship.
# OIL (124132): pinned from the accredited PCDB records we hold, applied to oil
# regular (127) and combi (130):
# A → PCDB 17292 (Firebird Enviromax Blue Supreme): (90.6, 82.8)
# B → PCDB 10498 (Worcester Greenstar Danesmoor): (88.0, 80.2)
# No separate Elmhurst build was needed: the gas builds above proved Elmhurst's
# worksheet (206)/(217) for a database boiler are its PCDB winter/summer read back
# verbatim (18790 → 90.6/81.6, 15029 → 88.0/79.4, both exact), per SAP 10.2 App
# D2.1 (PCDB overrides Table 4b). So the PCDB record IS the accredited value.
_BAND_SLOT_EFFICIENCY_PCT: dict[tuple[int, BoilerEfficiencyBand], tuple[float, float]] = {
(102, BoilerEfficiencyBand.A): (90.6, 81.6),
(104, BoilerEfficiencyBand.A): (90.6, 81.6),
(102, BoilerEfficiencyBand.B): (88.0, 79.4),
(104, BoilerEfficiencyBand.B): (88.0, 79.4),
(127, BoilerEfficiencyBand.A): (90.6, 82.8),
(130, BoilerEfficiencyBand.A): (90.6, 82.8),
(127, BoilerEfficiencyBand.B): (88.0, 80.2),
(130, BoilerEfficiencyBand.B): (88.0, 80.2),
}

View file

@ -49,29 +49,27 @@ def test_c_and_below_bands_resolve_to_the_table_4b_pair(
# (Worcester Greenstar 8000 Life) 90.6/81.6; B → PCDB 15029 (Baxi Duo-tec
# Combi 24 HE) 88.0/79.4. Gas/LPG/biogas share codes — one pair per band on
# both regular (102) and combi (104).
# Gas A/B — validated on accredited Elmhurst (PCDB 18790 / 15029).
(102, BoilerEfficiencyBand.A, (90.6, 81.6)),
(104, BoilerEfficiencyBand.A, (90.6, 81.6)),
(102, BoilerEfficiencyBand.B, (88.0, 79.4)),
(104, BoilerEfficiencyBand.B, (88.0, 79.4)),
# Oil A/B — pinned from the accredited PCDB records (17292 / 10498). The
# gas builds proved Elmhurst's (206)/(217) == the PCDB winter/summer, so
# the PCDB record is the accredited value (no separate oil build needed).
(127, BoilerEfficiencyBand.A, (90.6, 82.8)),
(130, BoilerEfficiencyBand.A, (90.6, 82.8)),
(127, BoilerEfficiencyBand.B, (88.0, 80.2)),
(130, BoilerEfficiencyBand.B, (88.0, 80.2)),
],
)
def test_gas_a_and_b_bands_use_the_elmhurst_validated_slot(
def test_a_and_b_bands_use_the_accredited_slot(
base_code: int, band: BoilerEfficiencyBand, expected: tuple[float, float]
) -> None:
# These exceed Table 4b's 84% ceiling (the A under-credit the fix removes).
# A/B exceed Table 4b's 84% ceiling (the under-credit the fix removes).
assert band_seasonal_efficiency_pct(base_code, band) == expected
@pytest.mark.parametrize("band", [BoilerEfficiencyBand.A, BoilerEfficiencyBand.B])
@pytest.mark.parametrize("base_code", [127, 130])
def test_oil_a_and_b_bands_are_pending_validation(
base_code: int, band: BoilerEfficiencyBand
) -> None:
# Oil A/B not yet validated — no slot, so an oil A/B boiler keeps its
# condensing code default (127/130). No invented numbers ship (ADR-0068).
assert band_seasonal_efficiency_pct(base_code, band) is None
@pytest.mark.parametrize(
"base_code",
[
@ -118,10 +116,9 @@ def test_overlay_sets_the_band_efficiency_slot_on_gas_and_oil_boilers(
[
# No band → no slot (today's behaviour: keep the condensing code default).
("Gas boiler, combi", None),
# Oil A/B still pending Elmhurst → no slot yet.
("Oil boiler, combi", BoilerEfficiencyBand.A),
# Non-banded boilers ignore the band letter entirely.
("Electric boiler", BoilerEfficiencyBand.D),
("Electric boiler", BoilerEfficiencyBand.A),
("Solid fuel boiler", BoilerEfficiencyBand.G),
("Gas CPSU", BoilerEfficiencyBand.D),
],