mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
5 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
fb3a84ce94 |
Slice 102b: cylinder storage loss via SAP 10.2 Tables 2/2a/2b
SAP 10.2 §4 line 7690 (full spec PDF p.136) defines the cylinder storage
loss cascade for any cert with a hot water cylinder lodged:
(54) = V × L × VF × TF (Table 2 absence-of-declared-loss branch)
(55) = (54) (no manufacturer's declared loss)
(56)m = (55) × n_m (per spec, n_m = days in month)
where
L = Table 2 (PDF p.158) Note 1 formula for the lodged insulation type
(factory-insulated cylinders: 0.005 + 0.55/(t+4.0); loose jacket:
0.005 + 1.76/(t+12.8))
VF = Table 2a (PDF p.158) Note 2 closed form (120/V)^(1/3)
TF = Table 2b (PDF p.159) base 0.60 for indirect / electric-immersion
cylinders, × 1.3 if no thermostat, × 0.9 if DHW separately timed
Prior, `water_heating_from_cert` hard-coded `solar_storage_monthly_kwh
= zero12` and `_water_heating_worksheet_and_gains` had no path to
populate it. The new `cylinder_storage_loss_monthly_kwh` helper in
`worksheet/water_heating.py` exposes Tables 2 / 2a / 2b as small typed
functions plus a composite; the cert-side orchestrator in
`rdsap/cert_to_inputs.py::_cylinder_storage_loss_override` resolves
the lodged cylinder fields and injects the override.
Code → litres mapping ground-truthed against worksheet (47) line refs
in /sap worksheets/Additional data with api/<cert>/dr87-*.pdf for the
7-cert ASHP cohort: code 3 → 160 L (Medium, 6 certs) and code 4 →
210 L (Large, cert 9418). Codes 2 / 5 / 6 (Normal / Inaccessible /
Exact) absent from the cohort and not yet mapped.
Cylinder insulation type code → "factory_insulated" mapping
(_CYLINDER_INSULATION_TYPE_FACTORY = 1) ground-truthed against all 7
ASHP cohort worksheets ("Foam" lodgement → SAP 10.2 Table 2 Note 2
"factory-insulated cylinder where the insulation is applied in the
course of manufacture irrespective of the insulation material used").
RdSAP §3 default table (PDF p.57) — "Hot water separately timed:
Post-1998 boiler: Yes" — applied to heat-pump main heating systems
(cat 4) per the cohort worksheet evidence.
Cert 0380 (Mitsubishi ASHP, 160 L factory 50 mm, thermostat + separately
timed) lands the spec formula at worksheet (56) Jan = 36.9530 kWh/month
(test pinned at 1e-4); HW kWh/yr 242.21 → 431.38, recovering ~189 kWh/yr
of cylinder loss the cascade was previously dropping.
Cohort regression: cert 0390-2954 (oil boiler + 160 L cylinder) tightens
PE residual -28.6783 → -27.5026 kWh/m² and CO2 residual -2.7640 →
-2.6570 t/yr — both move closer to the lodged values (improvement).
Re-pinned with a slice-102b note.
Closed boiler chain tests (001479, 0330, 9501) unaffected: those certs
lodge has_hot_water_cylinder=false so the override stays None and the
existing zero-storage-loss default fires.
|
||
|
|
a736db3f4a |
Slice 101b: HP cert 0380 — cavity+EWI wall U + Table 11 cat-4 secondary
Two HP-specific cascade gaps blocking cert 0380:
(a) Cavity wall + filled cavity + external insulation:
Cert 0380's `walls[0].description="Cavity wall, filled cavity and
external insulation"` with `wall_insulation_type=6` +
`wall_insulation_thickness="100mm"`. RdSAP 10 §4-4 (page 73) lists
"cavity plus external" as a distinct insulation type code (6 in
the API schema; 7 is "cavity plus internal"). The U-value is the
composite U = 1 / (1/U_filled + R_ins) per §5.8 page 40 + Table 14
R-value lookup, with the cascade-2-d.p. round matching the dr87
worksheet's column display.
For cert 0380: U_filled (age D)=0.7 + R_ins (100mm @ λ=0.04)=2.5
→ U_unrounded=0.2545 → rounded 0.25 (worksheet exact). Walls HLC
14.87 → 11.6150 (= worksheet 11.6150). (37) total fabric heat
loss 99.34 → **96.0889** (= worksheet 96.0889 EXACT).
Added `WALL_INSULATION_CAVITY_PLUS_EXTERNAL: Final[int] = 6` and
`WALL_INSULATION_CAVITY_PLUS_INTERNAL: Final[int] = 7` constants
+ `_WALL_INSULATION_LAMBDA_W_PER_MK = 0.04` default thermal
conductivity. New `u_wall` branch fires when cavity + composite
insulation type + non-zero thickness.
(b) SAP 10.2 Table 11 secondary fraction — missing cat-4 entry:
The dict `_SECONDARY_HEATING_FRACTION_BY_CATEGORY` had entries
for cats 1/2/3/5/6/7/10 but DID NOT include cat 4 (heat pump),
despite the inline comment explicitly noting "Cat 4 (heat pump):
0.00 (HP eff includes any secondary)". Cert 0380 lodges
`secondary_heating_type=691` + `main_heating_category=4` (HP,
PCDB idx 104568), so the cascade fell through to the DEFAULT
fraction 0.10 — billing 547 kWh × 13.19 p/kWh = £72 as
"secondary heating" that the worksheet correctly shows as £0.
Added `4: 0.00` to the dict.
Effect on cert 0380 API path:
- walls HLC 14.87 → 11.62 (worksheet exact)
- (37) total HLC 99.34 → 96.09 (worksheet exact)
- main_heating_cost £282 → £314 (worksheet £316)
- secondary_heating £72 → £0 (worksheet £0)
- sap_continuous 87.62 → 90.48 (Δ -0.89 → +1.97 — over-correcting
because hot-water cascade is still cascade-£66 vs worksheet £204
including electric shower; HP HW-COP + electric-shower cost are
the next slices).
No golden cert residual shifts (cohort certs don't lodge HP cat 4
or composite cavity+EWI walls).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
485a74028e |
Slice 96: flat-roof U-value defaults — RdSAP 10 §5.11 Table 18 col (3)
Cert 0330 (mid-terrace boiler, Summary_000897.pdf) Summary path was at
Δ +0.4667 SAP vs worksheet 61.5993 because Ext1's flat roof fell through
`_ROOF_BY_AGE` (Table 18 column (1), pitched-roof "between joists"
defaults) to 0.40 W/m²K for age D — the spec value is 2.30 W/m²K from
column (3) "Flat roof" (RdSAP 10 spec page 45).
RdSAP 10 §5.11 Table 18 column (3) verbatim:
Age A,B,C,D → 2.30; E → 1.50; F → 0.68; G → 0.40; H,I → 0.35;
J,K → 0.25; L → 0.18; M → 0.15.
Footnote (a): "If the roof insulation is 'none' use U = 2.3 (all roof
types, except for thatched roofs)" — confirms the col-3 entries for
old ages are the uninsulated row, applied because cert 0330's Ext1
lodges "Flat" construction with no measured insulation thickness.
Changes:
- `_FLAT_ROOF_BY_AGE` added in rdsap_uvalues.py
- `u_roof` gains `is_flat_roof: bool = False` parameter
- `heat_transmission_from_cert` detects flat roofs from
`part.roof_construction_type` ("flat" substring) and routes through
the new column.
Effect on baseline:
- cert 0330 Summary chain test: RED Δ+0.4667 → GREEN at 1e-4 (worksheet
total fabric heat loss 237.7549 W/K matches cascade to 4 d.p.)
- cert 001479 Layer 4 chain test: unchanged (Main pitched, no flat
components)
- cohort certs 000477/000516: unchanged (no flat roofs)
- golden cert 0300-2747-7640-2526-2135: SAP residual +1 → 0 (improved),
Ext1 is genuinely flat; pe/co2 residuals re-pinned. The dwelling has
the same Main-pitched + Ext1-flat shape as cert 0330; same fix.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
a7b08a4e8f |
refactor: move docs/sap-spec/ contents into domain/sap10_calculator/
Locality of reference — SAP-specific docs, specs, and runtime data
now live alongside the calculator that consumes them, mirroring the
prior packages→domain layout moves.
Move targets:
- Narrative MDs → domain/sap10_calculator/docs/
NEXT_AGENT_PROMPT.md, HANDOVER_NEXT.md, SAP_CALCULATOR.md
- Spec PDFs → domain/sap10_calculator/docs/specs/
RdSAP 10 Specification 10-06-2025.pdf
PCDF_Spec_Rev-06b_12_May_2021.pdf
sap-10-2-full-specification-2025-03-14.pdf
sap-10-3-full-specification-2026-01-13.pdf
- PCDB runtime data → domain/sap10_calculator/tables/pcdb/data/
pcdb10.dat (8.3MB) + 7× pcdb_table_*.jsonl (18MB total)
Path code rewrites (load-bearing):
- tables/pcdb/__init__.py: replaced parents[4]/'docs'/'sap-spec' with
Path(__file__).resolve().parent/'data' for Table 105 JSONL loading.
- tables/pcdb/postcode_weather.py: same rebase for the pcdb10.dat path
read by _postcode_climate_table().
- tables/pcdb/etl.py __main__: same rebase for the manual ETL invocation
(source + output_dir both now point inside the package).
- tests/test_pcdb_etl.py: _PCDB_DAT_PATH now derives from
parents[1]/'tables'/'pcdb'/'data' (was parents[3]/'docs'/'sap-spec').
Citation rewrites:
- 12 .py docstrings and 4 .md docs (ADRs + READMEs + narrative docs)
had `docs/sap-spec/<file>` strings rewritten to their new locations.
- Two cases where the catch-all sed misfired (an ADR-0009 line about a
PCDB extract; the pcdb __init__.py docstring about ETL output) were
hand-corrected to point at tables/pcdb/data/ rather than docs/specs/.
docs/sap-spec/ is now empty (will be removed in a follow-up sweep or
left as a vestigial empty dir for future repurposing). ADRs 0009 and
0010 remain at docs/adr/ — they're part of the chronological
cross-cutting decision log, not calculator-specific narrative.
Verified:
- Calculator's 1e-4 production gate
(test_api_001479_full_chain_sap_matches_worksheet_pdf_exactly) GREEN.
- Wider sweep (domain/sap10_calculator/ + domain/sap10_ml/): 1654
passed / 20 failed — exact pre-move baseline. All 20 failures
pre-existing (10 hand-built skeleton + 4 cohort chain + 6 cohort
diff).
- Pyright net-zero on the 4 touched runtime/test files (0 errors)
and unchanged on heat_transmission.py (13) / cert_to_inputs.py (35) /
mapper.py (33).
Co-Authored-By: Claude Opus 4.7 <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 (
|