From 87c36a80d379a498f8da95233fcf4e6a637152b4 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 5 Aug 2024 20:50:43 +0100 Subject: [PATCH] tweaking heating rec logic --- backend/Property.py | 4 + recommendations/HeatingRecommender.py | 4 +- recommendations/Recommendations.py | 4 +- recommendations/RoofRecommendations.py | 101 ++++++++++++++++--------- 4 files changed, 74 insertions(+), 39 deletions(-) diff --git a/backend/Property.py b/backend/Property.py index ba22ce60..b8563b87 100644 --- a/backend/Property.py +++ b/backend/Property.py @@ -438,6 +438,10 @@ class Property: # Replace the understores with hyphens simulation_epc = {k.replace("_", "-"): v for k, v in simulation_epc.items()} + # Add in today's costs (unadjusted + simulation_epc["heating-cost-current"] = int(self.energy_cost_estimates["unadjusted"]["heating"]) + simulation_epc["hot-water-cost-current"] = int(self.energy_cost_estimates["unadjusted"]["hot_water"]) + simulation_epc["lighting-cost-current"] = int(self.energy_cost_estimates["unadjusted"]["lighting"]) simulation_epc.update(phase_epc_transformation) self.simulation_epcs[phase] = simulation_epc diff --git a/recommendations/HeatingRecommender.py b/recommendations/HeatingRecommender.py index 1a3b6159..d8e597e7 100644 --- a/recommendations/HeatingRecommender.py +++ b/recommendations/HeatingRecommender.py @@ -62,8 +62,6 @@ class HeatingRecommender: {"suitable": True} ) # We allow for the non-invasive recommendation to be that ASHP is not suitable - if not non_invasive_ashp_recommendation["suitable"]: - return # This option will prevent other heating recommendations from being specified, other than an ASHP ashp_only_heating_recommendation = non_invasive_ashp_recommendation.get( @@ -127,7 +125,7 @@ class HeatingRecommender: # In the future, we'll allow overrides, so that non-intrusive surveys can contradict these conditions # and either allow or prevent the recommendation of an air source heat pump - if self.property.is_ashp_valid(exclusions=exclusions): + if self.property.is_ashp_valid(exclusions=exclusions) and non_invasive_ashp_recommendation["suitable"]: self.recommend_air_source_heat_pump( phase=phase, has_cavity_or_loft_recommendations=has_cavity_or_loft_recommendations, diff --git a/recommendations/Recommendations.py b/recommendations/Recommendations.py index 23b0e7df..1c12d5eb 100644 --- a/recommendations/Recommendations.py +++ b/recommendations/Recommendations.py @@ -488,7 +488,7 @@ class Recommendations: float(property_instance.data["energy-consumption-current"]) - new_heat_demand ) - if rec["type"] == "lighting": + if rec["type"] == "low_energy_lighting": new_heating_cost = property_instance.energy_cost_estimates["adjusted"]["heating"] new_hot_water_cost = property_instance.energy_cost_estimates["adjusted"]["hot_water"] new_lighting_cost = min( @@ -556,10 +556,12 @@ class Recommendations: new_heating_kwh = energy_consumption_client.score_new_data( new_data=scoring_df, target="heating_kwh" )[0] + new_heating_kwh = 0 if new_heating_kwh < 0 else new_heating_kwh new_hot_water_kwh = energy_consumption_client.score_new_data( new_data=scoring_df, target="hot_water_kwh" )[0] + new_hot_water_kwh = 0 if new_hot_water_kwh < 0 else new_hot_water_kwh # Adjust these figures new_heating_kwh_adjusted = AnnualBillSavings.adjust_energy_to_metered( diff --git a/recommendations/RoofRecommendations.py b/recommendations/RoofRecommendations.py index fa2cb53c..5075928e 100644 --- a/recommendations/RoofRecommendations.py +++ b/recommendations/RoofRecommendations.py @@ -405,18 +405,23 @@ class RoofRecommendations: :return: """ - roof_roof_insulation_materials = [m for m in self.materials if m["type"] == "room_roof_insulation"] - if not roof_roof_insulation_materials: - raise ValueError("No room in roof insulation materials found") + # TODO: We temporarilty use costs from SCIS for RIR insulation. The costing was £180/m2 floor + roof_roof_insulation_materials = [ + { + "type": "room_roof_insulation", + "description": "Insulating the ceiling of the roof roof and re-decorate", + "depths": [100], + "depth_unit": "mm", + "r_value_per_mm": 0.038, + "thermal_conductivity": 0.022, + "cost": [180], + } + ] - if self.property.pitched_roof_area is None: - raise ValueError("pitched_roof_area not included as property attribute") - - lowest_selected_u_value = None + # lowest_selected_u_value = None recommendations = [] for material in roof_roof_insulation_materials: for depth, cost_per_unit in zip(material["depths"], material["cost"]): - part_u_value = r_value_per_mm_to_u_value(depth, material["r_value_per_mm"]) _, new_u_value = calculate_u_value_uplift(u_value, part_u_value) @@ -428,36 +433,62 @@ class RoofRecommendations: # If I have a lowest U value and my new u value is lower than the lowest value, it's # further into the diminishing returns threshold and can shouldn't be - if is_diminishing_returns( - recommendations, new_u_value, lowest_selected_u_value, self.DIMINISHING_RETURNS_U_VALUE - ): - continue + # if is_diminishing_returns( + # recommendations, new_u_value, lowest_selected_u_value, self.DIMINISHING_RETURNS_U_VALUE + # ): + # continue # We allow a small tolerance for error so we don't discount the recommendation entirely - if new_u_value <= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE: - lowest_selected_u_value = update_lowest_selected_u_value(lowest_selected_u_value, new_u_value) + # if new_u_value <= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE: + # lowest_selected_u_value = update_lowest_selected_u_value(lowest_selected_u_value, new_u_value) - estimated_cost = cost_per_unit * self.property.pitched_roof_area + estimated_cost = cost_per_unit * self.property.insulation_floor_area - recommendations.append( - { - "phase": phase, - "parts": [ - get_recommended_part( - part=material, - selected_depth=depth, - quantity=self.property.pitched_roof_area, - quantity_unit=QuantityUnits.m2.value, - selected_total_cost=estimated_cost - ) - ], - "type": "room_roof_insulation", - "description": self.make_room_roof_insulation_description(material, depth), - "starting_u_value": u_value, - "new_u_value": new_u_value, - "sap_points": None, - "cost": estimated_cost, - } - ) + # Could also be Roof room(s), ceiling insulated + new_descriptin = "Pitched, insulated at rafters" + 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_" + ) + if self.property.data["roof-energy-eff"] in ["Very Poor", "Poor"]: + new_efficiency = "Average" + else: + new_efficiency = self.property.data["roof-energy-eff"] + + simulation_config = { + **roof_simulation_config, + "roof_thermal_transmittance_ending": new_u_value, + "roof_energy_eff_ending": new_efficiency + } + + already_installed = "flat_roof_insulation" in self.property.already_installed + cost_result = { + "total": estimated_cost, + "labour_hours": 80, + "labour_days": 5, + } + if already_installed: + cost_result = override_costs(cost_result) + + recommendations.append( + { + "phase": phase, + "parts": [ + # TODO + ], + "type": "room_roof_insulation", + "description": "Insulate room in roof at rafters and re-decorate", + "starting_u_value": u_value, + "new_u_value": None, + "sap_points": None, + "simulation_config": simulation_config, + "description_simulation": { + "roof-description": new_descriptin, + "roof-energy-eff": new_efficiency + }, + **cost_result, + "already_installed": already_installed + } + ) self.recommendations = recommendations