Model/backend/documents_parser
Khalim Conn-Kowlessar c727b3cb5a Slice S0380.156: SAP 10.2 Table 3 WHC=903 electric-immersion zero-loss guard
SAP 10.2 Table 3 (PDF p.160) verbatim:

  Primary loss is set to zero for the following:
      Electric immersion heater
      Combi boiler ...
      CPSU ...
      Boiler and thermal store within a single casing
      Separate boiler and thermal store connected by no more than 1.5
      m of insulated pipework
      Direct-acting electric boiler
      Heat pump (...) with hot water vessel integral to package

The Elmhurst WHC=903 lodging signals exactly the first row: "HW from
a separate electric immersion heater" — the cylinder is heated by an
immersion element inside the tank, no primary pipework between any
heat generator and the cylinder. The rule is universal: regardless
of what main heating exists for space heating, electric immersion
means no primary circuit means no primary loss.

Pre-slice `_primary_loss_applies` only consulted `water_heating_code`
in the Table 4a wet-boiler branch (codes 151-161 / 191-196). The Cat
4 HP branch returned True unconditionally when no PCDB record was
lodged; the Cat 1/2 boiler branch returned True unconditionally; the
PCDB Table 322 + Table 4b non-PCDB branches likewise. For the
electric 2 corpus variant (sap_main_heating_code=524 Cat 5 warm-air
ASHP, main_heating_category=4 per Elmhurst mapper, no PCDB record,
WHC=903 + cylinder), the Cat-4 branch falsely returned True and the
cascade added ~510 kWh/yr primary loss to a system with no primary
circuit at all.

Per-line walk discipline applied: cascade `water_heating_from_cert`
output dump showed `primary_loss_monthly_kwh_annual = 509.98` while
worksheet (59)m = 0 every month → spec lookup found Table 3 verbatim
"Electric immersion heater" zero-loss line.

Adds `_WHC_ELECTRIC_IMMERSION: Final[int] = 903` constant + a
top-of-function `if water_heating_code == _WHC_ELECTRIC_IMMERSION:
return False` guard that fires before any of the system-type-keyed
branches.

Closures electric 2:
  HW kWh 2849.22 → 2339.24 (matches worksheet (62)/(64) = 2384.12
  within the residual ~45 kWh storage-loss gap)
  ΔSAP −0.4584 → +0.8118 (cascade swung past the worksheet by +1.27
  — the pre-slice 'near-correct' value was offsetting cascade bugs
  per [[feedback-software-no-special-handling]]; the +0.81 residual
  exposes a separate upstream gap to chase in a follow-up slice)
  Δcost +£10.56 → −£18.71
  ΔCO2 +47.89 → −7.21 kg
  ΔPE +443.13 → −161.68 kWh

No regressions on the other 24 cohort variants — only electric 2 has
the (Cat 4 HP, no PCDB, WHC=903) combination in the corpus.
Extended handover suite: 900 pass / 0 fail (was 899 — +1 from the
new AAA test). Pyright net-zero (43 → 43).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 15:40:25 +00:00
..
handler address JTK review comments 2026-04-20 15:11:17 +00:00
tests Slice S0380.156: SAP 10.2 Table 3 WHC=903 electric-immersion zero-loss guard 2026-06-04 15:40:25 +00:00
__init__.py Map to RdSapSiteNotes from site notes JSON 🟥 2026-04-16 13:54:03 +00:00
db_writer.py include updating epc_property_data to pashub to ara workflow 2026-04-29 09:55:14 +00:00
elmhurst_extractor.py Slice S0380.140: §4 cylinder storage loss — extractor picks up §16 thermostat lodging + Table 2b note b restricts ×0.9 to boiler/warm-air/HP systems 2026-05-31 19:03:58 +00:00
extractor.py Handle wall thickness "Unmeasurable" 🟩 2026-04-30 16:41:16 +00:00
local_runner.py update local runner to work for elmhurst 2026-04-24 14:01:36 +00:00
parser.py load ecmk site notes to db 2026-04-29 11:20:47 +00:00
pdf.py update local runner to work for elmhurst 2026-04-24 14:01:36 +00:00