Added costing for boiler

This commit is contained in:
Khalim Conn-Kowlessar 2024-04-03 14:39:28 +01:00
parent 08a657eb9f
commit 45552f5e06
4 changed files with 137 additions and 6 deletions

View file

@ -59,6 +59,26 @@ TTZC_ROOM_TEMPERATURE_SENSOR_LABOUR_HOURS = 0.17 # (Assume ~ 10 mins install pe
TTZC_SMART_RADIATOR_VALUES = 50
TTZC_SMART_RADIATOR_VALUES_LABOUR_HOURS = 0.37 # (Assume ~ 15-30 mins install per valve)
# Low carbon combi boiler - median value based on £2200 - £3000 range
LOW_CARBON_COMBI_BOILER = 2200
# boiler prices based on
# https://www.greenmatch.co.uk/boilers/30kw-boiler
# https://www.greenmatch.co.uk/boilers/35kw-boiler
# https://www.greenmatch.co.uk/boilers/40kw-boiler
# These are exclusive of installation costs
COMBI_BOILER_COSTS = {
"30kw": 1550,
"35kw": 1610,
"40kw": 1625
}
CONVENTIONAL_BOILER_COSTS = {
"30kw": 1117,
"35kw": 1546,
"40kw": 1776
}
class Costs:
"""
@ -1079,3 +1099,34 @@ class Costs:
"labour_hours": labour_hours,
"labour_days": labour_days,
}
def low_carbon_boiler(self, is_combi, size):
"""
Based on a basic estimate of median value £2600 to install a low carbon combi boiler
:return:
"""
unit_cost = COMBI_BOILER_COSTS[size] if is_combi else CONVENTIONAL_BOILER_COSTS[size]
# The unit cost is the cost without VAT
# We now need to estimate the cost of the works
labour_days = 2
labour_rate = 500
# Average cost of installation is 1 (maybe 2days) at £300 per day
# https://www.checkatrade.com/blog/cost-guides/new-boiler-cost/
# To be pessimistic, assume 2 days work and £500 day rate
labour_cost = labour_rate * self.labour_adjustment_factor * labour_days
# Add contingency and preliminaries
labour_cost = labour_cost * (1 + self.CONTINGENCY + self.PRELIMINARIES)
vat = labour_cost * self.VAT_RATE
subtotal_before_vat = unit_cost + labour_cost
total_cost = subtotal_before_vat + vat
return {
"total": total_cost,
"subtotal": subtotal_before_vat,
"vat": vat,
"labour_hours": labour_days * 8,
"labour_days": labour_days,
}

View file

@ -189,7 +189,7 @@ class HeatingControlRecommender:
# 2) The current heating controls are not already at 'Very Good' or above
if (
(self.property["thermostatic_control"] == "time and temperature zone control") or
(self.property.main_heating_controls["thermostatic_control"] == "time and temperature zone control") or
(self.property.data["mainheatc-energy-eff"] in ["Very Good"])
):
# No recommendation needed
@ -209,7 +209,9 @@ class HeatingControlRecommender:
self.recommendation.append(
{
"description": "upgrade heating controls to Room thermostat, programmer and TRVs",
**self.costs.time_and_temperature_zone_control(),
**self.costs.time_and_temperature_zone_control(
number_heated_rooms=int(self.property.data["number-heated-rooms"])
),
"simulation_config": simulation_config
}
)

View file

@ -28,7 +28,7 @@ class HeatingRecommender:
# if the property has mains heating with boiler and radiators, we recommend optimal heating controls
if self.property.main_heating["clean_description"] in ["Boiler and radiators, mains gas"]:
self.recommend_roomstat_programmer_trvs(phase=phase)
self.recommend_boiler_upgrades(phase=phase)
return
@staticmethod
@ -188,14 +188,89 @@ class HeatingRecommender:
self.recommendations.extend(recommendations)
def recommend_roomstat_programmer_trvs(self, phase):
"""
@staticmethod
def estimate_boiler_size(property_type, built_form, floor_area, floor_height, num_heated_rooms):
# Step 1: Base size estimation based on property type (as a starting point)
base_size = {
'Flat': 25,
'House': 30,
'Maisonette': 28,
'Bungalow': 27
}
# Step 2: Calculate the volume of the property
volume = floor_area * floor_height
# Step 3: Adjust base size for built form (to account for heat retention)
form_adjustment = {
'Mid-Terrace': 0,
'End-Terrace': 2,
'Semi-Detached': 4,
'Detached': 6
}
# Step 4: Further adjust for the total volume and number of heated rooms
volume_adjustment = (volume / 100) # Simplified adjustment factor for volume
rooms_adjustment = (num_heated_rooms - 5) * 0.5 # Assuming base case of 5 rooms
# Calculate the estimated boiler size
estimated_size = base_size[property_type] + form_adjustment[built_form] + volume_adjustment + rooms_adjustment
# Step 5: Align with available boiler sizes and ensure it does not exceed 35kW, as it's rare to need more
available_sizes = [30, 35, 40, 45, 50]
estimated_size = min(max(estimated_size, 30), 40) # Ensure within 30kW to 35kW range
# Find the closest available size (in this case, either rounding up or down to align with 30 or 35)
closest_size = min(available_sizes, key=lambda x: abs(x - estimated_size))
return closest_size
def recommend_boiler_upgrades(self, phase):
"""
This boiler recommendation will only recommend a like-for-like upgrade, since changing the system
is generally more expensive
:param phase:
:return:
"""
# We now recommend boiler upgrades, if applicable
if self.property.data["mainheat-energy-eff"] in ["Very Poor", "Poor", "Average"]:
boiler_size = self.estimate_boiler_size(
property_type=self.property.data["property-type"],
built_form=self.property.data["built-form"],
floor_area=self.property.floor_area,
floor_height=self.property.floor_height,
num_heated_rooms=self.property.data["number-heated-rooms"],
)
# If heating and hot water come from the mains, we need a combi boiler, otherwise we need a regular boiler
is_combi = self.property.hotwater["clean_description"] in ["From main system"]
if is_combi:
description = "Upgrade to a low carbon combi boiler"
else:
description = "Upgrade to a low carbon boiler"
self.recommendations.append(
{
"phase": phase,
"parts": [
# TODO
],
"type": "heating",
"description": description,
"starting_u_value": None,
"new_u_value": None,
"sap_points": None,
**self.costs.low_carbon_boiler(is_combi=is_combi, size=f"{boiler_size}kw")
}
)
# We recommend the heating controls
controls_recommender = HeatingControlRecommender(self.property)
controls_recommender.recommend(heating_description="Boiler and radiators, mains gas")
# We may have 2 recommendations from the heating controls
controls_recommender.recommendation
# The heating controls recommendation is distrinct from the boiler upgrade recommendation
# We insert phase into the recommendations for heating controls
for recommendation in controls_recommender.recommendation:
recommendation["phase"] = phase

View file

@ -110,6 +110,9 @@ class Recommendations:
self.heating_recommender.recommend(phase=phase)
if self.heating_recommender.recommendations:
property_recommendations.append(self.heating_recommender.recommendations)
# We check if we have distinct heating and heating controls recommendations
# If so, we increment by 2 (one of the heating system, one for the heating controls)
# otherwise we incremenet by 1
phase += 1
# Hot water