docs: handover — cert 0380 HW cascade (slices 102a-e shipped, MIT residual deferred to next session)

This commit is contained in:
Khalim Conn-Kowlessar 2026-05-27 12:42:44 +00:00 committed by Jun-te Kim
parent 2e5c519861
commit ebb492c5d3

View file

@ -0,0 +1,150 @@
# Handover — cert 0380 HP HW cascade (slices 102a-e shipped, MIT residual + 6 cohort ASHPs to go)
Branch `feature/per-cert-mapper-validation`. Picks up from the previous
handover at [`HANDOVER_CERT_9501_AND_HEATPUMPS.md`](HANDOVER_CERT_9501_AND_HEATPUMPS.md)
after a `/grill-me``/tdd` session shipped 7 slices closing the HW HP
cascade for cert 0380 to **+0.60 SAP delta vs worksheet 88.5104**
(down from +2.92 at session start). The PCDB Table 362 typed parser
is now in place so the remaining 6 ASHP certs can close in 1-2 slices
each once the MIT residual is fixed.
## What landed this session (commits on branch)
| Slice | Commit | What it did |
|---|---|---|
| **102a** | 4d3a0e95 | SAP 10.2 §4 line 7702 — gate Table 3a combi-loss default on `main_heating_category ∈ {1,2,3,6}` so HP certs (cat 4) zero (61)m. Closed combi certs unaffected. |
| **102b** | 76fdab42 | SAP 10.2 §4 line 7690 + Tables 2/2a/2b — `cylinder_storage_loss_monthly_kwh` helper + cert-side override resolves (56)m from cert cylinder fields. Cohort ground-truth: `cylinder_size` code 3→160L (Medium), code 4→210L (Large); `cylinder_insulation_type` code 1 → "factory_insulated". |
| **102c.1** | 70aa709c | Typed `HeatPumpRecord` parser for PCDB Table 362 format 465 header (vessel mode, volume, heat loss, HX area, max output). Field offsets reverse-engineered against BRE web entry for record 104568. |
| **102c.2** | 5b78a1e2 | Format-465 PSR-group decoding (14 groups × 9 fields each; offsets 0/2/6 = PSR / η_space,1 / η_water,3). `interpolate_heat_pump_efficiency_at_psr` per spec PDF p.100 line 5957, with min/max clamping per p.101 lines 6007-6008. |
| **102d** | c4a1045c | SAP 10.2 §4 line 7700 + Table 3 — `primary_loss_monthly_kwh` helper + PCDB-aware vessel gate (HPs with `hw_vessel_mode != 1` apply primary loss). RdSAP §3 age-band default for pipework insulation (A-J → p=0.0, K-M → p=1.0). |
| **102e** | 7a8c8fac | SAP 10.2 Appendix N3.6 + N3.7(a) — heat-pump APM efficiencies. PSR formula `max_output / (HLC × 24.2 K)`, N3.6 0.95 in-use factor for space, N3.7 in-use factor (0.95 or 0.60) for water. The 0.60 branch always fires for Open EPC API certs (HX area never lodged → criterion unknown → 0.60). |
Plus pre-implementation ground-truth: API JSON fetched for all 6
remaining ASHP cohort certs; `cylinder_size` and
`cylinder_insulation_type` codes confirmed across the cohort.
## Cumulative state at session end
Cert 0380 (Mitsubishi ASHP PCDB 104568, semi-detached bungalow,
age D, TFA 60.43 m²):
| Metric | Cascade | Worksheet target | Δ |
|---|---|---|---|
| (37) total fabric heat loss W/K | 96.0889 | 96.0889 | **exact** (from prior session 101a-c) |
| (62) annual demand kWh/yr | 1502.16 | 1502.16 | **exact at 1e-4** ✓ |
| (56)m Jan storage loss kWh/month | 36.9530 | 36.9530 | **exact** ✓ |
| (59)m Jan primary loss kWh/month | 43.3132 | 43.3132 | **exact** ✓ |
| main_heating_efficiency (COP_space) | 2.2348 | 2.2305 | +0.0043 (0.2%) |
| HW kWh/yr | 878.05 | 877.97 | +0.08 |
| **SAP continuous** | **89.11** | **88.51** | **+0.60** |
## Remaining +0.60 SAP residual — root cause: MIT 0.42°C drift
The cascade computes **mean internal temperature annual avg = 18.94°C**
vs the worksheet's **19.36°C** (worksheet line 933 MIT monthly avg
~19.24/18.45/.../18.57 → annual avg 19.36). The 0.42°C lower MIT
reduces useful space heating by ~163 kWh/yr (cascade 5187.09 vs
worksheet 5349.73 — line (98c)).
Heat gains from water heating MATCH worksheet at 4 d.p. (cascade
(65)m Jan = 98.4586, worksheet 98.4586). HTC also matches at (39)
annual avg = 127.158 W/K. The drift is **inside the MIT cascade
itself** — likely the heating control type / responsiveness mapping
for HPs.
For cert 0380:
- `main_heating_control = 2206` (lodged)
- BRE convention: 2206 = "Programmer, TRVs and bypass" (SAP control type 2)
- HP main heating, weather compensation lodged as "No"
Investigation pointers:
- `_control_type(main)` and `_responsiveness(main)` in [cert_to_inputs.py](../rdsap/cert_to_inputs.py) — probably mapping HPs to a different control type or responsiveness than the worksheet expects.
- Worksheet line 333 (or thereabouts): `(93)m adjusted MIT` — cross-check what control type / Tdh / Th2 values are used.
- SAP 10.2 §7 Table N7 (PDF p.107) defines bimodal/unimodal heating temperatures per control type — HP certs may need a different row.
## Remaining slices (recommended next session)
### 1. Slice 102f-prep: MIT cascade drift fix (HIGH PRIORITY)
Drill into [`mean_internal_temperature_monthly`](../worksheet/mean_internal_temp.py) or its caller in cert_to_inputs.py. Suggested approach:
1. Pin cascade's MIT monthly tuple for cert 0380 against worksheet line 933 (12-tuple ranging 18.4520.18°C).
2. Probe `_control_type`, `_responsiveness`, and the `control_temperature_adjustment_c=0.0` arg — at least one of these is likely off for HP certs.
3. Inspect the cohort's other 6 ASHP certs to see if they share the drift.
Once MIT lands at 1e-4, slice 102f Layer 4 chain test should close at SAP 88.5104 ± 1e-4.
### 2. Slice 102f: Layer 4 chain test cert 0380 API
After MIT fix, add to [`backend/documents_parser/tests/test_summary_pdf_mapper_chain.py`](../../../backend/documents_parser/tests/test_summary_pdf_mapper_chain.py) alongside the closed-cert chain tests:
```python
def test_api_0380_full_chain_sap_matches_worksheet_pdf_exactly() -> None:
...
assert abs(result.sap_score_continuous - 88.5104) < 1e-4
```
### 3. Cohort closure: remaining 6 ASHP certs
After cert 0380 closes, re-probe each:
- **0350, 2225, 2636, 3800, 9285** — all PCDB 104568 (same as 0380), `cylinder_size=3` → 160L, `cylinder_insulation_type=1`. Should close in 1 slice each (Layer 4 chain test) once MIT fix lands.
- **9418** — PCDB **102421** (Daikin Altherma EDLQ05CAV3), `cylinder_size=4` → 210L. May need a small APM helper validation if the Daikin PSR groups have a different shape; otherwise close in 1 slice.
## Open items / known gaps
### Summary path (cert 0380)
Still catastrophic at Δ -58.37 SAP. The Elmhurst PDF extractor mis-identifies the HP. Deferred to a separate `documents_parser/` workstream per Q7 in this session's grilling. Don't tackle until API path lands at 1e-4 for all 7 ASHPs.
### Cylinder volume / insulation type mappings
Cohort coverage:
- `cylinder_size` codes 3 / 4 ground-truthed; codes 2 / 5 / 6 unknown.
- `cylinder_insulation_type` code 1 = factory-insulated ground-truthed; code 0 / 2 unknown.
These currently `return None` in the override resolver, falling through to the cascade's zero defaults. When a non-cohort cert exercises an unknown code, the cascade will silently apply zero loss — a known limitation.
### PSR formula 0.4% drift
Spec formula gives PSR = 1.4266 for cert 0380; worksheet implies 1.4321. The 0.4% drift propagates to η_space at ~0.2%, contributing maybe 0.04 SAP to the residual (small vs the MIT 0.60 dominant). Investigate as part of slice 102f-prep MIT work — they may share a root cause (e.g., a different (39) effective for design heat loss).
### Closed-cert regression
Cert 0390-2954 (oil boiler + cylinder, age band F → A-J p=0.0) now picks up SAP 10.2 Tables 2/2a/2b + Table 3 losses. Pin re-set during slice 102b (PE -28.68 → -27.50, CO2 -2.76 → -2.66) and slice 102d (PE -27.50 → -26.01, CO2 -2.66 → -2.52; SAP residual -6 → -7). Both directions are improvements (closer to lodged values).
## Test baselines you should see
```bash
PYTHONPATH=/workspaces/model python -m pytest \
backend/documents_parser/tests/test_summary_pdf_mapper_chain.py \
backend/documents_parser/tests/test_elmhurst_extractor.py \
backend/documents_parser/tests/test_elmhurst_end_to_end.py \
domain/sap10_calculator/worksheet/tests/test_e2e_elmhurst_sap_score.py \
domain/sap10_calculator/worksheet/tests/test_water_heating.py \
domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py \
domain/sap10_calculator/rdsap/tests/test_golden_fixtures.py \
domain/sap10_calculator/tests/test_pcdb_table_362_lookup.py \
domain/sap10_ml/tests/test_rdsap_uvalues.py \
datatypes/epc/schema/tests/test_schema_loading.py \
--no-cov -q
```
Expected: **624 pass + 9 pre-existing 001479 Layer 1 fails + 1 pre-existing FEE fail = 10 fails**. Three Layer 4 1e-4 production gates remain GREEN (closed certs 001479, 0330, 9501 on both Summary and API).
## Pyright baselines (unchanged net-zero)
- `datatypes/epc/domain/mapper.py`: 32
- `domain/sap10_calculator/worksheet/water_heating.py`: 1
- `domain/sap10_calculator/worksheet/heat_transmission.py`: 13
- `domain/sap10_calculator/rdsap/cert_to_inputs.py`: 35
- `domain/sap10_ml/rdsap_uvalues.py`: 1 (pre-existing)
- `datatypes/epc/domain/epc_property_data.py`: 1 (pre-existing)
## Conventions (preserved)
- One slice = one commit; stage by name.
- AAA test convention: literal `# Arrange / # Act / # Assert` headers.
- `abs(diff) <= tol` (NOT `pytest.approx` per [`feedback_abs_diff_over_pytest_approx`](../../../../home/vscode/.claude/projects/-workspaces-model/memory/MEMORY.md)).
- 1e-4 worksheet tolerance for end-state pins (Layer 4 chain tests);
intermediate slice tests may use 1e-2 to 1e-3 absorbing known drifts
documented in commit messages.
- Spec citation in commit messages (RdSAP 10 / SAP 10.2 page or line ref).
- Pyright net-zero per file.
Good luck closing the MIT residual and cert 0380 to 1e-4. The HW HP
cascade itself is now spec-faithful from (45)m through (217); the
final SAP-rating drift is a §7 MIT problem, not §4 HW.