Updated wall recommended partially for new costs

This commit is contained in:
Khalim Conn-Kowlessar 2023-11-24 08:15:32 +00:00
parent 80b4d2390c
commit cb52c9f7a3

View file

@ -1,6 +1,8 @@
import math import math
from typing import List from typing import List
import pandas as pd
from datatypes.enums import QuantityUnits from datatypes.enums import QuantityUnits
from backend.Property import Property from backend.Property import Property
from BaseUtility import Definitions from BaseUtility import Definitions
@ -9,6 +11,7 @@ from recommendations.recommendation_utils import (
get_recommended_part, get_wall_u_value get_recommended_part, get_wall_u_value
) )
from recommendations.config import PARTIALLY_FILLED_PERCENTAGE_ASSUMPTION from recommendations.config import PARTIALLY_FILLED_PERCENTAGE_ASSUMPTION
from recommendations.Costs import Costs
from utils.logger import setup_logger from utils.logger import setup_logger
logger = setup_logger() logger = setup_logger()
@ -50,13 +53,36 @@ class WallRecommendations(Definitions):
materials: List materials: List
): ):
self.property = property_instance self.property = property_instance
self.costs = Costs(self.property)
# For audit purposes, when estimating u values we'll store it # For audit purposes, when estimating u values we'll store it
self.estimated_u_value = None self.estimated_u_value = None
# Will contains a list of recommended measures # Will contains a list of recommended measures
self.recommendations = [] self.recommendations = []
self.materials = materials self.cavity_wall_insulation_materials = [
part for part in materials if part["type"] == "cavity_wall_insulation"
]
self.internal_wall_insulation_materials = [
part for part in materials if part["type"] == "internal_wall_insulation"
]
self.internal_wall_non_insulation_materials = [
part for part in materials if part["type"] in [
"iwi_wall_demolition", "iwi_vapour_barrier", "iwi_redecoration"
]
]
self.external_wall_insulation_materials = [
part for part in materials if part["type"] == "external_wall_insulation"
]
self.external_wall_non_insulation_materials = [
part for part in materials if part["type"] in [
"ewi_wall_demolition", "ewi_wall_preparation", "ewi_wall_redecoration"
]
]
@property @property
def ewi_valid(self): def ewi_valid(self):
@ -200,15 +226,15 @@ class WallRecommendations(Definitions):
self.recommendations = recommendations self.recommendations = recommendations
def _find_insulation(self, parts, u_value): def _find_insulation(self, u_value, insulation_materials, non_insulation_materials):
lowest_selected_u_value = None lowest_selected_u_value = None
recommendations = [] recommendations = []
for part in parts: for _, insulation_material_group in insulation_materials.groupby("description"):
for depth, cost_per_unit in zip(part["depths"], part["cost"]): for _, material in insulation_material_group.iterrows():
part_u_value = r_value_per_mm_to_u_value(depth, part["r_value_per_mm"])
part_u_value = r_value_per_mm_to_u_value(material["depth"], material["r_value_per_mm"])
_, new_u_value = calculate_u_value_uplift(u_value, part_u_value) _, new_u_value = calculate_u_value_uplift(u_value, part_u_value)
new_u_value = math.ceil(new_u_value * 100.0) / 100.0 new_u_value = math.ceil(new_u_value * 100.0) / 100.0
@ -225,27 +251,40 @@ class WallRecommendations(Definitions):
# We allow a small tolerance for error so we don't discount the recommendation entirely # 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: 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) lowest_selected_u_value = update_lowest_selected_u_value(lowest_selected_u_value, new_u_value)
estimated_cost = cost_per_unit * self.property.insulation_wall_area if material["type"] == "internal_wall_insulation":
cost_result = self.costs.internal_wall_insulation(
wall_area=self.property.insulation_wall_area,
material=material.to_dict(),
non_insulation_materials=non_insulation_materials
)
elif material["type"] == "external_wall_insulation":
cost_result = self.costs.external_wall_insulation(
wall_area=self.property.insulation_wall_area,
material=material.to_dict(),
non_insulation_materials=non_insulation_materials
)
else:
raise ValueError("Invalid material type")
recommendations.append( recommendations.append(
{ {
"parts": [ "parts": [
get_recommended_part( get_recommended_part(
part=part, part=material.to_dict(),
selected_depth=depth,
quantity=self.property.insulation_wall_area, quantity=self.property.insulation_wall_area,
quantity_unit=QuantityUnits.m2.value, quantity_unit=QuantityUnits.m2.value,
selected_total_cost=estimated_cost cost_result=cost_result
) )
], ],
"type": "wall_insulation", "type": "wall_insulation",
"description": "Install " + self._make_description(part, depth), "description": "Install " + self._make_description(material),
"starting_u_value": u_value, "starting_u_value": u_value,
"new_u_value": new_u_value, "new_u_value": new_u_value,
"sap_points": None, "sap_points": None,
"cost": estimated_cost, **cost_result
} }
) )
@ -258,27 +297,32 @@ class WallRecommendations(Definitions):
:return: :return:
""" """
ewi_parts = [
part for part in self.materials if part["type"] == "external_wall_insulation"
] if self.ewi_valid else []
iwi_parts = [part for part in self.materials if part["type"] == "internal_wall_insulation"]
# Recommend external and internal wall insulation separately # Recommend external and internal wall insulation separately
# Since external and internal wall insulation are sufficiently different, # Since external and internal wall insulation are sufficiently different,
# we separate the logic for for recommending them, therefore we don't # we separate the logic for for recommending them, therefore we don't
# consider diminishing returns between the two # consider diminishing returns between the two
ewi_recommendations = self._find_insulation(ewi_parts, u_value) ewi_recommendations = []
iwi_recommendations = self._find_insulation(iwi_parts, u_value) if self.ewi_valid:
ewi_recommendations = self._find_insulation(
u_value=u_value,
insulation_materials=pd.DataFrame(self.external_wall_insulation_materials),
non_insulation_materials=self.external_wall_non_insulation_materials
)
iwi_recommendations = self._find_insulation(
u_value=u_value,
insulation_materials=pd.DataFrame(self.internal_wall_insulation_materials),
non_insulation_materials=self.internal_wall_non_insulation_materials
)
self.recommendations += ewi_recommendations + iwi_recommendations self.recommendations += ewi_recommendations + iwi_recommendations
self.prune_diminishing_recommendations() self.prune_diminishing_recommendations()
@staticmethod @staticmethod
def _make_description(part, depth): def _make_description(material):
return f"{depth}{part['depth_unit']} {part['description']}" return f"{int(material['depth'])}{material['depth_unit']} {material['description']}"
def prune_diminishing_recommendations(self): def prune_diminishing_recommendations(self):
# For any recommendations, if we have at least 1 reommendation that does not exhibit diminishing returns # For any recommendations, if we have at least 1 reommendation that does not exhibit diminishing returns