From ea9086ba37c612ffe6bb517c3d3e655328640396 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 5 Aug 2024 18:06:16 +0100 Subject: [PATCH] optimise fetching of spatial data --- backend/app/plan/router.py | 27 +++++++++++++++++++++-- etl/customers/newhaven/newhaven_study.py | 16 ++++++++++++++ recommendations/Costs.py | 4 ++-- recommendations/HeatingRecommender.py | 6 ++++- recommendations/SecondaryHeating.py | 3 +++ recommendations/SolarPvRecommendations.py | 6 ++++- 6 files changed, 56 insertions(+), 6 deletions(-) diff --git a/backend/app/plan/router.py b/backend/app/plan/router.py index 3e2c724f..d4b2a9a5 100644 --- a/backend/app/plan/router.py +++ b/backend/app/plan/router.py @@ -451,10 +451,34 @@ async def trigger_plan(body: PlanTriggerRequest): # ) # We need to prepare the EPC so it's in the same format as the training data + # TODO: Move this/tidy it up + uprn_map = {} + for uprn in [p.uprn for p in input_properties]: + filtered_df = uprn_filenames[ + (uprn_filenames["lower"] <= int(uprn)) + & (uprn_filenames["upper"] >= int(uprn)) + ] + if filtered_df["filenames"].values[0] in uprn_map: + uprn_map[filtered_df["filenames"].values[0]].append(int(uprn)) + else: + uprn_map[filtered_df["filenames"].values[0]] = [int(uprn)] + + for filename, associated_uprn in tqdm(uprn_map.items(), total=len(uprn_map)): + # Read in the file + spatial_data = read_dataframe_from_s3_parquet( + bucket_name="retrofit-data-dev", file_key=f"spatial/{filename}" + ) + + spatial_df = spatial_data[spatial_data["UPRN"].isin(associated_uprn)] + for p in input_properties: + if p.uprn in associated_uprn: + p.set_spatial(spatial_df[spatial_df["UPRN"] == p.uprn]) + logger.info("Getting spatial data") for p in tqdm(input_properties): + if p.spatial is None: + raise Exception("Missed setting of spatial data for a property") p.get_components(cleaned=cleaned, energy_consumption_client=energy_consumption_client) - p.get_spatial_data(uprn_filenames) logger.info("Performing solar analysis") # TODO: Tidy this up @@ -613,7 +637,6 @@ async def trigger_plan(body: PlanTriggerRequest): recommendations_scoring_data = [] representative_recommendations = {} for p in tqdm(input_properties): - recommender = Recommendations(property_instance=p, materials=materials, exclusions=body.exclusions) property_recommendations, property_representative_recommendations = recommender.recommend() diff --git a/etl/customers/newhaven/newhaven_study.py b/etl/customers/newhaven/newhaven_study.py index ab601fdc..7c53405f 100644 --- a/etl/customers/newhaven/newhaven_study.py +++ b/etl/customers/newhaven/newhaven_study.py @@ -177,22 +177,38 @@ def make_asset_list(): property_non_invasive_recs.append( { "type": "air_source_heat_pump", + "suitable": True, "size": property_ashp_potential["Recommended Heat Pump Size [kW]"].values[0], "cost": property_costs["Air Source Heat Pump - Total"].values[0], "ashp_only_heating_recommendation": True } ) + else: + property_non_invasive_recs.append( + { + "type": "air_source_heat_pump", + "suitable": False + } + ) if not property_pv_potential.empty: property_non_invasive_recs.append( { "type": "solar_pv", + "suitable": True, "array_wattage": property_pv_potential["Recommended Array Size [kW]"].values[0] * 1000, "initial_ac_kwh_per_year": property_pv_potential["Annual Generation [kWh]"].values[0], "panneled_roof_area": property_pv_potential["Roof area suitable for PV [m^2]"].values[0], "cost": property_costs["Rooftop PV - Total"].values[0], } ) + else: + property_non_invasive_recs.append( + { + "type": "solar_pv", + "suitable": False + } + ) non_invasive_recommendations.append( { diff --git a/recommendations/Costs.py b/recommendations/Costs.py index c1feb18a..8deed75a 100644 --- a/recommendations/Costs.py +++ b/recommendations/Costs.py @@ -100,8 +100,8 @@ CONDENSING_BOILER_COSTS = { # The unit is a 15kw boiler, capable of outputting between 3kw and 15kw. Costs seem to be around £1800 ELECTRIC_BOILER_COSTS = 1800 -# Assumes 3 hours to remove each heater (including re-decorating) -ROOM_HEATER_REMOVAL_COST = 120 +# Assumes 1 hours to remove each heater (including re-decorating) +ROOM_HEATER_REMOVAL_COST = 50 ROOM_HEATER_REMOVAL_LABOUR_HOURS = 3 # This is a cost quoted by Jim for a system flush - existig system will run more efficiently diff --git a/recommendations/HeatingRecommender.py b/recommendations/HeatingRecommender.py index 523bfe3b..1a3b6159 100644 --- a/recommendations/HeatingRecommender.py +++ b/recommendations/HeatingRecommender.py @@ -58,8 +58,12 @@ class HeatingRecommender: exclusions = [] if exclusions is None else exclusions non_invasive_ashp_recommendation = next( - (r for r in self.property.non_invasive_recommendations if r["type"] == "air_source_heat_pump"), {} + (r for r in self.property.non_invasive_recommendations if r["type"] == "air_source_heat_pump"), + {"suitable": True} ) + # We allow for the non-invasive recommendation to be that ASHP is not suitable + if not non_invasive_ashp_recommendation["suitable"]: + return # This option will prevent other heating recommendations from being specified, other than an ASHP ashp_only_heating_recommendation = non_invasive_ashp_recommendation.get( diff --git a/recommendations/SecondaryHeating.py b/recommendations/SecondaryHeating.py index 5d763510..aed48da2 100644 --- a/recommendations/SecondaryHeating.py +++ b/recommendations/SecondaryHeating.py @@ -60,6 +60,9 @@ class SecondaryHeating: **costs, "simulation_config": { "secondheat_description_ending": "None" + }, + "description_simulation": { + "secondheat-description": "None" } } ) diff --git a/recommendations/SolarPvRecommendations.py b/recommendations/SolarPvRecommendations.py index 5069b9fb..3e7ede28 100644 --- a/recommendations/SolarPvRecommendations.py +++ b/recommendations/SolarPvRecommendations.py @@ -153,9 +153,13 @@ class SolarPvRecommendations: return non_invasive_recommendation = next( - (r for r in self.property.non_invasive_recommendations if r["type"] == "solar_pv"), {} + (r for r in self.property.non_invasive_recommendations if r["type"] == "solar_pv"), {"suitable": True} ) + # We allow for the non-invasive recommendation to be that solar PV is not suitable + if not non_invasive_recommendation["suitable"]: + return + if non_invasive_recommendation: roof_area = esimtate_pitched_roof_area(