Created high heat retention storage heaters recommendations + set up HotwaterRecommendations class

This commit is contained in:
Khalim Conn-Kowlessar 2024-02-20 20:16:56 +00:00
parent 3dc5405b74
commit 6933678810
8 changed files with 90 additions and 117 deletions

View file

@ -42,6 +42,25 @@ logger = setup_logger()
BATCH_SIZE = 5
def patch_epc(config, epc_records):
"""
This utility function is useful to patch the epc data if we have data from the customer
:return:
"""
number_habitable_rooms = config.get("number-habitable-rooms", None)
number_heated_rooms = config.get("number-heated-rooms", None)
if number_habitable_rooms is not None:
epc_records["original_epc"]["number-habitable-rooms"] = int(number_habitable_rooms)
if number_heated_rooms is not None:
epc_records["original_epc"]["number-heated-rooms"] = int(number_heated_rooms)
return epc_records
router = APIRouter(
prefix="/plan",
tags=["plan"],
@ -56,6 +75,9 @@ async def trigger_plan(body: PlanTriggerRequest):
session = sessionmaker(bind=db_engine)()
created_at = datetime.now().isoformat()
# TODO: We should store the trigger file path in the database with the plan so we can track the file that
# triggered the plan
try:
session.begin()
logger.info("Getting the inputs")
@ -96,8 +118,7 @@ async def trigger_plan(body: PlanTriggerRequest):
'full_sap_epc': epc_searcher.full_sap_epc.copy(),
'old_data': epc_searcher.older_epcs.copy(),
}
# We can patch the data if we are provided data from the customer
epc_records = patch_epc(config, epc_records)
prepared_epc = EPCRecord(
epc_records=epc_records,

View file

@ -73,15 +73,26 @@ def app():
if newest_epc is None:
raise Exception("FX ME")
if row["Beds"] == "Studio":
number_heated_rooms = 2
number_habitable_rooms = 2
else:
# Assume one room for communal space, one room for bathroom
number_heated_rooms = row["Beds"] + 2
number_habitable_rooms = row["Beds"] + 2
to_append = {
**row.to_dict(),
"uprn": newest_epc["uprn"],
"postcode to check": newest_epc["postcode"],
"address": newest_epc["address1"],
"postcode": newest_epc["postcode"],
# "walls-description": newest_epc["walls-description"],
# "roof-description": newest_epc["roof-description"],
# "floor-description": newest_epc["floor-description"],
# "total-floor-area": newest_epc["total-floor-area"],
"full-address": newest_epc["address"],
"number-heated-rooms": number_heated_rooms,
"number-habitable-rooms": number_habitable_rooms,
}
processed_asset_list.append(to_append)
@ -92,23 +103,6 @@ def app():
epc_data_df = pd.DataFrame(epc_data)
example = epc_data_df.iloc[11, :]
rest = epc_data_df[epc_data_df["address1"] != example["address1"]]
z = rest[
(rest["total-floor-area"] == example["total-floor-area"]) &
(rest["current-energy-rating"] == "C")
]
# Walls better in the example
z["walls-description"]
example["walls-description"]
# Example has a property above
z["roof-description"]
example["roof-description"]
compare = pd.concat([pd.DataFrame(example).T, z])
compare["mainheat-description"]
# We store this data
# Store the data in s3
filename = f"{USER_ID}/{PORTFOLIO_ID}/test_inputs.csv"

View file

@ -36,9 +36,7 @@ def app():
cleaned_data = {}
epc_directories = [entry for entry in EPC_DIRECTORY.iterdir() if entry.is_dir()]
for directory in tqdm(epc_directories):
data = pd.read_csv(directory / "certificates.csv", low_memory=False)
# Rename the columns to the same format as the api returns
data.columns = [c.replace("_", "-").lower() for c in data.columns]
# Take just date before the date threshold

View file

@ -935,7 +935,7 @@ class Costs:
"labour_days": labour_days,
}
def electric_storage_heaters(self, number_heated_rooms):
def high_heat_electric_storage_heaters(self, number_heated_rooms):
"""
We base the estimates for the cost of electric storage heaters on the cost per room as estimated by the
@ -946,7 +946,7 @@ class Costs:
:param number_heated_rooms: int, number of rooms to be heated
"""
total_cost = 1000 * number_heated_rooms
total_cost = 1500 * number_heated_rooms
subtotal_before_vat = total_cost / (1 + self.VAT_RATE)
vat = total_cost - subtotal_before_vat

View file

@ -19,14 +19,12 @@ class HeatingControlRecommender:
# This first iteration of the recommender will provide very basic recommendation
# We recommend heating controls based on the main heating system
if heating_description in [
"Room heaters, electric", "Electric storage heaters"
]:
if heating_description in ["Room heaters, electric"]:
self.recommend_room_heaters_electric_controls()
return
if heating_description in ["Electric storage heaters, radiators"]:
self.recommend_celect_type_controls()
if heating_description in ["Electric storage heaters", "Electric storage heaters, radiators"]:
self.recommend_high_heat_retention_controls()
return
def recommend_room_heaters_electric_controls(self):
@ -78,18 +76,18 @@ class HeatingControlRecommender:
# We don't implement any other recommendations right now
return
def recommend_celect_type_controls(self):
def recommend_high_heat_retention_controls(self):
"""
If the home has Electric storage heaters, radiators, we start by identifying potential heating controls that
could
be upgraded, that would provide a practical impact. This will be the least invasive improvement.
When applicable, we recommend upgrading the heating controls to high heat retention controls. This is a
specific type of control system that is designed to work with electric storage heaters. It is a more
efficient control system than the standard controls that come with electric storage heaters.
We can then consider the heating system itself
:return:
"""
# We recommend upgrading to Celect type controls
ending_config = MainheatControlAttributes("Celect-type controls").process()
ending_config = MainheatControlAttributes("Controls for high heat retention storage heaters").process()
# We look at what has changed in the ending config, and compare it to the current config
simulation_config = check_simulation_difference(
new_config=ending_config, old_config=self.property.main_heating_controls
@ -99,7 +97,7 @@ class HeatingControlRecommender:
self.recommendation.append(
{
"description": "upgrade heating controls to Celect type controls",
"description": "upgrade heating controls to High Heat Retention Storage Heater Controls",
**self.costs.celect_type_controls(),
"simulation_config": simulation_config
}

View file

@ -1,3 +1,5 @@
import pandas as pd
from recommendations.Costs import Costs
from recommendations.recommendation_utils import check_simulation_difference
from backend.Property import Property
@ -17,14 +19,11 @@ class HeatingRecommender:
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"] == "Room heaters, electric":
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_room_heaters_electric(phase=phase, system_change=False, heating_controls_only=True)
# self.recommend_electric_storage_heaters(phase=phase, system_change=True, heating_controls_only=False)
return
if self.property.main_heating["clean_description"] == "Electric storage heaters, radiators":
self.recommend_electric_storage_heaters(phase=phase, system_change=False, heating_controls_only=True)
self.recommend_electric_storage_heaters(phase=phase, system_change=True, heating_controls_only=False)
return
@staticmethod
@ -130,6 +129,9 @@ class HeatingRecommender:
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
@ -139,36 +141,20 @@ class HeatingRecommender:
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"] != "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
efficient_room_heaters = self.property.main_heating["clean_description"] == "Room heaters, electric" and (
self.property.data["mainheat-energy-eff"] not in ["Poor", "Very Poor", "Average"]
)
efficient_storage_heaters = (
(self.property.main_heating["clean_description"] in [
"Electric storage heaters, radiators", "Electric storage heaters"
]) and self.property.data["mainheat-energy-eff"] not in ["Poor", "Very Poor", "Average"]
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 efficient_room_heaters or efficient_storage_heaters:
# We do just heating controls
self.recommendations.extend(
self.combine_heating_and_controls(
controls_recommendations=controls_recommender.recommendation,
heating_simulation_config={},
costs={},
description="",
phase=phase,
heating_controls_only=heating_controls_only,
system_change=system_change
)
)
if already_installed_hh_retention:
# No recommendation needed
return
# Set up artefacts, suitable for the simulation and regardless of controls
@ -180,60 +166,10 @@ class HeatingRecommender:
heating_simulation_config["mainheat_energy_eff_ending"] = "Average"
# Upgrade to electric storage heaters
costs = self.costs.electric_storage_heaters(
costs = self.costs.high_heat_electric_storage_heaters(
number_heated_rooms=self.property.data["number-heated-rooms"]
)
description = "Install 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)
def recommend_room_heaters_electric(self, phase, system_change, heating_controls_only):
"""
If the home has Room heaters, electric, we start by identifying potential heating controls that could
be upgraded, that would provide a practical impact. This will be the least invasive improvement.
We can then consider the heating system itself
: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)
if self.property.main_heating_controls["clean_description"] != "Programmer and appliance thermostats":
controls_recommender.recommend(heating_description='Room heaters, electric')
if self.property.data["mainheat-energy-eff"] not in ["Poor", "Very Poor"]:
# We do just heating controls
self.recommendations.extend(
self.combine_heating_and_controls(
controls_recommendations=controls_recommender.recommendation,
heating_simulation_config={},
costs={},
description="",
phase=phase,
heating_controls_only=heating_controls_only,
system_change=system_change
)
)
return
costs = self.costs.electric_room_heaters(
number_heated_rooms=self.property.data["number-heated-rooms"]
)
description = "Upgrade electric room heaters to efficient electric radiators"
heating_simulation_config = {"mainheat_energy_eff_ending": "Average"}
description = "Install high heat retention electric storage heaters"
recommendations = self.combine_heating_and_controls(
controls_recommendations=controls_recommender.recommendation,

View file

@ -0,0 +1,13 @@
from backend.Property import Property
from recommendations.Costs import Costs
class HotwaterRecommendations:
def __init__(self, property_instance: Property):
self.property = property_instance
self.costs = Costs(self.property)
self.recommendation = []
def recommend(self):
pass

View file

@ -47,6 +47,12 @@ class WallRecommendations(Definitions):
# we still consider it as an option
U_VALUE_ERROR = 0.01
# Typically when the U-value is around 0.75 and below, and the home is a new build, this is a good indication
# that the home is already insulated with at least some partial insulation. We don't recommend insulation
# in this case. This estimate was verified with the Warmfront team and 0.75 has been used as a conservative
# threshold
NEW_BUILD_INSULATED = 0.75
def __init__(
self,
property_instance: Property,
@ -114,6 +120,13 @@ class WallRecommendations(Definitions):
if self.property.walls["thermal_transmittance_unit"] != self.U_VALUE_UNIT:
raise NotImplementedError("Haven't handled the case of other u value units yet")
# If the property is a new build and the U-value is below 0.75, we don't recommend insulation because it's
# not practical
if (self.property.data["transaction-type"] == "new dwelling") and (u_value <= self.NEW_BUILD_INSULATED):
# Recommend nothing
return
# We can't detect it's a cavity wall, but it was built after 1990 so likely built with insulation already
# + it already has a U-value WORSE than the building regulations, so we recommend either internal or
# external wall insulation