diff --git a/domain/sap10_calculator/docs/HANDOVER_MAPPER_BUGS.md b/domain/sap10_calculator/docs/HANDOVER_MAPPER_BUGS.md index eb8a96a1..24d8a4d5 100644 --- a/domain/sap10_calculator/docs/HANDOVER_MAPPER_BUGS.md +++ b/domain/sap10_calculator/docs/HANDOVER_MAPPER_BUGS.md @@ -6,13 +6,13 @@ state after closing the 0240 investigation and fixing the first of several API-mapper/cascade bugs the audit surfaced. - **Branch:** `feature/per-cert-mapper-validation` -- **HEAD:** `90f6720c` (S0380.211). Confirm with `git rev-parse HEAD`. +- **HEAD:** `08dd0b4c` (S0380.212). Confirm with `git rev-parse HEAD`. - **Baseline:** `2384 passed, 1 skipped, 0 failed` (AGENT_GUIDE §4 suite command). ALSO run `domain/sap10_ml/tests/` when touching `rdsap_uvalues.py` — 2 PRE-EXISTING stone-formula failures there, see Thread 1. -- **Next slice number:** **S0380.212**. -- **Open:** Thread 2 (community 9390) — UNBLOCKED: **case 14** (code-301 boiler + mains - gas) is on disk, target `(386)` heat-network CO2 = 0.2640. Threads 1 + 3 **CLOSED**. +- **Next slice number:** **S0380.213**. +- **Open:** Thread 2 *cost* tail only — 9390 SAP +4 (heat-network cost missing + 1/heat_source_eff scaling). CO2/PE collision FIXED (S0380.212). Threads 1 + 3 **CLOSED**. --- @@ -24,6 +24,7 @@ API-mapper/cascade bugs the audit surfaced. | **S0380.209** | Fixed the **API-path wall U** "as built, insulated (assumed)" bug — routes to the as-built age-band row, not the 50 mm retrofit bucket. New `_described_as_retrofit_insulated` in `heat_transmission.py`. Worksheet-validated by case 9 (sandstone J → 0.35) + case 10 (solid brick J → 0.35). Re-pinned 0240 PE +1.8687 → +5.5044, CO2 +0.0907 → +0.2757 (SAP integer 72 unchanged). | | **S0380.210** | **CLOSED cert 0390** (Thread 3). Cavity wall "as built, **partial** insulation (assumed)" (type 4) was mis-routed to the Table 6 "Filled cavity" row (band F 0.40) → should be "Cavity as built" (band F 1.0). New `_cavity_described_as_filled` in `rdsap_uvalues.py` excludes "partial insulation" from the filled trigger (keeps "insulated (assumed)" → filled). SAP +7 → +0, PE −27.97 → +0.53, CO2 −2.71 → −0.12. Mirrors S0380.209 on the cavity path. | | **S0380.211** | **CLOSED Thread 1 (roof).** 0240 Ext1 vaulted (code 5) NI roof returned 0.68 (§5.11.4 50 mm) → should be Table 18 col (1) age-band (band J 0.16), matching 33 cohort-2 `ND` vaulted roofs. New `u_roof(is_sloping_ceiling=...)` flag threaded from heat_transmission (codes 5/8). 0240 PE +5.50 → +1.52, CO2 +0.28 → +0.07 (SAP 72). Also corrected the S0380.210 cavity unit test in `domain/sap10_ml/tests/` (suite-command gap — see Thread 1). | +| **S0380.212** | **Thread 2 CO2/PE collision FIXED.** EPC fuel 20 = "mains gas (community)" collided with Table-12 biomass code 20 → community CO2 6.4× low. New `_heat_network_factor_fuel_code` translates 20→51 for heat-network mains only (5 sites: SH+HW CO2/PE/price). 9390 CO2 0.44→3.03 t (lodged 2.8), PE 204→220. Case-14-validated ((367) 0.2100 / (467) 1.1300). Cost +4 tail open. | Both also carry a memory: [[project_case7_combi_exonerated]], [[project_as_built_insulated_assumed_bug]]. @@ -100,51 +101,36 @@ formula 3.7408 vs Table-6 1.7 cap, granite + sandstone band A) — they fail at --- -## OPEN THREAD 2 — Community fuel-code collision (cert 9390) — UNBLOCKED, **case 14 in hand** +## THREAD 2 — Community fuel-code collision (cert 9390) — **CO2/PE FIXED (S0380.212); cost +4 open** -Cert **9390-2722-3520** (TFA 75.17): SAP +4 (calc 71 / lodged 67), PE ≈ matches -(204 vs 205), but **CO2 5.86 kg/m² vs lodged 38** (0.44 t vs 2.8 t — 6.5× low). -Community scheme (`sap_main_heating_code=301`, `main_fuel_type=20`). +Cert **9390-2722-3520** (community mains-gas boiler, `sap_main_heating_code=301`, +`main_fuel_type=20`). Authoritative: `datatypes/epc/domain/epc_codes.csv` +(RdSAP-Schema-17.0) `main_fuel,20,mains gas (community)`. -**Arbitrating worksheet (case 14, `simulated case 14/`):** community **Boilers** -(`(303a)`=1.0, no CHP) + **Mains Gas** + **SAP code 301** — 9390's exact scheme. -Key targets: `(386)` **Overall heat-network CO2 factor = 0.2640**, `(306)` -distribution loss factor (Table 12c) = **1.49**, `(385)` EI rating 69. So a -community mains-gas BOILER scheme should drive CO2 at the heat-network factor -**0.2640** (≈ base mains-gas 0.210 × 1.257 source/loss scaling), NOT the biomass -0.028 the collision currently returns. **API `main_fuel_type=20` = community mains -gas → Table 12 code 51** (base CO2 0.210, PEF 1.130). Cases 12 (CHP coal, 302) + -13 (CHP gas, 302) also on disk — useful for the CHP `(303a/303b)` split but NOT -9390's boiler scheme. +**Root cause (the collision):** the EPC `main_fuel_type` enum and the SAP Table 12 / +Table 32 numbering overlap in **18–25** — EPC 20='mains gas (community)' but Table-12 +code 20 is solid biomass (CO2 0.028). `co2_factor_kg_per_kwh`/`primary_energy_factor`/ +`unit_price_p_per_kwh` check the Table-12 dict FIRST, so the EPC community fuel got the +biomass factor instead of translating 20→51 (community mains gas: CO2 0.210, PE 1.130). -**Root cause:** `co2_factor_kg_per_kwh` / `unit_price_p_per_kwh` / -`primary_energy_factor` in `tables/table_12.py` + `table_32.py` use an -"accept-either-API-or-Table-12-code" lookup that checks the **Table-12 code first**: +**S0380.212 fix:** new `_heat_network_factor_fuel_code(main)` translates the EPC community +fuel → Table-12 code via `API_FUEL_TO_TABLE_12`, but ONLY for heat-network mains +(`_is_heat_network_main`) so a genuine biomass boiler keeps its raw factor. Applied at +**five** sites — space-heating CO2/PE/unit-price + water-heating (WHC 901) CO2/PE (9390's +HW is ALSO community gas, so both paths needed it). Worksheet-validated by **case 14** +(community boilers + mains gas, code 301): `(367)` CO2 0.2100, `(467)` PE 1.1300 = the +Table-12 code-51 values. 9390 CO2 **0.44 → 3.03 t** (lodged 2.8 — spec-correct factor over +the API-only register residual; 9390 is unpinned, retired P2.2 per ADR-0010 §10), PE +**204 → 220** (the prior 204≈205 was the collision coinciding with the register residual). +Summary path uses code 1 (no collision) → CH1-6 corpus untouched. Locked by 2 unit tests +in `test_cert_to_inputs.py`. -```python -if fuel_code in CO2_KG_PER_KWH: # API 20 IS ALSO a T12 code (wood logs, 0.028) - return CO2_KG_PER_KWH[fuel_code] # ← returns 0.028, never translates -translated = API_FUEL_TO_TABLE_12.get(fuel_code) # 20 → 51 (community gas, 0.21) — unreached -``` - -API community fuels **18–25** (`API_FUEL_TO_TABLE_12`: 18→75,19→76,20→51,21→52,22→53, -23→55,24→54,25→41) all **collide** with Table-12 codes 18–25 (biomass), so they silently -get the biomass factor. The heat-network CO2 path -(`cert_to_inputs.py` ~L2801-2819: `_co2_factor_kg_per_kwh(main) * scaling`) thus emits -`0.028 × 1.25 (1/0.80 heat-source-eff) = 0.035` instead of `0.21 × 1.25`. - -**Why the fix needs care (don't just translate):** a naive translate of the heat-network -fuel via `API_FUEL_TO_TABLE_32` corrects CO2 (0.44 → 3.03 t, ≈ lodged 2.8) but -**over-corrects PE** (204 → 219.9 vs lodged 205) and **doesn't move the SAP +4** (cost -is a *separate* community issue — price/standing-charge/heat-source-eff scaling). The -community scheme's declared PE/CO2/cost factors must be pinned against a **9390 Elmhurst -worksheet** before committing. This touches the community-heating corpus (the "touchy" -S0380.180-184 area — see [[project_heating_systems_corpus]]); run the heating-systems -corpus test after any change. - -The right long-term shape: translate API→Table-12 **explicitly** at the known-API-code -boundary instead of the ambiguous T12-first "accept-either" — but verify it against the -whole cohort. +**STILL OPEN — 9390 SAP +4 (separate cost-scaling gap):** the heat-network cost path +(`_fuel_cost_gbp_per_kwh`, cert_to_inputs.py ~L1888) does NOT apply the +`1/heat_source_eff` (1/0.80) scaling the CO2/PE paths do, so community fuel cost +under-counts → SAP over-reads. Validate the fix against case 14's **10b fuel-cost block** +(+ Table 32 note (l) £120 community standing charge). Run the heating-systems corpus after +(touchy area, [[project_heating_systems_corpus]]). --- @@ -186,7 +172,7 @@ that test. | cert | SAP resid | diagnosis | |---|---|---| | 0390-2954-3640 | ~~+7~~ **+0** | **CLOSED S0380.210** — cavity partial-insulation → as-built row | -| 9390-2722-3520 | +4 | community fuel-code collision → CO2 6× low (Thread 2, OPEN) | +| 9390-2722-3520 | +4 (unpinned) | **CO2/PE collision FIXED S0380.212** (CO2 0.44→3.03 t); SAP +4 cost-scaling tail open | | 0240-0200-5706 | −1 | NOT a bug — unpreserved 2013+ pump; true SAP 72. Roof PE-pin tightened by **S0380.211** (PE +5.50 → +1.52) | | 2130-1033-4050 | +1 | minor fabric precision (multi-part solid-brick wall); low value | | 7536-3827-0600 | +1 | minor fabric precision (multi-bp D/L/F cavity); low value |