diff --git a/recommendations/Costs.py b/recommendations/Costs.py index 08b05a8a..afcd48d9 100644 --- a/recommendations/Costs.py +++ b/recommendations/Costs.py @@ -1143,7 +1143,7 @@ class Costs: "labour_days": labour_days, } - def air_source_heat_pump(self): + def air_source_heat_pump(self, ashp_size): """ Based on the region and type of property, this function will produce a cost estimation for an air source heat pump. This cost will include the boiler upgrade scheme grant @@ -1151,14 +1151,19 @@ class Costs: """ # This is the average cost of a project, we'll add some additional contingency - regional_cost = MCS_AIR_SOURCE_HEAT_PUMP_COST_DATA[self.region] - total_cost = regional_cost * (1 + self.CONTINGENCY) - BOILER_UPGRADE_SCHEME_ASHP_VALUE + if ashp_size is None: + cost = [x for x in INSTALLER_ASHP_COSTS if x["capacity_kw"] is None][0]["cost"] + else: + cost = [x for x in INSTALLER_ASHP_COSTS if x][0]["cost"] + + # We add some contingency since there are additional costs such as resizing radiators, that could be required + total_cost = cost * (1 + self.CONTINGENCY) subtotal_before_vat = total_cost / (1 + self.VAT_RATE) vat = total_cost - subtotal_before_vat - # We assume 3 days installation - labour_days = 3 + # We assume 5 days installation + labour_days = 5 labour_hours = labour_days * 8 return { diff --git a/recommendations/HeatingRecommender.py b/recommendations/HeatingRecommender.py index 203bab87..bdc73e3b 100644 --- a/recommendations/HeatingRecommender.py +++ b/recommendations/HeatingRecommender.py @@ -1,3 +1,4 @@ +import re from recommendations.Costs import Costs, BOILER_UPGRADE_SCHEME_ASHP_VALUE from recommendations.recommendation_utils import ( check_simulation_difference, override_costs, combine_recommendation_configs @@ -368,6 +369,75 @@ class HeatingRecommender: description = ("Replace the existing boiler and cylinder without a thermostat with a new electric combi " "boiler") + def size_heat_pump(self): + """ + Given the methodology by installers (SCIS) this function will perform a basic heat loss calculation and + produce a recommendation for the size of the heat pump + :return: + """ + + floor_area = self.property.floor_area + + # We use the default heat loss W/m2 values are specified by the insaller, depending on the property type + + def remap_to_heat_loss(construction_age_band): + if "before 1900" in construction_age_band: + return "Pre 1900 (solid stone)" + elif "1900-1929" in construction_age_band: + return "Early 1900s (solid brick)" + elif re.search(r'1930|1949|1950|1966|1967|1975', construction_age_band): + return "1950-1980 (cavity void)" + elif re.search(r'1976|1982|1983|1990', construction_age_band): + return "Post 1980 (cavity wall construction)" + elif re.search(r'1991|1995|1996|2002|2003|2011', construction_age_band): + return "2000-2018" + elif "2012 onwards" in construction_age_band: + return "New build (2018+)" + else: + return None + + def select_heatpump_size(heat_loss_calculation): + """ + This function calculates the size of the heat pump based on the heat loss calculation, mapping + the heat loss calculation to the size of the heat pump in KW + :param heat_loss_calculation: This is calcualted as the floor area multipled by the heat loss constant, + divided by 1000 + """ + if heat_loss_calculation < 5: + return 5 + elif 5 <= heat_loss_calculation < 6: + return 6 + elif 6 <= heat_loss_calculation < 8.5: + return 8.5 + elif 8.5 <= heat_loss_calculation < 11.2: + return 11.2 + elif 11.2 <= heat_loss_calculation < 14: + return 14 + elif 14 <= heat_loss_calculation < 17: + return 17 + elif 17 <= heat_loss_calculation < 20: + return 20 + else: + return None + + heat_loss_constants = { + "New build (2018+)": 35, + "2000-2018": 50, + "Post 1980 (cavity wall construction)": 60, + "1950-1980 (cavity void)": 70, + "Early 1900s (solid brick)": 80, + "Pre 1900 (solid stone)": 90 + } + + heat_loss_group = remap_to_heat_loss(self.property.construction_age_band) + heat_loss_constant = heat_loss_constants[heat_loss_group] + + heat_loss_calculation = floor_area * heat_loss_constant / 1000 + + heat_pump_size = select_heatpump_size(heat_loss_calculation) + + return heat_pump_size + def recommend_air_source_heat_pump(self, phase, has_cavity_or_loft_recommendations, _return=False): """ This method will implement the recommendation for an air source heat pump @@ -383,8 +453,9 @@ class HeatingRecommender: controls_recommender = HeatingControlRecommender(self.property) controls_recommender.recommend(heating_description="Air source heat pump, radiators, electric") + ashp_size = self.size_heat_pump() - ashp_costs = self.costs.air_source_heat_pump() + ashp_costs = self.costs.air_source_heat_pump(ashp_size) if non_intrusive_recommendation: # Update with non-intrusive recommendation if non_intrusive_recommendation.get("cost"):