From 4e76e61a2f22e93b1655cde1ffbdbe2789aa854d Mon Sep 17 00:00:00 2001 From: jnnr <32454596+jnnr@users.noreply.github.com> Date: Wed, 13 Oct 2021 18:37:25 +0200 Subject: [PATCH 1/6] Add test cases for storage investment --- tests/test_cases.py | 131 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 tests/test_cases.py diff --git a/tests/test_cases.py b/tests/test_cases.py new file mode 100644 index 0000000..47d6885 --- /dev/null +++ b/tests/test_cases.py @@ -0,0 +1,131 @@ +import pandas as pd +import numpy as np +from oemof.network import Node +from oemof.solph import Source, Sink, Bus, Flow, Model, EnergySystem +from oemof.outputlib import processing +from oemoflex import facades as oemoflex_facades +from oemof.tabular import facades as tabular_facades + +# Set up an energy system model +solver = "cbc" +periods = 100 +datetimeindex = pd.date_range("1/1/2019", periods=periods, freq="H") +demand_timeseries = np.zeros(periods) +demand_timeseries[-5:] = 1 +heat_feedin_timeseries = np.zeros(periods) +heat_feedin_timeseries[:10] = 1 + + +class TestCases: + def prepare_es(self): + self.energysystem = EnergySystem(timeindex=datetimeindex) + + Node.registry = self.energysystem + + bus_heat = Bus(label="bus_heat") + + Source( + label="heat_source", + outputs={ + bus_heat: Flow( + nominal_value=1, fixed=True, actual_value=heat_feedin_timeseries + ) + }, + ) + + Source( + label="shortage", outputs={bus_heat: Flow(variable_costs=1e6)} + ) + + Sink(label="excess", inputs={bus_heat: Flow()}) + + Sink( + label="heat_demand", + inputs={ + bus_heat: Flow( + nominal_value=1, fixed=True, actual_value=demand_timeseries + ) + }, + ) + + return bus_heat + + def run_model(self): + # Create and solve the optimization model + optimization_model = Model(self.energysystem) + + optimization_model.solve( + solver=solver, solve_kwargs={"tee": False, "keepfiles": False} + ) + + # Get results + results = processing.results(optimization_model) + + # string_results = processing.convert_keys_to_strings(results) + # sequences = {k: v["sequences"] for k, v in string_results.items()} + # df = pd.concat(sequences, axis=1) + + return results + + def test_storage_investment(self): + + bus_heat = self.prepare_es() + + storage = tabular_facades.Storage( + label="facades_Storage", + carrier="heat", + tech="storage", + bus=bus_heat, + efficiency=0.9, + expandable=True, + storage_capacity=0, # Initially installed storage capacity + storage_capacity_potential=10, # Potential for investment in storage capacity + storage_capacity_cost=2, + capacity=1, + # Reduces the inflow capacity in respect to a non restricted capacity_potential. + # As a result the missing capacity is added on the outflow capacity (?) + capacity_cost=0, # Doesn't do anything? + capacity_potential=0.9, + ) + + results = self.run_model() + + capacity_in = results[bus_heat, storage]["scalars"]["invest"] + capacity_out = results[storage, bus_heat]["scalars"]["invest"] + storage_capacity = results[storage, None]["scalars"]["invest"] + + assert capacity_in == capacity_out + + def test_assymetric_storage_investment(self): + + bus_heat = self.prepare_es() + + storage = oemoflex_facades.AsymmetricStorage( + label="oemoflex_Storage", + carrier="heat", + tech="storage", + type="storage", + bus=bus_heat, + efficiency=0.9, + expandable=True, + storage_capacity=0, # Initially installed storage capacity + storage_capacity_potential=10, # Invested storage capacity + storage_capacity_cost=2, + capacity=0.03, + # Reduces the inflow capacity in respect to a non restricted capacity_potential. + # As a result the missing capacity is added on the outflow capacity (?) + capacity_cost=0, # Doesn't do anything. -> capacity_cost_charge instead + capacity_cost_charge=0, + capacity_cost_discharge=0, # Doesn't do anything? + # capacity_potential=0, + # Doesn't do anything. -> capacity_potential_charge and capacity_potential_discharge + # instead + capacity_potential_charge=0.9, + capacity_potential_discharge=0.9, + ) + + results = self.run_model() + + capacity_in = results[bus_heat, storage]["scalars"]["invest"] + capacity_out = results[storage, bus_heat]["scalars"]["invest"] + storage_capacity = results[storage, None]["scalars"]["invest"] From 497f88541bf312c1b2796435c2aca8b0576197f0 Mon Sep 17 00:00:00 2001 From: jnnr <32454596+jnnr@users.noreply.github.com> Date: Mon, 24 Jan 2022 15:20:44 +0100 Subject: [PATCH 2/6] Add constraint tests for facades --- ...symetric_storage_investment_green_field.lp | 119 ++++++++++ .../storage_investment_green_field.lp | 123 ++++++++++ tests/test_facade_constraints.py | 211 ++++++++++++++++++ 3 files changed, 453 insertions(+) create mode 100644 tests/_files/lp_files/assymetric_storage_investment_green_field.lp create mode 100644 tests/_files/lp_files/storage_investment_green_field.lp create mode 100644 tests/test_facade_constraints.py diff --git a/tests/_files/lp_files/assymetric_storage_investment_green_field.lp b/tests/_files/lp_files/assymetric_storage_investment_green_field.lp new file mode 100644 index 0000000..862244e --- /dev/null +++ b/tests/_files/lp_files/assymetric_storage_investment_green_field.lp @@ -0,0 +1,119 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++1300 GenericInvestmentStorageBlock_invest(storage) ++130 InvestmentFlow_invest(electricity_storage) ++260 InvestmentFlow_invest(storage_electricity) + +s.t. + +c_e_Bus_balance(electricity_0)_: +-1 flow(electricity_storage_0) ++1 flow(storage_electricity_0) += 0 + +c_e_Bus_balance(electricity_1)_: +-1 flow(electricity_storage_1) ++1 flow(storage_electricity_1) += 0 + +c_e_Bus_balance(electricity_2)_: +-1 flow(electricity_storage_2) ++1 flow(storage_electricity_2) += 0 + +c_u_InvestmentFlow_max(electricity_storage_0)_: +-1 InvestmentFlow_invest(electricity_storage) ++1 flow(electricity_storage_0) +<= 0 + +c_u_InvestmentFlow_max(electricity_storage_1)_: +-1 InvestmentFlow_invest(electricity_storage) ++1 flow(electricity_storage_1) +<= 0 + +c_u_InvestmentFlow_max(electricity_storage_2)_: +-1 InvestmentFlow_invest(electricity_storage) ++1 flow(electricity_storage_2) +<= 0 + +c_u_InvestmentFlow_max(storage_electricity_0)_: +-1 InvestmentFlow_invest(storage_electricity) ++1 flow(storage_electricity_0) +<= 0 + +c_u_InvestmentFlow_max(storage_electricity_1)_: +-1 InvestmentFlow_invest(storage_electricity) ++1 flow(storage_electricity_1) +<= 0 + +c_u_InvestmentFlow_max(storage_electricity_2)_: +-1 InvestmentFlow_invest(storage_electricity) ++1 flow(storage_electricity_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_init_cap_limit(storage)_: ++1 GenericInvestmentStorageBlock_init_cap(storage) +-1 GenericInvestmentStorageBlock_invest(storage) +<= 0 + +c_e_GenericInvestmentStorageBlock_balance_first(storage)_: ++1 GenericInvestmentStorageBlock_capacity(storage_0) +-1 GenericInvestmentStorageBlock_init_cap(storage) +-0.90000000000000002 flow(electricity_storage_0) ++1.5625 flow(storage_electricity_0) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_1)_: +-1 GenericInvestmentStorageBlock_capacity(storage_0) ++1 GenericInvestmentStorageBlock_capacity(storage_1) +-0.90000000000000002 flow(electricity_storage_1) ++1.5625 flow(storage_electricity_1) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_2)_: +-1 GenericInvestmentStorageBlock_capacity(storage_1) ++1 GenericInvestmentStorageBlock_capacity(storage_2) +-0.90000000000000002 flow(electricity_storage_2) ++1.5625 flow(storage_electricity_2) += 0 + +c_e_GenericInvestmentStorageBlock_balanced_cstr(storage)_: ++1 GenericInvestmentStorageBlock_capacity(storage_2) +-1 GenericInvestmentStorageBlock_init_cap(storage) += 0 + +c_u_GenericInvestmentStorageBlock_max_capacity(storage_0)_: ++1 GenericInvestmentStorageBlock_capacity(storage_0) +-1 GenericInvestmentStorageBlock_invest(storage) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_capacity(storage_1)_: ++1 GenericInvestmentStorageBlock_capacity(storage_1) +-1 GenericInvestmentStorageBlock_invest(storage) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_capacity(storage_2)_: ++1 GenericInvestmentStorageBlock_capacity(storage_2) +-1 GenericInvestmentStorageBlock_invest(storage) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricity_storage_0) <= +inf + 0 <= flow(electricity_storage_1) <= +inf + 0 <= flow(electricity_storage_2) <= +inf + 0 <= flow(storage_electricity_0) <= +inf + 0 <= flow(storage_electricity_1) <= +inf + 0 <= flow(storage_electricity_2) <= +inf + 0 <= InvestmentFlow_invest(electricity_storage) <= 5 + 0 <= InvestmentFlow_invest(storage_electricity) <= 7 + 0 <= GenericInvestmentStorageBlock_capacity(storage_0) <= +inf + 0 <= GenericInvestmentStorageBlock_capacity(storage_1) <= +inf + 0 <= GenericInvestmentStorageBlock_capacity(storage_2) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage) <= 10 + 0 <= GenericInvestmentStorageBlock_init_cap(storage) <= +inf +end diff --git a/tests/_files/lp_files/storage_investment_green_field.lp b/tests/_files/lp_files/storage_investment_green_field.lp new file mode 100644 index 0000000..54f8f32 --- /dev/null +++ b/tests/_files/lp_files/storage_investment_green_field.lp @@ -0,0 +1,123 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++1300 GenericInvestmentStorageBlock_invest(storage) ++240 InvestmentFlow_invest(electricity_storage) + +s.t. + +c_e_Bus_balance(electricity_0)_: +-1 flow(electricity_storage_0) ++1 flow(storage_electricity_0) += 0 + +c_e_Bus_balance(electricity_1)_: +-1 flow(electricity_storage_1) ++1 flow(storage_electricity_1) += 0 + +c_e_Bus_balance(electricity_2)_: +-1 flow(electricity_storage_2) ++1 flow(storage_electricity_2) += 0 + +c_u_InvestmentFlow_max(electricity_storage_0)_: +-1 InvestmentFlow_invest(electricity_storage) ++1 flow(electricity_storage_0) +<= 0 + +c_u_InvestmentFlow_max(electricity_storage_1)_: +-1 InvestmentFlow_invest(electricity_storage) ++1 flow(electricity_storage_1) +<= 0 + +c_u_InvestmentFlow_max(electricity_storage_2)_: +-1 InvestmentFlow_invest(electricity_storage) ++1 flow(electricity_storage_2) +<= 0 + +c_u_InvestmentFlow_max(storage_electricity_0)_: +-1 InvestmentFlow_invest(storage_electricity) ++1 flow(storage_electricity_0) +<= 0 + +c_u_InvestmentFlow_max(storage_electricity_1)_: +-1 InvestmentFlow_invest(storage_electricity) ++1 flow(storage_electricity_1) +<= 0 + +c_u_InvestmentFlow_max(storage_electricity_2)_: +-1 InvestmentFlow_invest(storage_electricity) ++1 flow(storage_electricity_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_init_cap_limit(storage)_: ++1 GenericInvestmentStorageBlock_init_cap(storage) +-1 GenericInvestmentStorageBlock_invest(storage) +<= 0 + +c_e_GenericInvestmentStorageBlock_balance_first(storage)_: ++1 GenericInvestmentStorageBlock_capacity(storage_0) +-1 GenericInvestmentStorageBlock_init_cap(storage) +-0.90000000000000002 flow(electricity_storage_0) ++1.1111111111111112 flow(storage_electricity_0) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_1)_: +-1 GenericInvestmentStorageBlock_capacity(storage_0) ++1 GenericInvestmentStorageBlock_capacity(storage_1) +-0.90000000000000002 flow(electricity_storage_1) ++1.1111111111111112 flow(storage_electricity_1) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_2)_: +-1 GenericInvestmentStorageBlock_capacity(storage_1) ++1 GenericInvestmentStorageBlock_capacity(storage_2) +-0.90000000000000002 flow(electricity_storage_2) ++1.1111111111111112 flow(storage_electricity_2) += 0 + +c_e_GenericInvestmentStorageBlock_balanced_cstr(storage)_: ++1 GenericInvestmentStorageBlock_capacity(storage_2) +-1 GenericInvestmentStorageBlock_init_cap(storage) += 0 + +c_e_GenericInvestmentStorageBlock_power_coupled(storage)_: +-1 InvestmentFlow_invest(electricity_storage) ++1 InvestmentFlow_invest(storage_electricity) += 0 + +c_u_GenericInvestmentStorageBlock_max_capacity(storage_0)_: ++1 GenericInvestmentStorageBlock_capacity(storage_0) +-1 GenericInvestmentStorageBlock_invest(storage) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_capacity(storage_1)_: ++1 GenericInvestmentStorageBlock_capacity(storage_1) +-1 GenericInvestmentStorageBlock_invest(storage) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_capacity(storage_2)_: ++1 GenericInvestmentStorageBlock_capacity(storage_2) +-1 GenericInvestmentStorageBlock_invest(storage) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricity_storage_0) <= +inf + 0 <= flow(electricity_storage_1) <= +inf + 0 <= flow(electricity_storage_2) <= +inf + 0 <= flow(storage_electricity_0) <= +inf + 0 <= flow(storage_electricity_1) <= +inf + 0 <= flow(storage_electricity_2) <= +inf + 0 <= InvestmentFlow_invest(electricity_storage) <= 3 + 0 <= InvestmentFlow_invest(storage_electricity) <= +inf + 0 <= GenericInvestmentStorageBlock_capacity(storage_0) <= +inf + 0 <= GenericInvestmentStorageBlock_capacity(storage_1) <= +inf + 0 <= GenericInvestmentStorageBlock_capacity(storage_2) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage) <= 10 + 0 <= GenericInvestmentStorageBlock_init_cap(storage) <= +inf +end diff --git a/tests/test_facade_constraints.py b/tests/test_facade_constraints.py new file mode 100644 index 0000000..669b665 --- /dev/null +++ b/tests/test_facade_constraints.py @@ -0,0 +1,211 @@ +import logging +import os +import re +from difflib import unified_diff + +import pandas as pd + +from oemof.network import Node +from oemof.tools import helpers +import oemof.solph as solph +import oemof.tabular.facades as tabular_facades + +import oemoflex.facades as oemoflex_facades + + +def chop_trailing_whitespace(lines): + return [re.sub(r"\s*$", "", line) for line in lines] + + +def remove(pattern, lines): + if not pattern: + return lines + return re.subn(pattern, "", "\n".join(lines))[0].split("\n") + + +def normalize_to_positive_results(lines): + negative_result_indices = [ + n for n, line in enumerate(lines) if re.match("^= -", line) + ] + equation_start_indices = [ + [n for n in reversed(range(0, nri)) if re.match(".*:$", lines[n])][0] + 1 + for nri in negative_result_indices + ] + for (start, end) in zip(equation_start_indices, negative_result_indices): + for n in range(start, end): + lines[n] = ( + "-" + if lines[n] and lines[n][0] == "+" + else "+" + if lines[n] + else lines[n] + ) + lines[n][1:] + lines[end] = "= " + lines[end][3:] + return lines + + +def compare_lp_files(lp_file_1, lp_file_2, ignored=None): + lines_1 = remove(ignored, chop_trailing_whitespace(lp_file_1.readlines())) + lines_2 = remove(ignored, chop_trailing_whitespace(lp_file_2.readlines())) + + lines_1 = normalize_to_positive_results(lines_1) + lines_2 = normalize_to_positive_results(lines_2) + + if not lines_1 == lines_2: + raise AssertionError( + "Failed matching lp_file_1 with lp_file_2:\n" + + "\n".join( + unified_diff( + lines_1, + lines_2, + fromfile=os.path.relpath(lp_file_1.name), + tofile=os.path.basename(lp_file_2.name), + lineterm="", + ) + ) + ) + + +class TestConstraints: + @classmethod + def setup_class(cls): + cls.objective_pattern = re.compile( + r"^objective.*(?=s\.t\.)", re.DOTALL | re.MULTILINE + ) + + cls.date_time_index = pd.date_range("1/1/2012", periods=3, freq="H") + + cls.tmpdir = helpers.extend_basic_path("tmp") + logging.info(cls.tmpdir) + + @classmethod + def setup(cls): + cls.energysystem = solph.EnergySystem( + groupings=solph.GROUPINGS, timeindex=cls.date_time_index + ) + Node.registry = cls.energysystem + + def get_om(self): + return solph.Model(self.energysystem, timeindex=self.energysystem.timeindex) + + def compare_to_reference_lp(self, ref_filename, my_om=None): + if my_om is None: + om = self.get_om() + else: + om = my_om + + tmp_filename = ref_filename.replace(".lp", "") + "_tmp.lp" + + new_filepath = os.path.join(self.tmpdir, tmp_filename) + + om.write(new_filepath, io_options={"symbolic_solver_labels": True}) + + ref_filepath = os.path.join( + os.path.dirname(__file__), "_files", "lp_files", ref_filename + ) + + with open(new_filepath) as new_file: + with open(ref_filepath) as ref_file: + compare_lp_files(new_file, ref_file) + + def test_storage_investment_green_field(self): + r""" + Storage investment without existing capacities. + """ + el_bus = solph.Bus(label="electricity") + + tabular_facades.Storage( + label="storage", + carrier="electricity", + tech="storage", + bus=el_bus, + efficiency=0.9, + expandable=True, + storage_capacity=0, # No initially installed storage capacity + storage_capacity_potential=10, + storage_capacity_cost=1300, + capacity=0, # No initially installed capacity + capacity_cost=240, + capacity_potential=3, + ) + + self.compare_to_reference_lp("storage_investment_green_field.lp") + + def test_storage_investment_brown_field(self): + r""" + Storage investment with existing capacities. + """ + bus_el = solph.Bus(label="electricity") + + tabular_facades.Storage( + label="storage", + carrier="electricity", + tech="storage", + bus=bus_el, + efficiency=0.9, + expandable=True, + storage_capacity=2, # Existing storage capacity + storage_capacity_potential=10, + storage_capacity_cost=1300, + capacity=1, # Existing capacity + capacity_cost=240, + capacity_potential=5, + ) + + self.compare_to_reference_lp("storage_investment_brown_field.lp") + + def test_assymetric_storage_investment_green_field(self): + r""" + Investment of assymetric storage without existing capacities. + """ + bus_el = solph.Bus(label="electricity") + + oemoflex_facades.AsymmetricStorage( + label="storage", + carrier="electricity", + tech="storage", + type="storage", + bus=bus_el, + efficiency_charge=0.9, + efficiency_discharge=0.64, + expandable=True, + storage_capacity=0, # No inital storage capacity + storage_capacity_potential=10, + storage_capacity_cost=1300, + capacity_charge=0, # No inital capacities + capacity_discharge=0, # No inital capacities + capacity_cost_charge=130, + capacity_cost_discharge=260, + capacity_potential_charge=5, + capacity_potential_discharge=7, + ) + + self.compare_to_reference_lp("assymetric_storage_investment_green_field.lp") + + def test_assymetric_storage_investment_brown_field(self): + r""" + Investment of assymetric storage with existing capacities. + """ + bus_el = solph.Bus(label="electricity") + + oemoflex_facades.AsymmetricStorage( + label="storage", + carrier="electricity", + tech="storage", + type="storage", + bus=bus_el, + efficiency_charge=0.9, + efficiency_discharge=0.64, + expandable=True, + storage_capacity=6, # Existing storage capacity + storage_capacity_potential=10, + storage_capacity_cost=1300, + capacity_charge=2, # Existing capacity + capacity_discharge=3, # Existing capacity + capacity_cost_charge=130, + capacity_cost_discharge=260, + capacity_potential_charge=5, + capacity_potential_discharge=7, + ) + + self.compare_to_reference_lp("assymetric_storage_investment_brown_field.lp") From e82e817f09d970133583941170f8878fabb4cb7d Mon Sep 17 00:00:00 2001 From: jnnr <32454596+jnnr@users.noreply.github.com> Date: Mon, 24 Jan 2022 17:49:42 +0100 Subject: [PATCH 3/6] Fix typo --- tests/test_cases.py | 2 +- tests/test_facade_constraints.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_cases.py b/tests/test_cases.py index 47d6885..c59ac62 100644 --- a/tests/test_cases.py +++ b/tests/test_cases.py @@ -96,7 +96,7 @@ def test_storage_investment(self): assert capacity_in == capacity_out - def test_assymetric_storage_investment(self): + def test_asymemtric_storage_investment(self): bus_heat = self.prepare_es() diff --git a/tests/test_facade_constraints.py b/tests/test_facade_constraints.py index 669b665..c9a40a6 100644 --- a/tests/test_facade_constraints.py +++ b/tests/test_facade_constraints.py @@ -154,9 +154,9 @@ def test_storage_investment_brown_field(self): self.compare_to_reference_lp("storage_investment_brown_field.lp") - def test_assymetric_storage_investment_green_field(self): + def test_asymmetric_storage_investment_green_field(self): r""" - Investment of assymetric storage without existing capacities. + Investment of asymmetric storage without existing capacities. """ bus_el = solph.Bus(label="electricity") @@ -180,11 +180,11 @@ def test_assymetric_storage_investment_green_field(self): capacity_potential_discharge=7, ) - self.compare_to_reference_lp("assymetric_storage_investment_green_field.lp") + self.compare_to_reference_lp("asymmetric_storage_investment_green_field.lp") - def test_assymetric_storage_investment_brown_field(self): + def test_asymmetric_storage_investment_brown_field(self): r""" - Investment of assymetric storage with existing capacities. + Investment of asymmetric storage with existing capacities. """ bus_el = solph.Bus(label="electricity") @@ -208,4 +208,4 @@ def test_assymetric_storage_investment_brown_field(self): capacity_potential_discharge=7, ) - self.compare_to_reference_lp("assymetric_storage_investment_brown_field.lp") + self.compare_to_reference_lp("asymmetric_storage_investment_brown_field.lp") From 0f51fb8fe62d4f5f287742c92cb539aa76ff46f7 Mon Sep 17 00:00:00 2001 From: jnnr <32454596+jnnr@users.noreply.github.com> Date: Mon, 24 Jan 2022 17:50:39 +0100 Subject: [PATCH 4/6] Set potentials on asymmetric storage --- oemoflex/facades.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/oemoflex/facades.py b/oemoflex/facades.py index b04381d..fa77d06 100644 --- a/oemoflex/facades.py +++ b/oemoflex/facades.py @@ -127,7 +127,9 @@ def build_solph_components(self): self.investment = Investment( ep_costs=self.storage_capacity_cost, - maximum=getattr(self, "storage_capacity_potential", float("+inf")), + maximum=self._get_maximum_additional_invest( + "storage_capacity_potential", "storage_capacity" + ), minimum=getattr(self, "minimum_storage_capacity", 0), existing=getattr(self, "storage_capacity", 0), ) @@ -135,7 +137,9 @@ def build_solph_components(self): fi = Flow( investment=Investment( ep_costs=self.capacity_cost_charge, - maximum=getattr(self, "capacity_potential_charge", float("+inf")), + maximum=self._get_maximum_additional_invest( + "capacity_potential_charge", "capacity_charge" + ), existing=getattr(self, "capacity_charge", 0), ), **self.input_parameters @@ -144,8 +148,8 @@ def build_solph_components(self): fo = Flow( investment=Investment( ep_costs=self.capacity_cost_discharge, - maximum=getattr( - self, "capacity_potential_discharge", float("+inf") + maximum=self._get_maximum_additional_invest( + "capacity_potential_discharge", "capacity_discharge" ), existing=getattr(self, "capacity_discharge", 0), ), From dc6778957593a88dfc06b7c00dbeb857ea63c5b8 Mon Sep 17 00:00:00 2001 From: jnnr <32454596+jnnr@users.noreply.github.com> Date: Mon, 24 Jan 2022 17:53:17 +0100 Subject: [PATCH 5/6] Set potential larger than existing in test case --- tests/test_cases.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases.py b/tests/test_cases.py index c59ac62..45e325e 100644 --- a/tests/test_cases.py +++ b/tests/test_cases.py @@ -85,7 +85,7 @@ def test_storage_investment(self): # Reduces the inflow capacity in respect to a non restricted capacity_potential. # As a result the missing capacity is added on the outflow capacity (?) capacity_cost=0, # Doesn't do anything? - capacity_potential=0.9, + capacity_potential=3, ) results = self.run_model() From 6e110027f777c79d537183bc530cd4ce10c4ec9e Mon Sep 17 00:00:00 2001 From: jnnr <32454596+jnnr@users.noreply.github.com> Date: Mon, 24 Jan 2022 17:54:02 +0100 Subject: [PATCH 6/6] Add reference lp files for brown field optimization --- ...ymmetric_storage_investment_brown_field.lp | 119 +++++++++++++++++ ...mmetric_storage_investment_green_field.lp} | 0 .../storage_investment_brown_field.lp | 123 ++++++++++++++++++ 3 files changed, 242 insertions(+) create mode 100644 tests/_files/lp_files/asymmetric_storage_investment_brown_field.lp rename tests/_files/lp_files/{assymetric_storage_investment_green_field.lp => asymmetric_storage_investment_green_field.lp} (100%) create mode 100644 tests/_files/lp_files/storage_investment_brown_field.lp diff --git a/tests/_files/lp_files/asymmetric_storage_investment_brown_field.lp b/tests/_files/lp_files/asymmetric_storage_investment_brown_field.lp new file mode 100644 index 0000000..5d15a73 --- /dev/null +++ b/tests/_files/lp_files/asymmetric_storage_investment_brown_field.lp @@ -0,0 +1,119 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++1300 GenericInvestmentStorageBlock_invest(storage) ++130 InvestmentFlow_invest(electricity_storage) ++260 InvestmentFlow_invest(storage_electricity) + +s.t. + +c_e_Bus_balance(electricity_0)_: +-1 flow(electricity_storage_0) ++1 flow(storage_electricity_0) += 0 + +c_e_Bus_balance(electricity_1)_: +-1 flow(electricity_storage_1) ++1 flow(storage_electricity_1) += 0 + +c_e_Bus_balance(electricity_2)_: +-1 flow(electricity_storage_2) ++1 flow(storage_electricity_2) += 0 + +c_u_InvestmentFlow_max(electricity_storage_0)_: +-1 InvestmentFlow_invest(electricity_storage) ++1 flow(electricity_storage_0) +<= 2 + +c_u_InvestmentFlow_max(electricity_storage_1)_: +-1 InvestmentFlow_invest(electricity_storage) ++1 flow(electricity_storage_1) +<= 2 + +c_u_InvestmentFlow_max(electricity_storage_2)_: +-1 InvestmentFlow_invest(electricity_storage) ++1 flow(electricity_storage_2) +<= 2 + +c_u_InvestmentFlow_max(storage_electricity_0)_: +-1 InvestmentFlow_invest(storage_electricity) ++1 flow(storage_electricity_0) +<= 3 + +c_u_InvestmentFlow_max(storage_electricity_1)_: +-1 InvestmentFlow_invest(storage_electricity) ++1 flow(storage_electricity_1) +<= 3 + +c_u_InvestmentFlow_max(storage_electricity_2)_: +-1 InvestmentFlow_invest(storage_electricity) ++1 flow(storage_electricity_2) +<= 3 + +c_u_GenericInvestmentStorageBlock_init_cap_limit(storage)_: ++1 GenericInvestmentStorageBlock_init_cap(storage) +-1 GenericInvestmentStorageBlock_invest(storage) +<= 6 + +c_e_GenericInvestmentStorageBlock_balance_first(storage)_: ++1 GenericInvestmentStorageBlock_capacity(storage_0) +-1 GenericInvestmentStorageBlock_init_cap(storage) +-0.90000000000000002 flow(electricity_storage_0) ++1.5625 flow(storage_electricity_0) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_1)_: +-1 GenericInvestmentStorageBlock_capacity(storage_0) ++1 GenericInvestmentStorageBlock_capacity(storage_1) +-0.90000000000000002 flow(electricity_storage_1) ++1.5625 flow(storage_electricity_1) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_2)_: +-1 GenericInvestmentStorageBlock_capacity(storage_1) ++1 GenericInvestmentStorageBlock_capacity(storage_2) +-0.90000000000000002 flow(electricity_storage_2) ++1.5625 flow(storage_electricity_2) += 0 + +c_e_GenericInvestmentStorageBlock_balanced_cstr(storage)_: ++1 GenericInvestmentStorageBlock_capacity(storage_2) +-1 GenericInvestmentStorageBlock_init_cap(storage) += 0 + +c_u_GenericInvestmentStorageBlock_max_capacity(storage_0)_: ++1 GenericInvestmentStorageBlock_capacity(storage_0) +-1 GenericInvestmentStorageBlock_invest(storage) +<= 6 + +c_u_GenericInvestmentStorageBlock_max_capacity(storage_1)_: ++1 GenericInvestmentStorageBlock_capacity(storage_1) +-1 GenericInvestmentStorageBlock_invest(storage) +<= 6 + +c_u_GenericInvestmentStorageBlock_max_capacity(storage_2)_: ++1 GenericInvestmentStorageBlock_capacity(storage_2) +-1 GenericInvestmentStorageBlock_invest(storage) +<= 6 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricity_storage_0) <= +inf + 0 <= flow(electricity_storage_1) <= +inf + 0 <= flow(electricity_storage_2) <= +inf + 0 <= flow(storage_electricity_0) <= +inf + 0 <= flow(storage_electricity_1) <= +inf + 0 <= flow(storage_electricity_2) <= +inf + 0 <= InvestmentFlow_invest(electricity_storage) <= 3 + 0 <= InvestmentFlow_invest(storage_electricity) <= 4 + 0 <= GenericInvestmentStorageBlock_capacity(storage_0) <= +inf + 0 <= GenericInvestmentStorageBlock_capacity(storage_1) <= +inf + 0 <= GenericInvestmentStorageBlock_capacity(storage_2) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage) <= 4 + 0 <= GenericInvestmentStorageBlock_init_cap(storage) <= +inf +end diff --git a/tests/_files/lp_files/assymetric_storage_investment_green_field.lp b/tests/_files/lp_files/asymmetric_storage_investment_green_field.lp similarity index 100% rename from tests/_files/lp_files/assymetric_storage_investment_green_field.lp rename to tests/_files/lp_files/asymmetric_storage_investment_green_field.lp diff --git a/tests/_files/lp_files/storage_investment_brown_field.lp b/tests/_files/lp_files/storage_investment_brown_field.lp new file mode 100644 index 0000000..a346d00 --- /dev/null +++ b/tests/_files/lp_files/storage_investment_brown_field.lp @@ -0,0 +1,123 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++1300 GenericInvestmentStorageBlock_invest(storage) ++240 InvestmentFlow_invest(electricity_storage) + +s.t. + +c_e_Bus_balance(electricity_0)_: +-1 flow(electricity_storage_0) ++1 flow(storage_electricity_0) += 0 + +c_e_Bus_balance(electricity_1)_: +-1 flow(electricity_storage_1) ++1 flow(storage_electricity_1) += 0 + +c_e_Bus_balance(electricity_2)_: +-1 flow(electricity_storage_2) ++1 flow(storage_electricity_2) += 0 + +c_u_InvestmentFlow_max(electricity_storage_0)_: +-1 InvestmentFlow_invest(electricity_storage) ++1 flow(electricity_storage_0) +<= 1 + +c_u_InvestmentFlow_max(electricity_storage_1)_: +-1 InvestmentFlow_invest(electricity_storage) ++1 flow(electricity_storage_1) +<= 1 + +c_u_InvestmentFlow_max(electricity_storage_2)_: +-1 InvestmentFlow_invest(electricity_storage) ++1 flow(electricity_storage_2) +<= 1 + +c_u_InvestmentFlow_max(storage_electricity_0)_: +-1 InvestmentFlow_invest(storage_electricity) ++1 flow(storage_electricity_0) +<= 1 + +c_u_InvestmentFlow_max(storage_electricity_1)_: +-1 InvestmentFlow_invest(storage_electricity) ++1 flow(storage_electricity_1) +<= 1 + +c_u_InvestmentFlow_max(storage_electricity_2)_: +-1 InvestmentFlow_invest(storage_electricity) ++1 flow(storage_electricity_2) +<= 1 + +c_u_GenericInvestmentStorageBlock_init_cap_limit(storage)_: ++1 GenericInvestmentStorageBlock_init_cap(storage) +-1 GenericInvestmentStorageBlock_invest(storage) +<= 2 + +c_e_GenericInvestmentStorageBlock_balance_first(storage)_: ++1 GenericInvestmentStorageBlock_capacity(storage_0) +-1 GenericInvestmentStorageBlock_init_cap(storage) +-0.90000000000000002 flow(electricity_storage_0) ++1.1111111111111112 flow(storage_electricity_0) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_1)_: +-1 GenericInvestmentStorageBlock_capacity(storage_0) ++1 GenericInvestmentStorageBlock_capacity(storage_1) +-0.90000000000000002 flow(electricity_storage_1) ++1.1111111111111112 flow(storage_electricity_1) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_2)_: +-1 GenericInvestmentStorageBlock_capacity(storage_1) ++1 GenericInvestmentStorageBlock_capacity(storage_2) +-0.90000000000000002 flow(electricity_storage_2) ++1.1111111111111112 flow(storage_electricity_2) += 0 + +c_e_GenericInvestmentStorageBlock_balanced_cstr(storage)_: ++1 GenericInvestmentStorageBlock_capacity(storage_2) +-1 GenericInvestmentStorageBlock_init_cap(storage) += 0 + +c_e_GenericInvestmentStorageBlock_power_coupled(storage)_: +-1 InvestmentFlow_invest(electricity_storage) ++1 InvestmentFlow_invest(storage_electricity) += 0 + +c_u_GenericInvestmentStorageBlock_max_capacity(storage_0)_: ++1 GenericInvestmentStorageBlock_capacity(storage_0) +-1 GenericInvestmentStorageBlock_invest(storage) +<= 2 + +c_u_GenericInvestmentStorageBlock_max_capacity(storage_1)_: ++1 GenericInvestmentStorageBlock_capacity(storage_1) +-1 GenericInvestmentStorageBlock_invest(storage) +<= 2 + +c_u_GenericInvestmentStorageBlock_max_capacity(storage_2)_: ++1 GenericInvestmentStorageBlock_capacity(storage_2) +-1 GenericInvestmentStorageBlock_invest(storage) +<= 2 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricity_storage_0) <= +inf + 0 <= flow(electricity_storage_1) <= +inf + 0 <= flow(electricity_storage_2) <= +inf + 0 <= flow(storage_electricity_0) <= +inf + 0 <= flow(storage_electricity_1) <= +inf + 0 <= flow(storage_electricity_2) <= +inf + 0 <= InvestmentFlow_invest(electricity_storage) <= 4 + 0 <= InvestmentFlow_invest(storage_electricity) <= +inf + 0 <= GenericInvestmentStorageBlock_capacity(storage_0) <= +inf + 0 <= GenericInvestmentStorageBlock_capacity(storage_1) <= +inf + 0 <= GenericInvestmentStorageBlock_capacity(storage_2) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage) <= 8 + 0 <= GenericInvestmentStorageBlock_init_cap(storage) <= +inf +end