mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
174 lines
8.2 KiB
Python
174 lines
8.2 KiB
Python
from backend.Property import Property
|
|
from typing import List
|
|
from recommendations.FloorRecommendations import FloorRecommendations
|
|
from recommendations.WallRecommendations import WallRecommendations
|
|
from recommendations.RoofRecommendations import RoofRecommendations
|
|
from recommendations.VentilationRecommendations import VentilationRecommendations
|
|
from recommendations.FireplaceRecommendations import FireplaceRecommendations
|
|
from recommendations.LightingRecommendations import LightingRecommendations
|
|
from recommendations.WindowsRecommendations import WindowsRecommendations
|
|
from backend.ml_models.AnnualBillSavings import AnnualBillSavings
|
|
|
|
|
|
class Recommendations:
|
|
"""
|
|
High level recommendations class, which sits above the measure specific recommendation classes
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
property_instance: Property,
|
|
materials: List
|
|
):
|
|
"""
|
|
:param property_instance: Instance of the Property class, for the home associated to property_id
|
|
:param materials: List of materials to be used in the recommendations
|
|
"""
|
|
|
|
self.property_instance = property_instance
|
|
self.materials = materials
|
|
|
|
self.floor_recommender = FloorRecommendations(property_instance=property_instance, materials=materials)
|
|
self.wall_recomender = WallRecommendations(property_instance=property_instance, materials=materials)
|
|
self.roof_recommender = RoofRecommendations(property_instance=property_instance, materials=materials)
|
|
self.ventilation_recomender = VentilationRecommendations(
|
|
property_instance=property_instance, materials=materials
|
|
)
|
|
self.fireplace_recommender = FireplaceRecommendations(property_instance=property_instance)
|
|
self.lighting_recommender = LightingRecommendations(property_instance=property_instance, materials=materials)
|
|
self.windows_recommender = WindowsRecommendations(property_instance=property_instance, materials=materials)
|
|
|
|
def recommend(self):
|
|
|
|
"""
|
|
This method runs the recommendations for the individual measures and then appends them to a list for output
|
|
:return:
|
|
"""
|
|
|
|
property_recommendations = []
|
|
|
|
# Floor recommendations
|
|
self.floor_recommender.recommend()
|
|
if self.floor_recommender.recommendations:
|
|
property_recommendations.append(self.floor_recommender.recommendations)
|
|
|
|
# Wall recommendations
|
|
self.wall_recomender.recommend()
|
|
if self.wall_recomender.recommendations:
|
|
property_recommendations.append(self.wall_recomender.recommendations)
|
|
|
|
# Roof recommendations
|
|
self.roof_recommender.recommend()
|
|
if self.roof_recommender.recommendations:
|
|
property_recommendations.append(self.roof_recommender.recommendations)
|
|
|
|
# Ventilation recommendations
|
|
# We only produce a ventilation recommendation if the property is recommended to have wall or roof insulation
|
|
if self.wall_recomender.recommendations or self.roof_recommender.recommendations:
|
|
self.ventilation_recomender.recommend()
|
|
if self.ventilation_recomender.recommendation:
|
|
property_recommendations.append(self.ventilation_recomender.recommendation)
|
|
|
|
# Fireplace sealing recommendations
|
|
self.fireplace_recommender.recommend()
|
|
if self.fireplace_recommender.recommendation:
|
|
property_recommendations.append(self.fireplace_recommender.recommendation)
|
|
|
|
# Lighting recommendations
|
|
self.lighting_recommender.recommend()
|
|
if self.lighting_recommender.recommendation:
|
|
property_recommendations.append(self.lighting_recommender.recommendation)
|
|
|
|
# Windows recommendations
|
|
self.windows_recommender.recommend()
|
|
if self.windows_recommender.recommendation:
|
|
property_recommendations.append(self.windows_recommender.recommendation)
|
|
|
|
# We insert temporary ids into the recommendations which is important for the optimiser later
|
|
property_recommendations = self.insert_temp_recommendation_id(property_recommendations)
|
|
|
|
return property_recommendations
|
|
|
|
@staticmethod
|
|
def insert_temp_recommendation_id(property_recommendations):
|
|
"""
|
|
Creates a temporary recommendation id which is needed for
|
|
filtering recommendations between default and no, after the optimiser has been
|
|
run
|
|
:param property_recommendations: nested list of recommendations, grouped by data_types
|
|
:return: Updated recommendations_to_upload, where where recommendation has a "recommendation_id"
|
|
integer inserted
|
|
"""
|
|
idx = 0
|
|
|
|
for recs in property_recommendations:
|
|
for rec in recs:
|
|
rec["recommendation_id"] = idx
|
|
idx += 1
|
|
|
|
return property_recommendations
|
|
|
|
@classmethod
|
|
def calculate_recommendation_impact(cls, property_instance, all_predictions, recommendations):
|
|
|
|
"""
|
|
Given predictions from the model apis, with method will update the recommendations with the predicted
|
|
impact of the recommendation on the property
|
|
|
|
:param property_instance: Instance of the Property class, for the home associated to property_id
|
|
:param all_predictions: dictionary of predictions from the model apis
|
|
:param recommendations: dictionary of recommendations for the property
|
|
:return:
|
|
"""
|
|
|
|
property_sap_predictions = all_predictions["sap_change_predictions"][
|
|
all_predictions["sap_change_predictions"]["property_id"] == str(property_instance.id)
|
|
]
|
|
property_heat_predictions = all_predictions["heat_demand_predictions"][
|
|
all_predictions["heat_demand_predictions"]["property_id"] == str(property_instance.id)
|
|
]
|
|
property_carbon_predictions = all_predictions["carbon_change_predictions"][
|
|
all_predictions["carbon_change_predictions"]["property_id"] == str(property_instance.id)
|
|
]
|
|
|
|
property_recommendations = recommendations[property_instance.id].copy()
|
|
|
|
for recommendations_by_type in property_recommendations:
|
|
for rec in recommendations_by_type:
|
|
|
|
new_heat_demand = property_heat_predictions[property_heat_predictions["recommendation_id"] == str(
|
|
rec["recommendation_id"]
|
|
)]["predictions"].values[0]
|
|
|
|
new_carbon = property_carbon_predictions[property_carbon_predictions["recommendation_id"] == str(
|
|
rec["recommendation_id"]
|
|
)]["predictions"].values[0]
|
|
|
|
# We don't use the model for low energy lighting at the moment
|
|
if rec["type"] != "low_energy_lighting":
|
|
new_sap = property_sap_predictions[property_sap_predictions["recommendation_id"] == str(
|
|
rec["recommendation_id"]
|
|
)]["predictions"].values[0]
|
|
rec["sap_points"] = new_sap - float(property_instance.data["current-energy-efficiency"])
|
|
|
|
if rec["type"] == "mechanical_ventilation":
|
|
# For the moment, we cap the number of SAP points that can be achieved by ventilation at 2
|
|
rec["sap_points"] = min(rec["sap_points"], VentilationRecommendations.SAP_LIMIT)
|
|
|
|
# Round to 2 decimal places
|
|
rec["sap_points"] = round(rec["sap_points"], 2)
|
|
rec["co2_equivalent_savings"] = float(property_instance.data["co2-emissions-current"]) - new_carbon
|
|
|
|
# Energy consumption current is per meter squared, so we need to multiply by the floor area to get
|
|
# an absolute figure for the home
|
|
rec["heat_demand"] = (
|
|
(float(property_instance.data["energy-consumption-current"]) - new_heat_demand
|
|
) * property_instance.floor_area)
|
|
|
|
rec["energy_cost_savings"] = AnnualBillSavings.estimate(rec["heat_demand"])
|
|
|
|
if (rec["sap_points"] is None) and (rec["co2_equivalent_savings"] is None) or (
|
|
rec["heat_demand"] is None) or (rec["energy_cost_savings"] is None):
|
|
raise ValueError("sap points, co2 or heat demand is missing")
|
|
|
|
return property_recommendations
|