mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Added costing for boiler
This commit is contained in:
parent
08a657eb9f
commit
45552f5e06
4 changed files with 137 additions and 6 deletions
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue