From be6188ad23ef45fca1a7d1873db3b1dfb40fb31a Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 26 Aug 2025 15:39:34 +0100 Subject: [PATCH] additional fixes for EPC C properties --- backend/Funding.py | 19 +++++++++++++++++++ backend/apis/GoogleSolarApi.py | 31 +++++++++++++++++++++++++++---- backend/engine/engine.py | 1 + 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/backend/Funding.py b/backend/Funding.py index 58f7891f..17ad5701 100644 --- a/backend/Funding.py +++ b/backend/Funding.py @@ -566,6 +566,10 @@ class Funding: measure_code = "LI_lessequal100" if existing_li_thickness <= 100 else "LI_greater100" pps = filtered_pps_matrix[filtered_pps_matrix["Measure_Type"] == measure_code] + # There's no funding for EPC C or above + if self.starting_sap_band in ["Low_C", "High_C", "Low_B", "High_B", "Low_A", "High_A"]: + return 0 + if pps.shape[0] != 1: raise ValueError(f"Invalid LI category: {measure_code}") return pps.squeeze()["Cost Savings"] @@ -597,6 +601,10 @@ class Funding: return pps.squeeze()["Cost Savings"] if measure_type == "solid_floor_insulation": + if self.starting_sap_band in ["Low_C", "High_C", "Low_B", "High_B", "Low_A", "High_A"]: + # We don't fund SFI for properties starting at C or above + return 0 + pps = filtered_pps_matrix[filtered_pps_matrix["Measure_Type"] == "SFI"] if pps.shape[0] != 1: raise ValueError("Invalid SFI category") @@ -607,9 +615,20 @@ class Funding: (filtered_pps_matrix["Measure_Type"] == "Solar_PV") & (filtered_pps_matrix["Pre_Main_Heating_Source"] == pre_heating_system) ] + + if solar_pps_df.empty and self.starting_sap_band in [ + "Low_C", "High_C", "Low_B", "High_B", "Low_B", "High_A" + ]: + # No funding for EPC C or above + return 0 return solar_pps_df.squeeze()["Cost Savings"] if measure_type == "air_source_heat_pump": + + # No funding for EPC C or above + if self.starting_sap_band in ["Low_C", "High_C", "Low_B", "High_B", "Low_A", "High_A"]: + return 0 + pps = filtered_pps_matrix[ (filtered_pps_matrix["Pre_Main_Heating_Source"] == pre_heating_system) & (filtered_pps_matrix["Post_Main_Heating_Source"] == "Air to Water ASHP") & diff --git a/backend/apis/GoogleSolarApi.py b/backend/apis/GoogleSolarApi.py index 9073b307..043f41a9 100644 --- a/backend/apis/GoogleSolarApi.py +++ b/backend/apis/GoogleSolarApi.py @@ -900,8 +900,7 @@ class GoogleSolarApi: return input_properties - @classmethod - def default_panel_performance(cls, property_instance): + def default_panel_performance(self, property_instance): """ In a small number of cases, where properties have simulated uprns, we do not have a longitude and latitude value and therefore we just return a default panel performance @@ -911,6 +910,20 @@ class GoogleSolarApi: cost_instance = Costs(property_instance=property_instance) + material_1_6 = next( + (m for m in self.solar_materials if m["type"] == "solar_pv" and + abs(m["size"] - 1.6) < 0.1 and not m["includes_battery"]), + None + ) + material_3_2 = next( + (m for m in self.solar_materials if m["type"] == "solar_pv" and + abs(m["size"] - 3.2) < 0.1 and not m["includes_battery"]), + None + ) + + if material_1_6 is None or material_3_2 is None: + raise ValueError("No suitable solar product found for the default configuration.") + # We return a 1.6 and 3.2 kwp system panel_performance = pd.DataFrame( [ @@ -918,7 +931,12 @@ class GoogleSolarApi: 'n_panels': 8, 'yearly_dc_energy': 3200 * assumptions.MEDIAN_WATTAGE_TO_DC, 'total_cost': cost_instance.solar_pv( - n_panels=8, has_battery=False, n_floors=property_instance.number_of_floors + solar_product=material_1_6, + scaffolding_options=[ + {"total_cost": 1000, "size": property_instance.number_of_floors}, + {"total_cost": 1000, "size": 3} + ], + n_floors=property_instance.number_of_floors )["total"], 'weighted_ratio': None, 'panneled_roof_area': 8 * assumptions.RDSAP_AREA_PER_PANEL, @@ -938,7 +956,12 @@ class GoogleSolarApi: 'n_panels': 4, 'yearly_dc_energy': 1600 * assumptions.MEDIAN_WATTAGE_TO_DC, 'total_cost': cost_instance.solar_pv( - n_panels=6, has_battery=False, n_floors=property_instance.number_of_floors + solar_product=material_3_2, + scaffolding_options=[ + {"total_cost": 1000, "size": property_instance.number_of_floors}, + {"total_cost": 1000, "size": 3} + ], + n_floors=property_instance.number_of_floors )["total"], 'weighted_ratio': None, 'panneled_roof_area': 4 * assumptions.RDSAP_AREA_PER_PANEL, diff --git a/backend/engine/engine.py b/backend/engine/engine.py index 64bb8d65..6f9cac21 100644 --- a/backend/engine/engine.py +++ b/backend/engine/engine.py @@ -894,6 +894,7 @@ async def model_engine(body: PlanTriggerRequest): 0, 0, 0, 0 ) continue + ( r["partial_project_score"], r["partial_project_funding"], r["innovation_uplift"], r["uplift_project_score"]