set up the rest of the solar recommendation

This commit is contained in:
Khalim Conn-Kowlessar 2024-01-05 12:22:20 +00:00
parent fcc4dc6b51
commit c3dbec6703
5 changed files with 69 additions and 9 deletions

View file

@ -221,11 +221,15 @@ class Property(Definitions):
setattr(self, attribute, value)
def get_components(self, cleaned):
def get_components(self, cleaned, photo_supply_lookup, floor_area_decile_thresholds):
"""
Given the cleaning that has been performed, we'll use this to identify the property
components, from roof to walls to windows, heating and hot water
:param cleaned: This is the dictionary of components found in cleaner.cleaned
:param photo_supply_lookup: This is the lookup table for the photo supply, used to estimate the percentage
of the roof that is suitable for solar panels
:param floor_area_decile_thresholds: This is the decile thresholds for the floor area, used in estimating the
solar pv roof area
:return:
"""
@ -295,6 +299,9 @@ class Property(Definitions):
self.set_floor_type()
self.set_floor_level()
self.set_windows_count()
self.set_solar_panel_area(
photo_supply_lookup=photo_supply_lookup, floor_area_decile_thresholds=floor_area_decile_thresholds
)
def set_age_band(self):
"""
@ -849,7 +856,9 @@ class Property(Definitions):
return i # Returns the decile index (0 to 9)
return len(thresholds)
floor_area_decile = classify_floor_area(self.floor_area, floor_area_decile_thresholds)
floor_area_decile = classify_floor_area(
self.floor_area, floor_area_decile_thresholds["floor_area_decile_thresholds"].values
)
# Given the photo_supply_lookup, we esimate the percentage of the roof that is suitable for solar panels

View file

@ -67,6 +67,12 @@ async def trigger_plan(body: PlanTriggerRequest):
cleaning_data = read_parquet_from_s3(
bucket_name=get_settings().DATA_BUCKET, file_key="sap_change_model/cleaning_dataset.parquet",
)
photo_supply_lookup = read_parquet_from_s3(
bucket_name=get_settings().DATA_BUCKET, file_key="solar_pv_supply/photo_supply_lookup.parquet",
)
floor_area_decile_thresholds = read_parquet_from_s3(
bucket_name=get_settings().DATA_BUCKET, file_key="solar_pv_supply/floor_area_decile_thresholds.parquet",
)
input_properties = []
for config in plan_input:
@ -129,7 +135,7 @@ async def trigger_plan(body: PlanTriggerRequest):
for p in input_properties:
# Property recommendations
p.get_components(cleaned)
p.get_components(cleaned, photo_supply_lookup, floor_area_decile_thresholds)
# This is temp - this should happen after scoring
cleaned_property_data = DataProcessor.apply_averages_cleaning(

View file

@ -101,7 +101,7 @@ def app():
save_dataframe_to_s3_parquet(
df=aggregated,
bucket_name="retrofit-data-dev",
file_key=f"solar_pv_supply/photo_supply_lookup.parquet",
file_key="solar_pv_supply/photo_supply_lookup.parquet",
)
floor_area_decile_thresholds = pd.DataFrame(decile_thresholds, columns=["floor_area_decile_thresholds"])

View file

@ -831,5 +831,35 @@ class Costs:
"labour_days": labour_days
}
def solar_pv(self, wattage):
pass
def solar_pv(self, wattage: float):
"""
Calculates the total cost for solar PV based data provided by the MCS dashboard, which contains
costing data for installations of renewable and clean energy measures.
The data in the dashboard is filtered on domestic building installations and then the data across the
various regions is manually collected. There is currently no automated way to get the data from the MCS
dashboard
:param wattage:
:return:
"""
# Get the cost data relevant to the region
regional_cost = MCS_SOLAR_PV_COST_DATA["-".join(["average_cost_per_kwh", self.region])]
kw = wattage / 1000
total_cost = kw * regional_cost
subtotal_before_vat = total_cost / (1 + self.VAT_RATE)
vat = total_cost - subtotal_before_vat
# Labour hours are based on estimates from online research but an average team seems to consist of 3 people
# and most jobs take around 2 days. Assuming an 8 hour day for 3 people across 2 days, gives us 72 hours of
# labour
return {
"total": total_cost,
"subtotal": subtotal_before_vat,
"vat": vat,
"labour_hours": 72,
"labour_days": 2,
}

View file

@ -16,7 +16,7 @@ class SolarPvRecommendations:
self.property = property_instance
self.costs = Costs(self.property)
self.recommendations = []
self.recommendation = []
def recommend(self):
"""
@ -35,12 +35,27 @@ class SolarPvRecommendations:
None, 0, self.property.DATA_ANOMALY_MATCHES
]
if not is_valid_property_type or not is_valid_roof_type or has_no_existing_solar_pv:
if not is_valid_property_type or not is_valid_roof_type or not has_no_existing_solar_pv:
return
# We now have a property which is potentially suitable for solar PV
number_solar_panels = np.floor(self.property.solar_pv_roof_area / self.SOLAR_PANEL_AREA)
solar_panel_capacity = number_solar_panels * self.SOLAR_PANEL_WATTAGE
solar_panel_wattage = number_solar_panels * self.SOLAR_PANEL_WATTAGE
# Given the wattage, we estimate the cost of the solar PV system. This is based on the MCS database
# of solar PV installations
cost_result = self.costs.solar_pv(wattage=solar_panel_wattage)
kw = int(np.round(solar_panel_wattage / 1000))
self.recommendation = [
{
"parts": [],
"type": "solar_pv",
"description": f"Install a {kw} kilowatt-peak (kWp) solar photovoltaic (PV) panel system on the roof",
"starting_u_value": None,
"new_u_value": None,
"sap_points": None,
**cost_result,
}
]