mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
Slice S0380.49: effective-monthly Table 12e PE factor for PV split per SAP 10.2 Appendix M1 §8
The PE cascade was crediting the PV split at annual Table 12 factors (IMPORT 1.501 / EXPORT 0.501) instead of the spec-correct effective monthly Table 12e factors. Per Appendix M1 §8 (p.94): "For calculation of primary energy, for electricity used within the dwelling apply the normal import PE factors for the relevant tariff from Table 12e. For the electricity exported, apply the factors for 'electricity sold to grid, PV', also from table 12e." Cert 0380 worksheet (page 5) lodges 1.4960 / 0.4268 — the effective monthly values weighted by E_PV,dw,m / E_PV,ex,m. The cascade now computes the same via `_effective_monthly_pe_factor` (the helper already in place for secondary heating, pumps+fans, lighting, electric showers). Two new Optional fields on `CalculatorInputs`: - `pv_dwelling_primary_factor` — falls back to `other_primary_factor` - `pv_exported_primary_factor` — falls back to `pv_export_primary_factor` Both populated in `cert_to_inputs.py` via `_effective_monthly_pe_ factor(pv_split.epv_*_monthly_kwh, fuel_code)` — code 30 (standard electricity) for dwelling, code 60 (electricity sold to grid, PV) for exported. Mirrors the existing CO2 cascade shape exactly. Cohort PE residual closure (kWh/m²): | Cert | Post-S0380.48 | Post-S0380.49 | |---|---:|---:| | 0350 | -3.58 | **-2.96** | | 0380 | -4.01 | **-3.06** | | 2225 | -4.50 | **-3.73** | | 2636 | -4.14 | **-3.44** | | 3800 | -4.01 | **-3.25** | | 9285 | -3.46 | **-2.81** | | 9418 | -3.76 | **-3.01** | | 2130 (PV gas) | -9.70 | **-8.22** | 7-cert ASHP+battery cluster closed by 0.6-0.8 kWh/m² each (matches the +0.074 differential between annual 0.501 and worksheet 0.4268 applied to E_PV,ex ≈ 640 kWh/yr / TFA 60.43 = 0.78 kWh/m²). The remaining -3 kWh/m² residual is β fine-tuning (cascade 0.751 vs worksheet 0.7426 — small monthly D_PV distribution detail). Cert 9501 (PV no battery) drifted +0.25 → +0.65 PE — known shape change from the factor correction; β=0.498 matches worksheet exactly so the drift uncovers a different small gap previously masked by the wrong factors. Still well within tolerance. CO2 + SAP unchanged. Pyright net-zero on touched files (34 errors before, 34 after — all pre-existing).
This commit is contained in:
parent
6788c99087
commit
e75198ce5d
3 changed files with 76 additions and 38 deletions
|
|
@ -231,8 +231,21 @@ class CalculatorInputs:
|
||||||
# used by synthetic CalculatorInputs constructions in unit tests).
|
# used by synthetic CalculatorInputs constructions in unit tests).
|
||||||
pv_dwelling_kwh_per_yr: Optional[float] = None
|
pv_dwelling_kwh_per_yr: Optional[float] = None
|
||||||
pv_exported_kwh_per_yr: Optional[float] = None
|
pv_exported_kwh_per_yr: Optional[float] = None
|
||||||
# SAP 10.2 Table 12 code 60 ("electricity sold to grid, PV") PE
|
# SAP 10.2 Appendix M1 §8 — per-cert PE factors for the PV split.
|
||||||
# factor = 0.501. Applied to E_PV,ex when split is set.
|
# Mirrors the §7 CO2 cascade shape: the dwelling factor is the
|
||||||
|
# effective monthly Table 12e IMPORT factor (Σ(E_PV,dw,m × PE_30,m) /
|
||||||
|
# Σ(E_PV,dw,m)); the exported factor is the effective monthly
|
||||||
|
# Table 12e factor for code 60 ("electricity sold to grid, PV").
|
||||||
|
# Both are precomputed in cert_to_inputs from the PV split. None
|
||||||
|
# falls back to the legacy annual values: `other_primary_factor`
|
||||||
|
# (1.501, standard electricity) for the dwelling portion and
|
||||||
|
# `pv_export_primary_factor` (0.501) for the exported portion —
|
||||||
|
# preserves synthetic CalculatorInputs constructions.
|
||||||
|
pv_dwelling_primary_factor: Optional[float] = None
|
||||||
|
pv_exported_primary_factor: Optional[float] = None
|
||||||
|
# Legacy annual fall-back for the exported PE factor (synthetic
|
||||||
|
# constructions or zero-export months that yield no effective
|
||||||
|
# monthly value). SAP 10.2 Table 12 code 60 = 0.501.
|
||||||
pv_export_primary_factor: float = 0.501
|
pv_export_primary_factor: float = 0.501
|
||||||
# SAP 10.2 Appendix M1 §6 (p.94) — IMPORT price for onsite-consumed
|
# SAP 10.2 Appendix M1 §6 (p.94) — IMPORT price for onsite-consumed
|
||||||
# PV generation. cert_to_inputs supplies this from Table 12a (standard
|
# PV generation. cert_to_inputs supplies this from Table 12a (standard
|
||||||
|
|
@ -608,9 +621,19 @@ def calculate_sap_from_inputs(inputs: CalculatorInputs) -> SapResult:
|
||||||
inputs.pv_dwelling_kwh_per_yr is not None
|
inputs.pv_dwelling_kwh_per_yr is not None
|
||||||
and inputs.pv_exported_kwh_per_yr is not None
|
and inputs.pv_exported_kwh_per_yr is not None
|
||||||
):
|
):
|
||||||
|
pv_dwelling_pe_factor = (
|
||||||
|
inputs.pv_dwelling_primary_factor
|
||||||
|
if inputs.pv_dwelling_primary_factor is not None
|
||||||
|
else inputs.other_primary_factor
|
||||||
|
)
|
||||||
|
pv_exported_pe_factor = (
|
||||||
|
inputs.pv_exported_primary_factor
|
||||||
|
if inputs.pv_exported_primary_factor is not None
|
||||||
|
else inputs.pv_export_primary_factor
|
||||||
|
)
|
||||||
pv_primary_offset_kwh = (
|
pv_primary_offset_kwh = (
|
||||||
inputs.pv_dwelling_kwh_per_yr * inputs.other_primary_factor
|
inputs.pv_dwelling_kwh_per_yr * pv_dwelling_pe_factor
|
||||||
+ inputs.pv_exported_kwh_per_yr * inputs.pv_export_primary_factor
|
+ inputs.pv_exported_kwh_per_yr * pv_exported_pe_factor
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
pv_primary_offset_kwh = (
|
pv_primary_offset_kwh = (
|
||||||
|
|
|
||||||
|
|
@ -3259,6 +3259,21 @@ def cert_to_inputs(
|
||||||
pv_split.epv_exported_monthly_kwh,
|
pv_split.epv_exported_monthly_kwh,
|
||||||
_PV_EXPORT_FUEL_CODE_TABLE_12,
|
_PV_EXPORT_FUEL_CODE_TABLE_12,
|
||||||
),
|
),
|
||||||
|
# SAP 10.2 Appendix M1 §8 — per-cert effective monthly PE
|
||||||
|
# factors for the PV split. Mirrors the §7 CO2 factors above:
|
||||||
|
# dwelling factor weights Table 12e code 30 (standard
|
||||||
|
# electricity import) by monthly E_PV,dw,m; exported factor
|
||||||
|
# weights code 60 ("electricity sold to grid, PV") by monthly
|
||||||
|
# E_PV,ex,m. Worksheet for cert 0380 lodges 1.4960 / 0.4268;
|
||||||
|
# the annual Table 12 fallbacks (1.501 / 0.501) over-credit by
|
||||||
|
# the differential when the cascade uses them directly.
|
||||||
|
pv_dwelling_primary_factor=_effective_monthly_pe_factor(
|
||||||
|
pv_split.epv_dwelling_monthly_kwh, _STANDARD_ELECTRICITY_FUEL_CODE,
|
||||||
|
),
|
||||||
|
pv_exported_primary_factor=_effective_monthly_pe_factor(
|
||||||
|
pv_split.epv_exported_monthly_kwh,
|
||||||
|
_PV_EXPORT_FUEL_CODE_TABLE_12,
|
||||||
|
),
|
||||||
secondary_heating_fraction=secondary_fraction_value,
|
secondary_heating_fraction=secondary_fraction_value,
|
||||||
secondary_heating_efficiency=secondary_efficiency_value,
|
secondary_heating_efficiency=secondary_efficiency_value,
|
||||||
energy_requirements=energy_requirements_result,
|
energy_requirements=energy_requirements_result,
|
||||||
|
|
|
||||||
|
|
@ -200,18 +200,19 @@ _EXPECTATIONS: tuple[_GoldenExpectation, ...] = (
|
||||||
cert_number="2130-1033-4050-5007-8395",
|
cert_number="2130-1033-4050-5007-8395",
|
||||||
actual_sap=82,
|
actual_sap=82,
|
||||||
expected_sap_resid=+1,
|
expected_sap_resid=+1,
|
||||||
expected_pe_resid_kwh_per_m2=-9.6962,
|
expected_pe_resid_kwh_per_m2=-8.2213,
|
||||||
expected_co2_resid_tonnes_per_yr=-0.0456,
|
expected_co2_resid_tonnes_per_yr=-0.0456,
|
||||||
notes=(
|
notes=(
|
||||||
"End-terrace + 1 extension, TFA 64, gas combi PCDB index 17505, "
|
"End-terrace + 1 extension, TFA 64, gas combi PCDB index 17505, "
|
||||||
"postcode DE22 (PCDB Table 172 match), PV: 2× 2.04 kWp arrays "
|
"postcode DE22 (PCDB Table 172 match), PV: 2× 2.04 kWp arrays "
|
||||||
"(SE + NW, overshading 1 + 2). Slice S0380.45 wired the "
|
"(SE + NW, overshading 1 + 2). Slice S0380.45 wired the "
|
||||||
"Appendix M1 β-split into the PE cascade: PE residual moved "
|
"Appendix M1 β-split into the PE cascade: PE residual moved "
|
||||||
"from -38.63 to -9.70 (the +28.9 kWh/m² shift = (1-β) × EPV × "
|
"from -38.63 to -9.70. Slice S0380.49 wired effective monthly "
|
||||||
"(import_PEF - export_PEF) credit correction). SAP integer "
|
"Table 12e PE factor (vs annual 1.501/0.501) into the PV "
|
||||||
"shifted +1 (82 → 83) via the same cascade interaction. The "
|
"split: residual closed -9.70 → -8.22. SAP integer shifted "
|
||||||
"-9.70 residual remains — gas combi PE under-count + secondary "
|
"+1 (82 → 83) via the cohort cascade interaction. Remaining "
|
||||||
"heating credit are likely candidates for follow-up."
|
"-8.22 residual sits in gas combi PE under-count + secondary "
|
||||||
|
"heating credit (deferred)."
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_GoldenExpectation(
|
_GoldenExpectation(
|
||||||
|
|
@ -256,94 +257,93 @@ _EXPECTATIONS: tuple[_GoldenExpectation, ...] = (
|
||||||
cert_number="0380-2471-3250-2596-8761",
|
cert_number="0380-2471-3250-2596-8761",
|
||||||
actual_sap=89,
|
actual_sap=89,
|
||||||
expected_sap_resid=+0,
|
expected_sap_resid=+0,
|
||||||
expected_pe_resid_kwh_per_m2=-4.0121,
|
expected_pe_resid_kwh_per_m2=-3.0557,
|
||||||
expected_co2_resid_tonnes_per_yr=-0.0549,
|
expected_co2_resid_tonnes_per_yr=-0.0549,
|
||||||
notes=(
|
notes=(
|
||||||
"Mitsubishi PUZ-WM50VHA PCDB 104568, semi-detached bungalow "
|
"Mitsubishi PUZ-WM50VHA PCDB 104568, semi-detached bungalow "
|
||||||
"TFA 60.43 age D, PV 3 kWp + 5 kWh battery. Worksheet SAP "
|
"TFA 60.43 age D, PV 3 kWp + 5 kWh battery. Worksheet SAP "
|
||||||
"88.5104. Slice S0380.48 surfaced the 5-kWh battery to the "
|
"88.5104. Slice S0380.48 surfaced the 5-kWh battery from "
|
||||||
"domain mapper: real-API certs lodge `pv_batteries` as "
|
"the flat-shape real-API JSON (PE +8.09 → -4.01). Slice "
|
||||||
"`[{\"battery_capacity\": 5}]` (flat list) but the schema "
|
"S0380.49 then wired effective-monthly Table 12e PE factor "
|
||||||
"expected a nested `{\"pv_battery\": {\"battery_capacity\": "
|
"into the PV split — dwelling 1.4952 (vs worksheet 1.4960), "
|
||||||
"5}}`. With battery surfaced, β rises 0.36 → 0.75 (vs "
|
"exported 0.4283 (vs worksheet 0.4268) — closing PE -4.01 "
|
||||||
"worksheet 0.74), flipping PE residual +8.09 → -4.01. The "
|
"→ -3.06. The residual -3 traces to a small β fine-tuning "
|
||||||
"remaining -4 under-shoot traces to the export PE factor "
|
"gap (cascade 0.751 vs worksheet 0.7426) — monthly D_PV "
|
||||||
"(synthetic default 0.501 vs worksheet's effective monthly "
|
"distribution detail."
|
||||||
"Table 12e ~0.427) plus a small β fine-tuning gap."
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_GoldenExpectation(
|
_GoldenExpectation(
|
||||||
cert_number="0350-2968-2650-2796-5255",
|
cert_number="0350-2968-2650-2796-5255",
|
||||||
actual_sap=84,
|
actual_sap=84,
|
||||||
expected_sap_resid=+0,
|
expected_sap_resid=+0,
|
||||||
expected_pe_resid_kwh_per_m2=-3.5819,
|
expected_pe_resid_kwh_per_m2=-2.9642,
|
||||||
expected_co2_resid_tonnes_per_yr=-0.0865,
|
expected_co2_resid_tonnes_per_yr=-0.0865,
|
||||||
notes=(
|
notes=(
|
||||||
"Mitsubishi PUZ-WM50VHA PCDB 104568, ASHP cohort cert with "
|
"Mitsubishi PUZ-WM50VHA PCDB 104568, ASHP cohort cert with "
|
||||||
"PV + 5 kWh battery. Worksheet SAP 84.1367. Slice S0380.48 "
|
"PV + 5 kWh battery. Worksheet SAP 84.1367. Slice S0380.49 "
|
||||||
"battery-surface fix shifted PE residual +2.73 → -3.58."
|
"Table 12e PE factor wiring closed PE -3.58 → -2.96."
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_GoldenExpectation(
|
_GoldenExpectation(
|
||||||
cert_number="2225-3062-8205-2856-7204",
|
cert_number="2225-3062-8205-2856-7204",
|
||||||
actual_sap=89,
|
actual_sap=89,
|
||||||
expected_sap_resid=+0,
|
expected_sap_resid=+0,
|
||||||
expected_pe_resid_kwh_per_m2=-4.5030,
|
expected_pe_resid_kwh_per_m2=-3.7259,
|
||||||
expected_co2_resid_tonnes_per_yr=-0.0729,
|
expected_co2_resid_tonnes_per_yr=-0.0729,
|
||||||
notes=(
|
notes=(
|
||||||
"Mitsubishi PUZ-WM50VHA PCDB 104568, ASHP cohort cert with "
|
"Mitsubishi PUZ-WM50VHA PCDB 104568, ASHP cohort cert with "
|
||||||
"PV + 5 kWh battery. Worksheet SAP 88.7921. Slice S0380.48 "
|
"PV + 5 kWh battery. Worksheet SAP 88.7921. Slice S0380.49 "
|
||||||
"battery-surface fix shifted PE residual +4.48 → -4.50."
|
"Table 12e PE factor wiring closed PE -4.50 → -3.73."
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_GoldenExpectation(
|
_GoldenExpectation(
|
||||||
cert_number="2636-0525-2600-0401-2296",
|
cert_number="2636-0525-2600-0401-2296",
|
||||||
actual_sap=86,
|
actual_sap=86,
|
||||||
expected_sap_resid=+0,
|
expected_sap_resid=+0,
|
||||||
expected_pe_resid_kwh_per_m2=-4.1432,
|
expected_pe_resid_kwh_per_m2=-3.4364,
|
||||||
expected_co2_resid_tonnes_per_yr=-0.0603,
|
expected_co2_resid_tonnes_per_yr=-0.0603,
|
||||||
notes=(
|
notes=(
|
||||||
"Mitsubishi PUZ-WM50VHA PCDB 104568, ASHP cohort cert with "
|
"Mitsubishi PUZ-WM50VHA PCDB 104568, ASHP cohort cert with "
|
||||||
"PV + 5 kWh battery + 3.74 m² cantilever + 12.76 m² alt wall. "
|
"PV + 5 kWh battery + 3.74 m² cantilever + 12.76 m² alt wall. "
|
||||||
"Worksheet SAP 86.2641. Slice S0380.48 battery-surface fix "
|
"Worksheet SAP 86.2641. Slice S0380.49 Table 12e PE factor "
|
||||||
"shifted PE residual +3.42 → -4.14."
|
"wiring closed PE -4.14 → -3.44."
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_GoldenExpectation(
|
_GoldenExpectation(
|
||||||
cert_number="3800-8515-0922-3398-3563",
|
cert_number="3800-8515-0922-3398-3563",
|
||||||
actual_sap=86,
|
actual_sap=86,
|
||||||
expected_sap_resid=+0,
|
expected_sap_resid=+0,
|
||||||
expected_pe_resid_kwh_per_m2=-4.0132,
|
expected_pe_resid_kwh_per_m2=-3.2511,
|
||||||
expected_co2_resid_tonnes_per_yr=-0.0166,
|
expected_co2_resid_tonnes_per_yr=-0.0166,
|
||||||
notes=(
|
notes=(
|
||||||
"Mitsubishi PUZ-WM50VHA PCDB 104568, ASHP cohort cert with "
|
"Mitsubishi PUZ-WM50VHA PCDB 104568, ASHP cohort cert with "
|
||||||
"PV + 5 kWh battery. Worksheet SAP 86.1458. Slice S0380.48 "
|
"PV + 5 kWh battery. Worksheet SAP 86.1458. Slice S0380.49 "
|
||||||
"battery-surface fix shifted PE residual +3.58 → -4.01."
|
"Table 12e PE factor wiring closed PE -4.01 → -3.25."
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_GoldenExpectation(
|
_GoldenExpectation(
|
||||||
cert_number="9285-3062-0205-7766-7200",
|
cert_number="9285-3062-0205-7766-7200",
|
||||||
actual_sap=84,
|
actual_sap=84,
|
||||||
expected_sap_resid=+0,
|
expected_sap_resid=+0,
|
||||||
expected_pe_resid_kwh_per_m2=-3.4619,
|
expected_pe_resid_kwh_per_m2=-2.8078,
|
||||||
expected_co2_resid_tonnes_per_yr=-0.1003,
|
expected_co2_resid_tonnes_per_yr=-0.1003,
|
||||||
notes=(
|
notes=(
|
||||||
"Mitsubishi PUZ-WM50VHA PCDB 104568, ASHP cohort cert with "
|
"Mitsubishi PUZ-WM50VHA PCDB 104568, ASHP cohort cert with "
|
||||||
"PV + 5 kWh battery. Worksheet SAP 84.1369. Slice S0380.48 "
|
"PV + 5 kWh battery. Worksheet SAP 84.1369. Slice S0380.49 "
|
||||||
"battery-surface fix shifted PE residual +3.20 → -3.46."
|
"Table 12e PE factor wiring closed PE -3.46 → -2.81."
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_GoldenExpectation(
|
_GoldenExpectation(
|
||||||
cert_number="9418-3062-8205-3566-7200",
|
cert_number="9418-3062-8205-3566-7200",
|
||||||
actual_sap=85,
|
actual_sap=85,
|
||||||
expected_sap_resid=+0,
|
expected_sap_resid=+0,
|
||||||
expected_pe_resid_kwh_per_m2=-3.7627,
|
expected_pe_resid_kwh_per_m2=-3.0084,
|
||||||
expected_co2_resid_tonnes_per_yr=-0.0485,
|
expected_co2_resid_tonnes_per_yr=-0.0485,
|
||||||
notes=(
|
notes=(
|
||||||
"Daikin Altherma EDLQ05CAV3 PCDB 102421 (heating_duration "
|
"Daikin Altherma EDLQ05CAV3 PCDB 102421 (heating_duration "
|
||||||
"code '24' — continuous, all days at Th) + 5 kWh battery. "
|
"code '24' — continuous, all days at Th) + 5 kWh battery. "
|
||||||
"Worksheet SAP 84.6305. Slice S0380.48 battery-surface fix "
|
"Worksheet SAP 84.6305. Slice S0380.49 Table 12e PE factor "
|
||||||
"shifted PE residual +4.67 → -3.76."
|
"wiring closed PE -3.76 → -3.01."
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue