mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Sibling migration to the sap10_calculator move — `domain.ml` now lives
at the root-level layout (`domain/sap10_ml/`) matching the pattern
already used by `domain.addresses`, `domain.tasks`, `domain.postcode`,
and `domain.sap10_calculator`.
Changes:
- `git mv packages/domain/src/domain/ml → domain/sap10_ml` (19 files;
history preserved).
- Subpackage rename: `domain.ml` → `domain.sap10_ml`. 32 references
rewritten across .py and .md files: 11 internal + 21 external
(datatypes/epc/domain/mapper.py, 14 files in domain/sap10_calculator,
2 backend tests, 2 ADRs, 1 README, 1 design doc).
- Path-string updates: `pytest.ini` testpath
`packages/domain/src/domain/ml/tests` → `domain/sap10_ml/tests` so
ML tests stay in the default auto-discovered sweep. `CONTEXT.md`
also updated.
`packages/domain/src/domain/` is now empty — the workspace `domain/`
tree has been fully migrated. Together with the `domain/__init__.py`
deletions from the sap10_calculator commit (29ac35cc), `domain` is
now a single root-level namespace package with subpackages
{addresses, sap10_calculator, sap10_ml, tasks} + the standalone
`postcode.py` module.
Verified:
- Focused sweep (backend mapper-chain + sap10_calculator worksheet
e2e + golden fixtures): 99 passed / 19 failed — identical baseline.
- Wider sweep (all sap10_calculator + sap10_ml): 1654 passed / 20
failed (same pre-existing failures).
- domain/sap10_ml/tests: 210/210 PASSED at new path.
- Pyright net-zero: heat_transmission.py 13, cert_to_inputs.py 35,
mapper.py 33, rdsap_uvalues.py 1 (all unchanged from baseline).
Note: `packages/domain/pyproject.toml` still declares
`packages = ["src/domain"]` for the hatchling wheel — that target
directory is now empty and the wheel build is effectively a no-op.
Retiring the workspace package or repointing the wheel is a follow-up.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
59 lines
1.9 KiB
Python
59 lines
1.9 KiB
Python
"""UCL per-band correction for Primary Energy Intensity.
|
|
|
|
Per Few et al. 2023 — "The over-prediction of energy use by EPCs in Great Britain"
|
|
(Energy & Buildings 288, 113024). Table 3 per-band linear correction.
|
|
|
|
Ported from `backend/ml_models/AnnualBillSavings.adjust_energy_to_metered`. Applied
|
|
to PEUI training labels per ADR-0007, *not* at runtime — the discontinuities at
|
|
EPC band boundaries that arose when this was applied post-prediction are what made
|
|
us fold it into the training labels instead.
|
|
|
|
Open question §15.14 in the PRD: the paper was calibrated on gas-heated, non-PV
|
|
homes in England and Wales rated under SAP 2012. The current implementation
|
|
extrapolates silently to all properties.
|
|
"""
|
|
|
|
from typing import Final
|
|
|
|
from datatypes.epc.domain.epc import Epc
|
|
|
|
|
|
_GRADIENTS: Final[dict[Epc, float]] = {
|
|
Epc.A: -0.10,
|
|
Epc.B: -0.10,
|
|
Epc.C: -0.43,
|
|
Epc.D: -0.52,
|
|
Epc.E: -0.70,
|
|
Epc.F: -0.76,
|
|
Epc.G: -0.76,
|
|
}
|
|
|
|
_INTERCEPTS: Final[dict[Epc, float]] = {
|
|
Epc.A: 28.0,
|
|
Epc.B: 28.0,
|
|
Epc.C: 97.0,
|
|
Epc.D: 119.0,
|
|
Epc.E: 160.0,
|
|
Epc.F: 157.0,
|
|
Epc.G: 157.0,
|
|
}
|
|
|
|
|
|
def apply_ucl_correction(peui_raw: float, band: Epc) -> float:
|
|
"""Return the metered-equivalent PEUI for an EPC's raw PEUI in a given band.
|
|
|
|
The Few et al. correction is one-sided: EPCs over-predict consumption, so the
|
|
correction only ever subtracts from PEUI. When the linear correction would
|
|
instead *add* to PEUI for an unusually low-PEUI property in its band, we clamp
|
|
to zero — leaving PEUI unchanged rather than inflating it.
|
|
"""
|
|
consumption_difference = _GRADIENTS[band] * peui_raw + _INTERCEPTS[band]
|
|
if consumption_difference > 0:
|
|
consumption_difference = 0.0
|
|
adjusted = peui_raw + consumption_difference
|
|
if adjusted < 0:
|
|
raise ValueError(
|
|
f"UCL-corrected PEUI is negative ({adjusted}) — "
|
|
f"impossible for raw PEUI {peui_raw} band {band.value}"
|
|
)
|
|
return adjusted
|