mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
intial impelmentation of strategic optimiser with tests
This commit is contained in:
parent
50a8629cba
commit
8e22ced679
7 changed files with 975 additions and 61 deletions
|
|
@ -73,61 +73,24 @@ def app():
|
||||||
Property UPRN
|
Property UPRN
|
||||||
"""
|
"""
|
||||||
|
|
||||||
data_folder = "/Users/khalimconn-kowlessar/Documents/hestia/Customers/West Kent"
|
data_folder = "/Users/khalimconn-kowlessar/Downloads"
|
||||||
data_filename = "West Kent Asset List.xlsx"
|
data_filename = "assests.xlsx"
|
||||||
sheet_name = "Sheet1"
|
sheet_name = "Sheet1"
|
||||||
postcode_column = "POSTCODE"
|
postcode_column = "Postcode"
|
||||||
address1_column = None
|
address1_column = "Address"
|
||||||
address1_method = "house_number_extraction"
|
address1_method = "house_number_extraction"
|
||||||
fulladdress_column = "ADDRESS"
|
fulladdress_column = None
|
||||||
address_cols_to_concat = []
|
address_cols_to_concat = ["Address"]
|
||||||
missing_postcodes_method = None
|
missing_postcodes_method = None
|
||||||
landlord_year_built = None
|
landlord_year_built = None
|
||||||
landlord_os_uprn = None
|
landlord_os_uprn = "UPRN"
|
||||||
landlord_property_type = "PROPERTY TYPE"
|
landlord_property_type = "Archetype"
|
||||||
landlord_built_form = None
|
landlord_built_form = "Bedroom Count"
|
||||||
landlord_wall_construction = "wall combined"
|
landlord_wall_construction = "Wall Insulation Type"
|
||||||
landlord_roof_construction = "HEATING SYSTEM"
|
landlord_roof_construction = "Roof Type"
|
||||||
landlord_heating_system = None
|
landlord_heating_system = "Boiler Type"
|
||||||
landlord_existing_pv = None
|
landlord_existing_pv = None
|
||||||
landlord_property_id = "UPRN"
|
landlord_property_id = "Tab"
|
||||||
landlord_sap = None
|
|
||||||
outcomes_filename = None
|
|
||||||
outcomes_sheetname = None
|
|
||||||
outcomes_postcode = None
|
|
||||||
outcomes_houseno = None
|
|
||||||
outcomes_id = None
|
|
||||||
outcomes_address = None
|
|
||||||
master_filepaths = []
|
|
||||||
master_id_colnames = []
|
|
||||||
master_to_asset_list_filepath = None
|
|
||||||
phase = False
|
|
||||||
ecosurv_landlords = None
|
|
||||||
asset_list_header = 0
|
|
||||||
landlord_block_reference = None
|
|
||||||
|
|
||||||
# Peabody data for cleaning
|
|
||||||
data_folder = (
|
|
||||||
"/Users/khalimconn-kowlessar/Documents/hestia/Customers/Peabody/Nov 2025 Consulting "
|
|
||||||
"Project/data_validation"
|
|
||||||
)
|
|
||||||
data_filename = "to_standardise_uprns.xlsx"
|
|
||||||
sheet_name = "Sheet1"
|
|
||||||
postcode_column = "POSTCODE"
|
|
||||||
address1_column = None
|
|
||||||
address1_method = "house_number_extraction"
|
|
||||||
fulladdress_column = "ADDRESS"
|
|
||||||
address_cols_to_concat = []
|
|
||||||
missing_postcodes_method = None
|
|
||||||
landlord_year_built = None
|
|
||||||
landlord_os_uprn = None
|
|
||||||
landlord_property_type = "PROPERTY TYPE"
|
|
||||||
landlord_built_form = None # Skipped as empty
|
|
||||||
landlord_wall_construction = "wall combined" # combin F + G
|
|
||||||
landlord_roof_construction = "HEATING SYSTEM" # Combine I + J
|
|
||||||
landlord_heating_system = None # Check with Khalim
|
|
||||||
landlord_existing_pv = None
|
|
||||||
landlord_property_id = "UPRN"
|
|
||||||
landlord_sap = None
|
landlord_sap = None
|
||||||
outcomes_filename = None
|
outcomes_filename = None
|
||||||
outcomes_sheetname = None
|
outcomes_sheetname = None
|
||||||
|
|
@ -279,7 +242,7 @@ def app():
|
||||||
if skip is not None and not force_retrieve_data:
|
if skip is not None and not force_retrieve_data:
|
||||||
if i <= skip:
|
if i <= skip:
|
||||||
continue
|
continue
|
||||||
chunk = asset_list.standardised_asset_list[i : i + chunk_size]
|
chunk = asset_list.standardised_asset_list[i: i + chunk_size]
|
||||||
epc_data_chunk, errors_chunk, no_epc_chunk = get_data(
|
epc_data_chunk, errors_chunk, no_epc_chunk = get_data(
|
||||||
df=chunk,
|
df=chunk,
|
||||||
row_id_name=asset_list.DOMNA_PROPERTY_ID,
|
row_id_name=asset_list.DOMNA_PROPERTY_ID,
|
||||||
|
|
@ -422,7 +385,7 @@ def app():
|
||||||
# Retrieve just the data we need
|
# Retrieve just the data we need
|
||||||
epc_df = epc_df[
|
epc_df = epc_df[
|
||||||
[asset_list.DOMNA_PROPERTY_ID] + list(asset_list.EPC_API_DATA_NAMES.keys())
|
[asset_list.DOMNA_PROPERTY_ID] + list(asset_list.EPC_API_DATA_NAMES.keys())
|
||||||
].rename(columns=asset_list.EPC_API_DATA_NAMES)
|
].rename(columns=asset_list.EPC_API_DATA_NAMES)
|
||||||
|
|
||||||
# Look for columns not in the find my EPC data, which will have happened if we didn't
|
# Look for columns not in the find my EPC data, which will have happened if we didn't
|
||||||
# retrieve it in the first place
|
# retrieve it in the first place
|
||||||
|
|
@ -439,7 +402,7 @@ def app():
|
||||||
find_my_epc_data[
|
find_my_epc_data[
|
||||||
[asset_list.DOMNA_PROPERTY_ID, "epc_has_floor_recommendation"]
|
[asset_list.DOMNA_PROPERTY_ID, "epc_has_floor_recommendation"]
|
||||||
+ list(asset_list.FIND_EPC_DATA_NAMES.keys())
|
+ list(asset_list.FIND_EPC_DATA_NAMES.keys())
|
||||||
].rename(columns=asset_list.FIND_EPC_DATA_NAMES),
|
].rename(columns=asset_list.FIND_EPC_DATA_NAMES),
|
||||||
how="left",
|
how="left",
|
||||||
on=asset_list.DOMNA_PROPERTY_ID,
|
on=asset_list.DOMNA_PROPERTY_ID,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1118,6 +1118,28 @@ async def model_engine(body: PlanTriggerRequest):
|
||||||
# When the goal is Increasing EPC, we can run the funding optimiser
|
# When the goal is Increasing EPC, we can run the funding optimiser
|
||||||
if body.goal == "Increasing EPC":
|
if body.goal == "Increasing EPC":
|
||||||
|
|
||||||
|
solutions_no_budget = optimise_with_scenarios(
|
||||||
|
p=p,
|
||||||
|
input_measures=input_measures,
|
||||||
|
budget=None,
|
||||||
|
target_gain=gain,
|
||||||
|
enforce_heat_pump_insulation=True,
|
||||||
|
enforce_fabric_first=body.enforce_fabric_first,
|
||||||
|
already_installed_sap=already_installed_sap, # To be passed to output
|
||||||
|
)
|
||||||
|
solutions_no_budget["total_cost"]
|
||||||
|
|
||||||
|
solutions_with_budget = optimise_with_scenarios(
|
||||||
|
p=p,
|
||||||
|
input_measures=input_measures,
|
||||||
|
budget=5000,
|
||||||
|
target_gain=gain,
|
||||||
|
enforce_heat_pump_insulation=True,
|
||||||
|
enforce_fabric_first=body.enforce_fabric_first,
|
||||||
|
already_installed_sap=already_installed_sap, # To be passed to output
|
||||||
|
)
|
||||||
|
solutions_with_budget["total_cost"]
|
||||||
|
|
||||||
solutions = optimise_with_scenarios(
|
solutions = optimise_with_scenarios(
|
||||||
p=p,
|
p=p,
|
||||||
input_measures=input_measures,
|
input_measures=input_measures,
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,16 @@ class CostOptimiser:
|
||||||
# We add an optional buffer to the minimum gain to allow for slack in the optimisation
|
# We add an optional buffer to the minimum gain to allow for slack in the optimisation
|
||||||
BUFFER = 0.2
|
BUFFER = 0.2
|
||||||
|
|
||||||
def __init__(self, components, min_gain, verbose=False):
|
def __init__(
|
||||||
|
self, components, min_gain, verbose=False, allow_slack=True
|
||||||
|
):
|
||||||
self.components = components
|
self.components = components
|
||||||
self.min_gain = min_gain
|
self.min_gain = min_gain
|
||||||
self.gain_constraint = None
|
self.gain_constraint = None
|
||||||
self.m = None
|
self.m = None
|
||||||
self.variables = []
|
self.variables = []
|
||||||
self.solution = []
|
self.solution = []
|
||||||
|
self.allow_slack = allow_slack
|
||||||
|
|
||||||
self.solution_cost = None
|
self.solution_cost = None
|
||||||
self.solution_gain = None
|
self.solution_gain = None
|
||||||
|
|
@ -81,6 +84,20 @@ class CostOptimiser:
|
||||||
for group_vars in self.variables:
|
for group_vars in self.variables:
|
||||||
self.m += xsum(var for var in group_vars) <= 1
|
self.m += xsum(var for var in group_vars) <= 1
|
||||||
|
|
||||||
|
def add_budget_constraint(self, budget: int | float) -> None:
|
||||||
|
# Inject budget constraint, which ensures that sum of cost_ig * x_ig <= budget, where cost_ig represents the
|
||||||
|
# cost for the ith component in group g, and x_ig is the binary decision variable for the ith component in
|
||||||
|
# group g
|
||||||
|
|
||||||
|
self.m += (
|
||||||
|
xsum(
|
||||||
|
item["cost"] * var
|
||||||
|
for group, group_vars in zip(self.components, self.variables)
|
||||||
|
for item, var in zip(group, group_vars)
|
||||||
|
)
|
||||||
|
<= budget
|
||||||
|
)
|
||||||
|
|
||||||
def setup_slack(self):
|
def setup_slack(self):
|
||||||
|
|
||||||
# Remove the original gain constraint
|
# Remove the original gain constraint
|
||||||
|
|
@ -109,10 +126,17 @@ class CostOptimiser:
|
||||||
self.m.optimize()
|
self.m.optimize()
|
||||||
|
|
||||||
if self.m.status == OptimizationStatus.INFEASIBLE:
|
if self.m.status == OptimizationStatus.INFEASIBLE:
|
||||||
# Turn off logging - too noisy
|
if self.allow_slack:
|
||||||
# logger.info("We have an infeasible model, setting up slack model")
|
self.setup_slack()
|
||||||
self.setup_slack()
|
self.m.optimize()
|
||||||
self.m.optimize()
|
else:
|
||||||
|
# Explicity return an empty solution
|
||||||
|
self.solution = []
|
||||||
|
self.solution_cost = 0
|
||||||
|
self.solution_gain = 0
|
||||||
|
return
|
||||||
|
|
||||||
|
# If we still have an infeasible solution, we return an empty solution
|
||||||
|
|
||||||
self.solution = [
|
self.solution = [
|
||||||
item for group, group_vars in zip(self.components, self.variables) for item, var in zip(group, group_vars)
|
item for group, group_vars in zip(self.components, self.variables) for item, var in zip(group, group_vars)
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ class GainOptimiser:
|
||||||
:param components: List of components, where each component is a dictionary with keys "id", "cost" and "gain"
|
: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_cost: Maximum cost constraint
|
||||||
:param max_gain: Maximum gain constraint
|
:param max_gain: Maximum gain constraint
|
||||||
:param allow_slack: If True, allows the model to use slack variables to relax the cost constraint if the model
|
:param allow_slack: If True, and the solution is infeasible, allows the model to use slack variables to relax
|
||||||
is infeasible. Defaults to True.
|
the cost constraint if the model. Defaults to True.
|
||||||
:param verbose: If True, enables verbose logging
|
:param verbose: If True, enables verbose logging
|
||||||
"""
|
"""
|
||||||
self.components = components
|
self.components = components
|
||||||
|
|
@ -86,6 +86,9 @@ class GainOptimiser:
|
||||||
for group_vars in self.variables:
|
for group_vars in self.variables:
|
||||||
self.m += xsum(var for var in group_vars) <= 1
|
self.m += xsum(var for var in group_vars) <= 1
|
||||||
|
|
||||||
|
self.m.max_gap = 0
|
||||||
|
self.m.integer_tol = 1e-9
|
||||||
|
|
||||||
def setup_slack(self):
|
def setup_slack(self):
|
||||||
# Remove the original cost constraint
|
# Remove the original cost constraint
|
||||||
self.m.remove(self.cost_constraint)
|
self.m.remove(self.cost_constraint)
|
||||||
|
|
@ -148,5 +151,5 @@ class GainOptimiser:
|
||||||
|
|
||||||
self.solution = solution
|
self.solution = solution
|
||||||
|
|
||||||
self.solution_gain = self.m.objective.x
|
self.solution_gain = sum(component['gain'] for component in self.solution)
|
||||||
self.solution_cost = sum([component['cost'] for component in self.solution])
|
self.solution_cost = sum([component['cost'] for component in self.solution])
|
||||||
|
|
|
||||||
175
recommendations/optimiser/StrategicOptimiser.py
Normal file
175
recommendations/optimiser/StrategicOptimiser.py
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
from enum import Enum
|
||||||
|
from mip import OptimizationStatus
|
||||||
|
from typing import Sequence, Optional, TypedDict, List
|
||||||
|
from recommendations.optimiser.CostOptimiser import CostOptimiser
|
||||||
|
from recommendations.optimiser.GainOptimiser import GainOptimiser
|
||||||
|
|
||||||
|
|
||||||
|
class Measure(TypedDict):
|
||||||
|
id: str
|
||||||
|
cost: float
|
||||||
|
gain: float
|
||||||
|
|
||||||
|
|
||||||
|
class Strategies(Enum):
|
||||||
|
CASE_1_TRY_MIN_COST_WITH_CONSTRAINTS = "case_1_try_min_cost_with_constraints"
|
||||||
|
CASE_1_SOLVE_MAX_GAIN_UNDER_BUDGET = "case_1_solve_max_gain_under_budget"
|
||||||
|
CASE_2_SOLVE_MAX_GAIN_UNDER_BUDGET = "case_2_solve_max_gain_under_budget"
|
||||||
|
CASE_3_SOLVE_MIN_COST_FOR_TARGET = "case_3_solve_min_cost_for_target"
|
||||||
|
|
||||||
|
|
||||||
|
class StrategicOptimiser:
|
||||||
|
"""
|
||||||
|
Domain-level optimiser implementing logical optimisation logic.
|
||||||
|
|
||||||
|
Behaviour:
|
||||||
|
|
||||||
|
1) If both budget and target_gain are provided:
|
||||||
|
- Minimise cost subject to:
|
||||||
|
gain >= target_gain
|
||||||
|
cost <= budget
|
||||||
|
- If infeasible:
|
||||||
|
maximise gain subject to cost <= budget
|
||||||
|
|
||||||
|
2) If only budget is provided:
|
||||||
|
- Maximise gain under budget
|
||||||
|
|
||||||
|
3) If only target_gain is provided:
|
||||||
|
- Minimise cost to achieve gain
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
components: Sequence[Sequence[Measure]],
|
||||||
|
budget: Optional[float] = None,
|
||||||
|
target_gain: Optional[float] = None,
|
||||||
|
verbose: bool = False,
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
if not components:
|
||||||
|
raise ValueError("Components cannot be empty.")
|
||||||
|
|
||||||
|
if budget is None and target_gain is None:
|
||||||
|
raise ValueError("At least one of budget or target_gain must be provided.")
|
||||||
|
|
||||||
|
self.components = components
|
||||||
|
self.budget = budget
|
||||||
|
self.target_gain = target_gain
|
||||||
|
self.verbose = verbose
|
||||||
|
|
||||||
|
self.solution: List[Measure] = []
|
||||||
|
self.solution_cost: float = 0.0
|
||||||
|
self.solution_gain: float = 0.0
|
||||||
|
|
||||||
|
# For debugging purposes, we keep a record of which option was selected
|
||||||
|
self.strategy_used: Optional[Strategies] = None
|
||||||
|
|
||||||
|
def solve(self) -> None:
|
||||||
|
"""
|
||||||
|
Primary entry point for solving the optimisation problem based on the provided budget and target gain.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Case 1: budget + target
|
||||||
|
if self.budget is not None and self.target_gain is not None:
|
||||||
|
# Given:
|
||||||
|
# Budget B
|
||||||
|
# Target gain G
|
||||||
|
#
|
||||||
|
# We want the solution to:
|
||||||
|
#
|
||||||
|
# Primary problem (P1)
|
||||||
|
# min cost
|
||||||
|
# subject to
|
||||||
|
#
|
||||||
|
# gain >= 𝐺
|
||||||
|
# cost <= B
|
||||||
|
# multiple-choice constraints
|
||||||
|
#
|
||||||
|
# If (P1) is feasible → that solution is exactly what you want.
|
||||||
|
# If (P1) is infeasible → solve the following problem (P2):
|
||||||
|
#
|
||||||
|
# max gain
|
||||||
|
# subject to
|
||||||
|
#
|
||||||
|
# cost <= B
|
||||||
|
if self._try_min_cost_with_constraints():
|
||||||
|
# Keep a record of the strategy used to solve the problem, for debugging purposes
|
||||||
|
self.strategy_used = Strategies.CASE_1_TRY_MIN_COST_WITH_CONSTRAINTS
|
||||||
|
return
|
||||||
|
self._solve_max_gain_under_budget()
|
||||||
|
self.strategy_used = Strategies.CASE_1_SOLVE_MAX_GAIN_UNDER_BUDGET
|
||||||
|
return
|
||||||
|
|
||||||
|
# Case 2: budget only
|
||||||
|
if self.budget is not None:
|
||||||
|
self._solve_max_gain_under_budget()
|
||||||
|
self.strategy_used = Strategies.CASE_2_SOLVE_MAX_GAIN_UNDER_BUDGET
|
||||||
|
return
|
||||||
|
|
||||||
|
# Case 3: target only
|
||||||
|
self._solve_min_cost_for_target()
|
||||||
|
self.strategy_used = Strategies.CASE_3_SOLVE_MIN_COST_FOR_TARGET
|
||||||
|
return
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# Internal Functions
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
|
||||||
|
def _try_min_cost_with_constraints(self) -> bool:
|
||||||
|
"""
|
||||||
|
Try to minimise cost while satisfying:
|
||||||
|
gain >= target_gain
|
||||||
|
cost <= budget
|
||||||
|
"""
|
||||||
|
|
||||||
|
opt = CostOptimiser(
|
||||||
|
self.components,
|
||||||
|
min_gain=self.target_gain,
|
||||||
|
verbose=self.verbose,
|
||||||
|
allow_slack=False
|
||||||
|
)
|
||||||
|
|
||||||
|
opt.setup()
|
||||||
|
opt.add_budget_constraint(self.budget)
|
||||||
|
opt.solve()
|
||||||
|
|
||||||
|
if opt.m.status == OptimizationStatus.INFEASIBLE:
|
||||||
|
return False
|
||||||
|
|
||||||
|
self._store_solution(opt.solution)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _solve_max_gain_under_budget(self) -> None:
|
||||||
|
|
||||||
|
opt = GainOptimiser(
|
||||||
|
self.components,
|
||||||
|
max_cost=self.budget,
|
||||||
|
max_gain=None,
|
||||||
|
allow_slack=False,
|
||||||
|
verbose=self.verbose
|
||||||
|
)
|
||||||
|
|
||||||
|
opt.setup()
|
||||||
|
opt.solve()
|
||||||
|
|
||||||
|
self._store_solution(opt.solution)
|
||||||
|
|
||||||
|
def _solve_min_cost_for_target(self) -> None:
|
||||||
|
|
||||||
|
opt = CostOptimiser(
|
||||||
|
self.components,
|
||||||
|
min_gain=self.target_gain,
|
||||||
|
verbose=self.verbose
|
||||||
|
)
|
||||||
|
|
||||||
|
opt.setup()
|
||||||
|
opt.solve()
|
||||||
|
|
||||||
|
self._store_solution(opt.solution)
|
||||||
|
|
||||||
|
def _store_solution(self, solution: List[Measure]) -> None:
|
||||||
|
self.solution = solution
|
||||||
|
self.solution_cost = sum(m["cost"] for m in solution)
|
||||||
|
self.solution_gain = sum(m["gain"] for m in solution)
|
||||||
|
|
@ -1119,6 +1119,7 @@ def run_optimizer(
|
||||||
return None, 0.0, 0.0
|
return None, 0.0, 0.0
|
||||||
|
|
||||||
if budget is not None:
|
if budget is not None:
|
||||||
|
|
||||||
opt = GainOptimiser(
|
opt = GainOptimiser(
|
||||||
input_measures, max_cost=budget, max_gain=0 if sub_target_gain == 0 else (sub_target_gain or float("inf")),
|
input_measures, max_cost=budget, max_gain=0 if sub_target_gain == 0 else (sub_target_gain or float("inf")),
|
||||||
allow_slack=allow_slack
|
allow_slack=allow_slack
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ from recommendations.tests.test_data.measures_to_optimise import measures_to_opt
|
||||||
from recommendations.optimiser import optimiser_functions
|
from recommendations.optimiser import optimiser_functions
|
||||||
from recommendations.optimiser.GainOptimiser import GainOptimiser
|
from recommendations.optimiser.GainOptimiser import GainOptimiser
|
||||||
from recommendations.optimiser.CostOptimiser import CostOptimiser
|
from recommendations.optimiser.CostOptimiser import CostOptimiser
|
||||||
|
from recommendations.optimiser.StrategicOptimiser import StrategicOptimiser, Strategies
|
||||||
|
|
||||||
|
|
||||||
class TestPrepareInputMeasures:
|
class TestPrepareInputMeasures:
|
||||||
|
|
@ -287,3 +288,728 @@ class TestIncreasingEpcE2e:
|
||||||
# We don't add ventilation as major insulation work isn't done
|
# We don't add ventilation as major insulation work isn't done
|
||||||
ventilation_added = any(rec["recommendation_id"] == "3_phase=2" and rec["default"] for rec in flattened)
|
ventilation_added = any(rec["recommendation_id"] == "3_phase=2" and rec["default"] for rec in flattened)
|
||||||
assert not ventilation_added, "Ventilation should not be added without major insulation work"
|
assert not ventilation_added, "Ventilation should not be added without major insulation work"
|
||||||
|
|
||||||
|
|
||||||
|
class TestStrategicOptimiser:
|
||||||
|
def test_budget_and_target_gain(self):
|
||||||
|
components = [
|
||||||
|
[
|
||||||
|
{'id': '0_phase=0', 'cost': 819.0, 'gain': 5.6, 'type': 'loft_insulation', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 819.0, 'raw_cost': 819.0, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False,
|
||||||
|
'has_battery': False, 'array_size': 0},
|
||||||
|
{'id': '1_phase=0', 'cost': 702.0, 'gain': 5.6, 'type': 'loft_insulation', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 702.0, 'raw_cost': 702.0, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False,
|
||||||
|
'has_battery': False, 'array_size': 0},
|
||||||
|
{'id': '2_phase=0', 'cost': 585.0, 'gain': 5.6, 'type': 'loft_insulation', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 585.0, 'raw_cost': 585.0, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False,
|
||||||
|
'has_battery': False, 'array_size': 0}],
|
||||||
|
[{'id': '4_phase=2', 'cost': 3656.25, 'gain': 2.0, 'type': 'suspended_floor_insulation',
|
||||||
|
'innovation_uplift': 0, 'cost_minus_uplift': 3656.25, 'raw_cost': 3656.25, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False, 'has_battery': False,
|
||||||
|
'array_size': 0}],
|
||||||
|
[{'id': '5_phase=3', 'cost': 17.5, 'gain': 1.0, 'type': 'low_energy_lighting', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 17.5, 'raw_cost': 17.5, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 0}],
|
||||||
|
[{'id': '6_phase=4', 'cost': 140, 'gain': 3.4, 'type': 'roomstat_programmer_trvs', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 140, 'raw_cost': 140, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 0},
|
||||||
|
{'id': '7_phase=4', 'cost': 874.5680000000001, 'gain': 4.2, 'type': 'time_temperature_zone_control',
|
||||||
|
'innovation_uplift': 0, 'cost_minus_uplift': 874.5680000000001, 'raw_cost': 874.5680000000001,
|
||||||
|
'partial_project_funding': 0, 'partial_project_score': 0, 'uplift_project_score': 0,
|
||||||
|
'already_installed': False, 'has_battery': False, 'array_size': 0}],
|
||||||
|
[{'id': '9_phase=6', 'cost': 5420.0, 'gain': 13.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5420.0, 'raw_cost': 5420.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.6},
|
||||||
|
{'id': '10_phase=6', 'cost': 6210.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6210.0, 'raw_cost': 6210.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.6,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '11_phase=6', 'cost': 6820.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6820.0, 'raw_cost': 6820.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.6,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '12_phase=6', 'cost': 7202.0, 'gain': 14.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7202.0, 'raw_cost': 7202.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.915},
|
||||||
|
{'id': '13_phase=6', 'cost': 6495.0, 'gain': 14.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6495.0, 'raw_cost': 6495.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.92},
|
||||||
|
{'id': '14_phase=6', 'cost': 7285.0, 'gain': 17.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7285.0, 'raw_cost': 7285.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.92,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '15_phase=6', 'cost': 7895.0, 'gain': 17.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7895.0, 'raw_cost': 7895.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.92,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '16_phase=6', 'cost': 5520.0, 'gain': 15.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5520.0, 'raw_cost': 5520.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 4.0},
|
||||||
|
{'id': '17_phase=6', 'cost': 6310.0, 'gain': 18.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6310.0, 'raw_cost': 6310.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 4.0,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '18_phase=6', 'cost': 6920.0, 'gain': 18.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6920.0, 'raw_cost': 6920.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 4.0,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '19_phase=6', 'cost': 5320.0, 'gain': 12.1, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5320.0, 'raw_cost': 5320.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.2},
|
||||||
|
{'id': '20_phase=6', 'cost': 6110.0, 'gain': 14.1, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6110.0, 'raw_cost': 6110.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.2,
|
||||||
|
'battery_gain': 2},
|
||||||
|
{'id': '21_phase=6', 'cost': 6720.0, 'gain': 14.1, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6720.0, 'raw_cost': 6720.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.2,
|
||||||
|
'battery_gain': 2},
|
||||||
|
{'id': '22_phase=6', 'cost': 6932.0, 'gain': 13.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6932.0, 'raw_cost': 6932.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.48},
|
||||||
|
{'id': '23_phase=6', 'cost': 6295.0, 'gain': 13.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6295.0, 'raw_cost': 6295.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.48},
|
||||||
|
{'id': '24_phase=6', 'cost': 7085.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7085.0, 'raw_cost': 7085.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.48,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '25_phase=6', 'cost': 7695.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7695.0, 'raw_cost': 7695.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.48,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '26_phase=6', 'cost': 5220.0, 'gain': 10.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5220.0, 'raw_cost': 5220.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.8},
|
||||||
|
{'id': '27_phase=6', 'cost': 6662.0, 'gain': 12.3, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6662.0, 'raw_cost': 6662.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.045},
|
||||||
|
{'id': '28_phase=6', 'cost': 6095.0, 'gain': 12.3, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6095.0, 'raw_cost': 6095.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.05},
|
||||||
|
{'id': '29_phase=6', 'cost': 5160.0, 'gain': 9.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5160.0, 'raw_cost': 5160.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.4},
|
||||||
|
{'id': '30_phase=6', 'cost': 6392.0, 'gain': 10.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6392.0, 'raw_cost': 6392.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.61},
|
||||||
|
{'id': '31_phase=6', 'cost': 5910.0, 'gain': 10.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5910.0, 'raw_cost': 5910.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.61},
|
||||||
|
{'id': '32_phase=6', 'cost': 5100.0, 'gain': 8.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5100.0, 'raw_cost': 5100.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.0},
|
||||||
|
{'id': '33_phase=6', 'cost': 6098.0, 'gain': 8.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6098.0, 'raw_cost': 6098.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.175},
|
||||||
|
{'id': '34_phase=6', 'cost': 5725.0, 'gain': 8.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5725.0, 'raw_cost': 5725.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.18},
|
||||||
|
{'id': '35_phase=6', 'cost': 5040.0, 'gain': 6.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5040.0, 'raw_cost': 5040.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 1.6},
|
||||||
|
{'id': '36_phase=6', 'cost': 5828.0, 'gain': 7.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5828.0, 'raw_cost': 5828.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 1.74},
|
||||||
|
{'id': '37_phase=6', 'cost': 5540.0, 'gain': 7.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5540.0, 'raw_cost': 5540.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 1.74}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
budget = 5000
|
||||||
|
target_gain = 11.5
|
||||||
|
|
||||||
|
opt = StrategicOptimiser(
|
||||||
|
components=components,
|
||||||
|
target_gain=target_gain,
|
||||||
|
budget=budget,
|
||||||
|
)
|
||||||
|
|
||||||
|
opt.solve()
|
||||||
|
|
||||||
|
# check strategy used
|
||||||
|
assert opt.strategy_used.value == "case_1_try_min_cost_with_constraints"
|
||||||
|
# Check the solution values
|
||||||
|
assert opt.solution_cost == 4398.75
|
||||||
|
assert opt.solution_gain == 12
|
||||||
|
|
||||||
|
def test_budget_and_target_gain_2(self):
|
||||||
|
components = [
|
||||||
|
[
|
||||||
|
{'id': '0_phase=0', 'cost': 819.0, 'gain': 5.6, 'type': 'loft_insulation', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 819.0, 'raw_cost': 819.0, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False,
|
||||||
|
'has_battery': False, 'array_size': 0},
|
||||||
|
{'id': '1_phase=0', 'cost': 702.0, 'gain': 5.6, 'type': 'loft_insulation', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 702.0, 'raw_cost': 702.0, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False,
|
||||||
|
'has_battery': False, 'array_size': 0},
|
||||||
|
{'id': '2_phase=0', 'cost': 585.0, 'gain': 5.6, 'type': 'loft_insulation', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 585.0, 'raw_cost': 585.0, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False,
|
||||||
|
'has_battery': False, 'array_size': 0}],
|
||||||
|
[{'id': '4_phase=2', 'cost': 3656.25, 'gain': 2.0, 'type': 'suspended_floor_insulation',
|
||||||
|
'innovation_uplift': 0, 'cost_minus_uplift': 3656.25, 'raw_cost': 3656.25, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False, 'has_battery': False,
|
||||||
|
'array_size': 0}],
|
||||||
|
[{'id': '5_phase=3', 'cost': 17.5, 'gain': 1.0, 'type': 'low_energy_lighting', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 17.5, 'raw_cost': 17.5, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 0}],
|
||||||
|
[{'id': '6_phase=4', 'cost': 140, 'gain': 3.4, 'type': 'roomstat_programmer_trvs', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 140, 'raw_cost': 140, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 0},
|
||||||
|
{'id': '7_phase=4', 'cost': 874.5680000000001, 'gain': 4.2, 'type': 'time_temperature_zone_control',
|
||||||
|
'innovation_uplift': 0, 'cost_minus_uplift': 874.5680000000001, 'raw_cost': 874.5680000000001,
|
||||||
|
'partial_project_funding': 0, 'partial_project_score': 0, 'uplift_project_score': 0,
|
||||||
|
'already_installed': False, 'has_battery': False, 'array_size': 0}],
|
||||||
|
[{'id': '9_phase=6', 'cost': 5420.0, 'gain': 13.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5420.0, 'raw_cost': 5420.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.6},
|
||||||
|
{'id': '10_phase=6', 'cost': 6210.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6210.0, 'raw_cost': 6210.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.6,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '11_phase=6', 'cost': 6820.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6820.0, 'raw_cost': 6820.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.6,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '12_phase=6', 'cost': 7202.0, 'gain': 14.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7202.0, 'raw_cost': 7202.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.915},
|
||||||
|
{'id': '13_phase=6', 'cost': 6495.0, 'gain': 14.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6495.0, 'raw_cost': 6495.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.92},
|
||||||
|
{'id': '14_phase=6', 'cost': 7285.0, 'gain': 17.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7285.0, 'raw_cost': 7285.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.92,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '15_phase=6', 'cost': 7895.0, 'gain': 17.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7895.0, 'raw_cost': 7895.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.92,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '16_phase=6', 'cost': 5520.0, 'gain': 15.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5520.0, 'raw_cost': 5520.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 4.0},
|
||||||
|
{'id': '17_phase=6', 'cost': 6310.0, 'gain': 18.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6310.0, 'raw_cost': 6310.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 4.0,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '18_phase=6', 'cost': 6920.0, 'gain': 18.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6920.0, 'raw_cost': 6920.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 4.0,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '19_phase=6', 'cost': 5320.0, 'gain': 12.1, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5320.0, 'raw_cost': 5320.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.2},
|
||||||
|
{'id': '20_phase=6', 'cost': 6110.0, 'gain': 14.1, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6110.0, 'raw_cost': 6110.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.2,
|
||||||
|
'battery_gain': 2},
|
||||||
|
{'id': '21_phase=6', 'cost': 6720.0, 'gain': 14.1, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6720.0, 'raw_cost': 6720.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.2,
|
||||||
|
'battery_gain': 2},
|
||||||
|
{'id': '22_phase=6', 'cost': 6932.0, 'gain': 13.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6932.0, 'raw_cost': 6932.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.48},
|
||||||
|
{'id': '23_phase=6', 'cost': 6295.0, 'gain': 13.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6295.0, 'raw_cost': 6295.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.48},
|
||||||
|
{'id': '24_phase=6', 'cost': 7085.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7085.0, 'raw_cost': 7085.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.48,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '25_phase=6', 'cost': 7695.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7695.0, 'raw_cost': 7695.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.48,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '26_phase=6', 'cost': 5220.0, 'gain': 10.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5220.0, 'raw_cost': 5220.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.8},
|
||||||
|
{'id': '27_phase=6', 'cost': 6662.0, 'gain': 12.3, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6662.0, 'raw_cost': 6662.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.045},
|
||||||
|
{'id': '28_phase=6', 'cost': 6095.0, 'gain': 12.3, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6095.0, 'raw_cost': 6095.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.05},
|
||||||
|
{'id': '29_phase=6', 'cost': 5160.0, 'gain': 9.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5160.0, 'raw_cost': 5160.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.4},
|
||||||
|
{'id': '30_phase=6', 'cost': 6392.0, 'gain': 10.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6392.0, 'raw_cost': 6392.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.61},
|
||||||
|
{'id': '31_phase=6', 'cost': 5910.0, 'gain': 10.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5910.0, 'raw_cost': 5910.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.61},
|
||||||
|
{'id': '32_phase=6', 'cost': 5100.0, 'gain': 8.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5100.0, 'raw_cost': 5100.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.0},
|
||||||
|
{'id': '33_phase=6', 'cost': 6098.0, 'gain': 8.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6098.0, 'raw_cost': 6098.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.175},
|
||||||
|
{'id': '34_phase=6', 'cost': 5725.0, 'gain': 8.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5725.0, 'raw_cost': 5725.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.18},
|
||||||
|
{'id': '35_phase=6', 'cost': 5040.0, 'gain': 6.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5040.0, 'raw_cost': 5040.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 1.6},
|
||||||
|
{'id': '36_phase=6', 'cost': 5828.0, 'gain': 7.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5828.0, 'raw_cost': 5828.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 1.74},
|
||||||
|
{'id': '37_phase=6', 'cost': 5540.0, 'gain': 7.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5540.0, 'raw_cost': 5540.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 1.74}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
budget = 4000
|
||||||
|
target_gain = 11.5
|
||||||
|
|
||||||
|
opt = StrategicOptimiser(
|
||||||
|
components=components,
|
||||||
|
target_gain=target_gain,
|
||||||
|
budget=budget,
|
||||||
|
)
|
||||||
|
|
||||||
|
opt.solve()
|
||||||
|
|
||||||
|
# We expect to use case 1, but we won't be able to meet the target gain, so we should get the best solution
|
||||||
|
# possible within the budget. We end up with an infeasible solution when we try
|
||||||
|
# case_1_try_min_cost_with_constraints
|
||||||
|
assert opt.strategy_used.value == "case_1_solve_max_gain_under_budget"
|
||||||
|
assert opt.solution_cost == 1477.0680000000002
|
||||||
|
assert opt.solution_gain == 10.8
|
||||||
|
|
||||||
|
def test_just_gain(self):
|
||||||
|
components = [
|
||||||
|
[
|
||||||
|
{'id': '0_phase=0', 'cost': 819.0, 'gain': 5.6, 'type': 'loft_insulation', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 819.0, 'raw_cost': 819.0, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False,
|
||||||
|
'has_battery': False, 'array_size': 0},
|
||||||
|
{'id': '1_phase=0', 'cost': 702.0, 'gain': 5.6, 'type': 'loft_insulation', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 702.0, 'raw_cost': 702.0, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False,
|
||||||
|
'has_battery': False, 'array_size': 0},
|
||||||
|
{'id': '2_phase=0', 'cost': 585.0, 'gain': 5.6, 'type': 'loft_insulation', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 585.0, 'raw_cost': 585.0, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False,
|
||||||
|
'has_battery': False, 'array_size': 0}],
|
||||||
|
[{'id': '4_phase=2', 'cost': 3656.25, 'gain': 2.0, 'type': 'suspended_floor_insulation',
|
||||||
|
'innovation_uplift': 0, 'cost_minus_uplift': 3656.25, 'raw_cost': 3656.25, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False, 'has_battery': False,
|
||||||
|
'array_size': 0}],
|
||||||
|
[{'id': '5_phase=3', 'cost': 17.5, 'gain': 1.0, 'type': 'low_energy_lighting', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 17.5, 'raw_cost': 17.5, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 0}],
|
||||||
|
[{'id': '6_phase=4', 'cost': 140, 'gain': 3.4, 'type': 'roomstat_programmer_trvs', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 140, 'raw_cost': 140, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 0},
|
||||||
|
{'id': '7_phase=4', 'cost': 874.5680000000001, 'gain': 4.2, 'type': 'time_temperature_zone_control',
|
||||||
|
'innovation_uplift': 0, 'cost_minus_uplift': 874.5680000000001, 'raw_cost': 874.5680000000001,
|
||||||
|
'partial_project_funding': 0, 'partial_project_score': 0, 'uplift_project_score': 0,
|
||||||
|
'already_installed': False, 'has_battery': False, 'array_size': 0}],
|
||||||
|
[{'id': '9_phase=6', 'cost': 5420.0, 'gain': 13.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5420.0, 'raw_cost': 5420.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.6},
|
||||||
|
{'id': '10_phase=6', 'cost': 6210.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6210.0, 'raw_cost': 6210.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.6,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '11_phase=6', 'cost': 6820.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6820.0, 'raw_cost': 6820.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.6,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '12_phase=6', 'cost': 7202.0, 'gain': 14.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7202.0, 'raw_cost': 7202.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.915},
|
||||||
|
{'id': '13_phase=6', 'cost': 6495.0, 'gain': 14.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6495.0, 'raw_cost': 6495.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.92},
|
||||||
|
{'id': '14_phase=6', 'cost': 7285.0, 'gain': 17.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7285.0, 'raw_cost': 7285.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.92,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '15_phase=6', 'cost': 7895.0, 'gain': 17.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7895.0, 'raw_cost': 7895.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.92,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '16_phase=6', 'cost': 5520.0, 'gain': 15.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5520.0, 'raw_cost': 5520.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 4.0},
|
||||||
|
{'id': '17_phase=6', 'cost': 6310.0, 'gain': 18.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6310.0, 'raw_cost': 6310.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 4.0,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '18_phase=6', 'cost': 6920.0, 'gain': 18.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6920.0, 'raw_cost': 6920.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 4.0,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '19_phase=6', 'cost': 5320.0, 'gain': 12.1, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5320.0, 'raw_cost': 5320.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.2},
|
||||||
|
{'id': '20_phase=6', 'cost': 6110.0, 'gain': 14.1, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6110.0, 'raw_cost': 6110.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.2,
|
||||||
|
'battery_gain': 2},
|
||||||
|
{'id': '21_phase=6', 'cost': 6720.0, 'gain': 14.1, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6720.0, 'raw_cost': 6720.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.2,
|
||||||
|
'battery_gain': 2},
|
||||||
|
{'id': '22_phase=6', 'cost': 6932.0, 'gain': 13.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6932.0, 'raw_cost': 6932.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.48},
|
||||||
|
{'id': '23_phase=6', 'cost': 6295.0, 'gain': 13.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6295.0, 'raw_cost': 6295.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.48},
|
||||||
|
{'id': '24_phase=6', 'cost': 7085.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7085.0, 'raw_cost': 7085.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.48,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '25_phase=6', 'cost': 7695.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7695.0, 'raw_cost': 7695.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.48,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '26_phase=6', 'cost': 5220.0, 'gain': 10.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5220.0, 'raw_cost': 5220.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.8},
|
||||||
|
{'id': '27_phase=6', 'cost': 6662.0, 'gain': 12.3, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6662.0, 'raw_cost': 6662.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.045},
|
||||||
|
{'id': '28_phase=6', 'cost': 6095.0, 'gain': 12.3, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6095.0, 'raw_cost': 6095.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.05},
|
||||||
|
{'id': '29_phase=6', 'cost': 5160.0, 'gain': 9.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5160.0, 'raw_cost': 5160.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.4},
|
||||||
|
{'id': '30_phase=6', 'cost': 6392.0, 'gain': 10.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6392.0, 'raw_cost': 6392.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.61},
|
||||||
|
{'id': '31_phase=6', 'cost': 5910.0, 'gain': 10.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5910.0, 'raw_cost': 5910.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.61},
|
||||||
|
{'id': '32_phase=6', 'cost': 5100.0, 'gain': 8.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5100.0, 'raw_cost': 5100.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.0},
|
||||||
|
{'id': '33_phase=6', 'cost': 6098.0, 'gain': 8.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6098.0, 'raw_cost': 6098.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.175},
|
||||||
|
{'id': '34_phase=6', 'cost': 5725.0, 'gain': 8.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5725.0, 'raw_cost': 5725.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.18},
|
||||||
|
{'id': '35_phase=6', 'cost': 5040.0, 'gain': 6.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5040.0, 'raw_cost': 5040.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 1.6},
|
||||||
|
{'id': '36_phase=6', 'cost': 5828.0, 'gain': 7.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5828.0, 'raw_cost': 5828.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 1.74},
|
||||||
|
{'id': '37_phase=6', 'cost': 5540.0, 'gain': 7.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5540.0, 'raw_cost': 5540.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 1.74}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
budget = None
|
||||||
|
target_gain = 11.5
|
||||||
|
|
||||||
|
opt = StrategicOptimiser(
|
||||||
|
components=components,
|
||||||
|
target_gain=target_gain,
|
||||||
|
budget=budget,
|
||||||
|
)
|
||||||
|
|
||||||
|
opt.solve()
|
||||||
|
|
||||||
|
# Should be case 3 - minimise cost for target gain
|
||||||
|
assert opt.strategy_used.value == "case_3_solve_min_cost_for_target"
|
||||||
|
assert opt.solution_cost == 4398.75
|
||||||
|
assert opt.solution_gain == 12
|
||||||
|
|
||||||
|
def test_just_gain2(self):
|
||||||
|
components = [
|
||||||
|
[
|
||||||
|
{'id': '0_phase=0', 'cost': 819.0, 'gain': 5.6, 'type': 'loft_insulation', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 819.0, 'raw_cost': 819.0, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False,
|
||||||
|
'has_battery': False, 'array_size': 0},
|
||||||
|
{'id': '1_phase=0', 'cost': 702.0, 'gain': 5.6, 'type': 'loft_insulation', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 702.0, 'raw_cost': 702.0, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False,
|
||||||
|
'has_battery': False, 'array_size': 0},
|
||||||
|
{'id': '2_phase=0', 'cost': 585.0, 'gain': 5.6, 'type': 'loft_insulation', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 585.0, 'raw_cost': 585.0, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False,
|
||||||
|
'has_battery': False, 'array_size': 0}],
|
||||||
|
[{'id': '4_phase=2', 'cost': 3656.25, 'gain': 2.0, 'type': 'suspended_floor_insulation',
|
||||||
|
'innovation_uplift': 0, 'cost_minus_uplift': 3656.25, 'raw_cost': 3656.25, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False, 'has_battery': False,
|
||||||
|
'array_size': 0}],
|
||||||
|
[{'id': '5_phase=3', 'cost': 17.5, 'gain': 1.0, 'type': 'low_energy_lighting', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 17.5, 'raw_cost': 17.5, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 0}],
|
||||||
|
[{'id': '6_phase=4', 'cost': 140, 'gain': 3.4, 'type': 'roomstat_programmer_trvs', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 140, 'raw_cost': 140, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 0},
|
||||||
|
{'id': '7_phase=4', 'cost': 874.5680000000001, 'gain': 4.2, 'type': 'time_temperature_zone_control',
|
||||||
|
'innovation_uplift': 0, 'cost_minus_uplift': 874.5680000000001, 'raw_cost': 874.5680000000001,
|
||||||
|
'partial_project_funding': 0, 'partial_project_score': 0, 'uplift_project_score': 0,
|
||||||
|
'already_installed': False, 'has_battery': False, 'array_size': 0}],
|
||||||
|
[{'id': '9_phase=6', 'cost': 5420.0, 'gain': 13.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5420.0, 'raw_cost': 5420.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.6},
|
||||||
|
{'id': '10_phase=6', 'cost': 6210.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6210.0, 'raw_cost': 6210.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.6,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '11_phase=6', 'cost': 6820.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6820.0, 'raw_cost': 6820.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.6,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '12_phase=6', 'cost': 7202.0, 'gain': 14.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7202.0, 'raw_cost': 7202.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.915},
|
||||||
|
{'id': '13_phase=6', 'cost': 6495.0, 'gain': 14.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6495.0, 'raw_cost': 6495.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.92},
|
||||||
|
{'id': '14_phase=6', 'cost': 7285.0, 'gain': 17.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7285.0, 'raw_cost': 7285.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.92,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '15_phase=6', 'cost': 7895.0, 'gain': 17.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7895.0, 'raw_cost': 7895.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.92,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '16_phase=6', 'cost': 5520.0, 'gain': 15.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5520.0, 'raw_cost': 5520.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 4.0},
|
||||||
|
{'id': '17_phase=6', 'cost': 6310.0, 'gain': 18.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6310.0, 'raw_cost': 6310.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 4.0,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '18_phase=6', 'cost': 6920.0, 'gain': 18.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6920.0, 'raw_cost': 6920.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 4.0,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '19_phase=6', 'cost': 5320.0, 'gain': 12.1, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5320.0, 'raw_cost': 5320.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.2},
|
||||||
|
{'id': '20_phase=6', 'cost': 6110.0, 'gain': 14.1, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6110.0, 'raw_cost': 6110.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.2,
|
||||||
|
'battery_gain': 2},
|
||||||
|
{'id': '21_phase=6', 'cost': 6720.0, 'gain': 14.1, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6720.0, 'raw_cost': 6720.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.2,
|
||||||
|
'battery_gain': 2},
|
||||||
|
{'id': '22_phase=6', 'cost': 6932.0, 'gain': 13.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6932.0, 'raw_cost': 6932.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.48},
|
||||||
|
{'id': '23_phase=6', 'cost': 6295.0, 'gain': 13.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6295.0, 'raw_cost': 6295.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.48},
|
||||||
|
{'id': '24_phase=6', 'cost': 7085.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7085.0, 'raw_cost': 7085.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.48,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '25_phase=6', 'cost': 7695.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7695.0, 'raw_cost': 7695.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.48,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '26_phase=6', 'cost': 5220.0, 'gain': 10.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5220.0, 'raw_cost': 5220.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.8},
|
||||||
|
{'id': '27_phase=6', 'cost': 6662.0, 'gain': 12.3, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6662.0, 'raw_cost': 6662.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.045},
|
||||||
|
{'id': '28_phase=6', 'cost': 6095.0, 'gain': 12.3, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6095.0, 'raw_cost': 6095.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.05},
|
||||||
|
{'id': '29_phase=6', 'cost': 5160.0, 'gain': 9.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5160.0, 'raw_cost': 5160.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.4},
|
||||||
|
{'id': '30_phase=6', 'cost': 6392.0, 'gain': 10.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6392.0, 'raw_cost': 6392.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.61},
|
||||||
|
{'id': '31_phase=6', 'cost': 5910.0, 'gain': 10.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5910.0, 'raw_cost': 5910.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.61},
|
||||||
|
{'id': '32_phase=6', 'cost': 5100.0, 'gain': 8.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5100.0, 'raw_cost': 5100.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.0},
|
||||||
|
{'id': '33_phase=6', 'cost': 6098.0, 'gain': 8.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6098.0, 'raw_cost': 6098.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.175},
|
||||||
|
{'id': '34_phase=6', 'cost': 5725.0, 'gain': 8.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5725.0, 'raw_cost': 5725.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.18},
|
||||||
|
{'id': '35_phase=6', 'cost': 5040.0, 'gain': 6.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5040.0, 'raw_cost': 5040.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 1.6},
|
||||||
|
{'id': '36_phase=6', 'cost': 5828.0, 'gain': 7.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5828.0, 'raw_cost': 5828.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 1.74},
|
||||||
|
{'id': '37_phase=6', 'cost': 5540.0, 'gain': 7.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5540.0, 'raw_cost': 5540.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 1.74}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
budget = None
|
||||||
|
target_gain = 20
|
||||||
|
|
||||||
|
opt = StrategicOptimiser(
|
||||||
|
components=components,
|
||||||
|
target_gain=target_gain,
|
||||||
|
budget=budget,
|
||||||
|
)
|
||||||
|
|
||||||
|
opt.solve()
|
||||||
|
|
||||||
|
# Should be case 3 - minimise cost for target gain
|
||||||
|
assert opt.strategy_used.value == "case_3_solve_min_cost_for_target"
|
||||||
|
assert opt.solution_cost == 5962.5
|
||||||
|
assert opt.solution_gain == 20.2
|
||||||
|
|
||||||
|
def test_just_budget(self):
|
||||||
|
components = [
|
||||||
|
[
|
||||||
|
{'id': '0_phase=0', 'cost': 819.0, 'gain': 5.6, 'type': 'loft_insulation', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 819.0, 'raw_cost': 819.0, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False,
|
||||||
|
'has_battery': False, 'array_size': 0},
|
||||||
|
{'id': '1_phase=0', 'cost': 702.0, 'gain': 5.6, 'type': 'loft_insulation', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 702.0, 'raw_cost': 702.0, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False,
|
||||||
|
'has_battery': False, 'array_size': 0},
|
||||||
|
{'id': '2_phase=0', 'cost': 585.0, 'gain': 5.6, 'type': 'loft_insulation', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 585.0, 'raw_cost': 585.0, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False,
|
||||||
|
'has_battery': False, 'array_size': 0}],
|
||||||
|
[{'id': '4_phase=2', 'cost': 3656.25, 'gain': 2.0, 'type': 'suspended_floor_insulation',
|
||||||
|
'innovation_uplift': 0, 'cost_minus_uplift': 3656.25, 'raw_cost': 3656.25, 'partial_project_funding': 0,
|
||||||
|
'partial_project_score': 0, 'uplift_project_score': 0, 'already_installed': False, 'has_battery': False,
|
||||||
|
'array_size': 0}],
|
||||||
|
[{'id': '5_phase=3', 'cost': 17.5, 'gain': 1.0, 'type': 'low_energy_lighting', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 17.5, 'raw_cost': 17.5, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 0}],
|
||||||
|
[{'id': '6_phase=4', 'cost': 140, 'gain': 3.4, 'type': 'roomstat_programmer_trvs', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 140, 'raw_cost': 140, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 0},
|
||||||
|
{'id': '7_phase=4', 'cost': 874.5680000000001, 'gain': 4.2, 'type': 'time_temperature_zone_control',
|
||||||
|
'innovation_uplift': 0, 'cost_minus_uplift': 874.5680000000001, 'raw_cost': 874.5680000000001,
|
||||||
|
'partial_project_funding': 0, 'partial_project_score': 0, 'uplift_project_score': 0,
|
||||||
|
'already_installed': False, 'has_battery': False, 'array_size': 0}],
|
||||||
|
[{'id': '9_phase=6', 'cost': 5420.0, 'gain': 13.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5420.0, 'raw_cost': 5420.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.6},
|
||||||
|
{'id': '10_phase=6', 'cost': 6210.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6210.0, 'raw_cost': 6210.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.6,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '11_phase=6', 'cost': 6820.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6820.0, 'raw_cost': 6820.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.6,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '12_phase=6', 'cost': 7202.0, 'gain': 14.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7202.0, 'raw_cost': 7202.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.915},
|
||||||
|
{'id': '13_phase=6', 'cost': 6495.0, 'gain': 14.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6495.0, 'raw_cost': 6495.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.92},
|
||||||
|
{'id': '14_phase=6', 'cost': 7285.0, 'gain': 17.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7285.0, 'raw_cost': 7285.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.92,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '15_phase=6', 'cost': 7895.0, 'gain': 17.5, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7895.0, 'raw_cost': 7895.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.92,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '16_phase=6', 'cost': 5520.0, 'gain': 15.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5520.0, 'raw_cost': 5520.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 4.0},
|
||||||
|
{'id': '17_phase=6', 'cost': 6310.0, 'gain': 18.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6310.0, 'raw_cost': 6310.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 4.0,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '18_phase=6', 'cost': 6920.0, 'gain': 18.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6920.0, 'raw_cost': 6920.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 4.0,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '19_phase=6', 'cost': 5320.0, 'gain': 12.1, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5320.0, 'raw_cost': 5320.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.2},
|
||||||
|
{'id': '20_phase=6', 'cost': 6110.0, 'gain': 14.1, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6110.0, 'raw_cost': 6110.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.2,
|
||||||
|
'battery_gain': 2},
|
||||||
|
{'id': '21_phase=6', 'cost': 6720.0, 'gain': 14.1, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6720.0, 'raw_cost': 6720.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.2,
|
||||||
|
'battery_gain': 2},
|
||||||
|
{'id': '22_phase=6', 'cost': 6932.0, 'gain': 13.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6932.0, 'raw_cost': 6932.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.48},
|
||||||
|
{'id': '23_phase=6', 'cost': 6295.0, 'gain': 13.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6295.0, 'raw_cost': 6295.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.48},
|
||||||
|
{'id': '24_phase=6', 'cost': 7085.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7085.0, 'raw_cost': 7085.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.48,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '25_phase=6', 'cost': 7695.0, 'gain': 16.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 7695.0, 'raw_cost': 7695.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': True, 'array_size': 3.48,
|
||||||
|
'battery_gain': 3},
|
||||||
|
{'id': '26_phase=6', 'cost': 5220.0, 'gain': 10.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5220.0, 'raw_cost': 5220.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.8},
|
||||||
|
{'id': '27_phase=6', 'cost': 6662.0, 'gain': 12.3, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6662.0, 'raw_cost': 6662.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.045},
|
||||||
|
{'id': '28_phase=6', 'cost': 6095.0, 'gain': 12.3, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6095.0, 'raw_cost': 6095.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 3.05},
|
||||||
|
{'id': '29_phase=6', 'cost': 5160.0, 'gain': 9.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5160.0, 'raw_cost': 5160.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.4},
|
||||||
|
{'id': '30_phase=6', 'cost': 6392.0, 'gain': 10.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6392.0, 'raw_cost': 6392.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.61},
|
||||||
|
{'id': '31_phase=6', 'cost': 5910.0, 'gain': 10.2, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5910.0, 'raw_cost': 5910.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.61},
|
||||||
|
{'id': '32_phase=6', 'cost': 5100.0, 'gain': 8.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5100.0, 'raw_cost': 5100.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.0},
|
||||||
|
{'id': '33_phase=6', 'cost': 6098.0, 'gain': 8.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 6098.0, 'raw_cost': 6098.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.175},
|
||||||
|
{'id': '34_phase=6', 'cost': 5725.0, 'gain': 8.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5725.0, 'raw_cost': 5725.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 2.18},
|
||||||
|
{'id': '35_phase=6', 'cost': 5040.0, 'gain': 6.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5040.0, 'raw_cost': 5040.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 1.6},
|
||||||
|
{'id': '36_phase=6', 'cost': 5828.0, 'gain': 7.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5828.0, 'raw_cost': 5828.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 1.74},
|
||||||
|
{'id': '37_phase=6', 'cost': 5540.0, 'gain': 7.0, 'type': 'solar_pv', 'innovation_uplift': 0,
|
||||||
|
'cost_minus_uplift': 5540.0, 'raw_cost': 5540.0, 'partial_project_funding': 0, 'partial_project_score': 0,
|
||||||
|
'uplift_project_score': 0, 'already_installed': False, 'has_battery': False, 'array_size': 1.74}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
budget = 10000
|
||||||
|
target_gain = None
|
||||||
|
|
||||||
|
opt = StrategicOptimiser(
|
||||||
|
components=components,
|
||||||
|
target_gain=target_gain,
|
||||||
|
budget=budget,
|
||||||
|
)
|
||||||
|
|
||||||
|
opt.solve()
|
||||||
|
|
||||||
|
# Should be case 3 - minimise cost for target gain
|
||||||
|
assert opt.strategy_used.value == "case_2_solve_max_gain_under_budget"
|
||||||
|
assert opt.solution_cost == 7787.068
|
||||||
|
assert opt.solution_gain == 28.8
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue