diff --git a/backend/app/plan/router.py b/backend/app/plan/router.py index a4d9e274..f06ca2ed 100644 --- a/backend/app/plan/router.py +++ b/backend/app/plan/router.py @@ -1,3 +1,4 @@ +from collections import defaultdict from fastapi import APIRouter, Depends from backend.app.db.models.portfolio import rating_lookup from backend.app.dependencies import validate_token @@ -20,6 +21,7 @@ from backend.app.db.functions.property_functions import ( from backend.app.plan.uvalue_estimates_walls import uvalue_estimates_walls from backend.app.plan.uvalue_estimates_floors import uvalue_estimates_floors from backend.app.plan.temp_cleaned_data import cleaned +from backend.app.plan.temp_materials_db import materials logger = setup_logger() @@ -79,6 +81,19 @@ lighting_averages = [ ] +def get_materials(materials): + materials_by_type = defaultdict(list) + + for material in materials: + material_type = material["type"] + materials_by_type[material_type].append(material) + + # Optionally, you can convert the defaultdict to a normal dict if desired + materials_by_type = dict(materials_by_type) + + return materials_by_type + + @router.post("/trigger") async def trigger_plan(body: PlanTriggerRequest): logger.info("Getting the inputs") @@ -137,6 +152,11 @@ async def trigger_plan(body: PlanTriggerRequest): ) p.set_is_in_conservation_area(in_conservation_area) + # The materials data could be cached or local so we don't need to make + # consistent requrests to the backend for + # the same data + materials_by_type = get_materials(materials) + logger.info("Getting components and properties recommendations") recommendations = [] for property_id, p in enumerate(input_properties): @@ -195,7 +215,8 @@ async def trigger_plan(body: PlanTriggerRequest): wall_recomendations = WallRecommendations( property_instance=p, uvalue_estimates=walls_u_value_estimate, - total_floor_area_group_decile=total_floor_area_group_decile + total_floor_area_group_decile=total_floor_area_group_decile, + materials=materials_by_type["external_wall_insulation"] + materials_by_type["internal_wall_insulation"] ) wall_recomendations.recommend() # insert property id diff --git a/backend/app/plan/temp_materials_db.py b/backend/app/plan/temp_materials_db.py new file mode 100644 index 00000000..5305c674 --- /dev/null +++ b/backend/app/plan/temp_materials_db.py @@ -0,0 +1,242 @@ +suspended_floor_insulation_parts = [ + { + # Example product + # All product types here: + # https://www.insulationsuperstore.co.uk/browse/insulation/brand/recticel/filterby/application/floors.html + "id": 1, + "type": "suspended_floor_insulation", + "description": "Rigid Insulation Foam Boards", + "depths": [25, 30, 40, 50, 60, 70, 75, 80, 90, 100, 110, 120, 130, 140, 150], + "depth_unit": "mm", + "cost": None, + "cost_unit": None, + "r_value_per_mm": 0.04545454545454546, + "r_value_unit": "square_meter_kelvin_per_watt", + "thermal_conductivity": 0.022, + "thermal_conductivity_unit": "watt_per_meter_kelvin", + "link": "https://www.insulationsuperstore.co.uk/product/recticel-eurothane-general-purpose-pir-insulation" + "-board-2400-x-1200-x-100mm.html" + }, + { + # All product types here: + # https://www.insulationsuperstore.co.uk/browse/insulation/brand/rockwool/filterby/application/floors + # /material/mineral-wool.html + "id": 2, + "type": "suspended_floor_insulation", + "description": "Mineral Wool Floor Insulation", + "depths": [25, 40, 50, 60, 75, 100], + "depth_unit": "mm", + "cost": None, + "cost_unit": None, + "r_value_per_mm": 0.02857142857142857, + "r_value_unit": "square_meter_kelvin_per_watt", + "thermal_conductivity": 0.035, + "thermal_conductivity_unit": "watt_per_meter_kelvin", + "link": "https://www.insulationsuperstore.co.uk/product/rockwool-rwa45-acoustic-insulation-slab-100mm-2-88m2" + "-pack.html" + }, +] + +solid_floor_insulation_parts = [ + { + # All product types here: + # https://www.insulationexpress.co.uk/floor-insulation/solid-floor-insulation?brand=7015&p=1 + # Example screed https://www.screwfix.com/p/mapei-ultraplan-3240-self-levelling-compound-25kg/4959f + "id": 3, + "type": "solid_floor_insulation", + "description": "Rigid Insulation Foam Boards with floor screed", + "depths": [25, 50, 70, 75, 100], + "depth_unit": "mm", + "cost": None, + "cost_unit": None, + "r_value_per_mm": 0.04545454545454546, + "r_value_unit": "square_meter_kelvin_per_watt", + "thermal_conductivity": 0.052631578947368425, + "thermal_conductivity_unit": "watt_per_meter_kelvin", + "link": "https://www.insulationexpress.co.uk/floor-insulation/solid-floor-insulation/k103-100mm" + }, + +] + +external_wall_insulation_parts = [ + { + "id": 4, + "type": "external_wall_insulation", + "description": "Mineral Wool External Wall Insulation", + "depths": [30, 50, 70, 80, 90, 100, 150, 200], + "depth_unit": "mm", + "cost": None, + "cost_unit": None, + "r_value_per_mm": 0.0278, + "r_value_unit": "square_meter_kelvin_per_watt", + "thermal_conductivity": 0.036, + "thermal_conductivity_unit": "watt_per_meter_kelvin", + "link": "https://insulationgo.co.uk/100mm-rockwool-external-wall-insulation-dual-density-slabs-a1-non" + "-combustible-slab-ewi-render-fire/" + }, + { + "id": 5, + "type": "external_wall_insulation", + "description": "Expanded Polystyrene External Wall Insulation", + "depths": [25, 50, 100, 125], + "depth_unit": "mm", + "cost": None, + "cost_unit": None, + "r_value_per_mm": 0.02703, + "r_value_unit": "square_meter_kelvin_per_watt", + "thermal_conductivity": 0.037, + "thermal_conductivity_unit": "watt_per_meter_kelvin", + "link": "https://www.insulationking.co.uk/products/polystyrene-eps70?variant=44156186558759" + }, + { + "id": 6, + "type": "external_wall_insulation", + "description": "Phenolic Foam External Wall Insulation", + "depths": [20, 50, 100], + "depth_unit": "mm", + "cost": None, + "cost_unit": None, + "r_value_per_mm": 0.043478260869565216, + "r_value_unit": "square_meter_kelvin_per_watt", + "thermal_conductivity": 0.023, + "thermal_conductivity_unit": "watt_per_meter_kelvin", + "link": "https://www.insulationshop.co/20mm_kooltherm_k5_external_wall_kingspan.html" + }, + { + "id": 7, + "type": "external_wall_insulation", + "description": "Polyisocyanurate/Polyurethane Foam External Wall Insulation", + "depths": [], + "depth_unit": "mm", + "cost": None, + "cost_unit": None, + "r_value_per_mm": None, + "r_value_unit": "square_meter_kelvin_per_watt", + "thermal_conductivity": None, + "thermal_conductivity_unit": "watt_per_meter_kelvin", + "link": None + }, + { + "id": 8, + "type": "external_wall_insulation", + "description": "Wood Fiber External Wall Insulation", + "depths": [40, 60], + "depth_unit": "mm", + "cost": None, + "cost_unit": None, + "r_value_per_mm": 0.023255813953488375, + "r_value_unit": "square_meter_kelvin_per_watt", + "thermal_conductivity": 0.043, + "thermal_conductivity_unit": "watt_per_meter_kelvin", + "link": "https://www.mikewye.co.uk/product/steico-duo-dry/" + }, + { + "id": 9, + "type": "external_wall_insulation", + "description": "Aerogel External Wall Insulation", + "depths": [10, 20, 30, 40, 50, 60, 70], + "depth_unit": "mm", + "cost": None, + "cost_unit": None, + "r_value_per_mm": 0.06666666666666667, + "r_value_unit": "square_meter_kelvin_per_watt", + "thermal_conductivity": 0.015, + "thermal_conductivity_unit": "watt_per_meter_kelvin", + "link": "https://www.thermablok.co.uk/site/wp-content/uploads/2022/09/Thermablok-Aerogel-Insulation-Blanket" + "-TDS-AIS-and-Steel-Related-Details.pdf" + }, + { + "id": 10, + "type": "external_wall_insulation", + "description": "Vacuum Insulation Panels External Wall Insulation", + "depths": [45, 60], + "depth_unit": "mm", + "cost": None, + "cost_unit": None, + "r_value_per_mm": 0.16666666666666666, + "r_value_unit": "square_meter_kelvin_per_watt", + "thermal_conductivity": 0.006, + "thermal_conductivity_unit": "watt_per_meter_kelvin", + "link": None + } +] + +internal_wall_insulation_parts = [ + { + "id": 11, + "type": "internal_wall_insulation", + "description": "Rigid Insulation Boards Internal Wall Insulation", + "depths": [25, 40, 50, 75, 100], + "depth_unit": "mm", + "cost": None, + "cost_unit": None, + "r_value_per_mm": 0.026315789473684213, + "r_value_unit": "square_meter_kelvin_per_watt", + "thermal_conductivity": 0.038, + "thermal_conductivity_unit": "watt_per_meter_kelvin", + "link": "https://www.insulationshop.co/25mm_polystyrene_insulation_eps_70jablite.html" + }, + { + "id": 12, + "type": "internal_wall_insulation", + "description": "Mineral Wool Internal Wall Insulation", + "depths": [140], + "depth_unit": "mm", + "cost": None, + "cost_unit": None, + "r_value_per_mm": 0.02857142857142857, + "r_value_unit": "square_meter_kelvin_per_watt", + "thermal_conductivity": 0.035, + "thermal_conductivity_unit": "watt_per_meter_kelvin", + "link": "https://www.rockwool.com/siteassets/rw-uk/downloads/datasheets/flexi.pdf" + }, + { + "id": 13, + "type": "internal_wall_insulation", + "description": "Insulated Plasterboard Internal Wall Insulation", + "depths": [25, 80], + "depth_unit": "mm", + "cost": None, + "cost_unit": None, + "r_value_per_mm": 0.02857142857142857, + "r_value_unit": "square_meter_kelvin_per_watt", + "thermal_conductivity": 0.019, + "thermal_conductivity_unit": "watt_per_meter_kelvin", + "link": "https://www.kingspan.com/gb/en/products/insulation-boards/wall-insulation-boards/kooltherm-k118" + "-insulated-plasterboard/" + }, + { + "id": 14, + "type": "internal_wall_insulation", + "description": "Reflective Internal Wall Insulation", + "depths": [], + "depth_unit": "mm", + "cost": None, + "cost_unit": None, + "r_value_per_mm": None, + "r_value_unit": "square_meter_kelvin_per_watt", + "thermal_conductivity": None, + "thermal_conductivity_unit": "watt_per_meter_kelvin", + "link": None + }, + { + "id": 15, + "type": "internal_wall_insulation", + "description": "Vacuum Insulation Panels Wall Insulation", + "depths": [20, 30], + "depth_unit": "mm", + "cost": None, + "cost_unit": None, + "r_value_per_mm": 0.125, + "r_value_unit": "square_meter_kelvin_per_watt", + "thermal_conductivity": 0.008, + "thermal_conductivity_unit": "watt_per_meter_kelvin", + "link": "https://www.insulationsuperstore.co.uk/product/vacutherm-vacupor-nt-b2-vacuum-insulated-panel-1m-x" + "-600mm-x-30mm.html" + }, +] + +materials = ( + suspended_floor_insulation_parts + solid_floor_insulation_parts + external_wall_insulation_parts + \ + internal_wall_insulation_parts +) diff --git a/recommendations/FloorRecommendations.py b/recommendations/FloorRecommendations.py index b934635c..caab80c8 100644 --- a/recommendations/FloorRecommendations.py +++ b/recommendations/FloorRecommendations.py @@ -95,7 +95,13 @@ class FloorRecommendations(BaseUtility): "3rd": 3 } - def __init__(self, property_instance: Property, uvalue_estimates: List, total_floor_area_group_decile: str): + def __init__( + self, + property_instance: Property, + uvalue_estimates: List, + total_floor_area_group_decile: str, + materials: List = None, + ): self.property = property_instance self.uvalue_estimates = uvalue_estimates self.total_floor_area_group_decile = total_floor_area_group_decile @@ -105,6 +111,11 @@ class FloorRecommendations(BaseUtility): # Will contains a list of recommended measures self.recommendations = [] + if materials: + self.materials = materials + else: + self.materials = parts + @staticmethod def _estimate_perimeter(floor_area, num_rooms): # Compute average room size based on total floor area and number of rooms diff --git a/recommendations/WallRecommendations.py b/recommendations/WallRecommendations.py index 35a5b022..99ad46d2 100644 --- a/recommendations/WallRecommendations.py +++ b/recommendations/WallRecommendations.py @@ -216,7 +216,7 @@ class WallRecommendations(BaseUtility): "solid_brick": 2, } - def __init__(self, property_instance: Property, uvalue_estimates, total_floor_area_group_decile): + def __init__(self, property_instance: Property, uvalue_estimates, total_floor_area_group_decile, materials=None): self.property = property_instance self.uvalue_estimates = uvalue_estimates self.total_floor_area_group_decile = total_floor_area_group_decile @@ -226,6 +226,11 @@ class WallRecommendations(BaseUtility): # Will contains a list of recommended measures self.recommendations = [] + if materials: + self.materials = materials + else: + self.materials = wall_parts + @property def ewi_valid(self): """ @@ -343,10 +348,10 @@ class WallRecommendations(BaseUtility): """ ewi_parts = [ - part for part in wall_parts if part["type"] == "external_wall_insulation" + part for part in self.materials if part["type"] == "external_wall_insulation" ] if self.ewi_valid else [] - iwi_parts = [part for part in wall_parts if part["type"] == "internal_wall_insulation"] + iwi_parts = [part for part in self.materials if part["type"] == "internal_wall_insulation"] # Recommend external and internal wall insulation separately # Since external and internal wall insulation are sufficiently different,