Model/recommendations/HeatingRecommender.py

184 lines
8.3 KiB
Python

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)