diff --git a/backend/apis/GoogleSolarApi.py b/backend/apis/GoogleSolarApi.py index 606b6970..f6e1b96d 100644 --- a/backend/apis/GoogleSolarApi.py +++ b/backend/apis/GoogleSolarApi.py @@ -69,7 +69,7 @@ class GoogleSolarApi: self.floor_area = None self.roof_area = None self.roof_segment_indexes = None - self.panel_area = None + self.panel_area = assumptions.RDSAP_AREA_PER_PANEL self.panel_wattage = None self.panel_performance = None @@ -166,10 +166,6 @@ class GoogleSolarApi: self.roof_area = self.insights_data["solarPotential"]["wholeRoofStats"]['areaMeters2'] self.floor_area = self.insights_data["solarPotential"]["wholeRoofStats"]['groundAreaMeters2'] - self.panel_area = ( - self.insights_data["solarPotential"]["panelHeightMeters"] * - self.insights_data["solarPotential"]["panelWidthMeters"] - ) self.panel_wattage = self.insights_data["solarPotential"]["panelCapacityWatts"] if self.panel_wattage != 400: # In the API documentation, it claims that the default output is 250W, however we've only seen 400W, so if @@ -450,8 +446,8 @@ class GoogleSolarApi: # We want max roi, minimal generation deficit, and max generation value - we create a ranking score # Assign equal weights to each metric - weights = {'roi': 0.6, 'generation_value': 0.2, 'generation_deficit': 0.2} - metrics = panel_performance[['roi', 'generation_value', 'generation_deficit']] + weights = {'roi': 0.8, 'generation_value': 0.2} + metrics = panel_performance[['roi', 'generation_value']].copy() # Normalize the columns (0 to 1 scale) scaler = MinMaxScaler() @@ -459,12 +455,11 @@ class GoogleSolarApi: # Convert normalized metrics back to a dataframe normalized_metrics_df = pd.DataFrame( - normalized_metrics, columns=['roi', 'generation_value', 'generation_deficit'] + normalized_metrics, columns=['roi', 'generation_value'] ) normalized_metrics_df['combined_score'] = ( normalized_metrics_df['roi'] * weights['roi'] + - normalized_metrics_df['generation_value'] * weights['generation_value'] + - (1 - normalized_metrics_df['generation_deficit']) * weights['generation_deficit'] + normalized_metrics_df['generation_value'] * weights['generation_value'] ) panel_performance['combined_score'] = normalized_metrics_df['combined_score'].values @@ -799,7 +794,6 @@ class GoogleSolarApi: "panel_performance": solar_api_client.panel_performance, "unit_share_of_energy": 1 }, - roof_area=solar_api_client.roof_area ) return input_properties @@ -825,7 +819,7 @@ class GoogleSolarApi: n_panels=10, has_battery=False, n_floors=property_instance.number_of_floors )["total"], 'weighted_ratio': None, - 'panneled_roof_area': 10 * 1.8, + 'panneled_roof_area': 10 * assumptions.RDSAP_AREA_PER_PANEL, 'array_wattage': 4000, 'initial_ac_kwh_per_year': 4000 * 0.95, # Assumed 95% efficient wattage -> ac 'lifetime_ac_kwh': None, @@ -845,7 +839,7 @@ class GoogleSolarApi: n_panels=6, has_battery=False, n_floors=property_instance.number_of_floors )["total"], 'weighted_ratio': None, - 'panneled_roof_area': 6 * 1.8, + 'panneled_roof_area': 6 * assumptions.RDSAP_AREA_PER_PANEL, 'array_wattage': 2400, 'initial_ac_kwh_per_year': 2400 * 0.95, # Assumed 95% efficient wattage -> ac 'lifetime_ac_kwh': None, diff --git a/backend/app/assumptions.py b/backend/app/assumptions.py index 80baa69f..5571e13b 100644 --- a/backend/app/assumptions.py +++ b/backend/app/assumptions.py @@ -7,6 +7,11 @@ AVERAGE_ASHP_EFFICIENCY = 250 # be exported SOLAR_CONSUMPTION_PROPORTION = 0.5 +# Typically, each solar panel takes up around 3.4 m2 of roof space under RdSAP. This was been verified in Elmhurst +RDSAP_AREA_PER_PANEL = 3.4 + +SOCIAL_TENURES = ["Rented (social)", "rental (social)"] + DESCRIPTIONS_TO_FUEL_TYPES = { "Air source heat pump, radiators, electric": { "fuel": "Electricity", "cop": AVERAGE_ASHP_EFFICIENCY / 100 diff --git a/backend/app/plan/router.py b/backend/app/plan/router.py index 2dc03e60..f4924c71 100644 --- a/backend/app/plan/router.py +++ b/backend/app/plan/router.py @@ -631,27 +631,38 @@ async def trigger_plan(body: PlanTriggerRequest): for p in input_properties: if not recommendations.get(p.id): continue + input_measures = prepare_input_measures(recommendations[p.id], body.goal) current_sap_points = int(p.data["current-energy-efficiency"]) target_sap_points = epc_to_sap_lower_bound(body.goal_value) sap_gain = CostOptimiser.calculate_sap_gain_with_slack(target_sap_points - current_sap_points) - if body.budget: - optimiser = GainOptimiser( - input_measures, max_cost=body.budget, max_gain=sap_gain if sap_gain > 0 else 0 - ) + if not body.optimise: + if body.goal != "Increasing EPC": + raise NotImplementedError("Only EPC optimisation is currently supported") + solution = [] + for sub_list in input_measures: + # Select the entry with the highest gain, and if tied, choose the one with the lowest cost + best_measure = max(sub_list, key=lambda x: (x['gain'], -x['cost'])) + solution.append(best_measure) else: - # The minimum gain is the minimum number of SAP points required to get to the target SAP band - # If the gain is negative, the optimiser will return an empty solution - optimiser = CostOptimiser( - input_measures, - min_gain=sap_gain - ) - optimiser.setup() - optimiser.solve() - solution = optimiser.solution + if body.budget: + optimiser = GainOptimiser( + input_measures, max_cost=body.budget, max_gain=sap_gain if sap_gain > 0 else 0 + ) + else: + # The minimum gain is the minimum number of SAP points required to get to the target SAP band + # If the gain is negative, the optimiser will return an empty solution + optimiser = CostOptimiser( + input_measures, + min_gain=sap_gain + ) + + optimiser.setup() + optimiser.solve() + solution = optimiser.solution selected_recommendations = {r["id"] for r in solution} diff --git a/backend/app/plan/schemas.py b/backend/app/plan/schemas.py index 447e0da2..29a7d5e3 100644 --- a/backend/app/plan/schemas.py +++ b/backend/app/plan/schemas.py @@ -86,6 +86,9 @@ class PlanTriggerRequest(BaseModel): # exists in the portfolio, it will be ignored multi_plan: Optional[bool] = False + # if False, allows optimisation to be switched off + optimise: Optional[bool] = False + _allowed_goals = {"Increasing EPC"} _allowed_housing_types = {"Social", "Private"} diff --git a/recommendations/Costs.py b/recommendations/Costs.py index 08b05a8a..5554245f 100644 --- a/recommendations/Costs.py +++ b/recommendations/Costs.py @@ -841,7 +841,7 @@ class Costs: "labour_days": labour_days, } - def high_heat_electric_storage_heaters(self, number_heated_rooms): + def high_heat_electric_storage_heaters(self, number_heated_rooms, needs_cylinder): """ We base the estimates for the cost of electric storage heaters on the cost per room as estimated by the @@ -852,7 +852,12 @@ class Costs: :param number_heated_rooms: int, number of rooms to be heated """ - total_cost = 1500 * number_heated_rooms + if needs_cylinder: + # 1000 is the cost of a new hot water cylinder + total_cost = 1200 * number_heated_rooms + 1000 + else: + # 500 is the cost of a dual immersion heater - a rough estimate + total_cost = 1200 * number_heated_rooms + 500 subtotal_before_vat = total_cost / (1 + self.VAT_RATE) vat = total_cost - subtotal_before_vat @@ -1143,7 +1148,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 +1156,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..7dc4f8b2 100644 --- a/recommendations/HeatingRecommender.py +++ b/recommendations/HeatingRecommender.py @@ -1,3 +1,5 @@ +import re +import backend.app.assumptions as assumptions 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 +370,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 +454,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"): @@ -413,11 +485,13 @@ class HeatingRecommender: # This is a map from the heating controls description to the description of the air source heat pump set up ashp_descriptions = { "Time and temperature zone control": ( - "Install an air source heat pump, and upgrade heating controls to Smart Thermostats, " - "room sensors and smart radiator valves (time & temperature zone control)." + f"Install a {ashp_size}KW air source heat pump, and upgrade heating controls to Smart Thermostats, " + "room sensors and smart radiator valves (time & temperature zone control). Ensure you have an 18 or " + "24 hour tariff" ), "Programmer, TRVs and bypass": ( - "Install an air source heat pump, with programmer, TRVs and a Bypass valve." + f"Install a {ashp_size}KW air source heat pump, with programmer, TRVs and a Bypass valve. Ensure you " + "have an 18 or 24 hour tariff" ), } @@ -434,7 +508,7 @@ class HeatingRecommender: ashp_costs_with_controls[key] += controls_rec[key] if controls_rec is None: - description = "Install a Mitsubish air source heat pump." + description = f"Install a {ashp_size}KW Air source heat pump. Ensure you have an 18 or 24 hour tariff" elif already_installed: description = "The property already has an air source heat pump, no further action needed." else: @@ -443,17 +517,16 @@ class HeatingRecommender: # If the property does not have existing cavity and loft insulation, we include a note that the cost # includes the boiler upgrade scheme and that the cavity and loft need to be treated, to ensure access # to the funding - if not non_intrusive_recommendation: + if not non_intrusive_recommendation and self.property.data["tenure"] not in assumptions.SOCIAL_TENURES: if has_cavity_or_loft_recommendations: description = description + ( - f" The cost includes the £" - f"{BOILER_UPGRADE_SCHEME_ASHP_VALUE} boiler upgrade scheme grant. " - f"You must ensure that the property has an insulated cavity and " - f"270mm+ loft insulation to qualify for the grant" + f" You must ensure that the property has an insulated cavity and " + f"270mm+ loft insulation to qualify for the grant, to claim £" + f"{BOILER_UPGRADE_SCHEME_ASHP_VALUE} of funding from the boiler upgrade scheme grant. " ) else: description = description + ( - f" The cost includes the £{BOILER_UPGRADE_SCHEME_ASHP_VALUE} boiler upgrade scheme grant" + f" £{BOILER_UPGRADE_SCHEME_ASHP_VALUE} of funding can be claimed from the boiler upgrade scheme" ) simulation_config = { @@ -739,20 +812,37 @@ class HeatingRecommender: new_heating_description = self.DUAL_HEATING_DESCRIPTIONS[ self.property.main_heating["clean_description"] ]["hhr"]["mainheating_description"] + new_hot_water_description = self.property.hotwater["clean_description"] # We keep the hot water system else: new_heating_description = "Electric storage heaters" + new_hot_water_description = "Electric immersion, off-peak" # Set up artefacts, suitable for the simulation and regardless of controls heating_ending_config = MainHeatAttributes(new_heating_description).process() heating_simulation_config = check_simulation_difference( new_config=heating_ending_config, old_config=self.property.main_heating ) + + hot_water_end_config = HotWaterAttributes(new_hot_water_description).process() + hot_water_simulation_config = check_simulation_difference( + new_config=hot_water_end_config, old_config=self.property.hotwater + ) + + heating_simulation_config = { + **heating_simulation_config, + **hot_water_simulation_config + } # This upgrade will only take the heating system to average energy efficiency - if self.property.data["mainheat-energy-eff"] in ["Very Poor", "Poor"]: + if self.property.data["mainheat-energy-eff"] in ["Very Poor", "Poor"] and not self.dual_heating: heating_simulation_config["mainheat_energy_eff_ending"] = "Average" else: heating_simulation_config["mainheat_energy_eff_ending"] = self.property.data["mainheat-energy-eff"] + if self.property.data["hot-water-energy-eff"] in ["Very Poor", "Poor"]: + heating_simulation_config["hot_water_energy_eff_ending"] = "Average" + else: + heating_simulation_config["hot_water_energy_eff_ending"] = self.property.data["hot-water-energy-eff"] + # If the property is off-gas and has no heating system in place, the number of heated rooms will actually # be 0, so we use the number of rooms as the figure number_heated_rooms = ( @@ -768,7 +858,8 @@ class HeatingRecommender: # Upgrade to electric storage heaters costs = self.costs.high_heat_electric_storage_heaters( - number_heated_rooms=number_heated_rooms + number_heated_rooms=number_heated_rooms, + needs_cylinder=self.property.hotwater["system_type"] == "from main system" ) if self.dual_heating: description = self.DUAL_HEATING_DESCRIPTIONS[ @@ -776,7 +867,7 @@ class HeatingRecommender: ]["hhr"]["recommendation_description"] else: - description = "Install high heat retention electric storage heaters." + description = "Install high heat retention electric storage heaters with an appropriate off-peak tariff." # We check the existing heating system and controls if ( @@ -790,6 +881,8 @@ class HeatingRecommender: heating_description_simulation = { "mainheat-description": new_heating_description, "mainheat-energy-eff": heating_simulation_config["mainheat_energy_eff_ending"], + "hotwater-description": new_hot_water_description, + "hot-water-energy-eff": heating_simulation_config["hot_water_energy_eff_ending"] } recommendations = self.combine_heating_and_controls( diff --git a/recommendations/RoofRecommendations.py b/recommendations/RoofRecommendations.py index c86c5b30..52313121 100644 --- a/recommendations/RoofRecommendations.py +++ b/recommendations/RoofRecommendations.py @@ -337,7 +337,7 @@ class RoofRecommendations: if proposed_depth >= 300: new_efficiency = "Very Good" else: - if self.property.data["walls-energy-eff"] not in ["Good", "Very Good"]: + if self.property.data["roof-energy-eff"] not in ["Good", "Very Good"]: new_efficiency = "Good" else: new_efficiency = "Very Good" @@ -416,7 +416,7 @@ class RoofRecommendations: self.recommendations = recommendations - def recommend_room_roof_insulation(self, u_value, phase): + def recommend_room_roof_insulation(self, u_value, phase, default_u_values): """ This method recommends room in roof insulation for properties that have been identified to possess a room in roof. @@ -455,6 +455,8 @@ class RoofRecommendations: - Flat ceilings can be insulated like a standard loft. :param u_value: Current u-value of the roof + :param phase: Phase of the recommendation + :param default_u_values: Use default u-values :return: """ @@ -495,7 +497,7 @@ class RoofRecommendations: sap_points = rir_non_invasive_recommendation.get("sap_points", None) # Could also be Roof room(s), ceiling insulated - new_descriptin = "Pitched, insulated at rafters" + new_descriptin = "Roof room(s), insulated" roof_ending_config = RoofAttributes(new_descriptin).process() roof_simulation_config = check_simulation_difference( new_config=roof_ending_config, old_config=self.property.roof, prefix="roof_" @@ -505,6 +507,19 @@ class RoofRecommendations: else: new_efficiency = self.property.data["roof-energy-eff"] + if default_u_values: + new_u_value = get_roof_u_value( + insulation_thickness="average", + has_dwelling_above=self.property.roof["has_dwelling_above"], + is_loft=self.property.roof["is_loft"], + is_roof_room=self.property.roof["is_roof_room"], + is_thatched=self.property.roof["is_thatched"], + age_band=self.property.age_band, + is_flat=self.property.roof["is_flat"], + is_pitched=self.property.roof["is_pitched"], + is_at_rafters=self.property.roof["is_at_rafters"], + ) + simulation_config = { **roof_simulation_config, "roof_thermal_transmittance_ending": new_u_value, diff --git a/recommendations/SolarPvRecommendations.py b/recommendations/SolarPvRecommendations.py index 1eb584ca..eb4616ea 100644 --- a/recommendations/SolarPvRecommendations.py +++ b/recommendations/SolarPvRecommendations.py @@ -17,6 +17,8 @@ class SolarPvRecommendations: MAX_SYSTEM_WATTAGE = 6000 MIN_SYSTEM_WATTAGE = 1000 + MAX_ROOF_AREA_PERCENTAGE = 0.7 + def __init__(self, property_instance): """ :param property_instance: Instance of the Property class, for the home associated to property_id @@ -187,15 +189,20 @@ class SolarPvRecommendations: ) else: # TODO: There may be some instances where we don't want to use the solar API so we should cover for them - panel_performance = self.property.solar_panel_configuration["panel_performance"] + panel_performance = self.property.solar_panel_configuration["panel_performance"].copy() + # We don't allow for more than 70% of the roof to be covered + panel_performance = panel_performance[ + panel_performance["panneled_roof_area"] / self.property.roof_area <= self.MAX_ROOF_AREA_PERCENTAGE + ] + roof_area = self.property.roof_area solar_configurations = panel_performance.head(3).reset_index(drop=True) # We combine each of these configurations with estimates with and without a battery for rank, recommendation_config in solar_configurations.iterrows(): roof_coverage_percent = round(recommendation_config["panneled_roof_area"] / roof_area * 100) - # We round up to the nearest 10 - roof_coverage_percent = np.ceil(roof_coverage_percent / 10) * 10 + # We round up to the nearest 5 + roof_coverage_percent = np.ceil(roof_coverage_percent / 5) * 5 for has_battery in [False, True]: cost_result = self.costs.solar_pv( has_battery=has_battery, diff --git a/recommendations/recommendation_utils.py b/recommendations/recommendation_utils.py index 8ddfe1ef..3364f0fa 100644 --- a/recommendations/recommendation_utils.py +++ b/recommendations/recommendation_utils.py @@ -282,11 +282,14 @@ def get_u_value_from_s9( ): """Get the U-value from table S9 based on the insulation thickness.""" - if thickness in ["below average", "average", "above average", "none", None, "0", 0] or ( + if thickness in ["below average", "average", "above average", "none", None] or ( not is_loft and not is_roof_room and not is_at_rafters ): return None + if thickness in [0, "0"] and is_loft: + return None + # Determine the column to refer based on the roof type column = ( "Thatched_roof_U_value_W_m2K" @@ -294,8 +297,11 @@ def get_u_value_from_s9( else "Slates_or_tiles_U_value_W_m2K" ) - # Get the correct U-value based on the insulation thickness - return s9[s9["Insulation_thickness_mm"] >= thickness][column].iloc[0] + if thickness in [0, "0"] and is_roof_room: + return s9[pd.isnull(s9["Insulation_thickness_mm"])][column].iloc[0] + else: + # Get the correct U-value based on the insulation thickness + return s9[s9["Insulation_thickness_mm"] >= thickness][column].iloc[0] def get_roof_u_value( diff --git a/recommendations/tests/test_data/heating_recommendations_data.py b/recommendations/tests/test_data/heating_recommendations_data.py index 8697e095..51bf0378 100644 --- a/recommendations/tests/test_data/heating_recommendations_data.py +++ b/recommendations/tests/test_data/heating_recommendations_data.py @@ -39,15 +39,8 @@ testing_examples = [ 'fixed-lighting-outlets-count': 10.0, 'low-energy-fixed-light-count': 7.0, 'uprn': 100110195416.0, 'uprn-source': 'Address Matched' }, - "heating_recommendation_descriptions": [ - "Install an air source heat pump, and upgrade heating controls to Smart Thermostats, room sensors and " - "smart radiator valves (time & temperature zone control). The cost includes the £7500 boiler upgrade " - "scheme grant", - ], - "heating_controls_recommendation_descriptions": [ - "Upgrade heating controls to Smart Thermostats, room sensors and smart radiator valves (time & " - "temperature zone control)" - ], + "heating_measure_types": ["air_source_heat_pump"], + "heating_controls_measure_types": ["time_temperature_zone_control"], "notes": "This property has a boiler, radiators & mains gas with good efficiency so the only recommendation" "we expect here is for an air source heat pump. The heating controls are a programmer, room thermostat" "and TRVs and so we should expect a TTZC recommendation" @@ -93,11 +86,10 @@ testing_examples = [ 'fixed-lighting-outlets-count': None, 'low-energy-fixed-light-count': None, 'uprn': 10012342725.0, 'uprn-source': 'Address Matched', }, - "heating_recommendation_descriptions": [ - "Install high heat retention electric storage heaters. Upgrade heating controls to High Heat Retention " - "Storage Heater Controls" + "heating_measure_types": [ + "high_heat_retention_storage_heater", ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "This property has electric room heaters and is off gas so a boiler recommendation is not appropriate." "We would expect a high heat retention storage recommendation. The property is a flat and therefore" "we don't expect an air source heat pump recommendation. We also wouldn't expect a specific heating" @@ -144,16 +136,8 @@ testing_examples = [ 'tenure': 'owner-occupied', 'fixed-lighting-outlets-count': 6.0, 'low-energy-fixed-light-count': 4.0, 'uprn': 100090311351.0, 'uprn-source': 'Address Matched', 'property-type_y': None, 'built-form_y': None, }, - "heating_recommendation_descriptions": [ - 'Install high heat retention electric storage heaters. The current electric heaters may be retrofit with ' - 'high heat retention storage controls however this is dependent on the existing system and may not be ' - 'possible. Upgrade heating controls to High Heat Retention Storage Heater Controls', - 'Install an air source heat pump, and upgrade heating controls to Smart Thermostats, room sensors and ' - 'smart radiator valves (time & temperature zone control). The cost includes the £7500 boiler upgrade ' - 'scheme grant' - - ], - "heating_controls_recommendation_descriptions": [], + "heating_measure_types": ['high_heat_retention_storage_heater', 'air_source_heat_pump'], + "heating_controls_measure_types": [], "notes": "This test has electric storage heaters with automatic charge control - we recommend hhr storage" "heaters in this case, but because there are already electic storage heaters in place, we " "note, in the description of the recommendation, that this upgrade may be possible by retrofitting" @@ -197,13 +181,10 @@ testing_examples = [ 'tenure': 'owner-occupied', 'fixed-lighting-outlets-count': None, 'low-energy-fixed-light-count': None, 'uprn': 100021560521.0, 'uprn-source': 'Address Matched', }, - "heating_recommendation_descriptions": [ - 'Upgrade to a new condensing boiler.' - ], - "heating_controls_recommendation_descriptions": [ - 'Upgrade heating controls to Room thermostat, programmer and TRVs', - 'Upgrade heating controls to Smart Thermostats, room sensors and smart radiator valves (time & ' - 'temperature zone control)' + "heating_measure_types": ['boiler_upgrade'], + "heating_controls_measure_types": [ + 'roomstat_programmer_trvs', + 'time_temperature_zone_control', ], "notes": "Because of this property is a maisonette, which already has a boiler (but an inefficient one due to " "the current water heating efficiency) the only recommendation we expect is for " @@ -250,12 +231,10 @@ testing_examples = [ 'tenure': 'owner-occupied', 'fixed-lighting-outlets-count': 9.0, 'low-energy-fixed-light-count': 5.0, 'uprn': 100021936225.0, 'uprn-source': 'Address Matched', }, - "heating_recommendation_descriptions": [ - ], - "heating_controls_recommendation_descriptions": [ - 'Upgrade heating controls to Room thermostat, programmer and TRVs', - 'Upgrade heating controls to Smart Thermostats, room sensors and smart radiator valves (time & ' - 'temperature zone control)' + "heating_measure_types": [], + "heating_controls_measure_types": [ + 'roomstat_programmer_trvs', + 'time_temperature_zone_control', ], "notes": "Because this property already has a boiler, we don't recommend HHR. We don't recommend an ashp " "because the home is mid-terraced. Because the heating controls are " @@ -302,11 +281,10 @@ testing_examples = [ 'tenure': 'rental (private)', 'fixed-lighting-outlets-count': 7.0, 'low-energy-fixed-light-count': 6.0, 'uprn': 43088770.0, 'uprn-source': 'Address Matched', }, - "heating_recommendation_descriptions": [ - 'Install high heat retention electric storage heaters. Upgrade heating controls to High Heat Retention ' - 'Storage Heater Controls' + "heating_measure_types": [ + 'high_heat_retention_storage_heater', ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "This property is a flat so we don't have an ASHP recommendation. It also doesn't have access to the " "mains and so it can't have a gas boiler. We don't expect any controls recommendations" }, @@ -348,16 +326,10 @@ testing_examples = [ 'tenure': 'owner-occupied', 'fixed-lighting-outlets-count': None, 'low-energy-fixed-light-count': None, 'uprn': 100080513604.0, 'uprn-source': 'Address Matched' }, - "heating_recommendation_descriptions": [ - 'Install an air source heat pump, and upgrade heating controls to Smart Thermostats, room sensors and ' - 'smart radiator valves (time & temperature zone control). The cost includes the £7500 boiler upgrade ' - 'scheme grant' - ], - "heating_controls_recommendation_descriptions": [ - 'Upgrade heating controls to Room thermostat, programmer and TRVs', - 'Upgrade heating controls to Smart Thermostats, room sensors and smart radiator valves (time & ' - 'temperature zone control)' - + "heating_measure_types": ['air_source_heat_pump'], + "heating_controls_measure_types": [ + 'roomstat_programmer_trvs', + 'time_temperature_zone_control', ], "notes": "This has a very efficient boiler and is a detached bungalow, but only has " "Programmer and room thermostat for heating controls so we'd expect an ASHP heating recommendation" @@ -404,14 +376,12 @@ testing_examples = [ 'fixed-lighting-outlets-count': 10.0, 'low-energy-fixed-light-count': 9.0, 'uprn': 100070358594, 'uprn-source': 'Address Matched', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Upgrade to a new condensing boiler. Upgrade heating controls to Room thermostat, programmer and TRVs', - 'Install high heat retention electric storage heaters. Upgrade heating controls to High Heat Retention ' - 'Storage Heater Controls', - 'Upgrade to a new condensing boiler. Upgrade heating controls to Smart Thermostats, room sensors and smart ' - 'radiator valves (time & temperature zone control)' + "heating_measure_types": [ + 'boiler_upgrade', + 'high_heat_retention_storage_heater', + 'boiler_upgrade' ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "This property has assumed electric heating and is mid-terrace house. It has a mains gas connection." "We can recommend a boiler upgrade and high heat retention storage heaters" }, @@ -453,15 +423,11 @@ testing_examples = [ 'fixed-lighting-outlets-count': 42.0, 'low-energy-fixed-light-count': 13.0, 'uprn': 100070985545, 'uprn-source': 'Address Matched', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Install an air source heat pump, and upgrade heating controls to Smart Thermostats, room sensors and ' - 'smart radiator valves (time & temperature zone control). The cost includes the £7500 boiler upgrade ' - 'scheme grant', - 'Install high heat retention electric storage heaters. Upgrade heating controls to High Heat Retention ' - 'Storage Heater Controls' - + "heating_measure_types": [ + 'air_source_heat_pump', + 'high_heat_retention_storage_heater', ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "This property has an oil boiler and doesn't have a mains gas connection so we can only recommend" "an air source heat pump and HHR (since if the home has a non-gas boiler, we recommend HHR)" }, @@ -505,17 +471,13 @@ testing_examples = [ 'fixed-lighting-outlets-count': None, 'low-energy-fixed-light-count': None, 'uprn': 2465031849, 'uprn-source': 'Address Matched', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Upgrade to a new condensing boiler. Upgrade heating controls to Room thermostat, programmer and TRVs', - 'Install high heat retention electric storage heaters. Upgrade heating controls to High Heat Retention ' - 'Storage Heater Controls', - 'Install an air source heat pump, and upgrade heating controls to Smart Thermostats, room sensors and ' - 'smart radiator valves (time & temperature zone control). The cost includes the £7500 boiler upgrade ' - 'scheme grant', - 'Upgrade to a new condensing boiler. Upgrade heating controls to Smart Thermostats, room sensors and ' - 'smart radiator valves (time & temperature zone control)' + "heating_measure_types": [ + 'boiler_upgrade', + 'high_heat_retention_storage_heater', + 'air_source_heat_pump', + 'boiler_upgrade' # TTZs ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "This property has room heaters, from the mains gas supply. We recommend a boiler upgrade as" "well as an air source heat pump and HHR (since the home has a room heater set up)" }, @@ -558,14 +520,12 @@ testing_examples = [ 'uprn-source': 'Energy Assessor', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Upgrade to a new condensing boiler. Upgrade heating controls to Room thermostat, programmer and TRVs', - 'Upgrade to a new condensing boiler. Upgrade heating controls to Smart Thermostats, room sensors and smart ' - 'radiator valves (time & temperature zone control)', - 'Install high heat retention electric storage heaters. Upgrade heating controls to High Heat Retention ' - 'Storage Heater Controls', + "heating_measure_types": [ + 'boiler_upgrade', + 'boiler_upgrade', + 'high_heat_retention_storage_heater', ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "This property has assumed electric heaters. Boiler upgrade, HHR are recommended. We don't recommend" "an ASHP off of the bat because it's mid-terrace." }, @@ -607,14 +567,12 @@ testing_examples = [ 'fixed-lighting-outlets-count': 10.0, 'low-energy-fixed-light-count': None, 'uprn': 100071089116, 'uprn-source': 'Energy Assessor', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Upgrade to a new condensing boiler. Upgrade heating controls to Room thermostat, programmer and TRVs', - 'Install high heat retention electric storage heaters. Upgrade heating controls to High Heat Retention ' - 'Storage Heater Controls', - 'Upgrade to a new condensing boiler. Upgrade heating controls to Smart Thermostats, room sensors and ' - 'smart radiator valves (time & temperature zone control)' + "heating_measure_types": [ + 'boiler_upgrade', + 'high_heat_retention_storage_heater', + 'boiler_upgrade' ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "This has a form of assumed electric heating and has a mains connection so we recommend HHR, boiler" "upgrade and ASHP" }, @@ -657,15 +615,12 @@ testing_examples = [ 'fixed-lighting-outlets-count': None, 'low-energy-fixed-light-count': None, 'uprn': 100030352255, 'uprn-source': 'Address Matched', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Upgrade to a new condensing boiler. Upgrade heating controls to Room thermostat, programmer and TRVs', - 'Upgrade to a new condensing boiler. Upgrade heating controls to Smart Thermostats, room sensors and smart ' - 'radiator valves (time & temperature zone control)', - 'Install high heat retention electric storage heaters. The current electric heaters may be retrofit with ' - 'high heat retention storage controls however this is dependent on the existing system and may not be ' - 'possible. Upgrade heating controls to High Heat Retention Storage Heater Controls' + "heating_measure_types": [ + 'boiler_upgrade', + 'boiler_upgrade', + 'high_heat_retention_storage_heater', ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "This property already has storage heaters with manual charge control. The home is mid terrace so" "the ashp is not suitable" }, @@ -709,14 +664,11 @@ testing_examples = [ 'fixed-lighting-outlets-count': None, 'low-energy-fixed-light-count': None, 'uprn': 10009573249, 'uprn-source': 'Address Matched', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Install high heat retention electric storage heaters. Upgrade heating controls to High Heat Retention ' - 'Storage Heater Controls', - 'Install an air source heat pump, and upgrade heating controls to Smart Thermostats, room sensors and ' - 'smart radiator valves (time & temperature zone control). The cost includes the £7500 boiler upgrade ' - 'scheme grant' + "heating_measure_types": [ + 'high_heat_retention_storage_heater', + 'air_source_heat_pump', ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "This property has an LFG boiler but it doesn't have a mains gas connection so we can only recommend" "an air source heat pump and hhr storage" }, @@ -758,14 +710,11 @@ testing_examples = [ 'fixed-lighting-outlets-count': 8.0, 'low-energy-fixed-light-count': None, 'uprn': 10013181470, 'uprn-source': 'Energy Assessor', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Install high heat retention electric storage heaters. Upgrade heating controls to High Heat Retention ' - 'Storage Heater Controls', - 'Install an air source heat pump, and upgrade heating controls to Smart Thermostats, room sensors and ' - 'smart radiator valves (time & temperature zone control). The cost includes the £7500 boiler upgrade ' - 'scheme grant' + "heating_measure_types": [ + 'high_heat_retention_storage_heater', + 'air_source_heat_pump', ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "This property has electric boilers in place, but does not have a mains connection so we don't " "recommend a boiler upgrade. We recommend HHR and ASHP" }, @@ -809,14 +758,11 @@ testing_examples = [ 'fixed-lighting-outlets-count': 11.0, 'low-energy-fixed-light-count': 7.0, 'uprn': 452047507, 'uprn-source': 'Address Matched', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Install an air source heat pump, and upgrade heating controls to Smart Thermostats, room sensors and ' - 'smart radiator valves (time & temperature zone control). The cost includes the £7500 boiler upgrade ' - 'scheme grant', - 'Install high heat retention electric storage heaters. Upgrade heating controls to High Heat Retention ' - 'Storage Heater Controls' + "heating_measure_types": [ + 'air_source_heat_pump', + 'high_heat_retention_storage_heater' ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "This property has a dual fuel boiler and no mains gas connection. We recommend ASHP and HHR, but" "no gas condensing boiler" }, @@ -857,14 +803,11 @@ testing_examples = [ 'fixed-lighting-outlets-count': 16.0, 'low-energy-fixed-light-count': 4.0, 'uprn': 100030309413, 'uprn-source': 'Energy Assessor', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Install an air source heat pump, and upgrade heating controls to Smart Thermostats, room sensors and ' - 'smart radiator valves (time & temperature zone control). The cost includes the £7500 boiler upgrade ' - 'scheme grant', - 'Install high heat retention electric storage heaters. Upgrade heating controls to High Heat Retention ' - 'Storage Heater Controls' + "heating_measure_types": [ + 'air_source_heat_pump', + 'high_heat_retention_storage_heater', ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "This property has a coal boiler and no mains gas connection. We recommend ASHP and HHR, but" "no gas condensing boiler" }, @@ -908,15 +851,11 @@ testing_examples = [ 'fixed-lighting-outlets-count': None, 'low-energy-fixed-light-count': None, 'uprn': 10007366417, 'uprn-source': 'Address Matched', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Install an air source heat pump, and upgrade heating controls to Smart Thermostats, room sensors and ' - 'smart radiator valves (time & temperature zone control). The cost includes the £7500 boiler upgrade ' - 'scheme grant', - 'Install high heat retention electric storage heaters. Upgrade heating controls to High Heat Retention ' - 'Storage Heater Controls' - + "heating_measure_types": [ + 'air_source_heat_pump', + 'high_heat_retention_storage_heater', ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "This property has a smokeless fuel boiler and no mains gas connection. We recommend ASHP and HHR, but" "no gas condensing boiler" }, @@ -958,15 +897,11 @@ testing_examples = [ 'fixed-lighting-outlets-count': None, 'low-energy-fixed-light-count': None, 'uprn': 100030256931, 'uprn-source': 'Address Matched', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Install an air source heat pump, and upgrade heating controls to Smart Thermostats, room sensors and ' - 'smart radiator valves (time & temperature zone control). The cost includes the £7500 boiler upgrade ' - 'scheme grant', - 'Install high heat retention electric storage heaters. Upgrade heating controls to High Heat Retention ' - 'Storage Heater Controls' - + "heating_measure_types": [ + 'air_source_heat_pump', + 'high_heat_retention_storage_heater', ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "This property has a wood pellets boiler and no mains gas connection. We recommend ASHP and HHR, but" "no gas condensing boiler" }, @@ -1009,11 +944,10 @@ testing_examples = [ 'fixed-lighting-outlets-count': None, 'low-energy-fixed-light-count': None, 'uprn': 10000460605, 'uprn-source': 'Address Matched', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Install high heat retention electric storage heaters. Upgrade heating controls to High Heat Retention ' - 'Storage Heater Controls' + "heating_measure_types": [ + 'high_heat_retention_storage_heater', ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "This is an end-terrace house, without mains gas connection, so all we recommend is HHR" }, { @@ -1054,8 +988,8 @@ testing_examples = [ 'fixed-lighting-outlets-count': None, 'low-energy-fixed-light-count': None, 'uprn': 100031045596, 'uprn-source': 'Address Matched', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [], - "heating_controls_recommendation_descriptions": [], + "heating_measure_types": [], + "heating_controls_measure_types": [], "notes": "This property already has an ashp. We don't recommend any heating upgrades" }, { @@ -1095,17 +1029,12 @@ testing_examples = [ 'tenure': 'Rented (social)', 'fixed-lighting-outlets-count': 11.0, 'low-energy-fixed-light-count': None, 'uprn': 90041166, 'uprn-source': 'Energy Assessor', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Install an air source heat pump, and upgrade heating controls to Smart Thermostats, room sensors and ' - 'smart radiator valves (time & temperature zone control). The cost includes the £7500 boiler upgrade ' - 'scheme grant', - 'Install high heat retention electric storage heaters alongside the boiler. The current electric heaters ' - 'may be retrofit with high heat retention storage controls however this is dependent on the existing ' - 'system and may not be possible. Upgrade heating controls to High Heat Retention Storage Heater Controls' + "heating_measure_types": [ + 'air_source_heat_pump', + 'high_heat_retention_storage_heater' ], - "heating_controls_recommendation_descriptions": [ - 'Upgrade heating controls to Smart Thermostats, room sensors and smart radiator valves (time & ' - 'temperature zone control)' + "heating_controls_measure_types": [ + 'time_temperature_zone_control', ], "notes": "This property has dual heating. A boiler and electric storage heaters. The heating is efficient so" "we recommend ASHP and HHR. We also recommend upgrading the heating controls for the boiler" @@ -1147,20 +1076,14 @@ testing_examples = [ 'tenure': 'Rented (social)', 'fixed-lighting-outlets-count': 11.0, 'low-energy-fixed-light-count': None, 'uprn': 90041166, 'uprn-source': 'Energy Assessor', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Install an air source heat pump, and upgrade heating controls to Smart Thermostats, room sensors and ' - 'smart radiator valves (time & temperature zone control). The cost includes the £7500 boiler upgrade ' - 'scheme grant', - 'Upgrade the existing boiler to a new, more efficient condensing boiler. ', - 'Upgrade both the existing boiler to a new condensing boiler and upgrade storage heaters to high heat ' - 'retention storage heaters.', - 'Install high heat retention electric storage heaters alongside the boiler. The current electric heaters ' - 'may be retrofit with high heat retention storage controls however this is dependent on the existing ' - 'system and may not be possible. Upgrade heating controls to High Heat Retention Storage Heater Controls' + "heating_measure_types": [ + 'air_source_heat_pump', + 'boiler_upgrade', + 'boiler_upgrade+high_heat_retention_storage_heater', + 'high_heat_retention_storage_heater' ], - "heating_controls_recommendation_descriptions": [ - 'Upgrade heating controls to Smart Thermostats, room sensors and smart radiator valves (time & ' - 'temperature zone control)' + "heating_controls_measure_types": [ + 'time_temperature_zone_control' ], "notes": "This property is a modified version of the previous dual heating property, where we lower the" "starting heating efficiency so that we a combined heating upgrade to both the boiler and the electric" @@ -1204,14 +1127,11 @@ testing_examples = [ 'tenure': 'rental (social)', 'fixed-lighting-outlets-count': None, 'low-energy-fixed-light-count': None, 'uprn': 10009574286, 'uprn-source': 'Address Matched', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Install an air source heat pump, and upgrade heating controls to Smart Thermostats, room sensors and ' - 'smart radiator valves (time & temperature zone control). The cost includes the £7500 boiler upgrade ' - 'scheme grant', - 'Install high heat retention electric storage heaters. Upgrade heating controls to High Heat Retention ' - 'Storage Heater Controls' + "heating_measure_types": [ + 'air_source_heat_pump', + 'high_heat_retention_storage_heater' ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "This property has anthracite heating without mains. " "We recommend ASHP and HHR, but no gas condensing boiler" }, @@ -1254,14 +1174,12 @@ testing_examples = [ 'fixed-lighting-outlets-count': 8.0, 'low-energy-fixed-light-count': None, 'uprn': 100031556691, 'uprn-source': 'Energy Assessor', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Upgrade to a new condensing boiler. Upgrade heating controls to Room thermostat, programmer and TRVs', - 'Upgrade to a new condensing boiler. Upgrade heating controls to Smart Thermostats, room sensors and smart ' - 'radiator valves (time & temperature zone control)', - 'Install high heat retention electric storage heaters. Upgrade heating controls to High Heat Retention ' - 'Storage Heater Controls' + "heating_measure_types": [ + 'boiler_upgrade', + 'boiler_upgrade', + 'high_heat_retention_storage_heater' ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "This property has room heaters with two different fuel sources, so we recommend HHR, ASHP, and a " "boiler upgrade" }, @@ -1302,11 +1220,10 @@ testing_examples = [ 'uprn': 100070685908, 'uprn-source': 'Address Matched', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Install high heat retention electric storage heaters. Upgrade heating controls to High Heat Retention ' - 'Storage Heater Controls' + "heating_measure_types": [ + 'high_heat_retention_storage_heater' ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "This property is a flag, without mains gas connection. Currently has underfloor electric heating" "so we recommend HHR" }, @@ -1348,17 +1265,13 @@ testing_examples = [ 'uprn': 100071209105, 'uprn-source': 'Address Matched', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Install an air source heat pump, and upgrade heating controls to Smart Thermostats, room sensors and ' - 'smart radiator valves (time & temperature zone control). The cost includes the £7500 boiler upgrade ' - 'scheme grant', - 'Upgrade to a new condensing boiler. Upgrade heating controls to Room thermostat, programmer and TRVs', - 'Upgrade to a new condensing boiler. Upgrade heating controls to Smart Thermostats, room sensors and smart ' - 'radiator valves (time & temperature zone control)', - 'Install high heat retention electric storage heaters. Upgrade heating controls to High Heat Retention ' - 'Storage Heater Controls' + "heating_measure_types": [ + 'air_source_heat_pump', + 'boiler_upgrade', + 'boiler_upgrade', + 'high_heat_retention_storage_heater' ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "The property has warm air electricaire heating, so we recommend ASHP and HHR. It also has a mains" "connection so we recommend a gas condensing boiler" }, @@ -1402,12 +1315,11 @@ testing_examples = [ 'uprn': 100070955137, 'uprn-source': 'Address Matched', 'sheating-energy-eff': None, 'sheating-env-eff': None }, - "heating_recommendation_descriptions": [ - 'Upgrade to a new condensing boiler. Upgrade heating controls to Room thermostat, programmer and TRVs', - 'Upgrade to a new condensing boiler. Upgrade heating controls to Smart Thermostats, room sensors and smart ' - 'radiator valves (time & temperature zone control)' + "heating_measure_types": [ + 'boiler_upgrade', + 'boiler_upgrade', ], - "heating_controls_recommendation_descriptions": [], + "heating_controls_measure_types": [], "notes": "This property has warm air mains gas heating, so we recommend a gas condensing boiler" } ] diff --git a/recommendations/tests/test_heating_recommendations.py b/recommendations/tests/test_heating_recommendations.py index 35373729..ed2e037d 100644 --- a/recommendations/tests/test_heating_recommendations.py +++ b/recommendations/tests/test_heating_recommendations.py @@ -96,19 +96,19 @@ class TestHeatingRecommendations: recommender.recommend(has_cavity_or_loft_recommendations=False) - assert len(recommender.heating_recommendations) == len(test_case["heating_recommendation_descriptions"]) + assert len(recommender.heating_recommendations) == len(test_case["heating_measure_types"]) assert ( len(recommender.heating_control_recommendations) == - len(test_case["heating_controls_recommendation_descriptions"]) + len(test_case["heating_controls_measure_types"]) ) - # Check the exact descriptions + # Check the exact measure types assert ( - {x["description"] for x in recommender.heating_recommendations} == - set(test_case["heating_recommendation_descriptions"]) + {x["measure_type"] for x in recommender.heating_recommendations} == + set(test_case["heating_measure_types"]) ) assert ( - {x["description"] for x in recommender.heating_control_recommendations} == - set(test_case["heating_controls_recommendation_descriptions"]) + {x["measure_type"] for x in recommender.heating_control_recommendations} == + set(test_case["heating_controls_measure_types"]) ) diff --git a/recommendations/tests/test_recommendation_utils.py b/recommendations/tests/test_recommendation_utils.py index 38322c41..b445a798 100644 --- a/recommendations/tests/test_recommendation_utils.py +++ b/recommendations/tests/test_recommendation_utils.py @@ -231,6 +231,15 @@ class TestRecommendationUtils: expected_uvalue = test_case["uvalue"] inputs = test_case.copy() del inputs["uvalue"] + # insulation_thickness = inputs["insulation_thickness"] + # has_dwelling_above = inputs["has_dwelling_above"] + # is_loft = inputs["is_loft"] + # is_roof_room = inputs["is_roof_room"] + # is_thatched = inputs["is_thatched"] + # age_band = inputs["age_band"] + # is_flat = inputs["is_flat"] + # is_pitched = inputs["is_pitched"] + # is_at_rafters = inputs["is_at_rafters"] uvalue = recommendation_utils.get_roof_u_value(**inputs) assert expected_uvalue == uvalue, f"Expected u value {expected_uvalue}, recieved {uvalue}" diff --git a/recommendations/tests/test_roof_recommendations.py b/recommendations/tests/test_roof_recommendations.py index 139975bd..7df861f7 100644 --- a/recommendations/tests/test_roof_recommendations.py +++ b/recommendations/tests/test_roof_recommendations.py @@ -35,7 +35,7 @@ class TestRoofRecommendations: def test_loft_insulation_recommendation_50mm_insulation(self): epc_record = EPCRecord() - epc_record.prepared_epc = {"county": "Kent"} + epc_record.prepared_epc = {"county": "Kent", "roof-energy-eff": "Very Poor"} property_instance2 = Property(id=0, address="fake", postcode="fake", epc_record=epc_record) property_instance2.age_band = "F" property_instance2.insulation_floor_area = 100 @@ -63,7 +63,7 @@ class TestRoofRecommendations: assert roof_recommender2.recommendations[0]["parts"][0]["depth"] == 270 epc_record = EPCRecord() - epc_record.prepared_epc = {"county": "Greater London Authority"} + epc_record.prepared_epc = {"county": "Greater London Authority", "roof-energy-eff": "Very Poor"} property_instance3 = Property(id=0, address="fake", postcode="fake", epc_record=epc_record) property_instance3.age_band = "F" property_instance3.insulation_floor_area = 100 @@ -89,7 +89,7 @@ class TestRoofRecommendations: def test_loft_insulation_recommendation_150mm_insulation(self): epc_record = EPCRecord() - epc_record.prepared_epc = {"county": "North East Lincolnshire"} + epc_record.prepared_epc = {"county": "North East Lincolnshire", "roof-energy-eff": "Good"} property_instance4 = Property(id=0, address="fake", postcode="fake", epc_record=epc_record) property_instance4.age_band = "F" property_instance4.insulation_floor_area = 100 @@ -117,7 +117,7 @@ class TestRoofRecommendations: assert roof_recommender4.recommendations[0]["parts"][0]["depth"] == 200 epc_record = EPCRecord() - epc_record.prepared_epc = {"county": "Somerset"} + epc_record.prepared_epc = {"county": "Somerset", "roof-energy-eff": "Good"} property_instance5 = Property(id=0, address="fake", postcode="fake", epc_record=epc_record) property_instance5.age_band = "F" property_instance5.insulation_floor_area = 100 @@ -189,8 +189,8 @@ class TestRoofRecommendations: roof_recommender7.recommend(phase=0) assert len(roof_recommender7.recommendations) == 1 - assert roof_recommender7.recommendations[0]["new_u_value"] == 0.23 - assert roof_recommender7.recommendations[0]["starting_u_value"] == 1.5 + assert roof_recommender7.recommendations[0]["new_u_value"] == 0.24 + assert roof_recommender7.recommendations[0]["starting_u_value"] == 2.3 assert roof_recommender7.recommendations[0]["description"] == "Insulate room in roof at rafters and re-decorate" def test_ceiling_insulated_room_in_roof(self):