mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Created high heat retention storage heaters recommendations + set up HotwaterRecommendations class
This commit is contained in:
parent
3dc5405b74
commit
6933678810
8 changed files with 90 additions and 117 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
13
recommendations/HotwaterRecommendations.py
Normal file
13
recommendations/HotwaterRecommendations.py
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue