mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
slice S-B19: PV generation cost credit (SAP 10.2 Appendix M)
Wires photovoltaic_arrays into the calculator as a per-kWh cost credit against the ECF numerator. Total annual PV kWh = sum(peak_power_kw) × 850 (UK-average yield per Appendix M, single national figure since ratings use UK-average weather per S-B18). Credit rate is Table 12 code 60 (PV export tariff) — 5.59 p/kWh under SAP spec prices, 13.19 p/kWh under cert-calibration prices. This is the first slice from the worksheet-driven phase (per user suggestion). PV was identified as a clear systemic gap that probe- driven iteration hadn't surfaced because only ~5-10% of certs have PV and the corpus probe is biased toward the most-frequent shapes. 100-cert: MAE 4.39 → 4.49 (small regression; bias -0.17 → -0.07) 300-cert: MAE 5.44 → 5.45 (essentially flat; bias 0.11 → 0.22) Net spec-correct, aggregate MAE neutral. The certs that DO have PV should see the right cost story now; ML residual will pick up the fidelity gap (no orientation/overshading/pitch on our yield). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
0102ff313a
commit
0d552b5a22
2 changed files with 43 additions and 1 deletions
|
|
@ -105,6 +105,11 @@ class CalculatorInputs:
|
|||
hot_water_fuel_cost_gbp_per_kwh: float
|
||||
other_fuel_cost_gbp_per_kwh: float
|
||||
co2_factor_kg_per_kwh: float
|
||||
# Generation offsets — applied as a cost credit against the ECF
|
||||
# numerator. SAP 10.2 Appendix M: PV self-consumption + export
|
||||
# collapse to a single credit at the export rate (Table 12 code 60).
|
||||
pv_generation_kwh_per_yr: float = 0.0
|
||||
pv_export_credit_gbp_per_kwh: float = 0.0
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
|
@ -265,11 +270,14 @@ def calculate_sap_from_inputs(inputs: CalculatorInputs) -> SapResult:
|
|||
+ inputs.pumps_fans_kwh_per_yr
|
||||
+ inputs.lighting_kwh_per_yr
|
||||
)
|
||||
total_cost = (
|
||||
pv_credit = inputs.pv_generation_kwh_per_yr * inputs.pv_export_credit_gbp_per_kwh
|
||||
total_cost = max(
|
||||
0.0,
|
||||
main_fuel_kwh * inputs.space_heating_fuel_cost_gbp_per_kwh
|
||||
+ inputs.hot_water_kwh_per_yr * inputs.hot_water_fuel_cost_gbp_per_kwh
|
||||
+ (inputs.pumps_fans_kwh_per_yr + inputs.lighting_kwh_per_yr)
|
||||
* inputs.other_fuel_cost_gbp_per_kwh
|
||||
- pv_credit,
|
||||
)
|
||||
ecf = energy_cost_factor(total_cost_gbp=total_cost, total_floor_area_m2=tfa)
|
||||
sap_int = sap_rating_integer(ecf=ecf)
|
||||
|
|
|
|||
|
|
@ -134,6 +134,19 @@ _DEFAULT_PUMPS_FANS_KWH_PER_YR: Final[float] = 130.0
|
|||
_INSTANTANEOUS_WATER_CODES: Final[frozenset[int]] = frozenset({907, 909})
|
||||
|
||||
|
||||
# UK-average annual PV yield (kWh per kWp). SAP 10.2 Appendix M references
|
||||
# regional yield factors; for rating purposes (Appendix U: ratings use UK
|
||||
# average weather) the single national figure applies. Derived from
|
||||
# `domain.ml.ecf._PV_YIELD_BY_REGION` UK-average baseline.
|
||||
_PV_ANNUAL_YIELD_KWH_PER_KWP: Final[float] = 850.0
|
||||
|
||||
|
||||
# SAP 10.2 Table 12 code 60 — PV export tariff. The calculator uses this
|
||||
# rate as the per-kWh PV cost credit applied against total annual fuel
|
||||
# cost in the ECF numerator.
|
||||
_PV_EXPORT_TARIFF_CODE: Final[int] = 60
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PriceTable:
|
||||
"""Seam between the spec-correct SAP 10.2/10.3 Table 12 prices and
|
||||
|
|
@ -475,6 +488,25 @@ def _hot_water_fuel_cost_gbp_per_kwh(
|
|||
return _fuel_cost_gbp_per_kwh(main, prices)
|
||||
|
||||
|
||||
def _pv_generation_kwh_per_yr(epc: EpcPropertyData) -> float:
|
||||
"""Annual PV generation (kWh/yr) summed across all photovoltaic
|
||||
arrays on the cert. SAP 10.2 Appendix M: yield = peak power × annual
|
||||
yield factor. We use the UK-average yield (Appendix U rule for
|
||||
ratings)."""
|
||||
arrays = epc.sap_energy_source.photovoltaic_arrays
|
||||
if not arrays:
|
||||
return 0.0
|
||||
total_kwp = sum(a.peak_power for a in arrays if a.peak_power is not None)
|
||||
return total_kwp * _PV_ANNUAL_YIELD_KWH_PER_KWP
|
||||
|
||||
|
||||
def _pv_export_credit_gbp_per_kwh(prices: PriceTable) -> float:
|
||||
"""PV cost credit per kWh generated. SAP 10.2 Table 12 code 60 (PV
|
||||
export to grid) — 5.59 p/kWh on the spec table, 13.19 p/kWh under
|
||||
cert calibration (legacy unit prices)."""
|
||||
return prices.unit_price_p_per_kwh(_PV_EXPORT_TARIFF_CODE) * _PENCE_TO_GBP
|
||||
|
||||
|
||||
def _other_fuel_cost_gbp_per_kwh(
|
||||
meter_type: object, prices: PriceTable
|
||||
) -> float:
|
||||
|
|
@ -665,4 +697,6 @@ def cert_to_inputs(
|
|||
epc.sap_energy_source.meter_type, prices
|
||||
),
|
||||
co2_factor_kg_per_kwh=_co2_factor_kg_per_kwh(main),
|
||||
pv_generation_kwh_per_yr=_pv_generation_kwh_per_yr(epc),
|
||||
pv_export_credit_gbp_per_kwh=_pv_export_credit_gbp_per_kwh(prices),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue