From b1f33cd27f4a68aa22676bbd76ab0905b8f37a02 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Fri, 29 May 2026 12:10:24 +0000 Subject: [PATCH] Slice S0380.72: HW PE/CO2 via Table 12d/12e monthly cascade (ASHP cohort follow-on) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit S0380.71 closed STANDARD-tariff electric MAIN heating PE/CO2 via the monthly Table 12d/12e cascade. The same spec rule applies to HOT WATER but the cascade still hardcoded the annual flat factors at `cert_to_inputs.py:3697-3699` (CO2) and `:3826-3828` (PE), plus the §12 / §13a section helpers. This slice extends the spec-citation fix to HW. Per SAP 10.2 Table 12d (p.195) and Table 12e (p.196) headers: "Where electricity is the fuel used, the relevant set of factors in the table below should be used to calculate the monthly [CO2 emissions / primary energy] instead the annual average factor given in Table 12." The rule applies to ALL electric end-uses regardless of tariff, including the HW path. For electric HW (`water_heating_fuel=29` API standard electricity → Table 12 code 30) the monthly cascade weighted by `wh_result.output_monthly_kwh` (HW demand monthly proxy) lands at ~0.140 CO2 / ~1.517 PE vs annual flat 0.136 / 1.501 on a HW demand profile. Cohort closure (20 STANDARD-tariff ASHP certs): Mean PE residual: −0.66 → −0.36 kWh/m² (≈+0.30 closure per cert) Worst cert 9796: −1.36 → −1.08 PE / −0.005 → −0.002 CO2 t/yr Cumulative S0380.71 + S0380.72 closure for the ASHP cohort: Mean PE residual: −3.10 → −0.36 kWh/m² (8.6× compression) Changes: - NEW `_hot_water_co2_factor_kg_per_kwh(epc, hw_monthly_kwh)` helper — electric HW fuel → monthly Table 12d cascade; non-electric HW fuel → annual Table 12 factor. - NEW `_hot_water_primary_factor(epc, hw_monthly_kwh)` helper — PE mirror per Table 12e. - `cert_to_inputs` `hot_water_co2_factor_kg_per_kwh` / `hot_water_primary_factor` fields routed through the new helpers (was annual flat `co2_factor_kg_per_kwh` / `primary_energy_factor`). - `environmental_section_from_cert` (§12) + `primary_energy_section_ from_cert` (§13a) section helpers updated to read the cert_to_inputs HW factor fields rather than recomputing annual flat — keeps the worksheet line refs in sync with the cascade. - Imports: add `PRIMARY_ENERGY_FACTOR`, `_DEFAULT_CO2_KG_PER_KWH`, `_DEFAULT_PEF` from `table_12` for the helpers' degenerate paths. Tests: - `test_electric_water_heating_co2_and_pe_factors_apply_monthly_ table_12d_12e` — pins electric HW > annual flat by the winter- weighting margin. - `test_gas_water_heating_co2_and_pe_factors_pass_through_annual_ table_12` — pins mains-gas HW at 0.210 / 1.130 (Table 12 code 1 annual factors). - `test_golden_fixtures.py`: 20 ASHP cluster cert pins updated to the post-S0380.72 residuals; other certs unchanged. Baseline: 546 pass + 9 expected `test_sap_result_pin[000565-*]` cascade-gap fails. Pyright net-zero on every touched file. Co-Authored-By: Claude Opus 4.7 --- .../sap10_calculator/rdsap/cert_to_inputs.py | 92 ++++++++++++++++--- .../rdsap/tests/test_cert_to_inputs.py | 87 ++++++++++++++++++ .../rdsap/tests/test_golden_fixtures.py | 54 +++++------ 3 files changed, 194 insertions(+), 39 deletions(-) diff --git a/domain/sap10_calculator/rdsap/cert_to_inputs.py b/domain/sap10_calculator/rdsap/cert_to_inputs.py index 6c23ccda..831d6f54 100644 --- a/domain/sap10_calculator/rdsap/cert_to_inputs.py +++ b/domain/sap10_calculator/rdsap/cert_to_inputs.py @@ -85,6 +85,9 @@ from domain.sap10_calculator.tables.pcdb.postcode_weather import ( from domain.sap10_calculator.tables.table_12 import ( API_FUEL_TO_TABLE_12, CO2_KG_PER_KWH, + PRIMARY_ENERGY_FACTOR, + _DEFAULT_CO2_KG_PER_KWH, # pyright: ignore[reportPrivateUsage] + _DEFAULT_PEF, # pyright: ignore[reportPrivateUsage] co2_monthly_factors_kg_per_kwh, co2_factor_kg_per_kwh, pe_monthly_factors_kwh_per_kwh, @@ -1538,6 +1541,70 @@ def _main_heating_primary_factor( return high_frac * high_factor + (1.0 - high_frac) * low_factor +def _hot_water_co2_factor_kg_per_kwh( + epc: EpcPropertyData, + hw_monthly_kwh: tuple[float, ...], +) -> float: + """SAP 10.2 Table 12 / 12d (p.195) per-end-use CO2 factor for the + cert's lodged water-heating fuel. HW-side analog of + `_main_heating_co2_factor_kg_per_kwh` (Slice S0380.71) + + `_secondary_heating_co2_factor_kg_per_kwh` (Slice S0380.70). + + Per Table 12d header (p.195): "Where electricity is the fuel + used, the relevant set of factors in the table below should be + used to calculate the monthly CO2 emissions instead the annual + average factor given in Table 12." → electric HW fuels apply the + monthly Table 12d cascade weighted by the cert's HW demand profile + (mirroring the worksheet's monthly weighting); non-electric HW + fuels (mains gas, oil, etc.) pass through the annual Table 12 + factor. + + `hw_monthly_kwh` is the monthly HW demand profile (proxy for + monthly HW fuel kWh — the calculator uses an annual-flat HW + efficiency so the SHAPE of fuel monthly is identical to demand + monthly, and `_effective_monthly_co2_factor` is shape-only).""" + fuel = _water_heating_fuel_code(epc) + if fuel is None: + return _DEFAULT_CO2_KG_PER_KWH + table_12_code = ( + fuel if fuel in CO2_KG_PER_KWH + else API_FUEL_TO_TABLE_12.get(fuel, fuel) + ) + monthly = _effective_monthly_co2_factor(hw_monthly_kwh, table_12_code) + if monthly is not None: + return monthly + return co2_factor_kg_per_kwh(fuel) + + +def _hot_water_primary_factor( + epc: EpcPropertyData, + hw_monthly_kwh: tuple[float, ...], +) -> float: + """SAP 10.2 Table 12 / 12e (p.196) per-end-use PE factor for the + cert's lodged water-heating fuel. PE-side mirror of + `_hot_water_co2_factor_kg_per_kwh`. Per Table 12e header (p.196): + electric HW fuels apply the monthly Table 12e cascade; non- + electric HW fuels pass through the annual Table 12 factor. + + Cohort closure context: cert 9796 (ASHP, water_heating_fuel=29 API + standard electricity → Table 12 code 30) lands at 1.5177 monthly- + weighted PE vs 1.501 annual flat (≈ +0.30 kWh/m² for the cert). + Same routing across the 20-cert STANDARD-tariff ASHP cohort + averages ~+0.3 kWh/m² closure on top of the S0380.71 main heating + fix.""" + fuel = _water_heating_fuel_code(epc) + if fuel is None: + return _DEFAULT_PEF + table_12_code = ( + fuel if fuel in PRIMARY_ENERGY_FACTOR + else API_FUEL_TO_TABLE_12.get(fuel, fuel) + ) + monthly = _effective_monthly_pe_factor(hw_monthly_kwh, table_12_code) + if monthly is not None: + return monthly + return primary_energy_factor(fuel) + + def _secondary_fuel_code(epc: EpcPropertyData) -> int: """SAP 10.2 secondary fuel code, resolved through the API mapper's Appendix M Table 4a spec-fuel routing. When no `secondary_fuel_type` @@ -1997,8 +2064,6 @@ def environmental_section_from_cert( main = _first_main_heating(epc) main_fuel = _main_fuel_code(main) main_factor = co2_factor_kg_per_kwh(main_fuel) - water_fuel = _water_heating_fuel_code(epc) - water_factor = co2_factor_kg_per_kwh(water_fuel) # Compute per-end-use CO2. For electricity end-uses, monthly Table 12d # cascade Σ(kWh_m × CO2_m); for gas end-uses, annual_kwh × annual factor. @@ -2010,10 +2075,13 @@ def environmental_section_from_cert( secondary_co2 = er.secondary_fuel_kwh_per_yr * ( secondary_eff if secondary_eff is not None else 0.0 ) - water_co2 = er.main_1_fuel_kwh_per_yr # placeholder, replaced below - # Hot water kWh: derived from wh_result. Recompute via cert_to_inputs path. + # Hot water kWh: derived from wh_result via cert_to_inputs. full_inputs = cert_to_inputs(epc, postcode_climate=postcode_climate) - water_co2 = full_inputs.hot_water_kwh_per_yr * water_factor + water_co2 = full_inputs.hot_water_kwh_per_yr * ( + full_inputs.hot_water_co2_factor_kg_per_kwh + if full_inputs.hot_water_co2_factor_kg_per_kwh is not None + else 0.0 + ) # Electric shower (264a) — distinct line ref when present. electric_shower_co2 = ( @@ -2104,15 +2172,13 @@ def primary_energy_section_from_cert( main = _first_main_heating(epc) main_fuel = _main_fuel_code(main) main_pe = primary_energy_factor(main_fuel) - water_fuel = _water_heating_fuel_code(epc) - water_pe = primary_energy_factor(water_fuel) main_1 = er.main_1_fuel_kwh_per_yr * main_pe main_2 = er.main_2_fuel_kwh_per_yr * main_pe secondary_pe_factor = _secondary_heating_primary_factor( epc, er.secondary_fuel_monthly_kwh, ) secondary = er.secondary_fuel_kwh_per_yr * secondary_pe_factor - water = full_inputs.hot_water_kwh_per_yr * water_pe + water = full_inputs.hot_water_kwh_per_yr * full_inputs.hot_water_primary_factor electric_shower = ( full_inputs.electric_shower_kwh_per_yr * (full_inputs.electric_shower_primary_factor or 0.0) @@ -3694,8 +3760,9 @@ def cert_to_inputs( secondary_heating_co2_factor_kg_per_kwh=_secondary_heating_co2_factor_kg_per_kwh( epc, energy_requirements_result.secondary_fuel_monthly_kwh, ), - hot_water_co2_factor_kg_per_kwh=co2_factor_kg_per_kwh( - _water_heating_fuel_code(epc) + hot_water_co2_factor_kg_per_kwh=_hot_water_co2_factor_kg_per_kwh( + epc, + wh_result.output_monthly_kwh if wh_result is not None else (0.0,) * 12, ), pumps_fans_co2_factor_kg_per_kwh=_effective_monthly_co2_factor( _days_in_month_proportioned(pumps_fans_kwh, _DAYS_IN_MONTH), @@ -3757,8 +3824,9 @@ def cert_to_inputs( main, _rdsap_tariff(epc), energy_requirements_result.main_1_fuel_monthly_kwh, ), - hot_water_primary_factor=primary_energy_factor( - _water_heating_fuel_code(epc) + hot_water_primary_factor=_hot_water_primary_factor( + epc, + wh_result.output_monthly_kwh if wh_result is not None else (0.0,) * 12, ), other_primary_factor=primary_energy_factor(30), # standard electricity # SAP 10.2 Table 12e (p.195) per-end-use effective PE factors. Same diff --git a/domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py b/domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py index 586feaf3..7859a74f 100644 --- a/domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py +++ b/domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py @@ -1009,6 +1009,93 @@ def test_standard_meter_ashp_main_heating_primary_factor_applies_monthly_table_1 ) +def test_electric_water_heating_co2_and_pe_factors_apply_monthly_table_12d_12e() -> None: + # Arrange — RdSAP cert with electric water heating + # (`water_heating_fuel=29` API standard electricity → Table 12 + # code 30). Pre-S0380.72 the cascade hardcoded annual flat + # `co2_factor_kg_per_kwh(29)` = 0.136 and + # `primary_energy_factor(29)` = 1.501 for the HW factor fields. + # Per SAP 10.2 Table 12d (p.195) and Table 12e (p.196) header text + # the spec rule applies to ALL electric end-uses regardless of + # tariff: "Where electricity is the fuel used, the relevant set + # of factors in the table below should be used to calculate the + # monthly [CO2 emissions / primary energy] instead the annual + # average factor given in Table 12." S0380.72 wires the HW factor + # fields through `_hot_water_co2_factor_kg_per_kwh` and + # `_hot_water_primary_factor` so the monthly Table 12d/12e cascade + # weighted by HW demand applies. + epc = make_minimal_sap10_epc( + total_floor_area_m2=_TYPICAL_TFA_M2, + habitable_rooms_count=3, + region_code="1", + dwelling_type="Detached bungalow", + sap_building_parts=[ + make_building_part( + floor_dimensions=[ + make_floor_dimension( + total_floor_area_m2=_TYPICAL_TFA_M2, floor=0, + ), + ], + ), + ], + sap_heating=make_sap_heating( + water_heating_fuel=29, # API standard electricity + main_heating_details=[ + MainHeatingDetail( + has_fghrs=False, main_fuel_type=29, heat_emitter_type=1, + emitter_temperature=1, main_heating_control=2106, + main_heating_category=4, sap_main_heating_code=224, + ), + ], + ), + ) + + # Act + inputs = cert_to_inputs(epc) + + # Assert — HW monthly cascade lands above annual flat by the + # winter-weighting margin (HW demand is slightly winter-skewed: + # daily_hot_water_l_per_day_monthly is higher Dec/Jan than Jun/Jul + # per the SAP 10.2 HW demand model). Tight bound: strictly greater + # than annual flat, by at least the monthly cascade differential. + co2 = inputs.hot_water_co2_factor_kg_per_kwh + pe = inputs.hot_water_primary_factor + assert co2 is not None and co2 > 0.136 + 1e-4, ( + f"expected monthly Table 12d cascade > 0.1361; got {co2}" + ) + assert pe is not None and pe > 1.501 + 1e-4, ( + f"expected monthly Table 12e cascade > 1.5011; got {pe}" + ) + + +def test_gas_water_heating_co2_and_pe_factors_pass_through_annual_table_12() -> None: + # Arrange — RdSAP cert with mains-gas water heating + # (`water_heating_fuel=26` API mains gas → Table 12 code 1). Per + # Table 12d/12e headers the monthly cascade applies "Where + # electricity is the fuel used"; non-electric fuels keep the + # annual Table 12 factor (0.210 CO2 / 1.130 PE for mains gas). + main = _gas_boiler_detail(sap_main_heating_code=102) + epc = make_minimal_sap10_epc( + total_floor_area_m2=_TYPICAL_TFA_M2, + habitable_rooms_count=4, + country_code="ENG", + sap_building_parts=[make_building_part()], + sap_heating=make_sap_heating( + water_heating_fuel=26, # API mains gas + main_heating_details=[main], + ), + ) + + # Act + inputs = cert_to_inputs(epc) + + # Assert — annual Table 12 factors for mains gas (code 1). + co2 = inputs.hot_water_co2_factor_kg_per_kwh + pe = inputs.hot_water_primary_factor + assert co2 is not None and abs(co2 - 0.210) <= 1e-4 + assert pe is not None and abs(pe - 1.130) <= 1e-4 + + def test_dual_meter_ashp_main_heating_primary_factor_applies_table_12a_grid_1_split() -> None: # Arrange — RdSAP 10 §12 page 62 Rule 1: HP without PCDB record → # TEN_HOUR tariff. Table 12a Grid 1 (SH) ASHP_OTHER + TEN_HOUR = diff --git a/domain/sap10_calculator/rdsap/tests/test_golden_fixtures.py b/domain/sap10_calculator/rdsap/tests/test_golden_fixtures.py index df066143..53532c6e 100644 --- a/domain/sap10_calculator/rdsap/tests/test_golden_fixtures.py +++ b/domain/sap10_calculator/rdsap/tests/test_golden_fixtures.py @@ -261,8 +261,8 @@ _EXPECTATIONS: tuple[_GoldenExpectation, ...] = ( cert_number="0380-2471-3250-2596-8761", actual_sap=89, expected_sap_resid=+0, - expected_pe_resid_kwh_per_m2=-0.1802, - expected_co2_resid_tonnes_per_yr=-0.0111, + expected_pe_resid_kwh_per_m2=+0.0504, + expected_co2_resid_tonnes_per_yr=-0.0077, notes=( "Mitsubishi PUZ-WM50VHA PCDB 104568, semi-detached bungalow " "TFA 60.43 age D, PV 3 kWp + 5 kWh battery. Worksheet SAP " @@ -281,8 +281,8 @@ _EXPECTATIONS: tuple[_GoldenExpectation, ...] = ( cert_number="0350-2968-2650-2796-5255", actual_sap=84, expected_sap_resid=+0, - expected_pe_resid_kwh_per_m2=-0.6985, - expected_co2_resid_tonnes_per_yr=-0.0345, + expected_pe_resid_kwh_per_m2=-0.4632, + expected_co2_resid_tonnes_per_yr=-0.0294, notes=( "Mitsubishi PUZ-WM50VHA PCDB 104568, ASHP cohort cert with " "PV + 5 kWh battery. Worksheet SAP 84.1367. Slice S0380.50 " @@ -293,8 +293,8 @@ _EXPECTATIONS: tuple[_GoldenExpectation, ...] = ( cert_number="2225-3062-8205-2856-7204", actual_sap=89, expected_sap_resid=+0, - expected_pe_resid_kwh_per_m2=-0.9922, - expected_co2_resid_tonnes_per_yr=-0.0179, + expected_pe_resid_kwh_per_m2=-0.6186, + expected_co2_resid_tonnes_per_yr=-0.0104, notes=( "Mitsubishi PUZ-WM50VHA PCDB 104568, ASHP cohort cert with " "PV + 5 kWh battery. Worksheet SAP 88.7921. Slice S0380.50 " @@ -305,8 +305,8 @@ _EXPECTATIONS: tuple[_GoldenExpectation, ...] = ( cert_number="2636-0525-2600-0401-2296", actual_sap=86, expected_sap_resid=+0, - expected_pe_resid_kwh_per_m2=-1.0590, - expected_co2_resid_tonnes_per_yr=-0.0126, + expected_pe_resid_kwh_per_m2=-0.6641, + expected_co2_resid_tonnes_per_yr=-0.0047, notes=( "Mitsubishi PUZ-WM50VHA PCDB 104568, ASHP cohort cert with " "PV + 5 kWh battery + 3.74 m² cantilever + 12.76 m² alt wall. " @@ -318,8 +318,8 @@ _EXPECTATIONS: tuple[_GoldenExpectation, ...] = ( cert_number="3800-8515-0922-3398-3563", actual_sap=86, expected_sap_resid=+0, - expected_pe_resid_kwh_per_m2=-0.7108, - expected_co2_resid_tonnes_per_yr=+0.0354, + expected_pe_resid_kwh_per_m2=-0.4553, + expected_co2_resid_tonnes_per_yr=+0.0405, notes=( "Mitsubishi PUZ-WM50VHA PCDB 104568, ASHP cohort cert with " "PV + 5 kWh battery. Worksheet SAP 86.1458. Slice S0380.50 " @@ -330,8 +330,8 @@ _EXPECTATIONS: tuple[_GoldenExpectation, ...] = ( cert_number="9285-3062-0205-7766-7200", actual_sap=84, expected_sap_resid=+0, - expected_pe_resid_kwh_per_m2=-0.5156, - expected_co2_resid_tonnes_per_yr=-0.0505, + expected_pe_resid_kwh_per_m2=-0.2702, + expected_co2_resid_tonnes_per_yr=-0.0454, notes=( "Mitsubishi PUZ-WM50VHA PCDB 104568, ASHP cohort cert with " "PV + 5 kWh battery. Worksheet SAP 84.1369. Slice S0380.50 " @@ -342,8 +342,8 @@ _EXPECTATIONS: tuple[_GoldenExpectation, ...] = ( cert_number="9418-3062-8205-3566-7200", actual_sap=85, expected_sap_resid=+0, - expected_pe_resid_kwh_per_m2=-1.0927, - expected_co2_resid_tonnes_per_yr=-0.0136, + expected_pe_resid_kwh_per_m2=-0.6590, + expected_co2_resid_tonnes_per_yr=-0.0058, notes=( "Daikin Altherma EDLQ05CAV3 PCDB 102421 (heating_duration " "code '24' — continuous, all days at Th) + 5 kWh battery. " @@ -368,13 +368,13 @@ _EXPECTATIONS: tuple[_GoldenExpectation, ...] = ( # `sap worksheets/Additional data with api/`. # ------------------------------------------------------------------ _GoldenExpectation(cert_number="0036-6325-1100-0063-1226", actual_sap=63, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=+0.4019, expected_co2_resid_tonnes_per_yr=+0.0255, notes="Cohort-2 baseline pin captured by S0380.69."), - _GoldenExpectation(cert_number="0100-5141-0522-4696-3463", actual_sap=86, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=+0.0197, expected_co2_resid_tonnes_per_yr=+0.0223, notes="Cohort-2 baseline pin captured by S0380.69."), + _GoldenExpectation(cert_number="0100-5141-0522-4696-3463", actual_sap=86, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=+0.2607, expected_co2_resid_tonnes_per_yr=+0.0275, notes="Cohort-2 baseline pin captured by S0380.69."), _GoldenExpectation(cert_number="0200-3155-0122-2602-3563", actual_sap=81, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=+0.9879, expected_co2_resid_tonnes_per_yr=-0.0097, notes="Cohort-2 baseline pin captured by S0380.69."), _GoldenExpectation(cert_number="0300-2403-2650-2206-0235", actual_sap=77, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=+0.5118, expected_co2_resid_tonnes_per_yr=+0.0442, notes="Cohort-2 baseline pin captured by S0380.69."), _GoldenExpectation(cert_number="0310-2763-5450-2506-3501", actual_sap=78, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=+0.4387, expected_co2_resid_tonnes_per_yr=+0.0149, notes="Cohort-2 baseline pin captured by S0380.69."), _GoldenExpectation(cert_number="0320-2126-2150-2326-6161", actual_sap=72, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.2060, expected_co2_resid_tonnes_per_yr=+0.0128, notes="Cohort-2 baseline pin captured by S0380.69."), - _GoldenExpectation(cert_number="0320-2756-8640-2296-1101", actual_sap=90, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.7813, expected_co2_resid_tonnes_per_yr=+0.0221, notes="Cohort-2 baseline pin captured by S0380.69."), - _GoldenExpectation(cert_number="0330-2257-3640-2196-3145", actual_sap=85, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.2015, expected_co2_resid_tonnes_per_yr=+0.0298, notes="Cohort-2 baseline pin captured by S0380.69."), + _GoldenExpectation(cert_number="0320-2756-8640-2296-1101", actual_sap=90, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.4689, expected_co2_resid_tonnes_per_yr=+0.0300, notes="Cohort-2 baseline pin captured by S0380.69."), + _GoldenExpectation(cert_number="0330-2257-3640-2196-3145", actual_sap=85, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=+0.0559, expected_co2_resid_tonnes_per_yr=+0.0349, notes="Cohort-2 baseline pin captured by S0380.69."), _GoldenExpectation(cert_number="0360-2266-5650-2106-8285", actual_sap=80, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=+0.2223, expected_co2_resid_tonnes_per_yr=-0.0171, notes="Cohort-2 baseline pin captured by S0380.69."), _GoldenExpectation(cert_number="0380-2530-6150-2326-4161", actual_sap=66, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.0893, expected_co2_resid_tonnes_per_yr=-0.0315, notes="Cohort-2 baseline pin captured by S0380.69."), _GoldenExpectation(cert_number="0390-2066-4250-2026-4555", actual_sap=65, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.2522, expected_co2_resid_tonnes_per_yr=+0.0005, notes="Cohort-2 baseline pin captured by S0380.69."), @@ -396,25 +396,25 @@ _EXPECTATIONS: tuple[_GoldenExpectation, ...] = ( # / CO2 +0.005 (lodged values are integer-rounded; rounding noise). _GoldenExpectation(cert_number="2102-3018-0205-7886-5204", actual_sap=64, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=+0.1961, expected_co2_resid_tonnes_per_yr=+0.0048, notes="Cohort-2 baseline pin. House coal secondary — S0380.70 routed CO2/PE through `secondary_fuel_type` per SAP 10.2 Table 12d/12e headers, closed PE +20.36 → +0.20 and CO2 -0.79 → +0.005 (lodged values integer-rounded)."), _GoldenExpectation(cert_number="2130-3018-4205-4686-5204", actual_sap=71, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=+0.4083, expected_co2_resid_tonnes_per_yr=-0.0357, notes="Cohort-2 baseline pin captured by S0380.69."), - _GoldenExpectation(cert_number="2336-3124-3600-0517-1292", actual_sap=83, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.3366, expected_co2_resid_tonnes_per_yr=-0.0465, notes="Cohort-2 baseline pin captured by S0380.69."), - _GoldenExpectation(cert_number="2536-2525-0600-0788-2292", actual_sap=80, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-1.0235, expected_co2_resid_tonnes_per_yr=-0.0290, notes="Cohort-2 baseline pin captured by S0380.69."), + _GoldenExpectation(cert_number="2336-3124-3600-0517-1292", actual_sap=83, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.0793, expected_co2_resid_tonnes_per_yr=-0.0415, notes="Cohort-2 baseline pin captured by S0380.69."), + _GoldenExpectation(cert_number="2536-2525-0600-0788-2292", actual_sap=80, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.6100, expected_co2_resid_tonnes_per_yr=-0.0245, notes="Cohort-2 baseline pin captured by S0380.69."), _GoldenExpectation(cert_number="2590-3025-7205-9066-0200", actual_sap=66, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.1309, expected_co2_resid_tonnes_per_yr=-0.0036, notes="Cohort-2 baseline pin captured by S0380.69."), _GoldenExpectation(cert_number="2699-3025-5205-8066-0200", actual_sap=69, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=+0.4755, expected_co2_resid_tonnes_per_yr=-0.0016, notes="Cohort-2 baseline pin captured by S0380.69."), - _GoldenExpectation(cert_number="2800-7999-0322-4594-3563", actual_sap=78, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.2402, expected_co2_resid_tonnes_per_yr=-0.0082, notes="Cohort-2 baseline pin captured by S0380.69."), + _GoldenExpectation(cert_number="2800-7999-0322-4594-3563", actual_sap=78, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=+0.0394, expected_co2_resid_tonnes_per_yr=-0.0050, notes="Cohort-2 baseline pin captured by S0380.69."), _GoldenExpectation(cert_number="3136-7925-4500-0246-6202", actual_sap=78, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=+0.2521, expected_co2_resid_tonnes_per_yr=-0.0486, notes="Cohort-2 baseline pin captured by S0380.69."), - _GoldenExpectation(cert_number="3336-2825-9400-0512-8292", actual_sap=78, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.5933, expected_co2_resid_tonnes_per_yr=-0.0453, notes="Cohort-2 baseline pin captured by S0380.69."), - _GoldenExpectation(cert_number="4536-5424-8600-0109-1226", actual_sap=82, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.5517, expected_co2_resid_tonnes_per_yr=-0.0106, notes="Cohort-2 baseline pin captured by S0380.69."), + _GoldenExpectation(cert_number="3336-2825-9400-0512-8292", actual_sap=78, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.3519, expected_co2_resid_tonnes_per_yr=-0.0421, notes="Cohort-2 baseline pin captured by S0380.69."), + _GoldenExpectation(cert_number="4536-5424-8600-0109-1226", actual_sap=82, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.3010, expected_co2_resid_tonnes_per_yr=-0.0056, notes="Cohort-2 baseline pin captured by S0380.69."), _GoldenExpectation(cert_number="4536-8325-3100-0409-1222", actual_sap=66, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.2794, expected_co2_resid_tonnes_per_yr=+0.0093, notes="Cohort-2 baseline pin captured by S0380.69."), - _GoldenExpectation(cert_number="4800-3992-0422-0599-3563", actual_sap=87, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.5884, expected_co2_resid_tonnes_per_yr=-0.0453, notes="Cohort-2 baseline pin captured by S0380.69."), + _GoldenExpectation(cert_number="4800-3992-0422-0599-3563", actual_sap=87, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.2102, expected_co2_resid_tonnes_per_yr=-0.0410, notes="Cohort-2 baseline pin captured by S0380.69."), _GoldenExpectation(cert_number="6835-3920-2509-0933-5226", actual_sap=80, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.1246, expected_co2_resid_tonnes_per_yr=-0.0238, notes="Cohort-2 baseline pin captured by S0380.69."), _GoldenExpectation(cert_number="7700-3362-0922-7022-3563", actual_sap=63, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.4141, expected_co2_resid_tonnes_per_yr=+0.0216, notes="Cohort-2 baseline pin captured by S0380.69."), _GoldenExpectation(cert_number="7800-1501-0922-7127-3563", actual_sap=65, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.0594, expected_co2_resid_tonnes_per_yr=+0.0440, notes="Cohort-2 baseline pin captured by S0380.69."), _GoldenExpectation(cert_number="7836-3125-0600-0526-2202", actual_sap=80, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=+0.3469, expected_co2_resid_tonnes_per_yr=+0.0164, notes="Cohort-2 baseline pin captured by S0380.69."), - _GoldenExpectation(cert_number="9036-0824-3500-0420-8222", actual_sap=84, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.7641, expected_co2_resid_tonnes_per_yr=+0.0284, notes="Cohort-2 baseline pin captured by S0380.69."), - _GoldenExpectation(cert_number="9370-3060-1205-3546-4204", actual_sap=88, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.4107, expected_co2_resid_tonnes_per_yr=-0.0115, notes="Cohort-2 baseline pin captured by S0380.69."), + _GoldenExpectation(cert_number="9036-0824-3500-0420-8222", actual_sap=84, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.5121, expected_co2_resid_tonnes_per_yr=+0.0334, notes="Cohort-2 baseline pin captured by S0380.69."), + _GoldenExpectation(cert_number="9370-3060-1205-3546-4204", actual_sap=88, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.2008, expected_co2_resid_tonnes_per_yr=-0.0062, notes="Cohort-2 baseline pin captured by S0380.69."), _GoldenExpectation(cert_number="9380-2957-7490-2595-3141", actual_sap=75, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=+0.4794, expected_co2_resid_tonnes_per_yr=-0.0245, notes="Cohort-2 baseline pin captured by S0380.69."), - _GoldenExpectation(cert_number="9421-3045-3205-1646-6200", actual_sap=87, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-1.0893, expected_co2_resid_tonnes_per_yr=-0.0096, notes="Cohort-2 baseline pin captured by S0380.69."), - _GoldenExpectation(cert_number="9796-3058-6205-0346-9200", actual_sap=90, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-1.3613, expected_co2_resid_tonnes_per_yr=-0.0048, notes="Cohort-2 baseline pin captured by S0380.69."), + _GoldenExpectation(cert_number="9421-3045-3205-1646-6200", actual_sap=87, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.7426, expected_co2_resid_tonnes_per_yr=-0.0049, notes="Cohort-2 baseline pin captured by S0380.69."), + _GoldenExpectation(cert_number="9796-3058-6205-0346-9200", actual_sap=90, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-1.0840, expected_co2_resid_tonnes_per_yr=-0.0016, notes="Cohort-2 baseline pin captured by S0380.69."), _GoldenExpectation(cert_number="9836-7525-9500-0575-1202", actual_sap=75, expected_sap_resid=+0, expected_pe_resid_kwh_per_m2=-0.5524, expected_co2_resid_tonnes_per_yr=+0.0010, notes="Cohort-2 baseline pin captured by S0380.69."), )