Commit graph

7367 commits

Author SHA1 Message Date
Khalim Conn-Kowlessar
e83c420f3b Resolve an electric-boiler override to SAP 191 dragging electricity 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 15:58:27 +00:00
Khalim Conn-Kowlessar
de4e44aacc Resolve solid-fuel room-heater overrides off Gas CPSU to their SAP codes 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 15:58:25 +00:00
Khalim Conn-Kowlessar
f253a7b9ba Resolve solid-fuel room-heater overrides off Gas CPSU to their SAP codes 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 15:58:25 +00:00
Jun-te Kim
de9159f9b8
Merge pull request #1399 from Hestia-Homes/fix/heating-rec-pcdb-boiler-fallback
Classify PCDB-index gas boilers with no SAP code for heating recs (#1388 A#2)
2026-07-01 15:57:07 +01:00
Jun-te Kim
5b4a1b4966 Classify PCDB-index gas boilers with no SAP code for heating recs
Gov-API certs that lodge a gas boiler as main_heating_category=2 + a PCDB
main_heating_index_number but omit sap_main_heating_code were carried with
a null code, so the heating generators (which gated on sap_main_heating_code
alone) never evaluated them for a boiler upgrade or a system tune-up — they
silently got nothing (£0 works, plateau at D). The SAP calculator already
handles this shape (cert_to_inputs._is_wet_boiler_main), so the calculator
modelled these as wet boilers while the recommenders ignored them.

Mirror the calculator's classification in the generator:
- _is_wet_boiler / _is_electric_boiler fall back, when no SAP code is
  lodged, to a PCDB Table 105 boiler index or main_heating_category in
  {1,2} (heat pumps excluded); electric boilers stay excluded by the code
  range when lodged, else by an electricity main fuel.
- _already_condensing reads the boiler's declared PCDB winter efficiency
  when there is no Table 4b code, so a modern condensing boiler lodged this
  way still isn't offered a pointless like-for-like swap (gets a tune-up).

Verified read-only on portfolio 814 / scenario 1271: pids 742304, 742306,
742322, 742325, 742336 now get a gas_boiler_upgrade (two cross D->C on the
boiler alone) where they had £0 works; predicted properties whose heating
donor is a PCDB-shape boiler (e.g. 742095/742102/742105) also recover a
tune-up. Fixes the A#2 slice of #1388 (and unblocks 742302/742367).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:52:47 +00:00
Jun-te Kim
10e260be23
Merge pull request #1395 from Hestia-Homes/fix/neighbour-divergence-fuel-cohort
neighbour_divergence: key cohorts on main-heating fuel
2026-07-01 15:49:38 +01:00
Jun-te Kim
78da4f390e
Merge pull request #1398 from Hestia-Homes/fix/rdsap-insulated-door-u-value
Normalize RdSAP insulated_door_u_value on the 20.0.0 + 21.0.0 paths
2026-07-01 15:49:24 +01:00
Jun-te Kim
ce0f14a829 Normalize RdSAP insulated_door_u_value on the 20.0.0 + 21.0.0 paths
PRD #1385 phase 2. The 20.0.0 and 21.0.0 mappers dropped insulated_door_u_value
(top-level Optional float), unlike 21.0.1. heat_transmission reads it as the
measured door U-value override (cert_to_inputs -> heat_transmission:809), so
dropping it forced every door onto the RdSAP default U-value.

Verification: new test 3 passed (21.0.0 sample lodges 3; 20.0.0 latent -> inject
1.5 + absent -> None); corpus 6002 passed; SAP-accuracy regressions pass; pyright
unchanged (39 -> 39).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:48:25 +00:00
Daniel Roth
0488696df8
Merge pull request #1397 from Hestia-Homes/fix/1376-hhrsh-baseline-archetype
Add the HHRSH baseline archetype so it stops scoring as old storage (#1376)
2026-07-01 15:41:52 +01:00
Khalim Conn-Kowlessar
2f577c3ebc Re-map old-storage HHRSH overrides onto the high-heat-retention archetype 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:34:49 +00:00
Daniel Roth
ae4a8569e9
Merge pull request #1396 from Hestia-Homes/community-heating-hhrsh
Don't recommend HHRSH or ASHP to properties with community heating
2026-07-01 15:33:57 +01:00
Khalim Conn-Kowlessar
04484edc67 Re-map old-storage HHRSH overrides onto the high-heat-retention archetype 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:33:24 +00:00
Jun-te Kim
d03b52b649
Merge pull request #1394 from Hestia-Homes/fix/rdsap-21-0-0-mvhr-ducts
Normalize RdSAP MVHR PCDF index + duct type on the 21.0.0 path
2026-07-01 15:32:20 +01:00
Khalim Conn-Kowlessar
598aac1556 Route main-heating overrides through the HHRSH guard before the LLM 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:32:14 +00:00
Khalim Conn-Kowlessar
b7848848a5 Deterministically guard the high-heat-retention storage description 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:30:40 +00:00
Khalim Conn-Kowlessar
9711b2e5f5 Deterministically guard the high-heat-retention storage description 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:30:13 +00:00
Khalim Conn-Kowlessar
01e7e1d831 Drag HHRSH's intrinsic control, electricity fuel and off-peak meter 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:29:41 +00:00
Khalim Conn-Kowlessar
ca10966c38 Drag HHRSH's intrinsic control, electricity fuel and off-peak meter 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:27:59 +00:00
Khalim Conn-Kowlessar
a2083b9c7d Resolve a high-heat-retention storage override to SAP code 409 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:27:30 +00:00
Khalim Conn-Kowlessar
79f48c0564 Resolve a high-heat-retention storage override to SAP code 409 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:26:25 +00:00
Jun-te Kim
009394bb19 neighbour_divergence: key cohorts on main-heating fuel
The neighbour SAP-divergence cohort was (postcode, property_type, built_form),
which mixed electric- and gas-heated dwellings. Electricity scores materially
lower in SAP than mains gas for identical fabric, so a mixed-fuel postcode
produced cross-fuel false outliers — an electric dwelling flagged only for being
electric among gas neighbours.

Add the main-heating fuel class to the cohort key (electricity variants — 29/30
+ off-peak 31-40 — collapse to one class; every other code is its own bucket).
Now every flagged divergence is a same-fuel comparison worth investigating. On
portfolio 814 this refined 20 raw divergences to 17 same-fuel ones while keeping
the genuine within-fuel outliers (e.g. the all-electric WC2B 4AW flats at SAP
26/38 vs a cohort median of 67).

Reaches the fuel via epc_main_heating_detail through the indexed property_id —
still never touches the recommendation table. Surfaced by the portfolio-814
recommendation audit (Work item B, #1388).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:24:31 +00:00
Jun-te Kim
7ee33b9ce8 Normalize RdSAP MVHR PCDF index + duct type on the 21.0.0 path
PRD #1385 phase 2. The 21.0.0 mapper dropped mechanical_ventilation_index_number
and mechanical_vent_duct_type, unlike 21.0.1. cert_to_inputs reads both (PCDB
Table 4f/329 heat-recovery/SFP lookup + the Table 329 in-use factor), so an
MVHR/MEV cert on the 21.0.0 path fell back to the SAP default efficiency.

Mirrors 21.0.1 exactly -- only the two fields it actually maps. The other four
mechanical_vent_duct_* schema fields have no mapper OR calculator consumer even
on 21.0.1 (verified), so they're deliberately left alone rather than populating
dead domain fields.

Verification: new test 1 passed (harvested 21.0.0 sample lodges index=12,
duct_type=3); corpus 6002 passed; SAP-accuracy regressions pass; pyright
unchanged (39 -> 39).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:24:12 +00:00
Daniel Roth
baaadcdf98
Merge pull request #1392 from Hestia-Homes/fix/1376-water-heating-house-coal
Reclassify water-heating overrides off the house-coal dumping ground (#1376)
2026-07-01 15:19:39 +01:00
Daniel Roth
6eb2a02a6e Gate community-heated dwellings from ASHP recommendation 🟩
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 14:18:52 +00:00
Jun-te Kim
e95c3cf906
Merge pull request #1393 from Hestia-Homes/fix/rdsap-21-0-0-percent-draughtproofed
Normalize RdSAP percent_draughtproofed on the 21.0.0 path
2026-07-01 15:17:50 +01:00
Daniel Roth
576fc948a6 Gate community-heated dwellings from ASHP recommendation 🟥
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 14:17:41 +00:00
Daniel Roth
3d562be398 Gate community-heated dwellings from HHRSH recommendation 🟩
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 14:14:52 +00:00
Daniel Roth
afe6638b36 Gate community-heated dwellings from HHRSH recommendation 🟥
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 14:13:57 +00:00
Jun-te Kim
a3a620a7e9 Normalize RdSAP percent_draughtproofed on the 21.0.0 path
PRD #1385 phase 2. 21.0.0 was the last mapper still dropping the lodged
percent_draughtproofed (top-level int) — every other path (17.0/…/21.0.1) reads
it. Dropping it understated the SAP §2 draughtproofing/infiltration credit for
21.0.0 certs.

Verification: new test 1 passed (harvested 21.0.0 sample lodges 100); corpus
6002 passed; SAP-accuracy regressions pass; pyright unchanged (39 -> 39).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:13:17 +00:00
Jun-te Kim
6819ca7d89
Merge pull request #1390 from Hestia-Homes/fix/rdsap-mapper-wwhrs-parity
Normalize RdSAP hot-water demand (WWHRS rooms) on the degraded 17.0/19.0 paths
2026-07-01 15:10:37 +01:00
Khalim Conn-Kowlessar
1debf5f5a3 Assert every water-heating reclassify target round-trips to a resolvable archetype 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:02:18 +00:00
Khalim Conn-Kowlessar
51abd15289 Re-map house-coal water-heating overrides onto faithful biomass and immersion archetypes 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 13:59:44 +00:00
Khalim Conn-Kowlessar
ffbef2d047 Re-map house-coal water-heating overrides onto faithful biomass and immersion archetypes 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 13:58:39 +00:00
Daniel Roth
355a632e5f Merge branch 'main' into community-heating-hhrsh 2026-07-01 13:58:29 +00:00
Jun-te Kim
2892ce8560
Merge pull request #1391 from Hestia-Homes/fix/rdsap-mapper-building-part-fabric
Normalize RdSAP building-part fabric mapping on the degraded 17.0 path
2026-07-01 14:58:28 +01:00
Jun-te Kim
460d687b58
Merge pull request #1389 from Hestia-Homes/fix/rdsap-mapper-main-heating-controls
Normalize RdSAP main_heating_controls mapping on the degraded 17.0 path
2026-07-01 14:58:20 +01:00
Daniel Roth
221e310fd8
Merge pull request #1382 from Hestia-Homes/incorrect-overrides
Fix HHRSH eligibility for on-gas-street electric dwellings; rename mains_gas → gas_connection_available
2026-07-01 14:53:14 +01:00
Khalim Conn-Kowlessar
36ead3ca1d Route water-heating overrides through the deterministic guard before the LLM 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 13:09:12 +00:00
Jun-te Kim
7bb89a36f1 Normalize RdSAP building-part fabric mapping on the degraded 17.0 path
PRD #1385, building-part fabric family. The 17.0 mapper hardcoded
floor_insulation_thickness / sap_room_in_roof to None and omitted
flat_roof_insulation_thickness, all of which the 17.0 schema lodges and the
19.0/20.0/21.x paths read:

  * floor_insulation_thickness: "NI" (no measured thickness) -> None so u_floor
    reaches its age-band default, else pass through (mirrors the 19.0 note).
  * flat_roof_insulation_thickness: pass through (was dropped).
  * sap_room_in_roof: map to SapRoomInRoof(floor_area, construction_age_band),
    None if absent (mirrors 19.0).

17.0-only: the 17.1 SapBuildingPart schema has none of these fields, so its None
there is correct (verified against the dataclass). roof_insulation_thickness is
intentionally left raw -- the sloping-ceiling resolve helper moves roof U-values
and is a separate follow-up.

Verification: new test 3 passed; test_mapper_corpus 6002 passed; SAP-accuracy /
RealCertExpectation regressions pass; pyright unchanged (39 -> 39, pre-existing).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 13:07:46 +00:00
Khalim Conn-Kowlessar
d0aa596ae7 Deterministically guard structured biomass and immersion water-heating descriptions 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 13:06:52 +00:00
Khalim Conn-Kowlessar
d7f069b7c5 Deterministically guard structured biomass and immersion water-heating descriptions 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 13:05:47 +00:00
Khalim Conn-Kowlessar
84fcf9da49 Resolve biomass, dual-fuel and biodiesel water-heating overrides off house coal 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 13:03:28 +00:00
Khalim Conn-Kowlessar
da15418fd8 Resolve biomass, dual-fuel and biodiesel water-heating overrides off house coal 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 13:02:38 +00:00
Khalim Conn-Kowlessar
ea3696e8d5 Resolve a from-main wood-logs water-heating override to the wood fuel 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 13:02:13 +00:00
Khalim Conn-Kowlessar
c24e465974 Resolve a from-main wood-logs water-heating override to the wood fuel 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 12:59:56 +00:00
Jun-te Kim
28b72fc67c Normalize RdSAP hot-water demand (WWHRS rooms) on the degraded 17.0/19.0 paths
PRD #1385, shower/WWHRS family. The 17.0 and 19.0 API mappers left
sap_heating.number_baths / mixer_shower_count as None, so the water-heating
demand (cert_to_inputs) fell back to defaults instead of the lodged bath/shower
room counts. Derive them from schema.sap_heating.instantaneous_wwhrs, mirroring
the 17.1/18.0/20.0 paths:

  number_baths       = rooms_with_bath_and_or_shower + rooms_with_bath_and_mixer_shower
  mixer_shower_count = rooms_with_mixer_shower_no_bath + rooms_with_bath_and_mixer_shower

Guarded on instantaneous_wwhrs is not None (Optional in the schema), like 17.1.
Counts, same meaning across the reduced-field family.

Verification: new test 2 passed; test_mapper_corpus 6002 passed; SAP-accuracy /
RealCertExpectation regressions pass; pyright unchanged (39 -> 39, pre-existing).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 12:52:33 +00:00
Jun-te Kim
366adaf8c8
Merge pull request #1387 from Hestia-Homes/fix/rdsap-mapper-lighting-parity
Normalize RdSAP lighting mapping on the degraded 17.0/19.0 paths
2026-07-01 13:48:20 +01:00
Jun-te Kim
04c29cc02a Normalize RdSAP main_heating_controls mapping on the degraded 17.0 path
PRD #1385, heating-controls family. The 17.0 API mapper omitted
main_heating_controls entirely, leaving the domain field None while the
19.0/20.0/21.x paths map the first lodged control via _map_energy_element. 17.0
lodges main_heating_controls (List[EnergyElement]) in all 23 local fixtures.

The mapped element is the same EnergyElement type 17.0 already carries for
main_heating / window, so this is straight parity (not new logic). Guarded on a
non-empty list -> None, matching the siblings.

Verification: new test 2 passed; test_mapper_corpus 6002 passed; recommendation
+ heating-control tests 85 passed (the domain EnergyElement field is distinct
from the subscriptable dict HeatingControlRecommender reads via the Property
wrapper, so no recommender destabilisation); SAP-accuracy regressions pass;
pyright unchanged (39 -> 39, pre-existing).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 12:44:02 +00:00
KhalimCK
9db8b60a54
Merge pull request #1384 from Hestia-Homes/fix/1376-glazing-proportion-reconcile
Defer aggregate-mix glazing overrides to the cert's per-window glazing (#1376)
2026-07-01 13:37:28 +01:00
Jun-te Kim
a3bdcccfb1 Normalize RdSAP lighting mapping on the degraded 17.0/19.0 paths
PRD #1385, lighting family. The 17.0 and 19.0 API mappers hardcoded every
fixed-lighting bulb count to 0, dropping the lodged lighting mix that the
reference-complete 17.1/18.0/20.0 paths read. The schema lodges total +
low-energy OUTLET counts (ADR-0028); mirror the reference split — low-energy
outlets → low_energy bulbs, the remainder → incandescent — so the SAP
lighting-energy calc sees the real low-energy credit instead of assuming none.

led/cfl stay 0 (reduced-field certs don't split those); 21.0.x use a different
bulb-based lighting schema and are out of scope. The `total - low_energy`
subtraction is safe: all 37 local 17.0/19.0 fixtures have low_energy <= total
(no negative incandescent), matching the reference paths that already do this
unclamped.

Verification: new test 2 passed; test_mapper_corpus 6002 passed; SAP-accuracy /
RealCertExpectation regressions 56 passed (no pinned shifts); pyright unchanged
(39 -> 39, pre-existing).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 12:30:17 +00:00