mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
docs: handover post S0380.200 — 6035+0240 closed; boiler-interlock −5pp OPEN
Captures the session's window/RR/dual-main work (S0380.196–200) and the
open priority: a spec-accurate per-system boiler-interlock −5pp (Table
4c(2)) adjustment. Root cause for case 6's remaining deltas (sys-1 eff 79
not 84 + HW 4824 vs 4902) is the "room thermostat present but no cylinder
thermostat → no interlock" path that the current {2101,2102} no-interlock
rule misses. 0240 shares the controls + cylinder_thermostat=N so it will
re-pin (apply spec uniformly). Secondary: dual-system Table 4f pumps.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
8ae978a646
commit
558aaf6d09
1 changed files with 154 additions and 0 deletions
154
domain/sap10_calculator/docs/HANDOVER_POST_S0380_200.md
Normal file
154
domain/sap10_calculator/docs/HANDOVER_POST_S0380_200.md
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
# Handover — post S0380.200 (dual-main split done; boiler-interlock −5pp OPEN)
|
||||
|
||||
Point-in-time note. Start from [`AGENT_GUIDE.md`](AGENT_GUIDE.md) for methodology,
|
||||
accuracy bar, and pipeline — this records *what this session did* and *what is open*.
|
||||
|
||||
- **Branch:** `feature/per-cert-mapper-validation`
|
||||
- **HEAD:** `8ae978a6` (S0380.200)
|
||||
- **Baseline:** `2355 passed, 1 skipped, 0 failed`. Verify with the AGENT_GUIDE §4 suite command.
|
||||
|
||||
---
|
||||
|
||||
## What this session shipped (S0380.196–200)
|
||||
|
||||
The through-line: **golden certs 6035 and 0240 were both closed to SAP-exact**
|
||||
by finding real API-mapper bugs (not "lodged divergence"), each confirmed
|
||||
against a user-generated Elmhurst worksheet ("simulated case 5/6").
|
||||
|
||||
| Slice | What | Spec |
|
||||
|---|---|---|
|
||||
| **.196** | API Simplified Type 1 room-in-roof: `room_in_roof_type_1` gables (length-only, no height) weren't deducted from the A_RR shell → whole shell billed as roof at U_RR=2.30 (+52 W/K). Route them through `detailed_surfaces` (gable area = L × 2.45 default RR storey height). **6035 SAP −2→+0 exact**, PE +19.16→+1.84. | RdSAP 10 §3.9.1(e) p.21; Table 4 p.22 |
|
||||
| **.197** | Promoted "simulated case 5" (detached sandstone RR) to e2e fixture (`001431_case5`, 11 pins @1e-4). Fixed sandstone wall label `"SS"`→2 (`_ELMHURST_WALL_CODE_TO_SAP10`) + `_parse_thickness_mm` for "400+ mm" roof insulation (trailing `+` was dropped → u_roof fell to age default). | — |
|
||||
| **.198** | **API `window_wall_type=4` → roof window.** These are roof-of-room rooflights; the mapper flattened them into `sap_windows` (vertical, (27), U=2.0) instead of `sap_roof_windows` ((27a), inclined U=2.30 + 45° solar). The inclined solar dominates → **0240 SAP −1→+0 exact**, PE +3.91→+1.95; 6035 PE +1.84→+1.37. Discriminator is `wall_type==4` NOT `window_type==2` (0390/7536 lodge window_type=2 on main walls). | SAP 10.2 §3 (27a); Table 6e Note 2 |
|
||||
| **.199** | Site-notes mirror of .198: extractor parses "Roof of Room" window rows (`_parse_window_from_anchors`); `_is_elmhurst_roof_window` location branch; `_ELMHURST_ROOF_WINDOW_U_BY_GLAZING["Double between 2002 and 2021"]=2.30`. Case 6 pinned on §3 windows (`test_section_3_roof_windows_case6_match_pdf`): (27)=22.7408, (27a)=13.0375 exact. | RdSAP 10 §3.7 |
|
||||
| **.200** | **SAP 10.2 §9a two-main-heating split** (203)/(204)/(205)/(207)/(213). The cascade lumped a 2-main dwelling into one system. Now `space_heating_fuel_monthly_kwh` splits demand (204) to sys1 @ (206) + (205) to sys2 @ (207); `_solve_month` sums main_1+main_2; `_main_heating_detail_efficiency` (new, the per-detail core of `_main_heating_efficiency`) gives each system its own efficiency. Site-notes: `_map_elmhurst_main_heating_2` inherits Main 1's fuel when §14.1 omits Fuel Type. Cost/CO2/PE main_2 paths were already wired. 0240 unchanged (identical Eq-D1 systems). | SAP 10.2 §9a |
|
||||
|
||||
Two new e2e fixtures: `001431_case5` (full SapResult, S0380.197) and
|
||||
`001431_case6` (§3 windows only, S0380.199 — see why below). Source PDFs
|
||||
tracked under `sap worksheets/golden fixture debugging/simulated case {5,6}/`;
|
||||
Summaries mirrored to `backend/documents_parser/tests/fixtures/Summary_001431_case{5,6}.pdf`.
|
||||
|
||||
---
|
||||
|
||||
## OPEN (the priority) — boiler-interlock −5pp efficiency adjustment, per main system
|
||||
|
||||
**Goal:** a RdSAP-10/SAP-10.2 **spec-accurate** implementation of the boiler
|
||||
interlock efficiency adjustment, applied **per main heating system**, done in
|
||||
the established pattern of this domain (per-line walk → cite spec → TDD →
|
||||
re-pin). This is the last gap blocking full closure of simulated **case 6**,
|
||||
and it will also re-pin golden **0240**.
|
||||
|
||||
### The evidence (simulated case 6)
|
||||
|
||||
`sap worksheets/golden fixture debugging/simulated case 6/` — detached, dual
|
||||
**oil** boiler (both SAP code **127**, base seasonal eff **84%**), radiators 51%
|
||||
(control **2106**) + underfloor 49% (control **2110**). Its P960 worksheet:
|
||||
|
||||
| line | worksheet | meaning |
|
||||
|---|---|---|
|
||||
| (206) main sys-1 eff | **79.0** | 84 − **5pp** |
|
||||
| (207) main sys-2 eff | **84.0** | base, no penalty |
|
||||
| (216) water-heater eff | **72.0** | also penalised (DHW leg of the −5pp) |
|
||||
| "Temperature adjustment" | 0.0000 | **flow temp has NO effect** — this is NOT a flow-temperature feature |
|
||||
|
||||
Summary §14 lodges it explicitly: system 1 **"Boiler Interlock: No"**, system 2
|
||||
**"Boiler Interlock: Yes"**. The 84→79 is the SAP 10.2 **Table 4c(2)** "no boiler
|
||||
interlock" −5pp **Space + DHW** adjustment (same mechanism as the AGENT_GUIDE
|
||||
"oil 6" worked example, S0380.177 — but that one fired off control 2101).
|
||||
|
||||
### Why control 2106 (which HAS a room thermostat) is "no interlock"
|
||||
|
||||
Per RdSAP 10 boiler-interlock rules (find + cite the exact §; the existing
|
||||
`_NO_INTERLOCK_CONTROLS = {2101, 2102}` block in `cert_to_inputs.py` ~line 1238
|
||||
quotes "RdSAP 10 §3 p.57: boiler interlock is assumed present if there is a room
|
||||
thermostat and [time control], AND — when there is a hot-water cylinder — a
|
||||
cylinder thermostat; otherwise not interlocked"): system 1 serves the **DHW
|
||||
cylinder**, the cylinder is present (`Hot Water Cylinder Present: Yes`) but
|
||||
**`Cylinder Thermostat: No`** → interlock **not** present → −5pp, *despite* the
|
||||
room thermostat. System 2 (underfloor, separate part, no cylinder interaction)
|
||||
keeps interlock via its zone control → no penalty.
|
||||
|
||||
So the determination is **not** "control ∈ {2101,2102}". It is, per system:
|
||||
`interlock present` ⇔ (room thermostat present, from the control code) AND
|
||||
(time/programmer control) AND (cylinder absent OR cylinder thermostat present).
|
||||
The current cascade only catches the "no room thermostat" path (2101/2102); it
|
||||
misses the "room thermostat present but no cylinder thermostat" path that 2106
|
||||
hits here.
|
||||
|
||||
### This single root cause explains BOTH remaining case-6 deltas
|
||||
|
||||
- space heating: sys-1 eff 79 not 84 → main fuel cascade 14925 vs ws **14736.96**
|
||||
- hot water: the −5pp DHW leg → cascade HW 4824 vs ws **4902.86** (lower cascade
|
||||
fuel ⇒ cascade eff too high ⇒ missing the penalty)
|
||||
|
||||
### 0240 will shift — and that is correct (apply the spec uniformly)
|
||||
|
||||
Golden **0240** has the SAME controls (sys1 2106 / sys2 2110) AND the same
|
||||
`cylinder_thermostat = "N"` with a cylinder present. So the spec-correct rule
|
||||
applies the −5pp to 0240's system 1 too. 0240 is currently SAP-exact (continuous
|
||||
72.55) **without** the penalty — that is an offsetting coincidence (it's API-only,
|
||||
±0.5 bar, no worksheet). Per [[feedback-software-no-special-handling]] +
|
||||
[[feedback-spec-floor-skepticism]]: implement the spec rule, let 0240 shift, and
|
||||
**re-pin** it with a documented note. Expect 0240 continuous SAP to drop ~0.3–0.5
|
||||
(may take the integer 73→72; if so the golden `expected_sap_resid` moves −1 and
|
||||
that is the new truth). Measure precisely and re-pin PE/CO2 too.
|
||||
|
||||
### Where to implement (per-line walk first, then TDD)
|
||||
|
||||
1. **Interlock determination.** Add a per-`MainHeatingDetail` helper, e.g.
|
||||
`_boiler_interlock_present(main, epc) -> bool`, encoding the RdSAP 10 rule
|
||||
above (room thermostat from control code + cylinder-thermostat gate when a
|
||||
cylinder is present). `epc.sap_heating.cylinder_thermostat` ("Y"/"N") and
|
||||
`cylinder_size`/`hot_water_cylinder_present` are the cylinder signals. The
|
||||
site-notes path already lodges `cylinder_thermostat` (mapper.py ~5183, string
|
||||
"Y"/"N"); the API path lodges it on `sap_heating.cylinder_thermostat` (0240 =
|
||||
"N").
|
||||
2. **Apply Table 4c(2) −5pp per system.** The existing −5pp lives near the
|
||||
`_NO_INTERLOCK_CONTROLS` block — find how it currently adjusts the seasonal
|
||||
efficiency for 2101/2102 and generalise it to fire on
|
||||
`not _boiler_interlock_present(main, epc)`, applied inside
|
||||
`_main_heating_detail_efficiency` so **each** main system gets its own
|
||||
adjustment (sys1 −5, sys2 0). Confirm the DHW leg (water-heater efficiency
|
||||
(216)) is penalised too — the §4 water-heating cascade reads
|
||||
`_main_heating_efficiency`; verify the −5pp flows there (case 6 (216)=72
|
||||
is the check).
|
||||
3. **Verify combi vs regular rows of Table 4c(2).** The "no interlock" −5pp has a
|
||||
combi row (Space −5 / DHW 0) and a regular-boiler row (Space −5 / DHW −5);
|
||||
the DHW leg is gated on cylinder presence. Case 6 is a regular oil boiler with
|
||||
a cylinder → DHW −5 applies (hence (216)=72). Read the table; don't assume.
|
||||
|
||||
### Validation target
|
||||
|
||||
After the fix, **promote case 6 to a full SapResult e2e fixture** (it's currently
|
||||
§3-windows-only because the lumped efficiency made (211)/(219)/(231) non-
|
||||
comparable). Case 6 worksheet Block-1 pin grid (P960-0001-001431):
|
||||
- SAP 72 (258), continuous **71.6597**, ECF **2.0316** (257)
|
||||
- total fuel cost **1162.5374** (255), CO2 **5953.6679** (272)
|
||||
- (211) main sys-1 fuel **7741.6458**, (213) main sys-2 fuel **6995.3106**
|
||||
(SapResult.main_heating_fuel_kwh_per_yr should be the sum **14736.9564**)
|
||||
- hot water **4902.8601** (219), lighting **357.6571** (232)
|
||||
- pumps/fans **356.0** (231) — **see the SECOND open item below**
|
||||
|
||||
### SECONDARY open item — dual-system auxiliary pumps (Table 4f)
|
||||
|
||||
Case 6 (231) = **356** = (230c) central-heating pump 156 + (230d) oil-boiler pump
|
||||
200. Cascade gives **241**. Two boilers → two pump contributions per Table 4f
|
||||
(note c: "where there are two main heating systems include two figures from this
|
||||
table" — same note already used for the 0240 oil-pump in S0380.148). Needs the
|
||||
per-system pump aggregation. Smaller than the interlock fix; do it after, then
|
||||
case 6's (231) pin closes and the full e2e fixture lands.
|
||||
|
||||
---
|
||||
|
||||
## Process notes
|
||||
- One slice = one commit, spec citation (page + line) in the message,
|
||||
`Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>` trailer.
|
||||
- AAA tests (`# Arrange/# Act/# Assert`), `abs(x-y) <= tol` (not `pytest.approx`).
|
||||
- New code passes `pyright` strict, 0 errors. (mapper.py + cert_to_inputs.py each
|
||||
carry **32 pre-existing** errors — don't add to them; check with a `git stash`
|
||||
baseline comparison.)
|
||||
- The Elmhurst worksheet is ground truth at abs=1e-4. 0240 is API-only (±0.5
|
||||
fallback) — case 6 is its worksheet-backed proxy for the heating archetype, but
|
||||
differs from 0240 on the boiler SAP code (127 vs 0240's 130 condensing combi),
|
||||
so pin case 6 to ITS OWN worksheet, not 0240's register.
|
||||
- Suite command + section/e2e harness layout: AGENT_GUIDE §2.6 + §4.
|
||||
Loading…
Add table
Reference in a new issue