Closes cert 000565 sap_score regression — 28 (Δ−1) → **29 ✓ EXACT**.
Continuous SAP 28.4735 → 28.5652 (Δ −0.035 → +0.057 vs worksheet
28.5087). Two coupled fixes that together close the (56)/(57)m
storage-loss gap per SAP 10.2 §4 + Table 2b.
## 1. (57)m solar storage adjustment — SAP 10.2 §4 line 7693 (p.137)
If the vessel contains dedicated solar storage or dedicated
WWHRS storage,
(57)m = (56)m × [(47) - Vs] ÷ (47), else (57)m = (56)m
where Vs is Vww from Appendix G3 or (H12) from Appendix H.
Total heat required for water heating calculated for each month
(62)m = 0.85 × (45)m + (46)m + (57)m + (59)m + (61)m
(62)m sums (57)m — the solar-adjusted storage loss — not raw
(56)m. The cascade's `_cylinder_storage_loss_override` was
passing (56)m straight through as `solar_storage_monthly_kwh_
override`, over-counting (62)m by Vs/V each month whenever solar
HW shares the cylinder. For cert 000565: V = 160 L, Vs = (H12) =
53 L per the combined-cylinder ⅓-volume convention (S0380.76);
(V − Vs)/V = 0.6688 (matches worksheet 50.7018/75.8157 = 0.6688).
Fix: when `epc.solar_water_heating` is True, return (57)m =
(56)m × (V − Vs)/V from `_cylinder_storage_loss_override`. The
combined-cylinder Vs derivation reuses the
`_COMBINED_CYLINDER_SOLAR_PREHEAT_FRACTION` constant established
by S0380.76 for the Appendix H orchestrator path.
## 2. separately_timed_dhw defaults True when a cylinder is lodged
SAP 10.2 Table 2b note b) (PDF p.159):
Multiply Temperature Factor by 0.9 if there is separate time
control of domestic hot water (boiler systems, warm air
systems and heat pump systems)
RdSAP 10 Specification §3 default table "Hot water separately
timed" (PDF p.57):
No programmer, pre-1998 boiler: - No
Programmer, pre-1998 boiler: - Yes
Post-1998 boiler: - Yes
When a hot-water cylinder is lodged, DHW is timed by its own
programmer / boost cycle regardless of which heat generator
(boiler, HP, or combi-acting-as-boiler) feeds it. Combi-only
dwellings (no cylinder) skip the multiplier — DHW is
instantaneous and shares the boiler's space-heating cycle.
The earlier `_separately_timed_dhw` heuristic gated only on
`main_heating_category == 4` (heat pumps), returning False for
boiler-family + cylinder combos. Cert 000565 (gas combi via
WHC 914 + 160 L cylinder + cyl-stat absent) fell through to TF
= 0.60 × 1.3 × 1.0 = 0.78; the worksheet uses 0.60 × 1.3 × 0.9
= 0.702. The 10% TF over-count drove +98 kWh/yr into (56)m before
compounding with the missing (57)m solar adjustment.
Fix: `_separately_timed_dhw(epc, main)` returns True when a
cylinder is lodged, in addition to the existing HP branch. Signature
gains `epc` so the helper can inspect `has_hot_water_cylinder`;
both call sites in `_primary_loss_override` and
`_cylinder_storage_loss_override` updated.
## Cert 000565 movements at HEAD (post-S0380.78 → post-this slice)
| Field | Pre-slice | Post-slice | Worksheet | Pre-Δ | Post-Δ |
|----------------------|-----------:|-----------:|-----------:|--------:|--------:|
| **sap_score** | **28** | **29** | **29** | −1 | **✓ 0** |
| sap_score_continuous | 28.4735 | 28.5652 | 28.5087 | −0.035 | +0.057 |
| ecf | 5.3904 | 5.3810 | 5.3866 | +0.004 | −0.006 |
| total_fuel_cost_gbp | 4683.39 | 4675.23 | 4680.26 | +3.13 | −5.03 |
| co2_kg | 6480.57 | 6388.80 | 6447.63 | +33 | −58.8 |
| hot_water_kwh | 4014.64 | 3517.37 | 3755.03 | +259.6 | −237.7 |
| space_heating_kwh | 58792.99 | 58936.06 | 59008.35 | −215.4 | −72.3 |
| main_heating_fuel | 34584.11 | 34668.27 | 34710.79 | −126.7 | −42.5 |
HW pin overshot −238 (down from +260) — within ~6% of the
worksheet, vs the +37% over-count three slices ago. Continuous
SAP residual flipped from Δ −0.035 to Δ +0.057, restoring integer
sap_score = 29 EXACT. The cumulative cert 000565 closure across
S0380.77/78/79:
hot_water_kwh: +1399 → +260 → −238 (84% closed)
sap_score_cont: +0.60 → −0.035 → +0.057 (90% closed)
ecf: −0.06 → +0.004 → −0.006 (90% closed)
## Cross-cohort impact — cert 0390 golden pin update
Golden cert `0390-2954-3640-2196-4175` (Firebird oil combi PCDF
9005 + 160 L cylinder + cyl-stat=Y, no solar) was previously
flagged at SAP residual −7 with the comment "traces to fabric
heat-loss / oil-fuel cost cascade rather than the §4 HW path".
That diagnosis was wrong: cert 0390's §4 HW cascade WAS
applying TF=0.60 instead of TF=0.54 for the (56)m storage loss,
contributing ~£20/yr cost over-count.
Per [[feedback-spec-floor-skepticism]] + [[feedback-golden-
residuals-near-zero]], the +1 SAP closure (53→54, residual
−7→−6) is the spec-correct outcome of applying RdSAP §3 default
"Programmer, pre-1998 boiler → Yes". Pin updated; revised notes
record the slice S0380.79 attribution.
## Tests
- `test_cylinder_storage_loss_applies_57m_solar_adjustment_per_sap_4_line_7693`
(test_cert_to_inputs.py) — pins `solar_storage_monthly_kwh[0]` to
worksheet (57)Jan = 50.7018 at abs=1e-4 and the 12-month sum to
596.9725 at abs=1e-3, for cert-000565-shape inputs (gas combi +
cylinder + solar HW + cyl-stat absent).
- Updated golden pin for cert 0390 per the cross-cohort impact note.
Test baseline: 548 → 550 pass + 9 expected `test_sap_result_pin
[000565-*]` fails (sap_score now PASSING; one fewer expected fail
than mid-slice). Pyright net-zero on touched files (46 baseline =
46 after).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|---|---|---|
| .devcontainer | ||
| .github/workflows | ||
| .idea | ||
| .vscode | ||
| applications | ||
| asset_list | ||
| backend | ||
| backlog | ||
| datatypes | ||
| deployment/terraform | ||
| docs/adr | ||
| domain | ||
| epr_data_exports | ||
| etl | ||
| infrastructure | ||
| model_data/requirements | ||
| orchestration | ||
| recommendations | ||
| repositories | ||
| scripts | ||
| sfr/principal_pitch | ||
| survey_report | ||
| tests | ||
| utilities | ||
| utils | ||
| .coveragerc | ||
| .dockerignore | ||
| .gitignore | ||
| __init__.py | ||
| ara_backend_design.md | ||
| BaseUtility.py | ||
| CLAUDE.md | ||
| conftest.py | ||
| CONTEXT.md | ||
| devcontainer.sh | ||
| Dockerfile.test | ||
| Dockerfile.test.dockerignore | ||
| Makefile | ||
| MEMORY.md | ||
| package-lock.json | ||
| package.json | ||
| pyproject.toml | ||
| pyrightconfig.json | ||
| pytest.ini | ||
| README.md | ||
| run_lambda_local.sh | ||
| serverless.yml | ||
| test.requirements.txt | ||
| tox.ini | ||
| UBIQUITOUS_LANGUAGE.md | ||
Model Repository
This repository contains the code pertaining to the development of the data science and machine learning products being utilised by Hestia.
The different folders in this repository relate to services that can be used independently, or can be imported and used as part of a larger application
Getting Started
Prerequisites
Dev Container Setup
This repo uses a Docker Compose-based dev container. The model-backend service joins a shared-dev Docker network so it can communicate with other local services (e.g. a frontend container) running on your machine.
VS Code users: The initializeCommand in devcontainer.json creates the shared-dev network automatically before the container starts. No manual step required — just open the repo and select Reopen in Container.
Non-VS Code / CI workflows: Run the following once before starting the container:
make dev-setup
This is idempotent and safe to re-run if the network already exists.
Folders
backend/
This folder contains the code for the fastapi backend service, which provides an interface to much of the functionality in this repository, for the frontend
model_data/
This folder contains related to the reading and preparation of assessment model data, including pulling out epc attributes
Testing
All tests can be run, against the configuration in pytest.ini running
pytest
This will run the complete panel of tests and report on coverage in the locations specified by the pytest.ini file.
To run tests in a specific service, e.g. inside of model_data, simply run
pytest --cov-config=model_data/.coveragerc --cov=model_data
This will produce the test results and coverage reports