diff --git a/domain/sap10_calculator/rdsap/cert_to_inputs.py b/domain/sap10_calculator/rdsap/cert_to_inputs.py index e3fafb7f..df04c3b6 100644 --- a/domain/sap10_calculator/rdsap/cert_to_inputs.py +++ b/domain/sap10_calculator/rdsap/cert_to_inputs.py @@ -883,7 +883,13 @@ def _synthesize_pv_arrays_from_percent_roof_area( if is_pitched: bp_pv_area /= cos_factor pv_area_m2 += bp_pv_area - kwp = _PV_PEAK_POWER_KWP_PER_M2 * pv_area_m2 + # RdSAP10 §15 p.66: "kWp for photovoltaics, etc.: 2 d.p." — round + # before the EPV cascade so it matches the worksheet's "Cells Peak" + # column (cert 6835: cascade 0.12 × 36.9 × 0.40 / cos(35°) = 2.16224 + # → 2.16, matching worksheet "Cells Peak = 2.16"). The 0.0022 kWp + # delta otherwise feeds straight into (233) PV generation as a + # +1.5 kWh/yr over-credit and a +0.015 SAP residual. + kwp = _round_half_up(_PV_PEAK_POWER_KWP_PER_M2 * pv_area_m2, 2) if kwp <= 0: return None return [ 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 3fbe3a95..6307bb9f 100644 --- a/domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py +++ b/domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py @@ -481,10 +481,14 @@ def test_pv_generation_synthesizes_array_from_percent_roof_area_per_rdsap_11_1() Cohort-2 cert 6835 (Semi-Detached bungalow, single storey, TFA 36.9 m², pitched roof) lodges only "Proportion of roof area = 40" — the - cascade must synthesize a 2.16 kWp array (= 36.9 × 0.40 / cos(35°) - × 0.12) and route the generation through the Appendix M cost - cascade. Verified against the worksheet's "Cells Peak = 2.16, - Orientation = South, Elevation = 30°, Overshading = Modest" line. + cascade must synthesize the array and route the generation through + the Appendix M cost cascade. Worksheet (cert 6835 dr87-0001-000624) + lodges "Cells Peak = 2.16, Orientation = South, Elevation = 30°, + Overshading = Modest" and worksheet (233) PV generation = 1492.3348 + kWh/yr. Per RdSAP10 §15 p.66 "kWp for photovoltaics, etc.: 2 d.p." + the cascade rounds kWp before the SAP 10.2 §M1 EPV calculation — + without the round, cascade's 2.16224 kWp drives a +1.5 kWh/yr + over-credit and a +0.015 SAP residual vs the worksheet. """ from datatypes.epc.domain.epc_property_data import ( PhotovoltaicSupply, PhotovoltaicSupplyNoneOrNoDetails, @@ -513,12 +517,11 @@ def test_pv_generation_synthesizes_array_from_percent_roof_area_per_rdsap_11_1() # Act gen_kwh = cert_to_inputs(epc).pv_generation_kwh_per_yr - # Assert — kWp = 0.12 × 36.9 × 0.40 / cos(35°) = 2.1622. With S = - # ~862 kWh/m²/yr at South 30° UK-avg (Appendix U3.3) and ZPV = 0.8 - # (Modest), annual EPV = 0.8 × 2.1622 × 862 × 0.8 ≈ 1490 kWh/yr — - # within 1% of the worksheet's 1492.33 kWh/yr. - assert 1450.0 < gen_kwh < 1530.0, ( - f"expected ~1490 kWh/yr (per RdSAP §11.1 b synthesis), got {gen_kwh:.2f}" + # Assert — EPV = 0.8 × 2.16 × S(South,30°,UK-avg) × 0.8(Modest); + # the cascade rounds kWp to 2 d.p. so the result lands on the + # worksheet's 1492.3348 kWh/yr at 1e-4. + assert abs(gen_kwh - 1492.3348) <= 1e-4, ( + f"expected 1492.3348 kWh/yr (worksheet cert 6835), got {gen_kwh:.4f}" )