From 14a1f35fb16cbf1199afbd66ce50f598b5d7a10b Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Fri, 12 Apr 2024 16:27:26 +0100 Subject: [PATCH] ammended system change costs for first time central heating --- recommendations/Costs.py | 72 +++++++++++++++++++++++++-- recommendations/HeatingRecommender.py | 9 +++- 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/recommendations/Costs.py b/recommendations/Costs.py index 45c17102..0e67b352 100644 --- a/recommendations/Costs.py +++ b/recommendations/Costs.py @@ -83,6 +83,14 @@ CONVENTIONAL_BOILER_COSTS = { ROOM_HEATER_REMOVAL_COST = 120 ROOM_HEATER_REMOVAL_LABOUR_HOURS = 3 +# This is a cost quoted by Jim for a system flush - existig system will run more efficiently +SYSTEM_FLUSH_COST = 250 + +SINGLE_RADIATOR_COST = 150 +DOUBLE_RADIATOR_COST = 300 +FLUE_COST = 600 +PIPEWORK_COST = 750 # Min cost is £500 + class Costs: """ @@ -1126,9 +1134,45 @@ class Costs: "labour_days": np.ceil(removal_labour_hours / 8), } - def boiler(self, is_combi, size, exising_room_heaters, n_heated_rooms): + @staticmethod + def _estimate_n_radiators(number_habitable_rooms, total_floor_area, property_type, built_form): + # Base number of radiators: one per habitable room + base_radiators = number_habitable_rooms + + # Additional radiators for non-habitable essential areas (e.g., kitchens, hallways) + additional_radiators = 3 # Initial assumption + + # Adjust additional radiators based on property type + if property_type == 'Flat': + additional_radiators -= 1 # Flats may need fewer radiators due to less exposure + elif property_type in ['House', 'Bungalow', 'Maisonette']: + # Multiple floors in Maisonette may require additional heating points + additional_radiators += 2 # Houses and bungalows might need more due to greater exposure + else: + raise Exception("Invalid property type") + + # Adjust total radiator needs based on built form + form_factor = { + 'Mid-Terrace': 0.95, + 'Semi-Detached': 1.05, + 'Detached': 1.25, + 'End-Terrace': 1.05 + } + + # Calculate total heating power needed and number of radiators based on standard output + total_heating_power_required = total_floor_area * 80 # Watts per square meter + radiator_output = 1000 # Average wattage per radiator + total_radiators_based_on_power = (total_heating_power_required / radiator_output) * form_factor[built_form] + + # Final estimation taking the higher of calculated needs or base room count + estimated_radiators = max(total_radiators_based_on_power, base_radiators + additional_radiators) + return round(estimated_radiators) + + def boiler(self, is_combi, size, exising_room_heaters, system_change, n_heated_rooms, n_rooms): """ Based on a basic estimate of median value £2600 to install a low carbon combi boiler + First time central heating vosts can als be found here: + https://www.checkatrade.com/blog/cost-guides/central-heating-installation-cost/ :return: """ @@ -1137,11 +1181,11 @@ class Costs: # We now need to estimate the cost of the works labour_days = 2 labour_hours = labour_days * 8 - labour_rate = 500 + labour_rate = 300 # 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 + # To be pessimistic, assume 2 days work labour_cost = labour_rate * self.labour_adjustment_factor * labour_days # Add contingency and preliminaries labour_cost = labour_cost * (1 + self.CONTINGENCY + self.PRELIMINARIES) @@ -1161,6 +1205,28 @@ class Costs: subtotal_before_vat += removal_costing["subtotal"] labour_hours += removal_costing["labour_hours"] labour_days += removal_costing["labour_days"] + vat += removal_costing["vat"] + + if system_change: + # We need the cost of radiators + n_radiators = self._estimate_n_radiators( + number_habitable_rooms=n_rooms, + total_floor_area=self.property.floor_area, + property_type=self.property.data["property-type"], + built_form=self.property.data["built-form"] + ) + + additionals_labour_cost = labour_rate * self.labour_adjustment_factor + radiator_cost = DOUBLE_RADIATOR_COST * n_radiators + system_change_cost = radiator_cost + FLUE_COST + PIPEWORK_COST + additionals_labour_cost + system_change_cost_before_vat = system_change_cost / (1 + self.VAT_RATE) + system_change_vat = system_change_cost - system_change_cost_before_vat + # We add an extra labour day for the system change + labour_days += 1 + labour_hours += 8 + total_cost += system_change_cost + subtotal_before_vat += system_change_cost_before_vat + vat += system_change_vat return { "total": total_cost, diff --git a/recommendations/HeatingRecommender.py b/recommendations/HeatingRecommender.py index 27e4985a..d83b755e 100644 --- a/recommendations/HeatingRecommender.py +++ b/recommendations/HeatingRecommender.py @@ -18,6 +18,11 @@ class HeatingRecommender: self.recommendations = [] def recommend(self, phase=0): + + # TODO: We could have a system flush recommendation for an existing boiler, where there is no need to replace + # the boiler, but instead flushing the system will make it run more efficiently. There is a cost for this + # in the Costs class, stored as SYSTEM_FLUSH_COST + self.recommendations = [] # This first iteration of the recommender will provide very basic recommendation # We recommend heating controls based on the main heating system @@ -361,7 +366,9 @@ class HeatingRecommender: is_combi=is_combi, size=f"{boiler_size}kw", exising_room_heaters=exising_room_heaters, - n_heated_rooms=self.property.data["number-heated-rooms"] + system_change=system_change, + n_heated_rooms=self.property.data["number-heated-rooms"], + n_rooms=self.property.number_of_rooms ) is_override = "heating" in self.property.override