starting to tidy up tweaks to optimiser

This commit is contained in:
Khalim Conn-Kowlessar 2025-03-19 19:21:11 +00:00
parent 7111f1a43a
commit b35d021db9
6 changed files with 79 additions and 36 deletions

View file

@ -3,7 +3,7 @@ STANDARD_BUILT_FORMS = {
# Houses
"end-terrace", "semi-detached", "detached", "mid-terrace",
# Flats
"ground floor", "mid-floor", "top-floor"
"ground floor", "mid-floor", "top-floor", "basement"
}
BUILT_FORM_MAPPINGS = {
@ -16,5 +16,29 @@ BUILT_FORM_MAPPINGS = {
'Maisonette': 'unknown',
'Flat': 'unknown',
'First Floor Flat General': 'mid-floor',
'Bungalow (Semi)': 'semi-detached'
'Bungalow (Semi)': 'semi-detached',
'Detached House': 'detached',
'End Terraced House': 'end-terrace',
'Studio (Ground floor)': 'ground floor',
'Mid Terraced House': 'mid-terrace',
'Ground Floor Flat': 'ground floor',
'Semi Detached House': 'semi-detached',
'Detached Property': 'detached',
'Level not confirmed': 'unknown',
'Bedsit': 'unknown',
'Cottage': 'detached',
'Terraced House': 'mid-terrace',
'Studio (1st Floor)': 'ground floor',
'Standard Maisonette': 'unknown',
'Third Floor Flat or Above': 'top-floor',
'Town House': 'end-terrace',
'Guest room in a complex': 'unknown',
'Back To Back House': 'mid-terrace',
'PIMSS EMPTY': 'unknown',
'Flat Basement': 'basement',
'House': 'unknown',
'Second Floor Flat': 'mid-floor',
'First Floor Flat': 'ground floor',
'Room Only': 'unknown'
}

View file

@ -70,6 +70,27 @@ PROPERTY_MAPPING = {
'House (Mid terrace)': 'house',
'Bungalow (Semi)': 'bungalow',
'Ground Floor Flat General': 'flat',
'House (Semi)': 'house'
'House (Semi)': 'house',
'Detached House': 'house',
'Bedsit': 'bedsit',
'Terraced House': 'house',
'Standard Maisonette': 'maisonette',
'End Terraced House': 'house',
'Third Floor Flat or Above': 'flat',
'Town House': 'house',
'Mid Terraced House': 'house',
'Back To Back House': 'house',
'Flat Basement': 'flat',
'Ground Floor Flat': 'flat',
'Semi Detached House': 'house',
'Second Floor Flat': 'flat',
'First Floor Flat': 'flat',
'Level not confirmed': 'flat',
'Cottage': 'house',
'Studio (1st Floor)': 'flat',
'Studio (Ground floor)': 'flat',
'Guest room in a complex': 'other',
'PIMSS EMPTY': 'bedsit',
'Room Only': 'other',
'Detached Property': 'house'
}

View file

@ -134,5 +134,6 @@ WALL_CONSTRUCTION_MAPPINGS = {
'Cavity CWI required': 'uninsulated cavity',
'Solid brick EWI installed': 'insulated solid brick',
'Cavity Cavity batts': 'filled cavity',
'Cavity CWI Completed by Dyson': 'filled cavity'
'Cavity CWI Completed by Dyson': 'filled cavity',
None: "unknown"
}

View file

@ -59,3 +59,9 @@ DESCRIPTIONS_TO_FUEL_TYPES = {
"Boiler and radiators, coal": {"fuel": "Coal", "cop": 0.85},
"From main system, no cylinderstat": {"fuel": "Natural Gas", "cop": 0.85},
}
# These are the measure types where if there is a ventilation recommendation, we force the inclusion of it
# if one of these has been recommended.
measures_needing_ventilation = [
"internal_wall_insulation", "external_wall_insulation", "cavity_wall_insulation"
]

View file

@ -27,6 +27,7 @@ from backend.app.dependencies import validate_token
from backend.app.plan.schemas import PlanTriggerRequest
from backend.app.plan.utils import get_cleaned
from backend.app.utils import epc_to_sap_lower_bound, sap_to_epc
import backend.app.assumptions as assumptions
from backend.ml_models.api import ModelApi
from backend.Property import Property
@ -707,32 +708,25 @@ async def trigger_plan(body: PlanTriggerRequest):
# we need to double unlist because we have a list of lists
property_measure_types = {rec["type"] for recs in recommendations[p.id] for rec in recs}
measures_to_optimise = recommendations[p.id]
property_required_measures = []
if body.required_measures:
property_required_measures = [
m for m in measures_to_optimise if m[0]["type"] in body.required_measures
]
measures_to_optimise = [
m for m in measures_to_optimise if m[0]["type"] not in body.required_measures
]
property_required_measures = [
m for m in recommendations[p.id] if m[0]["type"] in body.required_measures
]
measures_to_optimise = [
m for m in recommendations[p.id] if m[0]["type"] not in body.required_measures
]
# If we have a wall insulation measure, we MUST include mechanical ventilation
# Additionally, if we have required measures, they should also be included. Therefore
# we can discount the number of points required to get to the target SAP band (or increase)
# in the case of ventilation
measures_needing_ventilation = [
"internal_wall_insulation", "external_wall_insulation", "cavity_wall_insulation"
]
needs_ventilation = any(x in property_measure_types for x in measures_needing_ventilation)
needs_ventilation = any(x in property_measure_types for x in assumptions.measures_needing_ventilation)
input_measures = prepare_input_measures(
measures_to_optimise, body.goal, needs_ventilation, measures_needing_ventilation
)
input_measures = prepare_input_measures(measures_to_optimise, body.goal, needs_ventilation)
if not input_measures[0]:
# This means that we have no defaults
selected_recommendations = {}
solution = []
else:
fixed_gain = 0
@ -755,7 +749,7 @@ async def trigger_plan(body: PlanTriggerRequest):
# if the property needs ventilation, but the measure we optimise didn't include
# venilation we add the points for ventilation as a fixed gain
if needs_ventilation and any(
r in property_required_measure_types for r in measures_needing_ventilation
r in property_required_measure_types for r in assumptions.measures_needing_ventilation
):
fixed_gain += next(
(r[0]["sap_points"] for r in recommendations[p.id] if
@ -823,9 +817,7 @@ async def trigger_plan(body: PlanTriggerRequest):
)
# If wall insulation is selected, we also include mechanical ventilation as a best practice measure
if any(x in [r["type"] for r in solution] for x in [
"internal_wall_insulation", "external_wall_insulation", "cavity_wall_insulation"
]):
if any(x in [r["type"] for r in solution] for x in assumptions.measures_needing_ventilation):
ventilation_rec = next(
(r[0] for r in recommendations[p.id] if r[0]["type"] == "mechanical_ventilation"),
None
@ -854,14 +846,9 @@ async def trigger_plan(body: PlanTriggerRequest):
]
# We'll also unlist the recommendations so they're a bit easier to handle from here onwards
final_recommendations = [
recommendations[p.id] = [
rec for recommendations_by_type in final_recommendations for rec in recommendations_by_type
]
# Get defaults
defaults = [r for r in final_recommendations if r["default"]]
sum([r['sap_points'] for r in defaults])
recommendations[p.id] = final_recommendations
# when we have buildings, we tweak our solar PV recommendations as if one unit needs it, we apply it to all
# of them

View file

@ -1,4 +1,7 @@
def prepare_input_measures(property_recommendations, goal, needs_ventilation, measures_needing_ventilation):
import backend.app.assumptions as assumptions
def prepare_input_measures(property_recommendations, goal, needs_ventilation):
"""
Basic function to convert recommendations_to_upload to a format that is
suitable for the optimiser - large
@ -6,7 +9,6 @@ def prepare_input_measures(property_recommendations, goal, needs_ventilation, me
:param goal: goal to be optimised for, should be one of the keys in gain_map. E.g. if the gain is SAP points,
the goal should reflect that desired gain
:param needs_ventilation: boolean to indicate if the property needs ventilation
:param measures_needing_ventilation: list of measures that need ventilation
:return: Nested list of input measures
"""
@ -44,18 +46,20 @@ def prepare_input_measures(property_recommendations, goal, needs_ventilation, me
for rec in recs:
# We bundle the impact of ventilation with the measure
total = (
rec["total"] + ventilation_recommendation["total"] if rec["type"] in measures_needing_ventilation
rec["total"] + ventilation_recommendation["total"]
if rec["type"] in assumptions.measures_needing_ventilation
else rec["total"]
)
gain = (
rec[goal_key] + ventilation_recommendation[goal_key] if rec["type"] in measures_needing_ventilation
rec[goal_key] + ventilation_recommendation[goal_key]
if rec["type"] in assumptions.measures_needing_ventilation
else rec[goal_key]
)
rec_type = (
"+".join(
[rec["type"], ventilation_recommendation["type"]]
) if rec["type"] in measures_needing_ventilation
) if rec["type"] in assumptions.measures_needing_ventilation
else rec["type"]
)