Model/domain
Khalim Conn-Kowlessar 49de18e83a Slice S0380.45: wire β-split into PE cascade per SAP 10.2 Appendix M1 §8
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>
2026-05-28 18:34:56 +00:00
..
addresses get rid of comments 2026-05-20 13:21:11 +00:00
sap10_calculator Slice S0380.45: wire β-split into PE cascade per SAP 10.2 Appendix M1 §8 2026-05-28 18:34:56 +00:00
sap10_ml Slice S0380.26: RdSAP10 §5.8 dry-lining adjustment on alt walls — closes cert 7700 -0.44 → +5e-5 2026-05-28 10:56:11 +00:00
tasks added postcode splitter rewrite to ddd 2026-05-19 16:35:09 +00:00
postcode.py get rid of comments 2026-05-20 13:21:11 +00:00