mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
docs: handover for §8c Space cooling + 000490 SAP-score diagnostic
Two tickets in order for the next agent: 1. Ticket A — Investigate the 000490 +3 SAP overshoot. Corrects the previous agent's claim that "wiring water_heating_from_cert is the easy win"; that's already done. Real driver is the boiler efficiency cascade selecting 0.80 instead of the PDF Manufacturer-declared 0.882 (Vaillant Ecotec Pro). Time-boxed diagnostic; flag and defer if expensive. 2. Ticket B — §8c Space cooling (xlsx rows 435-466, lines (100)..(108)). All 6 Elmhurst fixtures = 0 cooling. Small slice; mirror §8 pattern. Includes spec anchors (Qcool formula sign, Jun-Aug inclusion rule), codebase pointers, slice plan, and the standard "do not" list. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
bb827803ac
commit
67af2e9b43
1 changed files with 207 additions and 0 deletions
207
docs/sap-spec/HANDOVER_NEXT.md
Normal file
207
docs/sap-spec/HANDOVER_NEXT.md
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
# Handover — §8c Space cooling + 000490 SAP-score diagnostic
|
||||
|
||||
**For the agent picking up the next chunk of work.** Read this BEFORE invoking `/grill-me`. Read all of it. Caveman mode is the house style — terse, technical, no filler.
|
||||
|
||||
Owner: `khalim@domna.homes`. Branch: `ara-backend-design-prd`.
|
||||
|
||||
This handover covers **two tickets in order**:
|
||||
|
||||
1. **Ticket A — Diagnose + (best-effort) close the 000490 e2e SAP-score gap.** §8 wiring exposed a +3 SAP overshoot. The previous agent claimed "the easy win is to wire `water_heating_from_cert` into `cert_to_inputs`" — that claim was **wrong** (it's already wired, see §A.2 below). The real gap is in upstream precision and is multi-source. Ticket A is to investigate and chip away, with explicit user check-ins before changing pinned expected values.
|
||||
2. **Ticket B — Implement §8c Space cooling.** xlsx rows 435–466, worksheet line refs (100)..(108). All 6 Elmhurst fixtures = 0 cooling. Should be a small slice.
|
||||
|
||||
Hard rules (same as previous handovers):
|
||||
- **Tolerance**: don't loosen test tolerances to make them pass. If the orchestrator can't hit the locked tolerance, pause and ask the user.
|
||||
- **Spec PDFs**: don't scan more than ~50 lines without checking with the user.
|
||||
- **Fixture `build_epc()`**: don't modify — handover §11 of the original §6 handover is still in force. Local `_build_section_*_epc(fixture)` wrappers if you need to override windows.
|
||||
- **Commit per slice**, one slice = one commit, AAA test convention (`# Arrange / # Act / # Assert`), Co-Authored-By trailer.
|
||||
|
||||
---
|
||||
|
||||
## §A — Ticket A: 000490 SAP-score gap diagnostic
|
||||
|
||||
### A.1 Current state
|
||||
After §8 wiring (commit `f6ab7626`) and `_currently_within_3_points` test update (in `f6ab7626` too), 000490 cert-driven calculator output is:
|
||||
|
||||
| Metric | Calculator | PDF | Delta |
|
||||
|---|---|---|---|
|
||||
| SAP integer | 60 | 57 | +3 |
|
||||
| Continuous SAP | 60.22 | 57.40 | +2.82 |
|
||||
| Annual space heating kWh | 11467.18 | 11183.275 | +2.5% |
|
||||
| Annual HW fuel kWh | 3090.47 | 2850.57 | +8.4% |
|
||||
| Total fuel cost £ | 756.99 | 807.54 | −6.3% |
|
||||
| ECF | 2.4538 | 3.0539 | −20% |
|
||||
|
||||
### A.2 What's already wired (do NOT redo)
|
||||
- ✅ `water_heating_from_cert` IS the active path in `cert_to_inputs._hw_kwh_and_gains_from_cert` (file `cert_to_inputs.py`, line ~738). Legacy `predicted_hot_water_kwh` is a fallback only when `epc.total_floor_area_m2 is None`. For Elmhurst fixtures TFA is always present.
|
||||
- ✅ `internal_gains_from_cert` (§5), `solar_gains_from_cert` (§6), `mean_internal_temperature_monthly` (§7), `space_heating_monthly_kwh` (§8) all wired.
|
||||
- ✅ Per-fixture orchestrator-level conformance: all 6 Elmhurst fixtures pass §3–§8 at line-ref level (see `test_*.py::test_*_matches_elmhurst_worksheet_all_fixtures` tests).
|
||||
|
||||
### A.3 Where the gap actually is
|
||||
|
||||
The drift is in **scalar derivations the cert→inputs adapter computes from cert codes** — not in the per-month physics. Verified:
|
||||
|
||||
```python
|
||||
inputs = cert_to_inputs(_w000490.build_epc())
|
||||
inputs.main_heating_efficiency # → 0.8000
|
||||
# PDF Vaillant Ecotec Pro Manufacturer-declared: 0.882
|
||||
```
|
||||
|
||||
Drivers (ordered by likely SAP impact):
|
||||
|
||||
1. **Water-heating efficiency** (`cert_to_inputs._legacy_water_heating_efficiency`)
|
||||
- Currently selects ~0.78-0.80 from a static SAP10 efficiency table by `sap_main_heating_code`.
|
||||
- PDF uses the **Manufacturer-declared** value (Vaillant Ecotec Pro 88.2%) from PCDB / cert efficiency lodgement.
|
||||
- Look for: cert field carrying the manufacturer efficiency directly, OR the PCDB lookup that's currently stubbed (per ADR-0009 grill: `NoOpPcdbLookup`).
|
||||
- Fixing this should drop HW fuel from 3090 → ~2750 kWh, closing ~340 kWh on annual fuel.
|
||||
|
||||
2. **Space heating delivered = q_heat / main_heating_efficiency**
|
||||
- Same efficiency cascade. PDF (210) Jan = 88.2% throughout. Ours = 80%.
|
||||
- q_heat itself is correct (we just nailed §8 to 1e-1 kWh).
|
||||
- Fixing efficiency drops `main_heating_fuel_kwh_per_yr` from 14334 → ~13000 kWh.
|
||||
|
||||
3. **§3 transmission HLC precision** (~+2.5% on annual space heating)
|
||||
- Calculator's `inputs.heat_transmission.total_w_per_k` = 236.6211 (matches PDF LINE_37).
|
||||
- But cert_to_inputs derives `total_w_per_k` from windows + walls + roof + floor + thermal_bridging. If any of those is off, HLC drifts → §7 MIT drifts → §8 space heating drifts.
|
||||
- This is much smaller than the efficiency gap.
|
||||
|
||||
### A.4 Recommended approach for Ticket A
|
||||
|
||||
**Do not** attempt to fix the entire efficiency cascade in one go — it touches §4 + §9 + §12 simultaneously and is a multi-day rewrite.
|
||||
|
||||
**Do** spend ~30 minutes investigating and either:
|
||||
|
||||
a) **Find a cert field that carries Manufacturer efficiency directly** that we're not reading (most likely: `epc.sap_heating.main_heating_details[0].efficiency_declared_pct` or similar). If present, wire it through `cert_to_inputs._main_heating_efficiency_pct` as a precedence-over-table override. Single-slice fix. **Surface findings to the user before implementing.**
|
||||
|
||||
b) **If no such cert field exists**, write a one-paragraph finding into `SPEC_COVERAGE.md` "Prioritised gap list" under "Boiler efficiency Manufacturer override" + ship as a known gap. Skip to Ticket B.
|
||||
|
||||
The user explicitly said in the previous turn: *"this is silly"* about the 000490 overshoot — meaning they want progress on it, but understand it might not be a one-line fix. Bias toward (a) if cheap; flag and defer if expensive.
|
||||
|
||||
### A.5 Don't update e2e tolerances during Ticket A
|
||||
The `_currently_within_3_points` ceiling for 000490 should stay 3 until Ticket A actually moves it. Tightening to 2 / 1 only after physics improves; loosening is a regression.
|
||||
|
||||
---
|
||||
|
||||
## §B — Ticket B: §8c Space cooling
|
||||
|
||||
### B.1 Mission
|
||||
Implement §8c (xlsx rows 435–466) — produce `cooling_monthly_kwh` for all 12 months. All 6 Elmhurst fixtures = 0 (no air conditioning lodged). Should be the smallest section yet.
|
||||
|
||||
Worksheet lines you must close:
|
||||
- (100)m heat loss rate L_m (cooling) — recomputed via §7 chain at cooling Ti (≈ same as heating MIT for our fixtures)
|
||||
- (101)m utilisation factor for loss η_loss (cooling Table 10a, formula differs from Table 9a)
|
||||
- (102)m useful loss = η × L
|
||||
- (103)m gains (excluding pumps/fans Table 5a per spec — see §6 of the SAP10.2 PDF)
|
||||
- (104)m cooling requirement Q_cool
|
||||
- ∑(104) over Jun-Aug only (cooling inclusion rule — June to August, disregarding September to May)
|
||||
- (105) cooled-area fraction f_C (almost always 0 in RdSAP — set 0 for all 6 fixtures)
|
||||
- (106)m × intermittency factor (Table 10b)
|
||||
- (107)m space cooling requirement after f_C
|
||||
- (108) cooling per m² = (107) / TFA
|
||||
|
||||
### B.2 SAP10.2 spec anchors (already extracted)
|
||||
|
||||
From the spec at `/tmp/sap102.txt`:
|
||||
- Line 10320: `Qcool = 0.024 × (Gm - mLm) × nm × fcool × fintermittent` (note the SIGN — gains − losses, opposite of heating)
|
||||
- Line 10321: `Set Qcool to zero if negative or less than 1 kWh.`
|
||||
- Line 10325: `Include the cooling requirements for each month from June to August (disregarding September to May).`
|
||||
|
||||
Table 10a (utilisation factor for cooling) is a separate formula from Table 9a heating η — page 186 of SAP10.2 spec.
|
||||
Table 10b (intermittency factor) — page 187.
|
||||
Table 10c (SEER for cooling system fuel calc) — page 187.
|
||||
|
||||
### B.3 Existing state
|
||||
- No `space_cooling.py` module exists.
|
||||
- Calculator has no cooling fields.
|
||||
- All 6 Elmhurst fixtures have `has_fixed_air_conditioning=False` → no cooling system → cooling fuel = 0.
|
||||
|
||||
### B.4 Likely shape (mirror §8 pattern)
|
||||
|
||||
```python
|
||||
@dataclass(frozen=True)
|
||||
class SpaceCoolingResult:
|
||||
heat_loss_rate_monthly_w: tuple[float, ...] # (100)
|
||||
utilisation_factor_loss_monthly: tuple[float, ...] # (101) — Table 10a
|
||||
useful_loss_monthly_w: tuple[float, ...] # (102)
|
||||
cooling_gains_monthly_w: tuple[float, ...] # (103)
|
||||
cooling_requirement_monthly_kwh: tuple[float, ...] # (104)
|
||||
intermittency_factor_monthly: tuple[float, ...] # (106)
|
||||
space_cooling_monthly_kwh: tuple[float, ...] # (107) ← what the calculator consumes
|
||||
space_cooling_kwh_per_yr: float
|
||||
space_cooling_per_m2_kwh: float # (108)
|
||||
|
||||
|
||||
def space_cooling_monthly_kwh(
|
||||
*,
|
||||
monthly_heat_transfer_coefficient_w_per_k: tuple[float, ...],
|
||||
monthly_internal_temperature_c: tuple[float, ...],
|
||||
monthly_external_temperature_c: tuple[float, ...],
|
||||
monthly_total_gains_w: tuple[float, ...],
|
||||
total_floor_area_m2: float,
|
||||
cooled_area_fraction: float = 0.0, # f_C — almost always 0 for RdSAP
|
||||
# Table 10b intermittency factor by control type, default to 0.25 (intermittent)
|
||||
intermittency_factor: float = 0.25,
|
||||
) -> SpaceCoolingResult: ...
|
||||
```
|
||||
|
||||
### B.5 Conformance budget
|
||||
**All 6 fixtures = 0 cooling** for every line. So conformance is trivial: assert every per-line tuple is zero. The interesting test is the **synthetic positive case** — pin a non-zero cooled-area fraction + warm summer month + verify Q_cool emerges, then verify with `f_C = 0` it collapses to zero.
|
||||
|
||||
### B.6 Calculator coupling
|
||||
After orchestrator: add `CalculatorInputs.space_cooling_monthly_kwh: tuple[float, ...]` field (defaults `(0.0,) * 12` for backwards-compat). The calculator's `MonthlyEntry` already has `space_heat_requirement_kwh`; either add `space_cool_requirement_kwh` or just expose the annual total on `SapResult`. Mirror §8 pattern.
|
||||
|
||||
### B.7 Slice plan (suggested)
|
||||
|
||||
```
|
||||
Slice 1 — SpaceCoolingResult + space_cooling_monthly_kwh orchestrator
|
||||
(Table 10a η_loss + Jun-Aug inclusion rule); synthetic positive
|
||||
test (non-zero f_C, hot July) + synthetic zero test (f_C=0)
|
||||
Slice 2 — Per-fixture SECTION_8C_COOLED_AREA_FRACTION=0 constants +
|
||||
ALL_FIXTURES e2e (every line tuple = 0 across all 6 fixtures)
|
||||
Slice 3 — CalculatorInputs.space_cooling_monthly_kwh + cert_to_inputs
|
||||
wiring (atomic) + drop legacy if any
|
||||
Slice 4 — SPEC_COVERAGE §8c row + slice progress table
|
||||
```
|
||||
|
||||
Smaller than §8 because there's no upstream-drift surprise to chase (all fixtures = 0).
|
||||
|
||||
---
|
||||
|
||||
## §C — Codebase pointers
|
||||
|
||||
- §8 orchestrator (close template): [packages/domain/src/domain/sap/worksheet/space_heating.py](../../packages/domain/src/domain/sap/worksheet/space_heating.py)
|
||||
- §7 orchestrator (per-zone η pattern): [packages/domain/src/domain/sap/worksheet/mean_internal_temperature.py](../../packages/domain/src/domain/sap/worksheet/mean_internal_temperature.py)
|
||||
- cert→inputs adapter: [packages/domain/src/domain/sap/rdsap/cert_to_inputs.py](../../packages/domain/src/domain/sap/rdsap/cert_to_inputs.py) (look for the chain at lines 870-960 for how §5/§6/§7/§8 stack)
|
||||
- E2e SAP-score test: [packages/domain/src/domain/sap/worksheet/tests/test_e2e_elmhurst_sap_score.py](../../packages/domain/src/domain/sap/worksheet/tests/test_e2e_elmhurst_sap_score.py)
|
||||
- §8 fixture pins (template): `_elmhurst_worksheet_000490.py` `SECTION_7_*` + `LINE_85..LINE_99` constants
|
||||
- §8 ALL_FIXTURES test: [test_space_heating.py](../../packages/domain/src/domain/sap/worksheet/tests/test_space_heating.py)
|
||||
- Extract script template: `/tmp/extract_section8.py` (rewrite for §8c — only ~30 lines)
|
||||
|
||||
## §D — Commit chain to inspect
|
||||
|
||||
```bash
|
||||
git log --oneline --grep '§8 slice'
|
||||
# 9113f30a §8 slice 1 — orchestrator + summer clamp
|
||||
# 1f078af7 §8 slice 2 — 6-fixture conformance
|
||||
# f6ab7626 §8 slice 3 — calculator + cert_to_inputs wiring
|
||||
# bb827803 docs — SPEC_COVERAGE §8 + slice progress
|
||||
```
|
||||
|
||||
## §E — Skills
|
||||
|
||||
The dev container ships `/grill-me`, `/tdd`, `/caveman`. Default flow:
|
||||
|
||||
```
|
||||
/grill-me → walk down design tree
|
||||
/tdd implement §8c Space cooling → one test → one impl → repeat
|
||||
```
|
||||
|
||||
User invokes `/grill-me` first when ready. Audit + surface unknowns first; wait for `/grill-me`.
|
||||
|
||||
## §F — Definitely do NOT
|
||||
|
||||
- Do **not** claim "wiring water_heating_from_cert is the easy win" — that ship has sailed (see §A.2).
|
||||
- Do **not** update the 000490 `_currently_within_3_points` ceiling unless Ticket A actually moves the SAP score.
|
||||
- Do **not** loosen test tolerances to make tests pass — ask the user.
|
||||
- Do **not** scan more than ~50 lines of a spec PDF before asking the user for the specific table/page.
|
||||
- Do **not** modify existing fixture `build_epc()` functions.
|
||||
- Do **not** invoke `/ultrareview` yourself.
|
||||
Loading…
Add table
Reference in a new issue