diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index 1c38228a1..638042178 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -153,6 +153,7 @@ from datatypes.epc.surveys.pashub_rdsap_site_notes import ( FloorMeasurement, HeatingAndHotWater, PasHubRdSapSiteNotes, + Renewables as PasHubRenewables, RoofSpaceDetail, Ventilation, WaterUse, @@ -367,6 +368,7 @@ class EpcPropertyDataMapper: wind_turbines_terrain_type=general.terrain_type, electricity_smart_meter_present=general.electricity_smart_meter, pv_connection=renewables.pv_connection, + photovoltaic_arrays=_pashub_pv_arrays(renewables), photovoltaic_supply=( PhotovoltaicSupply( none_or_no_details=PhotovoltaicSupplyNoneOrNoDetails( @@ -6697,6 +6699,37 @@ _ELMHURST_PV_OVERSHADING_TO_RDSAP: Dict[str, int] = { } +def _pashub_pv_arrays( + renewables: PasHubRenewables, +) -> Optional[List[PhotovoltaicArray]]: + """Build the Appendix M cascade's input list from the PasHub Renewables + per-system PV block, mirroring `_elmhurst_pv_arrays` below and reusing its + format-agnostic converters. PasHub lodges the orientation space-separated + ("South East") where the shared octant map keys hyphenated ("South-East"), + so normalise before the lookup. Returns None when no per-array detail is + lodged — the PV-absent path the cascade already handles (issue #1590 + bug 1: dropping this detail silently zeroed the PV credit).""" + if not renewables.pv_arrays: + return None + out: List[PhotovoltaicArray] = [] + for arr in renewables.pv_arrays: + if arr.kwp is None or arr.kwp <= 0.0: + continue + out.append( + PhotovoltaicArray( + peak_power=arr.kwp, + pitch=_elmhurst_pv_pitch_code( + arr.pitch_degrees if arr.pitch_degrees is not None else 30 + ), + orientation=_elmhurst_orientation_int( + (arr.orientation or "").replace(" ", "-") + ), + overshading=_elmhurst_pv_overshading_int(arr.overshading), + ) + ) + return out or None + + def _elmhurst_pv_arrays( renewables: ElmhurstRenewables, ) -> Optional[List[PhotovoltaicArray]]: