ammended RIR conditions

This commit is contained in:
Khalim Conn-Kowlessar 2024-11-05 17:37:04 +00:00
parent 2b6c86e557
commit 42dc635aa3
7 changed files with 166 additions and 10 deletions

2
.idea/Model.iml generated
View file

@ -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="Route-March" jdkType="Python SDK" />
<orderEntry type="jdk" jdkName="Fastapi-backend" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyNamespacePackagesService">

2
.idea/misc.xml generated
View file

@ -3,7 +3,7 @@
<component name="Black">
<option name="sdkName" value="Python 3.10 (backend)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Route-March" project-jdk-type="Python SDK" />
<component name="ProjectRootManager" version="2" project-jdk-name="Fastapi-backend" project-jdk-type="Python SDK" />
<component name="PyCharmProfessionalAdvertiser">
<option name="shown" value="true" />
</component>

View file

@ -759,7 +759,11 @@ async def trigger_plan(body: PlanTriggerRequest):
new_epc = sap_to_epc(new_sap_points)
new_epc_bands[p.id] = new_epc
valuations = PropertyValuation.estimate(property_instance=p, target_epc=new_epc)
total_cost = sum([r["total"] for r in default_recommendations])
valuations = PropertyValuation.estimate(
property_instance=p, target_epc=new_epc, total_cost=total_cost
)
property_value_increase_ranges[p.id] = valuations
if p.is_new:

View file

@ -203,7 +203,14 @@ class PropertyValuation:
return msm_increase, lloyds_increase
@classmethod
def estimate(cls, property_instance, target_epc):
def estimate(cls, property_instance, target_epc, total_cost=None):
"""
This function estimates the value of a property based on the current EPC rating and the target EPC rating
:param property_instance: An instance of the Property class
:param target_epc: The target EPC rating
:param total_cost: The total cost of the retrofit
:return:
"""
current_value = (
property_instance.valuation if property_instance.valuation else
cls.UPRN_VALUE_LOOKUP.get(property_instance.uprn)
@ -242,6 +249,19 @@ class PropertyValuation:
avg_increase = np.mean(all_increases)
if total_cost is not None:
# We CAP the retrofit ROI at 2
avg_increase_value = current_value * avg_increase
if avg_increase_value / total_cost > 2:
# We re-scale the % so that the average value increase is no more than 2 times the total cost
double_cost = 2 * total_cost
new_avg_increase = double_cost / current_value
scalar = new_avg_increase / avg_increase
# We scale the min and max increases by the same scalar
min_increase *= scalar
max_increase *= scalar
avg_increase = new_avg_increase
return {
"current_value": current_value,
"lower_bound_increased_value": float(current_value * (1 + min_increase)),

View file

@ -0,0 +1,123 @@
import pandas as pd
from utils.s3 import save_csv_to_s3
PORTFOLIO_ID = 115
USER_ID = 8
def app():
"""
Used to set up the remote assessments for Warwick
"""
asset_list = [
{
"uprn": 10033604792,
"address": "Flat 2, 3 Green Street",
"postcode": "W1K 6RN"
},
{
"uprn": 10033604794,
"address": "Flat 4, 3 Green Street",
"postcode": "W1K 6RN"
},
{
"uprn": 10033615515,
"address": "Apartment 4, 52 Green Street",
"postcode": "W1K 6RS"
}
]
asset_list = pd.DataFrame(asset_list)
# Store the asset list in s3
filename = f"{USER_ID}/{PORTFOLIO_ID}/asset_list.csv"
save_csv_to_s3(
dataframe=asset_list,
bucket_name="retrofit-plan-inputs-dev",
file_name=filename
)
non_invasive_recommendations = [
{
"uprn": 10033604792,
"recommendations": [
{
"type": "internal_wall_insulation",
"sap_points": 16,
"survey": True
}
]
},
{
"uprn": 10033604794,
"recommendations": [
{
"type": "internal_wall_insulation",
"sap_points": 14,
"survey": True
}
]
},
{
"uprn": 10033615515,
"recommendations": [
{
"type": "room_roof_insulation",
"sap_points": 12,
"survey": True
},
{
"type": "internal_wall_insulation",
"sap_points": 2,
"survey": True
}
]
}
]
# Store non-invasive recommendations in S3
non_invasive_recommendations_filename = f"{USER_ID}/{PORTFOLIO_ID}/non_invasive_recommendations.csv"
save_csv_to_s3(
dataframe=pd.DataFrame(non_invasive_recommendations),
bucket_name="retrofit-plan-inputs-dev",
file_name=non_invasive_recommendations_filename
)
valuation_data = [
{
"uprn": 10033604792,
"value": 3_692_000
},
{
"uprn": 10033604794,
"value": 3_789_000
},
{
"uprn": 10033615515,
"value": 3_499_000
}
]
# Store valuation data to s3
valuation_filename = f"{USER_ID}/{PORTFOLIO_ID}/valuation.csv"
save_csv_to_s3(
dataframe=pd.DataFrame(valuation_data),
bucket_name="retrofit-plan-inputs-dev",
file_name=valuation_filename
)
body = {
"portfolio_id": str(PORTFOLIO_ID),
"housing_type": "Private",
"goal": "Increasing EPC",
"goal_value": "C",
"trigger_file_path": filename,
"already_installed_file_path": "",
"patches_file_path": "",
"non_invasive_recommendations_file_path": non_invasive_recommendations_filename,
"valuation_file_path": valuation_filename,
"scenario_name": "Full package remote assessment",
"multi_plan": True,
"budget": None,
}
print(body)

View file

@ -519,6 +519,7 @@ class Recommendations:
# heating_cost_starting and heating_cost_ending are just the values in the EPC. However, with
# heating_cost_ending, we expect that the EPC will predict a heating cost based on what would happen
# if we implemented the recommendation today, so our starting value is the EPC
previous_phase_values = {
"sap": float(property_instance.data["current-energy-efficiency"]),
"carbon": float(property_instance.data["co2-emissions-current"]),
@ -541,8 +542,13 @@ class Recommendations:
previous_phase_values = previous_phase_values_multiple[0]
# We extract the values for the current phase
if rec.get("survey", False):
current_phase_sap = rec["sap_points"] + previous_phase_values["sap"]
else:
current_phase_sap = phase_energy_efficiency_metrics["sap_change"]
current_phase_values = {
"sap": phase_energy_efficiency_metrics["sap_change"],
"sap": current_phase_sap,
"carbon": phase_energy_efficiency_metrics["carbon_change"],
"heat_demand": phase_energy_efficiency_metrics["heat_demand"],
}

View file

@ -123,7 +123,11 @@ class RoofRecommendations:
self.property.roof["insulation_thickness"] in ["average", "above_average"]
)
return full_insulated_room_roof or room_roof_insulated_at_rafters
has_non_invasive_recommendation = any(
x["type"] == "room_roof_insulation" for x in self.property.non_invasive_recommendations
)
return (full_insulated_room_roof or room_roof_insulated_at_rafters) and not has_non_invasive_recommendation
def recommend(self, phase, measures=None, default_u_values=False):
@ -181,7 +185,8 @@ class RoofRecommendations:
# We firstly handle non-intrusive recommendations, which may override the normal roof insulation recommendations
if ("loft_insulation" in [x["type"] for x in non_invasive_recommendations]) or (
self.property.roof["is_pitched"] and "loft_insulation" in measures
self.property.roof["is_pitched"] and "loft_insulation" in measures and
not self.property.roof["is_at_rafters"]
):
self.recommend_roof_insulation(
u_value=u_value,
@ -512,8 +517,6 @@ class RoofRecommendations:
rir_non_invasive_recommendation.get("cost")
)
sap_points = rir_non_invasive_recommendation.get("sap_points", None)
# Could also be Roof room(s), ceiling insulated
new_descriptin = "Roof room(s), insulated"
roof_ending_config = RoofAttributes(new_descriptin).process()
@ -562,7 +565,7 @@ class RoofRecommendations:
"description": "Insulate room in roof at rafters and re-decorate",
"starting_u_value": u_value,
"new_u_value": new_u_value,
"sap_points": sap_points,
"sap_points": rir_non_invasive_recommendation.get("sap_points", None),
"simulation_config": simulation_config,
"description_simulation": {
"roof-description": new_descriptin,