diff --git a/scripts/dive_cert.py b/scripts/dive_cert.py index 6fa83efb..8d6da1b4 100644 --- a/scripts/dive_cert.py +++ b/scripts/dive_cert.py @@ -17,6 +17,7 @@ from datatypes.epc.domain.mapper import EpcPropertyDataMapper from domain.sap10_calculator.calculator import calculate_sap_from_inputs from domain.sap10_calculator.rdsap.cert_to_inputs import ( SAP_10_2_SPEC_PRICES, + cert_to_demand_inputs, cert_to_inputs, ) from scripts.profile_api_error import features @@ -40,6 +41,10 @@ def _dump(doc: dict[str, Any]) -> None: lodged_pe = doc.get("energy_consumption_current") epc = EpcPropertyDataMapper.from_api_response(doc) r = calculate_sap_from_inputs(cert_to_inputs(epc, prices=SAP_10_2_SPEC_PRICES)) + # SAP/EI rating is the UK-average rating cascade (`r`); EPC CO2/PE use the + # postcode demand cascade (SAP 10.2 Appendix U p.124). Display CO2/PE from + # the demand cascade so they compare like-for-like with the lodged EPC. + d = calculate_sap_from_inputs(cert_to_demand_inputs(epc, prices=SAP_10_2_SPEC_PRICES)) print("=" * 90) print(f"CERT {cert}") print( @@ -48,13 +53,13 @@ def _dump(doc: dict[str, Any]) -> None: ) if lodged_co2 is not None: print( - f" CO2 lodged={lodged_co2:.3f} ours={r.co2_kg_per_yr / 1000:.3f} t " - f"d={r.co2_kg_per_yr / 1000 - lodged_co2:+.3f}" + f" CO2 lodged={lodged_co2:.3f} ours={d.co2_kg_per_yr / 1000:.3f} t " + f"d={d.co2_kg_per_yr / 1000 - lodged_co2:+.3f} (demand cascade)" ) if lodged_pe is not None: print( - f" PE lodged={lodged_pe:.1f} ours={r.primary_energy_kwh_per_m2:.1f} " - f"d={r.primary_energy_kwh_per_m2 - lodged_pe:+.1f} kWh/m2" + f" PE lodged={lodged_pe:.1f} ours={d.primary_energy_kwh_per_m2:.1f} " + f"d={d.primary_energy_kwh_per_m2 - lodged_pe:+.1f} kWh/m2 (demand cascade)" ) print( f" energy kWh/yr: spaceheat={r.space_heating_kwh_per_yr:.0f} " diff --git a/scripts/profile_corpus_error.py b/scripts/profile_corpus_error.py index 851086a9..6ef2c4e6 100644 --- a/scripts/profile_corpus_error.py +++ b/scripts/profile_corpus_error.py @@ -37,6 +37,7 @@ from typing import Any, Optional from datatypes.epc.domain.mapper import EpcPropertyDataMapper from domain.sap10_calculator.calculator import calculate_sap_from_inputs from domain.sap10_calculator.rdsap.cert_to_inputs import ( + cert_to_demand_inputs, SAP_10_2_SPEC_PRICES, cert_to_inputs, ) @@ -96,6 +97,14 @@ def _compute(corpus: list[dict[str, Any]]) -> tuple[list[Row], int, int]: result = calculate_sap_from_inputs( cert_to_inputs(epc, prices=SAP_10_2_SPEC_PRICES) ) + # SAP/EI rating is the UK-average rating cascade (`result`); + # the EPC-displayed CO2/PE use the postcode demand cascade + # (SAP 10.2 Appendix U p.124). Use the demand cascade for the + # PE/CO2-vs-cost triage so it is not confounded by the climate + # difference (UK-average vs local weather). + demand = calculate_sap_from_inputs( + cert_to_demand_inputs(epc, prices=SAP_10_2_SPEC_PRICES) + ) except Exception: raised += 1 continue @@ -110,14 +119,14 @@ def _compute(corpus: list[dict[str, Any]]) -> tuple[list[Row], int, int]: rows.append(Row( cert=cert, sap_err=result.sap_score_continuous - lodged_sap, - co2_err_t=(result.co2_kg_per_yr / 1000.0 - lodged_co2_t) + co2_err_t=(demand.co2_kg_per_yr / 1000.0 - lodged_co2_t) if lodged_co2_t is not None else None, - pe_err=(result.primary_energy_kwh_per_m2 - lodged_pe) + pe_err=(demand.primary_energy_kwh_per_m2 - lodged_pe) if lodged_pe is not None else None, lodged_sap=lodged_sap, our_sap=result.sap_score_continuous, lodged_pe=lodged_pe, - our_pe=result.primary_energy_kwh_per_m2, + our_pe=demand.primary_energy_kwh_per_m2, feats=features(doc), )) return rows, skipped, raised