Slice S0380.63: SAP 10.2 Table 4f additive pumps_fans components — Main 2 flue + solar HW

Cert 000565 has two Table 4f line items the existing
`_PUMPS_FANS_KWH_BY_MAIN_CATEGORY` lookup misses:
- (230e) Main 2 gas-combi flue fan = 45 kWh (Main 1 is the HP, so
  Main 1's category doesn't carry the gas flue fan — the 2-main
  cert has its flue fan on Main 2)
- (230g) Solar HW pump = 80 kWh (= [25 + 5×H1] × 2 per Table 4f
  with H1 = 3 m² collector aperture default)

New `_table_4f_additive_components(epc)` sums these on top of the
Main 1 category base. Per SAP 10.2 Table 4f page 174:
  - Gas boiler flue fan (fan-assisted): 45 kWh
  - Solar thermal system pump (electrically powered):
    [25 + 5×H1] × (2000 ÷ 1000) kWh, where H1 is the solar
    collector aperture area in m²

H1 currently defaults to 3.0 m² (cert 000565 lodging — flat-panel
3 m² is the most common UK domestic solar HW spec). TODO: extend
the Elmhurst schema + extractor to lodge `solar_collector_aperture_
area_m2` from Summary §16 so the cascade reads the actual value.

Cert 000565 cascade impact:
- pumps_fans_kwh_per_yr: 130 → 255  (Δ −122.52 → +2.48)
  The remaining +2.48 surplus is the (230a) MEV component
  miscounted in the 130 default base — Main 1 HP should give base
  = 0 per Table 4f ("circulation pump in COP"), but mapper-side
  HP-category derivation is its own deferred slice. With MEV
  properly wired and HP category = 4, the cert closes exactly to
  the 252.52 worksheet pin.
- total_fuel_cost_gbp: Δ −167.49 → −150.93 (the +125 kWh delta
  bills at the ALL_OTHER_USES blended rate £0.1324 → +£16.55)
- sap_score_continuous: Δ +1.91 → +1.72

Deferred (out of slice scope):
- (230a) MEV / MVHR — needs PCDB MEV lookup table + IUF derivation.
  For cert 000565 worksheet shows MEV = 127.5 kWh = IUF × SFP ×
  1.22 × V (V = 641.59 m³, PCDF 500755 SFP = 0.1274, IUF ≈ 1.278
  derived from worksheet). Next slice.
- HP SAP code (224, 211-227, 521-524) → main_heating_category=4
  in mapper — would change pumps_fans Main 1 base from 130 default
  to 0 (correct per Table 4f HP row). Currently blocked on MEV
  cascade landing — without MEV, dropping base from 130 to 0
  worsens the residual.

Cohort regression check: 427 pass + 10 expected 000565 fails. The
14 Elmhurst Summary fixtures + JSON fixtures + cohort ASHP all
either: (a) have no Main 2 lodged → no flue contribution, or
(b) have no solar HW lodged → no pump contribution. The additive
helper returns 0 for those certs.

Spec source: SAP 10.2 §10a Table 4f page 174 (verified verbatim).
RdSAP 10 references Table 4f via §19.1.

Pyright net-zero (34 / 34).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-05-29 08:06:22 +00:00 committed by Jun-te Kim
parent 0786921357
commit 878088bf2d

View file

@ -206,6 +206,58 @@ _PUMPS_FANS_KWH_BY_MAIN_CATEGORY: Final[dict[int, float]] = {
# entry HP certs fell through to the 130 kWh/yr DEFAULT
# and over-billed £17/yr at electricity rate.
}
# SAP 10.2 Table 4f (page 174) — flue fan kWh for a gas-fired boiler
# with fan-assisted flue (row "Gas boiler flue fan"). Liquid-fuel
# (oil) boilers use 100; gas-fired heat pumps and warm-air also 45.
_TABLE_4F_GAS_FLUE_FAN_KWH: Final[float] = 45.0
# SAP 10.2 Table 4f row "Solar thermal system pump, electrically
# powered" — formula `[25 + 5×H1] × 2`. H1 is the solar collector
# aperture area in m². For cert 000565 the lodged 3 m² flat-panel
# array gives 2 × (25 + 15) = 80 kWh; without aperture lodging the
# cohort fall-through uses a 3 m² default.
_TABLE_4F_SOLAR_HW_PUMP_DEFAULT_H1_M2: Final[float] = 3.0
def _table_4f_additive_components(epc: EpcPropertyData) -> float:
"""Sum the SAP 10.2 Table 4f line items that the base
`_PUMPS_FANS_KWH_BY_MAIN_CATEGORY` lookup doesn't already cover —
i.e. components driven by per-cert lodgements rather than Main 1's
heating category alone.
Currently wired:
- (230e) Main 2 gas-boiler flue fan 45 kWh when a Main 2 system
is lodged with `fan_flue_present=True` and a gas fuel type.
Cert 000565 (Main 1 HP + Main 2 gas combi via WHC 914) is the
first fixture exercising this.
- (230g) Solar HW pump `[25 + 5×H1] × 2` per Table 4f. H1
defaults to 3 aperture (cert 000565 lodging) when the
schema doesn't carry the lodged value. TODO: parse the
Elmhurst §16 aperture area into the schema.
Not yet wired:
- (230a) MEV / MVHR `IUF × SFP × 1.22 × V` per Table 4f +
Table 4g defaults. PCDB MEV / MVHR lookup table is not yet in
the codebase; defer to next slice.
- (230f) Combi keep-hot 600 / 900 kWh per Table 4f when the
cert lodges keep-hot on the gas combi.
- (230b) Warm-air heating fans + (230c) for warm-air pump.
- (230h) WWHRS pump.
"""
total = 0.0
details = epc.sap_heating.main_heating_details if epc.sap_heating else []
if len(details) >= 2:
main_2 = details[1]
# Gas fuel codes per Table 32 + their RdSAP API equivalents.
main_2_fuel_is_gas = main_2.main_fuel_type in {1, 2, 3, 5, 7, 9, 26, 27}
if main_2.fan_flue_present and main_2_fuel_is_gas:
total += _TABLE_4F_GAS_FLUE_FAN_KWH
if epc.solar_water_heating:
total += (
25.0 + 5.0 * _TABLE_4F_SOLAR_HW_PUMP_DEFAULT_H1_M2
) * 2.0
return total
# SAP10.2 Table 6d note 1: "average or unknown" overshading is the
# default for existing dwellings. RdSAP doesn't lodge a per-dwelling
# overshading code so §5 always uses AVERAGE → Z_L = 0.83.
@ -3017,6 +3069,9 @@ def cert_to_inputs(
main_category if main_category is not None else -1,
_DEFAULT_PUMPS_FANS_KWH_PER_YR,
)
# SAP 10.2 Table 4f (p.174) — additive components on top of the
# Main 1 category base. Each component is per-cert-lodging:
pumps_fans_kwh += _table_4f_additive_components(epc)
primary_age = (
epc.sap_building_parts[0].construction_age_band if epc.sap_building_parts else None
)