mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Adding new materials to Materials enum
This commit is contained in:
parent
46f08c4aab
commit
d703447c6a
5 changed files with 12 additions and 188 deletions
2
.idea/Model.iml
generated
2
.idea/Model.iml
generated
|
|
@ -7,7 +7,7 @@
|
|||
<sourceFolder url="file://$MODULE_DIR$/open_uprn" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/recommendations" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.10 (model_data)" jdkType="Python SDK" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.10 (backend)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyNamespacePackagesService">
|
||||
|
|
|
|||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
|
|
@ -3,7 +3,7 @@
|
|||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.10 (backend)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (model_data)" project-jdk-type="Python SDK" />
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (backend)" project-jdk-type="Python SDK" />
|
||||
<component name="PythonCompatibilityInspectionAdvertiser">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ class MaterialType(enum.Enum):
|
|||
cavity_wall_insulation = "cavity_wall_insulation"
|
||||
mechanical_ventilation = "mechanical_ventilation"
|
||||
loft_insulation = "loft_insulation"
|
||||
exposed_floor_insulation = "exposed_floor_insulation"
|
||||
flat_roof_insulation = "flat_roof_insulation"
|
||||
room_roof_insulation = "room_roof_insulation"
|
||||
|
||||
|
||||
class DepthUnit(enum.Enum):
|
||||
|
|
|
|||
|
|
@ -1,176 +0,0 @@
|
|||
from datetime import datetime
|
||||
|
||||
import pandas as pd
|
||||
from epc_api.client import EpcClient
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.exc import IntegrityError, OperationalError
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from starlette.responses import Response
|
||||
|
||||
from backend.app.config import get_settings
|
||||
from backend.app.db.connection import db_engine
|
||||
from backend.app.db.functions.materials_functions import get_materials
|
||||
from backend.app.db.functions.portfolio_functions import aggregate_portfolio_recommendations
|
||||
from backend.app.db.functions.property_functions import (
|
||||
create_property, create_property_details_epc, create_property_targets, update_property_data
|
||||
)
|
||||
from backend.app.db.functions.recommendations_functions import (
|
||||
create_plan, create_plan_recommendations, upload_recommendations
|
||||
)
|
||||
from backend.app.db.models.portfolio import rating_lookup
|
||||
from backend.app.dependencies import validate_token
|
||||
from backend.app.plan.schemas import PlanTriggerRequest
|
||||
from backend.app.plan.utils import (
|
||||
create_recommendation_scoring_data, filter_materials, get_cleaned, insert_temp_recommendation_id
|
||||
)
|
||||
from backend.app.utils import epc_to_sap_lower_bound, read_csv_from_s3, read_parquet_from_s3
|
||||
|
||||
from backend.ml_models.sap_change_model.api import SAPChangeModelAPI
|
||||
from backend.Property import Property
|
||||
from etl.epc.DataProcessor import DataProcessor
|
||||
from etl.epc.settings import COLUMNS_TO_MERGE_ON
|
||||
from recommendations.FloorRecommendations import FloorRecommendations
|
||||
from recommendations.optimiser.CostOptimiser import CostOptimiser
|
||||
from recommendations.optimiser.GainOptimiser import GainOptimiser
|
||||
from recommendations.optimiser.optimiser_functions import prepare_input_measures
|
||||
from recommendations.WallRecommendations import WallRecommendations
|
||||
from utils.logger import setup_logger
|
||||
from utils.s3 import read_dataframe_from_s3_parquet
|
||||
|
||||
logger = setup_logger()
|
||||
|
||||
import pickle
|
||||
|
||||
with open('local_data.pickle', 'rb') as f:
|
||||
local_data = pickle.load(f)
|
||||
|
||||
with open("property_dimensions.pickle", "rb") as f:
|
||||
property_dimensions = pickle.load(f)
|
||||
|
||||
with open("sap_change_dataset.pickle", "rb") as f:
|
||||
sap_change_dataset = pickle.load(f)
|
||||
|
||||
created_at = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
|
||||
|
||||
plan_input = local_data["plan_input"]
|
||||
uprn_filenames = local_data["uprn_filenames"]
|
||||
local_property_data = local_data["local_property_data"]
|
||||
materials = local_data["materials"]
|
||||
materials_by_type = filter_materials(materials)
|
||||
cleaned = local_data["cleaned"]
|
||||
cleaning_data = local_data["cleaning_data"]
|
||||
|
||||
# Need to find some proper materials
|
||||
materials_by_type["walls"] += [
|
||||
{'id': 4, 'type': 'cavity_wall_insulation', 'description': 'Example Material 1',
|
||||
'depths': None,
|
||||
'depth_unit': None, 'cost': 20,
|
||||
'cost_unit': 'gbp_sq_meter', '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': None, 'created_at': None, 'is_active': True},
|
||||
{'id': 10, 'type': "cavity_wall_insulation", 'description': 'Example Material 2',
|
||||
'depths': None, 'depth_unit': None, 'cost': 25, 'cost_unit': 'gbp_sq_meter',
|
||||
'r_value_per_mm': 0.02631579, 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.038,
|
||||
'thermal_conductivity_unit': 'watt_per_meter_kelvin',
|
||||
'link': None,
|
||||
'created_at': None, 'is_active': True}
|
||||
]
|
||||
|
||||
epc_client = EpcClient(auth_token="NO-TOKEN")
|
||||
|
||||
input_properties = []
|
||||
for i, config in enumerate(plan_input):
|
||||
property_id = local_property_data[i]["id"]
|
||||
input_properties.append(
|
||||
Property(
|
||||
postcode=config['postcode'],
|
||||
address1=config['address'],
|
||||
epc_client=epc_client,
|
||||
id=property_id
|
||||
)
|
||||
)
|
||||
|
||||
logger.info("Getting EPC, and spatial data")
|
||||
for i, p in enumerate(input_properties):
|
||||
p.data = local_property_data[i]["data"]
|
||||
p.uprn = local_property_data[i]["uprn"]
|
||||
p.id = local_property_data[i]["id"]
|
||||
p.full_sap_epc = local_property_data[i]["full_sap_epc"]
|
||||
p.old_data = local_property_data[i]["old_data"]
|
||||
p.is_listed = False
|
||||
p.in_conservation_area = False
|
||||
p.is_heritage = False
|
||||
|
||||
p.set_year_built()
|
||||
|
||||
# TODO: TESTING
|
||||
p.data['number-habitable-rooms'] = 3
|
||||
|
||||
recommendations = {}
|
||||
recommendations_scoring_data = []
|
||||
|
||||
for p in input_properties:
|
||||
property_recommendations = []
|
||||
|
||||
# Property recommendations
|
||||
p.get_components(cleaned)
|
||||
|
||||
# Floor recommendations
|
||||
floor_recommender = FloorRecommendations(
|
||||
property_instance=p,
|
||||
materials=materials_by_type["floor"],
|
||||
)
|
||||
floor_recommender.recommend()
|
||||
|
||||
if floor_recommender.recommendations:
|
||||
property_recommendations.append(floor_recommender.recommendations)
|
||||
|
||||
# Wall recommendations
|
||||
|
||||
wall_recomender = WallRecommendations(
|
||||
property_instance=p,
|
||||
materials=materials_by_type["walls"]
|
||||
)
|
||||
wall_recomender.recommend()
|
||||
|
||||
if wall_recomender.recommendations:
|
||||
property_recommendations.append(wall_recomender.recommendations)
|
||||
|
||||
# We insert temporary ids into the recommendations which is important for the optimiser later
|
||||
property_recommendations = insert_temp_recommendation_id(property_recommendations)
|
||||
|
||||
if not property_recommendations:
|
||||
continue
|
||||
|
||||
recommendations[p.id] = property_recommendations
|
||||
|
||||
# Finally, we'll prepare data for predicting the impact on SAP
|
||||
# TODO: We should use the cleaned data from get_components in the data rather than the raw
|
||||
# values. We should create a method in Property which takes the EPC data and inserts the cleaned
|
||||
# data
|
||||
|
||||
data_processor = DataProcessor(None, newdata=True)
|
||||
data_processor.insert_data(pd.DataFrame([p.data.copy()]))
|
||||
data_processor.pre_process()
|
||||
|
||||
starting_epc_data = data_processor.get_component_features(suffix="_STARTING")
|
||||
ending_epc_data = data_processor.get_component_features(suffix="_ENDING")
|
||||
fixed_data = data_processor.get_fixed_features()
|
||||
|
||||
# We update the ending record with the recommended updates and we set lodgement date to today
|
||||
ending_epc_data["LODGEMENT_DATE_ENDING"] = created_at
|
||||
|
||||
for recommendations_by_type in property_recommendations:
|
||||
for rec in recommendations_by_type:
|
||||
scoring_dict = create_recommendation_scoring_data(
|
||||
property=p,
|
||||
recommendation=rec,
|
||||
starting_epc_data=starting_epc_data,
|
||||
ending_epc_data=ending_epc_data,
|
||||
fixed_data=fixed_data,
|
||||
)
|
||||
|
||||
recommendations_scoring_data.append(scoring_dict)
|
||||
|
||||
# cleanup
|
||||
del data_processor
|
||||
|
|
@ -47,7 +47,8 @@ class RoofRecommendations:
|
|||
|
||||
# Building regulations part L recommend installing at least 270mm of insulation, however generally we
|
||||
# experience diminishing returns in terms of SAP once we go beyond around 150mm of insulation
|
||||
if insulation_thickness >= self.MINIMUM_LOFT_ISULATION_MM:
|
||||
# This only holds true for pitched roofs
|
||||
if (insulation_thickness >= self.MINIMUM_LOFT_ISULATION_MM) and self.property.roof["is_pitched"]:
|
||||
return
|
||||
|
||||
# If we have a u-value already, need to implement this
|
||||
|
|
@ -61,7 +62,7 @@ class RoofRecommendations:
|
|||
return
|
||||
|
||||
if self.property.roof["is_roof_room"]:
|
||||
self.recommend_room_roof_insulation(u_value, insulation_thickness)
|
||||
self.recommend_room_roof_insulation(u_value)
|
||||
return
|
||||
|
||||
raise NotImplementedError("Implement me")
|
||||
|
|
@ -125,8 +126,9 @@ class RoofRecommendations:
|
|||
for depth, cost_per_unit in zip(material["depths"], material["cost"]):
|
||||
|
||||
# We make sure we hit a depth of 270mm. We should factor in any existing insulation if the
|
||||
# loft is already partially insulated
|
||||
if (depth + insulation_thickness) < self.MINIMUM_LOFT_ISULATION_MM:
|
||||
# loft is already partially insulated.
|
||||
# Note: This requirement is only for loft insulation
|
||||
if ((depth + insulation_thickness) < self.MINIMUM_LOFT_ISULATION_MM) and roof["is_pitched"]:
|
||||
continue
|
||||
|
||||
part_u_value = r_value_per_mm_to_u_value(depth, material["r_value_per_mm"])
|
||||
|
|
@ -178,7 +180,7 @@ class RoofRecommendations:
|
|||
|
||||
self.recommendations = recommendations
|
||||
|
||||
def recommend_room_roof_insulation(self, u_value, insulation_thickness):
|
||||
def recommend_room_roof_insulation(self, u_value):
|
||||
"""
|
||||
This method recommends room in roof insulation for properties that have been identified
|
||||
to possess a room in roof.
|
||||
|
|
@ -217,7 +219,6 @@ class RoofRecommendations:
|
|||
- Flat ceilings can be insulated like a standard loft.
|
||||
|
||||
:param u_value: Current u-value of the roof
|
||||
:param insulation_thickness: Current insulation thickness of the roof
|
||||
:return:
|
||||
"""
|
||||
|
||||
|
|
@ -232,10 +233,6 @@ class RoofRecommendations:
|
|||
recommendations = []
|
||||
for material in roof_roof_insulation_materials:
|
||||
for depth, cost_per_unit in zip(material["depths"], material["cost"]):
|
||||
# We make sure we hit a depth of 270mm. We should factor in any existing insulation if the
|
||||
# loft is already partially insulated
|
||||
if (depth + insulation_thickness) < self.MINIMUM_LOFT_ISULATION_MM:
|
||||
continue
|
||||
|
||||
part_u_value = r_value_per_mm_to_u_value(depth, material["r_value_per_mm"])
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue