import pandas as pd from recommendations.Costs import Costs from recommendations.recommendation_utils import check_simulation_difference from backend.Property import Property from etl.epc_clean.epc_attributes.MainheatAttributes import MainHeatAttributes from recommendations.HeatingControlRecommender import HeatingControlRecommender class HeatingRecommender: def __init__(self, property_instance: Property): self.property = property_instance self.costs = Costs(self.property) self.recommendations = [] def recommend(self, phase=0): self.recommendations = [] # This first iteration of the recommender will provide very basic recommendation # We recommend heating controls based on the main heating system if self.property.main_heating["clean_description"] in [ "Room heaters, electric", "Electric storage heaters", "Electric storage heaters, radiators" ]: # Recommend high heat retention storage heaters self.recommend_electric_storage_heaters(phase=phase, system_change=True, heating_controls_only=False) return @staticmethod def check_simulation_difference(old_config, new_config): """ Given two dictionaries, that describe the heating control configurations, this method will compare the two and pick out the differences. These differences will be things that have been added and things that have been removed. This will be used to determine how we should be updating the configuration in the simulation :return: """ differences = {key + "_ending": new_config[key] for key in new_config if old_config[key] != new_config[key]} return differences @staticmethod def combine_heating_and_controls( controls_recommendations, heating_simulation_config, costs, description, phase, heating_controls_only, system_change ): """ Given a recommendation for heating controls, and a recommendation for the heating system, we combine the two into a single recommendation :param controls_recommendations: The heating controls recommendations :param heating_simulation_config: The simulation configuration for the heating system :param costs: The costs of the heating system :param description: The description of the recommendation :param phase: The phase of the recommendation :param heating_controls_only: If True, we will also add a recommendation for heating controls only :param system_change: Indicates if we are recommending a different type of heating system, compared to the current system. If we have a system change and we have a heat control recommendation, we only recommend both heating and controls together :return: """ # We produce recommendations with & without heating controls # We will also produce a recommendation for heating controls only heating_controls_switch = [True, False] if controls_recommendations else [False] if not heating_simulation_config: heating_controls_switch = [] if system_change and len(controls_recommendations): heating_controls_switch = [True] output = [] for controls_switch in heating_controls_switch: total_costs = costs.copy() recommendation_simulation_config = heating_simulation_config.copy() recommendation_description = description if controls_switch: # We add the costs of the heating controls, onto each key in the costs dictionary for key in total_costs: total_costs[key] += controls_recommendations[0][key] recommendation_simulation_config = { **recommendation_simulation_config, **controls_recommendations[0]["simulation_config"] } recommendation_description = f"{description} and {controls_recommendations[0]['description']}" recommendation = { "phase": phase, "parts": [ # TODO ], "type": "heating", "description": recommendation_description, "starting_u_value": None, "new_u_value": None, "sap_points": None, **total_costs, "simulation_config": recommendation_simulation_config } output.append(recommendation) if heating_controls_only and len(controls_recommendations): # Also add on a recommendation for heating controls only heating_control_recommendation = controls_recommendations[0].copy() # Capitalize the first letter of the description heating_control_recommendation["description"] = ( heating_control_recommendation["description"][0].upper() + heating_control_recommendation["description"][1:] ) output.append( { "phase": phase, "parts": [ # TODO ], "type": "heating", "starting_u_value": None, "new_u_value": None, "sap_points": None, **heating_control_recommendation } ) return output def recommend_electric_storage_heaters(self, phase, system_change, heating_controls_only): """ We recommend electric storage heaters as an upgrade to the heating system. We will recommend upgrading to a high heat retention storage system, if the current system is not already high heat retention storage :param phase: The phase of the recommendation :param system_change: Indicates if we are recommending a different type of heating system, compared to the current system :param heating_controls_only: Indicates if we should include a recommendation for just heating controls :return: """ controls_recommender = HeatingControlRecommender(self.property) # The heating controls we're recommending for are based on the recommended heating system high_heat_retention_contols_desc = "Controls for high heat retention storage heaters" # We only recommend Celect-type controls if the current heating system is not Celect-type controls if self.property.main_heating_controls["clean_description"] != high_heat_retention_contols_desc: controls_recommender.recommend(heating_description="Electric storage heaters, radiators") # Conditions for not needing this recommendation already_installed_hh_retention = ( "Electric storage heaters" in self.property.main_heating["clean_description"] and self.property.main_heating_controls["clean_description"].lower() == high_heat_retention_contols_desc.lower() ) # Conditions for not recommending electric storage heaters if already_installed_hh_retention: # No recommendation needed return # Set up artefacts, suitable for the simulation and regardless of controls heating_ending_config = MainHeatAttributes("Electric storage heaters, radiators").process() heating_simulation_config = check_simulation_difference( new_config=heating_ending_config, old_config=self.property.main_heating ) # This upgrade will only take the heating system to average energy efficiency heating_simulation_config["mainheat_energy_eff_ending"] = "Average" # Upgrade to electric storage heaters costs = self.costs.high_heat_electric_storage_heaters( number_heated_rooms=self.property.data["number-heated-rooms"] ) description = "Install high heat retention electric storage heaters" recommendations = self.combine_heating_and_controls( controls_recommendations=controls_recommender.recommendation, heating_simulation_config=heating_simulation_config, costs=costs, description=description, phase=phase, heating_controls_only=heating_controls_only, system_change=system_change ) self.recommendations.extend(recommendations)