optimisation bugs fixed and reviewed with kieran

This commit is contained in:
Khalim Conn-Kowlessar 2024-02-20 13:52:28 +00:00
parent 7ee9476dc1
commit 3dc5405b74
6 changed files with 44 additions and 21 deletions

View file

@ -623,7 +623,6 @@ class Property:
floor_height=self.floor_height,
perimeter=self.perimeter,
built_form=self.data["built-form"],
property_type=self.data["property-type"],
)
self.insulation_floor_area = self.floor_area / self.number_of_floors

View file

@ -134,7 +134,6 @@ async def trigger_plan(body: PlanTriggerRequest):
p.get_spatial_data(uprn_filenames)
logger.info("Getting components and epc recommendations")
recommendations = {}
recommendations_scoring_data = []
representative_recommendations = {}

View file

@ -18,8 +18,9 @@ class HeatingRecommender:
# This first iteration of the recommender will provide very basic recommendation
# We recommend heating controls based on the main heating system
if self.property.main_heating["clean_description"] == "Room heaters, electric":
self.recommend_room_heaters_electric(phase=phase, system_change=False, heating_controls_only=True)
self.recommend_electric_storage_heaters(phase=phase, system_change=True, heating_controls_only=False)
# Recommend high heat retention storage heaters
# self.recommend_room_heaters_electric(phase=phase, system_change=False, heating_controls_only=True)
# self.recommend_electric_storage_heaters(phase=phase, system_change=True, heating_controls_only=False)
return
if self.property.main_heating["clean_description"] == "Electric storage heaters, radiators":
@ -164,7 +165,8 @@ class HeatingRecommender:
costs={},
description="",
phase=phase,
heating_controls_only=heating_controls_only
heating_controls_only=heating_controls_only,
system_change=system_change
)
)
return
@ -221,7 +223,8 @@ class HeatingRecommender:
costs={},
description="",
phase=phase,
heating_controls_only=heating_controls_only
heating_controls_only=heating_controls_only,
system_change=system_change
)
)
return

View file

@ -30,7 +30,9 @@ class CostOptimiser:
:param min_gain: Numerical value for the minimum gain
:return:
"""
if min_gain <= 5:
if min_gain == 0:
return min_gain
elif min_gain <= 5:
return min_gain + 0.5
elif min_gain <= 20:
return min_gain + 1.5

View file

@ -9,11 +9,15 @@ class GainOptimiser:
This class is used to maximise gain, given a constrained cost
"""
def __init__(self, components, max_cost, max_gain=None):
def __init__(self, components, max_cost, max_gain):
"""
This function will try and maximise the gain, given a constrained cost. If we specific a max_gain, then the
optimisation routine is constained to try not to exceed a maximum increase
If the maximum gain (`max_gain`) is explicitly set to 0, the optimization routine interprets this as an
instruction not to perform any optimization.
:param components: List of components, where each component is a dictionary with keys "id", "cost" and "gain"
:param max_cost: Maximum cost constraint
:param max_gain: Maximum gain constraint
@ -78,6 +82,10 @@ class GainOptimiser:
# Remove the original cost constraint
self.m.remove(self.cost_constraint)
if self.max_gain is not None:
# Remove the original max gain constraint
self.m.remove(self.max_gain_constraint)
# Add slack variable
s = self.m.add_var(lb=0)
@ -99,18 +107,34 @@ class GainOptimiser:
def solve(self):
# Solve the problem
if self.max_gain == 0:
logger.info("Max gain is set to 0, no optimisation will be performed")
# Nothing to do
return
self.m.optimize()
if self.m.status == OptimizationStatus.INFEASIBLE:
logger.info("We have an infeasible model, setting up slack model")
self.setup_slack()
self.m.optimize()
self.solution = [
solution = [
item for group, group_vars in zip(self.components, self.variables) for item, var in zip(group, group_vars)
if
var.x >= 0.99
]
if (self.m.status == OptimizationStatus.INFEASIBLE) or (
(self.m.status == OptimizationStatus.OPTIMAL) and not len(solution)
):
logger.info("We have an infeasible model, setting up slack model")
self.setup_slack()
self.m.optimize()
solution = [
item for group, group_vars in zip(self.components, self.variables) for item, var in
zip(group, group_vars)
if
var.x >= 0.99
]
self.solution = solution
self.solution_gain = self.m.objective.x
self.solution_cost = sum([component['cost'] for component in self.solution])

View file

@ -544,7 +544,7 @@ def get_wall_type(
return None
def estimate_external_wall_area(num_floors, floor_height, perimeter, built_form, property_type):
def estimate_external_wall_area(num_floors, floor_height, perimeter, built_form):
"""
This method estimates the external wall area based on fundamental assumptions about the home
@ -553,7 +553,6 @@ def estimate_external_wall_area(num_floors, floor_height, perimeter, built_form,
:param floor_height: Height of one floor in meters.
:param perimeter: Total perimeter of the building on one floor in meters.
:param built_form: The built form of the property. This is used to determine the number of exposed walls.
:param property_type: The type of the property. This is used to determine the number of exposed walls.
:return:
"""
wall_area_one_floor = perimeter * floor_height
@ -566,11 +565,8 @@ def estimate_external_wall_area(num_floors, floor_height, perimeter, built_form,
'Semi-Detached': 3,
'Detached': 4,
}
if built_form == "Detached" and property_type == "Flat":
# We don't have 4 exposed walls for a flat
exposed_wall_area = total_wall_area * (3 / 4)
else:
exposed_wall_area = total_wall_area * (number_exposed_walls.get(built_form, 3) / 4)
exposed_wall_area = total_wall_area * (number_exposed_walls.get(built_form, 3) / 4)
return exposed_wall_area