mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
5 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a7761ea83f |
fix(fuel): map gov-API community fuels 30/31/32 (waste/biomass/biogas) to Table-12 community rows, gated on heat-network context
The gov-API `main_fuel_type`/`water_heating_fuel` enum (epc_codes.csv) codes 30="waste combustion (community)", 31="biomass (community)", 32="biogas (community)" collide in VALUE with the Table-32 electricity codes 30 (standard rate), 31 (7-hour low) and 32 (7-hour high). All three sit in `_ELECTRIC_FUEL_CODES`, so `is_electric_fuel_code` flagged a community-scheme main as electric and `_is_electric_main` routed its cost through the off-peak electricity branch — BYPASSING the heat-network rate in `_heat_network_factor_fuel_code`. Cert 8536 (biomass community, SAP code 301) was billing at 5.5 p/kWh grid electricity instead of the 4.24 p/kWh heat-network rate → -17.2 SAP. Per RdSAP 10 §C / SAP 10.2 Table 12 (PDF p.191) the community waste/biomass/biogas rows are codes 42/43/44 (the same rows the backwards-compat enum codes 11/12/13 already map to). Add 30->42, 31->43, 32->44 to both API fuel-translation tables. The remap CANNOT be global (`canonical_fuel_code`): the cascade uses the bare Table-32 code 30 internally as `_STANDARD_ELECTRICITY_FUEL_CODE` (the RdSAP no-water-heating immersion default writes `water_heating_fuel=30`), so a blanket remap mis-prices genuine grid electricity as community waste (cert 2211 regressed +16 SAP in a prototype). Instead `_heat_network_community_fuel_code` translates only when `_is_heat_network_main` is true, at the `_main_fuel_code` / `_water_heating_fuel_code` fuel-TYPE boundary, where the community meaning is unambiguous. Per the strict-raise principle ([[reference-unmapped-sap-code]]), a heat-network main lodging a colliding community fuel the table doesn't cover raises `UnmappedSapCode` rather than silently falling through to the same-numbered electricity code. Eval (API SAP vs lodged): cert 8536 -17.25 -> -6.51, cert 5036 -6.29 -> +1.36; mean|err| 1.329 -> 1.312, within-1.0 67.88% -> 67.99%, within-2.0 81.74% -> 81.85%, within-0.5 held at 53.14%, 909 computed / 0 raises. No golden / calculator regressions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
19235d1144 |
fix(fuel): canonicalise colliding gov-API solid-fuel codes (anthracite/coal) at the fuel-type boundary
A coal main (gov-API main_fuel_type=33) was priced at the electricity 10-hour low rate (7.5 p) and anthracite (5) at the bulk-LPG rate (12.19 p), because the shared price/CO2/PE lookups check Table-32/12-code membership BEFORE translating the API enum — and codes 5/33 collide with a different-fuel Table code. This drove the cohort's single worst cert (2100 anthracite, -61 SAP). `is_electric_fuel_code(33)` also wrongly classified the coal main as electric. The gov-API fuel enum (confirmed by description-vs-code audit on main_heating[].description): 5=anthracite, 33=coal, 9=dual-fuel, 20/25/31=community. The collision can't be resolved inside the shared table functions — code 33 is ALSO the electricity-10h TARIFF code used by the dual-rate CO2/PE split (golden 000565), so normalising there breaks electricity certs. Instead `canonical_fuel_code` normalises the colliding SOLID-fuel enums (5->15 anthracite, 33->11 house coal) at the fuel-TYPE boundary in `_main_fuel_code` / `_water_heating_fuel_code`, where the code is known to be a fuel type (never a tariff code). Scoped to anthracite (5) + coal (33) — the unambiguous large mispricings. Dual-fuel (9, 0.45 p delta) and community (20/25/31, heat-network path) are deferred (noted in `_GOV_API_COLLISION_FUELS`). API SAP eval: mean|err| 1.424 -> 1.329 (the -61 anthracite outlier 2100 -> -11, residual now fabric); within-0.5 53.1% (flat); 909 computed, 0 raises. Golden + Elmhurst regression green (the shared table functions are unchanged, so the electricity-tariff CO2/PE path is untouched). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
8e86de2257 |
S0380.182: community-heating CHP+boilers CO2/PE credit (§12b/13b) — closes CH2/CH4 CO2+PE
SAP 10.2 worksheet block 12b (CO2) / 13b (PE) for community heating
"CHP and boilers" (SAP code 302). Per unit of network heat fuel
H = (307)+(310) the effective generation factor is:
chp×100/(362)×f_fuel − chp×(361)/(362)×f_disp + (1−chp)×100/(367)×f_fuel
(363)/(463) CHP fuel = chp_frac × 100/heat_eff × f_fuel
(364)/(464) less credit = −chp_frac × elec_eff/heat_eff × f_disp
(368)/(468) boiler fuel = (1−chp_frac) × 100/boiler_eff × f_fuel
f_fuel = Table 12 heat-network fuel factor (the CHP unit and the back-up
boilers burn the same community fuel — verified vs CH2 gas / CH4 oil /
CH6 coal worksheets (363)/(368)); f_disp = Table 12f (PDF p.196) credit
for the CHP-generated electricity. RdSAP 10 §C (p.58) defaults: heat eff
50% (362), electrical eff 25% (361), boiler eff 80% (367); CHP heat frac
0.35 per-cert via community_heating_chp_fraction.
New `_heat_network_code_302_effective_factor` + Table 12f flexible
constants (0.420 CO2 / 2.369 PE) + RdSAP §C efficiency constants, wired
into all four factor helpers (main + HW, CO2 + PE) ahead of the existing
single-fuel / 1-over-heat-source-eff path. The worksheet (368)/(468)
boiler emissions DISPLAY rounded/mis-aligned in the PDF, but the
(373)/(473)/(386)/(486) totals reconcile only with the boiler at the
full Table 12 factor — verified EXACT.
Two spec citations applied:
- Table 12f flexible-operation default for RdSAP community CHP is an
Elmhurst engine choice (Table 12f notes make "standard" the default);
mirrored per [[feedback-software-no-special-handling]] and documented
in SAP_CALCULATOR.md §8.3.
- Table 12 heat-network oil/biodiesel CO2 (codes 53/56) corrected
0.298 → 0.335 per Table 12 (p.189) "assumes 'gas oil'"; the code-302
oil cascade (CH4) was the first to exercise it. PE 1.180 was already
correct. No other variant uses these codes (no regression).
Closures (CO2 + PE only — the CHP credit does not touch cost/SAP):
CH2 (CHP/Gas) CO2 −1411.49→+0.0000, PE +1331.23→+0.0000 EXACT
CH4 (CHP/Oil) CO2 −4378.24→−0.0000, PE +319.81→−0.0000 EXACT
CH6 (CHP/Coal) CO2/PE re-pinned (+2411.54 / +5023.48) — its worksheet
lodges a manual DLF=1.0 the Summary doesn't carry, so
cascade DLF=1.45 over-scales H; same root as the CH6
SAP −7.49 / cost +£172 (separate DLF front).
CH2/CH4 are now CO2+PE-exact but still carry the heat-network cost/SAP
residual (+0.5277 SAP / −£12.16 cost, exposed by S0380.175 — cost-side,
untouched here). CH3 unchanged (code 304 community-HP COP front).
Corpus state: 37 variants EXACT on all four metrics (incl. CH1);
remaining residuals are CH2/CH4 cost+SAP, CH3 CO2+PE (HP COP), CH6
all-metric (DLF quirk). 2223 pass + 1 skip + 0 fail (tolerances 1e-4 all
metrics per S0380.181); pyright net-zero 43→43.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
68401c517a |
refactor: lift-and-shift packages/domain/src/domain/ml → domain/sap10_ml
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 (
|
||
|
|
29ac35ccbe |
refactor: lift-and-shift packages/domain/src/domain/sap → domain/sap10_calculator
Migration of the SAP 10.2 calculator package from the uv-workspace
src-layout (`packages/domain/src/domain/sap`) to the root-level layout
(`domain/sap10_calculator`), matching the pattern already used by
`domain.addresses` / `domain.tasks` / `domain.postcode`.
Changes:
- `git mv packages/domain/src/domain/sap → domain/sap10_calculator`
(92 files; git auto-detected all as renames so blame/history is
preserved).
- Subpackage rename: `domain.sap` → `domain.sap10_calculator`. 48
Python files rewritten (`from domain.sap.X` → `from domain.sap10_
calculator.X`); zero remaining `domain.sap` refs after the sed pass.
- Path-string updates: 3 .py files (test fixtures + xlsx loader) +
6 markdown docs (CONTEXT.md, 2 ADRs, 3 sap-spec docs, sap10_
calculator/README.md) had hard-coded `packages/domain/src/domain/
sap/...` paths rewritten to `domain/sap10_calculator/...`.
- `Path(__file__).parents[N]` rebasing: the old tree was 3 levels
deeper than the new one (`packages/domain/src/`), so 4× `parents[7]`
became `parents[4]` and 1× `parents[6]` became `parents[3]` across
`tables/pcdb/{__init__.py, postcode_weather.py, etl.py}`,
`worksheet/tests/_xlsx_loader.py`, and `tests/test_pcdb_etl.py`.
- PEP 420 namespace package: deleted both `domain/__init__.py`
(root + workspace, both load-bearing only as empty/docstring) so
Python combines `domain.sap10_calculator` (root) and `domain.ml`
(workspace) into one namespace package. Confirmed via
`domain.__path__ == ['/workspaces/model/domain',
'/workspaces/model/packages/domain/src/domain']`. Without this,
the root `domain/__init__.py` shadowed the workspace one and
`domain.ml` was unreachable.
Verified:
- Full sweep (`backend/documents_parser/tests/test_summary_pdf_
mapper_chain.py + domain/sap10_calculator/worksheet/tests/test_
e2e_elmhurst_sap_score.py + domain/sap10_calculator/rdsap/tests/
test_golden_fixtures.py`): 99 passed / 19 failed — exact same
counts as pre-refactor. All 19 failures pre-existing (9 hand-built
001479 + 6 cohort diff + 4 cohort chain non-spec).
- Wider sweep (all sap10_calculator + domain.ml): 1654 passed /
20 failed (the +1 vs the focused sweep is the pre-existing
`test_roof_insulated_assumed_with_ni_thickness_uses_50mm_per_
section_5_11_4` which was already failing on the previous baseline).
- Pyright net-zero on the three load-bearing baselines:
`heat_transmission.py` 13, `cert_to_inputs.py` 35, `mapper.py` 33.
Lift-and-shift only — no semantic renames (`Sap10Calculator` stays
`Sap10Calculator`), no testpaths edits in pytest.ini (sap tests
continue to be invoked by explicit pytest paths).
Note: `domain.ml` still lives at `packages/domain/src/domain/ml/`.
Migrating it would close out the dual-`domain/` layout but is
out of scope for this commit.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Renamed from packages/domain/src/domain/sap/tables/table_12.py (Browse further)