Commit graph

6430 commits

Author SHA1 Message Date
Jun-te Kim
32eef951ee Add corpus profiler for the ADR-0028 seeing-the-data table
Reusable per-schema profiler: glazed_area band mix, Validation Cohort size,
observed-vs-predicted band glazing/floor ratio, and the ND/str sentinels that
drive schema widening. Regenerates the ADR-0028 transfer-check table from any
harvested corpus.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 12:36:08 +00:00
Jun-te Kim
99981e07e7 Guard all RdSAP-Schema-19.0 corpus certs in the strict parse+map bucket 🟩
Promote RdSAP-Schema-19.0 into SUPPORTED so all 1000 corpus certs are held to
the strict parse+map guard. Drop the now-redundant cert[0] tracer (subsumed by
the parametrised bucket); keep the reduced-field synthesis behavioural test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 12:34:29 +00:00
Jun-te Kim
1fcad454fa Synthesise reduced-field windows for RdSAP-Schema-19.0 certs 🟩
Add the 19.0 synthesis seam over the shared _synthesise_reduced_field_windows
core (inherited 20.0.0 coefficients, ND glazing -> DG-modal default 2, per
ADR-0028). 19.0 glazed_type codes (1-4,6,7) are a subset of the verified 1-8
space. The 6 rich certs use lodged window_area directly; the windowless 994
synthesise a 4-way N/E/S/W split.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 12:33:26 +00:00
Jun-te Kim
8d8e2b1208 Synthesise reduced-field windows for RdSAP-Schema-19.0 certs 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 12:31:23 +00:00
Jun-te Kim
792f76f2fa Map RdSAP-Schema-19.0 certs to EpcPropertyData 🟩
Dispatch RdSAP-Schema-19.0 through from_api_response, parse-fix the schema
(data-driven required->optional, validated against the 1000-cert 19.0 corpus
per ADR-0028), and port 18.0's defensive mapper reads (dwelling_type str/dict/
number, photovoltaic_supply guard, sap_room_in_roof Measurement coercion).
All 1000 corpus certs now parse and map.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 12:29:36 +00:00
Jun-te Kim
5178197dc2 Map RdSAP-Schema-19.0 certs to EpcPropertyData 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 12:19:16 +00:00
Jun-te Kim
fcc2e5d0f8 Extract shared reduced-field window synthesis core across the RdSAP family 🟪
ADR-0028's deferred extraction, triggered by 17.1 as the second instance: the
inherited 20.0.0 coefficients (0.148 + band multipliers + 4-way split) now live
in one `_synthesise_reduced_field_windows` core. The 20.0.0 / 18.0 / 17.1 seams
keep their own names (so each can diverge) but collapse to glazing-type
resolution (cascade + that spec's ND handling) plus a call to the core.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 13:16:15 +00:00
Jun-te Kim
0f3321e655 Synthesise windows, lighting, ventilation and hot water for 17.1 certs 🟩
Rich certs (14/1000) use lodged window_area; the windowless majority synthesise
glazing from the glazed_area band via _synthesise_17_1_sap_windows (own seam,
inherited 20.0.0 coefficients); lighting/ventilation/hot-water mirror 18.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 13:13:37 +00:00
Jun-te Kim
a8895b41d4 Synthesise windows, lighting, ventilation and hot water for 17.1 certs 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 13:13:37 +00:00
Jun-te Kim
0e982f902f Guard all RdSAP-Schema-17.1 corpus certs in the strict parse+map bucket 🟩
Schema-fix delivered in the prior slice; this promotes the 1000-cert corpus
into the strict guard (matches 18.0/20.0.0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 12:21:51 +00:00
Jun-te Kim
25e550ce3c Dispatch and map RdSAP-Schema-17.1 certs end-to-end 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 12:21:17 +00:00
Jun-te Kim
4ea5367da6 Dispatch and map RdSAP-Schema-17.1 certs end-to-end 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 12:21:17 +00:00
Jun-te Kim
cc0e875fd8 Record pre-SAP10 RdSAP family coefficient transfer (ADR-0028)
Documents the inherit-and-validate decision for 18.0: reuse 20.0.0's 0.148 +
band multipliers (the corpus can't self-fit — 958/1000 band-1 with no measured
band-1 windows), validated against 18.0's own band-4 rich certs (0.223 obs vs
0.148 x 1.51 pred). References ADR-0027 one-way (keeps the accepted ADR immutable).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 12:06:03 +00:00
Jun-te Kim
744aa5482d Derive hot-water bath and mixer counts from room counts for 18.0 certs 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 12:02:16 +00:00
Jun-te Kim
6df307b49c Derive hot-water bath and mixer counts from room counts for 18.0 certs 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 12:02:16 +00:00
Jun-te Kim
e0d3d3fd2a Map chimneys, draughtproofing and sheltered sides for 18.0 certs 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 12:01:13 +00:00
Jun-te Kim
afdf85176d Map chimneys, draughtproofing and sheltered sides for 18.0 certs 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 12:01:13 +00:00
Jun-te Kim
2b5d596f9b Synthesise lighting bulb counts from outlet counts for 18.0 certs 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 11:59:33 +00:00
Jun-te Kim
70af9dee65 Synthesise lighting bulb counts from outlet counts for 18.0 certs 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 11:59:33 +00:00
Jun-te Kim
fb14f03873 Map Not-Defined glazing code to a valid type for windowless 18.0 certs 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 11:55:46 +00:00
Jun-te Kim
0008c33ea9 Map Not-Defined glazing code to a valid type for windowless 18.0 certs 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 11:55:46 +00:00
Jun-te Kim
42f02f33d8 Pin band-2 glazing scaling at x1.25 for 18.0 certs 🟩
Behaviour delivered by the band-multiplier dict in the prior slice; this pins
the band-2 ("More than typical") case against the inherited 20.0.0 coefficient.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 11:50:14 +00:00
Jun-te Kim
e0ff02b1ac Synthesise glazing from the glazed_area band for windowless 18.0 certs 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 11:49:38 +00:00
Jun-te Kim
bfedcd54dc Synthesise glazing from the glazed_area band for windowless 18.0 certs 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 11:49:38 +00:00
Jun-te Kim
162259b255 Use lodged window geometry for rich RdSAP-Schema-18.0 certs 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 11:45:12 +00:00
Jun-te Kim
17cb06a2f9 Use lodged window geometry for rich RdSAP-Schema-18.0 certs 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 11:41:19 +00:00
Jun-te Kim
51309328e6 Parse and map all RdSAP-Schema-18.0 corpus certs 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 11:39:07 +00:00
Jun-te Kim
024f3926af Parse and map all RdSAP-Schema-18.0 corpus certs 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 11:28:27 +00:00
Jun-te Kim
4fca5d7fed Dispatch and map RdSAP-Schema-18.0 certs end-to-end 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 11:18:15 +00:00
Jun-te Kim
cfc337f04a Dispatch and map RdSAP-Schema-18.0 certs end-to-end 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 11:12:53 +00:00
Jun-te Kim
362cd20f11 scripts? 2026-06-11 07:07:27 +00:00
Jun-te Kim
8ad560dc48 Merge branch 'feature/bill-derivation' of https://github.com/Hestia-Homes/Model into feature/junte+khalim 2026-06-10 16:44:43 +00:00
Jun-te Kim
deea05905b Record RdSAP 20.0.0 Reduced-Field Synthesis as implemented in resume doc
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 15:36:32 +00:00
Jun-te Kim
c0423295da Guard all 1000 RdSAP 20.0.0 certs as a strict parse-and-map bucket 🟩
Drops the xfail now that Reduced-Field Synthesis (ADR-0027) maps every
20.0.0 cert; the corpus test holds the whole bucket to a strict 1000/1000.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 15:35:19 +00:00
Jun-te Kim
c0b8a7d9f8 Use lodged window area for the 7 rich 20.0.0 certs' geometry 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 15:30:17 +00:00
Jun-te Kim
12ff15e55b Parse 20.0.0 conservatory building parts so all 1000 certs map 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 15:21:00 +00:00
Jun-te Kim
eb5bb89612 Map 20.0.0 lighting, ventilation and hot-water demand fields 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 14:46:54 +00:00
Jun-te Kim
3352f11be3 Synthesise 20.0.0 window geometry from glazed-area band and floor area 🟩
ADR-0027 Reduced-Field Synthesis: certs with no per-window array now get total
glazing = 0.148 x TFA x band-multiplier (median + quartile multipliers fit from
the 1000 real 21.0.1 certs), split 4-way across N/E/S/W with width=area/4,
height=1.0; glazing_type routed through the verified 21.0.1 cascade. Also guard
optional PhotovoltaicSupply.none_or_no_details (a parse straggler). Corpus maps
983/1000, up from 974.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 14:29:13 +00:00
Jun-te Kim
8074f4152c Map RdSAP 20.0.0 certs that omit reduced fields or lodge localised text 🟩
Required->optional defaults (kw_only + data-driven from corpus presence) so
993/1000 certs that omit sap_windows parse, and honest Union[str, DescriptionV1]
typing for description/dwelling_type which the corpus lodges as localised dicts
in ~half the certs. The never-run 20.0.0 mapper path now produces EpcPropertyData;
974/1000 corpus certs map (xpass), up from 7.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 14:14:18 +00:00
Jun-te Kim
5589a66e7c Document RdSAP 20.0.0 Reduced-Field Synthesis (CONTEXT.md term + ADR-0027)
Sharpen the glossary to decouple deterministic old-schema re-mapping from
neighbour-prediction gap-fill (a separate, unimplemented ML path), and add
the Reduced-Field Synthesis term. ADR-0027 records the pre-SAP10 20.0.0
mapper's best-attempt synthesis (corpus-fit glazing 0.148xTFAxband, 4-way
orientation) and its trade-offs. Grill resume doc captures every resolved branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 14:14:18 +00:00
Khalim Conn-Kowlessar
07f534ee11 feat(modelling): system tune-up options (standard + zone controls)
Add the system tune-up to the heating Recommendation: keep the existing wet
boiler but install better heating controls and fix the cylinder. Two competing
Options (the Optimiser picks <=1 across the whole heating rec) per the user's
two best control end-states:

- system_tune_up        — standard controls (programmer + room thermostat +
  TRVs, SAP 10.2 Table 4e code 2106)
- system_tune_up_zoned  — time-and-temperature zone control (code 2110, type 3):
  more SAP uplift for more cost

Both keep the boiler (no fuel / SAP code / flue change), set the control
ABSOLUTELY to their end-state, and apply the conditional cylinder fixes (an
80 mm jacket when under-insulated, a thermostat when absent — only when a
cylinder exists). Each control option is offered only when it genuinely improves
the existing control — standard is skipped when the control is already 2106 /
2110 / 2112, zone when already 2110 / 2112 — so neither is ever a downgrade or a
no-op.

Validated against the Elmhurst "system tune up" re-lodgements (cert 001431):
nine befores spanning controls 2101-2113 all converge to the two common afters,
proving the control overlay is absolute. The cascade pin is parametrised over
two starting controls (2101 "no control" + 2113 "room thermostat and TRVs") x
both afters, delta 0 (SAP/CO2/PE).

Wires the two MeasureTypes through contingencies (0.15), the offline catalogue
(500 / 900), the catalogue-coverage list, the report triggers, and the ARA
first-run seed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 10:20:46 +00:00
Khalim Conn-Kowlessar
3d108a9d9e feat(harness): explain gas_boiler_upgrade in the report triggers
Add the `gas_boiler_upgrade` branch to `report._triggers_for`, mirroring the
generator's eligibility guard so a cohort report explains why the boiler upgrade
fired: the wet-boiler SAP code, the mains-gas connection that makes the gas
end-state installable, and the cylinder presence that shapes the bundle (combi
vs regular + cylinder fixes).

No golden API cert selects the boiler upgrade (it competes with — and on houses
loses to — the ASHP bundle within the one heating Recommendation), so the branch
is covered by a direct `_triggers_for` unit test, following the repo pattern for
testing internal helpers (cert_to_inputs).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 08:44:37 +00:00
Khalim Conn-Kowlessar
2413bc87da feat(modelling): solid-fuel(coal)->gas boiler upgrade + boiler_flue_type end-state
Pin the coal-boiler-with-cylinder upgrade and add the `boiler_flue_type`
end-state field. A solid-fuel (coal) boiler (fuel 11, SAP code 153) on a
mains-gas street converts to a gas condensing boiler (fuel 11->26, code 102) —
the non-gas->gas path for a solid-fuel system, eligible because code 153 is in
the wet-boiler solid-fuel range 151-161 and mains gas is present.

New `boiler_flue_type` HeatingOverlay field, routed to main_heating_details[0]
and set to 2 (room-sealed/balanced) on both boiler shapes: every relodged after
lodges flue type 2, but coal's before lodged none. The field is SAP-inert (the
cascade score is unchanged by it), so it is written purely for end-state
fidelity — the overlay now represents the installed condensing boiler's flue.
Validated via the overlay-equality unit tests.

The coal after predates the user-locked "always add a cylinder thermostat when
absent" rule, so it stale-lodged thermostat 'N'; the pin corrects it to the
rule's end-state 'Y' in-test (the gas with-cylinder after got the same
correction by re-lodging). The cylinder is already 80 mm insulated, so the
jacket is skipped and only the thermostat is added; controls (2106) are
unchanged. Cascade-pinned delta 0 (SAP/CO2/PE).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 08:27:07 +00:00
Jun-te Kim
14dc4efeed save so i can continue 2026-06-09 17:07:55 +00:00
Khalim Conn-Kowlessar
7bc9797a26 test(modelling): pin non-gas->gas + already-insulated-cylinder boiler upgrades
Two more boiler-upgrade cascade pins, validating the existing generator across
fuels and cylinder states (no source change):

- oil combi: an oil boiler (fuel 28, code 130) on a mains-gas street converts to
  a gas condensing combi (fuel 28->26, code 104). Proves the non-gas -> gas
  conversion gated on a mains-gas connection (ADR-0024 revised).
- already-insulated cylinder: a gas boiler heating a pre-jacketed cylinder
  (type 2 / 80 mm, no thermostat) gets a new boiler + a thermostat, with the
  jacket NOT re-applied. Proves the cylinder path's skip-jacket branch against a
  real cert. (Sourced from an LPG re-lodgement whose fuel the Summary mapper
  reads as mains gas 26 — a separate LPG fuel-mapping gap, noted in the test.)

Both pin delta 0 (SAP/CO2/PE) against the relodged after.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 16:37:32 +00:00
Khalim Conn-Kowlessar
63dd69ff8b feat(modelling): gas combi boiler upgrade + controls-when-inadequate
Extend the gas-boiler-upgrade Option to combi (no-cylinder) dwellings and add
the controls upgrade shared by both boiler shapes. A dwelling has a cylinder or
it does not, so the one `gas_boiler_upgrade` Option is shaped per dwelling:

- no cylinder -> a gas condensing combi (Table 4b code 104), no cylinder fields
  touched;
- a cylinder  -> a regular boiler (code 102) heating it, with the conditional
  cylinder jacket/thermostat (slice 1).

Controls: bring an inadequate boiler control up to full programmer + room
thermostat + TRVs (SAP 10.2 Table 4e Group 1 code 2106). "Inadequate" = the
Group-1 codes with NO room thermostat (2101, 2102, 2107, 2108, 2109, 2111) —
these lack boiler interlock (Table 4c(2) / footnote c) p.171), so adding a room
thermostat genuinely improves SAP. Room-thermostatted (2103/2104/2105/2106/2113)
or better zone controls (2110/2112) are left unchanged — never downgraded, so
no phantom uplift. The with-cylinder cert (control 2106) is therefore untouched
and its pin still holds at delta 0.

Validated by the combi before/after re-lodgement (cert 001431, gas boiler
upgrade - no cylinder): control 2111 "TRVs and bypass" -> 2106, fan flue
False->True, SAP code 112 -> 104. Cascade-pinned delta 0 (SAP/CO2/PE). Removed
the slice-1 placeholder test asserting no boiler Option fires without a cylinder
(the combi Option now correctly fires there).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 16:28:00 +00:00
Khalim Conn-Kowlessar
31c74ab500 feat(modelling): gas-boiler-upgrade-with-cylinder option in the heating rec
Add the first boiler-upgrade option to the single "Heating & Hot Water"
Recommendation (ADR-0024 expansion): a dwelling whose existing wet boiler heats
a hot-water cylinder is offered a new gas condensing boiler, with the cylinder
jacketed when under-insulated and given a thermostat when absent. One competing
Option (the Optimiser picks <=1), folded into one composite Plan line.

The end-state is read from the Elmhurst before/after re-lodgements (cert 001431,
gas boiler upgrade - with cylinder), which REVISE ADR-0024:

- Target is always a gas condensing boiler, not fuel-preserving: every after
  lodges fuel 26. Gas->gas always; a non-gas wet boiler ->gas only with a
  mains-gas connection; electric boilers are left alone (electrification is the
  upgrade path). Eligibility = wet-boiler SAP code (Table 4a/4b 101-141 /
  151-161 / 191-196) + not an electric boiler + mains gas present.
- End-state is a Table 4b SAP code, not a PCDB index: code 102 (regular boiler
  + cylinder). The calculator derives the condensing seasonal efficiency from
  the code, so no efficiency input exists or is needed.
- A modern condensing boiler has a fanned flue: the after flips
  `fan_flue_present` False->True on every cert (SAP 10.2 Table 4f flue-fan +
  the Table 4b condensing-efficiency basis). Added as a new HeatingOverlay
  field, routed to main_heating_details[0].
- Cylinder thermostat is always added when absent (user-locked); the jacket is
  the 80 mm `cylinder_insulation_type=2` end-state, applied only when the
  cylinder is below 80 mm (never downgrading a better one). Both are conditional
  per-dwelling components, not a frozen overlay.

Cascade-pinned delta-0 (SAP/CO2/PE) against the relodged after via
`_assert_overlay_reproduces_after`. NB the absolute SAP on this dwelling is
subject to a separate Summary-path mapper roof-fidelity gap (we read the roof
better-insulated than Elmhurst, scoring ~75 vs the printed 56); the gap is
identical on before+after (the boiler measure never touches the roof) so it
cancels and the pin still proves the exact heating field-delta. Tracked on the
calculator branch.

Wires the new `gas_boiler_upgrade` MeasureType through contingencies (0.26),
the offline sample catalogue, the catalogue-coverage list, and the ARA
first-run integration seed (the option fires on any mains-gas boiler+cylinder
dwelling).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 16:16:46 +00:00
Jun-te Kim
3b7d26fe34 added test for a 1000 examples 2026-06-09 16:02:21 +00:00
Jun-te Kim
06cb4f7b6e Merge branch 'feature/bill-derivation' into feature/junte+khalim 2026-06-09 10:06:40 +00:00
Khalim Conn-Kowlessar
4006753620 fix(scripts): authenticate the EPC client with OPEN_EPC_API_TOKEN
The new gov EPC API (api.get-energy-performance-data..., Bearer auth) returns
403 "Bad authentication header" with EPC_AUTH_TOKEN but 200 with
OPEN_EPC_API_TOKEN — the token name is misleading (it is the Bearer token for
the new API, not the open-data API). Verified live against
/api/domestic/search. Unblocks the live EPC fetch in run_modelling_e2e.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 20:58:22 +00:00