added simulation for secondary heating

This commit is contained in:
Khalim Conn-Kowlessar 2024-04-11 19:14:49 +01:00
parent 61584a6320
commit 3ecd7a9742
5 changed files with 102 additions and 14 deletions

View file

@ -456,7 +456,9 @@ class Property:
"double glazing installed during or after 2002" "double glazing installed during or after 2002"
) )
if recommendation["type"] in ["heating", "hot_water_tank_insulation", "heating_control"]: if recommendation["type"] in [
"heating", "hot_water_tank_insulation", "heating_control", "secondary_heating"
]:
# We update the data, as defined in the recommendaton # We update the data, as defined in the recommendaton
simulation_config = recommendation["simulation_config"] simulation_config = recommendation["simulation_config"]
@ -477,7 +479,7 @@ class Property:
"loft_insulation", "room_roof_insulation", "flat_roof_insulation", "loft_insulation", "room_roof_insulation", "flat_roof_insulation",
"solid_floor_insulation", "suspended_floor_insulation", "exposed_floor_insulation", "solid_floor_insulation", "suspended_floor_insulation", "exposed_floor_insulation",
"windows_glazing", "solar_pv", "heating", "hot_water_tank_insulation", "windows_glazing", "solar_pv", "heating", "hot_water_tank_insulation",
"heating_control", "heating_control", "secondary_heating"
]: ]:
raise NotImplementedError( raise NotImplementedError(
"Implement me, given type %s" % recommendation["type"] "Implement me, given type %s" % recommendation["type"]

View file

@ -1104,6 +1104,28 @@ class Costs:
"labour_days": labour_days, "labour_days": labour_days,
} }
def heater_removal(self, n_rooms):
"""
Estimates the costs of removal of heaters, including the redecoration costs of the space behind the heater
:return:
"""
removal_cost = ROOM_HEATER_REMOVAL_COST * n_rooms
removal_labour_hours = ROOM_HEATER_REMOVAL_LABOUR_HOURS * n_rooms
vat = removal_cost * self.VAT_RATE
subtotal_before_vat = removal_cost
total_cost = subtotal_before_vat + vat
return {
"total": total_cost,
"subtotal": subtotal_before_vat,
"vat": vat,
"labour_hours": removal_labour_hours,
"labour_days": np.ceil(removal_labour_hours / 8),
}
def boiler(self, is_combi, size, exising_room_heaters, n_heated_rooms): def boiler(self, is_combi, size, exising_room_heaters, n_heated_rooms):
""" """
Based on a basic estimate of median value £2600 to install a low carbon combi boiler Based on a basic estimate of median value £2600 to install a low carbon combi boiler
@ -1114,6 +1136,7 @@ class Costs:
# The unit cost is the cost without VAT # The unit cost is the cost without VAT
# We now need to estimate the cost of the works # We now need to estimate the cost of the works
labour_days = 2 labour_days = 2
labour_hours = labour_days * 8
labour_rate = 500 labour_rate = 500
# Average cost of installation is 1 (maybe 2days) at £300 per day # Average cost of installation is 1 (maybe 2days) at £300 per day
@ -1123,26 +1146,26 @@ class Costs:
# Add contingency and preliminaries # Add contingency and preliminaries
labour_cost = labour_cost * (1 + self.CONTINGENCY + self.PRELIMINARIES) labour_cost = labour_cost * (1 + self.CONTINGENCY + self.PRELIMINARIES)
# if there are existing room heaters, we need to add the cost of removing them # labour_days = labour_days + (removal_labour_hours / 8)
if exising_room_heaters:
removal_cost = ROOM_HEATER_REMOVAL_COST * n_heated_rooms
removal_labour_hours = ROOM_HEATER_REMOVAL_LABOUR_HOURS * n_heated_rooms
else:
removal_cost = 0
removal_labour_hours = 0
labour_cost = labour_cost + removal_cost
labour_days = labour_days + (removal_labour_hours / 8)
vat = labour_cost * self.VAT_RATE vat = labour_cost * self.VAT_RATE
subtotal_before_vat = unit_cost + labour_cost subtotal_before_vat = unit_cost + labour_cost
total_cost = subtotal_before_vat + vat total_cost = subtotal_before_vat + vat
# if there are existing room heaters, we need to add the cost of removing them
if exising_room_heaters:
removal_costing = self.heater_removal(n_rooms=n_heated_rooms)
# Add the totals to the existing totals
total_cost += removal_costing["total"]
subtotal_before_vat += removal_costing["subtotal"]
labour_hours += removal_costing["labour_hours"]
labour_days += removal_costing["labour_days"]
return { return {
"total": total_cost, "total": total_cost,
"subtotal": subtotal_before_vat, "subtotal": subtotal_before_vat,
"vat": vat, "vat": vat,
"labour_hours": labour_days * 8, "labour_hours": labour_hours,
"labour_days": labour_days, "labour_days": labour_days,
} }

View file

@ -319,7 +319,7 @@ class HeatingRecommender:
# Otherwise, we recommend a gas condensing boiler, which will server a larger property, that has multiple # Otherwise, we recommend a gas condensing boiler, which will server a larger property, that has multiple
# bathrooms # bathrooms
is_combi = ( is_combi = (
(self.property.data["number-heated-rooms"] <= 4) and (self.property.number_of_rooms <= 4) and
(self.property.n_bathrooms in [None, 0, 1]) (self.property.n_bathrooms in [None, 0, 1])
) )
if is_combi: if is_combi:

View file

@ -11,6 +11,7 @@ from recommendations.SolarPvRecommendations import SolarPvRecommendations
from recommendations.WindowsRecommendations import WindowsRecommendations from recommendations.WindowsRecommendations import WindowsRecommendations
from recommendations.HeatingRecommender import HeatingRecommender from recommendations.HeatingRecommender import HeatingRecommender
from recommendations.HotwaterRecommendations import HotwaterRecommendations from recommendations.HotwaterRecommendations import HotwaterRecommendations
from recommendations.SecondaryHeating import SecondaryHeating
from backend.ml_models.AnnualBillSavings import AnnualBillSavings from backend.ml_models.AnnualBillSavings import AnnualBillSavings
@ -46,6 +47,7 @@ class Recommendations:
self.solar_recommender = SolarPvRecommendations(property_instance=property_instance) self.solar_recommender = SolarPvRecommendations(property_instance=property_instance)
self.heating_recommender = HeatingRecommender(property_instance=property_instance) self.heating_recommender = HeatingRecommender(property_instance=property_instance)
self.hotwater_recommender = HotwaterRecommendations(property_instance=property_instance) self.hotwater_recommender = HotwaterRecommendations(property_instance=property_instance)
self.secondary_heating_recommender = SecondaryHeating(property_instance=property_instance)
def recommend(self): def recommend(self):
@ -130,6 +132,12 @@ class Recommendations:
property_recommendations.append(self.lighting_recommender.recommendation) property_recommendations.append(self.lighting_recommender.recommendation)
phase += 1 phase += 1
if "secondary_heating" not in self.exclusions:
self.secondary_heating_recommender.recommend(phase=phase)
if self.secondary_heating_recommender.recommendation:
property_recommendations.append(self.secondary_heating_recommender.recommendation)
phase += 1
# Renewables # Renewables
if "solar_pv" not in self.exclusions: if "solar_pv" not in self.exclusions:
self.solar_recommender.recommend(phase=phase) self.solar_recommender.recommend(phase=phase)

View file

@ -0,0 +1,55 @@
from recommendations.Costs import Costs
from backend.Property import Property
class SecondaryHeating:
"""
This class recommends the removal of the secondary heating system for properties that have a primary heating
system.
"""
# The list of existing heating systems that are accepted
ACCEPTED_MAINHEAT_DESCRIPTIONS = ["Boiler and radiators, mains gas"]
ACCEPTED_SECONDHEAT_DESCRIPTIONS = ["Room heaters, electric"]
# These are the heaters where works are required to remove them
FIXED_HEATER_DESCRIPTIONS = ["Room heaters, electric"]
def __init__(self, property_instance: Property):
self.property = property_instance
self.costs = Costs(self.property)
self.recommendation = []
def recommend(self, phase: int):
# Reset
self.recommendation = []
if self.property.main_heating["clean_description"] not in self.ACCEPTED_MAINHEAT_DESCRIPTIONS:
return
# TODO: We need to clean secondary data
if self.property.data['secondheat-description'] not in self.ACCEPTED_SECONDHEAT_DESCRIPTIONS:
return
if self.property.data['secondheat-description'] in self.FIXED_HEATER_DESCRIPTIONS:
# We have an associated cost otherwise, there is no cost
n_rooms = self.property.data['number-heated-rooms']
else:
n_rooms = 0
costs = self.costs.heater_removal(n_rooms=n_rooms)
self.recommendation.append(
{
"phase": phase,
"parts": [],
"type": "secondary_heating",
"description": "Remove the secondary heating system",
"starting_u_value": None,
"new_u_value": None,
"sap_points": None,
**costs,
"simulation_config": {
"secondheat_description_ending": "None"
}
}
)