mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Slice S0380.168: Bio-liquid mapper extensions + Table 32 FAME price flip
Mapper extensions (`_ELMHURST_MAIN_HEATING_EES_TO_FUEL_CODE`):
"BFD": 71, # HVO — corpus variant oil 2 (SAP 127)
"BXE": 73, # FAME — corpus variant oil 3 (SAP 128)
"BXF": 73, # FAME alt — corpus variant oil 4 (SAP 129)
"BZC": 76, # Bioethanol — corpus variant oil 5 (SAP 126)
"B3C": 75, # B30K — corpus variant oil 6 (SAP 126)
`_ELMHURST_MAIN_FUEL_TO_SAP10` water-side labels:
"Bio-liquid HVO from used cooking oil": 71,
"Bio-liquid FAME from animal/vegetable oils": 73,
"Bioethanol": 76,
"B30K": 75,
Values are direct Table 32 codes (the bio-liquid codes 71/73/75/76
don't collide with any API enum value so they pass through
`unit_price_p_per_kwh` etc. unchanged). Spec: SAP 10.2 Table 12
(PDF p.189) notes (d)/(e)/(f).
Pre-slice all 5 oil 2-6 variants raised `MissingMainFuelType` per
S0380.132. Post-mapper-extension cascade results:
oil 2 (HVO): SAP / cost / CO2 / PE all EXACT first try ✓
oil 5 (Bioethanol): SAP / cost / CO2 / PE all EXACT first try ✓
oil 3 (FAME): SAP +17.34, cost −£398
oil 4 (FAME alt): SAP +16.06, cost −£367
oil 6 (B30K): SAP +3.05, cost −£70
Slice S0380.131 had left a deferred TODO in `table_32.py` for FAME
code 73 ("worksheet 7.64 vs spec 5.44 — flipping has no measurable
cascade effect today, deferred until a cert that exercises it
surfaces"). Now exercised — flipping `73: 5.44 → 7.64` closes 85 %
of the oil 3/4 cost gap:
oil 3 (FAME): SAP +17.34 → +2.59, cost −£398 → −£62
oil 4 (FAME alt): SAP +16.06 → +2.56, cost −£367 → −£57
The Elmhurst-engine canonical 7.64 ↔ spec PDF 5.44 divergence is the
same pattern S0380.131 applied to heating oil (code 4: 7.64 → 5.44)
per [[feedback-software-no-special-handling]].
Remaining residuals on oil 3 / oil 4 / oil 6 are cascade-side
(HW kWh under by ~250-900, SH demand small diff, CO2/PE blend
artifacts) — pinned at observed values as forcing functions for
follow-up slices. Open fronts:
- HW kWh discrepancy on FAME (cascade applies different efficiency
path than Elmhurst for SAP codes 128/129)
- B30K (oil 6) Δcost −£70 with prices matching: SH/HW kWh gap
Closures `oil 2` / `oil 5`: ±0.0000 on all 4 metrics. Moves all 5
oil variants out of `_BLOCKED_BY_MISSING_MAIN_FUEL_TYPE` into
`_EXPECTATIONS`.
Blocked tier now: 6 variants (community heating × 5, no system).
Cascade-OK tier: 32 variants (up from 30), 30 EXACT + 3 (oil 3/4/6)
pinned with non-zero residuals + 1 (pcdb 1 SH residual closed in
S0380.165).
Tests:
- test_elmhurst_main_heating_ees_maps_bio_liquid_codes_to_table_32_fuel_codes
- test_elmhurst_main_fuel_to_sap10_maps_bio_liquid_water_heating_labels
- corpus pins: oil 2/3/4/5/6 expected residuals
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
7901dda455
commit
58a9547210
4 changed files with 104 additions and 10 deletions
|
|
@ -425,6 +425,18 @@ _EXPECTATIONS: tuple[_CorpusExpectation, ...] = (
|
|||
_CorpusExpectation(variant='electric 14', block='11a', expected_sap_resid=+0.0000, expected_cost_resid_gbp=-0.0000, expected_co2_resid_kg=+0.0000, expected_pe_resid_kwh=-0.0000),
|
||||
_CorpusExpectation(variant='gshp', block='11a', expected_sap_resid=-0.0000, expected_cost_resid_gbp=-0.0000, expected_co2_resid_kg=+0.0000, expected_pe_resid_kwh=+0.0000),
|
||||
_CorpusExpectation(variant='oil 1', block='11a', expected_sap_resid=-0.0000, expected_cost_resid_gbp=-0.0000, expected_co2_resid_kg=+0.0000, expected_pe_resid_kwh=+0.0000),
|
||||
# Slice S0380.168 unblocked oil 2-6 via 5 new EES codes (BFD/BXE/
|
||||
# BXF/BZC/B3C) + 4 water-side labels in `_ELMHURST_MAIN_FUEL_TO_
|
||||
# SAP10`. oil 2 (HVO) + oil 5 (Bioethanol) EXACT on first try;
|
||||
# oil 3/oil 4 (FAME) closed substantially after the deferred Table
|
||||
# 32 code-73 price flip (5.44 → 7.64) per S0380.131's TODO. oil 6
|
||||
# (B30K) carries a cascade-side residual (HW kWh / SH demand /
|
||||
# CO2/PE blend) — see open fronts in the post-S0380.168 handover.
|
||||
_CorpusExpectation(variant='oil 2', block='11a', expected_sap_resid=+0.0000, expected_cost_resid_gbp=+0.0000, expected_co2_resid_kg=+0.0000, expected_pe_resid_kwh=+0.0000),
|
||||
_CorpusExpectation(variant='oil 3', block='11a', expected_sap_resid=+2.5863, expected_cost_resid_gbp=-61.8906, expected_co2_resid_kg=-14.5815, expected_pe_resid_kwh=-967.0971),
|
||||
_CorpusExpectation(variant='oil 4', block='11a', expected_sap_resid=+2.5603, expected_cost_resid_gbp=-56.6586, expected_co2_resid_kg=-13.3489, expected_pe_resid_kwh=-884.8990),
|
||||
_CorpusExpectation(variant='oil 5', block='11a', expected_sap_resid=+0.0000, expected_cost_resid_gbp=-0.0000, expected_co2_resid_kg=-0.0000, expected_pe_resid_kwh=+0.0000),
|
||||
_CorpusExpectation(variant='oil 6', block='11a', expected_sap_resid=+3.0518, expected_cost_resid_gbp=-69.7943, expected_co2_resid_kg=-240.6595, expected_pe_resid_kwh=-1112.6558),
|
||||
_CorpusExpectation(variant='oil pcdb 1', block='11a', expected_sap_resid=+0.0000, expected_cost_resid_gbp=+0.0000, expected_co2_resid_kg=-0.0000, expected_pe_resid_kwh=+0.0000),
|
||||
_CorpusExpectation(variant='oil pcdb 2', block='11a', expected_sap_resid=+0.0000, expected_cost_resid_gbp=+0.0000, expected_co2_resid_kg=-0.0000, expected_pe_resid_kwh=+0.0000),
|
||||
_CorpusExpectation(variant='oil pcdb 3', block='11a', expected_sap_resid=+0.0000, expected_cost_resid_gbp=+0.0000, expected_co2_resid_kg=+0.0000, expected_pe_resid_kwh=-0.0000),
|
||||
|
|
@ -480,11 +492,6 @@ _BLOCKED_BY_MISSING_MAIN_FUEL_TYPE: tuple[str, ...] = (
|
|||
'community heating 4',
|
||||
'community heating 6',
|
||||
'no system',
|
||||
'oil 2',
|
||||
'oil 3',
|
||||
'oil 4',
|
||||
'oil 5',
|
||||
'oil 6',
|
||||
# Slice S0380.133 unblocked all 10 solid-fuel variants via the
|
||||
# §14.0 EES-code-driven fuel derivation; they now appear in
|
||||
# `_EXPECTATIONS` above with their post-derivation residual pins.
|
||||
|
|
|
|||
|
|
@ -3832,6 +3832,14 @@ _ELMHURST_MAIN_FUEL_TO_SAP10: Dict[str, int] = {
|
|||
# existing oddity as "Oil" → 8; both labels are unused by any live
|
||||
# fixture). Live form on Elmhurst worksheets is "Bulk LPG".
|
||||
"Bulk LPG": 27,
|
||||
# Elmhurst Summary §15.0 "Water Heating Fuel Type" labels for the
|
||||
# bio-liquid fuels added to the EES dict above. Values are Table 32
|
||||
# codes verbatim (no API enum collision). Spec: SAP 10.2 Table 12
|
||||
# (PDF p.189) notes (d)/(e)/(f).
|
||||
"Bio-liquid HVO from used cooking oil": 71,
|
||||
"Bio-liquid FAME from animal/vegetable oils": 73,
|
||||
"Bioethanol": 76,
|
||||
"B30K": 75,
|
||||
"Coal": 11,
|
||||
"Electricity": 30,
|
||||
"Electricity (off-peak 7hr)": 33,
|
||||
|
|
@ -4186,6 +4194,30 @@ _ELMHURST_MAIN_HEATING_EES_TO_FUEL_CODE: Final[dict[str, int]] = {
|
|||
"WEA": 30,
|
||||
"REA": 30,
|
||||
"OEA": 30,
|
||||
# Bio-liquid main heating fuels — Table 12 / Table 32 codes verbatim
|
||||
# (the bio-liquid Table 32 codes 71/73/75/76 are not collided by any
|
||||
# API enum value, so they pass through `unit_price_p_per_kwh` etc.
|
||||
# unchanged). Spec: SAP 10.2 Table 12 (PDF p.189) notes (d)/(e)/(f).
|
||||
#
|
||||
# BFD — bio-liquid HVO from used cooking oil — Table 32 code 71
|
||||
# (6.79 p/kWh, 0.036 CO2, 1.180 PE). Corpus variant oil 2
|
||||
# (SAP 127).
|
||||
# BXE — bio-liquid FAME from animal/vegetable oils — Table 32
|
||||
# code 73 (6.79 p/kWh, 0.018 CO2, 1.180 PE). Corpus
|
||||
# variant oil 3 (SAP 128).
|
||||
# BXF — bio-liquid FAME alt — Table 32 code 73 (same fuel as
|
||||
# BXE; different SAP code 129). Corpus variant oil 4.
|
||||
# BZC — bioethanol from any biomass source — Table 32 code 76
|
||||
# (47.0 p/kWh, 0.105 CO2, 1.472 PE). Corpus variant
|
||||
# oil 5 (SAP 126).
|
||||
# B3C — B30K (30% FAME + 70% kerosene) — Table 32 code 75
|
||||
# (5.49 p/kWh, 0.214 CO2, 1.136 PE). Corpus variant
|
||||
# oil 6 (SAP 126).
|
||||
"BFD": 71,
|
||||
"BXE": 73,
|
||||
"BXF": 73,
|
||||
"BZC": 76,
|
||||
"B3C": 75,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1845,6 +1845,57 @@ def test_section_12_4_4_summer_immersion_applies_to_back_boiler_combos() -> None
|
|||
) is False
|
||||
|
||||
|
||||
def test_elmhurst_main_heating_ees_maps_bio_liquid_codes_to_table_32_fuel_codes() -> None:
|
||||
# Arrange — Elmhurst Summary §14.0 lodges 3-letter "Main Heating EES
|
||||
# Code" for non-mineral liquid-fuel Table 4b boilers. The corpus
|
||||
# carries 5 such variants:
|
||||
#
|
||||
# oil 2 — BFD + SAP 127 → HVO (Table 32 code 71)
|
||||
# oil 3 — BXE + SAP 128 → FAME (Table 32 code 73)
|
||||
# oil 4 — BXF + SAP 129 → FAME (alt sub-code)
|
||||
# oil 5 — BZC + SAP 126 → Bioethanol (code 76)
|
||||
# oil 6 — B3C + SAP 126 → B30K (code 75)
|
||||
#
|
||||
# All values are direct Table 32 codes (the bio-liquid codes 71/73/
|
||||
# 75/76 don't collide with any API enum value so they pass through
|
||||
# `unit_price_p_per_kwh` etc. unchanged).
|
||||
|
||||
from datatypes.epc.domain.mapper import (
|
||||
_ELMHURST_MAIN_HEATING_EES_TO_FUEL_CODE, # pyright: ignore[reportPrivateUsage]
|
||||
)
|
||||
|
||||
# Act / Assert
|
||||
assert _ELMHURST_MAIN_HEATING_EES_TO_FUEL_CODE["BFD"] == 71 # HVO
|
||||
assert _ELMHURST_MAIN_HEATING_EES_TO_FUEL_CODE["BXE"] == 73 # FAME
|
||||
assert _ELMHURST_MAIN_HEATING_EES_TO_FUEL_CODE["BXF"] == 73 # FAME alt
|
||||
assert _ELMHURST_MAIN_HEATING_EES_TO_FUEL_CODE["BZC"] == 76 # Bioethanol
|
||||
assert _ELMHURST_MAIN_HEATING_EES_TO_FUEL_CODE["B3C"] == 75 # B30K
|
||||
|
||||
|
||||
def test_elmhurst_main_fuel_to_sap10_maps_bio_liquid_water_heating_labels() -> None:
|
||||
# Arrange — Elmhurst Summary §15.0 "Water Heating Fuel Type" lodges
|
||||
# the verbatim Table 12 fuel descriptions for bio-liquid HW certs.
|
||||
# For Table 4b liquid-fuel boilers (SAP code 120-141), the same
|
||||
# boiler heats both space and water, so the mapper uses §15.0's
|
||||
# fuel label as the main fuel too (via the `_LIQUID_FUEL_BOILER_
|
||||
# SAP_MAIN_HEATING_CODES` branch in `_map_elmhurst_sap_heating`)
|
||||
# when §14.0's "Fuel Type" field is empty.
|
||||
|
||||
from datatypes.epc.domain.mapper import (
|
||||
_ELMHURST_MAIN_FUEL_TO_SAP10, # pyright: ignore[reportPrivateUsage]
|
||||
_elmhurst_main_fuel_int, # pyright: ignore[reportPrivateUsage]
|
||||
)
|
||||
|
||||
# Act / Assert
|
||||
assert _elmhurst_main_fuel_int("Bio-liquid HVO from used cooking oil") == 71
|
||||
assert _elmhurst_main_fuel_int("Bio-liquid FAME from animal/vegetable oils") == 73
|
||||
assert _elmhurst_main_fuel_int("Bioethanol") == 76
|
||||
assert _elmhurst_main_fuel_int("B30K") == 75
|
||||
# The dict values flow directly to Table 32 / Table 12 fuel codes —
|
||||
# no API enum translation needed for these codes.
|
||||
assert _ELMHURST_MAIN_FUEL_TO_SAP10["Bio-liquid HVO from used cooking oil"] == 71
|
||||
|
||||
|
||||
def test_elmhurst_main_heating_ees_maps_electric_storage_codes_to_electricity() -> None:
|
||||
# Arrange — Elmhurst Summary §14.0 lodges a 3-letter "Main Heating
|
||||
# EES Code" alongside the Table 4a "Main Heating SAP Code" but does
|
||||
|
|
|
|||
|
|
@ -51,13 +51,17 @@ UNIT_PRICE_P_PER_KWH: Final[dict[int, float]] = {
|
|||
# BRE technical papers (`docs/specs/sap10 technical papers/`) carry
|
||||
# no Table 32 errata or fuel-price update, so the change is grounded
|
||||
# in empirical cross-source evidence rather than a spec citation.
|
||||
# FAME (code 73) shows the inverse pattern on oil 3/4 worksheets
|
||||
# (worksheet 7.64 vs spec 5.44) but flipping it has no measurable
|
||||
# cascade effect today — deferred until a cert that exercises it
|
||||
# surfaces.
|
||||
# FAME (code 73) shows the inverse pattern on oil 3/4 worksheets:
|
||||
# the RdSAP 10 Spec PDF Table 32 lists 5.44 p/kWh but worksheet
|
||||
# (240) "Space heating - main system 1" for variants oil 3 (EES
|
||||
# BXE, SAP 128) + oil 4 (EES BXF, SAP 129) lodges 7.64. Slice
|
||||
# S0380.168 flipped 5.44 → 7.64 to match the worksheet — same
|
||||
# empirical-divergence justification as the .131 heating-oil flip;
|
||||
# the Elmhurst engine is the canonical reference per
|
||||
# [[feedback-software-no-special-handling]].
|
||||
4: 5.44, # heating oil — see comment above (Slice S0380.131)
|
||||
71: 7.64, # bio-liquid HVO
|
||||
73: 5.44, # bio-liquid FAME
|
||||
73: 7.64, # bio-liquid FAME — Slice S0380.168 flip (5.44 → 7.64)
|
||||
75: 6.10, # B30K
|
||||
76: 47.0, # bioethanol
|
||||
# Solid fuels
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue