ADR-0058: Sub-Ladder PV Configurations give small roofs the array their caps allow 🟪

Grilled decisions (issue #1496): 2-panel floor, every rung 2..floor(cap),
pro-rata per-segment yield (conservative), fill-by-generation, same cap
regime as today (no new roof gate), symmetric battery pairing. Glossary
gains "Sub-Ladder Configuration" ("trimmed"/"synthesized" deliberately
avoided — both collide with existing vocabulary).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-07-07 21:57:53 +00:00
parent bd01f403c9
commit 3ef432c8b2
2 changed files with 95 additions and 1 deletions

View file

@ -317,9 +317,13 @@ The bound that limits a **Solar PV Recommendation**'s array to the dwelling's *o
_Avoid_: roof-area haircut (overloads the MCS coverage haircut), double-property halving (the legacy semi-detached-only mechanism this replaces)
**Solar PV Recommendation**:
The single "Solar PV" **Recommendation** for a Property, carrying competing whole-array **Measure Options** built from the **Solar Potential** (ADR-0026). Up to **five conservatively-sized array configs** (capped, ranked by energy generation as the size-suitability proxy; deliberately *not* full-roof — imagery can miss obstructions, so a coverage/edge-setback haircut applies per MCS, and the **Dwelling-Roof Cap** bounds the array to the dwelling's own roof under **Footprint Conflation**), each offered **with and without a battery** (≤ 10 Options). Each Option is priced at a **single price point** from the rate sheet (kWp band + scaffolding-by-elevation + optional battery/diverter) — no multi-product price variants. The PV Overlay sets `photovoltaic_arrays` (one per segment: peak_power, orientation, pitch, overshading) plus `pv_diverter_present`, `pv_connection`, and **`is_dwelling_export_capable=True`** (an export meter is ensured post-install); the battery variant adds `pv_batteries`.
The single "Solar PV" **Recommendation** for a Property, carrying competing whole-array **Measure Options** built from the **Solar Potential** (ADR-0026). Up to **five conservatively-sized array configs** (capped, ranked by energy generation as the size-suitability proxy; deliberately *not* full-roof — imagery can miss obstructions, so a coverage/edge-setback haircut applies per MCS, and the **Dwelling-Roof Cap** bounds the array to the dwelling's own roof under **Footprint Conflation**), each offered **with and without a battery** (≤ 10 Options). Each Option is priced at a **single price point** from the rate sheet (kWp band + scaffolding-by-elevation + optional battery/diverter) — no multi-product price variants. The PV Overlay sets `photovoltaic_arrays` (one per segment: peak_power, orientation, pitch, overshading) plus `pv_diverter_present`, `pv_connection`, and **`is_dwelling_export_capable=True`** (an export meter is ensured post-install); the battery variant adds `pv_batteries`. When no Google rung fits under the caps, **Sub-Ladder Configurations** (ADR-0058) supply the small rungs instead of dropping PV entirely.
_Avoid_: solar bundle (PV is competing sized Options, not one fixed bundle like ASHP)
**Sub-Ladder Configuration**:
A PV array configuration Modelling derives **below Google's smallest offered rung**, so a small roof gets the array its caps allow instead of no PV at all (ADR-0058). Derived only when no `solarPanelConfigs` rung fits under the caps and at least 2 panels do: north planes dropped first, the rung's panels filled from segments ranked by per-panel yield (the **Dwelling-Roof Cap**'s fill-by-generation precedent), each kept segment's yearly energy scaled **pro-rata** (conservative — Google places panels best-first). One rung per whole panel count from **2** (the install floor) up to the cap; both caps (0.70 × max, **Dwelling-Roof Cap**) unchanged. Flows through the standard Option construction (battery pairing, pricing, PV Overlay) with no special cases.
_Avoid_: trimmed configuration ("trimmed" already means the north-segment drop), synthesized configuration ("synthesis" belongs to Reduced-Field Synthesis / EPC Prediction), minimum array (names the 2-panel floor, not the derived-rung concept)
**Solar PV Eligibility**:
The rule fixing whether the **Solar PV Recommendation** is offered (ADR-0026): a **house or bungalow**, **not listed and not heritage**, with **no existing PV**, and a **feasible Solar Potential** (the Google Solar API returned usable, non-north roof segments). Crucially a **conservation area does NOT block PV** — panels are offered (installed sympathetically), so the planning gate is `not blocks_internal` (listed/heritage only), **not** `blocks_external`; this is the opposite of an external fabric measure like EWI, and is deliberate (legacy + planning practice allow conservation-area PV on non-prominent roofs). Flats/maisonettes (building-level shared roof) are deferred.
_Avoid_: blocking conservation-area PV (only listed/heritage block), roof-area-from-floor-area estimate (eligibility uses the real Solar Potential)

View file

@ -0,0 +1,90 @@
---
status: accepted (extends ADR-0026 and ADR-0038)
---
# Sub-Ladder PV Configurations give small roofs the array their caps allow
ADR-0026 sizes a PV array by picking from Google's `solarPanelConfigs` ladder
under a conservative cap (0.70 × `maxArrayPanelsCount`, min'd with the
ADR-0038 Dwelling-Roof Cap when it resolves). Google's ladder typically starts
at **4 panels**, so on a small roof the cap falls below the smallest rung and
the feasible set is **empty** — the dwelling silently gets **no Solar PV
Measure Option at all** rather than a small array. ADR-0038 already states the
intent "never emit a zero-panel or unbounded array", but that governs the cap
fallback, not the ladder; the ladder cliff violated its spirit.
Motivation: portfolio 824 / scenario 1278 audit (2026-07-07). Property 750701
(UPRN 100010328594) — an unrestricted house short of goal C at D/67 on an
unlimited budget — has `maxArrayPanelsCount = 5` with rungs at 4 and 5 panels;
cap 0.70 × 5 = 3.5 < 4 no PV offered, while ~2 SAP of feasible PV would
plausibly reach C. The cliff affects every small-roofed house in every
portfolio, silently.
## Decision
**When no Google rung fits under the caps, derive Sub-Ladder Configurations —
array rungs below Google's smallest offered configuration — instead of
dropping PV entirely.**
- **Trigger**: the feasible set (north-dropped Google configs with
`panels_count <= panel_cap`) is empty AND `floor(panel_cap) >= 2`. When
`floor(panel_cap) < 2`, or nothing survives the north-segment drop, the
outcome stays no-PV — a roof that cannot take 2 panels is never offered
them.
- **Floor = 2 panels** — the practical install floor. Economics stay the
Optimiser's job (ADR-0024/0026 doctrine: eligibility and sizing encode
physical installability only); on least-cost-to-target a poor-£/kWh small
array is bought exactly when it is the only route to the goal band, which
is when the landlord wants it offered.
- **Every rung, not one**: emit a Sub-Ladder Configuration at every whole
panel count from 2 to `floor(panel_cap)` (in practice at most rungs 2 and
3 — the trigger implies `floor(panel_cap) <= 3`). Preserves ADR-0026's
genuine size/cost choice; the Optimiser stops at the cheapest array that
reaches goal.
- **Derivation**: start from Google's smallest rung, drop north planes first
(as for every config), then fill the rung's panel count from the remaining
segments **ranked by per-panel yield** — the ADR-0038 "fill by generation"
precedent.
- **Yield accounting is pro-rata per segment**: a kept segment contributes
`yearly_energy × kept_panels / segment_panels`. Provably conservative —
Google places panels best-first within a segment, so the kept panels' true
yield ≥ the pro-rata figure. Needs no new `SolarPotential` projection
fields.
- **Cap regime unchanged, no new gate**: the same `min(0.70 × max,
dwelling-roof budget)` applies; when the Dwelling-Roof Cap cannot resolve,
the 0.70-Google cap alone governs — exactly as it already does for full
configs, and a smaller array is strictly more conservative than what those
dwellings are offered today.
- **Downstream unchanged**: Sub-Ladder Configurations flow through the
existing option construction — with-and-without battery pairing, single
price point, PV Overlay — with no special cases.
## Considered options
- **Floor the cap at Google's smallest rung** (offer the 4-panel config when
the dwelling roof confirms it fits). Rejected: breaches the 70%
conservatism the cap encodes — the cap exists precisely to not trust
Google's maximum placement wholesale.
- **Exact per-panel yields** (extend the projection to carry Google's
`solarPanels` list and sum true kept-panel energies). Rejected for now:
new plumbing for precision that cannot change an Optimiser decision at 23
panels; pro-rata errs conservative. Revisit if sub-ladder sizing ever
extends beyond the small-roof trigger.
- **3-panel floor**. Rejected: gives up the smallest roofs (Google max 34
panels), where 2 panels is all the cap allows and may still clear a
near-boundary goal.
- **Require the Dwelling-Roof Cap to resolve before deriving**. Rejected:
stricter than today's treatment of full configs on unresolved roofs —
an inconsistent trust model.
## Consequences
- Small-roofed houses gain a PV path toward goal; on the motivating property
the expected offer is a 3-panel rung (~950 kWh/yr pro-rata from the
4-panel rung's 1,278). Follow-up: re-model 750701 after implementation to
confirm the +2 SAP to band C.
- A no-PV outcome on a small roof now always means `floor(panel_cap) < 2`,
no usable non-north segment, or ineligibility — never the ladder cliff.
- The audit skill's small-roof expectation (a `solar` row with no `solar_pv`
candidate is not automatically the solar-blind batch bug) shrinks to the
sub-2-panel residue once portfolios are re-modelled.