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 # Houses
"end-terrace", "semi-detached", "detached", "mid-terrace", "end-terrace", "semi-detached", "detached", "mid-terrace",
# Flats # Flats
"ground floor", "mid-floor", "top-floor" "ground floor", "mid-floor", "top-floor", "basement"
} }
BUILT_FORM_MAPPINGS = { BUILT_FORM_MAPPINGS = {
@ -16,5 +16,29 @@ BUILT_FORM_MAPPINGS = {
'Maisonette': 'unknown', 'Maisonette': 'unknown',
'Flat': 'unknown', 'Flat': 'unknown',
'First Floor Flat General': 'mid-floor', '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', 'House (Mid terrace)': 'house',
'Bungalow (Semi)': 'bungalow', 'Bungalow (Semi)': 'bungalow',
'Ground Floor Flat General': 'flat', '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', 'Cavity CWI required': 'uninsulated cavity',
'Solid brick EWI installed': 'insulated solid brick', 'Solid brick EWI installed': 'insulated solid brick',
'Cavity Cavity batts': 'filled cavity', '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}, "Boiler and radiators, coal": {"fuel": "Coal", "cop": 0.85},
"From main system, no cylinderstat": {"fuel": "Natural Gas", "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.schemas import PlanTriggerRequest
from backend.app.plan.utils import get_cleaned from backend.app.plan.utils import get_cleaned
from backend.app.utils import epc_to_sap_lower_bound, sap_to_epc 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.ml_models.api import ModelApi
from backend.Property import Property 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 # 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} property_measure_types = {rec["type"] for recs in recommendations[p.id] for rec in recs}
measures_to_optimise = recommendations[p.id] property_required_measures = [
property_required_measures = [] m for m in recommendations[p.id] if m[0]["type"] in body.required_measures
if body.required_measures: ]
property_required_measures = [ measures_to_optimise = [
m for m in measures_to_optimise if m[0]["type"] in body.required_measures m for m in recommendations[p.id] if m[0]["type"] not in body.required_measures
] ]
measures_to_optimise = [
m for m in measures_to_optimise if m[0]["type"] not in body.required_measures
]
# If we have a wall insulation measure, we MUST include mechanical ventilation # If we have a wall insulation measure, we MUST include mechanical ventilation
# Additionally, if we have required measures, they should also be included. Therefore # 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) # we can discount the number of points required to get to the target SAP band (or increase)
# in the case of ventilation # in the case of ventilation
measures_needing_ventilation = [ needs_ventilation = any(x in property_measure_types for x in assumptions.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)
input_measures = prepare_input_measures( input_measures = prepare_input_measures(measures_to_optimise, body.goal, needs_ventilation)
measures_to_optimise, body.goal, needs_ventilation, measures_needing_ventilation
)
if not input_measures[0]: if not input_measures[0]:
# This means that we have no defaults # This means that we have no defaults
selected_recommendations = {} selected_recommendations = {}
solution = []
else: else:
fixed_gain = 0 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 # if the property needs ventilation, but the measure we optimise didn't include
# venilation we add the points for ventilation as a fixed gain # venilation we add the points for ventilation as a fixed gain
if needs_ventilation and any( 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( fixed_gain += next(
(r[0]["sap_points"] for r in recommendations[p.id] if (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 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 [ if any(x in [r["type"] for r in solution] for x in assumptions.measures_needing_ventilation):
"internal_wall_insulation", "external_wall_insulation", "cavity_wall_insulation"
]):
ventilation_rec = next( ventilation_rec = next(
(r[0] for r in recommendations[p.id] if r[0]["type"] == "mechanical_ventilation"), (r[0] for r in recommendations[p.id] if r[0]["type"] == "mechanical_ventilation"),
None 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 # 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 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 # when we have buildings, we tweak our solar PV recommendations as if one unit needs it, we apply it to all
# of them # 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 Basic function to convert recommendations_to_upload to a format that is
suitable for the optimiser - large 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, :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 the goal should reflect that desired gain
:param needs_ventilation: boolean to indicate if the property needs ventilation :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 :return: Nested list of input measures
""" """
@ -44,18 +46,20 @@ def prepare_input_measures(property_recommendations, goal, needs_ventilation, me
for rec in recs: for rec in recs:
# We bundle the impact of ventilation with the measure # We bundle the impact of ventilation with the measure
total = ( 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"] else rec["total"]
) )
gain = ( 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] else rec[goal_key]
) )
rec_type = ( rec_type = (
"+".join( "+".join(
[rec["type"], ventilation_recommendation["type"]] [rec["type"], ventilation_recommendation["type"]]
) if rec["type"] in measures_needing_ventilation ) if rec["type"] in assumptions.measures_needing_ventilation
else rec["type"] else rec["type"]
) )