docs(corpus): find the third driver — mixer-shower default, 91% of gap now traced

Walked the water-heating worksheet section: assumed occupancy (42) matched
exactly (2.4234 both sides), but (43) average daily hot water use was
117.41 L/day (Elmhurst) vs 110.73 L/day (ours) — same occupancy, different
shower/bath demand. Root cause: the cert's shower_outlets field is
genuinely unlodged, which the mapper correctly maps to 0 mixer showers
per its documented, already-validated "no entry = no shower" convention.
Elmhurst's build shows a "Non-electric shower" fixture anyway — its UI
silently defaults one when the build script doesn't explicitly configure
"0 showers" to match the cert.

Verified: a single 7 L/min vented mixer reproduces Elmhurst's (42a)
monthly series exactly (4 d.p., all 12 months) and closes hot water to
2616.04 vs Elmhurst's 2616.03. Corpus-wide check across 170 certs sharing
this "bath present, no shower data" pattern shows no systematic bias
(mean dSAP +0.12, median -0.04) — confirms the mapper convention is
correct on average; this is a build-entry artifact on this one cert, not
a mapper bug (no code change warranted).

Combining all three found causes (window orientation, Table 11, shower
default): SAP 72.4567 vs Elmhurst's precise 72.0681 — residual 0.39,
~91% of the original 4.26-point gap now precisely explained.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jun-te Kim 2026-07-03 10:19:05 +00:00
parent d15ea8b7d4
commit 645789456e

View file

@ -907,12 +907,34 @@ _EXPECTATIONS: Final[tuple[RealCertExpectation, ...]] = (
# (Elmhurst Jan (83)=56.9 W vs our real-orientation 123.4 W). Verified:
# forcing our engine to ALSO use a single synthetic North window (i.e.
# replicating the build's simplification, not a cert or engine fact)
# plus the forced Table 11 above reproduces 72.62 -- matches Elmhurst's
# 72 almost exactly. Conclusion: the calculator is faithful (it already
# matches lodged exactly using the cert's REAL window orientations);
# both residuals are Elmhurst-worksheet-reconciliation artifacts (a
# deliberate spec choice + a build-entry simplification), not mapping
# or calculator bugs. PINNED to the observed exact match vs lodged.
# plus the forced Table 11 above reproduces 72.62 (precise Elmhurst
# value is 72.0681, not the rounded "72" -- true residual after Table
# 11 + windows was ~0.55, not ~1.6). Third cause found by walking the
# rest of the worksheet monthly: (43) Average daily hot water use is
# 117.4056 L/day (Elmhurst) vs our 110.7281 L/day -- SAME occupancy
# (42)=2.4234 both sides, so the gap is in shower/bath demand, not
# occupancy. Root cause: cert's `mixer_shower_count` is unlodged (raw
# `shower_outlets=None`), which `_mixer_shower_flow_rates_from_cert`
# maps to 0 showers (see its docstring: gov-API "no shower_outlets
# entry" convention, validated on cert 2225) -- but Elmhurst's build
# shows a "Non-electric shower" fixture (1 vented mixer @ Table J4),
# since its UI silently defaults one when the build script never
# explicitly configured "0 showers" to match the cert. Verified: a
# single 7 L/min vented mixer reproduces Elmhurst's (42a) monthly
# series EXACTLY (4 d.p., all 12 months) and closes hot water to
# 2616.04 vs Elmhurst's 2616.03 (Δ0.005). Corpus-wide check (170
# certs sharing "bath present, no shower data lodged") shows NO
# systematic bias (mean dSAP +0.12, median -0.04, 46.5% over-rated) --
# confirms the mapper's 0-showers convention is correct on average;
# this is a THIRD build-entry artifact, not a mapper bug. Combining
# all three (windows + Table 11 + 1 shower): SAP 72.4567 vs Elmhurst's
# precise 72.0681 -- residual 0.39, ~91% of the original 4.26-point
# gap (76.33 vs 72.07) now precisely explained. Conclusion: the
# calculator is faithful (matches lodged 76 exactly using the cert's
# REAL windows and REAL shower data); all three residuals are Elmhurst
# -reconciliation-build artifacts or deliberate spec choices, not
# mapping or calculator bugs. PINNED to the observed exact match vs
# lodged.
RealCertExpectation(
schema="RdSAP-Schema-21.0.1",
sample="uprn_100061275133",