mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
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>
|
||
|---|---|---|
| .. | ||
| addresses | ||
| sap10_calculator | ||
| sap10_ml | ||
| tasks | ||
| postcode.py | ||