The PE cascade in calculator.py was crediting ALL PV generation at the
IMPORT PEF (Table 12 ~1.501) instead of splitting per Appendix M1
§4/§8 — onsite-consumed E_PV,dw at the IMPORT PEF and exported E_PV,ex
at the EXPORT PEF (Table 12 code 60 = 0.501). The over-credit on the
exported portion was the primary driver of the ASHP-cohort PE Δ -7..-15
kWh/m² under-count.
Wiring (cert_to_inputs.py):
- `_pv_array_monthly_generation_kwh(array, climate)` — per-array E_PV,m
via Appendix M1 §2 (p.92) apportioning: 0.8 × kWp × ZPV × monthly
solar radiation. Reuses ORIENTATION/PITCH/Z lookups already in
`_pv_array_generation_kwh_per_yr`. Annual sum equals the existing
helper to float precision.
- `_pv_monthly_generation_kwh(epc, climate)` — sums per-array monthlies;
falls back to the same §11.1 b) percent-roof-area synthesis as the
annual helper for certs without per-array detail.
- `_pv_battery_capacity_kwh(epc)` — total usable battery capacity =
per-battery capacity × pv_battery_count. The 15 kWh cap per §3c is
applied inside `pv_beta_coefficients` and not duplicated here.
- `_pv_eligible_demand_monthly_kwh(...)` — assembles D_PV,m per §3a
p.93: lighting + appliances + cooking + electric showers + pumps
& fans, plus E_space,m when main fuel is Table-12 {30, 32, 34, 35,
38} (electricity not at off-peak) and E_water,m when water heating
fuel is Table-12 30 (standard electricity). Off-peak immersion ×
(243) and the Appendix G4 PV-diverter branch are deferred —
current cohort fixtures don't exercise them.
- In `cert_to_inputs`: assemble monthly EPV + DPV + battery, call
`pv_split_monthly`, pass `pv_dwelling_kwh_per_yr` +
`pv_exported_kwh_per_yr` through to CalculatorInputs.
Wiring (calculator.py):
- New fields: `pv_dwelling_kwh_per_yr: Optional[float]`,
`pv_exported_kwh_per_yr: Optional[float]`,
`pv_export_primary_factor: float = 0.501` (Table 12 code 60).
- PE cascade now does:
pv_offset = E_PV,dw × IMPORT_PEF + E_PV,ex × EXPORT_PEF
when both split fields are set. Legacy fall-through to all-IMPORT
when either is None (preserves synthetic CalculatorInputs
constructions in unit tests).
Test impact (golden-fixture residual shifts — all expected, re-pinned):
Pre-Slice 45 → Post-Slice 45:
- 0330 (no PV): +0.44 → +0.44 (unchanged ✓)
- 0350 (PV + 5 kWh battery): -7.78 → +2.73
- 0380 (PV + 5 kWh battery): -14.60 → +8.09
- 2130 (PV + gas combi): -38.63 → -9.70 (also SAP +1 shift)
- 2225 (PV + 5 kWh battery): -11.77 → +4.48
- 2636 (PV + 5 kWh battery): -9.65 → +3.42
- 3800 (PV + 5 kWh battery): -9.61 → +3.58
- 9285 (PV + 5 kWh battery): -7.96 → +3.20
- 9418 (PV + 5 kWh battery): -7.30 → +4.67
- 9501 (PV, no battery): -8.28 → +0.25 (CLOSED ✓)
Cert 9501 closing to +0.25 with the β-split alone confirms the
implementation is spec-correct. The 7-cert 5-kWh-battery cohort
now over-shoots in the positive direction because the cascade's
E_PV magnitude is ~3× the worksheet's (cert 0380 cascade 2570 kWh/yr
vs worksheet 831 kWh/yr — peak_power=3 interpreted as 3 kWp while
worksheet uses ~1 kWp). With E_PV overestimated, R_PV = E_PV / D_PV
is too high → β_m from §3d formula too low → not enough credit
shifts to the IMPORT factor. Slice S0380.46 audits the cascade's
E_PV magnitude (kWp interpretation, S lookup, or ZPV mapping).
Chain tests (cohort-1 + cohort-2 SAP-rating-vs-worksheet) all stay
<1e-4 — Slice 45 only touches the PE cascade; SAP rating uses the
cost cascade which is still on the old all-export path.
Test suite: 763 pass + 0 fail. Pyright net-zero on touched files.
Spec citations:
- SAP 10.2 specification Appendix M1 §3a (p.93) — D_PV,m assembly.
- SAP 10.2 specification Appendix M1 §3c-d (p.94) — β formula.
- SAP 10.2 specification Appendix M1 §4 (p.94) — E_PV,dw / E_PV,ex.
- SAP 10.2 specification Appendix M1 §8 (p.94) — PE factor split.
- SAP 10.2 Table 12 code 60 — EXPORT PEF = 0.501.
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