From 9b994ad55f632de5000ce27e178325f488c1a4fb Mon Sep 17 00:00:00 2001 From: uvchik Date: Tue, 31 Mar 2020 15:03:13 +0200 Subject: [PATCH 0001/1363] Use original file --- .appveyor.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/.appveyor.yml b/.appveyor.yml index ae9669e87..682265690 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -10,6 +10,42 @@ environment: PYTHON_HOME: C:\Python36 PYTHON_VERSION: '3.6' PYTHON_ARCH: '32' + - TOXENV: py36,codecov,coveralls + TOXPYTHON: C:\Python36\python.exe + PYTHON_HOME: C:\Python36 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '32' + - TOXENV: py36,codecov,coveralls + TOXPYTHON: C:\Python36-x64\python.exe + PYTHON_HOME: C:\Python36-x64 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '64' + - TOXENV: py37,codecov,coveralls + TOXPYTHON: C:\Python37\python.exe + PYTHON_HOME: C:\Python37 + PYTHON_VERSION: '3.7' + PYTHON_ARCH: '32' + - TOXENV: py37,codecov,coveralls + TOXPYTHON: C:\Python37-x64\python.exe + PYTHON_HOME: C:\Python37-x64 + PYTHON_VERSION: '3.7' + PYTHON_ARCH: '64' + - TOXENV: py38,codecov,coveralls + TOXPYTHON: C:\Python38\python.exe + PYTHON_HOME: C:\Python38 + PYTHON_VERSION: '3.8' + PYTHON_ARCH: '32' + - TOXENV: py38,codecov,coveralls + TOXPYTHON: C:\Python38-x64\python.exe + PYTHON_HOME: C:\Python38-x64 + PYTHON_VERSION: '3.8' + PYTHON_ARCH: '64' + - TOXENV: py37-nocov + TOXPYTHON: C:\Python37-x64\python.exe + PYTHON_HOME: C:\Python37-x64 + PYTHON_VERSION: '3.7' + PYTHON_ARCH: '64' + WHEEL_PATH: .tox/dist init: - ps: echo $env:TOXENV - ps: ls C:\Python* @@ -20,6 +56,7 @@ install: - '%PYTHON_HOME%\Scripts\pip --version' - '%PYTHON_HOME%\Scripts\tox --version' - ps: (new-object net.webclient).DownloadFile('https://osf.io/rmfb7/download', 'my_test_solver.exe') + - ps: exec /i my_test_solver.exe /quiet /qn /norestart /log install.log PROPERTY1=value1 PROPERTY2=value2 test_script: - cmd /E:ON /V:ON /C .\ci\appveyor-with-compiler.cmd %PYTHON_HOME%\Scripts\tox From a0e24c350e51eb47bcb1e979de4bc940cd3a4952 Mon Sep 17 00:00:00 2001 From: uvchik Date: Tue, 31 Mar 2020 15:09:06 +0200 Subject: [PATCH 0002/1363] Change file name of cbc solver --- .appveyor.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 682265690..e9d9f1a67 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -55,8 +55,7 @@ install: - '%PYTHON_HOME%\Scripts\easy_install --version' - '%PYTHON_HOME%\Scripts\pip --version' - '%PYTHON_HOME%\Scripts\tox --version' - - ps: (new-object net.webclient).DownloadFile('https://osf.io/rmfb7/download', 'my_test_solver.exe') - - ps: exec /i my_test_solver.exe /quiet /qn /norestart /log install.log PROPERTY1=value1 PROPERTY2=value2 + - ps: (new-object net.webclient).DownloadFile('https://osf.io/xyqre/download', 'cbc.exe') test_script: - cmd /E:ON /V:ON /C .\ci\appveyor-with-compiler.cmd %PYTHON_HOME%\Scripts\tox From 100fe6d5d39ae271fec19eb78fac26968275757b Mon Sep 17 00:00:00 2001 From: Caterina Koehl Date: Fri, 10 Apr 2020 13:49:32 +0200 Subject: [PATCH 0003/1363] Deleted unnecessary import --- oemof/tools/helpers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/oemof/tools/helpers.py b/oemof/tools/helpers.py index 0c0ff8fd8..95db9ddb0 100644 --- a/oemof/tools/helpers.py +++ b/oemof/tools/helpers.py @@ -14,7 +14,6 @@ import os import pandas as pd from collections import MutableMapping -from ..solph.plumbing import sequence def get_basic_path(): @@ -86,7 +85,7 @@ def calculate_timeincrement(timeindex, fill_value=None): timeincrement_sec = timeincrement.map(dt.timedelta.total_seconds) timeincrement_hourly = list(timeincrement_sec.map( lambda x: x/3600)) - timeincrement = sequence(timeincrement_hourly) + timeincrement = timeincrement_hourly return timeincrement else: raise AttributeError( From a5dae6be7392133844309d3a37d1a5b3ef9ab66a Mon Sep 17 00:00:00 2001 From: Caterina Koehl Date: Fri, 10 Apr 2020 15:48:56 +0200 Subject: [PATCH 0004/1363] Added constraint test for functionality --- tests/constraint_tests.py | 23 +++- tests/lp_files/nonequidistant_timeindex.lp | 140 +++++++++++++++++++++ 2 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 tests/lp_files/nonequidistant_timeindex.lp diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index 6546997dd..3c7510a3e 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -676,4 +676,25 @@ def test_dsm_module_interval(self): method='interval', shift_interval=2 ) - self.compare_lp_files('dsm_module_interval.lp') \ No newline at end of file + self.compare_lp_files('dsm_module_interval.lp') + + + def test_nonequidistant_timeindex(self): + """Constraint test of an energysystem with nonequidistant timeindex""" + idx2h = pd.date_range('1/1/2017', periods=3, freq='H') + idxh = pd.date_range("1/1/2017 04:00:00", periods=2, freq="2H") + idx30m = pd.date_range("1/1/2017 06:30:00", periods=3, freq="30min") + timeindex = idx2h.append([idxh, idx30m]) + timeincrement = helpers.calculate_timeincrement(timeindex) + es = solph.EnergySystem(timeindex=timeindex, + timeincrement=timeincrement) + b_gas = solph.Bus(label="gas") + b_th = solph.Bus(label = "heat") + boiler = solph.Transformer( + label="boiler", + inputs={b_gas: solph.Flow(variable_costs=100)}, + outputs={b_th: solph.Flow(nominal_value=200)} + ) + es.add(b_gas, b_th, boiler) + om = solph.Model(es) + self.compare_lp_files('nonequidistant_timeindex.lp', my_om=om) diff --git a/tests/lp_files/nonequidistant_timeindex.lp b/tests/lp_files/nonequidistant_timeindex.lp new file mode 100644 index 000000000..84bc077b2 --- /dev/null +++ b/tests/lp_files/nonequidistant_timeindex.lp @@ -0,0 +1,140 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++100 flow(gas_boiler_0) ++100 flow(gas_boiler_1) ++100 flow(gas_boiler_2) ++200 flow(gas_boiler_3) ++200 flow(gas_boiler_4) ++50 flow(gas_boiler_5) ++50 flow(gas_boiler_6) ++50 flow(gas_boiler_7) + +s.t. + +c_e_Bus_balance(gas_0)_: ++1 flow(gas_boiler_0) += 0 + +c_e_Bus_balance(gas_1)_: ++1 flow(gas_boiler_1) += 0 + +c_e_Bus_balance(gas_2)_: ++1 flow(gas_boiler_2) += 0 + +c_e_Bus_balance(gas_3)_: ++1 flow(gas_boiler_3) += 0 + +c_e_Bus_balance(gas_4)_: ++1 flow(gas_boiler_4) += 0 + +c_e_Bus_balance(gas_5)_: ++1 flow(gas_boiler_5) += 0 + +c_e_Bus_balance(gas_6)_: ++1 flow(gas_boiler_6) += 0 + +c_e_Bus_balance(gas_7)_: ++1 flow(gas_boiler_7) += 0 + +c_e_Bus_balance(heat_0)_: ++1 flow(boiler_heat_0) += 0 + +c_e_Bus_balance(heat_1)_: ++1 flow(boiler_heat_1) += 0 + +c_e_Bus_balance(heat_2)_: ++1 flow(boiler_heat_2) += 0 + +c_e_Bus_balance(heat_3)_: ++1 flow(boiler_heat_3) += 0 + +c_e_Bus_balance(heat_4)_: ++1 flow(boiler_heat_4) += 0 + +c_e_Bus_balance(heat_5)_: ++1 flow(boiler_heat_5) += 0 + +c_e_Bus_balance(heat_6)_: ++1 flow(boiler_heat_6) += 0 + +c_e_Bus_balance(heat_7)_: ++1 flow(boiler_heat_7) += 0 + +c_e_Transformer_relation(boiler_gas_heat_0)_: +-1 flow(boiler_heat_0) ++1 flow(gas_boiler_0) += 0 + +c_e_Transformer_relation(boiler_gas_heat_1)_: +-1 flow(boiler_heat_1) ++1 flow(gas_boiler_1) += 0 + +c_e_Transformer_relation(boiler_gas_heat_2)_: +-1 flow(boiler_heat_2) ++1 flow(gas_boiler_2) += 0 + +c_e_Transformer_relation(boiler_gas_heat_3)_: +-1 flow(boiler_heat_3) ++1 flow(gas_boiler_3) += 0 + +c_e_Transformer_relation(boiler_gas_heat_4)_: +-1 flow(boiler_heat_4) ++1 flow(gas_boiler_4) += 0 + +c_e_Transformer_relation(boiler_gas_heat_5)_: +-1 flow(boiler_heat_5) ++1 flow(gas_boiler_5) += 0 + +c_e_Transformer_relation(boiler_gas_heat_6)_: +-1 flow(boiler_heat_6) ++1 flow(gas_boiler_6) += 0 + +c_e_Transformer_relation(boiler_gas_heat_7)_: +-1 flow(boiler_heat_7) ++1 flow(gas_boiler_7) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(boiler_heat_0) <= 200 + 0 <= flow(boiler_heat_1) <= 200 + 0 <= flow(boiler_heat_2) <= 200 + 0 <= flow(boiler_heat_3) <= 200 + 0 <= flow(boiler_heat_4) <= 200 + 0 <= flow(boiler_heat_5) <= 200 + 0 <= flow(boiler_heat_6) <= 200 + 0 <= flow(boiler_heat_7) <= 200 + 0 <= flow(gas_boiler_0) <= +inf + 0 <= flow(gas_boiler_1) <= +inf + 0 <= flow(gas_boiler_2) <= +inf + 0 <= flow(gas_boiler_3) <= +inf + 0 <= flow(gas_boiler_4) <= +inf + 0 <= flow(gas_boiler_5) <= +inf + 0 <= flow(gas_boiler_6) <= +inf + 0 <= flow(gas_boiler_7) <= +inf +end From 1e1ddf542c08cff7c327ea2d6ba1def86fd48033 Mon Sep 17 00:00:00 2001 From: "c.koehl" Date: Wed, 10 Jun 2020 18:44:46 +0200 Subject: [PATCH 0005/1363] Adapted code to unboundling of oemof --- src/oemof/solph/helpers.py | 1 - tests/constraint_tests.py | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/oemof/solph/helpers.py b/src/oemof/solph/helpers.py index 9302dce1f..efbeb601f 100644 --- a/src/oemof/solph/helpers.py +++ b/src/oemof/solph/helpers.py @@ -15,7 +15,6 @@ from collections import MutableMapping import pandas as pd -from oemof.solph.plumbing import sequence def get_basic_path(): diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index 4cf2555ce..7a6a926fe 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -850,3 +850,23 @@ def test_nonconvex_invest_source_with_offset_no_minimum(self): investment=solph.Investment(ep_costs=500, maximum=1234, offset=34, nonconvex=True))}) self.compare_lp_files('flow_invest_with_offset_no_minimum.lp') + + def test_nonequidistant_timeindex(self): + """Constraint test of an energysystem with nonequidistant timeindex""" + idx2h = pd.date_range('1/1/2017', periods=3, freq='H') + idxh = pd.date_range("1/1/2017 04:00:00", periods=2, freq="2H") + idx30m = pd.date_range("1/1/2017 06:30:00", periods=3, freq="30min") + timeindex = idx2h.append([idxh, idx30m]) + timeincrement = solph.helpers.calculate_timeincrement(timeindex) + es = solph.EnergySystem(timeindex=timeindex, + timeincrement=timeincrement) + b_gas = solph.Bus(label="gas") + b_th = solph.Bus(label = "heat") + boiler = solph.Transformer( + label="boiler", + inputs={b_gas: solph.Flow(variable_costs=100)}, + outputs={b_th: solph.Flow(nominal_value=200)} + ) + es.add(b_gas, b_th, boiler) + om = solph.Model(es) + self.compare_lp_files('nonequidistant_timeindex.lp', my_om=om) \ No newline at end of file From 578a39cf9329c83085f58eb8ffe951e65faf053f Mon Sep 17 00:00:00 2001 From: "c.koehl" Date: Wed, 10 Jun 2020 18:46:58 +0200 Subject: [PATCH 0006/1363] Added new line in end of file --- tests/constraint_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index 7a6a926fe..7e47aac77 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -869,4 +869,4 @@ def test_nonequidistant_timeindex(self): ) es.add(b_gas, b_th, boiler) om = solph.Model(es) - self.compare_lp_files('nonequidistant_timeindex.lp', my_om=om) \ No newline at end of file + self.compare_lp_files('nonequidistant_timeindex.lp', my_om=om) From 63764fa76c113da5a8ba2cc50612d4e11228d16a Mon Sep 17 00:00:00 2001 From: "c.koehl" Date: Wed, 10 Jun 2020 18:52:10 +0200 Subject: [PATCH 0007/1363] Removed spaces around keyword (PEP8) --- tests/constraint_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index 7e47aac77..b0ff574c4 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -861,7 +861,7 @@ def test_nonequidistant_timeindex(self): es = solph.EnergySystem(timeindex=timeindex, timeincrement=timeincrement) b_gas = solph.Bus(label="gas") - b_th = solph.Bus(label = "heat") + b_th = solph.Bus(label="heat") boiler = solph.Transformer( label="boiler", inputs={b_gas: solph.Flow(variable_costs=100)}, From b790bccf6aa19a4de644a8309b46a524e62cd205 Mon Sep 17 00:00:00 2001 From: "c.koehl" Date: Mon, 24 Aug 2020 13:11:59 +0200 Subject: [PATCH 0008/1363] Added test with GenericStorage --- tests/constraint_tests.py | 25 +-- tests/lp_files/nonequidistant_timeindex.lp | 191 +++++++++++---------- 2 files changed, 114 insertions(+), 102 deletions(-) diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index 6e60d7497..9a17a4e19 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -903,19 +903,20 @@ def test_nonconvex_invest_source_with_offset_no_minimum(self): def test_nonequidistant_timeindex(self): """Constraint test of an energysystem with nonequidistant timeindex""" idx2h = pd.date_range('1/1/2017', periods=3, freq='H') - idxh = pd.date_range("1/1/2017 04:00:00", periods=2, freq="2H") - idx30m = pd.date_range("1/1/2017 06:30:00", periods=3, freq="30min") - timeindex = idx2h.append([idxh, idx30m]) - timeincrement = helpers.calculate_timeincrement(timeindex) + idxh = pd.date_range('1/1/2017 04:00:00', periods=2, freq='2H') + idx30m = pd.date_range('1/1/2017 06:30:00', periods=3, freq='30min') + timeindex = idxh.append([idx2h, idx30m]) + timeincrement = solph.helpers.calculate_timeincrement(timeindex) es = solph.EnergySystem(timeindex=timeindex, timeincrement=timeincrement) - b_gas = solph.Bus(label="gas") - b_th = solph.Bus(label="heat") - boiler = solph.Transformer( - label="boiler", - inputs={b_gas: solph.Flow(variable_costs=100)}, - outputs={b_th: solph.Flow(nominal_value=200)} - ) - es.add(b_gas, b_th, boiler) + b_th = solph.Bus(label='heat') + storage = solph.GenericStorage( + label='storage', + inputs={b_th: solph.Flow(nominal_value=100, variable_costs=56)}, + outputs={b_th: solph.Flow(nominal_value=100, variable_costs=24)}, + nominal_storage_capacity=300, + loss_rate=0.1, + initial_storage_level=1) + es.add(b_th, storage) om = solph.Model(es) self.compare_lp_files('nonequidistant_timeindex.lp', my_om=om) diff --git a/tests/lp_files/nonequidistant_timeindex.lp b/tests/lp_files/nonequidistant_timeindex.lp index 84bc077b2..d13e805b8 100644 --- a/tests/lp_files/nonequidistant_timeindex.lp +++ b/tests/lp_files/nonequidistant_timeindex.lp @@ -2,139 +2,150 @@ min objective: -+100 flow(gas_boiler_0) -+100 flow(gas_boiler_1) -+100 flow(gas_boiler_2) -+200 flow(gas_boiler_3) -+200 flow(gas_boiler_4) -+50 flow(gas_boiler_5) -+50 flow(gas_boiler_6) -+50 flow(gas_boiler_7) ++56 flow(heat_storage_0) ++56 flow(heat_storage_1) ++56 flow(heat_storage_2) ++112 flow(heat_storage_3) ++112 flow(heat_storage_4) ++28 flow(heat_storage_5) ++28 flow(heat_storage_6) ++28 flow(heat_storage_7) ++24 flow(storage_heat_0) ++24 flow(storage_heat_1) ++24 flow(storage_heat_2) ++48 flow(storage_heat_3) ++48 flow(storage_heat_4) ++12 flow(storage_heat_5) ++12 flow(storage_heat_6) ++12 flow(storage_heat_7) s.t. -c_e_Bus_balance(gas_0)_: -+1 flow(gas_boiler_0) -= 0 - -c_e_Bus_balance(gas_1)_: -+1 flow(gas_boiler_1) -= 0 - -c_e_Bus_balance(gas_2)_: -+1 flow(gas_boiler_2) -= 0 - -c_e_Bus_balance(gas_3)_: -+1 flow(gas_boiler_3) -= 0 - -c_e_Bus_balance(gas_4)_: -+1 flow(gas_boiler_4) -= 0 - -c_e_Bus_balance(gas_5)_: -+1 flow(gas_boiler_5) -= 0 - -c_e_Bus_balance(gas_6)_: -+1 flow(gas_boiler_6) -= 0 - -c_e_Bus_balance(gas_7)_: -+1 flow(gas_boiler_7) -= 0 - c_e_Bus_balance(heat_0)_: -+1 flow(boiler_heat_0) +-1 flow(heat_storage_0) ++1 flow(storage_heat_0) = 0 c_e_Bus_balance(heat_1)_: -+1 flow(boiler_heat_1) +-1 flow(heat_storage_1) ++1 flow(storage_heat_1) = 0 c_e_Bus_balance(heat_2)_: -+1 flow(boiler_heat_2) +-1 flow(heat_storage_2) ++1 flow(storage_heat_2) = 0 c_e_Bus_balance(heat_3)_: -+1 flow(boiler_heat_3) +-1 flow(heat_storage_3) ++1 flow(storage_heat_3) = 0 c_e_Bus_balance(heat_4)_: -+1 flow(boiler_heat_4) +-1 flow(heat_storage_4) ++1 flow(storage_heat_4) = 0 c_e_Bus_balance(heat_5)_: -+1 flow(boiler_heat_5) +-1 flow(heat_storage_5) ++1 flow(storage_heat_5) = 0 c_e_Bus_balance(heat_6)_: -+1 flow(boiler_heat_6) +-1 flow(heat_storage_6) ++1 flow(storage_heat_6) = 0 c_e_Bus_balance(heat_7)_: -+1 flow(boiler_heat_7) +-1 flow(heat_storage_7) ++1 flow(storage_heat_7) = 0 -c_e_Transformer_relation(boiler_gas_heat_0)_: --1 flow(boiler_heat_0) -+1 flow(gas_boiler_0) -= 0 +c_e_GenericStorageBlock_balance_first(storage)_: ++1 GenericStorageBlock_storage_content(storage_0) +-1 flow(heat_storage_0) ++1 flow(storage_heat_0) += 270 -c_e_Transformer_relation(boiler_gas_heat_1)_: --1 flow(boiler_heat_1) -+1 flow(gas_boiler_1) +c_e_GenericStorageBlock_balance(storage_1)_: +-0.90000000000000002 GenericStorageBlock_storage_content(storage_0) ++1 GenericStorageBlock_storage_content(storage_1) +-1 flow(heat_storage_1) ++1 flow(storage_heat_1) = 0 -c_e_Transformer_relation(boiler_gas_heat_2)_: --1 flow(boiler_heat_2) -+1 flow(gas_boiler_2) +c_e_GenericStorageBlock_balance(storage_2)_: +-0.90000000000000002 GenericStorageBlock_storage_content(storage_1) ++1 GenericStorageBlock_storage_content(storage_2) +-1 flow(heat_storage_2) ++1 flow(storage_heat_2) = 0 -c_e_Transformer_relation(boiler_gas_heat_3)_: --1 flow(boiler_heat_3) -+1 flow(gas_boiler_3) +c_e_GenericStorageBlock_balance(storage_3)_: +-0.81000000000000005 GenericStorageBlock_storage_content(storage_2) ++1 GenericStorageBlock_storage_content(storage_3) +-2 flow(heat_storage_3) ++2 flow(storage_heat_3) = 0 -c_e_Transformer_relation(boiler_gas_heat_4)_: --1 flow(boiler_heat_4) -+1 flow(gas_boiler_4) +c_e_GenericStorageBlock_balance(storage_4)_: +-0.81000000000000005 GenericStorageBlock_storage_content(storage_3) ++1 GenericStorageBlock_storage_content(storage_4) +-2 flow(heat_storage_4) ++2 flow(storage_heat_4) = 0 -c_e_Transformer_relation(boiler_gas_heat_5)_: --1 flow(boiler_heat_5) -+1 flow(gas_boiler_5) +c_e_GenericStorageBlock_balance(storage_5)_: +-0.94868329805051377 GenericStorageBlock_storage_content(storage_4) ++1 GenericStorageBlock_storage_content(storage_5) +-0.5 flow(heat_storage_5) ++0.5 flow(storage_heat_5) = 0 -c_e_Transformer_relation(boiler_gas_heat_6)_: --1 flow(boiler_heat_6) -+1 flow(gas_boiler_6) +c_e_GenericStorageBlock_balance(storage_6)_: +-0.94868329805051377 GenericStorageBlock_storage_content(storage_5) ++1 GenericStorageBlock_storage_content(storage_6) +-0.5 flow(heat_storage_6) ++0.5 flow(storage_heat_6) = 0 -c_e_Transformer_relation(boiler_gas_heat_7)_: --1 flow(boiler_heat_7) -+1 flow(gas_boiler_7) +c_e_GenericStorageBlock_balance(storage_7)_: +-0.94868329805051377 GenericStorageBlock_storage_content(storage_6) ++1 GenericStorageBlock_storage_content(storage_7) +-0.5 flow(heat_storage_7) ++0.5 flow(storage_heat_7) = 0 +c_e_GenericStorageBlock_balanced_cstr(storage)_: ++1 GenericStorageBlock_storage_content(storage_7) += 300 + c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(boiler_heat_0) <= 200 - 0 <= flow(boiler_heat_1) <= 200 - 0 <= flow(boiler_heat_2) <= 200 - 0 <= flow(boiler_heat_3) <= 200 - 0 <= flow(boiler_heat_4) <= 200 - 0 <= flow(boiler_heat_5) <= 200 - 0 <= flow(boiler_heat_6) <= 200 - 0 <= flow(boiler_heat_7) <= 200 - 0 <= flow(gas_boiler_0) <= +inf - 0 <= flow(gas_boiler_1) <= +inf - 0 <= flow(gas_boiler_2) <= +inf - 0 <= flow(gas_boiler_3) <= +inf - 0 <= flow(gas_boiler_4) <= +inf - 0 <= flow(gas_boiler_5) <= +inf - 0 <= flow(gas_boiler_6) <= +inf - 0 <= flow(gas_boiler_7) <= +inf -end + 0 <= flow(heat_storage_0) <= 100 + 0 <= flow(heat_storage_1) <= 100 + 0 <= flow(heat_storage_2) <= 100 + 0 <= flow(heat_storage_3) <= 100 + 0 <= flow(heat_storage_4) <= 100 + 0 <= flow(heat_storage_5) <= 100 + 0 <= flow(heat_storage_6) <= 100 + 0 <= flow(heat_storage_7) <= 100 + 0 <= flow(storage_heat_0) <= 100 + 0 <= flow(storage_heat_1) <= 100 + 0 <= flow(storage_heat_2) <= 100 + 0 <= flow(storage_heat_3) <= 100 + 0 <= flow(storage_heat_4) <= 100 + 0 <= flow(storage_heat_5) <= 100 + 0 <= flow(storage_heat_6) <= 100 + 0 <= flow(storage_heat_7) <= 100 + 0 <= GenericStorageBlock_storage_content(storage_0) <= 300 + 0 <= GenericStorageBlock_storage_content(storage_1) <= 300 + 0 <= GenericStorageBlock_storage_content(storage_2) <= 300 + 0 <= GenericStorageBlock_storage_content(storage_3) <= 300 + 0 <= GenericStorageBlock_storage_content(storage_4) <= 300 + 0 <= GenericStorageBlock_storage_content(storage_5) <= 300 + 0 <= GenericStorageBlock_storage_content(storage_6) <= 300 + 0 <= GenericStorageBlock_storage_content(storage_7) <= 300 +end \ No newline at end of file From 3961b429c8097ee612e626444f47955ec28dc7c5 Mon Sep 17 00:00:00 2001 From: "c.koehl" Date: Mon, 31 Aug 2020 12:27:12 +0200 Subject: [PATCH 0009/1363] Added another component to test A boiler was added to the tests to see the changes of a nonequidistant timeindex. Resolves: #701 --- tests/constraint_tests.py | 10 +- tests/lp_files/nonequidistant_timeindex.lp | 104 +++++++++++++++++++++ 2 files changed, 112 insertions(+), 2 deletions(-) diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index 3f868fcec..bed3a2c07 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -901,7 +901,7 @@ def test_nonconvex_invest_source_with_offset_no_minimum(self): offset=34, nonconvex=True))}) self.compare_lp_files('flow_invest_with_offset_no_minimum.lp') - def test_nonequidistant_timeindex(self): + def test_nonequidistant_storage(self): """Constraint test of an energysystem with nonequidistant timeindex""" idx2h = pd.date_range('1/1/2017', periods=3, freq='H') idxh = pd.date_range('1/1/2017 04:00:00', periods=2, freq='2H') @@ -910,7 +910,13 @@ def test_nonequidistant_timeindex(self): timeincrement = solph.helpers.calculate_timeincrement(timeindex) es = solph.EnergySystem(timeindex=timeindex, timeincrement=timeincrement) + b_gas = solph.Bus(label="gas") b_th = solph.Bus(label='heat') + boiler = solph.Transformer( + label="boiler", + inputs={b_gas: solph.Flow(variable_costs=100)}, + outputs={b_th: solph.Flow(nominal_value=200)} + ) storage = solph.GenericStorage( label='storage', inputs={b_th: solph.Flow(nominal_value=100, variable_costs=56)}, @@ -918,6 +924,6 @@ def test_nonequidistant_timeindex(self): nominal_storage_capacity=300, loss_rate=0.1, initial_storage_level=1) - es.add(b_th, storage) + es.add(b_gas, b_th, boiler, storage) om = solph.Model(es) self.compare_lp_files('nonequidistant_timeindex.lp', my_om=om) diff --git a/tests/lp_files/nonequidistant_timeindex.lp b/tests/lp_files/nonequidistant_timeindex.lp index d13e805b8..5fe723564 100644 --- a/tests/lp_files/nonequidistant_timeindex.lp +++ b/tests/lp_files/nonequidistant_timeindex.lp @@ -2,6 +2,14 @@ min objective: ++100 flow(gas_boiler_0) ++100 flow(gas_boiler_1) ++100 flow(gas_boiler_2) ++200 flow(gas_boiler_3) ++200 flow(gas_boiler_4) ++50 flow(gas_boiler_5) ++50 flow(gas_boiler_6) ++50 flow(gas_boiler_7) +56 flow(heat_storage_0) +56 flow(heat_storage_1) +56 flow(heat_storage_2) @@ -21,46 +29,126 @@ objective: s.t. +c_e_Bus_balance(gas_0)_: ++1 flow(gas_boiler_0) += 0 + +c_e_Bus_balance(gas_1)_: ++1 flow(gas_boiler_1) += 0 + +c_e_Bus_balance(gas_2)_: ++1 flow(gas_boiler_2) += 0 + +c_e_Bus_balance(gas_3)_: ++1 flow(gas_boiler_3) += 0 + +c_e_Bus_balance(gas_4)_: ++1 flow(gas_boiler_4) += 0 + +c_e_Bus_balance(gas_5)_: ++1 flow(gas_boiler_5) += 0 + +c_e_Bus_balance(gas_6)_: ++1 flow(gas_boiler_6) += 0 + +c_e_Bus_balance(gas_7)_: ++1 flow(gas_boiler_7) += 0 + c_e_Bus_balance(heat_0)_: ++1 flow(boiler_heat_0) -1 flow(heat_storage_0) +1 flow(storage_heat_0) = 0 c_e_Bus_balance(heat_1)_: ++1 flow(boiler_heat_1) -1 flow(heat_storage_1) +1 flow(storage_heat_1) = 0 c_e_Bus_balance(heat_2)_: ++1 flow(boiler_heat_2) -1 flow(heat_storage_2) +1 flow(storage_heat_2) = 0 c_e_Bus_balance(heat_3)_: ++1 flow(boiler_heat_3) -1 flow(heat_storage_3) +1 flow(storage_heat_3) = 0 c_e_Bus_balance(heat_4)_: ++1 flow(boiler_heat_4) -1 flow(heat_storage_4) +1 flow(storage_heat_4) = 0 c_e_Bus_balance(heat_5)_: ++1 flow(boiler_heat_5) -1 flow(heat_storage_5) +1 flow(storage_heat_5) = 0 c_e_Bus_balance(heat_6)_: ++1 flow(boiler_heat_6) -1 flow(heat_storage_6) +1 flow(storage_heat_6) = 0 c_e_Bus_balance(heat_7)_: ++1 flow(boiler_heat_7) -1 flow(heat_storage_7) +1 flow(storage_heat_7) = 0 +c_e_Transformer_relation(boiler_gas_heat_0)_: +-1 flow(boiler_heat_0) ++1 flow(gas_boiler_0) += 0 + +c_e_Transformer_relation(boiler_gas_heat_1)_: +-1 flow(boiler_heat_1) ++1 flow(gas_boiler_1) += 0 + +c_e_Transformer_relation(boiler_gas_heat_2)_: +-1 flow(boiler_heat_2) ++1 flow(gas_boiler_2) += 0 + +c_e_Transformer_relation(boiler_gas_heat_3)_: +-1 flow(boiler_heat_3) ++1 flow(gas_boiler_3) += 0 + +c_e_Transformer_relation(boiler_gas_heat_4)_: +-1 flow(boiler_heat_4) ++1 flow(gas_boiler_4) += 0 + +c_e_Transformer_relation(boiler_gas_heat_5)_: +-1 flow(boiler_heat_5) ++1 flow(gas_boiler_5) += 0 + +c_e_Transformer_relation(boiler_gas_heat_6)_: +-1 flow(boiler_heat_6) ++1 flow(gas_boiler_6) += 0 + +c_e_Transformer_relation(boiler_gas_heat_7)_: +-1 flow(boiler_heat_7) ++1 flow(gas_boiler_7) += 0 + c_e_GenericStorageBlock_balance_first(storage)_: +1 GenericStorageBlock_storage_content(storage_0) -1 flow(heat_storage_0) @@ -124,6 +212,22 @@ c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds + 0 <= flow(boiler_heat_0) <= 200 + 0 <= flow(boiler_heat_1) <= 200 + 0 <= flow(boiler_heat_2) <= 200 + 0 <= flow(boiler_heat_3) <= 200 + 0 <= flow(boiler_heat_4) <= 200 + 0 <= flow(boiler_heat_5) <= 200 + 0 <= flow(boiler_heat_6) <= 200 + 0 <= flow(boiler_heat_7) <= 200 + 0 <= flow(gas_boiler_0) <= +inf + 0 <= flow(gas_boiler_1) <= +inf + 0 <= flow(gas_boiler_2) <= +inf + 0 <= flow(gas_boiler_3) <= +inf + 0 <= flow(gas_boiler_4) <= +inf + 0 <= flow(gas_boiler_5) <= +inf + 0 <= flow(gas_boiler_6) <= +inf + 0 <= flow(gas_boiler_7) <= +inf 0 <= flow(heat_storage_0) <= 100 0 <= flow(heat_storage_1) <= 100 0 <= flow(heat_storage_2) <= 100 From 8a1582ed894207ce41a6cc217810c8a2dc6a0f73 Mon Sep 17 00:00:00 2001 From: uvchik Date: Wed, 2 Sep 2020 13:44:46 +0200 Subject: [PATCH 0010/1363] Add missing test for helpers module --- tests/test_helpers.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/test_helpers.py diff --git a/tests/test_helpers.py b/tests/test_helpers.py new file mode 100644 index 000000000..c33380a17 --- /dev/null +++ b/tests/test_helpers.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- + +"""This module is designed to test the helper functions.. + +SPDX-FileCopyrightText: Uwe Krien + +SPDX-License-Identifier: MIT + +""" + +import os + +from oemof.solph import helpers + + +def test_creation_of_extended_path(): + """Creation of a sub-folder based on the base path failed.""" + p = helpers.extend_basic_path("test_subfolder_X345qw34_tmp") + assert os.path.isdir(p) + os.rmdir(p) From 4f6208ee0792ea97c8ada06424c873e0a944b24a Mon Sep 17 00:00:00 2001 From: uvchik Date: Thu, 3 Jun 2021 09:57:21 +0200 Subject: [PATCH 0011/1363] Show failing references --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 459c9be43..e2c2e38e3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -55,7 +55,7 @@ def setup(app): napoleon_use_ivar = True napoleon_use_rtype = False napoleon_use_param = False -nitpicky = False +nitpicky = True exclude_patterns = ["_build", "whatsnew/*"] From 22b8b7fd2375cf5da668f1505c11a0e35eaa07ae Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 5 Jun 2021 16:12:58 +0200 Subject: [PATCH 0012/1363] Revise / fix docs for models module --- src/oemof/solph/models.py | 64 +++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/src/oemof/solph/models.py b/src/oemof/solph/models.py index 04bf0e0f8..d19b14498 100644 --- a/src/oemof/solph/models.py +++ b/src/oemof/solph/models.py @@ -7,6 +7,7 @@ SPDX-FileCopyrightText: Cord Kaldemeyer SPDX-FileCopyrightText: gplssm SPDX-FileCopyrightText: Patrik Schönfeldt +SPDX-FileCopyrightText: Johannes Kochems (jokochems) SPDX-License-Identifier: MIT @@ -37,7 +38,8 @@ class BaseModel(po.ConcreteModel): objective_weighting : array like (optional) Weights used for temporal objective function expressions. If nothing is passed `timeincrement` will be used which - is calculated from the freq length of the energy system timeindex . + is calculated from the freq length of the energy system timeindex or + can be directly passed as a sequence. auto_construct : boolean If this value is true, the set, variables, constraints, etc. are added, automatically when instantiating the model. For sequential model @@ -45,8 +47,8 @@ class BaseModel(po.ConcreteModel): and use methods `_add_parent_block_sets`, `_add_parent_block_variables`, `_add_blocks`, `_add_objective` - Attributes: - ----------- + Attributes + ---------- timeincrement : sequence Time increments. flows : dict @@ -57,14 +59,19 @@ class BaseModel(po.ConcreteModel): Energy system of the model. meta : `pyomo.opt.results.results_.SolverResults` or None Solver results. - dual : ... or None - rc : ... or None - + dual : `pyomo.core.base.suffix.Suffix` or None + Store the dual variables of the model if pyomo suffix is set to IMPORT + rc : `pyomo.core.base.suffix.Suffix` or None + Store the reduced costs of the model if pyomo suffix is set to IMPORT """ CONSTRAINT_GROUPS = [] + """The default list of constraint groups to be used for a model.""" def __init__(self, energysystem, **kwargs): + """Initialize a BaseModel, using its energysystem as well as + optional kwargs for specifying the timeincrement, objective_weigting + and constraint groups.""" super().__init__() # ######################## Arguments ################################# @@ -113,30 +120,33 @@ def __init__(self, energysystem, **kwargs): self._construct() def _construct(self): - """ """ + """Construct a BaseModel by adding parent block sets and variables + as well as child blocks and variables to it.""" self._add_parent_block_sets() self._add_parent_block_variables() self._add_child_blocks() self._add_objective() def _add_parent_block_sets(self): - """ " Method to create all sets located at the parent block, i.e. the - model itself as they are to be shared across all model components. + """Method to create all sets located at the parent block, i.e. in the + model itself, as they are to be shared across all model components. + See the class :py:class:~oemof.solph.models.Model for the sets created. """ pass def _add_parent_block_variables(self): - """ " Method to create all variables located at the parent block, + """Method to create all variables located at the parent block, i.e. the model itself as these variables are to be shared across - all model components. + all model components. See the class :py:class:~oemof.solph.models.Model + for the `flow` variable created. """ pass def _add_child_blocks(self): """Method to add the defined child blocks for components that have - been grouped in the defined constraint groups. + been grouped in the defined constraint groups. This collects all the + constraints from the component blocks and adds them to the model. """ - for group in self._constraint_groups: # create instance for block block = group() @@ -167,7 +177,6 @@ def receive_duals(self): """Method sets solver suffix to extract information about dual variables from solver. Shadow prices (duals) and reduced costs (rc) are set as attributes of the model. - """ # shadow prices self.dual = po.Suffix(direction=po.Suffix.IMPORT) @@ -175,7 +184,9 @@ def receive_duals(self): self.rc = po.Suffix(direction=po.Suffix.IMPORT) def results(self): - """Returns a nested dictionary of the results of this optimization""" + """Returns a nested dictionary of the results of this optimization. + See the processing module for more information on results extraction. + """ return processing.results(self) def solve(self, solver="cbc", solver_io="lp", **kwargs): @@ -184,7 +195,7 @@ def solve(self, solver="cbc", solver_io="lp", **kwargs): Parameters ---------- solver : string - solver to be used e.g. "glpk","gurobi","cplex" + solver to be used e.g. "cbc", "glpk","gurobi","cplex" solver_io : string pyomo solver interface file format: "lp","python","nl", etc. \**kwargs : keyword arguments @@ -198,10 +209,9 @@ def solve(self, solver="cbc", solver_io="lp", **kwargs): cmdline_options : dict Dictionary with command line options for solver e.g. {"mipgap":"0.01"} results in "--mipgap 0.01" - {"interior":" "} results in "--interior" - Gurobi solver takes numeric parameter values such as + \{"interior":" "} results in "--interior" + \Gurobi solver takes numeric parameter values such as {"method": 2} - """ solve_kwargs = kwargs.get("solve_kwargs", {}) solver_cmdline_options = kwargs.get("cmdline_options", {}) @@ -243,7 +253,7 @@ def relax_problem(self): class Model(BaseModel): - """An energy system model for operational and investment + """An energy system model for operational and/or investment optimization. Parameters @@ -253,17 +263,18 @@ class Model(BaseModel): constraint_groups : list Solph looks for these groups in the given energy system and uses them to create the constraints of the optimization problem. - Defaults to `Model.CONSTRAINTS` + Defaults to `Model.CONSTRAINT_GROUPS` + **The following basic sets are created**: - NODES : + NODES A set with all nodes of the given energy system. - TIMESTEPS : + TIMESTEPS A set with all timesteps of the given time horizon. - FLOWS : + FLOWS A 2 dimensional set with all flows. Index: `(source, target)` **The following basic variables are created**: @@ -287,7 +298,7 @@ def __init__(self, energysystem, **kwargs): super().__init__(energysystem, **kwargs) def _add_parent_block_sets(self): - """ """ + """Add all basic sets to the model, i.e. NODES, TIMESTEPS and FLOWS.""" # set with all nodes self.NODES = po.Set(initialize=[n for n in self.es.nodes]) @@ -330,7 +341,8 @@ def _add_parent_block_sets(self): ) def _add_parent_block_variables(self): - """ """ + """Add the parent block variables, which is the `flow` variable, + indexed by FLOWS and TIMESTEPS.""" self.flow = po.Var(self.FLOWS, self.TIMESTEPS, within=po.Reals) for (o, i) in self.FLOWS: From 423b76cdf993e98963840513351ced249ae01ff9 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 5 Jun 2021 16:30:24 +0200 Subject: [PATCH 0013/1363] Add minor addition for docs of options module --- src/oemof/solph/options.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/oemof/solph/options.py b/src/oemof/solph/options.py index 3a52dd298..d94a11148 100644 --- a/src/oemof/solph/options.py +++ b/src/oemof/solph/options.py @@ -17,7 +17,9 @@ class Investment: - """ + """Defines an Investment object holding all the specifications needed + for investment modeling. + Parameters ---------- maximum : float, :math:`P_{invest,max}` or :math:`E_{invest,max}` @@ -41,8 +43,12 @@ class Investment: For the variables, constraints and parts of the objective function, which - are created, see :class:`oemof.solph.blocks.investment_flow.InvestmentFlow` - and :class:`oemof.solph.components.generic_storage.GenericInvestmentStorageBlock`. + are created, see + :py:class:`~oemof.solph.blocks.investment_flow.InvestmentFlow`, + :py:class:`~oemof.solph.components.generic_storage.GenericInvestmentStorageBlock` + :py:class:`~oemof.solph.custom.sink_dsm.SinkDSMOemofInvestmentBlock`, + :py:class:`~oemof.solph.custom.sink_dsm.SinkDSMDLRInvestmentBlock` and + :py:class:`~oemof.solph.custom.sink_dsm.SinkDSMDIWInvestmentBlock`. """ # noqa: E501 @@ -103,7 +109,9 @@ def _check_invest_attributes_offset(self): class NonConvex: - """ + """Defines a NonConvex object holding all the specifications for NonConvex + Flows, i.e. Flows with binary variables associated to them. + Parameters ---------- startup_costs : numeric (iterable or scalar) @@ -122,9 +130,9 @@ class NonConvex: Be aware that minimum up and downtimes can contradict each other and may to infeasible problems. maximum_startups : numeric (0 or positive integer) - Maximum number of start-ups. + Maximum number of start-ups in the optimization timeframe. maximum_shutdowns : numeric (0 or positive integer) - Maximum number of shutdowns. + Maximum number of shutdowns in the optimization timeframe. initial_status : numeric (0 or 1) Integer value indicating the status of the flow in the first time step (0 = off, 1 = on). For minimum up and downtimes, the initial status @@ -138,9 +146,9 @@ class NonConvex: A dictionary containing the following two keys: * `'ub'`: numeric (iterable, scalar or None), the normed *upper - bound* on the positive difference (`flow[t-1] < flow[t]`) of + bound* of the positive difference (`flow[t-1] < flow[t]`) of two consecutive flow values. - * `'costs``: numeric (scalar or None), the gradient cost per + * `'costs'`: numeric (scalar or None), the gradient cost per unit. negative_gradient : :obj:`dict`, default: `{'ub': None, 'costs': 0}` @@ -149,7 +157,7 @@ class NonConvex: * `'ub'`: numeric (iterable, scalar or None), the normed *upper bound* on the negative difference (`flow[t-1] > flow[t]`) of two consecutive flow values. - * `'costs``: numeric (scalar or None), the gradient cost per + * `'costs'`: numeric (scalar or None), the gradient cost per unit. """ From 0b98a83de8ea735c39d8cf4845416d13e4050b61 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 11 Jun 2021 11:31:21 +0200 Subject: [PATCH 0014/1363] Begin revising docs for SinkDSM --- src/oemof/solph/custom/sink_dsm.py | 113 ++++++++++++++++------------- 1 file changed, 63 insertions(+), 50 deletions(-) diff --git a/src/oemof/solph/custom/sink_dsm.py b/src/oemof/solph/custom/sink_dsm.py index 067e74f32..2e57fb1d8 100644 --- a/src/oemof/solph/custom/sink_dsm.py +++ b/src/oemof/solph/custom/sink_dsm.py @@ -1,7 +1,11 @@ # -*- coding: utf-8 -*- """ -In-development functionality for demand-side management. +Implementation of demand-side management (demand response) which allows for + +* modeling load shifting and/or shedding of a given baseline demand, +* assessing both, a pure dispatch and an investment model and +* choosing among different (storage-alike) implementations. SPDX-FileCopyrightText: Uwe Krien SPDX-FileCopyrightText: Simon Hilpert @@ -11,6 +15,7 @@ SPDX-FileCopyrightText: jakob-wo SPDX-FileCopyrightText: gplssm SPDX-FileCopyrightText: jnnr +SPDX-FileCopyrightText: Julian Endres SPDX-FileCopyrightText: Johannes Kochems (jokochems) SPDX-License-Identifier: MIT @@ -34,38 +39,42 @@ class SinkDSM(Sink): r""" - Demand Side Management implemented as Sink with flexibility potential. + Demand Side Management implemented as a Sink with flexibility potential + to deviate from the baseline demand in upwards or downwards direction. There are several approaches possible which can be selected: - - DIW: Based on the paper by Zerrahn, Alexander and Schill, Wolf-Peter - (2015): `On the representation of demand-side management in power system - models `_, - in: Energy (84), pp. 840-845, 10.1016/j.energy.2015.03.037, - accessed 08.01.2021, pp. 842-843. - - DLR: Based on the PhD thesis of Gils, Hans Christian (2015): - `Balancing of Intermittent Renewable Power Generation by Demand Response - and Thermal Energy Storage`, Stuttgart, - , - accessed 08.01.2021, pp. 67-70. - - oemof: Created by Julian Endres. A fairly simple DSM representation which - demands the energy balance to be levelled out in fixed cycles + + * DIW: Based on the paper by Zerrahn, Alexander and Schill, Wolf-Peter + (2015): `On the representation of demand-side management in power system + models, in: Energy (84), pp. 840-845, + 10.1016/j.energy.2015.03.037 + `_, + accessed 08.01.2021, pp. 842-843. + * DLR: Based on the PhD thesis of Gils, Hans Christian (2015): + `Balancing of Intermittent Renewable Power Generation by Demand Response + and Thermal Energy Storage`, Stuttgart, + ``_, + accessed 08.01.2021, pp. 67-70. + * oemof: Created by Julian Endres. A fairly simple DSM representation which + demands the energy balance to be levelled out in fixed cycles An evaluation of different modeling approaches has been carried out and presented at the INREC 2020. Some of the results are as follows: - - DIW: A solid implementation with the tendency of slight overestimization - of potentials since a shift_time is not accounted for. It may get - computationally expensive due to a high time-interlinkage in constraint - formulations. - - DLR: An extensive modeling approach for demand response which neither - leads to an over- nor underestimization of potentials and balances modeling - detail and computation intensity. :attr:`fixes` and :attr:`addition` should - both be set to True which is the default value. - - oemof: A very computationally efficient approach which only requires the - energy balance to be levelled out in certain intervals. If demand response - is not at the center of the research and/or parameter availability is - limited, this approach should be chosen. Note that approach `oemof` does - allow for load shedding, but does not impose a limit on maximum amount of - shedded energy. + + * DIW: A solid implementation with the tendency of slight overestimization + of potentials since a `shift_time` is not included. It may get + computationally expensive due to a high time-interlinkage in constraint + formulations. + * DLR: An extensive modeling approach for demand response which neither + leads to an over- nor underestimization of potentials and balances + modeling detail and computation intensity. `fixes` and + `addition` should both be set to True which is the default value. + * oemof: A very computationally efficient approach which only requires the + energy balance to be levelled out in certain intervals. If demand + response is not at the center of the research and/or parameter + availability is limited, this approach should be chosen. + Note that approach `oemof` does allow for load shedding, + but does not impose a limit on maximum amount of shedded energy. SinkDSM adds additional constraints that allow to shift energy in certain time window constrained by :attr:`~capacity_up` and @@ -83,7 +92,7 @@ class SinkDSM(Sink): maximum DSM capacity that may be increased (normalized) capacity_down: int or array maximum DSM capacity that may be reduced (normalized) - approach: 'oemof', 'DIW', 'DLR' + approach: str, one of 'oemof', 'DIW', 'DLR' Choose one of the DSM modeling approaches. Read notes about which parameters to be applied for which approach. @@ -443,11 +452,11 @@ class SinkDSMOemofBlock(SimpleBlock): .. math:: & - (1) \quad DSM_{t}^{up} = 0 \quad \forall t - \quad if \space eligibility_{shift} = False \\ + (1) \quad DSM_{t}^{up} = 0 \\ + \forall t \quad if \space e_{shift} = False \\ & (2) \quad DSM_{t}^{do, shed} = 0 \quad \forall t - \quad if \space eligibility_{shed} = False \\ + \quad if \space e_{shed} = False \\ & (3) \quad \dot{E}_{t} = demand_{t} \cdot demand_{max} + DSM_{t}^{up} - DSM_{t}^{do, shift} - DSM_{t}^{do, shed} @@ -475,25 +484,28 @@ class SinkDSMOemofBlock(SimpleBlock): **Table: Symbols and attribute names of variables and parameters** + apparently, this won't be rendered + + ============================= ===================== ==== ======================================= + symbol attribute type explanation + ============================= ===================== ==== ======================================= + :math:`DSM_{t}^{up}` `dsm_up[g, t]` V DSM up shift (capacity shifted upwards) + :math:`DSM_{t}^{do, shift}` `dsm_do_shift[g, t]` V DSM down shift (capacity shifted downwards) + :math:`DSM_{t}^{do, shed}` `dsm_do_shed[g, t]` V DSM shedded (capacity shedded, i.e. not compensated for) + :math:`\dot{E}_{t}` `SinkDSM.inputs` V Energy flowing in from (electrical) inflow bus + :math:`demand_{t}` `demand[t]` P (Electrical) demand series (normalized) + :math:`demand_{max}` `max_demand` P Maximum demand value + :math:`E_{t}^{do}` `capacity_down[t]` P Capacity allowed for a load adjustment downwards + (normalized; shifting + shedding) + ============================= ===================== ==== ======================================= + .. csv-table:: Variables (V) and Parameters (P) :header: "symbol", "attribute", "type", "explanation" :widths: 1, 1, 1, 1 - ":math:`DSM_{t}^{up}` ", - ":attr:`~SinkDSM.dsm_up[g, t]` ","V", "DSM - up shift (capacity shifted upwards)" - ":math:`DSM_{t}^{do, shift}` ", - ":attr:`~SinkDSM.dsm_do_shift[g, t]` ", - "V","DSM down shift (capacity shifted downwards)" - ":math:`DSM_{t}^{do, shed}` ", - ":attr:`~SinkDSM.dsm_do_shed[g, t]` ", - "V","DSM shedded (capacity shedded, i.e. not compensated for)" - ":math:`\dot{E}_{t}`",":attr:`~SinkDSM.inputs`","V", "Energy - flowing in from (electrical) inflow bus" - ":math:`demand_{t}`",":attr:`~SinkDSM.demand[t]`","P", - "(Electrical) demand series (normalized)" - ":math:`demand_{max}`",":attr:`~SinkDSM.max_demand`","P", - "Maximum demand value" + + + ":math:`E_{t}^{do}`",":attr:`~SinkDSM.capacity_down[t]`","P", "Capacity allowed for a load adjustment downwards (normalized) (DSM down shift + DSM shedded)" @@ -510,11 +522,11 @@ class SinkDSMOemofBlock(SimpleBlock): ":math:`\eta`",":attr:`~SinkDSM.efficiency`","P", "Efficiency loss forload shifting processes" ":math:`\mathbb{T}` "," ","P", "Time steps" - ":math:`eligibility_{shift}` ", + ":math:`e_{shift}` ", ":attr:`~SinkDSM.shift_eligibility`","P", "Boolean parameter indicating if unit can be used for load shifting" - ":math:`eligibility_{shed}` ", + ":math:`e_{shed}` ", ":attr:`~SinkDSM.shed_eligibility`","P", "Boolean parameter indicating if unit can be used for load shedding" @@ -526,7 +538,8 @@ class SinkDSMOemofBlock(SimpleBlock): ":math:`cost_{t}^{dsm, do, shed}` ", ":attr:`~SinkDSM.cost_dsm_down_shed[t]`","P", "Variable costs for shedding load" - """ + + """ # noqa: E501 CONSTRAINT_GROUP = True def __init__(self, *args, **kwargs): From 28d0cc3c00ef2010ca52d06395070ccea8e10bc4 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 11 Jun 2021 17:22:57 +0200 Subject: [PATCH 0015/1363] Extend and tidy up docs of sink_dsm module - fix broken equations - fix alignment, line breaks - pretty everything up - fix typos - add explanation - change from broken unreadable csv tables to readable standard tables - disable checking for line length since tables exceed maximum line length --- src/oemof/solph/custom/sink_dsm.py | 1074 ++++++++++++++-------------- 1 file changed, 555 insertions(+), 519 deletions(-) diff --git a/src/oemof/solph/custom/sink_dsm.py b/src/oemof/solph/custom/sink_dsm.py index 2e57fb1d8..9f87f89ae 100644 --- a/src/oemof/solph/custom/sink_dsm.py +++ b/src/oemof/solph/custom/sink_dsm.py @@ -3,7 +3,8 @@ """ Implementation of demand-side management (demand response) which allows for -* modeling load shifting and/or shedding of a given baseline demand, +* modeling load shifting and/or shedding of a given baseline demand + for a demand response portfolio, * assessing both, a pure dispatch and an investment model and * choosing among different (storage-alike) implementations. @@ -45,9 +46,9 @@ class SinkDSM(Sink): There are several approaches possible which can be selected: * DIW: Based on the paper by Zerrahn, Alexander and Schill, Wolf-Peter - (2015): `On the representation of demand-side management in power system + (2015): On the representation of demand-side management in power system models, in: Energy (84), pp. 840-845, - 10.1016/j.energy.2015.03.037 + `10.1016/j.energy.2015.03.037 `_, accessed 08.01.2021, pp. 842-843. * DLR: Based on the PhD thesis of Gils, Hans Christian (2015): @@ -77,8 +78,7 @@ class SinkDSM(Sink): but does not impose a limit on maximum amount of shedded energy. SinkDSM adds additional constraints that allow to shift energy in certain - time window constrained by :attr:`~capacity_up` and - :attr:`~capacity_down`. + time window constrained by `capacity_up` and `capacity_down`. Parameters ---------- @@ -99,33 +99,38 @@ class SinkDSM(Sink): oemof : Simple model in which the load shift must be compensated in a - predefined fixed interval (:attr:`~shift_interval` is mandatory). - Within time windows of the length :attr:`~shift_interval` DSM - up and down shifts are balanced. See - :class:`~SinkDSMOemofBlock` for details. + predefined fixed interval (`shift_interval` is mandatory). + Within time windows of the length `shift_interval` DSM + up and down shifts are balanced. For details see + :class:`~SinkDSMOemofBlock` resp. + :class:`~SinkDSMOemofInvestmentBlock`. DIW : Sophisticated model based on the formulation by Zerrahn & Schill (2015a). The load shift of the component must be - compensated in a predefined delay time (:attr:`~delay_time` is + compensated in a predefined delay time (`delay_time` is mandatory). - For details see :class:`~SinkDSMDIWBlock`. + For details see + :class:`~SinkDSMDIWBlock` resp. + :class:`~SinkDSMDIWInvestmentBlock`. DLR : Sophisticated model based on the formulation by Gils (2015). The load shift of the component must be - compensated in a predefined delay time (:attr:`~delay_time` is + compensated in a predefined delay time (`delay_time` is mandatory). - For details see :class:`~SinkDSMDLRBlock`. + For details see + :class:`~SinkDSMDLRBlock` resp. + :class:`~SinkDSMDLRInvestmentBlock`. shift_interval: int - Only used when :attr:`~approach` is set to 'oemof'. Otherwise, can be + Only used when `approach` is set to "oemof". Otherwise, can be None. It's the interval in which between :math:`DSM_{t}^{up}` and :math:`DSM_{t}^{down}` have to be compensated. delay_time: int - Only used when :attr:`~approach` is set to 'DIW' or 'DLR'. Otherwise, + Only used when `approach` is set to "DIW" or "DLR". Otherwise, can be None. Length of symmetrical time windows around :math:`t` in which :math:`DSM_{t}^{up}` and :math:`DSM_{t,tt}^{down}` have to be @@ -133,11 +138,11 @@ class SinkDSM(Sink): Note: For approach 'DLR', an iterable is constructed in order to model flexible delay times shift_time: int - Only used when :attr:`~approach` is set to 'DLR'. + Only used when `approach` is set to "DLR". Duration of a single upwards or downwards shift (half a shifting cycle if there is immediate compensation) shed_time: int - Only used when :attr:`~shed_eligibility` is set to True. + Only used when `shed_eligibility` is set to True. Maximum length of a load shedding process at full capacity (used within energy limit constraint) max_demand: numeric @@ -165,40 +170,40 @@ class SinkDSM(Sink): efficiency : float Efficiency factor for load shifts (between 0 and 1) recovery_time_shift : int - Only used when :attr:`~approach` is set to 'DIW'. + Only used when `approach` is set to "DIW". Minimum time between the end of one load shifting process and the start of another for load shifting processes recovery_time_shed : int - Only used when :attr:`~approach` is set to 'DIW'. Minimum time between the end of one load shifting process and the start of another for load shedding processes ActivateYearLimit : boolean - Only used when :attr:`~approach` is set to 'DLR'. + Only used when `approach` is set to "DLR". Control parameter; activates constraints for year limit if set to True ActivateDayLimit : boolean - Only used when :attr:`~approach` is set to 'DLR'. + Only used when `approach` is set to "DLR". Control parameter; activates constraints for day limit if set to True n_yearLimit_shift : int - Only used when :attr:`~approach` is set to 'DLR'. + Only used when `approach` is set to "DLR". Maximum number of load shifts at full capacity per year, used to limit the amount of energy shifted per year. Optional parameter that is only needed when ActivateYearLimit is True n_yearLimit_shed : int - Only used when :attr:`~approach` is set to 'DLR'. + Only used when `approach` is set to "DLR". Maximum number of load sheds at full capacity per year, used to limit the amount of energy shedded per year. Mandatory parameter if load - shedding is allowed by setting shed_eligibility to True + shedding is allowed by setting `shed_eligibility` to True t_dayLimit: int - Only used when :attr:`~approach` is set to 'DLR'. + Only used when `approach` is set to "DLR". Maximum duration of load shifts at full capacity per day, used to limit the amount of energy shifted per day. Optional parameter that is only needed when ActivateDayLimit is True addition : boolean - Only used when :attr:`~approach` is set to 'DLR'. + Only used when `approach` is set to "DLR". Boolean parameter indicating whether or not to include additional - constraint (which corresponds to Eq. 10 from Zerrahn and Schill (2015a) + constraint (which corresponds to Eq. 10 + from Zerrahn and Schill (2015a)) fixes : boolean - Only used when :attr:`~approach` is set to 'DLR'. + Only used when `approach` is set to "DLR". Boolean parameter indicating whether or not to include additional fixes. These comprise prohibiting shifts which cannot be balanced within the optimization timeframe @@ -212,20 +217,26 @@ class SinkDSM(Sink): Note ---- - * :attr:`method` has been renamed to :attr:`approach`. - * As many constraints and dependencies are created in approach 'DIW', - computational cost might be high with a large 'delay_time' and with model - of high temporal resolution - * The approach 'DLR' preforms better in terms of calculation time, - compared to the approach 'DIW' - * Using :attr:`~approach` 'DIW' or 'DLR' might result in demand shifts that + * When you set up a dispatch model, you have to specify `max_capacity_up`, + `max_capacity_down` and `max_demand`. Don't set `flex_share_up` + and `flex_share_down` which shall only used for investment modeling. + * When using the investment mode, you have to specify `flex_share_up` + and `flex_share_down` instead of `max_capacity_up`, + `max_capacity_down` and `max_demand`. + * `method` has been renamed to `approach`. + * As many constraints and dependencies are created in approach "DIW", + computational cost might be high with a large `delay_time` and with model + of high temporal resolution. + * The approach "DLR" preforms better in terms of calculation time, + compared to the approach "DIW". + * Using `approach` "DIW" or "DLR" might result in demand shifts that exceed the specified delay time by activating up and down simultaneously in the time steps between to DSM events. Thus, the purpose of this component is to model demand response portfolios rather than individual demand units. * It's not recommended to assign cost to the flow that connects - :class:`~SinkDSM` with a bus. Instead, use :attr:`~SinkDSM.cost_dsm_up` - or :attr:`~cost_dsm_down_shift` + :class:`~SinkDSM` with a bus. Instead, use `cost_dsm_up` + or `cost_dsm_down_shift`. * Variable costs may be attributed to upshifts, downshifts or both. Costs for shedding may deviate from that for shifting (usually costs for shedding are much larger and equal to the value @@ -446,98 +457,84 @@ def constraint_group(self): class SinkDSMOemofBlock(SimpleBlock): r"""Constraints for SinkDSM with "oemof" approach - **The following constraints are created for approach = 'oemof':** + **The following constraints are created for approach = "oemof":** - .. _SinkDSMOemof equations: + .. _SinkDSMOemofBlock equations: .. math:: & (1) \quad DSM_{t}^{up} = 0 \\ - \forall t \quad if \space e_{shift} = False \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shift} = \textrm{False} \\ + & \\ & - (2) \quad DSM_{t}^{do, shed} = 0 \quad \forall t - \quad if \space e_{shed} = False \\ + (2) \quad DSM_{t}^{do, shed} = 0 \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shed} = \textrm{False} \\ + & \\ & (3) \quad \dot{E}_{t} = demand_{t} \cdot demand_{max} + DSM_{t}^{up} - - DSM_{t}^{do, shift} - DSM_{t}^{do, shed} - \quad \forall t \in \mathbb{T} \\ + - DSM_{t}^{do, shift} - DSM_{t}^{do, shed} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & - (4) \quad DSM_{t}^{up} \leq E_{t}^{up} \cdot E_{up, max} - \quad \forall t \in \mathbb{T} \\ + (4) \quad DSM_{t}^{up} \leq E_{t}^{up} \cdot E_{up, max} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (5) \quad DSM_{t}^{do, shift} + DSM_{t}^{do, shed} - \leq E_{t}^{do} \cdot E_{do, max} - \quad \forall t \in \mathbb{T} \\ + \leq E_{t}^{do} \cdot E_{do, max} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (6) \quad \sum_{t=t_s}^{t_s+\tau} DSM_{t}^{up} \cdot \eta = - \sum_{t=t_s}^{t_s+\tau} DSM_{t}^{do, shift} \quad \forall t_s \in - \{k \in \mathbb{T} \mid k \mod \tau = 0\} \\ - & + \sum_{t=t_s}^{t_s+\tau} DSM_{t}^{do, shift} \\ + & \quad \quad \quad \quad \forall t_s \in \{k \in \mathbb{T} + \mid k \mod \tau = 0\} \\ **The following parts of the objective function are created:** .. math:: - DSM_{t}^{up} \cdot cost_{t}^{dsm, up} + & + (DSM_{t}^{up} \cdot cost_{t}^{dsm, up} + DSM_{t}^{do, shift} \cdot cost_{t}^{dsm, do, shift} - + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed} - \quad \forall t \in \mathbb{T} \\ + + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed}) + \cdot \omega_{t} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ **Table: Symbols and attribute names of variables and parameters** - apparently, this won't be rendered - - ============================= ===================== ==== ======================================= - symbol attribute type explanation - ============================= ===================== ==== ======================================= - :math:`DSM_{t}^{up}` `dsm_up[g, t]` V DSM up shift (capacity shifted upwards) - :math:`DSM_{t}^{do, shift}` `dsm_do_shift[g, t]` V DSM down shift (capacity shifted downwards) - :math:`DSM_{t}^{do, shed}` `dsm_do_shed[g, t]` V DSM shedded (capacity shedded, i.e. not compensated for) - :math:`\dot{E}_{t}` `SinkDSM.inputs` V Energy flowing in from (electrical) inflow bus - :math:`demand_{t}` `demand[t]` P (Electrical) demand series (normalized) - :math:`demand_{max}` `max_demand` P Maximum demand value - :math:`E_{t}^{do}` `capacity_down[t]` P Capacity allowed for a load adjustment downwards - (normalized; shifting + shedding) - ============================= ===================== ==== ======================================= - - .. csv-table:: Variables (V) and Parameters (P) - :header: "symbol", "attribute", "type", "explanation" - :widths: 1, 1, 1, 1 - - - - - ":math:`E_{t}^{do}`",":attr:`~SinkDSM.capacity_down[t]`","P", - "Capacity allowed for a load adjustment downwards (normalized) - (DSM down shift + DSM shedded)" - ":math:`E_{t}^{up}`",":attr:`~SinkDSM.capacity_up[t]`","P", - "Capacity allowed for a shift upwards (normalized) (DSM up shift)" - ":math:`E_{do, max}`",":attr:`~SinkDSM.max_capacity_down`","P", - "Maximum capacity allowed for a load adjustment downwards - (DSM down shift + DSM shedded)" - ":math:`E_{up, max}`",":attr:`~SinkDSM.max_capacity_up`","P", - "Capacity allowed for a shift upwards (normalized) (DSM up shift)" - ":math:`\tau`",":attr:`~SinkDSM.shift_interval`","P", "Shift - interval (time within which the energy balance must be - levelled out" - ":math:`\eta`",":attr:`~SinkDSM.efficiency`","P", "Efficiency - loss forload shifting processes" - ":math:`\mathbb{T}` "," ","P", "Time steps" - ":math:`e_{shift}` ", - ":attr:`~SinkDSM.shift_eligibility`","P", - "Boolean parameter indicating if unit can be used for - load shifting" - ":math:`e_{shed}` ", - ":attr:`~SinkDSM.shed_eligibility`","P", - "Boolean parameter indicating if unit can be used for - load shedding" - ":math:`cost_{t}^{dsm, up}` ", ":attr:`~SinkDSM.cost_dsm_up[t]`", - "P", "Variable costs for an upwards shift" - ":math:`cost_{t}^{dsm, do, shift}` ", - ":attr:`~SinkDSM.cost_dsm_down_shift[t]`","P", - "Variable costs for a downwards shift (load shifting)" - ":math:`cost_{t}^{dsm, do, shed}` ", - ":attr:`~SinkDSM.cost_dsm_down_shed[t]`","P", - "Variable costs for shedding load" + .. table:: Variables (V) and Parameters (P) + :widths: 1, 1, 1, 1 + + ================================= ======================== ==== ======================================= + symbol attribute type explanation + ================================= ======================== ==== ======================================= + :math:`DSM_{t}^{up}` `dsm_up[g, t]` V DSM up shift (capacity shifted upwards) + :math:`DSM_{t}^{do, shift}` `dsm_do_shift[g, t]` V DSM down shift (capacity shifted downwards) + :math:`DSM_{t}^{do, shed}` `dsm_do_shed[g, t]` V DSM shedded (capacity shedded, i.e. not compensated for) + :math:`\dot{E}_{t}` `SinkDSM.inputs` V Energy flowing in from (electrical) inflow bus + :math:`demand_{t}` `demand[t]` P (Electrical) demand series (normalized) + :math:`demand_{max}` `max_demand` P Maximum demand value + :math:`E_{t}^{do}` `capacity_down[t]` P | Capacity allowed for a load adjustment downwards + | (normalized; shifting + shedding) + :math:`E_{t}^{up}` `capacity_up[t]` P Capacity allowed for a shift upwards (normalized) + :math:`E_{do, max}` `max_capacity_down` P | Maximum capacity allowed for a load adjustment downwards + | (shifting + shedding) + :math:`E_{up, max}` `max_capacity_up` P Maximum capacity allowed for a shift upwards + :math:`\tau` `shift_interval` P | interval (time within which the + | energy balance must be levelled out) + :math:`\eta` `efficiency` P Efficiency for load shifting processes + :math:`\mathbb{T}` P Time steps of the model + :math:`e_{shift}` `shift_eligibility` P | Boolean parameter indicating if unit can be used + | for load shifting + :math:`e_{shed}` `shed_eligibility` P | Boolean parameter indicating if unit can be used + | for load shedding + :math:`cost_{t}^{dsm, up}` `cost_dsm_up[t]` P Variable costs for an upwards shift + :math:`cost_{t}^{dsm, do, shift}` `cost_dsm_down_shift[t]` P Variable costs for a downwards shift (load shifting) + :math:`cost_{t}^{dsm, do, shed}` `cost_dsm_down_shift[t]` P Variable costs for shedding load + :math:`\omega_{t}` P Objective weighting of the model for timestep t + ================================= ======================== ==== ======================================= """ # noqa: E501 CONSTRAINT_GROUP = True @@ -734,88 +731,94 @@ def _objective_expression(self): class SinkDSMOemofInvestmentBlock(SimpleBlock): - r"""Constraints for SinkDSM with "oemof" approach and :attr:`investment` + r"""Constraints for SinkDSM with "oemof" approach and `investment` + defined - **The following constraints are created for approach = 'oemof' with an - investment object defined:** + **The following constraints are created for approach = "oemof" + with an investment object defined:** - .. _SinkDSMOemof equations: + .. _SinkDSMOemofInvestmentBlock equations: .. math:: & (1) \quad invest_{min} \leq invest \leq invest_{max} \\ + & \\ & - (2) \quad DSM_{t}^{up} = 0 \quad \forall t - \quad if \space eligibility_{shift} = False \\ + (2) \quad DSM_{t}^{up} = 0 \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shift} = \textrm{False} \\ + & \\ & - (3) \quad DSM_{t}^{do, shed} = 0 \quad \forall t - \quad if \space eligibility_{shed} = False \\ + (3) \quad DSM_{t}^{do, shed} = 0 \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shed} = \textrm{False} \\ + & \\ & (4) \quad \dot{E}_{t} = demand_{t} \cdot (invest + E_{exist}) - + DSM_{t}^{up} - - DSM_{t}^{do, shift} - DSM_{t}^{do, shed} - \quad \forall t \in \mathbb{T} \\ + + DSM_{t}^{up} - DSM_{t}^{do, shift} - DSM_{t}^{do, shed} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (5) \quad DSM_{t}^{up} \leq E_{t}^{up} \cdot (invest + E_{exist}) - \cdot s_{flex, up} - \quad \forall t \in \mathbb{T} \\ + \cdot s_{flex, up} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (6) \quad DSM_{t}^{do, shift} + DSM_{t}^{do, shed} \leq - E_{t}^{do} \cdot (invest + E_{exist}) \cdot s_{flex, do} - \quad \forall t \in \mathbb{T} \\ + E_{t}^{do} \cdot (invest + E_{exist}) \cdot s_{flex, do} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (7) \quad \sum_{t=t_s}^{t_s+\tau} DSM_{t}^{up} \cdot \eta = - \sum_{t=t_s}^{t_s+\tau} DSM_{t}^{do, shift} \quad \forall t_s \in + \sum_{t=t_s}^{t_s+\tau} DSM_{t}^{do, shift} \\ + & \quad \quad \quad \quad \forall t_s \in \{k \in \mathbb{T} \mid k \mod \tau = 0\} \\ - & **The following parts of the objective function are created:** * Investment annuity: .. math:: + & invest \cdot costs_{invest} \\ * Variable costs: .. math:: - DSM_{t}^{up} \cdot cost_{t}^{dsm, up} + & + (DSM_{t}^{up} \cdot cost_{t}^{dsm, up} + DSM_{t}^{do, shift} \cdot cost_{t}^{dsm, do, shift} - + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed} - \quad \forall t \in \mathbb{T} \\ + + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed}) + \cdot \omega_{t} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ - See remarks in :class:`oemof.solph.custom.SinkDSMOemofBlock`. + See remarks in :class:`~oemof.solph.custom.sink_dsm.SinkDSMOemofBlock`. **Symbols and attribute names of variables and parameters** - Please refer to :class:`oemof.solph.custom.SinkDSMOemofBlock`. - - The following variables and parameters are exclusively used for - investment modeling: + * Please refer to :class:`~oemof.solph.custom.sink_dsm.SinkDSMOemofBlock` + for a variables and parameter description. + * The following variables and parameters are exclusively used for + investment modeling: + + .. table:: Variables (V) and Parameters (P) + :widths: 1, 1, 1, 1 + + ================================= ======================== ==== ======================================= + symbol attribute type explanation + ================================= ======================== ==== ======================================= + :math:`invest` `invest` V | DSM capacity invested in + | Equals to the additionally installed capacity. + | The capacity share eligible for a shift is determined by flex share(s). + :math:`invest_{min}` `investment.minimum` P minimum investment + :math:`invest_{max}` `investment.maximum` P maximum investment + :math:`E_{exist}` `investment.existing` P existing DSM capacity + :math:`s_{flex, up}` `flex_share_up` P share of invested capacity that may be shift upwards at maximum + :math:`s_{flex, do}` `flex_share_do` P share of invested capacity that may be shift downwards at maximum + :math:`costs_{invest}` `investment.ep_costs` P specific investment annuity + ================================= ======================== ==== ======================================= - .. csv-table:: Variables (V) and Parameters (P) - :header: "symbol", "attribute", "type", "explanation" - :widths: 1, 1, 1, 1 - - ":math:`invest` ",":attr:`~SinkDSM.invest` ","V", "DSM capacity - invested in. Equals to the additionally installed capacity. - The capacity share eligible for a shift is determined - by flex share(s)." - ":math:`invest_{min}` ", ":attr:`~SinkDSM.investment.minimum` ", - "P", "minimum investment" - ":math:`invest_{max}` ", ":attr:`~SinkDSM.investment.maximum` ", - "P", "maximum investment" - ":math:`E_{exist}` ",":attr:`~SinkDSM.investment.existing` ", - "P", "existing DSM capacity" - ":math:`s_{flex, up}` ",":attr:`~SinkDSM.flex_share_up` ", - "P","Share of invested capacity that may be shift upwards - at maximum" - ":math:`s_{flex, do}` ",":attr:`~SinkDSM.flex_share_do` ", - "P", "Share of invested capacity that may be shift downwards - at maximum" - ":math:`costs_{invest}` ",":attr:`~SinkDSM.investment.epcosts` ", - "P", "specific investment annuity" - """ + """ # noqa: E501 CONSTRAINT_GROUP = True def __init__(self, *args, **kwargs): @@ -1039,125 +1042,125 @@ def _objective_expression(self): class SinkDSMDIWBlock(SimpleBlock): r"""Constraints for SinkDSM with "DIW" approach - **The following constraints are created for approach = 'DIW':** + **The following constraints are created for approach = "DIW":** - .. _SinkDSMDIW equations: + .. _SinkDSMDIWBlock equations: .. math:: & - (1) \quad DSM_{t}^{up} = 0 \quad \forall t - \quad if \space eligibility_{shift} = False \\ + (1) \quad DSM_{t}^{up} = 0 \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shift} = \textrm{False} \\ + & \\ & - (2) \quad DSM_{t}^{do, shed} = 0 \quad \forall t - \quad if \space eligibility_{shed} = False \\ + (2) \quad DSM_{t}^{do, shed} = 0 \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shed} = \textrm{False} \\ + & \\ & (3) \quad \dot{E}_{t} = demand_{t} \cdot demand_{max} + DSM_{t}^{up} - - \sum_{tt=t-L}^{t+L} DSM_{tt,t}^{do, shift} - DSM_{t}^{do, shed} \quad - \forall t \in \mathbb{T} \\ + \sum_{tt=t-L}^{t+L} DSM_{tt,t}^{do, shift} - DSM_{t}^{do, shed} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (4) \quad DSM_{t}^{up} \cdot \eta = - \sum_{tt=t-L}^{t+L} DSM_{t,tt}^{do, shift} - \quad \forall t \in \mathbb{T} \\ + \sum_{tt=t-L}^{t+L} DSM_{t,tt}^{do, shift} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & - (5) \quad DSM_{t}^{up} \leq E_{t}^{up} \cdot E_{up, max} - \quad \forall t \in \mathbb{T} \\ + (5) \quad DSM_{t}^{up} \leq E_{t}^{up} \cdot E_{up, max} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (6) \quad \sum_{t=tt-L}^{tt+L} DSM_{t,tt}^{do, shift} - + DSM_{tt}^{do, shed} \leq E_{tt}^{do} \cdot E_{do, max} - \quad \forall tt \in \mathbb{T} \\ + + DSM_{tt}^{do, shed} \leq E_{tt}^{do} \cdot E_{do, max} \\ + & \quad \quad \quad \quad \forall tt \in \mathbb{T} \\ + & \\ & (7) \quad DSM_{tt}^{up} + \sum_{t=tt-L}^{tt+L} DSM_{t,tt}^{do, shift} + DSM_{tt}^{do, shed} \leq - max \{ E_{tt}^{up} \cdot E_{up, max}, E_{tt}^{do} \cdot E_{do, max} \} - \quad \forall tt \in \mathbb{T} \\ - & - (8) \quad \sum_{tt=t}^{t+R-1} DSM_{tt}^{up} - \leq E_{t}^{up} \cdot E_{up, max} \cdot L \cdot \Delta t - \quad \forall t \in \mathbb{T} \\ + max \{ E_{tt}^{up} \cdot E_{up, max}, + E_{tt}^{do} \cdot E_{do, max} \} \\ + & \quad \quad \quad \quad \forall tt \in \mathbb{T} \\ + & \\ & - (9) \quad \sum_{tt=t}^{t+R-1} DSM_{tt}^{do, shed} - \leq E_{t}^{do} \cdot E_{do, max} \cdot t_{shed} \cdot \Delta t - \quad \forall t \in \mathbb{T} \\ + (8) \quad \sum_{tt=t}^{t+R_{shi}-1} DSM_{tt}^{up} + \leq E_{t}^{up} \cdot E_{up, max} \cdot L \cdot \Delta t \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & + (9) \quad \sum_{tt=t}^{t+R_{she}-1} DSM_{tt}^{do, shed} + \leq E_{t}^{do} \cdot E_{do, max} \cdot t_{shed} \cdot \Delta t \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + - *Note*: For the sake of readability, the handling of indices is not - displayed here. E.g. evaluating a variable for t-L may lead to a negative + Note + ---- + + For the sake of readability, the handling of indices is not + displayed here. E.g. evaluating a variable for `t-L` may lead to a negative and therefore infeasible index. This is addressed by limiting the sums to non-negative indices within the model index bounds. Please refer to the constraints implementation themselves. + **The following parts of the objective function are created:** .. math:: - DSM_{t}^{up} \cdot cost_{t}^{dsm, up} - + \sum_{tt=0}^{|T|} DSM_{t, tt}^{do, shift} \cdot + & + (DSM_{t}^{up} \cdot cost_{t}^{dsm, up} + + \sum_{tt=0}^{T} DSM_{t, tt}^{do, shift} \cdot cost_{t}^{dsm, do, shift} - + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed} - \quad \forall t \in \mathbb{T} \\ + + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed}) + \cdot \omega_{t} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ **Table: Symbols and attribute names of variables and parameters** - .. csv-table:: Variables (V) and Parameters (P) - :header: "symbol", "attribute", "type", "explanation" - :widths: 1, 1, 1, 1 - - ":math:`DSM_{t}^{up}` ",":attr:`~SinkDSM.dsm_up[g,t]`", - "V", "DSM up shift (additional load) in hour t" - ":math:`DSM_{t,tt}^{do, shift}` ", - ":attr:`~SinkDSM.dsm_do_shift[g,t,tt]`", - "V", "DSM down shift (less load) in hour tt - to compensate for upwards shifts in hour t" - ":math:`DSM_{t}^{do, shed}` ",":attr:`~SinkDSM.dsm_do_shed[g,t]` ", - "V","DSM shedded (capacity shedded, i.e. not compensated for)" - ":math:`\dot{E}_{t}` ",":attr:`flow[g,t]`","V","Energy - flowing in from (electrical) inflow bus" - ":math:`L`",":attr:`~SinkDSM.delay_time`","P", - "Maximum delay time for load shift - (time until the energy balance has to be levelled out again; - roundtrip time of one load shifting cycle, i.e. time window - for upshift and compensating downshift)" - ":math:`t_{she}`",":attr:`~SinkDSM.shed_time`","P", - "Maximum time for one load shedding process" - ":math:`demand_{t}`",":attr:`~SinkDSM.demand[t]`","P", - "(Electrical) demand series (normalized)" - ":math:`demand_{max}`",":attr:`~SinkDSM.max_demand`","P", - "Maximum demand value" - ":math:`E_{t}^{do}`",":attr:`~SinkDSM.capacity_down[t]`","P", - "Capacity allowed for a load adjustment downwards (normalized) - (DSM down shift + DSM shedded)" - ":math:`E_{t}^{up}`",":attr:`~SinkDSM.capacity_up[t]`","P", - "Capacity allowed for a shift upwards (normalized) (DSM up shift)" - ":math:`E_{do, max}`",":attr:`~SinkDSM.max_capacity_down`","P", - "Maximum capacity allowed for a load adjustment downwards - (DSM down shift + DSM shedded)" - ":math:`E_{up, max}`",":attr:`~SinkDSM.max_capacity_up`","P", - "Capacity allowed for a shift upwards (normalized) (DSM up shift)" - ":math:`\eta`",":attr:`~SinkDSM.efficiency`","P", "Efficiency - loss for load shifting processes" - ":math:`\mathbb{T}` "," ","P", "Time steps" - ":math:`eligibility_{shift}` ", - ":attr:`~SinkDSM.shift_eligibility`","P", - "Boolean parameter indicating if unit can be used for - load shifting" - ":math:`eligibility_{shed}` ", - ":attr:`~SinkDSM.shed_eligibility`","P", - "Boolean parameter indicating if unit can be used for - load shedding" - ":math:`cost_{t}^{dsm, up}` ", ":attr:`~SinkDSM.cost_dsm_up[t]`", - "P", "Variable costs for an upwards shift" - ":math:`cost_{t}^{dsm, do, shift}` ", - ":attr:`~SinkDSM.cost_dsm_down_shift[t]`","P", - "Variable costs for a downwards shift (load shifting)" - ":math:`cost_{t}^{dsm, do, shed}` ", - ":attr:`~SinkDSM.cost_dsm_down_shed[t]`","P", - "Variable costs for shedding load" - ":math:`\R`",":attr:`~SinkDSM.recovery_time_shift`","P", - "Minimum time between the end of one load shifting process - and the start of another" - ":math:`\Delta t`",":attr:`~models.Model.timeincrement`","P", - "The time increment of the model" - """ + .. table:: Variables (V) and Parameters (P) + :widths: 1, 1, 1, 1 + + ================================= ======================== ==== ======================================= + symbol attribute type explanation + ================================= ======================== ==== ======================================= + :math:`DSM_{t}^{up}` `dsm_up[g, t]` V DSM up shift (additional load) in hour t + :math:`DSM_{t, tt}^{do, shift}` `dsm_do_shift[g, t, tt]` V | DSM down shift (less load) in hour tt + | to compensate for upwards shifts in hour t + :math:`DSM_{t}^{do, shed}` `dsm_do_shed[g, t]` V DSM shedded (capacity shedded, i.e. not compensated for) + :math:`\dot{E}_{t}` `SinkDSM.inputs` V Energy flowing in from (electrical) inflow bus + :math:`L` `delay_time` P | Maximum delay time for load shift + | (time until the energy balance has to be levelled out again; + | roundtrip time of one load shifting cycle, i.e. time window + | for upshift and compensating downshift) + :math:`t_{she}` `shed_time` P Maximum time for one load shedding process + :math:`demand_{t}` `demand[t]` P (Electrical) demand series (normalized) + :math:`demand_{max}` `max_demand` P Maximum demand value + :math:`E_{t}^{do}` `capacity_down[t]` P | Capacity allowed for a load adjustment downwards + | (normalized; shifting + shedding) + :math:`E_{t}^{up}` `capacity_up[t]` P Capacity allowed for a shift upwards (normalized) + :math:`E_{do, max}` `max_capacity_down` P | Maximum capacity allowed for a load adjustment downwards + | (shifting + shedding) + :math:`E_{up, max}` `max_capacity_up` P Maximum capacity allowed for a shift upwards + :math:`\eta` `efficiency` P Efficiency for load shifting processes + :math:`\mathbb{T}` P Time steps of the model + :math:`e_{shift}` `shift_eligibility` P | Boolean parameter indicating if unit can be used + | for load shifting + :math:`e_{shed}` `shed_eligibility` P | Boolean parameter indicating if unit can be used + | for load shedding + :math:`cost_{t}^{dsm, up}` `cost_dsm_up[t]` P Variable costs for an upwards shift + :math:`cost_{t}^{dsm, do, shift}` `cost_dsm_down_shift[t]` P Variable costs for a downwards shift (load shifting) + :math:`cost_{t}^{dsm, do, shed}` `cost_dsm_down_shift[t]` P Variable costs for shedding load + :math:`\omega_{t}` P Objective weighting of the model for timestep t + :math:`R_{shi}` `recovery_time_shift` P | Minimum time between the end of one load shifting process + | and the start of another + :math:`R_{she}` `recovery_time_shed` P | Minimum time between the end of one load shedding process + | and the start of another + :math:`\Delta t` P The time increment of the model + :math:`\omega_{t}` P Objective weighting of the model for timestep t + ================================= ======================== ==== ======================================= + + """ # noqa E:501 CONSTRAINT_GROUP = True def __init__(self, *args, **kwargs): @@ -1682,112 +1685,126 @@ def _objective_expression(self): class SinkDSMDIWInvestmentBlock(SimpleBlock): - r"""Constraints for SinkDSM with "DIW" approach and :attr:`investment` + CONSTRAINT_GROUP = True + r"""Constraints for SinkDSM with "DIW" approach and `investment` defined - **The following constraints are created for approach = 'DIW' with an + **The following constraints are created for approach = "DIW" with an investment object defined:** - .. _SinkDSMDIW equations: + .. _SinkDSMDIWInvestmentBlock equations: .. math:: & (1) \quad invest_{min} \leq invest \leq invest_{max} \\ + & \\ & - (2) \quad DSM_{t}^{up} = 0 \quad \forall t - \quad if \space eligibility_{shift} = False \\ + (2) \quad DSM_{t}^{up} = 0 \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shift} = \textrm{False} \\ + & \\ & - (3) \quad DSM_{t}^{do, shed} = 0 \quad \forall t - \quad if \space eligibility_{shed} = False \\ + (3) \quad DSM_{t}^{do, shed} = 0 \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shed} = \textrm{False} \\ + & \\ & (4) \quad \dot{E}_{t} = demand_{t} \cdot (invest + E_{exist}) + DSM_{t}^{up} - - \sum_{tt=t-L}^{t+L} DSM_{tt,t}^{do, shift} - DSM_{t}^{do, shed} \quad - \forall t \in \mathbb{T} \\ + \sum_{tt=t-L}^{t+L} DSM_{tt,t}^{do, shift} - DSM_{t}^{do, shed} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (5) \quad DSM_{t}^{up} \cdot \eta = - \sum_{tt=t-L}^{t+L} DSM_{t,tt}^{do, shift} - \quad \forall t \in \mathbb{T} \\ + \sum_{tt=t-L}^{t+L} DSM_{t,tt}^{do, shift} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (6) \quad DSM_{t}^{up} \leq E_{t}^{up} \cdot (invest + E_{exist}) - \ s_{flex, up} - \quad \forall t \in \mathbb{T} \\ + \ s_{flex, up} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (7) \quad \sum_{t=tt-L}^{tt+L} DSM_{t,tt}^{do, shift} + DSM_{tt}^{do, shed} \leq E_{tt}^{do} \cdot (invest + E_{exist}) - \cdot s_{flex, do} - \quad \forall tt \in \mathbb{T} \\ + \cdot s_{flex, do} \\ + & \quad \quad \quad \quad \forall tt \in \mathbb{T} \\ + & \\ & (8) \quad DSM_{tt}^{up} + \sum_{t=tt-L}^{tt+L} DSM_{t,tt}^{do, shift} - + DSM_{tt}^{do, shed} \leq - max \{ E_{tt}^{up} \cdot s_{flex, up}, - E_{tt}^{do} \cdot s_{flex, do} \} \cdot (invest + E_{exist}) - \quad \forall tt \in \mathbb{T} \\ + + DSM_{tt}^{do, shed} \\ + & \quad \quad \leq max \{ E_{tt}^{up} \cdot s_{flex, up}, + E_{tt}^{do} \cdot s_{flex, do} \} \cdot (invest + E_{exist}) \\ + & \quad \quad \quad \quad \forall tt \in \mathbb{T} \\ + & \\ & (9) \quad \sum_{tt=t}^{t+R-1} DSM_{tt}^{up} \leq E_{t}^{up} \cdot (invest + E_{exist}) - \cdot s_{flex, up} \cdot L \cdot \Delta t - \quad \forall t \in \mathbb{T} \\ + \cdot s_{flex, up} \cdot L \cdot \Delta t \\ + & \quad \quad \quad \quad \forall tt \in \mathbb{T} \\ + & \\ & (10) \quad \sum_{tt=t}^{t+R-1} DSM_{tt}^{do, shed} \leq E_{t}^{do} \cdot (invest + E_{exist}) \cdot s_{flex, do} \cdot t_{shed} - \cdot \Delta t \quad \forall t \in \mathbb{T} \\ + \cdot \Delta t \\ + & \quad \quad \quad \quad \forall tt \in \mathbb{T} \\ + - *Note*: For the sake of readability, the handling of indices is not - displayed here. E.g. evaluating a variable for t-L may lead to a negative + Note + ---- + + For the sake of readability, the handling of indices is not + displayed here. E.g. evaluating a variable for `t-L` may lead to a negative and therefore infeasible index. This is addressed by limiting the sums to non-negative indices within the model index bounds. Please refer to the constraints implementation themselves. + **The following parts of the objective function are created:** * Investment annuity: .. math:: + & invest \cdot costs_{invest} \\ * Variable costs: .. math:: - DSM_{t}^{up} \cdot cost_{t}^{dsm, up} + & + (DSM_{t}^{up} \cdot cost_{t}^{dsm, up} + \sum_{tt=0}^{T} DSM_{t, tt}^{do, shift} \cdot cost_{t}^{dsm, do, shift} - + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed} - \quad \forall t \in \mathbb{T} + + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed}) + \cdot \omega_{t} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ **Table: Symbols and attribute names of variables and parameters** - Please refer to :class:`oemof.solph.custom.SinkDSMDIWBlock`. - - The following variables and parameters are exclusively used for - investment modeling: + * Please refer to :class:`~oemof.solph.custom.sink_dsm.SinkDSMDIWBlock` + for a variables and parameter description. + * The following variables and parameters are exclusively used for + investment modeling: + + .. table:: Variables (V) and Parameters (P) + :widths: 1, 1, 1, 1 + + ================================= ======================== ==== ======================================= + symbol attribute type explanation + ================================= ======================== ==== ======================================= + :math:`invest` `invest` V | DSM capacity invested in + | Equals to the additionally installed capacity. + | The capacity share eligible for a shift is determined by flex share(s). + :math:`invest_{min}` `investment.minimum` P minimum investment + :math:`invest_{max}` `investment.maximum` P maximum investment + :math:`E_{exist}` `investment.existing` P existing DSM capacity + :math:`s_{flex, up}` `flex_share_up` P share of invested capacity that may be shift upwards at maximum + :math:`s_{flex, do}` `flex_share_do` P share of invested capacity that may be shift downwards at maximum + :math:`costs_{invest}` `investment.ep_costs` P specific investment annuity + ================================= ======================== ==== ======================================= - .. csv-table:: Variables (V) and Parameters (P) - :header: "symbol", "attribute", "type", "explanation" - :widths: 1, 1, 1, 1 - - ":math:`invest` ",":attr:`~SinkDSM.invest` ","V", "DSM capacity - invested in. Equals to the additionally installed capacity. - The capacity share eligible for a shift is determined - by flex share(s)." - ":math:`invest_{min}` ", ":attr:`~SinkDSM.investment.minimum` ", - "P", "minimum investment" - ":math:`invest_{max}` ", ":attr:`~SinkDSM.investment.maximum` ", - "P", "maximum investment" - ":math:`E_{exist}` ",":attr:`~SinkDSM.investment.existing` ", - "P", "existing DSM capacity" - ":math:`s_{flex, up}` ",":attr:`~SinkDSM.flex_share_up` ", - "P","Share of invested capacity that may be shift upwards - at maximum" - ":math:`s_{flex, do}` ",":attr:`~SinkDSM.flex_share_do` ", - "P", "Share of invested capacity that may be shift downwards - at maximum" - ":math:`costs_{invest}` ",":attr:`~SinkDSM.investment.ep_costs` ", - "P", "specific investment annuity" - ":math:`T` "," ","P", "Overall amount of time steps (cardinality)" - """ - CONSTRAINT_GROUP = True + """ # noqa: E501 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -2366,216 +2383,215 @@ def _objective_expression(self): class SinkDSMDLRBlock(SimpleBlock): r"""Constraints for SinkDSM with "DLR" approach - **The following constraints are created for approach = 'DLR':** + **The following constraints are created for approach = "DLR":** - .. _SinkDSMDLR equations: + .. _SinkDSMDLRBlock equations: .. math:: & - (1) \quad DSM_{h, t}^{up} = 0 \quad \forall h \in H_{DR} - \forall t \in \mathbb{T} - \quad if \space eligibility_{shift} = False \\ + (1) \quad DSM_{h, t}^{up} = 0 \\ + & \quad \quad \quad \quad \forall h \in H_{DR}, t \in \mathbb{T} + \quad \textrm{if} \quad e_{shift} = \textrm{False} \\ + & \\ & - (2) \quad DSM_{t}^{do, shed} = 0 \quad \forall t \in \mathbb{T} - \quad if \space eligibility_{shed} = False \\ + (2) \quad DSM_{t}^{do, shed} = 0 \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shed} = \textrm{False} \\ + & \\ & - (3) \quad \dot{E}_{t} = demand_{t} \cdot demand_{max} + - \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + (3) \quad \dot{E}_{t} = demand_{t} \cdot demand_{max} \\ + & \quad \quad \quad \quad + \displaystyle\sum_{h=1}^{H_{DR}} + (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo} - DSM_{h, t}^{do, shift} - - DSM_{h, t}^{balanceUp}) - DSM_{t}^{do, shed} - \quad \forall t \in \mathbb{T} \\ + - DSM_{h, t}^{balanceUp}) - DSM_{t}^{do, shed} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (4) \quad DSM_{h, t}^{balanceDo} = - \frac{DSM_{h, t - h}^{do, shift}}{\eta} - \quad \forall h \in H_{DR} \forall t \in [h..T] \\ + \frac{DSM_{h, t - h}^{do, shift}}{\eta} \\ + & \quad \quad \quad \quad \forall h \in H_{DR}, t \in [h..T] \\ + & \\ & (5) \quad DSM_{h, t}^{balanceUp} = - DSM_{h, t-h}^{up} \cdot \eta - \quad \forall h \in H_{DR} \forall t \in [h..T] \\ + DSM_{h, t-h}^{up} \cdot \eta \\ + & \quad \quad \quad \quad \forall h \in H_{DR}, t \in [h..T] \\ + & \\ & (6) \quad DSM_{h, t}^{do, shift} = 0 - \quad \forall h \in H_{DR} - \forall t \in [T - h..T] \\ + \quad \forall h \in H_{DR} \\ + & \quad \quad \quad \quad \forall t \in [T - h..T] \\ + & \\ & (7) \quad DSM_{h, t}^{up} = 0 - \quad \forall h \in H_{DR} - \forall t \in [T - h..T] \\ + \quad \forall h \in H_{DR} \\ + & \quad \quad \quad \quad \forall t \in [T - h..T] \\ + & \\ & (8) \quad \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{do, shift} + DSM_{h, t}^{balanceUp}) + DSM_{t}^{do, shed} - \leq E_{t}^{do} \cdot E_{max, do} - \quad \forall t \in \mathbb{T} \\ + \leq E_{t}^{do} \cdot E_{max, do} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (9) \quad \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo}) - \leq E_{t}^{up} \cdot E_{max, up} - \quad \forall t \in \mathbb{T} \\ + \leq E_{t}^{up} \cdot E_{max, up} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (10) \quad \Delta t \cdot \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{do, shift} - DSM_{h, t}^{balanceDo} \cdot \eta) - = W_{t}^{levelDo} - W_{t-1}^{levelDo} - \quad \forall t \in [1..T] \\ + = W_{t}^{levelDo} - W_{t-1}^{levelDo} \\ + & \quad \quad \quad \quad \forall t \in [1..T] \\ + & \\ & (11) \quad \Delta t \cdot \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} \cdot \eta - DSM_{h, t}^{balanceUp}) - = W_{t}^{levelUp} - W_{t-1}^{levelUp} - \quad \forall t \in [1..T] \\ + = W_{t}^{levelUp} - W_{t-1}^{levelUp} \\ + & \quad \quad \quad \quad \forall t \in [1..T] \\ + & \\ & (12) \quad W_{t}^{levelDo} \leq \overline{E}_{t}^{do} - \cdot E_{max, do} \cdot t_{shift} - \quad \forall t \in \mathbb{T} \\ + \cdot E_{max, do} \cdot t_{shift} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (13) \quad W_{t}^{levelUp} \leq \overline{E}_{t}^{up} - \cdot E_{max, up} \cdot t_{shift} - \quad \forall t \in \mathbb{T} \\ + \cdot E_{max, up} \cdot t_{shift} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (14) \quad \displaystyle\sum_{t=0}^{T} DSM_{t}^{do, shed} \leq E_{max, do} \cdot \overline{E}_{t}^{do} \cdot t_{shed} \cdot n^{yearLimitShed} \\ + & \\ & (15) \quad \displaystyle\sum_{t=0}^{T} \sum_{h=1}^{H_{DR}} DSM_{h, t}^{do, shift} \leq E_{max, do} \cdot \overline{E}_{t}^{do} \cdot t_{shift} \cdot n^{yearLimitShift} \\ - (optional \space constraint) \\ + & \quad \quad \textrm{(optional constraint)} \\ + & \\ & (16) \quad \displaystyle\sum_{t=0}^{T} \sum_{h=1}^{H_{DR}} DSM_{h, t}^{up} \leq E_{max, up} \cdot \overline{E}_{t}^{up} \cdot t_{shift} \cdot n^{yearLimitShift} \\ - (optional \space constraint) \\ + & \quad \quad \textrm{(optional constraint)} \\ + & \\ & (17) \quad \displaystyle\sum_{h=1}^{H_{DR}} DSM_{h, t}^{do, shift} \leq E_{max, do} \cdot \overline{E}_{t}^{do} \cdot t_{shift} - \displaystyle\sum_{t'=1}^{t_{dayLimit}} \sum_{h=1}^{H_{DR}} - DSM_{h, t - t'}^{do, shift} - \quad \forall t \in [t-t_{dayLimit}..T] \\ - (optional \space constraint) \\ + DSM_{h, t - t'}^{do, shift} \\ + & \quad \quad \quad \quad \forall t \in [t-t_{dayLimit}..T] \\ + & \quad \quad \textrm{(optional constraint)} \\ + & \\ & (18) \quad \displaystyle\sum_{h=1}^{H_{DR}} DSM_{h, t}^{up} \leq E_{max, up} \cdot \overline{E}_{t}^{up} \cdot t_{shift} - \displaystyle\sum_{t'=1}^{t_{dayLimit}} \sum_{h=1}^{H_{DR}} - DSM_{h, t - t'}^{up} - \quad \forall t \in [t-t_{dayLimit}..T] \\ - (optional \space constraint) \\ + DSM_{h, t - t'}^{up} \\ + & \quad \quad \quad \quad \forall t \in [t-t_{dayLimit}..T] \\ + & \quad \quad \textrm{(optional constraint)} \\ + & \\ & (19) \quad \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo} + DSM_{h, t}^{do, shift} + DSM_{h, t}^{balanceUp}) - + DSM_{t}^{do, shed} - \leq \max \{E_{t}^{up} \cdot E_{max, up}, - E_{t}^{do} \cdot E_{max, do} \} - \quad \forall t \in \mathbb{T} \\ - (optional \space constraint) \\ - & + + DSM_{t}^{do, shed} \\ + & \quad \quad \leq \max \{E_{t}^{up} \cdot E_{max, up}, + E_{t}^{do} \cdot E_{max, do} \} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \quad \quad \textrm{(optional constraint)} \\ + + + Note + ---- - *Note*: For the sake of readability, the handling of indices is not - displayed here. E.g. evaluating a variable for t-L may lead to a negative + For the sake of readability, the handling of indices is not + displayed here. E.g. evaluating a variable for `t-L` may lead to a negative and therefore infeasible index. This is addressed by limiting the sums to non-negative indices within the model index bounds. Please refer to the constraints implementation themselves. + **The following parts of the objective function are created:** .. math:: - \sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo}) - \cdot cost_{t}^{dsm, up} - + \sum_{h=1}^{H_{DR}} (DSM_{h, t}^{do, shift} + DSM_{h, t}^{balanceUp}) - \cdot cost_{t}^{dsm, do, shift} - + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed} - \quad \forall t \in \mathbb{T} \\ + & + (\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo}) + \cdot cost_{t}^{dsm, up} \\ + & + \sum_{h=1}^{H_{DR}} (DSM_{h, t}^{do, shift} + + DSM_{h, t}^{balanceUp}) + \cdot cost_{t}^{dsm, do, shift} \\ + & + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed}) + \cdot \omega_{t} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ **Table: Symbols and attribute names of variables and parameters** - .. csv-table:: Variables (V) and Parameters (P) - :header: "symbol", "attribute", "type", "explanation" + .. table:: Variables (V), Parameters (P) and additional Sets (S) :widths: 1, 1, 1, 1 - ":math:`DSM_{h, t}^{up}` ",":attr:`~SinkDSM.dsm_up[g,h,t]`", - "V", "DSM up shift (additional load) in hour t with delay time h" - ":math:`DSM_{h, t}^{do, shift}` ", - ":attr:`~SinkDSM.dsm_do_shift[g,h, t]`", - "V", "DSM down shift (less load) in hour t with delay time h" - ":math:`DSM_{h, t}^{balanceUp}` ", - ":attr:`~SinkDSM.balance_dsm_up[g,h,t]`", - "V", "DSM down shift (less load) in hour t with delay time h - to balance previous upshift" - ":math:`DSM_{h, t}^{balanceDo}` ", - ":attr:`~SinkDSM.balance_dsm_do[g,h,t]`", - "V", "DSM up shift (additional load) in hour t with delay time h - to balance previous downshift" - ":math:`DSM_{t}^{do, shed}` ", - ":attr:`~SinkDSM.dsm_do_shed[g, t]` ", - "V","DSM shedded (capacity shedded, i.e. not compensated for)" - ":math:`\dot{E}_{t}` ",":attr:`flow[g,t]`","V","Energy - flowing in from (electrical) inflow bus" - ":math:`h`","element of :attr:`~SinkDSM.delay_time`","P", - "delay time for load shift (integer value from set of feasible - delay times per DSM portfolio) - (time until the energy balance has to be levelled out again; - roundtrip time of one load shifting cycle, i.e. time window - for upshift and compensating downshift)" - ":math:`H_{DR}`", - "`range(length(:attr:`~SinkDSM.delay_time`) + 1)`", - "P", "Set of feasible delay times for load shift of a certain - DSM portfolio - (time until the energy balance has to be levelled out again; - roundtrip time of one load shifting cycle, i.e. time window - for upshift and compensating downshift)" - ":math:`t_{shift}`",":attr:`~SinkDSM.shift_time`","P", - "Maximum time for a shift in one direction, i. e. maximum time - for an upshift or a downshift in a load shifting cycle" - ":math:`t_{she}`",":attr:`~SinkDSM.shed_time`","P", - "Maximum time for one load shedding process" - ":math:`demand_{t}`",":attr:`~SinkDSM.demand[t]`","P", - "(Electrical) demand series (normalized)" - ":math:`demand_{max}`",":attr:`~SinkDSM.max_demand`","P", - "Maximum demand value" - ":math:`E_{t}^{do}`",":attr:`~SinkDSM.capacity_down[t]`","P", - "Capacity allowed for a load adjustment downwards (normalized) - (DSM down shift + DSM shedded)" - ":math:`E_{t}^{up}`",":attr:`~SinkDSM.capacity_up[t]`","P", - "Capacity allowed for a shift upwards (normalized) (DSM up shift)" - ":math:`E_{do, max}`",":attr:`~SinkDSM.max_capacity_down`","P", - "Maximum capacity allowed for a load adjustment downwards - (DSM down shift + DSM shedded)" - ":math:`E_{up, max}`",":attr:`~SinkDSM.max_capacity_up`","P", - "Capacity allowed for a shift upwards (normalized) (DSM up shift)" - ":math:`\eta`",":attr:`~SinkDSM.efficiency`","P", "Efficiency - loss for load shifting processes" - ":math:`\mathbb{T}` "," ","P", "Set of time steps" - ":math:`T` "," ","P", "Overall amount of time steps (cardinality)" - ":math:`eligibility_{shift}` ", - ":attr:`~SinkDSM.shift_eligibility`","P", - "Boolean parameter indicating if unit can be used for - load shifting" - ":math:`eligibility_{shed}` ", - ":attr:`~SinkDSM.shed_eligibility`","P", - "Boolean parameter indicating if unit can be used for - load shedding" - ":math:`cost_{t}^{dsm, up}` ", ":attr:`~SinkDSM.cost_dsm_up[t]`", - "P", "Variable costs for an upwards shift" - ":math:`cost_{t}^{dsm, do, shift}` ", - ":attr:`~SinkDSM.cost_dsm_down_shift[t]`","P", - "Variable costs for a downwards shift (load shifting)" - ":math:`cost_{t}^{dsm, do, shed}` ", - ":attr:`~SinkDSM.cost_dsm_down_shed[t]`","P", - "Variable costs for shedding load" - ":math:`\Delta t`",":attr:`~models.Model.timeincrement`","P", - "The time increment of the model" - ":math:`n_{yearLimitshift}`",":attr:`~SinkDSM.n_yearLimitShift`", - "P", "Maximum allowed number of load shifts (at full capacity) - in the optimization timeframe" - ":math:`n_{yearLimitshed}`",":attr:`~SinkDSM.n_yearLimitShed`", - "P", "Maximum allowed number of load sheds (at full capacity) - in the optimization timeframe" - ":math:`t_{dayLimit}`",":attr:`~SinkDSM.t_dayLimit`", - "P", "Maximum duration of load shifts at full capacity per day - resp. in the last hours before the current" - """ + =========================================== ================================= ==== ======================================= + symbol attribute type explanation + =========================================== ================================= ==== ======================================= + :math:`DSM_{h, t}^{up}` `dsm_up[g,h,t]` V DSM up shift (additional load) in hour t with delay time h + :math:`DSM_{h, t}^{do, shift}` `dsm_do_shift[g, h, t]` V DSM down shift (less load) in hour t with delay time h + :math:`DSM_{h, t}^{balanceUp}` `balance_dsm_up[g, h, t]` V | DSM down shift (less load) in hour t with delay time h + | to balance previous upshift + :math:`DSM_{h, t}^{balanceDo}` `balance_dsm_do[g, h, t]` V | DSM up shift (additional load) in hour t with delay time h + | to balance previous downshift + :math:`DSM_{t}^{do, shed}` `dsm_do_shed[g, t]` V DSM shedded (capacity shedded, i.e. not compensated for) + :math:`\dot{E}_{t}` `SinkDSM.inputs` V Energy flowing in from (electrical) inflow bus + :math:`h` `delay_time` P | Maximum delay time for load shift + | (integer value from set of feasible delay times per DSM portfolio; + | time until the energy balance has to be levelled out again; + | roundtrip time of one load shifting cycle, i.e. time window + | for upshift and compensating downshift) + :math:`H_{DR}` `range(len(delay_time))` S | Set of feasible delay times for load shift + | of a certain DSM portfolio + :math:`t_{shift}` `shift_time` P | Maximum time for a shift in one direction, + | i. e. maximum time for an upshift *or* a downshift + | in a load shifting cycle + :math:`t_{she}` `shed_time` P Maximum time for one load shedding process + :math:`demand_{t}` `demand[t]` P (Electrical) demand series (normalized) + :math:`demand_{max}` `max_demand` P Maximum demand value + :math:`E_{t}^{do}` `capacity_down[t]` P | Capacity allowed for a load adjustment downwards + | (normalized; shifting + shedding) + :math:`E_{t}^{up}` `capacity_up[t]` P Capacity allowed for a shift upwards (normalized) + :math:`E_{do, max}` `max_capacity_down` P | Maximum capacity allowed for a load adjustment downwards + | (shifting + shedding) + :math:`E_{up, max}` `max_capacity_up` P Maximum capacity allowed for a shift upwards + :math:`\eta` `efficiency` P Efficiency for load shifting processes + :math:`\mathbb{T}` P Time steps of the model + :math:`e_{shift}` `shift_eligibility` P | Boolean parameter indicating if unit can be used + | for load shifting + :math:`e_{shed}` `shed_eligibility` P | Boolean parameter indicating if unit can be used + | for load shedding + :math:`cost_{t}^{dsm, up}` `cost_dsm_up[t]` P Variable costs for an upwards shift + :math:`cost_{t}^{dsm, do, shift}` `cost_dsm_down_shift[t]` P Variable costs for a downwards shift (load shifting) + :math:`cost_{t}^{dsm, do, shed}` `cost_dsm_down_shift[t]` P Variable costs for shedding load + :math:`\omega_{t}` P Objective weighting of the model for timestep t + :math:`R_{shi}` `recovery_time_shift` P | Minimum time between the end of one load shifting process + | and the start of another + :math:`R_{she}` `recovery_time_shed` P | Minimum time between the end of one load shedding process + | and the start of another + :math:`\Delta t` P The time increment of the model + :math:`\omega_{t}` P Objective weighting of the model for timestep t + :math:`n_{yearLimitShift}` `n_yeaLimitShift` P | Maximum allowed number of load shifts (at full capacity) + | in the optimization timeframe + :math:`n_{yearLimitShed}` `n_yeaLimitShed` P | Maximum allowed number of load sheds (at full capacity) + | in the optimization timeframe + :math:`t_{dayLimit}` `t_dayLimit` P | Maximum duration of load shifts at full capacity per day + | resp. in the last hours before the current" + =========================================== ================================= ==== ======================================= + + """ # noqa: E501 CONSTRAINT_GROUP = True def __init__(self, *args, **kwargs): @@ -3328,84 +3344,99 @@ def _objective_expression(self): return self.cost -class SinkDSMDLRInvestmentBlock(SinkDSMDLRBlock): - r"""Constraints for SinkDSM with "DLR" approach and :attr:`investment` +class SinkDSMDLRInvestmentBlock(SimpleBlock): + r"""Constraints for SinkDSM with "DLR" approach and `investment` defined - **The following constraints are created for approach = 'DLR' with an + **The following constraints are created for approach = "DLR" with an investment object defined:** - .. _SinkDSMDLR equations: + .. _SinkDSMDLRInvestmentBlock equations: .. math:: & (1) \quad invest_{min} \leq invest \leq invest_{max} \\ + & \\ & - (2) \quad DSM_{h, t}^{up} = 0 \quad \forall h \in H_{DR} - \forall t \in \mathbb{T} - \quad if \space eligibility_{shift} = False \\ + (2) \quad DSM_{h, t}^{up} = 0 \\ + & \quad \quad \quad \quad \forall h \in H_{DR}, t \in \mathbb{T} + \quad \textrm{if} \quad e_{shift} = \textrm{False} \\ & - (3) \quad DSM_{t}^{do, shed} = 0 \quad \forall t \in \mathbb{T} - \quad if \space eligibility_{shed} = False \\ + (3) \quad DSM_{t}^{do, shed} = 0 \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shed} = \textrm{False} \\ + & \\ & - (4) \quad \dot{E}_{t} = demand_{t} \cdot (invest + E_{exist}) - + \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + (4) \quad \dot{E}_{t} = demand_{t} \cdot (invest + E_{exist}) \\ + & \quad \quad \quad \quad + \displaystyle\sum_{h=1}^{H_{DR}} + (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo} - DSM_{h, t}^{do, shift} - - DSM_{h, t}^{balanceUp}) - DSM_{t}^{do, shed} - \quad \forall t \in \mathbb{T} \\ + - DSM_{h, t}^{balanceUp}) - DSM_{t}^{do, shed} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (5) \quad DSM_{h, t}^{balanceDo} = - \frac{DSM_{h, t - h}^{do, shift}}{\eta} - \quad \forall h \in H_{DR} \forall t \in [h..T] \\ + \frac{DSM_{h, t - h}^{do, shift}}{\eta} \\ + & \quad \quad \quad \quad \forall h \in H_{DR}, t \in [h..T] \\ + & \\ & (6) \quad DSM_{h, t}^{balanceUp} = - DSM_{h, t-h}^{up} \cdot \eta - \quad \forall h \in H_{DR} \forall t \in [h..T] \\ + DSM_{h, t-h}^{up} \cdot \eta \\ + & \quad \quad \quad \quad \forall h \in H_{DR}, t \in [h..T] \\ + & \\ & (7) \quad DSM_{h, t}^{do, shift} = 0 - \quad \forall h \in H_{DR} - \forall t \in [T - h..T] \\ + \quad \forall h \in H_{DR} \\ + & \quad \quad \quad \quad \forall t \in [T - h..T] \\ + & \\ & - (8) \quad DSM_{h, t}^{up} = 0 - \quad \forall h \in H_{DR} - \forall t \in [T - h..T] \\ + (8) \quad DSM_{h, t}^{up} = 0 \\ + & \quad \quad \quad \quad \forall h \in H_{DR}, t \in [T - h..T] \\ + & \\ & (9) \quad \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{do, shift} + DSM_{h, t}^{balanceUp}) + DSM_{t}^{do, shed} \leq E_{t}^{do} \cdot (invest + E_{exist}) - \cdot s_{flex, do} - \quad \forall t \in \mathbb{T} \\ + \cdot s_{flex, do} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (10) \quad \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo}) \leq E_{t}^{up} \cdot (invest + E_{exist}) - \cdot s_{flex, up} - \quad \forall t \in \mathbb{T} \\ + \cdot s_{flex, up} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (11) \quad \Delta t \cdot \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{do, shift} - DSM_{h, t}^{balanceDo} \cdot \eta) - = W_{t}^{levelDo} - W_{t-1}^{levelDo} - \quad \forall t \in [1..T] \\ + = W_{t}^{levelDo} - W_{t-1}^{levelDo} \\ + & \quad \quad \quad \quad \forall t \in [1..T] \\ + & \\ & (12) \quad \Delta t \cdot \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} \cdot \eta - DSM_{h, t}^{balanceUp}) - = W_{t}^{levelUp} - W_{t-1}^{levelUp} - \quad \forall t \in [1..T] \\ + = W_{t}^{levelUp} - W_{t-1}^{levelUp} \\ + & \quad \quad \quad \quad \forall t \in [1..T] \\ + & \\ & (13) \quad W_{t}^{levelDo} \leq \overline{E}_{t}^{do} \cdot (invest + E_{exist}) - \cdot s_{flex, do} \cdot t_{shift} - \quad \forall t \in \mathbb{T} \\ + \cdot s_{flex, do} \cdot t_{shift} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (14) \quad W_{t}^{levelUp} \leq \overline{E}_{t}^{up} \cdot (invest + E_{exist}) - \cdot s_{flex, up} \cdot t_{shift} - \quad \forall t \in \mathbb{T} \\ + \cdot s_{flex, up} \cdot t_{shift} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (15) \quad \displaystyle\sum_{t=0}^{T} DSM_{t}^{do, shed} \leq (invest + E_{exist}) \cdot s_{flex, do} \cdot \overline{E}_{t}^{do} \cdot t_{shed} \cdot n^{yearLimitShed} \\ + & \\ & (16) \quad \displaystyle\sum_{t=0}^{T} \sum_{h=1}^{H_{DR}} DSM_{h, t}^{do, shift} @@ -3413,7 +3444,8 @@ class SinkDSMDLRInvestmentBlock(SinkDSMDLRBlock): \cdot s_{flex, do} \cdot \overline{E}_{t}^{do} \cdot t_{shift} \cdot n^{yearLimitShift} \\ - (optional \space constraint) \\ + & \quad \quad \textrm{(optional constraint)} \\ + & \\ & (17) \quad \displaystyle\sum_{t=0}^{T} \sum_{h=1}^{H_{DR}} DSM_{h, t}^{up} @@ -3421,90 +3453,94 @@ class SinkDSMDLRInvestmentBlock(SinkDSMDLRBlock): \cdot s_{flex, up} \cdot \overline{E}_{t}^{up} \cdot t_{shift} \cdot n^{yearLimitShift} \\ - (optional \space constraint) \\ + & \quad \quad \textrm{(optional constraint)} \\ & (18) \quad \displaystyle\sum_{h=1}^{H_{DR}} DSM_{h, t}^{do, shift} \leq (invest + E_{exist}) \cdot s_{flex, do} \cdot \overline{E}_{t}^{do} \cdot t_{shift} - \displaystyle\sum_{t'=1}^{t_{dayLimit}} \sum_{h=1}^{H_{DR}} - DSM_{h, t - t'}^{do, shift} - \quad \forall t \in [t-t_{dayLimit}..T] \\ - (optional \space constraint) \\ + DSM_{h, t - t'}^{do, shift} \\ + & \quad \quad \quad \quad \forall t \in [t-t_{dayLimit}..T] \\ + & \quad \quad \textrm{(optional constraint)} \\ + & \\ & (19) \quad \displaystyle\sum_{h=1}^{H_{DR}} DSM_{h, t}^{up} \leq (invest + E_{exist}) \cdot s_{flex, up} \cdot \overline{E}_{t}^{up} \cdot t_{shift} - \displaystyle\sum_{t'=1}^{t_{dayLimit}} \sum_{h=1}^{H_{DR}} - DSM_{h, t - t'}^{up} - \quad \forall t \in [t-t_{dayLimit}..T] \\ - (optional \space constraint) \\ + DSM_{h, t - t'}^{up} \\ + & \quad \quad \quad \quad \forall t \in [t-t_{dayLimit}..T] \\ + & \quad \quad \textrm{(optional constraint)} \\ + & \\ & (20) \quad \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo} + DSM_{h, t}^{do, shift} + DSM_{h, t}^{balanceUp}) - + DSM_{t}^{shed} - \leq \max \{E_{t}^{up} \cdot s_{flex, up}, - E_{t}^{do} \cdot s_{flex, do} \} \cdot (invest + E_{exist}) - \quad \forall t \in \mathbb{T} \\ - (optional \space constraint) \\ - & + + DSM_{t}^{shed} \\ + & \quad \quad \leq \max \{E_{t}^{up} \cdot s_{flex, up}, + E_{t}^{do} \cdot s_{flex, do} \} \cdot (invest + E_{exist}) \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \quad \quad \textrm{(optional constraint)} \\ + + + Note + ---- - *Note*: For the sake of readability, the handling of indices is not - displayed here. E.g. evaluating a variable for t-L may lead to a negative + For the sake of readability, the handling of indices is not + displayed here. E.g. evaluating a variable for `t-L` may lead to a negative and therefore infeasible index. This is addressed by limiting the sums to non-negative indices within the model index bounds. Please refer to the constraints implementation themselves. + **The following parts of the objective function are created:** * Investment annuity: .. math:: + & invest \cdot costs_{invest} \\ * Variable costs: .. math:: - \sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo}) - \cdot cost_{t}^{dsm, up} - + \sum_{h=1}^{H_{DR}} (DSM_{h, t}^{do, shift} + DSM_{h, t}^{balanceUp}) - \cdot cost_{t}^{dsm, do, shift} - + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed} - \quad \forall t \in \mathbb{T} \\ + & + (\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo}) + \cdot cost_{t}^{dsm, up} \\ + & + \sum_{h=1}^{H_{DR}} (DSM_{h, t}^{do, shift} + + DSM_{h, t}^{balanceUp}) + \cdot cost_{t}^{dsm, do, shift} \\ + & + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed}) + \cdot \omega_{t} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ **Table: Symbols and attribute names of variables and parameters** - Please refer to :class:`oemof.solph.custom.SinkDSMDLRBlock`. + * Please refer to :class:`~oemof.solph.custom.sink_dsm.SinkDSMDLRBlock`. + * The following variables and parameters are exclusively used for + investment modeling: - The following variables and parameters are exclusively used for - investment modeling: - - .. csv-table:: Variables (V) and Parameters (P) - :header: "symbol", "attribute", "type", "explanation" + .. table:: Variables (V) and Parameters (P) :widths: 1, 1, 1, 1 - ":math:`invest` ",":attr:`~SinkDSM.invest` ","V", "DSM capacity - invested in. Equals to the additionally installed capacity. - The capacity share eligible for a shift is determined - by flex share(s)." - ":math:`invest_{min}` ", ":attr:`~SinkDSM.investment.minimum` ", - "P", "minimum investment" - ":math:`invest_{max}` ", ":attr:`~SinkDSM.investment.maximum` ", - "P", "maximum investment" - ":math:`E_{exist}` ",":attr:`~SinkDSM.investment.existing` ", - "P", "existing DSM capacity" - ":math:`s_{flex, up}` ",":attr:`~SinkDSM.flex_share_up` ", - "P","Share of invested capacity that may be shift upwards - at maximum" - ":math:`s_{flex, do}` ",":attr:`~SinkDSM.flex_share_do` ", - "P", "Share of invested capacity that may be shift downwards - at maximum" - ":math:`costs_{invest}` ",":attr:`~SinkDSM.investment.ep_costs` ", - "P", "specific investment annuity" - """ + ================================= ======================== ==== ======================================= + symbol attribute type explanation + ================================= ======================== ==== ======================================= + :math:`invest` `invest` V | DSM capacity invested in + | Equals to the additionally installed capacity. + | The capacity share eligible for a shift is determined by flex share(s). + :math:`invest_{min}` `investment.minimum` P minimum investment + :math:`invest_{max}` `investment.maximum` P maximum investment + :math:`E_{exist}` `investment.existing` P existing DSM capacity + :math:`s_{flex, up}` `flex_share_up` P share of invested capacity that may be shift upwards at maximum + :math:`s_{flex, do}` `flex_share_do` P share of invested capacity that may be shift downwards at maximum + :math:`costs_{invest}` `investment.ep_costs` P specific investment annuity + ================================= ======================== ==== ======================================= + + """ # noqa: E501 CONSTRAINT_GROUP = True def __init__(self, *args, **kwargs): From d4b03b8a2fa74544026885e66ae7701bea97aa90 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Thu, 2 Dec 2021 14:14:59 +0100 Subject: [PATCH 0016/1363] Add draft for user warning --- src/oemof/solph/flows/_flow.py | 18 +++++++++++++++++- tests/test_solph_network_classes.py | 9 +++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index c9c3c8c3e..377e0d9b1 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -14,7 +14,7 @@ SPDX-License-Identifier: MIT """ - +import math from warnings import warn from oemof.network import network as on @@ -220,6 +220,22 @@ def __init__(self, **kwargs): + "nonconvex flows!" ) + if not self.investment: + assumption_msg = ( + "If {} is set in a dispatch model, " + "nominal_value must be set as well." + ) + if self.summed_max is not None: + assert ( + math.isfinite(self.nominal_value), + assumption_msg.format("summed_max") + ) + if self.summex_min is not None: + assert ( + math.isfinite(self.nominal_value), + assumption_msg.format("summed_min") + ) + # Checking for impossible gradient combinations if self.nonconvex: if self.nonconvex.positive_gradient["ub"][0] is not None and ( diff --git a/tests/test_solph_network_classes.py b/tests/test_solph_network_classes.py index 10e114f09..7249890c5 100644 --- a/tests/test_solph_network_classes.py +++ b/tests/test_solph_network_classes.py @@ -92,6 +92,15 @@ def test_flow_with_fix_and_min_max(): solph.flows.Flow(fix=[1, 3], max=[0, 5], min=[4, 9]) +def test_summed_min_and_summed_max(): + msg1 = "If summed_max is set in a dispatch model," + msg2 = "If summed_min is set in a dispatch model," + with pytest.raises(AssertionError, match=msg1): + solph.flows.Flow(summed_max=0.3) + with pytest.raises(AssertionError, match=msg2): + solph.flows.Flow(summed_min=0.3) + + def test_min_max_values_for_bidirectional_flow(): a = solph.flows.Flow(bidirectional=True) # use default values b = solph.flows.Flow(bidirectional=True, min=-0.9, max=0.9) From 68780fc2c8074e138556ee1bd7783f67248d924b Mon Sep 17 00:00:00 2001 From: uvchik Date: Thu, 3 Jun 2021 09:57:21 +0200 Subject: [PATCH 0017/1363] Show failing references --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 459c9be43..e2c2e38e3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -55,7 +55,7 @@ def setup(app): napoleon_use_ivar = True napoleon_use_rtype = False napoleon_use_param = False -nitpicky = False +nitpicky = True exclude_patterns = ["_build", "whatsnew/*"] From 33e112f25ede0abb6f8b6945f02f07c061053a2f Mon Sep 17 00:00:00 2001 From: Ekaterina Zolotarevskaia Date: Thu, 2 Dec 2021 17:08:34 +0100 Subject: [PATCH 0018/1363] Revise docstrings for equate_variables and flow_count limit --- .../solph/constraints/equate_variables.py | 45 +++++++++++-------- .../solph/constraints/flow_count_limit.py | 17 +++---- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/src/oemof/solph/constraints/equate_variables.py b/src/oemof/solph/constraints/equate_variables.py index dc90b30cf..bb664cff2 100644 --- a/src/oemof/solph/constraints/equate_variables.py +++ b/src/oemof/solph/constraints/equate_variables.py @@ -14,28 +14,35 @@ def equate_variables(model, var1, var2, factor1=1, name=None): r""" - Adds a constraint to the given model that set two variables to equal - adaptable by a factor. - - **The following constraints are build:** - - .. math:: - var\textit{1} \cdot factor\textit{1} = var\textit{2} + Adds a constraint to the given model that sets two variables to equal adaptable by a factor. Parameters ---------- - var1 : pyomo.environ.Var - First variable, to be set to equal with Var2 and multiplied with - factor1. - var2 : pyomo.environ.Var - Second variable, to be set equal to (Var1 * factor1). - factor1 : float - Factor to define the proportion between the variables. - name : str - Optional name for the equation e.g. in the LP file. By default the - name is: equate + string representation of var1 and var2. - model : oemof.solph.Model - Model to which the constraint is added. + var1 : pyomo.environ.Var First variable, to be set to equal with Var2 and multiplied with factor1. + var2 : pyomo.environ.Var Second variable, to be set equal to (Var1 * factor1). + factor1 : float Factor to define the proportion between the variables. + name : str Optional name for the equation e.g. in the LP file. By default the name is: + equate + string representation of var1 and var2. + model : oemof.solph.Model Model to which the constraint is added. + + **The following constraints are build:** + + .. math:: var_1 \cdot factor_1 = var_1 + + The symbols used are defined as follows (with Variables (V) and Parameters (P)): + +------------------+---------------------+------+------------------------------------------------------------------------------------------------------------------------------------------------+ + | symbol | attribute | type | explanation | + +==================+=====================+======+================================================================================================================================================+ + | :math:`var_1` | pyomo.environ.Var` | V | First variable, to be set to equal with :math:`var_2` and multiplied with :math:`factor_1`var_1 | + +------------------+---------------------+------+------------------------------------------------------------------------------------------------------------------------------------------------+ + | :math:`var_2` | pyomo.environ.Var` | V | Second variable, to be set equal to :math:`var_1 \cdot factor_1` | + +------------------+---------------------+------+------------------------------------------------------------------------------------------------------------------------------------------------+ + | :math:`factor_1` | `float` | P | Factor to define the proportion between the variables. The default value is 1. | + +------------------+---------------------+------+------------------------------------------------------------------------------------------------------------------------------------------------+ + | name | `str` | P | Optional name for the equation e.g. in the LP file. By default the name is: equate + string representation of :math:`var_1` and :math:`var_2`. | + +------------------+---------------------+------+------------------------------------------------------------------------------------------------------------------------------------------------+ + | model | `oemof.solph.Model` | P | Model to which the constraint is added | + +------------------+---------------------+------+------------------------------------------------------------------------------------------------------------------------------------------------+ Examples -------- diff --git a/src/oemof/solph/constraints/flow_count_limit.py b/src/oemof/solph/constraints/flow_count_limit.py index cc78e409d..b7adbe0b8 100644 --- a/src/oemof/solph/constraints/flow_count_limit.py +++ b/src/oemof/solph/constraints/flow_count_limit.py @@ -56,14 +56,15 @@ def limit_active_flow_count( The symbols used are defined as follows (with Variables (V) and Parameters (P)): - ================== ==== =================================================== - math. symbol type explanation - ================== ==== =================================================== - :math:`X_n(t)` V status (0 or 1) of the flow :math:`n` - at time step :math:`t` - :math:`N_{X,min}` P lower_limit - :math:`N_{X,max}` P lower_limit - ================== ==== =================================================== + +-------------------+------+--------------------------------------------------------------+ + | symbol | type | explanation | + +===================+======+==============================================================+ + | :math:`X_n(t)` | V | status (0 or 1) of the flow :math:`n` at time step :math:`t` | + +-------------------+------+--------------------------------------------------------------+ + | :math:`N_{X,min}` | P | lower_limit | + +-------------------+------+--------------------------------------------------------------+ + | :math:`N_{X,max} | P | upper_limit | + +-------------------+------+--------------------------------------------------------------+ """ # number of concurrent active flows From 8d48faa42544dc5204782f4d191d2b63f08e65dc Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Thu, 2 Dec 2021 17:50:22 +0100 Subject: [PATCH 0019/1363] Change user warning; no need to use assert statements --- src/oemof/solph/flows/_flow.py | 25 ++++++++++++++----------- tests/test_solph_network_classes.py | 4 ++-- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 377e0d9b1..5b6570311 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -221,20 +221,23 @@ def __init__(self, **kwargs): ) if not self.investment: - assumption_msg = ( + warn_msg = ( "If {} is set in a dispatch model, " - "nominal_value must be set as well." + "nominal_value must be set as well.\n" + "Otherwise, it won't have any effect." ) if self.summed_max is not None: - assert ( - math.isfinite(self.nominal_value), - assumption_msg.format("summed_max") - ) - if self.summex_min is not None: - assert ( - math.isfinite(self.nominal_value), - assumption_msg.format("summed_min") - ) + if self.nominal_value is None: + warn( + warn_msg.format("summed_max"), + debugging.SuspiciousUsageWarning, + ) + if self.summed_min is not None: + if self.nominal_value is None: + warn( + warn_msg.format("summed_min"), + debugging.SuspiciousUsageWarning, + ) # Checking for impossible gradient combinations if self.nonconvex: diff --git a/tests/test_solph_network_classes.py b/tests/test_solph_network_classes.py index 7249890c5..a80a54c65 100644 --- a/tests/test_solph_network_classes.py +++ b/tests/test_solph_network_classes.py @@ -95,9 +95,9 @@ def test_flow_with_fix_and_min_max(): def test_summed_min_and_summed_max(): msg1 = "If summed_max is set in a dispatch model," msg2 = "If summed_min is set in a dispatch model," - with pytest.raises(AssertionError, match=msg1): + with pytest.warns(SuspiciousUsageWarning, match=msg1): solph.flows.Flow(summed_max=0.3) - with pytest.raises(AssertionError, match=msg2): + with pytest.warns(SuspiciousUsageWarning, match=msg2): solph.flows.Flow(summed_min=0.3) From 493f765f13cf649c394837437704df61ac395c7f Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Thu, 2 Dec 2021 17:52:54 +0100 Subject: [PATCH 0020/1363] Remove unused import --- src/oemof/solph/flows/_flow.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 5b6570311..8ec2c8a5e 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -14,7 +14,6 @@ SPDX-License-Identifier: MIT """ -import math from warnings import warn from oemof.network import network as on From 09cb0804ebcaea16b464ec7f6da7f014a5c9d61d Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Thu, 2 Dec 2021 18:04:50 +0100 Subject: [PATCH 0021/1363] Slightly restructure --- src/oemof/solph/flows/_flow.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 8ec2c8a5e..9a2d319cb 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -225,14 +225,13 @@ def __init__(self, **kwargs): "nominal_value must be set as well.\n" "Otherwise, it won't have any effect." ) - if self.summed_max is not None: - if self.nominal_value is None: + if self.nominal_value is None: + if self.summed_max is not None: warn( warn_msg.format("summed_max"), debugging.SuspiciousUsageWarning, ) - if self.summed_min is not None: - if self.nominal_value is None: + if self.summed_min is not None: warn( warn_msg.format("summed_min"), debugging.SuspiciousUsageWarning, From 7e1ae277ef35cbc4207145a8b677b851f77d6ee4 Mon Sep 17 00:00:00 2001 From: Johannes Kochems <40718083+jokochems@users.noreply.github.com> Date: Sat, 4 Dec 2021 12:14:25 +0100 Subject: [PATCH 0022/1363] Update src/oemof/solph/flows/_flow.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Include suggested change Co-authored-by: Johannes Röder <45163075+joroeder@users.noreply.github.com> --- src/oemof/solph/flows/_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 9a2d319cb..e294de351 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -221,7 +221,7 @@ def __init__(self, **kwargs): if not self.investment: warn_msg = ( - "If {} is set in a dispatch model, " + "If {} is set in a flow (except InvestmentFlow), " "nominal_value must be set as well.\n" "Otherwise, it won't have any effect." ) From 063f920fe15766cf9ca77af2ae1d0d9197011a57 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 4 Dec 2021 12:27:48 +0100 Subject: [PATCH 0023/1363] Update tests to match error message --- tests/test_solph_network_classes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_solph_network_classes.py b/tests/test_solph_network_classes.py index a80a54c65..b95500be2 100644 --- a/tests/test_solph_network_classes.py +++ b/tests/test_solph_network_classes.py @@ -93,8 +93,8 @@ def test_flow_with_fix_and_min_max(): def test_summed_min_and_summed_max(): - msg1 = "If summed_max is set in a dispatch model," - msg2 = "If summed_min is set in a dispatch model," + msg1 = "If summed_max is set in a flow " + msg2 = "If summed_min is set in a flow " with pytest.warns(SuspiciousUsageWarning, match=msg1): solph.flows.Flow(summed_max=0.3) with pytest.warns(SuspiciousUsageWarning, match=msg2): From c7729303f6e09e4693d7fede21a8c2a6602074c0 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 4 Dec 2021 13:39:41 +0100 Subject: [PATCH 0024/1363] Integrate multi-period draft into general modules --- src/oemof/solph/_energy_system.py | 43 ++++++++++- src/oemof/solph/_models.py | 70 +++++++++++------ src/oemof/solph/_options.py | 44 +++++++++-- src/oemof/solph/processing.py | 121 +++++++++++++++++++++--------- src/oemof/solph/views.py | 20 +++-- 5 files changed, 227 insertions(+), 71 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index c320a0efe..82bda5482 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -8,6 +8,7 @@ SPDX-FileCopyrightText: Cord Kaldemeyer SPDX-FileCopyrightText: Stephan Günther SPDX-FileCopyrightText: Birgit Schachler +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -32,7 +33,19 @@ class EnergySystem(es.EnergySystem): oemof.network directly. """ - def __init__(self, **kwargs): + def __init__(self, multi_period=False, periods=None, **kwargs): + """Initialize an EnergySystem + + Parameters + ---------- + multi_period : boolean + If True, a multi period model is used; defaults to False + + periods : dict + The periods of a multi period model + Keys are years as integer values, + values are the respective number of the period starting with zero + """ # Doing imports at runtime is generally frowned upon, but should work # for now. See the TODO in :func:`constraint_grouping # ` for more information. @@ -41,3 +54,31 @@ def __init__(self, **kwargs): kwargs["groupings"] = GROUPINGS + kwargs.get("groupings", []) super().__init__(**kwargs) + self.multi_period = multi_period + self._add_periods(periods) + + def _add_periods(self, periods): + """Add periods to the energy system + + * For a single model, periods only contain one value. + * For a multi period model, periods must equal to years used in the + timeindex. As a default, each year in the timeindex is mapped to + its own period. + + Parameters + ---------- + periods : dict + The periods of a multi period model + Keys are years as integer values, + values are the respective number of the period starting with zero + """ + if not self.multi_period: + periods = {"single_period": 0} + elif periods is None: + years = sorted( + list( + set(getattr(self.timeindex, 'year')) + ) + ) + periods = dict(zip(years, range(len(years)))) + self.periods = periods diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 8ac022d5c..8bbec958e 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -7,6 +7,7 @@ SPDX-FileCopyrightText: Cord Kaldemeyer SPDX-FileCopyrightText: gplssm SPDX-FileCopyrightText: Patrik Schönfeldt +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -14,6 +15,7 @@ import logging import warnings +from oemof.tools import debugging from pyomo import environ as po from pyomo.core.plugins.transform.relax_integrality import RelaxIntegrality from pyomo.opt import SolverFactory @@ -287,7 +289,16 @@ class Model(BaseModel): NonConvexFlowBlock, ] - def __init__(self, energysystem, **kwargs): + def __init__(self, energysystem, discount_rate=None, **kwargs): + if discount_rate is not None: + self.discount_rate = discount_rate + elif energysystem.multi_period: + self.discount_rate = 0.02 + msg = (f"By default, a discount_rate of {self.discount_rate} " + f"is used for a multi-period model. " + f"If you want to use another value, " + f"you have to specify the `discount_rate` attribute.") + warnings.warn(msg, debugging.SuspiciousUsageWarning) super().__init__(energysystem, **kwargs) def _add_parent_block_sets(self): @@ -296,10 +307,28 @@ def _add_parent_block_sets(self): self.NODES = po.Set(initialize=[n for n in self.es.nodes]) # pyomo set for timesteps of optimization problem - self.TIMESTEPS = po.Set( - initialize=range(len(self.es.timeindex)), ordered=True + self.TIMESTEPS = po.Set(initialize=range(len(self.es.timeindex)), + ordered=True) + + # pyomo set for timeindex of optimization problem + self.TIMEINDEX = po.Set( + initialize=list( + zip([self.es.periods[p] for p in self.es.timeindex.year], + range(len(self.es.timeindex))) + ), + ordered=True + ) + + self.PERIODS = po.Set( + initialize=sorted(list(set(self.es.periods.values()))) ) + # (Re-)Map timesteps to periods + timesteps_in_period = {p: [] for p in self.PERIODS} + for p, t in self.TIMEINDEX: + timesteps_in_period[p].append(t) + self.TIMESTEPS_IN_PERIOD = timesteps_in_period + # previous timesteps previous_timesteps = [x - 1 for x in self.TIMESTEPS] previous_timesteps[0] = self.TIMESTEPS.last() @@ -335,34 +364,33 @@ def _add_parent_block_sets(self): def _add_parent_block_variables(self): """ """ - self.flow = po.Var(self.FLOWS, self.TIMESTEPS, within=po.Reals) + self.flow = po.Var( + self.FLOWS, self.TIMEINDEX, within=po.Reals + ) for (o, i) in self.FLOWS: if self.flows[o, i].nominal_value is not None: if self.flows[o, i].fix[self.TIMESTEPS[1]] is not None: - for t in self.TIMESTEPS: - self.flow[o, i, t].value = ( + for p, t in self.TIMEINDEX: + self.flow[o, i, p, t].value = ( self.flows[o, i].fix[t] - * self.flows[o, i].nominal_value - ) - self.flow[o, i, t].fix() + * self.flows[o, i].nominal_value) + self.flow[o, i, p, t].fix() else: - for t in self.TIMESTEPS: - self.flow[o, i, t].setub( + for p, t in self.TIMEINDEX: + self.flow[o, i, p, t].setub( self.flows[o, i].max[t] - * self.flows[o, i].nominal_value - ) + * self.flows[o, i].nominal_value) if not self.flows[o, i].nonconvex: - for t in self.TIMESTEPS: - self.flow[o, i, t].setlb( + for p, t in self.TIMEINDEX: + self.flow[o, i, p, t].setlb( self.flows[o, i].min[t] - * self.flows[o, i].nominal_value - ) + * self.flows[o, i].nominal_value) elif (o, i) in self.UNIDIRECTIONAL_FLOWS: - for t in self.TIMESTEPS: - self.flow[o, i, t].setlb(0) + for p, t in self.TIMEINDEX: + self.flow[o, i, p, t].setlb(0) else: if (o, i) in self.UNIDIRECTIONAL_FLOWS: - for t in self.TIMESTEPS: - self.flow[o, i, t].setlb(0) + for p, t in self.TIMEINDEX: + self.flow[o, i, p, t].setlb(0) diff --git a/src/oemof/solph/_options.py b/src/oemof/solph/_options.py index 94b7b016b..ea26f8aec 100644 --- a/src/oemof/solph/_options.py +++ b/src/oemof/solph/_options.py @@ -8,11 +8,14 @@ SPDX-FileCopyrightText: Stephan Günther SPDX-FileCopyrightText: Patrik Schönfeldt SPDX-FileCopyrightText: jmloenneberga +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT """ +from warnings import warn +from oemof.tools import debugging from oemof.solph._plumbing import sequence @@ -54,15 +57,27 @@ def __init__( existing=0, nonconvex=False, offset=0, + overall_maximum=None, + overall_minimum=None, + lifetime=20, + age=0, + interest_rate=0, + fixed_costs=None, **kwargs, ): - self.maximum = maximum - self.minimum = minimum - self.ep_costs = ep_costs + self.maximum = sequence(maximum) + self.minimum = sequence(minimum) + self.ep_costs = sequence(ep_costs) self.existing = existing self.nonconvex = nonconvex - self.offset = offset + self.offset = sequence(offset) + self.overall_maximum = overall_maximum + self.overall_minimum = overall_minimum + self.lifetime = lifetime + self.age = age + self.interest_rate = interest_rate + self.fixed_costs = sequence(fixed_costs) for attribute in kwargs.keys(): value = kwargs.get(attribute) @@ -71,6 +86,7 @@ def __init__( self._check_invest_attributes() self._check_invest_attributes_maximum() self._check_invest_attributes_offset() + self._check_age_and_lifetime() def _check_invest_attributes(self): if (self.existing != 0) and (self.nonconvex is True): @@ -82,10 +98,10 @@ def _check_invest_attributes(self): raise AttributeError(e1) def _check_invest_attributes_maximum(self): - if (self.maximum == float("+inf")) and (self.nonconvex is True): + if (self.maximum[0] == float("+inf")) and (self.nonconvex is True): e2 = ( - "Please provide an maximum investment value in case of" - " nonconvex investemnt (nonconvex=True), which is in the" + "Please provide a maximum investment value in case of" + " nonconvex investment (nonconvex=True), which is in the" " expected magnitude." " \nVery high maximum values (> 10e8) as maximum investment" " limit might lead to numeric issues, so that no investment" @@ -94,13 +110,25 @@ def _check_invest_attributes_maximum(self): raise AttributeError(e2) def _check_invest_attributes_offset(self): - if (self.offset != 0) and (self.nonconvex is False): + if (self.offset[0] != 0) and (self.nonconvex is False): e3 = ( "If `nonconvex` is `False`, the `offset` parameter will be" " ignored." ) raise AttributeError(e3) + def _check_age_and_lifetime(self): + if self.lifetime == 20: + w1 = ("Using a lifetime of 20 periods," + " which is the default value.\nIf you don't consider a" + " multi-period investment model, you can safely ignore " + " this warning - all fine!") + warn(w1, debugging.SuspiciousUsageWarning) + if self.age >= self.lifetime: + e4 = ("A unit's age must be smaller than its " + "expected lifetime.") + raise AttributeError(e4) + class NonConvex: """ diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index 3d342038e..b9d41495c 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -9,6 +9,7 @@ SPDX-FileCopyrightText: Cord Kaldemeyer SPDX-FileCopyrightText: Stephan Günther SPDX-FileCopyrightText: henhuy +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -69,6 +70,35 @@ def remove_timestep(x): return x[:-1] +def get_timeindex(x): + """ + Get the timeindex from oemof tuples for multiperiod models. + Slice int values (timeindex, timesteps or periods) dependent on how + the variable is indexed. + + The timestep is removed from tuples of type `(n, n, int, int)`, + `(n, n, int)` and `(n, int)`. + """ + for i, n in enumerate(x): + if isinstance(n, int): + return x[i:] + return (0,) + + +def remove_timeindex(x): + """ + Remove the timeindex from oemof tuples for mulitperiod models. + Slice up to integer values (node labels) + + The timestep is removed from tuples of type `(n, n, int, int)`, + `(n, n, int)` and `(n, int)`. + """ + for i, n in enumerate(x): + if isinstance(n, int): + return x[:i] + return x + + def create_dataframe(om): """ Create a result dataframe with all optimization data. @@ -100,11 +130,11 @@ def create_dataframe(om): # columns for the oemof tuple and timestep are created df["oemof_tuple"] = df["pyomo_tuple"].map(get_tuple) df = df[df["oemof_tuple"].map(lambda x: x is not None)] - df["timestep"] = df["oemof_tuple"].map(get_timestep) - df["oemof_tuple"] = df["oemof_tuple"].map(remove_timestep) - + df["timeindex"] = df["oemof_tuple"].map(get_timeindex) + df["oemof_tuple"] = df["oemof_tuple"].map(remove_timeindex) # order the data by oemof tuple and timestep - df = df.sort_values(["oemof_tuple", "timestep"], ascending=[True, True]) + df = df.sort_values(["oemof_tuple", "timeindex"], + ascending=[True, True]) # drop empty decision variables df = df.dropna(subset=["value"]) @@ -118,17 +148,34 @@ def results(om): Results from Pyomo are written into a dictionary of pandas objects where a Series holds all scalar values and a dataframe all sequences for nodes - and flows. + and flows for a standard model. For a MultiPeriodModel, the investment + values are given in a DataFrame indexed by periods. The dictionary is keyed by the nodes e.g. `results[idx]['scalars']` - and flows e.g. `results[n, n]['sequences']`. + and flows e.g. `results[n, n]['sequences']` for a standard model. """ df = create_dataframe(om) + period_indexed = [ + "invest", + "total", + "old", + "old_exo", + "old_end" + ] + period_timestep_indexed = ["flow"] + # TODO: Take care of initial storage content instead of just ignoring + to_be_ignored = ["init_content"] + timestep_indexed = [el for el in df["variable_name"].unique() + if el not in period_indexed + and el not in period_timestep_indexed + and el not in to_be_ignored] + scalars_col = "period_scalars" + # create a dict of dataframes keyed by oemof tuples + # TODO / QUESTION: Could this be sped up using DFs / arrays instead?! df_dict = { - k - if len(k) > 1 - else (k[0], None): v[["timestep", "variable_name", "value"]] + k if len(k) > 1 else (k[0], None): + v[["timeindex", "variable_name", "value"]] for k, v in df.groupby("oemof_tuple") } @@ -136,45 +183,49 @@ def results(om): # dataframe dict into a series for scalar data and dataframe for sequences result = {} for k in df_dict: - df_dict[k].set_index("timestep", inplace=True) + df_dict[k].set_index("timeindex", inplace=True) df_dict[k] = df_dict[k].pivot(columns="variable_name", values="value") + + # TODO: Revise and potentially speed up + # Split data set + timeindex_cols = [col for col in df_dict[k].columns + if col in timestep_indexed or col == "flow"] + period_cols = [col for col in df_dict[k].columns + if col not in timeindex_cols] + sequences = df_dict[k][timeindex_cols].dropna() + if sequences.empty: + sequences = pd.DataFrame(index=om.es.timeindex) + # periods equal to years (will probably be the standard use case) + periods = sorted(list(set(om.es.timeindex.year))) + d = dict(zip([(el, ) for el in range(len(periods))], periods)) + period_scalars = df_dict[k][period_cols].dropna() + if period_scalars.empty: + period_scalars = pd.DataFrame(index=d.values()) try: - df_dict[k].index = om.es.timeindex - except ValueError as e: - msg = ( - "\nFlowBlock: {0}-{1}. This could be caused by NaN-values in" - " your input data." - ) - raise type(e)( - str(e) + msg.format(k[0].label, k[1].label) - ).with_traceback(sys.exc_info()[2]) - try: - condition = df_dict[k].isnull().any() - scalars = df_dict[k].loc[:, condition].dropna().iloc[0] - sequences = df_dict[k].loc[:, ~condition] - result[k] = {"scalars": scalars, "sequences": sequences} + sequences.index = om.es.timeindex + period_scalars.rename(index=d, inplace=True) + period_scalars.index.name = "period" + result[k] = {scalars_col: period_scalars, + "sequences": sequences} except IndexError: error_message = ( - "Cannot access index on result data. " - + "Did the optimization terminate" - + " without errors?" + "Some indices seem to be not matching.\n" + "Cannot properly extract model results." ) raise IndexError(error_message) # add dual variables for bus constraints if om.dual is not None: - grouped = groupby( - sorted(om.BusBlock.balance.iterkeys()), lambda p: p[0] - ) - for bus, timesteps in grouped: - duals = [ - om.dual[om.BusBlock.balance[bus, t]] for _, t in timesteps - ] + grouped = groupby(sorted(om.Bus.balance.iterkeys()), + lambda p: p[0]) + for bus, timeindex in grouped: + duals = [om.dual[om.Bus.balance[bus, p, t]] + for _, p, t in timeindex] df = pd.DataFrame({"duals": duals}, index=om.es.timeindex) if (bus, None) not in result.keys(): result[(bus, None)] = { "sequences": df, - "scalars": pd.Series(dtype=float), + scalars_col: pd.Series(dtype=float), } else: result[(bus, None)]["sequences"]["duals"] = duals diff --git a/src/oemof/solph/views.py b/src/oemof/solph/views.py index df6df4674..a95384abf 100644 --- a/src/oemof/solph/views.py +++ b/src/oemof/solph/views.py @@ -9,6 +9,7 @@ SPDX-FileCopyrightText: Cord Kaldemeyer SPDX-FileCopyrightText: Stephan Günther SPDX-FileCopyrightText: henhuy +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -29,8 +30,10 @@ def node(results, node, multiindex=False, keep_none_type=False): Obtain results for a single node e.g. a Bus or Component. Either a node or its label string can be passed. - Results are written into a dictionary which is keyed by 'scalars' and - 'sequences' holding respective data in a pandas Series and DataFrame. + Results are written into a dictionary which is keyed by 'scalars' + (resp. 'periods_scalars' for a multi-period model) and + 'sequences' holding respective data in a pandas Series (resp. DataFrame) + and DataFrame. """ def replace_none(col_list, reverse=False): @@ -58,19 +61,24 @@ def replace_none(col_list, reverse=False): filtered = {} # create a series with tuples as index labels for scalars + scalars_col = "scalars" + # Check for multi-period model (different naming) + if "period_scalars" in list(list(results.values())[0].keys()): + scalars_col = "period_scalars" + scalars = { - k: v["scalars"] + k: v[scalars_col] for k, v in results.items() - if node in k and not v["scalars"].empty + if node in k and not v[scalars_col].empty } if scalars: # aggregate data filtered["scalars"] = pd.concat(scalars.values(), axis=0) # assign index values idx = { - k: [c for c in v["scalars"].index] + k: [c for c in v[scalars_col].index] for k, v in results.items() - if node in k and not v["scalars"].empty + if node in k and not v[scalars_col].empty } idx = [tuple((k, m) for m in v) for k, v in idx.items()] idx = [i for sublist in idx for i in sublist] From 48d108b3f293156cfa46b6fdde5d857957698d8d Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 4 Dec 2021 13:48:02 +0100 Subject: [PATCH 0025/1363] Integrate multi-period draft into bus modules --- src/oemof/solph/buses/_bus.py | 13 +++++++------ .../solph/buses/experimental/_electrical_bus.py | 3 ++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/oemof/solph/buses/_bus.py b/src/oemof/solph/buses/_bus.py index 4267a5cd5..491e0d464 100644 --- a/src/oemof/solph/buses/_bus.py +++ b/src/oemof/solph/buses/_bus.py @@ -11,6 +11,7 @@ SPDX-FileCopyrightText: Birgit Schachler SPDX-FileCopyrightText: jnnr SPDX-FileCopyrightText: jmloenneberga +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -80,14 +81,14 @@ def _create(self, group=None): outs[n] = [o for o in n.outputs] def _busbalance_rule(block): - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: - lhs = sum(m.flow[i, g, t] for i in ins[g]) - rhs = sum(m.flow[g, o, t] for o in outs[g]) - expr = lhs == rhs + lhs = sum(m.flow[i, g, p, t] for i in ins[g]) + rhs = sum(m.flow[g, o, p, t] for o in outs[g]) + expr = (lhs == rhs) # no inflows no outflows yield: 0 == 0 which is True if expr is not True: - block.balance.add((g, t), expr) + block.balance.add((g, p, t), expr) - self.balance = Constraint(group, m.TIMESTEPS, noruleinit=True) + self.balance = Constraint(group, m.TIMEINDEX, noruleinit=True) self.balance_build = BuildAction(rule=_busbalance_rule) diff --git a/src/oemof/solph/buses/experimental/_electrical_bus.py b/src/oemof/solph/buses/experimental/_electrical_bus.py index 401989056..81e7ecaac 100644 --- a/src/oemof/solph/buses/experimental/_electrical_bus.py +++ b/src/oemof/solph/buses/experimental/_electrical_bus.py @@ -11,6 +11,7 @@ SPDX-FileCopyrightText: jakob-wo SPDX-FileCopyrightText: gplssm SPDX-FileCopyrightText: jnnr +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -20,7 +21,7 @@ class ElectricalBus(Bus): - r"""A electrical bus object. Every node has to be connected to BusBlock. + r"""An electrical bus object. Every node has to be connected to BusBlock. This BusBlock is used in combination with ElectricalLine objects for linear optimal power flow (lopf) calculations. From c2dae5a37e969b58af393ae81b02adb68a3d0e88 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 4 Dec 2021 14:24:17 +0100 Subject: [PATCH 0026/1363] Integrate multi-period draft into constraint modules --- src/oemof/solph/constraints/integral_limit.py | 151 +++++++++++++++--- .../solph/constraints/investment_limit.py | 125 ++++++++++++--- 2 files changed, 232 insertions(+), 44 deletions(-) diff --git a/src/oemof/solph/constraints/integral_limit.py b/src/oemof/solph/constraints/integral_limit.py index 6085b7715..ce0cd00a6 100644 --- a/src/oemof/solph/constraints/integral_limit.py +++ b/src/oemof/solph/constraints/integral_limit.py @@ -6,6 +6,8 @@ SPDX-FileCopyrightText: Simon Hilpert SPDX-FileCopyrightText: Patrik Schönfeldt SPDX-FileCopyrightText: Johannes Röder +SPDX-FileCopyrightText: Johannes Kochems +SPDX-FileCopyrightText: Johannes Giehl SPDX-License-Identifier: MIT @@ -30,6 +32,21 @@ def emission_limit(om, flows=None, limit=None): ) +def emission_limit_per_period(om, flows=None, limit=None): + r""" + Short handle for generic_periodical_integral_limit() + with keyword="emission_factor". Only applicable for multi-period models. + + Note + ---- + Flow objects required an attribute "emission_factor"! + + """ + generic_periodical_integral_limit( + om, keyword="emission_factor", flows=flows, limit=limit + ) + + def generic_integral_limit(om, keyword, flows=None, limit=None): r"""Set a global limit for flows weighted by attribute called keyword. The attribute named by keyword has to be added @@ -94,22 +111,7 @@ def generic_integral_limit(om, keyword, flows=None, limit=None): >>> model = solph.constraints.generic_integral_limit( ... model, "my_factor", flow_with_keyword, limit=777) """ - if flows is None: - flows = {} - for (i, o) in om.flows: - if hasattr(om.flows[i, o], keyword): - flows[(i, o)] = om.flows[i, o] - - else: - for (i, o) in flows: - if not hasattr(flows[i, o], keyword): - raise AttributeError( - ( - "Flow with source: {0} and target: {1} " - "has no attribute {2}." - ).format(i.label, o.label, keyword) - ) - + flows = _check_and_set_flows(om, flows, keyword) limit_name = "integral_limit_" + keyword setattr( @@ -117,11 +119,11 @@ def generic_integral_limit(om, keyword, flows=None, limit=None): limit_name, po.Expression( expr=sum( - om.flow[inflow, outflow, t] + om.flow[inflow, outflow, p, t] * om.timeincrement[t] * sequence(getattr(flows[inflow, outflow], keyword))[t] for (inflow, outflow) in flows - for t in om.TIMESTEPS + for p, t in om.TIMEINDEX ) ), ) @@ -133,3 +135,116 @@ def generic_integral_limit(om, keyword, flows=None, limit=None): ) return om + + +def generic_periodical_integral_limit(om, keyword, flows=None, limit=None): + r"""Set a global limit for flows for each period of a multi-period model + which is weighted by attribute called keyword. + The attribute named by keyword has to be added + to every flow you want to take into account. + + Total value of keyword attributes after optimization can be retrieved + calling the :attr:`om.oemof.solph.Model.integral_limit_${keyword}()`. + + Parameters + ---------- + om : oemof.solph.Model + Model to which constraints are added. + flows : dict + Dictionary holding the flows that should be considered in constraint. + Keys are (source, target) objects of the Flow. If no dictionary is + given all flows containing the keyword attribute will be + used. + keyword : string + attribute to consider + limit : sequence of float + Absolute limit of keyword attribute for the energy system. + + Note + ---- + Flow objects required an attribute named like keyword! + + **Constraint:** + + .. math:: \sum_{i \in F_I} \sum_{t \in T} P_i(t) \cdot w_i(t) + \cdot \tau(t) \leq L(p) \forall p in \textrm{PERIODS} + + + For the parameter and variable explanation, please refer to the docs + of generic_integral_limit. + + """ + flows = _check_and_set_flows(om, flows, keyword) + limit_name = "integral_limit_" + keyword + + if not om.es.multi_period: + msg = ("generic_periodical_integral_limit is only applicable\n" + "for multi-period models.\nFor standard models, use " + "generic_integral_limit instead.") + raise ValueError(msg) + + if limit is not None: + limit = sequence(limit) + else: + msg = ("You have to provide a limit for each period!\n" + "If you provide a scalar value, this will be applied as a " + "limit for each period.") + raise ValueError(msg) + + def _periodical_integral_limit_rule(m, p): + expr = sum( + om.flow[inflow, outflow, p, t] + * om.timeincrement[t] + * sequence(getattr(flows[inflow, outflow], keyword))[t] + for (inflow, outflow) in flows + for t in m.TIMESTEPS_IN_PERIOD[p] + ) + + return expr <= limit[p] + + om.periodical_integral_limit = po.Constraint( + om.PERIODS, + rule=_periodical_integral_limit_rule, + name=limit_name + "_constraint" + ) + + return om + + +def _check_and_set_flows(om, flows, keyword): + """Checks and sets flows if needed + + Parameters + ---------- + om : oemof.solph.Model + Model to which constraints are added. + + flows : dict + Dictionary holding the flows that should be considered in constraint. + Keys are (source, target) objects of the Flow. If no dictionary is + given all flows containing the keyword attribute will be + used. + + keyword : string + attribute to consider + + Returns + ------- + flows : dict + the flows to be considered + """ + if flows is None: + flows = {} + for (i, o) in om.flows: + if hasattr(om.flows[i, o], keyword): + flows[(i, o)] = om.flows[i, o] + + else: + for (i, o) in flows: + if not hasattr(flows[i, o], keyword): + raise AttributeError( + ( + "Flow with source: {0} and target: {1} " + "has no attribute {2}." + ).format(i.label, o.label, keyword) + ) diff --git a/src/oemof/solph/constraints/investment_limit.py b/src/oemof/solph/constraints/investment_limit.py index da526b3c5..dce7d7e85 100644 --- a/src/oemof/solph/constraints/investment_limit.py +++ b/src/oemof/solph/constraints/investment_limit.py @@ -6,6 +6,8 @@ SPDX-FileCopyrightText: Simon Hilpert SPDX-FileCopyrightText: Patrik Schönfeldt SPDX-FileCopyrightText: Johannes Röder +SPDX-FileCopyrightText: Johannes Kochems +SPDX-FileCopyrightText: Johannes Giehl SPDX-License-Identifier: MIT @@ -13,6 +15,8 @@ from pyomo import environ as po +from oemof.solph import sequence + def investment_limit(model, limit=None): r"""Set an absolute limit for the total investment costs of an investment @@ -36,6 +40,16 @@ def investment_rule(m): if hasattr(m, "GenericInvestmentStorageBlock"): expr += m.GenericInvestmentStorageBlock.investment_costs + + if hasattr(m, "SinkDSMOemofInvestmentBlock"): + expr += m.SinkDSMOemofInvestmentBlock.investment_costs + + if hasattr(m, "SinkDSMDIWInvestmentBlock"): + expr += m.SinkDSMDIWInvestmentBlock.investment_costs + + if hasattr(m, "SinkDSMDLRInvestmentBlock"): + expr += m.SinkDSMDLRInvestmentBlock.investment_costs + return expr <= limit model.investment_limit = po.Constraint(rule=investment_rule) @@ -43,6 +57,65 @@ def investment_rule(m): return model +def investment_limit_per_period(model, limit=None): + r"""Set an absolute limit for the total investment costs of a + investment optimization problem for each period + of the problem. + + .. math:: \sum_{investment\_costs(p)} \leq limit(p) + \forall p in \textrm{PERIODS} + + Parameters + ---------- + model : oemof.solph.Model + Model to which the constraint is added + limit : sequence of float, :math:`limit(p)` + Absolute limit of the investment for each period + (i.e. RHS of constraint) + """ + + if not model.es.multi_period: + msg = ("investment_limit_per_period is only applicable " + "for multi-period models.\nIn order to create such a model, " + "set attribute `multi_period` of your energy system to True.") + raise ValueError(msg) + + if limit is not None: + limit = sequence(limit) + else: + msg = ("You have to provide an investment limit for each period!\n" + "If you provide a scalar value, this will be applied as a " + "limit for each period.") + raise ValueError(msg) + + def investment_period_rule(m, p): + expr = 0 + + if hasattr(m, "InvestmentFlow"): + expr += m.InvestmentFlow.period_investment_costs[p] + + if hasattr(m, "GenericInvestmentStorageBlock"): + expr += m.GenericInvestmentStorageBlock.period_investment_costs[p] + + if hasattr(m, "SinkDSMOemofInvestmentBlock"): + expr += m.SinkDSMOemofInvestmentBlock.period_investment_costs[p] + + if hasattr(m, "SinkDSMDIWInvestmentBlock"): + expr += m.SinkDSMDIWInvestmentBlock.period_investment_costs[p] + + if hasattr(m, "SinkDSMDLRInvestmentBlock"): + expr += m.SinkDSMDLRInvestmentBlock.period_investment_costs[p] + + return expr <= limit[p] + + model.investment_limit_per_period = po.Constraint( + model.PERIODS, + rule=investment_period_rule + ) + + return model + + def additional_investment_flow_limit(model, keyword, limit=None): r""" Global limit for investment flows weighted by an attribute keyword. @@ -63,15 +136,15 @@ def additional_investment_flow_limit(model, keyword, limit=None): The symbols used are defined as follows (with Variables (V) and Parameters (P)): - +---------------+------------------------------------+------+--------------------------------------------------------------+ - | symbol | attribute | type | explanation | - +===============+====================================+======+==============================================================+ - | :math:`P_{i}` | `InvestmentFlowBlock.invest[i, o]` | V | installed capacity of investment flow | - +---------------+------------------------------------+------+--------------------------------------------------------------+ - | :math:`w_i` | `keyword` | P | weight given to investment flow named according to `keyword` | - +---------------+------------------------------------+------+--------------------------------------------------------------+ - | :math:`limit` | `limit` | P | global limit given by keyword `limit` | - +---------------+------------------------------------+------+--------------------------------------------------------------+ + +---------------+-------------------------------+------+--------------------------------------------------------------+ + | symbol | attribute | type | explanation | + +===============+===============================+======+==============================================================+ + | :math:`P_{i}` | `InvestmentFlow.invest[i, o]` | V | installed capacity of investment flow | + +---------------+-------------------------------+------+--------------------------------------------------------------+ + | :math:`w_i` | `keyword` | P | weight given to investment flow named according to `keyword` | + +---------------+-------------------------------+------+--------------------------------------------------------------+ + | :math:`limit` | `limit` | P | global limit given by keyword `limit` | + +---------------+-------------------------------+------+--------------------------------------------------------------+ Parameters ---------- @@ -94,12 +167,12 @@ def additional_investment_flow_limit(model, keyword, limit=None): >>> from oemof import solph >>> date_time_index = pd.date_range('1/1/2020', periods=5, freq='H') >>> es = solph.EnergySystem(timeindex=date_time_index) - >>> bus = solph.buses.Bus(label='bus_1') - >>> sink = solph.components.Sink(label="sink", inputs={bus: - ... solph.flows.Flow(nominal_value=10, fix=[10, 20, 30, 40, 50])}) - >>> src1 = solph.components.Source(label='source_0', outputs={bus: solph.flows.Flow( + >>> bus = solph.Bus(label='bus_1') + >>> sink = solph.Sink(label="sink", inputs={bus: + ... solph.Flow(nominal_value=10, fix=[10, 20, 30, 40, 50])}) + >>> src1 = solph.Source(label='source_0', outputs={bus: solph.Flow( ... investment=solph.Investment(ep_costs=50, space=4))}) - >>> src2 = solph.components.Source(label='source_1', outputs={bus: solph.flows.Flow( + >>> src2 = solph.Source(label='source_1', outputs={bus: solph.Flow( ... investment=solph.Investment(ep_costs=100, space=1))}) >>> es.add(bus, sink, src1, src2) >>> model = solph.Model(es) @@ -117,22 +190,22 @@ def additional_investment_flow_limit(model, keyword, limit=None): limit_name = "invest_limit_" + keyword - setattr( - model, - limit_name, - po.Expression( - expr=sum( - model.InvestmentFlowBlock.invest[inflow, outflow] + def _additional_limit_rule(block): + for p in model.PERIODS: + lhs = sum( + model.InvestmentFlow.invest[inflow, outflow, p] * getattr(invest_flows[inflow, outflow], keyword) for (inflow, outflow) in invest_flows ) - ), - ) + rhs = limit + model.add_limit.add(p, (lhs <= rhs)) - setattr( - model, - limit_name + "_constraint", - po.Constraint(expr=(getattr(model, limit_name) <= limit)), + model.add_limit = po.Constraint( + model.PERIODS, + noruleinit=True, + name=limit_name + "_constraint" ) + model.add_limit_build = po.BuildAction( + rule=_additional_limit_rule) return model From fbc4f94df0e8bc0473235c091f25c72108520135 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 4 Dec 2021 14:24:39 +0100 Subject: [PATCH 0027/1363] Fix typo and bug in docs --- src/oemof/solph/constraints/shared_limit.py | 2 +- src/oemof/solph/processing.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oemof/solph/constraints/shared_limit.py b/src/oemof/solph/constraints/shared_limit.py index 113d32b7d..9afc921bb 100644 --- a/src/oemof/solph/constraints/shared_limit.py +++ b/src/oemof/solph/constraints/shared_limit.py @@ -45,7 +45,7 @@ def shared_limit( lower_limit : numeric the lower limit upper_limit : numeric - the lower limit + the upper limit Examples -------- diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index b9d41495c..077358b00 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -105,7 +105,7 @@ def create_dataframe(om): Results from Pyomo are written into pandas DataFrame where separate columns are created for the variable index e.g. for tuples of the flows and - components or the timesteps. + components or the timesteps / timeindices. """ # get all pyomo variables including their block block_vars = list( From 2c81fc4df134e28c41b647710370d3d5b49bcb15 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sun, 5 Dec 2021 18:23:34 +0100 Subject: [PATCH 0028/1363] Integrate multi-period draft into modules of flow package --- src/oemof/solph/flows/_flow.py | 148 +++--- src/oemof/solph/flows/_investment_flow.py | 431 +++++++++++++++--- src/oemof/solph/flows/_non_convex_flow.py | 236 +++++++--- .../flows/experimental/_electrical_line.py | 8 +- 4 files changed, 623 insertions(+), 200 deletions(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index c9c3c8c3e..dee4c5323 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -10,6 +10,7 @@ SPDX-FileCopyrightText: Birgit Schachler SPDX-FileCopyrightText: jnnr SPDX-FileCopyrightText: jmloenneberga +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -60,8 +61,7 @@ class Flow(on.Edge): * `'ub'`: numeric (iterable, scalar or None), the normed *upper bound* on the positive difference (`flow[t-1] < flow[t]`) of two consecutive flow values. - * `'costs``: numeric (scalar or None), the gradient cost per - unit. + * `'costs``: REMOVED! negative_gradient : :obj:`dict`, default: `{'ub': None, 'costs': 0}` @@ -70,8 +70,7 @@ class Flow(on.Edge): * `'ub'`: numeric (iterable, scalar or None), the normed *upper bound* on the negative difference (`flow[t-1] > flow[t]`) of two consecutive flow values. - * `'costs``: numeric (scalar or None), the gradient cost per - unit. + * `'costs``: REMOVED! summed_max : numeric, :math:`f_{sum,max}` Specific maximum value summed over all timesteps. Will be multiplied @@ -82,6 +81,7 @@ class Flow(on.Edge): The costs associated with one unit of the flow. If this is set the costs will be added to the objective expression of the optimization problem. + Note: In a multi-period model, nominal costs have to be used. fixed : boolean Boolean value indicating if a flow is fixed during the optimization problem to its ex-ante set value. Used in combination with the @@ -104,12 +104,12 @@ class Flow(on.Edge): Notes ----- The following sets, variables, constraints and objective parts are created - * :py:class:`~oemof.solph..flows.flow.FlowBlock` - * :py:class:`~oemof.solph..flows.investment_flow.InvestmentFlowBlock` + * :py:class:`~oemof.solph.flows.flow.FlowBlock` + * :py:class:`~oemof.solph.flows.investment_flow.InvestmentFlowBlock` (additionally if Investment object is present) - * :py:class:`~oemof.solph..flows.non_convex_flow.NonConvexFlowBlock` + * :py:class:`~oemof.solph.flows.non_convex_flow.NonConvexFlowBlock` (If nonconvex object is present, CAUTION: replaces - :py:class:`~oemof.solph.flows.flow.FlowBlock` + :py:class:`~oemof.solphflows.flow.FlowBlock` class and a MILP will be build) Examples @@ -146,19 +146,21 @@ def __init__(self, **kwargs): "nonconvex", "integer", ] - sequences = ["fix", "variable_costs", "min", "max"] + sequences = ["fix", "variable_costs", "fixed_costs", "min", "max"] dictionaries = ["positive_gradient", "negative_gradient"] defaults = { "variable_costs": 0, - "positive_gradient": {"ub": None, "costs": 0}, - "negative_gradient": {"ub": None, "costs": 0}, + "positive_gradient": {"ub": None}, + "negative_gradient": {"ub": None}, } keys = [k for k in kwargs if k != "label"] if "fixed_costs" in keys: - raise AttributeError( - "The `fixed_costs` attribute has been removed" " with v0.2!" - ) + msg = ("Be aware that the fixed costs attribute is only\n" + "meant to be used for multi-period models.\n" + "It has been decided to remove the `fixed_costs` " + "attribute with v0.2 for regular uses!") + warn(msg, debugging.SuspiciousUsageWarning) if "actual_value" in keys: raise AttributeError( @@ -192,13 +194,24 @@ def __init__(self, **kwargs): if kwargs.get("max") is None: defaults["max"] = 1 + # Check gradient dictionaries for non-valid keys + for gradient_dict in ["negative_gradient", "positive_gradient"]: + if gradient_dict in kwargs: + if list(kwargs[gradient_dict].keys()) != list( + defaults[gradient_dict].keys() + ): + msg = ( + "Only the key 'ub' is allowed for the '{0}' attribute" + ) + raise AttributeError(msg.format(gradient_dict)) + for attribute in set(scalars + sequences + dictionaries + keys): value = kwargs.get(attribute, defaults.get(attribute)) if attribute in dictionaries: setattr( self, attribute, - {"ub": sequence(value["ub"]), "costs": value["costs"]}, + {"ub": sequence(value["ub"])}, ) else: @@ -398,8 +411,8 @@ def _flow_summed_max_rule(model): """Rule definition for build action of max. sum flow constraint.""" for inp, out in self.SUMMED_MAX_FLOWS: lhs = sum( - m.flow[inp, out, ts] * m.timeincrement[ts] - for ts in m.TIMESTEPS + m.flow[inp, out, p, ts] * m.timeincrement[ts] + for p, ts in m.TIMEINDEX ) rhs = ( m.flows[inp, out].summed_max @@ -414,8 +427,8 @@ def _flow_summed_min_rule(model): """Rule definition for build action of min. sum flow constraint.""" for inp, out in self.SUMMED_MIN_FLOWS: lhs = sum( - m.flow[inp, out, ts] * m.timeincrement[ts] - for ts in m.TIMESTEPS + m.flow[inp, out, p, ts] * m.timeincrement[ts] + for p, ts in m.TIMEINDEX ) rhs = ( m.flows[inp, out].summed_min @@ -429,18 +442,23 @@ def _flow_summed_min_rule(model): def _positive_gradient_flow_rule(model): """Rule definition for positive gradient constraint.""" for inp, out in self.POSITIVE_GRADIENT_FLOWS: - for ts in m.TIMESTEPS: - if ts > 0: - lhs = m.flow[inp, out, ts] - m.flow[inp, out, ts - 1] - rhs = self.positive_gradient[inp, out, ts] + for index in range(1, len(m.TIMEINDEX) + 1): + if m.TIMEINDEX[index][1] > 0: + lhs = (m.flow[inp, out, m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1]] + - m.flow[inp, out, m.TIMEINDEX[index - 1][0], + m.TIMEINDEX[index - 1][1]]) + rhs = self.positive_gradient[ + inp, out, m.TIMEINDEX[index][1]] self.positive_gradient_constr.add( - (inp, out, ts), lhs <= rhs - ) + (inp, out, m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1]), + lhs <= rhs) else: pass # return(Constraint.Skip) self.positive_gradient_constr = Constraint( - self.POSITIVE_GRADIENT_FLOWS, m.TIMESTEPS, noruleinit=True + self.POSITIVE_GRADIENT_FLOWS, m.TIMEINDEX, noruleinit=True ) self.positive_gradient_build = BuildAction( rule=_positive_gradient_flow_rule @@ -449,29 +467,34 @@ def _positive_gradient_flow_rule(model): def _negative_gradient_flow_rule(model): """Rule definition for negative gradient constraint.""" for inp, out in self.NEGATIVE_GRADIENT_FLOWS: - for ts in m.TIMESTEPS: - if ts > 0: - lhs = m.flow[inp, out, ts - 1] - m.flow[inp, out, ts] - rhs = self.negative_gradient[inp, out, ts] + for index in range(1, len(m.TIMEINDEX) + 1): + if m.TIMEINDEX[index][1] > 0: + lhs = (m.flow[inp, out, m.TIMEINDEX[index - 1][0], + m.TIMEINDEX[index - 1][1]] + - m.flow[inp, out, m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1]]) + rhs = self.negative_gradient[ + inp, out, m.TIMEINDEX[index][1]] self.negative_gradient_constr.add( - (inp, out, ts), lhs <= rhs - ) + (inp, out, m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1]), + lhs <= rhs) else: pass # return(Constraint.Skip) self.negative_gradient_constr = Constraint( - self.NEGATIVE_GRADIENT_FLOWS, m.TIMESTEPS, noruleinit=True + self.NEGATIVE_GRADIENT_FLOWS, m.TIMEINDEX, noruleinit=True ) self.negative_gradient_build = BuildAction( rule=_negative_gradient_flow_rule ) - def _integer_flow_rule(block, ii, oi, ti): + def _integer_flow_rule(block, ii, oi, pi, ti): """Force flow variable to NonNegativeInteger values.""" - return self.integer_flow[ii, oi, ti] == m.flow[ii, oi, ti] + return self.integer_flow[ii, oi, pi, ti] == m.flow[ii, oi, pi, ti] self.integer_flow_constr = Constraint( - self.INTEGER_FLOWS, m.TIMESTEPS, rule=_integer_flow_rule + self.INTEGER_FLOWS, m.TIMEINDEX, rule=_integer_flow_rule ) def _objective_expression(self): @@ -481,29 +504,36 @@ def _objective_expression(self): m = self.parent_block() variable_costs = 0 - gradient_costs = 0 - - for i, o in m.FLOWS: - if m.flows[i, o].variable_costs[0] is not None: - for t in m.TIMESTEPS: - variable_costs += ( - m.flow[i, o, t] - * m.objective_weighting[t] - * m.flows[i, o].variable_costs[t] - ) + fixed_costs = 0 + + if not m.es.multi_period: + for i, o in m.FLOWS: + if m.flows[i, o].variable_costs[0] is not None: + for p, t in m.TIMEINDEX: + variable_costs += ( + m.flow[i, o, p, t] + * m.objective_weighting[t] + * m.flows[i, o].variable_costs[t] + ) - if m.flows[i, o].positive_gradient["ub"][0] is not None: - for t in m.TIMESTEPS: - gradient_costs += ( - self.positive_gradient[i, o, t] - * m.flows[i, o].positive_gradient["costs"] - ) + else: + for i, o in m.FLOWS: + if m.flows[i, o].variable_costs[0] is not None: + for p, t in m.TIMEINDEX: + variable_costs += ( + m.flow[i, o, p, t] + * m.objective_weighting[t] + * m.flows[i, o].variable_costs[t] + * ((1 + m.discount_rate) ** -p) + ) - if m.flows[i, o].negative_gradient["ub"][0] is not None: - for t in m.TIMESTEPS: - gradient_costs += ( - self.negative_gradient[i, o, t] - * m.flows[i, o].negative_gradient["costs"] - ) + if (m.flows[i, o].fixed_costs[0] is not None + and m.flows[i, o].nominal_value is not None): + for p in m.PERIODS: + fixed_costs += ( + m.flows[i, o].nominal_value + * m.flows[i, o].fixed_costs[p] + * ((1 + m.discount_rate) ** -p) + ) - return variable_costs + gradient_costs + return variable_costs + fixed_costs diff --git a/src/oemof/solph/flows/_investment_flow.py b/src/oemof/solph/flows/_investment_flow.py index 0c859256c..b3deb779b 100644 --- a/src/oemof/solph/flows/_investment_flow.py +++ b/src/oemof/solph/flows/_investment_flow.py @@ -10,12 +10,17 @@ SPDX-FileCopyrightText: Birgit Schachler SPDX-FileCopyrightText: jnnr SPDX-FileCopyrightText: jmloenneberga +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT """ +from warnings import warn +from oemof.tools import debugging +from oemof.tools import economics from pyomo.core import Binary +from pyomo.core import BuildAction from pyomo.core import Constraint from pyomo.core import Expression from pyomo.core import NonNegativeReals @@ -292,145 +297,439 @@ def _create(self, group=None): ] ) + self.OVERALL_MAXIMUM_INVESTFLOWS = Set( + initialize=[ + (g[0], g[1]) for g in group + if g[2].investment.overall_maximum is not None] + ) + + self.OVERALL_MINIMUM_INVESTFLOWS = Set( + initialize=[ + (g[0], g[1]) for g in group + if g[2].investment.overall_minimum is not None] + ) + # ######################### VARIABLES ################################# - def _investvar_bound_rule(block, i, o): + def _investvar_bound_rule(block, i, o, p): """Rule definition for bounds of invest variable.""" if (i, o) in self.CONVEX_INVESTFLOWS: return ( - m.flows[i, o].investment.minimum, - m.flows[i, o].investment.maximum, + m.flows[i, o].investment.minimum[p], + m.flows[i, o].investment.maximum[p], ) elif (i, o) in self.NON_CONVEX_INVESTFLOWS: - return 0, m.flows[i, o].investment.maximum + return 0, m.flows[i, o].investment.maximum[p] - # create invest variable for a investment flow + # create invest variable for an investment flow self.invest = Var( self.INVESTFLOWS, + m.PERIODS, within=NonNegativeReals, bounds=_investvar_bound_rule, ) + # Total capacity + self.total = Var( + self.INVESTFLOWS, + m.PERIODS, + within=NonNegativeReals + ) + + # Old capacity is built out of old exogenous and endogenous capacities + self.old = Var( + self.INVESTFLOWS, + m.PERIODS, + within=NonNegativeReals + ) + + # Old endogenous capacity to be decommissioned (due to lifetime) + self.old_end = Var( + self.INVESTFLOWS, + m.PERIODS, + within=NonNegativeReals + ) + + # Old exogenous capacity to be decommissioned (due to lifetime) + self.old_exo = Var( + self.INVESTFLOWS, + m.PERIODS, + within=NonNegativeReals + ) + # create status variable for a non-convex investment flow - self.invest_status = Var(self.NON_CONVEX_INVESTFLOWS, within=Binary) - # ######################### CONSTRAINTS ############################### + self.invest_status = Var( + self.NON_CONVEX_INVESTFLOWS, + m.PERIODS, + within=Binary + ) - def _min_invest_rule(block, i, o): + # ######################### CONSTRAINTS ############################### + def _min_invest_rule(block): """Rule definition for applying a minimum investment""" - expr = ( - m.flows[i, o].investment.minimum * self.invest_status[i, o] - <= self.invest[i, o] - ) - return expr + for i, o in self.NON_CONVEX_INVESTFLOWS: + for p in m.PERIODS: + expr = ( + m.flows[i, o].investment.minimum[p] + * self.invest_status[i, o, p] + <= self.invest[i, o, p] + ) + self.minimum_rule.add((i, o, p), expr) self.minimum_rule = Constraint( - self.NON_CONVEX_INVESTFLOWS, rule=_min_invest_rule + self.NON_CONVEX_INVESTFLOWS, m.PERIODS, + noruleinit=True + ) + self.minimum_rule_build = BuildAction( + rule=_min_invest_rule ) - def _max_invest_rule(block, i, o): + def _max_invest_rule(block): """Rule definition for applying a minimum investment""" - expr = self.invest[i, o] <= ( - m.flows[i, o].investment.maximum * self.invest_status[i, o] - ) - return expr + for i, o in self.NON_CONVEX_INVESTFLOWS: + for p in m.PERIODS: + expr = ( + self.invest[i, o, p] + <= (m.flows[i, o].investment.maximum[p] + * self.invest_status[i, o, p]) + ) + self.maximum_rule.add((i, o, p), expr) self.maximum_rule = Constraint( - self.NON_CONVEX_INVESTFLOWS, rule=_max_invest_rule + self.NON_CONVEX_INVESTFLOWS, m.PERIODS, + noruleinit=True + ) + self.maximum_rule_build = BuildAction( + rule=_max_invest_rule + ) + + # Handle unit lifetimes + def _total_capacity_rule(block): + """Rule definition for determining total installed + capacity (taking decommissioning into account) + """ + for i, o in self.INVESTFLOWS: + for p in m.PERIODS: + if p == 0: + expr = (self.total[i, o, p] + == self.invest[i, o, p] + + m.flows[i, o].investment.existing) + self.total_rule.add((i, o, p), expr) + else: + expr = (self.total[i, o, p] + == self.invest[i, o, p] + + self.total[i, o, p - 1] + - self.old[i, o, p]) + self.total_rule.add((i, o, p), expr) + + self.total_rule = Constraint( + self.INVESTFLOWS, m.PERIODS, + noruleinit=True + ) + self.total_rule_build = BuildAction( + rule=_total_capacity_rule) + + def _old_capacity_rule_end(block): + """Rule definition for determining old endogenously installed + capacity to be decommissioned due to reaching its lifetime + """ + for i, o in self.INVESTFLOWS: + lifetime = m.flows[i, o].investment.lifetime + for p in m.PERIODS: + if lifetime <= p: + expr = (self.old_end[i, o, p] + == self.invest[i, o, p - lifetime]) + self.old_rule_end.add((i, o, p), expr) + else: + expr = (self.old_end[i, o, p] == 0) + self.old_rule_end.add((i, o, p), expr) + + self.old_rule_end = Constraint( + self.INVESTFLOWS, m.PERIODS, + noruleinit=True + ) + self.old_rule_end_build = BuildAction( + rule=_old_capacity_rule_end + ) + + def _old_capacity_rule_exo(block): + """Rule definition for determining old exogenously given capacity + to be decommissioned due to reaching its lifetime + """ + for i, o in self.INVESTFLOWS: + age = m.flows[i, o].investment.age + lifetime = m.flows[i, o].investment.lifetime + for p in m.PERIODS: + if lifetime - age == p: + expr = ( + self.old_exo[i, o, p] + == m.flows[i, o].investment.existing) + self.old_rule_exo.add((i, o, p), expr) + else: + expr = (self.old_exo[i, o, p] == 0) + self.old_rule_exo.add((i, o, p), expr) + + self.old_rule_exo = Constraint( + self.INVESTFLOWS, m.PERIODS, + noruleinit=True + ) + self.old_rule_exo_build = BuildAction( + rule=_old_capacity_rule_exo + ) + + def _old_capacity_rule(block): + """Rule definition for determining (overall) old capacity + to be decommissioned due to reaching its lifetime + """ + for i, o in self.INVESTFLOWS: + for p in m.PERIODS: + expr = ( + self.old[i, o, p] + == self.old_end[i, o, p] + self.old_exo[i, o, p]) + self.old_rule.add((i, o, p), expr) + + self.old_rule = Constraint( + self.INVESTFLOWS, m.PERIODS, + noruleinit=True + ) + self.old_rule_build = BuildAction( + rule=_old_capacity_rule ) - def _investflow_fixed_rule(block, i, o, t): + def _investflow_fixed_rule(block): """Rule definition of constraint to fix flow variable of investment flow to (normed) actual value """ - expr = m.flow[i, o, t] == ( - (m.flows[i, o].investment.existing + self.invest[i, o]) - * m.flows[i, o].fix[t] - ) - - return expr + for i, o in self.FIXED_INVESTFLOWS: + for p, t in m.TIMEINDEX: + expr = ( + m.flow[i, o, p, t] + == self.total[i, o, p] * m.flows[i, o].fix[t] + ) + self.fixed.add((i, o, p, t), expr) self.fixed = Constraint( - self.FIXED_INVESTFLOWS, m.TIMESTEPS, rule=_investflow_fixed_rule + self.FIXED_INVESTFLOWS, + m.TIMEINDEX, + noruleinit=True + ) + self.fixed_build = BuildAction( + rule=_investflow_fixed_rule ) - def _max_investflow_rule(block, i, o, t): + def _max_investflow_rule(block): """Rule definition of constraint setting an upper bound of flow variable in investment case. """ - expr = m.flow[i, o, t] <= ( - (m.flows[i, o].investment.existing + self.invest[i, o]) - * m.flows[i, o].max[t] - ) - return expr + for i, o in self.NON_FIXED_INVESTFLOWS: + for p, t in m.TIMEINDEX: + expr = ( + m.flow[i, o, p, t] + <= self.total[i, o, p] * m.flows[i, o].max[t] + ) + self.max.add((i, o, p, t), expr) self.max = Constraint( - self.NON_FIXED_INVESTFLOWS, m.TIMESTEPS, rule=_max_investflow_rule + self.NON_FIXED_INVESTFLOWS, + m.TIMEINDEX, + noruleinit=True + ) + self.max_build = BuildAction( + rule=_max_investflow_rule ) - def _min_investflow_rule(block, i, o, t): + def _min_investflow_rule(block): """Rule definition of constraint setting a lower bound on flow variable in investment case. """ - expr = m.flow[i, o, t] >= ( - (m.flows[i, o].investment.existing + self.invest[i, o]) - * m.flows[i, o].min[t] - ) - return expr + for i, o in self.MIN_INVESTFLOWS: + for p, t in m.TIMEINDEX: + expr = ( + m.flow[i, o, p, t] + >= self.total[i, o, p] * m.flows[i, o].min[t] + ) + self.min.add((i, o, p, t), expr) self.min = Constraint( - self.MIN_INVESTFLOWS, m.TIMESTEPS, rule=_min_investflow_rule + self.MIN_INVESTFLOWS, + m.TIMEINDEX, + noruleinit=True + ) + self.min_build = BuildAction( + rule=_min_investflow_rule ) def _summed_max_investflow_rule(block, i, o): """Rule definition for build action of max. sum flow constraint in investment case. """ - expr = sum( - m.flow[i, o, t] * m.timeincrement[t] for t in m.TIMESTEPS - ) <= m.flows[i, o].summed_max * ( - self.invest[i, o] + m.flows[i, o].investment.existing + expr = ( + sum(m.flow[i, o, p, t] * m.timeincrement[t] + for p, t in m.TIMEINDEX) + <= (m.flows[i, o].summed_max + * sum(self.total[i, o, p] for p in m.PERIODS)) ) return expr self.summed_max = Constraint( - self.SUMMED_MAX_INVESTFLOWS, rule=_summed_max_investflow_rule + self.SUMMED_MAX_INVESTFLOWS, + rule=_summed_max_investflow_rule ) def _summed_min_investflow_rule(block, i, o): """Rule definition for build action of min. sum flow constraint in investment case. """ - expr = sum( - m.flow[i, o, t] * m.timeincrement[t] for t in m.TIMESTEPS - ) >= ( - (m.flows[i, o].investment.existing + self.invest[i, o]) - * m.flows[i, o].summed_min + expr = ( + sum(m.flow[i, o, p, t] * m.timeincrement[t] + for p, t in m.TIMEINDEX) + >= (sum(self.total[i, o, p] for p in m.PERIODS) + * m.flows[i, o].summed_min) ) return expr self.summed_min = Constraint( - self.SUMMED_MIN_INVESTFLOWS, rule=_summed_min_investflow_rule + self.SUMMED_MIN_INVESTFLOWS, + rule=_summed_min_investflow_rule + ) + + def _overall_maximum_investflow_rule(block): + """Rule definition for maximum overall investment + in investment case. + + Note: In general, there are two different options to define + an overall maximum: + 1.) overall_max = limit for (net) installed capacity + for each period. This is the constraint used here + 2.) overall max = sum of all (gross) investments occurring + """ + for i, o in self.OVERALL_MAXIMUM_INVESTFLOWS: + for p in m.PERIODS: + expr = ( + self.total[i, o, p] + <= m.flows[i, o].investment.overall_maximum + ) + self.overall_maximum.add((i, o, p), expr) + + self.overall_maximum = Constraint( + self.OVERALL_MAXIMUM_INVESTFLOWS, + m.PERIODS, + noruleinit=True + ) + self.overall_maximum_build = BuildAction( + rule=_overall_maximum_investflow_rule + ) + + def _overall_minimum_investflow_rule(block, i, o): + """Rule definition for minimum overall investment + in investment case. + + Note: This is only applicable for the last period + """ + expr = ( + m.flows[i, o].investment.overall_minimum + <= self.total[i, o, m.PERIODS[-1]] + ) + return expr + + self.overall_minimum = Constraint( + self.OVERALL_MINIMUM_INVESTFLOWS, + rule=_overall_minimum_investflow_rule ) def _objective_expression(self): r"""Objective expression for flows with investment attribute of type - class:`.Investment`. The returned costs are fixed, variable and - investment costs. + class:`.Investment`. The returned costs are fixed and + investment costs. Variable costs are added from the standard flow + objective expression. """ if not hasattr(self, "INVESTFLOWS"): return 0 m = self.parent_block() investment_costs = 0 - - for i, o in self.CONVEX_INVESTFLOWS: - investment_costs += ( - self.invest[i, o] * m.flows[i, o].investment.ep_costs - ) - for i, o in self.NON_CONVEX_INVESTFLOWS: - investment_costs += ( - self.invest[i, o] * m.flows[i, o].investment.ep_costs - + self.invest_status[i, o] * m.flows[i, o].investment.offset - ) + period_investment_costs = {p: 0 for p in m.PERIODS} + fixed_costs = 0 + + if not m.es.multi_period: + for i, o in self.CONVEX_INVESTFLOWS: + for p in m.PERIODS: + investment_costs += ( + self.invest[i, o, p] + * m.flows[i, o].investment.ep_costs[p] + ) + + for i, o in self.NON_CONVEX_INVESTFLOWS: + for p in m.PERIODS: + investment_costs += ( + self.invest[i, o, p] + * m.flows[i, o].investment.ep_costs[p] + + self.invest_status[i, o, p] + * m.flows[i, o].investment.offset[p] + ) + + else: + msg = ("You did not specify an interest rate.\n" + "It will be set equal to the discount_rate of {} " + "of the model as a default.\nThis corresponds to a " + "social planner point of view and does not reflect " + "microeconomic interest requirements.") + + for i, o in self.CONVEX_INVESTFLOWS: + lifetime = m.flows[i, o].investment.lifetime + interest = m.flows[i, o].investment.interest_rate + if interest == 0: + warn(msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning) + interest = m.discount_rate + for p in m.PERIODS: + annuity = economics.annuity( + capex=m.flows[i, o].investment.ep_costs[p], + n=lifetime, + wacc=interest + ) + investment_costs_increment = ( + self.invest[i, o, p] * annuity * lifetime + * ((1 + m.discount_rate) ** (-p)) + ) + investment_costs += investment_costs_increment + period_investment_costs[p] += investment_costs_increment + + for i, o in self.NON_CONVEX_INVESTFLOWS: + lifetime = m.flows[i, o].investment.lifetime + interest = m.flows[i, o].investment.interest_rate + if interest == 0: + warn(msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning) + interest = m.discount_rate + for p in m.PERIODS: + annuity = economics.annuity( + capex=m.flows[i, o].investment.ep_costs[p], + n=lifetime, + wacc=interest + ) + investment_costs_increment = ( + (self.invest[i, o, p] * annuity * lifetime + + self.invest_status[i, o, p] + * m.flows[i, o].investment.offset[p]) + * ((1 + m.discount_rate) ** (-p)) + ) + investment_costs += investment_costs_increment + period_investment_costs[p] += investment_costs_increment + + for i, o in self.INVESTFLOWS: + if m.flows[i, o].investment.fixed_costs[0] is not None: + lifetime = m.flows[i, o].investment.lifetime + for p in m.PERIODS: + fixed_costs += ( + sum(self.invest[i, o, p] + * m.flows[i, o].investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(p, p + lifetime)) + * ((1 + m.discount_rate) ** (-p)) + ) self.investment_costs = Expression(expr=investment_costs) - return investment_costs + self.period_investment_costs = period_investment_costs + self.costs = Expression(expr=investment_costs + fixed_costs) + + return self.costs diff --git a/src/oemof/solph/flows/_non_convex_flow.py b/src/oemof/solph/flows/_non_convex_flow.py index eaf78d323..fad4237ff 100644 --- a/src/oemof/solph/flows/_non_convex_flow.py +++ b/src/oemof/solph/flows/_non_convex_flow.py @@ -10,7 +10,7 @@ SPDX-FileCopyrightText: Birgit Schachler SPDX-FileCopyrightText: jnnr SPDX-FileCopyrightText: jmloenneberga -SPDX-FileCopyrightText: Johannes Kochems (jokochems) +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -415,32 +415,32 @@ def _create(self, group=None): self.NEGATIVE_GRADIENT_FLOWS, m.TIMESTEPS ) - def _minimum_flow_rule(block, i, o, t): + def _minimum_flow_rule(block, i, o, p, t): """Rule definition for MILP minimum flow constraints.""" expr = ( self.status[i, o, t] * m.flows[i, o].min[t] * m.flows[i, o].nominal_value - <= m.flow[i, o, t] + <= m.flow[i, o, p, t] ) return expr self.min = Constraint( - self.MIN_FLOWS, m.TIMESTEPS, rule=_minimum_flow_rule + self.MIN_FLOWS, m.TIMEINDEX, rule=_minimum_flow_rule ) - def _maximum_flow_rule(block, i, o, t): + def _maximum_flow_rule(block, i, o, p, t): """Rule definition for MILP maximum flow constraints.""" expr = ( self.status[i, o, t] * m.flows[i, o].max[t] * m.flows[i, o].nominal_value - >= m.flow[i, o, t] + >= m.flow[i, o, p, t] ) return expr self.max = Constraint( - self.MIN_FLOWS, m.TIMESTEPS, rule=_maximum_flow_rule + self.MIN_FLOWS, m.TIMEINDEX, rule=_maximum_flow_rule ) def _startup_rule(block, i, o, t): @@ -559,21 +559,28 @@ def _min_downtime_rule(block, i, o, t): def _positive_gradient_flow_rule(block): """Rule definition for positive gradient constraint.""" for i, o in self.POSITIVE_GRADIENT_FLOWS: - for t in m.TIMESTEPS: - if t > 0: + for index in range(1, len(m.TIMEINDEX) + 1): + if m.TIMEINDEX[index][1] > 0: lhs = ( - m.flow[i, o, t] * self.status[i, o, t] - - m.flow[i, o, t - 1] * self.status[i, o, t - 1] + m.flow[i, o, m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1]] + * self.status[i, o, m.TIMEINDEX[index][1]] + - m.flow[i, o, m.TIMEINDEX[index - 1][0], + m.TIMEINDEX[index - 1][1]] + * self.status[i, o, m.TIMEINDEX[index - 1][1]] ) - rhs = self.positive_gradient[i, o, t] + rhs = self.positive_gradient[i, o, + m.TIMEINDEX[index][1]] self.positive_gradient_constr.add( - (i, o, t), lhs <= rhs + (i, o, m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1]), + lhs <= rhs ) else: pass # return(Constraint.Skip) self.positive_gradient_constr = Constraint( - self.POSITIVE_GRADIENT_FLOWS, m.TIMESTEPS, noruleinit=True + self.POSITIVE_GRADIENT_FLOWS, m.TIMEINDEX, noruleinit=True ) self.positive_gradient_build = BuildAction( rule=_positive_gradient_flow_rule @@ -582,21 +589,28 @@ def _positive_gradient_flow_rule(block): def _negative_gradient_flow_rule(block): """Rule definition for negative gradient constraint.""" for i, o in self.NEGATIVE_GRADIENT_FLOWS: - for t in m.TIMESTEPS: - if t > 0: + for index in range(1, len(m.TIMEINDEX) + 1): + if m.TIMEINDEX[index][1] > 0: lhs = ( - m.flow[i, o, t - 1] * self.status[i, o, t - 1] - - m.flow[i, o, t] * self.status[i, o, t] + m.flow[i, o, m.TIMEINDEX[index - 1][0], + m.TIMEINDEX[index - 1][1]] + * self.status[i, o, m.TIMEINDEX[index - 1][1]] + - m.flow[i, o, m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1]] + * self.status[i, o, m.TIMEINDEX[index][1]] ) - rhs = self.negative_gradient[i, o, t] + rhs = self.negative_gradient[i, o, + m.TIMEINDEX[index][1]] self.negative_gradient_constr.add( - (i, o, t), lhs <= rhs + (i, o, m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1]), + lhs <= rhs ) else: pass # return(Constraint.Skip) self.negative_gradient_constr = Constraint( - self.NEGATIVE_GRADIENT_FLOWS, m.TIMESTEPS, noruleinit=True + self.NEGATIVE_GRADIENT_FLOWS, m.TIMEINDEX, noruleinit=True ) self.negative_gradient_build = BuildAction( rule=_negative_gradient_flow_rule @@ -614,59 +628,139 @@ def _objective_expression(self): activity_costs = 0 gradient_costs = 0 - if self.STARTUPFLOWS: - for i, o in self.STARTUPFLOWS: - if m.flows[i, o].nonconvex.startup_costs[0] is not None: - startup_costs += sum( - self.startup[i, o, t] - * m.flows[i, o].nonconvex.startup_costs[t] - for t in m.TIMESTEPS - ) - self.startup_costs = Expression(expr=startup_costs) - - if self.SHUTDOWNFLOWS: - for i, o in self.SHUTDOWNFLOWS: - if m.flows[i, o].nonconvex.shutdown_costs[0] is not None: - shutdown_costs += sum( - self.shutdown[i, o, t] - * m.flows[i, o].nonconvex.shutdown_costs[t] - for t in m.TIMESTEPS - ) - self.shutdown_costs = Expression(expr=shutdown_costs) - - if self.ACTIVITYCOSTFLOWS: - for i, o in self.ACTIVITYCOSTFLOWS: - if m.flows[i, o].nonconvex.activity_costs[0] is not None: - activity_costs += sum( - self.status[i, o, t] - * m.flows[i, o].nonconvex.activity_costs[t] - for t in m.TIMESTEPS - ) - - self.activity_costs = Expression(expr=activity_costs) + if not m.em.multi_period: + if self.STARTUPFLOWS: + for i, o in self.STARTUPFLOWS: + if m.flows[i, o].nonconvex.startup_costs[0] is not None: + startup_costs += sum( + self.startup[i, o, t] + * m.flows[i, o].nonconvex.startup_costs[t] + for t in m.TIMESTEPS + ) - if self.POSITIVE_GRADIENT_FLOWS: - for i, o in self.POSITIVE_GRADIENT_FLOWS: - if ( - m.flows[i, o].nonconvex.positive_gradient["ub"][0] - is not None - ): - for t in m.TIMESTEPS: - gradient_costs += self.positive_gradient[i, o, t] * ( - m.flows[i, o].nonconvex.positive_gradient["costs"] + if self.SHUTDOWNFLOWS: + for i, o in self.SHUTDOWNFLOWS: + if m.flows[i, o].nonconvex.shutdown_costs[0] is not None: + shutdown_costs += sum( + self.shutdown[i, o, t] + * m.flows[i, o].nonconvex.shutdown_costs[t] + for t in m.TIMESTEPS ) - if self.NEGATIVE_GRADIENT_FLOWS: - for i, o in self.NEGATIVE_GRADIENT_FLOWS: - if ( - m.flows[i, o].nonconvex.negative_gradient["ub"][0] - is not None - ): - for t in m.TIMESTEPS: - gradient_costs += self.negative_gradient[i, o, t] * ( - m.flows[i, o].nonconvex.negative_gradient["costs"] + if self.ACTIVITYCOSTFLOWS: + for i, o in self.ACTIVITYCOSTFLOWS: + if m.flows[i, o].nonconvex.activity_costs[0] is not None: + activity_costs += sum( + self.status[i, o, t] + * m.flows[i, o].nonconvex.activity_costs[t] + for t in m.TIMESTEPS ) - self.gradient_costs = Expression(expr=gradient_costs) + if self.POSITIVE_GRADIENT_FLOWS: + for i, o in self.POSITIVE_GRADIENT_FLOWS: + if ( + m.flows[i, o].nonconvex.positive_gradient["ub"][0] + is not None + ): + for t in m.TIMESTEPS: + gradient_costs += ( + self.positive_gradient[i, o, t] * ( + m.flows[i, o].nonconvex.positive_gradient[ + "costs"] + ) + ) + + if self.NEGATIVE_GRADIENT_FLOWS: + for i, o in self.NEGATIVE_GRADIENT_FLOWS: + if ( + m.flows[i, o].nonconvex.negative_gradient["ub"][0] + is not None + ): + for t in m.TIMESTEPS: + gradient_costs += ( + self.negative_gradient[i, o, t] * ( + m.flows[i, o].nonconvex.negative_gradient[ + "costs"] + ) + ) - return startup_costs + shutdown_costs + activity_costs + gradient_costs + else: + if self.STARTUPFLOWS: + for i, o in self.STARTUPFLOWS: + if (m.flows[i, o].nonconvex.startup_costs[0] + is not None): + startup_costs += sum( + self.startup[i, o, t] + * m.flows[i, o].nonconvex.startup_costs[p] + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + for p, t in m.TIMEINDEX + ) + + if self.SHUTDOWNFLOWS: + for i, o in self.SHUTDOWNFLOWS: + if (m.flows[i, o].nonconvex.shutdown_costs[0] + is not None): + shutdown_costs += sum( + self.shutdown[i, o, t] + * m.flows[i, o].nonconvex.shutdown_costs[p] + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + for p, t in m.TIMEINDEX + ) + + if self.ACTIVITYCOSTFLOWS: + for i, o in self.ACTIVITYCOSTFLOWS: + if (m.flows[i, o].nonconvex.activity_costs[0] + is not None): + activity_costs += sum( + self.status[i, o, t] + * m.flows[i, o].nonconvex.activity_costs[p] + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + for p, t in m.TIMEINDEX + ) + + if self.POSITIVE_GRADIENT_FLOWS: + for i, o in self.POSITIVE_GRADIENT_FLOWS: + if ( + m.flows[i, o].nonconvex.positive_gradient["ub"][0] + is not None + ): + gradient_costs += sum( + self.positive_gradient[i, o, t] + * (m.flows[i, o].nonconvex + .positive_gradient["costs"]) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + for p, t in m.TIMEINDEX + ) + + if self.NEGATIVE_GRADIENT_FLOWS: + for i, o in self.NEGATIVE_GRADIENT_FLOWS: + if ( + (m.flows[i, o].nonconvex + .negative_gradient["ub"][0]) + is not None + ): + gradient_costs += sum( + self.negative_gradient[i, o, t] + * (m.flows[i, o].nonconvex + .negative_gradient["costs"]) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + for p, t in m.TIMEINDEX + ) + + self.activity_costs = Expression(expr=activity_costs) + self.gradient_costs = Expression(expr=gradient_costs) + self.startup_costs = Expression(expr=startup_costs) + self.shutdown_costs = Expression(expr=shutdown_costs) + + self.costs = Expression( + expr=( + startup_costs + shutdown_costs + + activity_costs + gradient_costs + ) + ) + return self.costs diff --git a/src/oemof/solph/flows/experimental/_electrical_line.py b/src/oemof/solph/flows/experimental/_electrical_line.py index a320fa38b..275c9447c 100644 --- a/src/oemof/solph/flows/experimental/_electrical_line.py +++ b/src/oemof/solph/flows/experimental/_electrical_line.py @@ -151,13 +151,13 @@ def _voltage_angle_bounds(block, b, t): bus.slack = True def _voltage_angle_relation(block): - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for n in group: if n.input.slack is True: self.voltage_angle[n.output, t].value = 0 self.voltage_angle[n.output, t].fix() try: - lhs = m.flow[n.input, n.output, t] + lhs = m.flow[n.input, n.output, p, t] rhs = ( 1 / n.reactance[t] @@ -171,8 +171,8 @@ def _voltage_angle_relation(block): "Error in constraint creation", "of node {}".format(n.label), ) - block.electrical_flow.add((n, t), (lhs == rhs)) + block.electrical_flow.add((n, p, t), (lhs == rhs)) - self.electrical_flow = Constraint(group, m.TIMESTEPS, noruleinit=True) + self.electrical_flow = Constraint(group, m.TIMEINDEX, noruleinit=True) self.electrical_flow_build = BuildAction(rule=_voltage_angle_relation) From c9f72518a1c79d1d5c601aa05981f336578ec555 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sun, 5 Dec 2021 19:00:29 +0100 Subject: [PATCH 0029/1363] Integrate multi-period draft into standard components --- .../components/_extraction_turbine_chp.py | 23 +- src/oemof/solph/components/_generic_chp.py | 21 +- .../solph/components/_generic_storage.py | 547 +++++++++++++++--- .../solph/components/_offset_transformer.py | 12 +- src/oemof/solph/components/_transformer.py | 13 +- 5 files changed, 497 insertions(+), 119 deletions(-) diff --git a/src/oemof/solph/components/_extraction_turbine_chp.py b/src/oemof/solph/components/_extraction_turbine_chp.py index 433f90002..e76716bdf 100644 --- a/src/oemof/solph/components/_extraction_turbine_chp.py +++ b/src/oemof/solph/components/_extraction_turbine_chp.py @@ -13,6 +13,7 @@ SPDX-FileCopyrightText: Stephan Günther SPDX-FileCopyrightText: FabianTU SPDX-FileCopyrightText: Johannes Röder +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -180,18 +181,18 @@ def _create(self, group=None): def _input_output_relation_rule(block): """Connection between input, main output and tapped output.""" - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] rhs = ( - m.flow[g, g.main_output, t] - + m.flow[g, g.tapped_output, t] + m.flow[g, g.main_output, p, t] + + m.flow[g, g.tapped_output, p, t] * g.main_flow_loss_index[t] ) / g.conversion_factor_full_condensation_sq[t] - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add((g, p, t), (lhs == rhs)) self.input_output_relation = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.input_output_relation_build = BuildAction( rule=_input_output_relation_rule @@ -199,17 +200,17 @@ def _input_output_relation_rule(block): def _out_flow_relation_rule(block): """Relation between main and tapped output in full chp mode.""" - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: - lhs = m.flow[g, g.main_output, t] + lhs = m.flow[g, g.main_output, p, t] rhs = ( - m.flow[g, g.tapped_output, t] + m.flow[g, g.tapped_output, p, t] * g.flow_relation_index[t] ) - block.out_flow_relation.add((g, t), (lhs >= rhs)) + block.out_flow_relation.add((g, p, t), (lhs >= rhs)) self.out_flow_relation = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.out_flow_relation_build = BuildAction( rule=_out_flow_relation_rule diff --git a/src/oemof/solph/components/_generic_chp.py b/src/oemof/solph/components/_generic_chp.py index 838543726..6f5bd663e 100644 --- a/src/oemof/solph/components/_generic_chp.py +++ b/src/oemof/solph/components/_generic_chp.py @@ -12,6 +12,7 @@ SPDX-FileCopyrightText: Stephan Günther SPDX-FileCopyrightText: FabianTU SPDX-FileCopyrightText: Johannes Röder +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -338,37 +339,37 @@ def _create(self, group=None): self.Y = Var(self.GENERICCHPS, m.TIMESTEPS, within=Binary) # constraint rules - def _H_flow_rule(block, n, t): + def _H_flow_rule(block, n, p, t): """Link fuel consumption to component inflow.""" expr = 0 expr += self.H_F[n, t] - expr += -m.flow[list(n.fuel_input.keys())[0], n, t] + expr += -m.flow[list(n.fuel_input.keys())[0], n, p, t] return expr == 0 self.H_flow = Constraint( - self.GENERICCHPS, m.TIMESTEPS, rule=_H_flow_rule + self.GENERICCHPS, m.TIMEINDEX, rule=_H_flow_rule ) - def _Q_flow_rule(block, n, t): + def _Q_flow_rule(block, n, p, t): """Link heat flow to component outflow.""" expr = 0 expr += self.Q[n, t] - expr += -m.flow[n, list(n.heat_output.keys())[0], t] + expr += -m.flow[n, list(n.heat_output.keys())[0], p, t] return expr == 0 self.Q_flow = Constraint( - self.GENERICCHPS, m.TIMESTEPS, rule=_Q_flow_rule + self.GENERICCHPS, m.TIMEINDEX, rule=_Q_flow_rule ) - def _P_flow_rule(block, n, t): + def _P_flow_rule(block, n, p, t): """Link power flow to component outflow.""" expr = 0 expr += self.P[n, t] - expr += -m.flow[n, list(n.electrical_output.keys())[0], t] + expr += -m.flow[n, list(n.electrical_output.keys())[0], p, t] return expr == 0 self.P_flow = Constraint( - self.GENERICCHPS, m.TIMESTEPS, rule=_P_flow_rule + self.GENERICCHPS, m.TIMEINDEX, rule=_P_flow_rule ) def _H_F_1_rule(block, n, t): @@ -497,7 +498,7 @@ def _objective_expression(self): r"""Objective expression for generic CHPs with no investment. Note: This adds nothing as variable costs are already - added in the Block :class:`FlowBlock`. + added in the Block :class:`Flow`. """ if not hasattr(self, "GENERICCHPS"): return 0 diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index 7525b7dc8..77f9d5943 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -12,14 +12,21 @@ SPDX-FileCopyrightText: Stephan Günther SPDX-FileCopyrightText: FabianTU SPDX-FileCopyrightText: Johannes Röder +SPDX-FileCopyrightText: Johannes Kochems +SPDX-FileCopyrightText: Johannes Giehl SPDX-License-Identifier: MIT """ +from warnings import warn + from oemof.network import network +from oemof.tools import debugging +from oemof.tools import economics from pyomo.core.base.block import SimpleBlock from pyomo.environ import Binary +from pyomo.environ import BuildAction from pyomo.environ import Constraint from pyomo.environ import Expression from pyomo.environ import NonNegativeReals @@ -152,6 +159,9 @@ def __init__( ) self.max_storage_level = solph_sequence(max_storage_level) self.min_storage_level = solph_sequence(min_storage_level) + self.fixed_costs = solph_sequence( + kwargs.get('fixed_costs', 0) + ) self.investment = kwargs.get("investment") self.invest_relation_input_output = kwargs.get( "invest_relation_input_output" @@ -163,6 +173,8 @@ def __init__( "invest_relation_output_capacity" ) self._invest_group = isinstance(self.investment, Investment) + self.lifetime_inflow = kwargs.get("lifetime_inflow", 20) + self.lifetime_outflow = kwargs.get("lifetime_outflow", 20) # Check number of flows. self._check_number_of_flows() @@ -244,6 +256,15 @@ def _check_invest_attributes(self): "or investment.minimum has to be non-zero." ) raise AttributeError(e3) + if ( + self.lifetime_inflow == 20 + or self.lifetime_outflow == 20 + ): + w1 = ("Using a lifetime of 20 periods," + " which is the default value.\nIf you don't consider a" + " multi-period investment model, you can safely ignore " + " this warning - all fine!") + warn(w1, debugging.SuspiciousUsageWarning) self._set_flows() @@ -443,7 +464,9 @@ def _storage_init_content_bound_rule(block, n): # ************* Constraints *************************** - reduced_timesteps = [x for x in m.TIMESTEPS if x > 0] + reduced_periods_timesteps = [ + (p, t) for (p, t) in m.TIMEINDEX if t > 0 + ] # storage balance constraint (first time step) def _storage_balance_first_rule(block, n): @@ -464,10 +487,10 @@ def _storage_balance_first_rule(block, n): ) expr += n.fixed_losses_absolute[0] * m.timeincrement[0] expr += ( - -m.flow[i[n], n, 0] * n.inflow_conversion_factor[0] + -m.flow[i[n], n, 0, 0] * n.inflow_conversion_factor[0] ) * m.timeincrement[0] expr += ( - m.flow[n, o[n], 0] / n.outflow_conversion_factor[0] + m.flow[n, o[n], 0, 0] / n.outflow_conversion_factor[0] ) * m.timeincrement[0] return expr == 0 @@ -476,7 +499,7 @@ def _storage_balance_first_rule(block, n): ) # storage balance constraint (every time step but the first) - def _storage_balance_rule(block, n, t): + def _storage_balance_rule(block, n, p, t): """ Rule definition for the storage balance of every storage n and every timestep but the first (t > 0). @@ -494,15 +517,16 @@ def _storage_balance_rule(block, n, t): ) expr += n.fixed_losses_absolute[t] * m.timeincrement[t] expr += ( - -m.flow[i[n], n, t] * n.inflow_conversion_factor[t] + -m.flow[i[n], n, p, t] * n.inflow_conversion_factor[t] ) * m.timeincrement[t] expr += ( - m.flow[n, o[n], t] / n.outflow_conversion_factor[t] + m.flow[n, o[n], p, t] / n.outflow_conversion_factor[t] ) * m.timeincrement[t] return expr == 0 self.balance = Constraint( - self.STORAGES, reduced_timesteps, rule=_storage_balance_rule + self.STORAGES, reduced_periods_timesteps, + rule=_storage_balance_rule ) def _balanced_storage_rule(block, n): @@ -519,34 +543,54 @@ def _balanced_storage_rule(block, n): self.STORAGES_BALANCED, rule=_balanced_storage_rule ) - def _power_coupled(block, n): + def _power_coupled(block): """ Rule definition for constraint to connect the input power and output power """ - expr = ( - m.InvestmentFlowBlock.invest[n, o[n]] - + m.flows[n, o[n]].investment.existing - ) * n.invest_relation_input_output == ( - m.InvestmentFlowBlock.invest[i[n], n] - + m.flows[i[n], n].investment.existing - ) - return expr + for n in self.STORAGES_WITH_INVEST_FLOW_REL: + for p in m.PERIODS: + expr = ( + m.InvestmentFlow.total[n, o[n], p] + ) * n.invest_relation_input_output == ( + m.InvestmentFlow.total[i[n], n, p] + ) + self.power_coupled.add((n, p), expr) self.power_coupled = Constraint( - self.STORAGES_WITH_INVEST_FLOW_REL, rule=_power_coupled + self.STORAGES_WITH_INVEST_FLOW_REL, + m.PERIODS, + noruleinit=True + ) + + self.power_coupled_build = BuildAction( + rule=_power_coupled ) def _objective_expression(self): r""" Objective expression for storages with no investment. - Note: This adds nothing as variable costs are already + Note: This adds nothing but fixed costs as variable costs are already added in the Block :class:`FlowBlock`. """ + m = self.parent_block() + if not hasattr(self, "STORAGES"): return 0 - return 0 + fixed_costs = 0 + + if m.es.multi_period: + for n in self.STORAGES: + if n.fixed_costs[0] is not None: + for p in m.PERIODS: + fixed_costs += ( + n.nominal_storage_capacity + * n.fixed_costs[p] + * ((1 + m.discount_rate) ** -p) + ) + self.fixed_costs = Expression(expr=fixed_costs) + return self.fixed_costs class GenericInvestmentStorageBlock(SimpleBlock): @@ -774,7 +818,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _create(self, group=None): - """ """ + """Create a storage block for investment modeling""" m = self.parent_block() if group is None: return None @@ -838,37 +882,215 @@ def _create(self, group=None): ] ) + self.OVERALL_MAXIMUM_INVESTSTORAGES = Set( + initialize=[ + n for n in group + if n.investment.overall_maximum is not None + ] + ) + + self.OVERALL_MINIMUM_INVESTSTORAGES = Set( + initialize=[ + n for n in group + if n.investment.overall_minimum is not None + ] + ) # ######################### Variables ################################ self.storage_content = Var( - self.INVESTSTORAGES, m.TIMESTEPS, within=NonNegativeReals + self.INVESTSTORAGES, m.TIMESTEPS, + within=NonNegativeReals ) - def _storage_investvar_bound_rule(block, n): + def _storage_investvar_bound_rule(block, n, p): """ Rule definition to bound the invested storage capacity `invest`. """ if n in self.CONVEX_INVESTSTORAGES: - return n.investment.minimum, n.investment.maximum + return n.investment.minimum[p], n.investment.maximum[p] elif n in self.NON_CONVEX_INVESTSTORAGES: - return 0, n.investment.maximum + return 0, n.investment.maximum[p] self.invest = Var( self.INVESTSTORAGES, + m.PERIODS, within=NonNegativeReals, bounds=_storage_investvar_bound_rule, ) - self.init_content = Var(self.INVESTSTORAGES, within=NonNegativeReals) + # Total capacity + self.total = Var( + self.INVESTSTORAGES, + m.PERIODS, + within=NonNegativeReals + ) + + # Old capacity to be decommissioned (due to lifetime) + # Old capacity is built out of old exogenous and endogenous capacities + self.old = Var( + self.INVESTSTORAGES, + m.PERIODS, + within=NonNegativeReals + ) + + # Old endogenous capacity to be decommissioned (due to lifetime) + self.old_end = Var( + self.INVESTSTORAGES, + m.PERIODS, + within=NonNegativeReals + ) + + # Old exogenous capacity to be decommissioned (due to lifetime) + self.old_exo = Var( + self.INVESTSTORAGES, + m.PERIODS, + within=NonNegativeReals + ) + + self.init_content = Var( + self.INVESTSTORAGES, + within=NonNegativeReals + ) # create status variable for a non-convex investment storage - self.invest_status = Var(self.NON_CONVEX_INVESTSTORAGES, within=Binary) + self.invest_status = Var( + self.NON_CONVEX_INVESTSTORAGES, + m.PERIODS, + within=Binary + ) # ######################### CONSTRAINTS ############################### i = {n: [i for i in n.inputs][0] for n in group} o = {n: [o for o in n.outputs][0] for n in group} - reduced_timesteps = [x for x in m.TIMESTEPS if x > 0] + reduced_periods_timesteps = [ + (p, t) for (p, t) in m.TIMEINDEX if t > 0 + ] + # Handle unit lifetimes + def _total_storage_capacity_rule(block): + """Rule definition for determining total installed + capacity (taking decommissioning into account) + """ + for n in self.INVESTSTORAGES: + for p in m.PERIODS: + if p == 0: + expr = (self.total[n, p] + == self.invest[n, p] + + n.investment.existing) + self.total_storage_rule.add((n, p), expr) + else: + expr = (self.total[n, p] + == self.invest[n, p] + + self.total[n, p - 1] + - self.old[n, p]) + self.total_storage_rule.add((n, p), expr) + + self.total_storage_rule = Constraint( + self.INVESTSTORAGES, + m.PERIODS, + noruleinit=True + ) + + self.total_storage_rule_build = BuildAction( + rule=_total_storage_capacity_rule + ) + + def _old_storage_capacity_rule_end(block): + """Rule definition for determining old endogenously installed + capacity to be decommissioned due to reaching its lifetime + """ + for n in self.INVESTSTORAGES: + lifetime = n.investment.lifetime + for p in m.PERIODS: + if lifetime <= p: + expr = (self.old_end[n, p] + == self.invest[n, p - lifetime]) + self.old_rule_end.add((n, p), expr) + else: + expr = (self.old_end[n, p] + == 0) + self.old_rule_end.add((n, p), expr) + + self.old_rule_end = Constraint( + self.INVESTSTORAGES, + m.PERIODS, + noruleinit=True + ) + + self.old_rule_end_build = BuildAction( + rule=_old_storage_capacity_rule_end + ) + + def _old_storage_capacity_rule_exo(block): + """Rule definition for determining old exogenously given capacity + to be decommissioned due to reaching its lifetime + """ + for n in self.INVESTSTORAGES: + age = n.investment.age + lifetime = n.investment.lifetime + for p in m.PERIODS: + if lifetime - age == p: + expr = ( + self.old_exo[n, p] + == n.investment.existing) + self.old_rule_exo.add((n, p), expr) + else: + expr = (self.old_exo[n, p] + == 0) + self.old_rule_exo.add((n, p), expr) + + self.old_rule_exo = Constraint( + self.INVESTSTORAGES, + m.PERIODS, + noruleinit=True + ) + + self.old_rule_exo_build = BuildAction( + rule=_old_storage_capacity_rule_exo + ) + + def _old_storage_capacity_rule(block): + """Rule definition for determining (overall) old capacity + to be decommissioned due to reaching its lifetime + """ + for n in self.INVESTSTORAGES: + for p in m.PERIODS: + expr = ( + self.old[n, p] + == self.old_end[n, p] + self.old_exo[n, p] + ) + self.old_rule.add((n, p), expr) + + self.old_rule = Constraint( + self.INVESTSTORAGES, + m.PERIODS, + noruleinit=True + ) + + self.old_rule_build = BuildAction( + rule=_old_storage_capacity_rule + ) + + def _storage_level_no_investments(block): + """Rule definition to force storage level to zero + as long as no investments have occurred + """ + for n in self.INVESTSTORAGES: + for p, t in m.TIMEINDEX: + expr = block.storage_content[n, t] <= self.total[n, p] + self.storage_level_noinv_rule.add((n, p, t), expr) + + self.storage_level_noinv_rule = Constraint( + self.INVESTSTORAGES, + m.TIMEINDEX, + noruleinit=True + ) + + self.storage_level_noinv_rule_build = BuildAction( + rule=_storage_level_no_investments + ) + + # TODO: Handle initial content ... make it a sequence?! def _inv_storage_init_content_max_rule(block, n): """Constraint for a variable initial storage capacity.""" return ( @@ -892,6 +1114,7 @@ def _inv_storage_init_content_fix_rule(block, n): rule=_inv_storage_init_content_fix_rule, ) + # TODO: Generalize! 0th step for the respective period / storage def _storage_balance_first_rule(block, n): """ Rule definition for the storage balance of every storage n for the @@ -905,23 +1128,24 @@ def _storage_balance_first_rule(block, n): ) expr += ( n.fixed_losses_relative[0] - * (n.investment.existing + self.invest[n]) + * (n.investment.existing + self.invest[n, 0]) * m.timeincrement[0] ) expr += n.fixed_losses_absolute[0] * m.timeincrement[0] expr += ( - -m.flow[i[n], n, 0] * n.inflow_conversion_factor[0] + -m.flow[i[n], n, 0, 0] * n.inflow_conversion_factor[0] ) * m.timeincrement[0] expr += ( - m.flow[n, o[n], 0] / n.outflow_conversion_factor[0] + m.flow[n, o[n], 0, 0] / n.outflow_conversion_factor[0] ) * m.timeincrement[0] return expr == 0 self.balance_first = Constraint( - self.INVESTSTORAGES, rule=_storage_balance_first_rule + self.INVESTSTORAGES, + rule=_storage_balance_first_rule ) - def _storage_balance_rule(block, n, t): + def _storage_balance_rule(block, n, p, t): """ Rule definition for the storage balance of every storage n for the every time step but the first. @@ -934,20 +1158,22 @@ def _storage_balance_rule(block, n, t): ) expr += ( n.fixed_losses_relative[t] - * (n.investment.existing + self.invest[n]) + * self.total[n, p] * m.timeincrement[t] ) expr += n.fixed_losses_absolute[t] * m.timeincrement[t] expr += ( - -m.flow[i[n], n, t] * n.inflow_conversion_factor[t] + -m.flow[i[n], n, p, t] * n.inflow_conversion_factor[t] ) * m.timeincrement[t] expr += ( - m.flow[n, o[n], t] / n.outflow_conversion_factor[t] + m.flow[n, o[n], p, t] / n.outflow_conversion_factor[t] ) * m.timeincrement[t] return expr == 0 self.balance = Constraint( - self.INVESTSTORAGES, reduced_timesteps, rule=_storage_balance_rule + self.INVESTSTORAGES, + reduced_periods_timesteps, + rule=_storage_balance_rule ) def _balanced_storage_rule(block, n): @@ -957,89 +1183,111 @@ def _balanced_storage_rule(block, n): ) self.balanced_cstr = Constraint( - self.INVESTSTORAGES_BALANCED, rule=_balanced_storage_rule + self.INVESTSTORAGES_BALANCED, + rule=_balanced_storage_rule ) - def _power_coupled(block, n): + def _power_coupled(block): """ Rule definition for constraint to connect the input power and output power """ - expr = ( - m.InvestmentFlowBlock.invest[n, o[n]] - + m.flows[n, o[n]].investment.existing - ) * n.invest_relation_input_output == ( - m.InvestmentFlowBlock.invest[i[n], n] - + m.flows[i[n], n].investment.existing - ) - return expr + for n in self.INVEST_REL_IN_OUT: + for p in m.PERIODS: + expr = ( + m.InvestmentFlow.total[n, o[n], p] + ) * n.invest_relation_input_output == ( + m.InvestmentFlow.total[i[n], n, p] + ) + self.power_coupled.add((n, p), expr) self.power_coupled = Constraint( - self.INVEST_REL_IN_OUT, rule=_power_coupled + self.INVEST_REL_IN_OUT, + m.PERIODS, + noruleinit=True + ) + + self.power_coupled_build = BuildAction( + rule=_power_coupled ) - def _storage_capacity_inflow_invest_rule(block, n): + def _storage_capacity_inflow_invest_rule(block): """ Rule definition of constraint connecting the inflow `InvestmentFlowBlock.invest of storage with invested capacity `invest` by nominal_storage_capacity__inflow_ratio """ - expr = ( - m.InvestmentFlowBlock.invest[i[n], n] - + m.flows[i[n], n].investment.existing - ) == ( - n.investment.existing + self.invest[n] - ) * n.invest_relation_input_capacity - return expr + for n in self.INVEST_REL_CAP_IN: + for p in m.PERIODS: + expr = ( + m.InvestmentFlow.total[i[n], n, p] + == self.total[n, p] + * n.invest_relation_input_capacity + ) + self.storage_capacity_inflow.add((n, p), expr) self.storage_capacity_inflow = Constraint( - self.INVEST_REL_CAP_IN, rule=_storage_capacity_inflow_invest_rule + self.INVEST_REL_CAP_IN, + m.PERIODS, + noruleinit=True + ) + + self.storage_capacity_inflow_build = BuildAction( + rule=_storage_capacity_inflow_invest_rule ) - def _storage_capacity_outflow_invest_rule(block, n): + def _storage_capacity_outflow_invest_rule(block): """ Rule definition of constraint connecting outflow `InvestmentFlowBlock.invest` of storage and invested capacity `invest` by nominal_storage_capacity__outflow_ratio """ - expr = ( - m.InvestmentFlowBlock.invest[n, o[n]] - + m.flows[n, o[n]].investment.existing - ) == ( - n.investment.existing + self.invest[n] - ) * n.invest_relation_output_capacity - return expr + for n in self.INVEST_REL_CAP_OUT: + for p in m.PERIODS: + expr = ( + ( + m.InvestmentFlow.total[n, o[n], p] + ) + == self.total[n, p] + * n.invest_relation_output_capacity + ) + self.storage_capacity_outflow.add((n, p), expr) self.storage_capacity_outflow = Constraint( - self.INVEST_REL_CAP_OUT, rule=_storage_capacity_outflow_invest_rule + self.INVEST_REL_CAP_OUT, + m.PERIODS, + noruleinit=True ) - def _max_storage_content_invest_rule(block, n, t): + self.storage_capacity_outflow_build = BuildAction( + rule=_storage_capacity_outflow_invest_rule) + + def _max_storage_content_invest_rule(block, n, p, t): """ Rule definition for upper bound constraint for the storage content. """ expr = ( self.storage_content[n, t] - <= (n.investment.existing + self.invest[n]) + <= self.total[n, p] * n.max_storage_level[t] ) return expr self.max_storage_content = Constraint( self.INVESTSTORAGES, - m.TIMESTEPS, + m.TIMEINDEX, rule=_max_storage_content_invest_rule, ) - def _min_storage_content_invest_rule(block, n, t): + def _min_storage_content_invest_rule(block, n, p, t): """ Rule definition of lower bound constraint for the storage content. """ expr = ( self.storage_content[n, t] - >= (n.investment.existing + self.invest[n]) + >= self.total[n, p] * n.min_storage_level[t] ) return expr @@ -1047,52 +1295,177 @@ def _min_storage_content_invest_rule(block, n, t): # Set the lower bound of the storage content if the attribute exists self.min_storage_content = Constraint( self.MIN_INVESTSTORAGES, - m.TIMESTEPS, + m.TIMEINDEX, rule=_min_storage_content_invest_rule, ) - def maximum_invest_limit(block, n): + def maximum_invest_limit(block, n, p): """ Constraint for the maximal investment in non convex investment storage. """ return ( - n.investment.maximum * self.invest_status[n] - self.invest[n] + n.investment.maximum[p] * self.invest_status[n, p] + - self.invest[n, p] ) >= 0 self.limit_max = Constraint( - self.NON_CONVEX_INVESTSTORAGES, rule=maximum_invest_limit + self.NON_CONVEX_INVESTSTORAGES, + m.PERIODS, + rule=maximum_invest_limit ) - def smallest_invest(block, n): + def smallest_invest(block, n, p): """ Constraint for the minimal investment in non convex investment storage if the invest is greater than 0. So the invest variable can be either 0 or greater than the minimum. """ return ( - self.invest[n] - (n.investment.minimum * self.invest_status[n]) + self.invest[n, p] + - n.investment.minimum[p] * self.invest_status[n, p] >= 0 ) self.limit_min = Constraint( - self.NON_CONVEX_INVESTSTORAGES, rule=smallest_invest + self.NON_CONVEX_INVESTSTORAGES, + m.PERIODS, + rule=smallest_invest + ) + + def _overall_storage_maximum_investflow_rule(block): + """Rule definition for maximum overall investment + in investment case. + + Note: There are two different options to define an overall maximum: + 1.) overall_max = limit for (net) installed capacity + for each period + 2.) overall max = sum of all (gross) investments occurring + """ + for n in self.OVERALL_MAXIMUM_INVESTSTORAGES: + for p in m.PERIODS: + expr = (self.total[n, p] + <= n.investment.overall_maximum) + self.overall_storage_maximum.add((n, p), expr) + + self.overall_storage_maximum = Constraint( + self.OVERALL_MAXIMUM_INVESTSTORAGES, + m.PERIODS, + noruleinit=True + ) + + self.overall_maximum_build = BuildAction( + rule=_overall_storage_maximum_investflow_rule + ) + + def _overall_minimum_investflow_rule(block): + """Rule definition for minimum overall investment + in investment case. + + Note: This is only applicable for the last period + """ + for n in self.OVERALL_MINIMUM_INVESTSTORAGES: + expr = (n.investment.overall_minimum + <= self.total[n, m.PERIODS[-1]]) + self.overall_minimum.add(n, expr) + + self.overall_minimum = Constraint( + self.OVERALL_MINIMUM_INVESTSTORAGES, + noruleinit=True + ) + + self.overall_minimum_build = BuildAction( + rule=_overall_minimum_investflow_rule ) def _objective_expression(self): """Objective expression with fixed and investement costs.""" + m = self.parent_block() + if not hasattr(self, "INVESTSTORAGES"): return 0 investment_costs = 0 + period_investment_costs = {p: 0 for p in m.PERIODS} + fixed_costs = 0 + + if not m.es.multi_period: + for n in self.CONVEX_INVESTSTORAGES: + for p in m.PERIODS: + investment_costs += ( + self.invest[n, p] * n.investment.ep_costs[p] + ) + for n in self.NON_CONVEX_INVESTSTORAGES: + for p in m.PERIODS: + investment_costs += ( + self.invest[n, p] * n.investment.ep_costs[p] + + self.invest_status[n, p] * n.investment.offset[p] + ) - for n in self.CONVEX_INVESTSTORAGES: - investment_costs += self.invest[n] * n.investment.ep_costs - for n in self.NON_CONVEX_INVESTSTORAGES: - investment_costs += ( - self.invest[n] * n.investment.ep_costs - + self.invest_status[n] * n.investment.offset - ) - self.investment_costs = Expression(expr=investment_costs) - - return investment_costs + else: + msg = ("You did not specify an interest rate.\n" + "It will be set equal to the discount_rate of {} " + "of the model as a default.\nThis corresponds to a " + "social planner point of view and does not reflect " + "microeconomic interest requirements.") + + for n in self.CONVEX_INVESTSTORAGES: + lifetime = n.investment.lifetime + interest = n.investment.interest_rate + if interest == 0: + warn(msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning) + interest = m.discount_rate + for p in m.PERIODS: + annuity = economics.annuity( + capex=n.investment.ep_costs[p], + n=lifetime, + wacc=interest + ) + investment_costs_increment = ( + self.invest[n, p] * annuity * lifetime + * ((1 + m.discount_rate) ** (-p)) + ) + investment_costs += investment_costs_increment + period_investment_costs[p] += investment_costs_increment + + for n in self.NON_CONVEX_INVESTSTORAGES: + lifetime = n.investment.lifetime + interest = n.investment.interest_rate + if interest == 0: + warn(msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning) + interest = m.discount_rate + for p in m.PERIODS: + annuity = economics.annuity( + capex=n.investment.ep_costs[p], + n=lifetime, + wacc=interest + ) + investment_costs_increment = ( + (self.invest[n, p] * annuity * lifetime + + self.invest_status[n, p] + * n.investment.offset[p]) + * ((1 + m.discount_rate) ** (-p)) + ) + investment_costs += investment_costs_increment + period_investment_costs[p] += investment_costs_increment + + for n in self.INVESTSTORAGES: + if n.investment.fixed_costs[0] is not None: + lifetime = n.investment.lifetime + for p in m.PERIODS: + fixed_costs += ( + sum(self.invest[n, p] + * n.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(p, p + lifetime) + ) + * ((1 + m.discount_rate) ** (-p)) + ) + + self.investment_costs = investment_costs + self.period_investment_costs = period_investment_costs + self.costs = Expression(expr=investment_costs + fixed_costs) + + return self.costs diff --git a/src/oemof/solph/components/_offset_transformer.py b/src/oemof/solph/components/_offset_transformer.py index 6923ebde5..5c0e27b84 100644 --- a/src/oemof/solph/components/_offset_transformer.py +++ b/src/oemof/solph/components/_offset_transformer.py @@ -12,6 +12,7 @@ SPDX-FileCopyrightText: Stephan Günther SPDX-FileCopyrightText: FabianTU SPDX-FileCopyrightText: Johannes Röder +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -144,19 +145,20 @@ def _create(self, group=None): self.OFFSETTRANSFORMERS = Set(initialize=[n for n in group]) - def _relation_rule(block, n, t): + def _relation_rule(block, n, p, t): """Link binary input and output flow to component outflow.""" expr = 0 - expr += -m.flow[n, list(n.outputs.keys())[0], t] + expr += -m.flow[n, list(n.outputs.keys())[0], p, t] expr += ( - m.flow[list(n.inputs.keys())[0], n, t] * n.coefficients[1][t] + m.flow[list(n.inputs.keys())[0], n, p, t] + * n.coefficients[1][t] ) expr += ( - m.NonConvexFlowBlock.status[list(n.inputs.keys())[0], n, t] + (m.NonConvexFlow.status[list(n.inputs.keys())[0], n, t]) * n.coefficients[0][t] ) return expr == 0 self.relation = Constraint( - self.OFFSETTRANSFORMERS, m.TIMESTEPS, rule=_relation_rule + self.OFFSETTRANSFORMERS, m.TIMEINDEX, rule=_relation_rule ) diff --git a/src/oemof/solph/components/_transformer.py b/src/oemof/solph/components/_transformer.py index 8c5e0d602..2a76cda29 100644 --- a/src/oemof/solph/components/_transformer.py +++ b/src/oemof/solph/components/_transformer.py @@ -13,6 +13,7 @@ SPDX-FileCopyrightText: Birgit Schachler SPDX-FileCopyrightText: jnnr SPDX-FileCopyrightText: jmloenneberga +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -163,8 +164,8 @@ def _create(self, group=None): self.relation = Constraint( [ - (n, i, o, t) - for t in m.TIMESTEPS + (n, i, o, p, t) + for p, t in m.TIMEINDEX for n in group for o in out_flows[n] for i in in_flows[n] @@ -173,17 +174,17 @@ def _create(self, group=None): ) def _input_output_relation(block): - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for n in group: for o in out_flows[n]: for i in in_flows[n]: try: lhs = ( - m.flow[i, n, t] + m.flow[i, n, p, t] * n.conversion_factors[o][t] ) rhs = ( - m.flow[n, o, t] + m.flow[n, o, p, t] * n.conversion_factors[i][t] ) except ValueError: @@ -193,6 +194,6 @@ def _input_output_relation(block): n.label, o.label ), ) - block.relation.add((n, i, o, t), (lhs == rhs)) + block.relation.add((n, i, o, p, t), (lhs == rhs)) self.relation_build = BuildAction(rule=_input_output_relation) From 9e0d1a066c0409f07397243f0e4e3a9583a986b6 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sun, 5 Dec 2021 19:16:09 +0100 Subject: [PATCH 0030/1363] Fix import --- src/oemof/solph/constraints/investment_limit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/solph/constraints/investment_limit.py b/src/oemof/solph/constraints/investment_limit.py index dce7d7e85..499a86c7d 100644 --- a/src/oemof/solph/constraints/investment_limit.py +++ b/src/oemof/solph/constraints/investment_limit.py @@ -15,7 +15,7 @@ from pyomo import environ as po -from oemof.solph import sequence +from oemof.solph._plumbing import sequence def investment_limit(model, limit=None): From 1be0274abf5ce41aaaa17b0c49c97d35fc6dc37c Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sun, 5 Dec 2021 19:16:24 +0100 Subject: [PATCH 0031/1363] Fix bug --- src/oemof/solph/processing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index 077358b00..fadc2bc39 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -216,10 +216,10 @@ def results(om): # add dual variables for bus constraints if om.dual is not None: - grouped = groupby(sorted(om.Bus.balance.iterkeys()), + grouped = groupby(sorted(om.BusBlock.balance.iterkeys()), lambda p: p[0]) for bus, timeindex in grouped: - duals = [om.dual[om.Bus.balance[bus, p, t]] + duals = [om.dual[om.BusBlock.balance[bus, p, t]] for _, p, t in timeindex] df = pd.DataFrame({"duals": duals}, index=om.es.timeindex) if (bus, None) not in result.keys(): From 33911b0b0ce4c6f77c91ca20b31859534463a856 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sun, 5 Dec 2021 19:37:45 +0100 Subject: [PATCH 0032/1363] Integrate multi-period draft into experimental components (but sink_dsm) --- .../components/experimental/_generic_caes.py | 19 +++-- .../solph/components/experimental/_link.py | 85 +++++++++++++++---- .../_piecewise_linear_transformer.py | 17 ++-- 3 files changed, 89 insertions(+), 32 deletions(-) diff --git a/src/oemof/solph/components/experimental/_generic_caes.py b/src/oemof/solph/components/experimental/_generic_caes.py index e9d8bf464..3be4dc705 100644 --- a/src/oemof/solph/components/experimental/_generic_caes.py +++ b/src/oemof/solph/components/experimental/_generic_caes.py @@ -11,6 +11,7 @@ SPDX-FileCopyrightText: jakob-wo SPDX-FileCopyrightText: gplssm SPDX-FileCopyrightText: jnnr +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -431,14 +432,14 @@ def _create(self, group=None): ) # Compression: Capacity on markets - def cmp_p_constr_rule(block, n, t): + def cmp_p_constr_rule(block, n, p, t): expr = 0 expr += -self.cmp_p[n, t] - expr += m.flow[list(n.electrical_input.keys())[0], n, t] + expr += m.flow[list(n.electrical_input.keys())[0], n, p, t] return expr == 0 self.cmp_p_constr = Constraint( - self.GENERICCAES, m.TIMESTEPS, rule=cmp_p_constr_rule + self.GENERICCAES, m.TIMEINDEX, rule=cmp_p_constr_rule ) # Compression: Max. capacity depending on cavern filling level @@ -521,14 +522,14 @@ def cmp_q_out_shr_constr_rule(block, n, t): ) # (10) Expansion: Capacity on markets - def exp_p_constr_rule(block, n, t): + def exp_p_constr_rule(block, n, p, t): expr = 0 expr += -self.exp_p[n, t] - expr += m.flow[n, list(n.electrical_output.keys())[0], t] + expr += m.flow[n, list(n.electrical_output.keys())[0], p, t] return expr == 0 self.exp_p_constr = Constraint( - self.GENERICCAES, m.TIMESTEPS, rule=exp_p_constr_rule + self.GENERICCAES, m.TIMEINDEX, rule=exp_p_constr_rule ) # (11-12) Expansion: Max. capacity depending on cavern filling level @@ -592,14 +593,14 @@ def exp_q_in_constr_rule(block, n, t): ) # (17) Expansion: Fuel allocation - def exp_q_fuel_constr_rule(block, n, t): + def exp_q_fuel_constr_rule(block, n, p, t): expr = 0 expr += -self.exp_q_fuel_in[n, t] - expr += m.flow[list(n.fuel_input.keys())[0], n, t] + expr += m.flow[list(n.fuel_input.keys())[0], n, p, t] return expr == 0 self.exp_q_fuel_constr = Constraint( - self.GENERICCAES, m.TIMESTEPS, rule=exp_q_fuel_constr_rule + self.GENERICCAES, m.TIMEINDEX, rule=exp_q_fuel_constr_rule ) # (18) Expansion: Definition of single heat flows diff --git a/src/oemof/solph/components/experimental/_link.py b/src/oemof/solph/components/experimental/_link.py index f9094d1a4..c98c76adb 100644 --- a/src/oemof/solph/components/experimental/_link.py +++ b/src/oemof/solph/components/experimental/_link.py @@ -12,12 +12,16 @@ SPDX-FileCopyrightText: jakob-wo SPDX-FileCopyrightText: gplssm SPDX-FileCopyrightText: jnnr +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT """ from oemof.network import network as on +from pyomo.core import Binary +from pyomo.core import Set +from pyomo.core import Var from pyomo.core.base.block import SimpleBlock from pyomo.environ import BuildAction from pyomo.environ import Constraint @@ -52,13 +56,12 @@ class Link(on.Transformer): >>> link = solph.components.experimental.Link( ... label="transshipment_link", - ... inputs={bel0: solph.flows.Flow(), - ... bel1: solph.flows.Flow()}, - ... outputs={bel0: solph.flows.Flow(), - ... bel1: solph.flows.Flow()}, - ... conversion_factors={(bel0, bel1): 0.92, (bel1, bel0): 0.99}) + ... inputs={bel0: solph.flows.Flow(nominal_value=4), + ... bel1: solph.flows.Flow(nominal_value=2)}, + ... outputs={bel0: solph.flows.Flow(), bel1: solph.flows.Flow()}, + ... conversion_factors={(bel0, bel1): 0.8, (bel1, bel0): 0.9}) >>> print(sorted([x[1][5] for x in link.conversion_factors.items()])) - [0.92, 0.99] + [0.8, 0.9] >>> type(link) @@ -67,7 +70,7 @@ class Link(on.Transformer): ['el0', 'el1'] >>> link.conversion_factors[(bel0, bel1)][3] - 0.92 + 0.8 """ def __init__(self, *args, **kwargs): @@ -99,8 +102,21 @@ class LinkBlock(SimpleBlock): **The following constraints are created:** - TODO: Add description for constraints - TODO: Add tests + .. _Link-equations: + + .. math:: + & + (1) \qquad P_{\mathrm{in},n}(t) = c_n(t) \times P_{\mathrm{out},n}(t) + \quad \forall t \in T, \forall n in {1,2} \\ + & + (2) \qquad 1 \ge \hat{S} + P_{\mathrm{in},1}(t) + / P_{\mathrm{in},1,\mathrm{max}} + \quad \forall t \in T \\ + & + (3) \qquad 0 \le \hat{S} - P_{\mathrm{in},2}(t) + / P_{2\mathrm{in},2,\mathrm{max}} + \quad \forall t \in T \\ + & """ CONSTRAINT_GROUP = True @@ -131,14 +147,25 @@ def _create(self, group=None): k: v for k, v in n.conversion_factors.items() } + self.LINKS = Set(initialize=[g for g in group]) + self.LINK_1ST_INFLOWS = Set( + initialize=[(list(c)[0][0], n) for n, c in all_conversions.items()] + ) + self.LINK_2ND_INFLOWS = Set( + initialize=[(list(c)[1][0], n) for n, c in all_conversions.items()] + ) + + # 0: Flows 1 connected; 1: Flows 2 connected + self.direction = Var(self.LINKS, m.TIMESTEPS, within=Binary) + def _input_output_relation(block): - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for n, conversion in all_conversions.items(): for cidx, c in conversion.items(): try: expr = ( - m.flow[n, cidx[1], t] - == c[t] * m.flow[cidx[0], n, t] + m.flow[n, cidx[1], p, t] + == c[t] * m.flow[cidx[0], n, p, t] ) except ValueError: raise ValueError( @@ -147,15 +174,43 @@ def _input_output_relation(block): cidx[0], cidx[1], n ), ) - block.relation.add((n, cidx[0], cidx[1], t), (expr)) + block.relation.add((n, cidx[0], cidx[1], p, t), expr) self.relation = Constraint( [ - (n, cidx[0], cidx[1], t) - for t in m.TIMESTEPS + (n, cidx[0], cidx[1], p, t) + for p, t in m.TIMEINDEX for n, conversion in all_conversions.items() for cidx, c in conversion.items() ], noruleinit=True, ) self.relation_build = BuildAction(rule=_input_output_relation) + + def _flow1_rule(block, i, link, p, t): + """Rule definition for Eq. (2).""" + expr = 1 >= ( + self.direction[link, t] + + m.flow[i, link, p, t] + * m.flows[i, link].max[t] + * m.flows[i, link].nominal_value + ) + return expr + + self.flow1 = Constraint( + self.LINK_1ST_INFLOWS, m.TIMEINDEX, rule=_flow1_rule + ) + + def _flow2_rule(block, i, link, p, t): + """Rule definition for Eq. (3).""" + expr = 0 <= ( + self.direction[link, t] + - m.flow[i, link, p, t] + * m.flows[i, link].max[t] + * m.flows[i, link].nominal_value + ) + return expr + + self.flow2 = Constraint( + self.LINK_2ND_INFLOWS, m.TIMEINDEX, rule=_flow2_rule + ) diff --git a/src/oemof/solph/components/experimental/_piecewise_linear_transformer.py b/src/oemof/solph/components/experimental/_piecewise_linear_transformer.py index bc72c4aa6..1e3567015 100644 --- a/src/oemof/solph/components/experimental/_piecewise_linear_transformer.py +++ b/src/oemof/solph/components/experimental/_piecewise_linear_transformer.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ -In-development transfomer with piecewise linar efficiencies. +In-development transformer with piecewise linear efficiencies. SPDX-FileCopyrightText: Uwe Krien SPDX-FileCopyrightText: Simon Hilpert @@ -11,6 +11,7 @@ SPDX-FileCopyrightText: jakob-wo SPDX-FileCopyrightText: gplssm SPDX-FileCopyrightText: jnnr +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -125,7 +126,7 @@ def _create(self, group=None): self.pw_repn = pw_repns[0] else: print( - "Cannot different piecewise representations ", + "Cannot model different piecewise representations ", [n.pw_repn for n in group], ) @@ -168,26 +169,26 @@ def get_outflow_bounds(model, n, t): self.PWLINEARTRANSFORMERS, m.TIMESTEPS, bounds=get_outflow_bounds ) - def _in_equation(block, n, t): + def _in_equation(block, n, p, t): """Link binary input and output flow to component outflow.""" expr = 0 - expr += -m.flow[list(n.inputs.keys())[0], n, t] + expr += -m.flow[list(n.inputs.keys())[0], n, p, t] expr += self.inflow[n, t] return expr == 0 self.equate_in = Constraint( - self.PWLINEARTRANSFORMERS, m.TIMESTEPS, rule=_in_equation + self.PWLINEARTRANSFORMERS, m.TIMEINDEX, rule=_in_equation ) - def _out_equation(block, n, t): + def _out_equation(block, n, p, t): """Link binary input and output flow to component outflow.""" expr = 0 - expr += -m.flow[n, list(n.outputs.keys())[0], t] + expr += -m.flow[n, list(n.outputs.keys())[0], p, t] expr += self.outflow[n, t] return expr == 0 self.equate_out = Constraint( - self.PWLINEARTRANSFORMERS, m.TIMESTEPS, rule=_out_equation + self.PWLINEARTRANSFORMERS, m.TIMEINDEX, rule=_out_equation ) self.piecewise = Piecewise( From 6f5d0c0b0645fc05f93a3c7f3691144c84f8bdb2 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sun, 5 Dec 2021 19:51:06 +0100 Subject: [PATCH 0033/1363] Begin adapting _sink_dsm.py --- .../components/experimental/_sink_dsm.py | 516 +++++++++++++----- 1 file changed, 385 insertions(+), 131 deletions(-) diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index 608f465b9..891acf0a3 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -17,8 +17,11 @@ """ import itertools +from warnings import warn from numpy import mean +from oemof.tools import debugging +from oemof.tools import economics from pyomo.core.base.block import SimpleBlock from pyomo.environ import BuildAction from pyomo.environ import Constraint @@ -78,7 +81,7 @@ class SinkDSM(Sink): For investment modeling, it is advised to use the maximum of the demand timeseries and the cumulated (fixed) infeed time series for normalization, because the balancing potential may be determined by - both. Elsewhise, underinvestments may occur. + both. Elsewise, underinvestments may occur. capacity_up: int or array maximum DSM capacity that may be increased (normalized) capacity_down: int or array @@ -179,7 +182,7 @@ class SinkDSM(Sink): Maximum number of load sheds at full capacity per year, used to limit the amount of energy shedded per year. Mandatory parameter if load shedding is allowed by setting shed_eligibility to True - t_dayLimit: int + t_dayLimit : int Only used when :attr:`~approach` is set to 'DLR'. Maximum duration of load shifts at full capacity per day, used to limit the amount of energy shifted per day. Optional parameter that is only @@ -199,6 +202,8 @@ class SinkDSM(Sink): shift_eligibility : boolean Boolean parameter indicating whether unit is eligible for load shifting + fixed_costs : numeric + Nominal value of fixed costs (per period) Note ---- @@ -254,6 +259,7 @@ def __init__( fixes=True, shed_eligibility=True, shift_eligibility=True, + fixed_costs=0, **kwargs, ): super().__init__(**kwargs) @@ -281,9 +287,9 @@ def __init__( self.flex_share_down = flex_share_down else: e1 = ( - "Please determine either **flex_share_down " + "Please determine either flex_share_down " "(investment modeling)\n or set " - "**max_demand and **max_capacity_down " + "max_demand and max_capacity_down " "(dispatch modeling).\n" "Otherwise, overdetermination occurs." ) @@ -291,10 +297,10 @@ def __init__( else: if max_capacity_down is None or max_demand is None: e2 = ( - "If you do not specify **flex_share_down\n" + "If you do not specify flex_share_down\n" "which should be used for investment modeling,\n" - "you have to specify **max_capacity_down " - "and **max_demand\n" + "you have to specify max_capacity_down " + "and max_demand\n" "instead which should be used for dispatch modeling." ) raise AttributeError(e2) @@ -315,10 +321,10 @@ def __init__( else: if max_capacity_up is None or max_demand is None: e4 = ( - "If you do not specify **flex_share_up\n" + "If you do not specify flex_share_up\n" "which should be used for investment modeling,\n" - "you have to specify **max_capacity_up " - "and **max_demand\n" + "you have to specify max_capacity_up " + "and max_demand\n" "instead which should be used for dispatch modeling." ) raise AttributeError(e4) @@ -342,6 +348,7 @@ def __init__( self.fixes = fixes self.shed_eligibility = shed_eligibility self.shift_eligibility = shift_eligibility + self.fixed_costs = sequence(fixed_costs) # Check whether investment mode is active or not self.investment = kwargs.get("investment") @@ -354,10 +361,10 @@ def __init__( ) and not self._invest_group: e5 = ( "If you are setting up a dispatch model, " - "you have to specify **max_demand**, **max_capacity_up** " - "and **max_capacity_down**.\n" - "The values you might have passed for **flex_share_up** " - "and **flex_share_down** will be ignored and only used in " + "you have to specify max_demand, max_capacity_up " + "and max_capacity_down.\n" + "The values you might have passed for flex_share_up " + "and flex_share_down will be ignored and only used in " "an investment model." ) raise AttributeError(e5) @@ -377,10 +384,10 @@ def _check_invest_attributes(self): ): e6 = ( "If an investment object is defined, the invest variable " - "replaces the **max_demand, the **max_capacity_down " + "replaces the **max_demand, the max_capacity_down " "as well as\n" - "the **max_capacity_up values. Therefore, **max_demand,\n" - "**max_capacity_up and **max_capacity_down values should be " + "the max_capacity_up values. Therefore, max_demand,\n" + "max_capacity_up and max_capacity_down values should be " "'None'.\n" ) raise AttributeError(e6) @@ -391,18 +398,19 @@ def constraint_group(self): if self.approach in [possible_approaches[0], possible_approaches[1]]: if self.delay_time is None: raise ValueError( - "Please define: **delay_time" " is a mandatory parameter" + "Please define: delay_time.\n" + "It is a mandatory parameter" ) if not self.shed_eligibility and not self.shift_eligibility: raise ValueError( - "At least one of **shed_eligibility" - " and **shift_eligibility must be True" + "At least one of shed_eligibility" + " and shift_eligibility must be True" ) if self.shed_eligibility: if self.recovery_time_shed is None: raise ValueError( "If unit is eligible for load shedding," - " **recovery_time_shed must be defined" + " recovery_time_shed must be defined" ) if self.approach == possible_approaches[0]: @@ -420,8 +428,9 @@ def constraint_group(self): elif self.approach == possible_approaches[2]: if self.shift_interval is None: raise ValueError( - "Please define: **shift_interval" - " is a mandatory parameter" + f"Please define: shift_interval.\n" + f"It is a mandatory parameter when using" + f" approach {self.approach}." ) if self._invest_group is True: return SinkDSMOemofInvestmentBlock @@ -509,6 +518,8 @@ class SinkDSMOemofBlock(SimpleBlock): levelled out" ":math:`\eta`",":attr:`~SinkDSM.efficiency`","P", "Efficiency loss forload shifting processes" + ":math:`\eta`",":attr:`efficiency`","P", "Efficiency loss for + load shifting processes" ":math:`\mathbb{T}` "," ","P", "Time steps" ":math:`eligibility_{shift}` ", ":attr:`~SinkDSM.shift_eligibility`","P", @@ -551,13 +562,14 @@ def _create(self, group=None): # Variable load shift down self.dsm_do_shift = Var( - self.dsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals + self.dsm, m.TIMESTEPS, initialize=0, + within=NonNegativeReals ) # Variable load shedding self.dsm_do_shed = Var( - self.dsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals - ) + self.dsm, m.TIMESTEPS, initialize=0, + within=NonNegativeReals) # Variable load shift up self.dsm_up = Var( @@ -594,10 +606,10 @@ def _input_output_relation_rule(block): The actual demand after DSM. Generator Production == Demand_el +- DSM """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # Inflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand + DSM_up - DSM_down rhs = ( @@ -608,10 +620,10 @@ def _input_output_relation_rule(block): ) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add((g, p, t), (lhs == rhs)) self.input_output_relation = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.input_output_relation_build = BuildAction( rule=_input_output_relation_rule @@ -701,23 +713,52 @@ def _objective_expression(self): m = self.parent_block() - dsm_cost = 0 + variable_costs = 0 + fixed_costs = 0 - for t in m.TIMESTEPS: + if not m.es.multi_period: + for t in m.TIMESTEPS: + for g in self.dsm: + variable_costs += ( + self.dsm_up[g, t] + * g.cost_dsm_up[t] + * m.objective_weighting[t] + ) + variable_costs += ( + self.dsm_do_shift[g, t] * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) * m.objective_weighting[t] + + else: for g in self.dsm: - dsm_cost += ( - self.dsm_up[g, t] - * g.cost_dsm_up[t] - * m.objective_weighting[t] - ) - dsm_cost += ( - self.dsm_do_shift[g, t] * g.cost_dsm_down_shift[t] - + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] - ) * m.objective_weighting[t] + for p, t in m.TIMEINDEX: + variable_costs += ( + self.dsm_up[g, t] + * m.objective_weighting[t] + * g.cost_dsm_up[p] + * ((1 + m.discount_rate) ** -p) + ) + variable_costs += ( + (self.dsm_do_shift[g, t] + * g.cost_dsm_down_shift[p] + + self.dsm_do_shed[g, t] + * g.cost_dsm_down_shed[p]) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + ) - self.cost = Expression(expr=dsm_cost) + if g.fixed_costs[0] is not None: + for p in m.PERIODS: + fixed_costs += ( + g.max_demand + * max(g.demand) + * g.fixed_costs[p] + * ((1 + m.discount_rate) ** (-p)) + ) - return self.cost + self.costs = Expression(expr=variable_costs + fixed_costs) + + return self.costs class SinkDSMOemofInvestmentBlock(SimpleBlock): @@ -828,36 +869,158 @@ def _create(self, group=None): # ************* VARIABLES ***************************** # Define bounds for investments in demand response - def _dsm_investvar_bound_rule(block, g): + def _dsm_investvar_bound_rule(block, g, p): """Rule definition to bound the invested demand response capacity `invest`. """ - return g.investment.minimum, g.investment.maximum + return g.investment.minimum[p], g.investment.maximum[p] # Investment in DR capacity self.invest = Var( self.investdsm, + m.PERIODS, within=NonNegativeReals, - bounds=_dsm_investvar_bound_rule, + bounds=_dsm_investvar_bound_rule + ) + + # Total capacity + self.total = Var( + self.investdsm, + m.PERIODS, + within=NonNegativeReals + ) + + # Old capacity to be decommissioned (due to lifetime) + # Old capacity is built out of old exogenous and endogenous capacities + self.old = Var( + self.investdsm, + m.PERIODS, + within=NonNegativeReals + ) + + # Old endogenous capacity to be decommissioned (due to lifetime) + self.old_end = Var( + self.investdsm, + m.PERIODS, + within=NonNegativeReals + ) + + # Old exogenous capacity to be decommissioned (due to lifetime) + self.old_exo = Var( + self.investdsm, + m.PERIODS, + within=NonNegativeReals ) # Variable load shift down self.dsm_do_shift = Var( - self.investdsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals + self.investdsm, m.TIMESTEPS, + initialize=0, + within=NonNegativeReals ) # Variable load shedding self.dsm_do_shed = Var( - self.investdsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals + self.investdsm, m.TIMESTEPS, initialize=0, + within=NonNegativeReals ) # Variable load shift up self.dsm_up = Var( - self.investdsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals + self.investdsm, m.TIMESTEPS, + initialize=0, + within=NonNegativeReals ) # ************* CONSTRAINTS ***************************** + # Handle unit lifetimes + def _total_dsm_capacity_rule(block): + """Rule definition for determining total installed + capacity (taking decommissioning into account) + """ + for g in group: + for p in m.PERIODS: + if p == 0: + expr = (self.total[g, p] + == self.invest[g, p] + + g.investment.existing) + self.total_dsm_rule.add((g, p), expr) + else: + expr = (self.total[g, p] + == self.invest[g, p] + + self.total[g, p - 1] + - self.old[g, p]) + self.total_dsm_rule.add((g, p), expr) + + self.total_dsm_rule = Constraint(group, m.PERIODS, + noruleinit=True) + self.total_dsm_rule_build = BuildAction( + rule=_total_dsm_capacity_rule) + + def _old_dsm_capacity_rule_end(block): + """Rule definition for determining old endogenously installed + capacity to be decommissioned due to reaching its lifetime + """ + for g in group: + lifetime = g.investment.lifetime + for p in m.PERIODS: + if lifetime <= p: + expr = (self.old_end[g, p] + == self.invest[g, p - lifetime]) + self.old_dsm_rule_end.add((g, p), expr) + else: + expr = (self.old_end[g, p] + == 0) + self.old_dsm_rule_end.add((g, p), expr) + + self.old_dsm_rule_end = Constraint(group, + m.PERIODS, + noruleinit=True) + self.old_dsm_rule_end_build = BuildAction( + rule=_old_dsm_capacity_rule_end) + + def _old_dsm_capacity_rule_exo(block): + """Rule definition for determining old exogenously given capacity + to be decommissioned due to reaching its lifetime + """ + for g in group: + age = g.investment.age + lifetime = g.investment.lifetime + for p in m.PERIODS: + if lifetime - age == p: + expr = ( + self.old_exo[g, p] + == g.investment.existing) + self.old_dsm_rule_exo.add((g, p), expr) + else: + expr = (self.old_exo[g, p] + == 0) + self.old_dsm_rule_exo.add((g, p), expr) + + self.old_dsm_rule_exo = Constraint(group, + m.PERIODS, + noruleinit=True) + self.old_dsm_rule_exo_build = BuildAction( + rule=_old_dsm_capacity_rule_exo) + + def _old_dsm_capacity_rule(block): + """Rule definition for determining (overall) old capacity + to be decommissioned due to reaching its lifetime + """ + for g in group: + for p in m.PERIODS: + expr = ( + self.old[g, p] + == self.old_end[g, p] + self.old_exo[g, p]) + self.old_dsm_rule.add((g, p), expr) + + self.old_dsm_rule = Constraint(group, + m.PERIODS, + noruleinit=True) + self.old_dsm_rule_build = BuildAction( + rule=_old_dsm_capacity_rule) + def _shift_shed_vars_rule(block): """Force shifting resp. shedding variables to zero dependent on how boolean parameters for shift resp. shed eligibility @@ -886,24 +1049,21 @@ def _input_output_relation_rule(block): The actual demand after DSM. Generator Production == Demand_el +- DSM """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # Inflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand + DSM_up - DSM_down - rhs = ( - g.demand[t] * (self.invest[g] + g.investment.existing) - + self.dsm_up[g, t] - - self.dsm_do_shift[g, t] - - self.dsm_do_shed[g, t] - ) + rhs = (g.demand[t] * self.total[g, p] + + self.dsm_up[g, t] - self.dsm_do_shift[g, t] + - self.dsm_do_shed[g, t]) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add((g, p, t), (lhs == rhs)) self.input_output_relation = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.input_output_relation_build = BuildAction( rule=_input_output_relation_rule @@ -914,23 +1074,19 @@ def dsm_up_constraint_rule(block): """Realised upward load shift at time t has to be smaller than upward DSM capacity at time t. """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # DSM up lhs = self.dsm_up[g, t] # Capacity dsm_up - rhs = ( - g.capacity_up[t] - * (self.invest[g] + g.investment.existing) - * g.flex_share_up - ) + rhs = (g.capacity_up[t] * self.total[g, p] + * g.flex_share_up) # add constraint - block.dsm_up_constraint.add((g, t), (lhs <= rhs)) + block.dsm_up_constraint.add((g, p, t), (lhs <= rhs)) - self.dsm_up_constraint = Constraint( - group, m.TIMESTEPS, noruleinit=True - ) + self.dsm_up_constraint = Constraint(group, m.TIMEINDEX, + noruleinit=True) self.dsm_up_constraint_build = BuildAction(rule=dsm_up_constraint_rule) # Upper bounds relation @@ -938,26 +1094,21 @@ def dsm_down_constraint_rule(block): """Realised downward load shift at time t has to be smaller than downward DSM capacity at time t. """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # DSM down lhs = self.dsm_do_shift[g, t] + self.dsm_do_shed[g, t] # Capacity dsm_down - rhs = ( - g.capacity_down[t] - * (self.invest[g] + g.investment.existing) - * g.flex_share_down - ) + rhs = (g.capacity_down[t] * self.total[g, p] + * g.flex_share_down) # add constraint - block.dsm_down_constraint.add((g, t), (lhs <= rhs)) + block.dsm_down_constraint.add((g, p, t), (lhs <= rhs)) - self.dsm_down_constraint = Constraint( - group, m.TIMESTEPS, noruleinit=True - ) + self.dsm_down_constraint = Constraint(group, m.TIMEINDEX, + noruleinit=True) self.dsm_down_constraint_build = BuildAction( - rule=dsm_down_constraint_rule - ) + rule=dsm_down_constraint_rule) def dsm_sum_constraint_rule(block): """Relation to compensate the total amount of positive @@ -1002,27 +1153,98 @@ def _objective_expression(self): m = self.parent_block() investment_costs = 0 + period_investment_costs = {p: 0 for p in m.PERIODS} variable_costs = 0 + fixed_costs = 0 + + if not m.es.multi_period: + for g in self.investdsm: + for p in m.PERIODS: + if g.investment.ep_costs is not None: + investment_costs += ( + self.invest[g, p] + * g.investment.ep_costs[p]) + else: + raise ValueError("Missing value for investment costs!") - for g in self.investdsm: - if g.investment.ep_costs is not None: - investment_costs += self.invest[g] * g.investment.ep_costs - else: - raise ValueError("Missing value for investment costs!") - for t in m.TIMESTEPS: - variable_costs += ( - self.dsm_up[g, t] - * g.cost_dsm_up[t] - * m.objective_weighting[t] - ) - variable_costs += ( - self.dsm_do_shift[g, t] * g.cost_dsm_down_shift[t] - + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] - ) * m.objective_weighting[t] + for t in m.TIMESTEPS: + variable_costs += ( + self.dsm_up[g, t] + * g.cost_dsm_up[t] + * m.objective_weighting[t] + ) + variable_costs += ( + self.dsm_do_shift[g, t] * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) * m.objective_weighting[t] - self.cost = Expression(expr=investment_costs + variable_costs) + else: + msg = ("You did not specify an interest rate.\n" + "It will be set equal to the discount_rate of {} " + "of the model as a default.\nThis corresponds to a " + "social planner point of view and does not reflect " + "microeconomic interest requirements.") + for g in self.investdsm: + if g.investment.ep_costs is not None: + lifetime = g.investment.lifetime + interest = g.investment.interest_rate + if interest == 0: + warn(msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning) + interest = m.discount_rate + for p in m.PERIODS: + annuity = economics.annuity( + capex=g.investment.ep_costs[p], + n=lifetime, + wacc=interest + ) + investment_costs_increment = ( + self.invest[g, p] * annuity * lifetime + * ((1 + m.discount_rate) ** (-p)) + ) + investment_costs += investment_costs_increment + period_investment_costs[p] += ( + investment_costs_increment + ) + else: + raise ValueError("Missing value for investment costs!") + + for p, t in m.TIMEINDEX: + variable_costs += ( + self.dsm_up[g, t] + * m.objective_weighting[t] + * g.cost_dsm_up[p] + * ((1 + m.discount_rate) ** -p) + ) + variable_costs += ( + (self.dsm_do_shift[g, t] + * g.cost_dsm_down_shift[p] + + self.dsm_do_shed[g, t] + * g.cost_dsm_down_shed[p]) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + ) - return self.cost + if g.investment.fixed_costs[0] is not None: + lifetime = g.investment.lifetime + for p in m.PERIODS: + fixed_costs += ( + sum(self.invest[g, p] + * max(g.demand) + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(p, p + lifetime) + ) + * ((1 + m.discount_rate) ** (-p)) + ) + + self.investment_costs = investment_costs + self.period_investment_costs = period_investment_costs + self.costs = Expression( + expr=investment_costs + variable_costs + fixed_costs + ) + + return self.costs class SinkDSMDIWBlock(SimpleBlock): @@ -1080,7 +1302,7 @@ class SinkDSMDIWBlock(SimpleBlock): .. math:: DSM_{t}^{up} \cdot cost_{t}^{dsm, up} - + \sum_{tt=0}^{|T|} DSM_{t, tt}^{do, shift} \cdot + + \sum_{tt=0}^{|T|} DSM_{tt, t}^{do, shift} \cdot cost_{t}^{dsm, do, shift} + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed} \quad \forall t \in \mathbb{T} \\ @@ -1218,13 +1440,13 @@ def _input_output_relation_rule(block): The actual demand after DSM. Sink Inflow == Demand +- DSM """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # first time steps: 0 + delay time if t <= g.delay_time: # Inflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand +- DSM rhs = ( g.demand[t] * g.max_demand @@ -1237,52 +1459,54 @@ def _input_output_relation_rule(block): ) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add( + (g, p, t), (lhs == rhs) + ) # main use case elif g.delay_time < t <= m.TIMESTEPS[-1] - g.delay_time: # Inflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand +- DSM rhs = ( g.demand[t] * g.max_demand + self.dsm_up[g, t] - sum( self.dsm_do_shift[g, tt, t] - for tt in range( - t - g.delay_time, t + g.delay_time + 1 - ) + for tt in range(t - g.delay_time, + t + g.delay_time + 1) ) - - self.dsm_do_shed[g, t] - ) + - self.dsm_do_shed[g, t]) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add( + (g, p, t), (lhs == rhs) + ) # last time steps: end - delay time else: # Inflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand +- DSM rhs = ( g.demand[t] * g.max_demand + self.dsm_up[g, t] - sum( self.dsm_do_shift[g, tt, t] - for tt in range( - t - g.delay_time, m.TIMESTEPS[-1] + 1 - ) + for tt in range(t - g.delay_time, + m.TIMESTEPS[-1] + 1) ) - - self.dsm_do_shed[g, t] - ) + - self.dsm_do_shed[g, t]) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add( + (g, p, t), (lhs == rhs) + ) self.input_output_relation = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.input_output_relation_build = BuildAction( rule=_input_output_relation_rule @@ -1650,24 +1874,54 @@ def _objective_expression(self): m = self.parent_block() - dsm_cost = 0 + variable_costs = 0 + fixed_costs = 0 - for t in m.TIMESTEPS: + if not m.es.multi_period: + for t in m.TIMESTEPS: + for g in self.dsm: + variable_costs += ( + self.dsm_up[g, t] + * g.cost_dsm_up[t] + * m.objective_weighting[t] + ) + variable_costs += ( + sum(self.dsm_do_shift[g, tt, t] for tt in m.TIMESTEPS) + * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) * m.objective_weighting[t] + + else: for g in self.dsm: - dsm_cost += ( - self.dsm_up[g, t] - * g.cost_dsm_up[t] - * m.objective_weighting[t] - ) - dsm_cost += ( - sum(self.dsm_do_shift[g, tt, t] for tt in m.TIMESTEPS) - * g.cost_dsm_down_shift[t] - + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] - ) * m.objective_weighting[t] + for p, t in m.TIMEINDEX: + variable_costs += ( + self.dsm_up[g, t] + * m.objective_weighting[t] + * g.cost_dsm_up[p] + * ((1 + m.discount_rate) ** -p) + ) + variable_costs += ( + (sum(self.dsm_do_shift[g, tt, t] + for tt in m.TIMESTEPS) + * g.cost_dsm_down_shift[p] + + self.dsm_do_shed[g, t] + * g.cost_dsm_down_shed[p]) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + ) + + if g.fixed_costs[0] is not None: + for p in m.PERIODS: + fixed_costs += ( + g.max_demand + * max(g.demand) + * g.fixed_costs[p] + * ((1 + m.discount_rate) ** (-p)) + ) - self.cost = Expression(expr=dsm_cost) + self.costs = Expression(expr=variable_costs + fixed_costs) - return self.cost + return self.costs class SinkDSMDIWInvestmentBlock(SimpleBlock): From 45f3e8ae056fb098174d7274df567e02fd75a5ea Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 10 Dec 2021 09:29:38 +0100 Subject: [PATCH 0034/1363] Integrate multi-period into _sink_dsm.py and format using black --- .../components/experimental/_sink_dsm.py | 1244 +++++++++++------ 1 file changed, 832 insertions(+), 412 deletions(-) diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index 891acf0a3..a6018d98b 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -11,7 +11,7 @@ SPDX-FileCopyrightText: jakob-wo SPDX-FileCopyrightText: gplssm SPDX-FileCopyrightText: jnnr -SPDX-FileCopyrightText: Johannes Kochems (jokochems) +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -384,7 +384,7 @@ def _check_invest_attributes(self): ): e6 = ( "If an investment object is defined, the invest variable " - "replaces the **max_demand, the max_capacity_down " + "replaces the max_demand, the max_capacity_down " "as well as\n" "the max_capacity_up values. Therefore, max_demand,\n" "max_capacity_up and max_capacity_down values should be " @@ -393,25 +393,27 @@ def _check_invest_attributes(self): raise AttributeError(e6) def constraint_group(self): - possible_approaches = ["DIW", "DLR", "oemof"] + possible_approaches = ["DIW", "DLR", "oemof"] # - if self.approach in [possible_approaches[0], possible_approaches[1]]: - if self.delay_time is None: + if not self.shed_eligibility and not self.shift_eligibility: + raise ValueError( + "At least one of shed_eligibility" + " and shift_eligibility must be True" + ) + if self.shed_eligibility: + if self.recovery_time_shed is None: raise ValueError( - "Please define: delay_time.\n" - "It is a mandatory parameter" + "If unit is eligible for load shedding," + " recovery_time_shed must be defined" ) - if not self.shed_eligibility and not self.shift_eligibility: + + if self.approach in [possible_approaches[0], possible_approaches[1]]: + if self.delay_time is None: raise ValueError( - "At least one of shed_eligibility" - " and shift_eligibility must be True" + f"Please define: delay_time.\n" + f"It is a mandatory parameter when using" + f" approach {self.approach}." ) - if self.shed_eligibility: - if self.recovery_time_shed is None: - raise ValueError( - "If unit is eligible for load shedding," - " recovery_time_shed must be defined" - ) if self.approach == possible_approaches[0]: if self._invest_group is True: @@ -562,14 +564,13 @@ def _create(self, group=None): # Variable load shift down self.dsm_do_shift = Var( - self.dsm, m.TIMESTEPS, initialize=0, - within=NonNegativeReals + self.dsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals ) # Variable load shedding self.dsm_do_shed = Var( - self.dsm, m.TIMESTEPS, initialize=0, - within=NonNegativeReals) + self.dsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals + ) # Variable load shift up self.dsm_up = Var( @@ -735,14 +736,14 @@ def _objective_expression(self): variable_costs += ( self.dsm_up[g, t] * m.objective_weighting[t] - * g.cost_dsm_up[p] + * g.cost_dsm_up[t] * ((1 + m.discount_rate) ** -p) ) variable_costs += ( - (self.dsm_do_shift[g, t] - * g.cost_dsm_down_shift[p] - + self.dsm_do_shed[g, t] - * g.cost_dsm_down_shed[p]) + ( + self.dsm_do_shift[g, t] * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) * m.objective_weighting[t] * ((1 + m.discount_rate) ** -p) ) @@ -880,56 +881,35 @@ def _dsm_investvar_bound_rule(block, g, p): self.investdsm, m.PERIODS, within=NonNegativeReals, - bounds=_dsm_investvar_bound_rule + bounds=_dsm_investvar_bound_rule, ) # Total capacity - self.total = Var( - self.investdsm, - m.PERIODS, - within=NonNegativeReals - ) + self.total = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) # Old capacity to be decommissioned (due to lifetime) # Old capacity is built out of old exogenous and endogenous capacities - self.old = Var( - self.investdsm, - m.PERIODS, - within=NonNegativeReals - ) + self.old = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) # Old endogenous capacity to be decommissioned (due to lifetime) - self.old_end = Var( - self.investdsm, - m.PERIODS, - within=NonNegativeReals - ) + self.old_end = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) # Old exogenous capacity to be decommissioned (due to lifetime) - self.old_exo = Var( - self.investdsm, - m.PERIODS, - within=NonNegativeReals - ) + self.old_exo = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) # Variable load shift down self.dsm_do_shift = Var( - self.investdsm, m.TIMESTEPS, - initialize=0, - within=NonNegativeReals + self.investdsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals ) # Variable load shedding self.dsm_do_shed = Var( - self.investdsm, m.TIMESTEPS, initialize=0, - within=NonNegativeReals + self.investdsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals ) # Variable load shift up self.dsm_up = Var( - self.investdsm, m.TIMESTEPS, - initialize=0, - within=NonNegativeReals + self.investdsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals ) # ************* CONSTRAINTS ***************************** @@ -942,21 +922,22 @@ def _total_dsm_capacity_rule(block): for g in group: for p in m.PERIODS: if p == 0: - expr = (self.total[g, p] - == self.invest[g, p] - + g.investment.existing) + expr = ( + self.total[g, p] + == self.invest[g, p] + g.investment.existing + ) self.total_dsm_rule.add((g, p), expr) else: - expr = (self.total[g, p] - == self.invest[g, p] - + self.total[g, p - 1] - - self.old[g, p]) + expr = ( + self.total[g, p] + == self.invest[g, p] + + self.total[g, p - 1] + - self.old[g, p] + ) self.total_dsm_rule.add((g, p), expr) - self.total_dsm_rule = Constraint(group, m.PERIODS, - noruleinit=True) - self.total_dsm_rule_build = BuildAction( - rule=_total_dsm_capacity_rule) + self.total_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) + self.total_dsm_rule_build = BuildAction(rule=_total_dsm_capacity_rule) def _old_dsm_capacity_rule_end(block): """Rule definition for determining old endogenously installed @@ -966,19 +947,18 @@ def _old_dsm_capacity_rule_end(block): lifetime = g.investment.lifetime for p in m.PERIODS: if lifetime <= p: - expr = (self.old_end[g, p] - == self.invest[g, p - lifetime]) + expr = ( + self.old_end[g, p] == self.invest[g, p - lifetime] + ) self.old_dsm_rule_end.add((g, p), expr) else: - expr = (self.old_end[g, p] - == 0) + expr = self.old_end[g, p] == 0 self.old_dsm_rule_end.add((g, p), expr) - self.old_dsm_rule_end = Constraint(group, - m.PERIODS, - noruleinit=True) + self.old_dsm_rule_end = Constraint(group, m.PERIODS, noruleinit=True) self.old_dsm_rule_end_build = BuildAction( - rule=_old_dsm_capacity_rule_end) + rule=_old_dsm_capacity_rule_end + ) def _old_dsm_capacity_rule_exo(block): """Rule definition for determining old exogenously given capacity @@ -989,20 +969,16 @@ def _old_dsm_capacity_rule_exo(block): lifetime = g.investment.lifetime for p in m.PERIODS: if lifetime - age == p: - expr = ( - self.old_exo[g, p] - == g.investment.existing) + expr = self.old_exo[g, p] == g.investment.existing self.old_dsm_rule_exo.add((g, p), expr) else: - expr = (self.old_exo[g, p] - == 0) + expr = self.old_exo[g, p] == 0 self.old_dsm_rule_exo.add((g, p), expr) - self.old_dsm_rule_exo = Constraint(group, - m.PERIODS, - noruleinit=True) + self.old_dsm_rule_exo = Constraint(group, m.PERIODS, noruleinit=True) self.old_dsm_rule_exo_build = BuildAction( - rule=_old_dsm_capacity_rule_exo) + rule=_old_dsm_capacity_rule_exo + ) def _old_dsm_capacity_rule(block): """Rule definition for determining (overall) old capacity @@ -1012,14 +988,12 @@ def _old_dsm_capacity_rule(block): for p in m.PERIODS: expr = ( self.old[g, p] - == self.old_end[g, p] + self.old_exo[g, p]) + == self.old_end[g, p] + self.old_exo[g, p] + ) self.old_dsm_rule.add((g, p), expr) - self.old_dsm_rule = Constraint(group, - m.PERIODS, - noruleinit=True) - self.old_dsm_rule_build = BuildAction( - rule=_old_dsm_capacity_rule) + self.old_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_build = BuildAction(rule=_old_dsm_capacity_rule) def _shift_shed_vars_rule(block): """Force shifting resp. shedding variables to zero dependent @@ -1055,9 +1029,12 @@ def _input_output_relation_rule(block): lhs = m.flow[g.inflow, g, p, t] # Demand + DSM_up - DSM_down - rhs = (g.demand[t] * self.total[g, p] - + self.dsm_up[g, t] - self.dsm_do_shift[g, t] - - self.dsm_do_shed[g, t]) + rhs = ( + g.demand[t] * self.total[g, p] + + self.dsm_up[g, t] + - self.dsm_do_shift[g, t] + - self.dsm_do_shed[g, t] + ) # add constraint block.input_output_relation.add((g, p, t), (lhs == rhs)) @@ -1079,14 +1056,14 @@ def dsm_up_constraint_rule(block): # DSM up lhs = self.dsm_up[g, t] # Capacity dsm_up - rhs = (g.capacity_up[t] * self.total[g, p] - * g.flex_share_up) + rhs = g.capacity_up[t] * self.total[g, p] * g.flex_share_up # add constraint block.dsm_up_constraint.add((g, p, t), (lhs <= rhs)) - self.dsm_up_constraint = Constraint(group, m.TIMEINDEX, - noruleinit=True) + self.dsm_up_constraint = Constraint( + group, m.TIMEINDEX, noruleinit=True + ) self.dsm_up_constraint_build = BuildAction(rule=dsm_up_constraint_rule) # Upper bounds relation @@ -1099,16 +1076,21 @@ def dsm_down_constraint_rule(block): # DSM down lhs = self.dsm_do_shift[g, t] + self.dsm_do_shed[g, t] # Capacity dsm_down - rhs = (g.capacity_down[t] * self.total[g, p] - * g.flex_share_down) + rhs = ( + g.capacity_down[t] + * self.total[g, p] + * g.flex_share_down + ) # add constraint block.dsm_down_constraint.add((g, p, t), (lhs <= rhs)) - self.dsm_down_constraint = Constraint(group, m.TIMEINDEX, - noruleinit=True) + self.dsm_down_constraint = Constraint( + group, m.TIMEINDEX, noruleinit=True + ) self.dsm_down_constraint_build = BuildAction( - rule=dsm_down_constraint_rule) + rule=dsm_down_constraint_rule + ) def dsm_sum_constraint_rule(block): """Relation to compensate the total amount of positive @@ -1162,8 +1144,8 @@ def _objective_expression(self): for p in m.PERIODS: if g.investment.ep_costs is not None: investment_costs += ( - self.invest[g, p] - * g.investment.ep_costs[p]) + self.invest[g, p] * g.investment.ep_costs[p] + ) else: raise ValueError("Missing value for investment costs!") @@ -1179,33 +1161,39 @@ def _objective_expression(self): ) * m.objective_weighting[t] else: - msg = ("You did not specify an interest rate.\n" - "It will be set equal to the discount_rate of {} " - "of the model as a default.\nThis corresponds to a " - "social planner point of view and does not reflect " - "microeconomic interest requirements.") + msg = ( + "You did not specify an interest rate.\n" + "It will be set equal to the discount_rate of {} " + "of the model as a default.\nThis corresponds to a " + "social planner point of view and does not reflect " + "microeconomic interest requirements." + ) for g in self.investdsm: if g.investment.ep_costs is not None: lifetime = g.investment.lifetime interest = g.investment.interest_rate if interest == 0: - warn(msg.format(m.discount_rate), - debugging.SuspiciousUsageWarning) + warn( + msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning, + ) interest = m.discount_rate for p in m.PERIODS: annuity = economics.annuity( capex=g.investment.ep_costs[p], n=lifetime, - wacc=interest + wacc=interest, ) investment_costs_increment = ( - self.invest[g, p] * annuity * lifetime + self.invest[g, p] + * annuity + * lifetime * ((1 + m.discount_rate) ** (-p)) ) investment_costs += investment_costs_increment - period_investment_costs[p] += ( - investment_costs_increment - ) + period_investment_costs[ + p + ] += investment_costs_increment else: raise ValueError("Missing value for investment costs!") @@ -1213,14 +1201,14 @@ def _objective_expression(self): variable_costs += ( self.dsm_up[g, t] * m.objective_weighting[t] - * g.cost_dsm_up[p] + * g.cost_dsm_up[t] * ((1 + m.discount_rate) ** -p) ) variable_costs += ( - (self.dsm_do_shift[g, t] - * g.cost_dsm_down_shift[p] - + self.dsm_do_shed[g, t] - * g.cost_dsm_down_shed[p]) + ( + self.dsm_do_shift[g, t] * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) * m.objective_weighting[t] * ((1 + m.discount_rate) ** -p) ) @@ -1228,15 +1216,13 @@ def _objective_expression(self): if g.investment.fixed_costs[0] is not None: lifetime = g.investment.lifetime for p in m.PERIODS: - fixed_costs += ( - sum(self.invest[g, p] - * max(g.demand) - * g.investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range(p, p + lifetime) - ) - * ((1 + m.discount_rate) ** (-p)) - ) + fixed_costs += sum( + self.invest[g, p] + * max(g.demand) + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(p, p + lifetime) + ) * ((1 + m.discount_rate) ** (-p)) self.investment_costs = investment_costs self.period_investment_costs = period_investment_costs @@ -1474,10 +1460,12 @@ def _input_output_relation_rule(block): + self.dsm_up[g, t] - sum( self.dsm_do_shift[g, tt, t] - for tt in range(t - g.delay_time, - t + g.delay_time + 1) + for tt in range( + t - g.delay_time, t + g.delay_time + 1 + ) ) - - self.dsm_do_shed[g, t]) + - self.dsm_do_shed[g, t] + ) # add constraint block.input_output_relation.add( @@ -1495,10 +1483,12 @@ def _input_output_relation_rule(block): + self.dsm_up[g, t] - sum( self.dsm_do_shift[g, tt, t] - for tt in range(t - g.delay_time, - m.TIMESTEPS[-1] + 1) + for tt in range( + t - g.delay_time, m.TIMESTEPS[-1] + 1 + ) ) - - self.dsm_do_shed[g, t]) + - self.dsm_do_shed[g, t] + ) # add constraint block.input_output_relation.add( @@ -1897,15 +1887,18 @@ def _objective_expression(self): variable_costs += ( self.dsm_up[g, t] * m.objective_weighting[t] - * g.cost_dsm_up[p] + * g.cost_dsm_up[t] * ((1 + m.discount_rate) ** -p) ) variable_costs += ( - (sum(self.dsm_do_shift[g, tt, t] - for tt in m.TIMESTEPS) - * g.cost_dsm_down_shift[p] - + self.dsm_do_shed[g, t] - * g.cost_dsm_down_shed[p]) + ( + sum( + self.dsm_do_shift[g, tt, t] + for tt in m.TIMESTEPS + ) + * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) * m.objective_weighting[t] * ((1 + m.discount_rate) ** -p) ) @@ -1994,7 +1987,7 @@ class SinkDSMDIWInvestmentBlock(SimpleBlock): .. math:: DSM_{t}^{up} \cdot cost_{t}^{dsm, up} - + \sum_{tt=0}^{T} DSM_{t, tt}^{do, shift} \cdot + + \sum_{tt=0}^{T} DSM_{tt, t}^{do, shift} \cdot cost_{t}^{dsm, do, shift} + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed} \quad \forall t \in \mathbb{T} @@ -2058,15 +2051,29 @@ def _dsm_investvar_bound_rule(block, g): """Rule definition to bound the demand response capacity invested in (`invest`). """ - return g.investment.minimum, g.investment.maximum + return g.investment.minimum[p], g.investment.maximum[p] # Investment in DR capacity self.invest = Var( self.investdsm, + m.PERIODS, within=NonNegativeReals, bounds=_dsm_investvar_bound_rule, ) + # Total capacity + self.total = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) + + # Old capacity to be decommissioned (due to lifetime) + # Old capacity is built out of old exogenous and endogenous capacities + self.old = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) + + # Old endogenous capacity to be decommissioned (due to lifetime) + self.old_end = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) + + # Old exogenous capacity to be decommissioned (due to lifetime) + self.old_exo = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) + # Variable load shift down self.dsm_do_shift = Var( self.investdsm, @@ -2088,6 +2095,87 @@ def _dsm_investvar_bound_rule(block, g): # ************* CONSTRAINTS ***************************** + # Handle unit lifetimes + def _total_dsm_capacity_rule(block): + """Rule definition for determining total installed + capacity (taking decommissioning into account) + """ + for g in group: + for p in m.PERIODS: + if p == 0: + expr = ( + self.total[g, p] + == self.invest[g, p] + g.investment.existing + ) + self.total_dsm_rule.add((g, p), expr) + else: + expr = ( + self.total[g, p] + == self.invest[g, p] + + self.total[g, p - 1] + - self.old[g, p] + ) + self.total_dsm_rule.add((g, p), expr) + + self.total_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) + self.total_dsm_rule_build = BuildAction(rule=_total_dsm_capacity_rule) + + def _old_dsm_capacity_rule_end(block): + """Rule definition for determining old endogenously installed + capacity to be decommissioned due to reaching its lifetime + """ + for g in group: + lifetime = g.investment.lifetime + for p in m.PERIODS: + if lifetime <= p: + expr = ( + self.old_end[g, p] == self.invest[g, p - lifetime] + ) + self.old_dsm_rule_end.add((g, p), expr) + else: + expr = self.old_end[g, p] == 0 + self.old_dsm_rule_end.add((g, p), expr) + + self.old_dsm_rule_end = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_end_build = BuildAction( + rule=_old_dsm_capacity_rule_end + ) + + def _old_dsm_capacity_rule_exo(block): + """Rule definition for determining old exogenously given capacity + to be decommissioned due to reaching its lifetime + """ + for g in group: + age = g.investment.age + lifetime = g.investment.lifetime + for p in m.PERIODS: + if lifetime - age == p: + expr = self.old_exo[g, p] == g.investment.existing + self.old_dsm_rule_exo.add((g, p), expr) + else: + expr = self.old_exo[g, p] == 0 + self.old_dsm_rule_exo.add((g, p), expr) + + self.old_dsm_rule_exo = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_exo_build = BuildAction( + rule=_old_dsm_capacity_rule_exo + ) + + def _old_dsm_capacity_rule(block): + """Rule definition for determining (overall) old capacity + to be decommissioned due to reaching its lifetime + """ + for g in group: + for p in m.PERIODS: + expr = ( + self.old[g, p] + == self.old_end[g, p] + self.old_exo[g, p] + ) + self.old_dsm_rule.add((g, p), expr) + + self.old_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_build = BuildAction(rule=_old_dsm_capacity_rule) + def _shift_shed_vars_rule(block): """Force shifting resp. shedding variables to zero dependent on how boolean parameters for shift resp. shed eligibility @@ -2117,18 +2205,17 @@ def _input_output_relation_rule(block): The actual demand after DSM. Sink Inflow == Demand +- DSM """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # first time steps: 0 + delay time if t <= g.delay_time: # Inflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand +- DSM rhs = ( - g.demand[t] - * (self.invest[g] + g.investment.existing) + g.demand[t] * self.total[g, p] + self.dsm_up[g, t] - sum( self.dsm_do_shift[g, tt, t] @@ -2138,17 +2225,18 @@ def _input_output_relation_rule(block): ) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add( + (g, p, t), (lhs == rhs) + ) # main use case elif g.delay_time < t <= m.TIMESTEPS[-1] - g.delay_time: # Inflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand +- DSM rhs = ( - g.demand[t] - * (self.invest[g] + g.investment.existing) + g.demand[t] * self.total[g, p] + self.dsm_up[g, t] - sum( self.dsm_do_shift[g, tt, t] @@ -2160,16 +2248,17 @@ def _input_output_relation_rule(block): ) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add( + (g, p, t), (lhs == rhs) + ) # last time steps: end - delay time else: # Inflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand +- DSM rhs = ( - g.demand[t] - * (self.invest[g] + g.investment.existing) + g.demand[t] * self.total[g, p] + self.dsm_up[g, t] - sum( self.dsm_do_shift[g, tt, t] @@ -2181,10 +2270,12 @@ def _input_output_relation_rule(block): ) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add( + (g, p, t), (lhs == rhs) + ) self.input_output_relation = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.input_output_relation_build = BuildAction( rule=_input_output_relation_rule @@ -2261,22 +2352,18 @@ def dsm_up_constraint_rule(block): Realised upward load shift at time t has to be smaller than upward DSM capacity at time t. """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # DSM up lhs = self.dsm_up[g, t] # Capacity dsm_up - rhs = ( - g.capacity_up[t] - * (self.invest[g] + g.investment.existing) - * g.flex_share_up - ) + rhs = g.capacity_up[t] * self.total[g, p] * g.flex_share_up # add constraint - block.dsm_up_constraint.add((g, t), (lhs <= rhs)) + block.dsm_up_constraint.add((g, p, t), (lhs <= rhs)) self.dsm_up_constraint = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.dsm_up_constraint_build = BuildAction(rule=dsm_up_constraint_rule) @@ -2286,7 +2373,7 @@ def dsm_do_constraint_rule(block): Realised downward load shift at time t has to be smaller than downward DSM capacity at time t. """ - for tt in m.TIMESTEPS: + for p, tt in m.TIMEINDEX: for g in group: # first times steps: 0 + delay @@ -2303,12 +2390,12 @@ def dsm_do_constraint_rule(block): # Capacity DSM down rhs = ( g.capacity_down[tt] - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_down ) # add constraint - block.dsm_do_constraint.add((g, tt), (lhs <= rhs)) + block.dsm_do_constraint.add((g, p, tt), (lhs <= rhs)) # main use case elif g.delay_time < tt <= m.TIMESTEPS[-1] - g.delay_time: @@ -2326,12 +2413,12 @@ def dsm_do_constraint_rule(block): # Capacity DSM down rhs = ( g.capacity_down[tt] - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_down ) # add constraint - block.dsm_do_constraint.add((g, tt), (lhs <= rhs)) + block.dsm_do_constraint.add((g, p, tt), (lhs <= rhs)) # last time steps: end - delay time else: @@ -2349,15 +2436,15 @@ def dsm_do_constraint_rule(block): # Capacity DSM down rhs = ( g.capacity_down[tt] - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_down ) # add constraint - block.dsm_do_constraint.add((g, tt), (lhs <= rhs)) + block.dsm_do_constraint.add((g, p, tt), (lhs <= rhs)) self.dsm_do_constraint = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.dsm_do_constraint_build = BuildAction(rule=dsm_do_constraint_rule) @@ -2369,7 +2456,7 @@ def c2_constraint_rule(block): total each individual DSM unit within the modeled portfolio can only be shifted up OR down at a given time. """ - for tt in m.TIMESTEPS: + for p, tt in m.TIMEINDEX: for g in group: # first times steps: 0 + delay time @@ -2390,11 +2477,11 @@ def c2_constraint_rule(block): g.capacity_up[tt] * g.flex_share_up, g.capacity_down[tt] * g.flex_share_down, ) - * (self.invest[g] + g.investment.existing) + * self.total[g, p] ) # add constraint - block.C2_constraint.add((g, tt), (lhs <= rhs)) + block.C2_constraint.add((g, p, tt), (lhs <= rhs)) elif g.delay_time < tt <= m.TIMESTEPS[-1] - g.delay_time: @@ -2415,11 +2502,11 @@ def c2_constraint_rule(block): g.capacity_up[tt] * g.flex_share_up, g.capacity_down[tt] * g.flex_share_down, ) - * (self.invest[g] + g.investment.existing) + * self.total[g, p] ) # add constraint - block.C2_constraint.add((g, tt), (lhs <= rhs)) + block.C2_constraint.add((g, p, tt), (lhs <= rhs)) else: @@ -2440,13 +2527,13 @@ def c2_constraint_rule(block): g.capacity_up[tt] * g.flex_share_up, g.capacity_down[tt] * g.flex_share_down, ) - * (self.invest[g] + g.investment.existing) + * self.total[g, p] ) # add constraint - block.C2_constraint.add((g, tt), (lhs <= rhs)) + block.C2_constraint.add((g, p, tt), (lhs <= rhs)) - self.C2_constraint = Constraint(group, m.TIMESTEPS, noruleinit=True) + self.C2_constraint = Constraint(group, m.TIMEINDEX, noruleinit=True) self.C2_constraint_build = BuildAction(rule=c2_constraint_rule) def recovery_constraint_rule(block): @@ -2456,7 +2543,7 @@ def recovery_constraint_rule(block): may take place. Rule is only applicable if a recovery time is defined. """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # No need to build constraint if no recovery @@ -2474,13 +2561,15 @@ def recovery_constraint_rule(block): # max energy shift for shifting process rhs = ( g.capacity_up[t] - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_up * g.delay_time * m.timeincrement[t] ) # add constraint - block.recovery_constraint.add((g, t), (lhs <= rhs)) + block.recovery_constraint.add( + (g, p, t), (lhs <= rhs) + ) # last time steps: end - recovery time else: @@ -2493,19 +2582,21 @@ def recovery_constraint_rule(block): # max energy shift for shifting process rhs = ( g.capacity_up[t] - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_up * g.delay_time * m.timeincrement[t] ) # add constraint - block.recovery_constraint.add((g, t), (lhs <= rhs)) + block.recovery_constraint.add( + (g, p, t), (lhs <= rhs) + ) else: pass # return(Constraint.Skip) self.recovery_constraint = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.recovery_constraint_build = BuildAction( rule=recovery_constraint_rule @@ -2518,10 +2609,9 @@ def shed_limit_constraint_rule(block): shedding is introduced in order to limit the overall amount of shedded energy. """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: - # Only applicable for load shedding if g.shed_eligibility: # main use case @@ -2535,14 +2625,14 @@ def shed_limit_constraint_rule(block): # max energy shift for shifting process rhs = ( g.capacity_down[t] - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_down * g.shed_time * m.timeincrement[t] ) # add constraint block.shed_limit_constraint.add( - (g, t), (lhs <= rhs) + (g, p, t), (lhs <= rhs) ) # last time steps: end - recovery time @@ -2556,21 +2646,21 @@ def shed_limit_constraint_rule(block): # max energy shift for shifting process rhs = ( g.capacity_down[t] - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_down * g.shed_time * m.timeincrement[t] ) # add constraint block.shed_limit_constraint.add( - (g, t), (lhs <= rhs) + (g, p, t), (lhs <= rhs) ) else: pass # return(Constraint.Skip) self.shed_limit_constraint = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.shed_limit_constraint_build = BuildAction( rule=shed_limit_constraint_rule @@ -2582,47 +2672,125 @@ def _objective_expression(self): m = self.parent_block() investment_costs = 0 + period_investment_costs = {p: 0 for p in m.PERIODS} variable_costs = 0 + fixed_costs = 0 - for g in self.investdsm: - if g.investment.ep_costs is not None: - investment_costs += self.invest[g] * g.investment.ep_costs - else: - raise ValueError("Missing value for investment costs!") - - for t in m.TIMESTEPS: - variable_costs += ( - self.dsm_up[g, t] - * g.cost_dsm_up[t] - * m.objective_weighting[t] - ) - variable_costs += ( - sum(self.dsm_do_shift[g, tt, t] for tt in m.TIMESTEPS) - * g.cost_dsm_down_shift[t] - + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] - ) * m.objective_weighting[t] - - self.cost = Expression(expr=investment_costs + variable_costs) - - return self.cost - - -class SinkDSMDLRBlock(SimpleBlock): - r"""Constraints for SinkDSM with "DLR" approach - - **The following constraints are created for approach = 'DLR':** + if not m.es.multi_period: + for g in self.investdsm: + for p in m.PERIODS: + if g.investment.ep_costs is not None: + investment_costs += ( + self.invest[g, p] * g.investment.ep_costs[p] + ) + else: + raise ValueError("Missing value for investment costs!") - .. _SinkDSMDLR equations: + for t in m.TIMESTEPS: + variable_costs += ( + self.dsm_up[g, t] + * g.cost_dsm_up[t] + * m.objective_weighting[t] + ) + variable_costs += ( + sum(self.dsm_do_shift[g, tt, t] for tt in m.TIMESTEPS) + * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) * m.objective_weighting[t] - .. math:: - & - (1) \quad DSM_{h, t}^{up} = 0 \quad \forall h \in H_{DR} - \forall t \in \mathbb{T} - \quad if \space eligibility_{shift} = False \\ - & - (2) \quad DSM_{t}^{do, shed} = 0 \quad \forall t \in \mathbb{T} - \quad if \space eligibility_{shed} = False \\ - & + else: + msg = ( + "You did not specify an interest rate.\n" + "It will be set equal to the discount_rate of {} " + "of the model as a default.\nThis corresponds to a " + "social planner point of view and does not reflect " + "microeconomic interest requirements." + ) + for g in self.investdsm: + if g.investment.ep_costs is not None: + lifetime = g.investment.lifetime + interest = g.investment.interest_rate + if interest == 0: + warn( + msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning, + ) + interest = m.discount_rate + for p in m.PERIODS: + annuity = economics.annuity( + capex=g.investment.ep_costs[p], + n=lifetime, + wacc=interest, + ) + investment_costs_increment = ( + self.invest[g, p] + * annuity + * lifetime + * ((1 + m.discount_rate) ** (-p)) + ) + investment_costs += investment_costs_increment + period_investment_costs[ + p + ] += investment_costs_increment + else: + raise ValueError("Missing value for investment costs!") + + for p, t in m.TIMEINDEX: + variable_costs += ( + self.dsm_up[g, t] + * m.objective_weighting[t] + * g.cost_dsm_up[t] + * ((1 + m.discount_rate) ** -p) + ) + variable_costs += ( + ( + sum( + self.dsm_do_shift[g, tt, t] + for tt in m.TIMESTEPS + ) + * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + ) + + if g.investment.fixed_costs[0] is not None: + lifetime = g.investment.lifetime + for p in m.PERIODS: + fixed_costs += sum( + self.invest[g, p] + * max(g.demand) + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(p, p + lifetime) + ) * ((1 + m.discount_rate) ** (-p)) + + self.investment_costs = investment_costs + self.period_investment_costs = period_investment_costs + self.costs = Expression( + expr=investment_costs + fixed_costs + variable_costs + ) + + return self.costs + + +class SinkDSMDLRBlock(SimpleBlock): + r"""Constraints for SinkDSM with "DLR" approach + + **The following constraints are created for approach = 'DLR':** + + .. _SinkDSMDLR equations: + + .. math:: + & + (1) \quad DSM_{h, t}^{up} = 0 \quad \forall h \in H_{DR} + \forall t \in \mathbb{T} + \quad if \space eligibility_{shift} = False \\ + & + (2) \quad DSM_{t}^{do, shed} = 0 \quad \forall t \in \mathbb{T} + \quad if \space eligibility_{shed} = False \\ + & (3) \quad \dot{E}_{t} = demand_{t} \cdot demand_{max} + \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo} - DSM_{h, t}^{do, shift} @@ -2925,10 +3093,10 @@ def _input_output_relation_rule(block): The actual demand after DR. BusBlock outflow == Demand +- DR (i.e. effective Sink consumption) """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # outflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand +- DR rhs = ( @@ -2944,10 +3112,10 @@ def _input_output_relation_rule(block): ) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add((g, p, t), (lhs == rhs)) self.input_output_relation = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.input_output_relation_build = BuildAction( rule=_input_output_relation_rule @@ -3306,24 +3474,31 @@ def dr_yearly_limit_shed_rule(block): for g in group: if g.shed_eligibility: - # sum of all load reductions - lhs = sum(self.dsm_do_shed[g, t] for t in m.TIMESTEPS) + for p in m.PERIODS: + # sum of all load reductions + lhs = sum( + self.dsm_do_shed[g, t] + for pp, t in m.TIMEINDEX + if pp == p + ) - # year limit - rhs = ( - g.capacity_down_mean - * g.max_capacity_down - * g.shed_time - * g.n_yearLimit_shed - ) + # year limit + rhs = ( + g.capacity_down_mean + * g.max_capacity_down + * g.shed_time + * g.n_yearLimit_shed + ) - # add constraint - block.dr_yearly_limit_shed.add(g, (lhs <= rhs)) + # add constraint + block.dr_yearly_limit_shed.add((g, p), (lhs <= rhs)) else: pass # return(Constraint.Skip) - self.dr_yearly_limit_shed = Constraint(group, noruleinit=True) + self.dr_yearly_limit_shed = Constraint( + group, m.PERIODS, noruleinit=True + ) self.dr_yearly_limit_shed_build = BuildAction( rule=dr_yearly_limit_shed_rule ) @@ -3338,27 +3513,34 @@ def dr_yearly_limit_red_rule(block): for g in group: if g.ActivateYearLimit: - # sum of all load reductions - lhs = sum( - sum(self.dsm_do_shift[g, h, t] for h in g.delay_time) - for t in m.TIMESTEPS - ) + for p in m.PERIODS: + # sum of all load reductions + lhs = sum( + sum( + self.dsm_do_shift[g, h, t] + for h in g.delay_time + ) + for pp, t in m.TIMEINDEX + if pp == p + ) - # year limit - rhs = ( - g.capacity_down_mean - * g.max_capacity_down - * g.shift_time - * g.n_yearLimit_shift - ) + # year limit + rhs = ( + g.capacity_down_mean + * g.max_capacity_down + * g.shift_time + * g.n_yearLimit_shift + ) - # add constraint - block.dr_yearly_limit_red.add(g, (lhs <= rhs)) + # add constraint + block.dr_yearly_limit_red.add((g, p), (lhs <= rhs)) else: pass # return(Constraint.Skip) - self.dr_yearly_limit_red = Constraint(group, noruleinit=True) + self.dr_yearly_limit_red = Constraint( + group, m.PERIODS, noruleinit=True + ) self.dr_yearly_limit_red_build = BuildAction( rule=dr_yearly_limit_red_rule ) @@ -3371,34 +3553,38 @@ def dr_yearly_limit_inc_rule(block): for g in group: if g.ActivateYearLimit: - # sum of all load increases - lhs = sum( - sum(self.dsm_up[g, h, t] for h in g.delay_time) - for t in m.TIMESTEPS - ) + for p in m.PERIODS: + # sum of all load increases + lhs = sum( + sum(self.dsm_up[g, h, t] for h in g.delay_time) + for pp, t in m.TIMEINDEX + if pp == p + ) - # year limit - rhs = ( - g.capacity_up_mean - * g.max_capacity_up - * g.shift_time - * g.n_yearLimit_shift - ) + # year limit + rhs = ( + g.capacity_up_mean + * g.max_capacity_up + * g.shift_time + * g.n_yearLimit_shift + ) - # add constraint - block.dr_yearly_limit_inc.add(g, (lhs <= rhs)) + # add constraint + block.dr_yearly_limit_inc.add((g, p), (lhs <= rhs)) else: pass # return(Constraint.Skip) - self.dr_yearly_limit_inc = Constraint(group, noruleinit=True) + self.dr_yearly_limit_inc = Constraint( + group, m.PERIODS, noruleinit=True + ) self.dr_yearly_limit_inc_build = BuildAction( rule=dr_yearly_limit_inc_rule ) # Equation 4.19 def dr_daily_limit_red_rule(block): - """ "Introduce rolling (energy) limit for load reductions + """Introduce rolling (energy) limit for load reductions This effectively limits DR utilization dependent on activations within previous hours. """ @@ -3545,31 +3731,71 @@ def _objective_expression(self): """ m = self.parent_block() - dr_cost = 0 + variable_costs = 0 + fixed_costs = 0 + + if not m.es.multi_period: + for t in m.TIMESTEPS: + for g in self.DR: + variable_costs += ( + sum( + self.dsm_up[g, h, t] + self.balance_dsm_do[g, h, t] + for h in g.delay_time + ) + * g.cost_dsm_up[t] + * m.objective_weighting[t] + ) + variable_costs += ( + sum( + self.dsm_do_shift[g, h, t] + + self.balance_dsm_up[g, h, t] + for h in g.delay_time + ) + * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) * m.objective_weighting[t] - for t in m.TIMESTEPS: + else: for g in self.DR: - dr_cost += ( - sum( - self.dsm_up[g, h, t] + self.balance_dsm_do[g, h, t] - for h in g.delay_time + for p, t in m.TIMEINDEX: + variable_costs += ( + ( + sum( + self.dsm_up[g, h, t] + + self.balance_dsm_do[g, h, t] + for h in g.delay_time + ) + * g.cost_dsm_up[t] + ) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) ) - * g.cost_dsm_up[t] - * m.objective_weighting[t] - ) - dr_cost += ( - sum( - self.dsm_do_shift[g, h, t] - + self.balance_dsm_up[g, h, t] - for h in g.delay_time + variable_costs += ( + ( + sum( + self.dsm_do_shift[g, h, t] + + self.balance_dsm_up[g, h, t] + for h in g.delay_time + ) + * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) ) - * g.cost_dsm_down_shift[t] - + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] - ) * m.objective_weighting[t] - self.cost = Expression(expr=dr_cost) + if g.fixed_costs[0] is not None: + for p in m.PERIODS: + fixed_costs += ( + g.max_demand + * max(g.demand) + * g.fixed_costs[p] + * ((1 + m.discount_rate) ** (-p)) + ) + + self.costs = Expression(expr=variable_costs + fixed_costs) - return self.cost + return self.costs class SinkDSMDLRInvestmentBlock(SinkDSMDLRBlock): @@ -3791,19 +4017,32 @@ def _create(self, group=None): # ************* VARIABLES ***************************** # Define bounds for investments in demand response - def _dr_investvar_bound_rule(block, g): + def _dr_investvar_bound_rule(block, g, p): """Rule definition to bound the invested demand response capacity `invest`. """ - return g.investment.minimum, g.investment.maximum + return g.investment.minimum[p], g.investment.maximum[p] # Investment in DR capacity self.invest = Var( self.INVESTDR, + m.PERIODS, within=NonNegativeReals, bounds=_dr_investvar_bound_rule, ) + # Total capacity + self.total = Var(self.INVESTDR, m.PERIODS, within=NonNegativeReals) + + # Old capacity to be decommissioned (due to lifetime) + self.old = Var(self.INVESTDR, m.PERIODS, within=NonNegativeReals) + + # Old endogenous capacity to be decommissioned (due to lifetime) + self.old_end = Var(self.INVESTDR, m.PERIODS, within=NonNegativeReals) + + # Old exogenous capacity to be decommissioned (due to lifetime) + self.old_exo = Var(self.INVESTDR, m.PERIODS, within=NonNegativeReals) + # Variable load shift down (capacity) self.dsm_do_shift = Var( self.INVESTDR_H, m.TIMESTEPS, initialize=0, within=NonNegativeReals @@ -3841,6 +4080,87 @@ def _dr_investvar_bound_rule(block, g): # ************* CONSTRAINTS ***************************** + # Handle unit lifetimes + def _total_capacity_rule(block): + """Rule definition for determining total installed + capacity (taking decommissioning into account) + """ + for g in group: + for p in m.PERIODS: + if p == 0: + expr = ( + self.total[g, p] + == self.invest[g, p] + g.investment.existing + ) + self.total_dsm_rule.add((g, p), expr) + else: + expr = ( + self.total[g, p] + == self.invest[g, p] + + self.total[g, p - 1] + - self.old[g, p] + ) + self.total_dsm_rule.add((g, p), expr) + + self.total_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) + self.total_dsm_rule_build = BuildAction(rule=_total_capacity_rule) + + def _old_dsm_capacity_rule_end(block): + """Rule definition for determining old endogenously installed + capacity to be decommissioned due to reaching its lifetime + """ + for g in group: + lifetime = g.investment.lifetime + for p in m.PERIODS: + if lifetime <= p: + expr = ( + self.old_end[g, p] == self.invest[g, p - lifetime] + ) + self.old_dsm_rule_end.add((g, p), expr) + else: + expr = self.old_end[g, p] == 0 + self.old_dsm_rule_end.add((g, p), expr) + + self.old_dsm_rule_end = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_end_build = BuildAction( + rule=_old_dsm_capacity_rule_end + ) + + def _old_dsm_capacity_rule_exo(block): + """Rule definition for determining old exogenously given capacity + to be decommissioned due to reaching its lifetime + """ + for g in group: + age = g.investment.age + lifetime = g.investment.lifetime + for p in m.PERIODS: + if lifetime - age == p: + expr = self.old_exo[g, p] == g.investment.existing + self.old_dsm_rule_exo.add((g, p), expr) + else: + expr = self.old_exo[g, p] == 0 + self.old_dsm_rule_exo.add((g, p), expr) + + self.old_dsm_rule_exo = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_exo_build = BuildAction( + rule=_old_dsm_capacity_rule_exo + ) + + def _old_dsm_capacity_rule(block): + """Rule definition for determining (overall) old capacity + to be decommissioned due to reaching its lifetime + """ + for g in group: + for p in m.PERIODS: + expr = ( + self.old[g, p] + == self.old_end[g, p] + self.old_exo[g, p] + ) + self.old_dsm_rule.add((g, p), expr) + + self.old_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_build = BuildAction(rule=_old_dsm_capacity_rule) + def _shift_shed_vars_rule(block): """Force shifting resp. shedding variables to zero dependent on how boolean parameters for shift resp. shed eligibility @@ -3873,15 +4193,15 @@ def _input_output_relation_rule(block): The actual demand after DR. BusBlock outflow == Demand +- DR (i.e. effective Sink consumption) """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # outflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand +- DR rhs = ( - g.demand[t] * (self.invest[g] + g.investment.existing) + g.demand[t] * self.total[g, p] + sum( self.dsm_up[g, h, t] + self.balance_dsm_do[g, h, t] @@ -3893,10 +4213,10 @@ def _input_output_relation_rule(block): ) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add((g, p, t), (lhs == rhs)) self.input_output_relation = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.input_output_relation_build = BuildAction( rule=_input_output_relation_rule @@ -4064,7 +4384,7 @@ def availability_red_rule(block): """Load reduction must be smaller than or equal to the (time-dependent) capacity limit """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # load reduction lhs = ( @@ -4079,14 +4399,14 @@ def availability_red_rule(block): # upper bound rhs = ( g.capacity_down[t] - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_down ) # add constraint - block.availability_red.add((g, t), (lhs <= rhs)) + block.availability_red.add((g, p, t), (lhs <= rhs)) - self.availability_red = Constraint(group, m.TIMESTEPS, noruleinit=True) + self.availability_red = Constraint(group, m.TIMEINDEX, noruleinit=True) self.availability_red_build = BuildAction(rule=availability_red_rule) # Equation 4.12 @@ -4094,7 +4414,7 @@ def availability_inc_rule(block): """Load increase must be smaller than or equal to the (time-dependent) capacity limit """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # load increase lhs = sum( @@ -4103,16 +4423,12 @@ def availability_inc_rule(block): ) # upper bound - rhs = ( - g.capacity_up[t] - * (self.invest[g] + g.investment.existing) - * g.flex_share_up - ) + rhs = g.capacity_up[t] * self.total[g, p] * g.flex_share_up # add constraint - block.availability_inc.add((g, t), (lhs <= rhs)) + block.availability_inc.add((g, p, t), (lhs <= rhs)) - self.availability_inc = Constraint(group, m.TIMESTEPS, noruleinit=True) + self.availability_inc = Constraint(group, m.TIMEINDEX, noruleinit=True) self.availability_inc_build = BuildAction(rule=availability_inc_rule) # Equation 4.13 @@ -4198,7 +4514,7 @@ def dr_storage_limit_red_rule(block): """ Fictious demand response storage level for load reduction limit """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: if g.shift_eligibility: @@ -4208,13 +4524,13 @@ def dr_storage_limit_red_rule(block): # maximum (time-dependent) available shifting capacity rhs = ( g.capacity_down_mean - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_down * g.shift_time ) # add constraint - block.dr_storage_limit_red.add((g, t), (lhs <= rhs)) + block.dr_storage_limit_red.add((g, p, t), (lhs <= rhs)) else: lhs = self.dsm_do_level[g, t] @@ -4222,10 +4538,10 @@ def dr_storage_limit_red_rule(block): rhs = 0 # add constraint - block.dr_storage_limit_red.add((g, t), (lhs <= rhs)) + block.dr_storage_limit_red.add((g, p, t), (lhs <= rhs)) self.dr_storage_limit_red = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.dr_storage_level_red_build = BuildAction( rule=dr_storage_limit_red_rule @@ -4233,10 +4549,8 @@ def dr_storage_limit_red_rule(block): # Equation 4.16 def dr_storage_limit_inc_rule(block): - """ - Fictious demand response storage level for load increase limit - """ - for t in m.TIMESTEPS: + """Fictious demand response storage level for load increase limit""" + for p, t in m.TIMEINDEX: for g in group: # fictious demand response load reduction storage level lhs = self.dsm_up_level[g, t] @@ -4244,16 +4558,16 @@ def dr_storage_limit_inc_rule(block): # maximum (time-dependent) available shifting capacity rhs = ( g.capacity_up_mean - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_up * g.shift_time ) # add constraint - block.dr_storage_limit_inc.add((g, t), (lhs <= rhs)) + block.dr_storage_limit_inc.add((g, p, t), (lhs <= rhs)) self.dr_storage_limit_inc = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.dr_storage_level_inc_build = BuildAction( rule=dr_storage_limit_inc_rule @@ -4268,23 +4582,26 @@ def dr_yearly_limit_shed_rule(block): to the others. """ for g in group: - if g.shed_eligibility: - # sum of all load reductions - lhs = sum(self.dsm_do_shed[g, t] for t in m.TIMESTEPS) + for p in m.PERIODS: + if g.shed_eligibility: + # sum of all load reductions + lhs = sum(self.dsm_do_shed[g, t] for t in m.TIMESTEPS) - # year limit - rhs = ( - g.capacity_down_mean - * (self.invest[g] + g.investment.existing) - * g.flex_share_down - * g.shed_time - * g.n_yearLimit_shed - ) + # year limit + rhs = ( + g.capacity_down_mean + * self.total[g, p] + * g.flex_share_down + * g.shed_time + * g.n_yearLimit_shed + ) - # add constraint - block.dr_yearly_limit_shed.add(g, (lhs <= rhs)) + # add constraint + block.dr_yearly_limit_shed.add((g, p), (lhs <= rhs)) - self.dr_yearly_limit_shed = Constraint(group, noruleinit=True) + self.dr_yearly_limit_shed = Constraint( + group, m.PERIODS, noruleinit=True + ) self.dr_yearly_limit_shed_build = BuildAction( rule=dr_yearly_limit_shed_rule ) @@ -4299,28 +4616,35 @@ def dr_yearly_limit_red_rule(block): for g in group: if g.ActivateYearLimit: - # sum of all load reductions - lhs = sum( - sum(self.dsm_do_shift[g, h, t] for h in g.delay_time) - for t in m.TIMESTEPS - ) + for p in m.PERIODS: + # sum of all load reductions + lhs = sum( + sum( + self.dsm_do_shift[g, h, t] + for h in g.delay_time + ) + for pp, t in m.TIMEINDEX + if pp == p + ) - # year limit - rhs = ( - g.capacity_down_mean - * (self.invest[g] + g.investment.existing) - * g.flex_share_down - * g.shift_time - * g.n_yearLimit_shift - ) + # year limit + rhs = ( + g.capacity_down_mean + * self.total[g, p] + * g.flex_share_down + * g.shift_time + * g.n_yearLimit_shift + ) - # add constraint - block.dr_yearly_limit_red.add(g, (lhs <= rhs)) + # add constraint + block.dr_yearly_limit_red.add((g, p), (lhs <= rhs)) else: pass # return(Constraint.Skip) - self.dr_yearly_limit_red = Constraint(group, noruleinit=True) + self.dr_yearly_limit_red = Constraint( + group, m.PERIODS, noruleinit=True + ) self.dr_yearly_limit_red_build = BuildAction( rule=dr_yearly_limit_red_rule ) @@ -4333,28 +4657,32 @@ def dr_yearly_limit_inc_rule(block): for g in group: if g.ActivateYearLimit: - # sum of all load increases - lhs = sum( - sum(self.dsm_up[g, h, t] for h in g.delay_time) - for t in m.TIMESTEPS - ) + for p in m.PERIODS: + # sum of all load increases + lhs = sum( + sum(self.dsm_up[g, h, t] for h in g.delay_time) + for pp, t in m.TIMEINDEX + if pp == p + ) - # year limit - rhs = ( - g.capacity_up_mean - * (self.invest[g] + g.investment.existing) - * g.flex_share_up - * g.shift_time - * g.n_yearLimit_shift - ) + # year limit + rhs = ( + g.capacity_up_mean + * self.total[g, p] + * g.flex_share_up + * g.shift_time + * g.n_yearLimit_shift + ) - # add constraint - block.dr_yearly_limit_inc.add(g, (lhs <= rhs)) + # add constraint + block.dr_yearly_limit_inc.add((g, p), (lhs <= rhs)) else: pass # return(Constraint.Skip) - self.dr_yearly_limit_inc = Constraint(group, noruleinit=True) + self.dr_yearly_limit_inc = Constraint( + group, m.PERIODS, noruleinit=True + ) self.dr_yearly_limit_inc_build = BuildAction( rule=dr_yearly_limit_inc_rule ) @@ -4365,7 +4693,7 @@ def dr_daily_limit_red_rule(block): This effectively limits DR utilization dependent on activations within previous hours. """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: if g.ActivateDayLimit: @@ -4380,9 +4708,9 @@ def dr_daily_limit_red_rule(block): ) # daily limit - rhs = g.capacity_down_mean * ( - self.invest[g] + g.investment.existing - ) * g.flex_share_down * g.shift_time - sum( + rhs = g.capacity_down_mean * self.total[ + g, p + ] * g.flex_share_down * g.shift_time - sum( sum( self.dsm_do_shift[g, h, t - t_dash] for h in g.delay_time @@ -4391,7 +4719,9 @@ def dr_daily_limit_red_rule(block): ) # add constraint - block.dr_daily_limit_red.add((g, t), (lhs <= rhs)) + block.dr_daily_limit_red.add( + (g, p, t), (lhs <= rhs) + ) else: pass # return(Constraint.Skip) @@ -4400,7 +4730,7 @@ def dr_daily_limit_red_rule(block): pass # return(Constraint.Skip) self.dr_daily_limit_red = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.dr_daily_limit_red_build = BuildAction( rule=dr_daily_limit_red_rule @@ -4412,7 +4742,7 @@ def dr_daily_limit_inc_rule(block): This effectively limits DR utilization dependent on activations within previous hours. """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: if g.ActivateDayLimit: @@ -4426,9 +4756,9 @@ def dr_daily_limit_inc_rule(block): ) # daily limit - rhs = g.capacity_up_mean * ( - self.invest[g] + g.investment.existing - ) * g.flex_share_up * g.shift_time - sum( + rhs = g.capacity_up_mean * self.total[ + g, p + ] * g.flex_share_up * g.shift_time - sum( sum( self.dsm_up[g, h, t - t_dash] for h in g.delay_time @@ -4437,7 +4767,9 @@ def dr_daily_limit_inc_rule(block): ) # add constraint - block.dr_daily_limit_inc.add((g, t), (lhs <= rhs)) + block.dr_daily_limit_inc.add( + (g, p, t), (lhs <= rhs) + ) else: pass # return(Constraint.Skip) @@ -4446,7 +4778,7 @@ def dr_daily_limit_inc_rule(block): pass # return(Constraint.Skip) self.dr_daily_limit_inc = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.dr_daily_limit_inc_build = BuildAction( rule=dr_daily_limit_inc_rule @@ -4458,7 +4790,7 @@ def dr_logical_constraint_rule(block): The sum of upwards and downwards shifts may not be greater than the (bigger) capacity limit. """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: if g.addition: @@ -4481,17 +4813,19 @@ def dr_logical_constraint_rule(block): g.capacity_down[t] * g.flex_share_down, g.capacity_up[t] * g.flex_share_up, ) - * (self.invest[g] + g.investment.existing) + * self.total[g, p] ) # add constraint - block.dr_logical_constraint.add((g, t), (lhs <= rhs)) + block.dr_logical_constraint.add( + (g, p, t), (lhs <= rhs) + ) else: pass # return(Constraint.Skip) self.dr_logical_constraint = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.dr_logical_constraint_build = BuildAction( rule=dr_logical_constraint_rule @@ -4504,32 +4838,118 @@ def _objective_expression(self): m = self.parent_block() investment_costs = 0 + period_investment_costs = {p: 0 for p in m.PERIODS} variable_costs = 0 + fixed_costs = 0 - for g in self.INVESTDR: - if g.investment.ep_costs is not None: - investment_costs += self.invest[g] * g.investment.ep_costs - else: - raise ValueError("Missing value for investment costs!") - for t in m.TIMESTEPS: - variable_costs += ( - sum( - self.dsm_up[g, h, t] + self.balance_dsm_do[g, h, t] - for h in g.delay_time + if not m.es.multi_period: + for g in self.INVESTDR: + for p in m.PERIODS: + if g.investment.ep_costs is not None: + investment_costs += ( + self.invest[g] * g.investment.ep_costs + ) + else: + raise ValueError("Missing value for investment costs!") + + for t in m.TIMESTEPS: + variable_costs += ( + sum( + self.dsm_up[g, h, t] + self.balance_dsm_do[g, h, t] + for h in g.delay_time + ) + * g.cost_dsm_up[t] + * m.objective_weighting[t] ) - * g.cost_dsm_up[t] - * m.objective_weighting[t] - ) - variable_costs += ( - sum( - self.dsm_do_shift[g, h, t] - + self.balance_dsm_up[g, h, t] - for h in g.delay_time + variable_costs += ( + sum( + self.dsm_do_shift[g, h, t] + + self.balance_dsm_up[g, h, t] + for h in g.delay_time + ) + * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) * m.objective_weighting[t] + + else: + msg = ( + "You did not specify an interest rate.\n" + "It will be set equal to the discount_rate of {} " + "of the model as a default.\nThis corresponds to a " + "social planner point of view and does not reflect " + "microeconomic interest requirements." + ) + for g in self.INVESTDR: + if g.investment.ep_costs is not None: + lifetime = g.investment.lifetime + interest = g.investment.interest_rate + if interest == 0: + warn( + msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning, + ) + interest = m.discount_rate + for p in m.PERIODS: + annuity = economics.annuity( + capex=g.investment.ep_costs[p], + n=lifetime, + wacc=interest, + ) + investment_costs_increment = ( + self.invest[g, p] + * annuity + * lifetime + * ((1 + m.discount_rate) ** (-p)) + ) + investment_costs += investment_costs_increment + period_investment_costs[ + p + ] += investment_costs_increment + else: + raise ValueError("Missing value for investment costs!") + + for p, t in m.TIMEINDEX: + variable_costs += ( + ( + sum( + self.dsm_up[g, h, t] + + self.balance_dsm_do[g, h, t] + for h in g.delay_time + ) + * g.cost_dsm_up[t] + ) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + ) + variable_costs += ( + ( + sum( + self.dsm_do_shift[g, h, t] + + self.balance_dsm_up[g, h, t] + for h in g.delay_time + ) + * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) ) - * g.cost_dsm_down_shift[t] - + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] - ) * m.objective_weighting[t] - self.cost = Expression(expr=investment_costs + variable_costs) + if g.investment.fixed_costs[0] is not None: + lifetime = g.investment.lifetime + for p in m.PERIODS: + fixed_costs += sum( + self.invest[g, p] + * max(g.demand) + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(p, p + lifetime) + ) * ((1 + m.discount_rate) ** (-p)) - return self.cost + self.investment_costs = investment_costs + self.period_investment_costs = period_investment_costs + self.costs = Expression( + expr=investment_costs + fixed_costs + variable_costs + ) + + return self.costs From fe438ed69da378dee1d7191fe8598551c4825498 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 10 Dec 2021 09:40:33 +0100 Subject: [PATCH 0035/1363] Extend warning message --- src/oemof/solph/flows/_flow.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index dee4c5323..347616afa 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -158,8 +158,12 @@ def __init__(self, **kwargs): if "fixed_costs" in keys: msg = ("Be aware that the fixed costs attribute is only\n" "meant to be used for multi-period models.\n" + "If you wish to set up a multi-period model, set the" + " multi_period attribute of your energy system to True.\n" "It has been decided to remove the `fixed_costs` " - "attribute with v0.2 for regular uses!") + "attribute with v0.2 for regular uses.\n" + "If you specify `fixed_costs` for a regular model, " + "it will simply be ignored.") warn(msg, debugging.SuspiciousUsageWarning) if "actual_value" in keys: From 884f9c17f3895f8b800aaff9f6150d88f92488a5 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 10 Dec 2021 10:12:23 +0100 Subject: [PATCH 0036/1363] Include temporary fix for standard models TODO: Generalize periods! --- src/oemof/solph/_models.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 8bbec958e..8a06ae293 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -310,14 +310,24 @@ def _add_parent_block_sets(self): self.TIMESTEPS = po.Set(initialize=range(len(self.es.timeindex)), ordered=True) - # pyomo set for timeindex of optimization problem - self.TIMEINDEX = po.Set( - initialize=list( - zip([self.es.periods[p] for p in self.es.timeindex.year], - range(len(self.es.timeindex))) - ), - ordered=True - ) + # TODO: Generalize! + if not self.es.multi_period: + self.TIMEINDEX = po.Set( + initialize=list( + zip([0] * len(self.es.timeindex.year), + range(len(self.es.timeindex))) + ), + ordered=True + ) + else: + # pyomo set for timeindex of optimization problem + self.TIMEINDEX = po.Set( + initialize=list( + zip([self.es.periods[p] for p in self.es.timeindex.year], + range(len(self.es.timeindex))) + ), + ordered=True + ) self.PERIODS = po.Set( initialize=sorted(list(set(self.es.periods.values()))) From 59968236259d987c973c35025f9f2e24998b57b9 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 10 Dec 2021 14:53:39 +0100 Subject: [PATCH 0037/1363] Include processing.py with separation between standard and multi-period --- src/oemof/solph/processing.py | 205 ++++++++++++++++++++++++++-------- 1 file changed, 156 insertions(+), 49 deletions(-) diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index fadc2bc39..d8761e188 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -72,7 +72,7 @@ def remove_timestep(x): def get_timeindex(x): """ - Get the timeindex from oemof tuples for multiperiod models. + Get the timeindex from oemof tuples. Slice int values (timeindex, timesteps or periods) dependent on how the variable is indexed. @@ -87,7 +87,7 @@ def get_timeindex(x): def remove_timeindex(x): """ - Remove the timeindex from oemof tuples for mulitperiod models. + Remove the timeindex from oemof tuples. Slice up to integer values (node labels) The timestep is removed from tuples of type `(n, n, int, int)`, @@ -133,8 +133,7 @@ def create_dataframe(om): df["timeindex"] = df["oemof_tuple"].map(get_timeindex) df["oemof_tuple"] = df["oemof_tuple"].map(remove_timeindex) # order the data by oemof tuple and timestep - df = df.sort_values(["oemof_tuple", "timeindex"], - ascending=[True, True]) + df = df.sort_values(["oemof_tuple", "timeindex"], ascending=[True, True]) # drop empty decision variables df = df.dropna(subset=["value"]) @@ -148,56 +147,178 @@ def results(om): Results from Pyomo are written into a dictionary of pandas objects where a Series holds all scalar values and a dataframe all sequences for nodes - and flows for a standard model. For a MultiPeriodModel, the investment + and flows for a standard model. For a multi-period model, the investment values are given in a DataFrame indexed by periods. The dictionary is keyed by the nodes e.g. `results[idx]['scalars']` and flows e.g. `results[n, n]['sequences']` for a standard model. """ + # Extraction steps that are the same for both model types df = create_dataframe(om) - - period_indexed = [ - "invest", - "total", - "old", - "old_exo", - "old_end" - ] - period_timestep_indexed = ["flow"] - # TODO: Take care of initial storage content instead of just ignoring - to_be_ignored = ["init_content"] - timestep_indexed = [el for el in df["variable_name"].unique() - if el not in period_indexed - and el not in period_timestep_indexed - and el not in to_be_ignored] - scalars_col = "period_scalars" + period_indexed = ["invest", "total", "old", "old_end", "old_exo"] # create a dict of dataframes keyed by oemof tuples - # TODO / QUESTION: Could this be sped up using DFs / arrays instead?! df_dict = { - k if len(k) > 1 else (k[0], None): - v[["timeindex", "variable_name", "value"]] + k + if len(k) > 1 + else (k[0], None): v[["timeindex", "variable_name", "value"]] for k, v in df.groupby("oemof_tuple") } - # create final result dictionary by splitting up the dataframes in the - # dataframe dict into a series for scalar data and dataframe for sequences result = {} + + # Standard model results extraction + if not om.es.multi_period: + result = _extract_standard_model_result( + om, df_dict, period_indexed, result + ) + scalars_col = "scalars" + + # Results extraction for a multi-period model + else: + result = _extract_multi_period_model_result( + om, df, df_dict, period_indexed, result + ) + scalars_col = "period_scalars" + + # add dual variables for bus constraints + if om.dual is not None: + grouped = groupby( + sorted(om.BusBlock.balance.iterkeys()), lambda p: p[0] + ) + for bus, timeindex in grouped: + duals = [ + om.dual[om.BusBlock.balance[bus, p, t]] + for _, p, t in timeindex + ] + df = pd.DataFrame({"duals": duals}, index=om.es.timeindex) + if (bus, None) not in result.keys(): + result[(bus, None)] = { + "sequences": df, + scalars_col: pd.Series(dtype=float), + } + else: + result[(bus, None)]["sequences"]["duals"] = duals + + return result + + +def _extract_standard_model_result( + om, df_dict, period_indexed=None, result=None +): + """Extract and return the results of a standard model + + Parameters + ---------- + om : oemof.solph.models.Model + The optimization model + df_dict : dict + dictionary of results DataFrames + period_indexed : list + list of variables that are indexed by periods + result : dict + dictionary to store the results + """ for k in df_dict: df_dict[k].set_index("timeindex", inplace=True) df_dict[k] = df_dict[k].pivot(columns="variable_name", values="value") + try: + # Enable reindexing by replacing period by first timeindex + try: + df_dict[k].loc[ + [(0, 0)], + [ + col + for col in df_dict[k].columns + if col in period_indexed + ], + ] = ( + df_dict[k] + .loc[ + [(0,)], + [ + col + for col in df_dict[k].columns + if col in period_indexed + ], + ] + .values + ) + df_dict[k].drop(index=[(0,)], inplace=True) + except KeyError: + pass + df_dict[k].index = om.es.timeindex + except ValueError as e: + msg = ( + "\nFlowBlock: {0}-{1}. This could be caused by NaN-values in" + " your input data." + ) + raise type(e)( + str(e) + msg.format(k[0].label, k[1].label) + ).with_traceback(sys.exc_info()[2]) + try: + condition = df_dict[k].isnull().any() + scalars = df_dict[k].loc[:, condition].dropna().iloc[0] + sequences = df_dict[k].loc[:, ~condition] + result[k] = {"scalars": scalars, "sequences": sequences} + except IndexError: + error_message = ( + "Cannot access index on result data. " + + "Did the optimization terminate" + + " without errors?" + ) + raise IndexError(error_message) + + return result + + +def _extract_multi_period_model_result( + om, df, df_dict, period_indexed=None, result=None +): + """Extract and return the results of a multi-period model + + Parameters + ---------- + om : oemof.solph.models.Model + The ptimization model + df : DataFrame + DataFrame containing all results + df_dict : dict + dictionary of results DataFrames + period_indexed : list + list of variables that are indexed by periods + result : dict + dictionary to store the results + """ + period_timestep_indexed = ["flow"] + # TODO: Take care of initial storage content instead of just ignoring + to_be_ignored = ["init_content"] + timestep_indexed = [ + el + for el in df["variable_name"].unique() + if el not in period_indexed + and el not in period_timestep_indexed + and el not in to_be_ignored + ] + for k in df_dict: + df_dict[k].set_index("timeindex", inplace=True) + df_dict[k] = df_dict[k].pivot(columns="variable_name", values="value") # TODO: Revise and potentially speed up # Split data set - timeindex_cols = [col for col in df_dict[k].columns - if col in timestep_indexed or col == "flow"] - period_cols = [col for col in df_dict[k].columns - if col not in timeindex_cols] + timeindex_cols = [ + col + for col in df_dict[k].columns + if col in timestep_indexed or col == "flow" + ] + period_cols = [ + col for col in df_dict[k].columns if col not in timeindex_cols + ] sequences = df_dict[k][timeindex_cols].dropna() if sequences.empty: sequences = pd.DataFrame(index=om.es.timeindex) # periods equal to years (will probably be the standard use case) periods = sorted(list(set(om.es.timeindex.year))) - d = dict(zip([(el, ) for el in range(len(periods))], periods)) + d = dict(zip([(el,) for el in range(len(periods))], periods)) period_scalars = df_dict[k][period_cols].dropna() if period_scalars.empty: period_scalars = pd.DataFrame(index=d.values()) @@ -205,8 +326,10 @@ def results(om): sequences.index = om.es.timeindex period_scalars.rename(index=d, inplace=True) period_scalars.index.name = "period" - result[k] = {scalars_col: period_scalars, - "sequences": sequences} + result[k] = { + "period_scalars": period_scalars, + "sequences": sequences, + } except IndexError: error_message = ( "Some indices seem to be not matching.\n" @@ -214,22 +337,6 @@ def results(om): ) raise IndexError(error_message) - # add dual variables for bus constraints - if om.dual is not None: - grouped = groupby(sorted(om.BusBlock.balance.iterkeys()), - lambda p: p[0]) - for bus, timeindex in grouped: - duals = [om.dual[om.BusBlock.balance[bus, p, t]] - for _, p, t in timeindex] - df = pd.DataFrame({"duals": duals}, index=om.es.timeindex) - if (bus, None) not in result.keys(): - result[(bus, None)] = { - "sequences": df, - scalars_col: pd.Series(dtype=float), - } - else: - result[(bus, None)]["sequences"]["duals"] = duals - return result From 467b6919cc34ac58958fd03d99867aa8970c936c Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 10 Dec 2021 16:49:58 +0100 Subject: [PATCH 0038/1363] Add PEP8 fix --- src/oemof/solph/components/experimental/_sink_dsm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index a6018d98b..0208ba9c2 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -4549,7 +4549,8 @@ def dr_storage_limit_red_rule(block): # Equation 4.16 def dr_storage_limit_inc_rule(block): - """Fictious demand response storage level for load increase limit""" + """Fictious demand response storage level + for load increase limit""" for p, t in m.TIMEINDEX: for g in group: # fictious demand response load reduction storage level From 7fa552f3c74fc31fc74326c4716ccd11e5f00b0e Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 10 Dec 2021 17:23:47 +0100 Subject: [PATCH 0039/1363] Add draft for handling initial storage content --- .../solph/components/_generic_storage.py | 405 +++++++++--------- 1 file changed, 209 insertions(+), 196 deletions(-) diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index 77f9d5943..8b532465d 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -159,9 +159,7 @@ def __init__( ) self.max_storage_level = solph_sequence(max_storage_level) self.min_storage_level = solph_sequence(min_storage_level) - self.fixed_costs = solph_sequence( - kwargs.get('fixed_costs', 0) - ) + self.fixed_costs = solph_sequence(kwargs.get("fixed_costs", 0)) self.investment = kwargs.get("investment") self.invest_relation_input_output = kwargs.get( "invest_relation_input_output" @@ -256,14 +254,13 @@ def _check_invest_attributes(self): "or investment.minimum has to be non-zero." ) raise AttributeError(e3) - if ( - self.lifetime_inflow == 20 - or self.lifetime_outflow == 20 - ): - w1 = ("Using a lifetime of 20 periods," - " which is the default value.\nIf you don't consider a" - " multi-period investment model, you can safely ignore " - " this warning - all fine!") + if self.lifetime_inflow == 20 or self.lifetime_outflow == 20: + w1 = ( + "Using a lifetime of 20 periods," + " which is the default value.\nIf you don't consider a" + " multi-period investment model, you can safely ignore " + " this warning - all fine!" + ) warn(w1, debugging.SuspiciousUsageWarning) self._set_flows() @@ -464,9 +461,7 @@ def _storage_init_content_bound_rule(block, n): # ************* Constraints *************************** - reduced_periods_timesteps = [ - (p, t) for (p, t) in m.TIMEINDEX if t > 0 - ] + reduced_periods_timesteps = [(p, t) for (p, t) in m.TIMEINDEX if t > 0] # storage balance constraint (first time step) def _storage_balance_first_rule(block, n): @@ -525,8 +520,9 @@ def _storage_balance_rule(block, n, p, t): return expr == 0 self.balance = Constraint( - self.STORAGES, reduced_periods_timesteps, - rule=_storage_balance_rule + self.STORAGES, + reduced_periods_timesteps, + rule=_storage_balance_rule, ) def _balanced_storage_rule(block, n): @@ -558,14 +554,10 @@ def _power_coupled(block): self.power_coupled.add((n, p), expr) self.power_coupled = Constraint( - self.STORAGES_WITH_INVEST_FLOW_REL, - m.PERIODS, - noruleinit=True + self.STORAGES_WITH_INVEST_FLOW_REL, m.PERIODS, noruleinit=True ) - self.power_coupled_build = BuildAction( - rule=_power_coupled - ) + self.power_coupled_build = BuildAction(rule=_power_coupled) def _objective_expression(self): r""" @@ -884,21 +876,18 @@ def _create(self, group=None): self.OVERALL_MAXIMUM_INVESTSTORAGES = Set( initialize=[ - n for n in group - if n.investment.overall_maximum is not None + n for n in group if n.investment.overall_maximum is not None ] ) self.OVERALL_MINIMUM_INVESTSTORAGES = Set( initialize=[ - n for n in group - if n.investment.overall_minimum is not None + n for n in group if n.investment.overall_minimum is not None ] ) # ######################### Variables ################################ self.storage_content = Var( - self.INVESTSTORAGES, m.TIMESTEPS, - within=NonNegativeReals + self.INVESTSTORAGES, m.TIMESTEPS, within=NonNegativeReals ) def _storage_investvar_bound_rule(block, n, p): @@ -919,52 +908,38 @@ def _storage_investvar_bound_rule(block, n, p): # Total capacity self.total = Var( - self.INVESTSTORAGES, - m.PERIODS, - within=NonNegativeReals + self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals, + initialize=0 ) # Old capacity to be decommissioned (due to lifetime) # Old capacity is built out of old exogenous and endogenous capacities - self.old = Var( - self.INVESTSTORAGES, - m.PERIODS, - within=NonNegativeReals - ) + self.old = Var(self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals) # Old endogenous capacity to be decommissioned (due to lifetime) self.old_end = Var( - self.INVESTSTORAGES, - m.PERIODS, - within=NonNegativeReals + self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals ) # Old exogenous capacity to be decommissioned (due to lifetime) self.old_exo = Var( - self.INVESTSTORAGES, - m.PERIODS, - within=NonNegativeReals + self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals ) self.init_content = Var( - self.INVESTSTORAGES, - within=NonNegativeReals + self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals ) # create status variable for a non-convex investment storage self.invest_status = Var( - self.NON_CONVEX_INVESTSTORAGES, - m.PERIODS, - within=Binary + self.NON_CONVEX_INVESTSTORAGES, m.PERIODS, within=Binary ) # ######################### CONSTRAINTS ############################### i = {n: [i for i in n.inputs][0] for n in group} o = {n: [o for o in n.outputs][0] for n in group} - reduced_periods_timesteps = [ - (p, t) for (p, t) in m.TIMEINDEX if t > 0 - ] + reduced_periods_timesteps = [(p, t) for (p, t) in m.TIMEINDEX if t > 0] # Handle unit lifetimes def _total_storage_capacity_rule(block): @@ -974,21 +949,22 @@ def _total_storage_capacity_rule(block): for n in self.INVESTSTORAGES: for p in m.PERIODS: if p == 0: - expr = (self.total[n, p] - == self.invest[n, p] - + n.investment.existing) + expr = ( + self.total[n, p] + == self.invest[n, p] + n.investment.existing + ) self.total_storage_rule.add((n, p), expr) else: - expr = (self.total[n, p] - == self.invest[n, p] - + self.total[n, p - 1] - - self.old[n, p]) + expr = ( + self.total[n, p] + == self.invest[n, p] + + self.total[n, p - 1] + - self.old[n, p] + ) self.total_storage_rule.add((n, p), expr) self.total_storage_rule = Constraint( - self.INVESTSTORAGES, - m.PERIODS, - noruleinit=True + self.INVESTSTORAGES, m.PERIODS, noruleinit=True ) self.total_storage_rule_build = BuildAction( @@ -1003,18 +979,16 @@ def _old_storage_capacity_rule_end(block): lifetime = n.investment.lifetime for p in m.PERIODS: if lifetime <= p: - expr = (self.old_end[n, p] - == self.invest[n, p - lifetime]) + expr = ( + self.old_end[n, p] == self.invest[n, p - lifetime] + ) self.old_rule_end.add((n, p), expr) else: - expr = (self.old_end[n, p] - == 0) + expr = self.old_end[n, p] == 0 self.old_rule_end.add((n, p), expr) self.old_rule_end = Constraint( - self.INVESTSTORAGES, - m.PERIODS, - noruleinit=True + self.INVESTSTORAGES, m.PERIODS, noruleinit=True ) self.old_rule_end_build = BuildAction( @@ -1030,19 +1004,14 @@ def _old_storage_capacity_rule_exo(block): lifetime = n.investment.lifetime for p in m.PERIODS: if lifetime - age == p: - expr = ( - self.old_exo[n, p] - == n.investment.existing) + expr = self.old_exo[n, p] == n.investment.existing self.old_rule_exo.add((n, p), expr) else: - expr = (self.old_exo[n, p] - == 0) + expr = self.old_exo[n, p] == 0 self.old_rule_exo.add((n, p), expr) self.old_rule_exo = Constraint( - self.INVESTSTORAGES, - m.PERIODS, - noruleinit=True + self.INVESTSTORAGES, m.PERIODS, noruleinit=True ) self.old_rule_exo_build = BuildAction( @@ -1062,14 +1031,10 @@ def _old_storage_capacity_rule(block): self.old_rule.add((n, p), expr) self.old_rule = Constraint( - self.INVESTSTORAGES, - m.PERIODS, - noruleinit=True + self.INVESTSTORAGES, m.PERIODS, noruleinit=True ) - self.old_rule_build = BuildAction( - rule=_old_storage_capacity_rule - ) + self.old_rule_build = BuildAction(rule=_old_storage_capacity_rule) def _storage_level_no_investments(block): """Rule definition to force storage level to zero @@ -1078,70 +1043,104 @@ def _storage_level_no_investments(block): for n in self.INVESTSTORAGES: for p, t in m.TIMEINDEX: expr = block.storage_content[n, t] <= self.total[n, p] - self.storage_level_noinv_rule.add((n, p, t), expr) + block.storage_level_noinv_rule.add((n, p, t), expr) self.storage_level_noinv_rule = Constraint( - self.INVESTSTORAGES, - m.TIMEINDEX, - noruleinit=True + self.INVESTSTORAGES, m.TIMEINDEX, noruleinit=True ) self.storage_level_noinv_rule_build = BuildAction( rule=_storage_level_no_investments ) - # TODO: Handle initial content ... make it a sequence?! - def _inv_storage_init_content_max_rule(block, n): + def _inv_storage_init_content_max_rule(block): """Constraint for a variable initial storage capacity.""" - return ( - block.init_content[n] - <= n.investment.existing + block.invest[n] - ) + for n in self.INVESTSTORAGES_NO_INIT_CONTENT: + for p in m.PERIODS: + expr = (block.init_content[n, p] + <= block.total[n, p]) + block.init_content_limit.add((n, p), expr) self.init_content_limit = Constraint( self.INVESTSTORAGES_NO_INIT_CONTENT, - rule=_inv_storage_init_content_max_rule, + m.PERIODS, + noruleinit=True + ) + self.init_content_limit_build = BuildAction( + rule=_inv_storage_init_content_max_rule ) - def _inv_storage_init_content_fix_rule(block, n): + def _inv_storage_init_content_fix_rule(block): """Constraint for a fixed initial storage capacity.""" - return block.init_content[n] == n.initial_storage_level * ( - n.investment.existing + block.invest[n] - ) + for n in self.INVESTSTORAGES: + for p in m.PERIODS: + if p == 0: + expr = ( + block.init_content[n, p] + == n.initial_storage_level + * block.total[n, p] + ) + self.init_content_fix.add((n, p), expr) + else: + expr = ( + block.init_content[n, p] + == n.initial_storage_level + * min(0, block.total[n, p] > block.total[n, p-1]) + ) + self.init_content_fix.add((n, p), expr) self.init_content_fix = Constraint( self.INVESTSTORAGES_INIT_CONTENT, - rule=_inv_storage_init_content_fix_rule, + m. PERIODS, + noruleinit=True + ) + self.init_content_fix_build = BuildAction( + rule=_inv_storage_init_content_fix_rule ) - # TODO: Generalize! 0th step for the respective period / storage - def _storage_balance_first_rule(block, n): + # TODO: Check new init_content implementation! + def _storage_balance_first_rule(block): """ Rule definition for the storage balance of every storage n for the - first time step. + first time step of every period. """ - expr = 0 - expr += block.storage_content[n, 0] - expr += ( - -block.init_content[n] - * (1 - n.loss_rate[0]) ** m.timeincrement[0] - ) - expr += ( - n.fixed_losses_relative[0] - * (n.investment.existing + self.invest[n, 0]) - * m.timeincrement[0] - ) - expr += n.fixed_losses_absolute[0] * m.timeincrement[0] - expr += ( - -m.flow[i[n], n, 0, 0] * n.inflow_conversion_factor[0] - ) * m.timeincrement[0] - expr += ( - m.flow[n, o[n], 0, 0] / n.outflow_conversion_factor[0] - ) * m.timeincrement[0] - return expr == 0 + for n in self.INVESTSTORAGES: + for p in m.PERIODS: + first_step = m.TIMESTEPS_IN_PERIOD[p][0] + + lhs = 0 + lhs += block.storage_content[n, first_step] + lhs += ( + -block.init_content[n, p] + * (1 - n.loss_rate[first_step]) + ** m.timeincrement[first_step] + ) + lhs += ( + n.fixed_losses_relative[first_step] + * block.total[n, p] + * m.timeincrement[first_step] + ) + lhs += ( + n.fixed_losses_absolute[first_step] + * m.timeincrement[first_step] + ) + lhs += ( + -m.flow[i[n], n, p, first_step] + * n.inflow_conversion_factor[first_step] + ) * m.timeincrement[first_step] + lhs += ( + m.flow[n, o[n], p, first_step] + / n.outflow_conversion_factor[first_step] + ) * m.timeincrement[first_step] + rhs = 0 + self.balance_first.add((n, p), (lhs == rhs)) self.balance_first = Constraint( self.INVESTSTORAGES, + m.PERIODS, + noruleinit=True, + ) + self.balance_first_build = BuildAction( rule=_storage_balance_first_rule ) @@ -1173,19 +1172,47 @@ def _storage_balance_rule(block, n, p, t): self.balance = Constraint( self.INVESTSTORAGES, reduced_periods_timesteps, - rule=_storage_balance_rule + rule=_storage_balance_rule, ) - def _balanced_storage_rule(block, n): - return ( - block.storage_content[n, m.TIMESTEPS[-1]] - == block.init_content[n] + if not m.es.multi_period: + def _balanced_storage_rule(block, n): + return ( + block.storage_content[n, m.TIMESTEPS[-1]] + == block.init_content[n, 0] + ) + + self.balanced_cstr = Constraint( + self.INVESTSTORAGES_BALANCED, rule=_balanced_storage_rule ) - self.balanced_cstr = Constraint( - self.INVESTSTORAGES_BALANCED, - rule=_balanced_storage_rule - ) + else: + def _lifetime_balanced_storage_rule(block): + for n in self.INVESTSTORAGES_BALANCED: + lifetime = n.investment.lifetime + age = n.investment.age + for p in m.PERIODS: + if p == lifetime - age: + last_step = m.TIMESTEPS_IN_PERIOD[p][-1] + first_step = ( + m.TIMESTEPS_IN_PERIOD[p - lifetime + age][0] + ) + expr = ( + block.storage_content[n, last_step] + == block.storage_content[n, first_step] + ) + self.lifetime_balanced_cstr.add((n, p), expr) + else: + pass + + self.lifetime_balanced_cstr = Constraint( + self.INVESTSTORAGES_BALANCED, + m.PERIODS, + noruleinit=True + ) + self.lifetime_balanced_cstr_build = BuildAction( + rule=_lifetime_balanced_storage_rule + ) def _power_coupled(block): """ @@ -1195,21 +1222,17 @@ def _power_coupled(block): for n in self.INVEST_REL_IN_OUT: for p in m.PERIODS: expr = ( - m.InvestmentFlow.total[n, o[n], p] - ) * n.invest_relation_input_output == ( - m.InvestmentFlow.total[i[n], n, p] - ) + m.InvestmentFlowBlock.total[n, o[n], p] + ) * n.invest_relation_input_output == ( + m.InvestmentFlowBlock.total[i[n], n, p] + ) self.power_coupled.add((n, p), expr) self.power_coupled = Constraint( - self.INVEST_REL_IN_OUT, - m.PERIODS, - noruleinit=True + self.INVEST_REL_IN_OUT, m.PERIODS, noruleinit=True ) - self.power_coupled_build = BuildAction( - rule=_power_coupled - ) + self.power_coupled_build = BuildAction(rule=_power_coupled) def _storage_capacity_inflow_invest_rule(block): """ @@ -1221,15 +1244,12 @@ def _storage_capacity_inflow_invest_rule(block): for p in m.PERIODS: expr = ( m.InvestmentFlow.total[i[n], n, p] - == self.total[n, p] - * n.invest_relation_input_capacity + == self.total[n, p] * n.invest_relation_input_capacity ) self.storage_capacity_inflow.add((n, p), expr) self.storage_capacity_inflow = Constraint( - self.INVEST_REL_CAP_IN, - m.PERIODS, - noruleinit=True + self.INVEST_REL_CAP_IN, m.PERIODS, noruleinit=True ) self.storage_capacity_inflow_build = BuildAction( @@ -1244,23 +1264,18 @@ def _storage_capacity_outflow_invest_rule(block): """ for n in self.INVEST_REL_CAP_OUT: for p in m.PERIODS: - expr = ( - ( - m.InvestmentFlow.total[n, o[n], p] - ) - == self.total[n, p] - * n.invest_relation_output_capacity - ) + expr = (m.InvestmentFlow.total[n, o[n], p]) == self.total[ + n, p + ] * n.invest_relation_output_capacity self.storage_capacity_outflow.add((n, p), expr) self.storage_capacity_outflow = Constraint( - self.INVEST_REL_CAP_OUT, - m.PERIODS, - noruleinit=True + self.INVEST_REL_CAP_OUT, m.PERIODS, noruleinit=True ) self.storage_capacity_outflow_build = BuildAction( - rule=_storage_capacity_outflow_invest_rule) + rule=_storage_capacity_outflow_invest_rule + ) def _max_storage_content_invest_rule(block, n, p, t): """ @@ -1269,8 +1284,7 @@ def _max_storage_content_invest_rule(block, n, p, t): """ expr = ( self.storage_content[n, t] - <= self.total[n, p] - * n.max_storage_level[t] + <= self.total[n, p] * n.max_storage_level[t] ) return expr @@ -1287,8 +1301,7 @@ def _min_storage_content_invest_rule(block, n, p, t): """ expr = ( self.storage_content[n, t] - >= self.total[n, p] - * n.min_storage_level[t] + >= self.total[n, p] * n.min_storage_level[t] ) return expr @@ -1312,7 +1325,7 @@ def maximum_invest_limit(block, n, p): self.limit_max = Constraint( self.NON_CONVEX_INVESTSTORAGES, m.PERIODS, - rule=maximum_invest_limit + rule=maximum_invest_limit, ) def smallest_invest(block, n, p): @@ -1328,9 +1341,7 @@ def smallest_invest(block, n, p): ) self.limit_min = Constraint( - self.NON_CONVEX_INVESTSTORAGES, - m.PERIODS, - rule=smallest_invest + self.NON_CONVEX_INVESTSTORAGES, m.PERIODS, rule=smallest_invest ) def _overall_storage_maximum_investflow_rule(block): @@ -1344,14 +1355,11 @@ def _overall_storage_maximum_investflow_rule(block): """ for n in self.OVERALL_MAXIMUM_INVESTSTORAGES: for p in m.PERIODS: - expr = (self.total[n, p] - <= n.investment.overall_maximum) + expr = self.total[n, p] <= n.investment.overall_maximum self.overall_storage_maximum.add((n, p), expr) self.overall_storage_maximum = Constraint( - self.OVERALL_MAXIMUM_INVESTSTORAGES, - m.PERIODS, - noruleinit=True + self.OVERALL_MAXIMUM_INVESTSTORAGES, m.PERIODS, noruleinit=True ) self.overall_maximum_build = BuildAction( @@ -1365,13 +1373,14 @@ def _overall_minimum_investflow_rule(block): Note: This is only applicable for the last period """ for n in self.OVERALL_MINIMUM_INVESTSTORAGES: - expr = (n.investment.overall_minimum - <= self.total[n, m.PERIODS[-1]]) + expr = ( + n.investment.overall_minimum + <= self.total[n, m.PERIODS[-1]] + ) self.overall_minimum.add(n, expr) self.overall_minimum = Constraint( - self.OVERALL_MINIMUM_INVESTSTORAGES, - noruleinit=True + self.OVERALL_MINIMUM_INVESTSTORAGES, noruleinit=True ) self.overall_minimum_build = BuildAction( @@ -1403,27 +1412,33 @@ def _objective_expression(self): ) else: - msg = ("You did not specify an interest rate.\n" - "It will be set equal to the discount_rate of {} " - "of the model as a default.\nThis corresponds to a " - "social planner point of view and does not reflect " - "microeconomic interest requirements.") + msg = ( + "You did not specify an interest rate.\n" + "It will be set equal to the discount_rate of {} " + "of the model as a default.\nThis corresponds to a " + "social planner point of view and does not reflect " + "microeconomic interest requirements." + ) for n in self.CONVEX_INVESTSTORAGES: lifetime = n.investment.lifetime interest = n.investment.interest_rate if interest == 0: - warn(msg.format(m.discount_rate), - debugging.SuspiciousUsageWarning) + warn( + msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning, + ) interest = m.discount_rate for p in m.PERIODS: annuity = economics.annuity( capex=n.investment.ep_costs[p], n=lifetime, - wacc=interest + wacc=interest, ) investment_costs_increment = ( - self.invest[n, p] * annuity * lifetime + self.invest[n, p] + * annuity + * lifetime * ((1 + m.discount_rate) ** (-p)) ) investment_costs += investment_costs_increment @@ -1433,21 +1448,21 @@ def _objective_expression(self): lifetime = n.investment.lifetime interest = n.investment.interest_rate if interest == 0: - warn(msg.format(m.discount_rate), - debugging.SuspiciousUsageWarning) + warn( + msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning, + ) interest = m.discount_rate for p in m.PERIODS: annuity = economics.annuity( capex=n.investment.ep_costs[p], n=lifetime, - wacc=interest + wacc=interest, ) investment_costs_increment = ( - (self.invest[n, p] * annuity * lifetime - + self.invest_status[n, p] - * n.investment.offset[p]) - * ((1 + m.discount_rate) ** (-p)) - ) + self.invest[n, p] * annuity * lifetime + + self.invest_status[n, p] * n.investment.offset[p] + ) * ((1 + m.discount_rate) ** (-p)) investment_costs += investment_costs_increment period_investment_costs[p] += investment_costs_increment @@ -1455,14 +1470,12 @@ def _objective_expression(self): if n.investment.fixed_costs[0] is not None: lifetime = n.investment.lifetime for p in m.PERIODS: - fixed_costs += ( - sum(self.invest[n, p] - * n.investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range(p, p + lifetime) - ) - * ((1 + m.discount_rate) ** (-p)) - ) + fixed_costs += sum( + self.invest[n, p] + * n.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(p, p + lifetime) + ) * ((1 + m.discount_rate) ** (-p)) self.investment_costs = investment_costs self.period_investment_costs = period_investment_costs From 7b7faf78fcc84e3fbae92aab309d9f08aabf3d5f Mon Sep 17 00:00:00 2001 From: uvchik Date: Fri, 10 Dec 2021 17:55:55 +0100 Subject: [PATCH 0040/1363] Create first draft to ensure actual behaviour --- src/oemof/solph/_energy_system.py | 46 ++++++++++++++++++- src/oemof/solph/_models.py | 19 +------- src/oemof/solph/processing.py | 8 +++- tests/test_models.py | 9 ++-- .../test_simple_dispatch_one.py | 6 ++- tests/test_warnings.py | 1 + 6 files changed, 63 insertions(+), 26 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index c320a0efe..8612ebf0e 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -13,6 +13,8 @@ """ +import numpy as np +import pandas as pd from oemof.network import energy_system as es @@ -32,7 +34,9 @@ class EnergySystem(es.EnergySystem): oemof.network directly. """ - def __init__(self, **kwargs): + def __init__( + self, timeindex=None, timeincrement=None, mode="implicit", **kwargs + ): # Doing imports at runtime is generally frowned upon, but should work # for now. See the TODO in :func:`constraint_grouping # ` for more information. @@ -40,4 +44,42 @@ def __init__(self, **kwargs): kwargs["groupings"] = GROUPINGS + kwargs.get("groupings", []) - super().__init__(**kwargs) + if not ( + isinstance(timeindex, pd.DatetimeIndex) + or isinstance(timeindex, type(None)) + ): + msg = ( + "Parameter 'timeindex' has to be of type " + "pandas.datetimeindex or NoneType and not of type {0}" + ) + raise TypeError(msg.format(type(timeindex))) + + if mode == "implicit" and timeindex is not None: + # Add one timestep to the timeindex. + timeindex = timeindex.union( + pd.date_range( + timeindex[-1] + timeindex.freq, + periods=1, + freq=timeindex.freq, + ) + ) + + if timeincrement is not None and timeindex is not None: + raise AttributeError("Don't do it.") + + elif timeincrement is None and timeindex is None: + pass + + elif timeindex is not None and timeincrement is None: + df = pd.DataFrame(timeindex) + timedelta = df.diff() + timeincrement = ( + (timedelta / np.timedelta64(1, "h"))[1:].set_index(0).index + ) + + if mode == "implicit" and timeindex is not None: + timeindex = timeindex[1:] + + super().__init__( + timeindex=timeindex, timeincrement=timeincrement, **kwargs + ) diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 8ac022d5c..5a4a36e76 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -75,22 +75,7 @@ def __init__(self, energysystem, **kwargs): self.name = kwargs.get("name", type(self).__name__) self.es = energysystem - self.timeincrement = sequence( - kwargs.get("timeincrement", self.es.timeincrement) - ) - if self.timeincrement[0] is None: - try: - self.timeincrement = sequence( - self.es.timeindex.freq.nanos / 3.6e12 - ) - except AttributeError: - msg = ( - "No valid time increment found. Please pass a valid " - "timeincremet parameter or pass an EnergySystem with " - "a valid time index. Please note that a valid time" - "index need to have a 'freq' attribute." - ) - raise AttributeError(msg) + self.timeincrement = kwargs.get("timeincrement", self.es.timeincrement) self.objective_weighting = kwargs.get( "objective_weighting", self.timeincrement @@ -297,7 +282,7 @@ def _add_parent_block_sets(self): # pyomo set for timesteps of optimization problem self.TIMESTEPS = po.Set( - initialize=range(len(self.es.timeindex)), ordered=True + initialize=range(len(self.es.timeincrement)), ordered=True ) # previous timesteps diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index 3d342038e..c527ee591 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -132,6 +132,12 @@ def results(om): for k, v in df.groupby("oemof_tuple") } + # Define index + if om.es.timeindex is None: + result_index = list(range(len(om.es.timeincrement))) + else: + result_index = om.es.timeindex + # create final result dictionary by splitting up the dataframes in the # dataframe dict into a series for scalar data and dataframe for sequences result = {} @@ -139,7 +145,7 @@ def results(om): df_dict[k].set_index("timestep", inplace=True) df_dict[k] = df_dict[k].pivot(columns="variable_name", values="value") try: - df_dict[k].index = om.es.timeindex + df_dict[k].index = result_index except ValueError as e: msg = ( "\nFlowBlock: {0}-{1}. This could be caused by NaN-values in" diff --git a/tests/test_models.py b/tests/test_models.py index f17c9d4a7..d7c519ac2 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -91,7 +91,7 @@ def test_nonequ_with_non_valid_fill(): def test_optimal_solution(): - es = solph.EnergySystem(timeindex=[1]) + es = solph.EnergySystem(timeincrement=[1]) bel = solph.buses.Bus(label="bus") es.add(bel) es.add( @@ -104,16 +104,17 @@ def test_optimal_solution(): outputs={bel: solph.flows.Flow(variable_costs=5)} ) ) - m = solph.Model(es, timeincrement=1) + m = solph.Model(es) m.solve("cbc") m.results() solph.processing.meta_results(m) def test_infeasible_model(): + warnings.filterwarnings("ignore", category=FutureWarning) with pytest.raises(ValueError, match=""): with warnings.catch_warnings(record=True) as w: - es = solph.EnergySystem(timeindex=[1]) + es = solph.EnergySystem(timeincrement=[1]) bel = solph.buses.Bus(label="bus") es.add(bel) es.add( @@ -130,7 +131,7 @@ def test_infeasible_model(): } ) ) - m = solph.Model(es, timeincrement=1) + m = solph.Model(es) m.solve(solver="cbc") assert "Optimization ended with status" in str(w[0].message) solph.processing.meta_results(m) diff --git a/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one.py b/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one.py index bfd789264..63aaec144 100644 --- a/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one.py +++ b/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one.py @@ -79,7 +79,7 @@ def test_dispatch_one_time_step(solver="cbc"): conversion_factors={bel: 1 / 3, b_heat_source: (cop - 1) / cop}, ) - energysystem = EnergySystem(timeindex=[1]) + energysystem = EnergySystem(timeincrement=[1]) energysystem.add( bgas, bel, @@ -97,7 +97,7 @@ def test_dispatch_one_time_step(solver="cbc"): # ################################ optimization ########################### # create optimization model based on energy_system - optimization_model = Model(energysystem=energysystem, timeincrement=1) + optimization_model = Model(energysystem=energysystem) # solve problem optimization_model.solve(solver=solver) @@ -111,6 +111,8 @@ def test_dispatch_one_time_step(solver="cbc"): # generate results to be evaluated in tests results = data["sequences"].sum(axis=0).to_dict() + print("*************", data["sequences"].index) + test_results = { (("wind", "b_el"), "flow"): 33, (("b_el", "demand_elec"), "flow"): 26, diff --git a/tests/test_warnings.py b/tests/test_warnings.py index b81367557..7f8c40793 100644 --- a/tests/test_warnings.py +++ b/tests/test_warnings.py @@ -22,6 +22,7 @@ def warning_fixture(): """Explicitly activate the warnings.""" warnings.filterwarnings("always", category=SuspiciousUsageWarning) + warnings.filterwarnings("ignore", category=FutureWarning) def test_that_the_sink_warnings_actually_get_raised(warning_fixture): From 0e585493e63d89fac25142935f054639066617af Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 11 Dec 2021 15:39:11 +0100 Subject: [PATCH 0041/1363] Introduce _periods class for easier handling --- src/oemof/solph/__init__.py | 2 ++ src/oemof/solph/_periods.py | 58 +++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/oemof/solph/_periods.py diff --git a/src/oemof/solph/__init__.py b/src/oemof/solph/__init__.py index ca12a91fe..9edc42c5b 100644 --- a/src/oemof/solph/__init__.py +++ b/src/oemof/solph/__init__.py @@ -13,6 +13,7 @@ from ._options import Investment from ._options import NonConvex from ._plumbing import sequence +from ._periods import Period __all__ = [ "buses", @@ -28,4 +29,5 @@ "Investment", "NonConvex", "sequence", + "Period" ] diff --git a/src/oemof/solph/_periods.py b/src/oemof/solph/_periods.py new file mode 100644 index 000000000..d681583b4 --- /dev/null +++ b/src/oemof/solph/_periods.py @@ -0,0 +1,58 @@ +""" +solph class defining periods for a multi-period model + +SPDX-FileCopyrightText: Johannes Kochems + +SPDX-License-Identifier: MIT + +""" +import pandas as pd + + +class Period: + """Periods for a multi-period optimization model + + Periods are defined on an annual basis. + Thus, it is not (yet) allowed to consider sub-annual periods + """ + + def __init__(self, start=None, end=None): + """Initialize a Period object + + Parameters + ---------- + start : str + String representation for the starting timestamp of a period + end : str + String representation for the last timestamp of a period + """ + e1 = ( + "Please define a valid {} time.\n" + "It has to be of type str and a valid date string representation." + ) + if start is None or not isinstance(start, str): + raise ValueError(e1.format("start")) + if end is None or not isinstance(start, str): + raise ValueError(e1.format("end")) + + e2 = ( + "You did not specify a valid date string representation which" + " is needed for converting {} time to a pandas.Timestamp object.\n" + "Please refer to the pandas date and time documentation." + ) + try: + self.start = pd.Timestamp(start) + except ValueError: + raise ValueError(e2.format("start")) + try: + self.end = pd.Timestamp(end) + except ValueError: + raise ValueError(e2.format("end")) + + def _extract_periods_length(self): + """Extracts the periods length in full years""" + return self.end.year - self.start.year + 1 + + @property + def periods_length(self): + return self._extract_periods_length() From 0a89cec6d61d7f08c72c3f1e2aa56819a5627c3f Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 11 Dec 2021 15:41:29 +0100 Subject: [PATCH 0042/1363] Fix typo --- src/oemof/solph/_models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 8a06ae293..727dc521a 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -88,9 +88,9 @@ def __init__(self, energysystem, **kwargs): except AttributeError: msg = ( "No valid time increment found. Please pass a valid " - "timeincremet parameter or pass an EnergySystem with " + "timeincrement parameter or pass an EnergySystem with " "a valid time index. Please note that a valid time" - "index need to have a 'freq' attribute." + "index needs to have a 'freq' attribute." ) raise AttributeError(msg) From aff289bf773de5c2d1e4dedb602d4d58f6e0b282 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 11 Dec 2021 15:41:37 +0100 Subject: [PATCH 0043/1363] Add docs --- src/oemof/solph/_options.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/oemof/solph/_options.py b/src/oemof/solph/_options.py index ea26f8aec..7325987fc 100644 --- a/src/oemof/solph/_options.py +++ b/src/oemof/solph/_options.py @@ -41,6 +41,25 @@ class Investment: offset : float, :math:`c_{invest,fix}` Additional fix investment costs. Only applicable if `nonconvex` is set to `True`. + overall_maximum : float + Overall maximum capacity investment, i.e. the amount of capacity + that can be totally installed at maximum in any period (taking into + account decommissionings); only applicable for multi-period models + overall_minimum : float + Overall minimum capacity investment that needs to be installed + in the last period of the simulation (taking into account + decommissionings); only applicable for multi-period models + lifetime : int + Units lifetime, given in years; only applicable for multi-period + models + age : int + Units start age, given in years at the beginning of the simulation; + only applicable for multi-period models + interest_rate : float + Interest rate for calculating annuities when investing in that unit; + only applicable for multi-period models + fixed_costs : list of float + Fixed costs in each period; only applicable for multi-period models For the variables, constraints and parts of the objective function, which From 2da105a36e529c5edeaddf3bdf39c1c14696fb5d Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 11 Dec 2021 15:41:58 +0100 Subject: [PATCH 0044/1363] Introduce periods_length --- src/oemof/solph/_energy_system.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 82bda5482..30a322d6b 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -56,6 +56,7 @@ def __init__(self, multi_period=False, periods=None, **kwargs): super().__init__(**kwargs) self.multi_period = multi_period self._add_periods(periods) + self.periods_length = {k: v.periods_length for k, v in periods.items()} def _add_periods(self, periods): """Add periods to the energy system @@ -72,13 +73,19 @@ def _add_periods(self, periods): Keys are years as integer values, values are the respective number of the period starting with zero """ + # if not self.multi_period: + # periods = {"single_period": 0} + # elif periods is None: + # years = sorted( + # list( + # set(getattr(self.timeindex, 'year')) + # ) + # ) + # periods = dict(zip(years, range(len(years)))) + # self.periods = periods if not self.multi_period: - periods = {"single_period": 0} + periods = [0] + # TODO: Define a default elif periods is None: - years = sorted( - list( - set(getattr(self.timeindex, 'year')) - ) - ) - periods = dict(zip(years, range(len(years)))) + pass self.periods = periods From 4bb5f3f3ded241b28bf9ce2ed0f7370365fcf94b Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 11 Dec 2021 18:45:33 +0100 Subject: [PATCH 0045/1363] Include default and determine gaps between periods --- src/oemof/solph/_energy_system.py | 56 ++++++++++++++++++++++++++----- src/oemof/solph/_periods.py | 36 ++++++++++++-------- 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 30a322d6b..721779a51 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -16,6 +16,8 @@ from oemof.network import energy_system as es +from oemof.solph._periods import Period + class EnergySystem(es.EnergySystem): """A variant of the class EnergySystem from @@ -55,23 +57,29 @@ def __init__(self, multi_period=False, periods=None, **kwargs): super().__init__(**kwargs) self.multi_period = multi_period - self._add_periods(periods) - self.periods_length = {k: v.periods_length for k, v in periods.items()} + self.periods = self._add_periods(periods) + self._extract_periods_lengths_and_gap() def _add_periods(self, periods): - """Add periods to the energy system + """Returns periods to be added to the energy system * For a single model, periods only contain one value. - * For a multi period model, periods must equal to years used in the + * For a multi-period model, periods are based on the years used in the timeindex. As a default, each year in the timeindex is mapped to its own period. Parameters ---------- periods : dict - The periods of a multi period model + Periods of a (multi-period) model Keys are years as integer values, - values are the respective number of the period starting with zero + values are the periods defined by their first and last timestep. + For a standard model, only one period is used. + + Returns + ------- + periods : dict + Periods of the energy system """ # if not self.multi_period: # periods = {"single_period": 0} @@ -85,7 +93,37 @@ def _add_periods(self, periods): # self.periods = periods if not self.multi_period: periods = [0] - # TODO: Define a default elif periods is None: - pass - self.periods = periods + years = sorted( + list( + set(getattr(self.timeindex, 'year')) + ) + ) + + periods = {} + filter_series = self.timeindex.to_series() + for number, year in enumerate(years): + start = filter_series.loc[ + filter_series.index.year == year].min() + end = filter_series.loc[filter_series.index.year == year].max() + periods[number] = Period(start, end) + + return periods + + def _extract_periods_lengths_and_gap(self): + """Determine length of one and diff between two subsequent periods""" + periods_gap = {0: 0} + if not self.multi_period: + periods_length = {0: 1} + else: + periods_length = {} + + previous_end = None + for number, (k, v) in enumerate(self.periods.items()): + periods_length[k] = v.periods_length + if number >= 1: + periods_gap[k] = v.start.year - previous_end.year - 1 + previous_end = v.end + + self.periods_length = periods_length + self.periods_gap = periods_gap diff --git a/src/oemof/solph/_periods.py b/src/oemof/solph/_periods.py index d681583b4..4f5fbb44a 100644 --- a/src/oemof/solph/_periods.py +++ b/src/oemof/solph/_periods.py @@ -21,18 +21,20 @@ def __init__(self, start=None, end=None): Parameters ---------- - start : str - String representation for the starting timestamp of a period + start : pd.Timestamp or str + Starting timestamp of a period or its string representation end : str - String representation for the last timestamp of a period + Last timestamp of a period or its string representation """ e1 = ( "Please define a valid {} time.\n" - "It has to be of type str and a valid date string representation." + "It has to be either of type pandas.Timestamp or " + "of type str and in that case needs to be a valid " + "date string representation." ) - if start is None or not isinstance(start, str): + if start is None or type(start) not in [str, pd.Timestamp]: raise ValueError(e1.format("start")) - if end is None or not isinstance(start, str): + if end is None or type(end) not in [str, pd.Timestamp]: raise ValueError(e1.format("end")) e2 = ( @@ -40,14 +42,20 @@ def __init__(self, start=None, end=None): " is needed for converting {} time to a pandas.Timestamp object.\n" "Please refer to the pandas date and time documentation." ) - try: - self.start = pd.Timestamp(start) - except ValueError: - raise ValueError(e2.format("start")) - try: - self.end = pd.Timestamp(end) - except ValueError: - raise ValueError(e2.format("end")) + if isinstance(start, str): + try: + self.start = pd.Timestamp(start) + except ValueError: + raise ValueError(e2.format("start")) + else: + self.start = start + if isinstance(end, str): + try: + self.end = pd.Timestamp(end) + except ValueError: + raise ValueError(e2.format("end")) + else: + self.end = end def _extract_periods_length(self): """Extracts the periods length in full years""" From dc478fd6b8e365880b1fddcb8a9f6e6c9d5022a4 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sun, 12 Dec 2021 11:55:45 +0100 Subject: [PATCH 0046/1363] Adjust periods definition (annual basis) --- src/oemof/solph/_energy_system.py | 24 +++++++++--------------- src/oemof/solph/_models.py | 12 ++++++++---- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 721779a51..10ee01f71 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -13,7 +13,7 @@ SPDX-License-Identifier: MIT """ - +import pandas as pd from oemof.network import energy_system as es from oemof.solph._periods import Period @@ -81,16 +81,6 @@ def _add_periods(self, periods): periods : dict Periods of the energy system """ - # if not self.multi_period: - # periods = {"single_period": 0} - # elif periods is None: - # years = sorted( - # list( - # set(getattr(self.timeindex, 'year')) - # ) - # ) - # periods = dict(zip(years, range(len(years)))) - # self.periods = periods if not self.multi_period: periods = [0] elif periods is None: @@ -106,7 +96,11 @@ def _add_periods(self, periods): start = filter_series.loc[ filter_series.index.year == year].min() end = filter_series.loc[filter_series.index.year == year].max() - periods[number] = Period(start, end) + periods[number] = pd.date_range(start, end, freq="H") + else: + for k in periods.keys(): + if not isinstance(k, int): + raise ValueError("Period keys must be of type int.") return periods @@ -120,10 +114,10 @@ def _extract_periods_lengths_and_gap(self): previous_end = None for number, (k, v) in enumerate(self.periods.items()): - periods_length[k] = v.periods_length + periods_length[k] = v.max().year - v.min().year + 1 if number >= 1: - periods_gap[k] = v.start.year - previous_end.year - 1 - previous_end = v.end + periods_gap[k] = v.min().year - previous_end.year - 1 + previous_end = v.max() self.periods_length = periods_length self.periods_gap = periods_gap diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 727dc521a..f4edd5cd0 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -320,17 +320,21 @@ def _add_parent_block_sets(self): ordered=True ) else: - # pyomo set for timeindex of optimization problem + nested_list = [ + [k] * len(self.es.periods[k]) for k in self.es.periods.keys() + ] + flattened_list = [ + item for sublist in nested_list for item in sublist + ] self.TIMEINDEX = po.Set( initialize=list( - zip([self.es.periods[p] for p in self.es.timeindex.year], - range(len(self.es.timeindex))) + zip(flattened_list, range(len(self.es.timeindex))) ), ordered=True ) self.PERIODS = po.Set( - initialize=sorted(list(set(self.es.periods.values()))) + initialize=sorted(list(set(self.es.periods.keys()))) ) # (Re-)Map timesteps to periods From b8638609c3477920cf354ea9c2da620ed1d12569 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sun, 12 Dec 2021 11:57:39 +0100 Subject: [PATCH 0047/1363] Remove obsolete _periods module (substituted by plain pd.date_range) --- src/oemof/solph/__init__.py | 2 - src/oemof/solph/_energy_system.py | 2 - src/oemof/solph/_periods.py | 66 ------------------------------- 3 files changed, 70 deletions(-) delete mode 100644 src/oemof/solph/_periods.py diff --git a/src/oemof/solph/__init__.py b/src/oemof/solph/__init__.py index 9edc42c5b..ca12a91fe 100644 --- a/src/oemof/solph/__init__.py +++ b/src/oemof/solph/__init__.py @@ -13,7 +13,6 @@ from ._options import Investment from ._options import NonConvex from ._plumbing import sequence -from ._periods import Period __all__ = [ "buses", @@ -29,5 +28,4 @@ "Investment", "NonConvex", "sequence", - "Period" ] diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 10ee01f71..d29fd466d 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -16,8 +16,6 @@ import pandas as pd from oemof.network import energy_system as es -from oemof.solph._periods import Period - class EnergySystem(es.EnergySystem): """A variant of the class EnergySystem from diff --git a/src/oemof/solph/_periods.py b/src/oemof/solph/_periods.py deleted file mode 100644 index 4f5fbb44a..000000000 --- a/src/oemof/solph/_periods.py +++ /dev/null @@ -1,66 +0,0 @@ -""" -solph class defining periods for a multi-period model - -SPDX-FileCopyrightText: Johannes Kochems - -SPDX-License-Identifier: MIT - -""" -import pandas as pd - - -class Period: - """Periods for a multi-period optimization model - - Periods are defined on an annual basis. - Thus, it is not (yet) allowed to consider sub-annual periods - """ - - def __init__(self, start=None, end=None): - """Initialize a Period object - - Parameters - ---------- - start : pd.Timestamp or str - Starting timestamp of a period or its string representation - end : str - Last timestamp of a period or its string representation - """ - e1 = ( - "Please define a valid {} time.\n" - "It has to be either of type pandas.Timestamp or " - "of type str and in that case needs to be a valid " - "date string representation." - ) - if start is None or type(start) not in [str, pd.Timestamp]: - raise ValueError(e1.format("start")) - if end is None or type(end) not in [str, pd.Timestamp]: - raise ValueError(e1.format("end")) - - e2 = ( - "You did not specify a valid date string representation which" - " is needed for converting {} time to a pandas.Timestamp object.\n" - "Please refer to the pandas date and time documentation." - ) - if isinstance(start, str): - try: - self.start = pd.Timestamp(start) - except ValueError: - raise ValueError(e2.format("start")) - else: - self.start = start - if isinstance(end, str): - try: - self.end = pd.Timestamp(end) - except ValueError: - raise ValueError(e2.format("end")) - else: - self.end = end - - def _extract_periods_length(self): - """Extracts the periods length in full years""" - return self.end.year - self.start.year + 1 - - @property - def periods_length(self): - return self._extract_periods_length() From ca9f4d1a32eaa306de2927862d8067eede093ac0 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Mon, 13 Dec 2021 08:47:42 +0100 Subject: [PATCH 0048/1363] Adapt docs --- src/oemof/solph/_energy_system.py | 9 +++++++-- src/oemof/solph/_models.py | 1 - 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index d29fd466d..eda38e250 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -61,7 +61,7 @@ def __init__(self, multi_period=False, periods=None, **kwargs): def _add_periods(self, periods): """Returns periods to be added to the energy system - * For a single model, periods only contain one value. + * For a standard model, periods only contain one value. * For a multi-period model, periods are based on the years used in the timeindex. As a default, each year in the timeindex is mapped to its own period. @@ -103,7 +103,12 @@ def _add_periods(self, periods): return periods def _extract_periods_lengths_and_gap(self): - """Determine length of one and diff between two subsequent periods""" + """Determine length of one and difference between subsequent periods + + * `periods_length` contains the length of a period in full years + * `periods_gap` is the difference in years between subsequent periods, + attributed to the latter one + """ periods_gap = {0: 0} if not self.multi_period: periods_length = {0: 1} diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index f4edd5cd0..344f4322e 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -310,7 +310,6 @@ def _add_parent_block_sets(self): self.TIMESTEPS = po.Set(initialize=range(len(self.es.timeindex)), ordered=True) - # TODO: Generalize! if not self.es.multi_period: self.TIMEINDEX = po.Set( initialize=list( From 16e7b344bee55333b5801de4e19a75aeedf50c47 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Mon, 13 Dec 2021 10:06:32 +0100 Subject: [PATCH 0049/1363] Include first draft for lifetime conversion --- src/oemof/solph/_energy_system.py | 26 +++++++++++++++++------ src/oemof/solph/flows/_investment_flow.py | 14 ++++++++++-- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index eda38e250..0b4693f0f 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -56,7 +56,7 @@ def __init__(self, multi_period=False, periods=None, **kwargs): super().__init__(**kwargs) self.multi_period = multi_period self.periods = self._add_periods(periods) - self._extract_periods_lengths_and_gap() + self._extract_periods_lengths_gap_and_years() def _add_periods(self, periods): """Returns periods to be added to the energy system @@ -102,25 +102,37 @@ def _add_periods(self, periods): return periods - def _extract_periods_lengths_and_gap(self): + # TODO: Check if length and gap are needed (no decommissions within period) + def _extract_periods_lengths_gap_and_years(self): """Determine length of one and difference between subsequent periods + and map periods to simulation years starting with 0 * `periods_length` contains the length of a period in full years * `periods_gap` is the difference in years between subsequent periods, - attributed to the latter one + attributed to the prior one + * `periods_years` is the simulation year corresponding to the start + of a period, starting with 0 """ - periods_gap = {0: 0} + periods_gap = {} if not self.multi_period: periods_length = {0: 1} else: periods_length = {} + periods_years = {0: 0} previous_end = None - for number, (k, v) in enumerate(self.periods.items()): + for k, v in self.periods.items(): periods_length[k] = v.max().year - v.min().year + 1 - if number >= 1: - periods_gap[k] = v.min().year - previous_end.year - 1 + if k >= 1: + periods_gap[k-1] = v.min().year - previous_end.year - 1 + periods_years[k] = ( + sum( + periods_length[kk] + periods_gap[kk] + for kk in range(k) + ) + ) previous_end = v.max() self.periods_length = periods_length self.periods_gap = periods_gap + self.periods_years = periods_years diff --git a/src/oemof/solph/flows/_investment_flow.py b/src/oemof/solph/flows/_investment_flow.py index b3deb779b..843d5eb00 100644 --- a/src/oemof/solph/flows/_investment_flow.py +++ b/src/oemof/solph/flows/_investment_flow.py @@ -435,9 +435,19 @@ def _old_capacity_rule_end(block): for i, o in self.INVESTFLOWS: lifetime = m.flows[i, o].investment.lifetime for p in m.PERIODS: - if lifetime <= p: + # No shutdown in first period + if p == 0: + expr = (self.old_end[i, o, p] == 0) + self.old_rule_end.add((i, o, p), expr) + elif lifetime <= m.es.periods_years[p]: + # Obtain commissioning period + comm_p = 0 + for k, v in m.es.periods_years.items(): + if m.es.periods_years[p] - lifetime - v < 0: + comm_p = k - 1 + break expr = (self.old_end[i, o, p] - == self.invest[i, o, p - lifetime]) + == self.invest[i, o, comm_p]) self.old_rule_end.add((i, o, p), expr) else: expr = (self.old_end[i, o, p] == 0) From 874cbbcef0647eedbceccd09db69761fbe15e430 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 13 Dec 2021 16:49:13 +0100 Subject: [PATCH 0050/1363] Rename mode to timemode to make it clearer --- src/oemof/solph/_energy_system.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 8612ebf0e..d1c8274df 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -35,7 +35,7 @@ class EnergySystem(es.EnergySystem): """ def __init__( - self, timeindex=None, timeincrement=None, mode="implicit", **kwargs + self, timeindex=None, timeincrement=None, timemode="implicit", **kwargs ): # Doing imports at runtime is generally frowned upon, but should work # for now. See the TODO in :func:`constraint_grouping @@ -54,7 +54,9 @@ def __init__( ) raise TypeError(msg.format(type(timeindex))) - if mode == "implicit" and timeindex is not None: + self.timemode = timemode + + if timemode == "implicit" and timeindex is not None: # Add one timestep to the timeindex. timeindex = timeindex.union( pd.date_range( From 3a8424ff985fa61b3b49276c0a796426e39f0b79 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 13 Dec 2021 16:49:33 +0100 Subject: [PATCH 0051/1363] Do not cut timeindex --- src/oemof/solph/_energy_system.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index d1c8274df..649e3489b 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -79,9 +79,6 @@ def __init__( (timedelta / np.timedelta64(1, "h"))[1:].set_index(0).index ) - if mode == "implicit" and timeindex is not None: - timeindex = timeindex[1:] - super().__init__( timeindex=timeindex, timeincrement=timeincrement, **kwargs ) From 4869291c9c347d5dffd8ec92948e6cd7933538a7 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 13 Dec 2021 16:50:07 +0100 Subject: [PATCH 0052/1363] Add time points to the model in addition to time intervals (TIMESTEPS) --- src/oemof/solph/_models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 5a4a36e76..9764b1076 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -284,6 +284,9 @@ def _add_parent_block_sets(self): self.TIMESTEPS = po.Set( initialize=range(len(self.es.timeincrement)), ordered=True ) + self.TIMEPOINTS = po.Set( + initialize=range(len(self.es.timeincrement)+1), ordered=True + ) # previous timesteps previous_timesteps = [x - 1 for x in self.TIMESTEPS] From eb29ffc790db64703023183aac04fae582a3f7b1 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 13 Dec 2021 16:51:48 +0100 Subject: [PATCH 0053/1363] Revise processing to allow sequences with a different time scale --- src/oemof/solph/processing.py | 83 +++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 24 deletions(-) diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index c527ee591..3f0c60d52 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -112,6 +112,38 @@ def create_dataframe(om): return df +def divide_scalars_sequences(df_dict, k): + try: + condition = df_dict[k][1:].isnull().any() + scalars = df_dict[k].loc[:, condition].dropna().iloc[0] + sequences = df_dict[k].loc[:, ~condition] + return {"scalars": scalars, "sequences": sequences} + except IndexError: + error_message = ( + "Cannot access index on result data. " + + "Did the optimization terminate" + + " without errors?" + ) + raise IndexError(error_message) + + +def set_result_index(df_dict, k, result_index): + try: + df_dict[k].index = result_index + except ValueError: + try: + df_dict[k] = df_dict[k][1:] + df_dict[k].index = result_index + except ValueError as e: + msg = ( + "\nFlowBlock: {0}-{1}. This could be caused by NaN-values " + "in your input data." + ) + raise type(e)( + str(e) + msg.format(k[0].label, k[1].label) + ).with_traceback(sys.exc_info()[2]) + + def results(om): """ Create a result dictionary from the result DataFrame. @@ -134,38 +166,41 @@ def results(om): # Define index if om.es.timeindex is None: - result_index = list(range(len(om.es.timeincrement))) + result_index = list(range(len(om.es.timeincrement) + 1)) else: result_index = om.es.timeindex # create final result dictionary by splitting up the dataframes in the # dataframe dict into a series for scalar data and dataframe for sequences result = {} - for k in df_dict: - df_dict[k].set_index("timestep", inplace=True) - df_dict[k] = df_dict[k].pivot(columns="variable_name", values="value") - try: - df_dict[k].index = result_index - except ValueError as e: - msg = ( - "\nFlowBlock: {0}-{1}. This could be caused by NaN-values in" - " your input data." + if om.es.timemode == "implicit": + # In the implicit time mode the first time point is removed. + # The values of intervals belong to the time at the end of the + # interval. + result_index = result_index[1:] + for k in df_dict: + df_dict[k].set_index("timestep", inplace=True) + df_dict[k] = df_dict[k].pivot( + columns="variable_name", values="value" ) - raise type(e)( - str(e) + msg.format(k[0].label, k[1].label) - ).with_traceback(sys.exc_info()[2]) - try: - condition = df_dict[k].isnull().any() - scalars = df_dict[k].loc[:, condition].dropna().iloc[0] - sequences = df_dict[k].loc[:, ~condition] - result[k] = {"scalars": scalars, "sequences": sequences} - except IndexError: - error_message = ( - "Cannot access index on result data. " - + "Did the optimization terminate" - + " without errors?" + set_result_index(df_dict, k, result_index) + result[k] = divide_scalars_sequences(df_dict, k) + elif om.es.timemode == "explicit": + for k in df_dict: + df_dict[k].set_index("timestep", inplace=True) + df_dict[k] = df_dict[k].pivot( + columns="variable_name", values="value" + ) + # Add empty row on top + df_dict[k] = pd.concat( + [ + pd.DataFrame(columns=df_dict[k].columns, index=[0]), + df_dict[k], + ], + ignore_index=True, ) - raise IndexError(error_message) + set_result_index(df_dict, k, result_index) + result[k] = divide_scalars_sequences(df_dict, k) # add dual variables for bus constraints if om.dual is not None: From 5445fcd63687c6a31828376dffb7859f9287c402 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 13 Dec 2021 16:52:40 +0100 Subject: [PATCH 0054/1363] Use new TIMEPOINTS in GenericStorage --- .../solph/components/_generic_storage.py | 64 ++++--------------- 1 file changed, 13 insertions(+), 51 deletions(-) diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index 7525b7dc8..32d12bd25 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -401,6 +401,9 @@ def _create(self, group=None): initialize=[n for n in group if n.balanced is True] ) + self.STORAGES_INITITAL_LEVEL = Set( + initialize=[n for n in group if n.initial_storage_level is not None]) + self.STORAGES_WITH_INVEST_FLOW_REL = Set( initialize=[ n for n in group if n.invest_relation_input_output is not None @@ -421,70 +424,29 @@ def _storage_content_bound_rule(block, n, t): return bounds self.storage_content = Var( - self.STORAGES, m.TIMESTEPS, bounds=_storage_content_bound_rule - ) - - def _storage_init_content_bound_rule(block, n): - return 0, n.nominal_storage_capacity - - self.init_content = Var( - self.STORAGES, - within=NonNegativeReals, - bounds=_storage_init_content_bound_rule, + self.STORAGES, m.TIMEPOINTS, bounds=_storage_content_bound_rule ) # set the initial storage content + # ToDo: More elegant code possible? for n in group: if n.initial_storage_level is not None: - self.init_content[n] = ( + self.storage_content[n, 0] = ( n.initial_storage_level * n.nominal_storage_capacity ) - self.init_content[n].fix() + self.storage_content[n, 0].fix() # ************* Constraints *************************** - reduced_timesteps = [x for x in m.TIMESTEPS if x > 0] - - # storage balance constraint (first time step) - def _storage_balance_first_rule(block, n): - """ - Rule definition for the storage balance of every storage n for - the first timestep. - """ - expr = 0 - expr += block.storage_content[n, 0] - expr += ( - -block.init_content[n] - * (1 - n.loss_rate[0]) ** m.timeincrement[0] - ) - expr += ( - n.fixed_losses_relative[0] - * n.nominal_storage_capacity - * m.timeincrement[0] - ) - expr += n.fixed_losses_absolute[0] * m.timeincrement[0] - expr += ( - -m.flow[i[n], n, 0] * n.inflow_conversion_factor[0] - ) * m.timeincrement[0] - expr += ( - m.flow[n, o[n], 0] / n.outflow_conversion_factor[0] - ) * m.timeincrement[0] - return expr == 0 - - self.balance_first = Constraint( - self.STORAGES, rule=_storage_balance_first_rule - ) - - # storage balance constraint (every time step but the first) def _storage_balance_rule(block, n, t): """ Rule definition for the storage balance of every storage n and - every timestep but the first (t > 0). + every timestep. """ expr = 0 - expr += block.storage_content[n, t] + expr += block.storage_content[n, t+1] expr += ( - -block.storage_content[n, t - 1] + -block.storage_content[n, t] * (1 - n.loss_rate[t]) ** m.timeincrement[t] ) expr += ( @@ -502,7 +464,7 @@ def _storage_balance_rule(block, n, t): return expr == 0 self.balance = Constraint( - self.STORAGES, reduced_timesteps, rule=_storage_balance_rule + self.STORAGES, m.TIMESTEPS, rule=_storage_balance_rule ) def _balanced_storage_rule(block, n): @@ -511,8 +473,8 @@ def _balanced_storage_rule(block, n): if balanced. """ return ( - block.storage_content[n, m.TIMESTEPS[-1]] - == block.init_content[n] + block.storage_content[n, m.TIMEPOINTS[-1]] + == block.storage_content[n, m.TIMEPOINTS[1]] ) self.balanced_cstr = Constraint( From 28c0f030cb51d743fcc1a3e9a1ab217ac454b9a8 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 4 Dec 2021 13:39:41 +0100 Subject: [PATCH 0055/1363] Integrate multi-period draft into general modules --- src/oemof/solph/_energy_system.py | 43 ++++++++++- src/oemof/solph/_models.py | 70 +++++++++++------ src/oemof/solph/_options.py | 44 +++++++++-- src/oemof/solph/processing.py | 121 +++++++++++++++++++++--------- src/oemof/solph/views.py | 20 +++-- 5 files changed, 227 insertions(+), 71 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index c320a0efe..82bda5482 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -8,6 +8,7 @@ SPDX-FileCopyrightText: Cord Kaldemeyer SPDX-FileCopyrightText: Stephan Günther SPDX-FileCopyrightText: Birgit Schachler +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -32,7 +33,19 @@ class EnergySystem(es.EnergySystem): oemof.network directly. """ - def __init__(self, **kwargs): + def __init__(self, multi_period=False, periods=None, **kwargs): + """Initialize an EnergySystem + + Parameters + ---------- + multi_period : boolean + If True, a multi period model is used; defaults to False + + periods : dict + The periods of a multi period model + Keys are years as integer values, + values are the respective number of the period starting with zero + """ # Doing imports at runtime is generally frowned upon, but should work # for now. See the TODO in :func:`constraint_grouping # ` for more information. @@ -41,3 +54,31 @@ def __init__(self, **kwargs): kwargs["groupings"] = GROUPINGS + kwargs.get("groupings", []) super().__init__(**kwargs) + self.multi_period = multi_period + self._add_periods(periods) + + def _add_periods(self, periods): + """Add periods to the energy system + + * For a single model, periods only contain one value. + * For a multi period model, periods must equal to years used in the + timeindex. As a default, each year in the timeindex is mapped to + its own period. + + Parameters + ---------- + periods : dict + The periods of a multi period model + Keys are years as integer values, + values are the respective number of the period starting with zero + """ + if not self.multi_period: + periods = {"single_period": 0} + elif periods is None: + years = sorted( + list( + set(getattr(self.timeindex, 'year')) + ) + ) + periods = dict(zip(years, range(len(years)))) + self.periods = periods diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 8ac022d5c..8bbec958e 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -7,6 +7,7 @@ SPDX-FileCopyrightText: Cord Kaldemeyer SPDX-FileCopyrightText: gplssm SPDX-FileCopyrightText: Patrik Schönfeldt +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -14,6 +15,7 @@ import logging import warnings +from oemof.tools import debugging from pyomo import environ as po from pyomo.core.plugins.transform.relax_integrality import RelaxIntegrality from pyomo.opt import SolverFactory @@ -287,7 +289,16 @@ class Model(BaseModel): NonConvexFlowBlock, ] - def __init__(self, energysystem, **kwargs): + def __init__(self, energysystem, discount_rate=None, **kwargs): + if discount_rate is not None: + self.discount_rate = discount_rate + elif energysystem.multi_period: + self.discount_rate = 0.02 + msg = (f"By default, a discount_rate of {self.discount_rate} " + f"is used for a multi-period model. " + f"If you want to use another value, " + f"you have to specify the `discount_rate` attribute.") + warnings.warn(msg, debugging.SuspiciousUsageWarning) super().__init__(energysystem, **kwargs) def _add_parent_block_sets(self): @@ -296,10 +307,28 @@ def _add_parent_block_sets(self): self.NODES = po.Set(initialize=[n for n in self.es.nodes]) # pyomo set for timesteps of optimization problem - self.TIMESTEPS = po.Set( - initialize=range(len(self.es.timeindex)), ordered=True + self.TIMESTEPS = po.Set(initialize=range(len(self.es.timeindex)), + ordered=True) + + # pyomo set for timeindex of optimization problem + self.TIMEINDEX = po.Set( + initialize=list( + zip([self.es.periods[p] for p in self.es.timeindex.year], + range(len(self.es.timeindex))) + ), + ordered=True + ) + + self.PERIODS = po.Set( + initialize=sorted(list(set(self.es.periods.values()))) ) + # (Re-)Map timesteps to periods + timesteps_in_period = {p: [] for p in self.PERIODS} + for p, t in self.TIMEINDEX: + timesteps_in_period[p].append(t) + self.TIMESTEPS_IN_PERIOD = timesteps_in_period + # previous timesteps previous_timesteps = [x - 1 for x in self.TIMESTEPS] previous_timesteps[0] = self.TIMESTEPS.last() @@ -335,34 +364,33 @@ def _add_parent_block_sets(self): def _add_parent_block_variables(self): """ """ - self.flow = po.Var(self.FLOWS, self.TIMESTEPS, within=po.Reals) + self.flow = po.Var( + self.FLOWS, self.TIMEINDEX, within=po.Reals + ) for (o, i) in self.FLOWS: if self.flows[o, i].nominal_value is not None: if self.flows[o, i].fix[self.TIMESTEPS[1]] is not None: - for t in self.TIMESTEPS: - self.flow[o, i, t].value = ( + for p, t in self.TIMEINDEX: + self.flow[o, i, p, t].value = ( self.flows[o, i].fix[t] - * self.flows[o, i].nominal_value - ) - self.flow[o, i, t].fix() + * self.flows[o, i].nominal_value) + self.flow[o, i, p, t].fix() else: - for t in self.TIMESTEPS: - self.flow[o, i, t].setub( + for p, t in self.TIMEINDEX: + self.flow[o, i, p, t].setub( self.flows[o, i].max[t] - * self.flows[o, i].nominal_value - ) + * self.flows[o, i].nominal_value) if not self.flows[o, i].nonconvex: - for t in self.TIMESTEPS: - self.flow[o, i, t].setlb( + for p, t in self.TIMEINDEX: + self.flow[o, i, p, t].setlb( self.flows[o, i].min[t] - * self.flows[o, i].nominal_value - ) + * self.flows[o, i].nominal_value) elif (o, i) in self.UNIDIRECTIONAL_FLOWS: - for t in self.TIMESTEPS: - self.flow[o, i, t].setlb(0) + for p, t in self.TIMEINDEX: + self.flow[o, i, p, t].setlb(0) else: if (o, i) in self.UNIDIRECTIONAL_FLOWS: - for t in self.TIMESTEPS: - self.flow[o, i, t].setlb(0) + for p, t in self.TIMEINDEX: + self.flow[o, i, p, t].setlb(0) diff --git a/src/oemof/solph/_options.py b/src/oemof/solph/_options.py index 9b8ca2d75..6b0e3e23b 100644 --- a/src/oemof/solph/_options.py +++ b/src/oemof/solph/_options.py @@ -8,11 +8,14 @@ SPDX-FileCopyrightText: Stephan Günther SPDX-FileCopyrightText: Patrik Schönfeldt SPDX-FileCopyrightText: jmloenneberga +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT """ +from warnings import warn +from oemof.tools import debugging from oemof.solph._plumbing import sequence @@ -54,15 +57,27 @@ def __init__( existing=0, nonconvex=False, offset=0, + overall_maximum=None, + overall_minimum=None, + lifetime=20, + age=0, + interest_rate=0, + fixed_costs=None, **kwargs, ): - self.maximum = maximum - self.minimum = minimum - self.ep_costs = ep_costs + self.maximum = sequence(maximum) + self.minimum = sequence(minimum) + self.ep_costs = sequence(ep_costs) self.existing = existing self.nonconvex = nonconvex - self.offset = offset + self.offset = sequence(offset) + self.overall_maximum = overall_maximum + self.overall_minimum = overall_minimum + self.lifetime = lifetime + self.age = age + self.interest_rate = interest_rate + self.fixed_costs = sequence(fixed_costs) for attribute in kwargs.keys(): value = kwargs.get(attribute) @@ -71,6 +86,7 @@ def __init__( self._check_invest_attributes() self._check_invest_attributes_maximum() self._check_invest_attributes_offset() + self._check_age_and_lifetime() def _check_invest_attributes(self): if (self.existing != 0) and (self.nonconvex is True): @@ -82,10 +98,10 @@ def _check_invest_attributes(self): raise AttributeError(e1) def _check_invest_attributes_maximum(self): - if (self.maximum == float("+inf")) and (self.nonconvex is True): + if (self.maximum[0] == float("+inf")) and (self.nonconvex is True): e2 = ( - "Please provide an maximum investment value in case of" - " nonconvex investemnt (nonconvex=True), which is in the" + "Please provide a maximum investment value in case of" + " nonconvex investment (nonconvex=True), which is in the" " expected magnitude." " \nVery high maximum values (> 10e8) as maximum investment" " limit might lead to numeric issues, so that no investment" @@ -94,13 +110,25 @@ def _check_invest_attributes_maximum(self): raise AttributeError(e2) def _check_invest_attributes_offset(self): - if (self.offset != 0) and (self.nonconvex is False): + if (self.offset[0] != 0) and (self.nonconvex is False): e3 = ( "If `nonconvex` is `False`, the `offset` parameter will be" " ignored." ) raise AttributeError(e3) + def _check_age_and_lifetime(self): + if self.lifetime == 20: + w1 = ("Using a lifetime of 20 periods," + " which is the default value.\nIf you don't consider a" + " multi-period investment model, you can safely ignore " + " this warning - all fine!") + warn(w1, debugging.SuspiciousUsageWarning) + if self.age >= self.lifetime: + e4 = ("A unit's age must be smaller than its " + "expected lifetime.") + raise AttributeError(e4) + class NonConvex: """ diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index 3d342038e..b9d41495c 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -9,6 +9,7 @@ SPDX-FileCopyrightText: Cord Kaldemeyer SPDX-FileCopyrightText: Stephan Günther SPDX-FileCopyrightText: henhuy +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -69,6 +70,35 @@ def remove_timestep(x): return x[:-1] +def get_timeindex(x): + """ + Get the timeindex from oemof tuples for multiperiod models. + Slice int values (timeindex, timesteps or periods) dependent on how + the variable is indexed. + + The timestep is removed from tuples of type `(n, n, int, int)`, + `(n, n, int)` and `(n, int)`. + """ + for i, n in enumerate(x): + if isinstance(n, int): + return x[i:] + return (0,) + + +def remove_timeindex(x): + """ + Remove the timeindex from oemof tuples for mulitperiod models. + Slice up to integer values (node labels) + + The timestep is removed from tuples of type `(n, n, int, int)`, + `(n, n, int)` and `(n, int)`. + """ + for i, n in enumerate(x): + if isinstance(n, int): + return x[:i] + return x + + def create_dataframe(om): """ Create a result dataframe with all optimization data. @@ -100,11 +130,11 @@ def create_dataframe(om): # columns for the oemof tuple and timestep are created df["oemof_tuple"] = df["pyomo_tuple"].map(get_tuple) df = df[df["oemof_tuple"].map(lambda x: x is not None)] - df["timestep"] = df["oemof_tuple"].map(get_timestep) - df["oemof_tuple"] = df["oemof_tuple"].map(remove_timestep) - + df["timeindex"] = df["oemof_tuple"].map(get_timeindex) + df["oemof_tuple"] = df["oemof_tuple"].map(remove_timeindex) # order the data by oemof tuple and timestep - df = df.sort_values(["oemof_tuple", "timestep"], ascending=[True, True]) + df = df.sort_values(["oemof_tuple", "timeindex"], + ascending=[True, True]) # drop empty decision variables df = df.dropna(subset=["value"]) @@ -118,17 +148,34 @@ def results(om): Results from Pyomo are written into a dictionary of pandas objects where a Series holds all scalar values and a dataframe all sequences for nodes - and flows. + and flows for a standard model. For a MultiPeriodModel, the investment + values are given in a DataFrame indexed by periods. The dictionary is keyed by the nodes e.g. `results[idx]['scalars']` - and flows e.g. `results[n, n]['sequences']`. + and flows e.g. `results[n, n]['sequences']` for a standard model. """ df = create_dataframe(om) + period_indexed = [ + "invest", + "total", + "old", + "old_exo", + "old_end" + ] + period_timestep_indexed = ["flow"] + # TODO: Take care of initial storage content instead of just ignoring + to_be_ignored = ["init_content"] + timestep_indexed = [el for el in df["variable_name"].unique() + if el not in period_indexed + and el not in period_timestep_indexed + and el not in to_be_ignored] + scalars_col = "period_scalars" + # create a dict of dataframes keyed by oemof tuples + # TODO / QUESTION: Could this be sped up using DFs / arrays instead?! df_dict = { - k - if len(k) > 1 - else (k[0], None): v[["timestep", "variable_name", "value"]] + k if len(k) > 1 else (k[0], None): + v[["timeindex", "variable_name", "value"]] for k, v in df.groupby("oemof_tuple") } @@ -136,45 +183,49 @@ def results(om): # dataframe dict into a series for scalar data and dataframe for sequences result = {} for k in df_dict: - df_dict[k].set_index("timestep", inplace=True) + df_dict[k].set_index("timeindex", inplace=True) df_dict[k] = df_dict[k].pivot(columns="variable_name", values="value") + + # TODO: Revise and potentially speed up + # Split data set + timeindex_cols = [col for col in df_dict[k].columns + if col in timestep_indexed or col == "flow"] + period_cols = [col for col in df_dict[k].columns + if col not in timeindex_cols] + sequences = df_dict[k][timeindex_cols].dropna() + if sequences.empty: + sequences = pd.DataFrame(index=om.es.timeindex) + # periods equal to years (will probably be the standard use case) + periods = sorted(list(set(om.es.timeindex.year))) + d = dict(zip([(el, ) for el in range(len(periods))], periods)) + period_scalars = df_dict[k][period_cols].dropna() + if period_scalars.empty: + period_scalars = pd.DataFrame(index=d.values()) try: - df_dict[k].index = om.es.timeindex - except ValueError as e: - msg = ( - "\nFlowBlock: {0}-{1}. This could be caused by NaN-values in" - " your input data." - ) - raise type(e)( - str(e) + msg.format(k[0].label, k[1].label) - ).with_traceback(sys.exc_info()[2]) - try: - condition = df_dict[k].isnull().any() - scalars = df_dict[k].loc[:, condition].dropna().iloc[0] - sequences = df_dict[k].loc[:, ~condition] - result[k] = {"scalars": scalars, "sequences": sequences} + sequences.index = om.es.timeindex + period_scalars.rename(index=d, inplace=True) + period_scalars.index.name = "period" + result[k] = {scalars_col: period_scalars, + "sequences": sequences} except IndexError: error_message = ( - "Cannot access index on result data. " - + "Did the optimization terminate" - + " without errors?" + "Some indices seem to be not matching.\n" + "Cannot properly extract model results." ) raise IndexError(error_message) # add dual variables for bus constraints if om.dual is not None: - grouped = groupby( - sorted(om.BusBlock.balance.iterkeys()), lambda p: p[0] - ) - for bus, timesteps in grouped: - duals = [ - om.dual[om.BusBlock.balance[bus, t]] for _, t in timesteps - ] + grouped = groupby(sorted(om.Bus.balance.iterkeys()), + lambda p: p[0]) + for bus, timeindex in grouped: + duals = [om.dual[om.Bus.balance[bus, p, t]] + for _, p, t in timeindex] df = pd.DataFrame({"duals": duals}, index=om.es.timeindex) if (bus, None) not in result.keys(): result[(bus, None)] = { "sequences": df, - "scalars": pd.Series(dtype=float), + scalars_col: pd.Series(dtype=float), } else: result[(bus, None)]["sequences"]["duals"] = duals diff --git a/src/oemof/solph/views.py b/src/oemof/solph/views.py index df6df4674..a95384abf 100644 --- a/src/oemof/solph/views.py +++ b/src/oemof/solph/views.py @@ -9,6 +9,7 @@ SPDX-FileCopyrightText: Cord Kaldemeyer SPDX-FileCopyrightText: Stephan Günther SPDX-FileCopyrightText: henhuy +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -29,8 +30,10 @@ def node(results, node, multiindex=False, keep_none_type=False): Obtain results for a single node e.g. a Bus or Component. Either a node or its label string can be passed. - Results are written into a dictionary which is keyed by 'scalars' and - 'sequences' holding respective data in a pandas Series and DataFrame. + Results are written into a dictionary which is keyed by 'scalars' + (resp. 'periods_scalars' for a multi-period model) and + 'sequences' holding respective data in a pandas Series (resp. DataFrame) + and DataFrame. """ def replace_none(col_list, reverse=False): @@ -58,19 +61,24 @@ def replace_none(col_list, reverse=False): filtered = {} # create a series with tuples as index labels for scalars + scalars_col = "scalars" + # Check for multi-period model (different naming) + if "period_scalars" in list(list(results.values())[0].keys()): + scalars_col = "period_scalars" + scalars = { - k: v["scalars"] + k: v[scalars_col] for k, v in results.items() - if node in k and not v["scalars"].empty + if node in k and not v[scalars_col].empty } if scalars: # aggregate data filtered["scalars"] = pd.concat(scalars.values(), axis=0) # assign index values idx = { - k: [c for c in v["scalars"].index] + k: [c for c in v[scalars_col].index] for k, v in results.items() - if node in k and not v["scalars"].empty + if node in k and not v[scalars_col].empty } idx = [tuple((k, m) for m in v) for k, v in idx.items()] idx = [i for sublist in idx for i in sublist] From 60096d0f3dc0f892ce5cf245628eedf087660b8d Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 4 Dec 2021 13:48:02 +0100 Subject: [PATCH 0056/1363] Integrate multi-period draft into bus modules --- src/oemof/solph/buses/_bus.py | 13 +++++++------ .../solph/buses/experimental/_electrical_bus.py | 3 ++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/oemof/solph/buses/_bus.py b/src/oemof/solph/buses/_bus.py index 4267a5cd5..491e0d464 100644 --- a/src/oemof/solph/buses/_bus.py +++ b/src/oemof/solph/buses/_bus.py @@ -11,6 +11,7 @@ SPDX-FileCopyrightText: Birgit Schachler SPDX-FileCopyrightText: jnnr SPDX-FileCopyrightText: jmloenneberga +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -80,14 +81,14 @@ def _create(self, group=None): outs[n] = [o for o in n.outputs] def _busbalance_rule(block): - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: - lhs = sum(m.flow[i, g, t] for i in ins[g]) - rhs = sum(m.flow[g, o, t] for o in outs[g]) - expr = lhs == rhs + lhs = sum(m.flow[i, g, p, t] for i in ins[g]) + rhs = sum(m.flow[g, o, p, t] for o in outs[g]) + expr = (lhs == rhs) # no inflows no outflows yield: 0 == 0 which is True if expr is not True: - block.balance.add((g, t), expr) + block.balance.add((g, p, t), expr) - self.balance = Constraint(group, m.TIMESTEPS, noruleinit=True) + self.balance = Constraint(group, m.TIMEINDEX, noruleinit=True) self.balance_build = BuildAction(rule=_busbalance_rule) diff --git a/src/oemof/solph/buses/experimental/_electrical_bus.py b/src/oemof/solph/buses/experimental/_electrical_bus.py index 401989056..81e7ecaac 100644 --- a/src/oemof/solph/buses/experimental/_electrical_bus.py +++ b/src/oemof/solph/buses/experimental/_electrical_bus.py @@ -11,6 +11,7 @@ SPDX-FileCopyrightText: jakob-wo SPDX-FileCopyrightText: gplssm SPDX-FileCopyrightText: jnnr +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -20,7 +21,7 @@ class ElectricalBus(Bus): - r"""A electrical bus object. Every node has to be connected to BusBlock. + r"""An electrical bus object. Every node has to be connected to BusBlock. This BusBlock is used in combination with ElectricalLine objects for linear optimal power flow (lopf) calculations. From 072bded097912a1a0b5f7d0d8824e2eb3624fa19 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 4 Dec 2021 14:24:17 +0100 Subject: [PATCH 0057/1363] Integrate multi-period draft into constraint modules --- src/oemof/solph/constraints/integral_limit.py | 151 +++++++++++++++--- .../solph/constraints/investment_limit.py | 125 ++++++++++++--- 2 files changed, 232 insertions(+), 44 deletions(-) diff --git a/src/oemof/solph/constraints/integral_limit.py b/src/oemof/solph/constraints/integral_limit.py index 6085b7715..ce0cd00a6 100644 --- a/src/oemof/solph/constraints/integral_limit.py +++ b/src/oemof/solph/constraints/integral_limit.py @@ -6,6 +6,8 @@ SPDX-FileCopyrightText: Simon Hilpert SPDX-FileCopyrightText: Patrik Schönfeldt SPDX-FileCopyrightText: Johannes Röder +SPDX-FileCopyrightText: Johannes Kochems +SPDX-FileCopyrightText: Johannes Giehl SPDX-License-Identifier: MIT @@ -30,6 +32,21 @@ def emission_limit(om, flows=None, limit=None): ) +def emission_limit_per_period(om, flows=None, limit=None): + r""" + Short handle for generic_periodical_integral_limit() + with keyword="emission_factor". Only applicable for multi-period models. + + Note + ---- + Flow objects required an attribute "emission_factor"! + + """ + generic_periodical_integral_limit( + om, keyword="emission_factor", flows=flows, limit=limit + ) + + def generic_integral_limit(om, keyword, flows=None, limit=None): r"""Set a global limit for flows weighted by attribute called keyword. The attribute named by keyword has to be added @@ -94,22 +111,7 @@ def generic_integral_limit(om, keyword, flows=None, limit=None): >>> model = solph.constraints.generic_integral_limit( ... model, "my_factor", flow_with_keyword, limit=777) """ - if flows is None: - flows = {} - for (i, o) in om.flows: - if hasattr(om.flows[i, o], keyword): - flows[(i, o)] = om.flows[i, o] - - else: - for (i, o) in flows: - if not hasattr(flows[i, o], keyword): - raise AttributeError( - ( - "Flow with source: {0} and target: {1} " - "has no attribute {2}." - ).format(i.label, o.label, keyword) - ) - + flows = _check_and_set_flows(om, flows, keyword) limit_name = "integral_limit_" + keyword setattr( @@ -117,11 +119,11 @@ def generic_integral_limit(om, keyword, flows=None, limit=None): limit_name, po.Expression( expr=sum( - om.flow[inflow, outflow, t] + om.flow[inflow, outflow, p, t] * om.timeincrement[t] * sequence(getattr(flows[inflow, outflow], keyword))[t] for (inflow, outflow) in flows - for t in om.TIMESTEPS + for p, t in om.TIMEINDEX ) ), ) @@ -133,3 +135,116 @@ def generic_integral_limit(om, keyword, flows=None, limit=None): ) return om + + +def generic_periodical_integral_limit(om, keyword, flows=None, limit=None): + r"""Set a global limit for flows for each period of a multi-period model + which is weighted by attribute called keyword. + The attribute named by keyword has to be added + to every flow you want to take into account. + + Total value of keyword attributes after optimization can be retrieved + calling the :attr:`om.oemof.solph.Model.integral_limit_${keyword}()`. + + Parameters + ---------- + om : oemof.solph.Model + Model to which constraints are added. + flows : dict + Dictionary holding the flows that should be considered in constraint. + Keys are (source, target) objects of the Flow. If no dictionary is + given all flows containing the keyword attribute will be + used. + keyword : string + attribute to consider + limit : sequence of float + Absolute limit of keyword attribute for the energy system. + + Note + ---- + Flow objects required an attribute named like keyword! + + **Constraint:** + + .. math:: \sum_{i \in F_I} \sum_{t \in T} P_i(t) \cdot w_i(t) + \cdot \tau(t) \leq L(p) \forall p in \textrm{PERIODS} + + + For the parameter and variable explanation, please refer to the docs + of generic_integral_limit. + + """ + flows = _check_and_set_flows(om, flows, keyword) + limit_name = "integral_limit_" + keyword + + if not om.es.multi_period: + msg = ("generic_periodical_integral_limit is only applicable\n" + "for multi-period models.\nFor standard models, use " + "generic_integral_limit instead.") + raise ValueError(msg) + + if limit is not None: + limit = sequence(limit) + else: + msg = ("You have to provide a limit for each period!\n" + "If you provide a scalar value, this will be applied as a " + "limit for each period.") + raise ValueError(msg) + + def _periodical_integral_limit_rule(m, p): + expr = sum( + om.flow[inflow, outflow, p, t] + * om.timeincrement[t] + * sequence(getattr(flows[inflow, outflow], keyword))[t] + for (inflow, outflow) in flows + for t in m.TIMESTEPS_IN_PERIOD[p] + ) + + return expr <= limit[p] + + om.periodical_integral_limit = po.Constraint( + om.PERIODS, + rule=_periodical_integral_limit_rule, + name=limit_name + "_constraint" + ) + + return om + + +def _check_and_set_flows(om, flows, keyword): + """Checks and sets flows if needed + + Parameters + ---------- + om : oemof.solph.Model + Model to which constraints are added. + + flows : dict + Dictionary holding the flows that should be considered in constraint. + Keys are (source, target) objects of the Flow. If no dictionary is + given all flows containing the keyword attribute will be + used. + + keyword : string + attribute to consider + + Returns + ------- + flows : dict + the flows to be considered + """ + if flows is None: + flows = {} + for (i, o) in om.flows: + if hasattr(om.flows[i, o], keyword): + flows[(i, o)] = om.flows[i, o] + + else: + for (i, o) in flows: + if not hasattr(flows[i, o], keyword): + raise AttributeError( + ( + "Flow with source: {0} and target: {1} " + "has no attribute {2}." + ).format(i.label, o.label, keyword) + ) diff --git a/src/oemof/solph/constraints/investment_limit.py b/src/oemof/solph/constraints/investment_limit.py index da526b3c5..dce7d7e85 100644 --- a/src/oemof/solph/constraints/investment_limit.py +++ b/src/oemof/solph/constraints/investment_limit.py @@ -6,6 +6,8 @@ SPDX-FileCopyrightText: Simon Hilpert SPDX-FileCopyrightText: Patrik Schönfeldt SPDX-FileCopyrightText: Johannes Röder +SPDX-FileCopyrightText: Johannes Kochems +SPDX-FileCopyrightText: Johannes Giehl SPDX-License-Identifier: MIT @@ -13,6 +15,8 @@ from pyomo import environ as po +from oemof.solph import sequence + def investment_limit(model, limit=None): r"""Set an absolute limit for the total investment costs of an investment @@ -36,6 +40,16 @@ def investment_rule(m): if hasattr(m, "GenericInvestmentStorageBlock"): expr += m.GenericInvestmentStorageBlock.investment_costs + + if hasattr(m, "SinkDSMOemofInvestmentBlock"): + expr += m.SinkDSMOemofInvestmentBlock.investment_costs + + if hasattr(m, "SinkDSMDIWInvestmentBlock"): + expr += m.SinkDSMDIWInvestmentBlock.investment_costs + + if hasattr(m, "SinkDSMDLRInvestmentBlock"): + expr += m.SinkDSMDLRInvestmentBlock.investment_costs + return expr <= limit model.investment_limit = po.Constraint(rule=investment_rule) @@ -43,6 +57,65 @@ def investment_rule(m): return model +def investment_limit_per_period(model, limit=None): + r"""Set an absolute limit for the total investment costs of a + investment optimization problem for each period + of the problem. + + .. math:: \sum_{investment\_costs(p)} \leq limit(p) + \forall p in \textrm{PERIODS} + + Parameters + ---------- + model : oemof.solph.Model + Model to which the constraint is added + limit : sequence of float, :math:`limit(p)` + Absolute limit of the investment for each period + (i.e. RHS of constraint) + """ + + if not model.es.multi_period: + msg = ("investment_limit_per_period is only applicable " + "for multi-period models.\nIn order to create such a model, " + "set attribute `multi_period` of your energy system to True.") + raise ValueError(msg) + + if limit is not None: + limit = sequence(limit) + else: + msg = ("You have to provide an investment limit for each period!\n" + "If you provide a scalar value, this will be applied as a " + "limit for each period.") + raise ValueError(msg) + + def investment_period_rule(m, p): + expr = 0 + + if hasattr(m, "InvestmentFlow"): + expr += m.InvestmentFlow.period_investment_costs[p] + + if hasattr(m, "GenericInvestmentStorageBlock"): + expr += m.GenericInvestmentStorageBlock.period_investment_costs[p] + + if hasattr(m, "SinkDSMOemofInvestmentBlock"): + expr += m.SinkDSMOemofInvestmentBlock.period_investment_costs[p] + + if hasattr(m, "SinkDSMDIWInvestmentBlock"): + expr += m.SinkDSMDIWInvestmentBlock.period_investment_costs[p] + + if hasattr(m, "SinkDSMDLRInvestmentBlock"): + expr += m.SinkDSMDLRInvestmentBlock.period_investment_costs[p] + + return expr <= limit[p] + + model.investment_limit_per_period = po.Constraint( + model.PERIODS, + rule=investment_period_rule + ) + + return model + + def additional_investment_flow_limit(model, keyword, limit=None): r""" Global limit for investment flows weighted by an attribute keyword. @@ -63,15 +136,15 @@ def additional_investment_flow_limit(model, keyword, limit=None): The symbols used are defined as follows (with Variables (V) and Parameters (P)): - +---------------+------------------------------------+------+--------------------------------------------------------------+ - | symbol | attribute | type | explanation | - +===============+====================================+======+==============================================================+ - | :math:`P_{i}` | `InvestmentFlowBlock.invest[i, o]` | V | installed capacity of investment flow | - +---------------+------------------------------------+------+--------------------------------------------------------------+ - | :math:`w_i` | `keyword` | P | weight given to investment flow named according to `keyword` | - +---------------+------------------------------------+------+--------------------------------------------------------------+ - | :math:`limit` | `limit` | P | global limit given by keyword `limit` | - +---------------+------------------------------------+------+--------------------------------------------------------------+ + +---------------+-------------------------------+------+--------------------------------------------------------------+ + | symbol | attribute | type | explanation | + +===============+===============================+======+==============================================================+ + | :math:`P_{i}` | `InvestmentFlow.invest[i, o]` | V | installed capacity of investment flow | + +---------------+-------------------------------+------+--------------------------------------------------------------+ + | :math:`w_i` | `keyword` | P | weight given to investment flow named according to `keyword` | + +---------------+-------------------------------+------+--------------------------------------------------------------+ + | :math:`limit` | `limit` | P | global limit given by keyword `limit` | + +---------------+-------------------------------+------+--------------------------------------------------------------+ Parameters ---------- @@ -94,12 +167,12 @@ def additional_investment_flow_limit(model, keyword, limit=None): >>> from oemof import solph >>> date_time_index = pd.date_range('1/1/2020', periods=5, freq='H') >>> es = solph.EnergySystem(timeindex=date_time_index) - >>> bus = solph.buses.Bus(label='bus_1') - >>> sink = solph.components.Sink(label="sink", inputs={bus: - ... solph.flows.Flow(nominal_value=10, fix=[10, 20, 30, 40, 50])}) - >>> src1 = solph.components.Source(label='source_0', outputs={bus: solph.flows.Flow( + >>> bus = solph.Bus(label='bus_1') + >>> sink = solph.Sink(label="sink", inputs={bus: + ... solph.Flow(nominal_value=10, fix=[10, 20, 30, 40, 50])}) + >>> src1 = solph.Source(label='source_0', outputs={bus: solph.Flow( ... investment=solph.Investment(ep_costs=50, space=4))}) - >>> src2 = solph.components.Source(label='source_1', outputs={bus: solph.flows.Flow( + >>> src2 = solph.Source(label='source_1', outputs={bus: solph.Flow( ... investment=solph.Investment(ep_costs=100, space=1))}) >>> es.add(bus, sink, src1, src2) >>> model = solph.Model(es) @@ -117,22 +190,22 @@ def additional_investment_flow_limit(model, keyword, limit=None): limit_name = "invest_limit_" + keyword - setattr( - model, - limit_name, - po.Expression( - expr=sum( - model.InvestmentFlowBlock.invest[inflow, outflow] + def _additional_limit_rule(block): + for p in model.PERIODS: + lhs = sum( + model.InvestmentFlow.invest[inflow, outflow, p] * getattr(invest_flows[inflow, outflow], keyword) for (inflow, outflow) in invest_flows ) - ), - ) + rhs = limit + model.add_limit.add(p, (lhs <= rhs)) - setattr( - model, - limit_name + "_constraint", - po.Constraint(expr=(getattr(model, limit_name) <= limit)), + model.add_limit = po.Constraint( + model.PERIODS, + noruleinit=True, + name=limit_name + "_constraint" ) + model.add_limit_build = po.BuildAction( + rule=_additional_limit_rule) return model From 9956ab69436f09e7cb17797e33917da16aa3312c Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 4 Dec 2021 14:24:39 +0100 Subject: [PATCH 0058/1363] Fix typo and bug in docs --- src/oemof/solph/constraints/shared_limit.py | 2 +- src/oemof/solph/processing.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oemof/solph/constraints/shared_limit.py b/src/oemof/solph/constraints/shared_limit.py index 113d32b7d..9afc921bb 100644 --- a/src/oemof/solph/constraints/shared_limit.py +++ b/src/oemof/solph/constraints/shared_limit.py @@ -45,7 +45,7 @@ def shared_limit( lower_limit : numeric the lower limit upper_limit : numeric - the lower limit + the upper limit Examples -------- diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index b9d41495c..077358b00 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -105,7 +105,7 @@ def create_dataframe(om): Results from Pyomo are written into pandas DataFrame where separate columns are created for the variable index e.g. for tuples of the flows and - components or the timesteps. + components or the timesteps / timeindices. """ # get all pyomo variables including their block block_vars = list( From 3b57236c8270ff562b68f7827677aae3711c8a3c Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sun, 5 Dec 2021 18:23:34 +0100 Subject: [PATCH 0059/1363] Integrate multi-period draft into modules of flow package --- src/oemof/solph/flows/_flow.py | 148 +++--- src/oemof/solph/flows/_investment_flow.py | 431 +++++++++++++++--- src/oemof/solph/flows/_non_convex_flow.py | 266 +++++++---- .../flows/experimental/_electrical_line.py | 8 +- 4 files changed, 642 insertions(+), 211 deletions(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index c9c3c8c3e..dee4c5323 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -10,6 +10,7 @@ SPDX-FileCopyrightText: Birgit Schachler SPDX-FileCopyrightText: jnnr SPDX-FileCopyrightText: jmloenneberga +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -60,8 +61,7 @@ class Flow(on.Edge): * `'ub'`: numeric (iterable, scalar or None), the normed *upper bound* on the positive difference (`flow[t-1] < flow[t]`) of two consecutive flow values. - * `'costs``: numeric (scalar or None), the gradient cost per - unit. + * `'costs``: REMOVED! negative_gradient : :obj:`dict`, default: `{'ub': None, 'costs': 0}` @@ -70,8 +70,7 @@ class Flow(on.Edge): * `'ub'`: numeric (iterable, scalar or None), the normed *upper bound* on the negative difference (`flow[t-1] > flow[t]`) of two consecutive flow values. - * `'costs``: numeric (scalar or None), the gradient cost per - unit. + * `'costs``: REMOVED! summed_max : numeric, :math:`f_{sum,max}` Specific maximum value summed over all timesteps. Will be multiplied @@ -82,6 +81,7 @@ class Flow(on.Edge): The costs associated with one unit of the flow. If this is set the costs will be added to the objective expression of the optimization problem. + Note: In a multi-period model, nominal costs have to be used. fixed : boolean Boolean value indicating if a flow is fixed during the optimization problem to its ex-ante set value. Used in combination with the @@ -104,12 +104,12 @@ class Flow(on.Edge): Notes ----- The following sets, variables, constraints and objective parts are created - * :py:class:`~oemof.solph..flows.flow.FlowBlock` - * :py:class:`~oemof.solph..flows.investment_flow.InvestmentFlowBlock` + * :py:class:`~oemof.solph.flows.flow.FlowBlock` + * :py:class:`~oemof.solph.flows.investment_flow.InvestmentFlowBlock` (additionally if Investment object is present) - * :py:class:`~oemof.solph..flows.non_convex_flow.NonConvexFlowBlock` + * :py:class:`~oemof.solph.flows.non_convex_flow.NonConvexFlowBlock` (If nonconvex object is present, CAUTION: replaces - :py:class:`~oemof.solph.flows.flow.FlowBlock` + :py:class:`~oemof.solphflows.flow.FlowBlock` class and a MILP will be build) Examples @@ -146,19 +146,21 @@ def __init__(self, **kwargs): "nonconvex", "integer", ] - sequences = ["fix", "variable_costs", "min", "max"] + sequences = ["fix", "variable_costs", "fixed_costs", "min", "max"] dictionaries = ["positive_gradient", "negative_gradient"] defaults = { "variable_costs": 0, - "positive_gradient": {"ub": None, "costs": 0}, - "negative_gradient": {"ub": None, "costs": 0}, + "positive_gradient": {"ub": None}, + "negative_gradient": {"ub": None}, } keys = [k for k in kwargs if k != "label"] if "fixed_costs" in keys: - raise AttributeError( - "The `fixed_costs` attribute has been removed" " with v0.2!" - ) + msg = ("Be aware that the fixed costs attribute is only\n" + "meant to be used for multi-period models.\n" + "It has been decided to remove the `fixed_costs` " + "attribute with v0.2 for regular uses!") + warn(msg, debugging.SuspiciousUsageWarning) if "actual_value" in keys: raise AttributeError( @@ -192,13 +194,24 @@ def __init__(self, **kwargs): if kwargs.get("max") is None: defaults["max"] = 1 + # Check gradient dictionaries for non-valid keys + for gradient_dict in ["negative_gradient", "positive_gradient"]: + if gradient_dict in kwargs: + if list(kwargs[gradient_dict].keys()) != list( + defaults[gradient_dict].keys() + ): + msg = ( + "Only the key 'ub' is allowed for the '{0}' attribute" + ) + raise AttributeError(msg.format(gradient_dict)) + for attribute in set(scalars + sequences + dictionaries + keys): value = kwargs.get(attribute, defaults.get(attribute)) if attribute in dictionaries: setattr( self, attribute, - {"ub": sequence(value["ub"]), "costs": value["costs"]}, + {"ub": sequence(value["ub"])}, ) else: @@ -398,8 +411,8 @@ def _flow_summed_max_rule(model): """Rule definition for build action of max. sum flow constraint.""" for inp, out in self.SUMMED_MAX_FLOWS: lhs = sum( - m.flow[inp, out, ts] * m.timeincrement[ts] - for ts in m.TIMESTEPS + m.flow[inp, out, p, ts] * m.timeincrement[ts] + for p, ts in m.TIMEINDEX ) rhs = ( m.flows[inp, out].summed_max @@ -414,8 +427,8 @@ def _flow_summed_min_rule(model): """Rule definition for build action of min. sum flow constraint.""" for inp, out in self.SUMMED_MIN_FLOWS: lhs = sum( - m.flow[inp, out, ts] * m.timeincrement[ts] - for ts in m.TIMESTEPS + m.flow[inp, out, p, ts] * m.timeincrement[ts] + for p, ts in m.TIMEINDEX ) rhs = ( m.flows[inp, out].summed_min @@ -429,18 +442,23 @@ def _flow_summed_min_rule(model): def _positive_gradient_flow_rule(model): """Rule definition for positive gradient constraint.""" for inp, out in self.POSITIVE_GRADIENT_FLOWS: - for ts in m.TIMESTEPS: - if ts > 0: - lhs = m.flow[inp, out, ts] - m.flow[inp, out, ts - 1] - rhs = self.positive_gradient[inp, out, ts] + for index in range(1, len(m.TIMEINDEX) + 1): + if m.TIMEINDEX[index][1] > 0: + lhs = (m.flow[inp, out, m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1]] + - m.flow[inp, out, m.TIMEINDEX[index - 1][0], + m.TIMEINDEX[index - 1][1]]) + rhs = self.positive_gradient[ + inp, out, m.TIMEINDEX[index][1]] self.positive_gradient_constr.add( - (inp, out, ts), lhs <= rhs - ) + (inp, out, m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1]), + lhs <= rhs) else: pass # return(Constraint.Skip) self.positive_gradient_constr = Constraint( - self.POSITIVE_GRADIENT_FLOWS, m.TIMESTEPS, noruleinit=True + self.POSITIVE_GRADIENT_FLOWS, m.TIMEINDEX, noruleinit=True ) self.positive_gradient_build = BuildAction( rule=_positive_gradient_flow_rule @@ -449,29 +467,34 @@ def _positive_gradient_flow_rule(model): def _negative_gradient_flow_rule(model): """Rule definition for negative gradient constraint.""" for inp, out in self.NEGATIVE_GRADIENT_FLOWS: - for ts in m.TIMESTEPS: - if ts > 0: - lhs = m.flow[inp, out, ts - 1] - m.flow[inp, out, ts] - rhs = self.negative_gradient[inp, out, ts] + for index in range(1, len(m.TIMEINDEX) + 1): + if m.TIMEINDEX[index][1] > 0: + lhs = (m.flow[inp, out, m.TIMEINDEX[index - 1][0], + m.TIMEINDEX[index - 1][1]] + - m.flow[inp, out, m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1]]) + rhs = self.negative_gradient[ + inp, out, m.TIMEINDEX[index][1]] self.negative_gradient_constr.add( - (inp, out, ts), lhs <= rhs - ) + (inp, out, m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1]), + lhs <= rhs) else: pass # return(Constraint.Skip) self.negative_gradient_constr = Constraint( - self.NEGATIVE_GRADIENT_FLOWS, m.TIMESTEPS, noruleinit=True + self.NEGATIVE_GRADIENT_FLOWS, m.TIMEINDEX, noruleinit=True ) self.negative_gradient_build = BuildAction( rule=_negative_gradient_flow_rule ) - def _integer_flow_rule(block, ii, oi, ti): + def _integer_flow_rule(block, ii, oi, pi, ti): """Force flow variable to NonNegativeInteger values.""" - return self.integer_flow[ii, oi, ti] == m.flow[ii, oi, ti] + return self.integer_flow[ii, oi, pi, ti] == m.flow[ii, oi, pi, ti] self.integer_flow_constr = Constraint( - self.INTEGER_FLOWS, m.TIMESTEPS, rule=_integer_flow_rule + self.INTEGER_FLOWS, m.TIMEINDEX, rule=_integer_flow_rule ) def _objective_expression(self): @@ -481,29 +504,36 @@ def _objective_expression(self): m = self.parent_block() variable_costs = 0 - gradient_costs = 0 - - for i, o in m.FLOWS: - if m.flows[i, o].variable_costs[0] is not None: - for t in m.TIMESTEPS: - variable_costs += ( - m.flow[i, o, t] - * m.objective_weighting[t] - * m.flows[i, o].variable_costs[t] - ) + fixed_costs = 0 + + if not m.es.multi_period: + for i, o in m.FLOWS: + if m.flows[i, o].variable_costs[0] is not None: + for p, t in m.TIMEINDEX: + variable_costs += ( + m.flow[i, o, p, t] + * m.objective_weighting[t] + * m.flows[i, o].variable_costs[t] + ) - if m.flows[i, o].positive_gradient["ub"][0] is not None: - for t in m.TIMESTEPS: - gradient_costs += ( - self.positive_gradient[i, o, t] - * m.flows[i, o].positive_gradient["costs"] - ) + else: + for i, o in m.FLOWS: + if m.flows[i, o].variable_costs[0] is not None: + for p, t in m.TIMEINDEX: + variable_costs += ( + m.flow[i, o, p, t] + * m.objective_weighting[t] + * m.flows[i, o].variable_costs[t] + * ((1 + m.discount_rate) ** -p) + ) - if m.flows[i, o].negative_gradient["ub"][0] is not None: - for t in m.TIMESTEPS: - gradient_costs += ( - self.negative_gradient[i, o, t] - * m.flows[i, o].negative_gradient["costs"] - ) + if (m.flows[i, o].fixed_costs[0] is not None + and m.flows[i, o].nominal_value is not None): + for p in m.PERIODS: + fixed_costs += ( + m.flows[i, o].nominal_value + * m.flows[i, o].fixed_costs[p] + * ((1 + m.discount_rate) ** -p) + ) - return variable_costs + gradient_costs + return variable_costs + fixed_costs diff --git a/src/oemof/solph/flows/_investment_flow.py b/src/oemof/solph/flows/_investment_flow.py index 0c859256c..b3deb779b 100644 --- a/src/oemof/solph/flows/_investment_flow.py +++ b/src/oemof/solph/flows/_investment_flow.py @@ -10,12 +10,17 @@ SPDX-FileCopyrightText: Birgit Schachler SPDX-FileCopyrightText: jnnr SPDX-FileCopyrightText: jmloenneberga +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT """ +from warnings import warn +from oemof.tools import debugging +from oemof.tools import economics from pyomo.core import Binary +from pyomo.core import BuildAction from pyomo.core import Constraint from pyomo.core import Expression from pyomo.core import NonNegativeReals @@ -292,145 +297,439 @@ def _create(self, group=None): ] ) + self.OVERALL_MAXIMUM_INVESTFLOWS = Set( + initialize=[ + (g[0], g[1]) for g in group + if g[2].investment.overall_maximum is not None] + ) + + self.OVERALL_MINIMUM_INVESTFLOWS = Set( + initialize=[ + (g[0], g[1]) for g in group + if g[2].investment.overall_minimum is not None] + ) + # ######################### VARIABLES ################################# - def _investvar_bound_rule(block, i, o): + def _investvar_bound_rule(block, i, o, p): """Rule definition for bounds of invest variable.""" if (i, o) in self.CONVEX_INVESTFLOWS: return ( - m.flows[i, o].investment.minimum, - m.flows[i, o].investment.maximum, + m.flows[i, o].investment.minimum[p], + m.flows[i, o].investment.maximum[p], ) elif (i, o) in self.NON_CONVEX_INVESTFLOWS: - return 0, m.flows[i, o].investment.maximum + return 0, m.flows[i, o].investment.maximum[p] - # create invest variable for a investment flow + # create invest variable for an investment flow self.invest = Var( self.INVESTFLOWS, + m.PERIODS, within=NonNegativeReals, bounds=_investvar_bound_rule, ) + # Total capacity + self.total = Var( + self.INVESTFLOWS, + m.PERIODS, + within=NonNegativeReals + ) + + # Old capacity is built out of old exogenous and endogenous capacities + self.old = Var( + self.INVESTFLOWS, + m.PERIODS, + within=NonNegativeReals + ) + + # Old endogenous capacity to be decommissioned (due to lifetime) + self.old_end = Var( + self.INVESTFLOWS, + m.PERIODS, + within=NonNegativeReals + ) + + # Old exogenous capacity to be decommissioned (due to lifetime) + self.old_exo = Var( + self.INVESTFLOWS, + m.PERIODS, + within=NonNegativeReals + ) + # create status variable for a non-convex investment flow - self.invest_status = Var(self.NON_CONVEX_INVESTFLOWS, within=Binary) - # ######################### CONSTRAINTS ############################### + self.invest_status = Var( + self.NON_CONVEX_INVESTFLOWS, + m.PERIODS, + within=Binary + ) - def _min_invest_rule(block, i, o): + # ######################### CONSTRAINTS ############################### + def _min_invest_rule(block): """Rule definition for applying a minimum investment""" - expr = ( - m.flows[i, o].investment.minimum * self.invest_status[i, o] - <= self.invest[i, o] - ) - return expr + for i, o in self.NON_CONVEX_INVESTFLOWS: + for p in m.PERIODS: + expr = ( + m.flows[i, o].investment.minimum[p] + * self.invest_status[i, o, p] + <= self.invest[i, o, p] + ) + self.minimum_rule.add((i, o, p), expr) self.minimum_rule = Constraint( - self.NON_CONVEX_INVESTFLOWS, rule=_min_invest_rule + self.NON_CONVEX_INVESTFLOWS, m.PERIODS, + noruleinit=True + ) + self.minimum_rule_build = BuildAction( + rule=_min_invest_rule ) - def _max_invest_rule(block, i, o): + def _max_invest_rule(block): """Rule definition for applying a minimum investment""" - expr = self.invest[i, o] <= ( - m.flows[i, o].investment.maximum * self.invest_status[i, o] - ) - return expr + for i, o in self.NON_CONVEX_INVESTFLOWS: + for p in m.PERIODS: + expr = ( + self.invest[i, o, p] + <= (m.flows[i, o].investment.maximum[p] + * self.invest_status[i, o, p]) + ) + self.maximum_rule.add((i, o, p), expr) self.maximum_rule = Constraint( - self.NON_CONVEX_INVESTFLOWS, rule=_max_invest_rule + self.NON_CONVEX_INVESTFLOWS, m.PERIODS, + noruleinit=True + ) + self.maximum_rule_build = BuildAction( + rule=_max_invest_rule + ) + + # Handle unit lifetimes + def _total_capacity_rule(block): + """Rule definition for determining total installed + capacity (taking decommissioning into account) + """ + for i, o in self.INVESTFLOWS: + for p in m.PERIODS: + if p == 0: + expr = (self.total[i, o, p] + == self.invest[i, o, p] + + m.flows[i, o].investment.existing) + self.total_rule.add((i, o, p), expr) + else: + expr = (self.total[i, o, p] + == self.invest[i, o, p] + + self.total[i, o, p - 1] + - self.old[i, o, p]) + self.total_rule.add((i, o, p), expr) + + self.total_rule = Constraint( + self.INVESTFLOWS, m.PERIODS, + noruleinit=True + ) + self.total_rule_build = BuildAction( + rule=_total_capacity_rule) + + def _old_capacity_rule_end(block): + """Rule definition for determining old endogenously installed + capacity to be decommissioned due to reaching its lifetime + """ + for i, o in self.INVESTFLOWS: + lifetime = m.flows[i, o].investment.lifetime + for p in m.PERIODS: + if lifetime <= p: + expr = (self.old_end[i, o, p] + == self.invest[i, o, p - lifetime]) + self.old_rule_end.add((i, o, p), expr) + else: + expr = (self.old_end[i, o, p] == 0) + self.old_rule_end.add((i, o, p), expr) + + self.old_rule_end = Constraint( + self.INVESTFLOWS, m.PERIODS, + noruleinit=True + ) + self.old_rule_end_build = BuildAction( + rule=_old_capacity_rule_end + ) + + def _old_capacity_rule_exo(block): + """Rule definition for determining old exogenously given capacity + to be decommissioned due to reaching its lifetime + """ + for i, o in self.INVESTFLOWS: + age = m.flows[i, o].investment.age + lifetime = m.flows[i, o].investment.lifetime + for p in m.PERIODS: + if lifetime - age == p: + expr = ( + self.old_exo[i, o, p] + == m.flows[i, o].investment.existing) + self.old_rule_exo.add((i, o, p), expr) + else: + expr = (self.old_exo[i, o, p] == 0) + self.old_rule_exo.add((i, o, p), expr) + + self.old_rule_exo = Constraint( + self.INVESTFLOWS, m.PERIODS, + noruleinit=True + ) + self.old_rule_exo_build = BuildAction( + rule=_old_capacity_rule_exo + ) + + def _old_capacity_rule(block): + """Rule definition for determining (overall) old capacity + to be decommissioned due to reaching its lifetime + """ + for i, o in self.INVESTFLOWS: + for p in m.PERIODS: + expr = ( + self.old[i, o, p] + == self.old_end[i, o, p] + self.old_exo[i, o, p]) + self.old_rule.add((i, o, p), expr) + + self.old_rule = Constraint( + self.INVESTFLOWS, m.PERIODS, + noruleinit=True + ) + self.old_rule_build = BuildAction( + rule=_old_capacity_rule ) - def _investflow_fixed_rule(block, i, o, t): + def _investflow_fixed_rule(block): """Rule definition of constraint to fix flow variable of investment flow to (normed) actual value """ - expr = m.flow[i, o, t] == ( - (m.flows[i, o].investment.existing + self.invest[i, o]) - * m.flows[i, o].fix[t] - ) - - return expr + for i, o in self.FIXED_INVESTFLOWS: + for p, t in m.TIMEINDEX: + expr = ( + m.flow[i, o, p, t] + == self.total[i, o, p] * m.flows[i, o].fix[t] + ) + self.fixed.add((i, o, p, t), expr) self.fixed = Constraint( - self.FIXED_INVESTFLOWS, m.TIMESTEPS, rule=_investflow_fixed_rule + self.FIXED_INVESTFLOWS, + m.TIMEINDEX, + noruleinit=True + ) + self.fixed_build = BuildAction( + rule=_investflow_fixed_rule ) - def _max_investflow_rule(block, i, o, t): + def _max_investflow_rule(block): """Rule definition of constraint setting an upper bound of flow variable in investment case. """ - expr = m.flow[i, o, t] <= ( - (m.flows[i, o].investment.existing + self.invest[i, o]) - * m.flows[i, o].max[t] - ) - return expr + for i, o in self.NON_FIXED_INVESTFLOWS: + for p, t in m.TIMEINDEX: + expr = ( + m.flow[i, o, p, t] + <= self.total[i, o, p] * m.flows[i, o].max[t] + ) + self.max.add((i, o, p, t), expr) self.max = Constraint( - self.NON_FIXED_INVESTFLOWS, m.TIMESTEPS, rule=_max_investflow_rule + self.NON_FIXED_INVESTFLOWS, + m.TIMEINDEX, + noruleinit=True + ) + self.max_build = BuildAction( + rule=_max_investflow_rule ) - def _min_investflow_rule(block, i, o, t): + def _min_investflow_rule(block): """Rule definition of constraint setting a lower bound on flow variable in investment case. """ - expr = m.flow[i, o, t] >= ( - (m.flows[i, o].investment.existing + self.invest[i, o]) - * m.flows[i, o].min[t] - ) - return expr + for i, o in self.MIN_INVESTFLOWS: + for p, t in m.TIMEINDEX: + expr = ( + m.flow[i, o, p, t] + >= self.total[i, o, p] * m.flows[i, o].min[t] + ) + self.min.add((i, o, p, t), expr) self.min = Constraint( - self.MIN_INVESTFLOWS, m.TIMESTEPS, rule=_min_investflow_rule + self.MIN_INVESTFLOWS, + m.TIMEINDEX, + noruleinit=True + ) + self.min_build = BuildAction( + rule=_min_investflow_rule ) def _summed_max_investflow_rule(block, i, o): """Rule definition for build action of max. sum flow constraint in investment case. """ - expr = sum( - m.flow[i, o, t] * m.timeincrement[t] for t in m.TIMESTEPS - ) <= m.flows[i, o].summed_max * ( - self.invest[i, o] + m.flows[i, o].investment.existing + expr = ( + sum(m.flow[i, o, p, t] * m.timeincrement[t] + for p, t in m.TIMEINDEX) + <= (m.flows[i, o].summed_max + * sum(self.total[i, o, p] for p in m.PERIODS)) ) return expr self.summed_max = Constraint( - self.SUMMED_MAX_INVESTFLOWS, rule=_summed_max_investflow_rule + self.SUMMED_MAX_INVESTFLOWS, + rule=_summed_max_investflow_rule ) def _summed_min_investflow_rule(block, i, o): """Rule definition for build action of min. sum flow constraint in investment case. """ - expr = sum( - m.flow[i, o, t] * m.timeincrement[t] for t in m.TIMESTEPS - ) >= ( - (m.flows[i, o].investment.existing + self.invest[i, o]) - * m.flows[i, o].summed_min + expr = ( + sum(m.flow[i, o, p, t] * m.timeincrement[t] + for p, t in m.TIMEINDEX) + >= (sum(self.total[i, o, p] for p in m.PERIODS) + * m.flows[i, o].summed_min) ) return expr self.summed_min = Constraint( - self.SUMMED_MIN_INVESTFLOWS, rule=_summed_min_investflow_rule + self.SUMMED_MIN_INVESTFLOWS, + rule=_summed_min_investflow_rule + ) + + def _overall_maximum_investflow_rule(block): + """Rule definition for maximum overall investment + in investment case. + + Note: In general, there are two different options to define + an overall maximum: + 1.) overall_max = limit for (net) installed capacity + for each period. This is the constraint used here + 2.) overall max = sum of all (gross) investments occurring + """ + for i, o in self.OVERALL_MAXIMUM_INVESTFLOWS: + for p in m.PERIODS: + expr = ( + self.total[i, o, p] + <= m.flows[i, o].investment.overall_maximum + ) + self.overall_maximum.add((i, o, p), expr) + + self.overall_maximum = Constraint( + self.OVERALL_MAXIMUM_INVESTFLOWS, + m.PERIODS, + noruleinit=True + ) + self.overall_maximum_build = BuildAction( + rule=_overall_maximum_investflow_rule + ) + + def _overall_minimum_investflow_rule(block, i, o): + """Rule definition for minimum overall investment + in investment case. + + Note: This is only applicable for the last period + """ + expr = ( + m.flows[i, o].investment.overall_minimum + <= self.total[i, o, m.PERIODS[-1]] + ) + return expr + + self.overall_minimum = Constraint( + self.OVERALL_MINIMUM_INVESTFLOWS, + rule=_overall_minimum_investflow_rule ) def _objective_expression(self): r"""Objective expression for flows with investment attribute of type - class:`.Investment`. The returned costs are fixed, variable and - investment costs. + class:`.Investment`. The returned costs are fixed and + investment costs. Variable costs are added from the standard flow + objective expression. """ if not hasattr(self, "INVESTFLOWS"): return 0 m = self.parent_block() investment_costs = 0 - - for i, o in self.CONVEX_INVESTFLOWS: - investment_costs += ( - self.invest[i, o] * m.flows[i, o].investment.ep_costs - ) - for i, o in self.NON_CONVEX_INVESTFLOWS: - investment_costs += ( - self.invest[i, o] * m.flows[i, o].investment.ep_costs - + self.invest_status[i, o] * m.flows[i, o].investment.offset - ) + period_investment_costs = {p: 0 for p in m.PERIODS} + fixed_costs = 0 + + if not m.es.multi_period: + for i, o in self.CONVEX_INVESTFLOWS: + for p in m.PERIODS: + investment_costs += ( + self.invest[i, o, p] + * m.flows[i, o].investment.ep_costs[p] + ) + + for i, o in self.NON_CONVEX_INVESTFLOWS: + for p in m.PERIODS: + investment_costs += ( + self.invest[i, o, p] + * m.flows[i, o].investment.ep_costs[p] + + self.invest_status[i, o, p] + * m.flows[i, o].investment.offset[p] + ) + + else: + msg = ("You did not specify an interest rate.\n" + "It will be set equal to the discount_rate of {} " + "of the model as a default.\nThis corresponds to a " + "social planner point of view and does not reflect " + "microeconomic interest requirements.") + + for i, o in self.CONVEX_INVESTFLOWS: + lifetime = m.flows[i, o].investment.lifetime + interest = m.flows[i, o].investment.interest_rate + if interest == 0: + warn(msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning) + interest = m.discount_rate + for p in m.PERIODS: + annuity = economics.annuity( + capex=m.flows[i, o].investment.ep_costs[p], + n=lifetime, + wacc=interest + ) + investment_costs_increment = ( + self.invest[i, o, p] * annuity * lifetime + * ((1 + m.discount_rate) ** (-p)) + ) + investment_costs += investment_costs_increment + period_investment_costs[p] += investment_costs_increment + + for i, o in self.NON_CONVEX_INVESTFLOWS: + lifetime = m.flows[i, o].investment.lifetime + interest = m.flows[i, o].investment.interest_rate + if interest == 0: + warn(msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning) + interest = m.discount_rate + for p in m.PERIODS: + annuity = economics.annuity( + capex=m.flows[i, o].investment.ep_costs[p], + n=lifetime, + wacc=interest + ) + investment_costs_increment = ( + (self.invest[i, o, p] * annuity * lifetime + + self.invest_status[i, o, p] + * m.flows[i, o].investment.offset[p]) + * ((1 + m.discount_rate) ** (-p)) + ) + investment_costs += investment_costs_increment + period_investment_costs[p] += investment_costs_increment + + for i, o in self.INVESTFLOWS: + if m.flows[i, o].investment.fixed_costs[0] is not None: + lifetime = m.flows[i, o].investment.lifetime + for p in m.PERIODS: + fixed_costs += ( + sum(self.invest[i, o, p] + * m.flows[i, o].investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(p, p + lifetime)) + * ((1 + m.discount_rate) ** (-p)) + ) self.investment_costs = Expression(expr=investment_costs) - return investment_costs + self.period_investment_costs = period_investment_costs + self.costs = Expression(expr=investment_costs + fixed_costs) + + return self.costs diff --git a/src/oemof/solph/flows/_non_convex_flow.py b/src/oemof/solph/flows/_non_convex_flow.py index f0a15cd98..7c13e239c 100644 --- a/src/oemof/solph/flows/_non_convex_flow.py +++ b/src/oemof/solph/flows/_non_convex_flow.py @@ -10,7 +10,7 @@ SPDX-FileCopyrightText: Birgit Schachler SPDX-FileCopyrightText: jnnr SPDX-FileCopyrightText: jmloenneberga -SPDX-FileCopyrightText: Johannes Kochems (jokochems) +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -433,32 +433,32 @@ def _create(self, group=None): self.NEGATIVE_GRADIENT_FLOWS, m.TIMESTEPS ) - def _minimum_flow_rule(block, i, o, t): + def _minimum_flow_rule(block, i, o, p, t): """Rule definition for MILP minimum flow constraints.""" expr = ( self.status[i, o, t] * m.flows[i, o].min[t] * m.flows[i, o].nominal_value - <= m.flow[i, o, t] + <= m.flow[i, o, p, t] ) return expr self.min = Constraint( - self.MIN_FLOWS, m.TIMESTEPS, rule=_minimum_flow_rule + self.MIN_FLOWS, m.TIMEINDEX, rule=_minimum_flow_rule ) - def _maximum_flow_rule(block, i, o, t): + def _maximum_flow_rule(block, i, o, p, t): """Rule definition for MILP maximum flow constraints.""" expr = ( self.status[i, o, t] * m.flows[i, o].max[t] * m.flows[i, o].nominal_value - >= m.flow[i, o, t] + >= m.flow[i, o, p, t] ) return expr self.max = Constraint( - self.MIN_FLOWS, m.TIMESTEPS, rule=_maximum_flow_rule + self.MIN_FLOWS, m.TIMEINDEX, rule=_maximum_flow_rule ) def _startup_rule(block, i, o, t): @@ -577,21 +577,28 @@ def _min_downtime_rule(block, i, o, t): def _positive_gradient_flow_rule(block): """Rule definition for positive gradient constraint.""" for i, o in self.POSITIVE_GRADIENT_FLOWS: - for t in m.TIMESTEPS: - if t > 0: + for index in range(1, len(m.TIMEINDEX) + 1): + if m.TIMEINDEX[index][1] > 0: lhs = ( - m.flow[i, o, t] * self.status[i, o, t] - - m.flow[i, o, t - 1] * self.status[i, o, t - 1] + m.flow[i, o, m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1]] + * self.status[i, o, m.TIMEINDEX[index][1]] + - m.flow[i, o, m.TIMEINDEX[index - 1][0], + m.TIMEINDEX[index - 1][1]] + * self.status[i, o, m.TIMEINDEX[index - 1][1]] ) - rhs = self.positive_gradient[i, o, t] + rhs = self.positive_gradient[i, o, + m.TIMEINDEX[index][1]] self.positive_gradient_constr.add( - (i, o, t), lhs <= rhs + (i, o, m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1]), + lhs <= rhs ) else: pass # return(Constraint.Skip) self.positive_gradient_constr = Constraint( - self.POSITIVE_GRADIENT_FLOWS, m.TIMESTEPS, noruleinit=True + self.POSITIVE_GRADIENT_FLOWS, m.TIMEINDEX, noruleinit=True ) self.positive_gradient_build = BuildAction( rule=_positive_gradient_flow_rule @@ -600,21 +607,28 @@ def _positive_gradient_flow_rule(block): def _negative_gradient_flow_rule(block): """Rule definition for negative gradient constraint.""" for i, o in self.NEGATIVE_GRADIENT_FLOWS: - for t in m.TIMESTEPS: - if t > 0: + for index in range(1, len(m.TIMEINDEX) + 1): + if m.TIMEINDEX[index][1] > 0: lhs = ( - m.flow[i, o, t - 1] * self.status[i, o, t - 1] - - m.flow[i, o, t] * self.status[i, o, t] + m.flow[i, o, m.TIMEINDEX[index - 1][0], + m.TIMEINDEX[index - 1][1]] + * self.status[i, o, m.TIMEINDEX[index - 1][1]] + - m.flow[i, o, m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1]] + * self.status[i, o, m.TIMEINDEX[index][1]] ) - rhs = self.negative_gradient[i, o, t] + rhs = self.negative_gradient[i, o, + m.TIMEINDEX[index][1]] self.negative_gradient_constr.add( - (i, o, t), lhs <= rhs + (i, o, m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1]), + lhs <= rhs ) else: pass # return(Constraint.Skip) self.negative_gradient_constr = Constraint( - self.NEGATIVE_GRADIENT_FLOWS, m.TIMESTEPS, noruleinit=True + self.NEGATIVE_GRADIENT_FLOWS, m.TIMEINDEX, noruleinit=True ) self.negative_gradient_build = BuildAction( rule=_negative_gradient_flow_rule @@ -633,76 +647,164 @@ def _objective_expression(self): inactivity_costs = 0 gradient_costs = 0 - if self.STARTUPFLOWS: - for i, o in self.STARTUPFLOWS: - if m.flows[i, o].nonconvex.startup_costs[0] is not None: - startup_costs += sum( - self.startup[i, o, t] - * m.flows[i, o].nonconvex.startup_costs[t] - for t in m.TIMESTEPS - ) - self.startup_costs = Expression(expr=startup_costs) + if not m.em.multi_period: + if self.STARTUPFLOWS: + for i, o in self.STARTUPFLOWS: + if m.flows[i, o].nonconvex.startup_costs[0] is not None: + startup_costs += sum( + self.startup[i, o, t] + * m.flows[i, o].nonconvex.startup_costs[t] + for t in m.TIMESTEPS + ) - if self.SHUTDOWNFLOWS: - for i, o in self.SHUTDOWNFLOWS: - if m.flows[i, o].nonconvex.shutdown_costs[0] is not None: - shutdown_costs += sum( - self.shutdown[i, o, t] - * m.flows[i, o].nonconvex.shutdown_costs[t] - for t in m.TIMESTEPS - ) - self.shutdown_costs = Expression(expr=shutdown_costs) - - if self.ACTIVITYCOSTFLOWS: - for i, o in self.ACTIVITYCOSTFLOWS: - if m.flows[i, o].nonconvex.activity_costs[0] is not None: - activity_costs += sum( - self.status[i, o, t] - * m.flows[i, o].nonconvex.activity_costs[t] - for t in m.TIMESTEPS - ) + if self.SHUTDOWNFLOWS: + for i, o in self.SHUTDOWNFLOWS: + if m.flows[i, o].nonconvex.shutdown_costs[0] is not None: + shutdown_costs += sum( + self.shutdown[i, o, t] + * m.flows[i, o].nonconvex.shutdown_costs[t] + for t in m.TIMESTEPS + ) + + if self.ACTIVITYCOSTFLOWS: + for i, o in self.ACTIVITYCOSTFLOWS: + if m.flows[i, o].nonconvex.activity_costs[0] is not None: + activity_costs += sum( + self.status[i, o, t] + * m.flows[i, o].nonconvex.activity_costs[t] + for t in m.TIMESTEPS + ) self.activity_costs = Expression(expr=activity_costs) - if self.INACTIVITYCOSTFLOWS: - for i, o in self.INACTIVITYCOSTFLOWS: - if m.flows[i, o].nonconvex.inactivity_costs[0] is not None: - inactivity_costs += sum( - (1 - self.status[i, o, t]) - * m.flows[i, o].nonconvex.inactivity_costs[t] - for t in m.TIMESTEPS - ) + if self.INACTIVITYCOSTFLOWS: + for i, o in self.INACTIVITYCOSTFLOWS: + if m.flows[i, o].nonconvex.inactivity_costs[0] is not None: + inactivity_costs += sum( + (1 - self.status[i, o, t]) + * m.flows[i, o].nonconvex.inactivity_costs[t] + for t in m.TIMESTEPS + ) - self.inactivity_costs = Expression(expr=inactivity_costs) + self.inactivity_costs = Expression(expr=inactivity_costs) + + if self.POSITIVE_GRADIENT_FLOWS: + for i, o in self.POSITIVE_GRADIENT_FLOWS: + if ( + m.flows[i, o].nonconvex.positive_gradient["ub"][0] + is not None + ): + for t in m.TIMESTEPS: + gradient_costs += ( + self.positive_gradient[i, o, t] * ( + m.flows[i, o].nonconvex.positive_gradient[ + "costs"] + ) + ) + + if self.NEGATIVE_GRADIENT_FLOWS: + for i, o in self.NEGATIVE_GRADIENT_FLOWS: + if ( + m.flows[i, o].nonconvex.negative_gradient["ub"][0] + is not None + ): + for t in m.TIMESTEPS: + gradient_costs += ( + self.negative_gradient[i, o, t] * ( + m.flows[i, o].nonconvex.negative_gradient[ + "costs"] + ) + ) + + else: + if self.STARTUPFLOWS: + for i, o in self.STARTUPFLOWS: + if (m.flows[i, o].nonconvex.startup_costs[0] + is not None): + startup_costs += sum( + self.startup[i, o, t] + * m.flows[i, o].nonconvex.startup_costs[t] + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + for p, t in m.TIMEINDEX + ) - if self.POSITIVE_GRADIENT_FLOWS: - for i, o in self.POSITIVE_GRADIENT_FLOWS: - if ( - m.flows[i, o].nonconvex.positive_gradient["ub"][0] - is not None - ): - for t in m.TIMESTEPS: - gradient_costs += self.positive_gradient[i, o, t] * ( - m.flows[i, o].nonconvex.positive_gradient["costs"] + if self.SHUTDOWNFLOWS: + for i, o in self.SHUTDOWNFLOWS: + if (m.flows[i, o].nonconvex.shutdown_costs[0] + is not None): + shutdown_costs += sum( + self.shutdown[i, o, t] + * m.flows[i, o].nonconvex.shutdown_costs[t] + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + for p, t in m.TIMEINDEX ) - if self.NEGATIVE_GRADIENT_FLOWS: - for i, o in self.NEGATIVE_GRADIENT_FLOWS: - if ( - m.flows[i, o].nonconvex.negative_gradient["ub"][0] - is not None - ): - for t in m.TIMESTEPS: - gradient_costs += self.negative_gradient[i, o, t] * ( - m.flows[i, o].nonconvex.negative_gradient["costs"] + if self.ACTIVITYCOSTFLOWS: + for i, o in self.ACTIVITYCOSTFLOWS: + if (m.flows[i, o].nonconvex.activity_costs[0] + is not None): + activity_costs += sum( + self.status[i, o, t] + * m.flows[i, o].nonconvex.activity_costs[t] + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + for p, t in m.TIMEINDEX ) - self.gradient_costs = Expression(expr=gradient_costs) + if self.INACTIVITYCOSTFLOWS: + for i, o in self.INACTIVITYCOSTFLOWS: + if m.flows[i, o].nonconvex.inactivity_costs[0] is not None: + inactivity_costs += sum( + (1 - self.status[i, o, t]) + * m.flows[i, o].nonconvex.inactivity_costs[t] + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + for p, t in m.TIMEINDEX + ) + + if self.POSITIVE_GRADIENT_FLOWS: + for i, o in self.POSITIVE_GRADIENT_FLOWS: + if ( + m.flows[i, o].nonconvex.positive_gradient["ub"][0] + is not None + ): + gradient_costs += sum( + self.positive_gradient[i, o, t] + * (m.flows[i, o].nonconvex + .positive_gradient["costs"]) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + for p, t in m.TIMEINDEX + ) - return ( - startup_costs - + shutdown_costs - + activity_costs - + inactivity_costs - + gradient_costs + if self.NEGATIVE_GRADIENT_FLOWS: + for i, o in self.NEGATIVE_GRADIENT_FLOWS: + if ( + (m.flows[i, o].nonconvex + .negative_gradient["ub"][0]) + is not None + ): + gradient_costs += sum( + self.negative_gradient[i, o, t] + * (m.flows[i, o].nonconvex + .negative_gradient["costs"]) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + for p, t in m.TIMEINDEX + ) + + self.activity_costs = Expression(expr=activity_costs) + self.inactivity_costs = Expression(expr=inactivity_costs) + self.gradient_costs = Expression(expr=gradient_costs) + self.startup_costs = Expression(expr=startup_costs) + self.shutdown_costs = Expression(expr=shutdown_costs) + + self.costs = Expression( + expr=( + startup_costs + shutdown_costs + + activity_costs + inactivity_costs + gradient_costs + ) ) + return self.costs diff --git a/src/oemof/solph/flows/experimental/_electrical_line.py b/src/oemof/solph/flows/experimental/_electrical_line.py index a320fa38b..275c9447c 100644 --- a/src/oemof/solph/flows/experimental/_electrical_line.py +++ b/src/oemof/solph/flows/experimental/_electrical_line.py @@ -151,13 +151,13 @@ def _voltage_angle_bounds(block, b, t): bus.slack = True def _voltage_angle_relation(block): - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for n in group: if n.input.slack is True: self.voltage_angle[n.output, t].value = 0 self.voltage_angle[n.output, t].fix() try: - lhs = m.flow[n.input, n.output, t] + lhs = m.flow[n.input, n.output, p, t] rhs = ( 1 / n.reactance[t] @@ -171,8 +171,8 @@ def _voltage_angle_relation(block): "Error in constraint creation", "of node {}".format(n.label), ) - block.electrical_flow.add((n, t), (lhs == rhs)) + block.electrical_flow.add((n, p, t), (lhs == rhs)) - self.electrical_flow = Constraint(group, m.TIMESTEPS, noruleinit=True) + self.electrical_flow = Constraint(group, m.TIMEINDEX, noruleinit=True) self.electrical_flow_build = BuildAction(rule=_voltage_angle_relation) From 6ce63959081a851a74ce0cb027df5b8a9de6bb13 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sun, 5 Dec 2021 19:00:29 +0100 Subject: [PATCH 0060/1363] Integrate multi-period draft into standard components --- .../components/_extraction_turbine_chp.py | 23 +- src/oemof/solph/components/_generic_chp.py | 21 +- .../solph/components/_generic_storage.py | 547 +++++++++++++++--- .../solph/components/_offset_transformer.py | 12 +- src/oemof/solph/components/_transformer.py | 13 +- 5 files changed, 497 insertions(+), 119 deletions(-) diff --git a/src/oemof/solph/components/_extraction_turbine_chp.py b/src/oemof/solph/components/_extraction_turbine_chp.py index 433f90002..e76716bdf 100644 --- a/src/oemof/solph/components/_extraction_turbine_chp.py +++ b/src/oemof/solph/components/_extraction_turbine_chp.py @@ -13,6 +13,7 @@ SPDX-FileCopyrightText: Stephan Günther SPDX-FileCopyrightText: FabianTU SPDX-FileCopyrightText: Johannes Röder +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -180,18 +181,18 @@ def _create(self, group=None): def _input_output_relation_rule(block): """Connection between input, main output and tapped output.""" - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] rhs = ( - m.flow[g, g.main_output, t] - + m.flow[g, g.tapped_output, t] + m.flow[g, g.main_output, p, t] + + m.flow[g, g.tapped_output, p, t] * g.main_flow_loss_index[t] ) / g.conversion_factor_full_condensation_sq[t] - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add((g, p, t), (lhs == rhs)) self.input_output_relation = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.input_output_relation_build = BuildAction( rule=_input_output_relation_rule @@ -199,17 +200,17 @@ def _input_output_relation_rule(block): def _out_flow_relation_rule(block): """Relation between main and tapped output in full chp mode.""" - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: - lhs = m.flow[g, g.main_output, t] + lhs = m.flow[g, g.main_output, p, t] rhs = ( - m.flow[g, g.tapped_output, t] + m.flow[g, g.tapped_output, p, t] * g.flow_relation_index[t] ) - block.out_flow_relation.add((g, t), (lhs >= rhs)) + block.out_flow_relation.add((g, p, t), (lhs >= rhs)) self.out_flow_relation = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.out_flow_relation_build = BuildAction( rule=_out_flow_relation_rule diff --git a/src/oemof/solph/components/_generic_chp.py b/src/oemof/solph/components/_generic_chp.py index 838543726..6f5bd663e 100644 --- a/src/oemof/solph/components/_generic_chp.py +++ b/src/oemof/solph/components/_generic_chp.py @@ -12,6 +12,7 @@ SPDX-FileCopyrightText: Stephan Günther SPDX-FileCopyrightText: FabianTU SPDX-FileCopyrightText: Johannes Röder +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -338,37 +339,37 @@ def _create(self, group=None): self.Y = Var(self.GENERICCHPS, m.TIMESTEPS, within=Binary) # constraint rules - def _H_flow_rule(block, n, t): + def _H_flow_rule(block, n, p, t): """Link fuel consumption to component inflow.""" expr = 0 expr += self.H_F[n, t] - expr += -m.flow[list(n.fuel_input.keys())[0], n, t] + expr += -m.flow[list(n.fuel_input.keys())[0], n, p, t] return expr == 0 self.H_flow = Constraint( - self.GENERICCHPS, m.TIMESTEPS, rule=_H_flow_rule + self.GENERICCHPS, m.TIMEINDEX, rule=_H_flow_rule ) - def _Q_flow_rule(block, n, t): + def _Q_flow_rule(block, n, p, t): """Link heat flow to component outflow.""" expr = 0 expr += self.Q[n, t] - expr += -m.flow[n, list(n.heat_output.keys())[0], t] + expr += -m.flow[n, list(n.heat_output.keys())[0], p, t] return expr == 0 self.Q_flow = Constraint( - self.GENERICCHPS, m.TIMESTEPS, rule=_Q_flow_rule + self.GENERICCHPS, m.TIMEINDEX, rule=_Q_flow_rule ) - def _P_flow_rule(block, n, t): + def _P_flow_rule(block, n, p, t): """Link power flow to component outflow.""" expr = 0 expr += self.P[n, t] - expr += -m.flow[n, list(n.electrical_output.keys())[0], t] + expr += -m.flow[n, list(n.electrical_output.keys())[0], p, t] return expr == 0 self.P_flow = Constraint( - self.GENERICCHPS, m.TIMESTEPS, rule=_P_flow_rule + self.GENERICCHPS, m.TIMEINDEX, rule=_P_flow_rule ) def _H_F_1_rule(block, n, t): @@ -497,7 +498,7 @@ def _objective_expression(self): r"""Objective expression for generic CHPs with no investment. Note: This adds nothing as variable costs are already - added in the Block :class:`FlowBlock`. + added in the Block :class:`Flow`. """ if not hasattr(self, "GENERICCHPS"): return 0 diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index 7525b7dc8..77f9d5943 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -12,14 +12,21 @@ SPDX-FileCopyrightText: Stephan Günther SPDX-FileCopyrightText: FabianTU SPDX-FileCopyrightText: Johannes Röder +SPDX-FileCopyrightText: Johannes Kochems +SPDX-FileCopyrightText: Johannes Giehl SPDX-License-Identifier: MIT """ +from warnings import warn + from oemof.network import network +from oemof.tools import debugging +from oemof.tools import economics from pyomo.core.base.block import SimpleBlock from pyomo.environ import Binary +from pyomo.environ import BuildAction from pyomo.environ import Constraint from pyomo.environ import Expression from pyomo.environ import NonNegativeReals @@ -152,6 +159,9 @@ def __init__( ) self.max_storage_level = solph_sequence(max_storage_level) self.min_storage_level = solph_sequence(min_storage_level) + self.fixed_costs = solph_sequence( + kwargs.get('fixed_costs', 0) + ) self.investment = kwargs.get("investment") self.invest_relation_input_output = kwargs.get( "invest_relation_input_output" @@ -163,6 +173,8 @@ def __init__( "invest_relation_output_capacity" ) self._invest_group = isinstance(self.investment, Investment) + self.lifetime_inflow = kwargs.get("lifetime_inflow", 20) + self.lifetime_outflow = kwargs.get("lifetime_outflow", 20) # Check number of flows. self._check_number_of_flows() @@ -244,6 +256,15 @@ def _check_invest_attributes(self): "or investment.minimum has to be non-zero." ) raise AttributeError(e3) + if ( + self.lifetime_inflow == 20 + or self.lifetime_outflow == 20 + ): + w1 = ("Using a lifetime of 20 periods," + " which is the default value.\nIf you don't consider a" + " multi-period investment model, you can safely ignore " + " this warning - all fine!") + warn(w1, debugging.SuspiciousUsageWarning) self._set_flows() @@ -443,7 +464,9 @@ def _storage_init_content_bound_rule(block, n): # ************* Constraints *************************** - reduced_timesteps = [x for x in m.TIMESTEPS if x > 0] + reduced_periods_timesteps = [ + (p, t) for (p, t) in m.TIMEINDEX if t > 0 + ] # storage balance constraint (first time step) def _storage_balance_first_rule(block, n): @@ -464,10 +487,10 @@ def _storage_balance_first_rule(block, n): ) expr += n.fixed_losses_absolute[0] * m.timeincrement[0] expr += ( - -m.flow[i[n], n, 0] * n.inflow_conversion_factor[0] + -m.flow[i[n], n, 0, 0] * n.inflow_conversion_factor[0] ) * m.timeincrement[0] expr += ( - m.flow[n, o[n], 0] / n.outflow_conversion_factor[0] + m.flow[n, o[n], 0, 0] / n.outflow_conversion_factor[0] ) * m.timeincrement[0] return expr == 0 @@ -476,7 +499,7 @@ def _storage_balance_first_rule(block, n): ) # storage balance constraint (every time step but the first) - def _storage_balance_rule(block, n, t): + def _storage_balance_rule(block, n, p, t): """ Rule definition for the storage balance of every storage n and every timestep but the first (t > 0). @@ -494,15 +517,16 @@ def _storage_balance_rule(block, n, t): ) expr += n.fixed_losses_absolute[t] * m.timeincrement[t] expr += ( - -m.flow[i[n], n, t] * n.inflow_conversion_factor[t] + -m.flow[i[n], n, p, t] * n.inflow_conversion_factor[t] ) * m.timeincrement[t] expr += ( - m.flow[n, o[n], t] / n.outflow_conversion_factor[t] + m.flow[n, o[n], p, t] / n.outflow_conversion_factor[t] ) * m.timeincrement[t] return expr == 0 self.balance = Constraint( - self.STORAGES, reduced_timesteps, rule=_storage_balance_rule + self.STORAGES, reduced_periods_timesteps, + rule=_storage_balance_rule ) def _balanced_storage_rule(block, n): @@ -519,34 +543,54 @@ def _balanced_storage_rule(block, n): self.STORAGES_BALANCED, rule=_balanced_storage_rule ) - def _power_coupled(block, n): + def _power_coupled(block): """ Rule definition for constraint to connect the input power and output power """ - expr = ( - m.InvestmentFlowBlock.invest[n, o[n]] - + m.flows[n, o[n]].investment.existing - ) * n.invest_relation_input_output == ( - m.InvestmentFlowBlock.invest[i[n], n] - + m.flows[i[n], n].investment.existing - ) - return expr + for n in self.STORAGES_WITH_INVEST_FLOW_REL: + for p in m.PERIODS: + expr = ( + m.InvestmentFlow.total[n, o[n], p] + ) * n.invest_relation_input_output == ( + m.InvestmentFlow.total[i[n], n, p] + ) + self.power_coupled.add((n, p), expr) self.power_coupled = Constraint( - self.STORAGES_WITH_INVEST_FLOW_REL, rule=_power_coupled + self.STORAGES_WITH_INVEST_FLOW_REL, + m.PERIODS, + noruleinit=True + ) + + self.power_coupled_build = BuildAction( + rule=_power_coupled ) def _objective_expression(self): r""" Objective expression for storages with no investment. - Note: This adds nothing as variable costs are already + Note: This adds nothing but fixed costs as variable costs are already added in the Block :class:`FlowBlock`. """ + m = self.parent_block() + if not hasattr(self, "STORAGES"): return 0 - return 0 + fixed_costs = 0 + + if m.es.multi_period: + for n in self.STORAGES: + if n.fixed_costs[0] is not None: + for p in m.PERIODS: + fixed_costs += ( + n.nominal_storage_capacity + * n.fixed_costs[p] + * ((1 + m.discount_rate) ** -p) + ) + self.fixed_costs = Expression(expr=fixed_costs) + return self.fixed_costs class GenericInvestmentStorageBlock(SimpleBlock): @@ -774,7 +818,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _create(self, group=None): - """ """ + """Create a storage block for investment modeling""" m = self.parent_block() if group is None: return None @@ -838,37 +882,215 @@ def _create(self, group=None): ] ) + self.OVERALL_MAXIMUM_INVESTSTORAGES = Set( + initialize=[ + n for n in group + if n.investment.overall_maximum is not None + ] + ) + + self.OVERALL_MINIMUM_INVESTSTORAGES = Set( + initialize=[ + n for n in group + if n.investment.overall_minimum is not None + ] + ) # ######################### Variables ################################ self.storage_content = Var( - self.INVESTSTORAGES, m.TIMESTEPS, within=NonNegativeReals + self.INVESTSTORAGES, m.TIMESTEPS, + within=NonNegativeReals ) - def _storage_investvar_bound_rule(block, n): + def _storage_investvar_bound_rule(block, n, p): """ Rule definition to bound the invested storage capacity `invest`. """ if n in self.CONVEX_INVESTSTORAGES: - return n.investment.minimum, n.investment.maximum + return n.investment.minimum[p], n.investment.maximum[p] elif n in self.NON_CONVEX_INVESTSTORAGES: - return 0, n.investment.maximum + return 0, n.investment.maximum[p] self.invest = Var( self.INVESTSTORAGES, + m.PERIODS, within=NonNegativeReals, bounds=_storage_investvar_bound_rule, ) - self.init_content = Var(self.INVESTSTORAGES, within=NonNegativeReals) + # Total capacity + self.total = Var( + self.INVESTSTORAGES, + m.PERIODS, + within=NonNegativeReals + ) + + # Old capacity to be decommissioned (due to lifetime) + # Old capacity is built out of old exogenous and endogenous capacities + self.old = Var( + self.INVESTSTORAGES, + m.PERIODS, + within=NonNegativeReals + ) + + # Old endogenous capacity to be decommissioned (due to lifetime) + self.old_end = Var( + self.INVESTSTORAGES, + m.PERIODS, + within=NonNegativeReals + ) + + # Old exogenous capacity to be decommissioned (due to lifetime) + self.old_exo = Var( + self.INVESTSTORAGES, + m.PERIODS, + within=NonNegativeReals + ) + + self.init_content = Var( + self.INVESTSTORAGES, + within=NonNegativeReals + ) # create status variable for a non-convex investment storage - self.invest_status = Var(self.NON_CONVEX_INVESTSTORAGES, within=Binary) + self.invest_status = Var( + self.NON_CONVEX_INVESTSTORAGES, + m.PERIODS, + within=Binary + ) # ######################### CONSTRAINTS ############################### i = {n: [i for i in n.inputs][0] for n in group} o = {n: [o for o in n.outputs][0] for n in group} - reduced_timesteps = [x for x in m.TIMESTEPS if x > 0] + reduced_periods_timesteps = [ + (p, t) for (p, t) in m.TIMEINDEX if t > 0 + ] + # Handle unit lifetimes + def _total_storage_capacity_rule(block): + """Rule definition for determining total installed + capacity (taking decommissioning into account) + """ + for n in self.INVESTSTORAGES: + for p in m.PERIODS: + if p == 0: + expr = (self.total[n, p] + == self.invest[n, p] + + n.investment.existing) + self.total_storage_rule.add((n, p), expr) + else: + expr = (self.total[n, p] + == self.invest[n, p] + + self.total[n, p - 1] + - self.old[n, p]) + self.total_storage_rule.add((n, p), expr) + + self.total_storage_rule = Constraint( + self.INVESTSTORAGES, + m.PERIODS, + noruleinit=True + ) + + self.total_storage_rule_build = BuildAction( + rule=_total_storage_capacity_rule + ) + + def _old_storage_capacity_rule_end(block): + """Rule definition for determining old endogenously installed + capacity to be decommissioned due to reaching its lifetime + """ + for n in self.INVESTSTORAGES: + lifetime = n.investment.lifetime + for p in m.PERIODS: + if lifetime <= p: + expr = (self.old_end[n, p] + == self.invest[n, p - lifetime]) + self.old_rule_end.add((n, p), expr) + else: + expr = (self.old_end[n, p] + == 0) + self.old_rule_end.add((n, p), expr) + + self.old_rule_end = Constraint( + self.INVESTSTORAGES, + m.PERIODS, + noruleinit=True + ) + + self.old_rule_end_build = BuildAction( + rule=_old_storage_capacity_rule_end + ) + + def _old_storage_capacity_rule_exo(block): + """Rule definition for determining old exogenously given capacity + to be decommissioned due to reaching its lifetime + """ + for n in self.INVESTSTORAGES: + age = n.investment.age + lifetime = n.investment.lifetime + for p in m.PERIODS: + if lifetime - age == p: + expr = ( + self.old_exo[n, p] + == n.investment.existing) + self.old_rule_exo.add((n, p), expr) + else: + expr = (self.old_exo[n, p] + == 0) + self.old_rule_exo.add((n, p), expr) + + self.old_rule_exo = Constraint( + self.INVESTSTORAGES, + m.PERIODS, + noruleinit=True + ) + + self.old_rule_exo_build = BuildAction( + rule=_old_storage_capacity_rule_exo + ) + + def _old_storage_capacity_rule(block): + """Rule definition for determining (overall) old capacity + to be decommissioned due to reaching its lifetime + """ + for n in self.INVESTSTORAGES: + for p in m.PERIODS: + expr = ( + self.old[n, p] + == self.old_end[n, p] + self.old_exo[n, p] + ) + self.old_rule.add((n, p), expr) + + self.old_rule = Constraint( + self.INVESTSTORAGES, + m.PERIODS, + noruleinit=True + ) + + self.old_rule_build = BuildAction( + rule=_old_storage_capacity_rule + ) + + def _storage_level_no_investments(block): + """Rule definition to force storage level to zero + as long as no investments have occurred + """ + for n in self.INVESTSTORAGES: + for p, t in m.TIMEINDEX: + expr = block.storage_content[n, t] <= self.total[n, p] + self.storage_level_noinv_rule.add((n, p, t), expr) + + self.storage_level_noinv_rule = Constraint( + self.INVESTSTORAGES, + m.TIMEINDEX, + noruleinit=True + ) + + self.storage_level_noinv_rule_build = BuildAction( + rule=_storage_level_no_investments + ) + + # TODO: Handle initial content ... make it a sequence?! def _inv_storage_init_content_max_rule(block, n): """Constraint for a variable initial storage capacity.""" return ( @@ -892,6 +1114,7 @@ def _inv_storage_init_content_fix_rule(block, n): rule=_inv_storage_init_content_fix_rule, ) + # TODO: Generalize! 0th step for the respective period / storage def _storage_balance_first_rule(block, n): """ Rule definition for the storage balance of every storage n for the @@ -905,23 +1128,24 @@ def _storage_balance_first_rule(block, n): ) expr += ( n.fixed_losses_relative[0] - * (n.investment.existing + self.invest[n]) + * (n.investment.existing + self.invest[n, 0]) * m.timeincrement[0] ) expr += n.fixed_losses_absolute[0] * m.timeincrement[0] expr += ( - -m.flow[i[n], n, 0] * n.inflow_conversion_factor[0] + -m.flow[i[n], n, 0, 0] * n.inflow_conversion_factor[0] ) * m.timeincrement[0] expr += ( - m.flow[n, o[n], 0] / n.outflow_conversion_factor[0] + m.flow[n, o[n], 0, 0] / n.outflow_conversion_factor[0] ) * m.timeincrement[0] return expr == 0 self.balance_first = Constraint( - self.INVESTSTORAGES, rule=_storage_balance_first_rule + self.INVESTSTORAGES, + rule=_storage_balance_first_rule ) - def _storage_balance_rule(block, n, t): + def _storage_balance_rule(block, n, p, t): """ Rule definition for the storage balance of every storage n for the every time step but the first. @@ -934,20 +1158,22 @@ def _storage_balance_rule(block, n, t): ) expr += ( n.fixed_losses_relative[t] - * (n.investment.existing + self.invest[n]) + * self.total[n, p] * m.timeincrement[t] ) expr += n.fixed_losses_absolute[t] * m.timeincrement[t] expr += ( - -m.flow[i[n], n, t] * n.inflow_conversion_factor[t] + -m.flow[i[n], n, p, t] * n.inflow_conversion_factor[t] ) * m.timeincrement[t] expr += ( - m.flow[n, o[n], t] / n.outflow_conversion_factor[t] + m.flow[n, o[n], p, t] / n.outflow_conversion_factor[t] ) * m.timeincrement[t] return expr == 0 self.balance = Constraint( - self.INVESTSTORAGES, reduced_timesteps, rule=_storage_balance_rule + self.INVESTSTORAGES, + reduced_periods_timesteps, + rule=_storage_balance_rule ) def _balanced_storage_rule(block, n): @@ -957,89 +1183,111 @@ def _balanced_storage_rule(block, n): ) self.balanced_cstr = Constraint( - self.INVESTSTORAGES_BALANCED, rule=_balanced_storage_rule + self.INVESTSTORAGES_BALANCED, + rule=_balanced_storage_rule ) - def _power_coupled(block, n): + def _power_coupled(block): """ Rule definition for constraint to connect the input power and output power """ - expr = ( - m.InvestmentFlowBlock.invest[n, o[n]] - + m.flows[n, o[n]].investment.existing - ) * n.invest_relation_input_output == ( - m.InvestmentFlowBlock.invest[i[n], n] - + m.flows[i[n], n].investment.existing - ) - return expr + for n in self.INVEST_REL_IN_OUT: + for p in m.PERIODS: + expr = ( + m.InvestmentFlow.total[n, o[n], p] + ) * n.invest_relation_input_output == ( + m.InvestmentFlow.total[i[n], n, p] + ) + self.power_coupled.add((n, p), expr) self.power_coupled = Constraint( - self.INVEST_REL_IN_OUT, rule=_power_coupled + self.INVEST_REL_IN_OUT, + m.PERIODS, + noruleinit=True + ) + + self.power_coupled_build = BuildAction( + rule=_power_coupled ) - def _storage_capacity_inflow_invest_rule(block, n): + def _storage_capacity_inflow_invest_rule(block): """ Rule definition of constraint connecting the inflow `InvestmentFlowBlock.invest of storage with invested capacity `invest` by nominal_storage_capacity__inflow_ratio """ - expr = ( - m.InvestmentFlowBlock.invest[i[n], n] - + m.flows[i[n], n].investment.existing - ) == ( - n.investment.existing + self.invest[n] - ) * n.invest_relation_input_capacity - return expr + for n in self.INVEST_REL_CAP_IN: + for p in m.PERIODS: + expr = ( + m.InvestmentFlow.total[i[n], n, p] + == self.total[n, p] + * n.invest_relation_input_capacity + ) + self.storage_capacity_inflow.add((n, p), expr) self.storage_capacity_inflow = Constraint( - self.INVEST_REL_CAP_IN, rule=_storage_capacity_inflow_invest_rule + self.INVEST_REL_CAP_IN, + m.PERIODS, + noruleinit=True + ) + + self.storage_capacity_inflow_build = BuildAction( + rule=_storage_capacity_inflow_invest_rule ) - def _storage_capacity_outflow_invest_rule(block, n): + def _storage_capacity_outflow_invest_rule(block): """ Rule definition of constraint connecting outflow `InvestmentFlowBlock.invest` of storage and invested capacity `invest` by nominal_storage_capacity__outflow_ratio """ - expr = ( - m.InvestmentFlowBlock.invest[n, o[n]] - + m.flows[n, o[n]].investment.existing - ) == ( - n.investment.existing + self.invest[n] - ) * n.invest_relation_output_capacity - return expr + for n in self.INVEST_REL_CAP_OUT: + for p in m.PERIODS: + expr = ( + ( + m.InvestmentFlow.total[n, o[n], p] + ) + == self.total[n, p] + * n.invest_relation_output_capacity + ) + self.storage_capacity_outflow.add((n, p), expr) self.storage_capacity_outflow = Constraint( - self.INVEST_REL_CAP_OUT, rule=_storage_capacity_outflow_invest_rule + self.INVEST_REL_CAP_OUT, + m.PERIODS, + noruleinit=True ) - def _max_storage_content_invest_rule(block, n, t): + self.storage_capacity_outflow_build = BuildAction( + rule=_storage_capacity_outflow_invest_rule) + + def _max_storage_content_invest_rule(block, n, p, t): """ Rule definition for upper bound constraint for the storage content. """ expr = ( self.storage_content[n, t] - <= (n.investment.existing + self.invest[n]) + <= self.total[n, p] * n.max_storage_level[t] ) return expr self.max_storage_content = Constraint( self.INVESTSTORAGES, - m.TIMESTEPS, + m.TIMEINDEX, rule=_max_storage_content_invest_rule, ) - def _min_storage_content_invest_rule(block, n, t): + def _min_storage_content_invest_rule(block, n, p, t): """ Rule definition of lower bound constraint for the storage content. """ expr = ( self.storage_content[n, t] - >= (n.investment.existing + self.invest[n]) + >= self.total[n, p] * n.min_storage_level[t] ) return expr @@ -1047,52 +1295,177 @@ def _min_storage_content_invest_rule(block, n, t): # Set the lower bound of the storage content if the attribute exists self.min_storage_content = Constraint( self.MIN_INVESTSTORAGES, - m.TIMESTEPS, + m.TIMEINDEX, rule=_min_storage_content_invest_rule, ) - def maximum_invest_limit(block, n): + def maximum_invest_limit(block, n, p): """ Constraint for the maximal investment in non convex investment storage. """ return ( - n.investment.maximum * self.invest_status[n] - self.invest[n] + n.investment.maximum[p] * self.invest_status[n, p] + - self.invest[n, p] ) >= 0 self.limit_max = Constraint( - self.NON_CONVEX_INVESTSTORAGES, rule=maximum_invest_limit + self.NON_CONVEX_INVESTSTORAGES, + m.PERIODS, + rule=maximum_invest_limit ) - def smallest_invest(block, n): + def smallest_invest(block, n, p): """ Constraint for the minimal investment in non convex investment storage if the invest is greater than 0. So the invest variable can be either 0 or greater than the minimum. """ return ( - self.invest[n] - (n.investment.minimum * self.invest_status[n]) + self.invest[n, p] + - n.investment.minimum[p] * self.invest_status[n, p] >= 0 ) self.limit_min = Constraint( - self.NON_CONVEX_INVESTSTORAGES, rule=smallest_invest + self.NON_CONVEX_INVESTSTORAGES, + m.PERIODS, + rule=smallest_invest + ) + + def _overall_storage_maximum_investflow_rule(block): + """Rule definition for maximum overall investment + in investment case. + + Note: There are two different options to define an overall maximum: + 1.) overall_max = limit for (net) installed capacity + for each period + 2.) overall max = sum of all (gross) investments occurring + """ + for n in self.OVERALL_MAXIMUM_INVESTSTORAGES: + for p in m.PERIODS: + expr = (self.total[n, p] + <= n.investment.overall_maximum) + self.overall_storage_maximum.add((n, p), expr) + + self.overall_storage_maximum = Constraint( + self.OVERALL_MAXIMUM_INVESTSTORAGES, + m.PERIODS, + noruleinit=True + ) + + self.overall_maximum_build = BuildAction( + rule=_overall_storage_maximum_investflow_rule + ) + + def _overall_minimum_investflow_rule(block): + """Rule definition for minimum overall investment + in investment case. + + Note: This is only applicable for the last period + """ + for n in self.OVERALL_MINIMUM_INVESTSTORAGES: + expr = (n.investment.overall_minimum + <= self.total[n, m.PERIODS[-1]]) + self.overall_minimum.add(n, expr) + + self.overall_minimum = Constraint( + self.OVERALL_MINIMUM_INVESTSTORAGES, + noruleinit=True + ) + + self.overall_minimum_build = BuildAction( + rule=_overall_minimum_investflow_rule ) def _objective_expression(self): """Objective expression with fixed and investement costs.""" + m = self.parent_block() + if not hasattr(self, "INVESTSTORAGES"): return 0 investment_costs = 0 + period_investment_costs = {p: 0 for p in m.PERIODS} + fixed_costs = 0 + + if not m.es.multi_period: + for n in self.CONVEX_INVESTSTORAGES: + for p in m.PERIODS: + investment_costs += ( + self.invest[n, p] * n.investment.ep_costs[p] + ) + for n in self.NON_CONVEX_INVESTSTORAGES: + for p in m.PERIODS: + investment_costs += ( + self.invest[n, p] * n.investment.ep_costs[p] + + self.invest_status[n, p] * n.investment.offset[p] + ) - for n in self.CONVEX_INVESTSTORAGES: - investment_costs += self.invest[n] * n.investment.ep_costs - for n in self.NON_CONVEX_INVESTSTORAGES: - investment_costs += ( - self.invest[n] * n.investment.ep_costs - + self.invest_status[n] * n.investment.offset - ) - self.investment_costs = Expression(expr=investment_costs) - - return investment_costs + else: + msg = ("You did not specify an interest rate.\n" + "It will be set equal to the discount_rate of {} " + "of the model as a default.\nThis corresponds to a " + "social planner point of view and does not reflect " + "microeconomic interest requirements.") + + for n in self.CONVEX_INVESTSTORAGES: + lifetime = n.investment.lifetime + interest = n.investment.interest_rate + if interest == 0: + warn(msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning) + interest = m.discount_rate + for p in m.PERIODS: + annuity = economics.annuity( + capex=n.investment.ep_costs[p], + n=lifetime, + wacc=interest + ) + investment_costs_increment = ( + self.invest[n, p] * annuity * lifetime + * ((1 + m.discount_rate) ** (-p)) + ) + investment_costs += investment_costs_increment + period_investment_costs[p] += investment_costs_increment + + for n in self.NON_CONVEX_INVESTSTORAGES: + lifetime = n.investment.lifetime + interest = n.investment.interest_rate + if interest == 0: + warn(msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning) + interest = m.discount_rate + for p in m.PERIODS: + annuity = economics.annuity( + capex=n.investment.ep_costs[p], + n=lifetime, + wacc=interest + ) + investment_costs_increment = ( + (self.invest[n, p] * annuity * lifetime + + self.invest_status[n, p] + * n.investment.offset[p]) + * ((1 + m.discount_rate) ** (-p)) + ) + investment_costs += investment_costs_increment + period_investment_costs[p] += investment_costs_increment + + for n in self.INVESTSTORAGES: + if n.investment.fixed_costs[0] is not None: + lifetime = n.investment.lifetime + for p in m.PERIODS: + fixed_costs += ( + sum(self.invest[n, p] + * n.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(p, p + lifetime) + ) + * ((1 + m.discount_rate) ** (-p)) + ) + + self.investment_costs = investment_costs + self.period_investment_costs = period_investment_costs + self.costs = Expression(expr=investment_costs + fixed_costs) + + return self.costs diff --git a/src/oemof/solph/components/_offset_transformer.py b/src/oemof/solph/components/_offset_transformer.py index 6923ebde5..5c0e27b84 100644 --- a/src/oemof/solph/components/_offset_transformer.py +++ b/src/oemof/solph/components/_offset_transformer.py @@ -12,6 +12,7 @@ SPDX-FileCopyrightText: Stephan Günther SPDX-FileCopyrightText: FabianTU SPDX-FileCopyrightText: Johannes Röder +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -144,19 +145,20 @@ def _create(self, group=None): self.OFFSETTRANSFORMERS = Set(initialize=[n for n in group]) - def _relation_rule(block, n, t): + def _relation_rule(block, n, p, t): """Link binary input and output flow to component outflow.""" expr = 0 - expr += -m.flow[n, list(n.outputs.keys())[0], t] + expr += -m.flow[n, list(n.outputs.keys())[0], p, t] expr += ( - m.flow[list(n.inputs.keys())[0], n, t] * n.coefficients[1][t] + m.flow[list(n.inputs.keys())[0], n, p, t] + * n.coefficients[1][t] ) expr += ( - m.NonConvexFlowBlock.status[list(n.inputs.keys())[0], n, t] + (m.NonConvexFlow.status[list(n.inputs.keys())[0], n, t]) * n.coefficients[0][t] ) return expr == 0 self.relation = Constraint( - self.OFFSETTRANSFORMERS, m.TIMESTEPS, rule=_relation_rule + self.OFFSETTRANSFORMERS, m.TIMEINDEX, rule=_relation_rule ) diff --git a/src/oemof/solph/components/_transformer.py b/src/oemof/solph/components/_transformer.py index 8c5e0d602..2a76cda29 100644 --- a/src/oemof/solph/components/_transformer.py +++ b/src/oemof/solph/components/_transformer.py @@ -13,6 +13,7 @@ SPDX-FileCopyrightText: Birgit Schachler SPDX-FileCopyrightText: jnnr SPDX-FileCopyrightText: jmloenneberga +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -163,8 +164,8 @@ def _create(self, group=None): self.relation = Constraint( [ - (n, i, o, t) - for t in m.TIMESTEPS + (n, i, o, p, t) + for p, t in m.TIMEINDEX for n in group for o in out_flows[n] for i in in_flows[n] @@ -173,17 +174,17 @@ def _create(self, group=None): ) def _input_output_relation(block): - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for n in group: for o in out_flows[n]: for i in in_flows[n]: try: lhs = ( - m.flow[i, n, t] + m.flow[i, n, p, t] * n.conversion_factors[o][t] ) rhs = ( - m.flow[n, o, t] + m.flow[n, o, p, t] * n.conversion_factors[i][t] ) except ValueError: @@ -193,6 +194,6 @@ def _input_output_relation(block): n.label, o.label ), ) - block.relation.add((n, i, o, t), (lhs == rhs)) + block.relation.add((n, i, o, p, t), (lhs == rhs)) self.relation_build = BuildAction(rule=_input_output_relation) From 8f7c2c8d1a6d1d7ca773c021f13a033fd72b6e81 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sun, 5 Dec 2021 19:16:09 +0100 Subject: [PATCH 0061/1363] Fix import --- src/oemof/solph/constraints/investment_limit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/solph/constraints/investment_limit.py b/src/oemof/solph/constraints/investment_limit.py index dce7d7e85..499a86c7d 100644 --- a/src/oemof/solph/constraints/investment_limit.py +++ b/src/oemof/solph/constraints/investment_limit.py @@ -15,7 +15,7 @@ from pyomo import environ as po -from oemof.solph import sequence +from oemof.solph._plumbing import sequence def investment_limit(model, limit=None): From 83f788bffb581772ee7d89128a34f288f88bc609 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sun, 5 Dec 2021 19:16:24 +0100 Subject: [PATCH 0062/1363] Fix bug --- src/oemof/solph/processing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index 077358b00..fadc2bc39 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -216,10 +216,10 @@ def results(om): # add dual variables for bus constraints if om.dual is not None: - grouped = groupby(sorted(om.Bus.balance.iterkeys()), + grouped = groupby(sorted(om.BusBlock.balance.iterkeys()), lambda p: p[0]) for bus, timeindex in grouped: - duals = [om.dual[om.Bus.balance[bus, p, t]] + duals = [om.dual[om.BusBlock.balance[bus, p, t]] for _, p, t in timeindex] df = pd.DataFrame({"duals": duals}, index=om.es.timeindex) if (bus, None) not in result.keys(): From 93dcf52ba7f8f8466d57930c6bb6c7164e65528f Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sun, 5 Dec 2021 19:37:45 +0100 Subject: [PATCH 0063/1363] Integrate multi-period draft into experimental components (but sink_dsm) --- .../components/experimental/_generic_caes.py | 19 +++-- .../solph/components/experimental/_link.py | 85 +++++++++++++++---- .../_piecewise_linear_transformer.py | 17 ++-- 3 files changed, 89 insertions(+), 32 deletions(-) diff --git a/src/oemof/solph/components/experimental/_generic_caes.py b/src/oemof/solph/components/experimental/_generic_caes.py index e9d8bf464..3be4dc705 100644 --- a/src/oemof/solph/components/experimental/_generic_caes.py +++ b/src/oemof/solph/components/experimental/_generic_caes.py @@ -11,6 +11,7 @@ SPDX-FileCopyrightText: jakob-wo SPDX-FileCopyrightText: gplssm SPDX-FileCopyrightText: jnnr +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -431,14 +432,14 @@ def _create(self, group=None): ) # Compression: Capacity on markets - def cmp_p_constr_rule(block, n, t): + def cmp_p_constr_rule(block, n, p, t): expr = 0 expr += -self.cmp_p[n, t] - expr += m.flow[list(n.electrical_input.keys())[0], n, t] + expr += m.flow[list(n.electrical_input.keys())[0], n, p, t] return expr == 0 self.cmp_p_constr = Constraint( - self.GENERICCAES, m.TIMESTEPS, rule=cmp_p_constr_rule + self.GENERICCAES, m.TIMEINDEX, rule=cmp_p_constr_rule ) # Compression: Max. capacity depending on cavern filling level @@ -521,14 +522,14 @@ def cmp_q_out_shr_constr_rule(block, n, t): ) # (10) Expansion: Capacity on markets - def exp_p_constr_rule(block, n, t): + def exp_p_constr_rule(block, n, p, t): expr = 0 expr += -self.exp_p[n, t] - expr += m.flow[n, list(n.electrical_output.keys())[0], t] + expr += m.flow[n, list(n.electrical_output.keys())[0], p, t] return expr == 0 self.exp_p_constr = Constraint( - self.GENERICCAES, m.TIMESTEPS, rule=exp_p_constr_rule + self.GENERICCAES, m.TIMEINDEX, rule=exp_p_constr_rule ) # (11-12) Expansion: Max. capacity depending on cavern filling level @@ -592,14 +593,14 @@ def exp_q_in_constr_rule(block, n, t): ) # (17) Expansion: Fuel allocation - def exp_q_fuel_constr_rule(block, n, t): + def exp_q_fuel_constr_rule(block, n, p, t): expr = 0 expr += -self.exp_q_fuel_in[n, t] - expr += m.flow[list(n.fuel_input.keys())[0], n, t] + expr += m.flow[list(n.fuel_input.keys())[0], n, p, t] return expr == 0 self.exp_q_fuel_constr = Constraint( - self.GENERICCAES, m.TIMESTEPS, rule=exp_q_fuel_constr_rule + self.GENERICCAES, m.TIMEINDEX, rule=exp_q_fuel_constr_rule ) # (18) Expansion: Definition of single heat flows diff --git a/src/oemof/solph/components/experimental/_link.py b/src/oemof/solph/components/experimental/_link.py index f9094d1a4..c98c76adb 100644 --- a/src/oemof/solph/components/experimental/_link.py +++ b/src/oemof/solph/components/experimental/_link.py @@ -12,12 +12,16 @@ SPDX-FileCopyrightText: jakob-wo SPDX-FileCopyrightText: gplssm SPDX-FileCopyrightText: jnnr +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT """ from oemof.network import network as on +from pyomo.core import Binary +from pyomo.core import Set +from pyomo.core import Var from pyomo.core.base.block import SimpleBlock from pyomo.environ import BuildAction from pyomo.environ import Constraint @@ -52,13 +56,12 @@ class Link(on.Transformer): >>> link = solph.components.experimental.Link( ... label="transshipment_link", - ... inputs={bel0: solph.flows.Flow(), - ... bel1: solph.flows.Flow()}, - ... outputs={bel0: solph.flows.Flow(), - ... bel1: solph.flows.Flow()}, - ... conversion_factors={(bel0, bel1): 0.92, (bel1, bel0): 0.99}) + ... inputs={bel0: solph.flows.Flow(nominal_value=4), + ... bel1: solph.flows.Flow(nominal_value=2)}, + ... outputs={bel0: solph.flows.Flow(), bel1: solph.flows.Flow()}, + ... conversion_factors={(bel0, bel1): 0.8, (bel1, bel0): 0.9}) >>> print(sorted([x[1][5] for x in link.conversion_factors.items()])) - [0.92, 0.99] + [0.8, 0.9] >>> type(link) @@ -67,7 +70,7 @@ class Link(on.Transformer): ['el0', 'el1'] >>> link.conversion_factors[(bel0, bel1)][3] - 0.92 + 0.8 """ def __init__(self, *args, **kwargs): @@ -99,8 +102,21 @@ class LinkBlock(SimpleBlock): **The following constraints are created:** - TODO: Add description for constraints - TODO: Add tests + .. _Link-equations: + + .. math:: + & + (1) \qquad P_{\mathrm{in},n}(t) = c_n(t) \times P_{\mathrm{out},n}(t) + \quad \forall t \in T, \forall n in {1,2} \\ + & + (2) \qquad 1 \ge \hat{S} + P_{\mathrm{in},1}(t) + / P_{\mathrm{in},1,\mathrm{max}} + \quad \forall t \in T \\ + & + (3) \qquad 0 \le \hat{S} - P_{\mathrm{in},2}(t) + / P_{2\mathrm{in},2,\mathrm{max}} + \quad \forall t \in T \\ + & """ CONSTRAINT_GROUP = True @@ -131,14 +147,25 @@ def _create(self, group=None): k: v for k, v in n.conversion_factors.items() } + self.LINKS = Set(initialize=[g for g in group]) + self.LINK_1ST_INFLOWS = Set( + initialize=[(list(c)[0][0], n) for n, c in all_conversions.items()] + ) + self.LINK_2ND_INFLOWS = Set( + initialize=[(list(c)[1][0], n) for n, c in all_conversions.items()] + ) + + # 0: Flows 1 connected; 1: Flows 2 connected + self.direction = Var(self.LINKS, m.TIMESTEPS, within=Binary) + def _input_output_relation(block): - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for n, conversion in all_conversions.items(): for cidx, c in conversion.items(): try: expr = ( - m.flow[n, cidx[1], t] - == c[t] * m.flow[cidx[0], n, t] + m.flow[n, cidx[1], p, t] + == c[t] * m.flow[cidx[0], n, p, t] ) except ValueError: raise ValueError( @@ -147,15 +174,43 @@ def _input_output_relation(block): cidx[0], cidx[1], n ), ) - block.relation.add((n, cidx[0], cidx[1], t), (expr)) + block.relation.add((n, cidx[0], cidx[1], p, t), expr) self.relation = Constraint( [ - (n, cidx[0], cidx[1], t) - for t in m.TIMESTEPS + (n, cidx[0], cidx[1], p, t) + for p, t in m.TIMEINDEX for n, conversion in all_conversions.items() for cidx, c in conversion.items() ], noruleinit=True, ) self.relation_build = BuildAction(rule=_input_output_relation) + + def _flow1_rule(block, i, link, p, t): + """Rule definition for Eq. (2).""" + expr = 1 >= ( + self.direction[link, t] + + m.flow[i, link, p, t] + * m.flows[i, link].max[t] + * m.flows[i, link].nominal_value + ) + return expr + + self.flow1 = Constraint( + self.LINK_1ST_INFLOWS, m.TIMEINDEX, rule=_flow1_rule + ) + + def _flow2_rule(block, i, link, p, t): + """Rule definition for Eq. (3).""" + expr = 0 <= ( + self.direction[link, t] + - m.flow[i, link, p, t] + * m.flows[i, link].max[t] + * m.flows[i, link].nominal_value + ) + return expr + + self.flow2 = Constraint( + self.LINK_2ND_INFLOWS, m.TIMEINDEX, rule=_flow2_rule + ) diff --git a/src/oemof/solph/components/experimental/_piecewise_linear_transformer.py b/src/oemof/solph/components/experimental/_piecewise_linear_transformer.py index bc72c4aa6..1e3567015 100644 --- a/src/oemof/solph/components/experimental/_piecewise_linear_transformer.py +++ b/src/oemof/solph/components/experimental/_piecewise_linear_transformer.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ -In-development transfomer with piecewise linar efficiencies. +In-development transformer with piecewise linear efficiencies. SPDX-FileCopyrightText: Uwe Krien SPDX-FileCopyrightText: Simon Hilpert @@ -11,6 +11,7 @@ SPDX-FileCopyrightText: jakob-wo SPDX-FileCopyrightText: gplssm SPDX-FileCopyrightText: jnnr +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -125,7 +126,7 @@ def _create(self, group=None): self.pw_repn = pw_repns[0] else: print( - "Cannot different piecewise representations ", + "Cannot model different piecewise representations ", [n.pw_repn for n in group], ) @@ -168,26 +169,26 @@ def get_outflow_bounds(model, n, t): self.PWLINEARTRANSFORMERS, m.TIMESTEPS, bounds=get_outflow_bounds ) - def _in_equation(block, n, t): + def _in_equation(block, n, p, t): """Link binary input and output flow to component outflow.""" expr = 0 - expr += -m.flow[list(n.inputs.keys())[0], n, t] + expr += -m.flow[list(n.inputs.keys())[0], n, p, t] expr += self.inflow[n, t] return expr == 0 self.equate_in = Constraint( - self.PWLINEARTRANSFORMERS, m.TIMESTEPS, rule=_in_equation + self.PWLINEARTRANSFORMERS, m.TIMEINDEX, rule=_in_equation ) - def _out_equation(block, n, t): + def _out_equation(block, n, p, t): """Link binary input and output flow to component outflow.""" expr = 0 - expr += -m.flow[n, list(n.outputs.keys())[0], t] + expr += -m.flow[n, list(n.outputs.keys())[0], p, t] expr += self.outflow[n, t] return expr == 0 self.equate_out = Constraint( - self.PWLINEARTRANSFORMERS, m.TIMESTEPS, rule=_out_equation + self.PWLINEARTRANSFORMERS, m.TIMEINDEX, rule=_out_equation ) self.piecewise = Piecewise( From 3e9484adbc9461499d7e5d72b0654f6e8121a83f Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sun, 5 Dec 2021 19:51:06 +0100 Subject: [PATCH 0064/1363] Begin adapting _sink_dsm.py --- .../components/experimental/_sink_dsm.py | 516 +++++++++++++----- 1 file changed, 385 insertions(+), 131 deletions(-) diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index 608f465b9..891acf0a3 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -17,8 +17,11 @@ """ import itertools +from warnings import warn from numpy import mean +from oemof.tools import debugging +from oemof.tools import economics from pyomo.core.base.block import SimpleBlock from pyomo.environ import BuildAction from pyomo.environ import Constraint @@ -78,7 +81,7 @@ class SinkDSM(Sink): For investment modeling, it is advised to use the maximum of the demand timeseries and the cumulated (fixed) infeed time series for normalization, because the balancing potential may be determined by - both. Elsewhise, underinvestments may occur. + both. Elsewise, underinvestments may occur. capacity_up: int or array maximum DSM capacity that may be increased (normalized) capacity_down: int or array @@ -179,7 +182,7 @@ class SinkDSM(Sink): Maximum number of load sheds at full capacity per year, used to limit the amount of energy shedded per year. Mandatory parameter if load shedding is allowed by setting shed_eligibility to True - t_dayLimit: int + t_dayLimit : int Only used when :attr:`~approach` is set to 'DLR'. Maximum duration of load shifts at full capacity per day, used to limit the amount of energy shifted per day. Optional parameter that is only @@ -199,6 +202,8 @@ class SinkDSM(Sink): shift_eligibility : boolean Boolean parameter indicating whether unit is eligible for load shifting + fixed_costs : numeric + Nominal value of fixed costs (per period) Note ---- @@ -254,6 +259,7 @@ def __init__( fixes=True, shed_eligibility=True, shift_eligibility=True, + fixed_costs=0, **kwargs, ): super().__init__(**kwargs) @@ -281,9 +287,9 @@ def __init__( self.flex_share_down = flex_share_down else: e1 = ( - "Please determine either **flex_share_down " + "Please determine either flex_share_down " "(investment modeling)\n or set " - "**max_demand and **max_capacity_down " + "max_demand and max_capacity_down " "(dispatch modeling).\n" "Otherwise, overdetermination occurs." ) @@ -291,10 +297,10 @@ def __init__( else: if max_capacity_down is None or max_demand is None: e2 = ( - "If you do not specify **flex_share_down\n" + "If you do not specify flex_share_down\n" "which should be used for investment modeling,\n" - "you have to specify **max_capacity_down " - "and **max_demand\n" + "you have to specify max_capacity_down " + "and max_demand\n" "instead which should be used for dispatch modeling." ) raise AttributeError(e2) @@ -315,10 +321,10 @@ def __init__( else: if max_capacity_up is None or max_demand is None: e4 = ( - "If you do not specify **flex_share_up\n" + "If you do not specify flex_share_up\n" "which should be used for investment modeling,\n" - "you have to specify **max_capacity_up " - "and **max_demand\n" + "you have to specify max_capacity_up " + "and max_demand\n" "instead which should be used for dispatch modeling." ) raise AttributeError(e4) @@ -342,6 +348,7 @@ def __init__( self.fixes = fixes self.shed_eligibility = shed_eligibility self.shift_eligibility = shift_eligibility + self.fixed_costs = sequence(fixed_costs) # Check whether investment mode is active or not self.investment = kwargs.get("investment") @@ -354,10 +361,10 @@ def __init__( ) and not self._invest_group: e5 = ( "If you are setting up a dispatch model, " - "you have to specify **max_demand**, **max_capacity_up** " - "and **max_capacity_down**.\n" - "The values you might have passed for **flex_share_up** " - "and **flex_share_down** will be ignored and only used in " + "you have to specify max_demand, max_capacity_up " + "and max_capacity_down.\n" + "The values you might have passed for flex_share_up " + "and flex_share_down will be ignored and only used in " "an investment model." ) raise AttributeError(e5) @@ -377,10 +384,10 @@ def _check_invest_attributes(self): ): e6 = ( "If an investment object is defined, the invest variable " - "replaces the **max_demand, the **max_capacity_down " + "replaces the **max_demand, the max_capacity_down " "as well as\n" - "the **max_capacity_up values. Therefore, **max_demand,\n" - "**max_capacity_up and **max_capacity_down values should be " + "the max_capacity_up values. Therefore, max_demand,\n" + "max_capacity_up and max_capacity_down values should be " "'None'.\n" ) raise AttributeError(e6) @@ -391,18 +398,19 @@ def constraint_group(self): if self.approach in [possible_approaches[0], possible_approaches[1]]: if self.delay_time is None: raise ValueError( - "Please define: **delay_time" " is a mandatory parameter" + "Please define: delay_time.\n" + "It is a mandatory parameter" ) if not self.shed_eligibility and not self.shift_eligibility: raise ValueError( - "At least one of **shed_eligibility" - " and **shift_eligibility must be True" + "At least one of shed_eligibility" + " and shift_eligibility must be True" ) if self.shed_eligibility: if self.recovery_time_shed is None: raise ValueError( "If unit is eligible for load shedding," - " **recovery_time_shed must be defined" + " recovery_time_shed must be defined" ) if self.approach == possible_approaches[0]: @@ -420,8 +428,9 @@ def constraint_group(self): elif self.approach == possible_approaches[2]: if self.shift_interval is None: raise ValueError( - "Please define: **shift_interval" - " is a mandatory parameter" + f"Please define: shift_interval.\n" + f"It is a mandatory parameter when using" + f" approach {self.approach}." ) if self._invest_group is True: return SinkDSMOemofInvestmentBlock @@ -509,6 +518,8 @@ class SinkDSMOemofBlock(SimpleBlock): levelled out" ":math:`\eta`",":attr:`~SinkDSM.efficiency`","P", "Efficiency loss forload shifting processes" + ":math:`\eta`",":attr:`efficiency`","P", "Efficiency loss for + load shifting processes" ":math:`\mathbb{T}` "," ","P", "Time steps" ":math:`eligibility_{shift}` ", ":attr:`~SinkDSM.shift_eligibility`","P", @@ -551,13 +562,14 @@ def _create(self, group=None): # Variable load shift down self.dsm_do_shift = Var( - self.dsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals + self.dsm, m.TIMESTEPS, initialize=0, + within=NonNegativeReals ) # Variable load shedding self.dsm_do_shed = Var( - self.dsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals - ) + self.dsm, m.TIMESTEPS, initialize=0, + within=NonNegativeReals) # Variable load shift up self.dsm_up = Var( @@ -594,10 +606,10 @@ def _input_output_relation_rule(block): The actual demand after DSM. Generator Production == Demand_el +- DSM """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # Inflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand + DSM_up - DSM_down rhs = ( @@ -608,10 +620,10 @@ def _input_output_relation_rule(block): ) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add((g, p, t), (lhs == rhs)) self.input_output_relation = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.input_output_relation_build = BuildAction( rule=_input_output_relation_rule @@ -701,23 +713,52 @@ def _objective_expression(self): m = self.parent_block() - dsm_cost = 0 + variable_costs = 0 + fixed_costs = 0 - for t in m.TIMESTEPS: + if not m.es.multi_period: + for t in m.TIMESTEPS: + for g in self.dsm: + variable_costs += ( + self.dsm_up[g, t] + * g.cost_dsm_up[t] + * m.objective_weighting[t] + ) + variable_costs += ( + self.dsm_do_shift[g, t] * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) * m.objective_weighting[t] + + else: for g in self.dsm: - dsm_cost += ( - self.dsm_up[g, t] - * g.cost_dsm_up[t] - * m.objective_weighting[t] - ) - dsm_cost += ( - self.dsm_do_shift[g, t] * g.cost_dsm_down_shift[t] - + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] - ) * m.objective_weighting[t] + for p, t in m.TIMEINDEX: + variable_costs += ( + self.dsm_up[g, t] + * m.objective_weighting[t] + * g.cost_dsm_up[p] + * ((1 + m.discount_rate) ** -p) + ) + variable_costs += ( + (self.dsm_do_shift[g, t] + * g.cost_dsm_down_shift[p] + + self.dsm_do_shed[g, t] + * g.cost_dsm_down_shed[p]) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + ) - self.cost = Expression(expr=dsm_cost) + if g.fixed_costs[0] is not None: + for p in m.PERIODS: + fixed_costs += ( + g.max_demand + * max(g.demand) + * g.fixed_costs[p] + * ((1 + m.discount_rate) ** (-p)) + ) - return self.cost + self.costs = Expression(expr=variable_costs + fixed_costs) + + return self.costs class SinkDSMOemofInvestmentBlock(SimpleBlock): @@ -828,36 +869,158 @@ def _create(self, group=None): # ************* VARIABLES ***************************** # Define bounds for investments in demand response - def _dsm_investvar_bound_rule(block, g): + def _dsm_investvar_bound_rule(block, g, p): """Rule definition to bound the invested demand response capacity `invest`. """ - return g.investment.minimum, g.investment.maximum + return g.investment.minimum[p], g.investment.maximum[p] # Investment in DR capacity self.invest = Var( self.investdsm, + m.PERIODS, within=NonNegativeReals, - bounds=_dsm_investvar_bound_rule, + bounds=_dsm_investvar_bound_rule + ) + + # Total capacity + self.total = Var( + self.investdsm, + m.PERIODS, + within=NonNegativeReals + ) + + # Old capacity to be decommissioned (due to lifetime) + # Old capacity is built out of old exogenous and endogenous capacities + self.old = Var( + self.investdsm, + m.PERIODS, + within=NonNegativeReals + ) + + # Old endogenous capacity to be decommissioned (due to lifetime) + self.old_end = Var( + self.investdsm, + m.PERIODS, + within=NonNegativeReals + ) + + # Old exogenous capacity to be decommissioned (due to lifetime) + self.old_exo = Var( + self.investdsm, + m.PERIODS, + within=NonNegativeReals ) # Variable load shift down self.dsm_do_shift = Var( - self.investdsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals + self.investdsm, m.TIMESTEPS, + initialize=0, + within=NonNegativeReals ) # Variable load shedding self.dsm_do_shed = Var( - self.investdsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals + self.investdsm, m.TIMESTEPS, initialize=0, + within=NonNegativeReals ) # Variable load shift up self.dsm_up = Var( - self.investdsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals + self.investdsm, m.TIMESTEPS, + initialize=0, + within=NonNegativeReals ) # ************* CONSTRAINTS ***************************** + # Handle unit lifetimes + def _total_dsm_capacity_rule(block): + """Rule definition for determining total installed + capacity (taking decommissioning into account) + """ + for g in group: + for p in m.PERIODS: + if p == 0: + expr = (self.total[g, p] + == self.invest[g, p] + + g.investment.existing) + self.total_dsm_rule.add((g, p), expr) + else: + expr = (self.total[g, p] + == self.invest[g, p] + + self.total[g, p - 1] + - self.old[g, p]) + self.total_dsm_rule.add((g, p), expr) + + self.total_dsm_rule = Constraint(group, m.PERIODS, + noruleinit=True) + self.total_dsm_rule_build = BuildAction( + rule=_total_dsm_capacity_rule) + + def _old_dsm_capacity_rule_end(block): + """Rule definition for determining old endogenously installed + capacity to be decommissioned due to reaching its lifetime + """ + for g in group: + lifetime = g.investment.lifetime + for p in m.PERIODS: + if lifetime <= p: + expr = (self.old_end[g, p] + == self.invest[g, p - lifetime]) + self.old_dsm_rule_end.add((g, p), expr) + else: + expr = (self.old_end[g, p] + == 0) + self.old_dsm_rule_end.add((g, p), expr) + + self.old_dsm_rule_end = Constraint(group, + m.PERIODS, + noruleinit=True) + self.old_dsm_rule_end_build = BuildAction( + rule=_old_dsm_capacity_rule_end) + + def _old_dsm_capacity_rule_exo(block): + """Rule definition for determining old exogenously given capacity + to be decommissioned due to reaching its lifetime + """ + for g in group: + age = g.investment.age + lifetime = g.investment.lifetime + for p in m.PERIODS: + if lifetime - age == p: + expr = ( + self.old_exo[g, p] + == g.investment.existing) + self.old_dsm_rule_exo.add((g, p), expr) + else: + expr = (self.old_exo[g, p] + == 0) + self.old_dsm_rule_exo.add((g, p), expr) + + self.old_dsm_rule_exo = Constraint(group, + m.PERIODS, + noruleinit=True) + self.old_dsm_rule_exo_build = BuildAction( + rule=_old_dsm_capacity_rule_exo) + + def _old_dsm_capacity_rule(block): + """Rule definition for determining (overall) old capacity + to be decommissioned due to reaching its lifetime + """ + for g in group: + for p in m.PERIODS: + expr = ( + self.old[g, p] + == self.old_end[g, p] + self.old_exo[g, p]) + self.old_dsm_rule.add((g, p), expr) + + self.old_dsm_rule = Constraint(group, + m.PERIODS, + noruleinit=True) + self.old_dsm_rule_build = BuildAction( + rule=_old_dsm_capacity_rule) + def _shift_shed_vars_rule(block): """Force shifting resp. shedding variables to zero dependent on how boolean parameters for shift resp. shed eligibility @@ -886,24 +1049,21 @@ def _input_output_relation_rule(block): The actual demand after DSM. Generator Production == Demand_el +- DSM """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # Inflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand + DSM_up - DSM_down - rhs = ( - g.demand[t] * (self.invest[g] + g.investment.existing) - + self.dsm_up[g, t] - - self.dsm_do_shift[g, t] - - self.dsm_do_shed[g, t] - ) + rhs = (g.demand[t] * self.total[g, p] + + self.dsm_up[g, t] - self.dsm_do_shift[g, t] + - self.dsm_do_shed[g, t]) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add((g, p, t), (lhs == rhs)) self.input_output_relation = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.input_output_relation_build = BuildAction( rule=_input_output_relation_rule @@ -914,23 +1074,19 @@ def dsm_up_constraint_rule(block): """Realised upward load shift at time t has to be smaller than upward DSM capacity at time t. """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # DSM up lhs = self.dsm_up[g, t] # Capacity dsm_up - rhs = ( - g.capacity_up[t] - * (self.invest[g] + g.investment.existing) - * g.flex_share_up - ) + rhs = (g.capacity_up[t] * self.total[g, p] + * g.flex_share_up) # add constraint - block.dsm_up_constraint.add((g, t), (lhs <= rhs)) + block.dsm_up_constraint.add((g, p, t), (lhs <= rhs)) - self.dsm_up_constraint = Constraint( - group, m.TIMESTEPS, noruleinit=True - ) + self.dsm_up_constraint = Constraint(group, m.TIMEINDEX, + noruleinit=True) self.dsm_up_constraint_build = BuildAction(rule=dsm_up_constraint_rule) # Upper bounds relation @@ -938,26 +1094,21 @@ def dsm_down_constraint_rule(block): """Realised downward load shift at time t has to be smaller than downward DSM capacity at time t. """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # DSM down lhs = self.dsm_do_shift[g, t] + self.dsm_do_shed[g, t] # Capacity dsm_down - rhs = ( - g.capacity_down[t] - * (self.invest[g] + g.investment.existing) - * g.flex_share_down - ) + rhs = (g.capacity_down[t] * self.total[g, p] + * g.flex_share_down) # add constraint - block.dsm_down_constraint.add((g, t), (lhs <= rhs)) + block.dsm_down_constraint.add((g, p, t), (lhs <= rhs)) - self.dsm_down_constraint = Constraint( - group, m.TIMESTEPS, noruleinit=True - ) + self.dsm_down_constraint = Constraint(group, m.TIMEINDEX, + noruleinit=True) self.dsm_down_constraint_build = BuildAction( - rule=dsm_down_constraint_rule - ) + rule=dsm_down_constraint_rule) def dsm_sum_constraint_rule(block): """Relation to compensate the total amount of positive @@ -1002,27 +1153,98 @@ def _objective_expression(self): m = self.parent_block() investment_costs = 0 + period_investment_costs = {p: 0 for p in m.PERIODS} variable_costs = 0 + fixed_costs = 0 + + if not m.es.multi_period: + for g in self.investdsm: + for p in m.PERIODS: + if g.investment.ep_costs is not None: + investment_costs += ( + self.invest[g, p] + * g.investment.ep_costs[p]) + else: + raise ValueError("Missing value for investment costs!") - for g in self.investdsm: - if g.investment.ep_costs is not None: - investment_costs += self.invest[g] * g.investment.ep_costs - else: - raise ValueError("Missing value for investment costs!") - for t in m.TIMESTEPS: - variable_costs += ( - self.dsm_up[g, t] - * g.cost_dsm_up[t] - * m.objective_weighting[t] - ) - variable_costs += ( - self.dsm_do_shift[g, t] * g.cost_dsm_down_shift[t] - + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] - ) * m.objective_weighting[t] + for t in m.TIMESTEPS: + variable_costs += ( + self.dsm_up[g, t] + * g.cost_dsm_up[t] + * m.objective_weighting[t] + ) + variable_costs += ( + self.dsm_do_shift[g, t] * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) * m.objective_weighting[t] - self.cost = Expression(expr=investment_costs + variable_costs) + else: + msg = ("You did not specify an interest rate.\n" + "It will be set equal to the discount_rate of {} " + "of the model as a default.\nThis corresponds to a " + "social planner point of view and does not reflect " + "microeconomic interest requirements.") + for g in self.investdsm: + if g.investment.ep_costs is not None: + lifetime = g.investment.lifetime + interest = g.investment.interest_rate + if interest == 0: + warn(msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning) + interest = m.discount_rate + for p in m.PERIODS: + annuity = economics.annuity( + capex=g.investment.ep_costs[p], + n=lifetime, + wacc=interest + ) + investment_costs_increment = ( + self.invest[g, p] * annuity * lifetime + * ((1 + m.discount_rate) ** (-p)) + ) + investment_costs += investment_costs_increment + period_investment_costs[p] += ( + investment_costs_increment + ) + else: + raise ValueError("Missing value for investment costs!") + + for p, t in m.TIMEINDEX: + variable_costs += ( + self.dsm_up[g, t] + * m.objective_weighting[t] + * g.cost_dsm_up[p] + * ((1 + m.discount_rate) ** -p) + ) + variable_costs += ( + (self.dsm_do_shift[g, t] + * g.cost_dsm_down_shift[p] + + self.dsm_do_shed[g, t] + * g.cost_dsm_down_shed[p]) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + ) - return self.cost + if g.investment.fixed_costs[0] is not None: + lifetime = g.investment.lifetime + for p in m.PERIODS: + fixed_costs += ( + sum(self.invest[g, p] + * max(g.demand) + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(p, p + lifetime) + ) + * ((1 + m.discount_rate) ** (-p)) + ) + + self.investment_costs = investment_costs + self.period_investment_costs = period_investment_costs + self.costs = Expression( + expr=investment_costs + variable_costs + fixed_costs + ) + + return self.costs class SinkDSMDIWBlock(SimpleBlock): @@ -1080,7 +1302,7 @@ class SinkDSMDIWBlock(SimpleBlock): .. math:: DSM_{t}^{up} \cdot cost_{t}^{dsm, up} - + \sum_{tt=0}^{|T|} DSM_{t, tt}^{do, shift} \cdot + + \sum_{tt=0}^{|T|} DSM_{tt, t}^{do, shift} \cdot cost_{t}^{dsm, do, shift} + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed} \quad \forall t \in \mathbb{T} \\ @@ -1218,13 +1440,13 @@ def _input_output_relation_rule(block): The actual demand after DSM. Sink Inflow == Demand +- DSM """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # first time steps: 0 + delay time if t <= g.delay_time: # Inflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand +- DSM rhs = ( g.demand[t] * g.max_demand @@ -1237,52 +1459,54 @@ def _input_output_relation_rule(block): ) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add( + (g, p, t), (lhs == rhs) + ) # main use case elif g.delay_time < t <= m.TIMESTEPS[-1] - g.delay_time: # Inflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand +- DSM rhs = ( g.demand[t] * g.max_demand + self.dsm_up[g, t] - sum( self.dsm_do_shift[g, tt, t] - for tt in range( - t - g.delay_time, t + g.delay_time + 1 - ) + for tt in range(t - g.delay_time, + t + g.delay_time + 1) ) - - self.dsm_do_shed[g, t] - ) + - self.dsm_do_shed[g, t]) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add( + (g, p, t), (lhs == rhs) + ) # last time steps: end - delay time else: # Inflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand +- DSM rhs = ( g.demand[t] * g.max_demand + self.dsm_up[g, t] - sum( self.dsm_do_shift[g, tt, t] - for tt in range( - t - g.delay_time, m.TIMESTEPS[-1] + 1 - ) + for tt in range(t - g.delay_time, + m.TIMESTEPS[-1] + 1) ) - - self.dsm_do_shed[g, t] - ) + - self.dsm_do_shed[g, t]) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add( + (g, p, t), (lhs == rhs) + ) self.input_output_relation = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.input_output_relation_build = BuildAction( rule=_input_output_relation_rule @@ -1650,24 +1874,54 @@ def _objective_expression(self): m = self.parent_block() - dsm_cost = 0 + variable_costs = 0 + fixed_costs = 0 - for t in m.TIMESTEPS: + if not m.es.multi_period: + for t in m.TIMESTEPS: + for g in self.dsm: + variable_costs += ( + self.dsm_up[g, t] + * g.cost_dsm_up[t] + * m.objective_weighting[t] + ) + variable_costs += ( + sum(self.dsm_do_shift[g, tt, t] for tt in m.TIMESTEPS) + * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) * m.objective_weighting[t] + + else: for g in self.dsm: - dsm_cost += ( - self.dsm_up[g, t] - * g.cost_dsm_up[t] - * m.objective_weighting[t] - ) - dsm_cost += ( - sum(self.dsm_do_shift[g, tt, t] for tt in m.TIMESTEPS) - * g.cost_dsm_down_shift[t] - + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] - ) * m.objective_weighting[t] + for p, t in m.TIMEINDEX: + variable_costs += ( + self.dsm_up[g, t] + * m.objective_weighting[t] + * g.cost_dsm_up[p] + * ((1 + m.discount_rate) ** -p) + ) + variable_costs += ( + (sum(self.dsm_do_shift[g, tt, t] + for tt in m.TIMESTEPS) + * g.cost_dsm_down_shift[p] + + self.dsm_do_shed[g, t] + * g.cost_dsm_down_shed[p]) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + ) + + if g.fixed_costs[0] is not None: + for p in m.PERIODS: + fixed_costs += ( + g.max_demand + * max(g.demand) + * g.fixed_costs[p] + * ((1 + m.discount_rate) ** (-p)) + ) - self.cost = Expression(expr=dsm_cost) + self.costs = Expression(expr=variable_costs + fixed_costs) - return self.cost + return self.costs class SinkDSMDIWInvestmentBlock(SimpleBlock): From cd8e99202619675bc96ef78065024b913798e15d Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 10 Dec 2021 09:29:38 +0100 Subject: [PATCH 0065/1363] Integrate multi-period into _sink_dsm.py and format using black --- .../components/experimental/_sink_dsm.py | 1244 +++++++++++------ 1 file changed, 832 insertions(+), 412 deletions(-) diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index 891acf0a3..a6018d98b 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -11,7 +11,7 @@ SPDX-FileCopyrightText: jakob-wo SPDX-FileCopyrightText: gplssm SPDX-FileCopyrightText: jnnr -SPDX-FileCopyrightText: Johannes Kochems (jokochems) +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -384,7 +384,7 @@ def _check_invest_attributes(self): ): e6 = ( "If an investment object is defined, the invest variable " - "replaces the **max_demand, the max_capacity_down " + "replaces the max_demand, the max_capacity_down " "as well as\n" "the max_capacity_up values. Therefore, max_demand,\n" "max_capacity_up and max_capacity_down values should be " @@ -393,25 +393,27 @@ def _check_invest_attributes(self): raise AttributeError(e6) def constraint_group(self): - possible_approaches = ["DIW", "DLR", "oemof"] + possible_approaches = ["DIW", "DLR", "oemof"] # - if self.approach in [possible_approaches[0], possible_approaches[1]]: - if self.delay_time is None: + if not self.shed_eligibility and not self.shift_eligibility: + raise ValueError( + "At least one of shed_eligibility" + " and shift_eligibility must be True" + ) + if self.shed_eligibility: + if self.recovery_time_shed is None: raise ValueError( - "Please define: delay_time.\n" - "It is a mandatory parameter" + "If unit is eligible for load shedding," + " recovery_time_shed must be defined" ) - if not self.shed_eligibility and not self.shift_eligibility: + + if self.approach in [possible_approaches[0], possible_approaches[1]]: + if self.delay_time is None: raise ValueError( - "At least one of shed_eligibility" - " and shift_eligibility must be True" + f"Please define: delay_time.\n" + f"It is a mandatory parameter when using" + f" approach {self.approach}." ) - if self.shed_eligibility: - if self.recovery_time_shed is None: - raise ValueError( - "If unit is eligible for load shedding," - " recovery_time_shed must be defined" - ) if self.approach == possible_approaches[0]: if self._invest_group is True: @@ -562,14 +564,13 @@ def _create(self, group=None): # Variable load shift down self.dsm_do_shift = Var( - self.dsm, m.TIMESTEPS, initialize=0, - within=NonNegativeReals + self.dsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals ) # Variable load shedding self.dsm_do_shed = Var( - self.dsm, m.TIMESTEPS, initialize=0, - within=NonNegativeReals) + self.dsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals + ) # Variable load shift up self.dsm_up = Var( @@ -735,14 +736,14 @@ def _objective_expression(self): variable_costs += ( self.dsm_up[g, t] * m.objective_weighting[t] - * g.cost_dsm_up[p] + * g.cost_dsm_up[t] * ((1 + m.discount_rate) ** -p) ) variable_costs += ( - (self.dsm_do_shift[g, t] - * g.cost_dsm_down_shift[p] - + self.dsm_do_shed[g, t] - * g.cost_dsm_down_shed[p]) + ( + self.dsm_do_shift[g, t] * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) * m.objective_weighting[t] * ((1 + m.discount_rate) ** -p) ) @@ -880,56 +881,35 @@ def _dsm_investvar_bound_rule(block, g, p): self.investdsm, m.PERIODS, within=NonNegativeReals, - bounds=_dsm_investvar_bound_rule + bounds=_dsm_investvar_bound_rule, ) # Total capacity - self.total = Var( - self.investdsm, - m.PERIODS, - within=NonNegativeReals - ) + self.total = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) # Old capacity to be decommissioned (due to lifetime) # Old capacity is built out of old exogenous and endogenous capacities - self.old = Var( - self.investdsm, - m.PERIODS, - within=NonNegativeReals - ) + self.old = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) # Old endogenous capacity to be decommissioned (due to lifetime) - self.old_end = Var( - self.investdsm, - m.PERIODS, - within=NonNegativeReals - ) + self.old_end = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) # Old exogenous capacity to be decommissioned (due to lifetime) - self.old_exo = Var( - self.investdsm, - m.PERIODS, - within=NonNegativeReals - ) + self.old_exo = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) # Variable load shift down self.dsm_do_shift = Var( - self.investdsm, m.TIMESTEPS, - initialize=0, - within=NonNegativeReals + self.investdsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals ) # Variable load shedding self.dsm_do_shed = Var( - self.investdsm, m.TIMESTEPS, initialize=0, - within=NonNegativeReals + self.investdsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals ) # Variable load shift up self.dsm_up = Var( - self.investdsm, m.TIMESTEPS, - initialize=0, - within=NonNegativeReals + self.investdsm, m.TIMESTEPS, initialize=0, within=NonNegativeReals ) # ************* CONSTRAINTS ***************************** @@ -942,21 +922,22 @@ def _total_dsm_capacity_rule(block): for g in group: for p in m.PERIODS: if p == 0: - expr = (self.total[g, p] - == self.invest[g, p] - + g.investment.existing) + expr = ( + self.total[g, p] + == self.invest[g, p] + g.investment.existing + ) self.total_dsm_rule.add((g, p), expr) else: - expr = (self.total[g, p] - == self.invest[g, p] - + self.total[g, p - 1] - - self.old[g, p]) + expr = ( + self.total[g, p] + == self.invest[g, p] + + self.total[g, p - 1] + - self.old[g, p] + ) self.total_dsm_rule.add((g, p), expr) - self.total_dsm_rule = Constraint(group, m.PERIODS, - noruleinit=True) - self.total_dsm_rule_build = BuildAction( - rule=_total_dsm_capacity_rule) + self.total_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) + self.total_dsm_rule_build = BuildAction(rule=_total_dsm_capacity_rule) def _old_dsm_capacity_rule_end(block): """Rule definition for determining old endogenously installed @@ -966,19 +947,18 @@ def _old_dsm_capacity_rule_end(block): lifetime = g.investment.lifetime for p in m.PERIODS: if lifetime <= p: - expr = (self.old_end[g, p] - == self.invest[g, p - lifetime]) + expr = ( + self.old_end[g, p] == self.invest[g, p - lifetime] + ) self.old_dsm_rule_end.add((g, p), expr) else: - expr = (self.old_end[g, p] - == 0) + expr = self.old_end[g, p] == 0 self.old_dsm_rule_end.add((g, p), expr) - self.old_dsm_rule_end = Constraint(group, - m.PERIODS, - noruleinit=True) + self.old_dsm_rule_end = Constraint(group, m.PERIODS, noruleinit=True) self.old_dsm_rule_end_build = BuildAction( - rule=_old_dsm_capacity_rule_end) + rule=_old_dsm_capacity_rule_end + ) def _old_dsm_capacity_rule_exo(block): """Rule definition for determining old exogenously given capacity @@ -989,20 +969,16 @@ def _old_dsm_capacity_rule_exo(block): lifetime = g.investment.lifetime for p in m.PERIODS: if lifetime - age == p: - expr = ( - self.old_exo[g, p] - == g.investment.existing) + expr = self.old_exo[g, p] == g.investment.existing self.old_dsm_rule_exo.add((g, p), expr) else: - expr = (self.old_exo[g, p] - == 0) + expr = self.old_exo[g, p] == 0 self.old_dsm_rule_exo.add((g, p), expr) - self.old_dsm_rule_exo = Constraint(group, - m.PERIODS, - noruleinit=True) + self.old_dsm_rule_exo = Constraint(group, m.PERIODS, noruleinit=True) self.old_dsm_rule_exo_build = BuildAction( - rule=_old_dsm_capacity_rule_exo) + rule=_old_dsm_capacity_rule_exo + ) def _old_dsm_capacity_rule(block): """Rule definition for determining (overall) old capacity @@ -1012,14 +988,12 @@ def _old_dsm_capacity_rule(block): for p in m.PERIODS: expr = ( self.old[g, p] - == self.old_end[g, p] + self.old_exo[g, p]) + == self.old_end[g, p] + self.old_exo[g, p] + ) self.old_dsm_rule.add((g, p), expr) - self.old_dsm_rule = Constraint(group, - m.PERIODS, - noruleinit=True) - self.old_dsm_rule_build = BuildAction( - rule=_old_dsm_capacity_rule) + self.old_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_build = BuildAction(rule=_old_dsm_capacity_rule) def _shift_shed_vars_rule(block): """Force shifting resp. shedding variables to zero dependent @@ -1055,9 +1029,12 @@ def _input_output_relation_rule(block): lhs = m.flow[g.inflow, g, p, t] # Demand + DSM_up - DSM_down - rhs = (g.demand[t] * self.total[g, p] - + self.dsm_up[g, t] - self.dsm_do_shift[g, t] - - self.dsm_do_shed[g, t]) + rhs = ( + g.demand[t] * self.total[g, p] + + self.dsm_up[g, t] + - self.dsm_do_shift[g, t] + - self.dsm_do_shed[g, t] + ) # add constraint block.input_output_relation.add((g, p, t), (lhs == rhs)) @@ -1079,14 +1056,14 @@ def dsm_up_constraint_rule(block): # DSM up lhs = self.dsm_up[g, t] # Capacity dsm_up - rhs = (g.capacity_up[t] * self.total[g, p] - * g.flex_share_up) + rhs = g.capacity_up[t] * self.total[g, p] * g.flex_share_up # add constraint block.dsm_up_constraint.add((g, p, t), (lhs <= rhs)) - self.dsm_up_constraint = Constraint(group, m.TIMEINDEX, - noruleinit=True) + self.dsm_up_constraint = Constraint( + group, m.TIMEINDEX, noruleinit=True + ) self.dsm_up_constraint_build = BuildAction(rule=dsm_up_constraint_rule) # Upper bounds relation @@ -1099,16 +1076,21 @@ def dsm_down_constraint_rule(block): # DSM down lhs = self.dsm_do_shift[g, t] + self.dsm_do_shed[g, t] # Capacity dsm_down - rhs = (g.capacity_down[t] * self.total[g, p] - * g.flex_share_down) + rhs = ( + g.capacity_down[t] + * self.total[g, p] + * g.flex_share_down + ) # add constraint block.dsm_down_constraint.add((g, p, t), (lhs <= rhs)) - self.dsm_down_constraint = Constraint(group, m.TIMEINDEX, - noruleinit=True) + self.dsm_down_constraint = Constraint( + group, m.TIMEINDEX, noruleinit=True + ) self.dsm_down_constraint_build = BuildAction( - rule=dsm_down_constraint_rule) + rule=dsm_down_constraint_rule + ) def dsm_sum_constraint_rule(block): """Relation to compensate the total amount of positive @@ -1162,8 +1144,8 @@ def _objective_expression(self): for p in m.PERIODS: if g.investment.ep_costs is not None: investment_costs += ( - self.invest[g, p] - * g.investment.ep_costs[p]) + self.invest[g, p] * g.investment.ep_costs[p] + ) else: raise ValueError("Missing value for investment costs!") @@ -1179,33 +1161,39 @@ def _objective_expression(self): ) * m.objective_weighting[t] else: - msg = ("You did not specify an interest rate.\n" - "It will be set equal to the discount_rate of {} " - "of the model as a default.\nThis corresponds to a " - "social planner point of view and does not reflect " - "microeconomic interest requirements.") + msg = ( + "You did not specify an interest rate.\n" + "It will be set equal to the discount_rate of {} " + "of the model as a default.\nThis corresponds to a " + "social planner point of view and does not reflect " + "microeconomic interest requirements." + ) for g in self.investdsm: if g.investment.ep_costs is not None: lifetime = g.investment.lifetime interest = g.investment.interest_rate if interest == 0: - warn(msg.format(m.discount_rate), - debugging.SuspiciousUsageWarning) + warn( + msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning, + ) interest = m.discount_rate for p in m.PERIODS: annuity = economics.annuity( capex=g.investment.ep_costs[p], n=lifetime, - wacc=interest + wacc=interest, ) investment_costs_increment = ( - self.invest[g, p] * annuity * lifetime + self.invest[g, p] + * annuity + * lifetime * ((1 + m.discount_rate) ** (-p)) ) investment_costs += investment_costs_increment - period_investment_costs[p] += ( - investment_costs_increment - ) + period_investment_costs[ + p + ] += investment_costs_increment else: raise ValueError("Missing value for investment costs!") @@ -1213,14 +1201,14 @@ def _objective_expression(self): variable_costs += ( self.dsm_up[g, t] * m.objective_weighting[t] - * g.cost_dsm_up[p] + * g.cost_dsm_up[t] * ((1 + m.discount_rate) ** -p) ) variable_costs += ( - (self.dsm_do_shift[g, t] - * g.cost_dsm_down_shift[p] - + self.dsm_do_shed[g, t] - * g.cost_dsm_down_shed[p]) + ( + self.dsm_do_shift[g, t] * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) * m.objective_weighting[t] * ((1 + m.discount_rate) ** -p) ) @@ -1228,15 +1216,13 @@ def _objective_expression(self): if g.investment.fixed_costs[0] is not None: lifetime = g.investment.lifetime for p in m.PERIODS: - fixed_costs += ( - sum(self.invest[g, p] - * max(g.demand) - * g.investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range(p, p + lifetime) - ) - * ((1 + m.discount_rate) ** (-p)) - ) + fixed_costs += sum( + self.invest[g, p] + * max(g.demand) + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(p, p + lifetime) + ) * ((1 + m.discount_rate) ** (-p)) self.investment_costs = investment_costs self.period_investment_costs = period_investment_costs @@ -1474,10 +1460,12 @@ def _input_output_relation_rule(block): + self.dsm_up[g, t] - sum( self.dsm_do_shift[g, tt, t] - for tt in range(t - g.delay_time, - t + g.delay_time + 1) + for tt in range( + t - g.delay_time, t + g.delay_time + 1 + ) ) - - self.dsm_do_shed[g, t]) + - self.dsm_do_shed[g, t] + ) # add constraint block.input_output_relation.add( @@ -1495,10 +1483,12 @@ def _input_output_relation_rule(block): + self.dsm_up[g, t] - sum( self.dsm_do_shift[g, tt, t] - for tt in range(t - g.delay_time, - m.TIMESTEPS[-1] + 1) + for tt in range( + t - g.delay_time, m.TIMESTEPS[-1] + 1 + ) ) - - self.dsm_do_shed[g, t]) + - self.dsm_do_shed[g, t] + ) # add constraint block.input_output_relation.add( @@ -1897,15 +1887,18 @@ def _objective_expression(self): variable_costs += ( self.dsm_up[g, t] * m.objective_weighting[t] - * g.cost_dsm_up[p] + * g.cost_dsm_up[t] * ((1 + m.discount_rate) ** -p) ) variable_costs += ( - (sum(self.dsm_do_shift[g, tt, t] - for tt in m.TIMESTEPS) - * g.cost_dsm_down_shift[p] - + self.dsm_do_shed[g, t] - * g.cost_dsm_down_shed[p]) + ( + sum( + self.dsm_do_shift[g, tt, t] + for tt in m.TIMESTEPS + ) + * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) * m.objective_weighting[t] * ((1 + m.discount_rate) ** -p) ) @@ -1994,7 +1987,7 @@ class SinkDSMDIWInvestmentBlock(SimpleBlock): .. math:: DSM_{t}^{up} \cdot cost_{t}^{dsm, up} - + \sum_{tt=0}^{T} DSM_{t, tt}^{do, shift} \cdot + + \sum_{tt=0}^{T} DSM_{tt, t}^{do, shift} \cdot cost_{t}^{dsm, do, shift} + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed} \quad \forall t \in \mathbb{T} @@ -2058,15 +2051,29 @@ def _dsm_investvar_bound_rule(block, g): """Rule definition to bound the demand response capacity invested in (`invest`). """ - return g.investment.minimum, g.investment.maximum + return g.investment.minimum[p], g.investment.maximum[p] # Investment in DR capacity self.invest = Var( self.investdsm, + m.PERIODS, within=NonNegativeReals, bounds=_dsm_investvar_bound_rule, ) + # Total capacity + self.total = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) + + # Old capacity to be decommissioned (due to lifetime) + # Old capacity is built out of old exogenous and endogenous capacities + self.old = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) + + # Old endogenous capacity to be decommissioned (due to lifetime) + self.old_end = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) + + # Old exogenous capacity to be decommissioned (due to lifetime) + self.old_exo = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) + # Variable load shift down self.dsm_do_shift = Var( self.investdsm, @@ -2088,6 +2095,87 @@ def _dsm_investvar_bound_rule(block, g): # ************* CONSTRAINTS ***************************** + # Handle unit lifetimes + def _total_dsm_capacity_rule(block): + """Rule definition for determining total installed + capacity (taking decommissioning into account) + """ + for g in group: + for p in m.PERIODS: + if p == 0: + expr = ( + self.total[g, p] + == self.invest[g, p] + g.investment.existing + ) + self.total_dsm_rule.add((g, p), expr) + else: + expr = ( + self.total[g, p] + == self.invest[g, p] + + self.total[g, p - 1] + - self.old[g, p] + ) + self.total_dsm_rule.add((g, p), expr) + + self.total_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) + self.total_dsm_rule_build = BuildAction(rule=_total_dsm_capacity_rule) + + def _old_dsm_capacity_rule_end(block): + """Rule definition for determining old endogenously installed + capacity to be decommissioned due to reaching its lifetime + """ + for g in group: + lifetime = g.investment.lifetime + for p in m.PERIODS: + if lifetime <= p: + expr = ( + self.old_end[g, p] == self.invest[g, p - lifetime] + ) + self.old_dsm_rule_end.add((g, p), expr) + else: + expr = self.old_end[g, p] == 0 + self.old_dsm_rule_end.add((g, p), expr) + + self.old_dsm_rule_end = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_end_build = BuildAction( + rule=_old_dsm_capacity_rule_end + ) + + def _old_dsm_capacity_rule_exo(block): + """Rule definition for determining old exogenously given capacity + to be decommissioned due to reaching its lifetime + """ + for g in group: + age = g.investment.age + lifetime = g.investment.lifetime + for p in m.PERIODS: + if lifetime - age == p: + expr = self.old_exo[g, p] == g.investment.existing + self.old_dsm_rule_exo.add((g, p), expr) + else: + expr = self.old_exo[g, p] == 0 + self.old_dsm_rule_exo.add((g, p), expr) + + self.old_dsm_rule_exo = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_exo_build = BuildAction( + rule=_old_dsm_capacity_rule_exo + ) + + def _old_dsm_capacity_rule(block): + """Rule definition for determining (overall) old capacity + to be decommissioned due to reaching its lifetime + """ + for g in group: + for p in m.PERIODS: + expr = ( + self.old[g, p] + == self.old_end[g, p] + self.old_exo[g, p] + ) + self.old_dsm_rule.add((g, p), expr) + + self.old_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_build = BuildAction(rule=_old_dsm_capacity_rule) + def _shift_shed_vars_rule(block): """Force shifting resp. shedding variables to zero dependent on how boolean parameters for shift resp. shed eligibility @@ -2117,18 +2205,17 @@ def _input_output_relation_rule(block): The actual demand after DSM. Sink Inflow == Demand +- DSM """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # first time steps: 0 + delay time if t <= g.delay_time: # Inflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand +- DSM rhs = ( - g.demand[t] - * (self.invest[g] + g.investment.existing) + g.demand[t] * self.total[g, p] + self.dsm_up[g, t] - sum( self.dsm_do_shift[g, tt, t] @@ -2138,17 +2225,18 @@ def _input_output_relation_rule(block): ) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add( + (g, p, t), (lhs == rhs) + ) # main use case elif g.delay_time < t <= m.TIMESTEPS[-1] - g.delay_time: # Inflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand +- DSM rhs = ( - g.demand[t] - * (self.invest[g] + g.investment.existing) + g.demand[t] * self.total[g, p] + self.dsm_up[g, t] - sum( self.dsm_do_shift[g, tt, t] @@ -2160,16 +2248,17 @@ def _input_output_relation_rule(block): ) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add( + (g, p, t), (lhs == rhs) + ) # last time steps: end - delay time else: # Inflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand +- DSM rhs = ( - g.demand[t] - * (self.invest[g] + g.investment.existing) + g.demand[t] * self.total[g, p] + self.dsm_up[g, t] - sum( self.dsm_do_shift[g, tt, t] @@ -2181,10 +2270,12 @@ def _input_output_relation_rule(block): ) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add( + (g, p, t), (lhs == rhs) + ) self.input_output_relation = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.input_output_relation_build = BuildAction( rule=_input_output_relation_rule @@ -2261,22 +2352,18 @@ def dsm_up_constraint_rule(block): Realised upward load shift at time t has to be smaller than upward DSM capacity at time t. """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # DSM up lhs = self.dsm_up[g, t] # Capacity dsm_up - rhs = ( - g.capacity_up[t] - * (self.invest[g] + g.investment.existing) - * g.flex_share_up - ) + rhs = g.capacity_up[t] * self.total[g, p] * g.flex_share_up # add constraint - block.dsm_up_constraint.add((g, t), (lhs <= rhs)) + block.dsm_up_constraint.add((g, p, t), (lhs <= rhs)) self.dsm_up_constraint = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.dsm_up_constraint_build = BuildAction(rule=dsm_up_constraint_rule) @@ -2286,7 +2373,7 @@ def dsm_do_constraint_rule(block): Realised downward load shift at time t has to be smaller than downward DSM capacity at time t. """ - for tt in m.TIMESTEPS: + for p, tt in m.TIMEINDEX: for g in group: # first times steps: 0 + delay @@ -2303,12 +2390,12 @@ def dsm_do_constraint_rule(block): # Capacity DSM down rhs = ( g.capacity_down[tt] - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_down ) # add constraint - block.dsm_do_constraint.add((g, tt), (lhs <= rhs)) + block.dsm_do_constraint.add((g, p, tt), (lhs <= rhs)) # main use case elif g.delay_time < tt <= m.TIMESTEPS[-1] - g.delay_time: @@ -2326,12 +2413,12 @@ def dsm_do_constraint_rule(block): # Capacity DSM down rhs = ( g.capacity_down[tt] - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_down ) # add constraint - block.dsm_do_constraint.add((g, tt), (lhs <= rhs)) + block.dsm_do_constraint.add((g, p, tt), (lhs <= rhs)) # last time steps: end - delay time else: @@ -2349,15 +2436,15 @@ def dsm_do_constraint_rule(block): # Capacity DSM down rhs = ( g.capacity_down[tt] - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_down ) # add constraint - block.dsm_do_constraint.add((g, tt), (lhs <= rhs)) + block.dsm_do_constraint.add((g, p, tt), (lhs <= rhs)) self.dsm_do_constraint = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.dsm_do_constraint_build = BuildAction(rule=dsm_do_constraint_rule) @@ -2369,7 +2456,7 @@ def c2_constraint_rule(block): total each individual DSM unit within the modeled portfolio can only be shifted up OR down at a given time. """ - for tt in m.TIMESTEPS: + for p, tt in m.TIMEINDEX: for g in group: # first times steps: 0 + delay time @@ -2390,11 +2477,11 @@ def c2_constraint_rule(block): g.capacity_up[tt] * g.flex_share_up, g.capacity_down[tt] * g.flex_share_down, ) - * (self.invest[g] + g.investment.existing) + * self.total[g, p] ) # add constraint - block.C2_constraint.add((g, tt), (lhs <= rhs)) + block.C2_constraint.add((g, p, tt), (lhs <= rhs)) elif g.delay_time < tt <= m.TIMESTEPS[-1] - g.delay_time: @@ -2415,11 +2502,11 @@ def c2_constraint_rule(block): g.capacity_up[tt] * g.flex_share_up, g.capacity_down[tt] * g.flex_share_down, ) - * (self.invest[g] + g.investment.existing) + * self.total[g, p] ) # add constraint - block.C2_constraint.add((g, tt), (lhs <= rhs)) + block.C2_constraint.add((g, p, tt), (lhs <= rhs)) else: @@ -2440,13 +2527,13 @@ def c2_constraint_rule(block): g.capacity_up[tt] * g.flex_share_up, g.capacity_down[tt] * g.flex_share_down, ) - * (self.invest[g] + g.investment.existing) + * self.total[g, p] ) # add constraint - block.C2_constraint.add((g, tt), (lhs <= rhs)) + block.C2_constraint.add((g, p, tt), (lhs <= rhs)) - self.C2_constraint = Constraint(group, m.TIMESTEPS, noruleinit=True) + self.C2_constraint = Constraint(group, m.TIMEINDEX, noruleinit=True) self.C2_constraint_build = BuildAction(rule=c2_constraint_rule) def recovery_constraint_rule(block): @@ -2456,7 +2543,7 @@ def recovery_constraint_rule(block): may take place. Rule is only applicable if a recovery time is defined. """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # No need to build constraint if no recovery @@ -2474,13 +2561,15 @@ def recovery_constraint_rule(block): # max energy shift for shifting process rhs = ( g.capacity_up[t] - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_up * g.delay_time * m.timeincrement[t] ) # add constraint - block.recovery_constraint.add((g, t), (lhs <= rhs)) + block.recovery_constraint.add( + (g, p, t), (lhs <= rhs) + ) # last time steps: end - recovery time else: @@ -2493,19 +2582,21 @@ def recovery_constraint_rule(block): # max energy shift for shifting process rhs = ( g.capacity_up[t] - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_up * g.delay_time * m.timeincrement[t] ) # add constraint - block.recovery_constraint.add((g, t), (lhs <= rhs)) + block.recovery_constraint.add( + (g, p, t), (lhs <= rhs) + ) else: pass # return(Constraint.Skip) self.recovery_constraint = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.recovery_constraint_build = BuildAction( rule=recovery_constraint_rule @@ -2518,10 +2609,9 @@ def shed_limit_constraint_rule(block): shedding is introduced in order to limit the overall amount of shedded energy. """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: - # Only applicable for load shedding if g.shed_eligibility: # main use case @@ -2535,14 +2625,14 @@ def shed_limit_constraint_rule(block): # max energy shift for shifting process rhs = ( g.capacity_down[t] - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_down * g.shed_time * m.timeincrement[t] ) # add constraint block.shed_limit_constraint.add( - (g, t), (lhs <= rhs) + (g, p, t), (lhs <= rhs) ) # last time steps: end - recovery time @@ -2556,21 +2646,21 @@ def shed_limit_constraint_rule(block): # max energy shift for shifting process rhs = ( g.capacity_down[t] - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_down * g.shed_time * m.timeincrement[t] ) # add constraint block.shed_limit_constraint.add( - (g, t), (lhs <= rhs) + (g, p, t), (lhs <= rhs) ) else: pass # return(Constraint.Skip) self.shed_limit_constraint = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.shed_limit_constraint_build = BuildAction( rule=shed_limit_constraint_rule @@ -2582,47 +2672,125 @@ def _objective_expression(self): m = self.parent_block() investment_costs = 0 + period_investment_costs = {p: 0 for p in m.PERIODS} variable_costs = 0 + fixed_costs = 0 - for g in self.investdsm: - if g.investment.ep_costs is not None: - investment_costs += self.invest[g] * g.investment.ep_costs - else: - raise ValueError("Missing value for investment costs!") - - for t in m.TIMESTEPS: - variable_costs += ( - self.dsm_up[g, t] - * g.cost_dsm_up[t] - * m.objective_weighting[t] - ) - variable_costs += ( - sum(self.dsm_do_shift[g, tt, t] for tt in m.TIMESTEPS) - * g.cost_dsm_down_shift[t] - + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] - ) * m.objective_weighting[t] - - self.cost = Expression(expr=investment_costs + variable_costs) - - return self.cost - - -class SinkDSMDLRBlock(SimpleBlock): - r"""Constraints for SinkDSM with "DLR" approach - - **The following constraints are created for approach = 'DLR':** + if not m.es.multi_period: + for g in self.investdsm: + for p in m.PERIODS: + if g.investment.ep_costs is not None: + investment_costs += ( + self.invest[g, p] * g.investment.ep_costs[p] + ) + else: + raise ValueError("Missing value for investment costs!") - .. _SinkDSMDLR equations: + for t in m.TIMESTEPS: + variable_costs += ( + self.dsm_up[g, t] + * g.cost_dsm_up[t] + * m.objective_weighting[t] + ) + variable_costs += ( + sum(self.dsm_do_shift[g, tt, t] for tt in m.TIMESTEPS) + * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) * m.objective_weighting[t] - .. math:: - & - (1) \quad DSM_{h, t}^{up} = 0 \quad \forall h \in H_{DR} - \forall t \in \mathbb{T} - \quad if \space eligibility_{shift} = False \\ - & - (2) \quad DSM_{t}^{do, shed} = 0 \quad \forall t \in \mathbb{T} - \quad if \space eligibility_{shed} = False \\ - & + else: + msg = ( + "You did not specify an interest rate.\n" + "It will be set equal to the discount_rate of {} " + "of the model as a default.\nThis corresponds to a " + "social planner point of view and does not reflect " + "microeconomic interest requirements." + ) + for g in self.investdsm: + if g.investment.ep_costs is not None: + lifetime = g.investment.lifetime + interest = g.investment.interest_rate + if interest == 0: + warn( + msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning, + ) + interest = m.discount_rate + for p in m.PERIODS: + annuity = economics.annuity( + capex=g.investment.ep_costs[p], + n=lifetime, + wacc=interest, + ) + investment_costs_increment = ( + self.invest[g, p] + * annuity + * lifetime + * ((1 + m.discount_rate) ** (-p)) + ) + investment_costs += investment_costs_increment + period_investment_costs[ + p + ] += investment_costs_increment + else: + raise ValueError("Missing value for investment costs!") + + for p, t in m.TIMEINDEX: + variable_costs += ( + self.dsm_up[g, t] + * m.objective_weighting[t] + * g.cost_dsm_up[t] + * ((1 + m.discount_rate) ** -p) + ) + variable_costs += ( + ( + sum( + self.dsm_do_shift[g, tt, t] + for tt in m.TIMESTEPS + ) + * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + ) + + if g.investment.fixed_costs[0] is not None: + lifetime = g.investment.lifetime + for p in m.PERIODS: + fixed_costs += sum( + self.invest[g, p] + * max(g.demand) + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(p, p + lifetime) + ) * ((1 + m.discount_rate) ** (-p)) + + self.investment_costs = investment_costs + self.period_investment_costs = period_investment_costs + self.costs = Expression( + expr=investment_costs + fixed_costs + variable_costs + ) + + return self.costs + + +class SinkDSMDLRBlock(SimpleBlock): + r"""Constraints for SinkDSM with "DLR" approach + + **The following constraints are created for approach = 'DLR':** + + .. _SinkDSMDLR equations: + + .. math:: + & + (1) \quad DSM_{h, t}^{up} = 0 \quad \forall h \in H_{DR} + \forall t \in \mathbb{T} + \quad if \space eligibility_{shift} = False \\ + & + (2) \quad DSM_{t}^{do, shed} = 0 \quad \forall t \in \mathbb{T} + \quad if \space eligibility_{shed} = False \\ + & (3) \quad \dot{E}_{t} = demand_{t} \cdot demand_{max} + \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo} - DSM_{h, t}^{do, shift} @@ -2925,10 +3093,10 @@ def _input_output_relation_rule(block): The actual demand after DR. BusBlock outflow == Demand +- DR (i.e. effective Sink consumption) """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # outflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand +- DR rhs = ( @@ -2944,10 +3112,10 @@ def _input_output_relation_rule(block): ) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add((g, p, t), (lhs == rhs)) self.input_output_relation = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.input_output_relation_build = BuildAction( rule=_input_output_relation_rule @@ -3306,24 +3474,31 @@ def dr_yearly_limit_shed_rule(block): for g in group: if g.shed_eligibility: - # sum of all load reductions - lhs = sum(self.dsm_do_shed[g, t] for t in m.TIMESTEPS) + for p in m.PERIODS: + # sum of all load reductions + lhs = sum( + self.dsm_do_shed[g, t] + for pp, t in m.TIMEINDEX + if pp == p + ) - # year limit - rhs = ( - g.capacity_down_mean - * g.max_capacity_down - * g.shed_time - * g.n_yearLimit_shed - ) + # year limit + rhs = ( + g.capacity_down_mean + * g.max_capacity_down + * g.shed_time + * g.n_yearLimit_shed + ) - # add constraint - block.dr_yearly_limit_shed.add(g, (lhs <= rhs)) + # add constraint + block.dr_yearly_limit_shed.add((g, p), (lhs <= rhs)) else: pass # return(Constraint.Skip) - self.dr_yearly_limit_shed = Constraint(group, noruleinit=True) + self.dr_yearly_limit_shed = Constraint( + group, m.PERIODS, noruleinit=True + ) self.dr_yearly_limit_shed_build = BuildAction( rule=dr_yearly_limit_shed_rule ) @@ -3338,27 +3513,34 @@ def dr_yearly_limit_red_rule(block): for g in group: if g.ActivateYearLimit: - # sum of all load reductions - lhs = sum( - sum(self.dsm_do_shift[g, h, t] for h in g.delay_time) - for t in m.TIMESTEPS - ) + for p in m.PERIODS: + # sum of all load reductions + lhs = sum( + sum( + self.dsm_do_shift[g, h, t] + for h in g.delay_time + ) + for pp, t in m.TIMEINDEX + if pp == p + ) - # year limit - rhs = ( - g.capacity_down_mean - * g.max_capacity_down - * g.shift_time - * g.n_yearLimit_shift - ) + # year limit + rhs = ( + g.capacity_down_mean + * g.max_capacity_down + * g.shift_time + * g.n_yearLimit_shift + ) - # add constraint - block.dr_yearly_limit_red.add(g, (lhs <= rhs)) + # add constraint + block.dr_yearly_limit_red.add((g, p), (lhs <= rhs)) else: pass # return(Constraint.Skip) - self.dr_yearly_limit_red = Constraint(group, noruleinit=True) + self.dr_yearly_limit_red = Constraint( + group, m.PERIODS, noruleinit=True + ) self.dr_yearly_limit_red_build = BuildAction( rule=dr_yearly_limit_red_rule ) @@ -3371,34 +3553,38 @@ def dr_yearly_limit_inc_rule(block): for g in group: if g.ActivateYearLimit: - # sum of all load increases - lhs = sum( - sum(self.dsm_up[g, h, t] for h in g.delay_time) - for t in m.TIMESTEPS - ) + for p in m.PERIODS: + # sum of all load increases + lhs = sum( + sum(self.dsm_up[g, h, t] for h in g.delay_time) + for pp, t in m.TIMEINDEX + if pp == p + ) - # year limit - rhs = ( - g.capacity_up_mean - * g.max_capacity_up - * g.shift_time - * g.n_yearLimit_shift - ) + # year limit + rhs = ( + g.capacity_up_mean + * g.max_capacity_up + * g.shift_time + * g.n_yearLimit_shift + ) - # add constraint - block.dr_yearly_limit_inc.add(g, (lhs <= rhs)) + # add constraint + block.dr_yearly_limit_inc.add((g, p), (lhs <= rhs)) else: pass # return(Constraint.Skip) - self.dr_yearly_limit_inc = Constraint(group, noruleinit=True) + self.dr_yearly_limit_inc = Constraint( + group, m.PERIODS, noruleinit=True + ) self.dr_yearly_limit_inc_build = BuildAction( rule=dr_yearly_limit_inc_rule ) # Equation 4.19 def dr_daily_limit_red_rule(block): - """ "Introduce rolling (energy) limit for load reductions + """Introduce rolling (energy) limit for load reductions This effectively limits DR utilization dependent on activations within previous hours. """ @@ -3545,31 +3731,71 @@ def _objective_expression(self): """ m = self.parent_block() - dr_cost = 0 + variable_costs = 0 + fixed_costs = 0 + + if not m.es.multi_period: + for t in m.TIMESTEPS: + for g in self.DR: + variable_costs += ( + sum( + self.dsm_up[g, h, t] + self.balance_dsm_do[g, h, t] + for h in g.delay_time + ) + * g.cost_dsm_up[t] + * m.objective_weighting[t] + ) + variable_costs += ( + sum( + self.dsm_do_shift[g, h, t] + + self.balance_dsm_up[g, h, t] + for h in g.delay_time + ) + * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) * m.objective_weighting[t] - for t in m.TIMESTEPS: + else: for g in self.DR: - dr_cost += ( - sum( - self.dsm_up[g, h, t] + self.balance_dsm_do[g, h, t] - for h in g.delay_time + for p, t in m.TIMEINDEX: + variable_costs += ( + ( + sum( + self.dsm_up[g, h, t] + + self.balance_dsm_do[g, h, t] + for h in g.delay_time + ) + * g.cost_dsm_up[t] + ) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) ) - * g.cost_dsm_up[t] - * m.objective_weighting[t] - ) - dr_cost += ( - sum( - self.dsm_do_shift[g, h, t] - + self.balance_dsm_up[g, h, t] - for h in g.delay_time + variable_costs += ( + ( + sum( + self.dsm_do_shift[g, h, t] + + self.balance_dsm_up[g, h, t] + for h in g.delay_time + ) + * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) ) - * g.cost_dsm_down_shift[t] - + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] - ) * m.objective_weighting[t] - self.cost = Expression(expr=dr_cost) + if g.fixed_costs[0] is not None: + for p in m.PERIODS: + fixed_costs += ( + g.max_demand + * max(g.demand) + * g.fixed_costs[p] + * ((1 + m.discount_rate) ** (-p)) + ) + + self.costs = Expression(expr=variable_costs + fixed_costs) - return self.cost + return self.costs class SinkDSMDLRInvestmentBlock(SinkDSMDLRBlock): @@ -3791,19 +4017,32 @@ def _create(self, group=None): # ************* VARIABLES ***************************** # Define bounds for investments in demand response - def _dr_investvar_bound_rule(block, g): + def _dr_investvar_bound_rule(block, g, p): """Rule definition to bound the invested demand response capacity `invest`. """ - return g.investment.minimum, g.investment.maximum + return g.investment.minimum[p], g.investment.maximum[p] # Investment in DR capacity self.invest = Var( self.INVESTDR, + m.PERIODS, within=NonNegativeReals, bounds=_dr_investvar_bound_rule, ) + # Total capacity + self.total = Var(self.INVESTDR, m.PERIODS, within=NonNegativeReals) + + # Old capacity to be decommissioned (due to lifetime) + self.old = Var(self.INVESTDR, m.PERIODS, within=NonNegativeReals) + + # Old endogenous capacity to be decommissioned (due to lifetime) + self.old_end = Var(self.INVESTDR, m.PERIODS, within=NonNegativeReals) + + # Old exogenous capacity to be decommissioned (due to lifetime) + self.old_exo = Var(self.INVESTDR, m.PERIODS, within=NonNegativeReals) + # Variable load shift down (capacity) self.dsm_do_shift = Var( self.INVESTDR_H, m.TIMESTEPS, initialize=0, within=NonNegativeReals @@ -3841,6 +4080,87 @@ def _dr_investvar_bound_rule(block, g): # ************* CONSTRAINTS ***************************** + # Handle unit lifetimes + def _total_capacity_rule(block): + """Rule definition for determining total installed + capacity (taking decommissioning into account) + """ + for g in group: + for p in m.PERIODS: + if p == 0: + expr = ( + self.total[g, p] + == self.invest[g, p] + g.investment.existing + ) + self.total_dsm_rule.add((g, p), expr) + else: + expr = ( + self.total[g, p] + == self.invest[g, p] + + self.total[g, p - 1] + - self.old[g, p] + ) + self.total_dsm_rule.add((g, p), expr) + + self.total_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) + self.total_dsm_rule_build = BuildAction(rule=_total_capacity_rule) + + def _old_dsm_capacity_rule_end(block): + """Rule definition for determining old endogenously installed + capacity to be decommissioned due to reaching its lifetime + """ + for g in group: + lifetime = g.investment.lifetime + for p in m.PERIODS: + if lifetime <= p: + expr = ( + self.old_end[g, p] == self.invest[g, p - lifetime] + ) + self.old_dsm_rule_end.add((g, p), expr) + else: + expr = self.old_end[g, p] == 0 + self.old_dsm_rule_end.add((g, p), expr) + + self.old_dsm_rule_end = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_end_build = BuildAction( + rule=_old_dsm_capacity_rule_end + ) + + def _old_dsm_capacity_rule_exo(block): + """Rule definition for determining old exogenously given capacity + to be decommissioned due to reaching its lifetime + """ + for g in group: + age = g.investment.age + lifetime = g.investment.lifetime + for p in m.PERIODS: + if lifetime - age == p: + expr = self.old_exo[g, p] == g.investment.existing + self.old_dsm_rule_exo.add((g, p), expr) + else: + expr = self.old_exo[g, p] == 0 + self.old_dsm_rule_exo.add((g, p), expr) + + self.old_dsm_rule_exo = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_exo_build = BuildAction( + rule=_old_dsm_capacity_rule_exo + ) + + def _old_dsm_capacity_rule(block): + """Rule definition for determining (overall) old capacity + to be decommissioned due to reaching its lifetime + """ + for g in group: + for p in m.PERIODS: + expr = ( + self.old[g, p] + == self.old_end[g, p] + self.old_exo[g, p] + ) + self.old_dsm_rule.add((g, p), expr) + + self.old_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_build = BuildAction(rule=_old_dsm_capacity_rule) + def _shift_shed_vars_rule(block): """Force shifting resp. shedding variables to zero dependent on how boolean parameters for shift resp. shed eligibility @@ -3873,15 +4193,15 @@ def _input_output_relation_rule(block): The actual demand after DR. BusBlock outflow == Demand +- DR (i.e. effective Sink consumption) """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # outflow from bus - lhs = m.flow[g.inflow, g, t] + lhs = m.flow[g.inflow, g, p, t] # Demand +- DR rhs = ( - g.demand[t] * (self.invest[g] + g.investment.existing) + g.demand[t] * self.total[g, p] + sum( self.dsm_up[g, h, t] + self.balance_dsm_do[g, h, t] @@ -3893,10 +4213,10 @@ def _input_output_relation_rule(block): ) # add constraint - block.input_output_relation.add((g, t), (lhs == rhs)) + block.input_output_relation.add((g, p, t), (lhs == rhs)) self.input_output_relation = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.input_output_relation_build = BuildAction( rule=_input_output_relation_rule @@ -4064,7 +4384,7 @@ def availability_red_rule(block): """Load reduction must be smaller than or equal to the (time-dependent) capacity limit """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # load reduction lhs = ( @@ -4079,14 +4399,14 @@ def availability_red_rule(block): # upper bound rhs = ( g.capacity_down[t] - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_down ) # add constraint - block.availability_red.add((g, t), (lhs <= rhs)) + block.availability_red.add((g, p, t), (lhs <= rhs)) - self.availability_red = Constraint(group, m.TIMESTEPS, noruleinit=True) + self.availability_red = Constraint(group, m.TIMEINDEX, noruleinit=True) self.availability_red_build = BuildAction(rule=availability_red_rule) # Equation 4.12 @@ -4094,7 +4414,7 @@ def availability_inc_rule(block): """Load increase must be smaller than or equal to the (time-dependent) capacity limit """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: # load increase lhs = sum( @@ -4103,16 +4423,12 @@ def availability_inc_rule(block): ) # upper bound - rhs = ( - g.capacity_up[t] - * (self.invest[g] + g.investment.existing) - * g.flex_share_up - ) + rhs = g.capacity_up[t] * self.total[g, p] * g.flex_share_up # add constraint - block.availability_inc.add((g, t), (lhs <= rhs)) + block.availability_inc.add((g, p, t), (lhs <= rhs)) - self.availability_inc = Constraint(group, m.TIMESTEPS, noruleinit=True) + self.availability_inc = Constraint(group, m.TIMEINDEX, noruleinit=True) self.availability_inc_build = BuildAction(rule=availability_inc_rule) # Equation 4.13 @@ -4198,7 +4514,7 @@ def dr_storage_limit_red_rule(block): """ Fictious demand response storage level for load reduction limit """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: if g.shift_eligibility: @@ -4208,13 +4524,13 @@ def dr_storage_limit_red_rule(block): # maximum (time-dependent) available shifting capacity rhs = ( g.capacity_down_mean - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_down * g.shift_time ) # add constraint - block.dr_storage_limit_red.add((g, t), (lhs <= rhs)) + block.dr_storage_limit_red.add((g, p, t), (lhs <= rhs)) else: lhs = self.dsm_do_level[g, t] @@ -4222,10 +4538,10 @@ def dr_storage_limit_red_rule(block): rhs = 0 # add constraint - block.dr_storage_limit_red.add((g, t), (lhs <= rhs)) + block.dr_storage_limit_red.add((g, p, t), (lhs <= rhs)) self.dr_storage_limit_red = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.dr_storage_level_red_build = BuildAction( rule=dr_storage_limit_red_rule @@ -4233,10 +4549,8 @@ def dr_storage_limit_red_rule(block): # Equation 4.16 def dr_storage_limit_inc_rule(block): - """ - Fictious demand response storage level for load increase limit - """ - for t in m.TIMESTEPS: + """Fictious demand response storage level for load increase limit""" + for p, t in m.TIMEINDEX: for g in group: # fictious demand response load reduction storage level lhs = self.dsm_up_level[g, t] @@ -4244,16 +4558,16 @@ def dr_storage_limit_inc_rule(block): # maximum (time-dependent) available shifting capacity rhs = ( g.capacity_up_mean - * (self.invest[g] + g.investment.existing) + * self.total[g, p] * g.flex_share_up * g.shift_time ) # add constraint - block.dr_storage_limit_inc.add((g, t), (lhs <= rhs)) + block.dr_storage_limit_inc.add((g, p, t), (lhs <= rhs)) self.dr_storage_limit_inc = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.dr_storage_level_inc_build = BuildAction( rule=dr_storage_limit_inc_rule @@ -4268,23 +4582,26 @@ def dr_yearly_limit_shed_rule(block): to the others. """ for g in group: - if g.shed_eligibility: - # sum of all load reductions - lhs = sum(self.dsm_do_shed[g, t] for t in m.TIMESTEPS) + for p in m.PERIODS: + if g.shed_eligibility: + # sum of all load reductions + lhs = sum(self.dsm_do_shed[g, t] for t in m.TIMESTEPS) - # year limit - rhs = ( - g.capacity_down_mean - * (self.invest[g] + g.investment.existing) - * g.flex_share_down - * g.shed_time - * g.n_yearLimit_shed - ) + # year limit + rhs = ( + g.capacity_down_mean + * self.total[g, p] + * g.flex_share_down + * g.shed_time + * g.n_yearLimit_shed + ) - # add constraint - block.dr_yearly_limit_shed.add(g, (lhs <= rhs)) + # add constraint + block.dr_yearly_limit_shed.add((g, p), (lhs <= rhs)) - self.dr_yearly_limit_shed = Constraint(group, noruleinit=True) + self.dr_yearly_limit_shed = Constraint( + group, m.PERIODS, noruleinit=True + ) self.dr_yearly_limit_shed_build = BuildAction( rule=dr_yearly_limit_shed_rule ) @@ -4299,28 +4616,35 @@ def dr_yearly_limit_red_rule(block): for g in group: if g.ActivateYearLimit: - # sum of all load reductions - lhs = sum( - sum(self.dsm_do_shift[g, h, t] for h in g.delay_time) - for t in m.TIMESTEPS - ) + for p in m.PERIODS: + # sum of all load reductions + lhs = sum( + sum( + self.dsm_do_shift[g, h, t] + for h in g.delay_time + ) + for pp, t in m.TIMEINDEX + if pp == p + ) - # year limit - rhs = ( - g.capacity_down_mean - * (self.invest[g] + g.investment.existing) - * g.flex_share_down - * g.shift_time - * g.n_yearLimit_shift - ) + # year limit + rhs = ( + g.capacity_down_mean + * self.total[g, p] + * g.flex_share_down + * g.shift_time + * g.n_yearLimit_shift + ) - # add constraint - block.dr_yearly_limit_red.add(g, (lhs <= rhs)) + # add constraint + block.dr_yearly_limit_red.add((g, p), (lhs <= rhs)) else: pass # return(Constraint.Skip) - self.dr_yearly_limit_red = Constraint(group, noruleinit=True) + self.dr_yearly_limit_red = Constraint( + group, m.PERIODS, noruleinit=True + ) self.dr_yearly_limit_red_build = BuildAction( rule=dr_yearly_limit_red_rule ) @@ -4333,28 +4657,32 @@ def dr_yearly_limit_inc_rule(block): for g in group: if g.ActivateYearLimit: - # sum of all load increases - lhs = sum( - sum(self.dsm_up[g, h, t] for h in g.delay_time) - for t in m.TIMESTEPS - ) + for p in m.PERIODS: + # sum of all load increases + lhs = sum( + sum(self.dsm_up[g, h, t] for h in g.delay_time) + for pp, t in m.TIMEINDEX + if pp == p + ) - # year limit - rhs = ( - g.capacity_up_mean - * (self.invest[g] + g.investment.existing) - * g.flex_share_up - * g.shift_time - * g.n_yearLimit_shift - ) + # year limit + rhs = ( + g.capacity_up_mean + * self.total[g, p] + * g.flex_share_up + * g.shift_time + * g.n_yearLimit_shift + ) - # add constraint - block.dr_yearly_limit_inc.add(g, (lhs <= rhs)) + # add constraint + block.dr_yearly_limit_inc.add((g, p), (lhs <= rhs)) else: pass # return(Constraint.Skip) - self.dr_yearly_limit_inc = Constraint(group, noruleinit=True) + self.dr_yearly_limit_inc = Constraint( + group, m.PERIODS, noruleinit=True + ) self.dr_yearly_limit_inc_build = BuildAction( rule=dr_yearly_limit_inc_rule ) @@ -4365,7 +4693,7 @@ def dr_daily_limit_red_rule(block): This effectively limits DR utilization dependent on activations within previous hours. """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: if g.ActivateDayLimit: @@ -4380,9 +4708,9 @@ def dr_daily_limit_red_rule(block): ) # daily limit - rhs = g.capacity_down_mean * ( - self.invest[g] + g.investment.existing - ) * g.flex_share_down * g.shift_time - sum( + rhs = g.capacity_down_mean * self.total[ + g, p + ] * g.flex_share_down * g.shift_time - sum( sum( self.dsm_do_shift[g, h, t - t_dash] for h in g.delay_time @@ -4391,7 +4719,9 @@ def dr_daily_limit_red_rule(block): ) # add constraint - block.dr_daily_limit_red.add((g, t), (lhs <= rhs)) + block.dr_daily_limit_red.add( + (g, p, t), (lhs <= rhs) + ) else: pass # return(Constraint.Skip) @@ -4400,7 +4730,7 @@ def dr_daily_limit_red_rule(block): pass # return(Constraint.Skip) self.dr_daily_limit_red = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.dr_daily_limit_red_build = BuildAction( rule=dr_daily_limit_red_rule @@ -4412,7 +4742,7 @@ def dr_daily_limit_inc_rule(block): This effectively limits DR utilization dependent on activations within previous hours. """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: if g.ActivateDayLimit: @@ -4426,9 +4756,9 @@ def dr_daily_limit_inc_rule(block): ) # daily limit - rhs = g.capacity_up_mean * ( - self.invest[g] + g.investment.existing - ) * g.flex_share_up * g.shift_time - sum( + rhs = g.capacity_up_mean * self.total[ + g, p + ] * g.flex_share_up * g.shift_time - sum( sum( self.dsm_up[g, h, t - t_dash] for h in g.delay_time @@ -4437,7 +4767,9 @@ def dr_daily_limit_inc_rule(block): ) # add constraint - block.dr_daily_limit_inc.add((g, t), (lhs <= rhs)) + block.dr_daily_limit_inc.add( + (g, p, t), (lhs <= rhs) + ) else: pass # return(Constraint.Skip) @@ -4446,7 +4778,7 @@ def dr_daily_limit_inc_rule(block): pass # return(Constraint.Skip) self.dr_daily_limit_inc = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.dr_daily_limit_inc_build = BuildAction( rule=dr_daily_limit_inc_rule @@ -4458,7 +4790,7 @@ def dr_logical_constraint_rule(block): The sum of upwards and downwards shifts may not be greater than the (bigger) capacity limit. """ - for t in m.TIMESTEPS: + for p, t in m.TIMEINDEX: for g in group: if g.addition: @@ -4481,17 +4813,19 @@ def dr_logical_constraint_rule(block): g.capacity_down[t] * g.flex_share_down, g.capacity_up[t] * g.flex_share_up, ) - * (self.invest[g] + g.investment.existing) + * self.total[g, p] ) # add constraint - block.dr_logical_constraint.add((g, t), (lhs <= rhs)) + block.dr_logical_constraint.add( + (g, p, t), (lhs <= rhs) + ) else: pass # return(Constraint.Skip) self.dr_logical_constraint = Constraint( - group, m.TIMESTEPS, noruleinit=True + group, m.TIMEINDEX, noruleinit=True ) self.dr_logical_constraint_build = BuildAction( rule=dr_logical_constraint_rule @@ -4504,32 +4838,118 @@ def _objective_expression(self): m = self.parent_block() investment_costs = 0 + period_investment_costs = {p: 0 for p in m.PERIODS} variable_costs = 0 + fixed_costs = 0 - for g in self.INVESTDR: - if g.investment.ep_costs is not None: - investment_costs += self.invest[g] * g.investment.ep_costs - else: - raise ValueError("Missing value for investment costs!") - for t in m.TIMESTEPS: - variable_costs += ( - sum( - self.dsm_up[g, h, t] + self.balance_dsm_do[g, h, t] - for h in g.delay_time + if not m.es.multi_period: + for g in self.INVESTDR: + for p in m.PERIODS: + if g.investment.ep_costs is not None: + investment_costs += ( + self.invest[g] * g.investment.ep_costs + ) + else: + raise ValueError("Missing value for investment costs!") + + for t in m.TIMESTEPS: + variable_costs += ( + sum( + self.dsm_up[g, h, t] + self.balance_dsm_do[g, h, t] + for h in g.delay_time + ) + * g.cost_dsm_up[t] + * m.objective_weighting[t] ) - * g.cost_dsm_up[t] - * m.objective_weighting[t] - ) - variable_costs += ( - sum( - self.dsm_do_shift[g, h, t] - + self.balance_dsm_up[g, h, t] - for h in g.delay_time + variable_costs += ( + sum( + self.dsm_do_shift[g, h, t] + + self.balance_dsm_up[g, h, t] + for h in g.delay_time + ) + * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) * m.objective_weighting[t] + + else: + msg = ( + "You did not specify an interest rate.\n" + "It will be set equal to the discount_rate of {} " + "of the model as a default.\nThis corresponds to a " + "social planner point of view and does not reflect " + "microeconomic interest requirements." + ) + for g in self.INVESTDR: + if g.investment.ep_costs is not None: + lifetime = g.investment.lifetime + interest = g.investment.interest_rate + if interest == 0: + warn( + msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning, + ) + interest = m.discount_rate + for p in m.PERIODS: + annuity = economics.annuity( + capex=g.investment.ep_costs[p], + n=lifetime, + wacc=interest, + ) + investment_costs_increment = ( + self.invest[g, p] + * annuity + * lifetime + * ((1 + m.discount_rate) ** (-p)) + ) + investment_costs += investment_costs_increment + period_investment_costs[ + p + ] += investment_costs_increment + else: + raise ValueError("Missing value for investment costs!") + + for p, t in m.TIMEINDEX: + variable_costs += ( + ( + sum( + self.dsm_up[g, h, t] + + self.balance_dsm_do[g, h, t] + for h in g.delay_time + ) + * g.cost_dsm_up[t] + ) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) + ) + variable_costs += ( + ( + sum( + self.dsm_do_shift[g, h, t] + + self.balance_dsm_up[g, h, t] + for h in g.delay_time + ) + * g.cost_dsm_down_shift[t] + + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] + ) + * m.objective_weighting[t] + * ((1 + m.discount_rate) ** -p) ) - * g.cost_dsm_down_shift[t] - + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] - ) * m.objective_weighting[t] - self.cost = Expression(expr=investment_costs + variable_costs) + if g.investment.fixed_costs[0] is not None: + lifetime = g.investment.lifetime + for p in m.PERIODS: + fixed_costs += sum( + self.invest[g, p] + * max(g.demand) + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(p, p + lifetime) + ) * ((1 + m.discount_rate) ** (-p)) - return self.cost + self.investment_costs = investment_costs + self.period_investment_costs = period_investment_costs + self.costs = Expression( + expr=investment_costs + fixed_costs + variable_costs + ) + + return self.costs From e399cde9c8264e62e15c7a01317291b07686a3cb Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 10 Dec 2021 09:40:33 +0100 Subject: [PATCH 0066/1363] Extend warning message --- src/oemof/solph/flows/_flow.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index dee4c5323..347616afa 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -158,8 +158,12 @@ def __init__(self, **kwargs): if "fixed_costs" in keys: msg = ("Be aware that the fixed costs attribute is only\n" "meant to be used for multi-period models.\n" + "If you wish to set up a multi-period model, set the" + " multi_period attribute of your energy system to True.\n" "It has been decided to remove the `fixed_costs` " - "attribute with v0.2 for regular uses!") + "attribute with v0.2 for regular uses.\n" + "If you specify `fixed_costs` for a regular model, " + "it will simply be ignored.") warn(msg, debugging.SuspiciousUsageWarning) if "actual_value" in keys: From fbf6d8760b73aff1a27774096220c0c38be0f492 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 10 Dec 2021 10:12:23 +0100 Subject: [PATCH 0067/1363] Include temporary fix for standard models TODO: Generalize periods! --- src/oemof/solph/_models.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 8bbec958e..8a06ae293 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -310,14 +310,24 @@ def _add_parent_block_sets(self): self.TIMESTEPS = po.Set(initialize=range(len(self.es.timeindex)), ordered=True) - # pyomo set for timeindex of optimization problem - self.TIMEINDEX = po.Set( - initialize=list( - zip([self.es.periods[p] for p in self.es.timeindex.year], - range(len(self.es.timeindex))) - ), - ordered=True - ) + # TODO: Generalize! + if not self.es.multi_period: + self.TIMEINDEX = po.Set( + initialize=list( + zip([0] * len(self.es.timeindex.year), + range(len(self.es.timeindex))) + ), + ordered=True + ) + else: + # pyomo set for timeindex of optimization problem + self.TIMEINDEX = po.Set( + initialize=list( + zip([self.es.periods[p] for p in self.es.timeindex.year], + range(len(self.es.timeindex))) + ), + ordered=True + ) self.PERIODS = po.Set( initialize=sorted(list(set(self.es.periods.values()))) From 10c9ab598dc2594dbb1cb183e4bef613d3d630e6 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 10 Dec 2021 14:53:39 +0100 Subject: [PATCH 0068/1363] Include processing.py with separation between standard and multi-period --- src/oemof/solph/processing.py | 205 ++++++++++++++++++++++++++-------- 1 file changed, 156 insertions(+), 49 deletions(-) diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index fadc2bc39..d8761e188 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -72,7 +72,7 @@ def remove_timestep(x): def get_timeindex(x): """ - Get the timeindex from oemof tuples for multiperiod models. + Get the timeindex from oemof tuples. Slice int values (timeindex, timesteps or periods) dependent on how the variable is indexed. @@ -87,7 +87,7 @@ def get_timeindex(x): def remove_timeindex(x): """ - Remove the timeindex from oemof tuples for mulitperiod models. + Remove the timeindex from oemof tuples. Slice up to integer values (node labels) The timestep is removed from tuples of type `(n, n, int, int)`, @@ -133,8 +133,7 @@ def create_dataframe(om): df["timeindex"] = df["oemof_tuple"].map(get_timeindex) df["oemof_tuple"] = df["oemof_tuple"].map(remove_timeindex) # order the data by oemof tuple and timestep - df = df.sort_values(["oemof_tuple", "timeindex"], - ascending=[True, True]) + df = df.sort_values(["oemof_tuple", "timeindex"], ascending=[True, True]) # drop empty decision variables df = df.dropna(subset=["value"]) @@ -148,56 +147,178 @@ def results(om): Results from Pyomo are written into a dictionary of pandas objects where a Series holds all scalar values and a dataframe all sequences for nodes - and flows for a standard model. For a MultiPeriodModel, the investment + and flows for a standard model. For a multi-period model, the investment values are given in a DataFrame indexed by periods. The dictionary is keyed by the nodes e.g. `results[idx]['scalars']` and flows e.g. `results[n, n]['sequences']` for a standard model. """ + # Extraction steps that are the same for both model types df = create_dataframe(om) - - period_indexed = [ - "invest", - "total", - "old", - "old_exo", - "old_end" - ] - period_timestep_indexed = ["flow"] - # TODO: Take care of initial storage content instead of just ignoring - to_be_ignored = ["init_content"] - timestep_indexed = [el for el in df["variable_name"].unique() - if el not in period_indexed - and el not in period_timestep_indexed - and el not in to_be_ignored] - scalars_col = "period_scalars" + period_indexed = ["invest", "total", "old", "old_end", "old_exo"] # create a dict of dataframes keyed by oemof tuples - # TODO / QUESTION: Could this be sped up using DFs / arrays instead?! df_dict = { - k if len(k) > 1 else (k[0], None): - v[["timeindex", "variable_name", "value"]] + k + if len(k) > 1 + else (k[0], None): v[["timeindex", "variable_name", "value"]] for k, v in df.groupby("oemof_tuple") } - # create final result dictionary by splitting up the dataframes in the - # dataframe dict into a series for scalar data and dataframe for sequences result = {} + + # Standard model results extraction + if not om.es.multi_period: + result = _extract_standard_model_result( + om, df_dict, period_indexed, result + ) + scalars_col = "scalars" + + # Results extraction for a multi-period model + else: + result = _extract_multi_period_model_result( + om, df, df_dict, period_indexed, result + ) + scalars_col = "period_scalars" + + # add dual variables for bus constraints + if om.dual is not None: + grouped = groupby( + sorted(om.BusBlock.balance.iterkeys()), lambda p: p[0] + ) + for bus, timeindex in grouped: + duals = [ + om.dual[om.BusBlock.balance[bus, p, t]] + for _, p, t in timeindex + ] + df = pd.DataFrame({"duals": duals}, index=om.es.timeindex) + if (bus, None) not in result.keys(): + result[(bus, None)] = { + "sequences": df, + scalars_col: pd.Series(dtype=float), + } + else: + result[(bus, None)]["sequences"]["duals"] = duals + + return result + + +def _extract_standard_model_result( + om, df_dict, period_indexed=None, result=None +): + """Extract and return the results of a standard model + + Parameters + ---------- + om : oemof.solph.models.Model + The optimization model + df_dict : dict + dictionary of results DataFrames + period_indexed : list + list of variables that are indexed by periods + result : dict + dictionary to store the results + """ for k in df_dict: df_dict[k].set_index("timeindex", inplace=True) df_dict[k] = df_dict[k].pivot(columns="variable_name", values="value") + try: + # Enable reindexing by replacing period by first timeindex + try: + df_dict[k].loc[ + [(0, 0)], + [ + col + for col in df_dict[k].columns + if col in period_indexed + ], + ] = ( + df_dict[k] + .loc[ + [(0,)], + [ + col + for col in df_dict[k].columns + if col in period_indexed + ], + ] + .values + ) + df_dict[k].drop(index=[(0,)], inplace=True) + except KeyError: + pass + df_dict[k].index = om.es.timeindex + except ValueError as e: + msg = ( + "\nFlowBlock: {0}-{1}. This could be caused by NaN-values in" + " your input data." + ) + raise type(e)( + str(e) + msg.format(k[0].label, k[1].label) + ).with_traceback(sys.exc_info()[2]) + try: + condition = df_dict[k].isnull().any() + scalars = df_dict[k].loc[:, condition].dropna().iloc[0] + sequences = df_dict[k].loc[:, ~condition] + result[k] = {"scalars": scalars, "sequences": sequences} + except IndexError: + error_message = ( + "Cannot access index on result data. " + + "Did the optimization terminate" + + " without errors?" + ) + raise IndexError(error_message) + + return result + + +def _extract_multi_period_model_result( + om, df, df_dict, period_indexed=None, result=None +): + """Extract and return the results of a multi-period model + + Parameters + ---------- + om : oemof.solph.models.Model + The ptimization model + df : DataFrame + DataFrame containing all results + df_dict : dict + dictionary of results DataFrames + period_indexed : list + list of variables that are indexed by periods + result : dict + dictionary to store the results + """ + period_timestep_indexed = ["flow"] + # TODO: Take care of initial storage content instead of just ignoring + to_be_ignored = ["init_content"] + timestep_indexed = [ + el + for el in df["variable_name"].unique() + if el not in period_indexed + and el not in period_timestep_indexed + and el not in to_be_ignored + ] + for k in df_dict: + df_dict[k].set_index("timeindex", inplace=True) + df_dict[k] = df_dict[k].pivot(columns="variable_name", values="value") # TODO: Revise and potentially speed up # Split data set - timeindex_cols = [col for col in df_dict[k].columns - if col in timestep_indexed or col == "flow"] - period_cols = [col for col in df_dict[k].columns - if col not in timeindex_cols] + timeindex_cols = [ + col + for col in df_dict[k].columns + if col in timestep_indexed or col == "flow" + ] + period_cols = [ + col for col in df_dict[k].columns if col not in timeindex_cols + ] sequences = df_dict[k][timeindex_cols].dropna() if sequences.empty: sequences = pd.DataFrame(index=om.es.timeindex) # periods equal to years (will probably be the standard use case) periods = sorted(list(set(om.es.timeindex.year))) - d = dict(zip([(el, ) for el in range(len(periods))], periods)) + d = dict(zip([(el,) for el in range(len(periods))], periods)) period_scalars = df_dict[k][period_cols].dropna() if period_scalars.empty: period_scalars = pd.DataFrame(index=d.values()) @@ -205,8 +326,10 @@ def results(om): sequences.index = om.es.timeindex period_scalars.rename(index=d, inplace=True) period_scalars.index.name = "period" - result[k] = {scalars_col: period_scalars, - "sequences": sequences} + result[k] = { + "period_scalars": period_scalars, + "sequences": sequences, + } except IndexError: error_message = ( "Some indices seem to be not matching.\n" @@ -214,22 +337,6 @@ def results(om): ) raise IndexError(error_message) - # add dual variables for bus constraints - if om.dual is not None: - grouped = groupby(sorted(om.BusBlock.balance.iterkeys()), - lambda p: p[0]) - for bus, timeindex in grouped: - duals = [om.dual[om.BusBlock.balance[bus, p, t]] - for _, p, t in timeindex] - df = pd.DataFrame({"duals": duals}, index=om.es.timeindex) - if (bus, None) not in result.keys(): - result[(bus, None)] = { - "sequences": df, - scalars_col: pd.Series(dtype=float), - } - else: - result[(bus, None)]["sequences"]["duals"] = duals - return result From 97f1de133a3e0eb2d3171720e1062fad6ddf2ed0 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 10 Dec 2021 16:49:58 +0100 Subject: [PATCH 0069/1363] Add PEP8 fix --- src/oemof/solph/components/experimental/_sink_dsm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index a6018d98b..0208ba9c2 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -4549,7 +4549,8 @@ def dr_storage_limit_red_rule(block): # Equation 4.16 def dr_storage_limit_inc_rule(block): - """Fictious demand response storage level for load increase limit""" + """Fictious demand response storage level + for load increase limit""" for p, t in m.TIMEINDEX: for g in group: # fictious demand response load reduction storage level From 21aec6c1e54ae692bda3fe96b3be52ef7ff0c574 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 10 Dec 2021 17:23:47 +0100 Subject: [PATCH 0070/1363] Add draft for handling initial storage content --- .../solph/components/_generic_storage.py | 405 +++++++++--------- 1 file changed, 209 insertions(+), 196 deletions(-) diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index 77f9d5943..8b532465d 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -159,9 +159,7 @@ def __init__( ) self.max_storage_level = solph_sequence(max_storage_level) self.min_storage_level = solph_sequence(min_storage_level) - self.fixed_costs = solph_sequence( - kwargs.get('fixed_costs', 0) - ) + self.fixed_costs = solph_sequence(kwargs.get("fixed_costs", 0)) self.investment = kwargs.get("investment") self.invest_relation_input_output = kwargs.get( "invest_relation_input_output" @@ -256,14 +254,13 @@ def _check_invest_attributes(self): "or investment.minimum has to be non-zero." ) raise AttributeError(e3) - if ( - self.lifetime_inflow == 20 - or self.lifetime_outflow == 20 - ): - w1 = ("Using a lifetime of 20 periods," - " which is the default value.\nIf you don't consider a" - " multi-period investment model, you can safely ignore " - " this warning - all fine!") + if self.lifetime_inflow == 20 or self.lifetime_outflow == 20: + w1 = ( + "Using a lifetime of 20 periods," + " which is the default value.\nIf you don't consider a" + " multi-period investment model, you can safely ignore " + " this warning - all fine!" + ) warn(w1, debugging.SuspiciousUsageWarning) self._set_flows() @@ -464,9 +461,7 @@ def _storage_init_content_bound_rule(block, n): # ************* Constraints *************************** - reduced_periods_timesteps = [ - (p, t) for (p, t) in m.TIMEINDEX if t > 0 - ] + reduced_periods_timesteps = [(p, t) for (p, t) in m.TIMEINDEX if t > 0] # storage balance constraint (first time step) def _storage_balance_first_rule(block, n): @@ -525,8 +520,9 @@ def _storage_balance_rule(block, n, p, t): return expr == 0 self.balance = Constraint( - self.STORAGES, reduced_periods_timesteps, - rule=_storage_balance_rule + self.STORAGES, + reduced_periods_timesteps, + rule=_storage_balance_rule, ) def _balanced_storage_rule(block, n): @@ -558,14 +554,10 @@ def _power_coupled(block): self.power_coupled.add((n, p), expr) self.power_coupled = Constraint( - self.STORAGES_WITH_INVEST_FLOW_REL, - m.PERIODS, - noruleinit=True + self.STORAGES_WITH_INVEST_FLOW_REL, m.PERIODS, noruleinit=True ) - self.power_coupled_build = BuildAction( - rule=_power_coupled - ) + self.power_coupled_build = BuildAction(rule=_power_coupled) def _objective_expression(self): r""" @@ -884,21 +876,18 @@ def _create(self, group=None): self.OVERALL_MAXIMUM_INVESTSTORAGES = Set( initialize=[ - n for n in group - if n.investment.overall_maximum is not None + n for n in group if n.investment.overall_maximum is not None ] ) self.OVERALL_MINIMUM_INVESTSTORAGES = Set( initialize=[ - n for n in group - if n.investment.overall_minimum is not None + n for n in group if n.investment.overall_minimum is not None ] ) # ######################### Variables ################################ self.storage_content = Var( - self.INVESTSTORAGES, m.TIMESTEPS, - within=NonNegativeReals + self.INVESTSTORAGES, m.TIMESTEPS, within=NonNegativeReals ) def _storage_investvar_bound_rule(block, n, p): @@ -919,52 +908,38 @@ def _storage_investvar_bound_rule(block, n, p): # Total capacity self.total = Var( - self.INVESTSTORAGES, - m.PERIODS, - within=NonNegativeReals + self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals, + initialize=0 ) # Old capacity to be decommissioned (due to lifetime) # Old capacity is built out of old exogenous and endogenous capacities - self.old = Var( - self.INVESTSTORAGES, - m.PERIODS, - within=NonNegativeReals - ) + self.old = Var(self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals) # Old endogenous capacity to be decommissioned (due to lifetime) self.old_end = Var( - self.INVESTSTORAGES, - m.PERIODS, - within=NonNegativeReals + self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals ) # Old exogenous capacity to be decommissioned (due to lifetime) self.old_exo = Var( - self.INVESTSTORAGES, - m.PERIODS, - within=NonNegativeReals + self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals ) self.init_content = Var( - self.INVESTSTORAGES, - within=NonNegativeReals + self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals ) # create status variable for a non-convex investment storage self.invest_status = Var( - self.NON_CONVEX_INVESTSTORAGES, - m.PERIODS, - within=Binary + self.NON_CONVEX_INVESTSTORAGES, m.PERIODS, within=Binary ) # ######################### CONSTRAINTS ############################### i = {n: [i for i in n.inputs][0] for n in group} o = {n: [o for o in n.outputs][0] for n in group} - reduced_periods_timesteps = [ - (p, t) for (p, t) in m.TIMEINDEX if t > 0 - ] + reduced_periods_timesteps = [(p, t) for (p, t) in m.TIMEINDEX if t > 0] # Handle unit lifetimes def _total_storage_capacity_rule(block): @@ -974,21 +949,22 @@ def _total_storage_capacity_rule(block): for n in self.INVESTSTORAGES: for p in m.PERIODS: if p == 0: - expr = (self.total[n, p] - == self.invest[n, p] - + n.investment.existing) + expr = ( + self.total[n, p] + == self.invest[n, p] + n.investment.existing + ) self.total_storage_rule.add((n, p), expr) else: - expr = (self.total[n, p] - == self.invest[n, p] - + self.total[n, p - 1] - - self.old[n, p]) + expr = ( + self.total[n, p] + == self.invest[n, p] + + self.total[n, p - 1] + - self.old[n, p] + ) self.total_storage_rule.add((n, p), expr) self.total_storage_rule = Constraint( - self.INVESTSTORAGES, - m.PERIODS, - noruleinit=True + self.INVESTSTORAGES, m.PERIODS, noruleinit=True ) self.total_storage_rule_build = BuildAction( @@ -1003,18 +979,16 @@ def _old_storage_capacity_rule_end(block): lifetime = n.investment.lifetime for p in m.PERIODS: if lifetime <= p: - expr = (self.old_end[n, p] - == self.invest[n, p - lifetime]) + expr = ( + self.old_end[n, p] == self.invest[n, p - lifetime] + ) self.old_rule_end.add((n, p), expr) else: - expr = (self.old_end[n, p] - == 0) + expr = self.old_end[n, p] == 0 self.old_rule_end.add((n, p), expr) self.old_rule_end = Constraint( - self.INVESTSTORAGES, - m.PERIODS, - noruleinit=True + self.INVESTSTORAGES, m.PERIODS, noruleinit=True ) self.old_rule_end_build = BuildAction( @@ -1030,19 +1004,14 @@ def _old_storage_capacity_rule_exo(block): lifetime = n.investment.lifetime for p in m.PERIODS: if lifetime - age == p: - expr = ( - self.old_exo[n, p] - == n.investment.existing) + expr = self.old_exo[n, p] == n.investment.existing self.old_rule_exo.add((n, p), expr) else: - expr = (self.old_exo[n, p] - == 0) + expr = self.old_exo[n, p] == 0 self.old_rule_exo.add((n, p), expr) self.old_rule_exo = Constraint( - self.INVESTSTORAGES, - m.PERIODS, - noruleinit=True + self.INVESTSTORAGES, m.PERIODS, noruleinit=True ) self.old_rule_exo_build = BuildAction( @@ -1062,14 +1031,10 @@ def _old_storage_capacity_rule(block): self.old_rule.add((n, p), expr) self.old_rule = Constraint( - self.INVESTSTORAGES, - m.PERIODS, - noruleinit=True + self.INVESTSTORAGES, m.PERIODS, noruleinit=True ) - self.old_rule_build = BuildAction( - rule=_old_storage_capacity_rule - ) + self.old_rule_build = BuildAction(rule=_old_storage_capacity_rule) def _storage_level_no_investments(block): """Rule definition to force storage level to zero @@ -1078,70 +1043,104 @@ def _storage_level_no_investments(block): for n in self.INVESTSTORAGES: for p, t in m.TIMEINDEX: expr = block.storage_content[n, t] <= self.total[n, p] - self.storage_level_noinv_rule.add((n, p, t), expr) + block.storage_level_noinv_rule.add((n, p, t), expr) self.storage_level_noinv_rule = Constraint( - self.INVESTSTORAGES, - m.TIMEINDEX, - noruleinit=True + self.INVESTSTORAGES, m.TIMEINDEX, noruleinit=True ) self.storage_level_noinv_rule_build = BuildAction( rule=_storage_level_no_investments ) - # TODO: Handle initial content ... make it a sequence?! - def _inv_storage_init_content_max_rule(block, n): + def _inv_storage_init_content_max_rule(block): """Constraint for a variable initial storage capacity.""" - return ( - block.init_content[n] - <= n.investment.existing + block.invest[n] - ) + for n in self.INVESTSTORAGES_NO_INIT_CONTENT: + for p in m.PERIODS: + expr = (block.init_content[n, p] + <= block.total[n, p]) + block.init_content_limit.add((n, p), expr) self.init_content_limit = Constraint( self.INVESTSTORAGES_NO_INIT_CONTENT, - rule=_inv_storage_init_content_max_rule, + m.PERIODS, + noruleinit=True + ) + self.init_content_limit_build = BuildAction( + rule=_inv_storage_init_content_max_rule ) - def _inv_storage_init_content_fix_rule(block, n): + def _inv_storage_init_content_fix_rule(block): """Constraint for a fixed initial storage capacity.""" - return block.init_content[n] == n.initial_storage_level * ( - n.investment.existing + block.invest[n] - ) + for n in self.INVESTSTORAGES: + for p in m.PERIODS: + if p == 0: + expr = ( + block.init_content[n, p] + == n.initial_storage_level + * block.total[n, p] + ) + self.init_content_fix.add((n, p), expr) + else: + expr = ( + block.init_content[n, p] + == n.initial_storage_level + * min(0, block.total[n, p] > block.total[n, p-1]) + ) + self.init_content_fix.add((n, p), expr) self.init_content_fix = Constraint( self.INVESTSTORAGES_INIT_CONTENT, - rule=_inv_storage_init_content_fix_rule, + m. PERIODS, + noruleinit=True + ) + self.init_content_fix_build = BuildAction( + rule=_inv_storage_init_content_fix_rule ) - # TODO: Generalize! 0th step for the respective period / storage - def _storage_balance_first_rule(block, n): + # TODO: Check new init_content implementation! + def _storage_balance_first_rule(block): """ Rule definition for the storage balance of every storage n for the - first time step. + first time step of every period. """ - expr = 0 - expr += block.storage_content[n, 0] - expr += ( - -block.init_content[n] - * (1 - n.loss_rate[0]) ** m.timeincrement[0] - ) - expr += ( - n.fixed_losses_relative[0] - * (n.investment.existing + self.invest[n, 0]) - * m.timeincrement[0] - ) - expr += n.fixed_losses_absolute[0] * m.timeincrement[0] - expr += ( - -m.flow[i[n], n, 0, 0] * n.inflow_conversion_factor[0] - ) * m.timeincrement[0] - expr += ( - m.flow[n, o[n], 0, 0] / n.outflow_conversion_factor[0] - ) * m.timeincrement[0] - return expr == 0 + for n in self.INVESTSTORAGES: + for p in m.PERIODS: + first_step = m.TIMESTEPS_IN_PERIOD[p][0] + + lhs = 0 + lhs += block.storage_content[n, first_step] + lhs += ( + -block.init_content[n, p] + * (1 - n.loss_rate[first_step]) + ** m.timeincrement[first_step] + ) + lhs += ( + n.fixed_losses_relative[first_step] + * block.total[n, p] + * m.timeincrement[first_step] + ) + lhs += ( + n.fixed_losses_absolute[first_step] + * m.timeincrement[first_step] + ) + lhs += ( + -m.flow[i[n], n, p, first_step] + * n.inflow_conversion_factor[first_step] + ) * m.timeincrement[first_step] + lhs += ( + m.flow[n, o[n], p, first_step] + / n.outflow_conversion_factor[first_step] + ) * m.timeincrement[first_step] + rhs = 0 + self.balance_first.add((n, p), (lhs == rhs)) self.balance_first = Constraint( self.INVESTSTORAGES, + m.PERIODS, + noruleinit=True, + ) + self.balance_first_build = BuildAction( rule=_storage_balance_first_rule ) @@ -1173,19 +1172,47 @@ def _storage_balance_rule(block, n, p, t): self.balance = Constraint( self.INVESTSTORAGES, reduced_periods_timesteps, - rule=_storage_balance_rule + rule=_storage_balance_rule, ) - def _balanced_storage_rule(block, n): - return ( - block.storage_content[n, m.TIMESTEPS[-1]] - == block.init_content[n] + if not m.es.multi_period: + def _balanced_storage_rule(block, n): + return ( + block.storage_content[n, m.TIMESTEPS[-1]] + == block.init_content[n, 0] + ) + + self.balanced_cstr = Constraint( + self.INVESTSTORAGES_BALANCED, rule=_balanced_storage_rule ) - self.balanced_cstr = Constraint( - self.INVESTSTORAGES_BALANCED, - rule=_balanced_storage_rule - ) + else: + def _lifetime_balanced_storage_rule(block): + for n in self.INVESTSTORAGES_BALANCED: + lifetime = n.investment.lifetime + age = n.investment.age + for p in m.PERIODS: + if p == lifetime - age: + last_step = m.TIMESTEPS_IN_PERIOD[p][-1] + first_step = ( + m.TIMESTEPS_IN_PERIOD[p - lifetime + age][0] + ) + expr = ( + block.storage_content[n, last_step] + == block.storage_content[n, first_step] + ) + self.lifetime_balanced_cstr.add((n, p), expr) + else: + pass + + self.lifetime_balanced_cstr = Constraint( + self.INVESTSTORAGES_BALANCED, + m.PERIODS, + noruleinit=True + ) + self.lifetime_balanced_cstr_build = BuildAction( + rule=_lifetime_balanced_storage_rule + ) def _power_coupled(block): """ @@ -1195,21 +1222,17 @@ def _power_coupled(block): for n in self.INVEST_REL_IN_OUT: for p in m.PERIODS: expr = ( - m.InvestmentFlow.total[n, o[n], p] - ) * n.invest_relation_input_output == ( - m.InvestmentFlow.total[i[n], n, p] - ) + m.InvestmentFlowBlock.total[n, o[n], p] + ) * n.invest_relation_input_output == ( + m.InvestmentFlowBlock.total[i[n], n, p] + ) self.power_coupled.add((n, p), expr) self.power_coupled = Constraint( - self.INVEST_REL_IN_OUT, - m.PERIODS, - noruleinit=True + self.INVEST_REL_IN_OUT, m.PERIODS, noruleinit=True ) - self.power_coupled_build = BuildAction( - rule=_power_coupled - ) + self.power_coupled_build = BuildAction(rule=_power_coupled) def _storage_capacity_inflow_invest_rule(block): """ @@ -1221,15 +1244,12 @@ def _storage_capacity_inflow_invest_rule(block): for p in m.PERIODS: expr = ( m.InvestmentFlow.total[i[n], n, p] - == self.total[n, p] - * n.invest_relation_input_capacity + == self.total[n, p] * n.invest_relation_input_capacity ) self.storage_capacity_inflow.add((n, p), expr) self.storage_capacity_inflow = Constraint( - self.INVEST_REL_CAP_IN, - m.PERIODS, - noruleinit=True + self.INVEST_REL_CAP_IN, m.PERIODS, noruleinit=True ) self.storage_capacity_inflow_build = BuildAction( @@ -1244,23 +1264,18 @@ def _storage_capacity_outflow_invest_rule(block): """ for n in self.INVEST_REL_CAP_OUT: for p in m.PERIODS: - expr = ( - ( - m.InvestmentFlow.total[n, o[n], p] - ) - == self.total[n, p] - * n.invest_relation_output_capacity - ) + expr = (m.InvestmentFlow.total[n, o[n], p]) == self.total[ + n, p + ] * n.invest_relation_output_capacity self.storage_capacity_outflow.add((n, p), expr) self.storage_capacity_outflow = Constraint( - self.INVEST_REL_CAP_OUT, - m.PERIODS, - noruleinit=True + self.INVEST_REL_CAP_OUT, m.PERIODS, noruleinit=True ) self.storage_capacity_outflow_build = BuildAction( - rule=_storage_capacity_outflow_invest_rule) + rule=_storage_capacity_outflow_invest_rule + ) def _max_storage_content_invest_rule(block, n, p, t): """ @@ -1269,8 +1284,7 @@ def _max_storage_content_invest_rule(block, n, p, t): """ expr = ( self.storage_content[n, t] - <= self.total[n, p] - * n.max_storage_level[t] + <= self.total[n, p] * n.max_storage_level[t] ) return expr @@ -1287,8 +1301,7 @@ def _min_storage_content_invest_rule(block, n, p, t): """ expr = ( self.storage_content[n, t] - >= self.total[n, p] - * n.min_storage_level[t] + >= self.total[n, p] * n.min_storage_level[t] ) return expr @@ -1312,7 +1325,7 @@ def maximum_invest_limit(block, n, p): self.limit_max = Constraint( self.NON_CONVEX_INVESTSTORAGES, m.PERIODS, - rule=maximum_invest_limit + rule=maximum_invest_limit, ) def smallest_invest(block, n, p): @@ -1328,9 +1341,7 @@ def smallest_invest(block, n, p): ) self.limit_min = Constraint( - self.NON_CONVEX_INVESTSTORAGES, - m.PERIODS, - rule=smallest_invest + self.NON_CONVEX_INVESTSTORAGES, m.PERIODS, rule=smallest_invest ) def _overall_storage_maximum_investflow_rule(block): @@ -1344,14 +1355,11 @@ def _overall_storage_maximum_investflow_rule(block): """ for n in self.OVERALL_MAXIMUM_INVESTSTORAGES: for p in m.PERIODS: - expr = (self.total[n, p] - <= n.investment.overall_maximum) + expr = self.total[n, p] <= n.investment.overall_maximum self.overall_storage_maximum.add((n, p), expr) self.overall_storage_maximum = Constraint( - self.OVERALL_MAXIMUM_INVESTSTORAGES, - m.PERIODS, - noruleinit=True + self.OVERALL_MAXIMUM_INVESTSTORAGES, m.PERIODS, noruleinit=True ) self.overall_maximum_build = BuildAction( @@ -1365,13 +1373,14 @@ def _overall_minimum_investflow_rule(block): Note: This is only applicable for the last period """ for n in self.OVERALL_MINIMUM_INVESTSTORAGES: - expr = (n.investment.overall_minimum - <= self.total[n, m.PERIODS[-1]]) + expr = ( + n.investment.overall_minimum + <= self.total[n, m.PERIODS[-1]] + ) self.overall_minimum.add(n, expr) self.overall_minimum = Constraint( - self.OVERALL_MINIMUM_INVESTSTORAGES, - noruleinit=True + self.OVERALL_MINIMUM_INVESTSTORAGES, noruleinit=True ) self.overall_minimum_build = BuildAction( @@ -1403,27 +1412,33 @@ def _objective_expression(self): ) else: - msg = ("You did not specify an interest rate.\n" - "It will be set equal to the discount_rate of {} " - "of the model as a default.\nThis corresponds to a " - "social planner point of view and does not reflect " - "microeconomic interest requirements.") + msg = ( + "You did not specify an interest rate.\n" + "It will be set equal to the discount_rate of {} " + "of the model as a default.\nThis corresponds to a " + "social planner point of view and does not reflect " + "microeconomic interest requirements." + ) for n in self.CONVEX_INVESTSTORAGES: lifetime = n.investment.lifetime interest = n.investment.interest_rate if interest == 0: - warn(msg.format(m.discount_rate), - debugging.SuspiciousUsageWarning) + warn( + msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning, + ) interest = m.discount_rate for p in m.PERIODS: annuity = economics.annuity( capex=n.investment.ep_costs[p], n=lifetime, - wacc=interest + wacc=interest, ) investment_costs_increment = ( - self.invest[n, p] * annuity * lifetime + self.invest[n, p] + * annuity + * lifetime * ((1 + m.discount_rate) ** (-p)) ) investment_costs += investment_costs_increment @@ -1433,21 +1448,21 @@ def _objective_expression(self): lifetime = n.investment.lifetime interest = n.investment.interest_rate if interest == 0: - warn(msg.format(m.discount_rate), - debugging.SuspiciousUsageWarning) + warn( + msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning, + ) interest = m.discount_rate for p in m.PERIODS: annuity = economics.annuity( capex=n.investment.ep_costs[p], n=lifetime, - wacc=interest + wacc=interest, ) investment_costs_increment = ( - (self.invest[n, p] * annuity * lifetime - + self.invest_status[n, p] - * n.investment.offset[p]) - * ((1 + m.discount_rate) ** (-p)) - ) + self.invest[n, p] * annuity * lifetime + + self.invest_status[n, p] * n.investment.offset[p] + ) * ((1 + m.discount_rate) ** (-p)) investment_costs += investment_costs_increment period_investment_costs[p] += investment_costs_increment @@ -1455,14 +1470,12 @@ def _objective_expression(self): if n.investment.fixed_costs[0] is not None: lifetime = n.investment.lifetime for p in m.PERIODS: - fixed_costs += ( - sum(self.invest[n, p] - * n.investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range(p, p + lifetime) - ) - * ((1 + m.discount_rate) ** (-p)) - ) + fixed_costs += sum( + self.invest[n, p] + * n.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(p, p + lifetime) + ) * ((1 + m.discount_rate) ** (-p)) self.investment_costs = investment_costs self.period_investment_costs = period_investment_costs From f9aa75d73a6c7b431a2ae84798558e70d7ee5b10 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 11 Dec 2021 15:39:11 +0100 Subject: [PATCH 0071/1363] Introduce _periods class for easier handling --- src/oemof/solph/__init__.py | 2 ++ src/oemof/solph/_periods.py | 58 +++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/oemof/solph/_periods.py diff --git a/src/oemof/solph/__init__.py b/src/oemof/solph/__init__.py index ca12a91fe..9edc42c5b 100644 --- a/src/oemof/solph/__init__.py +++ b/src/oemof/solph/__init__.py @@ -13,6 +13,7 @@ from ._options import Investment from ._options import NonConvex from ._plumbing import sequence +from ._periods import Period __all__ = [ "buses", @@ -28,4 +29,5 @@ "Investment", "NonConvex", "sequence", + "Period" ] diff --git a/src/oemof/solph/_periods.py b/src/oemof/solph/_periods.py new file mode 100644 index 000000000..d681583b4 --- /dev/null +++ b/src/oemof/solph/_periods.py @@ -0,0 +1,58 @@ +""" +solph class defining periods for a multi-period model + +SPDX-FileCopyrightText: Johannes Kochems + +SPDX-License-Identifier: MIT + +""" +import pandas as pd + + +class Period: + """Periods for a multi-period optimization model + + Periods are defined on an annual basis. + Thus, it is not (yet) allowed to consider sub-annual periods + """ + + def __init__(self, start=None, end=None): + """Initialize a Period object + + Parameters + ---------- + start : str + String representation for the starting timestamp of a period + end : str + String representation for the last timestamp of a period + """ + e1 = ( + "Please define a valid {} time.\n" + "It has to be of type str and a valid date string representation." + ) + if start is None or not isinstance(start, str): + raise ValueError(e1.format("start")) + if end is None or not isinstance(start, str): + raise ValueError(e1.format("end")) + + e2 = ( + "You did not specify a valid date string representation which" + " is needed for converting {} time to a pandas.Timestamp object.\n" + "Please refer to the pandas date and time documentation." + ) + try: + self.start = pd.Timestamp(start) + except ValueError: + raise ValueError(e2.format("start")) + try: + self.end = pd.Timestamp(end) + except ValueError: + raise ValueError(e2.format("end")) + + def _extract_periods_length(self): + """Extracts the periods length in full years""" + return self.end.year - self.start.year + 1 + + @property + def periods_length(self): + return self._extract_periods_length() From ffb035d95aee7be63458a7e120bcac50f38ca9ae Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 11 Dec 2021 15:41:29 +0100 Subject: [PATCH 0072/1363] Fix typo --- src/oemof/solph/_models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 8a06ae293..727dc521a 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -88,9 +88,9 @@ def __init__(self, energysystem, **kwargs): except AttributeError: msg = ( "No valid time increment found. Please pass a valid " - "timeincremet parameter or pass an EnergySystem with " + "timeincrement parameter or pass an EnergySystem with " "a valid time index. Please note that a valid time" - "index need to have a 'freq' attribute." + "index needs to have a 'freq' attribute." ) raise AttributeError(msg) From bf59b66207da3cb9de6a37792486639746578bb3 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 11 Dec 2021 15:41:37 +0100 Subject: [PATCH 0073/1363] Add docs --- src/oemof/solph/_options.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/oemof/solph/_options.py b/src/oemof/solph/_options.py index 6b0e3e23b..20e2de2e9 100644 --- a/src/oemof/solph/_options.py +++ b/src/oemof/solph/_options.py @@ -41,6 +41,25 @@ class Investment: offset : float, :math:`c_{invest,fix}` Additional fix investment costs. Only applicable if `nonconvex` is set to `True`. + overall_maximum : float + Overall maximum capacity investment, i.e. the amount of capacity + that can be totally installed at maximum in any period (taking into + account decommissionings); only applicable for multi-period models + overall_minimum : float + Overall minimum capacity investment that needs to be installed + in the last period of the simulation (taking into account + decommissionings); only applicable for multi-period models + lifetime : int + Units lifetime, given in years; only applicable for multi-period + models + age : int + Units start age, given in years at the beginning of the simulation; + only applicable for multi-period models + interest_rate : float + Interest rate for calculating annuities when investing in that unit; + only applicable for multi-period models + fixed_costs : list of float + Fixed costs in each period; only applicable for multi-period models For the variables, constraints and parts of the objective function, which From 9c5acf132af365e3cbee50a2899f52e6e89495a6 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 11 Dec 2021 15:41:58 +0100 Subject: [PATCH 0074/1363] Introduce periods_length --- src/oemof/solph/_energy_system.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 82bda5482..30a322d6b 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -56,6 +56,7 @@ def __init__(self, multi_period=False, periods=None, **kwargs): super().__init__(**kwargs) self.multi_period = multi_period self._add_periods(periods) + self.periods_length = {k: v.periods_length for k, v in periods.items()} def _add_periods(self, periods): """Add periods to the energy system @@ -72,13 +73,19 @@ def _add_periods(self, periods): Keys are years as integer values, values are the respective number of the period starting with zero """ + # if not self.multi_period: + # periods = {"single_period": 0} + # elif periods is None: + # years = sorted( + # list( + # set(getattr(self.timeindex, 'year')) + # ) + # ) + # periods = dict(zip(years, range(len(years)))) + # self.periods = periods if not self.multi_period: - periods = {"single_period": 0} + periods = [0] + # TODO: Define a default elif periods is None: - years = sorted( - list( - set(getattr(self.timeindex, 'year')) - ) - ) - periods = dict(zip(years, range(len(years)))) + pass self.periods = periods From 057e1684e9d41e1f85551666094357694910e948 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 11 Dec 2021 18:45:33 +0100 Subject: [PATCH 0075/1363] Include default and determine gaps between periods --- src/oemof/solph/_energy_system.py | 56 ++++++++++++++++++++++++++----- src/oemof/solph/_periods.py | 36 ++++++++++++-------- 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 30a322d6b..721779a51 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -16,6 +16,8 @@ from oemof.network import energy_system as es +from oemof.solph._periods import Period + class EnergySystem(es.EnergySystem): """A variant of the class EnergySystem from @@ -55,23 +57,29 @@ def __init__(self, multi_period=False, periods=None, **kwargs): super().__init__(**kwargs) self.multi_period = multi_period - self._add_periods(periods) - self.periods_length = {k: v.periods_length for k, v in periods.items()} + self.periods = self._add_periods(periods) + self._extract_periods_lengths_and_gap() def _add_periods(self, periods): - """Add periods to the energy system + """Returns periods to be added to the energy system * For a single model, periods only contain one value. - * For a multi period model, periods must equal to years used in the + * For a multi-period model, periods are based on the years used in the timeindex. As a default, each year in the timeindex is mapped to its own period. Parameters ---------- periods : dict - The periods of a multi period model + Periods of a (multi-period) model Keys are years as integer values, - values are the respective number of the period starting with zero + values are the periods defined by their first and last timestep. + For a standard model, only one period is used. + + Returns + ------- + periods : dict + Periods of the energy system """ # if not self.multi_period: # periods = {"single_period": 0} @@ -85,7 +93,37 @@ def _add_periods(self, periods): # self.periods = periods if not self.multi_period: periods = [0] - # TODO: Define a default elif periods is None: - pass - self.periods = periods + years = sorted( + list( + set(getattr(self.timeindex, 'year')) + ) + ) + + periods = {} + filter_series = self.timeindex.to_series() + for number, year in enumerate(years): + start = filter_series.loc[ + filter_series.index.year == year].min() + end = filter_series.loc[filter_series.index.year == year].max() + periods[number] = Period(start, end) + + return periods + + def _extract_periods_lengths_and_gap(self): + """Determine length of one and diff between two subsequent periods""" + periods_gap = {0: 0} + if not self.multi_period: + periods_length = {0: 1} + else: + periods_length = {} + + previous_end = None + for number, (k, v) in enumerate(self.periods.items()): + periods_length[k] = v.periods_length + if number >= 1: + periods_gap[k] = v.start.year - previous_end.year - 1 + previous_end = v.end + + self.periods_length = periods_length + self.periods_gap = periods_gap diff --git a/src/oemof/solph/_periods.py b/src/oemof/solph/_periods.py index d681583b4..4f5fbb44a 100644 --- a/src/oemof/solph/_periods.py +++ b/src/oemof/solph/_periods.py @@ -21,18 +21,20 @@ def __init__(self, start=None, end=None): Parameters ---------- - start : str - String representation for the starting timestamp of a period + start : pd.Timestamp or str + Starting timestamp of a period or its string representation end : str - String representation for the last timestamp of a period + Last timestamp of a period or its string representation """ e1 = ( "Please define a valid {} time.\n" - "It has to be of type str and a valid date string representation." + "It has to be either of type pandas.Timestamp or " + "of type str and in that case needs to be a valid " + "date string representation." ) - if start is None or not isinstance(start, str): + if start is None or type(start) not in [str, pd.Timestamp]: raise ValueError(e1.format("start")) - if end is None or not isinstance(start, str): + if end is None or type(end) not in [str, pd.Timestamp]: raise ValueError(e1.format("end")) e2 = ( @@ -40,14 +42,20 @@ def __init__(self, start=None, end=None): " is needed for converting {} time to a pandas.Timestamp object.\n" "Please refer to the pandas date and time documentation." ) - try: - self.start = pd.Timestamp(start) - except ValueError: - raise ValueError(e2.format("start")) - try: - self.end = pd.Timestamp(end) - except ValueError: - raise ValueError(e2.format("end")) + if isinstance(start, str): + try: + self.start = pd.Timestamp(start) + except ValueError: + raise ValueError(e2.format("start")) + else: + self.start = start + if isinstance(end, str): + try: + self.end = pd.Timestamp(end) + except ValueError: + raise ValueError(e2.format("end")) + else: + self.end = end def _extract_periods_length(self): """Extracts the periods length in full years""" From 806380396f6c4d9d1295bb4b4911b5ed0cb15012 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sun, 12 Dec 2021 11:55:45 +0100 Subject: [PATCH 0076/1363] Adjust periods definition (annual basis) --- src/oemof/solph/_energy_system.py | 24 +++++++++--------------- src/oemof/solph/_models.py | 12 ++++++++---- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 721779a51..10ee01f71 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -13,7 +13,7 @@ SPDX-License-Identifier: MIT """ - +import pandas as pd from oemof.network import energy_system as es from oemof.solph._periods import Period @@ -81,16 +81,6 @@ def _add_periods(self, periods): periods : dict Periods of the energy system """ - # if not self.multi_period: - # periods = {"single_period": 0} - # elif periods is None: - # years = sorted( - # list( - # set(getattr(self.timeindex, 'year')) - # ) - # ) - # periods = dict(zip(years, range(len(years)))) - # self.periods = periods if not self.multi_period: periods = [0] elif periods is None: @@ -106,7 +96,11 @@ def _add_periods(self, periods): start = filter_series.loc[ filter_series.index.year == year].min() end = filter_series.loc[filter_series.index.year == year].max() - periods[number] = Period(start, end) + periods[number] = pd.date_range(start, end, freq="H") + else: + for k in periods.keys(): + if not isinstance(k, int): + raise ValueError("Period keys must be of type int.") return periods @@ -120,10 +114,10 @@ def _extract_periods_lengths_and_gap(self): previous_end = None for number, (k, v) in enumerate(self.periods.items()): - periods_length[k] = v.periods_length + periods_length[k] = v.max().year - v.min().year + 1 if number >= 1: - periods_gap[k] = v.start.year - previous_end.year - 1 - previous_end = v.end + periods_gap[k] = v.min().year - previous_end.year - 1 + previous_end = v.max() self.periods_length = periods_length self.periods_gap = periods_gap diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 727dc521a..f4edd5cd0 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -320,17 +320,21 @@ def _add_parent_block_sets(self): ordered=True ) else: - # pyomo set for timeindex of optimization problem + nested_list = [ + [k] * len(self.es.periods[k]) for k in self.es.periods.keys() + ] + flattened_list = [ + item for sublist in nested_list for item in sublist + ] self.TIMEINDEX = po.Set( initialize=list( - zip([self.es.periods[p] for p in self.es.timeindex.year], - range(len(self.es.timeindex))) + zip(flattened_list, range(len(self.es.timeindex))) ), ordered=True ) self.PERIODS = po.Set( - initialize=sorted(list(set(self.es.periods.values()))) + initialize=sorted(list(set(self.es.periods.keys()))) ) # (Re-)Map timesteps to periods From 43efd124b6920bcb6c1213afeec9d75210754721 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sun, 12 Dec 2021 11:57:39 +0100 Subject: [PATCH 0077/1363] Remove obsolete _periods module (substituted by plain pd.date_range) --- src/oemof/solph/__init__.py | 2 - src/oemof/solph/_energy_system.py | 2 - src/oemof/solph/_periods.py | 66 ------------------------------- 3 files changed, 70 deletions(-) delete mode 100644 src/oemof/solph/_periods.py diff --git a/src/oemof/solph/__init__.py b/src/oemof/solph/__init__.py index 9edc42c5b..ca12a91fe 100644 --- a/src/oemof/solph/__init__.py +++ b/src/oemof/solph/__init__.py @@ -13,7 +13,6 @@ from ._options import Investment from ._options import NonConvex from ._plumbing import sequence -from ._periods import Period __all__ = [ "buses", @@ -29,5 +28,4 @@ "Investment", "NonConvex", "sequence", - "Period" ] diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 10ee01f71..d29fd466d 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -16,8 +16,6 @@ import pandas as pd from oemof.network import energy_system as es -from oemof.solph._periods import Period - class EnergySystem(es.EnergySystem): """A variant of the class EnergySystem from diff --git a/src/oemof/solph/_periods.py b/src/oemof/solph/_periods.py deleted file mode 100644 index 4f5fbb44a..000000000 --- a/src/oemof/solph/_periods.py +++ /dev/null @@ -1,66 +0,0 @@ -""" -solph class defining periods for a multi-period model - -SPDX-FileCopyrightText: Johannes Kochems - -SPDX-License-Identifier: MIT - -""" -import pandas as pd - - -class Period: - """Periods for a multi-period optimization model - - Periods are defined on an annual basis. - Thus, it is not (yet) allowed to consider sub-annual periods - """ - - def __init__(self, start=None, end=None): - """Initialize a Period object - - Parameters - ---------- - start : pd.Timestamp or str - Starting timestamp of a period or its string representation - end : str - Last timestamp of a period or its string representation - """ - e1 = ( - "Please define a valid {} time.\n" - "It has to be either of type pandas.Timestamp or " - "of type str and in that case needs to be a valid " - "date string representation." - ) - if start is None or type(start) not in [str, pd.Timestamp]: - raise ValueError(e1.format("start")) - if end is None or type(end) not in [str, pd.Timestamp]: - raise ValueError(e1.format("end")) - - e2 = ( - "You did not specify a valid date string representation which" - " is needed for converting {} time to a pandas.Timestamp object.\n" - "Please refer to the pandas date and time documentation." - ) - if isinstance(start, str): - try: - self.start = pd.Timestamp(start) - except ValueError: - raise ValueError(e2.format("start")) - else: - self.start = start - if isinstance(end, str): - try: - self.end = pd.Timestamp(end) - except ValueError: - raise ValueError(e2.format("end")) - else: - self.end = end - - def _extract_periods_length(self): - """Extracts the periods length in full years""" - return self.end.year - self.start.year + 1 - - @property - def periods_length(self): - return self._extract_periods_length() From 56697517738bfcb269d3112961cd2d9d2ecd3fc8 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Mon, 13 Dec 2021 08:47:42 +0100 Subject: [PATCH 0078/1363] Adapt docs --- src/oemof/solph/_energy_system.py | 9 +++++++-- src/oemof/solph/_models.py | 1 - 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index d29fd466d..eda38e250 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -61,7 +61,7 @@ def __init__(self, multi_period=False, periods=None, **kwargs): def _add_periods(self, periods): """Returns periods to be added to the energy system - * For a single model, periods only contain one value. + * For a standard model, periods only contain one value. * For a multi-period model, periods are based on the years used in the timeindex. As a default, each year in the timeindex is mapped to its own period. @@ -103,7 +103,12 @@ def _add_periods(self, periods): return periods def _extract_periods_lengths_and_gap(self): - """Determine length of one and diff between two subsequent periods""" + """Determine length of one and difference between subsequent periods + + * `periods_length` contains the length of a period in full years + * `periods_gap` is the difference in years between subsequent periods, + attributed to the latter one + """ periods_gap = {0: 0} if not self.multi_period: periods_length = {0: 1} diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index f4edd5cd0..344f4322e 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -310,7 +310,6 @@ def _add_parent_block_sets(self): self.TIMESTEPS = po.Set(initialize=range(len(self.es.timeindex)), ordered=True) - # TODO: Generalize! if not self.es.multi_period: self.TIMEINDEX = po.Set( initialize=list( From ef486719c24a260aa1a9e9cc1f94068cfdff8693 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Mon, 13 Dec 2021 10:06:32 +0100 Subject: [PATCH 0079/1363] Include first draft for lifetime conversion --- src/oemof/solph/_energy_system.py | 26 +++++++++++++++++------ src/oemof/solph/flows/_investment_flow.py | 14 ++++++++++-- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index eda38e250..0b4693f0f 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -56,7 +56,7 @@ def __init__(self, multi_period=False, periods=None, **kwargs): super().__init__(**kwargs) self.multi_period = multi_period self.periods = self._add_periods(periods) - self._extract_periods_lengths_and_gap() + self._extract_periods_lengths_gap_and_years() def _add_periods(self, periods): """Returns periods to be added to the energy system @@ -102,25 +102,37 @@ def _add_periods(self, periods): return periods - def _extract_periods_lengths_and_gap(self): + # TODO: Check if length and gap are needed (no decommissions within period) + def _extract_periods_lengths_gap_and_years(self): """Determine length of one and difference between subsequent periods + and map periods to simulation years starting with 0 * `periods_length` contains the length of a period in full years * `periods_gap` is the difference in years between subsequent periods, - attributed to the latter one + attributed to the prior one + * `periods_years` is the simulation year corresponding to the start + of a period, starting with 0 """ - periods_gap = {0: 0} + periods_gap = {} if not self.multi_period: periods_length = {0: 1} else: periods_length = {} + periods_years = {0: 0} previous_end = None - for number, (k, v) in enumerate(self.periods.items()): + for k, v in self.periods.items(): periods_length[k] = v.max().year - v.min().year + 1 - if number >= 1: - periods_gap[k] = v.min().year - previous_end.year - 1 + if k >= 1: + periods_gap[k-1] = v.min().year - previous_end.year - 1 + periods_years[k] = ( + sum( + periods_length[kk] + periods_gap[kk] + for kk in range(k) + ) + ) previous_end = v.max() self.periods_length = periods_length self.periods_gap = periods_gap + self.periods_years = periods_years diff --git a/src/oemof/solph/flows/_investment_flow.py b/src/oemof/solph/flows/_investment_flow.py index b3deb779b..843d5eb00 100644 --- a/src/oemof/solph/flows/_investment_flow.py +++ b/src/oemof/solph/flows/_investment_flow.py @@ -435,9 +435,19 @@ def _old_capacity_rule_end(block): for i, o in self.INVESTFLOWS: lifetime = m.flows[i, o].investment.lifetime for p in m.PERIODS: - if lifetime <= p: + # No shutdown in first period + if p == 0: + expr = (self.old_end[i, o, p] == 0) + self.old_rule_end.add((i, o, p), expr) + elif lifetime <= m.es.periods_years[p]: + # Obtain commissioning period + comm_p = 0 + for k, v in m.es.periods_years.items(): + if m.es.periods_years[p] - lifetime - v < 0: + comm_p = k - 1 + break expr = (self.old_end[i, o, p] - == self.invest[i, o, p - lifetime]) + == self.invest[i, o, comm_p]) self.old_rule_end.add((i, o, p), expr) else: expr = (self.old_end[i, o, p] == 0) From 6afed497b5e6e91d75a765a675e6a564f07dd195 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 17 Dec 2021 11:37:04 +0100 Subject: [PATCH 0080/1363] Introduce warning for very suspicious multi-period stuff --- src/oemof/solph/_energy_system.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 0b4693f0f..90b891c71 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -13,8 +13,11 @@ SPDX-License-Identifier: MIT """ +import warnings + import pandas as pd from oemof.network import energy_system as es +from oemof.tools import debugging class EnergySystem(es.EnergySystem): @@ -54,6 +57,18 @@ def __init__(self, multi_period=False, periods=None, **kwargs): kwargs["groupings"] = GROUPINGS + kwargs.get("groupings", []) super().__init__(**kwargs) + + if multi_period: + msg = ( + "CAUTION! You specified 'multi_period=True' for your " + "energy system.\n This will lead to creating " + "a multi-period optimization modeling which can be " + "used e.g. for long-term investment modeling.\n" + "Please be aware that the feature is experimental as of " + "now. If you find anything suspicious or any bugs, " + "please report them." + ) + warnings.warn(msg, debugging.SuspiciousUsageWarning) self.multi_period = multi_period self.periods = self._add_periods(periods) self._extract_periods_lengths_gap_and_years() From c35552a5a10137bb59f25c5ad1c17aec6a3aa52d Mon Sep 17 00:00:00 2001 From: uvchik Date: Fri, 17 Dec 2021 12:11:11 +0100 Subject: [PATCH 0081/1363] Fix import --- tests/flow_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/flow_tests.py b/tests/flow_tests.py index 48ed86a14..5f93fc126 100644 --- a/tests/flow_tests.py +++ b/tests/flow_tests.py @@ -11,7 +11,7 @@ import pytest -from oemof.solph import Flow +from oemof.solph.flows import Flow def test_error_in_gradient_attribute(): From e6f8f1523caf629c6f436847a16e54eae6ff15a5 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 17 Dec 2021 12:15:04 +0100 Subject: [PATCH 0082/1363] Fix compatibility with standard model --- src/oemof/solph/_energy_system.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 90b891c71..3356e50f3 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -95,7 +95,7 @@ def _add_periods(self, periods): Periods of the energy system """ if not self.multi_period: - periods = [0] + periods = {0: 0} elif periods is None: years = sorted( list( @@ -131,6 +131,7 @@ def _extract_periods_lengths_gap_and_years(self): periods_gap = {} if not self.multi_period: periods_length = {0: 1} + periods_years = {0: 0} else: periods_length = {} periods_years = {0: 0} From 48984fbfbb2f46857a72d9e32cfc2e5c7d671004 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 17 Dec 2021 12:15:40 +0100 Subject: [PATCH 0083/1363] Define rule for decommissioning of exogenous capacity --- src/oemof/solph/flows/_investment_flow.py | 191 ++++++++++++---------- 1 file changed, 101 insertions(+), 90 deletions(-) diff --git a/src/oemof/solph/flows/_investment_flow.py b/src/oemof/solph/flows/_investment_flow.py index 843d5eb00..fe63fcbfc 100644 --- a/src/oemof/solph/flows/_investment_flow.py +++ b/src/oemof/solph/flows/_investment_flow.py @@ -335,26 +335,26 @@ def _investvar_bound_rule(block, i, o, p): within=NonNegativeReals ) - # Old capacity is built out of old exogenous and endogenous capacities - self.old = Var( - self.INVESTFLOWS, - m.PERIODS, - within=NonNegativeReals - ) + if m.es.multi_period: + self.old = Var( + self.INVESTFLOWS, + m.PERIODS, + within=NonNegativeReals + ) - # Old endogenous capacity to be decommissioned (due to lifetime) - self.old_end = Var( - self.INVESTFLOWS, - m.PERIODS, - within=NonNegativeReals - ) + # Old endogenous capacity to be decommissioned (due to lifetime) + self.old_end = Var( + self.INVESTFLOWS, + m.PERIODS, + within=NonNegativeReals + ) - # Old exogenous capacity to be decommissioned (due to lifetime) - self.old_exo = Var( - self.INVESTFLOWS, - m.PERIODS, - within=NonNegativeReals - ) + # Old exogenous capacity to be decommissioned (due to lifetime) + self.old_exo = Var( + self.INVESTFLOWS, + m.PERIODS, + within=NonNegativeReals + ) # create status variable for a non-convex investment flow self.invest_status = Var( @@ -428,82 +428,93 @@ def _total_capacity_rule(block): self.total_rule_build = BuildAction( rule=_total_capacity_rule) - def _old_capacity_rule_end(block): - """Rule definition for determining old endogenously installed - capacity to be decommissioned due to reaching its lifetime - """ - for i, o in self.INVESTFLOWS: - lifetime = m.flows[i, o].investment.lifetime - for p in m.PERIODS: - # No shutdown in first period - if p == 0: - expr = (self.old_end[i, o, p] == 0) - self.old_rule_end.add((i, o, p), expr) - elif lifetime <= m.es.periods_years[p]: - # Obtain commissioning period - comm_p = 0 - for k, v in m.es.periods_years.items(): - if m.es.periods_years[p] - lifetime - v < 0: - comm_p = k - 1 - break - expr = (self.old_end[i, o, p] - == self.invest[i, o, comm_p]) - self.old_rule_end.add((i, o, p), expr) - else: - expr = (self.old_end[i, o, p] == 0) - self.old_rule_end.add((i, o, p), expr) + if m.es.multi_period: + def _old_capacity_rule_end(block): + """Rule definition for determining old endogenously installed + capacity to be decommissioned due to reaching its lifetime + """ + for i, o in self.INVESTFLOWS: + lifetime = m.flows[i, o].investment.lifetime + for p in m.PERIODS: + # No shutdown in first period + if p == 0: + expr = (self.old_end[i, o, p] == 0) + self.old_rule_end.add((i, o, p), expr) + elif lifetime <= m.es.periods_years[p]: + # Obtain commissioning period + comm_p = 0 + for k, v in m.es.periods_years.items(): + if m.es.periods_years[p] - lifetime - v < 0: + comm_p = k - 1 + break + expr = (self.old_end[i, o, p] + == self.invest[i, o, comm_p]) + self.old_rule_end.add((i, o, p), expr) + else: + expr = (self.old_end[i, o, p] == 0) + self.old_rule_end.add((i, o, p), expr) + + self.old_rule_end = Constraint( + self.INVESTFLOWS, m.PERIODS, + noruleinit=True + ) + self.old_rule_end_build = BuildAction( + rule=_old_capacity_rule_end + ) - self.old_rule_end = Constraint( - self.INVESTFLOWS, m.PERIODS, - noruleinit=True - ) - self.old_rule_end_build = BuildAction( - rule=_old_capacity_rule_end - ) + def _old_capacity_rule_exo(block): + """Rule definition for determining old exogenously given + capacity to be decommissioned due to reaching its lifetime + """ + for i, o in self.INVESTFLOWS: + age = m.flows[i, o].investment.age + lifetime = m.flows[i, o].investment.lifetime + is_decommissioned = False + for p in m.PERIODS: + # No shutdown in first period + if p == 0: + expr = (self.old_exo[i, o, p] == 0) + self.old_rule_exo.add((i, o, p), expr) + elif lifetime - age <= m.es.periods_years[p]: + if not is_decommissioned: + expr = ( + self.old_exo[i, o, p] + == m.flows[i, o].investment.existing + ) + is_decommissioned = True + else: + expr = (self.old_exo[i, o, p] == 0) + self.old_rule_exo.add((i, o, p), expr) + else: + expr = (self.old_exo[i, o, p] == 0) + self.old_rule_exo.add((i, o, p), expr) + + self.old_rule_exo = Constraint( + self.INVESTFLOWS, m.PERIODS, + noruleinit=True + ) + self.old_rule_exo_build = BuildAction( + rule=_old_capacity_rule_exo + ) - def _old_capacity_rule_exo(block): - """Rule definition for determining old exogenously given capacity - to be decommissioned due to reaching its lifetime - """ - for i, o in self.INVESTFLOWS: - age = m.flows[i, o].investment.age - lifetime = m.flows[i, o].investment.lifetime - for p in m.PERIODS: - if lifetime - age == p: + def _old_capacity_rule(block): + """Rule definition for determining (overall) old capacity + to be decommissioned due to reaching its lifetime + """ + for i, o in self.INVESTFLOWS: + for p in m.PERIODS: expr = ( - self.old_exo[i, o, p] - == m.flows[i, o].investment.existing) - self.old_rule_exo.add((i, o, p), expr) - else: - expr = (self.old_exo[i, o, p] == 0) - self.old_rule_exo.add((i, o, p), expr) + self.old[i, o, p] + == self.old_end[i, o, p] + self.old_exo[i, o, p]) + self.old_rule.add((i, o, p), expr) - self.old_rule_exo = Constraint( - self.INVESTFLOWS, m.PERIODS, - noruleinit=True - ) - self.old_rule_exo_build = BuildAction( - rule=_old_capacity_rule_exo - ) - - def _old_capacity_rule(block): - """Rule definition for determining (overall) old capacity - to be decommissioned due to reaching its lifetime - """ - for i, o in self.INVESTFLOWS: - for p in m.PERIODS: - expr = ( - self.old[i, o, p] - == self.old_end[i, o, p] + self.old_exo[i, o, p]) - self.old_rule.add((i, o, p), expr) - - self.old_rule = Constraint( - self.INVESTFLOWS, m.PERIODS, - noruleinit=True - ) - self.old_rule_build = BuildAction( - rule=_old_capacity_rule - ) + self.old_rule = Constraint( + self.INVESTFLOWS, m.PERIODS, + noruleinit=True + ) + self.old_rule_build = BuildAction( + rule=_old_capacity_rule + ) def _investflow_fixed_rule(block): """Rule definition of constraint to fix flow variable From 2ffb6df1ada21374682d0469153253ea958c8d51 Mon Sep 17 00:00:00 2001 From: uvchik Date: Fri, 17 Dec 2021 12:16:57 +0100 Subject: [PATCH 0084/1363] Rename parameter to make it clearer --- src/oemof/solph/_energy_system.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 649e3489b..928d3d341 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -32,10 +32,23 @@ class EnergySystem(es.EnergySystem): solph.GROUPINGS , you can just use EnergySystem ` of oemof.network directly. + + Parameters + ---------- + timeindex : pandas.DatetimeIndex + + timeincrement : iterable + + timemode + kwargs """ def __init__( - self, timeindex=None, timeincrement=None, timemode="implicit", **kwargs + self, + timeindex=None, + timeincrement=None, + infer_last_interval=True, + **kwargs, ): # Doing imports at runtime is generally frowned upon, but should work # for now. See the TODO in :func:`constraint_grouping @@ -54,10 +67,14 @@ def __init__( ) raise TypeError(msg.format(type(timeindex))) - self.timemode = timemode + if infer_last_interval is True: + self.timemode = "implicit" + else: + self.timemode = "explicit" + + if infer_last_interval is True and timeindex is not None: + # Add one time interval to the timeindex by adding one time point. - if timemode == "implicit" and timeindex is not None: - # Add one timestep to the timeindex. timeindex = timeindex.union( pd.date_range( timeindex[-1] + timeindex.freq, From 5d22681878d43b46228619ff08ddda8a11c83aea Mon Sep 17 00:00:00 2001 From: uvchik Date: Fri, 17 Dec 2021 12:17:10 +0100 Subject: [PATCH 0085/1363] Add error messages --- src/oemof/solph/_energy_system.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 928d3d341..102f95780 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -74,6 +74,14 @@ def __init__( if infer_last_interval is True and timeindex is not None: # Add one time interval to the timeindex by adding one time point. + if timeindex.freq is None: + msg = ( + "You cannot infer the last interval if the 'freq' " + "attribute of your DatetimeIndex is None. Set " + " 'infer_last_interval=False' or specify a DatetimeIndex " + "with a valid frequency." + ) + raise AttributeError(msg) timeindex = timeindex.union( pd.date_range( @@ -83,11 +91,21 @@ def __init__( ) ) + # catch wrong combinations and infer timeincrement from timeindex. if timeincrement is not None and timeindex is not None: - raise AttributeError("Don't do it.") + msg = ( + "Specifying the timeincrement and the timeindex parameter at " + "the same time is not allowed since these might be " + "conflicting to each other." + ) + raise AttributeError(msg) elif timeincrement is None and timeindex is None: - pass + msg = ( + "You have to either define the parameter timeincrement or " + "timeindex to initialise a valid EnergySystem." + ) + raise AttributeError(msg) elif timeindex is not None and timeincrement is None: df = pd.DataFrame(timeindex) From 5a57cdd10d9a47657d8dcde837bf67bc127821f6 Mon Sep 17 00:00:00 2001 From: uvchik Date: Fri, 17 Dec 2021 12:18:10 +0100 Subject: [PATCH 0086/1363] Remove absend cost gradient costs --- src/oemof/solph/flows/_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index b56984784..936cad098 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -198,7 +198,7 @@ def __init__(self, **kwargs): setattr( self, attribute, - {"ub": sequence(value["ub"]), "costs": value["costs"]}, + {"ub": sequence(value["ub"])}, ) else: From 96065b963106a9bfb29472ecb8fca123b2fe0d16 Mon Sep 17 00:00:00 2001 From: uvchik Date: Fri, 17 Dec 2021 12:19:10 +0100 Subject: [PATCH 0087/1363] Rename parameter om to model and add new parameter --- src/oemof/solph/processing.py | 52 ++++++++++++------- .../test_simple_invest_fixed.py | 2 +- .../test_simple_model/test_simple_dispatch.py | 2 +- .../test_simple_dispatch_one.py | 6 +-- .../test_simple_model/test_simple_invest.py | 2 +- 5 files changed, 39 insertions(+), 25 deletions(-) diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index 3f0c60d52..36713b671 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -144,7 +144,7 @@ def set_result_index(df_dict, k, result_index): ).with_traceback(sys.exc_info()[2]) -def results(om): +def results(model, remove_last_time_point=None): """ Create a result dictionary from the result DataFrame. @@ -153,8 +153,28 @@ def results(om): and flows. The dictionary is keyed by the nodes e.g. `results[idx]['scalars']` and flows e.g. `results[n, n]['sequences']`. + + Parameters + ---------- + model : oemof.solph.BaseModel + A solved oemof.solph model. + remove_last_time_point : bool + The last time point of all TIMEPOINT variables is removed to get the + same length as the TIMESTEP (interval) variables without getting + nan-values. By default the last time point is removed if it has not + been defined by the user in the EnergySystem but inferred. If all + time points has been defined explicitly by the user the last time point + will not be removed by default. In that case all interval variables + will get one row with nan-values to have the same index for all + variables. """ - df = create_dataframe(om) + if remove_last_time_point is None: + if model.es.timemode == "implicit": + remove_last_time_point = True + else: + remove_last_time_point = False + + df = create_dataframe(model) # create a dict of dataframes keyed by oemof tuples df_dict = { @@ -165,15 +185,15 @@ def results(om): } # Define index - if om.es.timeindex is None: - result_index = list(range(len(om.es.timeincrement) + 1)) + if model.es.timeindex is None: + result_index = list(range(len(model.es.timeincrement) + 1)) else: - result_index = om.es.timeindex + result_index = model.es.timeindex # create final result dictionary by splitting up the dataframes in the # dataframe dict into a series for scalar data and dataframe for sequences result = {} - if om.es.timemode == "implicit": + if remove_last_time_point is True: # In the implicit time mode the first time point is removed. # The values of intervals belong to the time at the end of the # interval. @@ -185,33 +205,27 @@ def results(om): ) set_result_index(df_dict, k, result_index) result[k] = divide_scalars_sequences(df_dict, k) - elif om.es.timemode == "explicit": + else: for k in df_dict: df_dict[k].set_index("timestep", inplace=True) df_dict[k] = df_dict[k].pivot( columns="variable_name", values="value" ) - # Add empty row on top - df_dict[k] = pd.concat( - [ - pd.DataFrame(columns=df_dict[k].columns, index=[0]), - df_dict[k], - ], - ignore_index=True, - ) + # Add empty row at the end + df_dict[k] = df_dict[k].append(pd.Series(), ignore_index=True) set_result_index(df_dict, k, result_index) result[k] = divide_scalars_sequences(df_dict, k) # add dual variables for bus constraints - if om.dual is not None: + if model.dual is not None: grouped = groupby( - sorted(om.BusBlock.balance.iterkeys()), lambda p: p[0] + sorted(model.BusBlock.balance.iterkeys()), lambda p: p[0] ) for bus, timesteps in grouped: duals = [ - om.dual[om.BusBlock.balance[bus, t]] for _, t in timesteps + model.dual[model.BusBlock.balance[bus, t]] for _, t in timesteps ] - df = pd.DataFrame({"duals": duals}, index=om.es.timeindex) + df = pd.DataFrame({"duals": duals}, index=model.es.timeindex) if (bus, None) not in result.keys(): result[(bus, None)] = { "sequences": df, diff --git a/tests/test_scripts/test_solph/test_invest_fix_flow/test_simple_invest_fixed.py b/tests/test_scripts/test_solph/test_invest_fix_flow/test_simple_invest_fixed.py index ecc8ef737..31a4f244a 100644 --- a/tests/test_scripts/test_solph/test_invest_fix_flow/test_simple_invest_fixed.py +++ b/tests/test_scripts/test_solph/test_invest_fix_flow/test_simple_invest_fixed.py @@ -80,7 +80,7 @@ def test_dispatch_fix_example(solver="cbc", periods=10): # ################################ results ################################ # generic result object - results = processing.results(om=optimization_model) + results = processing.results(model=optimization_model) # subset of results that includes all flows into and from electrical bus # sequences are stored within a pandas.DataFrames and scalars e.g. diff --git a/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch.py b/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch.py index d81ae10c3..22ce5b98b 100644 --- a/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch.py +++ b/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch.py @@ -165,7 +165,7 @@ def test_dispatch_example(solver="cbc", periods=24 * 5): # ################################ results ################################ # generic result object - results = processing.results(om=optimization_model) + results = processing.results(model=optimization_model) # subset of results that includes all flows into and from electrical bus # sequences are stored within a pandas.DataFrames and scalars e.g. diff --git a/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one.py b/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one.py index 63aaec144..6bd422468 100644 --- a/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one.py +++ b/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one.py @@ -79,7 +79,7 @@ def test_dispatch_one_time_step(solver="cbc"): conversion_factors={bel: 1 / 3, b_heat_source: (cop - 1) / cop}, ) - energysystem = EnergySystem(timeincrement=[1]) + energysystem = EnergySystem(timeincrement=[1], timemode="explicit") energysystem.add( bgas, bel, @@ -106,12 +106,12 @@ def test_dispatch_one_time_step(solver="cbc"): optimization_model.results() # ################################ results ################################ - data = views.node(processing.results(om=optimization_model), "b_el") + data = views.node(processing.results(model=optimization_model), "b_el") # generate results to be evaluated in tests results = data["sequences"].sum(axis=0).to_dict() - print("*************", data["sequences"].index) + print("DateTimeIndex:", data["sequences"].index) test_results = { (("wind", "b_el"), "flow"): 33, diff --git a/tests/test_scripts/test_solph/test_simple_model/test_simple_invest.py b/tests/test_scripts/test_solph/test_simple_model/test_simple_invest.py index 285fba257..d3db48aa8 100644 --- a/tests/test_scripts/test_solph/test_simple_model/test_simple_invest.py +++ b/tests/test_scripts/test_solph/test_simple_model/test_simple_invest.py @@ -181,7 +181,7 @@ def test_dispatch_example(solver="cbc", periods=24 * 5): # ################################ results ################################ # generic result object - results = processing.results(om=optimization_model) + results = processing.results(model=optimization_model) # subset of results that includes all flows into and from electrical bus # sequences are stored within a pandas.DataFrames and scalars e.g. From 088da305472b43d60054f1244a37e6796de36ef3 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 17 Dec 2021 15:19:08 +0100 Subject: [PATCH 0088/1363] Adjust constraints for old capacity for proper lifetime tracking / decomissioning --- .../solph/components/_generic_storage.py | 162 ++++++++++-------- .../components/experimental/_sink_dsm.py | 146 ++++++++++------ src/oemof/solph/flows/_investment_flow.py | 15 +- 3 files changed, 194 insertions(+), 129 deletions(-) diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index 8b532465d..f0ced20b9 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -912,19 +912,21 @@ def _storage_investvar_bound_rule(block, n, p): initialize=0 ) - # Old capacity to be decommissioned (due to lifetime) - # Old capacity is built out of old exogenous and endogenous capacities - self.old = Var(self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals) + if m.es.multi_period: + # Old capacity to be decommissioned (due to lifetime) + self.old = Var( + self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals + ) - # Old endogenous capacity to be decommissioned (due to lifetime) - self.old_end = Var( - self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals - ) + # Old endogenous capacity to be decommissioned (due to lifetime) + self.old_end = Var( + self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals + ) - # Old exogenous capacity to be decommissioned (due to lifetime) - self.old_exo = Var( - self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals - ) + # Old exogenous capacity to be decommissioned (due to lifetime) + self.old_exo = Var( + self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals + ) self.init_content = Var( self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals @@ -971,70 +973,96 @@ def _total_storage_capacity_rule(block): rule=_total_storage_capacity_rule ) - def _old_storage_capacity_rule_end(block): - """Rule definition for determining old endogenously installed - capacity to be decommissioned due to reaching its lifetime - """ - for n in self.INVESTSTORAGES: - lifetime = n.investment.lifetime - for p in m.PERIODS: - if lifetime <= p: - expr = ( - self.old_end[n, p] == self.invest[n, p - lifetime] - ) - self.old_rule_end.add((n, p), expr) - else: - expr = self.old_end[n, p] == 0 - self.old_rule_end.add((n, p), expr) + if m.es.multi_period: + def _old_storage_capacity_rule_end(block): + """Rule definition for determining old endogenously installed + capacity to be decommissioned due to reaching its lifetime + """ + for n in self.INVESTSTORAGES: + lifetime = n.investment.lifetime + for p in m.PERIODS: + # No shutdown in first period + if p == 0: + expr = (self.old_end[n, p] == 0) + self.old_rule_end.add((n, p), expr) + elif lifetime <= m.es.periods_years[p]: + # Obtain commissioning period + comm_p = 0 + for k, v in m.es.periods_years.items(): + if m.es.periods_years[p] - lifetime - v < 0: + # change of sign is detected + comm_p = k - 1 + break + expr = ( + self.old_end[n, p] + == self.invest[n, comm_p] + ) + self.old_rule_end.add((n, p), expr) + else: + expr = self.old_end[n, p] == 0 + self.old_rule_end.add((n, p), expr) - self.old_rule_end = Constraint( - self.INVESTSTORAGES, m.PERIODS, noruleinit=True - ) + self.old_rule_end = Constraint( + self.INVESTSTORAGES, m.PERIODS, noruleinit=True + ) - self.old_rule_end_build = BuildAction( - rule=_old_storage_capacity_rule_end - ) + self.old_rule_end_build = BuildAction( + rule=_old_storage_capacity_rule_end + ) - def _old_storage_capacity_rule_exo(block): - """Rule definition for determining old exogenously given capacity - to be decommissioned due to reaching its lifetime - """ - for n in self.INVESTSTORAGES: - age = n.investment.age - lifetime = n.investment.lifetime - for p in m.PERIODS: - if lifetime - age == p: - expr = self.old_exo[n, p] == n.investment.existing - self.old_rule_exo.add((n, p), expr) - else: - expr = self.old_exo[n, p] == 0 - self.old_rule_exo.add((n, p), expr) + def _old_storage_capacity_rule_exo(block): + """Rule definition for determining old exogenously given + capacity to be decommissioned due to reaching its lifetime + """ + for n in self.INVESTSTORAGES: + age = n.investment.age + lifetime = n.investment.lifetime + is_decommissioned = False + for p in m.PERIODS: + # No shutdown in first period + if p == 0: + expr = (self.old_exo[n, p] == 0) + self.old_rule_exo.add((n, p), expr) + elif lifetime - age <= m.es.periods_years[p]: + # Track decommissioning status + if not is_decommissioned: + expr = ( + self.old_exo[n, p] + == n.investment.existing + ) + is_decommissioned = True + else: + expr = (self.old_exo[n, p] == 0) + self.old_rule_exo.add((n, p), expr) + else: + expr = self.old_exo[n, p] == 0 + self.old_rule_exo.add((n, p), expr) - self.old_rule_exo = Constraint( - self.INVESTSTORAGES, m.PERIODS, noruleinit=True - ) + self.old_rule_exo = Constraint( + self.INVESTSTORAGES, m.PERIODS, noruleinit=True + ) - self.old_rule_exo_build = BuildAction( - rule=_old_storage_capacity_rule_exo - ) + self.old_rule_exo_build = BuildAction( + rule=_old_storage_capacity_rule_exo + ) - def _old_storage_capacity_rule(block): - """Rule definition for determining (overall) old capacity - to be decommissioned due to reaching its lifetime - """ - for n in self.INVESTSTORAGES: - for p in m.PERIODS: - expr = ( - self.old[n, p] - == self.old_end[n, p] + self.old_exo[n, p] - ) - self.old_rule.add((n, p), expr) + def _old_storage_capacity_rule(block): + """Rule definition for determining (overall) old capacity + to be decommissioned due to reaching its lifetime + """ + for n in self.INVESTSTORAGES: + for p in m.PERIODS: + expr = ( + self.old[n, p] + == self.old_end[n, p] + self.old_exo[n, p] + ) + self.old_rule.add((n, p), expr) - self.old_rule = Constraint( - self.INVESTSTORAGES, m.PERIODS, noruleinit=True - ) + self.old_rule = Constraint( + self.INVESTSTORAGES, m.PERIODS, noruleinit=True + ) - self.old_rule_build = BuildAction(rule=_old_storage_capacity_rule) + self.old_rule_build = BuildAction(rule=_old_storage_capacity_rule) def _storage_level_no_investments(block): """Rule definition to force storage level to zero diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index 0208ba9c2..a06a76103 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -887,15 +887,19 @@ def _dsm_investvar_bound_rule(block, g, p): # Total capacity self.total = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) - # Old capacity to be decommissioned (due to lifetime) - # Old capacity is built out of old exogenous and endogenous capacities - self.old = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) + if m.es.multi_period: + # Old capacity to be decommissioned (due to lifetime) + self.old = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) - # Old endogenous capacity to be decommissioned (due to lifetime) - self.old_end = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) + # Old endogenous capacity to be decommissioned (due to lifetime) + self.old_end = Var( + self.investdsm, m.PERIODS, within=NonNegativeReals + ) - # Old exogenous capacity to be decommissioned (due to lifetime) - self.old_exo = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) + # Old exogenous capacity to be decommissioned (due to lifetime) + self.old_exo = Var( + self.investdsm, m.PERIODS, within=NonNegativeReals + ) # Variable load shift down self.dsm_do_shift = Var( @@ -939,61 +943,89 @@ def _total_dsm_capacity_rule(block): self.total_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) self.total_dsm_rule_build = BuildAction(rule=_total_dsm_capacity_rule) - def _old_dsm_capacity_rule_end(block): - """Rule definition for determining old endogenously installed - capacity to be decommissioned due to reaching its lifetime - """ - for g in group: - lifetime = g.investment.lifetime - for p in m.PERIODS: - if lifetime <= p: - expr = ( - self.old_end[g, p] == self.invest[g, p - lifetime] - ) - self.old_dsm_rule_end.add((g, p), expr) - else: - expr = self.old_end[g, p] == 0 - self.old_dsm_rule_end.add((g, p), expr) + if m.es.multi_period: + def _old_dsm_capacity_rule_end(block): + """Rule definition for determining old endogenously installed + capacity to be decommissioned due to reaching its lifetime + """ + for g in group: + lifetime = g.investment.lifetime + for p in m.PERIODS: + # No shutdown in first period + if p == 0: + expr = (self.old_end[g, p] == 0) + self.old_rule_end.add((g, p), expr) + elif lifetime <= m.es.periods_years[p]: + # Obtain commissioning period + comm_p = 0 + for k, v in m.es.periods_years.items(): + if m.es.periods_years[p] - lifetime - v < 0: + # change of sign is detected + comm_p = k - 1 + break + expr = (self.old_end[g, p] + == self.invest[g, comm_p]) + self.old_rule_end.add((g, p), expr) + else: + expr = self.old_end[g, p] == 0 + self.old_dsm_rule_end.add((g, p), expr) - self.old_dsm_rule_end = Constraint(group, m.PERIODS, noruleinit=True) - self.old_dsm_rule_end_build = BuildAction( - rule=_old_dsm_capacity_rule_end - ) + self.old_dsm_rule_end = Constraint( + group, m.PERIODS, noruleinit=True + ) + self.old_dsm_rule_end_build = BuildAction( + rule=_old_dsm_capacity_rule_end + ) - def _old_dsm_capacity_rule_exo(block): - """Rule definition for determining old exogenously given capacity - to be decommissioned due to reaching its lifetime - """ - for g in group: - age = g.investment.age - lifetime = g.investment.lifetime - for p in m.PERIODS: - if lifetime - age == p: - expr = self.old_exo[g, p] == g.investment.existing - self.old_dsm_rule_exo.add((g, p), expr) - else: - expr = self.old_exo[g, p] == 0 - self.old_dsm_rule_exo.add((g, p), expr) + def _old_dsm_capacity_rule_exo(block): + """Rule definition for determining old exogenously given + capacity to be decommissioned due to reaching its lifetime + """ + for g in group: + age = g.investment.age + lifetime = g.investment.lifetime + is_decommissioned = False + for p in m.PERIODS: + # No shutdown in first period + if p == 0: + expr = (self.old_exo[g, p] == 0) + self.old_rule_exo.add((g, p), expr) + elif lifetime - age <= m.es.periods_years[p]: + # Track decommissioning status + if not is_decommissioned: + expr = ( + self.old_exo[g, p] + == m.flows[g].investment.existing + ) + is_decommissioned = True + else: + expr = (self.old_exo[g, p] == 0) + self.old_rule_exo.add((g, p), expr) + else: + expr = self.old_exo[g, p] == 0 + self.old_dsm_rule_exo.add((g, p), expr) - self.old_dsm_rule_exo = Constraint(group, m.PERIODS, noruleinit=True) - self.old_dsm_rule_exo_build = BuildAction( - rule=_old_dsm_capacity_rule_exo - ) + self.old_dsm_rule_exo = Constraint( + group, m.PERIODS, noruleinit=True + ) + self.old_dsm_rule_exo_build = BuildAction( + rule=_old_dsm_capacity_rule_exo + ) - def _old_dsm_capacity_rule(block): - """Rule definition for determining (overall) old capacity - to be decommissioned due to reaching its lifetime - """ - for g in group: - for p in m.PERIODS: - expr = ( - self.old[g, p] - == self.old_end[g, p] + self.old_exo[g, p] - ) - self.old_dsm_rule.add((g, p), expr) + def _old_dsm_capacity_rule(block): + """Rule definition for determining (overall) old capacity + to be decommissioned due to reaching its lifetime + """ + for g in group: + for p in m.PERIODS: + expr = ( + self.old[g, p] + == self.old_end[g, p] + self.old_exo[g, p] + ) + self.old_dsm_rule.add((g, p), expr) - self.old_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) - self.old_dsm_rule_build = BuildAction(rule=_old_dsm_capacity_rule) + self.old_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_build = BuildAction(rule=_old_dsm_capacity_rule) def _shift_shed_vars_rule(block): """Force shifting resp. shedding variables to zero dependent diff --git a/src/oemof/solph/flows/_investment_flow.py b/src/oemof/solph/flows/_investment_flow.py index fe63fcbfc..0dcd6613e 100644 --- a/src/oemof/solph/flows/_investment_flow.py +++ b/src/oemof/solph/flows/_investment_flow.py @@ -414,6 +414,7 @@ def _total_capacity_rule(block): == self.invest[i, o, p] + m.flows[i, o].investment.existing) self.total_rule.add((i, o, p), expr) + # applicable for multi-period model only else: expr = (self.total[i, o, p] == self.invest[i, o, p] @@ -445,6 +446,7 @@ def _old_capacity_rule_end(block): comm_p = 0 for k, v in m.es.periods_years.items(): if m.es.periods_years[p] - lifetime - v < 0: + # change of sign is detected comm_p = k - 1 break expr = (self.old_end[i, o, p] @@ -476,6 +478,7 @@ def _old_capacity_rule_exo(block): expr = (self.old_exo[i, o, p] == 0) self.old_rule_exo.add((i, o, p), expr) elif lifetime - age <= m.es.periods_years[p]: + # Track decommissioning status if not is_decommissioned: expr = ( self.old_exo[i, o, p] @@ -689,11 +692,13 @@ def _objective_expression(self): ) else: - msg = ("You did not specify an interest rate.\n" - "It will be set equal to the discount_rate of {} " - "of the model as a default.\nThis corresponds to a " - "social planner point of view and does not reflect " - "microeconomic interest requirements.") + msg = ( + "You did not specify an interest rate.\n" + "It will be set equal to the discount_rate of {} " + "of the model as a default.\nThis corresponds to a " + "social planner point of view and does not reflect " + "microeconomic interest requirements." + ) for i, o in self.CONVEX_INVESTFLOWS: lifetime = m.flows[i, o].investment.lifetime From 8f77379527d09d3cde7eb8570c5d119ea66a8372 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 17 Dec 2021 16:59:33 +0100 Subject: [PATCH 0089/1363] Add proper discounting / year conversion --- src/oemof/solph/_options.py | 17 +-- src/oemof/solph/components/_generic_chp.py | 2 +- .../solph/components/_generic_storage.py | 57 +++++---- .../components/experimental/_sink_dsm.py | 109 ++++++++++++------ src/oemof/solph/flows/_flow.py | 14 ++- src/oemof/solph/flows/_investment_flow.py | 23 +++- src/oemof/solph/flows/_non_convex_flow.py | 19 ++- 7 files changed, 157 insertions(+), 84 deletions(-) diff --git a/src/oemof/solph/_options.py b/src/oemof/solph/_options.py index 20e2de2e9..950be9ae4 100644 --- a/src/oemof/solph/_options.py +++ b/src/oemof/solph/_options.py @@ -78,7 +78,7 @@ def __init__( offset=0, overall_maximum=None, overall_minimum=None, - lifetime=20, + lifetime=None, age=0, interest_rate=0, fixed_costs=None, @@ -137,16 +137,11 @@ def _check_invest_attributes_offset(self): raise AttributeError(e3) def _check_age_and_lifetime(self): - if self.lifetime == 20: - w1 = ("Using a lifetime of 20 periods," - " which is the default value.\nIf you don't consider a" - " multi-period investment model, you can safely ignore " - " this warning - all fine!") - warn(w1, debugging.SuspiciousUsageWarning) - if self.age >= self.lifetime: - e4 = ("A unit's age must be smaller than its " - "expected lifetime.") - raise AttributeError(e4) + if self.lifetime is not None: + if self.age >= self.lifetime: + e4 = ("A unit's age must be smaller than its " + "expected lifetime.") + raise AttributeError(e4) class NonConvex: diff --git a/src/oemof/solph/components/_generic_chp.py b/src/oemof/solph/components/_generic_chp.py index 6f5bd663e..903e5d7bc 100644 --- a/src/oemof/solph/components/_generic_chp.py +++ b/src/oemof/solph/components/_generic_chp.py @@ -498,7 +498,7 @@ def _objective_expression(self): r"""Objective expression for generic CHPs with no investment. Note: This adds nothing as variable costs are already - added in the Block :class:`Flow`. + added in the Block :class:`FlowBlock`. """ if not hasattr(self, "GENERICCHPS"): return 0 diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index f0ced20b9..9b2017d46 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -171,8 +171,8 @@ def __init__( "invest_relation_output_capacity" ) self._invest_group = isinstance(self.investment, Investment) - self.lifetime_inflow = kwargs.get("lifetime_inflow", 20) - self.lifetime_outflow = kwargs.get("lifetime_outflow", 20) + self.lifetime_inflow = kwargs.get("lifetime_inflow", None) + self.lifetime_outflow = kwargs.get("lifetime_outflow", None) # Check number of flows. self._check_number_of_flows() @@ -254,14 +254,6 @@ def _check_invest_attributes(self): "or investment.minimum has to be non-zero." ) raise AttributeError(e3) - if self.lifetime_inflow == 20 or self.lifetime_outflow == 20: - w1 = ( - "Using a lifetime of 20 periods," - " which is the default value.\nIf you don't consider a" - " multi-period investment model, you can safely ignore " - " this warning - all fine!" - ) - warn(w1, debugging.SuspiciousUsageWarning) self._set_flows() @@ -980,6 +972,12 @@ def _old_storage_capacity_rule_end(block): """ for n in self.INVESTSTORAGES: lifetime = n.investment.lifetime + if lifetime is None: + msg = ("You have to specify a lifetime " + "for an InvestmentFlow in " + "a multi-period model! Value for {} " + "is missing.".format(n)) + raise ValueError(msg) for p in m.PERIODS: # No shutdown in first period if p == 0: @@ -1416,7 +1414,7 @@ def _overall_minimum_investflow_rule(block): ) def _objective_expression(self): - """Objective expression with fixed and investement costs.""" + """Objective expression with fixed and investment costs.""" m = self.parent_block() if not hasattr(self, "INVESTSTORAGES"): @@ -1467,7 +1465,8 @@ def _objective_expression(self): self.invest[n, p] * annuity * lifetime - * ((1 + m.discount_rate) ** (-p)) + * ((1 + m.discount_rate) + ** (-m.es.periods_years[p])) ) investment_costs += investment_costs_increment period_investment_costs[p] += investment_costs_increment @@ -1488,9 +1487,12 @@ def _objective_expression(self): wacc=interest, ) investment_costs_increment = ( - self.invest[n, p] * annuity * lifetime - + self.invest_status[n, p] * n.investment.offset[p] - ) * ((1 + m.discount_rate) ** (-p)) + ( + self.invest[n, p] * annuity * lifetime + + self.invest_status[n, p] * n.investment.offset[p] + ) * ((1 + m.discount_rate) + ** (-m.es.periods_years[p])) + ) investment_costs += investment_costs_increment period_investment_costs[p] += investment_costs_increment @@ -1498,15 +1500,22 @@ def _objective_expression(self): if n.investment.fixed_costs[0] is not None: lifetime = n.investment.lifetime for p in m.PERIODS: - fixed_costs += sum( - self.invest[n, p] - * n.investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range(p, p + lifetime) - ) * ((1 + m.discount_rate) ** (-p)) - - self.investment_costs = investment_costs - self.period_investment_costs = period_investment_costs + fixed_costs += ( + sum( + self.invest[n, p] + * n.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime + ) + ) * ((1 + m.discount_rate) + ** (-m.es.periods_years[p])) + ) + + self.investment_costs = Expression(expr=investment_costs) + self.period_investment_costs = Expression(expr=period_investment_costs) + self.fixed_costs = Expression(expr=fixed_costs) self.costs = Expression(expr=investment_costs + fixed_costs) return self.costs diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index a06a76103..6ca85e43a 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -17,6 +17,7 @@ """ import itertools +from _ast import Expression from warnings import warn from numpy import mean @@ -737,7 +738,8 @@ def _objective_expression(self): self.dsm_up[g, t] * m.objective_weighting[t] * g.cost_dsm_up[t] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) variable_costs += ( ( @@ -745,7 +747,8 @@ def _objective_expression(self): + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] ) * m.objective_weighting[t] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) if g.fixed_costs[0] is not None: @@ -754,9 +757,12 @@ def _objective_expression(self): g.max_demand * max(g.demand) * g.fixed_costs[p] - * ((1 + m.discount_rate) ** (-p)) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) + self.variable_costs = Expression(expr=variable_costs) + self.fixed_costs = Expression(expr=fixed_costs) self.costs = Expression(expr=variable_costs + fixed_costs) return self.costs @@ -1220,7 +1226,8 @@ def _objective_expression(self): self.invest[g, p] * annuity * lifetime - * ((1 + m.discount_rate) ** (-p)) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) investment_costs += investment_costs_increment period_investment_costs[ @@ -1234,7 +1241,8 @@ def _objective_expression(self): self.dsm_up[g, t] * m.objective_weighting[t] * g.cost_dsm_up[t] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) variable_costs += ( ( @@ -1242,7 +1250,8 @@ def _objective_expression(self): + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] ) * m.objective_weighting[t] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) if g.investment.fixed_costs[0] is not None: @@ -1253,11 +1262,17 @@ def _objective_expression(self): * max(g.demand) * g.investment.fixed_costs[pp] * ((1 + m.discount_rate) ** (-pp)) - for pp in range(p, p + lifetime) - ) * ((1 + m.discount_rate) ** (-p)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime + ) + ) * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) - self.investment_costs = investment_costs - self.period_investment_costs = period_investment_costs + self.variable_costs = Expression(expr=variable_costs) + self.fixed_costs = Expression(expr=fixed_costs) + self.investment_costs = Expression(expr=investment_costs) + self.period_investment_costs = Expression(expr=period_investment_costs) self.costs = Expression( expr=investment_costs + variable_costs + fixed_costs ) @@ -1920,7 +1935,8 @@ def _objective_expression(self): self.dsm_up[g, t] * m.objective_weighting[t] * g.cost_dsm_up[t] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) variable_costs += ( ( @@ -1932,7 +1948,8 @@ def _objective_expression(self): + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] ) * m.objective_weighting[t] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) if g.fixed_costs[0] is not None: @@ -1941,9 +1958,12 @@ def _objective_expression(self): g.max_demand * max(g.demand) * g.fixed_costs[p] - * ((1 + m.discount_rate) ** (-p)) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) + self.variable_costs = Expression(expr=variable_costs) + self.fixed_costs = Expression(expr=fixed_costs) self.costs = Expression(expr=variable_costs + fixed_costs) return self.costs @@ -2758,7 +2778,8 @@ def _objective_expression(self): self.invest[g, p] * annuity * lifetime - * ((1 + m.discount_rate) ** (-p)) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) investment_costs += investment_costs_increment period_investment_costs[ @@ -2772,7 +2793,8 @@ def _objective_expression(self): self.dsm_up[g, t] * m.objective_weighting[t] * g.cost_dsm_up[t] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) variable_costs += ( ( @@ -2784,7 +2806,8 @@ def _objective_expression(self): + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] ) * m.objective_weighting[t] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) if g.investment.fixed_costs[0] is not None: @@ -2795,13 +2818,19 @@ def _objective_expression(self): * max(g.demand) * g.investment.fixed_costs[pp] * ((1 + m.discount_rate) ** (-pp)) - for pp in range(p, p + lifetime) - ) * ((1 + m.discount_rate) ** (-p)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime + ) + ) * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) - self.investment_costs = investment_costs - self.period_investment_costs = period_investment_costs + self.variable_costs = Expression(expr=variable_costs) + self.fixed_costs = Expression(expr=fixed_costs) + self.investment_costs = Expression(expr=investment_costs) + self.period_investment_costs = Expression(expr=period_investment_costs) self.costs = Expression( - expr=investment_costs + fixed_costs + variable_costs + expr=investment_costs + variable_costs + fixed_costs ) return self.costs @@ -3800,7 +3829,8 @@ def _objective_expression(self): * g.cost_dsm_up[t] ) * m.objective_weighting[t] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) variable_costs += ( ( @@ -3813,7 +3843,8 @@ def _objective_expression(self): + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] ) * m.objective_weighting[t] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) if g.fixed_costs[0] is not None: @@ -3822,9 +3853,12 @@ def _objective_expression(self): g.max_demand * max(g.demand) * g.fixed_costs[p] - * ((1 + m.discount_rate) ** (-p)) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) + self.variable_costs = Expression(expr=variable_costs) + self.fixed_costs = Expression(expr=fixed_costs) self.costs = Expression(expr=variable_costs + fixed_costs) return self.costs @@ -4880,7 +4914,7 @@ def _objective_expression(self): for p in m.PERIODS: if g.investment.ep_costs is not None: investment_costs += ( - self.invest[g] * g.investment.ep_costs + self.invest[g, p] * g.investment.ep_costs[p] ) else: raise ValueError("Missing value for investment costs!") @@ -4932,7 +4966,8 @@ def _objective_expression(self): self.invest[g, p] * annuity * lifetime - * ((1 + m.discount_rate) ** (-p)) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) investment_costs += investment_costs_increment period_investment_costs[ @@ -4952,7 +4987,8 @@ def _objective_expression(self): * g.cost_dsm_up[t] ) * m.objective_weighting[t] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) variable_costs += ( ( @@ -4965,7 +5001,8 @@ def _objective_expression(self): + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] ) * m.objective_weighting[t] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) if g.investment.fixed_costs[0] is not None: @@ -4976,13 +5013,19 @@ def _objective_expression(self): * max(g.demand) * g.investment.fixed_costs[pp] * ((1 + m.discount_rate) ** (-pp)) - for pp in range(p, p + lifetime) - ) * ((1 + m.discount_rate) ** (-p)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime + ) + ) * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) - self.investment_costs = investment_costs - self.period_investment_costs = period_investment_costs + self.variable_costs = Expression(expr=variable_costs) + self.fixed_costs = Expression(expr=fixed_costs) + self.investment_costs = Expression(expr=investment_costs) + self.period_investment_costs = Expression(expr=period_investment_costs) self.costs = Expression( - expr=investment_costs + fixed_costs + variable_costs + expr=investment_costs + variable_costs + fixed_costs ) return self.costs diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 347616afa..6df0a9b66 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -20,7 +20,7 @@ from oemof.network import network as on from oemof.tools import debugging -from pyomo.core import BuildAction +from pyomo.core import BuildAction, Expression from pyomo.core import Constraint from pyomo.core import NonNegativeIntegers from pyomo.core import Set @@ -528,7 +528,8 @@ def _objective_expression(self): m.flow[i, o, p, t] * m.objective_weighting[t] * m.flows[i, o].variable_costs[t] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) if (m.flows[i, o].fixed_costs[0] is not None @@ -537,7 +538,12 @@ def _objective_expression(self): fixed_costs += ( m.flows[i, o].nominal_value * m.flows[i, o].fixed_costs[p] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) ) - return variable_costs + fixed_costs + self.variable_costs = Expression(expr=variable_costs) + self.fixed_costs = Expression(expr=fixed_costs) + self.costs = Expression(expr=variable_costs + fixed_costs) + + return self.costs diff --git a/src/oemof/solph/flows/_investment_flow.py b/src/oemof/solph/flows/_investment_flow.py index 0dcd6613e..491a223ca 100644 --- a/src/oemof/solph/flows/_investment_flow.py +++ b/src/oemof/solph/flows/_investment_flow.py @@ -436,6 +436,12 @@ def _old_capacity_rule_end(block): """ for i, o in self.INVESTFLOWS: lifetime = m.flows[i, o].investment.lifetime + if lifetime is None: + msg = ("You have to specify a lifetime " + "for an InvestmentFlow in " + "a multi-period model! Value for {} " + "is missing.".format((i, o))) + raise ValueError(msg) for p in m.PERIODS: # No shutdown in first period if p == 0: @@ -715,7 +721,8 @@ def _objective_expression(self): ) investment_costs_increment = ( self.invest[i, o, p] * annuity * lifetime - * ((1 + m.discount_rate) ** (-p)) + * ((1 + m.discount_rate) + ** (-m.es.periods_years[p])) ) investment_costs += investment_costs_increment period_investment_costs[p] += investment_costs_increment @@ -737,7 +744,8 @@ def _objective_expression(self): (self.invest[i, o, p] * annuity * lifetime + self.invest_status[i, o, p] * m.flows[i, o].investment.offset[p]) - * ((1 + m.discount_rate) ** (-p)) + * ((1 + m.discount_rate) + ** (-m.es.periods_years[p])) ) investment_costs += investment_costs_increment period_investment_costs[p] += investment_costs_increment @@ -750,12 +758,17 @@ def _objective_expression(self): sum(self.invest[i, o, p] * m.flows[i, o].investment.fixed_costs[pp] * ((1 + m.discount_rate) ** (-pp)) - for pp in range(p, p + lifetime)) - * ((1 + m.discount_rate) ** (-p)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime) + ) + * ((1 + m.discount_rate) + ** (-m.es.periods_years[p])) ) self.investment_costs = Expression(expr=investment_costs) - self.period_investment_costs = period_investment_costs + self.period_investment_costs = Expression(expr=period_investment_costs) + self.fixed_costs = Expression(expr=fixed_costs) self.costs = Expression(expr=investment_costs + fixed_costs) return self.costs diff --git a/src/oemof/solph/flows/_non_convex_flow.py b/src/oemof/solph/flows/_non_convex_flow.py index 7c13e239c..4aff996f9 100644 --- a/src/oemof/solph/flows/_non_convex_flow.py +++ b/src/oemof/solph/flows/_non_convex_flow.py @@ -725,7 +725,8 @@ def _objective_expression(self): self.startup[i, o, t] * m.flows[i, o].nonconvex.startup_costs[t] * m.objective_weighting[t] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) for p, t in m.TIMEINDEX ) @@ -737,7 +738,8 @@ def _objective_expression(self): self.shutdown[i, o, t] * m.flows[i, o].nonconvex.shutdown_costs[t] * m.objective_weighting[t] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) for p, t in m.TIMEINDEX ) @@ -749,7 +751,8 @@ def _objective_expression(self): self.status[i, o, t] * m.flows[i, o].nonconvex.activity_costs[t] * m.objective_weighting[t] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) for p, t in m.TIMEINDEX ) @@ -760,7 +763,8 @@ def _objective_expression(self): (1 - self.status[i, o, t]) * m.flows[i, o].nonconvex.inactivity_costs[t] * m.objective_weighting[t] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) for p, t in m.TIMEINDEX ) @@ -775,7 +779,8 @@ def _objective_expression(self): * (m.flows[i, o].nonconvex .positive_gradient["costs"]) * m.objective_weighting[t] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) for p, t in m.TIMEINDEX ) @@ -791,7 +796,8 @@ def _objective_expression(self): * (m.flows[i, o].nonconvex .negative_gradient["costs"]) * m.objective_weighting[t] - * ((1 + m.discount_rate) ** -p) + * ((1 + m.discount_rate) + ** -m.es.periods_years[p]) for p, t in m.TIMEINDEX ) @@ -807,4 +813,5 @@ def _objective_expression(self): + activity_costs + inactivity_costs + gradient_costs ) ) + return self.costs From e42ac2f3976dfd412be3abc2d5cb085c2a0a71b3 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 17 Dec 2021 17:50:39 +0100 Subject: [PATCH 0090/1363] Extend _sink_dsm by overall max / min --- .../components/experimental/_sink_dsm.py | 153 ++++++++++++++++++ src/oemof/solph/flows/_investment_flow.py | 71 ++++---- 2 files changed, 186 insertions(+), 38 deletions(-) diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index 6ca85e43a..6dac7abfd 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -873,6 +873,18 @@ def _create(self, group=None): # Set of DSM Components self.investdsm = Set(initialize=[n for n in group]) + self.OVERALL_MAXIMUM_INVESTDSM = Set( + initialize=[ + g for g in group if g.investment.overall_maximum is not None + ] + ) + + self.OVERALL_MINIMUM_INVESTDSM = Set( + initialize=[ + g for g in group if g.investment.overall_minimum is not None + ] + ) + # ************* VARIABLES ***************************** # Define bounds for investments in demand response @@ -1167,6 +1179,45 @@ def dsm_sum_constraint_rule(block): rule=dsm_sum_constraint_rule ) + if m.es.multi_period: + def _overall_dsm_maximum_investflow_rule(block): + """Rule definition for maximum overall investment + in investment case. + """ + for g in self.OVERALL_MAXIMUM_INVESTDSM: + for p in m.PERIODS: + expr = self.total[g, p] <= g.investment.overall_maximum + self.overall_dsm_maximum.add((g, p), expr) + + self.overall_dsm_maximum = Constraint( + self.OVERALL_MAXIMUM_INVESTDSM, m.PERIODS, noruleinit=True + ) + + self.overall_maximum_build = BuildAction( + rule=_overall_dsm_maximum_investflow_rule + ) + + def _overall_minimum_dsm_investflow_rule(block): + """Rule definition for minimum overall investment + in investment case. + + Note: This is only applicable for the last period + """ + for g in self.OVERALL_MINIMUM_INVESTDSM: + expr = ( + g.investment.overall_minimum + <= self.total[g, m.PERIODS[-1]] + ) + self.overall_minimum.add(g, expr) + + self.overall_minimum = Constraint( + self.OVERALL_MINIMUM_INVESTDSM, noruleinit=True + ) + + self.overall_minimum_build = BuildAction( + rule=_overall_minimum_dsm_investflow_rule + ) + def _objective_expression(self): r"""Objective expression with variable and investment costs for DSM""" @@ -2096,6 +2147,18 @@ def _create(self, group=None): # Set of DSM Components self.investdsm = Set(initialize=[g for g in group]) + self.OVERALL_MAXIMUM_INVESTDSM = Set( + initialize=[ + g for g in group if g.investment.overall_maximum is not None + ] + ) + + self.OVERALL_MINIMUM_INVESTDSM = Set( + initialize=[ + g for g in group if g.investment.overall_minimum is not None + ] + ) + # ************* VARIABLES ***************************** # Define bounds for investments in demand response @@ -2718,6 +2781,45 @@ def shed_limit_constraint_rule(block): rule=shed_limit_constraint_rule ) + if m.es.multi_period: + def _overall_dsm_maximum_investflow_rule(block): + """Rule definition for maximum overall investment + in investment case. + """ + for g in self.OVERALL_MAXIMUM_INVESTDSM: + for p in m.PERIODS: + expr = self.total[g, p] <= g.investment.overall_maximum + self.overall_dsm_maximum.add((g, p), expr) + + self.overall_dsm_maximum = Constraint( + self.OVERALL_MAXIMUM_INVESTDSM, m.PERIODS, noruleinit=True + ) + + self.overall_maximum_build = BuildAction( + rule=_overall_dsm_maximum_investflow_rule + ) + + def _overall_minimum_dsm_investflow_rule(block): + """Rule definition for minimum overall investment + in investment case. + + Note: This is only applicable for the last period + """ + for g in self.OVERALL_MINIMUM_INVESTDSM: + expr = ( + g.investment.overall_minimum + <= self.total[g, m.PERIODS[-1]] + ) + self.overall_minimum.add(g, expr) + + self.overall_minimum = Constraint( + self.OVERALL_MINIMUM_INVESTDSM, noruleinit=True + ) + + self.overall_minimum_build = BuildAction( + rule=_overall_minimum_dsm_investflow_rule + ) + def _objective_expression(self): r"""Objective expression with variable and investment costs for DSM""" @@ -4080,6 +4182,18 @@ def _create(self, group=None): ], ) + self.OVERALL_MAXIMUM_INVESTDSM = Set( + initialize=[ + g for g in group if g.investment.overall_maximum is not None + ] + ) + + self.OVERALL_MINIMUM_INVESTDSM = Set( + initialize=[ + g for g in group if g.investment.overall_minimum is not None + ] + ) + # ************* VARIABLES ***************************** # Define bounds for investments in demand response @@ -4898,6 +5012,45 @@ def dr_logical_constraint_rule(block): rule=dr_logical_constraint_rule ) + if m.es.multi_period: + def _overall_dsm_maximum_investflow_rule(block): + """Rule definition for maximum overall investment + in investment case. + """ + for g in self.OVERALL_MAXIMUM_INVESTDSM: + for p in m.PERIODS: + expr = self.total[g, p] <= g.investment.overall_maximum + self.overall_dsm_maximum.add((g, p), expr) + + self.overall_dsm_maximum = Constraint( + self.OVERALL_MAXIMUM_INVESTDSM, m.PERIODS, noruleinit=True + ) + + self.overall_maximum_build = BuildAction( + rule=_overall_dsm_maximum_investflow_rule + ) + + def _overall_minimum_dsm_investflow_rule(block): + """Rule definition for minimum overall investment + in investment case. + + Note: This is only applicable for the last period + """ + for g in self.OVERALL_MINIMUM_INVESTDSM: + expr = ( + g.investment.overall_minimum + <= self.total[g, m.PERIODS[-1]] + ) + self.overall_minimum.add(g, expr) + + self.overall_minimum = Constraint( + self.OVERALL_MINIMUM_INVESTDSM, noruleinit=True + ) + + self.overall_minimum_build = BuildAction( + rule=_overall_minimum_dsm_investflow_rule + ) + def _objective_expression(self): r"""Objective expression with variable and investment costs for DSM; Equation 4.23 from Gils (2015) diff --git a/src/oemof/solph/flows/_investment_flow.py b/src/oemof/solph/flows/_investment_flow.py index 491a223ca..1c59b3ff0 100644 --- a/src/oemof/solph/flows/_investment_flow.py +++ b/src/oemof/solph/flows/_investment_flow.py @@ -622,49 +622,44 @@ def _summed_min_investflow_rule(block, i, o): rule=_summed_min_investflow_rule ) - def _overall_maximum_investflow_rule(block): - """Rule definition for maximum overall investment - in investment case. + if m.es.multi_period: + def _overall_maximum_investflow_rule(block): + """Rule definition for maximum overall investment + in investment case. + """ + for i, o in self.OVERALL_MAXIMUM_INVESTFLOWS: + for p in m.PERIODS: + expr = ( + self.total[i, o, p] + <= m.flows[i, o].investment.overall_maximum + ) + self.overall_maximum.add((i, o, p), expr) - Note: In general, there are two different options to define - an overall maximum: - 1.) overall_max = limit for (net) installed capacity - for each period. This is the constraint used here - 2.) overall max = sum of all (gross) investments occurring - """ - for i, o in self.OVERALL_MAXIMUM_INVESTFLOWS: - for p in m.PERIODS: - expr = ( - self.total[i, o, p] - <= m.flows[i, o].investment.overall_maximum - ) - self.overall_maximum.add((i, o, p), expr) + self.overall_maximum = Constraint( + self.OVERALL_MAXIMUM_INVESTFLOWS, + m.PERIODS, + noruleinit=True + ) + self.overall_maximum_build = BuildAction( + rule=_overall_maximum_investflow_rule + ) - self.overall_maximum = Constraint( - self.OVERALL_MAXIMUM_INVESTFLOWS, - m.PERIODS, - noruleinit=True - ) - self.overall_maximum_build = BuildAction( - rule=_overall_maximum_investflow_rule - ) + def _overall_minimum_investflow_rule(block, i, o): + """Rule definition for minimum overall investment + in investment case. - def _overall_minimum_investflow_rule(block, i, o): - """Rule definition for minimum overall investment - in investment case. + Note: This is only applicable for the last period + """ + expr = ( + m.flows[i, o].investment.overall_minimum + <= self.total[i, o, m.PERIODS[-1]] + ) + return expr - Note: This is only applicable for the last period - """ - expr = ( - m.flows[i, o].investment.overall_minimum - <= self.total[i, o, m.PERIODS[-1]] + self.overall_minimum = Constraint( + self.OVERALL_MINIMUM_INVESTFLOWS, + rule=_overall_minimum_investflow_rule ) - return expr - - self.overall_minimum = Constraint( - self.OVERALL_MINIMUM_INVESTFLOWS, - rule=_overall_minimum_investflow_rule - ) def _objective_expression(self): r"""Objective expression for flows with investment attribute of type From d299ed7c9fb9ed303eb8fd70ad5fa1741c223721 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 17 Dec 2021 17:51:53 +0100 Subject: [PATCH 0091/1363] Modify storage TODO: Check / debug new additions and handle initial content! --- .../solph/components/_generic_storage.py | 104 ++++++++---------- 1 file changed, 48 insertions(+), 56 deletions(-) diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index 9b2017d46..798f996aa 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -24,6 +24,7 @@ from oemof.network import network from oemof.tools import debugging from oemof.tools import economics +from pyomo.core import value from pyomo.core.base.block import SimpleBlock from pyomo.environ import Binary from pyomo.environ import BuildAction @@ -1062,23 +1063,6 @@ def _old_storage_capacity_rule(block): self.old_rule_build = BuildAction(rule=_old_storage_capacity_rule) - def _storage_level_no_investments(block): - """Rule definition to force storage level to zero - as long as no investments have occurred - """ - for n in self.INVESTSTORAGES: - for p, t in m.TIMEINDEX: - expr = block.storage_content[n, t] <= self.total[n, p] - block.storage_level_noinv_rule.add((n, p, t), expr) - - self.storage_level_noinv_rule = Constraint( - self.INVESTSTORAGES, m.TIMEINDEX, noruleinit=True - ) - - self.storage_level_noinv_rule_build = BuildAction( - rule=_storage_level_no_investments - ) - def _inv_storage_init_content_max_rule(block): """Constraint for a variable initial storage capacity.""" for n in self.INVESTSTORAGES_NO_INIT_CONTENT: @@ -1111,7 +1095,12 @@ def _inv_storage_init_content_fix_rule(block): expr = ( block.init_content[n, p] == n.initial_storage_level - * min(0, block.total[n, p] > block.total[n, p-1]) + * min( + 0, value( + block.total[n, p] + - block.total[n, p-1] + ) + ) ) self.init_content_fix.add((n, p), expr) @@ -1218,10 +1207,17 @@ def _lifetime_balanced_storage_rule(block): lifetime = n.investment.lifetime age = n.investment.age for p in m.PERIODS: - if p == lifetime - age: + if lifetime - age <= m.es.periods_years[p]: + # Obtain commissioning period + comm_p = 0 + for k, v in m.es.periods_years.items(): + if m.es.periods_years[p] - lifetime - v < 0: + # change of sign is detected + comm_p = k - 1 + break last_step = m.TIMESTEPS_IN_PERIOD[p][-1] first_step = ( - m.TIMESTEPS_IN_PERIOD[p - lifetime + age][0] + m.TIMESTEPS_IN_PERIOD[comm_p][0] ) expr = ( block.storage_content[n, last_step] @@ -1370,48 +1366,44 @@ def smallest_invest(block, n, p): self.NON_CONVEX_INVESTSTORAGES, m.PERIODS, rule=smallest_invest ) - def _overall_storage_maximum_investflow_rule(block): - """Rule definition for maximum overall investment - in investment case. - - Note: There are two different options to define an overall maximum: - 1.) overall_max = limit for (net) installed capacity - for each period - 2.) overall max = sum of all (gross) investments occurring - """ - for n in self.OVERALL_MAXIMUM_INVESTSTORAGES: - for p in m.PERIODS: - expr = self.total[n, p] <= n.investment.overall_maximum - self.overall_storage_maximum.add((n, p), expr) + if m.es.multi_period: + def _overall_storage_maximum_investflow_rule(block): + """Rule definition for maximum overall investment + in investment case. + """ + for n in self.OVERALL_MAXIMUM_INVESTSTORAGES: + for p in m.PERIODS: + expr = self.total[n, p] <= n.investment.overall_maximum + self.overall_storage_maximum.add((n, p), expr) - self.overall_storage_maximum = Constraint( - self.OVERALL_MAXIMUM_INVESTSTORAGES, m.PERIODS, noruleinit=True - ) + self.overall_storage_maximum = Constraint( + self.OVERALL_MAXIMUM_INVESTSTORAGES, m.PERIODS, noruleinit=True + ) - self.overall_maximum_build = BuildAction( - rule=_overall_storage_maximum_investflow_rule - ) + self.overall_maximum_build = BuildAction( + rule=_overall_storage_maximum_investflow_rule + ) - def _overall_minimum_investflow_rule(block): - """Rule definition for minimum overall investment - in investment case. + def _overall_minimum_investflow_rule(block): + """Rule definition for minimum overall investment + in investment case. - Note: This is only applicable for the last period - """ - for n in self.OVERALL_MINIMUM_INVESTSTORAGES: - expr = ( - n.investment.overall_minimum - <= self.total[n, m.PERIODS[-1]] - ) - self.overall_minimum.add(n, expr) + Note: This is only applicable for the last period + """ + for n in self.OVERALL_MINIMUM_INVESTSTORAGES: + expr = ( + n.investment.overall_minimum + <= self.total[n, m.PERIODS[-1]] + ) + self.overall_minimum.add(n, expr) - self.overall_minimum = Constraint( - self.OVERALL_MINIMUM_INVESTSTORAGES, noruleinit=True - ) + self.overall_minimum = Constraint( + self.OVERALL_MINIMUM_INVESTSTORAGES, noruleinit=True + ) - self.overall_minimum_build = BuildAction( - rule=_overall_minimum_investflow_rule - ) + self.overall_minimum_build = BuildAction( + rule=_overall_minimum_investflow_rule + ) def _objective_expression(self): """Objective expression with fixed and investment costs.""" From 0c89fb966d7d9a7ae233bd11cbcbefca6c87b09d Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 17 Dec 2021 17:56:19 +0100 Subject: [PATCH 0092/1363] Reformat using black --- src/oemof/solph/_energy_system.py | 18 +- src/oemof/solph/_models.py | 38 +-- src/oemof/solph/_options.py | 6 +- src/oemof/solph/buses/_bus.py | 2 +- .../solph/components/_generic_storage.py | 102 +++----- .../solph/components/_offset_transformer.py | 5 +- .../components/experimental/_sink_dsm.py | 82 +++--- src/oemof/solph/constraints/integral_limit.py | 18 +- .../solph/constraints/investment_limit.py | 26 +- src/oemof/solph/flows/_flow.py | 94 ++++--- src/oemof/solph/flows/_investment_flow.py | 240 ++++++++---------- src/oemof/solph/flows/_non_convex_flow.py | 138 ++++++---- 12 files changed, 384 insertions(+), 385 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 3356e50f3..29c264b12 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -97,17 +97,14 @@ def _add_periods(self, periods): if not self.multi_period: periods = {0: 0} elif periods is None: - years = sorted( - list( - set(getattr(self.timeindex, 'year')) - ) - ) + years = sorted(list(set(getattr(self.timeindex, "year")))) periods = {} filter_series = self.timeindex.to_series() for number, year in enumerate(years): start = filter_series.loc[ - filter_series.index.year == year].min() + filter_series.index.year == year + ].min() end = filter_series.loc[filter_series.index.year == year].max() periods[number] = pd.date_range(start, end, freq="H") else: @@ -140,12 +137,9 @@ def _extract_periods_lengths_gap_and_years(self): for k, v in self.periods.items(): periods_length[k] = v.max().year - v.min().year + 1 if k >= 1: - periods_gap[k-1] = v.min().year - previous_end.year - 1 - periods_years[k] = ( - sum( - periods_length[kk] + periods_gap[kk] - for kk in range(k) - ) + periods_gap[k - 1] = v.min().year - previous_end.year - 1 + periods_years[k] = sum( + periods_length[kk] + periods_gap[kk] for kk in range(k) ) previous_end = v.max() diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 344f4322e..e7d68860f 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -294,10 +294,12 @@ def __init__(self, energysystem, discount_rate=None, **kwargs): self.discount_rate = discount_rate elif energysystem.multi_period: self.discount_rate = 0.02 - msg = (f"By default, a discount_rate of {self.discount_rate} " - f"is used for a multi-period model. " - f"If you want to use another value, " - f"you have to specify the `discount_rate` attribute.") + msg = ( + f"By default, a discount_rate of {self.discount_rate} " + f"is used for a multi-period model. " + f"If you want to use another value, " + f"you have to specify the `discount_rate` attribute." + ) warnings.warn(msg, debugging.SuspiciousUsageWarning) super().__init__(energysystem, **kwargs) @@ -307,16 +309,19 @@ def _add_parent_block_sets(self): self.NODES = po.Set(initialize=[n for n in self.es.nodes]) # pyomo set for timesteps of optimization problem - self.TIMESTEPS = po.Set(initialize=range(len(self.es.timeindex)), - ordered=True) + self.TIMESTEPS = po.Set( + initialize=range(len(self.es.timeindex)), ordered=True + ) if not self.es.multi_period: self.TIMEINDEX = po.Set( initialize=list( - zip([0] * len(self.es.timeindex.year), - range(len(self.es.timeindex))) + zip( + [0] * len(self.es.timeindex.year), + range(len(self.es.timeindex)), + ) ), - ordered=True + ordered=True, ) else: nested_list = [ @@ -329,7 +334,7 @@ def _add_parent_block_sets(self): initialize=list( zip(flattened_list, range(len(self.es.timeindex))) ), - ordered=True + ordered=True, ) self.PERIODS = po.Set( @@ -377,9 +382,7 @@ def _add_parent_block_sets(self): def _add_parent_block_variables(self): """ """ - self.flow = po.Var( - self.FLOWS, self.TIMEINDEX, within=po.Reals - ) + self.flow = po.Var(self.FLOWS, self.TIMEINDEX, within=po.Reals) for (o, i) in self.FLOWS: if self.flows[o, i].nominal_value is not None: @@ -387,19 +390,22 @@ def _add_parent_block_variables(self): for p, t in self.TIMEINDEX: self.flow[o, i, p, t].value = ( self.flows[o, i].fix[t] - * self.flows[o, i].nominal_value) + * self.flows[o, i].nominal_value + ) self.flow[o, i, p, t].fix() else: for p, t in self.TIMEINDEX: self.flow[o, i, p, t].setub( self.flows[o, i].max[t] - * self.flows[o, i].nominal_value) + * self.flows[o, i].nominal_value + ) if not self.flows[o, i].nonconvex: for p, t in self.TIMEINDEX: self.flow[o, i, p, t].setlb( self.flows[o, i].min[t] - * self.flows[o, i].nominal_value) + * self.flows[o, i].nominal_value + ) elif (o, i) in self.UNIDIRECTIONAL_FLOWS: for p, t in self.TIMEINDEX: self.flow[o, i, p, t].setlb(0) diff --git a/src/oemof/solph/_options.py b/src/oemof/solph/_options.py index 950be9ae4..b3e3edd6a 100644 --- a/src/oemof/solph/_options.py +++ b/src/oemof/solph/_options.py @@ -139,8 +139,10 @@ def _check_invest_attributes_offset(self): def _check_age_and_lifetime(self): if self.lifetime is not None: if self.age >= self.lifetime: - e4 = ("A unit's age must be smaller than its " - "expected lifetime.") + e4 = ( + "A unit's age must be smaller than its " + "expected lifetime." + ) raise AttributeError(e4) diff --git a/src/oemof/solph/buses/_bus.py b/src/oemof/solph/buses/_bus.py index 491e0d464..12c6e5db5 100644 --- a/src/oemof/solph/buses/_bus.py +++ b/src/oemof/solph/buses/_bus.py @@ -85,7 +85,7 @@ def _busbalance_rule(block): for g in group: lhs = sum(m.flow[i, g, p, t] for i in ins[g]) rhs = sum(m.flow[g, o, p, t] for o in outs[g]) - expr = (lhs == rhs) + expr = lhs == rhs # no inflows no outflows yield: 0 == 0 which is True if expr is not True: block.balance.add((g, p, t), expr) diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index 798f996aa..d86a5fd0d 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -901,8 +901,10 @@ def _storage_investvar_bound_rule(block, n, p): # Total capacity self.total = Var( - self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals, - initialize=0 + self.INVESTSTORAGES, + m.PERIODS, + within=NonNegativeReals, + initialize=0, ) if m.es.multi_period: @@ -967,6 +969,7 @@ def _total_storage_capacity_rule(block): ) if m.es.multi_period: + def _old_storage_capacity_rule_end(block): """Rule definition for determining old endogenously installed capacity to be decommissioned due to reaching its lifetime @@ -974,15 +977,17 @@ def _old_storage_capacity_rule_end(block): for n in self.INVESTSTORAGES: lifetime = n.investment.lifetime if lifetime is None: - msg = ("You have to specify a lifetime " - "for an InvestmentFlow in " - "a multi-period model! Value for {} " - "is missing.".format(n)) + msg = ( + "You have to specify a lifetime " + "for an InvestmentFlow in " + "a multi-period model! Value for {} " + "is missing.".format(n) + ) raise ValueError(msg) for p in m.PERIODS: # No shutdown in first period if p == 0: - expr = (self.old_end[n, p] == 0) + expr = self.old_end[n, p] == 0 self.old_rule_end.add((n, p), expr) elif lifetime <= m.es.periods_years[p]: # Obtain commissioning period @@ -992,10 +997,7 @@ def _old_storage_capacity_rule_end(block): # change of sign is detected comm_p = k - 1 break - expr = ( - self.old_end[n, p] - == self.invest[n, comm_p] - ) + expr = self.old_end[n, p] == self.invest[n, comm_p] self.old_rule_end.add((n, p), expr) else: expr = self.old_end[n, p] == 0 @@ -1020,18 +1022,17 @@ def _old_storage_capacity_rule_exo(block): for p in m.PERIODS: # No shutdown in first period if p == 0: - expr = (self.old_exo[n, p] == 0) + expr = self.old_exo[n, p] == 0 self.old_rule_exo.add((n, p), expr) elif lifetime - age <= m.es.periods_years[p]: # Track decommissioning status if not is_decommissioned: expr = ( - self.old_exo[n, p] - == n.investment.existing + self.old_exo[n, p] == n.investment.existing ) is_decommissioned = True else: - expr = (self.old_exo[n, p] == 0) + expr = self.old_exo[n, p] == 0 self.old_rule_exo.add((n, p), expr) else: expr = self.old_exo[n, p] == 0 @@ -1067,14 +1068,11 @@ def _inv_storage_init_content_max_rule(block): """Constraint for a variable initial storage capacity.""" for n in self.INVESTSTORAGES_NO_INIT_CONTENT: for p in m.PERIODS: - expr = (block.init_content[n, p] - <= block.total[n, p]) + expr = block.init_content[n, p] <= block.total[n, p] block.init_content_limit.add((n, p), expr) self.init_content_limit = Constraint( - self.INVESTSTORAGES_NO_INIT_CONTENT, - m.PERIODS, - noruleinit=True + self.INVESTSTORAGES_NO_INIT_CONTENT, m.PERIODS, noruleinit=True ) self.init_content_limit_build = BuildAction( rule=_inv_storage_init_content_max_rule @@ -1087,27 +1085,19 @@ def _inv_storage_init_content_fix_rule(block): if p == 0: expr = ( block.init_content[n, p] - == n.initial_storage_level - * block.total[n, p] + == n.initial_storage_level * block.total[n, p] ) self.init_content_fix.add((n, p), expr) else: - expr = ( - block.init_content[n, p] - == n.initial_storage_level - * min( - 0, value( - block.total[n, p] - - block.total[n, p-1] - ) - ) + expr = block.init_content[ + n, p + ] == n.initial_storage_level * min( + 0, value(block.total[n, p] - block.total[n, p - 1]) ) self.init_content_fix.add((n, p), expr) self.init_content_fix = Constraint( - self.INVESTSTORAGES_INIT_CONTENT, - m. PERIODS, - noruleinit=True + self.INVESTSTORAGES_INIT_CONTENT, m.PERIODS, noruleinit=True ) self.init_content_fix_build = BuildAction( rule=_inv_storage_init_content_fix_rule @@ -1191,6 +1181,7 @@ def _storage_balance_rule(block, n, p, t): ) if not m.es.multi_period: + def _balanced_storage_rule(block, n): return ( block.storage_content[n, m.TIMESTEPS[-1]] @@ -1202,6 +1193,7 @@ def _balanced_storage_rule(block, n): ) else: + def _lifetime_balanced_storage_rule(block): for n in self.INVESTSTORAGES_BALANCED: lifetime = n.investment.lifetime @@ -1216,9 +1208,7 @@ def _lifetime_balanced_storage_rule(block): comm_p = k - 1 break last_step = m.TIMESTEPS_IN_PERIOD[p][-1] - first_step = ( - m.TIMESTEPS_IN_PERIOD[comm_p][0] - ) + first_step = m.TIMESTEPS_IN_PERIOD[comm_p][0] expr = ( block.storage_content[n, last_step] == block.storage_content[n, first_step] @@ -1228,9 +1218,7 @@ def _lifetime_balanced_storage_rule(block): pass self.lifetime_balanced_cstr = Constraint( - self.INVESTSTORAGES_BALANCED, - m.PERIODS, - noruleinit=True + self.INVESTSTORAGES_BALANCED, m.PERIODS, noruleinit=True ) self.lifetime_balanced_cstr_build = BuildAction( rule=_lifetime_balanced_storage_rule @@ -1367,6 +1355,7 @@ def smallest_invest(block, n, p): ) if m.es.multi_period: + def _overall_storage_maximum_investflow_rule(block): """Rule definition for maximum overall investment in investment case. @@ -1457,8 +1446,7 @@ def _objective_expression(self): self.invest[n, p] * annuity * lifetime - * ((1 + m.discount_rate) - ** (-m.es.periods_years[p])) + * ((1 + m.discount_rate) ** (-m.es.periods_years[p])) ) investment_costs += investment_costs_increment period_investment_costs[p] += investment_costs_increment @@ -1479,12 +1467,9 @@ def _objective_expression(self): wacc=interest, ) investment_costs_increment = ( - ( - self.invest[n, p] * annuity * lifetime - + self.invest_status[n, p] * n.investment.offset[p] - ) * ((1 + m.discount_rate) - ** (-m.es.periods_years[p])) - ) + self.invest[n, p] * annuity * lifetime + + self.invest_status[n, p] * n.investment.offset[p] + ) * ((1 + m.discount_rate) ** (-m.es.periods_years[p])) investment_costs += investment_costs_increment period_investment_costs[p] += investment_costs_increment @@ -1492,18 +1477,15 @@ def _objective_expression(self): if n.investment.fixed_costs[0] is not None: lifetime = n.investment.lifetime for p in m.PERIODS: - fixed_costs += ( - sum( - self.invest[n, p] - * n.investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range( - m.es.periods_years[p], - m.es.periods_years[p] + lifetime - ) - ) * ((1 + m.discount_rate) - ** (-m.es.periods_years[p])) - ) + fixed_costs += sum( + self.invest[n, p] + * n.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime, + ) + ) * ((1 + m.discount_rate) ** (-m.es.periods_years[p])) self.investment_costs = Expression(expr=investment_costs) self.period_investment_costs = Expression(expr=period_investment_costs) diff --git a/src/oemof/solph/components/_offset_transformer.py b/src/oemof/solph/components/_offset_transformer.py index 5c0e27b84..63a86a3db 100644 --- a/src/oemof/solph/components/_offset_transformer.py +++ b/src/oemof/solph/components/_offset_transformer.py @@ -154,9 +154,8 @@ def _relation_rule(block, n, p, t): * n.coefficients[1][t] ) expr += ( - (m.NonConvexFlow.status[list(n.inputs.keys())[0], n, t]) - * n.coefficients[0][t] - ) + m.NonConvexFlow.status[list(n.inputs.keys())[0], n, t] + ) * n.coefficients[0][t] return expr == 0 self.relation = Constraint( diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index 6dac7abfd..aa8f4323f 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -738,8 +738,7 @@ def _objective_expression(self): self.dsm_up[g, t] * m.objective_weighting[t] * g.cost_dsm_up[t] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) variable_costs += ( ( @@ -747,8 +746,7 @@ def _objective_expression(self): + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] ) * m.objective_weighting[t] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) if g.fixed_costs[0] is not None: @@ -757,8 +755,7 @@ def _objective_expression(self): g.max_demand * max(g.demand) * g.fixed_costs[p] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) self.variable_costs = Expression(expr=variable_costs) @@ -962,6 +959,7 @@ def _total_dsm_capacity_rule(block): self.total_dsm_rule_build = BuildAction(rule=_total_dsm_capacity_rule) if m.es.multi_period: + def _old_dsm_capacity_rule_end(block): """Rule definition for determining old endogenously installed capacity to be decommissioned due to reaching its lifetime @@ -971,7 +969,7 @@ def _old_dsm_capacity_rule_end(block): for p in m.PERIODS: # No shutdown in first period if p == 0: - expr = (self.old_end[g, p] == 0) + expr = self.old_end[g, p] == 0 self.old_rule_end.add((g, p), expr) elif lifetime <= m.es.periods_years[p]: # Obtain commissioning period @@ -981,8 +979,7 @@ def _old_dsm_capacity_rule_end(block): # change of sign is detected comm_p = k - 1 break - expr = (self.old_end[g, p] - == self.invest[g, comm_p]) + expr = self.old_end[g, p] == self.invest[g, comm_p] self.old_rule_end.add((g, p), expr) else: expr = self.old_end[g, p] == 0 @@ -1006,7 +1003,7 @@ def _old_dsm_capacity_rule_exo(block): for p in m.PERIODS: # No shutdown in first period if p == 0: - expr = (self.old_exo[g, p] == 0) + expr = self.old_exo[g, p] == 0 self.old_rule_exo.add((g, p), expr) elif lifetime - age <= m.es.periods_years[p]: # Track decommissioning status @@ -1017,7 +1014,7 @@ def _old_dsm_capacity_rule_exo(block): ) is_decommissioned = True else: - expr = (self.old_exo[g, p] == 0) + expr = self.old_exo[g, p] == 0 self.old_rule_exo.add((g, p), expr) else: expr = self.old_exo[g, p] == 0 @@ -1180,6 +1177,7 @@ def dsm_sum_constraint_rule(block): ) if m.es.multi_period: + def _overall_dsm_maximum_investflow_rule(block): """Rule definition for maximum overall investment in investment case. @@ -1277,8 +1275,7 @@ def _objective_expression(self): self.invest[g, p] * annuity * lifetime - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) investment_costs += investment_costs_increment period_investment_costs[ @@ -1292,8 +1289,7 @@ def _objective_expression(self): self.dsm_up[g, t] * m.objective_weighting[t] * g.cost_dsm_up[t] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) variable_costs += ( ( @@ -1301,8 +1297,7 @@ def _objective_expression(self): + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] ) * m.objective_weighting[t] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) if g.investment.fixed_costs[0] is not None: @@ -1315,10 +1310,9 @@ def _objective_expression(self): * ((1 + m.discount_rate) ** (-pp)) for pp in range( m.es.periods_years[p], - m.es.periods_years[p] + lifetime + m.es.periods_years[p] + lifetime, ) - ) * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + ) * ((1 + m.discount_rate) ** -m.es.periods_years[p]) self.variable_costs = Expression(expr=variable_costs) self.fixed_costs = Expression(expr=fixed_costs) @@ -1986,8 +1980,7 @@ def _objective_expression(self): self.dsm_up[g, t] * m.objective_weighting[t] * g.cost_dsm_up[t] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) variable_costs += ( ( @@ -1999,8 +1992,7 @@ def _objective_expression(self): + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] ) * m.objective_weighting[t] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) if g.fixed_costs[0] is not None: @@ -2009,8 +2001,7 @@ def _objective_expression(self): g.max_demand * max(g.demand) * g.fixed_costs[p] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) self.variable_costs = Expression(expr=variable_costs) @@ -2782,6 +2773,7 @@ def shed_limit_constraint_rule(block): ) if m.es.multi_period: + def _overall_dsm_maximum_investflow_rule(block): """Rule definition for maximum overall investment in investment case. @@ -2880,8 +2872,7 @@ def _objective_expression(self): self.invest[g, p] * annuity * lifetime - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) investment_costs += investment_costs_increment period_investment_costs[ @@ -2895,8 +2886,7 @@ def _objective_expression(self): self.dsm_up[g, t] * m.objective_weighting[t] * g.cost_dsm_up[t] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) variable_costs += ( ( @@ -2908,8 +2898,7 @@ def _objective_expression(self): + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] ) * m.objective_weighting[t] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) if g.investment.fixed_costs[0] is not None: @@ -2922,10 +2911,9 @@ def _objective_expression(self): * ((1 + m.discount_rate) ** (-pp)) for pp in range( m.es.periods_years[p], - m.es.periods_years[p] + lifetime + m.es.periods_years[p] + lifetime, ) - ) * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + ) * ((1 + m.discount_rate) ** -m.es.periods_years[p]) self.variable_costs = Expression(expr=variable_costs) self.fixed_costs = Expression(expr=fixed_costs) @@ -3931,8 +3919,7 @@ def _objective_expression(self): * g.cost_dsm_up[t] ) * m.objective_weighting[t] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) variable_costs += ( ( @@ -3945,8 +3932,7 @@ def _objective_expression(self): + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] ) * m.objective_weighting[t] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) if g.fixed_costs[0] is not None: @@ -3955,8 +3941,7 @@ def _objective_expression(self): g.max_demand * max(g.demand) * g.fixed_costs[p] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) self.variable_costs = Expression(expr=variable_costs) @@ -5013,6 +4998,7 @@ def dr_logical_constraint_rule(block): ) if m.es.multi_period: + def _overall_dsm_maximum_investflow_rule(block): """Rule definition for maximum overall investment in investment case. @@ -5119,8 +5105,7 @@ def _objective_expression(self): self.invest[g, p] * annuity * lifetime - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) investment_costs += investment_costs_increment period_investment_costs[ @@ -5140,8 +5125,7 @@ def _objective_expression(self): * g.cost_dsm_up[t] ) * m.objective_weighting[t] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) variable_costs += ( ( @@ -5154,8 +5138,7 @@ def _objective_expression(self): + self.dsm_do_shed[g, t] * g.cost_dsm_down_shed[t] ) * m.objective_weighting[t] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) if g.investment.fixed_costs[0] is not None: @@ -5168,10 +5151,9 @@ def _objective_expression(self): * ((1 + m.discount_rate) ** (-pp)) for pp in range( m.es.periods_years[p], - m.es.periods_years[p] + lifetime + m.es.periods_years[p] + lifetime, ) - ) * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + ) * ((1 + m.discount_rate) ** -m.es.periods_years[p]) self.variable_costs = Expression(expr=variable_costs) self.fixed_costs = Expression(expr=fixed_costs) diff --git a/src/oemof/solph/constraints/integral_limit.py b/src/oemof/solph/constraints/integral_limit.py index ce0cd00a6..6b2167105 100644 --- a/src/oemof/solph/constraints/integral_limit.py +++ b/src/oemof/solph/constraints/integral_limit.py @@ -178,17 +178,21 @@ def generic_periodical_integral_limit(om, keyword, flows=None, limit=None): limit_name = "integral_limit_" + keyword if not om.es.multi_period: - msg = ("generic_periodical_integral_limit is only applicable\n" - "for multi-period models.\nFor standard models, use " - "generic_integral_limit instead.") + msg = ( + "generic_periodical_integral_limit is only applicable\n" + "for multi-period models.\nFor standard models, use " + "generic_integral_limit instead." + ) raise ValueError(msg) if limit is not None: limit = sequence(limit) else: - msg = ("You have to provide a limit for each period!\n" - "If you provide a scalar value, this will be applied as a " - "limit for each period.") + msg = ( + "You have to provide a limit for each period!\n" + "If you provide a scalar value, this will be applied as a " + "limit for each period." + ) raise ValueError(msg) def _periodical_integral_limit_rule(m, p): @@ -205,7 +209,7 @@ def _periodical_integral_limit_rule(m, p): om.periodical_integral_limit = po.Constraint( om.PERIODS, rule=_periodical_integral_limit_rule, - name=limit_name + "_constraint" + name=limit_name + "_constraint", ) return om diff --git a/src/oemof/solph/constraints/investment_limit.py b/src/oemof/solph/constraints/investment_limit.py index 499a86c7d..0401bf7f8 100644 --- a/src/oemof/solph/constraints/investment_limit.py +++ b/src/oemof/solph/constraints/investment_limit.py @@ -75,17 +75,21 @@ def investment_limit_per_period(model, limit=None): """ if not model.es.multi_period: - msg = ("investment_limit_per_period is only applicable " - "for multi-period models.\nIn order to create such a model, " - "set attribute `multi_period` of your energy system to True.") + msg = ( + "investment_limit_per_period is only applicable " + "for multi-period models.\nIn order to create such a model, " + "set attribute `multi_period` of your energy system to True." + ) raise ValueError(msg) if limit is not None: limit = sequence(limit) else: - msg = ("You have to provide an investment limit for each period!\n" - "If you provide a scalar value, this will be applied as a " - "limit for each period.") + msg = ( + "You have to provide an investment limit for each period!\n" + "If you provide a scalar value, this will be applied as a " + "limit for each period." + ) raise ValueError(msg) def investment_period_rule(m, p): @@ -109,8 +113,7 @@ def investment_period_rule(m, p): return expr <= limit[p] model.investment_limit_per_period = po.Constraint( - model.PERIODS, - rule=investment_period_rule + model.PERIODS, rule=investment_period_rule ) return model @@ -201,11 +204,8 @@ def _additional_limit_rule(block): model.add_limit.add(p, (lhs <= rhs)) model.add_limit = po.Constraint( - model.PERIODS, - noruleinit=True, - name=limit_name + "_constraint" + model.PERIODS, noruleinit=True, name=limit_name + "_constraint" ) - model.add_limit_build = po.BuildAction( - rule=_additional_limit_rule) + model.add_limit_build = po.BuildAction(rule=_additional_limit_rule) return model diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 6df0a9b66..586b80bc1 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -156,14 +156,16 @@ def __init__(self, **kwargs): keys = [k for k in kwargs if k != "label"] if "fixed_costs" in keys: - msg = ("Be aware that the fixed costs attribute is only\n" - "meant to be used for multi-period models.\n" - "If you wish to set up a multi-period model, set the" - " multi_period attribute of your energy system to True.\n" - "It has been decided to remove the `fixed_costs` " - "attribute with v0.2 for regular uses.\n" - "If you specify `fixed_costs` for a regular model, " - "it will simply be ignored.") + msg = ( + "Be aware that the fixed costs attribute is only\n" + "meant to be used for multi-period models.\n" + "If you wish to set up a multi-period model, set the" + " multi_period attribute of your energy system to True.\n" + "It has been decided to remove the `fixed_costs` " + "attribute with v0.2 for regular uses.\n" + "If you specify `fixed_costs` for a regular model, " + "it will simply be ignored." + ) warn(msg, debugging.SuspiciousUsageWarning) if "actual_value" in keys: @@ -448,16 +450,32 @@ def _positive_gradient_flow_rule(model): for inp, out in self.POSITIVE_GRADIENT_FLOWS: for index in range(1, len(m.TIMEINDEX) + 1): if m.TIMEINDEX[index][1] > 0: - lhs = (m.flow[inp, out, m.TIMEINDEX[index][0], - m.TIMEINDEX[index][1]] - - m.flow[inp, out, m.TIMEINDEX[index - 1][0], - m.TIMEINDEX[index - 1][1]]) + lhs = ( + m.flow[ + inp, + out, + m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1], + ] + - m.flow[ + inp, + out, + m.TIMEINDEX[index - 1][0], + m.TIMEINDEX[index - 1][1], + ] + ) rhs = self.positive_gradient[ - inp, out, m.TIMEINDEX[index][1]] + inp, out, m.TIMEINDEX[index][1] + ] self.positive_gradient_constr.add( - (inp, out, m.TIMEINDEX[index][0], - m.TIMEINDEX[index][1]), - lhs <= rhs) + ( + inp, + out, + m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1], + ), + lhs <= rhs, + ) else: pass # return(Constraint.Skip) @@ -473,16 +491,32 @@ def _negative_gradient_flow_rule(model): for inp, out in self.NEGATIVE_GRADIENT_FLOWS: for index in range(1, len(m.TIMEINDEX) + 1): if m.TIMEINDEX[index][1] > 0: - lhs = (m.flow[inp, out, m.TIMEINDEX[index - 1][0], - m.TIMEINDEX[index - 1][1]] - - m.flow[inp, out, m.TIMEINDEX[index][0], - m.TIMEINDEX[index][1]]) + lhs = ( + m.flow[ + inp, + out, + m.TIMEINDEX[index - 1][0], + m.TIMEINDEX[index - 1][1], + ] + - m.flow[ + inp, + out, + m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1], + ] + ) rhs = self.negative_gradient[ - inp, out, m.TIMEINDEX[index][1]] + inp, out, m.TIMEINDEX[index][1] + ] self.negative_gradient_constr.add( - (inp, out, m.TIMEINDEX[index][0], - m.TIMEINDEX[index][1]), - lhs <= rhs) + ( + inp, + out, + m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1], + ), + lhs <= rhs, + ) else: pass # return(Constraint.Skip) @@ -528,18 +562,18 @@ def _objective_expression(self): m.flow[i, o, p, t] * m.objective_weighting[t] * m.flows[i, o].variable_costs[t] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) - if (m.flows[i, o].fixed_costs[0] is not None - and m.flows[i, o].nominal_value is not None): + if ( + m.flows[i, o].fixed_costs[0] is not None + and m.flows[i, o].nominal_value is not None + ): for p in m.PERIODS: fixed_costs += ( m.flows[i, o].nominal_value * m.flows[i, o].fixed_costs[p] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) self.variable_costs = Expression(expr=variable_costs) diff --git a/src/oemof/solph/flows/_investment_flow.py b/src/oemof/solph/flows/_investment_flow.py index 1c59b3ff0..28b5ecb72 100644 --- a/src/oemof/solph/flows/_investment_flow.py +++ b/src/oemof/solph/flows/_investment_flow.py @@ -299,14 +299,18 @@ def _create(self, group=None): self.OVERALL_MAXIMUM_INVESTFLOWS = Set( initialize=[ - (g[0], g[1]) for g in group - if g[2].investment.overall_maximum is not None] + (g[0], g[1]) + for g in group + if g[2].investment.overall_maximum is not None + ] ) self.OVERALL_MINIMUM_INVESTFLOWS = Set( initialize=[ - (g[0], g[1]) for g in group - if g[2].investment.overall_minimum is not None] + (g[0], g[1]) + for g in group + if g[2].investment.overall_minimum is not None + ] ) # ######################### VARIABLES ################################# @@ -329,38 +333,26 @@ def _investvar_bound_rule(block, i, o, p): ) # Total capacity - self.total = Var( - self.INVESTFLOWS, - m.PERIODS, - within=NonNegativeReals - ) + self.total = Var(self.INVESTFLOWS, m.PERIODS, within=NonNegativeReals) if m.es.multi_period: self.old = Var( - self.INVESTFLOWS, - m.PERIODS, - within=NonNegativeReals + self.INVESTFLOWS, m.PERIODS, within=NonNegativeReals ) # Old endogenous capacity to be decommissioned (due to lifetime) self.old_end = Var( - self.INVESTFLOWS, - m.PERIODS, - within=NonNegativeReals + self.INVESTFLOWS, m.PERIODS, within=NonNegativeReals ) # Old exogenous capacity to be decommissioned (due to lifetime) self.old_exo = Var( - self.INVESTFLOWS, - m.PERIODS, - within=NonNegativeReals + self.INVESTFLOWS, m.PERIODS, within=NonNegativeReals ) # create status variable for a non-convex investment flow self.invest_status = Var( - self.NON_CONVEX_INVESTFLOWS, - m.PERIODS, - within=Binary + self.NON_CONVEX_INVESTFLOWS, m.PERIODS, within=Binary ) # ######################### CONSTRAINTS ############################### @@ -376,31 +368,24 @@ def _min_invest_rule(block): self.minimum_rule.add((i, o, p), expr) self.minimum_rule = Constraint( - self.NON_CONVEX_INVESTFLOWS, m.PERIODS, - noruleinit=True - ) - self.minimum_rule_build = BuildAction( - rule=_min_invest_rule + self.NON_CONVEX_INVESTFLOWS, m.PERIODS, noruleinit=True ) + self.minimum_rule_build = BuildAction(rule=_min_invest_rule) def _max_invest_rule(block): """Rule definition for applying a minimum investment""" for i, o in self.NON_CONVEX_INVESTFLOWS: for p in m.PERIODS: - expr = ( - self.invest[i, o, p] - <= (m.flows[i, o].investment.maximum[p] - * self.invest_status[i, o, p]) + expr = self.invest[i, o, p] <= ( + m.flows[i, o].investment.maximum[p] + * self.invest_status[i, o, p] ) self.maximum_rule.add((i, o, p), expr) self.maximum_rule = Constraint( - self.NON_CONVEX_INVESTFLOWS, m.PERIODS, - noruleinit=True - ) - self.maximum_rule_build = BuildAction( - rule=_max_invest_rule + self.NON_CONVEX_INVESTFLOWS, m.PERIODS, noruleinit=True ) + self.maximum_rule_build = BuildAction(rule=_max_invest_rule) # Handle unit lifetimes def _total_capacity_rule(block): @@ -410,26 +395,29 @@ def _total_capacity_rule(block): for i, o in self.INVESTFLOWS: for p in m.PERIODS: if p == 0: - expr = (self.total[i, o, p] - == self.invest[i, o, p] - + m.flows[i, o].investment.existing) + expr = ( + self.total[i, o, p] + == self.invest[i, o, p] + + m.flows[i, o].investment.existing + ) self.total_rule.add((i, o, p), expr) # applicable for multi-period model only else: - expr = (self.total[i, o, p] - == self.invest[i, o, p] - + self.total[i, o, p - 1] - - self.old[i, o, p]) + expr = ( + self.total[i, o, p] + == self.invest[i, o, p] + + self.total[i, o, p - 1] + - self.old[i, o, p] + ) self.total_rule.add((i, o, p), expr) self.total_rule = Constraint( - self.INVESTFLOWS, m.PERIODS, - noruleinit=True + self.INVESTFLOWS, m.PERIODS, noruleinit=True ) - self.total_rule_build = BuildAction( - rule=_total_capacity_rule) + self.total_rule_build = BuildAction(rule=_total_capacity_rule) if m.es.multi_period: + def _old_capacity_rule_end(block): """Rule definition for determining old endogenously installed capacity to be decommissioned due to reaching its lifetime @@ -437,15 +425,17 @@ def _old_capacity_rule_end(block): for i, o in self.INVESTFLOWS: lifetime = m.flows[i, o].investment.lifetime if lifetime is None: - msg = ("You have to specify a lifetime " - "for an InvestmentFlow in " - "a multi-period model! Value for {} " - "is missing.".format((i, o))) + msg = ( + "You have to specify a lifetime " + "for an InvestmentFlow in " + "a multi-period model! Value for {} " + "is missing.".format((i, o)) + ) raise ValueError(msg) for p in m.PERIODS: # No shutdown in first period if p == 0: - expr = (self.old_end[i, o, p] == 0) + expr = self.old_end[i, o, p] == 0 self.old_rule_end.add((i, o, p), expr) elif lifetime <= m.es.periods_years[p]: # Obtain commissioning period @@ -455,20 +445,19 @@ def _old_capacity_rule_end(block): # change of sign is detected comm_p = k - 1 break - expr = (self.old_end[i, o, p] - == self.invest[i, o, comm_p]) + expr = ( + self.old_end[i, o, p] + == self.invest[i, o, comm_p] + ) self.old_rule_end.add((i, o, p), expr) else: - expr = (self.old_end[i, o, p] == 0) + expr = self.old_end[i, o, p] == 0 self.old_rule_end.add((i, o, p), expr) self.old_rule_end = Constraint( - self.INVESTFLOWS, m.PERIODS, - noruleinit=True - ) - self.old_rule_end_build = BuildAction( - rule=_old_capacity_rule_end + self.INVESTFLOWS, m.PERIODS, noruleinit=True ) + self.old_rule_end_build = BuildAction(rule=_old_capacity_rule_end) def _old_capacity_rule_exo(block): """Rule definition for determining old exogenously given @@ -481,7 +470,7 @@ def _old_capacity_rule_exo(block): for p in m.PERIODS: # No shutdown in first period if p == 0: - expr = (self.old_exo[i, o, p] == 0) + expr = self.old_exo[i, o, p] == 0 self.old_rule_exo.add((i, o, p), expr) elif lifetime - age <= m.es.periods_years[p]: # Track decommissioning status @@ -492,19 +481,16 @@ def _old_capacity_rule_exo(block): ) is_decommissioned = True else: - expr = (self.old_exo[i, o, p] == 0) + expr = self.old_exo[i, o, p] == 0 self.old_rule_exo.add((i, o, p), expr) else: - expr = (self.old_exo[i, o, p] == 0) + expr = self.old_exo[i, o, p] == 0 self.old_rule_exo.add((i, o, p), expr) self.old_rule_exo = Constraint( - self.INVESTFLOWS, m.PERIODS, - noruleinit=True - ) - self.old_rule_exo_build = BuildAction( - rule=_old_capacity_rule_exo + self.INVESTFLOWS, m.PERIODS, noruleinit=True ) + self.old_rule_exo_build = BuildAction(rule=_old_capacity_rule_exo) def _old_capacity_rule(block): """Rule definition for determining (overall) old capacity @@ -514,16 +500,14 @@ def _old_capacity_rule(block): for p in m.PERIODS: expr = ( self.old[i, o, p] - == self.old_end[i, o, p] + self.old_exo[i, o, p]) + == self.old_end[i, o, p] + self.old_exo[i, o, p] + ) self.old_rule.add((i, o, p), expr) self.old_rule = Constraint( - self.INVESTFLOWS, m.PERIODS, - noruleinit=True - ) - self.old_rule_build = BuildAction( - rule=_old_capacity_rule + self.INVESTFLOWS, m.PERIODS, noruleinit=True ) + self.old_rule_build = BuildAction(rule=_old_capacity_rule) def _investflow_fixed_rule(block): """Rule definition of constraint to fix flow variable @@ -538,13 +522,9 @@ def _investflow_fixed_rule(block): self.fixed.add((i, o, p, t), expr) self.fixed = Constraint( - self.FIXED_INVESTFLOWS, - m.TIMEINDEX, - noruleinit=True - ) - self.fixed_build = BuildAction( - rule=_investflow_fixed_rule + self.FIXED_INVESTFLOWS, m.TIMEINDEX, noruleinit=True ) + self.fixed_build = BuildAction(rule=_investflow_fixed_rule) def _max_investflow_rule(block): """Rule definition of constraint setting an upper bound of flow @@ -559,13 +539,9 @@ def _max_investflow_rule(block): self.max.add((i, o, p, t), expr) self.max = Constraint( - self.NON_FIXED_INVESTFLOWS, - m.TIMEINDEX, - noruleinit=True - ) - self.max_build = BuildAction( - rule=_max_investflow_rule + self.NON_FIXED_INVESTFLOWS, m.TIMEINDEX, noruleinit=True ) + self.max_build = BuildAction(rule=_max_investflow_rule) def _min_investflow_rule(block): """Rule definition of constraint setting a lower bound on flow @@ -580,49 +556,44 @@ def _min_investflow_rule(block): self.min.add((i, o, p, t), expr) self.min = Constraint( - self.MIN_INVESTFLOWS, - m.TIMEINDEX, - noruleinit=True - ) - self.min_build = BuildAction( - rule=_min_investflow_rule + self.MIN_INVESTFLOWS, m.TIMEINDEX, noruleinit=True ) + self.min_build = BuildAction(rule=_min_investflow_rule) def _summed_max_investflow_rule(block, i, o): """Rule definition for build action of max. sum flow constraint in investment case. """ - expr = ( - sum(m.flow[i, o, p, t] * m.timeincrement[t] - for p, t in m.TIMEINDEX) - <= (m.flows[i, o].summed_max - * sum(self.total[i, o, p] for p in m.PERIODS)) + expr = sum( + m.flow[i, o, p, t] * m.timeincrement[t] for p, t in m.TIMEINDEX + ) <= ( + m.flows[i, o].summed_max + * sum(self.total[i, o, p] for p in m.PERIODS) ) return expr self.summed_max = Constraint( - self.SUMMED_MAX_INVESTFLOWS, - rule=_summed_max_investflow_rule + self.SUMMED_MAX_INVESTFLOWS, rule=_summed_max_investflow_rule ) def _summed_min_investflow_rule(block, i, o): """Rule definition for build action of min. sum flow constraint in investment case. """ - expr = ( - sum(m.flow[i, o, p, t] * m.timeincrement[t] - for p, t in m.TIMEINDEX) - >= (sum(self.total[i, o, p] for p in m.PERIODS) - * m.flows[i, o].summed_min) + expr = sum( + m.flow[i, o, p, t] * m.timeincrement[t] for p, t in m.TIMEINDEX + ) >= ( + sum(self.total[i, o, p] for p in m.PERIODS) + * m.flows[i, o].summed_min ) return expr self.summed_min = Constraint( - self.SUMMED_MIN_INVESTFLOWS, - rule=_summed_min_investflow_rule + self.SUMMED_MIN_INVESTFLOWS, rule=_summed_min_investflow_rule ) if m.es.multi_period: + def _overall_maximum_investflow_rule(block): """Rule definition for maximum overall investment in investment case. @@ -636,9 +607,7 @@ def _overall_maximum_investflow_rule(block): self.overall_maximum.add((i, o, p), expr) self.overall_maximum = Constraint( - self.OVERALL_MAXIMUM_INVESTFLOWS, - m.PERIODS, - noruleinit=True + self.OVERALL_MAXIMUM_INVESTFLOWS, m.PERIODS, noruleinit=True ) self.overall_maximum_build = BuildAction( rule=_overall_maximum_investflow_rule @@ -658,7 +627,7 @@ def _overall_minimum_investflow_rule(block, i, o): self.overall_minimum = Constraint( self.OVERALL_MINIMUM_INVESTFLOWS, - rule=_overall_minimum_investflow_rule + rule=_overall_minimum_investflow_rule, ) def _objective_expression(self): @@ -705,19 +674,22 @@ def _objective_expression(self): lifetime = m.flows[i, o].investment.lifetime interest = m.flows[i, o].investment.interest_rate if interest == 0: - warn(msg.format(m.discount_rate), - debugging.SuspiciousUsageWarning) + warn( + msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning, + ) interest = m.discount_rate for p in m.PERIODS: annuity = economics.annuity( capex=m.flows[i, o].investment.ep_costs[p], n=lifetime, - wacc=interest + wacc=interest, ) investment_costs_increment = ( - self.invest[i, o, p] * annuity * lifetime - * ((1 + m.discount_rate) - ** (-m.es.periods_years[p])) + self.invest[i, o, p] + * annuity + * lifetime + * ((1 + m.discount_rate) ** (-m.es.periods_years[p])) ) investment_costs += investment_costs_increment period_investment_costs[p] += investment_costs_increment @@ -726,22 +698,22 @@ def _objective_expression(self): lifetime = m.flows[i, o].investment.lifetime interest = m.flows[i, o].investment.interest_rate if interest == 0: - warn(msg.format(m.discount_rate), - debugging.SuspiciousUsageWarning) + warn( + msg.format(m.discount_rate), + debugging.SuspiciousUsageWarning, + ) interest = m.discount_rate for p in m.PERIODS: annuity = economics.annuity( capex=m.flows[i, o].investment.ep_costs[p], n=lifetime, - wacc=interest + wacc=interest, ) investment_costs_increment = ( - (self.invest[i, o, p] * annuity * lifetime - + self.invest_status[i, o, p] - * m.flows[i, o].investment.offset[p]) - * ((1 + m.discount_rate) - ** (-m.es.periods_years[p])) - ) + self.invest[i, o, p] * annuity * lifetime + + self.invest_status[i, o, p] + * m.flows[i, o].investment.offset[p] + ) * ((1 + m.discount_rate) ** (-m.es.periods_years[p])) investment_costs += investment_costs_increment period_investment_costs[p] += investment_costs_increment @@ -749,17 +721,15 @@ def _objective_expression(self): if m.flows[i, o].investment.fixed_costs[0] is not None: lifetime = m.flows[i, o].investment.lifetime for p in m.PERIODS: - fixed_costs += ( - sum(self.invest[i, o, p] - * m.flows[i, o].investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range( - m.es.periods_years[p], - m.es.periods_years[p] + lifetime) - ) - * ((1 + m.discount_rate) - ** (-m.es.periods_years[p])) - ) + fixed_costs += sum( + self.invest[i, o, p] + * m.flows[i, o].investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime, + ) + ) * ((1 + m.discount_rate) ** (-m.es.periods_years[p])) self.investment_costs = Expression(expr=investment_costs) self.period_investment_costs = Expression(expr=period_investment_costs) diff --git a/src/oemof/solph/flows/_non_convex_flow.py b/src/oemof/solph/flows/_non_convex_flow.py index 4aff996f9..5607eaaef 100644 --- a/src/oemof/solph/flows/_non_convex_flow.py +++ b/src/oemof/solph/flows/_non_convex_flow.py @@ -580,19 +580,32 @@ def _positive_gradient_flow_rule(block): for index in range(1, len(m.TIMEINDEX) + 1): if m.TIMEINDEX[index][1] > 0: lhs = ( - m.flow[i, o, m.TIMEINDEX[index][0], - m.TIMEINDEX[index][1]] + m.flow[ + i, + o, + m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1], + ] * self.status[i, o, m.TIMEINDEX[index][1]] - - m.flow[i, o, m.TIMEINDEX[index - 1][0], - m.TIMEINDEX[index - 1][1]] + - m.flow[ + i, + o, + m.TIMEINDEX[index - 1][0], + m.TIMEINDEX[index - 1][1], + ] * self.status[i, o, m.TIMEINDEX[index - 1][1]] ) - rhs = self.positive_gradient[i, o, - m.TIMEINDEX[index][1]] + rhs = self.positive_gradient[ + i, o, m.TIMEINDEX[index][1] + ] self.positive_gradient_constr.add( - (i, o, m.TIMEINDEX[index][0], - m.TIMEINDEX[index][1]), - lhs <= rhs + ( + i, + o, + m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1], + ), + lhs <= rhs, ) else: pass # return(Constraint.Skip) @@ -610,19 +623,32 @@ def _negative_gradient_flow_rule(block): for index in range(1, len(m.TIMEINDEX) + 1): if m.TIMEINDEX[index][1] > 0: lhs = ( - m.flow[i, o, m.TIMEINDEX[index - 1][0], - m.TIMEINDEX[index - 1][1]] + m.flow[ + i, + o, + m.TIMEINDEX[index - 1][0], + m.TIMEINDEX[index - 1][1], + ] * self.status[i, o, m.TIMEINDEX[index - 1][1]] - - m.flow[i, o, m.TIMEINDEX[index][0], - m.TIMEINDEX[index][1]] + - m.flow[ + i, + o, + m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1], + ] * self.status[i, o, m.TIMEINDEX[index][1]] ) - rhs = self.negative_gradient[i, o, - m.TIMEINDEX[index][1]] + rhs = self.negative_gradient[ + i, o, m.TIMEINDEX[index][1] + ] self.negative_gradient_constr.add( - (i, o, m.TIMEINDEX[index][0], - m.TIMEINDEX[index][1]), - lhs <= rhs + ( + i, + o, + m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1], + ), + lhs <= rhs, ) else: pass # return(Constraint.Skip) @@ -695,11 +721,12 @@ def _objective_expression(self): is not None ): for t in m.TIMESTEPS: - gradient_costs += ( - self.positive_gradient[i, o, t] * ( - m.flows[i, o].nonconvex.positive_gradient[ - "costs"] - ) + gradient_costs += self.positive_gradient[ + i, o, t + ] * ( + m.flows[i, o].nonconvex.positive_gradient[ + "costs" + ] ) if self.NEGATIVE_GRADIENT_FLOWS: @@ -709,50 +736,45 @@ def _objective_expression(self): is not None ): for t in m.TIMESTEPS: - gradient_costs += ( - self.negative_gradient[i, o, t] * ( - m.flows[i, o].nonconvex.negative_gradient[ - "costs"] - ) + gradient_costs += self.negative_gradient[ + i, o, t + ] * ( + m.flows[i, o].nonconvex.negative_gradient[ + "costs" + ] ) else: if self.STARTUPFLOWS: for i, o in self.STARTUPFLOWS: - if (m.flows[i, o].nonconvex.startup_costs[0] - is not None): + if m.flows[i, o].nonconvex.startup_costs[0] is not None: startup_costs += sum( self.startup[i, o, t] * m.flows[i, o].nonconvex.startup_costs[t] * m.objective_weighting[t] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) for p, t in m.TIMEINDEX ) if self.SHUTDOWNFLOWS: for i, o in self.SHUTDOWNFLOWS: - if (m.flows[i, o].nonconvex.shutdown_costs[0] - is not None): + if m.flows[i, o].nonconvex.shutdown_costs[0] is not None: shutdown_costs += sum( self.shutdown[i, o, t] * m.flows[i, o].nonconvex.shutdown_costs[t] * m.objective_weighting[t] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) for p, t in m.TIMEINDEX ) if self.ACTIVITYCOSTFLOWS: for i, o in self.ACTIVITYCOSTFLOWS: - if (m.flows[i, o].nonconvex.activity_costs[0] - is not None): + if m.flows[i, o].nonconvex.activity_costs[0] is not None: activity_costs += sum( self.status[i, o, t] * m.flows[i, o].nonconvex.activity_costs[t] * m.objective_weighting[t] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) for p, t in m.TIMEINDEX ) @@ -763,8 +785,7 @@ def _objective_expression(self): (1 - self.status[i, o, t]) * m.flows[i, o].nonconvex.inactivity_costs[t] * m.objective_weighting[t] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) for p, t in m.TIMEINDEX ) @@ -776,28 +797,30 @@ def _objective_expression(self): ): gradient_costs += sum( self.positive_gradient[i, o, t] - * (m.flows[i, o].nonconvex - .positive_gradient["costs"]) + * ( + m.flows[i, o].nonconvex.positive_gradient[ + "costs" + ] + ) * m.objective_weighting[t] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) for p, t in m.TIMEINDEX ) if self.NEGATIVE_GRADIENT_FLOWS: for i, o in self.NEGATIVE_GRADIENT_FLOWS: if ( - (m.flows[i, o].nonconvex - .negative_gradient["ub"][0]) - is not None - ): + m.flows[i, o].nonconvex.negative_gradient["ub"][0] + ) is not None: gradient_costs += sum( self.negative_gradient[i, o, t] - * (m.flows[i, o].nonconvex - .negative_gradient["costs"]) + * ( + m.flows[i, o].nonconvex.negative_gradient[ + "costs" + ] + ) * m.objective_weighting[t] - * ((1 + m.discount_rate) - ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) for p, t in m.TIMEINDEX ) @@ -809,8 +832,11 @@ def _objective_expression(self): self.costs = Expression( expr=( - startup_costs + shutdown_costs - + activity_costs + inactivity_costs + gradient_costs + startup_costs + + shutdown_costs + + activity_costs + + inactivity_costs + + gradient_costs ) ) From cd975976073c7734c58c9a38977611cfcf34954c Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 18 Dec 2021 15:13:36 +0100 Subject: [PATCH 0093/1363] Add check for finite values --- src/oemof/solph/flows/_flow.py | 10 ++++++++++ tests/test_solph_network_classes.py | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index e294de351..376b3c126 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -14,6 +14,7 @@ SPDX-License-Identifier: MIT """ +import math from warnings import warn from oemof.network import network as on @@ -219,6 +220,10 @@ def __init__(self, **kwargs): + "nonconvex flows!" ) + infinite_error_msg = ( + "{} must be a finite value. Passing an infinite " + "value is not allowed." + ) if not self.investment: warn_msg = ( "If {} is set in a flow (except InvestmentFlow), " @@ -236,6 +241,11 @@ def __init__(self, **kwargs): warn_msg.format("summed_min"), debugging.SuspiciousUsageWarning, ) + else: + assert math.isfinite( + self.nominal_value + ), infinite_error_msg.format("nominal_value") + assert math.isfinite(self.max[0]), infinite_error_msg.format("max") # Checking for impossible gradient combinations if self.nonconvex: diff --git a/tests/test_solph_network_classes.py b/tests/test_solph_network_classes.py index b95500be2..7fbbff969 100644 --- a/tests/test_solph_network_classes.py +++ b/tests/test_solph_network_classes.py @@ -92,6 +92,15 @@ def test_flow_with_fix_and_min_max(): solph.flows.Flow(fix=[1, 3], max=[0, 5], min=[4, 9]) +def test_infinite_values(): + msg1 = "nominal_value must be a finite value" + msg2 = "max must be a finite value" + with pytest.raises(AssertionError, match=msg1): + solph.flows.Flow(nominal_value=float("+inf")) + with pytest.raises(AssertionError, match=msg2): + solph.flows.Flow(max=float("+inf")) + + def test_summed_min_and_summed_max(): msg1 = "If summed_max is set in a flow " msg2 = "If summed_min is set in a flow " From ff16eddf79f779bd741a91d70d98a51aa7604cbb Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 18 Dec 2021 16:35:34 +0100 Subject: [PATCH 0094/1363] Fix identation errors --- .../solph/components/_generic_storage.py | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index d86a5fd0d..c2c56f8d6 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -1069,7 +1069,7 @@ def _inv_storage_init_content_max_rule(block): for n in self.INVESTSTORAGES_NO_INIT_CONTENT: for p in m.PERIODS: expr = block.init_content[n, p] <= block.total[n, p] - block.init_content_limit.add((n, p), expr) + block.init_content_limit.add((n, p), expr) self.init_content_limit = Constraint( self.INVESTSTORAGES_NO_INIT_CONTENT, m.PERIODS, noruleinit=True @@ -1080,7 +1080,8 @@ def _inv_storage_init_content_max_rule(block): def _inv_storage_init_content_fix_rule(block): """Constraint for a fixed initial storage capacity.""" - for n in self.INVESTSTORAGES: + for n in self.INVESTSTORAGES_INIT_CONTENT: + is_invested = False for p in m.PERIODS: if p == 0: expr = ( @@ -1089,11 +1090,17 @@ def _inv_storage_init_content_fix_rule(block): ) self.init_content_fix.add((n, p), expr) else: - expr = block.init_content[ - n, p - ] == n.initial_storage_level * min( - 0, value(block.total[n, p] - block.total[n, p - 1]) - ) + if not is_invested: + expr = block.init_content[ + n, p + ] == n.initial_storage_level * block.total[n, p] + else: + expr = block.init_content[ + n, p + ] == self.storage_content[ + m.TIMESTEPS_IN_PERIOD[p-1][-1] + ] + self.init_content_fix.add((n, p), expr) self.init_content_fix = Constraint( @@ -1236,7 +1243,7 @@ def _power_coupled(block): ) * n.invest_relation_input_output == ( m.InvestmentFlowBlock.total[i[n], n, p] ) - self.power_coupled.add((n, p), expr) + self.power_coupled.add((n, p), expr) self.power_coupled = Constraint( self.INVEST_REL_IN_OUT, m.PERIODS, noruleinit=True From c88a5da080bf436ec6a8064e59f36434de77b413 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 18 Dec 2021 17:16:11 +0100 Subject: [PATCH 0095/1363] Differentiate standard and multi-period storage definition --- .../solph/components/_generic_storage.py | 176 +++++++++--------- 1 file changed, 84 insertions(+), 92 deletions(-) diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index c2c56f8d6..09baf0be6 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -923,9 +923,10 @@ def _storage_investvar_bound_rule(block, n, p): self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals ) - self.init_content = Var( - self.INVESTSTORAGES, m.PERIODS, within=NonNegativeReals - ) + else: + self.init_content = Var( + self.INVESTSTORAGES, within=NonNegativeReals + ) # create status variable for a non-convex investment storage self.invest_status = Var( @@ -968,6 +969,7 @@ def _total_storage_capacity_rule(block): rule=_total_storage_capacity_rule ) + # multi-period storage implementation for time intervals if m.es.multi_period: def _old_storage_capacity_rule_end(block): @@ -1064,102 +1066,92 @@ def _old_storage_capacity_rule(block): self.old_rule_build = BuildAction(rule=_old_storage_capacity_rule) - def _inv_storage_init_content_max_rule(block): - """Constraint for a variable initial storage capacity.""" - for n in self.INVESTSTORAGES_NO_INIT_CONTENT: - for p in m.PERIODS: - expr = block.init_content[n, p] <= block.total[n, p] - block.init_content_limit.add((n, p), expr) - - self.init_content_limit = Constraint( - self.INVESTSTORAGES_NO_INIT_CONTENT, m.PERIODS, noruleinit=True - ) - self.init_content_limit_build = BuildAction( - rule=_inv_storage_init_content_max_rule - ) - - def _inv_storage_init_content_fix_rule(block): - """Constraint for a fixed initial storage capacity.""" - for n in self.INVESTSTORAGES_INIT_CONTENT: - is_invested = False - for p in m.PERIODS: - if p == 0: - expr = ( - block.init_content[n, p] - == n.initial_storage_level * block.total[n, p] - ) - self.init_content_fix.add((n, p), expr) - else: - if not is_invested: - expr = block.init_content[ - n, p - ] == n.initial_storage_level * block.total[n, p] + def _init_content_multi_period_rule(block): + """Rule defining the initial storage level + for a multi-period model + """ + for n in self.INVESTSTORAGES_INIT_CONTENT: + is_commissioned = False + for p in m.PERIODS: + if not is_commissioned: + expr = ( + self.storage_content[ + n, m.TIMESTEPS_IN_PERIOD[p][0] + ] + == n.initial_storage_level * self.total[n, p] + ) + is_commissioned = True + self.init_content_multi_period.add((n, p), expr) else: - expr = block.init_content[ - n, p - ] == self.storage_content[ - m.TIMESTEPS_IN_PERIOD[p-1][-1] - ] + pass + self.init_content_multi_period = Constraint( + self.INVESTSTORAGES_INIT_CONTENT, m.PERIODS, noruleinit=True + ) + self.init_content_multi_period_rule_build = BuildAction( + rule=_init_content_multi_period_rule + ) + + # Standard storage implementation for discrete time points + else: + def _inv_storage_init_content_max_rule(block, n): + """Constraint for a variable initial storage capacity.""" + return ( + block.init_content[n] + <= n.investment.existing + block.invest[n, 0] + ) - self.init_content_fix.add((n, p), expr) + self.init_content_limit = Constraint( + self.INVESTSTORAGES_NO_INIT_CONTENT, + rule=_inv_storage_init_content_max_rule, + ) - self.init_content_fix = Constraint( - self.INVESTSTORAGES_INIT_CONTENT, m.PERIODS, noruleinit=True - ) - self.init_content_fix_build = BuildAction( - rule=_inv_storage_init_content_fix_rule - ) + def _inv_storage_init_content_fix_rule(block, n): + """Constraint for a fixed initial storage capacity.""" + return ( + block.init_content[n] + <= n.investment.existing + block.invest[n, 0] + ) - # TODO: Check new init_content implementation! - def _storage_balance_first_rule(block): - """ - Rule definition for the storage balance of every storage n for the - first time step of every period. - """ - for n in self.INVESTSTORAGES: - for p in m.PERIODS: - first_step = m.TIMESTEPS_IN_PERIOD[p][0] - - lhs = 0 - lhs += block.storage_content[n, first_step] - lhs += ( - -block.init_content[n, p] - * (1 - n.loss_rate[first_step]) - ** m.timeincrement[first_step] - ) - lhs += ( - n.fixed_losses_relative[first_step] - * block.total[n, p] - * m.timeincrement[first_step] - ) - lhs += ( - n.fixed_losses_absolute[first_step] - * m.timeincrement[first_step] - ) - lhs += ( - -m.flow[i[n], n, p, first_step] - * n.inflow_conversion_factor[first_step] - ) * m.timeincrement[first_step] - lhs += ( - m.flow[n, o[n], p, first_step] - / n.outflow_conversion_factor[first_step] - ) * m.timeincrement[first_step] - rhs = 0 - self.balance_first.add((n, p), (lhs == rhs)) + self.init_content_fix = Constraint( + self.INVESTSTORAGES_INIT_CONTENT, + rule=_inv_storage_init_content_fix_rule, + ) - self.balance_first = Constraint( - self.INVESTSTORAGES, - m.PERIODS, - noruleinit=True, - ) - self.balance_first_build = BuildAction( - rule=_storage_balance_first_rule - ) + def _storage_balance_first_rule(block, n): + """ + Rule definition for the storage balance of every storage n + for the first time step. + """ + expr = 0 + expr += block.storage_content[n, 0] + expr += ( + -block.init_content[n] + * (1 - n.loss_rate[0]) ** m.timeincrement[0] + ) + expr += ( + n.fixed_losses_relative[0] + * (n.investment.existing + self.invest[n, 0]) + * m.timeincrement[0] + ) + expr += n.fixed_losses_absolute[0] * m.timeincrement[0] + expr += ( + -m.flow[i[n], n, 0, 0] + * n.inflow_conversion_factor[0] + ) * m.timeincrement[0] + expr += ( + m.flow[n, o[n], 0, 0] + / n.outflow_conversion_factor[0] + ) * m.timeincrement[0] + return expr == 0 + + self.balance_first = Constraint( + self.INVESTSTORAGES, rule=_storage_balance_first_rule + ) def _storage_balance_rule(block, n, p, t): """ - Rule definition for the storage balance of every storage n for the - every time step but the first. + Rule definition for the storage balance of every storage n + for every time step but the first. """ expr = 0 expr += block.storage_content[n, t] @@ -1192,7 +1184,7 @@ def _storage_balance_rule(block, n, p, t): def _balanced_storage_rule(block, n): return ( block.storage_content[n, m.TIMESTEPS[-1]] - == block.init_content[n, 0] + == block.init_content[n] ) self.balanced_cstr = Constraint( From e1c42345fd6e1f4acead286bdb3379ef43276aec Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 18 Dec 2021 17:45:33 +0100 Subject: [PATCH 0096/1363] Drop unused attributes in EnergySystem --- src/oemof/solph/_energy_system.py | 42 +++++++++---------------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 29c264b12..eb0ee513d 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -71,12 +71,12 @@ def __init__(self, multi_period=False, periods=None, **kwargs): warnings.warn(msg, debugging.SuspiciousUsageWarning) self.multi_period = multi_period self.periods = self._add_periods(periods) - self._extract_periods_lengths_gap_and_years() + self._extract_periods_years() def _add_periods(self, periods): """Returns periods to be added to the energy system - * For a standard model, periods only contain one value. + * For a standard model, periods only contain one value {0: 0} * For a multi-period model, periods are based on the years used in the timeindex. As a default, each year in the timeindex is mapped to its own period. @@ -85,14 +85,14 @@ def _add_periods(self, periods): ---------- periods : dict Periods of a (multi-period) model - Keys are years as integer values, - values are the periods defined by their first and last timestep. + Keys are periods as increasing integer values, starting from 0, + values are the periods defined by a pandas.date_range For a standard model, only one period is used. Returns ------- periods : dict - Periods of the energy system + Periods of the energy system (ensure it being set) """ if not self.multi_period: periods = {0: 0} @@ -114,35 +114,17 @@ def _add_periods(self, periods): return periods - # TODO: Check if length and gap are needed (no decommissions within period) - def _extract_periods_lengths_gap_and_years(self): - """Determine length of one and difference between subsequent periods - and map periods to simulation years starting with 0 + def _extract_periods_years(self): + """Map simulation years to the respective period based on timeindices - * `periods_length` contains the length of a period in full years - * `periods_gap` is the difference in years between subsequent periods, - attributed to the prior one * `periods_years` is the simulation year corresponding to the start of a period, starting with 0 """ - periods_gap = {} - if not self.multi_period: - periods_length = {0: 1} - periods_years = {0: 0} - else: - periods_length = {} - periods_years = {0: 0} - - previous_end = None + periods_years = {0: 0} + if self.multi_period: + start_year = self.periods[0].min().year for k, v in self.periods.items(): - periods_length[k] = v.max().year - v.min().year + 1 if k >= 1: - periods_gap[k - 1] = v.min().year - previous_end.year - 1 - periods_years[k] = sum( - periods_length[kk] + periods_gap[kk] for kk in range(k) - ) - previous_end = v.max() - - self.periods_length = periods_length - self.periods_gap = periods_gap + periods_years[k] = v.min().year - start_year + self.periods_years = periods_years From 45b32b8d3c86d8b34743928e0adb40574cad7cfa Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 18 Dec 2021 19:02:01 +0100 Subject: [PATCH 0097/1363] Add decommissioning option for dispatch flows and fixed costs for existing units --- src/oemof/solph/flows/_flow.py | 87 +++++++++++++++++++++++ src/oemof/solph/flows/_investment_flow.py | 19 +++++ 2 files changed, 106 insertions(+) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 586b80bc1..3e10e2ddc 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -145,6 +145,8 @@ def __init__(self, **kwargs): "investment", "nonconvex", "integer", + "lifetime", + "age" ] sequences = ["fix", "variable_costs", "fixed_costs", "min", "max"] dictionaries = ["positive_gradient", "negative_gradient"] @@ -389,6 +391,22 @@ def _create(self, group=None): self.INTEGER_FLOWS = Set( initialize=[(g[0], g[1]) for g in group if g[2].integer] ) + + self.LIFETIME_FLOWS = Set( + initialize=[ + (g[0], g[1]) + for g in group + if g[2].lifetime is not None and g[2].age is None + ] + ) + + self.LIFETIME_AGE_FLOWS = Set( + initialize=[ + (g[0], g[1]) + for g in group + if g[2].lifetime is not None and g[2].age is not None + ] + ) # ######################### Variables ################################ self.positive_gradient = Var(self.POSITIVE_GRADIENT_FLOWS, m.TIMESTEPS) @@ -535,6 +553,52 @@ def _integer_flow_rule(block, ii, oi, pi, ti): self.INTEGER_FLOWS, m.TIMEINDEX, rule=_integer_flow_rule ) + if m.es.multi_period: + + def _lifetime_output_rule(block): + """Force flow value to zero when lifetime is reached""" + for inp, out in self.LIFETIME_FLOWS: + for p, ts in m.TIMEINDEX: + if m.flows[inp, out].lifetime <= m.es.periods_years[p]: + lhs = m.flow[inp, out, p, ts] + rhs = 0 + self.lifetime_output.add((inp, out, p, ts), + (lhs == rhs)) + else: + pass # return Constraint.skip() + + self.lifetime_output = Constraint( + self.LIFETIME_FLOWS, m.TIMEINDEX, noruleinit=True + ) + self.lifetime_output_build = BuildAction( + rule=_lifetime_output_rule + ) + + def _lifetime_age_output_rule(block): + """Force flow value to zero when lifetime is reached + considering initial age + """ + for inp, out in self.LIFETIME_AGE_FLOWS: + for p, ts in m.TIMEINDEX: + if ( + m.flows[inp, out].lifetime + - m.flows[inp, out].age + <= m.es.periods_years[p] + ): + lhs = m.flow[inp, out, p, ts] + rhs = 0 + self.lifetime_age_output.add((inp, out, p, ts), + (lhs == rhs)) + else: + pass # return Constraint.skip() + + self.lifetime_age_output = Constraint( + self.LIFETIME_AGE_FLOWS, m.TIMEINDEX, noruleinit=True + ) + self.lifetime_age_output_build = BuildAction( + rule=_lifetime_age_output_rule + ) + def _objective_expression(self): r"""Objective expression for all standard flows with fixed costs and variable costs. @@ -565,9 +629,12 @@ def _objective_expression(self): * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) + # Include fixed costs of units operating "forever" if ( m.flows[i, o].fixed_costs[0] is not None and m.flows[i, o].nominal_value is not None + and (i, o) not in self.LIFETIME_FLOWS + and (i, o) not in self.LIFETIME_AGE_FLOWS ): for p in m.PERIODS: fixed_costs += ( @@ -576,6 +643,26 @@ def _objective_expression(self): * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) + # Fixed costs for units with limited lifetime + for i, o in self.LIFETIME_FLOWS: + if m.flows[i, o].fixed_costs[0] is not None: + fixed_costs += sum( + m.flows[i, o].nominal_value + * m.flows[i, o].fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(0, m.flows[i, o].lifetime) + ) + + for i, o in self.LIFETIME_AGE_FLOWS: + if m.flows[i, o].fixed_costs[0] is not None: + fixed_costs += sum( + m.flows[i, o].nominal_value + * m.flows[i, o].fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(0, m.flows[i, o].lifetime + - m.flows[i, o].age) + ) + self.variable_costs = Expression(expr=variable_costs) self.fixed_costs = Expression(expr=fixed_costs) self.costs = Expression(expr=variable_costs + fixed_costs) diff --git a/src/oemof/solph/flows/_investment_flow.py b/src/oemof/solph/flows/_investment_flow.py index 28b5ecb72..02d91643c 100644 --- a/src/oemof/solph/flows/_investment_flow.py +++ b/src/oemof/solph/flows/_investment_flow.py @@ -297,6 +297,14 @@ def _create(self, group=None): ] ) + self.EXISTING_INVESTFLOWS = Set( + initialize=[ + (g[0], g[1]) + for g in group + if g[2].investment.existing is not None + ] + ) + self.OVERALL_MAXIMUM_INVESTFLOWS = Set( initialize=[ (g[0], g[1]) @@ -731,6 +739,17 @@ def _objective_expression(self): ) ) * ((1 + m.discount_rate) ** (-m.es.periods_years[p])) + for i, o in self.EXISTING_INVESTFLOWS: + if m.flows[i, o].investment.fixed_costs[0] is not None: + lifetime = m.flows[i, o].investment.lifetime + age = m.flows[i, o].investment.age + fixed_costs += sum( + m.flows[i, o].investment.existing + * m.flows[i, o].investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(0, lifetime - age) + ) + self.investment_costs = Expression(expr=investment_costs) self.period_investment_costs = Expression(expr=period_investment_costs) self.fixed_costs = Expression(expr=fixed_costs) From 16324f39f4a6830e37457f0d1667d7dd9b536a4f Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 18 Dec 2021 19:02:53 +0100 Subject: [PATCH 0098/1363] Reformat using black --- .../solph/components/_generic_storage.py | 34 +++++----- .../components/experimental/_sink_dsm.py | 63 +++++++++++-------- src/oemof/solph/flows/_flow.py | 20 +++--- src/oemof/solph/flows/_investment_flow.py | 22 ++++--- 4 files changed, 81 insertions(+), 58 deletions(-) diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index 09baf0be6..f859606f8 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -1084,6 +1084,7 @@ def _init_content_multi_period_rule(block): self.init_content_multi_period.add((n, p), expr) else: pass + self.init_content_multi_period = Constraint( self.INVESTSTORAGES_INIT_CONTENT, m.PERIODS, noruleinit=True ) @@ -1093,6 +1094,7 @@ def _init_content_multi_period_rule(block): # Standard storage implementation for discrete time points else: + def _inv_storage_init_content_max_rule(block, n): """Constraint for a variable initial storage capacity.""" return ( @@ -1135,13 +1137,11 @@ def _storage_balance_first_rule(block, n): ) expr += n.fixed_losses_absolute[0] * m.timeincrement[0] expr += ( - -m.flow[i[n], n, 0, 0] - * n.inflow_conversion_factor[0] - ) * m.timeincrement[0] + -m.flow[i[n], n, 0, 0] * n.inflow_conversion_factor[0] + ) * m.timeincrement[0] expr += ( - m.flow[n, o[n], 0, 0] - / n.outflow_conversion_factor[0] - ) * m.timeincrement[0] + m.flow[n, o[n], 0, 0] / n.outflow_conversion_factor[0] + ) * m.timeincrement[0] return expr == 0 self.balance_first = Constraint( @@ -1476,15 +1476,21 @@ def _objective_expression(self): if n.investment.fixed_costs[0] is not None: lifetime = n.investment.lifetime for p in m.PERIODS: - fixed_costs += sum( - self.invest[n, p] - * n.investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range( - m.es.periods_years[p], - m.es.periods_years[p] + lifetime, + fixed_costs += ( + sum( + self.invest[n, p] + * n.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime, + ) + ) + * ( + (1 + m.discount_rate) + ** (-m.es.periods_years[p]) ) - ) * ((1 + m.discount_rate) ** (-m.es.periods_years[p])) + ) self.investment_costs = Expression(expr=investment_costs) self.period_investment_costs = Expression(expr=period_investment_costs) diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index aa8f4323f..ae4fef005 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -1303,16 +1303,19 @@ def _objective_expression(self): if g.investment.fixed_costs[0] is not None: lifetime = g.investment.lifetime for p in m.PERIODS: - fixed_costs += sum( - self.invest[g, p] - * max(g.demand) - * g.investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range( - m.es.periods_years[p], - m.es.periods_years[p] + lifetime, + fixed_costs += ( + sum( + self.invest[g, p] + * max(g.demand) + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime, + ) ) - ) * ((1 + m.discount_rate) ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) + ) self.variable_costs = Expression(expr=variable_costs) self.fixed_costs = Expression(expr=fixed_costs) @@ -2904,16 +2907,19 @@ def _objective_expression(self): if g.investment.fixed_costs[0] is not None: lifetime = g.investment.lifetime for p in m.PERIODS: - fixed_costs += sum( - self.invest[g, p] - * max(g.demand) - * g.investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range( - m.es.periods_years[p], - m.es.periods_years[p] + lifetime, + fixed_costs += ( + sum( + self.invest[g, p] + * max(g.demand) + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime, + ) ) - ) * ((1 + m.discount_rate) ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) + ) self.variable_costs = Expression(expr=variable_costs) self.fixed_costs = Expression(expr=fixed_costs) @@ -5144,16 +5150,19 @@ def _objective_expression(self): if g.investment.fixed_costs[0] is not None: lifetime = g.investment.lifetime for p in m.PERIODS: - fixed_costs += sum( - self.invest[g, p] - * max(g.demand) - * g.investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range( - m.es.periods_years[p], - m.es.periods_years[p] + lifetime, + fixed_costs += ( + sum( + self.invest[g, p] + * max(g.demand) + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime, + ) ) - ) * ((1 + m.discount_rate) ** -m.es.periods_years[p]) + * ((1 + m.discount_rate) ** -m.es.periods_years[p]) + ) self.variable_costs = Expression(expr=variable_costs) self.fixed_costs = Expression(expr=fixed_costs) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 3e10e2ddc..c5d0aed31 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -146,7 +146,7 @@ def __init__(self, **kwargs): "nonconvex", "integer", "lifetime", - "age" + "age", ] sequences = ["fix", "variable_costs", "fixed_costs", "min", "max"] dictionaries = ["positive_gradient", "negative_gradient"] @@ -562,8 +562,9 @@ def _lifetime_output_rule(block): if m.flows[inp, out].lifetime <= m.es.periods_years[p]: lhs = m.flow[inp, out, p, ts] rhs = 0 - self.lifetime_output.add((inp, out, p, ts), - (lhs == rhs)) + self.lifetime_output.add( + (inp, out, p, ts), (lhs == rhs) + ) else: pass # return Constraint.skip() @@ -581,14 +582,14 @@ def _lifetime_age_output_rule(block): for inp, out in self.LIFETIME_AGE_FLOWS: for p, ts in m.TIMEINDEX: if ( - m.flows[inp, out].lifetime - - m.flows[inp, out].age + m.flows[inp, out].lifetime - m.flows[inp, out].age <= m.es.periods_years[p] ): lhs = m.flow[inp, out, p, ts] rhs = 0 - self.lifetime_age_output.add((inp, out, p, ts), - (lhs == rhs)) + self.lifetime_age_output.add( + (inp, out, p, ts), (lhs == rhs) + ) else: pass # return Constraint.skip() @@ -659,8 +660,9 @@ def _objective_expression(self): m.flows[i, o].nominal_value * m.flows[i, o].fixed_costs[pp] * ((1 + m.discount_rate) ** (-pp)) - for pp in range(0, m.flows[i, o].lifetime - - m.flows[i, o].age) + for pp in range( + 0, m.flows[i, o].lifetime - m.flows[i, o].age + ) ) self.variable_costs = Expression(expr=variable_costs) diff --git a/src/oemof/solph/flows/_investment_flow.py b/src/oemof/solph/flows/_investment_flow.py index 02d91643c..111f9a35a 100644 --- a/src/oemof/solph/flows/_investment_flow.py +++ b/src/oemof/solph/flows/_investment_flow.py @@ -729,15 +729,21 @@ def _objective_expression(self): if m.flows[i, o].investment.fixed_costs[0] is not None: lifetime = m.flows[i, o].investment.lifetime for p in m.PERIODS: - fixed_costs += sum( - self.invest[i, o, p] - * m.flows[i, o].investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range( - m.es.periods_years[p], - m.es.periods_years[p] + lifetime, + fixed_costs += ( + sum( + self.invest[i, o, p] + * m.flows[i, o].investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime, + ) + ) + * ( + (1 + m.discount_rate) + ** (-m.es.periods_years[p]) ) - ) * ((1 + m.discount_rate) ** (-m.es.periods_years[p])) + ) for i, o in self.EXISTING_INVESTFLOWS: if m.flows[i, o].investment.fixed_costs[0] is not None: From 98e089379cd2289a3c02f9efe63d75d98da384b2 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 20 Dec 2021 22:25:18 +0100 Subject: [PATCH 0099/1363] Replace time mode parameter by more speaking parameter --- src/oemof/solph/_energy_system.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 102f95780..3d291b6b9 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -39,7 +39,11 @@ class EnergySystem(es.EnergySystem): timeincrement : iterable - timemode + infer_last_interval : bool + Add an interval to the last time point. The end time of this interval + is unknown so it does only work for an equidistant DatetimeIndex with + a 'freq' attribute that is not None. The parameter has no effect on the + timeincrement parameter. kwargs """ From 77eea67dc6a8e429c7338170897c3f98e8824554 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 20 Dec 2021 22:30:31 +0100 Subject: [PATCH 0100/1363] Add error messages --- src/oemof/solph/_energy_system.py | 17 +++++++++-------- src/oemof/solph/_models.py | 9 ++++++++- src/oemof/solph/flows/_flow.py | 11 +++++++++++ 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 3d291b6b9..dfb00a5b8 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -104,20 +104,21 @@ def __init__( ) raise AttributeError(msg) - elif timeincrement is None and timeindex is None: - msg = ( - "You have to either define the parameter timeincrement or " - "timeindex to initialise a valid EnergySystem." - ) - raise AttributeError(msg) - elif timeindex is not None and timeincrement is None: df = pd.DataFrame(timeindex) timedelta = df.diff() - timeincrement = ( + timeincrement = pd.Series( (timedelta / np.timedelta64(1, "h"))[1:].set_index(0).index ) + if timeincrement is not None and (pd.Series(timeincrement) <= 0).any(): + msg = ( + "The time increment is inconsistent. Negative values and zero " + "is not allowed.\nThis is caused by a inconsistent " + "timeincrement parameter or an incorrect timeindex." + ) + raise TypeError(msg) + super().__init__( timeindex=timeindex, timeincrement=timeincrement, **kwargs ) diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 9764b1076..0cf74b75c 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -280,12 +280,19 @@ def _add_parent_block_sets(self): # set with all nodes self.NODES = po.Set(initialize=[n for n in self.es.nodes]) + if not hasattr(self.es, "timeincrement"): + msg = ( + "The EnergySystem needs to have a valid 'timeincrement' " + "attribute to build a model." + ) + raise AttributeError(msg) + # pyomo set for timesteps of optimization problem self.TIMESTEPS = po.Set( initialize=range(len(self.es.timeincrement)), ordered=True ) self.TIMEPOINTS = po.Set( - initialize=range(len(self.es.timeincrement)+1), ordered=True + initialize=range(len(self.es.timeincrement) + 1), ordered=True ) # previous timesteps diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 936cad098..927a8cd9f 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -192,6 +192,17 @@ def __init__(self, **kwargs): if kwargs.get("max") is None: defaults["max"] = 1 + # Check gradient dictionaries for non-valid keys + for gradient_dict in ["negative_gradient", "positive_gradient"]: + if gradient_dict in kwargs: + if list(kwargs[gradient_dict].keys()) != list( + defaults[gradient_dict].keys() + ): + msg = ( + "Only the key 'ub' is allowed for the '{0}' attribute" + ) + raise AttributeError(msg.format(gradient_dict)) + for attribute in set(scalars + sequences + dictionaries + keys): value = kwargs.get(attribute, defaults.get(attribute)) if attribute in dictionaries: From 53d1f43cf38050c9e79a496ca636d5d4fbc28545 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 20 Dec 2021 22:31:55 +0100 Subject: [PATCH 0101/1363] Remove obsolete helper function Due to the new time index, the helper function is not needed anymore. Now it is possible to define a time index with n intervals and n+1 points. --- src/oemof/solph/helpers.py | 40 -------------------------------------- 1 file changed, 40 deletions(-) diff --git a/src/oemof/solph/helpers.py b/src/oemof/solph/helpers.py index f7f156c58..c43fdbc40 100644 --- a/src/oemof/solph/helpers.py +++ b/src/oemof/solph/helpers.py @@ -16,14 +16,9 @@ """ -import datetime as dt import os from collections.abc import MutableMapping -import pandas as pd - -from oemof.solph._plumbing import sequence - def get_basic_path(): """Returns the basic oemof path and creates it if necessary. @@ -67,38 +62,3 @@ def flatten(d, parent_key="", sep="_"): else: items.append((new_key, v)) return dict(items) - - -def calculate_timeincrement(timeindex, fill_value=None): - """ - Calculates timeincrement for `timeindex` - - Parameters - ---------- - timeindex: pd.DatetimeIndex - timeindex of energysystem - fill_value: numerical - timeincrement for first timestep in hours - """ - if isinstance(timeindex, pd.DatetimeIndex) and ( - fill_value - and isinstance(fill_value, pd.Timedelta) - or fill_value is None - ): - if len(set(timeindex)) != len(timeindex): - raise IndexError("No equal DatetimeIndex allowed!") - timeindex = timeindex.to_series() - timeindex_sorted = timeindex.sort_values() - if fill_value: - timeincrement = timeindex_sorted.diff().fillna(value=fill_value) - else: - timeincrement = timeindex_sorted.diff().fillna(method="bfill") - timeincrement_sec = timeincrement.map(dt.timedelta.total_seconds) - timeincrement_hourly = list(timeincrement_sec.map(lambda x: x / 3600)) - timeincrement = sequence(timeincrement_hourly) - return timeincrement - else: - raise AttributeError( - "'timeindex' must be of type 'DatetimeIndex' and " - + "'fill_value' of type 'Timedelta'." - ) From 54206c1a4b522e901d9f5ee887eab8ad8d2dd652 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 20 Dec 2021 22:32:34 +0100 Subject: [PATCH 0102/1363] Fix merging errors --- src/oemof/solph/components/experimental/_link.py | 6 +++--- src/oemof/solph/flows/_flow.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/oemof/solph/components/experimental/_link.py b/src/oemof/solph/components/experimental/_link.py index bef098558..ae984e248 100644 --- a/src/oemof/solph/components/experimental/_link.py +++ b/src/oemof/solph/components/experimental/_link.py @@ -55,9 +55,9 @@ class Link(on.Transformer): >>> link = solph.components.experimental.Link( ... label="transshipment_link", - ... inputs={bel0: solph.Flow(nominal_value=4), - ... bel1: solph.Flow(nominal_value=2)}, - ... outputs={bel0: solph.Flow(), bel1: solph.Flow()}, + ... inputs={bel0: solph.flows.Flow(nominal_value=4), + ... bel1: solph.flows.Flow(nominal_value=2)}, + ... outputs={bel0: solph.flows.Flow(), bel1: solph.flows.Flow()}, ... conversion_factors={(bel0, bel1): 0.8, (bel1, bel0): 0.9}) >>> print(sorted([x[1][5] for x in link.conversion_factors.items()])) [0.8, 0.9] diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 927a8cd9f..4515a7794 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -150,8 +150,8 @@ def __init__(self, **kwargs): dictionaries = ["positive_gradient", "negative_gradient"] defaults = { "variable_costs": 0, - "positive_gradient": {"ub": None, "costs": 0}, - "negative_gradient": {"ub": None, "costs": 0}, + "positive_gradient": {"ub": None}, + "negative_gradient": {"ub": None}, } keys = [k for k in kwargs if k != "label"] From bee6b120320fa98925f98636ad2405a918317a9d Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 20 Dec 2021 22:33:06 +0100 Subject: [PATCH 0103/1363] Change right index to left index --- src/oemof/solph/processing.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index 36713b671..4b32b7271 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -114,7 +114,7 @@ def create_dataframe(om): def divide_scalars_sequences(df_dict, k): try: - condition = df_dict[k][1:].isnull().any() + condition = df_dict[k][:-1].isnull().any() scalars = df_dict[k].loc[:, condition].dropna().iloc[0] sequences = df_dict[k].loc[:, ~condition] return {"scalars": scalars, "sequences": sequences} @@ -132,7 +132,7 @@ def set_result_index(df_dict, k, result_index): df_dict[k].index = result_index except ValueError: try: - df_dict[k] = df_dict[k][1:] + df_dict[k] = df_dict[k][:-1] df_dict[k].index = result_index except ValueError as e: msg = ( @@ -197,13 +197,12 @@ def results(model, remove_last_time_point=None): # In the implicit time mode the first time point is removed. # The values of intervals belong to the time at the end of the # interval. - result_index = result_index[1:] for k in df_dict: df_dict[k].set_index("timestep", inplace=True) df_dict[k] = df_dict[k].pivot( columns="variable_name", values="value" ) - set_result_index(df_dict, k, result_index) + set_result_index(df_dict, k, result_index[:-1]) result[k] = divide_scalars_sequences(df_dict, k) else: for k in df_dict: @@ -223,9 +222,10 @@ def results(model, remove_last_time_point=None): ) for bus, timesteps in grouped: duals = [ - model.dual[model.BusBlock.balance[bus, t]] for _, t in timesteps + model.dual[model.BusBlock.balance[bus, t]] + for _, t in timesteps ] - df = pd.DataFrame({"duals": duals}, index=model.es.timeindex) + df = pd.DataFrame({"duals": duals}, index=result_index[:-1]) if (bus, None) not in result.keys(): result[(bus, None)] = { "sequences": df, From 01c1544ea22c7b091af0c8b62cc6ea7f100f4c81 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 20 Dec 2021 22:33:34 +0100 Subject: [PATCH 0104/1363] Adapt LP-files because the name of the first time step changed --- tests/lp_files/shared_limit.lp | 50 +++++++++++++------------- tests/lp_files/source_with_gradient.lp | 6 ++-- tests/lp_files/storage.lp | 18 +++++----- tests/lp_files/storage_fixed_losses.lp | 20 +++++------ tests/lp_files/storage_invest_3.lp | 24 ++++++------- tests/lp_files/storage_invest_5.lp | 24 ++++++------- tests/lp_files/storage_unbalanced.lp | 20 +++++------ 7 files changed, 80 insertions(+), 82 deletions(-) diff --git a/tests/lp_files/shared_limit.lp b/tests/lp_files/shared_limit.lp index 5f9de12cf..c2f566bcb 100644 --- a/tests/lp_files/shared_limit.lp +++ b/tests/lp_files/shared_limit.lp @@ -1,6 +1,6 @@ \* Source Pyomo model name=Model *\ -min +min objective: +0 ONE_VAR_CONSTANT @@ -45,59 +45,59 @@ c_e_BusBlock_balance(bus_2)_: +1 flow(storage2_bus_2) = 0 -c_e_GenericStorageBlock_balance_first(storage1)_: --1 GenericStorageBlock_init_content(storage1) -+1 GenericStorageBlock_storage_content(storage1_0) +c_e_GenericStorageBlock_balance(storage1_0)_: +-1 GenericStorageBlock_storage_content(storage1_0) ++1 GenericStorageBlock_storage_content(storage1_1) -1 flow(bus_storage1_0) +1 flow(storage1_bus_0) = 0 -c_e_GenericStorageBlock_balance_first(storage2)_: --1 GenericStorageBlock_init_content(storage2) -+1 GenericStorageBlock_storage_content(storage2_0) --1 flow(bus_storage2_0) -+1 flow(storage2_bus_0) -= 0 - c_e_GenericStorageBlock_balance(storage1_1)_: --1 GenericStorageBlock_storage_content(storage1_0) -+1 GenericStorageBlock_storage_content(storage1_1) +-1 GenericStorageBlock_storage_content(storage1_1) ++1 GenericStorageBlock_storage_content(storage1_2) -1 flow(bus_storage1_1) +1 flow(storage1_bus_1) = 0 c_e_GenericStorageBlock_balance(storage1_2)_: --1 GenericStorageBlock_storage_content(storage1_1) -+1 GenericStorageBlock_storage_content(storage1_2) +-1 GenericStorageBlock_storage_content(storage1_2) ++1 GenericStorageBlock_storage_content(storage1_3) -1 flow(bus_storage1_2) +1 flow(storage1_bus_2) = 0 -c_e_GenericStorageBlock_balance(storage2_1)_: +c_e_GenericStorageBlock_balance(storage2_0)_: -1 GenericStorageBlock_storage_content(storage2_0) +1 GenericStorageBlock_storage_content(storage2_1) +-1 flow(bus_storage2_0) ++1 flow(storage2_bus_0) += 0 + +c_e_GenericStorageBlock_balance(storage2_1)_: +-1 GenericStorageBlock_storage_content(storage2_1) ++1 GenericStorageBlock_storage_content(storage2_2) -1 flow(bus_storage2_1) +1 flow(storage2_bus_1) = 0 c_e_GenericStorageBlock_balance(storage2_2)_: --1 GenericStorageBlock_storage_content(storage2_1) -+1 GenericStorageBlock_storage_content(storage2_2) +-1 GenericStorageBlock_storage_content(storage2_2) ++1 GenericStorageBlock_storage_content(storage2_3) -1 flow(bus_storage2_2) +1 flow(storage2_bus_2) = 0 c_e_GenericStorageBlock_balanced_cstr(storage1)_: --1 GenericStorageBlock_init_content(storage1) -+1 GenericStorageBlock_storage_content(storage1_2) +-1 GenericStorageBlock_storage_content(storage1_0) ++1 GenericStorageBlock_storage_content(storage1_3) = 0 c_e_GenericStorageBlock_balanced_cstr(storage2)_: --1 GenericStorageBlock_init_content(storage2) -+1 GenericStorageBlock_storage_content(storage2_2) +-1 GenericStorageBlock_storage_content(storage2_0) ++1 GenericStorageBlock_storage_content(storage2_3) = 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds @@ -119,9 +119,9 @@ bounds 0 <= GenericStorageBlock_storage_content(storage1_0) <= 5 0 <= GenericStorageBlock_storage_content(storage1_1) <= 5 0 <= GenericStorageBlock_storage_content(storage1_2) <= 5 + 0 <= GenericStorageBlock_storage_content(storage1_3) <= 5 0 <= GenericStorageBlock_storage_content(storage2_0) <= 5 0 <= GenericStorageBlock_storage_content(storage2_1) <= 5 0 <= GenericStorageBlock_storage_content(storage2_2) <= 5 - 0 <= GenericStorageBlock_init_content(storage1) <= 5 - 0 <= GenericStorageBlock_init_content(storage2) <= 5 + 0 <= GenericStorageBlock_storage_content(storage2_3) <= 5 end diff --git a/tests/lp_files/source_with_gradient.lp b/tests/lp_files/source_with_gradient.lp index 487c7256f..19da7bb22 100644 --- a/tests/lp_files/source_with_gradient.lp +++ b/tests/lp_files/source_with_gradient.lp @@ -1,6 +1,6 @@ \* Source Pyomo model name=Model *\ -min +min objective: +23 flow(powerplant_electricityBus_0) +23 flow(powerplant_electricityBus_1) @@ -44,17 +44,15 @@ c_u_FlowBlock_negative_gradient_constr(powerplant_electricityBus_2)_: -1 flow(powerplant_electricityBus_2) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds 0 <= flow(powerplant_electricityBus_0) <= 999 0 <= flow(powerplant_electricityBus_1) <= 999 0 <= flow(powerplant_electricityBus_2) <= 999 - -inf <= FlowBlock_positive_gradient(powerplant_electricityBus_0) <= 29.969999999999999 -inf <= FlowBlock_positive_gradient(powerplant_electricityBus_1) <= 29.969999999999999 -inf <= FlowBlock_positive_gradient(powerplant_electricityBus_2) <= 29.969999999999999 - -inf <= FlowBlock_negative_gradient(powerplant_electricityBus_0) <= 49.950000000000003 -inf <= FlowBlock_negative_gradient(powerplant_electricityBus_1) <= 49.950000000000003 -inf <= FlowBlock_negative_gradient(powerplant_electricityBus_2) <= 49.950000000000003 end diff --git a/tests/lp_files/storage.lp b/tests/lp_files/storage.lp index 8851e24b5..934a7ba24 100644 --- a/tests/lp_files/storage.lp +++ b/tests/lp_files/storage.lp @@ -26,31 +26,31 @@ c_e_BusBlock_balance(electricityBus_2)_: +1 flow(storage_no_invest_electricityBus_2) = 0 -c_e_GenericStorageBlock_balance_first(storage_no_invest)_: -+1 GenericStorageBlock_storage_content(storage_no_invest_0) +c_e_GenericStorageBlock_balance(storage_no_invest_0)_: ++1 GenericStorageBlock_storage_content(storage_no_invest_1) -0.96999999999999997 flow(electricityBus_storage_no_invest_0) +1.1627906976744187 flow(storage_no_invest_electricityBus_0) = 34800 c_e_GenericStorageBlock_balance(storage_no_invest_1)_: --0.87 GenericStorageBlock_storage_content(storage_no_invest_0) -+1 GenericStorageBlock_storage_content(storage_no_invest_1) +-0.87 GenericStorageBlock_storage_content(storage_no_invest_1) ++1 GenericStorageBlock_storage_content(storage_no_invest_2) -0.96999999999999997 flow(electricityBus_storage_no_invest_1) +1.1627906976744187 flow(storage_no_invest_electricityBus_1) = 0 c_e_GenericStorageBlock_balance(storage_no_invest_2)_: --0.87 GenericStorageBlock_storage_content(storage_no_invest_1) -+1 GenericStorageBlock_storage_content(storage_no_invest_2) +-0.87 GenericStorageBlock_storage_content(storage_no_invest_2) ++1 GenericStorageBlock_storage_content(storage_no_invest_3) -0.96999999999999997 flow(electricityBus_storage_no_invest_2) +1.1627906976744187 flow(storage_no_invest_electricityBus_2) = 0 c_e_GenericStorageBlock_balanced_cstr(storage_no_invest)_: -+1 GenericStorageBlock_storage_content(storage_no_invest_2) ++1 GenericStorageBlock_storage_content(storage_no_invest_3) = 40000 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds @@ -60,7 +60,7 @@ bounds 0 <= flow(storage_no_invest_electricityBus_0) <= 16667 0 <= flow(storage_no_invest_electricityBus_1) <= 16667 0 <= flow(storage_no_invest_electricityBus_2) <= 16667 - 0 <= GenericStorageBlock_storage_content(storage_no_invest_0) <= 100000 0 <= GenericStorageBlock_storage_content(storage_no_invest_1) <= 100000 0 <= GenericStorageBlock_storage_content(storage_no_invest_2) <= 100000 + 0 <= GenericStorageBlock_storage_content(storage_no_invest_3) <= 100000 end diff --git a/tests/lp_files/storage_fixed_losses.lp b/tests/lp_files/storage_fixed_losses.lp index 324b01255..2c6e264ad 100644 --- a/tests/lp_files/storage_fixed_losses.lp +++ b/tests/lp_files/storage_fixed_losses.lp @@ -1,6 +1,6 @@ \* Source Pyomo model name=Model *\ -min +min objective: +56 flow(electricityBus_storage_no_invest_0) +56 flow(electricityBus_storage_no_invest_1) @@ -26,31 +26,31 @@ c_e_BusBlock_balance(electricityBus_2)_: +1 flow(storage_no_invest_electricityBus_2) = 0 -c_e_GenericStorageBlock_balance_first(storage_no_invest)_: -+1 GenericStorageBlock_storage_content(storage_no_invest_0) +c_e_GenericStorageBlock_balance(storage_no_invest_0)_: ++1 GenericStorageBlock_storage_content(storage_no_invest_1) -0.96999999999999997 flow(electricityBus_storage_no_invest_0) +1.1627906976744187 flow(storage_no_invest_electricityBus_0) = 33797 c_e_GenericStorageBlock_balance(storage_no_invest_1)_: --0.87 GenericStorageBlock_storage_content(storage_no_invest_0) -+1 GenericStorageBlock_storage_content(storage_no_invest_1) +-0.87 GenericStorageBlock_storage_content(storage_no_invest_1) ++1 GenericStorageBlock_storage_content(storage_no_invest_2) -0.96999999999999997 flow(electricityBus_storage_no_invest_1) +1.1627906976744187 flow(storage_no_invest_electricityBus_1) = -1003 c_e_GenericStorageBlock_balance(storage_no_invest_2)_: --0.87 GenericStorageBlock_storage_content(storage_no_invest_1) -+1 GenericStorageBlock_storage_content(storage_no_invest_2) +-0.87 GenericStorageBlock_storage_content(storage_no_invest_2) ++1 GenericStorageBlock_storage_content(storage_no_invest_3) -0.96999999999999997 flow(electricityBus_storage_no_invest_2) +1.1627906976744187 flow(storage_no_invest_electricityBus_2) = -1003 c_e_GenericStorageBlock_balanced_cstr(storage_no_invest)_: -+1 GenericStorageBlock_storage_content(storage_no_invest_2) ++1 GenericStorageBlock_storage_content(storage_no_invest_3) = 40000 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds @@ -60,7 +60,7 @@ bounds 0 <= flow(storage_no_invest_electricityBus_0) <= 16667 0 <= flow(storage_no_invest_electricityBus_1) <= 16667 0 <= flow(storage_no_invest_electricityBus_2) <= 16667 - 0 <= GenericStorageBlock_storage_content(storage_no_invest_0) <= 100000 0 <= GenericStorageBlock_storage_content(storage_no_invest_1) <= 100000 0 <= GenericStorageBlock_storage_content(storage_no_invest_2) <= 100000 + 0 <= GenericStorageBlock_storage_content(storage_no_invest_3) <= 100000 end diff --git a/tests/lp_files/storage_invest_3.lp b/tests/lp_files/storage_invest_3.lp index 3e39dfb83..b5924abd3 100644 --- a/tests/lp_files/storage_invest_3.lp +++ b/tests/lp_files/storage_invest_3.lp @@ -1,6 +1,6 @@ \* Source Pyomo model name=Model *\ -min +min objective: +99 InvestmentFlowBlock_invest(electricityBus_storage3) +9 InvestmentFlowBlock_invest(storage3_electricityBus) @@ -52,33 +52,33 @@ c_u_InvestmentFlowBlock_max(storage3_electricityBus_2)_: +1 flow(storage3_electricityBus_2) <= 0 -c_e_GenericStorageBlock_balance_first(storage3)_: --1 GenericStorageBlock_init_content(storage3) -+1 GenericStorageBlock_storage_content(storage3_0) +c_e_GenericStorageBlock_balance(storage3_0)_: +-1 GenericStorageBlock_storage_content(storage3_0) ++1 GenericStorageBlock_storage_content(storage3_1) -1 flow(electricityBus_storage3_0) +1 flow(storage3_electricityBus_0) = 0 c_e_GenericStorageBlock_balance(storage3_1)_: --1 GenericStorageBlock_storage_content(storage3_0) -+1 GenericStorageBlock_storage_content(storage3_1) +-1 GenericStorageBlock_storage_content(storage3_1) ++1 GenericStorageBlock_storage_content(storage3_2) -1 flow(electricityBus_storage3_1) +1 flow(storage3_electricityBus_1) = 0 c_e_GenericStorageBlock_balance(storage3_2)_: --1 GenericStorageBlock_storage_content(storage3_1) -+1 GenericStorageBlock_storage_content(storage3_2) +-1 GenericStorageBlock_storage_content(storage3_2) ++1 GenericStorageBlock_storage_content(storage3_3) -1 flow(electricityBus_storage3_2) +1 flow(storage3_electricityBus_2) = 0 c_e_GenericStorageBlock_balanced_cstr(storage3)_: --1 GenericStorageBlock_init_content(storage3) -+1 GenericStorageBlock_storage_content(storage3_2) +-1 GenericStorageBlock_storage_content(storage3_0) ++1 GenericStorageBlock_storage_content(storage3_3) = 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds @@ -93,5 +93,5 @@ bounds 0 <= GenericStorageBlock_storage_content(storage3_0) <= 5000 0 <= GenericStorageBlock_storage_content(storage3_1) <= 5000 0 <= GenericStorageBlock_storage_content(storage3_2) <= 5000 - 0 <= GenericStorageBlock_init_content(storage3) <= 5000 + 0 <= GenericStorageBlock_storage_content(storage3_3) <= 5000 end diff --git a/tests/lp_files/storage_invest_5.lp b/tests/lp_files/storage_invest_5.lp index a71910b5d..b3c8fd449 100644 --- a/tests/lp_files/storage_invest_5.lp +++ b/tests/lp_files/storage_invest_5.lp @@ -1,6 +1,6 @@ \* Source Pyomo model name=Model *\ -min +min objective: +99 InvestmentFlowBlock_invest(electricityBus_storage5) @@ -51,30 +51,30 @@ c_u_InvestmentFlowBlock_max(storage5_electricityBus_2)_: +1 flow(storage5_electricityBus_2) <= 100 -c_e_GenericStorageBlock_balance_first(storage5)_: --1 GenericStorageBlock_init_content(storage5) -+1 GenericStorageBlock_storage_content(storage5_0) +c_e_GenericStorageBlock_balance(storage5_0)_: +-1 GenericStorageBlock_storage_content(storage5_0) ++1 GenericStorageBlock_storage_content(storage5_1) -1 flow(electricityBus_storage5_0) +1 flow(storage5_electricityBus_0) = 0 c_e_GenericStorageBlock_balance(storage5_1)_: --1 GenericStorageBlock_storage_content(storage5_0) -+1 GenericStorageBlock_storage_content(storage5_1) +-1 GenericStorageBlock_storage_content(storage5_1) ++1 GenericStorageBlock_storage_content(storage5_2) -1 flow(electricityBus_storage5_1) +1 flow(storage5_electricityBus_1) = 0 c_e_GenericStorageBlock_balance(storage5_2)_: --1 GenericStorageBlock_storage_content(storage5_1) -+1 GenericStorageBlock_storage_content(storage5_2) +-1 GenericStorageBlock_storage_content(storage5_2) ++1 GenericStorageBlock_storage_content(storage5_3) -1 flow(electricityBus_storage5_2) +1 flow(storage5_electricityBus_2) = 0 c_e_GenericStorageBlock_balanced_cstr(storage5)_: --1 GenericStorageBlock_init_content(storage5) -+1 GenericStorageBlock_storage_content(storage5_2) +-1 GenericStorageBlock_storage_content(storage5_0) ++1 GenericStorageBlock_storage_content(storage5_3) = 0 c_e_GenericStorageBlock_power_coupled(storage5)_: @@ -82,7 +82,7 @@ c_e_GenericStorageBlock_power_coupled(storage5)_: +1.1000000000000001 InvestmentFlowBlock_invest(storage5_electricityBus) = -1.4210854715202004e-14 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds @@ -97,5 +97,5 @@ bounds 0 <= GenericStorageBlock_storage_content(storage5_0) <= 10000 0 <= GenericStorageBlock_storage_content(storage5_1) <= 10000 0 <= GenericStorageBlock_storage_content(storage5_2) <= 10000 - 0 <= GenericStorageBlock_init_content(storage5) <= 10000 + 0 <= GenericStorageBlock_storage_content(storage5_3) <= 10000 end diff --git a/tests/lp_files/storage_unbalanced.lp b/tests/lp_files/storage_unbalanced.lp index 833504861..bc5a87cd0 100644 --- a/tests/lp_files/storage_unbalanced.lp +++ b/tests/lp_files/storage_unbalanced.lp @@ -1,6 +1,6 @@ \* Source Pyomo model name=Model *\ -min +min objective: +0 ONE_VAR_CONSTANT @@ -21,28 +21,28 @@ c_e_BusBlock_balance(electricityBus_2)_: +1 flow(storage1_electricityBus_2) = 0 -c_e_GenericStorageBlock_balance_first(storage1)_: --1 GenericStorageBlock_init_content(storage1) -+1 GenericStorageBlock_storage_content(storage1_0) +c_e_GenericStorageBlock_balance(storage1_0)_: +-1 GenericStorageBlock_storage_content(storage1_0) ++1 GenericStorageBlock_storage_content(storage1_1) -1 flow(electricityBus_storage1_0) +1 flow(storage1_electricityBus_0) = 0 c_e_GenericStorageBlock_balance(storage1_1)_: --1 GenericStorageBlock_storage_content(storage1_0) -+1 GenericStorageBlock_storage_content(storage1_1) +-1 GenericStorageBlock_storage_content(storage1_1) ++1 GenericStorageBlock_storage_content(storage1_2) -1 flow(electricityBus_storage1_1) +1 flow(storage1_electricityBus_1) = 0 c_e_GenericStorageBlock_balance(storage1_2)_: --1 GenericStorageBlock_storage_content(storage1_1) -+1 GenericStorageBlock_storage_content(storage1_2) +-1 GenericStorageBlock_storage_content(storage1_2) ++1 GenericStorageBlock_storage_content(storage1_3) -1 flow(electricityBus_storage1_2) +1 flow(storage1_electricityBus_2) = 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds @@ -55,5 +55,5 @@ bounds 0 <= GenericStorageBlock_storage_content(storage1_0) <= 1111 0 <= GenericStorageBlock_storage_content(storage1_1) <= 1111 0 <= GenericStorageBlock_storage_content(storage1_2) <= 1111 - 0 <= GenericStorageBlock_init_content(storage1) <= 1111 + 0 <= GenericStorageBlock_storage_content(storage1_3) <= 1111 end From fd8c1c69093712ebea4d3656bc4f958fcb21013f Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 20 Dec 2021 22:33:55 +0100 Subject: [PATCH 0105/1363] Revise and add tests for the new indexes --- tests/test_models.py | 74 ----- tests/test_non_equidistant_time_index.py | 259 ++++++++++++++++++ ...t_simple_dispatch_one_explicit_timemode.py | 124 +++++++++ tests/test_time_index.py | 144 ++++++++++ 4 files changed, 527 insertions(+), 74 deletions(-) create mode 100644 tests/test_non_equidistant_time_index.py create mode 100644 tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one_explicit_timemode.py create mode 100644 tests/test_time_index.py diff --git a/tests/test_models.py b/tests/test_models.py index d7c519ac2..b6919fbc2 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -11,83 +11,9 @@ import warnings -import pandas as pd import pytest from oemof import solph -from oemof.solph.helpers import calculate_timeincrement - - -def test_timeincrement_with_valid_timeindex(): - datetimeindex = pd.date_range("1/1/2012", periods=1, freq="H") - es = solph.EnergySystem(timeindex=datetimeindex) - m = solph._models.BaseModel(es) - assert m.timeincrement[0] == 1 - assert es.timeindex.freq.nanos / 3.6e12 == 1 - - -def test_timeincrement_with_non_valid_timeindex(): - with pytest.raises(AttributeError): - es = solph.EnergySystem(timeindex=4) - solph._models.BaseModel(es) - - -def test_timeincrement_value(): - es = solph.EnergySystem(timeindex=4) - m = solph._models.BaseModel(es, timeincrement=3) - assert m.timeincrement[0] == 3 - - -def test_timeincrement_list(): - es = solph.EnergySystem(timeindex=4) - m = solph._models.BaseModel(es, timeincrement=[0, 1, 2, 3]) - assert m.timeincrement[3] == 3 - - -def test_nonequ_timeincrement(): - timeindex_hourly = pd.date_range("1/1/2019", periods=2, freq="H") - timeindex_30mins = pd.date_range( - "1/1/2019 03:00:00", periods=2, freq="30min" - ) - timeindex_2h = pd.date_range("1/1/2019 04:00:00", periods=2, freq="2H") - timeindex = timeindex_hourly.append([timeindex_30mins, timeindex_2h]) - timeincrement = calculate_timeincrement(timeindex=timeindex) - assert timeincrement == solph.sequence([1.0, 1.0, 2.0, 0.5, 0.5, 2.0]) - - -def test_nonequ_timeincrement_fill(): - timeindex_hourly = pd.date_range("1/1/2019", periods=2, freq="H") - timeindex_30mins = pd.date_range( - "1/1/2019 03:00:00", periods=2, freq="30min" - ) - timeindex_2h = pd.date_range("1/1/2019 04:00:00", periods=2, freq="2H") - timeindex = timeindex_hourly.append([timeindex_30mins, timeindex_2h]) - fvalue = pd.Timedelta(hours=9) - timeincrement = calculate_timeincrement( - timeindex=timeindex, fill_value=fvalue - ) - assert timeincrement == solph.sequence([9.0, 1.0, 2.0, 0.5, 0.5, 2.0]) - - -def test_nonequ_duplicate_timeindex(): - with pytest.raises(IndexError): - timeindex_hourly = pd.date_range("1/1/2019", periods=2, freq="H") - timeindex_45mins = pd.date_range("1/1/2019", periods=2, freq="45min") - timeindex = timeindex_hourly.append([timeindex_45mins]) - calculate_timeincrement(timeindex=timeindex) - - -def test_nonequ_with_non_valid_timeindex(): - with pytest.raises(AttributeError): - timeindex = [5, 4] - calculate_timeincrement(timeindex=timeindex) - - -def test_nonequ_with_non_valid_fill(): - with pytest.raises(AttributeError): - timeindex = pd.date_range("1/1/2019", periods=2, freq="H") - fill_value = 2 - calculate_timeincrement(timeindex=timeindex, fill_value=fill_value) def test_optimal_solution(): diff --git a/tests/test_non_equidistant_time_index.py b/tests/test_non_equidistant_time_index.py new file mode 100644 index 000000000..8873a7f14 --- /dev/null +++ b/tests/test_non_equidistant_time_index.py @@ -0,0 +1,259 @@ +# -*- coding: utf-8 - + +"""Test the definition of the time index of the model. + +SPDX-FileCopyrightText: Uwe Krien + +SPDX-License-Identifier: MIT +""" +import copy +import datetime +import random + +import pandas as pd + +from oemof.solph import EnergySystem +from oemof.solph import Investment +from oemof.solph import Model +from oemof.solph import buses +from oemof.solph import components as cmp +from oemof.solph import flows +from oemof.solph import processing + + +class TestParameterResult: + @classmethod + def setup_class(cls): + dtindex1 = pd.date_range("1/1/2012", periods=24, freq="H") + dtindex2 = pd.date_range("1/2/2012", periods=49, freq="30min") + dtindex = dtindex1.union(dtindex2) + es = EnergySystem(timeindex=dtindex, infer_last_interval=False) + + # BUSSES + b_el1 = buses.Bus(label="b_el1") + b_diesel = buses.Bus(label="b_diesel", balanced=False) + es.add(b_el1, b_diesel) + + # TEST DIESEL: + dg = cmp.Transformer( + label="diesel_generator", + inputs={b_diesel: flows.Flow(variable_costs=2)}, + outputs={ + b_el1: flows.Flow( + variable_costs=1, investment=Investment(ep_costs=500) + ) + }, + conversion_factors={b_el1: 0.5}, + ) + + batt = cmp.GenericStorage( + label="storage", + nominal_storage_capacity=1000, + inputs={b_el1: flows.Flow(variable_costs=3)}, + outputs={b_el1: flows.Flow(variable_costs=2.5)}, + loss_rate=0.00, + invest_relation_input_capacity=1 / 6, + invest_relation_output_capacity=1 / 6, + inflow_conversion_factor=1, + outflow_conversion_factor=0.9, + ) + + random.seed(1) + demand_values = random.sample(range(40, 120), 72) + demand = cmp.Sink( + label="demand_el", + inputs={ + b_el1: flows.Flow( + nominal_value=1, + fix=demand_values, + ) + }, + ) + es.add(dg, batt, demand) + model = Model(es) + model.receive_duals() + model.solve() + results = processing.results(model, remove_last_time_point=False) + cls.flows = {k: v for k, v in results.items() if k[1] is not None} + cls.comp = {k: v for k, v in results.items() if k[1] is None} + cls.es = es + cls.model = model + + def test_timesteps_timeincrements_with_storage_charging(self): + storage_content = [ + v["sequences"]["storage_content"] + for k, v in self.comp.items() + if k[0].label == "storage" + ][0] + assert storage_content[0] == storage_content[-1] + + charge = [ + v["sequences"]["flow"] + for k, v in self.flows.items() + if k[1].label == "storage" + ][0] + # Calculate the next storage content and verify it with the storage + # content of the results (charging). + # Charging - timestep (ts) with its timeincrement (ti) + time = [(23, 1), (24, 0.5)] + for ts, ti in time: + assert round(storage_content[ts] + charge[ts] * ti, 5) == round( + storage_content[ts + 1], 5 + ) + assert self.es.timeincrement[ts] == ti + assert charge.isnull().any() + + def test_timesteps_timeincrements_with_storage_discharging(self): + storage_content = [ + v["sequences"]["storage_content"] + for k, v in self.comp.items() + if k[0].label == "storage" + ][0] + # Storage content at the last time point is equal to the content of + # the first time point because the storage is balanced. + assert storage_content[0] == storage_content[-1] + + discharge = [ + v["sequences"]["flow"] + for k, v in self.flows.items() + if k[0].label == "storage" + ][0] + + # Calculate the next storage content and verify it with the storage + # content of the results (discharging). + # Discharging - timestep (ts) with its timeincrement (ti) + time = [(7, 1), (40, 0.5)] + for ts, ti in time: + assert ( + round( + storage_content[ts] + - (discharge[ts] + (discharge[ts] * 1 / 9)) * ti, + 5, + ) + == round(storage_content[ts + 1], 5) + ) + assert self.es.timeincrement[ts] == ti + + def test_timeincrements(self): + assert self.es.timeincrement.sum() == 48 + + def test_without_last_time_point(self): + results = processing.results(self.model, remove_last_time_point=True) + flow = {k: v for k, v in results.items() if k[1] is not None} + comp = {k: v for k, v in results.items() if k[1] is None} + storage_content = [ + v["sequences"]["storage_content"] + for k, v in comp.items() + if k[0].label == "storage" + ][0] + charge = [ + v["sequences"]["flow"] + for k, v in flow.items() + if k[1].label == "storage" + ][0] + # The first and the last value are not the same because the last value + # of the storage is missing. Adding the charging of the last time step + # will result the final storage content, which is equal to the first + assert storage_content[0] != storage_content[-1] + assert storage_content[0] == storage_content[-1] + charge[-1] / 2 + assert not charge.isnull().any() + assert storage_content.index[0] == datetime.datetime( + 2012, 1, 1, 0, 0, 0 + ) + assert charge.index[0] == datetime.datetime(2012, 1, 1, 0, 0, 0) + assert storage_content.index[-1] == datetime.datetime( + 2012, 1, 2, 23, 30, 0 + ) + assert charge.index[-1] == datetime.datetime(2012, 1, 2, 23, 30, 0) + + def test_time_index_with_last_time_point(self): + storage_content = [ + v["sequences"]["storage_content"] + for k, v in self.comp.items() + if k[0].label == "storage" + ][0] + assert storage_content[0] == storage_content[-1] + + charge = [ + v["sequences"]["flow"] + for k, v in self.flows.items() + if k[1].label == "storage" + ][0] + assert storage_content.index[0] == datetime.datetime( + 2012, 1, 1, 0, 0, 0 + ) + assert charge.index[0] == datetime.datetime(2012, 1, 1, 0, 0, 0) + assert storage_content.index[-1] == datetime.datetime( + 2012, 1, 3, 0, 0, 0 + ) + assert charge.index[-1] == datetime.datetime(2012, 1, 3, 0, 0, 0) + + def test_numeric_index(self): + self.es.timeindex = None + model = Model(self.es) + model.receive_duals() + model.solve() + results = processing.results(self.model) + flow = {k: v for k, v in results.items() if k[1] is not None} + diesel_generator_out = [ + v["sequences"]["flow"] + for k, v in flow.items() + if k[0].label == "diesel_generator" + ][0] + assert diesel_generator_out.index[0] == 0 + assert diesel_generator_out.index[-1] == 72 + assert len(diesel_generator_out.index) == 73 + + storage_content = [ + v["sequences"]["storage_content"] + for k, v in self.comp.items() + if k[0].label == "storage" + ][0] + assert storage_content[0] == storage_content[-1] + + charge = [ + v["sequences"]["flow"] + for k, v in self.flows.items() + if k[1].label == "storage" + ][0] + # Calculate the next storage content and verify it with the storage + # content of the results (charging). + # Charging - timestep (ts) with its timeincrement (ti) + time = [(23, 1), (24, 0.5)] + for ts, ti in time: + assert round(storage_content[ts] + charge[ts] * ti, 5) == round( + storage_content[ts + 1], 5 + ) + assert self.es.timeincrement[ts] == ti + assert charge.isnull().any() + + def test_default_value_explicit(self): + """ + In the explicit mode the default value for the processing is to not + remove the last value and allow nan-values. The explicit mode is + triggered by setting `infer_last_interval` to `False`. + """ + assert self.es.timemode == "explicit" + model = Model(self.es) + model.receive_duals() + model.solve() + results = processing.results(model) + flow = {k: v for k, v in results.items() if k[1] is not None} + assert 73 == len([v["sequences"]["flow"] for k, v in flow.items()][0]) + + def test_default_value_implicit(self): + """ + In the implicit mode the default value for the processing is to remove + the last value and allow nan-values. + """ + assert self.es.timemode == "explicit" + test_es = EnergySystem() + assert test_es.timemode == "implicit" + my_es = copy.copy(self.es) + my_es.timemode = test_es.timemode + model = Model(my_es) + model.receive_duals() + model.solve() + results = processing.results(model) + flow = {k: v for k, v in results.items() if k[1] is not None} + assert 72 == len([v["sequences"]["flow"] for k, v in flow.items()][0]) diff --git a/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one_explicit_timemode.py b/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one_explicit_timemode.py new file mode 100644 index 000000000..6bd422468 --- /dev/null +++ b/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one_explicit_timemode.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- + +""" This example shows how to create an energysystem with oemof objects and +solve it with the solph module. + +This file is part of project oemof (github.com/oemof/oemof). It's copyrighted +by the contributors recorded in the version control history of the file, +available from its original location +oemof/tests/test_scripts/test_solph/test_simple_dispatch/test_simple_dispatch.py + +SPDX-License-Identifier: MIT +""" + +from nose.tools import eq_ +from oemof.network.network import Node + +from oemof.solph import EnergySystem +from oemof.solph import Model +from oemof.solph import processing +from oemof.solph import views +from oemof.solph.buses import Bus +from oemof.solph.components import Sink +from oemof.solph.components import Source +from oemof.solph.components import Transformer +from oemof.solph.flows import Flow + + +def test_dispatch_one_time_step(solver="cbc"): + """Create an energy system and optimize the dispatch at least costs.""" + + # ######################### create energysystem components ################ + Node.registry = None + + # resource buses + bgas = Bus(label="gas", balanced=False) + + # electricity and heat + bel = Bus(label="b_el") + bth = Bus(label="b_th") + + # an excess and a shortage variable can help to avoid infeasible problems + excess_el = Sink(label="excess_el", inputs={bel: Flow()}) + + # sources + wind = Source( + label="wind", outputs={bel: Flow(fix=0.5, nominal_value=66.3)} + ) + + # demands (electricity/heat) + demand_el = Sink( + label="demand_elec", inputs={bel: Flow(nominal_value=85, fix=0.3)} + ) + + demand_th = Sink( + label="demand_therm", inputs={bth: Flow(nominal_value=40, fix=0.2)} + ) + + # combined heat and power plant (chp) + pp_chp = Transformer( + label="pp_chp", + inputs={bgas: Flow()}, + outputs={ + bel: Flow(nominal_value=30, variable_costs=42), + bth: Flow(nominal_value=40), + }, + conversion_factors={bel: 0.3, bth: 0.4}, + ) + + # heatpump with a coefficient of performance (COP) of 3 + b_heat_source = Bus(label="b_heat_source") + + heat_source = Source(label="heat_source", outputs={b_heat_source: Flow()}) + + cop = 3 + heat_pump = Transformer( + label="heat_pump", + inputs={bel: Flow(), b_heat_source: Flow()}, + outputs={bth: Flow(nominal_value=10)}, + conversion_factors={bel: 1 / 3, b_heat_source: (cop - 1) / cop}, + ) + + energysystem = EnergySystem(timeincrement=[1], timemode="explicit") + energysystem.add( + bgas, + bel, + bth, + excess_el, + wind, + demand_el, + demand_th, + pp_chp, + b_heat_source, + heat_source, + heat_pump, + ) + + # ################################ optimization ########################### + + # create optimization model based on energy_system + optimization_model = Model(energysystem=energysystem) + + # solve problem + optimization_model.solve(solver=solver) + + # write back results from optimization object to energysystem + optimization_model.results() + + # ################################ results ################################ + data = views.node(processing.results(model=optimization_model), "b_el") + + # generate results to be evaluated in tests + results = data["sequences"].sum(axis=0).to_dict() + + print("DateTimeIndex:", data["sequences"].index) + + test_results = { + (("wind", "b_el"), "flow"): 33, + (("b_el", "demand_elec"), "flow"): 26, + (("b_el", "excess_el"), "flow"): 5, + (("b_el", "heat_pump"), "flow"): 3, + } + + for key in test_results.keys(): + eq_(int(round(results[key])), int(round(test_results[key]))) diff --git a/tests/test_time_index.py b/tests/test_time_index.py new file mode 100644 index 000000000..7d185d082 --- /dev/null +++ b/tests/test_time_index.py @@ -0,0 +1,144 @@ +# -*- coding: utf-8 - + +"""Test the definition of the time index of the model. + +SPDX-FileCopyrightText: Uwe Krien +SPDX-FileCopyrightText: Stephan Günther + +SPDX-License-Identifier: MIT +""" + +import pandas as pd +import pytest + +from oemof import solph + + +def test_energysystem_with_datetimeindex_infer_last_interval(): + """Test EnergySystem with DatetimeIndex (equidistant)""" + datetimeindex = pd.date_range("1/1/2012", periods=24, freq="H") + es = solph.EnergySystem(timeindex=datetimeindex) + assert es.timeincrement[1] == 1.0 + assert es.timeincrement.sum() == 24 + + +def test_energysystem_with_datetimeindex(): + datetimeindex = pd.date_range("1/1/2012", periods=24, freq="H") + es = solph.EnergySystem(timeindex=datetimeindex, infer_last_interval=False) + assert es.timeincrement[1] == 1.0 + assert es.timeincrement.sum() == 23 + + +def test_energysystem_with_datetimeindex_non_equidistant_infer_last_interval(): + """Test EnergySystem with DatetimeIndex (non-equidistant)""" + dtindex1 = pd.date_range("1/1/2012", periods=24, freq="H") + dtindex2 = pd.date_range("1/2/2012", periods=49, freq="30min") + dtindex = dtindex1.union(dtindex2) + msg = ( + "You cannot infer the last interval if the 'freq' attribute of your " + "DatetimeIndex is None." + ) + with pytest.raises(AttributeError, match=msg): + solph.EnergySystem(timeindex=dtindex) + + +def test_energysystem_with_datetimeindex_non_equidistant(): + """Test EnergySystem with DatetimeIndex (non-equidistant)""" + dtindex1 = pd.date_range("1/1/2012", periods=24, freq="H") + dtindex2 = pd.date_range("1/2/2012", periods=49, freq="30min") + dtindex = dtindex1.union(dtindex2) + es = solph.EnergySystem(timeindex=dtindex, infer_last_interval=False) + assert es.timeincrement.sum() == 48.0 + assert es.timeincrement[0] == 1 + assert es.timeincrement[25] == 0.5 + + +def test_energysystem_with_numeric_index_infer_last_interval(): + """Test EnergySystem with numeric index (equidistant)""" + time_increments = [1, 1, 1, 1, 1] + es = solph.EnergySystem(timeincrement=time_increments) + assert es.timeincrement[1] == 1.0 + assert pd.Series(es.timeincrement).sum() == 5 + + +def test_energysystem_with_numeric_index(): + """Test EnergySystem with numeric index (equidistant)""" + time_increments = [1, 1, 1, 1, 1] + es = solph.EnergySystem( + timeincrement=time_increments, infer_last_interval=False + ) + assert es.timeincrement[1] == 1.0 + assert pd.Series(es.timeincrement).sum() == 5 + + +def test_energysystem_with_numeric_index_non_equidistant_infer_last_interval(): + """ + Test EnergySystem with DatetimeIndex (non-equidistant) + 'infer_last_interval=True/False' does not have any effect. + """ + time_increments = [1, 1, 1, 1, 1, 0.5, 0.5, 0.25, 0.25, 0.5] + + es = solph.EnergySystem( + timeincrement=time_increments, infer_last_interval=True + ) + assert pd.Series(es.timeincrement).sum() == 7.0 + assert es.timeincrement[0] == 1 + assert es.timeincrement[6] == 0.5 + + +def test_energysystem_with_numeric_index_non_equidistant(): + """ + Test EnergySystem with DatetimeIndex (non-equidistant) + 'infer_last_interval=True/False' does not have any effect. + """ + time_increments = [1, 1, 1, 1, 1, 0.5, 0.5, 0.25, 0.25, 0.5] + es = solph.EnergySystem( + timeincrement=time_increments, infer_last_interval=False + ) + assert pd.Series(es.timeincrement).sum() == 7.0 + assert es.timeincrement[0] == 1 + assert es.timeincrement[8] == 0.25 + + +def test_model_timeincrement_with_valid_timeindex(): + datetimeindex = pd.date_range("1/1/2012", periods=5, freq="H") + es = solph.EnergySystem(timeindex=datetimeindex) + m = solph._models.BaseModel(es) + assert es.timeincrement.sum() == 5 + assert m.timeincrement.sum() == 5 + assert m.timeincrement[2] == 1 + + +def test_timeincrement_with_non_valid_timeindex(): + with pytest.raises( + TypeError, match="Parameter 'timeindex' has to be of type" + ): + solph.EnergySystem(timeindex=4) + + +def test_overwrite_timeincrement(): + es = solph.EnergySystem( + timeindex=pd.date_range("1/1/2012", periods=2, freq="H") + ) + assert es.timeincrement[0] == 1 + m = solph._models.BaseModel(es, timeincrement=[3]) + assert m.timeincrement[0] == 3 + + +def test_model_timeincrement_list(): + es = solph.EnergySystem() + m = solph._models.BaseModel(es, timeincrement=[0, 1, 2, 3]) + assert m.timeincrement[3] == 3 + + +def test_nonequ_inconsistent_timeindex(): + # with pytest.raises(IndexError): + timeindex_one = pd.date_range("1/1/2019", periods=1, freq="H") + timeindex_hourly = pd.date_range("1/1/2019", periods=3, freq="H") + timeindex_45mins = pd.date_range("1/1/2019", periods=2, freq="45min") + timeindex1 = timeindex_one.append(timeindex_hourly) + timeindex2 = timeindex_hourly.append([timeindex_45mins]) + with pytest.raises(TypeError): + solph.EnergySystem(timeindex=timeindex1, infer_last_interval=False) + with pytest.raises(TypeError): + solph.EnergySystem(timeindex=timeindex2, infer_last_interval=False) From 1cb7eb94a7b3d058626280b4983b9e296dae0ce6 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 20 Dec 2021 22:36:26 +0100 Subject: [PATCH 0106/1363] Fix style issues --- src/oemof/solph/components/_generic_storage.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index 32d12bd25..001162771 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -402,7 +402,10 @@ def _create(self, group=None): ) self.STORAGES_INITITAL_LEVEL = Set( - initialize=[n for n in group if n.initial_storage_level is not None]) + initialize=[ + n for n in group if n.initial_storage_level is not None + ] + ) self.STORAGES_WITH_INVEST_FLOW_REL = Set( initialize=[ @@ -444,7 +447,7 @@ def _storage_balance_rule(block, n, t): every timestep. """ expr = 0 - expr += block.storage_content[n, t+1] + expr += block.storage_content[n, t + 1] expr += ( -block.storage_content[n, t] * (1 - n.loss_rate[t]) ** m.timeincrement[t] From eb3aec4f3337b70655ecb32b29f3cb7cea943cce Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 20 Dec 2021 22:36:35 +0100 Subject: [PATCH 0107/1363] Remove wrong import --- src/oemof/solph/_models.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 0cf74b75c..786382528 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -19,7 +19,6 @@ from pyomo.opt import SolverFactory from oemof.solph import processing -from oemof.solph._plumbing import sequence from oemof.solph.buses._bus import BusBlock from oemof.solph.components._transformer import TransformerBlock from oemof.solph.flows._flow import FlowBlock From 051ac42b61f070aa328ae39973109aa68b2e1f26 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 20 Dec 2021 23:14:30 +0100 Subject: [PATCH 0108/1363] Test error messages --- src/oemof/solph/_models.py | 2 +- tests/test_time_index.py | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 786382528..333aa95b2 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -279,7 +279,7 @@ def _add_parent_block_sets(self): # set with all nodes self.NODES = po.Set(initialize=[n for n in self.es.nodes]) - if not hasattr(self.es, "timeincrement"): + if self.es.timeincrement is None: msg = ( "The EnergySystem needs to have a valid 'timeincrement' " "attribute to build a model." diff --git a/tests/test_time_index.py b/tests/test_time_index.py index 7d185d082..91d5870bf 100644 --- a/tests/test_time_index.py +++ b/tests/test_time_index.py @@ -116,6 +116,28 @@ def test_timeincrement_with_non_valid_timeindex(): solph.EnergySystem(timeindex=4) +def test_conflicting_time_index(): + msg = ( + "Specifying the timeincrement and the timeindex parameter at the same " + "time is not allowed" + ) + with pytest.raises(AttributeError, match=msg): + solph.EnergySystem( + timeindex=pd.date_range("1/1/2012", periods=2, freq="H"), + timeincrement=[1, 2, 3, 4], + ) + + +def test_missing_timeincrement(): + msg = ( + "The EnergySystem needs to have a valid 'timeincrement' attribute to " + "build a model." + ) + es = solph.EnergySystem() + with pytest.raises(AttributeError, match=msg): + solph.Model(es) + + def test_overwrite_timeincrement(): es = solph.EnergySystem( timeindex=pd.date_range("1/1/2012", periods=2, freq="H") From efd74ff5322143af10bd1feeb73247a6790a08e4 Mon Sep 17 00:00:00 2001 From: uvchik Date: Tue, 21 Dec 2021 21:39:59 +0100 Subject: [PATCH 0109/1363] Fix merge bugs --- src/oemof/solph/_options.py | 6 ++--- src/oemof/solph/flows/_flow.py | 17 ++++++++++--- src/oemof/solph/flows/_non_convex_flow.py | 24 ------------------- tests/constraint_tests.py | 8 +++---- tests/flow_tests.py | 2 +- .../source_with_nonconvex_gradient.lp | 12 ++-------- tests/test_models.py | 1 + tests/test_warnings.py | 2 +- 8 files changed, 26 insertions(+), 46 deletions(-) diff --git a/src/oemof/solph/_options.py b/src/oemof/solph/_options.py index 9b8ca2d75..312fa543f 100644 --- a/src/oemof/solph/_options.py +++ b/src/oemof/solph/_options.py @@ -172,8 +172,8 @@ def __init__(self, **kwargs): dictionaries = ["positive_gradient", "negative_gradient"] defaults = { "initial_status": 0, - "positive_gradient": {"ub": None, "costs": 0}, - "negative_gradient": {"ub": None, "costs": 0}, + "positive_gradient": {"ub": None}, + "negative_gradient": {"ub": None}, } for attribute in set( @@ -184,7 +184,7 @@ def __init__(self, **kwargs): setattr( self, attribute, - {"ub": sequence(value["ub"]), "costs": value["costs"]}, + {"ub": sequence(value["ub"])}, ) else: setattr( diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index b56984784..4515a7794 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -150,8 +150,8 @@ def __init__(self, **kwargs): dictionaries = ["positive_gradient", "negative_gradient"] defaults = { "variable_costs": 0, - "positive_gradient": {"ub": None, "costs": 0}, - "negative_gradient": {"ub": None, "costs": 0}, + "positive_gradient": {"ub": None}, + "negative_gradient": {"ub": None}, } keys = [k for k in kwargs if k != "label"] @@ -192,13 +192,24 @@ def __init__(self, **kwargs): if kwargs.get("max") is None: defaults["max"] = 1 + # Check gradient dictionaries for non-valid keys + for gradient_dict in ["negative_gradient", "positive_gradient"]: + if gradient_dict in kwargs: + if list(kwargs[gradient_dict].keys()) != list( + defaults[gradient_dict].keys() + ): + msg = ( + "Only the key 'ub' is allowed for the '{0}' attribute" + ) + raise AttributeError(msg.format(gradient_dict)) + for attribute in set(scalars + sequences + dictionaries + keys): value = kwargs.get(attribute, defaults.get(attribute)) if attribute in dictionaries: setattr( self, attribute, - {"ub": sequence(value["ub"]), "costs": value["costs"]}, + {"ub": sequence(value["ub"])}, ) else: diff --git a/src/oemof/solph/flows/_non_convex_flow.py b/src/oemof/solph/flows/_non_convex_flow.py index f0a15cd98..e84dcc96c 100644 --- a/src/oemof/solph/flows/_non_convex_flow.py +++ b/src/oemof/solph/flows/_non_convex_flow.py @@ -675,30 +675,6 @@ def _objective_expression(self): self.inactivity_costs = Expression(expr=inactivity_costs) - if self.POSITIVE_GRADIENT_FLOWS: - for i, o in self.POSITIVE_GRADIENT_FLOWS: - if ( - m.flows[i, o].nonconvex.positive_gradient["ub"][0] - is not None - ): - for t in m.TIMESTEPS: - gradient_costs += self.positive_gradient[i, o, t] * ( - m.flows[i, o].nonconvex.positive_gradient["costs"] - ) - - if self.NEGATIVE_GRADIENT_FLOWS: - for i, o in self.NEGATIVE_GRADIENT_FLOWS: - if ( - m.flows[i, o].nonconvex.negative_gradient["ub"][0] - is not None - ): - for t in m.TIMESTEPS: - gradient_costs += self.negative_gradient[i, o, t] * ( - m.flows[i, o].nonconvex.negative_gradient["costs"] - ) - - self.gradient_costs = Expression(expr=gradient_costs) - return ( startup_costs + shutdown_costs diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index 5908565f2..76df0e2be 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -912,8 +912,8 @@ def test_nonconvex_gradient(self): nominal_value=999, variable_costs=23, nonconvex=solph.NonConvex( - positive_gradient={"ub": 0.03, "costs": 7}, - negative_gradient={"ub": 0.05, "costs": 8}, + positive_gradient={"ub": 0.03}, + negative_gradient={"ub": 0.05}, ), ) }, @@ -932,7 +932,7 @@ def test_nonconvex_positive_gradient_error(self): with pytest.raises(ValueError, match=msg): solph.flows.Flow( nonconvex=solph.NonConvex( - positive_gradient={"ub": 0.03, "costs": 7}, + positive_gradient={"ub": 0.03}, ), positive_gradient={"ub": 0.03}, ) @@ -948,7 +948,7 @@ def test_nonconvex_negative_gradient_error(self): with pytest.raises(ValueError, match=msg): solph.flows.Flow( nonconvex=solph.NonConvex( - negative_gradient={"ub": 0.03, "costs": 7}, + negative_gradient={"ub": 0.03}, ), negative_gradient={"ub": 0.03}, ) diff --git a/tests/flow_tests.py b/tests/flow_tests.py index 48ed86a14..5f93fc126 100644 --- a/tests/flow_tests.py +++ b/tests/flow_tests.py @@ -11,7 +11,7 @@ import pytest -from oemof.solph import Flow +from oemof.solph.flows import Flow def test_error_in_gradient_attribute(): diff --git a/tests/lp_files/source_with_nonconvex_gradient.lp b/tests/lp_files/source_with_nonconvex_gradient.lp index f1171cfca..492de2a40 100644 --- a/tests/lp_files/source_with_nonconvex_gradient.lp +++ b/tests/lp_files/source_with_nonconvex_gradient.lp @@ -1,13 +1,7 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+8 NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_0) -+8 NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_1) -+8 NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_2) -+7 NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_0) -+7 NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_1) -+7 NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_2) +23 flow(powerplant_electricityBus_0) +23 flow(powerplant_electricityBus_1) +23 flow(powerplant_electricityBus_2) @@ -85,7 +79,7 @@ c_u_NonConvexFlowBlock_negative_gradient_constr(powerplant_electricityBus_2)_: ] <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds @@ -95,10 +89,8 @@ bounds 0 <= NonConvexFlowBlock_status(powerplant_electricityBus_0) <= 1 0 <= NonConvexFlowBlock_status(powerplant_electricityBus_1) <= 1 0 <= NonConvexFlowBlock_status(powerplant_electricityBus_2) <= 1 - -inf <= NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_0) <= +inf -inf <= NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_1) <= +inf -inf <= NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_2) <= +inf - -inf <= NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_0) <= +inf -inf <= NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_1) <= +inf -inf <= NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_2) <= +inf binary diff --git a/tests/test_models.py b/tests/test_models.py index f17c9d4a7..dae6fa0a0 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -111,6 +111,7 @@ def test_optimal_solution(): def test_infeasible_model(): + warnings.filterwarnings("ignore", category=FutureWarning) with pytest.raises(ValueError, match=""): with warnings.catch_warnings(record=True) as w: es = solph.EnergySystem(timeindex=[1]) diff --git a/tests/test_warnings.py b/tests/test_warnings.py index b81367557..ceb156734 100644 --- a/tests/test_warnings.py +++ b/tests/test_warnings.py @@ -22,7 +22,7 @@ def warning_fixture(): """Explicitly activate the warnings.""" warnings.filterwarnings("always", category=SuspiciousUsageWarning) - + warnings.filterwarnings("ignore", category=FutureWarning) def test_that_the_sink_warnings_actually_get_raised(warning_fixture): """Sink doesn't warn about potentially erroneous usage.""" From 8b6b13f131eda82559b936d450684dbf258f0828 Mon Sep 17 00:00:00 2001 From: uvchik Date: Tue, 21 Dec 2021 21:49:28 +0100 Subject: [PATCH 0110/1363] Fix style issues --- .../solph/constraints/equate_variables.py | 23 ++++++++++++------- .../solph/constraints/flow_count_limit.py | 2 +- tests/test_warnings.py | 1 + 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/oemof/solph/constraints/equate_variables.py b/src/oemof/solph/constraints/equate_variables.py index ba2d135ac..aaf5b2070 100644 --- a/src/oemof/solph/constraints/equate_variables.py +++ b/src/oemof/solph/constraints/equate_variables.py @@ -18,18 +18,25 @@ def equate_variables(model, var1, var2, factor1=1, name=None): Parameters ---------- - var1 : pyomo.environ.Var First variable, to be set to equal with Var2 and multiplied with factor1. - var2 : pyomo.environ.Var Second variable, to be set equal to (Var1 * factor1). - factor1 : float Factor to define the proportion between the variables. - name : str Optional name for the equation e.g. in the LP file. By default the name is: - equate + string representation of var1 and var2. - model : oemof.solph.Model Model to which the constraint is added. + var1 : pyomo.environ.Var + First variable, to be set to equal with Var2 and multiplied with + factor1. + var2 : pyomo.environ.Var + Second variable, to be set equal to (Var1 * factor1). + factor1 : float + Factor to define the proportion between the variables. + name : str + Optional name for the equation e.g. in the LP file. By default the + name is: equate + string representation of var1 and var2. + model : oemof.solph.Model + Model to which the constraint is added. **The following constraints are build:** .. math:: var_1 \cdot factor_1 = var_1 - The symbols used are defined as follows (with Variables (V) and Parameters (P)): + The symbols used are defined as follows (with Variables (V) and Parameters + (P)): +------------------+---------------------+------+------------------------------------------------------------------------------------------------------------------------------------------------+ | symbol | attribute | type | explanation | +==================+=====================+======+================================================================================================================================================+ @@ -75,7 +82,7 @@ def equate_variables(model, var1, var2, factor1=1, name=None): ... om, ... om.InvestmentFlowBlock.invest[line12, bel2], ... om.InvestmentFlowBlock.invest[line21, bel1]) - """ + """ # noqa: E501 if name is None: name = "_".join(["equate", str(var1), str(var2)]) diff --git a/src/oemof/solph/constraints/flow_count_limit.py b/src/oemof/solph/constraints/flow_count_limit.py index 54b3c476e..9a4b82985 100644 --- a/src/oemof/solph/constraints/flow_count_limit.py +++ b/src/oemof/solph/constraints/flow_count_limit.py @@ -65,7 +65,7 @@ def limit_active_flow_count( +-------------------+------+--------------------------------------------------------------+ | :math:`N_{X,max} | P | upper_limit | +-------------------+------+--------------------------------------------------------------+ - """ + """ # noqa: E501 # number of concurrent active flows setattr(model, constraint_name, po.Var(model.TIMESTEPS)) diff --git a/tests/test_warnings.py b/tests/test_warnings.py index ceb156734..7f8c40793 100644 --- a/tests/test_warnings.py +++ b/tests/test_warnings.py @@ -24,6 +24,7 @@ def warning_fixture(): warnings.filterwarnings("always", category=SuspiciousUsageWarning) warnings.filterwarnings("ignore", category=FutureWarning) + def test_that_the_sink_warnings_actually_get_raised(warning_fixture): """Sink doesn't warn about potentially erroneous usage.""" look_out = network.Bus() From b759831698f9f7e7f6004a911bda26c24c9b6da9 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 7 Jan 2022 18:18:07 +0100 Subject: [PATCH 0111/1363] Add bugfix --- src/oemof/solph/constraints/integral_limit.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/oemof/solph/constraints/integral_limit.py b/src/oemof/solph/constraints/integral_limit.py index 6b2167105..6e81c9dfd 100644 --- a/src/oemof/solph/constraints/integral_limit.py +++ b/src/oemof/solph/constraints/integral_limit.py @@ -252,3 +252,5 @@ def _check_and_set_flows(om, flows, keyword): "has no attribute {2}." ).format(i.label, o.label, keyword) ) + + return flows From 6160862c3679192e493a3e85d74a0797377e36f5 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 8 Jan 2022 14:34:00 +0100 Subject: [PATCH 0112/1363] Simplify multi-period storage and add fixes for SinkDSM --- .../solph/components/_generic_storage.py | 90 +++++---------- .../components/experimental/_sink_dsm.py | 104 +++++++++++++----- 2 files changed, 105 insertions(+), 89 deletions(-) diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index f859606f8..d70c1fd8b 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -808,6 +808,24 @@ def _create(self, group=None): if group is None: return None + # ########################## CHECKS ################################### + if m.es.multi_period: + for n in group: + e1 = ( + "For a multi-period model, fixed absolute losses" + " are not supported. Please remove parameter." + ) + if n.fixed_losses_absolute.default != 0: + raise ValueError(e1) + e2 = ( + "For a multi-period model, initial_storage_level is" + " not supported. Please remove parameter. storage_content" + " will be zero, until there is some storage capacity" + " installed." + ) + if n.initial_storage_level is not None: + raise ValueError(e2) + # ########################## SETS ##################################### self.INVESTSTORAGES = Set(initialize=[n for n in group]) @@ -1066,32 +1084,6 @@ def _old_storage_capacity_rule(block): self.old_rule_build = BuildAction(rule=_old_storage_capacity_rule) - def _init_content_multi_period_rule(block): - """Rule defining the initial storage level - for a multi-period model - """ - for n in self.INVESTSTORAGES_INIT_CONTENT: - is_commissioned = False - for p in m.PERIODS: - if not is_commissioned: - expr = ( - self.storage_content[ - n, m.TIMESTEPS_IN_PERIOD[p][0] - ] - == n.initial_storage_level * self.total[n, p] - ) - is_commissioned = True - self.init_content_multi_period.add((n, p), expr) - else: - pass - - self.init_content_multi_period = Constraint( - self.INVESTSTORAGES_INIT_CONTENT, m.PERIODS, noruleinit=True - ) - self.init_content_multi_period_rule_build = BuildAction( - rule=_init_content_multi_period_rule - ) - # Standard storage implementation for discrete time points else: @@ -1111,7 +1103,9 @@ def _inv_storage_init_content_fix_rule(block, n): """Constraint for a fixed initial storage capacity.""" return ( block.init_content[n] - <= n.investment.existing + block.invest[n, 0] + == n.initial_storage_level * ( + n.investment.existing + block.invest[n, 0] + ) ) self.init_content_fix = Constraint( @@ -1191,38 +1185,6 @@ def _balanced_storage_rule(block, n): self.INVESTSTORAGES_BALANCED, rule=_balanced_storage_rule ) - else: - - def _lifetime_balanced_storage_rule(block): - for n in self.INVESTSTORAGES_BALANCED: - lifetime = n.investment.lifetime - age = n.investment.age - for p in m.PERIODS: - if lifetime - age <= m.es.periods_years[p]: - # Obtain commissioning period - comm_p = 0 - for k, v in m.es.periods_years.items(): - if m.es.periods_years[p] - lifetime - v < 0: - # change of sign is detected - comm_p = k - 1 - break - last_step = m.TIMESTEPS_IN_PERIOD[p][-1] - first_step = m.TIMESTEPS_IN_PERIOD[comm_p][0] - expr = ( - block.storage_content[n, last_step] - == block.storage_content[n, first_step] - ) - self.lifetime_balanced_cstr.add((n, p), expr) - else: - pass - - self.lifetime_balanced_cstr = Constraint( - self.INVESTSTORAGES_BALANCED, m.PERIODS, noruleinit=True - ) - self.lifetime_balanced_cstr_build = BuildAction( - rule=_lifetime_balanced_storage_rule - ) - def _power_coupled(block): """ Rule definition for constraint to connect the input power @@ -1252,7 +1214,7 @@ def _storage_capacity_inflow_invest_rule(block): for n in self.INVEST_REL_CAP_IN: for p in m.PERIODS: expr = ( - m.InvestmentFlow.total[i[n], n, p] + m.InvestmentFlowBlock.total[i[n], n, p] == self.total[n, p] * n.invest_relation_input_capacity ) self.storage_capacity_inflow.add((n, p), expr) @@ -1273,9 +1235,11 @@ def _storage_capacity_outflow_invest_rule(block): """ for n in self.INVEST_REL_CAP_OUT: for p in m.PERIODS: - expr = (m.InvestmentFlow.total[n, o[n], p]) == self.total[ - n, p - ] * n.invest_relation_output_capacity + expr = ( + m.InvestmentFlowBlock.total[n, o[n], p] + == self.total[n, p] + * n.invest_relation_output_capacity + ) self.storage_capacity_outflow.add((n, p), expr) self.storage_capacity_outflow = Constraint( diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index ae4fef005..6b6b96158 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -970,7 +970,7 @@ def _old_dsm_capacity_rule_end(block): # No shutdown in first period if p == 0: expr = self.old_end[g, p] == 0 - self.old_rule_end.add((g, p), expr) + self.old_dsm_rule_end.add((g, p), expr) elif lifetime <= m.es.periods_years[p]: # Obtain commissioning period comm_p = 0 @@ -980,7 +980,7 @@ def _old_dsm_capacity_rule_end(block): comm_p = k - 1 break expr = self.old_end[g, p] == self.invest[g, comm_p] - self.old_rule_end.add((g, p), expr) + self.old_dsm_rule_end.add((g, p), expr) else: expr = self.old_end[g, p] == 0 self.old_dsm_rule_end.add((g, p), expr) @@ -1004,18 +1004,18 @@ def _old_dsm_capacity_rule_exo(block): # No shutdown in first period if p == 0: expr = self.old_exo[g, p] == 0 - self.old_rule_exo.add((g, p), expr) + self.old_dsm_rule_exo.add((g, p), expr) elif lifetime - age <= m.es.periods_years[p]: # Track decommissioning status if not is_decommissioned: expr = ( self.old_exo[g, p] - == m.flows[g].investment.existing + == g.investment.existing ) is_decommissioned = True else: expr = self.old_exo[g, p] == 0 - self.old_rule_exo.add((g, p), expr) + self.old_dsm_rule_exo.add((g, p), expr) else: expr = self.old_exo[g, p] == 0 self.old_dsm_rule_exo.add((g, p), expr) @@ -2156,7 +2156,7 @@ def _create(self, group=None): # ************* VARIABLES ***************************** # Define bounds for investments in demand response - def _dsm_investvar_bound_rule(block, g): + def _dsm_investvar_bound_rule(block, g, p): """Rule definition to bound the demand response capacity invested in (`invest`). """ @@ -2236,36 +2236,62 @@ def _old_dsm_capacity_rule_end(block): for g in group: lifetime = g.investment.lifetime for p in m.PERIODS: - if lifetime <= p: - expr = ( - self.old_end[g, p] == self.invest[g, p - lifetime] - ) + # No shutdown in first period + if p == 0: + expr = self.old_end[g, p] == 0 + self.old_dsm_rule_end.add((g, p), expr) + elif lifetime <= m.es.periods_years[p]: + # Obtain commissioning period + comm_p = 0 + for k, v in m.es.periods_years.items(): + if m.es.periods_years[p] - lifetime - v < 0: + # change of sign is detected + comm_p = k - 1 + break + expr = self.old_end[g, p] == self.invest[g, comm_p] self.old_dsm_rule_end.add((g, p), expr) else: expr = self.old_end[g, p] == 0 self.old_dsm_rule_end.add((g, p), expr) - self.old_dsm_rule_end = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_end = Constraint( + group, m.PERIODS, noruleinit=True + ) self.old_dsm_rule_end_build = BuildAction( rule=_old_dsm_capacity_rule_end ) def _old_dsm_capacity_rule_exo(block): - """Rule definition for determining old exogenously given capacity - to be decommissioned due to reaching its lifetime + """Rule definition for determining old exogenously given + capacity to be decommissioned due to reaching its lifetime """ for g in group: age = g.investment.age lifetime = g.investment.lifetime + is_decommissioned = False for p in m.PERIODS: - if lifetime - age == p: - expr = self.old_exo[g, p] == g.investment.existing + # No shutdown in first period + if p == 0: + expr = self.old_exo[g, p] == 0 + self.old_dsm_rule_exo.add((g, p), expr) + elif lifetime - age <= m.es.periods_years[p]: + # Track decommissioning status + if not is_decommissioned: + expr = ( + self.old_exo[g, p] + == g.investment.existing + ) + is_decommissioned = True + else: + expr = self.old_exo[g, p] == 0 self.old_dsm_rule_exo.add((g, p), expr) else: expr = self.old_exo[g, p] == 0 self.old_dsm_rule_exo.add((g, p), expr) - self.old_dsm_rule_exo = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_exo = Constraint( + group, m.PERIODS, noruleinit=True + ) self.old_dsm_rule_exo_build = BuildAction( rule=_old_dsm_capacity_rule_exo ) @@ -4283,36 +4309,62 @@ def _old_dsm_capacity_rule_end(block): for g in group: lifetime = g.investment.lifetime for p in m.PERIODS: - if lifetime <= p: - expr = ( - self.old_end[g, p] == self.invest[g, p - lifetime] - ) + # No shutdown in first period + if p == 0: + expr = self.old_end[g, p] == 0 + self.old_dsm_rule_end.add((g, p), expr) + elif lifetime <= m.es.periods_years[p]: + # Obtain commissioning period + comm_p = 0 + for k, v in m.es.periods_years.items(): + if m.es.periods_years[p] - lifetime - v < 0: + # change of sign is detected + comm_p = k - 1 + break + expr = self.old_end[g, p] == self.invest[g, comm_p] self.old_dsm_rule_end.add((g, p), expr) else: expr = self.old_end[g, p] == 0 self.old_dsm_rule_end.add((g, p), expr) - self.old_dsm_rule_end = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_end = Constraint( + group, m.PERIODS, noruleinit=True + ) self.old_dsm_rule_end_build = BuildAction( rule=_old_dsm_capacity_rule_end ) def _old_dsm_capacity_rule_exo(block): - """Rule definition for determining old exogenously given capacity - to be decommissioned due to reaching its lifetime + """Rule definition for determining old exogenously given + capacity to be decommissioned due to reaching its lifetime """ for g in group: age = g.investment.age lifetime = g.investment.lifetime + is_decommissioned = False for p in m.PERIODS: - if lifetime - age == p: - expr = self.old_exo[g, p] == g.investment.existing + # No shutdown in first period + if p == 0: + expr = self.old_exo[g, p] == 0 + self.old_dsm_rule_exo.add((g, p), expr) + elif lifetime - age <= m.es.periods_years[p]: + # Track decommissioning status + if not is_decommissioned: + expr = ( + self.old_exo[g, p] + == g.investment.existing + ) + is_decommissioned = True + else: + expr = self.old_exo[g, p] == 0 self.old_dsm_rule_exo.add((g, p), expr) else: expr = self.old_exo[g, p] == 0 self.old_dsm_rule_exo.add((g, p), expr) - self.old_dsm_rule_exo = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_exo = Constraint( + group, m.PERIODS, noruleinit=True + ) self.old_dsm_rule_exo_build = BuildAction( rule=_old_dsm_capacity_rule_exo ) From e47dab887a7993f233522a2691077aa025eb236f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Mon, 10 Jan 2022 10:49:12 +0100 Subject: [PATCH 0113/1363] Adjust time step example --- .../time_step_example/time_step_example.py | 54 ++++++++++++++----- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/examples/time_step_example/time_step_example.py b/examples/time_step_example/time_step_example.py index f5431e6c4..2a06dc332 100644 --- a/examples/time_step_example/time_step_example.py +++ b/examples/time_step_example/time_step_example.py @@ -4,16 +4,19 @@ General description ------------------- -A minimal example to show how (non-hourly) time steps work. - -* One purpose is to illustrate that the nominal_value in Flows - has to be interpreted in means of power: - We have nominal_value=1, but the storage content of an ideal - storage just changes in steps of 0.25, when having four time - steps per hour. -* Also, the initial_storage_level of a GenericStorage is given - _before_ the first time step. If the storage is balanced, +A minimal example to show how time steps work. + +* Flows are defined in time intervals, storage content at points in time. + Thus, there is one more value for storage contents then for the + flow values. +* Time intervals are named by the time at the beginning of that interval. + The quantity changes to the given value at the given point in time. +* The initial_storage_level of a GenericStorage is given + at the first time step. If the storage is balanced, this is the same storage level as in the last time step. +* The nominal_value in Flows has to be interpreted in means of power: + We have nominal_value=0.5, but the maximum change of the storage content + of an ideal storage is 0.125. Installation requirements ------------------------- @@ -25,6 +28,11 @@ import pandas as pd from oemof import solph +try: + import matplotlib.pyplot as plt +except ModuleNotFoundError: + plt = None + solver = "cbc" # 'glpk', 'gurobi',... solver_verbose = False # show/hide solver output @@ -37,7 +45,9 @@ label="source", outputs={ bus: solph.flows.Flow( - nominal_value=1, variable_costs=-1, max=[0, 0, 0, 0, 1, 1, 1, 1] + nominal_value=2, + variable_costs=0.2, + max=[0, 0, 0, 0, 1, 0.25, 0.75, 1] ) }, ) @@ -51,7 +61,10 @@ sink = solph.components.Sink( label="sink", inputs={ - bus: solph.flows.Flow(nominal_value=1, max=[1, 1, 1, 1, 0, 0, 0, 0]) + bus: solph.flows.Flow( + nominal_value=2, + variable_costs=0.1, + fix=[1, 1, 0.5, 0.5, 0, 0, 0, 0]) }, ) @@ -61,4 +74,21 @@ results = solph.processing.results(model) -print(results[(storage, None)]["sequences"]) +results_df = results[(storage, None)]["sequences"].copy() +results_df["storage_inflow"] = results[(bus, storage)]["sequences"]["flow"] +results_df["storage_outflow"] = results[(storage, bus)]["sequences"]["flow"] + +print(results_df) + +if plt is not None: + plt.plot(results[(bus, storage)]["sequences"], + drawstyle="steps-post", + label="Storage inflow") + plt.plot(results[(storage, None)]["sequences"], + label="Storage content") + plt.plot(results[(storage, bus)]["sequences"], + drawstyle="steps-post", + label="Storage outflow") + + plt.legend(loc="lower left") + plt.show() From 2f81f1f678b7213fd8a5182e8afdde0f865d9da8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Mon, 10 Jan 2022 13:37:27 +0100 Subject: [PATCH 0114/1363] Adhere to Black coding style --- .../time_step_example/time_step_example.py | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/examples/time_step_example/time_step_example.py b/examples/time_step_example/time_step_example.py index 2a06dc332..da5777ea2 100644 --- a/examples/time_step_example/time_step_example.py +++ b/examples/time_step_example/time_step_example.py @@ -47,7 +47,7 @@ bus: solph.flows.Flow( nominal_value=2, variable_costs=0.2, - max=[0, 0, 0, 0, 1, 0.25, 0.75, 1] + max=[0, 0, 0, 0, 1, 0.25, 0.75, 1], ) }, ) @@ -64,7 +64,8 @@ bus: solph.flows.Flow( nominal_value=2, variable_costs=0.1, - fix=[1, 1, 0.5, 0.5, 0, 0, 0, 0]) + fix=[1, 1, 0.5, 0.5, 0, 0, 0, 0], + ) }, ) @@ -81,14 +82,17 @@ print(results_df) if plt is not None: - plt.plot(results[(bus, storage)]["sequences"], - drawstyle="steps-post", - label="Storage inflow") - plt.plot(results[(storage, None)]["sequences"], - label="Storage content") - plt.plot(results[(storage, bus)]["sequences"], - drawstyle="steps-post", - label="Storage outflow") + plt.plot( + results[(bus, storage)]["sequences"], + drawstyle="steps-post", + label="Storage inflow", + ) + plt.plot(results[(storage, None)]["sequences"], label="Storage content") + plt.plot( + results[(storage, bus)]["sequences"], + drawstyle="steps-post", + label="Storage outflow", + ) plt.legend(loc="lower left") plt.show() From 6efe3a1af6adb67e0f1592e3cbb0790653afe9b6 Mon Sep 17 00:00:00 2001 From: jnnr <32454596+jnnr@users.noreply.github.com> Date: Tue, 11 Jan 2022 15:17:00 +0100 Subject: [PATCH 0115/1363] Add test for new constraint --- tests/constraint_tests.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index 88515d891..cd1e7b0bc 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -883,6 +883,42 @@ def test_equate_variables_constraint(self): self.compare_lp_files("connect_investment.lp", my_om=om) + def test_equate_flows_constraint(self): + """Testing the equate_flows function in the constraint module.""" + bus1 = solph.buses.Bus(label="Bus1") + storage = solph.components.GenericStorage( + label="storage_constraint", + invest_relation_input_capacity=0.2, + invest_relation_output_capacity=0.2, + inputs={bus1: solph.flows.Flow()}, + outputs={bus1: solph.flows.Flow()}, + investment=solph.Investment(ep_costs=145), + ) + sink = solph.components.Sink( + label="Sink", + inputs={ + bus1: solph.flows.Flow( + investment=solph.Investment(ep_costs=500) + ) + }, + ) + source = solph.components.Source( + label="Source", + outputs={ + bus1: solph.flows.Flow( + investment=solph.Investment(ep_costs=123) + ) + }, + ) + om = self.get_om() + solph.constraints.equate_flows( + om, + [(source, bus1)], + [(bus1, sink)], + 2, + ) + self.compare_lp_files("equate_flows.lp", my_om=om) + def test_gradient(self): """Testing gradient constraints and costs.""" bel = solph.buses.Bus(label="electricityBus") From 9d9c471ce191d0dc5aacb5c1f2be59279c23ff11 Mon Sep 17 00:00:00 2001 From: jnnr <32454596+jnnr@users.noreply.github.com> Date: Tue, 11 Jan 2022 15:28:58 +0100 Subject: [PATCH 0116/1363] Add new constraint to equate flows --- src/oemof/solph/constraints/__init__.py | 1 + src/oemof/solph/constraints/equate_flows.py | 41 +++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/oemof/solph/constraints/equate_flows.py diff --git a/src/oemof/solph/constraints/__init__.py b/src/oemof/solph/constraints/__init__.py index 5ddbfd44f..1c832105c 100644 --- a/src/oemof/solph/constraints/__init__.py +++ b/src/oemof/solph/constraints/__init__.py @@ -3,6 +3,7 @@ Additional constraints to be used in an oemof energy model. """ +from .equate_flows import equate_flows # noqa: F401 from .equate_variables import equate_variables # noqa: F401 from .flow_count_limit import limit_active_flow_count # noqa: F401 from .flow_count_limit import limit_active_flow_count_by_keyword # noqa: F401 diff --git a/src/oemof/solph/constraints/equate_flows.py b/src/oemof/solph/constraints/equate_flows.py new file mode 100644 index 000000000..b90aec87a --- /dev/null +++ b/src/oemof/solph/constraints/equate_flows.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +"""Constraints to relate variables in an existing model. + +SPDX-FileCopyrightText: Uwe Krien +SPDX-FileCopyrightText: Simon Hilpert + +SPDX-License-Identifier: MIT + +""" + +from pyomo import environ as po + + +def equate_flows(model, flows1, flows2, factor1=1, name="equate_flows"): + r""" + Adds a constraint to the given model that sets the sum of two groups of + flows equal or proportional by a factor. + """ + def _equate_flow_groups_rule(m): + for ts in m.TIMESTEPS: + sum1_t = sum( + m.flow[fi, fo, ts] for fi, fo in flows1 + ) + sum2_t = sum( + m.flow[fi, fo, ts] for fi, fo in flows2 + ) + expr = sum1_t * factor1 == sum2_t + if expr is not True: + getattr(m, name).add(ts, expr) + + setattr( + model, + name, + po.Constraint(model.TIMESTEPS, noruleinit=True), + ) + setattr( + model, + name + "_build", + po.BuildAction(rule=_equate_flow_groups_rule), + ) From 04b1f9f9ce988b9134360875e9751fcbd56a8ceb Mon Sep 17 00:00:00 2001 From: jnnr <32454596+jnnr@users.noreply.github.com> Date: Tue, 11 Jan 2022 15:29:36 +0100 Subject: [PATCH 0117/1363] Add default lp file --- tests/lp_files/equate_flows.lp | 188 +++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 tests/lp_files/equate_flows.lp diff --git a/tests/lp_files/equate_flows.lp b/tests/lp_files/equate_flows.lp new file mode 100644 index 000000000..5b252d464 --- /dev/null +++ b/tests/lp_files/equate_flows.lp @@ -0,0 +1,188 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++145 GenericInvestmentStorageBlock_invest(storage_constraint) ++500 InvestmentFlowBlock_invest(Bus1_Sink) ++123 InvestmentFlowBlock_invest(Source_Bus1) + +s.t. + +c_e_equate_flows(0)_: +-1 flow(Bus1_Sink_0) ++2 flow(Source_Bus1_0) += 0 + +c_e_equate_flows(1)_: +-1 flow(Bus1_Sink_1) ++2 flow(Source_Bus1_1) += 0 + +c_e_equate_flows(2)_: +-1 flow(Bus1_Sink_2) ++2 flow(Source_Bus1_2) += 0 + +c_e_BusBlock_balance(Bus1_0)_: +-1 flow(Bus1_Sink_0) +-1 flow(Bus1_storage_constraint_0) ++1 flow(Source_Bus1_0) ++1 flow(storage_constraint_Bus1_0) += 0 + +c_e_BusBlock_balance(Bus1_1)_: +-1 flow(Bus1_Sink_1) +-1 flow(Bus1_storage_constraint_1) ++1 flow(Source_Bus1_1) ++1 flow(storage_constraint_Bus1_1) += 0 + +c_e_BusBlock_balance(Bus1_2)_: +-1 flow(Bus1_Sink_2) +-1 flow(Bus1_storage_constraint_2) ++1 flow(Source_Bus1_2) ++1 flow(storage_constraint_Bus1_2) += 0 + +c_u_InvestmentFlowBlock_max(Bus1_Sink_0)_: +-1 InvestmentFlowBlock_invest(Bus1_Sink) ++1 flow(Bus1_Sink_0) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_Sink_1)_: +-1 InvestmentFlowBlock_invest(Bus1_Sink) ++1 flow(Bus1_Sink_1) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_Sink_2)_: +-1 InvestmentFlowBlock_invest(Bus1_Sink) ++1 flow(Bus1_Sink_2) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_storage_constraint_0)_: +-1 InvestmentFlowBlock_invest(Bus1_storage_constraint) ++1 flow(Bus1_storage_constraint_0) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_storage_constraint_1)_: +-1 InvestmentFlowBlock_invest(Bus1_storage_constraint) ++1 flow(Bus1_storage_constraint_1) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_storage_constraint_2)_: +-1 InvestmentFlowBlock_invest(Bus1_storage_constraint) ++1 flow(Bus1_storage_constraint_2) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0)_: +-1 InvestmentFlowBlock_invest(Source_Bus1) ++1 flow(Source_Bus1_0) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_1)_: +-1 InvestmentFlowBlock_invest(Source_Bus1) ++1 flow(Source_Bus1_1) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_2)_: +-1 InvestmentFlowBlock_invest(Source_Bus1) ++1 flow(Source_Bus1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_constraint_Bus1_0)_: +-1 InvestmentFlowBlock_invest(storage_constraint_Bus1) ++1 flow(storage_constraint_Bus1_0) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_constraint_Bus1_1)_: +-1 InvestmentFlowBlock_invest(storage_constraint_Bus1) ++1 flow(storage_constraint_Bus1_1) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_constraint_Bus1_2)_: +-1 InvestmentFlowBlock_invest(storage_constraint_Bus1) ++1 flow(storage_constraint_Bus1_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_init_content_limit(storage_constraint)_: ++1 GenericInvestmentStorageBlock_init_content(storage_constraint) +-1 GenericInvestmentStorageBlock_invest(storage_constraint) +<= 0 + +c_e_GenericInvestmentStorageBlock_balance_first(storage_constraint)_: +-1 GenericInvestmentStorageBlock_init_content(storage_constraint) ++1 GenericInvestmentStorageBlock_storage_content(storage_constraint_0) +-1 flow(Bus1_storage_constraint_0) ++1 flow(storage_constraint_Bus1_0) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_constraint_1)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_constraint_0) ++1 GenericInvestmentStorageBlock_storage_content(storage_constraint_1) +-1 flow(Bus1_storage_constraint_1) ++1 flow(storage_constraint_Bus1_1) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_constraint_2)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_constraint_1) ++1 GenericInvestmentStorageBlock_storage_content(storage_constraint_2) +-1 flow(Bus1_storage_constraint_2) ++1 flow(storage_constraint_Bus1_2) += 0 + +c_e_GenericInvestmentStorageBlock_balanced_cstr(storage_constraint)_: +-1 GenericInvestmentStorageBlock_init_content(storage_constraint) ++1 GenericInvestmentStorageBlock_storage_content(storage_constraint_2) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_constraint)_: +-0.20000000000000001 GenericInvestmentStorageBlock_invest(storage_constraint) ++1 InvestmentFlowBlock_invest(Bus1_storage_constraint) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_constraint)_: +-0.20000000000000001 GenericInvestmentStorageBlock_invest(storage_constraint) ++1 InvestmentFlowBlock_invest(storage_constraint_Bus1) += 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_constraint_0)_: +-1 GenericInvestmentStorageBlock_invest(storage_constraint) ++1 GenericInvestmentStorageBlock_storage_content(storage_constraint_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_constraint_1)_: +-1 GenericInvestmentStorageBlock_invest(storage_constraint) ++1 GenericInvestmentStorageBlock_storage_content(storage_constraint_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_constraint_2)_: +-1 GenericInvestmentStorageBlock_invest(storage_constraint) ++1 GenericInvestmentStorageBlock_storage_content(storage_constraint_2) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(Bus1_Sink_0) <= +inf + 0 <= flow(Bus1_Sink_1) <= +inf + 0 <= flow(Bus1_Sink_2) <= +inf + 0 <= flow(Bus1_storage_constraint_0) <= +inf + 0 <= flow(Bus1_storage_constraint_1) <= +inf + 0 <= flow(Bus1_storage_constraint_2) <= +inf + 0 <= flow(Source_Bus1_0) <= +inf + 0 <= flow(Source_Bus1_1) <= +inf + 0 <= flow(Source_Bus1_2) <= +inf + 0 <= flow(storage_constraint_Bus1_0) <= +inf + 0 <= flow(storage_constraint_Bus1_1) <= +inf + 0 <= flow(storage_constraint_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_invest(Bus1_Sink) <= +inf + 0 <= InvestmentFlowBlock_invest(Bus1_storage_constraint) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1) <= +inf + 0 <= InvestmentFlowBlock_invest(storage_constraint_Bus1) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_constraint_0) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_constraint_1) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_constraint_2) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage_constraint) <= +inf + 0 <= GenericInvestmentStorageBlock_init_content(storage_constraint) <= +inf +end From 36e36dd6f97f84576968c5463ee58466f3e4ec46 Mon Sep 17 00:00:00 2001 From: jnnr <32454596+jnnr@users.noreply.github.com> Date: Tue, 11 Jan 2022 15:48:27 +0100 Subject: [PATCH 0118/1363] Add constraint for equating flows by keyword --- src/oemof/solph/constraints/__init__.py | 1 + src/oemof/solph/constraints/equate_flows.py | 21 +++++++++++++++++++++ tests/constraint_tests.py | 12 +++++++----- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/oemof/solph/constraints/__init__.py b/src/oemof/solph/constraints/__init__.py index 1c832105c..e633814bf 100644 --- a/src/oemof/solph/constraints/__init__.py +++ b/src/oemof/solph/constraints/__init__.py @@ -4,6 +4,7 @@ """ from .equate_flows import equate_flows # noqa: F401 +from .equate_flows import equate_flows_by_keyword # noqa: F401 from .equate_variables import equate_variables # noqa: F401 from .flow_count_limit import limit_active_flow_count # noqa: F401 from .flow_count_limit import limit_active_flow_count_by_keyword # noqa: F401 diff --git a/src/oemof/solph/constraints/equate_flows.py b/src/oemof/solph/constraints/equate_flows.py index b90aec87a..ead66e70c 100644 --- a/src/oemof/solph/constraints/equate_flows.py +++ b/src/oemof/solph/constraints/equate_flows.py @@ -39,3 +39,24 @@ def _equate_flow_groups_rule(m): name + "_build", po.BuildAction(rule=_equate_flow_groups_rule), ) + + +def equate_flows_by_keyword(model, keyword1, keyword2, factor1=1, name="equate_flows"): + r""" + This wrapper for equate_flows allows to equate groups of flows by using a keyword + instead of a list of flows. + """ + flows = {} + for n, keyword in enumerate([keyword1, keyword2]): + flows[n] = [] + for (i, o) in model.flows: + if hasattr(model.flows[i, o], keyword): + flows[n].append((i, o)) + + return equate_flows( + model, + flows[0], + flows[1], + factor1=factor1, + name=name + ) diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index cd1e7b0bc..6d38eadc1 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -898,7 +898,8 @@ def test_equate_flows_constraint(self): label="Sink", inputs={ bus1: solph.flows.Flow( - investment=solph.Investment(ep_costs=500) + investment=solph.Investment(ep_costs=500), + outgoing_flow=True, ) }, ) @@ -906,15 +907,16 @@ def test_equate_flows_constraint(self): label="Source", outputs={ bus1: solph.flows.Flow( - investment=solph.Investment(ep_costs=123) + investment=solph.Investment(ep_costs=123), + incoming_flow=True, ) }, ) om = self.get_om() - solph.constraints.equate_flows( + solph.constraints.equate_flows_by_keyword( om, - [(source, bus1)], - [(bus1, sink)], + "incoming_flow", + "outgoing_flow", 2, ) self.compare_lp_files("equate_flows.lp", my_om=om) From 58c3103c3061b3c5664b5d6885bd04d22f9277d9 Mon Sep 17 00:00:00 2001 From: jnnr <32454596+jnnr@users.noreply.github.com> Date: Tue, 11 Jan 2022 15:59:18 +0100 Subject: [PATCH 0119/1363] Return the model --- src/oemof/solph/constraints/equate_flows.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/oemof/solph/constraints/equate_flows.py b/src/oemof/solph/constraints/equate_flows.py index ead66e70c..b427b93fa 100644 --- a/src/oemof/solph/constraints/equate_flows.py +++ b/src/oemof/solph/constraints/equate_flows.py @@ -40,6 +40,8 @@ def _equate_flow_groups_rule(m): po.BuildAction(rule=_equate_flow_groups_rule), ) + return model + def equate_flows_by_keyword(model, keyword1, keyword2, factor1=1, name="equate_flows"): r""" From 53eb43ca8e218c9af1cf01e440456acabfde3bfc Mon Sep 17 00:00:00 2001 From: jnnr <32454596+jnnr@users.noreply.github.com> Date: Thu, 13 Jan 2022 14:49:43 +0100 Subject: [PATCH 0120/1363] Wrap lines --- src/oemof/solph/constraints/equate_flows.py | 25 ++++++++------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/oemof/solph/constraints/equate_flows.py b/src/oemof/solph/constraints/equate_flows.py index b427b93fa..bf6d9efa9 100644 --- a/src/oemof/solph/constraints/equate_flows.py +++ b/src/oemof/solph/constraints/equate_flows.py @@ -17,14 +17,11 @@ def equate_flows(model, flows1, flows2, factor1=1, name="equate_flows"): Adds a constraint to the given model that sets the sum of two groups of flows equal or proportional by a factor. """ + def _equate_flow_groups_rule(m): for ts in m.TIMESTEPS: - sum1_t = sum( - m.flow[fi, fo, ts] for fi, fo in flows1 - ) - sum2_t = sum( - m.flow[fi, fo, ts] for fi, fo in flows2 - ) + sum1_t = sum(m.flow[fi, fo, ts] for fi, fo in flows1) + sum2_t = sum(m.flow[fi, fo, ts] for fi, fo in flows2) expr = sum1_t * factor1 == sum2_t if expr is not True: getattr(m, name).add(ts, expr) @@ -43,10 +40,12 @@ def _equate_flow_groups_rule(m): return model -def equate_flows_by_keyword(model, keyword1, keyword2, factor1=1, name="equate_flows"): +def equate_flows_by_keyword( + model, keyword1, keyword2, factor1=1, name="equate_flows" +): r""" - This wrapper for equate_flows allows to equate groups of flows by using a keyword - instead of a list of flows. + This wrapper for equate_flows allows to equate groups of flows by using a + keyword instead of a list of flows. """ flows = {} for n, keyword in enumerate([keyword1, keyword2]): @@ -55,10 +54,4 @@ def equate_flows_by_keyword(model, keyword1, keyword2, factor1=1, name="equate_f if hasattr(model.flows[i, o], keyword): flows[n].append((i, o)) - return equate_flows( - model, - flows[0], - flows[1], - factor1=factor1, - name=name - ) + return equate_flows(model, flows[0], flows[1], factor1=factor1, name=name) From dba2db5b93933889f97c35ccc4d9ea7de8ecf3c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Tue, 8 Feb 2022 14:46:52 +0100 Subject: [PATCH 0121/1363] Raise error on missing nominal_value in Flow Fixes #818 --- src/oemof/solph/flows/_flow.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 4515a7794..5f87637e2 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -116,7 +116,7 @@ class and a MILP will be build) -------- Creating a fixed flow object: - >>> f = Flow(fix=[10, 4, 4], variable_costs=5) + >>> f = Flow(nominal_vlaue=2, fix=[10, 4, 4], variable_costs=5) >>> f.variable_costs[2] 5 >>> f.fix[2] @@ -182,6 +182,14 @@ def __init__(self, **kwargs): raise AttributeError( "It is not allowed to define min/max if fix is defined." ) + if kwargs.get("nominal_value") is None and ( + kwargs.get("fix") is not None + or kwargs.get("min") is not None + or kwargs.get("max") is not None + ): + raise AttributeError( + "The arguments min/max/fix need nominal_value to be defined." + ) # Set default value for min and max if kwargs.get("min") is None: From 467e98c8783bcfbfe6619accb4da423d1b3be110 Mon Sep 17 00:00:00 2001 From: jnnr <32454596+jnnr@users.noreply.github.com> Date: Tue, 8 Feb 2022 15:06:41 +0100 Subject: [PATCH 0122/1363] Drop investment and storage from test and add second source --- tests/constraint_tests.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index 6d38eadc1..4339e251c 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -886,29 +886,33 @@ def test_equate_variables_constraint(self): def test_equate_flows_constraint(self): """Testing the equate_flows function in the constraint module.""" bus1 = solph.buses.Bus(label="Bus1") - storage = solph.components.GenericStorage( - label="storage_constraint", - invest_relation_input_capacity=0.2, - invest_relation_output_capacity=0.2, - inputs={bus1: solph.flows.Flow()}, - outputs={bus1: solph.flows.Flow()}, - investment=solph.Investment(ep_costs=145), - ) sink = solph.components.Sink( label="Sink", inputs={ bus1: solph.flows.Flow( - investment=solph.Investment(ep_costs=500), + nominal_value=300, outgoing_flow=True, + variable_costs=2, ) }, ) - source = solph.components.Source( - label="Source", + source1 = solph.components.Source( + label="Source1", outputs={ bus1: solph.flows.Flow( - investment=solph.Investment(ep_costs=123), + nominal_value=400, incoming_flow=True, + variable_costs=2, + ) + }, + ) + source2 = solph.components.Source( + label="Source2", + outputs={ + bus1: solph.flows.Flow( + nominal_value=200, + incoming_flow=True, + variable_costs=10, ) }, ) From 726c60b6ac9dcde0afe72cb7101026685ca3afb4 Mon Sep 17 00:00:00 2001 From: jnnr <32454596+jnnr@users.noreply.github.com> Date: Tue, 8 Feb 2022 15:07:30 +0100 Subject: [PATCH 0123/1363] Drop unnecessary assignments --- tests/constraint_tests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index 4339e251c..b88e7283b 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -886,7 +886,7 @@ def test_equate_variables_constraint(self): def test_equate_flows_constraint(self): """Testing the equate_flows function in the constraint module.""" bus1 = solph.buses.Bus(label="Bus1") - sink = solph.components.Sink( + solph.components.Sink( label="Sink", inputs={ bus1: solph.flows.Flow( @@ -896,7 +896,7 @@ def test_equate_flows_constraint(self): ) }, ) - source1 = solph.components.Source( + solph.components.Source( label="Source1", outputs={ bus1: solph.flows.Flow( @@ -906,7 +906,7 @@ def test_equate_flows_constraint(self): ) }, ) - source2 = solph.components.Source( + solph.components.Source( label="Source2", outputs={ bus1: solph.flows.Flow( From 3db429b131ca548892d8626c9505316a32e18499 Mon Sep 17 00:00:00 2001 From: jnnr <32454596+jnnr@users.noreply.github.com> Date: Tue, 8 Feb 2022 15:10:16 +0100 Subject: [PATCH 0124/1363] Update expected lp-file --- tests/lp_files/equate_flows.lp | 186 ++++++--------------------------- 1 file changed, 32 insertions(+), 154 deletions(-) diff --git a/tests/lp_files/equate_flows.lp b/tests/lp_files/equate_flows.lp index 5b252d464..8a84f844c 100644 --- a/tests/lp_files/equate_flows.lp +++ b/tests/lp_files/equate_flows.lp @@ -1,188 +1,66 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+145 GenericInvestmentStorageBlock_invest(storage_constraint) -+500 InvestmentFlowBlock_invest(Bus1_Sink) -+123 InvestmentFlowBlock_invest(Source_Bus1) ++2 flow(Bus1_Sink_0) ++2 flow(Bus1_Sink_1) ++2 flow(Bus1_Sink_2) ++2 flow(Source1_Bus1_0) ++2 flow(Source1_Bus1_1) ++2 flow(Source1_Bus1_2) ++10 flow(Source2_Bus1_0) ++10 flow(Source2_Bus1_1) ++10 flow(Source2_Bus1_2) s.t. c_e_equate_flows(0)_: -1 flow(Bus1_Sink_0) -+2 flow(Source_Bus1_0) ++2 flow(Source1_Bus1_0) ++2 flow(Source2_Bus1_0) = 0 c_e_equate_flows(1)_: -1 flow(Bus1_Sink_1) -+2 flow(Source_Bus1_1) ++2 flow(Source1_Bus1_1) ++2 flow(Source2_Bus1_1) = 0 c_e_equate_flows(2)_: -1 flow(Bus1_Sink_2) -+2 flow(Source_Bus1_2) ++2 flow(Source1_Bus1_2) ++2 flow(Source2_Bus1_2) = 0 c_e_BusBlock_balance(Bus1_0)_: -1 flow(Bus1_Sink_0) --1 flow(Bus1_storage_constraint_0) -+1 flow(Source_Bus1_0) -+1 flow(storage_constraint_Bus1_0) ++1 flow(Source1_Bus1_0) ++1 flow(Source2_Bus1_0) = 0 c_e_BusBlock_balance(Bus1_1)_: -1 flow(Bus1_Sink_1) --1 flow(Bus1_storage_constraint_1) -+1 flow(Source_Bus1_1) -+1 flow(storage_constraint_Bus1_1) ++1 flow(Source1_Bus1_1) ++1 flow(Source2_Bus1_1) = 0 c_e_BusBlock_balance(Bus1_2)_: -1 flow(Bus1_Sink_2) --1 flow(Bus1_storage_constraint_2) -+1 flow(Source_Bus1_2) -+1 flow(storage_constraint_Bus1_2) ++1 flow(Source1_Bus1_2) ++1 flow(Source2_Bus1_2) = 0 -c_u_InvestmentFlowBlock_max(Bus1_Sink_0)_: --1 InvestmentFlowBlock_invest(Bus1_Sink) -+1 flow(Bus1_Sink_0) -<= 0 - -c_u_InvestmentFlowBlock_max(Bus1_Sink_1)_: --1 InvestmentFlowBlock_invest(Bus1_Sink) -+1 flow(Bus1_Sink_1) -<= 0 - -c_u_InvestmentFlowBlock_max(Bus1_Sink_2)_: --1 InvestmentFlowBlock_invest(Bus1_Sink) -+1 flow(Bus1_Sink_2) -<= 0 - -c_u_InvestmentFlowBlock_max(Bus1_storage_constraint_0)_: --1 InvestmentFlowBlock_invest(Bus1_storage_constraint) -+1 flow(Bus1_storage_constraint_0) -<= 0 - -c_u_InvestmentFlowBlock_max(Bus1_storage_constraint_1)_: --1 InvestmentFlowBlock_invest(Bus1_storage_constraint) -+1 flow(Bus1_storage_constraint_1) -<= 0 - -c_u_InvestmentFlowBlock_max(Bus1_storage_constraint_2)_: --1 InvestmentFlowBlock_invest(Bus1_storage_constraint) -+1 flow(Bus1_storage_constraint_2) -<= 0 - -c_u_InvestmentFlowBlock_max(Source_Bus1_0)_: --1 InvestmentFlowBlock_invest(Source_Bus1) -+1 flow(Source_Bus1_0) -<= 0 - -c_u_InvestmentFlowBlock_max(Source_Bus1_1)_: --1 InvestmentFlowBlock_invest(Source_Bus1) -+1 flow(Source_Bus1_1) -<= 0 - -c_u_InvestmentFlowBlock_max(Source_Bus1_2)_: --1 InvestmentFlowBlock_invest(Source_Bus1) -+1 flow(Source_Bus1_2) -<= 0 - -c_u_InvestmentFlowBlock_max(storage_constraint_Bus1_0)_: --1 InvestmentFlowBlock_invest(storage_constraint_Bus1) -+1 flow(storage_constraint_Bus1_0) -<= 0 - -c_u_InvestmentFlowBlock_max(storage_constraint_Bus1_1)_: --1 InvestmentFlowBlock_invest(storage_constraint_Bus1) -+1 flow(storage_constraint_Bus1_1) -<= 0 - -c_u_InvestmentFlowBlock_max(storage_constraint_Bus1_2)_: --1 InvestmentFlowBlock_invest(storage_constraint_Bus1) -+1 flow(storage_constraint_Bus1_2) -<= 0 - -c_u_GenericInvestmentStorageBlock_init_content_limit(storage_constraint)_: -+1 GenericInvestmentStorageBlock_init_content(storage_constraint) --1 GenericInvestmentStorageBlock_invest(storage_constraint) -<= 0 - -c_e_GenericInvestmentStorageBlock_balance_first(storage_constraint)_: --1 GenericInvestmentStorageBlock_init_content(storage_constraint) -+1 GenericInvestmentStorageBlock_storage_content(storage_constraint_0) --1 flow(Bus1_storage_constraint_0) -+1 flow(storage_constraint_Bus1_0) -= 0 - -c_e_GenericInvestmentStorageBlock_balance(storage_constraint_1)_: --1 GenericInvestmentStorageBlock_storage_content(storage_constraint_0) -+1 GenericInvestmentStorageBlock_storage_content(storage_constraint_1) --1 flow(Bus1_storage_constraint_1) -+1 flow(storage_constraint_Bus1_1) -= 0 - -c_e_GenericInvestmentStorageBlock_balance(storage_constraint_2)_: --1 GenericInvestmentStorageBlock_storage_content(storage_constraint_1) -+1 GenericInvestmentStorageBlock_storage_content(storage_constraint_2) --1 flow(Bus1_storage_constraint_2) -+1 flow(storage_constraint_Bus1_2) -= 0 - -c_e_GenericInvestmentStorageBlock_balanced_cstr(storage_constraint)_: --1 GenericInvestmentStorageBlock_init_content(storage_constraint) -+1 GenericInvestmentStorageBlock_storage_content(storage_constraint_2) -= 0 - -c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_constraint)_: --0.20000000000000001 GenericInvestmentStorageBlock_invest(storage_constraint) -+1 InvestmentFlowBlock_invest(Bus1_storage_constraint) -= 0 - -c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_constraint)_: --0.20000000000000001 GenericInvestmentStorageBlock_invest(storage_constraint) -+1 InvestmentFlowBlock_invest(storage_constraint_Bus1) -= 0 - -c_u_GenericInvestmentStorageBlock_max_storage_content(storage_constraint_0)_: --1 GenericInvestmentStorageBlock_invest(storage_constraint) -+1 GenericInvestmentStorageBlock_storage_content(storage_constraint_0) -<= 0 - -c_u_GenericInvestmentStorageBlock_max_storage_content(storage_constraint_1)_: --1 GenericInvestmentStorageBlock_invest(storage_constraint) -+1 GenericInvestmentStorageBlock_storage_content(storage_constraint_1) -<= 0 - -c_u_GenericInvestmentStorageBlock_max_storage_content(storage_constraint_2)_: --1 GenericInvestmentStorageBlock_invest(storage_constraint) -+1 GenericInvestmentStorageBlock_storage_content(storage_constraint_2) -<= 0 - -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(Bus1_Sink_0) <= +inf - 0 <= flow(Bus1_Sink_1) <= +inf - 0 <= flow(Bus1_Sink_2) <= +inf - 0 <= flow(Bus1_storage_constraint_0) <= +inf - 0 <= flow(Bus1_storage_constraint_1) <= +inf - 0 <= flow(Bus1_storage_constraint_2) <= +inf - 0 <= flow(Source_Bus1_0) <= +inf - 0 <= flow(Source_Bus1_1) <= +inf - 0 <= flow(Source_Bus1_2) <= +inf - 0 <= flow(storage_constraint_Bus1_0) <= +inf - 0 <= flow(storage_constraint_Bus1_1) <= +inf - 0 <= flow(storage_constraint_Bus1_2) <= +inf - 0 <= InvestmentFlowBlock_invest(Bus1_Sink) <= +inf - 0 <= InvestmentFlowBlock_invest(Bus1_storage_constraint) <= +inf - 0 <= InvestmentFlowBlock_invest(Source_Bus1) <= +inf - 0 <= InvestmentFlowBlock_invest(storage_constraint_Bus1) <= +inf - 0 <= GenericInvestmentStorageBlock_storage_content(storage_constraint_0) <= +inf - 0 <= GenericInvestmentStorageBlock_storage_content(storage_constraint_1) <= +inf - 0 <= GenericInvestmentStorageBlock_storage_content(storage_constraint_2) <= +inf - 0 <= GenericInvestmentStorageBlock_invest(storage_constraint) <= +inf - 0 <= GenericInvestmentStorageBlock_init_content(storage_constraint) <= +inf + 0 <= flow(Bus1_Sink_0) <= 300 + 0 <= flow(Bus1_Sink_1) <= 300 + 0 <= flow(Bus1_Sink_2) <= 300 + 0 <= flow(Source1_Bus1_0) <= 400 + 0 <= flow(Source1_Bus1_1) <= 400 + 0 <= flow(Source1_Bus1_2) <= 400 + 0 <= flow(Source2_Bus1_0) <= 200 + 0 <= flow(Source2_Bus1_1) <= 200 + 0 <= flow(Source2_Bus1_2) <= 200 end From 3d0f464c137f0709915ab0fd30cf006576db79be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Tue, 8 Feb 2022 17:00:46 +0100 Subject: [PATCH 0125/1363] Fix typo --- src/oemof/solph/flows/_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 5f87637e2..828ac1a78 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -116,7 +116,7 @@ class and a MILP will be build) -------- Creating a fixed flow object: - >>> f = Flow(nominal_vlaue=2, fix=[10, 4, 4], variable_costs=5) + >>> f = Flow(nominal_value=2, fix=[10, 4, 4], variable_costs=5) >>> f.variable_costs[2] 5 >>> f.fix[2] From d20f6bbe36c55ba87352c8b8de8668916c439f9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Tue, 8 Feb 2022 17:32:09 +0100 Subject: [PATCH 0126/1363] Fix Investment in Flow not being possible --- src/oemof/solph/flows/_flow.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 828ac1a78..a727cf740 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -182,13 +182,17 @@ def __init__(self, **kwargs): raise AttributeError( "It is not allowed to define min/max if fix is defined." ) - if kwargs.get("nominal_value") is None and ( + if ( + kwargs.get("nominal_value") is None + and kwargs.get("investment") is None + ) and ( kwargs.get("fix") is not None or kwargs.get("min") is not None or kwargs.get("max") is not None ): raise AttributeError( - "The arguments min/max/fix need nominal_value to be defined." + "The arguments min/max/fix need either" + + "nominal_value or investment to be defined as well." ) # Set default value for min and max From ad2873f528f0c2fdb8fcc43481bbf2b4be0f93d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Tue, 8 Feb 2022 17:32:37 +0100 Subject: [PATCH 0127/1363] Fix test for bidirectional Flow --- tests/test_solph_network_classes.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/test_solph_network_classes.py b/tests/test_solph_network_classes.py index 10e114f09..d92ada384 100644 --- a/tests/test_solph_network_classes.py +++ b/tests/test_solph_network_classes.py @@ -93,13 +93,20 @@ def test_flow_with_fix_and_min_max(): def test_min_max_values_for_bidirectional_flow(): - a = solph.flows.Flow(bidirectional=True) # use default values - b = solph.flows.Flow(bidirectional=True, min=-0.9, max=0.9) + a = solph.flows.Flow( + bidirectional=True, + nominal_value=1, + ) # use default values + b = solph.flows.Flow( + bidirectional=True, + nominal_value=1, + min=-0.8, + max=0.9) assert a.bidirectional assert a.max[0] == 1 assert a.min[0] == -1 assert b.max[0] == 0.9 - assert b.min[0] == -0.9 + assert b.min[0] == -0.8 def test_deprecated_actual_value(): From 6b0987a548176f3b63c130c67a34a657ace05f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Tue, 8 Feb 2022 17:40:13 +0100 Subject: [PATCH 0128/1363] Add unit test for check for meaningful min/max/fix --- src/oemof/solph/flows/_flow.py | 6 +++--- tests/test_solph_network_classes.py | 10 +++++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index a727cf740..3cd7ea880 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -180,7 +180,7 @@ def __init__(self, **kwargs): kwargs.get("min") is not None or kwargs.get("max") is not None ): raise AttributeError( - "It is not allowed to define min/max if fix is defined." + "It is not allowed to define `min`/`max` if `fix` is defined." ) if ( kwargs.get("nominal_value") is None @@ -191,8 +191,8 @@ def __init__(self, **kwargs): or kwargs.get("max") is not None ): raise AttributeError( - "The arguments min/max/fix need either" - + "nominal_value or investment to be defined as well." + "The arguments `min`/`max`/`fix` need either" + + "`nominal_value` or `investment` to be defined as well." ) # Set default value for min and max diff --git a/tests/test_solph_network_classes.py b/tests/test_solph_network_classes.py index d92ada384..8f47d78a4 100644 --- a/tests/test_solph_network_classes.py +++ b/tests/test_solph_network_classes.py @@ -83,7 +83,7 @@ def test_error_of_deprecated_fixed_costs(): def test_flow_with_fix_and_min_max(): - msg = "It is not allowed to define min/max if fix is defined." + msg = "It is not allowed to define `min`/`max` if `fix` is defined." with pytest.raises(AttributeError, match=msg): solph.flows.Flow(fix=[1, 3], min=[0, 5]) with pytest.raises(AttributeError, match=msg): @@ -109,6 +109,14 @@ def test_min_max_values_for_bidirectional_flow(): assert b.min[0] == -0.8 +def test_limit_without_nominal_value(): + """Deprecated error for actual_warning is not raised correctly.""" + msg = ("The arguments `min`/`max`/`fix` need either" + + "`nominal_value` or `investment` to be defined as well.") + with pytest.raises(AttributeError, match=msg): + solph.flows.Flow(max=0.8) + + def test_deprecated_actual_value(): """Deprecated error for actual_warning is not raised correctly.""" msg = "The `actual_value` attribute has been renamed to `fix`" From 0c08c1bcf30973c57777abb113977d488dfb5a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Tue, 8 Feb 2022 17:42:04 +0100 Subject: [PATCH 0129/1363] Fix line alignment --- tests/test_solph_network_classes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_solph_network_classes.py b/tests/test_solph_network_classes.py index 8f47d78a4..59a5d6bc9 100644 --- a/tests/test_solph_network_classes.py +++ b/tests/test_solph_network_classes.py @@ -112,7 +112,7 @@ def test_min_max_values_for_bidirectional_flow(): def test_limit_without_nominal_value(): """Deprecated error for actual_warning is not raised correctly.""" msg = ("The arguments `min`/`max`/`fix` need either" - + "`nominal_value` or `investment` to be defined as well.") + + "`nominal_value` or `investment` to be defined as well.") with pytest.raises(AttributeError, match=msg): solph.flows.Flow(max=0.8) From 21836228872b14669b4010e5b53a7264cde348b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Tue, 8 Feb 2022 17:46:38 +0100 Subject: [PATCH 0130/1363] Adhere to Black style --- tests/test_solph_network_classes.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_solph_network_classes.py b/tests/test_solph_network_classes.py index 59a5d6bc9..66f8e12be 100644 --- a/tests/test_solph_network_classes.py +++ b/tests/test_solph_network_classes.py @@ -98,10 +98,8 @@ def test_min_max_values_for_bidirectional_flow(): nominal_value=1, ) # use default values b = solph.flows.Flow( - bidirectional=True, - nominal_value=1, - min=-0.8, - max=0.9) + bidirectional=True, nominal_value=1, min=-0.8, max=0.9 + ) assert a.bidirectional assert a.max[0] == 1 assert a.min[0] == -1 @@ -111,8 +109,10 @@ def test_min_max_values_for_bidirectional_flow(): def test_limit_without_nominal_value(): """Deprecated error for actual_warning is not raised correctly.""" - msg = ("The arguments `min`/`max`/`fix` need either" - + "`nominal_value` or `investment` to be defined as well.") + msg = ( + "The arguments `min`/`max`/`fix` need either" + + "`nominal_value` or `investment` to be defined as well." + ) with pytest.raises(AttributeError, match=msg): solph.flows.Flow(max=0.8) From 777d40e36043b525abe88dcc55f1a01948a21106 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 11 Feb 2022 18:26:57 +0100 Subject: [PATCH 0131/1363] Change from error to warning and tidy up imports and formatting --- .../solph/components/_generic_storage.py | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index d70c1fd8b..6e232ebe4 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -24,7 +24,6 @@ from oemof.network import network from oemof.tools import debugging from oemof.tools import economics -from pyomo.core import value from pyomo.core.base.block import SimpleBlock from pyomo.environ import Binary from pyomo.environ import BuildAction @@ -811,20 +810,21 @@ def _create(self, group=None): # ########################## CHECKS ################################### if m.es.multi_period: for n in group: - e1 = ( + error = ( "For a multi-period model, fixed absolute losses" " are not supported. Please remove parameter." ) if n.fixed_losses_absolute.default != 0: - raise ValueError(e1) - e2 = ( + raise ValueError(error) + warning = ( "For a multi-period model, initial_storage_level is" - " not supported. Please remove parameter. storage_content" - " will be zero, until there is some storage capacity" - " installed." + " not supported.\nIt is suggested to remove that" + " parameter since it has no effect.\nstorage_content" + " will be zero, until there is some usable storage " + " capacity installed." ) if n.initial_storage_level is not None: - raise ValueError(e2) + warn(warning, debugging.SuspiciousUsageWarning) # ########################## SETS ##################################### @@ -1101,11 +1101,8 @@ def _inv_storage_init_content_max_rule(block, n): def _inv_storage_init_content_fix_rule(block, n): """Constraint for a fixed initial storage capacity.""" - return ( - block.init_content[n] - == n.initial_storage_level * ( - n.investment.existing + block.invest[n, 0] - ) + return block.init_content[n] == n.initial_storage_level * ( + n.investment.existing + block.invest[n, 0] ) self.init_content_fix = Constraint( @@ -1237,8 +1234,7 @@ def _storage_capacity_outflow_invest_rule(block): for p in m.PERIODS: expr = ( m.InvestmentFlowBlock.total[n, o[n], p] - == self.total[n, p] - * n.invest_relation_output_capacity + == self.total[n, p] * n.invest_relation_output_capacity ) self.storage_capacity_outflow.add((n, p), expr) From a39557a32bd4fa955f6995d3b3edd02258311e4a Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 11 Feb 2022 18:28:03 +0100 Subject: [PATCH 0132/1363] Fix gradient constraint for proper results extraction TODO: Check whether this needs an issue and bug fix within the status quo of the framework --- src/oemof/solph/flows/_flow.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 95c9b733e..da80ede59 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -495,7 +495,17 @@ def _positive_gradient_flow_rule(model): lhs <= rhs, ) else: - pass # return(Constraint.Skip) + lhs = self.positive_gradient[inp, out, 0] + rhs = 0 + self.positive_gradient_constr.add( + ( + inp, + out, + m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1], + ), + lhs == rhs, + ) self.positive_gradient_constr = Constraint( self.POSITIVE_GRADIENT_FLOWS, m.TIMEINDEX, noruleinit=True @@ -536,7 +546,17 @@ def _negative_gradient_flow_rule(model): lhs <= rhs, ) else: - pass # return(Constraint.Skip) + lhs = self.negative_gradient[inp, out, 0] + rhs = 0 + self.negative_gradient_constr.add( + ( + inp, + out, + m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1], + ), + lhs == rhs, + ) self.negative_gradient_constr = Constraint( self.NEGATIVE_GRADIENT_FLOWS, m.TIMEINDEX, noruleinit=True From e76229dbf92a4753ed9834733471b3c98bb65656 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 11 Feb 2022 18:30:07 +0100 Subject: [PATCH 0133/1363] Adjust processing to work with both, multi-period and standard models --- src/oemof/solph/processing.py | 141 ++++++++++++++++++++++++---------- 1 file changed, 101 insertions(+), 40 deletions(-) diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index d8761e188..19dac720d 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -18,6 +18,7 @@ import sys from itertools import groupby +import numpy as np import pandas as pd from oemof.network.network import Node from pyomo.core.base.piecewise import IndexedPiecewise @@ -155,6 +156,12 @@ def results(om): # Extraction steps that are the same for both model types df = create_dataframe(om) period_indexed = ["invest", "total", "old", "old_end", "old_exo"] + period_timestep_indexed = ["flow"] + timestep_indexed = [ + el + for el in df["variable_name"].unique() + if el not in period_indexed and el not in period_timestep_indexed + ] # create a dict of dataframes keyed by oemof tuples df_dict = { @@ -169,14 +176,19 @@ def results(om): # Standard model results extraction if not om.es.multi_period: result = _extract_standard_model_result( - om, df_dict, period_indexed, result + om, + df_dict, + period_indexed, + timestep_indexed, + period_timestep_indexed, + result, ) scalars_col = "scalars" # Results extraction for a multi-period model else: result = _extract_multi_period_model_result( - om, df, df_dict, period_indexed, result + om, df_dict, timestep_indexed, period_timestep_indexed, result ) scalars_col = "period_scalars" @@ -203,7 +215,12 @@ def results(om): def _extract_standard_model_result( - om, df_dict, period_indexed=None, result=None + om, + df_dict, + period_indexed=None, + timestep_indexed=None, + period_timestep_indexed=None, + result=None, ): """Extract and return the results of a standard model @@ -215,37 +232,30 @@ def _extract_standard_model_result( dictionary of results DataFrames period_indexed : list list of variables that are indexed by periods + timestep_indexed : list + list of variables that are indexed by timesteps + period_timestep_indexed : list + list of variables that are indexed by period and timesteps (timeindex) result : dict dictionary to store the results + + Returns + ------- + result : dict + dictionary with results stored """ for k in df_dict: df_dict[k].set_index("timeindex", inplace=True) df_dict[k] = df_dict[k].pivot(columns="variable_name", values="value") try: - # Enable reindexing by replacing period by first timeindex - try: - df_dict[k].loc[ - [(0, 0)], - [ - col - for col in df_dict[k].columns - if col in period_indexed - ], - ] = ( - df_dict[k] - .loc[ - [(0,)], - [ - col - for col in df_dict[k].columns - if col in period_indexed - ], - ] - .values - ) - df_dict[k].drop(index=[(0,)], inplace=True) - except KeyError: - pass + # Enable reindexing by replacing period and timestep indices + print(k) + df_dict[k] = _replace_non_timeindex_indices( + df_dict[k], + period_indexed, + timestep_indexed, + period_timestep_indexed, + ) df_dict[k].index = om.es.timeindex except ValueError as e: msg = ( @@ -271,8 +281,65 @@ def _extract_standard_model_result( return result +def _replace_non_timeindex_indices( + df, period_indexed, timestep_indexed, period_timestep_indexed +): + """Replace period and timestpes indices by timeindex values + + Parameters + ---------- + df : pd.DataFrame + DataFrame obtained from the dict of results DataFrames + period_indexed : list + List of all values that are indexed by periods + timestep_indexed : list + List of all values that are indexed by timesteps + period_timestep_indexed : list + list of variables that are indexed by period and timesteps (timeindex) + + Returns + ------- + df : pd.DataFrame + Manipulated DataFrame containing only timestep indices + """ + rename_dict = {key: (0, key[0]) for key in df.index if len(key) == 1} + to_concat = [] + # Split into different data sets dependent on indexation + period_timestep_indexed_df = df[ + [col for col in df.columns if col in period_timestep_indexed] + ] + timestep_indexed_df = df[ + [col for col in df.columns if col in timestep_indexed] + ] + period_indexed_df = df[ + [col for col in df.columns if col in period_indexed] + ] + + period_timestep_indexed_df = period_timestep_indexed_df.dropna() + if not period_timestep_indexed_df.empty: + to_concat.append(period_timestep_indexed_df) + + period_indexed_df = period_indexed_df.dropna() + period_indexed_df = period_indexed_df.rename(index={(0,): (0, 0)}) + if not period_indexed_df.empty: + to_concat.append(period_indexed_df) + + timestep_indexed_df = timestep_indexed_df.dropna() + timestep_indexed_df = timestep_indexed_df.rename(index=rename_dict) + if not timestep_indexed_df.empty: + to_concat.append(timestep_indexed_df) + + df = pd.concat(to_concat, axis=1) + + return df + + def _extract_multi_period_model_result( - om, df, df_dict, period_indexed=None, result=None + om, + df_dict, + timestep_indexed=None, + period_timestep_indexed=None, + result=None, ): """Extract and return the results of a multi-period model @@ -280,24 +347,18 @@ def _extract_multi_period_model_result( ---------- om : oemof.solph.models.Model The ptimization model - df : DataFrame - DataFrame containing all results df_dict : dict dictionary of results DataFrames - period_indexed : list - list of variables that are indexed by periods + timestep_indexed : list + list of variables that are indexed by timesteps + period_timestep_indexed : list + list of variables that are indexed by periods and timesteps (timeindex) result : dict dictionary to store the results """ - period_timestep_indexed = ["flow"] - # TODO: Take care of initial storage content instead of just ignoring to_be_ignored = ["init_content"] timestep_indexed = [ - el - for el in df["variable_name"].unique() - if el not in period_indexed - and el not in period_timestep_indexed - and el not in to_be_ignored + var for var in timestep_indexed if var not in to_be_ignored ] for k in df_dict: @@ -308,7 +369,7 @@ def _extract_multi_period_model_result( timeindex_cols = [ col for col in df_dict[k].columns - if col in timestep_indexed or col == "flow" + if col in timestep_indexed or col in period_timestep_indexed ] period_cols = [ col for col in df_dict[k].columns if col not in timeindex_cols From 785ebbb2c3fb9487342c4569c1998f474fe8dd3d Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 12 Feb 2022 14:00:54 +0100 Subject: [PATCH 0134/1363] Add bug fix --- src/oemof/solph/components/_generic_storage.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index 6e232ebe4..fc1b7ac39 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -539,9 +539,9 @@ def _power_coupled(block): for n in self.STORAGES_WITH_INVEST_FLOW_REL: for p in m.PERIODS: expr = ( - m.InvestmentFlow.total[n, o[n], p] + m.InvestmentFlowBlock.total[n, o[n], p] ) * n.invest_relation_input_output == ( - m.InvestmentFlow.total[i[n], n, p] + m.InvestmentFlowBlock.total[i[n], n, p] ) self.power_coupled.add((n, p), expr) From 878743d2c85e8cc5c7df8fb50e89234e054f01ae Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 12 Feb 2022 14:01:17 +0100 Subject: [PATCH 0135/1363] Begin adjusting constraint tests --- tests/constraint_tests.py | 12 +- tests/lp_files/fixed_source_invest_sink.lp | 72 ++++---- tests/lp_files/fixed_source_variable_sink.lp | 28 ++-- tests/lp_files/invest_source_fixed_sink.lp | 56 ++++--- tests/lp_files/linear_transformer.lp | 64 ++++---- tests/lp_files/linear_transformer_invest.lp | 92 ++++++----- tests/lp_files/max_source_min_sink.lp | 40 ++--- tests/lp_files/nominal_value_to_zero.lp | 22 +-- tests/lp_files/storage.lp | 62 +++---- tests/lp_files/storage_invest_1.lp | 162 ++++++++++-------- tests/lp_files/storage_invest_2.lp | 130 ++++++++------- tests/lp_files/storage_invest_3.lp | 106 ++++++------ tests/lp_files/storage_invest_4.lp | 74 +++++---- tests/lp_files/storage_invest_5.lp | 134 ++++++++------- tests/lp_files/storage_invest_6.lp | 164 ++++++++++--------- tests/lp_files/storage_invest_minimum.lp | 74 +++++---- tests/lp_files/storage_invest_unbalanced.lp | 138 +++++++++------- tests/lp_files/storage_unbalanced.lp | 50 +++--- 18 files changed, 803 insertions(+), 677 deletions(-) diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index 88515d891..f60755d0e 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -209,7 +209,7 @@ def test_fixed_source_variable_sink(self): label="wind", outputs={ bel: solph.flows.Flow( - fix=[0.43, 0.72, 0.29], nominal_value=10e5 + fix=[0.43, 0.72, 0.29], nominal_value=1e6 ) }, ) @@ -230,14 +230,14 @@ def test_nominal_value_to_zero(self): self.compare_lp_files("nominal_value_to_zero.lp") def test_fixed_source_invest_sink(self): - """Wrong constraints for fixed source + invest sink w. `summed_max`.""" + """Constraints test for fixed source + invest sink w. `summed_max`""" bel = solph.buses.Bus(label="electricityBus") solph.components.Source( label="wind", outputs={ - bel: solph.flows.Flow(fix=[12, 16, 14], nominal_value=1000000) + bel: solph.flows.Flow(fix=[12, 16, 14], nominal_value=1e6) }, ) @@ -249,7 +249,7 @@ def test_fixed_source_invest_sink(self): variable_costs=25, max=0.8, investment=solph.Investment( - ep_costs=500, maximum=10e5, existing=50 + ep_costs=500, maximum=1e6, existing=50 ), ) }, @@ -276,7 +276,7 @@ def test_invest_source_fixed_sink(self): solph.components.Sink( label="excess", inputs={ - bel: solph.flows.Flow(fix=[0.5, 0.8, 0.3], nominal_value=10e4) + bel: solph.flows.Flow(fix=[0.5, 0.8, 0.3], nominal_value=1e5) }, ) @@ -294,7 +294,7 @@ def test_storage(self): outputs={ bel: solph.flows.Flow(nominal_value=16667, variable_costs=24) }, - nominal_storage_capacity=10e4, + nominal_storage_capacity=1e5, loss_rate=0.13, inflow_conversion_factor=0.97, outflow_conversion_factor=0.86, diff --git a/tests/lp_files/fixed_source_invest_sink.lp b/tests/lp_files/fixed_source_invest_sink.lp index e94c520a9..90261218e 100644 --- a/tests/lp_files/fixed_source_invest_sink.lp +++ b/tests/lp_files/fixed_source_invest_sink.lp @@ -1,54 +1,60 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+500 InvestmentFlowBlock_invest(electricityBus_excess) -+25 flow(electricityBus_excess_0) -+25 flow(electricityBus_excess_1) -+25 flow(electricityBus_excess_2) ++500 InvestmentFlowBlock_invest(electricityBus_excess_0) ++25 flow(electricityBus_excess_0_0) ++25 flow(electricityBus_excess_0_1) ++25 flow(electricityBus_excess_0_2) s.t. -c_e_BusBlock_balance(electricityBus_0)_: --1 flow(electricityBus_excess_0) +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_excess_0_0) = -12000000 -c_e_BusBlock_balance(electricityBus_1)_: --1 flow(electricityBus_excess_1) +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_excess_0_1) = -16000000 -c_e_BusBlock_balance(electricityBus_2)_: --1 flow(electricityBus_excess_2) +c_e_BusBlock_balance(electricityBus_0_2)_: +-1 flow(electricityBus_excess_0_2) = -14000000 -c_u_InvestmentFlowBlock_max(electricityBus_excess_0)_: --0.80000000000000004 InvestmentFlowBlock_invest(electricityBus_excess) -+1 flow(electricityBus_excess_0) -<= 40 +c_e_InvestmentFlowBlock_total_rule(electricityBus_excess_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_excess_0) ++1 InvestmentFlowBlock_total(electricityBus_excess_0) += 50 -c_u_InvestmentFlowBlock_max(electricityBus_excess_1)_: --0.80000000000000004 InvestmentFlowBlock_invest(electricityBus_excess) -+1 flow(electricityBus_excess_1) -<= 40 +c_u_InvestmentFlowBlock_max(electricityBus_excess_0_0)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_excess_0) ++1 flow(electricityBus_excess_0_0) +<= 0 -c_u_InvestmentFlowBlock_max(electricityBus_excess_2)_: --0.80000000000000004 InvestmentFlowBlock_invest(electricityBus_excess) -+1 flow(electricityBus_excess_2) -<= 40 +c_u_InvestmentFlowBlock_max(electricityBus_excess_0_1)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_excess_0) ++1 flow(electricityBus_excess_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_excess_0_2)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_excess_0) ++1 flow(electricityBus_excess_0_2) +<= 0 c_u_InvestmentFlowBlock_summed_max(electricityBus_excess)_: --2.2999999999999998 InvestmentFlowBlock_invest(electricityBus_excess) -+1 flow(electricityBus_excess_0) -+1 flow(electricityBus_excess_1) -+1 flow(electricityBus_excess_2) -<= 114.99999999999999 +-2.2999999999999998 InvestmentFlowBlock_total(electricityBus_excess_0) ++1 flow(electricityBus_excess_0_0) ++1 flow(electricityBus_excess_0_1) ++1 flow(electricityBus_excess_0_2) +<= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(electricityBus_excess_0) <= +inf - 0 <= flow(electricityBus_excess_1) <= +inf - 0 <= flow(electricityBus_excess_2) <= +inf - 0 <= InvestmentFlowBlock_invest(electricityBus_excess) <= 1000000 + 0 <= flow(electricityBus_excess_0_0) <= +inf + 0 <= flow(electricityBus_excess_0_1) <= +inf + 0 <= flow(electricityBus_excess_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_excess_0) <= 1000000 + 0 <= InvestmentFlowBlock_total(electricityBus_excess_0) <= +inf end diff --git a/tests/lp_files/fixed_source_variable_sink.lp b/tests/lp_files/fixed_source_variable_sink.lp index 18a3dc979..8169d6a69 100644 --- a/tests/lp_files/fixed_source_variable_sink.lp +++ b/tests/lp_files/fixed_source_variable_sink.lp @@ -1,30 +1,30 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+40 flow(electricityBus_excess_0) -+40 flow(electricityBus_excess_1) -+40 flow(electricityBus_excess_2) ++40 flow(electricityBus_excess_0_0) ++40 flow(electricityBus_excess_0_1) ++40 flow(electricityBus_excess_0_2) s.t. -c_e_BusBlock_balance(electricityBus_0)_: --1 flow(electricityBus_excess_0) +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_excess_0_0) = -430000 -c_e_BusBlock_balance(electricityBus_1)_: --1 flow(electricityBus_excess_1) +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_excess_0_1) = -720000 -c_e_BusBlock_balance(electricityBus_2)_: --1 flow(electricityBus_excess_2) +c_e_BusBlock_balance(electricityBus_0_2)_: +-1 flow(electricityBus_excess_0_2) = -290000 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(electricityBus_excess_0) <= +inf - 0 <= flow(electricityBus_excess_1) <= +inf - 0 <= flow(electricityBus_excess_2) <= +inf + 0 <= flow(electricityBus_excess_0_0) <= +inf + 0 <= flow(electricityBus_excess_0_1) <= +inf + 0 <= flow(electricityBus_excess_0_2) <= +inf end diff --git a/tests/lp_files/invest_source_fixed_sink.lp b/tests/lp_files/invest_source_fixed_sink.lp index b6eff5d02..318477847 100644 --- a/tests/lp_files/invest_source_fixed_sink.lp +++ b/tests/lp_files/invest_source_fixed_sink.lp @@ -1,47 +1,53 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+123 InvestmentFlowBlock_invest(pv_electricityBus) -+13 flow(pv_electricityBus_0) -+13 flow(pv_electricityBus_1) -+13 flow(pv_electricityBus_2) ++123 InvestmentFlowBlock_invest(pv_electricityBus_0) ++13 flow(pv_electricityBus_0_0) ++13 flow(pv_electricityBus_0_1) ++13 flow(pv_electricityBus_0_2) s.t. -c_e_BusBlock_balance(electricityBus_0)_: -+1 flow(pv_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(pv_electricityBus_0_0) = 50000 -c_e_BusBlock_balance(electricityBus_1)_: -+1 flow(pv_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(pv_electricityBus_0_1) = 80000 -c_e_BusBlock_balance(electricityBus_2)_: -+1 flow(pv_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(pv_electricityBus_0_2) = 30000 -c_u_InvestmentFlowBlock_max(pv_electricityBus_0)_: --45 InvestmentFlowBlock_invest(pv_electricityBus) -+1 flow(pv_electricityBus_0) +c_e_InvestmentFlowBlock_total_rule(pv_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(pv_electricityBus_0) ++1 InvestmentFlowBlock_total(pv_electricityBus_0) += 0 + +c_u_InvestmentFlowBlock_max(pv_electricityBus_0_0)_: +-45 InvestmentFlowBlock_total(pv_electricityBus_0) ++1 flow(pv_electricityBus_0_0) <= 0 -c_u_InvestmentFlowBlock_max(pv_electricityBus_1)_: --83 InvestmentFlowBlock_invest(pv_electricityBus) -+1 flow(pv_electricityBus_1) +c_u_InvestmentFlowBlock_max(pv_electricityBus_0_1)_: +-83 InvestmentFlowBlock_total(pv_electricityBus_0) ++1 flow(pv_electricityBus_0_1) <= 0 -c_u_InvestmentFlowBlock_max(pv_electricityBus_2)_: --65 InvestmentFlowBlock_invest(pv_electricityBus) -+1 flow(pv_electricityBus_2) +c_u_InvestmentFlowBlock_max(pv_electricityBus_0_2)_: +-65 InvestmentFlowBlock_total(pv_electricityBus_0) ++1 flow(pv_electricityBus_0_2) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(pv_electricityBus_0) <= +inf - 0 <= flow(pv_electricityBus_1) <= +inf - 0 <= flow(pv_electricityBus_2) <= +inf - 0 <= InvestmentFlowBlock_invest(pv_electricityBus) <= +inf + 0 <= flow(pv_electricityBus_0_0) <= +inf + 0 <= flow(pv_electricityBus_0_1) <= +inf + 0 <= flow(pv_electricityBus_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(pv_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(pv_electricityBus_0) <= +inf end diff --git a/tests/lp_files/linear_transformer.lp b/tests/lp_files/linear_transformer.lp index 53e15cc28..31dea46c7 100644 --- a/tests/lp_files/linear_transformer.lp +++ b/tests/lp_files/linear_transformer.lp @@ -1,60 +1,60 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+50 flow(powerplantGas_electricity_0) -+50 flow(powerplantGas_electricity_1) -+50 flow(powerplantGas_electricity_2) ++50 flow(powerplantGas_electricity_0_0) ++50 flow(powerplantGas_electricity_0_1) ++50 flow(powerplantGas_electricity_0_2) s.t. -c_e_BusBlock_balance(electricity_0)_: -+1 flow(powerplantGas_electricity_0) +c_e_BusBlock_balance(electricity_0_0)_: ++1 flow(powerplantGas_electricity_0_0) = 0 -c_e_BusBlock_balance(electricity_1)_: -+1 flow(powerplantGas_electricity_1) +c_e_BusBlock_balance(electricity_0_1)_: ++1 flow(powerplantGas_electricity_0_1) = 0 -c_e_BusBlock_balance(electricity_2)_: -+1 flow(powerplantGas_electricity_2) +c_e_BusBlock_balance(electricity_0_2)_: ++1 flow(powerplantGas_electricity_0_2) = 0 -c_e_BusBlock_balance(gas_0)_: -+1 flow(gas_powerplantGas_0) +c_e_BusBlock_balance(gas_0_0)_: ++1 flow(gas_powerplantGas_0_0) = 0 -c_e_BusBlock_balance(gas_1)_: -+1 flow(gas_powerplantGas_1) +c_e_BusBlock_balance(gas_0_1)_: ++1 flow(gas_powerplantGas_0_1) = 0 -c_e_BusBlock_balance(gas_2)_: -+1 flow(gas_powerplantGas_2) +c_e_BusBlock_balance(gas_0_2)_: ++1 flow(gas_powerplantGas_0_2) = 0 -c_e_TransformerBlock_relation(powerplantGas_gas_electricity_0)_: -+0.57999999999999996 flow(gas_powerplantGas_0) --1 flow(powerplantGas_electricity_0) +c_e_TransformerBlock_relation(powerplantGas_gas_electricity_0_0)_: ++0.57999999999999996 flow(gas_powerplantGas_0_0) +-1 flow(powerplantGas_electricity_0_0) = 0 -c_e_TransformerBlock_relation(powerplantGas_gas_electricity_1)_: -+0.57999999999999996 flow(gas_powerplantGas_1) --1 flow(powerplantGas_electricity_1) +c_e_TransformerBlock_relation(powerplantGas_gas_electricity_0_1)_: ++0.57999999999999996 flow(gas_powerplantGas_0_1) +-1 flow(powerplantGas_electricity_0_1) = 0 -c_e_TransformerBlock_relation(powerplantGas_gas_electricity_2)_: -+0.57999999999999996 flow(gas_powerplantGas_2) --1 flow(powerplantGas_electricity_2) +c_e_TransformerBlock_relation(powerplantGas_gas_electricity_0_2)_: ++0.57999999999999996 flow(gas_powerplantGas_0_2) +-1 flow(powerplantGas_electricity_0_2) = 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(gas_powerplantGas_0) <= +inf - 0 <= flow(gas_powerplantGas_1) <= +inf - 0 <= flow(gas_powerplantGas_2) <= +inf - 0 <= flow(powerplantGas_electricity_0) <= 100000000000 - 0 <= flow(powerplantGas_electricity_1) <= 100000000000 - 0 <= flow(powerplantGas_electricity_2) <= 100000000000 + 0 <= flow(gas_powerplantGas_0_0) <= +inf + 0 <= flow(gas_powerplantGas_0_1) <= +inf + 0 <= flow(gas_powerplantGas_0_2) <= +inf + 0 <= flow(powerplantGas_electricity_0_0) <= 100000000000 + 0 <= flow(powerplantGas_electricity_0_1) <= 100000000000 + 0 <= flow(powerplantGas_electricity_0_2) <= 100000000000 end diff --git a/tests/lp_files/linear_transformer_invest.lp b/tests/lp_files/linear_transformer_invest.lp index 6b1f2b06f..1e73bc44b 100644 --- a/tests/lp_files/linear_transformer_invest.lp +++ b/tests/lp_files/linear_transformer_invest.lp @@ -1,77 +1,83 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+20 InvestmentFlowBlock_invest(powerplant_gas_electricity) -+50 flow(powerplant_gas_electricity_0) -+50 flow(powerplant_gas_electricity_1) -+50 flow(powerplant_gas_electricity_2) ++20 InvestmentFlowBlock_invest(powerplant_gas_electricity_0) ++50 flow(powerplant_gas_electricity_0_0) ++50 flow(powerplant_gas_electricity_0_1) ++50 flow(powerplant_gas_electricity_0_2) s.t. -c_e_BusBlock_balance(electricity_0)_: -+1 flow(powerplant_gas_electricity_0) +c_e_BusBlock_balance(electricity_0_0)_: ++1 flow(powerplant_gas_electricity_0_0) = 0 -c_e_BusBlock_balance(electricity_1)_: -+1 flow(powerplant_gas_electricity_1) +c_e_BusBlock_balance(electricity_0_1)_: ++1 flow(powerplant_gas_electricity_0_1) = 0 -c_e_BusBlock_balance(electricity_2)_: -+1 flow(powerplant_gas_electricity_2) +c_e_BusBlock_balance(electricity_0_2)_: ++1 flow(powerplant_gas_electricity_0_2) = 0 -c_e_BusBlock_balance(gas_0)_: -+1 flow(gas_powerplant_gas_0) +c_e_BusBlock_balance(gas_0_0)_: ++1 flow(gas_powerplant_gas_0_0) = 0 -c_e_BusBlock_balance(gas_1)_: -+1 flow(gas_powerplant_gas_1) +c_e_BusBlock_balance(gas_0_1)_: ++1 flow(gas_powerplant_gas_0_1) = 0 -c_e_BusBlock_balance(gas_2)_: -+1 flow(gas_powerplant_gas_2) +c_e_BusBlock_balance(gas_0_2)_: ++1 flow(gas_powerplant_gas_0_2) = 0 -c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_0)_: -+0.57999999999999996 flow(gas_powerplant_gas_0) --1 flow(powerplant_gas_electricity_0) +c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_0_0)_: ++0.57999999999999996 flow(gas_powerplant_gas_0_0) +-1 flow(powerplant_gas_electricity_0_0) = 0 -c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_1)_: -+0.57999999999999996 flow(gas_powerplant_gas_1) --1 flow(powerplant_gas_electricity_1) +c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_0_1)_: ++0.57999999999999996 flow(gas_powerplant_gas_0_1) +-1 flow(powerplant_gas_electricity_0_1) = 0 -c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_2)_: -+0.57999999999999996 flow(gas_powerplant_gas_2) --1 flow(powerplant_gas_electricity_2) +c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_0_2)_: ++0.57999999999999996 flow(gas_powerplant_gas_0_2) +-1 flow(powerplant_gas_electricity_0_2) = 0 -c_u_InvestmentFlowBlock_max(powerplant_gas_electricity_0)_: --1 InvestmentFlowBlock_invest(powerplant_gas_electricity) -+1 flow(powerplant_gas_electricity_0) +c_e_InvestmentFlowBlock_total_rule(powerplant_gas_electricity_0)_: +-1 InvestmentFlowBlock_invest(powerplant_gas_electricity_0) ++1 InvestmentFlowBlock_total(powerplant_gas_electricity_0) += 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_electricity_0_0)_: +-1 InvestmentFlowBlock_total(powerplant_gas_electricity_0) ++1 flow(powerplant_gas_electricity_0_0) <= 0 -c_u_InvestmentFlowBlock_max(powerplant_gas_electricity_1)_: --1 InvestmentFlowBlock_invest(powerplant_gas_electricity) -+1 flow(powerplant_gas_electricity_1) +c_u_InvestmentFlowBlock_max(powerplant_gas_electricity_0_1)_: +-1 InvestmentFlowBlock_total(powerplant_gas_electricity_0) ++1 flow(powerplant_gas_electricity_0_1) <= 0 -c_u_InvestmentFlowBlock_max(powerplant_gas_electricity_2)_: --1 InvestmentFlowBlock_invest(powerplant_gas_electricity) -+1 flow(powerplant_gas_electricity_2) +c_u_InvestmentFlowBlock_max(powerplant_gas_electricity_0_2)_: +-1 InvestmentFlowBlock_total(powerplant_gas_electricity_0) ++1 flow(powerplant_gas_electricity_0_2) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(gas_powerplant_gas_0) <= +inf - 0 <= flow(gas_powerplant_gas_1) <= +inf - 0 <= flow(gas_powerplant_gas_2) <= +inf - 0 <= flow(powerplant_gas_electricity_0) <= +inf - 0 <= flow(powerplant_gas_electricity_1) <= +inf - 0 <= flow(powerplant_gas_electricity_2) <= +inf - 0 <= InvestmentFlowBlock_invest(powerplant_gas_electricity) <= 1000 + 0 <= flow(gas_powerplant_gas_0_0) <= +inf + 0 <= flow(gas_powerplant_gas_0_1) <= +inf + 0 <= flow(gas_powerplant_gas_0_2) <= +inf + 0 <= flow(powerplant_gas_electricity_0_0) <= +inf + 0 <= flow(powerplant_gas_electricity_0_1) <= +inf + 0 <= flow(powerplant_gas_electricity_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(powerplant_gas_electricity_0) <= 1000 + 0 <= InvestmentFlowBlock_total(powerplant_gas_electricity_0) <= +inf end diff --git a/tests/lp_files/max_source_min_sink.lp b/tests/lp_files/max_source_min_sink.lp index 9006641bc..4ddf6f0df 100644 --- a/tests/lp_files/max_source_min_sink.lp +++ b/tests/lp_files/max_source_min_sink.lp @@ -1,36 +1,36 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+14 flow(electricityBus_minDemand_0) -+14 flow(electricityBus_minDemand_1) -+14 flow(electricityBus_minDemand_2) ++14 flow(electricityBus_minDemand_0_0) ++14 flow(electricityBus_minDemand_0_1) ++14 flow(electricityBus_minDemand_0_2) s.t. -c_e_BusBlock_balance(electricityBus_0)_: --1 flow(electricityBus_minDemand_0) -+1 flow(wind_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_minDemand_0_0) ++1 flow(wind_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: --1 flow(electricityBus_minDemand_1) -+1 flow(wind_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_minDemand_0_1) ++1 flow(wind_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: --1 flow(electricityBus_minDemand_2) -+1 flow(wind_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: +-1 flow(electricityBus_minDemand_0_2) ++1 flow(wind_electricityBus_0_2) = 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 45.359999999999999 <= flow(electricityBus_minDemand_0) <= 54 - 50.759999999999998 <= flow(electricityBus_minDemand_1) <= 54 - 31.859999999999999 <= flow(electricityBus_minDemand_2) <= 54 - 0 <= flow(wind_electricityBus_0) <= 45.899999999999999 - 0 <= flow(wind_electricityBus_1) <= 51.299999999999997 - 0 <= flow(wind_electricityBus_2) <= 32.939999999999998 + 45.359999999999999 <= flow(electricityBus_minDemand_0_0) <= 54 + 50.759999999999998 <= flow(electricityBus_minDemand_0_1) <= 54 + 31.859999999999999 <= flow(electricityBus_minDemand_0_2) <= 54 + 0 <= flow(wind_electricityBus_0_0) <= 45.899999999999999 + 0 <= flow(wind_electricityBus_0_1) <= 51.299999999999997 + 0 <= flow(wind_electricityBus_0_2) <= 32.939999999999998 end diff --git a/tests/lp_files/nominal_value_to_zero.lp b/tests/lp_files/nominal_value_to_zero.lp index 624c2a239..79ef7734e 100644 --- a/tests/lp_files/nominal_value_to_zero.lp +++ b/tests/lp_files/nominal_value_to_zero.lp @@ -1,28 +1,28 @@ \* Source Pyomo model name=Model *\ -min +min objective: +0 ONE_VAR_CONSTANT s.t. -c_e_BusBlock_balance(electricityBus_0)_: -+1 flow(s1_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(s1_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: -+1 flow(s1_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(s1_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: -+1 flow(s1_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(s1_electricityBus_0_2) = 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(s1_electricityBus_0) <= 0 - 0 <= flow(s1_electricityBus_1) <= 0 - 0 <= flow(s1_electricityBus_2) <= 0 + 0 <= flow(s1_electricityBus_0_0) <= 0 + 0 <= flow(s1_electricityBus_0_1) <= 0 + 0 <= flow(s1_electricityBus_0_2) <= 0 end diff --git a/tests/lp_files/storage.lp b/tests/lp_files/storage.lp index 8851e24b5..3f5d9fe46 100644 --- a/tests/lp_files/storage.lp +++ b/tests/lp_files/storage.lp @@ -1,65 +1,65 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+56 flow(electricityBus_storage_no_invest_0) -+56 flow(electricityBus_storage_no_invest_1) -+56 flow(electricityBus_storage_no_invest_2) -+24 flow(storage_no_invest_electricityBus_0) -+24 flow(storage_no_invest_electricityBus_1) -+24 flow(storage_no_invest_electricityBus_2) ++56 flow(electricityBus_storage_no_invest_0_0) ++56 flow(electricityBus_storage_no_invest_0_1) ++56 flow(electricityBus_storage_no_invest_0_2) ++24 flow(storage_no_invest_electricityBus_0_0) ++24 flow(storage_no_invest_electricityBus_0_1) ++24 flow(storage_no_invest_electricityBus_0_2) s.t. -c_e_BusBlock_balance(electricityBus_0)_: --1 flow(electricityBus_storage_no_invest_0) -+1 flow(storage_no_invest_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage_no_invest_0_0) ++1 flow(storage_no_invest_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: --1 flow(electricityBus_storage_no_invest_1) -+1 flow(storage_no_invest_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage_no_invest_0_1) ++1 flow(storage_no_invest_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: --1 flow(electricityBus_storage_no_invest_2) -+1 flow(storage_no_invest_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: +-1 flow(electricityBus_storage_no_invest_0_2) ++1 flow(storage_no_invest_electricityBus_0_2) = 0 c_e_GenericStorageBlock_balance_first(storage_no_invest)_: +1 GenericStorageBlock_storage_content(storage_no_invest_0) --0.96999999999999997 flow(electricityBus_storage_no_invest_0) -+1.1627906976744187 flow(storage_no_invest_electricityBus_0) +-0.96999999999999997 flow(electricityBus_storage_no_invest_0_0) ++1.1627906976744187 flow(storage_no_invest_electricityBus_0_0) = 34800 -c_e_GenericStorageBlock_balance(storage_no_invest_1)_: +c_e_GenericStorageBlock_balance(storage_no_invest_0_1)_: -0.87 GenericStorageBlock_storage_content(storage_no_invest_0) +1 GenericStorageBlock_storage_content(storage_no_invest_1) --0.96999999999999997 flow(electricityBus_storage_no_invest_1) -+1.1627906976744187 flow(storage_no_invest_electricityBus_1) +-0.96999999999999997 flow(electricityBus_storage_no_invest_0_1) ++1.1627906976744187 flow(storage_no_invest_electricityBus_0_1) = 0 -c_e_GenericStorageBlock_balance(storage_no_invest_2)_: +c_e_GenericStorageBlock_balance(storage_no_invest_0_2)_: -0.87 GenericStorageBlock_storage_content(storage_no_invest_1) +1 GenericStorageBlock_storage_content(storage_no_invest_2) --0.96999999999999997 flow(electricityBus_storage_no_invest_2) -+1.1627906976744187 flow(storage_no_invest_electricityBus_2) +-0.96999999999999997 flow(electricityBus_storage_no_invest_0_2) ++1.1627906976744187 flow(storage_no_invest_electricityBus_0_2) = 0 c_e_GenericStorageBlock_balanced_cstr(storage_no_invest)_: +1 GenericStorageBlock_storage_content(storage_no_invest_2) = 40000 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(electricityBus_storage_no_invest_0) <= 16667 - 0 <= flow(electricityBus_storage_no_invest_1) <= 16667 - 0 <= flow(electricityBus_storage_no_invest_2) <= 16667 - 0 <= flow(storage_no_invest_electricityBus_0) <= 16667 - 0 <= flow(storage_no_invest_electricityBus_1) <= 16667 - 0 <= flow(storage_no_invest_electricityBus_2) <= 16667 + 0 <= flow(electricityBus_storage_no_invest_0_0) <= 16667 + 0 <= flow(electricityBus_storage_no_invest_0_1) <= 16667 + 0 <= flow(electricityBus_storage_no_invest_0_2) <= 16667 + 0 <= flow(storage_no_invest_electricityBus_0_0) <= 16667 + 0 <= flow(storage_no_invest_electricityBus_0_1) <= 16667 + 0 <= flow(storage_no_invest_electricityBus_0_2) <= 16667 0 <= GenericStorageBlock_storage_content(storage_no_invest_0) <= 100000 0 <= GenericStorageBlock_storage_content(storage_no_invest_1) <= 100000 0 <= GenericStorageBlock_storage_content(storage_no_invest_2) <= 100000 diff --git a/tests/lp_files/storage_invest_1.lp b/tests/lp_files/storage_invest_1.lp index 21ab34637..c9060c443 100644 --- a/tests/lp_files/storage_invest_1.lp +++ b/tests/lp_files/storage_invest_1.lp @@ -1,86 +1,101 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+145 GenericInvestmentStorageBlock_invest(storage1) -+56 flow(electricityBus_storage1_0) -+56 flow(electricityBus_storage1_1) -+56 flow(electricityBus_storage1_2) -+24 flow(storage1_electricityBus_0) -+24 flow(storage1_electricityBus_1) -+24 flow(storage1_electricityBus_2) ++145 GenericInvestmentStorageBlock_invest(storage1_0) ++56 flow(electricityBus_storage1_0_0) ++56 flow(electricityBus_storage1_0_1) ++56 flow(electricityBus_storage1_0_2) ++24 flow(storage1_electricityBus_0_0) ++24 flow(storage1_electricityBus_0_1) ++24 flow(storage1_electricityBus_0_2) s.t. -c_e_BusBlock_balance(electricityBus_0)_: --1 flow(electricityBus_storage1_0) -+1 flow(storage1_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage1_0_0) ++1 flow(storage1_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: --1 flow(electricityBus_storage1_1) -+1 flow(storage1_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage1_0_1) ++1 flow(storage1_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: --1 flow(electricityBus_storage1_2) -+1 flow(storage1_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: +-1 flow(electricityBus_storage1_0_2) ++1 flow(storage1_electricityBus_0_2) = 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage1_0)_: --1 InvestmentFlowBlock_invest(electricityBus_storage1) -+1 flow(electricityBus_storage1_0) +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage1_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage1_0) ++1 InvestmentFlowBlock_total(electricityBus_storage1_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage1_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(storage1_electricityBus_0) ++1 InvestmentFlowBlock_total(storage1_electricityBus_0) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage1_0_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage1_0) ++1 flow(electricityBus_storage1_0_0) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage1_1)_: --1 InvestmentFlowBlock_invest(electricityBus_storage1) -+1 flow(electricityBus_storage1_1) +c_u_InvestmentFlowBlock_max(electricityBus_storage1_0_1)_: +-1 InvestmentFlowBlock_total(electricityBus_storage1_0) ++1 flow(electricityBus_storage1_0_1) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage1_2)_: --1 InvestmentFlowBlock_invest(electricityBus_storage1) -+1 flow(electricityBus_storage1_2) +c_u_InvestmentFlowBlock_max(electricityBus_storage1_0_2)_: +-1 InvestmentFlowBlock_total(electricityBus_storage1_0) ++1 flow(electricityBus_storage1_0_2) <= 0 -c_u_InvestmentFlowBlock_max(storage1_electricityBus_0)_: --1 InvestmentFlowBlock_invest(storage1_electricityBus) -+1 flow(storage1_electricityBus_0) +c_u_InvestmentFlowBlock_max(storage1_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(storage1_electricityBus_0) ++1 flow(storage1_electricityBus_0_0) <= 0 -c_u_InvestmentFlowBlock_max(storage1_electricityBus_1)_: --1 InvestmentFlowBlock_invest(storage1_electricityBus) -+1 flow(storage1_electricityBus_1) +c_u_InvestmentFlowBlock_max(storage1_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(storage1_electricityBus_0) ++1 flow(storage1_electricityBus_0_1) <= 0 -c_u_InvestmentFlowBlock_max(storage1_electricityBus_2)_: --1 InvestmentFlowBlock_invest(storage1_electricityBus) -+1 flow(storage1_electricityBus_2) +c_u_InvestmentFlowBlock_max(storage1_electricityBus_0_2)_: +-1 InvestmentFlowBlock_total(storage1_electricityBus_0) ++1 flow(storage1_electricityBus_0_2) <= 0 +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage1_0)_: +-1 GenericInvestmentStorageBlock_invest(storage1_0) ++1 GenericInvestmentStorageBlock_total(storage1_0) += 0 + c_u_GenericInvestmentStorageBlock_init_content_limit(storage1)_: +1 GenericInvestmentStorageBlock_init_content(storage1) --1 GenericInvestmentStorageBlock_invest(storage1) +-1 GenericInvestmentStorageBlock_invest(storage1_0) <= 0 c_e_GenericInvestmentStorageBlock_balance_first(storage1)_: -0.87 GenericInvestmentStorageBlock_init_content(storage1) +1 GenericInvestmentStorageBlock_storage_content(storage1_0) --0.96999999999999997 flow(electricityBus_storage1_0) -+1.1627906976744187 flow(storage1_electricityBus_0) +-0.96999999999999997 flow(electricityBus_storage1_0_0) ++1.1627906976744187 flow(storage1_electricityBus_0_0) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage1_1)_: +c_e_GenericInvestmentStorageBlock_balance(storage1_0_1)_: -0.87 GenericInvestmentStorageBlock_storage_content(storage1_0) +1 GenericInvestmentStorageBlock_storage_content(storage1_1) --0.96999999999999997 flow(electricityBus_storage1_1) -+1.1627906976744187 flow(storage1_electricityBus_1) +-0.96999999999999997 flow(electricityBus_storage1_0_1) ++1.1627906976744187 flow(storage1_electricityBus_0_1) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage1_2)_: +c_e_GenericInvestmentStorageBlock_balance(storage1_0_2)_: -0.87 GenericInvestmentStorageBlock_storage_content(storage1_1) +1 GenericInvestmentStorageBlock_storage_content(storage1_2) --0.96999999999999997 flow(electricityBus_storage1_2) -+1.1627906976744187 flow(storage1_electricityBus_2) +-0.96999999999999997 flow(electricityBus_storage1_0_2) ++1.1627906976744187 flow(storage1_electricityBus_0_2) = 0 c_e_GenericInvestmentStorageBlock_balanced_cstr(storage1)_: @@ -88,61 +103,64 @@ c_e_GenericInvestmentStorageBlock_balanced_cstr(storage1)_: +1 GenericInvestmentStorageBlock_storage_content(storage1_2) = 0 -c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage1)_: --0.16666666666666666 GenericInvestmentStorageBlock_invest(storage1) -+1 InvestmentFlowBlock_invest(electricityBus_storage1) +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage1_0)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage1_0) ++1 InvestmentFlowBlock_total(electricityBus_storage1_0) = 0 -c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage1)_: --0.16666666666666666 GenericInvestmentStorageBlock_invest(storage1) -+1 InvestmentFlowBlock_invest(storage1_electricityBus) +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage1_0)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage1_0) ++1 InvestmentFlowBlock_total(storage1_electricityBus_0) = 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0)_: --0.90000000000000002 GenericInvestmentStorageBlock_invest(storage1) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0_0)_: +1 GenericInvestmentStorageBlock_storage_content(storage1_0) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage1_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_1)_: --0.90000000000000002 GenericInvestmentStorageBlock_invest(storage1) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0_1)_: +1 GenericInvestmentStorageBlock_storage_content(storage1_1) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage1_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_2)_: --0.90000000000000002 GenericInvestmentStorageBlock_invest(storage1) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0_2)_: +1 GenericInvestmentStorageBlock_storage_content(storage1_2) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage1_0) <= 0 -c_u_GenericInvestmentStorageBlock_min_storage_content(storage1_0)_: -+0.10000000000000001 GenericInvestmentStorageBlock_invest(storage1) +c_u_GenericInvestmentStorageBlock_min_storage_content(storage1_0_0)_: -1 GenericInvestmentStorageBlock_storage_content(storage1_0) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage1_0) <= 0 -c_u_GenericInvestmentStorageBlock_min_storage_content(storage1_1)_: -+0.10000000000000001 GenericInvestmentStorageBlock_invest(storage1) +c_u_GenericInvestmentStorageBlock_min_storage_content(storage1_0_1)_: -1 GenericInvestmentStorageBlock_storage_content(storage1_1) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage1_0) <= 0 -c_u_GenericInvestmentStorageBlock_min_storage_content(storage1_2)_: -+0.10000000000000001 GenericInvestmentStorageBlock_invest(storage1) +c_u_GenericInvestmentStorageBlock_min_storage_content(storage1_0_2)_: -1 GenericInvestmentStorageBlock_storage_content(storage1_2) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage1_0) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(electricityBus_storage1_0) <= +inf - 0 <= flow(electricityBus_storage1_1) <= +inf - 0 <= flow(electricityBus_storage1_2) <= +inf - 0 <= flow(storage1_electricityBus_0) <= +inf - 0 <= flow(storage1_electricityBus_1) <= +inf - 0 <= flow(storage1_electricityBus_2) <= +inf - 0 <= InvestmentFlowBlock_invest(electricityBus_storage1) <= +inf - 0 <= InvestmentFlowBlock_invest(storage1_electricityBus) <= +inf + 0 <= flow(electricityBus_storage1_0_0) <= +inf + 0 <= flow(electricityBus_storage1_0_1) <= +inf + 0 <= flow(electricityBus_storage1_0_2) <= +inf + 0 <= flow(storage1_electricityBus_0_0) <= +inf + 0 <= flow(storage1_electricityBus_0_1) <= +inf + 0 <= flow(storage1_electricityBus_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage1_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage1_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage1_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage1_electricityBus_0) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage1_0) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage1_1) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage1_2) <= +inf - 0 <= GenericInvestmentStorageBlock_invest(storage1) <= 234 + 0 <= GenericInvestmentStorageBlock_invest(storage1_0) <= 234 + 0 <= GenericInvestmentStorageBlock_total(storage1_0) <= +inf 0 <= GenericInvestmentStorageBlock_init_content(storage1) <= +inf end diff --git a/tests/lp_files/storage_invest_2.lp b/tests/lp_files/storage_invest_2.lp index 9aa09e717..ea3339192 100644 --- a/tests/lp_files/storage_invest_2.lp +++ b/tests/lp_files/storage_invest_2.lp @@ -1,82 +1,97 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+145 GenericInvestmentStorageBlock_invest(storage2) -+99 InvestmentFlowBlock_invest(electricityBus_storage2) -+9 InvestmentFlowBlock_invest(storage2_electricityBus) ++145 GenericInvestmentStorageBlock_invest(storage2_0) ++99 InvestmentFlowBlock_invest(electricityBus_storage2_0) ++9 InvestmentFlowBlock_invest(storage2_electricityBus_0) s.t. -c_e_BusBlock_balance(electricityBus_0)_: --1 flow(electricityBus_storage2_0) -+1 flow(storage2_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage2_0_0) ++1 flow(storage2_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: --1 flow(electricityBus_storage2_1) -+1 flow(storage2_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage2_0_1) ++1 flow(storage2_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: --1 flow(electricityBus_storage2_2) -+1 flow(storage2_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: +-1 flow(electricityBus_storage2_0_2) ++1 flow(storage2_electricityBus_0_2) = 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage2_0)_: --1 InvestmentFlowBlock_invest(electricityBus_storage2) -+1 flow(electricityBus_storage2_0) +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage2_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage2_0) ++1 InvestmentFlowBlock_total(electricityBus_storage2_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage2_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(storage2_electricityBus_0) ++1 InvestmentFlowBlock_total(storage2_electricityBus_0) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage2_0_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage2_0) ++1 flow(electricityBus_storage2_0_0) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage2_1)_: --1 InvestmentFlowBlock_invest(electricityBus_storage2) -+1 flow(electricityBus_storage2_1) +c_u_InvestmentFlowBlock_max(electricityBus_storage2_0_1)_: +-1 InvestmentFlowBlock_total(electricityBus_storage2_0) ++1 flow(electricityBus_storage2_0_1) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage2_2)_: --1 InvestmentFlowBlock_invest(electricityBus_storage2) -+1 flow(electricityBus_storage2_2) +c_u_InvestmentFlowBlock_max(electricityBus_storage2_0_2)_: +-1 InvestmentFlowBlock_total(electricityBus_storage2_0) ++1 flow(electricityBus_storage2_0_2) <= 0 -c_u_InvestmentFlowBlock_max(storage2_electricityBus_0)_: --1 InvestmentFlowBlock_invest(storage2_electricityBus) -+1 flow(storage2_electricityBus_0) +c_u_InvestmentFlowBlock_max(storage2_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(storage2_electricityBus_0) ++1 flow(storage2_electricityBus_0_0) <= 0 -c_u_InvestmentFlowBlock_max(storage2_electricityBus_1)_: --1 InvestmentFlowBlock_invest(storage2_electricityBus) -+1 flow(storage2_electricityBus_1) +c_u_InvestmentFlowBlock_max(storage2_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(storage2_electricityBus_0) ++1 flow(storage2_electricityBus_0_1) <= 0 -c_u_InvestmentFlowBlock_max(storage2_electricityBus_2)_: --1 InvestmentFlowBlock_invest(storage2_electricityBus) -+1 flow(storage2_electricityBus_2) +c_u_InvestmentFlowBlock_max(storage2_electricityBus_0_2)_: +-1 InvestmentFlowBlock_total(storage2_electricityBus_0) ++1 flow(storage2_electricityBus_0_2) <= 0 +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage2_0)_: +-1 GenericInvestmentStorageBlock_invest(storage2_0) ++1 GenericInvestmentStorageBlock_total(storage2_0) += 0 + c_e_GenericInvestmentStorageBlock_init_content_fix(storage2)_: +1 GenericInvestmentStorageBlock_init_content(storage2) --0.5 GenericInvestmentStorageBlock_invest(storage2) +-0.5 GenericInvestmentStorageBlock_invest(storage2_0) = 0 c_e_GenericInvestmentStorageBlock_balance_first(storage2)_: -1 GenericInvestmentStorageBlock_init_content(storage2) +1 GenericInvestmentStorageBlock_storage_content(storage2_0) --1 flow(electricityBus_storage2_0) -+1 flow(storage2_electricityBus_0) +-1 flow(electricityBus_storage2_0_0) ++1 flow(storage2_electricityBus_0_0) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage2_1)_: +c_e_GenericInvestmentStorageBlock_balance(storage2_0_1)_: -1 GenericInvestmentStorageBlock_storage_content(storage2_0) +1 GenericInvestmentStorageBlock_storage_content(storage2_1) --1 flow(electricityBus_storage2_1) -+1 flow(storage2_electricityBus_1) +-1 flow(electricityBus_storage2_0_1) ++1 flow(storage2_electricityBus_0_1) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage2_2)_: +c_e_GenericInvestmentStorageBlock_balance(storage2_0_2)_: -1 GenericInvestmentStorageBlock_storage_content(storage2_1) +1 GenericInvestmentStorageBlock_storage_content(storage2_2) --1 flow(electricityBus_storage2_2) -+1 flow(storage2_electricityBus_2) +-1 flow(electricityBus_storage2_0_2) ++1 flow(storage2_electricityBus_0_2) = 0 c_e_GenericInvestmentStorageBlock_balanced_cstr(storage2)_: @@ -84,36 +99,39 @@ c_e_GenericInvestmentStorageBlock_balanced_cstr(storage2)_: +1 GenericInvestmentStorageBlock_storage_content(storage2_2) = 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage2_0)_: --1 GenericInvestmentStorageBlock_invest(storage2) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage2_0_0)_: +1 GenericInvestmentStorageBlock_storage_content(storage2_0) +-1 GenericInvestmentStorageBlock_total(storage2_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage2_1)_: --1 GenericInvestmentStorageBlock_invest(storage2) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage2_0_1)_: +1 GenericInvestmentStorageBlock_storage_content(storage2_1) +-1 GenericInvestmentStorageBlock_total(storage2_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage2_2)_: --1 GenericInvestmentStorageBlock_invest(storage2) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage2_0_2)_: +1 GenericInvestmentStorageBlock_storage_content(storage2_2) +-1 GenericInvestmentStorageBlock_total(storage2_0) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(electricityBus_storage2_0) <= +inf - 0 <= flow(electricityBus_storage2_1) <= +inf - 0 <= flow(electricityBus_storage2_2) <= +inf - 0 <= flow(storage2_electricityBus_0) <= +inf - 0 <= flow(storage2_electricityBus_1) <= +inf - 0 <= flow(storage2_electricityBus_2) <= +inf - 0 <= InvestmentFlowBlock_invest(electricityBus_storage2) <= +inf - 0 <= InvestmentFlowBlock_invest(storage2_electricityBus) <= +inf + 0 <= flow(electricityBus_storage2_0_0) <= +inf + 0 <= flow(electricityBus_storage2_0_1) <= +inf + 0 <= flow(electricityBus_storage2_0_2) <= +inf + 0 <= flow(storage2_electricityBus_0_0) <= +inf + 0 <= flow(storage2_electricityBus_0_1) <= +inf + 0 <= flow(storage2_electricityBus_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage2_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage2_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage2_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage2_electricityBus_0) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage2_0) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage2_1) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage2_2) <= +inf - 0 <= GenericInvestmentStorageBlock_invest(storage2) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage2_0) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage2_0) <= +inf 0 <= GenericInvestmentStorageBlock_init_content(storage2) <= +inf end diff --git a/tests/lp_files/storage_invest_3.lp b/tests/lp_files/storage_invest_3.lp index 3e39dfb83..739136b62 100644 --- a/tests/lp_files/storage_invest_3.lp +++ b/tests/lp_files/storage_invest_3.lp @@ -1,76 +1,86 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+99 InvestmentFlowBlock_invest(electricityBus_storage3) -+9 InvestmentFlowBlock_invest(storage3_electricityBus) ++99 InvestmentFlowBlock_invest(electricityBus_storage3_0) ++9 InvestmentFlowBlock_invest(storage3_electricityBus_0) s.t. -c_e_BusBlock_balance(electricityBus_0)_: --1 flow(electricityBus_storage3_0) -+1 flow(storage3_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage3_0_0) ++1 flow(storage3_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: --1 flow(electricityBus_storage3_1) -+1 flow(storage3_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage3_0_1) ++1 flow(storage3_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: --1 flow(electricityBus_storage3_2) -+1 flow(storage3_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: +-1 flow(electricityBus_storage3_0_2) ++1 flow(storage3_electricityBus_0_2) = 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage3_0)_: --1 InvestmentFlowBlock_invest(electricityBus_storage3) -+1 flow(electricityBus_storage3_0) +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage3_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage3_0) ++1 InvestmentFlowBlock_total(electricityBus_storage3_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage3_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(storage3_electricityBus_0) ++1 InvestmentFlowBlock_total(storage3_electricityBus_0) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage3_0_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage3_0) ++1 flow(electricityBus_storage3_0_0) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage3_1)_: --1 InvestmentFlowBlock_invest(electricityBus_storage3) -+1 flow(electricityBus_storage3_1) +c_u_InvestmentFlowBlock_max(electricityBus_storage3_0_1)_: +-1 InvestmentFlowBlock_total(electricityBus_storage3_0) ++1 flow(electricityBus_storage3_0_1) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage3_2)_: --1 InvestmentFlowBlock_invest(electricityBus_storage3) -+1 flow(electricityBus_storage3_2) +c_u_InvestmentFlowBlock_max(electricityBus_storage3_0_2)_: +-1 InvestmentFlowBlock_total(electricityBus_storage3_0) ++1 flow(electricityBus_storage3_0_2) <= 0 -c_u_InvestmentFlowBlock_max(storage3_electricityBus_0)_: --1 InvestmentFlowBlock_invest(storage3_electricityBus) -+1 flow(storage3_electricityBus_0) +c_u_InvestmentFlowBlock_max(storage3_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(storage3_electricityBus_0) ++1 flow(storage3_electricityBus_0_0) <= 0 -c_u_InvestmentFlowBlock_max(storage3_electricityBus_1)_: --1 InvestmentFlowBlock_invest(storage3_electricityBus) -+1 flow(storage3_electricityBus_1) +c_u_InvestmentFlowBlock_max(storage3_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(storage3_electricityBus_0) ++1 flow(storage3_electricityBus_0_1) <= 0 -c_u_InvestmentFlowBlock_max(storage3_electricityBus_2)_: --1 InvestmentFlowBlock_invest(storage3_electricityBus) -+1 flow(storage3_electricityBus_2) +c_u_InvestmentFlowBlock_max(storage3_electricityBus_0_2)_: +-1 InvestmentFlowBlock_total(storage3_electricityBus_0) ++1 flow(storage3_electricityBus_0_2) <= 0 c_e_GenericStorageBlock_balance_first(storage3)_: -1 GenericStorageBlock_init_content(storage3) +1 GenericStorageBlock_storage_content(storage3_0) --1 flow(electricityBus_storage3_0) -+1 flow(storage3_electricityBus_0) +-1 flow(electricityBus_storage3_0_0) ++1 flow(storage3_electricityBus_0_0) = 0 -c_e_GenericStorageBlock_balance(storage3_1)_: +c_e_GenericStorageBlock_balance(storage3_0_1)_: -1 GenericStorageBlock_storage_content(storage3_0) +1 GenericStorageBlock_storage_content(storage3_1) --1 flow(electricityBus_storage3_1) -+1 flow(storage3_electricityBus_1) +-1 flow(electricityBus_storage3_0_1) ++1 flow(storage3_electricityBus_0_1) = 0 -c_e_GenericStorageBlock_balance(storage3_2)_: +c_e_GenericStorageBlock_balance(storage3_0_2)_: -1 GenericStorageBlock_storage_content(storage3_1) +1 GenericStorageBlock_storage_content(storage3_2) --1 flow(electricityBus_storage3_2) -+1 flow(storage3_electricityBus_2) +-1 flow(electricityBus_storage3_0_2) ++1 flow(storage3_electricityBus_0_2) = 0 c_e_GenericStorageBlock_balanced_cstr(storage3)_: @@ -78,18 +88,20 @@ c_e_GenericStorageBlock_balanced_cstr(storage3)_: +1 GenericStorageBlock_storage_content(storage3_2) = 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(electricityBus_storage3_0) <= +inf - 0 <= flow(electricityBus_storage3_1) <= +inf - 0 <= flow(electricityBus_storage3_2) <= +inf - 0 <= flow(storage3_electricityBus_0) <= +inf - 0 <= flow(storage3_electricityBus_1) <= +inf - 0 <= flow(storage3_electricityBus_2) <= +inf - 0 <= InvestmentFlowBlock_invest(electricityBus_storage3) <= +inf - 0 <= InvestmentFlowBlock_invest(storage3_electricityBus) <= +inf + 0 <= flow(electricityBus_storage3_0_0) <= +inf + 0 <= flow(electricityBus_storage3_0_1) <= +inf + 0 <= flow(electricityBus_storage3_0_2) <= +inf + 0 <= flow(storage3_electricityBus_0_0) <= +inf + 0 <= flow(storage3_electricityBus_0_1) <= +inf + 0 <= flow(storage3_electricityBus_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage3_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage3_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage3_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage3_electricityBus_0) <= +inf 0 <= GenericStorageBlock_storage_content(storage3_0) <= 5000 0 <= GenericStorageBlock_storage_content(storage3_1) <= 5000 0 <= GenericStorageBlock_storage_content(storage3_2) <= 5000 diff --git a/tests/lp_files/storage_invest_4.lp b/tests/lp_files/storage_invest_4.lp index 8d29c2d6d..bc71d3727 100644 --- a/tests/lp_files/storage_invest_4.lp +++ b/tests/lp_files/storage_invest_4.lp @@ -1,50 +1,55 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+145 GenericInvestmentStorageBlock_invest(storage4) ++145 GenericInvestmentStorageBlock_invest(storage4_0) s.t. -c_e_BusBlock_balance(electricityBus_0)_: --1 flow(electricityBus_storage4_0) -+1 flow(storage4_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage4_0_0) ++1 flow(storage4_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: --1 flow(electricityBus_storage4_1) -+1 flow(storage4_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage4_0_1) ++1 flow(storage4_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: --1 flow(electricityBus_storage4_2) -+1 flow(storage4_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: +-1 flow(electricityBus_storage4_0_2) ++1 flow(storage4_electricityBus_0_2) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage4_0)_: +-1 GenericInvestmentStorageBlock_invest(storage4_0) ++1 GenericInvestmentStorageBlock_total(storage4_0) = 0 c_u_GenericInvestmentStorageBlock_init_content_limit(storage4)_: +1 GenericInvestmentStorageBlock_init_content(storage4) --1 GenericInvestmentStorageBlock_invest(storage4) +-1 GenericInvestmentStorageBlock_invest(storage4_0) <= 0 c_e_GenericInvestmentStorageBlock_balance_first(storage4)_: -1 GenericInvestmentStorageBlock_init_content(storage4) +1 GenericInvestmentStorageBlock_storage_content(storage4_0) --1 flow(electricityBus_storage4_0) -+1 flow(storage4_electricityBus_0) +-1 flow(electricityBus_storage4_0_0) ++1 flow(storage4_electricityBus_0_0) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage4_1)_: +c_e_GenericInvestmentStorageBlock_balance(storage4_0_1)_: -1 GenericInvestmentStorageBlock_storage_content(storage4_0) +1 GenericInvestmentStorageBlock_storage_content(storage4_1) --1 flow(electricityBus_storage4_1) -+1 flow(storage4_electricityBus_1) +-1 flow(electricityBus_storage4_0_1) ++1 flow(storage4_electricityBus_0_1) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage4_2)_: +c_e_GenericInvestmentStorageBlock_balance(storage4_0_2)_: -1 GenericInvestmentStorageBlock_storage_content(storage4_1) +1 GenericInvestmentStorageBlock_storage_content(storage4_2) --1 flow(electricityBus_storage4_2) -+1 flow(storage4_electricityBus_2) +-1 flow(electricityBus_storage4_0_2) ++1 flow(storage4_electricityBus_0_2) = 0 c_e_GenericInvestmentStorageBlock_balanced_cstr(storage4)_: @@ -52,34 +57,35 @@ c_e_GenericInvestmentStorageBlock_balanced_cstr(storage4)_: +1 GenericInvestmentStorageBlock_storage_content(storage4_2) = 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage4_0)_: --1 GenericInvestmentStorageBlock_invest(storage4) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage4_0_0)_: +1 GenericInvestmentStorageBlock_storage_content(storage4_0) +-1 GenericInvestmentStorageBlock_total(storage4_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage4_1)_: --1 GenericInvestmentStorageBlock_invest(storage4) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage4_0_1)_: +1 GenericInvestmentStorageBlock_storage_content(storage4_1) +-1 GenericInvestmentStorageBlock_total(storage4_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage4_2)_: --1 GenericInvestmentStorageBlock_invest(storage4) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage4_0_2)_: +1 GenericInvestmentStorageBlock_storage_content(storage4_2) +-1 GenericInvestmentStorageBlock_total(storage4_0) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(electricityBus_storage4_0) <= 80 - 0 <= flow(electricityBus_storage4_1) <= 80 - 0 <= flow(electricityBus_storage4_2) <= 80 - 0 <= flow(storage4_electricityBus_0) <= 100 - 0 <= flow(storage4_electricityBus_1) <= 100 - 0 <= flow(storage4_electricityBus_2) <= 100 + 0 <= flow(electricityBus_storage4_0_0) <= 80 + 0 <= flow(electricityBus_storage4_0_1) <= 80 + 0 <= flow(electricityBus_storage4_0_2) <= 80 + 0 <= flow(storage4_electricityBus_0_0) <= 100 + 0 <= flow(storage4_electricityBus_0_1) <= 100 + 0 <= flow(storage4_electricityBus_0_2) <= 100 0 <= GenericInvestmentStorageBlock_storage_content(storage4_0) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage4_1) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage4_2) <= +inf - 0 <= GenericInvestmentStorageBlock_invest(storage4) <= 500 + 0 <= GenericInvestmentStorageBlock_invest(storage4_0) <= 500 + 0 <= GenericInvestmentStorageBlock_total(storage4_0) <= +inf 0 <= GenericInvestmentStorageBlock_init_content(storage4) <= +inf end diff --git a/tests/lp_files/storage_invest_5.lp b/tests/lp_files/storage_invest_5.lp index a71910b5d..bb39ef262 100644 --- a/tests/lp_files/storage_invest_5.lp +++ b/tests/lp_files/storage_invest_5.lp @@ -1,75 +1,85 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+99 InvestmentFlowBlock_invest(electricityBus_storage5) ++99 InvestmentFlowBlock_invest(electricityBus_storage5_0) s.t. -c_e_BusBlock_balance(electricityBus_0)_: --1 flow(electricityBus_storage5_0) -+1 flow(storage5_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage5_0_0) ++1 flow(storage5_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: --1 flow(electricityBus_storage5_1) -+1 flow(storage5_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage5_0_1) ++1 flow(storage5_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: --1 flow(electricityBus_storage5_2) -+1 flow(storage5_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: +-1 flow(electricityBus_storage5_0_2) ++1 flow(storage5_electricityBus_0_2) = 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage5_0)_: --1 InvestmentFlowBlock_invest(electricityBus_storage5) -+1 flow(electricityBus_storage5_0) -<= 110 - -c_u_InvestmentFlowBlock_max(electricityBus_storage5_1)_: --1 InvestmentFlowBlock_invest(electricityBus_storage5) -+1 flow(electricityBus_storage5_1) -<= 110 - -c_u_InvestmentFlowBlock_max(electricityBus_storage5_2)_: --1 InvestmentFlowBlock_invest(electricityBus_storage5) -+1 flow(electricityBus_storage5_2) -<= 110 - -c_u_InvestmentFlowBlock_max(storage5_electricityBus_0)_: --1 InvestmentFlowBlock_invest(storage5_electricityBus) -+1 flow(storage5_electricityBus_0) -<= 100 - -c_u_InvestmentFlowBlock_max(storage5_electricityBus_1)_: --1 InvestmentFlowBlock_invest(storage5_electricityBus) -+1 flow(storage5_electricityBus_1) -<= 100 - -c_u_InvestmentFlowBlock_max(storage5_electricityBus_2)_: --1 InvestmentFlowBlock_invest(storage5_electricityBus) -+1 flow(storage5_electricityBus_2) -<= 100 +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage5_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage5_0) ++1 InvestmentFlowBlock_total(electricityBus_storage5_0) += 110 + +c_e_InvestmentFlowBlock_total_rule(storage5_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(storage5_electricityBus_0) ++1 InvestmentFlowBlock_total(storage5_electricityBus_0) += 100 + +c_u_InvestmentFlowBlock_max(electricityBus_storage5_0_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage5_0) ++1 flow(electricityBus_storage5_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage5_0_1)_: +-1 InvestmentFlowBlock_total(electricityBus_storage5_0) ++1 flow(electricityBus_storage5_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage5_0_2)_: +-1 InvestmentFlowBlock_total(electricityBus_storage5_0) ++1 flow(electricityBus_storage5_0_2) +<= 0 + +c_u_InvestmentFlowBlock_max(storage5_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(storage5_electricityBus_0) ++1 flow(storage5_electricityBus_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(storage5_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(storage5_electricityBus_0) ++1 flow(storage5_electricityBus_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(storage5_electricityBus_0_2)_: +-1 InvestmentFlowBlock_total(storage5_electricityBus_0) ++1 flow(storage5_electricityBus_0_2) +<= 0 c_e_GenericStorageBlock_balance_first(storage5)_: -1 GenericStorageBlock_init_content(storage5) +1 GenericStorageBlock_storage_content(storage5_0) --1 flow(electricityBus_storage5_0) -+1 flow(storage5_electricityBus_0) +-1 flow(electricityBus_storage5_0_0) ++1 flow(storage5_electricityBus_0_0) = 0 -c_e_GenericStorageBlock_balance(storage5_1)_: +c_e_GenericStorageBlock_balance(storage5_0_1)_: -1 GenericStorageBlock_storage_content(storage5_0) +1 GenericStorageBlock_storage_content(storage5_1) --1 flow(electricityBus_storage5_1) -+1 flow(storage5_electricityBus_1) +-1 flow(electricityBus_storage5_0_1) ++1 flow(storage5_electricityBus_0_1) = 0 -c_e_GenericStorageBlock_balance(storage5_2)_: +c_e_GenericStorageBlock_balance(storage5_0_2)_: -1 GenericStorageBlock_storage_content(storage5_1) +1 GenericStorageBlock_storage_content(storage5_2) --1 flow(electricityBus_storage5_2) -+1 flow(storage5_electricityBus_2) +-1 flow(electricityBus_storage5_0_2) ++1 flow(storage5_electricityBus_0_2) = 0 c_e_GenericStorageBlock_balanced_cstr(storage5)_: @@ -77,23 +87,25 @@ c_e_GenericStorageBlock_balanced_cstr(storage5)_: +1 GenericStorageBlock_storage_content(storage5_2) = 0 -c_e_GenericStorageBlock_power_coupled(storage5)_: --1 InvestmentFlowBlock_invest(electricityBus_storage5) -+1.1000000000000001 InvestmentFlowBlock_invest(storage5_electricityBus) -= -1.4210854715202004e-14 +c_e_GenericStorageBlock_power_coupled(storage5_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage5_0) ++1.1000000000000001 InvestmentFlowBlock_total(storage5_electricityBus_0) += 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(electricityBus_storage5_0) <= +inf - 0 <= flow(electricityBus_storage5_1) <= +inf - 0 <= flow(electricityBus_storage5_2) <= +inf - 0 <= flow(storage5_electricityBus_0) <= +inf - 0 <= flow(storage5_electricityBus_1) <= +inf - 0 <= flow(storage5_electricityBus_2) <= +inf - 0 <= InvestmentFlowBlock_invest(electricityBus_storage5) <= +inf - 0 <= InvestmentFlowBlock_invest(storage5_electricityBus) <= +inf + 0 <= flow(electricityBus_storage5_0_0) <= +inf + 0 <= flow(electricityBus_storage5_0_1) <= +inf + 0 <= flow(electricityBus_storage5_0_2) <= +inf + 0 <= flow(storage5_electricityBus_0_0) <= +inf + 0 <= flow(storage5_electricityBus_0_1) <= +inf + 0 <= flow(storage5_electricityBus_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage5_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage5_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage5_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage5_electricityBus_0) <= +inf 0 <= GenericStorageBlock_storage_content(storage5_0) <= 10000 0 <= GenericStorageBlock_storage_content(storage5_1) <= 10000 0 <= GenericStorageBlock_storage_content(storage5_2) <= 10000 diff --git a/tests/lp_files/storage_invest_6.lp b/tests/lp_files/storage_invest_6.lp index 030d94140..fdd5361cf 100644 --- a/tests/lp_files/storage_invest_6.lp +++ b/tests/lp_files/storage_invest_6.lp @@ -1,81 +1,96 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+145 GenericInvestmentStorageBlock_invest(storage6) -+99 InvestmentFlowBlock_invest(electricityBus_storage6) ++145 GenericInvestmentStorageBlock_invest(storage6_0) ++99 InvestmentFlowBlock_invest(electricityBus_storage6_0) s.t. -c_e_BusBlock_balance(electricityBus_0)_: --1 flow(electricityBus_storage6_0) -+1 flow(storage6_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage6_0_0) ++1 flow(storage6_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: --1 flow(electricityBus_storage6_1) -+1 flow(storage6_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage6_0_1) ++1 flow(storage6_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: --1 flow(electricityBus_storage6_2) -+1 flow(storage6_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: +-1 flow(electricityBus_storage6_0_2) ++1 flow(storage6_electricityBus_0_2) = 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage6_0)_: --1 InvestmentFlowBlock_invest(electricityBus_storage6) -+1 flow(electricityBus_storage6_0) -<= 110 - -c_u_InvestmentFlowBlock_max(electricityBus_storage6_1)_: --1 InvestmentFlowBlock_invest(electricityBus_storage6) -+1 flow(electricityBus_storage6_1) -<= 110 - -c_u_InvestmentFlowBlock_max(electricityBus_storage6_2)_: --1 InvestmentFlowBlock_invest(electricityBus_storage6) -+1 flow(electricityBus_storage6_2) -<= 110 - -c_u_InvestmentFlowBlock_max(storage6_electricityBus_0)_: --1 InvestmentFlowBlock_invest(storage6_electricityBus) -+1 flow(storage6_electricityBus_0) -<= 100 - -c_u_InvestmentFlowBlock_max(storage6_electricityBus_1)_: --1 InvestmentFlowBlock_invest(storage6_electricityBus) -+1 flow(storage6_electricityBus_1) -<= 100 - -c_u_InvestmentFlowBlock_max(storage6_electricityBus_2)_: --1 InvestmentFlowBlock_invest(storage6_electricityBus) -+1 flow(storage6_electricityBus_2) -<= 100 +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage6_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage6_0) ++1 InvestmentFlowBlock_total(electricityBus_storage6_0) += 110 + +c_e_InvestmentFlowBlock_total_rule(storage6_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(storage6_electricityBus_0) ++1 InvestmentFlowBlock_total(storage6_electricityBus_0) += 100 + +c_u_InvestmentFlowBlock_max(electricityBus_storage6_0_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage6_0) ++1 flow(electricityBus_storage6_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage6_0_1)_: +-1 InvestmentFlowBlock_total(electricityBus_storage6_0) ++1 flow(electricityBus_storage6_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage6_0_2)_: +-1 InvestmentFlowBlock_total(electricityBus_storage6_0) ++1 flow(electricityBus_storage6_0_2) +<= 0 + +c_u_InvestmentFlowBlock_max(storage6_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(storage6_electricityBus_0) ++1 flow(storage6_electricityBus_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(storage6_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(storage6_electricityBus_0) ++1 flow(storage6_electricityBus_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(storage6_electricityBus_0_2)_: +-1 InvestmentFlowBlock_total(storage6_electricityBus_0) ++1 flow(storage6_electricityBus_0_2) +<= 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage6_0)_: +-1 GenericInvestmentStorageBlock_invest(storage6_0) ++1 GenericInvestmentStorageBlock_total(storage6_0) += 10000 c_u_GenericInvestmentStorageBlock_init_content_limit(storage6)_: +1 GenericInvestmentStorageBlock_init_content(storage6) --1 GenericInvestmentStorageBlock_invest(storage6) +-1 GenericInvestmentStorageBlock_invest(storage6_0) <= 10000 c_e_GenericInvestmentStorageBlock_balance_first(storage6)_: -1 GenericInvestmentStorageBlock_init_content(storage6) +1 GenericInvestmentStorageBlock_storage_content(storage6_0) --1 flow(electricityBus_storage6_0) -+1 flow(storage6_electricityBus_0) +-1 flow(electricityBus_storage6_0_0) ++1 flow(storage6_electricityBus_0_0) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage6_1)_: +c_e_GenericInvestmentStorageBlock_balance(storage6_0_1)_: -1 GenericInvestmentStorageBlock_storage_content(storage6_0) +1 GenericInvestmentStorageBlock_storage_content(storage6_1) --1 flow(electricityBus_storage6_1) -+1 flow(storage6_electricityBus_1) +-1 flow(electricityBus_storage6_0_1) ++1 flow(storage6_electricityBus_0_1) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage6_2)_: +c_e_GenericInvestmentStorageBlock_balance(storage6_0_2)_: -1 GenericInvestmentStorageBlock_storage_content(storage6_1) +1 GenericInvestmentStorageBlock_storage_content(storage6_2) --1 flow(electricityBus_storage6_2) -+1 flow(storage6_electricityBus_2) +-1 flow(electricityBus_storage6_0_2) ++1 flow(storage6_electricityBus_0_2) = 0 c_e_GenericInvestmentStorageBlock_balanced_cstr(storage6)_: @@ -83,41 +98,44 @@ c_e_GenericInvestmentStorageBlock_balanced_cstr(storage6)_: +1 GenericInvestmentStorageBlock_storage_content(storage6_2) = 0 -c_e_GenericInvestmentStorageBlock_power_coupled(storage6)_: --1 InvestmentFlowBlock_invest(electricityBus_storage6) -+1.1000000000000001 InvestmentFlowBlock_invest(storage6_electricityBus) -= -1.4210854715202004e-14 +c_e_GenericInvestmentStorageBlock_power_coupled(storage6_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage6_0) ++1.1000000000000001 InvestmentFlowBlock_total(storage6_electricityBus_0) += 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage6_0)_: --1 GenericInvestmentStorageBlock_invest(storage6) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage6_0_0)_: +1 GenericInvestmentStorageBlock_storage_content(storage6_0) -<= 10000 +-1 GenericInvestmentStorageBlock_total(storage6_0) +<= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage6_1)_: --1 GenericInvestmentStorageBlock_invest(storage6) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage6_0_1)_: +1 GenericInvestmentStorageBlock_storage_content(storage6_1) -<= 10000 +-1 GenericInvestmentStorageBlock_total(storage6_0) +<= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage6_2)_: --1 GenericInvestmentStorageBlock_invest(storage6) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage6_0_2)_: +1 GenericInvestmentStorageBlock_storage_content(storage6_2) -<= 10000 +-1 GenericInvestmentStorageBlock_total(storage6_0) +<= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(electricityBus_storage6_0) <= +inf - 0 <= flow(electricityBus_storage6_1) <= +inf - 0 <= flow(electricityBus_storage6_2) <= +inf - 0 <= flow(storage6_electricityBus_0) <= +inf - 0 <= flow(storage6_electricityBus_1) <= +inf - 0 <= flow(storage6_electricityBus_2) <= +inf - 0 <= InvestmentFlowBlock_invest(electricityBus_storage6) <= +inf - 0 <= InvestmentFlowBlock_invest(storage6_electricityBus) <= +inf + 0 <= flow(electricityBus_storage6_0_0) <= +inf + 0 <= flow(electricityBus_storage6_0_1) <= +inf + 0 <= flow(electricityBus_storage6_0_2) <= +inf + 0 <= flow(storage6_electricityBus_0_0) <= +inf + 0 <= flow(storage6_electricityBus_0_1) <= +inf + 0 <= flow(storage6_electricityBus_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage6_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage6_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage6_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage6_electricityBus_0) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage6_0) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage6_1) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage6_2) <= +inf - 0 <= GenericInvestmentStorageBlock_invest(storage6) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage6_0) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage6_0) <= +inf 0 <= GenericInvestmentStorageBlock_init_content(storage6) <= +inf end diff --git a/tests/lp_files/storage_invest_minimum.lp b/tests/lp_files/storage_invest_minimum.lp index 4038a7663..4f22b8018 100644 --- a/tests/lp_files/storage_invest_minimum.lp +++ b/tests/lp_files/storage_invest_minimum.lp @@ -1,50 +1,55 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+145 GenericInvestmentStorageBlock_invest(storage1) ++145 GenericInvestmentStorageBlock_invest(storage1_0) s.t. -c_e_BusBlock_balance(electricityBus_0)_: --1 flow(electricityBus_storage1_0) -+1 flow(storage1_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage1_0_0) ++1 flow(storage1_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: --1 flow(electricityBus_storage1_1) -+1 flow(storage1_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage1_0_1) ++1 flow(storage1_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: --1 flow(electricityBus_storage1_2) -+1 flow(storage1_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: +-1 flow(electricityBus_storage1_0_2) ++1 flow(storage1_electricityBus_0_2) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage1_0)_: +-1 GenericInvestmentStorageBlock_invest(storage1_0) ++1 GenericInvestmentStorageBlock_total(storage1_0) = 0 c_u_GenericInvestmentStorageBlock_init_content_limit(storage1)_: +1 GenericInvestmentStorageBlock_init_content(storage1) --1 GenericInvestmentStorageBlock_invest(storage1) +-1 GenericInvestmentStorageBlock_invest(storage1_0) <= 0 c_e_GenericInvestmentStorageBlock_balance_first(storage1)_: -1 GenericInvestmentStorageBlock_init_content(storage1) +1 GenericInvestmentStorageBlock_storage_content(storage1_0) --1 flow(electricityBus_storage1_0) -+1 flow(storage1_electricityBus_0) +-1 flow(electricityBus_storage1_0_0) ++1 flow(storage1_electricityBus_0_0) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage1_1)_: +c_e_GenericInvestmentStorageBlock_balance(storage1_0_1)_: -1 GenericInvestmentStorageBlock_storage_content(storage1_0) +1 GenericInvestmentStorageBlock_storage_content(storage1_1) --1 flow(electricityBus_storage1_1) -+1 flow(storage1_electricityBus_1) +-1 flow(electricityBus_storage1_0_1) ++1 flow(storage1_electricityBus_0_1) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage1_2)_: +c_e_GenericInvestmentStorageBlock_balance(storage1_0_2)_: -1 GenericInvestmentStorageBlock_storage_content(storage1_1) +1 GenericInvestmentStorageBlock_storage_content(storage1_2) --1 flow(electricityBus_storage1_2) -+1 flow(storage1_electricityBus_2) +-1 flow(electricityBus_storage1_0_2) ++1 flow(storage1_electricityBus_0_2) = 0 c_e_GenericInvestmentStorageBlock_balanced_cstr(storage1)_: @@ -52,34 +57,35 @@ c_e_GenericInvestmentStorageBlock_balanced_cstr(storage1)_: +1 GenericInvestmentStorageBlock_storage_content(storage1_2) = 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0)_: --1 GenericInvestmentStorageBlock_invest(storage1) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0_0)_: +1 GenericInvestmentStorageBlock_storage_content(storage1_0) +-1 GenericInvestmentStorageBlock_total(storage1_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_1)_: --1 GenericInvestmentStorageBlock_invest(storage1) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0_1)_: +1 GenericInvestmentStorageBlock_storage_content(storage1_1) +-1 GenericInvestmentStorageBlock_total(storage1_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_2)_: --1 GenericInvestmentStorageBlock_invest(storage1) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0_2)_: +1 GenericInvestmentStorageBlock_storage_content(storage1_2) +-1 GenericInvestmentStorageBlock_total(storage1_0) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(electricityBus_storage1_0) <= +inf - 0 <= flow(electricityBus_storage1_1) <= +inf - 0 <= flow(electricityBus_storage1_2) <= +inf - 0 <= flow(storage1_electricityBus_0) <= +inf - 0 <= flow(storage1_electricityBus_1) <= +inf - 0 <= flow(storage1_electricityBus_2) <= +inf + 0 <= flow(electricityBus_storage1_0_0) <= +inf + 0 <= flow(electricityBus_storage1_0_1) <= +inf + 0 <= flow(electricityBus_storage1_0_2) <= +inf + 0 <= flow(storage1_electricityBus_0_0) <= +inf + 0 <= flow(storage1_electricityBus_0_1) <= +inf + 0 <= flow(storage1_electricityBus_0_2) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage1_0) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage1_1) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage1_2) <= +inf - 100 <= GenericInvestmentStorageBlock_invest(storage1) <= 200 + 100 <= GenericInvestmentStorageBlock_invest(storage1_0) <= 200 + 0 <= GenericInvestmentStorageBlock_total(storage1_0) <= +inf 0 <= GenericInvestmentStorageBlock_init_content(storage1) <= +inf end diff --git a/tests/lp_files/storage_invest_unbalanced.lp b/tests/lp_files/storage_invest_unbalanced.lp index b9e917e2d..bfed3cc52 100644 --- a/tests/lp_files/storage_invest_unbalanced.lp +++ b/tests/lp_files/storage_invest_unbalanced.lp @@ -1,122 +1,140 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+145 GenericInvestmentStorageBlock_invest(storage1) ++145 GenericInvestmentStorageBlock_invest(storage1_0) s.t. -c_e_BusBlock_balance(electricityBus_0)_: --1 flow(electricityBus_storage1_0) -+1 flow(storage1_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage1_0_0) ++1 flow(storage1_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: --1 flow(electricityBus_storage1_1) -+1 flow(storage1_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage1_0_1) ++1 flow(storage1_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: --1 flow(electricityBus_storage1_2) -+1 flow(storage1_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: +-1 flow(electricityBus_storage1_0_2) ++1 flow(storage1_electricityBus_0_2) = 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage1_0)_: --1 InvestmentFlowBlock_invest(electricityBus_storage1) -+1 flow(electricityBus_storage1_0) +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage1_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage1_0) ++1 InvestmentFlowBlock_total(electricityBus_storage1_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage1_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(storage1_electricityBus_0) ++1 InvestmentFlowBlock_total(storage1_electricityBus_0) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage1_0_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage1_0) ++1 flow(electricityBus_storage1_0_0) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage1_1)_: --1 InvestmentFlowBlock_invest(electricityBus_storage1) -+1 flow(electricityBus_storage1_1) +c_u_InvestmentFlowBlock_max(electricityBus_storage1_0_1)_: +-1 InvestmentFlowBlock_total(electricityBus_storage1_0) ++1 flow(electricityBus_storage1_0_1) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage1_2)_: --1 InvestmentFlowBlock_invest(electricityBus_storage1) -+1 flow(electricityBus_storage1_2) +c_u_InvestmentFlowBlock_max(electricityBus_storage1_0_2)_: +-1 InvestmentFlowBlock_total(electricityBus_storage1_0) ++1 flow(electricityBus_storage1_0_2) <= 0 -c_u_InvestmentFlowBlock_max(storage1_electricityBus_0)_: --1 InvestmentFlowBlock_invest(storage1_electricityBus) -+1 flow(storage1_electricityBus_0) +c_u_InvestmentFlowBlock_max(storage1_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(storage1_electricityBus_0) ++1 flow(storage1_electricityBus_0_0) <= 0 -c_u_InvestmentFlowBlock_max(storage1_electricityBus_1)_: --1 InvestmentFlowBlock_invest(storage1_electricityBus) -+1 flow(storage1_electricityBus_1) +c_u_InvestmentFlowBlock_max(storage1_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(storage1_electricityBus_0) ++1 flow(storage1_electricityBus_0_1) <= 0 -c_u_InvestmentFlowBlock_max(storage1_electricityBus_2)_: --1 InvestmentFlowBlock_invest(storage1_electricityBus) -+1 flow(storage1_electricityBus_2) +c_u_InvestmentFlowBlock_max(storage1_electricityBus_0_2)_: +-1 InvestmentFlowBlock_total(storage1_electricityBus_0) ++1 flow(storage1_electricityBus_0_2) <= 0 +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage1_0)_: +-1 GenericInvestmentStorageBlock_invest(storage1_0) ++1 GenericInvestmentStorageBlock_total(storage1_0) += 0 + c_e_GenericInvestmentStorageBlock_init_content_fix(storage1)_: +1 GenericInvestmentStorageBlock_init_content(storage1) --0.5 GenericInvestmentStorageBlock_invest(storage1) +-0.5 GenericInvestmentStorageBlock_invest(storage1_0) = 0 c_e_GenericInvestmentStorageBlock_balance_first(storage1)_: -1 GenericInvestmentStorageBlock_init_content(storage1) +1 GenericInvestmentStorageBlock_storage_content(storage1_0) --1 flow(electricityBus_storage1_0) -+1 flow(storage1_electricityBus_0) +-1 flow(electricityBus_storage1_0_0) ++1 flow(storage1_electricityBus_0_0) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage1_1)_: +c_e_GenericInvestmentStorageBlock_balance(storage1_0_1)_: -1 GenericInvestmentStorageBlock_storage_content(storage1_0) +1 GenericInvestmentStorageBlock_storage_content(storage1_1) --1 flow(electricityBus_storage1_1) -+1 flow(storage1_electricityBus_1) +-1 flow(electricityBus_storage1_0_1) ++1 flow(storage1_electricityBus_0_1) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage1_2)_: +c_e_GenericInvestmentStorageBlock_balance(storage1_0_2)_: -1 GenericInvestmentStorageBlock_storage_content(storage1_1) +1 GenericInvestmentStorageBlock_storage_content(storage1_2) --1 flow(electricityBus_storage1_2) -+1 flow(storage1_electricityBus_2) +-1 flow(electricityBus_storage1_0_2) ++1 flow(storage1_electricityBus_0_2) = 0 -c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage1)_: --1 GenericInvestmentStorageBlock_invest(storage1) -+1 InvestmentFlowBlock_invest(electricityBus_storage1) +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage1_0)_: +-1 GenericInvestmentStorageBlock_total(storage1_0) ++1 InvestmentFlowBlock_total(electricityBus_storage1_0) = 0 -c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage1)_: --1 GenericInvestmentStorageBlock_invest(storage1) -+1 InvestmentFlowBlock_invest(storage1_electricityBus) +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage1_0)_: +-1 GenericInvestmentStorageBlock_total(storage1_0) ++1 InvestmentFlowBlock_total(storage1_electricityBus_0) = 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0)_: --1 GenericInvestmentStorageBlock_invest(storage1) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0_0)_: +1 GenericInvestmentStorageBlock_storage_content(storage1_0) +-1 GenericInvestmentStorageBlock_total(storage1_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_1)_: --1 GenericInvestmentStorageBlock_invest(storage1) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0_1)_: +1 GenericInvestmentStorageBlock_storage_content(storage1_1) +-1 GenericInvestmentStorageBlock_total(storage1_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_2)_: --1 GenericInvestmentStorageBlock_invest(storage1) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0_2)_: +1 GenericInvestmentStorageBlock_storage_content(storage1_2) +-1 GenericInvestmentStorageBlock_total(storage1_0) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(electricityBus_storage1_0) <= +inf - 0 <= flow(electricityBus_storage1_1) <= +inf - 0 <= flow(electricityBus_storage1_2) <= +inf - 0 <= flow(storage1_electricityBus_0) <= +inf - 0 <= flow(storage1_electricityBus_1) <= +inf - 0 <= flow(storage1_electricityBus_2) <= +inf - 0 <= InvestmentFlowBlock_invest(electricityBus_storage1) <= +inf - 0 <= InvestmentFlowBlock_invest(storage1_electricityBus) <= +inf + 0 <= flow(electricityBus_storage1_0_0) <= +inf + 0 <= flow(electricityBus_storage1_0_1) <= +inf + 0 <= flow(electricityBus_storage1_0_2) <= +inf + 0 <= flow(storage1_electricityBus_0_0) <= +inf + 0 <= flow(storage1_electricityBus_0_1) <= +inf + 0 <= flow(storage1_electricityBus_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage1_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage1_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage1_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage1_electricityBus_0) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage1_0) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage1_1) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage1_2) <= +inf - 0 <= GenericInvestmentStorageBlock_invest(storage1) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage1_0) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage1_0) <= +inf 0 <= GenericInvestmentStorageBlock_init_content(storage1) <= +inf end diff --git a/tests/lp_files/storage_unbalanced.lp b/tests/lp_files/storage_unbalanced.lp index 833504861..f4e6fa6f8 100644 --- a/tests/lp_files/storage_unbalanced.lp +++ b/tests/lp_files/storage_unbalanced.lp @@ -1,57 +1,57 @@ \* Source Pyomo model name=Model *\ -min +min objective: +0 ONE_VAR_CONSTANT s.t. -c_e_BusBlock_balance(electricityBus_0)_: --1 flow(electricityBus_storage1_0) -+1 flow(storage1_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage1_0_0) ++1 flow(storage1_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: --1 flow(electricityBus_storage1_1) -+1 flow(storage1_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage1_0_1) ++1 flow(storage1_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: --1 flow(electricityBus_storage1_2) -+1 flow(storage1_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: +-1 flow(electricityBus_storage1_0_2) ++1 flow(storage1_electricityBus_0_2) = 0 c_e_GenericStorageBlock_balance_first(storage1)_: -1 GenericStorageBlock_init_content(storage1) +1 GenericStorageBlock_storage_content(storage1_0) --1 flow(electricityBus_storage1_0) -+1 flow(storage1_electricityBus_0) +-1 flow(electricityBus_storage1_0_0) ++1 flow(storage1_electricityBus_0_0) = 0 -c_e_GenericStorageBlock_balance(storage1_1)_: +c_e_GenericStorageBlock_balance(storage1_0_1)_: -1 GenericStorageBlock_storage_content(storage1_0) +1 GenericStorageBlock_storage_content(storage1_1) --1 flow(electricityBus_storage1_1) -+1 flow(storage1_electricityBus_1) +-1 flow(electricityBus_storage1_0_1) ++1 flow(storage1_electricityBus_0_1) = 0 -c_e_GenericStorageBlock_balance(storage1_2)_: +c_e_GenericStorageBlock_balance(storage1_0_2)_: -1 GenericStorageBlock_storage_content(storage1_1) +1 GenericStorageBlock_storage_content(storage1_2) --1 flow(electricityBus_storage1_2) -+1 flow(storage1_electricityBus_2) +-1 flow(electricityBus_storage1_0_2) ++1 flow(storage1_electricityBus_0_2) = 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(electricityBus_storage1_0) <= +inf - 0 <= flow(electricityBus_storage1_1) <= +inf - 0 <= flow(electricityBus_storage1_2) <= +inf - 0 <= flow(storage1_electricityBus_0) <= +inf - 0 <= flow(storage1_electricityBus_1) <= +inf - 0 <= flow(storage1_electricityBus_2) <= +inf + 0 <= flow(electricityBus_storage1_0_0) <= +inf + 0 <= flow(electricityBus_storage1_0_1) <= +inf + 0 <= flow(electricityBus_storage1_0_2) <= +inf + 0 <= flow(storage1_electricityBus_0_0) <= +inf + 0 <= flow(storage1_electricityBus_0_1) <= +inf + 0 <= flow(storage1_electricityBus_0_2) <= +inf 0 <= GenericStorageBlock_storage_content(storage1_0) <= 1111 0 <= GenericStorageBlock_storage_content(storage1_1) <= 1111 0 <= GenericStorageBlock_storage_content(storage1_2) <= 1111 From e3946e31c4297840fdf4a82595dae598ff1e1f0a Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 12 Feb 2022 17:08:30 +0100 Subject: [PATCH 0136/1363] Add bug fixes --- src/oemof/solph/constraints/investment_limit.py | 2 +- src/oemof/solph/flows/_non_convex_flow.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oemof/solph/constraints/investment_limit.py b/src/oemof/solph/constraints/investment_limit.py index 0401bf7f8..4aa10dd1a 100644 --- a/src/oemof/solph/constraints/investment_limit.py +++ b/src/oemof/solph/constraints/investment_limit.py @@ -196,7 +196,7 @@ def additional_investment_flow_limit(model, keyword, limit=None): def _additional_limit_rule(block): for p in model.PERIODS: lhs = sum( - model.InvestmentFlow.invest[inflow, outflow, p] + model.InvestmentFlowBlock.invest[inflow, outflow, p] * getattr(invest_flows[inflow, outflow], keyword) for (inflow, outflow) in invest_flows ) diff --git a/src/oemof/solph/flows/_non_convex_flow.py b/src/oemof/solph/flows/_non_convex_flow.py index 5607eaaef..76787302f 100644 --- a/src/oemof/solph/flows/_non_convex_flow.py +++ b/src/oemof/solph/flows/_non_convex_flow.py @@ -673,7 +673,7 @@ def _objective_expression(self): inactivity_costs = 0 gradient_costs = 0 - if not m.em.multi_period: + if not m.es.multi_period: if self.STARTUPFLOWS: for i, o in self.STARTUPFLOWS: if m.flows[i, o].nonconvex.startup_costs[0] is not None: From 02d62c93c2836d0ace53380b02892878dff47709 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 12 Feb 2022 17:23:17 +0100 Subject: [PATCH 0137/1363] Add lower bounds for gradient variables --- src/oemof/solph/flows/_flow.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index da80ede59..ba26f6321 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -23,6 +23,7 @@ from pyomo.core import BuildAction, Expression from pyomo.core import Constraint from pyomo.core import NonNegativeIntegers +from pyomo.core import NonNegativeReals from pyomo.core import Set from pyomo.core import Var from pyomo.core.base.block import SimpleBlock @@ -409,9 +410,13 @@ def _create(self, group=None): ) # ######################### Variables ################################ - self.positive_gradient = Var(self.POSITIVE_GRADIENT_FLOWS, m.TIMESTEPS) + self.positive_gradient = Var( + self.POSITIVE_GRADIENT_FLOWS, m.TIMESTEPS, within=NonNegativeReals + ) - self.negative_gradient = Var(self.NEGATIVE_GRADIENT_FLOWS, m.TIMESTEPS) + self.negative_gradient = Var( + self.NEGATIVE_GRADIENT_FLOWS, m.TIMESTEPS, within=NonNegativeReals + ) self.integer_flow = Var( self.INTEGER_FLOWS, m.TIMESTEPS, within=NonNegativeIntegers From ace6a413151d713041ab44211cde54708ef948f6 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 12 Feb 2022 17:37:13 +0100 Subject: [PATCH 0138/1363] Fix nonconvex gradients (missing bounds) --- src/oemof/solph/flows/_non_convex_flow.py | 107 ++++++++-------------- 1 file changed, 38 insertions(+), 69 deletions(-) diff --git a/src/oemof/solph/flows/_non_convex_flow.py b/src/oemof/solph/flows/_non_convex_flow.py index 76787302f..ec909fc06 100644 --- a/src/oemof/solph/flows/_non_convex_flow.py +++ b/src/oemof/solph/flows/_non_convex_flow.py @@ -20,6 +20,7 @@ from pyomo.core import BuildAction from pyomo.core import Constraint from pyomo.core import Expression +from pyomo.core import NonNegativeReals from pyomo.core import Set from pyomo.core import Var from pyomo.core.base.block import SimpleBlock @@ -425,14 +426,27 @@ def _create(self, group=None): if self.POSITIVE_GRADIENT_FLOWS: self.positive_gradient = Var( - self.POSITIVE_GRADIENT_FLOWS, m.TIMESTEPS + self.POSITIVE_GRADIENT_FLOWS, m.TIMESTEPS, within=NonNegativeReals ) if self.NEGATIVE_GRADIENT_FLOWS: self.negative_gradient = Var( - self.NEGATIVE_GRADIENT_FLOWS, m.TIMESTEPS + self.NEGATIVE_GRADIENT_FLOWS, m.TIMESTEPS, within=NonNegativeReals ) + # set upper bound of gradient variable + for i, o, f in group: + if m.flows[i, o].nonconvex.positive_gradient["ub"][0] is not None: + for t in m.TIMESTEPS: + self.positive_gradient[i, o, t].setub( + f.nonconvex.positive_gradient["ub"][t] * f.nominal_value + ) + if m.flows[i, o].nonconvex.negative_gradient["ub"][0] is not None: + for t in m.TIMESTEPS: + self.negative_gradient[i, o, t].setub( + f.nonconvex.negative_gradient["ub"][t] * f.nominal_value + ) + def _minimum_flow_rule(block, i, o, p, t): """Rule definition for MILP minimum flow constraints.""" expr = ( @@ -608,7 +622,17 @@ def _positive_gradient_flow_rule(block): lhs <= rhs, ) else: - pass # return(Constraint.Skip) + lhs = self.positive_gradient[i, o, 0] + rhs = 0 + self.positive_gradient_constr.add( + ( + i, + o, + m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1], + ), + lhs == rhs, + ) self.positive_gradient_constr = Constraint( self.POSITIVE_GRADIENT_FLOWS, m.TIMEINDEX, noruleinit=True @@ -651,7 +675,17 @@ def _negative_gradient_flow_rule(block): lhs <= rhs, ) else: - pass # return(Constraint.Skip) + lhs = self.negative_gradient[i, o, 0] + rhs = 0 + self.negative_gradient_constr.add( + ( + i, + o, + m.TIMEINDEX[index][0], + m.TIMEINDEX[index][1], + ), + lhs == rhs, + ) self.negative_gradient_constr = Constraint( self.NEGATIVE_GRADIENT_FLOWS, m.TIMEINDEX, noruleinit=True @@ -714,36 +748,6 @@ def _objective_expression(self): self.inactivity_costs = Expression(expr=inactivity_costs) - if self.POSITIVE_GRADIENT_FLOWS: - for i, o in self.POSITIVE_GRADIENT_FLOWS: - if ( - m.flows[i, o].nonconvex.positive_gradient["ub"][0] - is not None - ): - for t in m.TIMESTEPS: - gradient_costs += self.positive_gradient[ - i, o, t - ] * ( - m.flows[i, o].nonconvex.positive_gradient[ - "costs" - ] - ) - - if self.NEGATIVE_GRADIENT_FLOWS: - for i, o in self.NEGATIVE_GRADIENT_FLOWS: - if ( - m.flows[i, o].nonconvex.negative_gradient["ub"][0] - is not None - ): - for t in m.TIMESTEPS: - gradient_costs += self.negative_gradient[ - i, o, t - ] * ( - m.flows[i, o].nonconvex.negative_gradient[ - "costs" - ] - ) - else: if self.STARTUPFLOWS: for i, o in self.STARTUPFLOWS: @@ -789,41 +793,6 @@ def _objective_expression(self): for p, t in m.TIMEINDEX ) - if self.POSITIVE_GRADIENT_FLOWS: - for i, o in self.POSITIVE_GRADIENT_FLOWS: - if ( - m.flows[i, o].nonconvex.positive_gradient["ub"][0] - is not None - ): - gradient_costs += sum( - self.positive_gradient[i, o, t] - * ( - m.flows[i, o].nonconvex.positive_gradient[ - "costs" - ] - ) - * m.objective_weighting[t] - * ((1 + m.discount_rate) ** -m.es.periods_years[p]) - for p, t in m.TIMEINDEX - ) - - if self.NEGATIVE_GRADIENT_FLOWS: - for i, o in self.NEGATIVE_GRADIENT_FLOWS: - if ( - m.flows[i, o].nonconvex.negative_gradient["ub"][0] - ) is not None: - gradient_costs += sum( - self.negative_gradient[i, o, t] - * ( - m.flows[i, o].nonconvex.negative_gradient[ - "costs" - ] - ) - * m.objective_weighting[t] - * ((1 + m.discount_rate) ** -m.es.periods_years[p]) - for p, t in m.TIMEINDEX - ) - self.activity_costs = Expression(expr=activity_costs) self.inactivity_costs = Expression(expr=inactivity_costs) self.gradient_costs = Expression(expr=gradient_costs) From 67fb6639636a0657d12bc86403ab4bd50d9ddb6b Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 12 Feb 2022 18:21:09 +0100 Subject: [PATCH 0139/1363] Fix bug and type hint --- src/oemof/solph/components/_offset_transformer.py | 2 +- src/oemof/solph/components/experimental/_sink_dsm.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/oemof/solph/components/_offset_transformer.py b/src/oemof/solph/components/_offset_transformer.py index 63a86a3db..ed6547af7 100644 --- a/src/oemof/solph/components/_offset_transformer.py +++ b/src/oemof/solph/components/_offset_transformer.py @@ -154,7 +154,7 @@ def _relation_rule(block, n, p, t): * n.coefficients[1][t] ) expr += ( - m.NonConvexFlow.status[list(n.inputs.keys())[0], n, t] + m.NonConvexFlowBlock.status[list(n.inputs.keys())[0], n, t] ) * n.coefficients[0][t] return expr == 0 diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index 6b6b96158..8c666994f 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -83,9 +83,9 @@ class SinkDSM(Sink): demand timeseries and the cumulated (fixed) infeed time series for normalization, because the balancing potential may be determined by both. Elsewise, underinvestments may occur. - capacity_up: int or array + capacity_up: int or iterable maximum DSM capacity that may be increased (normalized) - capacity_down: int or array + capacity_down: int or iterable maximum DSM capacity that may be reduced (normalized) approach: 'oemof', 'DIW', 'DLR' Choose one of the DSM modeling approaches. Read notes about which From a5f7fe9ae9b4b7335606e1c573886c8c2829cee4 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 12 Feb 2022 18:38:47 +0100 Subject: [PATCH 0140/1363] Attribute "old" variable to multi-period only and reformat --- .../components/experimental/_sink_dsm.py | 337 +++++++++--------- 1 file changed, 174 insertions(+), 163 deletions(-) diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index 8c666994f..f59436ac8 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -1009,8 +1009,7 @@ def _old_dsm_capacity_rule_exo(block): # Track decommissioning status if not is_decommissioned: expr = ( - self.old_exo[g, p] - == g.investment.existing + self.old_exo[g, p] == g.investment.existing ) is_decommissioned = True else: @@ -2173,15 +2172,20 @@ def _dsm_investvar_bound_rule(block, g, p): # Total capacity self.total = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) - # Old capacity to be decommissioned (due to lifetime) - # Old capacity is built out of old exogenous and endogenous capacities - self.old = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) + if m.es.multi_period: + # Old capacity to be decommissioned (due to lifetime) + # Old capacity is built out of old exogenous and endogenous capacities + self.old = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) - # Old endogenous capacity to be decommissioned (due to lifetime) - self.old_end = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) + # Old endogenous capacity to be decommissioned (due to lifetime) + self.old_end = Var( + self.investdsm, m.PERIODS, within=NonNegativeReals + ) - # Old exogenous capacity to be decommissioned (due to lifetime) - self.old_exo = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) + # Old exogenous capacity to be decommissioned (due to lifetime) + self.old_exo = Var( + self.investdsm, m.PERIODS, within=NonNegativeReals + ) # Variable load shift down self.dsm_do_shift = Var( @@ -2229,87 +2233,88 @@ def _total_dsm_capacity_rule(block): self.total_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) self.total_dsm_rule_build = BuildAction(rule=_total_dsm_capacity_rule) - def _old_dsm_capacity_rule_end(block): - """Rule definition for determining old endogenously installed - capacity to be decommissioned due to reaching its lifetime - """ - for g in group: - lifetime = g.investment.lifetime - for p in m.PERIODS: - # No shutdown in first period - if p == 0: - expr = self.old_end[g, p] == 0 - self.old_dsm_rule_end.add((g, p), expr) - elif lifetime <= m.es.periods_years[p]: - # Obtain commissioning period - comm_p = 0 - for k, v in m.es.periods_years.items(): - if m.es.periods_years[p] - lifetime - v < 0: - # change of sign is detected - comm_p = k - 1 - break - expr = self.old_end[g, p] == self.invest[g, comm_p] - self.old_dsm_rule_end.add((g, p), expr) - else: - expr = self.old_end[g, p] == 0 - self.old_dsm_rule_end.add((g, p), expr) + if m.es.multi_period: - self.old_dsm_rule_end = Constraint( - group, m.PERIODS, noruleinit=True - ) - self.old_dsm_rule_end_build = BuildAction( - rule=_old_dsm_capacity_rule_end - ) + def _old_dsm_capacity_rule_end(block): + """Rule definition for determining old endogenously installed + capacity to be decommissioned due to reaching its lifetime + """ + for g in group: + lifetime = g.investment.lifetime + for p in m.PERIODS: + # No shutdown in first period + if p == 0: + expr = self.old_end[g, p] == 0 + self.old_dsm_rule_end.add((g, p), expr) + elif lifetime <= m.es.periods_years[p]: + # Obtain commissioning period + comm_p = 0 + for k, v in m.es.periods_years.items(): + if m.es.periods_years[p] - lifetime - v < 0: + # change of sign is detected + comm_p = k - 1 + break + expr = self.old_end[g, p] == self.invest[g, comm_p] + self.old_dsm_rule_end.add((g, p), expr) + else: + expr = self.old_end[g, p] == 0 + self.old_dsm_rule_end.add((g, p), expr) - def _old_dsm_capacity_rule_exo(block): - """Rule definition for determining old exogenously given - capacity to be decommissioned due to reaching its lifetime - """ - for g in group: - age = g.investment.age - lifetime = g.investment.lifetime - is_decommissioned = False - for p in m.PERIODS: - # No shutdown in first period - if p == 0: - expr = self.old_exo[g, p] == 0 - self.old_dsm_rule_exo.add((g, p), expr) - elif lifetime - age <= m.es.periods_years[p]: - # Track decommissioning status - if not is_decommissioned: - expr = ( - self.old_exo[g, p] - == g.investment.existing - ) - is_decommissioned = True + self.old_dsm_rule_end = Constraint( + group, m.PERIODS, noruleinit=True + ) + self.old_dsm_rule_end_build = BuildAction( + rule=_old_dsm_capacity_rule_end + ) + + def _old_dsm_capacity_rule_exo(block): + """Rule definition for determining old exogenously given + capacity to be decommissioned due to reaching its lifetime + """ + for g in group: + age = g.investment.age + lifetime = g.investment.lifetime + is_decommissioned = False + for p in m.PERIODS: + # No shutdown in first period + if p == 0: + expr = self.old_exo[g, p] == 0 + self.old_dsm_rule_exo.add((g, p), expr) + elif lifetime - age <= m.es.periods_years[p]: + # Track decommissioning status + if not is_decommissioned: + expr = ( + self.old_exo[g, p] == g.investment.existing + ) + is_decommissioned = True + else: + expr = self.old_exo[g, p] == 0 + self.old_dsm_rule_exo.add((g, p), expr) else: expr = self.old_exo[g, p] == 0 - self.old_dsm_rule_exo.add((g, p), expr) - else: - expr = self.old_exo[g, p] == 0 - self.old_dsm_rule_exo.add((g, p), expr) + self.old_dsm_rule_exo.add((g, p), expr) - self.old_dsm_rule_exo = Constraint( - group, m.PERIODS, noruleinit=True - ) - self.old_dsm_rule_exo_build = BuildAction( - rule=_old_dsm_capacity_rule_exo - ) + self.old_dsm_rule_exo = Constraint( + group, m.PERIODS, noruleinit=True + ) + self.old_dsm_rule_exo_build = BuildAction( + rule=_old_dsm_capacity_rule_exo + ) - def _old_dsm_capacity_rule(block): - """Rule definition for determining (overall) old capacity - to be decommissioned due to reaching its lifetime - """ - for g in group: - for p in m.PERIODS: - expr = ( - self.old[g, p] - == self.old_end[g, p] + self.old_exo[g, p] - ) - self.old_dsm_rule.add((g, p), expr) + def _old_dsm_capacity_rule(block): + """Rule definition for determining (overall) old capacity + to be decommissioned due to reaching its lifetime + """ + for g in group: + for p in m.PERIODS: + expr = ( + self.old[g, p] + == self.old_end[g, p] + self.old_exo[g, p] + ) + self.old_dsm_rule.add((g, p), expr) - self.old_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) - self.old_dsm_rule_build = BuildAction(rule=_old_dsm_capacity_rule) + self.old_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_build = BuildAction(rule=_old_dsm_capacity_rule) def _shift_shed_vars_rule(block): """Force shifting resp. shedding variables to zero dependent @@ -4231,14 +4236,19 @@ def _dr_investvar_bound_rule(block, g, p): # Total capacity self.total = Var(self.INVESTDR, m.PERIODS, within=NonNegativeReals) - # Old capacity to be decommissioned (due to lifetime) - self.old = Var(self.INVESTDR, m.PERIODS, within=NonNegativeReals) + if m.es.multi_period: + # Old capacity to be decommissioned (due to lifetime) + self.old = Var(self.INVESTDR, m.PERIODS, within=NonNegativeReals) - # Old endogenous capacity to be decommissioned (due to lifetime) - self.old_end = Var(self.INVESTDR, m.PERIODS, within=NonNegativeReals) + # Old endogenous capacity to be decommissioned (due to lifetime) + self.old_end = Var( + self.INVESTDR, m.PERIODS, within=NonNegativeReals + ) - # Old exogenous capacity to be decommissioned (due to lifetime) - self.old_exo = Var(self.INVESTDR, m.PERIODS, within=NonNegativeReals) + # Old exogenous capacity to be decommissioned (due to lifetime) + self.old_exo = Var( + self.INVESTDR, m.PERIODS, within=NonNegativeReals + ) # Variable load shift down (capacity) self.dsm_do_shift = Var( @@ -4302,87 +4312,88 @@ def _total_capacity_rule(block): self.total_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) self.total_dsm_rule_build = BuildAction(rule=_total_capacity_rule) - def _old_dsm_capacity_rule_end(block): - """Rule definition for determining old endogenously installed - capacity to be decommissioned due to reaching its lifetime - """ - for g in group: - lifetime = g.investment.lifetime - for p in m.PERIODS: - # No shutdown in first period - if p == 0: - expr = self.old_end[g, p] == 0 - self.old_dsm_rule_end.add((g, p), expr) - elif lifetime <= m.es.periods_years[p]: - # Obtain commissioning period - comm_p = 0 - for k, v in m.es.periods_years.items(): - if m.es.periods_years[p] - lifetime - v < 0: - # change of sign is detected - comm_p = k - 1 - break - expr = self.old_end[g, p] == self.invest[g, comm_p] - self.old_dsm_rule_end.add((g, p), expr) - else: - expr = self.old_end[g, p] == 0 - self.old_dsm_rule_end.add((g, p), expr) + if m.es.multi_period: - self.old_dsm_rule_end = Constraint( - group, m.PERIODS, noruleinit=True - ) - self.old_dsm_rule_end_build = BuildAction( - rule=_old_dsm_capacity_rule_end - ) + def _old_dsm_capacity_rule_end(block): + """Rule definition for determining old endogenously installed + capacity to be decommissioned due to reaching its lifetime + """ + for g in group: + lifetime = g.investment.lifetime + for p in m.PERIODS: + # No shutdown in first period + if p == 0: + expr = self.old_end[g, p] == 0 + self.old_dsm_rule_end.add((g, p), expr) + elif lifetime <= m.es.periods_years[p]: + # Obtain commissioning period + comm_p = 0 + for k, v in m.es.periods_years.items(): + if m.es.periods_years[p] - lifetime - v < 0: + # change of sign is detected + comm_p = k - 1 + break + expr = self.old_end[g, p] == self.invest[g, comm_p] + self.old_dsm_rule_end.add((g, p), expr) + else: + expr = self.old_end[g, p] == 0 + self.old_dsm_rule_end.add((g, p), expr) - def _old_dsm_capacity_rule_exo(block): - """Rule definition for determining old exogenously given - capacity to be decommissioned due to reaching its lifetime - """ - for g in group: - age = g.investment.age - lifetime = g.investment.lifetime - is_decommissioned = False - for p in m.PERIODS: - # No shutdown in first period - if p == 0: - expr = self.old_exo[g, p] == 0 - self.old_dsm_rule_exo.add((g, p), expr) - elif lifetime - age <= m.es.periods_years[p]: - # Track decommissioning status - if not is_decommissioned: - expr = ( - self.old_exo[g, p] - == g.investment.existing - ) - is_decommissioned = True + self.old_dsm_rule_end = Constraint( + group, m.PERIODS, noruleinit=True + ) + self.old_dsm_rule_end_build = BuildAction( + rule=_old_dsm_capacity_rule_end + ) + + def _old_dsm_capacity_rule_exo(block): + """Rule definition for determining old exogenously given + capacity to be decommissioned due to reaching its lifetime + """ + for g in group: + age = g.investment.age + lifetime = g.investment.lifetime + is_decommissioned = False + for p in m.PERIODS: + # No shutdown in first period + if p == 0: + expr = self.old_exo[g, p] == 0 + self.old_dsm_rule_exo.add((g, p), expr) + elif lifetime - age <= m.es.periods_years[p]: + # Track decommissioning status + if not is_decommissioned: + expr = ( + self.old_exo[g, p] == g.investment.existing + ) + is_decommissioned = True + else: + expr = self.old_exo[g, p] == 0 + self.old_dsm_rule_exo.add((g, p), expr) else: expr = self.old_exo[g, p] == 0 - self.old_dsm_rule_exo.add((g, p), expr) - else: - expr = self.old_exo[g, p] == 0 - self.old_dsm_rule_exo.add((g, p), expr) + self.old_dsm_rule_exo.add((g, p), expr) - self.old_dsm_rule_exo = Constraint( - group, m.PERIODS, noruleinit=True - ) - self.old_dsm_rule_exo_build = BuildAction( - rule=_old_dsm_capacity_rule_exo - ) + self.old_dsm_rule_exo = Constraint( + group, m.PERIODS, noruleinit=True + ) + self.old_dsm_rule_exo_build = BuildAction( + rule=_old_dsm_capacity_rule_exo + ) - def _old_dsm_capacity_rule(block): - """Rule definition for determining (overall) old capacity - to be decommissioned due to reaching its lifetime - """ - for g in group: - for p in m.PERIODS: - expr = ( - self.old[g, p] - == self.old_end[g, p] + self.old_exo[g, p] - ) - self.old_dsm_rule.add((g, p), expr) + def _old_dsm_capacity_rule(block): + """Rule definition for determining (overall) old capacity + to be decommissioned due to reaching its lifetime + """ + for g in group: + for p in m.PERIODS: + expr = ( + self.old[g, p] + == self.old_end[g, p] + self.old_exo[g, p] + ) + self.old_dsm_rule.add((g, p), expr) - self.old_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) - self.old_dsm_rule_build = BuildAction(rule=_old_dsm_capacity_rule) + self.old_dsm_rule = Constraint(group, m.PERIODS, noruleinit=True) + self.old_dsm_rule_build = BuildAction(rule=_old_dsm_capacity_rule) def _shift_shed_vars_rule(block): """Force shifting resp. shedding variables to zero dependent From 959fb96a2a9867b984ba3ecbfb8895a09a27448a Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 12 Feb 2022 18:39:20 +0100 Subject: [PATCH 0141/1363] Update most constraint tests TODO: Resume! --- tests/constraint_tests.py | 36 +-- tests/lp_files/activity_costs.lp | 52 ++-- tests/lp_files/connect_investment.lp | 268 ++++++++++-------- tests/lp_files/dsm_module_DIW.lp | 34 +-- tests/lp_files/dsm_module_DLR.lp | 34 +-- tests/lp_files/dsm_module_oemof.lp | 34 +-- tests/lp_files/emission_limit.lp | 58 ++-- tests/lp_files/emission_limit_no_error.lp | 40 +++ tests/lp_files/flow_count_limit.lp | 130 ++++----- tests/lp_files/generic_invest_limit.lp | 136 +++++---- tests/lp_files/inactivity_costs.lp | 52 ++-- tests/lp_files/investment_limit.lp | 182 ++++++------ tests/lp_files/linear_transformer_chp.lp | 100 +++---- .../lp_files/linear_transformer_chp_invest.lp | 128 +++++---- tests/lp_files/maximum_shutdowns.lp | 52 ++-- tests/lp_files/maximum_startups.lp | 52 ++-- tests/lp_files/min_max_runtime.lp | 68 ++--- tests/lp_files/offsettransformer.lp | 82 +++--- .../piecewise_linear_transformer_cc.lp | 70 ++--- .../piecewise_linear_transformer_dcc.lp | 70 ++--- tests/lp_files/shared_limit.lp | 90 +++--- tests/lp_files/source_with_gradient.lp | 70 +++-- .../source_with_nonconvex_gradient.lp | 90 +++--- tests/lp_files/storage_fixed_losses.lp | 62 ++-- .../lp_files/storage_invest_1_fixed_losses.lp | 168 ++++++----- tests/lp_files/transformer.lp | 160 +++++------ tests/lp_files/transformer_invest.lp | 188 ++++++------ .../transformer_invest_with_existing.lp | 194 +++++++------ tests/lp_files/variable_chp.lp | 178 ++++++------ 29 files changed, 1524 insertions(+), 1354 deletions(-) create mode 100644 tests/lp_files/emission_limit_no_error.lp diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index f60755d0e..9ef76d69b 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -528,7 +528,7 @@ def test_transformer(self): bth = solph.buses.Bus(label="thermalBus") solph.components.Transformer( - label="powerplantGasCoal", + label="powerplantGasBiomass", inputs={bbms: solph.flows.Flow(), bgas: solph.flows.Flow()}, outputs={ bel: solph.flows.Flow(variable_costs=50), @@ -598,7 +598,7 @@ def test_linear_transformer_chp(self): solph.components.Transformer( label="CHPpowerplantGas", inputs={ - bgas: solph.flows.Flow(nominal_value=10e10, variable_costs=50) + bgas: solph.flows.Flow(nominal_value=1e11, variable_costs=50) }, outputs={bel: solph.flows.Flow(), bheat: solph.flows.Flow()}, conversion_factors={bel: 0.4, bheat: 0.5}, @@ -628,7 +628,7 @@ def test_linear_transformer_chp_invest(self): self.compare_lp_files("linear_transformer_chp_invest.lp") def test_variable_chp(self): - """ """ + """Test ExctractionTurbineCHP basic functionality""" bel = solph.buses.Bus(label="electricityBus") bth = solph.buses.Bus(label="heatBus") bgas = solph.buses.Bus(label="commodityBus") @@ -652,7 +652,7 @@ def test_variable_chp(self): self.compare_lp_files("variable_chp.lp") def test_generic_invest_limit(self): - """ """ + """Test a generic keyword investment limit""" bus = solph.buses.Bus(label="bus_1") solph.components.Source( @@ -689,7 +689,7 @@ def test_generic_invest_limit(self): self.compare_lp_files("generic_invest_limit.lp", my_om=om) def test_emission_constraints(self): - """ """ + """Test emissions constraint""" bel = solph.buses.Bus(label="electricityBus") solph.components.Source( @@ -719,7 +719,7 @@ def test_emission_constraints(self): self.compare_lp_files("emission_limit.lp", my_om=om) def test_flow_count_limit(self): - """ """ + """Test limiting the count of nonconvex flows""" bel = solph.buses.Bus(label="electricityBus") solph.components.Source( @@ -773,7 +773,7 @@ def test_flow_count_limit(self): self.compare_lp_files("flow_count_limit.lp", my_om=om) def test_shared_limit(self): - """ """ + """Test an overall limit shared among components""" b1 = solph.buses.Bus(label="bus") storage1 = solph.components.GenericStorage( @@ -805,7 +805,7 @@ def test_shared_limit(self): self.compare_lp_files("shared_limit.lp", my_om=model) def test_flow_without_emission_for_emission_constraint(self): - """ """ + """Test AttributeError if passed flow misses emission attribute""" def define_emission_limit(): bel = solph.buses.Bus(label="electricityBus") @@ -827,7 +827,7 @@ def define_emission_limit(): assert_raises(AttributeError, define_emission_limit) def test_flow_without_emission_for_emission_constraint_no_error(self): - """ """ + """Test that no error is thrown if no flows are explicity passed""" bel = solph.buses.Bus(label="electricityBus") solph.components.Source( label="source1", @@ -841,11 +841,13 @@ def test_flow_without_emission_for_emission_constraint_no_error(self): om = self.get_om() solph.constraints.emission_limit(om, limit=777) + self.compare_lp_files("emission_limit_no_error.lp", my_om=om) + def test_equate_variables_constraint(self): """Testing the equate_variables function in the constraint module.""" bus1 = solph.buses.Bus(label="Bus1") storage = solph.components.GenericStorage( - label="storage_constraint", + label="storage", invest_relation_input_capacity=0.2, invest_relation_output_capacity=0.2, inputs={bus1: solph.flows.Flow()}, @@ -871,20 +873,20 @@ def test_equate_variables_constraint(self): om = self.get_om() solph.constraints.equate_variables( om, - om.InvestmentFlowBlock.invest[source, bus1], - om.InvestmentFlowBlock.invest[bus1, sink], + om.InvestmentFlowBlock.invest[source, bus1, 0], + om.InvestmentFlowBlock.invest[bus1, sink, 0], 2, ) solph.constraints.equate_variables( om, - om.InvestmentFlowBlock.invest[source, bus1], - om.GenericInvestmentStorageBlock.invest[storage], + om.InvestmentFlowBlock.invest[source, bus1, 0], + om.GenericInvestmentStorageBlock.invest[storage, 0], ) self.compare_lp_files("connect_investment.lp", my_om=om) def test_gradient(self): - """Testing gradient constraints and costs.""" + """Testing gradient constraints""" bel = solph.buses.Bus(label="electricityBus") solph.components.Source( @@ -902,7 +904,7 @@ def test_gradient(self): self.compare_lp_files("source_with_gradient.lp") def test_nonconvex_gradient(self): - """Testing gradient constraints and costs.""" + """Testing gradient constraints""" bel = solph.buses.Bus(label="electricityBus") solph.components.Source( @@ -990,7 +992,7 @@ def test_min_max_runtime(self): variable_costs=10, minimum_downtime=4, minimum_uptime=2, - initial_status=2, + initial_status=1, startup_costs=5, shutdown_costs=7, ) diff --git a/tests/lp_files/activity_costs.lp b/tests/lp_files/activity_costs.lp index 35b61852e..6a2bf63f6 100644 --- a/tests/lp_files/activity_costs.lp +++ b/tests/lp_files/activity_costs.lp @@ -1,65 +1,65 @@ \* Source Pyomo model name=Model *\ -min +min objective: +2 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_0) +2 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_1) +2 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_2) -+10 flow(cheap_plant_activity_costs_Bus_C_0) -+10 flow(cheap_plant_activity_costs_Bus_C_1) -+10 flow(cheap_plant_activity_costs_Bus_C_2) ++10 flow(cheap_plant_activity_costs_Bus_C_0_0) ++10 flow(cheap_plant_activity_costs_Bus_C_0_1) ++10 flow(cheap_plant_activity_costs_Bus_C_0_2) s.t. -c_e_BusBlock_balance(Bus_C_0)_: -+1 flow(cheap_plant_activity_costs_Bus_C_0) +c_e_BusBlock_balance(Bus_C_0_0)_: ++1 flow(cheap_plant_activity_costs_Bus_C_0_0) = 0 -c_e_BusBlock_balance(Bus_C_1)_: -+1 flow(cheap_plant_activity_costs_Bus_C_1) +c_e_BusBlock_balance(Bus_C_0_1)_: ++1 flow(cheap_plant_activity_costs_Bus_C_0_1) = 0 -c_e_BusBlock_balance(Bus_C_2)_: -+1 flow(cheap_plant_activity_costs_Bus_C_2) +c_e_BusBlock_balance(Bus_C_0_2)_: ++1 flow(cheap_plant_activity_costs_Bus_C_0_2) = 0 -c_u_NonConvexFlowBlock_min(cheap_plant_activity_costs_Bus_C_0)_: +c_u_NonConvexFlowBlock_min(cheap_plant_activity_costs_Bus_C_0_0)_: +5 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_0) --1 flow(cheap_plant_activity_costs_Bus_C_0) +-1 flow(cheap_plant_activity_costs_Bus_C_0_0) <= 0 -c_u_NonConvexFlowBlock_min(cheap_plant_activity_costs_Bus_C_1)_: +c_u_NonConvexFlowBlock_min(cheap_plant_activity_costs_Bus_C_0_1)_: +5 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_1) --1 flow(cheap_plant_activity_costs_Bus_C_1) +-1 flow(cheap_plant_activity_costs_Bus_C_0_1) <= 0 -c_u_NonConvexFlowBlock_min(cheap_plant_activity_costs_Bus_C_2)_: +c_u_NonConvexFlowBlock_min(cheap_plant_activity_costs_Bus_C_0_2)_: +5 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_2) --1 flow(cheap_plant_activity_costs_Bus_C_2) +-1 flow(cheap_plant_activity_costs_Bus_C_0_2) <= 0 -c_u_NonConvexFlowBlock_max(cheap_plant_activity_costs_Bus_C_0)_: +c_u_NonConvexFlowBlock_max(cheap_plant_activity_costs_Bus_C_0_0)_: -10 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_0) -+1 flow(cheap_plant_activity_costs_Bus_C_0) ++1 flow(cheap_plant_activity_costs_Bus_C_0_0) <= 0 -c_u_NonConvexFlowBlock_max(cheap_plant_activity_costs_Bus_C_1)_: +c_u_NonConvexFlowBlock_max(cheap_plant_activity_costs_Bus_C_0_1)_: -10 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_1) -+1 flow(cheap_plant_activity_costs_Bus_C_1) ++1 flow(cheap_plant_activity_costs_Bus_C_0_1) <= 0 -c_u_NonConvexFlowBlock_max(cheap_plant_activity_costs_Bus_C_2)_: +c_u_NonConvexFlowBlock_max(cheap_plant_activity_costs_Bus_C_0_2)_: -10 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_2) -+1 flow(cheap_plant_activity_costs_Bus_C_2) ++1 flow(cheap_plant_activity_costs_Bus_C_0_2) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(cheap_plant_activity_costs_Bus_C_0) <= 10 - 0 <= flow(cheap_plant_activity_costs_Bus_C_1) <= 10 - 0 <= flow(cheap_plant_activity_costs_Bus_C_2) <= 10 + 0 <= flow(cheap_plant_activity_costs_Bus_C_0_0) <= 10 + 0 <= flow(cheap_plant_activity_costs_Bus_C_0_1) <= 10 + 0 <= flow(cheap_plant_activity_costs_Bus_C_0_2) <= 10 0 <= NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_0) <= 1 0 <= NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_1) <= 1 0 <= NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_2) <= 1 diff --git a/tests/lp_files/connect_investment.lp b/tests/lp_files/connect_investment.lp index d77aa2cc1..34e6daa75 100644 --- a/tests/lp_files/connect_investment.lp +++ b/tests/lp_files/connect_investment.lp @@ -1,183 +1,213 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+145 GenericInvestmentStorageBlock_invest(storage_constraint) -+500 InvestmentFlowBlock_invest(Bus1_Sink) -+123 InvestmentFlowBlock_invest(Source_Bus1) ++145 GenericInvestmentStorageBlock_invest(storage_0) ++500 InvestmentFlowBlock_invest(Bus1_Sink_0) ++123 InvestmentFlowBlock_invest(Source_Bus1_0) s.t. -c_e_equate_InvestmentFlowBlock_invest(Source_Bus1)_InvestmentFlowBlock_invest(Bus1_Sink)_: --1 InvestmentFlowBlock_invest(Bus1_Sink) -+2 InvestmentFlowBlock_invest(Source_Bus1) +c_e_equate_InvestmentFlowBlock_invest(Source_Bus1_0)_InvestmentFlowBlock_invest(Bus1_Sink_0)_: +-1 InvestmentFlowBlock_invest(Bus1_Sink_0) ++2 InvestmentFlowBlock_invest(Source_Bus1_0) = 0 -c_e_equate_InvestmentFlowBlock_invest(Source_Bus1)_GenericInvestmentStorageBlock_invest(storage_constraint)_: --1 GenericInvestmentStorageBlock_invest(storage_constraint) -+1 InvestmentFlowBlock_invest(Source_Bus1) +c_e_equate_InvestmentFlowBlock_invest(Source_Bus1_0)_GenericInvestmentStorageBlock_invest(storage_0)_: +-1 GenericInvestmentStorageBlock_invest(storage_0) ++1 InvestmentFlowBlock_invest(Source_Bus1_0) = 0 -c_e_BusBlock_balance(Bus1_0)_: --1 flow(Bus1_Sink_0) --1 flow(Bus1_storage_constraint_0) -+1 flow(Source_Bus1_0) -+1 flow(storage_constraint_Bus1_0) +c_e_BusBlock_balance(Bus1_0_0)_: +-1 flow(Bus1_Sink_0_0) +-1 flow(Bus1_storage_0_0) ++1 flow(Source_Bus1_0_0) ++1 flow(storage_Bus1_0_0) = 0 -c_e_BusBlock_balance(Bus1_1)_: --1 flow(Bus1_Sink_1) --1 flow(Bus1_storage_constraint_1) -+1 flow(Source_Bus1_1) -+1 flow(storage_constraint_Bus1_1) +c_e_BusBlock_balance(Bus1_0_1)_: +-1 flow(Bus1_Sink_0_1) +-1 flow(Bus1_storage_0_1) ++1 flow(Source_Bus1_0_1) ++1 flow(storage_Bus1_0_1) = 0 -c_e_BusBlock_balance(Bus1_2)_: --1 flow(Bus1_Sink_2) --1 flow(Bus1_storage_constraint_2) -+1 flow(Source_Bus1_2) -+1 flow(storage_constraint_Bus1_2) +c_e_BusBlock_balance(Bus1_0_2)_: +-1 flow(Bus1_Sink_0_2) +-1 flow(Bus1_storage_0_2) ++1 flow(Source_Bus1_0_2) ++1 flow(storage_Bus1_0_2) = 0 -c_u_InvestmentFlowBlock_max(Bus1_Sink_0)_: --1 InvestmentFlowBlock_invest(Bus1_Sink) -+1 flow(Bus1_Sink_0) +c_e_InvestmentFlowBlock_total_rule(Bus1_Sink_0)_: +-1 InvestmentFlowBlock_invest(Bus1_Sink_0) ++1 InvestmentFlowBlock_total(Bus1_Sink_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(Bus1_storage_0)_: +-1 InvestmentFlowBlock_invest(Bus1_storage_0) ++1 InvestmentFlowBlock_total(Bus1_storage_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_0)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_0) ++1 InvestmentFlowBlock_total(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_Bus1_0)_: +-1 InvestmentFlowBlock_invest(storage_Bus1_0) ++1 InvestmentFlowBlock_total(storage_Bus1_0) += 0 + +c_u_InvestmentFlowBlock_max(Bus1_Sink_0_0)_: +-1 InvestmentFlowBlock_total(Bus1_Sink_0) ++1 flow(Bus1_Sink_0_0) <= 0 -c_u_InvestmentFlowBlock_max(Bus1_Sink_1)_: --1 InvestmentFlowBlock_invest(Bus1_Sink) -+1 flow(Bus1_Sink_1) +c_u_InvestmentFlowBlock_max(Bus1_Sink_0_1)_: +-1 InvestmentFlowBlock_total(Bus1_Sink_0) ++1 flow(Bus1_Sink_0_1) <= 0 -c_u_InvestmentFlowBlock_max(Bus1_Sink_2)_: --1 InvestmentFlowBlock_invest(Bus1_Sink) -+1 flow(Bus1_Sink_2) +c_u_InvestmentFlowBlock_max(Bus1_Sink_0_2)_: +-1 InvestmentFlowBlock_total(Bus1_Sink_0) ++1 flow(Bus1_Sink_0_2) <= 0 -c_u_InvestmentFlowBlock_max(Bus1_storage_constraint_0)_: --1 InvestmentFlowBlock_invest(Bus1_storage_constraint) -+1 flow(Bus1_storage_constraint_0) +c_u_InvestmentFlowBlock_max(Bus1_storage_0_0)_: +-1 InvestmentFlowBlock_total(Bus1_storage_0) ++1 flow(Bus1_storage_0_0) <= 0 -c_u_InvestmentFlowBlock_max(Bus1_storage_constraint_1)_: --1 InvestmentFlowBlock_invest(Bus1_storage_constraint) -+1 flow(Bus1_storage_constraint_1) +c_u_InvestmentFlowBlock_max(Bus1_storage_0_1)_: +-1 InvestmentFlowBlock_total(Bus1_storage_0) ++1 flow(Bus1_storage_0_1) <= 0 -c_u_InvestmentFlowBlock_max(Bus1_storage_constraint_2)_: --1 InvestmentFlowBlock_invest(Bus1_storage_constraint) -+1 flow(Bus1_storage_constraint_2) +c_u_InvestmentFlowBlock_max(Bus1_storage_0_2)_: +-1 InvestmentFlowBlock_total(Bus1_storage_0) ++1 flow(Bus1_storage_0_2) <= 0 -c_u_InvestmentFlowBlock_max(Source_Bus1_0)_: --1 InvestmentFlowBlock_invest(Source_Bus1) -+1 flow(Source_Bus1_0) +c_u_InvestmentFlowBlock_max(Source_Bus1_0_0)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_0) <= 0 -c_u_InvestmentFlowBlock_max(Source_Bus1_1)_: --1 InvestmentFlowBlock_invest(Source_Bus1) -+1 flow(Source_Bus1_1) +c_u_InvestmentFlowBlock_max(Source_Bus1_0_1)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_1) <= 0 -c_u_InvestmentFlowBlock_max(Source_Bus1_2)_: --1 InvestmentFlowBlock_invest(Source_Bus1) -+1 flow(Source_Bus1_2) +c_u_InvestmentFlowBlock_max(Source_Bus1_0_2)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_2) <= 0 -c_u_InvestmentFlowBlock_max(storage_constraint_Bus1_0)_: --1 InvestmentFlowBlock_invest(storage_constraint_Bus1) -+1 flow(storage_constraint_Bus1_0) +c_u_InvestmentFlowBlock_max(storage_Bus1_0_0)_: +-1 InvestmentFlowBlock_total(storage_Bus1_0) ++1 flow(storage_Bus1_0_0) <= 0 -c_u_InvestmentFlowBlock_max(storage_constraint_Bus1_1)_: --1 InvestmentFlowBlock_invest(storage_constraint_Bus1) -+1 flow(storage_constraint_Bus1_1) +c_u_InvestmentFlowBlock_max(storage_Bus1_0_1)_: +-1 InvestmentFlowBlock_total(storage_Bus1_0) ++1 flow(storage_Bus1_0_1) <= 0 -c_u_InvestmentFlowBlock_max(storage_constraint_Bus1_2)_: --1 InvestmentFlowBlock_invest(storage_constraint_Bus1) -+1 flow(storage_constraint_Bus1_2) +c_u_InvestmentFlowBlock_max(storage_Bus1_0_2)_: +-1 InvestmentFlowBlock_total(storage_Bus1_0) ++1 flow(storage_Bus1_0_2) <= 0 -c_u_GenericInvestmentStorageBlock_init_content_limit(storage_constraint)_: -+1 GenericInvestmentStorageBlock_init_content(storage_constraint) --1 GenericInvestmentStorageBlock_invest(storage_constraint) +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_0)_: +-1 GenericInvestmentStorageBlock_invest(storage_0) ++1 GenericInvestmentStorageBlock_total(storage_0) += 0 + +c_u_GenericInvestmentStorageBlock_init_content_limit(storage)_: ++1 GenericInvestmentStorageBlock_init_content(storage) +-1 GenericInvestmentStorageBlock_invest(storage_0) <= 0 -c_e_GenericInvestmentStorageBlock_balance_first(storage_constraint)_: --1 GenericInvestmentStorageBlock_init_content(storage_constraint) -+1 GenericInvestmentStorageBlock_storage_content(storage_constraint_0) --1 flow(Bus1_storage_constraint_0) -+1 flow(storage_constraint_Bus1_0) +c_e_GenericInvestmentStorageBlock_balance_first(storage)_: +-1 GenericInvestmentStorageBlock_init_content(storage) ++1 GenericInvestmentStorageBlock_storage_content(storage_0) +-1 flow(Bus1_storage_0_0) ++1 flow(storage_Bus1_0_0) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage_constraint_1)_: --1 GenericInvestmentStorageBlock_storage_content(storage_constraint_0) -+1 GenericInvestmentStorageBlock_storage_content(storage_constraint_1) --1 flow(Bus1_storage_constraint_1) -+1 flow(storage_constraint_Bus1_1) +c_e_GenericInvestmentStorageBlock_balance(storage_0_1)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_0) ++1 GenericInvestmentStorageBlock_storage_content(storage_1) +-1 flow(Bus1_storage_0_1) ++1 flow(storage_Bus1_0_1) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage_constraint_2)_: --1 GenericInvestmentStorageBlock_storage_content(storage_constraint_1) -+1 GenericInvestmentStorageBlock_storage_content(storage_constraint_2) --1 flow(Bus1_storage_constraint_2) -+1 flow(storage_constraint_Bus1_2) +c_e_GenericInvestmentStorageBlock_balance(storage_0_2)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_1) ++1 GenericInvestmentStorageBlock_storage_content(storage_2) +-1 flow(Bus1_storage_0_2) ++1 flow(storage_Bus1_0_2) = 0 -c_e_GenericInvestmentStorageBlock_balanced_cstr(storage_constraint)_: --1 GenericInvestmentStorageBlock_init_content(storage_constraint) -+1 GenericInvestmentStorageBlock_storage_content(storage_constraint_2) +c_e_GenericInvestmentStorageBlock_balanced_cstr(storage)_: +-1 GenericInvestmentStorageBlock_init_content(storage) ++1 GenericInvestmentStorageBlock_storage_content(storage_2) = 0 -c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_constraint)_: --0.20000000000000001 GenericInvestmentStorageBlock_invest(storage_constraint) -+1 InvestmentFlowBlock_invest(Bus1_storage_constraint) +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_0)_: +-0.20000000000000001 GenericInvestmentStorageBlock_total(storage_0) ++1 InvestmentFlowBlock_total(Bus1_storage_0) = 0 -c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_constraint)_: --0.20000000000000001 GenericInvestmentStorageBlock_invest(storage_constraint) -+1 InvestmentFlowBlock_invest(storage_constraint_Bus1) +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_0)_: +-0.20000000000000001 GenericInvestmentStorageBlock_total(storage_0) ++1 InvestmentFlowBlock_total(storage_Bus1_0) = 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage_constraint_0)_: --1 GenericInvestmentStorageBlock_invest(storage_constraint) -+1 GenericInvestmentStorageBlock_storage_content(storage_constraint_0) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_0_0)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_0) +-1 GenericInvestmentStorageBlock_total(storage_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage_constraint_1)_: --1 GenericInvestmentStorageBlock_invest(storage_constraint) -+1 GenericInvestmentStorageBlock_storage_content(storage_constraint_1) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_0_1)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_1) +-1 GenericInvestmentStorageBlock_total(storage_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage_constraint_2)_: --1 GenericInvestmentStorageBlock_invest(storage_constraint) -+1 GenericInvestmentStorageBlock_storage_content(storage_constraint_2) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_0_2)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_2) +-1 GenericInvestmentStorageBlock_total(storage_0) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(Bus1_Sink_0) <= +inf - 0 <= flow(Bus1_Sink_1) <= +inf - 0 <= flow(Bus1_Sink_2) <= +inf - 0 <= flow(Bus1_storage_constraint_0) <= +inf - 0 <= flow(Bus1_storage_constraint_1) <= +inf - 0 <= flow(Bus1_storage_constraint_2) <= +inf - 0 <= flow(Source_Bus1_0) <= +inf - 0 <= flow(Source_Bus1_1) <= +inf - 0 <= flow(Source_Bus1_2) <= +inf - 0 <= flow(storage_constraint_Bus1_0) <= +inf - 0 <= flow(storage_constraint_Bus1_1) <= +inf - 0 <= flow(storage_constraint_Bus1_2) <= +inf - 0 <= InvestmentFlowBlock_invest(Bus1_Sink) <= +inf - 0 <= InvestmentFlowBlock_invest(Bus1_storage_constraint) <= +inf - 0 <= InvestmentFlowBlock_invest(Source_Bus1) <= +inf - 0 <= InvestmentFlowBlock_invest(storage_constraint_Bus1) <= +inf - 0 <= GenericInvestmentStorageBlock_storage_content(storage_constraint_0) <= +inf - 0 <= GenericInvestmentStorageBlock_storage_content(storage_constraint_1) <= +inf - 0 <= GenericInvestmentStorageBlock_storage_content(storage_constraint_2) <= +inf - 0 <= GenericInvestmentStorageBlock_invest(storage_constraint) <= +inf - 0 <= GenericInvestmentStorageBlock_init_content(storage_constraint) <= +inf + 0 <= flow(Bus1_Sink_0_0) <= +inf + 0 <= flow(Bus1_Sink_0_1) <= +inf + 0 <= flow(Bus1_Sink_0_2) <= +inf + 0 <= flow(Bus1_storage_0_0) <= +inf + 0 <= flow(Bus1_storage_0_1) <= +inf + 0 <= flow(Bus1_storage_0_2) <= +inf + 0 <= flow(Source_Bus1_0_0) <= +inf + 0 <= flow(Source_Bus1_0_1) <= +inf + 0 <= flow(Source_Bus1_0_2) <= +inf + 0 <= flow(storage_Bus1_0_0) <= +inf + 0 <= flow(storage_Bus1_0_1) <= +inf + 0 <= flow(storage_Bus1_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(Bus1_Sink_0) <= +inf + 0 <= InvestmentFlowBlock_invest(Bus1_storage_0) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_total(Bus1_Sink_0) <= +inf + 0 <= InvestmentFlowBlock_total(Bus1_storage_0) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage_Bus1_0) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_0) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_1) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_2) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage_0) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage_0) <= +inf + 0 <= GenericInvestmentStorageBlock_init_content(storage) <= +inf end diff --git a/tests/lp_files/dsm_module_DIW.lp b/tests/lp_files/dsm_module_DIW.lp index 1d2a6824b..336026117 100644 --- a/tests/lp_files/dsm_module_DIW.lp +++ b/tests/lp_files/dsm_module_DIW.lp @@ -1,6 +1,6 @@ \* Source Pyomo model name=Model *\ -min +min objective: +2 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) +2 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) @@ -14,16 +14,16 @@ objective: s.t. -c_e_BusBlock_balance(bus_elec_0)_: -+1 flow(bus_elec_demand_dsm_0) +c_e_BusBlock_balance(bus_elec_0_0)_: ++1 flow(bus_elec_demand_dsm_0_0) = 0 -c_e_BusBlock_balance(bus_elec_1)_: -+1 flow(bus_elec_demand_dsm_1) +c_e_BusBlock_balance(bus_elec_0_1)_: ++1 flow(bus_elec_demand_dsm_0_1) = 0 -c_e_BusBlock_balance(bus_elec_2)_: -+1 flow(bus_elec_demand_dsm_2) +c_e_BusBlock_balance(bus_elec_0_2)_: ++1 flow(bus_elec_demand_dsm_0_2) = 0 c_e_SinkDSMDIWBlock_shift_shed_vars(demand_dsm_0)_: @@ -38,29 +38,29 @@ c_e_SinkDSMDIWBlock_shift_shed_vars(demand_dsm_2)_: +1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) = 0 -c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_0)_: +c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_0_0)_: +1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_0) +1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) +1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_0) -1 SinkDSMDIWBlock_dsm_up(demand_dsm_0) -+1 flow(bus_elec_demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_0) = 1 -c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_1)_: +c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_0_1)_: +1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) +1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) +1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_1) +1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_1) -1 SinkDSMDIWBlock_dsm_up(demand_dsm_1) -+1 flow(bus_elec_demand_dsm_1) ++1 flow(bus_elec_demand_dsm_0_1) = 1 -c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_2)_: +c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_0_2)_: +1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) +1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_2) +1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_2) -1 SinkDSMDIWBlock_dsm_up(demand_dsm_2) -+1 flow(bus_elec_demand_dsm_2) ++1 flow(bus_elec_demand_dsm_0_2) = 1 c_e_SinkDSMDIWBlock_dsm_updo_constraint(demand_dsm_0)_: @@ -135,13 +135,13 @@ c_u_SinkDSMDIWBlock_C2_constraint(demand_dsm_2)_: +1 SinkDSMDIWBlock_dsm_up(demand_dsm_2) <= 0.5 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(bus_elec_demand_dsm_0) <= +inf - 0 <= flow(bus_elec_demand_dsm_1) <= +inf - 0 <= flow(bus_elec_demand_dsm_2) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_0) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_1) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_2) <= +inf 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) <= +inf 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) <= +inf 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_2) <= +inf diff --git a/tests/lp_files/dsm_module_DLR.lp b/tests/lp_files/dsm_module_DLR.lp index ae9a63e16..3c72179e5 100644 --- a/tests/lp_files/dsm_module_DLR.lp +++ b/tests/lp_files/dsm_module_DLR.lp @@ -1,6 +1,6 @@ \* Source Pyomo model name=Model *\ -min +min objective: +2 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_0) +2 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) @@ -17,16 +17,16 @@ objective: s.t. -c_e_BusBlock_balance(bus_elec_0)_: -+1 flow(bus_elec_demand_dsm_0) +c_e_BusBlock_balance(bus_elec_0_0)_: ++1 flow(bus_elec_demand_dsm_0_0) = 0 -c_e_BusBlock_balance(bus_elec_1)_: -+1 flow(bus_elec_demand_dsm_1) +c_e_BusBlock_balance(bus_elec_0_1)_: ++1 flow(bus_elec_demand_dsm_0_1) = 0 -c_e_BusBlock_balance(bus_elec_2)_: -+1 flow(bus_elec_demand_dsm_2) +c_e_BusBlock_balance(bus_elec_0_2)_: ++1 flow(bus_elec_demand_dsm_0_2) = 0 c_e_SinkDSMDLRBlock_shift_shed_vars(demand_dsm_1_0)_: @@ -53,7 +53,7 @@ c_e_SinkDSMDLRBlock_shift_shed_vars(demand_dsm_2_2)_: +1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_2) = 0 -c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_0)_: +c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_0_0)_: -1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_0) -1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_0) +1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_0) @@ -63,10 +63,10 @@ c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_0)_: +1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) -1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) -1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) -+1 flow(bus_elec_demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_0) = 1 -c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_1)_: +c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_0_1)_: -1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_1) -1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_1) +1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) @@ -76,10 +76,10 @@ c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_1)_: +1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) -1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) -1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) -+1 flow(bus_elec_demand_dsm_1) ++1 flow(bus_elec_demand_dsm_0_1) = 1 -c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_2)_: +c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_0_2)_: -1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_2) -1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_2) +1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) @@ -89,7 +89,7 @@ c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_2)_: +1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) -1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) -1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) -+1 flow(bus_elec_demand_dsm_2) ++1 flow(bus_elec_demand_dsm_0_2) = 1 c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_1_0)_: @@ -315,13 +315,13 @@ c_u_SinkDSMDLRBlock_dr_logical_constraint(demand_dsm_2)_: +1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) <= 0.5 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(bus_elec_demand_dsm_0) <= +inf - 0 <= flow(bus_elec_demand_dsm_1) <= +inf - 0 <= flow(bus_elec_demand_dsm_2) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_0) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_1) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_2) <= +inf 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) <= +inf 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) <= +inf 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) <= +inf diff --git a/tests/lp_files/dsm_module_oemof.lp b/tests/lp_files/dsm_module_oemof.lp index 1f6375107..6b5c74041 100644 --- a/tests/lp_files/dsm_module_oemof.lp +++ b/tests/lp_files/dsm_module_oemof.lp @@ -1,6 +1,6 @@ \* Source Pyomo model name=Model *\ -min +min objective: +2 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_0) +2 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_1) @@ -8,16 +8,16 @@ objective: s.t. -c_e_BusBlock_balance(bus_elec_0)_: -+1 flow(bus_elec_demand_dsm_0) +c_e_BusBlock_balance(bus_elec_0_0)_: ++1 flow(bus_elec_demand_dsm_0_0) = 0 -c_e_BusBlock_balance(bus_elec_1)_: -+1 flow(bus_elec_demand_dsm_1) +c_e_BusBlock_balance(bus_elec_0_1)_: ++1 flow(bus_elec_demand_dsm_0_1) = 0 -c_e_BusBlock_balance(bus_elec_2)_: -+1 flow(bus_elec_demand_dsm_2) +c_e_BusBlock_balance(bus_elec_0_2)_: ++1 flow(bus_elec_demand_dsm_0_2) = 0 c_e_SinkDSMOemofBlock_shift_shed_vars(demand_dsm_0)_: @@ -32,25 +32,25 @@ c_e_SinkDSMOemofBlock_shift_shed_vars(demand_dsm_2)_: +1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_2) = 0 -c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_0)_: +c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_0_0)_: +1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_0) +1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_0) -1 SinkDSMOemofBlock_dsm_up(demand_dsm_0) -+1 flow(bus_elec_demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_0) = 1 -c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_1)_: +c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_0_1)_: +1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_1) +1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_1) -1 SinkDSMOemofBlock_dsm_up(demand_dsm_1) -+1 flow(bus_elec_demand_dsm_1) ++1 flow(bus_elec_demand_dsm_0_1) = 1 -c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_2)_: +c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_0_2)_: +1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_2) +1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_2) -1 SinkDSMOemofBlock_dsm_up(demand_dsm_2) -+1 flow(bus_elec_demand_dsm_2) ++1 flow(bus_elec_demand_dsm_0_2) = 1 c_u_SinkDSMOemofBlock_dsm_up_constraint(demand_dsm_0)_: @@ -87,13 +87,13 @@ c_e_SinkDSMOemofBlock_dsm_sum_constraint(demand_dsm_0)_: +1 SinkDSMOemofBlock_dsm_up(demand_dsm_1) = 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(bus_elec_demand_dsm_0) <= +inf - 0 <= flow(bus_elec_demand_dsm_1) <= +inf - 0 <= flow(bus_elec_demand_dsm_2) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_0) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_1) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_2) <= +inf 0 <= SinkDSMOemofBlock_dsm_do_shift(demand_dsm_0) <= +inf 0 <= SinkDSMOemofBlock_dsm_do_shift(demand_dsm_1) <= +inf 0 <= SinkDSMOemofBlock_dsm_do_shift(demand_dsm_2) <= +inf diff --git a/tests/lp_files/emission_limit.lp b/tests/lp_files/emission_limit.lp index f0d01e52a..287095269 100644 --- a/tests/lp_files/emission_limit.lp +++ b/tests/lp_files/emission_limit.lp @@ -1,49 +1,49 @@ \* Source Pyomo model name=Model *\ -min +min objective: +0 ONE_VAR_CONSTANT s.t. c_u_integral_limit_emission_factor_constraint_: -+0.5 flow(source1_electricityBus_0) --1 flow(source1_electricityBus_1) -+2 flow(source1_electricityBus_2) -+3.5 flow(source2_electricityBus_0) -+3.5 flow(source2_electricityBus_1) -+3.5 flow(source2_electricityBus_2) ++0.5 flow(source1_electricityBus_0_0) +-1 flow(source1_electricityBus_0_1) ++2 flow(source1_electricityBus_0_2) ++3.5 flow(source2_electricityBus_0_0) ++3.5 flow(source2_electricityBus_0_1) ++3.5 flow(source2_electricityBus_0_2) <= 777 -c_e_BusBlock_balance(electricityBus_0)_: -+1 flow(source1_electricityBus_0) -+1 flow(source2_electricityBus_0) -+1 flow(source3_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(source1_electricityBus_0_0) ++1 flow(source2_electricityBus_0_0) ++1 flow(source3_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: -+1 flow(source1_electricityBus_1) -+1 flow(source2_electricityBus_1) -+1 flow(source3_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(source1_electricityBus_0_1) ++1 flow(source2_electricityBus_0_1) ++1 flow(source3_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: -+1 flow(source1_electricityBus_2) -+1 flow(source2_electricityBus_2) -+1 flow(source3_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(source1_electricityBus_0_2) ++1 flow(source2_electricityBus_0_2) ++1 flow(source3_electricityBus_0_2) = 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(source1_electricityBus_0) <= 100 - 0 <= flow(source1_electricityBus_1) <= 100 - 0 <= flow(source1_electricityBus_2) <= 100 - 0 <= flow(source2_electricityBus_0) <= 100 - 0 <= flow(source2_electricityBus_1) <= 100 - 0 <= flow(source2_electricityBus_2) <= 100 - 0 <= flow(source3_electricityBus_0) <= 100 - 0 <= flow(source3_electricityBus_1) <= 100 - 0 <= flow(source3_electricityBus_2) <= 100 + 0 <= flow(source1_electricityBus_0_0) <= 100 + 0 <= flow(source1_electricityBus_0_1) <= 100 + 0 <= flow(source1_electricityBus_0_2) <= 100 + 0 <= flow(source2_electricityBus_0_0) <= 100 + 0 <= flow(source2_electricityBus_0_1) <= 100 + 0 <= flow(source2_electricityBus_0_2) <= 100 + 0 <= flow(source3_electricityBus_0_0) <= 100 + 0 <= flow(source3_electricityBus_0_1) <= 100 + 0 <= flow(source3_electricityBus_0_2) <= 100 end diff --git a/tests/lp_files/emission_limit_no_error.lp b/tests/lp_files/emission_limit_no_error.lp new file mode 100644 index 000000000..bde83919f --- /dev/null +++ b/tests/lp_files/emission_limit_no_error.lp @@ -0,0 +1,40 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++0 ONE_VAR_CONSTANT + +s.t. + +c_u_integral_limit_emission_factor_constraint_: ++0.80000000000000004 flow(source1_electricityBus_0_0) ++0.80000000000000004 flow(source1_electricityBus_0_1) ++0.80000000000000004 flow(source1_electricityBus_0_2) +<= 777 + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(source1_electricityBus_0_0) ++1 flow(source2_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(source1_electricityBus_0_1) ++1 flow(source2_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(source1_electricityBus_0_2) ++1 flow(source2_electricityBus_0_2) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(source1_electricityBus_0_0) <= 100 + 0 <= flow(source1_electricityBus_0_1) <= 100 + 0 <= flow(source1_electricityBus_0_2) <= 100 + 0 <= flow(source2_electricityBus_0_0) <= 100 + 0 <= flow(source2_electricityBus_0_1) <= 100 + 0 <= flow(source2_electricityBus_0_2) <= 100 +end diff --git a/tests/lp_files/flow_count_limit.lp b/tests/lp_files/flow_count_limit.lp index f01d7f851..dd1b996e4 100644 --- a/tests/lp_files/flow_count_limit.lp +++ b/tests/lp_files/flow_count_limit.lp @@ -1,6 +1,6 @@ \* Source Pyomo model name=Model *\ -min +min objective: +0 ONE_VAR_CONSTANT @@ -24,124 +24,124 @@ c_e_emission_factor_constraint(2)_: -1 emission_factor(2) = 0 -c_e_BusBlock_balance(electricityBus_0)_: -+1 flow(source1_electricityBus_0) -+1 flow(source2_electricityBus_0) -+1 flow(source3_electricityBus_0) -+1 flow(source4_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(source1_electricityBus_0_0) ++1 flow(source2_electricityBus_0_0) ++1 flow(source3_electricityBus_0_0) ++1 flow(source4_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: -+1 flow(source1_electricityBus_1) -+1 flow(source2_electricityBus_1) -+1 flow(source3_electricityBus_1) -+1 flow(source4_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(source1_electricityBus_0_1) ++1 flow(source2_electricityBus_0_1) ++1 flow(source3_electricityBus_0_1) ++1 flow(source4_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: -+1 flow(source1_electricityBus_2) -+1 flow(source2_electricityBus_2) -+1 flow(source3_electricityBus_2) -+1 flow(source4_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(source1_electricityBus_0_2) ++1 flow(source2_electricityBus_0_2) ++1 flow(source3_electricityBus_0_2) ++1 flow(source4_electricityBus_0_2) = 0 -c_l_NonConvexFlowBlock_min(source1_electricityBus_0)_: -+1 flow(source1_electricityBus_0) +c_l_NonConvexFlowBlock_min(source1_electricityBus_0_0)_: ++1 flow(source1_electricityBus_0_0) >= 0 -c_l_NonConvexFlowBlock_min(source1_electricityBus_1)_: -+1 flow(source1_electricityBus_1) +c_l_NonConvexFlowBlock_min(source1_electricityBus_0_1)_: ++1 flow(source1_electricityBus_0_1) >= 0 -c_l_NonConvexFlowBlock_min(source1_electricityBus_2)_: -+1 flow(source1_electricityBus_2) +c_l_NonConvexFlowBlock_min(source1_electricityBus_0_2)_: ++1 flow(source1_electricityBus_0_2) >= 0 -c_l_NonConvexFlowBlock_min(source2_electricityBus_0)_: -+1 flow(source2_electricityBus_0) +c_l_NonConvexFlowBlock_min(source2_electricityBus_0_0)_: ++1 flow(source2_electricityBus_0_0) >= 0 -c_l_NonConvexFlowBlock_min(source2_electricityBus_1)_: -+1 flow(source2_electricityBus_1) +c_l_NonConvexFlowBlock_min(source2_electricityBus_0_1)_: ++1 flow(source2_electricityBus_0_1) >= 0 -c_l_NonConvexFlowBlock_min(source2_electricityBus_2)_: -+1 flow(source2_electricityBus_2) +c_l_NonConvexFlowBlock_min(source2_electricityBus_0_2)_: ++1 flow(source2_electricityBus_0_2) >= 0 -c_l_NonConvexFlowBlock_min(source3_electricityBus_0)_: -+1 flow(source3_electricityBus_0) +c_l_NonConvexFlowBlock_min(source3_electricityBus_0_0)_: ++1 flow(source3_electricityBus_0_0) >= 0 -c_l_NonConvexFlowBlock_min(source3_electricityBus_1)_: -+1 flow(source3_electricityBus_1) +c_l_NonConvexFlowBlock_min(source3_electricityBus_0_1)_: ++1 flow(source3_electricityBus_0_1) >= 0 -c_l_NonConvexFlowBlock_min(source3_electricityBus_2)_: -+1 flow(source3_electricityBus_2) +c_l_NonConvexFlowBlock_min(source3_electricityBus_0_2)_: ++1 flow(source3_electricityBus_0_2) >= 0 -c_u_NonConvexFlowBlock_max(source1_electricityBus_0)_: +c_u_NonConvexFlowBlock_max(source1_electricityBus_0_0)_: -100 NonConvexFlowBlock_status(source1_electricityBus_0) -+1 flow(source1_electricityBus_0) ++1 flow(source1_electricityBus_0_0) <= 0 -c_u_NonConvexFlowBlock_max(source1_electricityBus_1)_: +c_u_NonConvexFlowBlock_max(source1_electricityBus_0_1)_: -100 NonConvexFlowBlock_status(source1_electricityBus_1) -+1 flow(source1_electricityBus_1) ++1 flow(source1_electricityBus_0_1) <= 0 -c_u_NonConvexFlowBlock_max(source1_electricityBus_2)_: +c_u_NonConvexFlowBlock_max(source1_electricityBus_0_2)_: -100 NonConvexFlowBlock_status(source1_electricityBus_2) -+1 flow(source1_electricityBus_2) ++1 flow(source1_electricityBus_0_2) <= 0 -c_u_NonConvexFlowBlock_max(source2_electricityBus_0)_: +c_u_NonConvexFlowBlock_max(source2_electricityBus_0_0)_: -100 NonConvexFlowBlock_status(source2_electricityBus_0) -+1 flow(source2_electricityBus_0) ++1 flow(source2_electricityBus_0_0) <= 0 -c_u_NonConvexFlowBlock_max(source2_electricityBus_1)_: +c_u_NonConvexFlowBlock_max(source2_electricityBus_0_1)_: -100 NonConvexFlowBlock_status(source2_electricityBus_1) -+1 flow(source2_electricityBus_1) ++1 flow(source2_electricityBus_0_1) <= 0 -c_u_NonConvexFlowBlock_max(source2_electricityBus_2)_: +c_u_NonConvexFlowBlock_max(source2_electricityBus_0_2)_: -100 NonConvexFlowBlock_status(source2_electricityBus_2) -+1 flow(source2_electricityBus_2) ++1 flow(source2_electricityBus_0_2) <= 0 -c_u_NonConvexFlowBlock_max(source3_electricityBus_0)_: +c_u_NonConvexFlowBlock_max(source3_electricityBus_0_0)_: -100 NonConvexFlowBlock_status(source3_electricityBus_0) -+1 flow(source3_electricityBus_0) ++1 flow(source3_electricityBus_0_0) <= 0 -c_u_NonConvexFlowBlock_max(source3_electricityBus_1)_: +c_u_NonConvexFlowBlock_max(source3_electricityBus_0_1)_: -100 NonConvexFlowBlock_status(source3_electricityBus_1) -+1 flow(source3_electricityBus_1) ++1 flow(source3_electricityBus_0_1) <= 0 -c_u_NonConvexFlowBlock_max(source3_electricityBus_2)_: +c_u_NonConvexFlowBlock_max(source3_electricityBus_0_2)_: -100 NonConvexFlowBlock_status(source3_electricityBus_2) -+1 flow(source3_electricityBus_2) ++1 flow(source3_electricityBus_0_2) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(source1_electricityBus_0) <= 100 - 0 <= flow(source1_electricityBus_1) <= 100 - 0 <= flow(source1_electricityBus_2) <= 100 - 0 <= flow(source2_electricityBus_0) <= 100 - 0 <= flow(source2_electricityBus_1) <= 100 - 0 <= flow(source2_electricityBus_2) <= 100 - 0 <= flow(source3_electricityBus_0) <= 100 - 0 <= flow(source3_electricityBus_1) <= 100 - 0 <= flow(source3_electricityBus_2) <= 100 - 30 <= flow(source4_electricityBus_0) <= 100 - 30 <= flow(source4_electricityBus_1) <= 100 - 30 <= flow(source4_electricityBus_2) <= 100 + 0 <= flow(source1_electricityBus_0_0) <= 100 + 0 <= flow(source1_electricityBus_0_1) <= 100 + 0 <= flow(source1_electricityBus_0_2) <= 100 + 0 <= flow(source2_electricityBus_0_0) <= 100 + 0 <= flow(source2_electricityBus_0_1) <= 100 + 0 <= flow(source2_electricityBus_0_2) <= 100 + 0 <= flow(source3_electricityBus_0_0) <= 100 + 0 <= flow(source3_electricityBus_0_1) <= 100 + 0 <= flow(source3_electricityBus_0_2) <= 100 + 30 <= flow(source4_electricityBus_0_0) <= 100 + 30 <= flow(source4_electricityBus_0_1) <= 100 + 30 <= flow(source4_electricityBus_0_2) <= 100 1 <= emission_factor(0) <= 2 1 <= emission_factor(1) <= 2 1 <= emission_factor(2) <= 2 diff --git a/tests/lp_files/generic_invest_limit.lp b/tests/lp_files/generic_invest_limit.lp index 184058e0f..6b59ddbe6 100644 --- a/tests/lp_files/generic_invest_limit.lp +++ b/tests/lp_files/generic_invest_limit.lp @@ -1,95 +1,113 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+50 InvestmentFlowBlock_invest(source_0_bus_1) -+100 InvestmentFlowBlock_invest(source_1_bus_1) -+75 InvestmentFlowBlock_invest(source_2_bus_1) ++50 InvestmentFlowBlock_invest(source_0_bus_1_0) ++100 InvestmentFlowBlock_invest(source_1_bus_1_0) ++75 InvestmentFlowBlock_invest(source_2_bus_1_0) s.t. -c_u_invest_limit_space_constraint_: -+4 InvestmentFlowBlock_invest(source_0_bus_1) -+1 InvestmentFlowBlock_invest(source_1_bus_1) +c_u_add_limit(0)_: ++4 InvestmentFlowBlock_invest(source_0_bus_1_0) ++1 InvestmentFlowBlock_invest(source_1_bus_1_0) <= 20 -c_e_BusBlock_balance(bus_1_0)_: -+1 flow(source_0_bus_1_0) -+1 flow(source_1_bus_1_0) -+1 flow(source_2_bus_1_0) +c_e_BusBlock_balance(bus_1_0_0)_: ++1 flow(source_0_bus_1_0_0) ++1 flow(source_1_bus_1_0_0) ++1 flow(source_2_bus_1_0_0) = 0 -c_e_BusBlock_balance(bus_1_1)_: -+1 flow(source_0_bus_1_1) -+1 flow(source_1_bus_1_1) -+1 flow(source_2_bus_1_1) +c_e_BusBlock_balance(bus_1_0_1)_: ++1 flow(source_0_bus_1_0_1) ++1 flow(source_1_bus_1_0_1) ++1 flow(source_2_bus_1_0_1) = 0 -c_e_BusBlock_balance(bus_1_2)_: -+1 flow(source_0_bus_1_2) -+1 flow(source_1_bus_1_2) -+1 flow(source_2_bus_1_2) +c_e_BusBlock_balance(bus_1_0_2)_: ++1 flow(source_0_bus_1_0_2) ++1 flow(source_1_bus_1_0_2) ++1 flow(source_2_bus_1_0_2) = 0 -c_u_InvestmentFlowBlock_max(source_0_bus_1_0)_: --1 InvestmentFlowBlock_invest(source_0_bus_1) -+1 flow(source_0_bus_1_0) +c_e_InvestmentFlowBlock_total_rule(source_0_bus_1_0)_: +-1 InvestmentFlowBlock_invest(source_0_bus_1_0) ++1 InvestmentFlowBlock_total(source_0_bus_1_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(source_1_bus_1_0)_: +-1 InvestmentFlowBlock_invest(source_1_bus_1_0) ++1 InvestmentFlowBlock_total(source_1_bus_1_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(source_2_bus_1_0)_: +-1 InvestmentFlowBlock_invest(source_2_bus_1_0) ++1 InvestmentFlowBlock_total(source_2_bus_1_0) += 0 + +c_u_InvestmentFlowBlock_max(source_0_bus_1_0_0)_: +-1 InvestmentFlowBlock_total(source_0_bus_1_0) ++1 flow(source_0_bus_1_0_0) <= 0 -c_u_InvestmentFlowBlock_max(source_0_bus_1_1)_: --1 InvestmentFlowBlock_invest(source_0_bus_1) -+1 flow(source_0_bus_1_1) +c_u_InvestmentFlowBlock_max(source_0_bus_1_0_1)_: +-1 InvestmentFlowBlock_total(source_0_bus_1_0) ++1 flow(source_0_bus_1_0_1) <= 0 -c_u_InvestmentFlowBlock_max(source_0_bus_1_2)_: --1 InvestmentFlowBlock_invest(source_0_bus_1) -+1 flow(source_0_bus_1_2) +c_u_InvestmentFlowBlock_max(source_0_bus_1_0_2)_: +-1 InvestmentFlowBlock_total(source_0_bus_1_0) ++1 flow(source_0_bus_1_0_2) <= 0 -c_u_InvestmentFlowBlock_max(source_1_bus_1_0)_: --1 InvestmentFlowBlock_invest(source_1_bus_1) -+1 flow(source_1_bus_1_0) +c_u_InvestmentFlowBlock_max(source_1_bus_1_0_0)_: +-1 InvestmentFlowBlock_total(source_1_bus_1_0) ++1 flow(source_1_bus_1_0_0) <= 0 -c_u_InvestmentFlowBlock_max(source_1_bus_1_1)_: --1 InvestmentFlowBlock_invest(source_1_bus_1) -+1 flow(source_1_bus_1_1) +c_u_InvestmentFlowBlock_max(source_1_bus_1_0_1)_: +-1 InvestmentFlowBlock_total(source_1_bus_1_0) ++1 flow(source_1_bus_1_0_1) <= 0 -c_u_InvestmentFlowBlock_max(source_1_bus_1_2)_: --1 InvestmentFlowBlock_invest(source_1_bus_1) -+1 flow(source_1_bus_1_2) +c_u_InvestmentFlowBlock_max(source_1_bus_1_0_2)_: +-1 InvestmentFlowBlock_total(source_1_bus_1_0) ++1 flow(source_1_bus_1_0_2) <= 0 -c_u_InvestmentFlowBlock_max(source_2_bus_1_0)_: --1 InvestmentFlowBlock_invest(source_2_bus_1) -+1 flow(source_2_bus_1_0) +c_u_InvestmentFlowBlock_max(source_2_bus_1_0_0)_: +-1 InvestmentFlowBlock_total(source_2_bus_1_0) ++1 flow(source_2_bus_1_0_0) <= 0 -c_u_InvestmentFlowBlock_max(source_2_bus_1_1)_: --1 InvestmentFlowBlock_invest(source_2_bus_1) -+1 flow(source_2_bus_1_1) +c_u_InvestmentFlowBlock_max(source_2_bus_1_0_1)_: +-1 InvestmentFlowBlock_total(source_2_bus_1_0) ++1 flow(source_2_bus_1_0_1) <= 0 -c_u_InvestmentFlowBlock_max(source_2_bus_1_2)_: --1 InvestmentFlowBlock_invest(source_2_bus_1) -+1 flow(source_2_bus_1_2) +c_u_InvestmentFlowBlock_max(source_2_bus_1_0_2)_: +-1 InvestmentFlowBlock_total(source_2_bus_1_0) ++1 flow(source_2_bus_1_0_2) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(source_0_bus_1_0) <= +inf - 0 <= flow(source_0_bus_1_1) <= +inf - 0 <= flow(source_0_bus_1_2) <= +inf - 0 <= flow(source_1_bus_1_0) <= +inf - 0 <= flow(source_1_bus_1_1) <= +inf - 0 <= flow(source_1_bus_1_2) <= +inf - 0 <= flow(source_2_bus_1_0) <= +inf - 0 <= flow(source_2_bus_1_1) <= +inf - 0 <= flow(source_2_bus_1_2) <= +inf - 0 <= InvestmentFlowBlock_invest(source_0_bus_1) <= +inf - 0 <= InvestmentFlowBlock_invest(source_1_bus_1) <= +inf - 0 <= InvestmentFlowBlock_invest(source_2_bus_1) <= +inf + 0 <= flow(source_0_bus_1_0_0) <= +inf + 0 <= flow(source_0_bus_1_0_1) <= +inf + 0 <= flow(source_0_bus_1_0_2) <= +inf + 0 <= flow(source_1_bus_1_0_0) <= +inf + 0 <= flow(source_1_bus_1_0_1) <= +inf + 0 <= flow(source_1_bus_1_0_2) <= +inf + 0 <= flow(source_2_bus_1_0_0) <= +inf + 0 <= flow(source_2_bus_1_0_1) <= +inf + 0 <= flow(source_2_bus_1_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(source_0_bus_1_0) <= +inf + 0 <= InvestmentFlowBlock_invest(source_1_bus_1_0) <= +inf + 0 <= InvestmentFlowBlock_invest(source_2_bus_1_0) <= +inf + 0 <= InvestmentFlowBlock_total(source_0_bus_1_0) <= +inf + 0 <= InvestmentFlowBlock_total(source_1_bus_1_0) <= +inf + 0 <= InvestmentFlowBlock_total(source_2_bus_1_0) <= +inf end diff --git a/tests/lp_files/inactivity_costs.lp b/tests/lp_files/inactivity_costs.lp index f98be67bf..ecff98a30 100644 --- a/tests/lp_files/inactivity_costs.lp +++ b/tests/lp_files/inactivity_costs.lp @@ -1,66 +1,66 @@ \* Source Pyomo model name=Model *\ -min +min objective: -2 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_0) -2 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_1) -2 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_2) -+10 flow(cheap_plant_inactivity_costs_Bus_C_0) -+10 flow(cheap_plant_inactivity_costs_Bus_C_1) -+10 flow(cheap_plant_inactivity_costs_Bus_C_2) ++10 flow(cheap_plant_inactivity_costs_Bus_C_0_0) ++10 flow(cheap_plant_inactivity_costs_Bus_C_0_1) ++10 flow(cheap_plant_inactivity_costs_Bus_C_0_2) +6 ONE_VAR_CONSTANT s.t. -c_e_BusBlock_balance(Bus_C_0)_: -+1 flow(cheap_plant_inactivity_costs_Bus_C_0) +c_e_BusBlock_balance(Bus_C_0_0)_: ++1 flow(cheap_plant_inactivity_costs_Bus_C_0_0) = 0 -c_e_BusBlock_balance(Bus_C_1)_: -+1 flow(cheap_plant_inactivity_costs_Bus_C_1) +c_e_BusBlock_balance(Bus_C_0_1)_: ++1 flow(cheap_plant_inactivity_costs_Bus_C_0_1) = 0 -c_e_BusBlock_balance(Bus_C_2)_: -+1 flow(cheap_plant_inactivity_costs_Bus_C_2) +c_e_BusBlock_balance(Bus_C_0_2)_: ++1 flow(cheap_plant_inactivity_costs_Bus_C_0_2) = 0 -c_u_NonConvexFlowBlock_min(cheap_plant_inactivity_costs_Bus_C_0)_: +c_u_NonConvexFlowBlock_min(cheap_plant_inactivity_costs_Bus_C_0_0)_: +5 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_0) --1 flow(cheap_plant_inactivity_costs_Bus_C_0) +-1 flow(cheap_plant_inactivity_costs_Bus_C_0_0) <= 0 -c_u_NonConvexFlowBlock_min(cheap_plant_inactivity_costs_Bus_C_1)_: +c_u_NonConvexFlowBlock_min(cheap_plant_inactivity_costs_Bus_C_0_1)_: +5 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_1) --1 flow(cheap_plant_inactivity_costs_Bus_C_1) +-1 flow(cheap_plant_inactivity_costs_Bus_C_0_1) <= 0 -c_u_NonConvexFlowBlock_min(cheap_plant_inactivity_costs_Bus_C_2)_: +c_u_NonConvexFlowBlock_min(cheap_plant_inactivity_costs_Bus_C_0_2)_: +5 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_2) --1 flow(cheap_plant_inactivity_costs_Bus_C_2) +-1 flow(cheap_plant_inactivity_costs_Bus_C_0_2) <= 0 -c_u_NonConvexFlowBlock_max(cheap_plant_inactivity_costs_Bus_C_0)_: +c_u_NonConvexFlowBlock_max(cheap_plant_inactivity_costs_Bus_C_0_0)_: -10 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_0) -+1 flow(cheap_plant_inactivity_costs_Bus_C_0) ++1 flow(cheap_plant_inactivity_costs_Bus_C_0_0) <= 0 -c_u_NonConvexFlowBlock_max(cheap_plant_inactivity_costs_Bus_C_1)_: +c_u_NonConvexFlowBlock_max(cheap_plant_inactivity_costs_Bus_C_0_1)_: -10 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_1) -+1 flow(cheap_plant_inactivity_costs_Bus_C_1) ++1 flow(cheap_plant_inactivity_costs_Bus_C_0_1) <= 0 -c_u_NonConvexFlowBlock_max(cheap_plant_inactivity_costs_Bus_C_2)_: +c_u_NonConvexFlowBlock_max(cheap_plant_inactivity_costs_Bus_C_0_2)_: -10 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_2) -+1 flow(cheap_plant_inactivity_costs_Bus_C_2) ++1 flow(cheap_plant_inactivity_costs_Bus_C_0_2) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(cheap_plant_inactivity_costs_Bus_C_0) <= 10 - 0 <= flow(cheap_plant_inactivity_costs_Bus_C_1) <= 10 - 0 <= flow(cheap_plant_inactivity_costs_Bus_C_2) <= 10 + 0 <= flow(cheap_plant_inactivity_costs_Bus_C_0_0) <= 10 + 0 <= flow(cheap_plant_inactivity_costs_Bus_C_0_1) <= 10 + 0 <= flow(cheap_plant_inactivity_costs_Bus_C_0_2) <= 10 0 <= NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_0) <= 1 0 <= NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_1) <= 1 0 <= NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_2) <= 1 diff --git a/tests/lp_files/investment_limit.lp b/tests/lp_files/investment_limit.lp index bcdfec4d0..dd071ce68 100644 --- a/tests/lp_files/investment_limit.lp +++ b/tests/lp_files/investment_limit.lp @@ -1,104 +1,124 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+145 GenericInvestmentStorageBlock_invest(storage_invest_limit) -+123 InvestmentFlowBlock_invest(Source_Bus1) ++145 GenericInvestmentStorageBlock_invest(storage_invest_limit_0) ++123 InvestmentFlowBlock_invest(Source_Bus1_0) s.t. c_u_investment_limit_: -+145 GenericInvestmentStorageBlock_invest(storage_invest_limit) -+123 InvestmentFlowBlock_invest(Source_Bus1) ++145 GenericInvestmentStorageBlock_invest(storage_invest_limit_0) ++123 InvestmentFlowBlock_invest(Source_Bus1_0) <= 900 -c_e_BusBlock_balance(Bus1_0)_: --1 flow(Bus1_storage_invest_limit_0) -+1 flow(Source_Bus1_0) -+1 flow(storage_invest_limit_Bus1_0) +c_e_BusBlock_balance(Bus1_0_0)_: +-1 flow(Bus1_storage_invest_limit_0_0) ++1 flow(Source_Bus1_0_0) ++1 flow(storage_invest_limit_Bus1_0_0) = 0 -c_e_BusBlock_balance(Bus1_1)_: --1 flow(Bus1_storage_invest_limit_1) -+1 flow(Source_Bus1_1) -+1 flow(storage_invest_limit_Bus1_1) +c_e_BusBlock_balance(Bus1_0_1)_: +-1 flow(Bus1_storage_invest_limit_0_1) ++1 flow(Source_Bus1_0_1) ++1 flow(storage_invest_limit_Bus1_0_1) = 0 -c_e_BusBlock_balance(Bus1_2)_: --1 flow(Bus1_storage_invest_limit_2) -+1 flow(Source_Bus1_2) -+1 flow(storage_invest_limit_Bus1_2) +c_e_BusBlock_balance(Bus1_0_2)_: +-1 flow(Bus1_storage_invest_limit_0_2) ++1 flow(Source_Bus1_0_2) ++1 flow(storage_invest_limit_Bus1_0_2) = 0 -c_u_InvestmentFlowBlock_max(Bus1_storage_invest_limit_0)_: --1 InvestmentFlowBlock_invest(Bus1_storage_invest_limit) -+1 flow(Bus1_storage_invest_limit_0) +c_e_InvestmentFlowBlock_total_rule(Bus1_storage_invest_limit_0)_: +-1 InvestmentFlowBlock_invest(Bus1_storage_invest_limit_0) ++1 InvestmentFlowBlock_total(Bus1_storage_invest_limit_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_0)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_0) ++1 InvestmentFlowBlock_total(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_invest_limit_Bus1_0)_: +-1 InvestmentFlowBlock_invest(storage_invest_limit_Bus1_0) ++1 InvestmentFlowBlock_total(storage_invest_limit_Bus1_0) += 0 + +c_u_InvestmentFlowBlock_max(Bus1_storage_invest_limit_0_0)_: +-1 InvestmentFlowBlock_total(Bus1_storage_invest_limit_0) ++1 flow(Bus1_storage_invest_limit_0_0) <= 0 -c_u_InvestmentFlowBlock_max(Bus1_storage_invest_limit_1)_: --1 InvestmentFlowBlock_invest(Bus1_storage_invest_limit) -+1 flow(Bus1_storage_invest_limit_1) +c_u_InvestmentFlowBlock_max(Bus1_storage_invest_limit_0_1)_: +-1 InvestmentFlowBlock_total(Bus1_storage_invest_limit_0) ++1 flow(Bus1_storage_invest_limit_0_1) <= 0 -c_u_InvestmentFlowBlock_max(Bus1_storage_invest_limit_2)_: --1 InvestmentFlowBlock_invest(Bus1_storage_invest_limit) -+1 flow(Bus1_storage_invest_limit_2) +c_u_InvestmentFlowBlock_max(Bus1_storage_invest_limit_0_2)_: +-1 InvestmentFlowBlock_total(Bus1_storage_invest_limit_0) ++1 flow(Bus1_storage_invest_limit_0_2) <= 0 -c_u_InvestmentFlowBlock_max(Source_Bus1_0)_: --1 InvestmentFlowBlock_invest(Source_Bus1) -+1 flow(Source_Bus1_0) +c_u_InvestmentFlowBlock_max(Source_Bus1_0_0)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_0) <= 0 -c_u_InvestmentFlowBlock_max(Source_Bus1_1)_: --1 InvestmentFlowBlock_invest(Source_Bus1) -+1 flow(Source_Bus1_1) +c_u_InvestmentFlowBlock_max(Source_Bus1_0_1)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_1) <= 0 -c_u_InvestmentFlowBlock_max(Source_Bus1_2)_: --1 InvestmentFlowBlock_invest(Source_Bus1) -+1 flow(Source_Bus1_2) +c_u_InvestmentFlowBlock_max(Source_Bus1_0_2)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_2) <= 0 -c_u_InvestmentFlowBlock_max(storage_invest_limit_Bus1_0)_: --1 InvestmentFlowBlock_invest(storage_invest_limit_Bus1) -+1 flow(storage_invest_limit_Bus1_0) +c_u_InvestmentFlowBlock_max(storage_invest_limit_Bus1_0_0)_: +-1 InvestmentFlowBlock_total(storage_invest_limit_Bus1_0) ++1 flow(storage_invest_limit_Bus1_0_0) <= 0 -c_u_InvestmentFlowBlock_max(storage_invest_limit_Bus1_1)_: --1 InvestmentFlowBlock_invest(storage_invest_limit_Bus1) -+1 flow(storage_invest_limit_Bus1_1) +c_u_InvestmentFlowBlock_max(storage_invest_limit_Bus1_0_1)_: +-1 InvestmentFlowBlock_total(storage_invest_limit_Bus1_0) ++1 flow(storage_invest_limit_Bus1_0_1) <= 0 -c_u_InvestmentFlowBlock_max(storage_invest_limit_Bus1_2)_: --1 InvestmentFlowBlock_invest(storage_invest_limit_Bus1) -+1 flow(storage_invest_limit_Bus1_2) +c_u_InvestmentFlowBlock_max(storage_invest_limit_Bus1_0_2)_: +-1 InvestmentFlowBlock_total(storage_invest_limit_Bus1_0) ++1 flow(storage_invest_limit_Bus1_0_2) <= 0 +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_invest_limit_0)_: +-1 GenericInvestmentStorageBlock_invest(storage_invest_limit_0) ++1 GenericInvestmentStorageBlock_total(storage_invest_limit_0) += 0 + c_u_GenericInvestmentStorageBlock_init_content_limit(storage_invest_limit)_: +1 GenericInvestmentStorageBlock_init_content(storage_invest_limit) --1 GenericInvestmentStorageBlock_invest(storage_invest_limit) +-1 GenericInvestmentStorageBlock_invest(storage_invest_limit_0) <= 0 c_e_GenericInvestmentStorageBlock_balance_first(storage_invest_limit)_: -1 GenericInvestmentStorageBlock_init_content(storage_invest_limit) +1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_0) --1 flow(Bus1_storage_invest_limit_0) -+1 flow(storage_invest_limit_Bus1_0) +-1 flow(Bus1_storage_invest_limit_0_0) ++1 flow(storage_invest_limit_Bus1_0_0) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage_invest_limit_1)_: +c_e_GenericInvestmentStorageBlock_balance(storage_invest_limit_0_1)_: -1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_0) +1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_1) --1 flow(Bus1_storage_invest_limit_1) -+1 flow(storage_invest_limit_Bus1_1) +-1 flow(Bus1_storage_invest_limit_0_1) ++1 flow(storage_invest_limit_Bus1_0_1) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage_invest_limit_2)_: +c_e_GenericInvestmentStorageBlock_balance(storage_invest_limit_0_2)_: -1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_1) +1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_2) --1 flow(Bus1_storage_invest_limit_2) -+1 flow(storage_invest_limit_Bus1_2) +-1 flow(Bus1_storage_invest_limit_0_2) ++1 flow(storage_invest_limit_Bus1_0_2) = 0 c_e_GenericInvestmentStorageBlock_balanced_cstr(storage_invest_limit)_: @@ -106,50 +126,54 @@ c_e_GenericInvestmentStorageBlock_balanced_cstr(storage_invest_limit)_: +1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_2) = 0 -c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_invest_limit)_: --0.20000000000000001 GenericInvestmentStorageBlock_invest(storage_invest_limit) -+1 InvestmentFlowBlock_invest(Bus1_storage_invest_limit) +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_invest_limit_0)_: +-0.20000000000000001 GenericInvestmentStorageBlock_total(storage_invest_limit_0) ++1 InvestmentFlowBlock_total(Bus1_storage_invest_limit_0) = 0 -c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_invest_limit)_: --0.20000000000000001 GenericInvestmentStorageBlock_invest(storage_invest_limit) -+1 InvestmentFlowBlock_invest(storage_invest_limit_Bus1) +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_invest_limit_0)_: +-0.20000000000000001 GenericInvestmentStorageBlock_total(storage_invest_limit_0) ++1 InvestmentFlowBlock_total(storage_invest_limit_Bus1_0) = 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage_invest_limit_0)_: --1 GenericInvestmentStorageBlock_invest(storage_invest_limit) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_invest_limit_0_0)_: +1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_0) +-1 GenericInvestmentStorageBlock_total(storage_invest_limit_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage_invest_limit_1)_: --1 GenericInvestmentStorageBlock_invest(storage_invest_limit) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_invest_limit_0_1)_: +1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_1) +-1 GenericInvestmentStorageBlock_total(storage_invest_limit_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage_invest_limit_2)_: --1 GenericInvestmentStorageBlock_invest(storage_invest_limit) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_invest_limit_0_2)_: +1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_2) +-1 GenericInvestmentStorageBlock_total(storage_invest_limit_0) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(Bus1_storage_invest_limit_0) <= +inf - 0 <= flow(Bus1_storage_invest_limit_1) <= +inf - 0 <= flow(Bus1_storage_invest_limit_2) <= +inf - 0 <= flow(Source_Bus1_0) <= +inf - 0 <= flow(Source_Bus1_1) <= +inf - 0 <= flow(Source_Bus1_2) <= +inf - 0 <= flow(storage_invest_limit_Bus1_0) <= +inf - 0 <= flow(storage_invest_limit_Bus1_1) <= +inf - 0 <= flow(storage_invest_limit_Bus1_2) <= +inf - 0 <= InvestmentFlowBlock_invest(Bus1_storage_invest_limit) <= +inf - 0 <= InvestmentFlowBlock_invest(Source_Bus1) <= +inf - 0 <= InvestmentFlowBlock_invest(storage_invest_limit_Bus1) <= +inf + 0 <= flow(Bus1_storage_invest_limit_0_0) <= +inf + 0 <= flow(Bus1_storage_invest_limit_0_1) <= +inf + 0 <= flow(Bus1_storage_invest_limit_0_2) <= +inf + 0 <= flow(Source_Bus1_0_0) <= +inf + 0 <= flow(Source_Bus1_0_1) <= +inf + 0 <= flow(Source_Bus1_0_2) <= +inf + 0 <= flow(storage_invest_limit_Bus1_0_0) <= +inf + 0 <= flow(storage_invest_limit_Bus1_0_1) <= +inf + 0 <= flow(storage_invest_limit_Bus1_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(Bus1_storage_invest_limit_0) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage_invest_limit_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_total(Bus1_storage_invest_limit_0) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage_invest_limit_Bus1_0) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage_invest_limit_0) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage_invest_limit_1) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage_invest_limit_2) <= +inf - 0 <= GenericInvestmentStorageBlock_invest(storage_invest_limit) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage_invest_limit_0) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage_invest_limit_0) <= +inf 0 <= GenericInvestmentStorageBlock_init_content(storage_invest_limit) <= +inf end diff --git a/tests/lp_files/linear_transformer_chp.lp b/tests/lp_files/linear_transformer_chp.lp index 132868fca..6b5273c61 100644 --- a/tests/lp_files/linear_transformer_chp.lp +++ b/tests/lp_files/linear_transformer_chp.lp @@ -1,90 +1,90 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+50 flow(gasBus_CHPpowerplantGas_0) -+50 flow(gasBus_CHPpowerplantGas_1) -+50 flow(gasBus_CHPpowerplantGas_2) ++50 flow(gasBus_CHPpowerplantGas_0_0) ++50 flow(gasBus_CHPpowerplantGas_0_1) ++50 flow(gasBus_CHPpowerplantGas_0_2) s.t. -c_e_BusBlock_balance(electricityBus_0)_: -+1 flow(CHPpowerplantGas_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(CHPpowerplantGas_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: -+1 flow(CHPpowerplantGas_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(CHPpowerplantGas_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: -+1 flow(CHPpowerplantGas_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(CHPpowerplantGas_electricityBus_0_2) = 0 -c_e_BusBlock_balance(gasBus_0)_: -+1 flow(gasBus_CHPpowerplantGas_0) +c_e_BusBlock_balance(gasBus_0_0)_: ++1 flow(gasBus_CHPpowerplantGas_0_0) = 0 -c_e_BusBlock_balance(gasBus_1)_: -+1 flow(gasBus_CHPpowerplantGas_1) +c_e_BusBlock_balance(gasBus_0_1)_: ++1 flow(gasBus_CHPpowerplantGas_0_1) = 0 -c_e_BusBlock_balance(gasBus_2)_: -+1 flow(gasBus_CHPpowerplantGas_2) +c_e_BusBlock_balance(gasBus_0_2)_: ++1 flow(gasBus_CHPpowerplantGas_0_2) = 0 -c_e_BusBlock_balance(heatBus_0)_: -+1 flow(CHPpowerplantGas_heatBus_0) +c_e_BusBlock_balance(heatBus_0_0)_: ++1 flow(CHPpowerplantGas_heatBus_0_0) = 0 -c_e_BusBlock_balance(heatBus_1)_: -+1 flow(CHPpowerplantGas_heatBus_1) +c_e_BusBlock_balance(heatBus_0_1)_: ++1 flow(CHPpowerplantGas_heatBus_0_1) = 0 -c_e_BusBlock_balance(heatBus_2)_: -+1 flow(CHPpowerplantGas_heatBus_2) +c_e_BusBlock_balance(heatBus_0_2)_: ++1 flow(CHPpowerplantGas_heatBus_0_2) = 0 -c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_electricityBus_0)_: --1 flow(CHPpowerplantGas_electricityBus_0) -+0.40000000000000002 flow(gasBus_CHPpowerplantGas_0) +c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_electricityBus_0_0)_: +-1 flow(CHPpowerplantGas_electricityBus_0_0) ++0.40000000000000002 flow(gasBus_CHPpowerplantGas_0_0) = 0 -c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_electricityBus_1)_: --1 flow(CHPpowerplantGas_electricityBus_1) -+0.40000000000000002 flow(gasBus_CHPpowerplantGas_1) +c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_electricityBus_0_1)_: +-1 flow(CHPpowerplantGas_electricityBus_0_1) ++0.40000000000000002 flow(gasBus_CHPpowerplantGas_0_1) = 0 -c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_electricityBus_2)_: --1 flow(CHPpowerplantGas_electricityBus_2) -+0.40000000000000002 flow(gasBus_CHPpowerplantGas_2) +c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_electricityBus_0_2)_: +-1 flow(CHPpowerplantGas_electricityBus_0_2) ++0.40000000000000002 flow(gasBus_CHPpowerplantGas_0_2) = 0 -c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_heatBus_0)_: --1 flow(CHPpowerplantGas_heatBus_0) -+0.5 flow(gasBus_CHPpowerplantGas_0) +c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_heatBus_0_0)_: +-1 flow(CHPpowerplantGas_heatBus_0_0) ++0.5 flow(gasBus_CHPpowerplantGas_0_0) = 0 -c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_heatBus_1)_: --1 flow(CHPpowerplantGas_heatBus_1) -+0.5 flow(gasBus_CHPpowerplantGas_1) +c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_heatBus_0_1)_: +-1 flow(CHPpowerplantGas_heatBus_0_1) ++0.5 flow(gasBus_CHPpowerplantGas_0_1) = 0 -c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_heatBus_2)_: --1 flow(CHPpowerplantGas_heatBus_2) -+0.5 flow(gasBus_CHPpowerplantGas_2) +c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_heatBus_0_2)_: +-1 flow(CHPpowerplantGas_heatBus_0_2) ++0.5 flow(gasBus_CHPpowerplantGas_0_2) = 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(CHPpowerplantGas_electricityBus_0) <= +inf - 0 <= flow(CHPpowerplantGas_electricityBus_1) <= +inf - 0 <= flow(CHPpowerplantGas_electricityBus_2) <= +inf - 0 <= flow(CHPpowerplantGas_heatBus_0) <= +inf - 0 <= flow(CHPpowerplantGas_heatBus_1) <= +inf - 0 <= flow(CHPpowerplantGas_heatBus_2) <= +inf - 0 <= flow(gasBus_CHPpowerplantGas_0) <= 100000000000 - 0 <= flow(gasBus_CHPpowerplantGas_1) <= 100000000000 - 0 <= flow(gasBus_CHPpowerplantGas_2) <= 100000000000 + 0 <= flow(CHPpowerplantGas_electricityBus_0_0) <= +inf + 0 <= flow(CHPpowerplantGas_electricityBus_0_1) <= +inf + 0 <= flow(CHPpowerplantGas_electricityBus_0_2) <= +inf + 0 <= flow(CHPpowerplantGas_heatBus_0_0) <= +inf + 0 <= flow(CHPpowerplantGas_heatBus_0_1) <= +inf + 0 <= flow(CHPpowerplantGas_heatBus_0_2) <= +inf + 0 <= flow(gasBus_CHPpowerplantGas_0_0) <= 100000000000 + 0 <= flow(gasBus_CHPpowerplantGas_0_1) <= 100000000000 + 0 <= flow(gasBus_CHPpowerplantGas_0_2) <= 100000000000 end diff --git a/tests/lp_files/linear_transformer_chp_invest.lp b/tests/lp_files/linear_transformer_chp_invest.lp index 57f39b400..018e930e8 100644 --- a/tests/lp_files/linear_transformer_chp_invest.lp +++ b/tests/lp_files/linear_transformer_chp_invest.lp @@ -1,107 +1,113 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+20 InvestmentFlowBlock_invest(gasBus_chp_powerplant_gas) -+50 flow(gasBus_chp_powerplant_gas_0) -+50 flow(gasBus_chp_powerplant_gas_1) -+50 flow(gasBus_chp_powerplant_gas_2) ++20 InvestmentFlowBlock_invest(gasBus_chp_powerplant_gas_0) ++50 flow(gasBus_chp_powerplant_gas_0_0) ++50 flow(gasBus_chp_powerplant_gas_0_1) ++50 flow(gasBus_chp_powerplant_gas_0_2) s.t. -c_e_BusBlock_balance(electricityBus_0)_: -+1 flow(chp_powerplant_gas_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(chp_powerplant_gas_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: -+1 flow(chp_powerplant_gas_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(chp_powerplant_gas_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: -+1 flow(chp_powerplant_gas_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(chp_powerplant_gas_electricityBus_0_2) = 0 -c_e_BusBlock_balance(gasBus_0)_: -+1 flow(gasBus_chp_powerplant_gas_0) +c_e_BusBlock_balance(gasBus_0_0)_: ++1 flow(gasBus_chp_powerplant_gas_0_0) = 0 -c_e_BusBlock_balance(gasBus_1)_: -+1 flow(gasBus_chp_powerplant_gas_1) +c_e_BusBlock_balance(gasBus_0_1)_: ++1 flow(gasBus_chp_powerplant_gas_0_1) = 0 -c_e_BusBlock_balance(gasBus_2)_: -+1 flow(gasBus_chp_powerplant_gas_2) +c_e_BusBlock_balance(gasBus_0_2)_: ++1 flow(gasBus_chp_powerplant_gas_0_2) = 0 -c_e_BusBlock_balance(heatBus_0)_: -+1 flow(chp_powerplant_gas_heatBus_0) +c_e_BusBlock_balance(heatBus_0_0)_: ++1 flow(chp_powerplant_gas_heatBus_0_0) = 0 -c_e_BusBlock_balance(heatBus_1)_: -+1 flow(chp_powerplant_gas_heatBus_1) +c_e_BusBlock_balance(heatBus_0_1)_: ++1 flow(chp_powerplant_gas_heatBus_0_1) = 0 -c_e_BusBlock_balance(heatBus_2)_: -+1 flow(chp_powerplant_gas_heatBus_2) +c_e_BusBlock_balance(heatBus_0_2)_: ++1 flow(chp_powerplant_gas_heatBus_0_2) = 0 -c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_electricityBus_0)_: --1 flow(chp_powerplant_gas_electricityBus_0) -+0.40000000000000002 flow(gasBus_chp_powerplant_gas_0) +c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_electricityBus_0_0)_: +-1 flow(chp_powerplant_gas_electricityBus_0_0) ++0.40000000000000002 flow(gasBus_chp_powerplant_gas_0_0) = 0 -c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_electricityBus_1)_: --1 flow(chp_powerplant_gas_electricityBus_1) -+0.40000000000000002 flow(gasBus_chp_powerplant_gas_1) +c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_electricityBus_0_1)_: +-1 flow(chp_powerplant_gas_electricityBus_0_1) ++0.40000000000000002 flow(gasBus_chp_powerplant_gas_0_1) = 0 -c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_electricityBus_2)_: --1 flow(chp_powerplant_gas_electricityBus_2) -+0.40000000000000002 flow(gasBus_chp_powerplant_gas_2) +c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_electricityBus_0_2)_: +-1 flow(chp_powerplant_gas_electricityBus_0_2) ++0.40000000000000002 flow(gasBus_chp_powerplant_gas_0_2) = 0 -c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_heatBus_0)_: --1 flow(chp_powerplant_gas_heatBus_0) -+0.5 flow(gasBus_chp_powerplant_gas_0) +c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_heatBus_0_0)_: +-1 flow(chp_powerplant_gas_heatBus_0_0) ++0.5 flow(gasBus_chp_powerplant_gas_0_0) = 0 -c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_heatBus_1)_: --1 flow(chp_powerplant_gas_heatBus_1) -+0.5 flow(gasBus_chp_powerplant_gas_1) +c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_heatBus_0_1)_: +-1 flow(chp_powerplant_gas_heatBus_0_1) ++0.5 flow(gasBus_chp_powerplant_gas_0_1) = 0 -c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_heatBus_2)_: --1 flow(chp_powerplant_gas_heatBus_2) -+0.5 flow(gasBus_chp_powerplant_gas_2) +c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_heatBus_0_2)_: +-1 flow(chp_powerplant_gas_heatBus_0_2) ++0.5 flow(gasBus_chp_powerplant_gas_0_2) = 0 -c_u_InvestmentFlowBlock_max(gasBus_chp_powerplant_gas_0)_: --1 InvestmentFlowBlock_invest(gasBus_chp_powerplant_gas) -+1 flow(gasBus_chp_powerplant_gas_0) +c_e_InvestmentFlowBlock_total_rule(gasBus_chp_powerplant_gas_0)_: +-1 InvestmentFlowBlock_invest(gasBus_chp_powerplant_gas_0) ++1 InvestmentFlowBlock_total(gasBus_chp_powerplant_gas_0) += 0 + +c_u_InvestmentFlowBlock_max(gasBus_chp_powerplant_gas_0_0)_: +-1 InvestmentFlowBlock_total(gasBus_chp_powerplant_gas_0) ++1 flow(gasBus_chp_powerplant_gas_0_0) <= 0 -c_u_InvestmentFlowBlock_max(gasBus_chp_powerplant_gas_1)_: --1 InvestmentFlowBlock_invest(gasBus_chp_powerplant_gas) -+1 flow(gasBus_chp_powerplant_gas_1) +c_u_InvestmentFlowBlock_max(gasBus_chp_powerplant_gas_0_1)_: +-1 InvestmentFlowBlock_total(gasBus_chp_powerplant_gas_0) ++1 flow(gasBus_chp_powerplant_gas_0_1) <= 0 -c_u_InvestmentFlowBlock_max(gasBus_chp_powerplant_gas_2)_: --1 InvestmentFlowBlock_invest(gasBus_chp_powerplant_gas) -+1 flow(gasBus_chp_powerplant_gas_2) +c_u_InvestmentFlowBlock_max(gasBus_chp_powerplant_gas_0_2)_: +-1 InvestmentFlowBlock_total(gasBus_chp_powerplant_gas_0) ++1 flow(gasBus_chp_powerplant_gas_0_2) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(chp_powerplant_gas_electricityBus_0) <= +inf - 0 <= flow(chp_powerplant_gas_electricityBus_1) <= +inf - 0 <= flow(chp_powerplant_gas_electricityBus_2) <= +inf - 0 <= flow(chp_powerplant_gas_heatBus_0) <= +inf - 0 <= flow(chp_powerplant_gas_heatBus_1) <= +inf - 0 <= flow(chp_powerplant_gas_heatBus_2) <= +inf - 0 <= flow(gasBus_chp_powerplant_gas_0) <= +inf - 0 <= flow(gasBus_chp_powerplant_gas_1) <= +inf - 0 <= flow(gasBus_chp_powerplant_gas_2) <= +inf - 0 <= InvestmentFlowBlock_invest(gasBus_chp_powerplant_gas) <= 1000 + 0 <= flow(chp_powerplant_gas_electricityBus_0_0) <= +inf + 0 <= flow(chp_powerplant_gas_electricityBus_0_1) <= +inf + 0 <= flow(chp_powerplant_gas_electricityBus_0_2) <= +inf + 0 <= flow(chp_powerplant_gas_heatBus_0_0) <= +inf + 0 <= flow(chp_powerplant_gas_heatBus_0_1) <= +inf + 0 <= flow(chp_powerplant_gas_heatBus_0_2) <= +inf + 0 <= flow(gasBus_chp_powerplant_gas_0_0) <= +inf + 0 <= flow(gasBus_chp_powerplant_gas_0_1) <= +inf + 0 <= flow(gasBus_chp_powerplant_gas_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(gasBus_chp_powerplant_gas_0) <= 1000 + 0 <= InvestmentFlowBlock_total(gasBus_chp_powerplant_gas_0) <= +inf end diff --git a/tests/lp_files/maximum_shutdowns.lp b/tests/lp_files/maximum_shutdowns.lp index 9c3200a79..1a8554fdd 100644 --- a/tests/lp_files/maximum_shutdowns.lp +++ b/tests/lp_files/maximum_shutdowns.lp @@ -1,53 +1,53 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+10 flow(cheap_plant_maximum_shutdowns_Bus_C_0) -+10 flow(cheap_plant_maximum_shutdowns_Bus_C_1) -+10 flow(cheap_plant_maximum_shutdowns_Bus_C_2) ++10 flow(cheap_plant_maximum_shutdowns_Bus_C_0_0) ++10 flow(cheap_plant_maximum_shutdowns_Bus_C_0_1) ++10 flow(cheap_plant_maximum_shutdowns_Bus_C_0_2) s.t. -c_e_BusBlock_balance(Bus_C_0)_: -+1 flow(cheap_plant_maximum_shutdowns_Bus_C_0) +c_e_BusBlock_balance(Bus_C_0_0)_: ++1 flow(cheap_plant_maximum_shutdowns_Bus_C_0_0) = 0 -c_e_BusBlock_balance(Bus_C_1)_: -+1 flow(cheap_plant_maximum_shutdowns_Bus_C_1) +c_e_BusBlock_balance(Bus_C_0_1)_: ++1 flow(cheap_plant_maximum_shutdowns_Bus_C_0_1) = 0 -c_e_BusBlock_balance(Bus_C_2)_: -+1 flow(cheap_plant_maximum_shutdowns_Bus_C_2) +c_e_BusBlock_balance(Bus_C_0_2)_: ++1 flow(cheap_plant_maximum_shutdowns_Bus_C_0_2) = 0 -c_u_NonConvexFlowBlock_min(cheap_plant_maximum_shutdowns_Bus_C_0)_: +c_u_NonConvexFlowBlock_min(cheap_plant_maximum_shutdowns_Bus_C_0_0)_: +5 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_0) --1 flow(cheap_plant_maximum_shutdowns_Bus_C_0) +-1 flow(cheap_plant_maximum_shutdowns_Bus_C_0_0) <= 0 -c_u_NonConvexFlowBlock_min(cheap_plant_maximum_shutdowns_Bus_C_1)_: +c_u_NonConvexFlowBlock_min(cheap_plant_maximum_shutdowns_Bus_C_0_1)_: +5 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_1) --1 flow(cheap_plant_maximum_shutdowns_Bus_C_1) +-1 flow(cheap_plant_maximum_shutdowns_Bus_C_0_1) <= 0 -c_u_NonConvexFlowBlock_min(cheap_plant_maximum_shutdowns_Bus_C_2)_: +c_u_NonConvexFlowBlock_min(cheap_plant_maximum_shutdowns_Bus_C_0_2)_: +5 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_2) --1 flow(cheap_plant_maximum_shutdowns_Bus_C_2) +-1 flow(cheap_plant_maximum_shutdowns_Bus_C_0_2) <= 0 -c_u_NonConvexFlowBlock_max(cheap_plant_maximum_shutdowns_Bus_C_0)_: +c_u_NonConvexFlowBlock_max(cheap_plant_maximum_shutdowns_Bus_C_0_0)_: -10 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_0) -+1 flow(cheap_plant_maximum_shutdowns_Bus_C_0) ++1 flow(cheap_plant_maximum_shutdowns_Bus_C_0_0) <= 0 -c_u_NonConvexFlowBlock_max(cheap_plant_maximum_shutdowns_Bus_C_1)_: +c_u_NonConvexFlowBlock_max(cheap_plant_maximum_shutdowns_Bus_C_0_1)_: -10 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_1) -+1 flow(cheap_plant_maximum_shutdowns_Bus_C_1) ++1 flow(cheap_plant_maximum_shutdowns_Bus_C_0_1) <= 0 -c_u_NonConvexFlowBlock_max(cheap_plant_maximum_shutdowns_Bus_C_2)_: +c_u_NonConvexFlowBlock_max(cheap_plant_maximum_shutdowns_Bus_C_0_2)_: -10 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_2) -+1 flow(cheap_plant_maximum_shutdowns_Bus_C_2) ++1 flow(cheap_plant_maximum_shutdowns_Bus_C_0_2) <= 0 c_u_NonConvexFlowBlock_shutdown_constr(cheap_plant_maximum_shutdowns_Bus_C_0)_: @@ -73,13 +73,13 @@ c_u_NonConvexFlowBlock_max_shutdown_constr(cheap_plant_maximum_shutdowns_Bus_C)_ +1 NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_2) <= 2 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(cheap_plant_maximum_shutdowns_Bus_C_0) <= 10 - 0 <= flow(cheap_plant_maximum_shutdowns_Bus_C_1) <= 10 - 0 <= flow(cheap_plant_maximum_shutdowns_Bus_C_2) <= 10 + 0 <= flow(cheap_plant_maximum_shutdowns_Bus_C_0_0) <= 10 + 0 <= flow(cheap_plant_maximum_shutdowns_Bus_C_0_1) <= 10 + 0 <= flow(cheap_plant_maximum_shutdowns_Bus_C_0_2) <= 10 0 <= NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_0) <= 1 0 <= NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_1) <= 1 0 <= NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_2) <= 1 diff --git a/tests/lp_files/maximum_startups.lp b/tests/lp_files/maximum_startups.lp index c00692540..ef5d2e9de 100644 --- a/tests/lp_files/maximum_startups.lp +++ b/tests/lp_files/maximum_startups.lp @@ -1,53 +1,53 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+10 flow(cheap_plant_maximum_startups_Bus_C_0) -+10 flow(cheap_plant_maximum_startups_Bus_C_1) -+10 flow(cheap_plant_maximum_startups_Bus_C_2) ++10 flow(cheap_plant_maximum_startups_Bus_C_0_0) ++10 flow(cheap_plant_maximum_startups_Bus_C_0_1) ++10 flow(cheap_plant_maximum_startups_Bus_C_0_2) s.t. -c_e_BusBlock_balance(Bus_C_0)_: -+1 flow(cheap_plant_maximum_startups_Bus_C_0) +c_e_BusBlock_balance(Bus_C_0_0)_: ++1 flow(cheap_plant_maximum_startups_Bus_C_0_0) = 0 -c_e_BusBlock_balance(Bus_C_1)_: -+1 flow(cheap_plant_maximum_startups_Bus_C_1) +c_e_BusBlock_balance(Bus_C_0_1)_: ++1 flow(cheap_plant_maximum_startups_Bus_C_0_1) = 0 -c_e_BusBlock_balance(Bus_C_2)_: -+1 flow(cheap_plant_maximum_startups_Bus_C_2) +c_e_BusBlock_balance(Bus_C_0_2)_: ++1 flow(cheap_plant_maximum_startups_Bus_C_0_2) = 0 -c_u_NonConvexFlowBlock_min(cheap_plant_maximum_startups_Bus_C_0)_: +c_u_NonConvexFlowBlock_min(cheap_plant_maximum_startups_Bus_C_0_0)_: +5 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_0) --1 flow(cheap_plant_maximum_startups_Bus_C_0) +-1 flow(cheap_plant_maximum_startups_Bus_C_0_0) <= 0 -c_u_NonConvexFlowBlock_min(cheap_plant_maximum_startups_Bus_C_1)_: +c_u_NonConvexFlowBlock_min(cheap_plant_maximum_startups_Bus_C_0_1)_: +5 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_1) --1 flow(cheap_plant_maximum_startups_Bus_C_1) +-1 flow(cheap_plant_maximum_startups_Bus_C_0_1) <= 0 -c_u_NonConvexFlowBlock_min(cheap_plant_maximum_startups_Bus_C_2)_: +c_u_NonConvexFlowBlock_min(cheap_plant_maximum_startups_Bus_C_0_2)_: +5 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_2) --1 flow(cheap_plant_maximum_startups_Bus_C_2) +-1 flow(cheap_plant_maximum_startups_Bus_C_0_2) <= 0 -c_u_NonConvexFlowBlock_max(cheap_plant_maximum_startups_Bus_C_0)_: +c_u_NonConvexFlowBlock_max(cheap_plant_maximum_startups_Bus_C_0_0)_: -10 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_0) -+1 flow(cheap_plant_maximum_startups_Bus_C_0) ++1 flow(cheap_plant_maximum_startups_Bus_C_0_0) <= 0 -c_u_NonConvexFlowBlock_max(cheap_plant_maximum_startups_Bus_C_1)_: +c_u_NonConvexFlowBlock_max(cheap_plant_maximum_startups_Bus_C_0_1)_: -10 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_1) -+1 flow(cheap_plant_maximum_startups_Bus_C_1) ++1 flow(cheap_plant_maximum_startups_Bus_C_0_1) <= 0 -c_u_NonConvexFlowBlock_max(cheap_plant_maximum_startups_Bus_C_2)_: +c_u_NonConvexFlowBlock_max(cheap_plant_maximum_startups_Bus_C_0_2)_: -10 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_2) -+1 flow(cheap_plant_maximum_startups_Bus_C_2) ++1 flow(cheap_plant_maximum_startups_Bus_C_0_2) <= 0 c_u_NonConvexFlowBlock_startup_constr(cheap_plant_maximum_startups_Bus_C_0)_: @@ -73,13 +73,13 @@ c_u_NonConvexFlowBlock_max_startup_constr(cheap_plant_maximum_startups_Bus_C)_: +1 NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_2) <= 2 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(cheap_plant_maximum_startups_Bus_C_0) <= 10 - 0 <= flow(cheap_plant_maximum_startups_Bus_C_1) <= 10 - 0 <= flow(cheap_plant_maximum_startups_Bus_C_2) <= 10 + 0 <= flow(cheap_plant_maximum_startups_Bus_C_0_0) <= 10 + 0 <= flow(cheap_plant_maximum_startups_Bus_C_0_1) <= 10 + 0 <= flow(cheap_plant_maximum_startups_Bus_C_0_2) <= 10 0 <= NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_0) <= 1 0 <= NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_1) <= 1 0 <= NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_2) <= 1 diff --git a/tests/lp_files/min_max_runtime.lp b/tests/lp_files/min_max_runtime.lp index 0f0d39a1f..e3baf6014 100644 --- a/tests/lp_files/min_max_runtime.lp +++ b/tests/lp_files/min_max_runtime.lp @@ -1,6 +1,6 @@ \* Source Pyomo model name=Model *\ -min +min objective: +7 NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_0) +7 NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_1) @@ -8,58 +8,58 @@ objective: +5 NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_0) +5 NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_1) +5 NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_2) -+10 flow(cheap_plant_min_down_constraints_Bus_T_0) -+10 flow(cheap_plant_min_down_constraints_Bus_T_1) -+10 flow(cheap_plant_min_down_constraints_Bus_T_2) ++10 flow(cheap_plant_min_down_constraints_Bus_T_0_0) ++10 flow(cheap_plant_min_down_constraints_Bus_T_0_1) ++10 flow(cheap_plant_min_down_constraints_Bus_T_0_2) s.t. -c_e_BusBlock_balance(Bus_T_0)_: -+1 flow(cheap_plant_min_down_constraints_Bus_T_0) +c_e_BusBlock_balance(Bus_T_0_0)_: ++1 flow(cheap_plant_min_down_constraints_Bus_T_0_0) = 0 -c_e_BusBlock_balance(Bus_T_1)_: -+1 flow(cheap_plant_min_down_constraints_Bus_T_1) +c_e_BusBlock_balance(Bus_T_0_1)_: ++1 flow(cheap_plant_min_down_constraints_Bus_T_0_1) = 0 -c_e_BusBlock_balance(Bus_T_2)_: -+1 flow(cheap_plant_min_down_constraints_Bus_T_2) +c_e_BusBlock_balance(Bus_T_0_2)_: ++1 flow(cheap_plant_min_down_constraints_Bus_T_0_2) = 0 -c_u_NonConvexFlowBlock_min(cheap_plant_min_down_constraints_Bus_T_0)_: +c_u_NonConvexFlowBlock_min(cheap_plant_min_down_constraints_Bus_T_0_0)_: +5 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_0) --1 flow(cheap_plant_min_down_constraints_Bus_T_0) +-1 flow(cheap_plant_min_down_constraints_Bus_T_0_0) <= 0 -c_u_NonConvexFlowBlock_min(cheap_plant_min_down_constraints_Bus_T_1)_: +c_u_NonConvexFlowBlock_min(cheap_plant_min_down_constraints_Bus_T_0_1)_: +5 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_1) --1 flow(cheap_plant_min_down_constraints_Bus_T_1) +-1 flow(cheap_plant_min_down_constraints_Bus_T_0_1) <= 0 -c_u_NonConvexFlowBlock_min(cheap_plant_min_down_constraints_Bus_T_2)_: +c_u_NonConvexFlowBlock_min(cheap_plant_min_down_constraints_Bus_T_0_2)_: +5 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_2) --1 flow(cheap_plant_min_down_constraints_Bus_T_2) +-1 flow(cheap_plant_min_down_constraints_Bus_T_0_2) <= 0 -c_u_NonConvexFlowBlock_max(cheap_plant_min_down_constraints_Bus_T_0)_: +c_u_NonConvexFlowBlock_max(cheap_plant_min_down_constraints_Bus_T_0_0)_: -10 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_0) -+1 flow(cheap_plant_min_down_constraints_Bus_T_0) ++1 flow(cheap_plant_min_down_constraints_Bus_T_0_0) <= 0 -c_u_NonConvexFlowBlock_max(cheap_plant_min_down_constraints_Bus_T_1)_: +c_u_NonConvexFlowBlock_max(cheap_plant_min_down_constraints_Bus_T_0_1)_: -10 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_1) -+1 flow(cheap_plant_min_down_constraints_Bus_T_1) ++1 flow(cheap_plant_min_down_constraints_Bus_T_0_1) <= 0 -c_u_NonConvexFlowBlock_max(cheap_plant_min_down_constraints_Bus_T_2)_: +c_u_NonConvexFlowBlock_max(cheap_plant_min_down_constraints_Bus_T_0_2)_: -10 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_2) -+1 flow(cheap_plant_min_down_constraints_Bus_T_2) ++1 flow(cheap_plant_min_down_constraints_Bus_T_0_2) <= 0 c_u_NonConvexFlowBlock_startup_constr(cheap_plant_min_down_constraints_Bus_T_0)_: -1 NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_0) +1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_0) -<= 2 +<= 1 c_u_NonConvexFlowBlock_startup_constr(cheap_plant_min_down_constraints_Bus_T_1)_: -1 NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_1) @@ -76,7 +76,7 @@ c_u_NonConvexFlowBlock_startup_constr(cheap_plant_min_down_constraints_Bus_T_2)_ c_u_NonConvexFlowBlock_shutdown_constr(cheap_plant_min_down_constraints_Bus_T_0)_: -1 NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_0) -1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_0) -<= -2 +<= -1 c_u_NonConvexFlowBlock_shutdown_constr(cheap_plant_min_down_constraints_Bus_T_1)_: -1 NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_1) @@ -92,35 +92,35 @@ c_u_NonConvexFlowBlock_shutdown_constr(cheap_plant_min_down_constraints_Bus_T_2) c_e_NonConvexFlowBlock_min_uptime_constr(cheap_plant_min_down_constraints_Bus_T_0)_: +1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_0) -= 2 += 1 c_e_NonConvexFlowBlock_min_uptime_constr(cheap_plant_min_down_constraints_Bus_T_1)_: +1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_1) -= 2 += 1 c_e_NonConvexFlowBlock_min_uptime_constr(cheap_plant_min_down_constraints_Bus_T_2)_: +1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_2) -= 2 += 1 c_e_NonConvexFlowBlock_min_downtime_constr(cheap_plant_min_down_constraints_Bus_T_0)_: +1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_0) -= 2 += 1 c_e_NonConvexFlowBlock_min_downtime_constr(cheap_plant_min_down_constraints_Bus_T_1)_: +1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_1) -= 2 += 1 c_e_NonConvexFlowBlock_min_downtime_constr(cheap_plant_min_down_constraints_Bus_T_2)_: +1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_2) -= 2 += 1 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(cheap_plant_min_down_constraints_Bus_T_0) <= 10 - 0 <= flow(cheap_plant_min_down_constraints_Bus_T_1) <= 10 - 0 <= flow(cheap_plant_min_down_constraints_Bus_T_2) <= 10 + 0 <= flow(cheap_plant_min_down_constraints_Bus_T_0_0) <= 10 + 0 <= flow(cheap_plant_min_down_constraints_Bus_T_0_1) <= 10 + 0 <= flow(cheap_plant_min_down_constraints_Bus_T_0_2) <= 10 0 <= NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_0) <= 1 0 <= NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_1) <= 1 0 <= NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_2) <= 1 diff --git a/tests/lp_files/offsettransformer.lp b/tests/lp_files/offsettransformer.lp index 7a8f0b14c..fe17a96ce 100644 --- a/tests/lp_files/offsettransformer.lp +++ b/tests/lp_files/offsettransformer.lp @@ -1,93 +1,93 @@ \* Source Pyomo model name=Model *\ -min +min objective: +0 ONE_VAR_CONSTANT s.t. -c_e_BusBlock_balance(gasBus_0)_: -+1 flow(gasBus_gasboiler_0) +c_e_BusBlock_balance(gasBus_0_0)_: ++1 flow(gasBus_gasboiler_0_0) = 0 -c_e_BusBlock_balance(gasBus_1)_: -+1 flow(gasBus_gasboiler_1) +c_e_BusBlock_balance(gasBus_0_1)_: ++1 flow(gasBus_gasboiler_0_1) = 0 -c_e_BusBlock_balance(gasBus_2)_: -+1 flow(gasBus_gasboiler_2) +c_e_BusBlock_balance(gasBus_0_2)_: ++1 flow(gasBus_gasboiler_0_2) = 0 -c_e_BusBlock_balance(thermalBus_0)_: -+1 flow(gasboiler_thermalBus_0) +c_e_BusBlock_balance(thermalBus_0_0)_: ++1 flow(gasboiler_thermalBus_0_0) = 0 -c_e_BusBlock_balance(thermalBus_1)_: -+1 flow(gasboiler_thermalBus_1) +c_e_BusBlock_balance(thermalBus_0_1)_: ++1 flow(gasboiler_thermalBus_0_1) = 0 -c_e_BusBlock_balance(thermalBus_2)_: -+1 flow(gasboiler_thermalBus_2) +c_e_BusBlock_balance(thermalBus_0_2)_: ++1 flow(gasboiler_thermalBus_0_2) = 0 -c_u_NonConvexFlowBlock_min(gasBus_gasboiler_0)_: +c_u_NonConvexFlowBlock_min(gasBus_gasboiler_0_0)_: +32 NonConvexFlowBlock_status(gasBus_gasboiler_0) --1 flow(gasBus_gasboiler_0) +-1 flow(gasBus_gasboiler_0_0) <= 0 -c_u_NonConvexFlowBlock_min(gasBus_gasboiler_1)_: +c_u_NonConvexFlowBlock_min(gasBus_gasboiler_0_1)_: +32 NonConvexFlowBlock_status(gasBus_gasboiler_1) --1 flow(gasBus_gasboiler_1) +-1 flow(gasBus_gasboiler_0_1) <= 0 -c_u_NonConvexFlowBlock_min(gasBus_gasboiler_2)_: +c_u_NonConvexFlowBlock_min(gasBus_gasboiler_0_2)_: +32 NonConvexFlowBlock_status(gasBus_gasboiler_2) --1 flow(gasBus_gasboiler_2) +-1 flow(gasBus_gasboiler_0_2) <= 0 -c_u_NonConvexFlowBlock_max(gasBus_gasboiler_0)_: +c_u_NonConvexFlowBlock_max(gasBus_gasboiler_0_0)_: -100 NonConvexFlowBlock_status(gasBus_gasboiler_0) -+1 flow(gasBus_gasboiler_0) ++1 flow(gasBus_gasboiler_0_0) <= 0 -c_u_NonConvexFlowBlock_max(gasBus_gasboiler_1)_: +c_u_NonConvexFlowBlock_max(gasBus_gasboiler_0_1)_: -100 NonConvexFlowBlock_status(gasBus_gasboiler_1) -+1 flow(gasBus_gasboiler_1) ++1 flow(gasBus_gasboiler_0_1) <= 0 -c_u_NonConvexFlowBlock_max(gasBus_gasboiler_2)_: +c_u_NonConvexFlowBlock_max(gasBus_gasboiler_0_2)_: -100 NonConvexFlowBlock_status(gasBus_gasboiler_2) -+1 flow(gasBus_gasboiler_2) ++1 flow(gasBus_gasboiler_0_2) <= 0 -c_e_OffsetTransformerBlock_relation(gasboiler_0)_: +c_e_OffsetTransformerBlock_relation(gasboiler_0_0)_: -17 NonConvexFlowBlock_status(gasBus_gasboiler_0) -+0.90000000000000002 flow(gasBus_gasboiler_0) --1 flow(gasboiler_thermalBus_0) ++0.90000000000000002 flow(gasBus_gasboiler_0_0) +-1 flow(gasboiler_thermalBus_0_0) = 0 -c_e_OffsetTransformerBlock_relation(gasboiler_1)_: +c_e_OffsetTransformerBlock_relation(gasboiler_0_1)_: -17 NonConvexFlowBlock_status(gasBus_gasboiler_1) -+0.90000000000000002 flow(gasBus_gasboiler_1) --1 flow(gasboiler_thermalBus_1) ++0.90000000000000002 flow(gasBus_gasboiler_0_1) +-1 flow(gasboiler_thermalBus_0_1) = 0 -c_e_OffsetTransformerBlock_relation(gasboiler_2)_: +c_e_OffsetTransformerBlock_relation(gasboiler_0_2)_: -17 NonConvexFlowBlock_status(gasBus_gasboiler_2) -+0.90000000000000002 flow(gasBus_gasboiler_2) --1 flow(gasboiler_thermalBus_2) ++0.90000000000000002 flow(gasBus_gasboiler_0_2) +-1 flow(gasboiler_thermalBus_0_2) = 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(gasBus_gasboiler_0) <= 100 - 0 <= flow(gasBus_gasboiler_1) <= 100 - 0 <= flow(gasBus_gasboiler_2) <= 100 - 0 <= flow(gasboiler_thermalBus_0) <= +inf - 0 <= flow(gasboiler_thermalBus_1) <= +inf - 0 <= flow(gasboiler_thermalBus_2) <= +inf + 0 <= flow(gasBus_gasboiler_0_0) <= 100 + 0 <= flow(gasBus_gasboiler_0_1) <= 100 + 0 <= flow(gasBus_gasboiler_0_2) <= 100 + 0 <= flow(gasboiler_thermalBus_0_0) <= +inf + 0 <= flow(gasboiler_thermalBus_0_1) <= +inf + 0 <= flow(gasboiler_thermalBus_0_2) <= +inf 0 <= NonConvexFlowBlock_status(gasBus_gasboiler_0) <= 1 0 <= NonConvexFlowBlock_status(gasBus_gasboiler_1) <= 1 0 <= NonConvexFlowBlock_status(gasBus_gasboiler_2) <= 1 diff --git a/tests/lp_files/piecewise_linear_transformer_cc.lp b/tests/lp_files/piecewise_linear_transformer_cc.lp index a780e1e23..397074875 100644 --- a/tests/lp_files/piecewise_linear_transformer_cc.lp +++ b/tests/lp_files/piecewise_linear_transformer_cc.lp @@ -1,65 +1,65 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+1 flow(gasBus_pwltf_0) -+1 flow(gasBus_pwltf_1) -+1 flow(gasBus_pwltf_2) ++1 flow(gasBus_pwltf_0_0) ++1 flow(gasBus_pwltf_0_1) ++1 flow(gasBus_pwltf_0_2) s.t. -c_e_BusBlock_balance(electricityBus_0)_: -+1 flow(pwltf_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(pwltf_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: -+1 flow(pwltf_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(pwltf_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: -+1 flow(pwltf_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(pwltf_electricityBus_0_2) = 0 -c_e_BusBlock_balance(gasBus_0)_: -+1 flow(gasBus_pwltf_0) +c_e_BusBlock_balance(gasBus_0_0)_: ++1 flow(gasBus_pwltf_0_0) = 0 -c_e_BusBlock_balance(gasBus_1)_: -+1 flow(gasBus_pwltf_1) +c_e_BusBlock_balance(gasBus_0_1)_: ++1 flow(gasBus_pwltf_0_1) = 0 -c_e_BusBlock_balance(gasBus_2)_: -+1 flow(gasBus_pwltf_2) +c_e_BusBlock_balance(gasBus_0_2)_: ++1 flow(gasBus_pwltf_0_2) = 0 -c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_0)_: +c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_0_0)_: +1 PiecewiseLinearTransformerBlock_inflow(pwltf_0) --1 flow(gasBus_pwltf_0) +-1 flow(gasBus_pwltf_0_0) = 0 -c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_1)_: +c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_0_1)_: +1 PiecewiseLinearTransformerBlock_inflow(pwltf_1) --1 flow(gasBus_pwltf_1) +-1 flow(gasBus_pwltf_0_1) = 0 -c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_2)_: +c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_0_2)_: +1 PiecewiseLinearTransformerBlock_inflow(pwltf_2) --1 flow(gasBus_pwltf_2) +-1 flow(gasBus_pwltf_0_2) = 0 -c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_0)_: +c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_0_0)_: +1 PiecewiseLinearTransformerBlock_outflow(pwltf_0) --1 flow(pwltf_electricityBus_0) +-1 flow(pwltf_electricityBus_0_0) = 0 -c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_1)_: +c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_0_1)_: +1 PiecewiseLinearTransformerBlock_outflow(pwltf_1) --1 flow(pwltf_electricityBus_1) +-1 flow(pwltf_electricityBus_0_1) = 0 -c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_2)_: +c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_0_2)_: +1 PiecewiseLinearTransformerBlock_outflow(pwltf_2) --1 flow(pwltf_electricityBus_2) +-1 flow(pwltf_electricityBus_0_2) = 0 c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_constraint1_: @@ -239,16 +239,16 @@ c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_constraint5_: +1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(4) = 1 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(gasBus_pwltf_0) <= 100 - 0 <= flow(gasBus_pwltf_1) <= 100 - 0 <= flow(gasBus_pwltf_2) <= 100 - 0 <= flow(pwltf_electricityBus_0) <= +inf - 0 <= flow(pwltf_electricityBus_1) <= +inf - 0 <= flow(pwltf_electricityBus_2) <= +inf + 0 <= flow(gasBus_pwltf_0_0) <= 100 + 0 <= flow(gasBus_pwltf_0_1) <= 100 + 0 <= flow(gasBus_pwltf_0_2) <= 100 + 0 <= flow(pwltf_electricityBus_0_0) <= +inf + 0 <= flow(pwltf_electricityBus_0_1) <= +inf + 0 <= flow(pwltf_electricityBus_0_2) <= +inf 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_0) <= 100 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_1) <= 100 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_2) <= 100 diff --git a/tests/lp_files/piecewise_linear_transformer_dcc.lp b/tests/lp_files/piecewise_linear_transformer_dcc.lp index 600d617b2..e50b7ee0d 100644 --- a/tests/lp_files/piecewise_linear_transformer_dcc.lp +++ b/tests/lp_files/piecewise_linear_transformer_dcc.lp @@ -1,65 +1,65 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+1 flow(gasBus_pwltf_0) -+1 flow(gasBus_pwltf_1) -+1 flow(gasBus_pwltf_2) ++1 flow(gasBus_pwltf_0_0) ++1 flow(gasBus_pwltf_0_1) ++1 flow(gasBus_pwltf_0_2) s.t. -c_e_BusBlock_balance(electricityBus_0)_: -+1 flow(pwltf_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(pwltf_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: -+1 flow(pwltf_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(pwltf_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: -+1 flow(pwltf_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(pwltf_electricityBus_0_2) = 0 -c_e_BusBlock_balance(gasBus_0)_: -+1 flow(gasBus_pwltf_0) +c_e_BusBlock_balance(gasBus_0_0)_: ++1 flow(gasBus_pwltf_0_0) = 0 -c_e_BusBlock_balance(gasBus_1)_: -+1 flow(gasBus_pwltf_1) +c_e_BusBlock_balance(gasBus_0_1)_: ++1 flow(gasBus_pwltf_0_1) = 0 -c_e_BusBlock_balance(gasBus_2)_: -+1 flow(gasBus_pwltf_2) +c_e_BusBlock_balance(gasBus_0_2)_: ++1 flow(gasBus_pwltf_0_2) = 0 -c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_0)_: +c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_0_0)_: +1 PiecewiseLinearTransformerBlock_inflow(pwltf_0) --1 flow(gasBus_pwltf_0) +-1 flow(gasBus_pwltf_0_0) = 0 -c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_1)_: +c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_0_1)_: +1 PiecewiseLinearTransformerBlock_inflow(pwltf_1) --1 flow(gasBus_pwltf_1) +-1 flow(gasBus_pwltf_0_1) = 0 -c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_2)_: +c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_0_2)_: +1 PiecewiseLinearTransformerBlock_inflow(pwltf_2) --1 flow(gasBus_pwltf_2) +-1 flow(gasBus_pwltf_0_2) = 0 -c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_0)_: +c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_0_0)_: +1 PiecewiseLinearTransformerBlock_outflow(pwltf_0) --1 flow(pwltf_electricityBus_0) +-1 flow(pwltf_electricityBus_0_0) = 0 -c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_1)_: +c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_0_1)_: +1 PiecewiseLinearTransformerBlock_outflow(pwltf_1) --1 flow(pwltf_electricityBus_1) +-1 flow(pwltf_electricityBus_0_1) = 0 -c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_2)_: +c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_0_2)_: +1 PiecewiseLinearTransformerBlock_outflow(pwltf_2) --1 flow(pwltf_electricityBus_2) +-1 flow(pwltf_electricityBus_0_2) = 0 c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_constraint1_: @@ -221,16 +221,16 @@ c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_constraint4_: +1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(4) = 1 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(gasBus_pwltf_0) <= 100 - 0 <= flow(gasBus_pwltf_1) <= 100 - 0 <= flow(gasBus_pwltf_2) <= 100 - 0 <= flow(pwltf_electricityBus_0) <= +inf - 0 <= flow(pwltf_electricityBus_1) <= +inf - 0 <= flow(pwltf_electricityBus_2) <= +inf + 0 <= flow(gasBus_pwltf_0_0) <= 100 + 0 <= flow(gasBus_pwltf_0_1) <= 100 + 0 <= flow(gasBus_pwltf_0_2) <= 100 + 0 <= flow(pwltf_electricityBus_0_0) <= +inf + 0 <= flow(pwltf_electricityBus_0_1) <= +inf + 0 <= flow(pwltf_electricityBus_0_2) <= +inf 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_0) <= 100 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_1) <= 100 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_2) <= 100 diff --git a/tests/lp_files/shared_limit.lp b/tests/lp_files/shared_limit.lp index 5f9de12cf..5a6320ade 100644 --- a/tests/lp_files/shared_limit.lp +++ b/tests/lp_files/shared_limit.lp @@ -1,6 +1,6 @@ \* Source Pyomo model name=Model *\ -min +min objective: +0 ONE_VAR_CONSTANT @@ -24,67 +24,67 @@ c_e_limit_storage_constraint(2)_: -1 limit_storage(2) = 0 -c_e_BusBlock_balance(bus_0)_: --1 flow(bus_storage1_0) --1 flow(bus_storage2_0) -+1 flow(storage1_bus_0) -+1 flow(storage2_bus_0) +c_e_BusBlock_balance(bus_0_0)_: +-1 flow(bus_storage1_0_0) +-1 flow(bus_storage2_0_0) ++1 flow(storage1_bus_0_0) ++1 flow(storage2_bus_0_0) = 0 -c_e_BusBlock_balance(bus_1)_: --1 flow(bus_storage1_1) --1 flow(bus_storage2_1) -+1 flow(storage1_bus_1) -+1 flow(storage2_bus_1) +c_e_BusBlock_balance(bus_0_1)_: +-1 flow(bus_storage1_0_1) +-1 flow(bus_storage2_0_1) ++1 flow(storage1_bus_0_1) ++1 flow(storage2_bus_0_1) = 0 -c_e_BusBlock_balance(bus_2)_: --1 flow(bus_storage1_2) --1 flow(bus_storage2_2) -+1 flow(storage1_bus_2) -+1 flow(storage2_bus_2) +c_e_BusBlock_balance(bus_0_2)_: +-1 flow(bus_storage1_0_2) +-1 flow(bus_storage2_0_2) ++1 flow(storage1_bus_0_2) ++1 flow(storage2_bus_0_2) = 0 c_e_GenericStorageBlock_balance_first(storage1)_: -1 GenericStorageBlock_init_content(storage1) +1 GenericStorageBlock_storage_content(storage1_0) --1 flow(bus_storage1_0) -+1 flow(storage1_bus_0) +-1 flow(bus_storage1_0_0) ++1 flow(storage1_bus_0_0) = 0 c_e_GenericStorageBlock_balance_first(storage2)_: -1 GenericStorageBlock_init_content(storage2) +1 GenericStorageBlock_storage_content(storage2_0) --1 flow(bus_storage2_0) -+1 flow(storage2_bus_0) +-1 flow(bus_storage2_0_0) ++1 flow(storage2_bus_0_0) = 0 -c_e_GenericStorageBlock_balance(storage1_1)_: +c_e_GenericStorageBlock_balance(storage1_0_1)_: -1 GenericStorageBlock_storage_content(storage1_0) +1 GenericStorageBlock_storage_content(storage1_1) --1 flow(bus_storage1_1) -+1 flow(storage1_bus_1) +-1 flow(bus_storage1_0_1) ++1 flow(storage1_bus_0_1) = 0 -c_e_GenericStorageBlock_balance(storage1_2)_: +c_e_GenericStorageBlock_balance(storage1_0_2)_: -1 GenericStorageBlock_storage_content(storage1_1) +1 GenericStorageBlock_storage_content(storage1_2) --1 flow(bus_storage1_2) -+1 flow(storage1_bus_2) +-1 flow(bus_storage1_0_2) ++1 flow(storage1_bus_0_2) = 0 -c_e_GenericStorageBlock_balance(storage2_1)_: +c_e_GenericStorageBlock_balance(storage2_0_1)_: -1 GenericStorageBlock_storage_content(storage2_0) +1 GenericStorageBlock_storage_content(storage2_1) --1 flow(bus_storage2_1) -+1 flow(storage2_bus_1) +-1 flow(bus_storage2_0_1) ++1 flow(storage2_bus_0_1) = 0 -c_e_GenericStorageBlock_balance(storage2_2)_: +c_e_GenericStorageBlock_balance(storage2_0_2)_: -1 GenericStorageBlock_storage_content(storage2_1) +1 GenericStorageBlock_storage_content(storage2_2) --1 flow(bus_storage2_2) -+1 flow(storage2_bus_2) +-1 flow(bus_storage2_0_2) ++1 flow(storage2_bus_0_2) = 0 c_e_GenericStorageBlock_balanced_cstr(storage1)_: @@ -97,22 +97,22 @@ c_e_GenericStorageBlock_balanced_cstr(storage2)_: +1 GenericStorageBlock_storage_content(storage2_2) = 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(bus_storage1_0) <= +inf - 0 <= flow(bus_storage1_1) <= +inf - 0 <= flow(bus_storage1_2) <= +inf - 0 <= flow(bus_storage2_0) <= +inf - 0 <= flow(bus_storage2_1) <= +inf - 0 <= flow(bus_storage2_2) <= +inf - 0 <= flow(storage1_bus_0) <= +inf - 0 <= flow(storage1_bus_1) <= +inf - 0 <= flow(storage1_bus_2) <= +inf - 0 <= flow(storage2_bus_0) <= +inf - 0 <= flow(storage2_bus_1) <= +inf - 0 <= flow(storage2_bus_2) <= +inf + 0 <= flow(bus_storage1_0_0) <= +inf + 0 <= flow(bus_storage1_0_1) <= +inf + 0 <= flow(bus_storage1_0_2) <= +inf + 0 <= flow(bus_storage2_0_0) <= +inf + 0 <= flow(bus_storage2_0_1) <= +inf + 0 <= flow(bus_storage2_0_2) <= +inf + 0 <= flow(storage1_bus_0_0) <= +inf + 0 <= flow(storage1_bus_0_1) <= +inf + 0 <= flow(storage1_bus_0_2) <= +inf + 0 <= flow(storage2_bus_0_0) <= +inf + 0 <= flow(storage2_bus_0_1) <= +inf + 0 <= flow(storage2_bus_0_2) <= +inf 0 <= limit_storage(0) <= 7 0 <= limit_storage(1) <= 7 0 <= limit_storage(2) <= 7 diff --git a/tests/lp_files/source_with_gradient.lp b/tests/lp_files/source_with_gradient.lp index a04c1a543..c47fec18e 100644 --- a/tests/lp_files/source_with_gradient.lp +++ b/tests/lp_files/source_with_gradient.lp @@ -1,58 +1,68 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+23 flow(powerplant_electricityBus_0) -+23 flow(powerplant_electricityBus_1) -+23 flow(powerplant_electricityBus_2) ++23 flow(powerplant_electricityBus_0_0) ++23 flow(powerplant_electricityBus_0_1) ++23 flow(powerplant_electricityBus_0_2) s.t. -c_e_BusBlock_balance(electricityBus_0)_: -+1 flow(powerplant_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(powerplant_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: -+1 flow(powerplant_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(powerplant_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: -+1 flow(powerplant_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(powerplant_electricityBus_0_2) = 0 -c_u_FlowBlock_positive_gradient_constr(powerplant_electricityBus_1)_: +c_e_FlowBlock_positive_gradient_constr(powerplant_electricityBus_0_0)_: ++1 FlowBlock_positive_gradient(powerplant_electricityBus_0) += 0 + +c_u_FlowBlock_positive_gradient_constr(powerplant_electricityBus_0_1)_: -1 FlowBlock_positive_gradient(powerplant_electricityBus_1) --1 flow(powerplant_electricityBus_0) -+1 flow(powerplant_electricityBus_1) +-1 flow(powerplant_electricityBus_0_0) ++1 flow(powerplant_electricityBus_0_1) <= 0 -c_u_FlowBlock_positive_gradient_constr(powerplant_electricityBus_2)_: +c_u_FlowBlock_positive_gradient_constr(powerplant_electricityBus_0_2)_: -1 FlowBlock_positive_gradient(powerplant_electricityBus_2) --1 flow(powerplant_electricityBus_1) -+1 flow(powerplant_electricityBus_2) +-1 flow(powerplant_electricityBus_0_1) ++1 flow(powerplant_electricityBus_0_2) <= 0 -c_u_FlowBlock_negative_gradient_constr(powerplant_electricityBus_1)_: +c_e_FlowBlock_negative_gradient_constr(powerplant_electricityBus_0_0)_: ++1 FlowBlock_negative_gradient(powerplant_electricityBus_0) += 0 + +c_u_FlowBlock_negative_gradient_constr(powerplant_electricityBus_0_1)_: -1 FlowBlock_negative_gradient(powerplant_electricityBus_1) -+1 flow(powerplant_electricityBus_0) --1 flow(powerplant_electricityBus_1) ++1 flow(powerplant_electricityBus_0_0) +-1 flow(powerplant_electricityBus_0_1) <= 0 -c_u_FlowBlock_negative_gradient_constr(powerplant_electricityBus_2)_: +c_u_FlowBlock_negative_gradient_constr(powerplant_electricityBus_0_2)_: -1 FlowBlock_negative_gradient(powerplant_electricityBus_2) -+1 flow(powerplant_electricityBus_1) --1 flow(powerplant_electricityBus_2) ++1 flow(powerplant_electricityBus_0_1) +-1 flow(powerplant_electricityBus_0_2) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(powerplant_electricityBus_0) <= 999 - 0 <= flow(powerplant_electricityBus_1) <= 999 - 0 <= flow(powerplant_electricityBus_2) <= 999 - -inf <= FlowBlock_positive_gradient(powerplant_electricityBus_1) <= 29.969999999999999 - -inf <= FlowBlock_positive_gradient(powerplant_electricityBus_2) <= 29.969999999999999 - -inf <= FlowBlock_negative_gradient(powerplant_electricityBus_1) <= 49.950000000000003 - -inf <= FlowBlock_negative_gradient(powerplant_electricityBus_2) <= 49.950000000000003 + 0 <= flow(powerplant_electricityBus_0_0) <= 999 + 0 <= flow(powerplant_electricityBus_0_1) <= 999 + 0 <= flow(powerplant_electricityBus_0_2) <= 999 + 0 <= FlowBlock_positive_gradient(powerplant_electricityBus_0) <= 29.969999999999999 + 0 <= FlowBlock_positive_gradient(powerplant_electricityBus_1) <= 29.969999999999999 + 0 <= FlowBlock_positive_gradient(powerplant_electricityBus_2) <= 29.969999999999999 + 0 <= FlowBlock_negative_gradient(powerplant_electricityBus_0) <= 49.950000000000003 + 0 <= FlowBlock_negative_gradient(powerplant_electricityBus_1) <= 49.950000000000003 + 0 <= FlowBlock_negative_gradient(powerplant_electricityBus_2) <= 49.950000000000003 end diff --git a/tests/lp_files/source_with_nonconvex_gradient.lp b/tests/lp_files/source_with_nonconvex_gradient.lp index 492de2a40..b3276c85c 100644 --- a/tests/lp_files/source_with_nonconvex_gradient.lp +++ b/tests/lp_files/source_with_nonconvex_gradient.lp @@ -2,80 +2,88 @@ min objective: -+23 flow(powerplant_electricityBus_0) -+23 flow(powerplant_electricityBus_1) -+23 flow(powerplant_electricityBus_2) ++23 flow(powerplant_electricityBus_0_0) ++23 flow(powerplant_electricityBus_0_1) ++23 flow(powerplant_electricityBus_0_2) s.t. -c_e_BusBlock_balance(electricityBus_0)_: -+1 flow(powerplant_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(powerplant_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: -+1 flow(powerplant_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(powerplant_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: -+1 flow(powerplant_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(powerplant_electricityBus_0_2) = 0 -c_l_NonConvexFlowBlock_min(powerplant_electricityBus_0)_: -+1 flow(powerplant_electricityBus_0) +c_l_NonConvexFlowBlock_min(powerplant_electricityBus_0_0)_: ++1 flow(powerplant_electricityBus_0_0) >= 0 -c_l_NonConvexFlowBlock_min(powerplant_electricityBus_1)_: -+1 flow(powerplant_electricityBus_1) +c_l_NonConvexFlowBlock_min(powerplant_electricityBus_0_1)_: ++1 flow(powerplant_electricityBus_0_1) >= 0 -c_l_NonConvexFlowBlock_min(powerplant_electricityBus_2)_: -+1 flow(powerplant_electricityBus_2) +c_l_NonConvexFlowBlock_min(powerplant_electricityBus_0_2)_: ++1 flow(powerplant_electricityBus_0_2) >= 0 -c_u_NonConvexFlowBlock_max(powerplant_electricityBus_0)_: +c_u_NonConvexFlowBlock_max(powerplant_electricityBus_0_0)_: -999 NonConvexFlowBlock_status(powerplant_electricityBus_0) -+1 flow(powerplant_electricityBus_0) ++1 flow(powerplant_electricityBus_0_0) <= 0 -c_u_NonConvexFlowBlock_max(powerplant_electricityBus_1)_: +c_u_NonConvexFlowBlock_max(powerplant_electricityBus_0_1)_: -999 NonConvexFlowBlock_status(powerplant_electricityBus_1) -+1 flow(powerplant_electricityBus_1) ++1 flow(powerplant_electricityBus_0_1) <= 0 -c_u_NonConvexFlowBlock_max(powerplant_electricityBus_2)_: +c_u_NonConvexFlowBlock_max(powerplant_electricityBus_0_2)_: -999 NonConvexFlowBlock_status(powerplant_electricityBus_2) -+1 flow(powerplant_electricityBus_2) ++1 flow(powerplant_electricityBus_0_2) <= 0 -c_u_NonConvexFlowBlock_positive_gradient_constr(powerplant_electricityBus_1)_: +c_e_NonConvexFlowBlock_positive_gradient_constr(powerplant_electricityBus_0_0)_: ++1 NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_0) += 0 + +c_u_NonConvexFlowBlock_positive_gradient_constr(powerplant_electricityBus_0_1)_: -1 NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_1) + [ --1 NonConvexFlowBlock_status(powerplant_electricityBus_0) * flow(powerplant_electricityBus_0) -+1 NonConvexFlowBlock_status(powerplant_electricityBus_1) * flow(powerplant_electricityBus_1) +-1 NonConvexFlowBlock_status(powerplant_electricityBus_0) * flow(powerplant_electricityBus_0_0) ++1 NonConvexFlowBlock_status(powerplant_electricityBus_1) * flow(powerplant_electricityBus_0_1) ] <= 0 -c_u_NonConvexFlowBlock_positive_gradient_constr(powerplant_electricityBus_2)_: +c_u_NonConvexFlowBlock_positive_gradient_constr(powerplant_electricityBus_0_2)_: -1 NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_2) + [ --1 NonConvexFlowBlock_status(powerplant_electricityBus_1) * flow(powerplant_electricityBus_1) -+1 NonConvexFlowBlock_status(powerplant_electricityBus_2) * flow(powerplant_electricityBus_2) +-1 NonConvexFlowBlock_status(powerplant_electricityBus_1) * flow(powerplant_electricityBus_0_1) ++1 NonConvexFlowBlock_status(powerplant_electricityBus_2) * flow(powerplant_electricityBus_0_2) ] <= 0 -c_u_NonConvexFlowBlock_negative_gradient_constr(powerplant_electricityBus_1)_: +c_e_NonConvexFlowBlock_negative_gradient_constr(powerplant_electricityBus_0_0)_: ++1 NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_0) += 0 + +c_u_NonConvexFlowBlock_negative_gradient_constr(powerplant_electricityBus_0_1)_: -1 NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_1) + [ -+1 NonConvexFlowBlock_status(powerplant_electricityBus_0) * flow(powerplant_electricityBus_0) --1 NonConvexFlowBlock_status(powerplant_electricityBus_1) * flow(powerplant_electricityBus_1) ++1 NonConvexFlowBlock_status(powerplant_electricityBus_0) * flow(powerplant_electricityBus_0_0) +-1 NonConvexFlowBlock_status(powerplant_electricityBus_1) * flow(powerplant_electricityBus_0_1) ] <= 0 -c_u_NonConvexFlowBlock_negative_gradient_constr(powerplant_electricityBus_2)_: +c_u_NonConvexFlowBlock_negative_gradient_constr(powerplant_electricityBus_0_2)_: -1 NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_2) + [ -+1 NonConvexFlowBlock_status(powerplant_electricityBus_1) * flow(powerplant_electricityBus_1) --1 NonConvexFlowBlock_status(powerplant_electricityBus_2) * flow(powerplant_electricityBus_2) ++1 NonConvexFlowBlock_status(powerplant_electricityBus_1) * flow(powerplant_electricityBus_0_1) +-1 NonConvexFlowBlock_status(powerplant_electricityBus_2) * flow(powerplant_electricityBus_0_2) ] <= 0 @@ -83,16 +91,18 @@ c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(powerplant_electricityBus_0) <= 999 - 0 <= flow(powerplant_electricityBus_1) <= 999 - 0 <= flow(powerplant_electricityBus_2) <= 999 + 0 <= flow(powerplant_electricityBus_0_0) <= 999 + 0 <= flow(powerplant_electricityBus_0_1) <= 999 + 0 <= flow(powerplant_electricityBus_0_2) <= 999 0 <= NonConvexFlowBlock_status(powerplant_electricityBus_0) <= 1 0 <= NonConvexFlowBlock_status(powerplant_electricityBus_1) <= 1 0 <= NonConvexFlowBlock_status(powerplant_electricityBus_2) <= 1 - -inf <= NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_1) <= +inf - -inf <= NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_2) <= +inf - -inf <= NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_1) <= +inf - -inf <= NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_2) <= +inf + 0 <= NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_0) <= 29.969999999999999 + 0 <= NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_1) <= 29.969999999999999 + 0 <= NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_2) <= 29.969999999999999 + 0 <= NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_0) <= 49.950000000000003 + 0 <= NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_1) <= 49.950000000000003 + 0 <= NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_2) <= 49.950000000000003 binary NonConvexFlowBlock_status(powerplant_electricityBus_0) NonConvexFlowBlock_status(powerplant_electricityBus_1) diff --git a/tests/lp_files/storage_fixed_losses.lp b/tests/lp_files/storage_fixed_losses.lp index 324b01255..47750e891 100644 --- a/tests/lp_files/storage_fixed_losses.lp +++ b/tests/lp_files/storage_fixed_losses.lp @@ -1,65 +1,65 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+56 flow(electricityBus_storage_no_invest_0) -+56 flow(electricityBus_storage_no_invest_1) -+56 flow(electricityBus_storage_no_invest_2) -+24 flow(storage_no_invest_electricityBus_0) -+24 flow(storage_no_invest_electricityBus_1) -+24 flow(storage_no_invest_electricityBus_2) ++56 flow(electricityBus_storage_no_invest_0_0) ++56 flow(electricityBus_storage_no_invest_0_1) ++56 flow(electricityBus_storage_no_invest_0_2) ++24 flow(storage_no_invest_electricityBus_0_0) ++24 flow(storage_no_invest_electricityBus_0_1) ++24 flow(storage_no_invest_electricityBus_0_2) s.t. -c_e_BusBlock_balance(electricityBus_0)_: --1 flow(electricityBus_storage_no_invest_0) -+1 flow(storage_no_invest_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage_no_invest_0_0) ++1 flow(storage_no_invest_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: --1 flow(electricityBus_storage_no_invest_1) -+1 flow(storage_no_invest_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage_no_invest_0_1) ++1 flow(storage_no_invest_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: --1 flow(electricityBus_storage_no_invest_2) -+1 flow(storage_no_invest_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: +-1 flow(electricityBus_storage_no_invest_0_2) ++1 flow(storage_no_invest_electricityBus_0_2) = 0 c_e_GenericStorageBlock_balance_first(storage_no_invest)_: +1 GenericStorageBlock_storage_content(storage_no_invest_0) --0.96999999999999997 flow(electricityBus_storage_no_invest_0) -+1.1627906976744187 flow(storage_no_invest_electricityBus_0) +-0.96999999999999997 flow(electricityBus_storage_no_invest_0_0) ++1.1627906976744187 flow(storage_no_invest_electricityBus_0_0) = 33797 -c_e_GenericStorageBlock_balance(storage_no_invest_1)_: +c_e_GenericStorageBlock_balance(storage_no_invest_0_1)_: -0.87 GenericStorageBlock_storage_content(storage_no_invest_0) +1 GenericStorageBlock_storage_content(storage_no_invest_1) --0.96999999999999997 flow(electricityBus_storage_no_invest_1) -+1.1627906976744187 flow(storage_no_invest_electricityBus_1) +-0.96999999999999997 flow(electricityBus_storage_no_invest_0_1) ++1.1627906976744187 flow(storage_no_invest_electricityBus_0_1) = -1003 -c_e_GenericStorageBlock_balance(storage_no_invest_2)_: +c_e_GenericStorageBlock_balance(storage_no_invest_0_2)_: -0.87 GenericStorageBlock_storage_content(storage_no_invest_1) +1 GenericStorageBlock_storage_content(storage_no_invest_2) --0.96999999999999997 flow(electricityBus_storage_no_invest_2) -+1.1627906976744187 flow(storage_no_invest_electricityBus_2) +-0.96999999999999997 flow(electricityBus_storage_no_invest_0_2) ++1.1627906976744187 flow(storage_no_invest_electricityBus_0_2) = -1003 c_e_GenericStorageBlock_balanced_cstr(storage_no_invest)_: +1 GenericStorageBlock_storage_content(storage_no_invest_2) = 40000 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(electricityBus_storage_no_invest_0) <= 16667 - 0 <= flow(electricityBus_storage_no_invest_1) <= 16667 - 0 <= flow(electricityBus_storage_no_invest_2) <= 16667 - 0 <= flow(storage_no_invest_electricityBus_0) <= 16667 - 0 <= flow(storage_no_invest_electricityBus_1) <= 16667 - 0 <= flow(storage_no_invest_electricityBus_2) <= 16667 + 0 <= flow(electricityBus_storage_no_invest_0_0) <= 16667 + 0 <= flow(electricityBus_storage_no_invest_0_1) <= 16667 + 0 <= flow(electricityBus_storage_no_invest_0_2) <= 16667 + 0 <= flow(storage_no_invest_electricityBus_0_0) <= 16667 + 0 <= flow(storage_no_invest_electricityBus_0_1) <= 16667 + 0 <= flow(storage_no_invest_electricityBus_0_2) <= 16667 0 <= GenericStorageBlock_storage_content(storage_no_invest_0) <= 100000 0 <= GenericStorageBlock_storage_content(storage_no_invest_1) <= 100000 0 <= GenericStorageBlock_storage_content(storage_no_invest_2) <= 100000 diff --git a/tests/lp_files/storage_invest_1_fixed_losses.lp b/tests/lp_files/storage_invest_1_fixed_losses.lp index b5a47e1d7..29186bd49 100644 --- a/tests/lp_files/storage_invest_1_fixed_losses.lp +++ b/tests/lp_files/storage_invest_1_fixed_losses.lp @@ -1,89 +1,104 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+145 GenericInvestmentStorageBlock_invest(storage1) -+56 flow(electricityBus_storage1_0) -+56 flow(electricityBus_storage1_1) -+56 flow(electricityBus_storage1_2) -+24 flow(storage1_electricityBus_0) -+24 flow(storage1_electricityBus_1) -+24 flow(storage1_electricityBus_2) ++145 GenericInvestmentStorageBlock_invest(storage1_0) ++56 flow(electricityBus_storage1_0_0) ++56 flow(electricityBus_storage1_0_1) ++56 flow(electricityBus_storage1_0_2) ++24 flow(storage1_electricityBus_0_0) ++24 flow(storage1_electricityBus_0_1) ++24 flow(storage1_electricityBus_0_2) s.t. -c_e_BusBlock_balance(electricityBus_0)_: --1 flow(electricityBus_storage1_0) -+1 flow(storage1_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage1_0_0) ++1 flow(storage1_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: --1 flow(electricityBus_storage1_1) -+1 flow(storage1_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage1_0_1) ++1 flow(storage1_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: --1 flow(electricityBus_storage1_2) -+1 flow(storage1_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: +-1 flow(electricityBus_storage1_0_2) ++1 flow(storage1_electricityBus_0_2) = 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage1_0)_: --1 InvestmentFlowBlock_invest(electricityBus_storage1) -+1 flow(electricityBus_storage1_0) +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage1_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage1_0) ++1 InvestmentFlowBlock_total(electricityBus_storage1_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage1_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(storage1_electricityBus_0) ++1 InvestmentFlowBlock_total(storage1_electricityBus_0) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage1_0_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage1_0) ++1 flow(electricityBus_storage1_0_0) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage1_1)_: --1 InvestmentFlowBlock_invest(electricityBus_storage1) -+1 flow(electricityBus_storage1_1) +c_u_InvestmentFlowBlock_max(electricityBus_storage1_0_1)_: +-1 InvestmentFlowBlock_total(electricityBus_storage1_0) ++1 flow(electricityBus_storage1_0_1) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage1_2)_: --1 InvestmentFlowBlock_invest(electricityBus_storage1) -+1 flow(electricityBus_storage1_2) +c_u_InvestmentFlowBlock_max(electricityBus_storage1_0_2)_: +-1 InvestmentFlowBlock_total(electricityBus_storage1_0) ++1 flow(electricityBus_storage1_0_2) <= 0 -c_u_InvestmentFlowBlock_max(storage1_electricityBus_0)_: --1 InvestmentFlowBlock_invest(storage1_electricityBus) -+1 flow(storage1_electricityBus_0) +c_u_InvestmentFlowBlock_max(storage1_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(storage1_electricityBus_0) ++1 flow(storage1_electricityBus_0_0) <= 0 -c_u_InvestmentFlowBlock_max(storage1_electricityBus_1)_: --1 InvestmentFlowBlock_invest(storage1_electricityBus) -+1 flow(storage1_electricityBus_1) +c_u_InvestmentFlowBlock_max(storage1_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(storage1_electricityBus_0) ++1 flow(storage1_electricityBus_0_1) <= 0 -c_u_InvestmentFlowBlock_max(storage1_electricityBus_2)_: --1 InvestmentFlowBlock_invest(storage1_electricityBus) -+1 flow(storage1_electricityBus_2) +c_u_InvestmentFlowBlock_max(storage1_electricityBus_0_2)_: +-1 InvestmentFlowBlock_total(storage1_electricityBus_0) ++1 flow(storage1_electricityBus_0_2) <= 0 +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage1_0)_: +-1 GenericInvestmentStorageBlock_invest(storage1_0) ++1 GenericInvestmentStorageBlock_total(storage1_0) += 0 + c_u_GenericInvestmentStorageBlock_init_content_limit(storage1)_: +1 GenericInvestmentStorageBlock_init_content(storage1) --1 GenericInvestmentStorageBlock_invest(storage1) +-1 GenericInvestmentStorageBlock_invest(storage1_0) <= 0 c_e_GenericInvestmentStorageBlock_balance_first(storage1)_: -0.87 GenericInvestmentStorageBlock_init_content(storage1) -+0.01 GenericInvestmentStorageBlock_invest(storage1) ++0.01 GenericInvestmentStorageBlock_invest(storage1_0) +1 GenericInvestmentStorageBlock_storage_content(storage1_0) --0.96999999999999997 flow(electricityBus_storage1_0) -+1.1627906976744187 flow(storage1_electricityBus_0) +-0.96999999999999997 flow(electricityBus_storage1_0_0) ++1.1627906976744187 flow(storage1_electricityBus_0_0) = -3 -c_e_GenericInvestmentStorageBlock_balance(storage1_1)_: -+0.01 GenericInvestmentStorageBlock_invest(storage1) +c_e_GenericInvestmentStorageBlock_balance(storage1_0_1)_: -0.87 GenericInvestmentStorageBlock_storage_content(storage1_0) +1 GenericInvestmentStorageBlock_storage_content(storage1_1) --0.96999999999999997 flow(electricityBus_storage1_1) -+1.1627906976744187 flow(storage1_electricityBus_1) ++0.01 GenericInvestmentStorageBlock_total(storage1_0) +-0.96999999999999997 flow(electricityBus_storage1_0_1) ++1.1627906976744187 flow(storage1_electricityBus_0_1) = -3 -c_e_GenericInvestmentStorageBlock_balance(storage1_2)_: -+0.01 GenericInvestmentStorageBlock_invest(storage1) +c_e_GenericInvestmentStorageBlock_balance(storage1_0_2)_: -0.87 GenericInvestmentStorageBlock_storage_content(storage1_1) +1 GenericInvestmentStorageBlock_storage_content(storage1_2) --0.96999999999999997 flow(electricityBus_storage1_2) -+1.1627906976744187 flow(storage1_electricityBus_2) ++0.01 GenericInvestmentStorageBlock_total(storage1_0) +-0.96999999999999997 flow(electricityBus_storage1_0_2) ++1.1627906976744187 flow(storage1_electricityBus_0_2) = -3 c_e_GenericInvestmentStorageBlock_balanced_cstr(storage1)_: @@ -91,61 +106,64 @@ c_e_GenericInvestmentStorageBlock_balanced_cstr(storage1)_: +1 GenericInvestmentStorageBlock_storage_content(storage1_2) = 0 -c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage1)_: --0.16666666666666666 GenericInvestmentStorageBlock_invest(storage1) -+1 InvestmentFlowBlock_invest(electricityBus_storage1) +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage1_0)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage1_0) ++1 InvestmentFlowBlock_total(electricityBus_storage1_0) = 0 -c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage1)_: --0.16666666666666666 GenericInvestmentStorageBlock_invest(storage1) -+1 InvestmentFlowBlock_invest(storage1_electricityBus) +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage1_0)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage1_0) ++1 InvestmentFlowBlock_total(storage1_electricityBus_0) = 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0)_: --0.90000000000000002 GenericInvestmentStorageBlock_invest(storage1) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0_0)_: +1 GenericInvestmentStorageBlock_storage_content(storage1_0) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage1_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_1)_: --0.90000000000000002 GenericInvestmentStorageBlock_invest(storage1) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0_1)_: +1 GenericInvestmentStorageBlock_storage_content(storage1_1) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage1_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_2)_: --0.90000000000000002 GenericInvestmentStorageBlock_invest(storage1) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0_2)_: +1 GenericInvestmentStorageBlock_storage_content(storage1_2) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage1_0) <= 0 -c_u_GenericInvestmentStorageBlock_min_storage_content(storage1_0)_: -+0.10000000000000001 GenericInvestmentStorageBlock_invest(storage1) +c_u_GenericInvestmentStorageBlock_min_storage_content(storage1_0_0)_: -1 GenericInvestmentStorageBlock_storage_content(storage1_0) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage1_0) <= 0 -c_u_GenericInvestmentStorageBlock_min_storage_content(storage1_1)_: -+0.10000000000000001 GenericInvestmentStorageBlock_invest(storage1) +c_u_GenericInvestmentStorageBlock_min_storage_content(storage1_0_1)_: -1 GenericInvestmentStorageBlock_storage_content(storage1_1) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage1_0) <= 0 -c_u_GenericInvestmentStorageBlock_min_storage_content(storage1_2)_: -+0.10000000000000001 GenericInvestmentStorageBlock_invest(storage1) +c_u_GenericInvestmentStorageBlock_min_storage_content(storage1_0_2)_: -1 GenericInvestmentStorageBlock_storage_content(storage1_2) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage1_0) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(electricityBus_storage1_0) <= +inf - 0 <= flow(electricityBus_storage1_1) <= +inf - 0 <= flow(electricityBus_storage1_2) <= +inf - 0 <= flow(storage1_electricityBus_0) <= +inf - 0 <= flow(storage1_electricityBus_1) <= +inf - 0 <= flow(storage1_electricityBus_2) <= +inf - 0 <= InvestmentFlowBlock_invest(electricityBus_storage1) <= +inf - 0 <= InvestmentFlowBlock_invest(storage1_electricityBus) <= +inf + 0 <= flow(electricityBus_storage1_0_0) <= +inf + 0 <= flow(electricityBus_storage1_0_1) <= +inf + 0 <= flow(electricityBus_storage1_0_2) <= +inf + 0 <= flow(storage1_electricityBus_0_0) <= +inf + 0 <= flow(storage1_electricityBus_0_1) <= +inf + 0 <= flow(storage1_electricityBus_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage1_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage1_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage1_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage1_electricityBus_0) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage1_0) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage1_1) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage1_2) <= +inf - 0 <= GenericInvestmentStorageBlock_invest(storage1) <= 234 + 0 <= GenericInvestmentStorageBlock_invest(storage1_0) <= 234 + 0 <= GenericInvestmentStorageBlock_total(storage1_0) <= +inf 0 <= GenericInvestmentStorageBlock_init_content(storage1) <= +inf end diff --git a/tests/lp_files/transformer.lp b/tests/lp_files/transformer.lp index 0c5d3d11b..3f5f33803 100644 --- a/tests/lp_files/transformer.lp +++ b/tests/lp_files/transformer.lp @@ -1,138 +1,138 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+50 flow(powerplantGasCoal_electricityBus_0) -+50 flow(powerplantGasCoal_electricityBus_1) -+50 flow(powerplantGasCoal_electricityBus_2) -+20 flow(powerplantGasCoal_thermalBus_0) -+20 flow(powerplantGasCoal_thermalBus_1) -+20 flow(powerplantGasCoal_thermalBus_2) ++50 flow(powerplantGasBiomass_electricityBus_0_0) ++50 flow(powerplantGasBiomass_electricityBus_0_1) ++50 flow(powerplantGasBiomass_electricityBus_0_2) ++20 flow(powerplantGasBiomass_thermalBus_0_0) ++20 flow(powerplantGasBiomass_thermalBus_0_1) ++20 flow(powerplantGasBiomass_thermalBus_0_2) s.t. -c_e_BusBlock_balance(biomassBus_0)_: -+1 flow(biomassBus_powerplantGasCoal_0) +c_e_BusBlock_balance(biomassBus_0_0)_: ++1 flow(biomassBus_powerplantGasBiomass_0_0) = 0 -c_e_BusBlock_balance(biomassBus_1)_: -+1 flow(biomassBus_powerplantGasCoal_1) +c_e_BusBlock_balance(biomassBus_0_1)_: ++1 flow(biomassBus_powerplantGasBiomass_0_1) = 0 -c_e_BusBlock_balance(biomassBus_2)_: -+1 flow(biomassBus_powerplantGasCoal_2) +c_e_BusBlock_balance(biomassBus_0_2)_: ++1 flow(biomassBus_powerplantGasBiomass_0_2) = 0 -c_e_BusBlock_balance(electricityBus_0)_: -+1 flow(powerplantGasCoal_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(powerplantGasBiomass_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: -+1 flow(powerplantGasCoal_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(powerplantGasBiomass_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: -+1 flow(powerplantGasCoal_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(powerplantGasBiomass_electricityBus_0_2) = 0 -c_e_BusBlock_balance(gasBus_0)_: -+1 flow(gasBus_powerplantGasCoal_0) +c_e_BusBlock_balance(gasBus_0_0)_: ++1 flow(gasBus_powerplantGasBiomass_0_0) = 0 -c_e_BusBlock_balance(gasBus_1)_: -+1 flow(gasBus_powerplantGasCoal_1) +c_e_BusBlock_balance(gasBus_0_1)_: ++1 flow(gasBus_powerplantGasBiomass_0_1) = 0 -c_e_BusBlock_balance(gasBus_2)_: -+1 flow(gasBus_powerplantGasCoal_2) +c_e_BusBlock_balance(gasBus_0_2)_: ++1 flow(gasBus_powerplantGasBiomass_0_2) = 0 -c_e_BusBlock_balance(thermalBus_0)_: -+1 flow(powerplantGasCoal_thermalBus_0) +c_e_BusBlock_balance(thermalBus_0_0)_: ++1 flow(powerplantGasBiomass_thermalBus_0_0) = 0 -c_e_BusBlock_balance(thermalBus_1)_: -+1 flow(powerplantGasCoal_thermalBus_1) +c_e_BusBlock_balance(thermalBus_0_1)_: ++1 flow(powerplantGasBiomass_thermalBus_0_1) = 0 -c_e_BusBlock_balance(thermalBus_2)_: -+1 flow(powerplantGasCoal_thermalBus_2) +c_e_BusBlock_balance(thermalBus_0_2)_: ++1 flow(powerplantGasBiomass_thermalBus_0_2) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_biomassBus_electricityBus_0)_: -+0.29999999999999999 flow(biomassBus_powerplantGasCoal_0) --0.10000000000000001 flow(powerplantGasCoal_electricityBus_0) +c_e_TransformerBlock_relation(powerplantGasBiomass_biomassBus_electricityBus_0_0)_: ++0.29999999999999999 flow(biomassBus_powerplantGasBiomass_0_0) +-0.10000000000000001 flow(powerplantGasBiomass_electricityBus_0_0) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_biomassBus_electricityBus_1)_: -+0.29999999999999999 flow(biomassBus_powerplantGasCoal_1) --0.10000000000000001 flow(powerplantGasCoal_electricityBus_1) +c_e_TransformerBlock_relation(powerplantGasBiomass_biomassBus_electricityBus_0_1)_: ++0.29999999999999999 flow(biomassBus_powerplantGasBiomass_0_1) +-0.10000000000000001 flow(powerplantGasBiomass_electricityBus_0_1) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_biomassBus_electricityBus_2)_: -+0.29999999999999999 flow(biomassBus_powerplantGasCoal_2) --0.10000000000000001 flow(powerplantGasCoal_electricityBus_2) +c_e_TransformerBlock_relation(powerplantGasBiomass_biomassBus_electricityBus_0_2)_: ++0.29999999999999999 flow(biomassBus_powerplantGasBiomass_0_2) +-0.10000000000000001 flow(powerplantGasBiomass_electricityBus_0_2) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_biomassBus_thermalBus_0)_: -+0.5 flow(biomassBus_powerplantGasCoal_0) --0.10000000000000001 flow(powerplantGasCoal_thermalBus_0) +c_e_TransformerBlock_relation(powerplantGasBiomass_biomassBus_thermalBus_0_0)_: ++0.5 flow(biomassBus_powerplantGasBiomass_0_0) +-0.10000000000000001 flow(powerplantGasBiomass_thermalBus_0_0) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_biomassBus_thermalBus_1)_: -+0.5 flow(biomassBus_powerplantGasCoal_1) --0.10000000000000001 flow(powerplantGasCoal_thermalBus_1) +c_e_TransformerBlock_relation(powerplantGasBiomass_biomassBus_thermalBus_0_1)_: ++0.5 flow(biomassBus_powerplantGasBiomass_0_1) +-0.10000000000000001 flow(powerplantGasBiomass_thermalBus_0_1) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_biomassBus_thermalBus_2)_: -+0.5 flow(biomassBus_powerplantGasCoal_2) --0.10000000000000001 flow(powerplantGasCoal_thermalBus_2) +c_e_TransformerBlock_relation(powerplantGasBiomass_biomassBus_thermalBus_0_2)_: ++0.5 flow(biomassBus_powerplantGasBiomass_0_2) +-0.10000000000000001 flow(powerplantGasBiomass_thermalBus_0_2) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_gasBus_electricityBus_0)_: -+0.29999999999999999 flow(gasBus_powerplantGasCoal_0) --0.40000000000000002 flow(powerplantGasCoal_electricityBus_0) +c_e_TransformerBlock_relation(powerplantGasBiomass_gasBus_electricityBus_0_0)_: ++0.29999999999999999 flow(gasBus_powerplantGasBiomass_0_0) +-0.40000000000000002 flow(powerplantGasBiomass_electricityBus_0_0) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_gasBus_electricityBus_1)_: -+0.29999999999999999 flow(gasBus_powerplantGasCoal_1) --0.40000000000000002 flow(powerplantGasCoal_electricityBus_1) +c_e_TransformerBlock_relation(powerplantGasBiomass_gasBus_electricityBus_0_1)_: ++0.29999999999999999 flow(gasBus_powerplantGasBiomass_0_1) +-0.40000000000000002 flow(powerplantGasBiomass_electricityBus_0_1) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_gasBus_electricityBus_2)_: -+0.29999999999999999 flow(gasBus_powerplantGasCoal_2) --0.40000000000000002 flow(powerplantGasCoal_electricityBus_2) +c_e_TransformerBlock_relation(powerplantGasBiomass_gasBus_electricityBus_0_2)_: ++0.29999999999999999 flow(gasBus_powerplantGasBiomass_0_2) +-0.40000000000000002 flow(powerplantGasBiomass_electricityBus_0_2) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_gasBus_thermalBus_0)_: -+0.5 flow(gasBus_powerplantGasCoal_0) --0.40000000000000002 flow(powerplantGasCoal_thermalBus_0) +c_e_TransformerBlock_relation(powerplantGasBiomass_gasBus_thermalBus_0_0)_: ++0.5 flow(gasBus_powerplantGasBiomass_0_0) +-0.40000000000000002 flow(powerplantGasBiomass_thermalBus_0_0) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_gasBus_thermalBus_1)_: -+0.5 flow(gasBus_powerplantGasCoal_1) --0.40000000000000002 flow(powerplantGasCoal_thermalBus_1) +c_e_TransformerBlock_relation(powerplantGasBiomass_gasBus_thermalBus_0_1)_: ++0.5 flow(gasBus_powerplantGasBiomass_0_1) +-0.40000000000000002 flow(powerplantGasBiomass_thermalBus_0_1) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_gasBus_thermalBus_2)_: -+0.5 flow(gasBus_powerplantGasCoal_2) --0.40000000000000002 flow(powerplantGasCoal_thermalBus_2) +c_e_TransformerBlock_relation(powerplantGasBiomass_gasBus_thermalBus_0_2)_: ++0.5 flow(gasBus_powerplantGasBiomass_0_2) +-0.40000000000000002 flow(powerplantGasBiomass_thermalBus_0_2) = 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(biomassBus_powerplantGasCoal_0) <= +inf - 0 <= flow(biomassBus_powerplantGasCoal_1) <= +inf - 0 <= flow(biomassBus_powerplantGasCoal_2) <= +inf - 0 <= flow(gasBus_powerplantGasCoal_0) <= +inf - 0 <= flow(gasBus_powerplantGasCoal_1) <= +inf - 0 <= flow(gasBus_powerplantGasCoal_2) <= +inf - 0 <= flow(powerplantGasCoal_electricityBus_0) <= +inf - 0 <= flow(powerplantGasCoal_electricityBus_1) <= +inf - 0 <= flow(powerplantGasCoal_electricityBus_2) <= +inf - 0 <= flow(powerplantGasCoal_thermalBus_0) <= 50000000000 - 0 <= flow(powerplantGasCoal_thermalBus_1) <= 50000000000 - 0 <= flow(powerplantGasCoal_thermalBus_2) <= 50000000000 + 0 <= flow(biomassBus_powerplantGasBiomass_0_0) <= +inf + 0 <= flow(biomassBus_powerplantGasBiomass_0_1) <= +inf + 0 <= flow(biomassBus_powerplantGasBiomass_0_2) <= +inf + 0 <= flow(gasBus_powerplantGasBiomass_0_0) <= +inf + 0 <= flow(gasBus_powerplantGasBiomass_0_1) <= +inf + 0 <= flow(gasBus_powerplantGasBiomass_0_2) <= +inf + 0 <= flow(powerplantGasBiomass_electricityBus_0_0) <= +inf + 0 <= flow(powerplantGasBiomass_electricityBus_0_1) <= +inf + 0 <= flow(powerplantGasBiomass_electricityBus_0_2) <= +inf + 0 <= flow(powerplantGasBiomass_thermalBus_0_0) <= 50000000000 + 0 <= flow(powerplantGasBiomass_thermalBus_0_1) <= 50000000000 + 0 <= flow(powerplantGasBiomass_thermalBus_0_2) <= 50000000000 end diff --git a/tests/lp_files/transformer_invest.lp b/tests/lp_files/transformer_invest.lp index 25e4fe67d..beabc2785 100644 --- a/tests/lp_files/transformer_invest.lp +++ b/tests/lp_files/transformer_invest.lp @@ -1,155 +1,161 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+20 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus) -+50 flow(powerplant_gas_coal_electricityBus_0) -+50 flow(powerplant_gas_coal_electricityBus_1) -+50 flow(powerplant_gas_coal_electricityBus_2) -+20 flow(powerplant_gas_coal_thermalBus_0) -+20 flow(powerplant_gas_coal_thermalBus_1) -+20 flow(powerplant_gas_coal_thermalBus_2) ++20 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_0) ++50 flow(powerplant_gas_coal_electricityBus_0_0) ++50 flow(powerplant_gas_coal_electricityBus_0_1) ++50 flow(powerplant_gas_coal_electricityBus_0_2) ++20 flow(powerplant_gas_coal_thermalBus_0_0) ++20 flow(powerplant_gas_coal_thermalBus_0_1) ++20 flow(powerplant_gas_coal_thermalBus_0_2) s.t. -c_e_BusBlock_balance(coalBus_0)_: -+1 flow(coalBus_powerplant_gas_coal_0) +c_e_BusBlock_balance(coalBus_0_0)_: ++1 flow(coalBus_powerplant_gas_coal_0_0) = 0 -c_e_BusBlock_balance(coalBus_1)_: -+1 flow(coalBus_powerplant_gas_coal_1) +c_e_BusBlock_balance(coalBus_0_1)_: ++1 flow(coalBus_powerplant_gas_coal_0_1) = 0 -c_e_BusBlock_balance(coalBus_2)_: -+1 flow(coalBus_powerplant_gas_coal_2) +c_e_BusBlock_balance(coalBus_0_2)_: ++1 flow(coalBus_powerplant_gas_coal_0_2) = 0 -c_e_BusBlock_balance(electricityBus_0)_: -+1 flow(powerplant_gas_coal_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(powerplant_gas_coal_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: -+1 flow(powerplant_gas_coal_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(powerplant_gas_coal_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: -+1 flow(powerplant_gas_coal_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(powerplant_gas_coal_electricityBus_0_2) = 0 -c_e_BusBlock_balance(gasBus_0)_: -+1 flow(gasBus_powerplant_gas_coal_0) +c_e_BusBlock_balance(gasBus_0_0)_: ++1 flow(gasBus_powerplant_gas_coal_0_0) = 0 -c_e_BusBlock_balance(gasBus_1)_: -+1 flow(gasBus_powerplant_gas_coal_1) +c_e_BusBlock_balance(gasBus_0_1)_: ++1 flow(gasBus_powerplant_gas_coal_0_1) = 0 -c_e_BusBlock_balance(gasBus_2)_: -+1 flow(gasBus_powerplant_gas_coal_2) +c_e_BusBlock_balance(gasBus_0_2)_: ++1 flow(gasBus_powerplant_gas_coal_0_2) = 0 -c_e_BusBlock_balance(thermalBus_0)_: -+1 flow(powerplant_gas_coal_thermalBus_0) +c_e_BusBlock_balance(thermalBus_0_0)_: ++1 flow(powerplant_gas_coal_thermalBus_0_0) = 0 -c_e_BusBlock_balance(thermalBus_1)_: -+1 flow(powerplant_gas_coal_thermalBus_1) +c_e_BusBlock_balance(thermalBus_0_1)_: ++1 flow(powerplant_gas_coal_thermalBus_0_1) = 0 -c_e_BusBlock_balance(thermalBus_2)_: -+1 flow(powerplant_gas_coal_thermalBus_2) +c_e_BusBlock_balance(thermalBus_0_2)_: ++1 flow(powerplant_gas_coal_thermalBus_0_2) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_0)_: -+0.29999999999999999 flow(coalBus_powerplant_gas_coal_0) --0.20000000000000001 flow(powerplant_gas_coal_electricityBus_0) +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_0_0)_: ++0.29999999999999999 flow(coalBus_powerplant_gas_coal_0_0) +-0.20000000000000001 flow(powerplant_gas_coal_electricityBus_0_0) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_1)_: -+0.29999999999999999 flow(coalBus_powerplant_gas_coal_1) --0.20000000000000001 flow(powerplant_gas_coal_electricityBus_1) +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_0_1)_: ++0.29999999999999999 flow(coalBus_powerplant_gas_coal_0_1) +-0.20000000000000001 flow(powerplant_gas_coal_electricityBus_0_1) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_2)_: -+0.29999999999999999 flow(coalBus_powerplant_gas_coal_2) --0.20000000000000001 flow(powerplant_gas_coal_electricityBus_2) +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_0_2)_: ++0.29999999999999999 flow(coalBus_powerplant_gas_coal_0_2) +-0.20000000000000001 flow(powerplant_gas_coal_electricityBus_0_2) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_0)_: -+0.5 flow(coalBus_powerplant_gas_coal_0) --0.20000000000000001 flow(powerplant_gas_coal_thermalBus_0) +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_0_0)_: ++0.5 flow(coalBus_powerplant_gas_coal_0_0) +-0.20000000000000001 flow(powerplant_gas_coal_thermalBus_0_0) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_1)_: -+0.5 flow(coalBus_powerplant_gas_coal_1) --0.20000000000000001 flow(powerplant_gas_coal_thermalBus_1) +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_0_1)_: ++0.5 flow(coalBus_powerplant_gas_coal_0_1) +-0.20000000000000001 flow(powerplant_gas_coal_thermalBus_0_1) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_2)_: -+0.5 flow(coalBus_powerplant_gas_coal_2) --0.20000000000000001 flow(powerplant_gas_coal_thermalBus_2) +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_0_2)_: ++0.5 flow(coalBus_powerplant_gas_coal_0_2) +-0.20000000000000001 flow(powerplant_gas_coal_thermalBus_0_2) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_0)_: -+0.29999999999999999 flow(gasBus_powerplant_gas_coal_0) --0.57999999999999996 flow(powerplant_gas_coal_electricityBus_0) +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_0_0)_: ++0.29999999999999999 flow(gasBus_powerplant_gas_coal_0_0) +-0.57999999999999996 flow(powerplant_gas_coal_electricityBus_0_0) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_1)_: -+0.29999999999999999 flow(gasBus_powerplant_gas_coal_1) --0.57999999999999996 flow(powerplant_gas_coal_electricityBus_1) +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_0_1)_: ++0.29999999999999999 flow(gasBus_powerplant_gas_coal_0_1) +-0.57999999999999996 flow(powerplant_gas_coal_electricityBus_0_1) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_2)_: -+0.29999999999999999 flow(gasBus_powerplant_gas_coal_2) --0.57999999999999996 flow(powerplant_gas_coal_electricityBus_2) +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_0_2)_: ++0.29999999999999999 flow(gasBus_powerplant_gas_coal_0_2) +-0.57999999999999996 flow(powerplant_gas_coal_electricityBus_0_2) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_0)_: -+0.5 flow(gasBus_powerplant_gas_coal_0) --0.57999999999999996 flow(powerplant_gas_coal_thermalBus_0) +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_0_0)_: ++0.5 flow(gasBus_powerplant_gas_coal_0_0) +-0.57999999999999996 flow(powerplant_gas_coal_thermalBus_0_0) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_1)_: -+0.5 flow(gasBus_powerplant_gas_coal_1) --0.57999999999999996 flow(powerplant_gas_coal_thermalBus_1) +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_0_1)_: ++0.5 flow(gasBus_powerplant_gas_coal_0_1) +-0.57999999999999996 flow(powerplant_gas_coal_thermalBus_0_1) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_2)_: -+0.5 flow(gasBus_powerplant_gas_coal_2) --0.57999999999999996 flow(powerplant_gas_coal_thermalBus_2) +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_0_2)_: ++0.5 flow(gasBus_powerplant_gas_coal_0_2) +-0.57999999999999996 flow(powerplant_gas_coal_thermalBus_0_2) = 0 -c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_0)_: --1 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus) -+1 flow(powerplant_gas_coal_electricityBus_0) +c_e_InvestmentFlowBlock_total_rule(powerplant_gas_coal_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_0) ++1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) += 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) ++1 flow(powerplant_gas_coal_electricityBus_0_0) <= 0 -c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_1)_: --1 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus) -+1 flow(powerplant_gas_coal_electricityBus_1) +c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) ++1 flow(powerplant_gas_coal_electricityBus_0_1) <= 0 -c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_2)_: --1 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus) -+1 flow(powerplant_gas_coal_electricityBus_2) +c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_0_2)_: +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) ++1 flow(powerplant_gas_coal_electricityBus_0_2) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(coalBus_powerplant_gas_coal_0) <= +inf - 0 <= flow(coalBus_powerplant_gas_coal_1) <= +inf - 0 <= flow(coalBus_powerplant_gas_coal_2) <= +inf - 0 <= flow(gasBus_powerplant_gas_coal_0) <= +inf - 0 <= flow(gasBus_powerplant_gas_coal_1) <= +inf - 0 <= flow(gasBus_powerplant_gas_coal_2) <= +inf - 0 <= flow(powerplant_gas_coal_electricityBus_0) <= +inf - 0 <= flow(powerplant_gas_coal_electricityBus_1) <= +inf - 0 <= flow(powerplant_gas_coal_electricityBus_2) <= +inf - 0 <= flow(powerplant_gas_coal_thermalBus_0) <= +inf - 0 <= flow(powerplant_gas_coal_thermalBus_1) <= +inf - 0 <= flow(powerplant_gas_coal_thermalBus_2) <= +inf - 0 <= InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus) <= 1000 + 0 <= flow(coalBus_powerplant_gas_coal_0_0) <= +inf + 0 <= flow(coalBus_powerplant_gas_coal_0_1) <= +inf + 0 <= flow(coalBus_powerplant_gas_coal_0_2) <= +inf + 0 <= flow(gasBus_powerplant_gas_coal_0_0) <= +inf + 0 <= flow(gasBus_powerplant_gas_coal_0_1) <= +inf + 0 <= flow(gasBus_powerplant_gas_coal_0_2) <= +inf + 0 <= flow(powerplant_gas_coal_electricityBus_0_0) <= +inf + 0 <= flow(powerplant_gas_coal_electricityBus_0_1) <= +inf + 0 <= flow(powerplant_gas_coal_electricityBus_0_2) <= +inf + 0 <= flow(powerplant_gas_coal_thermalBus_0_0) <= +inf + 0 <= flow(powerplant_gas_coal_thermalBus_0_1) <= +inf + 0 <= flow(powerplant_gas_coal_thermalBus_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_0) <= 1000 + 0 <= InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) <= +inf end diff --git a/tests/lp_files/transformer_invest_with_existing.lp b/tests/lp_files/transformer_invest_with_existing.lp index 3e2af2ebf..0087c2ecd 100644 --- a/tests/lp_files/transformer_invest_with_existing.lp +++ b/tests/lp_files/transformer_invest_with_existing.lp @@ -1,155 +1,161 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+20 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus) -+50 flow(powerplant_gas_coal_electricityBus_0) -+50 flow(powerplant_gas_coal_electricityBus_1) -+50 flow(powerplant_gas_coal_electricityBus_2) -+20 flow(powerplant_gas_coal_thermalBus_0) -+20 flow(powerplant_gas_coal_thermalBus_1) -+20 flow(powerplant_gas_coal_thermalBus_2) ++20 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_0) ++50 flow(powerplant_gas_coal_electricityBus_0_0) ++50 flow(powerplant_gas_coal_electricityBus_0_1) ++50 flow(powerplant_gas_coal_electricityBus_0_2) ++20 flow(powerplant_gas_coal_thermalBus_0_0) ++20 flow(powerplant_gas_coal_thermalBus_0_1) ++20 flow(powerplant_gas_coal_thermalBus_0_2) s.t. -c_e_BusBlock_balance(coalBus_0)_: -+1 flow(coalBus_powerplant_gas_coal_0) +c_e_BusBlock_balance(coalBus_0_0)_: ++1 flow(coalBus_powerplant_gas_coal_0_0) = 0 -c_e_BusBlock_balance(coalBus_1)_: -+1 flow(coalBus_powerplant_gas_coal_1) +c_e_BusBlock_balance(coalBus_0_1)_: ++1 flow(coalBus_powerplant_gas_coal_0_1) = 0 -c_e_BusBlock_balance(coalBus_2)_: -+1 flow(coalBus_powerplant_gas_coal_2) +c_e_BusBlock_balance(coalBus_0_2)_: ++1 flow(coalBus_powerplant_gas_coal_0_2) = 0 -c_e_BusBlock_balance(electricityBus_0)_: -+1 flow(powerplant_gas_coal_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(powerplant_gas_coal_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: -+1 flow(powerplant_gas_coal_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(powerplant_gas_coal_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: -+1 flow(powerplant_gas_coal_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(powerplant_gas_coal_electricityBus_0_2) = 0 -c_e_BusBlock_balance(gasBus_0)_: -+1 flow(gasBus_powerplant_gas_coal_0) +c_e_BusBlock_balance(gasBus_0_0)_: ++1 flow(gasBus_powerplant_gas_coal_0_0) = 0 -c_e_BusBlock_balance(gasBus_1)_: -+1 flow(gasBus_powerplant_gas_coal_1) +c_e_BusBlock_balance(gasBus_0_1)_: ++1 flow(gasBus_powerplant_gas_coal_0_1) = 0 -c_e_BusBlock_balance(gasBus_2)_: -+1 flow(gasBus_powerplant_gas_coal_2) +c_e_BusBlock_balance(gasBus_0_2)_: ++1 flow(gasBus_powerplant_gas_coal_0_2) = 0 -c_e_BusBlock_balance(thermalBus_0)_: -+1 flow(powerplant_gas_coal_thermalBus_0) +c_e_BusBlock_balance(thermalBus_0_0)_: ++1 flow(powerplant_gas_coal_thermalBus_0_0) = 0 -c_e_BusBlock_balance(thermalBus_1)_: -+1 flow(powerplant_gas_coal_thermalBus_1) +c_e_BusBlock_balance(thermalBus_0_1)_: ++1 flow(powerplant_gas_coal_thermalBus_0_1) = 0 -c_e_BusBlock_balance(thermalBus_2)_: -+1 flow(powerplant_gas_coal_thermalBus_2) +c_e_BusBlock_balance(thermalBus_0_2)_: ++1 flow(powerplant_gas_coal_thermalBus_0_2) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_0)_: -+0.29999999999999999 flow(coalBus_powerplant_gas_coal_0) --0.20000000000000001 flow(powerplant_gas_coal_electricityBus_0) +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_0_0)_: ++0.29999999999999999 flow(coalBus_powerplant_gas_coal_0_0) +-0.20000000000000001 flow(powerplant_gas_coal_electricityBus_0_0) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_1)_: -+0.29999999999999999 flow(coalBus_powerplant_gas_coal_1) --0.20000000000000001 flow(powerplant_gas_coal_electricityBus_1) +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_0_1)_: ++0.29999999999999999 flow(coalBus_powerplant_gas_coal_0_1) +-0.20000000000000001 flow(powerplant_gas_coal_electricityBus_0_1) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_2)_: -+0.29999999999999999 flow(coalBus_powerplant_gas_coal_2) --0.20000000000000001 flow(powerplant_gas_coal_electricityBus_2) +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_0_2)_: ++0.29999999999999999 flow(coalBus_powerplant_gas_coal_0_2) +-0.20000000000000001 flow(powerplant_gas_coal_electricityBus_0_2) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_0)_: -+0.5 flow(coalBus_powerplant_gas_coal_0) --0.20000000000000001 flow(powerplant_gas_coal_thermalBus_0) +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_0_0)_: ++0.5 flow(coalBus_powerplant_gas_coal_0_0) +-0.20000000000000001 flow(powerplant_gas_coal_thermalBus_0_0) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_1)_: -+0.5 flow(coalBus_powerplant_gas_coal_1) --0.20000000000000001 flow(powerplant_gas_coal_thermalBus_1) +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_0_1)_: ++0.5 flow(coalBus_powerplant_gas_coal_0_1) +-0.20000000000000001 flow(powerplant_gas_coal_thermalBus_0_1) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_2)_: -+0.5 flow(coalBus_powerplant_gas_coal_2) --0.20000000000000001 flow(powerplant_gas_coal_thermalBus_2) +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_0_2)_: ++0.5 flow(coalBus_powerplant_gas_coal_0_2) +-0.20000000000000001 flow(powerplant_gas_coal_thermalBus_0_2) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_0)_: -+0.29999999999999999 flow(gasBus_powerplant_gas_coal_0) --0.57999999999999996 flow(powerplant_gas_coal_electricityBus_0) +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_0_0)_: ++0.29999999999999999 flow(gasBus_powerplant_gas_coal_0_0) +-0.57999999999999996 flow(powerplant_gas_coal_electricityBus_0_0) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_1)_: -+0.29999999999999999 flow(gasBus_powerplant_gas_coal_1) --0.57999999999999996 flow(powerplant_gas_coal_electricityBus_1) +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_0_1)_: ++0.29999999999999999 flow(gasBus_powerplant_gas_coal_0_1) +-0.57999999999999996 flow(powerplant_gas_coal_electricityBus_0_1) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_2)_: -+0.29999999999999999 flow(gasBus_powerplant_gas_coal_2) --0.57999999999999996 flow(powerplant_gas_coal_electricityBus_2) +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_0_2)_: ++0.29999999999999999 flow(gasBus_powerplant_gas_coal_0_2) +-0.57999999999999996 flow(powerplant_gas_coal_electricityBus_0_2) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_0)_: -+0.5 flow(gasBus_powerplant_gas_coal_0) --0.57999999999999996 flow(powerplant_gas_coal_thermalBus_0) +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_0_0)_: ++0.5 flow(gasBus_powerplant_gas_coal_0_0) +-0.57999999999999996 flow(powerplant_gas_coal_thermalBus_0_0) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_1)_: -+0.5 flow(gasBus_powerplant_gas_coal_1) --0.57999999999999996 flow(powerplant_gas_coal_thermalBus_1) +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_0_1)_: ++0.5 flow(gasBus_powerplant_gas_coal_0_1) +-0.57999999999999996 flow(powerplant_gas_coal_thermalBus_0_1) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_2)_: -+0.5 flow(gasBus_powerplant_gas_coal_2) --0.57999999999999996 flow(powerplant_gas_coal_thermalBus_2) +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_0_2)_: ++0.5 flow(gasBus_powerplant_gas_coal_0_2) +-0.57999999999999996 flow(powerplant_gas_coal_thermalBus_0_2) = 0 -c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_0)_: --1 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus) -+1 flow(powerplant_gas_coal_electricityBus_0) -<= 200 +c_e_InvestmentFlowBlock_total_rule(powerplant_gas_coal_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_0) ++1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) += 200 -c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_1)_: --1 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus) -+1 flow(powerplant_gas_coal_electricityBus_1) -<= 200 +c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) ++1 flow(powerplant_gas_coal_electricityBus_0_0) +<= 0 -c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_2)_: --1 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus) -+1 flow(powerplant_gas_coal_electricityBus_2) -<= 200 +c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) ++1 flow(powerplant_gas_coal_electricityBus_0_1) +<= 0 -c_e_ONE_VAR_CONSTANT: +c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_0_2)_: +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) ++1 flow(powerplant_gas_coal_electricityBus_0_2) +<= 0 + +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(coalBus_powerplant_gas_coal_0) <= +inf - 0 <= flow(coalBus_powerplant_gas_coal_1) <= +inf - 0 <= flow(coalBus_powerplant_gas_coal_2) <= +inf - 0 <= flow(gasBus_powerplant_gas_coal_0) <= +inf - 0 <= flow(gasBus_powerplant_gas_coal_1) <= +inf - 0 <= flow(gasBus_powerplant_gas_coal_2) <= +inf - 0 <= flow(powerplant_gas_coal_electricityBus_0) <= +inf - 0 <= flow(powerplant_gas_coal_electricityBus_1) <= +inf - 0 <= flow(powerplant_gas_coal_electricityBus_2) <= +inf - 0 <= flow(powerplant_gas_coal_thermalBus_0) <= +inf - 0 <= flow(powerplant_gas_coal_thermalBus_1) <= +inf - 0 <= flow(powerplant_gas_coal_thermalBus_2) <= +inf - 0 <= InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus) <= 1000 + 0 <= flow(coalBus_powerplant_gas_coal_0_0) <= +inf + 0 <= flow(coalBus_powerplant_gas_coal_0_1) <= +inf + 0 <= flow(coalBus_powerplant_gas_coal_0_2) <= +inf + 0 <= flow(gasBus_powerplant_gas_coal_0_0) <= +inf + 0 <= flow(gasBus_powerplant_gas_coal_0_1) <= +inf + 0 <= flow(gasBus_powerplant_gas_coal_0_2) <= +inf + 0 <= flow(powerplant_gas_coal_electricityBus_0_0) <= +inf + 0 <= flow(powerplant_gas_coal_electricityBus_0_1) <= +inf + 0 <= flow(powerplant_gas_coal_electricityBus_0_2) <= +inf + 0 <= flow(powerplant_gas_coal_thermalBus_0_0) <= +inf + 0 <= flow(powerplant_gas_coal_thermalBus_0_1) <= +inf + 0 <= flow(powerplant_gas_coal_thermalBus_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_0) <= 1000 + 0 <= InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) <= +inf end diff --git a/tests/lp_files/variable_chp.lp b/tests/lp_files/variable_chp.lp index 1b5ba905d..7931be337 100644 --- a/tests/lp_files/variable_chp.lp +++ b/tests/lp_files/variable_chp.lp @@ -1,142 +1,142 @@ \* Source Pyomo model name=Model *\ -min +min objective: +0 ONE_VAR_CONSTANT s.t. -c_e_BusBlock_balance(commodityBus_0)_: -+1 flow(commodityBus_variable_chp_gas1_0) -+1 flow(commodityBus_variable_chp_gas2_0) +c_e_BusBlock_balance(commodityBus_0_0)_: ++1 flow(commodityBus_variable_chp_gas1_0_0) ++1 flow(commodityBus_variable_chp_gas2_0_0) = 0 -c_e_BusBlock_balance(commodityBus_1)_: -+1 flow(commodityBus_variable_chp_gas1_1) -+1 flow(commodityBus_variable_chp_gas2_1) +c_e_BusBlock_balance(commodityBus_0_1)_: ++1 flow(commodityBus_variable_chp_gas1_0_1) ++1 flow(commodityBus_variable_chp_gas2_0_1) = 0 -c_e_BusBlock_balance(commodityBus_2)_: -+1 flow(commodityBus_variable_chp_gas1_2) -+1 flow(commodityBus_variable_chp_gas2_2) +c_e_BusBlock_balance(commodityBus_0_2)_: ++1 flow(commodityBus_variable_chp_gas1_0_2) ++1 flow(commodityBus_variable_chp_gas2_0_2) = 0 -c_e_BusBlock_balance(electricityBus_0)_: -+1 flow(variable_chp_gas1_electricityBus_0) -+1 flow(variable_chp_gas2_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(variable_chp_gas1_electricityBus_0_0) ++1 flow(variable_chp_gas2_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: -+1 flow(variable_chp_gas1_electricityBus_1) -+1 flow(variable_chp_gas2_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(variable_chp_gas1_electricityBus_0_1) ++1 flow(variable_chp_gas2_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: -+1 flow(variable_chp_gas1_electricityBus_2) -+1 flow(variable_chp_gas2_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(variable_chp_gas1_electricityBus_0_2) ++1 flow(variable_chp_gas2_electricityBus_0_2) = 0 -c_e_BusBlock_balance(heatBus_0)_: -+1 flow(variable_chp_gas1_heatBus_0) -+1 flow(variable_chp_gas2_heatBus_0) +c_e_BusBlock_balance(heatBus_0_0)_: ++1 flow(variable_chp_gas1_heatBus_0_0) ++1 flow(variable_chp_gas2_heatBus_0_0) = 0 -c_e_BusBlock_balance(heatBus_1)_: -+1 flow(variable_chp_gas1_heatBus_1) -+1 flow(variable_chp_gas2_heatBus_1) +c_e_BusBlock_balance(heatBus_0_1)_: ++1 flow(variable_chp_gas1_heatBus_0_1) ++1 flow(variable_chp_gas2_heatBus_0_1) = 0 -c_e_BusBlock_balance(heatBus_2)_: -+1 flow(variable_chp_gas1_heatBus_2) -+1 flow(variable_chp_gas2_heatBus_2) +c_e_BusBlock_balance(heatBus_0_2)_: ++1 flow(variable_chp_gas1_heatBus_0_2) ++1 flow(variable_chp_gas2_heatBus_0_2) = 0 -c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas1_0)_: -+1 flow(commodityBus_variable_chp_gas1_0) --2 flow(variable_chp_gas1_electricityBus_0) --0.80000000000000004 flow(variable_chp_gas1_heatBus_0) +c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas1_0_0)_: ++1 flow(commodityBus_variable_chp_gas1_0_0) +-2 flow(variable_chp_gas1_electricityBus_0_0) +-0.80000000000000004 flow(variable_chp_gas1_heatBus_0_0) = 0 -c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas1_1)_: -+1 flow(commodityBus_variable_chp_gas1_1) --2 flow(variable_chp_gas1_electricityBus_1) --0.80000000000000004 flow(variable_chp_gas1_heatBus_1) +c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas1_0_1)_: ++1 flow(commodityBus_variable_chp_gas1_0_1) +-2 flow(variable_chp_gas1_electricityBus_0_1) +-0.80000000000000004 flow(variable_chp_gas1_heatBus_0_1) = 0 -c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas1_2)_: -+1 flow(commodityBus_variable_chp_gas1_2) --2 flow(variable_chp_gas1_electricityBus_2) --0.80000000000000004 flow(variable_chp_gas1_heatBus_2) +c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas1_0_2)_: ++1 flow(commodityBus_variable_chp_gas1_0_2) +-2 flow(variable_chp_gas1_electricityBus_0_2) +-0.80000000000000004 flow(variable_chp_gas1_heatBus_0_2) = 0 -c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas2_0)_: -+1 flow(commodityBus_variable_chp_gas2_0) --2 flow(variable_chp_gas2_electricityBus_0) --0.80000000000000004 flow(variable_chp_gas2_heatBus_0) +c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas2_0_0)_: ++1 flow(commodityBus_variable_chp_gas2_0_0) +-2 flow(variable_chp_gas2_electricityBus_0_0) +-0.80000000000000004 flow(variable_chp_gas2_heatBus_0_0) = 0 -c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas2_1)_: -+1 flow(commodityBus_variable_chp_gas2_1) --2 flow(variable_chp_gas2_electricityBus_1) --0.80000000000000004 flow(variable_chp_gas2_heatBus_1) +c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas2_0_1)_: ++1 flow(commodityBus_variable_chp_gas2_0_1) +-2 flow(variable_chp_gas2_electricityBus_0_1) +-0.80000000000000004 flow(variable_chp_gas2_heatBus_0_1) = 0 -c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas2_2)_: -+1 flow(commodityBus_variable_chp_gas2_2) --2 flow(variable_chp_gas2_electricityBus_2) --0.80000000000000004 flow(variable_chp_gas2_heatBus_2) +c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas2_0_2)_: ++1 flow(commodityBus_variable_chp_gas2_0_2) +-2 flow(variable_chp_gas2_electricityBus_0_2) +-0.80000000000000004 flow(variable_chp_gas2_heatBus_0_2) = 0 -c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas1_0)_: --1 flow(variable_chp_gas1_electricityBus_0) -+0.59999999999999998 flow(variable_chp_gas1_heatBus_0) +c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas1_0_0)_: +-1 flow(variable_chp_gas1_electricityBus_0_0) ++0.59999999999999998 flow(variable_chp_gas1_heatBus_0_0) <= 0 -c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas1_1)_: --1 flow(variable_chp_gas1_electricityBus_1) -+0.59999999999999998 flow(variable_chp_gas1_heatBus_1) +c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas1_0_1)_: +-1 flow(variable_chp_gas1_electricityBus_0_1) ++0.59999999999999998 flow(variable_chp_gas1_heatBus_0_1) <= 0 -c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas1_2)_: --1 flow(variable_chp_gas1_electricityBus_2) -+0.59999999999999998 flow(variable_chp_gas1_heatBus_2) +c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas1_0_2)_: +-1 flow(variable_chp_gas1_electricityBus_0_2) ++0.59999999999999998 flow(variable_chp_gas1_heatBus_0_2) <= 0 -c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas2_0)_: --1 flow(variable_chp_gas2_electricityBus_0) -+0.59999999999999998 flow(variable_chp_gas2_heatBus_0) +c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas2_0_0)_: +-1 flow(variable_chp_gas2_electricityBus_0_0) ++0.59999999999999998 flow(variable_chp_gas2_heatBus_0_0) <= 0 -c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas2_1)_: --1 flow(variable_chp_gas2_electricityBus_1) -+0.59999999999999998 flow(variable_chp_gas2_heatBus_1) +c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas2_0_1)_: +-1 flow(variable_chp_gas2_electricityBus_0_1) ++0.59999999999999998 flow(variable_chp_gas2_heatBus_0_1) <= 0 -c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas2_2)_: --1 flow(variable_chp_gas2_electricityBus_2) -+0.59999999999999998 flow(variable_chp_gas2_heatBus_2) +c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas2_0_2)_: +-1 flow(variable_chp_gas2_electricityBus_0_2) ++0.59999999999999998 flow(variable_chp_gas2_heatBus_0_2) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(commodityBus_variable_chp_gas1_0) <= 100 - 0 <= flow(commodityBus_variable_chp_gas1_1) <= 100 - 0 <= flow(commodityBus_variable_chp_gas1_2) <= 100 - 0 <= flow(commodityBus_variable_chp_gas2_0) <= 100 - 0 <= flow(commodityBus_variable_chp_gas2_1) <= 100 - 0 <= flow(commodityBus_variable_chp_gas2_2) <= 100 - 0 <= flow(variable_chp_gas1_electricityBus_0) <= +inf - 0 <= flow(variable_chp_gas1_electricityBus_1) <= +inf - 0 <= flow(variable_chp_gas1_electricityBus_2) <= +inf - 0 <= flow(variable_chp_gas1_heatBus_0) <= +inf - 0 <= flow(variable_chp_gas1_heatBus_1) <= +inf - 0 <= flow(variable_chp_gas1_heatBus_2) <= +inf - 0 <= flow(variable_chp_gas2_electricityBus_0) <= +inf - 0 <= flow(variable_chp_gas2_electricityBus_1) <= +inf - 0 <= flow(variable_chp_gas2_electricityBus_2) <= +inf - 0 <= flow(variable_chp_gas2_heatBus_0) <= +inf - 0 <= flow(variable_chp_gas2_heatBus_1) <= +inf - 0 <= flow(variable_chp_gas2_heatBus_2) <= +inf + 0 <= flow(commodityBus_variable_chp_gas1_0_0) <= 100 + 0 <= flow(commodityBus_variable_chp_gas1_0_1) <= 100 + 0 <= flow(commodityBus_variable_chp_gas1_0_2) <= 100 + 0 <= flow(commodityBus_variable_chp_gas2_0_0) <= 100 + 0 <= flow(commodityBus_variable_chp_gas2_0_1) <= 100 + 0 <= flow(commodityBus_variable_chp_gas2_0_2) <= 100 + 0 <= flow(variable_chp_gas1_electricityBus_0_0) <= +inf + 0 <= flow(variable_chp_gas1_electricityBus_0_1) <= +inf + 0 <= flow(variable_chp_gas1_electricityBus_0_2) <= +inf + 0 <= flow(variable_chp_gas1_heatBus_0_0) <= +inf + 0 <= flow(variable_chp_gas1_heatBus_0_1) <= +inf + 0 <= flow(variable_chp_gas1_heatBus_0_2) <= +inf + 0 <= flow(variable_chp_gas2_electricityBus_0_0) <= +inf + 0 <= flow(variable_chp_gas2_electricityBus_0_1) <= +inf + 0 <= flow(variable_chp_gas2_electricityBus_0_2) <= +inf + 0 <= flow(variable_chp_gas2_heatBus_0_0) <= +inf + 0 <= flow(variable_chp_gas2_heatBus_0_1) <= +inf + 0 <= flow(variable_chp_gas2_heatBus_0_2) <= +inf end From 4f12d252fbfde369b8258c603f91bff58de5ba3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Thu, 17 Feb 2022 14:36:47 +0100 Subject: [PATCH 0142/1363] Rename Transformer to Converter When people hear "transformer", they think of electrical devices. However, the Transformer is neither meant to be (only) electrical nor bidirectional (as electrical transformers typically are). Thus, I got the suggestion to rename it to Converter. The term is more general and does not imply that back-conversion is possible. Fix #728 --- src/oemof/solph/_console_scripts.py | 2 +- src/oemof/solph/_models.py | 4 +- src/oemof/solph/components/__init__.py | 8 +- .../{_transformer.py => _converter.py} | 42 +-- .../components/_extraction_turbine_chp.py | 4 +- ...et_transformer.py => _offset_converter.py} | 28 +- .../solph/components/experimental/__init__.py | 4 +- ...rmer.py => _piecewise_linear_converter.py} | 36 +-- .../solph/constraints/equate_variables.py | 4 +- src/oemof/solph/views.py | 6 +- tests/constraint_tests.py | 56 ++-- .../lp_files/{transformer.lp => converter.lp} | 24 +- ...nsformer_invest.lp => converter_invest.lp} | 24 +- ...g.lp => converter_invest_with_existing.lp} | 24 +- ...ear_transformer.lp => linear_converter.lp} | 6 +- ...sformer_chp.lp => linear_converter_chp.lp} | 12 +- ...vest.lp => linear_converter_chp_invest.lp} | 12 +- ...r_invest.lp => linear_converter_invest.lp} | 6 +- ...fsettransformer.lp => offset_converter.lp} | 6 +- .../lp_files/piecewise_linear_converter_cc.lp | 298 ++++++++++++++++++ .../piecewise_linear_converter_dcc.lp | 289 +++++++++++++++++ .../piecewise_linear_transformer_cc.lp | 298 ------------------ .../piecewise_linear_transformer_dcc.lp | 289 ----------------- tests/test_components.py | 16 +- tests/test_constraints_module.py | 4 +- tests/test_outputlib/__init__.py | 14 +- tests/test_processing.py | 6 +- .../test_connect_invest.py | 4 +- .../test_add_constraints.py | 4 +- .../test_piecewiselineartransformer.py | 6 +- .../test_simple_model/test_simple_dispatch.py | 14 +- .../test_simple_dispatch_one.py | 6 +- .../test_simple_model/test_simple_invest.py | 14 +- .../test_invest_storage_regression.py | 4 +- .../test_storage_investment.py | 8 +- .../test_storage_with_tuple_label.py | 6 +- .../test_variable_chp/test_variable_chp.py | 2 +- tests/test_solph_network_classes.py | 10 +- tests/test_warnings.py | 10 +- 39 files changed, 805 insertions(+), 805 deletions(-) rename src/oemof/solph/components/{_transformer.py => _converter.py} (83%) rename src/oemof/solph/components/{_offset_transformer.py => _offset_converter.py} (83%) rename src/oemof/solph/components/experimental/{_piecewise_linear_transformer.py => _piecewise_linear_converter.py} (83%) rename tests/lp_files/{transformer.lp => converter.lp} (78%) rename tests/lp_files/{transformer_invest.lp => converter_invest.lp} (81%) rename tests/lp_files/{transformer_invest_with_existing.lp => converter_invest_with_existing.lp} (81%) rename tests/lp_files/{linear_transformer.lp => linear_converter.lp} (86%) rename tests/lp_files/{linear_transformer_chp.lp => linear_converter_chp.lp} (82%) rename tests/lp_files/{linear_transformer_chp_invest.lp => linear_converter_chp_invest.lp} (85%) rename tests/lp_files/{linear_transformer_invest.lp => linear_converter_invest.lp} (90%) rename tests/lp_files/{offsettransformer.lp => offset_converter.lp} (93%) create mode 100644 tests/lp_files/piecewise_linear_converter_cc.lp create mode 100644 tests/lp_files/piecewise_linear_converter_dcc.lp delete mode 100644 tests/lp_files/piecewise_linear_transformer_cc.lp delete mode 100644 tests/lp_files/piecewise_linear_transformer_dcc.lp diff --git a/src/oemof/solph/_console_scripts.py b/src/oemof/solph/_console_scripts.py index 74aea41d4..1611df225 100644 --- a/src/oemof/solph/_console_scripts.py +++ b/src/oemof/solph/_console_scripts.py @@ -34,7 +34,7 @@ def check_oemof_installation(silent=False): bel: solph.flows.Flow(fix=[10, 20, 30, 40, 50], nominal_value=1) }, ) - solph.components.Transformer( + solph.components.Converter( label="pp_gas", inputs={bgas: solph.flows.Flow()}, outputs={ diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 8ac022d5c..0534d28e3 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -21,7 +21,7 @@ from oemof.solph import processing from oemof.solph._plumbing import sequence from oemof.solph.buses._bus import BusBlock -from oemof.solph.components._transformer import TransformerBlock +from oemof.solph.components._converter import ConverterBlock from oemof.solph.flows._flow import FlowBlock from oemof.solph.flows._investment_flow import InvestmentFlowBlock from oemof.solph.flows._non_convex_flow import NonConvexFlowBlock @@ -281,7 +281,7 @@ class Model(BaseModel): CONSTRAINT_GROUPS = [ BusBlock, - TransformerBlock, + ConverterBlock, InvestmentFlowBlock, FlowBlock, NonConvexFlowBlock, diff --git a/src/oemof/solph/components/__init__.py b/src/oemof/solph/components/__init__.py index 0ccab7538..6a0e5206c 100644 --- a/src/oemof/solph/components/__init__.py +++ b/src/oemof/solph/components/__init__.py @@ -13,18 +13,18 @@ from ._extraction_turbine_chp import ExtractionTurbineCHP from ._generic_chp import GenericCHP from ._generic_storage import GenericStorage -from ._offset_transformer import OffsetTransformer +from ._offset_converter import OffsetConverter from ._sink import Sink from ._source import Source -from ._transformer import Transformer +from ._converter import Converter __all__ = [ + "Converter", "experimental", "ExtractionTurbineCHP", "GenericCHP", "GenericStorage", - "OffsetTransformer", + "OffsetConverter", "Sink", "Source", - "Transformer", ] diff --git a/src/oemof/solph/components/_transformer.py b/src/oemof/solph/components/_converter.py similarity index 83% rename from src/oemof/solph/components/_transformer.py rename to src/oemof/solph/components/_converter.py index 8c5e0d602..fc7cdfc07 100644 --- a/src/oemof/solph/components/_transformer.py +++ b/src/oemof/solph/components/_converter.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- """ -solph version of oemof.network.Transformer including +solph version of oemof.network.Converter including sets, variables, constraints and parts of the objective function -for TransformerBlock objects. +for ConverterBlock objects. SPDX-FileCopyrightText: Uwe Krien SPDX-FileCopyrightText: Simon Hilpert @@ -27,8 +27,8 @@ from oemof.solph._plumbing import sequence -class Transformer(on.Transformer): - """A linear TransformerBlock object with n inputs and n outputs. +class Converter(on.Transformer): + """A linear ConverterBlock object with n inputs and n outputs. Parameters ---------- @@ -40,7 +40,7 @@ class Transformer(on.Transformer): Examples -------- - Defining an linear transformer: + Defining an linear converter: >>> from oemof import solph >>> bgas = solph.buses.Bus(label='natural_gas') @@ -48,7 +48,7 @@ class Transformer(on.Transformer): >>> bel = solph.buses.Bus(label='electricity') >>> bheat = solph.buses.Bus(label='heat') - >>> trsf = solph.components.Transformer( + >>> trsf = solph.components.Converter( ... label='pp_gas_1', ... inputs={bgas: solph.flows.Flow(), bcoal: solph.flows.Flow()}, ... outputs={bel: solph.flows.Flow(), bheat: solph.flows.Flow()}, @@ -58,12 +58,12 @@ class Transformer(on.Transformer): [0.2, 0.3, 0.5, 0.8] >>> type(trsf) - + >>> sorted([str(i) for i in trsf.inputs]) ['hard_coal', 'natural_gas'] - >>> trsf_new = solph.components.Transformer( + >>> trsf_new = solph.components.Converter( ... label='pp_gas_2', ... inputs={bgas: solph.flows.Flow()}, ... outputs={bel: solph.flows.Flow(), bheat: solph.flows.Flow()}, @@ -74,7 +74,7 @@ class Transformer(on.Transformer): Notes ----- The following sets, variables, constraints and objective parts are created - * :py:class:`~oemof.solph.components._transformer.TransformerBlock` + * :py:class:`~oemof.solph.components._converter.ConverterBlock` """ def __init__(self, *args, **kwargs): @@ -96,38 +96,38 @@ def __init__(self, *args, **kwargs): self.conversion_factors[cf] = sequence(1) def constraint_group(self): - return TransformerBlock + return ConverterBlock -class TransformerBlock(SimpleBlock): +class ConverterBlock(SimpleBlock): r"""Block for the linear relation of nodes with type - :class:`~oemof.solph.components._transformer.TransformerBlock` + :class:`~oemof.solph.components._converter.ConverterBlock` **The following sets are created:** (-> see basic sets at :class:`.Model` ) - TRANSFORMERS + CONVERTERS A set with all - :class:`~oemof.solph.components._transformer.Transformer` objects. + :class:`~oemof.solph.components._converter.Converter` objects. **The following constraints are created:** - Linear relation :attr:`om.TransformerBlock.relation[i,o,t]` + Linear relation :attr:`om.ConverterBlock.relation[i,o,t]` .. math:: \P_{i,n}(t) \times \eta_{n,o}(t) = \ \P_{n,o}(t) \times \eta_{n,i}(t), \\ \forall t \in \textrm{TIMESTEPS}, \\ - \forall n \in \textrm{TRANSFORMERS}, \\ + \forall n \in \textrm{CONVERTERS}, \\ \forall i \in \textrm{INPUTS(n)}, \\ \forall o \in \textrm{OUTPUTS(n)}, ====================== ============================ ============= symbol attribute explanation ====================== ============================ ============= - :math:`P_{i,n}(t)` `flow[i, n, t]` TransformerBlock + :math:`P_{i,n}(t)` `flow[i, n, t]` ConverterBlock inflow - :math:`P_{n,o}(t)` `flow[n, o, t]` TransformerBlock + :math:`P_{n,o}(t)` `flow[n, o, t]` ConverterBlock outflow :math:`\eta_{i,n}(t)` `conversion_factor[i, n, t]` Conversion @@ -140,16 +140,16 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _create(self, group=None): - """Creates the linear constraint for the class:`TransformerBlock` + """Creates the linear constraint for the class:`ConverterBlock` block. Parameters ---------- group : list - List of oemof.solph.components.Transformers objects for which + List of oemof.solph.components.Converters objects for which the linear relation of inputs and outputs is created e.g. group = [trsf1, trsf2, trsf3, ...]. Note that the relation is created for all existing relations of all inputs and all outputs - of the transformer. The components inside the list need to hold + of the converter. The components inside the list need to hold an attribute `conversion_factors` of type dict containing the conversion factors for all inputs to outputs. """ diff --git a/src/oemof/solph/components/_extraction_turbine_chp.py b/src/oemof/solph/components/_extraction_turbine_chp.py index 433f90002..bbcc75e34 100644 --- a/src/oemof/solph/components/_extraction_turbine_chp.py +++ b/src/oemof/solph/components/_extraction_turbine_chp.py @@ -23,10 +23,10 @@ from pyomo.environ import Constraint from oemof.solph._plumbing import sequence as solph_sequence -from oemof.solph.components._transformer import Transformer +from oemof.solph.components._converter import Converter -class ExtractionTurbineCHP(Transformer): +class ExtractionTurbineCHP(Converter): r""" A CHP with an extraction turbine in a linear model. For more options see the :class:`~oemof.solph.components.GenericCHP` class. diff --git a/src/oemof/solph/components/_offset_transformer.py b/src/oemof/solph/components/_offset_converter.py similarity index 83% rename from src/oemof/solph/components/_offset_transformer.py rename to src/oemof/solph/components/_offset_converter.py index 6923ebde5..f2404d837 100644 --- a/src/oemof/solph/components/_offset_transformer.py +++ b/src/oemof/solph/components/_offset_converter.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 - """ -OffsetTransformer and associated individual constraints (blocks) and groupings. +OffsetConverter and associated individual constraints (blocks) and groupings. SPDX-FileCopyrightText: Uwe Krien SPDX-FileCopyrightText: Simon Hilpert @@ -25,7 +25,7 @@ from oemof.solph._plumbing import sequence as solph_sequence -class OffsetTransformer(network.Transformer): +class OffsetConverter(network.Transformer): """An object with one input and one output. Parameters @@ -40,7 +40,7 @@ class OffsetTransformer(network.Transformer): Notes ----- The sets, variables, constraints and objective parts are created - * :py:class:`~oemof.solph.components._offset_transformer.OffsetTransformerBlock` + * :py:class:`~oemof.solph.components._offset_converter.OffsetConverterBlock` Examples -------- @@ -50,7 +50,7 @@ class OffsetTransformer(network.Transformer): >>> bel = solph.buses.Bus(label='bel') >>> bth = solph.buses.Bus(label='bth') - >>> ostf = solph.components.OffsetTransformer( + >>> ostf = solph.components.OffsetConverter( ... label='ostf', ... inputs={bel: solph.flows.Flow( ... nominal_value=60, min=0.5, max=1.0, @@ -59,7 +59,7 @@ class OffsetTransformer(network.Transformer): ... coefficients=(20, 0.5)) >>> type(ostf) - + """ # noqa: E501 def __init__(self, *args, **kwargs): @@ -83,21 +83,21 @@ def __init__(self, *args, **kwargs): if len(self.inputs) > 1 or len(self.outputs) > 1: raise ValueError( - "Component `OffsetTransformer` must not have " + "Component `OffsetConverter` must not have " + "more than 1 input and 1 output!" ) def constraint_group(self): - return OffsetTransformerBlock + return OffsetConverterBlock -class OffsetTransformerBlock(SimpleBlock): +class OffsetConverterBlock(SimpleBlock): r"""Block for the relation of nodes with type - :class:`~oemof.solph.components._offset_transformer.OffsetTransformer` + :class:`~oemof.solph.components._offset_converter.OffsetConverter` **The following constraints are created:** - .. _OffsetTransformer-equations: + .. _OffsetConverter-equations: .. math:: & @@ -126,12 +126,12 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _create(self, group=None): - """Creates the relation for the class:`OffsetTransformer`. + """Creates the relation for the class:`OffsetConverter`. Parameters ---------- group : list - List of oemof.solph.experimental.OffsetTransformer objects for + List of oemof.solph.experimental.OffsetConverter objects for which the relation of inputs and outputs is created e.g. group = [ostf1, ostf2, ostf3, ...]. The components inside the list need to hold an attribute `coefficients` of type dict @@ -142,7 +142,7 @@ def _create(self, group=None): m = self.parent_block() - self.OFFSETTRANSFORMERS = Set(initialize=[n for n in group]) + self.OFFSETCONVERTERS = Set(initialize=[n for n in group]) def _relation_rule(block, n, t): """Link binary input and output flow to component outflow.""" @@ -158,5 +158,5 @@ def _relation_rule(block, n, t): return expr == 0 self.relation = Constraint( - self.OFFSETTRANSFORMERS, m.TIMESTEPS, rule=_relation_rule + self.OFFSETCONVERTERS, m.TIMESTEPS, rule=_relation_rule ) diff --git a/src/oemof/solph/components/experimental/__init__.py b/src/oemof/solph/components/experimental/__init__.py index 64938d80e..4ac7c944b 100644 --- a/src/oemof/solph/components/experimental/__init__.py +++ b/src/oemof/solph/components/experimental/__init__.py @@ -9,12 +9,12 @@ from ._generic_caes import GenericCAES from ._link import Link -from ._piecewise_linear_transformer import PiecewiseLinearTransformer +from ._piecewise_linear_converter import PiecewiseLinearConverter from ._sink_dsm import SinkDSM __all__ = [ "GenericCAES", "Link", - "PiecewiseLinearTransformer", + "PiecewiseLinearConverter", "SinkDSM", ] diff --git a/src/oemof/solph/components/experimental/_piecewise_linear_transformer.py b/src/oemof/solph/components/experimental/_piecewise_linear_converter.py similarity index 83% rename from src/oemof/solph/components/experimental/_piecewise_linear_transformer.py rename to src/oemof/solph/components/experimental/_piecewise_linear_converter.py index bc72c4aa6..80d1c2603 100644 --- a/src/oemof/solph/components/experimental/_piecewise_linear_transformer.py +++ b/src/oemof/solph/components/experimental/_piecewise_linear_converter.py @@ -25,8 +25,8 @@ from pyomo.environ import Var -class PiecewiseLinearTransformer(on.Transformer): - """Component to model a transformer with one input and one output +class PiecewiseLinearConverter(on.Transformer): + """Component to model an energy converter with one input and one output and an arbitrary piecewise linear conversion function. Parameters @@ -50,7 +50,7 @@ class PiecewiseLinearTransformer(on.Transformer): >>> b_gas = solph.buses.Bus(label='biogas') >>> b_el = solph.buses.Bus(label='electricity') - >>> pwltf = solph.components.experimental.PiecewiseLinearTransformer( + >>> pwltf = solph.components.experimental.PiecewiseLinearConverter( ... label='pwltf', ... inputs={b_gas: solph.flows.Flow( ... nominal_value=100, @@ -61,8 +61,8 @@ class PiecewiseLinearTransformer(on.Transformer): ... pw_repn='CC') >>> type(pwltf) - + """ def __init__(self, *args, **kwargs): @@ -74,7 +74,7 @@ def __init__(self, *args, **kwargs): if len(self.inputs) > 1 or len(self.outputs) > 1: raise ValueError( - "Component `PiecewiseLinearTransformer` cannot have " + "Component `PiecewiseLinearConverter` cannot have " + "more than 1 input and 1 output!" ) @@ -86,12 +86,12 @@ def __init__(self, *args, **kwargs): ) def constraint_group(self): - return PiecewiseLinearTransformerBlock + return PiecewiseLinearConverterBlock -class PiecewiseLinearTransformerBlock(SimpleBlock): +class PiecewiseLinearConverterBlock(SimpleBlock): r"""Block for the relation of nodes with type - :class:`~oemof.solph.components.experimental._piecewise_linear_transformer.PiecewiseLinearTransformer` + :class:`~oemof.solph.components.experimental._piecewise_linear_converter.PiecewiseLinearConverter` **The following constraints are created:** @@ -102,13 +102,13 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _create(self, group=None): - """Creates the relation for the class:`PiecewiseLinearTransformer`. + """Creates the relation for the class:`PiecewiseLinearConverter`. Parameters ---------- group : list List of - oemof.solph.components.experimental.PiecewiseLinearTransformer + oemof.solph.components.experimental.PiecewiseLinearConverter objects for which the relation of inputs and outputs is created e.g. group = [pwltf1, pwltf2, pwltf3, ...]. @@ -118,7 +118,7 @@ def _create(self, group=None): m = self.parent_block() - self.PWLINEARTRANSFORMERS = Set(initialize=[n for n in group]) + self.PWLINEARCONVERTERS = Set(initialize=[n for n in group]) pw_repns = [n.pw_repn for n in group] if all(x == pw_repns[0] for x in pw_repns): @@ -136,7 +136,7 @@ def build_breakpoints(block, n): self.breakpoints[(n, t)] = n.in_breakpoints self.breakpoint_build = BuildAction( - self.PWLINEARTRANSFORMERS, rule=build_breakpoints + self.PWLINEARCONVERTERS, rule=build_breakpoints ) def _conversion_function(block, n, t, x): @@ -162,10 +162,10 @@ def get_outflow_bounds(model, n, t): return lower_bound_out[n], upper_bound_out[n] self.inflow = Var( - self.PWLINEARTRANSFORMERS, m.TIMESTEPS, bounds=get_inflow_bounds + self.PWLINEARCONVERTERS, m.TIMESTEPS, bounds=get_inflow_bounds ) self.outflow = Var( - self.PWLINEARTRANSFORMERS, m.TIMESTEPS, bounds=get_outflow_bounds + self.PWLINEARCONVERTERS, m.TIMESTEPS, bounds=get_outflow_bounds ) def _in_equation(block, n, t): @@ -176,7 +176,7 @@ def _in_equation(block, n, t): return expr == 0 self.equate_in = Constraint( - self.PWLINEARTRANSFORMERS, m.TIMESTEPS, rule=_in_equation + self.PWLINEARCONVERTERS, m.TIMESTEPS, rule=_in_equation ) def _out_equation(block, n, t): @@ -187,11 +187,11 @@ def _out_equation(block, n, t): return expr == 0 self.equate_out = Constraint( - self.PWLINEARTRANSFORMERS, m.TIMESTEPS, rule=_out_equation + self.PWLINEARCONVERTERS, m.TIMESTEPS, rule=_out_equation ) self.piecewise = Piecewise( - self.PWLINEARTRANSFORMERS, + self.PWLINEARCONVERTERS, m.TIMESTEPS, self.outflow, self.inflow, diff --git a/src/oemof/solph/constraints/equate_variables.py b/src/oemof/solph/constraints/equate_variables.py index 3f06d505b..c587aeb0a 100644 --- a/src/oemof/solph/constraints/equate_variables.py +++ b/src/oemof/solph/constraints/equate_variables.py @@ -51,12 +51,12 @@ def equate_variables(model, var1, var2, factor1=1, name=None): >>> bel1 = solph.buses.Bus(label='electricity1') >>> bel2 = solph.buses.Bus(label='electricity2') >>> energysystem.add(bel1, bel2) - >>> energysystem.add(solph.components.Transformer( + >>> energysystem.add(solph.components.Converter( ... label='powerline_1_2', ... inputs={bel1: solph.flows.Flow()}, ... outputs={bel2: solph.flows.Flow( ... investment=solph.Investment(ep_costs=20))})) - >>> energysystem.add(solph.components.Transformer( + >>> energysystem.add(solph.components.Converter( ... label='powerline_2_1', ... inputs={bel2: solph.flows.Flow()}, ... outputs={bel1: solph.flows.Flow( diff --git a/src/oemof/solph/views.py b/src/oemof/solph/views.py index df6df4674..f9f05b638 100644 --- a/src/oemof/solph/views.py +++ b/src/oemof/solph/views.py @@ -150,9 +150,9 @@ def filter_nodes(results, option=NodeOption.All, exclude_busses=False): * :attr:`NodeOption.All`: `'all'`: Returns all nodes * :attr:`NodeOption.HasOutputs`: `'has_outputs'`: - Returns nodes with an output flow (eg. Transformer, Source) + Returns nodes with an output flow (eg. Converter, Source) * :attr:`NodeOption.HasInputs`: `'has_inputs'`: - Returns nodes with an input flow (eg. Transformer, Sink) + Returns nodes with an input flow (eg. Converter, Sink) * :attr:`NodeOption.HasOnlyOutputs`: `'has_only_outputs'`: Returns nodes having only output flows (eg. Source) * :attr:`NodeOption.HasOnlyInputs`: `'has_only_inputs'`: @@ -313,7 +313,7 @@ def node_output_by_type(results, node_type, droplevel=None): # Then collect node weights views.node_output_by_type( m.results(), - node_type=solph.components.Transformer + node_type=solph.components.Converter ) """ if droplevel is None: diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index 53c97172b..0029c512e 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -141,12 +141,12 @@ def normalize_to_positive_results(lines): ) def test_linear_transformer(self): - """Constraint test of a Transformer without Investment.""" + """Constraint test of a Converter without Investment.""" bgas = solph.buses.Bus(label="gas") bel = solph.buses.Bus(label="electricity") - solph.components.Transformer( + solph.components.Converter( label="powerplantGas", inputs={bgas: solph.flows.Flow()}, outputs={ @@ -155,16 +155,16 @@ def test_linear_transformer(self): conversion_factors={bel: 0.58}, ) - self.compare_lp_files("linear_transformer.lp") + self.compare_lp_files("linear_converter.lp") def test_linear_transformer_invest(self): - """Constraint test of a Transformer with Investment.""" + """Constraint test of a Converter with Investment.""" bgas = solph.buses.Bus(label="gas") bel = solph.buses.Bus(label="electricity") - solph.components.Transformer( + solph.components.Converter( label="powerplant_gas", inputs={bgas: solph.flows.Flow()}, outputs={ @@ -176,7 +176,7 @@ def test_linear_transformer_invest(self): conversion_factors={bel: 0.58}, ) - self.compare_lp_files("linear_transformer_invest.lp") + self.compare_lp_files("linear_converter_invest.lp") def test_max_source_min_sink(self): """ """ @@ -527,7 +527,7 @@ def test_transformer(self): bel = solph.buses.Bus(label="electricityBus") bth = solph.buses.Bus(label="thermalBus") - solph.components.Transformer( + solph.components.Converter( label="powerplantGasCoal", inputs={bbms: solph.flows.Flow(), bgas: solph.flows.Flow()}, outputs={ @@ -537,7 +537,7 @@ def test_transformer(self): conversion_factors={bgas: 0.4, bbms: 0.1, bel: 0.3, bth: 0.5}, ) - self.compare_lp_files("transformer.lp") + self.compare_lp_files("converter.lp") def test_transformer_invest(self): """Constraint test of a LinearN1Transformer with Investment.""" @@ -547,7 +547,7 @@ def test_transformer_invest(self): bel = solph.buses.Bus(label="electricityBus") bth = solph.buses.Bus(label="thermalBus") - solph.components.Transformer( + solph.components.Converter( label="powerplant_gas_coal", inputs={bgas: solph.flows.Flow(), bcoal: solph.flows.Flow()}, outputs={ @@ -560,7 +560,7 @@ def test_transformer_invest(self): conversion_factors={bgas: 0.58, bcoal: 0.2, bel: 0.3, bth: 0.5}, ) - self.compare_lp_files("transformer_invest.lp") + self.compare_lp_files("converter_invest.lp") def test_transformer_invest_with_existing(self): """Constraint test of a LinearN1Transformer with Investment.""" @@ -570,7 +570,7 @@ def test_transformer_invest_with_existing(self): bel = solph.buses.Bus(label="electricityBus") bth = solph.buses.Bus(label="thermalBus") - solph.components.Transformer( + solph.components.Converter( label="powerplant_gas_coal", inputs={bgas: solph.flows.Flow(), bcoal: solph.flows.Flow()}, outputs={ @@ -585,17 +585,17 @@ def test_transformer_invest_with_existing(self): conversion_factors={bgas: 0.58, bcoal: 0.2, bel: 0.3, bth: 0.5}, ) - self.compare_lp_files("transformer_invest_with_existing.lp") + self.compare_lp_files("converter_invest_with_existing.lp") def test_linear_transformer_chp(self): """ - Constraint test of a Transformer without Investment (two outputs). + Constraint test of a Converter without Investment (two outputs). """ bgas = solph.buses.Bus(label="gasBus") bheat = solph.buses.Bus(label="heatBus") bel = solph.buses.Bus(label="electricityBus") - solph.components.Transformer( + solph.components.Converter( label="CHPpowerplantGas", inputs={ bgas: solph.flows.Flow(nominal_value=10e10, variable_costs=50) @@ -604,16 +604,16 @@ def test_linear_transformer_chp(self): conversion_factors={bel: 0.4, bheat: 0.5}, ) - self.compare_lp_files("linear_transformer_chp.lp") + self.compare_lp_files("linear_converter_chp.lp") def test_linear_transformer_chp_invest(self): - """Constraint test of a Transformer with Investment (two outputs).""" + """Constraint test of a Converter with Investment (two outputs).""" bgas = solph.buses.Bus(label="gasBus") bheat = solph.buses.Bus(label="heatBus") bel = solph.buses.Bus(label="electricityBus") - solph.components.Transformer( + solph.components.Converter( label="chp_powerplant_gas", inputs={ bgas: solph.flows.Flow( @@ -625,7 +625,7 @@ def test_linear_transformer_chp_invest(self): conversion_factors={bel: 0.4, bheat: 0.5}, ) - self.compare_lp_files("linear_transformer_chp_invest.lp") + self.compare_lp_files("linear_converter_chp_invest.lp") def test_variable_chp(self): """ """ @@ -1033,10 +1033,10 @@ def test_inactivity_costs(self): self.compare_lp_files("inactivity_costs.lp") def test_piecewise_linear_transformer_cc(self): - """Testing PiecewiseLinearTransformer using CC formulation.""" + """Testing PiecewiseLinearConverter using CC formulation.""" bgas = solph.buses.Bus(label="gasBus") bel = solph.buses.Bus(label="electricityBus") - solph.components.experimental.PiecewiseLinearTransformer( + solph.components.experimental.PiecewiseLinearConverter( label="pwltf", inputs={ bgas: solph.flows.Flow(nominal_value=100, variable_costs=1) @@ -1046,13 +1046,13 @@ def test_piecewise_linear_transformer_cc(self): conversion_function=lambda x: x**2, pw_repn="CC", ) - self.compare_lp_files("piecewise_linear_transformer_cc.lp") + self.compare_lp_files("piecewise_linear_converter_cc.lp") def test_piecewise_linear_transformer_dcc(self): - """Testing PiecewiseLinearTransformer using DCC formulation.""" + """Testing PiecewiseLinearConverter using DCC formulation.""" bgas = solph.buses.Bus(label="gasBus") bel = solph.buses.Bus(label="electricityBus") - solph.components.experimental.PiecewiseLinearTransformer( + solph.components.experimental.PiecewiseLinearConverter( label="pwltf", inputs={ bgas: solph.flows.Flow(nominal_value=100, variable_costs=1) @@ -1062,7 +1062,7 @@ def test_piecewise_linear_transformer_dcc(self): conversion_function=lambda x: x**2, pw_repn="DCC", ) - self.compare_lp_files("piecewise_linear_transformer_dcc.lp") + self.compare_lp_files("piecewise_linear_converter_dcc.lp") def test_maximum_startups(self): """Testing maximum_startups attribute for nonconvex flows.""" @@ -1098,12 +1098,12 @@ def test_maximum_shutdowns(self): ) self.compare_lp_files("maximum_shutdowns.lp") - def test_offsettransformer(self): - """Constraint test of a OffsetTransformer.""" + def test_offsetconverter(self): + """Constraint test of a OffsetConverter.""" bgas = solph.buses.Bus(label="gasBus") bth = solph.buses.Bus(label="thermalBus") - solph.components.OffsetTransformer( + solph.components.OffsetConverter( label="gasboiler", inputs={ bgas: solph.flows.Flow( @@ -1116,7 +1116,7 @@ def test_offsettransformer(self): coefficients=[-17, 0.9], ) - self.compare_lp_files("offsettransformer.lp") + self.compare_lp_files("offset_converter.lp") def test_dsm_module_DIW(self): """Constraint test of SinkDSM with approach=DLR""" diff --git a/tests/lp_files/transformer.lp b/tests/lp_files/converter.lp similarity index 78% rename from tests/lp_files/transformer.lp rename to tests/lp_files/converter.lp index 0c5d3d11b..f8eb1ef7e 100644 --- a/tests/lp_files/transformer.lp +++ b/tests/lp_files/converter.lp @@ -59,62 +59,62 @@ c_e_BusBlock_balance(thermalBus_2)_: +1 flow(powerplantGasCoal_thermalBus_2) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_biomassBus_electricityBus_0)_: +c_e_ConverterBlock_relation(powerplantGasCoal_biomassBus_electricityBus_0)_: +0.29999999999999999 flow(biomassBus_powerplantGasCoal_0) -0.10000000000000001 flow(powerplantGasCoal_electricityBus_0) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_biomassBus_electricityBus_1)_: +c_e_ConverterBlock_relation(powerplantGasCoal_biomassBus_electricityBus_1)_: +0.29999999999999999 flow(biomassBus_powerplantGasCoal_1) -0.10000000000000001 flow(powerplantGasCoal_electricityBus_1) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_biomassBus_electricityBus_2)_: +c_e_ConverterBlock_relation(powerplantGasCoal_biomassBus_electricityBus_2)_: +0.29999999999999999 flow(biomassBus_powerplantGasCoal_2) -0.10000000000000001 flow(powerplantGasCoal_electricityBus_2) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_biomassBus_thermalBus_0)_: +c_e_ConverterBlock_relation(powerplantGasCoal_biomassBus_thermalBus_0)_: +0.5 flow(biomassBus_powerplantGasCoal_0) -0.10000000000000001 flow(powerplantGasCoal_thermalBus_0) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_biomassBus_thermalBus_1)_: +c_e_ConverterBlock_relation(powerplantGasCoal_biomassBus_thermalBus_1)_: +0.5 flow(biomassBus_powerplantGasCoal_1) -0.10000000000000001 flow(powerplantGasCoal_thermalBus_1) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_biomassBus_thermalBus_2)_: +c_e_ConverterBlock_relation(powerplantGasCoal_biomassBus_thermalBus_2)_: +0.5 flow(biomassBus_powerplantGasCoal_2) -0.10000000000000001 flow(powerplantGasCoal_thermalBus_2) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_gasBus_electricityBus_0)_: +c_e_ConverterBlock_relation(powerplantGasCoal_gasBus_electricityBus_0)_: +0.29999999999999999 flow(gasBus_powerplantGasCoal_0) -0.40000000000000002 flow(powerplantGasCoal_electricityBus_0) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_gasBus_electricityBus_1)_: +c_e_ConverterBlock_relation(powerplantGasCoal_gasBus_electricityBus_1)_: +0.29999999999999999 flow(gasBus_powerplantGasCoal_1) -0.40000000000000002 flow(powerplantGasCoal_electricityBus_1) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_gasBus_electricityBus_2)_: +c_e_ConverterBlock_relation(powerplantGasCoal_gasBus_electricityBus_2)_: +0.29999999999999999 flow(gasBus_powerplantGasCoal_2) -0.40000000000000002 flow(powerplantGasCoal_electricityBus_2) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_gasBus_thermalBus_0)_: +c_e_ConverterBlock_relation(powerplantGasCoal_gasBus_thermalBus_0)_: +0.5 flow(gasBus_powerplantGasCoal_0) -0.40000000000000002 flow(powerplantGasCoal_thermalBus_0) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_gasBus_thermalBus_1)_: +c_e_ConverterBlock_relation(powerplantGasCoal_gasBus_thermalBus_1)_: +0.5 flow(gasBus_powerplantGasCoal_1) -0.40000000000000002 flow(powerplantGasCoal_thermalBus_1) = 0 -c_e_TransformerBlock_relation(powerplantGasCoal_gasBus_thermalBus_2)_: +c_e_ConverterBlock_relation(powerplantGasCoal_gasBus_thermalBus_2)_: +0.5 flow(gasBus_powerplantGasCoal_2) -0.40000000000000002 flow(powerplantGasCoal_thermalBus_2) = 0 diff --git a/tests/lp_files/transformer_invest.lp b/tests/lp_files/converter_invest.lp similarity index 81% rename from tests/lp_files/transformer_invest.lp rename to tests/lp_files/converter_invest.lp index 25e4fe67d..9475085d5 100644 --- a/tests/lp_files/transformer_invest.lp +++ b/tests/lp_files/converter_invest.lp @@ -60,62 +60,62 @@ c_e_BusBlock_balance(thermalBus_2)_: +1 flow(powerplant_gas_coal_thermalBus_2) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_0)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_coalBus_electricityBus_0)_: +0.29999999999999999 flow(coalBus_powerplant_gas_coal_0) -0.20000000000000001 flow(powerplant_gas_coal_electricityBus_0) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_1)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_coalBus_electricityBus_1)_: +0.29999999999999999 flow(coalBus_powerplant_gas_coal_1) -0.20000000000000001 flow(powerplant_gas_coal_electricityBus_1) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_2)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_coalBus_electricityBus_2)_: +0.29999999999999999 flow(coalBus_powerplant_gas_coal_2) -0.20000000000000001 flow(powerplant_gas_coal_electricityBus_2) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_0)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_coalBus_thermalBus_0)_: +0.5 flow(coalBus_powerplant_gas_coal_0) -0.20000000000000001 flow(powerplant_gas_coal_thermalBus_0) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_1)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_coalBus_thermalBus_1)_: +0.5 flow(coalBus_powerplant_gas_coal_1) -0.20000000000000001 flow(powerplant_gas_coal_thermalBus_1) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_2)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_coalBus_thermalBus_2)_: +0.5 flow(coalBus_powerplant_gas_coal_2) -0.20000000000000001 flow(powerplant_gas_coal_thermalBus_2) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_0)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_gasBus_electricityBus_0)_: +0.29999999999999999 flow(gasBus_powerplant_gas_coal_0) -0.57999999999999996 flow(powerplant_gas_coal_electricityBus_0) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_1)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_gasBus_electricityBus_1)_: +0.29999999999999999 flow(gasBus_powerplant_gas_coal_1) -0.57999999999999996 flow(powerplant_gas_coal_electricityBus_1) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_2)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_gasBus_electricityBus_2)_: +0.29999999999999999 flow(gasBus_powerplant_gas_coal_2) -0.57999999999999996 flow(powerplant_gas_coal_electricityBus_2) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_0)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_gasBus_thermalBus_0)_: +0.5 flow(gasBus_powerplant_gas_coal_0) -0.57999999999999996 flow(powerplant_gas_coal_thermalBus_0) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_1)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_gasBus_thermalBus_1)_: +0.5 flow(gasBus_powerplant_gas_coal_1) -0.57999999999999996 flow(powerplant_gas_coal_thermalBus_1) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_2)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_gasBus_thermalBus_2)_: +0.5 flow(gasBus_powerplant_gas_coal_2) -0.57999999999999996 flow(powerplant_gas_coal_thermalBus_2) = 0 diff --git a/tests/lp_files/transformer_invest_with_existing.lp b/tests/lp_files/converter_invest_with_existing.lp similarity index 81% rename from tests/lp_files/transformer_invest_with_existing.lp rename to tests/lp_files/converter_invest_with_existing.lp index 3e2af2ebf..f7a184537 100644 --- a/tests/lp_files/transformer_invest_with_existing.lp +++ b/tests/lp_files/converter_invest_with_existing.lp @@ -60,62 +60,62 @@ c_e_BusBlock_balance(thermalBus_2)_: +1 flow(powerplant_gas_coal_thermalBus_2) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_0)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_coalBus_electricityBus_0)_: +0.29999999999999999 flow(coalBus_powerplant_gas_coal_0) -0.20000000000000001 flow(powerplant_gas_coal_electricityBus_0) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_1)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_coalBus_electricityBus_1)_: +0.29999999999999999 flow(coalBus_powerplant_gas_coal_1) -0.20000000000000001 flow(powerplant_gas_coal_electricityBus_1) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_2)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_coalBus_electricityBus_2)_: +0.29999999999999999 flow(coalBus_powerplant_gas_coal_2) -0.20000000000000001 flow(powerplant_gas_coal_electricityBus_2) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_0)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_coalBus_thermalBus_0)_: +0.5 flow(coalBus_powerplant_gas_coal_0) -0.20000000000000001 flow(powerplant_gas_coal_thermalBus_0) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_1)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_coalBus_thermalBus_1)_: +0.5 flow(coalBus_powerplant_gas_coal_1) -0.20000000000000001 flow(powerplant_gas_coal_thermalBus_1) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_2)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_coalBus_thermalBus_2)_: +0.5 flow(coalBus_powerplant_gas_coal_2) -0.20000000000000001 flow(powerplant_gas_coal_thermalBus_2) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_0)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_gasBus_electricityBus_0)_: +0.29999999999999999 flow(gasBus_powerplant_gas_coal_0) -0.57999999999999996 flow(powerplant_gas_coal_electricityBus_0) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_1)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_gasBus_electricityBus_1)_: +0.29999999999999999 flow(gasBus_powerplant_gas_coal_1) -0.57999999999999996 flow(powerplant_gas_coal_electricityBus_1) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_2)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_gasBus_electricityBus_2)_: +0.29999999999999999 flow(gasBus_powerplant_gas_coal_2) -0.57999999999999996 flow(powerplant_gas_coal_electricityBus_2) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_0)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_gasBus_thermalBus_0)_: +0.5 flow(gasBus_powerplant_gas_coal_0) -0.57999999999999996 flow(powerplant_gas_coal_thermalBus_0) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_1)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_gasBus_thermalBus_1)_: +0.5 flow(gasBus_powerplant_gas_coal_1) -0.57999999999999996 flow(powerplant_gas_coal_thermalBus_1) = 0 -c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_2)_: +c_e_ConverterBlock_relation(powerplant_gas_coal_gasBus_thermalBus_2)_: +0.5 flow(gasBus_powerplant_gas_coal_2) -0.57999999999999996 flow(powerplant_gas_coal_thermalBus_2) = 0 diff --git a/tests/lp_files/linear_transformer.lp b/tests/lp_files/linear_converter.lp similarity index 86% rename from tests/lp_files/linear_transformer.lp rename to tests/lp_files/linear_converter.lp index 53e15cc28..1441b0e4a 100644 --- a/tests/lp_files/linear_transformer.lp +++ b/tests/lp_files/linear_converter.lp @@ -32,17 +32,17 @@ c_e_BusBlock_balance(gas_2)_: +1 flow(gas_powerplantGas_2) = 0 -c_e_TransformerBlock_relation(powerplantGas_gas_electricity_0)_: +c_e_ConverterBlock_relation(powerplantGas_gas_electricity_0)_: +0.57999999999999996 flow(gas_powerplantGas_0) -1 flow(powerplantGas_electricity_0) = 0 -c_e_TransformerBlock_relation(powerplantGas_gas_electricity_1)_: +c_e_ConverterBlock_relation(powerplantGas_gas_electricity_1)_: +0.57999999999999996 flow(gas_powerplantGas_1) -1 flow(powerplantGas_electricity_1) = 0 -c_e_TransformerBlock_relation(powerplantGas_gas_electricity_2)_: +c_e_ConverterBlock_relation(powerplantGas_gas_electricity_2)_: +0.57999999999999996 flow(gas_powerplantGas_2) -1 flow(powerplantGas_electricity_2) = 0 diff --git a/tests/lp_files/linear_transformer_chp.lp b/tests/lp_files/linear_converter_chp.lp similarity index 82% rename from tests/lp_files/linear_transformer_chp.lp rename to tests/lp_files/linear_converter_chp.lp index 132868fca..11e7be878 100644 --- a/tests/lp_files/linear_transformer_chp.lp +++ b/tests/lp_files/linear_converter_chp.lp @@ -44,32 +44,32 @@ c_e_BusBlock_balance(heatBus_2)_: +1 flow(CHPpowerplantGas_heatBus_2) = 0 -c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_electricityBus_0)_: +c_e_ConverterBlock_relation(CHPpowerplantGas_gasBus_electricityBus_0)_: -1 flow(CHPpowerplantGas_electricityBus_0) +0.40000000000000002 flow(gasBus_CHPpowerplantGas_0) = 0 -c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_electricityBus_1)_: +c_e_ConverterBlock_relation(CHPpowerplantGas_gasBus_electricityBus_1)_: -1 flow(CHPpowerplantGas_electricityBus_1) +0.40000000000000002 flow(gasBus_CHPpowerplantGas_1) = 0 -c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_electricityBus_2)_: +c_e_ConverterBlock_relation(CHPpowerplantGas_gasBus_electricityBus_2)_: -1 flow(CHPpowerplantGas_electricityBus_2) +0.40000000000000002 flow(gasBus_CHPpowerplantGas_2) = 0 -c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_heatBus_0)_: +c_e_ConverterBlock_relation(CHPpowerplantGas_gasBus_heatBus_0)_: -1 flow(CHPpowerplantGas_heatBus_0) +0.5 flow(gasBus_CHPpowerplantGas_0) = 0 -c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_heatBus_1)_: +c_e_ConverterBlock_relation(CHPpowerplantGas_gasBus_heatBus_1)_: -1 flow(CHPpowerplantGas_heatBus_1) +0.5 flow(gasBus_CHPpowerplantGas_1) = 0 -c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_heatBus_2)_: +c_e_ConverterBlock_relation(CHPpowerplantGas_gasBus_heatBus_2)_: -1 flow(CHPpowerplantGas_heatBus_2) +0.5 flow(gasBus_CHPpowerplantGas_2) = 0 diff --git a/tests/lp_files/linear_transformer_chp_invest.lp b/tests/lp_files/linear_converter_chp_invest.lp similarity index 85% rename from tests/lp_files/linear_transformer_chp_invest.lp rename to tests/lp_files/linear_converter_chp_invest.lp index 57f39b400..94df0b5f9 100644 --- a/tests/lp_files/linear_transformer_chp_invest.lp +++ b/tests/lp_files/linear_converter_chp_invest.lp @@ -45,32 +45,32 @@ c_e_BusBlock_balance(heatBus_2)_: +1 flow(chp_powerplant_gas_heatBus_2) = 0 -c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_electricityBus_0)_: +c_e_ConverterBlock_relation(chp_powerplant_gas_gasBus_electricityBus_0)_: -1 flow(chp_powerplant_gas_electricityBus_0) +0.40000000000000002 flow(gasBus_chp_powerplant_gas_0) = 0 -c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_electricityBus_1)_: +c_e_ConverterBlock_relation(chp_powerplant_gas_gasBus_electricityBus_1)_: -1 flow(chp_powerplant_gas_electricityBus_1) +0.40000000000000002 flow(gasBus_chp_powerplant_gas_1) = 0 -c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_electricityBus_2)_: +c_e_ConverterBlock_relation(chp_powerplant_gas_gasBus_electricityBus_2)_: -1 flow(chp_powerplant_gas_electricityBus_2) +0.40000000000000002 flow(gasBus_chp_powerplant_gas_2) = 0 -c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_heatBus_0)_: +c_e_ConverterBlock_relation(chp_powerplant_gas_gasBus_heatBus_0)_: -1 flow(chp_powerplant_gas_heatBus_0) +0.5 flow(gasBus_chp_powerplant_gas_0) = 0 -c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_heatBus_1)_: +c_e_ConverterBlock_relation(chp_powerplant_gas_gasBus_heatBus_1)_: -1 flow(chp_powerplant_gas_heatBus_1) +0.5 flow(gasBus_chp_powerplant_gas_1) = 0 -c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_heatBus_2)_: +c_e_ConverterBlock_relation(chp_powerplant_gas_gasBus_heatBus_2)_: -1 flow(chp_powerplant_gas_heatBus_2) +0.5 flow(gasBus_chp_powerplant_gas_2) = 0 diff --git a/tests/lp_files/linear_transformer_invest.lp b/tests/lp_files/linear_converter_invest.lp similarity index 90% rename from tests/lp_files/linear_transformer_invest.lp rename to tests/lp_files/linear_converter_invest.lp index 6b1f2b06f..f08d630d9 100644 --- a/tests/lp_files/linear_transformer_invest.lp +++ b/tests/lp_files/linear_converter_invest.lp @@ -33,17 +33,17 @@ c_e_BusBlock_balance(gas_2)_: +1 flow(gas_powerplant_gas_2) = 0 -c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_0)_: +c_e_ConverterBlock_relation(powerplant_gas_gas_electricity_0)_: +0.57999999999999996 flow(gas_powerplant_gas_0) -1 flow(powerplant_gas_electricity_0) = 0 -c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_1)_: +c_e_ConverterBlock_relation(powerplant_gas_gas_electricity_1)_: +0.57999999999999996 flow(gas_powerplant_gas_1) -1 flow(powerplant_gas_electricity_1) = 0 -c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_2)_: +c_e_ConverterBlock_relation(powerplant_gas_gas_electricity_2)_: +0.57999999999999996 flow(gas_powerplant_gas_2) -1 flow(powerplant_gas_electricity_2) = 0 diff --git a/tests/lp_files/offsettransformer.lp b/tests/lp_files/offset_converter.lp similarity index 93% rename from tests/lp_files/offsettransformer.lp rename to tests/lp_files/offset_converter.lp index 7a8f0b14c..d53b80b9b 100644 --- a/tests/lp_files/offsettransformer.lp +++ b/tests/lp_files/offset_converter.lp @@ -60,19 +60,19 @@ c_u_NonConvexFlowBlock_max(gasBus_gasboiler_2)_: +1 flow(gasBus_gasboiler_2) <= 0 -c_e_OffsetTransformerBlock_relation(gasboiler_0)_: +c_e_OffsetConverterBlock_relation(gasboiler_0)_: -17 NonConvexFlowBlock_status(gasBus_gasboiler_0) +0.90000000000000002 flow(gasBus_gasboiler_0) -1 flow(gasboiler_thermalBus_0) = 0 -c_e_OffsetTransformerBlock_relation(gasboiler_1)_: +c_e_OffsetConverterBlock_relation(gasboiler_1)_: -17 NonConvexFlowBlock_status(gasBus_gasboiler_1) +0.90000000000000002 flow(gasBus_gasboiler_1) -1 flow(gasboiler_thermalBus_1) = 0 -c_e_OffsetTransformerBlock_relation(gasboiler_2)_: +c_e_OffsetConverterBlock_relation(gasboiler_2)_: -17 NonConvexFlowBlock_status(gasBus_gasboiler_2) +0.90000000000000002 flow(gasBus_gasboiler_2) -1 flow(gasboiler_thermalBus_2) diff --git a/tests/lp_files/piecewise_linear_converter_cc.lp b/tests/lp_files/piecewise_linear_converter_cc.lp new file mode 100644 index 000000000..e7cb68ee0 --- /dev/null +++ b/tests/lp_files/piecewise_linear_converter_cc.lp @@ -0,0 +1,298 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++1 flow(gasBus_pwltf_0) ++1 flow(gasBus_pwltf_1) ++1 flow(gasBus_pwltf_2) + +s.t. + +c_e_BusBlock_balance(electricityBus_0)_: ++1 flow(pwltf_electricityBus_0) += 0 + +c_e_BusBlock_balance(electricityBus_1)_: ++1 flow(pwltf_electricityBus_1) += 0 + +c_e_BusBlock_balance(electricityBus_2)_: ++1 flow(pwltf_electricityBus_2) += 0 + +c_e_BusBlock_balance(gasBus_0)_: ++1 flow(gasBus_pwltf_0) += 0 + +c_e_BusBlock_balance(gasBus_1)_: ++1 flow(gasBus_pwltf_1) += 0 + +c_e_BusBlock_balance(gasBus_2)_: ++1 flow(gasBus_pwltf_2) += 0 + +c_e_PiecewiseLinearConverterBlock_equate_in(pwltf_0)_: ++1 PiecewiseLinearConverterBlock_inflow(pwltf_0) +-1 flow(gasBus_pwltf_0) += 0 + +c_e_PiecewiseLinearConverterBlock_equate_in(pwltf_1)_: ++1 PiecewiseLinearConverterBlock_inflow(pwltf_1) +-1 flow(gasBus_pwltf_1) += 0 + +c_e_PiecewiseLinearConverterBlock_equate_in(pwltf_2)_: ++1 PiecewiseLinearConverterBlock_inflow(pwltf_2) +-1 flow(gasBus_pwltf_2) += 0 + +c_e_PiecewiseLinearConverterBlock_equate_out(pwltf_0)_: ++1 PiecewiseLinearConverterBlock_outflow(pwltf_0) +-1 flow(pwltf_electricityBus_0) += 0 + +c_e_PiecewiseLinearConverterBlock_equate_out(pwltf_1)_: ++1 PiecewiseLinearConverterBlock_outflow(pwltf_1) +-1 flow(pwltf_electricityBus_1) += 0 + +c_e_PiecewiseLinearConverterBlock_equate_out(pwltf_2)_: ++1 PiecewiseLinearConverterBlock_outflow(pwltf_2) +-1 flow(pwltf_electricityBus_2) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_constraint1_: ++1 PiecewiseLinearConverterBlock_inflow(pwltf_0) +-25 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(2) +-50 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(3) +-75 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(4) +-100 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(5) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_constraint2_: ++1 PiecewiseLinearConverterBlock_outflow(pwltf_0) +-625 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(2) +-2500 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(3) +-5625 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(4) +-10000 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(5) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_constraint3_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(1) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(2) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(3) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(4) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(5) += 1 + +c_u_PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_constraint4(1)_: +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(1) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(1) +<= 0 + +c_u_PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_constraint4(2)_: +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(1) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(2) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(2) +<= 0 + +c_u_PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_constraint4(3)_: +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(2) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(3) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(3) +<= 0 + +c_u_PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_constraint4(4)_: +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(3) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(4) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(4) +<= 0 + +c_u_PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_constraint4(5)_: +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(4) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(5) +<= 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_constraint5_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(1) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(2) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(3) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(4) += 1 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_constraint1_: ++1 PiecewiseLinearConverterBlock_inflow(pwltf_1) +-25 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(2) +-50 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(3) +-75 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(4) +-100 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(5) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_constraint2_: ++1 PiecewiseLinearConverterBlock_outflow(pwltf_1) +-625 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(2) +-2500 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(3) +-5625 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(4) +-10000 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(5) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_constraint3_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(1) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(2) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(3) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(4) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(5) += 1 + +c_u_PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_constraint4(1)_: +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(1) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(1) +<= 0 + +c_u_PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_constraint4(2)_: +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(1) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(2) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(2) +<= 0 + +c_u_PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_constraint4(3)_: +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(2) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(3) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(3) +<= 0 + +c_u_PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_constraint4(4)_: +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(3) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(4) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(4) +<= 0 + +c_u_PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_constraint4(5)_: +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(4) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(5) +<= 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_constraint5_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(1) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(2) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(3) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(4) += 1 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_constraint1_: ++1 PiecewiseLinearConverterBlock_inflow(pwltf_2) +-25 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(2) +-50 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(3) +-75 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(4) +-100 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(5) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_constraint2_: ++1 PiecewiseLinearConverterBlock_outflow(pwltf_2) +-625 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(2) +-2500 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(3) +-5625 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(4) +-10000 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(5) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_constraint3_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(1) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(2) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(3) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(4) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(5) += 1 + +c_u_PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_constraint4(1)_: +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(1) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(1) +<= 0 + +c_u_PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_constraint4(2)_: +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(1) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(2) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(2) +<= 0 + +c_u_PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_constraint4(3)_: +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(2) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(3) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(3) +<= 0 + +c_u_PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_constraint4(4)_: +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(3) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(4) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(4) +<= 0 + +c_u_PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_constraint4(5)_: +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(4) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(5) +<= 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_constraint5_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(1) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(2) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(3) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(4) += 1 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(gasBus_pwltf_0) <= 100 + 0 <= flow(gasBus_pwltf_1) <= 100 + 0 <= flow(gasBus_pwltf_2) <= 100 + 0 <= flow(pwltf_electricityBus_0) <= +inf + 0 <= flow(pwltf_electricityBus_1) <= +inf + 0 <= flow(pwltf_electricityBus_2) <= +inf + 0 <= PiecewiseLinearConverterBlock_inflow(pwltf_0) <= 100 + 0 <= PiecewiseLinearConverterBlock_inflow(pwltf_1) <= 100 + 0 <= PiecewiseLinearConverterBlock_inflow(pwltf_2) <= 100 + 0 <= PiecewiseLinearConverterBlock_outflow(pwltf_0) <= 10000 + 0 <= PiecewiseLinearConverterBlock_outflow(pwltf_1) <= 10000 + 0 <= PiecewiseLinearConverterBlock_outflow(pwltf_2) <= 10000 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(1) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(2) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(3) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(4) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_lambda(5) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(1) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(2) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(3) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(4) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(1) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(2) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(3) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(4) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_lambda(5) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(1) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(2) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(3) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(4) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(1) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(2) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(3) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(4) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_lambda(5) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(1) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(2) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(3) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(4) <= 1 +binary + PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(1) + PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(2) + PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(3) + PiecewiseLinearConverterBlock_piecewise(pwltf_0)_CC_bin_y(4) + PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(1) + PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(2) + PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(3) + PiecewiseLinearConverterBlock_piecewise(pwltf_1)_CC_bin_y(4) + PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(1) + PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(2) + PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(3) + PiecewiseLinearConverterBlock_piecewise(pwltf_2)_CC_bin_y(4) +end diff --git a/tests/lp_files/piecewise_linear_converter_dcc.lp b/tests/lp_files/piecewise_linear_converter_dcc.lp new file mode 100644 index 000000000..6468d8cf5 --- /dev/null +++ b/tests/lp_files/piecewise_linear_converter_dcc.lp @@ -0,0 +1,289 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++1 flow(gasBus_pwltf_0) ++1 flow(gasBus_pwltf_1) ++1 flow(gasBus_pwltf_2) + +s.t. + +c_e_BusBlock_balance(electricityBus_0)_: ++1 flow(pwltf_electricityBus_0) += 0 + +c_e_BusBlock_balance(electricityBus_1)_: ++1 flow(pwltf_electricityBus_1) += 0 + +c_e_BusBlock_balance(electricityBus_2)_: ++1 flow(pwltf_electricityBus_2) += 0 + +c_e_BusBlock_balance(gasBus_0)_: ++1 flow(gasBus_pwltf_0) += 0 + +c_e_BusBlock_balance(gasBus_1)_: ++1 flow(gasBus_pwltf_1) += 0 + +c_e_BusBlock_balance(gasBus_2)_: ++1 flow(gasBus_pwltf_2) += 0 + +c_e_PiecewiseLinearConverterBlock_equate_in(pwltf_0)_: ++1 PiecewiseLinearConverterBlock_inflow(pwltf_0) +-1 flow(gasBus_pwltf_0) += 0 + +c_e_PiecewiseLinearConverterBlock_equate_in(pwltf_1)_: ++1 PiecewiseLinearConverterBlock_inflow(pwltf_1) +-1 flow(gasBus_pwltf_1) += 0 + +c_e_PiecewiseLinearConverterBlock_equate_in(pwltf_2)_: ++1 PiecewiseLinearConverterBlock_inflow(pwltf_2) +-1 flow(gasBus_pwltf_2) += 0 + +c_e_PiecewiseLinearConverterBlock_equate_out(pwltf_0)_: ++1 PiecewiseLinearConverterBlock_outflow(pwltf_0) +-1 flow(pwltf_electricityBus_0) += 0 + +c_e_PiecewiseLinearConverterBlock_equate_out(pwltf_1)_: ++1 PiecewiseLinearConverterBlock_outflow(pwltf_1) +-1 flow(pwltf_electricityBus_1) += 0 + +c_e_PiecewiseLinearConverterBlock_equate_out(pwltf_2)_: ++1 PiecewiseLinearConverterBlock_outflow(pwltf_2) +-1 flow(pwltf_electricityBus_2) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_constraint1_: ++1 PiecewiseLinearConverterBlock_inflow(pwltf_0) +-25 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(1_2) +-25 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(2_2) +-50 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(2_3) +-50 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(3_3) +-75 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(3_4) +-75 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(4_4) +-100 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_constraint2_: ++1 PiecewiseLinearConverterBlock_outflow(pwltf_0) +-625 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(1_2) +-625 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(2_2) +-2500 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(2_3) +-2500 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(3_3) +-5625 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(3_4) +-5625 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(4_4) +-10000 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_constraint3(1)_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_bin_y(1) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(1_1) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(1_2) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_constraint3(2)_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_bin_y(2) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(2_2) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(2_3) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_constraint3(3)_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_bin_y(3) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(3_3) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(3_4) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_constraint3(4)_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_bin_y(4) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(4_4) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_constraint4_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_bin_y(1) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_bin_y(2) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_bin_y(3) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_bin_y(4) += 1 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_constraint1_: ++1 PiecewiseLinearConverterBlock_inflow(pwltf_1) +-25 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(1_2) +-25 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(2_2) +-50 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(2_3) +-50 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(3_3) +-75 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(3_4) +-75 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(4_4) +-100 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_constraint2_: ++1 PiecewiseLinearConverterBlock_outflow(pwltf_1) +-625 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(1_2) +-625 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(2_2) +-2500 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(2_3) +-2500 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(3_3) +-5625 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(3_4) +-5625 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(4_4) +-10000 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_constraint3(1)_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_bin_y(1) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(1_1) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(1_2) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_constraint3(2)_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_bin_y(2) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(2_2) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(2_3) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_constraint3(3)_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_bin_y(3) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(3_3) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(3_4) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_constraint3(4)_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_bin_y(4) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(4_4) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_constraint4_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_bin_y(1) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_bin_y(2) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_bin_y(3) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_bin_y(4) += 1 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_constraint1_: ++1 PiecewiseLinearConverterBlock_inflow(pwltf_2) +-25 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(1_2) +-25 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(2_2) +-50 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(2_3) +-50 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(3_3) +-75 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(3_4) +-75 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(4_4) +-100 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_constraint2_: ++1 PiecewiseLinearConverterBlock_outflow(pwltf_2) +-625 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(1_2) +-625 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(2_2) +-2500 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(2_3) +-2500 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(3_3) +-5625 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(3_4) +-5625 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(4_4) +-10000 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_constraint3(1)_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_bin_y(1) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(1_1) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(1_2) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_constraint3(2)_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_bin_y(2) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(2_2) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(2_3) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_constraint3(3)_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_bin_y(3) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(3_3) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(3_4) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_constraint3(4)_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_bin_y(4) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(4_4) +-1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_constraint4_: ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_bin_y(1) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_bin_y(2) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_bin_y(3) ++1 PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_bin_y(4) += 1 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(gasBus_pwltf_0) <= 100 + 0 <= flow(gasBus_pwltf_1) <= 100 + 0 <= flow(gasBus_pwltf_2) <= 100 + 0 <= flow(pwltf_electricityBus_0) <= +inf + 0 <= flow(pwltf_electricityBus_1) <= +inf + 0 <= flow(pwltf_electricityBus_2) <= +inf + 0 <= PiecewiseLinearConverterBlock_inflow(pwltf_0) <= 100 + 0 <= PiecewiseLinearConverterBlock_inflow(pwltf_1) <= 100 + 0 <= PiecewiseLinearConverterBlock_inflow(pwltf_2) <= 100 + 0 <= PiecewiseLinearConverterBlock_outflow(pwltf_0) <= 10000 + 0 <= PiecewiseLinearConverterBlock_outflow(pwltf_1) <= 10000 + 0 <= PiecewiseLinearConverterBlock_outflow(pwltf_2) <= 10000 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(1_1) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(1_2) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(2_2) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(2_3) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(3_3) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(3_4) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(4_4) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_lambda(4_5) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_bin_y(1) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_bin_y(2) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_bin_y(3) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_bin_y(4) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(1_1) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(1_2) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(2_2) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(2_3) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(3_3) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(3_4) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(4_4) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_lambda(4_5) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_bin_y(1) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_bin_y(2) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_bin_y(3) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_bin_y(4) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(1_1) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(1_2) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(2_2) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(2_3) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(3_3) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(3_4) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(4_4) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_lambda(4_5) <= +inf + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_bin_y(1) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_bin_y(2) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_bin_y(3) <= 1 + 0 <= PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_bin_y(4) <= 1 +binary + PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_bin_y(1) + PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_bin_y(2) + PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_bin_y(3) + PiecewiseLinearConverterBlock_piecewise(pwltf_0)_DCC_bin_y(4) + PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_bin_y(1) + PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_bin_y(2) + PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_bin_y(3) + PiecewiseLinearConverterBlock_piecewise(pwltf_1)_DCC_bin_y(4) + PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_bin_y(1) + PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_bin_y(2) + PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_bin_y(3) + PiecewiseLinearConverterBlock_piecewise(pwltf_2)_DCC_bin_y(4) +end diff --git a/tests/lp_files/piecewise_linear_transformer_cc.lp b/tests/lp_files/piecewise_linear_transformer_cc.lp deleted file mode 100644 index a780e1e23..000000000 --- a/tests/lp_files/piecewise_linear_transformer_cc.lp +++ /dev/null @@ -1,298 +0,0 @@ -\* Source Pyomo model name=Model *\ - -min -objective: -+1 flow(gasBus_pwltf_0) -+1 flow(gasBus_pwltf_1) -+1 flow(gasBus_pwltf_2) - -s.t. - -c_e_BusBlock_balance(electricityBus_0)_: -+1 flow(pwltf_electricityBus_0) -= 0 - -c_e_BusBlock_balance(electricityBus_1)_: -+1 flow(pwltf_electricityBus_1) -= 0 - -c_e_BusBlock_balance(electricityBus_2)_: -+1 flow(pwltf_electricityBus_2) -= 0 - -c_e_BusBlock_balance(gasBus_0)_: -+1 flow(gasBus_pwltf_0) -= 0 - -c_e_BusBlock_balance(gasBus_1)_: -+1 flow(gasBus_pwltf_1) -= 0 - -c_e_BusBlock_balance(gasBus_2)_: -+1 flow(gasBus_pwltf_2) -= 0 - -c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_0)_: -+1 PiecewiseLinearTransformerBlock_inflow(pwltf_0) --1 flow(gasBus_pwltf_0) -= 0 - -c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_1)_: -+1 PiecewiseLinearTransformerBlock_inflow(pwltf_1) --1 flow(gasBus_pwltf_1) -= 0 - -c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_2)_: -+1 PiecewiseLinearTransformerBlock_inflow(pwltf_2) --1 flow(gasBus_pwltf_2) -= 0 - -c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_0)_: -+1 PiecewiseLinearTransformerBlock_outflow(pwltf_0) --1 flow(pwltf_electricityBus_0) -= 0 - -c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_1)_: -+1 PiecewiseLinearTransformerBlock_outflow(pwltf_1) --1 flow(pwltf_electricityBus_1) -= 0 - -c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_2)_: -+1 PiecewiseLinearTransformerBlock_outflow(pwltf_2) --1 flow(pwltf_electricityBus_2) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_constraint1_: -+1 PiecewiseLinearTransformerBlock_inflow(pwltf_0) --25 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(2) --50 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(3) --75 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(4) --100 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(5) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_constraint2_: -+1 PiecewiseLinearTransformerBlock_outflow(pwltf_0) --625 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(2) --2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(3) --5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(4) --10000 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(5) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_constraint3_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(1) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(2) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(3) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(4) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(5) -= 1 - -c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_constraint4(1)_: --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(1) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(1) -<= 0 - -c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_constraint4(2)_: --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(1) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(2) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(2) -<= 0 - -c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_constraint4(3)_: --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(2) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(3) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(3) -<= 0 - -c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_constraint4(4)_: --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(3) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(4) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(4) -<= 0 - -c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_constraint4(5)_: --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(4) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(5) -<= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_constraint5_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(1) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(2) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(3) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(4) -= 1 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_constraint1_: -+1 PiecewiseLinearTransformerBlock_inflow(pwltf_1) --25 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(2) --50 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(3) --75 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(4) --100 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(5) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_constraint2_: -+1 PiecewiseLinearTransformerBlock_outflow(pwltf_1) --625 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(2) --2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(3) --5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(4) --10000 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(5) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_constraint3_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(1) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(2) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(3) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(4) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(5) -= 1 - -c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_constraint4(1)_: --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(1) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(1) -<= 0 - -c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_constraint4(2)_: --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(1) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(2) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(2) -<= 0 - -c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_constraint4(3)_: --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(2) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(3) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(3) -<= 0 - -c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_constraint4(4)_: --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(3) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(4) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(4) -<= 0 - -c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_constraint4(5)_: --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(4) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(5) -<= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_constraint5_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(1) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(2) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(3) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(4) -= 1 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_constraint1_: -+1 PiecewiseLinearTransformerBlock_inflow(pwltf_2) --25 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(2) --50 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(3) --75 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(4) --100 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(5) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_constraint2_: -+1 PiecewiseLinearTransformerBlock_outflow(pwltf_2) --625 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(2) --2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(3) --5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(4) --10000 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(5) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_constraint3_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(1) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(2) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(3) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(4) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(5) -= 1 - -c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_constraint4(1)_: --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(1) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(1) -<= 0 - -c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_constraint4(2)_: --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(1) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(2) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(2) -<= 0 - -c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_constraint4(3)_: --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(2) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(3) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(3) -<= 0 - -c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_constraint4(4)_: --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(3) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(4) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(4) -<= 0 - -c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_constraint4(5)_: --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(4) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(5) -<= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_constraint5_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(1) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(2) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(3) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(4) -= 1 - -c_e_ONE_VAR_CONSTANT: -ONE_VAR_CONSTANT = 1.0 - -bounds - 0 <= flow(gasBus_pwltf_0) <= 100 - 0 <= flow(gasBus_pwltf_1) <= 100 - 0 <= flow(gasBus_pwltf_2) <= 100 - 0 <= flow(pwltf_electricityBus_0) <= +inf - 0 <= flow(pwltf_electricityBus_1) <= +inf - 0 <= flow(pwltf_electricityBus_2) <= +inf - 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_0) <= 100 - 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_1) <= 100 - 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_2) <= 100 - 0 <= PiecewiseLinearTransformerBlock_outflow(pwltf_0) <= 10000 - 0 <= PiecewiseLinearTransformerBlock_outflow(pwltf_1) <= 10000 - 0 <= PiecewiseLinearTransformerBlock_outflow(pwltf_2) <= 10000 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(1) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(2) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(3) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(4) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(5) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(1) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(2) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(3) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(4) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(1) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(2) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(3) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(4) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(5) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(1) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(2) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(3) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(4) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(1) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(2) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(3) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(4) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(5) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(1) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(2) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(3) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(4) <= 1 -binary - PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(1) - PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(2) - PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(3) - PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(4) - PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(1) - PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(2) - PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(3) - PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(4) - PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(1) - PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(2) - PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(3) - PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(4) -end diff --git a/tests/lp_files/piecewise_linear_transformer_dcc.lp b/tests/lp_files/piecewise_linear_transformer_dcc.lp deleted file mode 100644 index 600d617b2..000000000 --- a/tests/lp_files/piecewise_linear_transformer_dcc.lp +++ /dev/null @@ -1,289 +0,0 @@ -\* Source Pyomo model name=Model *\ - -min -objective: -+1 flow(gasBus_pwltf_0) -+1 flow(gasBus_pwltf_1) -+1 flow(gasBus_pwltf_2) - -s.t. - -c_e_BusBlock_balance(electricityBus_0)_: -+1 flow(pwltf_electricityBus_0) -= 0 - -c_e_BusBlock_balance(electricityBus_1)_: -+1 flow(pwltf_electricityBus_1) -= 0 - -c_e_BusBlock_balance(electricityBus_2)_: -+1 flow(pwltf_electricityBus_2) -= 0 - -c_e_BusBlock_balance(gasBus_0)_: -+1 flow(gasBus_pwltf_0) -= 0 - -c_e_BusBlock_balance(gasBus_1)_: -+1 flow(gasBus_pwltf_1) -= 0 - -c_e_BusBlock_balance(gasBus_2)_: -+1 flow(gasBus_pwltf_2) -= 0 - -c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_0)_: -+1 PiecewiseLinearTransformerBlock_inflow(pwltf_0) --1 flow(gasBus_pwltf_0) -= 0 - -c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_1)_: -+1 PiecewiseLinearTransformerBlock_inflow(pwltf_1) --1 flow(gasBus_pwltf_1) -= 0 - -c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_2)_: -+1 PiecewiseLinearTransformerBlock_inflow(pwltf_2) --1 flow(gasBus_pwltf_2) -= 0 - -c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_0)_: -+1 PiecewiseLinearTransformerBlock_outflow(pwltf_0) --1 flow(pwltf_electricityBus_0) -= 0 - -c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_1)_: -+1 PiecewiseLinearTransformerBlock_outflow(pwltf_1) --1 flow(pwltf_electricityBus_1) -= 0 - -c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_2)_: -+1 PiecewiseLinearTransformerBlock_outflow(pwltf_2) --1 flow(pwltf_electricityBus_2) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_constraint1_: -+1 PiecewiseLinearTransformerBlock_inflow(pwltf_0) --25 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(1_2) --25 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(2_2) --50 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(2_3) --50 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(3_3) --75 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(3_4) --75 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(4_4) --100 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(4_5) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_constraint2_: -+1 PiecewiseLinearTransformerBlock_outflow(pwltf_0) --625 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(1_2) --625 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(2_2) --2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(2_3) --2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(3_3) --5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(3_4) --5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(4_4) --10000 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(4_5) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_constraint3(1)_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(1) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(1_1) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(1_2) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_constraint3(2)_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(2) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(2_2) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(2_3) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_constraint3(3)_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(3) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(3_3) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(3_4) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_constraint3(4)_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(4) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(4_4) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(4_5) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_constraint4_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(1) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(2) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(3) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(4) -= 1 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_constraint1_: -+1 PiecewiseLinearTransformerBlock_inflow(pwltf_1) --25 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(1_2) --25 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(2_2) --50 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(2_3) --50 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(3_3) --75 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(3_4) --75 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(4_4) --100 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(4_5) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_constraint2_: -+1 PiecewiseLinearTransformerBlock_outflow(pwltf_1) --625 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(1_2) --625 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(2_2) --2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(2_3) --2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(3_3) --5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(3_4) --5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(4_4) --10000 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(4_5) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_constraint3(1)_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(1) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(1_1) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(1_2) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_constraint3(2)_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(2) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(2_2) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(2_3) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_constraint3(3)_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(3) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(3_3) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(3_4) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_constraint3(4)_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(4) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(4_4) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(4_5) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_constraint4_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(1) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(2) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(3) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(4) -= 1 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_constraint1_: -+1 PiecewiseLinearTransformerBlock_inflow(pwltf_2) --25 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(1_2) --25 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(2_2) --50 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(2_3) --50 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(3_3) --75 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(3_4) --75 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(4_4) --100 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(4_5) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_constraint2_: -+1 PiecewiseLinearTransformerBlock_outflow(pwltf_2) --625 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(1_2) --625 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(2_2) --2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(2_3) --2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(3_3) --5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(3_4) --5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(4_4) --10000 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(4_5) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_constraint3(1)_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(1) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(1_1) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(1_2) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_constraint3(2)_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(2) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(2_2) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(2_3) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_constraint3(3)_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(3) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(3_3) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(3_4) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_constraint3(4)_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(4) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(4_4) --1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(4_5) -= 0 - -c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_constraint4_: -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(1) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(2) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(3) -+1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(4) -= 1 - -c_e_ONE_VAR_CONSTANT: -ONE_VAR_CONSTANT = 1.0 - -bounds - 0 <= flow(gasBus_pwltf_0) <= 100 - 0 <= flow(gasBus_pwltf_1) <= 100 - 0 <= flow(gasBus_pwltf_2) <= 100 - 0 <= flow(pwltf_electricityBus_0) <= +inf - 0 <= flow(pwltf_electricityBus_1) <= +inf - 0 <= flow(pwltf_electricityBus_2) <= +inf - 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_0) <= 100 - 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_1) <= 100 - 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_2) <= 100 - 0 <= PiecewiseLinearTransformerBlock_outflow(pwltf_0) <= 10000 - 0 <= PiecewiseLinearTransformerBlock_outflow(pwltf_1) <= 10000 - 0 <= PiecewiseLinearTransformerBlock_outflow(pwltf_2) <= 10000 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(1_1) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(1_2) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(2_2) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(2_3) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(3_3) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(3_4) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(4_4) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(4_5) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(1) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(2) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(3) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(4) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(1_1) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(1_2) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(2_2) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(2_3) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(3_3) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(3_4) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(4_4) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(4_5) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(1) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(2) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(3) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(4) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(1_1) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(1_2) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(2_2) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(2_3) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(3_3) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(3_4) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(4_4) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(4_5) <= +inf - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(1) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(2) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(3) <= 1 - 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(4) <= 1 -binary - PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(1) - PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(2) - PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(3) - PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(4) - PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(1) - PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(2) - PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(3) - PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(4) - PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(1) - PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(2) - PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(3) - PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(4) -end diff --git a/tests/test_components.py b/tests/test_components.py index d3a66c353..aabfe99ab 100644 --- a/tests/test_components.py +++ b/tests/test_components.py @@ -230,7 +230,7 @@ def test_offsettransformer_wrong_flow_type(): TypeError, match=r"Input flows must be of type NonConvexFlow!" ): bgas = Bus(label="gasBus") - components.OffsetTransformer( + components.OffsetConverter( label="gasboiler", inputs={bgas: Flow()}, coefficients=(-17, 0.9) ) @@ -240,7 +240,7 @@ def test_offsettransformer_not_enough_coefficients(): ValueError, match=r"Two coefficients or coefficient series have to be given.", ): - components.OffsetTransformer(label="of1", coefficients=([1, 4, 7])) + components.OffsetConverter(label="of1", coefficients=([1, 4, 7])) def test_offsettransformer_too_many_coefficients(): @@ -248,22 +248,22 @@ def test_offsettransformer_too_many_coefficients(): ValueError, match=r"Two coefficients or coefficient series have to be given.", ): - components.OffsetTransformer(label="of2", coefficients=(1, 4, 7)) + components.OffsetConverter(label="of2", coefficients=(1, 4, 7)) def test_offsettransformer_empty(): """No NonConvexFlow for Inflow defined.""" - components.OffsetTransformer() + components.OffsetConverter() def test_offsettransformer__too_many_input_flows(): """Too many Input Flows defined.""" with pytest.raises( - ValueError, match=r"OffsetTransformer` must not have more than 1" + ValueError, match=r"OffsetConverter` must not have more than 1" ): bgas = Bus(label="GasBus") bcoal = Bus(label="CoalBus") - components.OffsetTransformer( + components.OffsetConverter( label="ostf_2_in", inputs={ bgas: NonConvexFlow(nominal_value=60, min=0.5, max=1.0), @@ -276,12 +276,12 @@ def test_offsettransformer__too_many_input_flows(): def test_offsettransformer_too_many_output_flows(): """Too many Output Flows defined.""" with pytest.raises( - ValueError, match="OffsetTransformer` must not have more than 1" + ValueError, match="OffsetConverter` must not have more than 1" ): bm1 = Bus(label="my_offset_Bus1") bm2 = Bus(label="my_offset_Bus2") - components.OffsetTransformer( + components.OffsetConverter( label="ostf_2_out", inputs={bm1: NonConvexFlow(nominal_value=60, min=0.5, max=1.0)}, outputs={bm1: Flow(), bm2: Flow()}, diff --git a/tests/test_constraints_module.py b/tests/test_constraints_module.py index b1bca0249..49c3d6c2b 100644 --- a/tests/test_constraints_module.py +++ b/tests/test_constraints_module.py @@ -28,7 +28,7 @@ def test_something_else(): bel2 = solph.buses.Bus(label="electricity2") energysystem.add(bel1, bel2) energysystem.add( - solph.components.Transformer( + solph.components.Converter( label="powerline_1_2", inputs={bel1: solph.flows.Flow()}, outputs={ @@ -39,7 +39,7 @@ def test_something_else(): ) ) energysystem.add( - solph.components.Transformer( + solph.components.Converter( label="powerline_2_1", inputs={bel2: solph.flows.Flow()}, outputs={ diff --git a/tests/test_outputlib/__init__.py b/tests/test_outputlib/__init__.py index cb814b8fd..0102c5ec5 100644 --- a/tests/test_outputlib/__init__.py +++ b/tests/test_outputlib/__init__.py @@ -7,7 +7,7 @@ from oemof.solph.buses import Bus from oemof.solph.components import Sink from oemof.solph.components import Source -from oemof.solph.components import Transformer +from oemof.solph.components import Converter from oemof.solph.flows import Flow filename = os.path.join(os.path.dirname(__file__), "input_data.csv") @@ -70,28 +70,28 @@ ) # power plants -pp_coal = Transformer( +pp_coal = Converter( label="pp_coal", inputs={bcoal: Flow()}, outputs={bel: Flow(nominal_value=20.2, variable_costs=25)}, conversion_factors={bel: 0.39}, ) -pp_lig = Transformer( +pp_lig = Converter( label="pp_lig", inputs={blig: Flow()}, outputs={bel: Flow(nominal_value=11.8, variable_costs=19)}, conversion_factors={bel: 0.41}, ) -pp_gas = Transformer( +pp_gas = Converter( label="pp_gas", inputs={bgas: Flow()}, outputs={bel: Flow(nominal_value=41, variable_costs=40)}, conversion_factors={bel: 0.50}, ) -pp_oil = Transformer( +pp_oil = Converter( label="pp_oil", inputs={boil: Flow()}, outputs={bel: Flow(nominal_value=5, variable_costs=50)}, @@ -99,7 +99,7 @@ ) # combined heat and power plant (chp) -pp_chp = Transformer( +pp_chp = Converter( label="pp_chp", inputs={bgas: Flow()}, outputs={ @@ -115,7 +115,7 @@ heat_source = Source(label="heat_source", outputs={b_heat_source: Flow()}) cop = 3 -heat_pump = Transformer( +heat_pump = Converter( label="heat_pump", inputs={bel: Flow(), b_heat_source: Flow()}, outputs={bth: Flow(nominal_value=10)}, diff --git a/tests/test_processing.py b/tests/test_processing.py index 3d52557eb..61e943075 100644 --- a/tests/test_processing.py +++ b/tests/test_processing.py @@ -24,7 +24,7 @@ from oemof.solph.buses import Bus from oemof.solph.components import GenericStorage from oemof.solph.components import Sink -from oemof.solph.components import Transformer +from oemof.solph.components import Converter from oemof.solph.flows import Flow @@ -45,7 +45,7 @@ def setup_class(cls): cls.es.add(b_el1, b_el2, b_diesel) # TEST DIESEL: - dg = Transformer( + dg = Converter( label="diesel", inputs={b_diesel: Flow(variable_costs=2)}, outputs={ @@ -275,7 +275,7 @@ def test_node_weight_by_type(self): def test_output_by_type_view(self): results = processing.results(self.om) transformer_output = views.node_output_by_type( - results, node_type=Transformer + results, node_type=Converter ) compare = views.node(results, "diesel", multiindex=True)["sequences"][ ("diesel", "b_el1", "flow") diff --git a/tests/test_scripts/test_solph/test_connect_invest/test_connect_invest.py b/tests/test_scripts/test_solph/test_connect_invest/test_connect_invest.py index 6d830b9d2..b1b1a3520 100644 --- a/tests/test_scripts/test_solph/test_connect_invest/test_connect_invest.py +++ b/tests/test_scripts/test_solph/test_connect_invest/test_connect_invest.py @@ -77,13 +77,13 @@ def test_connect_invest(): investment=Investment(ep_costs=0.2), ) - line12 = components.Transformer( + line12 = components.Converter( label="line12", inputs={bel1: Flow()}, outputs={bel2: Flow(investment=Investment(ep_costs=20))}, ) - line21 = components.Transformer( + line21 = components.Converter( label="line21", inputs={bel2: Flow()}, outputs={bel1: Flow(investment=Investment(ep_costs=20))}, diff --git a/tests/test_scripts/test_solph/test_flexible_modelling/test_add_constraints.py b/tests/test_scripts/test_solph/test_flexible_modelling/test_add_constraints.py index 09d991f20..84553e35f 100644 --- a/tests/test_scripts/test_solph/test_flexible_modelling/test_add_constraints.py +++ b/tests/test_scripts/test_solph/test_flexible_modelling/test_add_constraints.py @@ -44,13 +44,13 @@ def test_add_constraints_example(solver="cbc", nologg=False): label="Sink", inputs={b_el: Flow(nominal_value=40, fix=[0.5, 0.4, 0.3, 1])}, ) - pp_oil = components.Transformer( + pp_oil = components.Converter( label="pp_oil", inputs={boil: Flow()}, outputs={b_el: Flow(nominal_value=50, variable_costs=25)}, conversion_factors={b_el: 0.39}, ) - components.Transformer( + components.Converter( label="pp_lig", inputs={blig: Flow()}, outputs={b_el: Flow(nominal_value=50, variable_costs=10)}, diff --git a/tests/test_scripts/test_solph/test_piecewiselineartransformer/test_piecewiselineartransformer.py b/tests/test_scripts/test_solph/test_piecewiselineartransformer/test_piecewiselineartransformer.py index 4ccce8197..058066a50 100644 --- a/tests/test_scripts/test_solph/test_piecewiselineartransformer/test_piecewiselineartransformer.py +++ b/tests/test_scripts/test_solph/test_piecewiselineartransformer/test_piecewiselineartransformer.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ Example that illustrates how to use custom component -`PiecewiseLinearTransformer` can be used. +`PiecewiseLinearConverter` can be used. This file is part of project oemof (github.com/oemof/oemof). It's copyrighted by the contributors recorded in the version control history of the file, @@ -48,8 +48,8 @@ def conv_func(x): in_breakpoints = np.arange(0, 110, 25) out_breakpoints = conv_func(in_breakpoints) - # Create and add PiecewiseLinearTransformer - pwltf = solph.components.experimental.PiecewiseLinearTransformer( + # Create and add PiecewiseLinearConverter + pwltf = solph.components.experimental.PiecewiseLinearConverter( label="pwltf", inputs={b_gas: solph.flows.Flow(nominal_value=100, variable_costs=1)}, outputs={b_el: solph.flows.Flow()}, diff --git a/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch.py b/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch.py index d81ae10c3..ba17cc81d 100644 --- a/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch.py +++ b/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch.py @@ -25,7 +25,7 @@ from oemof.solph.buses import Bus from oemof.solph.components import Sink from oemof.solph.components import Source -from oemof.solph.components import Transformer +from oemof.solph.components import Converter from oemof.solph.flows import Flow @@ -73,28 +73,28 @@ def test_dispatch_example(solver="cbc", periods=24 * 5): ) # power plants - pp_coal = Transformer( + pp_coal = Converter( label="pp_coal", inputs={bcoal: Flow()}, outputs={bel: Flow(nominal_value=20.2, variable_costs=25)}, conversion_factors={bel: 0.39}, ) - pp_lig = Transformer( + pp_lig = Converter( label="pp_lig", inputs={blig: Flow()}, outputs={bel: Flow(nominal_value=11.8, variable_costs=19)}, conversion_factors={bel: 0.41}, ) - pp_gas = Transformer( + pp_gas = Converter( label="pp_gas", inputs={bgas: Flow()}, outputs={bel: Flow(nominal_value=41, variable_costs=40)}, conversion_factors={bel: 0.50}, ) - pp_oil = Transformer( + pp_oil = Converter( label="pp_oil", inputs={boil: Flow()}, outputs={bel: Flow(nominal_value=5, variable_costs=50)}, @@ -102,7 +102,7 @@ def test_dispatch_example(solver="cbc", periods=24 * 5): ) # combined heat and power plant (chp) - pp_chp = Transformer( + pp_chp = Converter( label="pp_chp", inputs={bgas: Flow()}, outputs={ @@ -118,7 +118,7 @@ def test_dispatch_example(solver="cbc", periods=24 * 5): heat_source = Source(label="heat_source", outputs={b_heat_source: Flow()}) cop = 3 - heat_pump = Transformer( + heat_pump = Converter( label="heat_pump", inputs={bel: Flow(), b_heat_source: Flow()}, outputs={bth: Flow(nominal_value=10)}, diff --git a/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one.py b/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one.py index bfd789264..20bad21d7 100644 --- a/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one.py +++ b/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one.py @@ -21,7 +21,7 @@ from oemof.solph.buses import Bus from oemof.solph.components import Sink from oemof.solph.components import Source -from oemof.solph.components import Transformer +from oemof.solph.components import Converter from oemof.solph.flows import Flow @@ -56,7 +56,7 @@ def test_dispatch_one_time_step(solver="cbc"): ) # combined heat and power plant (chp) - pp_chp = Transformer( + pp_chp = Converter( label="pp_chp", inputs={bgas: Flow()}, outputs={ @@ -72,7 +72,7 @@ def test_dispatch_one_time_step(solver="cbc"): heat_source = Source(label="heat_source", outputs={b_heat_source: Flow()}) cop = 3 - heat_pump = Transformer( + heat_pump = Converter( label="heat_pump", inputs={bel: Flow(), b_heat_source: Flow()}, outputs={bth: Flow(nominal_value=10)}, diff --git a/tests/test_scripts/test_solph/test_simple_model/test_simple_invest.py b/tests/test_scripts/test_solph/test_simple_model/test_simple_invest.py index 285fba257..4044a0725 100644 --- a/tests/test_scripts/test_solph/test_simple_model/test_simple_invest.py +++ b/tests/test_scripts/test_solph/test_simple_model/test_simple_invest.py @@ -28,7 +28,7 @@ from oemof.solph.buses import Bus from oemof.solph.components import Sink from oemof.solph.components import Source -from oemof.solph.components import Transformer +from oemof.solph.components import Converter from oemof.solph.flows import Flow @@ -91,28 +91,28 @@ def test_dispatch_example(solver="cbc", periods=24 * 5): ) # power plants - pp_coal = Transformer( + pp_coal = Converter( label="pp_coal", inputs={bcoal: Flow()}, outputs={bel: Flow(nominal_value=20.2, variable_costs=25)}, conversion_factors={bel: 0.39}, ) - pp_lig = Transformer( + pp_lig = Converter( label="pp_lig", inputs={blig: Flow()}, outputs={bel: Flow(nominal_value=11.8, variable_costs=19)}, conversion_factors={bel: 0.41}, ) - pp_gas = Transformer( + pp_gas = Converter( label="pp_gas", inputs={bgas: Flow()}, outputs={bel: Flow(nominal_value=41, variable_costs=40)}, conversion_factors={bel: 0.50}, ) - pp_oil = Transformer( + pp_oil = Converter( label="pp_oil", inputs={boil: Flow()}, outputs={bel: Flow(nominal_value=5, variable_costs=50)}, @@ -120,7 +120,7 @@ def test_dispatch_example(solver="cbc", periods=24 * 5): ) # combined heat and power plant (chp) - pp_chp = Transformer( + pp_chp = Converter( label="pp_chp", inputs={bgas: Flow()}, outputs={ @@ -136,7 +136,7 @@ def test_dispatch_example(solver="cbc", periods=24 * 5): heat_source = Source(label="heat_source", outputs={b_heat_source: Flow()}) cop = 3 - heat_pump = Transformer( + heat_pump = Converter( label="el_heat_pump", inputs={bel: Flow(), b_heat_source: Flow()}, outputs={bth: Flow(nominal_value=10)}, diff --git a/tests/test_scripts/test_solph/test_storage_investment/test_invest_storage_regression.py b/tests/test_scripts/test_solph/test_storage_investment/test_invest_storage_regression.py index cbd5a0181..6f1e752a4 100644 --- a/tests/test_scripts/test_solph/test_storage_investment/test_invest_storage_regression.py +++ b/tests/test_scripts/test_solph/test_storage_investment/test_invest_storage_regression.py @@ -45,8 +45,8 @@ def test_regression_investment_storage(solver="cbc"): # Sources solph.components.Source(label="rgas", outputs={bgas: solph.flows.Flow()}) - # Transformer - solph.components.Transformer( + # Converter + solph.components.Converter( label="pp_gas", inputs={bgas: solph.flows.Flow()}, outputs={bel: solph.flows.Flow(nominal_value=300000)}, diff --git a/tests/test_scripts/test_solph/test_storage_investment/test_storage_investment.py b/tests/test_scripts/test_solph/test_storage_investment/test_storage_investment.py index 0f7837253..f1cdb6c9d 100644 --- a/tests/test_scripts/test_solph/test_storage_investment/test_storage_investment.py +++ b/tests/test_scripts/test_solph/test_storage_investment/test_storage_investment.py @@ -18,7 +18,7 @@ demand(Sink) |<------------------| | | | | | | | | | - pp_gas(Transformer) |<---------| | | + pp_gas(Converter) |<---------| | | |------------------>| | | | | | storage(Storage) |<------------------| | @@ -97,8 +97,8 @@ def test_optimise_storage_size( outputs={bel: solph.flows.Flow(fix=data["pv"], nominal_value=582000)}, ) - # Transformer - PP_GAS = solph.components.Transformer( + # Converter + PP_GAS = solph.components.Converter( label="pp_gas", inputs={bgas: solph.flows.Flow()}, outputs={ @@ -183,7 +183,7 @@ def test_results_with_actual_dump(): def test_solph_transformer_attributes_before_dump_and_after_restore(): """dump/restore should preserve all attributes - of `solph.components.Transformer`""" + of `solph.components.Converter`""" energysystem = solph.EnergySystem() energysystem.restore() diff --git a/tests/test_scripts/test_solph/test_storage_investment/test_storage_with_tuple_label.py b/tests/test_scripts/test_solph/test_storage_investment/test_storage_with_tuple_label.py index e07ca278c..a37a8ae2d 100644 --- a/tests/test_scripts/test_solph/test_storage_investment/test_storage_with_tuple_label.py +++ b/tests/test_scripts/test_solph/test_storage_investment/test_storage_with_tuple_label.py @@ -18,7 +18,7 @@ demand(Sink) |<------------------| | | | | | | | | | - pp_gas(Transformer) |<---------| | | + pp_gas(Converter) |<---------| | | |------------------>| | | | | | storage(Storage) |<------------------| | @@ -115,8 +115,8 @@ def test_tuples_as_labels_example( }, ) - # Transformer - solph.components.Transformer( + # Converter + solph.components.Converter( label=Label("pp", "electricity", "natural_gas"), inputs={bgas: solph.flows.Flow()}, outputs={ diff --git a/tests/test_scripts/test_solph/test_variable_chp/test_variable_chp.py b/tests/test_scripts/test_solph/test_variable_chp/test_variable_chp.py index f1f1452c3..e6fc78803 100644 --- a/tests/test_scripts/test_solph/test_variable_chp/test_variable_chp.py +++ b/tests/test_scripts/test_solph/test_variable_chp/test_variable_chp.py @@ -97,7 +97,7 @@ def test_variable_chp(filename="variable_chp.csv", solver="cbc"): ) # create a fixed transformer to distribute to the heat_2 and elec_2 buses - solph.components.Transformer( + solph.components.Converter( label=("fixed_chp", "gas"), inputs={bgas: solph.flows.Flow(nominal_value=10e10)}, outputs={bel2: solph.flows.Flow(), bth2: solph.flows.Flow()}, diff --git a/tests/test_solph_network_classes.py b/tests/test_solph_network_classes.py index 10e114f09..8f84090bf 100644 --- a/tests/test_solph_network_classes.py +++ b/tests/test_solph_network_classes.py @@ -29,32 +29,32 @@ def teardown_class(cls): warnings.filterwarnings("always", category=SuspiciousUsageWarning) def test_empty_transformer(self): - transf = solph.components.Transformer() + transf = solph.components.Converter() assert isinstance(transf.conversion_factors, dict) assert len(transf.conversion_factors.keys()) == 0 def test_default_conversion_factor(self): - transf = solph.components.Transformer( + transf = solph.components.Converter( inputs={self.bus: solph.flows.Flow()} ) assert transf.conversion_factors[self.bus][2] == 1 def test_sequence_conversion_factor_from_scalar(self): - transf = solph.components.Transformer( + transf = solph.components.Converter( inputs={self.bus: solph.flows.Flow()}, conversion_factors={self.bus: 2}, ) assert transf.conversion_factors[self.bus][6] == 2 def test_sequence_conversion_factor_from_list_correct_length(self): - transf = solph.components.Transformer( + transf = solph.components.Converter( inputs={self.bus: solph.flows.Flow()}, conversion_factors={self.bus: [2]}, ) assert len(transf.conversion_factors[self.bus]) == 1 def test_sequence_conversion_factor_from_list_wrong_length(self): - transf = solph.components.Transformer( + transf = solph.components.Converter( inputs={self.bus: solph.flows.Flow()}, conversion_factors={self.bus: [2]}, ) diff --git a/tests/test_warnings.py b/tests/test_warnings.py index 7f8c40793..a8883acd2 100644 --- a/tests/test_warnings.py +++ b/tests/test_warnings.py @@ -78,24 +78,24 @@ def test_that_the_solph_source_warnings_actually_get_raised(warning_fixture): def test_that_the_transformer_warnings_actually_get_raised(warning_fixture): - """Transformer doesn't warn about potentially erroneous usage.""" + """Converter doesn't warn about potentially erroneous usage.""" look_out = network.Bus() msg = ( "Attribute is missing in Node of ." + " 'oemof.solph.components._converter.Converter'>." ) with warnings.catch_warnings(record=True) as w: - solph.components.Transformer( + solph.components.Converter( label="no input", outputs={look_out: "No inputs!"} ) assert len(w) == 1 assert msg in str(w[-1].message) msg = ( "Attribute is missing in Node of ." + " 'oemof.solph.components._converter.Converter'>." ) with warnings.catch_warnings(record=True) as w: - solph.components.Transformer( + solph.components.Converter( label="no output", inputs={look_out: "No outputs!"} ) assert len(w) == 1 From e67fc6e388038889769ce9d1eda84cc4ac7362f4 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 19 Feb 2022 14:23:38 +0100 Subject: [PATCH 0143/1363] Add / adjust constraint tests --- .../solph/components/_generic_storage.py | 2 +- tests/constraint_tests.py | 112 ++++++- tests/lp_files/dsm_module_DIW_extended.lp | 182 ++++++++++ tests/lp_files/dsm_module_DIW_invest.lp | 159 +++++---- tests/lp_files/dsm_module_DLR_extended.lp | 313 ++++++++++++++++++ tests/lp_files/dsm_module_DLR_invest.lp | 212 ++++++------ tests/lp_files/dsm_module_oemof_extended.lp | 100 ++++++ tests/lp_files/dsm_module_oemof_invest.lp | 112 +++---- tests/lp_files/flow_invest_with_offset.lp | 82 ++--- .../flow_invest_with_offset_no_minimum.lp | 80 ++--- tests/lp_files/flow_invest_without_offset.lp | 80 ++--- .../lp_files/storage_invest_all_nonconvex.lp | 184 +++++----- tests/lp_files/storage_invest_with_offset.lp | 224 +++++++------ .../lp_files/storage_invest_without_offset.lp | 178 +++++----- tests/test_components.py | 2 +- 15 files changed, 1404 insertions(+), 618 deletions(-) create mode 100644 tests/lp_files/dsm_module_DIW_extended.lp create mode 100644 tests/lp_files/dsm_module_DLR_extended.lp create mode 100644 tests/lp_files/dsm_module_oemof_extended.lp diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index fc1b7ac39..cf1facab5 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -247,7 +247,7 @@ def _check_invest_attributes(self): self.investment and sum(solph_sequence(self.fixed_losses_absolute)) != 0 and self.investment.existing == 0 - and self.investment.minimum == 0 + and self.investment.minimum[0] == 0 ): e3 = ( "With fixed_losses_absolute > 0, either investment.existing " diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index 9ef76d69b..0bbb6d04f 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -1181,6 +1181,91 @@ def test_dsm_module_oemof(self): ) self.compare_lp_files("dsm_module_oemof.lp") + def test_dsm_module_DIW_extended(self): + """Constraint test of SinkDSM with approach=DLR + + Test all possible parameters and constraints + """ + + b_elec = solph.buses.Bus(label="bus_elec") + solph.components.experimental.SinkDSM( + label="demand_dsm", + inputs={b_elec: solph.flows.Flow()}, + demand=[1, 0.9, 0.8], + capacity_up=[0.5, 0.4, 0.5], + capacity_down=[0.3, 0.3, 0.4], + approach="DIW", + max_demand=1, + max_capacity_up=1, + max_capacity_down=1, + delay_time=1, + cost_dsm_down_shift=1, + cost_dsm_up=1, + cost_dsm_down_shed=100, + efficiency=0.99, + recovery_time_shift=2, + recovery_time_shed=2, + shed_time=2, + ) + self.compare_lp_files("dsm_module_DIW_extended.lp") + + def test_dsm_module_DLR_extended(self): + """Constraint test of SinkDSM with approach=DLR""" + + b_elec = solph.buses.Bus(label="bus_elec") + solph.components.experimental.SinkDSM( + label="demand_dsm", + inputs={b_elec: solph.flows.Flow()}, + demand=[1, 0.9, 0.8], + capacity_up=[0.5, 0.4, 0.5], + capacity_down=[0.3, 0.3, 0.4], + approach="DLR", + max_demand=1, + max_capacity_up=1, + max_capacity_down=1, + delay_time=2, + shift_time=1, + cost_dsm_down_shift=1, + cost_dsm_up=1, + cost_dsm_down_shed=100, + efficiency=0.99, + recovery_time_shed=2, + ActivateYearLimit=True, + ActivateDayLimit=True, + n_yearLimit_shift=100, + n_yearLimit_shed=50, + t_dayLimit=3, + addition=False, + fixes=False, + shed_time=2, + ) + self.compare_lp_files("dsm_module_DLR_extended.lp") + + def test_dsm_module_oemof_extended(self): + """Constraint test of SinkDSM with approach=oemof""" + + b_elec = solph.buses.Bus(label="bus_elec") + solph.components.experimental.SinkDSM( + label="demand_dsm", + inputs={b_elec: solph.flows.Flow()}, + demand=[1, 0.9, 0.8], + capacity_up=[0.5, 0.4, 0.5], + capacity_down=[0.3, 0.3, 0.4], + approach="oemof", + shift_interval=2, + max_demand=1, + max_capacity_up=1, + max_capacity_down=1, + delay_time=2, + cost_dsm_down_shift=1, + cost_dsm_up=1, + cost_dsm_down_shed=100, + efficiency=0.99, + recovery_time_shed=2, + shed_time=2, + ) + self.compare_lp_files("dsm_module_oemof_extended.lp") + def test_dsm_module_DIW_invest(self): """Constraint test of SinkDSM with approach=DLR and investments""" @@ -1195,8 +1280,12 @@ def test_dsm_module_DIW_invest(self): flex_share_up=1, flex_share_down=1, delay_time=1, - cost_dsm_down_shift=2, - shed_eligibility=False, + cost_dsm_down_shift=1, + cost_dsm_up=1, + cost_dsm_down_shed=100, + shed_eligibility=True, + recovery_time_shed=2, + shed_time=2, investment=solph.Investment( ep_cost=100, existing=50, minimum=33, maximum=100 ), @@ -1218,8 +1307,13 @@ def test_dsm_module_DLR_invest(self): flex_share_down=1, delay_time=2, shift_time=1, - cost_dsm_down_shift=2, - shed_eligibility=False, + cost_dsm_down_shift=1, + cost_dsm_up=1, + cost_dsm_down_shed=100, + shed_eligibility=True, + recovery_time_shed=2, + shed_time=2, + n_yearLimit_shed=50, investment=solph.Investment( ep_cost=100, existing=50, minimum=33, maximum=100 ), @@ -1240,8 +1334,12 @@ def test_dsm_module_oemof_invest(self): flex_share_up=1, flex_share_down=1, shift_interval=2, - cost_dsm_down_shift=2, - shed_eligibility=False, + cost_dsm_down_shift=1, + cost_dsm_up=1, + cost_dsm_down_shed=100, + shed_eligibility=True, + recovery_time_shed=2, + shed_time=2, investment=solph.Investment( ep_cost=100, existing=50, minimum=33, maximum=100 ), @@ -1280,7 +1378,7 @@ def test_nonconvex_investment_storage_with_offset(self): bel = solph.buses.Bus(label="electricityBus") solph.components.GenericStorage( - label="storagenon_convex", + label="storage_non_convex", inputs={bel: solph.flows.Flow(variable_costs=56)}, outputs={bel: solph.flows.Flow(variable_costs=24)}, nominal_storage_capacity=None, diff --git a/tests/lp_files/dsm_module_DIW_extended.lp b/tests/lp_files/dsm_module_DIW_extended.lp new file mode 100644 index 000000000..32a53f137 --- /dev/null +++ b/tests/lp_files/dsm_module_DIW_extended.lp @@ -0,0 +1,182 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++100 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_0) ++100 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) ++100 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_0) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_1) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_2) + +s.t. + +c_e_BusBlock_balance(bus_elec_0_0)_: ++1 flow(bus_elec_demand_dsm_0_0) += 0 + +c_e_BusBlock_balance(bus_elec_0_1)_: ++1 flow(bus_elec_demand_dsm_0_1) += 0 + +c_e_BusBlock_balance(bus_elec_0_2)_: ++1 flow(bus_elec_demand_dsm_0_2) += 0 + +c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_0_0)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_0) +-1 SinkDSMDIWBlock_dsm_up(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_0) += 1 + +c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_0_1)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_1) +-1 SinkDSMDIWBlock_dsm_up(demand_dsm_1) ++1 flow(bus_elec_demand_dsm_0_1) += 0.90000000000000002 + +c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_0_2)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_2) +-1 SinkDSMDIWBlock_dsm_up(demand_dsm_2) ++1 flow(bus_elec_demand_dsm_0_2) += 0.80000000000000004 + +c_e_SinkDSMDIWBlock_dsm_updo_constraint(demand_dsm_0)_: +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) ++0.98999999999999999 SinkDSMDIWBlock_dsm_up(demand_dsm_0) += 0 + +c_e_SinkDSMDIWBlock_dsm_updo_constraint(demand_dsm_1)_: +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_0) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_1) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_2) ++0.98999999999999999 SinkDSMDIWBlock_dsm_up(demand_dsm_1) += 0 + +c_e_SinkDSMDIWBlock_dsm_updo_constraint(demand_dsm_2)_: +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_1) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_2) ++0.98999999999999999 SinkDSMDIWBlock_dsm_up(demand_dsm_2) += 0 + +c_u_SinkDSMDIWBlock_dsm_up_constraint(demand_dsm_0)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_0) +<= 0.5 + +c_u_SinkDSMDIWBlock_dsm_up_constraint(demand_dsm_1)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_1) +<= 0.40000000000000002 + +c_u_SinkDSMDIWBlock_dsm_up_constraint(demand_dsm_2)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_2) +<= 0.5 + +c_u_SinkDSMDIWBlock_dsm_do_constraint(demand_dsm_0)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_0) +<= 0.29999999999999999 + +c_u_SinkDSMDIWBlock_dsm_do_constraint(demand_dsm_1)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_1) +<= 0.29999999999999999 + +c_u_SinkDSMDIWBlock_dsm_do_constraint(demand_dsm_2)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_2) +<= 0.40000000000000002 + +c_u_SinkDSMDIWBlock_C2_constraint(demand_dsm_0)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_0) +<= 0.5 + +c_u_SinkDSMDIWBlock_C2_constraint(demand_dsm_1)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_1) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_1) +<= 0.40000000000000002 + +c_u_SinkDSMDIWBlock_C2_constraint(demand_dsm_2)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_2) +<= 0.5 + +c_u_SinkDSMDIWBlock_recovery_constraint(demand_dsm_0)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_0) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_1) +<= 0.5 + +c_u_SinkDSMDIWBlock_recovery_constraint(demand_dsm_1)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_1) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_2) +<= 0.40000000000000002 + +c_u_SinkDSMDIWBlock_recovery_constraint(demand_dsm_2)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_2) +<= 0.5 + +c_u_SinkDSMDIWBlock_shed_limit_constraint(demand_dsm_0)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) +<= 0.59999999999999998 + +c_u_SinkDSMDIWBlock_shed_limit_constraint(demand_dsm_1)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) +<= 0.59999999999999998 + +c_u_SinkDSMDIWBlock_shed_limit_constraint(demand_dsm_2)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) +<= 0.80000000000000004 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(bus_elec_demand_dsm_0_0) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_1) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shed(demand_dsm_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_up(demand_dsm_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_up(demand_dsm_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_up(demand_dsm_2) <= +inf +end diff --git a/tests/lp_files/dsm_module_DIW_invest.lp b/tests/lp_files/dsm_module_DIW_invest.lp index d60242058..1d258c469 100644 --- a/tests/lp_files/dsm_module_DIW_invest.lp +++ b/tests/lp_files/dsm_module_DIW_invest.lp @@ -1,70 +1,69 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+2 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_0) -+2 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_1) -+2 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_2) -+2 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_0) -+2 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_1) -+2 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_2) -+2 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_0) -+2 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_1) -+2 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_2) ++100 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_0) ++100 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_1) ++100 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_0) ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_1) ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_2) s.t. -c_e_BusBlock_balance(bus_elec_0)_: -+1 flow(bus_elec_demand_dsm_0) -= 0 - -c_e_BusBlock_balance(bus_elec_1)_: -+1 flow(bus_elec_demand_dsm_1) -= 0 - -c_e_BusBlock_balance(bus_elec_2)_: -+1 flow(bus_elec_demand_dsm_2) +c_e_BusBlock_balance(bus_elec_0_0)_: ++1 flow(bus_elec_demand_dsm_0_0) = 0 -c_e_SinkDSMDIWInvestmentBlock_shift_shed_vars(demand_dsm_0)_: -+1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_0) +c_e_BusBlock_balance(bus_elec_0_1)_: ++1 flow(bus_elec_demand_dsm_0_1) = 0 -c_e_SinkDSMDIWInvestmentBlock_shift_shed_vars(demand_dsm_1)_: -+1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_1) +c_e_BusBlock_balance(bus_elec_0_2)_: ++1 flow(bus_elec_demand_dsm_0_2) = 0 -c_e_SinkDSMDIWInvestmentBlock_shift_shed_vars(demand_dsm_2)_: -+1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_2) -= 0 +c_e_SinkDSMDIWInvestmentBlock_total_dsm_rule(demand_dsm_0)_: +-1 SinkDSMDIWInvestmentBlock_invest(demand_dsm_0) ++1 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) += 50 -c_e_SinkDSMDIWInvestmentBlock_input_output_relation(demand_dsm_0)_: +c_e_SinkDSMDIWInvestmentBlock_input_output_relation(demand_dsm_0_0)_: +1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_0) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_0) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_0) -1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_0) --1 SinkDSMDIWInvestmentBlock_invest(demand_dsm) -+1 flow(bus_elec_demand_dsm_0) -= 50 +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_0) += 0 -c_e_SinkDSMDIWInvestmentBlock_input_output_relation(demand_dsm_1)_: +c_e_SinkDSMDIWInvestmentBlock_input_output_relation(demand_dsm_0_1)_: +1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_1) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_1) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_1) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_1) -1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_1) --1 SinkDSMDIWInvestmentBlock_invest(demand_dsm) -+1 flow(bus_elec_demand_dsm_1) -= 50 +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_1) += 0 -c_e_SinkDSMDIWInvestmentBlock_input_output_relation(demand_dsm_2)_: +c_e_SinkDSMDIWInvestmentBlock_input_output_relation(demand_dsm_0_2)_: +1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_2) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_2) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_2) -1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_2) --1 SinkDSMDIWInvestmentBlock_invest(demand_dsm) -+1 flow(bus_elec_demand_dsm_2) -= 50 +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_2) += 0 c_e_SinkDSMDIWInvestmentBlock_dsm_updo_constraint(demand_dsm_0)_: -1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_0) @@ -85,76 +84,94 @@ c_e_SinkDSMDIWInvestmentBlock_dsm_updo_constraint(demand_dsm_2)_: +1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_2) = 0 -c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(demand_dsm_0)_: +c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(demand_dsm_0_0)_: +1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_0) --0.5 SinkDSMDIWInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(demand_dsm_1)_: +c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(demand_dsm_0_1)_: +1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_1) --0.5 SinkDSMDIWInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(demand_dsm_2)_: +c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(demand_dsm_0_2)_: +1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_2) --0.5 SinkDSMDIWInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(demand_dsm_0)_: +c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(demand_dsm_0_0)_: +1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_0) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_0) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_0) --0.5 SinkDSMDIWInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(demand_dsm_1)_: +c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(demand_dsm_0_1)_: +1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_1) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_1) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_1) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_1) --0.5 SinkDSMDIWInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(demand_dsm_2)_: +c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(demand_dsm_0_2)_: +1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_2) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_2) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_2) --0.5 SinkDSMDIWInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDIWInvestmentBlock_C2_constraint(demand_dsm_0)_: +c_u_SinkDSMDIWInvestmentBlock_C2_constraint(demand_dsm_0_0)_: +1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_0) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_0) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_0) +1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_0) --0.5 SinkDSMDIWInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDIWInvestmentBlock_C2_constraint(demand_dsm_1)_: +c_u_SinkDSMDIWInvestmentBlock_C2_constraint(demand_dsm_0_1)_: +1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_1) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_1) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_1) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_1) +1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_1) --0.5 SinkDSMDIWInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDIWInvestmentBlock_C2_constraint(demand_dsm_2)_: +c_u_SinkDSMDIWInvestmentBlock_C2_constraint(demand_dsm_0_2)_: +1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_2) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_2) +1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_2) +1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_2) --0.5 SinkDSMDIWInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_shed_limit_constraint(demand_dsm_0_0)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_1) +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_shed_limit_constraint(demand_dsm_0_1)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_2) +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_shed_limit_constraint(demand_dsm_0_2)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_2) +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(bus_elec_demand_dsm_0) <= +inf - 0 <= flow(bus_elec_demand_dsm_1) <= +inf - 0 <= flow(bus_elec_demand_dsm_2) <= +inf - 33 <= SinkDSMDIWInvestmentBlock_invest(demand_dsm) <= 100 + 0 <= flow(bus_elec_demand_dsm_0_0) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_1) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_2) <= +inf + 33 <= SinkDSMDIWInvestmentBlock_invest(demand_dsm_0) <= 100 + 0 <= SinkDSMDIWInvestmentBlock_total(demand_dsm_0) <= +inf 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_0) <= +inf 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_1) <= +inf 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_2) <= +inf diff --git a/tests/lp_files/dsm_module_DLR_extended.lp b/tests/lp_files/dsm_module_DLR_extended.lp new file mode 100644 index 000000000..33ff298b7 --- /dev/null +++ b/tests/lp_files/dsm_module_DLR_extended.lp @@ -0,0 +1,313 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_0) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_1) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_2) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_0) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_1) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_2) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_2) ++100 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_0) ++100 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_1) ++100 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) + +s.t. + +c_e_BusBlock_balance(bus_elec_0_0)_: ++1 flow(bus_elec_demand_dsm_0_0) += 0 + +c_e_BusBlock_balance(bus_elec_0_1)_: ++1 flow(bus_elec_demand_dsm_0_1) += 0 + +c_e_BusBlock_balance(bus_elec_0_2)_: ++1 flow(bus_elec_demand_dsm_0_2) += 0 + +c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_0_0)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_0) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_0) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) ++1 flow(bus_elec_demand_dsm_0_0) += 1 + +c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_0_1)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_1) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_1) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) ++1 flow(bus_elec_demand_dsm_0_1) += 0.90000000000000002 + +c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_0_2)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_2) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_2) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) ++1 flow(bus_elec_demand_dsm_0_2) += 0.80000000000000004 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_1_0)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_1_1)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_1) +-1.0101010101010102 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_1_2)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_2) +-1.0101010101010102 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_2_0)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_2_2)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_2) +-1.0101010101010102 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_1_0)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_1_1)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) +-0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_1_2)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) +-0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_2_0)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_2_2)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_2) +-0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) += 0 + +c_u_SinkDSMDLRBlock_availability_red(demand_dsm_0)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) +<= 0.29999999999999999 + +c_u_SinkDSMDLRBlock_availability_red(demand_dsm_1)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) +<= 0.29999999999999999 + +c_u_SinkDSMDLRBlock_availability_red(demand_dsm_2)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) +<= 0.40000000000000002 + +c_u_SinkDSMDLRBlock_availability_inc(demand_dsm_0)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_0) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) +<= 0.5 + +c_u_SinkDSMDLRBlock_availability_inc(demand_dsm_1)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_1) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) +<= 0.40000000000000002 + +c_u_SinkDSMDLRBlock_availability_inc(demand_dsm_2)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_2) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) +<= 0.5 + +c_e_SinkDSMDLRBlock_dr_storage_red(demand_dsm_0)_: ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_0) +-1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) +-1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_red(demand_dsm_1)_: +-0.98999999999999999 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_1) +-0.98999999999999999 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_0) +-1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_red(demand_dsm_2)_: +-0.98999999999999999 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_2) +-0.98999999999999999 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_1) +-1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_inc(demand_dsm_0)_: +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_0) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_inc(demand_dsm_1)_: +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_1) ++0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) ++0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_0) +-1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_1) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_inc(demand_dsm_2)_: +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_2) ++0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) ++0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_1) +-1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_2) += 0 + +c_u_SinkDSMDLRBlock_dr_storage_limit_red(demand_dsm_0)_: ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_0) +<= 0.33333333333333331 + +c_u_SinkDSMDLRBlock_dr_storage_limit_red(demand_dsm_1)_: ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_1) +<= 0.33333333333333331 + +c_u_SinkDSMDLRBlock_dr_storage_limit_red(demand_dsm_2)_: ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_2) +<= 0.33333333333333331 + +c_u_SinkDSMDLRBlock_dr_storage_limit_inc(demand_dsm_0)_: ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_0) +<= 0.46666666666666662 + +c_u_SinkDSMDLRBlock_dr_storage_limit_inc(demand_dsm_1)_: ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_1) +<= 0.46666666666666662 + +c_u_SinkDSMDLRBlock_dr_storage_limit_inc(demand_dsm_2)_: ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_2) +<= 0.46666666666666662 + +c_u_SinkDSMDLRBlock_dr_yearly_limit_shed(demand_dsm_0)_: ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_2) +<= 33.333333333333329 + +c_u_SinkDSMDLRBlock_dr_yearly_limit_red(demand_dsm_0)_: ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) +<= 33.333333333333329 + +c_u_SinkDSMDLRBlock_dr_yearly_limit_inc(demand_dsm_0)_: ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) +<= 46.666666666666664 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(bus_elec_demand_dsm_0_0) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_1) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shed(demand_dsm_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shed(demand_dsm_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shed(demand_dsm_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_level(demand_dsm_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_level(demand_dsm_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_level(demand_dsm_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up_level(demand_dsm_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up_level(demand_dsm_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up_level(demand_dsm_2) <= +inf +end diff --git a/tests/lp_files/dsm_module_DLR_invest.lp b/tests/lp_files/dsm_module_DLR_invest.lp index c5cd600b2..a315b1010 100644 --- a/tests/lp_files/dsm_module_DLR_invest.lp +++ b/tests/lp_files/dsm_module_DLR_invest.lp @@ -1,59 +1,55 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+2 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_0) -+2 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_1) -+2 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_2) -+2 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_0) -+2 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_1) -+2 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_2) -+2 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_0) -+2 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_1) -+2 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_2) -+2 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_0) -+2 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_1) -+2 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_0) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_1) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_2) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_0) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_1) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_2) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_2) ++100 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_0) ++100 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_1) ++100 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_2) s.t. -c_e_BusBlock_balance(bus_elec_0)_: -+1 flow(bus_elec_demand_dsm_0) -= 0 - -c_e_BusBlock_balance(bus_elec_1)_: -+1 flow(bus_elec_demand_dsm_1) +c_e_BusBlock_balance(bus_elec_0_0)_: ++1 flow(bus_elec_demand_dsm_0_0) = 0 -c_e_BusBlock_balance(bus_elec_2)_: -+1 flow(bus_elec_demand_dsm_2) +c_e_BusBlock_balance(bus_elec_0_1)_: ++1 flow(bus_elec_demand_dsm_0_1) = 0 -c_e_SinkDSMDLRInvestmentBlock_shift_shed_vars(demand_dsm_1_0)_: -+1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_0) -= 0 - -c_e_SinkDSMDLRInvestmentBlock_shift_shed_vars(demand_dsm_1_1)_: -+1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_1) -= 0 - -c_e_SinkDSMDLRInvestmentBlock_shift_shed_vars(demand_dsm_1_2)_: -+1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_2) -= 0 - -c_e_SinkDSMDLRInvestmentBlock_shift_shed_vars(demand_dsm_2_0)_: -+1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_0) -= 0 - -c_e_SinkDSMDLRInvestmentBlock_shift_shed_vars(demand_dsm_2_1)_: -+1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_1) +c_e_BusBlock_balance(bus_elec_0_2)_: ++1 flow(bus_elec_demand_dsm_0_2) = 0 -c_e_SinkDSMDLRInvestmentBlock_shift_shed_vars(demand_dsm_2_2)_: -+1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_2) -= 0 +c_e_SinkDSMDLRInvestmentBlock_total_dsm_rule(demand_dsm_0)_: +-1 SinkDSMDLRInvestmentBlock_invest(demand_dsm_0) ++1 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) += 50 -c_e_SinkDSMDLRInvestmentBlock_input_output_relation(demand_dsm_0)_: +c_e_SinkDSMDLRInvestmentBlock_input_output_relation(demand_dsm_0_0)_: -1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_0) -1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_0) +1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_0) @@ -63,11 +59,11 @@ c_e_SinkDSMDLRInvestmentBlock_input_output_relation(demand_dsm_0)_: +1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_0) -1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_0) -1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_0) --1 SinkDSMDLRInvestmentBlock_invest(demand_dsm) -+1 flow(bus_elec_demand_dsm_0) -= 50 +-1 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_0) += 0 -c_e_SinkDSMDLRInvestmentBlock_input_output_relation(demand_dsm_1)_: +c_e_SinkDSMDLRInvestmentBlock_input_output_relation(demand_dsm_0_1)_: -1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_1) -1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_1) +1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_1) @@ -77,11 +73,11 @@ c_e_SinkDSMDLRInvestmentBlock_input_output_relation(demand_dsm_1)_: +1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_1) -1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_1) -1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_1) --1 SinkDSMDLRInvestmentBlock_invest(demand_dsm) -+1 flow(bus_elec_demand_dsm_1) -= 50 +-1 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_1) += 0 -c_e_SinkDSMDLRInvestmentBlock_input_output_relation(demand_dsm_2)_: +c_e_SinkDSMDLRInvestmentBlock_input_output_relation(demand_dsm_0_2)_: -1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_2) -1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_2) +1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_2) @@ -91,9 +87,9 @@ c_e_SinkDSMDLRInvestmentBlock_input_output_relation(demand_dsm_2)_: +1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_2) -1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_2) -1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_2) --1 SinkDSMDLRInvestmentBlock_invest(demand_dsm) -+1 flow(bus_elec_demand_dsm_2) -= 50 +-1 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_2) += 0 c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(demand_dsm_1_0)_: +1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_0) @@ -165,56 +161,56 @@ c_e_SinkDSMDLRInvestmentBlock_no_comp_inc(demand_dsm_2_2)_: +1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_2) = 0 -c_u_SinkDSMDLRInvestmentBlock_availability_red(demand_dsm_0)_: +c_u_SinkDSMDLRInvestmentBlock_availability_red(demand_dsm_0_0)_: +1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_0) +1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_0) +1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_0) +1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_0) +1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_0) --0.5 SinkDSMDLRInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDLRInvestmentBlock_availability_red(demand_dsm_1)_: +c_u_SinkDSMDLRInvestmentBlock_availability_red(demand_dsm_0_1)_: +1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_1) +1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_1) +1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_1) +1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_1) +1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_1) --0.5 SinkDSMDLRInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDLRInvestmentBlock_availability_red(demand_dsm_2)_: +c_u_SinkDSMDLRInvestmentBlock_availability_red(demand_dsm_0_2)_: +1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_2) +1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_2) +1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_2) +1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_2) +1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_2) --0.5 SinkDSMDLRInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDLRInvestmentBlock_availability_inc(demand_dsm_0)_: +c_u_SinkDSMDLRInvestmentBlock_availability_inc(demand_dsm_0_0)_: +1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_0) +1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_0) +1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_0) +1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_0) --0.5 SinkDSMDLRInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDLRInvestmentBlock_availability_inc(demand_dsm_1)_: +c_u_SinkDSMDLRInvestmentBlock_availability_inc(demand_dsm_0_1)_: +1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_1) +1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_1) +1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_1) +1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_1) --0.5 SinkDSMDLRInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDLRInvestmentBlock_availability_inc(demand_dsm_2)_: +c_u_SinkDSMDLRInvestmentBlock_availability_inc(demand_dsm_0_2)_: +1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_2) +1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_2) +1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_2) +1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_2) --0.5 SinkDSMDLRInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 c_e_SinkDSMDLRInvestmentBlock_dr_storage_red(demand_dsm_0)_: +1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_0) @@ -264,37 +260,44 @@ c_e_SinkDSMDLRInvestmentBlock_dr_storage_inc(demand_dsm_2)_: -1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_2) = 0 -c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(demand_dsm_0)_: +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(demand_dsm_0_0)_: +1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_0) --0.5 SinkDSMDLRInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(demand_dsm_1)_: +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(demand_dsm_0_1)_: +1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_1) --0.5 SinkDSMDLRInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(demand_dsm_2)_: +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(demand_dsm_0_2)_: +1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_2) --0.5 SinkDSMDLRInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(demand_dsm_0)_: +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(demand_dsm_0_0)_: +1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_0) --0.5 SinkDSMDLRInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(demand_dsm_1)_: +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(demand_dsm_0_1)_: +1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_1) --0.5 SinkDSMDLRInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(demand_dsm_2)_: +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(demand_dsm_0_2)_: +1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_2) --0.5 SinkDSMDLRInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_yearly_limit_shed(demand_dsm_0)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_2) +-50 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(demand_dsm_0)_: +c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(demand_dsm_0_0)_: +1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_0) +1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_0) +1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_0) @@ -304,10 +307,10 @@ c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(demand_dsm_0)_: +1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_0) +1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_0) +1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_0) --0.5 SinkDSMDLRInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(demand_dsm_1)_: +c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(demand_dsm_0_1)_: +1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_1) +1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_1) +1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_1) @@ -317,10 +320,10 @@ c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(demand_dsm_1)_: +1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_1) +1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_1) +1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_1) --0.5 SinkDSMDLRInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(demand_dsm_2)_: +c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(demand_dsm_0_2)_: +1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_2) +1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_2) +1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_2) @@ -330,17 +333,18 @@ c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(demand_dsm_2)_: +1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_2) +1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_2) +1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_2) --0.5 SinkDSMDLRInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(bus_elec_demand_dsm_0) <= +inf - 0 <= flow(bus_elec_demand_dsm_1) <= +inf - 0 <= flow(bus_elec_demand_dsm_2) <= +inf - 33 <= SinkDSMDLRInvestmentBlock_invest(demand_dsm) <= 100 + 0 <= flow(bus_elec_demand_dsm_0_0) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_1) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_2) <= +inf + 33 <= SinkDSMDLRInvestmentBlock_invest(demand_dsm_0) <= 100 + 0 <= SinkDSMDLRInvestmentBlock_total(demand_dsm_0) <= +inf 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_0) <= +inf 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_1) <= +inf 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_2) <= +inf diff --git a/tests/lp_files/dsm_module_oemof_extended.lp b/tests/lp_files/dsm_module_oemof_extended.lp new file mode 100644 index 000000000..74543b81f --- /dev/null +++ b/tests/lp_files/dsm_module_oemof_extended.lp @@ -0,0 +1,100 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++100 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_0) ++100 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_1) ++100 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_0) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_1) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_2) ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_0) ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_1) ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_2) + +s.t. + +c_e_BusBlock_balance(bus_elec_0_0)_: ++1 flow(bus_elec_demand_dsm_0_0) += 0 + +c_e_BusBlock_balance(bus_elec_0_1)_: ++1 flow(bus_elec_demand_dsm_0_1) += 0 + +c_e_BusBlock_balance(bus_elec_0_2)_: ++1 flow(bus_elec_demand_dsm_0_2) += 0 + +c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_0_0)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_0) +-1 SinkDSMOemofBlock_dsm_up(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_0) += 1 + +c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_0_1)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_1) +-1 SinkDSMOemofBlock_dsm_up(demand_dsm_1) ++1 flow(bus_elec_demand_dsm_0_1) += 0.90000000000000002 + +c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_0_2)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_2) +-1 SinkDSMOemofBlock_dsm_up(demand_dsm_2) ++1 flow(bus_elec_demand_dsm_0_2) += 0.80000000000000004 + +c_u_SinkDSMOemofBlock_dsm_up_constraint(demand_dsm_0)_: ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_0) +<= 0.5 + +c_u_SinkDSMOemofBlock_dsm_up_constraint(demand_dsm_1)_: ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_1) +<= 0.40000000000000002 + +c_u_SinkDSMOemofBlock_dsm_up_constraint(demand_dsm_2)_: ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_2) +<= 0.5 + +c_u_SinkDSMOemofBlock_dsm_down_constraint(demand_dsm_0)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_0) +<= 0.29999999999999999 + +c_u_SinkDSMOemofBlock_dsm_down_constraint(demand_dsm_1)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_1) +<= 0.29999999999999999 + +c_u_SinkDSMOemofBlock_dsm_down_constraint(demand_dsm_2)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_2) +<= 0.40000000000000002 + +c_e_SinkDSMOemofBlock_dsm_sum_constraint(demand_dsm_0)_: +-1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_0) +-1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_1) ++0.98999999999999999 SinkDSMOemofBlock_dsm_up(demand_dsm_0) ++0.98999999999999999 SinkDSMOemofBlock_dsm_up(demand_dsm_1) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(bus_elec_demand_dsm_0_0) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_1) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_2) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shift(demand_dsm_0) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shift(demand_dsm_1) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shift(demand_dsm_2) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shed(demand_dsm_0) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shed(demand_dsm_1) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shed(demand_dsm_2) <= +inf + 0 <= SinkDSMOemofBlock_dsm_up(demand_dsm_0) <= +inf + 0 <= SinkDSMOemofBlock_dsm_up(demand_dsm_1) <= +inf + 0 <= SinkDSMOemofBlock_dsm_up(demand_dsm_2) <= +inf +end diff --git a/tests/lp_files/dsm_module_oemof_invest.lp b/tests/lp_files/dsm_module_oemof_invest.lp index 438163537..a0be361aa 100644 --- a/tests/lp_files/dsm_module_oemof_invest.lp +++ b/tests/lp_files/dsm_module_oemof_invest.lp @@ -1,93 +1,92 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+2 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_0) -+2 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_1) -+2 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_2) ++100 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_0) ++100 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_1) ++100 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_0) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_1) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_2) ++1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_0) ++1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_1) ++1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_2) s.t. -c_e_BusBlock_balance(bus_elec_0)_: -+1 flow(bus_elec_demand_dsm_0) +c_e_BusBlock_balance(bus_elec_0_0)_: ++1 flow(bus_elec_demand_dsm_0_0) = 0 -c_e_BusBlock_balance(bus_elec_1)_: -+1 flow(bus_elec_demand_dsm_1) +c_e_BusBlock_balance(bus_elec_0_1)_: ++1 flow(bus_elec_demand_dsm_0_1) = 0 -c_e_BusBlock_balance(bus_elec_2)_: -+1 flow(bus_elec_demand_dsm_2) +c_e_BusBlock_balance(bus_elec_0_2)_: ++1 flow(bus_elec_demand_dsm_0_2) = 0 -c_e_SinkDSMOemofInvestmentBlock_shift_shed_vars(demand_dsm_0)_: -+1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_0) -= 0 - -c_e_SinkDSMOemofInvestmentBlock_shift_shed_vars(demand_dsm_1)_: -+1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_1) -= 0 - -c_e_SinkDSMOemofInvestmentBlock_shift_shed_vars(demand_dsm_2)_: -+1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_2) -= 0 +c_e_SinkDSMOemofInvestmentBlock_total_dsm_rule(demand_dsm_0)_: +-1 SinkDSMOemofInvestmentBlock_invest(demand_dsm_0) ++1 SinkDSMOemofInvestmentBlock_total(demand_dsm_0) += 50 -c_e_SinkDSMOemofInvestmentBlock_input_output_relation(demand_dsm_0)_: +c_e_SinkDSMOemofInvestmentBlock_input_output_relation(demand_dsm_0_0)_: +1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_0) +1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_0) -1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_0) --1 SinkDSMOemofInvestmentBlock_invest(demand_dsm) -+1 flow(bus_elec_demand_dsm_0) -= 50 +-1 SinkDSMOemofInvestmentBlock_total(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_0) += 0 -c_e_SinkDSMOemofInvestmentBlock_input_output_relation(demand_dsm_1)_: +c_e_SinkDSMOemofInvestmentBlock_input_output_relation(demand_dsm_0_1)_: +1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_1) +1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_1) -1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_1) --1 SinkDSMOemofInvestmentBlock_invest(demand_dsm) -+1 flow(bus_elec_demand_dsm_1) -= 50 +-1 SinkDSMOemofInvestmentBlock_total(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_1) += 0 -c_e_SinkDSMOemofInvestmentBlock_input_output_relation(demand_dsm_2)_: +c_e_SinkDSMOemofInvestmentBlock_input_output_relation(demand_dsm_0_2)_: +1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_2) +1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_2) -1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_2) --1 SinkDSMOemofInvestmentBlock_invest(demand_dsm) -+1 flow(bus_elec_demand_dsm_2) -= 50 +-1 SinkDSMOemofInvestmentBlock_total(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_2) += 0 -c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(demand_dsm_0)_: +c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(demand_dsm_0_0)_: +1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_0) --0.5 SinkDSMOemofInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMOemofInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(demand_dsm_1)_: +c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(demand_dsm_0_1)_: +1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_1) --0.40000000000000002 SinkDSMOemofInvestmentBlock_invest(demand_dsm) -<= 20 +-0.40000000000000002 SinkDSMOemofInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(demand_dsm_2)_: +c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(demand_dsm_0_2)_: +1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_2) --0.5 SinkDSMOemofInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMOemofInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(demand_dsm_0)_: +c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(demand_dsm_0_0)_: +1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_0) +1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_0) --0.5 SinkDSMOemofInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMOemofInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(demand_dsm_1)_: +c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(demand_dsm_0_1)_: +1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_1) +1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_1) --0.40000000000000002 SinkDSMOemofInvestmentBlock_invest(demand_dsm) -<= 20 +-0.40000000000000002 SinkDSMOemofInvestmentBlock_total(demand_dsm_0) +<= 0 -c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(demand_dsm_2)_: +c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(demand_dsm_0_2)_: +1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_2) +1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_2) --0.5 SinkDSMOemofInvestmentBlock_invest(demand_dsm) -<= 25 +-0.5 SinkDSMOemofInvestmentBlock_total(demand_dsm_0) +<= 0 c_e_SinkDSMOemofInvestmentBlock_dsm_sum_constraint(demand_dsm_0)_: -1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_0) @@ -96,14 +95,15 @@ c_e_SinkDSMOemofInvestmentBlock_dsm_sum_constraint(demand_dsm_0)_: +1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_1) = 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(bus_elec_demand_dsm_0) <= +inf - 0 <= flow(bus_elec_demand_dsm_1) <= +inf - 0 <= flow(bus_elec_demand_dsm_2) <= +inf - 33 <= SinkDSMOemofInvestmentBlock_invest(demand_dsm) <= 100 + 0 <= flow(bus_elec_demand_dsm_0_0) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_1) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_2) <= +inf + 33 <= SinkDSMOemofInvestmentBlock_invest(demand_dsm_0) <= 100 + 0 <= SinkDSMOemofInvestmentBlock_total(demand_dsm_0) <= +inf 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_0) <= +inf 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_1) <= +inf 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_2) <= +inf diff --git a/tests/lp_files/flow_invest_with_offset.lp b/tests/lp_files/flow_invest_with_offset.lp index 57d5e2792..c665d03a6 100644 --- a/tests/lp_files/flow_invest_with_offset.lp +++ b/tests/lp_files/flow_invest_with_offset.lp @@ -1,68 +1,74 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+500 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest) -+34 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest) -+25 flow(electricityBus_source_nonconvex_invest_0) -+25 flow(electricityBus_source_nonconvex_invest_1) -+25 flow(electricityBus_source_nonconvex_invest_2) ++500 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) ++34 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_0) ++25 flow(electricityBus_source_nonconvex_invest_0_0) ++25 flow(electricityBus_source_nonconvex_invest_0_1) ++25 flow(electricityBus_source_nonconvex_invest_0_2) s.t. -c_e_BusBlock_balance(electricityBus_0)_: -+1 flow(electricityBus_source_nonconvex_invest_0) +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(electricityBus_source_nonconvex_invest_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: -+1 flow(electricityBus_source_nonconvex_invest_1) +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(electricityBus_source_nonconvex_invest_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: -+1 flow(electricityBus_source_nonconvex_invest_2) +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(electricityBus_source_nonconvex_invest_0_2) = 0 -c_u_InvestmentFlowBlock_minimum_rule(electricityBus_source_nonconvex_invest)_: --1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest) -+15 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest) +c_u_InvestmentFlowBlock_minimum_rule(electricityBus_source_nonconvex_invest_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) ++15 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_0) <= 0 -c_u_InvestmentFlowBlock_maximum_rule(electricityBus_source_nonconvex_invest)_: -+1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest) --20 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest) +c_u_InvestmentFlowBlock_maximum_rule(electricityBus_source_nonconvex_invest_0)_: ++1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) +-20 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_0) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_0)_: --0.80000000000000004 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest) -+1 flow(electricityBus_source_nonconvex_invest_0) +c_e_InvestmentFlowBlock_total_rule(electricityBus_source_nonconvex_invest_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) ++1 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_0_0)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) ++1 flow(electricityBus_source_nonconvex_invest_0_0) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_1)_: --0.80000000000000004 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest) -+1 flow(electricityBus_source_nonconvex_invest_1) +c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_0_1)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) ++1 flow(electricityBus_source_nonconvex_invest_0_1) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_2)_: --0.80000000000000004 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest) -+1 flow(electricityBus_source_nonconvex_invest_2) +c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_0_2)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) ++1 flow(electricityBus_source_nonconvex_invest_0_2) <= 0 c_u_InvestmentFlowBlock_summed_max(electricityBus_source_nonconvex_invest)_: --2.2999999999999998 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest) -+1 flow(electricityBus_source_nonconvex_invest_0) -+1 flow(electricityBus_source_nonconvex_invest_1) -+1 flow(electricityBus_source_nonconvex_invest_2) +-2.2999999999999998 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) ++1 flow(electricityBus_source_nonconvex_invest_0_0) ++1 flow(electricityBus_source_nonconvex_invest_0_1) ++1 flow(electricityBus_source_nonconvex_invest_0_2) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(electricityBus_source_nonconvex_invest_0) <= +inf - 0 <= flow(electricityBus_source_nonconvex_invest_1) <= +inf - 0 <= flow(electricityBus_source_nonconvex_invest_2) <= +inf - 0 <= InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest) <= 20 - 0 <= InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest) <= 1 + 0 <= flow(electricityBus_source_nonconvex_invest_0_0) <= +inf + 0 <= flow(electricityBus_source_nonconvex_invest_0_1) <= +inf + 0 <= flow(electricityBus_source_nonconvex_invest_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) <= 20 + 0 <= InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) <= +inf + 0 <= InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_0) <= 1 binary - InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest) + InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_0) end diff --git a/tests/lp_files/flow_invest_with_offset_no_minimum.lp b/tests/lp_files/flow_invest_with_offset_no_minimum.lp index db04165b6..a138f4bd4 100644 --- a/tests/lp_files/flow_invest_with_offset_no_minimum.lp +++ b/tests/lp_files/flow_invest_with_offset_no_minimum.lp @@ -1,67 +1,73 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+500 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest) -+34 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest) -+25 flow(electricityBus_source_nonconvex_invest_0) -+25 flow(electricityBus_source_nonconvex_invest_1) -+25 flow(electricityBus_source_nonconvex_invest_2) ++500 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) ++34 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_0) ++25 flow(electricityBus_source_nonconvex_invest_0_0) ++25 flow(electricityBus_source_nonconvex_invest_0_1) ++25 flow(electricityBus_source_nonconvex_invest_0_2) s.t. -c_e_BusBlock_balance(electricityBus_0)_: -+1 flow(electricityBus_source_nonconvex_invest_0) +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(electricityBus_source_nonconvex_invest_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: -+1 flow(electricityBus_source_nonconvex_invest_1) +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(electricityBus_source_nonconvex_invest_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: -+1 flow(electricityBus_source_nonconvex_invest_2) +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(electricityBus_source_nonconvex_invest_0_2) = 0 -c_l_InvestmentFlowBlock_minimum_rule(electricityBus_source_nonconvex_invest)_: -+1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest) +c_l_InvestmentFlowBlock_minimum_rule(electricityBus_source_nonconvex_invest_0)_: ++1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) >= 0 -c_u_InvestmentFlowBlock_maximum_rule(electricityBus_source_nonconvex_invest)_: -+1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest) --1234 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest) +c_u_InvestmentFlowBlock_maximum_rule(electricityBus_source_nonconvex_invest_0)_: ++1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) +-1234 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_0) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_0)_: --0.80000000000000004 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest) -+1 flow(electricityBus_source_nonconvex_invest_0) +c_e_InvestmentFlowBlock_total_rule(electricityBus_source_nonconvex_invest_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) ++1 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_0_0)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) ++1 flow(electricityBus_source_nonconvex_invest_0_0) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_1)_: --0.80000000000000004 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest) -+1 flow(electricityBus_source_nonconvex_invest_1) +c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_0_1)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) ++1 flow(electricityBus_source_nonconvex_invest_0_1) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_2)_: --0.80000000000000004 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest) -+1 flow(electricityBus_source_nonconvex_invest_2) +c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_0_2)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) ++1 flow(electricityBus_source_nonconvex_invest_0_2) <= 0 c_u_InvestmentFlowBlock_summed_max(electricityBus_source_nonconvex_invest)_: --2.2999999999999998 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest) -+1 flow(electricityBus_source_nonconvex_invest_0) -+1 flow(electricityBus_source_nonconvex_invest_1) -+1 flow(electricityBus_source_nonconvex_invest_2) +-2.2999999999999998 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) ++1 flow(electricityBus_source_nonconvex_invest_0_0) ++1 flow(electricityBus_source_nonconvex_invest_0_1) ++1 flow(electricityBus_source_nonconvex_invest_0_2) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(electricityBus_source_nonconvex_invest_0) <= +inf - 0 <= flow(electricityBus_source_nonconvex_invest_1) <= +inf - 0 <= flow(electricityBus_source_nonconvex_invest_2) <= +inf - 0 <= InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest) <= 1234 - 0 <= InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest) <= 1 + 0 <= flow(electricityBus_source_nonconvex_invest_0_0) <= +inf + 0 <= flow(electricityBus_source_nonconvex_invest_0_1) <= +inf + 0 <= flow(electricityBus_source_nonconvex_invest_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) <= 1234 + 0 <= InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) <= +inf + 0 <= InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_0) <= 1 binary - InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest) + InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_0) end diff --git a/tests/lp_files/flow_invest_without_offset.lp b/tests/lp_files/flow_invest_without_offset.lp index a25e1aea5..113a2fdc2 100644 --- a/tests/lp_files/flow_invest_without_offset.lp +++ b/tests/lp_files/flow_invest_without_offset.lp @@ -1,67 +1,73 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+500 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest) -+25 flow(electricityBus_sink_nonconvex_invest_0) -+25 flow(electricityBus_sink_nonconvex_invest_1) -+25 flow(electricityBus_sink_nonconvex_invest_2) ++500 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_0) ++25 flow(electricityBus_sink_nonconvex_invest_0_0) ++25 flow(electricityBus_sink_nonconvex_invest_0_1) ++25 flow(electricityBus_sink_nonconvex_invest_0_2) s.t. -c_e_BusBlock_balance(electricityBus_0)_: -+1 flow(electricityBus_sink_nonconvex_invest_0) +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(electricityBus_sink_nonconvex_invest_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: -+1 flow(electricityBus_sink_nonconvex_invest_1) +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(electricityBus_sink_nonconvex_invest_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: -+1 flow(electricityBus_sink_nonconvex_invest_2) +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(electricityBus_sink_nonconvex_invest_0_2) = 0 -c_u_InvestmentFlowBlock_minimum_rule(electricityBus_sink_nonconvex_invest)_: --1 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest) -+15 InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest) +c_u_InvestmentFlowBlock_minimum_rule(electricityBus_sink_nonconvex_invest_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_0) ++15 InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest_0) <= 0 -c_u_InvestmentFlowBlock_maximum_rule(electricityBus_sink_nonconvex_invest)_: -+1 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest) --172 InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest) +c_u_InvestmentFlowBlock_maximum_rule(electricityBus_sink_nonconvex_invest_0)_: ++1 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_0) +-172 InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest_0) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_sink_nonconvex_invest_0)_: --0.80000000000000004 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest) -+1 flow(electricityBus_sink_nonconvex_invest_0) +c_e_InvestmentFlowBlock_total_rule(electricityBus_sink_nonconvex_invest_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_0) ++1 InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_0) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_sink_nonconvex_invest_0_0)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_0) ++1 flow(electricityBus_sink_nonconvex_invest_0_0) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_sink_nonconvex_invest_1)_: --0.80000000000000004 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest) -+1 flow(electricityBus_sink_nonconvex_invest_1) +c_u_InvestmentFlowBlock_max(electricityBus_sink_nonconvex_invest_0_1)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_0) ++1 flow(electricityBus_sink_nonconvex_invest_0_1) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_sink_nonconvex_invest_2)_: --0.80000000000000004 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest) -+1 flow(electricityBus_sink_nonconvex_invest_2) +c_u_InvestmentFlowBlock_max(electricityBus_sink_nonconvex_invest_0_2)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_0) ++1 flow(electricityBus_sink_nonconvex_invest_0_2) <= 0 c_u_InvestmentFlowBlock_summed_max(electricityBus_sink_nonconvex_invest)_: --2.2999999999999998 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest) -+1 flow(electricityBus_sink_nonconvex_invest_0) -+1 flow(electricityBus_sink_nonconvex_invest_1) -+1 flow(electricityBus_sink_nonconvex_invest_2) +-2.2999999999999998 InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_0) ++1 flow(electricityBus_sink_nonconvex_invest_0_0) ++1 flow(electricityBus_sink_nonconvex_invest_0_1) ++1 flow(electricityBus_sink_nonconvex_invest_0_2) <= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(electricityBus_sink_nonconvex_invest_0) <= +inf - 0 <= flow(electricityBus_sink_nonconvex_invest_1) <= +inf - 0 <= flow(electricityBus_sink_nonconvex_invest_2) <= +inf - 0 <= InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest) <= 172 - 0 <= InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest) <= 1 + 0 <= flow(electricityBus_sink_nonconvex_invest_0_0) <= +inf + 0 <= flow(electricityBus_sink_nonconvex_invest_0_1) <= +inf + 0 <= flow(electricityBus_sink_nonconvex_invest_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_0) <= 172 + 0 <= InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_0) <= +inf + 0 <= InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest_0) <= 1 binary - InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest) + InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest_0) end diff --git a/tests/lp_files/storage_invest_all_nonconvex.lp b/tests/lp_files/storage_invest_all_nonconvex.lp index 864233a69..e603a411d 100644 --- a/tests/lp_files/storage_invest_all_nonconvex.lp +++ b/tests/lp_files/storage_invest_all_nonconvex.lp @@ -1,105 +1,120 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+20 GenericInvestmentStorageBlock_invest(storage_all_nonconvex) -+30 GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex) -+10 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex) -+10 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1) -+10 InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex) -+15 InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1) ++20 GenericInvestmentStorageBlock_invest(storage_all_nonconvex_0) ++30 GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_0) ++10 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_0) ++10 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_0) ++10 InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_0) ++15 InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_0) s.t. -c_e_BusBlock_balance(bus1_0)_: --1 flow(bus1_storage_all_nonconvex_0) -+1 flow(storage_all_nonconvex_bus1_0) +c_e_BusBlock_balance(bus1_0_0)_: +-1 flow(bus1_storage_all_nonconvex_0_0) ++1 flow(storage_all_nonconvex_bus1_0_0) = 0 -c_e_BusBlock_balance(bus1_1)_: --1 flow(bus1_storage_all_nonconvex_1) -+1 flow(storage_all_nonconvex_bus1_1) +c_e_BusBlock_balance(bus1_0_1)_: +-1 flow(bus1_storage_all_nonconvex_0_1) ++1 flow(storage_all_nonconvex_bus1_0_1) = 0 -c_e_BusBlock_balance(bus1_2)_: --1 flow(bus1_storage_all_nonconvex_2) -+1 flow(storage_all_nonconvex_bus1_2) +c_e_BusBlock_balance(bus1_0_2)_: +-1 flow(bus1_storage_all_nonconvex_0_2) ++1 flow(storage_all_nonconvex_bus1_0_2) = 0 -c_u_InvestmentFlowBlock_minimum_rule(bus1_storage_all_nonconvex)_: --1 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex) -+5 InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex) +c_u_InvestmentFlowBlock_minimum_rule(bus1_storage_all_nonconvex_0)_: +-1 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_0) ++5 InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_0) <= 0 -c_u_InvestmentFlowBlock_minimum_rule(storage_all_nonconvex_bus1)_: --1 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1) -+8 InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1) +c_u_InvestmentFlowBlock_minimum_rule(storage_all_nonconvex_bus1_0)_: +-1 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_0) ++8 InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_0) <= 0 -c_u_InvestmentFlowBlock_maximum_rule(bus1_storage_all_nonconvex)_: -+1 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex) --30 InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex) +c_u_InvestmentFlowBlock_maximum_rule(bus1_storage_all_nonconvex_0)_: ++1 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_0) +-30 InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_0) <= 0 -c_u_InvestmentFlowBlock_maximum_rule(storage_all_nonconvex_bus1)_: -+1 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1) --20 InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1) +c_u_InvestmentFlowBlock_maximum_rule(storage_all_nonconvex_bus1_0)_: ++1 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_0) +-20 InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_0) <= 0 -c_u_InvestmentFlowBlock_max(bus1_storage_all_nonconvex_0)_: --1 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex) -+1 flow(bus1_storage_all_nonconvex_0) +c_e_InvestmentFlowBlock_total_rule(bus1_storage_all_nonconvex_0)_: +-1 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_0) ++1 InvestmentFlowBlock_total(bus1_storage_all_nonconvex_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_all_nonconvex_bus1_0)_: +-1 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_0) ++1 InvestmentFlowBlock_total(storage_all_nonconvex_bus1_0) += 0 + +c_u_InvestmentFlowBlock_max(bus1_storage_all_nonconvex_0_0)_: +-1 InvestmentFlowBlock_total(bus1_storage_all_nonconvex_0) ++1 flow(bus1_storage_all_nonconvex_0_0) <= 0 -c_u_InvestmentFlowBlock_max(bus1_storage_all_nonconvex_1)_: --1 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex) -+1 flow(bus1_storage_all_nonconvex_1) +c_u_InvestmentFlowBlock_max(bus1_storage_all_nonconvex_0_1)_: +-1 InvestmentFlowBlock_total(bus1_storage_all_nonconvex_0) ++1 flow(bus1_storage_all_nonconvex_0_1) <= 0 -c_u_InvestmentFlowBlock_max(bus1_storage_all_nonconvex_2)_: --1 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex) -+1 flow(bus1_storage_all_nonconvex_2) +c_u_InvestmentFlowBlock_max(bus1_storage_all_nonconvex_0_2)_: +-1 InvestmentFlowBlock_total(bus1_storage_all_nonconvex_0) ++1 flow(bus1_storage_all_nonconvex_0_2) <= 0 -c_u_InvestmentFlowBlock_max(storage_all_nonconvex_bus1_0)_: --1 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1) -+1 flow(storage_all_nonconvex_bus1_0) +c_u_InvestmentFlowBlock_max(storage_all_nonconvex_bus1_0_0)_: +-1 InvestmentFlowBlock_total(storage_all_nonconvex_bus1_0) ++1 flow(storage_all_nonconvex_bus1_0_0) <= 0 -c_u_InvestmentFlowBlock_max(storage_all_nonconvex_bus1_1)_: --1 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1) -+1 flow(storage_all_nonconvex_bus1_1) +c_u_InvestmentFlowBlock_max(storage_all_nonconvex_bus1_0_1)_: +-1 InvestmentFlowBlock_total(storage_all_nonconvex_bus1_0) ++1 flow(storage_all_nonconvex_bus1_0_1) <= 0 -c_u_InvestmentFlowBlock_max(storage_all_nonconvex_bus1_2)_: --1 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1) -+1 flow(storage_all_nonconvex_bus1_2) +c_u_InvestmentFlowBlock_max(storage_all_nonconvex_bus1_0_2)_: +-1 InvestmentFlowBlock_total(storage_all_nonconvex_bus1_0) ++1 flow(storage_all_nonconvex_bus1_0_2) <= 0 +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_all_nonconvex_0)_: +-1 GenericInvestmentStorageBlock_invest(storage_all_nonconvex_0) ++1 GenericInvestmentStorageBlock_total(storage_all_nonconvex_0) += 0 + c_u_GenericInvestmentStorageBlock_init_content_limit(storage_all_nonconvex)_: +1 GenericInvestmentStorageBlock_init_content(storage_all_nonconvex) --1 GenericInvestmentStorageBlock_invest(storage_all_nonconvex) +-1 GenericInvestmentStorageBlock_invest(storage_all_nonconvex_0) <= 0 c_e_GenericInvestmentStorageBlock_balance_first(storage_all_nonconvex)_: -1 GenericInvestmentStorageBlock_init_content(storage_all_nonconvex) +1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_0) --1 flow(bus1_storage_all_nonconvex_0) -+1 flow(storage_all_nonconvex_bus1_0) +-1 flow(bus1_storage_all_nonconvex_0_0) ++1 flow(storage_all_nonconvex_bus1_0_0) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage_all_nonconvex_1)_: +c_e_GenericInvestmentStorageBlock_balance(storage_all_nonconvex_0_1)_: -1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_0) +1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_1) --1 flow(bus1_storage_all_nonconvex_1) -+1 flow(storage_all_nonconvex_bus1_1) +-1 flow(bus1_storage_all_nonconvex_0_1) ++1 flow(storage_all_nonconvex_bus1_0_1) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage_all_nonconvex_2)_: +c_e_GenericInvestmentStorageBlock_balance(storage_all_nonconvex_0_2)_: -1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_1) +1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_2) --1 flow(bus1_storage_all_nonconvex_2) -+1 flow(storage_all_nonconvex_bus1_2) +-1 flow(bus1_storage_all_nonconvex_0_2) ++1 flow(storage_all_nonconvex_bus1_0_2) = 0 c_e_GenericInvestmentStorageBlock_balanced_cstr(storage_all_nonconvex)_: @@ -107,53 +122,56 @@ c_e_GenericInvestmentStorageBlock_balanced_cstr(storage_all_nonconvex)_: +1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_2) = 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage_all_nonconvex_0)_: --1 GenericInvestmentStorageBlock_invest(storage_all_nonconvex) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_all_nonconvex_0_0)_: +1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_0) +-1 GenericInvestmentStorageBlock_total(storage_all_nonconvex_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage_all_nonconvex_1)_: --1 GenericInvestmentStorageBlock_invest(storage_all_nonconvex) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_all_nonconvex_0_1)_: +1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_1) +-1 GenericInvestmentStorageBlock_total(storage_all_nonconvex_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage_all_nonconvex_2)_: --1 GenericInvestmentStorageBlock_invest(storage_all_nonconvex) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_all_nonconvex_0_2)_: +1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_2) +-1 GenericInvestmentStorageBlock_total(storage_all_nonconvex_0) <= 0 -c_l_GenericInvestmentStorageBlock_limit_max(storage_all_nonconvex)_: --1 GenericInvestmentStorageBlock_invest(storage_all_nonconvex) -+100 GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex) +c_l_GenericInvestmentStorageBlock_limit_max(storage_all_nonconvex_0)_: +-1 GenericInvestmentStorageBlock_invest(storage_all_nonconvex_0) ++100 GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_0) >= 0 -c_l_GenericInvestmentStorageBlock_limit_min(storage_all_nonconvex)_: -+1 GenericInvestmentStorageBlock_invest(storage_all_nonconvex) --20 GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex) +c_l_GenericInvestmentStorageBlock_limit_min(storage_all_nonconvex_0)_: ++1 GenericInvestmentStorageBlock_invest(storage_all_nonconvex_0) +-20 GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_0) >= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(bus1_storage_all_nonconvex_0) <= +inf - 0 <= flow(bus1_storage_all_nonconvex_1) <= +inf - 0 <= flow(bus1_storage_all_nonconvex_2) <= +inf - 0 <= flow(storage_all_nonconvex_bus1_0) <= +inf - 0 <= flow(storage_all_nonconvex_bus1_1) <= +inf - 0 <= flow(storage_all_nonconvex_bus1_2) <= +inf - 0 <= InvestmentFlowBlock_invest(bus1_storage_all_nonconvex) <= 30 - 0 <= InvestmentFlowBlock_invest(storage_all_nonconvex_bus1) <= 20 - 0 <= InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex) <= 1 - 0 <= InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1) <= 1 + 0 <= flow(bus1_storage_all_nonconvex_0_0) <= +inf + 0 <= flow(bus1_storage_all_nonconvex_0_1) <= +inf + 0 <= flow(bus1_storage_all_nonconvex_0_2) <= +inf + 0 <= flow(storage_all_nonconvex_bus1_0_0) <= +inf + 0 <= flow(storage_all_nonconvex_bus1_0_1) <= +inf + 0 <= flow(storage_all_nonconvex_bus1_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_0) <= 30 + 0 <= InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_0) <= 20 + 0 <= InvestmentFlowBlock_total(bus1_storage_all_nonconvex_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage_all_nonconvex_bus1_0) <= +inf + 0 <= InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_0) <= 1 + 0 <= InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_0) <= 1 0 <= GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_0) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_1) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_2) <= +inf - 0 <= GenericInvestmentStorageBlock_invest(storage_all_nonconvex) <= 100 + 0 <= GenericInvestmentStorageBlock_invest(storage_all_nonconvex_0) <= 100 + 0 <= GenericInvestmentStorageBlock_total(storage_all_nonconvex_0) <= +inf 0 <= GenericInvestmentStorageBlock_init_content(storage_all_nonconvex) <= +inf - 0 <= GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex) <= 1 + 0 <= GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_0) <= 1 binary - InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex) - InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1) - GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex) + InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_0) + InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_0) + GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_0) end diff --git a/tests/lp_files/storage_invest_with_offset.lp b/tests/lp_files/storage_invest_with_offset.lp index 2a8e01d0f..dfe713b8e 100644 --- a/tests/lp_files/storage_invest_with_offset.lp +++ b/tests/lp_files/storage_invest_with_offset.lp @@ -1,162 +1,180 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+145 GenericInvestmentStorageBlock_invest(storagenon_convex) -+5 GenericInvestmentStorageBlock_invest_status(storagenon_convex) -+56 flow(electricityBus_storagenon_convex_0) -+56 flow(electricityBus_storagenon_convex_1) -+56 flow(electricityBus_storagenon_convex_2) -+24 flow(storagenon_convex_electricityBus_0) -+24 flow(storagenon_convex_electricityBus_1) -+24 flow(storagenon_convex_electricityBus_2) ++145 GenericInvestmentStorageBlock_invest(storage_non_convex_0) ++5 GenericInvestmentStorageBlock_invest_status(storage_non_convex_0) ++56 flow(electricityBus_storage_non_convex_0_0) ++56 flow(electricityBus_storage_non_convex_0_1) ++56 flow(electricityBus_storage_non_convex_0_2) ++24 flow(storage_non_convex_electricityBus_0_0) ++24 flow(storage_non_convex_electricityBus_0_1) ++24 flow(storage_non_convex_electricityBus_0_2) s.t. -c_e_BusBlock_balance(electricityBus_0)_: --1 flow(electricityBus_storagenon_convex_0) -+1 flow(storagenon_convex_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage_non_convex_0_0) ++1 flow(storage_non_convex_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: --1 flow(electricityBus_storagenon_convex_1) -+1 flow(storagenon_convex_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage_non_convex_0_1) ++1 flow(storage_non_convex_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: --1 flow(electricityBus_storagenon_convex_2) -+1 flow(storagenon_convex_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: +-1 flow(electricityBus_storage_non_convex_0_2) ++1 flow(storage_non_convex_electricityBus_0_2) = 0 -c_u_InvestmentFlowBlock_max(electricityBus_storagenon_convex_0)_: --1 InvestmentFlowBlock_invest(electricityBus_storagenon_convex) -+1 flow(electricityBus_storagenon_convex_0) +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage_non_convex_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage_non_convex_0) ++1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_non_convex_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(storage_non_convex_electricityBus_0) ++1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_0_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) ++1 flow(electricityBus_storage_non_convex_0_0) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_storagenon_convex_1)_: --1 InvestmentFlowBlock_invest(electricityBus_storagenon_convex) -+1 flow(electricityBus_storagenon_convex_1) +c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_0_1)_: +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) ++1 flow(electricityBus_storage_non_convex_0_1) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_storagenon_convex_2)_: --1 InvestmentFlowBlock_invest(electricityBus_storagenon_convex) -+1 flow(electricityBus_storagenon_convex_2) +c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_0_2)_: +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) ++1 flow(electricityBus_storage_non_convex_0_2) <= 0 -c_u_InvestmentFlowBlock_max(storagenon_convex_electricityBus_0)_: --1 InvestmentFlowBlock_invest(storagenon_convex_electricityBus) -+1 flow(storagenon_convex_electricityBus_0) +c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) ++1 flow(storage_non_convex_electricityBus_0_0) <= 0 -c_u_InvestmentFlowBlock_max(storagenon_convex_electricityBus_1)_: --1 InvestmentFlowBlock_invest(storagenon_convex_electricityBus) -+1 flow(storagenon_convex_electricityBus_1) +c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) ++1 flow(storage_non_convex_electricityBus_0_1) <= 0 -c_u_InvestmentFlowBlock_max(storagenon_convex_electricityBus_2)_: --1 InvestmentFlowBlock_invest(storagenon_convex_electricityBus) -+1 flow(storagenon_convex_electricityBus_2) +c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_0_2)_: +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) ++1 flow(storage_non_convex_electricityBus_0_2) <= 0 -c_u_GenericInvestmentStorageBlock_init_content_limit(storagenon_convex)_: -+1 GenericInvestmentStorageBlock_init_content(storagenon_convex) --1 GenericInvestmentStorageBlock_invest(storagenon_convex) +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_non_convex_0)_: +-1 GenericInvestmentStorageBlock_invest(storage_non_convex_0) ++1 GenericInvestmentStorageBlock_total(storage_non_convex_0) += 0 + +c_u_GenericInvestmentStorageBlock_init_content_limit(storage_non_convex)_: ++1 GenericInvestmentStorageBlock_init_content(storage_non_convex) +-1 GenericInvestmentStorageBlock_invest(storage_non_convex_0) <= 0 -c_e_GenericInvestmentStorageBlock_balance_first(storagenon_convex)_: --0.87 GenericInvestmentStorageBlock_init_content(storagenon_convex) -+1 GenericInvestmentStorageBlock_storage_content(storagenon_convex_0) --0.96999999999999997 flow(electricityBus_storagenon_convex_0) -+1.1627906976744187 flow(storagenon_convex_electricityBus_0) +c_e_GenericInvestmentStorageBlock_balance_first(storage_non_convex)_: +-0.87 GenericInvestmentStorageBlock_init_content(storage_non_convex) ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_0) +-0.96999999999999997 flow(electricityBus_storage_non_convex_0_0) ++1.1627906976744187 flow(storage_non_convex_electricityBus_0_0) = 0 -c_e_GenericInvestmentStorageBlock_balance(storagenon_convex_1)_: --0.87 GenericInvestmentStorageBlock_storage_content(storagenon_convex_0) -+1 GenericInvestmentStorageBlock_storage_content(storagenon_convex_1) --0.96999999999999997 flow(electricityBus_storagenon_convex_1) -+1.1627906976744187 flow(storagenon_convex_electricityBus_1) +c_e_GenericInvestmentStorageBlock_balance(storage_non_convex_0_1)_: +-0.87 GenericInvestmentStorageBlock_storage_content(storage_non_convex_0) ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) +-0.96999999999999997 flow(electricityBus_storage_non_convex_0_1) ++1.1627906976744187 flow(storage_non_convex_electricityBus_0_1) = 0 -c_e_GenericInvestmentStorageBlock_balance(storagenon_convex_2)_: --0.87 GenericInvestmentStorageBlock_storage_content(storagenon_convex_1) -+1 GenericInvestmentStorageBlock_storage_content(storagenon_convex_2) --0.96999999999999997 flow(electricityBus_storagenon_convex_2) -+1.1627906976744187 flow(storagenon_convex_electricityBus_2) +c_e_GenericInvestmentStorageBlock_balance(storage_non_convex_0_2)_: +-0.87 GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) +-0.96999999999999997 flow(electricityBus_storage_non_convex_0_2) ++1.1627906976744187 flow(storage_non_convex_electricityBus_0_2) = 0 -c_e_GenericInvestmentStorageBlock_balanced_cstr(storagenon_convex)_: --1 GenericInvestmentStorageBlock_init_content(storagenon_convex) -+1 GenericInvestmentStorageBlock_storage_content(storagenon_convex_2) +c_e_GenericInvestmentStorageBlock_balanced_cstr(storage_non_convex)_: +-1 GenericInvestmentStorageBlock_init_content(storage_non_convex) ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) = 0 -c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storagenon_convex)_: --0.16666666666666666 GenericInvestmentStorageBlock_invest(storagenon_convex) -+1 InvestmentFlowBlock_invest(electricityBus_storagenon_convex) +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_non_convex_0)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage_non_convex_0) ++1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) = 0 -c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storagenon_convex)_: --0.16666666666666666 GenericInvestmentStorageBlock_invest(storagenon_convex) -+1 InvestmentFlowBlock_invest(storagenon_convex_electricityBus) +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_non_convex_0)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage_non_convex_0) ++1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) = 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storagenon_convex_0)_: --0.90000000000000002 GenericInvestmentStorageBlock_invest(storagenon_convex) -+1 GenericInvestmentStorageBlock_storage_content(storagenon_convex_0) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_0_0)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_0) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage_non_convex_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storagenon_convex_1)_: --0.90000000000000002 GenericInvestmentStorageBlock_invest(storagenon_convex) -+1 GenericInvestmentStorageBlock_storage_content(storagenon_convex_1) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_0_1)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage_non_convex_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storagenon_convex_2)_: --0.90000000000000002 GenericInvestmentStorageBlock_invest(storagenon_convex) -+1 GenericInvestmentStorageBlock_storage_content(storagenon_convex_2) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_0_2)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage_non_convex_0) <= 0 -c_u_GenericInvestmentStorageBlock_min_storage_content(storagenon_convex_0)_: -+0.10000000000000001 GenericInvestmentStorageBlock_invest(storagenon_convex) --1 GenericInvestmentStorageBlock_storage_content(storagenon_convex_0) +c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_0_0)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_0) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage_non_convex_0) <= 0 -c_u_GenericInvestmentStorageBlock_min_storage_content(storagenon_convex_1)_: -+0.10000000000000001 GenericInvestmentStorageBlock_invest(storagenon_convex) --1 GenericInvestmentStorageBlock_storage_content(storagenon_convex_1) +c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_0_1)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage_non_convex_0) <= 0 -c_u_GenericInvestmentStorageBlock_min_storage_content(storagenon_convex_2)_: -+0.10000000000000001 GenericInvestmentStorageBlock_invest(storagenon_convex) --1 GenericInvestmentStorageBlock_storage_content(storagenon_convex_2) +c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_0_2)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage_non_convex_0) <= 0 -c_l_GenericInvestmentStorageBlock_limit_max(storagenon_convex)_: --1 GenericInvestmentStorageBlock_invest(storagenon_convex) -+1454 GenericInvestmentStorageBlock_invest_status(storagenon_convex) +c_l_GenericInvestmentStorageBlock_limit_max(storage_non_convex_0)_: +-1 GenericInvestmentStorageBlock_invest(storage_non_convex_0) ++1454 GenericInvestmentStorageBlock_invest_status(storage_non_convex_0) >= 0 -c_l_GenericInvestmentStorageBlock_limit_min(storagenon_convex)_: -+1 GenericInvestmentStorageBlock_invest(storagenon_convex) --19 GenericInvestmentStorageBlock_invest_status(storagenon_convex) +c_l_GenericInvestmentStorageBlock_limit_min(storage_non_convex_0)_: ++1 GenericInvestmentStorageBlock_invest(storage_non_convex_0) +-19 GenericInvestmentStorageBlock_invest_status(storage_non_convex_0) >= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(electricityBus_storagenon_convex_0) <= +inf - 0 <= flow(electricityBus_storagenon_convex_1) <= +inf - 0 <= flow(electricityBus_storagenon_convex_2) <= +inf - 0 <= flow(storagenon_convex_electricityBus_0) <= +inf - 0 <= flow(storagenon_convex_electricityBus_1) <= +inf - 0 <= flow(storagenon_convex_electricityBus_2) <= +inf - 0 <= InvestmentFlowBlock_invest(electricityBus_storagenon_convex) <= +inf - 0 <= InvestmentFlowBlock_invest(storagenon_convex_electricityBus) <= +inf - 0 <= GenericInvestmentStorageBlock_storage_content(storagenon_convex_0) <= +inf - 0 <= GenericInvestmentStorageBlock_storage_content(storagenon_convex_1) <= +inf - 0 <= GenericInvestmentStorageBlock_storage_content(storagenon_convex_2) <= +inf - 0 <= GenericInvestmentStorageBlock_invest(storagenon_convex) <= 1454 - 0 <= GenericInvestmentStorageBlock_init_content(storagenon_convex) <= +inf - 0 <= GenericInvestmentStorageBlock_invest_status(storagenon_convex) <= 1 + 0 <= flow(electricityBus_storage_non_convex_0_0) <= +inf + 0 <= flow(electricityBus_storage_non_convex_0_1) <= +inf + 0 <= flow(electricityBus_storage_non_convex_0_2) <= +inf + 0 <= flow(storage_non_convex_electricityBus_0_0) <= +inf + 0 <= flow(storage_non_convex_electricityBus_0_1) <= +inf + 0 <= flow(storage_non_convex_electricityBus_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage_non_convex_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage_non_convex_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_non_convex_0) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage_non_convex_0) <= 1454 + 0 <= GenericInvestmentStorageBlock_total(storage_non_convex_0) <= +inf + 0 <= GenericInvestmentStorageBlock_init_content(storage_non_convex) <= +inf + 0 <= GenericInvestmentStorageBlock_invest_status(storage_non_convex_0) <= 1 binary - GenericInvestmentStorageBlock_invest_status(storagenon_convex) + GenericInvestmentStorageBlock_invest_status(storage_non_convex_0) end diff --git a/tests/lp_files/storage_invest_without_offset.lp b/tests/lp_files/storage_invest_without_offset.lp index f9d4f4607..128f4afda 100644 --- a/tests/lp_files/storage_invest_without_offset.lp +++ b/tests/lp_files/storage_invest_without_offset.lp @@ -1,86 +1,101 @@ \* Source Pyomo model name=Model *\ -min +min objective: -+141 GenericInvestmentStorageBlock_invest(storage_non_convex) -+56 flow(electricityBus_storage_non_convex_0) -+56 flow(electricityBus_storage_non_convex_1) -+56 flow(electricityBus_storage_non_convex_2) -+24 flow(storage_non_convex_electricityBus_0) -+24 flow(storage_non_convex_electricityBus_1) -+24 flow(storage_non_convex_electricityBus_2) ++141 GenericInvestmentStorageBlock_invest(storage_non_convex_0) ++56 flow(electricityBus_storage_non_convex_0_0) ++56 flow(electricityBus_storage_non_convex_0_1) ++56 flow(electricityBus_storage_non_convex_0_2) ++24 flow(storage_non_convex_electricityBus_0_0) ++24 flow(storage_non_convex_electricityBus_0_1) ++24 flow(storage_non_convex_electricityBus_0_2) s.t. -c_e_BusBlock_balance(electricityBus_0)_: --1 flow(electricityBus_storage_non_convex_0) -+1 flow(storage_non_convex_electricityBus_0) +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage_non_convex_0_0) ++1 flow(storage_non_convex_electricityBus_0_0) = 0 -c_e_BusBlock_balance(electricityBus_1)_: --1 flow(electricityBus_storage_non_convex_1) -+1 flow(storage_non_convex_electricityBus_1) +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage_non_convex_0_1) ++1 flow(storage_non_convex_electricityBus_0_1) = 0 -c_e_BusBlock_balance(electricityBus_2)_: --1 flow(electricityBus_storage_non_convex_2) -+1 flow(storage_non_convex_electricityBus_2) +c_e_BusBlock_balance(electricityBus_0_2)_: +-1 flow(electricityBus_storage_non_convex_0_2) ++1 flow(storage_non_convex_electricityBus_0_2) = 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_0)_: --1 InvestmentFlowBlock_invest(electricityBus_storage_non_convex) -+1 flow(electricityBus_storage_non_convex_0) +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage_non_convex_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage_non_convex_0) ++1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_non_convex_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(storage_non_convex_electricityBus_0) ++1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_0_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) ++1 flow(electricityBus_storage_non_convex_0_0) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_1)_: --1 InvestmentFlowBlock_invest(electricityBus_storage_non_convex) -+1 flow(electricityBus_storage_non_convex_1) +c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_0_1)_: +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) ++1 flow(electricityBus_storage_non_convex_0_1) <= 0 -c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_2)_: --1 InvestmentFlowBlock_invest(electricityBus_storage_non_convex) -+1 flow(electricityBus_storage_non_convex_2) +c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_0_2)_: +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) ++1 flow(electricityBus_storage_non_convex_0_2) <= 0 -c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_0)_: --1 InvestmentFlowBlock_invest(storage_non_convex_electricityBus) -+1 flow(storage_non_convex_electricityBus_0) +c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) ++1 flow(storage_non_convex_electricityBus_0_0) <= 0 -c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_1)_: --1 InvestmentFlowBlock_invest(storage_non_convex_electricityBus) -+1 flow(storage_non_convex_electricityBus_1) +c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) ++1 flow(storage_non_convex_electricityBus_0_1) <= 0 -c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_2)_: --1 InvestmentFlowBlock_invest(storage_non_convex_electricityBus) -+1 flow(storage_non_convex_electricityBus_2) +c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_0_2)_: +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) ++1 flow(storage_non_convex_electricityBus_0_2) <= 0 +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_non_convex_0)_: +-1 GenericInvestmentStorageBlock_invest(storage_non_convex_0) ++1 GenericInvestmentStorageBlock_total(storage_non_convex_0) += 0 + c_u_GenericInvestmentStorageBlock_init_content_limit(storage_non_convex)_: +1 GenericInvestmentStorageBlock_init_content(storage_non_convex) --1 GenericInvestmentStorageBlock_invest(storage_non_convex) +-1 GenericInvestmentStorageBlock_invest(storage_non_convex_0) <= 0 c_e_GenericInvestmentStorageBlock_balance_first(storage_non_convex)_: -0.87 GenericInvestmentStorageBlock_init_content(storage_non_convex) +1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_0) --0.96999999999999997 flow(electricityBus_storage_non_convex_0) -+1.1627906976744187 flow(storage_non_convex_electricityBus_0) +-0.96999999999999997 flow(electricityBus_storage_non_convex_0_0) ++1.1627906976744187 flow(storage_non_convex_electricityBus_0_0) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage_non_convex_1)_: +c_e_GenericInvestmentStorageBlock_balance(storage_non_convex_0_1)_: -0.87 GenericInvestmentStorageBlock_storage_content(storage_non_convex_0) +1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) --0.96999999999999997 flow(electricityBus_storage_non_convex_1) -+1.1627906976744187 flow(storage_non_convex_electricityBus_1) +-0.96999999999999997 flow(electricityBus_storage_non_convex_0_1) ++1.1627906976744187 flow(storage_non_convex_electricityBus_0_1) = 0 -c_e_GenericInvestmentStorageBlock_balance(storage_non_convex_2)_: +c_e_GenericInvestmentStorageBlock_balance(storage_non_convex_0_2)_: -0.87 GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) +1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) --0.96999999999999997 flow(electricityBus_storage_non_convex_2) -+1.1627906976744187 flow(storage_non_convex_electricityBus_2) +-0.96999999999999997 flow(electricityBus_storage_non_convex_0_2) ++1.1627906976744187 flow(storage_non_convex_electricityBus_0_2) = 0 c_e_GenericInvestmentStorageBlock_balanced_cstr(storage_non_convex)_: @@ -88,74 +103,77 @@ c_e_GenericInvestmentStorageBlock_balanced_cstr(storage_non_convex)_: +1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) = 0 -c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_non_convex)_: --0.16666666666666666 GenericInvestmentStorageBlock_invest(storage_non_convex) -+1 InvestmentFlowBlock_invest(electricityBus_storage_non_convex) +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_non_convex_0)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage_non_convex_0) ++1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) = 0 -c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_non_convex)_: --0.16666666666666666 GenericInvestmentStorageBlock_invest(storage_non_convex) -+1 InvestmentFlowBlock_invest(storage_non_convex_electricityBus) +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_non_convex_0)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage_non_convex_0) ++1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) = 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_0)_: --0.90000000000000002 GenericInvestmentStorageBlock_invest(storage_non_convex) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_0_0)_: +1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_0) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage_non_convex_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_1)_: --0.90000000000000002 GenericInvestmentStorageBlock_invest(storage_non_convex) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_0_1)_: +1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage_non_convex_0) <= 0 -c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_2)_: --0.90000000000000002 GenericInvestmentStorageBlock_invest(storage_non_convex) +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_0_2)_: +1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage_non_convex_0) <= 0 -c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_0)_: -+0.10000000000000001 GenericInvestmentStorageBlock_invest(storage_non_convex) +c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_0_0)_: -1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_0) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage_non_convex_0) <= 0 -c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_1)_: -+0.10000000000000001 GenericInvestmentStorageBlock_invest(storage_non_convex) +c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_0_1)_: -1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage_non_convex_0) <= 0 -c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_2)_: -+0.10000000000000001 GenericInvestmentStorageBlock_invest(storage_non_convex) +c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_0_2)_: -1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage_non_convex_0) <= 0 -c_l_GenericInvestmentStorageBlock_limit_max(storage_non_convex)_: --1 GenericInvestmentStorageBlock_invest(storage_non_convex) -+244 GenericInvestmentStorageBlock_invest_status(storage_non_convex) +c_l_GenericInvestmentStorageBlock_limit_max(storage_non_convex_0)_: +-1 GenericInvestmentStorageBlock_invest(storage_non_convex_0) ++244 GenericInvestmentStorageBlock_invest_status(storage_non_convex_0) >= 0 -c_l_GenericInvestmentStorageBlock_limit_min(storage_non_convex)_: -+1 GenericInvestmentStorageBlock_invest(storage_non_convex) --12 GenericInvestmentStorageBlock_invest_status(storage_non_convex) +c_l_GenericInvestmentStorageBlock_limit_min(storage_non_convex_0)_: ++1 GenericInvestmentStorageBlock_invest(storage_non_convex_0) +-12 GenericInvestmentStorageBlock_invest_status(storage_non_convex_0) >= 0 -c_e_ONE_VAR_CONSTANT: +c_e_ONE_VAR_CONSTANT: ONE_VAR_CONSTANT = 1.0 bounds - 0 <= flow(electricityBus_storage_non_convex_0) <= +inf - 0 <= flow(electricityBus_storage_non_convex_1) <= +inf - 0 <= flow(electricityBus_storage_non_convex_2) <= +inf - 0 <= flow(storage_non_convex_electricityBus_0) <= +inf - 0 <= flow(storage_non_convex_electricityBus_1) <= +inf - 0 <= flow(storage_non_convex_electricityBus_2) <= +inf - 0 <= InvestmentFlowBlock_invest(electricityBus_storage_non_convex) <= +inf - 0 <= InvestmentFlowBlock_invest(storage_non_convex_electricityBus) <= +inf + 0 <= flow(electricityBus_storage_non_convex_0_0) <= +inf + 0 <= flow(electricityBus_storage_non_convex_0_1) <= +inf + 0 <= flow(electricityBus_storage_non_convex_0_2) <= +inf + 0 <= flow(storage_non_convex_electricityBus_0_0) <= +inf + 0 <= flow(storage_non_convex_electricityBus_0_1) <= +inf + 0 <= flow(storage_non_convex_electricityBus_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage_non_convex_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage_non_convex_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage_non_convex_0) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) <= +inf 0 <= GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) <= +inf - 0 <= GenericInvestmentStorageBlock_invest(storage_non_convex) <= 244 + 0 <= GenericInvestmentStorageBlock_invest(storage_non_convex_0) <= 244 + 0 <= GenericInvestmentStorageBlock_total(storage_non_convex_0) <= +inf 0 <= GenericInvestmentStorageBlock_init_content(storage_non_convex) <= +inf - 0 <= GenericInvestmentStorageBlock_invest_status(storage_non_convex) <= 1 + 0 <= GenericInvestmentStorageBlock_invest_status(storage_non_convex_0) <= 1 binary - GenericInvestmentStorageBlock_invest_status(storage_non_convex) + GenericInvestmentStorageBlock_invest_status(storage_non_convex_0) end diff --git a/tests/test_components.py b/tests/test_components.py index d3a66c353..b139adbda 100644 --- a/tests/test_components.py +++ b/tests/test_components.py @@ -147,7 +147,7 @@ def test_generic_storage_with_non_convex_investment(): def test_generic_storage_with_non_convex_invest_maximum(): """No investment maximum at nonconvex investment.""" with pytest.raises( - AttributeError, match=r"Please provide an maximum investment value" + AttributeError, match=r"Please provide a maximum investment value" ): bel = Bus() components.GenericStorage( From bf4b956697bc2bd80a889a35640cff4c5c2af283 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 19 Feb 2022 17:28:21 +0100 Subject: [PATCH 0144/1363] Fix handling standard model --- src/oemof/solph/_models.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index e7d68860f..2c04d2e24 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -15,6 +15,7 @@ import logging import warnings +import pandas as pd from oemof.tools import debugging from pyomo import environ as po from pyomo.core.plugins.transform.relax_integrality import RelaxIntegrality @@ -184,7 +185,7 @@ def results(self): """Returns a nested dictionary of the results of this optimization""" return processing.results(self) - def solve(self, solver="cbc", solver_io="lp", **kwargs): + def solve(self, solver="gurobi", solver_io="lp", **kwargs): r"""Takes care of communication with solver to solve the model. Parameters @@ -317,7 +318,7 @@ def _add_parent_block_sets(self): self.TIMEINDEX = po.Set( initialize=list( zip( - [0] * len(self.es.timeindex.year), + [0] * len(self.es.timeindex), range(len(self.es.timeindex)), ) ), From de4cc4e3d666ef2162906f6a1c5722a0fc3f58d4 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 19 Feb 2022 17:28:41 +0100 Subject: [PATCH 0145/1363] Fix PEP8 stuff --- src/oemof/solph/components/experimental/_sink_dsm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index f59436ac8..ed7848bbc 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -2174,7 +2174,7 @@ def _dsm_investvar_bound_rule(block, g, p): if m.es.multi_period: # Old capacity to be decommissioned (due to lifetime) - # Old capacity is built out of old exogenous and endogenous capacities + # Old capacity built out of old exogenous and endogenous capacities self.old = Var(self.investdsm, m.PERIODS, within=NonNegativeReals) # Old endogenous capacity to be decommissioned (due to lifetime) From 83e189896449facf4b25f1982287a0da88539d0c Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 19 Feb 2022 17:28:54 +0100 Subject: [PATCH 0146/1363] Fix storage results processing --- src/oemof/solph/processing.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index 19dac720d..605ffc347 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -249,7 +249,6 @@ def _extract_standard_model_result( df_dict[k] = df_dict[k].pivot(columns="variable_name", values="value") try: # Enable reindexing by replacing period and timestep indices - print(k) df_dict[k] = _replace_non_timeindex_indices( df_dict[k], period_indexed, @@ -324,7 +323,9 @@ def _replace_non_timeindex_indices( if not period_indexed_df.empty: to_concat.append(period_indexed_df) - timestep_indexed_df = timestep_indexed_df.dropna() + # Handle storages differently + if "storage_content" not in timestep_indexed_df.columns: + timestep_indexed_df = timestep_indexed_df.dropna() timestep_indexed_df = timestep_indexed_df.rename(index=rename_dict) if not timestep_indexed_df.empty: to_concat.append(timestep_indexed_df) From 75e00cbcb9c652baf1635a020cda4194eb34b8dc Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 19 Feb 2022 17:30:35 +0100 Subject: [PATCH 0147/1363] Fix standard solver --- src/oemof/solph/_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 2c04d2e24..58262eeae 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -185,7 +185,7 @@ def results(self): """Returns a nested dictionary of the results of this optimization""" return processing.results(self) - def solve(self, solver="gurobi", solver_io="lp", **kwargs): + def solve(self, solver="cbc", solver_io="lp", **kwargs): r"""Takes care of communication with solver to solve the model. Parameters From 274f82a7fe2421634187dddb7923b866e1a90884 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 19 Feb 2022 17:34:27 +0100 Subject: [PATCH 0148/1363] Remove unnecessary imports --- src/oemof/solph/_models.py | 1 - src/oemof/solph/_options.py | 2 -- src/oemof/solph/processing.py | 1 - 3 files changed, 4 deletions(-) diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 58262eeae..2fa3d7aa9 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -15,7 +15,6 @@ import logging import warnings -import pandas as pd from oemof.tools import debugging from pyomo import environ as po from pyomo.core.plugins.transform.relax_integrality import RelaxIntegrality diff --git a/src/oemof/solph/_options.py b/src/oemof/solph/_options.py index fa4bece41..ba7cbb06b 100644 --- a/src/oemof/solph/_options.py +++ b/src/oemof/solph/_options.py @@ -13,9 +13,7 @@ SPDX-License-Identifier: MIT """ -from warnings import warn -from oemof.tools import debugging from oemof.solph._plumbing import sequence diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index 605ffc347..f60b1b6d6 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -18,7 +18,6 @@ import sys from itertools import groupby -import numpy as np import pandas as pd from oemof.network.network import Node from pyomo.core.base.piecewise import IndexedPiecewise From 9ddba3e45ff056020be4a818cf2c7989c8cd97de Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 19 Feb 2022 17:48:47 +0100 Subject: [PATCH 0149/1363] Update tests --- tests/test_constraints_module.py | 4 ++-- tests/test_processing.py | 25 +++++++++++++++++-------- tests/test_solph_network_classes.py | 17 ++++++++++++++--- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/tests/test_constraints_module.py b/tests/test_constraints_module.py index b1bca0249..c3efd63c4 100644 --- a/tests/test_constraints_module.py +++ b/tests/test_constraints_module.py @@ -54,7 +54,7 @@ def test_something_else(): line21 = energysystem.groups["powerline_2_1"] solph.constraints.equate_variables( om, - om.InvestmentFlowBlock.invest[line12, bel2], - om.InvestmentFlowBlock.invest[line21, bel1], + om.InvestmentFlowBlock.invest[line12, bel2, 0], + om.InvestmentFlowBlock.invest[line21, bel1, 0], name="my_name", ) diff --git a/tests/test_processing.py b/tests/test_processing.py index 3d52557eb..434f7a37f 100644 --- a/tests/test_processing.py +++ b/tests/test_processing.py @@ -116,7 +116,9 @@ def test_flows_without_none_exclusion(self): param_results = processing.parameter_as_dict( self.es, exclude_none=False ) - scalar_attributes = { + default_attributes = { + "age": None, + "lifetime": None, "integer": None, "investment": None, "nominal_value": 1, @@ -128,13 +130,14 @@ def test_flows_without_none_exclusion(self): "negative_gradient_ub": None, "positive_gradient_ub": None, "variable_costs": 0, + "fixed_costs": None, "flow": None, "values": None, "label": str(b_el2.outputs[demand].label), } assert_series_equal( param_results[(b_el2, demand)]["scalars"].sort_index(), - pandas.Series(scalar_attributes).sort_index(), + pandas.Series(default_attributes).sort_index(), ) sequences_attributes = { "fix": self.demand_values, @@ -159,13 +162,16 @@ def test_nodes_with_none_exclusion(self): "initial_storage_level": 0, "invest_relation_input_capacity": 1 / 6, "invest_relation_output_capacity": 1 / 6, - "investment_ep_costs": 0.4, + "investment_age": 0, "investment_existing": 0, + "investment_interest_rate": 0, + "investment_nonconvex": False, + "investment_ep_costs": 0.4, "investment_maximum": float("inf"), "investment_minimum": 0, - "investment_nonconvex": False, "investment_offset": 0, "label": "storage", + "fixed_costs": 0, "fixed_losses_absolute": 0, "fixed_losses_relative": 0, "inflow_conversion_factor": 1, @@ -195,13 +201,16 @@ def test_nodes_with_none_exclusion_old_name(self): "initial_storage_level": 0, "invest_relation_input_capacity": 1 / 6, "invest_relation_output_capacity": 1 / 6, - "investment_ep_costs": 0.4, + "investment_age": 0, "investment_existing": 0, + "investment_interest_rate": 0, + "investment_nonconvex": False, + "investment_ep_costs": 0.4, "investment_maximum": float("inf"), "investment_minimum": 0, - "investment_nonconvex": False, "investment_offset": 0, "label": "storage", + "fixed_costs": 0, "fixed_losses_absolute": 0, "fixed_losses_relative": 0, "inflow_conversion_factor": 1, @@ -256,7 +265,7 @@ def test_multiindex_sequences(self): def test_error_from_nan_values(self): trsf = self.es.groups["diesel"] bus = self.es.groups["b_el1"] - self.mod.flow[trsf, bus, 5] = float("nan") + self.mod.flow[trsf, bus, 0, 5] = float("nan") with assert_raises(ValueError): processing.results(self.mod) @@ -270,7 +279,7 @@ def test_node_weight_by_type(self): storage_content = views.node_weight_by_type( results, node_type=GenericStorage ) - eq_(round(float(storage_content.sum()), 6), 1437.500003) + eq_(round(float(storage_content.sum()), 1), 1437.5) def test_output_by_type_view(self): results = processing.results(self.om) diff --git a/tests/test_solph_network_classes.py b/tests/test_solph_network_classes.py index 10e114f09..0e19520a6 100644 --- a/tests/test_solph_network_classes.py +++ b/tests/test_solph_network_classes.py @@ -76,10 +76,21 @@ def test_wrong_combination_of_options(): ) -def test_error_of_deprecated_fixed_costs(): - msg = "The `fixed_costs` attribute has been removed with v0.2!" - with pytest.raises(AttributeError, match=msg): +def test_fixed_costs_warning(): + msg = ( + "Be aware that the fixed costs attribute is only\n" + "meant to be used for multi-period models.\n" + "If you wish to set up a multi-period model, set the" + " multi_period attribute of your energy system to True.\n" + "It has been decided to remove the `fixed_costs` " + "attribute with v0.2 for regular uses.\n" + "If you specify `fixed_costs` for a regular model, " + "it will simply be ignored." + ) + with warnings.catch_warnings(record=True) as w: solph.flows.Flow(fixed_costs=34) + assert len(w) != 0 + assert msg == str(w[-1].message) def test_flow_with_fix_and_min_max(): From 305acb6f21b92152c862c0131d65acd4a70d704c Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 19 Feb 2022 17:50:39 +0100 Subject: [PATCH 0150/1363] Format using black --- src/oemof/solph/flows/_non_convex_flow.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/oemof/solph/flows/_non_convex_flow.py b/src/oemof/solph/flows/_non_convex_flow.py index ec909fc06..035877305 100644 --- a/src/oemof/solph/flows/_non_convex_flow.py +++ b/src/oemof/solph/flows/_non_convex_flow.py @@ -426,12 +426,16 @@ def _create(self, group=None): if self.POSITIVE_GRADIENT_FLOWS: self.positive_gradient = Var( - self.POSITIVE_GRADIENT_FLOWS, m.TIMESTEPS, within=NonNegativeReals + self.POSITIVE_GRADIENT_FLOWS, + m.TIMESTEPS, + within=NonNegativeReals, ) if self.NEGATIVE_GRADIENT_FLOWS: self.negative_gradient = Var( - self.NEGATIVE_GRADIENT_FLOWS, m.TIMESTEPS, within=NonNegativeReals + self.NEGATIVE_GRADIENT_FLOWS, + m.TIMESTEPS, + within=NonNegativeReals, ) # set upper bound of gradient variable @@ -439,12 +443,14 @@ def _create(self, group=None): if m.flows[i, o].nonconvex.positive_gradient["ub"][0] is not None: for t in m.TIMESTEPS: self.positive_gradient[i, o, t].setub( - f.nonconvex.positive_gradient["ub"][t] * f.nominal_value + f.nonconvex.positive_gradient["ub"][t] + * f.nominal_value ) if m.flows[i, o].nonconvex.negative_gradient["ub"][0] is not None: for t in m.TIMESTEPS: self.negative_gradient[i, o, t].setub( - f.nonconvex.negative_gradient["ub"][t] * f.nominal_value + f.nonconvex.negative_gradient["ub"][t] + * f.nominal_value ) def _minimum_flow_rule(block, i, o, p, t): From ef96f35b8eae676e9dd995df4c4df426f36b882a Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 19 Feb 2022 18:00:17 +0100 Subject: [PATCH 0151/1363] Remove accidental import --- src/oemof/solph/components/experimental/_sink_dsm.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index ed7848bbc..6bbc657ec 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -17,7 +17,6 @@ """ import itertools -from _ast import Expression from warnings import warn from numpy import mean From ebf43f879e2e5b986f7fb1b3faa736c54b6ec946 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 4 Mar 2022 11:12:02 +0100 Subject: [PATCH 0152/1363] Update test scripts --- tests/test_components.py | 6 +++++ .../test_connect_invest.py | 22 +++++++++++++------ .../test_add_constraints.py | 14 ++++++------ 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/tests/test_components.py b/tests/test_components.py index b139adbda..a90991090 100644 --- a/tests/test_components.py +++ b/tests/test_components.py @@ -311,3 +311,9 @@ def test_generic_chp_without_warning(): back_pressure=False, ) warnings.filterwarnings("always", category=SuspiciousUsageWarning) + + +# ********* SinkDSM ********* + +def test_sink_dsm_attribute_error1(): + pass diff --git a/tests/test_scripts/test_solph/test_connect_invest/test_connect_invest.py b/tests/test_scripts/test_solph/test_connect_invest/test_connect_invest.py index 6d830b9d2..ea125cf96 100644 --- a/tests/test_scripts/test_solph/test_connect_invest/test_connect_invest.py +++ b/tests/test_scripts/test_solph/test_connect_invest/test_connect_invest.py @@ -93,26 +93,34 @@ def test_connect_invest(): constraints.equate_variables( om, - om.InvestmentFlowBlock.invest[line12, bel2], - om.InvestmentFlowBlock.invest[line21, bel1], + om.InvestmentFlowBlock.invest[line12, bel2, 0], + om.InvestmentFlowBlock.invest[line21, bel1, 0], 2, ) constraints.equate_variables( om, - om.InvestmentFlowBlock.invest[line12, bel2], - om.GenericInvestmentStorageBlock.invest[storage], + om.InvestmentFlowBlock.invest[line12, bel2, 0], + om.GenericInvestmentStorageBlock.invest[storage, 0], ) # if tee_switch is true solver messages will be displayed logging.info("Solve the optimization problem") - om.solve(solver="cbc") + om.solve(solver="cbc", tee=True) # check if the new result object is working for custom components results = processing.results(om) my_results = dict() - my_results["line12"] = float(views.node(results, "line12")["scalars"]) - my_results["line21"] = float(views.node(results, "line21")["scalars"]) + my_results["line12"] = float( + views.node(results, "line12")["scalars"].loc[ + [(("line12", "electricity2"), "invest")] + ] + ) + my_results["line21"] = float( + views.node(results, "line21")["scalars"].loc[ + [(("line21", "electricity1"), "invest")] + ] + ) stor_res = views.node(results, "storage")["scalars"] my_results["storage_in"] = stor_res[ [(("electricity1", "storage"), "invest")] diff --git a/tests/test_scripts/test_solph/test_flexible_modelling/test_add_constraints.py b/tests/test_scripts/test_solph/test_flexible_modelling/test_add_constraints.py index 09d991f20..d2cc5f819 100644 --- a/tests/test_scripts/test_solph/test_flexible_modelling/test_add_constraints.py +++ b/tests/test_scripts/test_solph/test_flexible_modelling/test_add_constraints.py @@ -94,26 +94,26 @@ def test_add_constraints_example(solver="cbc", nologg=False): # add the sub-model to the oemof Model instance om.add_component("MyBlock", myblock) - def _inflow_share_rule(m, si, e, ti): + def _inflow_share_rule(m, si, e, p, ti): """pyomo rule definition: Here we can use all objects from the block or the om object, in this case we don't need anything from the block except the newly defined set MYFLOWS. """ - expr = om.flow[si, e, ti] >= om.flows[si, e].outflow_share[ti] * sum( - om.flow[i, o, ti] for (i, o) in om.FLOWS if o == e - ) + expr = om.flow[si, e, p, ti] >= om.flows[si, e].outflow_share[ + ti + ] * sum(om.flow[i, o, p, ti] for (i, o) in om.FLOWS if o == e) return expr myblock.inflow_share = po.Constraint( - myblock.MYFLOWS, om.TIMESTEPS, rule=_inflow_share_rule + myblock.MYFLOWS, om.TIMEINDEX, rule=_inflow_share_rule ) # add emission constraint myblock.emission_constr = po.Constraint( expr=( sum( - om.flow[i, o, t] + om.flow[i, o, p, t] for (i, o) in myblock.COMMODITYFLOWS - for t in om.TIMESTEPS + for p, t in om.TIMEINDEX ) <= emission_limit ) From f9aca410e0f2cb61a3df497ecd725c5528d89e22 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 4 Mar 2022 11:25:58 +0100 Subject: [PATCH 0153/1363] Fix / add tests --- .../solph/constraints/equate_variables.py | 4 +- .../test_multi_period_dispatch_model.py | 342 ++++++++++++++++++ .../test_storage_investment.py | 4 +- 3 files changed, 346 insertions(+), 4 deletions(-) create mode 100644 tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_dispatch_model.py diff --git a/src/oemof/solph/constraints/equate_variables.py b/src/oemof/solph/constraints/equate_variables.py index 3f06d505b..8700f7cd1 100644 --- a/src/oemof/solph/constraints/equate_variables.py +++ b/src/oemof/solph/constraints/equate_variables.py @@ -66,8 +66,8 @@ def equate_variables(model, var1, var2, factor1=1, name=None): >>> line21 = energysystem.groups['powerline_2_1'] >>> solph.constraints.equate_variables( ... om, - ... om.InvestmentFlowBlock.invest[line12, bel2], - ... om.InvestmentFlowBlock.invest[line21, bel1]) + ... om.InvestmentFlowBlock.invest[line12, bel2, 0], + ... om.InvestmentFlowBlock.invest[line21, bel1, 0]) """ if name is None: name = "_".join(["equate", str(var1), str(var2)]) diff --git a/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_dispatch_model.py b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_dispatch_model.py new file mode 100644 index 000000000..cc0e364d8 --- /dev/null +++ b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_dispatch_model.py @@ -0,0 +1,342 @@ +""" +Test for creating an MultiPeriod dispatch optimization model + +Create an energy system consisting of the following fleets +- lignite +- hardcoal +- CCGT +- GT +- Wind +- GenericStorage unit +- SinkDSM unit +for Germany + +Add wind source and demand sink for FR and links for exchange. +""" + +import pandas as pd +from nose.tools import eq_ + +from oemof.solph import flows +from oemof.solph import components +from oemof.solph import buses +from oemof.solph import EnergySystem +from oemof.solph import Model +from oemof.solph import processing +from oemof.solph import views + + +def test_multi_period_dispatch_model(solver="cbc"): + """Test a simple multi_period dispatch model""" + + t_idx_1 = pd.date_range("1/1/2020", periods=3, freq="H") + t_idx_2 = pd.date_range("1/1/2030", periods=3, freq="H") + t_idx_3 = pd.date_range("1/1/2040", periods=3, freq="H") + + # Create an overall timeindex + t_idx_1_Series = pd.Series(index=t_idx_1, dtype="float64") + t_idx_2_Series = pd.Series(index=t_idx_2, dtype="float64") + t_idx_3_Series = pd.Series(index=t_idx_3, dtype="float64") + + timeindex = pd.concat( + [t_idx_1_Series, t_idx_2_Series, t_idx_3_Series] + ).index + + es = EnergySystem( + timeindex=timeindex, timeincrement=[1] * len(timeindex), multi_period=True + ) + + # Create buses + bus_lignite = buses.Bus( + label="DE_bus_lignite", balanced=True + ) + bus_hardcoal = buses.Bus( + label="DE_bus_hardcoal", balanced=True + ) + bus_natgas = buses.Bus( + label="DE_bus_natgas", balanced=True + ) + bus_el = buses.Bus(label="DE_bus_el", balanced=True) + bus_el_FR = buses.Bus(label="FR_bus_el", balanced=True) + + # Create sources + source_lignite = components.Source( + label="DE_source_lignite", + outputs={ + bus_lignite: flows.Flow(variable_costs=5) + }, + ) + source_hardcoal = components.Source( + label="DE_source_hardcoal", + outputs={ + bus_hardcoal: flows.Flow(variable_costs=10) + }, + ) + source_natgas = components.Source( + label="DE_source_natgas", + outputs={ + bus_natgas: flows.Flow(variable_costs=20) + }, + ) + source_wind = components.Source( + label="DE_source_wind", + outputs={ + bus_el: flows.Flow( + variable_costs=0, + fix=[110] + [90] * (len(timeindex) - 1), + nominal_value=1, + ) + }, + ) + source_shortage = components.Source( + label="DE_source_shortage", + outputs={ + bus_el: flows.Flow( + variable_costs=1e10, nominal_value=1e10 + ) + }, + ) + source_wind_FR = components.Source( + label="FR_source_wind", + outputs={ + bus_el_FR: flows.Flow( + variable_costs=0, + fix=[45] * len(timeindex), + nominal_value=1, + ) + }, + ) + source_shortage_FR = components.Source( + label="FR_source_shortage", + outputs={ + bus_el_FR: flows.Flow( + variable_costs=1e10, nominal_value=1e10 + ) + }, + ) + + # Create sinks + sink_el = components.Sink( + label="DE_sink_el", + inputs={ + bus_el: flows.Flow( + fix=[80] * len(timeindex), nominal_value=1 + ) + }, + ) + sink_excess = components.Sink( + label="DE_sink_excess", + inputs={ + bus_el: flows.Flow( + variable_costs=1e10, nominal_value=1e10 + ) + }, + ) + sink_el_FR = components.Sink( + label="FR_sink_el", + inputs={ + bus_el_FR: flows.Flow( + fix=[50] * len(timeindex), nominal_value=1 + ) + }, + ) + sink_excess_FR = components.Sink( + label="FR_sink_excess", + inputs={ + bus_el_FR: flows.Flow( + variable_costs=1e3, nominal_value=1e10 + ) + }, + ) + + # Create transformers + pp_lignite = components.Transformer( + label="DE_pp_lignite", + inputs={bus_lignite: flows.Flow()}, + outputs={bus_el: flows.Flow(nominal_value=100, variable_costs=1)}, + conversion_factors={bus_el: 0.38}, + ) + + pp_hardcoal = components.Transformer( + label="DE_pp_hardcoal", + inputs={bus_hardcoal: flows.Flow()}, + outputs={bus_el: flows.Flow(nominal_value=100, variable_costs=2)}, + conversion_factors={bus_el: 0.45}, + ) + + pp_natgas_CCGT = components.Transformer( + label="DE_pp_natgas_CCGT", + inputs={bus_natgas: flows.Flow()}, + outputs={ + bus_el: flows.Flow( + nominal_value=100, + variable_costs=3, + ) + }, + conversion_factors={bus_el: 0.6}, + ) + + pp_natgas_GT = components.Transformer( + label="DE_pp_natgas_GT", + inputs={bus_natgas: flows.Flow()}, + outputs={ + bus_el: flows.Flow( + nominal_value=100, + variable_costs=4, + ) + }, + conversion_factors={bus_el: 0.4}, + ) + + storage_el = components.GenericStorage( + label="DE_storage_el", + inputs={ + bus_el: flows.Flow( + nominal_value=20, variable_costs=0, max=1 + ) + }, + outputs={ + bus_el: flows.Flow( + nominal_value=20, variable_costs=0, max=1 + ) + }, + nominal_storage_capacity=20, + loss_rate=0, + initial_storage_level=0, + max_storage_level=1, + min_storage_level=0, + inflow_conversion_factor=1, + outflow_conversion_factor=1, + balanced=True, + fixed_costs=10, + ) + + link_DE_FR = components.experimental.Link( + label="link_DE_FR", + inputs={ + bus_el: flows.Flow(nominal_value=10), + bus_el_FR: flows.Flow(nominal_value=10), + }, + outputs={ + bus_el_FR: flows.Flow(), + bus_el: flows.Flow(), + }, + conversion_factors={ + (bus_el, bus_el_FR): 0.999999, + (bus_el_FR, bus_el): 0.999999, + }, + ) + + # Parameterize and create SinkDSM + approach = "DLR" + + kwargs_all = { + "label": "demand_dsm", + "inputs": {bus_el: flows.Flow(variable_costs=0)}, + "demand": [1] * len(timeindex), + "capacity_up": [1] * len(timeindex), + "capacity_down": [1] * len(timeindex), + "delay_time": 4, + "shed_time": 2, + "recovery_time_shift": 0, + "recovery_time_shed": 24, + "cost_dsm_up": 0.01, + "cost_dsm_down_shift": 0.01, + "cost_dsm_down_shed": 1000, + "efficiency": 1.0, + "shed_eligibility": False, + "shift_eligibility": True, + "max_demand": 20, + "max_capacity_down": 10, + "max_capacity_up": 10, + "shift_time": 2, + } + + kwargs_dict = { + "oemof": {"shift_interval": 24}, + "DIW": {}, + "DLR": { + "ActivateYearLimit": True, + "ActivateDayLimit": True, + "n_yearLimit_shift": 2, + "n_yearLimit_shed": 10, + "t_dayLimit": 2, + "addition": True, + "fixes": True, + }, + } + + dsm_unit = components.experimental.SinkDSM( + **kwargs_all, + approach=approach, + **kwargs_dict[approach], + ) + + es.add( + source_lignite, + source_hardcoal, + source_natgas, + source_wind, + source_shortage, + bus_lignite, + bus_hardcoal, + bus_natgas, + bus_el, + pp_lignite, + pp_hardcoal, + pp_natgas_CCGT, + pp_natgas_GT, + sink_el, + sink_excess, + storage_el, + source_wind_FR, + source_shortage_FR, + bus_el_FR, + sink_el_FR, + sink_excess_FR, + link_DE_FR, + dsm_unit, + ) + + om = Model(es, discount_rate=0.02) + om.solve(solver=solver) + + results = processing.results(om) + test_results = { + "DE_source_lignite": 187, + "DE_source_hardcoal": 0, + "DE_source_natgas": 0, + "DE_source_wind": 830, + "DE_source_shortage": 0, + "DE_bus_lignite": 374, + "DE_bus_hardcoal": 0, + "DE_bus_natgas": 0, + "DE_bus_el": 1822, + "DE_pp_lignite": 258, + "DE_pp_hardcoal": 0, + "DE_pp_natgas_CCGT": 0, + "DE_pp_natgas_GT": 0, + "DE_sink_el": 720, + "DE_sink_excess": 0, + "DE_storage_el": 30, + "FR_source_wind": 405, + "FR_source_shortage": 44, + "FR_bus_el": 900, + "FR_sink_el": 450, + "FR_sink_excess": 0, + "link_DE_FR": 2, + "demand_dsm": 180, + } + + for key in test_results.keys(): + eq_( + ( + int( + views.node(results, key)["sequences"] + .sum(axis=0) + .round(0) + .sum() + ) + ), + test_results[key], + ) diff --git a/tests/test_scripts/test_solph/test_storage_investment/test_storage_investment.py b/tests/test_scripts/test_solph/test_storage_investment/test_storage_investment.py index 0f7837253..1a7a7d4d3 100644 --- a/tests/test_scripts/test_solph/test_storage_investment/test_storage_investment.py +++ b/tests/test_scripts/test_solph/test_storage_investment/test_storage_investment.py @@ -171,8 +171,8 @@ def test_results_with_actual_dump(): # Problem results assert meta["problem"]["Lower bound"] == 4.231675777e17 assert meta["problem"]["Upper bound"], 4.231675777e17 - assert meta["problem"]["Number of variables"] == 2805 - assert meta["problem"]["Number of constraints"] == 2806 + assert meta["problem"]["Number of variables"] == 2808 + assert meta["problem"]["Number of constraints"] == 2809 assert meta["problem"]["Number of nonzeros"] == 1197 assert meta["problem"]["Number of objectives"] == 1 assert str(meta["problem"]["Sense"]) == "minimize" From 95c8cbe1ea99af88ae70cd016c78106a021324db Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 4 Mar 2022 15:23:36 +0100 Subject: [PATCH 0154/1363] Add tests --- .../test_multi_period_dispatch_model.py | 120 ++--- .../test_multi_period_investment_model.py | 450 ++++++++++++++++++ 2 files changed, 493 insertions(+), 77 deletions(-) create mode 100644 tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py diff --git a/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_dispatch_model.py b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_dispatch_model.py index cc0e364d8..69debbe98 100644 --- a/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_dispatch_model.py +++ b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_dispatch_model.py @@ -1,5 +1,5 @@ """ -Test for creating an MultiPeriod dispatch optimization model +Test for creating an multi-period dispatch optimization model Create an energy system consisting of the following fleets - lignite @@ -34,49 +34,39 @@ def test_multi_period_dispatch_model(solver="cbc"): t_idx_3 = pd.date_range("1/1/2040", periods=3, freq="H") # Create an overall timeindex - t_idx_1_Series = pd.Series(index=t_idx_1, dtype="float64") - t_idx_2_Series = pd.Series(index=t_idx_2, dtype="float64") - t_idx_3_Series = pd.Series(index=t_idx_3, dtype="float64") + t_idx_1_series = pd.Series(index=t_idx_1, dtype="float64") + t_idx_2_series = pd.Series(index=t_idx_2, dtype="float64") + t_idx_3_series = pd.Series(index=t_idx_3, dtype="float64") timeindex = pd.concat( - [t_idx_1_Series, t_idx_2_Series, t_idx_3_Series] + [t_idx_1_series, t_idx_2_series, t_idx_3_series] ).index es = EnergySystem( - timeindex=timeindex, timeincrement=[1] * len(timeindex), multi_period=True + timeindex=timeindex, + timeincrement=[1] * len(timeindex), + multi_period=True, ) # Create buses - bus_lignite = buses.Bus( - label="DE_bus_lignite", balanced=True - ) - bus_hardcoal = buses.Bus( - label="DE_bus_hardcoal", balanced=True - ) - bus_natgas = buses.Bus( - label="DE_bus_natgas", balanced=True - ) + bus_lignite = buses.Bus(label="DE_bus_lignite", balanced=True) + bus_hardcoal = buses.Bus(label="DE_bus_hardcoal", balanced=True) + bus_natgas = buses.Bus(label="DE_bus_natgas", balanced=True) bus_el = buses.Bus(label="DE_bus_el", balanced=True) - bus_el_FR = buses.Bus(label="FR_bus_el", balanced=True) + bus_el_fr = buses.Bus(label="FR_bus_el", balanced=True) # Create sources source_lignite = components.Source( label="DE_source_lignite", - outputs={ - bus_lignite: flows.Flow(variable_costs=5) - }, + outputs={bus_lignite: flows.Flow(variable_costs=5)}, ) source_hardcoal = components.Source( label="DE_source_hardcoal", - outputs={ - bus_hardcoal: flows.Flow(variable_costs=10) - }, + outputs={bus_hardcoal: flows.Flow(variable_costs=10)}, ) source_natgas = components.Source( label="DE_source_natgas", - outputs={ - bus_natgas: flows.Flow(variable_costs=20) - }, + outputs={bus_natgas: flows.Flow(variable_costs=20)}, ) source_wind = components.Source( label="DE_source_wind", @@ -90,28 +80,22 @@ def test_multi_period_dispatch_model(solver="cbc"): ) source_shortage = components.Source( label="DE_source_shortage", - outputs={ - bus_el: flows.Flow( - variable_costs=1e10, nominal_value=1e10 - ) - }, + outputs={bus_el: flows.Flow(variable_costs=1e10, nominal_value=1e10)}, ) - source_wind_FR = components.Source( + source_wind_fr = components.Source( label="FR_source_wind", outputs={ - bus_el_FR: flows.Flow( + bus_el_fr: flows.Flow( variable_costs=0, fix=[45] * len(timeindex), nominal_value=1, ) }, ) - source_shortage_FR = components.Source( + source_shortage_fr = components.Source( label="FR_source_shortage", outputs={ - bus_el_FR: flows.Flow( - variable_costs=1e10, nominal_value=1e10 - ) + bus_el_fr: flows.Flow(variable_costs=1e10, nominal_value=1e10) }, ) @@ -119,34 +103,22 @@ def test_multi_period_dispatch_model(solver="cbc"): sink_el = components.Sink( label="DE_sink_el", inputs={ - bus_el: flows.Flow( - fix=[80] * len(timeindex), nominal_value=1 - ) + bus_el: flows.Flow(fix=[80] * len(timeindex), nominal_value=1) }, ) sink_excess = components.Sink( label="DE_sink_excess", - inputs={ - bus_el: flows.Flow( - variable_costs=1e10, nominal_value=1e10 - ) - }, + inputs={bus_el: flows.Flow(variable_costs=1e10, nominal_value=1e10)}, ) - sink_el_FR = components.Sink( + sink_el_fr = components.Sink( label="FR_sink_el", inputs={ - bus_el_FR: flows.Flow( - fix=[50] * len(timeindex), nominal_value=1 - ) + bus_el_fr: flows.Flow(fix=[50] * len(timeindex), nominal_value=1) }, ) - sink_excess_FR = components.Sink( + sink_excess_fr = components.Sink( label="FR_sink_excess", - inputs={ - bus_el_FR: flows.Flow( - variable_costs=1e3, nominal_value=1e10 - ) - }, + inputs={bus_el_fr: flows.Flow(variable_costs=1e3, nominal_value=1e10)}, ) # Create transformers @@ -164,7 +136,7 @@ def test_multi_period_dispatch_model(solver="cbc"): conversion_factors={bus_el: 0.45}, ) - pp_natgas_CCGT = components.Transformer( + pp_natgas_ccgt = components.Transformer( label="DE_pp_natgas_CCGT", inputs={bus_natgas: flows.Flow()}, outputs={ @@ -176,7 +148,7 @@ def test_multi_period_dispatch_model(solver="cbc"): conversion_factors={bus_el: 0.6}, ) - pp_natgas_GT = components.Transformer( + pp_natgas_gt = components.Transformer( label="DE_pp_natgas_GT", inputs={bus_natgas: flows.Flow()}, outputs={ @@ -190,15 +162,9 @@ def test_multi_period_dispatch_model(solver="cbc"): storage_el = components.GenericStorage( label="DE_storage_el", - inputs={ - bus_el: flows.Flow( - nominal_value=20, variable_costs=0, max=1 - ) - }, + inputs={bus_el: flows.Flow(nominal_value=20, variable_costs=0, max=1)}, outputs={ - bus_el: flows.Flow( - nominal_value=20, variable_costs=0, max=1 - ) + bus_el: flows.Flow(nominal_value=20, variable_costs=0, max=1) }, nominal_storage_capacity=20, loss_rate=0, @@ -211,19 +177,19 @@ def test_multi_period_dispatch_model(solver="cbc"): fixed_costs=10, ) - link_DE_FR = components.experimental.Link( + link_de_fr = components.experimental.Link( label="link_DE_FR", inputs={ bus_el: flows.Flow(nominal_value=10), - bus_el_FR: flows.Flow(nominal_value=10), + bus_el_fr: flows.Flow(nominal_value=10), }, outputs={ - bus_el_FR: flows.Flow(), + bus_el_fr: flows.Flow(), bus_el: flows.Flow(), }, conversion_factors={ - (bus_el, bus_el_FR): 0.999999, - (bus_el_FR, bus_el): 0.999999, + (bus_el, bus_el_fr): 0.999999, + (bus_el_fr, bus_el): 0.999999, }, ) @@ -284,17 +250,17 @@ def test_multi_period_dispatch_model(solver="cbc"): bus_el, pp_lignite, pp_hardcoal, - pp_natgas_CCGT, - pp_natgas_GT, + pp_natgas_ccgt, + pp_natgas_gt, sink_el, sink_excess, storage_el, - source_wind_FR, - source_shortage_FR, - bus_el_FR, - sink_el_FR, - sink_excess_FR, - link_DE_FR, + source_wind_fr, + source_shortage_fr, + bus_el_fr, + sink_el_fr, + sink_excess_fr, + link_de_fr, dsm_unit, ) diff --git a/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py new file mode 100644 index 000000000..f56b040e7 --- /dev/null +++ b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py @@ -0,0 +1,450 @@ +""" +Test for creating an multi-period investment optimization model + +Create an energy system consisting of the following fleets +- lignite +- hardcoal +- CCGT +- GT +- Wind +- GenericStorage unit +- SinkDSM unit +for Germany + +Add wind source and demand sink for FR and links for exchange. +""" + +import pandas as pd +from nose.tools import eq_ + +from oemof.solph import flows, _options +from oemof.solph import components +from oemof.solph import buses +from oemof.solph import EnergySystem +from oemof.solph import Model +from oemof.solph import processing +from oemof.solph import views + + +def test_multi_period_investment_model(solver="cbc"): + """Test a simple multi_period investment model + for multiple SinkDSM approaches""" + + for approach in ["oemof", "DLR", "DIW"]: + t_idx_1 = pd.date_range("1/1/2020", periods=3, freq="H") + t_idx_2 = pd.date_range("1/1/2030", periods=3, freq="H") + t_idx_3 = pd.date_range("1/1/2040", periods=3, freq="H") + + # Create an overall timeindex + t_idx_1_series = pd.Series(index=t_idx_1, dtype="float64") + t_idx_2_series = pd.Series(index=t_idx_2, dtype="float64") + t_idx_3_series = pd.Series(index=t_idx_3, dtype="float64") + + timeindex = pd.concat( + [t_idx_1_series, t_idx_2_series, t_idx_3_series] + ).index + + es = EnergySystem( + timeindex=timeindex, + timeincrement=[1] * len(timeindex), + multi_period=True, + ) + + # Create buses + bus_lignite = buses.Bus( + label="DE_bus_lignite", + balanced=True + ) + bus_hardcoal = buses.Bus( + label="DE_bus_hardcoal", + balanced=True + ) + bus_natgas = buses.Bus( + label="DE_bus_natgas", + balanced=True + ) + bus_el = buses.Bus( + label="DE_bus_el", + balanced=True + ) + bus_el_FR = buses.Bus( + label="FR_bus_el", + balanced=True + ) + + # Create sources + source_lignite = components.Source( + label="DE_source_lignite", + outputs={ + bus_lignite: flows.Flow( + variable_costs=5 + ) + }, + ) + source_hardcoal = components.Source( + label="DE_source_hardcoal", + outputs={ + bus_hardcoal: flows.Flow( + variable_costs=10 + ) + }, + ) + source_natgas = components.Source( + label="DE_source_natgas", + outputs={ + bus_natgas: flows.Flow( + variable_costs=20 + ) + }, + ) + source_wind = components.Source( + label="DE_source_wind", + outputs={ + bus_el: flows.Flow( + variable_costs=0, + fix=[110] + [90] * (len(timeindex) - 1), + nominal_value=1 + ) + }, + ) + source_shortage = components.Source( + label="DE_source_shortage", + outputs={ + bus_el: flows.Flow( + variable_costs=1e10, + nominal_value=1e10 + ) + }, + ) + source_wind_FR = components.Source( + label="FR_source_wind", + outputs={ + bus_el_FR: flows.Flow( + variable_costs=0, + fix=[45] * len(timeindex), + nominal_value=1 + ) + }, + ) + source_shortage_FR = components.Source( + label="FR_source_shortage", + outputs={ + bus_el_FR: flows.Flow( + variable_costs=1e10, + nominal_value=1e10 + ) + }, + ) + + # Create sinks + sink_el = components.Sink( + label="DE_sink_el", + inputs={ + bus_el: flows.Flow( + fix=[80] * len(timeindex), + nominal_value=1 + ) + }, + ) + sink_excess = components.Sink( + label="DE_sink_excess", + inputs={ + bus_el: flows.Flow( + variable_costs=1e10, + nominal_value=1e10 + ) + }, + ) + sink_el_FR = components.Sink( + label="FR_sink_el", + inputs={ + bus_el_FR: flows.Flow( + fix=[50] * len(timeindex), + nominal_value=1 + ) + }, + ) + sink_excess_FR = components.Sink( + label="FR_sink_excess", + inputs={ + bus_el_FR: flows.Flow( + variable_costs=1e3, + nominal_value=1e10 + ) + }, + ) + + # Create transformers + pp_lignite = components.Transformer( + label="DE_pp_lignite", + inputs={bus_lignite: flows.Flow()}, + outputs={ + bus_el: flows.Flow( + investment=_options.Investment( + maximum=1000, + ep_costs=2e6, + existing=0, + lifetime=20, + age=0, + interest_rate=0.02, + ), + variable_costs=1 + ) + }, + conversion_factors={bus_el: 0.38}, + ) + + pp_hardcoal = components.Transformer( + label="DE_pp_hardcoal", + inputs={bus_hardcoal: flows.Flow()}, # )}, + outputs={ + bus_el: flows.Flow( + investment=_options.Investment( + maximum=1000, + ep_costs=1.6e6, + existing=0, + lifetime=20, + age=0, + interest_rate=0.02, + ), + variable_costs=2 + ) + }, + conversion_factors={bus_el: 0.45}, + ) + + pp_natgas_CCGT = components.Transformer( + label="DE_pp_natgas_CCGT", + inputs={bus_natgas: flows.Flow()}, # )}, + outputs={ + bus_el: flows.Flow( + investment=_options.Investment( + maximum=1000, + ep_costs=1e6, + existing=0, + lifetime=20, + age=0, + interest_rate=0.02, + ), + variable_costs=3 + ) + }, + conversion_factors={bus_el: 0.6}, + ) + + pp_natgas_GT = components.Transformer( + label="DE_pp_natgas_GT", + inputs={bus_natgas: flows.Flow()}, # )}, + outputs={ + bus_el: flows.Flow( + investment=_options.Investment( + maximum=1000, + ep_costs=[0.6e6, 0.5e6, 0.8e6, 0.4e6], + existing=0, + lifetime=20, + age=0, + interest_rate=0.02, + fixed_costs=1000 + ), + variable_costs=4 + ) + }, + conversion_factors={bus_el: 0.4}, + ) + + storage_el = components.GenericStorage( + label="DE_storage_el", + inputs={ + bus_el: flows.Flow( + variable_costs=0, + max=1, + investment=_options.Investment( + maximum=20, + ep_costs=1000, + existing=10, + lifetime=2, + age=1, + interest_rate=0.02, + ), + ) + }, + outputs={ + bus_el: flows.Flow( + variable_costs=0, + max=1, + investment=_options.Investment( + maximum=20, + ep_costs=1000, + existing=10, + lifetime=2, + age=1, + interest_rate=0.02 + ), + ) + }, + loss_rate=0, + initial_storage_level=0, + max_storage_level=1, + min_storage_level=0, + inflow_conversion_factor=1, + outflow_conversion_factor=1, + balanced=True, + invest_relation_input_output=1, + invest_relation_input_capacity=None, + invest_relation_output_capacity=None, + fixed_costs=10, + investment=_options.Investment( + maximum=20, + ep_costs=1000, + existing=10, + lifetime=2, + age=1, + interest_rate=0.02, + fixed_costs=10 + ), + ) + + link_DE_FR = components.experimental.Link( + label="link_DE_FR", + inputs={ + bus_el: flows.Flow( + nominal_value=10, + ), + bus_el_FR: flows.Flow( + nominal_value=10, + ), + }, + outputs={bus_el_FR: flows.Flow(), bus_el: flows.Flow()}, + conversion_factors={ + (bus_el, bus_el_FR): 0.999999, + (bus_el_FR, bus_el): 0.999999, + }, + ) + + kwargs_all = { + "label": "demand_dsm", + "inputs": { + bus_el: flows.Flow( + variable_costs=0, + ) + }, + "demand": [1] * len(timeindex), + "capacity_up": [1] * len(timeindex), + "capacity_down": [1] * len(timeindex), + "delay_time": 4, + "shed_time": 2, + "recovery_time_shift": 0, + "recovery_time_shed": 24, + "cost_dsm_up": 0.01, + "cost_dsm_down_shift": 0.01, + "cost_dsm_down_shed": 1000, + "efficiency": 1.0, + "shed_eligibility": False, + "shift_eligibility": True, + "flex_share_up": 1, + "flex_share_down": 1, + "shift_time": 2, + } + + kwargs_dict = { + "oemof": {"shift_interval": 24}, + "DIW": {}, + "DLR": { + "ActivateYearLimit": True, + "ActivateDayLimit": True, + "n_yearLimit_shift": 2, + "n_yearLimit_shed": 10, + "t_dayLimit": 2, + "addition": True, + "fixes": True, + }, + } + + dsm_unit = components.experimental.SinkDSM( + **kwargs_all, + approach=approach, + **kwargs_dict[approach], + investment=_options.Investment( + existing=10, + maximum=20, + ep_costs=10, + lifetime=2, + age=1, + interest_rate=0.02, + ), + ) + + es.add( + source_lignite, + source_hardcoal, + source_natgas, + source_wind, + source_shortage, + bus_lignite, + bus_hardcoal, + bus_natgas, + bus_el, + pp_lignite, + pp_hardcoal, + pp_natgas_CCGT, + pp_natgas_GT, + sink_el, + sink_excess, + storage_el, + source_wind_FR, + source_shortage_FR, + bus_el_FR, + sink_el_FR, + sink_excess_FR, + link_DE_FR, + dsm_unit, + ) + + om = Model(es, discount_rate=0.02) + om.receive_duals() + om.solve(solver=solver) + + results = processing.results(om) + test_results = { + "DE_bus_el": pd.Series( + { + "invest": 0, + "old": 20, + "old_end": 20, + "old_exo": 20, + "total": 20, + }, + name="variable_name", + ), + "demand_dsm": pd.Series( + { + "invest": 13, + "old": 10, + "old_end": 0, + "old_exo": 10, + "total": 37, + }, + name="variable_name", + ), + "DE_storage_el": pd.Series( + { + "invest": 0, + "old": 30, + "old_end": 0, + "old_exo": 30, + "total": 30, + }, + name="variable_name", + ), + } + + for key in test_results.keys(): + eq_( + ( + views.node(results, key)["scalars"] + .sum(axis=0) + .round(0) + .convert_dtypes("int") + ).any(), + test_results[key].any(), + ) From cbfae48e85ea008751836f93e1e75735cabc3b67 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 4 Mar 2022 15:36:27 +0100 Subject: [PATCH 0155/1363] Fix imports and doc test --- .../solph/constraints/investment_limit.py | 28 +++++++++---------- src/oemof/solph/flows/_flow.py | 3 +- .../test_multi_period_dispatch_model.py | 6 ++-- .../test_multi_period_investment_model.py | 7 +++-- 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/oemof/solph/constraints/investment_limit.py b/src/oemof/solph/constraints/investment_limit.py index 4aa10dd1a..9b9a4b10c 100644 --- a/src/oemof/solph/constraints/investment_limit.py +++ b/src/oemof/solph/constraints/investment_limit.py @@ -139,15 +139,15 @@ def additional_investment_flow_limit(model, keyword, limit=None): The symbols used are defined as follows (with Variables (V) and Parameters (P)): - +---------------+-------------------------------+------+--------------------------------------------------------------+ - | symbol | attribute | type | explanation | - +===============+===============================+======+==============================================================+ - | :math:`P_{i}` | `InvestmentFlow.invest[i, o]` | V | installed capacity of investment flow | - +---------------+-------------------------------+------+--------------------------------------------------------------+ - | :math:`w_i` | `keyword` | P | weight given to investment flow named according to `keyword` | - +---------------+-------------------------------+------+--------------------------------------------------------------+ - | :math:`limit` | `limit` | P | global limit given by keyword `limit` | - +---------------+-------------------------------+------+--------------------------------------------------------------+ + +---------------+------------------------------------+------+--------------------------------------------------------------+ + | symbol | attribute | type | explanation | + +===============+====================================+======+==============================================================+ + | :math:`P_{i}` | `InvestmentFlowBlock.invest[i, o]` | V | installed capacity of investment flow | + +---------------+------------------------------------+------+--------------------------------------------------------------+ + | :math:`w_i` | `keyword` | P | weight given to investment flow named according to `keyword` | + +---------------+------------------------------------+------+--------------------------------------------------------------+ + | :math:`limit` | `limit` | P | global limit given by keyword `limit` | + +---------------+------------------------------------+------+--------------------------------------------------------------+ Parameters ---------- @@ -170,12 +170,12 @@ def additional_investment_flow_limit(model, keyword, limit=None): >>> from oemof import solph >>> date_time_index = pd.date_range('1/1/2020', periods=5, freq='H') >>> es = solph.EnergySystem(timeindex=date_time_index) - >>> bus = solph.Bus(label='bus_1') - >>> sink = solph.Sink(label="sink", inputs={bus: - ... solph.Flow(nominal_value=10, fix=[10, 20, 30, 40, 50])}) - >>> src1 = solph.Source(label='source_0', outputs={bus: solph.Flow( + >>> bus = solph.buses.Bus(label='bus_1') + >>> sink = solph.components.Sink(label="sink", inputs={bus: + ... solph.flows.Flow(nominal_value=10, fix=[10, 20, 30, 40, 50])}) + >>> src1 = solph.components.Source(label='source_0', outputs={bus: solph.flows.Flow( ... investment=solph.Investment(ep_costs=50, space=4))}) - >>> src2 = solph.Source(label='source_1', outputs={bus: solph.Flow( + >>> src2 = solph.components.Source(label='source_1', outputs={bus: solph.flows.Flow( ... investment=solph.Investment(ep_costs=100, space=1))}) >>> es.add(bus, sink, src1, src2) >>> model = solph.Model(es) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index ba26f6321..b0b4eb6c4 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -20,8 +20,9 @@ from oemof.network import network as on from oemof.tools import debugging -from pyomo.core import BuildAction, Expression +from pyomo.core import BuildAction from pyomo.core import Constraint +from pyomo.core import Expression from pyomo.core import NonNegativeIntegers from pyomo.core import NonNegativeReals from pyomo.core import Set diff --git a/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_dispatch_model.py b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_dispatch_model.py index 69debbe98..946c173cf 100644 --- a/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_dispatch_model.py +++ b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_dispatch_model.py @@ -17,11 +17,11 @@ import pandas as pd from nose.tools import eq_ -from oemof.solph import flows -from oemof.solph import components -from oemof.solph import buses from oemof.solph import EnergySystem from oemof.solph import Model +from oemof.solph import buses +from oemof.solph import components +from oemof.solph import flows from oemof.solph import processing from oemof.solph import views diff --git a/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py index f56b040e7..28dd152cf 100644 --- a/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py +++ b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py @@ -17,11 +17,12 @@ import pandas as pd from nose.tools import eq_ -from oemof.solph import flows, _options -from oemof.solph import components -from oemof.solph import buses from oemof.solph import EnergySystem from oemof.solph import Model +from oemof.solph import _options +from oemof.solph import buses +from oemof.solph import components +from oemof.solph import flows from oemof.solph import processing from oemof.solph import views From 378d2ceba0004f597dba31f221cede8d27d84709 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 4 Mar 2022 16:01:47 +0100 Subject: [PATCH 0156/1363] Define generic keyword investment limit across all periods --- .../solph/constraints/investment_limit.py | 20 +++++++++++-------- tests/lp_files/generic_invest_limit.lp | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/oemof/solph/constraints/investment_limit.py b/src/oemof/solph/constraints/investment_limit.py index 9b9a4b10c..0f4d717f2 100644 --- a/src/oemof/solph/constraints/investment_limit.py +++ b/src/oemof/solph/constraints/investment_limit.py @@ -193,19 +193,23 @@ def additional_investment_flow_limit(model, keyword, limit=None): limit_name = "invest_limit_" + keyword - def _additional_limit_rule(block): - for p in model.PERIODS: - lhs = sum( + setattr( + model, + limit_name, + po.Expression( + expr=sum( model.InvestmentFlowBlock.invest[inflow, outflow, p] * getattr(invest_flows[inflow, outflow], keyword) for (inflow, outflow) in invest_flows + for p in model.PERIODS ) - rhs = limit - model.add_limit.add(p, (lhs <= rhs)) + ), + ) - model.add_limit = po.Constraint( - model.PERIODS, noruleinit=True, name=limit_name + "_constraint" + setattr( + model, + limit_name + "_constraint", + po.Constraint(expr=(getattr(model, limit_name) <= limit)), ) - model.add_limit_build = po.BuildAction(rule=_additional_limit_rule) return model diff --git a/tests/lp_files/generic_invest_limit.lp b/tests/lp_files/generic_invest_limit.lp index 6b59ddbe6..4d1d67e19 100644 --- a/tests/lp_files/generic_invest_limit.lp +++ b/tests/lp_files/generic_invest_limit.lp @@ -8,7 +8,7 @@ objective: s.t. -c_u_add_limit(0)_: +c_u_invest_limit_space_constraint_: +4 InvestmentFlowBlock_invest(source_0_bus_1_0) +1 InvestmentFlowBlock_invest(source_1_bus_1_0) <= 20 From 8658f59c5be6a9ae1ccdf28c2feb6ad230a29120 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 4 Mar 2022 16:28:05 +0100 Subject: [PATCH 0157/1363] Add tests for additions in EnergySystem --- tests/test_energy_system.py | 51 +++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tests/test_energy_system.py diff --git a/tests/test_energy_system.py b/tests/test_energy_system.py new file mode 100644 index 000000000..fb3293170 --- /dev/null +++ b/tests/test_energy_system.py @@ -0,0 +1,51 @@ +"""Tests of the energy_system module. + +This file is part of project oemof (github.com/oemof/oemof). It's copyrighted +by the contributors recorded in the version control history of the file, +available from its original location oemof/tests/test_components.py + +SPDX-License-Identifier: MIT +""" + +import pandas as pd +import pytest + +from oemof.solph import EnergySystem + + +def test_add_periods(): + """test method _add_periods of energy system""" + timeindex = pd.date_range(start="2012-01-01", periods=10000, freq="H") + es = EnergySystem(timeindex=timeindex, multi_period=True) + assert len(es.periods) == 2 + assert es.periods[0].equals( + pd.date_range(start="2012-01-01", periods=8784, freq="H") + ) + assert es.periods[1].equals( + pd.date_range(start="2013-01-01", periods=1216, freq="H") + ) + + +def test_extract_periods_years(): + """test method _extract_periods_years of energy system""" + t_idx_1 = pd.date_range("1/1/2020", periods=3, freq="H").to_series() + t_idx_2 = pd.date_range("1/1/2041", periods=3, freq="H").to_series() + t_idx_3 = pd.date_range("1/1/2050", periods=3, freq="H").to_series() + timeindex = pd.concat([t_idx_1, t_idx_2, t_idx_3]).index + es = EnergySystem( + timeindex=timeindex, + timeincrement=[1] * len(timeindex), + multi_period=True, + ) + periods_years = { + 0: 0, + 1: 21, + 2: 30 + } + assert es.periods_years == periods_years + + +def test_type_error(): + """test type error getting thrown for energy system's periods""" + with pytest.raises(ValueError, match="must be of type int"): + EnergySystem(multi_period=True, periods={0.1: "A", 0.2: "B"}) From 6a34e32a5420c9996e1f0ccdd8aed9fc91acca64 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 4 Mar 2022 17:06:09 +0100 Subject: [PATCH 0158/1363] Try to fix storage tests --- .../test_storage_investment/test_storage_investment.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_scripts/test_solph/test_storage_investment/test_storage_investment.py b/tests/test_scripts/test_solph/test_storage_investment/test_storage_investment.py index 1a7a7d4d3..e20d39a99 100644 --- a/tests/test_scripts/test_solph/test_storage_investment/test_storage_investment.py +++ b/tests/test_scripts/test_solph/test_storage_investment/test_storage_investment.py @@ -134,6 +134,7 @@ def test_optimise_storage_size( def test_results_with_actual_dump(): + test_optimise_storage_size() energysystem = solph.EnergySystem() energysystem.restore() @@ -184,6 +185,7 @@ def test_results_with_actual_dump(): def test_solph_transformer_attributes_before_dump_and_after_restore(): """dump/restore should preserve all attributes of `solph.components.Transformer`""" + test_optimise_storage_size() energysystem = solph.EnergySystem() energysystem.restore() From 569a424fcee5942c5e273d75076c09515413b591 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sun, 6 Mar 2022 14:06:25 +0100 Subject: [PATCH 0159/1363] Adjust indexing to circumvent pd Error; correct console_scripts in setup.py --- setup.py | 2 +- src/oemof/solph/processing.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index 28248df53..d4be689d9 100644 --- a/setup.py +++ b/setup.py @@ -92,7 +92,7 @@ def read(*names, **kwargs): entry_points={ "console_scripts": [ "oemof_installation_test = " - + "oemof.solph.console_scripts:check_oemof_installation" + + "oemof.solph._console_scripts:check_oemof_installation" ] }, ) diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index f60b1b6d6..12f12ca0a 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -282,7 +282,7 @@ def _extract_standard_model_result( def _replace_non_timeindex_indices( df, period_indexed, timestep_indexed, period_timestep_indexed ): - """Replace period and timestpes indices by timeindex values + """Replace timeindex values by timesteps values; we only have one period Parameters ---------- @@ -300,7 +300,7 @@ def _replace_non_timeindex_indices( df : pd.DataFrame Manipulated DataFrame containing only timestep indices """ - rename_dict = {key: (0, key[0]) for key in df.index if len(key) == 1} + rename_dict = {key: (key[1],) for key in df.index if len(key) > 1} to_concat = [] # Split into different data sets dependent on indexation period_timestep_indexed_df = df[ @@ -314,18 +314,19 @@ def _replace_non_timeindex_indices( ] period_timestep_indexed_df = period_timestep_indexed_df.dropna() + period_timestep_indexed_df = period_timestep_indexed_df.rename( + index=rename_dict + ) if not period_timestep_indexed_df.empty: to_concat.append(period_timestep_indexed_df) period_indexed_df = period_indexed_df.dropna() - period_indexed_df = period_indexed_df.rename(index={(0,): (0, 0)}) if not period_indexed_df.empty: to_concat.append(period_indexed_df) # Handle storages differently if "storage_content" not in timestep_indexed_df.columns: timestep_indexed_df = timestep_indexed_df.dropna() - timestep_indexed_df = timestep_indexed_df.rename(index=rename_dict) if not timestep_indexed_df.empty: to_concat.append(timestep_indexed_df) From 0e71f6bd203d6202bf4060ce836a9282660ebea6 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sun, 6 Mar 2022 17:07:03 +0100 Subject: [PATCH 0160/1363] Add tests for re-increasing coverage --- tests/test_energy_system.py | 2 +- tests/test_models.py | 32 ++++++++++++++++++++++++++++++++ tests/test_options.py | 26 ++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 tests/test_options.py diff --git a/tests/test_energy_system.py b/tests/test_energy_system.py index fb3293170..b76631bc0 100644 --- a/tests/test_energy_system.py +++ b/tests/test_energy_system.py @@ -1,4 +1,4 @@ -"""Tests of the energy_system module. +"""Tests of the _energy_system module. This file is part of project oemof (github.com/oemof/oemof). It's copyrighted by the contributors recorded in the version control history of the file, diff --git a/tests/test_models.py b/tests/test_models.py index dae6fa0a0..f21090f4a 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -135,3 +135,35 @@ def test_infeasible_model(): m.solve(solver="cbc") assert "Optimization ended with status" in str(w[0].message) solph.processing.meta_results(m) + + +def test_multi_period_default_discount_rate(): + """Test error being thrown for default multi-period discount rate""" + warnings.filterwarnings("ignore", category=FutureWarning) + timeindex = pd.date_range(start="2017-01-01", periods=100, freq="D") + es = solph.EnergySystem(timeindex=timeindex, multi_period=True) + bel = solph.buses.Bus() + es.add(bel) + es.add( + solph.components.Sink( + inputs={bel: solph.flows.Flow( + nominal_value=5, + fix=[1] * len(timeindex)) + } + ) + ) + es.add( + solph.components.Source( + outputs={ + bel: solph.flows.Flow( + nominal_value=4, variable_costs=5 + ) + } + ) + ) + msg = ( + "By default, a discount_rate of 0.02 is used for a multi-period model." + ) + with warnings.catch_warnings(record=True) as w: + solph.Model(es) + assert msg in str(w[0].message) diff --git a/tests/test_options.py b/tests/test_options.py new file mode 100644 index 000000000..91e2ec868 --- /dev/null +++ b/tests/test_options.py @@ -0,0 +1,26 @@ +"""Tests of the _options module. + +This file is part of project oemof (github.com/oemof/oemof). It's copyrighted +by the contributors recorded in the version control history of the file, +available from its original location oemof/tests/test_components.py + +SPDX-License-Identifier: MIT +""" +import pytest + +from oemof import solph + + +def test_check_age_and_lifetime(): + """Check error being thrown if age > lifetime""" + msg = ( + "A unit's age must be smaller than its " + "expected lifetime." + ) + with pytest.raises(AttributeError, match=msg): + solph.components.Sink( + investment=solph.Investment( + age=41, + lifetime=40 + ) + ) From 19e2fe49fa5852f8bca3be9ee1e197b4426fa290 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Mon, 7 Mar 2022 10:44:39 +0100 Subject: [PATCH 0161/1363] Add missing imports in __init__.py file --- src/oemof/solph/constraints/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/oemof/solph/constraints/__init__.py b/src/oemof/solph/constraints/__init__.py index 5ddbfd44f..69aefc603 100644 --- a/src/oemof/solph/constraints/__init__.py +++ b/src/oemof/solph/constraints/__init__.py @@ -7,7 +7,10 @@ from .flow_count_limit import limit_active_flow_count # noqa: F401 from .flow_count_limit import limit_active_flow_count_by_keyword # noqa: F401 from .integral_limit import emission_limit # noqa: F401 +from .integral_limit import emission_limit_per_period # noqa: F401 from .integral_limit import generic_integral_limit # noqa: F401 +from .integral_limit import generic_periodical_integral_limit # noqa: F401 from .investment_limit import additional_investment_flow_limit # noqa: F401 from .investment_limit import investment_limit # noqa: F401 +from .investment_limit import investment_limit_per_period # noqa: F401 from .shared_limit import shared_limit # noqa: F401 From 665753b61ca1857172764ca7406c3b142d3a4ce1 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Mon, 7 Mar 2022 10:46:56 +0100 Subject: [PATCH 0162/1363] Add fixes and constraint tests (both standard and multi-period) --- .../components/experimental/_sink_dsm.py | 6 +- tests/constraint_tests.py | 166 +++++++- tests/lp_files/dsm_module_DIW_invest.lp | 1 + tests/lp_files/dsm_module_DLR_invest.lp | 1 + tests/lp_files/dsm_module_oemof_invest.lp | 1 + tests/lp_files/emission_budget_limit.lp | 82 ++++ ...fixed_source_variable_sink_multi_period.lp | 48 +++ .../lp_files/investment_limit_with_dsm_DIW.lp | 217 +++++++++++ .../lp_files/investment_limit_with_dsm_DLR.lp | 314 +++++++++++++++ .../investment_limit_with_dsm_oemof.lp | 160 ++++++++ .../linear_transformer_invest_multi_period.lp | 233 +++++++++++ ...ear_transformer_invest_multi_period_old.lp | 234 +++++++++++ .../linear_transformer_multi_period.lp | 108 ++++++ .../max_source_min_sink_multi_period.lp | 60 +++ tests/lp_files/periodical_emission_limit.lp | 88 +++++ tests/multi_period_constraint_tests.py | 365 ++++++++++++++++++ 16 files changed, 2076 insertions(+), 8 deletions(-) create mode 100644 tests/lp_files/emission_budget_limit.lp create mode 100644 tests/lp_files/fixed_source_variable_sink_multi_period.lp create mode 100644 tests/lp_files/investment_limit_with_dsm_DIW.lp create mode 100644 tests/lp_files/investment_limit_with_dsm_DLR.lp create mode 100644 tests/lp_files/investment_limit_with_dsm_oemof.lp create mode 100644 tests/lp_files/linear_transformer_invest_multi_period.lp create mode 100644 tests/lp_files/linear_transformer_invest_multi_period_old.lp create mode 100644 tests/lp_files/linear_transformer_multi_period.lp create mode 100644 tests/lp_files/max_source_min_sink_multi_period.lp create mode 100644 tests/lp_files/periodical_emission_limit.lp create mode 100644 tests/multi_period_constraint_tests.py diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index 6bbc657ec..014c41a00 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -148,12 +148,12 @@ class SinkDSM(Sink): flex_share_up: float Flexible share of installed capacity eligible for upshifts (used for invest mode) - cost_dsm_up : int + cost_dsm_up : float Cost per unit of DSM activity that increases the demand - cost_dsm_down_shift : int + cost_dsm_down_shift : float Cost per unit of DSM activity that decreases the demand for load shifting - cost_dsm_down_shed : int + cost_dsm_down_shed : float Cost per unit of DSM activity that decreases the demand for load shedding efficiency : float diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index dad1b6260..54177d632 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -979,6 +979,136 @@ def test_investment_limit(self): self.compare_lp_files("investment_limit.lp", my_om=om) + def test_investment_limit_with_dsm1(self): + """Testing the investment_limit function in the constraint module.""" + bus1 = solph.buses.Bus(label="Bus1") + solph.components.Source( + label="Source", + outputs={ + bus1: solph.flows.Flow( + investment=solph.Investment(ep_costs=123) + ) + }, + ) + solph.components.experimental.SinkDSM( + label="sink_dsm_DIW", + approach="DIW", + inputs={bus1: solph.flows.Flow()}, + demand=[1] * 3, + capacity_up=[0.5] * 3, + capacity_down=[0.5] * 3, + flex_share_up=1, + flex_share_down=1, + delay_time=1, + cost_dsm_down_shift=0.5, + cost_dsm_up=0.5, + shed_eligibility=False, + investment=solph.Investment( + ep_costs=100, existing=50, minimum=33, maximum=100 + ), + ) + om = self.get_om() + solph.constraints.investment_limit(om, limit=900) + + self.compare_lp_files("investment_limit_with_dsm_DIW.lp", my_om=om) + + def test_investment_limit_with_dsm2(self): + """Testing the investment_limit function in the constraint module.""" + bus1 = solph.buses.Bus(label="Bus1") + solph.components.Source( + label="Source", + outputs={ + bus1: solph.flows.Flow( + investment=solph.Investment(ep_costs=123) + ) + }, + ) + solph.components.experimental.SinkDSM( + label="sink_dsm_DLR", + approach="DLR", + inputs={bus1: solph.flows.Flow()}, + demand=[1] * 3, + capacity_up=[0.5] * 3, + capacity_down=[0.5] * 3, + flex_share_up=1, + flex_share_down=1, + delay_time=1, + shift_time=1, + cost_dsm_down_shift=0.5, + cost_dsm_up=0.5, + shed_eligibility=False, + investment=solph.Investment( + ep_costs=100, existing=50, minimum=33, maximum=100 + ), + ) + om = self.get_om() + solph.constraints.investment_limit(om, limit=900) + + self.compare_lp_files("investment_limit_with_dsm_DLR.lp", my_om=om) + + def test_investment_limit_with_dsm3(self): + """Testing the investment_limit function in the constraint module.""" + bus1 = solph.buses.Bus(label="Bus1") + solph.components.Source( + label="Source", + outputs={ + bus1: solph.flows.Flow( + investment=solph.Investment(ep_costs=123) + ) + }, + ) + solph.components.experimental.SinkDSM( + label="sink_dsm_oemof", + approach="oemof", + inputs={bus1: solph.flows.Flow()}, + demand=[1] * 3, + capacity_up=[0.5] * 3, + capacity_down=[0.5] * 3, + flex_share_up=1, + flex_share_down=1, + delay_time=1, + shift_interval=2, + cost_dsm_down_shift=0.5, + cost_dsm_up=0.5, + shed_eligibility=False, + investment=solph.Investment( + ep_costs=100, existing=50, minimum=33, maximum=100 + ), + ) + om = self.get_om() + solph.constraints.investment_limit(om, limit=900) + + self.compare_lp_files("investment_limit_with_dsm_oemof.lp", my_om=om) + + def test_investment_limit_per_period_error_no_multi_period(self): + """Test error being thrown if model is not a multi-period model""" + bus1 = solph.buses.Bus(label="Bus1") + solph.components.GenericStorage( + label="storage_invest_limit", + invest_relation_input_capacity=0.2, + invest_relation_output_capacity=0.2, + inputs={bus1: solph.flows.Flow()}, + outputs={bus1: solph.flows.Flow()}, + investment=solph.Investment(ep_costs=145), + ) + solph.components.Source( + label="Source", + outputs={ + bus1: solph.flows.Flow( + investment=solph.Investment(ep_costs=123) + ) + }, + ) + om = self.get_om() + + msg = ( + "investment_limit_per_period is only applicable " + "for multi-period models.\nIn order to create such a model, " + "set attribute `multi_period` of your energy system to True." + ) + with pytest.raises(ValueError, match=msg): + solph.constraints.investment_limit_per_period(om, limit=900) + def test_min_max_runtime(self): """Testing min and max runtimes for nonconvex flows.""" bus_t = solph.buses.Bus(label="Bus_T") @@ -1045,7 +1175,7 @@ def test_piecewise_linear_transformer_cc(self): }, outputs={bel: solph.flows.Flow()}, in_breakpoints=[0, 25, 50, 75, 100], - conversion_function=lambda x: x**2, + conversion_function=lambda x: x ** 2, pw_repn="CC", ) self.compare_lp_files("piecewise_linear_transformer_cc.lp") @@ -1061,7 +1191,7 @@ def test_piecewise_linear_transformer_dcc(self): }, outputs={bel: solph.flows.Flow()}, in_breakpoints=[0, 25, 50, 75, 100], - conversion_function=lambda x: x**2, + conversion_function=lambda x: x ** 2, pw_repn="DCC", ) self.compare_lp_files("piecewise_linear_transformer_dcc.lp") @@ -1287,7 +1417,7 @@ def test_dsm_module_DIW_invest(self): recovery_time_shed=2, shed_time=2, investment=solph.Investment( - ep_cost=100, existing=50, minimum=33, maximum=100 + ep_costs=100, existing=50, minimum=33, maximum=100 ), ) self.compare_lp_files("dsm_module_DIW_invest.lp") @@ -1315,7 +1445,7 @@ def test_dsm_module_DLR_invest(self): shed_time=2, n_yearLimit_shed=50, investment=solph.Investment( - ep_cost=100, existing=50, minimum=33, maximum=100 + ep_costs=100, existing=50, minimum=33, maximum=100 ), ) self.compare_lp_files("dsm_module_DLR_invest.lp") @@ -1341,7 +1471,7 @@ def test_dsm_module_oemof_invest(self): recovery_time_shed=2, shed_time=2, investment=solph.Investment( - ep_cost=100, existing=50, minimum=33, maximum=100 + ep_costs=100, existing=50, minimum=33, maximum=100 ), ) self.compare_lp_files("dsm_module_oemof_invest.lp") @@ -1495,3 +1625,29 @@ def test_nonconvex_invest_source_with_offset_no_minimum(self): }, ) self.compare_lp_files("flow_invest_with_offset_no_minimum.lp") + + def test_integral_limit_error_no_multi_period(self): + """Test error being thrown if model is not a multi-period model""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Source( + label="pv_source", + inputs={ + bel: solph.flows.Flow( + nominal_value=100, + variable_costs=20, + space=40, + fix=[0.3, 0.5, 0.8], + ) + }, + ) + om = self.get_om() + msg = ( + "generic_periodical_integral_limit is only applicable\n" + "for multi-period models.\nFor standard models, use " + "generic_integral_limit instead." + ) + with pytest.raises(ValueError, match=msg): + solph.constraints.generic_periodical_integral_limit( + om, keyword="space" + ) diff --git a/tests/lp_files/dsm_module_DIW_invest.lp b/tests/lp_files/dsm_module_DIW_invest.lp index 1d258c469..1ad40a062 100644 --- a/tests/lp_files/dsm_module_DIW_invest.lp +++ b/tests/lp_files/dsm_module_DIW_invest.lp @@ -17,6 +17,7 @@ objective: +1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_0) +1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_1) +1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_2) ++100 SinkDSMDIWInvestmentBlock_invest(demand_dsm_0) s.t. diff --git a/tests/lp_files/dsm_module_DLR_invest.lp b/tests/lp_files/dsm_module_DLR_invest.lp index a315b1010..ebcc0599f 100644 --- a/tests/lp_files/dsm_module_DLR_invest.lp +++ b/tests/lp_files/dsm_module_DLR_invest.lp @@ -29,6 +29,7 @@ objective: +1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_0) +1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_1) +1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_2) ++100 SinkDSMDLRInvestmentBlock_invest(demand_dsm_0) s.t. diff --git a/tests/lp_files/dsm_module_oemof_invest.lp b/tests/lp_files/dsm_module_oemof_invest.lp index a0be361aa..ba48cdcfb 100644 --- a/tests/lp_files/dsm_module_oemof_invest.lp +++ b/tests/lp_files/dsm_module_oemof_invest.lp @@ -11,6 +11,7 @@ objective: +1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_0) +1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_1) +1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_2) ++100 SinkDSMOemofInvestmentBlock_invest(demand_dsm_0) s.t. diff --git a/tests/lp_files/emission_budget_limit.lp b/tests/lp_files/emission_budget_limit.lp new file mode 100644 index 000000000..0f771fa25 --- /dev/null +++ b/tests/lp_files/emission_budget_limit.lp @@ -0,0 +1,82 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++0 ONE_VAR_CONSTANT + +s.t. + +c_u_integral_limit_emission_factor_constraint_: ++0.5 flow(source1_electricityBus_0_0) +-1 flow(source1_electricityBus_0_1) ++2 flow(source1_electricityBus_1_2) ++1 flow(source1_electricityBus_1_3) ++0.5 flow(source1_electricityBus_2_4) ++0.5 flow(source1_electricityBus_2_5) ++3.5 flow(source2_electricityBus_0_0) ++3.5 flow(source2_electricityBus_0_1) ++3.5 flow(source2_electricityBus_1_2) ++3.5 flow(source2_electricityBus_1_3) ++3.5 flow(source2_electricityBus_2_4) ++3.5 flow(source2_electricityBus_2_5) +<= 777 + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(source1_electricityBus_0_0) ++1 flow(source2_electricityBus_0_0) ++1 flow(source3_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(source1_electricityBus_0_1) ++1 flow(source2_electricityBus_0_1) ++1 flow(source3_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(source1_electricityBus_1_2) ++1 flow(source2_electricityBus_1_2) ++1 flow(source3_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(source1_electricityBus_1_3) ++1 flow(source2_electricityBus_1_3) ++1 flow(source3_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(source1_electricityBus_2_4) ++1 flow(source2_electricityBus_2_4) ++1 flow(source3_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(source1_electricityBus_2_5) ++1 flow(source2_electricityBus_2_5) ++1 flow(source3_electricityBus_2_5) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(source1_electricityBus_0_0) <= 100 + 0 <= flow(source1_electricityBus_0_1) <= 100 + 0 <= flow(source1_electricityBus_1_2) <= 100 + 0 <= flow(source1_electricityBus_1_3) <= 100 + 0 <= flow(source1_electricityBus_2_4) <= 100 + 0 <= flow(source1_electricityBus_2_5) <= 100 + 0 <= flow(source2_electricityBus_0_0) <= 100 + 0 <= flow(source2_electricityBus_0_1) <= 100 + 0 <= flow(source2_electricityBus_1_2) <= 100 + 0 <= flow(source2_electricityBus_1_3) <= 100 + 0 <= flow(source2_electricityBus_2_4) <= 100 + 0 <= flow(source2_electricityBus_2_5) <= 100 + 0 <= flow(source3_electricityBus_0_0) <= 100 + 0 <= flow(source3_electricityBus_0_1) <= 100 + 0 <= flow(source3_electricityBus_1_2) <= 100 + 0 <= flow(source3_electricityBus_1_3) <= 100 + 0 <= flow(source3_electricityBus_2_4) <= 100 + 0 <= flow(source3_electricityBus_2_5) <= 100 +end diff --git a/tests/lp_files/fixed_source_variable_sink_multi_period.lp b/tests/lp_files/fixed_source_variable_sink_multi_period.lp new file mode 100644 index 000000000..c231524c6 --- /dev/null +++ b/tests/lp_files/fixed_source_variable_sink_multi_period.lp @@ -0,0 +1,48 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++40 flow(electricityBus_excess_0_0) ++40 flow(electricityBus_excess_0_1) ++39.2156862745098 flow(electricityBus_excess_1_2) ++39.2156862745098 flow(electricityBus_excess_1_3) ++38.446751249519416 flow(electricityBus_excess_2_4) ++38.446751249519416 flow(electricityBus_excess_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_excess_0_0) += -430000 + +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_excess_0_1) += -720000 + +c_e_BusBlock_balance(electricityBus_1_2)_: +-1 flow(electricityBus_excess_1_2) += -290000 + +c_e_BusBlock_balance(electricityBus_1_3)_: +-1 flow(electricityBus_excess_1_3) += -330000 + +c_e_BusBlock_balance(electricityBus_2_4)_: +-1 flow(electricityBus_excess_2_4) += -330000 + +c_e_BusBlock_balance(electricityBus_2_5)_: +-1 flow(electricityBus_excess_2_5) += -330000 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_excess_0_0) <= +inf + 0 <= flow(electricityBus_excess_0_1) <= +inf + 0 <= flow(electricityBus_excess_1_2) <= +inf + 0 <= flow(electricityBus_excess_1_3) <= +inf + 0 <= flow(electricityBus_excess_2_4) <= +inf + 0 <= flow(electricityBus_excess_2_5) <= +inf +end diff --git a/tests/lp_files/investment_limit_with_dsm_DIW.lp b/tests/lp_files/investment_limit_with_dsm_DIW.lp new file mode 100644 index 000000000..72fe98d73 --- /dev/null +++ b/tests/lp_files/investment_limit_with_dsm_DIW.lp @@ -0,0 +1,217 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++123 InvestmentFlowBlock_invest(Source_Bus1_0) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_0) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_1) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_2) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_0) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_1) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_2) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_0) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_1) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_2) ++0.5 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_0) ++0.5 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_1) ++0.5 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_2) ++100 SinkDSMDIWInvestmentBlock_invest(sink_dsm_DIW_0) + +s.t. + +c_u_investment_limit_: ++123 InvestmentFlowBlock_invest(Source_Bus1_0) ++100 SinkDSMDIWInvestmentBlock_invest(sink_dsm_DIW_0) +<= 900 + +c_e_BusBlock_balance(Bus1_0_0)_: +-1 flow(Bus1_sink_dsm_DIW_0_0) ++1 flow(Source_Bus1_0_0) += 0 + +c_e_BusBlock_balance(Bus1_0_1)_: +-1 flow(Bus1_sink_dsm_DIW_0_1) ++1 flow(Source_Bus1_0_1) += 0 + +c_e_BusBlock_balance(Bus1_0_2)_: +-1 flow(Bus1_sink_dsm_DIW_0_2) ++1 flow(Source_Bus1_0_2) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_0)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_0) ++1 InvestmentFlowBlock_total(Source_Bus1_0) += 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0_0)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0_1)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0_2)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_2) +<= 0 + +c_e_SinkDSMDIWInvestmentBlock_total_dsm_rule(sink_dsm_DIW_0)_: +-1 SinkDSMDIWInvestmentBlock_invest(sink_dsm_DIW_0) ++1 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) += 50 + +c_e_SinkDSMDIWInvestmentBlock_shift_shed_vars(sink_dsm_DIW_0)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_0) += 0 + +c_e_SinkDSMDIWInvestmentBlock_shift_shed_vars(sink_dsm_DIW_1)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_1) += 0 + +c_e_SinkDSMDIWInvestmentBlock_shift_shed_vars(sink_dsm_DIW_2)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_2) += 0 + +c_e_SinkDSMDIWInvestmentBlock_input_output_relation(sink_dsm_DIW_0_0)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_0) +-1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_0) +-1 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) ++1 flow(Bus1_sink_dsm_DIW_0_0) += 0 + +c_e_SinkDSMDIWInvestmentBlock_input_output_relation(sink_dsm_DIW_0_1)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_1) +-1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_1) +-1 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) ++1 flow(Bus1_sink_dsm_DIW_0_1) += 0 + +c_e_SinkDSMDIWInvestmentBlock_input_output_relation(sink_dsm_DIW_0_2)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_2) +-1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_2) +-1 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) ++1 flow(Bus1_sink_dsm_DIW_0_2) += 0 + +c_e_SinkDSMDIWInvestmentBlock_dsm_updo_constraint(sink_dsm_DIW_0)_: +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_0) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_1) ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_0) += 0 + +c_e_SinkDSMDIWInvestmentBlock_dsm_updo_constraint(sink_dsm_DIW_1)_: +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_0) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_1) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_2) ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_1) += 0 + +c_e_SinkDSMDIWInvestmentBlock_dsm_updo_constraint(sink_dsm_DIW_2)_: +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_1) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_2) ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_2) += 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(sink_dsm_DIW_0_0)_: ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_0) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(sink_dsm_DIW_0_1)_: ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_1) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(sink_dsm_DIW_0_2)_: ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_2) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(sink_dsm_DIW_0_0)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_0) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(sink_dsm_DIW_0_1)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_1) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(sink_dsm_DIW_0_2)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_2) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_C2_constraint(sink_dsm_DIW_0_0)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_0) ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_0) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_C2_constraint(sink_dsm_DIW_0_1)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_1) ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_1) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_C2_constraint(sink_dsm_DIW_0_2)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_2) ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_2) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(Bus1_sink_dsm_DIW_0_0) <= +inf + 0 <= flow(Bus1_sink_dsm_DIW_0_1) <= +inf + 0 <= flow(Bus1_sink_dsm_DIW_0_2) <= +inf + 0 <= flow(Source_Bus1_0_0) <= +inf + 0 <= flow(Source_Bus1_0_1) <= +inf + 0 <= flow(Source_Bus1_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_0) <= +inf + 33 <= SinkDSMDIWInvestmentBlock_invest(sink_dsm_DIW_0) <= 100 + 0 <= SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_2) <= +inf +end diff --git a/tests/lp_files/investment_limit_with_dsm_DLR.lp b/tests/lp_files/investment_limit_with_dsm_DLR.lp new file mode 100644 index 000000000..8aab9c37d --- /dev/null +++ b/tests/lp_files/investment_limit_with_dsm_DLR.lp @@ -0,0 +1,314 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++123 InvestmentFlowBlock_invest(Source_Bus1_0) ++0.5 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_0) ++0.5 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_1) ++0.5 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_2) ++0.5 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_0) ++0.5 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_1) ++0.5 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_2) ++0.5 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_0) ++0.5 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_1) ++0.5 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_2) ++0.5 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_0) ++0.5 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_1) ++0.5 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_2) ++100 SinkDSMDLRInvestmentBlock_invest(sink_dsm_DLR_0) + +s.t. + +c_u_investment_limit_: ++123 InvestmentFlowBlock_invest(Source_Bus1_0) ++100 SinkDSMDLRInvestmentBlock_invest(sink_dsm_DLR_0) +<= 900 + +c_e_BusBlock_balance(Bus1_0_0)_: +-1 flow(Bus1_sink_dsm_DLR_0_0) ++1 flow(Source_Bus1_0_0) += 0 + +c_e_BusBlock_balance(Bus1_0_1)_: +-1 flow(Bus1_sink_dsm_DLR_0_1) ++1 flow(Source_Bus1_0_1) += 0 + +c_e_BusBlock_balance(Bus1_0_2)_: +-1 flow(Bus1_sink_dsm_DLR_0_2) ++1 flow(Source_Bus1_0_2) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_0)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_0) ++1 InvestmentFlowBlock_total(Source_Bus1_0) += 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0_0)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0_1)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0_2)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_2) +<= 0 + +c_e_SinkDSMDLRInvestmentBlock_total_dsm_rule(sink_dsm_DLR_0)_: +-1 SinkDSMDLRInvestmentBlock_invest(sink_dsm_DLR_0) ++1 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) += 50 + +c_e_SinkDSMDLRInvestmentBlock_shift_shed_vars(sink_dsm_DLR_1_0)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_shift_shed_vars(sink_dsm_DLR_1_1)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_shift_shed_vars(sink_dsm_DLR_1_2)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_input_output_relation(sink_dsm_DLR_0_0)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_0) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_0) +-1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_0) +-1 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) ++1 flow(Bus1_sink_dsm_DLR_0_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_input_output_relation(sink_dsm_DLR_0_1)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_1) +-1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_1) +-1 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) ++1 flow(Bus1_sink_dsm_DLR_0_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_input_output_relation(sink_dsm_DLR_0_2)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_2) +-1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_2) +-1 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) ++1 flow(Bus1_sink_dsm_DLR_0_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(sink_dsm_DLR_1_0)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(sink_dsm_DLR_1_1)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_1) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(sink_dsm_DLR_1_2)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_2) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(sink_dsm_DLR_1_0)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(sink_dsm_DLR_1_1)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_1) +-1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(sink_dsm_DLR_1_2)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_2) +-1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_no_comp_red(sink_dsm_DLR_1_2)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_no_comp_inc(sink_dsm_DLR_1_2)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_2) += 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_red(sink_dsm_DLR_0_0)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_0) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_red(sink_dsm_DLR_0_1)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_1) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_red(sink_dsm_DLR_0_2)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_2) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_inc(sink_dsm_DLR_0_0)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_0) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_inc(sink_dsm_DLR_0_1)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_1) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_inc(sink_dsm_DLR_0_2)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_2) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_red(sink_dsm_DLR_0)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_0) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_red(sink_dsm_DLR_1)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_0) +-1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_red(sink_dsm_DLR_2)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_1) +-1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_inc(sink_dsm_DLR_0)_: +-1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_inc(sink_dsm_DLR_1)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_0) +-1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_inc(sink_dsm_DLR_2)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_1) +-1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_2) += 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(sink_dsm_DLR_0_0)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_0) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(sink_dsm_DLR_0_1)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_1) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(sink_dsm_DLR_0_2)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_2) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(sink_dsm_DLR_0_0)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_0) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(sink_dsm_DLR_0_1)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_1) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(sink_dsm_DLR_0_2)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_2) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(sink_dsm_DLR_0_0)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_0) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_0) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(sink_dsm_DLR_0_1)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_1) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(sink_dsm_DLR_0_2)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_2) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(Bus1_sink_dsm_DLR_0_0) <= +inf + 0 <= flow(Bus1_sink_dsm_DLR_0_1) <= +inf + 0 <= flow(Bus1_sink_dsm_DLR_0_2) <= +inf + 0 <= flow(Source_Bus1_0_0) <= +inf + 0 <= flow(Source_Bus1_0_1) <= +inf + 0 <= flow(Source_Bus1_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_0) <= +inf + 33 <= SinkDSMDLRInvestmentBlock_invest(sink_dsm_DLR_0) <= 100 + 0 <= SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_2) <= +inf +end diff --git a/tests/lp_files/investment_limit_with_dsm_oemof.lp b/tests/lp_files/investment_limit_with_dsm_oemof.lp new file mode 100644 index 000000000..4e2dd99d9 --- /dev/null +++ b/tests/lp_files/investment_limit_with_dsm_oemof.lp @@ -0,0 +1,160 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++123 InvestmentFlowBlock_invest(Source_Bus1_0) ++0.5 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_0) ++0.5 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_1) ++0.5 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_2) ++0.5 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_0) ++0.5 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_1) ++0.5 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_2) ++100 SinkDSMOemofInvestmentBlock_invest(sink_dsm_oemof_0) + +s.t. + +c_u_investment_limit_: ++123 InvestmentFlowBlock_invest(Source_Bus1_0) ++100 SinkDSMOemofInvestmentBlock_invest(sink_dsm_oemof_0) +<= 900 + +c_e_BusBlock_balance(Bus1_0_0)_: +-1 flow(Bus1_sink_dsm_oemof_0_0) ++1 flow(Source_Bus1_0_0) += 0 + +c_e_BusBlock_balance(Bus1_0_1)_: +-1 flow(Bus1_sink_dsm_oemof_0_1) ++1 flow(Source_Bus1_0_1) += 0 + +c_e_BusBlock_balance(Bus1_0_2)_: +-1 flow(Bus1_sink_dsm_oemof_0_2) ++1 flow(Source_Bus1_0_2) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_0)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_0) ++1 InvestmentFlowBlock_total(Source_Bus1_0) += 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0_0)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0_1)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0_2)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_2) +<= 0 + +c_e_SinkDSMOemofInvestmentBlock_total_dsm_rule(sink_dsm_oemof_0)_: +-1 SinkDSMOemofInvestmentBlock_invest(sink_dsm_oemof_0) ++1 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) += 50 + +c_e_SinkDSMOemofInvestmentBlock_shift_shed_vars(sink_dsm_oemof_0)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_0) += 0 + +c_e_SinkDSMOemofInvestmentBlock_shift_shed_vars(sink_dsm_oemof_1)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_1) += 0 + +c_e_SinkDSMOemofInvestmentBlock_shift_shed_vars(sink_dsm_oemof_2)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_2) += 0 + +c_e_SinkDSMOemofInvestmentBlock_input_output_relation(sink_dsm_oemof_0_0)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_0) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_0) +-1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_0) +-1 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) ++1 flow(Bus1_sink_dsm_oemof_0_0) += 0 + +c_e_SinkDSMOemofInvestmentBlock_input_output_relation(sink_dsm_oemof_0_1)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_1) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_1) +-1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_1) +-1 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) ++1 flow(Bus1_sink_dsm_oemof_0_1) += 0 + +c_e_SinkDSMOemofInvestmentBlock_input_output_relation(sink_dsm_oemof_0_2)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_2) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_2) +-1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_2) +-1 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) ++1 flow(Bus1_sink_dsm_oemof_0_2) += 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(sink_dsm_oemof_0_0)_: ++1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_0) +-0.5 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(sink_dsm_oemof_0_1)_: ++1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_1) +-0.5 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(sink_dsm_oemof_0_2)_: ++1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_2) +-0.5 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(sink_dsm_oemof_0_0)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_0) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_0) +-0.5 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(sink_dsm_oemof_0_1)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_1) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_1) +-0.5 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(sink_dsm_oemof_0_2)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_2) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_2) +-0.5 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) +<= 0 + +c_e_SinkDSMOemofInvestmentBlock_dsm_sum_constraint(sink_dsm_oemof_0)_: +-1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_0) +-1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_1) ++1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_0) ++1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_1) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(Bus1_sink_dsm_oemof_0_0) <= +inf + 0 <= flow(Bus1_sink_dsm_oemof_0_1) <= +inf + 0 <= flow(Bus1_sink_dsm_oemof_0_2) <= +inf + 0 <= flow(Source_Bus1_0_0) <= +inf + 0 <= flow(Source_Bus1_0_1) <= +inf + 0 <= flow(Source_Bus1_0_2) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_0) <= +inf + 33 <= SinkDSMOemofInvestmentBlock_invest(sink_dsm_oemof_0) <= 100 + 0 <= SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_0) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_1) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_2) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_0) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_1) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_2) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_0) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_1) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_2) <= +inf +end diff --git a/tests/lp_files/linear_transformer_invest_multi_period.lp b/tests/lp_files/linear_transformer_invest_multi_period.lp new file mode 100644 index 000000000..29ee748fc --- /dev/null +++ b/tests/lp_files/linear_transformer_invest_multi_period.lp @@ -0,0 +1,233 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++29.24459823787798 InvestmentFlowBlock_invest(powerplant_gas_electricity_0) ++28.671174743017627 InvestmentFlowBlock_invest(powerplant_gas_electricity_1) ++28.108994846095712 InvestmentFlowBlock_invest(powerplant_gas_electricity_2) ++50 flow(powerplant_gas_electricity_0_0) ++50 flow(powerplant_gas_electricity_0_1) ++49.019607843137251 flow(powerplant_gas_electricity_1_2) ++49.019607843137251 flow(powerplant_gas_electricity_1_3) ++48.058439061899264 flow(powerplant_gas_electricity_2_4) ++48.058439061899264 flow(powerplant_gas_electricity_2_5) + +s.t. + +c_e_BusBlock_balance(electricity_0_0)_: ++1 flow(powerplant_gas_electricity_0_0) += 0 + +c_e_BusBlock_balance(electricity_0_1)_: ++1 flow(powerplant_gas_electricity_0_1) += 0 + +c_e_BusBlock_balance(electricity_1_2)_: ++1 flow(powerplant_gas_electricity_1_2) += 0 + +c_e_BusBlock_balance(electricity_1_3)_: ++1 flow(powerplant_gas_electricity_1_3) += 0 + +c_e_BusBlock_balance(electricity_2_4)_: ++1 flow(powerplant_gas_electricity_2_4) += 0 + +c_e_BusBlock_balance(electricity_2_5)_: ++1 flow(powerplant_gas_electricity_2_5) += 0 + +c_e_BusBlock_balance(gas_0_0)_: ++1 flow(gas_powerplant_gas_0_0) += 0 + +c_e_BusBlock_balance(gas_0_1)_: ++1 flow(gas_powerplant_gas_0_1) += 0 + +c_e_BusBlock_balance(gas_1_2)_: ++1 flow(gas_powerplant_gas_1_2) += 0 + +c_e_BusBlock_balance(gas_1_3)_: ++1 flow(gas_powerplant_gas_1_3) += 0 + +c_e_BusBlock_balance(gas_2_4)_: ++1 flow(gas_powerplant_gas_2_4) += 0 + +c_e_BusBlock_balance(gas_2_5)_: ++1 flow(gas_powerplant_gas_2_5) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_0_0)_: ++0.57999999999999996 flow(gas_powerplant_gas_0_0) +-1 flow(powerplant_gas_electricity_0_0) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_0_1)_: ++0.57999999999999996 flow(gas_powerplant_gas_0_1) +-1 flow(powerplant_gas_electricity_0_1) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_1_2)_: ++0.57999999999999996 flow(gas_powerplant_gas_1_2) +-1 flow(powerplant_gas_electricity_1_2) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_1_3)_: ++0.57999999999999996 flow(gas_powerplant_gas_1_3) +-1 flow(powerplant_gas_electricity_1_3) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_2_4)_: ++0.57999999999999996 flow(gas_powerplant_gas_2_4) +-1 flow(powerplant_gas_electricity_2_4) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_2_5)_: ++0.57999999999999996 flow(gas_powerplant_gas_2_5) +-1 flow(powerplant_gas_electricity_2_5) += 0 + +c_e_InvestmentFlowBlock_total_rule(powerplant_gas_electricity_0)_: +-1 InvestmentFlowBlock_invest(powerplant_gas_electricity_0) ++1 InvestmentFlowBlock_total(powerplant_gas_electricity_0) += 50 + +c_e_InvestmentFlowBlock_total_rule(powerplant_gas_electricity_1)_: +-1 InvestmentFlowBlock_invest(powerplant_gas_electricity_1) ++1 InvestmentFlowBlock_old(powerplant_gas_electricity_1) +-1 InvestmentFlowBlock_total(powerplant_gas_electricity_0) ++1 InvestmentFlowBlock_total(powerplant_gas_electricity_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(powerplant_gas_electricity_2)_: +-1 InvestmentFlowBlock_invest(powerplant_gas_electricity_2) ++1 InvestmentFlowBlock_old(powerplant_gas_electricity_2) +-1 InvestmentFlowBlock_total(powerplant_gas_electricity_1) ++1 InvestmentFlowBlock_total(powerplant_gas_electricity_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(powerplant_gas_electricity_0)_: ++1 InvestmentFlowBlock_old_end(powerplant_gas_electricity_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(powerplant_gas_electricity_1)_: ++1 InvestmentFlowBlock_old_end(powerplant_gas_electricity_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(powerplant_gas_electricity_2)_: ++1 InvestmentFlowBlock_old_end(powerplant_gas_electricity_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(powerplant_gas_electricity_0)_: ++1 InvestmentFlowBlock_old_exo(powerplant_gas_electricity_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(powerplant_gas_electricity_1)_: ++1 InvestmentFlowBlock_old_exo(powerplant_gas_electricity_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(powerplant_gas_electricity_2)_: ++1 InvestmentFlowBlock_old_exo(powerplant_gas_electricity_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(powerplant_gas_electricity_0)_: ++1 InvestmentFlowBlock_old(powerplant_gas_electricity_0) +-1 InvestmentFlowBlock_old_end(powerplant_gas_electricity_0) +-1 InvestmentFlowBlock_old_exo(powerplant_gas_electricity_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(powerplant_gas_electricity_1)_: ++1 InvestmentFlowBlock_old(powerplant_gas_electricity_1) +-1 InvestmentFlowBlock_old_end(powerplant_gas_electricity_1) +-1 InvestmentFlowBlock_old_exo(powerplant_gas_electricity_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(powerplant_gas_electricity_2)_: ++1 InvestmentFlowBlock_old(powerplant_gas_electricity_2) +-1 InvestmentFlowBlock_old_end(powerplant_gas_electricity_2) +-1 InvestmentFlowBlock_old_exo(powerplant_gas_electricity_2) += 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_electricity_0_0)_: +-1 InvestmentFlowBlock_total(powerplant_gas_electricity_0) ++1 flow(powerplant_gas_electricity_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_electricity_0_1)_: +-1 InvestmentFlowBlock_total(powerplant_gas_electricity_0) ++1 flow(powerplant_gas_electricity_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_electricity_1_2)_: +-1 InvestmentFlowBlock_total(powerplant_gas_electricity_1) ++1 flow(powerplant_gas_electricity_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_electricity_1_3)_: +-1 InvestmentFlowBlock_total(powerplant_gas_electricity_1) ++1 flow(powerplant_gas_electricity_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_electricity_2_4)_: +-1 InvestmentFlowBlock_total(powerplant_gas_electricity_2) ++1 flow(powerplant_gas_electricity_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_electricity_2_5)_: +-1 InvestmentFlowBlock_total(powerplant_gas_electricity_2) ++1 flow(powerplant_gas_electricity_2_5) +<= 0 + +c_u_InvestmentFlowBlock_overall_maximum(powerplant_gas_electricity_0)_: ++1 InvestmentFlowBlock_total(powerplant_gas_electricity_0) +<= 10000 + +c_u_InvestmentFlowBlock_overall_maximum(powerplant_gas_electricity_1)_: ++1 InvestmentFlowBlock_total(powerplant_gas_electricity_1) +<= 10000 + +c_u_InvestmentFlowBlock_overall_maximum(powerplant_gas_electricity_2)_: ++1 InvestmentFlowBlock_total(powerplant_gas_electricity_2) +<= 10000 + +c_l_InvestmentFlowBlock_overall_minimum(powerplant_gas_electricity)_: ++1 InvestmentFlowBlock_total(powerplant_gas_electricity_2) +>= 200 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(gas_powerplant_gas_0_0) <= +inf + 0 <= flow(gas_powerplant_gas_0_1) <= +inf + 0 <= flow(gas_powerplant_gas_1_2) <= +inf + 0 <= flow(gas_powerplant_gas_1_3) <= +inf + 0 <= flow(gas_powerplant_gas_2_4) <= +inf + 0 <= flow(gas_powerplant_gas_2_5) <= +inf + 0 <= flow(powerplant_gas_electricity_0_0) <= +inf + 0 <= flow(powerplant_gas_electricity_0_1) <= +inf + 0 <= flow(powerplant_gas_electricity_1_2) <= +inf + 0 <= flow(powerplant_gas_electricity_1_3) <= +inf + 0 <= flow(powerplant_gas_electricity_2_4) <= +inf + 0 <= flow(powerplant_gas_electricity_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(powerplant_gas_electricity_0) <= 1000 + 0 <= InvestmentFlowBlock_invest(powerplant_gas_electricity_1) <= 1000 + 0 <= InvestmentFlowBlock_invest(powerplant_gas_electricity_2) <= 1000 + 0 <= InvestmentFlowBlock_total(powerplant_gas_electricity_0) <= +inf + 0 <= InvestmentFlowBlock_total(powerplant_gas_electricity_1) <= +inf + 0 <= InvestmentFlowBlock_total(powerplant_gas_electricity_2) <= +inf + 0 <= InvestmentFlowBlock_old(powerplant_gas_electricity_0) <= +inf + 0 <= InvestmentFlowBlock_old(powerplant_gas_electricity_1) <= +inf + 0 <= InvestmentFlowBlock_old(powerplant_gas_electricity_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(powerplant_gas_electricity_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(powerplant_gas_electricity_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(powerplant_gas_electricity_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(powerplant_gas_electricity_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(powerplant_gas_electricity_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(powerplant_gas_electricity_2) <= +inf +end diff --git a/tests/lp_files/linear_transformer_invest_multi_period_old.lp b/tests/lp_files/linear_transformer_invest_multi_period_old.lp new file mode 100644 index 000000000..b6627dc98 --- /dev/null +++ b/tests/lp_files/linear_transformer_invest_multi_period_old.lp @@ -0,0 +1,234 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++20.601980198019806 InvestmentFlowBlock_invest(powerplant_gas_electricity_0) ++20.198019801980202 InvestmentFlowBlock_invest(powerplant_gas_electricity_1) ++19.801980198019805 InvestmentFlowBlock_invest(powerplant_gas_electricity_2) ++50 flow(powerplant_gas_electricity_0_0) ++50 flow(powerplant_gas_electricity_0_1) ++49.019607843137251 flow(powerplant_gas_electricity_1_2) ++49.019607843137251 flow(powerplant_gas_electricity_1_3) ++48.058439061899264 flow(powerplant_gas_electricity_2_4) ++48.058439061899264 flow(powerplant_gas_electricity_2_5) + +s.t. + +c_e_BusBlock_balance(electricity_0_0)_: ++1 flow(powerplant_gas_electricity_0_0) += 0 + +c_e_BusBlock_balance(electricity_0_1)_: ++1 flow(powerplant_gas_electricity_0_1) += 0 + +c_e_BusBlock_balance(electricity_1_2)_: ++1 flow(powerplant_gas_electricity_1_2) += 0 + +c_e_BusBlock_balance(electricity_1_3)_: ++1 flow(powerplant_gas_electricity_1_3) += 0 + +c_e_BusBlock_balance(electricity_2_4)_: ++1 flow(powerplant_gas_electricity_2_4) += 0 + +c_e_BusBlock_balance(electricity_2_5)_: ++1 flow(powerplant_gas_electricity_2_5) += 0 + +c_e_BusBlock_balance(gas_0_0)_: ++1 flow(gas_powerplant_gas_0_0) += 0 + +c_e_BusBlock_balance(gas_0_1)_: ++1 flow(gas_powerplant_gas_0_1) += 0 + +c_e_BusBlock_balance(gas_1_2)_: ++1 flow(gas_powerplant_gas_1_2) += 0 + +c_e_BusBlock_balance(gas_1_3)_: ++1 flow(gas_powerplant_gas_1_3) += 0 + +c_e_BusBlock_balance(gas_2_4)_: ++1 flow(gas_powerplant_gas_2_4) += 0 + +c_e_BusBlock_balance(gas_2_5)_: ++1 flow(gas_powerplant_gas_2_5) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_0_0)_: ++0.57999999999999996 flow(gas_powerplant_gas_0_0) +-1 flow(powerplant_gas_electricity_0_0) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_0_1)_: ++0.57999999999999996 flow(gas_powerplant_gas_0_1) +-1 flow(powerplant_gas_electricity_0_1) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_1_2)_: ++0.57999999999999996 flow(gas_powerplant_gas_1_2) +-1 flow(powerplant_gas_electricity_1_2) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_1_3)_: ++0.57999999999999996 flow(gas_powerplant_gas_1_3) +-1 flow(powerplant_gas_electricity_1_3) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_2_4)_: ++0.57999999999999996 flow(gas_powerplant_gas_2_4) +-1 flow(powerplant_gas_electricity_2_4) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_gas_electricity_2_5)_: ++0.57999999999999996 flow(gas_powerplant_gas_2_5) +-1 flow(powerplant_gas_electricity_2_5) += 0 + +c_e_InvestmentFlowBlock_total_rule(powerplant_gas_electricity_0)_: +-1 InvestmentFlowBlock_invest(powerplant_gas_electricity_0) ++1 InvestmentFlowBlock_total(powerplant_gas_electricity_0) += 50 + +c_e_InvestmentFlowBlock_total_rule(powerplant_gas_electricity_1)_: +-1 InvestmentFlowBlock_invest(powerplant_gas_electricity_1) ++1 InvestmentFlowBlock_old(powerplant_gas_electricity_1) +-1 InvestmentFlowBlock_total(powerplant_gas_electricity_0) ++1 InvestmentFlowBlock_total(powerplant_gas_electricity_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(powerplant_gas_electricity_2)_: +-1 InvestmentFlowBlock_invest(powerplant_gas_electricity_2) ++1 InvestmentFlowBlock_old(powerplant_gas_electricity_2) +-1 InvestmentFlowBlock_total(powerplant_gas_electricity_1) ++1 InvestmentFlowBlock_total(powerplant_gas_electricity_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(powerplant_gas_electricity_0)_: ++1 InvestmentFlowBlock_old_end(powerplant_gas_electricity_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(powerplant_gas_electricity_1)_: ++1 InvestmentFlowBlock_old_end(powerplant_gas_electricity_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(powerplant_gas_electricity_2)_: +-1 InvestmentFlowBlock_invest(powerplant_gas_electricity_0) ++1 InvestmentFlowBlock_old_end(powerplant_gas_electricity_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(powerplant_gas_electricity_0)_: ++1 InvestmentFlowBlock_old_exo(powerplant_gas_electricity_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(powerplant_gas_electricity_1)_: ++1 InvestmentFlowBlock_old_exo(powerplant_gas_electricity_1) += 50 + +c_e_InvestmentFlowBlock_old_rule_exo(powerplant_gas_electricity_2)_: ++1 InvestmentFlowBlock_old_exo(powerplant_gas_electricity_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(powerplant_gas_electricity_0)_: ++1 InvestmentFlowBlock_old(powerplant_gas_electricity_0) +-1 InvestmentFlowBlock_old_end(powerplant_gas_electricity_0) +-1 InvestmentFlowBlock_old_exo(powerplant_gas_electricity_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(powerplant_gas_electricity_1)_: ++1 InvestmentFlowBlock_old(powerplant_gas_electricity_1) +-1 InvestmentFlowBlock_old_end(powerplant_gas_electricity_1) +-1 InvestmentFlowBlock_old_exo(powerplant_gas_electricity_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(powerplant_gas_electricity_2)_: ++1 InvestmentFlowBlock_old(powerplant_gas_electricity_2) +-1 InvestmentFlowBlock_old_end(powerplant_gas_electricity_2) +-1 InvestmentFlowBlock_old_exo(powerplant_gas_electricity_2) += 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_electricity_0_0)_: +-1 InvestmentFlowBlock_total(powerplant_gas_electricity_0) ++1 flow(powerplant_gas_electricity_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_electricity_0_1)_: +-1 InvestmentFlowBlock_total(powerplant_gas_electricity_0) ++1 flow(powerplant_gas_electricity_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_electricity_1_2)_: +-1 InvestmentFlowBlock_total(powerplant_gas_electricity_1) ++1 flow(powerplant_gas_electricity_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_electricity_1_3)_: +-1 InvestmentFlowBlock_total(powerplant_gas_electricity_1) ++1 flow(powerplant_gas_electricity_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_electricity_2_4)_: +-1 InvestmentFlowBlock_total(powerplant_gas_electricity_2) ++1 flow(powerplant_gas_electricity_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_electricity_2_5)_: +-1 InvestmentFlowBlock_total(powerplant_gas_electricity_2) ++1 flow(powerplant_gas_electricity_2_5) +<= 0 + +c_u_InvestmentFlowBlock_overall_maximum(powerplant_gas_electricity_0)_: ++1 InvestmentFlowBlock_total(powerplant_gas_electricity_0) +<= 10000 + +c_u_InvestmentFlowBlock_overall_maximum(powerplant_gas_electricity_1)_: ++1 InvestmentFlowBlock_total(powerplant_gas_electricity_1) +<= 10000 + +c_u_InvestmentFlowBlock_overall_maximum(powerplant_gas_electricity_2)_: ++1 InvestmentFlowBlock_total(powerplant_gas_electricity_2) +<= 10000 + +c_l_InvestmentFlowBlock_overall_minimum(powerplant_gas_electricity)_: ++1 InvestmentFlowBlock_total(powerplant_gas_electricity_2) +>= 200 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(gas_powerplant_gas_0_0) <= +inf + 0 <= flow(gas_powerplant_gas_0_1) <= +inf + 0 <= flow(gas_powerplant_gas_1_2) <= +inf + 0 <= flow(gas_powerplant_gas_1_3) <= +inf + 0 <= flow(gas_powerplant_gas_2_4) <= +inf + 0 <= flow(gas_powerplant_gas_2_5) <= +inf + 0 <= flow(powerplant_gas_electricity_0_0) <= +inf + 0 <= flow(powerplant_gas_electricity_0_1) <= +inf + 0 <= flow(powerplant_gas_electricity_1_2) <= +inf + 0 <= flow(powerplant_gas_electricity_1_3) <= +inf + 0 <= flow(powerplant_gas_electricity_2_4) <= +inf + 0 <= flow(powerplant_gas_electricity_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(powerplant_gas_electricity_0) <= 1000 + 0 <= InvestmentFlowBlock_invest(powerplant_gas_electricity_1) <= 1000 + 0 <= InvestmentFlowBlock_invest(powerplant_gas_electricity_2) <= 1000 + 0 <= InvestmentFlowBlock_total(powerplant_gas_electricity_0) <= +inf + 0 <= InvestmentFlowBlock_total(powerplant_gas_electricity_1) <= +inf + 0 <= InvestmentFlowBlock_total(powerplant_gas_electricity_2) <= +inf + 0 <= InvestmentFlowBlock_old(powerplant_gas_electricity_0) <= +inf + 0 <= InvestmentFlowBlock_old(powerplant_gas_electricity_1) <= +inf + 0 <= InvestmentFlowBlock_old(powerplant_gas_electricity_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(powerplant_gas_electricity_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(powerplant_gas_electricity_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(powerplant_gas_electricity_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(powerplant_gas_electricity_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(powerplant_gas_electricity_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(powerplant_gas_electricity_2) <= +inf +end diff --git a/tests/lp_files/linear_transformer_multi_period.lp b/tests/lp_files/linear_transformer_multi_period.lp new file mode 100644 index 000000000..88297d25f --- /dev/null +++ b/tests/lp_files/linear_transformer_multi_period.lp @@ -0,0 +1,108 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++50 flow(powerplantGas_electricity_0_0) ++50 flow(powerplantGas_electricity_0_1) ++49.019607843137251 flow(powerplantGas_electricity_1_2) ++49.019607843137251 flow(powerplantGas_electricity_1_3) ++48.058439061899264 flow(powerplantGas_electricity_2_4) ++48.058439061899264 flow(powerplantGas_electricity_2_5) + +s.t. + +c_e_BusBlock_balance(electricity_0_0)_: ++1 flow(powerplantGas_electricity_0_0) += 0 + +c_e_BusBlock_balance(electricity_0_1)_: ++1 flow(powerplantGas_electricity_0_1) += 0 + +c_e_BusBlock_balance(electricity_1_2)_: ++1 flow(powerplantGas_electricity_1_2) += 0 + +c_e_BusBlock_balance(electricity_1_3)_: ++1 flow(powerplantGas_electricity_1_3) += 0 + +c_e_BusBlock_balance(electricity_2_4)_: ++1 flow(powerplantGas_electricity_2_4) += 0 + +c_e_BusBlock_balance(electricity_2_5)_: ++1 flow(powerplantGas_electricity_2_5) += 0 + +c_e_BusBlock_balance(gas_0_0)_: ++1 flow(gas_powerplantGas_0_0) += 0 + +c_e_BusBlock_balance(gas_0_1)_: ++1 flow(gas_powerplantGas_0_1) += 0 + +c_e_BusBlock_balance(gas_1_2)_: ++1 flow(gas_powerplantGas_1_2) += 0 + +c_e_BusBlock_balance(gas_1_3)_: ++1 flow(gas_powerplantGas_1_3) += 0 + +c_e_BusBlock_balance(gas_2_4)_: ++1 flow(gas_powerplantGas_2_4) += 0 + +c_e_BusBlock_balance(gas_2_5)_: ++1 flow(gas_powerplantGas_2_5) += 0 + +c_e_TransformerBlock_relation(powerplantGas_gas_electricity_0_0)_: ++0.57999999999999996 flow(gas_powerplantGas_0_0) +-1 flow(powerplantGas_electricity_0_0) += 0 + +c_e_TransformerBlock_relation(powerplantGas_gas_electricity_0_1)_: ++0.57999999999999996 flow(gas_powerplantGas_0_1) +-1 flow(powerplantGas_electricity_0_1) += 0 + +c_e_TransformerBlock_relation(powerplantGas_gas_electricity_1_2)_: ++0.57999999999999996 flow(gas_powerplantGas_1_2) +-1 flow(powerplantGas_electricity_1_2) += 0 + +c_e_TransformerBlock_relation(powerplantGas_gas_electricity_1_3)_: ++0.57999999999999996 flow(gas_powerplantGas_1_3) +-1 flow(powerplantGas_electricity_1_3) += 0 + +c_e_TransformerBlock_relation(powerplantGas_gas_electricity_2_4)_: ++0.57999999999999996 flow(gas_powerplantGas_2_4) +-1 flow(powerplantGas_electricity_2_4) += 0 + +c_e_TransformerBlock_relation(powerplantGas_gas_electricity_2_5)_: ++0.57999999999999996 flow(gas_powerplantGas_2_5) +-1 flow(powerplantGas_electricity_2_5) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(gas_powerplantGas_0_0) <= +inf + 0 <= flow(gas_powerplantGas_0_1) <= +inf + 0 <= flow(gas_powerplantGas_1_2) <= +inf + 0 <= flow(gas_powerplantGas_1_3) <= +inf + 0 <= flow(gas_powerplantGas_2_4) <= +inf + 0 <= flow(gas_powerplantGas_2_5) <= +inf + 0 <= flow(powerplantGas_electricity_0_0) <= 100000000000 + 0 <= flow(powerplantGas_electricity_0_1) <= 100000000000 + 0 <= flow(powerplantGas_electricity_1_2) <= 100000000000 + 0 <= flow(powerplantGas_electricity_1_3) <= 100000000000 + 0 <= flow(powerplantGas_electricity_2_4) <= 100000000000 + 0 <= flow(powerplantGas_electricity_2_5) <= 100000000000 +end diff --git a/tests/lp_files/max_source_min_sink_multi_period.lp b/tests/lp_files/max_source_min_sink_multi_period.lp new file mode 100644 index 000000000..175d8b9d2 --- /dev/null +++ b/tests/lp_files/max_source_min_sink_multi_period.lp @@ -0,0 +1,60 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++14 flow(electricityBus_minDemand_0_0) ++14 flow(electricityBus_minDemand_0_1) ++13.725490196078431 flow(electricityBus_minDemand_1_2) ++13.725490196078431 flow(electricityBus_minDemand_1_3) ++13.456362937331795 flow(electricityBus_minDemand_2_4) ++13.456362937331795 flow(electricityBus_minDemand_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_minDemand_0_0) ++1 flow(wind_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_minDemand_0_1) ++1 flow(wind_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: +-1 flow(electricityBus_minDemand_1_2) ++1 flow(wind_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: +-1 flow(electricityBus_minDemand_1_3) ++1 flow(wind_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: +-1 flow(electricityBus_minDemand_2_4) ++1 flow(wind_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: +-1 flow(electricityBus_minDemand_2_5) ++1 flow(wind_electricityBus_2_5) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 45.359999999999999 <= flow(electricityBus_minDemand_0_0) <= 54 + 50.759999999999998 <= flow(electricityBus_minDemand_0_1) <= 54 + 31.859999999999999 <= flow(electricityBus_minDemand_1_2) <= 54 + 37.799999999999997 <= flow(electricityBus_minDemand_1_3) <= 54 + 52.379999999999995 <= flow(electricityBus_minDemand_2_4) <= 54 + 4.8599999999999994 <= flow(electricityBus_minDemand_2_5) <= 54 + 0 <= flow(wind_electricityBus_0_0) <= 45.899999999999999 + 0 <= flow(wind_electricityBus_0_1) <= 51.299999999999997 + 0 <= flow(wind_electricityBus_1_2) <= 32.939999999999998 + 0 <= flow(wind_electricityBus_1_3) <= 38.879999999999995 + 0 <= flow(wind_electricityBus_2_4) <= 53.460000000000001 + 0 <= flow(wind_electricityBus_2_5) <= 5.4000000000000004 +end diff --git a/tests/lp_files/periodical_emission_limit.lp b/tests/lp_files/periodical_emission_limit.lp new file mode 100644 index 000000000..9b1429d18 --- /dev/null +++ b/tests/lp_files/periodical_emission_limit.lp @@ -0,0 +1,88 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++0 ONE_VAR_CONSTANT + +s.t. + +c_u_periodical_integral_limit(0)_: ++0.5 flow(source1_electricityBus_0_0) +-1 flow(source1_electricityBus_0_1) ++3.5 flow(source2_electricityBus_0_0) ++3.5 flow(source2_electricityBus_0_1) +<= 222 + +c_u_periodical_integral_limit(1)_: ++2 flow(source1_electricityBus_1_2) ++1 flow(source1_electricityBus_1_3) ++3.5 flow(source2_electricityBus_1_2) ++3.5 flow(source2_electricityBus_1_3) +<= 222 + +c_u_periodical_integral_limit(2)_: ++0.5 flow(source1_electricityBus_2_4) ++0.5 flow(source1_electricityBus_2_5) ++3.5 flow(source2_electricityBus_2_4) ++3.5 flow(source2_electricityBus_2_5) +<= 222 + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(source1_electricityBus_0_0) ++1 flow(source2_electricityBus_0_0) ++1 flow(source3_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(source1_electricityBus_0_1) ++1 flow(source2_electricityBus_0_1) ++1 flow(source3_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(source1_electricityBus_1_2) ++1 flow(source2_electricityBus_1_2) ++1 flow(source3_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(source1_electricityBus_1_3) ++1 flow(source2_electricityBus_1_3) ++1 flow(source3_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(source1_electricityBus_2_4) ++1 flow(source2_electricityBus_2_4) ++1 flow(source3_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(source1_electricityBus_2_5) ++1 flow(source2_electricityBus_2_5) ++1 flow(source3_electricityBus_2_5) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(source1_electricityBus_0_0) <= 100 + 0 <= flow(source1_electricityBus_0_1) <= 100 + 0 <= flow(source1_electricityBus_1_2) <= 100 + 0 <= flow(source1_electricityBus_1_3) <= 100 + 0 <= flow(source1_electricityBus_2_4) <= 100 + 0 <= flow(source1_electricityBus_2_5) <= 100 + 0 <= flow(source2_electricityBus_0_0) <= 100 + 0 <= flow(source2_electricityBus_0_1) <= 100 + 0 <= flow(source2_electricityBus_1_2) <= 100 + 0 <= flow(source2_electricityBus_1_3) <= 100 + 0 <= flow(source2_electricityBus_2_4) <= 100 + 0 <= flow(source2_electricityBus_2_5) <= 100 + 0 <= flow(source3_electricityBus_0_0) <= 100 + 0 <= flow(source3_electricityBus_0_1) <= 100 + 0 <= flow(source3_electricityBus_1_2) <= 100 + 0 <= flow(source3_electricityBus_1_3) <= 100 + 0 <= flow(source3_electricityBus_2_4) <= 100 + 0 <= flow(source3_electricityBus_2_5) <= 100 +end diff --git a/tests/multi_period_constraint_tests.py b/tests/multi_period_constraint_tests.py new file mode 100644 index 000000000..9e0d416b6 --- /dev/null +++ b/tests/multi_period_constraint_tests.py @@ -0,0 +1,365 @@ +# -*- coding: utf-8 - + +"""Test the created constraints against approved constraints. + +This file is part of project oemof (github.com/oemof/oemof). It's copyrighted +by the contributors recorded in the version control history of the file, +available from its original location oemof/tests/constraint_tests.py + +SPDX-License-Identifier: MIT +""" + +import logging +import re +from difflib import unified_diff +from os import path as ospath + +import pandas as pd +import pytest +from nose.tools import assert_raises +from nose.tools import eq_ +from oemof.network.network import Node + +from oemof import solph + +logging.disable(logging.INFO) + + +class TestsMultiPeriodConstraint: + @classmethod + def setup_class(cls): + cls.objective_pattern = re.compile( + r"^objective.*(?=s\.t\.)", re.DOTALL | re.MULTILINE + ) + + timeindex1 = pd.date_range("1/1/2012", periods=2, freq="H") + timeindex2 = pd.date_range("1/1/2013", periods=2, freq="H") + timeindex3 = pd.date_range("1/1/2014", periods=2, freq="H") + cls.date_time_index = timeindex1.append(timeindex2).append(timeindex3) + + cls.tmppath = solph.helpers.extend_basic_path("tmp") + logging.info(cls.tmppath) + + def setup(self): + self.energysystem = solph.EnergySystem( + groupings=solph.GROUPINGS, + timeindex=self.date_time_index, + timeincrement=[1] * len(self.date_time_index), + multi_period=True, + ) + Node.registry = self.energysystem + + def get_om(self): + return solph.Model( + self.energysystem, timeindex=self.energysystem.timeindex + ) + + def compare_lp_files(self, filename, ignored=None, my_om=None): + r"""Compare lp-files to check constraints generated within solph. + + An lp-file is being generated automatically when the tests are + executed. Make sure that you create an empty file first and + transfer the content from the one that has been created automatically + into this one afterwards. Please ensure that the content is being + checked carefully. Otherwise, errors are included within the code base. + """ + if my_om is None: + om = self.get_om() + else: + om = my_om + tmp_filename = filename.replace(".lp", "") + "_tmp.lp" + new_filename = ospath.join(self.tmppath, tmp_filename) + om.write(new_filename, io_options={"symbolic_solver_labels": True}) + logging.info("Comparing with file: {0}".format(filename)) + with open(ospath.join(self.tmppath, tmp_filename)) as generated_file: + with open( + ospath.join( + ospath.dirname(ospath.realpath(__file__)), + "lp_files", + filename, + ) + ) as expected_file: + + def chop_trailing_whitespace(lines): + return [re.sub(r"\s*$", "", ln) for ln in lines] + + def remove(pattern, lines): + if not pattern: + return lines + return re.subn(pattern, "", "\n".join(lines))[0].split( + "\n" + ) + + expected = remove( + ignored, + chop_trailing_whitespace(expected_file.readlines()), + ) + generated = remove( + ignored, + chop_trailing_whitespace(generated_file.readlines()), + ) + + 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 + + expected = normalize_to_positive_results(expected) + generated = normalize_to_positive_results(generated) + + eq_( + generated, + expected, + "Failed matching expected with generated lp file:\n" + + "\n".join( + unified_diff( + expected, + generated, + fromfile=ospath.relpath(expected_file.name), + tofile=ospath.basename(generated_file.name), + lineterm="", + ) + ), + ) + + def test_emission_budget_limit(self): + """Test emissions budget limit constraint""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Source( + label="source1", + outputs={ + bel: solph.flows.Flow( + nominal_value=100, + emission_factor=[0.5, -1.0, 2.0, 1.0, 0.5, 0.5], + ) + }, + ) + solph.components.Source( + label="source2", + outputs={ + bel: solph.flows.Flow(nominal_value=100, emission_factor=3.5) + }, + ) + + # Should be ignored because the emission attribute is not defined. + solph.components.Source( + label="source3", outputs={bel: solph.flows.Flow(nominal_value=100)} + ) + + om = self.get_om() + + solph.constraints.emission_limit(om, limit=777) + + self.compare_lp_files("emission_budget_limit.lp", my_om=om) + + def test_periodical_emission_limit(self): + """Test periodical emissions constraint""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Source( + label="source1", + outputs={ + bel: solph.flows.Flow( + nominal_value=100, + emission_factor=[0.5, -1.0, 2.0, 1.0, 0.5, 0.5], + ) + }, + ) + solph.components.Source( + label="source2", + outputs={ + bel: solph.flows.Flow(nominal_value=100, emission_factor=3.5) + }, + ) + + # Should be ignored because the emission attribute is not defined. + solph.components.Source( + label="source3", outputs={bel: solph.flows.Flow(nominal_value=100)} + ) + + om = self.get_om() + + solph.constraints.emission_limit_per_period(om, limit=222) + + self.compare_lp_files("periodical_emission_limit.lp", my_om=om) + + def test_periodical_emission_limit_missing_limit(self): + """Test error for periodical emissions constraint""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Source( + label="source1", + outputs={ + bel: solph.flows.Flow( + nominal_value=100, + emission_factor=[0.5, -1.0, 2.0, 1.0, 0.5, 0.5], + ) + }, + ) + solph.components.Source( + label="source2", + outputs={ + bel: solph.flows.Flow(nominal_value=100, emission_factor=3.5) + }, + ) + + # Should be ignored because the emission attribute is not defined. + solph.components.Source( + label="source3", outputs={bel: solph.flows.Flow(nominal_value=100)} + ) + + om = self.get_om() + + msg = ( + "You have to provide a limit for each period!\n" + "If you provide a scalar value, this will be applied as a " + "limit for each period." + ) + with pytest.raises(ValueError, match=msg): + solph.constraints.emission_limit_per_period(om, limit=None) + + def test_linear_transformer(self): + """Constraint test of a Transformer without Investment.""" + bgas = solph.buses.Bus(label="gas") + + bel = solph.buses.Bus(label="electricity") + + solph.components.Transformer( + label="powerplantGas", + inputs={bgas: solph.flows.Flow()}, + outputs={ + bel: solph.flows.Flow(nominal_value=10e10, variable_costs=50) + }, + conversion_factors={bel: 0.58}, + ) + + self.compare_lp_files("linear_transformer_multi_period.lp") + + def test_linear_transformer_invest(self): + """Constraint test of a Transformer with Investment.""" + + bgas = solph.buses.Bus(label="gas") + + bel = solph.buses.Bus(label="electricity") + + solph.components.Transformer( + label="powerplant_gas", + inputs={bgas: solph.flows.Flow()}, + outputs={ + bel: solph.flows.Flow( + variable_costs=50, + investment=solph.Investment( + existing=50, + maximum=1000, + overall_maximum=10000, + overall_minimum=200, + ep_costs=20, + age=5, + lifetime=40, + ), + ) + }, + conversion_factors={bel: 0.58}, + ) + + self.compare_lp_files("linear_transformer_invest_multi_period.lp") + + def test_linear_transformer_invest_old_capacity(self): + """Constraint test of a Transformer with Investment.""" + + bgas = solph.buses.Bus(label="gas") + + bel = solph.buses.Bus(label="electricity") + + solph.components.Transformer( + label="powerplant_gas", + inputs={bgas: solph.flows.Flow()}, + outputs={ + bel: solph.flows.Flow( + variable_costs=50, + investment=solph.Investment( + existing=50, + maximum=1000, + overall_maximum=10000, + overall_minimum=200, + ep_costs=20, + age=1, + lifetime=2, + ), + ) + }, + conversion_factors={bel: 0.58}, + ) + + self.compare_lp_files("linear_transformer_invest_multi_period_old.lp") + + def test_max_source_min_sink(self): + """Test source with max, sink with min""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Source( + label="wind", + outputs={ + bel: solph.flows.Flow( + nominal_value=54, max=(0.85, 0.95, 0.61, 0.72, 0.99, 0.1) + ) + }, + ) + + solph.components.Sink( + label="minDemand", + inputs={ + bel: solph.flows.Flow( + nominal_value=54, + min=(0.84, 0.94, 0.59, 0.7, 0.97, 0.09), + variable_costs=14, + ) + }, + ) + + self.compare_lp_files("max_source_min_sink_multi_period.lp") + + def test_fixed_source_variable_sink(self): + """Constraint test with a fixed source and a variable sink.""" + + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Source( + label="wind", + outputs={ + bel: solph.flows.Flow( + fix=[0.43, 0.72, 0.29, 0.33, 0.33, 0.33], nominal_value=1e6 + ) + }, + ) + + solph.components.Sink( + label="excess", inputs={bel: solph.flows.Flow(variable_costs=40)} + ) + + self.compare_lp_files("fixed_source_variable_sink_multi_period.lp") From ad021fd64b01458e9a47b69468af044137af7f53 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Mon, 7 Mar 2022 11:18:30 +0100 Subject: [PATCH 0163/1363] Add multi-period constraint tests --- .../fixed_source_invest_sink_multi_period.lp | 169 ++++++++++++++++++ .../invest_source_fixed_sink_multi_period.lp | 157 ++++++++++++++++ .../nominal_value_to_zero_multi_period.lp | 43 +++++ tests/lp_files/storage_multi_period.lp | 117 ++++++++++++ tests/multi_period_constraint_tests.py | 109 +++++++++++ 5 files changed, 595 insertions(+) create mode 100644 tests/lp_files/fixed_source_invest_sink_multi_period.lp create mode 100644 tests/lp_files/invest_source_fixed_sink_multi_period.lp create mode 100644 tests/lp_files/nominal_value_to_zero_multi_period.lp create mode 100644 tests/lp_files/storage_multi_period.lp diff --git a/tests/lp_files/fixed_source_invest_sink_multi_period.lp b/tests/lp_files/fixed_source_invest_sink_multi_period.lp new file mode 100644 index 000000000..96a9daaa9 --- /dev/null +++ b/tests/lp_files/fixed_source_invest_sink_multi_period.lp @@ -0,0 +1,169 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++611.56718125290342 InvestmentFlowBlock_invest(electricityBus_excess_0) ++599.57566789500333 InvestmentFlowBlock_invest(electricityBus_excess_1) ++587.8192822500032 InvestmentFlowBlock_invest(electricityBus_excess_2) ++25 flow(electricityBus_excess_0_0) ++25 flow(electricityBus_excess_0_1) ++24.509803921568626 flow(electricityBus_excess_1_2) ++24.509803921568626 flow(electricityBus_excess_1_3) ++24.029219530949632 flow(electricityBus_excess_2_4) ++24.029219530949632 flow(electricityBus_excess_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_excess_0_0) += -12000000 + +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_excess_0_1) += -16000000 + +c_e_BusBlock_balance(electricityBus_1_2)_: +-1 flow(electricityBus_excess_1_2) += -14000000 + +c_e_BusBlock_balance(electricityBus_1_3)_: +-1 flow(electricityBus_excess_1_3) += -18000000 + +c_e_BusBlock_balance(electricityBus_2_4)_: +-1 flow(electricityBus_excess_2_4) += -18000000 + +c_e_BusBlock_balance(electricityBus_2_5)_: +-1 flow(electricityBus_excess_2_5) += -18000000 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_excess_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_excess_0) ++1 InvestmentFlowBlock_total(electricityBus_excess_0) += 50 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_excess_1)_: +-1 InvestmentFlowBlock_invest(electricityBus_excess_1) ++1 InvestmentFlowBlock_old(electricityBus_excess_1) +-1 InvestmentFlowBlock_total(electricityBus_excess_0) ++1 InvestmentFlowBlock_total(electricityBus_excess_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_excess_2)_: +-1 InvestmentFlowBlock_invest(electricityBus_excess_2) ++1 InvestmentFlowBlock_old(electricityBus_excess_2) +-1 InvestmentFlowBlock_total(electricityBus_excess_1) ++1 InvestmentFlowBlock_total(electricityBus_excess_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_excess_0)_: ++1 InvestmentFlowBlock_old_end(electricityBus_excess_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_excess_1)_: ++1 InvestmentFlowBlock_old_end(electricityBus_excess_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_excess_2)_: ++1 InvestmentFlowBlock_old_end(electricityBus_excess_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_excess_0)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_excess_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_excess_1)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_excess_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_excess_2)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_excess_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_excess_0)_: ++1 InvestmentFlowBlock_old(electricityBus_excess_0) +-1 InvestmentFlowBlock_old_end(electricityBus_excess_0) +-1 InvestmentFlowBlock_old_exo(electricityBus_excess_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_excess_1)_: ++1 InvestmentFlowBlock_old(electricityBus_excess_1) +-1 InvestmentFlowBlock_old_end(electricityBus_excess_1) +-1 InvestmentFlowBlock_old_exo(electricityBus_excess_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_excess_2)_: ++1 InvestmentFlowBlock_old(electricityBus_excess_2) +-1 InvestmentFlowBlock_old_end(electricityBus_excess_2) +-1 InvestmentFlowBlock_old_exo(electricityBus_excess_2) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_excess_0_0)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_excess_0) ++1 flow(electricityBus_excess_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_excess_0_1)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_excess_0) ++1 flow(electricityBus_excess_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_excess_1_2)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_excess_1) ++1 flow(electricityBus_excess_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_excess_1_3)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_excess_1) ++1 flow(electricityBus_excess_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_excess_2_4)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_excess_2) ++1 flow(electricityBus_excess_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_excess_2_5)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_excess_2) ++1 flow(electricityBus_excess_2_5) +<= 0 + +c_u_InvestmentFlowBlock_summed_max(electricityBus_excess)_: +-2.2999999999999998 InvestmentFlowBlock_total(electricityBus_excess_0) +-2.2999999999999998 InvestmentFlowBlock_total(electricityBus_excess_1) +-2.2999999999999998 InvestmentFlowBlock_total(electricityBus_excess_2) ++1 flow(electricityBus_excess_0_0) ++1 flow(electricityBus_excess_0_1) ++1 flow(electricityBus_excess_1_2) ++1 flow(electricityBus_excess_1_3) ++1 flow(electricityBus_excess_2_4) ++1 flow(electricityBus_excess_2_5) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_excess_0_0) <= +inf + 0 <= flow(electricityBus_excess_0_1) <= +inf + 0 <= flow(electricityBus_excess_1_2) <= +inf + 0 <= flow(electricityBus_excess_1_3) <= +inf + 0 <= flow(electricityBus_excess_2_4) <= +inf + 0 <= flow(electricityBus_excess_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_excess_0) <= 1000000 + 0 <= InvestmentFlowBlock_invest(electricityBus_excess_1) <= 1000000 + 0 <= InvestmentFlowBlock_invest(electricityBus_excess_2) <= 1000000 + 0 <= InvestmentFlowBlock_total(electricityBus_excess_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_excess_1) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_excess_2) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_excess_0) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_excess_1) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_excess_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_excess_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_excess_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_excess_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_excess_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_excess_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_excess_2) <= +inf +end diff --git a/tests/lp_files/invest_source_fixed_sink_multi_period.lp b/tests/lp_files/invest_source_fixed_sink_multi_period.lp new file mode 100644 index 000000000..cdd49b48e --- /dev/null +++ b/tests/lp_files/invest_source_fixed_sink_multi_period.lp @@ -0,0 +1,157 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++157.50284813348861 InvestmentFlowBlock_invest(pv_electricityBus_0) ++154.41455699361629 InvestmentFlowBlock_invest(pv_electricityBus_1) ++151.38682058197674 InvestmentFlowBlock_invest(pv_electricityBus_2) ++13 flow(pv_electricityBus_0_0) ++13 flow(pv_electricityBus_0_1) ++12.745098039215685 flow(pv_electricityBus_1_2) ++12.745098039215685 flow(pv_electricityBus_1_3) ++12.49519415609381 flow(pv_electricityBus_2_4) ++12.49519415609381 flow(pv_electricityBus_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(pv_electricityBus_0_0) += 50000 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(pv_electricityBus_0_1) += 80000 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(pv_electricityBus_1_2) += 30000 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(pv_electricityBus_1_3) += 60000 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(pv_electricityBus_2_4) += 70000 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(pv_electricityBus_2_5) += 20000 + +c_e_InvestmentFlowBlock_total_rule(pv_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(pv_electricityBus_0) ++1 InvestmentFlowBlock_total(pv_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(pv_electricityBus_1)_: +-1 InvestmentFlowBlock_invest(pv_electricityBus_1) ++1 InvestmentFlowBlock_old(pv_electricityBus_1) +-1 InvestmentFlowBlock_total(pv_electricityBus_0) ++1 InvestmentFlowBlock_total(pv_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(pv_electricityBus_2)_: +-1 InvestmentFlowBlock_invest(pv_electricityBus_2) ++1 InvestmentFlowBlock_old(pv_electricityBus_2) +-1 InvestmentFlowBlock_total(pv_electricityBus_1) ++1 InvestmentFlowBlock_total(pv_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(pv_electricityBus_0)_: ++1 InvestmentFlowBlock_old_end(pv_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(pv_electricityBus_1)_: ++1 InvestmentFlowBlock_old_end(pv_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(pv_electricityBus_2)_: ++1 InvestmentFlowBlock_old_end(pv_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(pv_electricityBus_0)_: ++1 InvestmentFlowBlock_old_exo(pv_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(pv_electricityBus_1)_: ++1 InvestmentFlowBlock_old_exo(pv_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(pv_electricityBus_2)_: ++1 InvestmentFlowBlock_old_exo(pv_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(pv_electricityBus_0)_: ++1 InvestmentFlowBlock_old(pv_electricityBus_0) +-1 InvestmentFlowBlock_old_end(pv_electricityBus_0) +-1 InvestmentFlowBlock_old_exo(pv_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(pv_electricityBus_1)_: ++1 InvestmentFlowBlock_old(pv_electricityBus_1) +-1 InvestmentFlowBlock_old_end(pv_electricityBus_1) +-1 InvestmentFlowBlock_old_exo(pv_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(pv_electricityBus_2)_: ++1 InvestmentFlowBlock_old(pv_electricityBus_2) +-1 InvestmentFlowBlock_old_end(pv_electricityBus_2) +-1 InvestmentFlowBlock_old_exo(pv_electricityBus_2) += 0 + +c_u_InvestmentFlowBlock_max(pv_electricityBus_0_0)_: +-45 InvestmentFlowBlock_total(pv_electricityBus_0) ++1 flow(pv_electricityBus_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(pv_electricityBus_0_1)_: +-83 InvestmentFlowBlock_total(pv_electricityBus_0) ++1 flow(pv_electricityBus_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(pv_electricityBus_1_2)_: +-65 InvestmentFlowBlock_total(pv_electricityBus_1) ++1 flow(pv_electricityBus_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(pv_electricityBus_1_3)_: +-67 InvestmentFlowBlock_total(pv_electricityBus_1) ++1 flow(pv_electricityBus_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(pv_electricityBus_2_4)_: +-33 InvestmentFlowBlock_total(pv_electricityBus_2) ++1 flow(pv_electricityBus_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(pv_electricityBus_2_5)_: +-96 InvestmentFlowBlock_total(pv_electricityBus_2) ++1 flow(pv_electricityBus_2_5) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(pv_electricityBus_0_0) <= +inf + 0 <= flow(pv_electricityBus_0_1) <= +inf + 0 <= flow(pv_electricityBus_1_2) <= +inf + 0 <= flow(pv_electricityBus_1_3) <= +inf + 0 <= flow(pv_electricityBus_2_4) <= +inf + 0 <= flow(pv_electricityBus_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(pv_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_invest(pv_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_invest(pv_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_total(pv_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(pv_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_total(pv_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old(pv_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old(pv_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old(pv_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(pv_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(pv_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(pv_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(pv_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(pv_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(pv_electricityBus_2) <= +inf +end diff --git a/tests/lp_files/nominal_value_to_zero_multi_period.lp b/tests/lp_files/nominal_value_to_zero_multi_period.lp new file mode 100644 index 000000000..5cb0f3a3e --- /dev/null +++ b/tests/lp_files/nominal_value_to_zero_multi_period.lp @@ -0,0 +1,43 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++0 ONE_VAR_CONSTANT + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(s1_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(s1_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(s1_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(s1_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(s1_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(s1_electricityBus_2_5) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(s1_electricityBus_0_0) <= 0 + 0 <= flow(s1_electricityBus_0_1) <= 0 + 0 <= flow(s1_electricityBus_1_2) <= 0 + 0 <= flow(s1_electricityBus_1_3) <= 0 + 0 <= flow(s1_electricityBus_2_4) <= 0 + 0 <= flow(s1_electricityBus_2_5) <= 0 +end diff --git a/tests/lp_files/storage_multi_period.lp b/tests/lp_files/storage_multi_period.lp new file mode 100644 index 000000000..037c50497 --- /dev/null +++ b/tests/lp_files/storage_multi_period.lp @@ -0,0 +1,117 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++56 flow(electricityBus_storage_no_invest_0_0) ++56 flow(electricityBus_storage_no_invest_0_1) ++54.901960784313722 flow(electricityBus_storage_no_invest_1_2) ++54.901960784313722 flow(electricityBus_storage_no_invest_1_3) ++53.825451749327179 flow(electricityBus_storage_no_invest_2_4) ++53.825451749327179 flow(electricityBus_storage_no_invest_2_5) ++24 flow(storage_no_invest_electricityBus_0_0) ++24 flow(storage_no_invest_electricityBus_0_1) ++23.52941176470588 flow(storage_no_invest_electricityBus_1_2) ++23.52941176470588 flow(storage_no_invest_electricityBus_1_3) ++23.068050749711649 flow(storage_no_invest_electricityBus_2_4) ++23.068050749711649 flow(storage_no_invest_electricityBus_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage_no_invest_0_0) ++1 flow(storage_no_invest_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage_no_invest_0_1) ++1 flow(storage_no_invest_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: +-1 flow(electricityBus_storage_no_invest_1_2) ++1 flow(storage_no_invest_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: +-1 flow(electricityBus_storage_no_invest_1_3) ++1 flow(storage_no_invest_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: +-1 flow(electricityBus_storage_no_invest_2_4) ++1 flow(storage_no_invest_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: +-1 flow(electricityBus_storage_no_invest_2_5) ++1 flow(storage_no_invest_electricityBus_2_5) += 0 + +c_e_GenericStorageBlock_balance_first(storage_no_invest)_: ++1 GenericStorageBlock_storage_content(storage_no_invest_0) +-0.96999999999999997 flow(electricityBus_storage_no_invest_0_0) ++1.1627906976744187 flow(storage_no_invest_electricityBus_0_0) += 34800 + +c_e_GenericStorageBlock_balance(storage_no_invest_0_1)_: +-0.87 GenericStorageBlock_storage_content(storage_no_invest_0) ++1 GenericStorageBlock_storage_content(storage_no_invest_1) +-0.96999999999999997 flow(electricityBus_storage_no_invest_0_1) ++1.1627906976744187 flow(storage_no_invest_electricityBus_0_1) += 0 + +c_e_GenericStorageBlock_balance(storage_no_invest_1_2)_: +-0.87 GenericStorageBlock_storage_content(storage_no_invest_1) ++1 GenericStorageBlock_storage_content(storage_no_invest_2) +-0.96999999999999997 flow(electricityBus_storage_no_invest_1_2) ++1.1627906976744187 flow(storage_no_invest_electricityBus_1_2) += 0 + +c_e_GenericStorageBlock_balance(storage_no_invest_1_3)_: +-0.87 GenericStorageBlock_storage_content(storage_no_invest_2) ++1 GenericStorageBlock_storage_content(storage_no_invest_3) +-0.96999999999999997 flow(electricityBus_storage_no_invest_1_3) ++1.1627906976744187 flow(storage_no_invest_electricityBus_1_3) += 0 + +c_e_GenericStorageBlock_balance(storage_no_invest_2_4)_: +-0.87 GenericStorageBlock_storage_content(storage_no_invest_3) ++1 GenericStorageBlock_storage_content(storage_no_invest_4) +-0.96999999999999997 flow(electricityBus_storage_no_invest_2_4) ++1.1627906976744187 flow(storage_no_invest_electricityBus_2_4) += 0 + +c_e_GenericStorageBlock_balance(storage_no_invest_2_5)_: +-0.87 GenericStorageBlock_storage_content(storage_no_invest_4) ++1 GenericStorageBlock_storage_content(storage_no_invest_5) +-0.96999999999999997 flow(electricityBus_storage_no_invest_2_5) ++1.1627906976744187 flow(storage_no_invest_electricityBus_2_5) += 0 + +c_e_GenericStorageBlock_balanced_cstr(storage_no_invest)_: ++1 GenericStorageBlock_storage_content(storage_no_invest_5) += 40000 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_storage_no_invest_0_0) <= 16667 + 0 <= flow(electricityBus_storage_no_invest_0_1) <= 16667 + 0 <= flow(electricityBus_storage_no_invest_1_2) <= 16667 + 0 <= flow(electricityBus_storage_no_invest_1_3) <= 16667 + 0 <= flow(electricityBus_storage_no_invest_2_4) <= 16667 + 0 <= flow(electricityBus_storage_no_invest_2_5) <= 16667 + 0 <= flow(storage_no_invest_electricityBus_0_0) <= 16667 + 0 <= flow(storage_no_invest_electricityBus_0_1) <= 16667 + 0 <= flow(storage_no_invest_electricityBus_1_2) <= 16667 + 0 <= flow(storage_no_invest_electricityBus_1_3) <= 16667 + 0 <= flow(storage_no_invest_electricityBus_2_4) <= 16667 + 0 <= flow(storage_no_invest_electricityBus_2_5) <= 16667 + 0 <= GenericStorageBlock_storage_content(storage_no_invest_0) <= 100000 + 0 <= GenericStorageBlock_storage_content(storage_no_invest_1) <= 100000 + 0 <= GenericStorageBlock_storage_content(storage_no_invest_2) <= 100000 + 0 <= GenericStorageBlock_storage_content(storage_no_invest_3) <= 100000 + 0 <= GenericStorageBlock_storage_content(storage_no_invest_4) <= 100000 + 0 <= GenericStorageBlock_storage_content(storage_no_invest_5) <= 100000 +end diff --git a/tests/multi_period_constraint_tests.py b/tests/multi_period_constraint_tests.py index 9e0d416b6..ab6de003e 100644 --- a/tests/multi_period_constraint_tests.py +++ b/tests/multi_period_constraint_tests.py @@ -363,3 +363,112 @@ def test_fixed_source_variable_sink(self): ) self.compare_lp_files("fixed_source_variable_sink_multi_period.lp") + + def test_nominal_value_to_zero(self): + """If the nominal value is set to zero nothing should happen.""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Source( + label="s1", outputs={bel: solph.flows.Flow(nominal_value=0)} + ) + self.compare_lp_files("nominal_value_to_zero_multi_period.lp") + + def test_fixed_source_invest_sink(self): + """Constraints test for fixed source + invest sink w. `summed_max`""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Source( + label="wind", + outputs={ + bel: solph.flows.Flow( + fix=[12, 16, 14, 18, 18, 18], nominal_value=1e6 + ) + }, + ) + + solph.components.Sink( + label="excess", + inputs={ + bel: solph.flows.Flow( + summed_max=2.3, + variable_costs=25, + max=0.8, + investment=solph.Investment( + ep_costs=500, maximum=1e6, existing=50, lifetime=20 + ), + ) + }, + ) + + self.compare_lp_files("fixed_source_invest_sink_multi_period.lp") + + def test_investment_lifetime_missing(self): + """Test error raised if lifetime attribute is missing""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Sink( + label="excess", + inputs={ + bel: solph.flows.Flow( + max=0.8, + investment=solph.Investment( + ep_costs=500, maximum=1e6, existing=50 + ), + ) + }, + ) + + msg = ( + "You have to specify a lifetime " + "for an InvestmentFlow in a multi-period model!" + ) + with pytest.raises(ValueError, match=msg): + self.get_om() + + def test_invest_source_fixed_sink(self): + """Constraint test with a fixed sink and a dispatch invest source.""" + + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Source( + label="pv", + outputs={ + bel: solph.flows.Flow( + max=[45, 83, 65, 67, 33, 96], + variable_costs=13, + investment=solph.Investment(ep_costs=123, lifetime=25), + ) + }, + ) + + solph.components.Sink( + label="excess", + inputs={ + bel: solph.flows.Flow( + fix=[0.5, 0.8, 0.3, 0.6, 0.7, 0.2], nominal_value=1e5 + ) + }, + ) + + self.compare_lp_files("invest_source_fixed_sink_multi_period.lp") + + def test_storage(self): + """ """ + bel = solph.buses.Bus(label="electricityBus") + + solph.components.GenericStorage( + label="storage_no_invest", + inputs={ + bel: solph.flows.Flow(nominal_value=16667, variable_costs=56) + }, + outputs={ + bel: solph.flows.Flow(nominal_value=16667, variable_costs=24) + }, + nominal_storage_capacity=1e5, + loss_rate=0.13, + inflow_conversion_factor=0.97, + outflow_conversion_factor=0.86, + initial_storage_level=0.4, + ) + + self.compare_lp_files("storage_multi_period.lp") From d5ef11a6691cb4c91fe95ca0afcaddeb04b4dbb2 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Mon, 7 Mar 2022 11:24:46 +0100 Subject: [PATCH 0164/1363] Remove unnecessary import --- tests/multi_period_constraint_tests.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/multi_period_constraint_tests.py b/tests/multi_period_constraint_tests.py index ab6de003e..ade700f1a 100644 --- a/tests/multi_period_constraint_tests.py +++ b/tests/multi_period_constraint_tests.py @@ -16,7 +16,6 @@ import pandas as pd import pytest -from nose.tools import assert_raises from nose.tools import eq_ from oemof.network.network import Node From 4152f2c7261d88ac83879267b3f5ec049d801d8b Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Mon, 7 Mar 2022 15:09:35 +0100 Subject: [PATCH 0165/1363] Add missing lifetime proxy --- src/oemof/solph/components/_generic_storage.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index cf1facab5..fdd5103e4 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -217,13 +217,13 @@ def _set_flows(self): self.invest_relation_input_capacity is not None and not isinstance(flow.investment, Investment) ): - flow.investment = Investment() + flow.investment = Investment(lifetime=self.lifetime_inflow) for flow in self.outputs.values(): if ( self.invest_relation_output_capacity is not None and not isinstance(flow.investment, Investment) ): - flow.investment = Investment() + flow.investment = Investment(lifetime=self.lifetime_outflow) def _check_invest_attributes(self): if self.investment and self.nominal_storage_capacity is not None: From e727d2dea0ed286ffcbc148c131be81499e1cc85 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Mon, 7 Mar 2022 17:38:15 +0100 Subject: [PATCH 0166/1363] Add bug fixes and TODO --- src/oemof/solph/components/_generic_storage.py | 6 +++--- src/oemof/solph/components/experimental/_sink_dsm.py | 6 +++--- src/oemof/solph/constraints/investment_limit.py | 4 ++-- src/oemof/solph/flows/_investment_flow.py | 2 +- src/oemof/solph/processing.py | 1 + 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index fdd5103e4..29301c15b 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -811,8 +811,8 @@ def _create(self, group=None): if m.es.multi_period: for n in group: error = ( - "For a multi-period model, fixed absolute losses" - " are not supported. Please remove parameter." + "For a multi-period investment model, fixed absolute" + " losses are not supported. Please remove parameter." ) if n.fixed_losses_absolute.default != 0: raise ValueError(error) @@ -1453,7 +1453,7 @@ def _objective_expression(self): ) self.investment_costs = Expression(expr=investment_costs) - self.period_investment_costs = Expression(expr=period_investment_costs) + self.period_investment_costs = period_investment_costs self.fixed_costs = Expression(expr=fixed_costs) self.costs = Expression(expr=investment_costs + fixed_costs) diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index 014c41a00..87b24102e 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -1318,7 +1318,7 @@ def _objective_expression(self): self.variable_costs = Expression(expr=variable_costs) self.fixed_costs = Expression(expr=fixed_costs) self.investment_costs = Expression(expr=investment_costs) - self.period_investment_costs = Expression(expr=period_investment_costs) + self.period_investment_costs = period_investment_costs self.costs = Expression( expr=investment_costs + variable_costs + fixed_costs ) @@ -2954,7 +2954,7 @@ def _objective_expression(self): self.variable_costs = Expression(expr=variable_costs) self.fixed_costs = Expression(expr=fixed_costs) self.investment_costs = Expression(expr=investment_costs) - self.period_investment_costs = Expression(expr=period_investment_costs) + self.period_investment_costs = period_investment_costs self.costs = Expression( expr=investment_costs + variable_costs + fixed_costs ) @@ -5229,7 +5229,7 @@ def _objective_expression(self): self.variable_costs = Expression(expr=variable_costs) self.fixed_costs = Expression(expr=fixed_costs) self.investment_costs = Expression(expr=investment_costs) - self.period_investment_costs = Expression(expr=period_investment_costs) + self.period_investment_costs = period_investment_costs self.costs = Expression( expr=investment_costs + variable_costs + fixed_costs ) diff --git a/src/oemof/solph/constraints/investment_limit.py b/src/oemof/solph/constraints/investment_limit.py index 0f4d717f2..55a82bdbe 100644 --- a/src/oemof/solph/constraints/investment_limit.py +++ b/src/oemof/solph/constraints/investment_limit.py @@ -95,8 +95,8 @@ def investment_limit_per_period(model, limit=None): def investment_period_rule(m, p): expr = 0 - if hasattr(m, "InvestmentFlow"): - expr += m.InvestmentFlow.period_investment_costs[p] + if hasattr(m, "InvestmentFlowBlock"): + expr += m.InvestmentFlowBlock.period_investment_costs[p] if hasattr(m, "GenericInvestmentStorageBlock"): expr += m.GenericInvestmentStorageBlock.period_investment_costs[p] diff --git a/src/oemof/solph/flows/_investment_flow.py b/src/oemof/solph/flows/_investment_flow.py index 111f9a35a..b5dd2aea9 100644 --- a/src/oemof/solph/flows/_investment_flow.py +++ b/src/oemof/solph/flows/_investment_flow.py @@ -757,7 +757,7 @@ def _objective_expression(self): ) self.investment_costs = Expression(expr=investment_costs) - self.period_investment_costs = Expression(expr=period_investment_costs) + self.period_investment_costs = period_investment_costs self.fixed_costs = Expression(expr=fixed_costs) self.costs = Expression(expr=investment_costs + fixed_costs) diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index 12f12ca0a..61ba8ab67 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -325,6 +325,7 @@ def _replace_non_timeindex_indices( to_concat.append(period_indexed_df) # Handle storages differently + # TODO: Handle SinkDSM for DIW and DLR approach! if "storage_content" not in timestep_indexed_df.columns: timestep_indexed_df = timestep_indexed_df.dropna() if not timestep_indexed_df.empty: From 2ff0dd156f0f71382d1aa237d4c1102e3b37b4a0 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Mon, 7 Mar 2022 17:42:32 +0100 Subject: [PATCH 0167/1363] Add / modify tests --- tests/constraint_tests.py | 2 +- tests/lp_files/activity_costs_multi_period.lp | 127 ++ .../connect_investment_multi_period.lp | 698 +++++++++++ .../lp_files/flow_count_limit_multi_period.lp | 320 +++++ .../lp_files/inactivity_costs_multi_period.lp | 128 ++ ...ear_transformer_chp_invest_multi_period.lp | 277 +++++ .../linear_transformer_chp_multi_period.lp | 168 +++ .../maximum_shutdowns_multi_period.lp | 177 +++ .../lp_files/maximum_startups_multi_period.lp | 177 +++ .../lp_files/min_max_runtime_multi_period.lp | 275 +++++ .../offsettransformer_multi_period.lp | 182 +++ tests/lp_files/periodical_emission_limit.lp | 6 +- tests/lp_files/periodical_investment_limit.lp | 582 +++++++++ ...eriodical_investment_limit_with_dsm_DIW.lp | 614 +++++++++ ...eriodical_investment_limit_with_dsm_DLR.lp | 762 ++++++++++++ ...iodical_investment_limit_with_dsm_oemof.lp | 463 +++++++ ...wise_linear_transformer_cc_multi_period.lp | 583 +++++++++ ...ise_linear_transformer_dcc_multi_period.lp | 565 +++++++++ tests/lp_files/shared_limit_multi_period.lp | 229 ++++ .../source_with_gradient_multi_period.lp | 128 ++ ...ce_with_nonconvex_gradient_multi_period.lp | 215 ++++ .../storage_fixed_losses_multi_period.lp | 117 ++ .../lp_files/storage_invest_1_multi_period.lp | 504 ++++++++ .../lp_files/storage_invest_2_multi_period.lp | 422 +++++++ .../lp_files/storage_invest_3_multi_period.lp | 327 +++++ .../lp_files/storage_invest_4_multi_period.lp | 205 +++ .../lp_files/storage_invest_5_multi_period.lp | 338 +++++ .../lp_files/storage_invest_6_multi_period.lp | 434 +++++++ .../storage_invest_minimum_multi_period.lp | 204 +++ .../storage_unbalanced_multi_period.lp | 104 ++ .../transformer_invest_multi_period.lp | 373 ++++++ ...ormer_invest_with_existing_multi_period.lp | 374 ++++++ tests/lp_files/transformer_multi_period.lp | 264 ++++ tests/lp_files/variable_chp_multi_period.lp | 271 ++++ tests/multi_period_constraint_tests.py | 1099 +++++++++++++++-- 35 files changed, 11613 insertions(+), 101 deletions(-) create mode 100644 tests/lp_files/activity_costs_multi_period.lp create mode 100644 tests/lp_files/connect_investment_multi_period.lp create mode 100644 tests/lp_files/flow_count_limit_multi_period.lp create mode 100644 tests/lp_files/inactivity_costs_multi_period.lp create mode 100644 tests/lp_files/linear_transformer_chp_invest_multi_period.lp create mode 100644 tests/lp_files/linear_transformer_chp_multi_period.lp create mode 100644 tests/lp_files/maximum_shutdowns_multi_period.lp create mode 100644 tests/lp_files/maximum_startups_multi_period.lp create mode 100644 tests/lp_files/min_max_runtime_multi_period.lp create mode 100644 tests/lp_files/offsettransformer_multi_period.lp create mode 100644 tests/lp_files/periodical_investment_limit.lp create mode 100644 tests/lp_files/periodical_investment_limit_with_dsm_DIW.lp create mode 100644 tests/lp_files/periodical_investment_limit_with_dsm_DLR.lp create mode 100644 tests/lp_files/periodical_investment_limit_with_dsm_oemof.lp create mode 100644 tests/lp_files/piecewise_linear_transformer_cc_multi_period.lp create mode 100644 tests/lp_files/piecewise_linear_transformer_dcc_multi_period.lp create mode 100644 tests/lp_files/shared_limit_multi_period.lp create mode 100644 tests/lp_files/source_with_gradient_multi_period.lp create mode 100644 tests/lp_files/source_with_nonconvex_gradient_multi_period.lp create mode 100644 tests/lp_files/storage_fixed_losses_multi_period.lp create mode 100644 tests/lp_files/storage_invest_1_multi_period.lp create mode 100644 tests/lp_files/storage_invest_2_multi_period.lp create mode 100644 tests/lp_files/storage_invest_3_multi_period.lp create mode 100644 tests/lp_files/storage_invest_4_multi_period.lp create mode 100644 tests/lp_files/storage_invest_5_multi_period.lp create mode 100644 tests/lp_files/storage_invest_6_multi_period.lp create mode 100644 tests/lp_files/storage_invest_minimum_multi_period.lp create mode 100644 tests/lp_files/storage_unbalanced_multi_period.lp create mode 100644 tests/lp_files/transformer_invest_multi_period.lp create mode 100644 tests/lp_files/transformer_invest_with_existing_multi_period.lp create mode 100644 tests/lp_files/transformer_multi_period.lp create mode 100644 tests/lp_files/variable_chp_multi_period.lp diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index 54177d632..3735442f6 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -827,7 +827,7 @@ def define_emission_limit(): assert_raises(AttributeError, define_emission_limit) def test_flow_without_emission_for_emission_constraint_no_error(self): - """Test that no error is thrown if no flows are explicity passed""" + """Test that no error is thrown if no flows are explicitly passed""" bel = solph.buses.Bus(label="electricityBus") solph.components.Source( label="source1", diff --git a/tests/lp_files/activity_costs_multi_period.lp b/tests/lp_files/activity_costs_multi_period.lp new file mode 100644 index 000000000..0defcf2ed --- /dev/null +++ b/tests/lp_files/activity_costs_multi_period.lp @@ -0,0 +1,127 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++2 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_0) ++2 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_1) ++1.9607843137254901 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_2) ++1.9607843137254901 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_3) ++1.9223375624759707 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_4) ++1.9223375624759707 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_5) ++10 flow(cheap_plant_activity_costs_Bus_C_0_0) ++10 flow(cheap_plant_activity_costs_Bus_C_0_1) ++9.8039215686274499 flow(cheap_plant_activity_costs_Bus_C_1_2) ++9.8039215686274499 flow(cheap_plant_activity_costs_Bus_C_1_3) ++9.6116878123798539 flow(cheap_plant_activity_costs_Bus_C_2_4) ++9.6116878123798539 flow(cheap_plant_activity_costs_Bus_C_2_5) + +s.t. + +c_e_BusBlock_balance(Bus_C_0_0)_: ++1 flow(cheap_plant_activity_costs_Bus_C_0_0) += 0 + +c_e_BusBlock_balance(Bus_C_0_1)_: ++1 flow(cheap_plant_activity_costs_Bus_C_0_1) += 0 + +c_e_BusBlock_balance(Bus_C_1_2)_: ++1 flow(cheap_plant_activity_costs_Bus_C_1_2) += 0 + +c_e_BusBlock_balance(Bus_C_1_3)_: ++1 flow(cheap_plant_activity_costs_Bus_C_1_3) += 0 + +c_e_BusBlock_balance(Bus_C_2_4)_: ++1 flow(cheap_plant_activity_costs_Bus_C_2_4) += 0 + +c_e_BusBlock_balance(Bus_C_2_5)_: ++1 flow(cheap_plant_activity_costs_Bus_C_2_5) += 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_activity_costs_Bus_C_0_0)_: ++5 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_0) +-1 flow(cheap_plant_activity_costs_Bus_C_0_0) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_activity_costs_Bus_C_0_1)_: ++5 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_1) +-1 flow(cheap_plant_activity_costs_Bus_C_0_1) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_activity_costs_Bus_C_1_2)_: ++5 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_2) +-1 flow(cheap_plant_activity_costs_Bus_C_1_2) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_activity_costs_Bus_C_1_3)_: ++5 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_3) +-1 flow(cheap_plant_activity_costs_Bus_C_1_3) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_activity_costs_Bus_C_2_4)_: ++5 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_4) +-1 flow(cheap_plant_activity_costs_Bus_C_2_4) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_activity_costs_Bus_C_2_5)_: ++5 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_5) +-1 flow(cheap_plant_activity_costs_Bus_C_2_5) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_activity_costs_Bus_C_0_0)_: +-10 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_0) ++1 flow(cheap_plant_activity_costs_Bus_C_0_0) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_activity_costs_Bus_C_0_1)_: +-10 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_1) ++1 flow(cheap_plant_activity_costs_Bus_C_0_1) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_activity_costs_Bus_C_1_2)_: +-10 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_2) ++1 flow(cheap_plant_activity_costs_Bus_C_1_2) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_activity_costs_Bus_C_1_3)_: +-10 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_3) ++1 flow(cheap_plant_activity_costs_Bus_C_1_3) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_activity_costs_Bus_C_2_4)_: +-10 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_4) ++1 flow(cheap_plant_activity_costs_Bus_C_2_4) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_activity_costs_Bus_C_2_5)_: +-10 NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_5) ++1 flow(cheap_plant_activity_costs_Bus_C_2_5) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(cheap_plant_activity_costs_Bus_C_0_0) <= 10 + 0 <= flow(cheap_plant_activity_costs_Bus_C_0_1) <= 10 + 0 <= flow(cheap_plant_activity_costs_Bus_C_1_2) <= 10 + 0 <= flow(cheap_plant_activity_costs_Bus_C_1_3) <= 10 + 0 <= flow(cheap_plant_activity_costs_Bus_C_2_4) <= 10 + 0 <= flow(cheap_plant_activity_costs_Bus_C_2_5) <= 10 + 0 <= NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_0) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_1) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_2) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_3) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_4) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_5) <= 1 +binary + NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_0) + NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_1) + NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_2) + NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_3) + NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_4) + NonConvexFlowBlock_status(cheap_plant_activity_costs_Bus_C_5) +end diff --git a/tests/lp_files/connect_investment_multi_period.lp b/tests/lp_files/connect_investment_multi_period.lp new file mode 100644 index 000000000..7f5098120 --- /dev/null +++ b/tests/lp_files/connect_investment_multi_period.lp @@ -0,0 +1,698 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++150.83828257744048 GenericInvestmentStorageBlock_invest(storage_0) ++147.8806691935691 GenericInvestmentStorageBlock_invest(storage_1) ++144.98104822898929 GenericInvestmentStorageBlock_invest(storage_2) ++520.13200888772576 InvestmentFlowBlock_invest(Bus1_Sink_0) ++509.93334204678996 InvestmentFlowBlock_invest(Bus1_Sink_1) ++499.9346490654803 InvestmentFlowBlock_invest(Bus1_Sink_2) ++127.95247418638057 InvestmentFlowBlock_invest(Source_Bus1_0) ++125.44360214351036 InvestmentFlowBlock_invest(Source_Bus1_1) ++122.9839236701082 InvestmentFlowBlock_invest(Source_Bus1_2) + +s.t. + +c_e_equate_InvestmentFlowBlock_invest(Source_Bus1_0)_InvestmentFlowBlock_invest(Bus1_Sink_0)_: +-1 InvestmentFlowBlock_invest(Bus1_Sink_0) ++2 InvestmentFlowBlock_invest(Source_Bus1_0) += 0 + +c_e_equate_InvestmentFlowBlock_invest(Source_Bus1_0)_GenericInvestmentStorageBlock_invest(storage_0)_: +-1 GenericInvestmentStorageBlock_invest(storage_0) ++1 InvestmentFlowBlock_invest(Source_Bus1_0) += 0 + +c_e_BusBlock_balance(Bus1_0_0)_: +-1 flow(Bus1_Sink_0_0) +-1 flow(Bus1_storage_0_0) ++1 flow(Source_Bus1_0_0) ++1 flow(storage_Bus1_0_0) += 0 + +c_e_BusBlock_balance(Bus1_0_1)_: +-1 flow(Bus1_Sink_0_1) +-1 flow(Bus1_storage_0_1) ++1 flow(Source_Bus1_0_1) ++1 flow(storage_Bus1_0_1) += 0 + +c_e_BusBlock_balance(Bus1_1_2)_: +-1 flow(Bus1_Sink_1_2) +-1 flow(Bus1_storage_1_2) ++1 flow(Source_Bus1_1_2) ++1 flow(storage_Bus1_1_2) += 0 + +c_e_BusBlock_balance(Bus1_1_3)_: +-1 flow(Bus1_Sink_1_3) +-1 flow(Bus1_storage_1_3) ++1 flow(Source_Bus1_1_3) ++1 flow(storage_Bus1_1_3) += 0 + +c_e_BusBlock_balance(Bus1_2_4)_: +-1 flow(Bus1_Sink_2_4) +-1 flow(Bus1_storage_2_4) ++1 flow(Source_Bus1_2_4) ++1 flow(storage_Bus1_2_4) += 0 + +c_e_BusBlock_balance(Bus1_2_5)_: +-1 flow(Bus1_Sink_2_5) +-1 flow(Bus1_storage_2_5) ++1 flow(Source_Bus1_2_5) ++1 flow(storage_Bus1_2_5) += 0 + +c_e_InvestmentFlowBlock_total_rule(Bus1_Sink_0)_: +-1 InvestmentFlowBlock_invest(Bus1_Sink_0) ++1 InvestmentFlowBlock_total(Bus1_Sink_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(Bus1_Sink_1)_: +-1 InvestmentFlowBlock_invest(Bus1_Sink_1) ++1 InvestmentFlowBlock_old(Bus1_Sink_1) +-1 InvestmentFlowBlock_total(Bus1_Sink_0) ++1 InvestmentFlowBlock_total(Bus1_Sink_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(Bus1_Sink_2)_: +-1 InvestmentFlowBlock_invest(Bus1_Sink_2) ++1 InvestmentFlowBlock_old(Bus1_Sink_2) +-1 InvestmentFlowBlock_total(Bus1_Sink_1) ++1 InvestmentFlowBlock_total(Bus1_Sink_2) += 0 + +c_e_InvestmentFlowBlock_total_rule(Bus1_storage_0)_: +-1 InvestmentFlowBlock_invest(Bus1_storage_0) ++1 InvestmentFlowBlock_total(Bus1_storage_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(Bus1_storage_1)_: +-1 InvestmentFlowBlock_invest(Bus1_storage_1) ++1 InvestmentFlowBlock_old(Bus1_storage_1) +-1 InvestmentFlowBlock_total(Bus1_storage_0) ++1 InvestmentFlowBlock_total(Bus1_storage_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(Bus1_storage_2)_: +-1 InvestmentFlowBlock_invest(Bus1_storage_2) ++1 InvestmentFlowBlock_old(Bus1_storage_2) +-1 InvestmentFlowBlock_total(Bus1_storage_1) ++1 InvestmentFlowBlock_total(Bus1_storage_2) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_0)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_0) ++1 InvestmentFlowBlock_total(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_1)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_1) ++1 InvestmentFlowBlock_old(Source_Bus1_1) +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 InvestmentFlowBlock_total(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_2)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_2) ++1 InvestmentFlowBlock_old(Source_Bus1_2) +-1 InvestmentFlowBlock_total(Source_Bus1_1) ++1 InvestmentFlowBlock_total(Source_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_Bus1_0)_: +-1 InvestmentFlowBlock_invest(storage_Bus1_0) ++1 InvestmentFlowBlock_total(storage_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_Bus1_1)_: +-1 InvestmentFlowBlock_invest(storage_Bus1_1) ++1 InvestmentFlowBlock_old(storage_Bus1_1) +-1 InvestmentFlowBlock_total(storage_Bus1_0) ++1 InvestmentFlowBlock_total(storage_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_Bus1_2)_: +-1 InvestmentFlowBlock_invest(storage_Bus1_2) ++1 InvestmentFlowBlock_old(storage_Bus1_2) +-1 InvestmentFlowBlock_total(storage_Bus1_1) ++1 InvestmentFlowBlock_total(storage_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Bus1_Sink_0)_: ++1 InvestmentFlowBlock_old_end(Bus1_Sink_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Bus1_Sink_1)_: ++1 InvestmentFlowBlock_old_end(Bus1_Sink_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Bus1_Sink_2)_: ++1 InvestmentFlowBlock_old_end(Bus1_Sink_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Bus1_storage_0)_: ++1 InvestmentFlowBlock_old_end(Bus1_storage_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Bus1_storage_1)_: ++1 InvestmentFlowBlock_old_end(Bus1_storage_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Bus1_storage_2)_: ++1 InvestmentFlowBlock_old_end(Bus1_storage_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Source_Bus1_0)_: ++1 InvestmentFlowBlock_old_end(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Source_Bus1_1)_: ++1 InvestmentFlowBlock_old_end(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Source_Bus1_2)_: ++1 InvestmentFlowBlock_old_end(Source_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage_Bus1_0)_: ++1 InvestmentFlowBlock_old_end(storage_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage_Bus1_1)_: ++1 InvestmentFlowBlock_old_end(storage_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage_Bus1_2)_: ++1 InvestmentFlowBlock_old_end(storage_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Bus1_Sink_0)_: ++1 InvestmentFlowBlock_old_exo(Bus1_Sink_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Bus1_Sink_1)_: ++1 InvestmentFlowBlock_old_exo(Bus1_Sink_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Bus1_Sink_2)_: ++1 InvestmentFlowBlock_old_exo(Bus1_Sink_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Bus1_storage_0)_: ++1 InvestmentFlowBlock_old_exo(Bus1_storage_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Bus1_storage_1)_: ++1 InvestmentFlowBlock_old_exo(Bus1_storage_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Bus1_storage_2)_: ++1 InvestmentFlowBlock_old_exo(Bus1_storage_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Source_Bus1_0)_: ++1 InvestmentFlowBlock_old_exo(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Source_Bus1_1)_: ++1 InvestmentFlowBlock_old_exo(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Source_Bus1_2)_: ++1 InvestmentFlowBlock_old_exo(Source_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage_Bus1_0)_: ++1 InvestmentFlowBlock_old_exo(storage_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage_Bus1_1)_: ++1 InvestmentFlowBlock_old_exo(storage_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage_Bus1_2)_: ++1 InvestmentFlowBlock_old_exo(storage_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(Bus1_Sink_0)_: ++1 InvestmentFlowBlock_old(Bus1_Sink_0) +-1 InvestmentFlowBlock_old_end(Bus1_Sink_0) +-1 InvestmentFlowBlock_old_exo(Bus1_Sink_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(Bus1_Sink_1)_: ++1 InvestmentFlowBlock_old(Bus1_Sink_1) +-1 InvestmentFlowBlock_old_end(Bus1_Sink_1) +-1 InvestmentFlowBlock_old_exo(Bus1_Sink_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(Bus1_Sink_2)_: ++1 InvestmentFlowBlock_old(Bus1_Sink_2) +-1 InvestmentFlowBlock_old_end(Bus1_Sink_2) +-1 InvestmentFlowBlock_old_exo(Bus1_Sink_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(Bus1_storage_0)_: ++1 InvestmentFlowBlock_old(Bus1_storage_0) +-1 InvestmentFlowBlock_old_end(Bus1_storage_0) +-1 InvestmentFlowBlock_old_exo(Bus1_storage_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(Bus1_storage_1)_: ++1 InvestmentFlowBlock_old(Bus1_storage_1) +-1 InvestmentFlowBlock_old_end(Bus1_storage_1) +-1 InvestmentFlowBlock_old_exo(Bus1_storage_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(Bus1_storage_2)_: ++1 InvestmentFlowBlock_old(Bus1_storage_2) +-1 InvestmentFlowBlock_old_end(Bus1_storage_2) +-1 InvestmentFlowBlock_old_exo(Bus1_storage_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(Source_Bus1_0)_: ++1 InvestmentFlowBlock_old(Source_Bus1_0) +-1 InvestmentFlowBlock_old_end(Source_Bus1_0) +-1 InvestmentFlowBlock_old_exo(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(Source_Bus1_1)_: ++1 InvestmentFlowBlock_old(Source_Bus1_1) +-1 InvestmentFlowBlock_old_end(Source_Bus1_1) +-1 InvestmentFlowBlock_old_exo(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(Source_Bus1_2)_: ++1 InvestmentFlowBlock_old(Source_Bus1_2) +-1 InvestmentFlowBlock_old_end(Source_Bus1_2) +-1 InvestmentFlowBlock_old_exo(Source_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage_Bus1_0)_: ++1 InvestmentFlowBlock_old(storage_Bus1_0) +-1 InvestmentFlowBlock_old_end(storage_Bus1_0) +-1 InvestmentFlowBlock_old_exo(storage_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage_Bus1_1)_: ++1 InvestmentFlowBlock_old(storage_Bus1_1) +-1 InvestmentFlowBlock_old_end(storage_Bus1_1) +-1 InvestmentFlowBlock_old_exo(storage_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage_Bus1_2)_: ++1 InvestmentFlowBlock_old(storage_Bus1_2) +-1 InvestmentFlowBlock_old_end(storage_Bus1_2) +-1 InvestmentFlowBlock_old_exo(storage_Bus1_2) += 0 + +c_u_InvestmentFlowBlock_max(Bus1_Sink_0_0)_: +-1 InvestmentFlowBlock_total(Bus1_Sink_0) ++1 flow(Bus1_Sink_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_Sink_0_1)_: +-1 InvestmentFlowBlock_total(Bus1_Sink_0) ++1 flow(Bus1_Sink_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_Sink_1_2)_: +-1 InvestmentFlowBlock_total(Bus1_Sink_1) ++1 flow(Bus1_Sink_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_Sink_1_3)_: +-1 InvestmentFlowBlock_total(Bus1_Sink_1) ++1 flow(Bus1_Sink_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_Sink_2_4)_: +-1 InvestmentFlowBlock_total(Bus1_Sink_2) ++1 flow(Bus1_Sink_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_Sink_2_5)_: +-1 InvestmentFlowBlock_total(Bus1_Sink_2) ++1 flow(Bus1_Sink_2_5) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_storage_0_0)_: +-1 InvestmentFlowBlock_total(Bus1_storage_0) ++1 flow(Bus1_storage_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_storage_0_1)_: +-1 InvestmentFlowBlock_total(Bus1_storage_0) ++1 flow(Bus1_storage_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_storage_1_2)_: +-1 InvestmentFlowBlock_total(Bus1_storage_1) ++1 flow(Bus1_storage_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_storage_1_3)_: +-1 InvestmentFlowBlock_total(Bus1_storage_1) ++1 flow(Bus1_storage_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_storage_2_4)_: +-1 InvestmentFlowBlock_total(Bus1_storage_2) ++1 flow(Bus1_storage_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_storage_2_5)_: +-1 InvestmentFlowBlock_total(Bus1_storage_2) ++1 flow(Bus1_storage_2_5) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0_0)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0_1)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_1_2)_: +-1 InvestmentFlowBlock_total(Source_Bus1_1) ++1 flow(Source_Bus1_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_1_3)_: +-1 InvestmentFlowBlock_total(Source_Bus1_1) ++1 flow(Source_Bus1_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_2_4)_: +-1 InvestmentFlowBlock_total(Source_Bus1_2) ++1 flow(Source_Bus1_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_2_5)_: +-1 InvestmentFlowBlock_total(Source_Bus1_2) ++1 flow(Source_Bus1_2_5) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_Bus1_0_0)_: +-1 InvestmentFlowBlock_total(storage_Bus1_0) ++1 flow(storage_Bus1_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_Bus1_0_1)_: +-1 InvestmentFlowBlock_total(storage_Bus1_0) ++1 flow(storage_Bus1_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_Bus1_1_2)_: +-1 InvestmentFlowBlock_total(storage_Bus1_1) ++1 flow(storage_Bus1_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_Bus1_1_3)_: +-1 InvestmentFlowBlock_total(storage_Bus1_1) ++1 flow(storage_Bus1_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_Bus1_2_4)_: +-1 InvestmentFlowBlock_total(storage_Bus1_2) ++1 flow(storage_Bus1_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_Bus1_2_5)_: +-1 InvestmentFlowBlock_total(storage_Bus1_2) ++1 flow(storage_Bus1_2_5) +<= 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_0)_: +-1 GenericInvestmentStorageBlock_invest(storage_0) ++1 GenericInvestmentStorageBlock_total(storage_0) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_1)_: +-1 GenericInvestmentStorageBlock_invest(storage_1) ++1 GenericInvestmentStorageBlock_old(storage_1) +-1 GenericInvestmentStorageBlock_total(storage_0) ++1 GenericInvestmentStorageBlock_total(storage_1) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_2)_: +-1 GenericInvestmentStorageBlock_invest(storage_2) ++1 GenericInvestmentStorageBlock_old(storage_2) +-1 GenericInvestmentStorageBlock_total(storage_1) ++1 GenericInvestmentStorageBlock_total(storage_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage_0)_: ++1 GenericInvestmentStorageBlock_old_end(storage_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage_1)_: ++1 GenericInvestmentStorageBlock_old_end(storage_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage_2)_: ++1 GenericInvestmentStorageBlock_old_end(storage_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage_0)_: ++1 GenericInvestmentStorageBlock_old_exo(storage_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage_1)_: ++1 GenericInvestmentStorageBlock_old_exo(storage_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage_2)_: ++1 GenericInvestmentStorageBlock_old_exo(storage_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage_0)_: ++1 GenericInvestmentStorageBlock_old(storage_0) +-1 GenericInvestmentStorageBlock_old_end(storage_0) +-1 GenericInvestmentStorageBlock_old_exo(storage_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage_1)_: ++1 GenericInvestmentStorageBlock_old(storage_1) +-1 GenericInvestmentStorageBlock_old_end(storage_1) +-1 GenericInvestmentStorageBlock_old_exo(storage_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage_2)_: ++1 GenericInvestmentStorageBlock_old(storage_2) +-1 GenericInvestmentStorageBlock_old_end(storage_2) +-1 GenericInvestmentStorageBlock_old_exo(storage_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_0_1)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_0) ++1 GenericInvestmentStorageBlock_storage_content(storage_1) +-1 flow(Bus1_storage_0_1) ++1 flow(storage_Bus1_0_1) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_1_2)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_1) ++1 GenericInvestmentStorageBlock_storage_content(storage_2) +-1 flow(Bus1_storage_1_2) ++1 flow(storage_Bus1_1_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_1_3)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_2) ++1 GenericInvestmentStorageBlock_storage_content(storage_3) +-1 flow(Bus1_storage_1_3) ++1 flow(storage_Bus1_1_3) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_2_4)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_3) ++1 GenericInvestmentStorageBlock_storage_content(storage_4) +-1 flow(Bus1_storage_2_4) ++1 flow(storage_Bus1_2_4) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_2_5)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_4) ++1 GenericInvestmentStorageBlock_storage_content(storage_5) +-1 flow(Bus1_storage_2_5) ++1 flow(storage_Bus1_2_5) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_0)_: +-0.20000000000000001 GenericInvestmentStorageBlock_total(storage_0) ++1 InvestmentFlowBlock_total(Bus1_storage_0) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_1)_: +-0.20000000000000001 GenericInvestmentStorageBlock_total(storage_1) ++1 InvestmentFlowBlock_total(Bus1_storage_1) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_2)_: +-0.20000000000000001 GenericInvestmentStorageBlock_total(storage_2) ++1 InvestmentFlowBlock_total(Bus1_storage_2) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_0)_: +-0.20000000000000001 GenericInvestmentStorageBlock_total(storage_0) ++1 InvestmentFlowBlock_total(storage_Bus1_0) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_1)_: +-0.20000000000000001 GenericInvestmentStorageBlock_total(storage_1) ++1 InvestmentFlowBlock_total(storage_Bus1_1) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_2)_: +-0.20000000000000001 GenericInvestmentStorageBlock_total(storage_2) ++1 InvestmentFlowBlock_total(storage_Bus1_2) += 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_0_0)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_0) +-1 GenericInvestmentStorageBlock_total(storage_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_0_1)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_1) +-1 GenericInvestmentStorageBlock_total(storage_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_1_2)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_2) +-1 GenericInvestmentStorageBlock_total(storage_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_1_3)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_3) +-1 GenericInvestmentStorageBlock_total(storage_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_2_4)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_4) +-1 GenericInvestmentStorageBlock_total(storage_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_2_5)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_5) +-1 GenericInvestmentStorageBlock_total(storage_2) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(Bus1_Sink_0_0) <= +inf + 0 <= flow(Bus1_Sink_0_1) <= +inf + 0 <= flow(Bus1_Sink_1_2) <= +inf + 0 <= flow(Bus1_Sink_1_3) <= +inf + 0 <= flow(Bus1_Sink_2_4) <= +inf + 0 <= flow(Bus1_Sink_2_5) <= +inf + 0 <= flow(Bus1_storage_0_0) <= +inf + 0 <= flow(Bus1_storage_0_1) <= +inf + 0 <= flow(Bus1_storage_1_2) <= +inf + 0 <= flow(Bus1_storage_1_3) <= +inf + 0 <= flow(Bus1_storage_2_4) <= +inf + 0 <= flow(Bus1_storage_2_5) <= +inf + 0 <= flow(Source_Bus1_0_0) <= +inf + 0 <= flow(Source_Bus1_0_1) <= +inf + 0 <= flow(Source_Bus1_1_2) <= +inf + 0 <= flow(Source_Bus1_1_3) <= +inf + 0 <= flow(Source_Bus1_2_4) <= +inf + 0 <= flow(Source_Bus1_2_5) <= +inf + 0 <= flow(storage_Bus1_0_0) <= +inf + 0 <= flow(storage_Bus1_0_1) <= +inf + 0 <= flow(storage_Bus1_1_2) <= +inf + 0 <= flow(storage_Bus1_1_3) <= +inf + 0 <= flow(storage_Bus1_2_4) <= +inf + 0 <= flow(storage_Bus1_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(Bus1_Sink_0) <= +inf + 0 <= InvestmentFlowBlock_invest(Bus1_Sink_1) <= +inf + 0 <= InvestmentFlowBlock_invest(Bus1_Sink_2) <= +inf + 0 <= InvestmentFlowBlock_invest(Bus1_storage_0) <= +inf + 0 <= InvestmentFlowBlock_invest(Bus1_storage_1) <= +inf + 0 <= InvestmentFlowBlock_invest(Bus1_storage_2) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_invest(storage_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_invest(storage_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_total(Bus1_Sink_0) <= +inf + 0 <= InvestmentFlowBlock_total(Bus1_Sink_1) <= +inf + 0 <= InvestmentFlowBlock_total(Bus1_Sink_2) <= +inf + 0 <= InvestmentFlowBlock_total(Bus1_storage_0) <= +inf + 0 <= InvestmentFlowBlock_total(Bus1_storage_1) <= +inf + 0 <= InvestmentFlowBlock_total(Bus1_storage_2) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_total(storage_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_total(storage_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old(Bus1_Sink_0) <= +inf + 0 <= InvestmentFlowBlock_old(Bus1_Sink_1) <= +inf + 0 <= InvestmentFlowBlock_old(Bus1_Sink_2) <= +inf + 0 <= InvestmentFlowBlock_old(Bus1_storage_0) <= +inf + 0 <= InvestmentFlowBlock_old(Bus1_storage_1) <= +inf + 0 <= InvestmentFlowBlock_old(Bus1_storage_2) <= +inf + 0 <= InvestmentFlowBlock_old(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old(storage_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old(storage_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old(storage_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(Bus1_Sink_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(Bus1_Sink_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(Bus1_Sink_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(Bus1_storage_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(Bus1_storage_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(Bus1_storage_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Bus1_Sink_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Bus1_Sink_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Bus1_Sink_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Bus1_storage_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Bus1_storage_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Bus1_storage_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage_Bus1_2) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_0) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_1) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_2) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_3) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_4) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_5) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage_0) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage_1) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage_2) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage_0) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage_1) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage_2) <= +inf +end diff --git a/tests/lp_files/flow_count_limit_multi_period.lp b/tests/lp_files/flow_count_limit_multi_period.lp new file mode 100644 index 000000000..9e7c80338 --- /dev/null +++ b/tests/lp_files/flow_count_limit_multi_period.lp @@ -0,0 +1,320 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++0 ONE_VAR_CONSTANT + +s.t. + +c_e_emission_factor_constraint(0)_: ++1 NonConvexFlowBlock_status(source1_electricityBus_0) ++1 NonConvexFlowBlock_status(source2_electricityBus_0) +-1 emission_factor(0) += 0 + +c_e_emission_factor_constraint(1)_: ++1 NonConvexFlowBlock_status(source1_electricityBus_1) ++1 NonConvexFlowBlock_status(source2_electricityBus_1) +-1 emission_factor(1) += 0 + +c_e_emission_factor_constraint(2)_: ++1 NonConvexFlowBlock_status(source1_electricityBus_2) ++1 NonConvexFlowBlock_status(source2_electricityBus_2) +-1 emission_factor(2) += 0 + +c_e_emission_factor_constraint(3)_: ++1 NonConvexFlowBlock_status(source1_electricityBus_3) ++1 NonConvexFlowBlock_status(source2_electricityBus_3) +-1 emission_factor(3) += 0 + +c_e_emission_factor_constraint(4)_: ++1 NonConvexFlowBlock_status(source1_electricityBus_4) ++1 NonConvexFlowBlock_status(source2_electricityBus_4) +-1 emission_factor(4) += 0 + +c_e_emission_factor_constraint(5)_: ++1 NonConvexFlowBlock_status(source1_electricityBus_5) ++1 NonConvexFlowBlock_status(source2_electricityBus_5) +-1 emission_factor(5) += 0 + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(source1_electricityBus_0_0) ++1 flow(source2_electricityBus_0_0) ++1 flow(source3_electricityBus_0_0) ++1 flow(source4_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(source1_electricityBus_0_1) ++1 flow(source2_electricityBus_0_1) ++1 flow(source3_electricityBus_0_1) ++1 flow(source4_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(source1_electricityBus_1_2) ++1 flow(source2_electricityBus_1_2) ++1 flow(source3_electricityBus_1_2) ++1 flow(source4_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(source1_electricityBus_1_3) ++1 flow(source2_electricityBus_1_3) ++1 flow(source3_electricityBus_1_3) ++1 flow(source4_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(source1_electricityBus_2_4) ++1 flow(source2_electricityBus_2_4) ++1 flow(source3_electricityBus_2_4) ++1 flow(source4_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(source1_electricityBus_2_5) ++1 flow(source2_electricityBus_2_5) ++1 flow(source3_electricityBus_2_5) ++1 flow(source4_electricityBus_2_5) += 0 + +c_l_NonConvexFlowBlock_min(source1_electricityBus_0_0)_: ++1 flow(source1_electricityBus_0_0) +>= 0 + +c_l_NonConvexFlowBlock_min(source1_electricityBus_0_1)_: ++1 flow(source1_electricityBus_0_1) +>= 0 + +c_l_NonConvexFlowBlock_min(source1_electricityBus_1_2)_: ++1 flow(source1_electricityBus_1_2) +>= 0 + +c_l_NonConvexFlowBlock_min(source1_electricityBus_1_3)_: ++1 flow(source1_electricityBus_1_3) +>= 0 + +c_l_NonConvexFlowBlock_min(source1_electricityBus_2_4)_: ++1 flow(source1_electricityBus_2_4) +>= 0 + +c_l_NonConvexFlowBlock_min(source1_electricityBus_2_5)_: ++1 flow(source1_electricityBus_2_5) +>= 0 + +c_l_NonConvexFlowBlock_min(source2_electricityBus_0_0)_: ++1 flow(source2_electricityBus_0_0) +>= 0 + +c_l_NonConvexFlowBlock_min(source2_electricityBus_0_1)_: ++1 flow(source2_electricityBus_0_1) +>= 0 + +c_l_NonConvexFlowBlock_min(source2_electricityBus_1_2)_: ++1 flow(source2_electricityBus_1_2) +>= 0 + +c_l_NonConvexFlowBlock_min(source2_electricityBus_1_3)_: ++1 flow(source2_electricityBus_1_3) +>= 0 + +c_l_NonConvexFlowBlock_min(source2_electricityBus_2_4)_: ++1 flow(source2_electricityBus_2_4) +>= 0 + +c_l_NonConvexFlowBlock_min(source2_electricityBus_2_5)_: ++1 flow(source2_electricityBus_2_5) +>= 0 + +c_l_NonConvexFlowBlock_min(source3_electricityBus_0_0)_: ++1 flow(source3_electricityBus_0_0) +>= 0 + +c_l_NonConvexFlowBlock_min(source3_electricityBus_0_1)_: ++1 flow(source3_electricityBus_0_1) +>= 0 + +c_l_NonConvexFlowBlock_min(source3_electricityBus_1_2)_: ++1 flow(source3_electricityBus_1_2) +>= 0 + +c_l_NonConvexFlowBlock_min(source3_electricityBus_1_3)_: ++1 flow(source3_electricityBus_1_3) +>= 0 + +c_l_NonConvexFlowBlock_min(source3_electricityBus_2_4)_: ++1 flow(source3_electricityBus_2_4) +>= 0 + +c_l_NonConvexFlowBlock_min(source3_electricityBus_2_5)_: ++1 flow(source3_electricityBus_2_5) +>= 0 + +c_u_NonConvexFlowBlock_max(source1_electricityBus_0_0)_: +-100 NonConvexFlowBlock_status(source1_electricityBus_0) ++1 flow(source1_electricityBus_0_0) +<= 0 + +c_u_NonConvexFlowBlock_max(source1_electricityBus_0_1)_: +-100 NonConvexFlowBlock_status(source1_electricityBus_1) ++1 flow(source1_electricityBus_0_1) +<= 0 + +c_u_NonConvexFlowBlock_max(source1_electricityBus_1_2)_: +-100 NonConvexFlowBlock_status(source1_electricityBus_2) ++1 flow(source1_electricityBus_1_2) +<= 0 + +c_u_NonConvexFlowBlock_max(source1_electricityBus_1_3)_: +-100 NonConvexFlowBlock_status(source1_electricityBus_3) ++1 flow(source1_electricityBus_1_3) +<= 0 + +c_u_NonConvexFlowBlock_max(source1_electricityBus_2_4)_: +-100 NonConvexFlowBlock_status(source1_electricityBus_4) ++1 flow(source1_electricityBus_2_4) +<= 0 + +c_u_NonConvexFlowBlock_max(source1_electricityBus_2_5)_: +-100 NonConvexFlowBlock_status(source1_electricityBus_5) ++1 flow(source1_electricityBus_2_5) +<= 0 + +c_u_NonConvexFlowBlock_max(source2_electricityBus_0_0)_: +-100 NonConvexFlowBlock_status(source2_electricityBus_0) ++1 flow(source2_electricityBus_0_0) +<= 0 + +c_u_NonConvexFlowBlock_max(source2_electricityBus_0_1)_: +-100 NonConvexFlowBlock_status(source2_electricityBus_1) ++1 flow(source2_electricityBus_0_1) +<= 0 + +c_u_NonConvexFlowBlock_max(source2_electricityBus_1_2)_: +-100 NonConvexFlowBlock_status(source2_electricityBus_2) ++1 flow(source2_electricityBus_1_2) +<= 0 + +c_u_NonConvexFlowBlock_max(source2_electricityBus_1_3)_: +-100 NonConvexFlowBlock_status(source2_electricityBus_3) ++1 flow(source2_electricityBus_1_3) +<= 0 + +c_u_NonConvexFlowBlock_max(source2_electricityBus_2_4)_: +-100 NonConvexFlowBlock_status(source2_electricityBus_4) ++1 flow(source2_electricityBus_2_4) +<= 0 + +c_u_NonConvexFlowBlock_max(source2_electricityBus_2_5)_: +-100 NonConvexFlowBlock_status(source2_electricityBus_5) ++1 flow(source2_electricityBus_2_5) +<= 0 + +c_u_NonConvexFlowBlock_max(source3_electricityBus_0_0)_: +-100 NonConvexFlowBlock_status(source3_electricityBus_0) ++1 flow(source3_electricityBus_0_0) +<= 0 + +c_u_NonConvexFlowBlock_max(source3_electricityBus_0_1)_: +-100 NonConvexFlowBlock_status(source3_electricityBus_1) ++1 flow(source3_electricityBus_0_1) +<= 0 + +c_u_NonConvexFlowBlock_max(source3_electricityBus_1_2)_: +-100 NonConvexFlowBlock_status(source3_electricityBus_2) ++1 flow(source3_electricityBus_1_2) +<= 0 + +c_u_NonConvexFlowBlock_max(source3_electricityBus_1_3)_: +-100 NonConvexFlowBlock_status(source3_electricityBus_3) ++1 flow(source3_electricityBus_1_3) +<= 0 + +c_u_NonConvexFlowBlock_max(source3_electricityBus_2_4)_: +-100 NonConvexFlowBlock_status(source3_electricityBus_4) ++1 flow(source3_electricityBus_2_4) +<= 0 + +c_u_NonConvexFlowBlock_max(source3_electricityBus_2_5)_: +-100 NonConvexFlowBlock_status(source3_electricityBus_5) ++1 flow(source3_electricityBus_2_5) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(source1_electricityBus_0_0) <= 100 + 0 <= flow(source1_electricityBus_0_1) <= 100 + 0 <= flow(source1_electricityBus_1_2) <= 100 + 0 <= flow(source1_electricityBus_1_3) <= 100 + 0 <= flow(source1_electricityBus_2_4) <= 100 + 0 <= flow(source1_electricityBus_2_5) <= 100 + 0 <= flow(source2_electricityBus_0_0) <= 100 + 0 <= flow(source2_electricityBus_0_1) <= 100 + 0 <= flow(source2_electricityBus_1_2) <= 100 + 0 <= flow(source2_electricityBus_1_3) <= 100 + 0 <= flow(source2_electricityBus_2_4) <= 100 + 0 <= flow(source2_electricityBus_2_5) <= 100 + 0 <= flow(source3_electricityBus_0_0) <= 100 + 0 <= flow(source3_electricityBus_0_1) <= 100 + 0 <= flow(source3_electricityBus_1_2) <= 100 + 0 <= flow(source3_electricityBus_1_3) <= 100 + 0 <= flow(source3_electricityBus_2_4) <= 100 + 0 <= flow(source3_electricityBus_2_5) <= 100 + 30 <= flow(source4_electricityBus_0_0) <= 100 + 30 <= flow(source4_electricityBus_0_1) <= 100 + 30 <= flow(source4_electricityBus_1_2) <= 100 + 30 <= flow(source4_electricityBus_1_3) <= 100 + 30 <= flow(source4_electricityBus_2_4) <= 100 + 30 <= flow(source4_electricityBus_2_5) <= 100 + 1 <= emission_factor(0) <= 2 + 1 <= emission_factor(1) <= 2 + 1 <= emission_factor(2) <= 2 + 1 <= emission_factor(3) <= 2 + 1 <= emission_factor(4) <= 2 + 1 <= emission_factor(5) <= 2 + 0 <= NonConvexFlowBlock_status(source1_electricityBus_0) <= 1 + 0 <= NonConvexFlowBlock_status(source1_electricityBus_1) <= 1 + 0 <= NonConvexFlowBlock_status(source1_electricityBus_2) <= 1 + 0 <= NonConvexFlowBlock_status(source1_electricityBus_3) <= 1 + 0 <= NonConvexFlowBlock_status(source1_electricityBus_4) <= 1 + 0 <= NonConvexFlowBlock_status(source1_electricityBus_5) <= 1 + 0 <= NonConvexFlowBlock_status(source2_electricityBus_0) <= 1 + 0 <= NonConvexFlowBlock_status(source2_electricityBus_1) <= 1 + 0 <= NonConvexFlowBlock_status(source2_electricityBus_2) <= 1 + 0 <= NonConvexFlowBlock_status(source2_electricityBus_3) <= 1 + 0 <= NonConvexFlowBlock_status(source2_electricityBus_4) <= 1 + 0 <= NonConvexFlowBlock_status(source2_electricityBus_5) <= 1 + 0 <= NonConvexFlowBlock_status(source3_electricityBus_0) <= 1 + 0 <= NonConvexFlowBlock_status(source3_electricityBus_1) <= 1 + 0 <= NonConvexFlowBlock_status(source3_electricityBus_2) <= 1 + 0 <= NonConvexFlowBlock_status(source3_electricityBus_3) <= 1 + 0 <= NonConvexFlowBlock_status(source3_electricityBus_4) <= 1 + 0 <= NonConvexFlowBlock_status(source3_electricityBus_5) <= 1 +binary + NonConvexFlowBlock_status(source1_electricityBus_0) + NonConvexFlowBlock_status(source1_electricityBus_1) + NonConvexFlowBlock_status(source1_electricityBus_2) + NonConvexFlowBlock_status(source1_electricityBus_3) + NonConvexFlowBlock_status(source1_electricityBus_4) + NonConvexFlowBlock_status(source1_electricityBus_5) + NonConvexFlowBlock_status(source2_electricityBus_0) + NonConvexFlowBlock_status(source2_electricityBus_1) + NonConvexFlowBlock_status(source2_electricityBus_2) + NonConvexFlowBlock_status(source2_electricityBus_3) + NonConvexFlowBlock_status(source2_electricityBus_4) + NonConvexFlowBlock_status(source2_electricityBus_5) + NonConvexFlowBlock_status(source3_electricityBus_0) + NonConvexFlowBlock_status(source3_electricityBus_1) + NonConvexFlowBlock_status(source3_electricityBus_2) + NonConvexFlowBlock_status(source3_electricityBus_3) + NonConvexFlowBlock_status(source3_electricityBus_4) + NonConvexFlowBlock_status(source3_electricityBus_5) +end diff --git a/tests/lp_files/inactivity_costs_multi_period.lp b/tests/lp_files/inactivity_costs_multi_period.lp new file mode 100644 index 000000000..f96639b0f --- /dev/null +++ b/tests/lp_files/inactivity_costs_multi_period.lp @@ -0,0 +1,128 @@ +\* Source Pyomo model name=Model *\ + +min +objective: +-2 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_0) +-2 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_1) +-1.9607843137254901 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_2) +-1.9607843137254901 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_3) +-1.9223375624759707 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_4) +-1.9223375624759707 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_5) ++10 flow(cheap_plant_inactivity_costs_Bus_C_0_0) ++10 flow(cheap_plant_inactivity_costs_Bus_C_0_1) ++9.8039215686274499 flow(cheap_plant_inactivity_costs_Bus_C_1_2) ++9.8039215686274499 flow(cheap_plant_inactivity_costs_Bus_C_1_3) ++9.6116878123798539 flow(cheap_plant_inactivity_costs_Bus_C_2_4) ++9.6116878123798539 flow(cheap_plant_inactivity_costs_Bus_C_2_5) ++11.766243752402922 ONE_VAR_CONSTANT + +s.t. + +c_e_BusBlock_balance(Bus_C_0_0)_: ++1 flow(cheap_plant_inactivity_costs_Bus_C_0_0) += 0 + +c_e_BusBlock_balance(Bus_C_0_1)_: ++1 flow(cheap_plant_inactivity_costs_Bus_C_0_1) += 0 + +c_e_BusBlock_balance(Bus_C_1_2)_: ++1 flow(cheap_plant_inactivity_costs_Bus_C_1_2) += 0 + +c_e_BusBlock_balance(Bus_C_1_3)_: ++1 flow(cheap_plant_inactivity_costs_Bus_C_1_3) += 0 + +c_e_BusBlock_balance(Bus_C_2_4)_: ++1 flow(cheap_plant_inactivity_costs_Bus_C_2_4) += 0 + +c_e_BusBlock_balance(Bus_C_2_5)_: ++1 flow(cheap_plant_inactivity_costs_Bus_C_2_5) += 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_inactivity_costs_Bus_C_0_0)_: ++5 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_0) +-1 flow(cheap_plant_inactivity_costs_Bus_C_0_0) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_inactivity_costs_Bus_C_0_1)_: ++5 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_1) +-1 flow(cheap_plant_inactivity_costs_Bus_C_0_1) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_inactivity_costs_Bus_C_1_2)_: ++5 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_2) +-1 flow(cheap_plant_inactivity_costs_Bus_C_1_2) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_inactivity_costs_Bus_C_1_3)_: ++5 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_3) +-1 flow(cheap_plant_inactivity_costs_Bus_C_1_3) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_inactivity_costs_Bus_C_2_4)_: ++5 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_4) +-1 flow(cheap_plant_inactivity_costs_Bus_C_2_4) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_inactivity_costs_Bus_C_2_5)_: ++5 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_5) +-1 flow(cheap_plant_inactivity_costs_Bus_C_2_5) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_inactivity_costs_Bus_C_0_0)_: +-10 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_0) ++1 flow(cheap_plant_inactivity_costs_Bus_C_0_0) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_inactivity_costs_Bus_C_0_1)_: +-10 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_1) ++1 flow(cheap_plant_inactivity_costs_Bus_C_0_1) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_inactivity_costs_Bus_C_1_2)_: +-10 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_2) ++1 flow(cheap_plant_inactivity_costs_Bus_C_1_2) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_inactivity_costs_Bus_C_1_3)_: +-10 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_3) ++1 flow(cheap_plant_inactivity_costs_Bus_C_1_3) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_inactivity_costs_Bus_C_2_4)_: +-10 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_4) ++1 flow(cheap_plant_inactivity_costs_Bus_C_2_4) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_inactivity_costs_Bus_C_2_5)_: +-10 NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_5) ++1 flow(cheap_plant_inactivity_costs_Bus_C_2_5) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(cheap_plant_inactivity_costs_Bus_C_0_0) <= 10 + 0 <= flow(cheap_plant_inactivity_costs_Bus_C_0_1) <= 10 + 0 <= flow(cheap_plant_inactivity_costs_Bus_C_1_2) <= 10 + 0 <= flow(cheap_plant_inactivity_costs_Bus_C_1_3) <= 10 + 0 <= flow(cheap_plant_inactivity_costs_Bus_C_2_4) <= 10 + 0 <= flow(cheap_plant_inactivity_costs_Bus_C_2_5) <= 10 + 0 <= NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_0) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_1) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_2) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_3) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_4) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_5) <= 1 +binary + NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_0) + NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_1) + NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_2) + NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_3) + NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_4) + NonConvexFlowBlock_status(cheap_plant_inactivity_costs_Bus_C_5) +end diff --git a/tests/lp_files/linear_transformer_chp_invest_multi_period.lp b/tests/lp_files/linear_transformer_chp_invest_multi_period.lp new file mode 100644 index 000000000..4ad96b273 --- /dev/null +++ b/tests/lp_files/linear_transformer_chp_invest_multi_period.lp @@ -0,0 +1,277 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++31.823209703696548 InvestmentFlowBlock_invest(gasBus_chp_powerplant_gas_0) ++31.199225199702497 InvestmentFlowBlock_invest(gasBus_chp_powerplant_gas_1) ++30.587475685982838 InvestmentFlowBlock_invest(gasBus_chp_powerplant_gas_2) ++50 flow(gasBus_chp_powerplant_gas_0_0) ++50 flow(gasBus_chp_powerplant_gas_0_1) ++49.019607843137251 flow(gasBus_chp_powerplant_gas_1_2) ++49.019607843137251 flow(gasBus_chp_powerplant_gas_1_3) ++48.058439061899264 flow(gasBus_chp_powerplant_gas_2_4) ++48.058439061899264 flow(gasBus_chp_powerplant_gas_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(chp_powerplant_gas_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(chp_powerplant_gas_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(chp_powerplant_gas_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(chp_powerplant_gas_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(chp_powerplant_gas_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(chp_powerplant_gas_electricityBus_2_5) += 0 + +c_e_BusBlock_balance(gasBus_0_0)_: ++1 flow(gasBus_chp_powerplant_gas_0_0) += 0 + +c_e_BusBlock_balance(gasBus_0_1)_: ++1 flow(gasBus_chp_powerplant_gas_0_1) += 0 + +c_e_BusBlock_balance(gasBus_1_2)_: ++1 flow(gasBus_chp_powerplant_gas_1_2) += 0 + +c_e_BusBlock_balance(gasBus_1_3)_: ++1 flow(gasBus_chp_powerplant_gas_1_3) += 0 + +c_e_BusBlock_balance(gasBus_2_4)_: ++1 flow(gasBus_chp_powerplant_gas_2_4) += 0 + +c_e_BusBlock_balance(gasBus_2_5)_: ++1 flow(gasBus_chp_powerplant_gas_2_5) += 0 + +c_e_BusBlock_balance(heatBus_0_0)_: ++1 flow(chp_powerplant_gas_heatBus_0_0) += 0 + +c_e_BusBlock_balance(heatBus_0_1)_: ++1 flow(chp_powerplant_gas_heatBus_0_1) += 0 + +c_e_BusBlock_balance(heatBus_1_2)_: ++1 flow(chp_powerplant_gas_heatBus_1_2) += 0 + +c_e_BusBlock_balance(heatBus_1_3)_: ++1 flow(chp_powerplant_gas_heatBus_1_3) += 0 + +c_e_BusBlock_balance(heatBus_2_4)_: ++1 flow(chp_powerplant_gas_heatBus_2_4) += 0 + +c_e_BusBlock_balance(heatBus_2_5)_: ++1 flow(chp_powerplant_gas_heatBus_2_5) += 0 + +c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_electricityBus_0_0)_: +-1 flow(chp_powerplant_gas_electricityBus_0_0) ++0.40000000000000002 flow(gasBus_chp_powerplant_gas_0_0) += 0 + +c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_electricityBus_0_1)_: +-1 flow(chp_powerplant_gas_electricityBus_0_1) ++0.40000000000000002 flow(gasBus_chp_powerplant_gas_0_1) += 0 + +c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_electricityBus_1_2)_: +-1 flow(chp_powerplant_gas_electricityBus_1_2) ++0.40000000000000002 flow(gasBus_chp_powerplant_gas_1_2) += 0 + +c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_electricityBus_1_3)_: +-1 flow(chp_powerplant_gas_electricityBus_1_3) ++0.40000000000000002 flow(gasBus_chp_powerplant_gas_1_3) += 0 + +c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_electricityBus_2_4)_: +-1 flow(chp_powerplant_gas_electricityBus_2_4) ++0.40000000000000002 flow(gasBus_chp_powerplant_gas_2_4) += 0 + +c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_electricityBus_2_5)_: +-1 flow(chp_powerplant_gas_electricityBus_2_5) ++0.40000000000000002 flow(gasBus_chp_powerplant_gas_2_5) += 0 + +c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_heatBus_0_0)_: +-1 flow(chp_powerplant_gas_heatBus_0_0) ++0.5 flow(gasBus_chp_powerplant_gas_0_0) += 0 + +c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_heatBus_0_1)_: +-1 flow(chp_powerplant_gas_heatBus_0_1) ++0.5 flow(gasBus_chp_powerplant_gas_0_1) += 0 + +c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_heatBus_1_2)_: +-1 flow(chp_powerplant_gas_heatBus_1_2) ++0.5 flow(gasBus_chp_powerplant_gas_1_2) += 0 + +c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_heatBus_1_3)_: +-1 flow(chp_powerplant_gas_heatBus_1_3) ++0.5 flow(gasBus_chp_powerplant_gas_1_3) += 0 + +c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_heatBus_2_4)_: +-1 flow(chp_powerplant_gas_heatBus_2_4) ++0.5 flow(gasBus_chp_powerplant_gas_2_4) += 0 + +c_e_TransformerBlock_relation(chp_powerplant_gas_gasBus_heatBus_2_5)_: +-1 flow(chp_powerplant_gas_heatBus_2_5) ++0.5 flow(gasBus_chp_powerplant_gas_2_5) += 0 + +c_e_InvestmentFlowBlock_total_rule(gasBus_chp_powerplant_gas_0)_: +-1 InvestmentFlowBlock_invest(gasBus_chp_powerplant_gas_0) ++1 InvestmentFlowBlock_total(gasBus_chp_powerplant_gas_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(gasBus_chp_powerplant_gas_1)_: +-1 InvestmentFlowBlock_invest(gasBus_chp_powerplant_gas_1) ++1 InvestmentFlowBlock_old(gasBus_chp_powerplant_gas_1) +-1 InvestmentFlowBlock_total(gasBus_chp_powerplant_gas_0) ++1 InvestmentFlowBlock_total(gasBus_chp_powerplant_gas_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(gasBus_chp_powerplant_gas_2)_: +-1 InvestmentFlowBlock_invest(gasBus_chp_powerplant_gas_2) ++1 InvestmentFlowBlock_old(gasBus_chp_powerplant_gas_2) +-1 InvestmentFlowBlock_total(gasBus_chp_powerplant_gas_1) ++1 InvestmentFlowBlock_total(gasBus_chp_powerplant_gas_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(gasBus_chp_powerplant_gas_0)_: ++1 InvestmentFlowBlock_old_end(gasBus_chp_powerplant_gas_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(gasBus_chp_powerplant_gas_1)_: ++1 InvestmentFlowBlock_old_end(gasBus_chp_powerplant_gas_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(gasBus_chp_powerplant_gas_2)_: ++1 InvestmentFlowBlock_old_end(gasBus_chp_powerplant_gas_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(gasBus_chp_powerplant_gas_0)_: ++1 InvestmentFlowBlock_old_exo(gasBus_chp_powerplant_gas_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(gasBus_chp_powerplant_gas_1)_: ++1 InvestmentFlowBlock_old_exo(gasBus_chp_powerplant_gas_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(gasBus_chp_powerplant_gas_2)_: ++1 InvestmentFlowBlock_old_exo(gasBus_chp_powerplant_gas_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(gasBus_chp_powerplant_gas_0)_: ++1 InvestmentFlowBlock_old(gasBus_chp_powerplant_gas_0) +-1 InvestmentFlowBlock_old_end(gasBus_chp_powerplant_gas_0) +-1 InvestmentFlowBlock_old_exo(gasBus_chp_powerplant_gas_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(gasBus_chp_powerplant_gas_1)_: ++1 InvestmentFlowBlock_old(gasBus_chp_powerplant_gas_1) +-1 InvestmentFlowBlock_old_end(gasBus_chp_powerplant_gas_1) +-1 InvestmentFlowBlock_old_exo(gasBus_chp_powerplant_gas_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(gasBus_chp_powerplant_gas_2)_: ++1 InvestmentFlowBlock_old(gasBus_chp_powerplant_gas_2) +-1 InvestmentFlowBlock_old_end(gasBus_chp_powerplant_gas_2) +-1 InvestmentFlowBlock_old_exo(gasBus_chp_powerplant_gas_2) += 0 + +c_u_InvestmentFlowBlock_max(gasBus_chp_powerplant_gas_0_0)_: +-1 InvestmentFlowBlock_total(gasBus_chp_powerplant_gas_0) ++1 flow(gasBus_chp_powerplant_gas_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(gasBus_chp_powerplant_gas_0_1)_: +-1 InvestmentFlowBlock_total(gasBus_chp_powerplant_gas_0) ++1 flow(gasBus_chp_powerplant_gas_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(gasBus_chp_powerplant_gas_1_2)_: +-1 InvestmentFlowBlock_total(gasBus_chp_powerplant_gas_1) ++1 flow(gasBus_chp_powerplant_gas_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(gasBus_chp_powerplant_gas_1_3)_: +-1 InvestmentFlowBlock_total(gasBus_chp_powerplant_gas_1) ++1 flow(gasBus_chp_powerplant_gas_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(gasBus_chp_powerplant_gas_2_4)_: +-1 InvestmentFlowBlock_total(gasBus_chp_powerplant_gas_2) ++1 flow(gasBus_chp_powerplant_gas_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(gasBus_chp_powerplant_gas_2_5)_: +-1 InvestmentFlowBlock_total(gasBus_chp_powerplant_gas_2) ++1 flow(gasBus_chp_powerplant_gas_2_5) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(chp_powerplant_gas_electricityBus_0_0) <= +inf + 0 <= flow(chp_powerplant_gas_electricityBus_0_1) <= +inf + 0 <= flow(chp_powerplant_gas_electricityBus_1_2) <= +inf + 0 <= flow(chp_powerplant_gas_electricityBus_1_3) <= +inf + 0 <= flow(chp_powerplant_gas_electricityBus_2_4) <= +inf + 0 <= flow(chp_powerplant_gas_electricityBus_2_5) <= +inf + 0 <= flow(chp_powerplant_gas_heatBus_0_0) <= +inf + 0 <= flow(chp_powerplant_gas_heatBus_0_1) <= +inf + 0 <= flow(chp_powerplant_gas_heatBus_1_2) <= +inf + 0 <= flow(chp_powerplant_gas_heatBus_1_3) <= +inf + 0 <= flow(chp_powerplant_gas_heatBus_2_4) <= +inf + 0 <= flow(chp_powerplant_gas_heatBus_2_5) <= +inf + 0 <= flow(gasBus_chp_powerplant_gas_0_0) <= +inf + 0 <= flow(gasBus_chp_powerplant_gas_0_1) <= +inf + 0 <= flow(gasBus_chp_powerplant_gas_1_2) <= +inf + 0 <= flow(gasBus_chp_powerplant_gas_1_3) <= +inf + 0 <= flow(gasBus_chp_powerplant_gas_2_4) <= +inf + 0 <= flow(gasBus_chp_powerplant_gas_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(gasBus_chp_powerplant_gas_0) <= 1000 + 0 <= InvestmentFlowBlock_invest(gasBus_chp_powerplant_gas_1) <= 1000 + 0 <= InvestmentFlowBlock_invest(gasBus_chp_powerplant_gas_2) <= 1000 + 0 <= InvestmentFlowBlock_total(gasBus_chp_powerplant_gas_0) <= +inf + 0 <= InvestmentFlowBlock_total(gasBus_chp_powerplant_gas_1) <= +inf + 0 <= InvestmentFlowBlock_total(gasBus_chp_powerplant_gas_2) <= +inf + 0 <= InvestmentFlowBlock_old(gasBus_chp_powerplant_gas_0) <= +inf + 0 <= InvestmentFlowBlock_old(gasBus_chp_powerplant_gas_1) <= +inf + 0 <= InvestmentFlowBlock_old(gasBus_chp_powerplant_gas_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(gasBus_chp_powerplant_gas_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(gasBus_chp_powerplant_gas_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(gasBus_chp_powerplant_gas_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(gasBus_chp_powerplant_gas_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(gasBus_chp_powerplant_gas_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(gasBus_chp_powerplant_gas_2) <= +inf +end diff --git a/tests/lp_files/linear_transformer_chp_multi_period.lp b/tests/lp_files/linear_transformer_chp_multi_period.lp new file mode 100644 index 000000000..e91c85b0a --- /dev/null +++ b/tests/lp_files/linear_transformer_chp_multi_period.lp @@ -0,0 +1,168 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++50 flow(gasBus_CHPpowerplantGas_0_0) ++50 flow(gasBus_CHPpowerplantGas_0_1) ++49.019607843137251 flow(gasBus_CHPpowerplantGas_1_2) ++49.019607843137251 flow(gasBus_CHPpowerplantGas_1_3) ++48.058439061899264 flow(gasBus_CHPpowerplantGas_2_4) ++48.058439061899264 flow(gasBus_CHPpowerplantGas_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(CHPpowerplantGas_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(CHPpowerplantGas_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(CHPpowerplantGas_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(CHPpowerplantGas_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(CHPpowerplantGas_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(CHPpowerplantGas_electricityBus_2_5) += 0 + +c_e_BusBlock_balance(gasBus_0_0)_: ++1 flow(gasBus_CHPpowerplantGas_0_0) += 0 + +c_e_BusBlock_balance(gasBus_0_1)_: ++1 flow(gasBus_CHPpowerplantGas_0_1) += 0 + +c_e_BusBlock_balance(gasBus_1_2)_: ++1 flow(gasBus_CHPpowerplantGas_1_2) += 0 + +c_e_BusBlock_balance(gasBus_1_3)_: ++1 flow(gasBus_CHPpowerplantGas_1_3) += 0 + +c_e_BusBlock_balance(gasBus_2_4)_: ++1 flow(gasBus_CHPpowerplantGas_2_4) += 0 + +c_e_BusBlock_balance(gasBus_2_5)_: ++1 flow(gasBus_CHPpowerplantGas_2_5) += 0 + +c_e_BusBlock_balance(heatBus_0_0)_: ++1 flow(CHPpowerplantGas_heatBus_0_0) += 0 + +c_e_BusBlock_balance(heatBus_0_1)_: ++1 flow(CHPpowerplantGas_heatBus_0_1) += 0 + +c_e_BusBlock_balance(heatBus_1_2)_: ++1 flow(CHPpowerplantGas_heatBus_1_2) += 0 + +c_e_BusBlock_balance(heatBus_1_3)_: ++1 flow(CHPpowerplantGas_heatBus_1_3) += 0 + +c_e_BusBlock_balance(heatBus_2_4)_: ++1 flow(CHPpowerplantGas_heatBus_2_4) += 0 + +c_e_BusBlock_balance(heatBus_2_5)_: ++1 flow(CHPpowerplantGas_heatBus_2_5) += 0 + +c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_electricityBus_0_0)_: +-1 flow(CHPpowerplantGas_electricityBus_0_0) ++0.40000000000000002 flow(gasBus_CHPpowerplantGas_0_0) += 0 + +c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_electricityBus_0_1)_: +-1 flow(CHPpowerplantGas_electricityBus_0_1) ++0.40000000000000002 flow(gasBus_CHPpowerplantGas_0_1) += 0 + +c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_electricityBus_1_2)_: +-1 flow(CHPpowerplantGas_electricityBus_1_2) ++0.40000000000000002 flow(gasBus_CHPpowerplantGas_1_2) += 0 + +c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_electricityBus_1_3)_: +-1 flow(CHPpowerplantGas_electricityBus_1_3) ++0.40000000000000002 flow(gasBus_CHPpowerplantGas_1_3) += 0 + +c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_electricityBus_2_4)_: +-1 flow(CHPpowerplantGas_electricityBus_2_4) ++0.40000000000000002 flow(gasBus_CHPpowerplantGas_2_4) += 0 + +c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_electricityBus_2_5)_: +-1 flow(CHPpowerplantGas_electricityBus_2_5) ++0.40000000000000002 flow(gasBus_CHPpowerplantGas_2_5) += 0 + +c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_heatBus_0_0)_: +-1 flow(CHPpowerplantGas_heatBus_0_0) ++0.5 flow(gasBus_CHPpowerplantGas_0_0) += 0 + +c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_heatBus_0_1)_: +-1 flow(CHPpowerplantGas_heatBus_0_1) ++0.5 flow(gasBus_CHPpowerplantGas_0_1) += 0 + +c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_heatBus_1_2)_: +-1 flow(CHPpowerplantGas_heatBus_1_2) ++0.5 flow(gasBus_CHPpowerplantGas_1_2) += 0 + +c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_heatBus_1_3)_: +-1 flow(CHPpowerplantGas_heatBus_1_3) ++0.5 flow(gasBus_CHPpowerplantGas_1_3) += 0 + +c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_heatBus_2_4)_: +-1 flow(CHPpowerplantGas_heatBus_2_4) ++0.5 flow(gasBus_CHPpowerplantGas_2_4) += 0 + +c_e_TransformerBlock_relation(CHPpowerplantGas_gasBus_heatBus_2_5)_: +-1 flow(CHPpowerplantGas_heatBus_2_5) ++0.5 flow(gasBus_CHPpowerplantGas_2_5) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(CHPpowerplantGas_electricityBus_0_0) <= +inf + 0 <= flow(CHPpowerplantGas_electricityBus_0_1) <= +inf + 0 <= flow(CHPpowerplantGas_electricityBus_1_2) <= +inf + 0 <= flow(CHPpowerplantGas_electricityBus_1_3) <= +inf + 0 <= flow(CHPpowerplantGas_electricityBus_2_4) <= +inf + 0 <= flow(CHPpowerplantGas_electricityBus_2_5) <= +inf + 0 <= flow(CHPpowerplantGas_heatBus_0_0) <= +inf + 0 <= flow(CHPpowerplantGas_heatBus_0_1) <= +inf + 0 <= flow(CHPpowerplantGas_heatBus_1_2) <= +inf + 0 <= flow(CHPpowerplantGas_heatBus_1_3) <= +inf + 0 <= flow(CHPpowerplantGas_heatBus_2_4) <= +inf + 0 <= flow(CHPpowerplantGas_heatBus_2_5) <= +inf + 0 <= flow(gasBus_CHPpowerplantGas_0_0) <= 100000000000 + 0 <= flow(gasBus_CHPpowerplantGas_0_1) <= 100000000000 + 0 <= flow(gasBus_CHPpowerplantGas_1_2) <= 100000000000 + 0 <= flow(gasBus_CHPpowerplantGas_1_3) <= 100000000000 + 0 <= flow(gasBus_CHPpowerplantGas_2_4) <= 100000000000 + 0 <= flow(gasBus_CHPpowerplantGas_2_5) <= 100000000000 +end diff --git a/tests/lp_files/maximum_shutdowns_multi_period.lp b/tests/lp_files/maximum_shutdowns_multi_period.lp new file mode 100644 index 000000000..adae5f75c --- /dev/null +++ b/tests/lp_files/maximum_shutdowns_multi_period.lp @@ -0,0 +1,177 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++10 flow(cheap_plant_maximum_shutdowns_Bus_C_0_0) ++10 flow(cheap_plant_maximum_shutdowns_Bus_C_0_1) ++9.8039215686274499 flow(cheap_plant_maximum_shutdowns_Bus_C_1_2) ++9.8039215686274499 flow(cheap_plant_maximum_shutdowns_Bus_C_1_3) ++9.6116878123798539 flow(cheap_plant_maximum_shutdowns_Bus_C_2_4) ++9.6116878123798539 flow(cheap_plant_maximum_shutdowns_Bus_C_2_5) + +s.t. + +c_e_BusBlock_balance(Bus_C_0_0)_: ++1 flow(cheap_plant_maximum_shutdowns_Bus_C_0_0) += 0 + +c_e_BusBlock_balance(Bus_C_0_1)_: ++1 flow(cheap_plant_maximum_shutdowns_Bus_C_0_1) += 0 + +c_e_BusBlock_balance(Bus_C_1_2)_: ++1 flow(cheap_plant_maximum_shutdowns_Bus_C_1_2) += 0 + +c_e_BusBlock_balance(Bus_C_1_3)_: ++1 flow(cheap_plant_maximum_shutdowns_Bus_C_1_3) += 0 + +c_e_BusBlock_balance(Bus_C_2_4)_: ++1 flow(cheap_plant_maximum_shutdowns_Bus_C_2_4) += 0 + +c_e_BusBlock_balance(Bus_C_2_5)_: ++1 flow(cheap_plant_maximum_shutdowns_Bus_C_2_5) += 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_maximum_shutdowns_Bus_C_0_0)_: ++5 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_0) +-1 flow(cheap_plant_maximum_shutdowns_Bus_C_0_0) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_maximum_shutdowns_Bus_C_0_1)_: ++5 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_1) +-1 flow(cheap_plant_maximum_shutdowns_Bus_C_0_1) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_maximum_shutdowns_Bus_C_1_2)_: ++5 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_2) +-1 flow(cheap_plant_maximum_shutdowns_Bus_C_1_2) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_maximum_shutdowns_Bus_C_1_3)_: ++5 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_3) +-1 flow(cheap_plant_maximum_shutdowns_Bus_C_1_3) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_maximum_shutdowns_Bus_C_2_4)_: ++5 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_4) +-1 flow(cheap_plant_maximum_shutdowns_Bus_C_2_4) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_maximum_shutdowns_Bus_C_2_5)_: ++5 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_5) +-1 flow(cheap_plant_maximum_shutdowns_Bus_C_2_5) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_maximum_shutdowns_Bus_C_0_0)_: +-10 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_0) ++1 flow(cheap_plant_maximum_shutdowns_Bus_C_0_0) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_maximum_shutdowns_Bus_C_0_1)_: +-10 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_1) ++1 flow(cheap_plant_maximum_shutdowns_Bus_C_0_1) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_maximum_shutdowns_Bus_C_1_2)_: +-10 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_2) ++1 flow(cheap_plant_maximum_shutdowns_Bus_C_1_2) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_maximum_shutdowns_Bus_C_1_3)_: +-10 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_3) ++1 flow(cheap_plant_maximum_shutdowns_Bus_C_1_3) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_maximum_shutdowns_Bus_C_2_4)_: +-10 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_4) ++1 flow(cheap_plant_maximum_shutdowns_Bus_C_2_4) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_maximum_shutdowns_Bus_C_2_5)_: +-10 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_5) ++1 flow(cheap_plant_maximum_shutdowns_Bus_C_2_5) +<= 0 + +c_u_NonConvexFlowBlock_shutdown_constr(cheap_plant_maximum_shutdowns_Bus_C_0)_: +-1 NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_0) +-1 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_0) +<= 0 + +c_u_NonConvexFlowBlock_shutdown_constr(cheap_plant_maximum_shutdowns_Bus_C_1)_: +-1 NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_1) ++1 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_0) +-1 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_1) +<= 0 + +c_u_NonConvexFlowBlock_shutdown_constr(cheap_plant_maximum_shutdowns_Bus_C_2)_: +-1 NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_2) ++1 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_1) +-1 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_2) +<= 0 + +c_u_NonConvexFlowBlock_shutdown_constr(cheap_plant_maximum_shutdowns_Bus_C_3)_: +-1 NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_3) ++1 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_2) +-1 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_3) +<= 0 + +c_u_NonConvexFlowBlock_shutdown_constr(cheap_plant_maximum_shutdowns_Bus_C_4)_: +-1 NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_4) ++1 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_3) +-1 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_4) +<= 0 + +c_u_NonConvexFlowBlock_shutdown_constr(cheap_plant_maximum_shutdowns_Bus_C_5)_: +-1 NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_5) ++1 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_4) +-1 NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_5) +<= 0 + +c_u_NonConvexFlowBlock_max_shutdown_constr(cheap_plant_maximum_shutdowns_Bus_C)_: ++1 NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_0) ++1 NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_1) ++1 NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_2) ++1 NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_3) ++1 NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_4) ++1 NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_5) +<= 2 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(cheap_plant_maximum_shutdowns_Bus_C_0_0) <= 10 + 0 <= flow(cheap_plant_maximum_shutdowns_Bus_C_0_1) <= 10 + 0 <= flow(cheap_plant_maximum_shutdowns_Bus_C_1_2) <= 10 + 0 <= flow(cheap_plant_maximum_shutdowns_Bus_C_1_3) <= 10 + 0 <= flow(cheap_plant_maximum_shutdowns_Bus_C_2_4) <= 10 + 0 <= flow(cheap_plant_maximum_shutdowns_Bus_C_2_5) <= 10 + 0 <= NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_0) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_1) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_2) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_3) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_4) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_5) <= 1 + 0 <= NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_0) <= 1 + 0 <= NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_1) <= 1 + 0 <= NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_2) <= 1 + 0 <= NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_3) <= 1 + 0 <= NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_4) <= 1 + 0 <= NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_5) <= 1 +binary + NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_0) + NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_1) + NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_2) + NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_3) + NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_4) + NonConvexFlowBlock_status(cheap_plant_maximum_shutdowns_Bus_C_5) + NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_0) + NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_1) + NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_2) + NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_3) + NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_4) + NonConvexFlowBlock_shutdown(cheap_plant_maximum_shutdowns_Bus_C_5) +end diff --git a/tests/lp_files/maximum_startups_multi_period.lp b/tests/lp_files/maximum_startups_multi_period.lp new file mode 100644 index 000000000..940c42c0a --- /dev/null +++ b/tests/lp_files/maximum_startups_multi_period.lp @@ -0,0 +1,177 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++10 flow(cheap_plant_maximum_startups_Bus_C_0_0) ++10 flow(cheap_plant_maximum_startups_Bus_C_0_1) ++9.8039215686274499 flow(cheap_plant_maximum_startups_Bus_C_1_2) ++9.8039215686274499 flow(cheap_plant_maximum_startups_Bus_C_1_3) ++9.6116878123798539 flow(cheap_plant_maximum_startups_Bus_C_2_4) ++9.6116878123798539 flow(cheap_plant_maximum_startups_Bus_C_2_5) + +s.t. + +c_e_BusBlock_balance(Bus_C_0_0)_: ++1 flow(cheap_plant_maximum_startups_Bus_C_0_0) += 0 + +c_e_BusBlock_balance(Bus_C_0_1)_: ++1 flow(cheap_plant_maximum_startups_Bus_C_0_1) += 0 + +c_e_BusBlock_balance(Bus_C_1_2)_: ++1 flow(cheap_plant_maximum_startups_Bus_C_1_2) += 0 + +c_e_BusBlock_balance(Bus_C_1_3)_: ++1 flow(cheap_plant_maximum_startups_Bus_C_1_3) += 0 + +c_e_BusBlock_balance(Bus_C_2_4)_: ++1 flow(cheap_plant_maximum_startups_Bus_C_2_4) += 0 + +c_e_BusBlock_balance(Bus_C_2_5)_: ++1 flow(cheap_plant_maximum_startups_Bus_C_2_5) += 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_maximum_startups_Bus_C_0_0)_: ++5 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_0) +-1 flow(cheap_plant_maximum_startups_Bus_C_0_0) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_maximum_startups_Bus_C_0_1)_: ++5 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_1) +-1 flow(cheap_plant_maximum_startups_Bus_C_0_1) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_maximum_startups_Bus_C_1_2)_: ++5 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_2) +-1 flow(cheap_plant_maximum_startups_Bus_C_1_2) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_maximum_startups_Bus_C_1_3)_: ++5 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_3) +-1 flow(cheap_plant_maximum_startups_Bus_C_1_3) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_maximum_startups_Bus_C_2_4)_: ++5 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_4) +-1 flow(cheap_plant_maximum_startups_Bus_C_2_4) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_maximum_startups_Bus_C_2_5)_: ++5 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_5) +-1 flow(cheap_plant_maximum_startups_Bus_C_2_5) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_maximum_startups_Bus_C_0_0)_: +-10 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_0) ++1 flow(cheap_plant_maximum_startups_Bus_C_0_0) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_maximum_startups_Bus_C_0_1)_: +-10 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_1) ++1 flow(cheap_plant_maximum_startups_Bus_C_0_1) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_maximum_startups_Bus_C_1_2)_: +-10 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_2) ++1 flow(cheap_plant_maximum_startups_Bus_C_1_2) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_maximum_startups_Bus_C_1_3)_: +-10 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_3) ++1 flow(cheap_plant_maximum_startups_Bus_C_1_3) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_maximum_startups_Bus_C_2_4)_: +-10 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_4) ++1 flow(cheap_plant_maximum_startups_Bus_C_2_4) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_maximum_startups_Bus_C_2_5)_: +-10 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_5) ++1 flow(cheap_plant_maximum_startups_Bus_C_2_5) +<= 0 + +c_u_NonConvexFlowBlock_startup_constr(cheap_plant_maximum_startups_Bus_C_0)_: +-1 NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_0) ++1 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_0) +<= 0 + +c_u_NonConvexFlowBlock_startup_constr(cheap_plant_maximum_startups_Bus_C_1)_: +-1 NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_1) +-1 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_0) ++1 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_1) +<= 0 + +c_u_NonConvexFlowBlock_startup_constr(cheap_plant_maximum_startups_Bus_C_2)_: +-1 NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_2) +-1 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_1) ++1 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_2) +<= 0 + +c_u_NonConvexFlowBlock_startup_constr(cheap_plant_maximum_startups_Bus_C_3)_: +-1 NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_3) +-1 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_2) ++1 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_3) +<= 0 + +c_u_NonConvexFlowBlock_startup_constr(cheap_plant_maximum_startups_Bus_C_4)_: +-1 NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_4) +-1 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_3) ++1 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_4) +<= 0 + +c_u_NonConvexFlowBlock_startup_constr(cheap_plant_maximum_startups_Bus_C_5)_: +-1 NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_5) +-1 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_4) ++1 NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_5) +<= 0 + +c_u_NonConvexFlowBlock_max_startup_constr(cheap_plant_maximum_startups_Bus_C)_: ++1 NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_0) ++1 NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_1) ++1 NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_2) ++1 NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_3) ++1 NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_4) ++1 NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_5) +<= 2 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(cheap_plant_maximum_startups_Bus_C_0_0) <= 10 + 0 <= flow(cheap_plant_maximum_startups_Bus_C_0_1) <= 10 + 0 <= flow(cheap_plant_maximum_startups_Bus_C_1_2) <= 10 + 0 <= flow(cheap_plant_maximum_startups_Bus_C_1_3) <= 10 + 0 <= flow(cheap_plant_maximum_startups_Bus_C_2_4) <= 10 + 0 <= flow(cheap_plant_maximum_startups_Bus_C_2_5) <= 10 + 0 <= NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_0) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_1) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_2) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_3) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_4) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_5) <= 1 + 0 <= NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_0) <= 1 + 0 <= NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_1) <= 1 + 0 <= NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_2) <= 1 + 0 <= NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_3) <= 1 + 0 <= NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_4) <= 1 + 0 <= NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_5) <= 1 +binary + NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_0) + NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_1) + NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_2) + NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_3) + NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_4) + NonConvexFlowBlock_status(cheap_plant_maximum_startups_Bus_C_5) + NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_0) + NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_1) + NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_2) + NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_3) + NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_4) + NonConvexFlowBlock_startup(cheap_plant_maximum_startups_Bus_C_5) +end diff --git a/tests/lp_files/min_max_runtime_multi_period.lp b/tests/lp_files/min_max_runtime_multi_period.lp new file mode 100644 index 000000000..4afc3e9e6 --- /dev/null +++ b/tests/lp_files/min_max_runtime_multi_period.lp @@ -0,0 +1,275 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++7 NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_0) ++7 NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_1) ++6.8627450980392153 NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_2) ++6.8627450980392153 NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_3) ++6.7281814686658974 NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_4) ++6.7281814686658974 NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_5) ++5 NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_0) ++5 NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_1) ++4.901960784313725 NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_2) ++4.901960784313725 NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_3) ++4.805843906189927 NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_4) ++4.805843906189927 NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_5) ++10 flow(cheap_plant_min_down_constraints_Bus_T_0_0) ++10 flow(cheap_plant_min_down_constraints_Bus_T_0_1) ++9.8039215686274499 flow(cheap_plant_min_down_constraints_Bus_T_1_2) ++9.8039215686274499 flow(cheap_plant_min_down_constraints_Bus_T_1_3) ++9.6116878123798539 flow(cheap_plant_min_down_constraints_Bus_T_2_4) ++9.6116878123798539 flow(cheap_plant_min_down_constraints_Bus_T_2_5) + +s.t. + +c_e_BusBlock_balance(Bus_T_0_0)_: ++1 flow(cheap_plant_min_down_constraints_Bus_T_0_0) += 0 + +c_e_BusBlock_balance(Bus_T_0_1)_: ++1 flow(cheap_plant_min_down_constraints_Bus_T_0_1) += 0 + +c_e_BusBlock_balance(Bus_T_1_2)_: ++1 flow(cheap_plant_min_down_constraints_Bus_T_1_2) += 0 + +c_e_BusBlock_balance(Bus_T_1_3)_: ++1 flow(cheap_plant_min_down_constraints_Bus_T_1_3) += 0 + +c_e_BusBlock_balance(Bus_T_2_4)_: ++1 flow(cheap_plant_min_down_constraints_Bus_T_2_4) += 0 + +c_e_BusBlock_balance(Bus_T_2_5)_: ++1 flow(cheap_plant_min_down_constraints_Bus_T_2_5) += 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_min_down_constraints_Bus_T_0_0)_: ++5 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_0) +-1 flow(cheap_plant_min_down_constraints_Bus_T_0_0) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_min_down_constraints_Bus_T_0_1)_: ++5 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_1) +-1 flow(cheap_plant_min_down_constraints_Bus_T_0_1) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_min_down_constraints_Bus_T_1_2)_: ++5 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_2) +-1 flow(cheap_plant_min_down_constraints_Bus_T_1_2) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_min_down_constraints_Bus_T_1_3)_: ++5 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_3) +-1 flow(cheap_plant_min_down_constraints_Bus_T_1_3) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_min_down_constraints_Bus_T_2_4)_: ++5 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_4) +-1 flow(cheap_plant_min_down_constraints_Bus_T_2_4) +<= 0 + +c_u_NonConvexFlowBlock_min(cheap_plant_min_down_constraints_Bus_T_2_5)_: ++5 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_5) +-1 flow(cheap_plant_min_down_constraints_Bus_T_2_5) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_min_down_constraints_Bus_T_0_0)_: +-10 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_0) ++1 flow(cheap_plant_min_down_constraints_Bus_T_0_0) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_min_down_constraints_Bus_T_0_1)_: +-10 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_1) ++1 flow(cheap_plant_min_down_constraints_Bus_T_0_1) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_min_down_constraints_Bus_T_1_2)_: +-10 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_2) ++1 flow(cheap_plant_min_down_constraints_Bus_T_1_2) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_min_down_constraints_Bus_T_1_3)_: +-10 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_3) ++1 flow(cheap_plant_min_down_constraints_Bus_T_1_3) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_min_down_constraints_Bus_T_2_4)_: +-10 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_4) ++1 flow(cheap_plant_min_down_constraints_Bus_T_2_4) +<= 0 + +c_u_NonConvexFlowBlock_max(cheap_plant_min_down_constraints_Bus_T_2_5)_: +-10 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_5) ++1 flow(cheap_plant_min_down_constraints_Bus_T_2_5) +<= 0 + +c_u_NonConvexFlowBlock_startup_constr(cheap_plant_min_down_constraints_Bus_T_0)_: +-1 NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_0) ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_0) +<= 1 + +c_u_NonConvexFlowBlock_startup_constr(cheap_plant_min_down_constraints_Bus_T_1)_: +-1 NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_1) +-1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_0) ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_1) +<= 0 + +c_u_NonConvexFlowBlock_startup_constr(cheap_plant_min_down_constraints_Bus_T_2)_: +-1 NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_2) +-1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_1) ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_2) +<= 0 + +c_u_NonConvexFlowBlock_startup_constr(cheap_plant_min_down_constraints_Bus_T_3)_: +-1 NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_3) +-1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_2) ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_3) +<= 0 + +c_u_NonConvexFlowBlock_startup_constr(cheap_plant_min_down_constraints_Bus_T_4)_: +-1 NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_4) +-1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_3) ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_4) +<= 0 + +c_u_NonConvexFlowBlock_startup_constr(cheap_plant_min_down_constraints_Bus_T_5)_: +-1 NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_5) +-1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_4) ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_5) +<= 0 + +c_u_NonConvexFlowBlock_shutdown_constr(cheap_plant_min_down_constraints_Bus_T_0)_: +-1 NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_0) +-1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_0) +<= -1 + +c_u_NonConvexFlowBlock_shutdown_constr(cheap_plant_min_down_constraints_Bus_T_1)_: +-1 NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_1) ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_0) +-1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_1) +<= 0 + +c_u_NonConvexFlowBlock_shutdown_constr(cheap_plant_min_down_constraints_Bus_T_2)_: +-1 NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_2) ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_1) +-1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_2) +<= 0 + +c_u_NonConvexFlowBlock_shutdown_constr(cheap_plant_min_down_constraints_Bus_T_3)_: +-1 NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_3) ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_2) +-1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_3) +<= 0 + +c_u_NonConvexFlowBlock_shutdown_constr(cheap_plant_min_down_constraints_Bus_T_4)_: +-1 NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_4) ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_3) +-1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_4) +<= 0 + +c_u_NonConvexFlowBlock_shutdown_constr(cheap_plant_min_down_constraints_Bus_T_5)_: +-1 NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_5) ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_4) +-1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_5) +<= 0 + +c_e_NonConvexFlowBlock_min_uptime_constr(cheap_plant_min_down_constraints_Bus_T_0)_: ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_0) += 1 + +c_e_NonConvexFlowBlock_min_uptime_constr(cheap_plant_min_down_constraints_Bus_T_1)_: ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_1) += 1 + +c_e_NonConvexFlowBlock_min_uptime_constr(cheap_plant_min_down_constraints_Bus_T_2)_: ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_2) += 1 + +c_e_NonConvexFlowBlock_min_uptime_constr(cheap_plant_min_down_constraints_Bus_T_3)_: ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_3) += 1 + +c_e_NonConvexFlowBlock_min_uptime_constr(cheap_plant_min_down_constraints_Bus_T_4)_: ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_4) += 1 + +c_e_NonConvexFlowBlock_min_uptime_constr(cheap_plant_min_down_constraints_Bus_T_5)_: ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_5) += 1 + +c_e_NonConvexFlowBlock_min_downtime_constr(cheap_plant_min_down_constraints_Bus_T_0)_: ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_0) += 1 + +c_e_NonConvexFlowBlock_min_downtime_constr(cheap_plant_min_down_constraints_Bus_T_1)_: ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_1) += 1 + +c_e_NonConvexFlowBlock_min_downtime_constr(cheap_plant_min_down_constraints_Bus_T_2)_: ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_2) += 1 + +c_e_NonConvexFlowBlock_min_downtime_constr(cheap_plant_min_down_constraints_Bus_T_3)_: ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_3) += 1 + +c_e_NonConvexFlowBlock_min_downtime_constr(cheap_plant_min_down_constraints_Bus_T_4)_: ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_4) += 1 + +c_e_NonConvexFlowBlock_min_downtime_constr(cheap_plant_min_down_constraints_Bus_T_5)_: ++1 NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_5) += 1 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(cheap_plant_min_down_constraints_Bus_T_0_0) <= 10 + 0 <= flow(cheap_plant_min_down_constraints_Bus_T_0_1) <= 10 + 0 <= flow(cheap_plant_min_down_constraints_Bus_T_1_2) <= 10 + 0 <= flow(cheap_plant_min_down_constraints_Bus_T_1_3) <= 10 + 0 <= flow(cheap_plant_min_down_constraints_Bus_T_2_4) <= 10 + 0 <= flow(cheap_plant_min_down_constraints_Bus_T_2_5) <= 10 + 0 <= NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_0) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_1) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_2) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_3) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_4) <= 1 + 0 <= NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_5) <= 1 + 0 <= NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_0) <= 1 + 0 <= NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_1) <= 1 + 0 <= NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_2) <= 1 + 0 <= NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_3) <= 1 + 0 <= NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_4) <= 1 + 0 <= NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_5) <= 1 + 0 <= NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_0) <= 1 + 0 <= NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_1) <= 1 + 0 <= NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_2) <= 1 + 0 <= NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_3) <= 1 + 0 <= NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_4) <= 1 + 0 <= NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_5) <= 1 +binary + NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_0) + NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_1) + NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_2) + NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_3) + NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_4) + NonConvexFlowBlock_status(cheap_plant_min_down_constraints_Bus_T_5) + NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_0) + NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_1) + NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_2) + NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_3) + NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_4) + NonConvexFlowBlock_startup(cheap_plant_min_down_constraints_Bus_T_5) + NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_0) + NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_1) + NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_2) + NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_3) + NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_4) + NonConvexFlowBlock_shutdown(cheap_plant_min_down_constraints_Bus_T_5) +end diff --git a/tests/lp_files/offsettransformer_multi_period.lp b/tests/lp_files/offsettransformer_multi_period.lp new file mode 100644 index 000000000..e986cd8cc --- /dev/null +++ b/tests/lp_files/offsettransformer_multi_period.lp @@ -0,0 +1,182 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++0 ONE_VAR_CONSTANT + +s.t. + +c_e_BusBlock_balance(gasBus_0_0)_: ++1 flow(gasBus_gasboiler_0_0) += 0 + +c_e_BusBlock_balance(gasBus_0_1)_: ++1 flow(gasBus_gasboiler_0_1) += 0 + +c_e_BusBlock_balance(gasBus_1_2)_: ++1 flow(gasBus_gasboiler_1_2) += 0 + +c_e_BusBlock_balance(gasBus_1_3)_: ++1 flow(gasBus_gasboiler_1_3) += 0 + +c_e_BusBlock_balance(gasBus_2_4)_: ++1 flow(gasBus_gasboiler_2_4) += 0 + +c_e_BusBlock_balance(gasBus_2_5)_: ++1 flow(gasBus_gasboiler_2_5) += 0 + +c_e_BusBlock_balance(thermalBus_0_0)_: ++1 flow(gasboiler_thermalBus_0_0) += 0 + +c_e_BusBlock_balance(thermalBus_0_1)_: ++1 flow(gasboiler_thermalBus_0_1) += 0 + +c_e_BusBlock_balance(thermalBus_1_2)_: ++1 flow(gasboiler_thermalBus_1_2) += 0 + +c_e_BusBlock_balance(thermalBus_1_3)_: ++1 flow(gasboiler_thermalBus_1_3) += 0 + +c_e_BusBlock_balance(thermalBus_2_4)_: ++1 flow(gasboiler_thermalBus_2_4) += 0 + +c_e_BusBlock_balance(thermalBus_2_5)_: ++1 flow(gasboiler_thermalBus_2_5) += 0 + +c_u_NonConvexFlowBlock_min(gasBus_gasboiler_0_0)_: ++32 NonConvexFlowBlock_status(gasBus_gasboiler_0) +-1 flow(gasBus_gasboiler_0_0) +<= 0 + +c_u_NonConvexFlowBlock_min(gasBus_gasboiler_0_1)_: ++32 NonConvexFlowBlock_status(gasBus_gasboiler_1) +-1 flow(gasBus_gasboiler_0_1) +<= 0 + +c_u_NonConvexFlowBlock_min(gasBus_gasboiler_1_2)_: ++32 NonConvexFlowBlock_status(gasBus_gasboiler_2) +-1 flow(gasBus_gasboiler_1_2) +<= 0 + +c_u_NonConvexFlowBlock_min(gasBus_gasboiler_1_3)_: ++32 NonConvexFlowBlock_status(gasBus_gasboiler_3) +-1 flow(gasBus_gasboiler_1_3) +<= 0 + +c_u_NonConvexFlowBlock_min(gasBus_gasboiler_2_4)_: ++32 NonConvexFlowBlock_status(gasBus_gasboiler_4) +-1 flow(gasBus_gasboiler_2_4) +<= 0 + +c_u_NonConvexFlowBlock_min(gasBus_gasboiler_2_5)_: ++32 NonConvexFlowBlock_status(gasBus_gasboiler_5) +-1 flow(gasBus_gasboiler_2_5) +<= 0 + +c_u_NonConvexFlowBlock_max(gasBus_gasboiler_0_0)_: +-100 NonConvexFlowBlock_status(gasBus_gasboiler_0) ++1 flow(gasBus_gasboiler_0_0) +<= 0 + +c_u_NonConvexFlowBlock_max(gasBus_gasboiler_0_1)_: +-100 NonConvexFlowBlock_status(gasBus_gasboiler_1) ++1 flow(gasBus_gasboiler_0_1) +<= 0 + +c_u_NonConvexFlowBlock_max(gasBus_gasboiler_1_2)_: +-100 NonConvexFlowBlock_status(gasBus_gasboiler_2) ++1 flow(gasBus_gasboiler_1_2) +<= 0 + +c_u_NonConvexFlowBlock_max(gasBus_gasboiler_1_3)_: +-100 NonConvexFlowBlock_status(gasBus_gasboiler_3) ++1 flow(gasBus_gasboiler_1_3) +<= 0 + +c_u_NonConvexFlowBlock_max(gasBus_gasboiler_2_4)_: +-100 NonConvexFlowBlock_status(gasBus_gasboiler_4) ++1 flow(gasBus_gasboiler_2_4) +<= 0 + +c_u_NonConvexFlowBlock_max(gasBus_gasboiler_2_5)_: +-100 NonConvexFlowBlock_status(gasBus_gasboiler_5) ++1 flow(gasBus_gasboiler_2_5) +<= 0 + +c_e_OffsetTransformerBlock_relation(gasboiler_0_0)_: +-17 NonConvexFlowBlock_status(gasBus_gasboiler_0) ++0.90000000000000002 flow(gasBus_gasboiler_0_0) +-1 flow(gasboiler_thermalBus_0_0) += 0 + +c_e_OffsetTransformerBlock_relation(gasboiler_0_1)_: +-17 NonConvexFlowBlock_status(gasBus_gasboiler_1) ++0.90000000000000002 flow(gasBus_gasboiler_0_1) +-1 flow(gasboiler_thermalBus_0_1) += 0 + +c_e_OffsetTransformerBlock_relation(gasboiler_1_2)_: +-17 NonConvexFlowBlock_status(gasBus_gasboiler_2) ++0.90000000000000002 flow(gasBus_gasboiler_1_2) +-1 flow(gasboiler_thermalBus_1_2) += 0 + +c_e_OffsetTransformerBlock_relation(gasboiler_1_3)_: +-17 NonConvexFlowBlock_status(gasBus_gasboiler_3) ++0.90000000000000002 flow(gasBus_gasboiler_1_3) +-1 flow(gasboiler_thermalBus_1_3) += 0 + +c_e_OffsetTransformerBlock_relation(gasboiler_2_4)_: +-17 NonConvexFlowBlock_status(gasBus_gasboiler_4) ++0.90000000000000002 flow(gasBus_gasboiler_2_4) +-1 flow(gasboiler_thermalBus_2_4) += 0 + +c_e_OffsetTransformerBlock_relation(gasboiler_2_5)_: +-17 NonConvexFlowBlock_status(gasBus_gasboiler_5) ++0.90000000000000002 flow(gasBus_gasboiler_2_5) +-1 flow(gasboiler_thermalBus_2_5) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(gasBus_gasboiler_0_0) <= 100 + 0 <= flow(gasBus_gasboiler_0_1) <= 100 + 0 <= flow(gasBus_gasboiler_1_2) <= 100 + 0 <= flow(gasBus_gasboiler_1_3) <= 100 + 0 <= flow(gasBus_gasboiler_2_4) <= 100 + 0 <= flow(gasBus_gasboiler_2_5) <= 100 + 0 <= flow(gasboiler_thermalBus_0_0) <= +inf + 0 <= flow(gasboiler_thermalBus_0_1) <= +inf + 0 <= flow(gasboiler_thermalBus_1_2) <= +inf + 0 <= flow(gasboiler_thermalBus_1_3) <= +inf + 0 <= flow(gasboiler_thermalBus_2_4) <= +inf + 0 <= flow(gasboiler_thermalBus_2_5) <= +inf + 0 <= NonConvexFlowBlock_status(gasBus_gasboiler_0) <= 1 + 0 <= NonConvexFlowBlock_status(gasBus_gasboiler_1) <= 1 + 0 <= NonConvexFlowBlock_status(gasBus_gasboiler_2) <= 1 + 0 <= NonConvexFlowBlock_status(gasBus_gasboiler_3) <= 1 + 0 <= NonConvexFlowBlock_status(gasBus_gasboiler_4) <= 1 + 0 <= NonConvexFlowBlock_status(gasBus_gasboiler_5) <= 1 +binary + NonConvexFlowBlock_status(gasBus_gasboiler_0) + NonConvexFlowBlock_status(gasBus_gasboiler_1) + NonConvexFlowBlock_status(gasBus_gasboiler_2) + NonConvexFlowBlock_status(gasBus_gasboiler_3) + NonConvexFlowBlock_status(gasBus_gasboiler_4) + NonConvexFlowBlock_status(gasBus_gasboiler_5) +end diff --git a/tests/lp_files/periodical_emission_limit.lp b/tests/lp_files/periodical_emission_limit.lp index 9b1429d18..55ca3c315 100644 --- a/tests/lp_files/periodical_emission_limit.lp +++ b/tests/lp_files/periodical_emission_limit.lp @@ -11,21 +11,21 @@ c_u_periodical_integral_limit(0)_: -1 flow(source1_electricityBus_0_1) +3.5 flow(source2_electricityBus_0_0) +3.5 flow(source2_electricityBus_0_1) -<= 222 +<= 300 c_u_periodical_integral_limit(1)_: +2 flow(source1_electricityBus_1_2) +1 flow(source1_electricityBus_1_3) +3.5 flow(source2_electricityBus_1_2) +3.5 flow(source2_electricityBus_1_3) -<= 222 +<= 200 c_u_periodical_integral_limit(2)_: +0.5 flow(source1_electricityBus_2_4) +0.5 flow(source1_electricityBus_2_5) +3.5 flow(source2_electricityBus_2_4) +3.5 flow(source2_electricityBus_2_5) -<= 222 +<= 100 c_e_BusBlock_balance(electricityBus_0_0)_: +1 flow(source1_electricityBus_0_0) diff --git a/tests/lp_files/periodical_investment_limit.lp b/tests/lp_files/periodical_investment_limit.lp new file mode 100644 index 000000000..8fc3bddde --- /dev/null +++ b/tests/lp_files/periodical_investment_limit.lp @@ -0,0 +1,582 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++194.22716197630274 GenericInvestmentStorageBlock_invest(storage_invest_limit_0) ++190.4187862512772 GenericInvestmentStorageBlock_invest(storage_invest_limit_1) ++186.68508456007567 GenericInvestmentStorageBlock_invest(storage_invest_limit_2) ++285.39374551281168 InvestmentFlowBlock_invest(Source_Bus1_0) ++279.7977897184428 InvestmentFlowBlock_invest(Source_Bus1_1) ++274.31155854749295 InvestmentFlowBlock_invest(Source_Bus1_2) + +s.t. + +c_u_investment_limit_per_period(0)_: ++194.22716197630274 GenericInvestmentStorageBlock_invest(storage_invest_limit_0) ++285.39374551281168 InvestmentFlowBlock_invest(Source_Bus1_0) +<= 500 + +c_u_investment_limit_per_period(1)_: ++190.4187862512772 GenericInvestmentStorageBlock_invest(storage_invest_limit_1) ++279.7977897184428 InvestmentFlowBlock_invest(Source_Bus1_1) +<= 400 + +c_u_investment_limit_per_period(2)_: ++186.68508456007567 GenericInvestmentStorageBlock_invest(storage_invest_limit_2) ++274.31155854749295 InvestmentFlowBlock_invest(Source_Bus1_2) +<= 300 + +c_e_BusBlock_balance(Bus1_0_0)_: +-1 flow(Bus1_storage_invest_limit_0_0) ++1 flow(Source_Bus1_0_0) ++1 flow(storage_invest_limit_Bus1_0_0) += 0 + +c_e_BusBlock_balance(Bus1_0_1)_: +-1 flow(Bus1_storage_invest_limit_0_1) ++1 flow(Source_Bus1_0_1) ++1 flow(storage_invest_limit_Bus1_0_1) += 0 + +c_e_BusBlock_balance(Bus1_1_2)_: +-1 flow(Bus1_storage_invest_limit_1_2) ++1 flow(Source_Bus1_1_2) ++1 flow(storage_invest_limit_Bus1_1_2) += 0 + +c_e_BusBlock_balance(Bus1_1_3)_: +-1 flow(Bus1_storage_invest_limit_1_3) ++1 flow(Source_Bus1_1_3) ++1 flow(storage_invest_limit_Bus1_1_3) += 0 + +c_e_BusBlock_balance(Bus1_2_4)_: +-1 flow(Bus1_storage_invest_limit_2_4) ++1 flow(Source_Bus1_2_4) ++1 flow(storage_invest_limit_Bus1_2_4) += 0 + +c_e_BusBlock_balance(Bus1_2_5)_: +-1 flow(Bus1_storage_invest_limit_2_5) ++1 flow(Source_Bus1_2_5) ++1 flow(storage_invest_limit_Bus1_2_5) += 0 + +c_e_InvestmentFlowBlock_total_rule(Bus1_storage_invest_limit_0)_: +-1 InvestmentFlowBlock_invest(Bus1_storage_invest_limit_0) ++1 InvestmentFlowBlock_total(Bus1_storage_invest_limit_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(Bus1_storage_invest_limit_1)_: +-1 InvestmentFlowBlock_invest(Bus1_storage_invest_limit_1) ++1 InvestmentFlowBlock_old(Bus1_storage_invest_limit_1) +-1 InvestmentFlowBlock_total(Bus1_storage_invest_limit_0) ++1 InvestmentFlowBlock_total(Bus1_storage_invest_limit_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(Bus1_storage_invest_limit_2)_: +-1 InvestmentFlowBlock_invest(Bus1_storage_invest_limit_2) ++1 InvestmentFlowBlock_old(Bus1_storage_invest_limit_2) +-1 InvestmentFlowBlock_total(Bus1_storage_invest_limit_1) ++1 InvestmentFlowBlock_total(Bus1_storage_invest_limit_2) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_0)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_0) ++1 InvestmentFlowBlock_total(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_1)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_1) ++1 InvestmentFlowBlock_old(Source_Bus1_1) +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 InvestmentFlowBlock_total(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_2)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_2) ++1 InvestmentFlowBlock_old(Source_Bus1_2) +-1 InvestmentFlowBlock_total(Source_Bus1_1) ++1 InvestmentFlowBlock_total(Source_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_invest_limit_Bus1_0)_: +-1 InvestmentFlowBlock_invest(storage_invest_limit_Bus1_0) ++1 InvestmentFlowBlock_total(storage_invest_limit_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_invest_limit_Bus1_1)_: +-1 InvestmentFlowBlock_invest(storage_invest_limit_Bus1_1) ++1 InvestmentFlowBlock_old(storage_invest_limit_Bus1_1) +-1 InvestmentFlowBlock_total(storage_invest_limit_Bus1_0) ++1 InvestmentFlowBlock_total(storage_invest_limit_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_invest_limit_Bus1_2)_: +-1 InvestmentFlowBlock_invest(storage_invest_limit_Bus1_2) ++1 InvestmentFlowBlock_old(storage_invest_limit_Bus1_2) +-1 InvestmentFlowBlock_total(storage_invest_limit_Bus1_1) ++1 InvestmentFlowBlock_total(storage_invest_limit_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Bus1_storage_invest_limit_0)_: ++1 InvestmentFlowBlock_old_end(Bus1_storage_invest_limit_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Bus1_storage_invest_limit_1)_: ++1 InvestmentFlowBlock_old_end(Bus1_storage_invest_limit_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Bus1_storage_invest_limit_2)_: ++1 InvestmentFlowBlock_old_end(Bus1_storage_invest_limit_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Source_Bus1_0)_: ++1 InvestmentFlowBlock_old_end(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Source_Bus1_1)_: ++1 InvestmentFlowBlock_old_end(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Source_Bus1_2)_: ++1 InvestmentFlowBlock_old_end(Source_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage_invest_limit_Bus1_0)_: ++1 InvestmentFlowBlock_old_end(storage_invest_limit_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage_invest_limit_Bus1_1)_: ++1 InvestmentFlowBlock_old_end(storage_invest_limit_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage_invest_limit_Bus1_2)_: ++1 InvestmentFlowBlock_old_end(storage_invest_limit_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Bus1_storage_invest_limit_0)_: ++1 InvestmentFlowBlock_old_exo(Bus1_storage_invest_limit_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Bus1_storage_invest_limit_1)_: ++1 InvestmentFlowBlock_old_exo(Bus1_storage_invest_limit_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Bus1_storage_invest_limit_2)_: ++1 InvestmentFlowBlock_old_exo(Bus1_storage_invest_limit_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Source_Bus1_0)_: ++1 InvestmentFlowBlock_old_exo(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Source_Bus1_1)_: ++1 InvestmentFlowBlock_old_exo(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Source_Bus1_2)_: ++1 InvestmentFlowBlock_old_exo(Source_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage_invest_limit_Bus1_0)_: ++1 InvestmentFlowBlock_old_exo(storage_invest_limit_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage_invest_limit_Bus1_1)_: ++1 InvestmentFlowBlock_old_exo(storage_invest_limit_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage_invest_limit_Bus1_2)_: ++1 InvestmentFlowBlock_old_exo(storage_invest_limit_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(Bus1_storage_invest_limit_0)_: ++1 InvestmentFlowBlock_old(Bus1_storage_invest_limit_0) +-1 InvestmentFlowBlock_old_end(Bus1_storage_invest_limit_0) +-1 InvestmentFlowBlock_old_exo(Bus1_storage_invest_limit_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(Bus1_storage_invest_limit_1)_: ++1 InvestmentFlowBlock_old(Bus1_storage_invest_limit_1) +-1 InvestmentFlowBlock_old_end(Bus1_storage_invest_limit_1) +-1 InvestmentFlowBlock_old_exo(Bus1_storage_invest_limit_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(Bus1_storage_invest_limit_2)_: ++1 InvestmentFlowBlock_old(Bus1_storage_invest_limit_2) +-1 InvestmentFlowBlock_old_end(Bus1_storage_invest_limit_2) +-1 InvestmentFlowBlock_old_exo(Bus1_storage_invest_limit_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(Source_Bus1_0)_: ++1 InvestmentFlowBlock_old(Source_Bus1_0) +-1 InvestmentFlowBlock_old_end(Source_Bus1_0) +-1 InvestmentFlowBlock_old_exo(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(Source_Bus1_1)_: ++1 InvestmentFlowBlock_old(Source_Bus1_1) +-1 InvestmentFlowBlock_old_end(Source_Bus1_1) +-1 InvestmentFlowBlock_old_exo(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(Source_Bus1_2)_: ++1 InvestmentFlowBlock_old(Source_Bus1_2) +-1 InvestmentFlowBlock_old_end(Source_Bus1_2) +-1 InvestmentFlowBlock_old_exo(Source_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage_invest_limit_Bus1_0)_: ++1 InvestmentFlowBlock_old(storage_invest_limit_Bus1_0) +-1 InvestmentFlowBlock_old_end(storage_invest_limit_Bus1_0) +-1 InvestmentFlowBlock_old_exo(storage_invest_limit_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage_invest_limit_Bus1_1)_: ++1 InvestmentFlowBlock_old(storage_invest_limit_Bus1_1) +-1 InvestmentFlowBlock_old_end(storage_invest_limit_Bus1_1) +-1 InvestmentFlowBlock_old_exo(storage_invest_limit_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage_invest_limit_Bus1_2)_: ++1 InvestmentFlowBlock_old(storage_invest_limit_Bus1_2) +-1 InvestmentFlowBlock_old_end(storage_invest_limit_Bus1_2) +-1 InvestmentFlowBlock_old_exo(storage_invest_limit_Bus1_2) += 0 + +c_u_InvestmentFlowBlock_max(Bus1_storage_invest_limit_0_0)_: +-1 InvestmentFlowBlock_total(Bus1_storage_invest_limit_0) ++1 flow(Bus1_storage_invest_limit_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_storage_invest_limit_0_1)_: +-1 InvestmentFlowBlock_total(Bus1_storage_invest_limit_0) ++1 flow(Bus1_storage_invest_limit_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_storage_invest_limit_1_2)_: +-1 InvestmentFlowBlock_total(Bus1_storage_invest_limit_1) ++1 flow(Bus1_storage_invest_limit_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_storage_invest_limit_1_3)_: +-1 InvestmentFlowBlock_total(Bus1_storage_invest_limit_1) ++1 flow(Bus1_storage_invest_limit_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_storage_invest_limit_2_4)_: +-1 InvestmentFlowBlock_total(Bus1_storage_invest_limit_2) ++1 flow(Bus1_storage_invest_limit_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(Bus1_storage_invest_limit_2_5)_: +-1 InvestmentFlowBlock_total(Bus1_storage_invest_limit_2) ++1 flow(Bus1_storage_invest_limit_2_5) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0_0)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0_1)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_1_2)_: +-1 InvestmentFlowBlock_total(Source_Bus1_1) ++1 flow(Source_Bus1_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_1_3)_: +-1 InvestmentFlowBlock_total(Source_Bus1_1) ++1 flow(Source_Bus1_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_2_4)_: +-1 InvestmentFlowBlock_total(Source_Bus1_2) ++1 flow(Source_Bus1_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_2_5)_: +-1 InvestmentFlowBlock_total(Source_Bus1_2) ++1 flow(Source_Bus1_2_5) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_invest_limit_Bus1_0_0)_: +-1 InvestmentFlowBlock_total(storage_invest_limit_Bus1_0) ++1 flow(storage_invest_limit_Bus1_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_invest_limit_Bus1_0_1)_: +-1 InvestmentFlowBlock_total(storage_invest_limit_Bus1_0) ++1 flow(storage_invest_limit_Bus1_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_invest_limit_Bus1_1_2)_: +-1 InvestmentFlowBlock_total(storage_invest_limit_Bus1_1) ++1 flow(storage_invest_limit_Bus1_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_invest_limit_Bus1_1_3)_: +-1 InvestmentFlowBlock_total(storage_invest_limit_Bus1_1) ++1 flow(storage_invest_limit_Bus1_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_invest_limit_Bus1_2_4)_: +-1 InvestmentFlowBlock_total(storage_invest_limit_Bus1_2) ++1 flow(storage_invest_limit_Bus1_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_invest_limit_Bus1_2_5)_: +-1 InvestmentFlowBlock_total(storage_invest_limit_Bus1_2) ++1 flow(storage_invest_limit_Bus1_2_5) +<= 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_invest_limit_0)_: +-1 GenericInvestmentStorageBlock_invest(storage_invest_limit_0) ++1 GenericInvestmentStorageBlock_total(storage_invest_limit_0) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_invest_limit_1)_: +-1 GenericInvestmentStorageBlock_invest(storage_invest_limit_1) ++1 GenericInvestmentStorageBlock_old(storage_invest_limit_1) +-1 GenericInvestmentStorageBlock_total(storage_invest_limit_0) ++1 GenericInvestmentStorageBlock_total(storage_invest_limit_1) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_invest_limit_2)_: +-1 GenericInvestmentStorageBlock_invest(storage_invest_limit_2) ++1 GenericInvestmentStorageBlock_old(storage_invest_limit_2) +-1 GenericInvestmentStorageBlock_total(storage_invest_limit_1) ++1 GenericInvestmentStorageBlock_total(storage_invest_limit_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage_invest_limit_0)_: ++1 GenericInvestmentStorageBlock_old_end(storage_invest_limit_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage_invest_limit_1)_: ++1 GenericInvestmentStorageBlock_old_end(storage_invest_limit_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage_invest_limit_2)_: ++1 GenericInvestmentStorageBlock_old_end(storage_invest_limit_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage_invest_limit_0)_: ++1 GenericInvestmentStorageBlock_old_exo(storage_invest_limit_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage_invest_limit_1)_: ++1 GenericInvestmentStorageBlock_old_exo(storage_invest_limit_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage_invest_limit_2)_: ++1 GenericInvestmentStorageBlock_old_exo(storage_invest_limit_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage_invest_limit_0)_: ++1 GenericInvestmentStorageBlock_old(storage_invest_limit_0) +-1 GenericInvestmentStorageBlock_old_end(storage_invest_limit_0) +-1 GenericInvestmentStorageBlock_old_exo(storage_invest_limit_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage_invest_limit_1)_: ++1 GenericInvestmentStorageBlock_old(storage_invest_limit_1) +-1 GenericInvestmentStorageBlock_old_end(storage_invest_limit_1) +-1 GenericInvestmentStorageBlock_old_exo(storage_invest_limit_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage_invest_limit_2)_: ++1 GenericInvestmentStorageBlock_old(storage_invest_limit_2) +-1 GenericInvestmentStorageBlock_old_end(storage_invest_limit_2) +-1 GenericInvestmentStorageBlock_old_exo(storage_invest_limit_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_invest_limit_0_1)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_0) ++1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_1) +-1 flow(Bus1_storage_invest_limit_0_1) ++1 flow(storage_invest_limit_Bus1_0_1) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_invest_limit_1_2)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_1) ++1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_2) +-1 flow(Bus1_storage_invest_limit_1_2) ++1 flow(storage_invest_limit_Bus1_1_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_invest_limit_1_3)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_2) ++1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_3) +-1 flow(Bus1_storage_invest_limit_1_3) ++1 flow(storage_invest_limit_Bus1_1_3) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_invest_limit_2_4)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_3) ++1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_4) +-1 flow(Bus1_storage_invest_limit_2_4) ++1 flow(storage_invest_limit_Bus1_2_4) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_invest_limit_2_5)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_4) ++1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_5) +-1 flow(Bus1_storage_invest_limit_2_5) ++1 flow(storage_invest_limit_Bus1_2_5) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_invest_limit_0)_: +-0.20000000000000001 GenericInvestmentStorageBlock_total(storage_invest_limit_0) ++1 InvestmentFlowBlock_total(Bus1_storage_invest_limit_0) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_invest_limit_1)_: +-0.20000000000000001 GenericInvestmentStorageBlock_total(storage_invest_limit_1) ++1 InvestmentFlowBlock_total(Bus1_storage_invest_limit_1) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_invest_limit_2)_: +-0.20000000000000001 GenericInvestmentStorageBlock_total(storage_invest_limit_2) ++1 InvestmentFlowBlock_total(Bus1_storage_invest_limit_2) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_invest_limit_0)_: +-0.20000000000000001 GenericInvestmentStorageBlock_total(storage_invest_limit_0) ++1 InvestmentFlowBlock_total(storage_invest_limit_Bus1_0) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_invest_limit_1)_: +-0.20000000000000001 GenericInvestmentStorageBlock_total(storage_invest_limit_1) ++1 InvestmentFlowBlock_total(storage_invest_limit_Bus1_1) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_invest_limit_2)_: +-0.20000000000000001 GenericInvestmentStorageBlock_total(storage_invest_limit_2) ++1 InvestmentFlowBlock_total(storage_invest_limit_Bus1_2) += 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_invest_limit_0_0)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_0) +-1 GenericInvestmentStorageBlock_total(storage_invest_limit_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_invest_limit_0_1)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_1) +-1 GenericInvestmentStorageBlock_total(storage_invest_limit_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_invest_limit_1_2)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_2) +-1 GenericInvestmentStorageBlock_total(storage_invest_limit_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_invest_limit_1_3)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_3) +-1 GenericInvestmentStorageBlock_total(storage_invest_limit_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_invest_limit_2_4)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_4) +-1 GenericInvestmentStorageBlock_total(storage_invest_limit_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_invest_limit_2_5)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_invest_limit_5) +-1 GenericInvestmentStorageBlock_total(storage_invest_limit_2) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(Bus1_storage_invest_limit_0_0) <= +inf + 0 <= flow(Bus1_storage_invest_limit_0_1) <= +inf + 0 <= flow(Bus1_storage_invest_limit_1_2) <= +inf + 0 <= flow(Bus1_storage_invest_limit_1_3) <= +inf + 0 <= flow(Bus1_storage_invest_limit_2_4) <= +inf + 0 <= flow(Bus1_storage_invest_limit_2_5) <= +inf + 0 <= flow(Source_Bus1_0_0) <= +inf + 0 <= flow(Source_Bus1_0_1) <= +inf + 0 <= flow(Source_Bus1_1_2) <= +inf + 0 <= flow(Source_Bus1_1_3) <= +inf + 0 <= flow(Source_Bus1_2_4) <= +inf + 0 <= flow(Source_Bus1_2_5) <= +inf + 0 <= flow(storage_invest_limit_Bus1_0_0) <= +inf + 0 <= flow(storage_invest_limit_Bus1_0_1) <= +inf + 0 <= flow(storage_invest_limit_Bus1_1_2) <= +inf + 0 <= flow(storage_invest_limit_Bus1_1_3) <= +inf + 0 <= flow(storage_invest_limit_Bus1_2_4) <= +inf + 0 <= flow(storage_invest_limit_Bus1_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(Bus1_storage_invest_limit_0) <= +inf + 0 <= InvestmentFlowBlock_invest(Bus1_storage_invest_limit_1) <= +inf + 0 <= InvestmentFlowBlock_invest(Bus1_storage_invest_limit_2) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_invest(storage_invest_limit_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage_invest_limit_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_invest(storage_invest_limit_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_total(Bus1_storage_invest_limit_0) <= +inf + 0 <= InvestmentFlowBlock_total(Bus1_storage_invest_limit_1) <= +inf + 0 <= InvestmentFlowBlock_total(Bus1_storage_invest_limit_2) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_total(storage_invest_limit_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage_invest_limit_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_total(storage_invest_limit_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old(Bus1_storage_invest_limit_0) <= +inf + 0 <= InvestmentFlowBlock_old(Bus1_storage_invest_limit_1) <= +inf + 0 <= InvestmentFlowBlock_old(Bus1_storage_invest_limit_2) <= +inf + 0 <= InvestmentFlowBlock_old(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old(storage_invest_limit_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old(storage_invest_limit_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old(storage_invest_limit_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(Bus1_storage_invest_limit_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(Bus1_storage_invest_limit_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(Bus1_storage_invest_limit_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage_invest_limit_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage_invest_limit_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage_invest_limit_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Bus1_storage_invest_limit_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Bus1_storage_invest_limit_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Bus1_storage_invest_limit_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage_invest_limit_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage_invest_limit_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage_invest_limit_Bus1_2) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_invest_limit_0) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_invest_limit_1) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_invest_limit_2) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_invest_limit_3) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_invest_limit_4) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_invest_limit_5) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage_invest_limit_0) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage_invest_limit_1) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage_invest_limit_2) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage_invest_limit_0) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage_invest_limit_1) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage_invest_limit_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage_invest_limit_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage_invest_limit_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage_invest_limit_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage_invest_limit_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage_invest_limit_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage_invest_limit_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage_invest_limit_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage_invest_limit_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage_invest_limit_2) <= +inf +end diff --git a/tests/lp_files/periodical_investment_limit_with_dsm_DIW.lp b/tests/lp_files/periodical_investment_limit_with_dsm_DIW.lp new file mode 100644 index 000000000..763d5fe6d --- /dev/null +++ b/tests/lp_files/periodical_investment_limit_with_dsm_DIW.lp @@ -0,0 +1,614 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++285.39374551281168 InvestmentFlowBlock_invest(Source_Bus1_0) ++279.7977897184428 InvestmentFlowBlock_invest(Source_Bus1_1) ++274.31155854749295 InvestmentFlowBlock_invest(Source_Bus1_2) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_0) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_1) ++0.49019607843137253 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_2) ++0.49019607843137253 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_3) ++0.48058439061899266 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_4) ++0.48058439061899266 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_5) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_0) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_1) ++0.49019607843137253 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_2) ++0.49019607843137253 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_3) ++0.48058439061899266 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_4) ++0.48058439061899266 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_5) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_0) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_1) ++0.49019607843137253 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_2) ++0.49019607843137253 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_3) ++0.48058439061899266 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_4) ++0.48058439061899266 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_5) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_0) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_1) ++0.49019607843137253 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_2) ++0.49019607843137253 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_3) ++0.48058439061899266 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_4) ++0.48058439061899266 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_5) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_0) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_1) ++0.49019607843137253 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_2) ++0.49019607843137253 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_3) ++0.48058439061899266 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_4) ++0.48058439061899266 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_5) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_0) ++0.5 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_1) ++0.49019607843137253 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_2) ++0.49019607843137253 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_3) ++0.48058439061899266 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_4) ++0.48058439061899266 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_5) ++0.5 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_0) ++0.5 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_1) ++0.49019607843137253 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_2) ++0.49019607843137253 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_3) ++0.48058439061899266 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_4) ++0.48058439061899266 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_5) ++103.00990099009903 SinkDSMDIWInvestmentBlock_invest(sink_dsm_DIW_0) ++100.990099009901 SinkDSMDIWInvestmentBlock_invest(sink_dsm_DIW_1) ++99.009900990099013 SinkDSMDIWInvestmentBlock_invest(sink_dsm_DIW_2) + +s.t. + +c_u_investment_limit_per_period(0)_: ++285.39374551281168 InvestmentFlowBlock_invest(Source_Bus1_0) ++103.00990099009903 SinkDSMDIWInvestmentBlock_invest(sink_dsm_DIW_0) +<= 400 + +c_u_investment_limit_per_period(1)_: ++279.7977897184428 InvestmentFlowBlock_invest(Source_Bus1_1) ++100.990099009901 SinkDSMDIWInvestmentBlock_invest(sink_dsm_DIW_1) +<= 300 + +c_u_investment_limit_per_period(2)_: ++274.31155854749295 InvestmentFlowBlock_invest(Source_Bus1_2) ++99.009900990099013 SinkDSMDIWInvestmentBlock_invest(sink_dsm_DIW_2) +<= 200 + +c_e_BusBlock_balance(Bus1_0_0)_: +-1 flow(Bus1_sink_dsm_DIW_0_0) ++1 flow(Source_Bus1_0_0) += 0 + +c_e_BusBlock_balance(Bus1_0_1)_: +-1 flow(Bus1_sink_dsm_DIW_0_1) ++1 flow(Source_Bus1_0_1) += 0 + +c_e_BusBlock_balance(Bus1_1_2)_: +-1 flow(Bus1_sink_dsm_DIW_1_2) ++1 flow(Source_Bus1_1_2) += 0 + +c_e_BusBlock_balance(Bus1_1_3)_: +-1 flow(Bus1_sink_dsm_DIW_1_3) ++1 flow(Source_Bus1_1_3) += 0 + +c_e_BusBlock_balance(Bus1_2_4)_: +-1 flow(Bus1_sink_dsm_DIW_2_4) ++1 flow(Source_Bus1_2_4) += 0 + +c_e_BusBlock_balance(Bus1_2_5)_: +-1 flow(Bus1_sink_dsm_DIW_2_5) ++1 flow(Source_Bus1_2_5) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_0)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_0) ++1 InvestmentFlowBlock_total(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_1)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_1) ++1 InvestmentFlowBlock_old(Source_Bus1_1) +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 InvestmentFlowBlock_total(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_2)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_2) ++1 InvestmentFlowBlock_old(Source_Bus1_2) +-1 InvestmentFlowBlock_total(Source_Bus1_1) ++1 InvestmentFlowBlock_total(Source_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Source_Bus1_0)_: ++1 InvestmentFlowBlock_old_end(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Source_Bus1_1)_: ++1 InvestmentFlowBlock_old_end(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Source_Bus1_2)_: ++1 InvestmentFlowBlock_old_end(Source_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Source_Bus1_0)_: ++1 InvestmentFlowBlock_old_exo(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Source_Bus1_1)_: ++1 InvestmentFlowBlock_old_exo(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Source_Bus1_2)_: ++1 InvestmentFlowBlock_old_exo(Source_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(Source_Bus1_0)_: ++1 InvestmentFlowBlock_old(Source_Bus1_0) +-1 InvestmentFlowBlock_old_end(Source_Bus1_0) +-1 InvestmentFlowBlock_old_exo(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(Source_Bus1_1)_: ++1 InvestmentFlowBlock_old(Source_Bus1_1) +-1 InvestmentFlowBlock_old_end(Source_Bus1_1) +-1 InvestmentFlowBlock_old_exo(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(Source_Bus1_2)_: ++1 InvestmentFlowBlock_old(Source_Bus1_2) +-1 InvestmentFlowBlock_old_end(Source_Bus1_2) +-1 InvestmentFlowBlock_old_exo(Source_Bus1_2) += 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0_0)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0_1)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_1_2)_: +-1 InvestmentFlowBlock_total(Source_Bus1_1) ++1 flow(Source_Bus1_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_1_3)_: +-1 InvestmentFlowBlock_total(Source_Bus1_1) ++1 flow(Source_Bus1_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_2_4)_: +-1 InvestmentFlowBlock_total(Source_Bus1_2) ++1 flow(Source_Bus1_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_2_5)_: +-1 InvestmentFlowBlock_total(Source_Bus1_2) ++1 flow(Source_Bus1_2_5) +<= 0 + +c_e_SinkDSMDIWInvestmentBlock_total_dsm_rule(sink_dsm_DIW_0)_: +-1 SinkDSMDIWInvestmentBlock_invest(sink_dsm_DIW_0) ++1 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) += 50 + +c_e_SinkDSMDIWInvestmentBlock_total_dsm_rule(sink_dsm_DIW_1)_: +-1 SinkDSMDIWInvestmentBlock_invest(sink_dsm_DIW_1) ++1 SinkDSMDIWInvestmentBlock_old(sink_dsm_DIW_1) +-1 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) ++1 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_1) += 0 + +c_e_SinkDSMDIWInvestmentBlock_total_dsm_rule(sink_dsm_DIW_2)_: +-1 SinkDSMDIWInvestmentBlock_invest(sink_dsm_DIW_2) ++1 SinkDSMDIWInvestmentBlock_old(sink_dsm_DIW_2) +-1 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_1) ++1 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_2) += 0 + +c_e_SinkDSMDIWInvestmentBlock_old_dsm_rule_end(sink_dsm_DIW_0)_: ++1 SinkDSMDIWInvestmentBlock_old_end(sink_dsm_DIW_0) += 0 + +c_e_SinkDSMDIWInvestmentBlock_old_dsm_rule_end(sink_dsm_DIW_1)_: ++1 SinkDSMDIWInvestmentBlock_old_end(sink_dsm_DIW_1) += 0 + +c_e_SinkDSMDIWInvestmentBlock_old_dsm_rule_end(sink_dsm_DIW_2)_: +-1 SinkDSMDIWInvestmentBlock_invest(sink_dsm_DIW_0) ++1 SinkDSMDIWInvestmentBlock_old_end(sink_dsm_DIW_2) += 0 + +c_e_SinkDSMDIWInvestmentBlock_old_dsm_rule_exo(sink_dsm_DIW_0)_: ++1 SinkDSMDIWInvestmentBlock_old_exo(sink_dsm_DIW_0) += 0 + +c_e_SinkDSMDIWInvestmentBlock_old_dsm_rule_exo(sink_dsm_DIW_1)_: ++1 SinkDSMDIWInvestmentBlock_old_exo(sink_dsm_DIW_1) += 50 + +c_e_SinkDSMDIWInvestmentBlock_old_dsm_rule_exo(sink_dsm_DIW_2)_: ++1 SinkDSMDIWInvestmentBlock_old_exo(sink_dsm_DIW_2) += 0 + +c_e_SinkDSMDIWInvestmentBlock_old_dsm_rule(sink_dsm_DIW_0)_: ++1 SinkDSMDIWInvestmentBlock_old(sink_dsm_DIW_0) +-1 SinkDSMDIWInvestmentBlock_old_end(sink_dsm_DIW_0) +-1 SinkDSMDIWInvestmentBlock_old_exo(sink_dsm_DIW_0) += 0 + +c_e_SinkDSMDIWInvestmentBlock_old_dsm_rule(sink_dsm_DIW_1)_: ++1 SinkDSMDIWInvestmentBlock_old(sink_dsm_DIW_1) +-1 SinkDSMDIWInvestmentBlock_old_end(sink_dsm_DIW_1) +-1 SinkDSMDIWInvestmentBlock_old_exo(sink_dsm_DIW_1) += 0 + +c_e_SinkDSMDIWInvestmentBlock_old_dsm_rule(sink_dsm_DIW_2)_: ++1 SinkDSMDIWInvestmentBlock_old(sink_dsm_DIW_2) +-1 SinkDSMDIWInvestmentBlock_old_end(sink_dsm_DIW_2) +-1 SinkDSMDIWInvestmentBlock_old_exo(sink_dsm_DIW_2) += 0 + +c_e_SinkDSMDIWInvestmentBlock_shift_shed_vars(sink_dsm_DIW_0)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_0) += 0 + +c_e_SinkDSMDIWInvestmentBlock_shift_shed_vars(sink_dsm_DIW_1)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_1) += 0 + +c_e_SinkDSMDIWInvestmentBlock_shift_shed_vars(sink_dsm_DIW_2)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_2) += 0 + +c_e_SinkDSMDIWInvestmentBlock_shift_shed_vars(sink_dsm_DIW_3)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_3) += 0 + +c_e_SinkDSMDIWInvestmentBlock_shift_shed_vars(sink_dsm_DIW_4)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_4) += 0 + +c_e_SinkDSMDIWInvestmentBlock_shift_shed_vars(sink_dsm_DIW_5)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_5) += 0 + +c_e_SinkDSMDIWInvestmentBlock_input_output_relation(sink_dsm_DIW_0_0)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_0) +-1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_0) +-1 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) ++1 flow(Bus1_sink_dsm_DIW_0_0) += 0 + +c_e_SinkDSMDIWInvestmentBlock_input_output_relation(sink_dsm_DIW_0_1)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_1) +-1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_1) +-1 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) ++1 flow(Bus1_sink_dsm_DIW_0_1) += 0 + +c_e_SinkDSMDIWInvestmentBlock_input_output_relation(sink_dsm_DIW_1_2)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_2) +-1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_2) +-1 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_1) ++1 flow(Bus1_sink_dsm_DIW_1_2) += 0 + +c_e_SinkDSMDIWInvestmentBlock_input_output_relation(sink_dsm_DIW_1_3)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_3) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_3) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_3) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_3) +-1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_3) +-1 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_1) ++1 flow(Bus1_sink_dsm_DIW_1_3) += 0 + +c_e_SinkDSMDIWInvestmentBlock_input_output_relation(sink_dsm_DIW_2_4)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_4) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_4) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_4) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_4) +-1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_4) +-1 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_2) ++1 flow(Bus1_sink_dsm_DIW_2_4) += 0 + +c_e_SinkDSMDIWInvestmentBlock_input_output_relation(sink_dsm_DIW_2_5)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_5) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_5) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_5) +-1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_5) +-1 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_2) ++1 flow(Bus1_sink_dsm_DIW_2_5) += 0 + +c_e_SinkDSMDIWInvestmentBlock_dsm_updo_constraint(sink_dsm_DIW_0)_: +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_0) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_1) ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_0) += 0 + +c_e_SinkDSMDIWInvestmentBlock_dsm_updo_constraint(sink_dsm_DIW_1)_: +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_0) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_1) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_2) ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_1) += 0 + +c_e_SinkDSMDIWInvestmentBlock_dsm_updo_constraint(sink_dsm_DIW_2)_: +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_1) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_2) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_3) ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_2) += 0 + +c_e_SinkDSMDIWInvestmentBlock_dsm_updo_constraint(sink_dsm_DIW_3)_: +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_2) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_3) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_4) ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_3) += 0 + +c_e_SinkDSMDIWInvestmentBlock_dsm_updo_constraint(sink_dsm_DIW_4)_: +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_3) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_4) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_5) ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_4) += 0 + +c_e_SinkDSMDIWInvestmentBlock_dsm_updo_constraint(sink_dsm_DIW_5)_: +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_4) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_5) ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_5) += 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(sink_dsm_DIW_0_0)_: ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_0) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(sink_dsm_DIW_0_1)_: ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_1) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(sink_dsm_DIW_1_2)_: ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_2) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_1) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(sink_dsm_DIW_1_3)_: ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_3) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_1) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(sink_dsm_DIW_2_4)_: ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_4) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_2) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(sink_dsm_DIW_2_5)_: ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_5) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_2) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(sink_dsm_DIW_0_0)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_0) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(sink_dsm_DIW_0_1)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_1) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(sink_dsm_DIW_1_2)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_2) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_1) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(sink_dsm_DIW_1_3)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_3) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_3) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_3) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_3) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_1) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(sink_dsm_DIW_2_4)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_4) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_4) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_4) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_4) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_2) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(sink_dsm_DIW_2_5)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_5) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_5) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_5) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_2) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_C2_constraint(sink_dsm_DIW_0_0)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_0) ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_0) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_C2_constraint(sink_dsm_DIW_0_1)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_1) ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_1) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_C2_constraint(sink_dsm_DIW_1_2)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_2) ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_2) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_1) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_C2_constraint(sink_dsm_DIW_1_3)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_3) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_3) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_3) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_3) ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_3) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_1) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_C2_constraint(sink_dsm_DIW_2_4)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_4) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_4) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_4) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_4) ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_4) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_2) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_C2_constraint(sink_dsm_DIW_2_5)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_5) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_5) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_5) ++1 SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_5) +-0.5 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_2) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_overall_dsm_maximum(sink_dsm_DIW_0)_: ++1 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) +<= 1000 + +c_u_SinkDSMDIWInvestmentBlock_overall_dsm_maximum(sink_dsm_DIW_1)_: ++1 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_1) +<= 1000 + +c_u_SinkDSMDIWInvestmentBlock_overall_dsm_maximum(sink_dsm_DIW_2)_: ++1 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_2) +<= 1000 + +c_l_SinkDSMDIWInvestmentBlock_overall_minimum(sink_dsm_DIW)_: ++1 SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_2) +>= 200 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(Bus1_sink_dsm_DIW_0_0) <= +inf + 0 <= flow(Bus1_sink_dsm_DIW_0_1) <= +inf + 0 <= flow(Bus1_sink_dsm_DIW_1_2) <= +inf + 0 <= flow(Bus1_sink_dsm_DIW_1_3) <= +inf + 0 <= flow(Bus1_sink_dsm_DIW_2_4) <= +inf + 0 <= flow(Bus1_sink_dsm_DIW_2_5) <= +inf + 0 <= flow(Source_Bus1_0_0) <= +inf + 0 <= flow(Source_Bus1_0_1) <= +inf + 0 <= flow(Source_Bus1_1_2) <= +inf + 0 <= flow(Source_Bus1_1_3) <= +inf + 0 <= flow(Source_Bus1_2_4) <= +inf + 0 <= flow(Source_Bus1_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Source_Bus1_2) <= +inf + 33 <= SinkDSMDIWInvestmentBlock_invest(sink_dsm_DIW_0) <= 100 + 33 <= SinkDSMDIWInvestmentBlock_invest(sink_dsm_DIW_1) <= 100 + 33 <= SinkDSMDIWInvestmentBlock_invest(sink_dsm_DIW_2) <= 100 + 0 <= SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_total(sink_dsm_DIW_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_old(sink_dsm_DIW_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_old(sink_dsm_DIW_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_old(sink_dsm_DIW_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_old_end(sink_dsm_DIW_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_old_end(sink_dsm_DIW_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_old_end(sink_dsm_DIW_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_old_exo(sink_dsm_DIW_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_old_exo(sink_dsm_DIW_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_old_exo(sink_dsm_DIW_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_3) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_4) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_0_5) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_3) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_4) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_1_5) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_3) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_4) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_2_5) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_3) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_4) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_3_5) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_3) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_4) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_4_5) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_3) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_4) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(sink_dsm_DIW_5_5) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_3) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_4) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shed(sink_dsm_DIW_5) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_3) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_4) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_up(sink_dsm_DIW_5) <= +inf +end diff --git a/tests/lp_files/periodical_investment_limit_with_dsm_DLR.lp b/tests/lp_files/periodical_investment_limit_with_dsm_DLR.lp new file mode 100644 index 000000000..3491e22a8 --- /dev/null +++ b/tests/lp_files/periodical_investment_limit_with_dsm_DLR.lp @@ -0,0 +1,762 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++285.39374551281168 InvestmentFlowBlock_invest(Source_Bus1_0) ++279.7977897184428 InvestmentFlowBlock_invest(Source_Bus1_1) ++274.31155854749295 InvestmentFlowBlock_invest(Source_Bus1_2) ++0.5 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_0) ++0.5 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_1) ++0.49019607843137253 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_2) ++0.49019607843137253 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_3) ++0.48058439061899266 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_4) ++0.48058439061899266 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_5) ++0.5 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_0) ++0.5 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_1) ++0.49019607843137253 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_2) ++0.49019607843137253 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_3) ++0.48058439061899266 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_4) ++0.48058439061899266 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_5) ++0.5 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_0) ++0.5 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_1) ++0.49019607843137253 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_2) ++0.49019607843137253 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_3) ++0.48058439061899266 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_4) ++0.48058439061899266 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_5) ++0.5 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_0) ++0.5 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_1) ++0.49019607843137253 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_2) ++0.49019607843137253 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_3) ++0.48058439061899266 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_4) ++0.48058439061899266 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_5) ++103.00990099009903 SinkDSMDLRInvestmentBlock_invest(sink_dsm_DLR_0) ++100.990099009901 SinkDSMDLRInvestmentBlock_invest(sink_dsm_DLR_1) ++99.009900990099013 SinkDSMDLRInvestmentBlock_invest(sink_dsm_DLR_2) + +s.t. + +c_u_investment_limit_per_period(0)_: ++285.39374551281168 InvestmentFlowBlock_invest(Source_Bus1_0) ++103.00990099009903 SinkDSMDLRInvestmentBlock_invest(sink_dsm_DLR_0) +<= 400 + +c_u_investment_limit_per_period(1)_: ++279.7977897184428 InvestmentFlowBlock_invest(Source_Bus1_1) ++100.990099009901 SinkDSMDLRInvestmentBlock_invest(sink_dsm_DLR_1) +<= 300 + +c_u_investment_limit_per_period(2)_: ++274.31155854749295 InvestmentFlowBlock_invest(Source_Bus1_2) ++99.009900990099013 SinkDSMDLRInvestmentBlock_invest(sink_dsm_DLR_2) +<= 200 + +c_e_BusBlock_balance(Bus1_0_0)_: +-1 flow(Bus1_sink_dsm_DLR_0_0) ++1 flow(Source_Bus1_0_0) += 0 + +c_e_BusBlock_balance(Bus1_0_1)_: +-1 flow(Bus1_sink_dsm_DLR_0_1) ++1 flow(Source_Bus1_0_1) += 0 + +c_e_BusBlock_balance(Bus1_1_2)_: +-1 flow(Bus1_sink_dsm_DLR_1_2) ++1 flow(Source_Bus1_1_2) += 0 + +c_e_BusBlock_balance(Bus1_1_3)_: +-1 flow(Bus1_sink_dsm_DLR_1_3) ++1 flow(Source_Bus1_1_3) += 0 + +c_e_BusBlock_balance(Bus1_2_4)_: +-1 flow(Bus1_sink_dsm_DLR_2_4) ++1 flow(Source_Bus1_2_4) += 0 + +c_e_BusBlock_balance(Bus1_2_5)_: +-1 flow(Bus1_sink_dsm_DLR_2_5) ++1 flow(Source_Bus1_2_5) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_0)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_0) ++1 InvestmentFlowBlock_total(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_1)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_1) ++1 InvestmentFlowBlock_old(Source_Bus1_1) +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 InvestmentFlowBlock_total(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_2)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_2) ++1 InvestmentFlowBlock_old(Source_Bus1_2) +-1 InvestmentFlowBlock_total(Source_Bus1_1) ++1 InvestmentFlowBlock_total(Source_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Source_Bus1_0)_: ++1 InvestmentFlowBlock_old_end(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Source_Bus1_1)_: ++1 InvestmentFlowBlock_old_end(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Source_Bus1_2)_: ++1 InvestmentFlowBlock_old_end(Source_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Source_Bus1_0)_: ++1 InvestmentFlowBlock_old_exo(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Source_Bus1_1)_: ++1 InvestmentFlowBlock_old_exo(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Source_Bus1_2)_: ++1 InvestmentFlowBlock_old_exo(Source_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(Source_Bus1_0)_: ++1 InvestmentFlowBlock_old(Source_Bus1_0) +-1 InvestmentFlowBlock_old_end(Source_Bus1_0) +-1 InvestmentFlowBlock_old_exo(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(Source_Bus1_1)_: ++1 InvestmentFlowBlock_old(Source_Bus1_1) +-1 InvestmentFlowBlock_old_end(Source_Bus1_1) +-1 InvestmentFlowBlock_old_exo(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(Source_Bus1_2)_: ++1 InvestmentFlowBlock_old(Source_Bus1_2) +-1 InvestmentFlowBlock_old_end(Source_Bus1_2) +-1 InvestmentFlowBlock_old_exo(Source_Bus1_2) += 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0_0)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0_1)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_1_2)_: +-1 InvestmentFlowBlock_total(Source_Bus1_1) ++1 flow(Source_Bus1_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_1_3)_: +-1 InvestmentFlowBlock_total(Source_Bus1_1) ++1 flow(Source_Bus1_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_2_4)_: +-1 InvestmentFlowBlock_total(Source_Bus1_2) ++1 flow(Source_Bus1_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_2_5)_: +-1 InvestmentFlowBlock_total(Source_Bus1_2) ++1 flow(Source_Bus1_2_5) +<= 0 + +c_e_SinkDSMDLRInvestmentBlock_total_dsm_rule(sink_dsm_DLR_0)_: +-1 SinkDSMDLRInvestmentBlock_invest(sink_dsm_DLR_0) ++1 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) += 50 + +c_e_SinkDSMDLRInvestmentBlock_total_dsm_rule(sink_dsm_DLR_1)_: +-1 SinkDSMDLRInvestmentBlock_invest(sink_dsm_DLR_1) ++1 SinkDSMDLRInvestmentBlock_old(sink_dsm_DLR_1) +-1 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) ++1 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_total_dsm_rule(sink_dsm_DLR_2)_: +-1 SinkDSMDLRInvestmentBlock_invest(sink_dsm_DLR_2) ++1 SinkDSMDLRInvestmentBlock_old(sink_dsm_DLR_2) +-1 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_1) ++1 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_old_dsm_rule_end(sink_dsm_DLR_0)_: ++1 SinkDSMDLRInvestmentBlock_old_end(sink_dsm_DLR_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_old_dsm_rule_end(sink_dsm_DLR_1)_: ++1 SinkDSMDLRInvestmentBlock_old_end(sink_dsm_DLR_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_old_dsm_rule_end(sink_dsm_DLR_2)_: +-1 SinkDSMDLRInvestmentBlock_invest(sink_dsm_DLR_0) ++1 SinkDSMDLRInvestmentBlock_old_end(sink_dsm_DLR_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_old_dsm_rule_exo(sink_dsm_DLR_0)_: ++1 SinkDSMDLRInvestmentBlock_old_exo(sink_dsm_DLR_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_old_dsm_rule_exo(sink_dsm_DLR_1)_: ++1 SinkDSMDLRInvestmentBlock_old_exo(sink_dsm_DLR_1) += 50 + +c_e_SinkDSMDLRInvestmentBlock_old_dsm_rule_exo(sink_dsm_DLR_2)_: ++1 SinkDSMDLRInvestmentBlock_old_exo(sink_dsm_DLR_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_old_dsm_rule(sink_dsm_DLR_0)_: ++1 SinkDSMDLRInvestmentBlock_old(sink_dsm_DLR_0) +-1 SinkDSMDLRInvestmentBlock_old_end(sink_dsm_DLR_0) +-1 SinkDSMDLRInvestmentBlock_old_exo(sink_dsm_DLR_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_old_dsm_rule(sink_dsm_DLR_1)_: ++1 SinkDSMDLRInvestmentBlock_old(sink_dsm_DLR_1) +-1 SinkDSMDLRInvestmentBlock_old_end(sink_dsm_DLR_1) +-1 SinkDSMDLRInvestmentBlock_old_exo(sink_dsm_DLR_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_old_dsm_rule(sink_dsm_DLR_2)_: ++1 SinkDSMDLRInvestmentBlock_old(sink_dsm_DLR_2) +-1 SinkDSMDLRInvestmentBlock_old_end(sink_dsm_DLR_2) +-1 SinkDSMDLRInvestmentBlock_old_exo(sink_dsm_DLR_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_shift_shed_vars(sink_dsm_DLR_1_0)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_shift_shed_vars(sink_dsm_DLR_1_1)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_shift_shed_vars(sink_dsm_DLR_1_2)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_shift_shed_vars(sink_dsm_DLR_1_3)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_3) += 0 + +c_e_SinkDSMDLRInvestmentBlock_shift_shed_vars(sink_dsm_DLR_1_4)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_4) += 0 + +c_e_SinkDSMDLRInvestmentBlock_shift_shed_vars(sink_dsm_DLR_1_5)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_5) += 0 + +c_e_SinkDSMDLRInvestmentBlock_input_output_relation(sink_dsm_DLR_0_0)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_0) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_0) +-1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_0) +-1 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) ++1 flow(Bus1_sink_dsm_DLR_0_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_input_output_relation(sink_dsm_DLR_0_1)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_1) +-1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_1) +-1 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) ++1 flow(Bus1_sink_dsm_DLR_0_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_input_output_relation(sink_dsm_DLR_1_2)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_2) +-1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_2) +-1 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_1) ++1 flow(Bus1_sink_dsm_DLR_1_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_input_output_relation(sink_dsm_DLR_1_3)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_3) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_3) +-1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_3) +-1 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_1) ++1 flow(Bus1_sink_dsm_DLR_1_3) += 0 + +c_e_SinkDSMDLRInvestmentBlock_input_output_relation(sink_dsm_DLR_2_4)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_4) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_4) +-1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_4) +-1 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_2) ++1 flow(Bus1_sink_dsm_DLR_2_4) += 0 + +c_e_SinkDSMDLRInvestmentBlock_input_output_relation(sink_dsm_DLR_2_5)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_5) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_5) +-1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_5) +-1 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_2) ++1 flow(Bus1_sink_dsm_DLR_2_5) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(sink_dsm_DLR_1_0)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(sink_dsm_DLR_1_1)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_1) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(sink_dsm_DLR_1_2)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_2) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(sink_dsm_DLR_1_3)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_3) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(sink_dsm_DLR_1_4)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_4) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_3) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(sink_dsm_DLR_1_5)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_5) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_4) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(sink_dsm_DLR_1_0)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(sink_dsm_DLR_1_1)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_1) +-1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(sink_dsm_DLR_1_2)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_2) +-1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(sink_dsm_DLR_1_3)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_3) +-1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(sink_dsm_DLR_1_4)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_4) +-1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_3) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(sink_dsm_DLR_1_5)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_5) +-1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_4) += 0 + +c_e_SinkDSMDLRInvestmentBlock_no_comp_red(sink_dsm_DLR_1_5)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_5) += 0 + +c_e_SinkDSMDLRInvestmentBlock_no_comp_inc(sink_dsm_DLR_1_5)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_5) += 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_red(sink_dsm_DLR_0_0)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_0) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_red(sink_dsm_DLR_0_1)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_1) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_red(sink_dsm_DLR_1_2)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_2) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_red(sink_dsm_DLR_1_3)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_3) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_red(sink_dsm_DLR_2_4)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_4) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_2) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_red(sink_dsm_DLR_2_5)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_5) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_2) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_inc(sink_dsm_DLR_0_0)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_0) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_inc(sink_dsm_DLR_0_1)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_1) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_inc(sink_dsm_DLR_1_2)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_2) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_inc(sink_dsm_DLR_1_3)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_3) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_3) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_inc(sink_dsm_DLR_2_4)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_4) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_4) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_2) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_inc(sink_dsm_DLR_2_5)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_5) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_5) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_2) +<= 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_red(sink_dsm_DLR_0)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_0) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_red(sink_dsm_DLR_1)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_0) +-1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_red(sink_dsm_DLR_2)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_1) +-1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_red(sink_dsm_DLR_3)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_2) +-1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_3) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_red(sink_dsm_DLR_4)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_3) +-1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_4) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_red(sink_dsm_DLR_5)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_4) +-1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_5) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_inc(sink_dsm_DLR_0)_: +-1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_inc(sink_dsm_DLR_1)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_0) +-1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_inc(sink_dsm_DLR_2)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_1) +-1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_inc(sink_dsm_DLR_3)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_3) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_3) ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_2) +-1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_3) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_inc(sink_dsm_DLR_4)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_4) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_4) ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_3) +-1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_4) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_inc(sink_dsm_DLR_5)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_5) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_5) ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_4) +-1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_5) += 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(sink_dsm_DLR_0_0)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_0) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(sink_dsm_DLR_0_1)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_1) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(sink_dsm_DLR_1_2)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_2) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(sink_dsm_DLR_1_3)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_3) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(sink_dsm_DLR_2_4)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_4) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_2) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(sink_dsm_DLR_2_5)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_5) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_2) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(sink_dsm_DLR_0_0)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_0) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(sink_dsm_DLR_0_1)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_1) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(sink_dsm_DLR_1_2)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_2) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(sink_dsm_DLR_1_3)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_3) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(sink_dsm_DLR_2_4)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_4) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_2) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(sink_dsm_DLR_2_5)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_5) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_2) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(sink_dsm_DLR_0_0)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_0) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_0) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(sink_dsm_DLR_0_1)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_1) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(sink_dsm_DLR_1_2)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_2) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(sink_dsm_DLR_1_3)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_3) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_3) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_3) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(sink_dsm_DLR_2_4)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_4) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_4) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_4) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_2) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(sink_dsm_DLR_2_5)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_5) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_5) ++1 SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_5) +-0.5 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_2) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_overall_dsm_maximum(sink_dsm_DLR_0)_: ++1 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) +<= 1000 + +c_u_SinkDSMDLRInvestmentBlock_overall_dsm_maximum(sink_dsm_DLR_1)_: ++1 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_1) +<= 1000 + +c_u_SinkDSMDLRInvestmentBlock_overall_dsm_maximum(sink_dsm_DLR_2)_: ++1 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_2) +<= 1000 + +c_l_SinkDSMDLRInvestmentBlock_overall_minimum(sink_dsm_DLR)_: ++1 SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_2) +>= 200 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(Bus1_sink_dsm_DLR_0_0) <= +inf + 0 <= flow(Bus1_sink_dsm_DLR_0_1) <= +inf + 0 <= flow(Bus1_sink_dsm_DLR_1_2) <= +inf + 0 <= flow(Bus1_sink_dsm_DLR_1_3) <= +inf + 0 <= flow(Bus1_sink_dsm_DLR_2_4) <= +inf + 0 <= flow(Bus1_sink_dsm_DLR_2_5) <= +inf + 0 <= flow(Source_Bus1_0_0) <= +inf + 0 <= flow(Source_Bus1_0_1) <= +inf + 0 <= flow(Source_Bus1_1_2) <= +inf + 0 <= flow(Source_Bus1_1_3) <= +inf + 0 <= flow(Source_Bus1_2_4) <= +inf + 0 <= flow(Source_Bus1_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Source_Bus1_2) <= +inf + 33 <= SinkDSMDLRInvestmentBlock_invest(sink_dsm_DLR_0) <= 100 + 33 <= SinkDSMDLRInvestmentBlock_invest(sink_dsm_DLR_1) <= 100 + 33 <= SinkDSMDLRInvestmentBlock_invest(sink_dsm_DLR_2) <= 100 + 0 <= SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_total(sink_dsm_DLR_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_old(sink_dsm_DLR_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_old(sink_dsm_DLR_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_old(sink_dsm_DLR_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_old_end(sink_dsm_DLR_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_old_end(sink_dsm_DLR_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_old_end(sink_dsm_DLR_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_old_exo(sink_dsm_DLR_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_old_exo(sink_dsm_DLR_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_old_exo(sink_dsm_DLR_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_3) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_4) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(sink_dsm_DLR_1_5) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_3) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_4) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shed(sink_dsm_DLR_5) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_3) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_4) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(sink_dsm_DLR_1_5) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_3) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_4) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(sink_dsm_DLR_1_5) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_3) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_4) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(sink_dsm_DLR_1_5) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_3) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_4) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_level(sink_dsm_DLR_5) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_3) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_4) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up_level(sink_dsm_DLR_5) <= +inf +end diff --git a/tests/lp_files/periodical_investment_limit_with_dsm_oemof.lp b/tests/lp_files/periodical_investment_limit_with_dsm_oemof.lp new file mode 100644 index 000000000..2888006eb --- /dev/null +++ b/tests/lp_files/periodical_investment_limit_with_dsm_oemof.lp @@ -0,0 +1,463 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++285.39374551281168 InvestmentFlowBlock_invest(Source_Bus1_0) ++279.7977897184428 InvestmentFlowBlock_invest(Source_Bus1_1) ++274.31155854749295 InvestmentFlowBlock_invest(Source_Bus1_2) ++0.5 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_0) ++0.5 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_1) ++0.49019607843137253 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_2) ++0.49019607843137253 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_3) ++0.48058439061899266 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_4) ++0.48058439061899266 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_5) ++0.5 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_0) ++0.5 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_1) ++0.49019607843137253 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_2) ++0.49019607843137253 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_3) ++0.48058439061899266 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_4) ++0.48058439061899266 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_5) ++103.00990099009903 SinkDSMOemofInvestmentBlock_invest(sink_dsm_oemof_0) ++100.990099009901 SinkDSMOemofInvestmentBlock_invest(sink_dsm_oemof_1) ++99.009900990099013 SinkDSMOemofInvestmentBlock_invest(sink_dsm_oemof_2) + +s.t. + +c_u_investment_limit_per_period(0)_: ++285.39374551281168 InvestmentFlowBlock_invest(Source_Bus1_0) ++103.00990099009903 SinkDSMOemofInvestmentBlock_invest(sink_dsm_oemof_0) +<= 400 + +c_u_investment_limit_per_period(1)_: ++279.7977897184428 InvestmentFlowBlock_invest(Source_Bus1_1) ++100.990099009901 SinkDSMOemofInvestmentBlock_invest(sink_dsm_oemof_1) +<= 300 + +c_u_investment_limit_per_period(2)_: ++274.31155854749295 InvestmentFlowBlock_invest(Source_Bus1_2) ++99.009900990099013 SinkDSMOemofInvestmentBlock_invest(sink_dsm_oemof_2) +<= 200 + +c_e_BusBlock_balance(Bus1_0_0)_: +-1 flow(Bus1_sink_dsm_oemof_0_0) ++1 flow(Source_Bus1_0_0) += 0 + +c_e_BusBlock_balance(Bus1_0_1)_: +-1 flow(Bus1_sink_dsm_oemof_0_1) ++1 flow(Source_Bus1_0_1) += 0 + +c_e_BusBlock_balance(Bus1_1_2)_: +-1 flow(Bus1_sink_dsm_oemof_1_2) ++1 flow(Source_Bus1_1_2) += 0 + +c_e_BusBlock_balance(Bus1_1_3)_: +-1 flow(Bus1_sink_dsm_oemof_1_3) ++1 flow(Source_Bus1_1_3) += 0 + +c_e_BusBlock_balance(Bus1_2_4)_: +-1 flow(Bus1_sink_dsm_oemof_2_4) ++1 flow(Source_Bus1_2_4) += 0 + +c_e_BusBlock_balance(Bus1_2_5)_: +-1 flow(Bus1_sink_dsm_oemof_2_5) ++1 flow(Source_Bus1_2_5) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_0)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_0) ++1 InvestmentFlowBlock_total(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_1)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_1) ++1 InvestmentFlowBlock_old(Source_Bus1_1) +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 InvestmentFlowBlock_total(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(Source_Bus1_2)_: +-1 InvestmentFlowBlock_invest(Source_Bus1_2) ++1 InvestmentFlowBlock_old(Source_Bus1_2) +-1 InvestmentFlowBlock_total(Source_Bus1_1) ++1 InvestmentFlowBlock_total(Source_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Source_Bus1_0)_: ++1 InvestmentFlowBlock_old_end(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Source_Bus1_1)_: ++1 InvestmentFlowBlock_old_end(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(Source_Bus1_2)_: ++1 InvestmentFlowBlock_old_end(Source_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Source_Bus1_0)_: ++1 InvestmentFlowBlock_old_exo(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Source_Bus1_1)_: ++1 InvestmentFlowBlock_old_exo(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(Source_Bus1_2)_: ++1 InvestmentFlowBlock_old_exo(Source_Bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(Source_Bus1_0)_: ++1 InvestmentFlowBlock_old(Source_Bus1_0) +-1 InvestmentFlowBlock_old_end(Source_Bus1_0) +-1 InvestmentFlowBlock_old_exo(Source_Bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(Source_Bus1_1)_: ++1 InvestmentFlowBlock_old(Source_Bus1_1) +-1 InvestmentFlowBlock_old_end(Source_Bus1_1) +-1 InvestmentFlowBlock_old_exo(Source_Bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(Source_Bus1_2)_: ++1 InvestmentFlowBlock_old(Source_Bus1_2) +-1 InvestmentFlowBlock_old_end(Source_Bus1_2) +-1 InvestmentFlowBlock_old_exo(Source_Bus1_2) += 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0_0)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_0_1)_: +-1 InvestmentFlowBlock_total(Source_Bus1_0) ++1 flow(Source_Bus1_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_1_2)_: +-1 InvestmentFlowBlock_total(Source_Bus1_1) ++1 flow(Source_Bus1_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_1_3)_: +-1 InvestmentFlowBlock_total(Source_Bus1_1) ++1 flow(Source_Bus1_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_2_4)_: +-1 InvestmentFlowBlock_total(Source_Bus1_2) ++1 flow(Source_Bus1_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(Source_Bus1_2_5)_: +-1 InvestmentFlowBlock_total(Source_Bus1_2) ++1 flow(Source_Bus1_2_5) +<= 0 + +c_e_SinkDSMOemofInvestmentBlock_total_dsm_rule(sink_dsm_oemof_0)_: +-1 SinkDSMOemofInvestmentBlock_invest(sink_dsm_oemof_0) ++1 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) += 50 + +c_e_SinkDSMOemofInvestmentBlock_total_dsm_rule(sink_dsm_oemof_1)_: +-1 SinkDSMOemofInvestmentBlock_invest(sink_dsm_oemof_1) ++1 SinkDSMOemofInvestmentBlock_old(sink_dsm_oemof_1) +-1 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) ++1 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_1) += 0 + +c_e_SinkDSMOemofInvestmentBlock_total_dsm_rule(sink_dsm_oemof_2)_: +-1 SinkDSMOemofInvestmentBlock_invest(sink_dsm_oemof_2) ++1 SinkDSMOemofInvestmentBlock_old(sink_dsm_oemof_2) +-1 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_1) ++1 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_2) += 0 + +c_e_SinkDSMOemofInvestmentBlock_old_dsm_rule_end(sink_dsm_oemof_0)_: ++1 SinkDSMOemofInvestmentBlock_old_end(sink_dsm_oemof_0) += 0 + +c_e_SinkDSMOemofInvestmentBlock_old_dsm_rule_end(sink_dsm_oemof_1)_: ++1 SinkDSMOemofInvestmentBlock_old_end(sink_dsm_oemof_1) += 0 + +c_e_SinkDSMOemofInvestmentBlock_old_dsm_rule_end(sink_dsm_oemof_2)_: +-1 SinkDSMOemofInvestmentBlock_invest(sink_dsm_oemof_0) ++1 SinkDSMOemofInvestmentBlock_old_end(sink_dsm_oemof_2) += 0 + +c_e_SinkDSMOemofInvestmentBlock_old_dsm_rule_exo(sink_dsm_oemof_0)_: ++1 SinkDSMOemofInvestmentBlock_old_exo(sink_dsm_oemof_0) += 0 + +c_e_SinkDSMOemofInvestmentBlock_old_dsm_rule_exo(sink_dsm_oemof_1)_: ++1 SinkDSMOemofInvestmentBlock_old_exo(sink_dsm_oemof_1) += 50 + +c_e_SinkDSMOemofInvestmentBlock_old_dsm_rule_exo(sink_dsm_oemof_2)_: ++1 SinkDSMOemofInvestmentBlock_old_exo(sink_dsm_oemof_2) += 0 + +c_e_SinkDSMOemofInvestmentBlock_old_dsm_rule(sink_dsm_oemof_0)_: ++1 SinkDSMOemofInvestmentBlock_old(sink_dsm_oemof_0) +-1 SinkDSMOemofInvestmentBlock_old_end(sink_dsm_oemof_0) +-1 SinkDSMOemofInvestmentBlock_old_exo(sink_dsm_oemof_0) += 0 + +c_e_SinkDSMOemofInvestmentBlock_old_dsm_rule(sink_dsm_oemof_1)_: ++1 SinkDSMOemofInvestmentBlock_old(sink_dsm_oemof_1) +-1 SinkDSMOemofInvestmentBlock_old_end(sink_dsm_oemof_1) +-1 SinkDSMOemofInvestmentBlock_old_exo(sink_dsm_oemof_1) += 0 + +c_e_SinkDSMOemofInvestmentBlock_old_dsm_rule(sink_dsm_oemof_2)_: ++1 SinkDSMOemofInvestmentBlock_old(sink_dsm_oemof_2) +-1 SinkDSMOemofInvestmentBlock_old_end(sink_dsm_oemof_2) +-1 SinkDSMOemofInvestmentBlock_old_exo(sink_dsm_oemof_2) += 0 + +c_e_SinkDSMOemofInvestmentBlock_shift_shed_vars(sink_dsm_oemof_0)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_0) += 0 + +c_e_SinkDSMOemofInvestmentBlock_shift_shed_vars(sink_dsm_oemof_1)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_1) += 0 + +c_e_SinkDSMOemofInvestmentBlock_shift_shed_vars(sink_dsm_oemof_2)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_2) += 0 + +c_e_SinkDSMOemofInvestmentBlock_shift_shed_vars(sink_dsm_oemof_3)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_3) += 0 + +c_e_SinkDSMOemofInvestmentBlock_shift_shed_vars(sink_dsm_oemof_4)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_4) += 0 + +c_e_SinkDSMOemofInvestmentBlock_shift_shed_vars(sink_dsm_oemof_5)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_5) += 0 + +c_e_SinkDSMOemofInvestmentBlock_input_output_relation(sink_dsm_oemof_0_0)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_0) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_0) +-1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_0) +-1 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) ++1 flow(Bus1_sink_dsm_oemof_0_0) += 0 + +c_e_SinkDSMOemofInvestmentBlock_input_output_relation(sink_dsm_oemof_0_1)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_1) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_1) +-1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_1) +-1 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) ++1 flow(Bus1_sink_dsm_oemof_0_1) += 0 + +c_e_SinkDSMOemofInvestmentBlock_input_output_relation(sink_dsm_oemof_1_2)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_2) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_2) +-1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_2) +-1 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_1) ++1 flow(Bus1_sink_dsm_oemof_1_2) += 0 + +c_e_SinkDSMOemofInvestmentBlock_input_output_relation(sink_dsm_oemof_1_3)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_3) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_3) +-1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_3) +-1 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_1) ++1 flow(Bus1_sink_dsm_oemof_1_3) += 0 + +c_e_SinkDSMOemofInvestmentBlock_input_output_relation(sink_dsm_oemof_2_4)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_4) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_4) +-1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_4) +-1 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_2) ++1 flow(Bus1_sink_dsm_oemof_2_4) += 0 + +c_e_SinkDSMOemofInvestmentBlock_input_output_relation(sink_dsm_oemof_2_5)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_5) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_5) +-1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_5) +-1 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_2) ++1 flow(Bus1_sink_dsm_oemof_2_5) += 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(sink_dsm_oemof_0_0)_: ++1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_0) +-0.5 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(sink_dsm_oemof_0_1)_: ++1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_1) +-0.5 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(sink_dsm_oemof_1_2)_: ++1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_2) +-0.5 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_1) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(sink_dsm_oemof_1_3)_: ++1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_3) +-0.5 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_1) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(sink_dsm_oemof_2_4)_: ++1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_4) +-0.5 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_2) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(sink_dsm_oemof_2_5)_: ++1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_5) +-0.5 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_2) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(sink_dsm_oemof_0_0)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_0) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_0) +-0.5 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(sink_dsm_oemof_0_1)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_1) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_1) +-0.5 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(sink_dsm_oemof_1_2)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_2) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_2) +-0.5 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_1) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(sink_dsm_oemof_1_3)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_3) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_3) +-0.5 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_1) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(sink_dsm_oemof_2_4)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_4) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_4) +-0.5 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_2) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(sink_dsm_oemof_2_5)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_5) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_5) +-0.5 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_2) +<= 0 + +c_e_SinkDSMOemofInvestmentBlock_dsm_sum_constraint(sink_dsm_oemof_0)_: +-1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_0) +-1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_1) ++1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_0) ++1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_1) += 0 + +c_e_SinkDSMOemofInvestmentBlock_dsm_sum_constraint(sink_dsm_oemof_2)_: +-1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_2) +-1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_3) ++1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_2) ++1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_3) += 0 + +c_e_SinkDSMOemofInvestmentBlock_dsm_sum_constraint(sink_dsm_oemof_4)_: +-1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_4) +-1 SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_5) ++1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_4) ++1 SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_5) += 0 + +c_u_SinkDSMOemofInvestmentBlock_overall_dsm_maximum(sink_dsm_oemof_0)_: ++1 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) +<= 1000 + +c_u_SinkDSMOemofInvestmentBlock_overall_dsm_maximum(sink_dsm_oemof_1)_: ++1 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_1) +<= 1000 + +c_u_SinkDSMOemofInvestmentBlock_overall_dsm_maximum(sink_dsm_oemof_2)_: ++1 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_2) +<= 1000 + +c_l_SinkDSMOemofInvestmentBlock_overall_minimum(sink_dsm_oemof)_: ++1 SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_2) +>= 200 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(Bus1_sink_dsm_oemof_0_0) <= +inf + 0 <= flow(Bus1_sink_dsm_oemof_0_1) <= +inf + 0 <= flow(Bus1_sink_dsm_oemof_1_2) <= +inf + 0 <= flow(Bus1_sink_dsm_oemof_1_3) <= +inf + 0 <= flow(Bus1_sink_dsm_oemof_2_4) <= +inf + 0 <= flow(Bus1_sink_dsm_oemof_2_5) <= +inf + 0 <= flow(Source_Bus1_0_0) <= +inf + 0 <= flow(Source_Bus1_0_1) <= +inf + 0 <= flow(Source_Bus1_1_2) <= +inf + 0 <= flow(Source_Bus1_1_3) <= +inf + 0 <= flow(Source_Bus1_2_4) <= +inf + 0 <= flow(Source_Bus1_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_invest(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_total(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(Source_Bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Source_Bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Source_Bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(Source_Bus1_2) <= +inf + 33 <= SinkDSMOemofInvestmentBlock_invest(sink_dsm_oemof_0) <= 100 + 33 <= SinkDSMOemofInvestmentBlock_invest(sink_dsm_oemof_1) <= 100 + 33 <= SinkDSMOemofInvestmentBlock_invest(sink_dsm_oemof_2) <= 100 + 0 <= SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_0) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_1) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_total(sink_dsm_oemof_2) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_old(sink_dsm_oemof_0) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_old(sink_dsm_oemof_1) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_old(sink_dsm_oemof_2) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_old_end(sink_dsm_oemof_0) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_old_end(sink_dsm_oemof_1) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_old_end(sink_dsm_oemof_2) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_old_exo(sink_dsm_oemof_0) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_old_exo(sink_dsm_oemof_1) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_old_exo(sink_dsm_oemof_2) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_0) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_1) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_2) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_3) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_4) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shift(sink_dsm_oemof_5) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_0) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_1) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_2) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_3) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_4) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shed(sink_dsm_oemof_5) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_0) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_1) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_2) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_3) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_4) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_up(sink_dsm_oemof_5) <= +inf +end diff --git a/tests/lp_files/piecewise_linear_transformer_cc_multi_period.lp b/tests/lp_files/piecewise_linear_transformer_cc_multi_period.lp new file mode 100644 index 000000000..a0d1c62e3 --- /dev/null +++ b/tests/lp_files/piecewise_linear_transformer_cc_multi_period.lp @@ -0,0 +1,583 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++1 flow(gasBus_pwltf_0_0) ++1 flow(gasBus_pwltf_0_1) ++0.98039215686274506 flow(gasBus_pwltf_1_2) ++0.98039215686274506 flow(gasBus_pwltf_1_3) ++0.96116878123798533 flow(gasBus_pwltf_2_4) ++0.96116878123798533 flow(gasBus_pwltf_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(pwltf_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(pwltf_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(pwltf_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(pwltf_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(pwltf_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(pwltf_electricityBus_2_5) += 0 + +c_e_BusBlock_balance(gasBus_0_0)_: ++1 flow(gasBus_pwltf_0_0) += 0 + +c_e_BusBlock_balance(gasBus_0_1)_: ++1 flow(gasBus_pwltf_0_1) += 0 + +c_e_BusBlock_balance(gasBus_1_2)_: ++1 flow(gasBus_pwltf_1_2) += 0 + +c_e_BusBlock_balance(gasBus_1_3)_: ++1 flow(gasBus_pwltf_1_3) += 0 + +c_e_BusBlock_balance(gasBus_2_4)_: ++1 flow(gasBus_pwltf_2_4) += 0 + +c_e_BusBlock_balance(gasBus_2_5)_: ++1 flow(gasBus_pwltf_2_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_0_0)_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_0) +-1 flow(gasBus_pwltf_0_0) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_0_1)_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_1) +-1 flow(gasBus_pwltf_0_1) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_1_2)_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_2) +-1 flow(gasBus_pwltf_1_2) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_1_3)_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_3) +-1 flow(gasBus_pwltf_1_3) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_2_4)_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_4) +-1 flow(gasBus_pwltf_2_4) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_2_5)_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_5) +-1 flow(gasBus_pwltf_2_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_0_0)_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_0) +-1 flow(pwltf_electricityBus_0_0) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_0_1)_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_1) +-1 flow(pwltf_electricityBus_0_1) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_1_2)_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_2) +-1 flow(pwltf_electricityBus_1_2) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_1_3)_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_3) +-1 flow(pwltf_electricityBus_1_3) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_2_4)_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_4) +-1 flow(pwltf_electricityBus_2_4) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_2_5)_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_5) +-1 flow(pwltf_electricityBus_2_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_constraint1_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_0) +-25 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(2) +-50 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(3) +-75 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(4) +-100 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_constraint2_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_0) +-625 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(2) +-2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(3) +-5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(4) +-10000 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_constraint3_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(4) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(5) += 1 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_constraint4(1)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(1) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_constraint4(2)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(1) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(2) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_constraint4(3)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(2) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(3) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_constraint4(4)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(3) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(4) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(4) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_constraint4(5)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(4) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(5) +<= 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_constraint5_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(4) += 1 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_constraint1_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_1) +-25 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(2) +-50 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(3) +-75 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(4) +-100 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_constraint2_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_1) +-625 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(2) +-2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(3) +-5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(4) +-10000 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_constraint3_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(4) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(5) += 1 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_constraint4(1)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(1) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_constraint4(2)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(1) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(2) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_constraint4(3)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(2) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(3) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_constraint4(4)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(3) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(4) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(4) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_constraint4(5)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(4) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(5) +<= 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_constraint5_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(4) += 1 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_constraint1_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_2) +-25 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(2) +-50 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(3) +-75 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(4) +-100 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_constraint2_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_2) +-625 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(2) +-2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(3) +-5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(4) +-10000 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_constraint3_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(4) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(5) += 1 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_constraint4(1)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(1) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_constraint4(2)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(1) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(2) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_constraint4(3)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(2) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(3) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_constraint4(4)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(3) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(4) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(4) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_constraint4(5)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(4) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(5) +<= 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_constraint5_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(4) += 1 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_constraint1_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_3) +-25 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(2) +-50 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(3) +-75 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(4) +-100 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_constraint2_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_3) +-625 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(2) +-2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(3) +-5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(4) +-10000 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_constraint3_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(4) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(5) += 1 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_constraint4(1)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(1) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_constraint4(2)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(1) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(2) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_constraint4(3)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(2) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(3) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_constraint4(4)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(3) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(4) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(4) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_constraint4(5)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(4) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(5) +<= 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_constraint5_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(4) += 1 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_constraint1_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_4) +-25 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(2) +-50 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(3) +-75 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(4) +-100 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_constraint2_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_4) +-625 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(2) +-2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(3) +-5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(4) +-10000 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_constraint3_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(4) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(5) += 1 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_constraint4(1)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(1) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_constraint4(2)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(1) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(2) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_constraint4(3)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(2) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(3) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_constraint4(4)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(3) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(4) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(4) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_constraint4(5)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(4) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(5) +<= 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_constraint5_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(4) += 1 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_constraint1_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_5) +-25 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(2) +-50 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(3) +-75 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(4) +-100 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_constraint2_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_5) +-625 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(2) +-2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(3) +-5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(4) +-10000 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_constraint3_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(4) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(5) += 1 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_constraint4(1)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(1) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_constraint4(2)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(1) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(2) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_constraint4(3)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(2) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(3) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_constraint4(4)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(3) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(4) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(4) +<= 0 + +c_u_PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_constraint4(5)_: +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(4) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(5) +<= 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_constraint5_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(4) += 1 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(gasBus_pwltf_0_0) <= 100 + 0 <= flow(gasBus_pwltf_0_1) <= 100 + 0 <= flow(gasBus_pwltf_1_2) <= 100 + 0 <= flow(gasBus_pwltf_1_3) <= 100 + 0 <= flow(gasBus_pwltf_2_4) <= 100 + 0 <= flow(gasBus_pwltf_2_5) <= 100 + 0 <= flow(pwltf_electricityBus_0_0) <= +inf + 0 <= flow(pwltf_electricityBus_0_1) <= +inf + 0 <= flow(pwltf_electricityBus_1_2) <= +inf + 0 <= flow(pwltf_electricityBus_1_3) <= +inf + 0 <= flow(pwltf_electricityBus_2_4) <= +inf + 0 <= flow(pwltf_electricityBus_2_5) <= +inf + 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_0) <= 100 + 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_1) <= 100 + 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_2) <= 100 + 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_3) <= 100 + 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_4) <= 100 + 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_5) <= 100 + 0 <= PiecewiseLinearTransformerBlock_outflow(pwltf_0) <= 10000 + 0 <= PiecewiseLinearTransformerBlock_outflow(pwltf_1) <= 10000 + 0 <= PiecewiseLinearTransformerBlock_outflow(pwltf_2) <= 10000 + 0 <= PiecewiseLinearTransformerBlock_outflow(pwltf_3) <= 10000 + 0 <= PiecewiseLinearTransformerBlock_outflow(pwltf_4) <= 10000 + 0 <= PiecewiseLinearTransformerBlock_outflow(pwltf_5) <= 10000 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(1) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(2) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(3) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(4) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_lambda(5) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(1) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(2) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(3) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(4) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(1) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(2) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(3) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(4) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_lambda(5) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(1) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(2) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(3) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(4) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(1) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(2) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(3) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(4) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_lambda(5) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(1) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(2) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(3) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(4) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(1) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(2) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(3) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(4) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_lambda(5) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(1) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(2) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(3) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(4) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(1) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(2) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(3) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(4) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_lambda(5) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(1) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(2) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(3) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(4) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(1) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(2) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(3) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(4) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_lambda(5) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(1) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(2) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(3) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(4) <= 1 +binary + PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(1) + PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(2) + PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(3) + PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_CC_bin_y(4) + PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(1) + PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(2) + PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(3) + PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_CC_bin_y(4) + PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(1) + PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(2) + PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(3) + PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_CC_bin_y(4) + PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(1) + PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(2) + PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(3) + PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_CC_bin_y(4) + PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(1) + PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(2) + PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(3) + PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_CC_bin_y(4) + PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(1) + PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(2) + PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(3) + PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_CC_bin_y(4) +end diff --git a/tests/lp_files/piecewise_linear_transformer_dcc_multi_period.lp b/tests/lp_files/piecewise_linear_transformer_dcc_multi_period.lp new file mode 100644 index 000000000..0f56eab0b --- /dev/null +++ b/tests/lp_files/piecewise_linear_transformer_dcc_multi_period.lp @@ -0,0 +1,565 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++1 flow(gasBus_pwltf_0_0) ++1 flow(gasBus_pwltf_0_1) ++0.98039215686274506 flow(gasBus_pwltf_1_2) ++0.98039215686274506 flow(gasBus_pwltf_1_3) ++0.96116878123798533 flow(gasBus_pwltf_2_4) ++0.96116878123798533 flow(gasBus_pwltf_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(pwltf_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(pwltf_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(pwltf_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(pwltf_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(pwltf_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(pwltf_electricityBus_2_5) += 0 + +c_e_BusBlock_balance(gasBus_0_0)_: ++1 flow(gasBus_pwltf_0_0) += 0 + +c_e_BusBlock_balance(gasBus_0_1)_: ++1 flow(gasBus_pwltf_0_1) += 0 + +c_e_BusBlock_balance(gasBus_1_2)_: ++1 flow(gasBus_pwltf_1_2) += 0 + +c_e_BusBlock_balance(gasBus_1_3)_: ++1 flow(gasBus_pwltf_1_3) += 0 + +c_e_BusBlock_balance(gasBus_2_4)_: ++1 flow(gasBus_pwltf_2_4) += 0 + +c_e_BusBlock_balance(gasBus_2_5)_: ++1 flow(gasBus_pwltf_2_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_0_0)_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_0) +-1 flow(gasBus_pwltf_0_0) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_0_1)_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_1) +-1 flow(gasBus_pwltf_0_1) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_1_2)_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_2) +-1 flow(gasBus_pwltf_1_2) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_1_3)_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_3) +-1 flow(gasBus_pwltf_1_3) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_2_4)_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_4) +-1 flow(gasBus_pwltf_2_4) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_in(pwltf_2_5)_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_5) +-1 flow(gasBus_pwltf_2_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_0_0)_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_0) +-1 flow(pwltf_electricityBus_0_0) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_0_1)_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_1) +-1 flow(pwltf_electricityBus_0_1) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_1_2)_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_2) +-1 flow(pwltf_electricityBus_1_2) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_1_3)_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_3) +-1 flow(pwltf_electricityBus_1_3) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_2_4)_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_4) +-1 flow(pwltf_electricityBus_2_4) += 0 + +c_e_PiecewiseLinearTransformerBlock_equate_out(pwltf_2_5)_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_5) +-1 flow(pwltf_electricityBus_2_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_constraint1_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_0) +-25 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(1_2) +-25 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(2_2) +-50 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(2_3) +-50 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(3_3) +-75 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(3_4) +-75 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(4_4) +-100 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_constraint2_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_0) +-625 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(1_2) +-625 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(2_2) +-2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(2_3) +-2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(3_3) +-5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(3_4) +-5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(4_4) +-10000 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_constraint3(1)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(1) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(1_1) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(1_2) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_constraint3(2)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(2) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(2_2) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(2_3) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_constraint3(3)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(3) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(3_3) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(3_4) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_constraint3(4)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(4) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(4_4) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_constraint4_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(4) += 1 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_constraint1_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_1) +-25 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(1_2) +-25 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(2_2) +-50 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(2_3) +-50 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(3_3) +-75 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(3_4) +-75 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(4_4) +-100 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_constraint2_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_1) +-625 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(1_2) +-625 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(2_2) +-2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(2_3) +-2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(3_3) +-5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(3_4) +-5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(4_4) +-10000 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_constraint3(1)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(1) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(1_1) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(1_2) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_constraint3(2)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(2) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(2_2) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(2_3) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_constraint3(3)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(3) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(3_3) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(3_4) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_constraint3(4)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(4) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(4_4) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_constraint4_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(4) += 1 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_constraint1_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_2) +-25 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(1_2) +-25 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(2_2) +-50 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(2_3) +-50 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(3_3) +-75 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(3_4) +-75 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(4_4) +-100 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_constraint2_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_2) +-625 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(1_2) +-625 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(2_2) +-2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(2_3) +-2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(3_3) +-5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(3_4) +-5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(4_4) +-10000 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_constraint3(1)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(1) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(1_1) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(1_2) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_constraint3(2)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(2) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(2_2) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(2_3) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_constraint3(3)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(3) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(3_3) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(3_4) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_constraint3(4)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(4) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(4_4) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_constraint4_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(4) += 1 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_constraint1_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_3) +-25 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(1_2) +-25 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(2_2) +-50 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(2_3) +-50 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(3_3) +-75 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(3_4) +-75 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(4_4) +-100 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_constraint2_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_3) +-625 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(1_2) +-625 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(2_2) +-2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(2_3) +-2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(3_3) +-5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(3_4) +-5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(4_4) +-10000 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_constraint3(1)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_bin_y(1) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(1_1) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(1_2) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_constraint3(2)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_bin_y(2) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(2_2) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(2_3) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_constraint3(3)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_bin_y(3) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(3_3) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(3_4) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_constraint3(4)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_bin_y(4) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(4_4) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_constraint4_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_bin_y(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_bin_y(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_bin_y(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_bin_y(4) += 1 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_constraint1_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_4) +-25 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(1_2) +-25 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(2_2) +-50 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(2_3) +-50 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(3_3) +-75 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(3_4) +-75 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(4_4) +-100 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_constraint2_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_4) +-625 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(1_2) +-625 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(2_2) +-2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(2_3) +-2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(3_3) +-5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(3_4) +-5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(4_4) +-10000 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_constraint3(1)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_bin_y(1) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(1_1) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(1_2) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_constraint3(2)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_bin_y(2) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(2_2) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(2_3) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_constraint3(3)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_bin_y(3) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(3_3) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(3_4) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_constraint3(4)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_bin_y(4) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(4_4) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_constraint4_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_bin_y(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_bin_y(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_bin_y(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_bin_y(4) += 1 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_constraint1_: ++1 PiecewiseLinearTransformerBlock_inflow(pwltf_5) +-25 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(1_2) +-25 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(2_2) +-50 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(2_3) +-50 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(3_3) +-75 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(3_4) +-75 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(4_4) +-100 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_constraint2_: ++1 PiecewiseLinearTransformerBlock_outflow(pwltf_5) +-625 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(1_2) +-625 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(2_2) +-2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(2_3) +-2500 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(3_3) +-5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(3_4) +-5625 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(4_4) +-10000 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_constraint3(1)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_bin_y(1) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(1_1) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(1_2) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_constraint3(2)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_bin_y(2) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(2_2) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(2_3) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_constraint3(3)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_bin_y(3) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(3_3) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(3_4) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_constraint3(4)_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_bin_y(4) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(4_4) +-1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(4_5) += 0 + +c_e_PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_constraint4_: ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_bin_y(1) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_bin_y(2) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_bin_y(3) ++1 PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_bin_y(4) += 1 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(gasBus_pwltf_0_0) <= 100 + 0 <= flow(gasBus_pwltf_0_1) <= 100 + 0 <= flow(gasBus_pwltf_1_2) <= 100 + 0 <= flow(gasBus_pwltf_1_3) <= 100 + 0 <= flow(gasBus_pwltf_2_4) <= 100 + 0 <= flow(gasBus_pwltf_2_5) <= 100 + 0 <= flow(pwltf_electricityBus_0_0) <= +inf + 0 <= flow(pwltf_electricityBus_0_1) <= +inf + 0 <= flow(pwltf_electricityBus_1_2) <= +inf + 0 <= flow(pwltf_electricityBus_1_3) <= +inf + 0 <= flow(pwltf_electricityBus_2_4) <= +inf + 0 <= flow(pwltf_electricityBus_2_5) <= +inf + 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_0) <= 100 + 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_1) <= 100 + 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_2) <= 100 + 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_3) <= 100 + 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_4) <= 100 + 0 <= PiecewiseLinearTransformerBlock_inflow(pwltf_5) <= 100 + 0 <= PiecewiseLinearTransformerBlock_outflow(pwltf_0) <= 10000 + 0 <= PiecewiseLinearTransformerBlock_outflow(pwltf_1) <= 10000 + 0 <= PiecewiseLinearTransformerBlock_outflow(pwltf_2) <= 10000 + 0 <= PiecewiseLinearTransformerBlock_outflow(pwltf_3) <= 10000 + 0 <= PiecewiseLinearTransformerBlock_outflow(pwltf_4) <= 10000 + 0 <= PiecewiseLinearTransformerBlock_outflow(pwltf_5) <= 10000 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(1_1) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(1_2) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(2_2) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(2_3) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(3_3) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(3_4) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(4_4) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_lambda(4_5) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(1) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(2) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(3) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(4) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(1_1) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(1_2) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(2_2) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(2_3) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(3_3) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(3_4) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(4_4) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_lambda(4_5) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(1) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(2) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(3) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(4) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(1_1) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(1_2) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(2_2) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(2_3) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(3_3) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(3_4) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(4_4) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_lambda(4_5) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(1) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(2) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(3) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(4) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(1_1) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(1_2) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(2_2) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(2_3) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(3_3) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(3_4) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(4_4) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_lambda(4_5) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_bin_y(1) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_bin_y(2) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_bin_y(3) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_bin_y(4) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(1_1) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(1_2) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(2_2) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(2_3) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(3_3) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(3_4) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(4_4) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_lambda(4_5) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_bin_y(1) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_bin_y(2) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_bin_y(3) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_bin_y(4) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(1_1) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(1_2) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(2_2) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(2_3) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(3_3) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(3_4) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(4_4) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_lambda(4_5) <= +inf + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_bin_y(1) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_bin_y(2) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_bin_y(3) <= 1 + 0 <= PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_bin_y(4) <= 1 +binary + PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(1) + PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(2) + PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(3) + PiecewiseLinearTransformerBlock_piecewise(pwltf_0)_DCC_bin_y(4) + PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(1) + PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(2) + PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(3) + PiecewiseLinearTransformerBlock_piecewise(pwltf_1)_DCC_bin_y(4) + PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(1) + PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(2) + PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(3) + PiecewiseLinearTransformerBlock_piecewise(pwltf_2)_DCC_bin_y(4) + PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_bin_y(1) + PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_bin_y(2) + PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_bin_y(3) + PiecewiseLinearTransformerBlock_piecewise(pwltf_3)_DCC_bin_y(4) + PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_bin_y(1) + PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_bin_y(2) + PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_bin_y(3) + PiecewiseLinearTransformerBlock_piecewise(pwltf_4)_DCC_bin_y(4) + PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_bin_y(1) + PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_bin_y(2) + PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_bin_y(3) + PiecewiseLinearTransformerBlock_piecewise(pwltf_5)_DCC_bin_y(4) +end diff --git a/tests/lp_files/shared_limit_multi_period.lp b/tests/lp_files/shared_limit_multi_period.lp new file mode 100644 index 000000000..39fab6783 --- /dev/null +++ b/tests/lp_files/shared_limit_multi_period.lp @@ -0,0 +1,229 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++0 ONE_VAR_CONSTANT + +s.t. + +c_e_limit_storage_constraint(0)_: ++0.5 GenericStorageBlock_storage_content(storage1_0) ++1.25 GenericStorageBlock_storage_content(storage2_0) +-1 limit_storage(0) += 0 + +c_e_limit_storage_constraint(1)_: ++0.5 GenericStorageBlock_storage_content(storage1_1) ++1.25 GenericStorageBlock_storage_content(storage2_1) +-1 limit_storage(1) += 0 + +c_e_limit_storage_constraint(2)_: ++0.5 GenericStorageBlock_storage_content(storage1_2) ++1.25 GenericStorageBlock_storage_content(storage2_2) +-1 limit_storage(2) += 0 + +c_e_limit_storage_constraint(3)_: ++0.5 GenericStorageBlock_storage_content(storage1_3) ++1.25 GenericStorageBlock_storage_content(storage2_3) +-1 limit_storage(3) += 0 + +c_e_limit_storage_constraint(4)_: ++0.5 GenericStorageBlock_storage_content(storage1_4) ++1.25 GenericStorageBlock_storage_content(storage2_4) +-1 limit_storage(4) += 0 + +c_e_limit_storage_constraint(5)_: ++0.5 GenericStorageBlock_storage_content(storage1_5) ++1.25 GenericStorageBlock_storage_content(storage2_5) +-1 limit_storage(5) += 0 + +c_e_BusBlock_balance(bus_0_0)_: +-1 flow(bus_storage1_0_0) +-1 flow(bus_storage2_0_0) ++1 flow(storage1_bus_0_0) ++1 flow(storage2_bus_0_0) += 0 + +c_e_BusBlock_balance(bus_0_1)_: +-1 flow(bus_storage1_0_1) +-1 flow(bus_storage2_0_1) ++1 flow(storage1_bus_0_1) ++1 flow(storage2_bus_0_1) += 0 + +c_e_BusBlock_balance(bus_1_2)_: +-1 flow(bus_storage1_1_2) +-1 flow(bus_storage2_1_2) ++1 flow(storage1_bus_1_2) ++1 flow(storage2_bus_1_2) += 0 + +c_e_BusBlock_balance(bus_1_3)_: +-1 flow(bus_storage1_1_3) +-1 flow(bus_storage2_1_3) ++1 flow(storage1_bus_1_3) ++1 flow(storage2_bus_1_3) += 0 + +c_e_BusBlock_balance(bus_2_4)_: +-1 flow(bus_storage1_2_4) +-1 flow(bus_storage2_2_4) ++1 flow(storage1_bus_2_4) ++1 flow(storage2_bus_2_4) += 0 + +c_e_BusBlock_balance(bus_2_5)_: +-1 flow(bus_storage1_2_5) +-1 flow(bus_storage2_2_5) ++1 flow(storage1_bus_2_5) ++1 flow(storage2_bus_2_5) += 0 + +c_e_GenericStorageBlock_balance_first(storage1)_: +-1 GenericStorageBlock_init_content(storage1) ++1 GenericStorageBlock_storage_content(storage1_0) +-1 flow(bus_storage1_0_0) ++1 flow(storage1_bus_0_0) += 0 + +c_e_GenericStorageBlock_balance_first(storage2)_: +-1 GenericStorageBlock_init_content(storage2) ++1 GenericStorageBlock_storage_content(storage2_0) +-1 flow(bus_storage2_0_0) ++1 flow(storage2_bus_0_0) += 0 + +c_e_GenericStorageBlock_balance(storage1_0_1)_: +-1 GenericStorageBlock_storage_content(storage1_0) ++1 GenericStorageBlock_storage_content(storage1_1) +-1 flow(bus_storage1_0_1) ++1 flow(storage1_bus_0_1) += 0 + +c_e_GenericStorageBlock_balance(storage1_1_2)_: +-1 GenericStorageBlock_storage_content(storage1_1) ++1 GenericStorageBlock_storage_content(storage1_2) +-1 flow(bus_storage1_1_2) ++1 flow(storage1_bus_1_2) += 0 + +c_e_GenericStorageBlock_balance(storage1_1_3)_: +-1 GenericStorageBlock_storage_content(storage1_2) ++1 GenericStorageBlock_storage_content(storage1_3) +-1 flow(bus_storage1_1_3) ++1 flow(storage1_bus_1_3) += 0 + +c_e_GenericStorageBlock_balance(storage1_2_4)_: +-1 GenericStorageBlock_storage_content(storage1_3) ++1 GenericStorageBlock_storage_content(storage1_4) +-1 flow(bus_storage1_2_4) ++1 flow(storage1_bus_2_4) += 0 + +c_e_GenericStorageBlock_balance(storage1_2_5)_: +-1 GenericStorageBlock_storage_content(storage1_4) ++1 GenericStorageBlock_storage_content(storage1_5) +-1 flow(bus_storage1_2_5) ++1 flow(storage1_bus_2_5) += 0 + +c_e_GenericStorageBlock_balance(storage2_0_1)_: +-1 GenericStorageBlock_storage_content(storage2_0) ++1 GenericStorageBlock_storage_content(storage2_1) +-1 flow(bus_storage2_0_1) ++1 flow(storage2_bus_0_1) += 0 + +c_e_GenericStorageBlock_balance(storage2_1_2)_: +-1 GenericStorageBlock_storage_content(storage2_1) ++1 GenericStorageBlock_storage_content(storage2_2) +-1 flow(bus_storage2_1_2) ++1 flow(storage2_bus_1_2) += 0 + +c_e_GenericStorageBlock_balance(storage2_1_3)_: +-1 GenericStorageBlock_storage_content(storage2_2) ++1 GenericStorageBlock_storage_content(storage2_3) +-1 flow(bus_storage2_1_3) ++1 flow(storage2_bus_1_3) += 0 + +c_e_GenericStorageBlock_balance(storage2_2_4)_: +-1 GenericStorageBlock_storage_content(storage2_3) ++1 GenericStorageBlock_storage_content(storage2_4) +-1 flow(bus_storage2_2_4) ++1 flow(storage2_bus_2_4) += 0 + +c_e_GenericStorageBlock_balance(storage2_2_5)_: +-1 GenericStorageBlock_storage_content(storage2_4) ++1 GenericStorageBlock_storage_content(storage2_5) +-1 flow(bus_storage2_2_5) ++1 flow(storage2_bus_2_5) += 0 + +c_e_GenericStorageBlock_balanced_cstr(storage1)_: +-1 GenericStorageBlock_init_content(storage1) ++1 GenericStorageBlock_storage_content(storage1_5) += 0 + +c_e_GenericStorageBlock_balanced_cstr(storage2)_: +-1 GenericStorageBlock_init_content(storage2) ++1 GenericStorageBlock_storage_content(storage2_5) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(bus_storage1_0_0) <= +inf + 0 <= flow(bus_storage1_0_1) <= +inf + 0 <= flow(bus_storage1_1_2) <= +inf + 0 <= flow(bus_storage1_1_3) <= +inf + 0 <= flow(bus_storage1_2_4) <= +inf + 0 <= flow(bus_storage1_2_5) <= +inf + 0 <= flow(bus_storage2_0_0) <= +inf + 0 <= flow(bus_storage2_0_1) <= +inf + 0 <= flow(bus_storage2_1_2) <= +inf + 0 <= flow(bus_storage2_1_3) <= +inf + 0 <= flow(bus_storage2_2_4) <= +inf + 0 <= flow(bus_storage2_2_5) <= +inf + 0 <= flow(storage1_bus_0_0) <= +inf + 0 <= flow(storage1_bus_0_1) <= +inf + 0 <= flow(storage1_bus_1_2) <= +inf + 0 <= flow(storage1_bus_1_3) <= +inf + 0 <= flow(storage1_bus_2_4) <= +inf + 0 <= flow(storage1_bus_2_5) <= +inf + 0 <= flow(storage2_bus_0_0) <= +inf + 0 <= flow(storage2_bus_0_1) <= +inf + 0 <= flow(storage2_bus_1_2) <= +inf + 0 <= flow(storage2_bus_1_3) <= +inf + 0 <= flow(storage2_bus_2_4) <= +inf + 0 <= flow(storage2_bus_2_5) <= +inf + 0 <= limit_storage(0) <= 7 + 0 <= limit_storage(1) <= 7 + 0 <= limit_storage(2) <= 7 + 0 <= limit_storage(3) <= 7 + 0 <= limit_storage(4) <= 7 + 0 <= limit_storage(5) <= 7 + 0 <= GenericStorageBlock_storage_content(storage1_0) <= 5 + 0 <= GenericStorageBlock_storage_content(storage1_1) <= 5 + 0 <= GenericStorageBlock_storage_content(storage1_2) <= 5 + 0 <= GenericStorageBlock_storage_content(storage1_3) <= 5 + 0 <= GenericStorageBlock_storage_content(storage1_4) <= 5 + 0 <= GenericStorageBlock_storage_content(storage1_5) <= 5 + 0 <= GenericStorageBlock_storage_content(storage2_0) <= 5 + 0 <= GenericStorageBlock_storage_content(storage2_1) <= 5 + 0 <= GenericStorageBlock_storage_content(storage2_2) <= 5 + 0 <= GenericStorageBlock_storage_content(storage2_3) <= 5 + 0 <= GenericStorageBlock_storage_content(storage2_4) <= 5 + 0 <= GenericStorageBlock_storage_content(storage2_5) <= 5 + 0 <= GenericStorageBlock_init_content(storage1) <= 5 + 0 <= GenericStorageBlock_init_content(storage2) <= 5 +end diff --git a/tests/lp_files/source_with_gradient_multi_period.lp b/tests/lp_files/source_with_gradient_multi_period.lp new file mode 100644 index 000000000..c469d98b0 --- /dev/null +++ b/tests/lp_files/source_with_gradient_multi_period.lp @@ -0,0 +1,128 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++23 flow(powerplant_electricityBus_0_0) ++23 flow(powerplant_electricityBus_0_1) ++22.549019607843135 flow(powerplant_electricityBus_1_2) ++22.549019607843135 flow(powerplant_electricityBus_1_3) ++22.106881968473662 flow(powerplant_electricityBus_2_4) ++22.106881968473662 flow(powerplant_electricityBus_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(powerplant_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(powerplant_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(powerplant_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(powerplant_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(powerplant_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(powerplant_electricityBus_2_5) += 0 + +c_e_FlowBlock_positive_gradient_constr(powerplant_electricityBus_0_0)_: ++1 FlowBlock_positive_gradient(powerplant_electricityBus_0) += 0 + +c_u_FlowBlock_positive_gradient_constr(powerplant_electricityBus_0_1)_: +-1 FlowBlock_positive_gradient(powerplant_electricityBus_1) +-1 flow(powerplant_electricityBus_0_0) ++1 flow(powerplant_electricityBus_0_1) +<= 0 + +c_u_FlowBlock_positive_gradient_constr(powerplant_electricityBus_1_2)_: +-1 FlowBlock_positive_gradient(powerplant_electricityBus_2) +-1 flow(powerplant_electricityBus_0_1) ++1 flow(powerplant_electricityBus_1_2) +<= 0 + +c_u_FlowBlock_positive_gradient_constr(powerplant_electricityBus_1_3)_: +-1 FlowBlock_positive_gradient(powerplant_electricityBus_3) +-1 flow(powerplant_electricityBus_1_2) ++1 flow(powerplant_electricityBus_1_3) +<= 0 + +c_u_FlowBlock_positive_gradient_constr(powerplant_electricityBus_2_4)_: +-1 FlowBlock_positive_gradient(powerplant_electricityBus_4) +-1 flow(powerplant_electricityBus_1_3) ++1 flow(powerplant_electricityBus_2_4) +<= 0 + +c_u_FlowBlock_positive_gradient_constr(powerplant_electricityBus_2_5)_: +-1 FlowBlock_positive_gradient(powerplant_electricityBus_5) +-1 flow(powerplant_electricityBus_2_4) ++1 flow(powerplant_electricityBus_2_5) +<= 0 + +c_e_FlowBlock_negative_gradient_constr(powerplant_electricityBus_0_0)_: ++1 FlowBlock_negative_gradient(powerplant_electricityBus_0) += 0 + +c_u_FlowBlock_negative_gradient_constr(powerplant_electricityBus_0_1)_: +-1 FlowBlock_negative_gradient(powerplant_electricityBus_1) ++1 flow(powerplant_electricityBus_0_0) +-1 flow(powerplant_electricityBus_0_1) +<= 0 + +c_u_FlowBlock_negative_gradient_constr(powerplant_electricityBus_1_2)_: +-1 FlowBlock_negative_gradient(powerplant_electricityBus_2) ++1 flow(powerplant_electricityBus_0_1) +-1 flow(powerplant_electricityBus_1_2) +<= 0 + +c_u_FlowBlock_negative_gradient_constr(powerplant_electricityBus_1_3)_: +-1 FlowBlock_negative_gradient(powerplant_electricityBus_3) ++1 flow(powerplant_electricityBus_1_2) +-1 flow(powerplant_electricityBus_1_3) +<= 0 + +c_u_FlowBlock_negative_gradient_constr(powerplant_electricityBus_2_4)_: +-1 FlowBlock_negative_gradient(powerplant_electricityBus_4) ++1 flow(powerplant_electricityBus_1_3) +-1 flow(powerplant_electricityBus_2_4) +<= 0 + +c_u_FlowBlock_negative_gradient_constr(powerplant_electricityBus_2_5)_: +-1 FlowBlock_negative_gradient(powerplant_electricityBus_5) ++1 flow(powerplant_electricityBus_2_4) +-1 flow(powerplant_electricityBus_2_5) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(powerplant_electricityBus_0_0) <= 999 + 0 <= flow(powerplant_electricityBus_0_1) <= 999 + 0 <= flow(powerplant_electricityBus_1_2) <= 999 + 0 <= flow(powerplant_electricityBus_1_3) <= 999 + 0 <= flow(powerplant_electricityBus_2_4) <= 999 + 0 <= flow(powerplant_electricityBus_2_5) <= 999 + 0 <= FlowBlock_positive_gradient(powerplant_electricityBus_0) <= 29.969999999999999 + 0 <= FlowBlock_positive_gradient(powerplant_electricityBus_1) <= 29.969999999999999 + 0 <= FlowBlock_positive_gradient(powerplant_electricityBus_2) <= 29.969999999999999 + 0 <= FlowBlock_positive_gradient(powerplant_electricityBus_3) <= 29.969999999999999 + 0 <= FlowBlock_positive_gradient(powerplant_electricityBus_4) <= 29.969999999999999 + 0 <= FlowBlock_positive_gradient(powerplant_electricityBus_5) <= 29.969999999999999 + 0 <= FlowBlock_negative_gradient(powerplant_electricityBus_0) <= 49.950000000000003 + 0 <= FlowBlock_negative_gradient(powerplant_electricityBus_1) <= 49.950000000000003 + 0 <= FlowBlock_negative_gradient(powerplant_electricityBus_2) <= 49.950000000000003 + 0 <= FlowBlock_negative_gradient(powerplant_electricityBus_3) <= 49.950000000000003 + 0 <= FlowBlock_negative_gradient(powerplant_electricityBus_4) <= 49.950000000000003 + 0 <= FlowBlock_negative_gradient(powerplant_electricityBus_5) <= 49.950000000000003 +end diff --git a/tests/lp_files/source_with_nonconvex_gradient_multi_period.lp b/tests/lp_files/source_with_nonconvex_gradient_multi_period.lp new file mode 100644 index 000000000..ae158eee1 --- /dev/null +++ b/tests/lp_files/source_with_nonconvex_gradient_multi_period.lp @@ -0,0 +1,215 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++23 flow(powerplant_electricityBus_0_0) ++23 flow(powerplant_electricityBus_0_1) ++22.549019607843135 flow(powerplant_electricityBus_1_2) ++22.549019607843135 flow(powerplant_electricityBus_1_3) ++22.106881968473662 flow(powerplant_electricityBus_2_4) ++22.106881968473662 flow(powerplant_electricityBus_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(powerplant_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(powerplant_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(powerplant_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(powerplant_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(powerplant_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(powerplant_electricityBus_2_5) += 0 + +c_l_NonConvexFlowBlock_min(powerplant_electricityBus_0_0)_: ++1 flow(powerplant_electricityBus_0_0) +>= 0 + +c_l_NonConvexFlowBlock_min(powerplant_electricityBus_0_1)_: ++1 flow(powerplant_electricityBus_0_1) +>= 0 + +c_l_NonConvexFlowBlock_min(powerplant_electricityBus_1_2)_: ++1 flow(powerplant_electricityBus_1_2) +>= 0 + +c_l_NonConvexFlowBlock_min(powerplant_electricityBus_1_3)_: ++1 flow(powerplant_electricityBus_1_3) +>= 0 + +c_l_NonConvexFlowBlock_min(powerplant_electricityBus_2_4)_: ++1 flow(powerplant_electricityBus_2_4) +>= 0 + +c_l_NonConvexFlowBlock_min(powerplant_electricityBus_2_5)_: ++1 flow(powerplant_electricityBus_2_5) +>= 0 + +c_u_NonConvexFlowBlock_max(powerplant_electricityBus_0_0)_: +-999 NonConvexFlowBlock_status(powerplant_electricityBus_0) ++1 flow(powerplant_electricityBus_0_0) +<= 0 + +c_u_NonConvexFlowBlock_max(powerplant_electricityBus_0_1)_: +-999 NonConvexFlowBlock_status(powerplant_electricityBus_1) ++1 flow(powerplant_electricityBus_0_1) +<= 0 + +c_u_NonConvexFlowBlock_max(powerplant_electricityBus_1_2)_: +-999 NonConvexFlowBlock_status(powerplant_electricityBus_2) ++1 flow(powerplant_electricityBus_1_2) +<= 0 + +c_u_NonConvexFlowBlock_max(powerplant_electricityBus_1_3)_: +-999 NonConvexFlowBlock_status(powerplant_electricityBus_3) ++1 flow(powerplant_electricityBus_1_3) +<= 0 + +c_u_NonConvexFlowBlock_max(powerplant_electricityBus_2_4)_: +-999 NonConvexFlowBlock_status(powerplant_electricityBus_4) ++1 flow(powerplant_electricityBus_2_4) +<= 0 + +c_u_NonConvexFlowBlock_max(powerplant_electricityBus_2_5)_: +-999 NonConvexFlowBlock_status(powerplant_electricityBus_5) ++1 flow(powerplant_electricityBus_2_5) +<= 0 + +c_e_NonConvexFlowBlock_positive_gradient_constr(powerplant_electricityBus_0_0)_: ++1 NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_0) += 0 + +c_u_NonConvexFlowBlock_positive_gradient_constr(powerplant_electricityBus_0_1)_: +-1 NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_1) ++ [ +-1 NonConvexFlowBlock_status(powerplant_electricityBus_0) * flow(powerplant_electricityBus_0_0) ++1 NonConvexFlowBlock_status(powerplant_electricityBus_1) * flow(powerplant_electricityBus_0_1) +] +<= 0 + +c_u_NonConvexFlowBlock_positive_gradient_constr(powerplant_electricityBus_1_2)_: +-1 NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_2) ++ [ +-1 NonConvexFlowBlock_status(powerplant_electricityBus_1) * flow(powerplant_electricityBus_0_1) ++1 NonConvexFlowBlock_status(powerplant_electricityBus_2) * flow(powerplant_electricityBus_1_2) +] +<= 0 + +c_u_NonConvexFlowBlock_positive_gradient_constr(powerplant_electricityBus_1_3)_: +-1 NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_3) ++ [ +-1 NonConvexFlowBlock_status(powerplant_electricityBus_2) * flow(powerplant_electricityBus_1_2) ++1 NonConvexFlowBlock_status(powerplant_electricityBus_3) * flow(powerplant_electricityBus_1_3) +] +<= 0 + +c_u_NonConvexFlowBlock_positive_gradient_constr(powerplant_electricityBus_2_4)_: +-1 NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_4) ++ [ +-1 NonConvexFlowBlock_status(powerplant_electricityBus_3) * flow(powerplant_electricityBus_1_3) ++1 NonConvexFlowBlock_status(powerplant_electricityBus_4) * flow(powerplant_electricityBus_2_4) +] +<= 0 + +c_u_NonConvexFlowBlock_positive_gradient_constr(powerplant_electricityBus_2_5)_: +-1 NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_5) ++ [ +-1 NonConvexFlowBlock_status(powerplant_electricityBus_4) * flow(powerplant_electricityBus_2_4) ++1 NonConvexFlowBlock_status(powerplant_electricityBus_5) * flow(powerplant_electricityBus_2_5) +] +<= 0 + +c_e_NonConvexFlowBlock_negative_gradient_constr(powerplant_electricityBus_0_0)_: ++1 NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_0) += 0 + +c_u_NonConvexFlowBlock_negative_gradient_constr(powerplant_electricityBus_0_1)_: +-1 NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_1) ++ [ ++1 NonConvexFlowBlock_status(powerplant_electricityBus_0) * flow(powerplant_electricityBus_0_0) +-1 NonConvexFlowBlock_status(powerplant_electricityBus_1) * flow(powerplant_electricityBus_0_1) +] +<= 0 + +c_u_NonConvexFlowBlock_negative_gradient_constr(powerplant_electricityBus_1_2)_: +-1 NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_2) ++ [ ++1 NonConvexFlowBlock_status(powerplant_electricityBus_1) * flow(powerplant_electricityBus_0_1) +-1 NonConvexFlowBlock_status(powerplant_electricityBus_2) * flow(powerplant_electricityBus_1_2) +] +<= 0 + +c_u_NonConvexFlowBlock_negative_gradient_constr(powerplant_electricityBus_1_3)_: +-1 NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_3) ++ [ ++1 NonConvexFlowBlock_status(powerplant_electricityBus_2) * flow(powerplant_electricityBus_1_2) +-1 NonConvexFlowBlock_status(powerplant_electricityBus_3) * flow(powerplant_electricityBus_1_3) +] +<= 0 + +c_u_NonConvexFlowBlock_negative_gradient_constr(powerplant_electricityBus_2_4)_: +-1 NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_4) ++ [ ++1 NonConvexFlowBlock_status(powerplant_electricityBus_3) * flow(powerplant_electricityBus_1_3) +-1 NonConvexFlowBlock_status(powerplant_electricityBus_4) * flow(powerplant_electricityBus_2_4) +] +<= 0 + +c_u_NonConvexFlowBlock_negative_gradient_constr(powerplant_electricityBus_2_5)_: +-1 NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_5) ++ [ ++1 NonConvexFlowBlock_status(powerplant_electricityBus_4) * flow(powerplant_electricityBus_2_4) +-1 NonConvexFlowBlock_status(powerplant_electricityBus_5) * flow(powerplant_electricityBus_2_5) +] +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(powerplant_electricityBus_0_0) <= 999 + 0 <= flow(powerplant_electricityBus_0_1) <= 999 + 0 <= flow(powerplant_electricityBus_1_2) <= 999 + 0 <= flow(powerplant_electricityBus_1_3) <= 999 + 0 <= flow(powerplant_electricityBus_2_4) <= 999 + 0 <= flow(powerplant_electricityBus_2_5) <= 999 + 0 <= NonConvexFlowBlock_status(powerplant_electricityBus_0) <= 1 + 0 <= NonConvexFlowBlock_status(powerplant_electricityBus_1) <= 1 + 0 <= NonConvexFlowBlock_status(powerplant_electricityBus_2) <= 1 + 0 <= NonConvexFlowBlock_status(powerplant_electricityBus_3) <= 1 + 0 <= NonConvexFlowBlock_status(powerplant_electricityBus_4) <= 1 + 0 <= NonConvexFlowBlock_status(powerplant_electricityBus_5) <= 1 + 0 <= NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_0) <= 29.969999999999999 + 0 <= NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_1) <= 29.969999999999999 + 0 <= NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_2) <= 29.969999999999999 + 0 <= NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_3) <= 29.969999999999999 + 0 <= NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_4) <= 29.969999999999999 + 0 <= NonConvexFlowBlock_positive_gradient(powerplant_electricityBus_5) <= 29.969999999999999 + 0 <= NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_0) <= 49.950000000000003 + 0 <= NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_1) <= 49.950000000000003 + 0 <= NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_2) <= 49.950000000000003 + 0 <= NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_3) <= 49.950000000000003 + 0 <= NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_4) <= 49.950000000000003 + 0 <= NonConvexFlowBlock_negative_gradient(powerplant_electricityBus_5) <= 49.950000000000003 +binary + NonConvexFlowBlock_status(powerplant_electricityBus_0) + NonConvexFlowBlock_status(powerplant_electricityBus_1) + NonConvexFlowBlock_status(powerplant_electricityBus_2) + NonConvexFlowBlock_status(powerplant_electricityBus_3) + NonConvexFlowBlock_status(powerplant_electricityBus_4) + NonConvexFlowBlock_status(powerplant_electricityBus_5) +end diff --git a/tests/lp_files/storage_fixed_losses_multi_period.lp b/tests/lp_files/storage_fixed_losses_multi_period.lp new file mode 100644 index 000000000..d48205b0b --- /dev/null +++ b/tests/lp_files/storage_fixed_losses_multi_period.lp @@ -0,0 +1,117 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++56 flow(electricityBus_storage_no_invest_0_0) ++56 flow(electricityBus_storage_no_invest_0_1) ++54.901960784313722 flow(electricityBus_storage_no_invest_1_2) ++54.901960784313722 flow(electricityBus_storage_no_invest_1_3) ++53.825451749327179 flow(electricityBus_storage_no_invest_2_4) ++53.825451749327179 flow(electricityBus_storage_no_invest_2_5) ++24 flow(storage_no_invest_electricityBus_0_0) ++24 flow(storage_no_invest_electricityBus_0_1) ++23.52941176470588 flow(storage_no_invest_electricityBus_1_2) ++23.52941176470588 flow(storage_no_invest_electricityBus_1_3) ++23.068050749711649 flow(storage_no_invest_electricityBus_2_4) ++23.068050749711649 flow(storage_no_invest_electricityBus_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage_no_invest_0_0) ++1 flow(storage_no_invest_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage_no_invest_0_1) ++1 flow(storage_no_invest_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: +-1 flow(electricityBus_storage_no_invest_1_2) ++1 flow(storage_no_invest_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: +-1 flow(electricityBus_storage_no_invest_1_3) ++1 flow(storage_no_invest_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: +-1 flow(electricityBus_storage_no_invest_2_4) ++1 flow(storage_no_invest_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: +-1 flow(electricityBus_storage_no_invest_2_5) ++1 flow(storage_no_invest_electricityBus_2_5) += 0 + +c_e_GenericStorageBlock_balance_first(storage_no_invest)_: ++1 GenericStorageBlock_storage_content(storage_no_invest_0) +-0.96999999999999997 flow(electricityBus_storage_no_invest_0_0) ++1.1627906976744187 flow(storage_no_invest_electricityBus_0_0) += 33797 + +c_e_GenericStorageBlock_balance(storage_no_invest_0_1)_: +-0.87 GenericStorageBlock_storage_content(storage_no_invest_0) ++1 GenericStorageBlock_storage_content(storage_no_invest_1) +-0.96999999999999997 flow(electricityBus_storage_no_invest_0_1) ++1.1627906976744187 flow(storage_no_invest_electricityBus_0_1) += -1003 + +c_e_GenericStorageBlock_balance(storage_no_invest_1_2)_: +-0.87 GenericStorageBlock_storage_content(storage_no_invest_1) ++1 GenericStorageBlock_storage_content(storage_no_invest_2) +-0.96999999999999997 flow(electricityBus_storage_no_invest_1_2) ++1.1627906976744187 flow(storage_no_invest_electricityBus_1_2) += -1003 + +c_e_GenericStorageBlock_balance(storage_no_invest_1_3)_: +-0.87 GenericStorageBlock_storage_content(storage_no_invest_2) ++1 GenericStorageBlock_storage_content(storage_no_invest_3) +-0.96999999999999997 flow(electricityBus_storage_no_invest_1_3) ++1.1627906976744187 flow(storage_no_invest_electricityBus_1_3) += -1003 + +c_e_GenericStorageBlock_balance(storage_no_invest_2_4)_: +-0.87 GenericStorageBlock_storage_content(storage_no_invest_3) ++1 GenericStorageBlock_storage_content(storage_no_invest_4) +-0.96999999999999997 flow(electricityBus_storage_no_invest_2_4) ++1.1627906976744187 flow(storage_no_invest_electricityBus_2_4) += -1003 + +c_e_GenericStorageBlock_balance(storage_no_invest_2_5)_: +-0.87 GenericStorageBlock_storage_content(storage_no_invest_4) ++1 GenericStorageBlock_storage_content(storage_no_invest_5) +-0.96999999999999997 flow(electricityBus_storage_no_invest_2_5) ++1.1627906976744187 flow(storage_no_invest_electricityBus_2_5) += -1003 + +c_e_GenericStorageBlock_balanced_cstr(storage_no_invest)_: ++1 GenericStorageBlock_storage_content(storage_no_invest_5) += 40000 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_storage_no_invest_0_0) <= 16667 + 0 <= flow(electricityBus_storage_no_invest_0_1) <= 16667 + 0 <= flow(electricityBus_storage_no_invest_1_2) <= 16667 + 0 <= flow(electricityBus_storage_no_invest_1_3) <= 16667 + 0 <= flow(electricityBus_storage_no_invest_2_4) <= 16667 + 0 <= flow(electricityBus_storage_no_invest_2_5) <= 16667 + 0 <= flow(storage_no_invest_electricityBus_0_0) <= 16667 + 0 <= flow(storage_no_invest_electricityBus_0_1) <= 16667 + 0 <= flow(storage_no_invest_electricityBus_1_2) <= 16667 + 0 <= flow(storage_no_invest_electricityBus_1_3) <= 16667 + 0 <= flow(storage_no_invest_electricityBus_2_4) <= 16667 + 0 <= flow(storage_no_invest_electricityBus_2_5) <= 16667 + 0 <= GenericStorageBlock_storage_content(storage_no_invest_0) <= 100000 + 0 <= GenericStorageBlock_storage_content(storage_no_invest_1) <= 100000 + 0 <= GenericStorageBlock_storage_content(storage_no_invest_2) <= 100000 + 0 <= GenericStorageBlock_storage_content(storage_no_invest_3) <= 100000 + 0 <= GenericStorageBlock_storage_content(storage_no_invest_4) <= 100000 + 0 <= GenericStorageBlock_storage_content(storage_no_invest_5) <= 100000 +end diff --git a/tests/lp_files/storage_invest_1_multi_period.lp b/tests/lp_files/storage_invest_1_multi_period.lp new file mode 100644 index 000000000..82f973438 --- /dev/null +++ b/tests/lp_files/storage_invest_1_multi_period.lp @@ -0,0 +1,504 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++232.70350285300475 GenericInvestmentStorageBlock_invest(storage1_0) ++228.14068907157326 GenericInvestmentStorageBlock_invest(storage1_1) ++223.66734222703261 GenericInvestmentStorageBlock_invest(storage1_2) ++56 flow(electricityBus_storage1_0_0) ++56 flow(electricityBus_storage1_0_1) ++54.901960784313722 flow(electricityBus_storage1_1_2) ++54.901960784313722 flow(electricityBus_storage1_1_3) ++53.825451749327179 flow(electricityBus_storage1_2_4) ++53.825451749327179 flow(electricityBus_storage1_2_5) ++24 flow(storage1_electricityBus_0_0) ++24 flow(storage1_electricityBus_0_1) ++23.52941176470588 flow(storage1_electricityBus_1_2) ++23.52941176470588 flow(storage1_electricityBus_1_3) ++23.068050749711649 flow(storage1_electricityBus_2_4) ++23.068050749711649 flow(storage1_electricityBus_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage1_0_0) ++1 flow(storage1_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage1_0_1) ++1 flow(storage1_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: +-1 flow(electricityBus_storage1_1_2) ++1 flow(storage1_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: +-1 flow(electricityBus_storage1_1_3) ++1 flow(storage1_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: +-1 flow(electricityBus_storage1_2_4) ++1 flow(storage1_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: +-1 flow(electricityBus_storage1_2_5) ++1 flow(storage1_electricityBus_2_5) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage1_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage1_0) ++1 InvestmentFlowBlock_total(electricityBus_storage1_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage1_1)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage1_1) ++1 InvestmentFlowBlock_old(electricityBus_storage1_1) +-1 InvestmentFlowBlock_total(electricityBus_storage1_0) ++1 InvestmentFlowBlock_total(electricityBus_storage1_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage1_2)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage1_2) ++1 InvestmentFlowBlock_old(electricityBus_storage1_2) +-1 InvestmentFlowBlock_total(electricityBus_storage1_1) ++1 InvestmentFlowBlock_total(electricityBus_storage1_2) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage1_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(storage1_electricityBus_0) ++1 InvestmentFlowBlock_total(storage1_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage1_electricityBus_1)_: +-1 InvestmentFlowBlock_invest(storage1_electricityBus_1) ++1 InvestmentFlowBlock_old(storage1_electricityBus_1) +-1 InvestmentFlowBlock_total(storage1_electricityBus_0) ++1 InvestmentFlowBlock_total(storage1_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage1_electricityBus_2)_: +-1 InvestmentFlowBlock_invest(storage1_electricityBus_2) ++1 InvestmentFlowBlock_old(storage1_electricityBus_2) +-1 InvestmentFlowBlock_total(storage1_electricityBus_1) ++1 InvestmentFlowBlock_total(storage1_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage1_0)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage1_1)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage1_2)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage1_electricityBus_0)_: ++1 InvestmentFlowBlock_old_end(storage1_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage1_electricityBus_1)_: ++1 InvestmentFlowBlock_old_end(storage1_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage1_electricityBus_2)_: ++1 InvestmentFlowBlock_old_end(storage1_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage1_0)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage1_1)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage1_2)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage1_electricityBus_0)_: ++1 InvestmentFlowBlock_old_exo(storage1_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage1_electricityBus_1)_: ++1 InvestmentFlowBlock_old_exo(storage1_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage1_electricityBus_2)_: ++1 InvestmentFlowBlock_old_exo(storage1_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage1_0)_: ++1 InvestmentFlowBlock_old(electricityBus_storage1_0) +-1 InvestmentFlowBlock_old_end(electricityBus_storage1_0) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage1_1)_: ++1 InvestmentFlowBlock_old(electricityBus_storage1_1) +-1 InvestmentFlowBlock_old_end(electricityBus_storage1_1) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage1_2)_: ++1 InvestmentFlowBlock_old(electricityBus_storage1_2) +-1 InvestmentFlowBlock_old_end(electricityBus_storage1_2) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage1_electricityBus_0)_: ++1 InvestmentFlowBlock_old(storage1_electricityBus_0) +-1 InvestmentFlowBlock_old_end(storage1_electricityBus_0) +-1 InvestmentFlowBlock_old_exo(storage1_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage1_electricityBus_1)_: ++1 InvestmentFlowBlock_old(storage1_electricityBus_1) +-1 InvestmentFlowBlock_old_end(storage1_electricityBus_1) +-1 InvestmentFlowBlock_old_exo(storage1_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage1_electricityBus_2)_: ++1 InvestmentFlowBlock_old(storage1_electricityBus_2) +-1 InvestmentFlowBlock_old_end(storage1_electricityBus_2) +-1 InvestmentFlowBlock_old_exo(storage1_electricityBus_2) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage1_0_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage1_0) ++1 flow(electricityBus_storage1_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage1_0_1)_: +-1 InvestmentFlowBlock_total(electricityBus_storage1_0) ++1 flow(electricityBus_storage1_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage1_1_2)_: +-1 InvestmentFlowBlock_total(electricityBus_storage1_1) ++1 flow(electricityBus_storage1_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage1_1_3)_: +-1 InvestmentFlowBlock_total(electricityBus_storage1_1) ++1 flow(electricityBus_storage1_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage1_2_4)_: +-1 InvestmentFlowBlock_total(electricityBus_storage1_2) ++1 flow(electricityBus_storage1_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage1_2_5)_: +-1 InvestmentFlowBlock_total(electricityBus_storage1_2) ++1 flow(electricityBus_storage1_2_5) +<= 0 + +c_u_InvestmentFlowBlock_max(storage1_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(storage1_electricityBus_0) ++1 flow(storage1_electricityBus_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(storage1_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(storage1_electricityBus_0) ++1 flow(storage1_electricityBus_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(storage1_electricityBus_1_2)_: +-1 InvestmentFlowBlock_total(storage1_electricityBus_1) ++1 flow(storage1_electricityBus_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(storage1_electricityBus_1_3)_: +-1 InvestmentFlowBlock_total(storage1_electricityBus_1) ++1 flow(storage1_electricityBus_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(storage1_electricityBus_2_4)_: +-1 InvestmentFlowBlock_total(storage1_electricityBus_2) ++1 flow(storage1_electricityBus_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(storage1_electricityBus_2_5)_: +-1 InvestmentFlowBlock_total(storage1_electricityBus_2) ++1 flow(storage1_electricityBus_2_5) +<= 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage1_0)_: +-1 GenericInvestmentStorageBlock_invest(storage1_0) ++1 GenericInvestmentStorageBlock_total(storage1_0) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage1_1)_: +-1 GenericInvestmentStorageBlock_invest(storage1_1) ++1 GenericInvestmentStorageBlock_old(storage1_1) +-1 GenericInvestmentStorageBlock_total(storage1_0) ++1 GenericInvestmentStorageBlock_total(storage1_1) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage1_2)_: +-1 GenericInvestmentStorageBlock_invest(storage1_2) ++1 GenericInvestmentStorageBlock_old(storage1_2) +-1 GenericInvestmentStorageBlock_total(storage1_1) ++1 GenericInvestmentStorageBlock_total(storage1_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage1_0)_: ++1 GenericInvestmentStorageBlock_old_end(storage1_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage1_1)_: ++1 GenericInvestmentStorageBlock_old_end(storage1_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage1_2)_: ++1 GenericInvestmentStorageBlock_old_end(storage1_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage1_0)_: ++1 GenericInvestmentStorageBlock_old_exo(storage1_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage1_1)_: ++1 GenericInvestmentStorageBlock_old_exo(storage1_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage1_2)_: ++1 GenericInvestmentStorageBlock_old_exo(storage1_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage1_0)_: ++1 GenericInvestmentStorageBlock_old(storage1_0) +-1 GenericInvestmentStorageBlock_old_end(storage1_0) +-1 GenericInvestmentStorageBlock_old_exo(storage1_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage1_1)_: ++1 GenericInvestmentStorageBlock_old(storage1_1) +-1 GenericInvestmentStorageBlock_old_end(storage1_1) +-1 GenericInvestmentStorageBlock_old_exo(storage1_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage1_2)_: ++1 GenericInvestmentStorageBlock_old(storage1_2) +-1 GenericInvestmentStorageBlock_old_end(storage1_2) +-1 GenericInvestmentStorageBlock_old_exo(storage1_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage1_0_1)_: +-0.87 GenericInvestmentStorageBlock_storage_content(storage1_0) ++1 GenericInvestmentStorageBlock_storage_content(storage1_1) +-0.96999999999999997 flow(electricityBus_storage1_0_1) ++1.1627906976744187 flow(storage1_electricityBus_0_1) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage1_1_2)_: +-0.87 GenericInvestmentStorageBlock_storage_content(storage1_1) ++1 GenericInvestmentStorageBlock_storage_content(storage1_2) +-0.96999999999999997 flow(electricityBus_storage1_1_2) ++1.1627906976744187 flow(storage1_electricityBus_1_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage1_1_3)_: +-0.87 GenericInvestmentStorageBlock_storage_content(storage1_2) ++1 GenericInvestmentStorageBlock_storage_content(storage1_3) +-0.96999999999999997 flow(electricityBus_storage1_1_3) ++1.1627906976744187 flow(storage1_electricityBus_1_3) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage1_2_4)_: +-0.87 GenericInvestmentStorageBlock_storage_content(storage1_3) ++1 GenericInvestmentStorageBlock_storage_content(storage1_4) +-0.96999999999999997 flow(electricityBus_storage1_2_4) ++1.1627906976744187 flow(storage1_electricityBus_2_4) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage1_2_5)_: +-0.87 GenericInvestmentStorageBlock_storage_content(storage1_4) ++1 GenericInvestmentStorageBlock_storage_content(storage1_5) +-0.96999999999999997 flow(electricityBus_storage1_2_5) ++1.1627906976744187 flow(storage1_electricityBus_2_5) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage1_0)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage1_0) ++1 InvestmentFlowBlock_total(electricityBus_storage1_0) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage1_1)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage1_1) ++1 InvestmentFlowBlock_total(electricityBus_storage1_1) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage1_2)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage1_2) ++1 InvestmentFlowBlock_total(electricityBus_storage1_2) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage1_0)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage1_0) ++1 InvestmentFlowBlock_total(storage1_electricityBus_0) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage1_1)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage1_1) ++1 InvestmentFlowBlock_total(storage1_electricityBus_1) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage1_2)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage1_2) ++1 InvestmentFlowBlock_total(storage1_electricityBus_2) += 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0_0)_: ++1 GenericInvestmentStorageBlock_storage_content(storage1_0) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage1_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0_1)_: ++1 GenericInvestmentStorageBlock_storage_content(storage1_1) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage1_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_1_2)_: ++1 GenericInvestmentStorageBlock_storage_content(storage1_2) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage1_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_1_3)_: ++1 GenericInvestmentStorageBlock_storage_content(storage1_3) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage1_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_2_4)_: ++1 GenericInvestmentStorageBlock_storage_content(storage1_4) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage1_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_2_5)_: ++1 GenericInvestmentStorageBlock_storage_content(storage1_5) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage1_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_min_storage_content(storage1_0_0)_: +-1 GenericInvestmentStorageBlock_storage_content(storage1_0) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage1_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_min_storage_content(storage1_0_1)_: +-1 GenericInvestmentStorageBlock_storage_content(storage1_1) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage1_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_min_storage_content(storage1_1_2)_: +-1 GenericInvestmentStorageBlock_storage_content(storage1_2) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage1_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_min_storage_content(storage1_1_3)_: +-1 GenericInvestmentStorageBlock_storage_content(storage1_3) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage1_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_min_storage_content(storage1_2_4)_: +-1 GenericInvestmentStorageBlock_storage_content(storage1_4) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage1_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_min_storage_content(storage1_2_5)_: +-1 GenericInvestmentStorageBlock_storage_content(storage1_5) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage1_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_overall_storage_maximum(storage1_0)_: ++1 GenericInvestmentStorageBlock_total(storage1_0) +<= 1000 + +c_u_GenericInvestmentStorageBlock_overall_storage_maximum(storage1_1)_: ++1 GenericInvestmentStorageBlock_total(storage1_1) +<= 1000 + +c_u_GenericInvestmentStorageBlock_overall_storage_maximum(storage1_2)_: ++1 GenericInvestmentStorageBlock_total(storage1_2) +<= 1000 + +c_l_GenericInvestmentStorageBlock_overall_minimum(storage1)_: ++1 GenericInvestmentStorageBlock_total(storage1_2) +>= 2 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_storage1_0_0) <= +inf + 0 <= flow(electricityBus_storage1_0_1) <= +inf + 0 <= flow(electricityBus_storage1_1_2) <= +inf + 0 <= flow(electricityBus_storage1_1_3) <= +inf + 0 <= flow(electricityBus_storage1_2_4) <= +inf + 0 <= flow(electricityBus_storage1_2_5) <= +inf + 0 <= flow(storage1_electricityBus_0_0) <= +inf + 0 <= flow(storage1_electricityBus_0_1) <= +inf + 0 <= flow(storage1_electricityBus_1_2) <= +inf + 0 <= flow(storage1_electricityBus_1_3) <= +inf + 0 <= flow(storage1_electricityBus_2_4) <= +inf + 0 <= flow(storage1_electricityBus_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage1_0) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage1_1) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage1_2) <= +inf + 0 <= InvestmentFlowBlock_invest(storage1_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage1_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_invest(storage1_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage1_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage1_1) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage1_2) <= +inf + 0 <= InvestmentFlowBlock_total(storage1_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage1_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_total(storage1_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage1_0) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage1_1) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage1_2) <= +inf + 0 <= InvestmentFlowBlock_old(storage1_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old(storage1_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old(storage1_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage1_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage1_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage1_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage1_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage1_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage1_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage1_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage1_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage1_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage1_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage1_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage1_electricityBus_2) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage1_0) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage1_1) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage1_2) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage1_3) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage1_4) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage1_5) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage1_0) <= 234 + 0 <= GenericInvestmentStorageBlock_invest(storage1_1) <= 234 + 0 <= GenericInvestmentStorageBlock_invest(storage1_2) <= 234 + 0 <= GenericInvestmentStorageBlock_total(storage1_0) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage1_1) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage1_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage1_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage1_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage1_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage1_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage1_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage1_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage1_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage1_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage1_2) <= +inf +end diff --git a/tests/lp_files/storage_invest_2_multi_period.lp b/tests/lp_files/storage_invest_2_multi_period.lp new file mode 100644 index 000000000..a6ad6f15d --- /dev/null +++ b/tests/lp_files/storage_invest_2_multi_period.lp @@ -0,0 +1,422 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++177.35448256334197 GenericInvestmentStorageBlock_invest(storage2_0) ++173.87694368955096 GenericInvestmentStorageBlock_invest(storage2_1) ++170.46759185250093 GenericInvestmentStorageBlock_invest(storage2_2) ++121.09030188807486 InvestmentFlowBlock_invest(electricityBus_storage2_0) ++118.71598224321065 InvestmentFlowBlock_invest(electricityBus_storage2_1) ++116.38821788550062 InvestmentFlowBlock_invest(electricityBus_storage2_2) ++11.00820926255226 InvestmentFlowBlock_invest(storage2_electricityBus_0) ++10.792362022110058 InvestmentFlowBlock_invest(storage2_electricityBus_1) ++10.580747080500057 InvestmentFlowBlock_invest(storage2_electricityBus_2) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage2_0_0) ++1 flow(storage2_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage2_0_1) ++1 flow(storage2_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: +-1 flow(electricityBus_storage2_1_2) ++1 flow(storage2_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: +-1 flow(electricityBus_storage2_1_3) ++1 flow(storage2_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: +-1 flow(electricityBus_storage2_2_4) ++1 flow(storage2_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: +-1 flow(electricityBus_storage2_2_5) ++1 flow(storage2_electricityBus_2_5) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage2_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage2_0) ++1 InvestmentFlowBlock_total(electricityBus_storage2_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage2_1)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage2_1) ++1 InvestmentFlowBlock_old(electricityBus_storage2_1) +-1 InvestmentFlowBlock_total(electricityBus_storage2_0) ++1 InvestmentFlowBlock_total(electricityBus_storage2_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage2_2)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage2_2) ++1 InvestmentFlowBlock_old(electricityBus_storage2_2) +-1 InvestmentFlowBlock_total(electricityBus_storage2_1) ++1 InvestmentFlowBlock_total(electricityBus_storage2_2) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage2_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(storage2_electricityBus_0) ++1 InvestmentFlowBlock_total(storage2_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage2_electricityBus_1)_: +-1 InvestmentFlowBlock_invest(storage2_electricityBus_1) ++1 InvestmentFlowBlock_old(storage2_electricityBus_1) +-1 InvestmentFlowBlock_total(storage2_electricityBus_0) ++1 InvestmentFlowBlock_total(storage2_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage2_electricityBus_2)_: +-1 InvestmentFlowBlock_invest(storage2_electricityBus_2) ++1 InvestmentFlowBlock_old(storage2_electricityBus_2) +-1 InvestmentFlowBlock_total(storage2_electricityBus_1) ++1 InvestmentFlowBlock_total(storage2_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage2_0)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage2_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage2_1)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage2_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage2_2)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage2_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage2_electricityBus_0)_: ++1 InvestmentFlowBlock_old_end(storage2_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage2_electricityBus_1)_: ++1 InvestmentFlowBlock_old_end(storage2_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage2_electricityBus_2)_: ++1 InvestmentFlowBlock_old_end(storage2_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage2_0)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage2_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage2_1)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage2_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage2_2)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage2_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage2_electricityBus_0)_: ++1 InvestmentFlowBlock_old_exo(storage2_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage2_electricityBus_1)_: ++1 InvestmentFlowBlock_old_exo(storage2_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage2_electricityBus_2)_: ++1 InvestmentFlowBlock_old_exo(storage2_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage2_0)_: ++1 InvestmentFlowBlock_old(electricityBus_storage2_0) +-1 InvestmentFlowBlock_old_end(electricityBus_storage2_0) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage2_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage2_1)_: ++1 InvestmentFlowBlock_old(electricityBus_storage2_1) +-1 InvestmentFlowBlock_old_end(electricityBus_storage2_1) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage2_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage2_2)_: ++1 InvestmentFlowBlock_old(electricityBus_storage2_2) +-1 InvestmentFlowBlock_old_end(electricityBus_storage2_2) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage2_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage2_electricityBus_0)_: ++1 InvestmentFlowBlock_old(storage2_electricityBus_0) +-1 InvestmentFlowBlock_old_end(storage2_electricityBus_0) +-1 InvestmentFlowBlock_old_exo(storage2_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage2_electricityBus_1)_: ++1 InvestmentFlowBlock_old(storage2_electricityBus_1) +-1 InvestmentFlowBlock_old_end(storage2_electricityBus_1) +-1 InvestmentFlowBlock_old_exo(storage2_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage2_electricityBus_2)_: ++1 InvestmentFlowBlock_old(storage2_electricityBus_2) +-1 InvestmentFlowBlock_old_end(storage2_electricityBus_2) +-1 InvestmentFlowBlock_old_exo(storage2_electricityBus_2) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage2_0_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage2_0) ++1 flow(electricityBus_storage2_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage2_0_1)_: +-1 InvestmentFlowBlock_total(electricityBus_storage2_0) ++1 flow(electricityBus_storage2_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage2_1_2)_: +-1 InvestmentFlowBlock_total(electricityBus_storage2_1) ++1 flow(electricityBus_storage2_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage2_1_3)_: +-1 InvestmentFlowBlock_total(electricityBus_storage2_1) ++1 flow(electricityBus_storage2_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage2_2_4)_: +-1 InvestmentFlowBlock_total(electricityBus_storage2_2) ++1 flow(electricityBus_storage2_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage2_2_5)_: +-1 InvestmentFlowBlock_total(electricityBus_storage2_2) ++1 flow(electricityBus_storage2_2_5) +<= 0 + +c_u_InvestmentFlowBlock_max(storage2_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(storage2_electricityBus_0) ++1 flow(storage2_electricityBus_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(storage2_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(storage2_electricityBus_0) ++1 flow(storage2_electricityBus_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(storage2_electricityBus_1_2)_: +-1 InvestmentFlowBlock_total(storage2_electricityBus_1) ++1 flow(storage2_electricityBus_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(storage2_electricityBus_1_3)_: +-1 InvestmentFlowBlock_total(storage2_electricityBus_1) ++1 flow(storage2_electricityBus_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(storage2_electricityBus_2_4)_: +-1 InvestmentFlowBlock_total(storage2_electricityBus_2) ++1 flow(storage2_electricityBus_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(storage2_electricityBus_2_5)_: +-1 InvestmentFlowBlock_total(storage2_electricityBus_2) ++1 flow(storage2_electricityBus_2_5) +<= 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage2_0)_: +-1 GenericInvestmentStorageBlock_invest(storage2_0) ++1 GenericInvestmentStorageBlock_total(storage2_0) += 20 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage2_1)_: +-1 GenericInvestmentStorageBlock_invest(storage2_1) ++1 GenericInvestmentStorageBlock_old(storage2_1) +-1 GenericInvestmentStorageBlock_total(storage2_0) ++1 GenericInvestmentStorageBlock_total(storage2_1) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage2_2)_: +-1 GenericInvestmentStorageBlock_invest(storage2_2) ++1 GenericInvestmentStorageBlock_old(storage2_2) +-1 GenericInvestmentStorageBlock_total(storage2_1) ++1 GenericInvestmentStorageBlock_total(storage2_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage2_0)_: ++1 GenericInvestmentStorageBlock_old_end(storage2_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage2_1)_: ++1 GenericInvestmentStorageBlock_old_end(storage2_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage2_2)_: ++1 GenericInvestmentStorageBlock_old_end(storage2_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage2_0)_: ++1 GenericInvestmentStorageBlock_old_exo(storage2_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage2_1)_: ++1 GenericInvestmentStorageBlock_old_exo(storage2_1) += 20 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage2_2)_: ++1 GenericInvestmentStorageBlock_old_exo(storage2_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage2_0)_: ++1 GenericInvestmentStorageBlock_old(storage2_0) +-1 GenericInvestmentStorageBlock_old_end(storage2_0) +-1 GenericInvestmentStorageBlock_old_exo(storage2_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage2_1)_: ++1 GenericInvestmentStorageBlock_old(storage2_1) +-1 GenericInvestmentStorageBlock_old_end(storage2_1) +-1 GenericInvestmentStorageBlock_old_exo(storage2_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage2_2)_: ++1 GenericInvestmentStorageBlock_old(storage2_2) +-1 GenericInvestmentStorageBlock_old_end(storage2_2) +-1 GenericInvestmentStorageBlock_old_exo(storage2_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage2_0_1)_: +-1 GenericInvestmentStorageBlock_storage_content(storage2_0) ++1 GenericInvestmentStorageBlock_storage_content(storage2_1) +-1 flow(electricityBus_storage2_0_1) ++1 flow(storage2_electricityBus_0_1) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage2_1_2)_: +-1 GenericInvestmentStorageBlock_storage_content(storage2_1) ++1 GenericInvestmentStorageBlock_storage_content(storage2_2) +-1 flow(electricityBus_storage2_1_2) ++1 flow(storage2_electricityBus_1_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage2_1_3)_: +-1 GenericInvestmentStorageBlock_storage_content(storage2_2) ++1 GenericInvestmentStorageBlock_storage_content(storage2_3) +-1 flow(electricityBus_storage2_1_3) ++1 flow(storage2_electricityBus_1_3) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage2_2_4)_: +-1 GenericInvestmentStorageBlock_storage_content(storage2_3) ++1 GenericInvestmentStorageBlock_storage_content(storage2_4) +-1 flow(electricityBus_storage2_2_4) ++1 flow(storage2_electricityBus_2_4) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage2_2_5)_: +-1 GenericInvestmentStorageBlock_storage_content(storage2_4) ++1 GenericInvestmentStorageBlock_storage_content(storage2_5) +-1 flow(electricityBus_storage2_2_5) ++1 flow(storage2_electricityBus_2_5) += 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage2_0_0)_: ++1 GenericInvestmentStorageBlock_storage_content(storage2_0) +-1 GenericInvestmentStorageBlock_total(storage2_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage2_0_1)_: ++1 GenericInvestmentStorageBlock_storage_content(storage2_1) +-1 GenericInvestmentStorageBlock_total(storage2_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage2_1_2)_: ++1 GenericInvestmentStorageBlock_storage_content(storage2_2) +-1 GenericInvestmentStorageBlock_total(storage2_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage2_1_3)_: ++1 GenericInvestmentStorageBlock_storage_content(storage2_3) +-1 GenericInvestmentStorageBlock_total(storage2_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage2_2_4)_: ++1 GenericInvestmentStorageBlock_storage_content(storage2_4) +-1 GenericInvestmentStorageBlock_total(storage2_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage2_2_5)_: ++1 GenericInvestmentStorageBlock_storage_content(storage2_5) +-1 GenericInvestmentStorageBlock_total(storage2_2) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_storage2_0_0) <= +inf + 0 <= flow(electricityBus_storage2_0_1) <= +inf + 0 <= flow(electricityBus_storage2_1_2) <= +inf + 0 <= flow(electricityBus_storage2_1_3) <= +inf + 0 <= flow(electricityBus_storage2_2_4) <= +inf + 0 <= flow(electricityBus_storage2_2_5) <= +inf + 0 <= flow(storage2_electricityBus_0_0) <= +inf + 0 <= flow(storage2_electricityBus_0_1) <= +inf + 0 <= flow(storage2_electricityBus_1_2) <= +inf + 0 <= flow(storage2_electricityBus_1_3) <= +inf + 0 <= flow(storage2_electricityBus_2_4) <= +inf + 0 <= flow(storage2_electricityBus_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage2_0) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage2_1) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage2_2) <= +inf + 0 <= InvestmentFlowBlock_invest(storage2_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage2_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_invest(storage2_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage2_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage2_1) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage2_2) <= +inf + 0 <= InvestmentFlowBlock_total(storage2_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage2_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_total(storage2_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage2_0) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage2_1) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage2_2) <= +inf + 0 <= InvestmentFlowBlock_old(storage2_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old(storage2_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old(storage2_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage2_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage2_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage2_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage2_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage2_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage2_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage2_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage2_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage2_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage2_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage2_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage2_electricityBus_2) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage2_0) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage2_1) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage2_2) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage2_3) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage2_4) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage2_5) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage2_0) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage2_1) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage2_2) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage2_0) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage2_1) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage2_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage2_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage2_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage2_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage2_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage2_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage2_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage2_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage2_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage2_2) <= +inf +end diff --git a/tests/lp_files/storage_invest_3_multi_period.lp b/tests/lp_files/storage_invest_3_multi_period.lp new file mode 100644 index 000000000..90e2f6839 --- /dev/null +++ b/tests/lp_files/storage_invest_3_multi_period.lp @@ -0,0 +1,327 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++101.97980198019803 InvestmentFlowBlock_invest(electricityBus_storage3_0) ++99.980198019801989 InvestmentFlowBlock_invest(electricityBus_storage3_1) ++98.019801980198025 InvestmentFlowBlock_invest(electricityBus_storage3_2) ++11.00820926255226 InvestmentFlowBlock_invest(storage3_electricityBus_0) ++10.792362022110058 InvestmentFlowBlock_invest(storage3_electricityBus_1) ++10.580747080500057 InvestmentFlowBlock_invest(storage3_electricityBus_2) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage3_0_0) ++1 flow(storage3_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage3_0_1) ++1 flow(storage3_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: +-1 flow(electricityBus_storage3_1_2) ++1 flow(storage3_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: +-1 flow(electricityBus_storage3_1_3) ++1 flow(storage3_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: +-1 flow(electricityBus_storage3_2_4) ++1 flow(storage3_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: +-1 flow(electricityBus_storage3_2_5) ++1 flow(storage3_electricityBus_2_5) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage3_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage3_0) ++1 InvestmentFlowBlock_total(electricityBus_storage3_0) += 10 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage3_1)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage3_1) ++1 InvestmentFlowBlock_old(electricityBus_storage3_1) +-1 InvestmentFlowBlock_total(electricityBus_storage3_0) ++1 InvestmentFlowBlock_total(electricityBus_storage3_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage3_2)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage3_2) ++1 InvestmentFlowBlock_old(electricityBus_storage3_2) +-1 InvestmentFlowBlock_total(electricityBus_storage3_1) ++1 InvestmentFlowBlock_total(electricityBus_storage3_2) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage3_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(storage3_electricityBus_0) ++1 InvestmentFlowBlock_total(storage3_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage3_electricityBus_1)_: +-1 InvestmentFlowBlock_invest(storage3_electricityBus_1) ++1 InvestmentFlowBlock_old(storage3_electricityBus_1) +-1 InvestmentFlowBlock_total(storage3_electricityBus_0) ++1 InvestmentFlowBlock_total(storage3_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage3_electricityBus_2)_: +-1 InvestmentFlowBlock_invest(storage3_electricityBus_2) ++1 InvestmentFlowBlock_old(storage3_electricityBus_2) +-1 InvestmentFlowBlock_total(storage3_electricityBus_1) ++1 InvestmentFlowBlock_total(storage3_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage3_0)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage3_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage3_1)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage3_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage3_2)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage3_0) ++1 InvestmentFlowBlock_old_end(electricityBus_storage3_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage3_electricityBus_0)_: ++1 InvestmentFlowBlock_old_end(storage3_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage3_electricityBus_1)_: ++1 InvestmentFlowBlock_old_end(storage3_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage3_electricityBus_2)_: ++1 InvestmentFlowBlock_old_end(storage3_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage3_0)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage3_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage3_1)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage3_1) += 10 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage3_2)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage3_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage3_electricityBus_0)_: ++1 InvestmentFlowBlock_old_exo(storage3_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage3_electricityBus_1)_: ++1 InvestmentFlowBlock_old_exo(storage3_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage3_electricityBus_2)_: ++1 InvestmentFlowBlock_old_exo(storage3_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage3_0)_: ++1 InvestmentFlowBlock_old(electricityBus_storage3_0) +-1 InvestmentFlowBlock_old_end(electricityBus_storage3_0) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage3_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage3_1)_: ++1 InvestmentFlowBlock_old(electricityBus_storage3_1) +-1 InvestmentFlowBlock_old_end(electricityBus_storage3_1) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage3_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage3_2)_: ++1 InvestmentFlowBlock_old(electricityBus_storage3_2) +-1 InvestmentFlowBlock_old_end(electricityBus_storage3_2) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage3_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage3_electricityBus_0)_: ++1 InvestmentFlowBlock_old(storage3_electricityBus_0) +-1 InvestmentFlowBlock_old_end(storage3_electricityBus_0) +-1 InvestmentFlowBlock_old_exo(storage3_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage3_electricityBus_1)_: ++1 InvestmentFlowBlock_old(storage3_electricityBus_1) +-1 InvestmentFlowBlock_old_end(storage3_electricityBus_1) +-1 InvestmentFlowBlock_old_exo(storage3_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage3_electricityBus_2)_: ++1 InvestmentFlowBlock_old(storage3_electricityBus_2) +-1 InvestmentFlowBlock_old_end(storage3_electricityBus_2) +-1 InvestmentFlowBlock_old_exo(storage3_electricityBus_2) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage3_0_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage3_0) ++1 flow(electricityBus_storage3_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage3_0_1)_: +-1 InvestmentFlowBlock_total(electricityBus_storage3_0) ++1 flow(electricityBus_storage3_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage3_1_2)_: +-1 InvestmentFlowBlock_total(electricityBus_storage3_1) ++1 flow(electricityBus_storage3_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage3_1_3)_: +-1 InvestmentFlowBlock_total(electricityBus_storage3_1) ++1 flow(electricityBus_storage3_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage3_2_4)_: +-1 InvestmentFlowBlock_total(electricityBus_storage3_2) ++1 flow(electricityBus_storage3_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage3_2_5)_: +-1 InvestmentFlowBlock_total(electricityBus_storage3_2) ++1 flow(electricityBus_storage3_2_5) +<= 0 + +c_u_InvestmentFlowBlock_max(storage3_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(storage3_electricityBus_0) ++1 flow(storage3_electricityBus_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(storage3_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(storage3_electricityBus_0) ++1 flow(storage3_electricityBus_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(storage3_electricityBus_1_2)_: +-1 InvestmentFlowBlock_total(storage3_electricityBus_1) ++1 flow(storage3_electricityBus_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(storage3_electricityBus_1_3)_: +-1 InvestmentFlowBlock_total(storage3_electricityBus_1) ++1 flow(storage3_electricityBus_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(storage3_electricityBus_2_4)_: +-1 InvestmentFlowBlock_total(storage3_electricityBus_2) ++1 flow(storage3_electricityBus_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(storage3_electricityBus_2_5)_: +-1 InvestmentFlowBlock_total(storage3_electricityBus_2) ++1 flow(storage3_electricityBus_2_5) +<= 0 + +c_e_GenericStorageBlock_balance_first(storage3)_: +-1 GenericStorageBlock_init_content(storage3) ++1 GenericStorageBlock_storage_content(storage3_0) +-1 flow(electricityBus_storage3_0_0) ++1 flow(storage3_electricityBus_0_0) += 0 + +c_e_GenericStorageBlock_balance(storage3_0_1)_: +-1 GenericStorageBlock_storage_content(storage3_0) ++1 GenericStorageBlock_storage_content(storage3_1) +-1 flow(electricityBus_storage3_0_1) ++1 flow(storage3_electricityBus_0_1) += 0 + +c_e_GenericStorageBlock_balance(storage3_1_2)_: +-1 GenericStorageBlock_storage_content(storage3_1) ++1 GenericStorageBlock_storage_content(storage3_2) +-1 flow(electricityBus_storage3_1_2) ++1 flow(storage3_electricityBus_1_2) += 0 + +c_e_GenericStorageBlock_balance(storage3_1_3)_: +-1 GenericStorageBlock_storage_content(storage3_2) ++1 GenericStorageBlock_storage_content(storage3_3) +-1 flow(electricityBus_storage3_1_3) ++1 flow(storage3_electricityBus_1_3) += 0 + +c_e_GenericStorageBlock_balance(storage3_2_4)_: +-1 GenericStorageBlock_storage_content(storage3_3) ++1 GenericStorageBlock_storage_content(storage3_4) +-1 flow(electricityBus_storage3_2_4) ++1 flow(storage3_electricityBus_2_4) += 0 + +c_e_GenericStorageBlock_balance(storage3_2_5)_: +-1 GenericStorageBlock_storage_content(storage3_4) ++1 GenericStorageBlock_storage_content(storage3_5) +-1 flow(electricityBus_storage3_2_5) ++1 flow(storage3_electricityBus_2_5) += 0 + +c_e_GenericStorageBlock_balanced_cstr(storage3)_: +-1 GenericStorageBlock_init_content(storage3) ++1 GenericStorageBlock_storage_content(storage3_5) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_storage3_0_0) <= +inf + 0 <= flow(electricityBus_storage3_0_1) <= +inf + 0 <= flow(electricityBus_storage3_1_2) <= +inf + 0 <= flow(electricityBus_storage3_1_3) <= +inf + 0 <= flow(electricityBus_storage3_2_4) <= +inf + 0 <= flow(electricityBus_storage3_2_5) <= +inf + 0 <= flow(storage3_electricityBus_0_0) <= +inf + 0 <= flow(storage3_electricityBus_0_1) <= +inf + 0 <= flow(storage3_electricityBus_1_2) <= +inf + 0 <= flow(storage3_electricityBus_1_3) <= +inf + 0 <= flow(storage3_electricityBus_2_4) <= +inf + 0 <= flow(storage3_electricityBus_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage3_0) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage3_1) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage3_2) <= +inf + 0 <= InvestmentFlowBlock_invest(storage3_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage3_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_invest(storage3_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage3_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage3_1) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage3_2) <= +inf + 0 <= InvestmentFlowBlock_total(storage3_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage3_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_total(storage3_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage3_0) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage3_1) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage3_2) <= +inf + 0 <= InvestmentFlowBlock_old(storage3_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old(storage3_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old(storage3_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage3_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage3_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage3_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage3_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage3_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage3_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage3_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage3_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage3_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage3_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage3_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage3_electricityBus_2) <= +inf + 0 <= GenericStorageBlock_storage_content(storage3_0) <= 5000 + 0 <= GenericStorageBlock_storage_content(storage3_1) <= 5000 + 0 <= GenericStorageBlock_storage_content(storage3_2) <= 5000 + 0 <= GenericStorageBlock_storage_content(storage3_3) <= 5000 + 0 <= GenericStorageBlock_storage_content(storage3_4) <= 5000 + 0 <= GenericStorageBlock_storage_content(storage3_5) <= 5000 + 0 <= GenericStorageBlock_init_content(storage3) <= 5000 +end diff --git a/tests/lp_files/storage_invest_4_multi_period.lp b/tests/lp_files/storage_invest_4_multi_period.lp new file mode 100644 index 000000000..a3243521c --- /dev/null +++ b/tests/lp_files/storage_invest_4_multi_period.lp @@ -0,0 +1,205 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++149.3643564356436 GenericInvestmentStorageBlock_invest(storage4_0) ++146.43564356435647 GenericInvestmentStorageBlock_invest(storage4_1) ++143.56435643564359 GenericInvestmentStorageBlock_invest(storage4_2) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage4_0_0) ++1 flow(storage4_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage4_0_1) ++1 flow(storage4_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: +-1 flow(electricityBus_storage4_1_2) ++1 flow(storage4_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: +-1 flow(electricityBus_storage4_1_3) ++1 flow(storage4_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: +-1 flow(electricityBus_storage4_2_4) ++1 flow(storage4_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: +-1 flow(electricityBus_storage4_2_5) ++1 flow(storage4_electricityBus_2_5) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage4_0)_: +-1 GenericInvestmentStorageBlock_invest(storage4_0) ++1 GenericInvestmentStorageBlock_total(storage4_0) += 100 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage4_1)_: +-1 GenericInvestmentStorageBlock_invest(storage4_1) ++1 GenericInvestmentStorageBlock_old(storage4_1) +-1 GenericInvestmentStorageBlock_total(storage4_0) ++1 GenericInvestmentStorageBlock_total(storage4_1) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage4_2)_: +-1 GenericInvestmentStorageBlock_invest(storage4_2) ++1 GenericInvestmentStorageBlock_old(storage4_2) +-1 GenericInvestmentStorageBlock_total(storage4_1) ++1 GenericInvestmentStorageBlock_total(storage4_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage4_0)_: ++1 GenericInvestmentStorageBlock_old_end(storage4_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage4_1)_: ++1 GenericInvestmentStorageBlock_old_end(storage4_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage4_2)_: +-1 GenericInvestmentStorageBlock_invest(storage4_0) ++1 GenericInvestmentStorageBlock_old_end(storage4_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage4_0)_: ++1 GenericInvestmentStorageBlock_old_exo(storage4_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage4_1)_: ++1 GenericInvestmentStorageBlock_old_exo(storage4_1) += 100 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage4_2)_: ++1 GenericInvestmentStorageBlock_old_exo(storage4_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage4_0)_: ++1 GenericInvestmentStorageBlock_old(storage4_0) +-1 GenericInvestmentStorageBlock_old_end(storage4_0) +-1 GenericInvestmentStorageBlock_old_exo(storage4_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage4_1)_: ++1 GenericInvestmentStorageBlock_old(storage4_1) +-1 GenericInvestmentStorageBlock_old_end(storage4_1) +-1 GenericInvestmentStorageBlock_old_exo(storage4_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage4_2)_: ++1 GenericInvestmentStorageBlock_old(storage4_2) +-1 GenericInvestmentStorageBlock_old_end(storage4_2) +-1 GenericInvestmentStorageBlock_old_exo(storage4_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage4_0_1)_: +-1 GenericInvestmentStorageBlock_storage_content(storage4_0) ++1 GenericInvestmentStorageBlock_storage_content(storage4_1) +-1 flow(electricityBus_storage4_0_1) ++1 flow(storage4_electricityBus_0_1) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage4_1_2)_: +-1 GenericInvestmentStorageBlock_storage_content(storage4_1) ++1 GenericInvestmentStorageBlock_storage_content(storage4_2) +-1 flow(electricityBus_storage4_1_2) ++1 flow(storage4_electricityBus_1_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage4_1_3)_: +-1 GenericInvestmentStorageBlock_storage_content(storage4_2) ++1 GenericInvestmentStorageBlock_storage_content(storage4_3) +-1 flow(electricityBus_storage4_1_3) ++1 flow(storage4_electricityBus_1_3) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage4_2_4)_: +-1 GenericInvestmentStorageBlock_storage_content(storage4_3) ++1 GenericInvestmentStorageBlock_storage_content(storage4_4) +-1 flow(electricityBus_storage4_2_4) ++1 flow(storage4_electricityBus_2_4) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage4_2_5)_: +-1 GenericInvestmentStorageBlock_storage_content(storage4_4) ++1 GenericInvestmentStorageBlock_storage_content(storage4_5) +-1 flow(electricityBus_storage4_2_5) ++1 flow(storage4_electricityBus_2_5) += 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage4_0_0)_: ++1 GenericInvestmentStorageBlock_storage_content(storage4_0) +-1 GenericInvestmentStorageBlock_total(storage4_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage4_0_1)_: ++1 GenericInvestmentStorageBlock_storage_content(storage4_1) +-1 GenericInvestmentStorageBlock_total(storage4_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage4_1_2)_: ++1 GenericInvestmentStorageBlock_storage_content(storage4_2) +-1 GenericInvestmentStorageBlock_total(storage4_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage4_1_3)_: ++1 GenericInvestmentStorageBlock_storage_content(storage4_3) +-1 GenericInvestmentStorageBlock_total(storage4_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage4_2_4)_: ++1 GenericInvestmentStorageBlock_storage_content(storage4_4) +-1 GenericInvestmentStorageBlock_total(storage4_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage4_2_5)_: ++1 GenericInvestmentStorageBlock_storage_content(storage4_5) +-1 GenericInvestmentStorageBlock_total(storage4_2) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_storage4_0_0) <= 80 + 0 <= flow(electricityBus_storage4_0_1) <= 80 + 0 <= flow(electricityBus_storage4_1_2) <= 80 + 0 <= flow(electricityBus_storage4_1_3) <= 80 + 0 <= flow(electricityBus_storage4_2_4) <= 80 + 0 <= flow(electricityBus_storage4_2_5) <= 80 + 0 <= flow(storage4_electricityBus_0_0) <= 100 + 0 <= flow(storage4_electricityBus_0_1) <= 100 + 0 <= flow(storage4_electricityBus_1_2) <= 100 + 0 <= flow(storage4_electricityBus_1_3) <= 100 + 0 <= flow(storage4_electricityBus_2_4) <= 100 + 0 <= flow(storage4_electricityBus_2_5) <= 100 + 0 <= GenericInvestmentStorageBlock_storage_content(storage4_0) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage4_1) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage4_2) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage4_3) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage4_4) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage4_5) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage4_0) <= 500 + 0 <= GenericInvestmentStorageBlock_invest(storage4_1) <= 500 + 0 <= GenericInvestmentStorageBlock_invest(storage4_2) <= 500 + 0 <= GenericInvestmentStorageBlock_total(storage4_0) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage4_1) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage4_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage4_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage4_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage4_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage4_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage4_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage4_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage4_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage4_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage4_2) <= +inf +end diff --git a/tests/lp_files/storage_invest_5_multi_period.lp b/tests/lp_files/storage_invest_5_multi_period.lp new file mode 100644 index 000000000..0e6e27416 --- /dev/null +++ b/tests/lp_files/storage_invest_5_multi_period.lp @@ -0,0 +1,338 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++121.09030188807486 InvestmentFlowBlock_invest(electricityBus_storage5_0) ++118.71598224321065 InvestmentFlowBlock_invest(electricityBus_storage5_1) ++116.38821788550062 InvestmentFlowBlock_invest(electricityBus_storage5_2) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage5_0_0) ++1 flow(storage5_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage5_0_1) ++1 flow(storage5_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: +-1 flow(electricityBus_storage5_1_2) ++1 flow(storage5_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: +-1 flow(electricityBus_storage5_1_3) ++1 flow(storage5_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: +-1 flow(electricityBus_storage5_2_4) ++1 flow(storage5_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: +-1 flow(electricityBus_storage5_2_5) ++1 flow(storage5_electricityBus_2_5) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage5_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage5_0) ++1 InvestmentFlowBlock_total(electricityBus_storage5_0) += 110 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage5_1)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage5_1) ++1 InvestmentFlowBlock_old(electricityBus_storage5_1) +-1 InvestmentFlowBlock_total(electricityBus_storage5_0) ++1 InvestmentFlowBlock_total(electricityBus_storage5_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage5_2)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage5_2) ++1 InvestmentFlowBlock_old(electricityBus_storage5_2) +-1 InvestmentFlowBlock_total(electricityBus_storage5_1) ++1 InvestmentFlowBlock_total(electricityBus_storage5_2) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage5_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(storage5_electricityBus_0) ++1 InvestmentFlowBlock_total(storage5_electricityBus_0) += 100 + +c_e_InvestmentFlowBlock_total_rule(storage5_electricityBus_1)_: +-1 InvestmentFlowBlock_invest(storage5_electricityBus_1) ++1 InvestmentFlowBlock_old(storage5_electricityBus_1) +-1 InvestmentFlowBlock_total(storage5_electricityBus_0) ++1 InvestmentFlowBlock_total(storage5_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage5_electricityBus_2)_: +-1 InvestmentFlowBlock_invest(storage5_electricityBus_2) ++1 InvestmentFlowBlock_old(storage5_electricityBus_2) +-1 InvestmentFlowBlock_total(storage5_electricityBus_1) ++1 InvestmentFlowBlock_total(storage5_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage5_0)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage5_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage5_1)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage5_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage5_2)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage5_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage5_electricityBus_0)_: ++1 InvestmentFlowBlock_old_end(storage5_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage5_electricityBus_1)_: ++1 InvestmentFlowBlock_old_end(storage5_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage5_electricityBus_2)_: ++1 InvestmentFlowBlock_old_end(storage5_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage5_0)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage5_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage5_1)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage5_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage5_2)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage5_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage5_electricityBus_0)_: ++1 InvestmentFlowBlock_old_exo(storage5_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage5_electricityBus_1)_: ++1 InvestmentFlowBlock_old_exo(storage5_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage5_electricityBus_2)_: ++1 InvestmentFlowBlock_old_exo(storage5_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage5_0)_: ++1 InvestmentFlowBlock_old(electricityBus_storage5_0) +-1 InvestmentFlowBlock_old_end(electricityBus_storage5_0) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage5_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage5_1)_: ++1 InvestmentFlowBlock_old(electricityBus_storage5_1) +-1 InvestmentFlowBlock_old_end(electricityBus_storage5_1) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage5_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage5_2)_: ++1 InvestmentFlowBlock_old(electricityBus_storage5_2) +-1 InvestmentFlowBlock_old_end(electricityBus_storage5_2) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage5_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage5_electricityBus_0)_: ++1 InvestmentFlowBlock_old(storage5_electricityBus_0) +-1 InvestmentFlowBlock_old_end(storage5_electricityBus_0) +-1 InvestmentFlowBlock_old_exo(storage5_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage5_electricityBus_1)_: ++1 InvestmentFlowBlock_old(storage5_electricityBus_1) +-1 InvestmentFlowBlock_old_end(storage5_electricityBus_1) +-1 InvestmentFlowBlock_old_exo(storage5_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage5_electricityBus_2)_: ++1 InvestmentFlowBlock_old(storage5_electricityBus_2) +-1 InvestmentFlowBlock_old_end(storage5_electricityBus_2) +-1 InvestmentFlowBlock_old_exo(storage5_electricityBus_2) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage5_0_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage5_0) ++1 flow(electricityBus_storage5_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage5_0_1)_: +-1 InvestmentFlowBlock_total(electricityBus_storage5_0) ++1 flow(electricityBus_storage5_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage5_1_2)_: +-1 InvestmentFlowBlock_total(electricityBus_storage5_1) ++1 flow(electricityBus_storage5_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage5_1_3)_: +-1 InvestmentFlowBlock_total(electricityBus_storage5_1) ++1 flow(electricityBus_storage5_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage5_2_4)_: +-1 InvestmentFlowBlock_total(electricityBus_storage5_2) ++1 flow(electricityBus_storage5_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage5_2_5)_: +-1 InvestmentFlowBlock_total(electricityBus_storage5_2) ++1 flow(electricityBus_storage5_2_5) +<= 0 + +c_u_InvestmentFlowBlock_max(storage5_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(storage5_electricityBus_0) ++1 flow(storage5_electricityBus_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(storage5_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(storage5_electricityBus_0) ++1 flow(storage5_electricityBus_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(storage5_electricityBus_1_2)_: +-1 InvestmentFlowBlock_total(storage5_electricityBus_1) ++1 flow(storage5_electricityBus_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(storage5_electricityBus_1_3)_: +-1 InvestmentFlowBlock_total(storage5_electricityBus_1) ++1 flow(storage5_electricityBus_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(storage5_electricityBus_2_4)_: +-1 InvestmentFlowBlock_total(storage5_electricityBus_2) ++1 flow(storage5_electricityBus_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(storage5_electricityBus_2_5)_: +-1 InvestmentFlowBlock_total(storage5_electricityBus_2) ++1 flow(storage5_electricityBus_2_5) +<= 0 + +c_e_GenericStorageBlock_balance_first(storage5)_: +-1 GenericStorageBlock_init_content(storage5) ++1 GenericStorageBlock_storage_content(storage5_0) +-1 flow(electricityBus_storage5_0_0) ++1 flow(storage5_electricityBus_0_0) += 0 + +c_e_GenericStorageBlock_balance(storage5_0_1)_: +-1 GenericStorageBlock_storage_content(storage5_0) ++1 GenericStorageBlock_storage_content(storage5_1) +-1 flow(electricityBus_storage5_0_1) ++1 flow(storage5_electricityBus_0_1) += 0 + +c_e_GenericStorageBlock_balance(storage5_1_2)_: +-1 GenericStorageBlock_storage_content(storage5_1) ++1 GenericStorageBlock_storage_content(storage5_2) +-1 flow(electricityBus_storage5_1_2) ++1 flow(storage5_electricityBus_1_2) += 0 + +c_e_GenericStorageBlock_balance(storage5_1_3)_: +-1 GenericStorageBlock_storage_content(storage5_2) ++1 GenericStorageBlock_storage_content(storage5_3) +-1 flow(electricityBus_storage5_1_3) ++1 flow(storage5_electricityBus_1_3) += 0 + +c_e_GenericStorageBlock_balance(storage5_2_4)_: +-1 GenericStorageBlock_storage_content(storage5_3) ++1 GenericStorageBlock_storage_content(storage5_4) +-1 flow(electricityBus_storage5_2_4) ++1 flow(storage5_electricityBus_2_4) += 0 + +c_e_GenericStorageBlock_balance(storage5_2_5)_: +-1 GenericStorageBlock_storage_content(storage5_4) ++1 GenericStorageBlock_storage_content(storage5_5) +-1 flow(electricityBus_storage5_2_5) ++1 flow(storage5_electricityBus_2_5) += 0 + +c_e_GenericStorageBlock_balanced_cstr(storage5)_: +-1 GenericStorageBlock_init_content(storage5) ++1 GenericStorageBlock_storage_content(storage5_5) += 0 + +c_e_GenericStorageBlock_power_coupled(storage5_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage5_0) ++1.1000000000000001 InvestmentFlowBlock_total(storage5_electricityBus_0) += 0 + +c_e_GenericStorageBlock_power_coupled(storage5_1)_: +-1 InvestmentFlowBlock_total(electricityBus_storage5_1) ++1.1000000000000001 InvestmentFlowBlock_total(storage5_electricityBus_1) += 0 + +c_e_GenericStorageBlock_power_coupled(storage5_2)_: +-1 InvestmentFlowBlock_total(electricityBus_storage5_2) ++1.1000000000000001 InvestmentFlowBlock_total(storage5_electricityBus_2) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_storage5_0_0) <= +inf + 0 <= flow(electricityBus_storage5_0_1) <= +inf + 0 <= flow(electricityBus_storage5_1_2) <= +inf + 0 <= flow(electricityBus_storage5_1_3) <= +inf + 0 <= flow(electricityBus_storage5_2_4) <= +inf + 0 <= flow(electricityBus_storage5_2_5) <= +inf + 0 <= flow(storage5_electricityBus_0_0) <= +inf + 0 <= flow(storage5_electricityBus_0_1) <= +inf + 0 <= flow(storage5_electricityBus_1_2) <= +inf + 0 <= flow(storage5_electricityBus_1_3) <= +inf + 0 <= flow(storage5_electricityBus_2_4) <= +inf + 0 <= flow(storage5_electricityBus_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage5_0) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage5_1) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage5_2) <= +inf + 0 <= InvestmentFlowBlock_invest(storage5_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage5_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_invest(storage5_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage5_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage5_1) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage5_2) <= +inf + 0 <= InvestmentFlowBlock_total(storage5_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage5_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_total(storage5_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage5_0) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage5_1) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage5_2) <= +inf + 0 <= InvestmentFlowBlock_old(storage5_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old(storage5_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old(storage5_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage5_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage5_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage5_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage5_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage5_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage5_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage5_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage5_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage5_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage5_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage5_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage5_electricityBus_2) <= +inf + 0 <= GenericStorageBlock_storage_content(storage5_0) <= 10000 + 0 <= GenericStorageBlock_storage_content(storage5_1) <= 10000 + 0 <= GenericStorageBlock_storage_content(storage5_2) <= 10000 + 0 <= GenericStorageBlock_storage_content(storage5_3) <= 10000 + 0 <= GenericStorageBlock_storage_content(storage5_4) <= 10000 + 0 <= GenericStorageBlock_storage_content(storage5_5) <= 10000 + 0 <= GenericStorageBlock_init_content(storage5) <= 10000 +end diff --git a/tests/lp_files/storage_invest_6_multi_period.lp b/tests/lp_files/storage_invest_6_multi_period.lp new file mode 100644 index 000000000..f3eda648a --- /dev/null +++ b/tests/lp_files/storage_invest_6_multi_period.lp @@ -0,0 +1,434 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++177.35448256334197 GenericInvestmentStorageBlock_invest(storage6_0) ++173.87694368955096 GenericInvestmentStorageBlock_invest(storage6_1) ++170.46759185250093 GenericInvestmentStorageBlock_invest(storage6_2) ++121.09030188807486 InvestmentFlowBlock_invest(electricityBus_storage6_0) ++118.71598224321065 InvestmentFlowBlock_invest(electricityBus_storage6_1) ++116.38821788550062 InvestmentFlowBlock_invest(electricityBus_storage6_2) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage6_0_0) ++1 flow(storage6_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage6_0_1) ++1 flow(storage6_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: +-1 flow(electricityBus_storage6_1_2) ++1 flow(storage6_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: +-1 flow(electricityBus_storage6_1_3) ++1 flow(storage6_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: +-1 flow(electricityBus_storage6_2_4) ++1 flow(storage6_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: +-1 flow(electricityBus_storage6_2_5) ++1 flow(storage6_electricityBus_2_5) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage6_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage6_0) ++1 InvestmentFlowBlock_total(electricityBus_storage6_0) += 110 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage6_1)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage6_1) ++1 InvestmentFlowBlock_old(electricityBus_storage6_1) +-1 InvestmentFlowBlock_total(electricityBus_storage6_0) ++1 InvestmentFlowBlock_total(electricityBus_storage6_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage6_2)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage6_2) ++1 InvestmentFlowBlock_old(electricityBus_storage6_2) +-1 InvestmentFlowBlock_total(electricityBus_storage6_1) ++1 InvestmentFlowBlock_total(electricityBus_storage6_2) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage6_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(storage6_electricityBus_0) ++1 InvestmentFlowBlock_total(storage6_electricityBus_0) += 100 + +c_e_InvestmentFlowBlock_total_rule(storage6_electricityBus_1)_: +-1 InvestmentFlowBlock_invest(storage6_electricityBus_1) ++1 InvestmentFlowBlock_old(storage6_electricityBus_1) +-1 InvestmentFlowBlock_total(storage6_electricityBus_0) ++1 InvestmentFlowBlock_total(storage6_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage6_electricityBus_2)_: +-1 InvestmentFlowBlock_invest(storage6_electricityBus_2) ++1 InvestmentFlowBlock_old(storage6_electricityBus_2) +-1 InvestmentFlowBlock_total(storage6_electricityBus_1) ++1 InvestmentFlowBlock_total(storage6_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage6_0)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage6_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage6_1)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage6_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage6_2)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage6_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage6_electricityBus_0)_: ++1 InvestmentFlowBlock_old_end(storage6_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage6_electricityBus_1)_: ++1 InvestmentFlowBlock_old_end(storage6_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage6_electricityBus_2)_: ++1 InvestmentFlowBlock_old_end(storage6_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage6_0)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage6_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage6_1)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage6_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage6_2)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage6_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage6_electricityBus_0)_: ++1 InvestmentFlowBlock_old_exo(storage6_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage6_electricityBus_1)_: ++1 InvestmentFlowBlock_old_exo(storage6_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage6_electricityBus_2)_: ++1 InvestmentFlowBlock_old_exo(storage6_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage6_0)_: ++1 InvestmentFlowBlock_old(electricityBus_storage6_0) +-1 InvestmentFlowBlock_old_end(electricityBus_storage6_0) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage6_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage6_1)_: ++1 InvestmentFlowBlock_old(electricityBus_storage6_1) +-1 InvestmentFlowBlock_old_end(electricityBus_storage6_1) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage6_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage6_2)_: ++1 InvestmentFlowBlock_old(electricityBus_storage6_2) +-1 InvestmentFlowBlock_old_end(electricityBus_storage6_2) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage6_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage6_electricityBus_0)_: ++1 InvestmentFlowBlock_old(storage6_electricityBus_0) +-1 InvestmentFlowBlock_old_end(storage6_electricityBus_0) +-1 InvestmentFlowBlock_old_exo(storage6_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage6_electricityBus_1)_: ++1 InvestmentFlowBlock_old(storage6_electricityBus_1) +-1 InvestmentFlowBlock_old_end(storage6_electricityBus_1) +-1 InvestmentFlowBlock_old_exo(storage6_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage6_electricityBus_2)_: ++1 InvestmentFlowBlock_old(storage6_electricityBus_2) +-1 InvestmentFlowBlock_old_end(storage6_electricityBus_2) +-1 InvestmentFlowBlock_old_exo(storage6_electricityBus_2) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage6_0_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage6_0) ++1 flow(electricityBus_storage6_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage6_0_1)_: +-1 InvestmentFlowBlock_total(electricityBus_storage6_0) ++1 flow(electricityBus_storage6_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage6_1_2)_: +-1 InvestmentFlowBlock_total(electricityBus_storage6_1) ++1 flow(electricityBus_storage6_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage6_1_3)_: +-1 InvestmentFlowBlock_total(electricityBus_storage6_1) ++1 flow(electricityBus_storage6_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage6_2_4)_: +-1 InvestmentFlowBlock_total(electricityBus_storage6_2) ++1 flow(electricityBus_storage6_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage6_2_5)_: +-1 InvestmentFlowBlock_total(electricityBus_storage6_2) ++1 flow(electricityBus_storage6_2_5) +<= 0 + +c_u_InvestmentFlowBlock_max(storage6_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(storage6_electricityBus_0) ++1 flow(storage6_electricityBus_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(storage6_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(storage6_electricityBus_0) ++1 flow(storage6_electricityBus_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(storage6_electricityBus_1_2)_: +-1 InvestmentFlowBlock_total(storage6_electricityBus_1) ++1 flow(storage6_electricityBus_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(storage6_electricityBus_1_3)_: +-1 InvestmentFlowBlock_total(storage6_electricityBus_1) ++1 flow(storage6_electricityBus_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(storage6_electricityBus_2_4)_: +-1 InvestmentFlowBlock_total(storage6_electricityBus_2) ++1 flow(storage6_electricityBus_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(storage6_electricityBus_2_5)_: +-1 InvestmentFlowBlock_total(storage6_electricityBus_2) ++1 flow(storage6_electricityBus_2_5) +<= 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage6_0)_: +-1 GenericInvestmentStorageBlock_invest(storage6_0) ++1 GenericInvestmentStorageBlock_total(storage6_0) += 1000 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage6_1)_: +-1 GenericInvestmentStorageBlock_invest(storage6_1) ++1 GenericInvestmentStorageBlock_old(storage6_1) +-1 GenericInvestmentStorageBlock_total(storage6_0) ++1 GenericInvestmentStorageBlock_total(storage6_1) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage6_2)_: +-1 GenericInvestmentStorageBlock_invest(storage6_2) ++1 GenericInvestmentStorageBlock_old(storage6_2) +-1 GenericInvestmentStorageBlock_total(storage6_1) ++1 GenericInvestmentStorageBlock_total(storage6_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage6_0)_: ++1 GenericInvestmentStorageBlock_old_end(storage6_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage6_1)_: ++1 GenericInvestmentStorageBlock_old_end(storage6_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage6_2)_: ++1 GenericInvestmentStorageBlock_old_end(storage6_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage6_0)_: ++1 GenericInvestmentStorageBlock_old_exo(storage6_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage6_1)_: ++1 GenericInvestmentStorageBlock_old_exo(storage6_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage6_2)_: ++1 GenericInvestmentStorageBlock_old_exo(storage6_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage6_0)_: ++1 GenericInvestmentStorageBlock_old(storage6_0) +-1 GenericInvestmentStorageBlock_old_end(storage6_0) +-1 GenericInvestmentStorageBlock_old_exo(storage6_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage6_1)_: ++1 GenericInvestmentStorageBlock_old(storage6_1) +-1 GenericInvestmentStorageBlock_old_end(storage6_1) +-1 GenericInvestmentStorageBlock_old_exo(storage6_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage6_2)_: ++1 GenericInvestmentStorageBlock_old(storage6_2) +-1 GenericInvestmentStorageBlock_old_end(storage6_2) +-1 GenericInvestmentStorageBlock_old_exo(storage6_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage6_0_1)_: +-1 GenericInvestmentStorageBlock_storage_content(storage6_0) ++1 GenericInvestmentStorageBlock_storage_content(storage6_1) +-1 flow(electricityBus_storage6_0_1) ++1 flow(storage6_electricityBus_0_1) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage6_1_2)_: +-1 GenericInvestmentStorageBlock_storage_content(storage6_1) ++1 GenericInvestmentStorageBlock_storage_content(storage6_2) +-1 flow(electricityBus_storage6_1_2) ++1 flow(storage6_electricityBus_1_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage6_1_3)_: +-1 GenericInvestmentStorageBlock_storage_content(storage6_2) ++1 GenericInvestmentStorageBlock_storage_content(storage6_3) +-1 flow(electricityBus_storage6_1_3) ++1 flow(storage6_electricityBus_1_3) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage6_2_4)_: +-1 GenericInvestmentStorageBlock_storage_content(storage6_3) ++1 GenericInvestmentStorageBlock_storage_content(storage6_4) +-1 flow(electricityBus_storage6_2_4) ++1 flow(storage6_electricityBus_2_4) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage6_2_5)_: +-1 GenericInvestmentStorageBlock_storage_content(storage6_4) ++1 GenericInvestmentStorageBlock_storage_content(storage6_5) +-1 flow(electricityBus_storage6_2_5) ++1 flow(storage6_electricityBus_2_5) += 0 + +c_e_GenericInvestmentStorageBlock_power_coupled(storage6_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage6_0) ++1.1000000000000001 InvestmentFlowBlock_total(storage6_electricityBus_0) += 0 + +c_e_GenericInvestmentStorageBlock_power_coupled(storage6_1)_: +-1 InvestmentFlowBlock_total(electricityBus_storage6_1) ++1.1000000000000001 InvestmentFlowBlock_total(storage6_electricityBus_1) += 0 + +c_e_GenericInvestmentStorageBlock_power_coupled(storage6_2)_: +-1 InvestmentFlowBlock_total(electricityBus_storage6_2) ++1.1000000000000001 InvestmentFlowBlock_total(storage6_electricityBus_2) += 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage6_0_0)_: ++1 GenericInvestmentStorageBlock_storage_content(storage6_0) +-1 GenericInvestmentStorageBlock_total(storage6_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage6_0_1)_: ++1 GenericInvestmentStorageBlock_storage_content(storage6_1) +-1 GenericInvestmentStorageBlock_total(storage6_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage6_1_2)_: ++1 GenericInvestmentStorageBlock_storage_content(storage6_2) +-1 GenericInvestmentStorageBlock_total(storage6_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage6_1_3)_: ++1 GenericInvestmentStorageBlock_storage_content(storage6_3) +-1 GenericInvestmentStorageBlock_total(storage6_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage6_2_4)_: ++1 GenericInvestmentStorageBlock_storage_content(storage6_4) +-1 GenericInvestmentStorageBlock_total(storage6_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage6_2_5)_: ++1 GenericInvestmentStorageBlock_storage_content(storage6_5) +-1 GenericInvestmentStorageBlock_total(storage6_2) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_storage6_0_0) <= +inf + 0 <= flow(electricityBus_storage6_0_1) <= +inf + 0 <= flow(electricityBus_storage6_1_2) <= +inf + 0 <= flow(electricityBus_storage6_1_3) <= +inf + 0 <= flow(electricityBus_storage6_2_4) <= +inf + 0 <= flow(electricityBus_storage6_2_5) <= +inf + 0 <= flow(storage6_electricityBus_0_0) <= +inf + 0 <= flow(storage6_electricityBus_0_1) <= +inf + 0 <= flow(storage6_electricityBus_1_2) <= +inf + 0 <= flow(storage6_electricityBus_1_3) <= +inf + 0 <= flow(storage6_electricityBus_2_4) <= +inf + 0 <= flow(storage6_electricityBus_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage6_0) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage6_1) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage6_2) <= +inf + 0 <= InvestmentFlowBlock_invest(storage6_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage6_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_invest(storage6_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage6_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage6_1) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage6_2) <= +inf + 0 <= InvestmentFlowBlock_total(storage6_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage6_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_total(storage6_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage6_0) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage6_1) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage6_2) <= +inf + 0 <= InvestmentFlowBlock_old(storage6_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old(storage6_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old(storage6_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage6_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage6_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage6_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage6_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage6_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage6_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage6_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage6_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage6_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage6_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage6_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage6_electricityBus_2) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage6_0) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage6_1) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage6_2) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage6_3) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage6_4) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage6_5) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage6_0) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage6_1) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage6_2) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage6_0) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage6_1) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage6_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage6_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage6_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage6_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage6_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage6_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage6_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage6_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage6_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage6_2) <= +inf +end diff --git a/tests/lp_files/storage_invest_minimum_multi_period.lp b/tests/lp_files/storage_invest_minimum_multi_period.lp new file mode 100644 index 000000000..4104411f0 --- /dev/null +++ b/tests/lp_files/storage_invest_minimum_multi_period.lp @@ -0,0 +1,204 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++212.02333722461535 GenericInvestmentStorageBlock_invest(storage1_0) ++207.86601688687779 GenericInvestmentStorageBlock_invest(storage1_1) ++203.79021263419389 GenericInvestmentStorageBlock_invest(storage1_2) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage1_0_0) ++1 flow(storage1_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage1_0_1) ++1 flow(storage1_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: +-1 flow(electricityBus_storage1_1_2) ++1 flow(storage1_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: +-1 flow(electricityBus_storage1_1_3) ++1 flow(storage1_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: +-1 flow(electricityBus_storage1_2_4) ++1 flow(storage1_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: +-1 flow(electricityBus_storage1_2_5) ++1 flow(storage1_electricityBus_2_5) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage1_0)_: +-1 GenericInvestmentStorageBlock_invest(storage1_0) ++1 GenericInvestmentStorageBlock_total(storage1_0) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage1_1)_: +-1 GenericInvestmentStorageBlock_invest(storage1_1) ++1 GenericInvestmentStorageBlock_old(storage1_1) +-1 GenericInvestmentStorageBlock_total(storage1_0) ++1 GenericInvestmentStorageBlock_total(storage1_1) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage1_2)_: +-1 GenericInvestmentStorageBlock_invest(storage1_2) ++1 GenericInvestmentStorageBlock_old(storage1_2) +-1 GenericInvestmentStorageBlock_total(storage1_1) ++1 GenericInvestmentStorageBlock_total(storage1_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage1_0)_: ++1 GenericInvestmentStorageBlock_old_end(storage1_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage1_1)_: ++1 GenericInvestmentStorageBlock_old_end(storage1_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage1_2)_: ++1 GenericInvestmentStorageBlock_old_end(storage1_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage1_0)_: ++1 GenericInvestmentStorageBlock_old_exo(storage1_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage1_1)_: ++1 GenericInvestmentStorageBlock_old_exo(storage1_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage1_2)_: ++1 GenericInvestmentStorageBlock_old_exo(storage1_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage1_0)_: ++1 GenericInvestmentStorageBlock_old(storage1_0) +-1 GenericInvestmentStorageBlock_old_end(storage1_0) +-1 GenericInvestmentStorageBlock_old_exo(storage1_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage1_1)_: ++1 GenericInvestmentStorageBlock_old(storage1_1) +-1 GenericInvestmentStorageBlock_old_end(storage1_1) +-1 GenericInvestmentStorageBlock_old_exo(storage1_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage1_2)_: ++1 GenericInvestmentStorageBlock_old(storage1_2) +-1 GenericInvestmentStorageBlock_old_end(storage1_2) +-1 GenericInvestmentStorageBlock_old_exo(storage1_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage1_0_1)_: +-1 GenericInvestmentStorageBlock_storage_content(storage1_0) ++1 GenericInvestmentStorageBlock_storage_content(storage1_1) +-1 flow(electricityBus_storage1_0_1) ++1 flow(storage1_electricityBus_0_1) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage1_1_2)_: +-1 GenericInvestmentStorageBlock_storage_content(storage1_1) ++1 GenericInvestmentStorageBlock_storage_content(storage1_2) +-1 flow(electricityBus_storage1_1_2) ++1 flow(storage1_electricityBus_1_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage1_1_3)_: +-1 GenericInvestmentStorageBlock_storage_content(storage1_2) ++1 GenericInvestmentStorageBlock_storage_content(storage1_3) +-1 flow(electricityBus_storage1_1_3) ++1 flow(storage1_electricityBus_1_3) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage1_2_4)_: +-1 GenericInvestmentStorageBlock_storage_content(storage1_3) ++1 GenericInvestmentStorageBlock_storage_content(storage1_4) +-1 flow(electricityBus_storage1_2_4) ++1 flow(storage1_electricityBus_2_4) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage1_2_5)_: +-1 GenericInvestmentStorageBlock_storage_content(storage1_4) ++1 GenericInvestmentStorageBlock_storage_content(storage1_5) +-1 flow(electricityBus_storage1_2_5) ++1 flow(storage1_electricityBus_2_5) += 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0_0)_: ++1 GenericInvestmentStorageBlock_storage_content(storage1_0) +-1 GenericInvestmentStorageBlock_total(storage1_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0_1)_: ++1 GenericInvestmentStorageBlock_storage_content(storage1_1) +-1 GenericInvestmentStorageBlock_total(storage1_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_1_2)_: ++1 GenericInvestmentStorageBlock_storage_content(storage1_2) +-1 GenericInvestmentStorageBlock_total(storage1_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_1_3)_: ++1 GenericInvestmentStorageBlock_storage_content(storage1_3) +-1 GenericInvestmentStorageBlock_total(storage1_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_2_4)_: ++1 GenericInvestmentStorageBlock_storage_content(storage1_4) +-1 GenericInvestmentStorageBlock_total(storage1_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_2_5)_: ++1 GenericInvestmentStorageBlock_storage_content(storage1_5) +-1 GenericInvestmentStorageBlock_total(storage1_2) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_storage1_0_0) <= +inf + 0 <= flow(electricityBus_storage1_0_1) <= +inf + 0 <= flow(electricityBus_storage1_1_2) <= +inf + 0 <= flow(electricityBus_storage1_1_3) <= +inf + 0 <= flow(electricityBus_storage1_2_4) <= +inf + 0 <= flow(electricityBus_storage1_2_5) <= +inf + 0 <= flow(storage1_electricityBus_0_0) <= +inf + 0 <= flow(storage1_electricityBus_0_1) <= +inf + 0 <= flow(storage1_electricityBus_1_2) <= +inf + 0 <= flow(storage1_electricityBus_1_3) <= +inf + 0 <= flow(storage1_electricityBus_2_4) <= +inf + 0 <= flow(storage1_electricityBus_2_5) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage1_0) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage1_1) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage1_2) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage1_3) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage1_4) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage1_5) <= +inf + 100 <= GenericInvestmentStorageBlock_invest(storage1_0) <= 200 + 100 <= GenericInvestmentStorageBlock_invest(storage1_1) <= 200 + 100 <= GenericInvestmentStorageBlock_invest(storage1_2) <= 200 + 0 <= GenericInvestmentStorageBlock_total(storage1_0) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage1_1) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage1_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage1_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage1_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage1_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage1_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage1_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage1_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage1_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage1_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage1_2) <= +inf +end diff --git a/tests/lp_files/storage_unbalanced_multi_period.lp b/tests/lp_files/storage_unbalanced_multi_period.lp new file mode 100644 index 000000000..1bb633a57 --- /dev/null +++ b/tests/lp_files/storage_unbalanced_multi_period.lp @@ -0,0 +1,104 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++0 ONE_VAR_CONSTANT + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage1_0_0) ++1 flow(storage1_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage1_0_1) ++1 flow(storage1_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: +-1 flow(electricityBus_storage1_1_2) ++1 flow(storage1_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: +-1 flow(electricityBus_storage1_1_3) ++1 flow(storage1_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: +-1 flow(electricityBus_storage1_2_4) ++1 flow(storage1_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: +-1 flow(electricityBus_storage1_2_5) ++1 flow(storage1_electricityBus_2_5) += 0 + +c_e_GenericStorageBlock_balance_first(storage1)_: +-1 GenericStorageBlock_init_content(storage1) ++1 GenericStorageBlock_storage_content(storage1_0) +-1 flow(electricityBus_storage1_0_0) ++1 flow(storage1_electricityBus_0_0) += 0 + +c_e_GenericStorageBlock_balance(storage1_0_1)_: +-1 GenericStorageBlock_storage_content(storage1_0) ++1 GenericStorageBlock_storage_content(storage1_1) +-1 flow(electricityBus_storage1_0_1) ++1 flow(storage1_electricityBus_0_1) += 0 + +c_e_GenericStorageBlock_balance(storage1_1_2)_: +-1 GenericStorageBlock_storage_content(storage1_1) ++1 GenericStorageBlock_storage_content(storage1_2) +-1 flow(electricityBus_storage1_1_2) ++1 flow(storage1_electricityBus_1_2) += 0 + +c_e_GenericStorageBlock_balance(storage1_1_3)_: +-1 GenericStorageBlock_storage_content(storage1_2) ++1 GenericStorageBlock_storage_content(storage1_3) +-1 flow(electricityBus_storage1_1_3) ++1 flow(storage1_electricityBus_1_3) += 0 + +c_e_GenericStorageBlock_balance(storage1_2_4)_: +-1 GenericStorageBlock_storage_content(storage1_3) ++1 GenericStorageBlock_storage_content(storage1_4) +-1 flow(electricityBus_storage1_2_4) ++1 flow(storage1_electricityBus_2_4) += 0 + +c_e_GenericStorageBlock_balance(storage1_2_5)_: +-1 GenericStorageBlock_storage_content(storage1_4) ++1 GenericStorageBlock_storage_content(storage1_5) +-1 flow(electricityBus_storage1_2_5) ++1 flow(storage1_electricityBus_2_5) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_storage1_0_0) <= +inf + 0 <= flow(electricityBus_storage1_0_1) <= +inf + 0 <= flow(electricityBus_storage1_1_2) <= +inf + 0 <= flow(electricityBus_storage1_1_3) <= +inf + 0 <= flow(electricityBus_storage1_2_4) <= +inf + 0 <= flow(electricityBus_storage1_2_5) <= +inf + 0 <= flow(storage1_electricityBus_0_0) <= +inf + 0 <= flow(storage1_electricityBus_0_1) <= +inf + 0 <= flow(storage1_electricityBus_1_2) <= +inf + 0 <= flow(storage1_electricityBus_1_3) <= +inf + 0 <= flow(storage1_electricityBus_2_4) <= +inf + 0 <= flow(storage1_electricityBus_2_5) <= +inf + 0 <= GenericStorageBlock_storage_content(storage1_0) <= 1111 + 0 <= GenericStorageBlock_storage_content(storage1_1) <= 1111 + 0 <= GenericStorageBlock_storage_content(storage1_2) <= 1111 + 0 <= GenericStorageBlock_storage_content(storage1_3) <= 1111 + 0 <= GenericStorageBlock_storage_content(storage1_4) <= 1111 + 0 <= GenericStorageBlock_storage_content(storage1_5) <= 1111 + 0 <= GenericStorageBlock_init_content(storage1) <= 1111 +end diff --git a/tests/lp_files/transformer_invest_multi_period.lp b/tests/lp_files/transformer_invest_multi_period.lp new file mode 100644 index 000000000..8f1c0c555 --- /dev/null +++ b/tests/lp_files/transformer_invest_multi_period.lp @@ -0,0 +1,373 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++24.462687250116133 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_0) ++23.983026715800129 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_1) ++23.512771290000128 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_2) ++50 flow(powerplant_gas_coal_electricityBus_0_0) ++50 flow(powerplant_gas_coal_electricityBus_0_1) ++49.019607843137251 flow(powerplant_gas_coal_electricityBus_1_2) ++49.019607843137251 flow(powerplant_gas_coal_electricityBus_1_3) ++48.058439061899264 flow(powerplant_gas_coal_electricityBus_2_4) ++48.058439061899264 flow(powerplant_gas_coal_electricityBus_2_5) ++20 flow(powerplant_gas_coal_thermalBus_0_0) ++20 flow(powerplant_gas_coal_thermalBus_0_1) ++19.6078431372549 flow(powerplant_gas_coal_thermalBus_1_2) ++19.6078431372549 flow(powerplant_gas_coal_thermalBus_1_3) ++19.223375624759708 flow(powerplant_gas_coal_thermalBus_2_4) ++19.223375624759708 flow(powerplant_gas_coal_thermalBus_2_5) + +s.t. + +c_e_BusBlock_balance(coalBus_0_0)_: ++1 flow(coalBus_powerplant_gas_coal_0_0) += 0 + +c_e_BusBlock_balance(coalBus_0_1)_: ++1 flow(coalBus_powerplant_gas_coal_0_1) += 0 + +c_e_BusBlock_balance(coalBus_1_2)_: ++1 flow(coalBus_powerplant_gas_coal_1_2) += 0 + +c_e_BusBlock_balance(coalBus_1_3)_: ++1 flow(coalBus_powerplant_gas_coal_1_3) += 0 + +c_e_BusBlock_balance(coalBus_2_4)_: ++1 flow(coalBus_powerplant_gas_coal_2_4) += 0 + +c_e_BusBlock_balance(coalBus_2_5)_: ++1 flow(coalBus_powerplant_gas_coal_2_5) += 0 + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(powerplant_gas_coal_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(powerplant_gas_coal_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(powerplant_gas_coal_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(powerplant_gas_coal_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(powerplant_gas_coal_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(powerplant_gas_coal_electricityBus_2_5) += 0 + +c_e_BusBlock_balance(gasBus_0_0)_: ++1 flow(gasBus_powerplant_gas_coal_0_0) += 0 + +c_e_BusBlock_balance(gasBus_0_1)_: ++1 flow(gasBus_powerplant_gas_coal_0_1) += 0 + +c_e_BusBlock_balance(gasBus_1_2)_: ++1 flow(gasBus_powerplant_gas_coal_1_2) += 0 + +c_e_BusBlock_balance(gasBus_1_3)_: ++1 flow(gasBus_powerplant_gas_coal_1_3) += 0 + +c_e_BusBlock_balance(gasBus_2_4)_: ++1 flow(gasBus_powerplant_gas_coal_2_4) += 0 + +c_e_BusBlock_balance(gasBus_2_5)_: ++1 flow(gasBus_powerplant_gas_coal_2_5) += 0 + +c_e_BusBlock_balance(thermalBus_0_0)_: ++1 flow(powerplant_gas_coal_thermalBus_0_0) += 0 + +c_e_BusBlock_balance(thermalBus_0_1)_: ++1 flow(powerplant_gas_coal_thermalBus_0_1) += 0 + +c_e_BusBlock_balance(thermalBus_1_2)_: ++1 flow(powerplant_gas_coal_thermalBus_1_2) += 0 + +c_e_BusBlock_balance(thermalBus_1_3)_: ++1 flow(powerplant_gas_coal_thermalBus_1_3) += 0 + +c_e_BusBlock_balance(thermalBus_2_4)_: ++1 flow(powerplant_gas_coal_thermalBus_2_4) += 0 + +c_e_BusBlock_balance(thermalBus_2_5)_: ++1 flow(powerplant_gas_coal_thermalBus_2_5) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_0_0)_: ++0.29999999999999999 flow(coalBus_powerplant_gas_coal_0_0) +-0.20000000000000001 flow(powerplant_gas_coal_electricityBus_0_0) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_0_1)_: ++0.29999999999999999 flow(coalBus_powerplant_gas_coal_0_1) +-0.20000000000000001 flow(powerplant_gas_coal_electricityBus_0_1) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_1_2)_: ++0.29999999999999999 flow(coalBus_powerplant_gas_coal_1_2) +-0.20000000000000001 flow(powerplant_gas_coal_electricityBus_1_2) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_1_3)_: ++0.29999999999999999 flow(coalBus_powerplant_gas_coal_1_3) +-0.20000000000000001 flow(powerplant_gas_coal_electricityBus_1_3) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_2_4)_: ++0.29999999999999999 flow(coalBus_powerplant_gas_coal_2_4) +-0.20000000000000001 flow(powerplant_gas_coal_electricityBus_2_4) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_2_5)_: ++0.29999999999999999 flow(coalBus_powerplant_gas_coal_2_5) +-0.20000000000000001 flow(powerplant_gas_coal_electricityBus_2_5) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_0_0)_: ++0.5 flow(coalBus_powerplant_gas_coal_0_0) +-0.20000000000000001 flow(powerplant_gas_coal_thermalBus_0_0) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_0_1)_: ++0.5 flow(coalBus_powerplant_gas_coal_0_1) +-0.20000000000000001 flow(powerplant_gas_coal_thermalBus_0_1) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_1_2)_: ++0.5 flow(coalBus_powerplant_gas_coal_1_2) +-0.20000000000000001 flow(powerplant_gas_coal_thermalBus_1_2) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_1_3)_: ++0.5 flow(coalBus_powerplant_gas_coal_1_3) +-0.20000000000000001 flow(powerplant_gas_coal_thermalBus_1_3) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_2_4)_: ++0.5 flow(coalBus_powerplant_gas_coal_2_4) +-0.20000000000000001 flow(powerplant_gas_coal_thermalBus_2_4) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_2_5)_: ++0.5 flow(coalBus_powerplant_gas_coal_2_5) +-0.20000000000000001 flow(powerplant_gas_coal_thermalBus_2_5) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_0_0)_: ++0.29999999999999999 flow(gasBus_powerplant_gas_coal_0_0) +-0.57999999999999996 flow(powerplant_gas_coal_electricityBus_0_0) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_0_1)_: ++0.29999999999999999 flow(gasBus_powerplant_gas_coal_0_1) +-0.57999999999999996 flow(powerplant_gas_coal_electricityBus_0_1) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_1_2)_: ++0.29999999999999999 flow(gasBus_powerplant_gas_coal_1_2) +-0.57999999999999996 flow(powerplant_gas_coal_electricityBus_1_2) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_1_3)_: ++0.29999999999999999 flow(gasBus_powerplant_gas_coal_1_3) +-0.57999999999999996 flow(powerplant_gas_coal_electricityBus_1_3) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_2_4)_: ++0.29999999999999999 flow(gasBus_powerplant_gas_coal_2_4) +-0.57999999999999996 flow(powerplant_gas_coal_electricityBus_2_4) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_2_5)_: ++0.29999999999999999 flow(gasBus_powerplant_gas_coal_2_5) +-0.57999999999999996 flow(powerplant_gas_coal_electricityBus_2_5) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_0_0)_: ++0.5 flow(gasBus_powerplant_gas_coal_0_0) +-0.57999999999999996 flow(powerplant_gas_coal_thermalBus_0_0) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_0_1)_: ++0.5 flow(gasBus_powerplant_gas_coal_0_1) +-0.57999999999999996 flow(powerplant_gas_coal_thermalBus_0_1) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_1_2)_: ++0.5 flow(gasBus_powerplant_gas_coal_1_2) +-0.57999999999999996 flow(powerplant_gas_coal_thermalBus_1_2) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_1_3)_: ++0.5 flow(gasBus_powerplant_gas_coal_1_3) +-0.57999999999999996 flow(powerplant_gas_coal_thermalBus_1_3) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_2_4)_: ++0.5 flow(gasBus_powerplant_gas_coal_2_4) +-0.57999999999999996 flow(powerplant_gas_coal_thermalBus_2_4) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_2_5)_: ++0.5 flow(gasBus_powerplant_gas_coal_2_5) +-0.57999999999999996 flow(powerplant_gas_coal_thermalBus_2_5) += 0 + +c_e_InvestmentFlowBlock_total_rule(powerplant_gas_coal_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_0) ++1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(powerplant_gas_coal_electricityBus_1)_: +-1 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_1) ++1 InvestmentFlowBlock_old(powerplant_gas_coal_electricityBus_1) +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) ++1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(powerplant_gas_coal_electricityBus_2)_: +-1 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_2) ++1 InvestmentFlowBlock_old(powerplant_gas_coal_electricityBus_2) +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_1) ++1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(powerplant_gas_coal_electricityBus_0)_: ++1 InvestmentFlowBlock_old_end(powerplant_gas_coal_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(powerplant_gas_coal_electricityBus_1)_: ++1 InvestmentFlowBlock_old_end(powerplant_gas_coal_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(powerplant_gas_coal_electricityBus_2)_: ++1 InvestmentFlowBlock_old_end(powerplant_gas_coal_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(powerplant_gas_coal_electricityBus_0)_: ++1 InvestmentFlowBlock_old_exo(powerplant_gas_coal_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(powerplant_gas_coal_electricityBus_1)_: ++1 InvestmentFlowBlock_old_exo(powerplant_gas_coal_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(powerplant_gas_coal_electricityBus_2)_: ++1 InvestmentFlowBlock_old_exo(powerplant_gas_coal_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(powerplant_gas_coal_electricityBus_0)_: ++1 InvestmentFlowBlock_old(powerplant_gas_coal_electricityBus_0) +-1 InvestmentFlowBlock_old_end(powerplant_gas_coal_electricityBus_0) +-1 InvestmentFlowBlock_old_exo(powerplant_gas_coal_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(powerplant_gas_coal_electricityBus_1)_: ++1 InvestmentFlowBlock_old(powerplant_gas_coal_electricityBus_1) +-1 InvestmentFlowBlock_old_end(powerplant_gas_coal_electricityBus_1) +-1 InvestmentFlowBlock_old_exo(powerplant_gas_coal_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(powerplant_gas_coal_electricityBus_2)_: ++1 InvestmentFlowBlock_old(powerplant_gas_coal_electricityBus_2) +-1 InvestmentFlowBlock_old_end(powerplant_gas_coal_electricityBus_2) +-1 InvestmentFlowBlock_old_exo(powerplant_gas_coal_electricityBus_2) += 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) ++1 flow(powerplant_gas_coal_electricityBus_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) ++1 flow(powerplant_gas_coal_electricityBus_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_1_2)_: +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_1) ++1 flow(powerplant_gas_coal_electricityBus_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_1_3)_: +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_1) ++1 flow(powerplant_gas_coal_electricityBus_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_2_4)_: +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_2) ++1 flow(powerplant_gas_coal_electricityBus_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_2_5)_: +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_2) ++1 flow(powerplant_gas_coal_electricityBus_2_5) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(coalBus_powerplant_gas_coal_0_0) <= +inf + 0 <= flow(coalBus_powerplant_gas_coal_0_1) <= +inf + 0 <= flow(coalBus_powerplant_gas_coal_1_2) <= +inf + 0 <= flow(coalBus_powerplant_gas_coal_1_3) <= +inf + 0 <= flow(coalBus_powerplant_gas_coal_2_4) <= +inf + 0 <= flow(coalBus_powerplant_gas_coal_2_5) <= +inf + 0 <= flow(gasBus_powerplant_gas_coal_0_0) <= +inf + 0 <= flow(gasBus_powerplant_gas_coal_0_1) <= +inf + 0 <= flow(gasBus_powerplant_gas_coal_1_2) <= +inf + 0 <= flow(gasBus_powerplant_gas_coal_1_3) <= +inf + 0 <= flow(gasBus_powerplant_gas_coal_2_4) <= +inf + 0 <= flow(gasBus_powerplant_gas_coal_2_5) <= +inf + 0 <= flow(powerplant_gas_coal_electricityBus_0_0) <= +inf + 0 <= flow(powerplant_gas_coal_electricityBus_0_1) <= +inf + 0 <= flow(powerplant_gas_coal_electricityBus_1_2) <= +inf + 0 <= flow(powerplant_gas_coal_electricityBus_1_3) <= +inf + 0 <= flow(powerplant_gas_coal_electricityBus_2_4) <= +inf + 0 <= flow(powerplant_gas_coal_electricityBus_2_5) <= +inf + 0 <= flow(powerplant_gas_coal_thermalBus_0_0) <= +inf + 0 <= flow(powerplant_gas_coal_thermalBus_0_1) <= +inf + 0 <= flow(powerplant_gas_coal_thermalBus_1_2) <= +inf + 0 <= flow(powerplant_gas_coal_thermalBus_1_3) <= +inf + 0 <= flow(powerplant_gas_coal_thermalBus_2_4) <= +inf + 0 <= flow(powerplant_gas_coal_thermalBus_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_0) <= 1000 + 0 <= InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_1) <= 1000 + 0 <= InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_2) <= 1000 + 0 <= InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old(powerplant_gas_coal_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old(powerplant_gas_coal_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old(powerplant_gas_coal_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(powerplant_gas_coal_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(powerplant_gas_coal_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(powerplant_gas_coal_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(powerplant_gas_coal_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(powerplant_gas_coal_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(powerplant_gas_coal_electricityBus_2) <= +inf +end diff --git a/tests/lp_files/transformer_invest_with_existing_multi_period.lp b/tests/lp_files/transformer_invest_with_existing_multi_period.lp new file mode 100644 index 000000000..1757b593d --- /dev/null +++ b/tests/lp_files/transformer_invest_with_existing_multi_period.lp @@ -0,0 +1,374 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++20.601980198019806 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_0) ++20.198019801980202 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_1) ++19.801980198019805 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_2) ++50 flow(powerplant_gas_coal_electricityBus_0_0) ++50 flow(powerplant_gas_coal_electricityBus_0_1) ++49.019607843137251 flow(powerplant_gas_coal_electricityBus_1_2) ++49.019607843137251 flow(powerplant_gas_coal_electricityBus_1_3) ++48.058439061899264 flow(powerplant_gas_coal_electricityBus_2_4) ++48.058439061899264 flow(powerplant_gas_coal_electricityBus_2_5) ++20 flow(powerplant_gas_coal_thermalBus_0_0) ++20 flow(powerplant_gas_coal_thermalBus_0_1) ++19.6078431372549 flow(powerplant_gas_coal_thermalBus_1_2) ++19.6078431372549 flow(powerplant_gas_coal_thermalBus_1_3) ++19.223375624759708 flow(powerplant_gas_coal_thermalBus_2_4) ++19.223375624759708 flow(powerplant_gas_coal_thermalBus_2_5) + +s.t. + +c_e_BusBlock_balance(coalBus_0_0)_: ++1 flow(coalBus_powerplant_gas_coal_0_0) += 0 + +c_e_BusBlock_balance(coalBus_0_1)_: ++1 flow(coalBus_powerplant_gas_coal_0_1) += 0 + +c_e_BusBlock_balance(coalBus_1_2)_: ++1 flow(coalBus_powerplant_gas_coal_1_2) += 0 + +c_e_BusBlock_balance(coalBus_1_3)_: ++1 flow(coalBus_powerplant_gas_coal_1_3) += 0 + +c_e_BusBlock_balance(coalBus_2_4)_: ++1 flow(coalBus_powerplant_gas_coal_2_4) += 0 + +c_e_BusBlock_balance(coalBus_2_5)_: ++1 flow(coalBus_powerplant_gas_coal_2_5) += 0 + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(powerplant_gas_coal_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(powerplant_gas_coal_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(powerplant_gas_coal_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(powerplant_gas_coal_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(powerplant_gas_coal_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(powerplant_gas_coal_electricityBus_2_5) += 0 + +c_e_BusBlock_balance(gasBus_0_0)_: ++1 flow(gasBus_powerplant_gas_coal_0_0) += 0 + +c_e_BusBlock_balance(gasBus_0_1)_: ++1 flow(gasBus_powerplant_gas_coal_0_1) += 0 + +c_e_BusBlock_balance(gasBus_1_2)_: ++1 flow(gasBus_powerplant_gas_coal_1_2) += 0 + +c_e_BusBlock_balance(gasBus_1_3)_: ++1 flow(gasBus_powerplant_gas_coal_1_3) += 0 + +c_e_BusBlock_balance(gasBus_2_4)_: ++1 flow(gasBus_powerplant_gas_coal_2_4) += 0 + +c_e_BusBlock_balance(gasBus_2_5)_: ++1 flow(gasBus_powerplant_gas_coal_2_5) += 0 + +c_e_BusBlock_balance(thermalBus_0_0)_: ++1 flow(powerplant_gas_coal_thermalBus_0_0) += 0 + +c_e_BusBlock_balance(thermalBus_0_1)_: ++1 flow(powerplant_gas_coal_thermalBus_0_1) += 0 + +c_e_BusBlock_balance(thermalBus_1_2)_: ++1 flow(powerplant_gas_coal_thermalBus_1_2) += 0 + +c_e_BusBlock_balance(thermalBus_1_3)_: ++1 flow(powerplant_gas_coal_thermalBus_1_3) += 0 + +c_e_BusBlock_balance(thermalBus_2_4)_: ++1 flow(powerplant_gas_coal_thermalBus_2_4) += 0 + +c_e_BusBlock_balance(thermalBus_2_5)_: ++1 flow(powerplant_gas_coal_thermalBus_2_5) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_0_0)_: ++0.29999999999999999 flow(coalBus_powerplant_gas_coal_0_0) +-0.20000000000000001 flow(powerplant_gas_coal_electricityBus_0_0) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_0_1)_: ++0.29999999999999999 flow(coalBus_powerplant_gas_coal_0_1) +-0.20000000000000001 flow(powerplant_gas_coal_electricityBus_0_1) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_1_2)_: ++0.29999999999999999 flow(coalBus_powerplant_gas_coal_1_2) +-0.20000000000000001 flow(powerplant_gas_coal_electricityBus_1_2) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_1_3)_: ++0.29999999999999999 flow(coalBus_powerplant_gas_coal_1_3) +-0.20000000000000001 flow(powerplant_gas_coal_electricityBus_1_3) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_2_4)_: ++0.29999999999999999 flow(coalBus_powerplant_gas_coal_2_4) +-0.20000000000000001 flow(powerplant_gas_coal_electricityBus_2_4) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_electricityBus_2_5)_: ++0.29999999999999999 flow(coalBus_powerplant_gas_coal_2_5) +-0.20000000000000001 flow(powerplant_gas_coal_electricityBus_2_5) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_0_0)_: ++0.5 flow(coalBus_powerplant_gas_coal_0_0) +-0.20000000000000001 flow(powerplant_gas_coal_thermalBus_0_0) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_0_1)_: ++0.5 flow(coalBus_powerplant_gas_coal_0_1) +-0.20000000000000001 flow(powerplant_gas_coal_thermalBus_0_1) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_1_2)_: ++0.5 flow(coalBus_powerplant_gas_coal_1_2) +-0.20000000000000001 flow(powerplant_gas_coal_thermalBus_1_2) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_1_3)_: ++0.5 flow(coalBus_powerplant_gas_coal_1_3) +-0.20000000000000001 flow(powerplant_gas_coal_thermalBus_1_3) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_2_4)_: ++0.5 flow(coalBus_powerplant_gas_coal_2_4) +-0.20000000000000001 flow(powerplant_gas_coal_thermalBus_2_4) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_coalBus_thermalBus_2_5)_: ++0.5 flow(coalBus_powerplant_gas_coal_2_5) +-0.20000000000000001 flow(powerplant_gas_coal_thermalBus_2_5) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_0_0)_: ++0.29999999999999999 flow(gasBus_powerplant_gas_coal_0_0) +-0.57999999999999996 flow(powerplant_gas_coal_electricityBus_0_0) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_0_1)_: ++0.29999999999999999 flow(gasBus_powerplant_gas_coal_0_1) +-0.57999999999999996 flow(powerplant_gas_coal_electricityBus_0_1) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_1_2)_: ++0.29999999999999999 flow(gasBus_powerplant_gas_coal_1_2) +-0.57999999999999996 flow(powerplant_gas_coal_electricityBus_1_2) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_1_3)_: ++0.29999999999999999 flow(gasBus_powerplant_gas_coal_1_3) +-0.57999999999999996 flow(powerplant_gas_coal_electricityBus_1_3) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_2_4)_: ++0.29999999999999999 flow(gasBus_powerplant_gas_coal_2_4) +-0.57999999999999996 flow(powerplant_gas_coal_electricityBus_2_4) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_electricityBus_2_5)_: ++0.29999999999999999 flow(gasBus_powerplant_gas_coal_2_5) +-0.57999999999999996 flow(powerplant_gas_coal_electricityBus_2_5) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_0_0)_: ++0.5 flow(gasBus_powerplant_gas_coal_0_0) +-0.57999999999999996 flow(powerplant_gas_coal_thermalBus_0_0) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_0_1)_: ++0.5 flow(gasBus_powerplant_gas_coal_0_1) +-0.57999999999999996 flow(powerplant_gas_coal_thermalBus_0_1) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_1_2)_: ++0.5 flow(gasBus_powerplant_gas_coal_1_2) +-0.57999999999999996 flow(powerplant_gas_coal_thermalBus_1_2) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_1_3)_: ++0.5 flow(gasBus_powerplant_gas_coal_1_3) +-0.57999999999999996 flow(powerplant_gas_coal_thermalBus_1_3) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_2_4)_: ++0.5 flow(gasBus_powerplant_gas_coal_2_4) +-0.57999999999999996 flow(powerplant_gas_coal_thermalBus_2_4) += 0 + +c_e_TransformerBlock_relation(powerplant_gas_coal_gasBus_thermalBus_2_5)_: ++0.5 flow(gasBus_powerplant_gas_coal_2_5) +-0.57999999999999996 flow(powerplant_gas_coal_thermalBus_2_5) += 0 + +c_e_InvestmentFlowBlock_total_rule(powerplant_gas_coal_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_0) ++1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) += 200 + +c_e_InvestmentFlowBlock_total_rule(powerplant_gas_coal_electricityBus_1)_: +-1 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_1) ++1 InvestmentFlowBlock_old(powerplant_gas_coal_electricityBus_1) +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) ++1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(powerplant_gas_coal_electricityBus_2)_: +-1 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_2) ++1 InvestmentFlowBlock_old(powerplant_gas_coal_electricityBus_2) +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_1) ++1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(powerplant_gas_coal_electricityBus_0)_: ++1 InvestmentFlowBlock_old_end(powerplant_gas_coal_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(powerplant_gas_coal_electricityBus_1)_: ++1 InvestmentFlowBlock_old_end(powerplant_gas_coal_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(powerplant_gas_coal_electricityBus_2)_: +-1 InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_0) ++1 InvestmentFlowBlock_old_end(powerplant_gas_coal_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(powerplant_gas_coal_electricityBus_0)_: ++1 InvestmentFlowBlock_old_exo(powerplant_gas_coal_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(powerplant_gas_coal_electricityBus_1)_: ++1 InvestmentFlowBlock_old_exo(powerplant_gas_coal_electricityBus_1) += 200 + +c_e_InvestmentFlowBlock_old_rule_exo(powerplant_gas_coal_electricityBus_2)_: ++1 InvestmentFlowBlock_old_exo(powerplant_gas_coal_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(powerplant_gas_coal_electricityBus_0)_: ++1 InvestmentFlowBlock_old(powerplant_gas_coal_electricityBus_0) +-1 InvestmentFlowBlock_old_end(powerplant_gas_coal_electricityBus_0) +-1 InvestmentFlowBlock_old_exo(powerplant_gas_coal_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(powerplant_gas_coal_electricityBus_1)_: ++1 InvestmentFlowBlock_old(powerplant_gas_coal_electricityBus_1) +-1 InvestmentFlowBlock_old_end(powerplant_gas_coal_electricityBus_1) +-1 InvestmentFlowBlock_old_exo(powerplant_gas_coal_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(powerplant_gas_coal_electricityBus_2)_: ++1 InvestmentFlowBlock_old(powerplant_gas_coal_electricityBus_2) +-1 InvestmentFlowBlock_old_end(powerplant_gas_coal_electricityBus_2) +-1 InvestmentFlowBlock_old_exo(powerplant_gas_coal_electricityBus_2) += 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) ++1 flow(powerplant_gas_coal_electricityBus_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) ++1 flow(powerplant_gas_coal_electricityBus_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_1_2)_: +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_1) ++1 flow(powerplant_gas_coal_electricityBus_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_1_3)_: +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_1) ++1 flow(powerplant_gas_coal_electricityBus_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_2_4)_: +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_2) ++1 flow(powerplant_gas_coal_electricityBus_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(powerplant_gas_coal_electricityBus_2_5)_: +-1 InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_2) ++1 flow(powerplant_gas_coal_electricityBus_2_5) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(coalBus_powerplant_gas_coal_0_0) <= +inf + 0 <= flow(coalBus_powerplant_gas_coal_0_1) <= +inf + 0 <= flow(coalBus_powerplant_gas_coal_1_2) <= +inf + 0 <= flow(coalBus_powerplant_gas_coal_1_3) <= +inf + 0 <= flow(coalBus_powerplant_gas_coal_2_4) <= +inf + 0 <= flow(coalBus_powerplant_gas_coal_2_5) <= +inf + 0 <= flow(gasBus_powerplant_gas_coal_0_0) <= +inf + 0 <= flow(gasBus_powerplant_gas_coal_0_1) <= +inf + 0 <= flow(gasBus_powerplant_gas_coal_1_2) <= +inf + 0 <= flow(gasBus_powerplant_gas_coal_1_3) <= +inf + 0 <= flow(gasBus_powerplant_gas_coal_2_4) <= +inf + 0 <= flow(gasBus_powerplant_gas_coal_2_5) <= +inf + 0 <= flow(powerplant_gas_coal_electricityBus_0_0) <= +inf + 0 <= flow(powerplant_gas_coal_electricityBus_0_1) <= +inf + 0 <= flow(powerplant_gas_coal_electricityBus_1_2) <= +inf + 0 <= flow(powerplant_gas_coal_electricityBus_1_3) <= +inf + 0 <= flow(powerplant_gas_coal_electricityBus_2_4) <= +inf + 0 <= flow(powerplant_gas_coal_electricityBus_2_5) <= +inf + 0 <= flow(powerplant_gas_coal_thermalBus_0_0) <= +inf + 0 <= flow(powerplant_gas_coal_thermalBus_0_1) <= +inf + 0 <= flow(powerplant_gas_coal_thermalBus_1_2) <= +inf + 0 <= flow(powerplant_gas_coal_thermalBus_1_3) <= +inf + 0 <= flow(powerplant_gas_coal_thermalBus_2_4) <= +inf + 0 <= flow(powerplant_gas_coal_thermalBus_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_0) <= 1000 + 0 <= InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_1) <= 1000 + 0 <= InvestmentFlowBlock_invest(powerplant_gas_coal_electricityBus_2) <= 1000 + 0 <= InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_total(powerplant_gas_coal_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old(powerplant_gas_coal_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old(powerplant_gas_coal_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old(powerplant_gas_coal_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(powerplant_gas_coal_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(powerplant_gas_coal_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(powerplant_gas_coal_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(powerplant_gas_coal_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(powerplant_gas_coal_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(powerplant_gas_coal_electricityBus_2) <= +inf +end diff --git a/tests/lp_files/transformer_multi_period.lp b/tests/lp_files/transformer_multi_period.lp new file mode 100644 index 000000000..73a73ca12 --- /dev/null +++ b/tests/lp_files/transformer_multi_period.lp @@ -0,0 +1,264 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++50 flow(powerplantGasBiomass_electricityBus_0_0) ++50 flow(powerplantGasBiomass_electricityBus_0_1) ++49.019607843137251 flow(powerplantGasBiomass_electricityBus_1_2) ++49.019607843137251 flow(powerplantGasBiomass_electricityBus_1_3) ++48.058439061899264 flow(powerplantGasBiomass_electricityBus_2_4) ++48.058439061899264 flow(powerplantGasBiomass_electricityBus_2_5) ++20 flow(powerplantGasBiomass_thermalBus_0_0) ++20 flow(powerplantGasBiomass_thermalBus_0_1) ++19.6078431372549 flow(powerplantGasBiomass_thermalBus_1_2) ++19.6078431372549 flow(powerplantGasBiomass_thermalBus_1_3) ++19.223375624759708 flow(powerplantGasBiomass_thermalBus_2_4) ++19.223375624759708 flow(powerplantGasBiomass_thermalBus_2_5) + +s.t. + +c_e_BusBlock_balance(biomassBus_0_0)_: ++1 flow(biomassBus_powerplantGasBiomass_0_0) += 0 + +c_e_BusBlock_balance(biomassBus_0_1)_: ++1 flow(biomassBus_powerplantGasBiomass_0_1) += 0 + +c_e_BusBlock_balance(biomassBus_1_2)_: ++1 flow(biomassBus_powerplantGasBiomass_1_2) += 0 + +c_e_BusBlock_balance(biomassBus_1_3)_: ++1 flow(biomassBus_powerplantGasBiomass_1_3) += 0 + +c_e_BusBlock_balance(biomassBus_2_4)_: ++1 flow(biomassBus_powerplantGasBiomass_2_4) += 0 + +c_e_BusBlock_balance(biomassBus_2_5)_: ++1 flow(biomassBus_powerplantGasBiomass_2_5) += 0 + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(powerplantGasBiomass_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(powerplantGasBiomass_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(powerplantGasBiomass_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(powerplantGasBiomass_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(powerplantGasBiomass_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(powerplantGasBiomass_electricityBus_2_5) += 0 + +c_e_BusBlock_balance(gasBus_0_0)_: ++1 flow(gasBus_powerplantGasBiomass_0_0) += 0 + +c_e_BusBlock_balance(gasBus_0_1)_: ++1 flow(gasBus_powerplantGasBiomass_0_1) += 0 + +c_e_BusBlock_balance(gasBus_1_2)_: ++1 flow(gasBus_powerplantGasBiomass_1_2) += 0 + +c_e_BusBlock_balance(gasBus_1_3)_: ++1 flow(gasBus_powerplantGasBiomass_1_3) += 0 + +c_e_BusBlock_balance(gasBus_2_4)_: ++1 flow(gasBus_powerplantGasBiomass_2_4) += 0 + +c_e_BusBlock_balance(gasBus_2_5)_: ++1 flow(gasBus_powerplantGasBiomass_2_5) += 0 + +c_e_BusBlock_balance(thermalBus_0_0)_: ++1 flow(powerplantGasBiomass_thermalBus_0_0) += 0 + +c_e_BusBlock_balance(thermalBus_0_1)_: ++1 flow(powerplantGasBiomass_thermalBus_0_1) += 0 + +c_e_BusBlock_balance(thermalBus_1_2)_: ++1 flow(powerplantGasBiomass_thermalBus_1_2) += 0 + +c_e_BusBlock_balance(thermalBus_1_3)_: ++1 flow(powerplantGasBiomass_thermalBus_1_3) += 0 + +c_e_BusBlock_balance(thermalBus_2_4)_: ++1 flow(powerplantGasBiomass_thermalBus_2_4) += 0 + +c_e_BusBlock_balance(thermalBus_2_5)_: ++1 flow(powerplantGasBiomass_thermalBus_2_5) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_biomassBus_electricityBus_0_0)_: ++0.29999999999999999 flow(biomassBus_powerplantGasBiomass_0_0) +-0.10000000000000001 flow(powerplantGasBiomass_electricityBus_0_0) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_biomassBus_electricityBus_0_1)_: ++0.29999999999999999 flow(biomassBus_powerplantGasBiomass_0_1) +-0.10000000000000001 flow(powerplantGasBiomass_electricityBus_0_1) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_biomassBus_electricityBus_1_2)_: ++0.29999999999999999 flow(biomassBus_powerplantGasBiomass_1_2) +-0.10000000000000001 flow(powerplantGasBiomass_electricityBus_1_2) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_biomassBus_electricityBus_1_3)_: ++0.29999999999999999 flow(biomassBus_powerplantGasBiomass_1_3) +-0.10000000000000001 flow(powerplantGasBiomass_electricityBus_1_3) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_biomassBus_electricityBus_2_4)_: ++0.29999999999999999 flow(biomassBus_powerplantGasBiomass_2_4) +-0.10000000000000001 flow(powerplantGasBiomass_electricityBus_2_4) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_biomassBus_electricityBus_2_5)_: ++0.29999999999999999 flow(biomassBus_powerplantGasBiomass_2_5) +-0.10000000000000001 flow(powerplantGasBiomass_electricityBus_2_5) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_biomassBus_thermalBus_0_0)_: ++0.5 flow(biomassBus_powerplantGasBiomass_0_0) +-0.10000000000000001 flow(powerplantGasBiomass_thermalBus_0_0) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_biomassBus_thermalBus_0_1)_: ++0.5 flow(biomassBus_powerplantGasBiomass_0_1) +-0.10000000000000001 flow(powerplantGasBiomass_thermalBus_0_1) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_biomassBus_thermalBus_1_2)_: ++0.5 flow(biomassBus_powerplantGasBiomass_1_2) +-0.10000000000000001 flow(powerplantGasBiomass_thermalBus_1_2) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_biomassBus_thermalBus_1_3)_: ++0.5 flow(biomassBus_powerplantGasBiomass_1_3) +-0.10000000000000001 flow(powerplantGasBiomass_thermalBus_1_3) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_biomassBus_thermalBus_2_4)_: ++0.5 flow(biomassBus_powerplantGasBiomass_2_4) +-0.10000000000000001 flow(powerplantGasBiomass_thermalBus_2_4) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_biomassBus_thermalBus_2_5)_: ++0.5 flow(biomassBus_powerplantGasBiomass_2_5) +-0.10000000000000001 flow(powerplantGasBiomass_thermalBus_2_5) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_gasBus_electricityBus_0_0)_: ++0.29999999999999999 flow(gasBus_powerplantGasBiomass_0_0) +-0.40000000000000002 flow(powerplantGasBiomass_electricityBus_0_0) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_gasBus_electricityBus_0_1)_: ++0.29999999999999999 flow(gasBus_powerplantGasBiomass_0_1) +-0.40000000000000002 flow(powerplantGasBiomass_electricityBus_0_1) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_gasBus_electricityBus_1_2)_: ++0.29999999999999999 flow(gasBus_powerplantGasBiomass_1_2) +-0.40000000000000002 flow(powerplantGasBiomass_electricityBus_1_2) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_gasBus_electricityBus_1_3)_: ++0.29999999999999999 flow(gasBus_powerplantGasBiomass_1_3) +-0.40000000000000002 flow(powerplantGasBiomass_electricityBus_1_3) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_gasBus_electricityBus_2_4)_: ++0.29999999999999999 flow(gasBus_powerplantGasBiomass_2_4) +-0.40000000000000002 flow(powerplantGasBiomass_electricityBus_2_4) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_gasBus_electricityBus_2_5)_: ++0.29999999999999999 flow(gasBus_powerplantGasBiomass_2_5) +-0.40000000000000002 flow(powerplantGasBiomass_electricityBus_2_5) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_gasBus_thermalBus_0_0)_: ++0.5 flow(gasBus_powerplantGasBiomass_0_0) +-0.40000000000000002 flow(powerplantGasBiomass_thermalBus_0_0) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_gasBus_thermalBus_0_1)_: ++0.5 flow(gasBus_powerplantGasBiomass_0_1) +-0.40000000000000002 flow(powerplantGasBiomass_thermalBus_0_1) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_gasBus_thermalBus_1_2)_: ++0.5 flow(gasBus_powerplantGasBiomass_1_2) +-0.40000000000000002 flow(powerplantGasBiomass_thermalBus_1_2) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_gasBus_thermalBus_1_3)_: ++0.5 flow(gasBus_powerplantGasBiomass_1_3) +-0.40000000000000002 flow(powerplantGasBiomass_thermalBus_1_3) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_gasBus_thermalBus_2_4)_: ++0.5 flow(gasBus_powerplantGasBiomass_2_4) +-0.40000000000000002 flow(powerplantGasBiomass_thermalBus_2_4) += 0 + +c_e_TransformerBlock_relation(powerplantGasBiomass_gasBus_thermalBus_2_5)_: ++0.5 flow(gasBus_powerplantGasBiomass_2_5) +-0.40000000000000002 flow(powerplantGasBiomass_thermalBus_2_5) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(biomassBus_powerplantGasBiomass_0_0) <= +inf + 0 <= flow(biomassBus_powerplantGasBiomass_0_1) <= +inf + 0 <= flow(biomassBus_powerplantGasBiomass_1_2) <= +inf + 0 <= flow(biomassBus_powerplantGasBiomass_1_3) <= +inf + 0 <= flow(biomassBus_powerplantGasBiomass_2_4) <= +inf + 0 <= flow(biomassBus_powerplantGasBiomass_2_5) <= +inf + 0 <= flow(gasBus_powerplantGasBiomass_0_0) <= +inf + 0 <= flow(gasBus_powerplantGasBiomass_0_1) <= +inf + 0 <= flow(gasBus_powerplantGasBiomass_1_2) <= +inf + 0 <= flow(gasBus_powerplantGasBiomass_1_3) <= +inf + 0 <= flow(gasBus_powerplantGasBiomass_2_4) <= +inf + 0 <= flow(gasBus_powerplantGasBiomass_2_5) <= +inf + 0 <= flow(powerplantGasBiomass_electricityBus_0_0) <= +inf + 0 <= flow(powerplantGasBiomass_electricityBus_0_1) <= +inf + 0 <= flow(powerplantGasBiomass_electricityBus_1_2) <= +inf + 0 <= flow(powerplantGasBiomass_electricityBus_1_3) <= +inf + 0 <= flow(powerplantGasBiomass_electricityBus_2_4) <= +inf + 0 <= flow(powerplantGasBiomass_electricityBus_2_5) <= +inf + 0 <= flow(powerplantGasBiomass_thermalBus_0_0) <= 50000000000 + 0 <= flow(powerplantGasBiomass_thermalBus_0_1) <= 50000000000 + 0 <= flow(powerplantGasBiomass_thermalBus_1_2) <= 50000000000 + 0 <= flow(powerplantGasBiomass_thermalBus_1_3) <= 50000000000 + 0 <= flow(powerplantGasBiomass_thermalBus_2_4) <= 50000000000 + 0 <= flow(powerplantGasBiomass_thermalBus_2_5) <= 50000000000 +end diff --git a/tests/lp_files/variable_chp_multi_period.lp b/tests/lp_files/variable_chp_multi_period.lp new file mode 100644 index 000000000..73a355b5c --- /dev/null +++ b/tests/lp_files/variable_chp_multi_period.lp @@ -0,0 +1,271 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++0 ONE_VAR_CONSTANT + +s.t. + +c_e_BusBlock_balance(commodityBus_0_0)_: ++1 flow(commodityBus_variable_chp_gas1_0_0) ++1 flow(commodityBus_variable_chp_gas2_0_0) += 0 + +c_e_BusBlock_balance(commodityBus_0_1)_: ++1 flow(commodityBus_variable_chp_gas1_0_1) ++1 flow(commodityBus_variable_chp_gas2_0_1) += 0 + +c_e_BusBlock_balance(commodityBus_1_2)_: ++1 flow(commodityBus_variable_chp_gas1_1_2) ++1 flow(commodityBus_variable_chp_gas2_1_2) += 0 + +c_e_BusBlock_balance(commodityBus_1_3)_: ++1 flow(commodityBus_variable_chp_gas1_1_3) ++1 flow(commodityBus_variable_chp_gas2_1_3) += 0 + +c_e_BusBlock_balance(commodityBus_2_4)_: ++1 flow(commodityBus_variable_chp_gas1_2_4) ++1 flow(commodityBus_variable_chp_gas2_2_4) += 0 + +c_e_BusBlock_balance(commodityBus_2_5)_: ++1 flow(commodityBus_variable_chp_gas1_2_5) ++1 flow(commodityBus_variable_chp_gas2_2_5) += 0 + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(variable_chp_gas1_electricityBus_0_0) ++1 flow(variable_chp_gas2_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(variable_chp_gas1_electricityBus_0_1) ++1 flow(variable_chp_gas2_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(variable_chp_gas1_electricityBus_1_2) ++1 flow(variable_chp_gas2_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(variable_chp_gas1_electricityBus_1_3) ++1 flow(variable_chp_gas2_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(variable_chp_gas1_electricityBus_2_4) ++1 flow(variable_chp_gas2_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(variable_chp_gas1_electricityBus_2_5) ++1 flow(variable_chp_gas2_electricityBus_2_5) += 0 + +c_e_BusBlock_balance(heatBus_0_0)_: ++1 flow(variable_chp_gas1_heatBus_0_0) ++1 flow(variable_chp_gas2_heatBus_0_0) += 0 + +c_e_BusBlock_balance(heatBus_0_1)_: ++1 flow(variable_chp_gas1_heatBus_0_1) ++1 flow(variable_chp_gas2_heatBus_0_1) += 0 + +c_e_BusBlock_balance(heatBus_1_2)_: ++1 flow(variable_chp_gas1_heatBus_1_2) ++1 flow(variable_chp_gas2_heatBus_1_2) += 0 + +c_e_BusBlock_balance(heatBus_1_3)_: ++1 flow(variable_chp_gas1_heatBus_1_3) ++1 flow(variable_chp_gas2_heatBus_1_3) += 0 + +c_e_BusBlock_balance(heatBus_2_4)_: ++1 flow(variable_chp_gas1_heatBus_2_4) ++1 flow(variable_chp_gas2_heatBus_2_4) += 0 + +c_e_BusBlock_balance(heatBus_2_5)_: ++1 flow(variable_chp_gas1_heatBus_2_5) ++1 flow(variable_chp_gas2_heatBus_2_5) += 0 + +c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas1_0_0)_: ++1 flow(commodityBus_variable_chp_gas1_0_0) +-2 flow(variable_chp_gas1_electricityBus_0_0) +-0.80000000000000004 flow(variable_chp_gas1_heatBus_0_0) += 0 + +c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas1_0_1)_: ++1 flow(commodityBus_variable_chp_gas1_0_1) +-2 flow(variable_chp_gas1_electricityBus_0_1) +-0.80000000000000004 flow(variable_chp_gas1_heatBus_0_1) += 0 + +c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas1_1_2)_: ++1 flow(commodityBus_variable_chp_gas1_1_2) +-2 flow(variable_chp_gas1_electricityBus_1_2) +-0.80000000000000004 flow(variable_chp_gas1_heatBus_1_2) += 0 + +c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas1_1_3)_: ++1 flow(commodityBus_variable_chp_gas1_1_3) +-2 flow(variable_chp_gas1_electricityBus_1_3) +-0.80000000000000004 flow(variable_chp_gas1_heatBus_1_3) += 0 + +c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas1_2_4)_: ++1 flow(commodityBus_variable_chp_gas1_2_4) +-2 flow(variable_chp_gas1_electricityBus_2_4) +-0.80000000000000004 flow(variable_chp_gas1_heatBus_2_4) += 0 + +c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas1_2_5)_: ++1 flow(commodityBus_variable_chp_gas1_2_5) +-2 flow(variable_chp_gas1_electricityBus_2_5) +-0.80000000000000004 flow(variable_chp_gas1_heatBus_2_5) += 0 + +c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas2_0_0)_: ++1 flow(commodityBus_variable_chp_gas2_0_0) +-2 flow(variable_chp_gas2_electricityBus_0_0) +-0.80000000000000004 flow(variable_chp_gas2_heatBus_0_0) += 0 + +c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas2_0_1)_: ++1 flow(commodityBus_variable_chp_gas2_0_1) +-2 flow(variable_chp_gas2_electricityBus_0_1) +-0.80000000000000004 flow(variable_chp_gas2_heatBus_0_1) += 0 + +c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas2_1_2)_: ++1 flow(commodityBus_variable_chp_gas2_1_2) +-2 flow(variable_chp_gas2_electricityBus_1_2) +-0.80000000000000004 flow(variable_chp_gas2_heatBus_1_2) += 0 + +c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas2_1_3)_: ++1 flow(commodityBus_variable_chp_gas2_1_3) +-2 flow(variable_chp_gas2_electricityBus_1_3) +-0.80000000000000004 flow(variable_chp_gas2_heatBus_1_3) += 0 + +c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas2_2_4)_: ++1 flow(commodityBus_variable_chp_gas2_2_4) +-2 flow(variable_chp_gas2_electricityBus_2_4) +-0.80000000000000004 flow(variable_chp_gas2_heatBus_2_4) += 0 + +c_e_ExtractionTurbineCHPBlock_input_output_relation(variable_chp_gas2_2_5)_: ++1 flow(commodityBus_variable_chp_gas2_2_5) +-2 flow(variable_chp_gas2_electricityBus_2_5) +-0.80000000000000004 flow(variable_chp_gas2_heatBus_2_5) += 0 + +c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas1_0_0)_: +-1 flow(variable_chp_gas1_electricityBus_0_0) ++0.59999999999999998 flow(variable_chp_gas1_heatBus_0_0) +<= 0 + +c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas1_0_1)_: +-1 flow(variable_chp_gas1_electricityBus_0_1) ++0.59999999999999998 flow(variable_chp_gas1_heatBus_0_1) +<= 0 + +c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas1_1_2)_: +-1 flow(variable_chp_gas1_electricityBus_1_2) ++0.59999999999999998 flow(variable_chp_gas1_heatBus_1_2) +<= 0 + +c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas1_1_3)_: +-1 flow(variable_chp_gas1_electricityBus_1_3) ++0.59999999999999998 flow(variable_chp_gas1_heatBus_1_3) +<= 0 + +c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas1_2_4)_: +-1 flow(variable_chp_gas1_electricityBus_2_4) ++0.59999999999999998 flow(variable_chp_gas1_heatBus_2_4) +<= 0 + +c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas1_2_5)_: +-1 flow(variable_chp_gas1_electricityBus_2_5) ++0.59999999999999998 flow(variable_chp_gas1_heatBus_2_5) +<= 0 + +c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas2_0_0)_: +-1 flow(variable_chp_gas2_electricityBus_0_0) ++0.59999999999999998 flow(variable_chp_gas2_heatBus_0_0) +<= 0 + +c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas2_0_1)_: +-1 flow(variable_chp_gas2_electricityBus_0_1) ++0.59999999999999998 flow(variable_chp_gas2_heatBus_0_1) +<= 0 + +c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas2_1_2)_: +-1 flow(variable_chp_gas2_electricityBus_1_2) ++0.59999999999999998 flow(variable_chp_gas2_heatBus_1_2) +<= 0 + +c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas2_1_3)_: +-1 flow(variable_chp_gas2_electricityBus_1_3) ++0.59999999999999998 flow(variable_chp_gas2_heatBus_1_3) +<= 0 + +c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas2_2_4)_: +-1 flow(variable_chp_gas2_electricityBus_2_4) ++0.59999999999999998 flow(variable_chp_gas2_heatBus_2_4) +<= 0 + +c_u_ExtractionTurbineCHPBlock_out_flow_relation(variable_chp_gas2_2_5)_: +-1 flow(variable_chp_gas2_electricityBus_2_5) ++0.59999999999999998 flow(variable_chp_gas2_heatBus_2_5) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(commodityBus_variable_chp_gas1_0_0) <= 100 + 0 <= flow(commodityBus_variable_chp_gas1_0_1) <= 100 + 0 <= flow(commodityBus_variable_chp_gas1_1_2) <= 100 + 0 <= flow(commodityBus_variable_chp_gas1_1_3) <= 100 + 0 <= flow(commodityBus_variable_chp_gas1_2_4) <= 100 + 0 <= flow(commodityBus_variable_chp_gas1_2_5) <= 100 + 0 <= flow(commodityBus_variable_chp_gas2_0_0) <= 100 + 0 <= flow(commodityBus_variable_chp_gas2_0_1) <= 100 + 0 <= flow(commodityBus_variable_chp_gas2_1_2) <= 100 + 0 <= flow(commodityBus_variable_chp_gas2_1_3) <= 100 + 0 <= flow(commodityBus_variable_chp_gas2_2_4) <= 100 + 0 <= flow(commodityBus_variable_chp_gas2_2_5) <= 100 + 0 <= flow(variable_chp_gas1_electricityBus_0_0) <= +inf + 0 <= flow(variable_chp_gas1_electricityBus_0_1) <= +inf + 0 <= flow(variable_chp_gas1_electricityBus_1_2) <= +inf + 0 <= flow(variable_chp_gas1_electricityBus_1_3) <= +inf + 0 <= flow(variable_chp_gas1_electricityBus_2_4) <= +inf + 0 <= flow(variable_chp_gas1_electricityBus_2_5) <= +inf + 0 <= flow(variable_chp_gas1_heatBus_0_0) <= +inf + 0 <= flow(variable_chp_gas1_heatBus_0_1) <= +inf + 0 <= flow(variable_chp_gas1_heatBus_1_2) <= +inf + 0 <= flow(variable_chp_gas1_heatBus_1_3) <= +inf + 0 <= flow(variable_chp_gas1_heatBus_2_4) <= +inf + 0 <= flow(variable_chp_gas1_heatBus_2_5) <= +inf + 0 <= flow(variable_chp_gas2_electricityBus_0_0) <= +inf + 0 <= flow(variable_chp_gas2_electricityBus_0_1) <= +inf + 0 <= flow(variable_chp_gas2_electricityBus_1_2) <= +inf + 0 <= flow(variable_chp_gas2_electricityBus_1_3) <= +inf + 0 <= flow(variable_chp_gas2_electricityBus_2_4) <= +inf + 0 <= flow(variable_chp_gas2_electricityBus_2_5) <= +inf + 0 <= flow(variable_chp_gas2_heatBus_0_0) <= +inf + 0 <= flow(variable_chp_gas2_heatBus_0_1) <= +inf + 0 <= flow(variable_chp_gas2_heatBus_1_2) <= +inf + 0 <= flow(variable_chp_gas2_heatBus_1_3) <= +inf + 0 <= flow(variable_chp_gas2_heatBus_2_4) <= +inf + 0 <= flow(variable_chp_gas2_heatBus_2_5) <= +inf +end diff --git a/tests/multi_period_constraint_tests.py b/tests/multi_period_constraint_tests.py index ade700f1a..7fb8a1b9f 100644 --- a/tests/multi_period_constraint_tests.py +++ b/tests/multi_period_constraint_tests.py @@ -11,6 +11,7 @@ import logging import re +import warnings from difflib import unified_diff from os import path as ospath @@ -145,103 +146,6 @@ def normalize_to_positive_results(lines): ), ) - def test_emission_budget_limit(self): - """Test emissions budget limit constraint""" - bel = solph.buses.Bus(label="electricityBus") - - solph.components.Source( - label="source1", - outputs={ - bel: solph.flows.Flow( - nominal_value=100, - emission_factor=[0.5, -1.0, 2.0, 1.0, 0.5, 0.5], - ) - }, - ) - solph.components.Source( - label="source2", - outputs={ - bel: solph.flows.Flow(nominal_value=100, emission_factor=3.5) - }, - ) - - # Should be ignored because the emission attribute is not defined. - solph.components.Source( - label="source3", outputs={bel: solph.flows.Flow(nominal_value=100)} - ) - - om = self.get_om() - - solph.constraints.emission_limit(om, limit=777) - - self.compare_lp_files("emission_budget_limit.lp", my_om=om) - - def test_periodical_emission_limit(self): - """Test periodical emissions constraint""" - bel = solph.buses.Bus(label="electricityBus") - - solph.components.Source( - label="source1", - outputs={ - bel: solph.flows.Flow( - nominal_value=100, - emission_factor=[0.5, -1.0, 2.0, 1.0, 0.5, 0.5], - ) - }, - ) - solph.components.Source( - label="source2", - outputs={ - bel: solph.flows.Flow(nominal_value=100, emission_factor=3.5) - }, - ) - - # Should be ignored because the emission attribute is not defined. - solph.components.Source( - label="source3", outputs={bel: solph.flows.Flow(nominal_value=100)} - ) - - om = self.get_om() - - solph.constraints.emission_limit_per_period(om, limit=222) - - self.compare_lp_files("periodical_emission_limit.lp", my_om=om) - - def test_periodical_emission_limit_missing_limit(self): - """Test error for periodical emissions constraint""" - bel = solph.buses.Bus(label="electricityBus") - - solph.components.Source( - label="source1", - outputs={ - bel: solph.flows.Flow( - nominal_value=100, - emission_factor=[0.5, -1.0, 2.0, 1.0, 0.5, 0.5], - ) - }, - ) - solph.components.Source( - label="source2", - outputs={ - bel: solph.flows.Flow(nominal_value=100, emission_factor=3.5) - }, - ) - - # Should be ignored because the emission attribute is not defined. - solph.components.Source( - label="source3", outputs={bel: solph.flows.Flow(nominal_value=100)} - ) - - om = self.get_om() - - msg = ( - "You have to provide a limit for each period!\n" - "If you provide a scalar value, this will be applied as a " - "limit for each period." - ) - with pytest.raises(ValueError, match=msg): - solph.constraints.emission_limit_per_period(om, limit=None) - def test_linear_transformer(self): """Constraint test of a Transformer without Investment.""" bgas = solph.buses.Bus(label="gas") @@ -471,3 +375,1004 @@ def test_storage(self): ) self.compare_lp_files("storage_multi_period.lp") + + def test_storage_invest_1(self): + """All invest variables are coupled. The invest variables of the Flows + will be created during the initialisation of the storage e.g. battery + """ + bel = solph.buses.Bus(label="electricityBus") + + solph.components.GenericStorage( + label="storage1", + inputs={bel: solph.flows.Flow(variable_costs=56)}, + outputs={bel: solph.flows.Flow(variable_costs=24)}, + nominal_storage_capacity=None, + loss_rate=0.13, + max_storage_level=0.9, + min_storage_level=0.1, + invest_relation_input_capacity=1 / 6, + invest_relation_output_capacity=1 / 6, + lifetime_inflow=20, + lifetime_outflow=20, + inflow_conversion_factor=0.97, + outflow_conversion_factor=0.86, + investment=solph.Investment( + ep_costs=145, + maximum=234, + lifetime=20, + interest_rate=0.05, + overall_maximum=1000, + overall_minimum=2, + ), + ) + + self.compare_lp_files("storage_invest_1_multi_period.lp") + + def test_storage_invest_2(self): + """All can be free extended to their own cost.""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.GenericStorage( + label="storage2", + inputs={ + bel: solph.flows.Flow( + investment=solph.Investment(ep_costs=99, lifetime=20) + ) + }, + outputs={ + bel: solph.flows.Flow( + investment=solph.Investment(ep_costs=9, lifetime=20) + ) + }, + investment=solph.Investment( + ep_costs=145, lifetime=20, existing=20, age=19 + ), + initial_storage_level=0.5, + ) + self.compare_lp_files("storage_invest_2_multi_period.lp") + + def test_storage_invest_3(self): + """The storage capacity is fixed, but the Flows can be extended. + e.g. PHES with a fixed basin but the pump and the turbine can be + adapted + """ + bel = solph.buses.Bus(label="electricityBus") + + solph.components.GenericStorage( + label="storage3", + inputs={ + bel: solph.flows.Flow( + investment=solph.Investment( + ep_costs=99, + lifetime=2, + age=1, + existing=10, + ) + ) + }, + outputs={ + bel: solph.flows.Flow( + investment=solph.Investment(ep_costs=9, lifetime=20) + ) + }, + nominal_storage_capacity=5000, + ) + self.compare_lp_files("storage_invest_3_multi_period.lp") + + def test_storage_invest_4(self): + """Only the storage capacity can be extended.""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.GenericStorage( + label="storage4", + inputs={bel: solph.flows.Flow(nominal_value=80)}, + outputs={bel: solph.flows.Flow(nominal_value=100)}, + investment=solph.Investment( + ep_costs=145, maximum=500, lifetime=2, age=1, existing=100 + ), + ) + self.compare_lp_files("storage_invest_4_multi_period.lp") + + def test_storage_invest_5(self): + """The storage capacity is fixed, but the Flows can be extended. + e.g. PHES with a fixed basin but the pump and the turbine can be + adapted. The installed capacity of the pump is 10 % bigger than the + the capacity of the turbine due to 'invest_relation_input_output=1.1'. + """ + bel = solph.buses.Bus(label="electricityBus") + + solph.components.GenericStorage( + label="storage5", + inputs={ + bel: solph.flows.Flow( + investment=solph.Investment( + ep_costs=99, existing=110, lifetime=20 + ) + ) + }, + outputs={ + bel: solph.flows.Flow( + investment=solph.Investment(existing=100, lifetime=20) + ) + }, + invest_relation_input_output=1.1, + nominal_storage_capacity=10000, + ) + self.compare_lp_files("storage_invest_5_multi_period.lp") + + def test_storage_invest_6(self): + """Like test_storage_invest_5 but there can also be an investment in + the basin. + """ + bel = solph.buses.Bus(label="electricityBus") + + solph.components.GenericStorage( + label="storage6", + inputs={ + bel: solph.flows.InvestmentFlow( + investment=solph.Investment( + ep_costs=99, existing=110, lifetime=20 + ) + ) + }, + outputs={ + bel: solph.flows.InvestmentFlow( + investment=solph.Investment(existing=100, lifetime=20) + ) + }, + invest_relation_input_output=1.1, + investment=solph.Investment( + ep_costs=145, existing=1000, lifetime=20, age=17 + ), + ) + self.compare_lp_files("storage_invest_6_multi_period.lp") + + def test_storage_minimum_invest(self): + """All invest variables are coupled. The invest variables of the Flows + will be created during the initialisation of the storage e.g. battery + """ + bel = solph.buses.Bus(label="electricityBus") + + solph.components.GenericStorage( + label="storage1", + inputs={bel: solph.flows.Flow()}, + outputs={bel: solph.flows.Flow()}, + investment=solph.Investment( + ep_costs=145, minimum=100, maximum=200, lifetime=40 + ), + lifetime_inflow=40, + lifetime_outflow=40, + ) + + self.compare_lp_files("storage_invest_minimum_multi_period.lp") + + def test_storage_unbalanced(self): + """Testing a unbalanced storage (e.g. battery).""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.GenericStorage( + label="storage1", + inputs={bel: solph.flows.Flow()}, + outputs={bel: solph.flows.Flow()}, + nominal_storage_capacity=1111, + initial_storage_level=None, + balanced=False, + invest_relation_input_capacity=1, + invest_relation_output_capacity=1, + ) + self.compare_lp_files("storage_unbalanced_multi_period.lp") + + def test_storage_fixed_losses(self): + """ """ + bel = solph.buses.Bus(label="electricityBus") + + solph.components.GenericStorage( + label="storage_no_invest", + inputs={ + bel: solph.flows.Flow(nominal_value=16667, variable_costs=56) + }, + outputs={ + bel: solph.flows.Flow(nominal_value=16667, variable_costs=24) + }, + nominal_storage_capacity=1e5, + loss_rate=0.13, + fixed_losses_relative=0.01, + fixed_losses_absolute=3, + inflow_conversion_factor=0.97, + outflow_conversion_factor=0.86, + initial_storage_level=0.4, + ) + + self.compare_lp_files("storage_fixed_losses_multi_period.lp") + + def test_storage_invest_1_fixed_losses(self): + """Test error for fixed losses""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.GenericStorage( + label="storage1", + inputs={bel: solph.flows.Flow(variable_costs=56)}, + outputs={bel: solph.flows.Flow(variable_costs=24)}, + nominal_storage_capacity=None, + loss_rate=0.13, + fixed_losses_relative=0.01, + fixed_losses_absolute=3, + max_storage_level=0.9, + min_storage_level=0.1, + invest_relation_input_capacity=1 / 6, + invest_relation_output_capacity=1 / 6, + inflow_conversion_factor=0.97, + outflow_conversion_factor=0.86, + lifetime_inflow=40, + lifetime_outflow=40, + investment=solph.Investment( + ep_costs=145, + maximum=234, + lifetime=20, + interest_rate=0.05, + overall_maximum=1000, + overall_minimum=2, + ), + ) + + msg = ( + "For a multi-period investment model, fixed absolute" + " losses are not supported. Please remove parameter." + ) + with pytest.raises(ValueError, match=msg): + self.get_om() + + def test_storage_invest_1_initial_storage_level(self): + """Test warning for initial storage level + with multi-period investments""" + bel = solph.buses.Bus(label="electricityBus") + solph.components.GenericStorage( + label="storage1", + inputs={bel: solph.flows.Flow(variable_costs=56)}, + outputs={bel: solph.flows.Flow(variable_costs=24)}, + nominal_storage_capacity=None, + loss_rate=0.13, + max_storage_level=0.9, + min_storage_level=0.1, + invest_relation_input_capacity=1 / 6, + invest_relation_output_capacity=1 / 6, + inflow_conversion_factor=0.97, + outflow_conversion_factor=0.86, + lifetime_inflow=40, + lifetime_outflow=40, + initial_storage_level=0.5, + investment=solph.Investment( + ep_costs=145, + maximum=234, + lifetime=20, + interest_rate=0.05, + overall_maximum=1000, + overall_minimum=2, + ), + ) + + msg = ( + "For a multi-period model, initial_storage_level is" + " not supported.\nIt is suggested to remove that" + " parameter since it has no effect.\nstorage_content" + " will be zero, until there is some usable storage " + " capacity installed." + ) + with warnings.catch_warnings(record=True) as w: + self.get_om() + assert msg in str(w[1].message) + + def test_transformer(self): + """Constraint test of a LinearN1Transformer without Investment.""" + bgas = solph.buses.Bus(label="gasBus") + bbms = solph.buses.Bus(label="biomassBus") + bel = solph.buses.Bus(label="electricityBus") + bth = solph.buses.Bus(label="thermalBus") + + solph.components.Transformer( + label="powerplantGasBiomass", + inputs={bbms: solph.flows.Flow(), bgas: solph.flows.Flow()}, + outputs={ + bel: solph.flows.Flow(variable_costs=50), + bth: solph.flows.Flow(nominal_value=5e10, variable_costs=20), + }, + conversion_factors={bgas: 0.4, bbms: 0.1, bel: 0.3, bth: 0.5}, + ) + + self.compare_lp_files("transformer_multi_period.lp") + + def test_transformer_invest(self): + """Constraint test of a LinearN1Transformer with Investment.""" + + bgas = solph.buses.Bus(label="gasBus") + bcoal = solph.buses.Bus(label="coalBus") + bel = solph.buses.Bus(label="electricityBus") + bth = solph.buses.Bus(label="thermalBus") + + solph.components.Transformer( + label="powerplant_gas_coal", + inputs={bgas: solph.flows.Flow(), bcoal: solph.flows.Flow()}, + outputs={ + bel: solph.flows.Flow( + variable_costs=50, + investment=solph.Investment( + maximum=1000, + ep_costs=20, + lifetime=20, + ), + ), + bth: solph.flows.Flow(variable_costs=20), + }, + conversion_factors={bgas: 0.58, bcoal: 0.2, bel: 0.3, bth: 0.5}, + ) + + self.compare_lp_files("transformer_invest_multi_period.lp") + + def test_transformer_invest_with_existing(self): + """Constraint test of a LinearN1Transformer with Investment.""" + + bgas = solph.buses.Bus(label="gasBus") + bcoal = solph.buses.Bus(label="coalBus") + bel = solph.buses.Bus(label="electricityBus") + bth = solph.buses.Bus(label="thermalBus") + + solph.components.Transformer( + label="powerplant_gas_coal", + inputs={bgas: solph.flows.Flow(), bcoal: solph.flows.Flow()}, + outputs={ + bel: solph.flows.Flow( + variable_costs=50, + investment=solph.Investment( + maximum=1000, + ep_costs=20, + existing=200, + lifetime=2, + age=1, + ), + ), + bth: solph.flows.Flow(variable_costs=20), + }, + conversion_factors={bgas: 0.58, bcoal: 0.2, bel: 0.3, bth: 0.5}, + ) + + self.compare_lp_files( + "transformer_invest_with_existing_multi_period.lp" + ) + + def test_linear_transformer_chp(self): + """ + Constraint test of a Transformer without Investment (two outputs). + """ + bgas = solph.buses.Bus(label="gasBus") + bheat = solph.buses.Bus(label="heatBus") + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Transformer( + label="CHPpowerplantGas", + inputs={ + bgas: solph.flows.Flow(nominal_value=1e11, variable_costs=50) + }, + outputs={bel: solph.flows.Flow(), bheat: solph.flows.Flow()}, + conversion_factors={bel: 0.4, bheat: 0.5}, + ) + + self.compare_lp_files("linear_transformer_chp_multi_period.lp") + + def test_linear_transformer_chp_invest(self): + """Constraint test of a Transformer with Investment (two outputs).""" + + bgas = solph.buses.Bus(label="gasBus") + bheat = solph.buses.Bus(label="heatBus") + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Transformer( + label="chp_powerplant_gas", + inputs={ + bgas: solph.flows.Flow( + variable_costs=50, + investment=solph.Investment( + maximum=1000, ep_costs=20, lifetime=50 + ), + ) + }, + outputs={bel: solph.flows.Flow(), bheat: solph.flows.Flow()}, + conversion_factors={bel: 0.4, bheat: 0.5}, + ) + + self.compare_lp_files("linear_transformer_chp_invest_multi_period.lp") + + def test_variable_chp(self): + """Test ExctractionTurbineCHP basic functionality""" + bel = solph.buses.Bus(label="electricityBus") + bth = solph.buses.Bus(label="heatBus") + bgas = solph.buses.Bus(label="commodityBus") + + solph.components.ExtractionTurbineCHP( + label="variable_chp_gas1", + inputs={bgas: solph.flows.Flow(nominal_value=100)}, + outputs={bel: solph.flows.Flow(), bth: solph.flows.Flow()}, + conversion_factors={bel: 0.3, bth: 0.5}, + conversion_factor_full_condensation={bel: 0.5}, + ) + + solph.components.ExtractionTurbineCHP( + label="variable_chp_gas2", + inputs={bgas: solph.flows.Flow(nominal_value=100)}, + outputs={bel: solph.flows.Flow(), bth: solph.flows.Flow()}, + conversion_factors={bel: 0.3, bth: 0.5}, + conversion_factor_full_condensation={bel: 0.5}, + ) + + self.compare_lp_files("variable_chp_multi_period.lp") + + def test_emission_budget_limit(self): + """Test emissions budget limit constraint""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Source( + label="source1", + outputs={ + bel: solph.flows.Flow( + nominal_value=100, + emission_factor=[0.5, -1.0, 2.0, 1.0, 0.5, 0.5], + ) + }, + ) + solph.components.Source( + label="source2", + outputs={ + bel: solph.flows.Flow(nominal_value=100, emission_factor=3.5) + }, + ) + + # Should be ignored because the emission attribute is not defined. + solph.components.Source( + label="source3", outputs={bel: solph.flows.Flow(nominal_value=100)} + ) + + om = self.get_om() + + solph.constraints.emission_limit(om, limit=777) + + self.compare_lp_files("emission_budget_limit.lp", my_om=om) + + def test_periodical_emission_limit(self): + """Test periodical emissions constraint""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Source( + label="source1", + outputs={ + bel: solph.flows.Flow( + nominal_value=100, + emission_factor=[0.5, -1.0, 2.0, 1.0, 0.5, 0.5], + ) + }, + ) + solph.components.Source( + label="source2", + outputs={ + bel: solph.flows.Flow(nominal_value=100, emission_factor=3.5) + }, + ) + + # Should be ignored because the emission attribute is not defined. + solph.components.Source( + label="source3", outputs={bel: solph.flows.Flow(nominal_value=100)} + ) + + om = self.get_om() + + solph.constraints.emission_limit_per_period(om, limit=[300, 200, 100]) + + self.compare_lp_files("periodical_emission_limit.lp", my_om=om) + + def test_periodical_emission_limit_missing_limit(self): + """Test error for periodical emissions constraint""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Source( + label="source1", + outputs={ + bel: solph.flows.Flow( + nominal_value=100, + emission_factor=[0.5, -1.0, 2.0, 1.0, 0.5, 0.5], + ) + }, + ) + solph.components.Source( + label="source2", + outputs={ + bel: solph.flows.Flow(nominal_value=100, emission_factor=3.5) + }, + ) + + # Should be ignored because the emission attribute is not defined. + solph.components.Source( + label="source3", outputs={bel: solph.flows.Flow(nominal_value=100)} + ) + + om = self.get_om() + + msg = ( + "You have to provide a limit for each period!\n" + "If you provide a scalar value, this will be applied as a " + "limit for each period." + ) + with pytest.raises(ValueError, match=msg): + solph.constraints.emission_limit_per_period(om, limit=None) + + def test_flow_count_limit(self): + """Test limiting the count of nonconvex flows""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Source( + label="source1", + outputs={ + bel: solph.flows.Flow( + nonconvex=solph.NonConvex(), + nominal_value=100, + emission_factor=0.5, + ) + }, + ) + solph.components.Source( + label="source2", + outputs={ + bel: solph.flows.Flow( + nonconvex=solph.NonConvex(), + nominal_value=100, + emission_factor=3.5, + ) + }, + ) + + # Should be ignored because emission_factor is not defined. + solph.components.Source( + label="source3", + outputs={ + bel: solph.flows.Flow( + nonconvex=solph.NonConvex(), nominal_value=100 + ) + }, + ) + + # Should be ignored because it is not NonConvex. + solph.components.Source( + label="source4", + outputs={ + bel: solph.flows.Flow( + emission_factor=1.5, min=0.3, nominal_value=100 + ) + }, + ) + + om = self.get_om() + + # one of the two flows has to be active + solph.constraints.limit_active_flow_count_by_keyword( + om, "emission_factor", lower_limit=1, upper_limit=2 + ) + + self.compare_lp_files("flow_count_limit_multi_period.lp", my_om=om) + + def test_shared_limit(self): + """Test an overall limit shared among components""" + b1 = solph.buses.Bus(label="bus") + + storage1 = solph.components.GenericStorage( + label="storage1", + nominal_storage_capacity=5, + inputs={b1: solph.flows.Flow()}, + outputs={b1: solph.flows.Flow()}, + ) + storage2 = solph.components.GenericStorage( + label="storage2", + nominal_storage_capacity=5, + inputs={b1: solph.flows.Flow()}, + outputs={b1: solph.flows.Flow()}, + ) + + model = self.get_om() + + components = [storage1, storage2] + + solph.constraints.shared_limit( + model, + model.GenericStorageBlock.storage_content, + "limit_storage", + components, + [0.5, 1.25], + upper_limit=7, + ) + + self.compare_lp_files("shared_limit_multi_period.lp", my_om=model) + + def test_equate_variables_constraint(self): + """Testing the equate_variables function in the constraint module.""" + bus1 = solph.buses.Bus(label="Bus1") + storage = solph.components.GenericStorage( + label="storage", + invest_relation_input_capacity=0.2, + invest_relation_output_capacity=0.2, + inputs={bus1: solph.flows.Flow()}, + outputs={bus1: solph.flows.Flow()}, + lifetime_inflow=3, + lifetime_outflow=3, + investment=solph.Investment(ep_costs=145, lifetime=3), + ) + sink = solph.components.Sink( + label="Sink", + inputs={ + bus1: solph.flows.Flow( + investment=solph.Investment(ep_costs=500, lifetime=3) + ) + }, + ) + source = solph.components.Source( + label="Source", + outputs={ + bus1: solph.flows.Flow( + investment=solph.Investment(ep_costs=123, lifetime=3) + ) + }, + ) + om = self.get_om() + solph.constraints.equate_variables( + om, + om.InvestmentFlowBlock.invest[source, bus1, 0], + om.InvestmentFlowBlock.invest[bus1, sink, 0], + 2, + ) + solph.constraints.equate_variables( + om, + om.InvestmentFlowBlock.invest[source, bus1, 0], + om.GenericInvestmentStorageBlock.invest[storage, 0], + ) + + self.compare_lp_files("connect_investment_multi_period.lp", my_om=om) + + def test_gradient(self): + """Testing gradient constraints""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Source( + label="powerplant", + outputs={ + bel: solph.flows.Flow( + nominal_value=999, + variable_costs=23, + positive_gradient={"ub": 0.03}, + negative_gradient={"ub": 0.05}, + ) + }, + ) + + self.compare_lp_files("source_with_gradient_multi_period.lp") + + def test_nonconvex_gradient(self): + """Testing gradient constraints""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Source( + label="powerplant", + outputs={ + bel: solph.flows.Flow( + nominal_value=999, + variable_costs=23, + nonconvex=solph.NonConvex( + positive_gradient={"ub": 0.03}, + negative_gradient={"ub": 0.05}, + ), + ) + }, + ) + + self.compare_lp_files("source_with_nonconvex_gradient_multi_period.lp") + + def test_periodical_investment_limit(self): + """Testing the investment_limit function in the constraint module.""" + bus1 = solph.buses.Bus(label="Bus1") + solph.components.GenericStorage( + label="storage_invest_limit", + invest_relation_input_capacity=0.2, + invest_relation_output_capacity=0.2, + inputs={bus1: solph.flows.Flow()}, + outputs={bus1: solph.flows.Flow()}, + lifetime_inflow=20, + lifetime_outflow=20, + investment=solph.Investment(ep_costs=145, lifetime=30), + ) + solph.components.Source( + label="Source", + outputs={ + bus1: solph.flows.Flow( + investment=solph.Investment(ep_costs=123, lifetime=100) + ) + }, + ) + om = self.get_om() + solph.constraints.investment_limit_per_period( + om, limit=[500, 400, 300] + ) + + self.compare_lp_files("periodical_investment_limit.lp", my_om=om) + + def test_perioical_investment_limit_with_dsm1(self): + """Testing the investment_limit function in the constraint module.""" + bus1 = solph.buses.Bus(label="Bus1") + solph.components.Source( + label="Source", + outputs={ + bus1: solph.flows.Flow( + investment=solph.Investment(ep_costs=123, lifetime=100) + ) + }, + ) + solph.components.experimental.SinkDSM( + label="sink_dsm_DIW", + approach="DIW", + inputs={bus1: solph.flows.Flow()}, + demand=[1] * 6, + capacity_up=[0.5] * 6, + capacity_down=[0.5] * 6, + flex_share_up=1, + flex_share_down=1, + delay_time=1, + cost_dsm_down_shift=0.5, + cost_dsm_up=0.5, + shed_eligibility=False, + investment=solph.Investment( + ep_costs=100, + existing=50, + minimum=33, + maximum=100, + age=1, + lifetime=2, + overall_maximum=1000, + overall_minimum=200, + ), + ) + om = self.get_om() + solph.constraints.investment_limit_per_period( + om, limit=[400, 300, 200] + ) + + self.compare_lp_files( + "periodical_investment_limit_with_dsm_DIW.lp", my_om=om + ) + + def test_periodical_investment_limit_with_dsm2(self): + """Testing the investment_limit function in the constraint module.""" + bus1 = solph.buses.Bus(label="Bus1") + solph.components.Source( + label="Source", + outputs={ + bus1: solph.flows.Flow( + investment=solph.Investment(ep_costs=123, lifetime=100) + ) + }, + ) + solph.components.experimental.SinkDSM( + label="sink_dsm_DLR", + approach="DLR", + inputs={bus1: solph.flows.Flow()}, + demand=[1] * 6, + capacity_up=[0.5] * 6, + capacity_down=[0.5] * 6, + flex_share_up=1, + flex_share_down=1, + delay_time=1, + shift_time=1, + cost_dsm_down_shift=0.5, + cost_dsm_up=0.5, + shed_eligibility=False, + investment=solph.Investment( + ep_costs=100, + existing=50, + minimum=33, + maximum=100, + age=1, + lifetime=2, + overall_maximum=1000, + overall_minimum=200, + ), + ) + om = self.get_om() + solph.constraints.investment_limit_per_period( + om, limit=[400, 300, 200] + ) + + self.compare_lp_files( + "periodical_investment_limit_with_dsm_DLR.lp", my_om=om + ) + + def test_periodical_investment_limit_with_dsm3(self): + """Testing the investment_limit function in the constraint module.""" + bus1 = solph.buses.Bus(label="Bus1") + solph.components.Source( + label="Source", + outputs={ + bus1: solph.flows.Flow( + investment=solph.Investment(ep_costs=123, lifetime=100) + ) + }, + ) + solph.components.experimental.SinkDSM( + label="sink_dsm_oemof", + approach="oemof", + inputs={bus1: solph.flows.Flow()}, + demand=[1] * 6, + capacity_up=[0.5] * 6, + capacity_down=[0.5] * 6, + flex_share_up=1, + flex_share_down=1, + delay_time=1, + shift_interval=2, + cost_dsm_down_shift=0.5, + cost_dsm_up=0.5, + shed_eligibility=False, + investment=solph.Investment( + ep_costs=100, + existing=50, + minimum=33, + maximum=100, + age=1, + lifetime=2, + overall_maximum=1000, + overall_minimum=200, + ), + ) + om = self.get_om() + solph.constraints.investment_limit_per_period( + om, limit=[400, 300, 200] + ) + + self.compare_lp_files( + "periodical_investment_limit_with_dsm_oemof.lp", my_om=om + ) + + def test_min_max_runtime(self): + """Testing min and max runtimes for nonconvex flows.""" + bus_t = solph.buses.Bus(label="Bus_T") + solph.components.Source( + label="cheap_plant_min_down_constraints", + outputs={ + bus_t: solph.flows.NonConvexFlow( + nominal_value=10, + min=0.5, + max=1.0, + variable_costs=10, + minimum_downtime=4, + minimum_uptime=2, + initial_status=1, + startup_costs=5, + shutdown_costs=7, + ) + }, + ) + self.compare_lp_files("min_max_runtime_multi_period.lp") + + def test_activity_costs(self): + """Testing activity_costs attribute for nonconvex flows.""" + bus_t = solph.buses.Bus(label="Bus_C") + solph.components.Source( + label="cheap_plant_activity_costs", + outputs={ + bus_t: solph.flows.NonConvexFlow( + nominal_value=10, + min=0.5, + max=1.0, + variable_costs=10, + activity_costs=2, + ) + }, + ) + self.compare_lp_files("activity_costs_multi_period.lp") + + def test_inactivity_costs(self): + """Testing inactivity_costs attribute for nonconvex flows.""" + bus_t = solph.buses.Bus(label="Bus_C") + solph.components.Source( + label="cheap_plant_inactivity_costs", + outputs={ + bus_t: solph.flows.NonConvexFlow( + nominal_value=10, + min=0.5, + max=1.0, + variable_costs=10, + inactivity_costs=2, + ) + }, + ) + self.compare_lp_files("inactivity_costs_multi_period.lp") + + def test_piecewise_linear_transformer_cc(self): + """Testing PiecewiseLinearTransformer using CC formulation.""" + bgas = solph.buses.Bus(label="gasBus") + bel = solph.buses.Bus(label="electricityBus") + solph.components.experimental.PiecewiseLinearTransformer( + label="pwltf", + inputs={ + bgas: solph.flows.Flow(nominal_value=100, variable_costs=1) + }, + outputs={bel: solph.flows.Flow()}, + in_breakpoints=[0, 25, 50, 75, 100], + conversion_function=lambda x: x ** 2, + pw_repn="CC", + ) + self.compare_lp_files( + "piecewise_linear_transformer_cc_multi_period.lp" + ) + + def test_piecewise_linear_transformer_dcc(self): + """Testing PiecewiseLinearTransformer using DCC formulation.""" + bgas = solph.buses.Bus(label="gasBus") + bel = solph.buses.Bus(label="electricityBus") + solph.components.experimental.PiecewiseLinearTransformer( + label="pwltf", + inputs={ + bgas: solph.flows.Flow(nominal_value=100, variable_costs=1) + }, + outputs={bel: solph.flows.Flow()}, + in_breakpoints=[0, 25, 50, 75, 100], + conversion_function=lambda x: x ** 2, + pw_repn="DCC", + ) + self.compare_lp_files( + "piecewise_linear_transformer_dcc_multi_period.lp" + ) + + def test_maximum_startups(self): + """Testing maximum_startups attribute for nonconvex flows.""" + bus_t = solph.buses.Bus(label="Bus_C") + solph.components.Source( + label="cheap_plant_maximum_startups", + outputs={ + bus_t: solph.flows.Flow( + nominal_value=10, + min=0.5, + max=1.0, + variable_costs=10, + nonconvex=solph.NonConvex(maximum_startups=2), + ) + }, + ) + self.compare_lp_files("maximum_startups_multi_period.lp") + + def test_maximum_shutdowns(self): + """Testing maximum_shutdowns attribute for nonconvex flows.""" + bus_t = solph.buses.Bus(label="Bus_C") + solph.components.Source( + label="cheap_plant_maximum_shutdowns", + outputs={ + bus_t: solph.flows.Flow( + nominal_value=10, + min=0.5, + max=1.0, + variable_costs=10, + nonconvex=solph.NonConvex(maximum_shutdowns=2), + ) + }, + ) + self.compare_lp_files("maximum_shutdowns_multi_period.lp") + + def test_offsettransformer(self): + """Constraint test of a OffsetTransformer.""" + bgas = solph.buses.Bus(label="gasBus") + bth = solph.buses.Bus(label="thermalBus") + + solph.components.OffsetTransformer( + label="gasboiler", + inputs={ + bgas: solph.flows.Flow( + nonconvex=solph.NonConvex(), + nominal_value=100, + min=0.32, + ) + }, + outputs={bth: solph.flows.Flow()}, + coefficients=[-17, 0.9], + ) + + self.compare_lp_files("offsettransformer_multi_period.lp") From c480312d04eb9b4852828c467e25f905c389d388 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Tue, 8 Mar 2022 11:56:56 +0100 Subject: [PATCH 0168/1363] Add fixed costs for existing units in multi-period components (scalar adder to objective) --- src/oemof/solph/_options.py | 2 +- .../solph/components/_generic_storage.py | 18 ++++++++ .../components/experimental/_sink_dsm.py | 45 +++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/oemof/solph/_options.py b/src/oemof/solph/_options.py index ba7cbb06b..8ac82dc1c 100644 --- a/src/oemof/solph/_options.py +++ b/src/oemof/solph/_options.py @@ -56,7 +56,7 @@ class Investment: interest_rate : float Interest rate for calculating annuities when investing in that unit; only applicable for multi-period models - fixed_costs : list of float + fixed_costs : float or list of float Fixed costs in each period; only applicable for multi-period models diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index 29301c15b..550e921a8 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -896,6 +896,13 @@ def _create(self, group=None): n for n in group if n.investment.overall_minimum is not None ] ) + + self.EXISTING_INVESTSTORAGES = Set( + initialize=[ + n for n in group if n.investment.existing is not None + ] + ) + # ######################### Variables ################################ self.storage_content = Var( self.INVESTSTORAGES, m.TIMESTEPS, within=NonNegativeReals @@ -1452,6 +1459,17 @@ def _objective_expression(self): ) ) + for n in self.EXISTING_INVESTSTORAGES: + if n.investment.fixed_costs[0] is not None: + lifetime = n.investment.lifetime + age = n.investment.age + fixed_costs += sum( + n.investment.existing + * n.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(0, lifetime - age) + ) + self.investment_costs = Expression(expr=investment_costs) self.period_investment_costs = period_investment_costs self.fixed_costs = Expression(expr=fixed_costs) diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index 87b24102e..9cf478c71 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -881,6 +881,10 @@ def _create(self, group=None): ] ) + self.EXISTING_INVESTDSM = Set( + initialize=[g for g in group if g.investment.existing is not None] + ) + # ************* VARIABLES ***************************** # Define bounds for investments in demand response @@ -1315,6 +1319,17 @@ def _objective_expression(self): * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) + for g in self.EXISTING_INVESTDSM: + if g.investment.fixed_costs[0] is not None: + lifetime = g.investment.lifetime + age = g.investment.age + fixed_costs += sum( + g.investment.existing + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(0, lifetime - age) + ) + self.variable_costs = Expression(expr=variable_costs) self.fixed_costs = Expression(expr=fixed_costs) self.investment_costs = Expression(expr=investment_costs) @@ -2151,6 +2166,10 @@ def _create(self, group=None): ] ) + self.EXISTING_INVESTDSM = Set( + initialize=[g for g in group if g.investment.existing is not None] + ) + # ************* VARIABLES ***************************** # Define bounds for investments in demand response @@ -2951,6 +2970,17 @@ def _objective_expression(self): * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) + for g in self.EXISTING_INVESTDSM: + if g.investment.fixed_costs[0] is not None: + lifetime = g.investment.lifetime + age = g.investment.age + fixed_costs += sum( + g.investment.existing + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(0, lifetime - age) + ) + self.variable_costs = Expression(expr=variable_costs) self.fixed_costs = Expression(expr=fixed_costs) self.investment_costs = Expression(expr=investment_costs) @@ -4215,6 +4245,10 @@ def _create(self, group=None): ] ) + self.EXISTING_INVESTDSM = Set( + initialize=[g for g in group if g.investment.existing is not None] + ) + # ************* VARIABLES ***************************** # Define bounds for investments in demand response @@ -5226,6 +5260,17 @@ def _objective_expression(self): * ((1 + m.discount_rate) ** -m.es.periods_years[p]) ) + for g in self.EXISTING_INVESTDSM: + if g.investment.fixed_costs[0] is not None: + lifetime = g.investment.lifetime + age = g.investment.age + fixed_costs += sum( + g.investment.existing + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range(0, lifetime - age) + ) + self.variable_costs = Expression(expr=variable_costs) self.fixed_costs = Expression(expr=fixed_costs) self.investment_costs = Expression(expr=investment_costs) From 9db0ea7485c057116835f08fef3138459ac69699 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Tue, 8 Mar 2022 12:50:58 +0100 Subject: [PATCH 0169/1363] Add tests and bug fix --- src/oemof/solph/flows/_flow.py | 2 +- tests/constraint_tests.py | 38 + .../dsm_module_DIW_extended_multi_period.lp | 398 +++++++++ .../dsm_module_DIW_invest_multi_period.lp | 495 ++++++++++ tests/lp_files/dsm_module_DIW_multi_period.lp | 352 ++++++++ .../dsm_module_DLR_extended_multi_period.lp | 709 +++++++++++++++ .../dsm_module_DLR_invest_multi_period.lp | 844 ++++++++++++++++++ tests/lp_files/dsm_module_DLR_multi_period.lp | 700 +++++++++++++++ .../dsm_module_oemof_extended_multi_period.lp | 195 ++++ .../dsm_module_oemof_invest_multi_period.lp | 309 +++++++ .../lp_files/dsm_module_oemof_multi_period.lp | 207 +++++ tests/lp_files/fixed_costs_sources.lp | 93 ++ .../flow_invest_with_offset_multi_period.lp | 209 +++++ ...est_with_offset_no_minimum_multi_period.lp | 206 +++++ ...flow_invest_without_offset_multi_period.lp | 206 +++++ tests/lp_files/flow_reaching_lifetime.lp | 56 ++ .../flow_reaching_lifetime_initial_age.lp | 64 ++ tests/lp_files/integer_source.lp | 52 ++ ...orage_invest_all_nonconvex_multi_period.lp | 540 +++++++++++ tests/lp_files/storage_invest_multi_period.lp | 221 +++++ ...storage_invest_with_offset_multi_period.lp | 528 +++++++++++ ...rage_invest_without_offset_multi_period.lp | 525 +++++++++++ tests/lp_files/summed_min_source.lp | 42 + .../summed_min_source_multi_period.lp | 66 ++ tests/multi_period_constraint_tests.py | 613 +++++++++++++ 25 files changed, 7669 insertions(+), 1 deletion(-) create mode 100644 tests/lp_files/dsm_module_DIW_extended_multi_period.lp create mode 100644 tests/lp_files/dsm_module_DIW_invest_multi_period.lp create mode 100644 tests/lp_files/dsm_module_DIW_multi_period.lp create mode 100644 tests/lp_files/dsm_module_DLR_extended_multi_period.lp create mode 100644 tests/lp_files/dsm_module_DLR_invest_multi_period.lp create mode 100644 tests/lp_files/dsm_module_DLR_multi_period.lp create mode 100644 tests/lp_files/dsm_module_oemof_extended_multi_period.lp create mode 100644 tests/lp_files/dsm_module_oemof_invest_multi_period.lp create mode 100644 tests/lp_files/dsm_module_oemof_multi_period.lp create mode 100644 tests/lp_files/fixed_costs_sources.lp create mode 100644 tests/lp_files/flow_invest_with_offset_multi_period.lp create mode 100644 tests/lp_files/flow_invest_with_offset_no_minimum_multi_period.lp create mode 100644 tests/lp_files/flow_invest_without_offset_multi_period.lp create mode 100644 tests/lp_files/flow_reaching_lifetime.lp create mode 100644 tests/lp_files/flow_reaching_lifetime_initial_age.lp create mode 100644 tests/lp_files/integer_source.lp create mode 100644 tests/lp_files/storage_invest_all_nonconvex_multi_period.lp create mode 100644 tests/lp_files/storage_invest_multi_period.lp create mode 100644 tests/lp_files/storage_invest_with_offset_multi_period.lp create mode 100644 tests/lp_files/storage_invest_without_offset_multi_period.lp create mode 100644 tests/lp_files/summed_min_source.lp create mode 100644 tests/lp_files/summed_min_source_multi_period.lp diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index b0b4eb6c4..5c78a9119 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -573,7 +573,7 @@ def _negative_gradient_flow_rule(model): def _integer_flow_rule(block, ii, oi, pi, ti): """Force flow variable to NonNegativeInteger values.""" - return self.integer_flow[ii, oi, pi, ti] == m.flow[ii, oi, pi, ti] + return self.integer_flow[ii, oi, ti] == m.flow[ii, oi, pi, ti] self.integer_flow_constr = Constraint( self.INTEGER_FLOWS, m.TIMEINDEX, rule=_integer_flow_rule diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index 3735442f6..e52782dd2 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -1651,3 +1651,41 @@ def test_integral_limit_error_no_multi_period(self): solph.constraints.generic_periodical_integral_limit( om, keyword="space" ) + + def test_summed_min_max_source(self): + """Constraints test summed_min and summed_max attribute of flow""" + + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Sink( + label="excess", + inputs={ + bel: solph.flows.Flow( + summed_min=3, + summed_max=100, + variable_costs=25, + max=0.8, + nominal_value=10 + ) + }, + ) + + self.compare_lp_files("summed_min_source.lp") + + def test_integer_flow_source(self): + """Test source with integer output""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Sink( + label="excess", + inputs={ + bel: solph.flows.Flow( + variable_costs=25, + max=1, + nominal_value=10, + integer=True + ) + }, + ) + + self.compare_lp_files("integer_source.lp") diff --git a/tests/lp_files/dsm_module_DIW_extended_multi_period.lp b/tests/lp_files/dsm_module_DIW_extended_multi_period.lp new file mode 100644 index 000000000..930cda484 --- /dev/null +++ b/tests/lp_files/dsm_module_DIW_extended_multi_period.lp @@ -0,0 +1,398 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++100 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_0) ++100 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) ++98.039215686274503 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) ++98.039215686274503 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_3) ++96.116878123798529 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_4) ++96.116878123798529 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) ++0.98039215686274506 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_2) ++0.98039215686274506 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_3) ++0.96116878123798533 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_4) ++0.96116878123798533 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_5) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_1) ++0.98039215686274506 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_2) ++0.98039215686274506 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_3) ++0.96116878123798533 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_4) ++0.96116878123798533 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_5) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_1) ++0.98039215686274506 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_2) ++0.98039215686274506 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_3) ++0.96116878123798533 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_4) ++0.96116878123798533 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_5) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_1) ++0.98039215686274506 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_2) ++0.98039215686274506 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_3) ++0.96116878123798533 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_4) ++0.96116878123798533 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_5) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_1) ++0.98039215686274506 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_2) ++0.98039215686274506 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_3) ++0.96116878123798533 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_4) ++0.96116878123798533 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_5) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_1) ++0.98039215686274506 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_2) ++0.98039215686274506 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_3) ++0.96116878123798533 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_4) ++0.96116878123798533 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_5) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_0) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_1) ++0.98039215686274506 SinkDSMDIWBlock_dsm_up(demand_dsm_2) ++0.98039215686274506 SinkDSMDIWBlock_dsm_up(demand_dsm_3) ++0.96116878123798533 SinkDSMDIWBlock_dsm_up(demand_dsm_4) ++0.96116878123798533 SinkDSMDIWBlock_dsm_up(demand_dsm_5) + +s.t. + +c_e_BusBlock_balance(bus_elec_0_0)_: ++1 flow(bus_elec_demand_dsm_0_0) += 0 + +c_e_BusBlock_balance(bus_elec_0_1)_: ++1 flow(bus_elec_demand_dsm_0_1) += 0 + +c_e_BusBlock_balance(bus_elec_1_2)_: ++1 flow(bus_elec_demand_dsm_1_2) += 0 + +c_e_BusBlock_balance(bus_elec_1_3)_: ++1 flow(bus_elec_demand_dsm_1_3) += 0 + +c_e_BusBlock_balance(bus_elec_2_4)_: ++1 flow(bus_elec_demand_dsm_2_4) += 0 + +c_e_BusBlock_balance(bus_elec_2_5)_: ++1 flow(bus_elec_demand_dsm_2_5) += 0 + +c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_0_0)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_0) +-1 SinkDSMDIWBlock_dsm_up(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_0) += 1 + +c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_0_1)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_1) +-1 SinkDSMDIWBlock_dsm_up(demand_dsm_1) ++1 flow(bus_elec_demand_dsm_0_1) += 0.90000000000000002 + +c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_1_2)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_2) +-1 SinkDSMDIWBlock_dsm_up(demand_dsm_2) ++1 flow(bus_elec_demand_dsm_1_2) += 0.80000000000000004 + +c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_1_3)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_3) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_3) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_3) +-1 SinkDSMDIWBlock_dsm_up(demand_dsm_3) ++1 flow(bus_elec_demand_dsm_1_3) += 0.69999999999999996 + +c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_2_4)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_4) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_4) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_4) +-1 SinkDSMDIWBlock_dsm_up(demand_dsm_4) ++1 flow(bus_elec_demand_dsm_2_4) += 0.69999999999999996 + +c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_2_5)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_5) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_5) +-1 SinkDSMDIWBlock_dsm_up(demand_dsm_5) ++1 flow(bus_elec_demand_dsm_2_5) += 0.69999999999999996 + +c_e_SinkDSMDIWBlock_dsm_updo_constraint(demand_dsm_0)_: +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) ++0.98999999999999999 SinkDSMDIWBlock_dsm_up(demand_dsm_0) += 0 + +c_e_SinkDSMDIWBlock_dsm_updo_constraint(demand_dsm_1)_: +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_0) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_1) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_2) ++0.98999999999999999 SinkDSMDIWBlock_dsm_up(demand_dsm_1) += 0 + +c_e_SinkDSMDIWBlock_dsm_updo_constraint(demand_dsm_2)_: +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_1) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_2) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_3) ++0.98999999999999999 SinkDSMDIWBlock_dsm_up(demand_dsm_2) += 0 + +c_e_SinkDSMDIWBlock_dsm_updo_constraint(demand_dsm_3)_: +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_2) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_3) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_4) ++0.98999999999999999 SinkDSMDIWBlock_dsm_up(demand_dsm_3) += 0 + +c_e_SinkDSMDIWBlock_dsm_updo_constraint(demand_dsm_4)_: +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_3) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_4) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_5) ++0.98999999999999999 SinkDSMDIWBlock_dsm_up(demand_dsm_4) += 0 + +c_e_SinkDSMDIWBlock_dsm_updo_constraint(demand_dsm_5)_: +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_4) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_5) ++0.98999999999999999 SinkDSMDIWBlock_dsm_up(demand_dsm_5) += 0 + +c_u_SinkDSMDIWBlock_dsm_up_constraint(demand_dsm_0)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_0) +<= 0.5 + +c_u_SinkDSMDIWBlock_dsm_up_constraint(demand_dsm_1)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_1) +<= 0.40000000000000002 + +c_u_SinkDSMDIWBlock_dsm_up_constraint(demand_dsm_2)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_2) +<= 0.5 + +c_u_SinkDSMDIWBlock_dsm_up_constraint(demand_dsm_3)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_3) +<= 0.29999999999999999 + +c_u_SinkDSMDIWBlock_dsm_up_constraint(demand_dsm_4)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_4) +<= 0.29999999999999999 + +c_u_SinkDSMDIWBlock_dsm_up_constraint(demand_dsm_5)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_5) +<= 0.29999999999999999 + +c_u_SinkDSMDIWBlock_dsm_do_constraint(demand_dsm_0)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_0) +<= 0.29999999999999999 + +c_u_SinkDSMDIWBlock_dsm_do_constraint(demand_dsm_1)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_1) +<= 0.29999999999999999 + +c_u_SinkDSMDIWBlock_dsm_do_constraint(demand_dsm_2)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_2) +<= 0.40000000000000002 + +c_u_SinkDSMDIWBlock_dsm_do_constraint(demand_dsm_3)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_3) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_3) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_3) +<= 0.29999999999999999 + +c_u_SinkDSMDIWBlock_dsm_do_constraint(demand_dsm_4)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_4) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_4) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_4) +<= 0.29999999999999999 + +c_u_SinkDSMDIWBlock_dsm_do_constraint(demand_dsm_5)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_5) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_5) +<= 0.29999999999999999 + +c_u_SinkDSMDIWBlock_C2_constraint(demand_dsm_0)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_0) +<= 0.5 + +c_u_SinkDSMDIWBlock_C2_constraint(demand_dsm_1)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_1) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_1) +<= 0.40000000000000002 + +c_u_SinkDSMDIWBlock_C2_constraint(demand_dsm_2)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_2) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_2) +<= 0.5 + +c_u_SinkDSMDIWBlock_C2_constraint(demand_dsm_3)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_3) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_3) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_3) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_3) +<= 0.29999999999999999 + +c_u_SinkDSMDIWBlock_C2_constraint(demand_dsm_4)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_4) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_4) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_4) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_4) +<= 0.29999999999999999 + +c_u_SinkDSMDIWBlock_C2_constraint(demand_dsm_5)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_5) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_5) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_5) +<= 0.29999999999999999 + +c_u_SinkDSMDIWBlock_recovery_constraint(demand_dsm_0)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_0) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_1) +<= 0.5 + +c_u_SinkDSMDIWBlock_recovery_constraint(demand_dsm_1)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_1) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_2) +<= 0.40000000000000002 + +c_u_SinkDSMDIWBlock_recovery_constraint(demand_dsm_2)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_2) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_3) +<= 0.5 + +c_u_SinkDSMDIWBlock_recovery_constraint(demand_dsm_3)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_3) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_4) +<= 0.29999999999999999 + +c_u_SinkDSMDIWBlock_recovery_constraint(demand_dsm_4)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_4) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_5) +<= 0.29999999999999999 + +c_u_SinkDSMDIWBlock_recovery_constraint(demand_dsm_5)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_5) +<= 0.29999999999999999 + +c_u_SinkDSMDIWBlock_shed_limit_constraint(demand_dsm_0)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) +<= 0.59999999999999998 + +c_u_SinkDSMDIWBlock_shed_limit_constraint(demand_dsm_1)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) +<= 0.59999999999999998 + +c_u_SinkDSMDIWBlock_shed_limit_constraint(demand_dsm_2)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_3) +<= 0.80000000000000004 + +c_u_SinkDSMDIWBlock_shed_limit_constraint(demand_dsm_3)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_4) +<= 0.59999999999999998 + +c_u_SinkDSMDIWBlock_shed_limit_constraint(demand_dsm_4)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_5) +<= 0.59999999999999998 + +c_u_SinkDSMDIWBlock_shed_limit_constraint(demand_dsm_5)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_5) +<= 0.59999999999999998 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(bus_elec_demand_dsm_0_0) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_1) <= +inf + 0 <= flow(bus_elec_demand_dsm_1_2) <= +inf + 0 <= flow(bus_elec_demand_dsm_1_3) <= +inf + 0 <= flow(bus_elec_demand_dsm_2_4) <= +inf + 0 <= flow(bus_elec_demand_dsm_2_5) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_3) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_4) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_5) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_3) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_4) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_5) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_3) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_4) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_5) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_3) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_4) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_5) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_3) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_4) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_5) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_3) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_4) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_5) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shed(demand_dsm_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shed(demand_dsm_3) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shed(demand_dsm_4) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shed(demand_dsm_5) <= +inf + 0 <= SinkDSMDIWBlock_dsm_up(demand_dsm_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_up(demand_dsm_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_up(demand_dsm_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_up(demand_dsm_3) <= +inf + 0 <= SinkDSMDIWBlock_dsm_up(demand_dsm_4) <= +inf + 0 <= SinkDSMDIWBlock_dsm_up(demand_dsm_5) <= +inf +end diff --git a/tests/lp_files/dsm_module_DIW_invest_multi_period.lp b/tests/lp_files/dsm_module_DIW_invest_multi_period.lp new file mode 100644 index 000000000..421c16fb6 --- /dev/null +++ b/tests/lp_files/dsm_module_DIW_invest_multi_period.lp @@ -0,0 +1,495 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++100 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_0) ++100 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_1) ++98.039215686274503 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_2) ++98.039215686274503 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_3) ++96.116878123798529 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_4) ++96.116878123798529 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_1) ++0.98039215686274506 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_2) ++0.98039215686274506 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_3) ++0.96116878123798533 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_4) ++0.96116878123798533 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_5) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_1) ++0.98039215686274506 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_2) ++0.98039215686274506 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_3) ++0.96116878123798533 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_4) ++0.96116878123798533 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_5) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_1) ++0.98039215686274506 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_2) ++0.98039215686274506 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_3) ++0.96116878123798533 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_4) ++0.96116878123798533 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_5) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_1) ++0.98039215686274506 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_2) ++0.98039215686274506 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_3) ++0.96116878123798533 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_4) ++0.96116878123798533 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_5) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_1) ++0.98039215686274506 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_2) ++0.98039215686274506 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_3) ++0.96116878123798533 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_4) ++0.96116878123798533 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_5) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_1) ++0.98039215686274506 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_2) ++0.98039215686274506 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_3) ++0.96116878123798533 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_4) ++0.96116878123798533 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_5) ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_0) ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_1) ++0.98039215686274506 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_2) ++0.98039215686274506 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_3) ++0.96116878123798533 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_4) ++0.96116878123798533 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_5) ++455.88267648036174 SinkDSMDIWInvestmentBlock_invest(demand_dsm_0) ++440.5314736691401 SinkDSMDIWInvestmentBlock_invest(demand_dsm_1) ++425.73027329942335 SinkDSMDIWInvestmentBlock_invest(demand_dsm_2) ++15992.031251718836 ONE_VAR_CONSTANT + +s.t. + +c_e_BusBlock_balance(bus_elec_0_0)_: ++1 flow(bus_elec_demand_dsm_0_0) += 0 + +c_e_BusBlock_balance(bus_elec_0_1)_: ++1 flow(bus_elec_demand_dsm_0_1) += 0 + +c_e_BusBlock_balance(bus_elec_1_2)_: ++1 flow(bus_elec_demand_dsm_1_2) += 0 + +c_e_BusBlock_balance(bus_elec_1_3)_: ++1 flow(bus_elec_demand_dsm_1_3) += 0 + +c_e_BusBlock_balance(bus_elec_2_4)_: ++1 flow(bus_elec_demand_dsm_2_4) += 0 + +c_e_BusBlock_balance(bus_elec_2_5)_: ++1 flow(bus_elec_demand_dsm_2_5) += 0 + +c_e_SinkDSMDIWInvestmentBlock_total_dsm_rule(demand_dsm_0)_: +-1 SinkDSMDIWInvestmentBlock_invest(demand_dsm_0) ++1 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) += 50 + +c_e_SinkDSMDIWInvestmentBlock_total_dsm_rule(demand_dsm_1)_: +-1 SinkDSMDIWInvestmentBlock_invest(demand_dsm_1) ++1 SinkDSMDIWInvestmentBlock_old(demand_dsm_1) +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) ++1 SinkDSMDIWInvestmentBlock_total(demand_dsm_1) += 0 + +c_e_SinkDSMDIWInvestmentBlock_total_dsm_rule(demand_dsm_2)_: +-1 SinkDSMDIWInvestmentBlock_invest(demand_dsm_2) ++1 SinkDSMDIWInvestmentBlock_old(demand_dsm_2) +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_1) ++1 SinkDSMDIWInvestmentBlock_total(demand_dsm_2) += 0 + +c_e_SinkDSMDIWInvestmentBlock_old_dsm_rule_end(demand_dsm_0)_: ++1 SinkDSMDIWInvestmentBlock_old_end(demand_dsm_0) += 0 + +c_e_SinkDSMDIWInvestmentBlock_old_dsm_rule_end(demand_dsm_1)_: ++1 SinkDSMDIWInvestmentBlock_old_end(demand_dsm_1) += 0 + +c_e_SinkDSMDIWInvestmentBlock_old_dsm_rule_end(demand_dsm_2)_: ++1 SinkDSMDIWInvestmentBlock_old_end(demand_dsm_2) += 0 + +c_e_SinkDSMDIWInvestmentBlock_old_dsm_rule_exo(demand_dsm_0)_: ++1 SinkDSMDIWInvestmentBlock_old_exo(demand_dsm_0) += 0 + +c_e_SinkDSMDIWInvestmentBlock_old_dsm_rule_exo(demand_dsm_1)_: ++1 SinkDSMDIWInvestmentBlock_old_exo(demand_dsm_1) += 0 + +c_e_SinkDSMDIWInvestmentBlock_old_dsm_rule_exo(demand_dsm_2)_: ++1 SinkDSMDIWInvestmentBlock_old_exo(demand_dsm_2) += 0 + +c_e_SinkDSMDIWInvestmentBlock_old_dsm_rule(demand_dsm_0)_: ++1 SinkDSMDIWInvestmentBlock_old(demand_dsm_0) +-1 SinkDSMDIWInvestmentBlock_old_end(demand_dsm_0) +-1 SinkDSMDIWInvestmentBlock_old_exo(demand_dsm_0) += 0 + +c_e_SinkDSMDIWInvestmentBlock_old_dsm_rule(demand_dsm_1)_: ++1 SinkDSMDIWInvestmentBlock_old(demand_dsm_1) +-1 SinkDSMDIWInvestmentBlock_old_end(demand_dsm_1) +-1 SinkDSMDIWInvestmentBlock_old_exo(demand_dsm_1) += 0 + +c_e_SinkDSMDIWInvestmentBlock_old_dsm_rule(demand_dsm_2)_: ++1 SinkDSMDIWInvestmentBlock_old(demand_dsm_2) +-1 SinkDSMDIWInvestmentBlock_old_end(demand_dsm_2) +-1 SinkDSMDIWInvestmentBlock_old_exo(demand_dsm_2) += 0 + +c_e_SinkDSMDIWInvestmentBlock_input_output_relation(demand_dsm_0_0)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_0) +-1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_0) +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_0) += 0 + +c_e_SinkDSMDIWInvestmentBlock_input_output_relation(demand_dsm_0_1)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_1) +-1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_1) +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_1) += 0 + +c_e_SinkDSMDIWInvestmentBlock_input_output_relation(demand_dsm_1_2)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_2) +-1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_2) +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_1) ++1 flow(bus_elec_demand_dsm_1_2) += 0 + +c_e_SinkDSMDIWInvestmentBlock_input_output_relation(demand_dsm_1_3)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_3) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_3) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_3) +-1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_3) +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_1) ++1 flow(bus_elec_demand_dsm_1_3) += 0 + +c_e_SinkDSMDIWInvestmentBlock_input_output_relation(demand_dsm_2_4)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_4) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_4) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_4) +-1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_4) +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_2) ++1 flow(bus_elec_demand_dsm_2_4) += 0 + +c_e_SinkDSMDIWInvestmentBlock_input_output_relation(demand_dsm_2_5)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_5) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_5) +-1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_5) +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_2) ++1 flow(bus_elec_demand_dsm_2_5) += 0 + +c_e_SinkDSMDIWInvestmentBlock_dsm_updo_constraint(demand_dsm_0)_: +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_0) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_1) ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_0) += 0 + +c_e_SinkDSMDIWInvestmentBlock_dsm_updo_constraint(demand_dsm_1)_: +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_0) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_1) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_1) += 0 + +c_e_SinkDSMDIWInvestmentBlock_dsm_updo_constraint(demand_dsm_2)_: +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_1) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_2) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_3) ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_2) += 0 + +c_e_SinkDSMDIWInvestmentBlock_dsm_updo_constraint(demand_dsm_3)_: +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_2) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_3) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_4) ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_3) += 0 + +c_e_SinkDSMDIWInvestmentBlock_dsm_updo_constraint(demand_dsm_4)_: +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_3) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_4) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_5) ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_4) += 0 + +c_e_SinkDSMDIWInvestmentBlock_dsm_updo_constraint(demand_dsm_5)_: +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_4) +-1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_5) ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_5) += 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(demand_dsm_0_0)_: ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_0) +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(demand_dsm_0_1)_: ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_1) +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(demand_dsm_1_2)_: ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_2) +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(demand_dsm_1_3)_: ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_3) +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(demand_dsm_2_4)_: ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_4) +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_up_constraint(demand_dsm_2_5)_: ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_5) +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(demand_dsm_0_0)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_0) +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(demand_dsm_0_1)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_1) +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(demand_dsm_1_2)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_2) +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(demand_dsm_1_3)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_3) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_3) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_3) +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(demand_dsm_2_4)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_4) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_4) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_4) +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_dsm_do_constraint(demand_dsm_2_5)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_5) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_5) +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_C2_constraint(demand_dsm_0_0)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_0) +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_C2_constraint(demand_dsm_0_1)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_1) ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_1) +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_C2_constraint(demand_dsm_1_2)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_2) ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_2) +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_C2_constraint(demand_dsm_1_3)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_3) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_3) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_3) ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_3) +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_C2_constraint(demand_dsm_2_4)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_4) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_4) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_4) ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_4) +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_C2_constraint(demand_dsm_2_5)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_5) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_5) ++1 SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_5) +-0.5 SinkDSMDIWInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_shed_limit_constraint(demand_dsm_0_0)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_1) +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_shed_limit_constraint(demand_dsm_0_1)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_2) +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_shed_limit_constraint(demand_dsm_1_2)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_3) +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_shed_limit_constraint(demand_dsm_1_3)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_4) +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_shed_limit_constraint(demand_dsm_2_4)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_5) +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_shed_limit_constraint(demand_dsm_2_5)_: ++1 SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_5) +-1 SinkDSMDIWInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMDIWInvestmentBlock_overall_dsm_maximum(demand_dsm_0)_: ++1 SinkDSMDIWInvestmentBlock_total(demand_dsm_0) +<= 1000 + +c_u_SinkDSMDIWInvestmentBlock_overall_dsm_maximum(demand_dsm_1)_: ++1 SinkDSMDIWInvestmentBlock_total(demand_dsm_1) +<= 1000 + +c_u_SinkDSMDIWInvestmentBlock_overall_dsm_maximum(demand_dsm_2)_: ++1 SinkDSMDIWInvestmentBlock_total(demand_dsm_2) +<= 1000 + +c_l_SinkDSMDIWInvestmentBlock_overall_minimum(demand_dsm)_: ++1 SinkDSMDIWInvestmentBlock_total(demand_dsm_2) +>= 5 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(bus_elec_demand_dsm_0_0) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_1) <= +inf + 0 <= flow(bus_elec_demand_dsm_1_2) <= +inf + 0 <= flow(bus_elec_demand_dsm_1_3) <= +inf + 0 <= flow(bus_elec_demand_dsm_2_4) <= +inf + 0 <= flow(bus_elec_demand_dsm_2_5) <= +inf + 33 <= SinkDSMDIWInvestmentBlock_invest(demand_dsm_0) <= 100 + 33 <= SinkDSMDIWInvestmentBlock_invest(demand_dsm_1) <= 100 + 33 <= SinkDSMDIWInvestmentBlock_invest(demand_dsm_2) <= 100 + 0 <= SinkDSMDIWInvestmentBlock_total(demand_dsm_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_total(demand_dsm_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_total(demand_dsm_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_old(demand_dsm_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_old(demand_dsm_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_old(demand_dsm_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_old_end(demand_dsm_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_old_end(demand_dsm_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_old_end(demand_dsm_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_old_exo(demand_dsm_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_old_exo(demand_dsm_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_old_exo(demand_dsm_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_3) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_4) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_0_5) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_3) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_4) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_1_5) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_3) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_4) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_2_5) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_3) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_4) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_3_5) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_3) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_4) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_4_5) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_3) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_4) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shift(demand_dsm_5_5) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_3) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_4) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_do_shed(demand_dsm_5) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_0) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_1) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_2) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_3) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_4) <= +inf + 0 <= SinkDSMDIWInvestmentBlock_dsm_up(demand_dsm_5) <= +inf +end diff --git a/tests/lp_files/dsm_module_DIW_multi_period.lp b/tests/lp_files/dsm_module_DIW_multi_period.lp new file mode 100644 index 000000000..0f987d8d1 --- /dev/null +++ b/tests/lp_files/dsm_module_DIW_multi_period.lp @@ -0,0 +1,352 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++2 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) ++2 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) ++1.9607843137254901 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_2) ++1.9607843137254901 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_3) ++1.9223375624759707 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_4) ++1.9223375624759707 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_5) ++2 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_0) ++2 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_1) ++1.9607843137254901 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_2) ++1.9607843137254901 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_3) ++1.9223375624759707 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_4) ++1.9223375624759707 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_5) ++2 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_0) ++2 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_1) ++1.9607843137254901 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_2) ++1.9607843137254901 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_3) ++1.9223375624759707 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_4) ++1.9223375624759707 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_5) ++2 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_0) ++2 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_1) ++1.9607843137254901 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_2) ++1.9607843137254901 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_3) ++1.9223375624759707 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_4) ++1.9223375624759707 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_5) ++2 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_0) ++2 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_1) ++1.9607843137254901 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_2) ++1.9607843137254901 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_3) ++1.9223375624759707 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_4) ++1.9223375624759707 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_5) ++2 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_0) ++2 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_1) ++1.9607843137254901 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_2) ++1.9607843137254901 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_3) ++1.9223375624759707 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_4) ++1.9223375624759707 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_5) + +s.t. + +c_e_BusBlock_balance(bus_elec_0_0)_: ++1 flow(bus_elec_demand_dsm_0_0) += 0 + +c_e_BusBlock_balance(bus_elec_0_1)_: ++1 flow(bus_elec_demand_dsm_0_1) += 0 + +c_e_BusBlock_balance(bus_elec_1_2)_: ++1 flow(bus_elec_demand_dsm_1_2) += 0 + +c_e_BusBlock_balance(bus_elec_1_3)_: ++1 flow(bus_elec_demand_dsm_1_3) += 0 + +c_e_BusBlock_balance(bus_elec_2_4)_: ++1 flow(bus_elec_demand_dsm_2_4) += 0 + +c_e_BusBlock_balance(bus_elec_2_5)_: ++1 flow(bus_elec_demand_dsm_2_5) += 0 + +c_e_SinkDSMDIWBlock_shift_shed_vars(demand_dsm_0)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_0) += 0 + +c_e_SinkDSMDIWBlock_shift_shed_vars(demand_dsm_1)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) += 0 + +c_e_SinkDSMDIWBlock_shift_shed_vars(demand_dsm_2)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) += 0 + +c_e_SinkDSMDIWBlock_shift_shed_vars(demand_dsm_3)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_3) += 0 + +c_e_SinkDSMDIWBlock_shift_shed_vars(demand_dsm_4)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_4) += 0 + +c_e_SinkDSMDIWBlock_shift_shed_vars(demand_dsm_5)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_5) += 0 + +c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_0_0)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_0) +-1 SinkDSMDIWBlock_dsm_up(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_0) += 1 + +c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_0_1)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_1) +-1 SinkDSMDIWBlock_dsm_up(demand_dsm_1) ++1 flow(bus_elec_demand_dsm_0_1) += 1 + +c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_1_2)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_2) +-1 SinkDSMDIWBlock_dsm_up(demand_dsm_2) ++1 flow(bus_elec_demand_dsm_1_2) += 1 + +c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_1_3)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_3) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_3) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_3) +-1 SinkDSMDIWBlock_dsm_up(demand_dsm_3) ++1 flow(bus_elec_demand_dsm_1_3) += 1 + +c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_2_4)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_4) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_4) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_4) +-1 SinkDSMDIWBlock_dsm_up(demand_dsm_4) ++1 flow(bus_elec_demand_dsm_2_4) += 1 + +c_e_SinkDSMDIWBlock_input_output_relation(demand_dsm_2_5)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_5) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_5) +-1 SinkDSMDIWBlock_dsm_up(demand_dsm_5) ++1 flow(bus_elec_demand_dsm_2_5) += 1 + +c_e_SinkDSMDIWBlock_dsm_updo_constraint(demand_dsm_0)_: +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_0) += 0 + +c_e_SinkDSMDIWBlock_dsm_updo_constraint(demand_dsm_1)_: +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_0) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_1) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_1) += 0 + +c_e_SinkDSMDIWBlock_dsm_updo_constraint(demand_dsm_2)_: +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_1) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_2) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_3) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_2) += 0 + +c_e_SinkDSMDIWBlock_dsm_updo_constraint(demand_dsm_3)_: +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_2) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_3) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_4) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_3) += 0 + +c_e_SinkDSMDIWBlock_dsm_updo_constraint(demand_dsm_4)_: +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_3) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_4) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_5) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_4) += 0 + +c_e_SinkDSMDIWBlock_dsm_updo_constraint(demand_dsm_5)_: +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_4) +-1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_5) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_5) += 0 + +c_u_SinkDSMDIWBlock_dsm_up_constraint(demand_dsm_0)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_0) +<= 0.5 + +c_u_SinkDSMDIWBlock_dsm_up_constraint(demand_dsm_1)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_1) +<= 0.5 + +c_u_SinkDSMDIWBlock_dsm_up_constraint(demand_dsm_2)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_2) +<= 0.5 + +c_u_SinkDSMDIWBlock_dsm_up_constraint(demand_dsm_3)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_3) +<= 0.5 + +c_u_SinkDSMDIWBlock_dsm_up_constraint(demand_dsm_4)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_4) +<= 0.5 + +c_u_SinkDSMDIWBlock_dsm_up_constraint(demand_dsm_5)_: ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_5) +<= 0.5 + +c_u_SinkDSMDIWBlock_dsm_do_constraint(demand_dsm_0)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_0) +<= 0.5 + +c_u_SinkDSMDIWBlock_dsm_do_constraint(demand_dsm_1)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_1) +<= 0.5 + +c_u_SinkDSMDIWBlock_dsm_do_constraint(demand_dsm_2)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_2) +<= 0.5 + +c_u_SinkDSMDIWBlock_dsm_do_constraint(demand_dsm_3)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_3) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_3) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_3) +<= 0.5 + +c_u_SinkDSMDIWBlock_dsm_do_constraint(demand_dsm_4)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_4) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_4) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_4) +<= 0.5 + +c_u_SinkDSMDIWBlock_dsm_do_constraint(demand_dsm_5)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_5) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_5) +<= 0.5 + +c_u_SinkDSMDIWBlock_C2_constraint(demand_dsm_0)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_0) +<= 0.5 + +c_u_SinkDSMDIWBlock_C2_constraint(demand_dsm_1)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_1) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_1) +<= 0.5 + +c_u_SinkDSMDIWBlock_C2_constraint(demand_dsm_2)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_2) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_2) +<= 0.5 + +c_u_SinkDSMDIWBlock_C2_constraint(demand_dsm_3)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_3) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_3) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_3) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_3) +<= 0.5 + +c_u_SinkDSMDIWBlock_C2_constraint(demand_dsm_4)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_4) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_4) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_4) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_4) +<= 0.5 + +c_u_SinkDSMDIWBlock_C2_constraint(demand_dsm_5)_: ++1 SinkDSMDIWBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_5) ++1 SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_5) ++1 SinkDSMDIWBlock_dsm_up(demand_dsm_5) +<= 0.5 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(bus_elec_demand_dsm_0_0) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_1) <= +inf + 0 <= flow(bus_elec_demand_dsm_1_2) <= +inf + 0 <= flow(bus_elec_demand_dsm_1_3) <= +inf + 0 <= flow(bus_elec_demand_dsm_2_4) <= +inf + 0 <= flow(bus_elec_demand_dsm_2_5) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_3) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_4) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_0_5) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_3) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_4) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_1_5) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_3) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_4) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_2_5) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_3) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_4) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_3_5) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_3) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_4) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_4_5) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_3) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_4) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shift(demand_dsm_5_5) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shed(demand_dsm_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shed(demand_dsm_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shed(demand_dsm_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shed(demand_dsm_3) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shed(demand_dsm_4) <= +inf + 0 <= SinkDSMDIWBlock_dsm_do_shed(demand_dsm_5) <= +inf + 0 <= SinkDSMDIWBlock_dsm_up(demand_dsm_0) <= +inf + 0 <= SinkDSMDIWBlock_dsm_up(demand_dsm_1) <= +inf + 0 <= SinkDSMDIWBlock_dsm_up(demand_dsm_2) <= +inf + 0 <= SinkDSMDIWBlock_dsm_up(demand_dsm_3) <= +inf + 0 <= SinkDSMDIWBlock_dsm_up(demand_dsm_4) <= +inf + 0 <= SinkDSMDIWBlock_dsm_up(demand_dsm_5) <= +inf +end diff --git a/tests/lp_files/dsm_module_DLR_extended_multi_period.lp b/tests/lp_files/dsm_module_DLR_extended_multi_period.lp new file mode 100644 index 000000000..4afd5f9c9 --- /dev/null +++ b/tests/lp_files/dsm_module_DLR_extended_multi_period.lp @@ -0,0 +1,709 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_0) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_1) ++0.98039215686274506 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_2) ++0.98039215686274506 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_3) ++0.96116878123798533 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_4) ++0.96116878123798533 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_5) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_0) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_1) ++0.98039215686274506 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_2) ++0.98039215686274506 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_3) ++0.96116878123798533 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_4) ++0.96116878123798533 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_5) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) ++0.98039215686274506 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) ++0.98039215686274506 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_3) ++0.96116878123798533 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_4) ++0.96116878123798533 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_1) ++0.98039215686274506 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_2) ++0.98039215686274506 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_3) ++0.96116878123798533 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_4) ++0.96116878123798533 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_5) ++100 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_0) ++100 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_1) ++98.039215686274503 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_2) ++98.039215686274503 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_3) ++96.116878123798529 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_4) ++96.116878123798529 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) ++0.98039215686274506 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) ++0.98039215686274506 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_3) ++0.96116878123798533 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_4) ++0.96116878123798533 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_5) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) ++0.98039215686274506 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) ++0.98039215686274506 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_3) ++0.96116878123798533 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_4) ++0.96116878123798533 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_5) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) ++0.98039215686274506 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) ++0.98039215686274506 SinkDSMDLRBlock_dsm_up(demand_dsm_1_3) ++0.96116878123798533 SinkDSMDLRBlock_dsm_up(demand_dsm_1_4) ++0.96116878123798533 SinkDSMDLRBlock_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) ++0.98039215686274506 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) ++0.98039215686274506 SinkDSMDLRBlock_dsm_up(demand_dsm_2_3) ++0.96116878123798533 SinkDSMDLRBlock_dsm_up(demand_dsm_2_4) ++0.96116878123798533 SinkDSMDLRBlock_dsm_up(demand_dsm_2_5) + +s.t. + +c_e_BusBlock_balance(bus_elec_0_0)_: ++1 flow(bus_elec_demand_dsm_0_0) += 0 + +c_e_BusBlock_balance(bus_elec_0_1)_: ++1 flow(bus_elec_demand_dsm_0_1) += 0 + +c_e_BusBlock_balance(bus_elec_1_2)_: ++1 flow(bus_elec_demand_dsm_1_2) += 0 + +c_e_BusBlock_balance(bus_elec_1_3)_: ++1 flow(bus_elec_demand_dsm_1_3) += 0 + +c_e_BusBlock_balance(bus_elec_2_4)_: ++1 flow(bus_elec_demand_dsm_2_4) += 0 + +c_e_BusBlock_balance(bus_elec_2_5)_: ++1 flow(bus_elec_demand_dsm_2_5) += 0 + +c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_0_0)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_0) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_0) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) ++1 flow(bus_elec_demand_dsm_0_0) += 1 + +c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_0_1)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_1) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_1) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) ++1 flow(bus_elec_demand_dsm_0_1) += 0.90000000000000002 + +c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_1_2)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_2) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_2) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) ++1 flow(bus_elec_demand_dsm_1_2) += 0.80000000000000004 + +c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_1_3)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_3) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_3) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_3) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_3) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_3) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_3) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_3) ++1 flow(bus_elec_demand_dsm_1_3) += 0.69999999999999996 + +c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_2_4)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_4) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_4) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_4) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_4) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_4) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_4) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_4) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_4) ++1 flow(bus_elec_demand_dsm_2_4) += 0.69999999999999996 + +c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_2_5)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_5) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_5) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_5) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_5) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_5) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_5) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_5) ++1 flow(bus_elec_demand_dsm_2_5) += 0.69999999999999996 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_1_0)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_1_1)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_1) +-1.0101010101010102 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_1_2)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_2) +-1.0101010101010102 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_1_3)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_3) +-1.0101010101010102 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_1_4)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_4) +-1.0101010101010102 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_3) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_1_5)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_5) +-1.0101010101010102 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_4) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_2_0)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_2_2)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_2) +-1.0101010101010102 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_2_3)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_3) +-1.0101010101010102 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_2_4)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_4) +-1.0101010101010102 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_2_5)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_5) +-1.0101010101010102 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_3) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_1_0)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_1_1)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) +-0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_1_2)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) +-0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_1_3)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_3) +-0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_1_4)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_4) +-0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_1_3) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_1_5)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_5) +-0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_1_4) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_2_0)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_2_2)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_2) +-0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_2_3)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_3) +-0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_2_4)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_4) +-0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_2_5)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_5) +-0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_2_3) += 0 + +c_u_SinkDSMDLRBlock_availability_red(demand_dsm_0)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) +<= 0.29999999999999999 + +c_u_SinkDSMDLRBlock_availability_red(demand_dsm_1)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) +<= 0.29999999999999999 + +c_u_SinkDSMDLRBlock_availability_red(demand_dsm_2)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) +<= 0.40000000000000002 + +c_u_SinkDSMDLRBlock_availability_red(demand_dsm_3)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_3) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_3) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_3) +<= 0.29999999999999999 + +c_u_SinkDSMDLRBlock_availability_red(demand_dsm_4)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_4) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_4) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_4) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_4) +<= 0.29999999999999999 + +c_u_SinkDSMDLRBlock_availability_red(demand_dsm_5)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_5) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_5) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_5) +<= 0.29999999999999999 + +c_u_SinkDSMDLRBlock_availability_inc(demand_dsm_0)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_0) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) +<= 0.5 + +c_u_SinkDSMDLRBlock_availability_inc(demand_dsm_1)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_1) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) +<= 0.40000000000000002 + +c_u_SinkDSMDLRBlock_availability_inc(demand_dsm_2)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_2) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) +<= 0.5 + +c_u_SinkDSMDLRBlock_availability_inc(demand_dsm_3)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_3) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_3) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_3) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_3) +<= 0.29999999999999999 + +c_u_SinkDSMDLRBlock_availability_inc(demand_dsm_4)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_4) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_4) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_4) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_4) +<= 0.29999999999999999 + +c_u_SinkDSMDLRBlock_availability_inc(demand_dsm_5)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_5) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_5) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_5) +<= 0.29999999999999999 + +c_e_SinkDSMDLRBlock_dr_storage_red(demand_dsm_0)_: ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_0) +-1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) +-1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_red(demand_dsm_1)_: +-0.98999999999999999 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_1) +-0.98999999999999999 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_0) +-1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_red(demand_dsm_2)_: +-0.98999999999999999 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_2) +-0.98999999999999999 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_1) +-1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_red(demand_dsm_3)_: +-0.98999999999999999 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_3) +-0.98999999999999999 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_3) ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_2) +-1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_3) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_red(demand_dsm_4)_: +-0.98999999999999999 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_4) +-0.98999999999999999 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_4) ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_3) +-1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_4) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_4) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_4) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_red(demand_dsm_5)_: +-0.98999999999999999 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_5) +-0.98999999999999999 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_5) ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_4) +-1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_5) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_5) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_5) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_inc(demand_dsm_0)_: +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_0) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_inc(demand_dsm_1)_: +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_1) ++0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) ++0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_0) +-1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_1) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_inc(demand_dsm_2)_: +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_2) ++0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) ++0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_1) +-1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_2) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_inc(demand_dsm_3)_: +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_3) +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_3) ++0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_1_3) ++0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_2_3) ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_2) +-1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_3) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_inc(demand_dsm_4)_: +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_4) +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_4) ++0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_1_4) ++0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_2_4) ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_3) +-1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_4) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_inc(demand_dsm_5)_: +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_5) +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_5) ++0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_1_5) ++0.98999999999999999 SinkDSMDLRBlock_dsm_up(demand_dsm_2_5) ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_4) +-1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_5) += 0 + +c_u_SinkDSMDLRBlock_dr_storage_limit_red(demand_dsm_0)_: ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_0) +<= 0.31666666666666671 + +c_u_SinkDSMDLRBlock_dr_storage_limit_red(demand_dsm_1)_: ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_1) +<= 0.31666666666666671 + +c_u_SinkDSMDLRBlock_dr_storage_limit_red(demand_dsm_2)_: ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_2) +<= 0.31666666666666671 + +c_u_SinkDSMDLRBlock_dr_storage_limit_red(demand_dsm_3)_: ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_3) +<= 0.31666666666666671 + +c_u_SinkDSMDLRBlock_dr_storage_limit_red(demand_dsm_4)_: ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_4) +<= 0.31666666666666671 + +c_u_SinkDSMDLRBlock_dr_storage_limit_red(demand_dsm_5)_: ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_5) +<= 0.31666666666666671 + +c_u_SinkDSMDLRBlock_dr_storage_limit_inc(demand_dsm_0)_: ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_0) +<= 0.3833333333333333 + +c_u_SinkDSMDLRBlock_dr_storage_limit_inc(demand_dsm_1)_: ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_1) +<= 0.3833333333333333 + +c_u_SinkDSMDLRBlock_dr_storage_limit_inc(demand_dsm_2)_: ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_2) +<= 0.3833333333333333 + +c_u_SinkDSMDLRBlock_dr_storage_limit_inc(demand_dsm_3)_: ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_3) +<= 0.3833333333333333 + +c_u_SinkDSMDLRBlock_dr_storage_limit_inc(demand_dsm_4)_: ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_4) +<= 0.3833333333333333 + +c_u_SinkDSMDLRBlock_dr_storage_limit_inc(demand_dsm_5)_: ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_5) +<= 0.3833333333333333 + +c_u_SinkDSMDLRBlock_dr_yearly_limit_shed(demand_dsm_0)_: ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_1) +<= 31.666666666666671 + +c_u_SinkDSMDLRBlock_dr_yearly_limit_shed(demand_dsm_1)_: ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_3) +<= 31.666666666666671 + +c_u_SinkDSMDLRBlock_dr_yearly_limit_shed(demand_dsm_2)_: ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_5) +<= 31.666666666666671 + +c_u_SinkDSMDLRBlock_dr_yearly_limit_red(demand_dsm_0)_: ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) +<= 31.666666666666671 + +c_u_SinkDSMDLRBlock_dr_yearly_limit_red(demand_dsm_1)_: ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_3) +<= 31.666666666666671 + +c_u_SinkDSMDLRBlock_dr_yearly_limit_red(demand_dsm_2)_: ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_4) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_5) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_4) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_5) +<= 31.666666666666671 + +c_u_SinkDSMDLRBlock_dr_yearly_limit_inc(demand_dsm_0)_: ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) +<= 38.333333333333329 + +c_u_SinkDSMDLRBlock_dr_yearly_limit_inc(demand_dsm_1)_: ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_3) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_3) +<= 38.333333333333329 + +c_u_SinkDSMDLRBlock_dr_yearly_limit_inc(demand_dsm_2)_: ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_4) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_4) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_5) +<= 38.333333333333329 + +c_u_SinkDSMDLRBlock_dr_daily_limit_red(demand_dsm_3)_: ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_3) +<= 0.31666666666666671 + +c_u_SinkDSMDLRBlock_dr_daily_limit_red(demand_dsm_4)_: ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_4) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_4) +<= 0.31666666666666671 + +c_u_SinkDSMDLRBlock_dr_daily_limit_red(demand_dsm_5)_: ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_4) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_5) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_4) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_5) +<= 0.31666666666666671 + +c_u_SinkDSMDLRBlock_dr_daily_limit_inc(demand_dsm_3)_: ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_3) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_3) +<= 0.3833333333333333 + +c_u_SinkDSMDLRBlock_dr_daily_limit_inc(demand_dsm_4)_: ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_3) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_4) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_3) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_4) +<= 0.3833333333333333 + +c_u_SinkDSMDLRBlock_dr_daily_limit_inc(demand_dsm_5)_: ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_3) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_4) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_3) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_4) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_5) +<= 0.3833333333333333 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(bus_elec_demand_dsm_0_0) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_1) <= +inf + 0 <= flow(bus_elec_demand_dsm_1_2) <= +inf + 0 <= flow(bus_elec_demand_dsm_1_3) <= +inf + 0 <= flow(bus_elec_demand_dsm_2_4) <= +inf + 0 <= flow(bus_elec_demand_dsm_2_5) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_3) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_4) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_5) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_3) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_4) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_5) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shed(demand_dsm_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shed(demand_dsm_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shed(demand_dsm_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shed(demand_dsm_3) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shed(demand_dsm_4) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shed(demand_dsm_5) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_1_3) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_1_4) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_1_5) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_2_3) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_2_4) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_2_5) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_3) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_4) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_5) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_3) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_4) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_5) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_3) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_4) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_5) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_3) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_4) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_5) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_level(demand_dsm_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_level(demand_dsm_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_level(demand_dsm_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_level(demand_dsm_3) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_level(demand_dsm_4) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_level(demand_dsm_5) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up_level(demand_dsm_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up_level(demand_dsm_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up_level(demand_dsm_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up_level(demand_dsm_3) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up_level(demand_dsm_4) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up_level(demand_dsm_5) <= +inf +end diff --git a/tests/lp_files/dsm_module_DLR_invest_multi_period.lp b/tests/lp_files/dsm_module_DLR_invest_multi_period.lp new file mode 100644 index 000000000..e1a4a6c30 --- /dev/null +++ b/tests/lp_files/dsm_module_DLR_invest_multi_period.lp @@ -0,0 +1,844 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_0) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_1) ++0.98039215686274506 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_2) ++0.98039215686274506 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_3) ++0.96116878123798533 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_4) ++0.96116878123798533 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_5) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_0) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_1) ++0.98039215686274506 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_2) ++0.98039215686274506 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_3) ++0.96116878123798533 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_4) ++0.96116878123798533 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_5) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_1) ++0.98039215686274506 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_2) ++0.98039215686274506 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_3) ++0.96116878123798533 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_4) ++0.96116878123798533 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_1) ++0.98039215686274506 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_2) ++0.98039215686274506 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_3) ++0.96116878123798533 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_4) ++0.96116878123798533 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_5) ++100 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_0) ++100 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_1) ++98.039215686274503 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_2) ++98.039215686274503 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_3) ++96.116878123798529 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_4) ++96.116878123798529 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_1) ++0.98039215686274506 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_2) ++0.98039215686274506 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_3) ++0.96116878123798533 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_4) ++0.96116878123798533 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_1) ++0.98039215686274506 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_2) ++0.98039215686274506 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_3) ++0.96116878123798533 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_4) ++0.96116878123798533 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_5) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_1) ++0.98039215686274506 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_2) ++0.98039215686274506 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_3) ++0.96116878123798533 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_4) ++0.96116878123798533 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_1) ++0.98039215686274506 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_2) ++0.98039215686274506 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_3) ++0.96116878123798533 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_4) ++0.96116878123798533 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_5) ++455.88267648036174 SinkDSMDLRInvestmentBlock_invest(demand_dsm_0) ++440.5314736691401 SinkDSMDLRInvestmentBlock_invest(demand_dsm_1) ++425.73027329942335 SinkDSMDLRInvestmentBlock_invest(demand_dsm_2) ++15992.031251718836 ONE_VAR_CONSTANT + +s.t. + +c_e_BusBlock_balance(bus_elec_0_0)_: ++1 flow(bus_elec_demand_dsm_0_0) += 0 + +c_e_BusBlock_balance(bus_elec_0_1)_: ++1 flow(bus_elec_demand_dsm_0_1) += 0 + +c_e_BusBlock_balance(bus_elec_1_2)_: ++1 flow(bus_elec_demand_dsm_1_2) += 0 + +c_e_BusBlock_balance(bus_elec_1_3)_: ++1 flow(bus_elec_demand_dsm_1_3) += 0 + +c_e_BusBlock_balance(bus_elec_2_4)_: ++1 flow(bus_elec_demand_dsm_2_4) += 0 + +c_e_BusBlock_balance(bus_elec_2_5)_: ++1 flow(bus_elec_demand_dsm_2_5) += 0 + +c_e_SinkDSMDLRInvestmentBlock_total_dsm_rule(demand_dsm_0)_: +-1 SinkDSMDLRInvestmentBlock_invest(demand_dsm_0) ++1 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) += 50 + +c_e_SinkDSMDLRInvestmentBlock_total_dsm_rule(demand_dsm_1)_: +-1 SinkDSMDLRInvestmentBlock_invest(demand_dsm_1) ++1 SinkDSMDLRInvestmentBlock_old(demand_dsm_1) +-1 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) ++1 SinkDSMDLRInvestmentBlock_total(demand_dsm_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_total_dsm_rule(demand_dsm_2)_: +-1 SinkDSMDLRInvestmentBlock_invest(demand_dsm_2) ++1 SinkDSMDLRInvestmentBlock_old(demand_dsm_2) +-1 SinkDSMDLRInvestmentBlock_total(demand_dsm_1) ++1 SinkDSMDLRInvestmentBlock_total(demand_dsm_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_old_dsm_rule_end(demand_dsm_0)_: ++1 SinkDSMDLRInvestmentBlock_old_end(demand_dsm_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_old_dsm_rule_end(demand_dsm_1)_: ++1 SinkDSMDLRInvestmentBlock_old_end(demand_dsm_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_old_dsm_rule_end(demand_dsm_2)_: ++1 SinkDSMDLRInvestmentBlock_old_end(demand_dsm_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_old_dsm_rule_exo(demand_dsm_0)_: ++1 SinkDSMDLRInvestmentBlock_old_exo(demand_dsm_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_old_dsm_rule_exo(demand_dsm_1)_: ++1 SinkDSMDLRInvestmentBlock_old_exo(demand_dsm_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_old_dsm_rule_exo(demand_dsm_2)_: ++1 SinkDSMDLRInvestmentBlock_old_exo(demand_dsm_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_old_dsm_rule(demand_dsm_0)_: ++1 SinkDSMDLRInvestmentBlock_old(demand_dsm_0) +-1 SinkDSMDLRInvestmentBlock_old_end(demand_dsm_0) +-1 SinkDSMDLRInvestmentBlock_old_exo(demand_dsm_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_old_dsm_rule(demand_dsm_1)_: ++1 SinkDSMDLRInvestmentBlock_old(demand_dsm_1) +-1 SinkDSMDLRInvestmentBlock_old_end(demand_dsm_1) +-1 SinkDSMDLRInvestmentBlock_old_exo(demand_dsm_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_old_dsm_rule(demand_dsm_2)_: ++1 SinkDSMDLRInvestmentBlock_old(demand_dsm_2) +-1 SinkDSMDLRInvestmentBlock_old_end(demand_dsm_2) +-1 SinkDSMDLRInvestmentBlock_old_exo(demand_dsm_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_input_output_relation(demand_dsm_0_0)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_0) +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_0) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_0) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_0) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_0) +-1 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_input_output_relation(demand_dsm_0_1)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_1) +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_1) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_1) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_1) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_1) +-1 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_input_output_relation(demand_dsm_1_2)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_2) +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_2) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_2) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_2) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_2) +-1 SinkDSMDLRInvestmentBlock_total(demand_dsm_1) ++1 flow(bus_elec_demand_dsm_1_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_input_output_relation(demand_dsm_1_3)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_3) +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_3) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_3) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_3) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_3) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_3) +-1 SinkDSMDLRInvestmentBlock_total(demand_dsm_1) ++1 flow(bus_elec_demand_dsm_1_3) += 0 + +c_e_SinkDSMDLRInvestmentBlock_input_output_relation(demand_dsm_2_4)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_4) +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_4) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_4) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_4) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_4) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_4) +-1 SinkDSMDLRInvestmentBlock_total(demand_dsm_2) ++1 flow(bus_elec_demand_dsm_2_4) += 0 + +c_e_SinkDSMDLRInvestmentBlock_input_output_relation(demand_dsm_2_5)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_5) +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_5) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_5) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_5) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_5) +-1 SinkDSMDLRInvestmentBlock_total(demand_dsm_2) ++1 flow(bus_elec_demand_dsm_2_5) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(demand_dsm_1_0)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(demand_dsm_1_1)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_1) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(demand_dsm_1_2)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_2) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(demand_dsm_1_3)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_3) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(demand_dsm_1_4)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_4) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_3) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(demand_dsm_1_5)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_5) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_4) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(demand_dsm_2_0)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(demand_dsm_2_2)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_2) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(demand_dsm_2_3)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_3) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(demand_dsm_2_4)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_4) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_red(demand_dsm_2_5)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_5) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_3) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(demand_dsm_1_0)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(demand_dsm_1_1)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_1) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(demand_dsm_1_2)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_2) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(demand_dsm_1_3)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_3) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(demand_dsm_1_4)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_4) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_3) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(demand_dsm_1_5)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_5) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_4) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(demand_dsm_2_0)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(demand_dsm_2_2)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_2) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(demand_dsm_2_3)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_3) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(demand_dsm_2_4)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_4) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_capacity_balance_inc(demand_dsm_2_5)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_5) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_3) += 0 + +c_e_SinkDSMDLRInvestmentBlock_no_comp_red(demand_dsm_1_5)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_5) += 0 + +c_e_SinkDSMDLRInvestmentBlock_no_comp_red(demand_dsm_2_4)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_4) += 0 + +c_e_SinkDSMDLRInvestmentBlock_no_comp_red(demand_dsm_2_5)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_5) += 0 + +c_e_SinkDSMDLRInvestmentBlock_no_comp_inc(demand_dsm_1_5)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_5) += 0 + +c_e_SinkDSMDLRInvestmentBlock_no_comp_inc(demand_dsm_2_4)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_4) += 0 + +c_e_SinkDSMDLRInvestmentBlock_no_comp_inc(demand_dsm_2_5)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_5) += 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_red(demand_dsm_0_0)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_0) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_red(demand_dsm_0_1)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_1) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_red(demand_dsm_1_2)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_2) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_red(demand_dsm_1_3)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_3) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_3) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_red(demand_dsm_2_4)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_4) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_4) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_red(demand_dsm_2_5)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_5) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_inc(demand_dsm_0_0)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_0) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_0) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_0) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_inc(demand_dsm_0_1)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_1) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_1) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_1) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_inc(demand_dsm_1_2)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_2) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_2) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_2) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_inc(demand_dsm_1_3)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_3) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_3) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_3) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_3) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_inc(demand_dsm_2_4)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_4) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_4) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_4) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_4) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_availability_inc(demand_dsm_2_5)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_5) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_5) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_5) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_red(demand_dsm_0)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_0) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_0) +-1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_red(demand_dsm_1)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_1) +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_0) +-1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_red(demand_dsm_2)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_2) +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_1) +-1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_red(demand_dsm_3)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_3) +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_2) +-1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_3) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_red(demand_dsm_4)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_4) +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_3) +-1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_4) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_red(demand_dsm_5)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_5) +-1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_4) +-1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_5) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_inc(demand_dsm_0)_: +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_0) +-1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_0) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_inc(demand_dsm_1)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_1) +-1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_0) +-1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_1) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_inc(demand_dsm_2)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_2) +-1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_1) +-1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_2) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_inc(demand_dsm_3)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_3) +-1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_3) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_3) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_3) ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_2) +-1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_3) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_inc(demand_dsm_4)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_4) +-1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_4) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_4) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_4) ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_3) +-1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_4) += 0 + +c_e_SinkDSMDLRInvestmentBlock_dr_storage_inc(demand_dsm_5)_: +-1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_5) +-1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_5) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_5) ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_4) +-1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_5) += 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(demand_dsm_0_0)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_0) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(demand_dsm_0_1)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_1) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(demand_dsm_1_2)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_2) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(demand_dsm_1_3)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_3) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(demand_dsm_2_4)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_4) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_red(demand_dsm_2_5)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_5) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(demand_dsm_0_0)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_0) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(demand_dsm_0_1)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_1) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(demand_dsm_1_2)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_2) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(demand_dsm_1_3)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_3) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(demand_dsm_2_4)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_4) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_storage_limit_inc(demand_dsm_2_5)_: ++1 SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_5) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_yearly_limit_shed(demand_dsm_0)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_5) +-50 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_yearly_limit_shed(demand_dsm_1)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_5) +-50 SinkDSMDLRInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_yearly_limit_shed(demand_dsm_2)_: ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_5) +-50 SinkDSMDLRInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(demand_dsm_0_0)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_0) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_0) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_0) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_0) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(demand_dsm_0_1)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_1) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_1) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_1) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_1) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(demand_dsm_1_2)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_2) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_2) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_2) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(demand_dsm_1_3)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_3) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_3) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_3) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_3) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_3) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_3) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_3) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(demand_dsm_2_4)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_4) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_4) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_4) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_4) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_4) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_4) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_4) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_dr_logical_constraint(demand_dsm_2_5)_: ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_5) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_5) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_5) ++1 SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_5) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_5) +-0.5 SinkDSMDLRInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMDLRInvestmentBlock_overall_dsm_maximum(demand_dsm_0)_: ++1 SinkDSMDLRInvestmentBlock_total(demand_dsm_0) +<= 1000 + +c_u_SinkDSMDLRInvestmentBlock_overall_dsm_maximum(demand_dsm_1)_: ++1 SinkDSMDLRInvestmentBlock_total(demand_dsm_1) +<= 1000 + +c_u_SinkDSMDLRInvestmentBlock_overall_dsm_maximum(demand_dsm_2)_: ++1 SinkDSMDLRInvestmentBlock_total(demand_dsm_2) +<= 1000 + +c_l_SinkDSMDLRInvestmentBlock_overall_minimum(demand_dsm)_: ++1 SinkDSMDLRInvestmentBlock_total(demand_dsm_2) +>= 5 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(bus_elec_demand_dsm_0_0) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_1) <= +inf + 0 <= flow(bus_elec_demand_dsm_1_2) <= +inf + 0 <= flow(bus_elec_demand_dsm_1_3) <= +inf + 0 <= flow(bus_elec_demand_dsm_2_4) <= +inf + 0 <= flow(bus_elec_demand_dsm_2_5) <= +inf + 33 <= SinkDSMDLRInvestmentBlock_invest(demand_dsm_0) <= 100 + 33 <= SinkDSMDLRInvestmentBlock_invest(demand_dsm_1) <= 100 + 33 <= SinkDSMDLRInvestmentBlock_invest(demand_dsm_2) <= 100 + 0 <= SinkDSMDLRInvestmentBlock_total(demand_dsm_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_total(demand_dsm_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_total(demand_dsm_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_old(demand_dsm_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_old(demand_dsm_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_old(demand_dsm_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_old_end(demand_dsm_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_old_end(demand_dsm_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_old_end(demand_dsm_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_old_exo(demand_dsm_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_old_exo(demand_dsm_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_old_exo(demand_dsm_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_3) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_4) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_1_5) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_3) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_4) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shift(demand_dsm_2_5) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_3) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_4) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_shed(demand_dsm_5) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_3) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_4) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_1_5) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_3) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_4) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up(demand_dsm_2_5) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_3) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_4) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_1_5) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_3) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_4) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_do(demand_dsm_2_5) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_3) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_4) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_1_5) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_3) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_4) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_balance_dsm_up(demand_dsm_2_5) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_3) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_4) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_do_level(demand_dsm_5) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_0) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_1) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_2) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_3) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_4) <= +inf + 0 <= SinkDSMDLRInvestmentBlock_dsm_up_level(demand_dsm_5) <= +inf +end diff --git a/tests/lp_files/dsm_module_DLR_multi_period.lp b/tests/lp_files/dsm_module_DLR_multi_period.lp new file mode 100644 index 000000000..c3b961db7 --- /dev/null +++ b/tests/lp_files/dsm_module_DLR_multi_period.lp @@ -0,0 +1,700 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++2 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_0) ++2 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) ++1.9607843137254901 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) ++1.9607843137254901 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_3) ++1.9223375624759707 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_4) ++1.9223375624759707 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_5) ++2 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_0) ++2 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_1) ++1.9607843137254901 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_2) ++1.9607843137254901 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_3) ++1.9223375624759707 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_4) ++1.9223375624759707 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_5) ++2 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) ++2 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) ++1.9607843137254901 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) ++1.9607843137254901 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_3) ++1.9223375624759707 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_4) ++1.9223375624759707 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_5) ++2 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) ++2 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) ++1.9607843137254901 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) ++1.9607843137254901 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_3) ++1.9223375624759707 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_4) ++1.9223375624759707 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_5) + +s.t. + +c_e_BusBlock_balance(bus_elec_0_0)_: ++1 flow(bus_elec_demand_dsm_0_0) += 0 + +c_e_BusBlock_balance(bus_elec_0_1)_: ++1 flow(bus_elec_demand_dsm_0_1) += 0 + +c_e_BusBlock_balance(bus_elec_1_2)_: ++1 flow(bus_elec_demand_dsm_1_2) += 0 + +c_e_BusBlock_balance(bus_elec_1_3)_: ++1 flow(bus_elec_demand_dsm_1_3) += 0 + +c_e_BusBlock_balance(bus_elec_2_4)_: ++1 flow(bus_elec_demand_dsm_2_4) += 0 + +c_e_BusBlock_balance(bus_elec_2_5)_: ++1 flow(bus_elec_demand_dsm_2_5) += 0 + +c_e_SinkDSMDLRBlock_shift_shed_vars(demand_dsm_1_0)_: ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_0) += 0 + +c_e_SinkDSMDLRBlock_shift_shed_vars(demand_dsm_1_1)_: ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_1) += 0 + +c_e_SinkDSMDLRBlock_shift_shed_vars(demand_dsm_1_2)_: ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_2) += 0 + +c_e_SinkDSMDLRBlock_shift_shed_vars(demand_dsm_1_3)_: ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_3) += 0 + +c_e_SinkDSMDLRBlock_shift_shed_vars(demand_dsm_1_4)_: ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_4) += 0 + +c_e_SinkDSMDLRBlock_shift_shed_vars(demand_dsm_1_5)_: ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_5) += 0 + +c_e_SinkDSMDLRBlock_shift_shed_vars(demand_dsm_2_0)_: ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_0) += 0 + +c_e_SinkDSMDLRBlock_shift_shed_vars(demand_dsm_2_1)_: ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_1) += 0 + +c_e_SinkDSMDLRBlock_shift_shed_vars(demand_dsm_2_2)_: ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_2) += 0 + +c_e_SinkDSMDLRBlock_shift_shed_vars(demand_dsm_2_3)_: ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_3) += 0 + +c_e_SinkDSMDLRBlock_shift_shed_vars(demand_dsm_2_4)_: ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_4) += 0 + +c_e_SinkDSMDLRBlock_shift_shed_vars(demand_dsm_2_5)_: ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_5) += 0 + +c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_0_0)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_0) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_0) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) ++1 flow(bus_elec_demand_dsm_0_0) += 1 + +c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_0_1)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_1) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_1) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) ++1 flow(bus_elec_demand_dsm_0_1) += 1 + +c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_1_2)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_2) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_2) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) ++1 flow(bus_elec_demand_dsm_1_2) += 1 + +c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_1_3)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_3) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_3) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_3) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_3) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_3) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_3) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_3) ++1 flow(bus_elec_demand_dsm_1_3) += 1 + +c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_2_4)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_4) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_4) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_4) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_4) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_4) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_4) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_4) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_4) ++1 flow(bus_elec_demand_dsm_2_4) += 1 + +c_e_SinkDSMDLRBlock_input_output_relation(demand_dsm_2_5)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_5) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_5) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_5) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_5) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_5) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_5) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_5) ++1 flow(bus_elec_demand_dsm_2_5) += 1 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_1_0)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_1_1)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_1) +-1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_1_2)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_2) +-1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_1_3)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_3) +-1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_1_4)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_4) +-1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_3) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_1_5)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_5) +-1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_4) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_2_0)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_2_2)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_2) +-1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_2_3)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_3) +-1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_2_4)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_4) +-1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_red(demand_dsm_2_5)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_5) +-1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_3) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_1_0)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_1_1)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_1_2)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_1_3)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_3) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_1_4)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_4) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_3) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_1_5)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_5) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_4) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_2_0)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_2_2)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_2) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_2_3)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_3) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_2_4)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_4) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) += 0 + +c_e_SinkDSMDLRBlock_capacity_balance_inc(demand_dsm_2_5)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_5) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_3) += 0 + +c_e_SinkDSMDLRBlock_no_comp_red(demand_dsm_1_5)_: ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_5) += 0 + +c_e_SinkDSMDLRBlock_no_comp_red(demand_dsm_2_4)_: ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_4) += 0 + +c_e_SinkDSMDLRBlock_no_comp_red(demand_dsm_2_5)_: ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_5) += 0 + +c_e_SinkDSMDLRBlock_no_comp_inc(demand_dsm_1_5)_: ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_5) += 0 + +c_e_SinkDSMDLRBlock_no_comp_inc(demand_dsm_2_4)_: ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_4) += 0 + +c_e_SinkDSMDLRBlock_no_comp_inc(demand_dsm_2_5)_: ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_5) += 0 + +c_u_SinkDSMDLRBlock_availability_red(demand_dsm_0)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) +<= 0.5 + +c_u_SinkDSMDLRBlock_availability_red(demand_dsm_1)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) +<= 0.5 + +c_u_SinkDSMDLRBlock_availability_red(demand_dsm_2)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) +<= 0.5 + +c_u_SinkDSMDLRBlock_availability_red(demand_dsm_3)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_3) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_3) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_3) +<= 0.5 + +c_u_SinkDSMDLRBlock_availability_red(demand_dsm_4)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_4) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_4) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_4) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_4) +<= 0.5 + +c_u_SinkDSMDLRBlock_availability_red(demand_dsm_5)_: ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_5) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_5) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_5) +<= 0.5 + +c_u_SinkDSMDLRBlock_availability_inc(demand_dsm_0)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_0) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) +<= 0.5 + +c_u_SinkDSMDLRBlock_availability_inc(demand_dsm_1)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_1) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) +<= 0.5 + +c_u_SinkDSMDLRBlock_availability_inc(demand_dsm_2)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_2) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) +<= 0.5 + +c_u_SinkDSMDLRBlock_availability_inc(demand_dsm_3)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_3) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_3) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_3) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_3) +<= 0.5 + +c_u_SinkDSMDLRBlock_availability_inc(demand_dsm_4)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_4) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_4) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_4) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_4) +<= 0.5 + +c_u_SinkDSMDLRBlock_availability_inc(demand_dsm_5)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_5) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_5) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_5) +<= 0.5 + +c_e_SinkDSMDLRBlock_dr_storage_red(demand_dsm_0)_: ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_0) +-1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) +-1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_red(demand_dsm_1)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_1) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_0) +-1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_red(demand_dsm_2)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_2) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_1) +-1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_red(demand_dsm_3)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_3) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_3) ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_2) +-1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_3) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_red(demand_dsm_4)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_4) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_4) ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_3) +-1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_4) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_4) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_4) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_red(demand_dsm_5)_: +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_5) +-1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_5) ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_4) +-1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_5) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_5) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_5) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_inc(demand_dsm_0)_: +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) +-1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_0) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_inc(demand_dsm_1)_: +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_0) +-1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_1) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_inc(demand_dsm_2)_: +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_1) +-1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_2) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_inc(demand_dsm_3)_: +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_3) +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_3) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_3) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_3) ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_2) +-1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_3) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_inc(demand_dsm_4)_: +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_4) +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_4) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_4) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_4) ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_3) +-1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_4) += 0 + +c_e_SinkDSMDLRBlock_dr_storage_inc(demand_dsm_5)_: +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_5) +-1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_5) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_5) ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_4) +-1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_5) += 0 + +c_u_SinkDSMDLRBlock_dr_storage_limit_red(demand_dsm_0)_: ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_0) +<= 0.5 + +c_u_SinkDSMDLRBlock_dr_storage_limit_red(demand_dsm_1)_: ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_1) +<= 0.5 + +c_u_SinkDSMDLRBlock_dr_storage_limit_red(demand_dsm_2)_: ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_2) +<= 0.5 + +c_u_SinkDSMDLRBlock_dr_storage_limit_red(demand_dsm_3)_: ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_3) +<= 0.5 + +c_u_SinkDSMDLRBlock_dr_storage_limit_red(demand_dsm_4)_: ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_4) +<= 0.5 + +c_u_SinkDSMDLRBlock_dr_storage_limit_red(demand_dsm_5)_: ++1 SinkDSMDLRBlock_dsm_do_level(demand_dsm_5) +<= 0.5 + +c_u_SinkDSMDLRBlock_dr_storage_limit_inc(demand_dsm_0)_: ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_0) +<= 0.5 + +c_u_SinkDSMDLRBlock_dr_storage_limit_inc(demand_dsm_1)_: ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_1) +<= 0.5 + +c_u_SinkDSMDLRBlock_dr_storage_limit_inc(demand_dsm_2)_: ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_2) +<= 0.5 + +c_u_SinkDSMDLRBlock_dr_storage_limit_inc(demand_dsm_3)_: ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_3) +<= 0.5 + +c_u_SinkDSMDLRBlock_dr_storage_limit_inc(demand_dsm_4)_: ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_4) +<= 0.5 + +c_u_SinkDSMDLRBlock_dr_storage_limit_inc(demand_dsm_5)_: ++1 SinkDSMDLRBlock_dsm_up_level(demand_dsm_5) +<= 0.5 + +c_u_SinkDSMDLRBlock_dr_logical_constraint(demand_dsm_0)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_0) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_0) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) +<= 0.5 + +c_u_SinkDSMDLRBlock_dr_logical_constraint(demand_dsm_1)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_1) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_1) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) +<= 0.5 + +c_u_SinkDSMDLRBlock_dr_logical_constraint(demand_dsm_2)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_2) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_2) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) +<= 0.5 + +c_u_SinkDSMDLRBlock_dr_logical_constraint(demand_dsm_3)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_3) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_3) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_3) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_3) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_3) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_3) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_3) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_3) +<= 0.5 + +c_u_SinkDSMDLRBlock_dr_logical_constraint(demand_dsm_4)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_4) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_4) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_4) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_4) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_4) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_4) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_4) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_4) +<= 0.5 + +c_u_SinkDSMDLRBlock_dr_logical_constraint(demand_dsm_5)_: ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_5) ++1 SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_5) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_5) ++1 SinkDSMDLRBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_5) ++1 SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_5) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_1_5) ++1 SinkDSMDLRBlock_dsm_up(demand_dsm_2_5) +<= 0.5 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(bus_elec_demand_dsm_0_0) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_1) <= +inf + 0 <= flow(bus_elec_demand_dsm_1_2) <= +inf + 0 <= flow(bus_elec_demand_dsm_1_3) <= +inf + 0 <= flow(bus_elec_demand_dsm_2_4) <= +inf + 0 <= flow(bus_elec_demand_dsm_2_5) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_3) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_4) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_1_5) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_3) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_4) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shift(demand_dsm_2_5) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shed(demand_dsm_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shed(demand_dsm_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shed(demand_dsm_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shed(demand_dsm_3) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shed(demand_dsm_4) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_shed(demand_dsm_5) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_1_3) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_1_4) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_1_5) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_2_3) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_2_4) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up(demand_dsm_2_5) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_3) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_4) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_1_5) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_3) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_4) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_do(demand_dsm_2_5) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_0) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_1) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_2) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_3) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_4) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_1_5) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_0) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_1) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_2) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_3) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_4) <= +inf + 0 <= SinkDSMDLRBlock_balance_dsm_up(demand_dsm_2_5) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_level(demand_dsm_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_level(demand_dsm_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_level(demand_dsm_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_level(demand_dsm_3) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_level(demand_dsm_4) <= +inf + 0 <= SinkDSMDLRBlock_dsm_do_level(demand_dsm_5) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up_level(demand_dsm_0) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up_level(demand_dsm_1) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up_level(demand_dsm_2) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up_level(demand_dsm_3) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up_level(demand_dsm_4) <= +inf + 0 <= SinkDSMDLRBlock_dsm_up_level(demand_dsm_5) <= +inf +end diff --git a/tests/lp_files/dsm_module_oemof_extended_multi_period.lp b/tests/lp_files/dsm_module_oemof_extended_multi_period.lp new file mode 100644 index 000000000..e93d3a748 --- /dev/null +++ b/tests/lp_files/dsm_module_oemof_extended_multi_period.lp @@ -0,0 +1,195 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++100 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_0) ++100 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_1) ++98.039215686274503 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_2) ++98.039215686274503 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_3) ++96.116878123798529 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_4) ++96.116878123798529 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_0) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_1) ++0.98039215686274506 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_2) ++0.98039215686274506 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_3) ++0.96116878123798533 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_4) ++0.96116878123798533 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_5) ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_0) ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_1) ++0.98039215686274506 SinkDSMOemofBlock_dsm_up(demand_dsm_2) ++0.98039215686274506 SinkDSMOemofBlock_dsm_up(demand_dsm_3) ++0.96116878123798533 SinkDSMOemofBlock_dsm_up(demand_dsm_4) ++0.96116878123798533 SinkDSMOemofBlock_dsm_up(demand_dsm_5) + +s.t. + +c_e_BusBlock_balance(bus_elec_0_0)_: ++1 flow(bus_elec_demand_dsm_0_0) += 0 + +c_e_BusBlock_balance(bus_elec_0_1)_: ++1 flow(bus_elec_demand_dsm_0_1) += 0 + +c_e_BusBlock_balance(bus_elec_1_2)_: ++1 flow(bus_elec_demand_dsm_1_2) += 0 + +c_e_BusBlock_balance(bus_elec_1_3)_: ++1 flow(bus_elec_demand_dsm_1_3) += 0 + +c_e_BusBlock_balance(bus_elec_2_4)_: ++1 flow(bus_elec_demand_dsm_2_4) += 0 + +c_e_BusBlock_balance(bus_elec_2_5)_: ++1 flow(bus_elec_demand_dsm_2_5) += 0 + +c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_0_0)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_0) +-1 SinkDSMOemofBlock_dsm_up(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_0) += 1 + +c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_0_1)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_1) +-1 SinkDSMOemofBlock_dsm_up(demand_dsm_1) ++1 flow(bus_elec_demand_dsm_0_1) += 0.90000000000000002 + +c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_1_2)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_2) +-1 SinkDSMOemofBlock_dsm_up(demand_dsm_2) ++1 flow(bus_elec_demand_dsm_1_2) += 0.80000000000000004 + +c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_1_3)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_3) +-1 SinkDSMOemofBlock_dsm_up(demand_dsm_3) ++1 flow(bus_elec_demand_dsm_1_3) += 0.69999999999999996 + +c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_2_4)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_4) +-1 SinkDSMOemofBlock_dsm_up(demand_dsm_4) ++1 flow(bus_elec_demand_dsm_2_4) += 0.69999999999999996 + +c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_2_5)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_5) +-1 SinkDSMOemofBlock_dsm_up(demand_dsm_5) ++1 flow(bus_elec_demand_dsm_2_5) += 0.69999999999999996 + +c_u_SinkDSMOemofBlock_dsm_up_constraint(demand_dsm_0)_: ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_0) +<= 0.5 + +c_u_SinkDSMOemofBlock_dsm_up_constraint(demand_dsm_1)_: ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_1) +<= 0.40000000000000002 + +c_u_SinkDSMOemofBlock_dsm_up_constraint(demand_dsm_2)_: ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_2) +<= 0.5 + +c_u_SinkDSMOemofBlock_dsm_up_constraint(demand_dsm_3)_: ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_3) +<= 0.29999999999999999 + +c_u_SinkDSMOemofBlock_dsm_up_constraint(demand_dsm_4)_: ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_4) +<= 0.29999999999999999 + +c_u_SinkDSMOemofBlock_dsm_up_constraint(demand_dsm_5)_: ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_5) +<= 0.29999999999999999 + +c_u_SinkDSMOemofBlock_dsm_down_constraint(demand_dsm_0)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_0) +<= 0.29999999999999999 + +c_u_SinkDSMOemofBlock_dsm_down_constraint(demand_dsm_1)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_1) +<= 0.29999999999999999 + +c_u_SinkDSMOemofBlock_dsm_down_constraint(demand_dsm_2)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_2) +<= 0.40000000000000002 + +c_u_SinkDSMOemofBlock_dsm_down_constraint(demand_dsm_3)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_3) +<= 0.29999999999999999 + +c_u_SinkDSMOemofBlock_dsm_down_constraint(demand_dsm_4)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_4) +<= 0.29999999999999999 + +c_u_SinkDSMOemofBlock_dsm_down_constraint(demand_dsm_5)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_5) +<= 0.29999999999999999 + +c_e_SinkDSMOemofBlock_dsm_sum_constraint(demand_dsm_0)_: +-1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_0) +-1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_1) ++0.98999999999999999 SinkDSMOemofBlock_dsm_up(demand_dsm_0) ++0.98999999999999999 SinkDSMOemofBlock_dsm_up(demand_dsm_1) += 0 + +c_e_SinkDSMOemofBlock_dsm_sum_constraint(demand_dsm_2)_: +-1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_2) +-1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_3) ++0.98999999999999999 SinkDSMOemofBlock_dsm_up(demand_dsm_2) ++0.98999999999999999 SinkDSMOemofBlock_dsm_up(demand_dsm_3) += 0 + +c_e_SinkDSMOemofBlock_dsm_sum_constraint(demand_dsm_4)_: +-1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_4) +-1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_5) ++0.98999999999999999 SinkDSMOemofBlock_dsm_up(demand_dsm_4) ++0.98999999999999999 SinkDSMOemofBlock_dsm_up(demand_dsm_5) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(bus_elec_demand_dsm_0_0) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_1) <= +inf + 0 <= flow(bus_elec_demand_dsm_1_2) <= +inf + 0 <= flow(bus_elec_demand_dsm_1_3) <= +inf + 0 <= flow(bus_elec_demand_dsm_2_4) <= +inf + 0 <= flow(bus_elec_demand_dsm_2_5) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shift(demand_dsm_0) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shift(demand_dsm_1) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shift(demand_dsm_2) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shift(demand_dsm_3) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shift(demand_dsm_4) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shift(demand_dsm_5) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shed(demand_dsm_0) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shed(demand_dsm_1) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shed(demand_dsm_2) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shed(demand_dsm_3) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shed(demand_dsm_4) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shed(demand_dsm_5) <= +inf + 0 <= SinkDSMOemofBlock_dsm_up(demand_dsm_0) <= +inf + 0 <= SinkDSMOemofBlock_dsm_up(demand_dsm_1) <= +inf + 0 <= SinkDSMOemofBlock_dsm_up(demand_dsm_2) <= +inf + 0 <= SinkDSMOemofBlock_dsm_up(demand_dsm_3) <= +inf + 0 <= SinkDSMOemofBlock_dsm_up(demand_dsm_4) <= +inf + 0 <= SinkDSMOemofBlock_dsm_up(demand_dsm_5) <= +inf +end diff --git a/tests/lp_files/dsm_module_oemof_invest_multi_period.lp b/tests/lp_files/dsm_module_oemof_invest_multi_period.lp new file mode 100644 index 000000000..83d8e6c82 --- /dev/null +++ b/tests/lp_files/dsm_module_oemof_invest_multi_period.lp @@ -0,0 +1,309 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++100 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_0) ++100 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_1) ++98.039215686274503 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_2) ++98.039215686274503 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_3) ++96.116878123798529 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_4) ++96.116878123798529 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_0) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_1) ++0.98039215686274506 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_2) ++0.98039215686274506 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_3) ++0.96116878123798533 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_4) ++0.96116878123798533 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_5) ++1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_0) ++1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_1) ++0.98039215686274506 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_2) ++0.98039215686274506 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_3) ++0.96116878123798533 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_4) ++0.96116878123798533 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_5) ++455.88267648036174 SinkDSMOemofInvestmentBlock_invest(demand_dsm_0) ++440.5314736691401 SinkDSMOemofInvestmentBlock_invest(demand_dsm_1) ++425.73027329942335 SinkDSMOemofInvestmentBlock_invest(demand_dsm_2) ++15992.031251718836 ONE_VAR_CONSTANT + +s.t. + +c_e_BusBlock_balance(bus_elec_0_0)_: ++1 flow(bus_elec_demand_dsm_0_0) += 0 + +c_e_BusBlock_balance(bus_elec_0_1)_: ++1 flow(bus_elec_demand_dsm_0_1) += 0 + +c_e_BusBlock_balance(bus_elec_1_2)_: ++1 flow(bus_elec_demand_dsm_1_2) += 0 + +c_e_BusBlock_balance(bus_elec_1_3)_: ++1 flow(bus_elec_demand_dsm_1_3) += 0 + +c_e_BusBlock_balance(bus_elec_2_4)_: ++1 flow(bus_elec_demand_dsm_2_4) += 0 + +c_e_BusBlock_balance(bus_elec_2_5)_: ++1 flow(bus_elec_demand_dsm_2_5) += 0 + +c_e_SinkDSMOemofInvestmentBlock_total_dsm_rule(demand_dsm_0)_: +-1 SinkDSMOemofInvestmentBlock_invest(demand_dsm_0) ++1 SinkDSMOemofInvestmentBlock_total(demand_dsm_0) += 50 + +c_e_SinkDSMOemofInvestmentBlock_total_dsm_rule(demand_dsm_1)_: +-1 SinkDSMOemofInvestmentBlock_invest(demand_dsm_1) ++1 SinkDSMOemofInvestmentBlock_old(demand_dsm_1) +-1 SinkDSMOemofInvestmentBlock_total(demand_dsm_0) ++1 SinkDSMOemofInvestmentBlock_total(demand_dsm_1) += 0 + +c_e_SinkDSMOemofInvestmentBlock_total_dsm_rule(demand_dsm_2)_: +-1 SinkDSMOemofInvestmentBlock_invest(demand_dsm_2) ++1 SinkDSMOemofInvestmentBlock_old(demand_dsm_2) +-1 SinkDSMOemofInvestmentBlock_total(demand_dsm_1) ++1 SinkDSMOemofInvestmentBlock_total(demand_dsm_2) += 0 + +c_e_SinkDSMOemofInvestmentBlock_old_dsm_rule_end(demand_dsm_0)_: ++1 SinkDSMOemofInvestmentBlock_old_end(demand_dsm_0) += 0 + +c_e_SinkDSMOemofInvestmentBlock_old_dsm_rule_end(demand_dsm_1)_: ++1 SinkDSMOemofInvestmentBlock_old_end(demand_dsm_1) += 0 + +c_e_SinkDSMOemofInvestmentBlock_old_dsm_rule_end(demand_dsm_2)_: ++1 SinkDSMOemofInvestmentBlock_old_end(demand_dsm_2) += 0 + +c_e_SinkDSMOemofInvestmentBlock_old_dsm_rule_exo(demand_dsm_0)_: ++1 SinkDSMOemofInvestmentBlock_old_exo(demand_dsm_0) += 0 + +c_e_SinkDSMOemofInvestmentBlock_old_dsm_rule_exo(demand_dsm_1)_: ++1 SinkDSMOemofInvestmentBlock_old_exo(demand_dsm_1) += 0 + +c_e_SinkDSMOemofInvestmentBlock_old_dsm_rule_exo(demand_dsm_2)_: ++1 SinkDSMOemofInvestmentBlock_old_exo(demand_dsm_2) += 0 + +c_e_SinkDSMOemofInvestmentBlock_old_dsm_rule(demand_dsm_0)_: ++1 SinkDSMOemofInvestmentBlock_old(demand_dsm_0) +-1 SinkDSMOemofInvestmentBlock_old_end(demand_dsm_0) +-1 SinkDSMOemofInvestmentBlock_old_exo(demand_dsm_0) += 0 + +c_e_SinkDSMOemofInvestmentBlock_old_dsm_rule(demand_dsm_1)_: ++1 SinkDSMOemofInvestmentBlock_old(demand_dsm_1) +-1 SinkDSMOemofInvestmentBlock_old_end(demand_dsm_1) +-1 SinkDSMOemofInvestmentBlock_old_exo(demand_dsm_1) += 0 + +c_e_SinkDSMOemofInvestmentBlock_old_dsm_rule(demand_dsm_2)_: ++1 SinkDSMOemofInvestmentBlock_old(demand_dsm_2) +-1 SinkDSMOemofInvestmentBlock_old_end(demand_dsm_2) +-1 SinkDSMOemofInvestmentBlock_old_exo(demand_dsm_2) += 0 + +c_e_SinkDSMOemofInvestmentBlock_input_output_relation(demand_dsm_0_0)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_0) +-1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_0) +-1 SinkDSMOemofInvestmentBlock_total(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_0) += 0 + +c_e_SinkDSMOemofInvestmentBlock_input_output_relation(demand_dsm_0_1)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_1) +-1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_1) +-1 SinkDSMOemofInvestmentBlock_total(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_1) += 0 + +c_e_SinkDSMOemofInvestmentBlock_input_output_relation(demand_dsm_1_2)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_2) +-1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_2) +-1 SinkDSMOemofInvestmentBlock_total(demand_dsm_1) ++1 flow(bus_elec_demand_dsm_1_2) += 0 + +c_e_SinkDSMOemofInvestmentBlock_input_output_relation(demand_dsm_1_3)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_3) +-1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_3) +-1 SinkDSMOemofInvestmentBlock_total(demand_dsm_1) ++1 flow(bus_elec_demand_dsm_1_3) += 0 + +c_e_SinkDSMOemofInvestmentBlock_input_output_relation(demand_dsm_2_4)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_4) +-1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_4) +-1 SinkDSMOemofInvestmentBlock_total(demand_dsm_2) ++1 flow(bus_elec_demand_dsm_2_4) += 0 + +c_e_SinkDSMOemofInvestmentBlock_input_output_relation(demand_dsm_2_5)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_5) +-1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_5) +-1 SinkDSMOemofInvestmentBlock_total(demand_dsm_2) ++1 flow(bus_elec_demand_dsm_2_5) += 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(demand_dsm_0_0)_: ++1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_0) +-0.45000000000000001 SinkDSMOemofInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(demand_dsm_0_1)_: ++1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_1) +-0.36000000000000004 SinkDSMOemofInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(demand_dsm_1_2)_: ++1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_2) +-0.45000000000000001 SinkDSMOemofInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(demand_dsm_1_3)_: ++1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_3) +-0.27000000000000002 SinkDSMOemofInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(demand_dsm_2_4)_: ++1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_4) +-0.27000000000000002 SinkDSMOemofInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_up_constraint(demand_dsm_2_5)_: ++1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_5) +-0.27000000000000002 SinkDSMOemofInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(demand_dsm_0_0)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_0) +-0.45000000000000001 SinkDSMOemofInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(demand_dsm_0_1)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_1) +-0.36000000000000004 SinkDSMOemofInvestmentBlock_total(demand_dsm_0) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(demand_dsm_1_2)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_2) +-0.45000000000000001 SinkDSMOemofInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(demand_dsm_1_3)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_3) +-0.27000000000000002 SinkDSMOemofInvestmentBlock_total(demand_dsm_1) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(demand_dsm_2_4)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_4) +-0.27000000000000002 SinkDSMOemofInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_u_SinkDSMOemofInvestmentBlock_dsm_down_constraint(demand_dsm_2_5)_: ++1 SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_5) +-0.27000000000000002 SinkDSMOemofInvestmentBlock_total(demand_dsm_2) +<= 0 + +c_e_SinkDSMOemofInvestmentBlock_dsm_sum_constraint(demand_dsm_0)_: +-1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_0) +-1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_1) ++1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_0) ++1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_1) += 0 + +c_e_SinkDSMOemofInvestmentBlock_dsm_sum_constraint(demand_dsm_2)_: +-1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_2) +-1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_3) ++1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_2) ++1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_3) += 0 + +c_e_SinkDSMOemofInvestmentBlock_dsm_sum_constraint(demand_dsm_4)_: +-1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_4) +-1 SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_5) ++1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_4) ++1 SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_5) += 0 + +c_u_SinkDSMOemofInvestmentBlock_overall_dsm_maximum(demand_dsm_0)_: ++1 SinkDSMOemofInvestmentBlock_total(demand_dsm_0) +<= 1000 + +c_u_SinkDSMOemofInvestmentBlock_overall_dsm_maximum(demand_dsm_1)_: ++1 SinkDSMOemofInvestmentBlock_total(demand_dsm_1) +<= 1000 + +c_u_SinkDSMOemofInvestmentBlock_overall_dsm_maximum(demand_dsm_2)_: ++1 SinkDSMOemofInvestmentBlock_total(demand_dsm_2) +<= 1000 + +c_l_SinkDSMOemofInvestmentBlock_overall_minimum(demand_dsm)_: ++1 SinkDSMOemofInvestmentBlock_total(demand_dsm_2) +>= 5 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(bus_elec_demand_dsm_0_0) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_1) <= +inf + 0 <= flow(bus_elec_demand_dsm_1_2) <= +inf + 0 <= flow(bus_elec_demand_dsm_1_3) <= +inf + 0 <= flow(bus_elec_demand_dsm_2_4) <= +inf + 0 <= flow(bus_elec_demand_dsm_2_5) <= +inf + 33 <= SinkDSMOemofInvestmentBlock_invest(demand_dsm_0) <= 100 + 33 <= SinkDSMOemofInvestmentBlock_invest(demand_dsm_1) <= 100 + 33 <= SinkDSMOemofInvestmentBlock_invest(demand_dsm_2) <= 100 + 0 <= SinkDSMOemofInvestmentBlock_total(demand_dsm_0) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_total(demand_dsm_1) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_total(demand_dsm_2) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_old(demand_dsm_0) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_old(demand_dsm_1) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_old(demand_dsm_2) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_old_end(demand_dsm_0) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_old_end(demand_dsm_1) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_old_end(demand_dsm_2) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_old_exo(demand_dsm_0) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_old_exo(demand_dsm_1) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_old_exo(demand_dsm_2) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_0) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_1) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_2) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_3) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_4) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shift(demand_dsm_5) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_0) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_1) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_2) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_3) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_4) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_do_shed(demand_dsm_5) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_0) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_1) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_2) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_3) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_4) <= +inf + 0 <= SinkDSMOemofInvestmentBlock_dsm_up(demand_dsm_5) <= +inf +end diff --git a/tests/lp_files/dsm_module_oemof_multi_period.lp b/tests/lp_files/dsm_module_oemof_multi_period.lp new file mode 100644 index 000000000..e872f0ead --- /dev/null +++ b/tests/lp_files/dsm_module_oemof_multi_period.lp @@ -0,0 +1,207 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++2 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_0) ++2 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_1) ++1.9607843137254901 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_2) ++1.9607843137254901 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_3) ++1.9223375624759707 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_4) ++1.9223375624759707 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_5) + +s.t. + +c_e_BusBlock_balance(bus_elec_0_0)_: ++1 flow(bus_elec_demand_dsm_0_0) += 0 + +c_e_BusBlock_balance(bus_elec_0_1)_: ++1 flow(bus_elec_demand_dsm_0_1) += 0 + +c_e_BusBlock_balance(bus_elec_1_2)_: ++1 flow(bus_elec_demand_dsm_1_2) += 0 + +c_e_BusBlock_balance(bus_elec_1_3)_: ++1 flow(bus_elec_demand_dsm_1_3) += 0 + +c_e_BusBlock_balance(bus_elec_2_4)_: ++1 flow(bus_elec_demand_dsm_2_4) += 0 + +c_e_BusBlock_balance(bus_elec_2_5)_: ++1 flow(bus_elec_demand_dsm_2_5) += 0 + +c_e_SinkDSMOemofBlock_shift_shed_vars(demand_dsm_0)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_0) += 0 + +c_e_SinkDSMOemofBlock_shift_shed_vars(demand_dsm_1)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_1) += 0 + +c_e_SinkDSMOemofBlock_shift_shed_vars(demand_dsm_2)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_2) += 0 + +c_e_SinkDSMOemofBlock_shift_shed_vars(demand_dsm_3)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_3) += 0 + +c_e_SinkDSMOemofBlock_shift_shed_vars(demand_dsm_4)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_4) += 0 + +c_e_SinkDSMOemofBlock_shift_shed_vars(demand_dsm_5)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_5) += 0 + +c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_0_0)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_0) +-1 SinkDSMOemofBlock_dsm_up(demand_dsm_0) ++1 flow(bus_elec_demand_dsm_0_0) += 1 + +c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_0_1)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_1) +-1 SinkDSMOemofBlock_dsm_up(demand_dsm_1) ++1 flow(bus_elec_demand_dsm_0_1) += 1 + +c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_1_2)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_2) +-1 SinkDSMOemofBlock_dsm_up(demand_dsm_2) ++1 flow(bus_elec_demand_dsm_1_2) += 1 + +c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_1_3)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_3) +-1 SinkDSMOemofBlock_dsm_up(demand_dsm_3) ++1 flow(bus_elec_demand_dsm_1_3) += 1 + +c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_2_4)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_4) +-1 SinkDSMOemofBlock_dsm_up(demand_dsm_4) ++1 flow(bus_elec_demand_dsm_2_4) += 1 + +c_e_SinkDSMOemofBlock_input_output_relation(demand_dsm_2_5)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_5) +-1 SinkDSMOemofBlock_dsm_up(demand_dsm_5) ++1 flow(bus_elec_demand_dsm_2_5) += 1 + +c_u_SinkDSMOemofBlock_dsm_up_constraint(demand_dsm_0)_: ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_0) +<= 0.5 + +c_u_SinkDSMOemofBlock_dsm_up_constraint(demand_dsm_1)_: ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_1) +<= 0.40000000000000002 + +c_u_SinkDSMOemofBlock_dsm_up_constraint(demand_dsm_2)_: ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_2) +<= 0.5 + +c_u_SinkDSMOemofBlock_dsm_up_constraint(demand_dsm_3)_: ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_3) +<= 0.29999999999999999 + +c_u_SinkDSMOemofBlock_dsm_up_constraint(demand_dsm_4)_: ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_4) +<= 0.29999999999999999 + +c_u_SinkDSMOemofBlock_dsm_up_constraint(demand_dsm_5)_: ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_5) +<= 0.29999999999999999 + +c_u_SinkDSMOemofBlock_dsm_down_constraint(demand_dsm_0)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_0) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_0) +<= 0.5 + +c_u_SinkDSMOemofBlock_dsm_down_constraint(demand_dsm_1)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_1) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_1) +<= 0.40000000000000002 + +c_u_SinkDSMOemofBlock_dsm_down_constraint(demand_dsm_2)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_2) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_2) +<= 0.5 + +c_u_SinkDSMOemofBlock_dsm_down_constraint(demand_dsm_3)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_3) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_3) +<= 0.29999999999999999 + +c_u_SinkDSMOemofBlock_dsm_down_constraint(demand_dsm_4)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_4) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_4) +<= 0.29999999999999999 + +c_u_SinkDSMOemofBlock_dsm_down_constraint(demand_dsm_5)_: ++1 SinkDSMOemofBlock_dsm_do_shed(demand_dsm_5) ++1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_5) +<= 0.29999999999999999 + +c_e_SinkDSMOemofBlock_dsm_sum_constraint(demand_dsm_0)_: +-1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_0) +-1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_1) ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_0) ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_1) += 0 + +c_e_SinkDSMOemofBlock_dsm_sum_constraint(demand_dsm_2)_: +-1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_2) +-1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_3) ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_2) ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_3) += 0 + +c_e_SinkDSMOemofBlock_dsm_sum_constraint(demand_dsm_4)_: +-1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_4) +-1 SinkDSMOemofBlock_dsm_do_shift(demand_dsm_5) ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_4) ++1 SinkDSMOemofBlock_dsm_up(demand_dsm_5) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(bus_elec_demand_dsm_0_0) <= +inf + 0 <= flow(bus_elec_demand_dsm_0_1) <= +inf + 0 <= flow(bus_elec_demand_dsm_1_2) <= +inf + 0 <= flow(bus_elec_demand_dsm_1_3) <= +inf + 0 <= flow(bus_elec_demand_dsm_2_4) <= +inf + 0 <= flow(bus_elec_demand_dsm_2_5) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shift(demand_dsm_0) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shift(demand_dsm_1) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shift(demand_dsm_2) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shift(demand_dsm_3) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shift(demand_dsm_4) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shift(demand_dsm_5) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shed(demand_dsm_0) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shed(demand_dsm_1) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shed(demand_dsm_2) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shed(demand_dsm_3) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shed(demand_dsm_4) <= +inf + 0 <= SinkDSMOemofBlock_dsm_do_shed(demand_dsm_5) <= +inf + 0 <= SinkDSMOemofBlock_dsm_up(demand_dsm_0) <= +inf + 0 <= SinkDSMOemofBlock_dsm_up(demand_dsm_1) <= +inf + 0 <= SinkDSMOemofBlock_dsm_up(demand_dsm_2) <= +inf + 0 <= SinkDSMOemofBlock_dsm_up(demand_dsm_3) <= +inf + 0 <= SinkDSMOemofBlock_dsm_up(demand_dsm_4) <= +inf + 0 <= SinkDSMOemofBlock_dsm_up(demand_dsm_5) <= +inf +end diff --git a/tests/lp_files/fixed_costs_sources.lp b/tests/lp_files/fixed_costs_sources.lp new file mode 100644 index 000000000..1ad2c3309 --- /dev/null +++ b/tests/lp_files/fixed_costs_sources.lp @@ -0,0 +1,93 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++25 flow(pv_forever_electricityBus_0_0) ++25 flow(pv_forever_electricityBus_0_1) ++24.509803921568626 flow(pv_forever_electricityBus_1_2) ++24.509803921568626 flow(pv_forever_electricityBus_1_3) ++24.029219530949632 flow(pv_forever_electricityBus_2_4) ++24.029219530949632 flow(pv_forever_electricityBus_2_5) ++25 flow(pv_with_lifetime_and_age_electricityBus_0_0) ++25 flow(pv_with_lifetime_and_age_electricityBus_0_1) ++24.509803921568626 flow(pv_with_lifetime_and_age_electricityBus_1_2) ++24.509803921568626 flow(pv_with_lifetime_and_age_electricityBus_1_3) ++24.029219530949632 flow(pv_with_lifetime_and_age_electricityBus_2_4) ++24.029219530949632 flow(pv_with_lifetime_and_age_electricityBus_2_5) ++25 flow(pv_with_lifetime_electricityBus_0_0) ++25 flow(pv_with_lifetime_electricityBus_0_1) ++24.509803921568626 flow(pv_with_lifetime_electricityBus_1_2) ++24.509803921568626 flow(pv_with_lifetime_electricityBus_1_3) ++24.029219530949632 flow(pv_with_lifetime_electricityBus_2_4) ++24.029219530949632 flow(pv_with_lifetime_electricityBus_2_5) ++648.01245319357577 ONE_VAR_CONSTANT + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(pv_forever_electricityBus_0_0) ++1 flow(pv_with_lifetime_and_age_electricityBus_0_0) ++1 flow(pv_with_lifetime_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(pv_forever_electricityBus_0_1) ++1 flow(pv_with_lifetime_and_age_electricityBus_0_1) ++1 flow(pv_with_lifetime_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(pv_forever_electricityBus_1_2) ++1 flow(pv_with_lifetime_and_age_electricityBus_1_2) ++1 flow(pv_with_lifetime_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(pv_forever_electricityBus_1_3) ++1 flow(pv_with_lifetime_and_age_electricityBus_1_3) ++1 flow(pv_with_lifetime_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(pv_forever_electricityBus_2_4) ++1 flow(pv_with_lifetime_and_age_electricityBus_2_4) ++1 flow(pv_with_lifetime_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(pv_forever_electricityBus_2_5) ++1 flow(pv_with_lifetime_and_age_electricityBus_2_5) ++1 flow(pv_with_lifetime_electricityBus_2_5) += 0 + +c_e_FlowBlock_lifetime_age_output(pv_with_lifetime_and_age_electricityBus_2_4)_: ++1 flow(pv_with_lifetime_and_age_electricityBus_2_4) += 0 + +c_e_FlowBlock_lifetime_age_output(pv_with_lifetime_and_age_electricityBus_2_5)_: ++1 flow(pv_with_lifetime_and_age_electricityBus_2_5) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(pv_forever_electricityBus_0_0) <= 8 + 0 <= flow(pv_forever_electricityBus_0_1) <= 8 + 0 <= flow(pv_forever_electricityBus_1_2) <= 8 + 0 <= flow(pv_forever_electricityBus_1_3) <= 8 + 0 <= flow(pv_forever_electricityBus_2_4) <= 8 + 0 <= flow(pv_forever_electricityBus_2_5) <= 8 + 0 <= flow(pv_with_lifetime_electricityBus_0_0) <= 8 + 0 <= flow(pv_with_lifetime_electricityBus_0_1) <= 8 + 0 <= flow(pv_with_lifetime_electricityBus_1_2) <= 8 + 0 <= flow(pv_with_lifetime_electricityBus_1_3) <= 8 + 0 <= flow(pv_with_lifetime_electricityBus_2_4) <= 8 + 0 <= flow(pv_with_lifetime_electricityBus_2_5) <= 8 + 0 <= flow(pv_with_lifetime_and_age_electricityBus_0_0) <= 8 + 0 <= flow(pv_with_lifetime_and_age_electricityBus_0_1) <= 8 + 0 <= flow(pv_with_lifetime_and_age_electricityBus_1_2) <= 8 + 0 <= flow(pv_with_lifetime_and_age_electricityBus_1_3) <= 8 + 0 <= flow(pv_with_lifetime_and_age_electricityBus_2_4) <= 8 + 0 <= flow(pv_with_lifetime_and_age_electricityBus_2_5) <= 8 +end diff --git a/tests/lp_files/flow_invest_with_offset_multi_period.lp b/tests/lp_files/flow_invest_with_offset_multi_period.lp new file mode 100644 index 000000000..9d12f58cb --- /dev/null +++ b/tests/lp_files/flow_invest_with_offset_multi_period.lp @@ -0,0 +1,209 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++611.56718125290342 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) ++599.57566789500333 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_1) ++587.8192822500032 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_2) ++34 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_0) ++33.333333333333329 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_1) ++32.679738562091501 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_2) ++25 flow(electricityBus_source_nonconvex_invest_0_0) ++25 flow(electricityBus_source_nonconvex_invest_0_1) ++24.509803921568626 flow(electricityBus_source_nonconvex_invest_1_2) ++24.509803921568626 flow(electricityBus_source_nonconvex_invest_1_3) ++24.029219530949632 flow(electricityBus_source_nonconvex_invest_2_4) ++24.029219530949632 flow(electricityBus_source_nonconvex_invest_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(electricityBus_source_nonconvex_invest_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(electricityBus_source_nonconvex_invest_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(electricityBus_source_nonconvex_invest_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(electricityBus_source_nonconvex_invest_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(electricityBus_source_nonconvex_invest_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(electricityBus_source_nonconvex_invest_2_5) += 0 + +c_u_InvestmentFlowBlock_minimum_rule(electricityBus_source_nonconvex_invest_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) ++15 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_0) +<= 0 + +c_u_InvestmentFlowBlock_minimum_rule(electricityBus_source_nonconvex_invest_1)_: +-1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_1) ++15 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_1) +<= 0 + +c_u_InvestmentFlowBlock_minimum_rule(electricityBus_source_nonconvex_invest_2)_: +-1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_2) ++15 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_2) +<= 0 + +c_u_InvestmentFlowBlock_maximum_rule(electricityBus_source_nonconvex_invest_0)_: ++1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) +-20 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_0) +<= 0 + +c_u_InvestmentFlowBlock_maximum_rule(electricityBus_source_nonconvex_invest_1)_: ++1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_1) +-20 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_1) +<= 0 + +c_u_InvestmentFlowBlock_maximum_rule(electricityBus_source_nonconvex_invest_2)_: ++1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_2) +-20 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_2) +<= 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_source_nonconvex_invest_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) ++1 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_source_nonconvex_invest_1)_: +-1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_1) ++1 InvestmentFlowBlock_old(electricityBus_source_nonconvex_invest_1) +-1 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) ++1 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_source_nonconvex_invest_2)_: +-1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_2) ++1 InvestmentFlowBlock_old(electricityBus_source_nonconvex_invest_2) +-1 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_1) ++1 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_source_nonconvex_invest_0)_: ++1 InvestmentFlowBlock_old_end(electricityBus_source_nonconvex_invest_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_source_nonconvex_invest_1)_: ++1 InvestmentFlowBlock_old_end(electricityBus_source_nonconvex_invest_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_source_nonconvex_invest_2)_: ++1 InvestmentFlowBlock_old_end(electricityBus_source_nonconvex_invest_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_source_nonconvex_invest_0)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_source_nonconvex_invest_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_source_nonconvex_invest_1)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_source_nonconvex_invest_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_source_nonconvex_invest_2)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_source_nonconvex_invest_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_source_nonconvex_invest_0)_: ++1 InvestmentFlowBlock_old(electricityBus_source_nonconvex_invest_0) +-1 InvestmentFlowBlock_old_end(electricityBus_source_nonconvex_invest_0) +-1 InvestmentFlowBlock_old_exo(electricityBus_source_nonconvex_invest_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_source_nonconvex_invest_1)_: ++1 InvestmentFlowBlock_old(electricityBus_source_nonconvex_invest_1) +-1 InvestmentFlowBlock_old_end(electricityBus_source_nonconvex_invest_1) +-1 InvestmentFlowBlock_old_exo(electricityBus_source_nonconvex_invest_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_source_nonconvex_invest_2)_: ++1 InvestmentFlowBlock_old(electricityBus_source_nonconvex_invest_2) +-1 InvestmentFlowBlock_old_end(electricityBus_source_nonconvex_invest_2) +-1 InvestmentFlowBlock_old_exo(electricityBus_source_nonconvex_invest_2) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_0_0)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) ++1 flow(electricityBus_source_nonconvex_invest_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_0_1)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) ++1 flow(electricityBus_source_nonconvex_invest_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_1_2)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_1) ++1 flow(electricityBus_source_nonconvex_invest_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_1_3)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_1) ++1 flow(electricityBus_source_nonconvex_invest_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_2_4)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_2) ++1 flow(electricityBus_source_nonconvex_invest_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_2_5)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_2) ++1 flow(electricityBus_source_nonconvex_invest_2_5) +<= 0 + +c_u_InvestmentFlowBlock_summed_max(electricityBus_source_nonconvex_invest)_: +-2.2999999999999998 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) +-2.2999999999999998 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_1) +-2.2999999999999998 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_2) ++1 flow(electricityBus_source_nonconvex_invest_0_0) ++1 flow(electricityBus_source_nonconvex_invest_0_1) ++1 flow(electricityBus_source_nonconvex_invest_1_2) ++1 flow(electricityBus_source_nonconvex_invest_1_3) ++1 flow(electricityBus_source_nonconvex_invest_2_4) ++1 flow(electricityBus_source_nonconvex_invest_2_5) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_source_nonconvex_invest_0_0) <= +inf + 0 <= flow(electricityBus_source_nonconvex_invest_0_1) <= +inf + 0 <= flow(electricityBus_source_nonconvex_invest_1_2) <= +inf + 0 <= flow(electricityBus_source_nonconvex_invest_1_3) <= +inf + 0 <= flow(electricityBus_source_nonconvex_invest_2_4) <= +inf + 0 <= flow(electricityBus_source_nonconvex_invest_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) <= 20 + 0 <= InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_1) <= 20 + 0 <= InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_2) <= 20 + 0 <= InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_1) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_2) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_source_nonconvex_invest_0) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_source_nonconvex_invest_1) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_source_nonconvex_invest_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_source_nonconvex_invest_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_source_nonconvex_invest_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_source_nonconvex_invest_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_source_nonconvex_invest_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_source_nonconvex_invest_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_source_nonconvex_invest_2) <= +inf + 0 <= InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_0) <= 1 + 0 <= InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_1) <= 1 + 0 <= InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_2) <= 1 +binary + InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_0) + InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_1) + InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_2) +end diff --git a/tests/lp_files/flow_invest_with_offset_no_minimum_multi_period.lp b/tests/lp_files/flow_invest_with_offset_no_minimum_multi_period.lp new file mode 100644 index 000000000..34d8e094b --- /dev/null +++ b/tests/lp_files/flow_invest_with_offset_no_minimum_multi_period.lp @@ -0,0 +1,206 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++611.56718125290342 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) ++599.57566789500333 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_1) ++587.8192822500032 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_2) ++34 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_0) ++33.333333333333329 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_1) ++32.679738562091501 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_2) ++25 flow(electricityBus_source_nonconvex_invest_0_0) ++25 flow(electricityBus_source_nonconvex_invest_0_1) ++24.509803921568626 flow(electricityBus_source_nonconvex_invest_1_2) ++24.509803921568626 flow(electricityBus_source_nonconvex_invest_1_3) ++24.029219530949632 flow(electricityBus_source_nonconvex_invest_2_4) ++24.029219530949632 flow(electricityBus_source_nonconvex_invest_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(electricityBus_source_nonconvex_invest_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(electricityBus_source_nonconvex_invest_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(electricityBus_source_nonconvex_invest_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(electricityBus_source_nonconvex_invest_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(electricityBus_source_nonconvex_invest_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(electricityBus_source_nonconvex_invest_2_5) += 0 + +c_l_InvestmentFlowBlock_minimum_rule(electricityBus_source_nonconvex_invest_0)_: ++1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) +>= 0 + +c_l_InvestmentFlowBlock_minimum_rule(electricityBus_source_nonconvex_invest_1)_: ++1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_1) +>= 0 + +c_l_InvestmentFlowBlock_minimum_rule(electricityBus_source_nonconvex_invest_2)_: ++1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_2) +>= 0 + +c_u_InvestmentFlowBlock_maximum_rule(electricityBus_source_nonconvex_invest_0)_: ++1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) +-1234 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_0) +<= 0 + +c_u_InvestmentFlowBlock_maximum_rule(electricityBus_source_nonconvex_invest_1)_: ++1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_1) +-1234 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_1) +<= 0 + +c_u_InvestmentFlowBlock_maximum_rule(electricityBus_source_nonconvex_invest_2)_: ++1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_2) +-1234 InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_2) +<= 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_source_nonconvex_invest_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) ++1 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_source_nonconvex_invest_1)_: +-1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_1) ++1 InvestmentFlowBlock_old(electricityBus_source_nonconvex_invest_1) +-1 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) ++1 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_source_nonconvex_invest_2)_: +-1 InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_2) ++1 InvestmentFlowBlock_old(electricityBus_source_nonconvex_invest_2) +-1 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_1) ++1 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_source_nonconvex_invest_0)_: ++1 InvestmentFlowBlock_old_end(electricityBus_source_nonconvex_invest_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_source_nonconvex_invest_1)_: ++1 InvestmentFlowBlock_old_end(electricityBus_source_nonconvex_invest_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_source_nonconvex_invest_2)_: ++1 InvestmentFlowBlock_old_end(electricityBus_source_nonconvex_invest_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_source_nonconvex_invest_0)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_source_nonconvex_invest_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_source_nonconvex_invest_1)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_source_nonconvex_invest_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_source_nonconvex_invest_2)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_source_nonconvex_invest_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_source_nonconvex_invest_0)_: ++1 InvestmentFlowBlock_old(electricityBus_source_nonconvex_invest_0) +-1 InvestmentFlowBlock_old_end(electricityBus_source_nonconvex_invest_0) +-1 InvestmentFlowBlock_old_exo(electricityBus_source_nonconvex_invest_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_source_nonconvex_invest_1)_: ++1 InvestmentFlowBlock_old(electricityBus_source_nonconvex_invest_1) +-1 InvestmentFlowBlock_old_end(electricityBus_source_nonconvex_invest_1) +-1 InvestmentFlowBlock_old_exo(electricityBus_source_nonconvex_invest_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_source_nonconvex_invest_2)_: ++1 InvestmentFlowBlock_old(electricityBus_source_nonconvex_invest_2) +-1 InvestmentFlowBlock_old_end(electricityBus_source_nonconvex_invest_2) +-1 InvestmentFlowBlock_old_exo(electricityBus_source_nonconvex_invest_2) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_0_0)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) ++1 flow(electricityBus_source_nonconvex_invest_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_0_1)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) ++1 flow(electricityBus_source_nonconvex_invest_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_1_2)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_1) ++1 flow(electricityBus_source_nonconvex_invest_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_1_3)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_1) ++1 flow(electricityBus_source_nonconvex_invest_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_2_4)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_2) ++1 flow(electricityBus_source_nonconvex_invest_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_source_nonconvex_invest_2_5)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_2) ++1 flow(electricityBus_source_nonconvex_invest_2_5) +<= 0 + +c_u_InvestmentFlowBlock_summed_max(electricityBus_source_nonconvex_invest)_: +-2.2999999999999998 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) +-2.2999999999999998 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_1) +-2.2999999999999998 InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_2) ++1 flow(electricityBus_source_nonconvex_invest_0_0) ++1 flow(electricityBus_source_nonconvex_invest_0_1) ++1 flow(electricityBus_source_nonconvex_invest_1_2) ++1 flow(electricityBus_source_nonconvex_invest_1_3) ++1 flow(electricityBus_source_nonconvex_invest_2_4) ++1 flow(electricityBus_source_nonconvex_invest_2_5) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_source_nonconvex_invest_0_0) <= +inf + 0 <= flow(electricityBus_source_nonconvex_invest_0_1) <= +inf + 0 <= flow(electricityBus_source_nonconvex_invest_1_2) <= +inf + 0 <= flow(electricityBus_source_nonconvex_invest_1_3) <= +inf + 0 <= flow(electricityBus_source_nonconvex_invest_2_4) <= +inf + 0 <= flow(electricityBus_source_nonconvex_invest_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_0) <= 1234 + 0 <= InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_1) <= 1234 + 0 <= InvestmentFlowBlock_invest(electricityBus_source_nonconvex_invest_2) <= 1234 + 0 <= InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_1) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_source_nonconvex_invest_2) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_source_nonconvex_invest_0) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_source_nonconvex_invest_1) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_source_nonconvex_invest_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_source_nonconvex_invest_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_source_nonconvex_invest_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_source_nonconvex_invest_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_source_nonconvex_invest_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_source_nonconvex_invest_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_source_nonconvex_invest_2) <= +inf + 0 <= InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_0) <= 1 + 0 <= InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_1) <= 1 + 0 <= InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_2) <= 1 +binary + InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_0) + InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_1) + InvestmentFlowBlock_invest_status(electricityBus_source_nonconvex_invest_2) +end diff --git a/tests/lp_files/flow_invest_without_offset_multi_period.lp b/tests/lp_files/flow_invest_without_offset_multi_period.lp new file mode 100644 index 000000000..2d3278a9a --- /dev/null +++ b/tests/lp_files/flow_invest_without_offset_multi_period.lp @@ -0,0 +1,206 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++611.56718125290342 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_0) ++599.57566789500333 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_1) ++587.8192822500032 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_2) ++25 flow(electricityBus_sink_nonconvex_invest_0_0) ++25 flow(electricityBus_sink_nonconvex_invest_0_1) ++24.509803921568626 flow(electricityBus_sink_nonconvex_invest_1_2) ++24.509803921568626 flow(electricityBus_sink_nonconvex_invest_1_3) ++24.029219530949632 flow(electricityBus_sink_nonconvex_invest_2_4) ++24.029219530949632 flow(electricityBus_sink_nonconvex_invest_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(electricityBus_sink_nonconvex_invest_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(electricityBus_sink_nonconvex_invest_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(electricityBus_sink_nonconvex_invest_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(electricityBus_sink_nonconvex_invest_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(electricityBus_sink_nonconvex_invest_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(electricityBus_sink_nonconvex_invest_2_5) += 0 + +c_u_InvestmentFlowBlock_minimum_rule(electricityBus_sink_nonconvex_invest_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_0) ++15 InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest_0) +<= 0 + +c_u_InvestmentFlowBlock_minimum_rule(electricityBus_sink_nonconvex_invest_1)_: +-1 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_1) ++15 InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest_1) +<= 0 + +c_u_InvestmentFlowBlock_minimum_rule(electricityBus_sink_nonconvex_invest_2)_: +-1 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_2) ++15 InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest_2) +<= 0 + +c_u_InvestmentFlowBlock_maximum_rule(electricityBus_sink_nonconvex_invest_0)_: ++1 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_0) +-172 InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest_0) +<= 0 + +c_u_InvestmentFlowBlock_maximum_rule(electricityBus_sink_nonconvex_invest_1)_: ++1 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_1) +-172 InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest_1) +<= 0 + +c_u_InvestmentFlowBlock_maximum_rule(electricityBus_sink_nonconvex_invest_2)_: ++1 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_2) +-172 InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest_2) +<= 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_sink_nonconvex_invest_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_0) ++1 InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_sink_nonconvex_invest_1)_: +-1 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_1) ++1 InvestmentFlowBlock_old(electricityBus_sink_nonconvex_invest_1) +-1 InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_0) ++1 InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_sink_nonconvex_invest_2)_: +-1 InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_2) ++1 InvestmentFlowBlock_old(electricityBus_sink_nonconvex_invest_2) +-1 InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_1) ++1 InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_sink_nonconvex_invest_0)_: ++1 InvestmentFlowBlock_old_end(electricityBus_sink_nonconvex_invest_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_sink_nonconvex_invest_1)_: ++1 InvestmentFlowBlock_old_end(electricityBus_sink_nonconvex_invest_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_sink_nonconvex_invest_2)_: ++1 InvestmentFlowBlock_old_end(electricityBus_sink_nonconvex_invest_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_sink_nonconvex_invest_0)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_sink_nonconvex_invest_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_sink_nonconvex_invest_1)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_sink_nonconvex_invest_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_sink_nonconvex_invest_2)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_sink_nonconvex_invest_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_sink_nonconvex_invest_0)_: ++1 InvestmentFlowBlock_old(electricityBus_sink_nonconvex_invest_0) +-1 InvestmentFlowBlock_old_end(electricityBus_sink_nonconvex_invest_0) +-1 InvestmentFlowBlock_old_exo(electricityBus_sink_nonconvex_invest_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_sink_nonconvex_invest_1)_: ++1 InvestmentFlowBlock_old(electricityBus_sink_nonconvex_invest_1) +-1 InvestmentFlowBlock_old_end(electricityBus_sink_nonconvex_invest_1) +-1 InvestmentFlowBlock_old_exo(electricityBus_sink_nonconvex_invest_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_sink_nonconvex_invest_2)_: ++1 InvestmentFlowBlock_old(electricityBus_sink_nonconvex_invest_2) +-1 InvestmentFlowBlock_old_end(electricityBus_sink_nonconvex_invest_2) +-1 InvestmentFlowBlock_old_exo(electricityBus_sink_nonconvex_invest_2) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_sink_nonconvex_invest_0_0)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_0) ++1 flow(electricityBus_sink_nonconvex_invest_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_sink_nonconvex_invest_0_1)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_0) ++1 flow(electricityBus_sink_nonconvex_invest_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_sink_nonconvex_invest_1_2)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_1) ++1 flow(electricityBus_sink_nonconvex_invest_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_sink_nonconvex_invest_1_3)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_1) ++1 flow(electricityBus_sink_nonconvex_invest_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_sink_nonconvex_invest_2_4)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_2) ++1 flow(electricityBus_sink_nonconvex_invest_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_sink_nonconvex_invest_2_5)_: +-0.80000000000000004 InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_2) ++1 flow(electricityBus_sink_nonconvex_invest_2_5) +<= 0 + +c_u_InvestmentFlowBlock_summed_max(electricityBus_sink_nonconvex_invest)_: +-2.2999999999999998 InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_0) +-2.2999999999999998 InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_1) +-2.2999999999999998 InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_2) ++1 flow(electricityBus_sink_nonconvex_invest_0_0) ++1 flow(electricityBus_sink_nonconvex_invest_0_1) ++1 flow(electricityBus_sink_nonconvex_invest_1_2) ++1 flow(electricityBus_sink_nonconvex_invest_1_3) ++1 flow(electricityBus_sink_nonconvex_invest_2_4) ++1 flow(electricityBus_sink_nonconvex_invest_2_5) +<= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_sink_nonconvex_invest_0_0) <= +inf + 0 <= flow(electricityBus_sink_nonconvex_invest_0_1) <= +inf + 0 <= flow(electricityBus_sink_nonconvex_invest_1_2) <= +inf + 0 <= flow(electricityBus_sink_nonconvex_invest_1_3) <= +inf + 0 <= flow(electricityBus_sink_nonconvex_invest_2_4) <= +inf + 0 <= flow(electricityBus_sink_nonconvex_invest_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_0) <= 172 + 0 <= InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_1) <= 172 + 0 <= InvestmentFlowBlock_invest(electricityBus_sink_nonconvex_invest_2) <= 172 + 0 <= InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_1) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_sink_nonconvex_invest_2) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_sink_nonconvex_invest_0) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_sink_nonconvex_invest_1) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_sink_nonconvex_invest_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_sink_nonconvex_invest_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_sink_nonconvex_invest_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_sink_nonconvex_invest_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_sink_nonconvex_invest_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_sink_nonconvex_invest_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_sink_nonconvex_invest_2) <= +inf + 0 <= InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest_0) <= 1 + 0 <= InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest_1) <= 1 + 0 <= InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest_2) <= 1 +binary + InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest_0) + InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest_1) + InvestmentFlowBlock_invest_status(electricityBus_sink_nonconvex_invest_2) +end diff --git a/tests/lp_files/flow_reaching_lifetime.lp b/tests/lp_files/flow_reaching_lifetime.lp new file mode 100644 index 000000000..0909a95f4 --- /dev/null +++ b/tests/lp_files/flow_reaching_lifetime.lp @@ -0,0 +1,56 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++25 flow(electricityBus_excess_0_0) ++25 flow(electricityBus_excess_0_1) ++24.509803921568626 flow(electricityBus_excess_1_2) ++24.509803921568626 flow(electricityBus_excess_1_3) ++24.029219530949632 flow(electricityBus_excess_2_4) ++24.029219530949632 flow(electricityBus_excess_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(electricityBus_excess_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(electricityBus_excess_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(electricityBus_excess_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(electricityBus_excess_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(electricityBus_excess_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(electricityBus_excess_2_5) += 0 + +c_e_FlowBlock_lifetime_output(electricityBus_excess_2_4)_: ++1 flow(electricityBus_excess_2_4) += 0 + +c_e_FlowBlock_lifetime_output(electricityBus_excess_2_5)_: ++1 flow(electricityBus_excess_2_5) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_excess_0_0) <= 8 + 0 <= flow(electricityBus_excess_0_1) <= 8 + 0 <= flow(electricityBus_excess_1_2) <= 8 + 0 <= flow(electricityBus_excess_1_3) <= 8 + 0 <= flow(electricityBus_excess_2_4) <= 8 + 0 <= flow(electricityBus_excess_2_5) <= 8 +end diff --git a/tests/lp_files/flow_reaching_lifetime_initial_age.lp b/tests/lp_files/flow_reaching_lifetime_initial_age.lp new file mode 100644 index 000000000..088c17de9 --- /dev/null +++ b/tests/lp_files/flow_reaching_lifetime_initial_age.lp @@ -0,0 +1,64 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++25 flow(electricityBus_excess_0_0) ++25 flow(electricityBus_excess_0_1) ++24.509803921568626 flow(electricityBus_excess_1_2) ++24.509803921568626 flow(electricityBus_excess_1_3) ++24.029219530949632 flow(electricityBus_excess_2_4) ++24.029219530949632 flow(electricityBus_excess_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(electricityBus_excess_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(electricityBus_excess_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(electricityBus_excess_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(electricityBus_excess_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(electricityBus_excess_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(electricityBus_excess_2_5) += 0 + +c_e_FlowBlock_lifetime_age_output(electricityBus_excess_1_2)_: ++1 flow(electricityBus_excess_1_2) += 0 + +c_e_FlowBlock_lifetime_age_output(electricityBus_excess_1_3)_: ++1 flow(electricityBus_excess_1_3) += 0 + +c_e_FlowBlock_lifetime_age_output(electricityBus_excess_2_4)_: ++1 flow(electricityBus_excess_2_4) += 0 + +c_e_FlowBlock_lifetime_age_output(electricityBus_excess_2_5)_: ++1 flow(electricityBus_excess_2_5) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_excess_0_0) <= 8 + 0 <= flow(electricityBus_excess_0_1) <= 8 + 0 <= flow(electricityBus_excess_1_2) <= 8 + 0 <= flow(electricityBus_excess_1_3) <= 8 + 0 <= flow(electricityBus_excess_2_4) <= 8 + 0 <= flow(electricityBus_excess_2_5) <= 8 +end diff --git a/tests/lp_files/integer_source.lp b/tests/lp_files/integer_source.lp new file mode 100644 index 000000000..6596b44f3 --- /dev/null +++ b/tests/lp_files/integer_source.lp @@ -0,0 +1,52 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++25 flow(electricityBus_excess_0_0) ++25 flow(electricityBus_excess_0_1) ++25 flow(electricityBus_excess_0_2) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(electricityBus_excess_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(electricityBus_excess_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(electricityBus_excess_0_2) += 0 + +c_e_FlowBlock_integer_flow_constr(electricityBus_excess_0_0)_: ++1 FlowBlock_integer_flow(electricityBus_excess_0) +-1 flow(electricityBus_excess_0_0) += 0 + +c_e_FlowBlock_integer_flow_constr(electricityBus_excess_0_1)_: ++1 FlowBlock_integer_flow(electricityBus_excess_1) +-1 flow(electricityBus_excess_0_1) += 0 + +c_e_FlowBlock_integer_flow_constr(electricityBus_excess_0_2)_: ++1 FlowBlock_integer_flow(electricityBus_excess_2) +-1 flow(electricityBus_excess_0_2) += 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_excess_0_0) <= 10 + 0 <= flow(electricityBus_excess_0_1) <= 10 + 0 <= flow(electricityBus_excess_0_2) <= 10 + 0 <= FlowBlock_integer_flow(electricityBus_excess_0) <= +inf + 0 <= FlowBlock_integer_flow(electricityBus_excess_1) <= +inf + 0 <= FlowBlock_integer_flow(electricityBus_excess_2) <= +inf +general + FlowBlock_integer_flow(electricityBus_excess_0) + FlowBlock_integer_flow(electricityBus_excess_1) + FlowBlock_integer_flow(electricityBus_excess_2) +end diff --git a/tests/lp_files/storage_invest_all_nonconvex_multi_period.lp b/tests/lp_files/storage_invest_all_nonconvex_multi_period.lp new file mode 100644 index 000000000..7229540d2 --- /dev/null +++ b/tests/lp_files/storage_invest_all_nonconvex_multi_period.lp @@ -0,0 +1,540 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++24.462687250116133 GenericInvestmentStorageBlock_invest(storage_all_nonconvex_0) ++23.983026715800129 GenericInvestmentStorageBlock_invest(storage_all_nonconvex_1) ++23.512771290000128 GenericInvestmentStorageBlock_invest(storage_all_nonconvex_2) ++30 GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_0) ++29.411764705882351 GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_1) ++28.83506343713956 GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_2) ++12.231343625058066 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_0) ++11.991513357900065 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_1) ++11.756385645000064 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_2) ++12.231343625058066 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_0) ++11.991513357900065 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_1) ++11.756385645000064 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_2) ++10 InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_0) ++9.8039215686274499 InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_1) ++9.6116878123798539 InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_2) ++15 InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_0) ++14.705882352941176 InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_1) ++14.41753171856978 InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_2) + +s.t. + +c_e_BusBlock_balance(bus1_0_0)_: +-1 flow(bus1_storage_all_nonconvex_0_0) ++1 flow(storage_all_nonconvex_bus1_0_0) += 0 + +c_e_BusBlock_balance(bus1_0_1)_: +-1 flow(bus1_storage_all_nonconvex_0_1) ++1 flow(storage_all_nonconvex_bus1_0_1) += 0 + +c_e_BusBlock_balance(bus1_1_2)_: +-1 flow(bus1_storage_all_nonconvex_1_2) ++1 flow(storage_all_nonconvex_bus1_1_2) += 0 + +c_e_BusBlock_balance(bus1_1_3)_: +-1 flow(bus1_storage_all_nonconvex_1_3) ++1 flow(storage_all_nonconvex_bus1_1_3) += 0 + +c_e_BusBlock_balance(bus1_2_4)_: +-1 flow(bus1_storage_all_nonconvex_2_4) ++1 flow(storage_all_nonconvex_bus1_2_4) += 0 + +c_e_BusBlock_balance(bus1_2_5)_: +-1 flow(bus1_storage_all_nonconvex_2_5) ++1 flow(storage_all_nonconvex_bus1_2_5) += 0 + +c_u_InvestmentFlowBlock_minimum_rule(bus1_storage_all_nonconvex_0)_: +-1 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_0) ++5 InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_0) +<= 0 + +c_u_InvestmentFlowBlock_minimum_rule(bus1_storage_all_nonconvex_1)_: +-1 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_1) ++5 InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_1) +<= 0 + +c_u_InvestmentFlowBlock_minimum_rule(bus1_storage_all_nonconvex_2)_: +-1 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_2) ++5 InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_2) +<= 0 + +c_u_InvestmentFlowBlock_minimum_rule(storage_all_nonconvex_bus1_0)_: +-1 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_0) ++8 InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_0) +<= 0 + +c_u_InvestmentFlowBlock_minimum_rule(storage_all_nonconvex_bus1_1)_: +-1 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_1) ++8 InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_1) +<= 0 + +c_u_InvestmentFlowBlock_minimum_rule(storage_all_nonconvex_bus1_2)_: +-1 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_2) ++8 InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_2) +<= 0 + +c_u_InvestmentFlowBlock_maximum_rule(bus1_storage_all_nonconvex_0)_: ++1 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_0) +-30 InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_0) +<= 0 + +c_u_InvestmentFlowBlock_maximum_rule(bus1_storage_all_nonconvex_1)_: ++1 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_1) +-30 InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_1) +<= 0 + +c_u_InvestmentFlowBlock_maximum_rule(bus1_storage_all_nonconvex_2)_: ++1 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_2) +-30 InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_2) +<= 0 + +c_u_InvestmentFlowBlock_maximum_rule(storage_all_nonconvex_bus1_0)_: ++1 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_0) +-20 InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_0) +<= 0 + +c_u_InvestmentFlowBlock_maximum_rule(storage_all_nonconvex_bus1_1)_: ++1 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_1) +-20 InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_1) +<= 0 + +c_u_InvestmentFlowBlock_maximum_rule(storage_all_nonconvex_bus1_2)_: ++1 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_2) +-20 InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_2) +<= 0 + +c_e_InvestmentFlowBlock_total_rule(bus1_storage_all_nonconvex_0)_: +-1 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_0) ++1 InvestmentFlowBlock_total(bus1_storage_all_nonconvex_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(bus1_storage_all_nonconvex_1)_: +-1 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_1) ++1 InvestmentFlowBlock_old(bus1_storage_all_nonconvex_1) +-1 InvestmentFlowBlock_total(bus1_storage_all_nonconvex_0) ++1 InvestmentFlowBlock_total(bus1_storage_all_nonconvex_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(bus1_storage_all_nonconvex_2)_: +-1 InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_2) ++1 InvestmentFlowBlock_old(bus1_storage_all_nonconvex_2) +-1 InvestmentFlowBlock_total(bus1_storage_all_nonconvex_1) ++1 InvestmentFlowBlock_total(bus1_storage_all_nonconvex_2) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_all_nonconvex_bus1_0)_: +-1 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_0) ++1 InvestmentFlowBlock_total(storage_all_nonconvex_bus1_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_all_nonconvex_bus1_1)_: +-1 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_1) ++1 InvestmentFlowBlock_old(storage_all_nonconvex_bus1_1) +-1 InvestmentFlowBlock_total(storage_all_nonconvex_bus1_0) ++1 InvestmentFlowBlock_total(storage_all_nonconvex_bus1_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_all_nonconvex_bus1_2)_: +-1 InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_2) ++1 InvestmentFlowBlock_old(storage_all_nonconvex_bus1_2) +-1 InvestmentFlowBlock_total(storage_all_nonconvex_bus1_1) ++1 InvestmentFlowBlock_total(storage_all_nonconvex_bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(bus1_storage_all_nonconvex_0)_: ++1 InvestmentFlowBlock_old_end(bus1_storage_all_nonconvex_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(bus1_storage_all_nonconvex_1)_: ++1 InvestmentFlowBlock_old_end(bus1_storage_all_nonconvex_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(bus1_storage_all_nonconvex_2)_: ++1 InvestmentFlowBlock_old_end(bus1_storage_all_nonconvex_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage_all_nonconvex_bus1_0)_: ++1 InvestmentFlowBlock_old_end(storage_all_nonconvex_bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage_all_nonconvex_bus1_1)_: ++1 InvestmentFlowBlock_old_end(storage_all_nonconvex_bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage_all_nonconvex_bus1_2)_: ++1 InvestmentFlowBlock_old_end(storage_all_nonconvex_bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(bus1_storage_all_nonconvex_0)_: ++1 InvestmentFlowBlock_old_exo(bus1_storage_all_nonconvex_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(bus1_storage_all_nonconvex_1)_: ++1 InvestmentFlowBlock_old_exo(bus1_storage_all_nonconvex_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(bus1_storage_all_nonconvex_2)_: ++1 InvestmentFlowBlock_old_exo(bus1_storage_all_nonconvex_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage_all_nonconvex_bus1_0)_: ++1 InvestmentFlowBlock_old_exo(storage_all_nonconvex_bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage_all_nonconvex_bus1_1)_: ++1 InvestmentFlowBlock_old_exo(storage_all_nonconvex_bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage_all_nonconvex_bus1_2)_: ++1 InvestmentFlowBlock_old_exo(storage_all_nonconvex_bus1_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(bus1_storage_all_nonconvex_0)_: ++1 InvestmentFlowBlock_old(bus1_storage_all_nonconvex_0) +-1 InvestmentFlowBlock_old_end(bus1_storage_all_nonconvex_0) +-1 InvestmentFlowBlock_old_exo(bus1_storage_all_nonconvex_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(bus1_storage_all_nonconvex_1)_: ++1 InvestmentFlowBlock_old(bus1_storage_all_nonconvex_1) +-1 InvestmentFlowBlock_old_end(bus1_storage_all_nonconvex_1) +-1 InvestmentFlowBlock_old_exo(bus1_storage_all_nonconvex_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(bus1_storage_all_nonconvex_2)_: ++1 InvestmentFlowBlock_old(bus1_storage_all_nonconvex_2) +-1 InvestmentFlowBlock_old_end(bus1_storage_all_nonconvex_2) +-1 InvestmentFlowBlock_old_exo(bus1_storage_all_nonconvex_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage_all_nonconvex_bus1_0)_: ++1 InvestmentFlowBlock_old(storage_all_nonconvex_bus1_0) +-1 InvestmentFlowBlock_old_end(storage_all_nonconvex_bus1_0) +-1 InvestmentFlowBlock_old_exo(storage_all_nonconvex_bus1_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage_all_nonconvex_bus1_1)_: ++1 InvestmentFlowBlock_old(storage_all_nonconvex_bus1_1) +-1 InvestmentFlowBlock_old_end(storage_all_nonconvex_bus1_1) +-1 InvestmentFlowBlock_old_exo(storage_all_nonconvex_bus1_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage_all_nonconvex_bus1_2)_: ++1 InvestmentFlowBlock_old(storage_all_nonconvex_bus1_2) +-1 InvestmentFlowBlock_old_end(storage_all_nonconvex_bus1_2) +-1 InvestmentFlowBlock_old_exo(storage_all_nonconvex_bus1_2) += 0 + +c_u_InvestmentFlowBlock_max(bus1_storage_all_nonconvex_0_0)_: +-1 InvestmentFlowBlock_total(bus1_storage_all_nonconvex_0) ++1 flow(bus1_storage_all_nonconvex_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(bus1_storage_all_nonconvex_0_1)_: +-1 InvestmentFlowBlock_total(bus1_storage_all_nonconvex_0) ++1 flow(bus1_storage_all_nonconvex_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(bus1_storage_all_nonconvex_1_2)_: +-1 InvestmentFlowBlock_total(bus1_storage_all_nonconvex_1) ++1 flow(bus1_storage_all_nonconvex_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(bus1_storage_all_nonconvex_1_3)_: +-1 InvestmentFlowBlock_total(bus1_storage_all_nonconvex_1) ++1 flow(bus1_storage_all_nonconvex_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(bus1_storage_all_nonconvex_2_4)_: +-1 InvestmentFlowBlock_total(bus1_storage_all_nonconvex_2) ++1 flow(bus1_storage_all_nonconvex_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(bus1_storage_all_nonconvex_2_5)_: +-1 InvestmentFlowBlock_total(bus1_storage_all_nonconvex_2) ++1 flow(bus1_storage_all_nonconvex_2_5) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_all_nonconvex_bus1_0_0)_: +-1 InvestmentFlowBlock_total(storage_all_nonconvex_bus1_0) ++1 flow(storage_all_nonconvex_bus1_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_all_nonconvex_bus1_0_1)_: +-1 InvestmentFlowBlock_total(storage_all_nonconvex_bus1_0) ++1 flow(storage_all_nonconvex_bus1_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_all_nonconvex_bus1_1_2)_: +-1 InvestmentFlowBlock_total(storage_all_nonconvex_bus1_1) ++1 flow(storage_all_nonconvex_bus1_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_all_nonconvex_bus1_1_3)_: +-1 InvestmentFlowBlock_total(storage_all_nonconvex_bus1_1) ++1 flow(storage_all_nonconvex_bus1_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_all_nonconvex_bus1_2_4)_: +-1 InvestmentFlowBlock_total(storage_all_nonconvex_bus1_2) ++1 flow(storage_all_nonconvex_bus1_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_all_nonconvex_bus1_2_5)_: +-1 InvestmentFlowBlock_total(storage_all_nonconvex_bus1_2) ++1 flow(storage_all_nonconvex_bus1_2_5) +<= 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_all_nonconvex_0)_: +-1 GenericInvestmentStorageBlock_invest(storage_all_nonconvex_0) ++1 GenericInvestmentStorageBlock_total(storage_all_nonconvex_0) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_all_nonconvex_1)_: +-1 GenericInvestmentStorageBlock_invest(storage_all_nonconvex_1) ++1 GenericInvestmentStorageBlock_old(storage_all_nonconvex_1) +-1 GenericInvestmentStorageBlock_total(storage_all_nonconvex_0) ++1 GenericInvestmentStorageBlock_total(storage_all_nonconvex_1) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_all_nonconvex_2)_: +-1 GenericInvestmentStorageBlock_invest(storage_all_nonconvex_2) ++1 GenericInvestmentStorageBlock_old(storage_all_nonconvex_2) +-1 GenericInvestmentStorageBlock_total(storage_all_nonconvex_1) ++1 GenericInvestmentStorageBlock_total(storage_all_nonconvex_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage_all_nonconvex_0)_: ++1 GenericInvestmentStorageBlock_old_end(storage_all_nonconvex_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage_all_nonconvex_1)_: ++1 GenericInvestmentStorageBlock_old_end(storage_all_nonconvex_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage_all_nonconvex_2)_: ++1 GenericInvestmentStorageBlock_old_end(storage_all_nonconvex_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage_all_nonconvex_0)_: ++1 GenericInvestmentStorageBlock_old_exo(storage_all_nonconvex_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage_all_nonconvex_1)_: ++1 GenericInvestmentStorageBlock_old_exo(storage_all_nonconvex_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage_all_nonconvex_2)_: ++1 GenericInvestmentStorageBlock_old_exo(storage_all_nonconvex_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage_all_nonconvex_0)_: ++1 GenericInvestmentStorageBlock_old(storage_all_nonconvex_0) +-1 GenericInvestmentStorageBlock_old_end(storage_all_nonconvex_0) +-1 GenericInvestmentStorageBlock_old_exo(storage_all_nonconvex_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage_all_nonconvex_1)_: ++1 GenericInvestmentStorageBlock_old(storage_all_nonconvex_1) +-1 GenericInvestmentStorageBlock_old_end(storage_all_nonconvex_1) +-1 GenericInvestmentStorageBlock_old_exo(storage_all_nonconvex_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage_all_nonconvex_2)_: ++1 GenericInvestmentStorageBlock_old(storage_all_nonconvex_2) +-1 GenericInvestmentStorageBlock_old_end(storage_all_nonconvex_2) +-1 GenericInvestmentStorageBlock_old_exo(storage_all_nonconvex_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_all_nonconvex_0_1)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_0) ++1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_1) +-1 flow(bus1_storage_all_nonconvex_0_1) ++1 flow(storage_all_nonconvex_bus1_0_1) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_all_nonconvex_1_2)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_1) ++1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_2) +-1 flow(bus1_storage_all_nonconvex_1_2) ++1 flow(storage_all_nonconvex_bus1_1_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_all_nonconvex_1_3)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_2) ++1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_3) +-1 flow(bus1_storage_all_nonconvex_1_3) ++1 flow(storage_all_nonconvex_bus1_1_3) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_all_nonconvex_2_4)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_3) ++1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_4) +-1 flow(bus1_storage_all_nonconvex_2_4) ++1 flow(storage_all_nonconvex_bus1_2_4) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_all_nonconvex_2_5)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_4) ++1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_5) +-1 flow(bus1_storage_all_nonconvex_2_5) ++1 flow(storage_all_nonconvex_bus1_2_5) += 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_all_nonconvex_0_0)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_0) +-1 GenericInvestmentStorageBlock_total(storage_all_nonconvex_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_all_nonconvex_0_1)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_1) +-1 GenericInvestmentStorageBlock_total(storage_all_nonconvex_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_all_nonconvex_1_2)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_2) +-1 GenericInvestmentStorageBlock_total(storage_all_nonconvex_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_all_nonconvex_1_3)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_3) +-1 GenericInvestmentStorageBlock_total(storage_all_nonconvex_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_all_nonconvex_2_4)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_4) +-1 GenericInvestmentStorageBlock_total(storage_all_nonconvex_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_all_nonconvex_2_5)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_5) +-1 GenericInvestmentStorageBlock_total(storage_all_nonconvex_2) +<= 0 + +c_l_GenericInvestmentStorageBlock_limit_max(storage_all_nonconvex_0)_: +-1 GenericInvestmentStorageBlock_invest(storage_all_nonconvex_0) ++100 GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_0) +>= 0 + +c_l_GenericInvestmentStorageBlock_limit_max(storage_all_nonconvex_1)_: +-1 GenericInvestmentStorageBlock_invest(storage_all_nonconvex_1) ++100 GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_1) +>= 0 + +c_l_GenericInvestmentStorageBlock_limit_max(storage_all_nonconvex_2)_: +-1 GenericInvestmentStorageBlock_invest(storage_all_nonconvex_2) ++100 GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_2) +>= 0 + +c_l_GenericInvestmentStorageBlock_limit_min(storage_all_nonconvex_0)_: ++1 GenericInvestmentStorageBlock_invest(storage_all_nonconvex_0) +-20 GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_0) +>= 0 + +c_l_GenericInvestmentStorageBlock_limit_min(storage_all_nonconvex_1)_: ++1 GenericInvestmentStorageBlock_invest(storage_all_nonconvex_1) +-20 GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_1) +>= 0 + +c_l_GenericInvestmentStorageBlock_limit_min(storage_all_nonconvex_2)_: ++1 GenericInvestmentStorageBlock_invest(storage_all_nonconvex_2) +-20 GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_2) +>= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(bus1_storage_all_nonconvex_0_0) <= +inf + 0 <= flow(bus1_storage_all_nonconvex_0_1) <= +inf + 0 <= flow(bus1_storage_all_nonconvex_1_2) <= +inf + 0 <= flow(bus1_storage_all_nonconvex_1_3) <= +inf + 0 <= flow(bus1_storage_all_nonconvex_2_4) <= +inf + 0 <= flow(bus1_storage_all_nonconvex_2_5) <= +inf + 0 <= flow(storage_all_nonconvex_bus1_0_0) <= +inf + 0 <= flow(storage_all_nonconvex_bus1_0_1) <= +inf + 0 <= flow(storage_all_nonconvex_bus1_1_2) <= +inf + 0 <= flow(storage_all_nonconvex_bus1_1_3) <= +inf + 0 <= flow(storage_all_nonconvex_bus1_2_4) <= +inf + 0 <= flow(storage_all_nonconvex_bus1_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_0) <= 30 + 0 <= InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_1) <= 30 + 0 <= InvestmentFlowBlock_invest(bus1_storage_all_nonconvex_2) <= 30 + 0 <= InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_0) <= 20 + 0 <= InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_1) <= 20 + 0 <= InvestmentFlowBlock_invest(storage_all_nonconvex_bus1_2) <= 20 + 0 <= InvestmentFlowBlock_total(bus1_storage_all_nonconvex_0) <= +inf + 0 <= InvestmentFlowBlock_total(bus1_storage_all_nonconvex_1) <= +inf + 0 <= InvestmentFlowBlock_total(bus1_storage_all_nonconvex_2) <= +inf + 0 <= InvestmentFlowBlock_total(storage_all_nonconvex_bus1_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage_all_nonconvex_bus1_1) <= +inf + 0 <= InvestmentFlowBlock_total(storage_all_nonconvex_bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old(bus1_storage_all_nonconvex_0) <= +inf + 0 <= InvestmentFlowBlock_old(bus1_storage_all_nonconvex_1) <= +inf + 0 <= InvestmentFlowBlock_old(bus1_storage_all_nonconvex_2) <= +inf + 0 <= InvestmentFlowBlock_old(storage_all_nonconvex_bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old(storage_all_nonconvex_bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old(storage_all_nonconvex_bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(bus1_storage_all_nonconvex_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(bus1_storage_all_nonconvex_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(bus1_storage_all_nonconvex_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage_all_nonconvex_bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage_all_nonconvex_bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage_all_nonconvex_bus1_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(bus1_storage_all_nonconvex_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(bus1_storage_all_nonconvex_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(bus1_storage_all_nonconvex_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage_all_nonconvex_bus1_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage_all_nonconvex_bus1_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage_all_nonconvex_bus1_2) <= +inf + 0 <= InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_0) <= 1 + 0 <= InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_1) <= 1 + 0 <= InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_2) <= 1 + 0 <= InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_0) <= 1 + 0 <= InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_1) <= 1 + 0 <= InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_2) <= 1 + 0 <= GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_0) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_1) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_2) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_3) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_4) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_all_nonconvex_5) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage_all_nonconvex_0) <= 100 + 0 <= GenericInvestmentStorageBlock_invest(storage_all_nonconvex_1) <= 100 + 0 <= GenericInvestmentStorageBlock_invest(storage_all_nonconvex_2) <= 100 + 0 <= GenericInvestmentStorageBlock_total(storage_all_nonconvex_0) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage_all_nonconvex_1) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage_all_nonconvex_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage_all_nonconvex_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage_all_nonconvex_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage_all_nonconvex_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage_all_nonconvex_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage_all_nonconvex_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage_all_nonconvex_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage_all_nonconvex_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage_all_nonconvex_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage_all_nonconvex_2) <= +inf + 0 <= GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_0) <= 1 + 0 <= GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_1) <= 1 + 0 <= GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_2) <= 1 +binary + InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_0) + InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_1) + InvestmentFlowBlock_invest_status(bus1_storage_all_nonconvex_2) + InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_0) + InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_1) + InvestmentFlowBlock_invest_status(storage_all_nonconvex_bus1_2) + GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_0) + GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_1) + GenericInvestmentStorageBlock_invest_status(storage_all_nonconvex_2) +end diff --git a/tests/lp_files/storage_invest_multi_period.lp b/tests/lp_files/storage_invest_multi_period.lp new file mode 100644 index 000000000..b1acd4989 --- /dev/null +++ b/tests/lp_files/storage_invest_multi_period.lp @@ -0,0 +1,221 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++351.53628135238012 GenericInvestmentStorageBlock_invest(storage1_0) ++341.96150336108451 GenericInvestmentStorageBlock_invest(storage1_1) ++332.6786079381219 GenericInvestmentStorageBlock_invest(storage1_2) ++250 ONE_VAR_CONSTANT + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage1_0_0) ++1 flow(storage1_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage1_0_1) ++1 flow(storage1_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: +-1 flow(electricityBus_storage1_1_2) ++1 flow(storage1_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: +-1 flow(electricityBus_storage1_1_3) ++1 flow(storage1_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: +-1 flow(electricityBus_storage1_2_4) ++1 flow(storage1_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: +-1 flow(electricityBus_storage1_2_5) ++1 flow(storage1_electricityBus_2_5) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage1_0)_: +-1 GenericInvestmentStorageBlock_invest(storage1_0) ++1 GenericInvestmentStorageBlock_total(storage1_0) += 50 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage1_1)_: +-1 GenericInvestmentStorageBlock_invest(storage1_1) ++1 GenericInvestmentStorageBlock_old(storage1_1) +-1 GenericInvestmentStorageBlock_total(storage1_0) ++1 GenericInvestmentStorageBlock_total(storage1_1) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage1_2)_: +-1 GenericInvestmentStorageBlock_invest(storage1_2) ++1 GenericInvestmentStorageBlock_old(storage1_2) +-1 GenericInvestmentStorageBlock_total(storage1_1) ++1 GenericInvestmentStorageBlock_total(storage1_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage1_0)_: ++1 GenericInvestmentStorageBlock_old_end(storage1_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage1_1)_: ++1 GenericInvestmentStorageBlock_old_end(storage1_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage1_2)_: ++1 GenericInvestmentStorageBlock_old_end(storage1_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage1_0)_: ++1 GenericInvestmentStorageBlock_old_exo(storage1_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage1_1)_: ++1 GenericInvestmentStorageBlock_old_exo(storage1_1) += 50 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage1_2)_: ++1 GenericInvestmentStorageBlock_old_exo(storage1_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage1_0)_: ++1 GenericInvestmentStorageBlock_old(storage1_0) +-1 GenericInvestmentStorageBlock_old_end(storage1_0) +-1 GenericInvestmentStorageBlock_old_exo(storage1_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage1_1)_: ++1 GenericInvestmentStorageBlock_old(storage1_1) +-1 GenericInvestmentStorageBlock_old_end(storage1_1) +-1 GenericInvestmentStorageBlock_old_exo(storage1_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage1_2)_: ++1 GenericInvestmentStorageBlock_old(storage1_2) +-1 GenericInvestmentStorageBlock_old_end(storage1_2) +-1 GenericInvestmentStorageBlock_old_exo(storage1_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage1_0_1)_: +-1 GenericInvestmentStorageBlock_storage_content(storage1_0) ++1 GenericInvestmentStorageBlock_storage_content(storage1_1) +-1 flow(electricityBus_storage1_0_1) ++1 flow(storage1_electricityBus_0_1) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage1_1_2)_: +-1 GenericInvestmentStorageBlock_storage_content(storage1_1) ++1 GenericInvestmentStorageBlock_storage_content(storage1_2) +-1 flow(electricityBus_storage1_1_2) ++1 flow(storage1_electricityBus_1_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage1_1_3)_: +-1 GenericInvestmentStorageBlock_storage_content(storage1_2) ++1 GenericInvestmentStorageBlock_storage_content(storage1_3) +-1 flow(electricityBus_storage1_1_3) ++1 flow(storage1_electricityBus_1_3) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage1_2_4)_: +-1 GenericInvestmentStorageBlock_storage_content(storage1_3) ++1 GenericInvestmentStorageBlock_storage_content(storage1_4) +-1 flow(electricityBus_storage1_2_4) ++1 flow(storage1_electricityBus_2_4) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage1_2_5)_: +-1 GenericInvestmentStorageBlock_storage_content(storage1_4) ++1 GenericInvestmentStorageBlock_storage_content(storage1_5) +-1 flow(electricityBus_storage1_2_5) ++1 flow(storage1_electricityBus_2_5) += 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0_0)_: ++1 GenericInvestmentStorageBlock_storage_content(storage1_0) +-1 GenericInvestmentStorageBlock_total(storage1_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_0_1)_: ++1 GenericInvestmentStorageBlock_storage_content(storage1_1) +-1 GenericInvestmentStorageBlock_total(storage1_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_1_2)_: ++1 GenericInvestmentStorageBlock_storage_content(storage1_2) +-1 GenericInvestmentStorageBlock_total(storage1_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_1_3)_: ++1 GenericInvestmentStorageBlock_storage_content(storage1_3) +-1 GenericInvestmentStorageBlock_total(storage1_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_2_4)_: ++1 GenericInvestmentStorageBlock_storage_content(storage1_4) +-1 GenericInvestmentStorageBlock_total(storage1_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage1_2_5)_: ++1 GenericInvestmentStorageBlock_storage_content(storage1_5) +-1 GenericInvestmentStorageBlock_total(storage1_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_overall_storage_maximum(storage1_0)_: ++1 GenericInvestmentStorageBlock_total(storage1_0) +<= 500 + +c_u_GenericInvestmentStorageBlock_overall_storage_maximum(storage1_1)_: ++1 GenericInvestmentStorageBlock_total(storage1_1) +<= 500 + +c_u_GenericInvestmentStorageBlock_overall_storage_maximum(storage1_2)_: ++1 GenericInvestmentStorageBlock_total(storage1_2) +<= 500 + +c_l_GenericInvestmentStorageBlock_overall_minimum(storage1)_: ++1 GenericInvestmentStorageBlock_total(storage1_2) +>= 10 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_storage1_0_0) <= +inf + 0 <= flow(electricityBus_storage1_0_1) <= +inf + 0 <= flow(electricityBus_storage1_1_2) <= +inf + 0 <= flow(electricityBus_storage1_1_3) <= +inf + 0 <= flow(electricityBus_storage1_2_4) <= +inf + 0 <= flow(electricityBus_storage1_2_5) <= +inf + 0 <= flow(storage1_electricityBus_0_0) <= +inf + 0 <= flow(storage1_electricityBus_0_1) <= +inf + 0 <= flow(storage1_electricityBus_1_2) <= +inf + 0 <= flow(storage1_electricityBus_1_3) <= +inf + 0 <= flow(storage1_electricityBus_2_4) <= +inf + 0 <= flow(storage1_electricityBus_2_5) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage1_0) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage1_1) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage1_2) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage1_3) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage1_4) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage1_5) <= +inf + 100 <= GenericInvestmentStorageBlock_invest(storage1_0) <= 200 + 100 <= GenericInvestmentStorageBlock_invest(storage1_1) <= 200 + 100 <= GenericInvestmentStorageBlock_invest(storage1_2) <= 200 + 0 <= GenericInvestmentStorageBlock_total(storage1_0) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage1_1) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage1_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage1_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage1_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage1_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage1_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage1_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage1_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage1_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage1_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage1_2) <= +inf +end diff --git a/tests/lp_files/storage_invest_with_offset_multi_period.lp b/tests/lp_files/storage_invest_with_offset_multi_period.lp new file mode 100644 index 000000000..a7c706ce1 --- /dev/null +++ b/tests/lp_files/storage_invest_with_offset_multi_period.lp @@ -0,0 +1,528 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++177.35448256334197 GenericInvestmentStorageBlock_invest(storage_non_convex_0) ++173.87694368955096 GenericInvestmentStorageBlock_invest(storage_non_convex_1) ++170.46759185250093 GenericInvestmentStorageBlock_invest(storage_non_convex_2) ++5 GenericInvestmentStorageBlock_invest_status(storage_non_convex_0) ++4.901960784313725 GenericInvestmentStorageBlock_invest_status(storage_non_convex_1) ++4.805843906189927 GenericInvestmentStorageBlock_invest_status(storage_non_convex_2) ++56 flow(electricityBus_storage_non_convex_0_0) ++56 flow(electricityBus_storage_non_convex_0_1) ++54.901960784313722 flow(electricityBus_storage_non_convex_1_2) ++54.901960784313722 flow(electricityBus_storage_non_convex_1_3) ++53.825451749327179 flow(electricityBus_storage_non_convex_2_4) ++53.825451749327179 flow(electricityBus_storage_non_convex_2_5) ++24 flow(storage_non_convex_electricityBus_0_0) ++24 flow(storage_non_convex_electricityBus_0_1) ++23.52941176470588 flow(storage_non_convex_electricityBus_1_2) ++23.52941176470588 flow(storage_non_convex_electricityBus_1_3) ++23.068050749711649 flow(storage_non_convex_electricityBus_2_4) ++23.068050749711649 flow(storage_non_convex_electricityBus_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage_non_convex_0_0) ++1 flow(storage_non_convex_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage_non_convex_0_1) ++1 flow(storage_non_convex_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: +-1 flow(electricityBus_storage_non_convex_1_2) ++1 flow(storage_non_convex_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: +-1 flow(electricityBus_storage_non_convex_1_3) ++1 flow(storage_non_convex_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: +-1 flow(electricityBus_storage_non_convex_2_4) ++1 flow(storage_non_convex_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: +-1 flow(electricityBus_storage_non_convex_2_5) ++1 flow(storage_non_convex_electricityBus_2_5) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage_non_convex_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage_non_convex_0) ++1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage_non_convex_1)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage_non_convex_1) ++1 InvestmentFlowBlock_old(electricityBus_storage_non_convex_1) +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) ++1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage_non_convex_2)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage_non_convex_2) ++1 InvestmentFlowBlock_old(electricityBus_storage_non_convex_2) +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_1) ++1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_2) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_non_convex_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(storage_non_convex_electricityBus_0) ++1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_non_convex_electricityBus_1)_: +-1 InvestmentFlowBlock_invest(storage_non_convex_electricityBus_1) ++1 InvestmentFlowBlock_old(storage_non_convex_electricityBus_1) +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) ++1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_non_convex_electricityBus_2)_: +-1 InvestmentFlowBlock_invest(storage_non_convex_electricityBus_2) ++1 InvestmentFlowBlock_old(storage_non_convex_electricityBus_2) +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_1) ++1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage_non_convex_0)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage_non_convex_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage_non_convex_1)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage_non_convex_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage_non_convex_2)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage_non_convex_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage_non_convex_electricityBus_0)_: ++1 InvestmentFlowBlock_old_end(storage_non_convex_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage_non_convex_electricityBus_1)_: ++1 InvestmentFlowBlock_old_end(storage_non_convex_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage_non_convex_electricityBus_2)_: ++1 InvestmentFlowBlock_old_end(storage_non_convex_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage_non_convex_0)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage_non_convex_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage_non_convex_1)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage_non_convex_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage_non_convex_2)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage_non_convex_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage_non_convex_electricityBus_0)_: ++1 InvestmentFlowBlock_old_exo(storage_non_convex_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage_non_convex_electricityBus_1)_: ++1 InvestmentFlowBlock_old_exo(storage_non_convex_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage_non_convex_electricityBus_2)_: ++1 InvestmentFlowBlock_old_exo(storage_non_convex_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage_non_convex_0)_: ++1 InvestmentFlowBlock_old(electricityBus_storage_non_convex_0) +-1 InvestmentFlowBlock_old_end(electricityBus_storage_non_convex_0) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage_non_convex_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage_non_convex_1)_: ++1 InvestmentFlowBlock_old(electricityBus_storage_non_convex_1) +-1 InvestmentFlowBlock_old_end(electricityBus_storage_non_convex_1) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage_non_convex_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage_non_convex_2)_: ++1 InvestmentFlowBlock_old(electricityBus_storage_non_convex_2) +-1 InvestmentFlowBlock_old_end(electricityBus_storage_non_convex_2) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage_non_convex_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage_non_convex_electricityBus_0)_: ++1 InvestmentFlowBlock_old(storage_non_convex_electricityBus_0) +-1 InvestmentFlowBlock_old_end(storage_non_convex_electricityBus_0) +-1 InvestmentFlowBlock_old_exo(storage_non_convex_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage_non_convex_electricityBus_1)_: ++1 InvestmentFlowBlock_old(storage_non_convex_electricityBus_1) +-1 InvestmentFlowBlock_old_end(storage_non_convex_electricityBus_1) +-1 InvestmentFlowBlock_old_exo(storage_non_convex_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage_non_convex_electricityBus_2)_: ++1 InvestmentFlowBlock_old(storage_non_convex_electricityBus_2) +-1 InvestmentFlowBlock_old_end(storage_non_convex_electricityBus_2) +-1 InvestmentFlowBlock_old_exo(storage_non_convex_electricityBus_2) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_0_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) ++1 flow(electricityBus_storage_non_convex_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_0_1)_: +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) ++1 flow(electricityBus_storage_non_convex_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_1_2)_: +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_1) ++1 flow(electricityBus_storage_non_convex_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_1_3)_: +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_1) ++1 flow(electricityBus_storage_non_convex_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_2_4)_: +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_2) ++1 flow(electricityBus_storage_non_convex_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_2_5)_: +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_2) ++1 flow(electricityBus_storage_non_convex_2_5) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) ++1 flow(storage_non_convex_electricityBus_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) ++1 flow(storage_non_convex_electricityBus_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_1_2)_: +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_1) ++1 flow(storage_non_convex_electricityBus_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_1_3)_: +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_1) ++1 flow(storage_non_convex_electricityBus_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_2_4)_: +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_2) ++1 flow(storage_non_convex_electricityBus_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_2_5)_: +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_2) ++1 flow(storage_non_convex_electricityBus_2_5) +<= 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_non_convex_0)_: +-1 GenericInvestmentStorageBlock_invest(storage_non_convex_0) ++1 GenericInvestmentStorageBlock_total(storage_non_convex_0) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_non_convex_1)_: +-1 GenericInvestmentStorageBlock_invest(storage_non_convex_1) ++1 GenericInvestmentStorageBlock_old(storage_non_convex_1) +-1 GenericInvestmentStorageBlock_total(storage_non_convex_0) ++1 GenericInvestmentStorageBlock_total(storage_non_convex_1) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_non_convex_2)_: +-1 GenericInvestmentStorageBlock_invest(storage_non_convex_2) ++1 GenericInvestmentStorageBlock_old(storage_non_convex_2) +-1 GenericInvestmentStorageBlock_total(storage_non_convex_1) ++1 GenericInvestmentStorageBlock_total(storage_non_convex_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage_non_convex_0)_: ++1 GenericInvestmentStorageBlock_old_end(storage_non_convex_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage_non_convex_1)_: ++1 GenericInvestmentStorageBlock_old_end(storage_non_convex_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage_non_convex_2)_: ++1 GenericInvestmentStorageBlock_old_end(storage_non_convex_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage_non_convex_0)_: ++1 GenericInvestmentStorageBlock_old_exo(storage_non_convex_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage_non_convex_1)_: ++1 GenericInvestmentStorageBlock_old_exo(storage_non_convex_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage_non_convex_2)_: ++1 GenericInvestmentStorageBlock_old_exo(storage_non_convex_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage_non_convex_0)_: ++1 GenericInvestmentStorageBlock_old(storage_non_convex_0) +-1 GenericInvestmentStorageBlock_old_end(storage_non_convex_0) +-1 GenericInvestmentStorageBlock_old_exo(storage_non_convex_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage_non_convex_1)_: ++1 GenericInvestmentStorageBlock_old(storage_non_convex_1) +-1 GenericInvestmentStorageBlock_old_end(storage_non_convex_1) +-1 GenericInvestmentStorageBlock_old_exo(storage_non_convex_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage_non_convex_2)_: ++1 GenericInvestmentStorageBlock_old(storage_non_convex_2) +-1 GenericInvestmentStorageBlock_old_end(storage_non_convex_2) +-1 GenericInvestmentStorageBlock_old_exo(storage_non_convex_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_non_convex_0_1)_: +-0.87 GenericInvestmentStorageBlock_storage_content(storage_non_convex_0) ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) +-0.96999999999999997 flow(electricityBus_storage_non_convex_0_1) ++1.1627906976744187 flow(storage_non_convex_electricityBus_0_1) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_non_convex_1_2)_: +-0.87 GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) +-0.96999999999999997 flow(electricityBus_storage_non_convex_1_2) ++1.1627906976744187 flow(storage_non_convex_electricityBus_1_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_non_convex_1_3)_: +-0.87 GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_3) +-0.96999999999999997 flow(electricityBus_storage_non_convex_1_3) ++1.1627906976744187 flow(storage_non_convex_electricityBus_1_3) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_non_convex_2_4)_: +-0.87 GenericInvestmentStorageBlock_storage_content(storage_non_convex_3) ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_4) +-0.96999999999999997 flow(electricityBus_storage_non_convex_2_4) ++1.1627906976744187 flow(storage_non_convex_electricityBus_2_4) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_non_convex_2_5)_: +-0.87 GenericInvestmentStorageBlock_storage_content(storage_non_convex_4) ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_5) +-0.96999999999999997 flow(electricityBus_storage_non_convex_2_5) ++1.1627906976744187 flow(storage_non_convex_electricityBus_2_5) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_non_convex_0)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage_non_convex_0) ++1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_non_convex_1)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage_non_convex_1) ++1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_1) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_non_convex_2)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage_non_convex_2) ++1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_2) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_non_convex_0)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage_non_convex_0) ++1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_non_convex_1)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage_non_convex_1) ++1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_1) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_non_convex_2)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage_non_convex_2) ++1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_2) += 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_0_0)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_0) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage_non_convex_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_0_1)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage_non_convex_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_1_2)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage_non_convex_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_1_3)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_3) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage_non_convex_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_2_4)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_4) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage_non_convex_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_2_5)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_5) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage_non_convex_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_0_0)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_0) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage_non_convex_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_0_1)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage_non_convex_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_1_2)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage_non_convex_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_1_3)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_3) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage_non_convex_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_2_4)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_4) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage_non_convex_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_2_5)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_5) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage_non_convex_2) +<= 0 + +c_l_GenericInvestmentStorageBlock_limit_max(storage_non_convex_0)_: +-1 GenericInvestmentStorageBlock_invest(storage_non_convex_0) ++1454 GenericInvestmentStorageBlock_invest_status(storage_non_convex_0) +>= 0 + +c_l_GenericInvestmentStorageBlock_limit_max(storage_non_convex_1)_: +-1 GenericInvestmentStorageBlock_invest(storage_non_convex_1) ++1454 GenericInvestmentStorageBlock_invest_status(storage_non_convex_1) +>= 0 + +c_l_GenericInvestmentStorageBlock_limit_max(storage_non_convex_2)_: +-1 GenericInvestmentStorageBlock_invest(storage_non_convex_2) ++1454 GenericInvestmentStorageBlock_invest_status(storage_non_convex_2) +>= 0 + +c_l_GenericInvestmentStorageBlock_limit_min(storage_non_convex_0)_: ++1 GenericInvestmentStorageBlock_invest(storage_non_convex_0) +-19 GenericInvestmentStorageBlock_invest_status(storage_non_convex_0) +>= 0 + +c_l_GenericInvestmentStorageBlock_limit_min(storage_non_convex_1)_: ++1 GenericInvestmentStorageBlock_invest(storage_non_convex_1) +-19 GenericInvestmentStorageBlock_invest_status(storage_non_convex_1) +>= 0 + +c_l_GenericInvestmentStorageBlock_limit_min(storage_non_convex_2)_: ++1 GenericInvestmentStorageBlock_invest(storage_non_convex_2) +-19 GenericInvestmentStorageBlock_invest_status(storage_non_convex_2) +>= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_storage_non_convex_0_0) <= +inf + 0 <= flow(electricityBus_storage_non_convex_0_1) <= +inf + 0 <= flow(electricityBus_storage_non_convex_1_2) <= +inf + 0 <= flow(electricityBus_storage_non_convex_1_3) <= +inf + 0 <= flow(electricityBus_storage_non_convex_2_4) <= +inf + 0 <= flow(electricityBus_storage_non_convex_2_5) <= +inf + 0 <= flow(storage_non_convex_electricityBus_0_0) <= +inf + 0 <= flow(storage_non_convex_electricityBus_0_1) <= +inf + 0 <= flow(storage_non_convex_electricityBus_1_2) <= +inf + 0 <= flow(storage_non_convex_electricityBus_1_3) <= +inf + 0 <= flow(storage_non_convex_electricityBus_2_4) <= +inf + 0 <= flow(storage_non_convex_electricityBus_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage_non_convex_0) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage_non_convex_1) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage_non_convex_2) <= +inf + 0 <= InvestmentFlowBlock_invest(storage_non_convex_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage_non_convex_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_invest(storage_non_convex_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage_non_convex_1) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage_non_convex_2) <= +inf + 0 <= InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage_non_convex_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_total(storage_non_convex_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage_non_convex_0) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage_non_convex_1) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage_non_convex_2) <= +inf + 0 <= InvestmentFlowBlock_old(storage_non_convex_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old(storage_non_convex_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old(storage_non_convex_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage_non_convex_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage_non_convex_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage_non_convex_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage_non_convex_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage_non_convex_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage_non_convex_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage_non_convex_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage_non_convex_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage_non_convex_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage_non_convex_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage_non_convex_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage_non_convex_electricityBus_2) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_non_convex_0) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_non_convex_3) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_non_convex_4) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_non_convex_5) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage_non_convex_0) <= 1454 + 0 <= GenericInvestmentStorageBlock_invest(storage_non_convex_1) <= 1454 + 0 <= GenericInvestmentStorageBlock_invest(storage_non_convex_2) <= 1454 + 0 <= GenericInvestmentStorageBlock_total(storage_non_convex_0) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage_non_convex_1) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage_non_convex_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage_non_convex_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage_non_convex_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage_non_convex_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage_non_convex_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage_non_convex_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage_non_convex_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage_non_convex_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage_non_convex_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage_non_convex_2) <= +inf + 0 <= GenericInvestmentStorageBlock_invest_status(storage_non_convex_0) <= 1 + 0 <= GenericInvestmentStorageBlock_invest_status(storage_non_convex_1) <= 1 + 0 <= GenericInvestmentStorageBlock_invest_status(storage_non_convex_2) <= 1 +binary + GenericInvestmentStorageBlock_invest_status(storage_non_convex_0) + GenericInvestmentStorageBlock_invest_status(storage_non_convex_1) + GenericInvestmentStorageBlock_invest_status(storage_non_convex_2) +end diff --git a/tests/lp_files/storage_invest_without_offset_multi_period.lp b/tests/lp_files/storage_invest_without_offset_multi_period.lp new file mode 100644 index 000000000..87ca2f0f8 --- /dev/null +++ b/tests/lp_files/storage_invest_without_offset_multi_period.lp @@ -0,0 +1,525 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++172.46194511331876 GenericInvestmentStorageBlock_invest(storage_non_convex_0) ++169.08033834639093 GenericInvestmentStorageBlock_invest(storage_non_convex_1) ++165.76503759450091 GenericInvestmentStorageBlock_invest(storage_non_convex_2) ++56 flow(electricityBus_storage_non_convex_0_0) ++56 flow(electricityBus_storage_non_convex_0_1) ++54.901960784313722 flow(electricityBus_storage_non_convex_1_2) ++54.901960784313722 flow(electricityBus_storage_non_convex_1_3) ++53.825451749327179 flow(electricityBus_storage_non_convex_2_4) ++53.825451749327179 flow(electricityBus_storage_non_convex_2_5) ++24 flow(storage_non_convex_electricityBus_0_0) ++24 flow(storage_non_convex_electricityBus_0_1) ++23.52941176470588 flow(storage_non_convex_electricityBus_1_2) ++23.52941176470588 flow(storage_non_convex_electricityBus_1_3) ++23.068050749711649 flow(storage_non_convex_electricityBus_2_4) ++23.068050749711649 flow(storage_non_convex_electricityBus_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: +-1 flow(electricityBus_storage_non_convex_0_0) ++1 flow(storage_non_convex_electricityBus_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: +-1 flow(electricityBus_storage_non_convex_0_1) ++1 flow(storage_non_convex_electricityBus_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: +-1 flow(electricityBus_storage_non_convex_1_2) ++1 flow(storage_non_convex_electricityBus_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: +-1 flow(electricityBus_storage_non_convex_1_3) ++1 flow(storage_non_convex_electricityBus_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: +-1 flow(electricityBus_storage_non_convex_2_4) ++1 flow(storage_non_convex_electricityBus_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: +-1 flow(electricityBus_storage_non_convex_2_5) ++1 flow(storage_non_convex_electricityBus_2_5) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage_non_convex_0)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage_non_convex_0) ++1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage_non_convex_1)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage_non_convex_1) ++1 InvestmentFlowBlock_old(electricityBus_storage_non_convex_1) +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) ++1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(electricityBus_storage_non_convex_2)_: +-1 InvestmentFlowBlock_invest(electricityBus_storage_non_convex_2) ++1 InvestmentFlowBlock_old(electricityBus_storage_non_convex_2) +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_1) ++1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_2) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_non_convex_electricityBus_0)_: +-1 InvestmentFlowBlock_invest(storage_non_convex_electricityBus_0) ++1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_non_convex_electricityBus_1)_: +-1 InvestmentFlowBlock_invest(storage_non_convex_electricityBus_1) ++1 InvestmentFlowBlock_old(storage_non_convex_electricityBus_1) +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) ++1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_total_rule(storage_non_convex_electricityBus_2)_: +-1 InvestmentFlowBlock_invest(storage_non_convex_electricityBus_2) ++1 InvestmentFlowBlock_old(storage_non_convex_electricityBus_2) +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_1) ++1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage_non_convex_0)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage_non_convex_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage_non_convex_1)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage_non_convex_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(electricityBus_storage_non_convex_2)_: ++1 InvestmentFlowBlock_old_end(electricityBus_storage_non_convex_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage_non_convex_electricityBus_0)_: ++1 InvestmentFlowBlock_old_end(storage_non_convex_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage_non_convex_electricityBus_1)_: ++1 InvestmentFlowBlock_old_end(storage_non_convex_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_end(storage_non_convex_electricityBus_2)_: ++1 InvestmentFlowBlock_old_end(storage_non_convex_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage_non_convex_0)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage_non_convex_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage_non_convex_1)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage_non_convex_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(electricityBus_storage_non_convex_2)_: ++1 InvestmentFlowBlock_old_exo(electricityBus_storage_non_convex_2) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage_non_convex_electricityBus_0)_: ++1 InvestmentFlowBlock_old_exo(storage_non_convex_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage_non_convex_electricityBus_1)_: ++1 InvestmentFlowBlock_old_exo(storage_non_convex_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule_exo(storage_non_convex_electricityBus_2)_: ++1 InvestmentFlowBlock_old_exo(storage_non_convex_electricityBus_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage_non_convex_0)_: ++1 InvestmentFlowBlock_old(electricityBus_storage_non_convex_0) +-1 InvestmentFlowBlock_old_end(electricityBus_storage_non_convex_0) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage_non_convex_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage_non_convex_1)_: ++1 InvestmentFlowBlock_old(electricityBus_storage_non_convex_1) +-1 InvestmentFlowBlock_old_end(electricityBus_storage_non_convex_1) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage_non_convex_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(electricityBus_storage_non_convex_2)_: ++1 InvestmentFlowBlock_old(electricityBus_storage_non_convex_2) +-1 InvestmentFlowBlock_old_end(electricityBus_storage_non_convex_2) +-1 InvestmentFlowBlock_old_exo(electricityBus_storage_non_convex_2) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage_non_convex_electricityBus_0)_: ++1 InvestmentFlowBlock_old(storage_non_convex_electricityBus_0) +-1 InvestmentFlowBlock_old_end(storage_non_convex_electricityBus_0) +-1 InvestmentFlowBlock_old_exo(storage_non_convex_electricityBus_0) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage_non_convex_electricityBus_1)_: ++1 InvestmentFlowBlock_old(storage_non_convex_electricityBus_1) +-1 InvestmentFlowBlock_old_end(storage_non_convex_electricityBus_1) +-1 InvestmentFlowBlock_old_exo(storage_non_convex_electricityBus_1) += 0 + +c_e_InvestmentFlowBlock_old_rule(storage_non_convex_electricityBus_2)_: ++1 InvestmentFlowBlock_old(storage_non_convex_electricityBus_2) +-1 InvestmentFlowBlock_old_end(storage_non_convex_electricityBus_2) +-1 InvestmentFlowBlock_old_exo(storage_non_convex_electricityBus_2) += 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_0_0)_: +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) ++1 flow(electricityBus_storage_non_convex_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_0_1)_: +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) ++1 flow(electricityBus_storage_non_convex_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_1_2)_: +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_1) ++1 flow(electricityBus_storage_non_convex_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_1_3)_: +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_1) ++1 flow(electricityBus_storage_non_convex_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_2_4)_: +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_2) ++1 flow(electricityBus_storage_non_convex_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(electricityBus_storage_non_convex_2_5)_: +-1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_2) ++1 flow(electricityBus_storage_non_convex_2_5) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_0_0)_: +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) ++1 flow(storage_non_convex_electricityBus_0_0) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_0_1)_: +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) ++1 flow(storage_non_convex_electricityBus_0_1) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_1_2)_: +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_1) ++1 flow(storage_non_convex_electricityBus_1_2) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_1_3)_: +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_1) ++1 flow(storage_non_convex_electricityBus_1_3) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_2_4)_: +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_2) ++1 flow(storage_non_convex_electricityBus_2_4) +<= 0 + +c_u_InvestmentFlowBlock_max(storage_non_convex_electricityBus_2_5)_: +-1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_2) ++1 flow(storage_non_convex_electricityBus_2_5) +<= 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_non_convex_0)_: +-1 GenericInvestmentStorageBlock_invest(storage_non_convex_0) ++1 GenericInvestmentStorageBlock_total(storage_non_convex_0) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_non_convex_1)_: +-1 GenericInvestmentStorageBlock_invest(storage_non_convex_1) ++1 GenericInvestmentStorageBlock_old(storage_non_convex_1) +-1 GenericInvestmentStorageBlock_total(storage_non_convex_0) ++1 GenericInvestmentStorageBlock_total(storage_non_convex_1) += 0 + +c_e_GenericInvestmentStorageBlock_total_storage_rule(storage_non_convex_2)_: +-1 GenericInvestmentStorageBlock_invest(storage_non_convex_2) ++1 GenericInvestmentStorageBlock_old(storage_non_convex_2) +-1 GenericInvestmentStorageBlock_total(storage_non_convex_1) ++1 GenericInvestmentStorageBlock_total(storage_non_convex_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage_non_convex_0)_: ++1 GenericInvestmentStorageBlock_old_end(storage_non_convex_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage_non_convex_1)_: ++1 GenericInvestmentStorageBlock_old_end(storage_non_convex_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_end(storage_non_convex_2)_: ++1 GenericInvestmentStorageBlock_old_end(storage_non_convex_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage_non_convex_0)_: ++1 GenericInvestmentStorageBlock_old_exo(storage_non_convex_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage_non_convex_1)_: ++1 GenericInvestmentStorageBlock_old_exo(storage_non_convex_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule_exo(storage_non_convex_2)_: ++1 GenericInvestmentStorageBlock_old_exo(storage_non_convex_2) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage_non_convex_0)_: ++1 GenericInvestmentStorageBlock_old(storage_non_convex_0) +-1 GenericInvestmentStorageBlock_old_end(storage_non_convex_0) +-1 GenericInvestmentStorageBlock_old_exo(storage_non_convex_0) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage_non_convex_1)_: ++1 GenericInvestmentStorageBlock_old(storage_non_convex_1) +-1 GenericInvestmentStorageBlock_old_end(storage_non_convex_1) +-1 GenericInvestmentStorageBlock_old_exo(storage_non_convex_1) += 0 + +c_e_GenericInvestmentStorageBlock_old_rule(storage_non_convex_2)_: ++1 GenericInvestmentStorageBlock_old(storage_non_convex_2) +-1 GenericInvestmentStorageBlock_old_end(storage_non_convex_2) +-1 GenericInvestmentStorageBlock_old_exo(storage_non_convex_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_non_convex_0_1)_: +-0.87 GenericInvestmentStorageBlock_storage_content(storage_non_convex_0) ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) +-0.96999999999999997 flow(electricityBus_storage_non_convex_0_1) ++1.1627906976744187 flow(storage_non_convex_electricityBus_0_1) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_non_convex_1_2)_: +-0.87 GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) +-0.96999999999999997 flow(electricityBus_storage_non_convex_1_2) ++1.1627906976744187 flow(storage_non_convex_electricityBus_1_2) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_non_convex_1_3)_: +-0.87 GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_3) +-0.96999999999999997 flow(electricityBus_storage_non_convex_1_3) ++1.1627906976744187 flow(storage_non_convex_electricityBus_1_3) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_non_convex_2_4)_: +-0.87 GenericInvestmentStorageBlock_storage_content(storage_non_convex_3) ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_4) +-0.96999999999999997 flow(electricityBus_storage_non_convex_2_4) ++1.1627906976744187 flow(storage_non_convex_electricityBus_2_4) += 0 + +c_e_GenericInvestmentStorageBlock_balance(storage_non_convex_2_5)_: +-0.87 GenericInvestmentStorageBlock_storage_content(storage_non_convex_4) ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_5) +-0.96999999999999997 flow(electricityBus_storage_non_convex_2_5) ++1.1627906976744187 flow(storage_non_convex_electricityBus_2_5) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_non_convex_0)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage_non_convex_0) ++1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_non_convex_1)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage_non_convex_1) ++1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_1) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_inflow(storage_non_convex_2)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage_non_convex_2) ++1 InvestmentFlowBlock_total(electricityBus_storage_non_convex_2) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_non_convex_0)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage_non_convex_0) ++1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_non_convex_1)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage_non_convex_1) ++1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_1) += 0 + +c_e_GenericInvestmentStorageBlock_storage_capacity_outflow(storage_non_convex_2)_: +-0.16666666666666666 GenericInvestmentStorageBlock_total(storage_non_convex_2) ++1 InvestmentFlowBlock_total(storage_non_convex_electricityBus_2) += 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_0_0)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_0) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage_non_convex_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_0_1)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage_non_convex_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_1_2)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage_non_convex_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_1_3)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_3) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage_non_convex_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_2_4)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_4) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage_non_convex_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_max_storage_content(storage_non_convex_2_5)_: ++1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_5) +-0.90000000000000002 GenericInvestmentStorageBlock_total(storage_non_convex_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_0_0)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_0) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage_non_convex_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_0_1)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage_non_convex_0) +<= 0 + +c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_1_2)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage_non_convex_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_1_3)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_3) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage_non_convex_1) +<= 0 + +c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_2_4)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_4) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage_non_convex_2) +<= 0 + +c_u_GenericInvestmentStorageBlock_min_storage_content(storage_non_convex_2_5)_: +-1 GenericInvestmentStorageBlock_storage_content(storage_non_convex_5) ++0.10000000000000001 GenericInvestmentStorageBlock_total(storage_non_convex_2) +<= 0 + +c_l_GenericInvestmentStorageBlock_limit_max(storage_non_convex_0)_: +-1 GenericInvestmentStorageBlock_invest(storage_non_convex_0) ++244 GenericInvestmentStorageBlock_invest_status(storage_non_convex_0) +>= 0 + +c_l_GenericInvestmentStorageBlock_limit_max(storage_non_convex_1)_: +-1 GenericInvestmentStorageBlock_invest(storage_non_convex_1) ++244 GenericInvestmentStorageBlock_invest_status(storage_non_convex_1) +>= 0 + +c_l_GenericInvestmentStorageBlock_limit_max(storage_non_convex_2)_: +-1 GenericInvestmentStorageBlock_invest(storage_non_convex_2) ++244 GenericInvestmentStorageBlock_invest_status(storage_non_convex_2) +>= 0 + +c_l_GenericInvestmentStorageBlock_limit_min(storage_non_convex_0)_: ++1 GenericInvestmentStorageBlock_invest(storage_non_convex_0) +-12 GenericInvestmentStorageBlock_invest_status(storage_non_convex_0) +>= 0 + +c_l_GenericInvestmentStorageBlock_limit_min(storage_non_convex_1)_: ++1 GenericInvestmentStorageBlock_invest(storage_non_convex_1) +-12 GenericInvestmentStorageBlock_invest_status(storage_non_convex_1) +>= 0 + +c_l_GenericInvestmentStorageBlock_limit_min(storage_non_convex_2)_: ++1 GenericInvestmentStorageBlock_invest(storage_non_convex_2) +-12 GenericInvestmentStorageBlock_invest_status(storage_non_convex_2) +>= 0 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_storage_non_convex_0_0) <= +inf + 0 <= flow(electricityBus_storage_non_convex_0_1) <= +inf + 0 <= flow(electricityBus_storage_non_convex_1_2) <= +inf + 0 <= flow(electricityBus_storage_non_convex_1_3) <= +inf + 0 <= flow(electricityBus_storage_non_convex_2_4) <= +inf + 0 <= flow(electricityBus_storage_non_convex_2_5) <= +inf + 0 <= flow(storage_non_convex_electricityBus_0_0) <= +inf + 0 <= flow(storage_non_convex_electricityBus_0_1) <= +inf + 0 <= flow(storage_non_convex_electricityBus_1_2) <= +inf + 0 <= flow(storage_non_convex_electricityBus_1_3) <= +inf + 0 <= flow(storage_non_convex_electricityBus_2_4) <= +inf + 0 <= flow(storage_non_convex_electricityBus_2_5) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage_non_convex_0) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage_non_convex_1) <= +inf + 0 <= InvestmentFlowBlock_invest(electricityBus_storage_non_convex_2) <= +inf + 0 <= InvestmentFlowBlock_invest(storage_non_convex_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_invest(storage_non_convex_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_invest(storage_non_convex_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage_non_convex_0) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage_non_convex_1) <= +inf + 0 <= InvestmentFlowBlock_total(electricityBus_storage_non_convex_2) <= +inf + 0 <= InvestmentFlowBlock_total(storage_non_convex_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_total(storage_non_convex_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_total(storage_non_convex_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage_non_convex_0) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage_non_convex_1) <= +inf + 0 <= InvestmentFlowBlock_old(electricityBus_storage_non_convex_2) <= +inf + 0 <= InvestmentFlowBlock_old(storage_non_convex_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old(storage_non_convex_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old(storage_non_convex_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage_non_convex_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage_non_convex_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(electricityBus_storage_non_convex_2) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage_non_convex_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage_non_convex_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_end(storage_non_convex_electricityBus_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage_non_convex_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage_non_convex_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(electricityBus_storage_non_convex_2) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage_non_convex_electricityBus_0) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage_non_convex_electricityBus_1) <= +inf + 0 <= InvestmentFlowBlock_old_exo(storage_non_convex_electricityBus_2) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_non_convex_0) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_non_convex_1) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_non_convex_2) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_non_convex_3) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_non_convex_4) <= +inf + 0 <= GenericInvestmentStorageBlock_storage_content(storage_non_convex_5) <= +inf + 0 <= GenericInvestmentStorageBlock_invest(storage_non_convex_0) <= 244 + 0 <= GenericInvestmentStorageBlock_invest(storage_non_convex_1) <= 244 + 0 <= GenericInvestmentStorageBlock_invest(storage_non_convex_2) <= 244 + 0 <= GenericInvestmentStorageBlock_total(storage_non_convex_0) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage_non_convex_1) <= +inf + 0 <= GenericInvestmentStorageBlock_total(storage_non_convex_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage_non_convex_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage_non_convex_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old(storage_non_convex_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage_non_convex_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage_non_convex_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_end(storage_non_convex_2) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage_non_convex_0) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage_non_convex_1) <= +inf + 0 <= GenericInvestmentStorageBlock_old_exo(storage_non_convex_2) <= +inf + 0 <= GenericInvestmentStorageBlock_invest_status(storage_non_convex_0) <= 1 + 0 <= GenericInvestmentStorageBlock_invest_status(storage_non_convex_1) <= 1 + 0 <= GenericInvestmentStorageBlock_invest_status(storage_non_convex_2) <= 1 +binary + GenericInvestmentStorageBlock_invest_status(storage_non_convex_0) + GenericInvestmentStorageBlock_invest_status(storage_non_convex_1) + GenericInvestmentStorageBlock_invest_status(storage_non_convex_2) +end diff --git a/tests/lp_files/summed_min_source.lp b/tests/lp_files/summed_min_source.lp new file mode 100644 index 000000000..425bbdb52 --- /dev/null +++ b/tests/lp_files/summed_min_source.lp @@ -0,0 +1,42 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++25 flow(electricityBus_excess_0_0) ++25 flow(electricityBus_excess_0_1) ++25 flow(electricityBus_excess_0_2) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(electricityBus_excess_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(electricityBus_excess_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_0_2)_: ++1 flow(electricityBus_excess_0_2) += 0 + +c_u_FlowBlock_summed_max(electricityBus_excess)_: ++1 flow(electricityBus_excess_0_0) ++1 flow(electricityBus_excess_0_1) ++1 flow(electricityBus_excess_0_2) +<= 1000 + +c_l_FlowBlock_summed_min(electricityBus_excess)_: ++1 flow(electricityBus_excess_0_0) ++1 flow(electricityBus_excess_0_1) ++1 flow(electricityBus_excess_0_2) +>= 30 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_excess_0_0) <= 8 + 0 <= flow(electricityBus_excess_0_1) <= 8 + 0 <= flow(electricityBus_excess_0_2) <= 8 +end diff --git a/tests/lp_files/summed_min_source_multi_period.lp b/tests/lp_files/summed_min_source_multi_period.lp new file mode 100644 index 000000000..a8419053d --- /dev/null +++ b/tests/lp_files/summed_min_source_multi_period.lp @@ -0,0 +1,66 @@ +\* Source Pyomo model name=Model *\ + +min +objective: ++25 flow(electricityBus_excess_0_0) ++25 flow(electricityBus_excess_0_1) ++24.509803921568626 flow(electricityBus_excess_1_2) ++24.509803921568626 flow(electricityBus_excess_1_3) ++24.029219530949632 flow(electricityBus_excess_2_4) ++24.029219530949632 flow(electricityBus_excess_2_5) + +s.t. + +c_e_BusBlock_balance(electricityBus_0_0)_: ++1 flow(electricityBus_excess_0_0) += 0 + +c_e_BusBlock_balance(electricityBus_0_1)_: ++1 flow(electricityBus_excess_0_1) += 0 + +c_e_BusBlock_balance(electricityBus_1_2)_: ++1 flow(electricityBus_excess_1_2) += 0 + +c_e_BusBlock_balance(electricityBus_1_3)_: ++1 flow(electricityBus_excess_1_3) += 0 + +c_e_BusBlock_balance(electricityBus_2_4)_: ++1 flow(electricityBus_excess_2_4) += 0 + +c_e_BusBlock_balance(electricityBus_2_5)_: ++1 flow(electricityBus_excess_2_5) += 0 + +c_u_FlowBlock_summed_max(electricityBus_excess)_: ++1 flow(electricityBus_excess_0_0) ++1 flow(electricityBus_excess_0_1) ++1 flow(electricityBus_excess_1_2) ++1 flow(electricityBus_excess_1_3) ++1 flow(electricityBus_excess_2_4) ++1 flow(electricityBus_excess_2_5) +<= 1000 + +c_l_FlowBlock_summed_min(electricityBus_excess)_: ++1 flow(electricityBus_excess_0_0) ++1 flow(electricityBus_excess_0_1) ++1 flow(electricityBus_excess_1_2) ++1 flow(electricityBus_excess_1_3) ++1 flow(electricityBus_excess_2_4) ++1 flow(electricityBus_excess_2_5) +>= 30 + +c_e_ONE_VAR_CONSTANT: +ONE_VAR_CONSTANT = 1.0 + +bounds + 0 <= flow(electricityBus_excess_0_0) <= 8 + 0 <= flow(electricityBus_excess_0_1) <= 8 + 0 <= flow(electricityBus_excess_1_2) <= 8 + 0 <= flow(electricityBus_excess_1_3) <= 8 + 0 <= flow(electricityBus_excess_2_4) <= 8 + 0 <= flow(electricityBus_excess_2_5) <= 8 +end diff --git a/tests/multi_period_constraint_tests.py b/tests/multi_period_constraint_tests.py index 7fb8a1b9f..20d3baed8 100644 --- a/tests/multi_period_constraint_tests.py +++ b/tests/multi_period_constraint_tests.py @@ -546,6 +546,31 @@ def test_storage_minimum_invest(self): self.compare_lp_files("storage_invest_minimum_multi_period.lp") + def test_storage_invest_multi_period(self): + """Test multi-period attributes such as age, fixed_costs, ...""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.GenericStorage( + label="storage1", + inputs={bel: solph.flows.Flow()}, + outputs={bel: solph.flows.Flow()}, + investment=solph.Investment( + ep_costs=145, + minimum=100, + maximum=200, + lifetime=40, + existing=50, + age=39, + overall_minimum=10, + overall_maximum=500, + fixed_costs=5, + ), + lifetime_inflow=40, + lifetime_outflow=40, + ) + + self.compare_lp_files("storage_invest_multi_period.lp") + def test_storage_unbalanced(self): """Testing a unbalanced storage (e.g. battery).""" bel = solph.buses.Bus(label="electricityBus") @@ -662,6 +687,40 @@ def test_storage_invest_1_initial_storage_level(self): self.get_om() assert msg in str(w[1].message) + def test_storage_invest_1_missing_lifetime(self): + """Test error thrown if storage misses necessary lifetime""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.GenericStorage( + label="storage1", + inputs={bel: solph.flows.Flow(variable_costs=56)}, + outputs={bel: solph.flows.Flow(variable_costs=24)}, + nominal_storage_capacity=None, + loss_rate=0.13, + max_storage_level=0.9, + min_storage_level=0.1, + invest_relation_input_capacity=1 / 6, + invest_relation_output_capacity=1 / 6, + inflow_conversion_factor=0.97, + outflow_conversion_factor=0.86, + lifetime_inflow=40, + lifetime_outflow=40, + investment=solph.Investment( + ep_costs=145, + maximum=234, + interest_rate=0.05, + overall_maximum=1000, + overall_minimum=2, + ), + ) + + msg = ( + "You have to specify a lifetime " + "for an InvestmentFlow in a multi-period model!" + ) + with pytest.raises(ValueError, match=msg): + self.get_om() + def test_transformer(self): """Constraint test of a LinearN1Transformer without Investment.""" bgas = solph.buses.Bus(label="gasBus") @@ -1232,6 +1291,32 @@ def test_periodical_investment_limit_with_dsm3(self): "periodical_investment_limit_with_dsm_oemof.lp", my_om=om ) + def test_periodical_investment_limit_missing(self): + """Testing the investment_limit function in the constraint module.""" + bus1 = solph.buses.Bus(label="Bus1") + solph.components.GenericStorage( + label="storage_invest_limit", + invest_relation_input_capacity=0.2, + invest_relation_output_capacity=0.2, + inputs={bus1: solph.flows.Flow()}, + outputs={bus1: solph.flows.Flow()}, + lifetime_inflow=20, + lifetime_outflow=20, + investment=solph.Investment(ep_costs=145, lifetime=30), + ) + solph.components.Source( + label="Source", + outputs={ + bus1: solph.flows.Flow( + investment=solph.Investment(ep_costs=123, lifetime=100) + ) + }, + ) + om = self.get_om() + msg = "You have to provide an investment limit for each period!" + with pytest.raises(ValueError, match=msg): + solph.constraints.investment_limit_per_period(om, limit=None) + def test_min_max_runtime(self): """Testing min and max runtimes for nonconvex flows.""" bus_t = solph.buses.Bus(label="Bus_T") @@ -1376,3 +1461,531 @@ def test_offsettransformer(self): ) self.compare_lp_files("offsettransformer_multi_period.lp") + + def test_dsm_module_DIW(self): + """Constraint test of SinkDSM with approach=DLR""" + + b_elec = solph.buses.Bus(label="bus_elec") + solph.components.experimental.SinkDSM( + label="demand_dsm", + inputs={b_elec: solph.flows.Flow()}, + demand=[1] * 6, + capacity_up=[0.5] * 6, + capacity_down=[0.5] * 6, + approach="DIW", + max_demand=1, + max_capacity_up=1, + max_capacity_down=1, + delay_time=1, + cost_dsm_down_shift=2, + shed_eligibility=False, + ) + self.compare_lp_files("dsm_module_DIW_multi_period.lp") + + def test_dsm_module_DLR(self): + """Constraint test of SinkDSM with approach=DLR""" + + b_elec = solph.buses.Bus(label="bus_elec") + solph.components.experimental.SinkDSM( + label="demand_dsm", + inputs={b_elec: solph.flows.Flow()}, + demand=[1] * 6, + capacity_up=[0.5] * 6, + capacity_down=[0.5] * 6, + approach="DLR", + max_demand=1, + max_capacity_up=1, + max_capacity_down=1, + delay_time=2, + shift_time=1, + cost_dsm_down_shift=2, + shed_eligibility=False, + ) + self.compare_lp_files("dsm_module_DLR_multi_period.lp") + + def test_dsm_module_oemof(self): + """Constraint test of SinkDSM with approach=oemof""" + + b_elec = solph.buses.Bus(label="bus_elec") + solph.components.experimental.SinkDSM( + label="demand_dsm", + inputs={b_elec: solph.flows.Flow()}, + demand=[1] * 6, + capacity_up=[0.5, 0.4, 0.5, 0.3, 0.3, 0.3], + capacity_down=[0.5, 0.4, 0.5, 0.3, 0.3, 0.3], + approach="oemof", + max_demand=1, + max_capacity_up=1, + max_capacity_down=1, + shift_interval=2, + cost_dsm_down_shift=2, + shed_eligibility=False, + ) + self.compare_lp_files("dsm_module_oemof_multi_period.lp") + + def test_dsm_module_DIW_extended(self): + """Constraint test of SinkDSM with approach=DLR + + Test all possible parameters and constraints + """ + + b_elec = solph.buses.Bus(label="bus_elec") + solph.components.experimental.SinkDSM( + label="demand_dsm", + inputs={b_elec: solph.flows.Flow()}, + demand=[1, 0.9, 0.8, 0.7, 0.7, 0.7], + capacity_up=[0.5, 0.4, 0.5, 0.3, 0.3, 0.3], + capacity_down=[0.3, 0.3, 0.4, 0.3, 0.3, 0.3], + approach="DIW", + max_demand=1, + max_capacity_up=1, + max_capacity_down=1, + delay_time=1, + cost_dsm_down_shift=1, + cost_dsm_up=1, + cost_dsm_down_shed=100, + efficiency=0.99, + recovery_time_shift=2, + recovery_time_shed=2, + shed_time=2, + ) + self.compare_lp_files("dsm_module_DIW_extended_multi_period.lp") + + def test_dsm_module_DLR_extended(self): + """Constraint test of SinkDSM with approach=DLR""" + + b_elec = solph.buses.Bus(label="bus_elec") + solph.components.experimental.SinkDSM( + label="demand_dsm", + inputs={b_elec: solph.flows.Flow()}, + demand=[1, 0.9, 0.8, 0.7, 0.7, 0.7], + capacity_up=[0.5, 0.4, 0.5, 0.3, 0.3, 0.3], + capacity_down=[0.3, 0.3, 0.4, 0.3, 0.3, 0.3], + approach="DLR", + max_demand=1, + max_capacity_up=1, + max_capacity_down=1, + delay_time=2, + shift_time=1, + cost_dsm_down_shift=1, + cost_dsm_up=1, + cost_dsm_down_shed=100, + efficiency=0.99, + recovery_time_shed=2, + ActivateYearLimit=True, + ActivateDayLimit=True, + n_yearLimit_shift=100, + n_yearLimit_shed=50, + t_dayLimit=3, + addition=False, + fixes=False, + shed_time=2, + ) + self.compare_lp_files("dsm_module_DLR_extended_multi_period.lp") + + def test_dsm_module_oemof_extended(self): + """Constraint test of SinkDSM with approach=oemof""" + + b_elec = solph.buses.Bus(label="bus_elec") + solph.components.experimental.SinkDSM( + label="demand_dsm", + inputs={b_elec: solph.flows.Flow()}, + demand=[1, 0.9, 0.8, 0.7, 0.7, 0.7], + capacity_up=[0.5, 0.4, 0.5, 0.3, 0.3, 0.3], + capacity_down=[0.3, 0.3, 0.4, 0.3, 0.3, 0.3], + approach="oemof", + shift_interval=2, + max_demand=1, + max_capacity_up=1, + max_capacity_down=1, + delay_time=2, + cost_dsm_down_shift=1, + cost_dsm_up=1, + cost_dsm_down_shed=100, + efficiency=0.99, + recovery_time_shed=2, + shed_time=2, + ) + self.compare_lp_files("dsm_module_oemof_extended_multi_period.lp") + + def test_dsm_module_DIW_invest(self): + """Constraint test of SinkDSM with approach=DLR and investments""" + + b_elec = solph.buses.Bus(label="bus_elec") + solph.components.experimental.SinkDSM( + label="demand_dsm", + inputs={b_elec: solph.flows.Flow()}, + demand=[1] * 6, + capacity_up=[0.5] * 6, + capacity_down=[0.5] * 6, + approach="DIW", + flex_share_up=1, + flex_share_down=1, + delay_time=1, + cost_dsm_down_shift=1, + cost_dsm_up=1, + cost_dsm_down_shed=100, + shed_eligibility=True, + recovery_time_shed=2, + shed_time=2, + investment=solph.Investment( + ep_costs=100, + existing=50, + minimum=33, + maximum=100, + age=1, + lifetime=20, + fixed_costs=20, + overall_maximum=1000, + overall_minimum=5, + ), + ) + self.compare_lp_files("dsm_module_DIW_invest_multi_period.lp") + + def test_dsm_module_DLR_invest(self): + """Constraint test of SinkDSM with approach=DLR and investments""" + + b_elec = solph.buses.Bus(label="bus_elec") + solph.components.experimental.SinkDSM( + label="demand_dsm", + inputs={b_elec: solph.flows.Flow()}, + demand=[1] * 6, + capacity_up=[0.5] * 6, + capacity_down=[0.5] * 6, + approach="DLR", + flex_share_up=1, + flex_share_down=1, + delay_time=2, + shift_time=1, + cost_dsm_down_shift=1, + cost_dsm_up=1, + cost_dsm_down_shed=100, + shed_eligibility=True, + recovery_time_shed=2, + shed_time=2, + n_yearLimit_shed=50, + investment=solph.Investment( + ep_costs=100, + existing=50, + minimum=33, + maximum=100, + age=1, + lifetime=20, + fixed_costs=20, + overall_maximum=1000, + overall_minimum=5, + ), + ) + self.compare_lp_files("dsm_module_DLR_invest_multi_period.lp") + + def test_dsm_module_oemof_invest(self): + """Constraint test of SinkDSM with approach=oemof and investments""" + + b_elec = solph.buses.Bus(label="bus_elec") + solph.components.experimental.SinkDSM( + label="demand_dsm", + inputs={b_elec: solph.flows.Flow()}, + demand=[1] * 6, + capacity_up=[0.5, 0.4, 0.5, 0.3, 0.3, 0.3], + capacity_down=[0.5, 0.4, 0.5, 0.3, 0.3, 0.3], + approach="oemof", + flex_share_up=0.9, + flex_share_down=0.9, + shift_interval=2, + cost_dsm_down_shift=1, + cost_dsm_up=1, + cost_dsm_down_shed=100, + shed_eligibility=True, + recovery_time_shed=2, + shed_time=2, + investment=solph.Investment( + ep_costs=100, + existing=50, + minimum=33, + maximum=100, + age=1, + lifetime=20, + fixed_costs=20, + overall_maximum=1000, + overall_minimum=5, + ), + ) + self.compare_lp_files("dsm_module_oemof_invest_multi_period.lp") + + def test_nonconvex_investment_storage_without_offset(self): + """All invest variables are coupled. The invest variables of the Flows + will be created during the initialisation of the storage e.g. battery + """ + bel = solph.buses.Bus(label="electricityBus") + + solph.components.GenericStorage( + label="storage_non_convex", + inputs={bel: solph.flows.Flow(variable_costs=56)}, + outputs={bel: solph.flows.Flow(variable_costs=24)}, + nominal_storage_capacity=None, + loss_rate=0.13, + max_storage_level=0.9, + min_storage_level=0.1, + invest_relation_input_capacity=1 / 6, + invest_relation_output_capacity=1 / 6, + inflow_conversion_factor=0.97, + outflow_conversion_factor=0.86, + lifetime_inflow=20, + lifetime_outflow=20, + investment=solph.Investment( + ep_costs=141, + maximum=244, + minimum=12, + nonconvex=True, + lifetime=20, + ), + ) + + self.compare_lp_files("storage_invest_without_offset_multi_period.lp") + + def test_nonconvex_investment_storage_with_offset(self): + """All invest variables are coupled. The invest variables of the Flows + will be created during the initialisation of the storage e.g. battery + """ + bel = solph.buses.Bus(label="electricityBus") + + solph.components.GenericStorage( + label="storage_non_convex", + inputs={bel: solph.flows.Flow(variable_costs=56)}, + outputs={bel: solph.flows.Flow(variable_costs=24)}, + nominal_storage_capacity=None, + loss_rate=0.13, + max_storage_level=0.9, + min_storage_level=0.1, + invest_relation_input_capacity=1 / 6, + invest_relation_output_capacity=1 / 6, + inflow_conversion_factor=0.97, + outflow_conversion_factor=0.86, + lifetime_inflow=20, + lifetime_outflow=20, + investment=solph.Investment( + ep_costs=145, + minimum=19, + offset=5, + nonconvex=True, + maximum=1454, + lifetime=20, + ), + ) + + self.compare_lp_files("storage_invest_with_offset_multi_period.lp") + + def test_nonconvex_invest_storage_all_nonconvex(self): + """All invest variables are free and nonconvex.""" + b1 = solph.buses.Bus(label="bus1") + + solph.components.GenericStorage( + label="storage_all_nonconvex", + inputs={ + b1: solph.flows.Flow( + investment=solph.Investment( + nonconvex=True, + minimum=5, + offset=10, + maximum=30, + ep_costs=10, + lifetime=20, + ) + ) + }, + outputs={ + b1: solph.flows.Flow( + investment=solph.Investment( + nonconvex=True, + minimum=8, + offset=15, + ep_costs=10, + maximum=20, + lifetime=20, + ) + ) + }, + investment=solph.Investment( + nonconvex=True, + ep_costs=20, + offset=30, + minimum=20, + maximum=100, + lifetime=20, + ), + ) + + self.compare_lp_files("storage_invest_all_nonconvex_multi_period.lp") + + def test_nonconvex_invest_sink_without_offset(self): + """Non convex invest flow without offset, with minimum.""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Sink( + label="sink_nonconvex_invest", + inputs={ + bel: solph.flows.Flow( + summed_max=2.3, + variable_costs=25, + max=0.8, + investment=solph.Investment( + ep_costs=500, + minimum=15, + nonconvex=True, + maximum=172, + lifetime=20, + ), + ) + }, + ) + self.compare_lp_files("flow_invest_without_offset_multi_period.lp") + + def test_nonconvex_invest_source_with_offset(self): + """Non convex invest flow with offset, with minimum.""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Source( + label="source_nonconvex_invest", + inputs={ + bel: solph.flows.Flow( + summed_max=2.3, + variable_costs=25, + max=0.8, + investment=solph.Investment( + ep_costs=500, + minimum=15, + maximum=20, + offset=34, + nonconvex=True, + lifetime=20, + ), + ) + }, + ) + self.compare_lp_files("flow_invest_with_offset_multi_period.lp") + + def test_nonconvex_invest_source_with_offset_no_minimum(self): + """Non convex invest flow with offset, without minimum.""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Source( + label="source_nonconvex_invest", + inputs={ + bel: solph.flows.Flow( + summed_max=2.3, + variable_costs=25, + max=0.8, + investment=solph.Investment( + ep_costs=500, + maximum=1234, + offset=34, + nonconvex=True, + lifetime=20, + ), + ) + }, + ) + self.compare_lp_files( + "flow_invest_with_offset_no_minimum_multi_period.lp" + ) + + def test_summed_min_max_source(self): + """Test sink with summed_min and summed_max attribute""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Sink( + label="excess", + inputs={ + bel: solph.flows.Flow( + summed_min=3, + summed_max=100, + variable_costs=25, + max=0.8, + nominal_value=10 + ) + }, + ) + + self.compare_lp_files("summed_min_source_multi_period.lp") + + def test_flow_reaching_lifetime(self): + """Test flow forced to zero once exceeding its lifetime""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Sink( + label="excess", + inputs={ + bel: solph.flows.Flow( + variable_costs=25, + max=0.8, + nominal_value=10, + lifetime=2 + ) + }, + ) + + self.compare_lp_files("flow_reaching_lifetime.lp") + + def test_flow_reaching_lifetime_initial_age(self): + """Test flow forced to zero once exceeding its lifetime with age""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Sink( + label="excess", + inputs={ + bel: solph.flows.Flow( + variable_costs=25, + max=0.8, + nominal_value=10, + lifetime=2, + age=1 + ) + }, + ) + + self.compare_lp_files("flow_reaching_lifetime_initial_age.lp") + + def test_fixed_costs(self): + """Test fixed_cost attribute for different kinds of flows""" + bel = solph.buses.Bus(label="electricityBus") + + solph.components.Source( + label="pv_forever", + outputs={ + bel: solph.flows.Flow( + variable_costs=25, + max=0.8, + nominal_value=10, + fixed_costs=3 + ) + }, + ) + + solph.components.Source( + label="pv_with_lifetime", + outputs={ + bel: solph.flows.Flow( + variable_costs=25, + max=0.8, + nominal_value=10, + fixed_costs=3, + lifetime=20 + ) + }, + ) + + solph.components.Source( + label="pv_with_lifetime_and_age", + outputs={ + bel: solph.flows.Flow( + variable_costs=25, + max=0.8, + nominal_value=10, + fixed_costs=3, + lifetime=20, + age=18 + ) + }, + ) + + self.compare_lp_files("fixed_costs_sources.lp") From b7df7e3c48b5cf7d1003fd911aa44af2570e4eb8 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Tue, 8 Mar 2022 13:04:33 +0100 Subject: [PATCH 0170/1363] Simplify and remove unused methods --- src/oemof/solph/flows/_flow.py | 4 ---- src/oemof/solph/processing.py | 26 -------------------------- 2 files changed, 30 deletions(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 5c78a9119..782b7450f 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -591,8 +591,6 @@ def _lifetime_output_rule(block): self.lifetime_output.add( (inp, out, p, ts), (lhs == rhs) ) - else: - pass # return Constraint.skip() self.lifetime_output = Constraint( self.LIFETIME_FLOWS, m.TIMEINDEX, noruleinit=True @@ -616,8 +614,6 @@ def _lifetime_age_output_rule(block): self.lifetime_age_output.add( (inp, out, p, ts), (lhs == rhs) ) - else: - pass # return Constraint.skip() self.lifetime_age_output = Constraint( self.LIFETIME_AGE_FLOWS, m.TIMEINDEX, noruleinit=True diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index 61ba8ab67..50657d14a 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -44,32 +44,6 @@ def get_tuple(x): return x -def get_timestep(x): - """ - Get the timestep from oemof tuples. - - The timestep from tuples `(n, n, int)`, `(n, n)`, `(n, int)` and (n,) - is fetched as the last element. For time-independent data (scalars) - zero ist returned. - """ - if all(issubclass(type(n), Node) for n in x): - return 0 - else: - return x[-1] - - -def remove_timestep(x): - """ - Remove the timestep from oemof tuples. - - The timestep is removed from tuples of type `(n, n, int)` and `(n, int)`. - """ - if all(issubclass(type(n), Node) for n in x): - return x - else: - return x[:-1] - - def get_timeindex(x): """ Get the timeindex from oemof tuples. From d48c20ec9c24ec826892c539ce290efe537e62bc Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Tue, 8 Mar 2022 16:13:43 +0100 Subject: [PATCH 0171/1363] Update docstrings --- src/oemof/solph/_energy_system.py | 30 ++++++----- src/oemof/solph/_models.py | 86 ++++++++++++++++++++----------- 2 files changed, 74 insertions(+), 42 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index eb0ee513d..97496c351 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -42,12 +42,14 @@ def __init__(self, multi_period=False, periods=None, **kwargs): Parameters ---------- multi_period : boolean - If True, a multi period model is used; defaults to False + If True, a multi-period model is used; defaults to False periods : dict - The periods of a multi period model - Keys are years as integer values, - values are the respective number of the period starting with zero + The periods of a multi-period model; + Keys are the numbers of the respective period, starting with zero, + values are pd.date_range objects carrying the timeindex for the + respective period; + For a standard model, periods only holds one entry, i.e. {0: 0} """ # Doing imports at runtime is generally frowned upon, but should work # for now. See the TODO in :func:`constraint_grouping @@ -77,22 +79,23 @@ def _add_periods(self, periods): """Returns periods to be added to the energy system * For a standard model, periods only contain one value {0: 0} - * For a multi-period model, periods are based on the years used in the - timeindex. As a default, each year in the timeindex is mapped to - its own period. + * For a multi-period model, periods are indexed with integer values + starting from zero. The keys are the time indices for the resective + period given by a pd.date_range object. As a default, + each year in the timeindex is mapped to its own period. Parameters ---------- periods : dict Periods of a (multi-period) model Keys are periods as increasing integer values, starting from 0, - values are the periods defined by a pandas.date_range + values are the periods defined by a pd.date_range object; For a standard model, only one period is used. Returns ------- periods : dict - Periods of the energy system (ensure it being set) + Periods of the energy system (to ensure it being set) """ if not self.multi_period: periods = {0: 0} @@ -115,10 +118,13 @@ def _add_periods(self, periods): return periods def _extract_periods_years(self): - """Map simulation years to the respective period based on timeindices + """Map simulation years to the respective period based on time indices - * `periods_years` is the simulation year corresponding to the start - of a period, starting with 0 + Returns + ------- + periods_years: dict + the simulation year of the start of each a period, + relative to the start of the optimization rund and starting with 0 """ periods_years = {0: 0} if self.multi_period: diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 2fa3d7aa9..0d81fe64e 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -30,7 +30,7 @@ class BaseModel(po.ConcreteModel): - """The BaseModel for other solph-models (Model, MultiPeriodModel, etc.) + """The BaseModel for other solph-models (Model, etc.) Parameters ---------- @@ -42,8 +42,8 @@ class BaseModel(po.ConcreteModel): Defaults to `Model.CONSTRAINTS` objective_weighting : array like (optional) Weights used for temporal objective function - expressions. If nothing is passed `timeincrement` will be used which - is calculated from the freq length of the energy system timeindex . + expressions. If nothing is passed, `timeincrement` will be used which + is calculated from the freq length of the energy system timeindex. auto_construct : boolean If this value is true, the set, variables, constraints, etc. are added, automatically when instantiating the model. For sequential model @@ -51,26 +51,32 @@ class BaseModel(po.ConcreteModel): and use methods `_add_parent_block_sets`, `_add_parent_block_variables`, `_add_blocks`, `_add_objective` - Attributes: - ----------- + Attributes + ---------- timeincrement : sequence - Time increments. + Time increments flows : dict - Flows of the model. + Flows of the model name : str - Name of the model. + Name of the model es : solph.EnergySystem - Energy system of the model. + Energy system of the model meta : `pyomo.opt.results.results_.SolverResults` or None - Solver results. - dual : ... or None - rc : ... or None + Solver results + dual : `pyomo.core.base.suffix.Suffix` or None + Store the dual variables of the model if pyomo suffix is set to IMPORT + rc : `pyomo.core.base.suffix.Suffix` or None + Store the reduced costs of the model if pyomo suffix is set to IMPORT """ CONSTRAINT_GROUPS = [] + """The default list of constraint groups to be used for a model""" def __init__(self, energysystem, **kwargs): + """Initialize a BaseModel, using its energysystem as well as + optional kwargs for specifying the timeincrement, objective_weighting + and constraint_groups.""" super().__init__() # ######################## Arguments ################################# @@ -119,28 +125,34 @@ def __init__(self, energysystem, **kwargs): self._construct() def _construct(self): - """ """ + """Construct a BaseModel by adding parent block sets and variables + as well as child blocks and variables to it. + """ self._add_parent_block_sets() self._add_parent_block_variables() self._add_child_blocks() self._add_objective() def _add_parent_block_sets(self): - """ " Method to create all sets located at the parent block, i.e. the - model itself as they are to be shared across all model components. + """Method to create all sets located at the parent block, i.e. in the + model itself, as they are to be shared across all model components. + See the class :py:class:~oemof.solph.models.Model for the sets created. """ pass def _add_parent_block_variables(self): - """ " Method to create all variables located at the parent block, + """Method to create all variables located at the parent block, i.e. the model itself as these variables are to be shared across - all model components. + all model components. See the class :py:class:~oemof.solph.models.Model + for the `flow` variable created. """ pass def _add_child_blocks(self): """Method to add the defined child blocks for components that have - been grouped in the defined constraint groups. + been grouped in the defined constraint groups. This collects all the + constraints from the buses, components and flows blocks + and adds them to the model. """ for group in self._constraint_groups: @@ -181,7 +193,9 @@ def receive_duals(self): self.rc = po.Suffix(direction=po.Suffix.IMPORT) def results(self): - """Returns a nested dictionary of the results of this optimization""" + """Returns a nested dictionary of the results of this optimization. + See the processing module for more information on results extraction. + """ return processing.results(self) def solve(self, solver="cbc", solver_io="lp", **kwargs): @@ -190,7 +204,7 @@ def solve(self, solver="cbc", solver_io="lp", **kwargs): Parameters ---------- solver : string - solver to be used e.g. "glpk","gurobi","cplex" + solver to be used e.g. "cbc", "glpk","gurobi","cplex" solver_io : string pyomo solver interface file format: "lp","python","nl", etc. \**kwargs : keyword arguments @@ -204,8 +218,8 @@ def solve(self, solver="cbc", solver_io="lp", **kwargs): cmdline_options : dict Dictionary with command line options for solver e.g. {"mipgap":"0.01"} results in "--mipgap 0.01" - {"interior":" "} results in "--interior" - Gurobi solver takes numeric parameter values such as + \{"interior":" "} results in "--interior" + \Gurobi solver takes numeric parameter values such as {"method": 2} """ @@ -249,7 +263,7 @@ def relax_problem(self): class Model(BaseModel): - """An energy system model for operational and investment + """An energy system model for operational and/or investment optimization. Parameters @@ -259,23 +273,32 @@ class Model(BaseModel): constraint_groups : list Solph looks for these groups in the given energy system and uses them to create the constraints of the optimization problem. - Defaults to `Model.CONSTRAINTS` + Defaults to `Model.CONSTRAINT_GROUPS` **The following basic sets are created**: - NODES : + NODES A set with all nodes of the given energy system. - TIMESTEPS : + TIMESTEPS A set with all timesteps of the given time horizon. - FLOWS : + PERIODS + A set with all investment periods of the given time horizon. + + TIMEINDEX + A set with all time indices of the given time horizon, whereby + time indices are defined as a tuple consisting of the period and the + timestep. E.g. (2, 10) would be timestep 10 (which is exactly the same + as in the TIMESTEPS set) and which is in period 2. + + FLOWS A 2 dimensional set with all flows. Index: `(source, target)` **The following basic variables are created**: flow - FlowBlock from source to target indexed by FLOWS, TIMESTEPS. + FlowBlock from source to target indexed by FLOWS, TIMEINDEX. Note: Bounds of this variable are set depending on attributes of the corresponding flow object. @@ -304,7 +327,9 @@ def __init__(self, energysystem, discount_rate=None, **kwargs): super().__init__(energysystem, **kwargs) def _add_parent_block_sets(self): - """ """ + """Add all basic sets to the model, i.e. NODES, TIMESTEPS and FLOWS. + Also create sets PERIODS and TIMEINDEX used for multi-period models. + """ # set with all nodes self.NODES = po.Set(initialize=[n for n in self.es.nodes]) @@ -381,7 +406,8 @@ def _add_parent_block_sets(self): ) def _add_parent_block_variables(self): - """ """ + """Add the parent block variables, which is the `flow` variable, + indexed by FLOWS and TIMEINDEX.""" self.flow = po.Var(self.FLOWS, self.TIMEINDEX, within=po.Reals) for (o, i) in self.FLOWS: From ef938a7c29c7382d46c957e47a6c37a96b6835d0 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 5 Jun 2021 16:12:58 +0200 Subject: [PATCH 0172/1363] Revise / fix docs for models module --- src/oemof/solph/_models.py | 64 ++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 8ac022d5c..c4d924eb5 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -7,6 +7,7 @@ SPDX-FileCopyrightText: Cord Kaldemeyer SPDX-FileCopyrightText: gplssm SPDX-FileCopyrightText: Patrik Schönfeldt +SPDX-FileCopyrightText: Johannes Kochems (jokochems) SPDX-License-Identifier: MIT @@ -41,7 +42,8 @@ class BaseModel(po.ConcreteModel): objective_weighting : array like (optional) Weights used for temporal objective function expressions. If nothing is passed `timeincrement` will be used which - is calculated from the freq length of the energy system timeindex . + is calculated from the freq length of the energy system timeindex or + can be directly passed as a sequence. auto_construct : boolean If this value is true, the set, variables, constraints, etc. are added, automatically when instantiating the model. For sequential model @@ -49,8 +51,8 @@ class BaseModel(po.ConcreteModel): and use methods `_add_parent_block_sets`, `_add_parent_block_variables`, `_add_blocks`, `_add_objective` - Attributes: - ----------- + Attributes + ---------- timeincrement : sequence Time increments. flows : dict @@ -61,14 +63,19 @@ class BaseModel(po.ConcreteModel): Energy system of the model. meta : `pyomo.opt.results.results_.SolverResults` or None Solver results. - dual : ... or None - rc : ... or None - + dual : `pyomo.core.base.suffix.Suffix` or None + Store the dual variables of the model if pyomo suffix is set to IMPORT + rc : `pyomo.core.base.suffix.Suffix` or None + Store the reduced costs of the model if pyomo suffix is set to IMPORT """ CONSTRAINT_GROUPS = [] + """The default list of constraint groups to be used for a model.""" def __init__(self, energysystem, **kwargs): + """Initialize a BaseModel, using its energysystem as well as + optional kwargs for specifying the timeincrement, objective_weigting + and constraint groups.""" super().__init__() # ######################## Arguments ################################# @@ -117,30 +124,33 @@ def __init__(self, energysystem, **kwargs): self._construct() def _construct(self): - """ """ + """Construct a BaseModel by adding parent block sets and variables + as well as child blocks and variables to it.""" self._add_parent_block_sets() self._add_parent_block_variables() self._add_child_blocks() self._add_objective() def _add_parent_block_sets(self): - """ " Method to create all sets located at the parent block, i.e. the - model itself as they are to be shared across all model components. + """Method to create all sets located at the parent block, i.e. in the + model itself, as they are to be shared across all model components. + See the class :py:class:~oemof.solph.models.Model for the sets created. """ pass def _add_parent_block_variables(self): - """ " Method to create all variables located at the parent block, + """Method to create all variables located at the parent block, i.e. the model itself as these variables are to be shared across - all model components. + all model components. See the class :py:class:~oemof.solph.models.Model + for the `flow` variable created. """ pass def _add_child_blocks(self): """Method to add the defined child blocks for components that have - been grouped in the defined constraint groups. + been grouped in the defined constraint groups. This collects all the + constraints from the component blocks and adds them to the model. """ - for group in self._constraint_groups: # create instance for block block = group() @@ -171,7 +181,6 @@ def receive_duals(self): """Method sets solver suffix to extract information about dual variables from solver. Shadow prices (duals) and reduced costs (rc) are set as attributes of the model. - """ # shadow prices self.dual = po.Suffix(direction=po.Suffix.IMPORT) @@ -179,7 +188,9 @@ def receive_duals(self): self.rc = po.Suffix(direction=po.Suffix.IMPORT) def results(self): - """Returns a nested dictionary of the results of this optimization""" + """Returns a nested dictionary of the results of this optimization. + See the processing module for more information on results extraction. + """ return processing.results(self) def solve(self, solver="cbc", solver_io="lp", **kwargs): @@ -188,7 +199,7 @@ def solve(self, solver="cbc", solver_io="lp", **kwargs): Parameters ---------- solver : string - solver to be used e.g. "glpk","gurobi","cplex" + solver to be used e.g. "cbc", "glpk","gurobi","cplex" solver_io : string pyomo solver interface file format: "lp","python","nl", etc. \**kwargs : keyword arguments @@ -202,10 +213,9 @@ def solve(self, solver="cbc", solver_io="lp", **kwargs): cmdline_options : dict Dictionary with command line options for solver e.g. {"mipgap":"0.01"} results in "--mipgap 0.01" - {"interior":" "} results in "--interior" - Gurobi solver takes numeric parameter values such as + \{"interior":" "} results in "--interior" + \Gurobi solver takes numeric parameter values such as {"method": 2} - """ solve_kwargs = kwargs.get("solve_kwargs", {}) solver_cmdline_options = kwargs.get("cmdline_options", {}) @@ -247,7 +257,7 @@ def relax_problem(self): class Model(BaseModel): - """An energy system model for operational and investment + """An energy system model for operational and/or investment optimization. Parameters @@ -257,17 +267,18 @@ class Model(BaseModel): constraint_groups : list Solph looks for these groups in the given energy system and uses them to create the constraints of the optimization problem. - Defaults to `Model.CONSTRAINTS` + Defaults to `Model.CONSTRAINT_GROUPS` + **The following basic sets are created**: - NODES : + NODES A set with all nodes of the given energy system. - TIMESTEPS : + TIMESTEPS A set with all timesteps of the given time horizon. - FLOWS : + FLOWS A 2 dimensional set with all flows. Index: `(source, target)` **The following basic variables are created**: @@ -291,7 +302,7 @@ def __init__(self, energysystem, **kwargs): super().__init__(energysystem, **kwargs) def _add_parent_block_sets(self): - """ """ + """Add all basic sets to the model, i.e. NODES, TIMESTEPS and FLOWS.""" # set with all nodes self.NODES = po.Set(initialize=[n for n in self.es.nodes]) @@ -334,7 +345,8 @@ def _add_parent_block_sets(self): ) def _add_parent_block_variables(self): - """ """ + """Add the parent block variables, which is the `flow` variable, + indexed by FLOWS and TIMESTEPS.""" self.flow = po.Var(self.FLOWS, self.TIMESTEPS, within=po.Reals) for (o, i) in self.FLOWS: From 306855a78c5e4ee0187ce2109ab56517007c7edc Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 5 Jun 2021 16:30:24 +0200 Subject: [PATCH 0173/1363] Add minor addition for docs of options module --- src/oemof/solph/_options.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/oemof/solph/_options.py b/src/oemof/solph/_options.py index 312fa543f..51c63aab1 100644 --- a/src/oemof/solph/_options.py +++ b/src/oemof/solph/_options.py @@ -17,7 +17,9 @@ class Investment: - """ + """Defines an Investment object holding all the specifications needed + for investment modeling. + Parameters ---------- maximum : float, :math:`P_{invest,max}` or :math:`E_{invest,max}` @@ -41,8 +43,12 @@ class Investment: For the variables, constraints and parts of the objective function, which - are created, see :class:`oemof.solph.blocks.investment_flow.InvestmentFlowBlock` - and :class:`oemof.solph.components._generic_storage.GenericInvestmentStorageBlock`. + are created, see + :py:class:`~oemof.solph.blocks.investment_flow.InvestmentFlow`, + :py:class:`~oemof.solph.components.generic_storage.GenericInvestmentStorageBlock` + :py:class:`~oemof.solph.custom.sink_dsm.SinkDSMOemofInvestmentBlock`, + :py:class:`~oemof.solph.custom.sink_dsm.SinkDSMDLRInvestmentBlock` and + :py:class:`~oemof.solph.custom.sink_dsm.SinkDSMDIWInvestmentBlock`. """ # noqa: E501 @@ -103,7 +109,9 @@ def _check_invest_attributes_offset(self): class NonConvex: - """ + """Defines a NonConvex object holding all the specifications for NonConvex + Flows, i.e. Flows with binary variables associated to them. + Parameters ---------- startup_costs : numeric (iterable or scalar) @@ -124,9 +132,9 @@ class NonConvex: Be aware that minimum up and downtimes can contradict each other and may to infeasible problems. maximum_startups : numeric (0 or positive integer) - Maximum number of start-ups. + Maximum number of start-ups in the optimization timeframe. maximum_shutdowns : numeric (0 or positive integer) - Maximum number of shutdowns. + Maximum number of shutdowns in the optimization timeframe. initial_status : numeric (0 or 1) Integer value indicating the status of the flow in the first time step (0 = off, 1 = on). For minimum up and downtimes, the initial status @@ -140,9 +148,9 @@ class NonConvex: A dictionary containing the following two keys: * `'ub'`: numeric (iterable, scalar or None), the normed *upper - bound* on the positive difference (`flow[t-1] < flow[t]`) of + bound* of the positive difference (`flow[t-1] < flow[t]`) of two consecutive flow values. - * `'costs``: numeric (scalar or None), the gradient cost per + * `'costs'`: numeric (scalar or None), the gradient cost per unit. negative_gradient : :obj:`dict`, default: `{'ub': None, 'costs': 0}` @@ -151,7 +159,7 @@ class NonConvex: * `'ub'`: numeric (iterable, scalar or None), the normed *upper bound* on the negative difference (`flow[t-1] > flow[t]`) of two consecutive flow values. - * `'costs``: numeric (scalar or None), the gradient cost per + * `'costs'`: numeric (scalar or None), the gradient cost per unit. """ From 73cec900c775c36fd3c3e7d2fc8d4347e925446d Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 11 Jun 2021 11:31:21 +0200 Subject: [PATCH 0174/1363] Begin revising docs for SinkDSM --- .../components/experimental/_sink_dsm.py | 113 ++++++++++-------- 1 file changed, 63 insertions(+), 50 deletions(-) diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index 608f465b9..5d28b938b 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -1,7 +1,11 @@ # -*- coding: utf-8 -*- """ -In-development functionality for demand-side management. +Implementation of demand-side management (demand response) which allows for + +* modeling load shifting and/or shedding of a given baseline demand, +* assessing both, a pure dispatch and an investment model and +* choosing among different (storage-alike) implementations. SPDX-FileCopyrightText: Uwe Krien SPDX-FileCopyrightText: Simon Hilpert @@ -11,6 +15,7 @@ SPDX-FileCopyrightText: jakob-wo SPDX-FileCopyrightText: gplssm SPDX-FileCopyrightText: jnnr +SPDX-FileCopyrightText: Julian Endres SPDX-FileCopyrightText: Johannes Kochems (jokochems) SPDX-License-Identifier: MIT @@ -34,38 +39,42 @@ class SinkDSM(Sink): r""" - Demand Side Management implemented as Sink with flexibility potential. + Demand Side Management implemented as a Sink with flexibility potential + to deviate from the baseline demand in upwards or downwards direction. There are several approaches possible which can be selected: - - DIW: Based on the paper by Zerrahn, Alexander and Schill, Wolf-Peter - (2015): `On the representation of demand-side management in power system - models `_, - in: Energy (84), pp. 840-845, 10.1016/j.energy.2015.03.037, - accessed 08.01.2021, pp. 842-843. - - DLR: Based on the PhD thesis of Gils, Hans Christian (2015): - `Balancing of Intermittent Renewable Power Generation by Demand Response - and Thermal Energy Storage`, Stuttgart, - , - accessed 08.01.2021, pp. 67-70. - - oemof: Created by Julian Endres. A fairly simple DSM representation which - demands the energy balance to be levelled out in fixed cycles + + * DIW: Based on the paper by Zerrahn, Alexander and Schill, Wolf-Peter + (2015): `On the representation of demand-side management in power system + models, in: Energy (84), pp. 840-845, + 10.1016/j.energy.2015.03.037 + `_, + accessed 08.01.2021, pp. 842-843. + * DLR: Based on the PhD thesis of Gils, Hans Christian (2015): + `Balancing of Intermittent Renewable Power Generation by Demand Response + and Thermal Energy Storage`, Stuttgart, + ``_, + accessed 08.01.2021, pp. 67-70. + * oemof: Created by Julian Endres. A fairly simple DSM representation which + demands the energy balance to be levelled out in fixed cycles An evaluation of different modeling approaches has been carried out and presented at the INREC 2020. Some of the results are as follows: - - DIW: A solid implementation with the tendency of slight overestimization - of potentials since a shift_time is not accounted for. It may get - computationally expensive due to a high time-interlinkage in constraint - formulations. - - DLR: An extensive modeling approach for demand response which neither - leads to an over- nor underestimization of potentials and balances modeling - detail and computation intensity. :attr:`fixes` and :attr:`addition` should - both be set to True which is the default value. - - oemof: A very computationally efficient approach which only requires the - energy balance to be levelled out in certain intervals. If demand response - is not at the center of the research and/or parameter availability is - limited, this approach should be chosen. Note that approach `oemof` does - allow for load shedding, but does not impose a limit on maximum amount of - shedded energy. + + * DIW: A solid implementation with the tendency of slight overestimization + of potentials since a `shift_time` is not included. It may get + computationally expensive due to a high time-interlinkage in constraint + formulations. + * DLR: An extensive modeling approach for demand response which neither + leads to an over- nor underestimization of potentials and balances + modeling detail and computation intensity. `fixes` and + `addition` should both be set to True which is the default value. + * oemof: A very computationally efficient approach which only requires the + energy balance to be levelled out in certain intervals. If demand + response is not at the center of the research and/or parameter + availability is limited, this approach should be chosen. + Note that approach `oemof` does allow for load shedding, + but does not impose a limit on maximum amount of shedded energy. SinkDSM adds additional constraints that allow to shift energy in certain time window constrained by :attr:`~capacity_up` and @@ -83,7 +92,7 @@ class SinkDSM(Sink): maximum DSM capacity that may be increased (normalized) capacity_down: int or array maximum DSM capacity that may be reduced (normalized) - approach: 'oemof', 'DIW', 'DLR' + approach: str, one of 'oemof', 'DIW', 'DLR' Choose one of the DSM modeling approaches. Read notes about which parameters to be applied for which approach. @@ -443,11 +452,11 @@ class SinkDSMOemofBlock(SimpleBlock): .. math:: & - (1) \quad DSM_{t}^{up} = 0 \quad \forall t - \quad if \space eligibility_{shift} = False \\ + (1) \quad DSM_{t}^{up} = 0 \\ + \forall t \quad if \space e_{shift} = False \\ & (2) \quad DSM_{t}^{do, shed} = 0 \quad \forall t - \quad if \space eligibility_{shed} = False \\ + \quad if \space e_{shed} = False \\ & (3) \quad \dot{E}_{t} = demand_{t} \cdot demand_{max} + DSM_{t}^{up} - DSM_{t}^{do, shift} - DSM_{t}^{do, shed} @@ -475,25 +484,28 @@ class SinkDSMOemofBlock(SimpleBlock): **Table: Symbols and attribute names of variables and parameters** + apparently, this won't be rendered + + ============================= ===================== ==== ======================================= + symbol attribute type explanation + ============================= ===================== ==== ======================================= + :math:`DSM_{t}^{up}` `dsm_up[g, t]` V DSM up shift (capacity shifted upwards) + :math:`DSM_{t}^{do, shift}` `dsm_do_shift[g, t]` V DSM down shift (capacity shifted downwards) + :math:`DSM_{t}^{do, shed}` `dsm_do_shed[g, t]` V DSM shedded (capacity shedded, i.e. not compensated for) + :math:`\dot{E}_{t}` `SinkDSM.inputs` V Energy flowing in from (electrical) inflow bus + :math:`demand_{t}` `demand[t]` P (Electrical) demand series (normalized) + :math:`demand_{max}` `max_demand` P Maximum demand value + :math:`E_{t}^{do}` `capacity_down[t]` P Capacity allowed for a load adjustment downwards + (normalized; shifting + shedding) + ============================= ===================== ==== ======================================= + .. csv-table:: Variables (V) and Parameters (P) :header: "symbol", "attribute", "type", "explanation" :widths: 1, 1, 1, 1 - ":math:`DSM_{t}^{up}` ", - ":attr:`~SinkDSM.dsm_up[g, t]` ","V", "DSM - up shift (capacity shifted upwards)" - ":math:`DSM_{t}^{do, shift}` ", - ":attr:`~SinkDSM.dsm_do_shift[g, t]` ", - "V","DSM down shift (capacity shifted downwards)" - ":math:`DSM_{t}^{do, shed}` ", - ":attr:`~SinkDSM.dsm_do_shed[g, t]` ", - "V","DSM shedded (capacity shedded, i.e. not compensated for)" - ":math:`\dot{E}_{t}`",":attr:`~SinkDSM.inputs`","V", "Energy - flowing in from (electrical) inflow bus" - ":math:`demand_{t}`",":attr:`~SinkDSM.demand[t]`","P", - "(Electrical) demand series (normalized)" - ":math:`demand_{max}`",":attr:`~SinkDSM.max_demand`","P", - "Maximum demand value" + + + ":math:`E_{t}^{do}`",":attr:`~SinkDSM.capacity_down[t]`","P", "Capacity allowed for a load adjustment downwards (normalized) (DSM down shift + DSM shedded)" @@ -510,11 +522,11 @@ class SinkDSMOemofBlock(SimpleBlock): ":math:`\eta`",":attr:`~SinkDSM.efficiency`","P", "Efficiency loss forload shifting processes" ":math:`\mathbb{T}` "," ","P", "Time steps" - ":math:`eligibility_{shift}` ", + ":math:`e_{shift}` ", ":attr:`~SinkDSM.shift_eligibility`","P", "Boolean parameter indicating if unit can be used for load shifting" - ":math:`eligibility_{shed}` ", + ":math:`e_{shed}` ", ":attr:`~SinkDSM.shed_eligibility`","P", "Boolean parameter indicating if unit can be used for load shedding" @@ -526,7 +538,8 @@ class SinkDSMOemofBlock(SimpleBlock): ":math:`cost_{t}^{dsm, do, shed}` ", ":attr:`~SinkDSM.cost_dsm_down_shed[t]`","P", "Variable costs for shedding load" - """ + + """ # noqa: E501 CONSTRAINT_GROUP = True def __init__(self, *args, **kwargs): From fcaeac0b77adbb2b8d636420b2a1e757233040c6 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 11 Jun 2021 17:22:57 +0200 Subject: [PATCH 0175/1363] Extend and tidy up docs of sink_dsm module - fix broken equations - fix alignment, line breaks - pretty everything up - fix typos - add explanation - change from broken unreadable csv tables to readable standard tables - disable checking for line length since tables exceed maximum line length --- .../components/experimental/_sink_dsm.py | 1077 +++++++++-------- 1 file changed, 557 insertions(+), 520 deletions(-) diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index 5d28b938b..b0c953343 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -3,7 +3,8 @@ """ Implementation of demand-side management (demand response) which allows for -* modeling load shifting and/or shedding of a given baseline demand, +* modeling load shifting and/or shedding of a given baseline demand + for a demand response portfolio, * assessing both, a pure dispatch and an investment model and * choosing among different (storage-alike) implementations. @@ -45,9 +46,9 @@ class SinkDSM(Sink): There are several approaches possible which can be selected: * DIW: Based on the paper by Zerrahn, Alexander and Schill, Wolf-Peter - (2015): `On the representation of demand-side management in power system + (2015): On the representation of demand-side management in power system models, in: Energy (84), pp. 840-845, - 10.1016/j.energy.2015.03.037 + `10.1016/j.energy.2015.03.037 `_, accessed 08.01.2021, pp. 842-843. * DLR: Based on the PhD thesis of Gils, Hans Christian (2015): @@ -77,8 +78,7 @@ class SinkDSM(Sink): but does not impose a limit on maximum amount of shedded energy. SinkDSM adds additional constraints that allow to shift energy in certain - time window constrained by :attr:`~capacity_up` and - :attr:`~capacity_down`. + time window constrained by `capacity_up` and `capacity_down`. Parameters ---------- @@ -99,33 +99,38 @@ class SinkDSM(Sink): oemof : Simple model in which the load shift must be compensated in a - predefined fixed interval (:attr:`~shift_interval` is mandatory). - Within time windows of the length :attr:`~shift_interval` DSM - up and down shifts are balanced. See - :class:`~SinkDSMOemofBlock` for details. + predefined fixed interval (`shift_interval` is mandatory). + Within time windows of the length `shift_interval` DSM + up and down shifts are balanced. For details see + :class:`~SinkDSMOemofBlock` resp. + :class:`~SinkDSMOemofInvestmentBlock`. DIW : Sophisticated model based on the formulation by Zerrahn & Schill (2015a). The load shift of the component must be - compensated in a predefined delay time (:attr:`~delay_time` is + compensated in a predefined delay time (`delay_time` is mandatory). - For details see :class:`~SinkDSMDIWBlock`. + For details see + :class:`~SinkDSMDIWBlock` resp. + :class:`~SinkDSMDIWInvestmentBlock`. DLR : Sophisticated model based on the formulation by Gils (2015). The load shift of the component must be - compensated in a predefined delay time (:attr:`~delay_time` is + compensated in a predefined delay time (`delay_time` is mandatory). - For details see :class:`~SinkDSMDLRBlock`. + For details see + :class:`~SinkDSMDLRBlock` resp. + :class:`~SinkDSMDLRInvestmentBlock`. shift_interval: int - Only used when :attr:`~approach` is set to 'oemof'. Otherwise, can be + Only used when `approach` is set to "oemof". Otherwise, can be None. It's the interval in which between :math:`DSM_{t}^{up}` and :math:`DSM_{t}^{down}` have to be compensated. delay_time: int - Only used when :attr:`~approach` is set to 'DIW' or 'DLR'. Otherwise, + Only used when `approach` is set to "DIW" or "DLR". Otherwise, can be None. Length of symmetrical time windows around :math:`t` in which :math:`DSM_{t}^{up}` and :math:`DSM_{t,tt}^{down}` have to be @@ -133,11 +138,11 @@ class SinkDSM(Sink): Note: For approach 'DLR', an iterable is constructed in order to model flexible delay times shift_time: int - Only used when :attr:`~approach` is set to 'DLR'. + Only used when `approach` is set to "DLR". Duration of a single upwards or downwards shift (half a shifting cycle if there is immediate compensation) shed_time: int - Only used when :attr:`~shed_eligibility` is set to True. + Only used when `shed_eligibility` is set to True. Maximum length of a load shedding process at full capacity (used within energy limit constraint) max_demand: numeric @@ -165,7 +170,7 @@ class SinkDSM(Sink): efficiency : float Efficiency factor for load shifts (between 0 and 1) recovery_time_shift : int - Only used when :attr:`~approach` is set to 'DIW'. + Only used when `approach` is set to "DIW". Minimum time between the end of one load shifting process and the start of another for load shifting processes recovery_time_shed : int @@ -173,32 +178,33 @@ class SinkDSM(Sink): Minimum time between the end of one load shifting process and the start of another for load shedding processes ActivateYearLimit : boolean - Only used when :attr:`~approach` is set to 'DLR'. + Only used when `approach` is set to "DLR". Control parameter; activates constraints for year limit if set to True ActivateDayLimit : boolean - Only used when :attr:`~approach` is set to 'DLR'. + Only used when `approach` is set to "DLR". Control parameter; activates constraints for day limit if set to True n_yearLimit_shift : int - Only used when :attr:`~approach` is set to 'DLR'. + Only used when `approach` is set to "DLR". Maximum number of load shifts at full capacity per year, used to limit the amount of energy shifted per year. Optional parameter that is only needed when ActivateYearLimit is True n_yearLimit_shed : int - Only used when :attr:`~approach` is set to 'DLR'. + Only used when `approach` is set to "DLR". Maximum number of load sheds at full capacity per year, used to limit the amount of energy shedded per year. Mandatory parameter if load - shedding is allowed by setting shed_eligibility to True + shedding is allowed by setting `shed_eligibility` to True t_dayLimit: int - Only used when :attr:`~approach` is set to 'DLR'. + Only used when `approach` is set to "DLR". Maximum duration of load shifts at full capacity per day, used to limit the amount of energy shifted per day. Optional parameter that is only needed when ActivateDayLimit is True addition : boolean - Only used when :attr:`~approach` is set to 'DLR'. + Only used when `approach` is set to "DLR". Boolean parameter indicating whether or not to include additional - constraint (which corresponds to Eq. 10 from Zerrahn and Schill (2015a) + constraint (which corresponds to Eq. 10 + from Zerrahn and Schill (2015a)) fixes : boolean - Only used when :attr:`~approach` is set to 'DLR'. + Only used when `approach` is set to "DLR". Boolean parameter indicating whether or not to include additional fixes. These comprise prohibiting shifts which cannot be balanced within the optimization timeframe @@ -212,20 +218,26 @@ class SinkDSM(Sink): Note ---- - * :attr:`method` has been renamed to :attr:`approach`. - * As many constraints and dependencies are created in approach 'DIW', - computational cost might be high with a large 'delay_time' and with model - of high temporal resolution - * The approach 'DLR' preforms better in terms of calculation time, - compared to the approach 'DIW' - * Using :attr:`~approach` 'DIW' or 'DLR' might result in demand shifts that + * When you set up a dispatch model, you have to specify `max_capacity_up`, + `max_capacity_down` and `max_demand`. Don't set `flex_share_up` + and `flex_share_down` which shall only used for investment modeling. + * When using the investment mode, you have to specify `flex_share_up` + and `flex_share_down` instead of `max_capacity_up`, + `max_capacity_down` and `max_demand`. + * `method` has been renamed to `approach`. + * As many constraints and dependencies are created in approach "DIW", + computational cost might be high with a large `delay_time` and with model + of high temporal resolution. + * The approach "DLR" preforms better in terms of calculation time, + compared to the approach "DIW". + * Using `approach` "DIW" or "DLR" might result in demand shifts that exceed the specified delay time by activating up and down simultaneously in the time steps between to DSM events. Thus, the purpose of this component is to model demand response portfolios rather than individual demand units. * It's not recommended to assign cost to the flow that connects - :class:`~SinkDSM` with a bus. Instead, use :attr:`~SinkDSM.cost_dsm_up` - or :attr:`~cost_dsm_down_shift` + :class:`~SinkDSM` with a bus. Instead, use `cost_dsm_up` + or `cost_dsm_down_shift`. * Variable costs may be attributed to upshifts, downshifts or both. Costs for shedding may deviate from that for shifting (usually costs for shedding are much larger and equal to the value @@ -446,98 +458,84 @@ def constraint_group(self): class SinkDSMOemofBlock(SimpleBlock): r"""Constraints for SinkDSM with "oemof" approach - **The following constraints are created for approach = 'oemof':** + **The following constraints are created for approach = "oemof":** - .. _SinkDSMOemof equations: + .. _SinkDSMOemofBlock equations: .. math:: & (1) \quad DSM_{t}^{up} = 0 \\ - \forall t \quad if \space e_{shift} = False \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shift} = \textrm{False} \\ + & \\ & - (2) \quad DSM_{t}^{do, shed} = 0 \quad \forall t - \quad if \space e_{shed} = False \\ + (2) \quad DSM_{t}^{do, shed} = 0 \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shed} = \textrm{False} \\ + & \\ & (3) \quad \dot{E}_{t} = demand_{t} \cdot demand_{max} + DSM_{t}^{up} - - DSM_{t}^{do, shift} - DSM_{t}^{do, shed} - \quad \forall t \in \mathbb{T} \\ + - DSM_{t}^{do, shift} - DSM_{t}^{do, shed} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & - (4) \quad DSM_{t}^{up} \leq E_{t}^{up} \cdot E_{up, max} - \quad \forall t \in \mathbb{T} \\ + (4) \quad DSM_{t}^{up} \leq E_{t}^{up} \cdot E_{up, max} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (5) \quad DSM_{t}^{do, shift} + DSM_{t}^{do, shed} - \leq E_{t}^{do} \cdot E_{do, max} - \quad \forall t \in \mathbb{T} \\ + \leq E_{t}^{do} \cdot E_{do, max} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (6) \quad \sum_{t=t_s}^{t_s+\tau} DSM_{t}^{up} \cdot \eta = - \sum_{t=t_s}^{t_s+\tau} DSM_{t}^{do, shift} \quad \forall t_s \in - \{k \in \mathbb{T} \mid k \mod \tau = 0\} \\ - & + \sum_{t=t_s}^{t_s+\tau} DSM_{t}^{do, shift} \\ + & \quad \quad \quad \quad \forall t_s \in \{k \in \mathbb{T} + \mid k \mod \tau = 0\} \\ **The following parts of the objective function are created:** .. math:: - DSM_{t}^{up} \cdot cost_{t}^{dsm, up} + & + (DSM_{t}^{up} \cdot cost_{t}^{dsm, up} + DSM_{t}^{do, shift} \cdot cost_{t}^{dsm, do, shift} - + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed} - \quad \forall t \in \mathbb{T} \\ + + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed}) + \cdot \omega_{t} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ **Table: Symbols and attribute names of variables and parameters** - apparently, this won't be rendered - - ============================= ===================== ==== ======================================= - symbol attribute type explanation - ============================= ===================== ==== ======================================= - :math:`DSM_{t}^{up}` `dsm_up[g, t]` V DSM up shift (capacity shifted upwards) - :math:`DSM_{t}^{do, shift}` `dsm_do_shift[g, t]` V DSM down shift (capacity shifted downwards) - :math:`DSM_{t}^{do, shed}` `dsm_do_shed[g, t]` V DSM shedded (capacity shedded, i.e. not compensated for) - :math:`\dot{E}_{t}` `SinkDSM.inputs` V Energy flowing in from (electrical) inflow bus - :math:`demand_{t}` `demand[t]` P (Electrical) demand series (normalized) - :math:`demand_{max}` `max_demand` P Maximum demand value - :math:`E_{t}^{do}` `capacity_down[t]` P Capacity allowed for a load adjustment downwards - (normalized; shifting + shedding) - ============================= ===================== ==== ======================================= - - .. csv-table:: Variables (V) and Parameters (P) - :header: "symbol", "attribute", "type", "explanation" - :widths: 1, 1, 1, 1 - - - - - ":math:`E_{t}^{do}`",":attr:`~SinkDSM.capacity_down[t]`","P", - "Capacity allowed for a load adjustment downwards (normalized) - (DSM down shift + DSM shedded)" - ":math:`E_{t}^{up}`",":attr:`~SinkDSM.capacity_up[t]`","P", - "Capacity allowed for a shift upwards (normalized) (DSM up shift)" - ":math:`E_{do, max}`",":attr:`~SinkDSM.max_capacity_down`","P", - "Maximum capacity allowed for a load adjustment downwards - (DSM down shift + DSM shedded)" - ":math:`E_{up, max}`",":attr:`~SinkDSM.max_capacity_up`","P", - "Capacity allowed for a shift upwards (normalized) (DSM up shift)" - ":math:`\tau`",":attr:`~SinkDSM.shift_interval`","P", "Shift - interval (time within which the energy balance must be - levelled out" - ":math:`\eta`",":attr:`~SinkDSM.efficiency`","P", "Efficiency - loss forload shifting processes" - ":math:`\mathbb{T}` "," ","P", "Time steps" - ":math:`e_{shift}` ", - ":attr:`~SinkDSM.shift_eligibility`","P", - "Boolean parameter indicating if unit can be used for - load shifting" - ":math:`e_{shed}` ", - ":attr:`~SinkDSM.shed_eligibility`","P", - "Boolean parameter indicating if unit can be used for - load shedding" - ":math:`cost_{t}^{dsm, up}` ", ":attr:`~SinkDSM.cost_dsm_up[t]`", - "P", "Variable costs for an upwards shift" - ":math:`cost_{t}^{dsm, do, shift}` ", - ":attr:`~SinkDSM.cost_dsm_down_shift[t]`","P", - "Variable costs for a downwards shift (load shifting)" - ":math:`cost_{t}^{dsm, do, shed}` ", - ":attr:`~SinkDSM.cost_dsm_down_shed[t]`","P", - "Variable costs for shedding load" + .. table:: Variables (V) and Parameters (P) + :widths: 1, 1, 1, 1 + + ================================= ======================== ==== ======================================= + symbol attribute type explanation + ================================= ======================== ==== ======================================= + :math:`DSM_{t}^{up}` `dsm_up[g, t]` V DSM up shift (capacity shifted upwards) + :math:`DSM_{t}^{do, shift}` `dsm_do_shift[g, t]` V DSM down shift (capacity shifted downwards) + :math:`DSM_{t}^{do, shed}` `dsm_do_shed[g, t]` V DSM shedded (capacity shedded, i.e. not compensated for) + :math:`\dot{E}_{t}` `SinkDSM.inputs` V Energy flowing in from (electrical) inflow bus + :math:`demand_{t}` `demand[t]` P (Electrical) demand series (normalized) + :math:`demand_{max}` `max_demand` P Maximum demand value + :math:`E_{t}^{do}` `capacity_down[t]` P | Capacity allowed for a load adjustment downwards + | (normalized; shifting + shedding) + :math:`E_{t}^{up}` `capacity_up[t]` P Capacity allowed for a shift upwards (normalized) + :math:`E_{do, max}` `max_capacity_down` P | Maximum capacity allowed for a load adjustment downwards + | (shifting + shedding) + :math:`E_{up, max}` `max_capacity_up` P Maximum capacity allowed for a shift upwards + :math:`\tau` `shift_interval` P | interval (time within which the + | energy balance must be levelled out) + :math:`\eta` `efficiency` P Efficiency for load shifting processes + :math:`\mathbb{T}` P Time steps of the model + :math:`e_{shift}` `shift_eligibility` P | Boolean parameter indicating if unit can be used + | for load shifting + :math:`e_{shed}` `shed_eligibility` P | Boolean parameter indicating if unit can be used + | for load shedding + :math:`cost_{t}^{dsm, up}` `cost_dsm_up[t]` P Variable costs for an upwards shift + :math:`cost_{t}^{dsm, do, shift}` `cost_dsm_down_shift[t]` P Variable costs for a downwards shift (load shifting) + :math:`cost_{t}^{dsm, do, shed}` `cost_dsm_down_shift[t]` P Variable costs for shedding load + :math:`\omega_{t}` P Objective weighting of the model for timestep t + ================================= ======================== ==== ======================================= """ # noqa: E501 CONSTRAINT_GROUP = True @@ -734,90 +732,96 @@ def _objective_expression(self): class SinkDSMOemofInvestmentBlock(SimpleBlock): - r"""Constraints for SinkDSM with "oemof" approach and :attr:`investment` + r"""Constraints for SinkDSM with "oemof" approach and `investment` + defined - **The following constraints are created for approach = 'oemof' with an - investment object defined:** + **The following constraints are created for approach = "oemof" + with an investment object defined:** - .. _SinkDSMOemof equations: + .. _SinkDSMOemofInvestmentBlock equations: .. math:: & (1) \quad invest_{min} \leq invest \leq invest_{max} \\ + & \\ & - (2) \quad DSM_{t}^{up} = 0 \quad \forall t - \quad if \space eligibility_{shift} = False \\ + (2) \quad DSM_{t}^{up} = 0 \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shift} = \textrm{False} \\ + & \\ & - (3) \quad DSM_{t}^{do, shed} = 0 \quad \forall t - \quad if \space eligibility_{shed} = False \\ + (3) \quad DSM_{t}^{do, shed} = 0 \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shed} = \textrm{False} \\ + & \\ & (4) \quad \dot{E}_{t} = demand_{t} \cdot (invest + E_{exist}) - + DSM_{t}^{up} - - DSM_{t}^{do, shift} - DSM_{t}^{do, shed} - \quad \forall t \in \mathbb{T} \\ + + DSM_{t}^{up} - DSM_{t}^{do, shift} - DSM_{t}^{do, shed} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (5) \quad DSM_{t}^{up} \leq E_{t}^{up} \cdot (invest + E_{exist}) - \cdot s_{flex, up} - \quad \forall t \in \mathbb{T} \\ + \cdot s_{flex, up} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (6) \quad DSM_{t}^{do, shift} + DSM_{t}^{do, shed} \leq - E_{t}^{do} \cdot (invest + E_{exist}) \cdot s_{flex, do} - \quad \forall t \in \mathbb{T} \\ + E_{t}^{do} \cdot (invest + E_{exist}) \cdot s_{flex, do} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (7) \quad \sum_{t=t_s}^{t_s+\tau} DSM_{t}^{up} \cdot \eta = - \sum_{t=t_s}^{t_s+\tau} DSM_{t}^{do, shift} \quad \forall t_s \in + \sum_{t=t_s}^{t_s+\tau} DSM_{t}^{do, shift} \\ + & \quad \quad \quad \quad \forall t_s \in \{k \in \mathbb{T} \mid k \mod \tau = 0\} \\ - & **The following parts of the objective function are created:** * Investment annuity: .. math:: + & invest \cdot costs_{invest} \\ * Variable costs: .. math:: - DSM_{t}^{up} \cdot cost_{t}^{dsm, up} + & + (DSM_{t}^{up} \cdot cost_{t}^{dsm, up} + DSM_{t}^{do, shift} \cdot cost_{t}^{dsm, do, shift} - + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed} - \quad \forall t \in \mathbb{T} \\ + + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed}) + \cdot \omega_{t} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ See remarks in :class:`oemof.solph.components.experimental._sink_dsm.SinkDSMOemofBlock`. **Symbols and attribute names of variables and parameters** - Please refer to - :class:`oemof.solph.components.experimental._sink_dsm.SinkDSMOemofBlock`. + * Please refer to + :class:`oemof.solph.components.experimental._sink_dsm.SinkDSMOemofBlock`. + for a variables and parameter description. + * The following variables and parameters are exclusively used for + investment modeling: + + .. table:: Variables (V) and Parameters (P) + :widths: 1, 1, 1, 1 + + ================================= ======================== ==== ======================================= + symbol attribute type explanation + ================================= ======================== ==== ======================================= + :math:`invest` `invest` V | DSM capacity invested in + | Equals to the additionally installed capacity. + | The capacity share eligible for a shift is determined by flex share(s). + :math:`invest_{min}` `investment.minimum` P minimum investment + :math:`invest_{max}` `investment.maximum` P maximum investment + :math:`E_{exist}` `investment.existing` P existing DSM capacity + :math:`s_{flex, up}` `flex_share_up` P share of invested capacity that may be shift upwards at maximum + :math:`s_{flex, do}` `flex_share_do` P share of invested capacity that may be shift downwards at maximum + :math:`costs_{invest}` `investment.ep_costs` P specific investment annuity + ================================= ======================== ==== ======================================= - The following variables and parameters are exclusively used for - investment modeling: - - .. csv-table:: Variables (V) and Parameters (P) - :header: "symbol", "attribute", "type", "explanation" - :widths: 1, 1, 1, 1 - - ":math:`invest` ",":attr:`~SinkDSM.invest` ","V", "DSM capacity - invested in. Equals to the additionally installed capacity. - The capacity share eligible for a shift is determined - by flex share(s)." - ":math:`invest_{min}` ", ":attr:`~SinkDSM.investment.minimum` ", - "P", "minimum investment" - ":math:`invest_{max}` ", ":attr:`~SinkDSM.investment.maximum` ", - "P", "maximum investment" - ":math:`E_{exist}` ",":attr:`~SinkDSM.investment.existing` ", - "P", "existing DSM capacity" - ":math:`s_{flex, up}` ",":attr:`~SinkDSM.flex_share_up` ", - "P","Share of invested capacity that may be shift upwards - at maximum" - ":math:`s_{flex, do}` ",":attr:`~SinkDSM.flex_share_do` ", - "P", "Share of invested capacity that may be shift downwards - at maximum" - ":math:`costs_{invest}` ",":attr:`~SinkDSM.investment.epcosts` ", - "P", "specific investment annuity" - """ + """ # noqa: E501 CONSTRAINT_GROUP = True def __init__(self, *args, **kwargs): @@ -1041,125 +1045,125 @@ def _objective_expression(self): class SinkDSMDIWBlock(SimpleBlock): r"""Constraints for SinkDSM with "DIW" approach - **The following constraints are created for approach = 'DIW':** + **The following constraints are created for approach = "DIW":** - .. _SinkDSMDIW equations: + .. _SinkDSMDIWBlock equations: .. math:: & - (1) \quad DSM_{t}^{up} = 0 \quad \forall t - \quad if \space eligibility_{shift} = False \\ + (1) \quad DSM_{t}^{up} = 0 \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shift} = \textrm{False} \\ + & \\ & - (2) \quad DSM_{t}^{do, shed} = 0 \quad \forall t - \quad if \space eligibility_{shed} = False \\ + (2) \quad DSM_{t}^{do, shed} = 0 \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shed} = \textrm{False} \\ + & \\ & (3) \quad \dot{E}_{t} = demand_{t} \cdot demand_{max} + DSM_{t}^{up} - - \sum_{tt=t-L}^{t+L} DSM_{tt,t}^{do, shift} - DSM_{t}^{do, shed} \quad - \forall t \in \mathbb{T} \\ + \sum_{tt=t-L}^{t+L} DSM_{tt,t}^{do, shift} - DSM_{t}^{do, shed} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (4) \quad DSM_{t}^{up} \cdot \eta = - \sum_{tt=t-L}^{t+L} DSM_{t,tt}^{do, shift} - \quad \forall t \in \mathbb{T} \\ + \sum_{tt=t-L}^{t+L} DSM_{t,tt}^{do, shift} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & - (5) \quad DSM_{t}^{up} \leq E_{t}^{up} \cdot E_{up, max} - \quad \forall t \in \mathbb{T} \\ + (5) \quad DSM_{t}^{up} \leq E_{t}^{up} \cdot E_{up, max} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (6) \quad \sum_{t=tt-L}^{tt+L} DSM_{t,tt}^{do, shift} - + DSM_{tt}^{do, shed} \leq E_{tt}^{do} \cdot E_{do, max} - \quad \forall tt \in \mathbb{T} \\ + + DSM_{tt}^{do, shed} \leq E_{tt}^{do} \cdot E_{do, max} \\ + & \quad \quad \quad \quad \forall tt \in \mathbb{T} \\ + & \\ & (7) \quad DSM_{tt}^{up} + \sum_{t=tt-L}^{tt+L} DSM_{t,tt}^{do, shift} + DSM_{tt}^{do, shed} \leq - max \{ E_{tt}^{up} \cdot E_{up, max}, E_{tt}^{do} \cdot E_{do, max} \} - \quad \forall tt \in \mathbb{T} \\ - & - (8) \quad \sum_{tt=t}^{t+R-1} DSM_{tt}^{up} - \leq E_{t}^{up} \cdot E_{up, max} \cdot L \cdot \Delta t - \quad \forall t \in \mathbb{T} \\ + max \{ E_{tt}^{up} \cdot E_{up, max}, + E_{tt}^{do} \cdot E_{do, max} \} \\ + & \quad \quad \quad \quad \forall tt \in \mathbb{T} \\ + & \\ & - (9) \quad \sum_{tt=t}^{t+R-1} DSM_{tt}^{do, shed} - \leq E_{t}^{do} \cdot E_{do, max} \cdot t_{shed} \cdot \Delta t - \quad \forall t \in \mathbb{T} \\ + (8) \quad \sum_{tt=t}^{t+R_{shi}-1} DSM_{tt}^{up} + \leq E_{t}^{up} \cdot E_{up, max} \cdot L \cdot \Delta t \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & + (9) \quad \sum_{tt=t}^{t+R_{she}-1} DSM_{tt}^{do, shed} + \leq E_{t}^{do} \cdot E_{do, max} \cdot t_{shed} \cdot \Delta t \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ - *Note*: For the sake of readability, the handling of indices is not - displayed here. E.g. evaluating a variable for t-L may lead to a negative + + Note + ---- + + For the sake of readability, the handling of indices is not + displayed here. E.g. evaluating a variable for `t-L` may lead to a negative and therefore infeasible index. This is addressed by limiting the sums to non-negative indices within the model index bounds. Please refer to the constraints implementation themselves. + **The following parts of the objective function are created:** .. math:: - DSM_{t}^{up} \cdot cost_{t}^{dsm, up} - + \sum_{tt=0}^{|T|} DSM_{t, tt}^{do, shift} \cdot + & + (DSM_{t}^{up} \cdot cost_{t}^{dsm, up} + + \sum_{tt=0}^{T} DSM_{t, tt}^{do, shift} \cdot cost_{t}^{dsm, do, shift} - + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed} - \quad \forall t \in \mathbb{T} \\ + + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed}) + \cdot \omega_{t} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ **Table: Symbols and attribute names of variables and parameters** - .. csv-table:: Variables (V) and Parameters (P) - :header: "symbol", "attribute", "type", "explanation" - :widths: 1, 1, 1, 1 - - ":math:`DSM_{t}^{up}` ",":attr:`~SinkDSM.dsm_up[g,t]`", - "V", "DSM up shift (additional load) in hour t" - ":math:`DSM_{t,tt}^{do, shift}` ", - ":attr:`~SinkDSM.dsm_do_shift[g,t,tt]`", - "V", "DSM down shift (less load) in hour tt - to compensate for upwards shifts in hour t" - ":math:`DSM_{t}^{do, shed}` ",":attr:`~SinkDSM.dsm_do_shed[g,t]` ", - "V","DSM shedded (capacity shedded, i.e. not compensated for)" - ":math:`\dot{E}_{t}` ",":attr:`flow[g,t]`","V","Energy - flowing in from (electrical) inflow bus" - ":math:`L`",":attr:`~SinkDSM.delay_time`","P", - "Maximum delay time for load shift - (time until the energy balance has to be levelled out again; - roundtrip time of one load shifting cycle, i.e. time window - for upshift and compensating downshift)" - ":math:`t_{she}`",":attr:`~SinkDSM.shed_time`","P", - "Maximum time for one load shedding process" - ":math:`demand_{t}`",":attr:`~SinkDSM.demand[t]`","P", - "(Electrical) demand series (normalized)" - ":math:`demand_{max}`",":attr:`~SinkDSM.max_demand`","P", - "Maximum demand value" - ":math:`E_{t}^{do}`",":attr:`~SinkDSM.capacity_down[t]`","P", - "Capacity allowed for a load adjustment downwards (normalized) - (DSM down shift + DSM shedded)" - ":math:`E_{t}^{up}`",":attr:`~SinkDSM.capacity_up[t]`","P", - "Capacity allowed for a shift upwards (normalized) (DSM up shift)" - ":math:`E_{do, max}`",":attr:`~SinkDSM.max_capacity_down`","P", - "Maximum capacity allowed for a load adjustment downwards - (DSM down shift + DSM shedded)" - ":math:`E_{up, max}`",":attr:`~SinkDSM.max_capacity_up`","P", - "Capacity allowed for a shift upwards (normalized) (DSM up shift)" - ":math:`\eta`",":attr:`~SinkDSM.efficiency`","P", "Efficiency - loss for load shifting processes" - ":math:`\mathbb{T}` "," ","P", "Time steps" - ":math:`eligibility_{shift}` ", - ":attr:`~SinkDSM.shift_eligibility`","P", - "Boolean parameter indicating if unit can be used for - load shifting" - ":math:`eligibility_{shed}` ", - ":attr:`~SinkDSM.shed_eligibility`","P", - "Boolean parameter indicating if unit can be used for - load shedding" - ":math:`cost_{t}^{dsm, up}` ", ":attr:`~SinkDSM.cost_dsm_up[t]`", - "P", "Variable costs for an upwards shift" - ":math:`cost_{t}^{dsm, do, shift}` ", - ":attr:`~SinkDSM.cost_dsm_down_shift[t]`","P", - "Variable costs for a downwards shift (load shifting)" - ":math:`cost_{t}^{dsm, do, shed}` ", - ":attr:`~SinkDSM.cost_dsm_down_shed[t]`","P", - "Variable costs for shedding load" - ":math:`\R`",":attr:`~SinkDSM.recovery_time_shift`","P", - "Minimum time between the end of one load shifting process - and the start of another" - ":math:`\Delta t`",":attr:`~models.Model.timeincrement`","P", - "The time increment of the model" - """ + .. table:: Variables (V) and Parameters (P) + :widths: 1, 1, 1, 1 + + ================================= ======================== ==== ======================================= + symbol attribute type explanation + ================================= ======================== ==== ======================================= + :math:`DSM_{t}^{up}` `dsm_up[g, t]` V DSM up shift (additional load) in hour t + :math:`DSM_{t, tt}^{do, shift}` `dsm_do_shift[g, t, tt]` V | DSM down shift (less load) in hour tt + | to compensate for upwards shifts in hour t + :math:`DSM_{t}^{do, shed}` `dsm_do_shed[g, t]` V DSM shedded (capacity shedded, i.e. not compensated for) + :math:`\dot{E}_{t}` `SinkDSM.inputs` V Energy flowing in from (electrical) inflow bus + :math:`L` `delay_time` P | Maximum delay time for load shift + | (time until the energy balance has to be levelled out again; + | roundtrip time of one load shifting cycle, i.e. time window + | for upshift and compensating downshift) + :math:`t_{she}` `shed_time` P Maximum time for one load shedding process + :math:`demand_{t}` `demand[t]` P (Electrical) demand series (normalized) + :math:`demand_{max}` `max_demand` P Maximum demand value + :math:`E_{t}^{do}` `capacity_down[t]` P | Capacity allowed for a load adjustment downwards + | (normalized; shifting + shedding) + :math:`E_{t}^{up}` `capacity_up[t]` P Capacity allowed for a shift upwards (normalized) + :math:`E_{do, max}` `max_capacity_down` P | Maximum capacity allowed for a load adjustment downwards + | (shifting + shedding) + :math:`E_{up, max}` `max_capacity_up` P Maximum capacity allowed for a shift upwards + :math:`\eta` `efficiency` P Efficiency for load shifting processes + :math:`\mathbb{T}` P Time steps of the model + :math:`e_{shift}` `shift_eligibility` P | Boolean parameter indicating if unit can be used + | for load shifting + :math:`e_{shed}` `shed_eligibility` P | Boolean parameter indicating if unit can be used + | for load shedding + :math:`cost_{t}^{dsm, up}` `cost_dsm_up[t]` P Variable costs for an upwards shift + :math:`cost_{t}^{dsm, do, shift}` `cost_dsm_down_shift[t]` P Variable costs for a downwards shift (load shifting) + :math:`cost_{t}^{dsm, do, shed}` `cost_dsm_down_shift[t]` P Variable costs for shedding load + :math:`\omega_{t}` P Objective weighting of the model for timestep t + :math:`R_{shi}` `recovery_time_shift` P | Minimum time between the end of one load shifting process + | and the start of another + :math:`R_{she}` `recovery_time_shed` P | Minimum time between the end of one load shedding process + | and the start of another + :math:`\Delta t` P The time increment of the model + :math:`\omega_{t}` P Objective weighting of the model for timestep t + ================================= ======================== ==== ======================================= + + """ # noqa E:501 CONSTRAINT_GROUP = True def __init__(self, *args, **kwargs): @@ -1684,113 +1688,127 @@ def _objective_expression(self): class SinkDSMDIWInvestmentBlock(SimpleBlock): - r"""Constraints for SinkDSM with "DIW" approach and :attr:`investment` + CONSTRAINT_GROUP = True + r"""Constraints for SinkDSM with "DIW" approach and `investment` defined - **The following constraints are created for approach = 'DIW' with an + **The following constraints are created for approach = "DIW" with an investment object defined:** - .. _SinkDSMDIW equations: + .. _SinkDSMDIWInvestmentBlock equations: .. math:: & (1) \quad invest_{min} \leq invest \leq invest_{max} \\ + & \\ & - (2) \quad DSM_{t}^{up} = 0 \quad \forall t - \quad if \space eligibility_{shift} = False \\ + (2) \quad DSM_{t}^{up} = 0 \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shift} = \textrm{False} \\ + & \\ & - (3) \quad DSM_{t}^{do, shed} = 0 \quad \forall t - \quad if \space eligibility_{shed} = False \\ + (3) \quad DSM_{t}^{do, shed} = 0 \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shed} = \textrm{False} \\ + & \\ & (4) \quad \dot{E}_{t} = demand_{t} \cdot (invest + E_{exist}) + DSM_{t}^{up} - - \sum_{tt=t-L}^{t+L} DSM_{tt,t}^{do, shift} - DSM_{t}^{do, shed} \quad - \forall t \in \mathbb{T} \\ + \sum_{tt=t-L}^{t+L} DSM_{tt,t}^{do, shift} - DSM_{t}^{do, shed} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (5) \quad DSM_{t}^{up} \cdot \eta = - \sum_{tt=t-L}^{t+L} DSM_{t,tt}^{do, shift} - \quad \forall t \in \mathbb{T} \\ + \sum_{tt=t-L}^{t+L} DSM_{t,tt}^{do, shift} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (6) \quad DSM_{t}^{up} \leq E_{t}^{up} \cdot (invest + E_{exist}) - \ s_{flex, up} - \quad \forall t \in \mathbb{T} \\ + \ s_{flex, up} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (7) \quad \sum_{t=tt-L}^{tt+L} DSM_{t,tt}^{do, shift} + DSM_{tt}^{do, shed} \leq E_{tt}^{do} \cdot (invest + E_{exist}) - \cdot s_{flex, do} - \quad \forall tt \in \mathbb{T} \\ + \cdot s_{flex, do} \\ + & \quad \quad \quad \quad \forall tt \in \mathbb{T} \\ + & \\ & (8) \quad DSM_{tt}^{up} + \sum_{t=tt-L}^{tt+L} DSM_{t,tt}^{do, shift} - + DSM_{tt}^{do, shed} \leq - max \{ E_{tt}^{up} \cdot s_{flex, up}, - E_{tt}^{do} \cdot s_{flex, do} \} \cdot (invest + E_{exist}) - \quad \forall tt \in \mathbb{T} \\ + + DSM_{tt}^{do, shed} \\ + & \quad \quad \leq max \{ E_{tt}^{up} \cdot s_{flex, up}, + E_{tt}^{do} \cdot s_{flex, do} \} \cdot (invest + E_{exist}) \\ + & \quad \quad \quad \quad \forall tt \in \mathbb{T} \\ + & \\ & (9) \quad \sum_{tt=t}^{t+R-1} DSM_{tt}^{up} \leq E_{t}^{up} \cdot (invest + E_{exist}) - \cdot s_{flex, up} \cdot L \cdot \Delta t - \quad \forall t \in \mathbb{T} \\ + \cdot s_{flex, up} \cdot L \cdot \Delta t \\ + & \quad \quad \quad \quad \forall tt \in \mathbb{T} \\ + & \\ & (10) \quad \sum_{tt=t}^{t+R-1} DSM_{tt}^{do, shed} \leq E_{t}^{do} \cdot (invest + E_{exist}) \cdot s_{flex, do} \cdot t_{shed} - \cdot \Delta t \quad \forall t \in \mathbb{T} \\ + \cdot \Delta t \\ + & \quad \quad \quad \quad \forall tt \in \mathbb{T} \\ - *Note*: For the sake of readability, the handling of indices is not - displayed here. E.g. evaluating a variable for t-L may lead to a negative + + Note + ---- + + For the sake of readability, the handling of indices is not + displayed here. E.g. evaluating a variable for `t-L` may lead to a negative and therefore infeasible index. This is addressed by limiting the sums to non-negative indices within the model index bounds. Please refer to the constraints implementation themselves. + **The following parts of the objective function are created:** * Investment annuity: .. math:: + & invest \cdot costs_{invest} \\ * Variable costs: .. math:: - DSM_{t}^{up} \cdot cost_{t}^{dsm, up} + & + (DSM_{t}^{up} \cdot cost_{t}^{dsm, up} + \sum_{tt=0}^{T} DSM_{t, tt}^{do, shift} \cdot cost_{t}^{dsm, do, shift} - + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed} - \quad \forall t \in \mathbb{T} + + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed}) + \cdot \omega_{t} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ **Table: Symbols and attribute names of variables and parameters** - Please refer to - :class:`oemof.solph.components.experimental._sink_dsm.SinkDSMDIWBlock`. + * Please refer to + :class:`oemof.solph.components.experimental._sink_dsm.SinkDSMDIWBlock` + for a variables and parameter description. + * The following variables and parameters are exclusively used for + investment modeling: + + .. table:: Variables (V) and Parameters (P) + :widths: 1, 1, 1, 1 + + ================================= ======================== ==== ======================================= + symbol attribute type explanation + ================================= ======================== ==== ======================================= + :math:`invest` `invest` V | DSM capacity invested in + | Equals to the additionally installed capacity. + | The capacity share eligible for a shift is determined by flex share(s). + :math:`invest_{min}` `investment.minimum` P minimum investment + :math:`invest_{max}` `investment.maximum` P maximum investment + :math:`E_{exist}` `investment.existing` P existing DSM capacity + :math:`s_{flex, up}` `flex_share_up` P share of invested capacity that may be shift upwards at maximum + :math:`s_{flex, do}` `flex_share_do` P share of invested capacity that may be shift downwards at maximum + :math:`costs_{invest}` `investment.ep_costs` P specific investment annuity + ================================= ======================== ==== ======================================= - The following variables and parameters are exclusively used for - investment modeling: - - .. csv-table:: Variables (V) and Parameters (P) - :header: "symbol", "attribute", "type", "explanation" - :widths: 1, 1, 1, 1 - - ":math:`invest` ",":attr:`~SinkDSM.invest` ","V", "DSM capacity - invested in. Equals to the additionally installed capacity. - The capacity share eligible for a shift is determined - by flex share(s)." - ":math:`invest_{min}` ", ":attr:`~SinkDSM.investment.minimum` ", - "P", "minimum investment" - ":math:`invest_{max}` ", ":attr:`~SinkDSM.investment.maximum` ", - "P", "maximum investment" - ":math:`E_{exist}` ",":attr:`~SinkDSM.investment.existing` ", - "P", "existing DSM capacity" - ":math:`s_{flex, up}` ",":attr:`~SinkDSM.flex_share_up` ", - "P","Share of invested capacity that may be shift upwards - at maximum" - ":math:`s_{flex, do}` ",":attr:`~SinkDSM.flex_share_do` ", - "P", "Share of invested capacity that may be shift downwards - at maximum" - ":math:`costs_{invest}` ",":attr:`~SinkDSM.investment.ep_costs` ", - "P", "specific investment annuity" - ":math:`T` "," ","P", "Overall amount of time steps (cardinality)" - """ - CONSTRAINT_GROUP = True + """ # noqa: E501 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -2369,216 +2387,215 @@ def _objective_expression(self): class SinkDSMDLRBlock(SimpleBlock): r"""Constraints for SinkDSM with "DLR" approach - **The following constraints are created for approach = 'DLR':** + **The following constraints are created for approach = "DLR":** - .. _SinkDSMDLR equations: + .. _SinkDSMDLRBlock equations: .. math:: & - (1) \quad DSM_{h, t}^{up} = 0 \quad \forall h \in H_{DR} - \forall t \in \mathbb{T} - \quad if \space eligibility_{shift} = False \\ + (1) \quad DSM_{h, t}^{up} = 0 \\ + & \quad \quad \quad \quad \forall h \in H_{DR}, t \in \mathbb{T} + \quad \textrm{if} \quad e_{shift} = \textrm{False} \\ + & \\ & - (2) \quad DSM_{t}^{do, shed} = 0 \quad \forall t \in \mathbb{T} - \quad if \space eligibility_{shed} = False \\ + (2) \quad DSM_{t}^{do, shed} = 0 \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shed} = \textrm{False} \\ + & \\ & - (3) \quad \dot{E}_{t} = demand_{t} \cdot demand_{max} + - \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + (3) \quad \dot{E}_{t} = demand_{t} \cdot demand_{max} \\ + & \quad \quad \quad \quad + \displaystyle\sum_{h=1}^{H_{DR}} + (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo} - DSM_{h, t}^{do, shift} - - DSM_{h, t}^{balanceUp}) - DSM_{t}^{do, shed} - \quad \forall t \in \mathbb{T} \\ + - DSM_{h, t}^{balanceUp}) - DSM_{t}^{do, shed} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (4) \quad DSM_{h, t}^{balanceDo} = - \frac{DSM_{h, t - h}^{do, shift}}{\eta} - \quad \forall h \in H_{DR} \forall t \in [h..T] \\ + \frac{DSM_{h, t - h}^{do, shift}}{\eta} \\ + & \quad \quad \quad \quad \forall h \in H_{DR}, t \in [h..T] \\ + & \\ & (5) \quad DSM_{h, t}^{balanceUp} = - DSM_{h, t-h}^{up} \cdot \eta - \quad \forall h \in H_{DR} \forall t \in [h..T] \\ + DSM_{h, t-h}^{up} \cdot \eta \\ + & \quad \quad \quad \quad \forall h \in H_{DR}, t \in [h..T] \\ + & \\ & (6) \quad DSM_{h, t}^{do, shift} = 0 - \quad \forall h \in H_{DR} - \forall t \in [T - h..T] \\ + \quad \forall h \in H_{DR} \\ + & \quad \quad \quad \quad \forall t \in [T - h..T] \\ + & \\ & (7) \quad DSM_{h, t}^{up} = 0 - \quad \forall h \in H_{DR} - \forall t \in [T - h..T] \\ + \quad \forall h \in H_{DR} \\ + & \quad \quad \quad \quad \forall t \in [T - h..T] \\ + & \\ & (8) \quad \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{do, shift} + DSM_{h, t}^{balanceUp}) + DSM_{t}^{do, shed} - \leq E_{t}^{do} \cdot E_{max, do} - \quad \forall t \in \mathbb{T} \\ + \leq E_{t}^{do} \cdot E_{max, do} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (9) \quad \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo}) - \leq E_{t}^{up} \cdot E_{max, up} - \quad \forall t \in \mathbb{T} \\ + \leq E_{t}^{up} \cdot E_{max, up} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (10) \quad \Delta t \cdot \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{do, shift} - DSM_{h, t}^{balanceDo} \cdot \eta) - = W_{t}^{levelDo} - W_{t-1}^{levelDo} - \quad \forall t \in [1..T] \\ + = W_{t}^{levelDo} - W_{t-1}^{levelDo} \\ + & \quad \quad \quad \quad \forall t \in [1..T] \\ + & \\ & (11) \quad \Delta t \cdot \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} \cdot \eta - DSM_{h, t}^{balanceUp}) - = W_{t}^{levelUp} - W_{t-1}^{levelUp} - \quad \forall t \in [1..T] \\ + = W_{t}^{levelUp} - W_{t-1}^{levelUp} \\ + & \quad \quad \quad \quad \forall t \in [1..T] \\ + & \\ & (12) \quad W_{t}^{levelDo} \leq \overline{E}_{t}^{do} - \cdot E_{max, do} \cdot t_{shift} - \quad \forall t \in \mathbb{T} \\ + \cdot E_{max, do} \cdot t_{shift} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (13) \quad W_{t}^{levelUp} \leq \overline{E}_{t}^{up} - \cdot E_{max, up} \cdot t_{shift} - \quad \forall t \in \mathbb{T} \\ + \cdot E_{max, up} \cdot t_{shift} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (14) \quad \displaystyle\sum_{t=0}^{T} DSM_{t}^{do, shed} \leq E_{max, do} \cdot \overline{E}_{t}^{do} \cdot t_{shed} \cdot n^{yearLimitShed} \\ + & \\ & (15) \quad \displaystyle\sum_{t=0}^{T} \sum_{h=1}^{H_{DR}} DSM_{h, t}^{do, shift} \leq E_{max, do} \cdot \overline{E}_{t}^{do} \cdot t_{shift} \cdot n^{yearLimitShift} \\ - (optional \space constraint) \\ + & \quad \quad \textrm{(optional constraint)} \\ + & \\ & (16) \quad \displaystyle\sum_{t=0}^{T} \sum_{h=1}^{H_{DR}} DSM_{h, t}^{up} \leq E_{max, up} \cdot \overline{E}_{t}^{up} \cdot t_{shift} \cdot n^{yearLimitShift} \\ - (optional \space constraint) \\ + & \quad \quad \textrm{(optional constraint)} \\ + & \\ & (17) \quad \displaystyle\sum_{h=1}^{H_{DR}} DSM_{h, t}^{do, shift} \leq E_{max, do} \cdot \overline{E}_{t}^{do} \cdot t_{shift} - \displaystyle\sum_{t'=1}^{t_{dayLimit}} \sum_{h=1}^{H_{DR}} - DSM_{h, t - t'}^{do, shift} - \quad \forall t \in [t-t_{dayLimit}..T] \\ - (optional \space constraint) \\ + DSM_{h, t - t'}^{do, shift} \\ + & \quad \quad \quad \quad \forall t \in [t-t_{dayLimit}..T] \\ + & \quad \quad \textrm{(optional constraint)} \\ + & \\ & (18) \quad \displaystyle\sum_{h=1}^{H_{DR}} DSM_{h, t}^{up} \leq E_{max, up} \cdot \overline{E}_{t}^{up} \cdot t_{shift} - \displaystyle\sum_{t'=1}^{t_{dayLimit}} \sum_{h=1}^{H_{DR}} - DSM_{h, t - t'}^{up} - \quad \forall t \in [t-t_{dayLimit}..T] \\ - (optional \space constraint) \\ + DSM_{h, t - t'}^{up} \\ + & \quad \quad \quad \quad \forall t \in [t-t_{dayLimit}..T] \\ + & \quad \quad \textrm{(optional constraint)} \\ + & \\ & (19) \quad \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo} + DSM_{h, t}^{do, shift} + DSM_{h, t}^{balanceUp}) - + DSM_{t}^{do, shed} - \leq \max \{E_{t}^{up} \cdot E_{max, up}, - E_{t}^{do} \cdot E_{max, do} \} - \quad \forall t \in \mathbb{T} \\ - (optional \space constraint) \\ - & + + DSM_{t}^{do, shed} \\ + & \quad \quad \leq \max \{E_{t}^{up} \cdot E_{max, up}, + E_{t}^{do} \cdot E_{max, do} \} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \quad \quad \textrm{(optional constraint)} \\ + - *Note*: For the sake of readability, the handling of indices is not - displayed here. E.g. evaluating a variable for t-L may lead to a negative + Note + ---- + + For the sake of readability, the handling of indices is not + displayed here. E.g. evaluating a variable for `t-L` may lead to a negative and therefore infeasible index. This is addressed by limiting the sums to non-negative indices within the model index bounds. Please refer to the constraints implementation themselves. + **The following parts of the objective function are created:** .. math:: - \sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo}) - \cdot cost_{t}^{dsm, up} - + \sum_{h=1}^{H_{DR}} (DSM_{h, t}^{do, shift} + DSM_{h, t}^{balanceUp}) - \cdot cost_{t}^{dsm, do, shift} - + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed} - \quad \forall t \in \mathbb{T} \\ + & + (\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo}) + \cdot cost_{t}^{dsm, up} \\ + & + \sum_{h=1}^{H_{DR}} (DSM_{h, t}^{do, shift} + + DSM_{h, t}^{balanceUp}) + \cdot cost_{t}^{dsm, do, shift} \\ + & + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed}) + \cdot \omega_{t} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ **Table: Symbols and attribute names of variables and parameters** - .. csv-table:: Variables (V) and Parameters (P) - :header: "symbol", "attribute", "type", "explanation" + .. table:: Variables (V), Parameters (P) and additional Sets (S) :widths: 1, 1, 1, 1 - ":math:`DSM_{h, t}^{up}` ",":attr:`~SinkDSM.dsm_up[g,h,t]`", - "V", "DSM up shift (additional load) in hour t with delay time h" - ":math:`DSM_{h, t}^{do, shift}` ", - ":attr:`~SinkDSM.dsm_do_shift[g,h, t]`", - "V", "DSM down shift (less load) in hour t with delay time h" - ":math:`DSM_{h, t}^{balanceUp}` ", - ":attr:`~SinkDSM.balance_dsm_up[g,h,t]`", - "V", "DSM down shift (less load) in hour t with delay time h - to balance previous upshift" - ":math:`DSM_{h, t}^{balanceDo}` ", - ":attr:`~SinkDSM.balance_dsm_do[g,h,t]`", - "V", "DSM up shift (additional load) in hour t with delay time h - to balance previous downshift" - ":math:`DSM_{t}^{do, shed}` ", - ":attr:`~SinkDSM.dsm_do_shed[g, t]` ", - "V","DSM shedded (capacity shedded, i.e. not compensated for)" - ":math:`\dot{E}_{t}` ",":attr:`flow[g,t]`","V","Energy - flowing in from (electrical) inflow bus" - ":math:`h`","element of :attr:`~SinkDSM.delay_time`","P", - "delay time for load shift (integer value from set of feasible - delay times per DSM portfolio) - (time until the energy balance has to be levelled out again; - roundtrip time of one load shifting cycle, i.e. time window - for upshift and compensating downshift)" - ":math:`H_{DR}`", - "`range(length(:attr:`~SinkDSM.delay_time`) + 1)`", - "P", "Set of feasible delay times for load shift of a certain - DSM portfolio - (time until the energy balance has to be levelled out again; - roundtrip time of one load shifting cycle, i.e. time window - for upshift and compensating downshift)" - ":math:`t_{shift}`",":attr:`~SinkDSM.shift_time`","P", - "Maximum time for a shift in one direction, i. e. maximum time - for an upshift or a downshift in a load shifting cycle" - ":math:`t_{she}`",":attr:`~SinkDSM.shed_time`","P", - "Maximum time for one load shedding process" - ":math:`demand_{t}`",":attr:`~SinkDSM.demand[t]`","P", - "(Electrical) demand series (normalized)" - ":math:`demand_{max}`",":attr:`~SinkDSM.max_demand`","P", - "Maximum demand value" - ":math:`E_{t}^{do}`",":attr:`~SinkDSM.capacity_down[t]`","P", - "Capacity allowed for a load adjustment downwards (normalized) - (DSM down shift + DSM shedded)" - ":math:`E_{t}^{up}`",":attr:`~SinkDSM.capacity_up[t]`","P", - "Capacity allowed for a shift upwards (normalized) (DSM up shift)" - ":math:`E_{do, max}`",":attr:`~SinkDSM.max_capacity_down`","P", - "Maximum capacity allowed for a load adjustment downwards - (DSM down shift + DSM shedded)" - ":math:`E_{up, max}`",":attr:`~SinkDSM.max_capacity_up`","P", - "Capacity allowed for a shift upwards (normalized) (DSM up shift)" - ":math:`\eta`",":attr:`~SinkDSM.efficiency`","P", "Efficiency - loss for load shifting processes" - ":math:`\mathbb{T}` "," ","P", "Set of time steps" - ":math:`T` "," ","P", "Overall amount of time steps (cardinality)" - ":math:`eligibility_{shift}` ", - ":attr:`~SinkDSM.shift_eligibility`","P", - "Boolean parameter indicating if unit can be used for - load shifting" - ":math:`eligibility_{shed}` ", - ":attr:`~SinkDSM.shed_eligibility`","P", - "Boolean parameter indicating if unit can be used for - load shedding" - ":math:`cost_{t}^{dsm, up}` ", ":attr:`~SinkDSM.cost_dsm_up[t]`", - "P", "Variable costs for an upwards shift" - ":math:`cost_{t}^{dsm, do, shift}` ", - ":attr:`~SinkDSM.cost_dsm_down_shift[t]`","P", - "Variable costs for a downwards shift (load shifting)" - ":math:`cost_{t}^{dsm, do, shed}` ", - ":attr:`~SinkDSM.cost_dsm_down_shed[t]`","P", - "Variable costs for shedding load" - ":math:`\Delta t`",":attr:`~models.Model.timeincrement`","P", - "The time increment of the model" - ":math:`n_{yearLimitshift}`",":attr:`~SinkDSM.n_yearLimitShift`", - "P", "Maximum allowed number of load shifts (at full capacity) - in the optimization timeframe" - ":math:`n_{yearLimitshed}`",":attr:`~SinkDSM.n_yearLimitShed`", - "P", "Maximum allowed number of load sheds (at full capacity) - in the optimization timeframe" - ":math:`t_{dayLimit}`",":attr:`~SinkDSM.t_dayLimit`", - "P", "Maximum duration of load shifts at full capacity per day - resp. in the last hours before the current" - """ + =========================================== ================================= ==== ======================================= + symbol attribute type explanation + =========================================== ================================= ==== ======================================= + :math:`DSM_{h, t}^{up}` `dsm_up[g,h,t]` V DSM up shift (additional load) in hour t with delay time h + :math:`DSM_{h, t}^{do, shift}` `dsm_do_shift[g, h, t]` V DSM down shift (less load) in hour t with delay time h + :math:`DSM_{h, t}^{balanceUp}` `balance_dsm_up[g, h, t]` V | DSM down shift (less load) in hour t with delay time h + | to balance previous upshift + :math:`DSM_{h, t}^{balanceDo}` `balance_dsm_do[g, h, t]` V | DSM up shift (additional load) in hour t with delay time h + | to balance previous downshift + :math:`DSM_{t}^{do, shed}` `dsm_do_shed[g, t]` V DSM shedded (capacity shedded, i.e. not compensated for) + :math:`\dot{E}_{t}` `SinkDSM.inputs` V Energy flowing in from (electrical) inflow bus + :math:`h` `delay_time` P | Maximum delay time for load shift + | (integer value from set of feasible delay times per DSM portfolio; + | time until the energy balance has to be levelled out again; + | roundtrip time of one load shifting cycle, i.e. time window + | for upshift and compensating downshift) + :math:`H_{DR}` `range(len(delay_time))` S | Set of feasible delay times for load shift + | of a certain DSM portfolio + :math:`t_{shift}` `shift_time` P | Maximum time for a shift in one direction, + | i. e. maximum time for an upshift *or* a downshift + | in a load shifting cycle + :math:`t_{she}` `shed_time` P Maximum time for one load shedding process + :math:`demand_{t}` `demand[t]` P (Electrical) demand series (normalized) + :math:`demand_{max}` `max_demand` P Maximum demand value + :math:`E_{t}^{do}` `capacity_down[t]` P | Capacity allowed for a load adjustment downwards + | (normalized; shifting + shedding) + :math:`E_{t}^{up}` `capacity_up[t]` P Capacity allowed for a shift upwards (normalized) + :math:`E_{do, max}` `max_capacity_down` P | Maximum capacity allowed for a load adjustment downwards + | (shifting + shedding) + :math:`E_{up, max}` `max_capacity_up` P Maximum capacity allowed for a shift upwards + :math:`\eta` `efficiency` P Efficiency for load shifting processes + :math:`\mathbb{T}` P Time steps of the model + :math:`e_{shift}` `shift_eligibility` P | Boolean parameter indicating if unit can be used + | for load shifting + :math:`e_{shed}` `shed_eligibility` P | Boolean parameter indicating if unit can be used + | for load shedding + :math:`cost_{t}^{dsm, up}` `cost_dsm_up[t]` P Variable costs for an upwards shift + :math:`cost_{t}^{dsm, do, shift}` `cost_dsm_down_shift[t]` P Variable costs for a downwards shift (load shifting) + :math:`cost_{t}^{dsm, do, shed}` `cost_dsm_down_shift[t]` P Variable costs for shedding load + :math:`\omega_{t}` P Objective weighting of the model for timestep t + :math:`R_{shi}` `recovery_time_shift` P | Minimum time between the end of one load shifting process + | and the start of another + :math:`R_{she}` `recovery_time_shed` P | Minimum time between the end of one load shedding process + | and the start of another + :math:`\Delta t` P The time increment of the model + :math:`\omega_{t}` P Objective weighting of the model for timestep t + :math:`n_{yearLimitShift}` `n_yeaLimitShift` P | Maximum allowed number of load shifts (at full capacity) + | in the optimization timeframe + :math:`n_{yearLimitShed}` `n_yeaLimitShed` P | Maximum allowed number of load sheds (at full capacity) + | in the optimization timeframe + :math:`t_{dayLimit}` `t_dayLimit` P | Maximum duration of load shifts at full capacity per day + | resp. in the last hours before the current" + =========================================== ================================= ==== ======================================= + + """ # noqa: E501 CONSTRAINT_GROUP = True def __init__(self, *args, **kwargs): @@ -3331,84 +3348,99 @@ def _objective_expression(self): return self.cost -class SinkDSMDLRInvestmentBlock(SinkDSMDLRBlock): - r"""Constraints for SinkDSM with "DLR" approach and :attr:`investment` +class SinkDSMDLRInvestmentBlock(SimpleBlock): + r"""Constraints for SinkDSM with "DLR" approach and `investment` defined - **The following constraints are created for approach = 'DLR' with an + **The following constraints are created for approach = "DLR" with an investment object defined:** - .. _SinkDSMDLR equations: + .. _SinkDSMDLRInvestmentBlock equations: .. math:: & (1) \quad invest_{min} \leq invest \leq invest_{max} \\ + & \\ & - (2) \quad DSM_{h, t}^{up} = 0 \quad \forall h \in H_{DR} - \forall t \in \mathbb{T} - \quad if \space eligibility_{shift} = False \\ + (2) \quad DSM_{h, t}^{up} = 0 \\ + & \quad \quad \quad \quad \forall h \in H_{DR}, t \in \mathbb{T} + \quad \textrm{if} \quad e_{shift} = \textrm{False} \\ & - (3) \quad DSM_{t}^{do, shed} = 0 \quad \forall t \in \mathbb{T} - \quad if \space eligibility_{shed} = False \\ + (3) \quad DSM_{t}^{do, shed} = 0 \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} + \quad \textrm{if} \quad e_{shed} = \textrm{False} \\ + & \\ & - (4) \quad \dot{E}_{t} = demand_{t} \cdot (invest + E_{exist}) - + \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + (4) \quad \dot{E}_{t} = demand_{t} \cdot (invest + E_{exist}) \\ + & \quad \quad \quad \quad + \displaystyle\sum_{h=1}^{H_{DR}} + (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo} - DSM_{h, t}^{do, shift} - - DSM_{h, t}^{balanceUp}) - DSM_{t}^{do, shed} - \quad \forall t \in \mathbb{T} \\ + - DSM_{h, t}^{balanceUp}) - DSM_{t}^{do, shed} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (5) \quad DSM_{h, t}^{balanceDo} = - \frac{DSM_{h, t - h}^{do, shift}}{\eta} - \quad \forall h \in H_{DR} \forall t \in [h..T] \\ + \frac{DSM_{h, t - h}^{do, shift}}{\eta} \\ + & \quad \quad \quad \quad \forall h \in H_{DR}, t \in [h..T] \\ + & \\ & (6) \quad DSM_{h, t}^{balanceUp} = - DSM_{h, t-h}^{up} \cdot \eta - \quad \forall h \in H_{DR} \forall t \in [h..T] \\ + DSM_{h, t-h}^{up} \cdot \eta \\ + & \quad \quad \quad \quad \forall h \in H_{DR}, t \in [h..T] \\ + & \\ & (7) \quad DSM_{h, t}^{do, shift} = 0 - \quad \forall h \in H_{DR} - \forall t \in [T - h..T] \\ + \quad \forall h \in H_{DR} \\ + & \quad \quad \quad \quad \forall t \in [T - h..T] \\ + & \\ & - (8) \quad DSM_{h, t}^{up} = 0 - \quad \forall h \in H_{DR} - \forall t \in [T - h..T] \\ + (8) \quad DSM_{h, t}^{up} = 0 \\ + & \quad \quad \quad \quad \forall h \in H_{DR}, t \in [T - h..T] \\ + & \\ & (9) \quad \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{do, shift} + DSM_{h, t}^{balanceUp}) + DSM_{t}^{do, shed} \leq E_{t}^{do} \cdot (invest + E_{exist}) - \cdot s_{flex, do} - \quad \forall t \in \mathbb{T} \\ + \cdot s_{flex, do} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (10) \quad \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo}) \leq E_{t}^{up} \cdot (invest + E_{exist}) - \cdot s_{flex, up} - \quad \forall t \in \mathbb{T} \\ + \cdot s_{flex, up} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (11) \quad \Delta t \cdot \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{do, shift} - DSM_{h, t}^{balanceDo} \cdot \eta) - = W_{t}^{levelDo} - W_{t-1}^{levelDo} - \quad \forall t \in [1..T] \\ + = W_{t}^{levelDo} - W_{t-1}^{levelDo} \\ + & \quad \quad \quad \quad \forall t \in [1..T] \\ + & \\ & (12) \quad \Delta t \cdot \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} \cdot \eta - DSM_{h, t}^{balanceUp}) - = W_{t}^{levelUp} - W_{t-1}^{levelUp} - \quad \forall t \in [1..T] \\ + = W_{t}^{levelUp} - W_{t-1}^{levelUp} \\ + & \quad \quad \quad \quad \forall t \in [1..T] \\ + & \\ & (13) \quad W_{t}^{levelDo} \leq \overline{E}_{t}^{do} \cdot (invest + E_{exist}) - \cdot s_{flex, do} \cdot t_{shift} - \quad \forall t \in \mathbb{T} \\ + \cdot s_{flex, do} \cdot t_{shift} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (14) \quad W_{t}^{levelUp} \leq \overline{E}_{t}^{up} \cdot (invest + E_{exist}) - \cdot s_{flex, up} \cdot t_{shift} - \quad \forall t \in \mathbb{T} \\ + \cdot s_{flex, up} \cdot t_{shift} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \\ & (15) \quad \displaystyle\sum_{t=0}^{T} DSM_{t}^{do, shed} \leq (invest + E_{exist}) \cdot s_{flex, do} \cdot \overline{E}_{t}^{do} \cdot t_{shed} \cdot n^{yearLimitShed} \\ + & \\ & (16) \quad \displaystyle\sum_{t=0}^{T} \sum_{h=1}^{H_{DR}} DSM_{h, t}^{do, shift} @@ -3416,7 +3448,8 @@ class SinkDSMDLRInvestmentBlock(SinkDSMDLRBlock): \cdot s_{flex, do} \cdot \overline{E}_{t}^{do} \cdot t_{shift} \cdot n^{yearLimitShift} \\ - (optional \space constraint) \\ + & \quad \quad \textrm{(optional constraint)} \\ + & \\ & (17) \quad \displaystyle\sum_{t=0}^{T} \sum_{h=1}^{H_{DR}} DSM_{h, t}^{up} @@ -3424,91 +3457,95 @@ class SinkDSMDLRInvestmentBlock(SinkDSMDLRBlock): \cdot s_{flex, up} \cdot \overline{E}_{t}^{up} \cdot t_{shift} \cdot n^{yearLimitShift} \\ - (optional \space constraint) \\ + & \quad \quad \textrm{(optional constraint)} \\ & (18) \quad \displaystyle\sum_{h=1}^{H_{DR}} DSM_{h, t}^{do, shift} \leq (invest + E_{exist}) \cdot s_{flex, do} \cdot \overline{E}_{t}^{do} \cdot t_{shift} - \displaystyle\sum_{t'=1}^{t_{dayLimit}} \sum_{h=1}^{H_{DR}} - DSM_{h, t - t'}^{do, shift} - \quad \forall t \in [t-t_{dayLimit}..T] \\ - (optional \space constraint) \\ + DSM_{h, t - t'}^{do, shift} \\ + & \quad \quad \quad \quad \forall t \in [t-t_{dayLimit}..T] \\ + & \quad \quad \textrm{(optional constraint)} \\ + & \\ & (19) \quad \displaystyle\sum_{h=1}^{H_{DR}} DSM_{h, t}^{up} \leq (invest + E_{exist}) \cdot s_{flex, up} \cdot \overline{E}_{t}^{up} \cdot t_{shift} - \displaystyle\sum_{t'=1}^{t_{dayLimit}} \sum_{h=1}^{H_{DR}} - DSM_{h, t - t'}^{up} - \quad \forall t \in [t-t_{dayLimit}..T] \\ - (optional \space constraint) \\ + DSM_{h, t - t'}^{up} \\ + & \quad \quad \quad \quad \forall t \in [t-t_{dayLimit}..T] \\ + & \quad \quad \textrm{(optional constraint)} \\ + & \\ & (20) \quad \displaystyle\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo} + DSM_{h, t}^{do, shift} + DSM_{h, t}^{balanceUp}) - + DSM_{t}^{shed} - \leq \max \{E_{t}^{up} \cdot s_{flex, up}, - E_{t}^{do} \cdot s_{flex, do} \} \cdot (invest + E_{exist}) - \quad \forall t \in \mathbb{T} \\ - (optional \space constraint) \\ - & + + DSM_{t}^{shed} \\ + & \quad \quad \leq \max \{E_{t}^{up} \cdot s_{flex, up}, + E_{t}^{do} \cdot s_{flex, do} \} \cdot (invest + E_{exist}) \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ + & \quad \quad \textrm{(optional constraint)} \\ + - *Note*: For the sake of readability, the handling of indices is not - displayed here. E.g. evaluating a variable for t-L may lead to a negative + Note + ---- + + For the sake of readability, the handling of indices is not + displayed here. E.g. evaluating a variable for `t-L` may lead to a negative and therefore infeasible index. This is addressed by limiting the sums to non-negative indices within the model index bounds. Please refer to the constraints implementation themselves. + **The following parts of the objective function are created:** * Investment annuity: .. math:: + & invest \cdot costs_{invest} \\ * Variable costs: .. math:: - \sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo}) - \cdot cost_{t}^{dsm, up} - + \sum_{h=1}^{H_{DR}} (DSM_{h, t}^{do, shift} + DSM_{h, t}^{balanceUp}) - \cdot cost_{t}^{dsm, do, shift} - + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed} - \quad \forall t \in \mathbb{T} \\ + & + (\sum_{h=1}^{H_{DR}} (DSM_{h, t}^{up} + DSM_{h, t}^{balanceDo}) + \cdot cost_{t}^{dsm, up} \\ + & + \sum_{h=1}^{H_{DR}} (DSM_{h, t}^{do, shift} + + DSM_{h, t}^{balanceUp}) + \cdot cost_{t}^{dsm, do, shift} \\ + & + DSM_{t}^{do, shed} \cdot cost_{t}^{dsm, do, shed}) + \cdot \omega_{t} \\ + & \quad \quad \quad \quad \forall t \in \mathbb{T} \\ **Table: Symbols and attribute names of variables and parameters** - Please refer to - :class:`oemof.solph.components.experimental._sink_dsm.SinkDSMDLRBlock`. + * Please refer to + :class:`oemof.solph.components.experimental._sink_dsm.SinkDSMDLRBlock`. + * The following variables and parameters are exclusively used for + investment modeling: - The following variables and parameters are exclusively used for - investment modeling: - - .. csv-table:: Variables (V) and Parameters (P) - :header: "symbol", "attribute", "type", "explanation" + .. table:: Variables (V) and Parameters (P) :widths: 1, 1, 1, 1 - ":math:`invest` ",":attr:`~SinkDSM.invest` ","V", "DSM capacity - invested in. Equals to the additionally installed capacity. - The capacity share eligible for a shift is determined - by flex share(s)." - ":math:`invest_{min}` ", ":attr:`~SinkDSM.investment.minimum` ", - "P", "minimum investment" - ":math:`invest_{max}` ", ":attr:`~SinkDSM.investment.maximum` ", - "P", "maximum investment" - ":math:`E_{exist}` ",":attr:`~SinkDSM.investment.existing` ", - "P", "existing DSM capacity" - ":math:`s_{flex, up}` ",":attr:`~SinkDSM.flex_share_up` ", - "P","Share of invested capacity that may be shift upwards - at maximum" - ":math:`s_{flex, do}` ",":attr:`~SinkDSM.flex_share_do` ", - "P", "Share of invested capacity that may be shift downwards - at maximum" - ":math:`costs_{invest}` ",":attr:`~SinkDSM.investment.ep_costs` ", - "P", "specific investment annuity" - """ + ================================= ======================== ==== ======================================= + symbol attribute type explanation + ================================= ======================== ==== ======================================= + :math:`invest` `invest` V | DSM capacity invested in + | Equals to the additionally installed capacity. + | The capacity share eligible for a shift is determined by flex share(s). + :math:`invest_{min}` `investment.minimum` P minimum investment + :math:`invest_{max}` `investment.maximum` P maximum investment + :math:`E_{exist}` `investment.existing` P existing DSM capacity + :math:`s_{flex, up}` `flex_share_up` P share of invested capacity that may be shift upwards at maximum + :math:`s_{flex, do}` `flex_share_do` P share of invested capacity that may be shift downwards at maximum + :math:`costs_{invest}` `investment.ep_costs` P specific investment annuity + ================================= ======================== ==== ======================================= + + """ # noqa: E501 CONSTRAINT_GROUP = True def __init__(self, *args, **kwargs): From ca4e348b953146a91868d2a97ac0784142ae9e15 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Tue, 8 Mar 2022 17:12:20 +0100 Subject: [PATCH 0176/1363] Add / fix api reference --- docs/reference/oemof.solph.rst | 202 ++++++++++++++++++++------------- 1 file changed, 123 insertions(+), 79 deletions(-) diff --git a/docs/reference/oemof.solph.rst b/docs/reference/oemof.solph.rst index c7bbf159a..91e1fcc2b 100644 --- a/docs/reference/oemof.solph.rst +++ b/docs/reference/oemof.solph.rst @@ -4,227 +4,271 @@ oemof.solph package Submodules ---------- -oemof.solph.EnergySystem ------------------------- +oemof.solph.buses +----------------- -.. automodule:: oemof.solph.network.energy_system +.. automodule:: oemof.solph.buses._bus :members: :undoc-members: :show-inheritance: -oemof.solph.buses.Bus ---------------- +oemof.solph.flows +----------------- -.. automodule:: oemof.solph.network.bus +oemof.solph.flows._flow ++++++++++++++++++++++++ + +.. automodule:: oemof.solph.flows._flow :members: :undoc-members: :show-inheritance: -.. automodule:: oemof.solph.blocks.bus +oemof.solph.flows._investment_flow +++++++++++++++++++++++++++++++++++ + +.. automodule:: oemof.solph.flows._investment_flow :members: :undoc-members: :show-inheritance: -oemof.solph.flows.Flow ----------------- +oemof.solph.flows._non_convex_flow +++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.network.flow +.. automodule:: oemof.solph.flows._non_convex_flow :members: :undoc-members: :show-inheritance: -.. automodule:: oemof.solph.blocks.flow +oemof.solph.components +---------------------- + +oemof.solph.components._extraction_turbine +++++++++++++++++++++++++++++++++++++++++++ + +.. automodule:: oemof.solph.components._extraction_turbine_chp :members: :undoc-members: :show-inheritance: -.. automodule:: oemof.solph.blocks.investment_flow +oemof.solph.components._generic_chp ++++++++++++++++++++++++++++++++++++ + +.. automodule:: oemof.solph.components._generic_chp :members: :undoc-members: :show-inheritance: -.. automodule:: oemof.solph.blocks.non_convex_flow +oemof.solph.components._generic_storage ++++++++++++++++++++++++++++++++++++++++ + +.. automodule:: oemof.solph.components._generic_storage :members: :undoc-members: :show-inheritance: -oemof.solph.components.Sink ----------------- +oemof.solph.components._offset_transformer +++++++++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.network.sink +.. automodule:: oemof.solph.components._offset_transformer :members: :undoc-members: :show-inheritance: -oemof.solph.components.Source ------------------- +oemof.solph.components._sink +++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.network.source +.. automodule:: oemof.solph.components._sink :members: :undoc-members: :show-inheritance: -oemof.solph.components.Transformer ------------------------ +oemof.solph.components._source +++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.network.transformer +.. automodule:: oemof.solph.components._source :members: :undoc-members: :show-inheritance: -.. automodule:: oemof.solph.blocks.transformer +oemof.solph.components._transformer ++++++++++++++++++++++++++++++++++++ + +.. automodule:: oemof.solph.components._transformer :members: :undoc-members: :show-inheritance: -oemof.solph.components._extractionTurbineCHP -------------------------------------------- +oemof.solph.constraints module +------------------------------ -.. automodule:: oemof.solph.components._extraction_turbine_chp +oemof.solph.components.equate_variables +++++++++++++++++++++++++++++++++++++++++ + +.. automodule:: oemof.solph.constraints.equate_variables :members: :undoc-members: :show-inheritance: -oemof.solph.components.GenericCHP ---------------------------------- +oemof.solph.components.flow_count_limit +++++++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.components._generic_chp +.. automodule:: oemof.solph.constraints.flow_count_limit :members: :undoc-members: :show-inheritance: -oemof.solph.components.GenericStorage -------------------------------------- +oemof.solph.components.integral_limit +++++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.components._generic_storage +.. automodule:: oemof.solph.constraints.integral_limit :members: :undoc-members: :show-inheritance: -oemof.solph.components.OffsetTransformer ----------------------------------------- +oemof.solph.components.investment_limit +++++++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.components._offset_transformer +.. automodule:: oemof.solph.constraints.investment_limit :members: :undoc-members: :show-inheritance: -oemof.solph.constraints module ------------------------------- +oemof.solph.components.shared_limit +++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.constraints - :members: equate_variables, limit_active_flow_count, limit_active_flow_count_by_keyword, emission_limit, generic_integral_limit, additional_investment_flow_limit, investment_limit, shared_limit +.. automodule:: oemof.solph.constraints.shared_limit + :members: :undoc-members: :show-inheritance: oemof.solph.console\_scripts module ----------------------------------- -.. automodule:: oemof.solph.console_scripts +.. automodule:: oemof.solph._console_scripts :members: :undoc-members: :show-inheritance: -oemof.solph.custom.ElectricalLine ---------------------------------- +oemof.solph._energy_system +-------------------------- -.. automodule:: oemof.solph.custom.electrical_line +.. automodule:: oemof.solph._energy_system :members: :undoc-members: :show-inheritance: -oemof.solph.custom.GenericCAES ------------------------------- +oemof.solph._groupings +---------------------- -.. automodule:: oemof.solph.custom.generic_caes +.. automodule:: oemof.solph._groupings :members: :undoc-members: :show-inheritance: -oemof.solph.custom.Link ------------------------ +oemof.solph._helpers +-------------------- -.. automodule:: oemof.solph.custom.link +.. automodule:: oemof.solph._helpers :members: :undoc-members: :show-inheritance: -oemof.solph.custom.PiecewiseLinearTransformer ---------------------------------------------- +oemof.solph._models +------------------- -.. automodule:: oemof.solph.custom.piecewise_linear_transformer +.. automodule:: oemof.solph._models :members: :undoc-members: :show-inheritance: -oemof.solph.custom.SinkDSM ---------------------------------------------- +oemof.solph._options +-------------------- -.. automodule:: oemof.solph.custom.sink_dsm +.. automodule:: oemof.solph._options :members: :undoc-members: :show-inheritance: -oemof.solph.groupings module ----------------------------- +oemof.solph._plumbing +--------------------- -.. automodule:: oemof.solph.groupings +.. automodule:: oemof.solph._plumbing :members: :undoc-members: :show-inheritance: -oemof.solph.helpers module --------------------------- +oemof.solph.processing +---------------------- -.. automodule:: oemof.solph.helpers +.. automodule:: oemof.solph.processing :members: :undoc-members: :show-inheritance: -oemof.solph.models module -------------------------- +oemof.solph.views +----------------- -.. automodule:: oemof.solph.models +.. automodule:: oemof.solph.views :members: :undoc-members: :show-inheritance: -oemof.solph.network module --------------------------- +Experimental submodules +----------------------- + +oemof.solph.buses.experimental +------------------------------ -.. automodule:: oemof.solph.network +oemof.solph.buses.experimental._electrical_bus +++++++++++++++++++++++++++++++++++++++++++++++ + +.. automodule:: oemof.solph.buses.experimental._electrical_bus :members: :undoc-members: :show-inheritance: -oemof.solph.options module --------------------------- +oemof.solph.components.experimental +----------------------------------- + +oemof.solph.components.experimental._generic_caes ++++++++++++++++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.options +.. automodule:: oemof.solph.components.experimental._generic_caes :members: :undoc-members: :show-inheritance: -oemof.solph._plumbing module ---------------------------- +oemof.solph.components.experimental._link ++++++++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.plumbing +.. automodule:: oemof.solph.components.experimental._link :members: :undoc-members: :show-inheritance: -oemof.solph.processing module ---------------------------------- +oemof.solph.components.experimental._piecewise_linear_transformer ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.processing +.. automodule:: oemof.solph.components.experimental._piecewise_linear_transformer :members: :undoc-members: :show-inheritance: -oemof.solph.views module ---------------------------------- +oemof.solph.components.experimental._sink_dsm ++++++++++++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.views +.. automodule:: oemof.solph.components.experimental._sink_dsm + :members: + :undoc-members: + :show-inheritance: + +oemof.solph.flows.experimental +------------------------------ + +oemof.solph.flows.experimental._electrical_line ++++++++++++++++++++++++++++++++++++++++++++++++ + +.. automodule:: oemof.solph.flows.experimental._electrical_line :members: :undoc-members: :show-inheritance: From 67ab6eb9218301e25634fce3ad82c4e0d5c1b6fe Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Tue, 8 Mar 2022 17:12:20 +0100 Subject: [PATCH 0177/1363] Add / fix api reference --- docs/reference/oemof.solph.rst | 204 ++++++++++++++++++++------------- 1 file changed, 125 insertions(+), 79 deletions(-) diff --git a/docs/reference/oemof.solph.rst b/docs/reference/oemof.solph.rst index c7bbf159a..df1326e64 100644 --- a/docs/reference/oemof.solph.rst +++ b/docs/reference/oemof.solph.rst @@ -1,230 +1,276 @@ oemof.solph package =================== +---------- Submodules ---------- -oemof.solph.EnergySystem ------------------------- +oemof.solph.buses +----------------- -.. automodule:: oemof.solph.network.energy_system +.. automodule:: oemof.solph.buses._bus :members: :undoc-members: :show-inheritance: -oemof.solph.buses.Bus ---------------- +oemof.solph.flows +----------------- + +oemof.solph.flows._flow ++++++++++++++++++++++++ -.. automodule:: oemof.solph.network.bus +.. automodule:: oemof.solph.flows._flow :members: :undoc-members: :show-inheritance: -.. automodule:: oemof.solph.blocks.bus +oemof.solph.flows._investment_flow +++++++++++++++++++++++++++++++++++ + +.. automodule:: oemof.solph.flows._investment_flow :members: :undoc-members: :show-inheritance: -oemof.solph.flows.Flow ----------------- +oemof.solph.flows._non_convex_flow +++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.network.flow +.. automodule:: oemof.solph.flows._non_convex_flow :members: :undoc-members: :show-inheritance: -.. automodule:: oemof.solph.blocks.flow +oemof.solph.components +---------------------- + +oemof.solph.components._extraction_turbine +++++++++++++++++++++++++++++++++++++++++++ + +.. automodule:: oemof.solph.components._extraction_turbine_chp :members: :undoc-members: :show-inheritance: -.. automodule:: oemof.solph.blocks.investment_flow +oemof.solph.components._generic_chp ++++++++++++++++++++++++++++++++++++ + +.. automodule:: oemof.solph.components._generic_chp :members: :undoc-members: :show-inheritance: -.. automodule:: oemof.solph.blocks.non_convex_flow +oemof.solph.components._generic_storage ++++++++++++++++++++++++++++++++++++++++ + +.. automodule:: oemof.solph.components._generic_storage :members: :undoc-members: :show-inheritance: -oemof.solph.components.Sink ----------------- +oemof.solph.components._offset_transformer +++++++++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.network.sink +.. automodule:: oemof.solph.components._offset_transformer :members: :undoc-members: :show-inheritance: -oemof.solph.components.Source ------------------- +oemof.solph.components._sink +++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.network.source +.. automodule:: oemof.solph.components._sink :members: :undoc-members: :show-inheritance: -oemof.solph.components.Transformer ------------------------ +oemof.solph.components._source +++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.network.transformer +.. automodule:: oemof.solph.components._source :members: :undoc-members: :show-inheritance: -.. automodule:: oemof.solph.blocks.transformer +oemof.solph.components._transformer ++++++++++++++++++++++++++++++++++++ + +.. automodule:: oemof.solph.components._transformer :members: :undoc-members: :show-inheritance: -oemof.solph.components._extractionTurbineCHP -------------------------------------------- +oemof.solph.constraints module +------------------------------ + +oemof.solph.components.equate_variables +++++++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.components._extraction_turbine_chp +.. automodule:: oemof.solph.constraints.equate_variables :members: :undoc-members: :show-inheritance: -oemof.solph.components.GenericCHP ---------------------------------- +oemof.solph.components.flow_count_limit +++++++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.components._generic_chp +.. automodule:: oemof.solph.constraints.flow_count_limit :members: :undoc-members: :show-inheritance: -oemof.solph.components.GenericStorage -------------------------------------- +oemof.solph.components.integral_limit +++++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.components._generic_storage +.. automodule:: oemof.solph.constraints.integral_limit :members: :undoc-members: :show-inheritance: -oemof.solph.components.OffsetTransformer ----------------------------------------- +oemof.solph.components.investment_limit +++++++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.components._offset_transformer +.. automodule:: oemof.solph.constraints.investment_limit :members: :undoc-members: :show-inheritance: -oemof.solph.constraints module ------------------------------- +oemof.solph.components.shared_limit +++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.constraints - :members: equate_variables, limit_active_flow_count, limit_active_flow_count_by_keyword, emission_limit, generic_integral_limit, additional_investment_flow_limit, investment_limit, shared_limit +.. automodule:: oemof.solph.constraints.shared_limit + :members: :undoc-members: :show-inheritance: oemof.solph.console\_scripts module ----------------------------------- -.. automodule:: oemof.solph.console_scripts +.. automodule:: oemof.solph._console_scripts :members: :undoc-members: :show-inheritance: -oemof.solph.custom.ElectricalLine ---------------------------------- +oemof.solph._energy_system +-------------------------- -.. automodule:: oemof.solph.custom.electrical_line +.. automodule:: oemof.solph._energy_system :members: :undoc-members: :show-inheritance: -oemof.solph.custom.GenericCAES ------------------------------- +oemof.solph._groupings +---------------------- -.. automodule:: oemof.solph.custom.generic_caes +.. automodule:: oemof.solph._groupings :members: :undoc-members: :show-inheritance: -oemof.solph.custom.Link ------------------------ +oemof.solph._helpers +-------------------- -.. automodule:: oemof.solph.custom.link +.. automodule:: oemof.solph._helpers :members: :undoc-members: :show-inheritance: -oemof.solph.custom.PiecewiseLinearTransformer ---------------------------------------------- +oemof.solph._models +------------------- -.. automodule:: oemof.solph.custom.piecewise_linear_transformer +.. automodule:: oemof.solph._models :members: :undoc-members: :show-inheritance: -oemof.solph.custom.SinkDSM ---------------------------------------------- +oemof.solph._options +-------------------- -.. automodule:: oemof.solph.custom.sink_dsm +.. automodule:: oemof.solph._options :members: :undoc-members: :show-inheritance: -oemof.solph.groupings module ----------------------------- +oemof.solph._plumbing +--------------------- -.. automodule:: oemof.solph.groupings +.. automodule:: oemof.solph._plumbing :members: :undoc-members: :show-inheritance: -oemof.solph.helpers module --------------------------- +oemof.solph.processing +---------------------- -.. automodule:: oemof.solph.helpers +.. automodule:: oemof.solph.processing :members: :undoc-members: :show-inheritance: -oemof.solph.models module -------------------------- +oemof.solph.views +----------------- -.. automodule:: oemof.solph.models +.. automodule:: oemof.solph.views :members: :undoc-members: :show-inheritance: -oemof.solph.network module --------------------------- +----------------------- +Experimental submodules +----------------------- + +oemof.solph.buses.experimental +------------------------------ + +oemof.solph.buses.experimental._electrical_bus +++++++++++++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.network +.. automodule:: oemof.solph.buses.experimental._electrical_bus :members: :undoc-members: :show-inheritance: -oemof.solph.options module --------------------------- +oemof.solph.components.experimental +----------------------------------- + +oemof.solph.components.experimental._generic_caes ++++++++++++++++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.options +.. automodule:: oemof.solph.components.experimental._generic_caes :members: :undoc-members: :show-inheritance: -oemof.solph._plumbing module ---------------------------- +oemof.solph.components.experimental._link ++++++++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.plumbing +.. automodule:: oemof.solph.components.experimental._link :members: :undoc-members: :show-inheritance: -oemof.solph.processing module ---------------------------------- +oemof.solph.components.experimental._piecewise_linear_transformer ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.processing +.. automodule:: oemof.solph.components.experimental._piecewise_linear_transformer :members: :undoc-members: :show-inheritance: -oemof.solph.views module ---------------------------------- +oemof.solph.components.experimental._sink_dsm ++++++++++++++++++++++++++++++++++++++++++++++ -.. automodule:: oemof.solph.views +.. automodule:: oemof.solph.components.experimental._sink_dsm + :members: + :undoc-members: + :show-inheritance: + +oemof.solph.flows.experimental +------------------------------ + +oemof.solph.flows.experimental._electrical_line ++++++++++++++++++++++++++++++++++++++++++++++++ + +.. automodule:: oemof.solph.flows.experimental._electrical_line :members: :undoc-members: :show-inheritance: From afd7a6d12195e37d1cc3f26345e3f56113a8cb28 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Tue, 8 Mar 2022 17:44:52 +0100 Subject: [PATCH 0178/1363] Fix black formatting and add no constraints info --- src/oemof/solph/components/_sink.py | 3 +- src/oemof/solph/components/_source.py | 3 +- .../components/experimental/_sink_dsm.py | 28 ++++++------------- tests/constraint_tests.py | 4 +-- .../test_piecewiselineartransformer.py | 2 +- 5 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/oemof/solph/components/_sink.py b/src/oemof/solph/components/_sink.py index eefb0f420..a8d3fa4ca 100644 --- a/src/oemof/solph/components/_sink.py +++ b/src/oemof/solph/components/_sink.py @@ -8,6 +8,7 @@ SPDX-FileCopyrightText: Cord Kaldemeyer SPDX-FileCopyrightText: Stephan Günther SPDX-FileCopyrightText: Birgit Schachler +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -19,7 +20,7 @@ class Sink(on.Sink): - """An object with one input flow.""" + """An object with one input flow and without any constraints.""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/src/oemof/solph/components/_source.py b/src/oemof/solph/components/_source.py index 73a974d36..d295695a6 100644 --- a/src/oemof/solph/components/_source.py +++ b/src/oemof/solph/components/_source.py @@ -8,6 +8,7 @@ SPDX-FileCopyrightText: Cord Kaldemeyer SPDX-FileCopyrightText: Stephan Günther SPDX-FileCopyrightText: Birgit Schachler +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -19,7 +20,7 @@ class Source(on.Source): - """An object with one output flow.""" + """An object with one output flow and without any constraints.""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index 1b5af1ef3..eb1fa857e 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -2161,13 +2161,10 @@ def c2_constraint_rule(block): + self.dsm_do_shed[g, tt] ) # max capacity at tt - rhs = ( - max( + rhs = max( g.capacity_up[tt] * g.flex_share_up, g.capacity_down[tt] * g.flex_share_down, - ) - * (self.invest[g] + g.investment.existing) - ) + ) * (self.invest[g] + g.investment.existing) # add constraint block.C2_constraint.add((g, tt), (lhs <= rhs)) @@ -2186,13 +2183,10 @@ def c2_constraint_rule(block): + self.dsm_do_shed[g, tt] ) # max capacity at tt - rhs = ( - max( + rhs = max( g.capacity_up[tt] * g.flex_share_up, g.capacity_down[tt] * g.flex_share_down, - ) - * (self.invest[g] + g.investment.existing) - ) + ) * (self.invest[g] + g.investment.existing) # add constraint block.C2_constraint.add((g, tt), (lhs <= rhs)) @@ -2211,13 +2205,10 @@ def c2_constraint_rule(block): + self.dsm_do_shed[g, tt] ) # max capacity at tt - rhs = ( - max( + rhs = max( g.capacity_up[tt] * g.flex_share_up, g.capacity_down[tt] * g.flex_share_down, - ) - * (self.invest[g] + g.investment.existing) - ) + ) * (self.invest[g] + g.investment.existing) # add constraint block.C2_constraint.add((g, tt), (lhs <= rhs)) @@ -4271,13 +4262,10 @@ def dr_logical_constraint_rule(block): ) # maximum capacity eligibly for load shifting - rhs = ( - max( + rhs = max( g.capacity_down[t] * g.flex_share_down, g.capacity_up[t] * g.flex_share_up, - ) - * (self.invest[g] + g.investment.existing) - ) + ) * (self.invest[g] + g.investment.existing) # add constraint block.dr_logical_constraint.add((g, t), (lhs <= rhs)) diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index 76df0e2be..a33f7e3b9 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -1043,7 +1043,7 @@ def test_piecewise_linear_transformer_cc(self): }, outputs={bel: solph.flows.Flow()}, in_breakpoints=[0, 25, 50, 75, 100], - conversion_function=lambda x: x ** 2, + conversion_function=lambda x: x**2, pw_repn="CC", ) self.compare_lp_files("piecewise_linear_transformer_cc.lp") @@ -1059,7 +1059,7 @@ def test_piecewise_linear_transformer_dcc(self): }, outputs={bel: solph.flows.Flow()}, in_breakpoints=[0, 25, 50, 75, 100], - conversion_function=lambda x: x ** 2, + conversion_function=lambda x: x**2, pw_repn="DCC", ) self.compare_lp_files("piecewise_linear_transformer_dcc.lp") diff --git a/tests/test_scripts/test_solph/test_piecewiselineartransformer/test_piecewiselineartransformer.py b/tests/test_scripts/test_solph/test_piecewiselineartransformer/test_piecewiselineartransformer.py index ec5c34055..4ccce8197 100644 --- a/tests/test_scripts/test_solph/test_piecewiselineartransformer/test_piecewiselineartransformer.py +++ b/tests/test_scripts/test_solph/test_piecewiselineartransformer/test_piecewiselineartransformer.py @@ -43,7 +43,7 @@ def test_pwltf(): # Define conversion function and breakpoints def conv_func(x): - return 0.01 * x ** 2 + return 0.01 * x**2 in_breakpoints = np.arange(0, 110, 25) out_breakpoints = conv_func(in_breakpoints) From e6039fe19df5ce850d2321da26ad55dd16ce206b Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Wed, 9 Mar 2022 16:21:58 +0100 Subject: [PATCH 0179/1363] Update docs (part 1) --- src/oemof/solph/_models.py | 4 +- src/oemof/solph/_options.py | 65 ++-- src/oemof/solph/buses/_bus.py | 21 +- .../buses/experimental/_electrical_bus.py | 14 +- .../components/_extraction_turbine_chp.py | 36 +- src/oemof/solph/components/_generic_chp.py | 36 +- .../solph/components/_generic_storage.py | 339 ++++++++++++++---- .../solph/components/_offset_transformer.py | 6 +- src/oemof/solph/components/_transformer.py | 10 +- 9 files changed, 388 insertions(+), 143 deletions(-) diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 0d81fe64e..918881bed 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -263,7 +263,7 @@ def relax_problem(self): class Model(BaseModel): - """An energy system model for operational and/or investment + """An energy system model for operational and/or investment optimization. Parameters @@ -275,6 +275,8 @@ class Model(BaseModel): to create the constraints of the optimization problem. Defaults to `Model.CONSTRAINT_GROUPS` + Sets and variables: + ------------------- **The following basic sets are created**: NODES diff --git a/src/oemof/solph/_options.py b/src/oemof/solph/_options.py index 8ac82dc1c..bb70936c4 100644 --- a/src/oemof/solph/_options.py +++ b/src/oemof/solph/_options.py @@ -21,48 +21,65 @@ class Investment: """ Parameters ---------- - maximum : float, :math:`P_{invest,max}` or :math:`E_{invest,max}` - Maximum of the additional invested capacity - minimum : float, :math:`P_{invest,min}` or :math:`E_{invest,min}` + maximum : float, :math:`P_{invest,max}(p)` or :math:`E_{invest,max}(p)` + Maximum of the additional invested capacity; + defined per period p for a multi-period model. + minimum : float, :math:`P_{invest,min}(p)` or :math:`E_{invest,min}(p)` Minimum of the additional invested capacity. If `nonconvex` is `True`, - `minimum` defines the threshold for the invested capacity. + `minimum` defines the threshold for the invested capacity; + defined per period p for a multi-period model. ep_costs : float, :math:`c_{invest,var}` - Equivalent periodical costs for the investment per flow capacity. + Equivalent periodical costs or investment expenses for the investment + + * For a standard model: equivalent periodical costs for the investment + per flow capacity, i.e. annuities for investments already calculated. + * For a multi-period model: Investment expenses for the respective + period (in nominal terms). Annuities are calculated within the + objective term, also considering age and lifetime existing : float, :math:`P_{exist}` or :math:`E_{exist}` Existing / installed capacity. The invested capacity is added on top - of this value. Not applicable if `nonconvex` is set to `True`. + of this value. Hence, existing capacities come at no additional costs. + Not applicable if `nonconvex` is set to `True`. nonconvex : bool If `True`, a binary variable for the status of the investment is created. This enables additional fix investment costs (*offset*) independent of the invested flow capacity. Therefore, use the `offset` parameter. offset : float, :math:`c_{invest,fix}` - Additional fix investment costs. Only applicable if `nonconvex` is set - to `True`. - overall_maximum : float + Additional fixed investment costs. Only applicable if `nonconvex` is + set to `True`. + overall_maximum : float, :math:`P_{overall,max}` or :math:`E_{overall,max}` Overall maximum capacity investment, i.e. the amount of capacity that can be totally installed at maximum in any period (taking into account decommissionings); only applicable for multi-period models - overall_minimum : float + overall_minimum : float :math:`P_{overall,min}` or :math:`E_{overall,min}` Overall minimum capacity investment that needs to be installed - in the last period of the simulation (taking into account + in the last period of the optimization (taking into account decommissionings); only applicable for multi-period models - lifetime : int + lifetime : int, :math:`l` Units lifetime, given in years; only applicable for multi-period models - age : int + age : int, :math:`a` Units start age, given in years at the beginning of the simulation; only applicable for multi-period models - interest_rate : float - Interest rate for calculating annuities when investing in that unit; + interest_rate : float, :math:`ir` + Interest rate for calculating annuities when investing in a particular + unit; only applicable for multi-period models. + If nothing else is specified, the interest rate is the same as the + model discount rate of the multi-period model. + fixed_costs : float or list of float, :math:`c_{fixed}(p)` + Fixed costs in each period (given in nominal terms); only applicable for multi-period models - fixed_costs : float or list of float - Fixed costs in each period; only applicable for multi-period models For the variables, constraints and parts of the objective function, which - are created, see :class:`oemof.solph.blocks.investment_flow.InvestmentFlowBlock` - and :class:`oemof.solph.components._generic_storage.GenericInvestmentStorageBlock`. + are created, see :class:`.InvestmentFlowBlock`, + :class:`.GenericInvestmentStorageBlock` + as well as + :class:`.SinkDSMOemofInvestmentBlock`, + :class:`.SinkDSMDIWInvestmentBlock` + and + :class:`.SinkDSMDLRInvestmentBlock`. """ # noqa: E501 @@ -106,6 +123,7 @@ def __init__( self._check_age_and_lifetime() def _check_invest_attributes(self): + """Throw an error if existing is other than 0 and nonconvex is True""" if (self.existing != 0) and (self.nonconvex is True): e1 = ( "Values for 'offset' and 'existing' are given in" @@ -115,6 +133,7 @@ def _check_invest_attributes(self): raise AttributeError(e1) def _check_invest_attributes_maximum(self): + """Throw an error if maximum is infinite and nonconvex is True""" if (self.maximum[0] == float("+inf")) and (self.nonconvex is True): e2 = ( "Please provide a maximum investment value in case of" @@ -127,6 +146,7 @@ def _check_invest_attributes_maximum(self): raise AttributeError(e2) def _check_invest_attributes_offset(self): + """Throw an error if offset is given without nonconvex=True""" if (self.offset[0] != 0) and (self.nonconvex is False): e3 = ( "If `nonconvex` is `False`, the `offset` parameter will be" @@ -135,6 +155,9 @@ def _check_invest_attributes_offset(self): raise AttributeError(e3) def _check_age_and_lifetime(self): + """Throw an error if age is chosen greater or equal to lifetime; + only applicable for multi-period models + """ if self.lifetime is not None: if self.age >= self.lifetime: e4 = ( @@ -166,7 +189,7 @@ class NonConvex: Be aware that minimum up and downtimes can contradict each other and may to infeasible problems. maximum_startups : numeric (0 or positive integer) - Maximum number of start-ups. + Maximum number of startups. maximum_shutdowns : numeric (0 or positive integer) Maximum number of shutdowns. initial_status : numeric (0 or 1) @@ -177,7 +200,7 @@ class NonConvex: fixed for the four first and last timesteps of the optimization period. If both, up and downtimes are defined, the initial status is set for the maximum of both e.g. for six timesteps if a minimum downtime of - six timesteps is defined in addition to a four timestep minimum uptime. + six timesteps is defined besides a four timestep minimum uptime. positive_gradient : :obj:`dict`, default: `{'ub': None, 'costs': 0}` A dictionary containing the following two keys: diff --git a/src/oemof/solph/buses/_bus.py b/src/oemof/solph/buses/_bus.py index 12c6e5db5..2d84c436b 100644 --- a/src/oemof/solph/buses/_bus.py +++ b/src/oemof/solph/buses/_bus.py @@ -26,6 +26,12 @@ class Bus(on.Bus): """A balance object. Every node has to be connected to BusBlock. + Attributes + ---------- + balanced: boolean + Indicates if bus is balanced, i.e. if the sum of inflows equals to + the sum of outflows for each timestep; defaults to True + Notes ----- The following sets, variables, constraints and objective parts are created @@ -49,12 +55,15 @@ class BusBlock(SimpleBlock): **The following constraints are build:** - BusBlock balance :attr:`om.BusBlock.balance[i, o, t]` - .. math:: - \sum_{i \in INPUTS(n)} flow(i, n, t) = - \sum_{o \in OUTPUTS(n)} flow(n, o, t), \\ - \forall n \in \textrm{BUSES}, - \forall t \in \textrm{TIMESTEPS}. + BusBlock balance :attr:`om.BusBlock.balance[i, o, p, t]` + + .. math:: + & + \sum_{i \in INPUTS(n)} flow(i, n, p, t) = + \sum_{o \in OUTPUTS(n)} flow(n, o, p, t), \\ + & + \forall n \in \textrm{BUSES}, + \forall p, t \in \textrm{TIMEINDEX}. """ def __init__(self, *args, **kwargs): diff --git a/src/oemof/solph/buses/experimental/_electrical_bus.py b/src/oemof/solph/buses/experimental/_electrical_bus.py index 81e7ecaac..f1436c03e 100644 --- a/src/oemof/solph/buses/experimental/_electrical_bus.py +++ b/src/oemof/solph/buses/experimental/_electrical_bus.py @@ -21,29 +21,31 @@ class ElectricalBus(Bus): - r"""An electrical bus object. Every node has to be connected to BusBlock. + r"""An electrical bus object used for linear optimal power flow (lopf) + + Every (spatial) node has to be connected to a BusBlock. This BusBlock is used in combination with ElectricalLine objects for linear optimal power flow (lopf) calculations. Parameters ---------- slack: boolean - If True BusBlock is slack bus for network + If True BusBlock is slack bus for electrical network v_max: numeric Maximum value of voltage angle at electrical bus v_min: numeric - Mininum value of voltag angle at electrical bus + Mininum value of voltage angle at electrical bus Note: This component is experimental. Use it with care. Notes ----- The following sets, variables, constraints and objective parts are created - * :py:class:`~oemof.solph.blocks.bus.BusBlock` + * :class:`.BusBlock` The objects are also used inside: - * :py:class:`~oemof.solph.experimental._electrical_line.ElectricalLine` + * :class:`.ElectricalLine` - """ + """ # noqa: E501 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/src/oemof/solph/components/_extraction_turbine_chp.py b/src/oemof/solph/components/_extraction_turbine_chp.py index e76716bdf..3e963e2b6 100644 --- a/src/oemof/solph/components/_extraction_turbine_chp.py +++ b/src/oemof/solph/components/_extraction_turbine_chp.py @@ -29,14 +29,15 @@ class ExtractionTurbineCHP(Transformer): r""" - A CHP with an extraction turbine in a linear model. For more options see - the :class:`~oemof.solph.components.GenericCHP` class. + A CHP with an extraction turbine in a linear model. For a more + detailled modelling approach providing more options, also see + the :class:`.GenericCHP` class. One main output flow has to be defined and is tapped by the remaining flow. The conversion factors have to be defined for the maximum tapped flow ( - full CHP mode) and for no tapped flow (full condensing mode). Even though + full CHP mode) and for no tapped flow (full condensing mode). Even though, it is possible to limit the variability of the tapped flow, so that the - full condensing mode will never be reached. + full condensing might never be reached. Parameters ---------- @@ -53,7 +54,7 @@ class ExtractionTurbineCHP(Transformer): Notes ----- The following sets, variables, constraints and objective parts are created - * :py:class:`~oemof.solph.components.extraction_turbine_chp.ExtractionTurbineCHPBlock` + * :class:`.ExtractionTurbineCHPBlock` Examples -------- @@ -82,7 +83,7 @@ def constraint_group(self): class ExtractionTurbineCHPBlock(SimpleBlock): r"""Block for the linear relation of nodes with type - :class:`~oemof.solph.components.ExtractionTurbineCHP` + :class:`oemof.solph.components.experimental._ExtractionTurbineCHP` **The following two constraints are created:** @@ -113,11 +114,11 @@ class ExtractionTurbineCHPBlock(SimpleBlock): ========================= ============================================ ==== ========= symbol attribute type explanation ========================= ============================================ ==== ========= - :math:`\dot H_{Fuel}` `flow[i, n, t]` V fuel input flow + :math:`\dot H_{Fuel}` `flow[i, n, p, t]` V fuel input flow - :math:`P_{el}` `flow[n, main_output, t]` V electric power + :math:`P_{el}` `flow[n, main_output, p, t]` V electric power - :math:`\dot Q_{th}` `flow[n, tapped_output, t]` V thermal output + :math:`\dot Q_{th}` `flow[n, tapped_output, p, t]` V thermal output :math:`\beta` `main_flow_loss_index[n, t]` P power loss index @@ -137,18 +138,19 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _create(self, group=None): - """Creates the linear constraint for the - :class:`oemof.solph.components.TransformerBlock` block. + """Creates the constraints for + :class:`oemof.solph.components.experimental._extraction_turbine_chp.ExtractionTurbineCHPBlock`. Parameters ---------- group : list - List of :class:`oemof.solph.components.ExtractionTurbineCHP` - (trsf) objects for which the linear relation of inputs and outputs - is created e.g. group = [trsf1, trsf2, trsf3, ...]. Note that the - relation is created for all existing relations of the inputs and - all outputs of the transformer. The components inside the list need - to hold all needed attributes. + List of + :class:`oemof.solph.components.experimental._extraction_turbine_chp.ExtractionTurbineCHP` + (trsf) objects for which the linear relation of inputs + and outputs is created e.g. group = [trsf1, trsf2, trsf3, ...]. + Note that the relation is created for all existing relations + of the inputs and all outputs of the transformer-like object. + The components inside the list need to hold all needed attributes. """ if group is None: return None diff --git a/src/oemof/solph/components/_generic_chp.py b/src/oemof/solph/components/_generic_chp.py index 6f5bd663e..8382adfc4 100644 --- a/src/oemof/solph/components/_generic_chp.py +++ b/src/oemof/solph/components/_generic_chp.py @@ -56,29 +56,31 @@ class GenericCHP(network.Transformer): Note ---- - An adaption for the flow parameter `H_L_FG_share_max` has been made to - set the flue gas losses at maximum heat extraction `H_L_FG_max` as share of - the fuel flow `H_F` e.g. for combined cycle extraction turbines. - The flow parameter `H_L_FG_share_min` can be used to set the flue gas - losses at minimum heat extraction `H_L_FG_min` as share of - the fuel flow `H_F` e.g. for motoric CHPs. - The boolean component parameter `back_pressure` can be set to model - back-pressure characteristics. + * An adaption for the flow parameter `H_L_FG_share_max` has been made to + set the flue gas losses at maximum heat extraction `H_L_FG_max` as share + of the fuel flow `H_F` e.g. for combined cycle extraction turbines. + * The flow parameter `H_L_FG_share_min` can be used to set the flue gas + losses at minimum heat extraction `H_L_FG_min` as share of + the fuel flow `H_F` e.g. for motoric CHPs. + * The boolean component parameter `back_pressure` can be set to model + back-pressure characteristics. Also have a look at the examples on how to use it. Parameters ---------- fuel_input : dict - Dictionary with key-value-pair of `oemof.Bus` and `oemof.Flow` object - for the fuel input. + Dictionary with key-value-pair of `oemof.solph.Bus` and + `oemof.solph.Flow` objects for the fuel input. electrical_output : dict - Dictionary with key-value-pair of `oemof.Bus` and `oemof.Flow` object - for the electrical output. Related parameters like `P_max_woDH` are - passed as attributes of the `oemof.Flow` object. + Dictionary with key-value-pair of `oemof.solph.Bus` and + `oemof.solph.Flow` objects for the electrical output. + Related parameters like `P_max_woDH` are passed as + attributes of the `oemof.Flow` object. heat_output : dict - Dictionary with key-value-pair of `oemof.Bus` and `oemof.Flow` object - for the heat output. Related parameters like `Q_CW_min` are passed as + Dictionary with key-value-pair of `oemof.solph.Bus` + and `oemof.solph.Flow` objects for the heat output. + Related parameters like `Q_CW_min` are passed as attributes of the `oemof.Flow` object. Beta : list of numerical values Beta values in same dimension as all other parameters (length of @@ -91,7 +93,7 @@ class GenericCHP(network.Transformer): Note ---- The following sets, variables, constraints and objective parts are created - * :py:class:`~oemof.solph.components._generic_chp.GenericCHPBlock` + * :class:`.GenericCHPBlock` Examples -------- @@ -498,7 +500,7 @@ def _objective_expression(self): r"""Objective expression for generic CHPs with no investment. Note: This adds nothing as variable costs are already - added in the Block :class:`Flow`. + added in the Block :class:`.FlowBlock`. """ if not hasattr(self, "GENERICCHPS"): return 0 diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index 550e921a8..633ffa732 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -67,6 +67,10 @@ class GenericStorage(network.Node): initial_storage_level : numeric, :math:`c(-1)` The relative storage content in the timestep before the first time step of optimization (between 0 and 1). + + Note: When investment mode is used in a multi-period model, + `initial_storage_level` is not supported. + Storage output is forced to zero until the storage unit is invested in. balanced : boolean Couple storage level of first and last time step. (Total inflow and total outflow are balanced.) @@ -75,9 +79,13 @@ class GenericStorage(network.Node): fixed_losses_relative : numeric (iterable or scalar), :math:`\gamma(t)` Losses independent of state of charge between two consecutive timesteps relative to nominal storage capacity. + + Note: Fixed losses are not supported in investment mode. fixed_losses_absolute : numeric (iterable or scalar), :math:`\delta(t)` Losses independent of state of charge and independent of nominal storage capacity between two consecutive timesteps. + + Note: Fixed losses are not supported in investment mode. inflow_conversion_factor : numeric (iterable or scalar), :math:`\eta_i(t)` The relative conversion factor, i.e. efficiency associated with the inflow of the storage. @@ -85,7 +93,7 @@ class GenericStorage(network.Node): see: inflow_conversion_factor min_storage_level : numeric (iterable or scalar), :math:`c_{min}(t)` The normed minimum storage content as fraction of the - nominal storage capacity (between 0 and 1). + nominal storage capacity or the invested capacity (between 0 and 1). To set different values in every time step use a sequence. max_storage_level : numeric (iterable or scalar), :math:`c_{max}(t)` see: min_storage_level @@ -95,13 +103,21 @@ class GenericStorage(network.Node): investment variable instead of to the nominal_storage_capacity. The nominal_storage_capacity should not be set (or set to None) if an investment object is used. + lifetime_inflow : int, :math:`n_{in}` + Determine the lifetime of an inflow; only applicable for multi-period + models which can invest in storage capacity and have an + invest_relation_input_capacity defined + lifetime_outflow : int, :math:`n_{in}` + Determine the lifetime of an outflow; only applicable for multi-period + models which can invest in storage capacity and have an + invest_relation_output_capacity defined Notes ----- The following sets, variables, constraints and objective parts are created - * :py:class:`~oemof.solph.components._generic_storage.GenericStorageBlock` + * :class:`.GenericStorageBlock` (if no Investment object present) - * :py:class:`~oemof.solph.components._generic_storage.GenericInvestmentStorageBlock` + * :class:`.GenericInvestmentStorageBlock` (if Investment object present) Examples @@ -212,6 +228,9 @@ def __init__( raise AttributeError(message.format("\n ".join(messages))) def _set_flows(self): + """Define inflow / outflow as investment flows when they are + coupled with storage capacity via invest relations + """ for flow in self.inputs.values(): if ( self.invest_relation_input_capacity is not None @@ -226,6 +245,7 @@ def _set_flows(self): flow.investment = Investment(lifetime=self.lifetime_outflow) def _check_invest_attributes(self): + """Raise errors for infeasible investment attribute combinations""" if self.investment and self.nominal_storage_capacity is not None: e1 = ( "If an investment object is defined the invest variable " @@ -258,6 +278,7 @@ def _check_invest_attributes(self): self._set_flows() def _check_number_of_flows(self): + """Ensure that there is only one inflow and outflow to the storage""" msg = "Only one {0} flow allowed in the GenericStorage {1}." check_node_object_for_missing_attribute(self, "inputs") check_node_object_for_missing_attribute(self, "outputs") @@ -267,7 +288,7 @@ def _check_number_of_flows(self): raise AttributeError(msg.format("output", self.label)) def _check_infeasible_parameter_combinations(self): - """Checks for infeasible parameter combinations and raises error""" + """Check for infeasible parameter combinations and raise error""" msg = ( "initial_storage_level must be greater or equal to " "min_storage_level and smaller or equal to " @@ -294,16 +315,16 @@ class GenericStorageBlock(SimpleBlock): :class:`.Model` ) STORAGES - A set with all :class:`.Storage` objects, which do not have an - attr:`investment` of type :class:`.Investment`. + A set with all :class:`.GenericStorage` objects, which do not have an + :attr:`investment` of type :class:`.Investment`. STORAGES_BALANCED - A set of all :py:class:`~.GenericStorage` objects, with 'balanced' attribute set - to True. + A set of all :class:`.GenericStorage` objects, with 'balanced' + attribute set to True. STORAGES_WITH_INVEST_FLOW_REL - A set with all :class:`.Storage` objects with two investment flows - coupled with the 'invest_relation_input_output' attribute. + A set with all :class:`.GenericStorage` objects with two investment + flows coupled with the 'invest_relation_input_output' attribute. **The following variables are created:** @@ -312,7 +333,7 @@ class GenericStorageBlock(SimpleBlock): storage content at the beginning is set by the parameter `initial_storage_level` or not set if `initial_storage_level` is None. The variable of storage s and timestep t can be accessed by: - `om.Storage.storage_content[s, t]` + `om.GenericStorageBlock.storage_content[s, t]` **The following constraints are created:** @@ -325,15 +346,16 @@ class GenericStorageBlock(SimpleBlock): (1 - \beta(t)) ^{\tau(t)/(t_u)} \\ &- \gamma(t)\cdot E_{nom} \cdot {\tau(t)/(t_u)}\\ &- \delta(t) \cdot {\tau(t)/(t_u)}\\ - &- \frac{\dot{E}_o(t)}{\eta_o(t)} \cdot \tau(t) - + \dot{E}_i(t) \cdot \eta_i(t) \cdot \tau(t) + &- \frac{\dot{E}_o(p, t)}{\eta_o(t)} \cdot \tau(t) + + \dot{E}_i(p, t) \cdot \eta_i(t) \cdot \tau(t) Connect the invest variables of the input and the output flow. .. math:: - InvestmentFlowBlock.invest(source(n), n) + existing = \\ - (InvestmentFlowBlock.invest(n, target(n)) + existing) * \\ + InvestmentFlowBlock.invest(source(n), n, p) + existing = \\ + (InvestmentFlowBlock.invest(n, target(n), p) + existing) * \\ invest\_relation\_input\_output(n) \\ - \forall n \in \textrm{INVEST\_REL\_IN\_OUT} + \forall n \in \textrm{INVEST\_REL\_IN\_OUT} \\ + \forall p \in \textrm{PERIODS} @@ -377,8 +399,22 @@ class GenericStorageBlock(SimpleBlock): **The following parts of the objective function are created:** + Standard model + -------------- + Nothing added to the objective function. + Multi-period model + ------------------ + + * :attr:`fixed_costs` not None + + .. math:: + \sum_{p}^{PERIODS} E_{nom} \cdot c_{fixed}(p) \cdot DF^{-p} + + whereby: + :math:`DF=(1+dr)` is the discount factor with discount rate math:`dr` + """ # noqa: E501 @@ -554,8 +590,12 @@ def _power_coupled(block): def _objective_expression(self): r""" Objective expression for storages with no investment. - Note: This adds nothing but fixed costs as variable costs are already - added in the Block :class:`FlowBlock`. + + Note + ---- + * For standard models, this adds nothing as variable costs are + already added in the Block :class:`.FlowBlock`. + * For multi-period models, fixed costs may be introduced and added here """ m = self.parent_block() @@ -580,22 +620,22 @@ def _objective_expression(self): class GenericInvestmentStorageBlock(SimpleBlock): r""" Block for all storages with :attr:`Investment` being not None. - See :class:`oemof.solph.options.Investment` for all parameters of the + See :class:`.Investment` for all parameters of the Investment class. **Variables** - All Storages are indexed by :math:`n`, which is omitted in the following - for the sake of convenience. + All Storages are indexed by :math:`n` (denoting the respective storage + unit), which is omitted in the following for the sake of convenience. The following variables are created as attributes of - :attr:`om.InvestmentStorage`: + :attr:`om.GenericInvestmentStorageBlock`: - * :math:`P_i(t)` + * :math:`P_i(p, t)` Inflow of the storage (created in :class:`oemof.solph.models.BaseModel`). - * :math:`P_o(t)` + * :math:`P_o(p, t)` Outflow of the storage (created in :class:`oemof.solph.models.BaseModel`). @@ -604,15 +644,34 @@ class GenericInvestmentStorageBlock(SimpleBlock): Current storage content (Absolute level of stored energy). - * :math:`E_{invest}` + * :math:`E_{invest}(p)` - Invested (nominal) capacity of the storage. + Invested (nominal) capacity of the storage in period p. + + * :math:`E_{total}(p)` + + Total installed (nominal) capacity of the storage in period p. + + * :math:`E_{old}(p)` + + Old (nominal) capacity of the storage to be decommissioned in period p. + + * :math:`E_{old,exo}(p)` + + Exogenous old (nominal) capacity of the storage to be decommissioned + in period p; existing capacity reaching its lifetime. + + * :math:`E_{old,endo}(p)` + + Endogenous old (nominal) capacity of the storage to be decommissioned + in period p; endgenous investments reaching their lifetime. * :math:`E(-1)` Initial storage content (before timestep 0). + Not applicable for a multi-period model. - * :math:`b_{invest}` + * :math:`b_{invest}(p)` Binary variable for the status of the investment, if :attr:`nonconvex` is `True`. @@ -621,49 +680,112 @@ class GenericInvestmentStorageBlock(SimpleBlock): The following constraints are created for all investment storages: - Storage balance (Same as for :class:`.GenericStorageBlock`) + Storage balance (Same as for :class:`.GenericStorageBlock`) .. math:: E(t) = &E(t-1) \cdot (1 - \beta(t)) ^{\tau(t)/(t_u)} \\ - &- \gamma(t)\cdot (E_{exist} + E_{invest}) \cdot {\tau(t)/(t_u)}\\ + &- \gamma(t)\cdot (E_{total}(p)) \cdot {\tau(t)/(t_u)}\\ &- \delta(t) \cdot {\tau(t)/(t_u)}\\ - &- \frac{P_o(t)}{\eta_o(t)} \cdot \tau(t) - + P_i(t) \cdot \eta_i(t) \cdot \tau(t) + &- \frac{\dot{E}_o(p, t))}{\eta_o(t)} \cdot \tau(t) + + \dot{E}_i(p, t) \cdot \eta_i(t) \cdot \tau(t) + + Total storage capacity (p > 0 for multi-period model only) + + .. math:: + & + if \quad p=0:\\ + & + E_{total}(p) = E_{exist} + E_{invest}(p)\\ + & + else:\\ + & + E_{total}(p) = E_{total}(p-1) + E_{invest}(p) - E_{old}(p)\\ + & + \forall p \in \textrm{PERIODS} + + Old storage capacity (p > 0 for multi-period model only) + + .. math:: + & + E_{old}(p) = E_{old,exo}(p) + E_{old,end}(p)\\ + &\\ + & + if \quad p=0:\\ + & + E_{old,end}(p) = 0\\ + & + else \quad if \quad l \leq year(p):\\ + & + E_{old,end}(p) = E_{invest}(p_{comm})\\ + & + else:\\ + & + E_{old,end}(p)\\ + &\\ + & + if \quad p=0:\\ + & + E_{old,exo}(p) = 0\\ + & + else \quad if \quad l - a \leq year(p):\\ + & + E_{old,exo}(p) = E_{exist} (*)\\ + & + else:\\ + & + E_{old,exo}(p) = 0\\ + & + \forall p \in \textrm{PERIODS} + + whereby: + + * (*) is only performed for the first period the condition is True. + A decommissioning flag is set to True. + * :math:`year(p)` is the year corresponding to period p + * :math:`p_{comm}` is the commissioning period of the storage Depending on the attribute :attr:`nonconvex`, the constraints for the - bounds of the decision variable :math:`E_{invest}` are different:\ + bounds of the decision variable :math:`E_{invest}(p)` are different:\ * :attr:`nonconvex = False` .. math:: - E_{invest, min} \le E_{invest} \le E_{invest, max} + & + E_{invest, min}(p) \le E_{invest}(p) \le E_{invest, max}(p) \\ + & + \forall p \in \textrm{PERIODS} * :attr:`nonconvex = True` .. math:: & - E_{invest, min} \cdot b_{invest} \le E_{invest}\\ + E_{invest, min}(p) \cdot b_{invest}(p) \le E_{invest}(p)\\ & - E_{invest} \le E_{invest, max} \cdot b_{invest}\\ + E_{invest}(p) \le E_{invest, max}(p) \cdot b_{invest}(p)\\ + & + \forall p \in \textrm{PERIODS} The following constraints are created depending on the attributes of - the :class:`.components.GenericStorage`: + the :class:`.GenericStorage`: - * :attr:`initial_storage_level is None` + * :attr:`initial_storage_level is None`; + not applicable for multi-period model Constraint for a variable initial storage content: .. math:: - E(-1) \le E_{invest} + E_{exist} + E(-1) \le E_{exist} + E_{invest}(0) - * :attr:`initial_storage_level is not None` + * :attr:`initial_storage_level is not None`; + not applicable for multi-period model An initial value for the storage content is given: .. math:: - E(-1) = (E_{invest} + E_{exist}) \cdot c(-1) + E(-1) = (E_{invest} + E_{exist}(0)) \cdot c(-1) - * :attr:`balanced=True` + * :attr:`balanced=True`; + not applicable for multi-period model The energy content of storage of the first and the last timestep are set equal: @@ -676,79 +798,164 @@ class GenericInvestmentStorageBlock(SimpleBlock): Connect the invest variables of the storage and the input flow: .. math:: - P_{i,invest} + P_{i,exist} = - (E_{invest} + E_{exist}) \cdot r_{cap,in} + & + P_{i,total}(p) = + E_{total}(p) \cdot r_{cap,in} \\ + & + \forall p \in \textrm{PERIODS} * :attr:`invest_relation_output_capacity is not None` Connect the invest variables of the storage and the output flow: .. math:: - P_{o,invest} + P_{o,exist} = - (E_{invest} + E_{exist}) \cdot r_{cap,out} + & + P_{o,total}(p) = + E_{total}(p) \cdot r_{cap,out}\\ + & + \forall p \in \textrm{PERIODS} * :attr:`invest_relation_input_output is not None` Connect the invest variables of the input and the output flow: .. math:: - P_{i,invest} + P_{i,exist} = - (P_{o,invest} + P_{o,exist}) \cdot r_{in,out} + & + P_{i,total}(p) = + P_{o,total}(p) \cdot r_{in,out}\\ + & + \forall p \in \textrm{PERIODS} * :attr:`max_storage_level` Rule for upper bound constraint for the storage content: .. math:: - E(t) \leq E_{invest} \cdot c_{max}(t) + & + E(t) \leq E_{total}(p) \cdot c_{max}(t)\\ + & + \forall p, t \in \textrm{TIMEINDEX} * :attr:`min_storage_level` Rule for lower bound constraint for the storage content: - .. math:: E(t) \geq E_{invest} \cdot c_{min}(t) + .. math:: + & + E(t) \geq E_{total}(p) \cdot c_{min}(t)\\ + & + \forall p, t \in \textrm{TIMEINDEX} **Objective function** - The part of the objective function added by the investment storages - also depends on whether a convex or nonconvex + Objective terms for a standard model and a multi-period model differ + quite strongly. Besides, the part of the objective function added by the + investment storages also depends on whether a convex or nonconvex investment option is selected. The following parts of the objective function are created: + Standard model + -------------- + + * :attr:`nonconvex = False` + + .. math:: + E_{invest}(0) \cdot c_{invest,var}(0) + + * :attr:`nonconvex = True` + + .. math:: + E_{invest}(0) \cdot c_{invest,var}(0) + + c_{invest,fix}(0) \cdot b_{invest}(0)\\ + + The total value of all investment costs of all *InvestmentStorages* + can be retrieved calling + :math:`om.GenericInvestmentStorageBlock.investment_costs.expr()`. + + Multi-period model + ------------------ + * :attr:`nonconvex = False` .. math:: - E_{invest} \cdot c_{invest,var} + & + E_{invest}(p) \cdot A(c_{invest,var}(p), l, ir) \cdot l + \cdot DF^{-p}\\ + & + \forall p \in \textrm{PERIODS} * :attr:`nonconvex = True` .. math:: - E_{invest} \cdot c_{invest,var} - + c_{invest,fix} \cdot b_{invest}\\ + & + E_{invest}(p) \cdot A(c_{invest,var}(p), l, ir) \cdot l + \cdot DF^{-p} + c_{invest,fix}(p) \cdot b_{invest}(p)\\ + & + \forall p \in \textrm{PERIODS} + + * :attr:`fixed_costs` not None for investments + + .. math:: + & + (\sum_{p} \sum_{pp=year(p)}^{year(p)+l} + E_{invest}(p) \cdot c_{fixed}(pp) \cdot DF^{-pp}) + \cdot DF^{-p}\\ + & + \forall p \in \textrm{PERIODS} + + * :attr:`fixed_costs` not None for existing capacity + + .. math:: + \sum_{pp=0}^{l-a} E_{exist} \cdot c_{fixed}(pp) + \cdot DF^{-pp} + + + whereby: + + * :math:`A(c_{invest,var}(p), l, ir)` A is the annuity for + investment expenses :math:`c_{invest,var}(p)` lifetime :math:`l` and + interest rate :math:`ir` + * :math:`DF=(1+dr)` is the discount factor with discount rate math:`dr` The total value of all investment costs of all *InvestmentStorages* can be retrieved calling - :meth:`om.GenericInvestmentStorageBlock.investment_costs.expr()`. + `om.GenericInvestmentStorageBlock.investment_costs.expr()`. .. csv-table:: List of Variables :header: "symbol", "attribute", "explanation" :widths: 1, 1, 1 - ":math:`P_i(t)`", ":attr:`flow[i[n], n, t]`", "Inflow of the storage" - ":math:`P_o(t)`", ":attr:`flow[n, o[n], t]`", "Outlfow of the storage" + ":math:`P_i(p, t)`", ":attr:`flow[i[n], n, p, t]`", "Inflow + of the storage" + ":math:`P_o(p, t)`", ":attr:`flow[n, o[n], p, t]`", "Outflow + of the storage" ":math:`E(t)`", ":attr:`storage_content[n, t]`", "Current storage content (current absolute stored energy)" - ":math:`E_{invest}`", ":attr:`invest[n, t]`", "Invested (nominal) + ":math:`E_{invest}(p)`", ":attr:`invest[n, p]`", "Invested (nominal) capacity of the storage" + ":math:`E_{old}(p)`", ":attr:`old[n, p]`", " + | Old (nominal) capacity of the storage + | to be decommissioned in period p" + ":math:`E_{old,exo}(p)`", ":attr:`old_exo[n, p]`", " + | Old (nominal) capacity of the storage + | to be decommissioned in period p + | which was exogenously given by :math:`E_{exist}`" + ":math:`E_{old,end}(p)`", ":attr:`old_end[n, p]`", " + | Old (nominal) capacity of the storage + | to be decommissioned in period p + | which was endogenously determined by :math:`E_{invest}(p_{comm})` + | whereby :math:`p_{comm}` is the commissioning period" ":math:`E(-1)`", ":attr:`init_cap[n]`", "Initial storage capacity (before timestep 0)" - ":math:`b_{invest}`", ":attr:`invest_status[i, o]`", "Binary variable - for the status of investment" - ":math:`P_{i,invest}`", ":attr:`InvestmentFlowBlock.invest[i[n], n]`", - "Invested (nominal) inflow (Investmentflow)" - ":math:`P_{o,invest}`", ":attr:`InvestmentFlowBlock.invest[n, o[n]]`", - "Invested (nominal) outflow (Investmentflow)" + ":math:`b_{invest}(p)`", ":attr:`invest_status[i, o, p]`", "Binary + variable for the status of investment" + ":math:`P_{i,invest}(p)`", " + :attr:`InvestmentFlowBlock.invest[i[n], n, p]`", " + Invested (nominal) inflow (InvestmentFlowBlock)" + ":math:`P_{o,invest}`", " + :attr:`InvestmentFlowBlock.invest[n, o[n]]`", " + Invested (nominal) outflow (InvestmentFlowBlock)" .. csv-table:: List of Parameters :header: "symbol", "attribute", "explanation" @@ -898,9 +1105,7 @@ def _create(self, group=None): ) self.EXISTING_INVESTSTORAGES = Set( - initialize=[ - n for n in group if n.investment.existing is not None - ] + initialize=[n for n in group if n.investment.existing is not None] ) # ######################### Variables ################################ diff --git a/src/oemof/solph/components/_offset_transformer.py b/src/oemof/solph/components/_offset_transformer.py index ed6547af7..b7def82bf 100644 --- a/src/oemof/solph/components/_offset_transformer.py +++ b/src/oemof/solph/components/_offset_transformer.py @@ -102,15 +102,15 @@ class OffsetTransformerBlock(SimpleBlock): .. math:: & - P_{out}(t) = C_1(t) \cdot P_{in}(t) + C_0(t) \cdot Y(t) \\ + P_{out}(p, t) = C_1(t) \cdot P_{in}(p, t) + C_0(t) \cdot Y(t) \\ .. csv-table:: Variables (V) and Parameters (P) :header: "symbol", "attribute", "type", "explanation" :widths: 1, 1, 1, 1 - ":math:`P_{out}(t)`", "`flow[n, o, t]`", "V", "Power of output" - ":math:`P_{in}(t)`", "`flow[i, n, t]`", "V","Power of input" + ":math:`P_{out}(p, t)`", "`flow[n, o, p, t]`", "V", "Power of output" + ":math:`P_{in}(p, t)`", "`flow[i, n, p, t]`", "V","Power of input" ":math:`Y(t)`", "`status[i, n, t]`", "V","binary status variable of nonconvex input flow " ":math:`C_1(t)`", "`coefficients[1][n, t]`", "P", "linear diff --git a/src/oemof/solph/components/_transformer.py b/src/oemof/solph/components/_transformer.py index 2a76cda29..eed4ae25c 100644 --- a/src/oemof/solph/components/_transformer.py +++ b/src/oemof/solph/components/_transformer.py @@ -115,9 +115,9 @@ class TransformerBlock(SimpleBlock): Linear relation :attr:`om.TransformerBlock.relation[i,o,t]` .. math:: - \P_{i,n}(t) \times \eta_{n,o}(t) = \ - \P_{n,o}(t) \times \eta_{n,i}(t), \\ - \forall t \in \textrm{TIMESTEPS}, \\ + \P_{i,n}(p, t) \times \eta_{n,o}(t) = \ + \P_{n,o}(p, t) \times \eta_{n,i}(t), \\ + \forall p, t \in \textrm{TIMINDEX}, \\ \forall n \in \textrm{TRANSFORMERS}, \\ \forall i \in \textrm{INPUTS(n)}, \\ \forall o \in \textrm{OUTPUTS(n)}, @@ -125,10 +125,10 @@ class TransformerBlock(SimpleBlock): ====================== ============================ ============= symbol attribute explanation ====================== ============================ ============= - :math:`P_{i,n}(t)` `flow[i, n, t]` TransformerBlock + :math:`P_{i,n}(p, t)` `flow[i, n, p, t]` TransformerBlock inflow - :math:`P_{n,o}(t)` `flow[n, o, t]` TransformerBlock + :math:`P_{n,o}(p, t)` `flow[n, o, p, t]` TransformerBlock outflow :math:`\eta_{i,n}(t)` `conversion_factor[i, n, t]` Conversion From cc141c90d876e1ef2fa0d350ba840570d6ed2d22 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Wed, 9 Mar 2022 18:20:01 +0100 Subject: [PATCH 0180/1363] Update docs (part 2) --- src/oemof/solph/_models.py | 3 +- .../solph/components/_generic_storage.py | 27 ++-- .../components/experimental/_generic_caes.py | 6 +- .../solph/components/experimental/_link.py | 7 +- src/oemof/solph/constraints/integral_limit.py | 20 +-- .../solph/constraints/investment_limit.py | 26 ++-- src/oemof/solph/flows/_flow.py | 146 ++++++++++++++++-- src/oemof/solph/flows/_non_convex_flow.py | 26 ++-- .../flows/experimental/_electrical_line.py | 5 +- 9 files changed, 198 insertions(+), 68 deletions(-) diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 918881bed..50dcd12d5 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -275,8 +275,7 @@ class Model(BaseModel): to create the constraints of the optimization problem. Defaults to `Model.CONSTRAINT_GROUPS` - Sets and variables: - ------------------- + **The following basic sets are created**: NODES diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index 633ffa732..5803abcfe 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -399,18 +399,17 @@ class GenericStorageBlock(SimpleBlock): **The following parts of the objective function are created:** - Standard model - -------------- + *Standard model* Nothing added to the objective function. - Multi-period model - ------------------ + *Multi-period model* * :attr:`fixed_costs` not None .. math:: - \sum_{p}^{PERIODS} E_{nom} \cdot c_{fixed}(p) \cdot DF^{-p} + \sum_{p \in \textrm{PERIODS}} E_{nom} + \cdot c_{fixed}(p) \cdot DF^{-p} whereby: :math:`DF=(1+dr)` is the discount factor with discount rate math:`dr` @@ -855,8 +854,7 @@ class GenericInvestmentStorageBlock(SimpleBlock): investment option is selected. The following parts of the objective function are created: - Standard model - -------------- + *Standard model* * :attr:`nonconvex = False` @@ -873,8 +871,7 @@ class GenericInvestmentStorageBlock(SimpleBlock): can be retrieved calling :math:`om.GenericInvestmentStorageBlock.investment_costs.expr()`. - Multi-period model - ------------------ + *Multi-period model* * :attr:`nonconvex = False` @@ -898,7 +895,7 @@ class GenericInvestmentStorageBlock(SimpleBlock): .. math:: & - (\sum_{p} \sum_{pp=year(p)}^{year(p)+l} + \sum_{pp=year(p)}^{year(p)+l} E_{invest}(p) \cdot c_{fixed}(pp) \cdot DF^{-pp}) \cdot DF^{-p}\\ & @@ -941,11 +938,11 @@ class GenericInvestmentStorageBlock(SimpleBlock): | Old (nominal) capacity of the storage | to be decommissioned in period p | which was exogenously given by :math:`E_{exist}`" - ":math:`E_{old,end}(p)`", ":attr:`old_end[n, p]`", " - | Old (nominal) capacity of the storage - | to be decommissioned in period p - | which was endogenously determined by :math:`E_{invest}(p_{comm})` - | whereby :math:`p_{comm}` is the commissioning period" + ":math:`E_{old,end}(p)`", ":attr:`old_end[n, p]`", " + | Old (nominal) capacity of the storage + | to be decommissioned in period p + | which was endogenously determined by :math:`E_{invest}(p_{comm})` + | whereby :math:`p_{comm}` is the commissioning period" ":math:`E(-1)`", ":attr:`init_cap[n]`", "Initial storage capacity (before timestep 0)" ":math:`b_{invest}(p)`", ":attr:`invest_status[i, o, p]`", "Binary diff --git a/src/oemof/solph/components/experimental/_generic_caes.py b/src/oemof/solph/components/experimental/_generic_caes.py index 3be4dc705..5b8201157 100644 --- a/src/oemof/solph/components/experimental/_generic_caes.py +++ b/src/oemof/solph/components/experimental/_generic_caes.py @@ -307,13 +307,13 @@ class GenericCAESBlock(SimpleBlock): ":math:`\tau`", "`cav_eta_tmp[n,t]`", "P", "Temporal efficiency (loss factor to take intertemporal losses into account)" ":math:`electrical\_input`", " - `flow[list(n.electrical_input.keys())[0], n, t]`", "P", " + `flow[list(n.electrical_input.keys())[0], p, n, t]`", "P", " Electr. power input into compression" ":math:`electrical\_output`", " - `flow[n, list(n.electrical_output.keys())[0], t]`", "P", " + `flow[n, list(n.electrical_output.keys())[0], p, t]`", "P", " Electr. power output of expansion" ":math:`fuel\_input`", " - `flow[list(n.fuel_input.keys())[0], n, t]`", "P", "Heat input + `flow[list(n.fuel_input.keys())[0], n, p, t]`", "P", "Heat input (external) into Expansion" """ diff --git a/src/oemof/solph/components/experimental/_link.py b/src/oemof/solph/components/experimental/_link.py index d3a537cdc..04b3cce4b 100644 --- a/src/oemof/solph/components/experimental/_link.py +++ b/src/oemof/solph/components/experimental/_link.py @@ -30,7 +30,7 @@ class Link(on.Transformer): - """A Link object with 1...2 inputs and 1...2 outputs. + """A Link object with 2 inputs and 2 outputs. Parameters ---------- @@ -107,10 +107,11 @@ class LinkBlock(SimpleBlock): .. math:: & - (1) \qquad P_{\mathrm{in},n}(t) = c_n(t) \times P_{\mathrm{out},n}(t) + (1) \qquad P_{\mathrm{in},n}(p, t) = c_n(t) + \times P_{\mathrm{out},n}(p, t) \quad \forall t \in T, \forall n in {1,2} \\ & - (2) \qquad 1 \ge \hat{S} + P_{\mathrm{in},1}(t) + (2) \qquad 1 \ge \hat{S} + P_{\mathrm{in},1}(p, t) / P_{\mathrm{in},1,\mathrm{max}} \quad \forall t \in T \\ & diff --git a/src/oemof/solph/constraints/integral_limit.py b/src/oemof/solph/constraints/integral_limit.py index 6e81c9dfd..d1015c5cb 100644 --- a/src/oemof/solph/constraints/integral_limit.py +++ b/src/oemof/solph/constraints/integral_limit.py @@ -21,6 +21,7 @@ def emission_limit(om, flows=None, limit=None): r""" Short handle for generic_integral_limit() with keyword="emission_factor". + Can be used to impose an emissions budget limit in a multi-period model. Note ---- @@ -36,6 +37,7 @@ def emission_limit_per_period(om, flows=None, limit=None): r""" Short handle for generic_periodical_integral_limit() with keyword="emission_factor". Only applicable for multi-period models. + Puts a limit on each period's emissions. Note ---- @@ -75,7 +77,7 @@ def generic_integral_limit(om, keyword, flows=None, limit=None): **Constraint:** - .. math:: \sum_{i \in F_E} \sum_{t \in T} P_i(t) \cdot w_i(t) + .. math:: \sum_{i \in F_E} \sum_{t \in T} P_i(p, t) \cdot w_i(t) \cdot \tau(t) \leq M @@ -85,14 +87,14 @@ def generic_integral_limit(om, keyword, flows=None, limit=None): The symbols used are defined as follows (with Variables (V) and Parameters (P)): - ================ ==== ===================================================== - math. symbol type explanation - ================ ==== ===================================================== - :math:`P_n(t)` V power flow :math:`n` at time step :math:`t` - :math:`w_N(t)` P weight given to Flow named according to `keyword` - :math:`\tau(t)` P width of time step :math:`t` - :math:`L` P global limit given by keyword `limit` - ================ ==== ===================================================== + ================= ==== ==================================================== + math. symbol type explanation + ================= ==== ==================================================== + :math:`P_n(p, t)` V power flow :math:`n` at time index :math:`p, t` + :math:`w_N(t)` P weight given to Flow named according to `keyword` + :math:`\tau(t)` P width of time step :math:`t` + :math:`L` P global limit given by keyword `limit` + ================= ==== ==================================================== Examples -------- diff --git a/src/oemof/solph/constraints/investment_limit.py b/src/oemof/solph/constraints/investment_limit.py index 55a82bdbe..63693a321 100644 --- a/src/oemof/solph/constraints/investment_limit.py +++ b/src/oemof/solph/constraints/investment_limit.py @@ -20,7 +20,7 @@ def investment_limit(model, limit=None): r"""Set an absolute limit for the total investment costs of an investment - optimization problem: + optimization problem (over all periods in case of a multi-period model): .. math:: \sum_{investment\_costs} \leq limit @@ -60,7 +60,7 @@ def investment_rule(m): def investment_limit_per_period(model, limit=None): r"""Set an absolute limit for the total investment costs of a investment optimization problem for each period - of the problem. + of the multi-period problem. .. math:: \sum_{investment\_costs(p)} \leq limit(p) \forall p in \textrm{PERIODS} @@ -131,7 +131,9 @@ def additional_investment_flow_limit(model, keyword, limit=None): Total value of keyword attributes after optimization can be retrieved calling the :attr:`oemof.solph.Model.invest_limit_${keyword}()`. - .. math:: \sum_{i \in IF} P_i \cdot w_i \leq limit + .. math:: + \sum_{p \in \textrm{PERIODS}} + \sum_{i \in IF} P_{i}(p) \cdot w_i \leq limit With `IF` being the set of InvestmentFlows considered for the integral limit. @@ -139,15 +141,15 @@ def additional_investment_flow_limit(model, keyword, limit=None): The symbols used are defined as follows (with Variables (V) and Parameters (P)): - +---------------+------------------------------------+------+--------------------------------------------------------------+ - | symbol | attribute | type | explanation | - +===============+====================================+======+==============================================================+ - | :math:`P_{i}` | `InvestmentFlowBlock.invest[i, o]` | V | installed capacity of investment flow | - +---------------+------------------------------------+------+--------------------------------------------------------------+ - | :math:`w_i` | `keyword` | P | weight given to investment flow named according to `keyword` | - +---------------+------------------------------------+------+--------------------------------------------------------------+ - | :math:`limit` | `limit` | P | global limit given by keyword `limit` | - +---------------+------------------------------------+------+--------------------------------------------------------------+ + +------------------+---------------------------------------+------+--------------------------------------------------------------+ + | symbol | attribute | type | explanation | + +==================+=======================================+======+==============================================================+ + | :math:`P_{i}(p)` | `InvestmentFlowBlock.invest[i, o, p]` | V | invested capacity of investment flow in period p | + +------------------+---------------------------------------+------+--------------------------------------------------------------+ + | :math:`w_i` | `keyword` | P | weight given to investment flow named according to `keyword` | + +------------------+---------------------------------------+------+--------------------------------------------------------------+ + | :math:`limit` | `limit` | P | global limit given by keyword `limit` | + +------------------+---------------------------------------+------+--------------------------------------------------------------+ Parameters ---------- diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 782b7450f..22f99acde 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -39,7 +39,7 @@ class Flow(on.Edge): which are handled specially are noted below. For the case where a parameter can be either a scalar or an iterable, a scalar value will be converted to a sequence containing the scalar value at - every index. This sequence is then stored under the paramter's key. + every index. This sequence is then stored under the parameter's key. Parameters ---------- @@ -101,7 +101,22 @@ class Flow(on.Edge): will be used instead of :class:`FlowBlock `. Note: at the moment this does not work if the investment attribute is - set . + set. + integer : boolean + If True, flow is forced to take only integer values + fixed_costs : numeric (iterable or scalar) + The fixed costs associated with a flow. + Note: These are only applicable for a multi-period model. + lifetime : int + The lifetime of a flow (usually given in years); + once it reaches its lifetime (considering also + an initial age), the flow is forced to 0. + Note: Only applicable for a multi-period model. + age : int + The initial age of a flow (usually given in years); + once it reaches its lifetime (considering also + an initial age), the flow is forced to 0. + Note: Only applicable for a multi-period model. Notes ----- @@ -295,44 +310,151 @@ class FlowBlock(SimpleBlock): INTEGER_FLOWS A set of flows where the attribute :attr:`integer` is True (forces flow to only take integer values) + LIFETIME_FLOWS + All flows with a given attribute :attr:`lifetime`, but no initial + :attr:`age` given + LIFETIME_AGE_FLOWS + All flows with a given attribute :attr:`lifetime` and an initial + :attr:`age` given **The following constraints are build:** FlowBlock max sum :attr:`om.FlowBlock.summed_max[i, o]` .. math:: - \sum_t flow(i, o, t) \cdot \tau + \sum_t flow(i, o, p, t) \cdot \tau \leq summed\_max(i, o) \cdot nominal\_value(i, o), \\ \forall (i, o) \in \textrm{SUMMED\_MAX\_FLOWS}. FlowBlock min sum :attr:`om.FlowBlock.summed_min[i, o]` .. math:: - \sum_t flow(i, o, t) \cdot \tau + \sum_t flow(i, o, p, t) \cdot \tau \geq summed\_min(i, o) \cdot nominal\_value(i, o), \\ \forall (i, o) \in \textrm{SUMMED\_MIN\_FLOWS}. Negative gradient constraint :attr:`om.FlowBlock.negative_gradient_constr[i, o]`: .. math:: - flow(i, o, t-1) - flow(i, o, t) \geq \ + & + if \quad t > 0:\\ + & + flow(i, o, p t-1) - flow(i, o, p, t) \geq \ negative\_gradient(i, o, t), \\ + & + else:\\ + & + flow(i, o, p, t) = 0\\ + & \forall (i, o) \in \textrm{NEGATIVE\_GRADIENT\_FLOWS}, \\ - \forall t \in \textrm{TIMESTEPS}. + \forall p, t \in \textrm{TIMEINDEX}\\. Positive gradient constraint :attr:`om.FlowBlock.positive_gradient_constr[i, o]`: - .. math:: flow(i, o, t) - flow(i, o, t-1) \geq \ - positive\__gradient(i, o, t), \\ + .. math:: + & + if \quad t > 0:\\ + & + flow(i, o, p t) - flow(i, o, p, t-1) \geq \ + positive\_gradient(i, o, t), \\ + & + else:\\ + & + flow(i, o, p, t) = 0\\ + & \forall (i, o) \in \textrm{POSITIVE\_GRADIENT\_FLOWS}, \\ - \forall t \in \textrm{TIMESTEPS}. + \forall p, t \in \textrm{TIMEINDEX}\\. + + Note + ---- + The gradient implementations combine a timestep and its predecessor. + This predecessor might also lie in the previous period which is taken + care of by checking the previous indices of the TIMEINDEX set. + + + Integer constraint + :attr:`om.FlowBlock.integer_flow_constr[i, o]`: + .. math:: + integer\_flow(i, o, t) = flow(i, o, p, t) \\ + \forall p, t \in \textrm{TIMEINDEX}, integer\_flow(i, o, t) + \in \textrm{N} + + Lifetime output constraint: Force flow to 0 once it exceeds its lifetime + :attr:`om.FlowBlock.lifetime_output[i, o]`: + .. math:: + & + if \quad lifetime(i, o) < year(p):\\ + & + flow(i, o, p, t) = 0 \\ + \forall p, t \in \textrm{TIMEINDEX}, + (i, o) \in \textrm{LIFETIME\_FLOWS} + + Lifetime age output constraint: + Force flow to 0 once it exceeds its lifetime, considering its initial age + :attr:`om.FlowBlock.lifetime_output[i, o]`: + .. math:: + & + if \quad lifetime(i, o) - age(i, o) < year(p):\\ + & + flow(i, o, p, t) = 0 \\ + \forall p, t \in \textrm{TIMEINDEX}, + (i, o) \in \textrm{LIFETIME\_FLOWS} + + Whereby: + + * year(p) is the year corresponding to period p + * lifetime(i, o) is the expected technical lifetime of flow (i, o) + * age(i, o) is the initial age of flow (i, o) **The following parts of the objective function are created:** - If :attr:`variable_costs` are set by the user: + *Standard model* + + If :attr:`variable_costs` is set by the user: .. math:: - \sum_{(i,o)} \sum_t flow(i, o, t) \cdot variable\_costs(i, o, t) + \sum_{(i,o)} \sum_{p, t} + flow(i, o, p, t) \cdot weight(t) \cdot variable\_costs(i, o, t) The expression can be accessed by :attr:`om.FlowBlock.variable_costs` and - their value after optimization by :meth:`om.FlowBlock.variable_costs()` . + their value after optimization by :meth:`om.FlowBlock.variable_costs()`. + + *Multi-period model* + + If :attr:`variable_costs` is set by the user: + .. math:: + \sum_{(i,o)} \sum_{p, t} + flow(i, o, p, t) \cdot weight(t) \cdot variable\_costs(i, o, t) + \cdot DF^{p} + + If :attr:`fixed_costs` is set by the user + and flow has no lifetime limitation: + .. math:: + \sum_{(i,o)} \sum_p + nominal\_value(i, o) \cdot fixed\_costs(i, o, p) + \cdot DF^{p} + + If :attr:`fixed_costs` is set by the user + and flow has an :attr:`lifetime` attribute defined: + .. math:: + \sum_{(i,o)} \sum_{pp=0}^{lifetime(i, o)} + nominal\_value(i, o) \cdot fixed\_costs(i, o, pp) + \cdot DF^{-pp} + + If :attr:`fixed_costs` is set by the user + and flow has an :attr:`lifetime` and an :attr:`age` attribute defined: + .. math:: + \sum_{(i,o)} \sum_{pp=0}^{lifetime(i, o) - age(i, o)} + nominal\_value(i, o) \cdot fixed\_costs(i, o, pp) + \cdot DF^{-pp} + + Whereby: + + * :math:`DF=(1+dr)` is the discount factor with discount rate math:`dr` + * :math:`weight(t)` is the objective weighting term for timestep t + + Cost expressions can be accessed by + :attr:`om.FlowBlock.variable_costs`, :attr:`om.FlowBlock.fixed_costs` and + :attr:`om.FlowBlock.costs`. Their values after optimization can be + retreived by :meth:`om.FlowBlock.variable_costs()`, + :meth:`om.FlowBlock.fixed_costs()` and :meth:`om.FlowBlock.costs()` """ diff --git a/src/oemof/solph/flows/_non_convex_flow.py b/src/oemof/solph/flows/_non_convex_flow.py index 035877305..3fe1e15b5 100644 --- a/src/oemof/solph/flows/_non_convex_flow.py +++ b/src/oemof/solph/flows/_non_convex_flow.py @@ -190,16 +190,16 @@ class NonConvexFlowBlock(SimpleBlock): Minimum flow constraint `om.NonConvexFlowBlock.min[i,o,t]` .. math:: - flow(i, o, t) \geq min(i, o, t) \cdot nominal\_value \ + flow(i, o, p, t) \geq min(i, o, t) \cdot nominal\_value \ \cdot status(i, o, t), \\ - \forall t \in \textrm{TIMESTEPS}, \\ + \forall p, t \in \textrm{TIMEINDEX}, \\ \forall (i, o) \in \textrm{NONCONVEX\_FLOWS}. Maximum flow constraint `om.NonConvexFlowBlock.max[i,o,t]` .. math:: - flow(i, o, t) \leq max(i, o, t) \cdot nominal\_value \ + flow(i, o, p, t) \leq max(i, o, t) \cdot nominal\_value \ \cdot status(i, o, t), \\ - \forall t \in \textrm{TIMESTEPS}, \\ + \forall p, t \in \textrm{TIMEINDEX}, \\ \forall (i, o) \in \textrm{NONCONVEX\_FLOWS}. Startup constraint `om.NonConvexFlowBlock.startup_constr[i,o,t]` @@ -264,20 +264,26 @@ class NonConvexFlowBlock(SimpleBlock): Positive gradient constraint `om.NonConvexFlowBlock.positive_gradient_constr[i, o]`: - .. math:: flow(i, o, t) \cdot status(i, o, t) - - flow(i, o, t-1) \cdot status(i, o, t-1) \geq \ + .. math:: flow(i, o, p t) \cdot status(i, o, t) + - flow(i, o, p, t-1) \cdot status(i, o, t-1) \geq \ positive\_gradient(i, o, t), \\ \forall (i, o) \in \textrm{POSITIVE\_GRADIENT\_FLOWS}, \\ - \forall t \in \textrm{TIMESTEPS}. + \forall p, t \in \textrm{TIMEINDEX}. Negative gradient constraint `om.NonConvexFlowBlock.negative_gradient_constr[i, o]`: .. math:: - flow(i, o, t-1) \cdot status(i, o, t-1) - - flow(i, o, t) \cdot status(i, o, t) \geq \ + flow(i, o, p, t-1) \cdot status(i, o, t-1) + - flow(i, o, p, t) \cdot status(i, o, t) \geq \ negative\_gradient(i, o, t), \\ \forall (i, o) \in \textrm{NEGATIVE\_GRADIENT\_FLOWS}, \\ - \forall t \in \textrm{TIMESTEPS}. + \forall p, t \in \textrm{TIMEINDEX}. + + Note + ---- + The gradient implementations combine a timestep and its predecessor. + This predecessor might also lie in the previous period which is taken + care of by checking the previous indices of the TIMEINDEX set. **The following parts of the objective function are created:** diff --git a/src/oemof/solph/flows/experimental/_electrical_line.py b/src/oemof/solph/flows/experimental/_electrical_line.py index 275c9447c..22088dc07 100644 --- a/src/oemof/solph/flows/experimental/_electrical_line.py +++ b/src/oemof/solph/flows/experimental/_electrical_line.py @@ -11,6 +11,7 @@ SPDX-FileCopyrightText: jakob-wo SPDX-FileCopyrightText: gplssm SPDX-FileCopyrightText: jnnr +SPDX-FileCopyrightText: Johannes Kochems SPDX-License-Identifier: MIT @@ -88,9 +89,9 @@ class ElectricalLineBlock(SimpleBlock): Linear relation :attr:`om.ElectricalLine.electrical_flow[n,t]` .. math:: - flow(n, o, t) = 1 / reactance(n, t) \\cdot () + flow(n, o, p, t) = 1 / reactance(n, t) \\cdot () voltage_angle(i(n), t) - volatage_angle(o(n), t), \\ - \forall t \\in \\textrm{TIMESTEPS}, \\ + \forall p, t \\in \\textrm{TIMEINDEX}, \\ \forall n \\in \\textrm{ELECTRICAL\_LINES}. TODO: Add equate constraint of flows From 83c864ea0d03571d769d9b65f35898686f698804 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Thu, 10 Mar 2022 12:18:52 +0100 Subject: [PATCH 0181/1363] Adjust and document processing (intermediate state) TODO: Fix / add special results extraction for SinkDSM --- src/oemof/solph/processing.py | 158 ++++++++++++++++++++++++---------- 1 file changed, 112 insertions(+), 46 deletions(-) diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index 50657d14a..784b218ec 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -75,11 +75,16 @@ def remove_timeindex(x): def create_dataframe(om): """ - Create a result dataframe with all optimization data. + Create a result DataFrame with all optimization data. - Results from Pyomo are written into pandas DataFrame where separate columns - are created for the variable index e.g. for tuples of the flows and - components or the timesteps / timeindices. + Results from Pyomo are written into one common pandas.DataFrame where + separate columns are created for the variable index e.g. for tuples + of the flows and components or the timeindex. + + Note: The "timeindex" column holds values for variables indexed + in timeindex (flow), periods (investment variables) + or timesteps (remainder) or even differently + (SinkDSM for approaches "DLR" and "DIW"). """ # get all pyomo variables including their block block_vars = list( @@ -117,14 +122,38 @@ def create_dataframe(om): def results(om): """ - Create a result dictionary from the result DataFrame. - - Results from Pyomo are written into a dictionary of pandas objects where - a Series holds all scalar values and a dataframe all sequences for nodes - and flows for a standard model. For a multi-period model, the investment - values are given in a DataFrame indexed by periods. - The dictionary is keyed by the nodes e.g. `results[idx]['scalars']` - and flows e.g. `results[n, n]['sequences']` for a standard model. + Create a nested result dictionary from the result DataFrame. + + The already rearranged results from Pyomo from the result DataFrame are + transferred into a nested dictionary of pandas objects. + The first level key of that dictionary is a node (denoting the respective + flow or component). + + The second level keys are "sequences" and "scalars" for a *standard model*: + + * A pd.DataFrame holds all results that are time-dependent, i.e. given as + a sequence and can be indexed with the energy system's timeindex. + * A pd.Series holds all scalar values which are applicable for timestep 0 + (i.e. investments). + + For a *multi-period model*, the second level key for "sequences" remains + the same while instead of "scalars", the key "period_scalars" is used: + + * For sequences, see standard model. + * Instead of a pd.Series, a pd.DataFrame holds scalar values indexed + by periods. These hold investment-related variables. + + Since with the introduction of the multi-period feature, variables are now + indexed differently, this needs to be sorted out again. I.e., dependent on + the variable type, the proper index has to be mapped to the timestep resp. + timeindex indices. + + Examples + -------- + * *Standard model*: `results[idx]['scalars']` + and flows `results[n, n]['sequences']`. + * *Multi-period model*: `results[idx]['period_scalars']` + and flows `results[n, n]['sequences']`. """ # Extraction steps that are the same for both model types df = create_dataframe(om) @@ -161,7 +190,12 @@ def results(om): # Results extraction for a multi-period model else: result = _extract_multi_period_model_result( - om, df_dict, timestep_indexed, period_timestep_indexed, result + om, + df_dict, + period_indexed, + timestep_indexed, + period_timestep_indexed, + result, ) scalars_col = "period_scalars" @@ -197,6 +231,13 @@ def _extract_standard_model_result( ): """Extract and return the results of a standard model + * Set index to timeindex and pivot results such that values are displayed + for the respective variables. Replace / map indices such that everything + is ultimately indexed by timesteps (not periods or timeindex) and reindex + with the energy system's timeindex. + * Filter for columns with nan values to retreive scalar variables. Split + up the DataFrame into sequences and scalars and return it. + Parameters ---------- om : oemof.solph.models.Model @@ -221,12 +262,12 @@ def _extract_standard_model_result( df_dict[k].set_index("timeindex", inplace=True) df_dict[k] = df_dict[k].pivot(columns="variable_name", values="value") try: - # Enable reindexing by replacing period and timestep indices - df_dict[k] = _replace_non_timeindex_indices( + # Enable reindexing by replacing period and timeindex indices + df_dict[k] = _replace_non_timestep_indices( df_dict[k], - period_indexed, timestep_indexed, period_timestep_indexed, + period_indexed, ) df_dict[k].index = om.es.timeindex except ValueError as e: @@ -253,21 +294,24 @@ def _extract_standard_model_result( return result -def _replace_non_timeindex_indices( - df, period_indexed, timestep_indexed, period_timestep_indexed +def _replace_non_timestep_indices( + df, timestep_indexed, period_timestep_indexed, period_indexed=None ): - """Replace timeindex values by timesteps values; we only have one period + """Replace timeindex values by timesteps values + + Use subsets defining how variables are indexed and rename the timeindexed + ones by replacing the tuple by its last entry, i.e. the timestep Parameters ---------- df : pd.DataFrame DataFrame obtained from the dict of results DataFrames - period_indexed : list - List of all values that are indexed by periods timestep_indexed : list List of all values that are indexed by timesteps period_timestep_indexed : list list of variables that are indexed by period and timesteps (timeindex) + period_indexed : list or None + List of all values that are indexed by periods Returns ------- @@ -276,16 +320,12 @@ def _replace_non_timeindex_indices( """ rename_dict = {key: (key[1],) for key in df.index if len(key) > 1} to_concat = [] + # Split into different data sets dependent on indexation + # Variables indexed by periods and timestep (i.e. timeindex) period_timestep_indexed_df = df[ [col for col in df.columns if col in period_timestep_indexed] ] - timestep_indexed_df = df[ - [col for col in df.columns if col in timestep_indexed] - ] - period_indexed_df = df[ - [col for col in df.columns if col in period_indexed] - ] period_timestep_indexed_df = period_timestep_indexed_df.dropna() period_timestep_indexed_df = period_timestep_indexed_df.rename( @@ -294,18 +334,32 @@ def _replace_non_timeindex_indices( if not period_timestep_indexed_df.empty: to_concat.append(period_timestep_indexed_df) - period_indexed_df = period_indexed_df.dropna() - if not period_indexed_df.empty: - to_concat.append(period_indexed_df) + # Variables indexed by timestep + timestep_indexed_df = df[ + [col for col in df.columns if col in timestep_indexed] + ] + # TODO: Also handle SinkDSM for DIW and DLR approach! # Handle storages differently - # TODO: Handle SinkDSM for DIW and DLR approach! if "storage_content" not in timestep_indexed_df.columns: timestep_indexed_df = timestep_indexed_df.dropna() if not timestep_indexed_df.empty: to_concat.append(timestep_indexed_df) - df = pd.concat(to_concat, axis=1) + # Variables indexed by periods (standard model only) + if period_indexed is not None: + period_indexed_df = df[ + [col for col in df.columns if col in period_indexed] + ] + period_indexed_df = period_indexed_df.dropna() + if not period_indexed_df.empty: + to_concat.append(period_indexed_df) + + # HACK! For now simply skip SinkDSM units not properly handled! + if len(to_concat) >= 1: + df = pd.concat(to_concat, axis=1) + else: + df = df.dropna() return df @@ -313,18 +367,24 @@ def _replace_non_timeindex_indices( def _extract_multi_period_model_result( om, df_dict, + period_indexed=None, timestep_indexed=None, period_timestep_indexed=None, result=None, ): """Extract and return the results of a multi-period model + * Set index to timeindex and pivot results such that values are displayed + for the respective variables. + Parameters ---------- om : oemof.solph.models.Model The ptimization model df_dict : dict dictionary of results DataFrames + period_indexed : list + list of variables that are indexed by periods timestep_indexed : list list of variables that are indexed by timesteps period_timestep_indexed : list @@ -340,27 +400,33 @@ def _extract_multi_period_model_result( for k in df_dict: df_dict[k].set_index("timeindex", inplace=True) df_dict[k] = df_dict[k].pivot(columns="variable_name", values="value") - # TODO: Revise and potentially speed up + try: + # Enable reindexing by replacing period and timeindex indices + sequences = _replace_non_timestep_indices( + df_dict[k], timestep_indexed, period_timestep_indexed + ) + sequences.index = om.es.timeindex + except ValueError as e: + msg = ( + "\nFlowBlock: {0}-{1}. This could be caused by NaN-values in" + " your input data." + ) + raise type(e)( + str(e) + msg.format(k[0].label, k[1].label) + ).with_traceback(sys.exc_info()[2]) # Split data set - timeindex_cols = [ - col - for col in df_dict[k].columns - if col in timestep_indexed or col in period_timestep_indexed - ] period_cols = [ - col for col in df_dict[k].columns if col not in timeindex_cols + col for col in df_dict[k].columns if col in period_indexed ] - sequences = df_dict[k][timeindex_cols].dropna() - if sequences.empty: - sequences = pd.DataFrame(index=om.es.timeindex) - # periods equal to years (will probably be the standard use case) - periods = sorted(list(set(om.es.timeindex.year))) - d = dict(zip([(el,) for el in range(len(periods))], periods)) + # map periods to their start years for displaying period results + d = { + (key,): val + om.es.periods[0].min().year + for key, val in om.es.periods_years.items() + } period_scalars = df_dict[k][period_cols].dropna() if period_scalars.empty: period_scalars = pd.DataFrame(index=d.values()) try: - sequences.index = om.es.timeindex period_scalars.rename(index=d, inplace=True) period_scalars.index.name = "period" result[k] = { From 72681af406abbd7a20cacc3cbcc951c5ff9a8efa Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Thu, 10 Mar 2022 15:39:26 +0100 Subject: [PATCH 0182/1363] commit temporary version of processing --- src/oemof/solph/processing.py | 65 ++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index 784b218ec..ea4ebc144 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -73,6 +73,30 @@ def remove_timeindex(x): return x +def get_timestep(x): + """ + Get the timestep from oemof tuples. + The timestep from tuples `(n, n, int)`, `(n, n)`, `(n, int)` and (n,) + is fetched as the last element. For time-independent data (scalars) + zero ist returned. + """ + if all(issubclass(type(n), Node) for n in x): + return 0 + else: + return x[-1] + + +def remove_timestep(x): + """ + Remove the timestep from oemof tuples. + The timestep is removed from tuples of type `(n, n, int)` and `(n, int)`. + """ + if all(issubclass(type(n), Node) for n in x): + return x + else: + return x[:-1] + + def create_dataframe(om): """ Create a result DataFrame with all optimization data. @@ -110,6 +134,11 @@ def create_dataframe(om): df["oemof_tuple"] = df["pyomo_tuple"].map(get_tuple) df = df[df["oemof_tuple"].map(lambda x: x is not None)] df["timeindex"] = df["oemof_tuple"].map(get_timeindex) + # Add standard results extraction needed only for SinkDSM results + # where first time index holds additional information + # for approaches "DLR" and "DIW" + df["timestep"] = df["oemof_tuple"].map(get_timestep) + df["oemof_tuple_extended"] = df["oemof_tuple"].map(remove_timestep) df["oemof_tuple"] = df["oemof_tuple"].map(remove_timeindex) # order the data by oemof tuple and timestep df = df.sort_values(["oemof_tuple", "timeindex"], ascending=[True, True]) @@ -159,10 +188,15 @@ def results(om): df = create_dataframe(om) period_indexed = ["invest", "total", "old", "old_end", "old_exo"] period_timestep_indexed = ["flow"] + + double_indexed_vars = _check_and_return_double_indexed_vars(om) + timestep_indexed = [ el for el in df["variable_name"].unique() - if el not in period_indexed and el not in period_timestep_indexed + if el not in period_indexed + and el not in period_timestep_indexed + and el not in double_indexed_vars ] # create a dict of dataframes keyed by oemof tuples @@ -221,6 +255,35 @@ def results(om): return result +def _check_and_return_double_indexed_vars(om): + """Checks if there are any double indexed vars; returns a list of these + + Double indexed vars are variables with a tuple as timeindex which does NOT + correspond to the timeindex of the model, but contain some other time- + related information. + + So far, this is only used for SinkDSM with approaches "DLR" and "DIW". + """ + double_indexed_vars = [] + if hasattr(om, "SinkDSMDLRBlock") or hasattr( + om, "SinkDSMDLRInvestmentBlock" + ): + double_indexed_vars = [ + "balance_dsm_do", + "balance_dsm_up", + "dsm_do_shift", + "dsm_up", + ] + elif hasattr(om, "SinkDSMDIWBlock") or hasattr( + om, "SinkDSMDIWInvestmentBlock" + ): + double_indexed_vars = [ + "dsm_do_shift" + ] + + return double_indexed_vars + + def _extract_standard_model_result( om, df_dict, From 074a752abc68b0b2a21a0236b93980b9b191f6a3 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Thu, 10 Mar 2022 17:53:55 +0100 Subject: [PATCH 0183/1363] Simplify and debug processing --- src/oemof/solph/processing.py | 304 ++++-------------- .../test_multi_period_dispatch_model.py | 2 +- 2 files changed, 70 insertions(+), 236 deletions(-) diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index ea4ebc144..842b20e70 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -27,8 +27,7 @@ def get_tuple(x): - """ - Get oemof tuple within iterable or create it. + """Get oemof tuple within iterable or create it Tuples from Pyomo are of type `(n, n, int)`, `(n, n)` and `(n, int)`. For single nodes `n` a tuple with one object `(n,)` is created. @@ -44,38 +43,9 @@ def get_tuple(x): return x -def get_timeindex(x): - """ - Get the timeindex from oemof tuples. - Slice int values (timeindex, timesteps or periods) dependent on how - the variable is indexed. - - The timestep is removed from tuples of type `(n, n, int, int)`, - `(n, n, int)` and `(n, int)`. - """ - for i, n in enumerate(x): - if isinstance(n, int): - return x[i:] - return (0,) - - -def remove_timeindex(x): - """ - Remove the timeindex from oemof tuples. - Slice up to integer values (node labels) - - The timestep is removed from tuples of type `(n, n, int, int)`, - `(n, n, int)` and `(n, int)`. - """ - for i, n in enumerate(x): - if isinstance(n, int): - return x[:i] - return x - - def get_timestep(x): - """ - Get the timestep from oemof tuples. + """Get the timestep from oemof tuples + The timestep from tuples `(n, n, int)`, `(n, n)`, `(n, int)` and (n,) is fetched as the last element. For time-independent data (scalars) zero ist returned. @@ -87,8 +57,8 @@ def get_timestep(x): def remove_timestep(x): - """ - Remove the timestep from oemof tuples. + """Remove the timestep from oemof tuples + The timestep is removed from tuples of type `(n, n, int)` and `(n, int)`. """ if all(issubclass(type(n), Node) for n in x): @@ -98,17 +68,11 @@ def remove_timestep(x): def create_dataframe(om): - """ - Create a result DataFrame with all optimization data. + """Create a result DataFrame with all optimization data Results from Pyomo are written into one common pandas.DataFrame where separate columns are created for the variable index e.g. for tuples - of the flows and components or the timeindex. - - Note: The "timeindex" column holds values for variables indexed - in timeindex (flow), periods (investment variables) - or timesteps (remainder) or even differently - (SinkDSM for approaches "DLR" and "DIW"). + of the flows and components or the timesteps. """ # get all pyomo variables including their block block_vars = list( @@ -133,15 +97,16 @@ def create_dataframe(om): # columns for the oemof tuple and timestep are created df["oemof_tuple"] = df["pyomo_tuple"].map(get_tuple) df = df[df["oemof_tuple"].map(lambda x: x is not None)] - df["timeindex"] = df["oemof_tuple"].map(get_timeindex) - # Add standard results extraction needed only for SinkDSM results - # where first time index holds additional information - # for approaches "DLR" and "DIW" df["timestep"] = df["oemof_tuple"].map(get_timestep) - df["oemof_tuple_extended"] = df["oemof_tuple"].map(remove_timestep) - df["oemof_tuple"] = df["oemof_tuple"].map(remove_timeindex) + df["oemof_tuple"] = df["oemof_tuple"].map(remove_timestep) + + # Hack: Use another call of remove timestep to get rid of period not needed + df.loc[df["variable_name"] == "flow", "oemof_tuple"] = df.loc[ + df["variable_name"] == "flow", "oemof_tuple" + ].map(remove_timestep) + # order the data by oemof tuple and timestep - df = df.sort_values(["oemof_tuple", "timeindex"], ascending=[True, True]) + df = df.sort_values(["oemof_tuple", "timestep"], ascending=[True, True]) # drop empty decision variables df = df.dropna(subset=["value"]) @@ -150,8 +115,7 @@ def create_dataframe(om): def results(om): - """ - Create a nested result dictionary from the result DataFrame. + """Create a nested result dictionary from the result DataFrame The already rearranged results from Pyomo from the result DataFrame are transferred into a nested dictionary of pandas objects. @@ -172,11 +136,6 @@ def results(om): * Instead of a pd.Series, a pd.DataFrame holds scalar values indexed by periods. These hold investment-related variables. - Since with the introduction of the multi-period feature, variables are now - indexed differently, this needs to be sorted out again. I.e., dependent on - the variable type, the proper index has to be mapped to the timestep resp. - timeindex indices. - Examples -------- * *Standard model*: `results[idx]['scalars']` @@ -186,24 +145,12 @@ def results(om): """ # Extraction steps that are the same for both model types df = create_dataframe(om) - period_indexed = ["invest", "total", "old", "old_end", "old_exo"] - period_timestep_indexed = ["flow"] - - double_indexed_vars = _check_and_return_double_indexed_vars(om) - - timestep_indexed = [ - el - for el in df["variable_name"].unique() - if el not in period_indexed - and el not in period_timestep_indexed - and el not in double_indexed_vars - ] # create a dict of dataframes keyed by oemof tuples df_dict = { k if len(k) > 1 - else (k[0], None): v[["timeindex", "variable_name", "value"]] + else (k[0], None): v[["timestep", "variable_name", "value"]] for k, v in df.groupby("oemof_tuple") } @@ -211,24 +158,17 @@ def results(om): # Standard model results extraction if not om.es.multi_period: - result = _extract_standard_model_result( - om, - df_dict, - period_indexed, - timestep_indexed, - period_timestep_indexed, - result, - ) + result = _extract_standard_model_result(om, df_dict, result) scalars_col = "scalars" # Results extraction for a multi-period model else: + period_indexed = ["invest", "total", "old", "old_end", "old_exo"] + result = _extract_multi_period_model_result( om, df_dict, period_indexed, - timestep_indexed, - period_timestep_indexed, result, ) scalars_col = "period_scalars" @@ -255,50 +195,12 @@ def results(om): return result -def _check_and_return_double_indexed_vars(om): - """Checks if there are any double indexed vars; returns a list of these - - Double indexed vars are variables with a tuple as timeindex which does NOT - correspond to the timeindex of the model, but contain some other time- - related information. - - So far, this is only used for SinkDSM with approaches "DLR" and "DIW". - """ - double_indexed_vars = [] - if hasattr(om, "SinkDSMDLRBlock") or hasattr( - om, "SinkDSMDLRInvestmentBlock" - ): - double_indexed_vars = [ - "balance_dsm_do", - "balance_dsm_up", - "dsm_do_shift", - "dsm_up", - ] - elif hasattr(om, "SinkDSMDIWBlock") or hasattr( - om, "SinkDSMDIWInvestmentBlock" - ): - double_indexed_vars = [ - "dsm_do_shift" - ] - - return double_indexed_vars - - -def _extract_standard_model_result( - om, - df_dict, - period_indexed=None, - timestep_indexed=None, - period_timestep_indexed=None, - result=None, -): +def _extract_standard_model_result(om, df_dict, result): """Extract and return the results of a standard model * Set index to timeindex and pivot results such that values are displayed - for the respective variables. Replace / map indices such that everything - is ultimately indexed by timesteps (not periods or timeindex) and reindex - with the energy system's timeindex. - * Filter for columns with nan values to retreive scalar variables. Split + for the respective variables. Reindex with the energy system's timeindex. + * Filter for columns with nan values to retrieve scalar variables. Split up the DataFrame into sequences and scalars and return it. Parameters @@ -307,12 +209,6 @@ def _extract_standard_model_result( The optimization model df_dict : dict dictionary of results DataFrames - period_indexed : list - list of variables that are indexed by periods - timestep_indexed : list - list of variables that are indexed by timesteps - period_timestep_indexed : list - list of variables that are indexed by period and timesteps (timeindex) result : dict dictionary to store the results @@ -322,25 +218,7 @@ def _extract_standard_model_result( dictionary with results stored """ for k in df_dict: - df_dict[k].set_index("timeindex", inplace=True) - df_dict[k] = df_dict[k].pivot(columns="variable_name", values="value") - try: - # Enable reindexing by replacing period and timeindex indices - df_dict[k] = _replace_non_timestep_indices( - df_dict[k], - timestep_indexed, - period_timestep_indexed, - period_indexed, - ) - df_dict[k].index = om.es.timeindex - except ValueError as e: - msg = ( - "\nFlowBlock: {0}-{1}. This could be caused by NaN-values in" - " your input data." - ) - raise type(e)( - str(e) + msg.format(k[0].label, k[1].label) - ).with_traceback(sys.exc_info()[2]) + df_dict[k] = _do_basic_results_extraction(om, df_dict, k) try: condition = df_dict[k].isnull().any() scalars = df_dict[k].loc[:, condition].dropna().iloc[0] @@ -357,88 +235,57 @@ def _extract_standard_model_result( return result -def _replace_non_timestep_indices( - df, timestep_indexed, period_timestep_indexed, period_indexed=None -): - """Replace timeindex values by timesteps values +def _do_basic_results_extraction(om, df_dict, k, reindex=True): + """Do a basic iterative results extraction for node k - Use subsets defining how variables are indexed and rename the timeindexed - ones by replacing the tuple by its last entry, i.e. the timestep + Set index to timeindex and pivot results such that values are displayed + for the respective variables. Use energy system's timeindex as index + if reindex = True (default; used for a standard model). Parameters ---------- - df : pd.DataFrame - DataFrame obtained from the dict of results DataFrames - timestep_indexed : list - List of all values that are indexed by timesteps - period_timestep_indexed : list - list of variables that are indexed by period and timesteps (timeindex) - period_indexed : list or None - List of all values that are indexed by periods + om : oemof.solph.models.Model + The optimization model + df_dict : dict + dictionary of results DataFrames + k : + oemof tuple + reindex : boolean + Reindex using the energy system's timeindex as an index if True Returns ------- - df : pd.DataFrame - Manipulated DataFrame containing only timestep indices + df_dict[k] : pd.DataFrame + Manipulated results for node k """ - rename_dict = {key: (key[1],) for key in df.index if len(key) > 1} - to_concat = [] - - # Split into different data sets dependent on indexation - # Variables indexed by periods and timestep (i.e. timeindex) - period_timestep_indexed_df = df[ - [col for col in df.columns if col in period_timestep_indexed] - ] - - period_timestep_indexed_df = period_timestep_indexed_df.dropna() - period_timestep_indexed_df = period_timestep_indexed_df.rename( - index=rename_dict - ) - if not period_timestep_indexed_df.empty: - to_concat.append(period_timestep_indexed_df) - - # Variables indexed by timestep - timestep_indexed_df = df[ - [col for col in df.columns if col in timestep_indexed] - ] - - # TODO: Also handle SinkDSM for DIW and DLR approach! - # Handle storages differently - if "storage_content" not in timestep_indexed_df.columns: - timestep_indexed_df = timestep_indexed_df.dropna() - if not timestep_indexed_df.empty: - to_concat.append(timestep_indexed_df) - - # Variables indexed by periods (standard model only) - if period_indexed is not None: - period_indexed_df = df[ - [col for col in df.columns if col in period_indexed] - ] - period_indexed_df = period_indexed_df.dropna() - if not period_indexed_df.empty: - to_concat.append(period_indexed_df) + df_dict[k].set_index("timestep", inplace=True) + df_dict[k] = df_dict[k].pivot(columns="variable_name", values="value") - # HACK! For now simply skip SinkDSM units not properly handled! - if len(to_concat) >= 1: - df = pd.concat(to_concat, axis=1) - else: - df = df.dropna() + if reindex: + try: + df_dict[k].index = om.es.timeindex + except ValueError as e: + msg = ( + "\nFlowBlock: {0}-{1}. This could be caused by NaN-values in" + " your input data." + ) + raise type(e)( + str(e) + msg.format(k[0].label, k[1].label) + ).with_traceback(sys.exc_info()[2]) - return df + return df_dict[k] def _extract_multi_period_model_result( om, df_dict, period_indexed=None, - timestep_indexed=None, - period_timestep_indexed=None, result=None, ): """Extract and return the results of a multi-period model - * Set index to timeindex and pivot results such that values are displayed - for the respective variables. + Difference to standard model is in the way, scalar values are extracted + since they now depend on periods. Parameters ---------- @@ -448,45 +295,32 @@ def _extract_multi_period_model_result( dictionary of results DataFrames period_indexed : list list of variables that are indexed by periods - timestep_indexed : list - list of variables that are indexed by timesteps - period_timestep_indexed : list - list of variables that are indexed by periods and timesteps (timeindex) result : dict dictionary to store the results - """ - to_be_ignored = ["init_content"] - timestep_indexed = [ - var for var in timestep_indexed if var not in to_be_ignored - ] + Returns + ------- + result : dict + dictionary with results stored + """ for k in df_dict: - df_dict[k].set_index("timeindex", inplace=True) - df_dict[k] = df_dict[k].pivot(columns="variable_name", values="value") - try: - # Enable reindexing by replacing period and timeindex indices - sequences = _replace_non_timestep_indices( - df_dict[k], timestep_indexed, period_timestep_indexed - ) - sequences.index = om.es.timeindex - except ValueError as e: - msg = ( - "\nFlowBlock: {0}-{1}. This could be caused by NaN-values in" - " your input data." - ) - raise type(e)( - str(e) + msg.format(k[0].label, k[1].label) - ).with_traceback(sys.exc_info()[2]) + df_dict[k] = _do_basic_results_extraction( + om, df_dict, k, reindex=False + ) # Split data set period_cols = [ col for col in df_dict[k].columns if col in period_indexed ] # map periods to their start years for displaying period results d = { - (key,): val + om.es.periods[0].min().year + key: val + om.es.periods[0].min().year for key, val in om.es.periods_years.items() } - period_scalars = df_dict[k][period_cols].dropna() + period_scalars = df_dict[k].loc[:, period_cols].dropna() + sequences = df_dict[k].loc[ + :, [col for col in df_dict[k].columns if col not in period_cols] + ] + sequences.index = om.es.timeindex if period_scalars.empty: period_scalars = pd.DataFrame(index=d.values()) try: diff --git a/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_dispatch_model.py b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_dispatch_model.py index 946c173cf..6e5c2b1f7 100644 --- a/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_dispatch_model.py +++ b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_dispatch_model.py @@ -291,7 +291,7 @@ def test_multi_period_dispatch_model(solver="cbc"): "FR_sink_el": 450, "FR_sink_excess": 0, "link_DE_FR": 2, - "demand_dsm": 180, + "demand_dsm": 281, } for key in test_results.keys(): From 28456452005b69eb1da4568d5bab2b728624b075 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Thu, 10 Mar 2022 18:47:42 +0100 Subject: [PATCH 0184/1363] Modify changelog --- docs/changelog.rst | 1 + docs/whatsnew/v0-5-0.rst | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index be0a4ef72..b9d6355eb 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -9,6 +9,7 @@ These are new features and improvements of note in each release :backlinks: top +.. include:: whatsnew/v0-5-0.rst .. include:: whatsnew/v0-4-4.rst .. include:: whatsnew/v0-4-2.rst .. include:: whatsnew/v0-4-1.rst diff --git a/docs/whatsnew/v0-5-0.rst b/docs/whatsnew/v0-5-0.rst index c95435e6f..0ea5264fc 100644 --- a/docs/whatsnew/v0-5-0.rst +++ b/docs/whatsnew/v0-5-0.rst @@ -16,10 +16,21 @@ New features ############ * Add `inactivity_costs` as an option for `NonConvexFlow`. +* Add option to run multi-period (dynamic) investment models with oemof.solph as an experimental feature: + * You can change from standard model to multi-period model by defining `multi_period=True` + as attribute of your energy system. Be aware that it is experimental as of now. + * Introduce new Pyomo Sets `PERIODS` and `TIMEINDEX` in ``Model`. + * Index all investment-related variables with `PERIODS` and flow variable with `TIMEINDEX`. + * Add lifetime tracking for investment options by introducing the attributes `lifetime` and `age`. + * Add new investment-related variables `total` holding the total capacity, `old` holding capacity + to be decommissioned, `old_exo` (for exogenous) holding existing capacity to be decommissioned and + `old_end` holding model-endogenously installed capacity to be decommissioned after its lifetime. + * Include discounting and calculating annuities in the objective function terms. Documentation ############# +* See extensive documentation and API reference for the new (experimental) multi-period feature. Bug fixes ######### @@ -38,4 +49,5 @@ Contributors ############ * Patrik Schönfeldt +* Johannes Kochems From a9b0473ce3c8daffddecf1727df0b4f8c7820be8 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Thu, 10 Mar 2022 19:28:54 +0100 Subject: [PATCH 0185/1363] Adjust _investment_flow docs --- src/oemof/solph/flows/_investment_flow.py | 287 +++++++++++++++++----- 1 file changed, 229 insertions(+), 58 deletions(-) diff --git a/src/oemof/solph/flows/_investment_flow.py b/src/oemof/solph/flows/_investment_flow.py index b5dd2aea9..1294be115 100644 --- a/src/oemof/solph/flows/_investment_flow.py +++ b/src/oemof/solph/flows/_investment_flow.py @@ -43,58 +43,144 @@ def __init__(self, **kwargs): class InvestmentFlowBlock(SimpleBlock): r"""Block for all flows with :attr:`Investment` being not None. - See :class:`oemof.solph.options.Investment` for all parameters of the + See :class:`.Investment` for all parameters of the *Investment* class. - See :class:`oemof.solph.network.FlowBlock` for all parameters of the *FlowBlock* + See :class:`.FlowBlock` for all parameters of the *FlowBlock* class. **Variables** - All *InvestmentFlowBlock* are indexed by a starting and ending node + All *InvestmentFlowBlock*s are indexed by a starting and ending node :math:`(i, o)`, which is omitted in the following for the sake of convenience. The following variables are created: - * :math:`P(t)` + * :math:`P(p, t)` - Actual flow value (created in :class:`oemof.solph.models.BaseModel`). + Actual flow value (created in :class:`.BaseModel`), + indexed by tuple of periods p and timestep t - * :math:`P_{invest}` + * :math:`P_{invest}(p)` - Value of the investment variable, i.e. equivalent to the nominal - value of the flows after optimization. + Value of the investment variable in period p, + equal to what is being invested and equivalent / similar to + the nominal value of the flows after optimization. - * :math:`b_{invest}` + * :math:`P_{total}(p)` + + Total installed capacity / energy in period p, + equivalent to the nominal value of the flows after optimization. + + * :math:`P_{old}(p)` + + Old capacity / energy to be decommissioned in period p + due to reaching its lifetime; applicable only for multi-period models. + + * :math:`P_{old,exo}(p)` + + Old exogenous capacity / energy to be decommissioned in period p + due to reaching its lifetime, i.e. the amount that has been specified + by :attr:`existing` when it is decommisioned; + applicable only for multi-period models. + + * :math:`P_{old,end}(p)` + + Old endogenous capacity / energy to be decommissioned in period p + due to reaching its lifetime, i.e. the amount that has been invested in + by the model itself that is decommissioned in a later period because + of reaching its lifetime; + applicable only for multi-period models. + + * :math:`b_{invest}(p)` Binary variable for the status of the investment, if :attr:`nonconvex` is `True`. **Constraints** - Depending on the attributes of the *InvestmentFlowBlock* and *FlowBlock*, different - constraints are created. The following constraint is created for all - *InvestmentFlowBlock*:\ + Depending on the attributes of the *InvestmentFlowBlock* and *FlowBlock*, + different constraints are created. The following constraints are created + for all *InvestmentFlowBlock*s:\ + + Total capacity / energy + + .. math:: + & + if \quad p=0:\\ + & + P_{total}(p) = P_{invest}(p) + P_{exist}(p) \\ + & + else:\\ + & + P_{total}(p) = P_{total}(p-1) + P_{invest}(p) - P_{old}(p) \\ + & + \forall p \in \textrm{PERIODS} + + Upper bound for the flow value - Upper bound for the flow value + .. math:: + P(p, t) \le ( P_{total}(p) ) \cdot f_{max}(t) \\ + \forall p, t \in \textrm{TIMEINDEX} + + For a multi-period model, the old capacity is defined as follows: .. math:: - P(t) \le ( P_{invest} + P_{exist} ) \cdot f_{max}(t) + & + P_{old}(p) = P_{old,exo}(p) + P_{old,end}(p)\\ + &\\ + & + if \quad p=0:\\ + & + P_{old,end}(p) = 0\\ + & + else \quad if \quad l \leq year(p):\\ + & + P_{old,end}(p) = P_{invest}(p_{comm})\\ + & + else:\\ + & + P_{old,end}(p)\\ + &\\ + & + if \quad p=0:\\ + & + P_{old,exo}(p) = 0\\ + & + else \quad if \quad l - a \leq year(p):\\ + & + P_{old,exo}(p) = P_{exist} (*)\\ + & + else:\\ + & + P_{old,exo}(p) = 0\\ + & + \forall p \in \textrm{PERIODS} + + whereby: - Depeding on the attribute :attr:`nonconvex`, the constraints for the bounds - of the decision variable :math:`P_{invest}` are different:\ + * (*) is only performed for the first period the condition is True. + A decommissioning flag is set to True. + * :math:`year(p)` is the year corresponding to period p + * :math:`p_{comm}` is the commissioning period of the flow + + Depending on the attribute :attr:`nonconvex`, the constraints for the + bounds of the decision variable :math:`P_{invest}(p)` are different:\ * :attr:`nonconvex = False` .. math:: - P_{invest, min} \le P_{invest} \le P_{invest, max} + P_{invest, min}(p) \le P_{invest}(p) \le P_{invest, max}(p) \\ + \forall p \in \textrm{PERIODS} * :attr:`nonconvex = True` .. math:: & - P_{invest, min} \cdot b_{invest} \le P_{invest}\\ + P_{invest, min}(P) \cdot b_{invest}(P) \le P_{invest}(p)\\ + & + P_{invest}(p) \le P_{invest, max}(p) \cdot b_{invest}(p)\\ & - P_{invest} \le P_{invest, max} \cdot b_{invest}\\ + \forall p \in \textrm{PERIODS} For all *InvestmentFlowBlock* (independent of the attribute :attr:`nonconvex`), the following additional constraints are created, if the appropriate @@ -105,51 +191,116 @@ class InvestmentFlowBlock(SimpleBlock): Actual value constraint for investments with fixed flow values .. math:: - P(t) = ( P_{invest} + P_{exist} ) \cdot f_{fix}(t) + P(p, t) = P_{total}(p) \cdot f_{fix}(t) \\ + \forall p, t \in \textrm{TIMEINDEX} * :attr:`min != 0` Lower bound for the flow values .. math:: - P(t) \geq ( P_{invest} + P_{exist} ) \cdot f_{min}(t) + P(p, t) \geq P_{total}(p) \cdot f_{min}(t) \\ + \forall p, t \in \textrm{TIMEINDEX} - * :attr:`summed_max is not None` + * :attr:`summed_max` is not None Upper bound for the sum of all flow values (e.g. maximum full load hours) .. math:: - \sum_t P(t) \cdot \tau(t) \leq ( P_{invest} + P_{exist} ) + \sum_{p, t} P(p, t) \cdot \tau(t) \leq P_{total}(p) \cdot f_{sum, min} - * :attr:`summed_min is not None` + * :attr:`summed_min` is not None Lower bound for the sum of all flow values (e.g. minimum full load hours) .. math:: - \sum_t P(t) \cdot \tau(t) \geq ( P_{invest} + P_{exist} ) + \sum_{p, t} P(t) \cdot \tau(t) \geq P_{total} \cdot f_{sum, min} + * :attr:`overall_maximum` is not None (for multi-period model only) + + Overall maximum of total installed capacity / energy for flow + + .. math:: + P_{total}(p) \leq P_{overall,max} \\ + \forall p \in \textrm{PERIODS} + + * :attr:`overall_minimum` is not None (for multi-period model only) + + Overall minimum of total installed capacity / energy for flow + + .. math:: + P_{total}(p_{last}) \geq P_{overall,min} + **Objective function** - The part of the objective function added by the *InvestmentFlowBlock* - also depends on whether a convex or nonconvex - *InvestmentFlowBlock* is selected. The following parts of the objective function - are created: + Objective terms for a standard model and a multi-period model differ + quite strongly. Besides, the part of the objective function added by the + *InvestmentFlowBlock* also depends on whether a convex or nonconvex + *InvestmentFlowBlock* is selected. The following parts of the objective + function are created: + + *Standard model* + + * :attr:`nonconvex = False` + + .. math:: + P_{invest}(0) \cdot c_{invest,var}(0) + + * :attr:`nonconvex = True` + + .. math:: + P_{invest}(0) \cdot c_{invest,var}(0) + + c_{invest,fix}(0) \cdot b_{invest}(0) \\ + + + *Multi-period model* * :attr:`nonconvex = False` .. math:: - P_{invest} \cdot c_{invest,var} + & + P_{invest}(p) \cdot A(c_{invest,var}(p), l, ir) \cdot l + \cdot DF^{-p}\\ + & + \forall p \in \textrm{PERIODS} * :attr:`nonconvex = True` .. math:: - P_{invest} \cdot c_{invest,var} - + c_{invest,fix} \cdot b_{invest}\\ + & + P_{invest}(p) \cdot A(c_{invest,var}(p), l, ir) \cdot l + \cdot DF^{-p} + c_{invest,fix}(p) \cdot b_{invest}(p)\\ + & + \forall p \in \textrm{PERIODS} + + * :attr:`fixed_costs` not None for investments + + .. math:: + & + \sum_{pp=year(p)}^{year(p)+l} + P_{invest}(p) \cdot c_{fixed}(pp) \cdot DF^{-pp}) + \cdot DF^{-p}\\ + & + \forall p \in \textrm{PERIODS} + + * :attr:`fixed_costs` not None for existing capacity + + .. math:: + \sum_{pp=0}^{l-a} P_{exist} \cdot c_{fixed}(pp) + \cdot DF^{-pp} + + + whereby: + + * :math:`A(c_{invest,var}(p), l, ir)` A is the annuity for + investment expenses :math:`c_{invest,var}(p)` lifetime :math:`l` and + interest rate :math:`ir` + * :math:`DF=(1+dr)` is the discount factor with discount rate math:`dr` The total value of all costs of all *InvestmentFlowBlock* can be retrieved calling :meth:`om.InvestmentFlowBlock.investment_costs.expr()`. @@ -158,36 +309,56 @@ class InvestmentFlowBlock(SimpleBlock): :header: "symbol", "attribute", "explanation" :widths: 1, 1, 1 - ":math:`P(t)`", ":py:obj:`flow[n, o, t]`", "Actual flow value" - ":math:`P_{invest}`", ":py:obj:`invest[i, o]`", "Invested flow + ":math:`P(p, t)`", ":py:obj:`flow[n, o, p, t]`", "Actual flow value" + ":math:`P_{invest}(p)`", ":py:obj:`invest[i, o, p]`", "Invested flow capacity" - ":math:`b_{invest}`", ":py:obj:`invest_status[i, o]`", "Binary status + ":math:`P_{total}(p, t)`", ":py:obj:`total[n, o, p]`", "Total flow capacity / energy" + ":math:`P_{old}(p, t)`", ":py:obj:`old[n, o, p]`", "Old flow capacity / energy" + ":math:`P_{old,exo}(p, t)`", ":py:obj:`old_exo[n, o, p]`", "Old exogenous flow capacity / energy" + ":math:`P_{old,end}(p, t)`", ":py:obj:`old_end[n, o, p]`", "Old endogenous flow capacity / energy" + ":math:`b_{invest}(p)`", ":py:obj:`invest_status[i, o, p]`", "Binary status of investment" List of Variables (in rst table syntax): - =================== ============================= ========= - symbol attribute explanation - =================== ============================= ========= - :math:`P(t)` :py:obj:`flow[n, o, t]` Actual flow value + ========================= ================================= ========= + symbol attribute explanation + ========================= ================================= ========= + :math:`P(p, t)` :py:obj:`flow[n, o, p, t]` Actual flow value + + :math:`P_{invest}(p)` :py:obj:`invest[i, o, p]` Invested flow capacity + + :math:`P_{total}(p, t)` :py:obj:`total[n, o, p]` Total flow capacity / energy + + :math:`P_{old}(p, t)` :py:obj:`old[n, o, p]` Old flow capacity / energy + + :math:`P_{old,exo}(p, t)` :py:obj:`old_exo[n, o, p]` Old exogenous flow capacity / energy - :math:`P_{invest}` :py:obj:`invest[i, o]` Invested flow capacity + :math:`P_{old,end}(p, t)` :py:obj:`old_end[n, o, p]` Old endogenous flow capacity / energy - :math:`b_{invest}` :py:obj:`invest_status[i, o]` Binary status of investment + :math:`b_{invest}(p)` :py:obj:`invest_status[i, o, p]` Binary status of investment - =================== ============================= ========= + ========================= ================================= ========= Grid table style: - +--------------------+-------------------------------+-----------------------------+ - | symbol | attribute | explanation | - +====================+===============================+=============================+ - | :math:`P(t)` | :py:obj:`flow[n, o, t]` | Actual flow value | - +--------------------+-------------------------------+-----------------------------+ - | :math:`P_{invest}` | :py:obj:`invest[i, o]` | Invested flow capacity | - +--------------------+-------------------------------+-----------------------------+ - | :math:`b_{invest}` | :py:obj:`invest_status[i, o]` | Binary status of investment | - +--------------------+-------------------------------+-----------------------------+ + +------------------------+----------------------------------+--------------------------------------------+ + | symbol | attribute | explanation | + +========================+==================================+============================================+ + | :math:`P(p, t)` | :py:obj:`flow[n, o, p, t]` | Actual flow value | + +------------------------+----------------------------------+--------------------------------------------+ + | :math:`P_{invest}(p)` | :py:obj:`invest[i, o, p]` | Invested flow capacity | + +------------------------+----------------------------------+--------------------------------------------+ + | :math:`P_{total}(p)` | :py:obj:`total[i, o, p]` | Total flow capacity / energy | + +------------------------+----------------------------------+--------------------------------------------+ + | :math:`P_{old}(p)` | :py:obj:`old[n, o, p]` | Old flow capacity / energy | + +------------------------+----------------------------------+--------------------------------------------+ + | :math:`P_{old,exo}(p)` | :py:obj:`old_exo[n, o, p]` | Old exogenous flow capacity / energy | + +------------------------+----------------------------------+--------------------------------------------+ + | :math:`P_{old,end}(p)` | :py:obj:`old_end[n, o, p]` | Old endogenous flow capacity / energy | + +------------------------+----------------------------------+--------------------------------------------+ + | :math:`b_{invest}(p)` | :py:obj:`invest_status[n, o, p]` | Binary status of investment | + +------------------------+----------------------------------+--------------------------------------------+ .. csv-table:: List of Parameters :header: "symbol", "attribute", "explanation" @@ -195,13 +366,13 @@ class InvestmentFlowBlock(SimpleBlock): ":math:`P_{exist}`", ":py:obj:`flows[i, o].investment.existing`", " Existing flow capacity" - ":math:`P_{invest,min}`", ":py:obj:`flows[i, o].investment.minimum`", " + ":math:`P_{invest,min}(p)`", ":py:obj:`flows[i, o].investment.minimum[p]`", " Minimum investment capacity" - ":math:`P_{invest,max}`", ":py:obj:`flows[i, o].investment.maximum`", " + ":math:`P_{invest,max}(p)`", ":py:obj:`flows[i, o].investment.maximum[p]`", " Maximum investment capacity" - ":math:`c_{invest,var}`", ":py:obj:`flows[i, o].investment.ep_costs` + ":math:`c_{invest,var}(p)`", ":py:obj:`flows[i, o].investment.ep_costs[p]` ", "Variable investment costs" - ":math:`c_{invest,fix}`", ":py:obj:`flows[i, o].investment.offset`", " + ":math:`c_{invest,fix}(p)`", ":py:obj:`flows[i, o].investment.offset[p]`", " Fix investment costs" ":math:`f_{actual}`", ":py:obj:`flows[i, o].fix[t]`", "Normed fixed value for the flow variable" @@ -225,9 +396,9 @@ class InvestmentFlowBlock(SimpleBlock): Note ---- - See also :class:`oemof.solph.network.FlowBlock`, - :class:`oemof.solph.blocks.FlowBlock` and - :class:`oemof.solph.options.Investment` + See also :class:`.FlowBlock`, + :class:`.FlowBlock` and + :class:`.Investment` """ # noqa: E501 From 1ef81aff1a6bc8d00bd0bb38e51081c8efde1acf Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 11 Mar 2022 10:15:34 +0100 Subject: [PATCH 0186/1363] Remove nonconvex gradient costs (not used anyhow) --- src/oemof/solph/flows/_non_convex_flow.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/oemof/solph/flows/_non_convex_flow.py b/src/oemof/solph/flows/_non_convex_flow.py index 3fe1e15b5..61643ab6c 100644 --- a/src/oemof/solph/flows/_non_convex_flow.py +++ b/src/oemof/solph/flows/_non_convex_flow.py @@ -70,8 +70,7 @@ class NonConvexFlow(Flow): * `'ub'`: numeric (iterable, scalar or None), the normed *upper bound* on the positive difference (`flow[t-1] < flow[t]`) of two consecutive flow values. - * `'costs``: numeric (scalar or None), the gradient cost per - unit. + * `'costs'`: REMOVED! negative_gradient : :obj:`dict`, default: `{'ub': None, 'costs': 0}` A dictionary containing the following two keys: @@ -79,8 +78,7 @@ class NonConvexFlow(Flow): * `'ub'`: numeric (iterable, scalar or None), the normed *upper bound* on the negative difference (`flow[t-1] > flow[t]`) of two consecutive flow values. - * `'costs``: numeric (scalar or None), the gradient cost per - unit. + * `'costs'`: REMOVED! """ def __init__( @@ -717,7 +715,6 @@ def _objective_expression(self): shutdown_costs = 0 activity_costs = 0 inactivity_costs = 0 - gradient_costs = 0 if not m.es.multi_period: if self.STARTUPFLOWS: @@ -807,7 +804,6 @@ def _objective_expression(self): self.activity_costs = Expression(expr=activity_costs) self.inactivity_costs = Expression(expr=inactivity_costs) - self.gradient_costs = Expression(expr=gradient_costs) self.startup_costs = Expression(expr=startup_costs) self.shutdown_costs = Expression(expr=shutdown_costs) @@ -817,7 +813,6 @@ def _objective_expression(self): + shutdown_costs + activity_costs + inactivity_costs - + gradient_costs ) ) From 19fe35dbfe259b668cabb474984c95f161691554 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 11 Mar 2022 11:29:09 +0100 Subject: [PATCH 0187/1363] Add / fix / update / prettify docstrings --- src/oemof/solph/_options.py | 4 +- .../solph/components/_generic_storage.py | 52 ++++++++-- src/oemof/solph/components/_transformer.py | 12 +-- .../components/experimental/_generic_caes.py | 38 ++++---- .../solph/constraints/flow_count_limit.py | 1 + src/oemof/solph/constraints/integral_limit.py | 4 +- .../solph/constraints/investment_limit.py | 7 +- src/oemof/solph/flows/_flow.py | 76 +++++++++------ src/oemof/solph/flows/_investment_flow.py | 96 +++++++++++++++---- src/oemof/solph/flows/_non_convex_flow.py | 56 +++++++---- .../flows/experimental/_electrical_line.py | 8 +- src/oemof/solph/processing.py | 6 +- src/oemof/solph/views.py | 75 ++++++++------- 13 files changed, 292 insertions(+), 143 deletions(-) diff --git a/src/oemof/solph/_options.py b/src/oemof/solph/_options.py index bb70936c4..9fe6d5047 100644 --- a/src/oemof/solph/_options.py +++ b/src/oemof/solph/_options.py @@ -207,7 +207,7 @@ class NonConvex: * `'ub'`: numeric (iterable, scalar or None), the normed *upper bound* on the positive difference (`flow[t-1] < flow[t]`) of two consecutive flow values. - * `'costs``: numeric (scalar or None), the gradient cost per + * `'costs'`: numeric (scalar or None), the gradient cost per unit. negative_gradient : :obj:`dict`, default: `{'ub': None, 'costs': 0}` @@ -216,7 +216,7 @@ class NonConvex: * `'ub'`: numeric (iterable, scalar or None), the normed *upper bound* on the negative difference (`flow[t-1] > flow[t]`) of two consecutive flow values. - * `'costs``: numeric (scalar or None), the gradient cost per + * `'costs'`: numeric (scalar or None), the gradient cost per unit. """ diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index 5803abcfe..eeae9b44a 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -412,7 +412,7 @@ class GenericStorageBlock(SimpleBlock): \cdot c_{fixed}(p) \cdot DF^{-p} whereby: - :math:`DF=(1+dr)` is the discount factor with discount rate math:`dr` + :math:`DF=(1+dr)` is the discount factor with discount rate :math:`dr` """ # noqa: E501 @@ -695,10 +695,12 @@ class GenericInvestmentStorageBlock(SimpleBlock): if \quad p=0:\\ & E_{total}(p) = E_{exist} + E_{invest}(p)\\ + &\\ & else:\\ & E_{total}(p) = E_{total}(p-1) + E_{invest}(p) - E_{old}(p)\\ + &\\ & \forall p \in \textrm{PERIODS} @@ -712,10 +714,12 @@ class GenericInvestmentStorageBlock(SimpleBlock): if \quad p=0:\\ & E_{old,end}(p) = 0\\ + &\\ & else \quad if \quad l \leq year(p):\\ & E_{old,end}(p) = E_{invest}(p_{comm})\\ + &\\ & else:\\ & @@ -725,21 +729,25 @@ class GenericInvestmentStorageBlock(SimpleBlock): if \quad p=0:\\ & E_{old,exo}(p) = 0\\ + &\\ & else \quad if \quad l - a \leq year(p):\\ & E_{old,exo}(p) = E_{exist} (*)\\ + &\\ & else:\\ & E_{old,exo}(p) = 0\\ + &\\ & \forall p \in \textrm{PERIODS} whereby: * (*) is only performed for the first period the condition is True. - A decommissioning flag is set to True. + A decommissioning flag is then set to True to prevent having falsely + added old capacity in future periods. * :math:`year(p)` is the year corresponding to period p * :math:`p_{comm}` is the commissioning period of the storage @@ -867,10 +875,6 @@ class GenericInvestmentStorageBlock(SimpleBlock): E_{invest}(0) \cdot c_{invest,var}(0) + c_{invest,fix}(0) \cdot b_{invest}(0)\\ - The total value of all investment costs of all *InvestmentStorages* - can be retrieved calling - :math:`om.GenericInvestmentStorageBlock.investment_costs.expr()`. - *Multi-period model* * :attr:`nonconvex = False` @@ -915,9 +919,32 @@ class GenericInvestmentStorageBlock(SimpleBlock): interest rate :math:`ir` * :math:`DF=(1+dr)` is the discount factor with discount rate math:`dr` - The total value of all investment costs of all *InvestmentStorages* - can be retrieved calling - `om.GenericInvestmentStorageBlock.investment_costs.expr()`. + The annuity hereby is: + + .. math:: + + A(c_{invest,var}(p), l, ir) = c_{invest,var}(p) \cdot + \frac {(1+i)^l \cdot i} {(1+i)^l - 1} \cdot + + It is retrieved, using oemof.tools.economics annuity function. The + interest rate is defined as a weighted average costs of capital (wacc) and + assumed constant over time. + + The overall summed cost expressions for all *InvestmentFlowBlock* objects + can be accessed by + + * :attr:`om.GenericInvestmentStorageBlock.investment_costs`, + * :attr:`om.GenericInvestmentStorageBlock.fixed_costs` and + * :attr:`om.GenericInvestmentStorageBlock.costs`. + + Their values after optimization can be retrieved by + + * :meth:`om.GenericInvestmentStorageBlock.investment_costs`, + * :attr:`om.GenericInvestmentStorageBlock.period_investment_costs` + (yielding a dict keyed by periods); note: this is not a Pyomo expression, + but calculated, + * :meth:`om.GenericInvestmentStorageBlock.fixed_costs` and + * :meth:`om.GenericInvestmentStorageBlock.costs`. .. csv-table:: List of Variables :header: "symbol", "attribute", "explanation" @@ -972,6 +999,8 @@ class GenericInvestmentStorageBlock(SimpleBlock): ", "Variable investment costs" ":math:`c_{invest,fix}`", "`flows[i, o].investment.offset`", " Fix investment costs" + ":math:`c_{fixed}`", "`flows[i, o].investment.fixed_costs`", " + Fixed costs; only allowed in multi-period model" ":math:`r_{cap,in}`", ":attr:`invest_relation_input_capacity`", " Relation of storage capacity and nominal inflow" ":math:`r_{cap,out}`", ":attr:`invest_relation_output_capacity`", " @@ -994,6 +1023,11 @@ class GenericInvestmentStorageBlock(SimpleBlock): value of storage content" ":math:`c_{min}`", "`flows[i, o].min[t]`", "Normed minimum value of storage content" + ":math:`l`", ":py:obj:`flows[i, o].investment.lifetime`", " + Lifetime for investments in storage capacity" + ":math:`a`", ":py:obj:`flows[i, o].investment.age`", " + Initial age of existing capacity / energy" + ":math:`ir`", ":py:obj:`flows[i, o].investment.interest_rate`", " ":math:`\tau(t)`", "", "Duration of time step" ":math:`t_u`", "", "Time unit of losses :math:`\beta(t)`, :math:`\gamma(t)`, :math:`\delta(t)` and timeincrement :math:`\tau(t)`" diff --git a/src/oemof/solph/components/_transformer.py b/src/oemof/solph/components/_transformer.py index eed4ae25c..d833b5f39 100644 --- a/src/oemof/solph/components/_transformer.py +++ b/src/oemof/solph/components/_transformer.py @@ -115,9 +115,9 @@ class TransformerBlock(SimpleBlock): Linear relation :attr:`om.TransformerBlock.relation[i,o,t]` .. math:: - \P_{i,n}(p, t) \times \eta_{n,o}(t) = \ - \P_{n,o}(p, t) \times \eta_{n,i}(t), \\ - \forall p, t \in \textrm{TIMINDEX}, \\ + P_{i,n}(p, t) \times \eta_{n,o}(t) = \ + P_{n,o}(p, t) \times \eta_{n,i}(t), \\ + \forall p, t \in \textrm{TIMEINDEX}, \\ \forall n \in \textrm{TRANSFORMERS}, \\ \forall i \in \textrm{INPUTS(n)}, \\ \forall o \in \textrm{OUTPUTS(n)}, @@ -126,13 +126,13 @@ class TransformerBlock(SimpleBlock): symbol attribute explanation ====================== ============================ ============= :math:`P_{i,n}(p, t)` `flow[i, n, p, t]` TransformerBlock - inflow + inflow :math:`P_{n,o}(p, t)` `flow[n, o, p, t]` TransformerBlock - outflow + outflow :math:`\eta_{i,n}(t)` `conversion_factor[i, n, t]` Conversion - efficiency + efficiency ====================== ============================ ============= """ diff --git a/src/oemof/solph/components/experimental/_generic_caes.py b/src/oemof/solph/components/experimental/_generic_caes.py index 5b8201157..ed6ae5dee 100644 --- a/src/oemof/solph/components/experimental/_generic_caes.py +++ b/src/oemof/solph/components/experimental/_generic_caes.py @@ -241,7 +241,7 @@ class GenericCAESBlock(SimpleBlock): ":math:`{P}_{cmp\_max}`", "`cmp_p_max[n,t]`", "V", "Max. compression power" ":math:`\dot{Q}_{cmp}` ", "`cmp_q_out_sum[n,t]`", "V", "Summed - heat flow in compression" + heat flow in compression" ":math:`\dot{Q}_{cmp\_out}` ", "`cmp_q_waste[n,t]`", "V", " Waste heat flow from compression" ":math:`ST_{exp}(t)`", "`exp_st[n,t]`", "V", "Status of @@ -256,7 +256,7 @@ class GenericCAESBlock(SimpleBlock): ":math:`\dot{Q}_{exp\_add}(t)`", "`exp_q_add_in[n,t]`", "V", " Additional heat flow into expansion" ":math:`CAV_{fil}(t)`", "`cav_level[n,t]`", "V", "Filling level - if CAE" + if CAE" ":math:`\dot{E}_{cas\_in}(t)`", "`cav_e_in[n,t]`", "V", " Exergy flow into CAS" ":math:`\dot{E}_{cas\_out}(t)`", "`cav_e_out[n,t]`", "V", " @@ -264,11 +264,11 @@ class GenericCAESBlock(SimpleBlock): ":math:`TES_{fil}(t)`", "`tes_level[n,t]`", "V", "Filling level of Thermal Energy Storage (TES)" ":math:`\dot{Q}_{tes\_in}(t)`", "`tes_e_in[n,t]`", "V", "Heat - flow into TES" + flow into TES" ":math:`\dot{Q}_{tes\_out}(t)`", "`tes_e_out[n,t]`", "V", "Heat - flow from TES" + flow from TES" ":math:`b_{cmp\_max}`", "`cmp_p_max_b[n,t]`", "P", "Specific - y-intersection" + y-intersection" ":math:`b_{cmp\_q}`", "`cmp_q_out_b[n,t]`", "P", "Specific y-intersection" ":math:`b_{exp\_max}`", "`exp_p_max_b[n,t]`", "P", "Specific @@ -280,32 +280,32 @@ class GenericCAESBlock(SimpleBlock): ":math:`b_{cas\_out}`", "`cav_e_out_b[n,t]`", "P", "Specific y-intersection" ":math:`m_{cmp\_max}`", "`cmp_p_max_m[n,t]`", "P", "Specific - slope" + slope" ":math:`m_{cmp\_q}`", "`cmp_q_out_m[n,t]`", "P", "Specific - slope" + slope" ":math:`m_{exp\_max}`", "`exp_p_max_m[n,t]`", "P", "Specific - slope" + slope" ":math:`m_{exp\_q}`", "`exp_q_in_m[n,t]`", "P", "Specific - slope" + slope" ":math:`m_{cas\_in}`", "`cav_e_in_m[n,t]`", "P", "Specific - slope" + slope" ":math:`m_{cas\_out}`", "`cav_e_out_m[n,t]`", "P", "Specific - slope" + slope" ":math:`P_{cmp\_min}`", "`cmp_p_min[n,t]`", "P", "Min. compression power" ":math:`r_{cmp\_tes}`", "`cmp_q_tes_share[n,t]`", "P", "Ratio - between waste heat flow and heat flow into TES" + between waste heat flow and heat flow into TES" ":math:`r_{exp\_tes}`", "`exp_q_tes_share[n,t]`", "P", "Ratio - between external heat flow into expansion and heat flows from TES and - additional source" + between external heat flow into expansion and heat flows from TES and + additional source" ":math:`\tau`", "`m.timeincrement[n,t]`", "P", "Time interval - length" + length" ":math:`TES_{fil\_max}`", "`tes_level_max[n,t]`", "P", "Max. filling level of TES" ":math:`CAS_{fil\_max}`", "`cav_level_max[n,t]`", "P", "Max. - filling level of TES" + filling level of TES" ":math:`\tau`", "`cav_eta_tmp[n,t]`", "P", "Temporal efficiency - (loss factor to take intertemporal losses into account)" + (loss factor to take intertemporal losses into account)" ":math:`electrical\_input`", " `flow[list(n.electrical_input.keys())[0], p, n, t]`", "P", " Electr. power input into compression" @@ -313,8 +313,8 @@ class GenericCAESBlock(SimpleBlock): `flow[n, list(n.electrical_output.keys())[0], p, t]`", "P", " Electr. power output of expansion" ":math:`fuel\_input`", " - `flow[list(n.fuel_input.keys())[0], n, p, t]`", "P", "Heat input - (external) into Expansion" + flow[list(n.fuel_input.keys())[0], n, p, t]`", "P", "Heat input + (external) into Expansion" """ diff --git a/src/oemof/solph/constraints/flow_count_limit.py b/src/oemof/solph/constraints/flow_count_limit.py index 331982174..5729acdad 100644 --- a/src/oemof/solph/constraints/flow_count_limit.py +++ b/src/oemof/solph/constraints/flow_count_limit.py @@ -45,6 +45,7 @@ def limit_active_flow_count( ---- FlowBlock objects required to be NonConvex + **Constraint:** .. math:: N_{X,min} \le \sum_{n \in F} X_n(t) diff --git a/src/oemof/solph/constraints/integral_limit.py b/src/oemof/solph/constraints/integral_limit.py index d1015c5cb..031366a2e 100644 --- a/src/oemof/solph/constraints/integral_limit.py +++ b/src/oemof/solph/constraints/integral_limit.py @@ -75,6 +75,7 @@ def generic_integral_limit(om, keyword, flows=None, limit=None): ---- Flow objects required an attribute named like keyword! + **Constraint:** .. math:: \sum_{i \in F_E} \sum_{t \in T} P_i(p, t) \cdot w_i(t) @@ -166,10 +167,11 @@ def generic_periodical_integral_limit(om, keyword, flows=None, limit=None): ---- Flow objects required an attribute named like keyword! + **Constraint:** .. math:: \sum_{i \in F_I} \sum_{t \in T} P_i(t) \cdot w_i(t) - \cdot \tau(t) \leq L(p) \forall p in \textrm{PERIODS} + \cdot \tau(t) \leq L(p) \forall p \in \textrm{PERIODS} For the parameter and variable explanation, please refer to the docs diff --git a/src/oemof/solph/constraints/investment_limit.py b/src/oemof/solph/constraints/investment_limit.py index 63693a321..c775437b3 100644 --- a/src/oemof/solph/constraints/investment_limit.py +++ b/src/oemof/solph/constraints/investment_limit.py @@ -59,11 +59,12 @@ def investment_rule(m): def investment_limit_per_period(model, limit=None): r"""Set an absolute limit for the total investment costs of a - investment optimization problem for each period + investment optimization problem for each period of the multi-period problem. - .. math:: \sum_{investment\_costs(p)} \leq limit(p) - \forall p in \textrm{PERIODS} + .. math:: + \sum_{investment\_costs(p)} \leq limit(p) + \forall p \in \textrm{PERIODS} Parameters ---------- diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 22f99acde..0c317938e 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -63,7 +63,7 @@ class Flow(on.Edge): * `'ub'`: numeric (iterable, scalar or None), the normed *upper bound* on the positive difference (`flow[t-1] < flow[t]`) of two consecutive flow values. - * `'costs``: REMOVED! + * `'costs'`: REMOVED! negative_gradient : :obj:`dict`, default: `{'ub': None, 'costs': 0}` @@ -72,14 +72,14 @@ class Flow(on.Edge): * `'ub'`: numeric (iterable, scalar or None), the normed *upper bound* on the negative difference (`flow[t-1] > flow[t]`) of two consecutive flow values. - * `'costs``: REMOVED! + * `'costs'`: REMOVED! summed_max : numeric, :math:`f_{sum,max}` Specific maximum value summed over all timesteps. Will be multiplied with the nominal_value to get the absolute limit. summed_min : numeric, :math:`f_{sum,min}` see above - variable_costs : numeric (iterable or scalar) + variable_costs : numeric (iterable or scalar), :math:`c_{var}` The costs associated with one unit of the flow. If this is set the costs will be added to the objective expression of the optimization problem. @@ -104,15 +104,15 @@ class Flow(on.Edge): set. integer : boolean If True, flow is forced to take only integer values - fixed_costs : numeric (iterable or scalar) + fixed_costs : numeric (iterable or scalar), :math:`c_{fixed}` The fixed costs associated with a flow. Note: These are only applicable for a multi-period model. - lifetime : int + lifetime : int, :math:`l` The lifetime of a flow (usually given in years); once it reaches its lifetime (considering also an initial age), the flow is forced to 0. Note: Only applicable for a multi-period model. - age : int + age : int, :math:`a` The initial age of a flow (usually given in years); once it reaches its lifetime (considering also an initial age), the flow is forced to 0. @@ -321,13 +321,13 @@ class FlowBlock(SimpleBlock): FlowBlock max sum :attr:`om.FlowBlock.summed_max[i, o]` .. math:: - \sum_t flow(i, o, p, t) \cdot \tau + \sum_{p, t \in \textrm{TIMEINDEX}} flow(i, o, p, t) \cdot \tau \leq summed\_max(i, o) \cdot nominal\_value(i, o), \\ \forall (i, o) \in \textrm{SUMMED\_MAX\_FLOWS}. FlowBlock min sum :attr:`om.FlowBlock.summed_min[i, o]` .. math:: - \sum_t flow(i, o, p, t) \cdot \tau + \sum_{p, t \in \textrm{TIMEINDEX}} flow(i, o, p, t) \cdot \tau \geq summed\_min(i, o) \cdot nominal\_value(i, o), \\ \forall (i, o) \in \textrm{SUMMED\_MIN\_FLOWS}. @@ -337,15 +337,18 @@ class FlowBlock(SimpleBlock): & if \quad t > 0:\\ & - flow(i, o, p t-1) - flow(i, o, p, t) \geq \ + flow(i, o, p, t-1) - flow(i, o, p, t) \geq \ negative\_gradient(i, o, t), \\ + &\\ & else:\\ & flow(i, o, p, t) = 0\\ + &\\ & \forall (i, o) \in \textrm{NEGATIVE\_GRADIENT\_FLOWS}, \\ - \forall p, t \in \textrm{TIMEINDEX}\\. + & + p, t \in \textrm{TIMEINDEX}\\. Positive gradient constraint :attr:`om.FlowBlock.positive_gradient_constr[i, o]`: @@ -355,19 +358,24 @@ class FlowBlock(SimpleBlock): & flow(i, o, p t) - flow(i, o, p, t-1) \geq \ positive\_gradient(i, o, t), \\ + &\\ & else:\\ & flow(i, o, p, t) = 0\\ + &\\ & \forall (i, o) \in \textrm{POSITIVE\_GRADIENT\_FLOWS}, \\ - \forall p, t \in \textrm{TIMEINDEX}\\. + & + p, t \in \textrm{TIMEINDEX}\\. Note ---- The gradient implementations combine a timestep and its predecessor. - This predecessor might also lie in the previous period which is taken - care of by checking the previous indices of the TIMEINDEX set. + This predecessor might also lie in the previous period. For the sake + of readability, this is not represented in the mathematical + formulation above, but taken care of by + checking the previous index of the TIMEINDEX set. Integer constraint @@ -384,25 +392,27 @@ class FlowBlock(SimpleBlock): if \quad lifetime(i, o) < year(p):\\ & flow(i, o, p, t) = 0 \\ + & \forall p, t \in \textrm{TIMEINDEX}, (i, o) \in \textrm{LIFETIME\_FLOWS} Lifetime age output constraint: Force flow to 0 once it exceeds its lifetime, considering its initial age - :attr:`om.FlowBlock.lifetime_output[i, o]`: + :attr:`om.FlowBlock.lifetime_age_output[i, o]`: .. math:: & if \quad lifetime(i, o) - age(i, o) < year(p):\\ & flow(i, o, p, t) = 0 \\ + & \forall p, t \in \textrm{TIMEINDEX}, - (i, o) \in \textrm{LIFETIME\_FLOWS} + (i, o) \in \textrm{LIFETIME\_AGE\_FLOWS} Whereby: - * year(p) is the year corresponding to period p - * lifetime(i, o) is the expected technical lifetime of flow (i, o) - * age(i, o) is the initial age of flow (i, o) + * :math:`year(p)` is the year corresponding to period p + * :math:`lifetime(i, o)` is the expected technical lifetime of flow (i, o) + * :math:`age(i, o)` is the initial age of flow (i, o) **The following parts of the objective function are created:** @@ -410,7 +420,7 @@ class FlowBlock(SimpleBlock): If :attr:`variable_costs` is set by the user: .. math:: - \sum_{(i,o)} \sum_{p, t} + \sum_{(i,o)} \sum_{p, t \in \textrm{TIMEINDEX}} flow(i, o, p, t) \cdot weight(t) \cdot variable\_costs(i, o, t) The expression can be accessed by :attr:`om.FlowBlock.variable_costs` and @@ -420,26 +430,26 @@ class FlowBlock(SimpleBlock): If :attr:`variable_costs` is set by the user: .. math:: - \sum_{(i,o)} \sum_{p, t} + \sum_{(i,o)} \sum_{p, t \in \textrm{TIMEINDEX}} flow(i, o, p, t) \cdot weight(t) \cdot variable\_costs(i, o, t) - \cdot DF^{p} + \cdot DF^{-p} If :attr:`fixed_costs` is set by the user and flow has no lifetime limitation: .. math:: - \sum_{(i,o)} \sum_p + \sum_{(i,o)} \sum_{p \in {PERIODS}} nominal\_value(i, o) \cdot fixed\_costs(i, o, p) - \cdot DF^{p} + \cdot DF^{-p} If :attr:`fixed_costs` is set by the user - and flow has an :attr:`lifetime` attribute defined: + and flow has a :attr:`lifetime` attribute defined: .. math:: \sum_{(i,o)} \sum_{pp=0}^{lifetime(i, o)} nominal\_value(i, o) \cdot fixed\_costs(i, o, pp) \cdot DF^{-pp} If :attr:`fixed_costs` is set by the user - and flow has an :attr:`lifetime` and an :attr:`age` attribute defined: + and flow has a :attr:`lifetime` and an :attr:`age` attribute defined: .. math:: \sum_{(i,o)} \sum_{pp=0}^{lifetime(i, o) - age(i, o)} nominal\_value(i, o) \cdot fixed\_costs(i, o, pp) @@ -447,14 +457,20 @@ class FlowBlock(SimpleBlock): Whereby: - * :math:`DF=(1+dr)` is the discount factor with discount rate math:`dr` + * :math:`DF=(1+dr)` is the discount factor with discount rate :math:`dr` * :math:`weight(t)` is the objective weighting term for timestep t Cost expressions can be accessed by - :attr:`om.FlowBlock.variable_costs`, :attr:`om.FlowBlock.fixed_costs` and - :attr:`om.FlowBlock.costs`. Their values after optimization can be - retreived by :meth:`om.FlowBlock.variable_costs()`, - :meth:`om.FlowBlock.fixed_costs()` and :meth:`om.FlowBlock.costs()` + + * :attr:`om.FlowBlock.variable_costs`, + * :attr:`om.FlowBlock.fixed_costs` and + * :attr:`om.FlowBlock.costs`. + + Their values after optimization can be retrieved by + + * :meth:`om.FlowBlock.variable_costs()`, + * :meth:`om.FlowBlock.fixed_costs()` and + * :meth:`om.FlowBlock.costs()` """ diff --git a/src/oemof/solph/flows/_investment_flow.py b/src/oemof/solph/flows/_investment_flow.py index 1294be115..a6b50af58 100644 --- a/src/oemof/solph/flows/_investment_flow.py +++ b/src/oemof/solph/flows/_investment_flow.py @@ -51,7 +51,7 @@ class InvestmentFlowBlock(SimpleBlock): **Variables** - All *InvestmentFlowBlock*s are indexed by a starting and ending node + All *InvestmentFlowBlock* objects are indexed by a starting and ending node :math:`(i, o)`, which is omitted in the following for the sake of convenience. The following variables are created: @@ -63,7 +63,7 @@ class InvestmentFlowBlock(SimpleBlock): * :math:`P_{invest}(p)` Value of the investment variable in period p, - equal to what is being invested and equivalent / similar to + equal to what is being invested and equivalent resp. similar to the nominal value of the flows after optimization. * :math:`P_{total}(p)` @@ -100,7 +100,7 @@ class InvestmentFlowBlock(SimpleBlock): Depending on the attributes of the *InvestmentFlowBlock* and *FlowBlock*, different constraints are created. The following constraints are created - for all *InvestmentFlowBlock*s:\ + for all *InvestmentFlowBlock* objects:\ Total capacity / energy @@ -109,17 +109,21 @@ class InvestmentFlowBlock(SimpleBlock): if \quad p=0:\\ & P_{total}(p) = P_{invest}(p) + P_{exist}(p) \\ + &\\ & else:\\ & P_{total}(p) = P_{total}(p-1) + P_{invest}(p) - P_{old}(p) \\ + &\\ & \forall p \in \textrm{PERIODS} Upper bound for the flow value .. math:: + & P(p, t) \le ( P_{total}(p) ) \cdot f_{max}(t) \\ + & \forall p, t \in \textrm{TIMEINDEX} For a multi-period model, the old capacity is defined as follows: @@ -132,10 +136,12 @@ class InvestmentFlowBlock(SimpleBlock): if \quad p=0:\\ & P_{old,end}(p) = 0\\ + &\\ & else \quad if \quad l \leq year(p):\\ & P_{old,end}(p) = P_{invest}(p_{comm})\\ + &\\ & else:\\ & @@ -145,23 +151,28 @@ class InvestmentFlowBlock(SimpleBlock): if \quad p=0:\\ & P_{old,exo}(p) = 0\\ + &\\ & else \quad if \quad l - a \leq year(p):\\ & P_{old,exo}(p) = P_{exist} (*)\\ + &\\ & else:\\ & P_{old,exo}(p) = 0\\ + &\\ & \forall p \in \textrm{PERIODS} whereby: * (*) is only performed for the first period the condition is True. - A decommissioning flag is set to True. + A decommissioning flag is then set to True to prevent having falsely + added old capacity in future periods. * :math:`year(p)` is the year corresponding to period p - * :math:`p_{comm}` is the commissioning period of the flow + * :math:`p_{comm}` is the commissioning period of the flow (which is + determined by the model itself) Depending on the attribute :attr:`nonconvex`, the constraints for the bounds of the decision variable :math:`P_{invest}(p)` are different:\ @@ -169,16 +180,19 @@ class InvestmentFlowBlock(SimpleBlock): * :attr:`nonconvex = False` .. math:: + & P_{invest, min}(p) \le P_{invest}(p) \le P_{invest, max}(p) \\ + & \forall p \in \textrm{PERIODS} * :attr:`nonconvex = True` .. math:: & - P_{invest, min}(P) \cdot b_{invest}(P) \le P_{invest}(p)\\ + P_{invest, min}(p) \cdot b_{invest}(p) \le P_{invest}(p)\\ & P_{invest}(p) \le P_{invest, max}(p) \cdot b_{invest}(p)\\ + &\\ & \forall p \in \textrm{PERIODS} @@ -191,7 +205,10 @@ class InvestmentFlowBlock(SimpleBlock): Actual value constraint for investments with fixed flow values .. math:: + & P(p, t) = P_{total}(p) \cdot f_{fix}(t) \\ + &\\ + & \forall p, t \in \textrm{TIMEINDEX} * :attr:`min != 0` @@ -199,7 +216,10 @@ class InvestmentFlowBlock(SimpleBlock): Lower bound for the flow values .. math:: + & P(p, t) \geq P_{total}(p) \cdot f_{min}(t) \\ + &\\ + & \forall p, t \in \textrm{TIMEINDEX} * :attr:`summed_max` is not None @@ -225,12 +245,16 @@ class InvestmentFlowBlock(SimpleBlock): Overall maximum of total installed capacity / energy for flow .. math:: + & P_{total}(p) \leq P_{overall,max} \\ + &\\ + & \forall p \in \textrm{PERIODS} * :attr:`overall_minimum` is not None (for multi-period model only) - Overall minimum of total installed capacity / energy for flow + Overall minimum of total installed capacity / energy for flow; + applicable only in last period .. math:: P_{total}(p_{last}) \geq P_{overall,min} @@ -257,6 +281,8 @@ class InvestmentFlowBlock(SimpleBlock): P_{invest}(0) \cdot c_{invest,var}(0) + c_{invest,fix}(0) \cdot b_{invest}(0) \\ + Whereby 0 denotes the 0th (investment) period since in a standard model, + there is only this one period. *Multi-period model* @@ -266,6 +292,7 @@ class InvestmentFlowBlock(SimpleBlock): & P_{invest}(p) \cdot A(c_{invest,var}(p), l, ir) \cdot l \cdot DF^{-p}\\ + &\\ & \forall p \in \textrm{PERIODS} @@ -273,8 +300,9 @@ class InvestmentFlowBlock(SimpleBlock): .. math:: & - P_{invest}(p) \cdot A(c_{invest,var}(p), l, ir) \cdot l - \cdot DF^{-p} + c_{invest,fix}(p) \cdot b_{invest}(p)\\ + (P_{invest}(p) \cdot A(c_{invest,var}(p), l, ir) \cdot l + + c_{invest,fix}(p) \cdot b_{invest}(p)) \cdot DF^{-p} \\ + &\\ & \forall p \in \textrm{PERIODS} @@ -282,9 +310,10 @@ class InvestmentFlowBlock(SimpleBlock): .. math:: & - \sum_{pp=year(p)}^{year(p)+l} + (\sum_{pp=year(p)}^{year(p)+l} P_{invest}(p) \cdot c_{fixed}(pp) \cdot DF^{-pp}) \cdot DF^{-p}\\ + &\\ & \forall p \in \textrm{PERIODS} @@ -302,8 +331,31 @@ class InvestmentFlowBlock(SimpleBlock): interest rate :math:`ir` * :math:`DF=(1+dr)` is the discount factor with discount rate math:`dr` - The total value of all costs of all *InvestmentFlowBlock* can be retrieved - calling :meth:`om.InvestmentFlowBlock.investment_costs.expr()`. + The annuity hereby is: + + .. math:: + + A(c_{invest,var}(p), l, ir) = c_{invest,var}(p) \cdot + \frac {(1+i)^l \cdot i} {(1+i)^l - 1} \cdot + + It is retrieved, using oemof.tools.economics annuity function. The + interest rate is defined as a weighted average costs of capital (wacc) and + assumed constant over time. + + The overall summed cost expressions for all *InvestmentFlowBlock* objects + can be accessed by + + * :attr:`om.InvestmentFlowBlock.investment_costs`, + * :attr:`om.InvestmentFlowBlock.fixed_costs` and + * :attr:`om.InvestmentFlowBlock.costs`. + + Their values after optimization can be retrieved by + + * :meth:`om.InvestmentFlowBlock.investment_costs`, + * :attr:`om.InvestmentFlowBlock.period_investment_costs` (yielding a dict + keyed by periods); note: this is not a Pyomo expression, but calculated, + * :meth:`om.InvestmentFlowBlock.fixed_costs` and + * :meth:`om.InvestmentFlowBlock.costs`. .. csv-table:: List of Variables (in csv table syntax) :header: "symbol", "attribute", "explanation" @@ -340,7 +392,7 @@ class InvestmentFlowBlock(SimpleBlock): ========================= ================================= ========= - Grid table style: + List of Variables (in grid table style): +------------------------+----------------------------------+--------------------------------------------+ | symbol | attribute | explanation | @@ -349,7 +401,7 @@ class InvestmentFlowBlock(SimpleBlock): +------------------------+----------------------------------+--------------------------------------------+ | :math:`P_{invest}(p)` | :py:obj:`invest[i, o, p]` | Invested flow capacity | +------------------------+----------------------------------+--------------------------------------------+ - | :math:`P_{total}(p)` | :py:obj:`total[i, o, p]` | Total flow capacity / energy | + | :math:`P_{total}(p)` | :py:obj:`total[i, o, p]` | Total flow capacity / energy | +------------------------+----------------------------------+--------------------------------------------+ | :math:`P_{old}(p)` | :py:obj:`old[n, o, p]` | Old flow capacity / energy | +------------------------+----------------------------------+--------------------------------------------+ @@ -374,6 +426,14 @@ class InvestmentFlowBlock(SimpleBlock): ", "Variable investment costs" ":math:`c_{invest,fix}(p)`", ":py:obj:`flows[i, o].investment.offset[p]`", " Fix investment costs" + ":math:`c_{fixed}`", "`flows[i, o].investment.fixed_costs`", " + Fixed costs; only allowed in multi-period model" + ":math:`l`", ":py:obj:`flows[i, o].investment.lifetime`", " + Lifetime for investments" + ":math:`a`", ":py:obj:`flows[i, o].investment.age`", " + Initial age of existing capacity / energy" + ":math:`ir`", ":py:obj:`flows[i, o].investment.interest_rate`", " + Interest rate for investments" ":math:`f_{actual}`", ":py:obj:`flows[i, o].fix[t]`", "Normed fixed value for the flow variable" ":math:`f_{max}`", ":py:obj:`flows[i, o].max[t]`", "Normed maximum @@ -386,6 +446,10 @@ class InvestmentFlowBlock(SimpleBlock): minimum of summed flow values (per installed capacity)" ":math:`\tau(t)`", ":py:obj:`timeincrement[t]`", "Time step width for each time step" + ":math:`year(p)`", ":py:obj:`oemof.solph.energy_system_Energy_System.period_years`"," + Mapping of periods to years of the energy system (needed for lifetime tracking)" + ":math:`dr`", ":py:obj:`oemof.solph.models.Model.discount_rate`", " + Discount rate of the model to calculate discount factor :math:`DF`" Note ---- @@ -396,9 +460,7 @@ class InvestmentFlowBlock(SimpleBlock): Note ---- - See also :class:`.FlowBlock`, - :class:`.FlowBlock` and - :class:`.Investment` + See also :class:`.FlowBlock` and :class:`.Investment` """ # noqa: E501 diff --git a/src/oemof/solph/flows/_non_convex_flow.py b/src/oemof/solph/flows/_non_convex_flow.py index 61643ab6c..a031d79d9 100644 --- a/src/oemof/solph/flows/_non_convex_flow.py +++ b/src/oemof/solph/flows/_non_convex_flow.py @@ -70,7 +70,8 @@ class NonConvexFlow(Flow): * `'ub'`: numeric (iterable, scalar or None), the normed *upper bound* on the positive difference (`flow[t-1] < flow[t]`) of two consecutive flow values. - * `'costs'`: REMOVED! + * `'costs'`: numeric (scalar or None), the gradient cost per + unit. negative_gradient : :obj:`dict`, default: `{'ub': None, 'costs': 0}` A dictionary containing the following two keys: @@ -78,7 +79,8 @@ class NonConvexFlow(Flow): * `'ub'`: numeric (iterable, scalar or None), the normed *upper bound* on the negative difference (`flow[t-1] > flow[t]`) of two consecutive flow values. - * `'costs'`: REMOVED! + * `'costs'`: numeric (scalar or None), the gradient cost per + unit. """ def __init__( @@ -191,27 +193,27 @@ class NonConvexFlowBlock(SimpleBlock): flow(i, o, p, t) \geq min(i, o, t) \cdot nominal\_value \ \cdot status(i, o, t), \\ \forall p, t \in \textrm{TIMEINDEX}, \\ - \forall (i, o) \in \textrm{NONCONVEX\_FLOWS}. + (i, o) \in \textrm{NONCONVEX\_FLOWS}. Maximum flow constraint `om.NonConvexFlowBlock.max[i,o,t]` .. math:: flow(i, o, p, t) \leq max(i, o, t) \cdot nominal\_value \ \cdot status(i, o, t), \\ \forall p, t \in \textrm{TIMEINDEX}, \\ - \forall (i, o) \in \textrm{NONCONVEX\_FLOWS}. + (i, o) \in \textrm{NONCONVEX\_FLOWS}. Startup constraint `om.NonConvexFlowBlock.startup_constr[i,o,t]` .. math:: startup(i, o, t) \geq \ status(i,o,t) - status(i, o, t-1) \\ \forall t \in \textrm{TIMESTEPS}, \\ - \forall (i,o) \in \textrm{STARTUPFLOWS}. + (i,o) \in \textrm{STARTUPFLOWS}. Maximum startups constraint `om.NonConvexFlowBlock.max_startup_constr[i,o,t]` .. math:: \sum_{t \in \textrm{TIMESTEPS}} startup(i, o, t) \leq \ - N_{start}(i,o) + N_{start}(i,o) \\ \forall (i,o) \in \textrm{MAXSTARTUPFLOWS}. Shutdown constraint `om.NonConvexFlowBlock.shutdown_constr[i,o,t]` @@ -219,13 +221,13 @@ class NonConvexFlowBlock(SimpleBlock): shutdown(i, o, t) \geq \ status(i, o, t-1) - status(i, o, t) \\ \forall t \in \textrm{TIMESTEPS}, \\ - \forall (i, o) \in \textrm{SHUTDOWNFLOWS}. + (i, o) \in \textrm{SHUTDOWNFLOWS}. Maximum shutdowns constraint `om.NonConvexFlowBlock.max_startup_constr[i,o,t]` .. math:: \sum_{t \in \textrm{TIMESTEPS}} startup(i, o, t) \leq \ - N_{shutdown}(i,o) + N_{shutdown}(i,o) \\ \forall (i,o) \in \textrm{MAXSHUTDOWNFLOWS}. Minimum uptime constraint `om.NonConvexFlowBlock.uptime_constr[i,o,t]` @@ -263,10 +265,10 @@ class NonConvexFlowBlock(SimpleBlock): Positive gradient constraint `om.NonConvexFlowBlock.positive_gradient_constr[i, o]`: .. math:: flow(i, o, p t) \cdot status(i, o, t) - - flow(i, o, p, t-1) \cdot status(i, o, t-1) \geq \ + - flow(i, o, p, t-1) \cdot status(i, o, t-1) \geq \ positive\_gradient(i, o, t), \\ \forall (i, o) \in \textrm{POSITIVE\_GRADIENT\_FLOWS}, \\ - \forall p, t \in \textrm{TIMEINDEX}. + p, t \in \textrm{TIMEINDEX}. Negative gradient constraint `om.NonConvexFlowBlock.negative_gradient_constr[i, o]`: @@ -275,7 +277,7 @@ class NonConvexFlowBlock(SimpleBlock): - flow(i, o, p, t) \cdot status(i, o, t) \geq \ negative\_gradient(i, o, t), \\ \forall (i, o) \in \textrm{NEGATIVE\_GRADIENT\_FLOWS}, \\ - \forall p, t \in \textrm{TIMEINDEX}. + p, t \in \textrm{TIMEINDEX}. Note ---- @@ -286,6 +288,8 @@ class NonConvexFlowBlock(SimpleBlock): **The following parts of the objective function are created:** + *Standard model* + If `nonconvex.startup_costs` is set by the user: .. math:: \sum_{i, o \in STARTUPFLOWS} \sum_t startup(i, o, t) \ @@ -306,15 +310,33 @@ class NonConvexFlowBlock(SimpleBlock): \sum_{i, o \in INACTIVITYCOSTFLOWS} \sum_t (1 - status(i, o, t)) \ \cdot inactivity\_costs(i, o) - If `nonconvex.positive_gradient["costs"]` is set by the user: + *Multi-period model* + + If `nonconvex.startup_costs` is set by the user: + .. math:: + \sum_{i, o \in STARTUPFLOWS} \sum_{p, t} startup(i, o, t) \ + \cdot startup\_costs(i, o) \cdot w(t) \cdot DF^{-p} + + If `nonconvex.shutdown_costs` is set by the user: .. math:: - \sum_{i, o \in POSITIVE_GRADIENT_FLOWS} \sum_t - positive_gradient(i, o, t) \cdot positive\_gradient\_costs(i, o) + \sum_{i, o \in SHUTDOWNFLOWS} \sum_t shutdown(i, o, t) \ + \cdot shutdown\_costs(i, o) \cdot w(t) \cdot DF^{-p} - If `nonconvex.negative_gradient["costs"]` is set by the user: + If `nonconvex.activity_costs` is set by the user: .. math:: - \sum_{i, o \in NEGATIVE_GRADIENT_FLOWS} \sum_t - negative_gradient(i, o, t) \cdot negative\_gradient\_costs(i, o) + \sum_{i, o \in ACTIVITYCOSTFLOWS} \sum_t status(i, o, t) \ + \cdot activity\_costs(i, o) \cdot w(t) \cdot DF^{-p} + + If `nonconvex.inactivity_costs` is set by the user: + .. math:: + \sum_{i, o \in INACTIVITYCOSTFLOWS} \sum_t (1 - status(i, o, t)) \ + \cdot inactivity\_costs(i, o) \cdot w(t) \cdot DF^{-p} + + Whereby + + * :math:`DF` is the discount factor calculated as :math:`(1+dr)` with + discount rate :math:`dr` + * :math:`w(t)` is the objective weighting for timestep t """ diff --git a/src/oemof/solph/flows/experimental/_electrical_line.py b/src/oemof/solph/flows/experimental/_electrical_line.py index 22088dc07..48524e373 100644 --- a/src/oemof/solph/flows/experimental/_electrical_line.py +++ b/src/oemof/solph/flows/experimental/_electrical_line.py @@ -89,10 +89,10 @@ class ElectricalLineBlock(SimpleBlock): Linear relation :attr:`om.ElectricalLine.electrical_flow[n,t]` .. math:: - flow(n, o, p, t) = 1 / reactance(n, t) \\cdot () - voltage_angle(i(n), t) - volatage_angle(o(n), t), \\ - \forall p, t \\in \\textrm{TIMEINDEX}, \\ - \forall n \\in \\textrm{ELECTRICAL\_LINES}. + flow(n, o, p, t) = 1 / reactance(n, t) \cdot + voltage\_angle(i(n), t) - voltage\_angle(o(n), t), \\ + \forall p, t \in \textrm{TIMEINDEX}, \\ + \forall n \in \textrm{ELECTRICAL\_LINES}. TODO: Add equate constraint of flows diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index 842b20e70..309baf332 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -139,9 +139,9 @@ def results(om): Examples -------- * *Standard model*: `results[idx]['scalars']` - and flows `results[n, n]['sequences']`. + and flows `results[n, n]['sequences']`. * *Multi-period model*: `results[idx]['period_scalars']` - and flows `results[n, n]['sequences']`. + and flows `results[n, n]['sequences']`. """ # Extraction steps that are the same for both model types df = create_dataframe(om) @@ -290,7 +290,7 @@ def _extract_multi_period_model_result( Parameters ---------- om : oemof.solph.models.Model - The ptimization model + The optimization model df_dict : dict dictionary of results DataFrames period_indexed : list diff --git a/src/oemof/solph/views.py b/src/oemof/solph/views.py index a95384abf..ccaa8d203 100644 --- a/src/oemof/solph/views.py +++ b/src/oemof/solph/views.py @@ -235,14 +235,16 @@ def node_weight_by_type(results, node_type): Example -------- - from oemof.outputlib import views + :: - # solve oemof model 'm' - # Then collect node weights - views.node_weight_by_type( - m.results(), - node_type=solph.components.GenericStorage - ) + from oemof.solph import views + + # solve oemof model 'm' + # Then collect node weights + views.node_weight_by_type( + m.results(), + node_type=solph.components.GenericStorage + ) """ group = { @@ -274,14 +276,19 @@ def node_input_by_type(results, node_type, droplevel=None): Specifies the type of the node for that inputs are selected droplevel: list - Notes + Examples ----- - from oemof import solph - from oemof.outputlib import views + :: + + from oemof import solph + from oemof.solph import views - # solve oemof solph model 'm' - # Then collect node weights - views.node_input_by_type(m.results(), node_type=solph.components.Sink) + # solve oemof solph model 'm' + # Then collect node weights + views.node_input_by_type( + m.results(), + node_type=solph.components.Sink + ) """ if droplevel is None: droplevel = [] @@ -314,15 +321,17 @@ def node_output_by_type(results, node_type, droplevel=None): Notes ----- - import oemof.solph as solph - from oemof.outputlib import views - - # solve oemof solph model 'm' - # Then collect node weights - views.node_output_by_type( - m.results(), - node_type=solph.components.Transformer - ) + :: + + import oemof.solph as solph + from oemof.solph import views + + # solve oemof solph model 'm' + # Then collect node weights + views.node_output_by_type( + m.results(), + node_type=solph.components.Transformer + ) """ if droplevel is None: droplevel = [] @@ -355,19 +364,21 @@ def net_storage_flow(results, node_type): Returns ------- pandas.DataFrame object with multiindex colums. Names of levels of columns - are: from, to, net_flow. + are: from, to, net_flow. Examples -------- - import oemof.solph as solph - from oemof.outputlib import views - - # solve oemof solph model 'm' - # Then collect node weights - views.net_storage_flow( - m.results(), - node_type=solph.components.GenericStorage - ) + :: + + import oemof.solph as solph + from oemof.solph import views + + # solve oemof solph model 'm' + # Then collect node weights + views.net_storage_flow( + m.results(), + node_type=solph.components.GenericStorage + ) """ group = { From 668fcc09f20ab0f9b03a6681dfbd568b64b3f7d0 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 11 Mar 2022 11:35:22 +0100 Subject: [PATCH 0188/1363] Fix docs tables identations --- .../components/_extraction_turbine_chp.py | 6 ++-- src/oemof/solph/components/_generic_chp.py | 30 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/oemof/solph/components/_extraction_turbine_chp.py b/src/oemof/solph/components/_extraction_turbine_chp.py index 3e963e2b6..827c4e38f 100644 --- a/src/oemof/solph/components/_extraction_turbine_chp.py +++ b/src/oemof/solph/components/_extraction_turbine_chp.py @@ -123,11 +123,11 @@ class ExtractionTurbineCHPBlock(SimpleBlock): :math:`\beta` `main_flow_loss_index[n, t]` P power loss index :math:`\eta_{el,woExtr}` `conversion_factor_full_condensation[n, t]` P electric efficiency - without heat extraction + without heat extraction :math:`\eta_{el,maxExtr}` `conversion_factors[main_output][n, t]` P electric efficiency - with max heat extraction + with max heat extraction :math:`\eta_{th,maxExtr}` `conversion_factors[tapped_output][n, t]` P thermal efficiency with - maximal heat extraction + maximal heat extraction ========================= ============================================ ==== ========= """ # noqa: E501 diff --git a/src/oemof/solph/components/_generic_chp.py b/src/oemof/solph/components/_generic_chp.py index 8382adfc4..54e323092 100644 --- a/src/oemof/solph/components/_generic_chp.py +++ b/src/oemof/solph/components/_generic_chp.py @@ -267,39 +267,39 @@ class GenericCHPBlock(SimpleBlock): math. symbol attribute type explanation =============================== ======================= ==== ======================= :math:`\dot{H}_{F}` `H_F[n,t]` V input of enthalpy - through fuel input + through fuel input :math:`P_{el}` `P[n,t]` V provided - electric power + electric power :math:`P_{el,woDH}` `P_woDH[n,t]` V electric power without - district heating + district heating :math:`P_{el,min,woDH}` `P_min_woDH[n,t]` P min. electric power - without district heating + without district heating :math:`P_{el,max,woDH}` `P_max_woDH[n,t]` P max. electric power - without district heating + without district heating :math:`\dot{Q}` `Q[n,t]` V provided heat :math:`\dot{Q}_{CW, min}` `Q_CW_min[n,t]` P minimal therm. condenser - load to cooling water + load to cooling water :math:`\dot{H}_{L,FG,min}` `H_L_FG_min[n,t]` V flue gas enthalpy loss - at min heat extraction + at min heat extraction :math:`\dot{H}_{L,FG,max}` `H_L_FG_max[n,t]` V flue gas enthalpy loss - at max heat extraction + at max heat extraction :math:`\dot{H}_{L,FG,sharemin}` `H_L_FG_share_min[n,t]` P share of flue gas loss - at min heat extraction + at min heat extraction :math:`\dot{H}_{L,FG,sharemax}` `H_L_FG_share_max[n,t]` P share of flue gas loss - at max heat extraction + at max heat extraction :math:`Y` `Y[n,t]` V status variable - on/off + on/off :math:`\alpha_0` `n.alphas[0][n,t]` P coefficient - describing efficiency + describing efficiency :math:`\alpha_1` `n.alphas[1][n,t]` P coefficient - describing efficiency + describing efficiency :math:`\beta` `Beta[n,t]` P power loss index :math:`\eta_{el,min,woDH}` `Eta_el_min_woDH[n,t]` P el. eff. at min. fuel - flow w/o distr. heating + flow w/o distr. heating :math:`\eta_{el,max,woDH}` `Eta_el_max_woDH[n,t]` P el. eff. at max. fuel - flow w/o distr. heating + flow w/o distr. heating =============================== ======================= ==== ======================= """ # noqa: E501 From 54493f1cab9949b0a8207b9f497c059a7f09f2d4 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 11 Mar 2022 16:36:12 +0100 Subject: [PATCH 0189/1363] Adjust key in views (remove inconsistency) --- src/oemof/solph/views.py | 18 +++++++++--------- .../test_multi_period_investment_model.py | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/oemof/solph/views.py b/src/oemof/solph/views.py index ccaa8d203..5289fd8cc 100644 --- a/src/oemof/solph/views.py +++ b/src/oemof/solph/views.py @@ -73,7 +73,7 @@ def replace_none(col_list, reverse=False): } if scalars: # aggregate data - filtered["scalars"] = pd.concat(scalars.values(), axis=0) + filtered[scalars_col] = pd.concat(scalars.values(), axis=0) # assign index values idx = { k: [c for c in v[scalars_col].index] @@ -82,29 +82,29 @@ def replace_none(col_list, reverse=False): } idx = [tuple((k, m) for m in v) for k, v in idx.items()] idx = [i for sublist in idx for i in sublist] - filtered["scalars"].index = idx + filtered[scalars_col].index = idx # Sort index # (if Nones are present, they have to be replaced while sorting) if keep_none_type: - filtered["scalars"].index = replace_none( - filtered["scalars"].index.tolist() + filtered[scalars_col].index = replace_none( + filtered[scalars_col].index.tolist() ) - filtered["scalars"].sort_index(axis=0, inplace=True) + filtered[scalars_col].sort_index(axis=0, inplace=True) if keep_none_type: - filtered["scalars"].index = replace_none( - filtered["scalars"].index.tolist(), True + filtered[scalars_col].index = replace_none( + filtered[scalars_col].index.tolist(), True ) if multiindex: idx = pd.MultiIndex.from_tuples( [ tuple([row[0][0], row[0][1], row[1]]) - for row in filtered["scalars"].index + for row in filtered[scalars_col].index ] ) idx.set_names(["from", "to", "type"], inplace=True) - filtered["scalars"].index = idx + filtered[scalars_col].index = idx # create a dataframe with tuples as column labels for sequences sequences = { diff --git a/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py index 28dd152cf..e3c9e2913 100644 --- a/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py +++ b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py @@ -442,7 +442,7 @@ def test_multi_period_investment_model(solver="cbc"): for key in test_results.keys(): eq_( ( - views.node(results, key)["scalars"] + views.node(results, key)["period_scalars"] .sum(axis=0) .round(0) .convert_dtypes("int") From 56d6a5ab245d67bfa45538d7167bc295a88885d2 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 11 Mar 2022 16:36:29 +0100 Subject: [PATCH 0190/1363] Update user docs and changelog --- docs/usage.rst | 254 ++++++++++++++++++++++++++++++++++----- docs/whatsnew/v0-5-0.rst | 7 +- 2 files changed, 228 insertions(+), 33 deletions(-) diff --git a/docs/usage.rst b/docs/usage.rst index 115160270..01a654a28 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -10,7 +10,7 @@ Solph is an oemof-package, designed to create and solve linear or mixed-integer This User's guide provides a user-friendly introduction into oemof-solph, which includes small examples and nice illustrations. -However, the functionality of oemof-solph go beyond the content of this User's guide section. +However, the functionalities of oemof-solph go beyond the content of this User's guide section. So, if you want to know all details of a certain component or a function, please go the :ref:`api_reference_label`. There, you will find a detailed and complete description of all oemof-solph modules. @@ -24,10 +24,10 @@ a detailed and complete description of all oemof-solph modules. How can I use solph? -------------------- -To use solph you have to install oemof and at least one solver (see :ref:`installation_label`), which can be used together with pyomo (e.g. CBC, GLPK, Gurobi, Cplex). See the `pyomo installation guide `_ for all supported solver. +To use solph you have to install oemof.solph and at least one solver (see :ref:`installation_label`), which can be used together with pyomo (e.g. CBC, GLPK, Gurobi, Cplex). See the `pyomo installation guide `_ for all supported solvers. You can test it by executing one of the existing examples (see :ref:`solph_examples_label`, or directly `oemof's example repository `__). Be aware that the examples require the CBC solver but you can change the solver name in the example files to your solver. -Once the example work you are close to your first energy model. +Once the examples work you are close to your first energy model. Handling of Warnings @@ -35,7 +35,7 @@ Handling of Warnings The solph library is designed to be as generic as possible to make it possible to use it in different use cases. This concept makes it difficult to raise -Error or Warnings because sometimes untypical combinations of parameters are +Errors or Warnings because sometimes untypical combinations of parameters are allowed even though they might be wrong in over 99% of the use cases. Therefore, a SuspiciousUsageWarning was introduced. This warning will warn you @@ -49,9 +49,9 @@ information. Set up an energy system ^^^^^^^^^^^^^^^^^^^^^^^ -In most cases an EnergySystem object is defined when we start to build up an energy system model. The EnergySystem object will be the main container for the model. +In most cases an EnergySystem object is defined when we start to build up an energy system model. The EnergySystem object will be the main container for the model's elements. -To define an EnergySystem we need a Datetime index to define the time range and increment of our model. An easy way to this is to use the pandas time_range function. +To define an EnergySystem we need a Datetime index to define the time range and increment of our model. An easy way to this is to use the pandas date_range function. The following code example defines the year 2011 in hourly steps. See `pandas date_range guide `_ for more information. .. code-block:: python @@ -72,7 +72,7 @@ Now you can start to add the components of the network. Add components to the energy system ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -After defining an instance of the EnergySystem class you have to add all nodes you define in the following to your EnergySystem. +After defining an instance of the EnergySystem class, in the following you have to add all nodes you define to your EnergySystem. Basically, there are two types of *nodes* - *components* and *buses*. Every Component has to be connected with one or more *buses*. The connection between a *component* and a *bus* is the *flow*. @@ -82,7 +82,7 @@ An example of a simple energy system shows the usage of the nodes for real world representations: .. image:: _files/oemof_solph_example.svg - :scale: 10 % + :scale: 30 % :alt: alternate text :align: center @@ -117,7 +117,7 @@ Therefore it is also possible to add lists or dictionaries with components but y Bus +++ -All flows into and out of a *bus* are balanced. Therefore an instance of the Bus class represents a grid or network without losses. To define an instance of a Bus only a unique label is necessary. If you do not set a label a random label is used but this makes it difficult to get the results later on. +All flows into and out of a *bus* are balanced (by default). Therefore an instance of the Bus class represents a grid or network without losses. To define an instance of a Bus only a unique label is necessary. If you do not set a label a random label is used but this makes it difficult to get the results later on. To make it easier to connect the bus to a component you can optionally assign a variable for later use. @@ -126,15 +126,15 @@ To make it easier to connect the bus to a component you can optionally assign a solph.buses.Bus(label='natural_gas') electricity_bus = solph.buses.Bus(label='electricity') -.. note:: See the :py:class:`~oemof.solph.network.bus.Bus` class for all parameters and the mathematical background. +.. note:: See the :py:class:`~oemof.solph.buses._bus.Bus` class for all parameters and the mathematical background. Flow ++++ -The flow class has to be used to connect. An instance of the Flow class is normally used in combination with the definition of a component. +The flow class has to be used to connect nodes and buses. An instance of the Flow class is normally used in combination with the definition of a component. A Flow can be limited by upper and lower bounds (constant or time-dependent) or by summarised limits. -For all parameters see the API documentation of the :py:class:`~oemof.solph.network.flow.Flow` class or the examples of the nodes below. A basic flow can be defined without any parameter. +For all parameters see the API documentation of the :py:class:`~oemof.solph.flows._flow.Flow` class or the examples of the nodes below. A basic flow can be defined without any parameter. .. code-block:: python @@ -142,12 +142,12 @@ For all parameters see the API documentation of the :py:class:`~oemof.solph.netw Oemof has different types of *flows* but you should be aware that you cannot connect every *flow* type with every *component*. -.. note:: See the :py:class:`~oemof.solph.network.flow.Flow` class for all parameters and the mathematical background. +.. note:: See the :py:class:`~oemof.solph.flows._flow.Flow` class for all parameters and the mathematical background. Components ++++++++++ -Components are divided in three categories. Basic components (solph.network), additional components (solph.components) and custom components (solph.custom). The custom section was created to lower the entry barrier for new components. Be aware that these components are in an experimental state. Let us know if you have used and tested these components. This is the first step to move them to the components section. +Components are divided in two categories. Well-tested components (solph.components) and experimental components (solph.components.experimental). The experimental section was created to lower the entry barrier for new components. Be aware that these components are in an experimental state. Let us know if you have successfully used and tested these components. This is the first step to move them to the regular components section. See :ref:`oemof_solph_components_label` for a list of all components. @@ -159,11 +159,14 @@ Optimise your energy system The typical optimisation of an energy system in solph is the dispatch optimisation, which means that the use of the sources is optimised to satisfy the demand at least costs. Therefore, variable cost can be defined for all components. The cost for gas should be defined in the gas source while the variable costs of the gas power plant are caused by operating material. +The actual fuel cost in turn is calculated in the framework itself considering the efficiency of the power plant. You can deviate from this scheme but you should keep it consistent to make it understandable for others. Costs do not have to be monetary costs but could be emissions or other variable units. -Furthermore, it is possible to optimise the capacity of different components (see :ref:`investment_mode_label`). +Furthermore, it is possible to optimise the capacity of different components using the investment mode (see :ref:`investment_mode_label`). + +Since v0.5, there also is the possibility to have multi-period (i.e. dynamic) investments over longer-time horizon which is in experimental state (see :ref:`multi_period_mode_label`). .. code-block:: python @@ -186,7 +189,7 @@ If you want to analyse the lp-file to see all equations and bounds you can write Analysing your results ^^^^^^^^^^^^^^^^^^^^^^ -If you want to analyse your results, you should first dump your EnergySystem instance, otherwise you have to run the simulation again. +If you want to analyse your results, you should first dump your EnergySystem instance to permanently store results. Otherwise you would have to run the simulation again. .. code-block:: python @@ -717,8 +720,8 @@ The parameters :math:`C_{0}` and :math:`C_{1}` can be given by scalars or by ser .. _oemof_solph_custom_electrical_line_label: -ElectricalLine (custom) -^^^^^^^^^^^^^^^^^^^^^^^ +ElectricalLine (experimnetal) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Electrical line. @@ -727,7 +730,7 @@ Electrical line. .. _oemof_solph_custom_link_label: -GenericCAES (custom) +GenericCAES (experimental) ^^^^^^^^^^^^^^^^^^^^^^^^^^ Compressed Air Energy Storage (CAES). @@ -741,8 +744,8 @@ The following constraints describe the CAES: .. _oemof_solph_components_generic_chp_label: -Link (custom) -^^^^^^^^^^^^^ +Link (experimental) +^^^^^^^^^^^^^^^^^^^ Link. @@ -752,8 +755,8 @@ Link. .. _oemof_solph_custom_sinkdsm_label: -SinkDSM (custom) -^^^^^^^^^^^^^^^^ +SinkDSM (experimental) +^^^^^^^^^^^^^^^^^^^^^^ :class:`~oemof.solph.custom.sink_dsm.SinkDSM` can used to represent flexibility in a demand time series. It can represent both, load shifting or load shedding. @@ -849,21 +852,29 @@ Using the investment mode ------------------------- As described in :ref:`oemof_solph_optimise_es_label` the typical way to optimise an energy system is the dispatch optimisation based on marginal costs. Solph also provides a combined dispatch and investment optimisation. -Based on investment costs you can compare the usage of existing components against building up new capacity. -The annual savings by building up new capacity must therefore compensate the annuity of the investment costs (the time period does not have to be one year but depends on your Datetime index). +This standard investment mode is limited to one period where all investments happen at the start of the optimization time frame. If you want to optimize longer-term horizons and allow investments at the beginning +of each of multiple periods, also taking into account units lifetimes, you can try the :ref:`multi_period_mode_label`. Please be aware that the multi-period feature is experimental. If you experience any bugs or unexpected +behaviour, please report them. + +In the standard investment mode, based on investment costs you can compare the usage of existing components against building up new capacity. +The annual savings by building up new capacity must therefore compensate the annuity of the investment costs (the time period does not have to be one year, but depends on your Datetime index). See the API of the :py:class:`~oemof.solph.options.Investment` class to see all possible parameters. -Basically an instance of the investment class can be added to a Flow or a +Basically, an instance of the Investment class can be added to a Flow or a Storage. All parameters that usually refer to the *nominal_value/capacity* will now refer to the investment variables and existing capacity. It is also possible to set a maximum limit for the capacity that can be build. If existing capacity is considered for a component with investment mode enabled, -the *ep_costs* still apply only to the newly built capacity. +the *ep_costs* still apply only to the newly built capacity, i.e. the existing capacity +comes at no costs. The investment object can be used in Flows and some components. See the :ref:`oemof_solph_components_label` section for detailed information of each -component. +component. Besides the flows, it can be invested into + +* :ref:`oemof_solph_components_generic_storage_label` and +* :ref:`oemof_solph_custom_sinkdsm_label` For example if you want to find out what would be the optimal capacity of a wind power plant to decrease the costs of an existing energy system, you can define @@ -890,7 +901,7 @@ allow for 30,000 kW of new installations and formulate as follows. maximum=30000, existing=20000))}) -The periodical costs (*ep_costs*) are typically calculated as follows: +The periodical costs (*ep_costs*) are typically calculated as annuities, i.e. as follows: .. code-block:: python @@ -948,7 +959,7 @@ the *ep_costs* value: :align: center In case of a convex investment (which is the default setting -`nonconvex=Flase`), the *minimum* attribute leads to a forced investment, +`nonconvex=False`), the *minimum* attribute leads to a forced investment, whereas in the nonconvex case, the investment can become zero as well. The calculation of the specific costs per kilowatt installed capacity results @@ -966,6 +977,189 @@ mathematical background, like variables and constraints, which are used. .. note:: At the moment the investment class is not compatible with the MIP classes :py:class:`~oemof.solph.options.NonConvex`. +.. _multi_period_mode_label: + +Using the multi-period (investment) mode (experimental) +------------------------------------------------------- +Sometimes you might be interested in how energy systems could evolve in the longer-term until 2045 or 2050 to meet some +carbon neutrality and climate protection or RES and energy efficiency targets. + +While in principle, you could try to model this in oemof using the standard investment mode described above (see :ref:`investment_mode_label`), +you would make the implicit assumption that your entire system is built at the start of your optimization and doesn't change over time. +To address this shortcoming, the multi-period (investment) feature has been introduced. Be aware that it is still experimental. +So feel free to report any bugs or unexpected behaviour if you come across them. + +While you can define a dispatch-ony multi-period system, it doesn't make much sense. The power of the multi-period feature +only unfolds if you look at long-term investments. Let's see how. + +First, you start by defining your energy system again, but you + +* choose a longer-term time horizon (spanning multiple years, i.e. multiple periods) and +* set the `multi_period` attribute of your energy system to True. + +.. code-block:: python + + import pandas as pd + import oemof.solph as solph + + my_index = pd.date_range('1/1/2013', periods=17520, freq='H') + my_energysystem = solph.EnergySystem(timeindex=my_index, multi_period=True) + +By default, the years of your timeindex will be used as periods. So in this example 2013 is period 0 and 2014 is period 1. +You could also define periods of your energy system explicitly. Please refer to the API reference on :py:class:`~oemof.solph.energy_system.EnergySystem` class for that. + +Then you add all the *components* and *buses* to your energy system, just as you are used to with, but with few additions. + +.. code-block:: python + + hydrogen_bus = solph.buses.Bus(label="hydrogen") + coal_bus = solph.buses.Bus(label="coal") + electricity_bus = solph.buses.Bus(label="electricity") + + hydrogen_source = solph.components.Source( + label="green_hydrogen", + outputs={ + hydrogen_bus: solph.flows.Flow( + variable_costs=[25] * 8760 + [30] * 8760 + ) + }, + ) + + coal_source = solph.components.Source( + label="hardcoal", + outputs={ + coal_bus: solph.flows.Flow(variable_costs=[20] * 8760 + [24] * 8760) + }, + ) + + electrical_sink = solph.components.Sink( + label="electricity_demand", + inputs={ + electricity_bus: solph.flows.Flow( + nominal_value=1000, fix=[0.8] * len(my_index) + ) + }, + ) + +So defining buses is the same as for standard models. Also defining components that do not have any investments associated with +them or any lifetime limitations is the same. + +Now if you want to have components that can be invested into, you use the investment option, just as in :ref:`investment_mode_label`, +but with a few minor additions and modifications in the investment object itself which you specify by additional attributes: + +* You have to specify a `lifetime` attribute. This is the components assumed technical lifetime in years. If it is 20 years, + the model invests into it and your simulation has a 30 years horizon, the plant will be decommissioned. Now the model is + free to reinvest or choose another option to fill up the missing capacity. +* You can define an initial `age` if you have `existing` capacity. If you do not specify anything, the default value 0 will be used, + meaning your `existing` capacity has just been newly invested. +* You can define an `interest_rate` that the investor you model has, i.e. the return he desires expressed as the weighted + average osts of capital (wacc) and used for calculating annuities in the model itself. +* You also can define `fixed_costs`, i.e. costs that occur every period independent of the plants usage. + +Here is an example + +.. code-block:: python + + hydrogen_power_plant = solph.components.Transformer( + label="hydrogen_pp", + inputs={hydrogen_bus: solph.flows.Flow()}, + outputs={ + electricity_bus: solph.flows.Flow( + investment=solph.Investment( + maximum=1000, + ep_costs=1e6, + lifetime=30, + interest_rate=0.06, + fixed_costs=100, + ), + variable_costs=3, + ) + }, + conversion_factors={electricity_bus: 0.6}, + ) + +.. warning:: + + The `ep_costs` attribute for investments is used in a different way in a multi-period model. Instead + of periodical costs, it depicts (nominal or real) investment expenses, so actual Euros you have to pay per kW or MW + installed. + + Annuities are calculated within the model. You do not have to do that. + Also the model takes care of discounting future expenses / cashflows. + +For components that is not invested into, you also can specify some additional attributes for their inflows and outflows: + +* You can specify a `lifetime` attribute. This can be used to depict existing plants going offline when reaching their lifetime. +* You can define an initial `age`. Also, this can be used for existing plants. +* You also can define `fixed_costs`, i.e. costs that occur every period independent of the plants usage. How they are handled + depends on whether the flow has a limit or unlimited lifetime. + +.. code-block:: python + + coal_power_plant = solph.components.Transformer( + label="existing_coal_pp", + inputs={coal_bus: solph.flows.Flow()}, + outputs={ + electricity_bus: solph.flows.Flow( + nominal_value=600, + max=1, + min=0.4, + lifetime=50, + age=46, + fixed_costs=100, + variable_costs=3, + ) + }, + conversion_factors={electricity_bus: 0.36}, + ) + +To solve our model and retrieve results, you basically perform the same operations as for standard models. +So it works like this: + +.. code-block:: python + + my_energysystem.add( + hydrogen_bus, + coal_bus, + electricity_bus, + hydrogen_source, + coal_source, + electrical_sink, + hydrogen_power_plant, + coal_power_plant, + ) + + om = solph.Model(my_energysystem) + om.solve(solver="cbc", solve_kwargs={"tee": True}) + + # Obtain results + results = solph.processing.results(om) + hydrogen_results = solph.views.node(results, "hydrogen_pp") + + # Show investment plan for hydrogen power plants + print(hydrogen_results["period_scalars"]) + +The keys in the results dict in a multi-period model are "sequences" and "period_scalars". +So for sequences, it is all the same, while for scalar values, we now have values for each period. + +Besides the `invest` variable, new variables are introduced as well. These are: + +* `total`: The total capacity installed +* `old`: Capacity to be decommissioned in a given period +* `old_end`: Endogenous capacity to be decommissioned in a given period. This is capacity that has been invested into + in the model itself. +* `old_exo`: Exogenous capacity to be decommissioned in a given period. This is capacity that was already existing and + given by the `existing` attribute. + +.. note:: + + * You can specify a `discount_rate` for the model. If you do not do so, 0.02 will be used as a default, corresponding + to sort of a social discount rate. + * You can specify an `interest_rate` for every investment object. If you do not do so, it will be chosen the same + as the model's `discount_rate`. This corresponds to a social planner point of view. + * For storage units, the `initial_content` is not allowed combined with multi-period investments. + The storage inflow and outflow are forced to zero until the storage unit is invested into. + Mixed Integer (Linear) Problems ------------------------------- diff --git a/docs/whatsnew/v0-5-0.rst b/docs/whatsnew/v0-5-0.rst index 0ea5264fc..a763f2650 100644 --- a/docs/whatsnew/v0-5-0.rst +++ b/docs/whatsnew/v0-5-0.rst @@ -19,18 +19,19 @@ New features * Add option to run multi-period (dynamic) investment models with oemof.solph as an experimental feature: * You can change from standard model to multi-period model by defining `multi_period=True` as attribute of your energy system. Be aware that it is experimental as of now. - * Introduce new Pyomo Sets `PERIODS` and `TIMEINDEX` in ``Model`. + * Introduce new Pyomo Sets `PERIODS` and `TIMEINDEX` in :class:`oemof.solph.models.Model`. * Index all investment-related variables with `PERIODS` and flow variable with `TIMEINDEX`. * Add lifetime tracking for investment options by introducing the attributes `lifetime` and `age`. * Add new investment-related variables `total` holding the total capacity, `old` holding capacity to be decommissioned, `old_exo` (for exogenous) holding existing capacity to be decommissioned and `old_end` holding model-endogenously installed capacity to be decommissioned after its lifetime. - * Include discounting and calculating annuities in the objective function terms. + * Include discounting and calculating annuities in the objective function terms. Introduce attribute `discount_rate` + of :class:`oemof.solph.models.Model` and `interest_rate` for individual investment objects (options.Investment). Documentation ############# -* See extensive documentation and API reference for the new (experimental) multi-period feature. +* See extensive documentation in user guide and API reference for the new (experimental) multi-period feature. Bug fixes ######### From e223894a7999e0f0d59a2cc43ba78c746cc65e4d Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 11 Mar 2022 17:04:05 +0100 Subject: [PATCH 0191/1363] Fix docs stuff --- docs/whatsnew/v0-5-0.rst | 14 ++++----- .../solph/components/_generic_storage.py | 11 ++++--- src/oemof/solph/flows/_flow.py | 31 ++++++++++--------- 3 files changed, 30 insertions(+), 26 deletions(-) diff --git a/docs/whatsnew/v0-5-0.rst b/docs/whatsnew/v0-5-0.rst index a763f2650..177072a11 100644 --- a/docs/whatsnew/v0-5-0.rst +++ b/docs/whatsnew/v0-5-0.rst @@ -3,7 +3,7 @@ v0.5.0 API changes -########### +^^^^^^^^^^^ * Parts of the energy system graph are now clearly structured into `buses`, `components`, and `flows`. * Public and private API are be more distinguished now. ('_' signifies private, public API is defined in init files.) @@ -13,7 +13,7 @@ API changes New features -############ +^^^^^^^^^^^^ * Add `inactivity_costs` as an option for `NonConvexFlow`. * Add option to run multi-period (dynamic) investment models with oemof.solph as an experimental feature: @@ -29,25 +29,25 @@ New features of :class:`oemof.solph.models.Model` and `interest_rate` for individual investment objects (options.Investment). Documentation -############# +^^^^^^^^^^^^^ * See extensive documentation in user guide and API reference for the new (experimental) multi-period feature. Bug fixes -######### +^^^^^^^^^ Testing -####### +^^^^^^^ Other changes -############# +^^^^^^^^^^^^^ Contributors -############ +^^^^^^^^^^^^ * Patrik Schönfeldt * Johannes Kochems diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index eeae9b44a..1d6731eb5 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -994,7 +994,7 @@ class GenericInvestmentStorageBlock(SimpleBlock): ":math:`P_{i,exist}`", "`flows[i[n], n].investment.existing` ", "Existing inflow capacity" ":math:`P_{o,exist}`", "`flows[n, o[n]].investment.existing` - ", "Existing outlfow capacity" + ", "Existing outflow capacity" ":math:`c_{invest,var}`", "`flows[i, o].investment.ep_costs` ", "Variable investment costs" ":math:`c_{invest,fix}`", "`flows[i, o].investment.offset`", " @@ -1017,17 +1017,18 @@ class GenericInvestmentStorageBlock(SimpleBlock): Conversion factor (i.e. efficiency) when storing energy" ":math:`\eta_o(t)`", "`outflow_conversion_factor[t]`", " Conversion factor when (i.e. efficiency) taking stored energy" - ":math:`c(-1)`", "`initial_storage_level`", "Initial relativ + ":math:`c(-1)`", "`initial_storage_level`", "Initial relative storage content (before timestep 0)" ":math:`c_{max}`", "`flows[i, o].max[t]`", "Normed maximum value of storage content" ":math:`c_{min}`", "`flows[i, o].min[t]`", "Normed minimum value of storage content" - ":math:`l`", ":py:obj:`flows[i, o].investment.lifetime`", " + ":math:`l`", "`flows[i, o].investment.lifetime`", " Lifetime for investments in storage capacity" - ":math:`a`", ":py:obj:`flows[i, o].investment.age`", " + ":math:`a`", "`flows[i, o].investment.age`", " Initial age of existing capacity / energy" - ":math:`ir`", ":py:obj:`flows[i, o].investment.interest_rate`", " + ":math:`ir`", "`flows[i, o].investment.interest_rate`", " + interest rate for investment" ":math:`\tau(t)`", "", "Duration of time step" ":math:`t_u`", "", "Time unit of losses :math:`\beta(t)`, :math:`\gamma(t)`, :math:`\delta(t)` and timeincrement :math:`\tau(t)`" diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 0c317938e..77823bc17 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -121,13 +121,14 @@ class Flow(on.Edge): Notes ----- The following sets, variables, constraints and objective parts are created - * :py:class:`~oemof.solph.flows.flow.FlowBlock` - * :py:class:`~oemof.solph.flows.investment_flow.InvestmentFlowBlock` - (additionally if Investment object is present) - * :py:class:`~oemof.solph.flows.non_convex_flow.NonConvexFlowBlock` - (If nonconvex object is present, CAUTION: replaces - :py:class:`~oemof.solph.flows.flow.FlowBlock` - class and a MILP will be build) + + * :py:class:`~oemof.solph.flows.flow.FlowBlock` + * :py:class:`~oemof.solph.flows.investment_flow.InvestmentFlowBlock` + (additionally if Investment object is present) + * :py:class:`~oemof.solph.flows.non_convex_flow.NonConvexFlowBlock` + (If nonconvex object is present, CAUTION: replaces + :py:class:`~oemof.solph.flows.flow.FlowBlock` + class and a MILP will be build) Examples -------- @@ -398,7 +399,8 @@ class FlowBlock(SimpleBlock): Lifetime age output constraint: Force flow to 0 once it exceeds its lifetime, considering its initial age - :attr:`om.FlowBlock.lifetime_age_output[i, o]`: + :attr:`om.FlowBlock.lifetime_age_output[i, o]`: + .. math:: & if \quad lifetime(i, o) - age(i, o) < year(p):\\ @@ -434,22 +436,23 @@ class FlowBlock(SimpleBlock): flow(i, o, p, t) \cdot weight(t) \cdot variable\_costs(i, o, t) \cdot DF^{-p} - If :attr:`fixed_costs` is set by the user - and flow has no lifetime limitation: + If :attr:`fixed_costs` is set by the user and no lifetime limitation: .. math:: \sum_{(i,o)} \sum_{p \in {PERIODS}} nominal\_value(i, o) \cdot fixed\_costs(i, o, p) \cdot DF^{-p} - If :attr:`fixed_costs` is set by the user - and flow has a :attr:`lifetime` attribute defined: + If :attr:`fixed_costs` is set by the user and flow + has a :attr:`lifetime` attribute defined: + .. math:: \sum_{(i,o)} \sum_{pp=0}^{lifetime(i, o)} nominal\_value(i, o) \cdot fixed\_costs(i, o, pp) \cdot DF^{-pp} - If :attr:`fixed_costs` is set by the user - and flow has a :attr:`lifetime` and an :attr:`age` attribute defined: + If :attr:`fixed_costs` is set by the user and flow has a :attr:`lifetime` + and an :attr:`age` attribute defined: + .. math:: \sum_{(i,o)} \sum_{pp=0}^{lifetime(i, o) - age(i, o)} nominal\_value(i, o) \cdot fixed\_costs(i, o, pp) From 4945efbc4844641779aa261abb2a71cadae45232 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 12 Mar 2022 12:43:11 +0100 Subject: [PATCH 0192/1363] Fix black issues --- .../solph/components/_generic_storage.py | 22 ++--- .../components/experimental/_sink_dsm.py | 63 ++++++-------- src/oemof/solph/flows/_investment_flow.py | 22 ++--- tests/constraint_tests.py | 11 +-- tests/multi_period_constraint_tests.py | 22 ++--- tests/test_components.py | 6 -- tests/test_energy_system.py | 6 +- tests/test_models.py | 13 ++- tests/test_options.py | 7 +- .../test_multi_period_investment_model.py | 83 +++++-------------- 10 files changed, 84 insertions(+), 171 deletions(-) diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index 1d6731eb5..6bae47830 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -1680,21 +1680,15 @@ def _objective_expression(self): if n.investment.fixed_costs[0] is not None: lifetime = n.investment.lifetime for p in m.PERIODS: - fixed_costs += ( - sum( - self.invest[n, p] - * n.investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range( - m.es.periods_years[p], - m.es.periods_years[p] + lifetime, - ) + fixed_costs += sum( + self.invest[n, p] + * n.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime, ) - * ( - (1 + m.discount_rate) - ** (-m.es.periods_years[p]) - ) - ) + ) * ((1 + m.discount_rate) ** (-m.es.periods_years[p])) for n in self.EXISTING_INVESTSTORAGES: if n.investment.fixed_costs[0] is not None: diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index 9cf478c71..55940597c 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -1305,19 +1305,16 @@ def _objective_expression(self): if g.investment.fixed_costs[0] is not None: lifetime = g.investment.lifetime for p in m.PERIODS: - fixed_costs += ( - sum( - self.invest[g, p] - * max(g.demand) - * g.investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range( - m.es.periods_years[p], - m.es.periods_years[p] + lifetime, - ) + fixed_costs += sum( + self.invest[g, p] + * max(g.demand) + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime, ) - * ((1 + m.discount_rate) ** -m.es.periods_years[p]) - ) + ) * ((1 + m.discount_rate) ** -m.es.periods_years[p]) for g in self.EXISTING_INVESTDSM: if g.investment.fixed_costs[0] is not None: @@ -2956,19 +2953,16 @@ def _objective_expression(self): if g.investment.fixed_costs[0] is not None: lifetime = g.investment.lifetime for p in m.PERIODS: - fixed_costs += ( - sum( - self.invest[g, p] - * max(g.demand) - * g.investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range( - m.es.periods_years[p], - m.es.periods_years[p] + lifetime, - ) + fixed_costs += sum( + self.invest[g, p] + * max(g.demand) + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime, ) - * ((1 + m.discount_rate) ** -m.es.periods_years[p]) - ) + ) * ((1 + m.discount_rate) ** -m.es.periods_years[p]) for g in self.EXISTING_INVESTDSM: if g.investment.fixed_costs[0] is not None: @@ -5246,19 +5240,16 @@ def _objective_expression(self): if g.investment.fixed_costs[0] is not None: lifetime = g.investment.lifetime for p in m.PERIODS: - fixed_costs += ( - sum( - self.invest[g, p] - * max(g.demand) - * g.investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range( - m.es.periods_years[p], - m.es.periods_years[p] + lifetime, - ) + fixed_costs += sum( + self.invest[g, p] + * max(g.demand) + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime, ) - * ((1 + m.discount_rate) ** -m.es.periods_years[p]) - ) + ) * ((1 + m.discount_rate) ** -m.es.periods_years[p]) for g in self.EXISTING_INVESTDSM: if g.investment.fixed_costs[0] is not None: diff --git a/src/oemof/solph/flows/_investment_flow.py b/src/oemof/solph/flows/_investment_flow.py index a6b50af58..33559e481 100644 --- a/src/oemof/solph/flows/_investment_flow.py +++ b/src/oemof/solph/flows/_investment_flow.py @@ -962,21 +962,15 @@ def _objective_expression(self): if m.flows[i, o].investment.fixed_costs[0] is not None: lifetime = m.flows[i, o].investment.lifetime for p in m.PERIODS: - fixed_costs += ( - sum( - self.invest[i, o, p] - * m.flows[i, o].investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range( - m.es.periods_years[p], - m.es.periods_years[p] + lifetime, - ) - ) - * ( - (1 + m.discount_rate) - ** (-m.es.periods_years[p]) + fixed_costs += sum( + self.invest[i, o, p] + * m.flows[i, o].investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime, ) - ) + ) * ((1 + m.discount_rate) ** (-m.es.periods_years[p])) for i, o in self.EXISTING_INVESTFLOWS: if m.flows[i, o].investment.fixed_costs[0] is not None: diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index e52782dd2..a4c0ec4ba 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -1175,7 +1175,7 @@ def test_piecewise_linear_transformer_cc(self): }, outputs={bel: solph.flows.Flow()}, in_breakpoints=[0, 25, 50, 75, 100], - conversion_function=lambda x: x ** 2, + conversion_function=lambda x: x**2, pw_repn="CC", ) self.compare_lp_files("piecewise_linear_transformer_cc.lp") @@ -1191,7 +1191,7 @@ def test_piecewise_linear_transformer_dcc(self): }, outputs={bel: solph.flows.Flow()}, in_breakpoints=[0, 25, 50, 75, 100], - conversion_function=lambda x: x ** 2, + conversion_function=lambda x: x**2, pw_repn="DCC", ) self.compare_lp_files("piecewise_linear_transformer_dcc.lp") @@ -1665,7 +1665,7 @@ def test_summed_min_max_source(self): summed_max=100, variable_costs=25, max=0.8, - nominal_value=10 + nominal_value=10, ) }, ) @@ -1680,10 +1680,7 @@ def test_integer_flow_source(self): label="excess", inputs={ bel: solph.flows.Flow( - variable_costs=25, - max=1, - nominal_value=10, - integer=True + variable_costs=25, max=1, nominal_value=10, integer=True ) }, ) diff --git a/tests/multi_period_constraint_tests.py b/tests/multi_period_constraint_tests.py index 20d3baed8..0bc6186d9 100644 --- a/tests/multi_period_constraint_tests.py +++ b/tests/multi_period_constraint_tests.py @@ -1383,7 +1383,7 @@ def test_piecewise_linear_transformer_cc(self): }, outputs={bel: solph.flows.Flow()}, in_breakpoints=[0, 25, 50, 75, 100], - conversion_function=lambda x: x ** 2, + conversion_function=lambda x: x**2, pw_repn="CC", ) self.compare_lp_files( @@ -1401,7 +1401,7 @@ def test_piecewise_linear_transformer_dcc(self): }, outputs={bel: solph.flows.Flow()}, in_breakpoints=[0, 25, 50, 75, 100], - conversion_function=lambda x: x ** 2, + conversion_function=lambda x: x**2, pw_repn="DCC", ) self.compare_lp_files( @@ -1901,7 +1901,7 @@ def test_summed_min_max_source(self): summed_max=100, variable_costs=25, max=0.8, - nominal_value=10 + nominal_value=10, ) }, ) @@ -1916,10 +1916,7 @@ def test_flow_reaching_lifetime(self): label="excess", inputs={ bel: solph.flows.Flow( - variable_costs=25, - max=0.8, - nominal_value=10, - lifetime=2 + variable_costs=25, max=0.8, nominal_value=10, lifetime=2 ) }, ) @@ -1938,7 +1935,7 @@ def test_flow_reaching_lifetime_initial_age(self): max=0.8, nominal_value=10, lifetime=2, - age=1 + age=1, ) }, ) @@ -1953,10 +1950,7 @@ def test_fixed_costs(self): label="pv_forever", outputs={ bel: solph.flows.Flow( - variable_costs=25, - max=0.8, - nominal_value=10, - fixed_costs=3 + variable_costs=25, max=0.8, nominal_value=10, fixed_costs=3 ) }, ) @@ -1969,7 +1963,7 @@ def test_fixed_costs(self): max=0.8, nominal_value=10, fixed_costs=3, - lifetime=20 + lifetime=20, ) }, ) @@ -1983,7 +1977,7 @@ def test_fixed_costs(self): nominal_value=10, fixed_costs=3, lifetime=20, - age=18 + age=18, ) }, ) diff --git a/tests/test_components.py b/tests/test_components.py index a90991090..b139adbda 100644 --- a/tests/test_components.py +++ b/tests/test_components.py @@ -311,9 +311,3 @@ def test_generic_chp_without_warning(): back_pressure=False, ) warnings.filterwarnings("always", category=SuspiciousUsageWarning) - - -# ********* SinkDSM ********* - -def test_sink_dsm_attribute_error1(): - pass diff --git a/tests/test_energy_system.py b/tests/test_energy_system.py index b76631bc0..f6478b5dc 100644 --- a/tests/test_energy_system.py +++ b/tests/test_energy_system.py @@ -37,11 +37,7 @@ def test_extract_periods_years(): timeincrement=[1] * len(timeindex), multi_period=True, ) - periods_years = { - 0: 0, - 1: 21, - 2: 30 - } + periods_years = {0: 0, 1: 21, 2: 30} assert es.periods_years == periods_years diff --git a/tests/test_models.py b/tests/test_models.py index f21090f4a..d0ca47d9a 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -146,19 +146,16 @@ def test_multi_period_default_discount_rate(): es.add(bel) es.add( solph.components.Sink( - inputs={bel: solph.flows.Flow( - nominal_value=5, - fix=[1] * len(timeindex)) + inputs={ + bel: solph.flows.Flow( + nominal_value=5, fix=[1] * len(timeindex) + ) } ) ) es.add( solph.components.Source( - outputs={ - bel: solph.flows.Flow( - nominal_value=4, variable_costs=5 - ) - } + outputs={bel: solph.flows.Flow(nominal_value=4, variable_costs=5)} ) ) msg = ( diff --git a/tests/test_options.py b/tests/test_options.py index 91e2ec868..20fd487f0 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -18,9 +18,4 @@ def test_check_age_and_lifetime(): "expected lifetime." ) with pytest.raises(AttributeError, match=msg): - solph.components.Sink( - investment=solph.Investment( - age=41, - lifetime=40 - ) - ) + solph.components.Sink(investment=solph.Investment(age=41, lifetime=40)) diff --git a/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py index e3c9e2913..28288409b 100644 --- a/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py +++ b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py @@ -52,43 +52,20 @@ def test_multi_period_investment_model(solver="cbc"): ) # Create buses - bus_lignite = buses.Bus( - label="DE_bus_lignite", - balanced=True - ) - bus_hardcoal = buses.Bus( - label="DE_bus_hardcoal", - balanced=True - ) - bus_natgas = buses.Bus( - label="DE_bus_natgas", - balanced=True - ) - bus_el = buses.Bus( - label="DE_bus_el", - balanced=True - ) - bus_el_FR = buses.Bus( - label="FR_bus_el", - balanced=True - ) + bus_lignite = buses.Bus(label="DE_bus_lignite", balanced=True) + bus_hardcoal = buses.Bus(label="DE_bus_hardcoal", balanced=True) + bus_natgas = buses.Bus(label="DE_bus_natgas", balanced=True) + bus_el = buses.Bus(label="DE_bus_el", balanced=True) + bus_el_FR = buses.Bus(label="FR_bus_el", balanced=True) # Create sources source_lignite = components.Source( label="DE_source_lignite", - outputs={ - bus_lignite: flows.Flow( - variable_costs=5 - ) - }, + outputs={bus_lignite: flows.Flow(variable_costs=5)}, ) source_hardcoal = components.Source( label="DE_source_hardcoal", - outputs={ - bus_hardcoal: flows.Flow( - variable_costs=10 - ) - }, + outputs={bus_hardcoal: flows.Flow(variable_costs=10)}, ) source_natgas = components.Source( label="DE_source_natgas", @@ -104,17 +81,14 @@ def test_multi_period_investment_model(solver="cbc"): bus_el: flows.Flow( variable_costs=0, fix=[110] + [90] * (len(timeindex) - 1), - nominal_value=1 + nominal_value=1, ) }, ) source_shortage = components.Source( label="DE_source_shortage", outputs={ - bus_el: flows.Flow( - variable_costs=1e10, - nominal_value=1e10 - ) + bus_el: flows.Flow(variable_costs=1e10, nominal_value=1e10) }, ) source_wind_FR = components.Source( @@ -123,17 +97,14 @@ def test_multi_period_investment_model(solver="cbc"): bus_el_FR: flows.Flow( variable_costs=0, fix=[45] * len(timeindex), - nominal_value=1 + nominal_value=1, ) }, ) source_shortage_FR = components.Source( label="FR_source_shortage", outputs={ - bus_el_FR: flows.Flow( - variable_costs=1e10, - nominal_value=1e10 - ) + bus_el_FR: flows.Flow(variable_costs=1e10, nominal_value=1e10) }, ) @@ -141,37 +112,27 @@ def test_multi_period_investment_model(solver="cbc"): sink_el = components.Sink( label="DE_sink_el", inputs={ - bus_el: flows.Flow( - fix=[80] * len(timeindex), - nominal_value=1 - ) + bus_el: flows.Flow(fix=[80] * len(timeindex), nominal_value=1) }, ) sink_excess = components.Sink( label="DE_sink_excess", inputs={ - bus_el: flows.Flow( - variable_costs=1e10, - nominal_value=1e10 - ) + bus_el: flows.Flow(variable_costs=1e10, nominal_value=1e10) }, ) sink_el_FR = components.Sink( label="FR_sink_el", inputs={ bus_el_FR: flows.Flow( - fix=[50] * len(timeindex), - nominal_value=1 + fix=[50] * len(timeindex), nominal_value=1 ) }, ) sink_excess_FR = components.Sink( label="FR_sink_excess", inputs={ - bus_el_FR: flows.Flow( - variable_costs=1e3, - nominal_value=1e10 - ) + bus_el_FR: flows.Flow(variable_costs=1e3, nominal_value=1e10) }, ) @@ -189,7 +150,7 @@ def test_multi_period_investment_model(solver="cbc"): age=0, interest_rate=0.02, ), - variable_costs=1 + variable_costs=1, ) }, conversion_factors={bus_el: 0.38}, @@ -208,7 +169,7 @@ def test_multi_period_investment_model(solver="cbc"): age=0, interest_rate=0.02, ), - variable_costs=2 + variable_costs=2, ) }, conversion_factors={bus_el: 0.45}, @@ -227,7 +188,7 @@ def test_multi_period_investment_model(solver="cbc"): age=0, interest_rate=0.02, ), - variable_costs=3 + variable_costs=3, ) }, conversion_factors={bus_el: 0.6}, @@ -245,9 +206,9 @@ def test_multi_period_investment_model(solver="cbc"): lifetime=20, age=0, interest_rate=0.02, - fixed_costs=1000 + fixed_costs=1000, ), - variable_costs=4 + variable_costs=4, ) }, conversion_factors={bus_el: 0.4}, @@ -279,7 +240,7 @@ def test_multi_period_investment_model(solver="cbc"): existing=10, lifetime=2, age=1, - interest_rate=0.02 + interest_rate=0.02, ), ) }, @@ -301,7 +262,7 @@ def test_multi_period_investment_model(solver="cbc"): lifetime=2, age=1, interest_rate=0.02, - fixed_costs=10 + fixed_costs=10, ), ) From c226a4abaefc4404932c72a19bbe3a738c7fd8bf Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 12 Mar 2022 12:43:11 +0100 Subject: [PATCH 0193/1363] Fix black issues --- .../solph/components/_generic_storage.py | 22 ++--- .../components/experimental/_sink_dsm.py | 63 ++++++-------- src/oemof/solph/flows/_investment_flow.py | 22 ++--- tests/constraint_tests.py | 11 +-- tests/multi_period_constraint_tests.py | 22 ++--- tests/test_components.py | 6 -- tests/test_energy_system.py | 6 +- tests/test_models.py | 13 ++- tests/test_options.py | 7 +- .../test_multi_period_investment_model.py | 83 +++++-------------- 10 files changed, 84 insertions(+), 171 deletions(-) diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index 1d6731eb5..6bae47830 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -1680,21 +1680,15 @@ def _objective_expression(self): if n.investment.fixed_costs[0] is not None: lifetime = n.investment.lifetime for p in m.PERIODS: - fixed_costs += ( - sum( - self.invest[n, p] - * n.investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range( - m.es.periods_years[p], - m.es.periods_years[p] + lifetime, - ) + fixed_costs += sum( + self.invest[n, p] + * n.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime, ) - * ( - (1 + m.discount_rate) - ** (-m.es.periods_years[p]) - ) - ) + ) * ((1 + m.discount_rate) ** (-m.es.periods_years[p])) for n in self.EXISTING_INVESTSTORAGES: if n.investment.fixed_costs[0] is not None: diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index 9cf478c71..55940597c 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -1305,19 +1305,16 @@ def _objective_expression(self): if g.investment.fixed_costs[0] is not None: lifetime = g.investment.lifetime for p in m.PERIODS: - fixed_costs += ( - sum( - self.invest[g, p] - * max(g.demand) - * g.investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range( - m.es.periods_years[p], - m.es.periods_years[p] + lifetime, - ) + fixed_costs += sum( + self.invest[g, p] + * max(g.demand) + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime, ) - * ((1 + m.discount_rate) ** -m.es.periods_years[p]) - ) + ) * ((1 + m.discount_rate) ** -m.es.periods_years[p]) for g in self.EXISTING_INVESTDSM: if g.investment.fixed_costs[0] is not None: @@ -2956,19 +2953,16 @@ def _objective_expression(self): if g.investment.fixed_costs[0] is not None: lifetime = g.investment.lifetime for p in m.PERIODS: - fixed_costs += ( - sum( - self.invest[g, p] - * max(g.demand) - * g.investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range( - m.es.periods_years[p], - m.es.periods_years[p] + lifetime, - ) + fixed_costs += sum( + self.invest[g, p] + * max(g.demand) + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime, ) - * ((1 + m.discount_rate) ** -m.es.periods_years[p]) - ) + ) * ((1 + m.discount_rate) ** -m.es.periods_years[p]) for g in self.EXISTING_INVESTDSM: if g.investment.fixed_costs[0] is not None: @@ -5246,19 +5240,16 @@ def _objective_expression(self): if g.investment.fixed_costs[0] is not None: lifetime = g.investment.lifetime for p in m.PERIODS: - fixed_costs += ( - sum( - self.invest[g, p] - * max(g.demand) - * g.investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range( - m.es.periods_years[p], - m.es.periods_years[p] + lifetime, - ) + fixed_costs += sum( + self.invest[g, p] + * max(g.demand) + * g.investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime, ) - * ((1 + m.discount_rate) ** -m.es.periods_years[p]) - ) + ) * ((1 + m.discount_rate) ** -m.es.periods_years[p]) for g in self.EXISTING_INVESTDSM: if g.investment.fixed_costs[0] is not None: diff --git a/src/oemof/solph/flows/_investment_flow.py b/src/oemof/solph/flows/_investment_flow.py index a6b50af58..33559e481 100644 --- a/src/oemof/solph/flows/_investment_flow.py +++ b/src/oemof/solph/flows/_investment_flow.py @@ -962,21 +962,15 @@ def _objective_expression(self): if m.flows[i, o].investment.fixed_costs[0] is not None: lifetime = m.flows[i, o].investment.lifetime for p in m.PERIODS: - fixed_costs += ( - sum( - self.invest[i, o, p] - * m.flows[i, o].investment.fixed_costs[pp] - * ((1 + m.discount_rate) ** (-pp)) - for pp in range( - m.es.periods_years[p], - m.es.periods_years[p] + lifetime, - ) - ) - * ( - (1 + m.discount_rate) - ** (-m.es.periods_years[p]) + fixed_costs += sum( + self.invest[i, o, p] + * m.flows[i, o].investment.fixed_costs[pp] + * ((1 + m.discount_rate) ** (-pp)) + for pp in range( + m.es.periods_years[p], + m.es.periods_years[p] + lifetime, ) - ) + ) * ((1 + m.discount_rate) ** (-m.es.periods_years[p])) for i, o in self.EXISTING_INVESTFLOWS: if m.flows[i, o].investment.fixed_costs[0] is not None: diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index e52782dd2..a4c0ec4ba 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -1175,7 +1175,7 @@ def test_piecewise_linear_transformer_cc(self): }, outputs={bel: solph.flows.Flow()}, in_breakpoints=[0, 25, 50, 75, 100], - conversion_function=lambda x: x ** 2, + conversion_function=lambda x: x**2, pw_repn="CC", ) self.compare_lp_files("piecewise_linear_transformer_cc.lp") @@ -1191,7 +1191,7 @@ def test_piecewise_linear_transformer_dcc(self): }, outputs={bel: solph.flows.Flow()}, in_breakpoints=[0, 25, 50, 75, 100], - conversion_function=lambda x: x ** 2, + conversion_function=lambda x: x**2, pw_repn="DCC", ) self.compare_lp_files("piecewise_linear_transformer_dcc.lp") @@ -1665,7 +1665,7 @@ def test_summed_min_max_source(self): summed_max=100, variable_costs=25, max=0.8, - nominal_value=10 + nominal_value=10, ) }, ) @@ -1680,10 +1680,7 @@ def test_integer_flow_source(self): label="excess", inputs={ bel: solph.flows.Flow( - variable_costs=25, - max=1, - nominal_value=10, - integer=True + variable_costs=25, max=1, nominal_value=10, integer=True ) }, ) diff --git a/tests/multi_period_constraint_tests.py b/tests/multi_period_constraint_tests.py index 20d3baed8..0bc6186d9 100644 --- a/tests/multi_period_constraint_tests.py +++ b/tests/multi_period_constraint_tests.py @@ -1383,7 +1383,7 @@ def test_piecewise_linear_transformer_cc(self): }, outputs={bel: solph.flows.Flow()}, in_breakpoints=[0, 25, 50, 75, 100], - conversion_function=lambda x: x ** 2, + conversion_function=lambda x: x**2, pw_repn="CC", ) self.compare_lp_files( @@ -1401,7 +1401,7 @@ def test_piecewise_linear_transformer_dcc(self): }, outputs={bel: solph.flows.Flow()}, in_breakpoints=[0, 25, 50, 75, 100], - conversion_function=lambda x: x ** 2, + conversion_function=lambda x: x**2, pw_repn="DCC", ) self.compare_lp_files( @@ -1901,7 +1901,7 @@ def test_summed_min_max_source(self): summed_max=100, variable_costs=25, max=0.8, - nominal_value=10 + nominal_value=10, ) }, ) @@ -1916,10 +1916,7 @@ def test_flow_reaching_lifetime(self): label="excess", inputs={ bel: solph.flows.Flow( - variable_costs=25, - max=0.8, - nominal_value=10, - lifetime=2 + variable_costs=25, max=0.8, nominal_value=10, lifetime=2 ) }, ) @@ -1938,7 +1935,7 @@ def test_flow_reaching_lifetime_initial_age(self): max=0.8, nominal_value=10, lifetime=2, - age=1 + age=1, ) }, ) @@ -1953,10 +1950,7 @@ def test_fixed_costs(self): label="pv_forever", outputs={ bel: solph.flows.Flow( - variable_costs=25, - max=0.8, - nominal_value=10, - fixed_costs=3 + variable_costs=25, max=0.8, nominal_value=10, fixed_costs=3 ) }, ) @@ -1969,7 +1963,7 @@ def test_fixed_costs(self): max=0.8, nominal_value=10, fixed_costs=3, - lifetime=20 + lifetime=20, ) }, ) @@ -1983,7 +1977,7 @@ def test_fixed_costs(self): nominal_value=10, fixed_costs=3, lifetime=20, - age=18 + age=18, ) }, ) diff --git a/tests/test_components.py b/tests/test_components.py index a90991090..b139adbda 100644 --- a/tests/test_components.py +++ b/tests/test_components.py @@ -311,9 +311,3 @@ def test_generic_chp_without_warning(): back_pressure=False, ) warnings.filterwarnings("always", category=SuspiciousUsageWarning) - - -# ********* SinkDSM ********* - -def test_sink_dsm_attribute_error1(): - pass diff --git a/tests/test_energy_system.py b/tests/test_energy_system.py index b76631bc0..f6478b5dc 100644 --- a/tests/test_energy_system.py +++ b/tests/test_energy_system.py @@ -37,11 +37,7 @@ def test_extract_periods_years(): timeincrement=[1] * len(timeindex), multi_period=True, ) - periods_years = { - 0: 0, - 1: 21, - 2: 30 - } + periods_years = {0: 0, 1: 21, 2: 30} assert es.periods_years == periods_years diff --git a/tests/test_models.py b/tests/test_models.py index f21090f4a..d0ca47d9a 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -146,19 +146,16 @@ def test_multi_period_default_discount_rate(): es.add(bel) es.add( solph.components.Sink( - inputs={bel: solph.flows.Flow( - nominal_value=5, - fix=[1] * len(timeindex)) + inputs={ + bel: solph.flows.Flow( + nominal_value=5, fix=[1] * len(timeindex) + ) } ) ) es.add( solph.components.Source( - outputs={ - bel: solph.flows.Flow( - nominal_value=4, variable_costs=5 - ) - } + outputs={bel: solph.flows.Flow(nominal_value=4, variable_costs=5)} ) ) msg = ( diff --git a/tests/test_options.py b/tests/test_options.py index 91e2ec868..20fd487f0 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -18,9 +18,4 @@ def test_check_age_and_lifetime(): "expected lifetime." ) with pytest.raises(AttributeError, match=msg): - solph.components.Sink( - investment=solph.Investment( - age=41, - lifetime=40 - ) - ) + solph.components.Sink(investment=solph.Investment(age=41, lifetime=40)) diff --git a/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py index e3c9e2913..28288409b 100644 --- a/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py +++ b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py @@ -52,43 +52,20 @@ def test_multi_period_investment_model(solver="cbc"): ) # Create buses - bus_lignite = buses.Bus( - label="DE_bus_lignite", - balanced=True - ) - bus_hardcoal = buses.Bus( - label="DE_bus_hardcoal", - balanced=True - ) - bus_natgas = buses.Bus( - label="DE_bus_natgas", - balanced=True - ) - bus_el = buses.Bus( - label="DE_bus_el", - balanced=True - ) - bus_el_FR = buses.Bus( - label="FR_bus_el", - balanced=True - ) + bus_lignite = buses.Bus(label="DE_bus_lignite", balanced=True) + bus_hardcoal = buses.Bus(label="DE_bus_hardcoal", balanced=True) + bus_natgas = buses.Bus(label="DE_bus_natgas", balanced=True) + bus_el = buses.Bus(label="DE_bus_el", balanced=True) + bus_el_FR = buses.Bus(label="FR_bus_el", balanced=True) # Create sources source_lignite = components.Source( label="DE_source_lignite", - outputs={ - bus_lignite: flows.Flow( - variable_costs=5 - ) - }, + outputs={bus_lignite: flows.Flow(variable_costs=5)}, ) source_hardcoal = components.Source( label="DE_source_hardcoal", - outputs={ - bus_hardcoal: flows.Flow( - variable_costs=10 - ) - }, + outputs={bus_hardcoal: flows.Flow(variable_costs=10)}, ) source_natgas = components.Source( label="DE_source_natgas", @@ -104,17 +81,14 @@ def test_multi_period_investment_model(solver="cbc"): bus_el: flows.Flow( variable_costs=0, fix=[110] + [90] * (len(timeindex) - 1), - nominal_value=1 + nominal_value=1, ) }, ) source_shortage = components.Source( label="DE_source_shortage", outputs={ - bus_el: flows.Flow( - variable_costs=1e10, - nominal_value=1e10 - ) + bus_el: flows.Flow(variable_costs=1e10, nominal_value=1e10) }, ) source_wind_FR = components.Source( @@ -123,17 +97,14 @@ def test_multi_period_investment_model(solver="cbc"): bus_el_FR: flows.Flow( variable_costs=0, fix=[45] * len(timeindex), - nominal_value=1 + nominal_value=1, ) }, ) source_shortage_FR = components.Source( label="FR_source_shortage", outputs={ - bus_el_FR: flows.Flow( - variable_costs=1e10, - nominal_value=1e10 - ) + bus_el_FR: flows.Flow(variable_costs=1e10, nominal_value=1e10) }, ) @@ -141,37 +112,27 @@ def test_multi_period_investment_model(solver="cbc"): sink_el = components.Sink( label="DE_sink_el", inputs={ - bus_el: flows.Flow( - fix=[80] * len(timeindex), - nominal_value=1 - ) + bus_el: flows.Flow(fix=[80] * len(timeindex), nominal_value=1) }, ) sink_excess = components.Sink( label="DE_sink_excess", inputs={ - bus_el: flows.Flow( - variable_costs=1e10, - nominal_value=1e10 - ) + bus_el: flows.Flow(variable_costs=1e10, nominal_value=1e10) }, ) sink_el_FR = components.Sink( label="FR_sink_el", inputs={ bus_el_FR: flows.Flow( - fix=[50] * len(timeindex), - nominal_value=1 + fix=[50] * len(timeindex), nominal_value=1 ) }, ) sink_excess_FR = components.Sink( label="FR_sink_excess", inputs={ - bus_el_FR: flows.Flow( - variable_costs=1e3, - nominal_value=1e10 - ) + bus_el_FR: flows.Flow(variable_costs=1e3, nominal_value=1e10) }, ) @@ -189,7 +150,7 @@ def test_multi_period_investment_model(solver="cbc"): age=0, interest_rate=0.02, ), - variable_costs=1 + variable_costs=1, ) }, conversion_factors={bus_el: 0.38}, @@ -208,7 +169,7 @@ def test_multi_period_investment_model(solver="cbc"): age=0, interest_rate=0.02, ), - variable_costs=2 + variable_costs=2, ) }, conversion_factors={bus_el: 0.45}, @@ -227,7 +188,7 @@ def test_multi_period_investment_model(solver="cbc"): age=0, interest_rate=0.02, ), - variable_costs=3 + variable_costs=3, ) }, conversion_factors={bus_el: 0.6}, @@ -245,9 +206,9 @@ def test_multi_period_investment_model(solver="cbc"): lifetime=20, age=0, interest_rate=0.02, - fixed_costs=1000 + fixed_costs=1000, ), - variable_costs=4 + variable_costs=4, ) }, conversion_factors={bus_el: 0.4}, @@ -279,7 +240,7 @@ def test_multi_period_investment_model(solver="cbc"): existing=10, lifetime=2, age=1, - interest_rate=0.02 + interest_rate=0.02, ), ) }, @@ -301,7 +262,7 @@ def test_multi_period_investment_model(solver="cbc"): lifetime=2, age=1, interest_rate=0.02, - fixed_costs=10 + fixed_costs=10, ), ) From 61d798e2c4367cd55146cfa79f2548165ed533fd Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Sat, 12 Mar 2022 12:48:48 +0100 Subject: [PATCH 0194/1363] Fix black issues --- tests/test_options.py | 5 +---- .../test_multi_period_investment_model.py | 6 +----- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/tests/test_options.py b/tests/test_options.py index 20fd487f0..c209c5e79 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -13,9 +13,6 @@ def test_check_age_and_lifetime(): """Check error being thrown if age > lifetime""" - msg = ( - "A unit's age must be smaller than its " - "expected lifetime." - ) + msg = "A unit's age must be smaller than its expected lifetime." with pytest.raises(AttributeError, match=msg): solph.components.Sink(investment=solph.Investment(age=41, lifetime=40)) diff --git a/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py index 28288409b..07d2663ad 100644 --- a/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py +++ b/tests/test_scripts/test_solph/test_multi_period_model/test_multi_period_investment_model.py @@ -69,11 +69,7 @@ def test_multi_period_investment_model(solver="cbc"): ) source_natgas = components.Source( label="DE_source_natgas", - outputs={ - bus_natgas: flows.Flow( - variable_costs=20 - ) - }, + outputs={bus_natgas: flows.Flow(variable_costs=20)}, ) source_wind = components.Source( label="DE_source_wind", From fae8b21f5894be69c162c312d21157a85019fa01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Mon, 14 Mar 2022 21:35:30 +0100 Subject: [PATCH 0195/1363] Adhere to Black --- tests/test_non_equidistant_time_index.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/tests/test_non_equidistant_time_index.py b/tests/test_non_equidistant_time_index.py index 8873a7f14..9add33190 100644 --- a/tests/test_non_equidistant_time_index.py +++ b/tests/test_non_equidistant_time_index.py @@ -124,14 +124,11 @@ def test_timesteps_timeincrements_with_storage_discharging(self): # Discharging - timestep (ts) with its timeincrement (ti) time = [(7, 1), (40, 0.5)] for ts, ti in time: - assert ( - round( - storage_content[ts] - - (discharge[ts] + (discharge[ts] * 1 / 9)) * ti, - 5, - ) - == round(storage_content[ts + 1], 5) - ) + assert round( + storage_content[ts] + - (discharge[ts] + (discharge[ts] * 1 / 9)) * ti, + 5, + ) == round(storage_content[ts + 1], 5) assert self.es.timeincrement[ts] == ti def test_timeincrements(self): From e7de9030f5aca0f0b787d122d9a04abe743a4dbc Mon Sep 17 00:00:00 2001 From: "RL-INSTITUT\\hendrik.huyskens" Date: Fri, 25 Mar 2022 11:23:03 +0100 Subject: [PATCH 0196/1363] Refactored assertion error in Link component into suspicious warning --- src/oemof/solph/custom/link.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/oemof/solph/custom/link.py b/src/oemof/solph/custom/link.py index c3d4780ab..3905b260c 100644 --- a/src/oemof/solph/custom/link.py +++ b/src/oemof/solph/custom/link.py @@ -16,7 +16,9 @@ SPDX-License-Identifier: MIT """ +from warnings import warn +from oemof.tools import debugging from oemof.network import network as on from pyomo.core import Binary from pyomo.core import Set @@ -80,14 +82,21 @@ def __init__(self, *args, **kwargs): for k, v in kwargs.get("conversion_factors", {}).items() } - wrong_args_message = ( - "Component `Link` must have exactly" - + "2 inputs, 2 outputs, and 2" - + "conversion factors connecting these." + msg = ( + "Component `Link` should have exactly " + + "2 inputs, 2 outputs, and 2 " + + "conversion factors connecting these. You are initializing " + + "a `Link`without obeying this specification. " + + "If this is intended and you know what you are doing you can " + + "disable the SuspiciousUsageWarning globally." ) - assert len(self.inputs) == 2, wrong_args_message - assert len(self.outputs) == 2, wrong_args_message - assert len(self.conversion_factors) == 2, wrong_args_message + + if ( + len(self.inputs) != 2 + or len(self.outputs) != 2 + or len(self.conversion_factors) != 2 + ): + warn(msg, debugging.SuspiciousUsageWarning) def constraint_group(self): return LinkBlock From d2e58336c6ab730bd29d5ed79bc8dcd722380794 Mon Sep 17 00:00:00 2001 From: "RL-INSTITUT\\hendrik.huyskens" Date: Fri, 25 Mar 2022 11:31:54 +0100 Subject: [PATCH 0197/1363] Fixed import order --- src/oemof/solph/custom/link.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/solph/custom/link.py b/src/oemof/solph/custom/link.py index 3905b260c..0fc301a78 100644 --- a/src/oemof/solph/custom/link.py +++ b/src/oemof/solph/custom/link.py @@ -18,8 +18,8 @@ """ from warnings import warn -from oemof.tools import debugging from oemof.network import network as on +from oemof.tools import debugging from pyomo.core import Binary from pyomo.core import Set from pyomo.core import Var From 0514de6f40595c1d98a5f19d96da96d57abc8892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Tue, 5 Apr 2022 20:58:56 +0200 Subject: [PATCH 0198/1363] Add installation of conda using cbc to readme The part about installing solvers is already pretty exhaustive, so I kept this extra info rather short. In particular, there is no information about conda. I assume that people know enough about it or look at other sources if they want to use conda. Implements #835 --- README.rst | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index f2690e2d3..37cbf7910 100644 --- a/README.rst +++ b/README.rst @@ -179,6 +179,16 @@ GLPK-solver: http://arnab-deka.com/posts/2010/02/installing-glpk-on-a-mac/ If you install the CBC solver via brew (highly recommended), it should work without additional configuration. +**conda** + +The CBC-solver can also be installed in a `conda` environment. Please note, that it is highly recomended to `use pip after conda `_, so: + +.. code:: console + + conda install -c conda-forge coincbc + pip install oemof.solph + + .. _check_installation_label: Installation test @@ -245,7 +255,7 @@ You are welcome to contribute your own examples via a `pull request Date: Tue, 5 Apr 2022 20:51:14 +0100 Subject: [PATCH 0199/1363] Rename oemof.network.network.Node to Entity --- docs/usage.rst | 2 +- docs/whatsnew/v0-2-0.rst | 2 +- src/oemof/solph/processing.py | 10 +++++----- tests/constraint_tests.py | 4 ++-- tests/solph_tests.py | 4 ++-- .../test_connect_invest/test_connect_invest.py | 2 +- .../test_flexible_modelling/test_add_constraints.py | 4 ++-- .../test_solph/test_generic_caes/test_generic_caes.py | 4 ++-- .../test_solph/test_generic_chp/test_generic_chp.py | 4 ++-- .../test_invest_fix_flow/test_simple_invest_fixed.py | 4 ++-- .../test_simple_model/test_simple_dispatch_one.py | 4 ++-- .../test_solph/test_simple_model/test_simple_invest.py | 4 ++-- .../test_invest_storage_regression.py | 4 ++-- .../test_storage_investment/test_storage_investment.py | 4 ++-- .../test_storage_with_tuple_label.py | 4 ++-- .../test_solph/test_variable_chp/test_variable_chp.py | 4 ++-- 16 files changed, 32 insertions(+), 32 deletions(-) diff --git a/docs/usage.rst b/docs/usage.rst index 59bf1b5c3..62098f846 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -794,7 +794,7 @@ This small example of PV, grid and SinkDSM shows how to use the component # Create Energy System es = solph.EnergySystem(timeindex=datetimeindex) - Node.registry = es + Entity.registry = es # Create bus representing electricity grid b_elec = solph.buses.Bus(label='Electricity bus') diff --git a/docs/whatsnew/v0-2-0.rst b/docs/whatsnew/v0-2-0.rst index 8f1c0aa71..7c951b098 100644 --- a/docs/whatsnew/v0-2-0.rst +++ b/docs/whatsnew/v0-2-0.rst @@ -24,7 +24,7 @@ API changes most recently created `energy system `. You can still restore the old automatic registration by manually assigning an `energy system - ` to `Node.registry + ` to `Entity.registry `. On the other hand you can still explicitly `add ` `nodes ` to an `energy system diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index 5e524dd33..7752d84cc 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -18,7 +18,7 @@ from itertools import groupby import pandas as pd -from oemof.network.network import Node +from oemof.network.network import Entity from pyomo.core.base.piecewise import IndexedPiecewise from pyomo.core.base.var import Var @@ -35,8 +35,8 @@ def get_tuple(x): for i in x: if isinstance(i, tuple): return i - elif issubclass(type(i), Node): - return (i,) + elif issubclass(type(i), Entity): + return i, # for standalone variables, x is used as identifying tuple if isinstance(x, tuple): @@ -51,7 +51,7 @@ def get_timestep(x): is fetched as the last element. For time-independent data (scalars) zero ist returned. """ - if all(issubclass(type(n), Node) for n in x): + if all(issubclass(type(n), Entity) for n in x): return 0 else: return x[-1] @@ -63,7 +63,7 @@ def remove_timestep(x): The timestep is removed from tuples of type `(n, n, int)` and `(n, int)`. """ - if all(issubclass(type(n), Node) for n in x): + if all(issubclass(type(n), Entity) for n in x): return x else: return x[:-1] diff --git a/tests/constraint_tests.py b/tests/constraint_tests.py index cedb45c3c..759dbc9be 100644 --- a/tests/constraint_tests.py +++ b/tests/constraint_tests.py @@ -18,7 +18,7 @@ import pytest from nose.tools import assert_raises from nose.tools import eq_ -from oemof.network.network import Node +from oemof.network.network import Entity from oemof import solph @@ -41,7 +41,7 @@ def setup(self): self.energysystem = solph.EnergySystem( groupings=solph.GROUPINGS, timeindex=self.date_time_index ) - Node.registry = self.energysystem + Entity.registry = self.energysystem def get_om(self): return solph.Model( diff --git a/tests/solph_tests.py b/tests/solph_tests.py index 4010ab21d..65f61a651 100644 --- a/tests/solph_tests.py +++ b/tests/solph_tests.py @@ -13,7 +13,7 @@ from nose.tools import ok_ from oemof.network.energy_system import EnergySystem as EnSys -from oemof.network.network import Node +from oemof.network.network import Entity from oemof import solph as solph from oemof.solph import Investment @@ -24,7 +24,7 @@ class TestsGrouping: def setup(self): self.es = EnSys(groupings=solph.GROUPINGS) - Node.registry = self.es + Entity.registry = self.es def test_investment_flow_grouping(self): """Flows of investment sink should be grouped. diff --git a/tests/test_scripts/test_solph/test_connect_invest/test_connect_invest.py b/tests/test_scripts/test_solph/test_connect_invest/test_connect_invest.py index 6d830b9d2..01a3dce47 100644 --- a/tests/test_scripts/test_solph/test_connect_invest/test_connect_invest.py +++ b/tests/test_scripts/test_solph/test_connect_invest/test_connect_invest.py @@ -32,7 +32,7 @@ def test_connect_invest(): date_time_index = pd.date_range("1/1/2012", periods=24 * 7, freq="H") energysystem = EnergySystem(timeindex=date_time_index) - network.Node.registry = energysystem + network.Entity.registry = energysystem # Read data file full_filename = os.path.join( diff --git a/tests/test_scripts/test_solph/test_flexible_modelling/test_add_constraints.py b/tests/test_scripts/test_solph/test_flexible_modelling/test_add_constraints.py index 09d991f20..8add3bb13 100644 --- a/tests/test_scripts/test_solph/test_flexible_modelling/test_add_constraints.py +++ b/tests/test_scripts/test_solph/test_flexible_modelling/test_add_constraints.py @@ -18,7 +18,7 @@ import pandas as pd from nose.tools import ok_ -from oemof.network.network import Node +from oemof.network.network import Entity from pyomo import environ as po from oemof.solph import EnergySystem @@ -34,7 +34,7 @@ def test_add_constraints_example(solver="cbc", nologg=False): # ##### creating an oemof solph optimization model, nothing special here ## # create an energy system object for the oemof solph nodes es = EnergySystem(timeindex=pd.date_range("1/1/2012", periods=4, freq="H")) - Node.registry = es + Entity.registry = es # add some nodes boil = Bus(label="oil", balanced=False) blig = Bus(label="lignite", balanced=False) diff --git a/tests/test_scripts/test_solph/test_generic_caes/test_generic_caes.py b/tests/test_scripts/test_solph/test_generic_caes/test_generic_caes.py index ac8aad4c0..dfd24f489 100644 --- a/tests/test_scripts/test_solph/test_generic_caes/test_generic_caes.py +++ b/tests/test_scripts/test_solph/test_generic_caes/test_generic_caes.py @@ -16,7 +16,7 @@ import pandas as pd from nose.tools import eq_ -from oemof.network.network import Node +from oemof.network.network import Entity from oemof.solph import EnergySystem from oemof.solph import Model @@ -40,7 +40,7 @@ def test_gen_caes(): # create an energy system idx = pd.date_range("1/1/2017", periods=periods, freq="H") es = EnergySystem(timeindex=idx) - Node.registry = es + Entity.registry = es # resources bgas = Bus(label="bgas") diff --git a/tests/test_scripts/test_solph/test_generic_chp/test_generic_chp.py b/tests/test_scripts/test_solph/test_generic_chp/test_generic_chp.py index 64612d119..2660975ee 100644 --- a/tests/test_scripts/test_solph/test_generic_chp/test_generic_chp.py +++ b/tests/test_scripts/test_solph/test_generic_chp/test_generic_chp.py @@ -16,7 +16,7 @@ import pandas as pd from nose.tools import eq_ -from oemof.network.network import Node +from oemof.network.network import Entity from oemof import solph as solph from oemof.solph import processing @@ -34,7 +34,7 @@ def test_gen_chp(): # create an energy system idx = pd.date_range("1/1/2017", periods=periods, freq="H") es = solph.EnergySystem(timeindex=idx) - Node.registry = es + Entity.registry = es # resources bgas = solph.buses.Bus(label="bgas") diff --git a/tests/test_scripts/test_solph/test_invest_fix_flow/test_simple_invest_fixed.py b/tests/test_scripts/test_solph/test_invest_fix_flow/test_simple_invest_fixed.py index ecc8ef737..93d8ec171 100644 --- a/tests/test_scripts/test_solph/test_invest_fix_flow/test_simple_invest_fixed.py +++ b/tests/test_scripts/test_solph/test_invest_fix_flow/test_simple_invest_fixed.py @@ -15,7 +15,7 @@ import os import pandas as pd -from oemof.network.network import Node +from oemof.network.network import Entity from oemof.tools import economics from oemof.solph import EnergySystem @@ -31,7 +31,7 @@ def test_dispatch_fix_example(solver="cbc", periods=10): """Invest in a flow with a `fix` sequence containing values > 1.""" - Node.registry = None + Entity.registry = None filename = os.path.join(os.path.dirname(__file__), "input_data.csv") data = pd.read_csv(filename, sep=",") diff --git a/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one.py b/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one.py index bfd789264..1d4361fbb 100644 --- a/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one.py +++ b/tests/test_scripts/test_solph/test_simple_model/test_simple_dispatch_one.py @@ -12,7 +12,7 @@ """ from nose.tools import eq_ -from oemof.network.network import Node +from oemof.network.network import Entity from oemof.solph import EnergySystem from oemof.solph import Model @@ -29,7 +29,7 @@ def test_dispatch_one_time_step(solver="cbc"): """Create an energy system and optimize the dispatch at least costs.""" # ######################### create energysystem components ################ - Node.registry = None + Entity.registry = None # resource buses bgas = Bus(label="gas", balanced=False) diff --git a/tests/test_scripts/test_solph/test_simple_model/test_simple_invest.py b/tests/test_scripts/test_solph/test_simple_model/test_simple_invest.py index 285fba257..26a704d48 100644 --- a/tests/test_scripts/test_solph/test_simple_model/test_simple_invest.py +++ b/tests/test_scripts/test_solph/test_simple_model/test_simple_invest.py @@ -17,7 +17,7 @@ import pandas as pd from nose.tools import eq_ -from oemof.network.network import Node +from oemof.network.network import Entity from oemof.tools import economics from oemof.solph import EnergySystem @@ -34,7 +34,7 @@ def test_dispatch_example(solver="cbc", periods=24 * 5): """Create an energy system and optimize the dispatch at least costs.""" - Node.registry = None + Entity.registry = None filename = os.path.join(os.path.dirname(__file__), "input_data.csv") data = pd.read_csv(filename, sep=",") diff --git a/tests/test_scripts/test_solph/test_storage_investment/test_invest_storage_regression.py b/tests/test_scripts/test_solph/test_storage_investment/test_invest_storage_regression.py index cbd5a0181..541cc0fe7 100644 --- a/tests/test_scripts/test_solph/test_storage_investment/test_invest_storage_regression.py +++ b/tests/test_scripts/test_solph/test_storage_investment/test_invest_storage_regression.py @@ -12,7 +12,7 @@ import logging import pandas as pd -from oemof.network.network import Node +from oemof.network.network import Entity from oemof import solph from oemof.solph import views @@ -27,7 +27,7 @@ def test_regression_investment_storage(solver="cbc"): date_time_index = pd.date_range("1/1/2012", periods=4, freq="H") energysystem = solph.EnergySystem(timeindex=date_time_index) - Node.registry = energysystem + Entity.registry = energysystem # Buses bgas = solph.buses.Bus(label=("natural", "gas")) diff --git a/tests/test_scripts/test_solph/test_storage_investment/test_storage_investment.py b/tests/test_scripts/test_solph/test_storage_investment/test_storage_investment.py index dcac701ed..4ce5e2ab3 100644 --- a/tests/test_scripts/test_solph/test_storage_investment/test_storage_investment.py +++ b/tests/test_scripts/test_solph/test_storage_investment/test_storage_investment.py @@ -39,7 +39,7 @@ import pandas as pd from nose.tools import eq_ -from oemof.network.network import Node +from oemof.network.network import Entity from oemof.tools import economics from oemof import solph @@ -58,7 +58,7 @@ def test_optimise_storage_size( date_time_index = pd.date_range("1/1/2012", periods=400, freq="H") energysystem = solph.EnergySystem(timeindex=date_time_index) - Node.registry = energysystem + Entity.registry = energysystem full_filename = os.path.join(os.path.dirname(__file__), filename) data = pd.read_csv(full_filename, sep=",") diff --git a/tests/test_scripts/test_solph/test_storage_investment/test_storage_with_tuple_label.py b/tests/test_scripts/test_solph/test_storage_investment/test_storage_with_tuple_label.py index 0b78bc08a..e135fbf7e 100644 --- a/tests/test_scripts/test_solph/test_storage_investment/test_storage_with_tuple_label.py +++ b/tests/test_scripts/test_solph/test_storage_investment/test_storage_with_tuple_label.py @@ -40,7 +40,7 @@ import pandas as pd from nose.tools import eq_ -from oemof.network.network import Node +from oemof.network.network import Entity from oemof import solph as solph from oemof.solph import processing @@ -68,7 +68,7 @@ def test_tuples_as_labels_example( date_time_index = pd.date_range("1/1/2012", periods=40, freq="H") energysystem = solph.EnergySystem(timeindex=date_time_index) - Node.registry = energysystem + Entity.registry = energysystem full_filename = os.path.join(os.path.dirname(__file__), filename) data = pd.read_csv(full_filename, sep=",") diff --git a/tests/test_scripts/test_solph/test_variable_chp/test_variable_chp.py b/tests/test_scripts/test_solph/test_variable_chp/test_variable_chp.py index f1f1452c3..532032fe5 100644 --- a/tests/test_scripts/test_solph/test_variable_chp/test_variable_chp.py +++ b/tests/test_scripts/test_solph/test_variable_chp/test_variable_chp.py @@ -17,7 +17,7 @@ import pandas as pd from nose.tools import eq_ -from oemof.network.network import Node +from oemof.network.network import Entity from oemof import solph from oemof.solph import views @@ -29,7 +29,7 @@ def test_variable_chp(filename="variable_chp.csv", solver="cbc"): # create time index for 192 hours in May. date_time_index = pd.date_range("5/5/2012", periods=5, freq="H") energysystem = solph.EnergySystem(timeindex=date_time_index) - Node.registry = energysystem + Entity.registry = energysystem # Read data file with heat and electrical demand (192 hours) full_filename = os.path.join(os.path.dirname(__file__), filename) From 2bf771e43cba0edb088754dc67f751b98cc9581e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Tue, 5 Apr 2022 22:07:52 +0200 Subject: [PATCH 0200/1363] Adhere to Black style --- src/oemof/solph/processing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index 7752d84cc..58d022c01 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -36,7 +36,7 @@ def get_tuple(x): if isinstance(i, tuple): return i elif issubclass(type(i), Entity): - return i, + return (i,) # for standalone variables, x is used as identifying tuple if isinstance(x, tuple): From 31dcdc1596e85b1f03f0eb388ce8195d97af286d Mon Sep 17 00:00:00 2001 From: "RL-INSTITUT\\hendrik.huyskens" Date: Mon, 11 Apr 2022 14:22:56 +0200 Subject: [PATCH 0201/1363] Added option to (un-)limit directions in Link component --- src/oemof/solph/custom/link.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/oemof/solph/custom/link.py b/src/oemof/solph/custom/link.py index c3d4780ab..3eb62d01e 100644 --- a/src/oemof/solph/custom/link.py +++ b/src/oemof/solph/custom/link.py @@ -38,6 +38,8 @@ class Link(on.Transformer): Keys are the connected tuples (input, output) bus objects. The dictionary values can either be a scalar or an iterable with length of time horizon for simulation. + limit_direction : boolean, default: True + Wether direction constraint should be set for Link component Note: This component is experimental. Use it with care. @@ -79,6 +81,7 @@ def __init__(self, *args, **kwargs): k: sequence(v) for k, v in kwargs.get("conversion_factors", {}).items() } + self.limit_direction = kwargs.get("limit_direction", True) wrong_args_message = ( "Component `Link` must have exactly" @@ -100,6 +103,7 @@ class LinkBlock(SimpleBlock): Note: This component is experimental. Use it with care. **The following constraints are created:** + (Equation 2&3 are only implemented, if `limit_direction` is enabled) .. _Link-equations: @@ -147,11 +151,15 @@ def _create(self, group=None): } self.LINKS = Set(initialize=[g for g in group]) + + directed_conversions = { + n: n.conversion_factors for n in group if n.limit_direction + } self.LINK_1ST_INFLOWS = Set( - initialize=[(list(c)[0][0], n) for n, c in all_conversions.items()] + initialize=[(list(c)[0][0], n) for n, c in directed_conversions.items()] ) self.LINK_2ND_INFLOWS = Set( - initialize=[(list(c)[1][0], n) for n, c in all_conversions.items()] + initialize=[(list(c)[1][0], n) for n, c in directed_conversions.items()] ) # 0: Flows 1 connected; 1: Flows 2 connected From a0ec8c7d05aab2b8f2e05e9c8b45f6395c028212 Mon Sep 17 00:00:00 2001 From: "RL-INSTITUT\\hendrik.huyskens" Date: Mon, 11 Apr 2022 16:36:55 +0200 Subject: [PATCH 0202/1363] Fixed flake8 error --- src/oemof/solph/custom/link.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/oemof/solph/custom/link.py b/src/oemof/solph/custom/link.py index 3eb62d01e..f5c73c281 100644 --- a/src/oemof/solph/custom/link.py +++ b/src/oemof/solph/custom/link.py @@ -156,10 +156,14 @@ def _create(self, group=None): n: n.conversion_factors for n in group if n.limit_direction } self.LINK_1ST_INFLOWS = Set( - initialize=[(list(c)[0][0], n) for n, c in directed_conversions.items()] + initialize=[ + (list(c)[0][0], n) for n, c in directed_conversions.items() + ] ) self.LINK_2ND_INFLOWS = Set( - initialize=[(list(c)[1][0], n) for n, c in directed_conversions.items()] + initialize=[ + (list(c)[1][0], n) for n, c in directed_conversions.items() + ] ) # 0: Flows 1 connected; 1: Flows 2 connected From 5a86ad4b62200dae39bfc44ddc68417d23265c64 Mon Sep 17 00:00:00 2001 From: uvchik Date: Sat, 16 Apr 2022 22:04:35 +0200 Subject: [PATCH 0203/1363] Fix balance constraint --- src/oemof/solph/components/_generic_storage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index b716844aa..f72b9d180 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -477,7 +477,7 @@ def _balanced_storage_rule(block, n): """ return ( block.storage_content[n, m.TIMEPOINTS.at(-1)] - == block.storage_content[n, m.TIMEPOINTS.at(-1)] + == block.storage_content[n, m.TIMEPOINTS.at(1)] ) self.balanced_cstr = Constraint( From 5aa69e493adbdba808df2443df474db70e94b549 Mon Sep 17 00:00:00 2001 From: uvchik Date: Sat, 16 Apr 2022 22:05:05 +0200 Subject: [PATCH 0204/1363] Add function to create time index --- src/oemof/solph/_energy_system.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index dfb00a5b8..673923166 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -13,6 +13,7 @@ """ +import calendar import numpy as np import pandas as pd from oemof.network import energy_system as es @@ -122,3 +123,14 @@ def __init__( super().__init__( timeindex=timeindex, timeincrement=timeincrement, **kwargs ) + + +def create_year_index(year, length=1, number=None): + if number is None: + if calendar.isleap(year): + hoy = 8784 + else: + hoy = 8760 + number = hoy/length + return pd.date_range(f"1/1/{year}", periods=number+1, freq=f"{length}H") + From 03a72c935976b340507fb0ef6aafe43e9d440d28 Mon Sep 17 00:00:00 2001 From: uvchik Date: Sat, 16 Apr 2022 22:05:36 +0200 Subject: [PATCH 0205/1363] Warn for changing default value in the future --- src/oemof/solph/_energy_system.py | 19 ++++++++++++++----- src/oemof/solph/processing.py | 11 ++--------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 673923166..737c4239a 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -14,6 +14,8 @@ """ import calendar +import warnings + import numpy as np import pandas as pd from oemof.network import energy_system as es @@ -52,7 +54,7 @@ def __init__( self, timeindex=None, timeincrement=None, - infer_last_interval=True, + infer_last_interval=None, **kwargs, ): # Doing imports at runtime is generally frowned upon, but should work @@ -72,10 +74,17 @@ def __init__( ) raise TypeError(msg.format(type(timeindex))) - if infer_last_interval is True: - self.timemode = "implicit" - else: - self.timemode = "explicit" + if infer_last_interval is None: + msg = ( + "The default behaviour will change in future versions.\n" + "At the moment the last interval of an equidistant time " + "index is added implicitly by default. Set " + "'infer_last_interval' explicitly 'True' or 'False' to avoid " + "this warning. In future versions 'False' will be the default" + "behaviour" + ) + warnings.warn(msg, FutureWarning) + infer_last_interval = True if infer_last_interval is True and timeindex is not None: # Add one time interval to the timeindex by adding one time point. diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index d3bcaee71..fe456025d 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -144,7 +144,7 @@ def set_result_index(df_dict, k, result_index): ).with_traceback(sys.exc_info()[2]) -def results(model, remove_last_time_point=None): +def results(model, remove_last_time_point=False): """ Create a result dictionary from the result DataFrame. @@ -168,12 +168,6 @@ def results(model, remove_last_time_point=None): will get one row with nan-values to have the same index for all variables. """ - if remove_last_time_point is None: - if model.es.timemode == "implicit": - remove_last_time_point = True - else: - remove_last_time_point = False - df = create_dataframe(model) # create a dict of dataframes keyed by oemof tuples @@ -194,8 +188,7 @@ def results(model, remove_last_time_point=None): # dataframe dict into a series for scalar data and dataframe for sequences result = {} if remove_last_time_point is True: - # In the implicit time mode the first time point is removed. - # The values of intervals belong to the time at the end of the + # The values of intervals belong to the time at the beginning of the # interval. for k in df_dict: df_dict[k].set_index("timestep", inplace=True) From e22c2053be54facb0e7862cb74d622b639a178a3 Mon Sep 17 00:00:00 2001 From: uvchik Date: Sat, 16 Apr 2022 22:07:28 +0200 Subject: [PATCH 0206/1363] Fix style issues --- src/oemof/solph/_energy_system.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 737c4239a..1fd9caadf 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -140,6 +140,5 @@ def create_year_index(year, length=1, number=None): hoy = 8784 else: hoy = 8760 - number = hoy/length - return pd.date_range(f"1/1/{year}", periods=number+1, freq=f"{length}H") - + number = hoy / length + return pd.date_range(f"1/1/{year}", periods=number + 1, freq=f"{length}H") From a032773e92b5dcf13773b7f3bd2e412adbc08ebf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Fri, 22 Apr 2022 22:03:29 +0200 Subject: [PATCH 0207/1363] Provide more convenient imports --- src/oemof/solph/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/oemof/solph/__init__.py b/src/oemof/solph/__init__.py index ca12a91fe..0111fc9d4 100644 --- a/src/oemof/solph/__init__.py +++ b/src/oemof/solph/__init__.py @@ -13,12 +13,16 @@ from ._options import Investment from ._options import NonConvex from ._plumbing import sequence +from .buses import Bus # default Bus (for convenience) +from .flows import Flow # default Flow (for convenience) __all__ = [ "buses", + "Bus", "components", "constraints", "flows", + "Flow", "helpers", "processing", "views", From 2e7e5aa5975e2508906e4c52751dabb9db6dfe0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Tue, 26 Apr 2022 16:56:16 +0200 Subject: [PATCH 0208/1363] Require oemof.network v0.4 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7c86b3cea..61b7388f5 100644 --- a/setup.py +++ b/setup.py @@ -86,7 +86,7 @@ def read(*names, **kwargs): "pyomo >= 5.7.0, < 5.7.3", "networkx", "oemof.tools", - "oemof.network", + "oemof.network >= 0.4.0, < 0.5.0", ], extras_require={ "dev": ["pytest", "sphinx", "sphinx_rtd_theme"], From 6538a52a86fcafad00444dd39bb173252f93ef57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Tue, 26 Apr 2022 17:44:53 +0200 Subject: [PATCH 0209/1363] Ignore FutureWarnings in warning tests --- tests/test_models.py | 3 +++ tests/test_warnings.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/tests/test_models.py b/tests/test_models.py index 928cbd39f..60a0eb50e 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -103,6 +103,9 @@ def test_optimal_solution(): def test_infeasible_model(): + # FutureWarning is i.e. emitted by network Entity registry + warnings.simplefilter(action='ignore', category=FutureWarning) + with pytest.raises(ValueError, match=""): with warnings.catch_warnings(record=True) as w: es = solph.EnergySystem(timeindex=[1]) diff --git a/tests/test_warnings.py b/tests/test_warnings.py index 78c833146..dd8a7cc83 100644 --- a/tests/test_warnings.py +++ b/tests/test_warnings.py @@ -23,6 +23,9 @@ def warning_fixture(): """Explicitly activate the warnings.""" warnings.filterwarnings("always", category=SuspiciousUsageWarning) + # FutureWarning is i.e. emitted by network Entity registry + warnings.simplefilter(action='ignore', category=FutureWarning) + def test_that_the_sink_warnings_actually_get_raised(warning_fixture): """Sink doesn't warn about potentially erroneous usage.""" From 16b1fe6ac4cfb5c0a5029dd7f15463a21718205c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Tue, 26 Apr 2022 17:46:32 +0200 Subject: [PATCH 0210/1363] Adhere to Black coding style --- tests/test_models.py | 2 +- tests/test_warnings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_models.py b/tests/test_models.py index 60a0eb50e..93d1e0296 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -104,7 +104,7 @@ def test_optimal_solution(): def test_infeasible_model(): # FutureWarning is i.e. emitted by network Entity registry - warnings.simplefilter(action='ignore', category=FutureWarning) + warnings.simplefilter(action="ignore", category=FutureWarning) with pytest.raises(ValueError, match=""): with warnings.catch_warnings(record=True) as w: diff --git a/tests/test_warnings.py b/tests/test_warnings.py index dd8a7cc83..d47639415 100644 --- a/tests/test_warnings.py +++ b/tests/test_warnings.py @@ -24,7 +24,7 @@ def warning_fixture(): warnings.filterwarnings("always", category=SuspiciousUsageWarning) # FutureWarning is i.e. emitted by network Entity registry - warnings.simplefilter(action='ignore', category=FutureWarning) + warnings.simplefilter(action="ignore", category=FutureWarning) def test_that_the_sink_warnings_actually_get_raised(warning_fixture): From d6d6434f533e9a27c3764f6e2a58afb2e168685b Mon Sep 17 00:00:00 2001 From: uvchik Date: Thu, 5 May 2022 08:18:12 +0200 Subject: [PATCH 0211/1363] Add docstring for new datetime function --- src/oemof/solph/_energy_system.py | 48 ++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 1fd9caadf..45eabab5d 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -74,7 +74,7 @@ def __init__( ) raise TypeError(msg.format(type(timeindex))) - if infer_last_interval is None: + if infer_last_interval is None and timeindex is not None: msg = ( "The default behaviour will change in future versions.\n" "At the moment the last interval of an equidistant time " @@ -134,11 +134,51 @@ def __init__( ) -def create_year_index(year, length=1, number=None): +def create_year_index(year, interval=1, number=None): + """ + Create a datetime index for one year. + + Notes + ----- + To create 8760 hourly intervals for a non leap year a datetime index with + 8761 time points need to be created. So the number of time steps is always + the number of intervals plus one. + + Parameters + ---------- + year : int + The year of the index. + interval : float + The time interval in hours e.g. 0.5 for 30min or 2 for a two hour + interval (default: 1). + number : int + The number of time intervals. By default number is calculated to create + an index of one year. For a shorter or longer period the number of + intervals can be set by the user. + + Examples + -------- + >>> len(create_year_index(2014)) + 8761 + >>> len(create_year_index(2012)) # leap year + 8785 + >>> len(create_year_index(2014, interval=0.5)) + 17521 + >>> len(create_year_index(2014, interval=0.5, number=10)) + 11 + >>> len(create_year_index(2014, number=10)) + 11 + >>> str(create_year_index(2014, interval=0.5, number=10)[-1]) + '2014-01-01 05:00:00' + >>> str(create_year_index(2014, interval=2, number=10)[-1]) + '2014-01-01 20:00:00' + """ if number is None: if calendar.isleap(year): hoy = 8784 else: hoy = 8760 - number = hoy / length - return pd.date_range(f"1/1/{year}", periods=number + 1, freq=f"{length}H") + number = hoy / interval + return pd.date_range( + f"1/1/{year}", periods=number + 1, freq=f"{interval}H" + ) From 4df2c908b462332aaf617512b0ada3b5a7d1fa07 Mon Sep 17 00:00:00 2001 From: uvchik Date: Thu, 5 May 2022 08:18:44 +0200 Subject: [PATCH 0212/1363] Make new datetime function available with the main API --- src/oemof/solph/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/oemof/solph/__init__.py b/src/oemof/solph/__init__.py index ca12a91fe..c393cdc85 100644 --- a/src/oemof/solph/__init__.py +++ b/src/oemof/solph/__init__.py @@ -8,6 +8,7 @@ from . import processing from . import views from ._energy_system import EnergySystem +from ._energy_system import create_year_index from ._groupings import GROUPINGS from ._models import Model from ._options import Investment @@ -28,4 +29,5 @@ "Investment", "NonConvex", "sequence", + "create_year_index" ] From bcd44a0dfdc9c1953a0299a227cbe5acde3d5e84 Mon Sep 17 00:00:00 2001 From: uvchik Date: Thu, 5 May 2022 08:18:56 +0200 Subject: [PATCH 0213/1363] Fix reference --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 6dea87c4a..8ca5d6769 100644 --- a/README.rst +++ b/README.rst @@ -123,7 +123,7 @@ Read our `contribution `_ If you have questions regarding the use of oemof you can visit the forum at `openmod-initiative.org `_ and open a new thread if your questions hasn't been already answered. From 4d955ab1b3c3de45f6fb3d763f67ba532a02eea2 Mon Sep 17 00:00:00 2001 From: uvchik Date: Thu, 5 May 2022 08:33:27 +0200 Subject: [PATCH 0214/1363] Remove obsolete tests --- tests/test_non_equidistant_time_index.py | 31 ------------------------ 1 file changed, 31 deletions(-) diff --git a/tests/test_non_equidistant_time_index.py b/tests/test_non_equidistant_time_index.py index 8873a7f14..23ef248e6 100644 --- a/tests/test_non_equidistant_time_index.py +++ b/tests/test_non_equidistant_time_index.py @@ -226,34 +226,3 @@ def test_numeric_index(self): ) assert self.es.timeincrement[ts] == ti assert charge.isnull().any() - - def test_default_value_explicit(self): - """ - In the explicit mode the default value for the processing is to not - remove the last value and allow nan-values. The explicit mode is - triggered by setting `infer_last_interval` to `False`. - """ - assert self.es.timemode == "explicit" - model = Model(self.es) - model.receive_duals() - model.solve() - results = processing.results(model) - flow = {k: v for k, v in results.items() if k[1] is not None} - assert 73 == len([v["sequences"]["flow"] for k, v in flow.items()][0]) - - def test_default_value_implicit(self): - """ - In the implicit mode the default value for the processing is to remove - the last value and allow nan-values. - """ - assert self.es.timemode == "explicit" - test_es = EnergySystem() - assert test_es.timemode == "implicit" - my_es = copy.copy(self.es) - my_es.timemode = test_es.timemode - model = Model(my_es) - model.receive_duals() - model.solve() - results = processing.results(model) - flow = {k: v for k, v in results.items() if k[1] is not None} - assert 72 == len([v["sequences"]["flow"] for k, v in flow.items()][0]) From b5205e6c562bb96905d3340e58221636073ea654 Mon Sep 17 00:00:00 2001 From: uvchik Date: Thu, 5 May 2022 08:52:03 +0200 Subject: [PATCH 0215/1363] Remove obsolete import --- tests/test_non_equidistant_time_index.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_non_equidistant_time_index.py b/tests/test_non_equidistant_time_index.py index 23ef248e6..ce27c8092 100644 --- a/tests/test_non_equidistant_time_index.py +++ b/tests/test_non_equidistant_time_index.py @@ -6,7 +6,6 @@ SPDX-License-Identifier: MIT """ -import copy import datetime import random From 2d85b0f282f87e8e0ddbebdf4c798dc84233accb Mon Sep 17 00:00:00 2001 From: uvchik Date: Thu, 5 May 2022 08:55:42 +0200 Subject: [PATCH 0216/1363] Fix tests --- tests/test_processing.py | 19 ++++++------- .../test_solph/test_lopf/test_lopf.py | 27 +++++++++---------- .../test_piecewiselineartransformer.py | 4 ++- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/tests/test_processing.py b/tests/test_processing.py index 6c5d85d86..fc5993abb 100644 --- a/tests/test_processing.py +++ b/tests/test_processing.py @@ -313,17 +313,18 @@ def test_net_storage_flow(self): storage_flow = views.net_storage_flow( results, node_type=GenericStorage ) + compare = views.node(results, "storage", multiindex=True)["sequences"] - eq_( + + assert ( ( - ( - compare[("storage", "b_el2", "flow")] - - compare[("b_el1", "storage", "flow")] - ).to_frame() - == storage_flow.values - ).all()[0], - True, - ) + compare[("storage", "b_el2", "flow")] + - compare[("b_el1", "storage", "flow")] + ) + .to_frame() + .fillna(0) + == storage_flow.values + ).all()[0] def test_output_by_type_view_empty(self): results = processing.results(self.om) diff --git a/tests/test_scripts/test_solph/test_lopf/test_lopf.py b/tests/test_scripts/test_solph/test_lopf/test_lopf.py index b9bd70091..f3ea9f649 100644 --- a/tests/test_scripts/test_solph/test_lopf/test_lopf.py +++ b/tests/test_scripts/test_solph/test_lopf/test_lopf.py @@ -15,7 +15,6 @@ import logging import pandas as pd -from nose.tools import eq_ from oemof.solph import EnergySystem from oemof.solph import Investment @@ -114,7 +113,7 @@ def test_lopf(solver="cbc"): logging.info("Running lopf on 3-Node exmaple system") om.solve(solver=solver) - results = processing.results(om) + results = processing.results(om, remove_last_time_point=True) generators = views.node_output_by_type(results, Source) @@ -125,24 +124,24 @@ def test_lopf(solver="cbc"): for key in generators_test_results.keys(): logging.debug("Test genertor production of {0}".format(key)) - eq_( - int(round(generators[key])), - int(round(generators_test_results[key])), + assert int(round(generators[key])) == int( + round(generators_test_results[key]) ) - eq_( - results[es.groups["b_2"], es.groups["b_0"]]["sequences"]["flow"][0], - -40, + assert ( + results[es.groups["b_2"], es.groups["b_0"]]["sequences"]["flow"][0] + == -40 ) - eq_( - results[es.groups["b_1"], es.groups["b_2"]]["sequences"]["flow"][0], 60 + assert ( + results[es.groups["b_1"], es.groups["b_2"]]["sequences"]["flow"][0] + == 60 ) - eq_( - results[es.groups["b_0"], es.groups["b_1"]]["sequences"]["flow"][0], - -20, + assert ( + results[es.groups["b_0"], es.groups["b_1"]]["sequences"]["flow"][0] + == -20 ) # objective function - eq_(round(processing.meta_results(om)["objective"]), 3200) + assert round(processing.meta_results(om)["objective"]) == 3200 diff --git a/tests/test_scripts/test_solph/test_piecewiselineartransformer/test_piecewiselineartransformer.py b/tests/test_scripts/test_solph/test_piecewiselineartransformer/test_piecewiselineartransformer.py index 4ccce8197..2922a2296 100644 --- a/tests/test_scripts/test_solph/test_piecewiselineartransformer/test_piecewiselineartransformer.py +++ b/tests/test_scripts/test_solph/test_piecewiselineartransformer/test_piecewiselineartransformer.py @@ -66,7 +66,9 @@ def conv_func(x): optimization_model.solve(solver="cbc") # Get results - results = processing.results(optimization_model) + results = processing.results( + optimization_model, remove_last_time_point=True + ) 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) From bf8d4d173ee36b874957595cdb0d2081f47a5c75 Mon Sep 17 00:00:00 2001 From: uvchik Date: Thu, 5 May 2022 08:59:51 +0200 Subject: [PATCH 0217/1363] Make black happy --- src/oemof/solph/__init__.py | 2 +- tests/test_non_equidistant_time_index.py | 13 +++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/oemof/solph/__init__.py b/src/oemof/solph/__init__.py index c393cdc85..eea5d149a 100644 --- a/src/oemof/solph/__init__.py +++ b/src/oemof/solph/__init__.py @@ -29,5 +29,5 @@ "Investment", "NonConvex", "sequence", - "create_year_index" + "create_year_index", ] diff --git a/tests/test_non_equidistant_time_index.py b/tests/test_non_equidistant_time_index.py index ce27c8092..eb6e34783 100644 --- a/tests/test_non_equidistant_time_index.py +++ b/tests/test_non_equidistant_time_index.py @@ -123,14 +123,11 @@ def test_timesteps_timeincrements_with_storage_discharging(self): # Discharging - timestep (ts) with its timeincrement (ti) time = [(7, 1), (40, 0.5)] for ts, ti in time: - assert ( - round( - storage_content[ts] - - (discharge[ts] + (discharge[ts] * 1 / 9)) * ti, - 5, - ) - == round(storage_content[ts + 1], 5) - ) + assert round( + storage_content[ts] + - (discharge[ts] + (discharge[ts] * 1 / 9)) * ti, + 5, + ) == round(storage_content[ts + 1], 5) assert self.es.timeincrement[ts] == ti def test_timeincrements(self): From ffd3f6e32e5cf936a085dfa7cfd8555863d52b73 Mon Sep 17 00:00:00 2001 From: uvchik Date: Thu, 5 May 2022 09:33:21 +0200 Subject: [PATCH 0218/1363] Remove warning from example --- examples/time_step_example/time_step_example.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/time_step_example/time_step_example.py b/examples/time_step_example/time_step_example.py index da5777ea2..a9f15e1a9 100644 --- a/examples/time_step_example/time_step_example.py +++ b/examples/time_step_example/time_step_example.py @@ -36,9 +36,11 @@ solver = "cbc" # 'glpk', 'gurobi',... solver_verbose = False # show/hide solver output -date_time_index = pd.date_range("1/1/2000", periods=8, freq="15T") +date_time_index = pd.date_range("1/1/2000", periods=9, freq="15T") -energy_system = solph.EnergySystem(timeindex=date_time_index) +energy_system = solph.EnergySystem( + timeindex=date_time_index, infer_last_interval=False +) bus = solph.buses.Bus(label="bus") source = solph.components.Source( From 9f6c14cc4ad20be2b2f9825fcc74b52fb3d47e28 Mon Sep 17 00:00:00 2001 From: uvchik Date: Thu, 5 May 2022 14:11:58 +0200 Subject: [PATCH 0219/1363] Update the docs --- docs/usage.rst | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/docs/usage.rst b/docs/usage.rst index 59bf1b5c3..0cc91c8ef 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -51,13 +51,29 @@ Set up an energy system In most cases an EnergySystem object is defined when we start to build up an energy system model. The EnergySystem object will be the main container for the model. -To define an EnergySystem we need a Datetime index to define the time range and increment of our model. An easy way to this is to use the pandas time_range function. -The following code example defines the year 2011 in hourly steps. See `pandas date_range guide `_ for more information. +The model time is defined by the number of intervals and the length of intervals. The length of each interval does not have to be the same. This can be defined in two ways: + +1. Define the length of each interval in an array/Series where the number of the elements is the number of intervals. +2. Define a `pandas.DatetimeIndex` with all time steps that encloses an interval. Be aware that you have to define 8761 time steps to get 8760 intervals. + +The index will also be used for the results. For a numeric index the resulting time series will indexed with a numeric index starting with 0. + +One can use the function +:py:func:`~oemof.solph._energy_system/create_year_index` to create an equidistant datetime index. By default the function creates an hourly index for one year, so online the year has to be passed to the function. But it is also possible to change the length of the interval to quarter hours etc.. The default number of intervals is the number needed to cover the given year but the value can be overwritten by the user. + +It is also possible to define the datetime index using pandas. See `pandas date_range guide `_ for more information. + +Both code blocks will create an hourly datetime index for 2011: + +.. code-block:: python + + from oemof.solph import create_year_index + my_index = create_year_index(2011) .. code-block:: python import pandas as pd - my_index = pd.date_range('1/1/2011', periods=8760, freq='H') + my_index = pd.date_range('1/1/2011', periods=8761, freq='H') This index can be used to define the EnergySystem: From 8bf07ac95b712b5de64a5ea251ea3c2109c1fb54 Mon Sep 17 00:00:00 2001 From: uvchik Date: Thu, 5 May 2022 14:12:17 +0200 Subject: [PATCH 0220/1363] Use capital letters for DatetimeIndex --- src/oemof/solph/_energy_system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 45eabab5d..f390d2b01 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -70,7 +70,7 @@ def __init__( ): msg = ( "Parameter 'timeindex' has to be of type " - "pandas.datetimeindex or NoneType and not of type {0}" + "pandas.DatetimeIndex or NoneType and not of type {0}" ) raise TypeError(msg.format(type(timeindex))) From e3d49cee156137e56a26063dcd3886fb451e1412 Mon Sep 17 00:00:00 2001 From: uvchik Date: Fri, 6 May 2022 10:28:12 +0200 Subject: [PATCH 0221/1363] Add example with dual variables --- .../dual_variable_example.py | 287 ++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 examples/dual_variable_example/dual_variable_example.py diff --git a/examples/dual_variable_example/dual_variable_example.py b/examples/dual_variable_example/dual_variable_example.py new file mode 100644 index 000000000..a79318948 --- /dev/null +++ b/examples/dual_variable_example/dual_variable_example.py @@ -0,0 +1,287 @@ +# -*- coding: utf-8 -*- + +""" +General description +------------------- + +A basic example to show how to get the dual variables from the system. Try +to understand the plot. + + +Installation requirements +------------------------- + +This example requires the version v0.5.x of oemof. Install by: + + pip install 'oemof.solph>=0.5,<0.6' + +Optional: + + pip install matplotlib + +SPDX-FileCopyrightText: Uwe Krien + +SPDX-License-Identifier: MIT +""" + + +# **************************************************************************** +# ********** PART 1 - Define and optimise the energy system ****************** +# **************************************************************************** + +############################################################################### +# imports +############################################################################### + +import logging +import os +import pprint as pp + +import pandas as pd +from oemof.solph import EnergySystem, Model, buses +from oemof.solph import components as cmp +from oemof.solph import flows, processing, views +from oemof.tools import logger + +try: + import matplotlib.pyplot as plt +except ImportError: + plt = None + + +solver = "cbc" # 'glpk', 'gurobi',.... +debug = False # Set number_of_timesteps to 3 to get a readable lp-file. +number_of_time_steps = 48 +solver_verbose = False # show/hide solver output + +# initiate the logger (see the API docs for more information) +logger.define_logging() + +date_time_index = pd.date_range( + "1/1/2012", periods=number_of_time_steps, freq="H" +) + +energysystem = EnergySystem(timeindex=date_time_index) + +demand = [ + 209, + 207, + 200, + 191, + 185, + 180, + 172, + 170, + 171, + 179, + 189, + 201, + 208, + 207, + 205, + 206, + 217, + 232, + 237, + 232, + 224, + 219, + 223, + 213, + 201, + 192, + 187, + 184, + 184, + 182, + 180, + 191, + 207, + 222, + 231, + 238, + 241, + 237, + 234, + 235, + 242, + 264, + 265, + 260, + 245, + 238, + 241, + 231, +] +pv = [ + 0.18, + 0.11, + 0.05, + 0.05, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.05, + 0.07, + 0.11, + 0.13, + 0.15, + 0.22, + 0.28, + 0.33, + 0.25, + 0.17, + 0.09, + 0.09, + 0.07, + 0.05, + 0.05, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.09, + 0.21, + 0.33, + 0.44, + 0.54, + 0.61, + 0.65, + 0.67, + 0.64, + 0.59, + 0.52, +] + +########################################################################## +# Create oemof object +########################################################################## + +# create natural gas bus +bus_gas = buses.Bus(label="natural_gas") + +# create electricity bus +bus_elec = buses.Bus(label="electricity") + +# adding the buses to the energy system +energysystem.add(bus_gas, bus_elec) + +# create excess component for the electricity bus to allow overproduction +energysystem.add(cmp.Sink(label="excess_bel", inputs={bus_elec: flows.Flow()})) + +# create source object representing the natural gas commodity (annual limit) +energysystem.add( + cmp.Source( + label="rgas", + outputs={bus_gas: flows.Flow(variable_costs=38)}, + ) +) + +# create fixed source object representing pv power plants +energysystem.add( + cmp.Source( + label="pv", + outputs={bus_elec: flows.Flow(fix=pv, nominal_value=700)}, + ) +) + +# create simple sink object representing the electrical demand +energysystem.add( + cmp.Sink( + label="demand", + inputs={bus_elec: flows.Flow(fix=demand, nominal_value=1)}, + ) +) + +# create simple transformer object representing a gas power plant +energysystem.add( + cmp.Transformer( + label="pp_gas", + inputs={bus_gas: flows.Flow()}, + outputs={bus_elec: flows.Flow(nominal_value=400)}, + conversion_factors={bus_elec: 0.5}, + ) +) + +# create storage object representing a battery +cap = 400 +storage = cmp.GenericStorage( + nominal_storage_capacity=cap, + label="storage", + inputs={bus_elec: flows.Flow(nominal_value=cap / 6)}, + outputs={ + bus_elec: flows.Flow(nominal_value=cap / 6, variable_costs=0.001) + }, + loss_rate=0.00, + initial_storage_level=0, + inflow_conversion_factor=1, + outflow_conversion_factor=0.8, +) + +energysystem.add(storage) + +########################################################################## +# Optimise the energy system and plot the results +########################################################################## + +# initialise the operational model +model = Model(energysystem) + +model.receive_duals() + +# if tee_switch is true solver messages will be displayed +model.solve(solver=solver, solve_kwargs={"tee": solver_verbose}) + +# add results to the energy system to make it possible to store them. +results = processing.results(model) + +flows_to_bus = pd.DataFrame( + { + str(k[0].label): v["sequences"]["flow"] + for k, v in results.items() + if k[1] is not None and k[1] == bus_elec + } +) +flows_from_bus = pd.DataFrame( + { + str(k[1].label): v["sequences"]["flow"] + for k, v in results.items() + if k[1] is not None and k[0] == bus_elec + } +) + +storage = pd.DataFrame( + { + str(k[0].label): v["sequences"]["storage_content"] + for k, v in results.items() + if k[1] is None and k[0] == storage + } +) + +duals = pd.DataFrame( + { + str(k[0].label): v["sequences"]["duals"] + for k, v in results.items() + if k[1] is None and isinstance(k[0], buses.Bus) + } +) + +my_flows = pd.concat( + [flows_to_bus, flows_from_bus, storage, duals], + keys=["to_bus", "from_bus", "content", "duals"], + axis=1, +) + +my_flows.plot() +plt.show() From f9da7256c8307206518944e13b2f2d60effbdfe1 Mon Sep 17 00:00:00 2001 From: uvchik Date: Fri, 6 May 2022 10:29:08 +0200 Subject: [PATCH 0222/1363] Do not reassign attribute to avoid warning --- src/oemof/solph/_models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/oemof/solph/_models.py b/src/oemof/solph/_models.py index 6dba3de68..feeb1d146 100644 --- a/src/oemof/solph/_models.py +++ b/src/oemof/solph/_models.py @@ -199,8 +199,10 @@ def receive_duals(self): """ # shadow prices + del self.dual self.dual = po.Suffix(direction=po.Suffix.IMPORT) # reduced costs + del self.rc self.rc = po.Suffix(direction=po.Suffix.IMPORT) def results(self): From 3af73ad9986e3356096e8b40300640f70c83e4a9 Mon Sep 17 00:00:00 2001 From: uvchik Date: Fri, 6 May 2022 10:29:32 +0200 Subject: [PATCH 0223/1363] Replace deprecated attribute --- src/oemof/solph/processing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index 5e524dd33..064a44650 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -164,7 +164,7 @@ def results(om): # add dual variables for bus constraints if om.dual is not None: grouped = groupby( - sorted(om.BusBlock.balance.iterkeys()), lambda p: p[0] + sorted(om.BusBlock.balance.keys()), lambda p: p[0] ) for bus, timesteps in grouped: duals = [ From cbc53e321c90ca6463d8e6085a4ad5ba4ef9fc67 Mon Sep 17 00:00:00 2001 From: uvchik Date: Fri, 6 May 2022 10:29:57 +0200 Subject: [PATCH 0224/1363] Skip examples when executing tests --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index 88193bda7..e0240872f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -24,6 +24,7 @@ norecursedirs = dist build migrations + examples python_files = test_*.py From ccf193cd1fef83435e4bcfa0ae54add79eacd5e4 Mon Sep 17 00:00:00 2001 From: uvchik Date: Fri, 6 May 2022 15:30:16 +0200 Subject: [PATCH 0225/1363] Make black happier --- src/oemof/solph/processing.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index 064a44650..ae046c35a 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -163,9 +163,7 @@ def results(om): # add dual variables for bus constraints if om.dual is not None: - grouped = groupby( - sorted(om.BusBlock.balance.keys()), lambda p: p[0] - ) + grouped = groupby(sorted(om.BusBlock.balance.keys()), lambda p: p[0]) for bus, timesteps in grouped: duals = [ om.dual[om.BusBlock.balance[bus, t]] for _, t in timesteps From 0bec14203b97936ec14db3da2ca7ff84a56ac7e0 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 16 May 2022 10:38:10 +0200 Subject: [PATCH 0226/1363] Use general literals AND an example --- docs/usage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage.rst b/docs/usage.rst index 0cc91c8ef..27d8fad16 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -54,7 +54,7 @@ In most cases an EnergySystem object is defined when we start to build up an ene The model time is defined by the number of intervals and the length of intervals. The length of each interval does not have to be the same. This can be defined in two ways: 1. Define the length of each interval in an array/Series where the number of the elements is the number of intervals. -2. Define a `pandas.DatetimeIndex` with all time steps that encloses an interval. Be aware that you have to define 8761 time steps to get 8760 intervals. +2. Define a `pandas.DatetimeIndex` with all time steps that encloses an interval. Be aware that you have to define n+1 time points to get n intervals. For non-leap year with hourly values that means 8761 time points to get 8760 interval e.g. 2018-01-01 00:00 to 2019-01-01 00:00. The index will also be used for the results. For a numeric index the resulting time series will indexed with a numeric index starting with 0. From 62b891740a4a31a121ec5584e03d82876eaffa2e Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 16 May 2022 11:02:30 +0200 Subject: [PATCH 0227/1363] Rename new time index function --- src/oemof/solph/_energy_system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index f390d2b01..616cf9be4 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -134,7 +134,7 @@ def __init__( ) -def create_year_index(year, interval=1, number=None): +def create_time_index(year, interval=1, number=None): """ Create a datetime index for one year. From 7d916f340c9ac46ba3a1af77250b9ef74660b148 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 16 May 2022 11:03:17 +0200 Subject: [PATCH 0228/1363] Round result of division to avoid truncation --- src/oemof/solph/_energy_system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index 616cf9be4..e60f69d7b 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -178,7 +178,7 @@ def create_time_index(year, interval=1, number=None): hoy = 8784 else: hoy = 8760 - number = hoy / interval + number = round(hoy / interval) return pd.date_range( f"1/1/{year}", periods=number + 1, freq=f"{interval}H" ) From 66847f208c2b30afe0c228263ba3f7870d8425f7 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 16 May 2022 11:05:59 +0200 Subject: [PATCH 0229/1363] Fix init import after renaming a function --- src/oemof/solph/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/solph/__init__.py b/src/oemof/solph/__init__.py index eea5d149a..7586369d1 100644 --- a/src/oemof/solph/__init__.py +++ b/src/oemof/solph/__init__.py @@ -8,7 +8,7 @@ from . import processing from . import views from ._energy_system import EnergySystem -from ._energy_system import create_year_index +from ._energy_system import create_time_index from ._groupings import GROUPINGS from ._models import Model from ._options import Investment From 73a284aec3d0f32f57b95113dd5fc1889cc4f16a Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 16 May 2022 11:10:36 +0200 Subject: [PATCH 0230/1363] Fix docstring of new function after renaming it --- src/oemof/solph/__init__.py | 2 +- src/oemof/solph/_energy_system.py | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/oemof/solph/__init__.py b/src/oemof/solph/__init__.py index 7586369d1..a10a7127c 100644 --- a/src/oemof/solph/__init__.py +++ b/src/oemof/solph/__init__.py @@ -29,5 +29,5 @@ "Investment", "NonConvex", "sequence", - "create_year_index", + "create_time_index", ] diff --git a/src/oemof/solph/_energy_system.py b/src/oemof/solph/_energy_system.py index e60f69d7b..8a3ea5bd5 100644 --- a/src/oemof/solph/_energy_system.py +++ b/src/oemof/solph/_energy_system.py @@ -158,19 +158,19 @@ def create_time_index(year, interval=1, number=None): Examples -------- - >>> len(create_year_index(2014)) + >>> len(create_time_index(2014)) 8761 - >>> len(create_year_index(2012)) # leap year + >>> len(create_time_index(2012)) # leap year 8785 - >>> len(create_year_index(2014, interval=0.5)) + >>> len(create_time_index(2014, interval=0.5)) 17521 - >>> len(create_year_index(2014, interval=0.5, number=10)) + >>> len(create_time_index(2014, interval=0.5, number=10)) 11 - >>> len(create_year_index(2014, number=10)) + >>> len(create_time_index(2014, number=10)) 11 - >>> str(create_year_index(2014, interval=0.5, number=10)[-1]) + >>> str(create_time_index(2014, interval=0.5, number=10)[-1]) '2014-01-01 05:00:00' - >>> str(create_year_index(2014, interval=2, number=10)[-1]) + >>> str(create_time_index(2014, interval=2, number=10)[-1]) '2014-01-01 20:00:00' """ if number is None: From 885264a6df62dbac1582ea507877325e8187dd90 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 16 May 2022 11:22:12 +0200 Subject: [PATCH 0231/1363] Bring order of import in line --- src/oemof/solph/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/solph/__init__.py b/src/oemof/solph/__init__.py index a10a7127c..13cfd55e1 100644 --- a/src/oemof/solph/__init__.py +++ b/src/oemof/solph/__init__.py @@ -24,10 +24,10 @@ "processing", "views", "EnergySystem", + "create_time_index", "GROUPINGS", "Model", "Investment", "NonConvex", "sequence", - "create_time_index", ] From 24217c92d6c54c15f3033c01f6ec6369c527bc40 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 16 May 2022 11:39:57 +0200 Subject: [PATCH 0232/1363] Fix attribute that has been renamed with Pyomo 6.4.1 Adding try/except it will work for the newest version but also for older ones. --- src/oemof/solph/processing.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index fe456025d..2b72fcb0a 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -86,7 +86,13 @@ def create_dataframe(om): # Drop the auxiliary variables introduced by pyomo's Piecewise parent_component = bv.parent_block().parent_component() if not isinstance(parent_component, IndexedPiecewise): - for i in getattr(bv, "_index"): + try: + idx_set = getattr(bv, "_index_set") + except AttributeError: + # To make it compatible with Pyomo < 6.4.1 + idx_set = getattr(bv, "_index") + + for i in idx_set: key = (str(bv).split(".")[0], str(bv).split(".")[-1], i) value = bv[i].value var_dict[key] = value From d470ac249491db0436e5e4f7c9d1adb29cb4fb8a Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 16 May 2022 14:16:32 +0200 Subject: [PATCH 0233/1363] Add more information to bus docstring --- src/oemof/solph/buses/_bus.py | 39 +++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/oemof/solph/buses/_bus.py b/src/oemof/solph/buses/_bus.py index 495925e92..3bbc3e626 100644 --- a/src/oemof/solph/buses/_bus.py +++ b/src/oemof/solph/buses/_bus.py @@ -46,15 +46,36 @@ def constraint_group(self): class BusBlock(ScalarBlock): r"""Block for all balanced buses. - **The following constraints are build:** - - BusBlock balance :attr:`om.BusBlock.balance[i, o, t]` - .. math:: - \sum_{i \in INPUTS(n)} flow(i, n, t) = - \sum_{o \in OUTPUTS(n)} flow(n, o, t), \\ - \forall n \in \textrm{BUSES}, - \forall t \in \textrm{TIMESTEPS}. - """ + The sum of all inputs of a Bus object must equal the sum of all outputs + within one time step. + + **The following constraints are build:** + + Bus balance: `om.Bus.balance[i, o, t]` + .. math:: + \sum_{i \in INPUTS(n)} P_{i}(t) = + \sum_{o \in OUTPUTS(n)} P_{o}(t), \\ + \forall t \in \textrm{TIMESTEPS}, \\ + \forall i \in \textrm{INPUTS}, \\ + \forall o \in \textrm{OUTPUTS} + + While INPUTS is the set of Component objects connected with the input of + the Bus object and OUPUTS the set of Component objects connected with the + output of the Bus object. + + The index :math:`n` is the index for the Bus node itself. Therefore, + a :math:`flow[i, n, t]` is a flow from the Component i to the Bus n at + time step t. + + ====================== ============================ ==================== + symbol attribute explanation + ====================== ============================ ==================== + :math:`P_{i}(t)` `flow[i, n, t]` Bus, inflow + + :math:`P_{o}(t)` `flow[n, o, t]` Bus, outflow + + ====================== ============================ ==================== + """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) From a75ac94b8e0a293749ab53e83b070bdc4fe43856 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 16 May 2022 14:28:18 +0200 Subject: [PATCH 0234/1363] Revise docstring of Flow-Block --- src/oemof/solph/flows/_flow.py | 175 ++++++++++++++++++++++++--------- 1 file changed, 131 insertions(+), 44 deletions(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index cab05db48..59995094d 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -275,9 +275,127 @@ def __init__(self, **kwargs): class FlowBlock(ScalarBlock): - r""" FlowBlock block with definitions for standard flows. + r""" Flow block with definitions for standard flows. - **The following variables are created**: + For standard flows the attributes `investment` and `nonconvex` are None. + + See :class:`~oemof.solph.network.Flow` for all parameters of the *Flow* + class. + + **Variables** + + All *Flow* objects are indexed by a starting and ending node + :math:`(i, o)`, which is omitted in the following for the sake of + convenience. The creation of some variables depend on the values of + *Flow* attributes. The following variables are created: + + * :math:`P(t)` + Actual flow value (created in :class:`oemof.solph.models.BaseModel`). + The variable is bound to :math:`f_{min}(t) \ge P(t) \le f_{max}(t)`. + + If `Flow.fix` is not None the variable is bound to + :math:`P(t) = f_{fix}`. + + * :math:`ve_n` (`Flow.negative_gradient` is not `None`) + Difference of a flow in consecutive timesteps if flow is reduced. The + variable is not bound. + + * :math:`ve_p` (`Flow.positive_gradient` is not `None`) + Difference of a flow in consecutive timesteps if flow is increased. The + variable is not bound. + + The following variable is build for Flows with the attribute + `integer_flows` being not None. + + **Constraints** + + The following constraints are created, if the appropriate attribute of the + *Flow* (see :class:`oemof.solph.network.Flow`) object is set: + + * `Flow.summed_max` is not `None` (`om.Flow.summed_max[i, o]`): + .. math:: + \sum_t P(t) \cdot \tau \leq F_{max} \cdot P_{nom} + + * `Flow.summed_min` is not `None` (`om.Flow.summed_min[i, o]`): + .. math:: + \sum_t P(t) \cdot \tau \geq F_{min} \cdot P_{nom} + + + * `Flow.negative_gradient` is not `None` (`om.Flow.negative_gradient_constr[i, o]`): + .. math:: + P(t-1) - P(t) \geq ve_n(t) + + * `Flow.positive_gradient` is not `None` (`om.Flow.positive_gradient_constr[i, o]`): + .. math:: + P(t) - P(t-1) \geq ve_p(t) + + **Objective function** + + Depending on the attributes of the `Flow` object the following parts of + the objective function are created: + + * `Flow.variable_costs` is not `None`: + .. math:: + \sum_{(i,o)} \sum_t flow(i, o, t) \cdot variable\_costs(i, o, t) + + .. csv-table:: List of Variables + :header: "symbol", "attribute", "explanation" + :widths: 1, 1, 1 + + ":math:`P(t)`", ":command:`flow[i, o][t]`", "Actual flow value" + + ":math:`ve_n`", ":command:`negative_gradient[n, o, t]`", "Invested flow + capacity" + ":math:`ve_p`", ":command:`positive_gradient[n, o, t]`", "Binary status + of investment" + + + + .. csv-table:: List of Parameters + :header: "symbol", "attribute", "explanation" + :widths: 1, 1, 1 + + ":math:`F_{max}`",":command:`flow[i, o].summed_max`", "Maximal full + load time" + ":math:`F_{min}`",":command:`flow[i, o].summed_max`", "Minimal full + load time" + + ":math:`P_{invest,min}`", ":py:obj:`flows[i, o].investment.minimum`", " + Minimum investment capacity" + ":math:`P_{invest,max}`", ":py:obj:`flows[i, o].investment.maximum`", " + Maximum investment capacity" + ":math:`c_{invest,var}`", ":py:obj:`flows[i, o].investment.ep_costs` + ", "Variable investment costs" + ":math:`c_{invest,fix}`", ":py:obj:`flows[i, o].investment.offset`", " + Fix investment costs" + ":math:`f_{actual}`", ":py:obj:`flows[i, o].fix[t]`", "Normed + fixed value for the flow variable" + ":math:`f_{max}`", ":py:obj:`flows[i, o].max[t]`", "Normed maximum + value of the flow" + ":math:`f_{min}`", ":py:obj:`flows[i, o].min[t]`", "Normed minimum + value of the flow" + ":math:`f_{sum,max}`", ":py:obj:`flows[i, o].summed_max`", "Specific + maximum of summed flow values (per installed capacity)" + ":math:`f_{sum,min}`", ":py:obj:`flows[i, o].summed_min`", "Specific + minimum of summed flow values (per installed capacity)" + ":math:`\tau(t)`", ":py:obj:`timeincrement[t]`", "Time step width for + each time step" + + Note + ---- + In case of a nonconvex investment flow (:attr:`nonconvex=True`), + the existing flow capacity :math:`P_{exist}` needs to be zero. + At least, it is not tested yet, whether this works out, or makes any sense + at all. + + Note + ---- + See also :class:`oemof.solph.network.Flow`, + :class:`oemof.solph.blocks.Flow` and + :class:`oemof.solph.options.Investment` + + **The following variables are created**: (-> see basic constraints at + :class:`.Model` ) negative_gradient : Difference of a flow in consecutive timesteps if flow is reduced @@ -287,52 +405,21 @@ class FlowBlock(ScalarBlock): Difference of a flow in consecutive timesteps if flow is increased indexed by NEGATIVE_GRADIENT_FLOWS, TIMESTEPS. - **The following sets are created:** (-> see basic sets at :class:`.Model` ) - - FULL_LOAD_TIME_MAX_FLOWS - A set of flows with the attribute :attr:`full_load_time_max` being not - None. - FULL_LOAD_TIME_MIN_FLOWS - A set of flows with the attribute :attr:`full_load_time_min` being not - None. - NEGATIVE_GRADIENT_FLOWS - A set of flows with the attribute :attr:`negative_gradient` being not - None. - POSITIVE_GRADIENT_FLOWS - A set of flows with the attribute :attr:`positive_gradient` being not - None + The following variable is build for Flows with the attribute + `integer_flows` being not None. + + + + **The following sets are created:** (-> see basic sets at + :class:`~_models.Model` ) + INTEGER_FLOWS A set of flows where the attribute :attr:`integer` is True (forces flow to only take integer values) **The following constraints are build:** - FlowBlock max sum :attr:`om.FlowBlock.full_load_time_max[i, o]` - .. math:: - \sum_t flow(i, o, t) \cdot \tau - \leq full\_load\_time\_max(i, o) \cdot nominal\_value(i, o), \\ - \forall (i, o) \in \textrm{FULL\_LOAD\_TIME\_MAX\_FLOWS}. - - FlowBlock min sum :attr:`om.FlowBlock.full_load_time_min[i, o]` - .. math:: - \sum_t flow(i, o, t) \cdot \tau - \geq full\_load\_time\_min(i, o) \cdot nominal\_value(i, o), \\ - \forall (i, o) \in \textrm{FULL\_LOAD\_TIME\_MIN\_FLOWS}. - Negative gradient constraint - :attr:`om.FlowBlock.negative_gradient_constr[i, o]`: - .. math:: - flow(i, o, t-1) - flow(i, o, t) \geq \ - negative\_gradient(i, o, t), \\ - \forall (i, o) \in \textrm{NEGATIVE\_GRADIENT\_FLOWS}, \\ - \forall t \in \textrm{TIMESTEPS}. - - Positive gradient constraint - :attr:`om.FlowBlock.positive_gradient_constr[i, o]`: - .. math:: flow(i, o, t) - flow(i, o, t-1) \geq \ - positive\__gradient(i, o, t), \\ - \forall (i, o) \in \textrm{POSITIVE\_GRADIENT\_FLOWS}, \\ - \forall t \in \textrm{TIMESTEPS}. **The following parts of the objective function are created:** @@ -340,10 +427,10 @@ class FlowBlock(ScalarBlock): .. math:: \sum_{(i,o)} \sum_t flow(i, o, t) \cdot variable\_costs(i, o, t) - The expression can be accessed by :attr:`om.FlowBlock.variable_costs` and - their value after optimization by :meth:`om.FlowBlock.variable_costs()` . + The expression can be accessed by `om.Flow.variable_costs` and + their value after optimization by `om.Flow.variable_costs()` . - """ + """ # noqa: E501 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) From 7896a1434a0cac2053927921d009426f1880de25 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 16 May 2022 14:30:48 +0200 Subject: [PATCH 0235/1363] Fix references in docstring --- src/oemof/solph/flows/_flow.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 59995094d..a6392e2e5 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -109,12 +109,12 @@ class Flow(on.Edge): Notes ----- The following sets, variables, constraints and objective parts are created - * :py:class:`~oemof.solph..flows.flow.FlowBlock` - * :py:class:`~oemof.solph..flows.investment_flow.InvestmentFlowBlock` + * :py:class:`~oemof.solph.flows._flow.FlowBlock` + * :py:class:`~oemof.solph.flows._investment_flow.InvestmentFlowBlock` (additionally if Investment object is present) - * :py:class:`~oemof.solph..flows.non_convex_flow.NonConvexFlowBlock` + * :py:class:`~oemof.solph.flows._non_convex_flow.NonConvexFlowBlock` (If nonconvex object is present, CAUTION: replaces - :py:class:`~oemof.solph.flows.flow.FlowBlock` + :py:class:`~oemof.solph.flows._flow.FlowBlock` class and a MILP will be build) Examples From 83d3a22d060a8c111f69362bf63fb6370af10e7a Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 16 May 2022 14:38:04 +0200 Subject: [PATCH 0236/1363] Fix refernece link --- docs/reference/oemof.solph.rst | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/docs/reference/oemof.solph.rst b/docs/reference/oemof.solph.rst index c7bbf159a..076ade2fb 100644 --- a/docs/reference/oemof.solph.rst +++ b/docs/reference/oemof.solph.rst @@ -25,25 +25,20 @@ oemof.solph.buses.Bus :undoc-members: :show-inheritance: -oemof.solph.flows.Flow ----------------- - -.. automodule:: oemof.solph.network.flow - :members: - :undoc-members: - :show-inheritance: +oemof.solph.flows +----------------- -.. automodule:: oemof.solph.blocks.flow +.. automodule:: oemof.solph.flows._flow :members: :undoc-members: :show-inheritance: -.. automodule:: oemof.solph.blocks.investment_flow +.. automodule:: oemof.solph.flows._investment_flow :members: :undoc-members: :show-inheritance: -.. automodule:: oemof.solph.blocks.non_convex_flow +.. automodule:: oemof.solph.blocks._non_convex_flow :members: :undoc-members: :show-inheritance: From bd60e530c546c2d19a25c8c8262dd349df18acc8 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 16 May 2022 16:52:05 +0200 Subject: [PATCH 0237/1363] Revise flow docstring --- docs/reference/oemof.solph.rst | 2 +- src/oemof/solph/flows/_flow.py | 164 +++++++++------------------------ 2 files changed, 43 insertions(+), 123 deletions(-) diff --git a/docs/reference/oemof.solph.rst b/docs/reference/oemof.solph.rst index 076ade2fb..7664ae560 100644 --- a/docs/reference/oemof.solph.rst +++ b/docs/reference/oemof.solph.rst @@ -179,7 +179,7 @@ oemof.solph.helpers module oemof.solph.models module ------------------------- -.. automodule:: oemof.solph.models +.. automodule:: oemof.solph._models :members: :undoc-members: :show-inheritance: diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index a6392e2e5..231c7de0c 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -49,29 +49,23 @@ class Flow(on.Edge): calculated by multiplying :attr:`nominal_value` with :attr:`max` min : numeric (iterable or scalar), :math:`f_{min}` Normed minimum value of the flow (see :attr:`max`). - fix : numeric (iterable or scalar), :math:`f_{actual}` + fix : numeric (iterable or scalar), :math:`f_{fix}` Normed fixed value for the flow variable. Will be multiplied with the - :attr:`nominal_value` to get the absolute value. If :attr:`fixed` is - set to :obj:`True` the flow variable will be fixed to `fix - * nominal_value`, i.e. this value is set exogenous. - positive_gradient : :obj:`dict`, default: `{'ub': None, 'costs': 0}` - A dictionary containing the following two keys: + :attr:`nominal_value` to get the absolute value. + positive_gradient : :obj:`dict`, default: `{'ub': None}` + A dictionary containing the following key: * `'ub'`: numeric (iterable, scalar or None), the normed *upper bound* on the positive difference (`flow[t-1] < flow[t]`) of two consecutive flow values. - * `'costs``: numeric (scalar or None), the gradient cost per - unit. - negative_gradient : :obj:`dict`, default: `{'ub': None, 'costs': 0}` + negative_gradient : :obj:`dict`, default: `{'ub': None}` - A dictionary containing the following two keys: + A dictionary containing the following key: * `'ub'`: numeric (iterable, scalar or None), the normed *upper bound* on the negative difference (`flow[t-1] > flow[t]`) of two consecutive flow values. - * `'costs``: numeric (scalar or None), the gradient cost per - unit. full_load_time_max : numeric, :math:`t_{full\_load,max}` Upper bound on the summed flow expressed as the equivalent time that @@ -91,31 +85,13 @@ class Flow(on.Edge): Boolean value indicating if a flow is fixed during the optimization problem to its ex-ante set value. Used in combination with the :attr:`fix`. - investment : :class:`Investment ` - Object indicating if a nominal_value of the flow is determined by - the optimization problem. Note: This will refer all attributes to an - investment variable instead of to the nominal_value. The nominal_value - should not be set (or set to None) if an investment object is used. - nonconvex : :class:`NonConvex ` - If a nonconvex flow object is added here, the flow constraints will - be altered significantly as the mathematical model for the flow - will be different, i.e. constraint etc. from - :class:`NonConvexFlowBlock ` - will be used instead of - :class:`FlowBlock `. - Note: at the moment this does not work if the investment attribute is - set . + integer : boolean + Set True to bound the flow values to integers. Notes ----- - The following sets, variables, constraints and objective parts are created - * :py:class:`~oemof.solph.flows._flow.FlowBlock` - * :py:class:`~oemof.solph.flows._investment_flow.InvestmentFlowBlock` - (additionally if Investment object is present) - * :py:class:`~oemof.solph.flows._non_convex_flow.NonConvexFlowBlock` - (If nonconvex object is present, CAUTION: replaces - :py:class:`~oemof.solph.flows._flow.FlowBlock` - class and a MILP will be build) + See :py:class:`~oemof.solph.flows._flow.FlowBlock` for the variables, + constraints and objective parts, that are created for a FLow object. Examples -------- @@ -277,10 +253,7 @@ def __init__(self, **kwargs): class FlowBlock(ScalarBlock): r""" Flow block with definitions for standard flows. - For standard flows the attributes `investment` and `nonconvex` are None. - - See :class:`~oemof.solph.network.Flow` for all parameters of the *Flow* - class. + See :class:`~oemof.solph.flows._flow.Flow` class for all parameters of the *Flow*. **Variables** @@ -290,45 +263,53 @@ class FlowBlock(ScalarBlock): *Flow* attributes. The following variables are created: * :math:`P(t)` - Actual flow value (created in :class:`oemof.solph.models.BaseModel`). - The variable is bound to :math:`f_{min}(t) \ge P(t) \le f_{max}(t)`. + Actual flow value (created in :class:`~oemof.solph._models.Model`). + The variable is bound to: :math:`f_{min}(t) \cdot P_{nom} \ge P(t) \le f_{max}(t) \cdot P_{nom}`. If `Flow.fix` is not None the variable is bound to :math:`P(t) = f_{fix}`. * :math:`ve_n` (`Flow.negative_gradient` is not `None`) Difference of a flow in consecutive timesteps if flow is reduced. The - variable is not bound. + variable is bound to: :math:`0 \ge ve_n \ge ve_n^{max}`. * :math:`ve_p` (`Flow.positive_gradient` is not `None`) Difference of a flow in consecutive timesteps if flow is increased. The - variable is not bound. + variable is bound to: :math:`0 \ge ve_p \ge ve_p^{max}`. The following variable is build for Flows with the attribute `integer_flows` being not None. + * :math:`i`(`Flow.integer` is `True`) + All flow values are integers. Variable is bound to non-negative + integers. + **Constraints** The following constraints are created, if the appropriate attribute of the *Flow* (see :class:`oemof.solph.network.Flow`) object is set: - * `Flow.summed_max` is not `None` (`om.Flow.summed_max[i, o]`): + * `Flow.full_load_time_max` is not `None` (full_load_time_max_constr): .. math:: \sum_t P(t) \cdot \tau \leq F_{max} \cdot P_{nom} - * `Flow.summed_min` is not `None` (`om.Flow.summed_min[i, o]`): + * `Flow.full_load_time_min` is not `None` (full_load_time_min_constr): .. math:: \sum_t P(t) \cdot \tau \geq F_{min} \cdot P_{nom} - * `Flow.negative_gradient` is not `None` (`om.Flow.negative_gradient_constr[i, o]`): + * `Flow.negative_gradient` is not `None` (negative_gradient_constr): .. math:: P(t-1) - P(t) \geq ve_n(t) - * `Flow.positive_gradient` is not `None` (`om.Flow.positive_gradient_constr[i, o]`): + * `Flow.positive_gradient` is not `None` (positive_gradient_constr): .. math:: P(t) - P(t-1) \geq ve_p(t) + * `Flow.integer` is `True` + .. math:: + P(t) = i(t) + **Objective function** Depending on the attributes of the `Flow` object the following parts of @@ -336,99 +317,38 @@ class FlowBlock(ScalarBlock): * `Flow.variable_costs` is not `None`: .. math:: - \sum_{(i,o)} \sum_t flow(i, o, t) \cdot variable\_costs(i, o, t) + \sum_{(i,o)} \sum_t P(t) \cdot c_{var}(i, o, t) .. csv-table:: List of Variables :header: "symbol", "attribute", "explanation" :widths: 1, 1, 1 ":math:`P(t)`", ":command:`flow[i, o][t]`", "Actual flow value" - - ":math:`ve_n`", ":command:`negative_gradient[n, o, t]`", "Invested flow - capacity" - ":math:`ve_p`", ":command:`positive_gradient[n, o, t]`", "Binary status - of investment" - + ":math:`ve_n`", ":command:`negative_gradient[n, o, t]`", "Negative gradient of the flow" + ":math:`ve_p`", ":command:`positive_gradient[n, o, t]`", "Positive gradient of the flow" + ":math:`i`", ":command:`integer_flow[i, o, t]`","Integer flow" .. csv-table:: List of Parameters :header: "symbol", "attribute", "explanation" :widths: 1, 1, 1 - ":math:`F_{max}`",":command:`flow[i, o].summed_max`", "Maximal full + ":math:`P_{nom}`", ":command:`flows[i, o].nominal_value`","Nominal value of the flow" + ":math:`F_{max}`",":command:`flow[i, o].full_load_time_max`", "Maximal full load time" - ":math:`F_{min}`",":command:`flow[i, o].summed_max`", "Minimal full + ":math:`F_{min}`",":command:`flow[i, o].full_load_time_min`", "Minimal full load time" - - ":math:`P_{invest,min}`", ":py:obj:`flows[i, o].investment.minimum`", " - Minimum investment capacity" - ":math:`P_{invest,max}`", ":py:obj:`flows[i, o].investment.maximum`", " - Maximum investment capacity" - ":math:`c_{invest,var}`", ":py:obj:`flows[i, o].investment.ep_costs` - ", "Variable investment costs" - ":math:`c_{invest,fix}`", ":py:obj:`flows[i, o].investment.offset`", " - Fix investment costs" - ":math:`f_{actual}`", ":py:obj:`flows[i, o].fix[t]`", "Normed - fixed value for the flow variable" - ":math:`f_{max}`", ":py:obj:`flows[i, o].max[t]`", "Normed maximum - value of the flow" - ":math:`f_{min}`", ":py:obj:`flows[i, o].min[t]`", "Normed minimum - value of the flow" - ":math:`f_{sum,max}`", ":py:obj:`flows[i, o].summed_max`", "Specific - maximum of summed flow values (per installed capacity)" - ":math:`f_{sum,min}`", ":py:obj:`flows[i, o].summed_min`", "Specific - minimum of summed flow values (per installed capacity)" - ":math:`\tau(t)`", ":py:obj:`timeincrement[t]`", "Time step width for - each time step" + ":math:`c_{var}`", ":command:`variable\_costs[t]`", "Variable cost of the flow" + ":math:`f_{max}`", ":command:`flows[i, o].max[t]`", "Normed maximum value of the flow, the absolute maximum is :math:`f_{max} \cdot P_{nom}`" + ":math:`f_{min}`", ":command:`flows[i, o].min[t]`", "Normed minimum value of the flow, the absolute minimum is :math:`f_{min} \cdot P_{nom}`" + ":math:`f_{fix}`", ":command:`flows[i, o].min[t]`", "Normed fixed value of the flow, the absolute fixed value is :math:`f_{fix} \cdot P_{nom}`" + ":math:`ve_n^{max}`",":command:`flows[i, o].negative_gradient`","Normed maximal negative gradient of the flow, the absolute maximum gradient is :math:`ve_n^{max} \cdot P_{nom}`" + ":math:`ve_p^{max}`",":command:`flows[i, o].positive_gradient`","Normed maximal positive gradient of the flow, the absolute maximum gradient is :math:`ve_n^{max} \cdot P_{nom}`" Note ---- - In case of a nonconvex investment flow (:attr:`nonconvex=True`), - the existing flow capacity :math:`P_{exist}` needs to be zero. - At least, it is not tested yet, whether this works out, or makes any sense - at all. - - Note - ---- - See also :class:`oemof.solph.network.Flow`, - :class:`oemof.solph.blocks.Flow` and - :class:`oemof.solph.options.Investment` - - **The following variables are created**: (-> see basic constraints at - :class:`.Model` ) - - negative_gradient : - Difference of a flow in consecutive timesteps if flow is reduced - indexed by NEGATIVE_GRADIENT_FLOWS, TIMESTEPS. - - positive_gradient : - Difference of a flow in consecutive timesteps if flow is increased - indexed by NEGATIVE_GRADIENT_FLOWS, TIMESTEPS. - - The following variable is build for Flows with the attribute - `integer_flows` being not None. - - - - **The following sets are created:** (-> see basic sets at - :class:`~_models.Model` ) - - INTEGER_FLOWS - A set of flows where the attribute :attr:`integer` is True (forces flow - to only take integer values) - - **The following constraints are build:** - - - - **The following parts of the objective function are created:** - - If :attr:`variable_costs` are set by the user: - .. math:: - \sum_{(i,o)} \sum_t flow(i, o, t) \cdot variable\_costs(i, o, t) - - The expression can be accessed by `om.Flow.variable_costs` and - their value after optimization by `om.Flow.variable_costs()` . + See the :class:`~oemof.solph.flows._flow.Flow` class for the definition of + all parameters from the "List of Parameters above. """ # noqa: E501 From 15cc90b5a585a6110873fe7b82ad60f34e083886 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 16 May 2022 16:57:49 +0200 Subject: [PATCH 0238/1363] Make Black happy --- src/oemof/solph/flows/_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 231c7de0c..35373b373 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -251,7 +251,7 @@ def __init__(self, **kwargs): class FlowBlock(ScalarBlock): - r""" Flow block with definitions for standard flows. + r"""Flow block with definitions for standard flows. See :class:`~oemof.solph.flows._flow.Flow` class for all parameters of the *Flow*. From 946da141fa4a7cee776a1df1db4f6b5b21bda77f Mon Sep 17 00:00:00 2001 From: uvchik Date: Wed, 18 May 2022 10:46:49 +0200 Subject: [PATCH 0239/1363] Add information to bus network class --- src/oemof/solph/buses/_bus.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/oemof/solph/buses/_bus.py b/src/oemof/solph/buses/_bus.py index 3bbc3e626..b61c60ac2 100644 --- a/src/oemof/solph/buses/_bus.py +++ b/src/oemof/solph/buses/_bus.py @@ -25,6 +25,9 @@ class Bus(on.Bus): """A balance object. Every node has to be connected to BusBlock. + The sum of all inputs of a Bus object must equal the sum of all outputs + within one time step. + Notes ----- The following sets, variables, constraints and objective parts are created From aa104d3279a13305375009aa9dc29589c709ce51 Mon Sep 17 00:00:00 2001 From: uvchik Date: Wed, 18 May 2022 10:50:11 +0200 Subject: [PATCH 0240/1363] Revise docstringof Transformer block --- src/oemof/solph/components/_transformer.py | 52 ++++++++++++---------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/oemof/solph/components/_transformer.py b/src/oemof/solph/components/_transformer.py index d0f047835..f31cf9f83 100644 --- a/src/oemof/solph/components/_transformer.py +++ b/src/oemof/solph/components/_transformer.py @@ -100,40 +100,44 @@ def constraint_group(self): class TransformerBlock(ScalarBlock): - r"""Block for the linear relation of nodes with type - :class:`~oemof.solph.components._transformer.TransformerBlock` - - **The following sets are created:** (-> see basic sets at - :class:`.Model` ) - - TRANSFORMERS - A set with all - :class:`~oemof.solph.components._transformer.Transformer` objects. + r""" + Block for the linear relation of nodes with type + :class:`~oemof.solph.network.transformer.Transformer` **The following constraints are created:** - Linear relation :attr:`om.TransformerBlock.relation[i,o,t]` + Linear relation `om.Transformer.relation[i,o,t]` .. math:: - \P_{i,n}(t) \times \eta_{n,o}(t) = \ - \P_{n,o}(t) \times \eta_{n,i}(t), \\ + P_{i}(t) \cdot \eta_{o}(t) = + P_{o}(t) \cdot \eta_{i}(t), \\ \forall t \in \textrm{TIMESTEPS}, \\ - \forall n \in \textrm{TRANSFORMERS}, \\ - \forall i \in \textrm{INPUTS(n)}, \\ - \forall o \in \textrm{OUTPUTS(n)}, + \forall i \in \textrm{INPUTS}, \\ + \forall o \in \textrm{OUTPUTS} + + While INPUTS is the set of Bus objects connected with the input of the + Transformer and OUPUTS the set of Bus objects connected with the output of + the Transformer. The constraint above will be created for all combinations + of INPUTS and OUTPUTS for all TIMESTEPS. A Transformer with two inflows and + two outflows for one day with an hourly resolution will lead to 96 + constraints. - ====================== ============================ ============= + The index :math: n is the index for the Transformer node itself. Therefore, + a `flow[i, n, t]` is a flow from the Bus i to the Transformer n at + time step t. + + ====================== ============================ ==================== symbol attribute explanation - ====================== ============================ ============= - :math:`P_{i,n}(t)` `flow[i, n, t]` TransformerBlock - inflow + ====================== ============================ ==================== + :math:`P_{i}(t)` `flow[i, n, t]` Transformer, inflow + + :math:`P_{o}(t)` `flow[n, o, t]` Transformer, outflow + + :math:`\eta_{i}(t)` `conversion_factor[i, n, t]` Inflow, efficiency - :math:`P_{n,o}(t)` `flow[n, o, t]` TransformerBlock - outflow + :math:`\eta_{o}(t)` `conversion_factor[n, o, t]` Outflow, efficiency - :math:`\eta_{i,n}(t)` `conversion_factor[i, n, t]` Conversion - efficiency + ====================== ============================ ==================== - ====================== ============================ ============= """ def __init__(self, *args, **kwargs): From 70247aeb8fd9177052c0ef60a480f06ce79ca9c6 Mon Sep 17 00:00:00 2001 From: uvchik Date: Wed, 18 May 2022 11:03:40 +0200 Subject: [PATCH 0241/1363] Adapt docstring of network class --- src/oemof/solph/components/_transformer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/solph/components/_transformer.py b/src/oemof/solph/components/_transformer.py index f31cf9f83..97cdac8ce 100644 --- a/src/oemof/solph/components/_transformer.py +++ b/src/oemof/solph/components/_transformer.py @@ -28,7 +28,7 @@ class Transformer(on.Transformer): - """A linear TransformerBlock object with n inputs and n outputs. + """A linear converter object with n inputs and n outputs. Parameters ---------- From 28ff24dadc30f92a22d89ac1a360663e489ae60f Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 19 May 2022 14:33:52 +0200 Subject: [PATCH 0242/1363] Add info on available tox environments --- CONTRIBUTING.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 5767d3a71..b71a78974 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -109,6 +109,20 @@ Note, to combine the coverage data from all the tox environments run: Tips ---- +To run only parts of the testing pipeline (e.g. documentation, stylcheck, +specific python version):: + + tox -e envname + +Available standard environments are:: + + clean + check + docs + py37 + py38 + py39 + To run a subset of tests:: tox -e envname -- pytest -k test_myfeature From d7cb7f5ad3d8668cc6f597b2e1692a9a8908aa5d Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 20 May 2022 11:04:49 +0200 Subject: [PATCH 0243/1363] Add bug fix --- src/oemof/solph/components/experimental/_sink_dsm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index 81347f17c..629a4e246 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -1687,7 +1687,6 @@ def _objective_expression(self): class SinkDSMDIWInvestmentBlock(SimpleBlock): - CONSTRAINT_GROUP = True r"""Constraints for SinkDSM with "DIW" approach and `investment` defined **The following constraints are created for approach = "DIW" with an @@ -1808,6 +1807,7 @@ class SinkDSMDIWInvestmentBlock(SimpleBlock): ================================= ======================== ==== ======================================= """ # noqa: E501 + CONSTRAINT_GROUP = True def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) From 2019712477227fd1922f6c28bf3e4976309d4b86 Mon Sep 17 00:00:00 2001 From: Johannes Kochems Date: Fri, 20 May 2022 11:17:07 +0200 Subject: [PATCH 0244/1363] Undo erroneous change to SimpleBlock --- .../solph/components/experimental/_sink_dsm.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/oemof/solph/components/experimental/_sink_dsm.py b/src/oemof/solph/components/experimental/_sink_dsm.py index 629a4e246..5b21363f3 100644 --- a/src/oemof/solph/components/experimental/_sink_dsm.py +++ b/src/oemof/solph/components/experimental/_sink_dsm.py @@ -25,7 +25,7 @@ import itertools from numpy import mean -from pyomo.core.base.block import SimpleBlock +from pyomo.core.base.block import ScalarBlock from pyomo.environ import BuildAction from pyomo.environ import Constraint from pyomo.environ import Expression @@ -454,7 +454,7 @@ def constraint_group(self): ) -class SinkDSMOemofBlock(SimpleBlock): +class SinkDSMOemofBlock(ScalarBlock): r"""Constraints for SinkDSM with "oemof" approach **The following constraints are created for approach = "oemof":** @@ -730,7 +730,7 @@ def _objective_expression(self): return self.cost -class SinkDSMOemofInvestmentBlock(SimpleBlock): +class SinkDSMOemofInvestmentBlock(ScalarBlock): r"""Constraints for SinkDSM with "oemof" approach and `investment` defined @@ -1041,7 +1041,7 @@ def _objective_expression(self): return self.cost -class SinkDSMDIWBlock(SimpleBlock): +class SinkDSMDIWBlock(ScalarBlock): r"""Constraints for SinkDSM with "DIW" approach **The following constraints are created for approach = "DIW":** @@ -1686,7 +1686,7 @@ def _objective_expression(self): return self.cost -class SinkDSMDIWInvestmentBlock(SimpleBlock): +class SinkDSMDIWInvestmentBlock(ScalarBlock): r"""Constraints for SinkDSM with "DIW" approach and `investment` defined **The following constraints are created for approach = "DIW" with an @@ -2374,7 +2374,7 @@ def _objective_expression(self): return self.cost -class SinkDSMDLRBlock(SimpleBlock): +class SinkDSMDLRBlock(ScalarBlock): r"""Constraints for SinkDSM with "DLR" approach **The following constraints are created for approach = "DLR":** @@ -3338,7 +3338,7 @@ def _objective_expression(self): return self.cost -class SinkDSMDLRInvestmentBlock(SimpleBlock): +class SinkDSMDLRInvestmentBlock(ScalarBlock): r"""Constraints for SinkDSM with "DLR" approach and `investment` defined **The following constraints are created for approach = "DLR" with an From af631dd970a5a60cc7e6a36276a689cf061990fc Mon Sep 17 00:00:00 2001 From: uvchik Date: Fri, 20 May 2022 14:40:11 +0200 Subject: [PATCH 0245/1363] Add internal function, change name and install extra requirements --- .../{time_step_example.py => time_index_example.py} | 11 +++-------- setup.py | 1 + 2 files changed, 4 insertions(+), 8 deletions(-) rename examples/time_step_example/{time_step_example.py => time_index_example.py} (92%) diff --git a/examples/time_step_example/time_step_example.py b/examples/time_step_example/time_index_example.py similarity index 92% rename from examples/time_step_example/time_step_example.py rename to examples/time_step_example/time_index_example.py index a9f15e1a9..91cf93a10 100644 --- a/examples/time_step_example/time_step_example.py +++ b/examples/time_step_example/time_index_example.py @@ -23,20 +23,15 @@ This example requires oemof.solph, install by: - pip install oemof.solph + pip install oemof.solph[examples] """ -import pandas as pd from oemof import solph - -try: - import matplotlib.pyplot as plt -except ModuleNotFoundError: - plt = None +import matplotlib.pyplot as plt solver = "cbc" # 'glpk', 'gurobi',... solver_verbose = False # show/hide solver output -date_time_index = pd.date_range("1/1/2000", periods=9, freq="15T") +date_time_index = solph.create_time_index(2000, interval=0.25, number=8) energy_system = solph.EnergySystem( timeindex=date_time_index, infer_last_interval=False diff --git a/setup.py b/setup.py index 420b99942..3ddd83a3e 100644 --- a/setup.py +++ b/setup.py @@ -90,6 +90,7 @@ def read(*names, **kwargs): ], extras_require={ "dev": ["pytest", "sphinx", "sphinx_rtd_theme"], + "examples": ["matplotlib"], "dummy": ["oemof"], }, entry_points={ From d7b7cdf66ce4781232afd3a232f70b90c1e5b379 Mon Sep 17 00:00:00 2001 From: uvchik Date: Fri, 20 May 2022 15:27:41 +0200 Subject: [PATCH 0246/1363] REmove append and empty Series to avoid FutureWarnings --- src/oemof/solph/processing.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/oemof/solph/processing.py b/src/oemof/solph/processing.py index 2b72fcb0a..e875a4700 100644 --- a/src/oemof/solph/processing.py +++ b/src/oemof/solph/processing.py @@ -210,7 +210,14 @@ def results(model, remove_last_time_point=False): columns="variable_name", values="value" ) # Add empty row at the end - df_dict[k] = df_dict[k].append(pd.Series(), ignore_index=True) + df_dict[k] = pd.concat( + [ + df_dict[k], + pd.DataFrame(data=["nan"], columns=df_dict[k].columns), + ], + ignore_index=True, + axis=0, + ) set_result_index(df_dict, k, result_index) result[k] = divide_scalars_sequences(df_dict, k) From 1697a0e747fe9916ef41680a777faee516f3d248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Fri, 20 May 2022 17:22:08 +0200 Subject: [PATCH 0247/1363] Remove doc for no longer existing Flow.fix The attribute was dropped in favour of the attribute fix which holds the values itself. --- src/oemof/solph/flows/_flow.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index 3cd7ea880..563cd0cff 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -82,10 +82,6 @@ class Flow(on.Edge): The costs associated with one unit of the flow. If this is set the costs will be added to the objective expression of the optimization problem. - fixed : boolean - Boolean value indicating if a flow is fixed during the optimization - problem to its ex-ante set value. Used in combination with the - :attr:`fix`. investment : :class:`Investment ` Object indicating if a nominal_value of the flow is determined by the optimization problem. Note: This will refer all attributes to an From 23ad41c505c275356a54e2271ebe950a67f5824d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Fri, 20 May 2022 18:18:34 +0200 Subject: [PATCH 0248/1363] Move Flow.nominal_value checks to single location --- src/oemof/solph/flows/_flow.py | 37 +++++++++++------------------ tests/test_solph_network_classes.py | 35 ++++++++++++++++++++++----- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index ca91bcd5d..123423dff 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -149,6 +149,13 @@ def __init__(self, **kwargs): "positive_gradient": {"ub": None}, "negative_gradient": {"ub": None}, } + need_nominal_value = [ + "fix", + "max", + "min", + "summed_max", + "summed_min", + ] keys = [k for k in kwargs if k != "label"] if "fixed_costs" in keys: @@ -178,18 +185,6 @@ def __init__(self, **kwargs): raise AttributeError( "It is not allowed to define `min`/`max` if `fix` is defined." ) - if ( - kwargs.get("nominal_value") is None - and kwargs.get("investment") is None - ) and ( - kwargs.get("fix") is not None - or kwargs.get("min") is not None - or kwargs.get("max") is not None - ): - raise AttributeError( - "The arguments `min`/`max`/`fix` need either" - + "`nominal_value` or `investment` to be defined as well." - ) # Set default value for min and max if kwargs.get("min") is None: @@ -244,22 +239,18 @@ def __init__(self, **kwargs): "value is not allowed." ) if not self.investment: - warn_msg = ( + warn_msg_no_nominal_value = ( "If {} is set in a flow (except InvestmentFlow), " "nominal_value must be set as well.\n" "Otherwise, it won't have any effect." ) if self.nominal_value is None: - if self.summed_max is not None: - warn( - warn_msg.format("summed_max"), - debugging.SuspiciousUsageWarning, - ) - if self.summed_min is not None: - warn( - warn_msg.format("summed_min"), - debugging.SuspiciousUsageWarning, - ) + for attr in need_nominal_value: + if hasattr(self, attr): + warn( + warn_msg_no_nominal_value.format(attr), + debugging.SuspiciousUsageWarning, + ) else: assert math.isfinite( self.nominal_value diff --git a/tests/test_solph_network_classes.py b/tests/test_solph_network_classes.py index ab73f381a..b86f4244f 100644 --- a/tests/test_solph_network_classes.py +++ b/tests/test_solph_network_classes.py @@ -101,14 +101,37 @@ def test_infinite_values(): solph.flows.Flow(max=float("+inf")) -def test_summed_min_and_summed_max(): - msg1 = "If summed_max is set in a flow " - msg2 = "If summed_min is set in a flow " - with pytest.warns(SuspiciousUsageWarning, match=msg1): - solph.flows.Flow(summed_max=0.3) - with pytest.warns(SuspiciousUsageWarning, match=msg2): +def test_attributes_needing_nominal_value_get_it(): + with pytest.warns( + SuspiciousUsageWarning, + match="If fix is set in a flow " + ): + solph.flows.Flow(fix=0.3) + + with pytest.warns( + SuspiciousUsageWarning, + match="If max is set in a flow " + ): + solph.flows.Flow(max=0.3) + + with pytest.warns( + SuspiciousUsageWarning, + match="If min is set in a flow " + ): + solph.flows.Flow(min=0.3) + + with pytest.warns( + SuspiciousUsageWarning, + match="If summed_max is set in a flow " + ): solph.flows.Flow(summed_min=0.3) + with pytest.warns( + SuspiciousUsageWarning, + match="If summed_min is set in a flow " + ): + solph.flows.Flow(summed_max=0.3) + def test_min_max_values_for_bidirectional_flow(): a = solph.flows.Flow( From 0b456d6896ba500ffdfd106f2287cafbee32df61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Fri, 20 May 2022 18:25:08 +0200 Subject: [PATCH 0249/1363] Adhere to Black style --- tests/test_solph_network_classes.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/tests/test_solph_network_classes.py b/tests/test_solph_network_classes.py index b86f4244f..d5017e2f6 100644 --- a/tests/test_solph_network_classes.py +++ b/tests/test_solph_network_classes.py @@ -103,32 +103,27 @@ def test_infinite_values(): def test_attributes_needing_nominal_value_get_it(): with pytest.warns( - SuspiciousUsageWarning, - match="If fix is set in a flow " + SuspiciousUsageWarning, match="If fix is set in a flow " ): solph.flows.Flow(fix=0.3) with pytest.warns( - SuspiciousUsageWarning, - match="If max is set in a flow " + SuspiciousUsageWarning, match="If max is set in a flow " ): solph.flows.Flow(max=0.3) with pytest.warns( - SuspiciousUsageWarning, - match="If min is set in a flow " + SuspiciousUsageWarning, match="If min is set in a flow " ): solph.flows.Flow(min=0.3) with pytest.warns( - SuspiciousUsageWarning, - match="If summed_max is set in a flow " + SuspiciousUsageWarning, match="If summed_max is set in a flow " ): solph.flows.Flow(summed_min=0.3) with pytest.warns( - SuspiciousUsageWarning, - match="If summed_min is set in a flow " + SuspiciousUsageWarning, match="If summed_min is set in a flow " ): solph.flows.Flow(summed_max=0.3) From 35a3f3819e5c300dbdf5607fc2aef0a4a8d524e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Fri, 20 May 2022 21:06:13 +0100 Subject: [PATCH 0250/1363] Fix Flow class tests --- src/oemof/solph/flows/_flow.py | 4 +++- tests/flow_tests.py | 6 +++--- tests/test_solph_network_classes.py | 16 +++------------- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/oemof/solph/flows/_flow.py b/src/oemof/solph/flows/_flow.py index b6d0a9366..02fec9429 100644 --- a/src/oemof/solph/flows/_flow.py +++ b/src/oemof/solph/flows/_flow.py @@ -170,6 +170,8 @@ def __init__(self, **kwargs): } need_nominal_value = [ "fix", + "full_load_time_max", + "full_load_time_min", "max", "min", "summed_max", @@ -265,7 +267,7 @@ def __init__(self, **kwargs): ) if self.nominal_value is None: for attr in need_nominal_value: - if hasattr(self, attr): + if kwargs.get(attr) is not None: warn( warn_msg_no_nominal_value.format(attr), debugging.SuspiciousUsageWarning, diff --git a/tests/flow_tests.py b/tests/flow_tests.py index 3e29b8a50..ba662ea9c 100644 --- a/tests/flow_tests.py +++ b/tests/flow_tests.py @@ -28,7 +28,7 @@ def test_summed_max_future_warning(): """Can be removed with v0.6.""" msg = "The parameter 'summed_max' ist deprecated and will be removed" with warnings.catch_warnings(record=True) as w: - Flow(summed_max=2) + Flow(nominal_value=1, summed_max=2) assert len(w) == 1 assert msg in str(w[-1].message) @@ -37,10 +37,10 @@ def test_summed_min_future_warning(): """Can be removed with v0.6.""" msg = "The parameter 'summed_min' ist deprecated and will be removed" with warnings.catch_warnings(record=True) as w: - Flow(summed_min=2) + Flow(nominal_value=1, summed_min=2) assert len(w) == 1 assert msg in str(w[-1].message) def test_source_with_full_load_time_max(): - Flow(full_load_time_max=2) + Flow(nominal_value=1, full_load_time_max=2) diff --git a/tests/test_solph_network_classes.py b/tests/test_solph_network_classes.py index d5017e2f6..142f5f76b 100644 --- a/tests/test_solph_network_classes.py +++ b/tests/test_solph_network_classes.py @@ -120,12 +120,12 @@ def test_attributes_needing_nominal_value_get_it(): with pytest.warns( SuspiciousUsageWarning, match="If summed_max is set in a flow " ): - solph.flows.Flow(summed_min=0.3) + solph.flows.Flow(summed_max=0.3) with pytest.warns( SuspiciousUsageWarning, match="If summed_min is set in a flow " ): - solph.flows.Flow(summed_max=0.3) + solph.flows.Flow(summed_min=0.3) def test_min_max_values_for_bidirectional_flow(): @@ -143,16 +143,6 @@ def test_min_max_values_for_bidirectional_flow(): assert b.min[0] == -0.8 -def test_limit_without_nominal_value(): - """Deprecated error for actual_warning is not raised correctly.""" - msg = ( - "The arguments `min`/`max`/`fix` need either" - + "`nominal_value` or `investment` to be defined as well." - ) - with pytest.raises(AttributeError, match=msg): - solph.flows.Flow(max=0.8) - - def test_deprecated_actual_value(): """Deprecated error for actual_warning is not raised correctly.""" msg = "The `actual_value` attribute has been renamed to `fix`" @@ -168,6 +158,6 @@ def test_warning_fixed_still_used(): "The `fixed` attribute does not change anything." ) with warnings.catch_warnings(record=True) as w: - solph.flows.Flow(fixed=True) + solph.flows.Flow(nominal_value=1, fixed=True) assert len(w) != 0 assert msg == str(w[-1].message) From 89de04f5e210624ade7292fc9f98ad59c0b39953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Fri, 20 May 2022 21:06:35 +0100 Subject: [PATCH 0251/1363] Remove redundancy in component test --- tests/test_components.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_components.py b/tests/test_components.py index d3a66c353..45f0b5ae9 100644 --- a/tests/test_components.py +++ b/tests/test_components.py @@ -15,7 +15,7 @@ from oemof.tools.debugging import SuspiciousUsageWarning from oemof.solph import Investment -from oemof.solph import components as components +from oemof.solph import components from oemof.solph.buses import Bus from oemof.solph.flows import Flow from oemof.solph.flows import NonConvexFlow From edc72ed55dea3e33e45117b19b1f1c485d54c9cf Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 23 May 2022 14:49:26 +0200 Subject: [PATCH 0252/1363] Add examples --- examples/activity_costs/activity_costs.py | 94 + examples/basic_example/basic_example.csv | 8761 +++++++++++++++++ examples/basic_example/basic_example.py | 285 + examples/gradient_example/gradient_example.py | 142 + .../time_index_example.py | 2 +- examples/tuple_as_labels/tuple_as_label.csv | 8761 +++++++++++++++++ examples/tuple_as_labels/tuple_as_label.py | 358 + 7 files changed, 18402 insertions(+), 1 deletion(-) create mode 100644 examples/activity_costs/activity_costs.py create mode 100644 examples/basic_example/basic_example.csv create mode 100644 examples/basic_example/basic_example.py create mode 100644 examples/gradient_example/gradient_example.py rename examples/{time_step_example => time_index_example}/time_index_example.py (97%) create mode 100644 examples/tuple_as_labels/tuple_as_label.csv create mode 100644 examples/tuple_as_labels/tuple_as_label.py diff --git a/examples/activity_costs/activity_costs.py b/examples/activity_costs/activity_costs.py new file mode 100644 index 000000000..1548cc199 --- /dev/null +++ b/examples/activity_costs/activity_costs.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- + +""" +General description +------------------- +This example illustrates the effect of activity_costs. + +There are the following components: + + - demand_heat: heat demand (constant, for the sake of simplicity) + - fireplace: wood firing, burns "for free" if somebody is around + - boiler: gas firing, consumes (paid) gas + +Notice that activity_costs is an attribute to NonConvex. +This is because it relies on the activity status of a component +which is only available for nonconvex flows. + + +Installation requirements +------------------------- + +This example requires oemof.solph (v0.5.x), install by: + + pip install oemof.solph[examples] + +""" + +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + +from oemof import solph + + +########################################################################## +# Calculate parameters and initialize the energy system and +########################################################################## + +periods = 24 +time = pd.date_range('1/1/2018', periods=periods, freq='H') + +demand_heat = np.full(periods, 5) +demand_heat[:4] = 0 +demand_heat[4:18] = 4 + +activity_costs = np.full(periods, 5) +activity_costs[18:] = 0 + +es = solph.EnergySystem(timeindex=time) + +b_heat = solph.Bus(label='b_heat') + +es.add(b_heat) + +sink_heat = solph.components.Sink( + label='demand', + inputs={b_heat: solph.Flow(fix=demand_heat, nominal_value=1)}) + +fireplace = solph.components.Source( + label='fireplace', + outputs={b_heat: solph.Flow(nominal_value=3, + variable_costs=0, + nonconvex=solph.NonConvex( + activity_costs=activity_costs))}) + +boiler = solph.components.Source( + label='boiler', + outputs={b_heat: solph.Flow(nominal_value=10, + variable_costs=1)}) + +es.add(sink_heat, fireplace, boiler) + +########################################################################## +# Optimise the energy system +########################################################################## + +# create an optimization problem and solve it +om = solph.Model(es) + +# solve model +om.solve(solver='cbc', solve_kwargs={'tee': True}) + +########################################################################## +# Check and plot the results +########################################################################## + +results = solph.processing.results(om) + +# plot data +data = solph.views.node(results, 'b_heat')['sequences'] +ax = data.plot(kind='line', drawstyle='steps-post', grid=True, rot=0) +ax.set_xlabel('Time') +ax.set_ylabel('Heat (arb. units)') +plt.show() diff --git a/examples/basic_example/basic_example.csv b/examples/basic_example/basic_example.csv new file mode 100644 index 000000000..20afccc6c --- /dev/null +++ b/examples/basic_example/basic_example.csv @@ -0,0 +1,8761 @@ +timestep,demand_el,pv,wind +1,209643.3415769,0,0.10702 +2,207497.2007709,0,0.074873 +3,200108.0148989,0,0.055654 +4,191892.6802004,0,0.065059 +5,185717.3331069,0,0.085573 +6,180672.7483735,0,0.096229 +7,172683.5661471,0,0.11794 +8,170048.1975444,0,0.15953 +9,171132.8063389,0,0.23717 +10,179532.7553002,0.062188,0.28515 +11,189155.7737532,0.15426,0.35724 +12,201026.4708567,0.16016,0.46354 +13,208466.425651,0.1036,0.56932 +14,207718.7378864,0.084862,0.68999 +15,205443.3670963,0.052101,0.79517 +16,206255.669853,0.014673,0.86893 +17,217240.2184947,0,0.92278 +18,232798.5854994,0,0.95895 +19,237321.6349401,0,0.97963 +20,232387.8187645,0,0.99052 +21,224306.3294067,0,0.99626 +22,219280.2060996,0,0.99859 +23,223701.7176957,0,0.99933 +24,213926.3924759,0,0.99923 +25,201834.1582569,0,0.99874 +26,192215.7551605,0,0.99845 +27,187152.7090008,0,0.99779 +28,184355.8029181,0,0.99735 +29,184438.8793364,0,0.9969 +30,182786.5816836,0,0.99637 +31,180105.0595152,0,0.99481 +32,191509.6056049,0,0.99304 +33,207104.8954623,0,0.99142 +34,222501.724987,0.047766,0.99039 +35,231127.8264203,0.12973,0.98986 +36,238410.8590912,0.15324,0.98947 +37,241184.688391,0.12837,0.99026 +38,237413.9420716,0.10443,0.99193 +39,234469.3445786,0.063981,0.99271 +40,235193.9555604,0.019217,0.99281 +41,242730.8328427,0,0.99158 +42,264196.8562598,0,0.99079 +43,265950.6917572,0,0.99146 +44,260283.0338866,0,0.99194 +45,245578.5078477,0,0.99202 +46,238849.3179655,0,0.99239 +47,241553.9169168,0,0.99198 +48,231372.4403186,0,0.99105 +49,218135.5976697,0,0.98873 +50,208761.8084717,0,0.98524 +51,201492.6218705,0,0.98152 +52,194421.895602,0,0.98 +53,192118.8326725,0,0.97512 +54,187983.4731838,0,0.96149 +55,181517.3586262,0,0.94655 +56,185246.5667365,0,0.94065 +57,191874.2187741,0,0.93581 +58,202715.6913621,0.063357,0.9057 +59,213700.2400039,0.1452,0.87636 +60,225649.3981692,0.13022,0.85986 +61,229346.2987835,0.052762,0.87187 +62,223484.7959368,0.04271,0.88404 +63,219326.3596653,0.025526,0.90285 +64,218684.8251018,0.0066043,0.92233 +65,230273.9854545,0,0.94465 +66,249824.6358941,0,0.95549 +67,254873.835984,0,0.96371 +68,249547.7144998,0,0.9678 +69,239841.6196285,0,0.97427 +70,235807.7979845,0,0.97693 +71,240723.1527338,0,0.9763 +72,229821.6805104,0,0.97646 +73,217863.2916319,0,0.97775 +74,207418.7397092,0,0.9796 +75,201935.6961014,0,0.98278 +76,199406.4806999,0,0.98391 +77,201095.7012053,0,0.98546 +78,205706.4424209,0,0.98765 +79,222427.8792818,0,0.99015 +80,247180.0365783,0,0.99225 +81,262415.3286229,0,0.99371 +82,271932.1938747,0.01319,0.9935 +83,278416.7698586,0.039835,0.99284 +84,283821.3524046,0.047415,0.9932 +85,285838.2632266,0.038888,0.9939 +86,282782.8971759,0.031488,0.9946 +87,279990.6064497,0.01869,0.99482 +88,278126.0023946,0.0044696,0.99465 +89,283147.5103451,0,0.99386 +90,300847.4027993,0,0.99291 +91,299767.4093614,0,0.99168 +92,294953.5924566,0,0.98756 +93,281296.7523596,0,0.97564 +94,273252.1858544,0,0.95063 +95,269292.2099155,0,0.89537 +96,256387.6729397,0,0.77284 +97,245984.6592261,0,0.68626 +98,236135.4883011,0,0.58396 +99,229480.1441241,0,0.45925 +100,225792.4742229,0,0.41573 +101,227089.3894197,0,0.39466 +102,233721.6568139,0,0.44267 +103,252852.3098054,0,0.5154 +104,281499.8280488,0,0.55825 +105,295244.3599207,0,0.59368 +106,298996.6448138,0.038505,0.56689 +107,301895.0887412,0.10504,0.52071 +108,305864.2953932,0.12442,0.53577 +109,306865.8277694,0.10471,0.54674 +110,305222.7608297,0.085151,0.56219 +111,301142.7856199,0.052307,0.59634 +112,298585.8780789,0.016081,0.60329 +113,301673.5516257,0,0.61437 +114,315150.3928164,0,0.67381 +115,318348.8349209,0,0.74123 +116,313221.1737692,0,0.82476 +117,299015.1062401,0,0.89963 +118,285884.4167923,0,0.94793 +119,280784.4477801,0,0.96764 +120,262627.6350253,0,0.97588 +121,249206.1781134,0,0.97923 +122,239144.7007861,0,0.96818 +123,232249.3580674,0,0.96364 +124,228247.8439193,0,0.96655 +125,228732.4563594,0,0.95912 +126,233227.8136606,0,0.9409 +127,249533.8684301,0,0.90182 +128,274466.0246328,0,0.84907 +129,285898.2628621,0,0.77243 +130,289996.6994981,0.065084,0.69315 +131,294847.4392555,0.17832,0.64418 +132,299712.0250825,0.22241,0.65718 +133,299970.4850506,0.20579,0.71166 +134,295973.5862591,0.16543,0.74100 +135,291344.3836172,0.10124,0.78431 +136,289456.7027792,0.033614,0.84363 +137,292567.4531088,0,0.89667 +138,307031.980606,0,0.94979 +139,312219.6413931,0,0.97085 +140,306639.6752973,0,0.9807 +141,292188.9938699,0,0.98644 +142,282561.3600604,0,0.99008 +143,279565.9936451,0,0.99254 +144,263587.6291923,0,0.99346 +145,249510.7916472,0,0.99428 +146,238558.5505015,0,0.99534 +147,232106.2820136,0,0.9965 +148,228478.6117479,0,0.99709 +149,230952.4428706,0,0.99769 +150,241156.9962516,0,0.99813 +151,266139.9213767,0,0.99826 +152,297625.8839119,0,0.99818 +153,307655.0537432,0,0.99804 +154,308287.3575936,0.00060963,0.99805 +155,310562.7283837,0.006088,0.99812 +156,313235.0198389,0.0074218,0.99831 +157,312976.5598709,0.0053112,0.99846 +158,310087.3466567,0.0039527,0.99843 +159,306081.2171521,0.0015335,0.99838 +160,304941.2240788,0,0.99828 +161,305795.0650446,0,0.9981 +162,320905.7424619,0,0.99796 +163,324574.9509367,0,0.99793 +164,320148.823984,0,0.99783 +165,306182.7549967,0,0.99774 +166,294173.5971959,0,0.99771 +167,286858.2570291,0,0.9976 +168,269818.3605647,0,0.99748 +169,255418.4480596,0,0.99739 +170,245320.0478797,0,0.99752 +171,238083.1687746,0,0.99789 +172,233121.6604595,0,0.99814 +173,234907.8034529,0,0.99854 +174,243616.9813046,0,0.99900 +175,265604.5400143,0,0.99941 +176,295710.5109345,0,0.99966 +177,306058.1403692,0,0.9998 +178,308236.5886713,0.01781,0.99986 +179,311079.6483197,0.050901,0.99987 +180,313968.8615339,0.060377,0.99984 +181,314162.7065099,0.050395,0.99976 +182,308250.434741,0.040749,0.99963 +183,303436.6178362,0.024728,0.99951 +184,300302.7907238,0.0073169,0.99937 +185,303062.7739539,0,0.99911 +186,318958.0619884,0,0.99851 +187,321094.9720813,0,0.99745 +188,313041.1748629,0,0.99587 +189,296398.1990637,0,0.99444 +190,286082.877125,0,0.99361 +191,283433.6624525,0,0.99172 +192,270256.819439,0,0.98884 +193,260107.6503369,0,0.98624 +194,250876.9371926,0,0.98548 +195,244406.2072784,0,0.98655 +196,239283.1614833,0,0.98977 +197,237243.1738784,0,0.99201 +198,235244.7244827,0,0.99339 +199,232637.0480194,0,0.99312 +200,242656.9871376,0,0.9922 +201,256646.1329078,0,0.98897 +202,270976.8150643,0.069159,0.98065 +203,280678.294579,0.1797,0.97199 +204,287061.3327182,0.21466,0.96335 +205,288930.55213,0.18781,0.95627 +206,284172.1195041,0.1522,0.9492 +207,276358.3208274,0.094903,0.94247 +208,274096.7961071,0.033534,0.92632 +209,277978.3109842,0,0.91662 +210,291856.6881967,0,0.92451 +211,296762.8122329,0,0.94718 +212,289281.3192295,0,0.96859 +213,273852.1822087,0,0.98113 +214,266292.2281436,0,0.98932 +215,267418.3751472,0,0.99379 +216,258759.9662178,0,0.99579 +217,246049.2742181,0,0.99628 +218,235383.1851798,0,0.99629 +219,228875.5324131,0,0.99633 +220,222030.9586166,0,0.99583 +221,217558.6780982,0,0.99524 +222,211129.4863932,0,0.99513 +223,200574.1659127,0,0.99542 +224,203860.299792,0,0.99524 +225,215638.6897642,0,0.99536 +226,229595.5280384,0.072382,0.99505 +227,238826.2411827,0.18447,0.99442 +228,251066.166812,0.22796,0.99288 +229,251735.393515,0.2106,0.99092 +230,242818.5246176,0.1762,0.98967 +231,236573.9471755,0.11512,0.9885 +232,234847.8038175,0.044413,0.98409 +233,239763.1585668,0,0.97846 +234,261321.4891153,0,0.9732 +235,272873.7266154,0,0.96894 +236,271350.6589466,0,0.95932 +237,261012.260225,0,0.95192 +238,254583.06852,0,0.93421 +239,258949.1958373,0,0.90943 +240,248361.5678607,0,0.86199 +241,237829.3241631,0,0.79485 +242,228506.3038873,0,0.68538 +243,224315.5601198,0,0.59208 +244,222017.1125469,0,0.50701 +245,224380.1751118,0,0.43518 +246,234764.7273992,0,0.36519 +247,263486.0913477,0,0.34052 +248,292812.0670072,0,0.38449 +249,305042.7619234,0,0.50811 +250,308878.1232348,0.074326,0.64962 +251,312902.7141657,0.17006,0.76382 +252,317988.8371083,0.18191,0.87351 +253,318602.6795323,0.13116,0.89541 +254,316262.6937503,0.12365,0.91036 +255,311587.3375427,0.092392,0.92456 +256,308730.4318245,0.042049,0.93285 +257,308531.9714919,0,0.94852 +258,320642.6671372,0,0.96455 +259,325461.0993986,0,0.98073 +260,321842.659846,0,0.98954 +261,308573.5097011,0,0.99261 +262,296605.8901095,0,0.99400 +263,290615.1572788,0,0.99409 +264,272550.6516554,0,0.99296 +265,257873.8177559,0,0.98899 +266,248186.184311,0,0.97863 +267,242481.6035878,0,0.9611 +268,239790.8507062,0,0.93708 +269,241392.3794368,0,0.9057 +270,250480.0165274,0,0.8716 +271,280784.4477801,0,0.84736 +272,312016.5657039,0,0.85063 +273,321759.5834277,0,0.87132 +274,321787.2755671,0.076031,0.89753 +275,323236.4975308,0.19581,0.92378 +276,324118.0306361,0.25131,0.94872 +277,325207.2547871,0.24616,0.95400 +278,322848.8075787,0.20346,0.95478 +279,318833.447361,0.13178,0.95506 +280,316918.0743835,0.051561,0.95434 +281,318118.0670923,0,0.95222 +282,332462.5953185,0,0.94683 +283,337521.0261216,0,0.93765 +284,333039.51489,0,0.9268 +285,318118.0670923,0,0.92163 +286,306118.1400047,0,0.9228 +287,297215.117177,0,0.93281 +288,279658.3007765,0,0.94648 +289,266347.6124224,0,0.9555 +290,257592.281005,0,0.96096 +291,250849.2450531,0,0.96759 +292,246833.8848354,0,0.97131 +293,249510.7916472,0,0.97032 +294,258616.8901641,0,0.96457 +295,286955.1795171,0,0.95566 +296,318348.8349209,0,0.94793 +297,328581.0804413,0,0.94044 +298,328179.5444196,0.078986,0.92875 +299,330750.2980303,0.17921,0.92272 +300,332693.3631471,0.1955,0.91626 +301,331045.6808509,0.14795,0.89946 +302,330224.147381,0.13245,0.8804 +303,326628.7846113,0.094354,0.84715 +304,324856.4876876,0.04193,0.84519 +305,324708.7962773,0,0.83288 +306,337534.8721913,0,0.81552 +307,341499.4634868,0,0.78685 +308,335721.0370585,0,0.74896 +309,318127.2978054,0,0.71534 +310,304170.4595312,0,0.67863 +311,296481.275482,0,0.6399 +312,279436.7636611,0,0.58711 +313,259318.424363,0,0.5171 +314,249496.9455775,0,0.44995 +315,242283.1432552,0,0.39156 +316,239707.774288,0,0.34776 +317,240473.9234789,0,0.30079 +318,250064.6344359,0,0.23124 +319,276575.2425863,0,0.18207 +320,306358.1385464,0,0.14744 +321,315971.9262862,0,0.11773 +322,316581.1533537,0.081722,0.086684 +323,319114.9841119,0.19784,0.056593 +324,322318.0415729,0.24148,0.066865 +325,322142.6580232,0.22345,0.079421 +326,319701.1343965,0.18917,0.059228 +327,314905.7789181,0.12726,0.042115 +328,311633.4911084,0.053661,0.032621 +329,310336.5759116,0,0.024503 +330,323504.188212,0,0.022502 +331,327750.3162584,0,0.026714 +332,322054.9662483,0,0.036999 +333,306108.9092915,0,0.055015 +334,291819.7653441,0,0.065307 +335,284329.0416275,0,0.065417 +336,266652.2259562,0,0.055026 +337,252330.7745128,0,0.036812 +338,242786.2171216,0,0.031955 +339,237446.2495676,0,0.031005 +340,235083.1870027,0,0.029617 +341,237400.0960019,0,0.03406 +342,246903.1151839,0,0.040213 +343,272186.0384862,0,0.050632 +344,302919.6979002,0,0.063647 +345,313770.4012013,0,0.073919 +346,315145.7774598,0.084359,0.072285 +347,319096.5226856,0.20794,0.079528 +348,322156.5040929,0.26362,0.10883 +349,320033.4400697,0.25794,0.17342 +350,315441.1602804,0.21806,0.22862 +351,308356.5879422,0.14692,0.24602 +352,304848.9169473,0.063073,0.27271 +353,303962.7684855,0.00027026,0.29775 +354,315459.6217067,0,0.31014 +355,318639.6023849,0,0.30274 +356,311421.1847061,0,0.28642 +357,294741.2860543,0,0.28736 +358,281707.5190945,0,0.28863 +359,277558.3135362,0,0.28624 +360,262807.6339316,0,0.28227 +361,248915.4106494,0,0.28659 +362,238309.3212466,0,0.28964 +363,231524.7470855,0,0.2783 +364,227154.0044117,0,0.26594 +365,225178.6317988,0,0.25472 +366,221320.1937045,0,0.23885 +367,219044.8229144,0,0.21481 +368,232909.3540572,0,0.20041 +369,248555.4128368,0,0.16956 +370,262687.6346607,0.091501,0.13897 +371,270819.8929408,0.2314,0.1646 +372,276658.3190046,0.31736,0.24502 +373,275167.5588318,0.34265,0.27324 +374,267962.9872227,0.2898,0.32255 +375,262553.7893201,0.19659,0.41404 +376,262036.869384,0.08639,0.51915 +377,265189.1579228,0.002534,0.61358 +378,281749.0573037,0,0.64783 +379,290061.3144901,0,0.65273 +380,283539.8156537,0,0.64911 +381,266896.8398545,0,0.64836 +382,260223.0342512,0,0.6751 +383,261298.4123325,0,0.68763 +384,250840.01434,0,0.7081 +385,239693.9282182,0,0.72421 +386,228377.0739033,0,0.72728 +387,219395.5900139,0,0.76893 +388,215712.5354693,0,0.7869 +389,212458.709086,0,0.79311 +390,204811.0632459,0,0.79782 +391,195340.3515598,0,0.80225 +392,197080.3409875,0,0.81419 +393,208798.7313242,0,0.82665 +394,224670.9425759,0.051356,0.83881 +395,236167.7957971,0.10563,0.86092 +396,248144.6461018,0.10694,0.8905 +397,247941.5704127,0.06994,0.91655 +398,239375.4686148,0.054844,0.93673 +399,232443.2030434,0.032835,0.95126 +400,229406.2984189,0.011109,0.9588 +401,231090.9035677,0,0.96529 +402,250276.9408382,0,0.9725 +403,263241.4774494,0,0.97973 +404,261381.4887508,0,0.98557 +405,251615.3942441,0,0.98951 +406,246284.6574033,0,0.99288 +407,249127.7170517,0,0.99484 +408,235489.338381,0,0.99521 +409,223175.5670465,0,0.99371 +410,214147.9295914,0,0.99037 +411,209643.3415769,0,0.98429 +412,209306.4205472,0,0.97188 +413,214175.6217308,0,0.9326 +414,226747.8530333,0,0.82715 +415,253793.8425462,0,0.62052 +416,285381.342926,0,0.36018 +417,298788.9537681,0,0.22249 +418,302582.7768704,0.073828,0.19643 +419,304968.9162182,0.1756,0.11004 +420,310553.4976705,0.2285,0.1088 +421,309251.9671172,0.2317,0.19864 +422,307784.2837272,0.18315,0.15331 +423,303035.0818145,0.11281,0.085065 +424,300118.1764609,0.043162,0.12235 +425,298645.8777144,0.00017489,0.33257 +426,311245.8011563,0,0.5732 +427,316941.1511664,0,0.69382 +428,311836.5667976,0,0.76668 +429,296564.3519003,0,0.85104 +430,284222.8884264,0,0.91486 +431,275758.324473,0,0.93142 +432,254167.6864285,0,0.90007 +433,237880.0930854,0,0.87802 +434,229553.9898292,0,0.90783 +435,226350.9323681,0,0.92705 +436,226410.9320036,0,0.93795 +437,231252.4410478,0,0.94272 +438,241756.992606,0,0.93711 +439,270072.2051762,0,0.91877 +440,301392.0148748,0,0.89093 +441,313031.9441498,0,0.85347 +442,312990.4059406,0.098843,0.81949 +443,315311.9302964,0.2117,0.81067 +444,317527.301451,0.24839,0.83933 +445,315519.6213421,0.22112,0.85515 +446,312727.330616,0.19697,0.86576 +447,307955.0519204,0.14278,0.83231 +448,306238.1392755,0.070014,0.76946 +449,305282.7604651,0.0028785,0.69714 +450,317130.3807858,0,0.6428 +451,322470.3483398,0,0.65468 +452,318321.1427814,0,0.71986 +453,301535.0909285,0,0.78958 +454,288459.7857596,0,0.87624 +455,278799.8444541,0,0.95262 +456,258206.1234291,0,0.98573 +457,242827.7553307,0,0.99518 +458,234732.4199032,0,0.99749 +459,229230.9148692,0,0.99735 +460,229304.7605743,0,0.99765 +461,233061.660824,0,0.99831 +462,242929.2931753,0,0.99845 +463,270976.8150643,0,0.99845 +464,303288.9264259,0,0.99851 +465,313378.0958927,0,0.99853 +466,313082.7130721,0.085616,0.9985 +467,314107.3222311,0.15548,0.9984 +468,315602.6977604,0.14604,0.99828 +469,312404.2556559,0.080527,0.99817 +470,309671.9645652,0.063467,0.99809 +471,306048.9096561,0.03865,0.99795 +472,304821.2248079,0.014077,0.99763 +473,304816.6094513,0,0.99722 +474,318385.7577735,0,0.99663 +475,325954.9425518,0,0.99556 +476,320508.8217966,0,0.99526 +477,305001.2237142,0,0.99594 +478,289170.5506717,0,0.99659 +479,280341.3735492,0,0.99742 +480,261575.3337268,0,0.99803 +481,246847.7309051,0,0.9985 +482,237977.0155734,0,0.99854 +483,233555.5039773,0,0.99823 +484,232373.9726948,0,0.99786 +485,234944.7263055,0,0.99718 +486,244821.5893699,0,0.99596 +487,271479.8889307,0,0.99369 +488,304082.7677564,0,0.99015 +489,313493.479807,0,0.9847 +490,312321.1792376,0.10665,0.9822 +491,312468.870648,0.20835,0.98227 +492,317019.6122281,0.23085,0.98238 +493,315907.3112942,0.18924,0.98236 +494,314015.0150996,0.18514,0.98138 +495,311038.1101106,0.14878,0.98149 +496,308661.2014759,0.083322,0.98526 +497,307031.980606,0.0045477,0.98561 +498,318658.0638112,0,0.98665 +499,324113.4152795,0,0.98868 +500,320227.2850457,0,0.99067 +501,304701.225537,0,0.99306 +502,290329.0051713,0,0.99535 +503,280945.9852601,0,0.99681 +504,261344.5658982,0,0.99708 +505,244996.9729196,0,0.99617 +506,236486.2554006,0,0.99308 +507,229895.5262156,0,0.98659 +508,229549.3744726,0,0.97334 +509,232152.4355793,0,0.94664 +510,242006.2218609,0,0.90225 +511,269052.2113737,0,0.82645 +512,301202.7852553,0,0.73384 +513,313415.0187452,0,0.63605 +514,313876.5544025,0.11259,0.54335 +515,314388.858982,0.26216,0.6225 +516,315791.9273799,0.36644,0.66641 +517,307239.6716517,0.4109,0.60637 +518,302324.3169024,0.33065,0.56103 +519,299162.7976504,0.21205,0.47328 +520,296416.66049,0.089959,0.42288 +521,295821.2794922,0.0064264,0.40352 +522,310936.572266,0,0.36031 +523,315958.0802165,0,0.3432 +524,309390.4278143,0,0.33227 +525,293338.2176564,0,0.3106 +526,278702.9219661,0,0.2786 +527,273866.0282785,0,0.26708 +528,257139.976061,0,0.29595 +529,241895.4533032,0,0.35447 +530,232410.8955474,0,0.42871 +531,225621.7060297,0,0.49802 +532,222354.0335767,0,0.5205 +533,220997.1187444,0,0.53696 +534,218001.7523291,0,0.53922 +535,216178.6864831,0,0.52435 +536,230527.8300659,0,0.49554 +537,248749.2578128,0,0.44619 +538,262544.558607,0.11674,0.40498 +539,270362.9726402,0.2322,0.54257 +540,275204.4816844,0.27641,0.81155 +541,272536.8055857,0.25711,0.92783 +542,263541.4756266,0.2089,0.9625 +543,256756.9014655,0.13572,0.96839 +544,255275.3720058,0.058979,0.96324 +545,256766.1321786,0.0035054,0.95253 +546,273081.4176612,0,0.95459 +547,281259.8295071,0,0.96195 +548,274733.715314,0,0.96279 +549,259203.0404487,0,0.95805 +550,249612.3294918,0,0.9366 +551,250216.9412027,0,0.9106 +552,238890.8561747,0,0.86504 +553,225229.4007211,0,0.81083 +554,215191.0001767,0,0.78969 +555,208291.0421013,0,0.79305 +556,204691.063975,0,0.78944 +557,202946.4591907,0,0.74833 +558,197449.5695133,0,0.7237 +559,189681.9244024,0,0.73519 +560,193821.8992476,0,0.75967 +561,205757.2113432,0.00032073,0.75674 +562,219801.7413923,0.12204,0.75671 +563,231017.0578626,0.22346,0.80033 +564,242509.2957272,0.24054,0.80029 +565,242546.2185798,0.19069,0.79045 +566,234040.1164173,0.16941,0.74929 +567,227357.0801009,0.12409,0.62488 +568,225778.6281532,0.064456,0.59529 +569,228847.8402737,0.004994,0.5899 +570,248103.1078927,0,0.54146 +571,262198.4068641,0,0.4619 +572,261663.0255017,0,0.37984 +573,252547.6962717,0,0.3208 +574,246160.0427758,0,0.34221 +575,248670.7967511,0,0.41383 +576,237977.0155734,0,0.48859 +577,224024.7926558,0,0.54042 +578,215084.8469755,0,0.57731 +579,211235.6395943,0,0.57927 +580,209758.7254912,0,0.52953 +581,215417.1526487,0,0.49879 +582,227684.7704175,0,0.49436 +583,254979.9891852,0,0.4836 +584,283978.274528,0,0.47116 +585,294935.1310303,0.00027803,0.38892 +586,299208.9512162,0.11435,0.38261 +587,304798.148025,0.25583,0.51549 +588,310405.8062602,0.35596,0.56004 +589,309284.2746132,0.40084,0.59285 +590,306976.5963271,0.31861,0.61685 +591,301664.3209125,0.20156,0.58826 +592,298655.1084275,0.085011,0.57727 +593,295327.436339,0.0078999,0.62733 +594,308370.4340119,0,0.63174 +595,316451.9233697,0,0.62305 +596,312127.3342616,0,0.6021 +597,298433.571312,0,0.58387 +598,286253.6453181,0,0.60978 +599,278610.6148346,0,0.63836 +600,259876.8825083,0,0.63287 +601,245343.1246626,0,0.64915 +602,235780.105845,0,0.68347 +603,231778.591697,0,0.69271 +604,230661.6754065,0,0.65726 +605,233837.0407282,0,0.59157 +606,245181.5871825,0,0.54967 +607,273755.2597207,0,0.52746 +608,304544.3034136,0,0.47002 +609,313895.0158288,0.00090956,0.3337 +610,315459.6217067,0.13152,0.23876 +611,318588.8334626,0.25851,0.21154 +612,320633.4364241,0.31893,0.19134 +613,318639.6023849,0.31593,0.19374 +614,318150.3745883,0.2622,0.19313 +615,315238.0845912,0.17744,0.14044 +616,311859.6435804,0.084356,0.11048 +617,309376.5817446,0.0082402,0.1157 +618,325341.1001277,0,0.070239 +619,331899.5218167,0,0.037115 +620,326845.7063702,0,0.023834 +621,312436.5631519,0,0.01433 +622,299832.0243534,0,0.0063603 +623,294408.9803811,0,0 +624,280230.6049915,0,0 +625,268466.061089,0,0 +626,259272.2707973,0,0 +627,253124.6158432,0,0.0078852 +628,246820.0387656,0,0.0093819 +629,246395.425961,0,0.011306 +630,255561.5241133,0,0.013868 +631,282607.5136261,0,0.016044 +632,314388.858982,0,0.017579 +633,322608.809037,0,0.017956 +634,323490.3421423,0.072897,0.016472 +635,326227.2485895,0.14703,0.015605 +636,330044.1484747,0.18543,0.025114 +637,328982.6164631,0.18826,0.035222 +638,328904.1554014,0.15363,0.034437 +639,327039.5513462,0.10141,0.031409 +640,325631.8675917,0.046302,0.024686 +641,326231.8639461,0.0039459,0.021278 +642,345944.0518658,0,0.019525 +643,354570.1532991,0,0.019196 +644,348214.8072993,0,0.018571 +645,330944.1430063,0,0.017661 +646,317624.2239391,0,0.013528 +647,310908.8801266,0,0.0098721 +648,295590.5116636,0,0.0081141 +649,280198.2974955,0,0.0083792 +650,270916.8154289,0,0.01062 +651,262798.4032184,0,0.01359 +652,257873.8177559,0,0.019258 +653,256018.4444139,0,0.025923 +654,262789.1725053,0,0.029036 +655,287338.2541126,0,0.024058 +656,316521.1537183,0,0.016066 +657,325502.6376077,0.0019241,0.012451 +658,327371.8570194,0.11252,0.009451 +659,331793.3686156,0.19273,0.0071942 +660,337557.9489742,0.20284,0.0073419 +661,337917.9467868,0.15859,0.0096857 +662,335121.0407041,0.16008,0.01168 +663,330242.6088073,0.13537,0.013427 +664,326236.4793027,0.085105,0.019871 +665,320254.9771852,0.0099926,0.038788 +666,330699.529108,0,0.059085 +667,337613.333253,0,0.065536 +668,329434.9214072,0,0.055114 +669,313018.09808,0,0.038575 +670,297935.1128022,0,0.026531 +671,290199.7751873,0,0.01888 +672,272901.4187549,0,0.013647 +673,258524.5830326,0,0.0099863 +674,246621.578433,0,0.0073218 +675,239453.9296765,0,0 +676,237649.3252568,0,0 +677,238147.7837666,0,0 +678,247295.4204926,0,0 +679,270746.0472357,0,0 +680,300496.6356998,0,0 +681,310922.7261963,0.0049051,0.0066516 +682,314481.1661134,0.092725,0.009744 +683,316267.3091068,0.17906,0.011895 +684,317827.2996282,0.22384,0.015142 +685,315981.1569994,0.22723,0.02137 +686,310031.9623779,0.18632,0.02972 +687,304110.4598958,0.12466,0.033554 +688,300575.0967615,0.05909,0.051754 +689,298595.1087921,0.0068946,0.10493 +690,314610.3960974,0,0.17512 +691,319678.0576137,0,0.24123 +692,312228.8721062,0,0.27587 +693,293707.4461821,0,0.28748 +694,282515.2064947,0,0.23545 +695,279155.2269101,0,0.16923 +696,266393.7659881,0,0.14274 +697,253327.6915324,0,0.1411 +698,245020.0497025,0,0.15028 +699,239398.5453976,0,0.17212 +700,237404.7113584,0,0.17803 +701,236527.7936097,0,0.17734 +702,231109.364994,0,0.17161 +703,226207.8563144,0,0.15902 +704,236970.8678407,0,0.15215 +705,252021.5456225,0.0082015,0.14196 +706,268156.8321987,0.15177,0.13472 +707,273732.1829379,0.24388,0.14259 +708,277678.3128071,0.24305,0.17271 +709,276275.2444091,0.17315,0.23283 +710,267667.6044021,0.19504,0.31592 +711,261224.5666273,0.182000,0.40845 +712,256586.1332723,0.12705,0.47659 +713,254712.298504,0.018871,0.52108 +714,270630.6633214,0,0.54901 +715,283124.4335622,0,0.59913 +716,275038.3288478,0,0.66041 +717,258833.811923,0,0.71034 +718,248850.7956574,0,0.72085 +719,252815.3869529,0,0.68153 +720,243464.6745377,0,0.63303 +721,232355.5112685,0,0.60579 +722,222820.1845904,0,0.60688 +723,215915.6111585,0,0.61024 +724,210321.798993,0,0.61096 +725,205674.1349249,0,0.63018 +726,201303.3922511,0,0.69459 +727,194214.2045562,0,0.78286 +728,197094.1870573,0,0.87492 +729,208194.1196133,0.0075359,0.92146 +730,223018.644923,0.090774,0.94808 +731,233509.3504115,0.13736,0.96416 +732,245703.1224752,0.12269,0.97528 +733,245015.4343459,0.065891,0.97977 +734,237104.7131813,0.053691,0.98161 +735,229290.9145046,0.035276,0.98008 +736,226992.4669317,0.015884,0.97177 +737,228201.6903536,0.00028619,0.94985 +738,248306.1835819,0,0.93276 +739,267261.4530237,0,0.93104 +740,264690.699413,0,0.93304 +741,256369.2115134,0,0.93906 +742,250627.7079377,0,0.95022 +743,256355.3654437,0,0.96573 +744,244466.2069138,0,0.97942 +745,242753.9096256,0,0.98686 +746,235715.490853,0,0.98693 +747,234210.8846105,0,0.98256 +748,232244.7427108,0,0.97341 +749,236403.1789823,0,0.95742 +750,244203.1315892,0,0.93298 +751,272993.7258863,0,0.89732 +752,305093.5308457,0,0.86126 +753,316128.8484097,0.010902,0.79369 +754,320504.2064401,0.12502,0.72486 +755,324939.5641059,0.2122,0.78548 +756,327367.2416629,0.23938,0.85623 +757,326471.8624879,0.21566,0.85352 +758,325678.0211575,0.20524,0.79117 +759,322262.6572941,0.16615,0.72938 +760,319064.2151896,0.10327,0.71853 +761,313318.0962572,0.017041,0.71877 +762,324764.1805562,0,0.6726 +763,334714.8893257,0,0.63641 +764,326933.3981451,0,0.61022 +765,311804.2593016,0,0.61461 +766,299739.717222,0,0.6464 +767,291741.3042824,0,0.70355 +768,277189.0850104,0,0.74149 +769,267792.2190295,0,0.74369 +770,262895.3257065,0,0.72499 +771,257255.3599753,0,0.69375 +772,252183.0831025,0,0.66858 +773,253738.4582673,0,0.71115 +774,261718.4097806,0,0.78794 +775,283733.6606297,0,0.84225 +776,313678.0940699,0,0.86282 +777,325733.4054363,0.015123,0.84316 +778,328128.7754973,0.17268,0.79507 +779,332277.9810556,0.25423,0.78963 +780,337654.8714622,0.23572,0.86378 +781,339011.7862944,0.14651,0.8781 +782,341601.0013314,0.18549,0.86207 +783,340396.3932661,0.18926,0.83731 +784,338716.4034738,0.1448,0.85284 +785,335748.7291979,0.026179,0.86073 +786,345187.133388,0,0.8585 +787,352391.7049971,0,0.86204 +788,343617.9121534,0,0.86600 +789,324750.3344865,0,0.85806 +790,307128.903094,0,0.83206 +791,296199.7387311,0,0.81018 +792,280156.7592863,0,0.78776 +793,265858.3846258,0,0.75611 +794,254393.8389005,0,0.72315 +795,247540.0343909,0,0.69888 +796,251407.7031984,0,0.67874 +797,256055.3672665,0,0.65061 +798,259784.5753768,0,0.58286 +799,282552.1293473,0,0.49443 +800,316345.7701686,0,0.40681 +801,328433.389031,0.019753,0.31904 +802,330796.451596,0.18123,0.24236 +803,335748.7291979,0.28346,0.13821 +804,340396.3932661,0.29684,0.073024 +805,339441.0144556,0.24114,0.040785 +806,337493.3339822,0.25536,0.024732 +807,332614.9020854,0.23084,0.020094 +808,330062.609901,0.16309,0.022274 +809,326933.3981451,0.032307,0.029868 +810,336394.879118,0,0.036648 +811,347873.2709129,0,0.034023 +812,337059.4904644,0,0.022889 +813,315505.7752724,0,0.015246 +814,298742.8002024,0,0.015589 +815,288210.5565047,0,0.019045 +816,269190.6720709,0,0.021908 +817,255044.6041772,0,0.022479 +818,250960.0136109,0,0.021658 +819,249192.3320437,0,0.021813 +820,247055.4219508,0,0.022115 +821,246330.810969,0,0.022573 +822,253673.8432753,0,0.02053 +823,277636.7745979,0,0.016579 +824,307871.9755021,0,0.015174 +825,316078.0794874,0.021348,0.014956 +826,316581.1533537,0.18544,0.018074 +827,318090.3749528,0.28811,0.024037 +828,319336.5212273,0.30358,0.033897 +829,316539.6151446,0.25083,0.049303 +830,313691.9401396,0.22878,0.062996 +831,309228.8903343,0.17831,0.063393 +832,307068.9034585,0.1085,0.063624 +833,303690.4624477,0.021063,0.090907 +834,315902.6959376,0,0.12707 +835,330279.5316599,0,0.17631 +836,323287.2664531,0,0.23933 +837,306985.8270402,0,0.29941 +838,292950.5277043,0,0.30928 +839,283885.9673966,0,0.31367 +840,264953.7747376,0,0.31615 +841,251781.5470807,0,0.32815 +842,246409.2720307,0,0.3465 +843,243963.1330475,0,0.35013 +844,244867.7429356,0,0.34777 +845,250115.4033582,0,0.35842 +846,256586.1332723,0,0.39132 +847,278832.1519501,0,0.44504 +848,309930.4245333,0,0.48987 +849,318607.2948889,0.023287,0.4427 +850,317850.3764111,0.1908,0.42267 +851,321708.8145054,0.33755,0.52629 +852,323407.265724,0.42508,0.5504 +853,321782.6602106,0.45064,0.53854 +854,314919.6249878,0.3687,0.49887 +855,307908.8983547,0.25054,0.44339 +856,303215.0807208,0.12627,0.4549 +857,301045.8631319,0.025977,0.52876 +858,314878.0867786,0,0.57437 +859,322973.4222062,0,0.58519 +860,313705.7862093,0,0.5665 +861,295461.2816796,0,0.53071 +862,280253.6817743,0,0.51686 +863,274512.1781986,0,0.46983 +864,258247.6616383,0,0.40783 +865,243783.1341412,0,0.36922 +866,234709.3431203,0,0.35799 +867,227518.6175809,0,0.34735 +868,225649.3981692,0,0.29147 +869,224010.9465861,0,0.24805 +870,222404.8024989,0,0.22789 +871,217558.6780982,0,0.23205 +872,227777.0775489,0,0.22072 +873,243150.8302908,0.029238,0.13482 +874,259203.0404487,0.20231,0.11406 +875,267542.9897746,0.3653,0.11606 +876,272204.4999125,0.47523,0.087782 +877,268724.5210571,0.52337,0.076376 +878,261473.7958822,0.46465,0.078945 +879,255575.370183,0.35472,0.088339 +880,253383.0758112,0.21371,0.13002 +881,254426.1463965,0.049709,0.22681 +882,268489.1378719,0,0.30367 +883,280992.1388259,0,0.30977 +884,272495.2673765,0,0.28166 +885,255399.9866333,0,0.25736 +886,245583.1232043,0,0.23734 +887,247660.0336618,0,0.20423 +888,236001.6429605,0,0.16083 +889,221740.1911526,0,0.13104 +890,213178.7047112,0,0.11971 +891,209144.8830671,0,0.11041 +892,209620.2647941,0,0.10955 +893,208420.2720853,0,0.10839 +894,203541.8401886,0,0.10683 +895,191601.9127364,0,0.10981 +896,194721.8937792,0,0.10129 +897,204667.9871922,0.03196,0.066631 +898,218426.3651337,0.21001,0.059866 +899,230873.9818088,0.37388,0.055309 +900,243944.6716212,0.48347,0.051599 +901,242601.6028587,0.53115,0.042214 +902,232632.4326628,0.46885,0.032306 +903,226037.0881212,0.35631,0.027702 +904,223946.331594,0.21436,0.055438 +905,225787.8588663,0.051394,0.13255 +906,241747.7618928,0,0.17986 +907,261999.9465315,0,0.1646 +908,259369.1932853,0,0.12955 +909,250433.8629616,0,0.098186 +910,244803.1279436,0,0.079143 +911,250004.6348004,0,0.062696 +912,239112.3932901,0,0.045306 +913,235443.1848153,0,0.030371 +914,230500.1379265,0,0.021185 +915,227804.7696884,0,0.01552 +916,227154.0044117,0,0.013464 +917,227818.6157581,0,0.013462 +918,237797.0166671,0,0.016698 +919,269453.7473955,0,0.024059 +920,303828.9231449,0,0.032372 +921,315787.3120233,0.036977,0.022754 +922,319567.2890559,0.21442,0.022034 +923,323836.4938852,0.37718,0.022997 +924,327334.9341669,0.48653,0.021713 +925,325525.7143906,0.53418,0.019708 +926,323070.3446942,0.4816,0.016741 +927,317384.2253973,0.37644,0.012638 +928,313895.0158288,0.23584,0.011193 +929,308379.664725,0.060186,0.021514 +930,317162.6882818,0,0.052125 +931,330865.6819446,0,0.096376 +932,324976.4869585,0,0.12491 +933,309598.1188601,0,0.12925 +934,295341.2824087,0,0.12705 +935,288464.4011162,0,0.11856 +936,270907.5847157,0,0.10543 +937,260481.4942192,0,0.095273 +938,254864.6052709,0,0.091407 +939,253253.8458272,0,0.093878 +940,250650.7847205,0,0.10451 +941,250512.3240234,0,0.11362 +942,257319.9749673,0,0.1093 +943,280142.9132166,0,0.10938 +944,310018.1163081,0,0.11368 +945,320481.1296572,0.031264,0.12125 +946,323181.1132519,0.1565,0.13302 +947,321630.3534437,0.2481,0.17571 +948,321247.2788482,0.28677,0.26911 +949,326019.5575438,0.27784,0.35756 +950,325202.6394305,0.25836,0.42117 +951,322068.812318,0.20852,0.53611 +952,319913.4407988,0.13508,0.69058 +953,314268.8597111,0.033622,0.81231 +954,322673.424029,0,0.87079 +955,333639.5112444,0,0.89939 +956,326573.4003325,0,0.91743 +957,312778.0995383,0,0.9332 +958,301987.3958726,0,0.93773 +959,293070.5269752,0,0.93969 +960,277359.8532036,0,0.94691 +961,266513.765259,0,0.95688 +962,258815.3504967,0,0.96785 +963,255123.0652389,0,0.97785 +964,252653.8494728,0,0.9858 +965,254827.6824183,0,0.99006 +966,261127.6441393,0,0.99175 +967,282838.2814547,0,0.99347 +968,310756.5733597,0,0.99500 +969,319488.8279942,0.017909,0.99586 +970,321981.1205432,0.045648,0.99554 +971,326619.5538982,0.086244,0.99455 +972,330644.1448291,0.11768,0.99318 +973,330127.224893,0.13498,0.99055 +974,327699.5473361,0.10871,0.98271 +975,324441.1055961,0.072472,0.96373 +976,323328.8046622,0.035766,0.92168 +977,317476.5325287,0.0073165,0.86295 +978,324921.1026796,0,0.8013 +979,337507.1800519,0,0.71869 +980,332213.3660636,0,0.63499 +981,316488.8462223,0,0.53098 +982,305028.9158536,0,0.42929 +983,298013.573864,0,0.33365 +984,281910.5947837,0,0.2396 +985,270690.6629568,0,0.18913 +986,263850.7045169,0,0.13279 +987,257389.2053159,0,0.10046 +988,258801.504427,0,0.094549 +989,259747.6525242,0,0.087781 +990,264455.3162278,0,0.066732 +991,287259.7930509,0,0.043741 +992,313258.0966218,0,0.025249 +993,322138.0426666,0.038574,0.012613 +994,326167.2489541,0.16682,0.0070121 +995,332407.2110397,0.26246,0 +996,336210.2648551,0.30676,0 +997,335236.4246184,0.30225,0 +998,333685.6648101,0.24942,0 +999,329014.9239591,0.17473,0 +1000,324496.489875,0.094871,0 +1001,315921.1573639,0.024269,0 +1002,322414.9640609,0,0 +1003,335914.8820345,0,0.010554 +1004,330994.9119286,0,0.018655 +1005,315030.3935455,0,0.032878 +1006,302088.9337172,0,0.065981 +1007,296933.5804261,0,0.10767 +1008,287476.7148097,0,0.11567 +1009,273999.8736191,0,0.13067 +1010,264270.701965,0,0.15051 +1011,261404.5655336,0,0.19231 +1012,257795.3566942,0,0.32727 +1013,257536.8967262,0,0.49564 +1014,261146.1055656,0,0.71932 +1015,281204.4452282,0,0.94118 +1016,306667.3674368,0,0.98983 +1017,316244.232324,0.020502,0.9968 +1018,321330.3552665,0.037821,0.99871 +1019,324565.7202236,0.0712,0.99926 +1020,329014.9239591,0.097888,0.9995 +1021,328807.2329134,0.11349,0.99965 +1022,322747.2697341,0.1235,0.99983 +1023,315025.7781889,0.11604,0.99989 +1024,310387.3448339,0.087382,0.99983 +1025,306344.2924767,0.022641,0.99989 +1026,313867.3236893,0,0.99988 +1027,330002.6102656,0,0.99972 +1028,323264.1896702,0,0.9992 +1029,304982.7622879,0,0.99742 +1030,291552.074663,0,0.99304 +1031,287098.2555708,0,0.98748 +1032,273409.1079778,0,0.98600 +1033,262489.1743281,0,0.99377 +1034,252796.9255266,0,0.99825 +1035,244309.2847904,0,0.99907 +1036,240884.6902138,0,0.99893 +1037,237607.7870476,0,0.99842 +1038,232586.2790971,0,0.99737 +1039,226374.009151,0,0.99455 +1040,230315.5236636,0,0.98926 +1041,243690.8270097,0.044783,0.98142 +1042,259895.3439346,0.16198,0.97361 +1043,270441.4337019,0.26626,0.98376 +1044,276164.4758514,0.33217,0.98409 +1045,275292.1734593,0.3564,0.97444 +1046,267750.6808204,0.28839,0.9524 +1047,259969.1896397,0.19533,0.92227 +1048,257273.8214016,0.10006,0.9275 +1049,254564.6070937,0.027059,0.93685 +1050,266564.5341813,0,0.94868 +1051,285044.4218962,0,0.96706 +1052,277872.1577831,0,0.9758 +1053,262184.5607943,0,0.97737 +1054,254541.5303109,0,0.96533 +1055,256581.5179157,0,0.93082 +1056,249076.9481294,0,0.8869 +1057,236352.41006,0,0.8401 +1058,228667.8413674,0,0.78347 +1059,224827.8646993,0,0.71166 +1060,223314.0277437,0,0.64512 +1061,220009.432438,0,0.60353 +1062,214212.5445834,0,0.60441 +1063,201464.9297311,0,0.64244 +1064,201169.5469105,0,0.67322 +1065,211812.5591659,0.053616,0.63725 +1066,225672.474952,0.19193,0.68778 +1067,238715.4726249,0.24652,0.77676 +1068,250627.7079377,0.22534,0.6052 +1069,247812.3404287,0.14808,0.40805 +1070,237173.9435298,0.13577,0.20333 +1071,229710.9119527,0.10814,0.14622 +1072,226895.5444437,0.070075,0.29857 +1073,226281.7020196,0.076997,0.66026 +1074,238161.6298363,0.0055329,0.79581 +1075,263236.8620928,0,0.77152 +1076,262983.0174813,0,0.73372 +1077,254703.0677909,0,0.70993 +1078,251264.6271446,0,0.70185 +1079,258206.1234291,0,0.69615 +1080,249598.4834221,0,0.67669 +1081,241812.3768849,0,0.66797 +1082,238650.8576329,0,0.69054 +1083,235470.8769547,0,0.73927 +1084,233029.353328,0,0.78253 +1085,232983.1997623,0,0.82125 +1086,238217.0141151,0,0.83858 +1087,254103.0714365,0,0.8254 +1088,280516.7570989,0,0.77779 +1089,294833.5931858,0.042624,0.73732 +1090,303976.6145552,0.1133,0.80965 +1091,308596.5864839,0.15541,0.89883 +1092,313055.0209326,0.15767,0.92733 +1093,311176.5708078,0.1288,0.93837 +1094,306967.3656139,0.11959,0.94176 +1095,301641.2441297,0.097042,0.92555 +1096,298355.1102503,0.06438,0.84339 +1097,293301.2948038,0.079605,0.76822 +1098,299416.6422619,0.0085579,0.71133 +1099,317550.3782339,0,0.63994 +1100,314259.628998,0,0.5712 +1101,301193.5545422,0,0.49918 +1102,290716.6951234,0,0.45381 +1103,285602.8800414,0,0.43966 +1104,270012.2055407,0,0.4217 +1105,257283.0521147,0,0.41591 +1106,252183.0831025,0,0.42299 +1107,250175.4029936,0,0.43938 +1108,247036.9605245,0,0.42674 +1109,249376.9463066,0,0.41365 +1110,252524.6194888,0,0.4199 +1111,272762.9580577,0,0.41506 +1112,297510.4999976,0,0.36900 +1113,306238.1392755,0.081744,0.40949 +1114,310913.4954831,0.2881,0.4681 +1115,313913.477255,0.42819,0.47437 +1116,316641.1529892,0.49632,0.42541 +1117,312136.5649748,0.49812,0.36621 +1118,304927.3780091,0.38474,0.3398 +1119,297164.3482547,0.24196,0.33337 +1120,293781.2918873,0.10755,0.27832 +1121,291441.3061052,0.09667,0.25284 +1122,300796.633877,0.010409,0.26659 +1123,323490.3421423,0,0.20693 +1124,322456.5022701,0,0.1413 +1125,308236.5886713,0,0.088752 +1126,296476.6601254,0,0.064382 +1127,291044.38544,0,0.04836 +1128,274862.945298,0,0.035668 +1129,266509.1499024,0,0.029707 +1130,263218.4006665,0,0.026465 +1131,257047.6689295,0,0.022317 +1132,253581.5361438,0,0.017363 +1133,255358.4484241,0,0.014111 +1134,259378.4239985,0,0.010876 +1135,280987.5234693,0,0.0078298 +1136,306482.7531739,0,0.0066513 +1137,313382.7112492,0.061254,0.010076 +1138,315528.8520553,0.17214,0.010433 +1139,315953.4648599,0.24596,0.010897 +1140,319248.8294524,0.27117,0.01341 +1141,317670.3775048,0.25405,0.016999 +1142,314601.1653843,0.22845,0.018716 +1143,312191.9492536,0.17983,0.018907 +1144,310391.9601905,0.11671,0.0145 +1145,307045.8266757,0.10273,0.00939 +1146,314421.166478,0.011819,0.006899 +1147,333048.7456032,0,0.0057923 +1148,327953.3919475,0,0 +1149,311490.4150547,0,0 +1150,298913.5683955,0,0 +1151,289401.3185003,0,0 +1152,272564.4977251,0,0.0078388 +1153,259752.2678808,0,0.016677 +1154,251135.3971606,0,0.041941 +1155,247013.8837417,0,0.090542 +1156,244909.2811448,0,0.11761 +1157,248804.6420917,0,0.11695 +1158,253535.3825781,0,0.12863 +1159,277161.392871,0,0.16996 +1160,304756.6098159,0,0.20804 +1161,313318.0962572,0.052739,0.27426 +1162,316894.9976006,0.11087,0.46292 +1163,322424.1947741,0.13918,0.63434 +1164,325064.1787334,0.12693,0.7672 +1165,323536.495708,0.085728,0.8412 +1166,318971.9080581,0.097167,0.89223 +1167,313604.2483647,0.094888,0.90561 +1168,308471.9718565,0.076045,0.91022 +1169,302250.4711972,0.090645,0.92091 +1170,309238.1210474,0.014315,0.91899 +1171,328008.7762264,0,0.90386 +1172,325299.5619185,0,0.85069 +1173,309182.7367686,0,0.69894 +1174,296292.0458626,0,0.44442 +1175,287052.1020051,0,0.20669 +1176,269942.9751921,0,0.089032 +1177,257910.7406085,0,0.053042 +1178,251776.9317241,0,0.037535 +1179,247816.9557852,0,0.034418 +1180,246400.0413176,0,0.041894 +1181,248578.4896196,0,0.061951 +1182,251689.2399493,0,0.084972 +1183,274046.0271848,0,0.091293 +1184,303307.3878522,0,0.086464 +1185,314984.2399798,0.071779,0.08097 +1186,317707.3003573,0.1828,0.088501 +1187,320568.8214321,0.29496,0.13105 +1188,322211.8883718,0.37797,0.24844 +1189,318445.7574089,0.42293,0.41999 +1190,312210.4106799,0.36481,0.55214 +1191,306261.2160584,0.27411,0.51937 +1192,302499.7004521,0.16862,0.49094 +1193,298955.1066047,0.1261,0.52201 +1194,307156.5952334,0.017967,0.52381 +1195,322271.8880072,0,0.55735 +1196,317231.9186304,0,0.59334 +1197,297265.8860993,0,0.62059 +1198,283835.1984743,0,0.59213 +1199,278518.3077032,0,0.53365 +1200,262553.7893201,0,0.44153 +1201,249123.1016951,0,0.32232 +1202,243072.3692291,0,0.20781 +1203,237487.7877767,0,0.11773 +1204,235087.8023592,0,0.081742 +1205,237321.6349401,0,0.072015 +1206,233777.0410927,0,0.068358 +1207,228243.2285627,0,0.062963 +1208,235272.4166221,0,0.050316 +1209,248670.7967511,0.068176,0.048015 +1210,258496.8908932,0.1542,0.053023 +1211,263643.0134711,0.2082,0.04553 +1212,266956.8394899,0.21858,0.040681 +1213,270072.2051762,0.19306,0.040083 +1214,263190.7085271,0.24162,0.042439 +1215,256184.5972505,0.25494,0.043631 +1216,253567.6900741,0.22034,0.046531 +1217,252229.2366682,0.14796,0.067841 +1218,262023.0233143,0.023384,0.0919 +1219,284772.1158585,0,0.10182 +1220,282229.0543872,0,0.10553 +1221,265323.0032634,0,0.10678 +1222,256553.8257763,0,0.11076 +1223,255663.0619579,0,0.11607 +1224,243806.210924,0,0.12386 +1225,232867.815848,0,0.13163 +1226,221763.2679354,0,0.13235 +1227,217046.3735187,0,0.13525 +1228,215024.8473401,0,0.14218 +1229,212384.8633808,0,0.17417 +1230,210441.7982639,0,0.23358 +1231,201501.8525837,0,0.30668 +1232,204931.0625168,0,0.33678 +1233,210940.2567737,0.089176,0.31087 +1234,218707.9018847,0.2104,0.38126 +1235,228580.1495925,0.33335,0.42922 +1236,238987.7786627,0.42379,0.38355 +1237,236592.4086017,0.47218,0.34032 +1238,227181.6965511,0.41035,0.35899 +1239,219778.6646094,0.31248,0.42372 +1240,217226.372425,0.19659,0.54876 +1241,219206.3603944,0.14489,0.75555 +1242,233260.1211567,0.025592,0.84461 +1243,262466.0975452,0,0.86634 +1244,263296.8617282,0,0.87257 +1245,254619.9913726,0,0.8771 +1246,249884.6355295,0,0.87953 +1247,252460.0044968,0,0.88463 +1248,240197.0020846,0,0.88793 +1249,231533.9777987,0,0.88462 +1250,226780.1605293,0,0.88445 +1251,226466.3162824,0,0.88003 +1252,226447.8548562,0,0.86189 +1253,230426.2922213,0,0.84499 +1254,236786.2535778,0,0.85841 +1255,269633.7463018,0,0.86419 +1256,299578.1797419,0,0.84412 +1257,310567.3437402,0.10137,0.85092 +1258,312828.8684606,0.24823,0.87938 +1259,314735.0107249,0.31713,0.88779 +1260,319078.0612593,0.31668,0.87301 +1261,317125.7654293,0.26155,0.85509 +1262,313405.7880321,0.2358,0.8387 +1263,307964.2826335,0.18755,0.79802 +1264,304133.5366786,0.12518,0.71986 +1265,299181.2590767,0.12042,0.73029 +1266,307535.0544723,0.025114,0.73738 +1267,330634.914116,0,0.68774 +1268,327634.9323441,0,0.65691 +1269,310784.2654991,0,0.62798 +1270,296698.1972409,0,0.58291 +1271,287421.3305309,0,0.51167 +1272,270427.5876322,0,0.44372 +1273,256203.0586768,0,0.41657 +1274,250253.8640553,0,0.39671 +1275,247235.4208571,0,0.36433 +1276,246353.8877519,0,0.35384 +1277,249423.0998723,0,0.37612 +1278,254961.5277589,0,0.36906 +1279,281430.5977002,0,0.3633 +1280,306861.2124128,0,0.34676 +1281,315616.5438302,0.087131,0.39303 +1282,316064.2334177,0.16582,0.42598 +1283,318307.2967117,0.22117,0.36177 +1284,322165.734806,0.2345,0.27887 +1285,320176.5161235,0.21296,0.24735 +1286,318482.6802615,0.17437,0.23397 +1287,313650.4019304,0.12202,0.19509 +1288,310978.1104751,0.067759,0.15207 +1289,304622.7644753,0.10381,0.10766 +1290,308924.2768005,0.028219,0.066235 +1291,326185.7103804,0,0.034122 +1292,323905.7242338,0,0.017014 +1293,306413.5228253,0,0.0088359 +1294,291427.4600355,0,0.0064066 +1295,281144.4455927,0,0 +1296,262867.633567,0,0 +1297,247886.1861338,0,0 +1298,240635.460959,0,0 +1299,238184.7066191,0,0.0083482 +1300,236633.9468109,0,0.01533 +1301,239467.7757462,0,0.030319 +1302,248329.2603647,0,0.054336 +1303,275795.2473256,0,0.080792 +1304,303478.1560454,0,0.096778 +1305,312168.8724708,0.09779,0.11844 +1306,310433.4983996,0.18777,0.14364 +1307,311181.1861643,0.32273,0.17041 +1308,313248.8659086,0.44328,0.18427 +1309,310387.3448339,0.52956,0.18963 +1310,306828.9049168,0.50562,0.19487 +1311,301092.0166976,0.43323,0.17559 +1312,298982.7987441,0.31863,0.12101 +1313,294888.9774646,0.20325,0.10474 +1314,302296.6247629,0.040373,0.11437 +1315,323208.8053914,0,0.11452 +1316,322664.1933158,0,0.096189 +1317,307193.518086,0,0.072987 +1318,291861.3035533,0,0.060827 +1319,279745.9925514,0,0.052717 +1320,260153.8039026,0,0.03805 +1321,241840.0690243,0,0.028668 +1322,237026.2521195,0,0.021124 +1323,236666.2543069,0,0.016433 +1324,239066.2397244,0,0.014416 +1325,243146.2149342,0,0.013786 +1326,249815.405181,0,0.012884 +1327,276224.4754868,0,0.011196 +1328,301539.7062851,0.001903,0.0088131 +1329,309455.0428063,0.11446,0.011611 +1330,306699.6749328,0.23572,0.011777 +1331,308167.3583227,0.28794,0.0088502 +1332,306939.6734745,0.27587,0.0057587 +1333,305291.9911783,0.21572,0 +1334,304313.535585,0.19464,0.0064622 +1335,299379.7194093,0.15553,0.011186 +1336,298493.5709475,0.1051,0.018315 +1337,295405.8974007,0.12253,0.030025 +1338,304295.0741587,0.035954,0.04079 +1339,322724.1929513,0,0.044343 +1340,320287.2846812,0,0.042613 +1341,305273.529752,0,0.038156 +1342,290947.462952,0,0.029626 +1343,280442.9113938,0,0.019574 +1344,259456.8850602,0,0.015715 +1345,243663.1348703,0,0.013576 +1346,234021.6549911,0,0.013103 +1347,232207.8198582,0,0.014221 +1348,232840.1237086,0,0.016129 +1349,235664.7219307,0,0.017386 +1350,242140.0672015,0,0.016446 +1351,265899.9228349,0,0.015124 +1352,292438.2231248,0.0023443,0.012777 +1353,304401.2273598,0.10677,0.016504 +1354,306062.7557258,0.1948,0.023935 +1355,306210.4471361,0.23141,0.023728 +1356,307442.7473409,0.21291,0.025872 +1357,304151.9981049,0.15436,0.034662 +1358,298368.95632,0.13011,0.059675 +1359,293236.6798118,0.095269,0.13576 +1360,291565.9207327,0.057103,0.21584 +1361,289184.3967414,0.10702,0.37181 +1362,294838.2085423,0.036902,0.58635 +1363,311610.4143255,0,0.69888 +1364,309067.3528543,0,0.7637 +1365,294722.824628,0,0.8084 +1366,281749.0573037,0,0.84968 +1367,274170.6418122,0,0.85138 +1368,258224.5848554,0,0.82315 +1369,247198.4980046,0,0.77658 +1370,238853.9333221,0,0.70015 +1371,239758.5432102,0,0.61104 +1372,240746.2295167,0,0.51738 +1373,240229.3095806,0,0.45548 +1374,234381.6528037,0,0.42165 +1375,231192.4414123,0,0.41729 +1376,235535.4919467,0.0070995,0.42897 +1377,246695.4241382,0.17129,0.67126 +1378,255127.6805955,0.38529,0.72581 +1379,257610.7424313,0.51695,0.6988 +1380,258141.5084371,0.57552,0.68341 +1381,255363.0637807,0.56655,0.70071 +1382,248943.1027888,0.49388,0.73953 +1383,238567.7812146,0.38119,0.77791 +1384,237510.8645596,0.24775,0.75792 +1385,238341.6287426,0.18758,0.62536 +1386,246672.3473553,0.049792,0.5739 +1387,270399.8954928,0,0.53254 +1388,271502.9657135,0,0.45955 +1389,254329.2239085,0,0.36827 +1390,241936.9915123,0,0.28836 +1391,239532.3907382,0,0.20868 +1392,226923.2365831,0,0.13834 +1393,212255.6333968,0,0.08291 +1394,199794.170652,0,0.046242 +1395,194634.2020043,0,0.024793 +1396,196724.9585315,0,0.012835 +1397,201474.1604442,0,0.0092142 +1398,201728.0050557,0,0.014165 +1399,197020.3413521,0,0.02765 +1400,197209.5709716,0.008643,0.039367 +1401,207917.198219,0.18088,0.055608 +1402,220526.3523741,0.39447,0.068844 +1403,232230.8966411,0.55421,0.085242 +1404,242080.067566,0.65232,0.11202 +1405,239998.541752,0.68775,0.16583 +1406,232992.4304755,0.65456,0.25136 +1407,228760.1484988,0.5625,0.35868 +1408,229152.4538074,0.41965,0.41041 +1409,233975.5014253,0.26007,0.42637 +1410,244503.1297664,0.064333,0.56707 +1411,267792.2190295,0,0.69843 +1412,274152.1803859,0,0.7846 +1413,264053.7802061,0,0.84165 +1414,260370.7256615,0,0.88502 +1415,263509.1681305,0,0.92341 +1416,251227.704292,0,0.94609 +1417,244207.7469458,0,0.95654 +1418,239121.6240033,0,0.95992 +1419,235941.6433251,0,0.95978 +1420,237501.6338465,0,0.96131 +1421,242398.5271695,0,0.96111 +1422,253406.1525941,0,0.95596 +1423,283784.429552,0,0.95076 +1424,308998.1225057,0.0085208,0.94361 +1425,315150.3928164,0.17189,0.93757 +1426,314245.7829282,0.36598,0.93252 +1427,314236.5522151,0.42831,0.93346 +1428,316784.2290429,0.39844,0.9231 +1429,314342.7054162,0.29974,0.91246 +1430,312579.6392057,0.28376,0.89655 +1431,309155.0446291,0.24117,0.85725 +1432,307567.3619683,0.17662,0.79015 +1433,304987.3776445,0.16111,0.77048 +1434,311956.5660684,0.051391,0.80556 +1435,330847.2205183,0,0.84522 +1436,335850.2670425,0,0.86368 +1437,318874.9855701,0,0.86693 +1438,304779.6865987,0,0.84335 +1439,295710.5109345,0,0.79444 +1440,276459.858672,0,0.72525 +1441,264783.0065445,0,0.64017 +1442,260823.0306056,0,0.54313 +1443,258792.2737138,0,0.44044 +1444,257753.8184851,0,0.36801 +1445,258058.4320188,0,0.32473 +1446,264658.391917,0,0.28553 +1447,293993.5982896,0,0.25114 +1448,319322.6751576,0.011055,0.21738 +1449,328696.4643556,0.14129,0.18203 +1450,327205.7041828,0.25091,0.16839 +1451,325281.1004923,0.33531,0.14504 +1452,327833.3926767,0.37314,0.11621 +1453,327307.2420274,0.36799,0.087539 +1454,324953.4101756,0.31512,0.060723 +1455,320462.6682309,0.23765,0.03974 +1456,317236.533987,0.15036,0.021899 +1457,312191.9492536,0.15598,0.010801 +1458,313576.5562253,0.054659,0 +1459,331977.9828784,0,0 +1460,335711.8063453,0,0 +1461,318542.6798969,0,0 +1462,303870.461354,0,0 +1463,292959.7584175,0,0.0060375 +1464,273690.6447287,0,0.010231 +1465,263416.8609991,0,0.01909 +1466,256812.2857444,0,0.034183 +1467,255866.1376471,0,0.057938 +1468,255893.8297865,0,0.090809 +1469,259918.4207174,0,0.1165 +1470,264519.9312198,0,0.15286 +1471,290301.3130319,0,0.21223 +1472,311361.1850706,0.010453,0.22568 +1473,317984.2217517,0.1352,0.31207 +1474,314933.4710575,0.25756,0.43093 +1475,313913.477255,0.32669,0.4826 +1476,313179.6355601,0.34314,0.51833 +1477,307479.6701935,0.31482,0.54637 +1478,304604.303049,0.33015,0.60076 +1479,302148.9333526,0.31058,0.64409 +1480,302245.8558406,0.25328,0.63298 +1481,302033.5494383,0.18206,0.55928 +1482,311956.5660684,0.0557,0.55232 +1483,334613.3514811,0,0.50248 +1484,337437.9497033,0,0.40544 +1485,320887.2810356,0,0.29197 +1486,305638.1429212,0,0.19745 +1487,294930.5156738,0,0.12968 +1488,276289.0904788,0,0.08145 +1489,264183.0101901,0,0.046502 +1490,259955.34357,0,0.023287 +1491,257809.2027639,0,0.011419 +1492,257887.6638257,0,0 +1493,260107.6503369,0,0 +1494,268124.5247027,0,0 +1495,293508.9858495,0,0.0069026 +1496,314222.7061454,0.014423,0.013407 +1497,320384.2071692,0.14649,0.022556 +1498,318602.6795323,0.27677,0.035018 +1499,317504.2246682,0.3695,0.069999 +1500,318445.7574089,0.41651,0.10652 +1501,313465.7876675,0.41876,0.11449 +1502,310835.0344214,0.38045,0.095221 +1503,306708.9056459,0.31027,0.068568 +1504,305347.3754571,0.21855,0.051463 +1505,304271.9973758,0.17038,0.041503 +1506,307973.5133467,0.056764,0.049184 +1507,326291.8635816,0,0.065072 +1508,331857.9836076,0,0.084415 +1509,315875.0037982,0,0.097748 +1510,302195.0869183,0,0.11815 +1511,293624.3697639,0,0.13044 +1512,277562.9288928,0,0.11987 +1513,262858.4028539,0,0.098313 +1514,257878.4331125,0,0.076355 +1515,251989.2381265,0,0.062608 +1516,250696.9382862,0,0.048866 +1517,253295.3840364,0,0.044733 +1518,259544.5768351,0,0.055063 +1519,284725.9622927,0,0.078549 +1520,304327.3816547,0.017602,0.090235 +1521,310055.0391607,0.17168,0.12486 +1522,307830.4372929,0.33788,0.27143 +1523,308458.1257868,0.46742,0.44225 +1524,312215.0260365,0.54919,0.5422 +1525,308384.2800816,0.58003,0.59865 +1526,303312.0032088,0.53959,0.66100 +1527,298332.0334674,0.45369,0.69912 +1528,295659.7420122,0.33222,0.69347 +1529,296938.1957827,0.22101,0.73499 +1530,307544.2851855,0.069431,0.82368 +1531,327108.7816948,0,0.83454 +1532,329702.6120884,0,0.78833 +1533,308416.5875776,0,0.71366 +1534,291348.9989738,0,0.66029 +1535,282584.4368433,0,0.62669 +1536,265267.6189845,0,0.59537 +1537,251403.0878418,0,0.5668 +1538,241503.1479945,0,0.53895 +1539,236103.1808051,0,0.50518 +1540,239490.8525291,0,0.43735 +1541,240293.9245726,0,0.37718 +1542,237649.3252568,0,0.34827 +1543,228390.919973,0,0.30907 +1544,232152.4355793,0.018947,0.20493 +1545,247083.1140903,0.13719,0.18019 +1546,260790.7231095,0.22813,0.30376 +1547,266495.3038327,0.27295,0.31565 +1548,269721.4380767,0.26928,0.33527 +1549,264335.316957,0.22735,0.33203 +1550,254643.0681554,0.20406,0.33328 +1551,248153.876815,0.1639,0.26771 +1552,246963.1148194,0.11342,0.14252 +1553,247936.9550561,0.13174,0.081375 +1554,254873.835984,0.058049,0.0538 +1555,276916.7789727,0,0.033908 +1556,282561.3600604,0,0.022171 +1557,267635.2969061,0,0.015405 +1558,257010.746077,0,0.012884 +1559,259350.731859,0,0.013748 +1560,250613.8618679,0,0.015677 +1561,239546.2368079,0,0.017222 +1562,232332.4344856,0,0.019118 +1563,228164.767501,0,0.02281 +1564,224749.4036376,0,0.031017 +1565,223724.7944786,0,0.05588 +1566,219644.8192688,0,0.1067 +1567,208277.1960316,0,0.17618 +1568,208175.658187,0.019971,0.22065 +1569,218015.5983988,0.12631,0.22828 +1570,226050.9341909,0.18654,0.26094 +1571,231898.5909679,0.23793,0.39591 +1572,237755.4784579,0.25647,0.48545 +1573,233883.1942939,0.24521,0.49749 +1574,222626.3396144,0.20347,0.48697 +1575,216174.0711265,0.1475,0.47168 +1576,214960.2323481,0.088579,0.49657 +1577,218532.5183349,0.12384,0.52542 +1578,232590.8944537,0.059699,0.54069 +1579,259156.886883,0,0.51452 +1580,272342.9606096,0,0.48474 +1581,263546.0909831,0,0.49128 +1582,257776.8952679,0,0.52846 +1583,263703.0131066,0,0.54735 +1584,253839.9961119,0,0.5483 +1585,246233.888481,0,0.52801 +1586,244023.1326829,0,0.49977 +1587,244055.4401789,0,0.48531 +1588,242449.2960918,0,0.47488 +1589,246529.2713016,0,0.49083 +1590,258173.8159331,0,0.50208 +1591,285676.7257466,0,0.47397 +1592,306418.1381819,0.024618,0.43106 +1593,315575.005621,0.1366,0.40765 +1594,314961.1631969,0.20357,0.49758 +1595,315611.9284736,0.25719,0.70305 +1596,318824.2166478,0.27516,0.81669 +1597,316387.3083777,0.26143,0.8687 +1598,314167.3218665,0.22424,0.87605 +1599,309468.8888761,0.1708,0.83818 +1600,307221.2102254,0.11072,0.71519 +1601,304885.8397999,0.13658,0.64578 +1602,310488.8826785,0.067159,0.60322 +1603,325451.8686854,0,0.51607 +1604,335231.8092618,0,0.41487 +1605,319516.5201336,0,0.32368 +1606,304105.8445392,0,0.25494 +1607,295235.1292075,0,0.24601 +1608,280479.8342464,0,0.28303 +1609,270446.0490585,0,0.33657 +1610,265452.2332474,0,0.38433 +1611,261035.3370079,0,0.41473 +1612,260343.033522,0,0.42523 +1613,264030.7034232,0,0.43766 +1614,270012.2055407,0,0.4477 +1615,292885.9127123,0,0.4297 +1616,313415.0187452,0.026867,0.35801 +1617,317421.1482499,0.13598,0.26541 +1618,314273.4750677,0.19232,0.22117 +1619,314075.0147351,0.25659,0.28151 +1620,315371.9299318,0.29471,0.3372 +1621,313885.7851156,0.30422,0.33635 +1622,312561.1777794,0.33097,0.32988 +1623,309095.0449937,0.32338,0.31472 +1624,308218.127245,0.27658,0.26527 +1625,308707.3550416,0.20998,0.23861 +1626,314785.7796472,0.083695,0.30092 +1627,331317.9868886,0,0.31559 +1628,342007.1527097,0,0.28042 +1629,326758.0145953,0,0.21717 +1630,312838.0991737,0,0.13892 +1631,305564.297216,0,0.077219 +1632,290952.0783086,0,0.041111 +1633,266352.227779,0,0.023904 +1634,260389.1870878,0,0.014487 +1635,255473.8323384,0,0.0077339 +1636,253964.6107393,0,0 +1637,256198.4433203,0,0 +1638,263338.3999374,0,0 +1639,287139.79378,0,0 +1640,307355.055566,0.039743,0 +1641,314656.5496632,0.217000,0 +1642,319008.8309107,0.39157,0 +1643,325673.4058009,0.52342,0.0057373 +1644,325031.8712374,0.6058,0.0093689 +1645,320111.9011314,0.6361,0.019276 +1646,316331.9240988,0.55255,0.044557 +1647,306238.1392755,0.42965,0.09963 +1648,307821.2065798,0.28718,0.13311 +1649,306002.7560904,0.21888,0.1389 +1650,309058.1221411,0.089676,0.19436 +1651,324376.4906041,0,0.25101 +1652,333002.5920375,0,0.29361 +1653,315810.3888062,0,0.35377 +1654,299928.9468414,0,0.43721 +1655,293485.9090667,0,0.5416 +1656,278278.3091614,0,0.65326 +1657,267829.1418821,0,0.76527 +1658,262650.7118081,0,0.86898 +1659,257795.3566942,0,0.93701 +1660,256863.0546666,0,0.96478 +1661,260467.6481495,0,0.97915 +1662,267547.6051312,0,0.98849 +1663,291685.9200035,0,0.99317 +1664,313156.5587772,0.030796,0.99563 +1665,323914.9549469,0.1081,0.99687 +1666,326434.9396353,0.09808,0.99762 +1667,328313.3897602,0.11968,0.99785 +1668,331101.0651297,0.12335,0.99804 +1669,327016.4745634,0.11179,0.99787 +1670,326601.0924719,0.10834,0.99652 +1671,320725.7435555,0.094779,0.99438 +1672,317550.3782339,0.072653,0.99065 +1673,311873.4896501,0.12724,0.98684 +1674,315510.390629,0.076704,0.98237 +1675,329536.4592518,0,0.97044 +1676,339141.0162784,0,0.96381 +1677,324136.4920624,0,0.96362 +1678,309118.1217766,0,0.96831 +1679,299592.0258116,0,0.97508 +1680,282893.6657336,0,0.98088 +1681,272384.4988188,0,0.98491 +1682,263559.9370528,0,0.98678 +1683,257103.0532084,0,0.98569 +1684,255515.3705476,0,0.98399 +1685,256489.2107843,0,0.98341 +1686,264330.7016004,0,0.98475 +1687,285949.0317844,0,0.98686 +1688,305342.7601005,0.04215,0.98761 +1689,317914.9914031,0.18894,0.98858 +1690,321607.2766608,0.3028,0.98801 +1691,323374.958228,0.3277,0.98517 +1692,323434.9578634,0.28926,0.98406 +1693,318681.1405941,0.20512,0.97882 +1694,310622.7280191,0.21551,0.9549 +1695,301498.1680759,0.20453,0.92582 +1696,294999.7460224,0.17065,0.90258 +1697,291644.3817944,0.17132,0.87102 +1698,294030.5211422,0.088269,0.80735 +1699,305038.1465668,0,0.69846 +1700,307442.7473409,0,0.53558 +1701,290167.4676913,0,0.34611 +1702,277059.8550264,0,0.20185 +1703,271876.8095959,0,0.11164 +1704,257564.5888656,0,0.05985 +1705,241263.1494528,0,0.036281 +1706,231912.4370376,0,0.03605 +1707,224934.0179005,0,0.050015 +1708,223581.7184248,0,0.080352 +1709,224306.3294067,0,0.15037 +1710,222127.8811046,0,0.28042 +1711,218435.5958469,0,0.42402 +1712,223078.6445585,0.045982,0.48784 +1713,238913.9329575,0.17694,0.58447 +1714,251458.4721207,0.26204,0.69205 +1715,258889.1962018,0.32044,0.69046 +1716,261916.8701132,0.33646,0.73211 +1717,260310.726026,0.31615,0.79065 +1718,253738.4582673,0.28167,0.83749 +1719,248176.9535979,0.22624,0.82021 +1720,245375.4321586,0.15844,0.67341 +1721,246150.8120627,0.16844,0.49042 +1722,252552.3116283,0.091244,0.43917 +1723,264723.006909,0,0.41361 +1724,266582.9956076,0,0.39426 +1725,250507.7086668,0,0.34978 +1726,242578.5260758,0,0.31386 +1727,242416.9885958,0,0.30477 +1728,230753.982538,0,0.31402 +1729,223189.4131162,0,0.31871 +1730,212758.7072631,0,0.32023 +1731,204940.2932299,0,0.32155 +1732,202687.9992227,0,0.31104 +1733,201524.9293665,0,0.29857 +1734,199244.9432199,0,0.31162 +1735,192178.8323079,0,0.29676 +1736,192580.3683297,0.055846,0.19497 +1737,205424.90567,0.22164,0.20329 +1738,217812.5227097,0.35995,0.28205 +1739,226484.7777087,0.43177,0.33985 +1740,237390.8652887,0.44819,0.37373 +1741,239033.9322284,0.41771,0.3965 +1742,230112.4479744,0.34117,0.41517 +1743,223978.6390901,0.24352,0.41491 +1744,221597.1150988,0.14356,0.35507 +1745,220941.7344656,0.16209,0.21238 +1746,228755.5331422,0.092373,0.17294 +1747,245749.2760409,0.0012441,0.1645 +1748,254172.3017851,0,0.1447 +1749,243155.4456473,0,0.11732 +1750,237007.7906932,0,0.097212 +1751,238530.8583621,0,0.081055 +1752,228533.9960268,0,0.068748 +1753,222266.3418018,0,0.082553 +1754,216104.840778,0,0.13704 +1755,217161.757433,0,0.20379 +1756,218670.9790321,0,0.27066 +1757,225280.1696434,0,0.36852 +1758,236772.4075081,0,0.50967 +1759,265752.2314246,0,0.64435 +1760,289452.0874226,0.057462,0.66429 +1761,302365.8551115,0.19449,0.71614 +1762,304922.7626525,0.28346,0.81977 +1763,307585.8233946,0.37622,0.92343 +1764,312358.1020902,0.43723,0.95147 +1765,312044.2578433,0.46291,0.95229 +1766,313128.8666378,0.39242,0.9444 +1767,311919.6432159,0.29612,0.92234 +1768,310239.6534236,0.19109,0.8819 +1769,306205.8317795,0.18668,0.87089 +1770,307876.5908587,0.10144,0.88467 +1771,321016.5110196,0.0017624,0.90152 +1772,325276.4851357,0,0.91015 +1773,306228.9085624,0,0.91578 +1774,289022.8592614,0,0.93801 +1775,277498.3139007,0,0.96093 +1776,258718.4280087,0,0.97021 +1777,248001.5700481,0,0.97112 +1778,241373.9180105,0,0.96789 +1779,239652.3900091,0,0.96629 +1780,240796.998439,0,0.96838 +1781,245366.2014454,0,0.9673 +1782,251832.316003,0,0.96199 +1783,275541.4027142,0,0.95558 +1784,297625.8839119,0.065024,0.93857 +1785,309408.8892406,0.22647,0.94477 +1786,309035.0453583,0.3575,0.96384 +1787,310188.8845013,0.43501,0.98032 +1788,313590.402295,0.46285,0.98699 +1789,311315.0315049,0.44622,0.98795 +1790,309168.8906989,0.45093,0.98749 +1791,305545.8357897,0.41789,0.97663 +1792,304511.9959176,0.34594,0.94557 +1793,300838.1720861,0.25624,0.94142 +1794,303459.6946191,0.119000,0.96125 +1795,315071.9317546,0.0038619,0.97121 +1796,323753.4174669,0,0.97456 +1797,306981.2116837,0,0.97543 +1798,293135.1419672,0,0.98131 +1799,283152.1257016,0,0.98694 +1800,264404.5473055,0,0.99107 +1801,252141.5448933,0,0.99328 +1802,246441.5795267,0,0.99369 +1803,242841.6014004,0,0.99305 +1804,243150.8302908,0,0.98735 +1805,246676.9627119,0,0.98099 +1806,253479.9982993,0,0.9807 +1807,276686.011144,0,0.98453 +1808,297985.8817245,0.055887,0.98447 +1809,307613.515534,0.13119,0.9854 +1810,305596.604712,0.11573,0.98675 +1811,304101.2291826,0.15075,0.99004 +1812,305061.2233496,0.17091,0.99237 +1813,297533.5767805,0.17548,0.99431 +1814,293236.6798118,0.15049,0.99299 +1815,289138.2431757,0.11495,0.98891 +1816,289184.3967414,0.075575,0.9803 +1817,287204.408772,0.083335,0.97109 +1818,293264.3719512,0.048585,0.9523 +1819,309155.0446291,0.00070852,0.91055 +1820,320458.0528744,0,0.8493 +1821,303515.078898,0,0.8221 +1822,284869.0383465,0,0.81326 +1823,270146.0508813,0,0.80874 +1824,249016.948494,0,0.79842 +1825,234833.9577478,0,0.78741 +1826,229877.0647893,0,0.76231 +1827,228510.9192439,0,0.71700 +1828,230841.6743128,0,0.6815 +1829,235807.7979845,0,0.61256 +1830,245643.1228397,0,0.58785 +1831,268106.0632764,0,0.65251 +1832,288196.710435,0.062591,0.72611 +1833,296961.2725655,0.14607,0.78603 +1834,293910.5218713,0.14147,0.86821 +1835,292802.836294,0.17517,0.91819 +1836,294441.2878771,0.18905,0.92505 +1837,290716.6951234,0.18435,0.9224 +1838,288169.0182956,0.1662,0.9092 +1839,284615.193735,0.13657,0.88396 +1840,284107.5045121,0.098506,0.83436 +1841,283909.0441795,0.14062,0.78115 +1842,290601.3112091,0.093804,0.69824 +1843,305148.9151245,0.0048182,0.61865 +1844,316728.8447641,0,0.55783 +1845,298641.2623578,0,0.52063 +1846,280535.2185252,0,0.50377 +1847,264501.4697936,0,0.46983 +1848,243681.5962966,0,0.4187 +1849,226895.5444437,0,0.33417 +1850,218334.0580023,0,0.21976 +1851,217424.8327576,0,0.12975 +1852,218467.9033429,0,0.07301 +1853,223129.4134808,0,0.040784 +1854,233366.2743578,0,0.02586 +1855,255487.6784081,0,0.024512 +1856,278882.9208724,0.071439,0.032656 +1857,293476.6783535,0.1624,0.047924 +1858,295908.9712671,0.17111,0.065704 +1859,296227.4308705,0.21132,0.07998 +1860,294805.9010463,0.22797,0.080586 +1861,288487.477899,0.22337,0.071451 +1862,279875.2225354,0.19986,0.073898 +1863,271327.5821638,0.16162,0.090563 +1864,269790.6684252,0.11546,0.10275 +1865,270192.204447,0.13859,0.11888 +1866,276990.6246778,0.088639,0.15387 +1867,287005.9484394,0.0060818,0.22915 +1868,299647.4100905,0,0.27567 +1869,281481.3666225,0,0.28934 +1870,262406.0979098,0,0.29677 +1871,251536.9331824,0,0.27959 +1872,231506.2856592,0,0.25829 +1873,213063.3207969,0,0.24132 +1874,205706.4424209,0,0.21845 +1875,202083.3875118,0,0.19419 +1876,206144.9012952,0,0.17975 +1877,208941.807378,0,0.17972 +1878,209615.6494375,0,0.18536 +1879,208231.0424659,0,0.15009 +1880,216847.9131861,0.099548,0.11558 +1881,231427.8245975,0.28875,0.2182 +1882,241775.4540323,0.45758,0.23368 +1883,246132.3506364,0.54939,0.19288 +1884,247221.5747874,0.5844,0.16598 +1885,241946.2222254,0.56786,0.14754 +1886,234095.5006962,0.52268,0.12635 +1887,227223.2347603,0.44159,0.10397 +1888,227758.6161226,0.33334,0.09322 +1889,227929.3843158,0.26286,0.097133 +1890,236287.795068,0.13697,0.16057 +1891,247784.6482892,0.0097739,0.25255 +1892,253987.6875222,0,0.29127 +1893,239389.3146845,0,0.29605 +1894,228063.2296564,0,0.30862 +1895,223895.5626718,0,0.30241 +1896,209647.9569335,0,0.28062 +1897,196369.5760754,0,0.26682 +1898,184392.7257707,0,0.26396 +1899,176338.9285523,0,0.26449 +1900,177838.9194382,0,0.24338 +1901,184665.0318084,0,0.24462 +1902,184771.1850096,0,0.27484 +1903,179283.5260453,0,0.25719 +1904,181051.2076124,0.10862,0.22221 +1905,193937.2831619,0.29657,0.30484 +1906,204137.2211864,0.46344,0.45737 +1907,213907.9310496,0.56494,0.48561 +1908,223766.3326877,0.61324,0.50079 +1909,223134.0288373,0.61158,0.52163 +1910,213547.933237,0.55552,0.54237 +1911,204954.1392996,0.4628,0.57474 +1912,200597.2426955,0.34392,0.58424 +1913,200948.009795,0.2663,0.58716 +1914,212057.1730642,0.13873,0.71363 +1915,231155.5185597,0.010521,0.77203 +1916,245555.4310649,0,0.76199 +1917,235498.5690941,0,0.74325 +1918,227694.0011306,0,0.76083 +1919,226858.6215911,0,0.76401 +1920,210414.1061245,0,0.75347 +1921,199351.096421,0,0.74021 +1922,196064.9625417,0,0.73474 +1923,198058.7965808,0,0.74601 +1924,200897.2408727,0,0.73802 +1925,212260.2487534,0,0.72742 +1926,227957.0764552,0,0.71933 +1927,255893.8297865,0,0.67021 +1928,282321.3615186,0.10658,0.5975 +1929,296033.5858945,0.25772,0.58308 +1930,296642.812962,0.36495,0.67794 +1931,298913.5683955,0.43802,0.72244 +1932,303893.5381369,0.46787,0.74959 +1933,300141.2532437,0.45801,0.73869 +1934,297713.5756868,0.40594,0.70921 +1935,292442.8384814,0.32735,0.65755 +1936,272375.2681057,0.23381,0.56717 +1937,284490.5791076,0.17567,0.55407 +1938,287089.0248577,0.089198,0.53526 +1939,299845.8704231,0.0070192,0.47900 +1940,316456.5387263,0,0.39236 +1941,300672.0192495,0,0.3493 +1942,281753.6726603,0,0.37804 +1943,265613.7707274,0,0.34956 +1944,242832.3706873,0,0.25089 +1945,230006.2947733,0,0.20897 +1946,222917.1070785,0,0.22212 +1947,219617.1271294,0,0.24628 +1948,220724.8127067,0,0.25894 +1949,226018.6266949,0,0.26787 +1950,235687.7987136,0,0.2947 +1951,259087.6565344,0.00095465,0.28412 +1952,281665.9808854,0.1133,0.23956 +1953,290970.5397349,0.26753,0.36819 +1954,286336.7217364,0.38171,0.4127 +1955,281079.8306007,0.46782,0.39294 +1956,280904.447051,0.51133,0.31296 +1957,278172.1559603,0.51317,0.1608 +1958,276044.4765805,0.50489,0.052453 +1959,273570.6454578,0.45916,0.01475 +1960,271322.9668072,0.37726,0 +1961,269199.902784,0.28013,0 +1962,276921.3943292,0.14466,0.0067956 +1963,292433.6077682,0.01401,0.01774 +1964,313068.8670023,0,0.038049 +1965,299878.1779191,0,0.07027 +1966,283013.6650045,0,0.10518 +1967,267362.9908683,0,0.13455 +1968,245666.1996226,0,0.15676 +1969,230698.5982591,0,0.19767 +1970,221929.420772,0,0.25953 +1971,220738.6587764,0,0.31673 +1972,223212.4898991,0,0.33964 +1973,228880.1477697,0,0.24719 +1974,237681.6327528,0,0.20093 +1975,258099.970228,0.0017668,0.18879 +1976,276898.3175464,0.13148,0.17372 +1977,286096.7231947,0.32108,0.19131 +1978,286225.9531787,0.48571,0.3259 +1979,284476.7330378,0.57399,0.39025 +1980,286073.6464118,0.60748,0.42245 +1981,281490.5973357,0.59154,0.43126 +1982,278832.1519501,0.4777,0.40222 +1983,274124.4882465,0.33779,0.35755 +1984,270644.5093911,0.1967,0.29358 +1985,268798.3667622,0.14447,0.27459 +1986,273529.1072487,0.073363,0.32961 +1987,285312.1125774,0.0060146,0.34231 +1988,301996.6265857,0,0.28509 +1989,286992.1023697,0,0.22575 +1990,268087.6018501,0,0.20388 +1991,250840.01434,0,0.17689 +1992,230024.7561996,0,0.17822 +1993,215232.5383858,0,0.21781 +1994,213547.933237,0,0.23891 +1995,210354.1064891,0,0.16869 +1996,212878.706534,0,0.12343 +1997,219603.2810596,0,0.10545 +1998,230061.6790521,0,0.096784 +1999,253060.0008512,0.0043367,0.077738 +2000,274378.332858,0.11,0.054913 +2001,285385.9582826,0.19629,0.043691 +2002,285492.1114837,0.19618,0.038628 +2003,287439.7919572,0.28701,0.038832 +2004,291159.7693543,0.36762,0.046091 +2005,286585.9509913,0.42628,0.047325 +2006,283355.2013908,0.39656,0.041322 +2007,282058.286194,0.34006,0.032212 +2008,279358.3025993,0.2623,0.021295 +2009,276584.4732995,0.20878,0.012879 +2010,281444.4437699,0.11594,0.0099149 +2011,291916.6878322,0.014157,0.0087041 +2012,306330.446407,0,0.0075473 +2013,289867.4695141,0,0.0063584 +2014,269352.2095509,0,0.0063343 +2015,251573.856035,0,0.0070717 +2016,228838.6095605,0,0.0092302 +2017,216750.9906981,0,0.013359 +2018,210349.4911325,0,0.016804 +2019,206938.7426257,0,0.018982 +2020,209883.3401187,0,0.021741 +2021,217004.8353095,0,0.022672 +2022,228206.3057101,0,0.019718 +2023,249866.1741033,0.0055062,0.016104 +2024,273556.7993881,0.11624,0.016345 +2025,286558.2588519,0.20436,0.017674 +2026,287407.4844612,0.20883,0.021511 +2027,289156.704602,0.29765,0.02998 +2028,292193.6092265,0.37415,0.045354 +2029,292359.7620631,0.42986,0.070074 +2030,287029.0252222,0.43351,0.095573 +2031,281601.3658934,0.40279,0.12486 +2032,280664.4485092,0.33883,0.14161 +2033,277189.0850104,0.24102,0.18804 +2034,278324.4627272,0.12261,0.30093 +2035,283858.2752572,0.014388,0.41398 +2036,288335.1711322,0,0.46674 +2037,270866.0465066,0,0.48129 +2038,253055.3854946,0,0.47751 +2039,240063.156744,0,0.44021 +2040,221527.8847502,0,0.39222 +2041,206680.2826576,0,0.35568 +2042,197163.4174058,0,0.33822 +2043,195783.4257908,0,0.32649 +2044,199300.3274987,0,0.32768 +2045,204538.7572081,0,0.31671 +2046,204054.1447681,0,0.30176 +2047,202752.6142147,0.0087325,0.30237 +2048,208595.6556351,0.13496,0.34113 +2049,220530.9677307,0.26035,0.43095 +2050,228330.9203376,0.32244,0.46462 +2051,231543.2085118,0.36383,0.4192 +2052,235590.8762256,0.36271,0.38933 +2053,232046.2823782,0.32917,0.36004 +2054,227223.2347603,0.31183,0.32888 +2055,221689.4222303,0.2728,0.30284 +2056,218758.6708069,0.21545,0.31497 +2057,219049.438271,0.15113,0.31031 +2058,228026.3068038,0.075803,0.37056 +2059,240663.1530984,0.0081447,0.4768 +2060,252843.0790923,0,0.50456 +2061,238526.2430055,0,0.48296 +2062,225017.0943188,0,0.47636 +2063,218495.5954823,0,0.47005 +2064,202203.3867826,0,0.46874 +2065,187798.8589209,0,0.40205 +2066,178369.685444,0,0.3029 +2067,178729.6832567,0,0.25615 +2068,182809.6584664,0,0.22262 +2069,183017.3495122,0,0.19355 +2070,179385.0638899,0,0.17948 +2071,177654.3051753,0.01061,0.18637 +2072,185283.4895891,0.12741,0.18900 +2073,195829.5793565,0.20208,0.18185 +2074,204806.4478893,0.18398,0.15115 +2075,215717.1508259,0.21775,0.11663 +2076,217512.5245325,0.23084,0.082119 +2077,208978.7302306,0.225000,0.057646 +2078,201386.4686694,0.19178,0.044415 +2079,198224.9494174,0.14722,0.03864 +2080,197832.6441088,0.09866,0.030527 +2081,202757.2295713,0.067857,0.025044 +2082,214789.4641549,0.032937,0.028329 +2083,223941.7162375,0.0025815,0.039347 +2084,233592.4268298,0,0.04572 +2085,227527.848294,0,0.042513 +2086,224615.558297,0,0.040661 +2087,210967.9489131,0,0.036966 +2088,195280.3519244,0,0.031445 +2089,189649.6169064,0,0.025273 +2090,190549.6114379,0,0.019145 +2091,193226.5182498,0,0.014621 +2092,200694.1651835,0,0.010102 +2093,211194.1013852,0,0.0096939 +2094,240792.3830824,0,0.014173 +2095,268881.4431805,0.012189,0.018816 +2096,286521.3359993,0.13466,0.023715 +2097,293204.3723158,0.21683,0.026869 +2098,298327.4181109,0.2129,0.024106 +2099,301059.7092016,0.24994,0.018494 +2100,296347.4301414,0.26333,0.016156 +2101,291496.6903841,0.25456,0.019942 +2102,283355.2013908,0.25685,0.034224 +2103,276579.8579429,0.23925,0.067925 +2104,269130.6724354,0.20166,0.14258 +2105,269167.595288,0.13787,0.23938 +2106,273819.8747127,0.068214,0.34471 +2107,280710.602075,0.008309,0.47964 +2108,291187.4614938,0,0.55723 +2109,278892.1515855,0,0.59022 +2110,262946.0946287,0,0.60714 +2111,240127.771736,0,0.60099 +2112,223941.7162375,0,0.59178 +2113,215375.6144396,0,0.59511 +2114,211494.0995624,0,0.61623 +2115,213238.7043466,0,0.63611 +2116,220069.4320734,0,0.63311 +2117,229397.0677058,0,0.65138 +2118,250835.3989834,0,0.69091 +2119,272970.6491035,0.016255,0.67932 +2120,283115.2028491,0.16547,0.74722 +2121,283470.5853051,0.32287,0.82664 +2122,283309.0478251,0.43687,0.85098 +2123,283553.6617234,0.53965,0.8604 +2124,277821.3888608,0.60329,0.86316 +2125,274092.1807505,0.62578,0.86226 +2126,270464.5104848,0.57785,0.85947 +2127,268295.2928959,0.49424,0.86598 +2128,263924.550222,0.3827,0.86217 +2129,265027.6204428,0.26262,0.80957 +2130,269338.3634812,0.133000,0.81269 +2131,272499.8827331,0.019582,0.86002 +2132,276372.1668971,0,0.86428 +2133,262719.9421567,0,0.84300 +2134,248878.4877968,0,0.81355 +2135,229766.2962315,0,0.7733 +2136,215421.7680053,0,0.7212 +2137,208475.6563642,0,0.67169 +2138,206297.2080621,0,0.63398 +2139,208743.3470454,0,0.60915 +2140,215140.2312544,0,0.57479 +2141,223101.7213413,0,0.53993 +2142,245938.5056604,0,0.52098 +2143,269186.0567143,0.018685,0.44457 +2144,282455.2068592,0.18484,0.4637 +2145,283645.9688549,0.37621,0.5838 +2146,282925.9732296,0.53981,0.59501 +2147,286119.7999775,0.66201,0.55781 +2148,282921.357873,0.73748,0.50992 +2149,280096.7596509,0.76474,0.45200 +2150,277406.0067693,0.7172,0.4155 +2151,271212.1982495,0.62595,0.40837 +2152,268553.7528639,0.49761,0.42204 +2153,269319.9020549,0.34926,0.3894 +2154,276727.5493532,0.18332,0.47779 +2155,281749.0573037,0.031732,0.61066 +2156,285907.4935752,0,0.62484 +2157,270256.819439,0,0.5744 +2158,254693.8370777,0,0.57213 +2159,234266.2688894,0,0.56052 +2160,232493.9719657,0,0.52525 +2161,226341.701655,0,0.47996 +2162,222755.5695984,0,0.44934 +2163,224938.6332571,0,0.43400 +2164,231732.4381313,0,0.39356 +2165,240473.9234789,0,0.37555 +2166,263518.3988437,0,0.3764 +2167,283964.4284583,0.023574,0.28327 +2168,294210.5200485,0.19287,0.21344 +2169,301396.6302314,0.38245,0.30341 +2170,309076.5835674,0.5439,0.38312 +2171,312067.3346262,0.65256,0.38361 +2172,306588.906375,0.71291,0.31064 +2173,301115.0934805,0.7258,0.23057 +2174,298359.7256069,0.67437,0.18062 +2175,292502.8381168,0.58256,0.16817 +2176,285492.1114837,0.45786,0.18897 +2177,278209.0788129,0.30929,0.20946 +2178,278684.4605398,0.15627,0.31098 +2179,278075.2334723,0.026256,0.45124 +2180,279510.6093662,0,0.49853 +2181,265129.1582874,0,0.49062 +2182,253530.7672216,0,0.44822 +2183,234330.8838814,0,0.38844 +2184,218218.674088,0,0.33825 +2185,211521.7917018,0,0.29303 +2186,210123.3386604,0,0.25231 +2187,209638.7262204,0,0.21277 +2188,208217.1963961,0,0.16712 +2189,203001.8434696,0,0.13531 +2190,190152.6907727,0,0.12701 +2191,187028.0943734,0.026429,0.096624 +2192,194486.510594,0.20039,0.04729 +2193,201303.3922511,0.38977,0.048483 +2194,205844.9031181,0.54962,0.19899 +2195,213875.6235536,0.66243,0.34615 +2196,212223.3259008,0.72782,0.43955 +2197,203574.1476846,0.62846,0.49415 +2198,192437.292276,0.58246,0.51819 +2199,190037.3068584,0.50138,0.53248 +2200,189769.6161772,0.39259,0.50224 +2201,198529.5629512,0.26387,0.40659 +2202,210294.1068536,0.13325,0.4902 +2203,218661.7483189,0.022669,0.61373 +2204,230597.0604145,0,0.64615 +2205,225146.3243028,0,0.62531 +2206,221610.9611685,0,0.62813 +2207,205960.2870324,0,0.62887 +2208,194417.2802454,0,0.59712 +2209,188108.0878113,0,0.53765 +2210,186603.4815687,0,0.46491 +2211,185731.1791766,0,0.39498 +2212,190037.3068584,0,0.2998 +2213,193074.2114829,0,0.22953 +2214,192289.6008656,0,0.19324 +2215,198141.8729991,0.025538,0.1117 +2216,211789.482383,0.17227,0.057826 +2217,220526.3523741,0.33106,0.049057 +2218,222954.029931,0.46501,0.071914 +2219,225598.6292469,0.55158,0.1026 +2220,223041.7217059,0.59837,0.10904 +2221,217724.8309348,0.60493,0.10694 +2222,215643.3051207,0.53172,0.10449 +2223,215407.9219356,0.4289,0.09713 +2224,216529.4535826,0.30954,0.082948 +2225,220840.196621,0.21353,0.059019 +2226,227472.4640152,0.11185,0.06383 +2227,231155.5185597,0.021084,0.083274 +2228,234686.2663374,0,0.10354 +2229,228460.1503216,0,0.12301 +2230,223807.8708969,0,0.15199 +2231,206804.8972851,0,0.17141 +2232,189968.0765098,0,0.16882 +2233,178854.2978841,0,0.16069 +2234,175748.162911,0,0.15227 +2235,176698.9263649,0,0.14024 +2236,178388.1468703,0,0.11704 +2237,178037.3797708,0,0.10298 +2238,175480.4722299,0,0.10336 +2239,175812.7779031,0.028052,0.091906 +2240,188795.7759405,0.17622,0.060414 +2241,198035.719798,0.33124,0.080105 +2242,205041.8310745,0.46108,0.25891 +2243,212887.9372472,0.51699,0.41456 +2244,206001.8252415,0.52599,0.53954 +2245,195248.0444284,0.49401,0.64457 +2246,190277.3054002,0.39216,0.68752 +2247,186857.3261802,0.27244,0.69991 +2248,185781.9480989,0.15459,0.64317 +2249,192441.9076325,0.10552,0.46077 +2250,202789.5370673,0.0542,0.38567 +2251,208180.2735436,0.0092449,0.36621 +2252,214457.1584817,0,0.35955 +2253,213358.7036175,0,0.35181 +2254,211798.7130961,0,0.3239 +2255,196701.8817486,0,0.32503 +2256,184346.572205,0,0.52337 +2257,179675.8313539,0,0.58992 +2258,177677.3819582,0,0.2918 +2259,176265.0828471,0,0.063604 +2260,179768.1384854,0,0.033168 +2261,179657.3699277,0,0.059539 +2262,176666.6188689,0,0.10366 +2263,179288.1414019,0.033685,0.10762 +2264,189428.0797909,0.17164,0.14255 +2265,201335.6997471,0.2923,0.26423 +2266,208438.7335116,0.37092,0.30695 +2267,213654.0864381,0.43954,0.35228 +2268,210723.3350148,0.47475,0.45358 +2269,203269.5341508,0.47742,0.5294 +2270,195308.0440638,0.43213,0.5944 +2271,190438.8428802,0.36247,0.62777 +2272,188301.9327873,0.27581,0.60973 +2273,195695.7340159,0.18108,0.52678 +2274,205734.1345603,0.090334,0.30742 +2275,212509.4780082,0.01655,0.21734 +2276,224070.9462215,0,0.14812 +2277,223484.7959368,0,0.10786 +2278,221509.4233239,0,0.092437 +2279,205752.5959866,0,0.08996 +2280,192861.9050806,0,0.096696 +2281,189695.7704721,0,0.11659 +2282,191929.603053,0,0.14259 +2283,194389.588106,0,0.18387 +2284,202337.2321232,0,0.23573 +2285,215509.4597801,0,0.31954 +2286,244955.4347105,0,0.42035 +2287,269209.1334972,0.037219,0.41677 +2288,281841.3644351,0.17023,0.52102 +2289,281661.3655288,0.27007,0.84027 +2290,281361.3673516,0.31854,0.92391 +2291,282685.9746878,0.35053,0.94102 +2292,276395.24368,0.34646,0.95454 +2293,271147.5832575,0.31234,0.96138 +2294,265023.0050862,0.25745,0.95746 +2295,260583.0320638,0.19025,0.9361 +2296,255321.5255715,0.12139,0.87229 +2297,258695.3512258,0.078557,0.82289 +2298,266084.5370978,0.038059,0.82984 +2299,272047.577789,0.0060385,0.85400 +2300,280479.8342464,0,0.88872 +2301,272975.26446,0,0.89515 +2302,259124.579387,0,0.87385 +2303,238766.2415472,0,0.82427 +2304,224218.6376318,0,0.76621 +2305,218094.0594606,0,0.72224 +2306,213012.5518746,0,0.71535 +2307,213469.4721753,0,0.8114 +2308,218735.5940241,0,0.89504 +2309,227167.8504814,0,0.93109 +2310,255912.2912128,0,0.94126 +2311,276806.0104149,0.041851,0.94745 +2312,285658.2643203,0.16471,0.96051 +2313,284116.7352252,0.23845,0.98125 +2314,282016.7479849,0.24682,0.99227 +2315,283045.9725005,0.29631,0.99471 +2316,278601.3841215,0.32392,0.99434 +2317,272827.5730497,0.32868,0.99463 +2318,266970.6855597,0.33456,0.99501 +2319,265766.0774943,0.31651,0.9941 +2320,264372.2398095,0.27354,0.98132 +2321,265909.1535481,0.19445,0.93268 +2322,270930.6614986,0.10765,0.88019 +2323,277646.0053111,0.025258,0.83252 +2324,287605.9447938,0,0.81656 +2325,277064.470383,0,0.81359 +2326,258076.8934451,0,0.81577 +2327,235664.7219307,0,0.79293 +2328,216963.2971004,0,0.74426 +2329,213894.0849799,0,0.68568 +2330,211835.6359487,0,0.60075 +2331,213109.4743626,0,0.47434 +2332,219127.8993327,0,0.34982 +2333,230749.3671814,0,0.24067 +2334,257010.746077,0,0.16068 +2335,277309.0842813,0.045714,0.094505 +2336,287739.7901344,0.1727,0.097244 +2337,288496.7086122,0.25243,0.094746 +2338,292627.4527443,0.27063,0.099613 +2339,294330.5193194,0.31226,0.10979 +2340,289539.7791975,0.32796,0.12224 +2341,285432.1118483,0.3194,0.13794 +2342,279939.8375274,0.30662,0.14915 +2343,277756.7738688,0.27407,0.14529 +2344,273921.4125573,0.22384,0.15088 +2345,274036.7964716,0.16009,0.15944 +2346,279413.6868782,0.089317,0.16972 +2347,284112.1198686,0.021316,0.24701 +2348,288164.402939,0,0.44153 +2349,278167.5406037,0,0.66512 +2350,262719.9421567,0,0.73013 +2351,240658.5377418,0,0.72207 +2352,225252.477504,0,0.65192 +2353,216940.2203175,0,0.56199 +2354,217697.1387953,0,0.52401 +2355,219137.1300459,0,0.52198 +2356,225021.7096754,0,0.5437 +2357,233767.8103796,0,0.57523 +2358,257130.7453478,0,0.58706 +2359,281259.8295071,0.049892,0.6098 +2360,292345.9159934,0.20033,0.6257 +2361,293993.5982896,0.33028,0.52300 +2362,296481.275482,0.4215,0.38169 +2363,298221.2649097,0.4526,0.32531 +2364,291704.3814298,0.43855,0.32505 +2365,283627.5074286,0.38555,0.31544 +2366,277290.622855,0.34407,0.2896 +2367,273192.1862189,0.28371,0.25167 +2368,270141.4355247,0.21176,0.19626 +2369,271401.4278689,0.13574,0.14184 +2370,273898.3357745,0.066671,0.087457 +2371,275462.9416524,0.013705,0.065315 +2372,275352.1730947,0,0.074418 +2373,267889.1415175,0,0.091725 +2374,254564.6070937,0,0.1008 +2375,234746.2659729,0,0.11777 +2376,217069.4503015,0,0.15748 +2377,209366.4201826,0,0.2366 +2378,204386.4504413,0,0.31800 +2379,207598.7386155,0,0.35026 +2380,211046.4099749,0,0.34081 +2381,210109.4925907,0,0.2972 +2382,205757.2113432,0,0.26269 +2383,212560.2469305,0.055296,0.22092 +2384,228746.3024291,0.17788,0.21355 +2385,240007.7724651,0.2429,0.28266 +2386,242883.1396096,0.23994,0.3376 +2387,246538.5020147,0.24477,0.36991 +2388,240072.3874572,0.22081,0.41228 +2389,232526.2794617,0.17468,0.45299 +2390,227786.3082621,0.21495,0.44795 +2391,226618.6230493,0.23479,0.39413 +2392,224878.6336216,0.22777,0.37264 +2393,231233.9796215,0.18916,0.34815 +2394,236878.5607092,0.1232,0.22586 +2395,239121.6240033,0.038428,0.15905 +2396,238175.475906,0,0.11172 +2397,232364.7419817,0,0.059709 +2398,227149.3890551,0,0.030962 +2399,210538.7207519,0,0.024484 +2400,197306.4934596,0,0.029197 +2401,189783.462247,0,0.043008 +2402,188020.3960364,0,0.083172 +2403,191084.9928003,0,0.12697 +2404,194740.3552055,0,0.15698 +2405,193928.0524488,0,0.15775 +2406,188565.0081119,0,0.13744 +2407,188140.3953073,0.05901,0.090011 +2408,197029.5720652,0.20739,0.073127 +2409,207137.2029583,0.33158,0.10155 +2410,218218.674088,0.41823,0.12156 +2411,229240.1455823,0.4827,0.1064 +2412,225280.1696434,0.51177,0.09197 +2413,215514.0751367,0.50679,0.084369 +2414,207857.1985835,0.48586,0.077694 +2415,204044.9140549,0.43568,0.071005 +2416,202567.9999518,0.35952,0.059232 +2417,210271.0300708,0.24558,0.046083 +2418,220304.8152586,0.13246,0.036011 +2419,226475.5469956,0.034329,0.034062 +2420,234824.7270346,0,0.028804 +2421,236112.4115182,0,0.021181 +2422,235286.2626918,0,0.015397 +2423,219534.0507111,0,0.010212 +2424,208858.7309597,0,0.006829 +2425,204575.6800607,0,0.0058889 +2426,202747.9988581,0,0.0065699 +2427,207040.2804702,0,0.0079075 +2428,215135.6158978,0,0.0089358 +2429,228197.074997,0,0.008777 +2430,262350.7136309,0,0.0080298 +2431,288492.0932556,0.064371,0.0074277 +2432,300164.3300266,0.22105,0.0072224 +2433,302545.8540178,0.36767,0.016066 +2434,307055.0573888,0.48787,0.023619 +2435,310161.1923619,0.48011,0.029797 +2436,304475.073065,0.41388,0.036621 +2437,299550.4876025,0.30417,0.041037 +2438,294455.1339468,0.29317,0.043072 +2439,292595.1452483,0.2636,0.043184 +2440,286885.9491685,0.21762,0.049476 +2441,285625.9568243,0.14686,0.054552 +2442,285182.8825934,0.078023,0.058648 +2443,285875.1860792,0.020071,0.075379 +2444,289336.7035083,0,0.075623 +2445,282621.3596958,0,0.050698 +2446,267561.4512009,0,0.025585 +2447,244678.5133162,0,0.013863 +2448,228427.8428256,0,0.010781 +2449,217115.6038673,0,0.013499 +2450,214378.69742,0,0.018234 +2451,217812.5227097,0,0.021796 +2452,224915.5564742,0,0.023298 +2453,237700.0941791,0,0.027438 +2454,267224.5301711,0,0.037252 +2455,290905.9247429,0.038269,0.032918 +2456,299278.1815647,0.11627,0.043395 +2457,299139.7208676,0.16988,0.10087 +2458,300072.0228951,0.19283,0.18051 +2459,302735.0836373,0.24414,0.25561 +2460,301355.0920222,0.2829,0.28622 +2461,298553.5705829,0.30613,0.26745 +2462,292392.0695591,0.26652,0.21587 +2463,289816.7005918,0.21253,0.14926 +2464,283138.2796319,0.15218,0.068398 +2465,283212.1253371,0.097517,0.02959 +2466,285293.6511511,0.048269,0.043071 +2467,287347.4848257,0.010783,0.10226 +2468,290596.6958525,0,0.18713 +2469,282215.2083175,0,0.2432 +2470,266084.5370978,0,0.29303 +2471,240501.6156184,0,0.30183 +2472,223600.1798511,0,0.2773 +2473,217267.9106341,0,0.25682 +2474,214092.5453125,0,0.23619 +2475,217503.2938193,0,0.21987 +2476,222755.5695984,0,0.20607 +2477,234201.6538974,0,0.19016 +2478,267427.6058603,0,0.16817 +2479,290070.5452033,0.05016,0.12723 +2480,298484.3402343,0.14499,0.098117 +2481,298498.186304,0.21512,0.10698 +2482,300662.7885364,0.25153,0.1689 +2483,305190.4533337,0.2924,0.21989 +2484,303681.2317346,0.31238,0.24072 +2485,301322.7845262,0.31128,0.23639 +2486,296125.893026,0.27909,0.20828 +2487,292073.6099556,0.2318,0.17441 +2488,285002.8836871,0.17486,0.1306 +2489,285478.265414,0.11187,0.075652 +2490,287389.0230349,0.055674,0.072354 +2491,290149.006265,0.013063,0.10374 +2492,291284.3839818,0,0.14908 +2493,282076.7476203,0,0.18717 +2494,265913.7689046,0,0.20276 +2495,243640.0580874,0,0.16774 +2496,223835.5630363,0,0.11712 +2497,215537.1519196,0,0.077535 +2498,214267.9288622,0,0.05116 +2499,216990.9892398,0,0.036408 +2500,221112.5026587,0,0.030665 +2501,232738.585864,0,0.039444 +2502,266282.9974304,0,0.074732 +2503,288344.4018453,0.078439,0.087711 +2504,295973.5862591,0.22754,0.081273 +2505,295650.511299,0.35636,0.15838 +2506,298415.1098857,0.45207,0.32538 +2507,302795.0832727,0.49896,0.32852 +2508,300422.7899946,0.50738,0.26937 +2509,297150.502185,0.48034,0.18513 +2510,291552.074663,0.37818,0.11935 +2511,286705.9502622,0.2601,0.08816 +2512,280687.5252921,0.14555,0.087623 +2513,281379.8287779,0.092981,0.087077 +2514,285132.1136711,0.046213,0.13601 +2515,286059.8003421,0.011081,0.2492 +2516,287873.6354749,0,0.30767 +2517,280202.912852,0,0.23372 +2518,262779.9417921,0,0.14097 +2519,236841.6378566,0,0.093063 +2520,219294.0521693,0,0.069746 +2521,213095.6282929,0,0.063227 +2522,213331.0114781,0,0.071448 +2523,216026.3797162,0,0.089835 +2524,223840.1783929,0,0.11837 +2525,236781.6382212,0,0.15419 +2526,264644.5458473,0,0.17703 +2527,286609.0277742,0.05228,0.20778 +2528,297376.654657,0.13988,0.22204 +2529,299135.105511,0.20806,0.19509 +2530,300847.4027993,0.2472,0.16998 +2531,299661.2561602,0.27962,0.15192 +2532,292308.9931408,0.2917,0.17981 +2533,284010.5820241,0.28417,0.25272 +2534,278735.2294621,0.31511,0.33083 +2535,272162.9617033,0.32125,0.41487 +2536,267847.6033084,0.29837,0.49782 +2537,270930.6614986,0.20467,0.45953 +2538,273662.9525893,0.11299,0.54231 +2539,273662.9525893,0.034708,0.67265 +2540,273639.8758064,0,0.72777 +2541,269255.2870629,0,0.72598 +2542,259839.9596557,0,0.72055 +2543,238623.1654935,0,0.70394 +2544,219478.6664322,0,0.65157 +2545,209537.1883758,0,0.55456 +2546,207654.1228943,0,0.44816 +2547,212324.8637454,0,0.35959 +2548,214337.1592108,0,0.2725 +2549,212661.7847751,0,0.21769 +2550,205521.828158,0,0.2287 +2551,213026.3979443,0.091119,0.22526 +2552,227338.6186746,0.19612,0.2456 +2553,233569.350047,0.22971,0.29655 +2554,233772.4257362,0.18803,0.26577 +2555,232230.8966411,0.24211,0.2289 +2556,226360.1630813,0.285000,0.18487 +2557,218352.5194286,0.31314,0.14768 +2558,213487.9336015,0.32302,0.1207 +2559,210755.6425108,0.3109,0.093849 +2560,209403.3430352,0.27499,0.068836 +2561,215994.0722202,0.18237,0.046925 +2562,226415.5473601,0.096636,0.029669 +2563,229743.2194487,0.028362,0.031186 +2564,231252.4410478,0,0.033537 +2565,230435.5229345,0,0.030125 +2566,225598.6292469,0,0.035205 +2567,209334.1126866,0,0.0424 +2568,194601.8945083,0,0.043333 +2569,183788.1140597,0,0.04236 +2570,177908.1497868,0,0.041358 +2571,181508.1279131,0,0.037974 +2572,186732.7115528,0,0.025959 +2573,188145.0106638,0,0.020102 +2574,182121.9703372,0,0.017798 +2575,183746.5758506,0.04383,0.0129 +2576,190494.2271591,0.10396,0.015031 +2577,196918.8035075,0.14059,0.021927 +2578,203167.9963062,0.15006,0.028821 +2579,210538.7207519,0.20664,0.039158 +2580,202849.5367027,0.25689,0.0456 +2581,190521.9192985,0.29432,0.046904 +2582,182191.2006858,0.28913,0.042751 +2583,179758.9077722,0.26593,0.035662 +2584,180668.133017,0.22512,0.022707 +2585,192095.7558896,0.14815,0.012512 +2586,207561.8157629,0.077585,0.0057887 +2587,216598.6839312,0.022454,0 +2588,224874.018265,0,0 +2589,229267.8377217,0,0.012991 +2590,225483.2453326,0,0.02641 +2591,207626.4307549,0,0.037402 +2592,193554.2085664,0,0.039027 +2593,185772.7173858,0,0.033559 +2594,188126.5492376,0,0.024624 +2595,193397.286443,0,0.016773 +2596,202074.1567986,0,0.012658 +2597,218541.7490481,0,0.0099363 +2598,253443.0754467,0,0.0084459 +2599,276524.473664,0.069904,0.0098381 +2600,282750.5896799,0.1513,0.015433 +2601,280479.8342464,0.19097,0.024984 +2602,281001.369539,0.18386,0.044673 +2603,282224.4390306,0.19887,0.082051 +2604,278956.7665775,0.19513,0.13188 +2605,276169.091208,0.17537,0.16202 +2606,271179.8907535,0.13984,0.16084 +2607,267007.6084122,0.099008,0.13311 +2608,261755.3326331,0.059185,0.081338 +2609,263546.0909831,0.037946,0.051628 +2610,270542.9715465,0.018819,0.054489 +2611,273926.0279139,0.0041125,0.095279 +2612,275144.4820489,0,0.17857 +2613,271369.1203729,0,0.28927 +2614,252907.6940843,0,0.39845 +2615,226997.0822882,0,0.47187 +2616,213238.7043466,0,0.46115 +2617,206897.2044165,0,0.39715 +2618,205784.9034826,0,0.34492 +2619,210700.258232,0,0.33095 +2620,216224.8400488,0,0.32466 +2621,229092.454172,0,0.33365 +2622,257278.4367581,0,0.3797 +2623,279579.8397148,0.074844,0.44786 +2624,286489.0285033,0.17999,0.5454 +2625,282325.9768752,0.26792,0.60097 +2626,286022.8774895,0.32818,0.64294 +2627,290467.4658685,0.42377,0.69463 +2628,288113.6340167,0.50281,0.71834 +2629,286405.952085,0.55628,0.72362 +2630,281315.2137859,0.54848,0.7251 +2631,279815.2229,0.50885,0.71639 +2632,273902.951131,0.43808,0.68177 +2633,274821.4070889,0.29824,0.6127 +2634,281176.7530888,0.16485,0.47432 +2635,282644.4364787,0.054791,0.52981 +2636,279256.7647547,0,0.59171 +2637,273907.5664876,0,0.56304 +2638,256152.2897545,0,0.49944 +2639,235101.6484289,0,0.40581 +2640,217724.8309348,0,0.29639 +2641,212380.2480242,0,0.20143 +2642,212841.7836814,0,0.13433 +2643,217050.9888752,0,0.091137 +2644,221869.4211366,0,0.059439 +2645,231446.2860238,0,0.040035 +2646,261843.024408,0.0010889,0.024973 +2647,284527.5019601,0.11466,0.010864 +2648,295281.2827733,0.27895,0.0098537 +2649,295068.9763709,0.43451,0.010914 +2650,295428.9741836,0.56487,0.013266 +2651,299702.7943694,0.63245,0.013282 +2652,297630.4992685,0.65746,0.013136 +2653,296121.2776694,0.64248,0.014788 +2654,291081.3082926,0.61024,0.019337 +2655,288376.7093413,0.54513,0.02791 +2656,285058.2679659,0.45164,0.041801 +2657,286133.6460472,0.30843,0.059245 +2658,288422.862907,0.17161,0.077053 +2659,284522.8866036,0.058458,0.11023 +2660,280705.9867184,0,0.12537 +2661,277978.3109842,0,0.091866 +2662,262244.5604298,0,0.055324 +2663,237644.7099002,0,0.02992 +2664,222690.9546064,0,0.015628 +2665,213437.1646793,0,0.0083566 +2666,213132.5511455,0,0 +2667,216349.4546763,0,0 +2668,222880.1842259,0,0 +2669,233966.2707122,0,0 +2670,259987.651066,0.0040444,0 +2671,279542.9168622,0.11846,0 +2672,286724.4116885,0.28109,0 +2673,285372.1122128,0.43491,0 +2674,286336.7217364,0.56375,0 +2675,289253.62709,0.65858,0 +2676,283747.5066994,0.71566,0 +2677,278490.6155638,0.73373,0 +2678,274022.9504019,0.68533,0 +2679,270566.0483294,0.60197,0 +2680,264519.9312198,0.49002,0 +2681,267067.6080477,0.33101,0.0058348 +2682,273699.8754419,0.18173,0.017066 +2683,277378.3146299,0.061425,0.078109 +2684,274562.9471209,0,0.19348 +2685,272412.1909582,0,0.24815 +2686,256530.7489935,0,0.21831 +2687,233458.5814893,0,0.1817 +2688,216727.9139152,0,0.14833 +2689,208147.9660476,0,0.11526 +2690,206767.9744325,0,0.094953 +2691,209292.5744775,0,0.088771 +2692,216930.9896044,0,0.099135 +2693,232013.9748822,0,0.1105 +2694,256115.366902,0.0047487,0.10157 +2695,276326.0133314,0.12169,0.049543 +2696,282173.6701083,0.28193,0.051393 +2697,278402.9237889,0.43196,0.062998 +2698,275509.0952181,0.55795,0.04645 +2699,276626.0115086,0.65135,0.03599 +2700,269559.9005966,0.70795,0.02788 +2701,261372.2580376,0.72603,0.020856 +2702,254513.8381714,0.674000,0.016844 +2703,250489.2472405,0.58807,0.016367 +2704,248920.026006,0.47475,0.018439 +2705,253673.8432753,0.31732,0.025269 +2706,259096.8872476,0.17214,0.050933 +2707,261875.331904,0.057686,0.15118 +2708,260739.9541873,0,0.3072 +2709,260698.4159781,0,0.41036 +2710,247189.2672914,0,0.49583 +2711,225667.8595955,0,0.51165 +2712,206546.437317,0,0.4788 +2713,198908.0221901,0,0.43891 +2714,196074.1932548,0,0.4076 +2715,195598.8115279,0,0.38155 +2716,197348.0316687,0,0.34481 +2717,198617.2547261,0,0.33518 +2718,199346.4810645,0.0073334,0.27049 +2719,208415.6567287,0.12664,0.21343 +2720,221343.2704874,0.28736,0.26927 +2721,228026.3068038,0.43773,0.3471 +2722,228598.6110188,0.56213,0.37775 +2723,227320.1572483,0.62145,0.38156 +2724,219778.6646094,0.63991,0.38369 +2725,208729.5009757,0.62057,0.41559 +2726,199946.4774188,0.5522,0.48262 +2727,198363.4101146,0.45769,0.54802 +2728,198201.8726346,0.34687,0.59334 +2729,206260.2852096,0.22553,0.60135 +2730,216972.5278135,0.11805,0.56856 +2731,221601.7304554,0.037522,0.70708 +2732,222570.9553355,0,0.79559 +2733,225852.4738583,0,0.81805 +2734,218897.1315041,0,0.8307 +2735,201178.7776236,0,0.8267 +2736,188795.7759405,0,0.8105 +2737,180091.2134454,0,0.78863 +2738,175655.8557796,0,0.76644 +2739,177995.8415617,0,0.74035 +2740,179588.1395791,0,0.69369 +2741,181171.2068833,0,0.66322 +2742,176518.9274586,0.0086458,0.59056 +2743,177949.687996,0.1336,0.57113 +2744,184978.8760554,0.28475,0.67326 +2745,194509.5873768,0.41361,0.72925 +2746,195908.0404182,0.50912,0.75513 +2747,201820.3121871,0.5694,0.77726 +2748,196757.2660275,0.59295,0.78677 +2749,185135.7981788,0.58169,0.78515 +2750,177465.0755559,0.57193,0.77679 +2751,179657.3699277,0.53023,0.76749 +2752,182606.5827773,0.45772,0.73043 +2753,193318.8253812,0.30772,0.64676 +2754,206472.5916119,0.16894,0.50122 +2755,213391.0111135,0.05889,0.40168 +2756,220041.739934,0.00020436,0.42023 +2757,227061.6972802,0,0.37075 +2758,222621.7242578,0,0.30476 +2759,205738.7499169,0,0.25227 +2760,195266.5058547,0,0.22097 +2761,187231.1700626,0,0.19691 +2762,187291.169698,0,0.17336 +2763,189700.3858287,0,0.14391 +2764,194929.5848249,0,0.11729 +2765,210838.7189291,0,0.097546 +2766,243686.2116531,0.010022,0.072624 +2767,273109.1098006,0.11939,0.060897 +2768,287209.0241286,0.26342,0.057915 +2769,292447.453838,0.4004,0.041836 +2770,297524.3460673,0.51664,0.034166 +2771,301941.2423069,0.48054,0.033467 +2772,296467.4294123,0.38782,0.03438 +2773,292807.4516506,0.25409,0.032388 +2774,288524.4007516,0.24166,0.02977 +2775,283198.2792674,0.21558,0.029024 +2776,277479.8524745,0.17787,0.031202 +2777,278458.3080678,0.11561,0.034307 +2778,277793.6967214,0.060455,0.028518 +2779,279856.7611091,0.019106,0.027761 +2780,275056.7902741,0,0.040973 +2781,272273.7302611,0,0.065444 +2782,254555.3763806,0,0.099961 +2783,231727.8227747,0,0.13057 +2784,218754.0554504,0,0.14294 +2785,212758.7072631,0,0.1239 +2786,209574.1112284,0,0.11008 +2787,211041.7946183,0,0.098863 +2788,217369.4484787,0,0.078185 +2789,231635.5156433,0,0.060887 +2790,256341.519374,0.0069938,0.049881 +2791,278578.3073386,0.065186,0.043999 +2792,288330.5557756,0.12973,0.1002 +2793,285445.957918,0.17696,0.22687 +2794,284767.5005019,0.20215,0.36171 +2795,287213.6394851,0.3204,0.48648 +2796,285976.7239238,0.43786,0.64584 +2797,284942.8840516,0.53743,0.78313 +2798,280212.1435652,0.52278,0.83094 +2799,277193.700367,0.4786,0.84288 +2800,268235.2932604,0.40798,0.79096 +2801,268272.216113,0.28681,0.67781 +2802,271216.813606,0.16838,0.45138 +2803,273787.5672167,0.066063,0.28929 +2804,272606.0359343,0.0014142,0.23814 +2805,270866.0465066,0,0.23472 +2806,251920.0077779,0,0.26409 +2807,226964.7747922,0,0.29972 +2808,214544.8502566,0,0.33028 +2809,208111.043195,0,0.34707 +2810,206458.7455422,0,0.33456 +2811,206777.2051456,0,0.25241 +2812,214507.927404,0,0.14513 +2813,231293.9792569,0,0.089608 +2814,255492.2937647,0.014238,0.060453 +2815,277442.9296219,0.14693,0.042999 +2816,284596.7323087,0.29135,0.057947 +2817,280862.9088418,0.41166,0.073278 +2818,281499.8280488,0.49909,0.08267 +2819,283535.2002971,0.54654,0.077063 +2820,279118.3040576,0.55549,0.070518 +2821,276002.9383714,0.52905,0.060127 +2822,270718.3550963,0.45725,0.048626 +2823,267049.1466214,0.36721,0.044805 +2824,263730.705246,0.26768,0.04157 +2825,265950.6917572,0.17415,0.041436 +2826,271022.96863,0.092008,0.034839 +2827,274184.4878819,0.030686,0.046449 +2828,272107.5774245,0,0.10659 +2829,266749.1484442,0,0.22435 +2830,249644.6369878,0,0.34712 +2831,226235.5484538,0,0.39287 +2832,211337.1774389,0,0.39004 +2833,204972.6007259,0,0.34574 +2834,200698.7805401,0,0.26834 +2835,201820.3121871,0,0.20136 +2836,207137.2029583,0,0.19386 +2837,222358.6489332,0,0.20204 +2838,250230.7872725,0.007038,0.16369 +2839,273086.0330178,0.03756,0.096337 +2840,279856.7611091,0.08388,0.060578 +2841,277830.6195739,0.12987,0.088542 +2842,278652.1530438,0.17068,0.10724 +2843,281767.51873,0.21996,0.087258 +2844,278204.4634563,0.26074,0.057808 +2845,275222.9431107,0.28866,0.040364 +2846,271682.9646198,0.32743,0.037015 +2847,270187.5890905,0.34203,0.033915 +2848,267870.6800912,0.32752,0.036047 +2849,270658.3554608,0.2353,0.037055 +2850,275315.2502421,0.14224,0.024065 +2851,276256.7829828,0.058795,0.017693 +2852,274378.332858,0.0013336,0.016848 +2853,272421.4216714,0,0.011554 +2854,253396.921881,0,0.0070267 +2855,228044.7682301,0,0 +2856,211392.5617178,0,0 +2857,207469.5086315,0,0.0067306 +2858,204049.5294115,0,0.0086194 +2859,204451.0654333,0,0.011576 +2860,210109.4925907,0,0.017902 +2861,225709.3978046,0,0.024363 +2862,255704.600167,0.013569,0.018084 +2863,277895.234566,0.098387,0.011545 +2864,288510.5546819,0.17877,0.010364 +2865,295322.8209824,0.23008,0.0097644 +2866,301913.5501674,0.24838,0.010175 +2867,307484.28555,0.27607,0.0087872 +2868,296725.8893803,0.28531,0.0077465 +2869,287822.8665527,0.27724,0.0071888 +2870,279990.6064497,0.25962,0.006784 +2871,275527.5566444,0.22805,0.0062233 +2872,271849.1174564,0.18619,0 +2873,273362.9544121,0.12169,0 +2874,270427.5876322,0.064887,0 +2875,264953.7747376,0.022098,0 +2876,255958.4447785,0,0 +2877,249280.0238186,0,0 +2878,234293.9610288,0,0 +2879,213787.9317787,0,0 +2880,196014.1936194,0,0 +2881,186077.3309195,0,0 +2882,181821.97216,0,0.0068472 +2883,179061.9889299,0,0.008467 +2884,179888.1377563,0,0.0085967 +2885,177931.2265697,0,0.0089551 +2886,170583.5789068,0.017834,0.012348 +2887,177418.9219902,0.11753,0.014832 +2888,188066.5496021,0.26338,0.016496 +2889,197601.8762802,0.42274,0.02204 +2890,204894.1396642,0.5786,0.027224 +2891,215172.5387504,0.6092,0.033955 +2892,212643.3233488,0.59541,0.042479 +2893,204571.0647041,0.54475,0.04995 +2894,199743.4017297,0.52098,0.059804 +2895,195714.1954422,0.4701,0.069108 +2896,197283.4166767,0.39567,0.088319 +2897,200638.7809047,0.25407,0.099349 +2898,206034.1327375,0.13181,0.16086 +2899,206251.0544964,0.043472,0.25414 +2900,203371.0719954,0.00084626,0.33719 +2901,207409.508996,0,0.38488 +2902,201515.6986534,0,0.42502 +2903,185652.7181149,0,0.41 +2904,172498.9518842,0,0.37368 +2905,164048.2340006,0,0.30257 +2906,160282.1030378,0,0.22806 +2907,162275.9370769,0,0.17346 +2908,164805.1524785,0,0.12229 +2909,167288.2143143,0,0.88000 +2910,166706.6793862,0.019616,0.050284 +2911,177114.3084564,0.12379,0.02493 +2912,189460.3872869,0.18511,0.025031 +2913,200421.8591458,0.18352,0.026222 +2914,208540.2713562,0.11956,0.024287 +2915,221523.2693937,0.21587,0.029183 +2916,217600.2163073,0.31621,0.076034 +2917,207921.8135755,0.40737,0.16641 +2918,201524.9293665,0.31966,0.14356 +2919,200223.3988132,0.21989,0.061052 +2920,202641.845657,0.12406,0.028404 +2921,210307.9529233,0.089553,0.023383 +2922,218047.9058948,0.054495,0.024003 +2923,220198.6620575,0.022674,0.029901 +2924,220549.4291569,0,0.030593 +2925,222677.1085367,0,0.025278 +2926,217461.7556102,0,0.02206 +2927,199992.6309846,0,0.016582 +2928,188648.0845302,0,0.011611 +2929,186991.1715208,0,0.0088642 +2930,183908.1133306,0,0.0079273 +2931,187480.3993175,0,0.0087486 +2932,196092.6546811,0,0.012026 +2933,210474.1057599,0,0.019867 +2934,243723.1345057,0.009967,0.027199 +2935,273972.1814796,0.039035,0.01743 +2936,290296.6976753,0.054652,0.015817 +2937,293347.4483695,0.044774,0.015653 +2938,299098.1826584,0.011838,0.014849 +2939,306611.9831579,0.050715,0.012457 +2940,305227.3761862,0.097117,0.012142 +2941,302522.777235,0.14376,0.023567 +2942,296998.1954181,0.14951,0.020188 +2943,293361.2944392,0.145000,0.030798 +2944,285256.7282985,0.13022,0.069552 +2945,285312.1125774,0.11305,0.14647 +2946,283918.2748926,0.08306,0.18685 +2947,282695.205401,0.043146,0.24001 +2948,276118.3222857,0.0011887,0.33274 +2949,269610.6695189,0,0.38863 +2950,251873.8542121,0,0.36552 +2951,227121.6969157,0,0.26464 +2952,213063.3207969,0,0.15587 +2953,208461.8102945,0,0.096309 +2954,205106.4460665,0,0.064715 +2955,207594.1232589,0,0.048858 +2956,216580.2225049,0,0.04722 +2957,226600.161623,0,0.052155 +2958,255307.6795018,0.018154,0.047938 +2959,282445.9761461,0.091517,0.059286 +2960,296365.8915677,0.14622,0.088964 +2961,297261.2707427,0.16612,0.11777 +2962,304350.4584375,0.14967,0.14053 +2963,309261.1978303,0.186000,0.11151 +2964,304678.1487542,0.21405,0.076433 +2965,297538.192137,0.23112,0.072903 +2966,293532.0626324,0.22645,0.080511 +2967,289415.16457,0.2094,0.10532 +2968,283364.432104,0.18009,0.17213 +2969,287079.7941445,0.11832,0.24333 +2970,282229.0543872,0.064031,0.31584 +2971,277590.6210322,0.022967,0.35802 +2972,270482.9719111,0,0.40577 +2973,268189.1396947,0,0.42986 +2974,252547.6962717,0,0.43608 +2975,231072.4421415,0,0.43678 +2976,217987.9062594,0,0.43596 +2977,214027.9303205,0,0.5173 +2978,210095.646521,0,0.50796 +2979,212587.93907,0,0.44862 +2980,219764.8185397,0,0.35831 +2981,227366.310814,0,0.28357 +2982,253992.3028788,0.028559,0.19753 +2983,279732.1464817,0.15705,0.2264 +2984,294085.9054211,0.26699,0.31085 +2985,293596.6776244,0.33964,0.3301 +2986,296615.1208226,0.37011,0.36194 +2987,300312.0214369,0.39768,0.46189 +2988,296185.8926614,0.39794,0.57217 +2989,293278.2180209,0.37262,0.61541 +2990,286959.7948737,0.32591,0.61015 +2991,284555.1940996,0.26446,0.60223 +2992,278573.6919821,0.19601,0.59441 +2993,278107.5409683,0.12874,0.57599 +2994,280244.4510612,0.070002,0.5282 +2995,280276.7585572,0.025539,0.41466 +2996,273829.1054259,5.0861e-05,0.3381 +2997,271226.0443192,0,0.33601 +2998,261067.6445039,0,0.35953 +2999,239398.5453976,0,0.44046 +3000,224680.173289,0,0.57651 +3001,219326.3596653,0,0.71135 +3002,216566.3764352,0,0.77531 +3003,217332.5256261,0,0.80209 +3004,224057.1001518,0,0.78363 +3005,235780.105845,0,0.77296 +3006,265447.6178909,0.024219,0.75424 +3007,292641.298814,0.11815,0.78678 +3008,308153.512253,0.20546,0.84261 +3009,313124.2512812,0.2692,0.86976 +3010,320001.1325737,0.30412,0.87088 +3011,326901.0906491,0.315000,0.86836 +3012,326762.6299519,0.29968,0.87618 +3013,323808.8017457,0.26233,0.88702 +3014,317698.0696442,0.28971,0.89718 +3015,312330.4099508,0.29697,0.88889 +3016,302315.0861892,0.28111,0.88323 +3017,301364.3227354,0.23663,0.85799 +3018,300607.4042575,0.17154,0.78066 +3019,298387.4177463,0.092009,0.61743 +3020,289147.4738889,0.0074246,0.52593 +3021,281107.5227402,0,0.46011 +3022,263864.5505866,0,0.3713 +3023,241936.9915123,0,0.25598 +3024,227652.4629215,0,0.16269 +3025,221458.6544017,0,0.11841 +3026,218117.1362434,0,0.11216 +3027,219409.4360836,0,0.1306 +3028,223674.0255563,0,0.16294 +3029,231783.2070536,0,0.16155 +3030,257490.7431605,0.018931,0.12047 +3031,285409.0350654,0.064867,0.08198 +3032,299430.4883316,0.1071,0.080812 +3033,303053.5432408,0.13051,0.060998 +3034,308656.5861194,0.13333,0.027802 +3035,311158.1093815,0.14215,0.014636 +3036,306293.5235544,0.13998,0.042913 +3037,296088.9701734,0.1281,0.25225 +3038,287615.1755069,0.17156,0.61684 +3039,283198.2792674,0.20008,0.83108 +3040,278015.2338368,0.20743,0.8357 +3041,278112.1563248,0.179000,0.78043 +3042,276441.3972457,0.13222,0.7619 +3043,273487.5690395,0.072372,0.72992 +3044,266287.612787,0.0059943,0.68704 +3045,262281.4832824,0,0.65188 +3046,250120.0187147,0,0.63883 +3047,229637.0662475,0,0.64437 +3048,213363.3189741,0,0.63177 +3049,205318.7524688,0,0.59888 +3050,200329.5520143,0,0.52632 +3051,201723.3896991,0,0.42714 +3052,204104.9136904,0,0.33954 +3053,203624.9166069,0,0.28897 +3054,204954.1392996,0.030645,0.21895 +3055,214341.7745674,0.13232,0.23352 +3056,228123.2292918,0.21362,0.43721 +3057,238470.8587266,0.26135,0.56334 +3058,242924.6778187,0.27119,0.55547 +3059,250281.5561948,0.28525,0.5001 +3060,246561.5787976,0.27713,0.43216 +3061,236490.8707572,0.25018,0.38115 +3062,225035.5557451,0.24113,0.35407 +3063,221906.3439892,0.21856,0.31643 +3064,220161.7392049,0.18453,0.28235 +3065,223701.7176957,0.15328,0.26045 +3066,227537.0790072,0.10989,0.19139 +3067,228423.227469,0.05861,0.13817 +3068,224597.0968707,0.0045832,0.13502 +3069,227200.1579774,0,0.14376 +3070,222797.1078076,0,0.17478 +3071,206874.1276336,0,0.21754 +3072,194671.1248569,0,0.25749 +3073,184235.8036472,0,0.27988 +3074,181826.5875166,0,0.29547 +3075,180691.2097998,0,0.30631 +3076,182620.428847,0,0.30615 +3077,182643.5056298,0,0.30989 +3078,176200.4678551,0.029749,0.28411 +3079,179560.4474396,0.1195,0.41323 +3080,193563.4392796,0.19882,0.65108 +3081,201778.773978,0.25374,0.73022 +3082,208461.8102945,0.27944,0.71939 +3083,216903.2974649,0.36514,0.63525 +3084,212500.2472951,0.43948,0.55012 +3085,201921.8500317,0.49332,0.49957 +3086,195100.3530181,0.45215,0.47584 +3087,191518.8363181,0.39048,0.49273 +3088,191758.8348598,0.31361,0.58062 +3089,200108.0148989,0.20802,0.64513 +3090,212043.3269945,0.11485,0.60668 +3091,216566.3764352,0.044252,0.45245 +3092,218878.6700778,0.0030082,0.3724 +3093,225815.5510058,0,0.39435 +3094,224906.3257611,0,0.43066 +3095,207095.6647491,0,0.46503 +3096,196572.6517646,0,0.46711 +3097,192483.4458417,0,0.43641 +3098,191449.6059695,0,0.40517 +3099,193900.3603093,0,0.36688 +3100,202734.1527884,0,0.29735 +3101,214461.7738383,0,0.2412 +3102,247498.4961817,0.033622,0.19599 +3103,276778.3182755,0.129000,0.20924 +3104,293264.3719512,0.22474,0.20223 +3105,297427.4235793,0.30363,0.23612 +3106,302795.0832727,0.35991,0.26049 +3107,307982.7440598,0.39615,0.27884 +3108,307996.5901295,0.40781,0.29312 +3109,304858.1476605,0.39606,0.30400 +3110,294759.7474806,0.36684,0.33152 +3111,288972.0903391,0.32017,0.35305 +3112,280465.9881766,0.26005,0.40912 +3113,281167.5223756,0.1986,0.46119 +3114,284273.6573487,0.13169,0.46821 +3115,283825.9677612,0.065855,0.37608 +3116,274059.8732545,0.0064412,0.37322 +3117,270127.589455,0,0.40302 +3118,255335.3716413,0,0.4169 +3119,232604.7405234,0,0.4102 +3120,217849.4455622,0,0.40415 +3121,210446.4136205,0,0.3973 +3122,207455.6625617,0,0.38374 +3123,209034.1145094,0,0.34934 +3124,216510.9921563,0,0.28679 +3125,227246.3115431,0,0.22747 +3126,254352.3006914,0.0377,0.1439 +3127,279141.3808404,0.14275,0.15036 +3128,290185.9291176,0.21547,0.16086 +3129,292142.8403042,0.24629,0.16763 +3130,296250.5076534,0.23339,0.20581 +3131,300533.5585524,0.28298,0.29491 +3132,294307.4425365,0.32023,0.38608 +3133,290421.3123028,0.3415,0.45435 +3134,286059.8003421,0.33938,0.48932 +3135,287130.5630668,0.3186,0.4889 +3136,283004.4342913,0.27957,0.48291 +3137,285353.6507866,0.24201,0.46052 +3138,288381.3246979,0.18195,0.40004 +3139,288464.4011162,0.1044,0.25731 +3140,280447.5267504,0.012419,0.18751 +3141,276422.9358194,0,0.14932 +3142,263799.9355946,0,0.11847 +3143,239744.6971405,0,0.10126 +3144,222210.9575229,0,0.09136 +3145,217581.754881,0,0.083395 +3146,214014.0842508,0,0.073312 +3147,216317.1471803,0,0.061097 +3148,222746.3388853,0,0.043897 +3149,230412.4461516,0,0.025909 +3150,259115.3486739,0.045973,0.014862 +3151,287661.3290726,0.17932,0.016376 +3152,300556.6353352,0.28295,0.018217 +3153,301945.8576634,0.34877,0.022497 +3154,305342.7601005,0.37241,0.025423 +3155,310590.4205231,0.38714,0.032704 +3156,306030.4482298,0.37327,0.046875 +3157,303422.7717665,0.33531,0.064778 +3158,297122.8100455,0.32037,0.087678 +3159,293605.9083376,0.28911,0.11761 +3160,286059.8003421,0.24385,0.17041 +3161,283309.0478251,0.22287,0.23038 +3162,283692.1224206,0.17512,0.26503 +3163,281347.5212819,0.10493,0.23905 +3164,273764.4904339,0.012944,0.30688 +3165,268922.9813897,0,0.34252 +3166,252404.6202179,0,0.32118 +3167,226503.239135,0,0.28428 +3168,206269.5159227,0,0.24818 +3169,195663.4265199,0,0.22391 +3170,189460.3872869,0,0.21852 +3171,191311.1452723,0,0.20962 +3172,194920.3541118,0,0.1361 +3173,190000.3840059,0,0.10332 +3174,180335.8273438,0.050232,0.081981 +3175,184489.6482587,0.18423,0.087789 +3176,197352.6470253,0.30841,0.17958 +3177,211424.8692138,0.41226,0.25699 +3178,220849.4273341,0.48742,0.24968 +3179,231049.3653586,0.51836,0.20598 +3180,228737.0717159,0.51523,0.16308 +3181,220618.6595055,0.48188,0.12857 +3182,213589.4714461,0.43685,0.10635 +3183,209675.6490729,0.37282,0.093296 +3184,208697.1934796,0.29573,0.10466 +3185,213321.7807649,0.20076,0.12599 +3186,218975.5925658,0.11527,0.12343 +3187,219963.2788723,0.048229,0.18709 +3188,218758.6708069,0.0049214,0.30123 +3189,221246.3479993,0,0.32182 +3190,218814.0550858,0,0.29973 +3191,202244.9249918,0,0.28436 +3192,191648.0663021,0,0.2739 +3193,187198.8625666,0,0.28165 +3194,185661.948828,0,0.31511 +3195,187101.9400785,0,0.33055 +3196,193660.3617676,0,0.31778 +3197,197624.9530631,0,0.28895 +3198,215112.5391149,0.045478,0.31082 +3199,238770.8569038,0.15308,0.38274 +3200,256110.7515454,0.25973,0.52337 +3201,265576.8478749,0.35037,0.62675 +3202,269066.0574434,0.41851,0.61989 +3203,274932.1756466,0.48027,0.56871 +3204,273178.3401492,0.51789,0.51907 +3205,266970.6855597,0.52955,0.50796 +3206,260430.7252969,0.4548,0.54264 +3207,257319.9749673,0.3621,0.61465 +3208,254019.9950182,0.26191,0.66604 +3209,256599.979342,0.2023,0.72212 +3210,258183.0466463,0.13666,0.7684 +3211,255446.140199,0.071306,0.70832 +3212,247516.957608,0.0089099,0.63769 +3213,245592.3539174,0,0.53258 +3214,235775.4904885,0,0.33935 +3215,215158.6926807,0,0.17892 +3216,199701.8635205,0,0.10152 +3217,192760.367236,0,0.059817 +3218,191191.1460015,0,0.028046 +3219,189718.847255,0,0.019516 +3220,192783.4440189,0,0.015633 +3221,193688.053907,0,0.012903 +3222,198829.5611284,0.037004,0.012139 +3223,210631.0278834,0.10552,0.011041 +3224,229613.9894647,0.15947,0.010616 +3225,244507.745123,0.18625,0.012001 +3226,251546.1638955,0.18381,0.018115 +3227,256909.2082324,0.3098,0.033575 +3228,255755.3690893,0.43761,0.068795 +3229,247950.8011258,0.54917,0.11272 +3230,240146.2331623,0.54076,0.14719 +3231,235697.0294267,0.5043,0.16493 +3232,234100.1160528,0.44134,0.14878 +3233,238807.7797564,0.3035,0.11495 +3234,243487.7513205,0.17787,0.06726 +3235,242564.6800061,0.077723,0.065536 +3236,233744.7335967,0.010338,0.093654 +3237,232752.4319337,0,0.11029 +3238,228450.9196085,0,0.1035 +3239,210271.0300708,0,0.080463 +3240,194578.8177254,0,0.05767 +3241,188177.3181599,0,0.043468 +3242,187429.6303952,0,0.033238 +3243,191098.83887,0,0.027629 +3244,192783.4440189,0,0.023423 +3245,189354.2340858,0,0.022892 +3246,183405.0394642,0.037188,0.026231 +3247,188666.5459565,0.093414,0.032571 +3248,197749.5676905,0.15111,0.054391 +3249,208397.1953025,0.19272,0.10459 +3250,217997.1369725,0.21656,0.13259 +3251,229401.6830623,0.2615,0.13349 +3252,228700.1488634,0.29485,0.13199 +3253,223041.7217059,0.31405,0.13912 +3254,216464.8385906,0.26279,0.16347 +3255,211683.3291818,0.20164,0.19172 +3256,210584.8743177,0.13873,0.19951 +3257,215029.4626966,0.11162,0.23439 +3258,220960.1958919,0.078552,0.31238 +3259,220632.5055752,0.042744,0.36845 +3260,215615.6129813,0.0046741,0.3516 +3261,221546.3461765,0,0.2786 +3262,226290.9327327,0,0.19907 +3263,207843.3525138,0,0.14289 +3264,194098.8206419,0,0.11682 +3265,192174.2169513,0,0.10993 +3266,191994.218045,0,0.11343 +3267,194643.4327174,0,0.11443 +3268,201197.2390499,0,0.089406 +3269,211775.6363133,0,0.055205 +3270,243427.7516851,0.061364,0.032657 +3271,273418.338691,0.19672,0.026935 +3272,285824.4171569,0.28982,0.017365 +3273,287264.4084074,0.33661,0.012565 +3274,290832.0790377,0.33504,0.012468 +3275,297644.3453382,0.35907,0.016358 +3276,297759.7292525,0.35997,0.022628 +3277,296153.5851654,0.33992,0.029569 +3278,289765.9316695,0.30819,0.039379 +3279,284333.6569841,0.26267,0.05212 +3280,280189.0667823,0.2082,0.065725 +3281,279178.303693,0.1698,0.07419 +3282,281430.5977002,0.12157,0.073257 +3283,281393.6748476,0.068308,0.062324 +3284,273746.0290076,0.0097488,0.062263 +3285,269878.3602001,0,0.052983 +3286,258866.119419,0,0.036479 +3287,232784.7394297,0,0.029781 +3288,213981.7767548,0,0.034249 +3289,208877.192386,0,0.047168 +3290,208757.1931151,0,0.060939 +3291,211032.5639052,0,0.065652 +3292,218555.5951178,0,0.063329 +3293,227980.1532381,0,0.055656 +3294,253835.3807553,0.062466,0.042321 +3295,279182.9190496,0.19583,0.039092 +3296,290970.5397349,0.3162,0.025306 +3297,292272.0702882,0.41214,0.018067 +3298,299176.6437202,0.47725,0.016346 +3299,307142.7491637,0.51103,0.016258 +3300,305042.7619234,0.51354,0.013212 +3301,303621.2320991,0.48715,0.011892 +3302,297312.039665,0.43869,0.011842 +3303,288219.7872179,0.37127,0.01122 +3304,283285.9710422,0.2918,0.0097219 +3305,281956.7483494,0.23858,0.0082284 +3306,281993.671202,0.17212,0.0066102 +3307,281019.8309653,0.098547,0 +3308,274018.3350453,0.015771,0 +3309,272689.1123526,0,0 +3310,262729.1728699,0,0.0090672 +3311,241964.6836517,0,0.032216 +3312,226346.3170116,0,0.082328 +3313,222709.4160327,0,0.14544 +3314,221264.8094256,0,0.19901 +3315,224384.7904684,0,0.22535 +3316,228150.9214313,0,0.23167 +3317,235507.7998073,0,0.23658 +3318,261723.0251371,0.05728,0.19188 +3319,288159.7875824,0.16551,0.18009 +3320,301281.2463171,0.26492,0.37004 +3321,304336.6123678,0.34409,0.5446 +3322,310618.1126625,0.39714,0.62297 +3323,317338.0718316,0.47261,0.65503 +3324,313955.0154642,0.52864,0.64647 +3325,312131.9496182,0.55983,0.60153 +3326,301876.6273149,0.50126,0.5305 +3327,295839.7409185,0.42179,0.45769 +3328,290467.4658685,0.3291,0.40081 +3329,290093.6219862,0.25217,0.29547 +3330,290938.2322389,0.1701,0.23552 +3331,287287.4851903,0.090762,0.29432 +3332,277346.0071339,0.014641,0.39481 +3333,271470.6582175,0,0.47984 +3334,259392.2700682,0,0.55864 +3335,236107.7961617,0,0.61484 +3336,219358.6671613,0,0.62283 +3337,213321.7807649,0,0.55900 +3338,210880.2571383,0,0.49777 +3339,212398.7094505,0,0.46212 +3340,216958.6817438,0,0.40864 +3341,225321.7078525,0,0.33988 +3342,254356.916048,0.031064,0.24086 +3343,283175.2024845,0.046377,0.25063 +3344,297238.1939598,0.080408,0.43431 +3345,299208.9512162,0.11226,0.60518 +3346,303108.9275196,0.13874,0.63056 +3347,309215.0442646,0.24281,0.6219 +3348,307050.4420322,0.34952,0.63761 +3349,303482.771402,0.4428,0.69019 +3350,298618.1855749,0.37457,0.7683 +3351,292548.9916825,0.29423,0.78943 +3352,285529.0343363,0.20933,0.74385 +3353,283779.8141954,0.15182,0.53784 +3354,283175.2024845,0.096019,0.31486 +3355,280290.6046269,0.047194,0.27443 +3356,272296.8070439,0.0067432,0.21224 +3357,266781.4559402,0,0.12516 +3358,257749.2031285,0,0.080458 +3359,233758.5796664,0,0.070445 +3360,215726.381539,0,0.058254 +3361,209029.4991528,0,0.039764 +3362,208231.0424659,0,0.047021 +3363,211364.8695784,0,0.086356 +3364,216483.3000169,0,0.098073 +3365,224832.4800559,0,0.054098 +3366,250466.1704576,0.059849,0.045065 +3367,276861.3946938,0.16266,0.06472 +3368,289050.5514008,0.25971,0.10002 +3369,289373.6263609,0.33714,0.14524 +3370,291381.3064698,0.38887,0.17893 +3371,292890.5280689,0.43432,0.19477 +3372,287545.9451583,0.45661,0.21954 +3373,280489.0649595,0.45534,0.21686 +3374,272799.8809103,0.39497,0.18838 +3375,267215.299458,0.31922,0.13834 +3376,262512.251111,0.23642,0.087139 +3377,263061.478543,0.18732,0.054063 +3378,263223.0160231,0.13119,0.026937 +3379,262419.9439795,0.073295,0.017622 +3380,254758.4520697,0.012262,0.034063 +3381,250992.3211069,0,0.097418 +3382,242735.4481993,0,0.20229 +3383,222635.5703276,0,0.30082 +3384,205254.1374768,0,0.36411 +3385,195677.2725896,0,0.3865 +3386,191329.6066986,0,0.37236 +3387,193480.3628613,0,0.34259 +3388,196138.8082468,0,0.29615 +3389,194264.9734785,0,0.2123 +3390,194924.9694683,0.070705,0.10506 +3391,206717.2055102,0.19963,0.11438 +3392,221721.7297263,0.31132,0.20039 +3393,229466.2980543,0.39716,0.36536 +3394,231843.206689,0.45166,0.54844 +3395,232780.1240731,0.50246,0.61652 +3396,225866.3199281,0.5265,0.5906 +3397,215892.5343756,0.52395,0.53353 +3398,206855.6662074,0.46768,0.43289 +3399,202872.6134856,0.39219,0.31922 +3400,202281.8478444,0.30494,0.23535 +3401,208918.7305951,0.2084,0.18852 +3402,215938.6879414,0.12169,0.086142 +3403,217960.21412,0.053588,0.052844 +3404,211789.482383,0.0084992,0.046091 +3405,210797.18072,0,0.047839 +3406,208521.8099299,0,0.062051 +3407,192557.2915468,0,0.10296 +3408,180543.5183895,0,0.18291 +3409,174834.3223098,0,0.26425 +3410,171765.1101893,0,0.28813 +3411,169425.1244072,0,0.28588 +3412,168912.8198277,0,0.28137 +3413,165945.1455518,0,0.24983 +3414,164103.6182795,0.073693,0.16418 +3415,168732.8209214,0.20396,0.25138 +3416,180312.7505609,0.30098,0.3839 +3417,184872.7228542,0.36131,0.43208 +3418,188901.9291417,0.38127,0.49381 +3419,195829.5793565,0.41496,0.53476 +3420,190840.378902,0.42407,0.56465 +3421,181974.2789269,0.41023,0.54371 +3422,175978.9307397,0.40983,0.46627 +3423,173205.1014398,0.38824,0.36034 +3424,175166.627983,0.34596,0.28556 +3425,185283.4895891,0.26196,0.21793 +3426,194348.0498968,0.17507,0.13449 +3427,198243.4108437,0.093673,0.06533 +3428,196914.1881509,0.017428,0.038087 +3429,199766.4785125,0,0.02695 +3430,201395.6993825,0,0.031766 +3431,188315.778857,0,0.057648 +3432,174049.7116925,0,0.12032 +3433,166937.4472148,0,0.21941 +3434,165423.6102591,0,0.3334 +3435,165538.9941734,0,0.41014 +3436,167343.5985932,0,0.40803 +3437,166018.9912569,0,0.34261 +3438,166660.5258205,0.076703,0.21851 +3439,171889.7248167,0.20833,0.30137 +3440,178535.8382806,0.27172,0.43144 +3441,184277.3418564,0.27314,0.48062 +3442,190531.1500117,0.21372,0.51866 +3443,198257.2569134,0.2186,0.57348 +3444,196392.6528583,0.20725,0.60671 +3445,189354.2340858,0.18224,0.62206 +3446,184540.417181,0.196000,0.62709 +3447,182694.2745521,0.19744,0.61902 +3448,185768.1020292,0.18526,0.59274 +3449,195760.3490079,0.16492,0.55858 +3450,201949.5421712,0.12915,0.39585 +3451,207257.2022291,0.081115,0.26414 +3452,206167.9780781,0.015362,0.27436 +3453,209638.7262204,0,0.32186 +3454,212569.4776437,0,0.41476 +3455,197417.2620173,0,0.50166 +3456,183728.1144243,0,0.52611 +3457,178780.4521789,0,0.4734 +3458,180045.0598797,0,0.42475 +3459,181065.0536822,0,0.39925 +3460,186820.4033276,0,0.38897 +3461,196517.2674857,0,0.39921 +3462,227795.5389752,0.065795,0.45036 +3463,260301.4953129,0.16403,0.51555 +3464,275975.2462319,0.25902,0.55482 +3465,280922.9084773,0.3367,0.61900 +3466,285552.1111192,0.39094,0.66132 +3467,291256.6918423,0.47799,0.64796 +3468,289267.4731597,0.54692,0.5633 +3469,286821.3341765,0.59198,0.41712 +3470,283341.3553211,0.54099,0.26986 +3471,281153.6763059,0.46826,0.16465 +3472,274175.2571688,0.37917,0.1048 +3473,273612.183667,0.27925,0.053661 +3474,274650.6388957,0.18067,0.01919 +3475,271682.9646198,0.093353,0.010796 +3476,261099.9519999,0.018585,0.013302 +3477,256327.6733043,0,0.021676 +3478,247424.6504766,0,0.046696 +3479,225981.7038424,0,0.10422 +3480,207709.5071732,0,0.18657 +3481,203426.4562742,0,0.2741 +3482,200735.7033927,0,0.35315 +3483,200814.1644544,0,0.42742 +3484,205323.3678254,0,0.48384 +3485,215195.6155332,0,0.48523 +3486,244826.2047265,0.079008,0.39532 +3487,274830.637802,0.2085,0.35655 +3488,289858.238801,0.3248,0.4191 +3489,293019.7580529,0.41923,0.55395 +3490,298604.3395052,0.48631,0.62462 +3491,304950.4547919,0.53693,0.68278 +3492,301396.6302314,0.55984,0.73172 +3493,297865.8824537,0.55396,0.74834 +3494,291284.3839818,0.50296,0.74211 +3495,285635.1875375,0.43167,0.70774 +3496,280027.5293023,0.34642,0.55073 +3497,280535.2185252,0.24235,0.30642 +3498,283544.4310103,0.14671,0.17789 +3499,280198.2974955,0.069058,0.13585 +3500,269532.2084572,0.013289,0.13123 +3501,263361.4767202,0,0.14357 +3502,248500.0285579,0,0.12762 +3503,222884.7995825,0,0.065813 +3504,210704.8735885,0,0.031883 +3505,206920.2811994,0,0.01547 +3506,203694.1469554,0,0.0066214 +3507,205171.0610585,0,0 +3508,209874.1094055,0,0 +3509,219381.7439442,0.00017452,0 +3510,248956.9488585,0.080692,0 +3511,277212.1617933,0.20997,0 +3512,291192.0768503,0.34129,0 +3513,295175.1295721,0.46114,0 +3514,298470.4941646,0.56055,0 +3515,302130.4719263,0.62647,0 +3516,297270.5014559,0.66221,0.006098 +3517,292068.994599,0.66746,0.0084959 +3518,285889.0321489,0.6298,0.012216 +3519,282252.1311701,0.56507,0.018653 +3520,274918.3295769,0.4775,0.029184 +3521,275370.634521,0.34388,0.040283 +3522,276819.8564846,0.21617,0.065451 +3523,276316.7826183,0.10768,0.1356 +3524,268258.3700433,0.022778,0.24278 +3525,262558.4046767,0,0.32794 +3526,249136.9477649,0,0.39308 +3527,224980.1714662,0,0.44869 +3528,209883.3401187,0,0.49584 +3529,206777.2051456,0,0.51808 +3530,203227.9959416,0,0.51600 +3531,204206.4515349,0,0.50262 +3532,210234.1072182,0,0.47626 +3533,217563.2934548,0.0002589,0.40926 +3534,244184.6701629,0.08071,0.26456 +3535,272209.1152691,0.20791,0.17118 +3536,285999.8007067,0.33752,0.16742 +3537,285542.880406,0.45572,0.24504 +3538,288829.0142854,0.55445,0.31248 +3539,292068.994599,0.61397,0.36441 +3540,287822.8665527,0.64386,0.42747 +3541,281629.0580328,0.64607,0.47691 +3542,273418.338691,0.58567,0.52707 +3543,269689.1305807,0.50012,0.6144 +3544,264810.6986839,0.39888,0.66745 +3545,266199.9210121,0.30682,0.62775 +3546,266195.3056555,0.20965,0.49985 +3547,263246.0928059,0.11661,0.31563 +3548,253147.6926261,0.025521,0.26233 +3549,245869.2753118,0,0.29147 +3550,240257.00172,0,0.32325 +3551,221315.5783479,0,0.29479 +3552,204201.8361784,0,0.24919 +3553,197112.6484835,0,0.19584 +3554,194177.2817037,0,0.1248 +3555,196563.4210515,0,0.069103 +3556,198912.6375467,0,0.038112 +3557,197689.5680551,0.0017217,0.02134 +3558,197537.2612882,0.084691,0.01736 +3559,206546.437317,0.21558,0.020045 +3560,220950.9651787,0.34549,0.023586 +3561,229263.2223652,0.46113,0.023021 +3562,232586.2790971,0.55325,0.023666 +3563,234192.4231842,0.56903,0.026683 +3564,226447.8548562,0.54835,0.028274 +3565,216681.7603495,0.49614,0.026675 +3566,209518.7269495,0.43924,0.02312 +3567,206814.1279982,0.36498,0.021906 +3568,207100.2801057,0.28093,0.027154 +3569,213700.2400039,0.20106,0.038505 +3570,219935.5867328,0.1259,0.080289 +3571,222363.2642898,0.062503,0.17453 +3572,216552.5303655,0.012594,0.2374 +3573,214937.1555652,0,0.2508 +3574,213104.8590061,0,0.25251 +3575,200592.6273389,0,0.25554 +3576,190932.6860334,0,0.26143 +3577,181245.0525885,0,0.25663 +3578,177608.1516096,0,0.2474 +3579,175540.4718653,0,0.21972 +3580,174594.323768,0,0.18852 +3581,172420.4908225,0.0011718,0.14793 +3582,173878.9434993,0.055947,0.11179 +3583,177677.3819582,0.10637,0.076954 +3584,186903.4797459,0.14477,0.049013 +3585,196803.4195932,0.15644,0.027629 +3586,206518.7451776,0.14149,0.014793 +3587,218218.674088,0.14369,0.0082655 +3588,216769.4521243,0.13517,0.0057252 +3589,208567.9634956,0.11744,0.0071957 +3590,202424.9238981,0.10061,0.014132 +3591,200117.245612,0.080024,0.035927 +3592,200297.2445183,0.058087,0.10464 +3593,207109.5108188,0.061316,0.21456 +3594,216852.5285426,0.054242,0.29178 +3595,219704.8189042,0.037653,0.27649 +3596,216690.9910626,0.0067512,0.16657 +3597,220037.1245774,0,0.10688 +3598,220194.0467009,0,0.090921 +3599,204824.9093156,0,0.13406 +3600,191883.4494873,0,0.23047 +3601,187171.1704271,0,0.32573 +3602,189709.6165418,0,0.35985 +3603,192746.5211663,0,0.35579 +3604,200274.1677355,0,0.31204 +3605,210797.18072,0.0022528,0.22641 +3606,242458.526805,0.086906,0.18385 +3607,267824.5265255,0.21421,0.19254 +3608,284573.6555259,0.29488,0.18396 +3609,290827.4636811,0.33113,0.22721 +3610,297792.0367485,0.32193,0.2964 +3611,303750.4620832,0.37169,0.37835 +3612,299642.7947339,0.40474,0.45901 +3613,298096.6502823,0.41841,0.5223 +3614,292802.836294,0.43068,0.56076 +3615,288399.7861242,0.41974,0.5326 +3616,283945.967032,0.38516,0.52269 +3617,283789.0449086,0.31345,0.49178 +3618,283802.8909783,0.22913,0.42635 +3619,280890.6009813,0.13882,0.28752 +3620,270644.5093911,0.062551,0.20616 +3621,262470.7129018,0,0.17944 +3622,251744.6242281,0,0.16859 +3623,231893.9756113,0,0.16437 +3624,216270.9936146,0,0.16622 +3625,207584.8925458,0,0.1612 +3626,204663.3718356,0,0.14378 +3627,206772.5897891,0,0.11027 +3628,211886.404871,0,0.076465 +3629,217849.4455622,0.0024097,0.048894 +3630,245435.431794,0.086726,0.035859 +3631,273986.0275493,0.2106,0.036404 +3632,286775.1806108,0.32593,0.03707 +3633,289189.012098,0.4219,0.037923 +3634,292908.9894952,0.49166,0.045087 +3635,295682.818795,0.54396,0.05102 +3636,289512.0870581,0.5685,0.048008 +3637,285219.805446,0.56551,0.044548 +3638,282256.7465266,0.50427,0.038541 +3639,280175.2207126,0.42314,0.02808 +3640,273879.8743482,0.33006,0.018696 +3641,276441.3972457,0.23629,0.010924 +3642,276699.8572138,0.1481,0 +3643,273598.3375973,0.074153,0 +3644,264949.1593811,0.045337,0.0057719 +3645,257744.5877719,0,0.03075 +3646,245804.6603198,0,0.12107 +3647,223932.4855243,0,0.2593 +3648,211867.9434447,0,0.32753 +3649,203860.299792,0,0.34744 +3650,199337.2503513,0,0.36386 +3651,200228.0141697,0,0.40995 +3652,206352.592341,0,0.43907 +3653,218527.9029783,0.0011367,0.43924 +3654,246436.9641702,0.041417,0.42055 +3655,272836.8037629,0.04862,0.40869 +3656,287329.0233994,0.076184,0.47631 +3657,290116.698769,0.098066,0.51967 +3658,295290.5134864,0.11203,0.57694 +3659,301299.7077433,0.089505,0.64409 +3660,298742.8002024,0.054891,0.69016 +3661,296010.5091117,0.013832,0.66439 +3662,290569.0037131,0.059454,0.56112 +3663,287121.3323537,0.098667,0.35429 +3664,281670.596242,0.12373,0.20047 +3665,278712.1526792,0.11024,0.11796 +3666,275795.2473256,0.08703,0.072181 +3667,271595.272845,0.056259,0.038042 +3668,261455.3344559,0.039309,0.035553 +3669,251730.7781584,0.00041625,0.051198 +3670,243746.2112886,0,0.06845 +3671,228529.3806702,0,0.091688 +3672,212887.9372472,0,0.11379 +3673,204811.0632459,0,0.12573 +3674,200001.8616977,0,0.13923 +3675,199438.7881959,0,0.17200 +3676,200735.7033927,0,0.19851 +3677,198275.7183397,0.0017972,0.18942 +3678,200288.0138052,0.066916,0.11500 +3679,210543.3361085,0.1453,0.10014 +3680,222040.1893297,0.24035,0.16885 +3681,229747.8348052,0.33261,0.19451 +3682,238512.3969358,0.41445,0.1407 +3683,246570.8095107,0.45461,0.10856 +3684,245080.0493379,0.47099,0.10484 +3685,238037.0152088,0.46356,0.095975 +3686,231321.6713963,0.40153,0.080836 +3687,225750.9360138,0.32312,0.08257 +3688,222174.0346703,0.23846,0.062146 +3689,225663.2442389,0.14232,0.042275 +3690,229973.9872773,0.065749,0.046923 +3691,229969.3719207,0.016677,0.092643 +3692,226637.0844756,0.028244,0.32632 +3693,223614.0259209,0.00021193,0.6088 +3694,224246.3297712,0,0.72017 +3695,208092.5817687,0,0.81553 +3696,195354.1976296,0,0.87584 +3697,186446.5594453,0,0.8569 +3698,181932.7407177,0,0.81825 +3699,181951.202144,0,0.84131 +3700,187946.5503312,0,0.90251 +3701,198081.8733637,0.0021706,0.93335 +3702,219746.3571134,0.074483,0.95067 +3703,242878.524253,0.17051,0.96527 +3704,254296.9164125,0.21675,0.97269 +3705,257989.2016703,0.21475,0.96873 +3706,262567.6353898,0.16501,0.96916 +3707,266606.0723905,0.19035,0.96317 +3708,261584.56444,0.20677,0.95613 +3709,253835.3807553,0.21343,0.95367 +3710,247784.6482892,0.22864,0.9253 +3711,242993.9081673,0.23011,0.87106 +3712,241249.303383,0.21662,0.80716 +3713,242735.4481993,0.1859,0.73825 +3714,243723.1345057,0.14268,0.64465 +3715,241964.6836517,0.090803,0.57234 +3716,233910.8864333,0.050267,0.56177 +3717,229840.1419367,0.00108,0.57707 +3718,227500.1561546,0,0.60765 +3719,210280.2607839,0,0.61406 +3720,193812.6685345,0,0.58806 +3721,184111.1890198,0,0.55631 +3722,180174.2898637,0,0.52134 +3723,181498.8971999,0,0.44478 +3724,180991.207977,0,0.3578 +3725,180192.75129,0.0024833,0.29886 +3726,189275.773024,0.052535,0.25032 +3727,202397.2317587,0.092243,0.25842 +3728,216852.5285426,0.13639,0.39399 +3729,226009.3959818,0.16683,0.59592 +3730,231953.9752467,0.18086,0.70757 +3731,233315.5054355,0.18831,0.74735 +3732,226323.2402287,0.18291,0.74458 +3733,217244.8338513,0.16585,0.72119 +3734,211466.4074229,0.15297,0.66472 +3735,207686.4303903,0.13322,0.63501 +3736,207215.66402,0.10873,0.54841 +3737,210391.0293416,0.10308,0.49447 +3738,214646.3881012,0.085999,0.35799 +3739,214701.77238,0.05835,0.22886 +3740,206532.5912473,0.041728,0.14573 +3741,203971.0683498,0.00098264,0.10109 +3742,206495.6683947,0,0.06428 +3743,192755.7518794,0,0.047481 +3744,180381.9809095,0,0.044439 +3745,171082.0374166,0,0.04916 +3746,166660.5258205,0,0.056344 +3747,164255.9250464,0,0.059303 +3748,163180.5469651,0,0.062736 +3749,159728.2602491,0.0028864,0.054664 +3750,164112.8489926,0.057373,0.078716 +3751,175180.4740527,0.10614,0.099099 +3752,186368.0983836,0.15476,0.10044 +3753,194435.7416717,0.18672,0.10882 +3754,201081.8551356,0.19971,0.14507 +3755,207907.9675058,0.24749,0.22205 +3756,205429.5210266,0.28701,0.35341 +3757,198072.6426506,0.31441,0.53645 +3758,192557.2915468,0.30552,0.68349 +3759,191412.6831169,0.28191,0.74411 +3760,193651.1310544,0.24498,0.76061 +3761,203707.9930252,0.1899,0.79796 +3762,214332.5438542,0.13169,0.82834 +3763,217724.8309348,0.07561,0.81145 +3764,214660.2341709,0.047702,0.65353 +3765,214000.2381811,0.0013762,0.56488 +3766,216003.3029334,0,0.55358 +3767,198455.717246,0,0.57686 +3768,187235.7854191,0,0.62708 +3769,184660.4164519,0,0.67862 +3770,179786.5999117,0,0.73678 +3771,181545.0507657,0,0.79458 +3772,186575.7894293,0,0.82694 +3773,198404.9483237,0.0036747,0.81761 +3774,234926.2648792,0.072947,0.83984 +3775,266892.2244979,0.15815,0.92229 +3776,286659.7966965,0.21016,0.93432 +3777,297487.4232147,0.22528,0.92953 +3778,304858.1476605,0.20259,0.90201 +3779,313442.7108847,0.26404,0.84042 +3780,309307.351396,0.31875,0.77618 +3781,305361.2215268,0.36091,0.73283 +3782,298078.188856,0.36719,0.69655 +3783,291870.5342664,0.3543,0.66036 +3784,284047.5048766,0.32219,0.65326 +3785,277244.4692893,0.24501,0.63741 +3786,275264.4813198,0.16643,0.57062 +3787,271009.1225603,0.093668,0.4148 +3788,259932.2667871,0.053539,0.23268 +3789,249972.3273044,0.0026762,0.22049 +3790,241738.5311797,0,0.20639 +3791,220549.4291569,0,0.19495 +3792,206278.7466358,0,0.18524 +3793,198954.1757558,0,0.15905 +3794,195811.1179302,0,0.13475 +3795,196609.5746172,0,0.11795 +3796,201668.0054203,0,0.11615 +3797,212029.4809247,0.0044976,0.093812 +3798,243349.2906234,0.087969,0.085344 +3799,273552.1840316,0.20605,0.11272 +3800,287384.4076783,0.28377,0.092639 +3801,289987.468785,0.32225,0.1047 +3802,294173.5971959,0.32053,0.17137 +3803,299222.7972859,0.38032,0.21847 +3804,295835.1255619,0.42458,0.21915 +3805,294699.7478452,0.44985,0.26873 +3806,292941.2969912,0.4403,0.30635 +3807,288289.0175664,0.4097,0.31518 +3808,281799.826226,0.36056,0.3109 +3809,280696.7560052,0.26587,0.28923 +3810,278998.3047867,0.1739,0.2455 +3811,274055.2578979,0.093263,0.17453 +3812,263610.7059751,0.054363,0.11867 +3813,256650.7482643,0.0029511,0.10146 +3814,246773.8851999,0,0.070149 +3815,225386.3228446,0,0.045174 +3816,212209.4798311,0,0.02988 +3817,203167.9963062,0,0.018778 +3818,198584.9472301,0,0.010876 +3819,198340.3333317,0,0.0060787 +3820,201972.618954,0,0 +3821,213492.5489581,0.0046938,0 +3822,249243.100966,0.08967,0 +3823,277987.5416974,0.20963,0 +3824,290684.3876274,0.32497,0 +3825,292133.609591,0.42507,0 +3826,297473.577145,0.50275,0 +3827,302504.3158087,0.55012,0 +3828,300367.4057158,0.56916,0 +3829,300113.5611043,0.56057,0.0093458 +3830,295747.433787,0.51056,0.015498 +3831,290296.6976753,0.43973,0.025006 +3832,283687.507064,0.35465,0.039365 +3833,280544.4492384,0.25809,0.056732 +3834,279796.7614737,0.16596,0.074917 +3835,276482.9354549,0.087128,0.081362 +3836,265673.7703629,0.05292,0.091542 +3837,256932.2850152,0.0030381,0.089872 +3838,248536.9514105,0,0.063568 +3839,227680.1550609,0,0.039641 +3840,213584.8560896,0,0.024259 +3841,205669.5195683,0,0.019224 +3842,201649.543994,0,0.02309 +3843,201446.4683048,0,0.032717 +3844,205443.3670963,0,0.042282 +3845,216709.4524889,0.004195,0.048929 +3846,251126.1664475,0.07602,0.041286 +3847,279732.1464817,0.1629,0.061289 +3848,293864.3683056,0.25932,0.084624 +3849,295825.8948488,0.34812,0.10034 +3850,299822.7936403,0.42266,0.11729 +3851,304775.0712422,0.4125,0.14902 +3852,301267.4002473,0.37012,0.20593 +3853,300242.7910883,0.30256,0.30917 +3854,297044.3489838,0.25495,0.43533 +3855,292862.8359294,0.1981,0.53136 +3856,287282.8698337,0.13835,0.58835 +3857,284338.2723407,0.12102,0.5913 +3858,282330.5922318,0.094663,0.54123 +3859,279436.7636611,0.061653,0.46556 +3860,268622.9832125,0.046597,0.30881 +3861,260527.6477849,0.003061,0.23803 +3862,254799.9902789,0,0.20857 +3863,234390.8835168,0,0.18858 +3864,218070.9826777,0,0.16466 +3865,208069.5049858,0,0.15129 +3866,205009.5235785,0,0.15544 +3867,206347.9769844,0,0.17088 +3868,211692.559895,0,0.18559 +3869,219390.9746573,0.0043249,0.16474 +3870,248061.5696835,0.076235,0.18324 +3871,277161.392871,0.16214,0.3191 +3872,293292.0640907,0.21676,0.39365 +3873,297833.5749577,0.23535,0.43607 +3874,303510.4635414,0.21683,0.48345 +3875,307433.5166277,0.23288,0.53738 +3876,299684.3329431,0.23498,0.58048 +3877,295336.6670521,0.2244,0.59414 +3878,288939.7828431,0.24723,0.58522 +3879,287795.1744132,0.25527,0.56042 +3880,284282.8880618,0.24597,0.50179 +3881,284324.426271,0.20255,0.39827 +3882,280392.1424715,0.15038,0.27855 +3883,272993.7258863,0.093783,0.1838 +3884,260255.3417472,0.056844,0.10822 +3885,253239.9997575,0.0037137,0.10512 +3886,250249.2486987,0,0.11753 +3887,230887.8278786,0,0.14479 +3888,214443.312412,0,0.17395 +3889,202120.3103643,0,0.19722 +3890,195538.8118924,0,0.23053 +3891,192266.5240828,0,0.24769 +3892,191564.9898838,0,0.25586 +3893,190004.9993624,0.0050064,0.20107 +3894,199641.8638851,0.091688,0.16257 +3895,213778.7010656,0.21428,0.13033 +3896,231713.976705,0.2689,0.12714 +3897,247798.4943589,0.26819,0.11815 +3898,257158.4374873,0.21222,0.096709 +3899,258146.1237937,0.22705,0.086018 +3900,252852.3098054,0.22847,0.088494 +3901,244433.8994178,0.21748,0.08997 +3902,235073.9562895,0.1951,0.07726 +3903,229069.3773891,0.16438,0.049604 +3904,226590.9309099,0.12869,0.031197 +3905,229673.9891001,0.094282,0.021734 +3906,232212.4352148,0.061115,0.016711 +3907,228404.7660427,0.032268,0.020486 +3908,217784.8305702,0.038232,0.062921 +3909,212103.3266299,0.0029366,0.16528 +3910,211729.4827476,0,0.31522 +3911,198031.1044414,0,0.42162 +3912,182532.7370721,0,0.45627 +3913,176477.3892494,0,0.42542 +3914,174095.8652582,0,0.35648 +3915,173417.4078421,0,0.27295 +3916,173080.4868123,0,0.24492 +3917,169157.433726,0.0043756,0.25241 +3918,170495.8871319,0.076081,0.34873 +3919,180045.0598797,0.16166,0.47189 +3920,192543.4454771,0.22536,0.55784 +3921,205217.2146242,0.26018,0.55021 +3922,214263.3135057,0.26352,0.57824 +3923,222395.5717858,0.34397,0.53552 +3924,219003.2847053,0.41565,0.43338 +3925,211143.3324629,0.47055,0.31254 +3926,204972.6007259,0.43925,0.26295 +3927,200131.0916817,0.38944,0.26 +3928,199517.2492576,0.32512,0.23931 +3929,205231.060694,0.27177,0.19607 +3930,212274.0948231,0.20516,0.1406 +3931,212297.1716059,0.13082,0.061518 +3932,206546.437317,0.06847,0.025663 +3933,204284.9125967,0.0054932,0.013445 +3934,210164.8768696,0,0.01376 +3935,197241.8784676,0,0.028453 +3936,186303.4833916,0,0.067846 +3937,182154.2778332,0,0.12443 +3938,182168.1239029,0,0.17659 +3939,183446.5776734,0,0.21758 +3940,188731.1609485,0,0.21658 +3941,198183.4112083,0.0050714,0.18254 +3942,234898.5727398,0.091015,0.092734 +3943,265456.848604,0.21134,0.063157 +3944,280281.3739138,0.32564,0.14561 +3945,285538.2650494,0.426000,0.18272 +3946,290619.7726354,0.50444,0.17662 +3947,292830.5284334,0.54408,0.16658 +3948,288741.3225105,0.55411,0.14994 +3949,285607.495398,0.53646,0.11949 +3950,281389.0594911,0.50763,0.087212 +3951,274466.0246328,0.4569,0.062305 +3952,264529.161933,0.38794,0.043468 +3953,264759.9297616,0.28622,0.029827 +3954,269795.2837818,0.18766,0.018677 +3955,268073.7557804,0.10174,0.015172 +3956,258192.2773594,0.059613,0.021363 +3957,249178.485974,0.0051135,0.028255 +3958,241290.8415922,0,0.022652 +3959,219349.4364482,0,0.014431 +3960,207649.5075378,0,0.0091681 +3961,202166.4639301,0,0 +3962,197468.0309396,0,0 +3963,198206.4879911,0,0 +3964,201806.4661174,0,0 +3965,208674.1166968,0.0050811,0 +3966,240164.6945886,0.090702,0.0070907 +3967,269804.514495,0.21032,0.010299 +3968,284887.4997728,0.33267,0.020998 +3969,289350.549578,0.44562,0.039594 +3970,293121.2958975,0.54036,0.05921 +3971,294390.5189548,0.56827,0.069003 +3972,287444.4073137,0.56282,0.071221 +3973,284592.1169521,0.52763,0.067962 +3974,279265.9954679,0.45166,0.069429 +3975,273907.5664876,0.35899,0.076087 +3976,268539.9067942,0.26003,0.080419 +3977,268166.0629119,0.18457,0.068711 +3978,267635.2969061,0.11503,0.030071 +3979,265922.9996178,0.058016,0.019361 +3980,256983.0539375,0.046136,0.030097 +3981,247761.5715064,0.0044321,0.13416 +3982,239541.6214514,0,0.32174 +3983,221370.9626268,0,0.22272 +3984,207257.2022291,0,0.099363 +3985,200915.702299,0,0.10161 +3986,197892.6437442,0,0.13568 +3987,198557.2550906,0,0.15527 +3988,201395.6993825,0,0.19322 +3989,210192.569009,0.0048903,0.17673 +3990,241429.3022894,0.087586,0.1483 +3991,267852.2186649,0.20115,0.22708 +3992,280295.2199835,0.28537,0.24196 +3993,281365.9827082,0.33881,0.25098 +3994,285898.2628621,0.35755,0.29668 +3995,293195.1416026,0.38926,0.34442 +3996,290952.0783086,0.40007,0.36993 +3997,289378.2417175,0.38997,0.37900 +3998,285935.1857146,0.36206,0.37326 +3999,282778.2818193,0.31874,0.33036 +4000,274927.5602901,0.26406,0.27743 +4001,273058.3408783,0.18414,0.27282 +4002,271650.6571238,0.11186,0.26398 +4003,269550.6698835,0.054173,0.18033 +4004,260218.4188946,0.045446,0.17109 +4005,250632.3232942,0.0045698,0.23005 +4006,243196.9838565,0,0.36187 +4007,225044.7864582,0,0.50142 +4008,211572.5606241,0,0.59305 +4009,205960.2870324,0,0.64856 +4010,201557.2368625,0,0.68938 +4011,202604.9228044,0,0.66434 +4012,206837.2047811,0,0.59858 +4013,213607.9328724,0.0031141,0.51359 +4014,245587.7385609,0.04823,0.42496 +4015,272610.6512908,0.069836,0.41495 +4016,285275.1897248,0.10606,0.49176 +4017,287564.4065846,0.13406,0.62026 +4018,293965.9061502,0.15173,0.66995 +4019,300538.1739089,0.21568,0.6695 +4020,297884.3438799,0.27756,0.69713 +4021,295045.8995881,0.32971,0.7358 +4022,290693.6183405,0.25169,0.76629 +4023,287185.9473457,0.16716,0.78083 +4024,280992.1388259,0.087362,0.79123 +4025,279478.3018702,0.085592,0.76273 +4026,277304.4689247,0.073449,0.61152 +4027,273081.4176612,0.051823,0.42495 +4028,263043.0171168,0.045318,0.24968 +4029,252570.7730545,0.004795,0.1366 +4030,246727.7316342,0,0.10006 +4031,225474.0146194,0,0.085386 +4032,210169.4922262,0,0.070756 +4033,205304.9063991,0,0.052898 +4034,204644.9104093,0,0.034814 +4035,205203.3685545,0,0.03083 +4036,208498.733147,0,0.027957 +4037,215772.5351048,0.0049952,0.019685 +4038,244258.5158681,0.089093,0.015041 +4039,271484.5042872,0.20603,0.011127 +4040,285455.1886311,0.3284,0.010059 +4041,289945.9305758,0.44239,0.010058 +4042,294695.1324886,0.53943,0.011963 +4043,300090.4843214,0.61036,0.015668 +4044,294768.9781937,0.65372,0.020719 +4045,277627.5438848,0.66845,0.026076 +4046,266167.6135161,0.64475,0.031608 +4047,263684.5516803,0.59349,0.037366 +4048,270081.4358893,0.51735,0.04145 +4049,269721.4380767,0.38999,0.042575 +4050,268512.2146548,0.26255,0.043483 +4051,263906.0887958,0.14746,0.051235 +4052,254047.6871576,0.075015,0.08017 +4053,244780.0511607,0.0065593,0.1131 +4054,237413.9420716,0,0.13102 +4055,218910.9775738,0,0.13935 +4056,203186.4577325,0,0.13407 +4057,195944.9632708,0,0.12047 +4058,195252.659785,0,0.10405 +4059,193189.5953972,0,0.087662 +4060,192917.2893595,0,0.076962 +4061,191971.1412622,0.0050826,0.05586 +4062,196069.5778982,0.090687,0.027487 +4063,208235.6578224,0.21001,0.020424 +4064,226507.8544916,0.33166,0.022617 +4065,241253.9187396,0.44373,0.016834 +4066,248666.1813945,0.53728,0.010718 +4067,252769.2333871,0.6063,0.0067711 +4068,248320.0296516,0.64815,0 +4069,240206.2327977,0.66247,0 +4070,234773.9581123,0.63671,0.0071332 +4071,228432.4581822,0.58392,0.010523 +4072,225926.3195635,0.50698,0.01284 +4073,228861.6863434,0.38329,0.014115 +4074,230458.5997174,0.25899,0.015751 +4075,228169.3828576,0.14634,0.020314 +4076,217249.4492079,0.074764,0.030544 +4077,209791.0329872,0.0065849,0.044811 +4078,208249.5038922,0,0.073737 +4079,193060.3654132,0,0.11494 +4080,180317.3659175,0,0.15004 +4081,172503.5672408,0,0.17462 +4082,168788.2052002,0,0.18709 +4083,171631.2648487,0,0.1961 +4084,170948.192076,0,0.17528 +4085,167491.2900035,0.0047396,0.13596 +4086,169203.5872917,0.087698,0.06763 +4087,174054.3270491,0.20417,0.039685 +4088,186012.7159275,0.32094,0.048876 +4089,198949.5603993,0.42738,0.055873 +4090,209763.3408478,0.51574,0.053059 +4091,221763.2679354,0.54728,0.052118 +4092,221301.7322782,0.54803,0.054106 +4093,212620.246566,0.52036,0.055198 +4094,204077.2215509,0.5116,0.054027 +4095,200564.9351995,0.47961,0.051032 +4096,201529.5447231,0.4256,0.044832 +4097,205969.5177455,0.31142,0.037071 +4098,214646.3881012,0.20176,0.032678 +4099,215011.0012704,0.10774,0.047599 +4100,211646.4063293,0.062283,0.088283 +4101,210298.7222102,0.0058353,0.15001 +4102,213769.4703524,0,0.23876 +4103,197975.7201625,0,0.31298 +4104,189146.54304,0,0.35002 +4105,186003.4852144,0,0.37015 +4106,184826.5692885,0,0.37431 +4107,185592.7184794,0,0.33342 +4108,193480.3628613,0,0.23856 +4109,205586.44315,0.0036867,0.17342 +4110,239910.8499771,0.064438,0.11223 +4111,269924.5137658,0.12646,0.10699 +4112,283752.122056,0.19874,0.1558 +4113,285709.0332426,0.26321,0.20964 +4114,291053.6161532,0.31494,0.21055 +4115,297718.1910434,0.38768,0.20706 +4116,296319.738002,0.44726,0.23987 +4117,291496.6903841,0.48611,0.25481 +4118,285164.4211671,0.46717,0.21041 +4119,280175.2207126,0.42959,0.19073 +4120,273459.8769001,0.37406,0.25018 +4121,271558.3499924,0.27189,0.3143 +4122,270566.0483294,0.17471,0.33871 +4123,267852.2186649,0.092031,0.24312 +4124,258469.1987538,0.059114,0.14939 +4125,249192.3320437,0.0060693,0.11331 +4126,242472.3728747,0,0.069914 +4127,220544.8138004,0,0.043944 +4128,204054.1447681,0,0.03741 +4129,198234.1801306,0,0.05239 +4130,197191.1095453,0,0.064238 +4131,200532.6277035,0,0.053351 +4132,204621.8336264,0,0.048087 +4133,211600.2527635,0.0046615,0.035759 +4134,243815.4416372,0.085597,0.030815 +4135,272319.8838268,0.19631,0.046577 +4136,282607.5136261,0.3069,0.044963 +4137,280110.6057206,0.40569,0.037452 +4138,284652.1165876,0.48509,0.033129 +4139,292239.7627922,0.53551,0.030162 +4140,292050.5331727,0.55913,0.029321 +4141,289784.3930958,0.55627,0.031267 +4142,284509.0405338,0.53258,0.034087 +4143,279478.3018702,0.48571,0.035166 +4144,273713.7215116,0.41877,0.034733 +4145,271710.6567593,0.32389,0.030633 +4146,270206.0505167,0.22545,0.022995 +4147,267547.6051312,0.13239,0.017965 +4148,258598.4287378,0.071507,0.017364 +4149,250784.6300611,0.0067779,0.013712 +4150,244046.2094658,0,0.0093062 +4151,223332.4891699,0,0.0074843 +4152,209454.1119575,0,0.0083401 +4153,204021.8372721,0,0.01248 +4154,199683.4020942,0,0.020475 +4155,199840.3242177,0,0.03264 +4156,203343.379856,0,0.051403 +4157,210958.7182,0.0046941,0.064125 +4158,242947.7546016,0.088382,0.037467 +4159,270912.2000723,0.20644,0.033098 +4160,280858.2934853,0.32761,0.035495 +4161,278762.9216015,0.43961,0.032234 +4162,281532.1355448,0.53352,0.028962 +4163,289715.1627472,0.6037,0.027346 +4164,287089.0248577,0.64673,0.025808 +4165,282487.5143552,0.66131,0.021543 +4166,278642.9223306,0.62817,0.01588 +4167,276713.7032835,0.56903,0.011831 +4168,270224.511943,0.48762,0.0090885 +4169,270806.0468711,0.34858,0.0081616 +4170,272513.7288028,0.21816,0.010316 +4171,267736.8347506,0.11063,0.018982 +4172,249750.790189,0.064574,0.047054 +4173,238110.860914,0.0063501,0.091011 +4174,238849.3179655,0,0.11648 +4175,226600.161623,0,0.12206 +4176,211106.4096103,0,0.10511 +4177,205309.5217557,0,0.081272 +4178,200749.5494624,0,0.061398 +4179,200957.2405081,0,0.048729 +4180,205120.2921362,0,0.038489 +4181,211604.8681201,0.0042253,0.028509 +4182,240423.1545566,0.080558,0.015156 +4183,264533.7772896,0.18203,0.0074214 +4184,273690.6447287,0.29293,0.0063977 +4185,277479.8524745,0.39792,0.0064386 +4186,284319.8109144,0.48981,0.0063323 +4187,289045.9360443,0.5652,0.0065617 +4188,286719.7963319,0.61876,0.0059209 +4189,283258.2789028,0.64526,0 +4190,279972.1450234,0.5785,0 +4191,277419.852839,0.48871,0 +4192,272089.1159982,0.38433,0 +4193,272333.7298965,0.27258,0 +4194,274059.8732545,0.16916,0 +4195,272278.3456176,0.084952,0 +4196,264067.6262758,0.056779,0.0098453 +4197,255598.4469659,0.005968,0.017149 +4198,247761.5715064,0,0.025923 +4199,226000.1652687,0,0.033351 +4200,212629.4772791,0,0.036551 +4201,204187.9901087,0,0.037783 +4202,203269.5341508,0,0.034867 +4203,204178.7593955,0,0.024917 +4204,207857.1985835,0,0.010983 +4205,213843.3160576,0.0028457,0 +4206,244923.1272145,0.050484,0.0072553 +4207,271373.7357295,0.081651,0.032261 +4208,284776.731215,0.090794,0.11861 +4209,286078.2617684,0.066716,0.31187 +4210,288565.9389608,0.012668,0.49642 +4211,291750.5349956,0.061262,0.60779 +4212,287647.4830029,0.11871,0.60559 +4213,282829.0507416,0.17464,0.49294 +4214,277456.7756916,0.1895,0.37996 +4215,272698.3430657,0.19394,0.34115 +4216,269389.1324035,0.18596,0.35801 +4217,269846.0527041,0.1948,0.38384 +4218,266873.7630716,0.1766,0.37978 +4219,262263.0218561,0.13242,0.31387 +4220,251121.5510909,0.073065,0.17787 +4221,242001.6065043,0.0072754,0.11645 +4222,237437.0188545,0,0.094724 +4223,220729.4280633,0,0.082872 +4224,205401.8288871,0,0.068739 +4225,196844.9578024,0,0.058516 +4226,195557.2733187,0,0.054777 +4227,195451.1201176,0,0.054237 +4228,194324.973114,0,0.050045 +4229,193581.9007058,0.0040884,0.032378 +4230,199032.6368176,0.077946,0.030749 +4231,211738.7134607,0.17261,0.02929 +4232,229013.9931103,0.27722,0.022515 +4233,239121.6240033,0.37467,0.01787 +4234,241595.455126,0.45734,0.015222 +4235,242832.3706873,0.55285,0.014024 +4236,235613.9530085,0.62887,0.014443 +4237,228086.3064393,0.67959,0.018287 +4238,220369.4302506,0.62537,0.025624 +4239,216866.3746124,0.54589,0.036226 +4240,216432.5310946,0.44759,0.050636 +4241,218781.7475898,0.34106,0.074774 +4242,221952.4975549,0.23319,0.11559 +4243,221204.8097902,0.13386,0.14526 +4244,212403.3248071,0.073532,0.18175 +4245,207197.2025937,0.0073155,0.19318 +4246,208249.5038922,0,0.15085 +4247,195146.5065838,0,0.10757 +4248,181895.8178652,0,0.075033 +4249,173195.8707266,0,0.054035 +4250,172138.9540716,0,0.039893 +4251,171912.8015996,0,0.02998 +4252,170297.4267993,0,0.025465 +4253,167062.0618423,0.00444,0.01852 +4254,171234.3441835,0.088512,0.012377 +4255,178614.2993424,0.20975,0.010162 +4256,189243.465528,0.32911,0.0071943 +4257,198968.0218255,0.43491,0 +4258,205101.8307099,0.52017,0 +4259,209421.8044615,0.5948,0.0058925 +4260,207718.7378864,0.64373,0.0071914 +4261,200348.0134406,0.66458,0.010142 +4262,196037.2704022,0.61713,0.01556 +4263,194357.28061,0.54457,0.024338 +4264,191998.8334016,0.45278,0.03899 +4265,193120.3650486,0.34058,0.057387 +4266,204187.9901087,0.22895,0.072361 +4267,209038.729866,0.12861,0.082371 +4268,209384.8816089,0.072017,0.09868 +4269,208895.6538123,0.0072466,0.099379 +4270,215232.5383858,0,0.075602 +4271,199364.9424908,0,0.055971 +4272,189381.9262252,0,0.044529 +4273,188574.2388251,0,0.037164 +4274,189368.0801555,0,0.033329 +4275,188740.3916617,0,0.031601 +4276,192644.9833217,0,0.028753 +4277,200897.2408727,0.0040545,0.022278 +4278,234501.6520746,0.082687,0.015227 +4279,264907.6211719,0.19151,0.025465 +4280,283239.8174765,0.29937,0.027523 +4281,287273.6391206,0.39388,0.024476 +4282,291362.8450435,0.46848,0.020458 +4283,296199.7387311,0.54877,0.018944 +4284,292899.758782,0.60694,0.021584 +4285,290652.0801314,0.63959,0.03036 +4286,287915.1736841,0.62185,0.041909 +4287,285150.5750974,0.57714,0.051407 +4288,279870.6071788,0.50753,0.061674 +4289,278227.5402391,0.37854,0.072675 +4290,278158.3098906,0.25139,0.08075 +4291,276367.5515406,0.13881,0.096954 +4292,268802.9821188,0.075176,0.14427 +4293,258939.9651241,0.0073994,0.17475 +4294,251338.4728498,0,0.16757 +4295,230320.1390202,0,0.14086 +4296,213404.8571832,0,0.11968 +4297,207649.5075378,0,0.11057 +4298,205078.7539271,0,0.11558 +4299,205360.290678,0,0.13258 +4300,209306.4205472,0,0.12299 +4301,215541.7672762,0.0028749,0.071231 +4302,245144.6643299,0.056483,0.034673 +4303,275882.9391005,0.1031,0.028498 +4304,288685.9382316,0.15706,0.023794 +4305,290149.006265,0.1993,0.020701 +4306,295419.7434704,0.22589,0.020258 +4307,298825.8766207,0.26797,0.01967 +4308,298124.3424217,0.30009,0.018499 +4309,295959.7401894,0.31906,0.019959 +4310,292179.7631568,0.31122,0.023196 +4311,289512.0870581,0.28877,0.025159 +4312,285773.6482346,0.25297,0.026418 +4313,285824.4171569,0.17879,0.028885 +4314,285044.4218962,0.11086,0.032834 +4315,282039.8247677,0.055542,0.050266 +4316,271350.6589466,0.048583,0.085496 +4317,262475.3282584,0.0057045,0.11062 +4318,251052.3207423,0,0.093255 +4319,229526.2976898,0,0.059174 +4320,216206.3786225,0,0.030422 +4321,211304.8699429,0,0.016559 +4322,207211.0486634,0,0.010579 +4323,208660.2706271,0,0.0068085 +4324,212546.4008608,0,0 +4325,217863.2916319,0.0021638,0 +4326,247696.9565143,0.045775,0 +4327,277401.3914127,0.070719,0.0074172 +4328,288602.8618133,0.10286,0.013533 +4329,289581.3174066,0.12264,0.019143 +4330,290938.2322389,0.12888,0.018866 +4331,299998.17719,0.13031,0.014656 +4332,296670.5051015,0.12124,0.010666 +4333,296315.1226454,0.10385,0.0074758 +4334,293287.4487341,0.16846,0 +4335,289442.8567095,0.21836,0 +4336,282852.1275244,0.24573,0 +4337,280752.1402841,0.15844,0 +4338,279842.9150394,0.084882,0 +4339,277687.5435202,0.032318,0.0073421 +4340,266458.3809802,0.040787,0.010472 +4341,258316.8919869,0.0051352,0.013842 +4342,252358.4666522,0,0.01617 +4343,232083.2052308,0,0.018325 +4344,216114.0714911,0,0.022928 +4345,208198.7349699,0,0.029805 +4346,207003.3576177,0,0.037535 +4347,208927.9613083,0,0.044207 +4348,212740.2458369,0,0.062875 +4349,218389.4422812,0.0016939,0.075726 +4350,247932.3396995,0.05937,0.090263 +4351,274687.5617483,0.11876,0.090425 +4352,287033.6405788,0.17457,0.10157 +4353,289133.6278191,0.21255,0.12011 +4354,295004.3613789,0.22979,0.13226 +4355,302135.0872829,0.23194,0.1332 +4356,300164.3300266,0.21672,0.14044 +4357,296716.6586672,0.18781,0.19171 +4358,293370.5251524,0.2029,0.33117 +4359,288233.6332876,0.20599,0.46874 +4360,282173.6701083,0.19553,0.55985 +4361,281541.366258,0.17321,0.55075 +4362,280996.7541824,0.13805,0.42509 +4363,279469.0711571,0.092909,0.25562 +4364,268609.1371428,0.060322,0.15652 +4365,257970.740244,0.0064292,0.10701 +4366,253646.1511359,0,0.058947 +4367,233103.1990332,0,0.030001 +4368,219030.9768447,0,0.015121 +4369,214041.7763902,0,0.011069 +4370,214189.4678005,0,0.01281 +4371,214295.6210017,0,0.018061 +4372,216760.2214112,0,0.022486 +4373,224034.0233689,0.0018218,0.02505 +4374,253530.7672216,0.065304,0.018334 +4375,278882.9208724,0.1399,0.020282 +4376,291842.842127,0.20483,0.021782 +4377,292678.2216666,0.24892,0.020894 +4378,297427.4235793,0.26888,0.021125 +4379,300210.4835923,0.25669,0.02092 +4380,296619.7361792,0.22168,0.021543 +4381,289313.6267255,0.16979,0.021168 +4382,282819.8200284,0.1622,0.019854 +4383,279519.8400793,0.14692,0.017501 +4384,276353.7054709,0.1251,0.014647 +4385,275878.3237439,0.10708,0.013647 +4386,275892.1698136,0.082476,0.013708 +4387,272809.1116234,0.053495,0.011643 +4388,263070.7092562,0.047654,0.010693 +4389,255464.6016253,0.005535,0.013324 +4390,252395.3895048,0,0.020979 +4391,235507.7998073,0,0.031889 +4392,219266.3600299,0,0.042698 +4393,208780.2698979,0,0.05014 +4394,203315.6877165,0,0.052694 +4395,203758.7619474,0,0.051125 +4396,203200.3038022,0,0.035904 +4397,200094.1688292,0.0015701,0.023465 +4398,203560.3016148,0.063101,0.023576 +4399,219898.6638803,0.13538,0.029093 +4400,236333.9486337,0.20314,0.040778 +4401,245278.5096705,0.2528,0.048648 +4402,249700.0212667,0.28074,0.05868 +4403,251670.778523,0.34934,0.063346 +4404,245638.5074832,0.40637,0.053814 +4405,239066.2397244,0.44699,0.03575 +4406,230453.9843608,0.44595,0.022054 +4407,229646.2969607,0.42368,0.011984 +4408,223923.2548112,0.38088,0.0061316 +4409,225183.2471554,0.27143,0 +4410,229978.6026339,0.16972,0 +4411,228340.1510507,0.085864,0 +4412,217069.4503015,0.057779,0.024065 +4413,215444.8447881,0.0060504,0.074651 +4414,215661.766547,0,0.12182 +4415,202872.6134856,0,0.1455 +4416,192377.2926405,0,0.1774 +4417,186621.942995,0,0.21281 +4418,185103.4906828,0,0.24943 +4419,182218.8928252,0,0.29368 +4420,181005.0540467,0,0.35444 +4421,177252.7691536,0.0013383,0.36737 +4422,178305.070452,0.059161,0.39133 +4423,186455.7901584,0.1228,0.57792 +4424,198044.9505111,0.22415,0.83471 +4425,206154.1320084,0.33631,0.93954 +4426,213871.008197,0.44855,0.97576 +4427,221878.6518497,0.39212,0.98702 +4428,218615.5947532,0.29056,0.98847 +4429,211563.329911,0.15812,0.98582 +4430,204751.0636105,0.10725,0.96131 +4431,200809.5490978,0.056367,0.87218 +4432,199941.8620623,0.011383,0.94013 +4433,207077.2033228,0.057812,0.9537 +4434,214743.3105892,0.079701,0.8995 +4435,216760.2214112,0.073369,0.86394 +4436,215066.3855492,0.054833,0.82807 +4437,215269.4612384,0.006029,0.80093 +4438,219714.0496174,0,0.76708 +4439,206057.2095204,0,0.70407 +4440,195718.8107988,0,0.64883 +4441,195174.1987232,0,0.6212 +4442,192548.0608337,0,0.5951 +4443,193614.2082019,0,0.55585 +4444,197652.6452025,0,0.47609 +4445,205807.9802655,0.0019657,0.3761 +4446,239246.2386307,0.082508,0.28427 +4447,272342.9606096,0.20664,0.32715 +4448,285325.9586471,0.30306,0.31969 +4449,286904.4105948,0.3689,0.26763 +4450,292775.1441546,0.39987,0.25044 +4451,302135.0872829,0.38844,0.2325 +4452,301419.7070142,0.34529,0.19633 +4453,298941.260535,0.27749,0.16436 +4454,292267.4549316,0.30074,0.15144 +4455,289839.7773747,0.30674,0.14315 +4456,283332.1246079,0.29324,0.12402 +4457,280650.6024395,0.2396,0.083258 +4458,280281.3739138,0.17696,0.044295 +4459,275998.3230148,0.11071,0.022022 +4460,266176.8442293,0.066013,0.014071 +4461,258639.9669469,0.0065193,0.011853 +4462,250798.4761308,0,0.012147 +4463,232004.744169,0,0.021896 +4464,216709.4524889,0,0.048212 +4465,206689.5133708,0,0.088544 +4466,204811.0632459,0,0.14112 +4467,209121.8062843,0,0.20182 +4468,212901.7833169,0,0.27309 +4469,219446.3589362,0.0010752,0.28978 +4470,248149.2614584,0.057439,0.24728 +4471,273889.1050613,0.11977,0.28276 +4472,286512.1052862,0.1665,0.45952 +4473,290319.7744582,0.18757,0.60008 +4474,295304.3595561,0.18201,0.59584 +4475,301581.2444942,0.20586,0.52272 +4476,298428.9559555,0.2185,0.38736 +4477,293472.062997,0.21912,0.22201 +4478,285805.9557306,0.20963,0.12519 +4479,280465.9881766,0.1922,0.092384 +4480,274973.7138558,0.16562,0.07327 +4481,275924.4773096,0.13071,0.05751 +4482,275915.2465965,0.092805,0.040159 +4483,272273.7302611,0.055212,0.025854 +4484,263504.552774,0.048139,0.019661 +4485,254989.2198984,0.0052908,0.016721 +4486,250678.47686,0,0.016786 +4487,231787.8224101,0,0.021853 +4488,218781.7475898,0,0.030124 +4489,212486.4012254,0,0.044144 +4490,210211.0304353,0,0.063281 +4491,210367.9525588,0,0.081805 +4492,214387.9281331,0,0.090713 +4493,220429.4298861,0.0013228,0.080298 +4494,248453.8749922,0.073482,0.076846 +4495,274369.1021448,0.1826,0.071327 +4496,284679.808727,0.28047,0.064097 +4497,284735.1930059,0.35687,0.056837 +4498,289124.397106,0.4065,0.054133 +4499,295904.3559105,0.41659,0.051627 +4500,292179.7631568,0.39731,0.040434 +4501,287725.9440646,0.35295,0.024915 +4502,282247.5158135,0.35646,0.012789 +4503,276801.3950584,0.34188,0.0057353 +4504,272970.6491035,0.30981,0 +4505,273482.953683,0.22058,0 +4506,273926.0279139,0.13787,0 +4507,267178.3766054,0.06995,0 +4508,247226.190144,0.052472,0 +4509,238304.70589,0.0053792,0 +4510,237663.1713265,0,0 +4511,227947.8457421,0,0 +4512,214521.7734737,0,0 +4513,210003.3393896,0,0 +4514,206786.4358588,0,0 +4515,207067.9726097,0,0 +4516,210529.4900388,0,0 +4517,215569.4594156,0,0 +4518,247286.1897794,0.023398,0 +4519,273750.6443642,0.0026999,0 +4520,284356.733767,0.028538,0 +4521,287001.3330828,0.073471,0 +4522,292821.2977203,0.13341,0 +4523,297418.1928662,0.17356,0 +4524,291759.7657087,0.20937,0 +4525,286996.7177262,0.23676,0 +4526,283405.9703131,0.21874,0 +4527,279810.6075434,0.1909,0.0057542 +4528,277429.0835522,0.1562,0.0093119 +4529,278338.3087969,0.13397,0.013938 +4530,279025.9969261,0.10328,0.018084 +4531,278624.4609044,0.066921,0.023046 +4532,269619.9002321,0.05118,0.036785 +4533,260961.4913027,0.0051634,0.054273 +4534,255644.6005316,0,0.066593 +4535,237677.0173962,0,0.074136 +4536,224472.4822433,0,0.073322 +4537,218477.134056,0,0.07044 +4538,213160.2432849,0,0.068098 +4539,212680.2462014,0,0.065159 +4540,216469.4539472,0,0.044328 +4541,222118.6503915,0,0.023014 +4542,251509.2410429,0.067631,0.010102 +4543,277927.542062,0.16899,0.009335 +4544,288704.3996579,0.22999,0.12000 +4545,292078.2253122,0.24952,0.013176 +4546,298308.9566846,0.22613,0.014462 +4547,302832.0061253,0.25503,0.015809 +4548,298327.4181109,0.26821,0.017258 +4549,292212.0706528,0.26929,0.020064 +4550,286442.8749376,0.28654,0.029239 +4551,281933.6715666,0.28794,0.048763 +4552,279939.8375274,0.27142,0.070024 +4553,280299.83534,0.21269,0.0782 +4554,278873.6901592,0.14986,0.066564 +4555,275033.7134912,0.088712,0.054595 +4556,263873.7812997,0.056856,0.056085 +4557,253696.9200581,0.0051634,0.053516 +4558,249580.0219958,0,0.0561 +4559,232161.6662925,0,0.057814 +4560,218721.7479544,0,0.057724 +4561,209509.4962363,0,0.062313 +4562,204455.6807898,0,0.072319 +4563,200828.0105241,0,0.09167 +4564,202184.9253563,0,0.10092 +4565,199729.55566,0,0.078704 +4566,210127.954017,0.068732,0.053014 +4567,221587.8843857,0.17806,0.043216 +4568,232849.3544217,0.30201,0.057485 +4569,243796.9802109,0.4229,0.10384 +4570,250323.0944039,0.53047,0.18108 +4571,251490.7796167,0.58768,0.30102 +4572,243473.9052508,0.61566,0.46799 +4573,232789.3547863,0.61522,0.64262 +4574,225284.7850000,0.56209,0.74429 +4575,222109.4196783,0.4848,0.77251 +4576,222995.5681402,0.39097,0.72732 +4577,227227.8501168,0.29,0.64689 +4578,234783.1888255,0.19117,0.56432 +4579,235401.6466061,0.10403,0.52894 +4580,226669.3919716,0.060822,0.50292 +4581,222510.9557001,0.0051438,0.45895 +4582,221407.8854794,0,0.44812 +4583,210266.4147142,0,0.44595 +4584,195234.1983587,0,0.42251 +4585,189331.1573029,0,0.38293 +4586,187461.9378912,0,0.33234 +4587,186534.2512202,0,0.29786 +4588,186285.0219653,0,0.25713 +4589,181554.2814788,0,0.18022 +4590,184881.9535673,0.070541,0.1082 +4591,192146.5248119,0.18661,0.11427 +4592,199757.2477994,0.30542,0.11871 +4593,207561.8157629,0.41321,0.15139 +4594,214918.6941389,0.50225,0.18064 +4595,221384.8086965,0.53464,0.20933 +4596,215306.384091,0.53473,0.24132 +4597,208240.273179,0.50536,0.26991 +4598,204903.3703773,0.47767,0.27673 +4599,203380.3027085,0.42855,0.27032 +4600,204635.6796962,0.36259,0.2813 +4601,210903.3339211,0.25507,0.29329 +4602,220281.7384758,0.15641,0.28091 +4603,224601.7122273,0.076551,0.31213 +4604,224121.7151438,0.052734,0.40505 +4605,223927.8701678,0.0037391,0.40706 +4606,227869.3846804,0,0.33471 +4607,217244.8338513,0,0.27077 +4608,203712.6083817,0,0.21837 +4609,199397.2499868,0,0.16697 +4610,202300.3092706,0,0.11102 +4611,201552.621506,0,0.060988 +4612,204861.8321682,0,0.038119 +4613,213178.7047112,0,0.024457 +4614,247424.6504766,0.053091,0.015956 +4615,274835.2531586,0.12457,0.015241 +4616,291270.5379121,0.24553,0.017022 +4617,297012.0414878,0.38568,0.020775 +4618,302818.1600556,0.53151,0.020412 +4619,308910.4307308,0.5675,0.021701 +4620,305430.4518754,0.5703,0.024037 +4621,302744.3143504,0.54286,0.028211 +4622,298105.8809954,0.52686,0.034049 +4623,290910.5400994,0.48682,0.039694 +4624,288561.3236042,0.42506,0.045585 +4625,288016.7115287,0.29551,0.65000 +4626,285229.0361591,0.17762,0.11606 +4627,281342.9059253,0.083868,0.24073 +4628,270782.9700883,0.053942,0.34275 +4629,264275.3173215,0.0035378,0.31393 +4630,256373.82687,0,0.20141 +4631,237875.4777288,0,0.12728 +4632,225377.0921314,0,0.091723 +4633,222797.1078076,0,0.073312 +4634,215177.154107,0,0.054416 +4635,213667.9325079,0,0.036061 +4636,215800.2272442,0,0.029065 +4637,221537.1154634,0,0.024137 +4638,251320.0114235,0.050514,0.023098 +4639,275462.9416524,0.11941,0.01964 +4640,287887.4815447,0.21967,0.017589 +4641,292608.991318,0.32744,0.017799 +4642,300062.792182,0.43211,0.013836 +4643,304738.1483896,0.50581,0.011782 +4644,299725.8711522,0.55868,0.013333 +4645,296384.352994,0.58706,0.016931 +4646,291459.7675315,0.57906,0.021422 +4647,288879.7832077,0.54458,0.025839 +4648,284112.1198686,0.48479,0.030601 +4649,281287.5216465,0.34947,0.034341 +4650,278725.9987489,0.22072,0.0368 +4651,276921.3943292,0.11253,0.045844 +4652,267704.5272546,0.061783,0.065211 +4653,259572.2689745,0.003669,0.075105 +4654,253216.9229746,0,0.057616 +4655,234017.0396345,0,0.0343 +4656,220840.196621,0,0.018826 +4657,212454.0937294,0,0.010791 +4658,209804.879057,0,0.0064879 +4659,211535.6377715,0,0 +4660,216058.6872122,0,0 +4661,222603.2628315,0,0 +4662,252053.8531185,0.049253,0 +4663,276736.7800663,0.11935,0 +4664,290310.543745,0.22069,0.0065386 +4665,293984.3675765,0.32985,0.0104 +4666,300436.6360643,0.43637,0.01667 +4667,305965.8332378,0.48896,0.023869 +4668,303455.0792625,0.51685,0.029015 +4669,300053.5614689,0.51951,0.031061 +4670,294413.5957377,0.51605,0.029759 +4671,290176.6984045,0.48817,0.026788 +4672,281905.9794272,0.43632,0.022715 +4673,280784.4477801,0.31028,0.016164 +4674,285824.4171569,0.19257,0.0087328 +4675,284485.963751,0.095563,0.0064323 +4676,275130.6359792,0.055629,0.012566 +4677,268189.1396947,0.0031035,0.031233 +4678,259539.9614785,0,0.060507 +4679,241927.7607992,0,0.096834 +4680,229604.7587515,0,0.14588 +4681,221514.0386805,0,0.20359 +4682,217470.9863233,0,0.25372 +4683,216977.1431701,0,0.30045 +4684,219686.3574779,0,0.35371 +4685,227555.5404335,0,0.37693 +4686,253299.9993929,0.054783,0.30491 +4687,279907.5300314,0.14601,0.30453 +4688,290481.3119382,0.2231,0.42961 +4689,294658.209636,0.27674,0.61346 +4690,301447.3991537,0.30322,0.72445 +4691,308601.2018405,0.34607,0.78587 +4692,303113.5428762,0.37152,0.81313 +4693,297561.2689199,0.381000,0.79803 +4694,290744.3872628,0.38951,0.66682 +4695,286350.5678061,0.37681,0.47134 +4696,281467.5205528,0.34298,0.33437 +4697,276644.4729349,0.25335,0.26296 +4698,272398.3448885,0.16569,0.20433 +4699,273002.9565995,0.08882,0.12245 +4700,266075.3063847,0.053582,0.088986 +4701,256659.9789775,0.0029222,0.073723 +4702,251467.7028338,0,0.054924 +4703,231501.6703027,0,0.044104 +4704,215832.5347402,0,0.04059 +4705,210118.7233039,0,0.039724 +4706,205466.4438791,0,0.038758 +4707,208240.273179,0,0.03348 +4708,213252.5504164,0,0.025384 +4709,219298.6675259,0,0.017746 +4710,245306.20181,0.050691,0.014777 +4711,267506.066922,0.13301,0.025853 +4712,277036.7782435,0.22363,0.043545 +4713,276081.3994331,0.30605,0.083467 +4714,281605.98125,0.37333,0.15456 +4715,296056.6626774,0.40133,0.25278 +4716,289922.853793,0.40309,0.32944 +4717,282150.5933255,0.38254,0.35938 +4718,275176.789545,0.36875,0.36563 +4719,271650.6571238,0.33768,0.35554 +4720,270035.2823236,0.29108,0.34536 +4721,271913.7324484,0.22555,0.33779 +4722,270746.0472357,0.15663,0.29522 +4723,268406.0614536,0.090293,0.21489 +4724,257269.206045,0.053568,0.11995 +4725,247904.6475601,0.0027382,0.090687 +4726,244729.2822385,0,0.054626 +4727,226544.7773442,0,0.033184 +4728,211194.1013852,0,0.026205 +4729,202591.0767347,0,0.028175 +4730,195658.8111633,0,0.03737 +4731,197740.3369774,0,0.049311 +4732,202411.0778284,0,0.052415 +4733,201700.3129163,0,0.044301 +4734,205064.9078574,0.061107,0.039047 +4735,212758.7072631,0.18119,0.046958 +4736,224310.9447632,0.25989,0.04613 +4737,235563.1840862,0.29466,0.048128 +4738,245167.7411128,0.28369,0.052152 +4739,250410.7861788,0.32372,0.058723 +4740,245080.0493379,0.3474,0.065051 +4741,236860.0992829,0.35406,0.070027 +4742,228289.3821284,0.3377,0.070731 +4743,222654.0317538,0.30593,0.066389 +4744,223858.6398192,0.26071,0.057697 +4745,225746.3206572,0.20054,0.043508 +4746,224583.250801,0.1379,0.026953 +4747,222261.7264452,0.078482,0.015968 +4748,211581.7913372,0.049011,0.011748 +4749,205097.2153534,0.0013989,0.0081451 +4750,205651.058142,0,0.0060873 +4751,193161.9032578,0,0 +4752,180345.0580569,0,0 +4753,174091.2499016,0,0.0068011 +4754,171105.1141995,0,0.010985 +4755,173431.2539118,0,0.018725 +4756,173089.7175255,0,0.030349 +4757,168654.3598596,0,0.036918 +4758,171354.3434544,0.058061,0.020334 +4759,178706.6064738,0.17895,0.018141 +4760,184914.2610633,0.28232,0.016922 +4761,189995.7686493,0.36053,0.015157 +4762,197001.8799258,0.40876,0.014964 +4763,205540.2895843,0.429000,0.014705 +4764,201898.7732489,0.41991,0.012945 +4765,191675.7584415,0.38626,0.011083 +4766,186012.7159275,0.37797,0.010405 +4767,182329.6613829,0.35142,0.011595 +4768,182209.6621121,0.30825,0.014638 +4769,190064.9989979,0.22385,0.016423 +4770,197343.4163122,0.14284,0.015163 +4771,201677.2361334,0.073694,0.014466 +4772,201644.9286374,0.046825,0.01835 +4773,202729.5374319,0.0011232,0.030006 +4774,208184.8889001,0,0.064982 +4775,194569.5870123,0,0.13442 +4776,184138.8811592,0,0.22951 +4777,177003.5398987,0,0.35525 +4778,177681.9973148,0,0.49758 +4779,181568.1275485,0,0.59326 +4780,186792.7111882,0,0.60627 +4781,195086.5069484,0,0.5929 +4782,226729.3916071,0.04765,0.58881 +4783,254859.9899143,0.13907,0.65788 +4784,269047.5960171,0.21252,0.79919 +4785,273533.7226053,0.25857,0.82824 +4786,283613.6613588,0.27264,0.83227 +4787,290882.84796,0.32102,0.84669 +4788,286682.8734793,0.35542,0.88476 +4789,280996.7541824,0.37277,0.91023 +4790,275758.324473,0.36892,0.91337 +4791,271904.5017353,0.34676,0.89585 +4792,270990.661134,0.30732,0.86838 +4793,272610.6512908,0.23469,0.82911 +4794,270607.5865385,0.1598,0.75651 +4795,268378.3693142,0.08959,0.47864 +4796,258372.2762657,0.051342,0.28262 +4797,252090.775971,0.0011807,0.29349 +4798,246206.1963415,0,0.36668 +4799,225497.0914023,0,0.52254 +4800,212094.0959168,0,0.7197 +4801,206712.5901536,0,0.84209 +4802,205803.3649089,0,0.8548 +4803,205438.7517397,0,0.83031 +4804,209532.5730192,0,0.78035 +4805,216944.8356741,0,0.70782 +4806,243076.9845856,0.037428,0.65021 +4807,266167.6135161,0.097507,0.73337 +4808,278670.6144701,0.14695,0.87298 +4809,283244.4328331,0.17321,0.93718 +4810,288450.5550465,0.17389,0.96815 +4811,293375.140509,0.20406,0.97884 +4812,290185.9291176,0.2248,0.98176 +4813,286636.7199136,0.23382,0.98711 +4814,282145.9779689,0.19239,0.99013 +4815,276829.0871978,0.1442,0.99001 +4816,273404.4926212,0.095178,0.98757 +4817,276358.3208274,0.11652,0.98034 +4818,276436.7818892,0.11358,0.96464 +4819,274747.5613837,0.086253,0.94649 +4820,264695.3147696,0.019806,0.94279 +4821,257947.6634611,0,0.92932 +4822,251375.3957024,0,0.9103 +4823,230717.0596854,0,0.88092 +4824,215214.0769595,0,0.87305 +4825,209643.3415769,0,0.87078 +4826,209158.7291369,0,0.88682 +4827,208226.4271093,0,0.93714 +4828,213031.0133009,0,0.96085 +4829,219543.2814242,0,0.96943 +4830,246206.1963415,0.049862,0.97271 +4831,270639.8940345,0.16057,0.97618 +4832,282418.2840067,0.20629,0.97859 +4833,285704.417886,0.18723,0.98237 +4834,292124.3788779,0.10385,0.98824 +4835,297616.6531988,0.12113,0.99275 +4836,294455.1339468,0.13211,0.99548 +4837,292470.5306208,0.136000,0.99613 +4838,288085.9418773,0.10788,0.99744 +4839,286498.2592164,0.076227,0.99614 +4840,283964.4284583,0.045632,0.9916 +4841,285141.3443842,0.060684,0.97949 +4842,284642.8858744,0.061125,0.96171 +4843,279487.5325833,0.046695,0.95242 +4844,266139.9213767,0.0093575,0.93176 +4845,260841.4920318,0,0.89398 +4846,251998.4688396,0,0.86161 +4847,232046.2823782,0,0.81599 +4848,219132.5146893,0,0.75396 +4849,212384.8633808,0,0.68895 +4850,213206.3968506,0,0.62714 +4851,214083.3145994,0,0.56961 +4852,218694.0558149,0,0.5311 +4853,225834.0124321,0,0.49600 +4854,250826.1682703,0.022606,0.45306 +4855,275384.4805907,0.04024,0.41636 +4856,288229.017931,0.051967,0.41731 +4857,291856.6881967,0.044211,0.51908 +4858,298382.8023897,0.018077,0.5594 +4859,303842.7692146,0.070173,0.58257 +4860,301415.0916576,0.13146,0.64565 +4861,298728.9541327,0.19125,0.69041 +4862,294708.9785583,0.17309,0.7107 +4863,289881.3155838,0.14714,0.70626 +4864,285939.8010712,0.1164,0.69809 +4865,284947.4994082,0.098897,0.65297 +4866,281998.2865586,0.074696,0.54431 +4867,276261.3983394,0.046068,0.37712 +4868,265359.926116,0.0090561,0.29855 +4869,257453.8203079,0,0.27176 +4870,246478.5023793,0,0.25457 +4871,226314.0095156,0,0.25875 +4872,213524.8564541,0,0.28334 +4873,206569.5140999,0,0.31535 +4874,206791.0512153,0,0.35182 +4875,207787.9682349,0,0.36721 +4876,211835.6359487,0,0.35825 +4877,219317.1289522,0,0.34562 +4878,244641.5904636,0.034851,0.33072 +4879,270284.5115785,0.10573,0.36149 +4880,285981.3392804,0.16678,0.43153 +4881,291256.6918423,0.20483,0.44662 +4882,295068.9763709,0.21766,0.43707 +4883,299675.1022299,0.22437,0.42737 +4884,294815.1317595,0.21367,0.42774 +4885,289152.0892454,0.18826,0.43933 +4886,281578.2891105,0.184000,0.45634 +4887,276681.3957875,0.17066,0.46355 +4888,274327.5639357,0.14868,0.43100 +4889,272292.1916874,0.11075,0.36315 +4890,268442.9843062,0.072675,0.2531 +4891,262313.7907784,0.038201,0.12866 +4892,249086.1788426,0.0069369,0.077752 +4893,241092.3812596,0,0.061924 +4894,231487.8242329,0,0.058436 +4895,212961.7829523,0,0.05523 +4896,198437.2558198,0,0.051243 +4897,192386.5233537,0,0.042364 +4898,188505.0084765,0,0.031677 +4899,188528.0852593,0,0.023267 +4900,191357.2988381,0,0.015504 +4901,194564.9716557,0,0.010113 +4902,203435.6869874,0.037187,0.0089552 +4903,215689.4586865,0.1219,0.011487 +4904,230458.5997174,0.22116,0.014724 +4905,245984.6592261,0.31717,0.016389 +4906,256706.1325432,0.40162,0.015334 +4907,261030.7216513,0.43801,0.013608 +4908,254241.5321337,0.44705,0.010711 +4909,245610.8153437,0.43148,0.0075654 +4910,236250.8722154,0.36793,0.0060088 +4911,231266.2871175,0.28965,0.0060491 +4912,229110.9155983,0.20632,0.0071469 +4913,228824.7634908,0.14203,0.0085396 +4914,229512.4516201,0.084006,0.0089507 +4915,225289.4003565,0.038163,0.0093217 +4916,215915.6111585,0.0067647,0.019132 +4917,211097.1788972,0,0.050835 +4918,209514.1115929,0,0.088052 +4919,195857.2714959,0,0.11537 +4920,188029.6267495,0,0.14105 +4921,180626.5948078,0,0.15944 +4922,174866.6298058,0,0.16519 +4923,176477.3892494,0,0.17927 +4924,177206.6155878,0,0.23015 +4925,174289.7102342,0,0.22964 +4926,175060.4747818,0.03388,0.20982 +4927,178365.0700875,0.11245,0.27915 +4928,187521.9375266,0.15865,0.26922 +4929,194966.5076775,0.1643,0.24865 +4930,204081.8369075,0.12903,0.19108 +4931,212403.3248071,0.15779,0.18211 +4932,206149.5166518,0.18008,0.21692 +4933,200315.7059446,0.19399,0.23595 +4934,197477.2616527,0.14518,0.21702 +4935,195746.5029382,0.091845,0.16717 +4936,195368.0436993,0.042719,0.10879 +4937,199120.3285924,0.066124,0.049933 +4938,205401.8288871,0.070392,0.02257 +4939,207464.8932749,0.054931,0.012872 +4940,205387.9828174,0.010577,0.012628 +4941,206283.3619924,0,0.017798 +4942,207432.5857789,0,0.018593 +4943,192404.9847799,0,0.012496 +4944,183105.0412871,0,0.0066122 +4945,181037.3615427,0,0 +4946,181009.6694033,0,0.0082843 +4947,182818.8891796,0,0.02593 +4948,188094.2417416,0,0.048533 +4949,197966.4894494,0,0.057176 +4950,226364.7784379,0.026404,0.057508 +4951,257181.5142701,0.083839,0.063349 +4952,273944.4893402,0.13365,0.045842 +4953,282279.8233095,0.16337,0.031701 +4954,289950.5459324,0.16919,0.024901 +4955,297676.6528342,0.17816,0.028526 +4956,296638.1976055,0.1737,0.051361 +4957,291547.4593064,0.15805,0.099532 +4958,286290.5681707,0.18485,0.15263 +4959,282164.4393952,0.19886,0.16349 +4960,275481.4030787,0.19704,0.1223 +4961,273847.5668522,0.15229,0.079476 +4962,273206.0322886,0.1043,0.045616 +4963,266310.6895698,0.057724,0.022645 +4964,252921.540154,0.011059,0.014177 +4965,245984.6592261,0,0.0094984 +4966,234930.8802358,0,0.0067106 +4967,214429.4663423,0,0 +4968,201095.7012053,0,0 +4969,195548.0426056,0,0.0061639 +4970,194883.4312592,0,0.0082834 +4971,194389.588106,0,0.011578 +4972,198700.3311444,0,0.019986 +4973,207229.5100897,0,0.033689 +4974,233426.2739932,0.023436,0.034482 +4975,260638.4163427,0.071415,0.027244 +4976,275061.4056306,0.10593,0.024649 +4977,282930.5885862,0.11551,0.022496 +4978,289996.6994981,0.099172,0.016915 +4979,295968.9709025,0.13386,0.01124 +4980,291699.7660733,0.16541,0.0079484 +4981,287633.6369332,0.19027,0.0070464 +4982,281665.9808854,0.1964,0.0076145 +4983,275947.5540925,0.19165,0.010494 +4984,268899.9046068,0.17523,0.012568 +4985,267639.9122626,0.10621,0.0094781 +4986,267090.6848305,0.050627,0.0088264 +4987,265599.9246577,0.014227,0.021413 +4988,256263.0583123,0.00090233,0.043445 +4989,251546.1638955,0,0.052823 +4990,240990.843415,0,0.05001 +4991,218887.900791,0,0.057409 +4992,204247.9897441,0,0.074593 +4993,199692.6328074,0,0.087571 +4994,199581.8642496,0,0.086705 +4995,199988.015628,0,0.070793 +4996,205337.2138951,0,0.049474 +4997,212477.1705122,0,0.029873 +4998,239047.7782981,0.020797,0.012448 +4999,265683.001076,0.062868,0.0072896 +5000,283447.5085223,0.14889,0.0069732 +5001,291233.6150595,0.25835,0.0086397 +5002,298765.8769852,0.37931,0.012445 +5003,306505.8299567,0.43089,0.020859 +5004,303228.9267905,0.45922,0.029266 +5005,298216.6495531,0.4638,0.032253 +5006,290832.0790377,0.41888,0.029164 +5007,282362.8997278,0.35503,0.021003 +5008,274964.4831426,0.27878,0.015423 +5009,272610.6512908,0.20971,0.010145 +5010,271867.5788827,0.13935,0.0062914 +5011,267699.9118981,0.074138,0 +5012,256267.6736688,0.014095,0 +5013,247156.9597954,0,0 +5014,236089.3347354,0,0.010671 +5015,215527.9212064,0,0.019607 +5016,204852.601455,0,0.030043 +5017,198631.1007958,0,0.040408 +5018,198774.1768495,0,0.050492 +5019,198658.7929352,0,0.057325 +5020,202651.0763701,0,0.079632 +5021,210317.1836365,0,0.11187 +5022,235747.798349,0.0282,0.10454 +5023,261284.5662628,0.119000,0.11357 +5024,279898.2993183,0.21815,0.18735 +5025,288339.7864887,0.30961,0.30379 +5026,293033.6041226,0.38616,0.30566 +5027,296592.0440397,0.3721,0.21205 +5028,291205.92292,0.32235,0.12666 +5029,286369.0292324,0.24392,0.058835 +5030,280729.0635013,0.23503,0.023932 +5031,277447.5449785,0.21388,0.011159 +5032,272462.9598805,0.18241,0.0073582 +5033,271789.117821,0.15374,0 +5034,269126.0570789,0.11471,0 +5035,266236.8438647,0.068877,0.0072571 +5036,256484.5954277,0.012589,0.015205 +5037,251689.2399493,0,0.025322 +5038,239527.7753816,0,0.036157 +5039,219386.3593008,0,0.044658 +5040,202784.9217107,0,0.041831 +5041,198224.9494174,0,0.03033 +5042,196992.6492127,0,0.014834 +5043,196895.7267247,0,0.0062417 +5044,199508.0185445,0,0 +5045,206227.9777135,0,0.022873 +5046,230966.2889403,0.011594,0.094994 +5047,256789.2089615,0.019966,0.20516 +5048,272259.8841914,0.065833,0.25804 +5049,276672.1650743,0.13457,0.29167 +5050,281527.5201882,0.21892,0.44834 +5051,287250.5623377,0.25896,0.62238 +5052,282745.9743233,0.28631,0.69997 +5053,273819.8747127,0.29861,0.72156 +5054,265766.0774943,0.2986,0.70506 +5055,260204.5728249,0.28242,0.64363 +5056,256410.7497226,0.25067,0.53677 +5057,255123.0652389,0.19555,0.37681 +5058,253023.0779986,0.13504,0.18239 +5059,248943.1027888,0.074763,0.083653 +5060,238844.702609,0.01321,0.066618 +5061,235373.9544667,0,0.064778 +5062,226129.3952527,0,0.083247 +5063,208701.8088362,0,0.12927 +5064,194260.358122,0,0.20195 +5065,186294.2526784,0,0.27437 +5066,182671.1977693,0,0.34448 +5067,180778.9015747,0,0.42169 +5068,182629.6595601,0,0.4982 +5069,183898.8826175,0,0.59173 +5070,190401.9200276,0.019999,0.6593 +5071,200809.5490978,0.078003,0.76199 +5072,213363.3189741,0.12058,0.91425 +5073,225524.7835417,0.1356,0.96383 +5074,232360.1266251,0.12118,0.95995 +5075,234437.0370826,0.15728,0.94185 +5076,231100.1342809,0.18834,0.94621 +5077,223817.10161,0.21101,0.93734 +5078,218717.1325978,0.18429,0.93166 +5079,213307.9346952,0.14881,0.95253 +5080,212094.0959168,0.11012,0.96300 +5081,216589.453218,0.096519,0.94932 +5082,220674.0437844,0.074055,0.9241 +5083,219667.8960517,0.044758,0.90914 +5084,210003.3393896,0.0066185,0.88868 +5085,207677.1996772,0,0.85947 +5086,203555.6862583,0,0.82098 +5087,192709.5983137,0,0.79066 +5088,175628.1636402,0,0.74707 +5089,169978.9671959,0,0.68475 +5090,166471.296201,0,0.62619 +5091,167329.7525234,0,0.56145 +5092,167800.5188938,0,0.50106 +5093,167726.6731886,0,0.4432 +5094,169388.2015546,0.022944,0.35474 +5095,172674.335434,0.10149,0.39047 +5096,181305.0522239,0.17234,0.42468 +5097,190637.3032128,0.21989,0.34971 +5098,200740.3187493,0.24072,0.27971 +5099,209509.4962363,0.2842,0.25782 +5100,203961.8376366,0.31316,0.26678 +5101,196692.6510355,0.32628,0.2821 +5102,193438.8246521,0.34136,0.28301 +5103,189866.5386653,0.33718,0.27733 +5104,190609.6110734,0.31143,0.29064 +5105,196374.191432,0.25082,0.28096 +5106,204151.0672561,0.17903,0.22036 +5107,206675.667301,0.10218,0.11669 +5108,205286.4449728,0.018292,0.063474 +5109,209241.8055552,0,0.04221 +5110,206500.2837513,0,0.031538 +5111,190383.4586013,0,0.025591 +5112,181609.6657577,0,0.022177 +5113,179472.7556648,0,0.021577 +5114,179431.2174556,0,0.02277 +5115,179052.7582167,0,0.024816 +5116,184125.0350895,0,0.023236 +5117,195631.1190239,0,0.018258 +5118,223180.1824031,0.027623,0.012656 +5119,253064.6162078,0.14678,0.012828 +5120,270275.2808653,0.22853,0.01199 +5121,278716.7680358,0.26002,0.012353 +5122,287956.7118932,0.23905,0.013597 +5123,296218.2001574,0.27355,0.01597 +5124,293398.2172918,0.29259,0.017231 +5125,288044.4036681,0.32824,0.021385 +5126,279759.8386211,0.34151,0.030557 +5127,275684.4787679,0.3346,0.036827 +5128,268775.2899794,0.30741,0.0321 +5129,266453.7656236,0.24604,0.02457 +5130,265816.8464166,0.17409,0.020883 +5131,262373.7904138,0.098004,0.021052 +5132,250886.1679057,0.016342,0.023782 +5133,244706.2054556,0,0.01963 +5134,230458.5997174,0,0.014239 +5135,210451.0289771,0,0.0092976 +5136,197897.2591008,0,0 +5137,195815.7332868,0,0 +5138,192441.9076325,0,0 +5139,191943.4491227,0,0 +5140,197537.2612882,0,0 +5141,204723.371471,0,0.01083 +5142,229027.83918,0.029469,0.017512 +5143,254352.3006914,0.16001,0.018786 +5144,270413.7415625,0.27587,0.022111 +5145,278758.3062449,0.35819,0.02819 +5146,281504.4434054,0.4014,0.0367 +5147,289059.782114,0.43999,0.049243 +5148,287236.716268,0.45053,0.074387 +5149,280239.8357046,0.43532,0.12697 +5150,273593.7222407,0.32503,0.19519 +5151,269582.9773795,0.20336,0.22625 +5152,264321.4708872,0.089378,0.2421 +5153,263403.0149294,0.12336,0.2543 +5154,263121.4781785,0.12414,0.27433 +5155,258856.8887058,0.090883,0.23242 +5156,250013.8655136,0.014523,0.20736 +5157,247415.4197634,0,0.1864 +5158,235161.6480644,0,0.15712 +5159,213538.7025238,0,0.15295 +5160,198741.8693535,0,0.17501 +5161,195732.6568685,0,0.24108 +5162,192501.907268,0,0.34755 +5163,192123.448029,0,0.41619 +5164,196992.6492127,0,0.39143 +5165,205549.5202974,0,0.33475 +5166,229678.6044567,0.027736,0.22416 +5167,256064.5979797,0.15718,0.21184 +5168,270026.0516104,0.27536,0.40761 +5169,272984.4951732,0.36209,0.54243 +5170,278342.9241534,0.41056,0.62946 +5171,285210.5747328,0.43507,0.6831 +5172,283572.1231497,0.42736,0.69078 +5173,282630.590409,0.39182,0.69331 +5174,277115.2393053,0.38166,0.69738 +5175,273695.2600853,0.35187,0.67229 +5176,267635.2969061,0.30389,0.61331 +5177,264164.5487638,0.2642,0.53392 +5178,264067.6262758,0.20192,0.38934 +5179,261773.7940594,0.12195,0.16875 +5180,253023.0779986,0.019923,0.093955 +5181,250526.1700931,0,0.065372 +5182,235535.4919467,0,0.051727 +5183,214000.2381811,0,0.047327 +5184,199443.4035525,0,0.055688 +5185,196711.1124618,0,0.086574 +5186,193775.7456819,0,0.15125 +5187,193424.9785824,0,0.25172 +5188,196992.6492127,0,0.30443 +5189,205475.6745923,0,0.3055 +5190,231192.4414123,0.016155,0.23501 +5191,256706.1325432,0.084251,0.26718 +5192,272370.6527491,0.16726,0.48355 +5193,279459.8404439,0.24632,0.68535 +5194,286064.4156987,0.31462,0.79626 +5195,291408.9986092,0.29153,0.85931 +5196,287070.5634314,0.23516,0.87622 +5197,282270.5925964,0.15418,0.87133 +5198,274936.7910032,0.13721,0.82302 +5199,270667.586174,0.11361,0.71595 +5200,267150.684466,0.08629,0.52603 +5201,268101.4479198,0.096533,0.24671 +5202,267344.529442,0.086897,0.11863 +5203,263730.705246,0.058113,0.10512 +5204,255432.2941293,0.0072931,0.18959 +5205,250766.1686348,0,0.46472 +5206,235203.1862735,0,0.62202 +5207,212564.8622871,0,0.54767 +5208,200126.4763252,0,0.47423 +5209,196891.1113681,0,0.45538 +5210,193803.4378213,0,0.45292 +5211,193637.2849847,0,0.44113 +5212,197841.8748219,0,0.39397 +5213,206814.1279982,0,0.35022 +5214,230537.0607791,0.023183,0.28945 +5215,255081.5270298,0.15121,0.24503 +5216,270455.2797716,0.24463,0.35713 +5217,275864.4776742,0.28257,0.43785 +5218,283835.1984743,0.26244,0.42996 +5219,290499.7733645,0.25592,0.37197 +5220,287513.6376623,0.2226,0.33672 +5221,278361.3855797,0.16921,0.29373 +5222,271581.4267752,0.16629,0.25308 +5223,265729.1546418,0.15396,0.21981 +5224,263352.2460071,0.13299,0.19578 +5225,264639.9304907,0.14163,0.17178 +5226,262590.7121727,0.12406,0.11477 +5227,257767.6645548,0.081752,0.08795 +5228,248006.1854047,0.011028,0.085854 +5229,241438.5330025,0,0.076919 +5230,229457.0673412,0,0.079844 +5231,210931.0260606,0,0.094387 +5232,198423.40975,0,0.1071 +5233,190904.993894,0,0.12563 +5234,184932.7224896,0,0.1449 +5235,185703.4870372,0,0.15993 +5236,186737.3269093,0,0.13848 +5237,190074.229711,0,0.10332 +5238,194320.3577574,0.021887,0.070959 +5239,204538.7572081,0.14909,0.049173 +5240,217623.2930902,0.27025,0.066465 +5241,225944.7809898,0.35972,0.079739 +5242,231381.6710318,0.41011,0.11219 +5243,234935.4955923,0.4557,0.17703 +5244,230213.985819,0.47193,0.23897 +5245,219912.50995,0.45969,0.29927 +5246,213769.4703524,0.45224,0.38528 +5247,209911.0322581,0.42105,0.47244 +5248,208221.8117527,0.36713,0.53197 +5249,213464.8568187,0.24523,0.46974 +5250,218334.0580023,0.13676,0.3472 +5251,217738.6770045,0.054494,0.31117 +5252,211609.4834767,0.0061208,0.32881 +5253,212241.7873271,0,0.3424 +5254,204215.6822481,0,0.30974 +5255,188772.6991577,0,0.27486 +5256,176463.5431797,0,0.26117 +5257,171506.6502212,0,0.27494 +5258,169840.5064987,0,0.26676 +5259,170643.5785422,0,0.21586 +5260,170689.732108,0,0.15796 +5261,170971.2688589,0,0.11383 +5262,169277.4329969,0.015289,0.082081 +5263,174382.0173657,0.099587,0.082361 +5264,186423.4826624,0.19511,0.095658 +5265,197717.2601945,0.2777,0.10333 +5266,209177.1905632,0.33938,0.13978 +5267,218269.4430103,0.36487,0.1761 +5268,213109.4743626,0.36286,0.21404 +5269,207506.431484,0.33737,0.27664 +5270,202614.1535176,0.353000,0.36038 +5271,199374.1732039,0.34704,0.4281 +5272,198644.9468655,0.31797,0.48702 +5273,202028.0032329,0.21287,0.51133 +5274,207732.5839561,0.11942,0.42874 +5275,208554.1174259,0.04785,0.35406 +5276,208277.1960316,0.0045077,0.34619 +5277,211780.2516698,0,0.33989 +5278,206786.4358588,0,0.32038 +5279,191352.6834815,0,0.31948 +5280,181549.6661222,0,0.35276 +5281,179195.8342704,0,0.39013 +5282,180128.136298,0,0.39863 +5283,181235.8218753,0,0.40975 +5284,186423.4826624,0,0.40731 +5285,199014.1753913,0,0.38128 +5286,229646.2969607,0.012681,0.32973 +5287,256964.5925112,0.092999,0.32594 +5288,272139.8849205,0.16254,0.46208 +5289,277904.4652791,0.19929,0.51857 +5290,283258.2789028,0.19941,0.53896 +5291,290310.543745,0.2427,0.59007 +5292,289069.0128271,0.27449,0.65806 +5293,287093.6402143,0.29186,0.71285 +5294,278993.6894301,0.28754,0.7512 +5295,270178.3583773,0.26709,0.76883 +5296,264339.9323135,0.23206,0.77928 +5297,264473.7776541,0.16342,0.75073 +5298,265041.4665125,0.098323,0.6554 +5299,263619.9366883,0.043562,0.42142 +5300,256447.6725752,0.0036532,0.32601 +5301,255395.3712767,0,0.2847 +5302,239707.774288,0,0.29195 +5303,216635.6067838,0,0.33154 +5304,200029.5538371,0,0.37597 +5305,195764.9643645,0,0.42305 +5306,195834.1947131,0,0.45372 +5307,196697.266392,0,0.46649 +5308,201201.8544065,0,0.46748 +5309,210649.4893097,0,0.4557 +5310,236084.7193788,0.010359,0.4185 +5311,257938.432748,0.076572,0.38384 +5312,268401.446097,0.14685,0.48833 +5313,273210.6476452,0.19947,0.5824 +5314,280272.1432006,0.22972,0.61169 +5315,286262.8760313,0.27117,0.61831 +5316,285976.7239238,0.29757,0.6464 +5317,284790.5772847,0.307000,0.68375 +5318,277646.0053111,0.31829,0.71293 +5319,272739.8812749,0.30989,0.73965 +5320,268521.4453679,0.28166,0.76264 +5321,269813.7452081,0.19202,0.76134 +5322,269993.7441144,0.11037,0.67379 +5323,266366.0738487,0.045486,0.5439 +5324,258681.5051561,0.0036741,0.5409 +5325,259309.1936499,0,0.53333 +5326,243372.3674062,0,0.52211 +5327,220770.9662724,0,0.4962 +5328,206181.8241478,0,0.47653 +5329,201589.5443585,0,0.48225 +5330,199549.5567536,0,0.50821 +5331,200509.5509206,0,0.55600 +5332,204658.756479,0,0.58959 +5333,215347.9223001,0,0.59323 +5334,241913.9147294,0.010331,0.55914 +5335,267012.2237688,0.084843,0.56331 +5336,280073.682868,0.15893,0.65035 +5337,286775.1806108,0.20751,0.64772 +5338,293347.4483695,0.22574,0.6447 +5339,299061.2598058,0.24096,0.6316 +5340,295992.0476854,0.23647,0.63196 +5341,289941.3152193,0.21541,0.63983 +5342,281038.2923916,0.24514,0.65487 +5343,274346.025362,0.2578,0.62268 +5344,269993.7441144,0.2495,0.56884 +5345,271429.1200084,0.16479,0.46872 +5346,271253.7364586,0.09024,0.31006 +5347,268826.0589017,0.034123,0.15193 +5348,263361.4767202,0.0017478,0.11833 +5349,259507.6539825,0,0.11084 +5350,242980.0620976,0,0.10027 +5351,219386.3593008,0,0.087803 +5352,204944.9085865,0,0.073944 +5353,201081.8551356,0,0.056191 +5354,198617.2547261,0,0.038995 +5355,199424.9421262,0,0.026234 +5356,205009.5235785,0,0.017972 +5357,214775.6180852,0,0.015084 +5358,242767.7556953,0.0055436,0.014392 +5359,268115.2939896,0.044533,0.014231 +5360,284476.7330378,0.09192,0.014574 +5361,288275.1714967,0.13051,0.016438 +5362,294704.3632017,0.15635,0.0093673 +5363,301105.8627673,0.17449,0.0064883 +5364,296222.815514,0.17988,0.007493 +5365,290181.313761,0.1741,0.0094975 +5366,284213.6577132,0.16473,0.016283 +5367,278347.53951,0.14605,0.032873 +5368,273672.1833024,0.12014,0.060884 +5369,274784.4842363,0.086536,0.10695 +5370,273746.0290076,0.053064,0.1422 +5371,270326.0497876,0.023257,0.16568 +5372,262055.3308103,0.00027438,0.19111 +5373,255538.4473304,0,0.20393 +5374,238701.6265552,0,0.18772 +5375,217272.5259907,0,0.17396 +5376,201631.0825677,0,0.17879 +5377,196157.2696731,0,0.20399 +5378,194800.3548409,0,0.24124 +5379,194943.4308946,0,0.26592 +5380,199628.0178154,0,0.24489 +5381,211978.7120025,0,0.2098 +5382,235683.183357,0.011452,0.15214 +5383,260726.1081175,0.12938,0.10702 +5384,276266.013696,0.25035,0.17272 +5385,280276.7585572,0.3378,0.20021 +5386,284389.041263,0.38485,0.20015 +5387,289078.2435403,0.42438,0.18201 +5388,284965.9608345,0.43443,0.17026 +5389,271733.7335421,0.41727,0.17029 +5390,264778.3911879,0.36379,0.17348 +5391,260689.185265,0.29435,0.15906 +5392,258981.5033333,0.21634,0.14942 +5393,263287.6310151,0.1417,0.1058 +5394,264367.624453,0.076485,0.053895 +5395,259521.5000522,0.027765,0.045626 +5396,252510.7734191,0.00062978,0.05726 +5397,248444.644279,0,0.086717 +5398,235133.9559249,0,0.11823 +5399,216026.3797162,0,0.15612 +5400,200071.0920463,0,0.19178 +5401,192104.9866028,0,0.21891 +5402,186225.0223298,0,0.24725 +5403,187092.7093654,0,0.32428 +5404,189672.6936892,0,0.40756 +5405,192123.448029,0,0.43267 +5406,196471.11392,0.0043753,0.38623 +5407,204395.6811544,0.042701,0.25988 +5408,219930.9713763,0.099465,0.11217 +5409,233181.6600949,0.15639,0.031045 +5410,240312.3859989,0.20646,0.015585 +5411,243123.1381513,0.2672,0.023491 +5412,237547.7874122,0.31942,0.046201 +5413,227961.6918118,0.35648,0.085818 +5414,219289.4368127,0.3329,0.11587 +5415,215061.7701926,0.29176,0.11827 +5416,216990.9892398,0.23742,0.091146 +5417,223877.1012455,0.15184,0.057364 +5418,228058.6142998,0.078841,0.042202 +5419,225612.4753166,0.026548,0.063035 +5420,221066.349093,0.00019786,0.1194 +5421,217918.6759108,0,0.17578 +5422,203611.0705371,0,0.19515 +5423,188071.1649587,0,0.18225 +5424,177732.7662371,0,0.18097 +5425,171640.4955618,0,0.2065 +5426,169535.8929649,0,0.23531 +5427,169365.1247718,0,0.22585 +5428,169720.5072278,0,0.20773 +5429,172162.0308545,0,0.19172 +5430,173805.0977942,0.0047802,0.16745 +5431,181332.7443633,0.070253,0.15809 +5432,191841.9112781,0.14448,0.18802 +5433,203214.1498719,0.20009,0.21989 +5434,214775.6180852,0.23181,0.22854 +5435,224481.7129564,0.25335,0.21351 +5436,221329.4244176,0.25518,0.19728 +5437,214600.2345354,0.23935,0.19677 +5438,209163.3444934,0.22113,0.2071 +5439,207847.9678704,0.19138,0.23005 +5440,207584.8925458,0.15333,0.2483 +5441,211895.6355842,0.15185,0.22447 +5442,216104.840778,0.12392,0.16034 +5443,216617.1453575,0.071695,0.1411 +5444,216372.5314591,0.00463,0.13497 +5445,215098.6930452,0,0.11291 +5446,207418.7397092,0,0.071051 +5447,189123.4662571,0,0.043893 +5448,183077.3491476,0,0.031363 +5449,179311.2181847,0,0.022911 +5450,178725.0679001,0,0.018003 +5451,180778.9015747,0,0.01541 +5452,185685.0256109,0,0.016124 +5453,198764.9461364,0,0.014442 +5454,232387.8187645,0.0071493,0.0090691 +5455,261529.1801611,0.11843,0.0062165 +5456,278287.5398746,0.2326,0.0060911 +5457,286756.7191845,0.30623,0.0068184 +5458,295765.8952133,0.33331,0.0072666 +5459,306330.446407,0.39123,0.0077289 +5460,302725.8529241,0.42668,0.0084526 +5461,298793.5691247,0.43805,0.0088095 +5462,292258.2242185,0.41312,0.0086923 +5463,284329.0416275,0.36669,0.0081856 +5464,277595.2363888,0.30273,0.0075022 +5465,276418.3204629,0.21422,0.0074272 +5466,274913.7142203,0.12815,0.0082184 +5467,272532.1902291,0.053977,0.011411 +5468,266162.9981595,0.0021818,0.015176 +5469,259438.4236339,0,0.016463 +5470,237727.7863185,0,0.01671 +5471,213211.0122072,0,0.017217 +5472,204432.604007,0,0.017342 +5473,201368.0072431,0,0.016739 +5474,197468.0309396,0,0.015919 +5475,198404.9483237,0,0.015223 +5476,202618.7688741,0,0.014029 +5477,215897.1497322,0,0.014448 +5478,242850.8321136,0.0062716,0.014771 +5479,268493.7532285,0.11439,0.015414 +5480,287933.6351104,0.26124,0.030562 +5481,293735.1383216,0.40146,0.035129 +5482,299744.3325785,0.51961,0.030096 +5483,307830.4372929,0.59269,0.024285 +5484,302268.9326235,0.62976,0.021623 +5485,299139.7208676,0.63141,0.02232 +5486,296375.1222809,0.60957,0.02735 +5487,292867.451286,0.55605,0.036436 +5488,289092.08961,0.47351,0.050007 +5489,286682.8734793,0.32993,0.064667 +5490,284504.4251773,0.19261,0.077445 +5491,282745.9743233,0.07784,0.14609 +5492,277955.2342014,0.0039962,0.23965 +5493,268276.8314696,0,0.29641 +5494,248795.4113785,0,0.33400 +5495,226775.5451728,0,0.34366 +5496,217854.0609188,0,0.32466 +5497,212463.3244425,0,0.30576 +5498,207021.819044,0,0.28598 +5499,207206.4333068,0,0.26363 +5500,209924.8783278,0,0.22881 +5501,220974.0419616,0,0.20014 +5502,246556.963441,0.0016016,0.1376 +5503,271309.1207375,0.11041,0.072612 +5504,289022.8592614,0.25593,0.062435 +5505,297067.4257667,0.3929,0.10485 +5506,304267.3820192,0.50903,0.13694 +5507,310696.5737242,0.59623,0.19545 +5508,306201.216423,0.65047,0.26858 +5509,302536.6233047,0.67015,0.33135 +5510,294704.3632017,0.62663,0.37451 +5511,286378.2599456,0.55175,0.39104 +5512,280862.9088418,0.45141,0.35205 +5513,279870.6071788,0.31688,0.27305 +5514,276713.7032835,0.18659,0.33521 +5515,272107.5774245,0.075477,0.49679 +5516,268678.3674914,0.0034013,0.60896 +5517,264653.7765604,0,0.63758 +5518,246653.885929,0,0.63727 +5519,225349.399992,0,0.61955 +5520,211563.329911,0,0.57687 +5521,206971.0501217,0,0.52103 +5522,202203.3867826,0,0.45832 +5523,203163.3809496,0,0.40625 +5524,207474.123988,0,0.38371 +5525,218292.5197932,0,0.36845 +5526,247267.7283531,0.0011365,0.28781 +5527,269181.4413577,0.10503,0.17323 +5528,281250.5987939,0.24812,0.14728 +5529,284610.5783784,0.38313,0.18058 +5530,287398.253748,0.49694,0.1811 +5531,295341.2824087,0.50547,0.15655 +5532,291247.4611292,0.46782,0.13173 +5533,286885.9491685,0.39067,0.10986 +5534,278892.1515855,0.29187,0.091148 +5535,270515.2794071,0.18299,0.084204 +5536,267215.299458,0.082151,0.076467 +5537,268793.7514057,0.052849,0.052997 +5538,268779.9053359,0.027199,0.039322 +5539,268724.5210571,0.0080148,0.042279 +5540,266236.8438647,0,0.050392 +5541,264081.4723455,0,0.053974 +5542,245269.2789574,0,0.046125 +5543,223720.179122,0,0.032655 +5544,209431.0351746,0,0.021628 +5545,205447.9824529,0,0.01504 +5546,201889.5425357,0,0.011083 +5547,201026.4708567,0,0.0085965 +5548,205032.6003614,0,0.0086759 +5549,216469.4539472,0,0.011322 +5550,241143.1501819,0,0.013494 +5551,265258.3882714,0.060619,0.01465 +5552,279039.8429958,0.14294,0.013691 +5553,281961.363706,0.21459,0.011038 +5554,285612.1107546,0.26778,0.0097215 +5555,291404.3832526,0.28305,0.0088602 +5556,289096.7049666,0.27269,0.0072812 +5557,282958.2807256,0.24123,0 +5558,267353.7601552,0.21664,0 +5559,263712.2438197,0.1811,0 +5560,262230.7143601,0.13883,0 +5561,264829.1601102,0.08884,0 +5562,265701.4625023,0.045527,0 +5563,263666.090254,0.013885,0.0063218 +5564,258118.4316543,0,0.0086617 +5565,255972.2908482,0,0.010718 +5566,240801.6137956,0,0.011374 +5567,221094.0412325,0,0.010925 +5568,202124.9257209,0,0.011331 +5569,193300.3639549,0,0.011881 +5570,192621.9065388,0,0.010965 +5571,196060.3471851,0,0.01025 +5572,195617.2729542,0,0.009798 +5573,197195.7249018,0,0.012179 +5574,199097.2518096,0,0.015968 +5575,210451.0289771,0.074723,0.022045 +5576,224029.4080123,0.16043,0.030036 +5577,235401.6466061,0.2125,0.034922 +5578,240053.9260309,0.22662,0.034886 +5579,244336.9769298,0.28821,0.034718 +5580,241433.9176459,0.33718,0.03833 +5581,232646.2787326,0.36858,0.049274 +5582,221592.4997422,0.353000,0.069845 +5583,218647.9022492,0.31762,0.098042 +5584,217664.8312993,0.2651,0.12866 +5585,220877.1194736,0.19284,0.14873 +5586,223738.6405483,0.11771,0.13887 +5587,223147.8749071,0.048038,0.11114 +5588,219201.7450379,0,0.10306 +5589,219483.2817888,0,0.068888 +5590,208881.8077425,0,0.046556 +5591,194163.4356339,0,0.037915 +5592,183852.7290517,0,0.035187 +5593,177215.846301,0,0.036464 +5594,176985.0784724,0,0.040542 +5595,177774.3044462,0,0.04512 +5596,176726.6185043,0,0.053581 +5597,177405.0759204,0,0.058013 +5598,177451.2294862,0,0.048961 +5599,181060.4383256,0.082382,0.0458 +5600,191578.8359535,0.18212,0.042262 +5601,198788.0229192,0.2482,0.037129 +5602,207566.4311195,0.27409,0.031957 +5603,218467.9033429,0.28549,0.027528 +5604,219243.283247,0.26951,0.025632 +5605,212251.0180402,0.23146,0.025963 +5606,207926.4289321,0.23689,0.028135 +5607,207511.0468406,0.22678,0.033123 +5608,210178.7229393,0.20064,0.03586 +5609,215800.2272442,0.15331,0.037244 +5610,219792.5106791,0.098224,0.037875 +5611,220974.0419616,0.041446,0.036583 +5612,222630.954971,0,0.037244 +5613,222081.7275389,0,0.031923 +5614,213164.8586415,0,0.027487 +5615,197131.1099098,0,0.021858 +5616,197038.8027784,0,0.015701 +5617,198012.6430151,0,0.011434 +5618,188241.9331519,0,0.0079469 +5619,186663.4812042,0,0 +5620,193498.8242875,0,0 +5621,209158.7291369,0,0 +5622,244567.7447584,0,0.0067708 +5623,271309.1207375,0.072453,0.011733 +5624,289590.5481198,0.17617,0.024026 +5625,300981.2481399,0.26289,0.036315 +5626,309053.5067846,0.32338,0.038857 +5627,317601.1471562,0.33111,0.041134 +5628,312861.1759566,0.30576,0.053112 +5629,307544.2851855,0.25442,0.08299 +5630,304096.6138261,0.21912,0.1343 +5631,302735.0836373,0.17351,0.18487 +5632,295179.7449287,0.12363,0.25508 +5633,292862.8359294,0.096572,0.32437 +5634,289978.2380718,0.062607,0.28458 +5635,287089.0248577,0.025799,0.19217 +5636,283405.9703131,0,0.15288 +5637,275232.1738238,0,0.12077 +5638,255099.9884561,0,0.085775 +5639,231450.9013804,0,0.042584 +5640,221546.3461765,0,0.017638 +5641,215615.6129813,0,0.0075751 +5642,210031.031529,0,0.0060852 +5643,210667.950736,0,0.009181 +5644,216686.375706,0,0.016235 +5645,227135.5429854,0,0.023806 +5646,253816.919329,0,0.028971 +5647,274613.7160431,0.090204,0.030024 +5648,290107.4680559,0.23652,0.056154 +5649,297990.4970811,0.37735,0.092845 +5650,304428.9194993,0.49668,0.14821 +5651,311218.1090169,0.53534,0.22431 +5652,307622.7462472,0.53067,0.27586 +5653,300427.4053512,0.4886,0.32188 +5654,294588.9792874,0.42327,0.38649 +5655,288995.167122,0.3378,0.4007 +5656,282316.7461621,0.24303,0.39588 +5657,281107.5227402,0.15403,0.32079 +5658,280105.990364,0.077261,0.27402 +5659,276524.473664,0.022203,0.32051 +5660,272393.7295319,0,0.40032 +5661,266019.9221058,0,0.47097 +5662,248767.7192391,0,0.51895 +5663,234626.266702,0,0.56838 +5664,223466.3345105,0,0.62457 +5665,218500.2108389,0,0.71479 +5666,213031.0133009,0,0.79955 +5667,211346.4081521,0,0.86088 +5668,216990.9892398,0,0.88256 +5669,230287.8315242,0,0.89331 +5670,256950.7464415,0,0.89894 +5671,278578.3073386,0.025297,0.89185 +5672,291072.0775794,0.068195,0.86807 +5673,294884.362108,0.10417,0.81476 +5674,300196.6375226,0.12812,0.7233 +5675,305495.0668674,0.1917,0.63853 +5676,301050.4784884,0.25285,0.61154 +5677,296439.7372729,0.30364,0.53277 +5678,290467.4658685,0.2932,0.42056 +5679,282349.0536581,0.26533,0.39382 +5680,276538.3197337,0.2224,0.35732 +5681,277747.5431556,0.13686,0.18717 +5682,277133.7007315,0.065476,0.12779 +5683,275144.4820489,0.016751,0.15313 +5684,272762.9580577,0,0.18200 +5685,264736.8529787,0,0.21161 +5686,244272.3619378,0,0.28683 +5687,223826.3323232,0,0.38417 +5688,210520.2593257,0,0.4725 +5689,205678.7502815,0,0.55235 +5690,201538.7754362,0,0.58541 +5691,202540.3078124,0,0.58555 +5692,207007.9729742,0,0.56454 +5693,218850.9779384,0,0.55942 +5694,248629.2585419,0,0.56024 +5695,273099.8790875,0.057584,0.56543 +5696,285229.0361591,0.15185,0.68391 +5697,288884.3985642,0.23229,0.83505 +5698,294995.1306658,0.28973,0.91102 +5699,300902.7870781,0.35627,0.93505 +5700,298124.3424217,0.40369,0.93882 +5701,297773.5753222,0.42778,0.90922 +5702,293236.6798118,0.42375,0.87445 +5703,290379.7740936,0.39357,0.82615 +5704,284430.5794721,0.33829,0.76762 +5705,285109.0368882,0.21391,0.56557 +5706,283336.7399645,0.10651,0.28245 +5707,282469.052929,0.029699,0.19917 +5708,278998.3047867,0,0.17229 +5709,266989.146986,0,0.17112 +5710,247586.1879566,0,0.20255 +5711,225003.2482491,0,0.2697 +5712,213201.7814941,0,0.3301 +5713,208955.6534477,0,0.38432 +5714,206107.9784427,0,0.42016 +5715,206343.3616279,0,0.43228 +5716,210737.1810845,0,0.41157 +5717,225289.4003565,0,0.38395 +5718,256101.5208322,0,0.32299 +5719,281435.2130568,0.072971,0.22915 +5720,297833.5749577,0.19292,0.28463 +5721,303644.308882,0.29283,0.36545 +5722,310576.5744534,0.36168,0.36714 +5723,316853.4593915,0.35823,0.31902 +5724,308536.5868485,0.3137,0.26424 +5725,297515.1153542,0.23866,0.20765 +5726,282565.975417,0.25109,0.14709 +5727,275089.0977701,0.24579,0.093316 +5728,271470.6582175,0.22106,0.068634 +5729,275232.1738238,0.13926,0.046219 +5730,276999.855391,0.068784,0.026929 +5731,273893.7204179,0.018202,0.022105 +5732,268558.3682205,0,0.02158 +5733,257149.2067741,0,0.0174 +5734,241096.9966162,0,0.012329 +5735,222261.7264452,0,0.01208 +5736,208812.577394,0,0.01368 +5737,203878.7612183,0,0.015699 +5738,200394.1670063,0,0.017962 +5739,197237.263111,0,0.022141 +5740,196060.3471851,0,0.028706 +5741,198312.6411923,0,0.033217 +5742,203878.7612183,0,0.039887 +5743,213612.548229,0.075007,0.040592 +5744,228630.9185148,0.22201,0.042591 +5745,239781.6199931,0.36362,0.064365 +5746,248472.3364185,0.48379,0.073656 +5747,253060.0008512,0.52388,0.079195 +5748,245638.5074832,0.52126,0.086072 +5749,235032.4180804,0.48103,0.090327 +5750,227933.9996724,0.43877,0.085791 +5751,223927.8701678,0.37221,0.074491 +5752,223314.0277437,0.28912,0.079742 +5753,225774.0127966,0.18216,0.075327 +5754,229803.2190841,0.089936,0.054758 +5755,230186.2936796,0.023599,0.048165 +5756,227610.9247123,0,0.047391 +5757,221638.653308,0,0.056874 +5758,211443.3306401,0,0.072109 +5759,196461.8832069,0,0.10429 +5760,182158.8931898,0,0.1589 +5761,176112.7760802,0,0.22472 +5762,172272.7994122,0,0.27919 +5763,170038.9668313,0,0.32485 +5764,175586.625431,0,0.35365 +5765,175909.7003911,0,0.36998 +5766,178434.300436,0,0.33831 +5767,184318.8800655,0.071625,0.21015 +5768,192764.9825926,0.21748,0.16002 +5769,202004.92645,0.35716,0.26191 +5770,211004.8717657,0.47592,0.31827 +5771,223110.9520545,0.55407,0.28004 +5772,223586.3337814,0.5963,0.21999 +5773,217969.4448331,0.60247,0.15826 +5774,212306.4023191,0.56873,0.11582 +5775,208291.0421013,0.50333,0.088533 +5776,212246.4026836,0.41118,0.075736 +5777,214992.5398441,0.26908,0.052741 +5778,222695.569963,0.1394,0.041544 +5779,225206.3239382,0.039445,0.048868 +5780,228095.5371524,0,0.055009 +5781,221814.0368577,0,0.047541 +5782,213635.6250119,0,0.029191 +5783,197504.9537922,0,0.024755 +5784,187231.1700626,0,0.018471 +5785,183395.8087511,0,0.01155 +5786,183954.2668963,0,0.0086388 +5787,187365.0154032,0,0.008115 +5788,188638.8538171,0,0.0077722 +5789,203227.9959416,0,0.0092818 +5790,241775.4540323,0,0.013217 +5791,268572.2142902,0.068348,0.013132 +5792,285178.2672368,0.21421,0.01229 +5793,291155.1539977,0.35355,0.01643 +5794,296130.5083825,0.47144,0.014056 +5795,301165.8624028,0.54151,0.010518 +5796,297833.5749577,0.57433,0.0075679 +5797,293296.6794472,0.57114,0.0061995 +5798,287892.0969012,0.52852,0.0060343 +5799,282644.4364787,0.45708,0.0065784 +5800,276889.0868332,0.3635,0.010883 +5801,277355.237847,0.24075,0.023192 +5802,277719.8510162,0.12638,0.066229 +5803,277632.1592413,0.035523,0.17974 +5804,274835.2531586,0,0.29143 +5805,263107.6321088,0,0.34544 +5806,242800.0631913,0,0.36487 +5807,220577.1212964,0,0.38228 +5808,207566.4311195,0,0.38565 +5809,204843.3707419,0,0.37067 +5810,204012.6065589,0,0.34616 +5811,206094.132373,0,0.31396 +5812,211992.5580722,0,0.25296 +5813,224107.8690741,0,0.20472 +5814,252492.3119928,0,0.21147 +5815,275366.0191644,0.063679,0.21783 +5816,286207.4917524,0.20959,0.25717 +5817,289724.3934604,0.34871,0.34758 +5818,296753.5815198,0.46558,0.4383 +5819,300035.1000426,0.54225,0.48185 +5820,293296.6794472,0.58387,0.45466 +5821,287389.0230349,0.59012,0.41935 +5822,280152.1439297,0.55754,0.37734 +5823,274018.3350453,0.49346,0.32612 +5824,268115.2939896,0.40252,0.2432 +5825,269564.5159532,0.2672,0.24457 +5826,270690.6629568,0.13992,0.34279 +5827,271212.1982495,0.037507,0.46306 +5828,274239.8721608,0,0.51441 +5829,263744.5513157,0,0.49763 +5830,241761.6079626,0,0.49872 +5831,219626.3578425,0,0.50025 +5832,213561.7793067,0,0.49613 +5833,211627.944903,0,0.50492 +5834,210054.1083119,0,0.52644 +5835,210487.9518296,0,0.53979 +5836,215975.6107939,0,0.48846 +5837,227837.0771844,0,0.44034 +5838,257610.7424313,0,0.39700 +5839,279736.7618382,0.060435,0.25949 +5840,289549.0099106,0.20576,0.16812 +5841,290476.6965816,0.34493,0.18063 +5842,292696.6830928,0.46216,0.21966 +5843,297778.1906788,0.46685,0.23509 +5844,294861.2853252,0.42194,0.25476 +5845,294002.8290028,0.33775,0.29239 +5846,289775.1623827,0.2895,0.30375 +5847,282999.8189348,0.22676,0.28127 +5848,274452.1785631,0.1584,0.27208 +5849,273916.7972008,0.099404,0.22454 +5850,275712.1709073,0.047583,0.15368 +5851,278624.4609044,0.0097814,0.10776 +5852,281052.1384613,0,0.067236 +5853,268184.5243381,0,0.037451 +5854,247512.3422515,0,0.018927 +5855,226494.0084219,0,0.010173 +5856,213280.2425558,0,0.0060006 +5857,209181.8059197,0,0 +5858,208000.2746373,0,0 +5859,208217.1963961,0,0 +5860,214747.9259457,0,0 +5861,226443.2394996,0,0.0061932 +5862,255510.755191,0,0.010342 +5863,277544.4674665,0.07037,0.012016 +5864,286710.5656188,0.22309,0.012898 +5865,287527.483732,0.34407,0.012661 +5866,290975.1550914,0.42052,0.011891 +5867,297099.7332627,0.47928,0.013364 +5868,294455.1339468,0.50141,0.015035 +5869,294205.9046919,0.48931,0.015897 +5870,288155.1722258,0.45093,0.016202 +5871,283881.35204,0.38709,0.015202 +5872,277595.2363888,0.30403,0.015566 +5873,277484.467831,0.19327,0.020566 +5874,279432.1483045,0.094363,0.03713 +5875,281421.3669871,0.020516,0.079228 +5876,283419.8163828,0,0.11988 +5877,269596.8234492,0,0.12334 +5878,249026.1792071,0,0.11733 +5879,228220.1517799,0,0.11826 +5880,216658.6835666,0,0.12573 +5881,212149.4801956,0,0.1297 +5882,211180.2553155,0,0.12032 +5883,212477.1705122,0,0.10855 +5884,219566.3582071,0,0.11637 +5885,231118.5957072,0,0.13181 +5886,260435.3406535,0,0.1335 +5887,282067.5169072,0.067717,0.09112 +5888,293070.5269752,0.22878,0.1027 +5889,293818.2147399,0.36829,0.10336 +5890,297228.9632467,0.46966,0.084981 +5891,299185.8744333,0.56201,0.073534 +5892,293310.5255169,0.61917,0.080953 +5893,287721.3287081,0.63904,0.10593 +5894,281965.9790626,0.61691,0.14694 +5895,276224.4754868,0.55813,0.19261 +5896,270270.6655088,0.46498,0.23507 +5897,272287.5763308,0.30707,0.23435 +5898,272227.5766953,0.15721,0.26636 +5899,273330.6469161,0.036905,0.36587 +5900,276173.7065645,0,0.39462 +5901,262286.0986389,0,0.3806 +5902,244581.5908281,0,0.38641 +5903,223660.1794866,0,0.38855 +5904,207469.5086315,0,0.37133 +5905,199558.7874668,0,0.33042 +5906,198677.2543615,0,0.28714 +5907,196346.4992926,0,0.26551 +5908,197712.6448379,0,0.24049 +5909,200901.8562293,0,0.2231 +5910,206227.9777135,0,0.20153 +5911,217014.0660227,0.062476,0.11047 +5912,234409.3449431,0.23628,0.071147 +5913,243810.8262806,0.4024,0.096717 +5914,248952.333502,0.5419,0.11696 +5915,252321.5437996,0.62119,0.14263 +5916,244013.9019698,0.6558,0.16046 +5917,236541.6396795,0.64827,0.16505 +5918,228926.3013354,0.59649,0.15188 +5919,223401.7195185,0.51146,0.13415 +5920,221541.73082,0.40101,0.11848 +5921,225930.9349201,0.27053,0.10455 +5922,232106.2820136,0.14186,0.19181 +5923,235590.8762256,0.032258,0.34274 +5924,237727.7863185,0,0.42015 +5925,227661.6936346,0,0.42814 +5926,217590.9855942,0,0.42011 +5927,199401.8653433,0,0.40578 +5928,185435.796356,0,0.39256 +5929,180520.4416067,0,0.38558 +5930,179205.0649836,0,0.37697 +5931,178572.7611332,0,0.36041 +5932,180035.8291666,0,0.34669 +5933,181268.1293713,0,0.34401 +5934,177261.9998667,0,0.32826 +5935,182611.1981338,0.058439,0.22178 +5936,191251.1456369,0.22881,0.16591 +5937,201054.1629962,0.39365,0.25438 +5938,206869.5122771,0.53206,0.40788 +5939,211203.3320983,0.62729,0.41204 +5940,207418.7397092,0.6807,0.3695 +5941,199748.0170862,0.69192,0.29907 +5942,194283.4349048,0.64587,0.21377 +5943,190720.3796311,0.56297,0.14113 +5944,192991.1350646,0.4498,0.087758 +5945,203264.9187942,0.29796,0.064172 +5946,217747.9077176,0.15153,0.081744 +5947,226484.7777087,0.032066,0.13174 +5948,234326.2685248,0,0.18456 +5949,229770.9115881,0,0.21118 +5950,220715.5819935,0,0.20300 +5951,202849.5367027,0,0.19358 +5952,190300.382183,0,0.1893 +5953,187775.7821381,0,0.18712 +5954,187226.554706,0,0.18471 +5955,188878.8523588,0,0.18001 +5956,196623.4206869,0,0.17412 +5957,213395.6264701,0,0.17617 +5958,251033.859316,0,0.17575 +5959,274964.4831426,0.055131,0.10042 +5960,286313.6449536,0.2239,0.044535 +5961,286276.722101,0.38883,0.040992 +5962,287550.5605149,0.52648,0.03805 +5963,291155.1539977,0.61782,0.029449 +5964,286609.0277742,0.66762,0.021452 +5965,280825.9859893,0.67697,0.018704 +5966,276838.3179109,0.60274,0.018231 +5967,273141.4172966,0.49562,0.017231 +5968,268456.8303759,0.36748,0.014438 +5969,270667.586174,0.23478,0.0093788 +5970,275241.404537,0.11303,0.0068752 +5971,280327.5274795,0.02088,0 +5972,283756.7374126,0,0 +5973,269061.4420868,0,0 +5974,248740.0270997,0,0.0091078 +5975,228570.9188793,0,0.015519 +5976,211526.4070584,0,0.023499 +5977,206947.9733388,0,0.030967 +5978,202969.5359736,0,0.03705 +5979,203892.607288,0,0.04183 +5980,210040.2622421,0,0.042771 +5981,225538.6296114,0,0.042879 +5982,257799.9720508,0,0.046642 +5983,281172.1377322,0.049507,0.04011 +5984,295964.3555459,0.21617,0.02753 +5985,303122.7735893,0.37926,0.054264 +5986,306671.9827933,0.5167,0.075664 +5987,318348.8349209,0.60782,0.092999 +5988,312505.7935005,0.65692,0.11318 +5989,309921.1938201,0.66467,0.12525 +5990,306916.5966917,0.55548,0.12002 +5991,304433.5348558,0.41806,0.092095 +5992,296804.3504421,0.27184,0.060397 +5993,296402.8144203,0.17772,0.034928 +5994,294076.6747079,0.087438,0.021059 +5995,295378.2052613,0.015138,0.014603 +5996,295424.358827,0,0.010742 +5997,278522.9230598,0,0.011254 +5998,258104.5855846,0,0.018469 +5999,236860.0992829,0,0.03448 +6000,224504.7897393,0,0.060449 +6001,218352.5194286,0,0.095597 +6002,212527.9394345,0,0.13396 +6003,213640.2403684,0,0.1701 +6004,220637.1209318,0,0.19319 +6005,233606.2728996,0,0.23247 +6006,266781.4559402,0,0.26322 +6007,292664.3755968,0.046706,0.20128 +6008,305527.3743634,0.2112,0.16335 +6009,310595.0358797,0.37445,0.2518 +6010,318556.5259666,0.51187,0.32197 +6011,325742.6361495,0.59813,0.27061 +6012,323379.5735845,0.64141,0.19319 +6013,322451.8869135,0.64304,0.12269 +6014,315621.1591867,0.58641,0.07098 +6015,310493.4980351,0.49656,0.038543 +6016,304835.0708776,0.38174,0.02119 +6017,300718.1728153,0.24438,0.0084926 +6018,297270.5014559,0.1165,0 +6019,297201.2711073,0.019068,0 +6020,293393.6019352,0,0.0076164 +6021,277987.5416974,0,0.022801 +6022,257093.8224953,0,0.047932 +6023,234044.7317739,0,0.075454 +6024,221846.3443537,0,0.096934 +6025,217549.447385,0,0.10439 +6026,212943.321526,0,0.10283 +6027,212897.1679603,0,0.099213 +6028,218449.4419166,0,0.087823 +6029,232950.8922663,0,0.081943 +6030,266536.8420419,0,0.073089 +6031,291616.689655,0.044165,0.037506 +6032,304461.2269953,0.19177,0.019539 +6033,305938.1410984,0.31695,0.018605 +6034,309298.1206829,0.39966,0.018075 +6035,318718.0634467,0.44568,0.017987 +6036,313396.557319,0.45231,0.019358 +6037,306044.2942995,0.42392,0.021269 +6038,303635.0781689,0.36836,0.023886 +6039,296785.8890158,0.29282,0.02706 +6040,290425.9276593,0.20702,0.027747 +6041,289041.3206877,0.13362,0.027665 +6042,289719.7781038,0.063436,0.034717 +6043,292336.6852802,0.0085925,0.040757 +6044,292479.761334,0,0.036475 +6045,276076.7840765,0,0.02864 +6046,255552.2934002,0,0.02627 +6047,234653.9588414,0,0.027468 +6048,219464.8203625,0,0.029341 +6049,214946.3862783,0,0.0306 +6050,210377.1832719,0,0.027411 +6051,210755.6425108,0,0.028729 +6052,215472.5369276,0,0.029972 +6053,228063.2296564,0,0.029324 +6054,263223.0160231,0,0.027401 +6055,287689.0212121,0.040031,0.026647 +6056,298101.2656388,0.18024,0.024524 +6057,299924.3314848,0.2857,0.021074 +6058,305291.9911783,0.3363,0.014113 +6059,307539.6698289,0.4319,0.0092677 +6060,300921.2485044,0.50469,0.0073494 +6061,293435.1401444,0.54765,0 +6062,285649.0336072,0.50719,0 +6063,281393.6748476,0.43585,0.0075066 +6064,277646.0053111,0.34,0.013338 +6065,278887.536229,0.2155,0.019117 +6066,279049.073709,0.099602,0.033835 +6067,281139.8302362,0.013436,0.071038 +6068,283032.1264308,0,0.12312 +6069,265346.0800463,0,0.16169 +6070,248721.5656734,0,0.16556 +6071,227578.6172163,0,0.15292 +6072,206703.3594405,0,0.12556 +6073,197195.7249018,0,0.099619 +6074,194551.125586,0,0.077838 +6075,195151.1219404,0,0.052738 +6076,196092.6546811,0,0.046571 +6077,198769.5614929,0,0.053251 +6078,205572.5970803,0,0.061428 +6079,216160.2250568,0.024767,0.068805 +6080,233961.6553556,0.11247,0.062795 +6081,245130.8182602,0.16373,0.051817 +6082,247604.6493829,0.16466,0.044765 +6083,246326.1956124,0.19424,0.040574 +6084,240695.4605944,0.20815,0.044066 +6085,232157.0509359,0.20639,0.054259 +6086,224384.7904684,0.17373,0.06935 +6087,218260.2122972,0.13225,0.092446 +6088,218463.2879863,0.088127,0.11413 +6089,224850.9414822,0.091963,0.12012 +6090,231690.8999221,0.062693,0.094691 +6091,235720.1062096,0.01076,0.11823 +6092,240160.079232,0,0.12764 +6093,225755.5513703,0,0.12253 +6094,214526.3888303,0,0.12896 +6095,198206.4879911,0,0.13913 +6096,189146.54304,0,0.16149 +6097,179071.219643,0,0.18626 +6098,175406.6265247,0,0.20232 +6099,177548.1519742,0,0.21354 +6100,177511.2291216,0,0.2362 +6101,179698.9081368,0,0.27051 +6102,182851.1966756,0,0.3062 +6103,186215.7916167,0.035675,0.33994 +6104,196258.8075177,0.16461,0.37547 +6105,203569.532328,0.24696,0.40798 +6106,212195.6337613,0.26227,0.42848 +6107,221140.1947982,0.32911,0.43671 +6108,219617.1271294,0.37567,0.42366 +6109,211831.0205921,0.3979,0.44500 +6110,205004.9082219,0.39586,0.5163 +6111,200744.9341058,0.36519,0.57445 +6112,202627.9995873,0.306000,0.59046 +6113,210631.0278834,0.21325,0.5636 +6114,219570.9735636,0.10813,0.40741 +6115,230449.3690042,0.014599,0.27623 +6116,239610.8517999,0,0.18156 +6117,229877.0647893,0,0.10882 +6118,222414.0332121,0,0.07209 +6119,206043.3634507,0,0.056277 +6120,194371.1266797,0,0.046982 +6121,191477.2981089,0,0.040856 +6122,189072.6973349,0,0.035258 +6123,189815.769743,0,0.027483 +6124,197223.4170413,0,0.019348 +6125,211904.8662973,0,0.014085 +6126,257647.6652839,0,0.010087 +6127,286493.6438599,0.032202,0.0059495 +6128,299785.8707877,0.1881,0 +6129,303048.9278842,0.33927,0 +6130,307913.5137112,0.45581,0 +6131,313544.2487293,0.53996,0 +6132,308624.2786234,0.58322,0 +6133,305375.0675966,0.58641,0 +6134,297658.1914079,0.57579,0 +6135,293887.4450885,0.52539,0 +6136,286724.4116885,0.43652,0 +6137,286447.4902942,0.29882,0 +6138,286710.5656188,0.14781,0 +6139,291935.1492584,0.017792,0 +6140,293287.4487341,0,0.0069945 +6141,274198.3339517,0,0.021374 +6142,251047.7053857,0,0.073837 +6143,225986.3191989,0,0.18213 +6144,214032.5456771,0,0.30909 +6145,208009.5053504,0,0.44751 +6146,207289.5097251,0,0.57045 +6147,210400.2600548,0,0.65239 +6148,216658.6835666,0,0.70401 +6149,232295.5116331,0,0.76715 +6150,269426.055256,0,0.81315 +6151,294524.3642954,0.029791,0.81627 +6152,307895.052285,0.14788,0.84912 +6153,310419.6523299,0.20546,0.94378 +6154,307147.3645203,0.18286,0.96705 +6155,321611.8920174,0.18872,0.95325 +6156,312524.2549268,0.17093,0.89137 +6157,309967.3473858,0.13399,0.77763 +6158,303888.9227803,0.14638,0.68527 +6159,298488.9555909,0.14475,0.61811 +6160,292821.2977203,0.12744,0.55669 +6161,291824.3807007,0.10544,0.51205 +6162,292964.373774,0.059697,0.4608 +6163,298207.41884,0.0063655,0.40058 +6164,295239.7445641,0,0.34321 +6165,276095.2455028,0,0.32999 +6166,255404.6019898,0,0.28835 +6167,234907.8034529,0,0.22965 +6168,223941.7162375,0,0.1967 +6169,222012.4971903,0,0.17011 +6170,219483.2817888,0,0.15466 +6171,221920.1900589,0,0.15849 +6172,227740.1546964,0,0.15013 +6173,242024.6832872,0,0.13555 +6174,277369.0839167,0,0.10929 +6175,297330.5010913,0.027845,0.086448 +6176,305564.297216,0.1564,0.099709 +6177,306344.2924767,0.24499,0.22294 +6178,311707.3368135,0.26826,0.35988 +6179,317919.6067597,0.28073,0.43597 +6180,314264.2443545,0.25841,0.45944 +6181,310368.8834076,0.2109,0.39934 +6182,304973.5315748,0.15155,0.25996 +6183,299038.183023,0.088283,0.09851 +6184,293176.6801764,0.033599,0.028487 +6185,291815.1499876,0.024291,0.0087332 +6186,290905.9247429,0.011113,0 +6187,294699.7478452,0,0 +6188,296961.2725655,0,0.012257 +6189,281384.4441345,0,0.030569 +6190,262004.561888,0,0.074378 +6191,238918.5483141,0,0.15945 +6192,223955.5623072,0,0.30519 +6193,220290.9691889,0,0.51739 +6194,215343.3069436,0,0.72571 +6195,216358.6853894,0,0.86704 +6196,222898.6456522,0,0.87456 +6197,237552.4027688,0,0.8008 +6198,272513.7288028,0,0.59844 +6199,294972.0538829,0.024281,0.30867 +6200,302264.3172669,0.16363,0.16085 +6201,300741.2495981,0.28203,0.23418 +6202,303528.9249677,0.34914,0.37838 +6203,312625.7927714,0.40664,0.52286 +6204,314402.7050517,0.42823,0.64299 +6205,311928.873929,0.41637,0.71812 +6206,306861.2124128,0.39509,0.76647 +6207,302735.0836373,0.34637,0.81418 +6208,294436.6725205,0.27375,0.86185 +6209,291893.6110493,0.21573,0.84196 +6210,290712.0797668,0.11589,0.82967 +6211,296605.8901095,0.011071,0.8242 +6212,295733.5877173,0,0.75779 +6213,277609.0824585,0,0.65269 +6214,257772.2799114,0,0.53041 +6215,235493.9537376,0,0.41318 +6216,217189.4495724,0,0.32855 +6217,213557.1639501,0,0.27534 +6218,209260.2669815,0,0.25343 +6219,210612.5664571,0,0.24198 +6220,217189.4495724,0,0.21444 +6221,231044.750002,0,0.17844 +6222,266066.0756715,0,0.14367 +6223,289032.0899746,0.022441,0.10041 +6224,297524.3460673,0.17802,0.084437 +6225,297145.8868284,0.35015,0.10751 +6226,301179.7084725,0.49677,0.10255 +6227,307313.5173569,0.51109,0.10193 +6228,302952.0053962,0.46076,0.096288 +6229,297238.1939598,0.36167,0.080605 +6230,290029.0069941,0.35623,0.066174 +6231,287282.8698337,0.32355,0.059226 +6232,284790.5772847,0.26462,0.07059 +6233,284089.0430858,0.22667,0.073918 +6234,284564.4248127,0.12797,0.071868 +6235,288644.4000225,0.010542,0.090991 +6236,289276.7038729,0,0.087296 +6237,270639.8940345,0,0.062466 +6238,256161.5204677,0,0.026996 +6239,236841.6378566,0,0.0096035 +6240,220014.0477946,0,0 +6241,211738.7134607,0,0 +6242,208097.1971253,0,0 +6243,209984.8779633,0,0.0068541 +6244,211572.5606241,0,0.011663 +6245,211406.4077875,0,0.018544 +6246,214812.5409378,0,0.027899 +6247,224241.7144147,0.020838,0.034246 +6248,239232.392561,0.17339,0.02203 +6249,250964.6289674,0.34136,0.020861 +6250,255127.6805955,0.48278,0.017414 +6251,255113.8345258,0.53627,0.013668 +6252,250632.3232942,0.53693,0.010147 +6253,242753.9096256,0.49269,0.0077382 +6254,237838.5548762,0.47282,0.0064247 +6255,235212.4169867,0.41992,0.0057029 +6256,232595.5098103,0.33579,0.0057703 +6257,235101.6484289,0.25452,0.0061171 +6258,239287.7768399,0.12872,0.0068361 +6259,246164.6581324,0.0087115,0.0066328 +6260,245393.8935848,0,0.0064147 +6261,231298.5946135,0,0.005888 +6262,222654.0317538,0,0.0057166 +6263,206786.4358588,0,0.0068634 +6264,193803.4378213,0,0.0088761 +6265,185832.7170212,0,0.011484 +6266,183654.2687191,0,0.013413 +6267,181955.8175006,0,0.013464 +6268,183589.6537271,0,0.012018 +6269,182080.432128,0,0.010387 +6270,182791.1970402,0,0.0091855 +6271,186409.6365927,0.017001,0.007858 +6272,195261.8904981,0.16777,0 +6273,204367.989015,0.33762,0.0059751 +6274,213183.3200678,0.48132,0.011657 +6275,220909.4269696,0.56805,0.013755 +6276,217494.0631062,0.6078,0.012958 +6277,208014.120707,0.60265,0.011617 +6278,202060.3107289,0.55902,0.01064 +6279,198248.0262003,0.47857,0.0097714 +6280,197135.7252664,0.36777,0.0098359 +6281,206324.9002016,0.26698,0.0099898 +6282,219164.8221853,0.12774,0.011668 +6283,231533.9777987,0.0056812,0.014515 +6284,236061.6425959,0,0.016377 +6285,225506.3221154,0,0.017317 +6286,220134.0470654,0,0.019121 +6287,204792.6018196,0,0.021996 +6288,196148.03896,0,0.022965 +6289,193383.4403732,0,0.022302 +6290,190877.3017546,0,0.021826 +6291,192958.8275686,0,0.021969 +6292,198981.8678953,0,0.022654 +6293,213358.7036175,0,0.02333 +6294,257523.0506565,0,0.022848 +6295,284070.5816595,0.01553,0.020147 +6296,294893.5928212,0.1625,0.011223 +6297,295295.128843,0.33117,0.0069507 +6298,298562.8012961,0.47095,0 +6299,301858.1658886,0.53521,0 +6300,297704.3449736,0.54924,0 +6301,294727.4399846,0.51871,0 +6302,289299.7806557,0.47833,0 +6303,286124.4153341,0.40605,0 +6304,281361.3673516,0.30836,0 +6305,282699.8207576,0.23628,0.0068602 +6306,287218.2548417,0.11566,0.013065 +6307,296684.3511712,0.0045248,0.019435 +6308,294704.3632017,0,0.02061 +6309,275864.4776742,0,0.016634 +6310,257310.7442541,0,0.011568 +6311,234626.266702,0,0.0085704 +6312,218809.4397292,0,0.0077139 +6313,215777.1504613,0,0.0076149 +6314,212467.9397991,0,0.0083645 +6315,212707.9383409,0,0.0098646 +6316,220623.2748621,0,0.010799 +6317,233989.3474951,0,0.0099045 +6318,273178.3401492,0,0.0080245 +6319,293365.9097958,0.013972,0.0068208 +6320,301410.4763011,0.15725,0 +6321,300436.6360643,0.32613,0 +6322,303348.9260614,0.46777,0.0060088 +6323,305836.6032538,0.55301,0.0066342 +6324,301258.1695342,0.59167,0.0082007 +6325,297441.269649,0.5857,0.01074 +6326,293388.9865787,0.538000,0.014247 +6327,289032.0899746,0.45456,0.01844 +6328,283059.8185702,0.34292,0.023201 +6329,283927.5056058,0.24795,0.026334 +6330,286881.3338119,0.11305,0.045338 +6331,296836.6579381,0.0034013,0.080864 +6332,295392.051331,0,0.11074 +6333,275726.016977,0,0.12092 +6334,255321.5255715,0,0.1222 +6335,231653.9770695,0,0.1207 +6336,220166.3545615,0,0.12593 +6337,213598.7021593,0,0.13666 +6338,208715.6549059,0,0.14943 +6339,210312.5682799,0,0.15532 +6340,218726.3633109,0,0.13935 +6341,230481.6765002,0,0.11201 +6342,271253.7364586,0,0.087684 +6343,292507.4534734,0.01085,0.059645 +6344,298996.6448138,0.15245,0.023007 +6345,297150.502185,0.32095,0.017919 +6346,299144.3362241,0.46233,0.034877 +6347,302218.1637012,0.54727,0.031533 +6348,298493.5709475,0.58559,0.029532 +6349,295724.3570042,0.57931,0.029178 +6350,291575.1514458,0.53128,0.029763 +6351,288418.2475505,0.44779,0.032336 +6352,281596.7505368,0.33614,0.030772 +6353,283119.8182056,0.24129,0.033969 +6354,285219.805446,0.10658,0.056177 +6355,296578.19797,0.0011586,0.0909 +6356,295918.2019802,0,0.12458 +6357,276533.7043772,0,0.15093 +6358,255529.2166173,0,0.17642 +6359,234718.5738335,0,0.1758 +6360,218827.9011555,0,0.15556 +6361,215698.6893996,0,0.1313 +6362,211531.022415,0,0.11125 +6363,212329.4791019,0,0.10235 +6364,219012.5154184,0,0.093693 +6365,233352.4282881,0,0.097086 +6366,270713.7397397,0,0.10942 +6367,291076.692936,0.0097739,0.10405 +6368,297279.732169,0.14801,0.051408 +6369,296121.2776694,0.31612,0.042073 +6370,298668.9544972,0.45633,0.10744 +6371,300953.5560004,0.52661,0.18621 +6372,296208.9694443,0.54745,0.21399 +6373,293121.2958975,0.52366,0.20755 +6374,290633.6187051,0.48166,0.1802 +6375,276067.5533634,0.40666,0.1373 +6376,275938.3233794,0.30529,0.077252 +6377,281605.98125,0.22416,0.037553 +6378,286752.1038279,0.098228,0.030473 +6379,299942.7929111,0.00051143,0.031038 +6380,298373.5716766,0,0.034867 +6381,278855.228733,0,0.04091 +6382,256572.2872026,0,0.056409 +6383,234460.1138654,0,0.08032 +6384,220037.1245774,0,0.10313 +6385,214507.927404,0,0.11878 +6386,207741.8146692,0,0.12205 +6387,210063.339025,0,0.11357 +6388,218661.7483189,0,0.09043 +6389,232073.9745176,0,0.069492 +6390,270016.8208973,0,0.055509 +6391,292572.0684654,0.0086488,0.042777 +6392,300072.0228951,0.12159,0.023185 +6393,298313.5720412,0.22187,0.022814 +6394,301595.090564,0.26057,0.024471 +6395,303432.0024797,0.2984,0.02625 +6396,297316.6550216,0.30391,0.022012 +6397,291150.5386412,0.28227,0.017045 +6398,286770.5652542,0.28598,0.014121 +6399,284749.0390756,0.26424,0.012213 +6400,282662.897905,0.21619,0.0093277 +6401,286544.4127822,0.18311,0.0059143 +6402,289825.931305,0.085889,0 +6403,296596.6593963,0,0 +6404,285889.0321489,0,0 +6405,266998.3776991,0,0 +6406,248707.7196037,0,0.01055 +6407,226055.5495475,0,0.017673 +6408,208434.118155,0,0.027679 +6409,199120.3285924,0,0.044505 +6410,193568.0546361,0,0.071767 +6411,196946.4956469,0,0.099305 +6412,198308.0258357,0,0.11027 +6413,202563.3845953,0,0.096903 +6414,208618.7324179,0,0.088902 +6415,220221.7388403,0.0057034,0.08751 +6416,238946.2404535,0.10866,0.084408 +6417,251606.163531,0.18507,0.10200 +6418,256073.8286928,0.18814,0.16677 +6419,258810.7351401,0.25068,0.30885 +6420,252995.3858592,0.29835,0.37966 +6421,241124.6887556,0.32522,0.38235 +6422,234307.8070985,0.29374,0.32106 +6423,231630.9002867,0.24134,0.23745 +6424,231760.1302707,0.17415,0.15258 +6425,237704.7095356,0.15974,0.079589 +6426,244983.1268499,0.075647,0.064441 +6427,251924.6231344,0,0.064396 +6428,242486.2189444,0,0.060266 +6429,228506.3038873,0,0.061738 +6430,218901.7468607,0,0.068811 +6431,200781.8569584,0,0.074389 +6432,189368.0801555,0,0.074239 +6433,183635.8072929,0,0.07978 +6434,181014.2847599,0,0.09011 +6435,180455.8266146,0,0.091649 +6436,180912.7469153,0,0.10267 +6437,179869.67633,0,0.13521 +6438,181286.5907976,0,0.16968 +6439,186035.7927104,0.0049599,0.16700 +6440,199600.3256759,0.10268,0.12732 +6441,207751.0453824,0.17157,0.21906 +6442,218144.8283828,0.16268,0.42605 +6443,227066.3126368,0.22105,0.49288 +6444,222824.799947,0.26618,0.49471 +6445,213644.855725,0.29206,0.46882 +6446,207991.0439241,0.28184,0.43127 +6447,206269.5159227,0.24815,0.39216 +6448,208327.9649539,0.19273,0.25798 +6449,215223.3076727,0.16494,0.1177 +6450,227606.3093558,0.071214,0.088558 +6451,240870.8441441,0,0.10767 +6452,239435.4682502,0,0.16116 +6453,229184.7613034,0,0.23145 +6454,222824.799947,0,0.28936 +6455,206089.5170164,0,0.35196 +6456,196840.3424458,0,0.39056 +6457,195580.3501016,0,0.40847 +6458,196332.6532228,0,0.43067 +6459,199521.8646142,0,0.44382 +6460,208900.2691688,0,0.45087 +6461,223798.6401837,0,0.4831 +6462,267570.681914,0,0.54621 +6463,295885.8944842,0.0041371,0.58394 +6464,305753.5268355,0.10159,0.57104 +6465,303995.0759815,0.182000,0.65897 +6466,307073.5188151,0.19143,0.8385 +6467,311711.9521701,0.19835,0.88966 +6468,309584.2727904,0.17488,0.8963 +6469,308610.4325536,0.13001,0.87473 +6470,305688.9118435,0.12795,0.82479 +6471,305375.0675966,0.11424,0.72795 +6472,302430.4701035,0.089416,0.57923 +6473,305559.6818594,0.11855,0.46694 +6474,309699.6567047,0.058985,0.40122 +6475,317984.2217517,0,0.40728 +6476,305430.4518754,0,0.40492 +6477,287162.8705628,0,0.37625 +6478,268673.7521348,0,0.26436 +6479,247563.1111738,0,0.16716 +6480,231732.4381313,0,0.14884 +6481,226687.8533979,0,0.18295 +6482,222538.6478395,0,0.25722 +6483,223120.1827676,0,0.34742 +6484,231067.8267849,0,0.41645 +6485,243196.9838565,0,0.45122 +6486,278315.232014,0,0.44188 +6487,304345.843081,0.0018356,0.41802 +6488,313701.1708527,0.1119,0.43724 +6489,315441.1602804,0.24395,0.54516 +6490,320568.8214321,0.33418,0.67303 +6491,324948.7948191,0.38029,0.71046 +6492,321768.8141408,0.38257,0.68015 +6493,318708.8327335,0.34757,0.60521 +6494,315611.9284736,0.34926,0.49442 +6495,310073.500587,0.31944,0.38352 +6496,305093.5308457,0.25693,0.27562 +6497,305818.1418275,0.1857,0.15411 +6498,307576.5926815,0.065195,0.10496 +6499,314675.0110894,0,0.090989 +6500,300524.3278392,0,0.13573 +6501,282016.7479849,0,0.22283 +6502,263670.7056106,0,0.34959 +6503,240635.460959,0,0.51944 +6504,224934.0179005,0,0.66827 +6505,221472.5004714,0,0.7785 +6506,218403.2883509,0,0.85097 +6507,221786.3447183,0,0.89782 +6508,227929.3843158,0,0.92135 +6509,239486.2371725,0,0.93346 +6510,276930.6250424,0,0.93673 +6511,299485.8726105,0.0014379,0.88759 +6512,304359.6891507,0.11559,0.70021 +6513,302707.3914978,0.27296,0.59551 +6514,303625.8474557,0.39896,0.79406 +6515,305882.7568195,0.40999,0.8809 +6516,300127.407174,0.35776,0.90488 +6517,296282.8151494,0.26063,0.91897 +6518,290347.4665976,0.26701,0.92066 +6519,286885.9491685,0.2471,0.91226 +6520,282482.8989987,0.19974,0.87827 +6521,285159.8058105,0.15949,0.81069 +6522,291621.3050115,0.057589,0.76546 +6523,305010.4544274,0,0.72082 +6524,296047.4319642,0,0.66287 +6525,277327.5457076,0,0.60086 +6526,259503.0386259,0,0.47881 +6527,237243.1738784,0,0.35652 +6528,223443.2577277,0,0.24500 +6529,219095.5918367,0,0.19535 +6530,216174.0711265,0,0.15739 +6531,218670.9790321,0,0.14858 +6532,225201.7085817,0,0.15477 +6533,238863.1640352,0,0.14946 +6534,273182.9555058,0,0.13687 +6535,294505.9028691,0.001015,0.1268 +6536,299965.869694,0.093526,0.094844 +6537,299730.4865088,0.18231,0.1364 +6538,300201.2528792,0.20182,0.26745 +6539,303288.9264259,0.20771,0.31407 +6540,299495.1033236,0.18005,0.30421 +6541,297542.8074936,0.12854,0.33427 +6542,296088.9701734,0.13161,0.37178 +6543,294879.7467515,0.12107,0.36025 +6544,291948.9953282,0.096378,0.43095 +6545,294256.6736142,0.11492,0.56559 +6546,298992.0294573,0.046174,0.66984 +6547,309228.8903343,0,0.72344 +6548,296421.2758466,0,0.74207 +6549,278038.3106197,0,0.75048 +6550,260444.5713666,0,0.75453 +6551,238853.9333221,0,0.74142 +6552,222958.6452876,0,0.72981 +6553,215855.6115231,0,0.71671 +6554,214604.849892,0,0.68416 +6555,217046.3735187,0,0.63605 +6556,223881.716602,0,0.55647 +6557,236237.0261457,0,0.45627 +6558,270492.2026242,0,0.35729 +6559,293804.3686702,0,0.27252 +6560,303367.3874877,0.085441,0.18535 +6561,304631.9951884,0.15252,0.11131 +6562,309095.0449937,0.13552,0.087669 +6563,312210.4106799,0.21227,0.1441 +6564,306690.4442196,0.2832,0.2562 +6565,297192.0403941,0.3365,0.28313 +6566,287047.4866485,0.31393,0.25849 +6567,281735.211234,0.26573,0.22083 +6568,276044.4765805,0.19599,0.1668 +6569,279792.1461171,0.15371,0.13845 +6570,284578.2708824,0.04991,0.16649 +6571,298447.4173818,0,0.19291 +6572,288141.3261561,0,0.20461 +6573,269656.8230847,0,0.21601 +6574,254213.8399942,0,0.23122 +6575,232812.4315692,0,0.22651 +6576,215227.9230292,0,0.20396 +6577,206767.9744325,0,0.17399 +6578,202480.308177,0,0.13977 +6579,202554.1538821,0,0.11256 +6580,206140.2859387,0,0.091624 +6581,205637.2120723,0,0.09604 +6582,212971.0136655,0,0.10425 +6583,224638.6350799,0,0.10369 +6584,240607.7688195,0.09933,0.074426 +6585,255049.2195338,0.23803,0.068279 +6586,262683.0193041,0.33609,0.070599 +6587,267261.4530237,0.35877,0.048797 +6588,263481.4759911,0.33017,0.0344 +6589,253775.3811199,0.32861,0.028325 +6590,245089.2800511,0.3376,0.024163 +6591,239629.3132262,0.313000,0.018674 +6592,238360.0901689,0.25209,0.016717 +6593,243303.1370577,0.19083,0.019728 +6594,251361.5496326,0.056845,0.034828 +6595,261879.9472606,0,0.053753 +6596,248740.0270997,0,0.070286 +6597,235184.7248472,0,0.078733 +6598,227144.7736985,0,0.069508 +6599,208346.4263802,0,0.055573 +6600,193748.0535424,0,0.041294 +6601,185218.8745971,0,0.034778 +6602,179597.3702922,0,0.034896 +6603,182620.428847,0,0.043141 +6604,187369.6307597,0,0.040322 +6605,189252.6962412,0,0.030914 +6606,190789.6099797,0,0.024366 +6607,193872.6681699,0,0.020329 +6608,201797.2354043,0.12702,0.015092 +6609,209587.9572981,0.33222,0.014324 +6610,216155.6097002,0.50791,0.017594 +6611,224860.1721953,0.54472,0.015954 +6612,223069.4138453,0.50595,0.015054 +6613,213977.1613982,0.41,0.014723 +6614,208194.1196133,0.3674,0.014946 +6615,204607.9875567,0.29738,0.016528 +6616,206089.5170164,0.20805,0.022645 +6617,214314.082428,0.16702,0.033508 +6618,229161.6845206,0.049017,0.063824 +6619,248920.026006,0,0.097136 +6620,244369.2844258,0,0.11515 +6621,232955.5076229,0,0.13264 +6622,224163.2533529,0,0.16124 +6623,206287.977349,0,0.19452 +6624,193812.6685345,0,0.20347 +6625,193720.361403,0,0.19862 +6626,192635.7526086,0,0.19529 +6627,195160.3526535,0,0.17812 +6628,199323.4042816,0,0.12488 +6629,214577.1577526,0,0.10739 +6630,257103.0532084,0,0.12202 +6631,283553.6617234,0,0.15237 +6632,295798.2027093,0.10441,0.17686 +6633,297007.4261312,0.24464,0.25897 +6634,300468.9435604,0.31171,0.44286 +6635,305287.3758217,0.36569,0.53231 +6636,301576.6291377,0.37779,0.57935 +6637,298802.7998378,0.35369,0.58201 +6638,295092.0531538,0.31723,0.59207 +6639,291884.3803361,0.25652,0.60165 +6640,284315.1955578,0.17881,0.59526 +6641,285067.4986791,0.15259,0.58143 +6642,292576.683822,0.042261,0.64713 +6643,309007.3532188,0,0.64388 +6644,296536.6597609,0,0.59867 +6645,277724.4663728,0,0.53956 +6646,257195.3603398,0,0.49774 +6647,235221.6476998,0,0.47088 +6648,220766.3509158,0,0.46446 +6649,216709.4524889,0,0.46582 +6650,212398.7094505,0,0.4707 +6651,213760.2396393,0,0.4246 +6652,217521.7552456,0,0.33363 +6653,228520.149957,0,0.31709 +6654,266536.8420419,0,0.31822 +6655,294912.0542475,0,0.33081 +6656,301853.550532,0.10759,0.3472 +6657,302453.5468864,0.29095,0.3599 +6658,308490.4332828,0.42973,0.39125 +6659,313401.1726755,0.52107,0.40343 +6660,309519.6577983,0.56157,0.38289 +6661,307904.2829981,0.55319,0.36344 +6662,302873.5443344,0.49051,0.33383 +6663,298373.5716766,0.39239,0.33045 +6664,292068.994599,0.26993,0.30237 +6665,292285.9163579,0.18184,0.31542 +6666,296241.2769403,0.04166,0.41056 +6667,310008.885595,0,0.41649 +6668,294958.2078132,0,0.36129 +6669,275324.4809553,0,0.28552 +6670,257223.0524793,0,0.25289 +6671,233440.120063,0,0.23088 +6672,220826.3505513,0,0.19214 +6673,216630.9914272,0,0.12856 +6674,209689.4951427,0,0.069616 +6675,212011.0194985,0,0.06014 +6676,216192.5325528,0,0.056543 +6677,229138.6077377,0,0.053378 +6678,268378.3693142,0,0.053463 +6679,296227.4308705,0,0.055683 +6680,302181.2408486,0.085514,0.054006 +6681,300635.096397,0.18962,0.060357 +6682,303648.9242386,0.20224,0.057896 +6683,308351.9725856,0.32404,0.064543 +6684,304105.8445392,0.43394,0.074819 +6685,302430.4701035,0.51345,0.081127 +6686,297335.1164479,0.46512,0.10997 +6687,293841.2915227,0.38124,0.14578 +6688,286899.7952382,0.26937,0.17871 +6689,288238.2486441,0.17802,0.29849 +6690,295424.358827,0.036199,0.48199 +6691,310050.4238041,0,0.55863 +6692,294473.5953731,0,0.52715 +6693,274498.3321288,0,0.44831 +6694,255123.0652389,0,0.37548 +6695,233430.8893498,0,0.31626 +6696,220383.2763203,0,0.26722 +6697,216741.7599849,0,0.19308 +6698,212597.1697831,0,0.15975 +6699,213238.7043466,0,0.15942 +6700,217563.2934548,0,0.14823 +6701,230287.8315242,0,0.15162 +6702,267626.0661929,0,0.16065 +6703,297279.732169,0,0.1772 +6704,304276.6127324,0.094206,0.18091 +6705,304581.2262661,0.27016,0.2036 +6706,308402.7415079,0.39322,0.28573 +6707,313770.4012013,0.50715,0.3658 +6708,309755.0409835,0.57922,0.43842 +6709,303939.6917026,0.60438,0.47861 +6710,298747.4155589,0.52208,0.46351 +6711,295622.8191596,0.40345,0.37783 +6712,289129.0124626,0.26436,0.27998 +6713,291805.9192744,0.17225,0.29526 +6714,297847.4210274,0.032954,0.36977 +6715,308753.5086074,0,0.39812 +6716,292521.2995431,0,0.38107 +6717,273224.4937149,0,0.33391 +6718,255256.9105795,0,0.29022 +6719,231801.6684799,0,0.23964 +6720,219834.0488883,0,0.18743 +6721,216469.4539472,0,0.14945 +6722,213626.3942987,0,0.13399 +6723,215435.614075,0,0.13506 +6724,220987.8880313,0,0.12466 +6725,234875.4959569,0,0.1123 +6726,271230.6596758,0,0.093195 +6727,299028.9523098,0,0.057214 +6728,306731.9824288,0.082238,0.035107 +6729,307936.5904941,0.21827,0.027094 +6730,312104.2574788,0.27865,0.030625 +6731,317061.1504372,0.3473,0.056428 +6732,312002.7196342,0.38164,0.082387 +6733,304235.0745232,0.38165,0.088764 +6734,293135.1419672,0.34863,0.072622 +6735,285625.9568243,0.2865,0.064994 +6736,278767.5369581,0.20121,0.056078 +6737,280198.2974955,0.14583,0.05214 +6738,286502.874573,0.026145,0.06952 +6739,302688.9300716,0,0.11191 +6740,288676.7075185,0,0.18243 +6741,271249.121102,0,0.27173 +6742,256890.7468061,0,0.33292 +6743,235355.4930404,0,0.3752 +6744,217429.4481142,0,0.39043 +6745,207963.3517847,0,0.38559 +6746,201520.31401,0,0.3867 +6747,203541.8401886,0,0.41027 +6748,205618.750646,0,0.41912 +6749,209504.8808798,0,0.39262 +6750,213963.3153285,0,0.30746 +6751,225570.9371074,0,0.16666 +6752,237820.0934499,0.065649,0.078835 +6753,248380.029287,0.14214,0.041336 +6754,254486.146032,0.1073,0.022854 +6755,256581.5179157,0.12736,0.013435 +6756,250881.5525491,0.13034,0.0098931 +6757,240464.6927658,0.11898,0.0095925 +6758,234815.4963215,0.12704,0.0096207 +6759,231524.7470855,0.11937,0.0080965 +6760,229189.37666,0.094001,0 +6761,234367.806734,0.10315,0 +6762,245236.9714614,0.020524,0 +6763,261921.4854697,0,0 +6764,249543.0991432,0,0.01446 +6765,233573.9654036,0,0.03302 +6766,223650.9487734,0,0.057574 +6767,205503.3667317,0,0.081196 +6768,190572.6882208,0,0.09731 +6769,183848.1136952,0,0.1118 +6770,183363.5012551,0,0.1224 +6771,187415.7843254,0,0.13445 +6772,188578.8541816,0,0.13781 +6773,189428.0797909,0,0.10500 +6774,188463.4702673,0,0.094316 +6775,189792.6929601,0,0.084275 +6776,198432.6404632,0.079688,0.068359 +6777,206920.2811994,0.26644,0.060376 +6778,215532.536563,0.40932,0.063859 +6779,224749.4036376,0.50756,0.069743 +6780,219792.5106791,0.55377,0.080836 +6781,208489.5024339,0.55014,0.10833 +6782,199235.7125067,0.47019,0.12559 +6783,195621.8883107,0.35702,0.11173 +6784,196701.8817486,0.22689,0.10141 +6785,208083.3510556,0.14532,0.16335 +6786,225520.1681851,0.021188,0.27851 +6787,245393.8935848,0,0.38845 +6788,236223.180076,0,0.46369 +6789,227324.7726049,0,0.50601 +6790,222441.7253515,0,0.53994 +6791,205549.5202974,0,0.56022 +6792,195229.5830021,0,0.56081 +6793,192834.2129412,0,0.55842 +6794,194869.5851895,0,0.55932 +6795,196932.6495772,0,0.56394 +6796,205203.3685545,0,0.54921 +6797,217143.2960067,0,0.52673 +6798,256009.2137008,0,0.50898 +6799,285570.5725454,0,0.48879 +6800,295396.6666876,0.076862,0.36194 +6801,296028.9705379,0.26907,0.26326 +6802,297565.8842765,0.42434,0.36451 +6803,300090.4843214,0.52328,0.44018 +6804,296222.815514,0.56674,0.43409 +6805,291067.4622229,0.55778,0.4223 +6806,285021.3451134,0.4812,0.39933 +6807,282469.052929,0.36931,0.31499 +6808,277516.775327,0.23724,0.30896 +6809,281319.8291425,0.14521,0.45422 +6810,290989.0011612,0.01741,0.61138 +6811,307668.8998129,0,0.67448 +6812,291865.9189099,0,0.67509 +6813,274244.4875174,0,0.64781 +6814,257043.053573,0,0.60824 +6815,235567.7994427,0,0.57783 +6816,220794.0430553,0,0.55678 +6817,216455.6078774,0,0.55534 +6818,214632.5420314,0,0.55106 +6819,217267.9106341,0,0.54617 +6820,224869.4029085,0,0.50777 +6821,236712.4078726,0,0.46077 +6822,265955.3071138,0,0.4357 +6823,293573.6008416,0,0.38394 +6824,300367.4057158,0.070777,0.26865 +6825,301890.4733846,0.25584,0.25782 +6826,304858.1476605,0.40528,0.2961 +6827,309131.9678463,0.5079,0.33045 +6828,304424.3041427,0.55782,0.33300 +6829,299693.5636562,0.55671,0.32793 +6830,293892.060445,0.46912,0.31717 +6831,289087.4742534,0.34868,0.2727 +6832,284915.1919122,0.21396,0.24927 +6833,287670.5597858,0.13181,0.35321 +6834,297353.5778742,0.014572,0.4741 +6835,311338.1082878,0,0.51165 +6836,294861.2853252,0,0.49887 +6837,278112.1563248,0,0.46518 +6838,261653.7947885,0,0.40964 +6839,238793.9336867,0,0.3596 +6840,227666.3089912,0,0.31156 +6841,221587.8843857,0,0.27758 +6842,218814.0550858,0,0.25142 +6843,222981.7220705,0,0.23447 +6844,229793.988371,0,0.21192 +6845,241692.377614,0,0.19718 +6846,272518.3441594,0,0.18998 +6847,294981.2845961,0,0.17995 +6848,302725.8529241,0.064268,0.10193 +6849,304133.5366786,0.2512,0.046347 +6850,307747.3608746,0.40373,0.027216 +6851,311642.7218215,0.49931,0.02946 +6852,306579.6756619,0.54144,0.019655 +6853,301036.6324187,0.53276,0.009793 +6854,294298.2118234,0.45396,0 +6855,288436.7089767,0.34191,0 +6856,283475.2006617,0.21299,0.012513 +6857,286285.9528141,0.12748,0.032377 +6858,296402.8144203,0.010938,0.058297 +6859,311721.1828833,0,0.073822 +6860,294755.132124,0,0.068231 +6861,278693.6912529,0,0.041682 +6862,264030.7034232,0,0.018653 +6863,241563.14763,0,0.0097017 +6864,227933.9996724,0,0.0061181 +6865,224320.1754764,0,0 +6866,221297.1169216,0,0 +6867,223295.5663174,0,0 +6868,230066.2944087,0,0 +6869,239260.0847005,0,0 +6870,269772.206999,0,0 +6871,295964.3555459,0,0 +6872,305102.7615588,0.053564,0 +6873,307442.7473409,0.197000,0 +6874,312067.3346262,0.28293,0 +6875,316631.922276,0.34796,0 +6876,313119.6359246,0.37139,0 +6877,308111.9740438,0.35688,0 +6878,301779.7048268,0.32036,0 +6879,297441.269649,0.25565,0.0073387 +6880,291219.7689898,0.17,0.014895 +6881,294344.3653891,0.049461,0.040414 +6882,304595.0723359,0,0.088589 +6883,312256.5642456,0,0.11987 +6884,295359.743835,0,0.12376 +6885,279044.4583524,0,0.11739 +6886,265203.0039925,0,0.09297 +6887,243044.6770896,0,0.067216 +6888,228612.4570885,0,0.047195 +6889,221601.7304554,0,0.037719 +6890,220304.8152586,0,0.036383 +6891,222607.8781881,0,0.040654 +6892,229277.0684349,0,0.043513 +6893,240090.8488834,0,0.04491 +6894,268793.7514057,0,0.052181 +6895,296679.7358146,0,0.05645 +6896,306575.0603053,0.052628,0.049957 +6897,310221.1919973,0.23309,0.047867 +6898,315510.390629,0.38163,0.067984 +6899,319687.2883268,0.48658,0.12035 +6900,316530.3844314,0.54063,0.14833 +6901,309616.5802864,0.5441,0.17179 +6902,300768.9417375,0.46224,0.20168 +6903,295941.2787631,0.34573,0.24188 +6904,290698.2336971,0.21174,0.25334 +6905,291713.612143,0.061068,0.36785 +6906,300247.4064449,0,0.51788 +6907,303607.3860294,0,0.57056 +6908,281190.5991585,0,0.59214 +6909,265383.0028988,0,0.59805 +6910,256041.5211968,0,0.60618 +6911,236841.6378566,0,0.5942 +6912,219506.3585716,0,0.56225 +6913,208651.0399139,0,0.53627 +6914,203846.4537223,0,0.51525 +6915,204760.2943236,0,0.49728 +6916,208872.5770294,0,0.45587 +6917,208475.6563642,0,0.43565 +6918,210880.2571383,0,0.4458 +6919,225432.4764103,0,0.47777 +6920,242998.5235239,0.050307,0.4313 +6921,258939.9651241,0.23329,0.40066 +6922,269998.359471,0.38952,0.5434 +6923,275624.4791325,0.48822,0.61903 +6924,270866.0465066,0.5305,0.64674 +6925,261598.4105097,0.51987,0.67054 +6926,254246.1474902,0.43186,0.68833 +6927,251084.6282383,0.31304,0.71551 +6928,248098.4925361,0.18309,0.75589 +6929,253364.614385,0.0507,0.84923 +6930,265304.5418371,0,0.89151 +6931,267344.529442,0,0.89029 +6932,249667.7137707,0,0.87042 +6933,235793.9519148,0,0.83708 +6934,230892.4432351,0,0.79674 +6935,218454.0572732,0,0.75362 +6936,203144.9195233,0,0.71602 +6937,194292.665618,0,0.68548 +6938,189492.6947829,0,0.65915 +6939,191731.1427204,0,0.64127 +6940,194112.6667116,0,0.63516 +6941,193208.0568235,0,0.65479 +6942,190138.844703,0,0.67411 +6943,192686.5215308,0,0.68409 +6944,202420.3085415,0.047389,0.61787 +6945,217900.2144845,0.2288,0.57229 +6946,232000.1288125,0.38539,0.71051 +6947,245153.8950431,0.48686,0.76669 +6948,243510.8281034,0.53218,0.76886 +6949,233361.6590012,0.52407,0.73408 +6950,226503.239135,0.43446,0.71168 +6951,220784.8123421,0.31347,0.70729 +6952,217327.9102696,0.18151,0.67781 +6953,223249.4127517,0.048817,0.73733 +6954,240695.4605944,0,0.82764 +6955,251223.0889355,0,0.84998 +6956,240538.5384709,0,0.84442 +6957,234977.0338015,0,0.83715 +6958,235226.2630564,0,0.82352 +6959,220337.1227546,0,0.80675 +6960,208757.1931151,0,0.76942 +6961,201866.4657529,0,0.7222 +6962,203274.1495074,0,0.67556 +6963,205807.9802655,0,0.61774 +6964,212380.2480242,0,0.54099 +6965,224874.018265,0,0.47954 +6966,258496.8908932,0,0.49238 +6967,289147.4738889,0,0.53453 +6968,300815.0953033,0.040724,0.52418 +6969,304802.7633816,0.21492,0.56416 +6970,309413.5045972,0.36065,0.75439 +6971,313848.862263,0.46435,0.87218 +6972,310701.1890808,0.51557,0.91200 +6973,307119.6723808,0.51512,0.91859 +6974,300404.3285683,0.42095,0.91829 +6975,296610.505466,0.29731,0.90512 +6976,288565.9389608,0.16636,0.86698 +6977,289424.3952832,0.043053,0.88991 +6978,302218.1637012,0,0.90203 +6979,310996.5719014,0,0.89168 +6980,293822.8300965,0,0.86823 +6981,278435.2312849,0,0.84183 +6982,265830.6924863,0,0.82357 +6983,244710.8208122,0,0.79637 +6984,231764.7456273,0,0.76208 +6985,228875.5324131,0,0.74045 +6986,224763.2497073,0,0.73054 +6987,226844.7755214,0,0.72091 +6988,233597.0421864,0,0.71974 +6989,244563.1294019,0,0.71714 +6990,272056.8085022,0,0.70856 +6991,300335.0982198,0,0.6883 +6992,310982.7258317,0.032261,0.61242 +6993,312722.7152594,0.14905,0.52648 +6994,318233.4510066,0.19638,0.48129 +6995,321625.7380871,0.20431,0.48558 +6996,318953.4466318,0.1677,0.46797 +6997,316682.6911983,0.10352,0.40404 +6998,311296.5700786,0.083725,0.34841 +6999,307068.9034585,0.057937,0.32179 +7000,299582.7950985,0.031061,0.34517 +7001,298650.4930709,0.0056685,0.38914 +7002,307562.7466118,0,0.44319 +7003,312635.0234846,0,0.51667 +7004,294247.4429011,0,0.53375 +7005,279445.9943742,0,0.5218 +7006,265886.0767652,0,0.54226 +7007,247096.96016,0,0.60313 +7008,231967.8213164,0,0.67462 +7009,228044.7682301,0,0.72577 +7010,226692.4687545,0,0.72629 +7011,224975.5561096,0,0.67278 +7012,229669.3737435,0,0.69555 +7013,240372.3856343,0,0.75888 +7014,268249.1393301,0,0.80581 +7015,298082.8042126,0,0.86903 +7016,308439.6643605,0.031253,0.89147 +7017,311079.6483197,0.16999,0.88945 +7018,316585.7687103,0.26683,0.89482 +7019,321178.0484996,0.32424,0.92029 +7020,318542.6798969,0.33414,0.9412 +7021,316438.0773,0.30389,0.93182 +7022,311688.8753873,0.26081,0.90884 +7023,304322.7662981,0.19496,0.86756 +7024,297279.732169,0.11662,0.79832 +7025,299435.1036882,0.027164,0.68058 +7026,313770.4012013,0,0.54636 +7027,320633.4364241,0,0.40324 +7028,308836.5850257,0,0.28038 +7029,297404.3467964,0,0.18314 +7030,287255.1776943,0,0.12555 +7031,267815.2958124,0,0.10221 +7032,255699.9848105,0,0.083891 +7033,249658.4830575,0,0.074721 +7034,245606.1999872,0,0.074909 +7035,244664.6672464,0,0.087219 +7036,243349.2906234,0,0.13177 +7037,246298.503473,0,0.18975 +7038,275361.4038078,0,0.2166 +7039,303999.6913381,0,0.23684 +7040,310719.6505071,0.021284,0.22376 +7041,311481.1843415,0.10387,0.18925 +7042,316811.9211824,0.10203,0.21613 +7043,322156.5040929,0.12882,0.33789 +7044,320042.6707829,0.13677,0.52625 +7045,315178.0849558,0.1277,0.65426 +7046,306127.3707178,0.10259,0.7274 +7047,301055.093845,0.069835,0.78448 +7048,294995.1306658,0.036243,0.86506 +7049,297422.8082227,0.0062841,0.92658 +7050,312376.5635165,0,0.95636 +7051,327261.0884617,0,0.96632 +7052,313835.0161933,0,0.9682 +7053,301862.7812451,0,0.96474 +7054,289429.0106398,0,0.94825 +7055,270482.9719111,0,0.91485 +7056,258178.4312897,0,0.8854 +7057,254153.8403588,0,0.88008 +7058,250286.1715513,0,0.92499 +7059,250092.3265753,0,0.95913 +7060,251800.008507,0,0.97648 +7061,260961.4913027,0,0.98323 +7062,285330.5740037,0,0.98503 +7063,309898.1170373,0,0.98357 +7064,316931.9204532,0.021937,0.97809 +7065,317134.9961424,0.13598,0.97009 +7066,319779.5954582,0.20003,0.95004 +7067,322313.4262164,0.22992,0.93400 +7068,319133.4455381,0.21643,0.92406 +7069,312408.8710125,0.17148,0.90994 +7070,305379.6829531,0.13729,0.84064 +7071,299181.2590767,0.093302,0.71319 +7072,292701.2984494,0.048161,0.57892 +7073,293472.062997,0.0085037,0.45708 +7074,304461.2269953,0,0.3693 +7075,306990.4423968,0,0.27628 +7076,289618.2402592,0,0.19609 +7077,274858.3299415,0,0.12715 +7078,266042.9988887,0,0.084561 +7079,247696.9565143,0,0.063294 +7080,231026.2885757,0,0.049407 +7081,223244.7973951,0,0.043722 +7082,217415.6020444,0,0.041414 +7083,214655.6188143,0,0.04348 +7084,214558.6963263,0,0.052251 +7085,215435.614075,0,0.082537 +7086,216884.8360387,0,0.13013 +7087,228866.3017,0,0.16024 +7088,245223.1253917,0.016198,0.13846 +7089,256341.519374,0.10334,0.11416 +7090,263924.550222,0.11549,0.099847 +7091,268281.4468262,0.15908,0.077521 +7092,264003.0112838,0.18414,0.060355 +7093,255090.7577429,0.18872,0.047923 +7094,248587.7203328,0.15104,0.035468 +7095,245583.1232043,0.10229,0.028099 +7096,244960.0500671,0.052369,0.02135 +7097,253641.5357793,0.0090764,0.015636 +7098,271572.1960621,0,0.009547 +7099,272006.0395799,0,0 +7100,254476.9153188,0,0 +7101,245647.7381963,0,0 +7102,239818.5428457,0,0.0068729 +7103,224130.9458569,0,0.018643 +7104,209878.7247621,0,0.037662 +7105,202480.308177,0,0.054826 +7106,203527.9941188,0,0.06437 +7107,202780.3063541,0,0.073152 +7108,205807.9802655,0,0.073121 +7109,206241.8237833,0,0.060952 +7110,206149.5166518,0,0.051714 +7111,213017.1672312,0,0.04588 +7112,223346.3352397,0.013749,0.049556 +7113,235729.3369228,0.080727,0.068476 +7114,250853.8604097,0.054839,0.093262 +7115,263283.0156585,0.06994,0.15739 +7116,264616.8537079,0.073423,0.33927 +7117,255201.5263007,0.066859,0.57692 +7118,245172.3564694,0.053736,0.74446 +7119,239615.4671565,0.036191,0.83005 +7120,237349.3270796,0.017839,0.88662 +7121,240053.9260309,0.0012759,0.94484 +7122,255949.2140654,0,0.96551 +7123,262004.561888,0,0.96892 +7124,250415.4015353,0,0.97119 +7125,242956.9853147,0,0.97435 +7126,241373.9180105,0,0.97635 +7127,225160.1703725,0,0.97786 +7128,215818.6886705,0,0.98032 +7129,209800.2637004,0,0.98282 +7130,211309.4852995,0,0.98313 +7131,215744.8429653,0,0.97726 +7132,219455.5896493,0,0.97796 +7133,229475.5287675,0,0.98196 +7134,267492.2208523,0,0.98587 +7135,299675.1022299,0,0.99084 +7136,308582.7404142,0.011964,0.99574 +7137,310461.1905391,0.080457,0.99746 +7138,312085.7960525,0.068294,0.99782 +7139,314951.9324838,0.088798,0.99795 +7140,311845.7975107,0.095057,0.99769 +7141,311135.0325986,0.088457,0.99681 +7142,307036.5959625,0.093263,0.99606 +7143,302453.5468864,0.083188,0.99565 +7144,294182.8279091,0.057018,0.99532 +7145,295281.2827733,0.0088505,0.99459 +7146,310571.9590968,0,0.99481 +7147,317633.4546522,0,0.99422 +7148,301899.7040977,0,0.99383 +7149,287762.8669172,0,0.99364 +7150,275130.6359792,0,0.99444 +7151,255478.447695,0,0.99531 +7152,242278.5278986,0,0.99526 +7153,238323.1673163,0,0.99451 +7154,234340.1145945,0,0.99369 +7155,236029.3350999,0,0.99241 +7156,239375.4686148,0,0.98865 +7157,248652.3353248,0,0.98148 +7158,280618.2949435,0,0.96811 +7159,311795.0285884,0,0.94915 +7160,315501.1599159,0.012184,0.91408 +7161,309353.5049618,0.13838,0.85164 +7162,308135.0508267,0.24381,0.79916 +7163,311822.7207279,0.30261,0.81527 +7164,308702.7396851,0.31155,0.82429 +7165,306796.5974208,0.279000,0.75646 +7166,301521.2448588,0.2179,0.64668 +7167,297852.0363839,0.14216,0.44081 +7168,293005.9119832,0.068135,0.24227 +7169,294575.1332177,0.010206,0.16728 +7170,313738.0937053,0,0.1491 +7171,316470.384796,0,0.16312 +7172,300427.4053512,0,0.19415 +7173,285685.9564597,0,0.21046 +7174,272352.1913228,0,0.22398 +7175,251620.0096007,0,0.22893 +7176,237349.3270796,0,0.22702 +7177,232000.1288125,0,0.22676 +7178,229447.8366281,0,0.23153 +7179,231843.206689,0,0.2039 +7180,236781.6382212,0,0.14012 +7181,246940.0380365,0,0.11688 +7182,280313.6814098,0,0.11419 +7183,307927.359781,0,0.12375 +7184,315099.6238941,0.011461,0.14265 +7185,313198.0969864,0.15145,0.16399 +7186,315764.2352405,0.29585,0.1848 +7187,318358.065634,0.38806,0.25728 +7188,314670.3957329,0.42602,0.32003 +7189,311873.4896501,0.41423,0.41281 +7190,307147.3645203,0.32684,0.45757 +7191,303044.3125276,0.21631,0.48839 +7192,297233.5786033,0.10557,0.60998 +7193,301027.4017056,0.016667,0.74115 +7194,316982.6893755,0,0.80358 +7195,317702.6850008,0,0.83099 +7196,299744.3325785,0,0.84062 +7197,284273.6573487,0,0.83959 +7198,270058.3591064,0,0.83286 +7199,249806.1744678,0,0.82102 +7200,235863.1822633,0,0.80059 +7201,230121.6786876,0,0.77611 +7202,226143.2413224,0,0.75761 +7203,226461.7009259,0,0.74613 +7204,231007.8271494,0,0.71976 +7205,240704.6913075,0,0.69943 +7206,275398.3266604,0,0.68771 +7207,306141.2167875,0,0.71259 +7208,314393.4743385,0.010401,0.73416 +7209,311836.5667976,0.14999,0.70836 +7210,314587.3193146,0.30023,0.71218 +7211,318182.6820843,0.3919,0.78333 +7212,313899.6311853,0.42629,0.77757 +7213,310031.9623779,0.40868,0.76386 +7214,304193.5363141,0.30822,0.67707 +7215,300916.6331479,0.18991,0.5777 +7216,295493.5891756,0.081605,0.48498 +7217,298627.4162881,0.011649,0.47116 +7218,314619.6268106,0,0.47277 +7219,315556.5441947,0,0.4511 +7220,300468.9435604,0,0.40223 +7221,284947.4994082,0,0.33944 +7222,271050.6607694,0,0.28492 +7223,248827.7188745,0,0.25022 +7224,235978.5661777,0,0.24762 +7225,230273.9854545,0,0.29959 +7226,227472.4640152,0,0.39053 +7227,228455.534965,0,0.47957 +7228,235018.5720106,0,0.55662 +7229,244613.8983241,0,0.61189 +7230,275518.3259313,0,0.64128 +7231,306953.5195442,0,0.63951 +7232,312090.411409,0.0059154,0.57045 +7233,306611.9831579,0.11404,0.42699 +7234,305979.6793075,0.20594,0.32844 +7235,306256.6007018,0.29168,0.32122 +7236,300653.5578232,0.34211,0.26717 +7237,294782.8242635,0.35329,0.2066 +7238,288870.5524945,0.27259,0.15471 +7239,283055.2032136,0.1738,0.11371 +7240,280092.1442943,0.079063,0.14903 +7241,285736.725382,0.01015,0.26283 +7242,306939.6734745,0,0.37948 +7243,308928.8921571,0,0.45848 +7244,289996.6994981,0,0.51634 +7245,273386.031195,0,0.56583 +7246,263490.7067043,0,0.60558 +7247,244189.2855195,0,0.59621 +7248,227647.8475649,0,0.56047 +7249,222755.5695984,0,0.53018 +7250,219146.360759,0,0.50524 +7251,217701.7541519,0,0.48082 +7252,218744.8247372,0,0.47759 +7253,217023.2967358,0,0.48927 +7254,217872.5223451,0,0.50928 +7255,232415.5109039,0,0.54108 +7256,245278.5096705,0.0059004,0.53828 +7257,255003.0659681,0.13796,0.46221 +7258,261492.2573085,0.29188,0.41674 +7259,263550.7063397,0.39148,0.46655 +7260,259719.9603848,0.43494,0.53796 +7261,252003.0841962,0.4255,0.57899 +7262,245453.8932203,0.33403,0.58495 +7263,242084.6829226,0.21808,0.59628 +7264,241452.3790722,0.10247,0.67771 +7265,249824.6358941,0.013377,0.76477 +7266,267986.0640055,0,0.81096 +7267,266832.2248625,0,0.82265 +7268,249616.9448484,0,0.81247 +7269,236546.255036,0,0.79174 +7270,232840.1237086,0,0.7562 +7271,215463.3062144,0,0.69494 +7272,201543.3907928,0,0.61145 +7273,193508.0550007,0,0.52489 +7274,189363.4647989,0,0.42537 +7275,187761.9360684,0,0.31047 +7276,192451.1383457,0,0.25913 +7277,192681.9061743,0,0.26283 +7278,190900.3785374,0,0.30566 +7279,190268.074687,0,0.37298 +7280,200218.7834566,0.0021919,0.4316 +7281,212675.6308448,0.098098,0.44427 +7282,222127.8811046,0.17641,0.52716 +7283,229973.9872773,0.24976,0.67849 +7284,239232.392561,0.29027,0.80463 +7285,234123.1928356,0.29592,0.84677 +7286,223235.5666819,0.22124,0.86428 +7287,217443.2941839,0.13366,0.87289 +7288,217180.2188593,0.054507,0.85137 +7289,220803.2737684,0.00506,0.85187 +7290,244267.7465812,0,0.8629 +7291,249501.5609341,0,0.85301 +7292,241724.68511,0,0.81962 +7293,229064.7620326,0,0.76546 +7294,222889.414939,0,0.69591 +7295,220092.5088563,0,0.58252 +7296,202503.3849598,0,0.46016 +7297,192958.8275686,0,0.40488 +7298,189391.1569383,0,0.3564 +7299,191684.9891547,0,0.22273 +7300,194814.2009106,0,0.13838 +7301,200426.4745023,0,0.22368 +7302,201404.9300956,0,0.39867 +7303,213178.7047112,0,0.49438 +7304,226281.7020196,0.0017129,0.51451 +7305,235263.185909,0.094172,0.57167 +7306,240497.0002618,0.17384,0.71348 +7307,243349.2906234,0.25796,0.83602 +7308,249953.8658781,0.31249,0.88783 +7309,247198.4980046,0.33084,0.91102 +7310,243016.9849502,0.26416,0.9044 +7311,239749.3124971,0.17565,0.86364 +7312,240104.6949532,0.083927,0.84178 +7313,247595.4186698,0.0089912,0.83751 +7314,271581.4267752,0,0.8073 +7315,278361.3855797,0,0.74691 +7316,272306.0377571,0,0.6465 +7317,258358.430196,0,0.52891 +7318,246760.0391302,0,0.3639 +7319,239426.237537,0,0.26483 +7320,221177.1176508,0,0.2441 +7321,208157.1967607,0,0.19428 +7322,204077.2215509,0,0.18234 +7323,202318.7706969,0,0.21436 +7324,205974.1331021,0,0.29814 +7325,213321.7807649,0,0.46042 +7326,226267.8559498,0,0.63819 +7327,266901.4552111,0,0.74298 +7328,296952.0418524,0.00078747,0.80698 +7329,307355.055566,0.062503,0.84595 +7330,310576.5744534,0.072059,0.8642 +7331,316013.4644954,0.10521,0.87723 +7332,322456.5022701,0.12377,0.87072 +7333,319331.9058707,0.12554,0.80964 +7334,316345.7701686,0.09635,0.69219 +7335,312191.9492536,0.060192,0.57984 +7336,311679.6446741,0.025421,0.57791 +7337,311693.4907438,0.00054698,0.62319 +7338,327662.6244835,0,0.65996 +7339,328054.9297921,0,0.68442 +7340,319004.2155541,0,0.71114 +7341,300745.8649547,0,0.76531 +7342,284213.6577132,0,0.81435 +7343,268715.2903439,0,0.86183 +7344,245195.4332522,0,0.89826 +7345,225330.9385657,0,0.91509 +7346,223678.6409129,0,0.90455 +7347,222700.1853196,0,0.90097 +7348,226706.3148242,0,0.88886 +7349,234150.8849751,0,0.86351 +7350,243432.3670417,0,0.84318 +7351,279810.6075434,0,0.85027 +7352,308107.3586873,0,0.87595 +7353,317753.4539231,0.11112,0.90747 +7354,317047.3043675,0.25262,0.91952 +7355,320647.2824938,0.32199,0.94278 +7356,327021.08992,0.32843,0.9574 +7357,322331.8876426,0.28497,0.96188 +7358,317305.7643356,0.23898,0.96653 +7359,305407.3750926,0.16862,0.97296 +7360,306385.8306859,0.086057,0.97864 +7361,307110.4416677,0.0076554,0.97991 +7362,322811.8847262,0,0.98067 +7363,322364.1951387,0,0.98369 +7364,312981.1752275,0,0.98478 +7365,296305.8919323,0,0.98435 +7366,280364.4503321,0,0.98095 +7367,266555.3034682,0,0.97725 +7368,239407.7761108,0,0.97174 +7369,221574.038316,0,0.95816 +7370,217295.6027736,0,0.92738 +7371,215910.9958019,0,0.88509 +7372,220572.5059398,0,0.83262 +7373,228289.3821284,0,0.76374 +7374,239121.6240033,0,0.75384 +7375,273104.4944441,0,0.77359 +7376,300459.7128472,0,0.73262 +7377,309242.736404,0.078462,0.60785 +7378,317901.1453334,0.14912,0.42151 +7379,321044.203159,0.20072,0.41807 +7380,325018.0251676,0.21667,0.45208 +7381,324311.8756121,0.20108,0.4659 +7382,323587.2646303,0.14998,0.41178 +7383,320651.8978504,0.089423,0.33788 +7384,317351.9179013,0.034698,0.28557 +7385,320231.9004023,0.0013249,0.31304 +7386,333524.1273301,0,0.34415 +7387,336164.1112894,0,0.36285 +7388,330934.9122931,0,0.40335 +7389,314047.3225956,0,0.4582 +7390,299116.6440847,0,0.52978 +7391,285049.0372528,0,0.59116 +7392,261312.2584022,0,0.63919 +7393,250276.9408382,0,0.65297 +7394,243395.4441891,0,0.64033 +7395,239135.470073,0,0.62861 +7396,239749.3124971,0,0.61061 +7397,246787.7312696,0,0.61487 +7398,261321.4891153,0,0.63195 +7399,295927.4326934,0,0.66484 +7400,318404.2191997,0,0.70249 +7401,322664.1933158,0.096261,0.70399 +7402,318953.4466318,0.22915,0.75965 +7403,321104.2027945,0.28799,0.79807 +7404,324505.7205881,0.28397,0.82271 +7405,318611.9102455,0.23269,0.80773 +7406,313170.4048469,0.18499,0.74715 +7407,307253.5177214,0.12087,0.66533 +7408,305328.9140308,0.054448,0.59333 +7409,306796.5974208,0.0028801,0.53878 +7410,321584.199878,0,0.51007 +7411,320047.2861394,0,0.49154 +7412,308836.5850257,0,0.48532 +7413,288293.632923,0,0.48195 +7414,271050.6607694,0,0.50161 +7415,259258.4247276,0,0.54918 +7416,240880.0748573,0,0.59634 +7417,223023.2602796,0,0.61899 +7418,214046.3917468,0,0.61432 +7419,211618.7141898,0,0.63729 +7420,214263.3135057,0,0.6584 +7421,215837.1500968,0,0.67296 +7422,217424.8327576,0,0.69547 +7423,219294.0521693,0,0.72375 +7424,230458.5997174,0,0.74846 +7425,242693.9099901,0.10601,0.76014 +7426,256133.8283282,0.28151,0.78016 +7427,265586.078588,0.39306,0.84905 +7428,271858.3481696,0.44109,0.88358 +7429,264723.006909,0.42961,0.90412 +7430,256881.5160929,0.29187,0.90394 +7431,250660.0154337,0.14571,0.88558 +7432,249676.9444838,0.035973,0.89528 +7433,256299.9811648,0.00085018,0.90352 +7434,275246.0198935,0,0.91231 +7435,277318.3149944,0,0.9156 +7436,267912.2183004,0,0.9123 +7437,248541.5667671,0,0.90424 +7438,236273.9489983,0,0.90022 +7439,232027.8209519,0,0.89586 +7440,217572.5241679,0,0.88148 +7441,202032.6185895,0,0.85994 +7442,195594.1961713,0,0.83837 +7443,192668.0601046,0,0.81205 +7444,192395.7540668,0,0.75098 +7445,195003.4305301,0,0.6787 +7446,192663.444748,0,0.66415 +7447,189252.6962412,0,0.69 +7448,191348.0681249,0,0.70771 +7449,201714.158986,0.047518,0.67434 +7450,215620.2283379,0.068775,0.6056 +7451,226678.6226848,0.088927,0.52551 +7452,239592.3903736,0.087151,0.47102 +7453,238475.4740832,0.069146,0.4131 +7454,230989.3657232,0.081475,0.32036 +7455,226300.1634458,0.075373,0.24782 +7456,224781.7111336,0.046893,0.24186 +7457,233989.3474951,0.0016808,0.25373 +7458,256553.8257763,0,0.25323 +7459,261884.5626171,0,0.23292 +7460,256392.2882963,0,0.23574 +7461,246150.8120627,0,0.31523 +7462,238143.16841,0,0.40814 +7463,238166.2451929,0,0.46045 +7464,222137.1118178,0,0.49705 +7465,209634.1108638,0,0.49994 +7466,208041.8128464,0,0.46215 +7467,206352.592341,0,0.45516 +7468,210695.6428754,0,0.44835 +7469,215195.6155332,0,0.44966 +7470,227781.6929055,0,0.45291 +7471,267626.0661929,0,0.47701 +7472,297473.577145,0,0.54578 +7473,308933.5075137,0.07518,0.58043 +7474,309118.1217766,0.19936,0.53278 +7475,312122.718905,0.3034,0.52521 +7476,316295.0012463,0.36535,0.59383 +7477,313844.2469065,0.37996,0.67857 +7478,314028.8611693,0.30506,0.6882 +7479,310364.2680511,0.20136,0.69039 +7480,309561.1960075,0.090443,0.75663 +7481,310899.6494134,0.004662,0.82902 +7482,327519.5484297,0,0.85669 +7483,326264.1714421,0,0.85904 +7484,317716.5310705,0,0.85286 +7485,300035.1000426,0,0.84206 +7486,283729.0452732,0,0.83645 +7487,270210.6658733,0,0.8381 +7488,247281.5744229,0,0.82328 +7489,230924.7507311,0,0.79285 +7490,225944.7809898,0,0.77045 +7491,224726.3268547,0,0.7703 +7492,226000.1652687,0,0.76205 +7493,232493.9719657,0,0.74411 +7494,240838.5366481,0,0.73204 +7495,277198.3157236,0,0.72323 +7496,308268.8961673,0,0.69717 +7497,317051.9197241,0.085946,0.60144 +7498,313770.4012013,0.25864,0.47133 +7499,314171.9372231,0.32164,0.41546 +7500,316188.8480451,0.30265,0.44761 +7501,310082.7313002,0.22649,0.45844 +7502,308356.5879422,0.15705,0.41091 +7503,307715.0533786,0.08196,0.27205 +7504,310041.193091,0.022862,0.16253 +7505,315501.1599159,0,0.12633 +7506,332744.1320694,0,0.11453 +7507,331964.1368087,0,0.11174 +7508,322230.3497981,0,0.10543 +7509,303279.6957128,0,0.10397 +7510,286433.6442244,0,0.09731 +7511,272107.5774245,0,0.084905 +7512,247720.0332972,0,0.082698 +7513,235009.3412975,0,0.08935 +7514,229166.2998772,0,0.080036 +7515,226087.8570435,0,0.079021 +7516,227204.773334,0,0.11452 +7517,233084.7376069,0,0.20415 +7518,241733.9158231,0,0.32659 +7519,275869.0930308,0,0.43142 +7520,304031.9988341,0,0.54986 +7521,310761.1887163,0.068452,0.61418 +7522,310488.8826785,0.20721,0.66895 +7523,315182.7003124,0.26433,0.76446 +7524,318805.7552215,0.25432,0.85543 +7525,315722.6970313,0.19609,0.88858 +7526,316082.694844,0.14916,0.91539 +7527,314799.6257169,0.090408,0.93041 +7528,316101.1562702,0.034542,0.94253 +7529,320425.7453783,0,0.94516 +7530,334027.2011965,0,0.94477 +7531,329919.5338473,0,0.93506 +7532,319959.5943646,0,0.90594 +7533,302278.1633366,0,0.85795 +7534,286544.4127822,0,0.78627 +7535,275172.1741884,0,0.70061 +7536,255672.292671,0,0.63266 +7537,240326.2320686,0,0.61976 +7538,235900.1051159,0,0.67077 +7539,229530.9130464,0,0.74861 +7540,229960.1412076,0,0.78586 +7541,236066.2579525,0,0.81752 +7542,245532.354282,0,0.85762 +7543,276150.6297817,0,0.88718 +7544,302319.7015458,0,0.90536 +7545,306662.7520802,0.035596,0.91887 +7546,301608.9366337,0.067991,0.92449 +7547,300321.25215,0.099857,0.92273 +7548,303801.2310054,0.11145,0.9218 +7549,304811.9940948,0.10433,0.91891 +7550,305065.8387062,0.079103,0.91075 +7551,306155.0628572,0.047256,0.90653 +7552,312150.4110445,0.016955,0.92785 +7553,321662.6609397,0,0.95177 +7554,336897.9529844,0,0.96545 +7555,337645.6407491,0,0.97172 +7556,331714.9075538,0,0.9731 +7557,315390.3913581,0,0.97126 +7558,300395.0978552,0,0.96431 +7559,286932.1027342,0,0.95239 +7560,265959.9224704,0,0.94419 +7561,249727.7134061,0,0.93751 +7562,241258.5340962,0,0.92419 +7563,239717.0050011,0,0.91031 +7564,238189.3219757,0,0.90455 +7565,241410.8408631,0,0.8892 +7566,251260.0117881,0,0.85435 +7567,282201.3622478,0,0.80672 +7568,310756.5733597,0,0.72729 +7569,320093.4397052,0.032451,0.6122 +7570,323522.6496383,0.060526,0.46646 +7571,326388.7860696,0.10497,0.35862 +7572,331424.1400898,0.13688,0.3506 +7573,326979.5517108,0.15061,0.35462 +7574,321671.8916528,0.11417,0.33638 +7575,318741.1402295,0.068299,0.29272 +7576,318607.2948889,0.024872,0.22671 +7577,320218.0543326,0,0.2042 +7578,333482.589121,0,0.17935 +7579,330921.0662234,0,0.14613 +7580,321127.2795773,0,0.11206 +7581,300168.9453832,0,0.088456 +7582,280632.1410132,0,0.072267 +7583,267256.8376671,0,0.054372 +7584,244360.0537127,0,0.036851 +7585,227366.310814,0,0.024068 +7586,216958.6817438,0,0.016299 +7587,212163.3262653,0,0.010759 +7588,211720.2520344,0,0.007482 +7589,215910.9958019,0,0.0059216 +7590,215717.1508259,0,0 +7591,219326.3596653,0,0 +7592,231820.1299061,0,0 +7593,249778.4823284,0.047003,0 +7594,259004.5801161,0.15367,0 +7595,264164.5487638,0.25522,0.0067406 +7596,270755.2779488,0.32292,0.012671 +7597,264870.6983193,0.34819,0.019112 +7598,256812.2857444,0.27297,0.020785 +7599,249543.0991432,0.17204,0.016381 +7600,248800.0267351,0.068978,0.015863 +7601,256309.211878,0.00046681,0.019425 +7602,276842.9332675,0,0.026715 +7603,279132.1501273,0,0.038972 +7604,267021.454482,0,0.056783 +7605,247272.3437097,0,0.076137 +7606,237483.1724202,0,0.097573 +7607,235078.5716461,0,0.1039 +7608,214757.1566589,0,0.077531 +7609,199955.708132,0,0.058379 +7610,189460.3872869,0,0.04657 +7611,188569.6234685,0,0.031648 +7612,192372.6772839,0,0.015906 +7613,196318.8071531,0,0.007778 +7614,197375.7238082,0,0 +7615,193826.5146042,0,0 +7616,198312.6411923,0,0.0083135 +7617,205004.9082219,0.050627,0.014611 +7618,211161.7938892,0.18552,0.021429 +7619,216870.9899689,0.2711,0.037515 +7620,224292.483337,0.30042,0.091248 +7621,224832.4800559,0.28046,0.17521 +7622,221329.4244176,0.21201,0.24208 +7623,217138.6806501,0.12601,0.22843 +7624,217890.9837714,0.045278,0.28186 +7625,231460.1320935,0,0.41824 +7626,252944.6169369,0,0.54979 +7627,260255.3417472,0,0.66832 +7628,255764.5998025,0,0.75763 +7629,244780.0511607,0,0.82701 +7630,236287.795068,0,0.87094 +7631,231981.6673862,0,0.89366 +7632,217027.9120924,0,0.92368 +7633,206892.5890599,0,0.94309 +7634,206121.8245124,0,0.95459 +7635,203712.6083817,0,0.96024 +7636,207940.2750018,0,0.9574 +7637,218246.3662274,0,0.95237 +7638,232410.8955474,0,0.94374 +7639,268941.442816,0,0.92612 +7640,297690.4989039,0,0.8909 +7641,303805.846362,0.020832,0.83251 +7642,302615.0843664,0.037444,0.71677 +7643,306851.9816996,0.073519,0.49351 +7644,312542.7163531,0.10368,0.29391 +7645,312464.2552914,0.12042,0.16569 +7646,313784.247271,0.090903,0.18652 +7647,309621.1956429,0.05378,0.19178 +7648,309127.3524897,0.018392,0.16891 +7649,313936.5540379,0,0.18545 +7650,327279.549888,0,0.1793 +7651,324274.9527595,0,0.15862 +7652,315431.9295673,0,0.13861 +7653,296905.8882867,0,0.11533 +7654,282870.5889507,0,0.099148 +7655,268309.1389656,0,0.11977 +7656,247507.7268949,0,0.16011 +7657,232503.2026788,0,0.1904 +7658,228450.9196085,0,0.20389 +7659,227301.695822,0,0.2273 +7660,230629.3679105,0,0.25911 +7661,237926.2466511,0,0.28794 +7662,249686.1751969,0,0.29645 +7663,279062.9197787,0,0.28993 +7664,306381.2153293,0,0.25955 +7665,315870.3884416,0.023242,0.21107 +7666,320162.6700537,0.061126,0.16594 +7667,324473.4130921,0.10684,0.18757 +7668,328105.6987144,0.13757,0.32509 +7669,324865.7184008,0.14793,0.66374 +7670,325054.9480202,0.1438,0.78353 +7671,322761.1158039,0.1125,0.72016 +7672,322890.3457879,0.055421,0.58837 +7673,326407.2474959,0,0.58216 +7674,337327.1811456,0,0.60564 +7675,333164.1295175,0,0.56515 +7676,323130.3443296,0,0.45901 +7677,305864.2953932,0,0.31388 +7678,291339.7682606,0,0.21398 +7679,276949.0864687,0,0.19483 +7680,252796.9255266,0,0.25634 +7681,238258.5523243,0,0.3788 +7682,236509.3321834,0,0.50461 +7683,234898.5727398,0,0.60116 +7684,236292.4104246,0,0.65719 +7685,241180.0730345,0,0.6617 +7686,251860.0081424,0,0.63173 +7687,279205.9958324,0,0.58241 +7688,307096.595598,0,0.54461 +7689,316562.6919275,0.038259,0.50507 +7690,318759.6016558,0.16628,0.42436 +7691,321911.8901946,0.25531,0.38899 +7692,327727.2394755,0.29065,0.43583 +7693,326379.5553564,0.27685,0.42152 +7694,324690.334851,0.20742,0.38402 +7695,321219.5867088,0.12056,0.2974 +7696,319244.2140959,0.040371,0.28122 +7697,321653.4302265,0,0.29091 +7698,332656.4402946,0,0.27242 +7699,328885.6939751,0,0.25049 +7700,318667.2945244,0,0.24083 +7701,301507.3987891,0,0.2499 +7702,286710.5656188,0,0.27007 +7703,275181.4049015,0,0.26059 +7704,251947.6999173,0,0.22085 +7705,237995.4769997,0,0.17921 +7706,230758.5978945,0,0.15046 +7707,228266.3053456,0,0.1279 +7708,231690.8999221,0,0.12033 +7709,237446.2495676,0,0.11717 +7710,251749.2395847,0,0.11279 +7711,282598.282913,0,0.1071 +7712,310368.8834076,0,0.092253 +7713,318874.9855701,0.03933,0.067325 +7714,319507.2894205,0.20166,0.030568 +7715,323601.1107,0.2998,0.015449 +7716,327593.3941349,0.32646,0.014206 +7717,325479.5608249,0.29408,0.014963 +7718,323684.1871183,0.2288,0.015537 +7719,320642.6671372,0.14042,0.018204 +7720,319894.9793725,0.051051,0.022576 +7721,323056.4986245,0,0.020685 +7722,334604.120768,0,0.015092 +7723,331668.7539881,0,0.010463 +7724,322156.5040929,0,0.0073769 +7725,305024.3004971,0,0.0060544 +7726,290555.1576434,0,0.0060082 +7727,278901.3822987,0,0.0066061 +7728,255736.907663,0,0.0075302 +7729,240944.6898493,0,0.007843 +7730,233204.7368778,0,0.0074137 +7731,229553.9898292,0,0.00622 +7732,232129.3587965,0,0 +7733,239333.9304056,0,0 +7734,251527.7024692,0,0 +7735,280341.3735492,0,0 +7736,308305.8190199,0,0.011058 +7737,316682.6911983,0.027427,0.025193 +7738,317721.1464271,0.12878,0.03538 +7739,321630.3534437,0.19117,0.04198 +7740,323836.4938852,0.20283,0.052796 +7741,320056.5168526,0.17388,0.085689 +7742,314545.7811054,0.13137,0.12859 +7743,308471.9718565,0.076757,0.17747 +7744,306856.5970562,0.025015,0.22299 +7745,313756.5551316,0,0.24355 +7746,328022.6222961,0,0.24735 +7747,324325.7216818,0,0.23665 +7748,311942.7199987,0,0.2292 +7749,294381.2882417,0,0.23829 +7750,279492.1479399,0,0.25919 +7751,270132.2048116,0,0.30191 +7752,249270.7931055,0,0.37196 +7753,233186.2754515,0,0.43119 +7754,226470.931639,0,0.46603 +7755,221338.6551308,0,0.50714 +7756,220923.2730393,0,0.55784 +7757,226692.4687545,0,0.60256 +7758,225234.0160777,0,0.62107 +7759,225760.1667269,0,0.61001 +7760,237192.4049561,0,0.57285 +7761,251961.545987,0.01948,0.51598 +7762,262563.0200333,0.09583,0.43147 +7763,267935.2950832,0.15422,0.39074 +7764,270750.6625923,0.1776,0.37015 +7765,266010.6913927,0.1683,0.34097 +7766,258699.9665824,0.12741,0.3191 +7767,252695.387682,0.074284,0.3205 +7768,254481.5306754,0.023745,0.33644 +7769,264861.4676062,0,0.29899 +7770,284153.6580778,0,0.25277 +7771,284005.9666675,0,0.25018 +7772,273967.566123,0,0.28983 +7773,255903.0604996,0,0.32357 +7774,245629.27677,0,0.32736 +7775,243321.5984839,0,0.39375 +7776,226097.0877567,0,0.51635 +7777,207584.8925458,0,0.6037 +7778,199891.09314,0,0.65109 +7779,196351.1146491,0,0.68447 +7780,199535.7106839,0,0.71289 +7781,200292.6291618,0,0.70993 +7782,197135.7252664,0,0.61421 +7783,193161.9032578,0,0.3768 +7784,197652.6452025,0,0.20016 +7785,208872.5770294,0.027595,0.19562 +7786,219437.1282231,0.17956,0.21927 +7787,230763.2132511,0.26103,0.15996 +7788,244364.6690693,0.26385,0.098825 +7789,242809.2939044,0.20842,0.07216 +7790,234501.6520746,0.18263,0.067264 +7791,228921.6859788,0.12768,0.064038 +7792,228044.7682301,0.052791,0.083429 +7793,238858.5486787,0,0.11749 +7794,258709.1972955,0,0.13584 +7795,263855.3198735,0,0.1463 +7796,258252.2769949,0,0.13697 +7797,247733.8793669,0,0.10851 +7798,242680.0639204,0,0.076755 +7799,241895.4533032,0,0.051126 +7800,225021.7096754,0,0.030857 +7801,214923.3094955,0,0.01769 +7802,210603.3357439,0,0.011139 +7803,210077.1850947,0,0.0077216 +7804,213912.5464062,0,0 +7805,223526.334146,0,0 +7806,238000.0923563,0,0 +7807,275213.7123975,0,0 +7808,309219.6596212,0,0 +7809,321450.3545374,0.014463,0.0073912 +7810,324141.1074189,0.071726,0.0087027 +7811,330911.8355103,0.11379,0.010683 +7812,336787.1844266,0.12486,0.011904 +7813,336307.1873431,0.10997,0.011984 +7814,336237.9569945,0.081595,0.011772 +7815,332997.9766809,0.045727,0.011963 +7816,332065.6746533,0.012883,0.013567 +7817,333597.9730353,0,0.013044 +7818,340419.4700489,0,0.010439 +7819,335102.5792778,0,0.0072846 +7820,326005.7114741,0,0 +7821,308808.8928862,0,0 +7822,295470.5123927,0,0 +7823,285681.3411032,0,0 +7824,262669.1732344,0,0.0057949 +7825,246538.5020147,0,0.0060626 +7826,241581.6090562,0,0.0063338 +7827,243658.5195137,0,0.0066279 +7828,245001.5882762,0,0.0063766 +7829,250627.7079377,0,0 +7830,259101.5026041,0,0 +7831,292216.6860093,0,0 +7832,322461.1176267,0,0 +7833,330768.7594565,0.016257,0.0059149 +7834,332291.8271254,0.11694,0.0060718 +7835,335716.4217019,0.17788,0 +7836,342796.3786836,0.18443,0 +7837,341822.5384469,0.14946,0 +7838,340451.7775449,0.11944,0 +7839,338319.4828086,0.074198,0 +7840,336450.2633969,0.025226,0 +7841,338688.7113344,0,0 +7842,348524.0361896,0,0 +7843,344933.2887765,0,0.0066009 +7844,333791.8180113,0,0.009599 +7845,316627.3069195,0,0.014829 +7846,303533.5403243,0,0.021508 +7847,292064.3792425,0,0.03059 +7848,272149.1156336,0,0.041993 +7849,254495.3767451,0,0.049123 +7850,246732.3469908,0,0.056841 +7851,243123.1381513,0,0.067253 +7852,244872.3582922,0,0.077522 +7853,246963.1148194,0,0.087428 +7854,258399.9684052,0,0.10051 +7855,290827.4636811,0,0.12600 +7856,320610.3596412,0,0.17113 +7857,329591.8435306,0.010635,0.25388 +7858,331539.5240041,0.067343,0.39865 +7859,336261.0337774,0.099965,0.5827 +7860,340585.6228855,0.096092,0.75296 +7861,338014.8692748,0.06688,0.85832 +7862,335393.3467418,0.049641,0.92652 +7863,331530.2932909,0.027287,0.96096 +7864,329545.6899649,0.0064174,0.97439 +7865,333754.8951587,0,0.97821 +7866,345551.7465572,0,0.9803 +7867,341804.0770206,0,0.97664 +7868,333205.6677266,0,0.96928 +7869,316594.9994235,0,0.92131 +7870,302153.5487092,0,0.82248 +7871,290130.5448387,0,0.73389 +7872,267902.9875872,0,0.70946 +7873,250876.9371926,0,0.76528 +7874,244470.8222704,0,0.82001 +7875,242412.3732392,0,0.8215 +7876,242458.526805,0,0.78179 +7877,246210.8116981,0,0.69671 +7878,255386.1405636,0,0.64885 +7879,286825.9495331,0,0.6987 +7880,317568.8396602,0,0.80936 +7881,325599.5600957,0.010624,0.90998 +7882,324939.5641059,0.077147,0.96248 +7883,328013.391583,0.13493,0.98563 +7884,331931.8293127,0.16081,0.99447 +7885,331341.0636715,0.15572,0.99718 +7886,330482.6073491,0.11786,0.99789 +7887,328830.3096962,0.067472,0.99808 +7888,329716.4581581,0.018977,0.99746 +7889,335231.8092618,0,0.99678 +7890,344504.0606153,0,0.99581 +7891,340802.5446444,0,0.99419 +7892,332628.7481551,0,0.99088 +7893,316235.0016108,0,0.98571 +7894,302130.4719263,0,0.97962 +7895,290518.2347908,0,0.97197 +7896,269162.9799314,0,0.96025 +7897,254218.4553508,0,0.94354 +7898,247221.5747874,0,0.92523 +7899,244498.5144098,0,0.91414 +7900,245523.1235689,0,0.9003 +7901,251126.1664475,0,0.88159 +7902,260513.8017152,0,0.86751 +7903,288529.0161082,0,0.86149 +7904,316996.5354452,0,0.85873 +7905,323688.8024749,0,0.8357 +7906,324454.9516658,0.019561,0.8052 +7907,327044.1667028,0.026911,0.80236 +7908,329245.6917877,0.019225,0.79896 +7909,327764.1623281,0.0037827,0.77092 +7910,322096.5044575,0.0021476,0.71263 +7911,318422.680626,4.0037e-05,0.65242 +7912,316784.2290429,0,0.59423 +7913,324261.1066898,0,0.49993 +7914,335453.3463773,0,0.40127 +7915,330745.6826737,0,0.34087 +7916,319862.6718765,0,0.28688 +7917,303593.5399597,0,0.23582 +7918,291159.7693543,0,0.19523 +7919,284121.3505818,0,0.15123 +7920,266661.4566693,0,0.10992 +7921,251306.1653538,0,0.07997 +7922,241775.4540323,0,0.064241 +7923,236393.9482691,0,0.054622 +7924,237340.0963664,0,0.05212 +7925,237732.4016751,0,0.050423 +7926,235046.2641501,0,0.045506 +7927,231713.976705,0,0.039406 +7928,245347.7400191,0,0.035897 +7929,259692.2682454,0.011107,0.032796 +7930,272712.1891354,0.12438,0.021789 +7931,277424.4681956,0.19337,0.019175 +7932,278509.07699,0.19448,0.020287 +7933,274775.2535232,0.14664,0.021257 +7934,268313.7543222,0.12779,0.020308 +7935,264635.3151342,0.086609,0.017426 +7936,265867.6153389,0.030703,0.016154 +7937,276801.3950584,0,0.013611 +7938,293375.140509,0,0.0093208 +7939,292045.9178162,0,0 +7940,283142.8949885,0,0 +7941,266712.2255916,0,0 +7942,258049.2013057,0,0 +7943,258524.5830326,0,0.007987 +7944,245850.8138855,0,0.0099243 +7945,234224.7306802,0,0.011191 +7946,223563.2569986,0,0.011182 +7947,218144.8283828,0,0.01157 +7948,218144.8283828,0,0.012754 +7949,218754.0554504,0,0.01487 +7950,213566.3946633,0,0.017695 +7951,206352.592341,0,0.021531 +7952,211946.4045064,0,0.026422 +7953,222870.9535127,0.011801,0.030849 +7954,235383.1851798,0.1466,0.028445 +7955,245661.584266,0.20772,0.02574 +7956,255713.8308802,0.17364,0.025176 +7957,254998.4506115,0.081886,0.025997 +7958,247812.3404287,0.07845,0.028631 +7959,243792.3648543,0.057578,0.032876 +7960,242347.7582472,0.020516,0.03873 +7961,253507.6904387,0,0.044329 +7962,273358.3390555,0,0.047846 +7963,279072.1504918,0,0.052992 +7964,274742.9460272,0,0.060821 +7965,266647.6105996,0,0.073414 +7966,262710.7114436,0,0.089588 +7967,263947.6270049,0,0.1038 +7968,251352.3189195,0,0.11594 +7969,243515.44346,0,0.12443 +7970,237880.0930854,0,0.12431 +7971,237317.0195836,0,0.11772 +7972,237100.0978247,0,0.11024 +7973,242075.4522095,0,0.097007 +7974,254209.2246377,0,0.085635 +7975,289502.8563449,0,0.082102 +7976,323134.9596862,0,0.082893 +7977,333713.3569496,0.0080064,0.084783 +7978,337821.0242988,0.13956,0.09261 +7979,344153.2935158,0.21215,0.1103 +7980,348860.9572194,0.19758,0.13827 +7981,348948.6489943,0.12487,0.17073 +7982,347568.6573792,0.12919,0.20838 +7983,343474.8360997,0.10148,0.25435 +7984,340904.082489,0.039499,0.30183 +7985,345833.2833081,0,0.34158 +7986,354630.1529346,0,0.36027 +7987,350010.1810059,0,0.35209 +7988,343299.4525499,0,0.33037 +7989,326813.3988742,0,0.31157 +7990,312819.6377474,0,0.30201 +7991,302499.7004521,0,0.30017 +7992,284896.7304859,0,0.3125 +7993,273021.4180258,0,0.3415 +7994,265922.9996178,0,0.37221 +7995,259433.8082773,0,0.38751 +7996,257130.7453478,0,0.39286 +7997,258667.6590864,0,0.40606 +7998,265743.0007115,0,0.4313 +7999,295078.2070841,0,0.46142 +8000,324459.5670224,0,0.49577 +8001,330473.3766359,0.0036056,0.50185 +8002,331576.4468567,0.13498,0.49967 +8003,334701.043256,0.23329,0.46497 +8004,339427.1683859,0.25983,0.44339 +8005,336741.0308609,0.22726,0.45159 +8006,335859.4977556,0.19394,0.45435 +8007,332614.9020854,0.12818,0.45421 +8008,331996.4443047,0.042056,0.46421 +8009,339708.7051368,0,0.4385 +8010,351533.2486747,0,0.40454 +8011,349054.8021954,0,0.39603 +8012,340830.2367838,0,0.40676 +8013,326434.9396353,0,0.4199 +8014,312584.2545623,0,0.4137 +8015,304262.7666627,0,0.40393 +8016,284795.1926413,0,0.38801 +8017,277821.3888608,0,0.36456 +8018,273275.2626372,0,0.35471 +8019,268590.6757165,0,0.36576 +8020,268369.138601,0,0.38453 +8021,272430.6523845,0,0.42536 +8022,281762.9033734,0,0.50611 +8023,310599.6512362,0,0.56834 +8024,341328.6952936,0,0.61321 +8025,347536.3498832,0.0033985,0.62518 +8026,347776.3484249,0.13346,0.54882 +8027,349280.9546675,0.2409,0.48236 +8028,354514.7690203,0.28083,0.45483 +8029,355645.5313805,0.26093,0.49288 +8030,356323.9887966,0.19698,0.53738 +8031,352516.3196245,0.11128,0.56494 +8032,353074.7777698,0.029046,0.62102 +8033,359993.1972714,0,0.68376 +8034,368693.1444099,0,0.73717 +8035,364700.860975,0,0.7811 +8036,356605.5255475,0,0.81278 +8037,344610.2138164,0,0.82962 +8038,331253.3718966,0,0.83008 +8039,322821.1154393,0,0.82233 +8040,309478.1195892,0,0.81405 +8041,299084.3365887,0,0.79968 +8042,288302.8636362,0,0.78528 +8043,279949.0682406,0,0.77771 +8044,272993.7258863,0,0.77114 +8045,273589.1068841,0,0.77721 +8046,279898.2993183,0,0.79497 +8047,304539.688057,0,0.82056 +8048,330925.68158,0,0.84792 +8049,336948.7219067,0.00052568,0.8584 +8050,337954.8696394,0.10336,0.86231 +8051,342191.7669726,0.18183,0.88824 +8052,345556.3619137,0.20034,0.9097 +8053,344739.4438005,0.1706,0.92003 +8054,343493.297526,0.13212,0.93626 +8055,338702.5574041,0.076778,0.94444 +8056,337308.7197193,0.020276,0.96034 +8057,342842.5322493,0,0.96791 +8058,353227.0845366,0,0.9754 +8059,349479.4150001,0,0.97623 +8060,343280.9911237,0,0.97531 +8061,330154.9170324,0,0.97093 +8062,318625.7563152,0,0.95652 +8063,310793.4962123,0,0.93182 +8064,296093.58553,0,0.90336 +8065,282496.7450684,0,0.88086 +8066,278942.9205078,0,0.84273 +8067,272910.649468,0,0.8033 +8068,270436.8183454,0,0.7856 +8069,269698.3612938,0,0.80507 +8070,277973.6956277,0,0.8261 +8071,301728.9359046,0,0.84532 +8072,329642.6124529,0,0.84985 +8073,336593.3394506,0.00097525,0.83377 +8074,337728.7171674,0.12306,0.75526 +8075,341407.1563554,0.19446,0.67531 +8076,343488.6821694,0.17767,0.66469 +8077,341421.0024251,0.10342,0.72275 +8078,334391.8143657,0.10119,0.76779 +8079,328082.6219316,0.075116,0.82198 +8080,325507.2529643,0.026409,0.86053 +8081,334174.8926068,0,0.88489 +8082,344910.2119936,0,0.89289 +8083,341933.3070046,0,0.9113 +8084,332734.9013563,0,0.92161 +8085,316516.5383617,0,0.92717 +8086,306833.5202734,0,0.91685 +8087,303732.0006569,0,0.8958 +8088,287675.1751423,0,0.86971 +8089,275453.7109393,0,0.85518 +8090,265793.7696338,0,0.83617 +8091,257772.2799114,0,0.82133 +8092,255021.5273944,0,0.78416 +8093,254735.3752869,0,0.74701 +8094,251823.0852899,0,0.6949 +8095,248846.1803008,0,0.64167 +8096,260832.2613187,0,0.58845 +8097,274530.6396249,0,0.5426 +8098,287564.4065846,0.059144,0.50249 +8099,293398.2172918,0.10956,0.46224 +8100,296799.7350855,0.12196,0.44416 +8101,294681.2864189,0.10297,0.41392 +8102,285999.8007067,0.097251,0.34969 +8103,280373.6810452,0.069838,0.27613 +8104,278675.2298266,0.022525,0.31882 +8105,291348.9989738,0,0.34842 +8106,307867.3601455,0,0.24986 +8107,304655.0719713,0,0.18438 +8108,296296.6612191,0,0.17509 +8109,280821.3706327,0,0.16955 +8110,274599.8699734,0,0.11002 +8111,270584.5097557,0,0.057014 +8112,260615.3395598,0,0.033664 +8113,248186.184311,0,0.022829 +8114,239255.4693439,0,0.018088 +8115,235364.7237536,0,0.020872 +8116,231437.0553107,0,0.037791 +8117,231584.746721,0,0.083482 +8118,230781.6746774,0,0.1603 +8119,223877.1012455,0,0.23608 +8120,228801.686708,0,0.30605 +8121,242033.9140003,0,0.33444 +8122,253189.2308352,0.09368,0.31997 +8123,262443.0207624,0.16013,0.29932 +8124,275449.0955827,0.1576,0.29989 +8125,277133.7007315,0.10661,0.3154 +8126,269998.359471,0.083665,0.33897 +8127,262207.6375772,0.048703,0.36475 +8128,260338.4181655,0.011439,0.41086 +8129,269739.899503,0,0.47271 +8130,282399.8225804,0,0.49402 +8131,284061.3509463,0,0.48956 +8132,279459.8404439,0,0.49864 +8133,269453.7473955,0,0.50909 +8134,264787.621901,0,0.50362 +8135,267482.9901392,0,0.46469 +8136,253392.3065244,0,0.41326 +8137,241203.1498173,0,0.36495 +8138,234483.1906483,0,0.31918 +8139,234418.5756563,0,0.2873 +8140,233352.4282881,0,0.25332 +8141,239541.6214514,0,0.22242 +8142,252160.0063196,0,0.2053 +8143,284772.1158585,0,0.19775 +8144,316258.0783937,0,0.19398 +8145,326554.9389062,0,0.18361 +8146,328304.159047,0.030811,0.15514 +8147,332951.8231152,0.067799,0.12922 +8148,337756.4093068,0.087308,0.11913 +8149,337396.4114942,0.087714,0.10614 +8150,335351.8085327,0.085487,0.096649 +8151,331271.8333229,0.062511,0.098739 +8152,329061.0775248,0.019959,0.11999 +8153,333819.5101507,0,0.14659 +8154,343802.5264163,0,0.16962 +8155,340354.8550569,0,0.18788 +8156,332868.7466969,0,0.20689 +8157,318034.990674,0,0.2216 +8158,306542.7528093,0,0.24599 +8159,300081.2536083,0,0.28002 +8160,280115.2210772,0,0.30938 +8161,266084.5370978,0,0.31841 +8162,258543.0444589,0,0.30781 +8163,255916.9065694,0,0.27332 +8164,254393.8389005,0,0.23136 +8165,257790.7413376,0,0.19293 +8166,265927.6149744,0,0.18062 +8167,292807.4516506,0,0.18969 +8168,323361.1121582,0,0.20748 +8169,332019.5210876,0,0.22176 +8170,331225.6797572,0.079386,0.23053 +8171,334364.1222263,0.14349,0.2284 +8172,338051.7921274,0.14686,0.23748 +8173,337530.2568347,0.10562,0.27797 +8174,335776.4213373,0.10757,0.38313 +8175,333150.2834478,0.081825,0.51821 +8176,331761.0611196,0.02757,0.62416 +8177,339625.6287185,0,0.69987 +8178,346668.6628476,0,0.71555 +8179,342251.7666081,0,0.67809 +8180,333944.1247782,0,0.60771 +8181,319262.6755222,0,0.52072 +8182,306727.3670722,0,0.44898 +8183,296919.7343564,0,0.41319 +8184,277761.3892254,0,0.41555 +8185,262696.8653738,0,0.39683 +8186,257379.9746027,0,0.31051 +8187,253152.3079826,0,0.18674 +8188,252418.4662877,0,0.090655 +8189,255755.3690893,0,0.041257 +8190,260906.1070239,0,0.021271 +8191,290822.8483246,0,0.013157 +8192,323019.5757719,0,0.0081927 +8193,332102.5975059,0,0 +8194,332757.9781391,0.066738,0 +8195,336777.9537135,0.13305,0 +8196,341402.5409988,0.15321,0.0072004 +8197,339104.0934259,0.13379,0.018584 +8198,337834.8703685,0.11755,0.038286 +8199,334927.1957281,0.078204,0.06655 +8200,335430.2695944,0.022828,0.088172 +8201,341213.3113793,0,0.10526 +8202,348810.1882971,0,0.11572 +8203,345325.5940851,0,0.11247 +8204,336930.2604804,0,0.11155 +8205,321085.7413682,0,0.11249 +8206,309681.1952784,0,0.10898 +8207,300205.8682357,0,0.10284 +8208,280378.2964018,0,0.080356 +8209,267012.2237688,0,0.071244 +8210,258935.3497675,0,0.06793 +8211,256161.5204677,0,0.067847 +8212,256881.5160929,0,0.078703 +8213,260730.7234741,0,0.085748 +8214,267515.2976352,0,0.079293 +8215,293093.6037581,0,0.068871 +8216,322142.6580232,0,0.046356 +8217,329919.5338473,0,0.026323 +8218,331774.9071893,0.10305,0.015907 +8219,337581.025757,0.18986,0.009965 +8220,341970.2298572,0.18617,0.0067076 +8221,341005.6203336,0.12163,0 +8222,339533.3215871,0.12709,0 +8223,337987.1771354,0.09885,0 +8224,338342.5595914,0.034206,0 +8225,344028.6788883,0,0 +8226,354574.7686557,0,0 +8227,351099.4051569,0,0 +8228,341697.9238194,0,0 +8229,325142.6397951,0,0 +8230,314255.0136414,0,0 +8231,303192.0039379,0,0 +8232,283825.9677612,0,0 +8233,271562.965349,0,0 +8234,266486.0731196,0,0 +8235,260379.9563746,0,0 +8236,261607.6412228,0,0 +8237,264007.6266403,0,0 +8238,271382.9664426,0,0 +8239,296605.8901095,0,0 +8240,324242.6452635,0,0 +8241,331327.2176018,0,0.007438 +8242,331428.7554464,0.099454,0.0061466 +8243,334899.5035886,0.19564,0.0089112 +8244,336676.4158689,0.20945,0.015344 +8245,334276.4304514,0.16226,0.01505 +8246,328876.463262,0.15104,0.014668 +8247,322391.8872781,0.10677,0.022283 +8248,321658.0455831,0.033761,0.038833 +8249,331507.2165081,0,0.066114 +8250,341490.2327737,0,0.11141 +8251,335554.8842219,0,0.16316 +8252,325045.7173071,0,0.17698 +8253,308379.664725,0,0.18975 +8254,297076.6564798,0,0.21529 +8255,294432.057164,0,0.25129 +8256,277359.8532036,0,0.27591 +8257,260878.4148844,0,0.28686 +8258,258201.5080726,0,0.2932 +8259,253263.0765404,0,0.29166 +8260,250840.01434,0,0.30677 +8261,251933.8538476,0,0.32417 +8262,250627.7079377,0,0.35053 +8263,252132.3141802,0,0.37185 +8264,262027.6386709,0,0.39459 +8265,276233.7062,0,0.41365 +8266,291616.689655,0.096559,0.38971 +8267,300164.3300266,0.18129,0.35968 +8268,303579.69389,0.17505,0.32873 +8269,303427.3871231,0.10782,0.30458 +8270,297833.5749577,0.1242,0.27452 +8271,292581.2991785,0.10288,0.24231 +8272,291482.8443144,0.037202,0.23164 +8273,298299.7259714,0,0.22558 +8274,308804.2775297,0,0.22136 +8275,305688.9118435,0,0.22478 +8276,295572.0502373,0,0.2313 +8277,281167.5223756,0,0.24053 +8278,272670.6509263,0,0.23844 +8279,271636.8110541,0,0.22229 +8280,257970.740244,0,0.21162 +8281,244493.8990533,0,0.20521 +8282,235263.185909,0,0.18797 +8283,228187.8442839,0,0.15663 +8284,225589.3985337,0,0.14093 +8285,223955.5623072,0,0.1346 +8286,221661.7300908,0,0.13847 +8287,211540.2531281,0,0.18375 +8288,215209.461603,0,0.2831 +8289,223397.104162,0,0.35757 +8290,235027.8027238,0.083357,0.36613 +8291,248260.0300162,0.19606,0.38708 +8292,261995.3311749,0.25445,0.42924 +8293,264270.701965,0.25701,0.45161 +8294,254499.9921017,0.2173,0.48001 +8295,250023.0962267,0.13936,0.51183 +8296,249672.3291272,0.039956,0.53508 +8297,260809.1845358,0,0.54254 +8298,274987.5599255,0,0.51411 +8299,278975.2280038,0,0.43586 +8300,273626.0297367,0,0.38508 +8301,265027.6204428,0,0.33253 +8302,262401.4825532,0,0.28276 +8303,262784.5571487,0,0.26985 +8304,251560.0099652,0,0.29033 +8305,244092.3630315,0,0.30924 +8306,239010.8554456,0,0.31445 +8307,235817.0286976,0,0.30479 +8308,234847.8038175,0,0.28064 +8309,240409.3084869,0,0.2693 +8310,251693.8553058,0,0.26881 +8311,283235.2021199,0,0.25934 +8312,316281.1551766,0,0.23404 +8313,325890.3275598,0,0.20686 +8314,325548.7911734,0.058551,0.16024 +8315,328054.9297921,0.12548,0.11272 +8316,331322.6022452,0.13288,0.090949 +8317,330081.0713273,0.097724,0.094143 +8318,329333.3835626,0.076399,0.12366 +8319,325982.6346912,0.043563,0.19147 +8320,326785.7067348,0.0093516,0.28662 +8321,335199.5017658,0,0.37216 +8322,346050.2050669,0,0.37589 +8323,344365.5999181,0,0.37301 +8324,336588.724094,0,0.38838 +8325,321593.4305911,0,0.38039 +8326,308490.4332828,0,0.40618 +8327,301788.93554,0,0.48463 +8328,283272.1249725,0,0.56486 +8329,272762.9580577,0,0.59762 +8330,262498.4050412,0,0.60742 +8331,261427.6423165,0,0.62095 +8332,259590.7304008,0,0.65088 +8333,262738.403583,0,0.68141 +8334,269993.7441144,0,0.65111 +8335,296993.5800615,0,0.57887 +8336,324316.4909687,0,0.52419 +8337,332097.9821493,0,0.46465 +8338,331604.1389961,0.081367,0.3793 +8339,335859.4977556,0.18567,0.31833 +8340,340650.2378775,0.21691,0.30617 +8341,339597.9365791,0.18919,0.33287 +8342,340202.54829,0.15205,0.39625 +8343,337345.6425719,0.091099,0.47527 +8344,338647.1731252,0.023109,0.55659 +8345,345976.3593618,0,0.52793 +8346,356379.3730754,0,0.40704 +8347,353370.1605904,0,0.32727 +8348,346419.4335927,0,0.33454 +8349,334156.4311805,0,0.41079 +8350,322834.961509,0,0.48173 +8351,314735.0107249,0,0.52567 +8352,299670.4868734,0,0.61703 +8353,288865.937138,0,0.70487 +8354,282053.6708375,0,0.79568 +8355,275776.7858993,0,0.85669 +8356,271899.8863787,0,0.91244 +8357,272444.4984542,0,0.96212 +8358,278721.3833924,0,0.98272 +8359,302199.7022749,0,0.98974 +8360,328248.7747681,0,0.99081 +8361,335536.4227956,0,0.98934 +8362,334345.6608,0.041315,0.9872 +8363,338914.8638064,0.10027,0.98499 +8364,342464.0730104,0.1205,0.98475 +8365,339944.088322,0.1075,0.98589 +8366,337202.5665181,0.084865,0.98703 +8367,333565.6655393,0.049047,0.98807 +8368,333261.0520055,0.01097,0.98802 +8369,338601.0195595,0,0.98661 +8370,348976.3411337,0,0.98415 +8371,344900.9812805,0,0.97986 +8372,337479.4879125,0,0.97032 +8373,323693.4178314,0,0.9574 +8374,312713.4845463,0,0.95338 +8375,306551.9835225,0,0.95122 +8376,291228.9997029,0,0.9617 +8377,277946.0034882,0,0.97452 +8378,270819.8929408,0,0.98605 +8379,267510.6822786,0,0.99369 +8380,264718.3915525,0,0.99801 +8381,265124.5429308,0,0.99904 +8382,272379.8834622,0,0.99936 +8383,296993.5800615,0,0.99953 +8384,325405.7151197,0,0.99954 +8385,334613.3514811,0,0.99944 +8386,335974.8816699,0.076333,0.99886 +8387,341661.0009668,0.17734,0.99595 +8388,346696.354987,0.20539,0.98717 +8389,347767.1177118,0.17477,0.97613 +8390,347508.6577437,0.14827,0.97244 +8391,345953.2825789,0.094512,0.9701 +8392,346114.820059,0.026127,0.97113 +8393,351094.7898003,0,0.97317 +8394,356540.9105555,0,0.97288 +8395,351824.0161387,0,0.9709 +8396,341624.0781142,0,0.96789 +8397,325931.8657689,0,0.97246 +8398,314915.0096312,0,0.97984 +8399,307908.8983547,0,0.98777 +8400,290915.155456,0,0.99268 +8401,275435.249513,0,0.99318 +8402,266047.6142452,0,0.99152 +8403,258538.4291023,0,0.98875 +8404,256641.5175512,0,0.98738 +8405,258247.6616383,0,0.9867 +8406,265779.923564,0,0.98456 +8407,287033.6405788,0,0.98515 +8408,315478.083133,0,0.98158 +8409,323984.1852955,0,0.97237 +8410,326596.4771153,0.068082,0.96382 +8411,331604.1389961,0.16603,0.93585 +8412,334304.1225908,0.202000,0.88085 +8413,331359.5250978,0.18387,0.79912 +8414,324214.9531241,0.16329,0.68427 +8415,317790.3767756,0.10958,0.50212 +8416,315948.8495034,0.032692,0.44449 +8417,323578.0339171,0,0.4001 +8418,334853.3500229,0,0.32068 +8419,329148.7692997,0,0.22251 +8420,319318.059801,0,0.18902 +8421,304188.9209575,0,0.26989 +8422,294030.5211422,0,0.43885 +8423,291473.6136012,0,0.57869 +8424,278033.6952631,0,0.63614 +8425,265812.2310601,0,0.64614 +8426,257241.5139056,0,0.65047 +8427,248993.8717111,0,0.67782 +8428,244346.207643,0,0.71454 +8429,244387.7458521,0,0.72899 +8430,241115.4580425,0,0.71347 +8431,234649.3434849,0,0.68918 +8432,246630.8091462,0,0.67725 +8433,259590.7304008,0,0.65679 +8434,272841.4191194,0.075927,0.60486 +8435,281485.9819791,0.18826,0.5502 +8436,286202.8763958,0.23332,0.55007 +8437,285021.3451134,0.21828,0.57933 +8438,278158.3098906,0.18046,0.57305 +8439,271419.8892952,0.11206,0.54138 +8440,271978.3474404,0.03044,0.56434 +8441,284245.9652092,0,0.56857 +8442,301872.0119583,0,0.54473 +8443,298742.8002024,0,0.51946 +8444,291588.9975155,0,0.50566 +8445,275693.709481,0,0.50035 +8446,268530.6760811,0,0.48093 +8447,271899.8863787,0,0.50343 +8448,262923.0178459,0,0.55878 +8449,247835.4172115,0,0.61296 +8450,235203.1862735,0,0.64724 +8451,230218.6011756,0,0.69673 +8452,224541.7125919,0,0.75417 +8453,220729.4280633,0,0.82183 +8454,215287.9226647,0,0.88312 +8455,206864.8969205,0,0.9102 +8456,211946.4045064,0,0.88136 +8457,219446.3589362,0,0.74538 +8458,233703.1953876,0.045248,0.56611 +8459,246510.8098753,0.12007,0.48677 +8460,260356.8795918,0.14798,0.52763 +8461,260629.1856295,0.13591,0.61516 +8462,252086.1606145,0.13195,0.69984 +8463,245236.9714614,0.096281,0.72501 +8464,245130.8182602,0.031187,0.7271 +8465,254458.4538926,0,0.70614 +8466,272402.9602451,0,0.66242 +8467,275596.786993,0,0.62805 +8468,271050.6607694,0,0.61383 +8469,262683.0193041,0,0.63296 +8470,258981.5033333,0,0.70637 +8471,261026.1062947,0,0.76615 +8472,248680.0274642,0,0.78459 +8473,237450.8649242,0,0.76327 +8474,233855.5021545,0,0.71428 +8475,231713.976705,0,0.65872 +8476,231487.8242329,0,0.59742 +8477,241184.688391,0,0.57208 +8478,254523.0688846,0,0.5834 +8479,283890.5827532,0,0.62749 +8480,312335.0253074,0,0.68259 +8481,321279.5863442,0,0.7316 +8482,320693.4360595,0.024353,0.76954 +8483,323799.5710326,0.065545,0.77623 +8484,327621.0862743,0.077279,0.78101 +8485,327071.8588422,0.065167,0.79522 +8486,324801.1034087,0.051955,0.76809 +8487,319248.8294524,0.030084,0.69961 +8488,314402.7050517,0.0061621,0.6351 +8489,320038.0554263,0,0.55894 +8490,331742.5996933,0,0.46071 +8491,328285.6976207,0,0.36955 +8492,322147.2733798,0,0.3233 +8493,309307.351396,0,0.30155 +8494,298133.5731348,0,0.2447 +8495,290495.1580079,0,0.18324 +8496,273219.8783584,0,0.14263 +8497,261003.0295119,0,0.11275 +8498,252607.6959071,0,0.086652 +8499,248112.3386058,0,0.070419 +8500,245527.7389254,0,0.059804 +8501,246289.2727598,0,0.049613 +8502,253143.0772695,0,0.041911 +8503,277701.3895899,0,0.04214 +8504,307470.4394803,0,0.052593 +8505,316987.3047321,0,0.075912 +8506,317554.9935905,0.045748,0.11843 +8507,321118.0488642,0.11429,0.18032 +8508,324574.9509367,0.12678,0.22566 +8509,324487.2591618,0.097124,0.25322 +8510,321242.6634916,0.077854,0.29564 +8511,316825.7672521,0.045819,0.35254 +8512,315247.3153044,0.01058,0.44002 +8513,322179.5808758,0,0.56244 +8514,333034.8995335,0,0.68561 +8515,329577.9974609,0,0.76272 +8516,321464.2006071,0,0.80896 +8517,305273.529752,0,0.82841 +8518,294635.1328532,0,0.80856 +8519,286419.7981547,0,0.74978 +8520,264579.9308553,0,0.6535 +8521,249187.7166872,0,0.55135 +8522,242716.986773,0,0.42815 +8523,236370.8714863,0,0.32046 +8524,236287.795068,0,0.24233 +8525,238877.010105,0,0.18179 +8526,245209.279322,0,0.15812 +8527,268507.5992982,0,0.1505 +8528,298212.0341966,0,0.15184 +8529,310091.9620133,0,0.14103 +8530,311942.7199987,0.065277,0.11975 +8531,312515.0242137,0.16665,0.10668 +8532,315025.7781889,0.19416,0.11196 +8533,312155.026401,0.16285,0.12027 +8534,308148.8968964,0.13111,0.13383 +8535,305508.9129371,0.078325,0.15182 +8536,304096.6138261,0.019805,0.19315 +8537,313659.6326436,0,0.22711 +8538,324367.259891,0,0.25258 +8539,319853.4411634,0,0.24158 +8540,310530.4208877,0,0.2116 +8541,295922.8173368,0,0.17888 +8542,284121.3505818,0,0.15366 +8543,276949.0864687,0,0.15051 +8544,254818.4517052,0,0.18786 +8545,239587.7750171,0,0.26248 +8546,228289.3821284,0,0.32025 +8547,227094.0047763,0,0.32553 +8548,228104.7678656,0,0.31277 +8549,230933.9814443,0,0.28862 +8550,233860.117511,0,0.2638 +8551,247480.0347555,0,0.27388 +8552,275075.2517004,0,0.3123 +8553,291238.230416,0,0.34321 +8554,297321.2703781,0.05211,0.30959 +8555,300196.6375226,0.1361,0.28778 +8556,304313.535585,0.1603,0.3106 +8557,301392.0148748,0.13602,0.36836 +8558,294436.6725205,0.12119,0.45551 +8559,289059.782114,0.081521,0.56423 +8560,289138.2431757,0.024306,0.6716 +8561,299855.1011363,0,0.74986 +8562,310087.3466567,0,0.79456 +8563,307248.9023648,0,0.81097 +8564,297182.809681,0,0.81371 +8565,280087.5289377,0,0.8112 +8566,266393.7659881,0,0.80432 +8567,261492.2573085,0,0.77786 +8568,245915.4288775,0,0.74438 +8569,230001.6794167,0,0.68861 +8570,221592.4997422,0,0.6088 +8571,216714.0678455,0,0.53231 +8572,212269.4794665,0,0.48515 +8573,212024.8655682,0,0.45998 +8574,207409.508996,0,0.47821 +8575,201460.3143745,0,0.50572 +8576,212749.47655,0,0.53254 +8577,230675.5214762,0,0.59212 +8578,246783.1159131,0.063675,0.62789 +8579,257056.8996427,0.15777,0.62765 +8580,261889.1779737,0.17247,0.61365 +8581,261289.1816193,0.12804,0.65175 +8582,253253.8458272,0.12068,0.70579 +8583,245624.6614135,0.085959,0.7405 +8584,240953.9205624,0.027547,0.77856 +8585,246949.2687497,0,0.77204 +8586,256798.4396746,0,0.75867 +8587,248250.799303,0,0.71027 +8588,234220.1153237,0,0.65329 +8589,226009.3959818,0,0.57858 +8590,227047.8512105,0,0.47154 +8591,236698.5618029,0,0.36638 +8592,230066.2944087,0,0.27693 +8593,221361.7319136,0,0.21512 +8594,209421.8044615,0,0.18201 +8595,205124.9074928,0,0.16325 +8596,200274.1677355,0,0.14100 +8597,199964.9388451,0,0.12063 +8598,195958.8093405,0,0.10522 +8599,186778.8651185,0,0.097947 +8600,191375.7602644,0,0.10447 +8601,201081.8551356,0,0.1206 +8602,218130.9823131,0.063984,0.11017 +8603,232830.8929954,0.19387,0.071791 +8604,245329.2785928,0.2738,0.061561 +8605,238627.7808501,0.29569,0.077746 +8606,223540.1802157,0.22196,0.11697 +8607,216801.7596204,0.12099,0.20653 +8608,212500.2472951,0.027366,0.35287 +8609,219598.6657031,0,0.46273 +8610,236066.2579525,0,0.55602 +8611,242458.526805,0,0.62841 +8612,240423.1545566,0,0.65102 +8613,234824.7270346,0,0.63953 +8614,236232.4107891,0,0.61878 +8615,242689.2946336,0,0.60933 +8616,233989.3474951,0,0.58297 +8617,223120.1827676,0,0.53283 +8618,218214.0587314,0,0.46668 +8619,213386.395757,0,0.39982 +8620,205420.2903134,0,0.37805 +8621,200846.4719504,0,0.36777 +8622,197334.185599,0,0.35307 +8623,185385.0274337,0,0.34159 +8624,191191.1460015,0,0.3225 +8625,201340.3151036,0,0.2996 +8626,215574.0747722,0.064027,0.25823 +8627,227818.6157581,0.14662,0.19500 +8628,238964.7018798,0.13784,0.1925 +8629,236952.4064144,0.06818,0.29416 +8630,225727.8592309,0.070788,0.34528 +8631,219764.8185397,0.054534,0.3398 +8632,216044.8411425,0.018459,0.33731 +8633,223812.4862535,0,0.38681 +8634,239389.3146845,0,0.36608 +8635,243838.51842,0,0.31138 +8636,240589.3073932,0,0.25746 +8637,236287.795068,0,0.22313 +8638,236486.2554006,0,0.20664 +8639,241115.4580425,0,0.22518 +8640,229715.5273092,0,0.26415 +8641,219026.3614881,0,0.30766 +8642,209537.1883758,0,0.33184 +8643,207211.0486634,0,0.35929 +8644,205531.0588712,0,0.36813 +8645,207224.8947331,0,0.36421 +8646,208632.5784876,0,0.38852 +8647,216534.0689392,0,0.44478 +8648,237981.63093,0,0.51619 +8649,255252.295223,0,0.60332 +8650,270132.2048116,0.046612,0.68042 +8651,279842.9150394,0.12424,0.74825 +8652,286830.5648896,0.14631,0.80302 +8653,289733.6241735,0.12272,0.83733 +8654,285205.9593762,0.10011,0.86131 +8655,278864.4594461,0.061026,0.87735 +8656,275864.4776742,0.015996,0.90077 +8657,284015.1973806,0,0.91636 +8658,298392.0331029,0,0.91685 +8659,297067.4257667,0,0.91356 +8660,289955.161289,0,0.90915 +8661,276718.3186401,0,0.89818 +8662,268936.8274594,0,0.87597 +8663,266615.3031036,0,0.83612 +8664,250960.0136109,0,0.79475 +8665,236338.5639903,0,0.76672 +8666,230237.0626019,0,0.72886 +8667,224126.3305004,0,0.67771 +8668,219515.5892848,0,0.64271 +8669,220849.4273341,0,0.59687 +8670,223263.2588214,0,0.5557 +8671,228626.3031582,0,0.49997 +8672,245689.2764055,0,0.44273 +8673,260753.800257,0,0.37353 +8674,272666.0355697,0.037407,0.29692 +8675,280650.6024395,0.1009,0.29135 +8676,287564.4065846,0.11862,0.33269 +8677,288436.7089767,0.098787,0.35377 +8678,281832.133722,0.098858,0.33142 +8679,275241.404537,0.074687,0.28486 +8680,272989.1105298,0.027139,0.27703 +8681,281250.5987939,0,0.23802 +8682,295752.0491436,0,0.18858 +8683,294782.8242635,0,0.14537 +8684,286682.8734793,0,0.11542 +8685,272689.1123526,0,0.091385 +8686,265599.9246577,0,0.061062 +8687,263790.7048814,0,0.04769 +8688,251716.9320887,0,0.046672 +8689,241858.5304506,0,0.051768 +8690,233237.0443738,0,0.063255 +8691,229120.1463114,0,0.075377 +8692,224269.4065541,0,0.087094 +8693,225090.9400239,0,0.097178 +8694,225381.707488,0,0.10078 +8695,228358.612477,0,0.097611 +8696,246316.9648993,0,0.098354 +8697,261141.490209,0,0.09919 +8698,272324.4991834,0.064548,0.10126 +8699,279169.0729799,0.16974,0.15607 +8700,284135.1966515,0.20024,0.24481 +8701,285515.1882666,0.16953,0.35195 +8702,279755.2232645,0.14213,0.41438 +8703,273649.1065196,0.09029,0.47966 +8704,271175.2753969,0.027597,0.54822 +8705,279667.5314897,0,0.57122 +8706,295853.5869882,0,0.56937 +8707,294930.5156738,0,0.55682 +8708,286692.1041925,0,0.55564 +8709,272278.3456176,0,0.56069 +8710,264109.1644849,0,0.54781 +8711,264021.4727101,0,0.5256 +8712,249981.5580176,0,0.49985 +8713,237750.8631014,0,0.4802 +8714,230297.0622373,0,0.46002 +8715,227010.928358,0,0.42095 +8716,223586.3337814,0,0.39658 +8717,222787.8770944,0,0.35309 +8718,221444.8083319,0,0.28859 +8719,223535.5648591,0,0.25684 +8720,241198.5344608,0,0.27737 +8721,256313.8272346,0,0.33214 +8722,268041.4482844,0.064234,0.42658 +8723,274724.4846009,0.17387,0.54732 +8724,280124.4517903,0.2134,0.62683 +8725,280969.062043,0.19244,0.72635 +8726,274752.1767403,0.15511,0.80526 +8727,269638.3616584,0.094047,0.87389 +8728,266947.6087768,0.027137,0.93099 +8729,274927.5602901,0,0.95698 +8730,290979.770448,0,0.97053 +8731,290278.236249,0,0.97693 +8732,281855.2105049,0,0.97879 +8733,268110.678633,0,0.98094 +8734,258136.8930806,0,0.98302 +8735,254315.3778388,0,0.98307 +8736,240760.0755864,0,0.97995 +8737,228612.4570885,0,0.96968 +8738,219575.5889202,0,0.9363 +8739,213760.2396393,0,0.8243 +8740,209851.0326227,0,0.71243 +8741,209181.8059197,0,0.8524 +8742,207806.4296612,0,0.94555 +8743,201751.0818386,0,0.93294 +8744,206740.2822931,0,0.87216 +8745,217263.2952776,0,0.79377 +8746,232521.6641051,0.042628,0.77555 +8747,241309.3030185,0.11524,0.85377 +8748,248804.6420917,0.13709,0.93809 +8749,251430.7799812,0.1166,0.95159 +8750,245786.1988935,0.10202,0.94428 +8751,240104.6949532,0.068199,0.92285 +8752,237321.6349401,0.022248,0.90364 +8753,245200.0486088,0,0.8829 +8754,261053.7984342,0,0.86574 +8755,256599.979342,0,0.83926 +8756,243169.2917171,0,0.80387 +8757,228880.1477697,0,0.7667 +8758,226554.0080573,0,0.7523 +8759,236343.1793469,0,0.73739 +8760,235840.1054805,0,0.70744 diff --git a/examples/basic_example/basic_example.py b/examples/basic_example/basic_example.py new file mode 100644 index 000000000..95c2e2dcc --- /dev/null +++ b/examples/basic_example/basic_example.py @@ -0,0 +1,285 @@ +# -*- coding: utf-8 -*- + +""" +General description +------------------- + +A basic example to show how to model a simple energy system with oemof.solph. + +The following energy system is modeled: + +.. code-block:: text + + input/output bgas bel + | | | + | | | + wind(FixedSource) |------------------>| + | | | + pv(FixedSource) |------------------>| + | | | + rgas(Commodity) |--------->| | + | | | + demand(Sink) |<------------------| + | | | + | | | + pp_gas(Transformer) |<---------| | + |------------------>| + | | | + storage(Storage) |<------------------| + |------------------>| + + +Data +---- +basic_example.csv + + +Installation requirements +------------------------- + +This example requires oemof.solph (v0.5.x), install by: + + pip install oemof.solph[examples] + +""" + +__copyright__ = "oemof developer group" +__license__ = "GPLv3" + +# **************************************************************************** +# ********** PART 1 - Define and optimise the energy system ****************** +# **************************************************************************** + +############################################################################### +# imports +############################################################################### + +import logging +import os +import pprint as pp +import warnings + +import matplotlib.pyplot as plt +import pandas as pd +from oemof.tools import logger + +from oemof.solph import EnergySystem +from oemof.solph import Model +from oemof.solph import buses +from oemof.solph import components as cmp +from oemof.solph import flows +from oemof.solph import processing +from oemof.solph import views + + +# Read data file +filename = os.path.join(os.getcwd(), "basic_example.csv") +try: + data = pd.read_csv(filename) +except FileNotFoundError: + msg = "Data file not found: {0}. Only one value used!" + warnings.warn(msg.format(filename), ResourceWarning) + data = pd.DataFrame({"pv": [0.3], "wind": [0.6], "demand_el": [500]}) + +solver = "cbc" # 'glpk', 'gurobi',.... +debug = False # Set number_of_timesteps to 3 to get a readable lp-file. +number_of_time_steps = len(data) +solver_verbose = False # show/hide solver output + +# initiate the logger (see the API docs for more information) +logger.define_logging( + logfile="oemof_example.log", + screen_level=logging.INFO, + file_level=logging.INFO, +) + +logging.info("Initialize the energy system") +date_time_index = pd.date_range( + "1/1/2012", periods=number_of_time_steps, freq="H" +) + +energysystem = EnergySystem(timeindex=date_time_index) + +########################################################################## +# Create oemof object +########################################################################## + +logging.info("Create oemof objects") + +# The bus objects were assigned to variables which makes it easier to connect +# components to these buses (see below). + +# create natural gas bus +bgas = buses.Bus(label="natural_gas") + +# create electricity bus +bel = buses.Bus(label="electricity") + +# adding the buses to the energy system +energysystem.add(bgas, bel) + +# create excess component for the electricity bus to allow overproduction +energysystem.add(cmp.Sink(label="excess_bel", inputs={bel: flows.Flow()})) + +# create source object representing the natural gas commodity (annual limit) +energysystem.add( + cmp.Source( + label="rgas", + outputs={bgas: flows.Flow(summed_max=1)}, + ) +) + +# create fixed source object representing wind power plants +energysystem.add( + cmp.Source( + label="wind", + outputs={bel: flows.Flow(fix=data["wind"], nominal_value=1000000)}, + ) +) + +# create fixed source object representing pv power plants +energysystem.add( + cmp.Source( + label="pv", + outputs={bel: flows.Flow(fix=data["pv"], nominal_value=582000)}, + ) +) + +# create simple sink object representing the electrical demand +energysystem.add( + cmp.Sink( + label="demand", + inputs={bel: flows.Flow(fix=data["demand_el"], nominal_value=1)}, + ) +) + +# create simple transformer object representing a gas power plant +energysystem.add( + cmp.Transformer( + label="pp_gas", + inputs={bgas: flows.Flow()}, + outputs={bel: flows.Flow(nominal_value=10e10, variable_costs=50)}, + conversion_factors={bel: 0.58}, + ) +) + +# create storage object representing a battery +storage = cmp.GenericStorage( + nominal_storage_capacity=10077997, + label="storage", + inputs={bel: flows.Flow(nominal_value=10077997 / 6)}, + outputs={ + bel: flows.Flow(nominal_value=10077997 / 6, variable_costs=0.001) + }, + loss_rate=0.00, + initial_storage_level=None, + inflow_conversion_factor=1, + outflow_conversion_factor=0.8, +) + +energysystem.add(storage) + +########################################################################## +# Optimise the energy system and plot the results +########################################################################## + +logging.info("Optimise the energy system") + +# initialise the operational model +model = Model(energysystem) + +# This is for debugging only. It is not(!) necessary to solve the problem and +# should be set to False to save time and disc space in normal use. For +# debugging the timesteps should be set to 3, to increase the readability of +# the lp-file. +if debug: + filename = os.path.join( + solph.helpers.extend_basic_path("lp_files"), "basic_example.lp" + ) + logging.info("Store lp-file in {0}.".format(filename)) + model.write(filename, io_options={"symbolic_solver_labels": True}) + +# if tee_switch is true solver messages will be displayed +logging.info("Solve the optimization problem") +model.solve(solver=solver, solve_kwargs={"tee": solver_verbose}) + +logging.info("Store the energy system with the results.") + +# The processing module of the outputlib can be used to extract the results +# from the model transfer them into a homogeneous structured dictionary. + +# add results to the energy system to make it possible to store them. +energysystem.results["main"] = processing.results(model) +energysystem.results["meta"] = processing.meta_results(model) + +# The default path is the '.oemof' folder in your $HOME directory. +# The default filename is 'es_dump.oemof'. +# You can omit the attributes (as None is the default value) for testing cases. +# You should use unique names/folders for valuable results to avoid +# overwriting. + +# store energy system with results +energysystem.dump(dpath=None, filename=None) + +# **************************************************************************** +# ********** PART 2 - Processing the results ********************************* +# **************************************************************************** + +logging.info("**** The script can be divided into two parts here.") +logging.info("Restore the energy system and the results.") +energysystem = EnergySystem() +energysystem.restore(dpath=None, filename=None) + +# define an alias for shorter calls below (optional) +results = energysystem.results["main"] +storage = energysystem.groups["storage"] + +# print a time slice of the state of charge +print("") +print("********* State of Charge (slice) *********") +print( + results[(storage, None)]["sequences"][ + "2012-02-25 08:00:00":"2012-02-26 15:00:00" + ] +) +print("") + + + +# get all variables of a specific component/bus +custom_storage = views.node(results, "storage") +electricity_bus = views.node(results, "electricity") + +# plot the time series (sequences) of a specific component/bus +if plt is not None: + fig, ax = plt.subplots(figsize=(10, 5)) + custom_storage["sequences"].plot( + ax=ax, kind="line", drawstyle="steps-post" + ) + plt.legend( + loc="upper center", + prop={"size": 8}, + bbox_to_anchor=(0.5, 1.25), + ncol=2, + ) + fig.subplots_adjust(top=0.8) + plt.show() + + fig, ax = plt.subplots(figsize=(10, 5)) + electricity_bus["sequences"].plot( + ax=ax, kind="line", drawstyle="steps-post" + ) + plt.legend( + loc="upper center", prop={"size": 8}, bbox_to_anchor=(0.5, 1.3), ncol=2 + ) + fig.subplots_adjust(top=0.8) + plt.show() + +# print the solver results +print("********* Meta results *********") +pp.pprint(energysystem.results["meta"]) +print("") + +# print the sums of the flows around the electricity bus +print("********* Main results *********") +print(electricity_bus["sequences"].sum(axis=0)) diff --git a/examples/gradient_example/gradient_example.py b/examples/gradient_example/gradient_example.py new file mode 100644 index 000000000..cb5a6bd13 --- /dev/null +++ b/examples/gradient_example/gradient_example.py @@ -0,0 +1,142 @@ +""" +General description +------------------- +The gradient constraint can restrict a component to change the output within +one time step. In this example a storage will buffer this restriction, so the +more flexible the power plant can be run the less the storage will be used. + +Change the GRADIENT variable in the example to see the effect on the usage of +the storage. + + +Installation requirements +------------------------- + +This example requires oemof.solph (v0.5.x), install by: + + pip install oemof.solph[examples] +""" + +import matplotlib.pyplot as plt +import pandas as pd +from oemof.solph import EnergySystem, Model, buses +from oemof.solph import components as cmp +from oemof.solph import flows, processing + + +# The gradient for the output of the natural gas power plant. +# Change the gradient between 0.1 and 0.0001 and check the results. The +# more flexible the power plant can be run the less the storage will be used. +GRADIENT = 0.001 + +date_time_index = pd.date_range("1/1/2012", periods=48, freq="H") +print(date_time_index) +energysystem = EnergySystem(timeindex=date_time_index, timemode="explicit") + +demand = [ + 209643, 207497, 200108, 191892, 185717, 180672, 172683, 170048, 171132, + 179532, 189155, 201026, 208466, 207718, 205443, 206255, 217240, 232798, + 237321, 232387, 224306, 219280, 223701, 213926, 201834, 192215, 187152, + 184355, 184438, 182786, 180105, 191509, 207104, 222501, 231127, 238410, + 241184, 237413, 234469, 235193, 242730, 264196, 265950, 260283, 245578, + 238849, 241553, 231372 +] + +# create natural gas bus +bgas = buses.Bus(label="natural_gas") + +# create electricity bus +bel = buses.Bus(label="electricity") + +# adding the buses to the energy system +energysystem.add(bgas, bel) + +# create excess component for the electricity bus to allow overproduction +energysystem.add(cmp.Sink(label="excess_bel", inputs={bel: flows.Flow()})) + +# create source object representing the natural gas commodity (annual limit) +energysystem.add( + cmp.Source( + label="rgas", + outputs={bgas: flows.Flow(variable_costs=5)}, + ) +) + +# create simple sink object representing the electrical demand +energysystem.add( + cmp.Sink( + label="demand", + inputs={bel: flows.Flow(fix=demand, nominal_value=1)}, + ) +) + +# create simple transformer object representing a gas power plant +energysystem.add( + cmp.Transformer( + label="pp_gas", + inputs={bgas: flows.Flow()}, + outputs={ + bel: flows.Flow( + nominal_value=10e5, + negative_gradient={"ub": GRADIENT}, + positive_gradient={"ub": GRADIENT}, + ) + }, + conversion_factors={bel: 0.58}, + ) +) + +# create storage object representing a battery +storage = cmp.GenericStorage( + nominal_storage_capacity=999999999, + label="storage", + inputs={bel: flows.Flow()}, + outputs={bel: flows.Flow()}, + loss_rate=0.0, + initial_storage_level=None, + inflow_conversion_factor=1, + outflow_conversion_factor=0.8, +) + +energysystem.add(storage) + +# initialise the operational model +model = Model(energysystem) + +# solve +model.solve(solver="cbc") + +# processing the results +results = processing.results(model) + +print("C", results[(storage, None)]["sequences"]) +print("f", results[(storage, bel)]["sequences"]) +ax = results[(storage, None)]["sequences"].diff().plot() +ax = results[(storage, bel)]["sequences"].mul(-1).plot(ax=ax) +ax = results[(storage, None)]["sequences"].plot(ax=ax) +results[(bel, storage)]["sequences"].plot(ax=ax) +plt.show() +exit(0) +# get all variables of a specific component/bus +custom_storage = solph.views.node(results, "storage") +electricity_bus = solph.views.node(results, "electricity") + +# plotting +fig, ax = plt.subplots(figsize=(10, 5)) +custom_storage["sequences"].plot(ax=ax, kind="line", drawstyle="steps-post") +plt.legend( + loc="upper center", + prop={"size": 8}, + bbox_to_anchor=(0.5, 1.25), + ncol=2, +) +fig.subplots_adjust(top=0.8) +plt.show() + +fig, ax = plt.subplots(figsize=(10, 5)) +electricity_bus["sequences"].plot(ax=ax, kind="line", drawstyle="steps-post") +plt.legend( + loc="upper center", prop={"size": 8}, bbox_to_anchor=(0.5, 1.3), ncol=2 +) +fig.subplots_adjust(top=0.8) +plt.show() diff --git a/examples/time_step_example/time_index_example.py b/examples/time_index_example/time_index_example.py similarity index 97% rename from examples/time_step_example/time_index_example.py rename to examples/time_index_example/time_index_example.py index 91cf93a10..051c79543 100644 --- a/examples/time_step_example/time_index_example.py +++ b/examples/time_index_example/time_index_example.py @@ -21,7 +21,7 @@ Installation requirements ------------------------- -This example requires oemof.solph, install by: +This example requires oemof.solph (v0.5.x), install by: pip install oemof.solph[examples] """ diff --git a/examples/tuple_as_labels/tuple_as_label.csv b/examples/tuple_as_labels/tuple_as_label.csv new file mode 100644 index 000000000..20afccc6c --- /dev/null +++ b/examples/tuple_as_labels/tuple_as_label.csv @@ -0,0 +1,8761 @@ +timestep,demand_el,pv,wind +1,209643.3415769,0,0.10702 +2,207497.2007709,0,0.074873 +3,200108.0148989,0,0.055654 +4,191892.6802004,0,0.065059 +5,185717.3331069,0,0.085573 +6,180672.7483735,0,0.096229 +7,172683.5661471,0,0.11794 +8,170048.1975444,0,0.15953 +9,171132.8063389,0,0.23717 +10,179532.7553002,0.062188,0.28515 +11,189155.7737532,0.15426,0.35724 +12,201026.4708567,0.16016,0.46354 +13,208466.425651,0.1036,0.56932 +14,207718.7378864,0.084862,0.68999 +15,205443.3670963,0.052101,0.79517 +16,206255.669853,0.014673,0.86893 +17,217240.2184947,0,0.92278 +18,232798.5854994,0,0.95895 +19,237321.6349401,0,0.97963 +20,232387.8187645,0,0.99052 +21,224306.3294067,0,0.99626 +22,219280.2060996,0,0.99859 +23,223701.7176957,0,0.99933 +24,213926.3924759,0,0.99923 +25,201834.1582569,0,0.99874 +26,192215.7551605,0,0.99845 +27,187152.7090008,0,0.99779 +28,184355.8029181,0,0.99735 +29,184438.8793364,0,0.9969 +30,182786.5816836,0,0.99637 +31,180105.0595152,0,0.99481 +32,191509.6056049,0,0.99304 +33,207104.8954623,0,0.99142 +34,222501.724987,0.047766,0.99039 +35,231127.8264203,0.12973,0.98986 +36,238410.8590912,0.15324,0.98947 +37,241184.688391,0.12837,0.99026 +38,237413.9420716,0.10443,0.99193 +39,234469.3445786,0.063981,0.99271 +40,235193.9555604,0.019217,0.99281 +41,242730.8328427,0,0.99158 +42,264196.8562598,0,0.99079 +43,265950.6917572,0,0.99146 +44,260283.0338866,0,0.99194 +45,245578.5078477,0,0.99202 +46,238849.3179655,0,0.99239 +47,241553.9169168,0,0.99198 +48,231372.4403186,0,0.99105 +49,218135.5976697,0,0.98873 +50,208761.8084717,0,0.98524 +51,201492.6218705,0,0.98152 +52,194421.895602,0,0.98 +53,192118.8326725,0,0.97512 +54,187983.4731838,0,0.96149 +55,181517.3586262,0,0.94655 +56,185246.5667365,0,0.94065 +57,191874.2187741,0,0.93581 +58,202715.6913621,0.063357,0.9057 +59,213700.2400039,0.1452,0.87636 +60,225649.3981692,0.13022,0.85986 +61,229346.2987835,0.052762,0.87187 +62,223484.7959368,0.04271,0.88404 +63,219326.3596653,0.025526,0.90285 +64,218684.8251018,0.0066043,0.92233 +65,230273.9854545,0,0.94465 +66,249824.6358941,0,0.95549 +67,254873.835984,0,0.96371 +68,249547.7144998,0,0.9678 +69,239841.6196285,0,0.97427 +70,235807.7979845,0,0.97693 +71,240723.1527338,0,0.9763 +72,229821.6805104,0,0.97646 +73,217863.2916319,0,0.97775 +74,207418.7397092,0,0.9796 +75,201935.6961014,0,0.98278 +76,199406.4806999,0,0.98391 +77,201095.7012053,0,0.98546 +78,205706.4424209,0,0.98765 +79,222427.8792818,0,0.99015 +80,247180.0365783,0,0.99225 +81,262415.3286229,0,0.99371 +82,271932.1938747,0.01319,0.9935 +83,278416.7698586,0.039835,0.99284 +84,283821.3524046,0.047415,0.9932 +85,285838.2632266,0.038888,0.9939 +86,282782.8971759,0.031488,0.9946 +87,279990.6064497,0.01869,0.99482 +88,278126.0023946,0.0044696,0.99465 +89,283147.5103451,0,0.99386 +90,300847.4027993,0,0.99291 +91,299767.4093614,0,0.99168 +92,294953.5924566,0,0.98756 +93,281296.7523596,0,0.97564 +94,273252.1858544,0,0.95063 +95,269292.2099155,0,0.89537 +96,256387.6729397,0,0.77284 +97,245984.6592261,0,0.68626 +98,236135.4883011,0,0.58396 +99,229480.1441241,0,0.45925 +100,225792.4742229,0,0.41573 +101,227089.3894197,0,0.39466 +102,233721.6568139,0,0.44267 +103,252852.3098054,0,0.5154 +104,281499.8280488,0,0.55825 +105,295244.3599207,0,0.59368 +106,298996.6448138,0.038505,0.56689 +107,301895.0887412,0.10504,0.52071 +108,305864.2953932,0.12442,0.53577 +109,306865.8277694,0.10471,0.54674 +110,305222.7608297,0.085151,0.56219 +111,301142.7856199,0.052307,0.59634 +112,298585.8780789,0.016081,0.60329 +113,301673.5516257,0,0.61437 +114,315150.3928164,0,0.67381 +115,318348.8349209,0,0.74123 +116,313221.1737692,0,0.82476 +117,299015.1062401,0,0.89963 +118,285884.4167923,0,0.94793 +119,280784.4477801,0,0.96764 +120,262627.6350253,0,0.97588 +121,249206.1781134,0,0.97923 +122,239144.7007861,0,0.96818 +123,232249.3580674,0,0.96364 +124,228247.8439193,0,0.96655 +125,228732.4563594,0,0.95912 +126,233227.8136606,0,0.9409 +127,249533.8684301,0,0.90182 +128,274466.0246328,0,0.84907 +129,285898.2628621,0,0.77243 +130,289996.6994981,0.065084,0.69315 +131,294847.4392555,0.17832,0.64418 +132,299712.0250825,0.22241,0.65718 +133,299970.4850506,0.20579,0.71166 +134,295973.5862591,0.16543,0.74100 +135,291344.3836172,0.10124,0.78431 +136,289456.7027792,0.033614,0.84363 +137,292567.4531088,0,0.89667 +138,307031.980606,0,0.94979 +139,312219.6413931,0,0.97085 +140,306639.6752973,0,0.9807 +141,292188.9938699,0,0.98644 +142,282561.3600604,0,0.99008 +143,279565.9936451,0,0.99254 +144,263587.6291923,0,0.99346 +145,249510.7916472,0,0.99428 +146,238558.5505015,0,0.99534 +147,232106.2820136,0,0.9965 +148,228478.6117479,0,0.99709 +149,230952.4428706,0,0.99769 +150,241156.9962516,0,0.99813 +151,266139.9213767,0,0.99826 +152,297625.8839119,0,0.99818 +153,307655.0537432,0,0.99804 +154,308287.3575936,0.00060963,0.99805 +155,310562.7283837,0.006088,0.99812 +156,313235.0198389,0.0074218,0.99831 +157,312976.5598709,0.0053112,0.99846 +158,310087.3466567,0.0039527,0.99843 +159,306081.2171521,0.0015335,0.99838 +160,304941.2240788,0,0.99828 +161,305795.0650446,0,0.9981 +162,320905.7424619,0,0.99796 +163,324574.9509367,0,0.99793 +164,320148.823984,0,0.99783 +165,306182.7549967,0,0.99774 +166,294173.5971959,0,0.99771 +167,286858.2570291,0,0.9976 +168,269818.3605647,0,0.99748 +169,255418.4480596,0,0.99739 +170,245320.0478797,0,0.99752 +171,238083.1687746,0,0.99789 +172,233121.6604595,0,0.99814 +173,234907.8034529,0,0.99854 +174,243616.9813046,0,0.99900 +175,265604.5400143,0,0.99941 +176,295710.5109345,0,0.99966 +177,306058.1403692,0,0.9998 +178,308236.5886713,0.01781,0.99986 +179,311079.6483197,0.050901,0.99987 +180,313968.8615339,0.060377,0.99984 +181,314162.7065099,0.050395,0.99976 +182,308250.434741,0.040749,0.99963 +183,303436.6178362,0.024728,0.99951 +184,300302.7907238,0.0073169,0.99937 +185,303062.7739539,0,0.99911 +186,318958.0619884,0,0.99851 +187,321094.9720813,0,0.99745 +188,313041.1748629,0,0.99587 +189,296398.1990637,0,0.99444 +190,286082.877125,0,0.99361 +191,283433.6624525,0,0.99172 +192,270256.819439,0,0.98884 +193,260107.6503369,0,0.98624 +194,250876.9371926,0,0.98548 +195,244406.2072784,0,0.98655 +196,239283.1614833,0,0.98977 +197,237243.1738784,0,0.99201 +198,235244.7244827,0,0.99339 +199,232637.0480194,0,0.99312 +200,242656.9871376,0,0.9922 +201,256646.1329078,0,0.98897 +202,270976.8150643,0.069159,0.98065 +203,280678.294579,0.1797,0.97199 +204,287061.3327182,0.21466,0.96335 +205,288930.55213,0.18781,0.95627 +206,284172.1195041,0.1522,0.9492 +207,276358.3208274,0.094903,0.94247 +208,274096.7961071,0.033534,0.92632 +209,277978.3109842,0,0.91662 +210,291856.6881967,0,0.92451 +211,296762.8122329,0,0.94718 +212,289281.3192295,0,0.96859 +213,273852.1822087,0,0.98113 +214,266292.2281436,0,0.98932 +215,267418.3751472,0,0.99379 +216,258759.9662178,0,0.99579 +217,246049.2742181,0,0.99628 +218,235383.1851798,0,0.99629 +219,228875.5324131,0,0.99633 +220,222030.9586166,0,0.99583 +221,217558.6780982,0,0.99524 +222,211129.4863932,0,0.99513 +223,200574.1659127,0,0.99542 +224,203860.299792,0,0.99524 +225,215638.6897642,0,0.99536 +226,229595.5280384,0.072382,0.99505 +227,238826.2411827,0.18447,0.99442 +228,251066.166812,0.22796,0.99288 +229,251735.393515,0.2106,0.99092 +230,242818.5246176,0.1762,0.98967 +231,236573.9471755,0.11512,0.9885 +232,234847.8038175,0.044413,0.98409 +233,239763.1585668,0,0.97846 +234,261321.4891153,0,0.9732 +235,272873.7266154,0,0.96894 +236,271350.6589466,0,0.95932 +237,261012.260225,0,0.95192 +238,254583.06852,0,0.93421 +239,258949.1958373,0,0.90943 +240,248361.5678607,0,0.86199 +241,237829.3241631,0,0.79485 +242,228506.3038873,0,0.68538 +243,224315.5601198,0,0.59208 +244,222017.1125469,0,0.50701 +245,224380.1751118,0,0.43518 +246,234764.7273992,0,0.36519 +247,263486.0913477,0,0.34052 +248,292812.0670072,0,0.38449 +249,305042.7619234,0,0.50811 +250,308878.1232348,0.074326,0.64962 +251,312902.7141657,0.17006,0.76382 +252,317988.8371083,0.18191,0.87351 +253,318602.6795323,0.13116,0.89541 +254,316262.6937503,0.12365,0.91036 +255,311587.3375427,0.092392,0.92456 +256,308730.4318245,0.042049,0.93285 +257,308531.9714919,0,0.94852 +258,320642.6671372,0,0.96455 +259,325461.0993986,0,0.98073 +260,321842.659846,0,0.98954 +261,308573.5097011,0,0.99261 +262,296605.8901095,0,0.99400 +263,290615.1572788,0,0.99409 +264,272550.6516554,0,0.99296 +265,257873.8177559,0,0.98899 +266,248186.184311,0,0.97863 +267,242481.6035878,0,0.9611 +268,239790.8507062,0,0.93708 +269,241392.3794368,0,0.9057 +270,250480.0165274,0,0.8716 +271,280784.4477801,0,0.84736 +272,312016.5657039,0,0.85063 +273,321759.5834277,0,0.87132 +274,321787.2755671,0.076031,0.89753 +275,323236.4975308,0.19581,0.92378 +276,324118.0306361,0.25131,0.94872 +277,325207.2547871,0.24616,0.95400 +278,322848.8075787,0.20346,0.95478 +279,318833.447361,0.13178,0.95506 +280,316918.0743835,0.051561,0.95434 +281,318118.0670923,0,0.95222 +282,332462.5953185,0,0.94683 +283,337521.0261216,0,0.93765 +284,333039.51489,0,0.9268 +285,318118.0670923,0,0.92163 +286,306118.1400047,0,0.9228 +287,297215.117177,0,0.93281 +288,279658.3007765,0,0.94648 +289,266347.6124224,0,0.9555 +290,257592.281005,0,0.96096 +291,250849.2450531,0,0.96759 +292,246833.8848354,0,0.97131 +293,249510.7916472,0,0.97032 +294,258616.8901641,0,0.96457 +295,286955.1795171,0,0.95566 +296,318348.8349209,0,0.94793 +297,328581.0804413,0,0.94044 +298,328179.5444196,0.078986,0.92875 +299,330750.2980303,0.17921,0.92272 +300,332693.3631471,0.1955,0.91626 +301,331045.6808509,0.14795,0.89946 +302,330224.147381,0.13245,0.8804 +303,326628.7846113,0.094354,0.84715 +304,324856.4876876,0.04193,0.84519 +305,324708.7962773,0,0.83288 +306,337534.8721913,0,0.81552 +307,341499.4634868,0,0.78685 +308,335721.0370585,0,0.74896 +309,318127.2978054,0,0.71534 +310,304170.4595312,0,0.67863 +311,296481.275482,0,0.6399 +312,279436.7636611,0,0.58711 +313,259318.424363,0,0.5171 +314,249496.9455775,0,0.44995 +315,242283.1432552,0,0.39156 +316,239707.774288,0,0.34776 +317,240473.9234789,0,0.30079 +318,250064.6344359,0,0.23124 +319,276575.2425863,0,0.18207 +320,306358.1385464,0,0.14744 +321,315971.9262862,0,0.11773 +322,316581.1533537,0.081722,0.086684 +323,319114.9841119,0.19784,0.056593 +324,322318.0415729,0.24148,0.066865 +325,322142.6580232,0.22345,0.079421 +326,319701.1343965,0.18917,0.059228 +327,314905.7789181,0.12726,0.042115 +328,311633.4911084,0.053661,0.032621 +329,310336.5759116,0,0.024503 +330,323504.188212,0,0.022502 +331,327750.3162584,0,0.026714 +332,322054.9662483,0,0.036999 +333,306108.9092915,0,0.055015 +334,291819.7653441,0,0.065307 +335,284329.0416275,0,0.065417 +336,266652.2259562,0,0.055026 +337,252330.7745128,0,0.036812 +338,242786.2171216,0,0.031955 +339,237446.2495676,0,0.031005 +340,235083.1870027,0,0.029617 +341,237400.0960019,0,0.03406 +342,246903.1151839,0,0.040213 +343,272186.0384862,0,0.050632 +344,302919.6979002,0,0.063647 +345,313770.4012013,0,0.073919 +346,315145.7774598,0.084359,0.072285 +347,319096.5226856,0.20794,0.079528 +348,322156.5040929,0.26362,0.10883 +349,320033.4400697,0.25794,0.17342 +350,315441.1602804,0.21806,0.22862 +351,308356.5879422,0.14692,0.24602 +352,304848.9169473,0.063073,0.27271 +353,303962.7684855,0.00027026,0.29775 +354,315459.6217067,0,0.31014 +355,318639.6023849,0,0.30274 +356,311421.1847061,0,0.28642 +357,294741.2860543,0,0.28736 +358,281707.5190945,0,0.28863 +359,277558.3135362,0,0.28624 +360,262807.6339316,0,0.28227 +361,248915.4106494,0,0.28659 +362,238309.3212466,0,0.28964 +363,231524.7470855,0,0.2783 +364,227154.0044117,0,0.26594 +365,225178.6317988,0,0.25472 +366,221320.1937045,0,0.23885 +367,219044.8229144,0,0.21481 +368,232909.3540572,0,0.20041 +369,248555.4128368,0,0.16956 +370,262687.6346607,0.091501,0.13897 +371,270819.8929408,0.2314,0.1646 +372,276658.3190046,0.31736,0.24502 +373,275167.5588318,0.34265,0.27324 +374,267962.9872227,0.2898,0.32255 +375,262553.7893201,0.19659,0.41404 +376,262036.869384,0.08639,0.51915 +377,265189.1579228,0.002534,0.61358 +378,281749.0573037,0,0.64783 +379,290061.3144901,0,0.65273 +380,283539.8156537,0,0.64911 +381,266896.8398545,0,0.64836 +382,260223.0342512,0,0.6751 +383,261298.4123325,0,0.68763 +384,250840.01434,0,0.7081 +385,239693.9282182,0,0.72421 +386,228377.0739033,0,0.72728 +387,219395.5900139,0,0.76893 +388,215712.5354693,0,0.7869 +389,212458.709086,0,0.79311 +390,204811.0632459,0,0.79782 +391,195340.3515598,0,0.80225 +392,197080.3409875,0,0.81419 +393,208798.7313242,0,0.82665 +394,224670.9425759,0.051356,0.83881 +395,236167.7957971,0.10563,0.86092 +396,248144.6461018,0.10694,0.8905 +397,247941.5704127,0.06994,0.91655 +398,239375.4686148,0.054844,0.93673 +399,232443.2030434,0.032835,0.95126 +400,229406.2984189,0.011109,0.9588 +401,231090.9035677,0,0.96529 +402,250276.9408382,0,0.9725 +403,263241.4774494,0,0.97973 +404,261381.4887508,0,0.98557 +405,251615.3942441,0,0.98951 +406,246284.6574033,0,0.99288 +407,249127.7170517,0,0.99484 +408,235489.338381,0,0.99521 +409,223175.5670465,0,0.99371 +410,214147.9295914,0,0.99037 +411,209643.3415769,0,0.98429 +412,209306.4205472,0,0.97188 +413,214175.6217308,0,0.9326 +414,226747.8530333,0,0.82715 +415,253793.8425462,0,0.62052 +416,285381.342926,0,0.36018 +417,298788.9537681,0,0.22249 +418,302582.7768704,0.073828,0.19643 +419,304968.9162182,0.1756,0.11004 +420,310553.4976705,0.2285,0.1088 +421,309251.9671172,0.2317,0.19864 +422,307784.2837272,0.18315,0.15331 +423,303035.0818145,0.11281,0.085065 +424,300118.1764609,0.043162,0.12235 +425,298645.8777144,0.00017489,0.33257 +426,311245.8011563,0,0.5732 +427,316941.1511664,0,0.69382 +428,311836.5667976,0,0.76668 +429,296564.3519003,0,0.85104 +430,284222.8884264,0,0.91486 +431,275758.324473,0,0.93142 +432,254167.6864285,0,0.90007 +433,237880.0930854,0,0.87802 +434,229553.9898292,0,0.90783 +435,226350.9323681,0,0.92705 +436,226410.9320036,0,0.93795 +437,231252.4410478,0,0.94272 +438,241756.992606,0,0.93711 +439,270072.2051762,0,0.91877 +440,301392.0148748,0,0.89093 +441,313031.9441498,0,0.85347 +442,312990.4059406,0.098843,0.81949 +443,315311.9302964,0.2117,0.81067 +444,317527.301451,0.24839,0.83933 +445,315519.6213421,0.22112,0.85515 +446,312727.330616,0.19697,0.86576 +447,307955.0519204,0.14278,0.83231 +448,306238.1392755,0.070014,0.76946 +449,305282.7604651,0.0028785,0.69714 +450,317130.3807858,0,0.6428 +451,322470.3483398,0,0.65468 +452,318321.1427814,0,0.71986 +453,301535.0909285,0,0.78958 +454,288459.7857596,0,0.87624 +455,278799.8444541,0,0.95262 +456,258206.1234291,0,0.98573 +457,242827.7553307,0,0.99518 +458,234732.4199032,0,0.99749 +459,229230.9148692,0,0.99735 +460,229304.7605743,0,0.99765 +461,233061.660824,0,0.99831 +462,242929.2931753,0,0.99845 +463,270976.8150643,0,0.99845 +464,303288.9264259,0,0.99851 +465,313378.0958927,0,0.99853 +466,313082.7130721,0.085616,0.9985 +467,314107.3222311,0.15548,0.9984 +468,315602.6977604,0.14604,0.99828 +469,312404.2556559,0.080527,0.99817 +470,309671.9645652,0.063467,0.99809 +471,306048.9096561,0.03865,0.99795 +472,304821.2248079,0.014077,0.99763 +473,304816.6094513,0,0.99722 +474,318385.7577735,0,0.99663 +475,325954.9425518,0,0.99556 +476,320508.8217966,0,0.99526 +477,305001.2237142,0,0.99594 +478,289170.5506717,0,0.99659 +479,280341.3735492,0,0.99742 +480,261575.3337268,0,0.99803 +481,246847.7309051,0,0.9985 +482,237977.0155734,0,0.99854 +483,233555.5039773,0,0.99823 +484,232373.9726948,0,0.99786 +485,234944.7263055,0,0.99718 +486,244821.5893699,0,0.99596 +487,271479.8889307,0,0.99369 +488,304082.7677564,0,0.99015 +489,313493.479807,0,0.9847 +490,312321.1792376,0.10665,0.9822 +491,312468.870648,0.20835,0.98227 +492,317019.6122281,0.23085,0.98238 +493,315907.3112942,0.18924,0.98236 +494,314015.0150996,0.18514,0.98138 +495,311038.1101106,0.14878,0.98149 +496,308661.2014759,0.083322,0.98526 +497,307031.980606,0.0045477,0.98561 +498,318658.0638112,0,0.98665 +499,324113.4152795,0,0.98868 +500,320227.2850457,0,0.99067 +501,304701.225537,0,0.99306 +502,290329.0051713,0,0.99535 +503,280945.9852601,0,0.99681 +504,261344.5658982,0,0.99708 +505,244996.9729196,0,0.99617 +506,236486.2554006,0,0.99308 +507,229895.5262156,0,0.98659 +508,229549.3744726,0,0.97334 +509,232152.4355793,0,0.94664 +510,242006.2218609,0,0.90225 +511,269052.2113737,0,0.82645 +512,301202.7852553,0,0.73384 +513,313415.0187452,0,0.63605 +514,313876.5544025,0.11259,0.54335 +515,314388.858982,0.26216,0.6225 +516,315791.9273799,0.36644,0.66641 +517,307239.6716517,0.4109,0.60637 +518,302324.3169024,0.33065,0.56103 +519,299162.7976504,0.21205,0.47328 +520,296416.66049,0.089959,0.42288 +521,295821.2794922,0.0064264,0.40352 +522,310936.572266,0,0.36031 +523,315958.0802165,0,0.3432 +524,309390.4278143,0,0.33227 +525,293338.2176564,0,0.3106 +526,278702.9219661,0,0.2786 +527,273866.0282785,0,0.26708 +528,257139.976061,0,0.29595 +529,241895.4533032,0,0.35447 +530,232410.8955474,0,0.42871 +531,225621.7060297,0,0.49802 +532,222354.0335767,0,0.5205 +533,220997.1187444,0,0.53696 +534,218001.7523291,0,0.53922 +535,216178.6864831,0,0.52435 +536,230527.8300659,0,0.49554 +537,248749.2578128,0,0.44619 +538,262544.558607,0.11674,0.40498 +539,270362.9726402,0.2322,0.54257 +540,275204.4816844,0.27641,0.81155 +541,272536.8055857,0.25711,0.92783 +542,263541.4756266,0.2089,0.9625 +543,256756.9014655,0.13572,0.96839 +544,255275.3720058,0.058979,0.96324 +545,256766.1321786,0.0035054,0.95253 +546,273081.4176612,0,0.95459 +547,281259.8295071,0,0.96195 +548,274733.715314,0,0.96279 +549,259203.0404487,0,0.95805 +550,249612.3294918,0,0.9366 +551,250216.9412027,0,0.9106 +552,238890.8561747,0,0.86504 +553,225229.4007211,0,0.81083 +554,215191.0001767,0,0.78969 +555,208291.0421013,0,0.79305 +556,204691.063975,0,0.78944 +557,202946.4591907,0,0.74833 +558,197449.5695133,0,0.7237 +559,189681.9244024,0,0.73519 +560,193821.8992476,0,0.75967 +561,205757.2113432,0.00032073,0.75674 +562,219801.7413923,0.12204,0.75671 +563,231017.0578626,0.22346,0.80033 +564,242509.2957272,0.24054,0.80029 +565,242546.2185798,0.19069,0.79045 +566,234040.1164173,0.16941,0.74929 +567,227357.0801009,0.12409,0.62488 +568,225778.6281532,0.064456,0.59529 +569,228847.8402737,0.004994,0.5899 +570,248103.1078927,0,0.54146 +571,262198.4068641,0,0.4619 +572,261663.0255017,0,0.37984 +573,252547.6962717,0,0.3208 +574,246160.0427758,0,0.34221 +575,248670.7967511,0,0.41383 +576,237977.0155734,0,0.48859 +577,224024.7926558,0,0.54042 +578,215084.8469755,0,0.57731 +579,211235.6395943,0,0.57927 +580,209758.7254912,0,0.52953 +581,215417.1526487,0,0.49879 +582,227684.7704175,0,0.49436 +583,254979.9891852,0,0.4836 +584,283978.274528,0,0.47116 +585,294935.1310303,0.00027803,0.38892 +586,299208.9512162,0.11435,0.38261 +587,304798.148025,0.25583,0.51549 +588,310405.8062602,0.35596,0.56004 +589,309284.2746132,0.40084,0.59285 +590,306976.5963271,0.31861,0.61685 +591,301664.3209125,0.20156,0.58826 +592,298655.1084275,0.085011,0.57727 +593,295327.436339,0.0078999,0.62733 +594,308370.4340119,0,0.63174 +595,316451.9233697,0,0.62305 +596,312127.3342616,0,0.6021 +597,298433.571312,0,0.58387 +598,286253.6453181,0,0.60978 +599,278610.6148346,0,0.63836 +600,259876.8825083,0,0.63287 +601,245343.1246626,0,0.64915 +602,235780.105845,0,0.68347 +603,231778.591697,0,0.69271 +604,230661.6754065,0,0.65726 +605,233837.0407282,0,0.59157 +606,245181.5871825,0,0.54967 +607,273755.2597207,0,0.52746 +608,304544.3034136,0,0.47002 +609,313895.0158288,0.00090956,0.3337 +610,315459.6217067,0.13152,0.23876 +611,318588.8334626,0.25851,0.21154 +612,320633.4364241,0.31893,0.19134 +613,318639.6023849,0.31593,0.19374 +614,318150.3745883,0.2622,0.19313 +615,315238.0845912,0.17744,0.14044 +616,311859.6435804,0.084356,0.11048 +617,309376.5817446,0.0082402,0.1157 +618,325341.1001277,0,0.070239 +619,331899.5218167,0,0.037115 +620,326845.7063702,0,0.023834 +621,312436.5631519,0,0.01433 +622,299832.0243534,0,0.0063603 +623,294408.9803811,0,0 +624,280230.6049915,0,0 +625,268466.061089,0,0 +626,259272.2707973,0,0 +627,253124.6158432,0,0.0078852 +628,246820.0387656,0,0.0093819 +629,246395.425961,0,0.011306 +630,255561.5241133,0,0.013868 +631,282607.5136261,0,0.016044 +632,314388.858982,0,0.017579 +633,322608.809037,0,0.017956 +634,323490.3421423,0.072897,0.016472 +635,326227.2485895,0.14703,0.015605 +636,330044.1484747,0.18543,0.025114 +637,328982.6164631,0.18826,0.035222 +638,328904.1554014,0.15363,0.034437 +639,327039.5513462,0.10141,0.031409 +640,325631.8675917,0.046302,0.024686 +641,326231.8639461,0.0039459,0.021278 +642,345944.0518658,0,0.019525 +643,354570.1532991,0,0.019196 +644,348214.8072993,0,0.018571 +645,330944.1430063,0,0.017661 +646,317624.2239391,0,0.013528 +647,310908.8801266,0,0.0098721 +648,295590.5116636,0,0.0081141 +649,280198.2974955,0,0.0083792 +650,270916.8154289,0,0.01062 +651,262798.4032184,0,0.01359 +652,257873.8177559,0,0.019258 +653,256018.4444139,0,0.025923 +654,262789.1725053,0,0.029036 +655,287338.2541126,0,0.024058 +656,316521.1537183,0,0.016066 +657,325502.6376077,0.0019241,0.012451 +658,327371.8570194,0.11252,0.009451 +659,331793.3686156,0.19273,0.0071942 +660,337557.9489742,0.20284,0.0073419 +661,337917.9467868,0.15859,0.0096857 +662,335121.0407041,0.16008,0.01168 +663,330242.6088073,0.13537,0.013427 +664,326236.4793027,0.085105,0.019871 +665,320254.9771852,0.0099926,0.038788 +666,330699.529108,0,0.059085 +667,337613.333253,0,0.065536 +668,329434.9214072,0,0.055114 +669,313018.09808,0,0.038575 +670,297935.1128022,0,0.026531 +671,290199.7751873,0,0.01888 +672,272901.4187549,0,0.013647 +673,258524.5830326,0,0.0099863 +674,246621.578433,0,0.0073218 +675,239453.9296765,0,0 +676,237649.3252568,0,0 +677,238147.7837666,0,0 +678,247295.4204926,0,0 +679,270746.0472357,0,0 +680,300496.6356998,0,0 +681,310922.7261963,0.0049051,0.0066516 +682,314481.1661134,0.092725,0.009744 +683,316267.3091068,0.17906,0.011895 +684,317827.2996282,0.22384,0.015142 +685,315981.1569994,0.22723,0.02137 +686,310031.9623779,0.18632,0.02972 +687,304110.4598958,0.12466,0.033554 +688,300575.0967615,0.05909,0.051754 +689,298595.1087921,0.0068946,0.10493 +690,314610.3960974,0,0.17512 +691,319678.0576137,0,0.24123 +692,312228.8721062,0,0.27587 +693,293707.4461821,0,0.28748 +694,282515.2064947,0,0.23545 +695,279155.2269101,0,0.16923 +696,266393.7659881,0,0.14274 +697,253327.6915324,0,0.1411 +698,245020.0497025,0,0.15028 +699,239398.5453976,0,0.17212 +700,237404.7113584,0,0.17803 +701,236527.7936097,0,0.17734 +702,231109.364994,0,0.17161 +703,226207.8563144,0,0.15902 +704,236970.8678407,0,0.15215 +705,252021.5456225,0.0082015,0.14196 +706,268156.8321987,0.15177,0.13472 +707,273732.1829379,0.24388,0.14259 +708,277678.3128071,0.24305,0.17271 +709,276275.2444091,0.17315,0.23283 +710,267667.6044021,0.19504,0.31592 +711,261224.5666273,0.182000,0.40845 +712,256586.1332723,0.12705,0.47659 +713,254712.298504,0.018871,0.52108 +714,270630.6633214,0,0.54901 +715,283124.4335622,0,0.59913 +716,275038.3288478,0,0.66041 +717,258833.811923,0,0.71034 +718,248850.7956574,0,0.72085 +719,252815.3869529,0,0.68153 +720,243464.6745377,0,0.63303 +721,232355.5112685,0,0.60579 +722,222820.1845904,0,0.60688 +723,215915.6111585,0,0.61024 +724,210321.798993,0,0.61096 +725,205674.1349249,0,0.63018 +726,201303.3922511,0,0.69459 +727,194214.2045562,0,0.78286 +728,197094.1870573,0,0.87492 +729,208194.1196133,0.0075359,0.92146 +730,223018.644923,0.090774,0.94808 +731,233509.3504115,0.13736,0.96416 +732,245703.1224752,0.12269,0.97528 +733,245015.4343459,0.065891,0.97977 +734,237104.7131813,0.053691,0.98161 +735,229290.9145046,0.035276,0.98008 +736,226992.4669317,0.015884,0.97177 +737,228201.6903536,0.00028619,0.94985 +738,248306.1835819,0,0.93276 +739,267261.4530237,0,0.93104 +740,264690.699413,0,0.93304 +741,256369.2115134,0,0.93906 +742,250627.7079377,0,0.95022 +743,256355.3654437,0,0.96573 +744,244466.2069138,0,0.97942 +745,242753.9096256,0,0.98686 +746,235715.490853,0,0.98693 +747,234210.8846105,0,0.98256 +748,232244.7427108,0,0.97341 +749,236403.1789823,0,0.95742 +750,244203.1315892,0,0.93298 +751,272993.7258863,0,0.89732 +752,305093.5308457,0,0.86126 +753,316128.8484097,0.010902,0.79369 +754,320504.2064401,0.12502,0.72486 +755,324939.5641059,0.2122,0.78548 +756,327367.2416629,0.23938,0.85623 +757,326471.8624879,0.21566,0.85352 +758,325678.0211575,0.20524,0.79117 +759,322262.6572941,0.16615,0.72938 +760,319064.2151896,0.10327,0.71853 +761,313318.0962572,0.017041,0.71877 +762,324764.1805562,0,0.6726 +763,334714.8893257,0,0.63641 +764,326933.3981451,0,0.61022 +765,311804.2593016,0,0.61461 +766,299739.717222,0,0.6464 +767,291741.3042824,0,0.70355 +768,277189.0850104,0,0.74149 +769,267792.2190295,0,0.74369 +770,262895.3257065,0,0.72499 +771,257255.3599753,0,0.69375 +772,252183.0831025,0,0.66858 +773,253738.4582673,0,0.71115 +774,261718.4097806,0,0.78794 +775,283733.6606297,0,0.84225 +776,313678.0940699,0,0.86282 +777,325733.4054363,0.015123,0.84316 +778,328128.7754973,0.17268,0.79507 +779,332277.9810556,0.25423,0.78963 +780,337654.8714622,0.23572,0.86378 +781,339011.7862944,0.14651,0.8781 +782,341601.0013314,0.18549,0.86207 +783,340396.3932661,0.18926,0.83731 +784,338716.4034738,0.1448,0.85284 +785,335748.7291979,0.026179,0.86073 +786,345187.133388,0,0.8585 +787,352391.7049971,0,0.86204 +788,343617.9121534,0,0.86600 +789,324750.3344865,0,0.85806 +790,307128.903094,0,0.83206 +791,296199.7387311,0,0.81018 +792,280156.7592863,0,0.78776 +793,265858.3846258,0,0.75611 +794,254393.8389005,0,0.72315 +795,247540.0343909,0,0.69888 +796,251407.7031984,0,0.67874 +797,256055.3672665,0,0.65061 +798,259784.5753768,0,0.58286 +799,282552.1293473,0,0.49443 +800,316345.7701686,0,0.40681 +801,328433.389031,0.019753,0.31904 +802,330796.451596,0.18123,0.24236 +803,335748.7291979,0.28346,0.13821 +804,340396.3932661,0.29684,0.073024 +805,339441.0144556,0.24114,0.040785 +806,337493.3339822,0.25536,0.024732 +807,332614.9020854,0.23084,0.020094 +808,330062.609901,0.16309,0.022274 +809,326933.3981451,0.032307,0.029868 +810,336394.879118,0,0.036648 +811,347873.2709129,0,0.034023 +812,337059.4904644,0,0.022889 +813,315505.7752724,0,0.015246 +814,298742.8002024,0,0.015589 +815,288210.5565047,0,0.019045 +816,269190.6720709,0,0.021908 +817,255044.6041772,0,0.022479 +818,250960.0136109,0,0.021658 +819,249192.3320437,0,0.021813 +820,247055.4219508,0,0.022115 +821,246330.810969,0,0.022573 +822,253673.8432753,0,0.02053 +823,277636.7745979,0,0.016579 +824,307871.9755021,0,0.015174 +825,316078.0794874,0.021348,0.014956 +826,316581.1533537,0.18544,0.018074 +827,318090.3749528,0.28811,0.024037 +828,319336.5212273,0.30358,0.033897 +829,316539.6151446,0.25083,0.049303 +830,313691.9401396,0.22878,0.062996 +831,309228.8903343,0.17831,0.063393 +832,307068.9034585,0.1085,0.063624 +833,303690.4624477,0.021063,0.090907 +834,315902.6959376,0,0.12707 +835,330279.5316599,0,0.17631 +836,323287.2664531,0,0.23933 +837,306985.8270402,0,0.29941 +838,292950.5277043,0,0.30928 +839,283885.9673966,0,0.31367 +840,264953.7747376,0,0.31615 +841,251781.5470807,0,0.32815 +842,246409.2720307,0,0.3465 +843,243963.1330475,0,0.35013 +844,244867.7429356,0,0.34777 +845,250115.4033582,0,0.35842 +846,256586.1332723,0,0.39132 +847,278832.1519501,0,0.44504 +848,309930.4245333,0,0.48987 +849,318607.2948889,0.023287,0.4427 +850,317850.3764111,0.1908,0.42267 +851,321708.8145054,0.33755,0.52629 +852,323407.265724,0.42508,0.5504 +853,321782.6602106,0.45064,0.53854 +854,314919.6249878,0.3687,0.49887 +855,307908.8983547,0.25054,0.44339 +856,303215.0807208,0.12627,0.4549 +857,301045.8631319,0.025977,0.52876 +858,314878.0867786,0,0.57437 +859,322973.4222062,0,0.58519 +860,313705.7862093,0,0.5665 +861,295461.2816796,0,0.53071 +862,280253.6817743,0,0.51686 +863,274512.1781986,0,0.46983 +864,258247.6616383,0,0.40783 +865,243783.1341412,0,0.36922 +866,234709.3431203,0,0.35799 +867,227518.6175809,0,0.34735 +868,225649.3981692,0,0.29147 +869,224010.9465861,0,0.24805 +870,222404.8024989,0,0.22789 +871,217558.6780982,0,0.23205 +872,227777.0775489,0,0.22072 +873,243150.8302908,0.029238,0.13482 +874,259203.0404487,0.20231,0.11406 +875,267542.9897746,0.3653,0.11606 +876,272204.4999125,0.47523,0.087782 +877,268724.5210571,0.52337,0.076376 +878,261473.7958822,0.46465,0.078945 +879,255575.370183,0.35472,0.088339 +880,253383.0758112,0.21371,0.13002 +881,254426.1463965,0.049709,0.22681 +882,268489.1378719,0,0.30367 +883,280992.1388259,0,0.30977 +884,272495.2673765,0,0.28166 +885,255399.9866333,0,0.25736 +886,245583.1232043,0,0.23734 +887,247660.0336618,0,0.20423 +888,236001.6429605,0,0.16083 +889,221740.1911526,0,0.13104 +890,213178.7047112,0,0.11971 +891,209144.8830671,0,0.11041 +892,209620.2647941,0,0.10955 +893,208420.2720853,0,0.10839 +894,203541.8401886,0,0.10683 +895,191601.9127364,0,0.10981 +896,194721.8937792,0,0.10129 +897,204667.9871922,0.03196,0.066631 +898,218426.3651337,0.21001,0.059866 +899,230873.9818088,0.37388,0.055309 +900,243944.6716212,0.48347,0.051599 +901,242601.6028587,0.53115,0.042214 +902,232632.4326628,0.46885,0.032306 +903,226037.0881212,0.35631,0.027702 +904,223946.331594,0.21436,0.055438 +905,225787.8588663,0.051394,0.13255 +906,241747.7618928,0,0.17986 +907,261999.9465315,0,0.1646 +908,259369.1932853,0,0.12955 +909,250433.8629616,0,0.098186 +910,244803.1279436,0,0.079143 +911,250004.6348004,0,0.062696 +912,239112.3932901,0,0.045306 +913,235443.1848153,0,0.030371 +914,230500.1379265,0,0.021185 +915,227804.7696884,0,0.01552 +916,227154.0044117,0,0.013464 +917,227818.6157581,0,0.013462 +918,237797.0166671,0,0.016698 +919,269453.7473955,0,0.024059 +920,303828.9231449,0,0.032372 +921,315787.3120233,0.036977,0.022754 +922,319567.2890559,0.21442,0.022034 +923,323836.4938852,0.37718,0.022997 +924,327334.9341669,0.48653,0.021713 +925,325525.7143906,0.53418,0.019708 +926,323070.3446942,0.4816,0.016741 +927,317384.2253973,0.37644,0.012638 +928,313895.0158288,0.23584,0.011193 +929,308379.664725,0.060186,0.021514 +930,317162.6882818,0,0.052125 +931,330865.6819446,0,0.096376 +932,324976.4869585,0,0.12491 +933,309598.1188601,0,0.12925 +934,295341.2824087,0,0.12705 +935,288464.4011162,0,0.11856 +936,270907.5847157,0,0.10543 +937,260481.4942192,0,0.095273 +938,254864.6052709,0,0.091407 +939,253253.8458272,0,0.093878 +940,250650.7847205,0,0.10451 +941,250512.3240234,0,0.11362 +942,257319.9749673,0,0.1093 +943,280142.9132166,0,0.10938 +944,310018.1163081,0,0.11368 +945,320481.1296572,0.031264,0.12125 +946,323181.1132519,0.1565,0.13302 +947,321630.3534437,0.2481,0.17571 +948,321247.2788482,0.28677,0.26911 +949,326019.5575438,0.27784,0.35756 +950,325202.6394305,0.25836,0.42117 +951,322068.812318,0.20852,0.53611 +952,319913.4407988,0.13508,0.69058 +953,314268.8597111,0.033622,0.81231 +954,322673.424029,0,0.87079 +955,333639.5112444,0,0.89939 +956,326573.4003325,0,0.91743 +957,312778.0995383,0,0.9332 +958,301987.3958726,0,0.93773 +959,293070.5269752,0,0.93969 +960,277359.8532036,0,0.94691 +961,266513.765259,0,0.95688 +962,258815.3504967,0,0.96785 +963,255123.0652389,0,0.97785 +964,252653.8494728,0,0.9858 +965,254827.6824183,0,0.99006 +966,261127.6441393,0,0.99175 +967,282838.2814547,0,0.99347 +968,310756.5733597,0,0.99500 +969,319488.8279942,0.017909,0.99586 +970,321981.1205432,0.045648,0.99554 +971,326619.5538982,0.086244,0.99455 +972,330644.1448291,0.11768,0.99318 +973,330127.224893,0.13498,0.99055 +974,327699.5473361,0.10871,0.98271 +975,324441.1055961,0.072472,0.96373 +976,323328.8046622,0.035766,0.92168 +977,317476.5325287,0.0073165,0.86295 +978,324921.1026796,0,0.8013 +979,337507.1800519,0,0.71869 +980,332213.3660636,0,0.63499 +981,316488.8462223,0,0.53098 +982,305028.9158536,0,0.42929 +983,298013.573864,0,0.33365 +984,281910.5947837,0,0.2396 +985,270690.6629568,0,0.18913 +986,263850.7045169,0,0.13279 +987,257389.2053159,0,0.10046 +988,258801.504427,0,0.094549 +989,259747.6525242,0,0.087781 +990,264455.3162278,0,0.066732 +991,287259.7930509,0,0.043741 +992,313258.0966218,0,0.025249 +993,322138.0426666,0.038574,0.012613 +994,326167.2489541,0.16682,0.0070121 +995,332407.2110397,0.26246,0 +996,336210.2648551,0.30676,0 +997,335236.4246184,0.30225,0 +998,333685.6648101,0.24942,0 +999,329014.9239591,0.17473,0 +1000,324496.489875,0.094871,0 +1001,315921.1573639,0.024269,0 +1002,322414.9640609,0,0 +1003,335914.8820345,0,0.010554 +1004,330994.9119286,0,0.018655 +1005,315030.3935455,0,0.032878 +1006,302088.9337172,0,0.065981 +1007,296933.5804261,0,0.10767 +1008,287476.7148097,0,0.11567 +1009,273999.8736191,0,0.13067 +1010,264270.701965,0,0.15051 +1011,261404.5655336,0,0.19231 +1012,257795.3566942,0,0.32727 +1013,257536.8967262,0,0.49564 +1014,261146.1055656,0,0.71932 +1015,281204.4452282,0,0.94118 +1016,306667.3674368,0,0.98983 +1017,316244.232324,0.020502,0.9968 +1018,321330.3552665,0.037821,0.99871 +1019,324565.7202236,0.0712,0.99926 +1020,329014.9239591,0.097888,0.9995 +1021,328807.2329134,0.11349,0.99965 +1022,322747.2697341,0.1235,0.99983 +1023,315025.7781889,0.11604,0.99989 +1024,310387.3448339,0.087382,0.99983 +1025,306344.2924767,0.022641,0.99989 +1026,313867.3236893,0,0.99988 +1027,330002.6102656,0,0.99972 +1028,323264.1896702,0,0.9992 +1029,304982.7622879,0,0.99742 +1030,291552.074663,0,0.99304 +1031,287098.2555708,0,0.98748 +1032,273409.1079778,0,0.98600 +1033,262489.1743281,0,0.99377 +1034,252796.9255266,0,0.99825 +1035,244309.2847904,0,0.99907 +1036,240884.6902138,0,0.99893 +1037,237607.7870476,0,0.99842 +1038,232586.2790971,0,0.99737 +1039,226374.009151,0,0.99455 +1040,230315.5236636,0,0.98926 +1041,243690.8270097,0.044783,0.98142 +1042,259895.3439346,0.16198,0.97361 +1043,270441.4337019,0.26626,0.98376 +1044,276164.4758514,0.33217,0.98409 +1045,275292.1734593,0.3564,0.97444 +1046,267750.6808204,0.28839,0.9524 +1047,259969.1896397,0.19533,0.92227 +1048,257273.8214016,0.10006,0.9275 +1049,254564.6070937,0.027059,0.93685 +1050,266564.5341813,0,0.94868 +1051,285044.4218962,0,0.96706 +1052,277872.1577831,0,0.9758 +1053,262184.5607943,0,0.97737 +1054,254541.5303109,0,0.96533 +1055,256581.5179157,0,0.93082 +1056,249076.9481294,0,0.8869 +1057,236352.41006,0,0.8401 +1058,228667.8413674,0,0.78347 +1059,224827.8646993,0,0.71166 +1060,223314.0277437,0,0.64512 +1061,220009.432438,0,0.60353 +1062,214212.5445834,0,0.60441 +1063,201464.9297311,0,0.64244 +1064,201169.5469105,0,0.67322 +1065,211812.5591659,0.053616,0.63725 +1066,225672.474952,0.19193,0.68778 +1067,238715.4726249,0.24652,0.77676 +1068,250627.7079377,0.22534,0.6052 +1069,247812.3404287,0.14808,0.40805 +1070,237173.9435298,0.13577,0.20333 +1071,229710.9119527,0.10814,0.14622 +1072,226895.5444437,0.070075,0.29857 +1073,226281.7020196,0.076997,0.66026 +1074,238161.6298363,0.0055329,0.79581 +1075,263236.8620928,0,0.77152 +1076,262983.0174813,0,0.73372 +1077,254703.0677909,0,0.70993 +1078,251264.6271446,0,0.70185 +1079,258206.1234291,0,0.69615 +1080,249598.4834221,0,0.67669 +1081,241812.3768849,0,0.66797 +1082,238650.8576329,0,0.69054 +1083,235470.8769547,0,0.73927 +1084,233029.353328,0,0.78253 +1085,232983.1997623,0,0.82125 +1086,238217.0141151,0,0.83858 +1087,254103.0714365,0,0.8254 +1088,280516.7570989,0,0.77779 +1089,294833.5931858,0.042624,0.73732 +1090,303976.6145552,0.1133,0.80965 +1091,308596.5864839,0.15541,0.89883 +1092,313055.0209326,0.15767,0.92733 +1093,311176.5708078,0.1288,0.93837 +1094,306967.3656139,0.11959,0.94176 +1095,301641.2441297,0.097042,0.92555 +1096,298355.1102503,0.06438,0.84339 +1097,293301.2948038,0.079605,0.76822 +1098,299416.6422619,0.0085579,0.71133 +1099,317550.3782339,0,0.63994 +1100,314259.628998,0,0.5712 +1101,301193.5545422,0,0.49918 +1102,290716.6951234,0,0.45381 +1103,285602.8800414,0,0.43966 +1104,270012.2055407,0,0.4217 +1105,257283.0521147,0,0.41591 +1106,252183.0831025,0,0.42299 +1107,250175.4029936,0,0.43938 +1108,247036.9605245,0,0.42674 +1109,249376.9463066,0,0.41365 +1110,252524.6194888,0,0.4199 +1111,272762.9580577,0,0.41506 +1112,297510.4999976,0,0.36900 +1113,306238.1392755,0.081744,0.40949 +1114,310913.4954831,0.2881,0.4681 +1115,313913.477255,0.42819,0.47437 +1116,316641.1529892,0.49632,0.42541 +1117,312136.5649748,0.49812,0.36621 +1118,304927.3780091,0.38474,0.3398 +1119,297164.3482547,0.24196,0.33337 +1120,293781.2918873,0.10755,0.27832 +1121,291441.3061052,0.09667,0.25284 +1122,300796.633877,0.010409,0.26659 +1123,323490.3421423,0,0.20693 +1124,322456.5022701,0,0.1413 +1125,308236.5886713,0,0.088752 +1126,296476.6601254,0,0.064382 +1127,291044.38544,0,0.04836 +1128,274862.945298,0,0.035668 +1129,266509.1499024,0,0.029707 +1130,263218.4006665,0,0.026465 +1131,257047.6689295,0,0.022317 +1132,253581.5361438,0,0.017363 +1133,255358.4484241,0,0.014111 +1134,259378.4239985,0,0.010876 +1135,280987.5234693,0,0.0078298 +1136,306482.7531739,0,0.0066513 +1137,313382.7112492,0.061254,0.010076 +1138,315528.8520553,0.17214,0.010433 +1139,315953.4648599,0.24596,0.010897 +1140,319248.8294524,0.27117,0.01341 +1141,317670.3775048,0.25405,0.016999 +1142,314601.1653843,0.22845,0.018716 +1143,312191.9492536,0.17983,0.018907 +1144,310391.9601905,0.11671,0.0145 +1145,307045.8266757,0.10273,0.00939 +1146,314421.166478,0.011819,0.006899 +1147,333048.7456032,0,0.0057923 +1148,327953.3919475,0,0 +1149,311490.4150547,0,0 +1150,298913.5683955,0,0 +1151,289401.3185003,0,0 +1152,272564.4977251,0,0.0078388 +1153,259752.2678808,0,0.016677 +1154,251135.3971606,0,0.041941 +1155,247013.8837417,0,0.090542 +1156,244909.2811448,0,0.11761 +1157,248804.6420917,0,0.11695 +1158,253535.3825781,0,0.12863 +1159,277161.392871,0,0.16996 +1160,304756.6098159,0,0.20804 +1161,313318.0962572,0.052739,0.27426 +1162,316894.9976006,0.11087,0.46292 +1163,322424.1947741,0.13918,0.63434 +1164,325064.1787334,0.12693,0.7672 +1165,323536.495708,0.085728,0.8412 +1166,318971.9080581,0.097167,0.89223 +1167,313604.2483647,0.094888,0.90561 +1168,308471.9718565,0.076045,0.91022 +1169,302250.4711972,0.090645,0.92091 +1170,309238.1210474,0.014315,0.91899 +1171,328008.7762264,0,0.90386 +1172,325299.5619185,0,0.85069 +1173,309182.7367686,0,0.69894 +1174,296292.0458626,0,0.44442 +1175,287052.1020051,0,0.20669 +1176,269942.9751921,0,0.089032 +1177,257910.7406085,0,0.053042 +1178,251776.9317241,0,0.037535 +1179,247816.9557852,0,0.034418 +1180,246400.0413176,0,0.041894 +1181,248578.4896196,0,0.061951 +1182,251689.2399493,0,0.084972 +1183,274046.0271848,0,0.091293 +1184,303307.3878522,0,0.086464 +1185,314984.2399798,0.071779,0.08097 +1186,317707.3003573,0.1828,0.088501 +1187,320568.8214321,0.29496,0.13105 +1188,322211.8883718,0.37797,0.24844 +1189,318445.7574089,0.42293,0.41999 +1190,312210.4106799,0.36481,0.55214 +1191,306261.2160584,0.27411,0.51937 +1192,302499.7004521,0.16862,0.49094 +1193,298955.1066047,0.1261,0.52201 +1194,307156.5952334,0.017967,0.52381 +1195,322271.8880072,0,0.55735 +1196,317231.9186304,0,0.59334 +1197,297265.8860993,0,0.62059 +1198,283835.1984743,0,0.59213 +1199,278518.3077032,0,0.53365 +1200,262553.7893201,0,0.44153 +1201,249123.1016951,0,0.32232 +1202,243072.3692291,0,0.20781 +1203,237487.7877767,0,0.11773 +1204,235087.8023592,0,0.081742 +1205,237321.6349401,0,0.072015 +1206,233777.0410927,0,0.068358 +1207,228243.2285627,0,0.062963 +1208,235272.4166221,0,0.050316 +1209,248670.7967511,0.068176,0.048015 +1210,258496.8908932,0.1542,0.053023 +1211,263643.0134711,0.2082,0.04553 +1212,266956.8394899,0.21858,0.040681 +1213,270072.2051762,0.19306,0.040083 +1214,263190.7085271,0.24162,0.042439 +1215,256184.5972505,0.25494,0.043631 +1216,253567.6900741,0.22034,0.046531 +1217,252229.2366682,0.14796,0.067841 +1218,262023.0233143,0.023384,0.0919 +1219,284772.1158585,0,0.10182 +1220,282229.0543872,0,0.10553 +1221,265323.0032634,0,0.10678 +1222,256553.8257763,0,0.11076 +1223,255663.0619579,0,0.11607 +1224,243806.210924,0,0.12386 +1225,232867.815848,0,0.13163 +1226,221763.2679354,0,0.13235 +1227,217046.3735187,0,0.13525 +1228,215024.8473401,0,0.14218 +1229,212384.8633808,0,0.17417 +1230,210441.7982639,0,0.23358 +1231,201501.8525837,0,0.30668 +1232,204931.0625168,0,0.33678 +1233,210940.2567737,0.089176,0.31087 +1234,218707.9018847,0.2104,0.38126 +1235,228580.1495925,0.33335,0.42922 +1236,238987.7786627,0.42379,0.38355 +1237,236592.4086017,0.47218,0.34032 +1238,227181.6965511,0.41035,0.35899 +1239,219778.6646094,0.31248,0.42372 +1240,217226.372425,0.19659,0.54876 +1241,219206.3603944,0.14489,0.75555 +1242,233260.1211567,0.025592,0.84461 +1243,262466.0975452,0,0.86634 +1244,263296.8617282,0,0.87257 +1245,254619.9913726,0,0.8771 +1246,249884.6355295,0,0.87953 +1247,252460.0044968,0,0.88463 +1248,240197.0020846,0,0.88793 +1249,231533.9777987,0,0.88462 +1250,226780.1605293,0,0.88445 +1251,226466.3162824,0,0.88003 +1252,226447.8548562,0,0.86189 +1253,230426.2922213,0,0.84499 +1254,236786.2535778,0,0.85841 +1255,269633.7463018,0,0.86419 +1256,299578.1797419,0,0.84412 +1257,310567.3437402,0.10137,0.85092 +1258,312828.8684606,0.24823,0.87938 +1259,314735.0107249,0.31713,0.88779 +1260,319078.0612593,0.31668,0.87301 +1261,317125.7654293,0.26155,0.85509 +1262,313405.7880321,0.2358,0.8387 +1263,307964.2826335,0.18755,0.79802 +1264,304133.5366786,0.12518,0.71986 +1265,299181.2590767,0.12042,0.73029 +1266,307535.0544723,0.025114,0.73738 +1267,330634.914116,0,0.68774 +1268,327634.9323441,0,0.65691 +1269,310784.2654991,0,0.62798 +1270,296698.1972409,0,0.58291 +1271,287421.3305309,0,0.51167 +1272,270427.5876322,0,0.44372 +1273,256203.0586768,0,0.41657 +1274,250253.8640553,0,0.39671 +1275,247235.4208571,0,0.36433 +1276,246353.8877519,0,0.35384 +1277,249423.0998723,0,0.37612 +1278,254961.5277589,0,0.36906 +1279,281430.5977002,0,0.3633 +1280,306861.2124128,0,0.34676 +1281,315616.5438302,0.087131,0.39303 +1282,316064.2334177,0.16582,0.42598 +1283,318307.2967117,0.22117,0.36177 +1284,322165.734806,0.2345,0.27887 +1285,320176.5161235,0.21296,0.24735 +1286,318482.6802615,0.17437,0.23397 +1287,313650.4019304,0.12202,0.19509 +1288,310978.1104751,0.067759,0.15207 +1289,304622.7644753,0.10381,0.10766 +1290,308924.2768005,0.028219,0.066235 +1291,326185.7103804,0,0.034122 +1292,323905.7242338,0,0.017014 +1293,306413.5228253,0,0.0088359 +1294,291427.4600355,0,0.0064066 +1295,281144.4455927,0,0 +1296,262867.633567,0,0 +1297,247886.1861338,0,0 +1298,240635.460959,0,0 +1299,238184.7066191,0,0.0083482 +1300,236633.9468109,0,0.01533 +1301,239467.7757462,0,0.030319 +1302,248329.2603647,0,0.054336 +1303,275795.2473256,0,0.080792 +1304,303478.1560454,0,0.096778 +1305,312168.8724708,0.09779,0.11844 +1306,310433.4983996,0.18777,0.14364 +1307,311181.1861643,0.32273,0.17041 +1308,313248.8659086,0.44328,0.18427 +1309,310387.3448339,0.52956,0.18963 +1310,306828.9049168,0.50562,0.19487 +1311,301092.0166976,0.43323,0.17559 +1312,298982.7987441,0.31863,0.12101 +1313,294888.9774646,0.20325,0.10474 +1314,302296.6247629,0.040373,0.11437 +1315,323208.8053914,0,0.11452 +1316,322664.1933158,0,0.096189 +1317,307193.518086,0,0.072987 +1318,291861.3035533,0,0.060827 +1319,279745.9925514,0,0.052717 +1320,260153.8039026,0,0.03805 +1321,241840.0690243,0,0.028668 +1322,237026.2521195,0,0.021124 +1323,236666.2543069,0,0.016433 +1324,239066.2397244,0,0.014416 +1325,243146.2149342,0,0.013786 +1326,249815.405181,0,0.012884 +1327,276224.4754868,0,0.011196 +1328,301539.7062851,0.001903,0.0088131 +1329,309455.0428063,0.11446,0.011611 +1330,306699.6749328,0.23572,0.011777 +1331,308167.3583227,0.28794,0.0088502 +1332,306939.6734745,0.27587,0.0057587 +1333,305291.9911783,0.21572,0 +1334,304313.535585,0.19464,0.0064622 +1335,299379.7194093,0.15553,0.011186 +1336,298493.5709475,0.1051,0.018315 +1337,295405.8974007,0.12253,0.030025 +1338,304295.0741587,0.035954,0.04079 +1339,322724.1929513,0,0.044343 +1340,320287.2846812,0,0.042613 +1341,305273.529752,0,0.038156 +1342,290947.462952,0,0.029626 +1343,280442.9113938,0,0.019574 +1344,259456.8850602,0,0.015715 +1345,243663.1348703,0,0.013576 +1346,234021.6549911,0,0.013103 +1347,232207.8198582,0,0.014221 +1348,232840.1237086,0,0.016129 +1349,235664.7219307,0,0.017386 +1350,242140.0672015,0,0.016446 +1351,265899.9228349,0,0.015124 +1352,292438.2231248,0.0023443,0.012777 +1353,304401.2273598,0.10677,0.016504 +1354,306062.7557258,0.1948,0.023935 +1355,306210.4471361,0.23141,0.023728 +1356,307442.7473409,0.21291,0.025872 +1357,304151.9981049,0.15436,0.034662 +1358,298368.95632,0.13011,0.059675 +1359,293236.6798118,0.095269,0.13576 +1360,291565.9207327,0.057103,0.21584 +1361,289184.3967414,0.10702,0.37181 +1362,294838.2085423,0.036902,0.58635 +1363,311610.4143255,0,0.69888 +1364,309067.3528543,0,0.7637 +1365,294722.824628,0,0.8084 +1366,281749.0573037,0,0.84968 +1367,274170.6418122,0,0.85138 +1368,258224.5848554,0,0.82315 +1369,247198.4980046,0,0.77658 +1370,238853.9333221,0,0.70015 +1371,239758.5432102,0,0.61104 +1372,240746.2295167,0,0.51738 +1373,240229.3095806,0,0.45548 +1374,234381.6528037,0,0.42165 +1375,231192.4414123,0,0.41729 +1376,235535.4919467,0.0070995,0.42897 +1377,246695.4241382,0.17129,0.67126 +1378,255127.6805955,0.38529,0.72581 +1379,257610.7424313,0.51695,0.6988 +1380,258141.5084371,0.57552,0.68341 +1381,255363.0637807,0.56655,0.70071 +1382,248943.1027888,0.49388,0.73953 +1383,238567.7812146,0.38119,0.77791 +1384,237510.8645596,0.24775,0.75792 +1385,238341.6287426,0.18758,0.62536 +1386,246672.3473553,0.049792,0.5739 +1387,270399.8954928,0,0.53254 +1388,271502.9657135,0,0.45955 +1389,254329.2239085,0,0.36827 +1390,241936.9915123,0,0.28836 +1391,239532.3907382,0,0.20868 +1392,226923.2365831,0,0.13834 +1393,212255.6333968,0,0.08291 +1394,199794.170652,0,0.046242 +1395,194634.2020043,0,0.024793 +1396,196724.9585315,0,0.012835 +1397,201474.1604442,0,0.0092142 +1398,201728.0050557,0,0.014165 +1399,197020.3413521,0,0.02765 +1400,197209.5709716,0.008643,0.039367 +1401,207917.198219,0.18088,0.055608 +1402,220526.3523741,0.39447,0.068844 +1403,232230.8966411,0.55421,0.085242 +1404,242080.067566,0.65232,0.11202 +1405,239998.541752,0.68775,0.16583 +1406,232992.4304755,0.65456,0.25136 +1407,228760.1484988,0.5625,0.35868 +1408,229152.4538074,0.41965,0.41041 +1409,233975.5014253,0.26007,0.42637 +1410,244503.1297664,0.064333,0.56707 +1411,267792.2190295,0,0.69843 +1412,274152.1803859,0,0.7846 +1413,264053.7802061,0,0.84165 +1414,260370.7256615,0,0.88502 +1415,263509.1681305,0,0.92341 +1416,251227.704292,0,0.94609 +1417,244207.7469458,0,0.95654 +1418,239121.6240033,0,0.95992 +1419,235941.6433251,0,0.95978 +1420,237501.6338465,0,0.96131 +1421,242398.5271695,0,0.96111 +1422,253406.1525941,0,0.95596 +1423,283784.429552,0,0.95076 +1424,308998.1225057,0.0085208,0.94361 +1425,315150.3928164,0.17189,0.93757 +1426,314245.7829282,0.36598,0.93252 +1427,314236.5522151,0.42831,0.93346 +1428,316784.2290429,0.39844,0.9231 +1429,314342.7054162,0.29974,0.91246 +1430,312579.6392057,0.28376,0.89655 +1431,309155.0446291,0.24117,0.85725 +1432,307567.3619683,0.17662,0.79015 +1433,304987.3776445,0.16111,0.77048 +1434,311956.5660684,0.051391,0.80556 +1435,330847.2205183,0,0.84522 +1436,335850.2670425,0,0.86368 +1437,318874.9855701,0,0.86693 +1438,304779.6865987,0,0.84335 +1439,295710.5109345,0,0.79444 +1440,276459.858672,0,0.72525 +1441,264783.0065445,0,0.64017 +1442,260823.0306056,0,0.54313 +1443,258792.2737138,0,0.44044 +1444,257753.8184851,0,0.36801 +1445,258058.4320188,0,0.32473 +1446,264658.391917,0,0.28553 +1447,293993.5982896,0,0.25114 +1448,319322.6751576,0.011055,0.21738 +1449,328696.4643556,0.14129,0.18203 +1450,327205.7041828,0.25091,0.16839 +1451,325281.1004923,0.33531,0.14504 +1452,327833.3926767,0.37314,0.11621 +1453,327307.2420274,0.36799,0.087539 +1454,324953.4101756,0.31512,0.060723 +1455,320462.6682309,0.23765,0.03974 +1456,317236.533987,0.15036,0.021899 +1457,312191.9492536,0.15598,0.010801 +1458,313576.5562253,0.054659,0 +1459,331977.9828784,0,0 +1460,335711.8063453,0,0 +1461,318542.6798969,0,0 +1462,303870.461354,0,0 +1463,292959.7584175,0,0.0060375 +1464,273690.6447287,0,0.010231 +1465,263416.8609991,0,0.01909 +1466,256812.2857444,0,0.034183 +1467,255866.1376471,0,0.057938 +1468,255893.8297865,0,0.090809 +1469,259918.4207174,0,0.1165 +1470,264519.9312198,0,0.15286 +1471,290301.3130319,0,0.21223 +1472,311361.1850706,0.010453,0.22568 +1473,317984.2217517,0.1352,0.31207 +1474,314933.4710575,0.25756,0.43093 +1475,313913.477255,0.32669,0.4826 +1476,313179.6355601,0.34314,0.51833 +1477,307479.6701935,0.31482,0.54637 +1478,304604.303049,0.33015,0.60076 +1479,302148.9333526,0.31058,0.64409 +1480,302245.8558406,0.25328,0.63298 +1481,302033.5494383,0.18206,0.55928 +1482,311956.5660684,0.0557,0.55232 +1483,334613.3514811,0,0.50248 +1484,337437.9497033,0,0.40544 +1485,320887.2810356,0,0.29197 +1486,305638.1429212,0,0.19745 +1487,294930.5156738,0,0.12968 +1488,276289.0904788,0,0.08145 +1489,264183.0101901,0,0.046502 +1490,259955.34357,0,0.023287 +1491,257809.2027639,0,0.011419 +1492,257887.6638257,0,0 +1493,260107.6503369,0,0 +1494,268124.5247027,0,0 +1495,293508.9858495,0,0.0069026 +1496,314222.7061454,0.014423,0.013407 +1497,320384.2071692,0.14649,0.022556 +1498,318602.6795323,0.27677,0.035018 +1499,317504.2246682,0.3695,0.069999 +1500,318445.7574089,0.41651,0.10652 +1501,313465.7876675,0.41876,0.11449 +1502,310835.0344214,0.38045,0.095221 +1503,306708.9056459,0.31027,0.068568 +1504,305347.3754571,0.21855,0.051463 +1505,304271.9973758,0.17038,0.041503 +1506,307973.5133467,0.056764,0.049184 +1507,326291.8635816,0,0.065072 +1508,331857.9836076,0,0.084415 +1509,315875.0037982,0,0.097748 +1510,302195.0869183,0,0.11815 +1511,293624.3697639,0,0.13044 +1512,277562.9288928,0,0.11987 +1513,262858.4028539,0,0.098313 +1514,257878.4331125,0,0.076355 +1515,251989.2381265,0,0.062608 +1516,250696.9382862,0,0.048866 +1517,253295.3840364,0,0.044733 +1518,259544.5768351,0,0.055063 +1519,284725.9622927,0,0.078549 +1520,304327.3816547,0.017602,0.090235 +1521,310055.0391607,0.17168,0.12486 +1522,307830.4372929,0.33788,0.27143 +1523,308458.1257868,0.46742,0.44225 +1524,312215.0260365,0.54919,0.5422 +1525,308384.2800816,0.58003,0.59865 +1526,303312.0032088,0.53959,0.66100 +1527,298332.0334674,0.45369,0.69912 +1528,295659.7420122,0.33222,0.69347 +1529,296938.1957827,0.22101,0.73499 +1530,307544.2851855,0.069431,0.82368 +1531,327108.7816948,0,0.83454 +1532,329702.6120884,0,0.78833 +1533,308416.5875776,0,0.71366 +1534,291348.9989738,0,0.66029 +1535,282584.4368433,0,0.62669 +1536,265267.6189845,0,0.59537 +1537,251403.0878418,0,0.5668 +1538,241503.1479945,0,0.53895 +1539,236103.1808051,0,0.50518 +1540,239490.8525291,0,0.43735 +1541,240293.9245726,0,0.37718 +1542,237649.3252568,0,0.34827 +1543,228390.919973,0,0.30907 +1544,232152.4355793,0.018947,0.20493 +1545,247083.1140903,0.13719,0.18019 +1546,260790.7231095,0.22813,0.30376 +1547,266495.3038327,0.27295,0.31565 +1548,269721.4380767,0.26928,0.33527 +1549,264335.316957,0.22735,0.33203 +1550,254643.0681554,0.20406,0.33328 +1551,248153.876815,0.1639,0.26771 +1552,246963.1148194,0.11342,0.14252 +1553,247936.9550561,0.13174,0.081375 +1554,254873.835984,0.058049,0.0538 +1555,276916.7789727,0,0.033908 +1556,282561.3600604,0,0.022171 +1557,267635.2969061,0,0.015405 +1558,257010.746077,0,0.012884 +1559,259350.731859,0,0.013748 +1560,250613.8618679,0,0.015677 +1561,239546.2368079,0,0.017222 +1562,232332.4344856,0,0.019118 +1563,228164.767501,0,0.02281 +1564,224749.4036376,0,0.031017 +1565,223724.7944786,0,0.05588 +1566,219644.8192688,0,0.1067 +1567,208277.1960316,0,0.17618 +1568,208175.658187,0.019971,0.22065 +1569,218015.5983988,0.12631,0.22828 +1570,226050.9341909,0.18654,0.26094 +1571,231898.5909679,0.23793,0.39591 +1572,237755.4784579,0.25647,0.48545 +1573,233883.1942939,0.24521,0.49749 +1574,222626.3396144,0.20347,0.48697 +1575,216174.0711265,0.1475,0.47168 +1576,214960.2323481,0.088579,0.49657 +1577,218532.5183349,0.12384,0.52542 +1578,232590.8944537,0.059699,0.54069 +1579,259156.886883,0,0.51452 +1580,272342.9606096,0,0.48474 +1581,263546.0909831,0,0.49128 +1582,257776.8952679,0,0.52846 +1583,263703.0131066,0,0.54735 +1584,253839.9961119,0,0.5483 +1585,246233.888481,0,0.52801 +1586,244023.1326829,0,0.49977 +1587,244055.4401789,0,0.48531 +1588,242449.2960918,0,0.47488 +1589,246529.2713016,0,0.49083 +1590,258173.8159331,0,0.50208 +1591,285676.7257466,0,0.47397 +1592,306418.1381819,0.024618,0.43106 +1593,315575.005621,0.1366,0.40765 +1594,314961.1631969,0.20357,0.49758 +1595,315611.9284736,0.25719,0.70305 +1596,318824.2166478,0.27516,0.81669 +1597,316387.3083777,0.26143,0.8687 +1598,314167.3218665,0.22424,0.87605 +1599,309468.8888761,0.1708,0.83818 +1600,307221.2102254,0.11072,0.71519 +1601,304885.8397999,0.13658,0.64578 +1602,310488.8826785,0.067159,0.60322 +1603,325451.8686854,0,0.51607 +1604,335231.8092618,0,0.41487 +1605,319516.5201336,0,0.32368 +1606,304105.8445392,0,0.25494 +1607,295235.1292075,0,0.24601 +1608,280479.8342464,0,0.28303 +1609,270446.0490585,0,0.33657 +1610,265452.2332474,0,0.38433 +1611,261035.3370079,0,0.41473 +1612,260343.033522,0,0.42523 +1613,264030.7034232,0,0.43766 +1614,270012.2055407,0,0.4477 +1615,292885.9127123,0,0.4297 +1616,313415.0187452,0.026867,0.35801 +1617,317421.1482499,0.13598,0.26541 +1618,314273.4750677,0.19232,0.22117 +1619,314075.0147351,0.25659,0.28151 +1620,315371.9299318,0.29471,0.3372 +1621,313885.7851156,0.30422,0.33635 +1622,312561.1777794,0.33097,0.32988 +1623,309095.0449937,0.32338,0.31472 +1624,308218.127245,0.27658,0.26527 +1625,308707.3550416,0.20998,0.23861 +1626,314785.7796472,0.083695,0.30092 +1627,331317.9868886,0,0.31559 +1628,342007.1527097,0,0.28042 +1629,326758.0145953,0,0.21717 +1630,312838.0991737,0,0.13892 +1631,305564.297216,0,0.077219 +1632,290952.0783086,0,0.041111 +1633,266352.227779,0,0.023904 +1634,260389.1870878,0,0.014487 +1635,255473.8323384,0,0.0077339 +1636,253964.6107393,0,0 +1637,256198.4433203,0,0 +1638,263338.3999374,0,0 +1639,287139.79378,0,0 +1640,307355.055566,0.039743,0 +1641,314656.5496632,0.217000,0 +1642,319008.8309107,0.39157,0 +1643,325673.4058009,0.52342,0.0057373 +1644,325031.8712374,0.6058,0.0093689 +1645,320111.9011314,0.6361,0.019276 +1646,316331.9240988,0.55255,0.044557 +1647,306238.1392755,0.42965,0.09963 +1648,307821.2065798,0.28718,0.13311 +1649,306002.7560904,0.21888,0.1389 +1650,309058.1221411,0.089676,0.19436 +1651,324376.4906041,0,0.25101 +1652,333002.5920375,0,0.29361 +1653,315810.3888062,0,0.35377 +1654,299928.9468414,0,0.43721 +1655,293485.9090667,0,0.5416 +1656,278278.3091614,0,0.65326 +1657,267829.1418821,0,0.76527 +1658,262650.7118081,0,0.86898 +1659,257795.3566942,0,0.93701 +1660,256863.0546666,0,0.96478 +1661,260467.6481495,0,0.97915 +1662,267547.6051312,0,0.98849 +1663,291685.9200035,0,0.99317 +1664,313156.5587772,0.030796,0.99563 +1665,323914.9549469,0.1081,0.99687 +1666,326434.9396353,0.09808,0.99762 +1667,328313.3897602,0.11968,0.99785 +1668,331101.0651297,0.12335,0.99804 +1669,327016.4745634,0.11179,0.99787 +1670,326601.0924719,0.10834,0.99652 +1671,320725.7435555,0.094779,0.99438 +1672,317550.3782339,0.072653,0.99065 +1673,311873.4896501,0.12724,0.98684 +1674,315510.390629,0.076704,0.98237 +1675,329536.4592518,0,0.97044 +1676,339141.0162784,0,0.96381 +1677,324136.4920624,0,0.96362 +1678,309118.1217766,0,0.96831 +1679,299592.0258116,0,0.97508 +1680,282893.6657336,0,0.98088 +1681,272384.4988188,0,0.98491 +1682,263559.9370528,0,0.98678 +1683,257103.0532084,0,0.98569 +1684,255515.3705476,0,0.98399 +1685,256489.2107843,0,0.98341 +1686,264330.7016004,0,0.98475 +1687,285949.0317844,0,0.98686 +1688,305342.7601005,0.04215,0.98761 +1689,317914.9914031,0.18894,0.98858 +1690,321607.2766608,0.3028,0.98801 +1691,323374.958228,0.3277,0.98517 +1692,323434.9578634,0.28926,0.98406 +1693,318681.1405941,0.20512,0.97882 +1694,310622.7280191,0.21551,0.9549 +1695,301498.1680759,0.20453,0.92582 +1696,294999.7460224,0.17065,0.90258 +1697,291644.3817944,0.17132,0.87102 +1698,294030.5211422,0.088269,0.80735 +1699,305038.1465668,0,0.69846 +1700,307442.7473409,0,0.53558 +1701,290167.4676913,0,0.34611 +1702,277059.8550264,0,0.20185 +1703,271876.8095959,0,0.11164 +1704,257564.5888656,0,0.05985 +1705,241263.1494528,0,0.036281 +1706,231912.4370376,0,0.03605 +1707,224934.0179005,0,0.050015 +1708,223581.7184248,0,0.080352 +1709,224306.3294067,0,0.15037 +1710,222127.8811046,0,0.28042 +1711,218435.5958469,0,0.42402 +1712,223078.6445585,0.045982,0.48784 +1713,238913.9329575,0.17694,0.58447 +1714,251458.4721207,0.26204,0.69205 +1715,258889.1962018,0.32044,0.69046 +1716,261916.8701132,0.33646,0.73211 +1717,260310.726026,0.31615,0.79065 +1718,253738.4582673,0.28167,0.83749 +1719,248176.9535979,0.22624,0.82021 +1720,245375.4321586,0.15844,0.67341 +1721,246150.8120627,0.16844,0.49042 +1722,252552.3116283,0.091244,0.43917 +1723,264723.006909,0,0.41361 +1724,266582.9956076,0,0.39426 +1725,250507.7086668,0,0.34978 +1726,242578.5260758,0,0.31386 +1727,242416.9885958,0,0.30477 +1728,230753.982538,0,0.31402 +1729,223189.4131162,0,0.31871 +1730,212758.7072631,0,0.32023 +1731,204940.2932299,0,0.32155 +1732,202687.9992227,0,0.31104 +1733,201524.9293665,0,0.29857 +1734,199244.9432199,0,0.31162 +1735,192178.8323079,0,0.29676 +1736,192580.3683297,0.055846,0.19497 +1737,205424.90567,0.22164,0.20329 +1738,217812.5227097,0.35995,0.28205 +1739,226484.7777087,0.43177,0.33985 +1740,237390.8652887,0.44819,0.37373 +1741,239033.9322284,0.41771,0.3965 +1742,230112.4479744,0.34117,0.41517 +1743,223978.6390901,0.24352,0.41491 +1744,221597.1150988,0.14356,0.35507 +1745,220941.7344656,0.16209,0.21238 +1746,228755.5331422,0.092373,0.17294 +1747,245749.2760409,0.0012441,0.1645 +1748,254172.3017851,0,0.1447 +1749,243155.4456473,0,0.11732 +1750,237007.7906932,0,0.097212 +1751,238530.8583621,0,0.081055 +1752,228533.9960268,0,0.068748 +1753,222266.3418018,0,0.082553 +1754,216104.840778,0,0.13704 +1755,217161.757433,0,0.20379 +1756,218670.9790321,0,0.27066 +1757,225280.1696434,0,0.36852 +1758,236772.4075081,0,0.50967 +1759,265752.2314246,0,0.64435 +1760,289452.0874226,0.057462,0.66429 +1761,302365.8551115,0.19449,0.71614 +1762,304922.7626525,0.28346,0.81977 +1763,307585.8233946,0.37622,0.92343 +1764,312358.1020902,0.43723,0.95147 +1765,312044.2578433,0.46291,0.95229 +1766,313128.8666378,0.39242,0.9444 +1767,311919.6432159,0.29612,0.92234 +1768,310239.6534236,0.19109,0.8819 +1769,306205.8317795,0.18668,0.87089 +1770,307876.5908587,0.10144,0.88467 +1771,321016.5110196,0.0017624,0.90152 +1772,325276.4851357,0,0.91015 +1773,306228.9085624,0,0.91578 +1774,289022.8592614,0,0.93801 +1775,277498.3139007,0,0.96093 +1776,258718.4280087,0,0.97021 +1777,248001.5700481,0,0.97112 +1778,241373.9180105,0,0.96789 +1779,239652.3900091,0,0.96629 +1780,240796.998439,0,0.96838 +1781,245366.2014454,0,0.9673 +1782,251832.316003,0,0.96199 +1783,275541.4027142,0,0.95558 +1784,297625.8839119,0.065024,0.93857 +1785,309408.8892406,0.22647,0.94477 +1786,309035.0453583,0.3575,0.96384 +1787,310188.8845013,0.43501,0.98032 +1788,313590.402295,0.46285,0.98699 +1789,311315.0315049,0.44622,0.98795 +1790,309168.8906989,0.45093,0.98749 +1791,305545.8357897,0.41789,0.97663 +1792,304511.9959176,0.34594,0.94557 +1793,300838.1720861,0.25624,0.94142 +1794,303459.6946191,0.119000,0.96125 +1795,315071.9317546,0.0038619,0.97121 +1796,323753.4174669,0,0.97456 +1797,306981.2116837,0,0.97543 +1798,293135.1419672,0,0.98131 +1799,283152.1257016,0,0.98694 +1800,264404.5473055,0,0.99107 +1801,252141.5448933,0,0.99328 +1802,246441.5795267,0,0.99369 +1803,242841.6014004,0,0.99305 +1804,243150.8302908,0,0.98735 +1805,246676.9627119,0,0.98099 +1806,253479.9982993,0,0.9807 +1807,276686.011144,0,0.98453 +1808,297985.8817245,0.055887,0.98447 +1809,307613.515534,0.13119,0.9854 +1810,305596.604712,0.11573,0.98675 +1811,304101.2291826,0.15075,0.99004 +1812,305061.2233496,0.17091,0.99237 +1813,297533.5767805,0.17548,0.99431 +1814,293236.6798118,0.15049,0.99299 +1815,289138.2431757,0.11495,0.98891 +1816,289184.3967414,0.075575,0.9803 +1817,287204.408772,0.083335,0.97109 +1818,293264.3719512,0.048585,0.9523 +1819,309155.0446291,0.00070852,0.91055 +1820,320458.0528744,0,0.8493 +1821,303515.078898,0,0.8221 +1822,284869.0383465,0,0.81326 +1823,270146.0508813,0,0.80874 +1824,249016.948494,0,0.79842 +1825,234833.9577478,0,0.78741 +1826,229877.0647893,0,0.76231 +1827,228510.9192439,0,0.71700 +1828,230841.6743128,0,0.6815 +1829,235807.7979845,0,0.61256 +1830,245643.1228397,0,0.58785 +1831,268106.0632764,0,0.65251 +1832,288196.710435,0.062591,0.72611 +1833,296961.2725655,0.14607,0.78603 +1834,293910.5218713,0.14147,0.86821 +1835,292802.836294,0.17517,0.91819 +1836,294441.2878771,0.18905,0.92505 +1837,290716.6951234,0.18435,0.9224 +1838,288169.0182956,0.1662,0.9092 +1839,284615.193735,0.13657,0.88396 +1840,284107.5045121,0.098506,0.83436 +1841,283909.0441795,0.14062,0.78115 +1842,290601.3112091,0.093804,0.69824 +1843,305148.9151245,0.0048182,0.61865 +1844,316728.8447641,0,0.55783 +1845,298641.2623578,0,0.52063 +1846,280535.2185252,0,0.50377 +1847,264501.4697936,0,0.46983 +1848,243681.5962966,0,0.4187 +1849,226895.5444437,0,0.33417 +1850,218334.0580023,0,0.21976 +1851,217424.8327576,0,0.12975 +1852,218467.9033429,0,0.07301 +1853,223129.4134808,0,0.040784 +1854,233366.2743578,0,0.02586 +1855,255487.6784081,0,0.024512 +1856,278882.9208724,0.071439,0.032656 +1857,293476.6783535,0.1624,0.047924 +1858,295908.9712671,0.17111,0.065704 +1859,296227.4308705,0.21132,0.07998 +1860,294805.9010463,0.22797,0.080586 +1861,288487.477899,0.22337,0.071451 +1862,279875.2225354,0.19986,0.073898 +1863,271327.5821638,0.16162,0.090563 +1864,269790.6684252,0.11546,0.10275 +1865,270192.204447,0.13859,0.11888 +1866,276990.6246778,0.088639,0.15387 +1867,287005.9484394,0.0060818,0.22915 +1868,299647.4100905,0,0.27567 +1869,281481.3666225,0,0.28934 +1870,262406.0979098,0,0.29677 +1871,251536.9331824,0,0.27959 +1872,231506.2856592,0,0.25829 +1873,213063.3207969,0,0.24132 +1874,205706.4424209,0,0.21845 +1875,202083.3875118,0,0.19419 +1876,206144.9012952,0,0.17975 +1877,208941.807378,0,0.17972 +1878,209615.6494375,0,0.18536 +1879,208231.0424659,0,0.15009 +1880,216847.9131861,0.099548,0.11558 +1881,231427.8245975,0.28875,0.2182 +1882,241775.4540323,0.45758,0.23368 +1883,246132.3506364,0.54939,0.19288 +1884,247221.5747874,0.5844,0.16598 +1885,241946.2222254,0.56786,0.14754 +1886,234095.5006962,0.52268,0.12635 +1887,227223.2347603,0.44159,0.10397 +1888,227758.6161226,0.33334,0.09322 +1889,227929.3843158,0.26286,0.097133 +1890,236287.795068,0.13697,0.16057 +1891,247784.6482892,0.0097739,0.25255 +1892,253987.6875222,0,0.29127 +1893,239389.3146845,0,0.29605 +1894,228063.2296564,0,0.30862 +1895,223895.5626718,0,0.30241 +1896,209647.9569335,0,0.28062 +1897,196369.5760754,0,0.26682 +1898,184392.7257707,0,0.26396 +1899,176338.9285523,0,0.26449 +1900,177838.9194382,0,0.24338 +1901,184665.0318084,0,0.24462 +1902,184771.1850096,0,0.27484 +1903,179283.5260453,0,0.25719 +1904,181051.2076124,0.10862,0.22221 +1905,193937.2831619,0.29657,0.30484 +1906,204137.2211864,0.46344,0.45737 +1907,213907.9310496,0.56494,0.48561 +1908,223766.3326877,0.61324,0.50079 +1909,223134.0288373,0.61158,0.52163 +1910,213547.933237,0.55552,0.54237 +1911,204954.1392996,0.4628,0.57474 +1912,200597.2426955,0.34392,0.58424 +1913,200948.009795,0.2663,0.58716 +1914,212057.1730642,0.13873,0.71363 +1915,231155.5185597,0.010521,0.77203 +1916,245555.4310649,0,0.76199 +1917,235498.5690941,0,0.74325 +1918,227694.0011306,0,0.76083 +1919,226858.6215911,0,0.76401 +1920,210414.1061245,0,0.75347 +1921,199351.096421,0,0.74021 +1922,196064.9625417,0,0.73474 +1923,198058.7965808,0,0.74601 +1924,200897.2408727,0,0.73802 +1925,212260.2487534,0,0.72742 +1926,227957.0764552,0,0.71933 +1927,255893.8297865,0,0.67021 +1928,282321.3615186,0.10658,0.5975 +1929,296033.5858945,0.25772,0.58308 +1930,296642.812962,0.36495,0.67794 +1931,298913.5683955,0.43802,0.72244 +1932,303893.5381369,0.46787,0.74959 +1933,300141.2532437,0.45801,0.73869 +1934,297713.5756868,0.40594,0.70921 +1935,292442.8384814,0.32735,0.65755 +1936,272375.2681057,0.23381,0.56717 +1937,284490.5791076,0.17567,0.55407 +1938,287089.0248577,0.089198,0.53526 +1939,299845.8704231,0.0070192,0.47900 +1940,316456.5387263,0,0.39236 +1941,300672.0192495,0,0.3493 +1942,281753.6726603,0,0.37804 +1943,265613.7707274,0,0.34956 +1944,242832.3706873,0,0.25089 +1945,230006.2947733,0,0.20897 +1946,222917.1070785,0,0.22212 +1947,219617.1271294,0,0.24628 +1948,220724.8127067,0,0.25894 +1949,226018.6266949,0,0.26787 +1950,235687.7987136,0,0.2947 +1951,259087.6565344,0.00095465,0.28412 +1952,281665.9808854,0.1133,0.23956 +1953,290970.5397349,0.26753,0.36819 +1954,286336.7217364,0.38171,0.4127 +1955,281079.8306007,0.46782,0.39294 +1956,280904.447051,0.51133,0.31296 +1957,278172.1559603,0.51317,0.1608 +1958,276044.4765805,0.50489,0.052453 +1959,273570.6454578,0.45916,0.01475 +1960,271322.9668072,0.37726,0 +1961,269199.902784,0.28013,0 +1962,276921.3943292,0.14466,0.0067956 +1963,292433.6077682,0.01401,0.01774 +1964,313068.8670023,0,0.038049 +1965,299878.1779191,0,0.07027 +1966,283013.6650045,0,0.10518 +1967,267362.9908683,0,0.13455 +1968,245666.1996226,0,0.15676 +1969,230698.5982591,0,0.19767 +1970,221929.420772,0,0.25953 +1971,220738.6587764,0,0.31673 +1972,223212.4898991,0,0.33964 +1973,228880.1477697,0,0.24719 +1974,237681.6327528,0,0.20093 +1975,258099.970228,0.0017668,0.18879 +1976,276898.3175464,0.13148,0.17372 +1977,286096.7231947,0.32108,0.19131 +1978,286225.9531787,0.48571,0.3259 +1979,284476.7330378,0.57399,0.39025 +1980,286073.6464118,0.60748,0.42245 +1981,281490.5973357,0.59154,0.43126 +1982,278832.1519501,0.4777,0.40222 +1983,274124.4882465,0.33779,0.35755 +1984,270644.5093911,0.1967,0.29358 +1985,268798.3667622,0.14447,0.27459 +1986,273529.1072487,0.073363,0.32961 +1987,285312.1125774,0.0060146,0.34231 +1988,301996.6265857,0,0.28509 +1989,286992.1023697,0,0.22575 +1990,268087.6018501,0,0.20388 +1991,250840.01434,0,0.17689 +1992,230024.7561996,0,0.17822 +1993,215232.5383858,0,0.21781 +1994,213547.933237,0,0.23891 +1995,210354.1064891,0,0.16869 +1996,212878.706534,0,0.12343 +1997,219603.2810596,0,0.10545 +1998,230061.6790521,0,0.096784 +1999,253060.0008512,0.0043367,0.077738 +2000,274378.332858,0.11,0.054913 +2001,285385.9582826,0.19629,0.043691 +2002,285492.1114837,0.19618,0.038628 +2003,287439.7919572,0.28701,0.038832 +2004,291159.7693543,0.36762,0.046091 +2005,286585.9509913,0.42628,0.047325 +2006,283355.2013908,0.39656,0.041322 +2007,282058.286194,0.34006,0.032212 +2008,279358.3025993,0.2623,0.021295 +2009,276584.4732995,0.20878,0.012879 +2010,281444.4437699,0.11594,0.0099149 +2011,291916.6878322,0.014157,0.0087041 +2012,306330.446407,0,0.0075473 +2013,289867.4695141,0,0.0063584 +2014,269352.2095509,0,0.0063343 +2015,251573.856035,0,0.0070717 +2016,228838.6095605,0,0.0092302 +2017,216750.9906981,0,0.013359 +2018,210349.4911325,0,0.016804 +2019,206938.7426257,0,0.018982 +2020,209883.3401187,0,0.021741 +2021,217004.8353095,0,0.022672 +2022,228206.3057101,0,0.019718 +2023,249866.1741033,0.0055062,0.016104 +2024,273556.7993881,0.11624,0.016345 +2025,286558.2588519,0.20436,0.017674 +2026,287407.4844612,0.20883,0.021511 +2027,289156.704602,0.29765,0.02998 +2028,292193.6092265,0.37415,0.045354 +2029,292359.7620631,0.42986,0.070074 +2030,287029.0252222,0.43351,0.095573 +2031,281601.3658934,0.40279,0.12486 +2032,280664.4485092,0.33883,0.14161 +2033,277189.0850104,0.24102,0.18804 +2034,278324.4627272,0.12261,0.30093 +2035,283858.2752572,0.014388,0.41398 +2036,288335.1711322,0,0.46674 +2037,270866.0465066,0,0.48129 +2038,253055.3854946,0,0.47751 +2039,240063.156744,0,0.44021 +2040,221527.8847502,0,0.39222 +2041,206680.2826576,0,0.35568 +2042,197163.4174058,0,0.33822 +2043,195783.4257908,0,0.32649 +2044,199300.3274987,0,0.32768 +2045,204538.7572081,0,0.31671 +2046,204054.1447681,0,0.30176 +2047,202752.6142147,0.0087325,0.30237 +2048,208595.6556351,0.13496,0.34113 +2049,220530.9677307,0.26035,0.43095 +2050,228330.9203376,0.32244,0.46462 +2051,231543.2085118,0.36383,0.4192 +2052,235590.8762256,0.36271,0.38933 +2053,232046.2823782,0.32917,0.36004 +2054,227223.2347603,0.31183,0.32888 +2055,221689.4222303,0.2728,0.30284 +2056,218758.6708069,0.21545,0.31497 +2057,219049.438271,0.15113,0.31031 +2058,228026.3068038,0.075803,0.37056 +2059,240663.1530984,0.0081447,0.4768 +2060,252843.0790923,0,0.50456 +2061,238526.2430055,0,0.48296 +2062,225017.0943188,0,0.47636 +2063,218495.5954823,0,0.47005 +2064,202203.3867826,0,0.46874 +2065,187798.8589209,0,0.40205 +2066,178369.685444,0,0.3029 +2067,178729.6832567,0,0.25615 +2068,182809.6584664,0,0.22262 +2069,183017.3495122,0,0.19355 +2070,179385.0638899,0,0.17948 +2071,177654.3051753,0.01061,0.18637 +2072,185283.4895891,0.12741,0.18900 +2073,195829.5793565,0.20208,0.18185 +2074,204806.4478893,0.18398,0.15115 +2075,215717.1508259,0.21775,0.11663 +2076,217512.5245325,0.23084,0.082119 +2077,208978.7302306,0.225000,0.057646 +2078,201386.4686694,0.19178,0.044415 +2079,198224.9494174,0.14722,0.03864 +2080,197832.6441088,0.09866,0.030527 +2081,202757.2295713,0.067857,0.025044 +2082,214789.4641549,0.032937,0.028329 +2083,223941.7162375,0.0025815,0.039347 +2084,233592.4268298,0,0.04572 +2085,227527.848294,0,0.042513 +2086,224615.558297,0,0.040661 +2087,210967.9489131,0,0.036966 +2088,195280.3519244,0,0.031445 +2089,189649.6169064,0,0.025273 +2090,190549.6114379,0,0.019145 +2091,193226.5182498,0,0.014621 +2092,200694.1651835,0,0.010102 +2093,211194.1013852,0,0.0096939 +2094,240792.3830824,0,0.014173 +2095,268881.4431805,0.012189,0.018816 +2096,286521.3359993,0.13466,0.023715 +2097,293204.3723158,0.21683,0.026869 +2098,298327.4181109,0.2129,0.024106 +2099,301059.7092016,0.24994,0.018494 +2100,296347.4301414,0.26333,0.016156 +2101,291496.6903841,0.25456,0.019942 +2102,283355.2013908,0.25685,0.034224 +2103,276579.8579429,0.23925,0.067925 +2104,269130.6724354,0.20166,0.14258 +2105,269167.595288,0.13787,0.23938 +2106,273819.8747127,0.068214,0.34471 +2107,280710.602075,0.008309,0.47964 +2108,291187.4614938,0,0.55723 +2109,278892.1515855,0,0.59022 +2110,262946.0946287,0,0.60714 +2111,240127.771736,0,0.60099 +2112,223941.7162375,0,0.59178 +2113,215375.6144396,0,0.59511 +2114,211494.0995624,0,0.61623 +2115,213238.7043466,0,0.63611 +2116,220069.4320734,0,0.63311 +2117,229397.0677058,0,0.65138 +2118,250835.3989834,0,0.69091 +2119,272970.6491035,0.016255,0.67932 +2120,283115.2028491,0.16547,0.74722 +2121,283470.5853051,0.32287,0.82664 +2122,283309.0478251,0.43687,0.85098 +2123,283553.6617234,0.53965,0.8604 +2124,277821.3888608,0.60329,0.86316 +2125,274092.1807505,0.62578,0.86226 +2126,270464.5104848,0.57785,0.85947 +2127,268295.2928959,0.49424,0.86598 +2128,263924.550222,0.3827,0.86217 +2129,265027.6204428,0.26262,0.80957 +2130,269338.3634812,0.133000,0.81269 +2131,272499.8827331,0.019582,0.86002 +2132,276372.1668971,0,0.86428 +2133,262719.9421567,0,0.84300 +2134,248878.4877968,0,0.81355 +2135,229766.2962315,0,0.7733 +2136,215421.7680053,0,0.7212 +2137,208475.6563642,0,0.67169 +2138,206297.2080621,0,0.63398 +2139,208743.3470454,0,0.60915 +2140,215140.2312544,0,0.57479 +2141,223101.7213413,0,0.53993 +2142,245938.5056604,0,0.52098 +2143,269186.0567143,0.018685,0.44457 +2144,282455.2068592,0.18484,0.4637 +2145,283645.9688549,0.37621,0.5838 +2146,282925.9732296,0.53981,0.59501 +2147,286119.7999775,0.66201,0.55781 +2148,282921.357873,0.73748,0.50992 +2149,280096.7596509,0.76474,0.45200 +2150,277406.0067693,0.7172,0.4155 +2151,271212.1982495,0.62595,0.40837 +2152,268553.7528639,0.49761,0.42204 +2153,269319.9020549,0.34926,0.3894 +2154,276727.5493532,0.18332,0.47779 +2155,281749.0573037,0.031732,0.61066 +2156,285907.4935752,0,0.62484 +2157,270256.819439,0,0.5744 +2158,254693.8370777,0,0.57213 +2159,234266.2688894,0,0.56052 +2160,232493.9719657,0,0.52525 +2161,226341.701655,0,0.47996 +2162,222755.5695984,0,0.44934 +2163,224938.6332571,0,0.43400 +2164,231732.4381313,0,0.39356 +2165,240473.9234789,0,0.37555 +2166,263518.3988437,0,0.3764 +2167,283964.4284583,0.023574,0.28327 +2168,294210.5200485,0.19287,0.21344 +2169,301396.6302314,0.38245,0.30341 +2170,309076.5835674,0.5439,0.38312 +2171,312067.3346262,0.65256,0.38361 +2172,306588.906375,0.71291,0.31064 +2173,301115.0934805,0.7258,0.23057 +2174,298359.7256069,0.67437,0.18062 +2175,292502.8381168,0.58256,0.16817 +2176,285492.1114837,0.45786,0.18897 +2177,278209.0788129,0.30929,0.20946 +2178,278684.4605398,0.15627,0.31098 +2179,278075.2334723,0.026256,0.45124 +2180,279510.6093662,0,0.49853 +2181,265129.1582874,0,0.49062 +2182,253530.7672216,0,0.44822 +2183,234330.8838814,0,0.38844 +2184,218218.674088,0,0.33825 +2185,211521.7917018,0,0.29303 +2186,210123.3386604,0,0.25231 +2187,209638.7262204,0,0.21277 +2188,208217.1963961,0,0.16712 +2189,203001.8434696,0,0.13531 +2190,190152.6907727,0,0.12701 +2191,187028.0943734,0.026429,0.096624 +2192,194486.510594,0.20039,0.04729 +2193,201303.3922511,0.38977,0.048483 +2194,205844.9031181,0.54962,0.19899 +2195,213875.6235536,0.66243,0.34615 +2196,212223.3259008,0.72782,0.43955 +2197,203574.1476846,0.62846,0.49415 +2198,192437.292276,0.58246,0.51819 +2199,190037.3068584,0.50138,0.53248 +2200,189769.6161772,0.39259,0.50224 +2201,198529.5629512,0.26387,0.40659 +2202,210294.1068536,0.13325,0.4902 +2203,218661.7483189,0.022669,0.61373 +2204,230597.0604145,0,0.64615 +2205,225146.3243028,0,0.62531 +2206,221610.9611685,0,0.62813 +2207,205960.2870324,0,0.62887 +2208,194417.2802454,0,0.59712 +2209,188108.0878113,0,0.53765 +2210,186603.4815687,0,0.46491 +2211,185731.1791766,0,0.39498 +2212,190037.3068584,0,0.2998 +2213,193074.2114829,0,0.22953 +2214,192289.6008656,0,0.19324 +2215,198141.8729991,0.025538,0.1117 +2216,211789.482383,0.17227,0.057826 +2217,220526.3523741,0.33106,0.049057 +2218,222954.029931,0.46501,0.071914 +2219,225598.6292469,0.55158,0.1026 +2220,223041.7217059,0.59837,0.10904 +2221,217724.8309348,0.60493,0.10694 +2222,215643.3051207,0.53172,0.10449 +2223,215407.9219356,0.4289,0.09713 +2224,216529.4535826,0.30954,0.082948 +2225,220840.196621,0.21353,0.059019 +2226,227472.4640152,0.11185,0.06383 +2227,231155.5185597,0.021084,0.083274 +2228,234686.2663374,0,0.10354 +2229,228460.1503216,0,0.12301 +2230,223807.8708969,0,0.15199 +2231,206804.8972851,0,0.17141 +2232,189968.0765098,0,0.16882 +2233,178854.2978841,0,0.16069 +2234,175748.162911,0,0.15227 +2235,176698.9263649,0,0.14024 +2236,178388.1468703,0,0.11704 +2237,178037.3797708,0,0.10298 +2238,175480.4722299,0,0.10336 +2239,175812.7779031,0.028052,0.091906 +2240,188795.7759405,0.17622,0.060414 +2241,198035.719798,0.33124,0.080105 +2242,205041.8310745,0.46108,0.25891 +2243,212887.9372472,0.51699,0.41456 +2244,206001.8252415,0.52599,0.53954 +2245,195248.0444284,0.49401,0.64457 +2246,190277.3054002,0.39216,0.68752 +2247,186857.3261802,0.27244,0.69991 +2248,185781.9480989,0.15459,0.64317 +2249,192441.9076325,0.10552,0.46077 +2250,202789.5370673,0.0542,0.38567 +2251,208180.2735436,0.0092449,0.36621 +2252,214457.1584817,0,0.35955 +2253,213358.7036175,0,0.35181 +2254,211798.7130961,0,0.3239 +2255,196701.8817486,0,0.32503 +2256,184346.572205,0,0.52337 +2257,179675.8313539,0,0.58992 +2258,177677.3819582,0,0.2918 +2259,176265.0828471,0,0.063604 +2260,179768.1384854,0,0.033168 +2261,179657.3699277,0,0.059539 +2262,176666.6188689,0,0.10366 +2263,179288.1414019,0.033685,0.10762 +2264,189428.0797909,0.17164,0.14255 +2265,201335.6997471,0.2923,0.26423 +2266,208438.7335116,0.37092,0.30695 +2267,213654.0864381,0.43954,0.35228 +2268,210723.3350148,0.47475,0.45358 +2269,203269.5341508,0.47742,0.5294 +2270,195308.0440638,0.43213,0.5944 +2271,190438.8428802,0.36247,0.62777 +2272,188301.9327873,0.27581,0.60973 +2273,195695.7340159,0.18108,0.52678 +2274,205734.1345603,0.090334,0.30742 +2275,212509.4780082,0.01655,0.21734 +2276,224070.9462215,0,0.14812 +2277,223484.7959368,0,0.10786 +2278,221509.4233239,0,0.092437 +2279,205752.5959866,0,0.08996 +2280,192861.9050806,0,0.096696 +2281,189695.7704721,0,0.11659 +2282,191929.603053,0,0.14259 +2283,194389.588106,0,0.18387 +2284,202337.2321232,0,0.23573 +2285,215509.4597801,0,0.31954 +2286,244955.4347105,0,0.42035 +2287,269209.1334972,0.037219,0.41677 +2288,281841.3644351,0.17023,0.52102 +2289,281661.3655288,0.27007,0.84027 +2290,281361.3673516,0.31854,0.92391 +2291,282685.9746878,0.35053,0.94102 +2292,276395.24368,0.34646,0.95454 +2293,271147.5832575,0.31234,0.96138 +2294,265023.0050862,0.25745,0.95746 +2295,260583.0320638,0.19025,0.9361 +2296,255321.5255715,0.12139,0.87229 +2297,258695.3512258,0.078557,0.82289 +2298,266084.5370978,0.038059,0.82984 +2299,272047.577789,0.0060385,0.85400 +2300,280479.8342464,0,0.88872 +2301,272975.26446,0,0.89515 +2302,259124.579387,0,0.87385 +2303,238766.2415472,0,0.82427 +2304,224218.6376318,0,0.76621 +2305,218094.0594606,0,0.72224 +2306,213012.5518746,0,0.71535 +2307,213469.4721753,0,0.8114 +2308,218735.5940241,0,0.89504 +2309,227167.8504814,0,0.93109 +2310,255912.2912128,0,0.94126 +2311,276806.0104149,0.041851,0.94745 +2312,285658.2643203,0.16471,0.96051 +2313,284116.7352252,0.23845,0.98125 +2314,282016.7479849,0.24682,0.99227 +2315,283045.9725005,0.29631,0.99471 +2316,278601.3841215,0.32392,0.99434 +2317,272827.5730497,0.32868,0.99463 +2318,266970.6855597,0.33456,0.99501 +2319,265766.0774943,0.31651,0.9941 +2320,264372.2398095,0.27354,0.98132 +2321,265909.1535481,0.19445,0.93268 +2322,270930.6614986,0.10765,0.88019 +2323,277646.0053111,0.025258,0.83252 +2324,287605.9447938,0,0.81656 +2325,277064.470383,0,0.81359 +2326,258076.8934451,0,0.81577 +2327,235664.7219307,0,0.79293 +2328,216963.2971004,0,0.74426 +2329,213894.0849799,0,0.68568 +2330,211835.6359487,0,0.60075 +2331,213109.4743626,0,0.47434 +2332,219127.8993327,0,0.34982 +2333,230749.3671814,0,0.24067 +2334,257010.746077,0,0.16068 +2335,277309.0842813,0.045714,0.094505 +2336,287739.7901344,0.1727,0.097244 +2337,288496.7086122,0.25243,0.094746 +2338,292627.4527443,0.27063,0.099613 +2339,294330.5193194,0.31226,0.10979 +2340,289539.7791975,0.32796,0.12224 +2341,285432.1118483,0.3194,0.13794 +2342,279939.8375274,0.30662,0.14915 +2343,277756.7738688,0.27407,0.14529 +2344,273921.4125573,0.22384,0.15088 +2345,274036.7964716,0.16009,0.15944 +2346,279413.6868782,0.089317,0.16972 +2347,284112.1198686,0.021316,0.24701 +2348,288164.402939,0,0.44153 +2349,278167.5406037,0,0.66512 +2350,262719.9421567,0,0.73013 +2351,240658.5377418,0,0.72207 +2352,225252.477504,0,0.65192 +2353,216940.2203175,0,0.56199 +2354,217697.1387953,0,0.52401 +2355,219137.1300459,0,0.52198 +2356,225021.7096754,0,0.5437 +2357,233767.8103796,0,0.57523 +2358,257130.7453478,0,0.58706 +2359,281259.8295071,0.049892,0.6098 +2360,292345.9159934,0.20033,0.6257 +2361,293993.5982896,0.33028,0.52300 +2362,296481.275482,0.4215,0.38169 +2363,298221.2649097,0.4526,0.32531 +2364,291704.3814298,0.43855,0.32505 +2365,283627.5074286,0.38555,0.31544 +2366,277290.622855,0.34407,0.2896 +2367,273192.1862189,0.28371,0.25167 +2368,270141.4355247,0.21176,0.19626 +2369,271401.4278689,0.13574,0.14184 +2370,273898.3357745,0.066671,0.087457 +2371,275462.9416524,0.013705,0.065315 +2372,275352.1730947,0,0.074418 +2373,267889.1415175,0,0.091725 +2374,254564.6070937,0,0.1008 +2375,234746.2659729,0,0.11777 +2376,217069.4503015,0,0.15748 +2377,209366.4201826,0,0.2366 +2378,204386.4504413,0,0.31800 +2379,207598.7386155,0,0.35026 +2380,211046.4099749,0,0.34081 +2381,210109.4925907,0,0.2972 +2382,205757.2113432,0,0.26269 +2383,212560.2469305,0.055296,0.22092 +2384,228746.3024291,0.17788,0.21355 +2385,240007.7724651,0.2429,0.28266 +2386,242883.1396096,0.23994,0.3376 +2387,246538.5020147,0.24477,0.36991 +2388,240072.3874572,0.22081,0.41228 +2389,232526.2794617,0.17468,0.45299 +2390,227786.3082621,0.21495,0.44795 +2391,226618.6230493,0.23479,0.39413 +2392,224878.6336216,0.22777,0.37264 +2393,231233.9796215,0.18916,0.34815 +2394,236878.5607092,0.1232,0.22586 +2395,239121.6240033,0.038428,0.15905 +2396,238175.475906,0,0.11172 +2397,232364.7419817,0,0.059709 +2398,227149.3890551,0,0.030962 +2399,210538.7207519,0,0.024484 +2400,197306.4934596,0,0.029197 +2401,189783.462247,0,0.043008 +2402,188020.3960364,0,0.083172 +2403,191084.9928003,0,0.12697 +2404,194740.3552055,0,0.15698 +2405,193928.0524488,0,0.15775 +2406,188565.0081119,0,0.13744 +2407,188140.3953073,0.05901,0.090011 +2408,197029.5720652,0.20739,0.073127 +2409,207137.2029583,0.33158,0.10155 +2410,218218.674088,0.41823,0.12156 +2411,229240.1455823,0.4827,0.1064 +2412,225280.1696434,0.51177,0.09197 +2413,215514.0751367,0.50679,0.084369 +2414,207857.1985835,0.48586,0.077694 +2415,204044.9140549,0.43568,0.071005 +2416,202567.9999518,0.35952,0.059232 +2417,210271.0300708,0.24558,0.046083 +2418,220304.8152586,0.13246,0.036011 +2419,226475.5469956,0.034329,0.034062 +2420,234824.7270346,0,0.028804 +2421,236112.4115182,0,0.021181 +2422,235286.2626918,0,0.015397 +2423,219534.0507111,0,0.010212 +2424,208858.7309597,0,0.006829 +2425,204575.6800607,0,0.0058889 +2426,202747.9988581,0,0.0065699 +2427,207040.2804702,0,0.0079075 +2428,215135.6158978,0,0.0089358 +2429,228197.074997,0,0.008777 +2430,262350.7136309,0,0.0080298 +2431,288492.0932556,0.064371,0.0074277 +2432,300164.3300266,0.22105,0.0072224 +2433,302545.8540178,0.36767,0.016066 +2434,307055.0573888,0.48787,0.023619 +2435,310161.1923619,0.48011,0.029797 +2436,304475.073065,0.41388,0.036621 +2437,299550.4876025,0.30417,0.041037 +2438,294455.1339468,0.29317,0.043072 +2439,292595.1452483,0.2636,0.043184 +2440,286885.9491685,0.21762,0.049476 +2441,285625.9568243,0.14686,0.054552 +2442,285182.8825934,0.078023,0.058648 +2443,285875.1860792,0.020071,0.075379 +2444,289336.7035083,0,0.075623 +2445,282621.3596958,0,0.050698 +2446,267561.4512009,0,0.025585 +2447,244678.5133162,0,0.013863 +2448,228427.8428256,0,0.010781 +2449,217115.6038673,0,0.013499 +2450,214378.69742,0,0.018234 +2451,217812.5227097,0,0.021796 +2452,224915.5564742,0,0.023298 +2453,237700.0941791,0,0.027438 +2454,267224.5301711,0,0.037252 +2455,290905.9247429,0.038269,0.032918 +2456,299278.1815647,0.11627,0.043395 +2457,299139.7208676,0.16988,0.10087 +2458,300072.0228951,0.19283,0.18051 +2459,302735.0836373,0.24414,0.25561 +2460,301355.0920222,0.2829,0.28622 +2461,298553.5705829,0.30613,0.26745 +2462,292392.0695591,0.26652,0.21587 +2463,289816.7005918,0.21253,0.14926 +2464,283138.2796319,0.15218,0.068398 +2465,283212.1253371,0.097517,0.02959 +2466,285293.6511511,0.048269,0.043071 +2467,287347.4848257,0.010783,0.10226 +2468,290596.6958525,0,0.18713 +2469,282215.2083175,0,0.2432 +2470,266084.5370978,0,0.29303 +2471,240501.6156184,0,0.30183 +2472,223600.1798511,0,0.2773 +2473,217267.9106341,0,0.25682 +2474,214092.5453125,0,0.23619 +2475,217503.2938193,0,0.21987 +2476,222755.5695984,0,0.20607 +2477,234201.6538974,0,0.19016 +2478,267427.6058603,0,0.16817 +2479,290070.5452033,0.05016,0.12723 +2480,298484.3402343,0.14499,0.098117 +2481,298498.186304,0.21512,0.10698 +2482,300662.7885364,0.25153,0.1689 +2483,305190.4533337,0.2924,0.21989 +2484,303681.2317346,0.31238,0.24072 +2485,301322.7845262,0.31128,0.23639 +2486,296125.893026,0.27909,0.20828 +2487,292073.6099556,0.2318,0.17441 +2488,285002.8836871,0.17486,0.1306 +2489,285478.265414,0.11187,0.075652 +2490,287389.0230349,0.055674,0.072354 +2491,290149.006265,0.013063,0.10374 +2492,291284.3839818,0,0.14908 +2493,282076.7476203,0,0.18717 +2494,265913.7689046,0,0.20276 +2495,243640.0580874,0,0.16774 +2496,223835.5630363,0,0.11712 +2497,215537.1519196,0,0.077535 +2498,214267.9288622,0,0.05116 +2499,216990.9892398,0,0.036408 +2500,221112.5026587,0,0.030665 +2501,232738.585864,0,0.039444 +2502,266282.9974304,0,0.074732 +2503,288344.4018453,0.078439,0.087711 +2504,295973.5862591,0.22754,0.081273 +2505,295650.511299,0.35636,0.15838 +2506,298415.1098857,0.45207,0.32538 +2507,302795.0832727,0.49896,0.32852 +2508,300422.7899946,0.50738,0.26937 +2509,297150.502185,0.48034,0.18513 +2510,291552.074663,0.37818,0.11935 +2511,286705.9502622,0.2601,0.08816 +2512,280687.5252921,0.14555,0.087623 +2513,281379.8287779,0.092981,0.087077 +2514,285132.1136711,0.046213,0.13601 +2515,286059.8003421,0.011081,0.2492 +2516,287873.6354749,0,0.30767 +2517,280202.912852,0,0.23372 +2518,262779.9417921,0,0.14097 +2519,236841.6378566,0,0.093063 +2520,219294.0521693,0,0.069746 +2521,213095.6282929,0,0.063227 +2522,213331.0114781,0,0.071448 +2523,216026.3797162,0,0.089835 +2524,223840.1783929,0,0.11837 +2525,236781.6382212,0,0.15419 +2526,264644.5458473,0,0.17703 +2527,286609.0277742,0.05228,0.20778 +2528,297376.654657,0.13988,0.22204 +2529,299135.105511,0.20806,0.19509 +2530,300847.4027993,0.2472,0.16998 +2531,299661.2561602,0.27962,0.15192 +2532,292308.9931408,0.2917,0.17981 +2533,284010.5820241,0.28417,0.25272 +2534,278735.2294621,0.31511,0.33083 +2535,272162.9617033,0.32125,0.41487 +2536,267847.6033084,0.29837,0.49782 +2537,270930.6614986,0.20467,0.45953 +2538,273662.9525893,0.11299,0.54231 +2539,273662.9525893,0.034708,0.67265 +2540,273639.8758064,0,0.72777 +2541,269255.2870629,0,0.72598 +2542,259839.9596557,0,0.72055 +2543,238623.1654935,0,0.70394 +2544,219478.6664322,0,0.65157 +2545,209537.1883758,0,0.55456 +2546,207654.1228943,0,0.44816 +2547,212324.8637454,0,0.35959 +2548,214337.1592108,0,0.2725 +2549,212661.7847751,0,0.21769 +2550,205521.828158,0,0.2287 +2551,213026.3979443,0.091119,0.22526 +2552,227338.6186746,0.19612,0.2456 +2553,233569.350047,0.22971,0.29655 +2554,233772.4257362,0.18803,0.26577 +2555,232230.8966411,0.24211,0.2289 +2556,226360.1630813,0.285000,0.18487 +2557,218352.5194286,0.31314,0.14768 +2558,213487.9336015,0.32302,0.1207 +2559,210755.6425108,0.3109,0.093849 +2560,209403.3430352,0.27499,0.068836 +2561,215994.0722202,0.18237,0.046925 +2562,226415.5473601,0.096636,0.029669 +2563,229743.2194487,0.028362,0.031186 +2564,231252.4410478,0,0.033537 +2565,230435.5229345,0,0.030125 +2566,225598.6292469,0,0.035205 +2567,209334.1126866,0,0.0424 +2568,194601.8945083,0,0.043333 +2569,183788.1140597,0,0.04236 +2570,177908.1497868,0,0.041358 +2571,181508.1279131,0,0.037974 +2572,186732.7115528,0,0.025959 +2573,188145.0106638,0,0.020102 +2574,182121.9703372,0,0.017798 +2575,183746.5758506,0.04383,0.0129 +2576,190494.2271591,0.10396,0.015031 +2577,196918.8035075,0.14059,0.021927 +2578,203167.9963062,0.15006,0.028821 +2579,210538.7207519,0.20664,0.039158 +2580,202849.5367027,0.25689,0.0456 +2581,190521.9192985,0.29432,0.046904 +2582,182191.2006858,0.28913,0.042751 +2583,179758.9077722,0.26593,0.035662 +2584,180668.133017,0.22512,0.022707 +2585,192095.7558896,0.14815,0.012512 +2586,207561.8157629,0.077585,0.0057887 +2587,216598.6839312,0.022454,0 +2588,224874.018265,0,0 +2589,229267.8377217,0,0.012991 +2590,225483.2453326,0,0.02641 +2591,207626.4307549,0,0.037402 +2592,193554.2085664,0,0.039027 +2593,185772.7173858,0,0.033559 +2594,188126.5492376,0,0.024624 +2595,193397.286443,0,0.016773 +2596,202074.1567986,0,0.012658 +2597,218541.7490481,0,0.0099363 +2598,253443.0754467,0,0.0084459 +2599,276524.473664,0.069904,0.0098381 +2600,282750.5896799,0.1513,0.015433 +2601,280479.8342464,0.19097,0.024984 +2602,281001.369539,0.18386,0.044673 +2603,282224.4390306,0.19887,0.082051 +2604,278956.7665775,0.19513,0.13188 +2605,276169.091208,0.17537,0.16202 +2606,271179.8907535,0.13984,0.16084 +2607,267007.6084122,0.099008,0.13311 +2608,261755.3326331,0.059185,0.081338 +2609,263546.0909831,0.037946,0.051628 +2610,270542.9715465,0.018819,0.054489 +2611,273926.0279139,0.0041125,0.095279 +2612,275144.4820489,0,0.17857 +2613,271369.1203729,0,0.28927 +2614,252907.6940843,0,0.39845 +2615,226997.0822882,0,0.47187 +2616,213238.7043466,0,0.46115 +2617,206897.2044165,0,0.39715 +2618,205784.9034826,0,0.34492 +2619,210700.258232,0,0.33095 +2620,216224.8400488,0,0.32466 +2621,229092.454172,0,0.33365 +2622,257278.4367581,0,0.3797 +2623,279579.8397148,0.074844,0.44786 +2624,286489.0285033,0.17999,0.5454 +2625,282325.9768752,0.26792,0.60097 +2626,286022.8774895,0.32818,0.64294 +2627,290467.4658685,0.42377,0.69463 +2628,288113.6340167,0.50281,0.71834 +2629,286405.952085,0.55628,0.72362 +2630,281315.2137859,0.54848,0.7251 +2631,279815.2229,0.50885,0.71639 +2632,273902.951131,0.43808,0.68177 +2633,274821.4070889,0.29824,0.6127 +2634,281176.7530888,0.16485,0.47432 +2635,282644.4364787,0.054791,0.52981 +2636,279256.7647547,0,0.59171 +2637,273907.5664876,0,0.56304 +2638,256152.2897545,0,0.49944 +2639,235101.6484289,0,0.40581 +2640,217724.8309348,0,0.29639 +2641,212380.2480242,0,0.20143 +2642,212841.7836814,0,0.13433 +2643,217050.9888752,0,0.091137 +2644,221869.4211366,0,0.059439 +2645,231446.2860238,0,0.040035 +2646,261843.024408,0.0010889,0.024973 +2647,284527.5019601,0.11466,0.010864 +2648,295281.2827733,0.27895,0.0098537 +2649,295068.9763709,0.43451,0.010914 +2650,295428.9741836,0.56487,0.013266 +2651,299702.7943694,0.63245,0.013282 +2652,297630.4992685,0.65746,0.013136 +2653,296121.2776694,0.64248,0.014788 +2654,291081.3082926,0.61024,0.019337 +2655,288376.7093413,0.54513,0.02791 +2656,285058.2679659,0.45164,0.041801 +2657,286133.6460472,0.30843,0.059245 +2658,288422.862907,0.17161,0.077053 +2659,284522.8866036,0.058458,0.11023 +2660,280705.9867184,0,0.12537 +2661,277978.3109842,0,0.091866 +2662,262244.5604298,0,0.055324 +2663,237644.7099002,0,0.02992 +2664,222690.9546064,0,0.015628 +2665,213437.1646793,0,0.0083566 +2666,213132.5511455,0,0 +2667,216349.4546763,0,0 +2668,222880.1842259,0,0 +2669,233966.2707122,0,0 +2670,259987.651066,0.0040444,0 +2671,279542.9168622,0.11846,0 +2672,286724.4116885,0.28109,0 +2673,285372.1122128,0.43491,0 +2674,286336.7217364,0.56375,0 +2675,289253.62709,0.65858,0 +2676,283747.5066994,0.71566,0 +2677,278490.6155638,0.73373,0 +2678,274022.9504019,0.68533,0 +2679,270566.0483294,0.60197,0 +2680,264519.9312198,0.49002,0 +2681,267067.6080477,0.33101,0.0058348 +2682,273699.8754419,0.18173,0.017066 +2683,277378.3146299,0.061425,0.078109 +2684,274562.9471209,0,0.19348 +2685,272412.1909582,0,0.24815 +2686,256530.7489935,0,0.21831 +2687,233458.5814893,0,0.1817 +2688,216727.9139152,0,0.14833 +2689,208147.9660476,0,0.11526 +2690,206767.9744325,0,0.094953 +2691,209292.5744775,0,0.088771 +2692,216930.9896044,0,0.099135 +2693,232013.9748822,0,0.1105 +2694,256115.366902,0.0047487,0.10157 +2695,276326.0133314,0.12169,0.049543 +2696,282173.6701083,0.28193,0.051393 +2697,278402.9237889,0.43196,0.062998 +2698,275509.0952181,0.55795,0.04645 +2699,276626.0115086,0.65135,0.03599 +2700,269559.9005966,0.70795,0.02788 +2701,261372.2580376,0.72603,0.020856 +2702,254513.8381714,0.674000,0.016844 +2703,250489.2472405,0.58807,0.016367 +2704,248920.026006,0.47475,0.018439 +2705,253673.8432753,0.31732,0.025269 +2706,259096.8872476,0.17214,0.050933 +2707,261875.331904,0.057686,0.15118 +2708,260739.9541873,0,0.3072 +2709,260698.4159781,0,0.41036 +2710,247189.2672914,0,0.49583 +2711,225667.8595955,0,0.51165 +2712,206546.437317,0,0.4788 +2713,198908.0221901,0,0.43891 +2714,196074.1932548,0,0.4076 +2715,195598.8115279,0,0.38155 +2716,197348.0316687,0,0.34481 +2717,198617.2547261,0,0.33518 +2718,199346.4810645,0.0073334,0.27049 +2719,208415.6567287,0.12664,0.21343 +2720,221343.2704874,0.28736,0.26927 +2721,228026.3068038,0.43773,0.3471 +2722,228598.6110188,0.56213,0.37775 +2723,227320.1572483,0.62145,0.38156 +2724,219778.6646094,0.63991,0.38369 +2725,208729.5009757,0.62057,0.41559 +2726,199946.4774188,0.5522,0.48262 +2727,198363.4101146,0.45769,0.54802 +2728,198201.8726346,0.34687,0.59334 +2729,206260.2852096,0.22553,0.60135 +2730,216972.5278135,0.11805,0.56856 +2731,221601.7304554,0.037522,0.70708 +2732,222570.9553355,0,0.79559 +2733,225852.4738583,0,0.81805 +2734,218897.1315041,0,0.8307 +2735,201178.7776236,0,0.8267 +2736,188795.7759405,0,0.8105 +2737,180091.2134454,0,0.78863 +2738,175655.8557796,0,0.76644 +2739,177995.8415617,0,0.74035 +2740,179588.1395791,0,0.69369 +2741,181171.2068833,0,0.66322 +2742,176518.9274586,0.0086458,0.59056 +2743,177949.687996,0.1336,0.57113 +2744,184978.8760554,0.28475,0.67326 +2745,194509.5873768,0.41361,0.72925 +2746,195908.0404182,0.50912,0.75513 +2747,201820.3121871,0.5694,0.77726 +2748,196757.2660275,0.59295,0.78677 +2749,185135.7981788,0.58169,0.78515 +2750,177465.0755559,0.57193,0.77679 +2751,179657.3699277,0.53023,0.76749 +2752,182606.5827773,0.45772,0.73043 +2753,193318.8253812,0.30772,0.64676 +2754,206472.5916119,0.16894,0.50122 +2755,213391.0111135,0.05889,0.40168 +2756,220041.739934,0.00020436,0.42023 +2757,227061.6972802,0,0.37075 +2758,222621.7242578,0,0.30476 +2759,205738.7499169,0,0.25227 +2760,195266.5058547,0,0.22097 +2761,187231.1700626,0,0.19691 +2762,187291.169698,0,0.17336 +2763,189700.3858287,0,0.14391 +2764,194929.5848249,0,0.11729 +2765,210838.7189291,0,0.097546 +2766,243686.2116531,0.010022,0.072624 +2767,273109.1098006,0.11939,0.060897 +2768,287209.0241286,0.26342,0.057915 +2769,292447.453838,0.4004,0.041836 +2770,297524.3460673,0.51664,0.034166 +2771,301941.2423069,0.48054,0.033467 +2772,296467.4294123,0.38782,0.03438 +2773,292807.4516506,0.25409,0.032388 +2774,288524.4007516,0.24166,0.02977 +2775,283198.2792674,0.21558,0.029024 +2776,277479.8524745,0.17787,0.031202 +2777,278458.3080678,0.11561,0.034307 +2778,277793.6967214,0.060455,0.028518 +2779,279856.7611091,0.019106,0.027761 +2780,275056.7902741,0,0.040973 +2781,272273.7302611,0,0.065444 +2782,254555.3763806,0,0.099961 +2783,231727.8227747,0,0.13057 +2784,218754.0554504,0,0.14294 +2785,212758.7072631,0,0.1239 +2786,209574.1112284,0,0.11008 +2787,211041.7946183,0,0.098863 +2788,217369.4484787,0,0.078185 +2789,231635.5156433,0,0.060887 +2790,256341.519374,0.0069938,0.049881 +2791,278578.3073386,0.065186,0.043999 +2792,288330.5557756,0.12973,0.1002 +2793,285445.957918,0.17696,0.22687 +2794,284767.5005019,0.20215,0.36171 +2795,287213.6394851,0.3204,0.48648 +2796,285976.7239238,0.43786,0.64584 +2797,284942.8840516,0.53743,0.78313 +2798,280212.1435652,0.52278,0.83094 +2799,277193.700367,0.4786,0.84288 +2800,268235.2932604,0.40798,0.79096 +2801,268272.216113,0.28681,0.67781 +2802,271216.813606,0.16838,0.45138 +2803,273787.5672167,0.066063,0.28929 +2804,272606.0359343,0.0014142,0.23814 +2805,270866.0465066,0,0.23472 +2806,251920.0077779,0,0.26409 +2807,226964.7747922,0,0.29972 +2808,214544.8502566,0,0.33028 +2809,208111.043195,0,0.34707 +2810,206458.7455422,0,0.33456 +2811,206777.2051456,0,0.25241 +2812,214507.927404,0,0.14513 +2813,231293.9792569,0,0.089608 +2814,255492.2937647,0.014238,0.060453 +2815,277442.9296219,0.14693,0.042999 +2816,284596.7323087,0.29135,0.057947 +2817,280862.9088418,0.41166,0.073278 +2818,281499.8280488,0.49909,0.08267 +2819,283535.2002971,0.54654,0.077063 +2820,279118.3040576,0.55549,0.070518 +2821,276002.9383714,0.52905,0.060127 +2822,270718.3550963,0.45725,0.048626 +2823,267049.1466214,0.36721,0.044805 +2824,263730.705246,0.26768,0.04157 +2825,265950.6917572,0.17415,0.041436 +2826,271022.96863,0.092008,0.034839 +2827,274184.4878819,0.030686,0.046449 +2828,272107.5774245,0,0.10659 +2829,266749.1484442,0,0.22435 +2830,249644.6369878,0,0.34712 +2831,226235.5484538,0,0.39287 +2832,211337.1774389,0,0.39004 +2833,204972.6007259,0,0.34574 +2834,200698.7805401,0,0.26834 +2835,201820.3121871,0,0.20136 +2836,207137.2029583,0,0.19386 +2837,222358.6489332,0,0.20204 +2838,250230.7872725,0.007038,0.16369 +2839,273086.0330178,0.03756,0.096337 +2840,279856.7611091,0.08388,0.060578 +2841,277830.6195739,0.12987,0.088542 +2842,278652.1530438,0.17068,0.10724 +2843,281767.51873,0.21996,0.087258 +2844,278204.4634563,0.26074,0.057808 +2845,275222.9431107,0.28866,0.040364 +2846,271682.9646198,0.32743,0.037015 +2847,270187.5890905,0.34203,0.033915 +2848,267870.6800912,0.32752,0.036047 +2849,270658.3554608,0.2353,0.037055 +2850,275315.2502421,0.14224,0.024065 +2851,276256.7829828,0.058795,0.017693 +2852,274378.332858,0.0013336,0.016848 +2853,272421.4216714,0,0.011554 +2854,253396.921881,0,0.0070267 +2855,228044.7682301,0,0 +2856,211392.5617178,0,0 +2857,207469.5086315,0,0.0067306 +2858,204049.5294115,0,0.0086194 +2859,204451.0654333,0,0.011576 +2860,210109.4925907,0,0.017902 +2861,225709.3978046,0,0.024363 +2862,255704.600167,0.013569,0.018084 +2863,277895.234566,0.098387,0.011545 +2864,288510.5546819,0.17877,0.010364 +2865,295322.8209824,0.23008,0.0097644 +2866,301913.5501674,0.24838,0.010175 +2867,307484.28555,0.27607,0.0087872 +2868,296725.8893803,0.28531,0.0077465 +2869,287822.8665527,0.27724,0.0071888 +2870,279990.6064497,0.25962,0.006784 +2871,275527.5566444,0.22805,0.0062233 +2872,271849.1174564,0.18619,0 +2873,273362.9544121,0.12169,0 +2874,270427.5876322,0.064887,0 +2875,264953.7747376,0.022098,0 +2876,255958.4447785,0,0 +2877,249280.0238186,0,0 +2878,234293.9610288,0,0 +2879,213787.9317787,0,0 +2880,196014.1936194,0,0 +2881,186077.3309195,0,0 +2882,181821.97216,0,0.0068472 +2883,179061.9889299,0,0.008467 +2884,179888.1377563,0,0.0085967 +2885,177931.2265697,0,0.0089551 +2886,170583.5789068,0.017834,0.012348 +2887,177418.9219902,0.11753,0.014832 +2888,188066.5496021,0.26338,0.016496 +2889,197601.8762802,0.42274,0.02204 +2890,204894.1396642,0.5786,0.027224 +2891,215172.5387504,0.6092,0.033955 +2892,212643.3233488,0.59541,0.042479 +2893,204571.0647041,0.54475,0.04995 +2894,199743.4017297,0.52098,0.059804 +2895,195714.1954422,0.4701,0.069108 +2896,197283.4166767,0.39567,0.088319 +2897,200638.7809047,0.25407,0.099349 +2898,206034.1327375,0.13181,0.16086 +2899,206251.0544964,0.043472,0.25414 +2900,203371.0719954,0.00084626,0.33719 +2901,207409.508996,0,0.38488 +2902,201515.6986534,0,0.42502 +2903,185652.7181149,0,0.41 +2904,172498.9518842,0,0.37368 +2905,164048.2340006,0,0.30257 +2906,160282.1030378,0,0.22806 +2907,162275.9370769,0,0.17346 +2908,164805.1524785,0,0.12229 +2909,167288.2143143,0,0.88000 +2910,166706.6793862,0.019616,0.050284 +2911,177114.3084564,0.12379,0.02493 +2912,189460.3872869,0.18511,0.025031 +2913,200421.8591458,0.18352,0.026222 +2914,208540.2713562,0.11956,0.024287 +2915,221523.2693937,0.21587,0.029183 +2916,217600.2163073,0.31621,0.076034 +2917,207921.8135755,0.40737,0.16641 +2918,201524.9293665,0.31966,0.14356 +2919,200223.3988132,0.21989,0.061052 +2920,202641.845657,0.12406,0.028404 +2921,210307.9529233,0.089553,0.023383 +2922,218047.9058948,0.054495,0.024003 +2923,220198.6620575,0.022674,0.029901 +2924,220549.4291569,0,0.030593 +2925,222677.1085367,0,0.025278 +2926,217461.7556102,0,0.02206 +2927,199992.6309846,0,0.016582 +2928,188648.0845302,0,0.011611 +2929,186991.1715208,0,0.0088642 +2930,183908.1133306,0,0.0079273 +2931,187480.3993175,0,0.0087486 +2932,196092.6546811,0,0.012026 +2933,210474.1057599,0,0.019867 +2934,243723.1345057,0.009967,0.027199 +2935,273972.1814796,0.039035,0.01743 +2936,290296.6976753,0.054652,0.015817 +2937,293347.4483695,0.044774,0.015653 +2938,299098.1826584,0.011838,0.014849 +2939,306611.9831579,0.050715,0.012457 +2940,305227.3761862,0.097117,0.012142 +2941,302522.777235,0.14376,0.023567 +2942,296998.1954181,0.14951,0.020188 +2943,293361.2944392,0.145000,0.030798 +2944,285256.7282985,0.13022,0.069552 +2945,285312.1125774,0.11305,0.14647 +2946,283918.2748926,0.08306,0.18685 +2947,282695.205401,0.043146,0.24001 +2948,276118.3222857,0.0011887,0.33274 +2949,269610.6695189,0,0.38863 +2950,251873.8542121,0,0.36552 +2951,227121.6969157,0,0.26464 +2952,213063.3207969,0,0.15587 +2953,208461.8102945,0,0.096309 +2954,205106.4460665,0,0.064715 +2955,207594.1232589,0,0.048858 +2956,216580.2225049,0,0.04722 +2957,226600.161623,0,0.052155 +2958,255307.6795018,0.018154,0.047938 +2959,282445.9761461,0.091517,0.059286 +2960,296365.8915677,0.14622,0.088964 +2961,297261.2707427,0.16612,0.11777 +2962,304350.4584375,0.14967,0.14053 +2963,309261.1978303,0.186000,0.11151 +2964,304678.1487542,0.21405,0.076433 +2965,297538.192137,0.23112,0.072903 +2966,293532.0626324,0.22645,0.080511 +2967,289415.16457,0.2094,0.10532 +2968,283364.432104,0.18009,0.17213 +2969,287079.7941445,0.11832,0.24333 +2970,282229.0543872,0.064031,0.31584 +2971,277590.6210322,0.022967,0.35802 +2972,270482.9719111,0,0.40577 +2973,268189.1396947,0,0.42986 +2974,252547.6962717,0,0.43608 +2975,231072.4421415,0,0.43678 +2976,217987.9062594,0,0.43596 +2977,214027.9303205,0,0.5173 +2978,210095.646521,0,0.50796 +2979,212587.93907,0,0.44862 +2980,219764.8185397,0,0.35831 +2981,227366.310814,0,0.28357 +2982,253992.3028788,0.028559,0.19753 +2983,279732.1464817,0.15705,0.2264 +2984,294085.9054211,0.26699,0.31085 +2985,293596.6776244,0.33964,0.3301 +2986,296615.1208226,0.37011,0.36194 +2987,300312.0214369,0.39768,0.46189 +2988,296185.8926614,0.39794,0.57217 +2989,293278.2180209,0.37262,0.61541 +2990,286959.7948737,0.32591,0.61015 +2991,284555.1940996,0.26446,0.60223 +2992,278573.6919821,0.19601,0.59441 +2993,278107.5409683,0.12874,0.57599 +2994,280244.4510612,0.070002,0.5282 +2995,280276.7585572,0.025539,0.41466 +2996,273829.1054259,5.0861e-05,0.3381 +2997,271226.0443192,0,0.33601 +2998,261067.6445039,0,0.35953 +2999,239398.5453976,0,0.44046 +3000,224680.173289,0,0.57651 +3001,219326.3596653,0,0.71135 +3002,216566.3764352,0,0.77531 +3003,217332.5256261,0,0.80209 +3004,224057.1001518,0,0.78363 +3005,235780.105845,0,0.77296 +3006,265447.6178909,0.024219,0.75424 +3007,292641.298814,0.11815,0.78678 +3008,308153.512253,0.20546,0.84261 +3009,313124.2512812,0.2692,0.86976 +3010,320001.1325737,0.30412,0.87088 +3011,326901.0906491,0.315000,0.86836 +3012,326762.6299519,0.29968,0.87618 +3013,323808.8017457,0.26233,0.88702 +3014,317698.0696442,0.28971,0.89718 +3015,312330.4099508,0.29697,0.88889 +3016,302315.0861892,0.28111,0.88323 +3017,301364.3227354,0.23663,0.85799 +3018,300607.4042575,0.17154,0.78066 +3019,298387.4177463,0.092009,0.61743 +3020,289147.4738889,0.0074246,0.52593 +3021,281107.5227402,0,0.46011 +3022,263864.5505866,0,0.3713 +3023,241936.9915123,0,0.25598 +3024,227652.4629215,0,0.16269 +3025,221458.6544017,0,0.11841 +3026,218117.1362434,0,0.11216 +3027,219409.4360836,0,0.1306 +3028,223674.0255563,0,0.16294 +3029,231783.2070536,0,0.16155 +3030,257490.7431605,0.018931,0.12047 +3031,285409.0350654,0.064867,0.08198 +3032,299430.4883316,0.1071,0.080812 +3033,303053.5432408,0.13051,0.060998 +3034,308656.5861194,0.13333,0.027802 +3035,311158.1093815,0.14215,0.014636 +3036,306293.5235544,0.13998,0.042913 +3037,296088.9701734,0.1281,0.25225 +3038,287615.1755069,0.17156,0.61684 +3039,283198.2792674,0.20008,0.83108 +3040,278015.2338368,0.20743,0.8357 +3041,278112.1563248,0.179000,0.78043 +3042,276441.3972457,0.13222,0.7619 +3043,273487.5690395,0.072372,0.72992 +3044,266287.612787,0.0059943,0.68704 +3045,262281.4832824,0,0.65188 +3046,250120.0187147,0,0.63883 +3047,229637.0662475,0,0.64437 +3048,213363.3189741,0,0.63177 +3049,205318.7524688,0,0.59888 +3050,200329.5520143,0,0.52632 +3051,201723.3896991,0,0.42714 +3052,204104.9136904,0,0.33954 +3053,203624.9166069,0,0.28897 +3054,204954.1392996,0.030645,0.21895 +3055,214341.7745674,0.13232,0.23352 +3056,228123.2292918,0.21362,0.43721 +3057,238470.8587266,0.26135,0.56334 +3058,242924.6778187,0.27119,0.55547 +3059,250281.5561948,0.28525,0.5001 +3060,246561.5787976,0.27713,0.43216 +3061,236490.8707572,0.25018,0.38115 +3062,225035.5557451,0.24113,0.35407 +3063,221906.3439892,0.21856,0.31643 +3064,220161.7392049,0.18453,0.28235 +3065,223701.7176957,0.15328,0.26045 +3066,227537.0790072,0.10989,0.19139 +3067,228423.227469,0.05861,0.13817 +3068,224597.0968707,0.0045832,0.13502 +3069,227200.1579774,0,0.14376 +3070,222797.1078076,0,0.17478 +3071,206874.1276336,0,0.21754 +3072,194671.1248569,0,0.25749 +3073,184235.8036472,0,0.27988 +3074,181826.5875166,0,0.29547 +3075,180691.2097998,0,0.30631 +3076,182620.428847,0,0.30615 +3077,182643.5056298,0,0.30989 +3078,176200.4678551,0.029749,0.28411 +3079,179560.4474396,0.1195,0.41323 +3080,193563.4392796,0.19882,0.65108 +3081,201778.773978,0.25374,0.73022 +3082,208461.8102945,0.27944,0.71939 +3083,216903.2974649,0.36514,0.63525 +3084,212500.2472951,0.43948,0.55012 +3085,201921.8500317,0.49332,0.49957 +3086,195100.3530181,0.45215,0.47584 +3087,191518.8363181,0.39048,0.49273 +3088,191758.8348598,0.31361,0.58062 +3089,200108.0148989,0.20802,0.64513 +3090,212043.3269945,0.11485,0.60668 +3091,216566.3764352,0.044252,0.45245 +3092,218878.6700778,0.0030082,0.3724 +3093,225815.5510058,0,0.39435 +3094,224906.3257611,0,0.43066 +3095,207095.6647491,0,0.46503 +3096,196572.6517646,0,0.46711 +3097,192483.4458417,0,0.43641 +3098,191449.6059695,0,0.40517 +3099,193900.3603093,0,0.36688 +3100,202734.1527884,0,0.29735 +3101,214461.7738383,0,0.2412 +3102,247498.4961817,0.033622,0.19599 +3103,276778.3182755,0.129000,0.20924 +3104,293264.3719512,0.22474,0.20223 +3105,297427.4235793,0.30363,0.23612 +3106,302795.0832727,0.35991,0.26049 +3107,307982.7440598,0.39615,0.27884 +3108,307996.5901295,0.40781,0.29312 +3109,304858.1476605,0.39606,0.30400 +3110,294759.7474806,0.36684,0.33152 +3111,288972.0903391,0.32017,0.35305 +3112,280465.9881766,0.26005,0.40912 +3113,281167.5223756,0.1986,0.46119 +3114,284273.6573487,0.13169,0.46821 +3115,283825.9677612,0.065855,0.37608 +3116,274059.8732545,0.0064412,0.37322 +3117,270127.589455,0,0.40302 +3118,255335.3716413,0,0.4169 +3119,232604.7405234,0,0.4102 +3120,217849.4455622,0,0.40415 +3121,210446.4136205,0,0.3973 +3122,207455.6625617,0,0.38374 +3123,209034.1145094,0,0.34934 +3124,216510.9921563,0,0.28679 +3125,227246.3115431,0,0.22747 +3126,254352.3006914,0.0377,0.1439 +3127,279141.3808404,0.14275,0.15036 +3128,290185.9291176,0.21547,0.16086 +3129,292142.8403042,0.24629,0.16763 +3130,296250.5076534,0.23339,0.20581 +3131,300533.5585524,0.28298,0.29491 +3132,294307.4425365,0.32023,0.38608 +3133,290421.3123028,0.3415,0.45435 +3134,286059.8003421,0.33938,0.48932 +3135,287130.5630668,0.3186,0.4889 +3136,283004.4342913,0.27957,0.48291 +3137,285353.6507866,0.24201,0.46052 +3138,288381.3246979,0.18195,0.40004 +3139,288464.4011162,0.1044,0.25731 +3140,280447.5267504,0.012419,0.18751 +3141,276422.9358194,0,0.14932 +3142,263799.9355946,0,0.11847 +3143,239744.6971405,0,0.10126 +3144,222210.9575229,0,0.09136 +3145,217581.754881,0,0.083395 +3146,214014.0842508,0,0.073312 +3147,216317.1471803,0,0.061097 +3148,222746.3388853,0,0.043897 +3149,230412.4461516,0,0.025909 +3150,259115.3486739,0.045973,0.014862 +3151,287661.3290726,0.17932,0.016376 +3152,300556.6353352,0.28295,0.018217 +3153,301945.8576634,0.34877,0.022497 +3154,305342.7601005,0.37241,0.025423 +3155,310590.4205231,0.38714,0.032704 +3156,306030.4482298,0.37327,0.046875 +3157,303422.7717665,0.33531,0.064778 +3158,297122.8100455,0.32037,0.087678 +3159,293605.9083376,0.28911,0.11761 +3160,286059.8003421,0.24385,0.17041 +3161,283309.0478251,0.22287,0.23038 +3162,283692.1224206,0.17512,0.26503 +3163,281347.5212819,0.10493,0.23905 +3164,273764.4904339,0.012944,0.30688 +3165,268922.9813897,0,0.34252 +3166,252404.6202179,0,0.32118 +3167,226503.239135,0,0.28428 +3168,206269.5159227,0,0.24818 +3169,195663.4265199,0,0.22391 +3170,189460.3872869,0,0.21852 +3171,191311.1452723,0,0.20962 +3172,194920.3541118,0,0.1361 +3173,190000.3840059,0,0.10332 +3174,180335.8273438,0.050232,0.081981 +3175,184489.6482587,0.18423,0.087789 +3176,197352.6470253,0.30841,0.17958 +3177,211424.8692138,0.41226,0.25699 +3178,220849.4273341,0.48742,0.24968 +3179,231049.3653586,0.51836,0.20598 +3180,228737.0717159,0.51523,0.16308 +3181,220618.6595055,0.48188,0.12857 +3182,213589.4714461,0.43685,0.10635 +3183,209675.6490729,0.37282,0.093296 +3184,208697.1934796,0.29573,0.10466 +3185,213321.7807649,0.20076,0.12599 +3186,218975.5925658,0.11527,0.12343 +3187,219963.2788723,0.048229,0.18709 +3188,218758.6708069,0.0049214,0.30123 +3189,221246.3479993,0,0.32182 +3190,218814.0550858,0,0.29973 +3191,202244.9249918,0,0.28436 +3192,191648.0663021,0,0.2739 +3193,187198.8625666,0,0.28165 +3194,185661.948828,0,0.31511 +3195,187101.9400785,0,0.33055 +3196,193660.3617676,0,0.31778 +3197,197624.9530631,0,0.28895 +3198,215112.5391149,0.045478,0.31082 +3199,238770.8569038,0.15308,0.38274 +3200,256110.7515454,0.25973,0.52337 +3201,265576.8478749,0.35037,0.62675 +3202,269066.0574434,0.41851,0.61989 +3203,274932.1756466,0.48027,0.56871 +3204,273178.3401492,0.51789,0.51907 +3205,266970.6855597,0.52955,0.50796 +3206,260430.7252969,0.4548,0.54264 +3207,257319.9749673,0.3621,0.61465 +3208,254019.9950182,0.26191,0.66604 +3209,256599.979342,0.2023,0.72212 +3210,258183.0466463,0.13666,0.7684 +3211,255446.140199,0.071306,0.70832 +3212,247516.957608,0.0089099,0.63769 +3213,245592.3539174,0,0.53258 +3214,235775.4904885,0,0.33935 +3215,215158.6926807,0,0.17892 +3216,199701.8635205,0,0.10152 +3217,192760.367236,0,0.059817 +3218,191191.1460015,0,0.028046 +3219,189718.847255,0,0.019516 +3220,192783.4440189,0,0.015633 +3221,193688.053907,0,0.012903 +3222,198829.5611284,0.037004,0.012139 +3223,210631.0278834,0.10552,0.011041 +3224,229613.9894647,0.15947,0.010616 +3225,244507.745123,0.18625,0.012001 +3226,251546.1638955,0.18381,0.018115 +3227,256909.2082324,0.3098,0.033575 +3228,255755.3690893,0.43761,0.068795 +3229,247950.8011258,0.54917,0.11272 +3230,240146.2331623,0.54076,0.14719 +3231,235697.0294267,0.5043,0.16493 +3232,234100.1160528,0.44134,0.14878 +3233,238807.7797564,0.3035,0.11495 +3234,243487.7513205,0.17787,0.06726 +3235,242564.6800061,0.077723,0.065536 +3236,233744.7335967,0.010338,0.093654 +3237,232752.4319337,0,0.11029 +3238,228450.9196085,0,0.1035 +3239,210271.0300708,0,0.080463 +3240,194578.8177254,0,0.05767 +3241,188177.3181599,0,0.043468 +3242,187429.6303952,0,0.033238 +3243,191098.83887,0,0.027629 +3244,192783.4440189,0,0.023423 +3245,189354.2340858,0,0.022892 +3246,183405.0394642,0.037188,0.026231 +3247,188666.5459565,0.093414,0.032571 +3248,197749.5676905,0.15111,0.054391 +3249,208397.1953025,0.19272,0.10459 +3250,217997.1369725,0.21656,0.13259 +3251,229401.6830623,0.2615,0.13349 +3252,228700.1488634,0.29485,0.13199 +3253,223041.7217059,0.31405,0.13912 +3254,216464.8385906,0.26279,0.16347 +3255,211683.3291818,0.20164,0.19172 +3256,210584.8743177,0.13873,0.19951 +3257,215029.4626966,0.11162,0.23439 +3258,220960.1958919,0.078552,0.31238 +3259,220632.5055752,0.042744,0.36845 +3260,215615.6129813,0.0046741,0.3516 +3261,221546.3461765,0,0.2786 +3262,226290.9327327,0,0.19907 +3263,207843.3525138,0,0.14289 +3264,194098.8206419,0,0.11682 +3265,192174.2169513,0,0.10993 +3266,191994.218045,0,0.11343 +3267,194643.4327174,0,0.11443 +3268,201197.2390499,0,0.089406 +3269,211775.6363133,0,0.055205 +3270,243427.7516851,0.061364,0.032657 +3271,273418.338691,0.19672,0.026935 +3272,285824.4171569,0.28982,0.017365 +3273,287264.4084074,0.33661,0.012565 +3274,290832.0790377,0.33504,0.012468 +3275,297644.3453382,0.35907,0.016358 +3276,297759.7292525,0.35997,0.022628 +3277,296153.5851654,0.33992,0.029569 +3278,289765.9316695,0.30819,0.039379 +3279,284333.6569841,0.26267,0.05212 +3280,280189.0667823,0.2082,0.065725 +3281,279178.303693,0.1698,0.07419 +3282,281430.5977002,0.12157,0.073257 +3283,281393.6748476,0.068308,0.062324 +3284,273746.0290076,0.0097488,0.062263 +3285,269878.3602001,0,0.052983 +3286,258866.119419,0,0.036479 +3287,232784.7394297,0,0.029781 +3288,213981.7767548,0,0.034249 +3289,208877.192386,0,0.047168 +3290,208757.1931151,0,0.060939 +3291,211032.5639052,0,0.065652 +3292,218555.5951178,0,0.063329 +3293,227980.1532381,0,0.055656 +3294,253835.3807553,0.062466,0.042321 +3295,279182.9190496,0.19583,0.039092 +3296,290970.5397349,0.3162,0.025306 +3297,292272.0702882,0.41214,0.018067 +3298,299176.6437202,0.47725,0.016346 +3299,307142.7491637,0.51103,0.016258 +3300,305042.7619234,0.51354,0.013212 +3301,303621.2320991,0.48715,0.011892 +3302,297312.039665,0.43869,0.011842 +3303,288219.7872179,0.37127,0.01122 +3304,283285.9710422,0.2918,0.0097219 +3305,281956.7483494,0.23858,0.0082284 +3306,281993.671202,0.17212,0.0066102 +3307,281019.8309653,0.098547,0 +3308,274018.3350453,0.015771,0 +3309,272689.1123526,0,0 +3310,262729.1728699,0,0.0090672 +3311,241964.6836517,0,0.032216 +3312,226346.3170116,0,0.082328 +3313,222709.4160327,0,0.14544 +3314,221264.8094256,0,0.19901 +3315,224384.7904684,0,0.22535 +3316,228150.9214313,0,0.23167 +3317,235507.7998073,0,0.23658 +3318,261723.0251371,0.05728,0.19188 +3319,288159.7875824,0.16551,0.18009 +3320,301281.2463171,0.26492,0.37004 +3321,304336.6123678,0.34409,0.5446 +3322,310618.1126625,0.39714,0.62297 +3323,317338.0718316,0.47261,0.65503 +3324,313955.0154642,0.52864,0.64647 +3325,312131.9496182,0.55983,0.60153 +3326,301876.6273149,0.50126,0.5305 +3327,295839.7409185,0.42179,0.45769 +3328,290467.4658685,0.3291,0.40081 +3329,290093.6219862,0.25217,0.29547 +3330,290938.2322389,0.1701,0.23552 +3331,287287.4851903,0.090762,0.29432 +3332,277346.0071339,0.014641,0.39481 +3333,271470.6582175,0,0.47984 +3334,259392.2700682,0,0.55864 +3335,236107.7961617,0,0.61484 +3336,219358.6671613,0,0.62283 +3337,213321.7807649,0,0.55900 +3338,210880.2571383,0,0.49777 +3339,212398.7094505,0,0.46212 +3340,216958.6817438,0,0.40864 +3341,225321.7078525,0,0.33988 +3342,254356.916048,0.031064,0.24086 +3343,283175.2024845,0.046377,0.25063 +3344,297238.1939598,0.080408,0.43431 +3345,299208.9512162,0.11226,0.60518 +3346,303108.9275196,0.13874,0.63056 +3347,309215.0442646,0.24281,0.6219 +3348,307050.4420322,0.34952,0.63761 +3349,303482.771402,0.4428,0.69019 +3350,298618.1855749,0.37457,0.7683 +3351,292548.9916825,0.29423,0.78943 +3352,285529.0343363,0.20933,0.74385 +3353,283779.8141954,0.15182,0.53784 +3354,283175.2024845,0.096019,0.31486 +3355,280290.6046269,0.047194,0.27443 +3356,272296.8070439,0.0067432,0.21224 +3357,266781.4559402,0,0.12516 +3358,257749.2031285,0,0.080458 +3359,233758.5796664,0,0.070445 +3360,215726.381539,0,0.058254 +3361,209029.4991528,0,0.039764 +3362,208231.0424659,0,0.047021 +3363,211364.8695784,0,0.086356 +3364,216483.3000169,0,0.098073 +3365,224832.4800559,0,0.054098 +3366,250466.1704576,0.059849,0.045065 +3367,276861.3946938,0.16266,0.06472 +3368,289050.5514008,0.25971,0.10002 +3369,289373.6263609,0.33714,0.14524 +3370,291381.3064698,0.38887,0.17893 +3371,292890.5280689,0.43432,0.19477 +3372,287545.9451583,0.45661,0.21954 +3373,280489.0649595,0.45534,0.21686 +3374,272799.8809103,0.39497,0.18838 +3375,267215.299458,0.31922,0.13834 +3376,262512.251111,0.23642,0.087139 +3377,263061.478543,0.18732,0.054063 +3378,263223.0160231,0.13119,0.026937 +3379,262419.9439795,0.073295,0.017622 +3380,254758.4520697,0.012262,0.034063 +3381,250992.3211069,0,0.097418 +3382,242735.4481993,0,0.20229 +3383,222635.5703276,0,0.30082 +3384,205254.1374768,0,0.36411 +3385,195677.2725896,0,0.3865 +3386,191329.6066986,0,0.37236 +3387,193480.3628613,0,0.34259 +3388,196138.8082468,0,0.29615 +3389,194264.9734785,0,0.2123 +3390,194924.9694683,0.070705,0.10506 +3391,206717.2055102,0.19963,0.11438 +3392,221721.7297263,0.31132,0.20039 +3393,229466.2980543,0.39716,0.36536 +3394,231843.206689,0.45166,0.54844 +3395,232780.1240731,0.50246,0.61652 +3396,225866.3199281,0.5265,0.5906 +3397,215892.5343756,0.52395,0.53353 +3398,206855.6662074,0.46768,0.43289 +3399,202872.6134856,0.39219,0.31922 +3400,202281.8478444,0.30494,0.23535 +3401,208918.7305951,0.2084,0.18852 +3402,215938.6879414,0.12169,0.086142 +3403,217960.21412,0.053588,0.052844 +3404,211789.482383,0.0084992,0.046091 +3405,210797.18072,0,0.047839 +3406,208521.8099299,0,0.062051 +3407,192557.2915468,0,0.10296 +3408,180543.5183895,0,0.18291 +3409,174834.3223098,0,0.26425 +3410,171765.1101893,0,0.28813 +3411,169425.1244072,0,0.28588 +3412,168912.8198277,0,0.28137 +3413,165945.1455518,0,0.24983 +3414,164103.6182795,0.073693,0.16418 +3415,168732.8209214,0.20396,0.25138 +3416,180312.7505609,0.30098,0.3839 +3417,184872.7228542,0.36131,0.43208 +3418,188901.9291417,0.38127,0.49381 +3419,195829.5793565,0.41496,0.53476 +3420,190840.378902,0.42407,0.56465 +3421,181974.2789269,0.41023,0.54371 +3422,175978.9307397,0.40983,0.46627 +3423,173205.1014398,0.38824,0.36034 +3424,175166.627983,0.34596,0.28556 +3425,185283.4895891,0.26196,0.21793 +3426,194348.0498968,0.17507,0.13449 +3427,198243.4108437,0.093673,0.06533 +3428,196914.1881509,0.017428,0.038087 +3429,199766.4785125,0,0.02695 +3430,201395.6993825,0,0.031766 +3431,188315.778857,0,0.057648 +3432,174049.7116925,0,0.12032 +3433,166937.4472148,0,0.21941 +3434,165423.6102591,0,0.3334 +3435,165538.9941734,0,0.41014 +3436,167343.5985932,0,0.40803 +3437,166018.9912569,0,0.34261 +3438,166660.5258205,0.076703,0.21851 +3439,171889.7248167,0.20833,0.30137 +3440,178535.8382806,0.27172,0.43144 +3441,184277.3418564,0.27314,0.48062 +3442,190531.1500117,0.21372,0.51866 +3443,198257.2569134,0.2186,0.57348 +3444,196392.6528583,0.20725,0.60671 +3445,189354.2340858,0.18224,0.62206 +3446,184540.417181,0.196000,0.62709 +3447,182694.2745521,0.19744,0.61902 +3448,185768.1020292,0.18526,0.59274 +3449,195760.3490079,0.16492,0.55858 +3450,201949.5421712,0.12915,0.39585 +3451,207257.2022291,0.081115,0.26414 +3452,206167.9780781,0.015362,0.27436 +3453,209638.7262204,0,0.32186 +3454,212569.4776437,0,0.41476 +3455,197417.2620173,0,0.50166 +3456,183728.1144243,0,0.52611 +3457,178780.4521789,0,0.4734 +3458,180045.0598797,0,0.42475 +3459,181065.0536822,0,0.39925 +3460,186820.4033276,0,0.38897 +3461,196517.2674857,0,0.39921 +3462,227795.5389752,0.065795,0.45036 +3463,260301.4953129,0.16403,0.51555 +3464,275975.2462319,0.25902,0.55482 +3465,280922.9084773,0.3367,0.61900 +3466,285552.1111192,0.39094,0.66132 +3467,291256.6918423,0.47799,0.64796 +3468,289267.4731597,0.54692,0.5633 +3469,286821.3341765,0.59198,0.41712 +3470,283341.3553211,0.54099,0.26986 +3471,281153.6763059,0.46826,0.16465 +3472,274175.2571688,0.37917,0.1048 +3473,273612.183667,0.27925,0.053661 +3474,274650.6388957,0.18067,0.01919 +3475,271682.9646198,0.093353,0.010796 +3476,261099.9519999,0.018585,0.013302 +3477,256327.6733043,0,0.021676 +3478,247424.6504766,0,0.046696 +3479,225981.7038424,0,0.10422 +3480,207709.5071732,0,0.18657 +3481,203426.4562742,0,0.2741 +3482,200735.7033927,0,0.35315 +3483,200814.1644544,0,0.42742 +3484,205323.3678254,0,0.48384 +3485,215195.6155332,0,0.48523 +3486,244826.2047265,0.079008,0.39532 +3487,274830.637802,0.2085,0.35655 +3488,289858.238801,0.3248,0.4191 +3489,293019.7580529,0.41923,0.55395 +3490,298604.3395052,0.48631,0.62462 +3491,304950.4547919,0.53693,0.68278 +3492,301396.6302314,0.55984,0.73172 +3493,297865.8824537,0.55396,0.74834 +3494,291284.3839818,0.50296,0.74211 +3495,285635.1875375,0.43167,0.70774 +3496,280027.5293023,0.34642,0.55073 +3497,280535.2185252,0.24235,0.30642 +3498,283544.4310103,0.14671,0.17789 +3499,280198.2974955,0.069058,0.13585 +3500,269532.2084572,0.013289,0.13123 +3501,263361.4767202,0,0.14357 +3502,248500.0285579,0,0.12762 +3503,222884.7995825,0,0.065813 +3504,210704.8735885,0,0.031883 +3505,206920.2811994,0,0.01547 +3506,203694.1469554,0,0.0066214 +3507,205171.0610585,0,0 +3508,209874.1094055,0,0 +3509,219381.7439442,0.00017452,0 +3510,248956.9488585,0.080692,0 +3511,277212.1617933,0.20997,0 +3512,291192.0768503,0.34129,0 +3513,295175.1295721,0.46114,0 +3514,298470.4941646,0.56055,0 +3515,302130.4719263,0.62647,0 +3516,297270.5014559,0.66221,0.006098 +3517,292068.994599,0.66746,0.0084959 +3518,285889.0321489,0.6298,0.012216 +3519,282252.1311701,0.56507,0.018653 +3520,274918.3295769,0.4775,0.029184 +3521,275370.634521,0.34388,0.040283 +3522,276819.8564846,0.21617,0.065451 +3523,276316.7826183,0.10768,0.1356 +3524,268258.3700433,0.022778,0.24278 +3525,262558.4046767,0,0.32794 +3526,249136.9477649,0,0.39308 +3527,224980.1714662,0,0.44869 +3528,209883.3401187,0,0.49584 +3529,206777.2051456,0,0.51808 +3530,203227.9959416,0,0.51600 +3531,204206.4515349,0,0.50262 +3532,210234.1072182,0,0.47626 +3533,217563.2934548,0.0002589,0.40926 +3534,244184.6701629,0.08071,0.26456 +3535,272209.1152691,0.20791,0.17118 +3536,285999.8007067,0.33752,0.16742 +3537,285542.880406,0.45572,0.24504 +3538,288829.0142854,0.55445,0.31248 +3539,292068.994599,0.61397,0.36441 +3540,287822.8665527,0.64386,0.42747 +3541,281629.0580328,0.64607,0.47691 +3542,273418.338691,0.58567,0.52707 +3543,269689.1305807,0.50012,0.6144 +3544,264810.6986839,0.39888,0.66745 +3545,266199.9210121,0.30682,0.62775 +3546,266195.3056555,0.20965,0.49985 +3547,263246.0928059,0.11661,0.31563 +3548,253147.6926261,0.025521,0.26233 +3549,245869.2753118,0,0.29147 +3550,240257.00172,0,0.32325 +3551,221315.5783479,0,0.29479 +3552,204201.8361784,0,0.24919 +3553,197112.6484835,0,0.19584 +3554,194177.2817037,0,0.1248 +3555,196563.4210515,0,0.069103 +3556,198912.6375467,0,0.038112 +3557,197689.5680551,0.0017217,0.02134 +3558,197537.2612882,0.084691,0.01736 +3559,206546.437317,0.21558,0.020045 +3560,220950.9651787,0.34549,0.023586 +3561,229263.2223652,0.46113,0.023021 +3562,232586.2790971,0.55325,0.023666 +3563,234192.4231842,0.56903,0.026683 +3564,226447.8548562,0.54835,0.028274 +3565,216681.7603495,0.49614,0.026675 +3566,209518.7269495,0.43924,0.02312 +3567,206814.1279982,0.36498,0.021906 +3568,207100.2801057,0.28093,0.027154 +3569,213700.2400039,0.20106,0.038505 +3570,219935.5867328,0.1259,0.080289 +3571,222363.2642898,0.062503,0.17453 +3572,216552.5303655,0.012594,0.2374 +3573,214937.1555652,0,0.2508 +3574,213104.8590061,0,0.25251 +3575,200592.6273389,0,0.25554 +3576,190932.6860334,0,0.26143 +3577,181245.0525885,0,0.25663 +3578,177608.1516096,0,0.2474 +3579,175540.4718653,0,0.21972 +3580,174594.323768,0,0.18852 +3581,172420.4908225,0.0011718,0.14793 +3582,173878.9434993,0.055947,0.11179 +3583,177677.3819582,0.10637,0.076954 +3584,186903.4797459,0.14477,0.049013 +3585,196803.4195932,0.15644,0.027629 +3586,206518.7451776,0.14149,0.014793 +3587,218218.674088,0.14369,0.0082655 +3588,216769.4521243,0.13517,0.0057252 +3589,208567.9634956,0.11744,0.0071957 +3590,202424.9238981,0.10061,0.014132 +3591,200117.245612,0.080024,0.035927 +3592,200297.2445183,0.058087,0.10464 +3593,207109.5108188,0.061316,0.21456 +3594,216852.5285426,0.054242,0.29178 +3595,219704.8189042,0.037653,0.27649 +3596,216690.9910626,0.0067512,0.16657 +3597,220037.1245774,0,0.10688 +3598,220194.0467009,0,0.090921 +3599,204824.9093156,0,0.13406 +3600,191883.4494873,0,0.23047 +3601,187171.1704271,0,0.32573 +3602,189709.6165418,0,0.35985 +3603,192746.5211663,0,0.35579 +3604,200274.1677355,0,0.31204 +3605,210797.18072,0.0022528,0.22641 +3606,242458.526805,0.086906,0.18385 +3607,267824.5265255,0.21421,0.19254 +3608,284573.6555259,0.29488,0.18396 +3609,290827.4636811,0.33113,0.22721 +3610,297792.0367485,0.32193,0.2964 +3611,303750.4620832,0.37169,0.37835 +3612,299642.7947339,0.40474,0.45901 +3613,298096.6502823,0.41841,0.5223 +3614,292802.836294,0.43068,0.56076 +3615,288399.7861242,0.41974,0.5326 +3616,283945.967032,0.38516,0.52269 +3617,283789.0449086,0.31345,0.49178 +3618,283802.8909783,0.22913,0.42635 +3619,280890.6009813,0.13882,0.28752 +3620,270644.5093911,0.062551,0.20616 +3621,262470.7129018,0,0.17944 +3622,251744.6242281,0,0.16859 +3623,231893.9756113,0,0.16437 +3624,216270.9936146,0,0.16622 +3625,207584.8925458,0,0.1612 +3626,204663.3718356,0,0.14378 +3627,206772.5897891,0,0.11027 +3628,211886.404871,0,0.076465 +3629,217849.4455622,0.0024097,0.048894 +3630,245435.431794,0.086726,0.035859 +3631,273986.0275493,0.2106,0.036404 +3632,286775.1806108,0.32593,0.03707 +3633,289189.012098,0.4219,0.037923 +3634,292908.9894952,0.49166,0.045087 +3635,295682.818795,0.54396,0.05102 +3636,289512.0870581,0.5685,0.048008 +3637,285219.805446,0.56551,0.044548 +3638,282256.7465266,0.50427,0.038541 +3639,280175.2207126,0.42314,0.02808 +3640,273879.8743482,0.33006,0.018696 +3641,276441.3972457,0.23629,0.010924 +3642,276699.8572138,0.1481,0 +3643,273598.3375973,0.074153,0 +3644,264949.1593811,0.045337,0.0057719 +3645,257744.5877719,0,0.03075 +3646,245804.6603198,0,0.12107 +3647,223932.4855243,0,0.2593 +3648,211867.9434447,0,0.32753 +3649,203860.299792,0,0.34744 +3650,199337.2503513,0,0.36386 +3651,200228.0141697,0,0.40995 +3652,206352.592341,0,0.43907 +3653,218527.9029783,0.0011367,0.43924 +3654,246436.9641702,0.041417,0.42055 +3655,272836.8037629,0.04862,0.40869 +3656,287329.0233994,0.076184,0.47631 +3657,290116.698769,0.098066,0.51967 +3658,295290.5134864,0.11203,0.57694 +3659,301299.7077433,0.089505,0.64409 +3660,298742.8002024,0.054891,0.69016 +3661,296010.5091117,0.013832,0.66439 +3662,290569.0037131,0.059454,0.56112 +3663,287121.3323537,0.098667,0.35429 +3664,281670.596242,0.12373,0.20047 +3665,278712.1526792,0.11024,0.11796 +3666,275795.2473256,0.08703,0.072181 +3667,271595.272845,0.056259,0.038042 +3668,261455.3344559,0.039309,0.035553 +3669,251730.7781584,0.00041625,0.051198 +3670,243746.2112886,0,0.06845 +3671,228529.3806702,0,0.091688 +3672,212887.9372472,0,0.11379 +3673,204811.0632459,0,0.12573 +3674,200001.8616977,0,0.13923 +3675,199438.7881959,0,0.17200 +3676,200735.7033927,0,0.19851 +3677,198275.7183397,0.0017972,0.18942 +3678,200288.0138052,0.066916,0.11500 +3679,210543.3361085,0.1453,0.10014 +3680,222040.1893297,0.24035,0.16885 +3681,229747.8348052,0.33261,0.19451 +3682,238512.3969358,0.41445,0.1407 +3683,246570.8095107,0.45461,0.10856 +3684,245080.0493379,0.47099,0.10484 +3685,238037.0152088,0.46356,0.095975 +3686,231321.6713963,0.40153,0.080836 +3687,225750.9360138,0.32312,0.08257 +3688,222174.0346703,0.23846,0.062146 +3689,225663.2442389,0.14232,0.042275 +3690,229973.9872773,0.065749,0.046923 +3691,229969.3719207,0.016677,0.092643 +3692,226637.0844756,0.028244,0.32632 +3693,223614.0259209,0.00021193,0.6088 +3694,224246.3297712,0,0.72017 +3695,208092.5817687,0,0.81553 +3696,195354.1976296,0,0.87584 +3697,186446.5594453,0,0.8569 +3698,181932.7407177,0,0.81825 +3699,181951.202144,0,0.84131 +3700,187946.5503312,0,0.90251 +3701,198081.8733637,0.0021706,0.93335 +3702,219746.3571134,0.074483,0.95067 +3703,242878.524253,0.17051,0.96527 +3704,254296.9164125,0.21675,0.97269 +3705,257989.2016703,0.21475,0.96873 +3706,262567.6353898,0.16501,0.96916 +3707,266606.0723905,0.19035,0.96317 +3708,261584.56444,0.20677,0.95613 +3709,253835.3807553,0.21343,0.95367 +3710,247784.6482892,0.22864,0.9253 +3711,242993.9081673,0.23011,0.87106 +3712,241249.303383,0.21662,0.80716 +3713,242735.4481993,0.1859,0.73825 +3714,243723.1345057,0.14268,0.64465 +3715,241964.6836517,0.090803,0.57234 +3716,233910.8864333,0.050267,0.56177 +3717,229840.1419367,0.00108,0.57707 +3718,227500.1561546,0,0.60765 +3719,210280.2607839,0,0.61406 +3720,193812.6685345,0,0.58806 +3721,184111.1890198,0,0.55631 +3722,180174.2898637,0,0.52134 +3723,181498.8971999,0,0.44478 +3724,180991.207977,0,0.3578 +3725,180192.75129,0.0024833,0.29886 +3726,189275.773024,0.052535,0.25032 +3727,202397.2317587,0.092243,0.25842 +3728,216852.5285426,0.13639,0.39399 +3729,226009.3959818,0.16683,0.59592 +3730,231953.9752467,0.18086,0.70757 +3731,233315.5054355,0.18831,0.74735 +3732,226323.2402287,0.18291,0.74458 +3733,217244.8338513,0.16585,0.72119 +3734,211466.4074229,0.15297,0.66472 +3735,207686.4303903,0.13322,0.63501 +3736,207215.66402,0.10873,0.54841 +3737,210391.0293416,0.10308,0.49447 +3738,214646.3881012,0.085999,0.35799 +3739,214701.77238,0.05835,0.22886 +3740,206532.5912473,0.041728,0.14573 +3741,203971.0683498,0.00098264,0.10109 +3742,206495.6683947,0,0.06428 +3743,192755.7518794,0,0.047481 +3744,180381.9809095,0,0.044439 +3745,171082.0374166,0,0.04916 +3746,166660.5258205,0,0.056344 +3747,164255.9250464,0,0.059303 +3748,163180.5469651,0,0.062736 +3749,159728.2602491,0.0028864,0.054664 +3750,164112.8489926,0.057373,0.078716 +3751,175180.4740527,0.10614,0.099099 +3752,186368.0983836,0.15476,0.10044 +3753,194435.7416717,0.18672,0.10882 +3754,201081.8551356,0.19971,0.14507 +3755,207907.9675058,0.24749,0.22205 +3756,205429.5210266,0.28701,0.35341 +3757,198072.6426506,0.31441,0.53645 +3758,192557.2915468,0.30552,0.68349 +3759,191412.6831169,0.28191,0.74411 +3760,193651.1310544,0.24498,0.76061 +3761,203707.9930252,0.1899,0.79796 +3762,214332.5438542,0.13169,0.82834 +3763,217724.8309348,0.07561,0.81145 +3764,214660.2341709,0.047702,0.65353 +3765,214000.2381811,0.0013762,0.56488 +3766,216003.3029334,0,0.55358 +3767,198455.717246,0,0.57686 +3768,187235.7854191,0,0.62708 +3769,184660.4164519,0,0.67862 +3770,179786.5999117,0,0.73678 +3771,181545.0507657,0,0.79458 +3772,186575.7894293,0,0.82694 +3773,198404.9483237,0.0036747,0.81761 +3774,234926.2648792,0.072947,0.83984 +3775,266892.2244979,0.15815,0.92229 +3776,286659.7966965,0.21016,0.93432 +3777,297487.4232147,0.22528,0.92953 +3778,304858.1476605,0.20259,0.90201 +3779,313442.7108847,0.26404,0.84042 +3780,309307.351396,0.31875,0.77618 +3781,305361.2215268,0.36091,0.73283 +3782,298078.188856,0.36719,0.69655 +3783,291870.5342664,0.3543,0.66036 +3784,284047.5048766,0.32219,0.65326 +3785,277244.4692893,0.24501,0.63741 +3786,275264.4813198,0.16643,0.57062 +3787,271009.1225603,0.093668,0.4148 +3788,259932.2667871,0.053539,0.23268 +3789,249972.3273044,0.0026762,0.22049 +3790,241738.5311797,0,0.20639 +3791,220549.4291569,0,0.19495 +3792,206278.7466358,0,0.18524 +3793,198954.1757558,0,0.15905 +3794,195811.1179302,0,0.13475 +3795,196609.5746172,0,0.11795 +3796,201668.0054203,0,0.11615 +3797,212029.4809247,0.0044976,0.093812 +3798,243349.2906234,0.087969,0.085344 +3799,273552.1840316,0.20605,0.11272 +3800,287384.4076783,0.28377,0.092639 +3801,289987.468785,0.32225,0.1047 +3802,294173.5971959,0.32053,0.17137 +3803,299222.7972859,0.38032,0.21847 +3804,295835.1255619,0.42458,0.21915 +3805,294699.7478452,0.44985,0.26873 +3806,292941.2969912,0.4403,0.30635 +3807,288289.0175664,0.4097,0.31518 +3808,281799.826226,0.36056,0.3109 +3809,280696.7560052,0.26587,0.28923 +3810,278998.3047867,0.1739,0.2455 +3811,274055.2578979,0.093263,0.17453 +3812,263610.7059751,0.054363,0.11867 +3813,256650.7482643,0.0029511,0.10146 +3814,246773.8851999,0,0.070149 +3815,225386.3228446,0,0.045174 +3816,212209.4798311,0,0.02988 +3817,203167.9963062,0,0.018778 +3818,198584.9472301,0,0.010876 +3819,198340.3333317,0,0.0060787 +3820,201972.618954,0,0 +3821,213492.5489581,0.0046938,0 +3822,249243.100966,0.08967,0 +3823,277987.5416974,0.20963,0 +3824,290684.3876274,0.32497,0 +3825,292133.609591,0.42507,0 +3826,297473.577145,0.50275,0 +3827,302504.3158087,0.55012,0 +3828,300367.4057158,0.56916,0 +3829,300113.5611043,0.56057,0.0093458 +3830,295747.433787,0.51056,0.015498 +3831,290296.6976753,0.43973,0.025006 +3832,283687.507064,0.35465,0.039365 +3833,280544.4492384,0.25809,0.056732 +3834,279796.7614737,0.16596,0.074917 +3835,276482.9354549,0.087128,0.081362 +3836,265673.7703629,0.05292,0.091542 +3837,256932.2850152,0.0030381,0.089872 +3838,248536.9514105,0,0.063568 +3839,227680.1550609,0,0.039641 +3840,213584.8560896,0,0.024259 +3841,205669.5195683,0,0.019224 +3842,201649.543994,0,0.02309 +3843,201446.4683048,0,0.032717 +3844,205443.3670963,0,0.042282 +3845,216709.4524889,0.004195,0.048929 +3846,251126.1664475,0.07602,0.041286 +3847,279732.1464817,0.1629,0.061289 +3848,293864.3683056,0.25932,0.084624 +3849,295825.8948488,0.34812,0.10034 +3850,299822.7936403,0.42266,0.11729 +3851,304775.0712422,0.4125,0.14902 +3852,301267.4002473,0.37012,0.20593 +3853,300242.7910883,0.30256,0.30917 +3854,297044.3489838,0.25495,0.43533 +3855,292862.8359294,0.1981,0.53136 +3856,287282.8698337,0.13835,0.58835 +3857,284338.2723407,0.12102,0.5913 +3858,282330.5922318,0.094663,0.54123 +3859,279436.7636611,0.061653,0.46556 +3860,268622.9832125,0.046597,0.30881 +3861,260527.6477849,0.003061,0.23803 +3862,254799.9902789,0,0.20857 +3863,234390.8835168,0,0.18858 +3864,218070.9826777,0,0.16466 +3865,208069.5049858,0,0.15129 +3866,205009.5235785,0,0.15544 +3867,206347.9769844,0,0.17088 +3868,211692.559895,0,0.18559 +3869,219390.9746573,0.0043249,0.16474 +3870,248061.5696835,0.076235,0.18324 +3871,277161.392871,0.16214,0.3191 +3872,293292.0640907,0.21676,0.39365 +3873,297833.5749577,0.23535,0.43607 +3874,303510.4635414,0.21683,0.48345 +3875,307433.5166277,0.23288,0.53738 +3876,299684.3329431,0.23498,0.58048 +3877,295336.6670521,0.2244,0.59414 +3878,288939.7828431,0.24723,0.58522 +3879,287795.1744132,0.25527,0.56042 +3880,284282.8880618,0.24597,0.50179 +3881,284324.426271,0.20255,0.39827 +3882,280392.1424715,0.15038,0.27855 +3883,272993.7258863,0.093783,0.1838 +3884,260255.3417472,0.056844,0.10822 +3885,253239.9997575,0.0037137,0.10512 +3886,250249.2486987,0,0.11753 +3887,230887.8278786,0,0.14479 +3888,214443.312412,0,0.17395 +3889,202120.3103643,0,0.19722 +3890,195538.8118924,0,0.23053 +3891,192266.5240828,0,0.24769 +3892,191564.9898838,0,0.25586 +3893,190004.9993624,0.0050064,0.20107 +3894,199641.8638851,0.091688,0.16257 +3895,213778.7010656,0.21428,0.13033 +3896,231713.976705,0.2689,0.12714 +3897,247798.4943589,0.26819,0.11815 +3898,257158.4374873,0.21222,0.096709 +3899,258146.1237937,0.22705,0.086018 +3900,252852.3098054,0.22847,0.088494 +3901,244433.8994178,0.21748,0.08997 +3902,235073.9562895,0.1951,0.07726 +3903,229069.3773891,0.16438,0.049604 +3904,226590.9309099,0.12869,0.031197 +3905,229673.9891001,0.094282,0.021734 +3906,232212.4352148,0.061115,0.016711 +3907,228404.7660427,0.032268,0.020486 +3908,217784.8305702,0.038232,0.062921 +3909,212103.3266299,0.0029366,0.16528 +3910,211729.4827476,0,0.31522 +3911,198031.1044414,0,0.42162 +3912,182532.7370721,0,0.45627 +3913,176477.3892494,0,0.42542 +3914,174095.8652582,0,0.35648 +3915,173417.4078421,0,0.27295 +3916,173080.4868123,0,0.24492 +3917,169157.433726,0.0043756,0.25241 +3918,170495.8871319,0.076081,0.34873 +3919,180045.0598797,0.16166,0.47189 +3920,192543.4454771,0.22536,0.55784 +3921,205217.2146242,0.26018,0.55021 +3922,214263.3135057,0.26352,0.57824 +3923,222395.5717858,0.34397,0.53552 +3924,219003.2847053,0.41565,0.43338 +3925,211143.3324629,0.47055,0.31254 +3926,204972.6007259,0.43925,0.26295 +3927,200131.0916817,0.38944,0.26 +3928,199517.2492576,0.32512,0.23931 +3929,205231.060694,0.27177,0.19607 +3930,212274.0948231,0.20516,0.1406 +3931,212297.1716059,0.13082,0.061518 +3932,206546.437317,0.06847,0.025663 +3933,204284.9125967,0.0054932,0.013445 +3934,210164.8768696,0,0.01376 +3935,197241.8784676,0,0.028453 +3936,186303.4833916,0,0.067846 +3937,182154.2778332,0,0.12443 +3938,182168.1239029,0,0.17659 +3939,183446.5776734,0,0.21758 +3940,188731.1609485,0,0.21658 +3941,198183.4112083,0.0050714,0.18254 +3942,234898.5727398,0.091015,0.092734 +3943,265456.848604,0.21134,0.063157 +3944,280281.3739138,0.32564,0.14561 +3945,285538.2650494,0.426000,0.18272 +3946,290619.7726354,0.50444,0.17662 +3947,292830.5284334,0.54408,0.16658 +3948,288741.3225105,0.55411,0.14994 +3949,285607.495398,0.53646,0.11949 +3950,281389.0594911,0.50763,0.087212 +3951,274466.0246328,0.4569,0.062305 +3952,264529.161933,0.38794,0.043468 +3953,264759.9297616,0.28622,0.029827 +3954,269795.2837818,0.18766,0.018677 +3955,268073.7557804,0.10174,0.015172 +3956,258192.2773594,0.059613,0.021363 +3957,249178.485974,0.0051135,0.028255 +3958,241290.8415922,0,0.022652 +3959,219349.4364482,0,0.014431 +3960,207649.5075378,0,0.0091681 +3961,202166.4639301,0,0 +3962,197468.0309396,0,0 +3963,198206.4879911,0,0 +3964,201806.4661174,0,0 +3965,208674.1166968,0.0050811,0 +3966,240164.6945886,0.090702,0.0070907 +3967,269804.514495,0.21032,0.010299 +3968,284887.4997728,0.33267,0.020998 +3969,289350.549578,0.44562,0.039594 +3970,293121.2958975,0.54036,0.05921 +3971,294390.5189548,0.56827,0.069003 +3972,287444.4073137,0.56282,0.071221 +3973,284592.1169521,0.52763,0.067962 +3974,279265.9954679,0.45166,0.069429 +3975,273907.5664876,0.35899,0.076087 +3976,268539.9067942,0.26003,0.080419 +3977,268166.0629119,0.18457,0.068711 +3978,267635.2969061,0.11503,0.030071 +3979,265922.9996178,0.058016,0.019361 +3980,256983.0539375,0.046136,0.030097 +3981,247761.5715064,0.0044321,0.13416 +3982,239541.6214514,0,0.32174 +3983,221370.9626268,0,0.22272 +3984,207257.2022291,0,0.099363 +3985,200915.702299,0,0.10161 +3986,197892.6437442,0,0.13568 +3987,198557.2550906,0,0.15527 +3988,201395.6993825,0,0.19322 +3989,210192.569009,0.0048903,0.17673 +3990,241429.3022894,0.087586,0.1483 +3991,267852.2186649,0.20115,0.22708 +3992,280295.2199835,0.28537,0.24196 +3993,281365.9827082,0.33881,0.25098 +3994,285898.2628621,0.35755,0.29668 +3995,293195.1416026,0.38926,0.34442 +3996,290952.0783086,0.40007,0.36993 +3997,289378.2417175,0.38997,0.37900 +3998,285935.1857146,0.36206,0.37326 +3999,282778.2818193,0.31874,0.33036 +4000,274927.5602901,0.26406,0.27743 +4001,273058.3408783,0.18414,0.27282 +4002,271650.6571238,0.11186,0.26398 +4003,269550.6698835,0.054173,0.18033 +4004,260218.4188946,0.045446,0.17109 +4005,250632.3232942,0.0045698,0.23005 +4006,243196.9838565,0,0.36187 +4007,225044.7864582,0,0.50142 +4008,211572.5606241,0,0.59305 +4009,205960.2870324,0,0.64856 +4010,201557.2368625,0,0.68938 +4011,202604.9228044,0,0.66434 +4012,206837.2047811,0,0.59858 +4013,213607.9328724,0.0031141,0.51359 +4014,245587.7385609,0.04823,0.42496 +4015,272610.6512908,0.069836,0.41495 +4016,285275.1897248,0.10606,0.49176 +4017,287564.4065846,0.13406,0.62026 +4018,293965.9061502,0.15173,0.66995 +4019,300538.1739089,0.21568,0.6695 +4020,297884.3438799,0.27756,0.69713 +4021,295045.8995881,0.32971,0.7358 +4022,290693.6183405,0.25169,0.76629 +4023,287185.9473457,0.16716,0.78083 +4024,280992.1388259,0.087362,0.79123 +4025,279478.3018702,0.085592,0.76273 +4026,277304.4689247,0.073449,0.61152 +4027,273081.4176612,0.051823,0.42495 +4028,263043.0171168,0.045318,0.24968 +4029,252570.7730545,0.004795,0.1366 +4030,246727.7316342,0,0.10006 +4031,225474.0146194,0,0.085386 +4032,210169.4922262,0,0.070756 +4033,205304.9063991,0,0.052898 +4034,204644.9104093,0,0.034814 +4035,205203.3685545,0,0.03083 +4036,208498.733147,0,0.027957 +4037,215772.5351048,0.0049952,0.019685 +4038,244258.5158681,0.089093,0.015041 +4039,271484.5042872,0.20603,0.011127 +4040,285455.1886311,0.3284,0.010059 +4041,289945.9305758,0.44239,0.010058 +4042,294695.1324886,0.53943,0.011963 +4043,300090.4843214,0.61036,0.015668 +4044,294768.9781937,0.65372,0.020719 +4045,277627.5438848,0.66845,0.026076 +4046,266167.6135161,0.64475,0.031608 +4047,263684.5516803,0.59349,0.037366 +4048,270081.4358893,0.51735,0.04145 +4049,269721.4380767,0.38999,0.042575 +4050,268512.2146548,0.26255,0.043483 +4051,263906.0887958,0.14746,0.051235 +4052,254047.6871576,0.075015,0.08017 +4053,244780.0511607,0.0065593,0.1131 +4054,237413.9420716,0,0.13102 +4055,218910.9775738,0,0.13935 +4056,203186.4577325,0,0.13407 +4057,195944.9632708,0,0.12047 +4058,195252.659785,0,0.10405 +4059,193189.5953972,0,0.087662 +4060,192917.2893595,0,0.076962 +4061,191971.1412622,0.0050826,0.05586 +4062,196069.5778982,0.090687,0.027487 +4063,208235.6578224,0.21001,0.020424 +4064,226507.8544916,0.33166,0.022617 +4065,241253.9187396,0.44373,0.016834 +4066,248666.1813945,0.53728,0.010718 +4067,252769.2333871,0.6063,0.0067711 +4068,248320.0296516,0.64815,0 +4069,240206.2327977,0.66247,0 +4070,234773.9581123,0.63671,0.0071332 +4071,228432.4581822,0.58392,0.010523 +4072,225926.3195635,0.50698,0.01284 +4073,228861.6863434,0.38329,0.014115 +4074,230458.5997174,0.25899,0.015751 +4075,228169.3828576,0.14634,0.020314 +4076,217249.4492079,0.074764,0.030544 +4077,209791.0329872,0.0065849,0.044811 +4078,208249.5038922,0,0.073737 +4079,193060.3654132,0,0.11494 +4080,180317.3659175,0,0.15004 +4081,172503.5672408,0,0.17462 +4082,168788.2052002,0,0.18709 +4083,171631.2648487,0,0.1961 +4084,170948.192076,0,0.17528 +4085,167491.2900035,0.0047396,0.13596 +4086,169203.5872917,0.087698,0.06763 +4087,174054.3270491,0.20417,0.039685 +4088,186012.7159275,0.32094,0.048876 +4089,198949.5603993,0.42738,0.055873 +4090,209763.3408478,0.51574,0.053059 +4091,221763.2679354,0.54728,0.052118 +4092,221301.7322782,0.54803,0.054106 +4093,212620.246566,0.52036,0.055198 +4094,204077.2215509,0.5116,0.054027 +4095,200564.9351995,0.47961,0.051032 +4096,201529.5447231,0.4256,0.044832 +4097,205969.5177455,0.31142,0.037071 +4098,214646.3881012,0.20176,0.032678 +4099,215011.0012704,0.10774,0.047599 +4100,211646.4063293,0.062283,0.088283 +4101,210298.7222102,0.0058353,0.15001 +4102,213769.4703524,0,0.23876 +4103,197975.7201625,0,0.31298 +4104,189146.54304,0,0.35002 +4105,186003.4852144,0,0.37015 +4106,184826.5692885,0,0.37431 +4107,185592.7184794,0,0.33342 +4108,193480.3628613,0,0.23856 +4109,205586.44315,0.0036867,0.17342 +4110,239910.8499771,0.064438,0.11223 +4111,269924.5137658,0.12646,0.10699 +4112,283752.122056,0.19874,0.1558 +4113,285709.0332426,0.26321,0.20964 +4114,291053.6161532,0.31494,0.21055 +4115,297718.1910434,0.38768,0.20706 +4116,296319.738002,0.44726,0.23987 +4117,291496.6903841,0.48611,0.25481 +4118,285164.4211671,0.46717,0.21041 +4119,280175.2207126,0.42959,0.19073 +4120,273459.8769001,0.37406,0.25018 +4121,271558.3499924,0.27189,0.3143 +4122,270566.0483294,0.17471,0.33871 +4123,267852.2186649,0.092031,0.24312 +4124,258469.1987538,0.059114,0.14939 +4125,249192.3320437,0.0060693,0.11331 +4126,242472.3728747,0,0.069914 +4127,220544.8138004,0,0.043944 +4128,204054.1447681,0,0.03741 +4129,198234.1801306,0,0.05239 +4130,197191.1095453,0,0.064238 +4131,200532.6277035,0,0.053351 +4132,204621.8336264,0,0.048087 +4133,211600.2527635,0.0046615,0.035759 +4134,243815.4416372,0.085597,0.030815 +4135,272319.8838268,0.19631,0.046577 +4136,282607.5136261,0.3069,0.044963 +4137,280110.6057206,0.40569,0.037452 +4138,284652.1165876,0.48509,0.033129 +4139,292239.7627922,0.53551,0.030162 +4140,292050.5331727,0.55913,0.029321 +4141,289784.3930958,0.55627,0.031267 +4142,284509.0405338,0.53258,0.034087 +4143,279478.3018702,0.48571,0.035166 +4144,273713.7215116,0.41877,0.034733 +4145,271710.6567593,0.32389,0.030633 +4146,270206.0505167,0.22545,0.022995 +4147,267547.6051312,0.13239,0.017965 +4148,258598.4287378,0.071507,0.017364 +4149,250784.6300611,0.0067779,0.013712 +4150,244046.2094658,0,0.0093062 +4151,223332.4891699,0,0.0074843 +4152,209454.1119575,0,0.0083401 +4153,204021.8372721,0,0.01248 +4154,199683.4020942,0,0.020475 +4155,199840.3242177,0,0.03264 +4156,203343.379856,0,0.051403 +4157,210958.7182,0.0046941,0.064125 +4158,242947.7546016,0.088382,0.037467 +4159,270912.2000723,0.20644,0.033098 +4160,280858.2934853,0.32761,0.035495 +4161,278762.9216015,0.43961,0.032234 +4162,281532.1355448,0.53352,0.028962 +4163,289715.1627472,0.6037,0.027346 +4164,287089.0248577,0.64673,0.025808 +4165,282487.5143552,0.66131,0.021543 +4166,278642.9223306,0.62817,0.01588 +4167,276713.7032835,0.56903,0.011831 +4168,270224.511943,0.48762,0.0090885 +4169,270806.0468711,0.34858,0.0081616 +4170,272513.7288028,0.21816,0.010316 +4171,267736.8347506,0.11063,0.018982 +4172,249750.790189,0.064574,0.047054 +4173,238110.860914,0.0063501,0.091011 +4174,238849.3179655,0,0.11648 +4175,226600.161623,0,0.12206 +4176,211106.4096103,0,0.10511 +4177,205309.5217557,0,0.081272 +4178,200749.5494624,0,0.061398 +4179,200957.2405081,0,0.048729 +4180,205120.2921362,0,0.038489 +4181,211604.8681201,0.0042253,0.028509 +4182,240423.1545566,0.080558,0.015156 +4183,264533.7772896,0.18203,0.0074214 +4184,273690.6447287,0.29293,0.0063977 +4185,277479.8524745,0.39792,0.0064386 +4186,284319.8109144,0.48981,0.0063323 +4187,289045.9360443,0.5652,0.0065617 +4188,286719.7963319,0.61876,0.0059209 +4189,283258.2789028,0.64526,0 +4190,279972.1450234,0.5785,0 +4191,277419.852839,0.48871,0 +4192,272089.1159982,0.38433,0 +4193,272333.7298965,0.27258,0 +4194,274059.8732545,0.16916,0 +4195,272278.3456176,0.084952,0 +4196,264067.6262758,0.056779,0.0098453 +4197,255598.4469659,0.005968,0.017149 +4198,247761.5715064,0,0.025923 +4199,226000.1652687,0,0.033351 +4200,212629.4772791,0,0.036551 +4201,204187.9901087,0,0.037783 +4202,203269.5341508,0,0.034867 +4203,204178.7593955,0,0.024917 +4204,207857.1985835,0,0.010983 +4205,213843.3160576,0.0028457,0 +4206,244923.1272145,0.050484,0.0072553 +4207,271373.7357295,0.081651,0.032261 +4208,284776.731215,0.090794,0.11861 +4209,286078.2617684,0.066716,0.31187 +4210,288565.9389608,0.012668,0.49642 +4211,291750.5349956,0.061262,0.60779 +4212,287647.4830029,0.11871,0.60559 +4213,282829.0507416,0.17464,0.49294 +4214,277456.7756916,0.1895,0.37996 +4215,272698.3430657,0.19394,0.34115 +4216,269389.1324035,0.18596,0.35801 +4217,269846.0527041,0.1948,0.38384 +4218,266873.7630716,0.1766,0.37978 +4219,262263.0218561,0.13242,0.31387 +4220,251121.5510909,0.073065,0.17787 +4221,242001.6065043,0.0072754,0.11645 +4222,237437.0188545,0,0.094724 +4223,220729.4280633,0,0.082872 +4224,205401.8288871,0,0.068739 +4225,196844.9578024,0,0.058516 +4226,195557.2733187,0,0.054777 +4227,195451.1201176,0,0.054237 +4228,194324.973114,0,0.050045 +4229,193581.9007058,0.0040884,0.032378 +4230,199032.6368176,0.077946,0.030749 +4231,211738.7134607,0.17261,0.02929 +4232,229013.9931103,0.27722,0.022515 +4233,239121.6240033,0.37467,0.01787 +4234,241595.455126,0.45734,0.015222 +4235,242832.3706873,0.55285,0.014024 +4236,235613.9530085,0.62887,0.014443 +4237,228086.3064393,0.67959,0.018287 +4238,220369.4302506,0.62537,0.025624 +4239,216866.3746124,0.54589,0.036226 +4240,216432.5310946,0.44759,0.050636 +4241,218781.7475898,0.34106,0.074774 +4242,221952.4975549,0.23319,0.11559 +4243,221204.8097902,0.13386,0.14526 +4244,212403.3248071,0.073532,0.18175 +4245,207197.2025937,0.0073155,0.19318 +4246,208249.5038922,0,0.15085 +4247,195146.5065838,0,0.10757 +4248,181895.8178652,0,0.075033 +4249,173195.8707266,0,0.054035 +4250,172138.9540716,0,0.039893 +4251,171912.8015996,0,0.02998 +4252,170297.4267993,0,0.025465 +4253,167062.0618423,0.00444,0.01852 +4254,171234.3441835,0.088512,0.012377 +4255,178614.2993424,0.20975,0.010162 +4256,189243.465528,0.32911,0.0071943 +4257,198968.0218255,0.43491,0 +4258,205101.8307099,0.52017,0 +4259,209421.8044615,0.5948,0.0058925 +4260,207718.7378864,0.64373,0.0071914 +4261,200348.0134406,0.66458,0.010142 +4262,196037.2704022,0.61713,0.01556 +4263,194357.28061,0.54457,0.024338 +4264,191998.8334016,0.45278,0.03899 +4265,193120.3650486,0.34058,0.057387 +4266,204187.9901087,0.22895,0.072361 +4267,209038.729866,0.12861,0.082371 +4268,209384.8816089,0.072017,0.09868 +4269,208895.6538123,0.0072466,0.099379 +4270,215232.5383858,0,0.075602 +4271,199364.9424908,0,0.055971 +4272,189381.9262252,0,0.044529 +4273,188574.2388251,0,0.037164 +4274,189368.0801555,0,0.033329 +4275,188740.3916617,0,0.031601 +4276,192644.9833217,0,0.028753 +4277,200897.2408727,0.0040545,0.022278 +4278,234501.6520746,0.082687,0.015227 +4279,264907.6211719,0.19151,0.025465 +4280,283239.8174765,0.29937,0.027523 +4281,287273.6391206,0.39388,0.024476 +4282,291362.8450435,0.46848,0.020458 +4283,296199.7387311,0.54877,0.018944 +4284,292899.758782,0.60694,0.021584 +4285,290652.0801314,0.63959,0.03036 +4286,287915.1736841,0.62185,0.041909 +4287,285150.5750974,0.57714,0.051407 +4288,279870.6071788,0.50753,0.061674 +4289,278227.5402391,0.37854,0.072675 +4290,278158.3098906,0.25139,0.08075 +4291,276367.5515406,0.13881,0.096954 +4292,268802.9821188,0.075176,0.14427 +4293,258939.9651241,0.0073994,0.17475 +4294,251338.4728498,0,0.16757 +4295,230320.1390202,0,0.14086 +4296,213404.8571832,0,0.11968 +4297,207649.5075378,0,0.11057 +4298,205078.7539271,0,0.11558 +4299,205360.290678,0,0.13258 +4300,209306.4205472,0,0.12299 +4301,215541.7672762,0.0028749,0.071231 +4302,245144.6643299,0.056483,0.034673 +4303,275882.9391005,0.1031,0.028498 +4304,288685.9382316,0.15706,0.023794 +4305,290149.006265,0.1993,0.020701 +4306,295419.7434704,0.22589,0.020258 +4307,298825.8766207,0.26797,0.01967 +4308,298124.3424217,0.30009,0.018499 +4309,295959.7401894,0.31906,0.019959 +4310,292179.7631568,0.31122,0.023196 +4311,289512.0870581,0.28877,0.025159 +4312,285773.6482346,0.25297,0.026418 +4313,285824.4171569,0.17879,0.028885 +4314,285044.4218962,0.11086,0.032834 +4315,282039.8247677,0.055542,0.050266 +4316,271350.6589466,0.048583,0.085496 +4317,262475.3282584,0.0057045,0.11062 +4318,251052.3207423,0,0.093255 +4319,229526.2976898,0,0.059174 +4320,216206.3786225,0,0.030422 +4321,211304.8699429,0,0.016559 +4322,207211.0486634,0,0.010579 +4323,208660.2706271,0,0.0068085 +4324,212546.4008608,0,0 +4325,217863.2916319,0.0021638,0 +4326,247696.9565143,0.045775,0 +4327,277401.3914127,0.070719,0.0074172 +4328,288602.8618133,0.10286,0.013533 +4329,289581.3174066,0.12264,0.019143 +4330,290938.2322389,0.12888,0.018866 +4331,299998.17719,0.13031,0.014656 +4332,296670.5051015,0.12124,0.010666 +4333,296315.1226454,0.10385,0.0074758 +4334,293287.4487341,0.16846,0 +4335,289442.8567095,0.21836,0 +4336,282852.1275244,0.24573,0 +4337,280752.1402841,0.15844,0 +4338,279842.9150394,0.084882,0 +4339,277687.5435202,0.032318,0.0073421 +4340,266458.3809802,0.040787,0.010472 +4341,258316.8919869,0.0051352,0.013842 +4342,252358.4666522,0,0.01617 +4343,232083.2052308,0,0.018325 +4344,216114.0714911,0,0.022928 +4345,208198.7349699,0,0.029805 +4346,207003.3576177,0,0.037535 +4347,208927.9613083,0,0.044207 +4348,212740.2458369,0,0.062875 +4349,218389.4422812,0.0016939,0.075726 +4350,247932.3396995,0.05937,0.090263 +4351,274687.5617483,0.11876,0.090425 +4352,287033.6405788,0.17457,0.10157 +4353,289133.6278191,0.21255,0.12011 +4354,295004.3613789,0.22979,0.13226 +4355,302135.0872829,0.23194,0.1332 +4356,300164.3300266,0.21672,0.14044 +4357,296716.6586672,0.18781,0.19171 +4358,293370.5251524,0.2029,0.33117 +4359,288233.6332876,0.20599,0.46874 +4360,282173.6701083,0.19553,0.55985 +4361,281541.366258,0.17321,0.55075 +4362,280996.7541824,0.13805,0.42509 +4363,279469.0711571,0.092909,0.25562 +4364,268609.1371428,0.060322,0.15652 +4365,257970.740244,0.0064292,0.10701 +4366,253646.1511359,0,0.058947 +4367,233103.1990332,0,0.030001 +4368,219030.9768447,0,0.015121 +4369,214041.7763902,0,0.011069 +4370,214189.4678005,0,0.01281 +4371,214295.6210017,0,0.018061 +4372,216760.2214112,0,0.022486 +4373,224034.0233689,0.0018218,0.02505 +4374,253530.7672216,0.065304,0.018334 +4375,278882.9208724,0.1399,0.020282 +4376,291842.842127,0.20483,0.021782 +4377,292678.2216666,0.24892,0.020894 +4378,297427.4235793,0.26888,0.021125 +4379,300210.4835923,0.25669,0.02092 +4380,296619.7361792,0.22168,0.021543 +4381,289313.6267255,0.16979,0.021168 +4382,282819.8200284,0.1622,0.019854 +4383,279519.8400793,0.14692,0.017501 +4384,276353.7054709,0.1251,0.014647 +4385,275878.3237439,0.10708,0.013647 +4386,275892.1698136,0.082476,0.013708 +4387,272809.1116234,0.053495,0.011643 +4388,263070.7092562,0.047654,0.010693 +4389,255464.6016253,0.005535,0.013324 +4390,252395.3895048,0,0.020979 +4391,235507.7998073,0,0.031889 +4392,219266.3600299,0,0.042698 +4393,208780.2698979,0,0.05014 +4394,203315.6877165,0,0.052694 +4395,203758.7619474,0,0.051125 +4396,203200.3038022,0,0.035904 +4397,200094.1688292,0.0015701,0.023465 +4398,203560.3016148,0.063101,0.023576 +4399,219898.6638803,0.13538,0.029093 +4400,236333.9486337,0.20314,0.040778 +4401,245278.5096705,0.2528,0.048648 +4402,249700.0212667,0.28074,0.05868 +4403,251670.778523,0.34934,0.063346 +4404,245638.5074832,0.40637,0.053814 +4405,239066.2397244,0.44699,0.03575 +4406,230453.9843608,0.44595,0.022054 +4407,229646.2969607,0.42368,0.011984 +4408,223923.2548112,0.38088,0.0061316 +4409,225183.2471554,0.27143,0 +4410,229978.6026339,0.16972,0 +4411,228340.1510507,0.085864,0 +4412,217069.4503015,0.057779,0.024065 +4413,215444.8447881,0.0060504,0.074651 +4414,215661.766547,0,0.12182 +4415,202872.6134856,0,0.1455 +4416,192377.2926405,0,0.1774 +4417,186621.942995,0,0.21281 +4418,185103.4906828,0,0.24943 +4419,182218.8928252,0,0.29368 +4420,181005.0540467,0,0.35444 +4421,177252.7691536,0.0013383,0.36737 +4422,178305.070452,0.059161,0.39133 +4423,186455.7901584,0.1228,0.57792 +4424,198044.9505111,0.22415,0.83471 +4425,206154.1320084,0.33631,0.93954 +4426,213871.008197,0.44855,0.97576 +4427,221878.6518497,0.39212,0.98702 +4428,218615.5947532,0.29056,0.98847 +4429,211563.329911,0.15812,0.98582 +4430,204751.0636105,0.10725,0.96131 +4431,200809.5490978,0.056367,0.87218 +4432,199941.8620623,0.011383,0.94013 +4433,207077.2033228,0.057812,0.9537 +4434,214743.3105892,0.079701,0.8995 +4435,216760.2214112,0.073369,0.86394 +4436,215066.3855492,0.054833,0.82807 +4437,215269.4612384,0.006029,0.80093 +4438,219714.0496174,0,0.76708 +4439,206057.2095204,0,0.70407 +4440,195718.8107988,0,0.64883 +4441,195174.1987232,0,0.6212 +4442,192548.0608337,0,0.5951 +4443,193614.2082019,0,0.55585 +4444,197652.6452025,0,0.47609 +4445,205807.9802655,0.0019657,0.3761 +4446,239246.2386307,0.082508,0.28427 +4447,272342.9606096,0.20664,0.32715 +4448,285325.9586471,0.30306,0.31969 +4449,286904.4105948,0.3689,0.26763 +4450,292775.1441546,0.39987,0.25044 +4451,302135.0872829,0.38844,0.2325 +4452,301419.7070142,0.34529,0.19633 +4453,298941.260535,0.27749,0.16436 +4454,292267.4549316,0.30074,0.15144 +4455,289839.7773747,0.30674,0.14315 +4456,283332.1246079,0.29324,0.12402 +4457,280650.6024395,0.2396,0.083258 +4458,280281.3739138,0.17696,0.044295 +4459,275998.3230148,0.11071,0.022022 +4460,266176.8442293,0.066013,0.014071 +4461,258639.9669469,0.0065193,0.011853 +4462,250798.4761308,0,0.012147 +4463,232004.744169,0,0.021896 +4464,216709.4524889,0,0.048212 +4465,206689.5133708,0,0.088544 +4466,204811.0632459,0,0.14112 +4467,209121.8062843,0,0.20182 +4468,212901.7833169,0,0.27309 +4469,219446.3589362,0.0010752,0.28978 +4470,248149.2614584,0.057439,0.24728 +4471,273889.1050613,0.11977,0.28276 +4472,286512.1052862,0.1665,0.45952 +4473,290319.7744582,0.18757,0.60008 +4474,295304.3595561,0.18201,0.59584 +4475,301581.2444942,0.20586,0.52272 +4476,298428.9559555,0.2185,0.38736 +4477,293472.062997,0.21912,0.22201 +4478,285805.9557306,0.20963,0.12519 +4479,280465.9881766,0.1922,0.092384 +4480,274973.7138558,0.16562,0.07327 +4481,275924.4773096,0.13071,0.05751 +4482,275915.2465965,0.092805,0.040159 +4483,272273.7302611,0.055212,0.025854 +4484,263504.552774,0.048139,0.019661 +4485,254989.2198984,0.0052908,0.016721 +4486,250678.47686,0,0.016786 +4487,231787.8224101,0,0.021853 +4488,218781.7475898,0,0.030124 +4489,212486.4012254,0,0.044144 +4490,210211.0304353,0,0.063281 +4491,210367.9525588,0,0.081805 +4492,214387.9281331,0,0.090713 +4493,220429.4298861,0.0013228,0.080298 +4494,248453.8749922,0.073482,0.076846 +4495,274369.1021448,0.1826,0.071327 +4496,284679.808727,0.28047,0.064097 +4497,284735.1930059,0.35687,0.056837 +4498,289124.397106,0.4065,0.054133 +4499,295904.3559105,0.41659,0.051627 +4500,292179.7631568,0.39731,0.040434 +4501,287725.9440646,0.35295,0.024915 +4502,282247.5158135,0.35646,0.012789 +4503,276801.3950584,0.34188,0.0057353 +4504,272970.6491035,0.30981,0 +4505,273482.953683,0.22058,0 +4506,273926.0279139,0.13787,0 +4507,267178.3766054,0.06995,0 +4508,247226.190144,0.052472,0 +4509,238304.70589,0.0053792,0 +4510,237663.1713265,0,0 +4511,227947.8457421,0,0 +4512,214521.7734737,0,0 +4513,210003.3393896,0,0 +4514,206786.4358588,0,0 +4515,207067.9726097,0,0 +4516,210529.4900388,0,0 +4517,215569.4594156,0,0 +4518,247286.1897794,0.023398,0 +4519,273750.6443642,0.0026999,0 +4520,284356.733767,0.028538,0 +4521,287001.3330828,0.073471,0 +4522,292821.2977203,0.13341,0 +4523,297418.1928662,0.17356,0 +4524,291759.7657087,0.20937,0 +4525,286996.7177262,0.23676,0 +4526,283405.9703131,0.21874,0 +4527,279810.6075434,0.1909,0.0057542 +4528,277429.0835522,0.1562,0.0093119 +4529,278338.3087969,0.13397,0.013938 +4530,279025.9969261,0.10328,0.018084 +4531,278624.4609044,0.066921,0.023046 +4532,269619.9002321,0.05118,0.036785 +4533,260961.4913027,0.0051634,0.054273 +4534,255644.6005316,0,0.066593 +4535,237677.0173962,0,0.074136 +4536,224472.4822433,0,0.073322 +4537,218477.134056,0,0.07044 +4538,213160.2432849,0,0.068098 +4539,212680.2462014,0,0.065159 +4540,216469.4539472,0,0.044328 +4541,222118.6503915,0,0.023014 +4542,251509.2410429,0.067631,0.010102 +4543,277927.542062,0.16899,0.009335 +4544,288704.3996579,0.22999,0.12000 +4545,292078.2253122,0.24952,0.013176 +4546,298308.9566846,0.22613,0.014462 +4547,302832.0061253,0.25503,0.015809 +4548,298327.4181109,0.26821,0.017258 +4549,292212.0706528,0.26929,0.020064 +4550,286442.8749376,0.28654,0.029239 +4551,281933.6715666,0.28794,0.048763 +4552,279939.8375274,0.27142,0.070024 +4553,280299.83534,0.21269,0.0782 +4554,278873.6901592,0.14986,0.066564 +4555,275033.7134912,0.088712,0.054595 +4556,263873.7812997,0.056856,0.056085 +4557,253696.9200581,0.0051634,0.053516 +4558,249580.0219958,0,0.0561 +4559,232161.6662925,0,0.057814 +4560,218721.7479544,0,0.057724 +4561,209509.4962363,0,0.062313 +4562,204455.6807898,0,0.072319 +4563,200828.0105241,0,0.09167 +4564,202184.9253563,0,0.10092 +4565,199729.55566,0,0.078704 +4566,210127.954017,0.068732,0.053014 +4567,221587.8843857,0.17806,0.043216 +4568,232849.3544217,0.30201,0.057485 +4569,243796.9802109,0.4229,0.10384 +4570,250323.0944039,0.53047,0.18108 +4571,251490.7796167,0.58768,0.30102 +4572,243473.9052508,0.61566,0.46799 +4573,232789.3547863,0.61522,0.64262 +4574,225284.7850000,0.56209,0.74429 +4575,222109.4196783,0.4848,0.77251 +4576,222995.5681402,0.39097,0.72732 +4577,227227.8501168,0.29,0.64689 +4578,234783.1888255,0.19117,0.56432 +4579,235401.6466061,0.10403,0.52894 +4580,226669.3919716,0.060822,0.50292 +4581,222510.9557001,0.0051438,0.45895 +4582,221407.8854794,0,0.44812 +4583,210266.4147142,0,0.44595 +4584,195234.1983587,0,0.42251 +4585,189331.1573029,0,0.38293 +4586,187461.9378912,0,0.33234 +4587,186534.2512202,0,0.29786 +4588,186285.0219653,0,0.25713 +4589,181554.2814788,0,0.18022 +4590,184881.9535673,0.070541,0.1082 +4591,192146.5248119,0.18661,0.11427 +4592,199757.2477994,0.30542,0.11871 +4593,207561.8157629,0.41321,0.15139 +4594,214918.6941389,0.50225,0.18064 +4595,221384.8086965,0.53464,0.20933 +4596,215306.384091,0.53473,0.24132 +4597,208240.273179,0.50536,0.26991 +4598,204903.3703773,0.47767,0.27673 +4599,203380.3027085,0.42855,0.27032 +4600,204635.6796962,0.36259,0.2813 +4601,210903.3339211,0.25507,0.29329 +4602,220281.7384758,0.15641,0.28091 +4603,224601.7122273,0.076551,0.31213 +4604,224121.7151438,0.052734,0.40505 +4605,223927.8701678,0.0037391,0.40706 +4606,227869.3846804,0,0.33471 +4607,217244.8338513,0,0.27077 +4608,203712.6083817,0,0.21837 +4609,199397.2499868,0,0.16697 +4610,202300.3092706,0,0.11102 +4611,201552.621506,0,0.060988 +4612,204861.8321682,0,0.038119 +4613,213178.7047112,0,0.024457 +4614,247424.6504766,0.053091,0.015956 +4615,274835.2531586,0.12457,0.015241 +4616,291270.5379121,0.24553,0.017022 +4617,297012.0414878,0.38568,0.020775 +4618,302818.1600556,0.53151,0.020412 +4619,308910.4307308,0.5675,0.021701 +4620,305430.4518754,0.5703,0.024037 +4621,302744.3143504,0.54286,0.028211 +4622,298105.8809954,0.52686,0.034049 +4623,290910.5400994,0.48682,0.039694 +4624,288561.3236042,0.42506,0.045585 +4625,288016.7115287,0.29551,0.65000 +4626,285229.0361591,0.17762,0.11606 +4627,281342.9059253,0.083868,0.24073 +4628,270782.9700883,0.053942,0.34275 +4629,264275.3173215,0.0035378,0.31393 +4630,256373.82687,0,0.20141 +4631,237875.4777288,0,0.12728 +4632,225377.0921314,0,0.091723 +4633,222797.1078076,0,0.073312 +4634,215177.154107,0,0.054416 +4635,213667.9325079,0,0.036061 +4636,215800.2272442,0,0.029065 +4637,221537.1154634,0,0.024137 +4638,251320.0114235,0.050514,0.023098 +4639,275462.9416524,0.11941,0.01964 +4640,287887.4815447,0.21967,0.017589 +4641,292608.991318,0.32744,0.017799 +4642,300062.792182,0.43211,0.013836 +4643,304738.1483896,0.50581,0.011782 +4644,299725.8711522,0.55868,0.013333 +4645,296384.352994,0.58706,0.016931 +4646,291459.7675315,0.57906,0.021422 +4647,288879.7832077,0.54458,0.025839 +4648,284112.1198686,0.48479,0.030601 +4649,281287.5216465,0.34947,0.034341 +4650,278725.9987489,0.22072,0.0368 +4651,276921.3943292,0.11253,0.045844 +4652,267704.5272546,0.061783,0.065211 +4653,259572.2689745,0.003669,0.075105 +4654,253216.9229746,0,0.057616 +4655,234017.0396345,0,0.0343 +4656,220840.196621,0,0.018826 +4657,212454.0937294,0,0.010791 +4658,209804.879057,0,0.0064879 +4659,211535.6377715,0,0 +4660,216058.6872122,0,0 +4661,222603.2628315,0,0 +4662,252053.8531185,0.049253,0 +4663,276736.7800663,0.11935,0 +4664,290310.543745,0.22069,0.0065386 +4665,293984.3675765,0.32985,0.0104 +4666,300436.6360643,0.43637,0.01667 +4667,305965.8332378,0.48896,0.023869 +4668,303455.0792625,0.51685,0.029015 +4669,300053.5614689,0.51951,0.031061 +4670,294413.5957377,0.51605,0.029759 +4671,290176.6984045,0.48817,0.026788 +4672,281905.9794272,0.43632,0.022715 +4673,280784.4477801,0.31028,0.016164 +4674,285824.4171569,0.19257,0.0087328 +4675,284485.963751,0.095563,0.0064323 +4676,275130.6359792,0.055629,0.012566 +4677,268189.1396947,0.0031035,0.031233 +4678,259539.9614785,0,0.060507 +4679,241927.7607992,0,0.096834 +4680,229604.7587515,0,0.14588 +4681,221514.0386805,0,0.20359 +4682,217470.9863233,0,0.25372 +4683,216977.1431701,0,0.30045 +4684,219686.3574779,0,0.35371 +4685,227555.5404335,0,0.37693 +4686,253299.9993929,0.054783,0.30491 +4687,279907.5300314,0.14601,0.30453 +4688,290481.3119382,0.2231,0.42961 +4689,294658.209636,0.27674,0.61346 +4690,301447.3991537,0.30322,0.72445 +4691,308601.2018405,0.34607,0.78587 +4692,303113.5428762,0.37152,0.81313 +4693,297561.2689199,0.381000,0.79803 +4694,290744.3872628,0.38951,0.66682 +4695,286350.5678061,0.37681,0.47134 +4696,281467.5205528,0.34298,0.33437 +4697,276644.4729349,0.25335,0.26296 +4698,272398.3448885,0.16569,0.20433 +4699,273002.9565995,0.08882,0.12245 +4700,266075.3063847,0.053582,0.088986 +4701,256659.9789775,0.0029222,0.073723 +4702,251467.7028338,0,0.054924 +4703,231501.6703027,0,0.044104 +4704,215832.5347402,0,0.04059 +4705,210118.7233039,0,0.039724 +4706,205466.4438791,0,0.038758 +4707,208240.273179,0,0.03348 +4708,213252.5504164,0,0.025384 +4709,219298.6675259,0,0.017746 +4710,245306.20181,0.050691,0.014777 +4711,267506.066922,0.13301,0.025853 +4712,277036.7782435,0.22363,0.043545 +4713,276081.3994331,0.30605,0.083467 +4714,281605.98125,0.37333,0.15456 +4715,296056.6626774,0.40133,0.25278 +4716,289922.853793,0.40309,0.32944 +4717,282150.5933255,0.38254,0.35938 +4718,275176.789545,0.36875,0.36563 +4719,271650.6571238,0.33768,0.35554 +4720,270035.2823236,0.29108,0.34536 +4721,271913.7324484,0.22555,0.33779 +4722,270746.0472357,0.15663,0.29522 +4723,268406.0614536,0.090293,0.21489 +4724,257269.206045,0.053568,0.11995 +4725,247904.6475601,0.0027382,0.090687 +4726,244729.2822385,0,0.054626 +4727,226544.7773442,0,0.033184 +4728,211194.1013852,0,0.026205 +4729,202591.0767347,0,0.028175 +4730,195658.8111633,0,0.03737 +4731,197740.3369774,0,0.049311 +4732,202411.0778284,0,0.052415 +4733,201700.3129163,0,0.044301 +4734,205064.9078574,0.061107,0.039047 +4735,212758.7072631,0.18119,0.046958 +4736,224310.9447632,0.25989,0.04613 +4737,235563.1840862,0.29466,0.048128 +4738,245167.7411128,0.28369,0.052152 +4739,250410.7861788,0.32372,0.058723 +4740,245080.0493379,0.3474,0.065051 +4741,236860.0992829,0.35406,0.070027 +4742,228289.3821284,0.3377,0.070731 +4743,222654.0317538,0.30593,0.066389 +4744,223858.6398192,0.26071,0.057697 +4745,225746.3206572,0.20054,0.043508 +4746,224583.250801,0.1379,0.026953 +4747,222261.7264452,0.078482,0.015968 +4748,211581.7913372,0.049011,0.011748 +4749,205097.2153534,0.0013989,0.0081451 +4750,205651.058142,0,0.0060873 +4751,193161.9032578,0,0 +4752,180345.0580569,0,0 +4753,174091.2499016,0,0.0068011 +4754,171105.1141995,0,0.010985 +4755,173431.2539118,0,0.018725 +4756,173089.7175255,0,0.030349 +4757,168654.3598596,0,0.036918 +4758,171354.3434544,0.058061,0.020334 +4759,178706.6064738,0.17895,0.018141 +4760,184914.2610633,0.28232,0.016922 +4761,189995.7686493,0.36053,0.015157 +4762,197001.8799258,0.40876,0.014964 +4763,205540.2895843,0.429000,0.014705 +4764,201898.7732489,0.41991,0.012945 +4765,191675.7584415,0.38626,0.011083 +4766,186012.7159275,0.37797,0.010405 +4767,182329.6613829,0.35142,0.011595 +4768,182209.6621121,0.30825,0.014638 +4769,190064.9989979,0.22385,0.016423 +4770,197343.4163122,0.14284,0.015163 +4771,201677.2361334,0.073694,0.014466 +4772,201644.9286374,0.046825,0.01835 +4773,202729.5374319,0.0011232,0.030006 +4774,208184.8889001,0,0.064982 +4775,194569.5870123,0,0.13442 +4776,184138.8811592,0,0.22951 +4777,177003.5398987,0,0.35525 +4778,177681.9973148,0,0.49758 +4779,181568.1275485,0,0.59326 +4780,186792.7111882,0,0.60627 +4781,195086.5069484,0,0.5929 +4782,226729.3916071,0.04765,0.58881 +4783,254859.9899143,0.13907,0.65788 +4784,269047.5960171,0.21252,0.79919 +4785,273533.7226053,0.25857,0.82824 +4786,283613.6613588,0.27264,0.83227 +4787,290882.84796,0.32102,0.84669 +4788,286682.8734793,0.35542,0.88476 +4789,280996.7541824,0.37277,0.91023 +4790,275758.324473,0.36892,0.91337 +4791,271904.5017353,0.34676,0.89585 +4792,270990.661134,0.30732,0.86838 +4793,272610.6512908,0.23469,0.82911 +4794,270607.5865385,0.1598,0.75651 +4795,268378.3693142,0.08959,0.47864 +4796,258372.2762657,0.051342,0.28262 +4797,252090.775971,0.0011807,0.29349 +4798,246206.1963415,0,0.36668 +4799,225497.0914023,0,0.52254 +4800,212094.0959168,0,0.7197 +4801,206712.5901536,0,0.84209 +4802,205803.3649089,0,0.8548 +4803,205438.7517397,0,0.83031 +4804,209532.5730192,0,0.78035 +4805,216944.8356741,0,0.70782 +4806,243076.9845856,0.037428,0.65021 +4807,266167.6135161,0.097507,0.73337 +4808,278670.6144701,0.14695,0.87298 +4809,283244.4328331,0.17321,0.93718 +4810,288450.5550465,0.17389,0.96815 +4811,293375.140509,0.20406,0.97884 +4812,290185.9291176,0.2248,0.98176 +4813,286636.7199136,0.23382,0.98711 +4814,282145.9779689,0.19239,0.99013 +4815,276829.0871978,0.1442,0.99001 +4816,273404.4926212,0.095178,0.98757 +4817,276358.3208274,0.11652,0.98034 +4818,276436.7818892,0.11358,0.96464 +4819,274747.5613837,0.086253,0.94649 +4820,264695.3147696,0.019806,0.94279 +4821,257947.6634611,0,0.92932 +4822,251375.3957024,0,0.9103 +4823,230717.0596854,0,0.88092 +4824,215214.0769595,0,0.87305 +4825,209643.3415769,0,0.87078 +4826,209158.7291369,0,0.88682 +4827,208226.4271093,0,0.93714 +4828,213031.0133009,0,0.96085 +4829,219543.2814242,0,0.96943 +4830,246206.1963415,0.049862,0.97271 +4831,270639.8940345,0.16057,0.97618 +4832,282418.2840067,0.20629,0.97859 +4833,285704.417886,0.18723,0.98237 +4834,292124.3788779,0.10385,0.98824 +4835,297616.6531988,0.12113,0.99275 +4836,294455.1339468,0.13211,0.99548 +4837,292470.5306208,0.136000,0.99613 +4838,288085.9418773,0.10788,0.99744 +4839,286498.2592164,0.076227,0.99614 +4840,283964.4284583,0.045632,0.9916 +4841,285141.3443842,0.060684,0.97949 +4842,284642.8858744,0.061125,0.96171 +4843,279487.5325833,0.046695,0.95242 +4844,266139.9213767,0.0093575,0.93176 +4845,260841.4920318,0,0.89398 +4846,251998.4688396,0,0.86161 +4847,232046.2823782,0,0.81599 +4848,219132.5146893,0,0.75396 +4849,212384.8633808,0,0.68895 +4850,213206.3968506,0,0.62714 +4851,214083.3145994,0,0.56961 +4852,218694.0558149,0,0.5311 +4853,225834.0124321,0,0.49600 +4854,250826.1682703,0.022606,0.45306 +4855,275384.4805907,0.04024,0.41636 +4856,288229.017931,0.051967,0.41731 +4857,291856.6881967,0.044211,0.51908 +4858,298382.8023897,0.018077,0.5594 +4859,303842.7692146,0.070173,0.58257 +4860,301415.0916576,0.13146,0.64565 +4861,298728.9541327,0.19125,0.69041 +4862,294708.9785583,0.17309,0.7107 +4863,289881.3155838,0.14714,0.70626 +4864,285939.8010712,0.1164,0.69809 +4865,284947.4994082,0.098897,0.65297 +4866,281998.2865586,0.074696,0.54431 +4867,276261.3983394,0.046068,0.37712 +4868,265359.926116,0.0090561,0.29855 +4869,257453.8203079,0,0.27176 +4870,246478.5023793,0,0.25457 +4871,226314.0095156,0,0.25875 +4872,213524.8564541,0,0.28334 +4873,206569.5140999,0,0.31535 +4874,206791.0512153,0,0.35182 +4875,207787.9682349,0,0.36721 +4876,211835.6359487,0,0.35825 +4877,219317.1289522,0,0.34562 +4878,244641.5904636,0.034851,0.33072 +4879,270284.5115785,0.10573,0.36149 +4880,285981.3392804,0.16678,0.43153 +4881,291256.6918423,0.20483,0.44662 +4882,295068.9763709,0.21766,0.43707 +4883,299675.1022299,0.22437,0.42737 +4884,294815.1317595,0.21367,0.42774 +4885,289152.0892454,0.18826,0.43933 +4886,281578.2891105,0.184000,0.45634 +4887,276681.3957875,0.17066,0.46355 +4888,274327.5639357,0.14868,0.43100 +4889,272292.1916874,0.11075,0.36315 +4890,268442.9843062,0.072675,0.2531 +4891,262313.7907784,0.038201,0.12866 +4892,249086.1788426,0.0069369,0.077752 +4893,241092.3812596,0,0.061924 +4894,231487.8242329,0,0.058436 +4895,212961.7829523,0,0.05523 +4896,198437.2558198,0,0.051243 +4897,192386.5233537,0,0.042364 +4898,188505.0084765,0,0.031677 +4899,188528.0852593,0,0.023267 +4900,191357.2988381,0,0.015504 +4901,194564.9716557,0,0.010113 +4902,203435.6869874,0.037187,0.0089552 +4903,215689.4586865,0.1219,0.011487 +4904,230458.5997174,0.22116,0.014724 +4905,245984.6592261,0.31717,0.016389 +4906,256706.1325432,0.40162,0.015334 +4907,261030.7216513,0.43801,0.013608 +4908,254241.5321337,0.44705,0.010711 +4909,245610.8153437,0.43148,0.0075654 +4910,236250.8722154,0.36793,0.0060088 +4911,231266.2871175,0.28965,0.0060491 +4912,229110.9155983,0.20632,0.0071469 +4913,228824.7634908,0.14203,0.0085396 +4914,229512.4516201,0.084006,0.0089507 +4915,225289.4003565,0.038163,0.0093217 +4916,215915.6111585,0.0067647,0.019132 +4917,211097.1788972,0,0.050835 +4918,209514.1115929,0,0.088052 +4919,195857.2714959,0,0.11537 +4920,188029.6267495,0,0.14105 +4921,180626.5948078,0,0.15944 +4922,174866.6298058,0,0.16519 +4923,176477.3892494,0,0.17927 +4924,177206.6155878,0,0.23015 +4925,174289.7102342,0,0.22964 +4926,175060.4747818,0.03388,0.20982 +4927,178365.0700875,0.11245,0.27915 +4928,187521.9375266,0.15865,0.26922 +4929,194966.5076775,0.1643,0.24865 +4930,204081.8369075,0.12903,0.19108 +4931,212403.3248071,0.15779,0.18211 +4932,206149.5166518,0.18008,0.21692 +4933,200315.7059446,0.19399,0.23595 +4934,197477.2616527,0.14518,0.21702 +4935,195746.5029382,0.091845,0.16717 +4936,195368.0436993,0.042719,0.10879 +4937,199120.3285924,0.066124,0.049933 +4938,205401.8288871,0.070392,0.02257 +4939,207464.8932749,0.054931,0.012872 +4940,205387.9828174,0.010577,0.012628 +4941,206283.3619924,0,0.017798 +4942,207432.5857789,0,0.018593 +4943,192404.9847799,0,0.012496 +4944,183105.0412871,0,0.0066122 +4945,181037.3615427,0,0 +4946,181009.6694033,0,0.0082843 +4947,182818.8891796,0,0.02593 +4948,188094.2417416,0,0.048533 +4949,197966.4894494,0,0.057176 +4950,226364.7784379,0.026404,0.057508 +4951,257181.5142701,0.083839,0.063349 +4952,273944.4893402,0.13365,0.045842 +4953,282279.8233095,0.16337,0.031701 +4954,289950.5459324,0.16919,0.024901 +4955,297676.6528342,0.17816,0.028526 +4956,296638.1976055,0.1737,0.051361 +4957,291547.4593064,0.15805,0.099532 +4958,286290.5681707,0.18485,0.15263 +4959,282164.4393952,0.19886,0.16349 +4960,275481.4030787,0.19704,0.1223 +4961,273847.5668522,0.15229,0.079476 +4962,273206.0322886,0.1043,0.045616 +4963,266310.6895698,0.057724,0.022645 +4964,252921.540154,0.011059,0.014177 +4965,245984.6592261,0,0.0094984 +4966,234930.8802358,0,0.0067106 +4967,214429.4663423,0,0 +4968,201095.7012053,0,0 +4969,195548.0426056,0,0.0061639 +4970,194883.4312592,0,0.0082834 +4971,194389.588106,0,0.011578 +4972,198700.3311444,0,0.019986 +4973,207229.5100897,0,0.033689 +4974,233426.2739932,0.023436,0.034482 +4975,260638.4163427,0.071415,0.027244 +4976,275061.4056306,0.10593,0.024649 +4977,282930.5885862,0.11551,0.022496 +4978,289996.6994981,0.099172,0.016915 +4979,295968.9709025,0.13386,0.01124 +4980,291699.7660733,0.16541,0.0079484 +4981,287633.6369332,0.19027,0.0070464 +4982,281665.9808854,0.1964,0.0076145 +4983,275947.5540925,0.19165,0.010494 +4984,268899.9046068,0.17523,0.012568 +4985,267639.9122626,0.10621,0.0094781 +4986,267090.6848305,0.050627,0.0088264 +4987,265599.9246577,0.014227,0.021413 +4988,256263.0583123,0.00090233,0.043445 +4989,251546.1638955,0,0.052823 +4990,240990.843415,0,0.05001 +4991,218887.900791,0,0.057409 +4992,204247.9897441,0,0.074593 +4993,199692.6328074,0,0.087571 +4994,199581.8642496,0,0.086705 +4995,199988.015628,0,0.070793 +4996,205337.2138951,0,0.049474 +4997,212477.1705122,0,0.029873 +4998,239047.7782981,0.020797,0.012448 +4999,265683.001076,0.062868,0.0072896 +5000,283447.5085223,0.14889,0.0069732 +5001,291233.6150595,0.25835,0.0086397 +5002,298765.8769852,0.37931,0.012445 +5003,306505.8299567,0.43089,0.020859 +5004,303228.9267905,0.45922,0.029266 +5005,298216.6495531,0.4638,0.032253 +5006,290832.0790377,0.41888,0.029164 +5007,282362.8997278,0.35503,0.021003 +5008,274964.4831426,0.27878,0.015423 +5009,272610.6512908,0.20971,0.010145 +5010,271867.5788827,0.13935,0.0062914 +5011,267699.9118981,0.074138,0 +5012,256267.6736688,0.014095,0 +5013,247156.9597954,0,0 +5014,236089.3347354,0,0.010671 +5015,215527.9212064,0,0.019607 +5016,204852.601455,0,0.030043 +5017,198631.1007958,0,0.040408 +5018,198774.1768495,0,0.050492 +5019,198658.7929352,0,0.057325 +5020,202651.0763701,0,0.079632 +5021,210317.1836365,0,0.11187 +5022,235747.798349,0.0282,0.10454 +5023,261284.5662628,0.119000,0.11357 +5024,279898.2993183,0.21815,0.18735 +5025,288339.7864887,0.30961,0.30379 +5026,293033.6041226,0.38616,0.30566 +5027,296592.0440397,0.3721,0.21205 +5028,291205.92292,0.32235,0.12666 +5029,286369.0292324,0.24392,0.058835 +5030,280729.0635013,0.23503,0.023932 +5031,277447.5449785,0.21388,0.011159 +5032,272462.9598805,0.18241,0.0073582 +5033,271789.117821,0.15374,0 +5034,269126.0570789,0.11471,0 +5035,266236.8438647,0.068877,0.0072571 +5036,256484.5954277,0.012589,0.015205 +5037,251689.2399493,0,0.025322 +5038,239527.7753816,0,0.036157 +5039,219386.3593008,0,0.044658 +5040,202784.9217107,0,0.041831 +5041,198224.9494174,0,0.03033 +5042,196992.6492127,0,0.014834 +5043,196895.7267247,0,0.0062417 +5044,199508.0185445,0,0 +5045,206227.9777135,0,0.022873 +5046,230966.2889403,0.011594,0.094994 +5047,256789.2089615,0.019966,0.20516 +5048,272259.8841914,0.065833,0.25804 +5049,276672.1650743,0.13457,0.29167 +5050,281527.5201882,0.21892,0.44834 +5051,287250.5623377,0.25896,0.62238 +5052,282745.9743233,0.28631,0.69997 +5053,273819.8747127,0.29861,0.72156 +5054,265766.0774943,0.2986,0.70506 +5055,260204.5728249,0.28242,0.64363 +5056,256410.7497226,0.25067,0.53677 +5057,255123.0652389,0.19555,0.37681 +5058,253023.0779986,0.13504,0.18239 +5059,248943.1027888,0.074763,0.083653 +5060,238844.702609,0.01321,0.066618 +5061,235373.9544667,0,0.064778 +5062,226129.3952527,0,0.083247 +5063,208701.8088362,0,0.12927 +5064,194260.358122,0,0.20195 +5065,186294.2526784,0,0.27437 +5066,182671.1977693,0,0.34448 +5067,180778.9015747,0,0.42169 +5068,182629.6595601,0,0.4982 +5069,183898.8826175,0,0.59173 +5070,190401.9200276,0.019999,0.6593 +5071,200809.5490978,0.078003,0.76199 +5072,213363.3189741,0.12058,0.91425 +5073,225524.7835417,0.1356,0.96383 +5074,232360.1266251,0.12118,0.95995 +5075,234437.0370826,0.15728,0.94185 +5076,231100.1342809,0.18834,0.94621 +5077,223817.10161,0.21101,0.93734 +5078,218717.1325978,0.18429,0.93166 +5079,213307.9346952,0.14881,0.95253 +5080,212094.0959168,0.11012,0.96300 +5081,216589.453218,0.096519,0.94932 +5082,220674.0437844,0.074055,0.9241 +5083,219667.8960517,0.044758,0.90914 +5084,210003.3393896,0.0066185,0.88868 +5085,207677.1996772,0,0.85947 +5086,203555.6862583,0,0.82098 +5087,192709.5983137,0,0.79066 +5088,175628.1636402,0,0.74707 +5089,169978.9671959,0,0.68475 +5090,166471.296201,0,0.62619 +5091,167329.7525234,0,0.56145 +5092,167800.5188938,0,0.50106 +5093,167726.6731886,0,0.4432 +5094,169388.2015546,0.022944,0.35474 +5095,172674.335434,0.10149,0.39047 +5096,181305.0522239,0.17234,0.42468 +5097,190637.3032128,0.21989,0.34971 +5098,200740.3187493,0.24072,0.27971 +5099,209509.4962363,0.2842,0.25782 +5100,203961.8376366,0.31316,0.26678 +5101,196692.6510355,0.32628,0.2821 +5102,193438.8246521,0.34136,0.28301 +5103,189866.5386653,0.33718,0.27733 +5104,190609.6110734,0.31143,0.29064 +5105,196374.191432,0.25082,0.28096 +5106,204151.0672561,0.17903,0.22036 +5107,206675.667301,0.10218,0.11669 +5108,205286.4449728,0.018292,0.063474 +5109,209241.8055552,0,0.04221 +5110,206500.2837513,0,0.031538 +5111,190383.4586013,0,0.025591 +5112,181609.6657577,0,0.022177 +5113,179472.7556648,0,0.021577 +5114,179431.2174556,0,0.02277 +5115,179052.7582167,0,0.024816 +5116,184125.0350895,0,0.023236 +5117,195631.1190239,0,0.018258 +5118,223180.1824031,0.027623,0.012656 +5119,253064.6162078,0.14678,0.012828 +5120,270275.2808653,0.22853,0.01199 +5121,278716.7680358,0.26002,0.012353 +5122,287956.7118932,0.23905,0.013597 +5123,296218.2001574,0.27355,0.01597 +5124,293398.2172918,0.29259,0.017231 +5125,288044.4036681,0.32824,0.021385 +5126,279759.8386211,0.34151,0.030557 +5127,275684.4787679,0.3346,0.036827 +5128,268775.2899794,0.30741,0.0321 +5129,266453.7656236,0.24604,0.02457 +5130,265816.8464166,0.17409,0.020883 +5131,262373.7904138,0.098004,0.021052 +5132,250886.1679057,0.016342,0.023782 +5133,244706.2054556,0,0.01963 +5134,230458.5997174,0,0.014239 +5135,210451.0289771,0,0.0092976 +5136,197897.2591008,0,0 +5137,195815.7332868,0,0 +5138,192441.9076325,0,0 +5139,191943.4491227,0,0 +5140,197537.2612882,0,0 +5141,204723.371471,0,0.01083 +5142,229027.83918,0.029469,0.017512 +5143,254352.3006914,0.16001,0.018786 +5144,270413.7415625,0.27587,0.022111 +5145,278758.3062449,0.35819,0.02819 +5146,281504.4434054,0.4014,0.0367 +5147,289059.782114,0.43999,0.049243 +5148,287236.716268,0.45053,0.074387 +5149,280239.8357046,0.43532,0.12697 +5150,273593.7222407,0.32503,0.19519 +5151,269582.9773795,0.20336,0.22625 +5152,264321.4708872,0.089378,0.2421 +5153,263403.0149294,0.12336,0.2543 +5154,263121.4781785,0.12414,0.27433 +5155,258856.8887058,0.090883,0.23242 +5156,250013.8655136,0.014523,0.20736 +5157,247415.4197634,0,0.1864 +5158,235161.6480644,0,0.15712 +5159,213538.7025238,0,0.15295 +5160,198741.8693535,0,0.17501 +5161,195732.6568685,0,0.24108 +5162,192501.907268,0,0.34755 +5163,192123.448029,0,0.41619 +5164,196992.6492127,0,0.39143 +5165,205549.5202974,0,0.33475 +5166,229678.6044567,0.027736,0.22416 +5167,256064.5979797,0.15718,0.21184 +5168,270026.0516104,0.27536,0.40761 +5169,272984.4951732,0.36209,0.54243 +5170,278342.9241534,0.41056,0.62946 +5171,285210.5747328,0.43507,0.6831 +5172,283572.1231497,0.42736,0.69078 +5173,282630.590409,0.39182,0.69331 +5174,277115.2393053,0.38166,0.69738 +5175,273695.2600853,0.35187,0.67229 +5176,267635.2969061,0.30389,0.61331 +5177,264164.5487638,0.2642,0.53392 +5178,264067.6262758,0.20192,0.38934 +5179,261773.7940594,0.12195,0.16875 +5180,253023.0779986,0.019923,0.093955 +5181,250526.1700931,0,0.065372 +5182,235535.4919467,0,0.051727 +5183,214000.2381811,0,0.047327 +5184,199443.4035525,0,0.055688 +5185,196711.1124618,0,0.086574 +5186,193775.7456819,0,0.15125 +5187,193424.9785824,0,0.25172 +5188,196992.6492127,0,0.30443 +5189,205475.6745923,0,0.3055 +5190,231192.4414123,0.016155,0.23501 +5191,256706.1325432,0.084251,0.26718 +5192,272370.6527491,0.16726,0.48355 +5193,279459.8404439,0.24632,0.68535 +5194,286064.4156987,0.31462,0.79626 +5195,291408.9986092,0.29153,0.85931 +5196,287070.5634314,0.23516,0.87622 +5197,282270.5925964,0.15418,0.87133 +5198,274936.7910032,0.13721,0.82302 +5199,270667.586174,0.11361,0.71595 +5200,267150.684466,0.08629,0.52603 +5201,268101.4479198,0.096533,0.24671 +5202,267344.529442,0.086897,0.11863 +5203,263730.705246,0.058113,0.10512 +5204,255432.2941293,0.0072931,0.18959 +5205,250766.1686348,0,0.46472 +5206,235203.1862735,0,0.62202 +5207,212564.8622871,0,0.54767 +5208,200126.4763252,0,0.47423 +5209,196891.1113681,0,0.45538 +5210,193803.4378213,0,0.45292 +5211,193637.2849847,0,0.44113 +5212,197841.8748219,0,0.39397 +5213,206814.1279982,0,0.35022 +5214,230537.0607791,0.023183,0.28945 +5215,255081.5270298,0.15121,0.24503 +5216,270455.2797716,0.24463,0.35713 +5217,275864.4776742,0.28257,0.43785 +5218,283835.1984743,0.26244,0.42996 +5219,290499.7733645,0.25592,0.37197 +5220,287513.6376623,0.2226,0.33672 +5221,278361.3855797,0.16921,0.29373 +5222,271581.4267752,0.16629,0.25308 +5223,265729.1546418,0.15396,0.21981 +5224,263352.2460071,0.13299,0.19578 +5225,264639.9304907,0.14163,0.17178 +5226,262590.7121727,0.12406,0.11477 +5227,257767.6645548,0.081752,0.08795 +5228,248006.1854047,0.011028,0.085854 +5229,241438.5330025,0,0.076919 +5230,229457.0673412,0,0.079844 +5231,210931.0260606,0,0.094387 +5232,198423.40975,0,0.1071 +5233,190904.993894,0,0.12563 +5234,184932.7224896,0,0.1449 +5235,185703.4870372,0,0.15993 +5236,186737.3269093,0,0.13848 +5237,190074.229711,0,0.10332 +5238,194320.3577574,0.021887,0.070959 +5239,204538.7572081,0.14909,0.049173 +5240,217623.2930902,0.27025,0.066465 +5241,225944.7809898,0.35972,0.079739 +5242,231381.6710318,0.41011,0.11219 +5243,234935.4955923,0.4557,0.17703 +5244,230213.985819,0.47193,0.23897 +5245,219912.50995,0.45969,0.29927 +5246,213769.4703524,0.45224,0.38528 +5247,209911.0322581,0.42105,0.47244 +5248,208221.8117527,0.36713,0.53197 +5249,213464.8568187,0.24523,0.46974 +5250,218334.0580023,0.13676,0.3472 +5251,217738.6770045,0.054494,0.31117 +5252,211609.4834767,0.0061208,0.32881 +5253,212241.7873271,0,0.3424 +5254,204215.6822481,0,0.30974 +5255,188772.6991577,0,0.27486 +5256,176463.5431797,0,0.26117 +5257,171506.6502212,0,0.27494 +5258,169840.5064987,0,0.26676 +5259,170643.5785422,0,0.21586 +5260,170689.732108,0,0.15796 +5261,170971.2688589,0,0.11383 +5262,169277.4329969,0.015289,0.082081 +5263,174382.0173657,0.099587,0.082361 +5264,186423.4826624,0.19511,0.095658 +5265,197717.2601945,0.2777,0.10333 +5266,209177.1905632,0.33938,0.13978 +5267,218269.4430103,0.36487,0.1761 +5268,213109.4743626,0.36286,0.21404 +5269,207506.431484,0.33737,0.27664 +5270,202614.1535176,0.353000,0.36038 +5271,199374.1732039,0.34704,0.4281 +5272,198644.9468655,0.31797,0.48702 +5273,202028.0032329,0.21287,0.51133 +5274,207732.5839561,0.11942,0.42874 +5275,208554.1174259,0.04785,0.35406 +5276,208277.1960316,0.0045077,0.34619 +5277,211780.2516698,0,0.33989 +5278,206786.4358588,0,0.32038 +5279,191352.6834815,0,0.31948 +5280,181549.6661222,0,0.35276 +5281,179195.8342704,0,0.39013 +5282,180128.136298,0,0.39863 +5283,181235.8218753,0,0.40975 +5284,186423.4826624,0,0.40731 +5285,199014.1753913,0,0.38128 +5286,229646.2969607,0.012681,0.32973 +5287,256964.5925112,0.092999,0.32594 +5288,272139.8849205,0.16254,0.46208 +5289,277904.4652791,0.19929,0.51857 +5290,283258.2789028,0.19941,0.53896 +5291,290310.543745,0.2427,0.59007 +5292,289069.0128271,0.27449,0.65806 +5293,287093.6402143,0.29186,0.71285 +5294,278993.6894301,0.28754,0.7512 +5295,270178.3583773,0.26709,0.76883 +5296,264339.9323135,0.23206,0.77928 +5297,264473.7776541,0.16342,0.75073 +5298,265041.4665125,0.098323,0.6554 +5299,263619.9366883,0.043562,0.42142 +5300,256447.6725752,0.0036532,0.32601 +5301,255395.3712767,0,0.2847 +5302,239707.774288,0,0.29195 +5303,216635.6067838,0,0.33154 +5304,200029.5538371,0,0.37597 +5305,195764.9643645,0,0.42305 +5306,195834.1947131,0,0.45372 +5307,196697.266392,0,0.46649 +5308,201201.8544065,0,0.46748 +5309,210649.4893097,0,0.4557 +5310,236084.7193788,0.010359,0.4185 +5311,257938.432748,0.076572,0.38384 +5312,268401.446097,0.14685,0.48833 +5313,273210.6476452,0.19947,0.5824 +5314,280272.1432006,0.22972,0.61169 +5315,286262.8760313,0.27117,0.61831 +5316,285976.7239238,0.29757,0.6464 +5317,284790.5772847,0.307000,0.68375 +5318,277646.0053111,0.31829,0.71293 +5319,272739.8812749,0.30989,0.73965 +5320,268521.4453679,0.28166,0.76264 +5321,269813.7452081,0.19202,0.76134 +5322,269993.7441144,0.11037,0.67379 +5323,266366.0738487,0.045486,0.5439 +5324,258681.5051561,0.0036741,0.5409 +5325,259309.1936499,0,0.53333 +5326,243372.3674062,0,0.52211 +5327,220770.9662724,0,0.4962 +5328,206181.8241478,0,0.47653 +5329,201589.5443585,0,0.48225 +5330,199549.5567536,0,0.50821 +5331,200509.5509206,0,0.55600 +5332,204658.756479,0,0.58959 +5333,215347.9223001,0,0.59323 +5334,241913.9147294,0.010331,0.55914 +5335,267012.2237688,0.084843,0.56331 +5336,280073.682868,0.15893,0.65035 +5337,286775.1806108,0.20751,0.64772 +5338,293347.4483695,0.22574,0.6447 +5339,299061.2598058,0.24096,0.6316 +5340,295992.0476854,0.23647,0.63196 +5341,289941.3152193,0.21541,0.63983 +5342,281038.2923916,0.24514,0.65487 +5343,274346.025362,0.2578,0.62268 +5344,269993.7441144,0.2495,0.56884 +5345,271429.1200084,0.16479,0.46872 +5346,271253.7364586,0.09024,0.31006 +5347,268826.0589017,0.034123,0.15193 +5348,263361.4767202,0.0017478,0.11833 +5349,259507.6539825,0,0.11084 +5350,242980.0620976,0,0.10027 +5351,219386.3593008,0,0.087803 +5352,204944.9085865,0,0.073944 +5353,201081.8551356,0,0.056191 +5354,198617.2547261,0,0.038995 +5355,199424.9421262,0,0.026234 +5356,205009.5235785,0,0.017972 +5357,214775.6180852,0,0.015084 +5358,242767.7556953,0.0055436,0.014392 +5359,268115.2939896,0.044533,0.014231 +5360,284476.7330378,0.09192,0.014574 +5361,288275.1714967,0.13051,0.016438 +5362,294704.3632017,0.15635,0.0093673 +5363,301105.8627673,0.17449,0.0064883 +5364,296222.815514,0.17988,0.007493 +5365,290181.313761,0.1741,0.0094975 +5366,284213.6577132,0.16473,0.016283 +5367,278347.53951,0.14605,0.032873 +5368,273672.1833024,0.12014,0.060884 +5369,274784.4842363,0.086536,0.10695 +5370,273746.0290076,0.053064,0.1422 +5371,270326.0497876,0.023257,0.16568 +5372,262055.3308103,0.00027438,0.19111 +5373,255538.4473304,0,0.20393 +5374,238701.6265552,0,0.18772 +5375,217272.5259907,0,0.17396 +5376,201631.0825677,0,0.17879 +5377,196157.2696731,0,0.20399 +5378,194800.3548409,0,0.24124 +5379,194943.4308946,0,0.26592 +5380,199628.0178154,0,0.24489 +5381,211978.7120025,0,0.2098 +5382,235683.183357,0.011452,0.15214 +5383,260726.1081175,0.12938,0.10702 +5384,276266.013696,0.25035,0.17272 +5385,280276.7585572,0.3378,0.20021 +5386,284389.041263,0.38485,0.20015 +5387,289078.2435403,0.42438,0.18201 +5388,284965.9608345,0.43443,0.17026 +5389,271733.7335421,0.41727,0.17029 +5390,264778.3911879,0.36379,0.17348 +5391,260689.185265,0.29435,0.15906 +5392,258981.5033333,0.21634,0.14942 +5393,263287.6310151,0.1417,0.1058 +5394,264367.624453,0.076485,0.053895 +5395,259521.5000522,0.027765,0.045626 +5396,252510.7734191,0.00062978,0.05726 +5397,248444.644279,0,0.086717 +5398,235133.9559249,0,0.11823 +5399,216026.3797162,0,0.15612 +5400,200071.0920463,0,0.19178 +5401,192104.9866028,0,0.21891 +5402,186225.0223298,0,0.24725 +5403,187092.7093654,0,0.32428 +5404,189672.6936892,0,0.40756 +5405,192123.448029,0,0.43267 +5406,196471.11392,0.0043753,0.38623 +5407,204395.6811544,0.042701,0.25988 +5408,219930.9713763,0.099465,0.11217 +5409,233181.6600949,0.15639,0.031045 +5410,240312.3859989,0.20646,0.015585 +5411,243123.1381513,0.2672,0.023491 +5412,237547.7874122,0.31942,0.046201 +5413,227961.6918118,0.35648,0.085818 +5414,219289.4368127,0.3329,0.11587 +5415,215061.7701926,0.29176,0.11827 +5416,216990.9892398,0.23742,0.091146 +5417,223877.1012455,0.15184,0.057364 +5418,228058.6142998,0.078841,0.042202 +5419,225612.4753166,0.026548,0.063035 +5420,221066.349093,0.00019786,0.1194 +5421,217918.6759108,0,0.17578 +5422,203611.0705371,0,0.19515 +5423,188071.1649587,0,0.18225 +5424,177732.7662371,0,0.18097 +5425,171640.4955618,0,0.2065 +5426,169535.8929649,0,0.23531 +5427,169365.1247718,0,0.22585 +5428,169720.5072278,0,0.20773 +5429,172162.0308545,0,0.19172 +5430,173805.0977942,0.0047802,0.16745 +5431,181332.7443633,0.070253,0.15809 +5432,191841.9112781,0.14448,0.18802 +5433,203214.1498719,0.20009,0.21989 +5434,214775.6180852,0.23181,0.22854 +5435,224481.7129564,0.25335,0.21351 +5436,221329.4244176,0.25518,0.19728 +5437,214600.2345354,0.23935,0.19677 +5438,209163.3444934,0.22113,0.2071 +5439,207847.9678704,0.19138,0.23005 +5440,207584.8925458,0.15333,0.2483 +5441,211895.6355842,0.15185,0.22447 +5442,216104.840778,0.12392,0.16034 +5443,216617.1453575,0.071695,0.1411 +5444,216372.5314591,0.00463,0.13497 +5445,215098.6930452,0,0.11291 +5446,207418.7397092,0,0.071051 +5447,189123.4662571,0,0.043893 +5448,183077.3491476,0,0.031363 +5449,179311.2181847,0,0.022911 +5450,178725.0679001,0,0.018003 +5451,180778.9015747,0,0.01541 +5452,185685.0256109,0,0.016124 +5453,198764.9461364,0,0.014442 +5454,232387.8187645,0.0071493,0.0090691 +5455,261529.1801611,0.11843,0.0062165 +5456,278287.5398746,0.2326,0.0060911 +5457,286756.7191845,0.30623,0.0068184 +5458,295765.8952133,0.33331,0.0072666 +5459,306330.446407,0.39123,0.0077289 +5460,302725.8529241,0.42668,0.0084526 +5461,298793.5691247,0.43805,0.0088095 +5462,292258.2242185,0.41312,0.0086923 +5463,284329.0416275,0.36669,0.0081856 +5464,277595.2363888,0.30273,0.0075022 +5465,276418.3204629,0.21422,0.0074272 +5466,274913.7142203,0.12815,0.0082184 +5467,272532.1902291,0.053977,0.011411 +5468,266162.9981595,0.0021818,0.015176 +5469,259438.4236339,0,0.016463 +5470,237727.7863185,0,0.01671 +5471,213211.0122072,0,0.017217 +5472,204432.604007,0,0.017342 +5473,201368.0072431,0,0.016739 +5474,197468.0309396,0,0.015919 +5475,198404.9483237,0,0.015223 +5476,202618.7688741,0,0.014029 +5477,215897.1497322,0,0.014448 +5478,242850.8321136,0.0062716,0.014771 +5479,268493.7532285,0.11439,0.015414 +5480,287933.6351104,0.26124,0.030562 +5481,293735.1383216,0.40146,0.035129 +5482,299744.3325785,0.51961,0.030096 +5483,307830.4372929,0.59269,0.024285 +5484,302268.9326235,0.62976,0.021623 +5485,299139.7208676,0.63141,0.02232 +5486,296375.1222809,0.60957,0.02735 +5487,292867.451286,0.55605,0.036436 +5488,289092.08961,0.47351,0.050007 +5489,286682.8734793,0.32993,0.064667 +5490,284504.4251773,0.19261,0.077445 +5491,282745.9743233,0.07784,0.14609 +5492,277955.2342014,0.0039962,0.23965 +5493,268276.8314696,0,0.29641 +5494,248795.4113785,0,0.33400 +5495,226775.5451728,0,0.34366 +5496,217854.0609188,0,0.32466 +5497,212463.3244425,0,0.30576 +5498,207021.819044,0,0.28598 +5499,207206.4333068,0,0.26363 +5500,209924.8783278,0,0.22881 +5501,220974.0419616,0,0.20014 +5502,246556.963441,0.0016016,0.1376 +5503,271309.1207375,0.11041,0.072612 +5504,289022.8592614,0.25593,0.062435 +5505,297067.4257667,0.3929,0.10485 +5506,304267.3820192,0.50903,0.13694 +5507,310696.5737242,0.59623,0.19545 +5508,306201.216423,0.65047,0.26858 +5509,302536.6233047,0.67015,0.33135 +5510,294704.3632017,0.62663,0.37451 +5511,286378.2599456,0.55175,0.39104 +5512,280862.9088418,0.45141,0.35205 +5513,279870.6071788,0.31688,0.27305 +5514,276713.7032835,0.18659,0.33521 +5515,272107.5774245,0.075477,0.49679 +5516,268678.3674914,0.0034013,0.60896 +5517,264653.7765604,0,0.63758 +5518,246653.885929,0,0.63727 +5519,225349.399992,0,0.61955 +5520,211563.329911,0,0.57687 +5521,206971.0501217,0,0.52103 +5522,202203.3867826,0,0.45832 +5523,203163.3809496,0,0.40625 +5524,207474.123988,0,0.38371 +5525,218292.5197932,0,0.36845 +5526,247267.7283531,0.0011365,0.28781 +5527,269181.4413577,0.10503,0.17323 +5528,281250.5987939,0.24812,0.14728 +5529,284610.5783784,0.38313,0.18058 +5530,287398.253748,0.49694,0.1811 +5531,295341.2824087,0.50547,0.15655 +5532,291247.4611292,0.46782,0.13173 +5533,286885.9491685,0.39067,0.10986 +5534,278892.1515855,0.29187,0.091148 +5535,270515.2794071,0.18299,0.084204 +5536,267215.299458,0.082151,0.076467 +5537,268793.7514057,0.052849,0.052997 +5538,268779.9053359,0.027199,0.039322 +5539,268724.5210571,0.0080148,0.042279 +5540,266236.8438647,0,0.050392 +5541,264081.4723455,0,0.053974 +5542,245269.2789574,0,0.046125 +5543,223720.179122,0,0.032655 +5544,209431.0351746,0,0.021628 +5545,205447.9824529,0,0.01504 +5546,201889.5425357,0,0.011083 +5547,201026.4708567,0,0.0085965 +5548,205032.6003614,0,0.0086759 +5549,216469.4539472,0,0.011322 +5550,241143.1501819,0,0.013494 +5551,265258.3882714,0.060619,0.01465 +5552,279039.8429958,0.14294,0.013691 +5553,281961.363706,0.21459,0.011038 +5554,285612.1107546,0.26778,0.0097215 +5555,291404.3832526,0.28305,0.0088602 +5556,289096.7049666,0.27269,0.0072812 +5557,282958.2807256,0.24123,0 +5558,267353.7601552,0.21664,0 +5559,263712.2438197,0.1811,0 +5560,262230.7143601,0.13883,0 +5561,264829.1601102,0.08884,0 +5562,265701.4625023,0.045527,0 +5563,263666.090254,0.013885,0.0063218 +5564,258118.4316543,0,0.0086617 +5565,255972.2908482,0,0.010718 +5566,240801.6137956,0,0.011374 +5567,221094.0412325,0,0.010925 +5568,202124.9257209,0,0.011331 +5569,193300.3639549,0,0.011881 +5570,192621.9065388,0,0.010965 +5571,196060.3471851,0,0.01025 +5572,195617.2729542,0,0.009798 +5573,197195.7249018,0,0.012179 +5574,199097.2518096,0,0.015968 +5575,210451.0289771,0.074723,0.022045 +5576,224029.4080123,0.16043,0.030036 +5577,235401.6466061,0.2125,0.034922 +5578,240053.9260309,0.22662,0.034886 +5579,244336.9769298,0.28821,0.034718 +5580,241433.9176459,0.33718,0.03833 +5581,232646.2787326,0.36858,0.049274 +5582,221592.4997422,0.353000,0.069845 +5583,218647.9022492,0.31762,0.098042 +5584,217664.8312993,0.2651,0.12866 +5585,220877.1194736,0.19284,0.14873 +5586,223738.6405483,0.11771,0.13887 +5587,223147.8749071,0.048038,0.11114 +5588,219201.7450379,0,0.10306 +5589,219483.2817888,0,0.068888 +5590,208881.8077425,0,0.046556 +5591,194163.4356339,0,0.037915 +5592,183852.7290517,0,0.035187 +5593,177215.846301,0,0.036464 +5594,176985.0784724,0,0.040542 +5595,177774.3044462,0,0.04512 +5596,176726.6185043,0,0.053581 +5597,177405.0759204,0,0.058013 +5598,177451.2294862,0,0.048961 +5599,181060.4383256,0.082382,0.0458 +5600,191578.8359535,0.18212,0.042262 +5601,198788.0229192,0.2482,0.037129 +5602,207566.4311195,0.27409,0.031957 +5603,218467.9033429,0.28549,0.027528 +5604,219243.283247,0.26951,0.025632 +5605,212251.0180402,0.23146,0.025963 +5606,207926.4289321,0.23689,0.028135 +5607,207511.0468406,0.22678,0.033123 +5608,210178.7229393,0.20064,0.03586 +5609,215800.2272442,0.15331,0.037244 +5610,219792.5106791,0.098224,0.037875 +5611,220974.0419616,0.041446,0.036583 +5612,222630.954971,0,0.037244 +5613,222081.7275389,0,0.031923 +5614,213164.8586415,0,0.027487 +5615,197131.1099098,0,0.021858 +5616,197038.8027784,0,0.015701 +5617,198012.6430151,0,0.011434 +5618,188241.9331519,0,0.0079469 +5619,186663.4812042,0,0 +5620,193498.8242875,0,0 +5621,209158.7291369,0,0 +5622,244567.7447584,0,0.0067708 +5623,271309.1207375,0.072453,0.011733 +5624,289590.5481198,0.17617,0.024026 +5625,300981.2481399,0.26289,0.036315 +5626,309053.5067846,0.32338,0.038857 +5627,317601.1471562,0.33111,0.041134 +5628,312861.1759566,0.30576,0.053112 +5629,307544.2851855,0.25442,0.08299 +5630,304096.6138261,0.21912,0.1343 +5631,302735.0836373,0.17351,0.18487 +5632,295179.7449287,0.12363,0.25508 +5633,292862.8359294,0.096572,0.32437 +5634,289978.2380718,0.062607,0.28458 +5635,287089.0248577,0.025799,0.19217 +5636,283405.9703131,0,0.15288 +5637,275232.1738238,0,0.12077 +5638,255099.9884561,0,0.085775 +5639,231450.9013804,0,0.042584 +5640,221546.3461765,0,0.017638 +5641,215615.6129813,0,0.0075751 +5642,210031.031529,0,0.0060852 +5643,210667.950736,0,0.009181 +5644,216686.375706,0,0.016235 +5645,227135.5429854,0,0.023806 +5646,253816.919329,0,0.028971 +5647,274613.7160431,0.090204,0.030024 +5648,290107.4680559,0.23652,0.056154 +5649,297990.4970811,0.37735,0.092845 +5650,304428.9194993,0.49668,0.14821 +5651,311218.1090169,0.53534,0.22431 +5652,307622.7462472,0.53067,0.27586 +5653,300427.4053512,0.4886,0.32188 +5654,294588.9792874,0.42327,0.38649 +5655,288995.167122,0.3378,0.4007 +5656,282316.7461621,0.24303,0.39588 +5657,281107.5227402,0.15403,0.32079 +5658,280105.990364,0.077261,0.27402 +5659,276524.473664,0.022203,0.32051 +5660,272393.7295319,0,0.40032 +5661,266019.9221058,0,0.47097 +5662,248767.7192391,0,0.51895 +5663,234626.266702,0,0.56838 +5664,223466.3345105,0,0.62457 +5665,218500.2108389,0,0.71479 +5666,213031.0133009,0,0.79955 +5667,211346.4081521,0,0.86088 +5668,216990.9892398,0,0.88256 +5669,230287.8315242,0,0.89331 +5670,256950.7464415,0,0.89894 +5671,278578.3073386,0.025297,0.89185 +5672,291072.0775794,0.068195,0.86807 +5673,294884.362108,0.10417,0.81476 +5674,300196.6375226,0.12812,0.7233 +5675,305495.0668674,0.1917,0.63853 +5676,301050.4784884,0.25285,0.61154 +5677,296439.7372729,0.30364,0.53277 +5678,290467.4658685,0.2932,0.42056 +5679,282349.0536581,0.26533,0.39382 +5680,276538.3197337,0.2224,0.35732 +5681,277747.5431556,0.13686,0.18717 +5682,277133.7007315,0.065476,0.12779 +5683,275144.4820489,0.016751,0.15313 +5684,272762.9580577,0,0.18200 +5685,264736.8529787,0,0.21161 +5686,244272.3619378,0,0.28683 +5687,223826.3323232,0,0.38417 +5688,210520.2593257,0,0.4725 +5689,205678.7502815,0,0.55235 +5690,201538.7754362,0,0.58541 +5691,202540.3078124,0,0.58555 +5692,207007.9729742,0,0.56454 +5693,218850.9779384,0,0.55942 +5694,248629.2585419,0,0.56024 +5695,273099.8790875,0.057584,0.56543 +5696,285229.0361591,0.15185,0.68391 +5697,288884.3985642,0.23229,0.83505 +5698,294995.1306658,0.28973,0.91102 +5699,300902.7870781,0.35627,0.93505 +5700,298124.3424217,0.40369,0.93882 +5701,297773.5753222,0.42778,0.90922 +5702,293236.6798118,0.42375,0.87445 +5703,290379.7740936,0.39357,0.82615 +5704,284430.5794721,0.33829,0.76762 +5705,285109.0368882,0.21391,0.56557 +5706,283336.7399645,0.10651,0.28245 +5707,282469.052929,0.029699,0.19917 +5708,278998.3047867,0,0.17229 +5709,266989.146986,0,0.17112 +5710,247586.1879566,0,0.20255 +5711,225003.2482491,0,0.2697 +5712,213201.7814941,0,0.3301 +5713,208955.6534477,0,0.38432 +5714,206107.9784427,0,0.42016 +5715,206343.3616279,0,0.43228 +5716,210737.1810845,0,0.41157 +5717,225289.4003565,0,0.38395 +5718,256101.5208322,0,0.32299 +5719,281435.2130568,0.072971,0.22915 +5720,297833.5749577,0.19292,0.28463 +5721,303644.308882,0.29283,0.36545 +5722,310576.5744534,0.36168,0.36714 +5723,316853.4593915,0.35823,0.31902 +5724,308536.5868485,0.3137,0.26424 +5725,297515.1153542,0.23866,0.20765 +5726,282565.975417,0.25109,0.14709 +5727,275089.0977701,0.24579,0.093316 +5728,271470.6582175,0.22106,0.068634 +5729,275232.1738238,0.13926,0.046219 +5730,276999.855391,0.068784,0.026929 +5731,273893.7204179,0.018202,0.022105 +5732,268558.3682205,0,0.02158 +5733,257149.2067741,0,0.0174 +5734,241096.9966162,0,0.012329 +5735,222261.7264452,0,0.01208 +5736,208812.577394,0,0.01368 +5737,203878.7612183,0,0.015699 +5738,200394.1670063,0,0.017962 +5739,197237.263111,0,0.022141 +5740,196060.3471851,0,0.028706 +5741,198312.6411923,0,0.033217 +5742,203878.7612183,0,0.039887 +5743,213612.548229,0.075007,0.040592 +5744,228630.9185148,0.22201,0.042591 +5745,239781.6199931,0.36362,0.064365 +5746,248472.3364185,0.48379,0.073656 +5747,253060.0008512,0.52388,0.079195 +5748,245638.5074832,0.52126,0.086072 +5749,235032.4180804,0.48103,0.090327 +5750,227933.9996724,0.43877,0.085791 +5751,223927.8701678,0.37221,0.074491 +5752,223314.0277437,0.28912,0.079742 +5753,225774.0127966,0.18216,0.075327 +5754,229803.2190841,0.089936,0.054758 +5755,230186.2936796,0.023599,0.048165 +5756,227610.9247123,0,0.047391 +5757,221638.653308,0,0.056874 +5758,211443.3306401,0,0.072109 +5759,196461.8832069,0,0.10429 +5760,182158.8931898,0,0.1589 +5761,176112.7760802,0,0.22472 +5762,172272.7994122,0,0.27919 +5763,170038.9668313,0,0.32485 +5764,175586.625431,0,0.35365 +5765,175909.7003911,0,0.36998 +5766,178434.300436,0,0.33831 +5767,184318.8800655,0.071625,0.21015 +5768,192764.9825926,0.21748,0.16002 +5769,202004.92645,0.35716,0.26191 +5770,211004.8717657,0.47592,0.31827 +5771,223110.9520545,0.55407,0.28004 +5772,223586.3337814,0.5963,0.21999 +5773,217969.4448331,0.60247,0.15826 +5774,212306.4023191,0.56873,0.11582 +5775,208291.0421013,0.50333,0.088533 +5776,212246.4026836,0.41118,0.075736 +5777,214992.5398441,0.26908,0.052741 +5778,222695.569963,0.1394,0.041544 +5779,225206.3239382,0.039445,0.048868 +5780,228095.5371524,0,0.055009 +5781,221814.0368577,0,0.047541 +5782,213635.6250119,0,0.029191 +5783,197504.9537922,0,0.024755 +5784,187231.1700626,0,0.018471 +5785,183395.8087511,0,0.01155 +5786,183954.2668963,0,0.0086388 +5787,187365.0154032,0,0.008115 +5788,188638.8538171,0,0.0077722 +5789,203227.9959416,0,0.0092818 +5790,241775.4540323,0,0.013217 +5791,268572.2142902,0.068348,0.013132 +5792,285178.2672368,0.21421,0.01229 +5793,291155.1539977,0.35355,0.01643 +5794,296130.5083825,0.47144,0.014056 +5795,301165.8624028,0.54151,0.010518 +5796,297833.5749577,0.57433,0.0075679 +5797,293296.6794472,0.57114,0.0061995 +5798,287892.0969012,0.52852,0.0060343 +5799,282644.4364787,0.45708,0.0065784 +5800,276889.0868332,0.3635,0.010883 +5801,277355.237847,0.24075,0.023192 +5802,277719.8510162,0.12638,0.066229 +5803,277632.1592413,0.035523,0.17974 +5804,274835.2531586,0,0.29143 +5805,263107.6321088,0,0.34544 +5806,242800.0631913,0,0.36487 +5807,220577.1212964,0,0.38228 +5808,207566.4311195,0,0.38565 +5809,204843.3707419,0,0.37067 +5810,204012.6065589,0,0.34616 +5811,206094.132373,0,0.31396 +5812,211992.5580722,0,0.25296 +5813,224107.8690741,0,0.20472 +5814,252492.3119928,0,0.21147 +5815,275366.0191644,0.063679,0.21783 +5816,286207.4917524,0.20959,0.25717 +5817,289724.3934604,0.34871,0.34758 +5818,296753.5815198,0.46558,0.4383 +5819,300035.1000426,0.54225,0.48185 +5820,293296.6794472,0.58387,0.45466 +5821,287389.0230349,0.59012,0.41935 +5822,280152.1439297,0.55754,0.37734 +5823,274018.3350453,0.49346,0.32612 +5824,268115.2939896,0.40252,0.2432 +5825,269564.5159532,0.2672,0.24457 +5826,270690.6629568,0.13992,0.34279 +5827,271212.1982495,0.037507,0.46306 +5828,274239.8721608,0,0.51441 +5829,263744.5513157,0,0.49763 +5830,241761.6079626,0,0.49872 +5831,219626.3578425,0,0.50025 +5832,213561.7793067,0,0.49613 +5833,211627.944903,0,0.50492 +5834,210054.1083119,0,0.52644 +5835,210487.9518296,0,0.53979 +5836,215975.6107939,0,0.48846 +5837,227837.0771844,0,0.44034 +5838,257610.7424313,0,0.39700 +5839,279736.7618382,0.060435,0.25949 +5840,289549.0099106,0.20576,0.16812 +5841,290476.6965816,0.34493,0.18063 +5842,292696.6830928,0.46216,0.21966 +5843,297778.1906788,0.46685,0.23509 +5844,294861.2853252,0.42194,0.25476 +5845,294002.8290028,0.33775,0.29239 +5846,289775.1623827,0.2895,0.30375 +5847,282999.8189348,0.22676,0.28127 +5848,274452.1785631,0.1584,0.27208 +5849,273916.7972008,0.099404,0.22454 +5850,275712.1709073,0.047583,0.15368 +5851,278624.4609044,0.0097814,0.10776 +5852,281052.1384613,0,0.067236 +5853,268184.5243381,0,0.037451 +5854,247512.3422515,0,0.018927 +5855,226494.0084219,0,0.010173 +5856,213280.2425558,0,0.0060006 +5857,209181.8059197,0,0 +5858,208000.2746373,0,0 +5859,208217.1963961,0,0 +5860,214747.9259457,0,0 +5861,226443.2394996,0,0.0061932 +5862,255510.755191,0,0.010342 +5863,277544.4674665,0.07037,0.012016 +5864,286710.5656188,0.22309,0.012898 +5865,287527.483732,0.34407,0.012661 +5866,290975.1550914,0.42052,0.011891 +5867,297099.7332627,0.47928,0.013364 +5868,294455.1339468,0.50141,0.015035 +5869,294205.9046919,0.48931,0.015897 +5870,288155.1722258,0.45093,0.016202 +5871,283881.35204,0.38709,0.015202 +5872,277595.2363888,0.30403,0.015566 +5873,277484.467831,0.19327,0.020566 +5874,279432.1483045,0.094363,0.03713 +5875,281421.3669871,0.020516,0.079228 +5876,283419.8163828,0,0.11988 +5877,269596.8234492,0,0.12334 +5878,249026.1792071,0,0.11733 +5879,228220.1517799,0,0.11826 +5880,216658.6835666,0,0.12573 +5881,212149.4801956,0,0.1297 +5882,211180.2553155,0,0.12032 +5883,212477.1705122,0,0.10855 +5884,219566.3582071,0,0.11637 +5885,231118.5957072,0,0.13181 +5886,260435.3406535,0,0.1335 +5887,282067.5169072,0.067717,0.09112 +5888,293070.5269752,0.22878,0.1027 +5889,293818.2147399,0.36829,0.10336 +5890,297228.9632467,0.46966,0.084981 +5891,299185.8744333,0.56201,0.073534 +5892,293310.5255169,0.61917,0.080953 +5893,287721.3287081,0.63904,0.10593 +5894,281965.9790626,0.61691,0.14694 +5895,276224.4754868,0.55813,0.19261 +5896,270270.6655088,0.46498,0.23507 +5897,272287.5763308,0.30707,0.23435 +5898,272227.5766953,0.15721,0.26636 +5899,273330.6469161,0.036905,0.36587 +5900,276173.7065645,0,0.39462 +5901,262286.0986389,0,0.3806 +5902,244581.5908281,0,0.38641 +5903,223660.1794866,0,0.38855 +5904,207469.5086315,0,0.37133 +5905,199558.7874668,0,0.33042 +5906,198677.2543615,0,0.28714 +5907,196346.4992926,0,0.26551 +5908,197712.6448379,0,0.24049 +5909,200901.8562293,0,0.2231 +5910,206227.9777135,0,0.20153 +5911,217014.0660227,0.062476,0.11047 +5912,234409.3449431,0.23628,0.071147 +5913,243810.8262806,0.4024,0.096717 +5914,248952.333502,0.5419,0.11696 +5915,252321.5437996,0.62119,0.14263 +5916,244013.9019698,0.6558,0.16046 +5917,236541.6396795,0.64827,0.16505 +5918,228926.3013354,0.59649,0.15188 +5919,223401.7195185,0.51146,0.13415 +5920,221541.73082,0.40101,0.11848 +5921,225930.9349201,0.27053,0.10455 +5922,232106.2820136,0.14186,0.19181 +5923,235590.8762256,0.032258,0.34274 +5924,237727.7863185,0,0.42015 +5925,227661.6936346,0,0.42814 +5926,217590.9855942,0,0.42011 +5927,199401.8653433,0,0.40578 +5928,185435.796356,0,0.39256 +5929,180520.4416067,0,0.38558 +5930,179205.0649836,0,0.37697 +5931,178572.7611332,0,0.36041 +5932,180035.8291666,0,0.34669 +5933,181268.1293713,0,0.34401 +5934,177261.9998667,0,0.32826 +5935,182611.1981338,0.058439,0.22178 +5936,191251.1456369,0.22881,0.16591 +5937,201054.1629962,0.39365,0.25438 +5938,206869.5122771,0.53206,0.40788 +5939,211203.3320983,0.62729,0.41204 +5940,207418.7397092,0.6807,0.3695 +5941,199748.0170862,0.69192,0.29907 +5942,194283.4349048,0.64587,0.21377 +5943,190720.3796311,0.56297,0.14113 +5944,192991.1350646,0.4498,0.087758 +5945,203264.9187942,0.29796,0.064172 +5946,217747.9077176,0.15153,0.081744 +5947,226484.7777087,0.032066,0.13174 +5948,234326.2685248,0,0.18456 +5949,229770.9115881,0,0.21118 +5950,220715.5819935,0,0.20300 +5951,202849.5367027,0,0.19358 +5952,190300.382183,0,0.1893 +5953,187775.7821381,0,0.18712 +5954,187226.554706,0,0.18471 +5955,188878.8523588,0,0.18001 +5956,196623.4206869,0,0.17412 +5957,213395.6264701,0,0.17617 +5958,251033.859316,0,0.17575 +5959,274964.4831426,0.055131,0.10042 +5960,286313.6449536,0.2239,0.044535 +5961,286276.722101,0.38883,0.040992 +5962,287550.5605149,0.52648,0.03805 +5963,291155.1539977,0.61782,0.029449 +5964,286609.0277742,0.66762,0.021452 +5965,280825.9859893,0.67697,0.018704 +5966,276838.3179109,0.60274,0.018231 +5967,273141.4172966,0.49562,0.017231 +5968,268456.8303759,0.36748,0.014438 +5969,270667.586174,0.23478,0.0093788 +5970,275241.404537,0.11303,0.0068752 +5971,280327.5274795,0.02088,0 +5972,283756.7374126,0,0 +5973,269061.4420868,0,0 +5974,248740.0270997,0,0.0091078 +5975,228570.9188793,0,0.015519 +5976,211526.4070584,0,0.023499 +5977,206947.9733388,0,0.030967 +5978,202969.5359736,0,0.03705 +5979,203892.607288,0,0.04183 +5980,210040.2622421,0,0.042771 +5981,225538.6296114,0,0.042879 +5982,257799.9720508,0,0.046642 +5983,281172.1377322,0.049507,0.04011 +5984,295964.3555459,0.21617,0.02753 +5985,303122.7735893,0.37926,0.054264 +5986,306671.9827933,0.5167,0.075664 +5987,318348.8349209,0.60782,0.092999 +5988,312505.7935005,0.65692,0.11318 +5989,309921.1938201,0.66467,0.12525 +5990,306916.5966917,0.55548,0.12002 +5991,304433.5348558,0.41806,0.092095 +5992,296804.3504421,0.27184,0.060397 +5993,296402.8144203,0.17772,0.034928 +5994,294076.6747079,0.087438,0.021059 +5995,295378.2052613,0.015138,0.014603 +5996,295424.358827,0,0.010742 +5997,278522.9230598,0,0.011254 +5998,258104.5855846,0,0.018469 +5999,236860.0992829,0,0.03448 +6000,224504.7897393,0,0.060449 +6001,218352.5194286,0,0.095597 +6002,212527.9394345,0,0.13396 +6003,213640.2403684,0,0.1701 +6004,220637.1209318,0,0.19319 +6005,233606.2728996,0,0.23247 +6006,266781.4559402,0,0.26322 +6007,292664.3755968,0.046706,0.20128 +6008,305527.3743634,0.2112,0.16335 +6009,310595.0358797,0.37445,0.2518 +6010,318556.5259666,0.51187,0.32197 +6011,325742.6361495,0.59813,0.27061 +6012,323379.5735845,0.64141,0.19319 +6013,322451.8869135,0.64304,0.12269 +6014,315621.1591867,0.58641,0.07098 +6015,310493.4980351,0.49656,0.038543 +6016,304835.0708776,0.38174,0.02119 +6017,300718.1728153,0.24438,0.0084926 +6018,297270.5014559,0.1165,0 +6019,297201.2711073,0.019068,0 +6020,293393.6019352,0,0.0076164 +6021,277987.5416974,0,0.022801 +6022,257093.8224953,0,0.047932 +6023,234044.7317739,0,0.075454 +6024,221846.3443537,0,0.096934 +6025,217549.447385,0,0.10439 +6026,212943.321526,0,0.10283 +6027,212897.1679603,0,0.099213 +6028,218449.4419166,0,0.087823 +6029,232950.8922663,0,0.081943 +6030,266536.8420419,0,0.073089 +6031,291616.689655,0.044165,0.037506 +6032,304461.2269953,0.19177,0.019539 +6033,305938.1410984,0.31695,0.018605 +6034,309298.1206829,0.39966,0.018075 +6035,318718.0634467,0.44568,0.017987 +6036,313396.557319,0.45231,0.019358 +6037,306044.2942995,0.42392,0.021269 +6038,303635.0781689,0.36836,0.023886 +6039,296785.8890158,0.29282,0.02706 +6040,290425.9276593,0.20702,0.027747 +6041,289041.3206877,0.13362,0.027665 +6042,289719.7781038,0.063436,0.034717 +6043,292336.6852802,0.0085925,0.040757 +6044,292479.761334,0,0.036475 +6045,276076.7840765,0,0.02864 +6046,255552.2934002,0,0.02627 +6047,234653.9588414,0,0.027468 +6048,219464.8203625,0,0.029341 +6049,214946.3862783,0,0.0306 +6050,210377.1832719,0,0.027411 +6051,210755.6425108,0,0.028729 +6052,215472.5369276,0,0.029972 +6053,228063.2296564,0,0.029324 +6054,263223.0160231,0,0.027401 +6055,287689.0212121,0.040031,0.026647 +6056,298101.2656388,0.18024,0.024524 +6057,299924.3314848,0.2857,0.021074 +6058,305291.9911783,0.3363,0.014113 +6059,307539.6698289,0.4319,0.0092677 +6060,300921.2485044,0.50469,0.0073494 +6061,293435.1401444,0.54765,0 +6062,285649.0336072,0.50719,0 +6063,281393.6748476,0.43585,0.0075066 +6064,277646.0053111,0.34,0.013338 +6065,278887.536229,0.2155,0.019117 +6066,279049.073709,0.099602,0.033835 +6067,281139.8302362,0.013436,0.071038 +6068,283032.1264308,0,0.12312 +6069,265346.0800463,0,0.16169 +6070,248721.5656734,0,0.16556 +6071,227578.6172163,0,0.15292 +6072,206703.3594405,0,0.12556 +6073,197195.7249018,0,0.099619 +6074,194551.125586,0,0.077838 +6075,195151.1219404,0,0.052738 +6076,196092.6546811,0,0.046571 +6077,198769.5614929,0,0.053251 +6078,205572.5970803,0,0.061428 +6079,216160.2250568,0.024767,0.068805 +6080,233961.6553556,0.11247,0.062795 +6081,245130.8182602,0.16373,0.051817 +6082,247604.6493829,0.16466,0.044765 +6083,246326.1956124,0.19424,0.040574 +6084,240695.4605944,0.20815,0.044066 +6085,232157.0509359,0.20639,0.054259 +6086,224384.7904684,0.17373,0.06935 +6087,218260.2122972,0.13225,0.092446 +6088,218463.2879863,0.088127,0.11413 +6089,224850.9414822,0.091963,0.12012 +6090,231690.8999221,0.062693,0.094691 +6091,235720.1062096,0.01076,0.11823 +6092,240160.079232,0,0.12764 +6093,225755.5513703,0,0.12253 +6094,214526.3888303,0,0.12896 +6095,198206.4879911,0,0.13913 +6096,189146.54304,0,0.16149 +6097,179071.219643,0,0.18626 +6098,175406.6265247,0,0.20232 +6099,177548.1519742,0,0.21354 +6100,177511.2291216,0,0.2362 +6101,179698.9081368,0,0.27051 +6102,182851.1966756,0,0.3062 +6103,186215.7916167,0.035675,0.33994 +6104,196258.8075177,0.16461,0.37547 +6105,203569.532328,0.24696,0.40798 +6106,212195.6337613,0.26227,0.42848 +6107,221140.1947982,0.32911,0.43671 +6108,219617.1271294,0.37567,0.42366 +6109,211831.0205921,0.3979,0.44500 +6110,205004.9082219,0.39586,0.5163 +6111,200744.9341058,0.36519,0.57445 +6112,202627.9995873,0.306000,0.59046 +6113,210631.0278834,0.21325,0.5636 +6114,219570.9735636,0.10813,0.40741 +6115,230449.3690042,0.014599,0.27623 +6116,239610.8517999,0,0.18156 +6117,229877.0647893,0,0.10882 +6118,222414.0332121,0,0.07209 +6119,206043.3634507,0,0.056277 +6120,194371.1266797,0,0.046982 +6121,191477.2981089,0,0.040856 +6122,189072.6973349,0,0.035258 +6123,189815.769743,0,0.027483 +6124,197223.4170413,0,0.019348 +6125,211904.8662973,0,0.014085 +6126,257647.6652839,0,0.010087 +6127,286493.6438599,0.032202,0.0059495 +6128,299785.8707877,0.1881,0 +6129,303048.9278842,0.33927,0 +6130,307913.5137112,0.45581,0 +6131,313544.2487293,0.53996,0 +6132,308624.2786234,0.58322,0 +6133,305375.0675966,0.58641,0 +6134,297658.1914079,0.57579,0 +6135,293887.4450885,0.52539,0 +6136,286724.4116885,0.43652,0 +6137,286447.4902942,0.29882,0 +6138,286710.5656188,0.14781,0 +6139,291935.1492584,0.017792,0 +6140,293287.4487341,0,0.0069945 +6141,274198.3339517,0,0.021374 +6142,251047.7053857,0,0.073837 +6143,225986.3191989,0,0.18213 +6144,214032.5456771,0,0.30909 +6145,208009.5053504,0,0.44751 +6146,207289.5097251,0,0.57045 +6147,210400.2600548,0,0.65239 +6148,216658.6835666,0,0.70401 +6149,232295.5116331,0,0.76715 +6150,269426.055256,0,0.81315 +6151,294524.3642954,0.029791,0.81627 +6152,307895.052285,0.14788,0.84912 +6153,310419.6523299,0.20546,0.94378 +6154,307147.3645203,0.18286,0.96705 +6155,321611.8920174,0.18872,0.95325 +6156,312524.2549268,0.17093,0.89137 +6157,309967.3473858,0.13399,0.77763 +6158,303888.9227803,0.14638,0.68527 +6159,298488.9555909,0.14475,0.61811 +6160,292821.2977203,0.12744,0.55669 +6161,291824.3807007,0.10544,0.51205 +6162,292964.373774,0.059697,0.4608 +6163,298207.41884,0.0063655,0.40058 +6164,295239.7445641,0,0.34321 +6165,276095.2455028,0,0.32999 +6166,255404.6019898,0,0.28835 +6167,234907.8034529,0,0.22965 +6168,223941.7162375,0,0.1967 +6169,222012.4971903,0,0.17011 +6170,219483.2817888,0,0.15466 +6171,221920.1900589,0,0.15849 +6172,227740.1546964,0,0.15013 +6173,242024.6832872,0,0.13555 +6174,277369.0839167,0,0.10929 +6175,297330.5010913,0.027845,0.086448 +6176,305564.297216,0.1564,0.099709 +6177,306344.2924767,0.24499,0.22294 +6178,311707.3368135,0.26826,0.35988 +6179,317919.6067597,0.28073,0.43597 +6180,314264.2443545,0.25841,0.45944 +6181,310368.8834076,0.2109,0.39934 +6182,304973.5315748,0.15155,0.25996 +6183,299038.183023,0.088283,0.09851 +6184,293176.6801764,0.033599,0.028487 +6185,291815.1499876,0.024291,0.0087332 +6186,290905.9247429,0.011113,0 +6187,294699.7478452,0,0 +6188,296961.2725655,0,0.012257 +6189,281384.4441345,0,0.030569 +6190,262004.561888,0,0.074378 +6191,238918.5483141,0,0.15945 +6192,223955.5623072,0,0.30519 +6193,220290.9691889,0,0.51739 +6194,215343.3069436,0,0.72571 +6195,216358.6853894,0,0.86704 +6196,222898.6456522,0,0.87456 +6197,237552.4027688,0,0.8008 +6198,272513.7288028,0,0.59844 +6199,294972.0538829,0.024281,0.30867 +6200,302264.3172669,0.16363,0.16085 +6201,300741.2495981,0.28203,0.23418 +6202,303528.9249677,0.34914,0.37838 +6203,312625.7927714,0.40664,0.52286 +6204,314402.7050517,0.42823,0.64299 +6205,311928.873929,0.41637,0.71812 +6206,306861.2124128,0.39509,0.76647 +6207,302735.0836373,0.34637,0.81418 +6208,294436.6725205,0.27375,0.86185 +6209,291893.6110493,0.21573,0.84196 +6210,290712.0797668,0.11589,0.82967 +6211,296605.8901095,0.011071,0.8242 +6212,295733.5877173,0,0.75779 +6213,277609.0824585,0,0.65269 +6214,257772.2799114,0,0.53041 +6215,235493.9537376,0,0.41318 +6216,217189.4495724,0,0.32855 +6217,213557.1639501,0,0.27534 +6218,209260.2669815,0,0.25343 +6219,210612.5664571,0,0.24198 +6220,217189.4495724,0,0.21444 +6221,231044.750002,0,0.17844 +6222,266066.0756715,0,0.14367 +6223,289032.0899746,0.022441,0.10041 +6224,297524.3460673,0.17802,0.084437 +6225,297145.8868284,0.35015,0.10751 +6226,301179.7084725,0.49677,0.10255 +6227,307313.5173569,0.51109,0.10193 +6228,302952.0053962,0.46076,0.096288 +6229,297238.1939598,0.36167,0.080605 +6230,290029.0069941,0.35623,0.066174 +6231,287282.8698337,0.32355,0.059226 +6232,284790.5772847,0.26462,0.07059 +6233,284089.0430858,0.22667,0.073918 +6234,284564.4248127,0.12797,0.071868 +6235,288644.4000225,0.010542,0.090991 +6236,289276.7038729,0,0.087296 +6237,270639.8940345,0,0.062466 +6238,256161.5204677,0,0.026996 +6239,236841.6378566,0,0.0096035 +6240,220014.0477946,0,0 +6241,211738.7134607,0,0 +6242,208097.1971253,0,0 +6243,209984.8779633,0,0.0068541 +6244,211572.5606241,0,0.011663 +6245,211406.4077875,0,0.018544 +6246,214812.5409378,0,0.027899 +6247,224241.7144147,0.020838,0.034246 +6248,239232.392561,0.17339,0.02203 +6249,250964.6289674,0.34136,0.020861 +6250,255127.6805955,0.48278,0.017414 +6251,255113.8345258,0.53627,0.013668 +6252,250632.3232942,0.53693,0.010147 +6253,242753.9096256,0.49269,0.0077382 +6254,237838.5548762,0.47282,0.0064247 +6255,235212.4169867,0.41992,0.0057029 +6256,232595.5098103,0.33579,0.0057703 +6257,235101.6484289,0.25452,0.0061171 +6258,239287.7768399,0.12872,0.0068361 +6259,246164.6581324,0.0087115,0.0066328 +6260,245393.8935848,0,0.0064147 +6261,231298.5946135,0,0.005888 +6262,222654.0317538,0,0.0057166 +6263,206786.4358588,0,0.0068634 +6264,193803.4378213,0,0.0088761 +6265,185832.7170212,0,0.011484 +6266,183654.2687191,0,0.013413 +6267,181955.8175006,0,0.013464 +6268,183589.6537271,0,0.012018 +6269,182080.432128,0,0.010387 +6270,182791.1970402,0,0.0091855 +6271,186409.6365927,0.017001,0.007858 +6272,195261.8904981,0.16777,0 +6273,204367.989015,0.33762,0.0059751 +6274,213183.3200678,0.48132,0.011657 +6275,220909.4269696,0.56805,0.013755 +6276,217494.0631062,0.6078,0.012958 +6277,208014.120707,0.60265,0.011617 +6278,202060.3107289,0.55902,0.01064 +6279,198248.0262003,0.47857,0.0097714 +6280,197135.7252664,0.36777,0.0098359 +6281,206324.9002016,0.26698,0.0099898 +6282,219164.8221853,0.12774,0.011668 +6283,231533.9777987,0.0056812,0.014515 +6284,236061.6425959,0,0.016377 +6285,225506.3221154,0,0.017317 +6286,220134.0470654,0,0.019121 +6287,204792.6018196,0,0.021996 +6288,196148.03896,0,0.022965 +6289,193383.4403732,0,0.022302 +6290,190877.3017546,0,0.021826 +6291,192958.8275686,0,0.021969 +6292,198981.8678953,0,0.022654 +6293,213358.7036175,0,0.02333 +6294,257523.0506565,0,0.022848 +6295,284070.5816595,0.01553,0.020147 +6296,294893.5928212,0.1625,0.011223 +6297,295295.128843,0.33117,0.0069507 +6298,298562.8012961,0.47095,0 +6299,301858.1658886,0.53521,0 +6300,297704.3449736,0.54924,0 +6301,294727.4399846,0.51871,0 +6302,289299.7806557,0.47833,0 +6303,286124.4153341,0.40605,0 +6304,281361.3673516,0.30836,0 +6305,282699.8207576,0.23628,0.0068602 +6306,287218.2548417,0.11566,0.013065 +6307,296684.3511712,0.0045248,0.019435 +6308,294704.3632017,0,0.02061 +6309,275864.4776742,0,0.016634 +6310,257310.7442541,0,0.011568 +6311,234626.266702,0,0.0085704 +6312,218809.4397292,0,0.0077139 +6313,215777.1504613,0,0.0076149 +6314,212467.9397991,0,0.0083645 +6315,212707.9383409,0,0.0098646 +6316,220623.2748621,0,0.010799 +6317,233989.3474951,0,0.0099045 +6318,273178.3401492,0,0.0080245 +6319,293365.9097958,0.013972,0.0068208 +6320,301410.4763011,0.15725,0 +6321,300436.6360643,0.32613,0 +6322,303348.9260614,0.46777,0.0060088 +6323,305836.6032538,0.55301,0.0066342 +6324,301258.1695342,0.59167,0.0082007 +6325,297441.269649,0.5857,0.01074 +6326,293388.9865787,0.538000,0.014247 +6327,289032.0899746,0.45456,0.01844 +6328,283059.8185702,0.34292,0.023201 +6329,283927.5056058,0.24795,0.026334 +6330,286881.3338119,0.11305,0.045338 +6331,296836.6579381,0.0034013,0.080864 +6332,295392.051331,0,0.11074 +6333,275726.016977,0,0.12092 +6334,255321.5255715,0,0.1222 +6335,231653.9770695,0,0.1207 +6336,220166.3545615,0,0.12593 +6337,213598.7021593,0,0.13666 +6338,208715.6549059,0,0.14943 +6339,210312.5682799,0,0.15532 +6340,218726.3633109,0,0.13935 +6341,230481.6765002,0,0.11201 +6342,271253.7364586,0,0.087684 +6343,292507.4534734,0.01085,0.059645 +6344,298996.6448138,0.15245,0.023007 +6345,297150.502185,0.32095,0.017919 +6346,299144.3362241,0.46233,0.034877 +6347,302218.1637012,0.54727,0.031533 +6348,298493.5709475,0.58559,0.029532 +6349,295724.3570042,0.57931,0.029178 +6350,291575.1514458,0.53128,0.029763 +6351,288418.2475505,0.44779,0.032336 +6352,281596.7505368,0.33614,0.030772 +6353,283119.8182056,0.24129,0.033969 +6354,285219.805446,0.10658,0.056177 +6355,296578.19797,0.0011586,0.0909 +6356,295918.2019802,0,0.12458 +6357,276533.7043772,0,0.15093 +6358,255529.2166173,0,0.17642 +6359,234718.5738335,0,0.1758 +6360,218827.9011555,0,0.15556 +6361,215698.6893996,0,0.1313 +6362,211531.022415,0,0.11125 +6363,212329.4791019,0,0.10235 +6364,219012.5154184,0,0.093693 +6365,233352.4282881,0,0.097086 +6366,270713.7397397,0,0.10942 +6367,291076.692936,0.0097739,0.10405 +6368,297279.732169,0.14801,0.051408 +6369,296121.2776694,0.31612,0.042073 +6370,298668.9544972,0.45633,0.10744 +6371,300953.5560004,0.52661,0.18621 +6372,296208.9694443,0.54745,0.21399 +6373,293121.2958975,0.52366,0.20755 +6374,290633.6187051,0.48166,0.1802 +6375,276067.5533634,0.40666,0.1373 +6376,275938.3233794,0.30529,0.077252 +6377,281605.98125,0.22416,0.037553 +6378,286752.1038279,0.098228,0.030473 +6379,299942.7929111,0.00051143,0.031038 +6380,298373.5716766,0,0.034867 +6381,278855.228733,0,0.04091 +6382,256572.2872026,0,0.056409 +6383,234460.1138654,0,0.08032 +6384,220037.1245774,0,0.10313 +6385,214507.927404,0,0.11878 +6386,207741.8146692,0,0.12205 +6387,210063.339025,0,0.11357 +6388,218661.7483189,0,0.09043 +6389,232073.9745176,0,0.069492 +6390,270016.8208973,0,0.055509 +6391,292572.0684654,0.0086488,0.042777 +6392,300072.0228951,0.12159,0.023185 +6393,298313.5720412,0.22187,0.022814 +6394,301595.090564,0.26057,0.024471 +6395,303432.0024797,0.2984,0.02625 +6396,297316.6550216,0.30391,0.022012 +6397,291150.5386412,0.28227,0.017045 +6398,286770.5652542,0.28598,0.014121 +6399,284749.0390756,0.26424,0.012213 +6400,282662.897905,0.21619,0.0093277 +6401,286544.4127822,0.18311,0.0059143 +6402,289825.931305,0.085889,0 +6403,296596.6593963,0,0 +6404,285889.0321489,0,0 +6405,266998.3776991,0,0 +6406,248707.7196037,0,0.01055 +6407,226055.5495475,0,0.017673 +6408,208434.118155,0,0.027679 +6409,199120.3285924,0,0.044505 +6410,193568.0546361,0,0.071767 +6411,196946.4956469,0,0.099305 +6412,198308.0258357,0,0.11027 +6413,202563.3845953,0,0.096903 +6414,208618.7324179,0,0.088902 +6415,220221.7388403,0.0057034,0.08751 +6416,238946.2404535,0.10866,0.084408 +6417,251606.163531,0.18507,0.10200 +6418,256073.8286928,0.18814,0.16677 +6419,258810.7351401,0.25068,0.30885 +6420,252995.3858592,0.29835,0.37966 +6421,241124.6887556,0.32522,0.38235 +6422,234307.8070985,0.29374,0.32106 +6423,231630.9002867,0.24134,0.23745 +6424,231760.1302707,0.17415,0.15258 +6425,237704.7095356,0.15974,0.079589 +6426,244983.1268499,0.075647,0.064441 +6427,251924.6231344,0,0.064396 +6428,242486.2189444,0,0.060266 +6429,228506.3038873,0,0.061738 +6430,218901.7468607,0,0.068811 +6431,200781.8569584,0,0.074389 +6432,189368.0801555,0,0.074239 +6433,183635.8072929,0,0.07978 +6434,181014.2847599,0,0.09011 +6435,180455.8266146,0,0.091649 +6436,180912.7469153,0,0.10267 +6437,179869.67633,0,0.13521 +6438,181286.5907976,0,0.16968 +6439,186035.7927104,0.0049599,0.16700 +6440,199600.3256759,0.10268,0.12732 +6441,207751.0453824,0.17157,0.21906 +6442,218144.8283828,0.16268,0.42605 +6443,227066.3126368,0.22105,0.49288 +6444,222824.799947,0.26618,0.49471 +6445,213644.855725,0.29206,0.46882 +6446,207991.0439241,0.28184,0.43127 +6447,206269.5159227,0.24815,0.39216 +6448,208327.9649539,0.19273,0.25798 +6449,215223.3076727,0.16494,0.1177 +6450,227606.3093558,0.071214,0.088558 +6451,240870.8441441,0,0.10767 +6452,239435.4682502,0,0.16116 +6453,229184.7613034,0,0.23145 +6454,222824.799947,0,0.28936 +6455,206089.5170164,0,0.35196 +6456,196840.3424458,0,0.39056 +6457,195580.3501016,0,0.40847 +6458,196332.6532228,0,0.43067 +6459,199521.8646142,0,0.44382 +6460,208900.2691688,0,0.45087 +6461,223798.6401837,0,0.4831 +6462,267570.681914,0,0.54621 +6463,295885.8944842,0.0041371,0.58394 +6464,305753.5268355,0.10159,0.57104 +6465,303995.0759815,0.182000,0.65897 +6466,307073.5188151,0.19143,0.8385 +6467,311711.9521701,0.19835,0.88966 +6468,309584.2727904,0.17488,0.8963 +6469,308610.4325536,0.13001,0.87473 +6470,305688.9118435,0.12795,0.82479 +6471,305375.0675966,0.11424,0.72795 +6472,302430.4701035,0.089416,0.57923 +6473,305559.6818594,0.11855,0.46694 +6474,309699.6567047,0.058985,0.40122 +6475,317984.2217517,0,0.40728 +6476,305430.4518754,0,0.40492 +6477,287162.8705628,0,0.37625 +6478,268673.7521348,0,0.26436 +6479,247563.1111738,0,0.16716 +6480,231732.4381313,0,0.14884 +6481,226687.8533979,0,0.18295 +6482,222538.6478395,0,0.25722 +6483,223120.1827676,0,0.34742 +6484,231067.8267849,0,0.41645 +6485,243196.9838565,0,0.45122 +6486,278315.232014,0,0.44188 +6487,304345.843081,0.0018356,0.41802 +6488,313701.1708527,0.1119,0.43724 +6489,315441.1602804,0.24395,0.54516 +6490,320568.8214321,0.33418,0.67303 +6491,324948.7948191,0.38029,0.71046 +6492,321768.8141408,0.38257,0.68015 +6493,318708.8327335,0.34757,0.60521 +6494,315611.9284736,0.34926,0.49442 +6495,310073.500587,0.31944,0.38352 +6496,305093.5308457,0.25693,0.27562 +6497,305818.1418275,0.1857,0.15411 +6498,307576.5926815,0.065195,0.10496 +6499,314675.0110894,0,0.090989 +6500,300524.3278392,0,0.13573 +6501,282016.7479849,0,0.22283 +6502,263670.7056106,0,0.34959 +6503,240635.460959,0,0.51944 +6504,224934.0179005,0,0.66827 +6505,221472.5004714,0,0.7785 +6506,218403.2883509,0,0.85097 +6507,221786.3447183,0,0.89782 +6508,227929.3843158,0,0.92135 +6509,239486.2371725,0,0.93346 +6510,276930.6250424,0,0.93673 +6511,299485.8726105,0.0014379,0.88759 +6512,304359.6891507,0.11559,0.70021 +6513,302707.3914978,0.27296,0.59551 +6514,303625.8474557,0.39896,0.79406 +6515,305882.7568195,0.40999,0.8809 +6516,300127.407174,0.35776,0.90488 +6517,296282.8151494,0.26063,0.91897 +6518,290347.4665976,0.26701,0.92066 +6519,286885.9491685,0.2471,0.91226 +6520,282482.8989987,0.19974,0.87827 +6521,285159.8058105,0.15949,0.81069 +6522,291621.3050115,0.057589,0.76546 +6523,305010.4544274,0,0.72082 +6524,296047.4319642,0,0.66287 +6525,277327.5457076,0,0.60086 +6526,259503.0386259,0,0.47881 +6527,237243.1738784,0,0.35652 +6528,223443.2577277,0,0.24500 +6529,219095.5918367,0,0.19535 +6530,216174.0711265,0,0.15739 +6531,218670.9790321,0,0.14858 +6532,225201.7085817,0,0.15477 +6533,238863.1640352,0,0.14946 +6534,273182.9555058,0,0.13687 +6535,294505.9028691,0.001015,0.1268 +6536,299965.869694,0.093526,0.094844 +6537,299730.4865088,0.18231,0.1364 +6538,300201.2528792,0.20182,0.26745 +6539,303288.9264259,0.20771,0.31407 +6540,299495.1033236,0.18005,0.30421 +6541,297542.8074936,0.12854,0.33427 +6542,296088.9701734,0.13161,0.37178 +6543,294879.7467515,0.12107,0.36025 +6544,291948.9953282,0.096378,0.43095 +6545,294256.6736142,0.11492,0.56559 +6546,298992.0294573,0.046174,0.66984 +6547,309228.8903343,0,0.72344 +6548,296421.2758466,0,0.74207 +6549,278038.3106197,0,0.75048 +6550,260444.5713666,0,0.75453 +6551,238853.9333221,0,0.74142 +6552,222958.6452876,0,0.72981 +6553,215855.6115231,0,0.71671 +6554,214604.849892,0,0.68416 +6555,217046.3735187,0,0.63605 +6556,223881.716602,0,0.55647 +6557,236237.0261457,0,0.45627 +6558,270492.2026242,0,0.35729 +6559,293804.3686702,0,0.27252 +6560,303367.3874877,0.085441,0.18535 +6561,304631.9951884,0.15252,0.11131 +6562,309095.0449937,0.13552,0.087669 +6563,312210.4106799,0.21227,0.1441 +6564,306690.4442196,0.2832,0.2562 +6565,297192.0403941,0.3365,0.28313 +6566,287047.4866485,0.31393,0.25849 +6567,281735.211234,0.26573,0.22083 +6568,276044.4765805,0.19599,0.1668 +6569,279792.1461171,0.15371,0.13845 +6570,284578.2708824,0.04991,0.16649 +6571,298447.4173818,0,0.19291 +6572,288141.3261561,0,0.20461 +6573,269656.8230847,0,0.21601 +6574,254213.8399942,0,0.23122 +6575,232812.4315692,0,0.22651 +6576,215227.9230292,0,0.20396 +6577,206767.9744325,0,0.17399 +6578,202480.308177,0,0.13977 +6579,202554.1538821,0,0.11256 +6580,206140.2859387,0,0.091624 +6581,205637.2120723,0,0.09604 +6582,212971.0136655,0,0.10425 +6583,224638.6350799,0,0.10369 +6584,240607.7688195,0.09933,0.074426 +6585,255049.2195338,0.23803,0.068279 +6586,262683.0193041,0.33609,0.070599 +6587,267261.4530237,0.35877,0.048797 +6588,263481.4759911,0.33017,0.0344 +6589,253775.3811199,0.32861,0.028325 +6590,245089.2800511,0.3376,0.024163 +6591,239629.3132262,0.313000,0.018674 +6592,238360.0901689,0.25209,0.016717 +6593,243303.1370577,0.19083,0.019728 +6594,251361.5496326,0.056845,0.034828 +6595,261879.9472606,0,0.053753 +6596,248740.0270997,0,0.070286 +6597,235184.7248472,0,0.078733 +6598,227144.7736985,0,0.069508 +6599,208346.4263802,0,0.055573 +6600,193748.0535424,0,0.041294 +6601,185218.8745971,0,0.034778 +6602,179597.3702922,0,0.034896 +6603,182620.428847,0,0.043141 +6604,187369.6307597,0,0.040322 +6605,189252.6962412,0,0.030914 +6606,190789.6099797,0,0.024366 +6607,193872.6681699,0,0.020329 +6608,201797.2354043,0.12702,0.015092 +6609,209587.9572981,0.33222,0.014324 +6610,216155.6097002,0.50791,0.017594 +6611,224860.1721953,0.54472,0.015954 +6612,223069.4138453,0.50595,0.015054 +6613,213977.1613982,0.41,0.014723 +6614,208194.1196133,0.3674,0.014946 +6615,204607.9875567,0.29738,0.016528 +6616,206089.5170164,0.20805,0.022645 +6617,214314.082428,0.16702,0.033508 +6618,229161.6845206,0.049017,0.063824 +6619,248920.026006,0,0.097136 +6620,244369.2844258,0,0.11515 +6621,232955.5076229,0,0.13264 +6622,224163.2533529,0,0.16124 +6623,206287.977349,0,0.19452 +6624,193812.6685345,0,0.20347 +6625,193720.361403,0,0.19862 +6626,192635.7526086,0,0.19529 +6627,195160.3526535,0,0.17812 +6628,199323.4042816,0,0.12488 +6629,214577.1577526,0,0.10739 +6630,257103.0532084,0,0.12202 +6631,283553.6617234,0,0.15237 +6632,295798.2027093,0.10441,0.17686 +6633,297007.4261312,0.24464,0.25897 +6634,300468.9435604,0.31171,0.44286 +6635,305287.3758217,0.36569,0.53231 +6636,301576.6291377,0.37779,0.57935 +6637,298802.7998378,0.35369,0.58201 +6638,295092.0531538,0.31723,0.59207 +6639,291884.3803361,0.25652,0.60165 +6640,284315.1955578,0.17881,0.59526 +6641,285067.4986791,0.15259,0.58143 +6642,292576.683822,0.042261,0.64713 +6643,309007.3532188,0,0.64388 +6644,296536.6597609,0,0.59867 +6645,277724.4663728,0,0.53956 +6646,257195.3603398,0,0.49774 +6647,235221.6476998,0,0.47088 +6648,220766.3509158,0,0.46446 +6649,216709.4524889,0,0.46582 +6650,212398.7094505,0,0.4707 +6651,213760.2396393,0,0.4246 +6652,217521.7552456,0,0.33363 +6653,228520.149957,0,0.31709 +6654,266536.8420419,0,0.31822 +6655,294912.0542475,0,0.33081 +6656,301853.550532,0.10759,0.3472 +6657,302453.5468864,0.29095,0.3599 +6658,308490.4332828,0.42973,0.39125 +6659,313401.1726755,0.52107,0.40343 +6660,309519.6577983,0.56157,0.38289 +6661,307904.2829981,0.55319,0.36344 +6662,302873.5443344,0.49051,0.33383 +6663,298373.5716766,0.39239,0.33045 +6664,292068.994599,0.26993,0.30237 +6665,292285.9163579,0.18184,0.31542 +6666,296241.2769403,0.04166,0.41056 +6667,310008.885595,0,0.41649 +6668,294958.2078132,0,0.36129 +6669,275324.4809553,0,0.28552 +6670,257223.0524793,0,0.25289 +6671,233440.120063,0,0.23088 +6672,220826.3505513,0,0.19214 +6673,216630.9914272,0,0.12856 +6674,209689.4951427,0,0.069616 +6675,212011.0194985,0,0.06014 +6676,216192.5325528,0,0.056543 +6677,229138.6077377,0,0.053378 +6678,268378.3693142,0,0.053463 +6679,296227.4308705,0,0.055683 +6680,302181.2408486,0.085514,0.054006 +6681,300635.096397,0.18962,0.060357 +6682,303648.9242386,0.20224,0.057896 +6683,308351.9725856,0.32404,0.064543 +6684,304105.8445392,0.43394,0.074819 +6685,302430.4701035,0.51345,0.081127 +6686,297335.1164479,0.46512,0.10997 +6687,293841.2915227,0.38124,0.14578 +6688,286899.7952382,0.26937,0.17871 +6689,288238.2486441,0.17802,0.29849 +6690,295424.358827,0.036199,0.48199 +6691,310050.4238041,0,0.55863 +6692,294473.5953731,0,0.52715 +6693,274498.3321288,0,0.44831 +6694,255123.0652389,0,0.37548 +6695,233430.8893498,0,0.31626 +6696,220383.2763203,0,0.26722 +6697,216741.7599849,0,0.19308 +6698,212597.1697831,0,0.15975 +6699,213238.7043466,0,0.15942 +6700,217563.2934548,0,0.14823 +6701,230287.8315242,0,0.15162 +6702,267626.0661929,0,0.16065 +6703,297279.732169,0,0.1772 +6704,304276.6127324,0.094206,0.18091 +6705,304581.2262661,0.27016,0.2036 +6706,308402.7415079,0.39322,0.28573 +6707,313770.4012013,0.50715,0.3658 +6708,309755.0409835,0.57922,0.43842 +6709,303939.6917026,0.60438,0.47861 +6710,298747.4155589,0.52208,0.46351 +6711,295622.8191596,0.40345,0.37783 +6712,289129.0124626,0.26436,0.27998 +6713,291805.9192744,0.17225,0.29526 +6714,297847.4210274,0.032954,0.36977 +6715,308753.5086074,0,0.39812 +6716,292521.2995431,0,0.38107 +6717,273224.4937149,0,0.33391 +6718,255256.9105795,0,0.29022 +6719,231801.6684799,0,0.23964 +6720,219834.0488883,0,0.18743 +6721,216469.4539472,0,0.14945 +6722,213626.3942987,0,0.13399 +6723,215435.614075,0,0.13506 +6724,220987.8880313,0,0.12466 +6725,234875.4959569,0,0.1123 +6726,271230.6596758,0,0.093195 +6727,299028.9523098,0,0.057214 +6728,306731.9824288,0.082238,0.035107 +6729,307936.5904941,0.21827,0.027094 +6730,312104.2574788,0.27865,0.030625 +6731,317061.1504372,0.3473,0.056428 +6732,312002.7196342,0.38164,0.082387 +6733,304235.0745232,0.38165,0.088764 +6734,293135.1419672,0.34863,0.072622 +6735,285625.9568243,0.2865,0.064994 +6736,278767.5369581,0.20121,0.056078 +6737,280198.2974955,0.14583,0.05214 +6738,286502.874573,0.026145,0.06952 +6739,302688.9300716,0,0.11191 +6740,288676.7075185,0,0.18243 +6741,271249.121102,0,0.27173 +6742,256890.7468061,0,0.33292 +6743,235355.4930404,0,0.3752 +6744,217429.4481142,0,0.39043 +6745,207963.3517847,0,0.38559 +6746,201520.31401,0,0.3867 +6747,203541.8401886,0,0.41027 +6748,205618.750646,0,0.41912 +6749,209504.8808798,0,0.39262 +6750,213963.3153285,0,0.30746 +6751,225570.9371074,0,0.16666 +6752,237820.0934499,0.065649,0.078835 +6753,248380.029287,0.14214,0.041336 +6754,254486.146032,0.1073,0.022854 +6755,256581.5179157,0.12736,0.013435 +6756,250881.5525491,0.13034,0.0098931 +6757,240464.6927658,0.11898,0.0095925 +6758,234815.4963215,0.12704,0.0096207 +6759,231524.7470855,0.11937,0.0080965 +6760,229189.37666,0.094001,0 +6761,234367.806734,0.10315,0 +6762,245236.9714614,0.020524,0 +6763,261921.4854697,0,0 +6764,249543.0991432,0,0.01446 +6765,233573.9654036,0,0.03302 +6766,223650.9487734,0,0.057574 +6767,205503.3667317,0,0.081196 +6768,190572.6882208,0,0.09731 +6769,183848.1136952,0,0.1118 +6770,183363.5012551,0,0.1224 +6771,187415.7843254,0,0.13445 +6772,188578.8541816,0,0.13781 +6773,189428.0797909,0,0.10500 +6774,188463.4702673,0,0.094316 +6775,189792.6929601,0,0.084275 +6776,198432.6404632,0.079688,0.068359 +6777,206920.2811994,0.26644,0.060376 +6778,215532.536563,0.40932,0.063859 +6779,224749.4036376,0.50756,0.069743 +6780,219792.5106791,0.55377,0.080836 +6781,208489.5024339,0.55014,0.10833 +6782,199235.7125067,0.47019,0.12559 +6783,195621.8883107,0.35702,0.11173 +6784,196701.8817486,0.22689,0.10141 +6785,208083.3510556,0.14532,0.16335 +6786,225520.1681851,0.021188,0.27851 +6787,245393.8935848,0,0.38845 +6788,236223.180076,0,0.46369 +6789,227324.7726049,0,0.50601 +6790,222441.7253515,0,0.53994 +6791,205549.5202974,0,0.56022 +6792,195229.5830021,0,0.56081 +6793,192834.2129412,0,0.55842 +6794,194869.5851895,0,0.55932 +6795,196932.6495772,0,0.56394 +6796,205203.3685545,0,0.54921 +6797,217143.2960067,0,0.52673 +6798,256009.2137008,0,0.50898 +6799,285570.5725454,0,0.48879 +6800,295396.6666876,0.076862,0.36194 +6801,296028.9705379,0.26907,0.26326 +6802,297565.8842765,0.42434,0.36451 +6803,300090.4843214,0.52328,0.44018 +6804,296222.815514,0.56674,0.43409 +6805,291067.4622229,0.55778,0.4223 +6806,285021.3451134,0.4812,0.39933 +6807,282469.052929,0.36931,0.31499 +6808,277516.775327,0.23724,0.30896 +6809,281319.8291425,0.14521,0.45422 +6810,290989.0011612,0.01741,0.61138 +6811,307668.8998129,0,0.67448 +6812,291865.9189099,0,0.67509 +6813,274244.4875174,0,0.64781 +6814,257043.053573,0,0.60824 +6815,235567.7994427,0,0.57783 +6816,220794.0430553,0,0.55678 +6817,216455.6078774,0,0.55534 +6818,214632.5420314,0,0.55106 +6819,217267.9106341,0,0.54617 +6820,224869.4029085,0,0.50777 +6821,236712.4078726,0,0.46077 +6822,265955.3071138,0,0.4357 +6823,293573.6008416,0,0.38394 +6824,300367.4057158,0.070777,0.26865 +6825,301890.4733846,0.25584,0.25782 +6826,304858.1476605,0.40528,0.2961 +6827,309131.9678463,0.5079,0.33045 +6828,304424.3041427,0.55782,0.33300 +6829,299693.5636562,0.55671,0.32793 +6830,293892.060445,0.46912,0.31717 +6831,289087.4742534,0.34868,0.2727 +6832,284915.1919122,0.21396,0.24927 +6833,287670.5597858,0.13181,0.35321 +6834,297353.5778742,0.014572,0.4741 +6835,311338.1082878,0,0.51165 +6836,294861.2853252,0,0.49887 +6837,278112.1563248,0,0.46518 +6838,261653.7947885,0,0.40964 +6839,238793.9336867,0,0.3596 +6840,227666.3089912,0,0.31156 +6841,221587.8843857,0,0.27758 +6842,218814.0550858,0,0.25142 +6843,222981.7220705,0,0.23447 +6844,229793.988371,0,0.21192 +6845,241692.377614,0,0.19718 +6846,272518.3441594,0,0.18998 +6847,294981.2845961,0,0.17995 +6848,302725.8529241,0.064268,0.10193 +6849,304133.5366786,0.2512,0.046347 +6850,307747.3608746,0.40373,0.027216 +6851,311642.7218215,0.49931,0.02946 +6852,306579.6756619,0.54144,0.019655 +6853,301036.6324187,0.53276,0.009793 +6854,294298.2118234,0.45396,0 +6855,288436.7089767,0.34191,0 +6856,283475.2006617,0.21299,0.012513 +6857,286285.9528141,0.12748,0.032377 +6858,296402.8144203,0.010938,0.058297 +6859,311721.1828833,0,0.073822 +6860,294755.132124,0,0.068231 +6861,278693.6912529,0,0.041682 +6862,264030.7034232,0,0.018653 +6863,241563.14763,0,0.0097017 +6864,227933.9996724,0,0.0061181 +6865,224320.1754764,0,0 +6866,221297.1169216,0,0 +6867,223295.5663174,0,0 +6868,230066.2944087,0,0 +6869,239260.0847005,0,0 +6870,269772.206999,0,0 +6871,295964.3555459,0,0 +6872,305102.7615588,0.053564,0 +6873,307442.7473409,0.197000,0 +6874,312067.3346262,0.28293,0 +6875,316631.922276,0.34796,0 +6876,313119.6359246,0.37139,0 +6877,308111.9740438,0.35688,0 +6878,301779.7048268,0.32036,0 +6879,297441.269649,0.25565,0.0073387 +6880,291219.7689898,0.17,0.014895 +6881,294344.3653891,0.049461,0.040414 +6882,304595.0723359,0,0.088589 +6883,312256.5642456,0,0.11987 +6884,295359.743835,0,0.12376 +6885,279044.4583524,0,0.11739 +6886,265203.0039925,0,0.09297 +6887,243044.6770896,0,0.067216 +6888,228612.4570885,0,0.047195 +6889,221601.7304554,0,0.037719 +6890,220304.8152586,0,0.036383 +6891,222607.8781881,0,0.040654 +6892,229277.0684349,0,0.043513 +6893,240090.8488834,0,0.04491 +6894,268793.7514057,0,0.052181 +6895,296679.7358146,0,0.05645 +6896,306575.0603053,0.052628,0.049957 +6897,310221.1919973,0.23309,0.047867 +6898,315510.390629,0.38163,0.067984 +6899,319687.2883268,0.48658,0.12035 +6900,316530.3844314,0.54063,0.14833 +6901,309616.5802864,0.5441,0.17179 +6902,300768.9417375,0.46224,0.20168 +6903,295941.2787631,0.34573,0.24188 +6904,290698.2336971,0.21174,0.25334 +6905,291713.612143,0.061068,0.36785 +6906,300247.4064449,0,0.51788 +6907,303607.3860294,0,0.57056 +6908,281190.5991585,0,0.59214 +6909,265383.0028988,0,0.59805 +6910,256041.5211968,0,0.60618 +6911,236841.6378566,0,0.5942 +6912,219506.3585716,0,0.56225 +6913,208651.0399139,0,0.53627 +6914,203846.4537223,0,0.51525 +6915,204760.2943236,0,0.49728 +6916,208872.5770294,0,0.45587 +6917,208475.6563642,0,0.43565 +6918,210880.2571383,0,0.4458 +6919,225432.4764103,0,0.47777 +6920,242998.5235239,0.050307,0.4313 +6921,258939.9651241,0.23329,0.40066 +6922,269998.359471,0.38952,0.5434 +6923,275624.4791325,0.48822,0.61903 +6924,270866.0465066,0.5305,0.64674 +6925,261598.4105097,0.51987,0.67054 +6926,254246.1474902,0.43186,0.68833 +6927,251084.6282383,0.31304,0.71551 +6928,248098.4925361,0.18309,0.75589 +6929,253364.614385,0.0507,0.84923 +6930,265304.5418371,0,0.89151 +6931,267344.529442,0,0.89029 +6932,249667.7137707,0,0.87042 +6933,235793.9519148,0,0.83708 +6934,230892.4432351,0,0.79674 +6935,218454.0572732,0,0.75362 +6936,203144.9195233,0,0.71602 +6937,194292.665618,0,0.68548 +6938,189492.6947829,0,0.65915 +6939,191731.1427204,0,0.64127 +6940,194112.6667116,0,0.63516 +6941,193208.0568235,0,0.65479 +6942,190138.844703,0,0.67411 +6943,192686.5215308,0,0.68409 +6944,202420.3085415,0.047389,0.61787 +6945,217900.2144845,0.2288,0.57229 +6946,232000.1288125,0.38539,0.71051 +6947,245153.8950431,0.48686,0.76669 +6948,243510.8281034,0.53218,0.76886 +6949,233361.6590012,0.52407,0.73408 +6950,226503.239135,0.43446,0.71168 +6951,220784.8123421,0.31347,0.70729 +6952,217327.9102696,0.18151,0.67781 +6953,223249.4127517,0.048817,0.73733 +6954,240695.4605944,0,0.82764 +6955,251223.0889355,0,0.84998 +6956,240538.5384709,0,0.84442 +6957,234977.0338015,0,0.83715 +6958,235226.2630564,0,0.82352 +6959,220337.1227546,0,0.80675 +6960,208757.1931151,0,0.76942 +6961,201866.4657529,0,0.7222 +6962,203274.1495074,0,0.67556 +6963,205807.9802655,0,0.61774 +6964,212380.2480242,0,0.54099 +6965,224874.018265,0,0.47954 +6966,258496.8908932,0,0.49238 +6967,289147.4738889,0,0.53453 +6968,300815.0953033,0.040724,0.52418 +6969,304802.7633816,0.21492,0.56416 +6970,309413.5045972,0.36065,0.75439 +6971,313848.862263,0.46435,0.87218 +6972,310701.1890808,0.51557,0.91200 +6973,307119.6723808,0.51512,0.91859 +6974,300404.3285683,0.42095,0.91829 +6975,296610.505466,0.29731,0.90512 +6976,288565.9389608,0.16636,0.86698 +6977,289424.3952832,0.043053,0.88991 +6978,302218.1637012,0,0.90203 +6979,310996.5719014,0,0.89168 +6980,293822.8300965,0,0.86823 +6981,278435.2312849,0,0.84183 +6982,265830.6924863,0,0.82357 +6983,244710.8208122,0,0.79637 +6984,231764.7456273,0,0.76208 +6985,228875.5324131,0,0.74045 +6986,224763.2497073,0,0.73054 +6987,226844.7755214,0,0.72091 +6988,233597.0421864,0,0.71974 +6989,244563.1294019,0,0.71714 +6990,272056.8085022,0,0.70856 +6991,300335.0982198,0,0.6883 +6992,310982.7258317,0.032261,0.61242 +6993,312722.7152594,0.14905,0.52648 +6994,318233.4510066,0.19638,0.48129 +6995,321625.7380871,0.20431,0.48558 +6996,318953.4466318,0.1677,0.46797 +6997,316682.6911983,0.10352,0.40404 +6998,311296.5700786,0.083725,0.34841 +6999,307068.9034585,0.057937,0.32179 +7000,299582.7950985,0.031061,0.34517 +7001,298650.4930709,0.0056685,0.38914 +7002,307562.7466118,0,0.44319 +7003,312635.0234846,0,0.51667 +7004,294247.4429011,0,0.53375 +7005,279445.9943742,0,0.5218 +7006,265886.0767652,0,0.54226 +7007,247096.96016,0,0.60313 +7008,231967.8213164,0,0.67462 +7009,228044.7682301,0,0.72577 +7010,226692.4687545,0,0.72629 +7011,224975.5561096,0,0.67278 +7012,229669.3737435,0,0.69555 +7013,240372.3856343,0,0.75888 +7014,268249.1393301,0,0.80581 +7015,298082.8042126,0,0.86903 +7016,308439.6643605,0.031253,0.89147 +7017,311079.6483197,0.16999,0.88945 +7018,316585.7687103,0.26683,0.89482 +7019,321178.0484996,0.32424,0.92029 +7020,318542.6798969,0.33414,0.9412 +7021,316438.0773,0.30389,0.93182 +7022,311688.8753873,0.26081,0.90884 +7023,304322.7662981,0.19496,0.86756 +7024,297279.732169,0.11662,0.79832 +7025,299435.1036882,0.027164,0.68058 +7026,313770.4012013,0,0.54636 +7027,320633.4364241,0,0.40324 +7028,308836.5850257,0,0.28038 +7029,297404.3467964,0,0.18314 +7030,287255.1776943,0,0.12555 +7031,267815.2958124,0,0.10221 +7032,255699.9848105,0,0.083891 +7033,249658.4830575,0,0.074721 +7034,245606.1999872,0,0.074909 +7035,244664.6672464,0,0.087219 +7036,243349.2906234,0,0.13177 +7037,246298.503473,0,0.18975 +7038,275361.4038078,0,0.2166 +7039,303999.6913381,0,0.23684 +7040,310719.6505071,0.021284,0.22376 +7041,311481.1843415,0.10387,0.18925 +7042,316811.9211824,0.10203,0.21613 +7043,322156.5040929,0.12882,0.33789 +7044,320042.6707829,0.13677,0.52625 +7045,315178.0849558,0.1277,0.65426 +7046,306127.3707178,0.10259,0.7274 +7047,301055.093845,0.069835,0.78448 +7048,294995.1306658,0.036243,0.86506 +7049,297422.8082227,0.0062841,0.92658 +7050,312376.5635165,0,0.95636 +7051,327261.0884617,0,0.96632 +7052,313835.0161933,0,0.9682 +7053,301862.7812451,0,0.96474 +7054,289429.0106398,0,0.94825 +7055,270482.9719111,0,0.91485 +7056,258178.4312897,0,0.8854 +7057,254153.8403588,0,0.88008 +7058,250286.1715513,0,0.92499 +7059,250092.3265753,0,0.95913 +7060,251800.008507,0,0.97648 +7061,260961.4913027,0,0.98323 +7062,285330.5740037,0,0.98503 +7063,309898.1170373,0,0.98357 +7064,316931.9204532,0.021937,0.97809 +7065,317134.9961424,0.13598,0.97009 +7066,319779.5954582,0.20003,0.95004 +7067,322313.4262164,0.22992,0.93400 +7068,319133.4455381,0.21643,0.92406 +7069,312408.8710125,0.17148,0.90994 +7070,305379.6829531,0.13729,0.84064 +7071,299181.2590767,0.093302,0.71319 +7072,292701.2984494,0.048161,0.57892 +7073,293472.062997,0.0085037,0.45708 +7074,304461.2269953,0,0.3693 +7075,306990.4423968,0,0.27628 +7076,289618.2402592,0,0.19609 +7077,274858.3299415,0,0.12715 +7078,266042.9988887,0,0.084561 +7079,247696.9565143,0,0.063294 +7080,231026.2885757,0,0.049407 +7081,223244.7973951,0,0.043722 +7082,217415.6020444,0,0.041414 +7083,214655.6188143,0,0.04348 +7084,214558.6963263,0,0.052251 +7085,215435.614075,0,0.082537 +7086,216884.8360387,0,0.13013 +7087,228866.3017,0,0.16024 +7088,245223.1253917,0.016198,0.13846 +7089,256341.519374,0.10334,0.11416 +7090,263924.550222,0.11549,0.099847 +7091,268281.4468262,0.15908,0.077521 +7092,264003.0112838,0.18414,0.060355 +7093,255090.7577429,0.18872,0.047923 +7094,248587.7203328,0.15104,0.035468 +7095,245583.1232043,0.10229,0.028099 +7096,244960.0500671,0.052369,0.02135 +7097,253641.5357793,0.0090764,0.015636 +7098,271572.1960621,0,0.009547 +7099,272006.0395799,0,0 +7100,254476.9153188,0,0 +7101,245647.7381963,0,0 +7102,239818.5428457,0,0.0068729 +7103,224130.9458569,0,0.018643 +7104,209878.7247621,0,0.037662 +7105,202480.308177,0,0.054826 +7106,203527.9941188,0,0.06437 +7107,202780.3063541,0,0.073152 +7108,205807.9802655,0,0.073121 +7109,206241.8237833,0,0.060952 +7110,206149.5166518,0,0.051714 +7111,213017.1672312,0,0.04588 +7112,223346.3352397,0.013749,0.049556 +7113,235729.3369228,0.080727,0.068476 +7114,250853.8604097,0.054839,0.093262 +7115,263283.0156585,0.06994,0.15739 +7116,264616.8537079,0.073423,0.33927 +7117,255201.5263007,0.066859,0.57692 +7118,245172.3564694,0.053736,0.74446 +7119,239615.4671565,0.036191,0.83005 +7120,237349.3270796,0.017839,0.88662 +7121,240053.9260309,0.0012759,0.94484 +7122,255949.2140654,0,0.96551 +7123,262004.561888,0,0.96892 +7124,250415.4015353,0,0.97119 +7125,242956.9853147,0,0.97435 +7126,241373.9180105,0,0.97635 +7127,225160.1703725,0,0.97786 +7128,215818.6886705,0,0.98032 +7129,209800.2637004,0,0.98282 +7130,211309.4852995,0,0.98313 +7131,215744.8429653,0,0.97726 +7132,219455.5896493,0,0.97796 +7133,229475.5287675,0,0.98196 +7134,267492.2208523,0,0.98587 +7135,299675.1022299,0,0.99084 +7136,308582.7404142,0.011964,0.99574 +7137,310461.1905391,0.080457,0.99746 +7138,312085.7960525,0.068294,0.99782 +7139,314951.9324838,0.088798,0.99795 +7140,311845.7975107,0.095057,0.99769 +7141,311135.0325986,0.088457,0.99681 +7142,307036.5959625,0.093263,0.99606 +7143,302453.5468864,0.083188,0.99565 +7144,294182.8279091,0.057018,0.99532 +7145,295281.2827733,0.0088505,0.99459 +7146,310571.9590968,0,0.99481 +7147,317633.4546522,0,0.99422 +7148,301899.7040977,0,0.99383 +7149,287762.8669172,0,0.99364 +7150,275130.6359792,0,0.99444 +7151,255478.447695,0,0.99531 +7152,242278.5278986,0,0.99526 +7153,238323.1673163,0,0.99451 +7154,234340.1145945,0,0.99369 +7155,236029.3350999,0,0.99241 +7156,239375.4686148,0,0.98865 +7157,248652.3353248,0,0.98148 +7158,280618.2949435,0,0.96811 +7159,311795.0285884,0,0.94915 +7160,315501.1599159,0.012184,0.91408 +7161,309353.5049618,0.13838,0.85164 +7162,308135.0508267,0.24381,0.79916 +7163,311822.7207279,0.30261,0.81527 +7164,308702.7396851,0.31155,0.82429 +7165,306796.5974208,0.279000,0.75646 +7166,301521.2448588,0.2179,0.64668 +7167,297852.0363839,0.14216,0.44081 +7168,293005.9119832,0.068135,0.24227 +7169,294575.1332177,0.010206,0.16728 +7170,313738.0937053,0,0.1491 +7171,316470.384796,0,0.16312 +7172,300427.4053512,0,0.19415 +7173,285685.9564597,0,0.21046 +7174,272352.1913228,0,0.22398 +7175,251620.0096007,0,0.22893 +7176,237349.3270796,0,0.22702 +7177,232000.1288125,0,0.22676 +7178,229447.8366281,0,0.23153 +7179,231843.206689,0,0.2039 +7180,236781.6382212,0,0.14012 +7181,246940.0380365,0,0.11688 +7182,280313.6814098,0,0.11419 +7183,307927.359781,0,0.12375 +7184,315099.6238941,0.011461,0.14265 +7185,313198.0969864,0.15145,0.16399 +7186,315764.2352405,0.29585,0.1848 +7187,318358.065634,0.38806,0.25728 +7188,314670.3957329,0.42602,0.32003 +7189,311873.4896501,0.41423,0.41281 +7190,307147.3645203,0.32684,0.45757 +7191,303044.3125276,0.21631,0.48839 +7192,297233.5786033,0.10557,0.60998 +7193,301027.4017056,0.016667,0.74115 +7194,316982.6893755,0,0.80358 +7195,317702.6850008,0,0.83099 +7196,299744.3325785,0,0.84062 +7197,284273.6573487,0,0.83959 +7198,270058.3591064,0,0.83286 +7199,249806.1744678,0,0.82102 +7200,235863.1822633,0,0.80059 +7201,230121.6786876,0,0.77611 +7202,226143.2413224,0,0.75761 +7203,226461.7009259,0,0.74613 +7204,231007.8271494,0,0.71976 +7205,240704.6913075,0,0.69943 +7206,275398.3266604,0,0.68771 +7207,306141.2167875,0,0.71259 +7208,314393.4743385,0.010401,0.73416 +7209,311836.5667976,0.14999,0.70836 +7210,314587.3193146,0.30023,0.71218 +7211,318182.6820843,0.3919,0.78333 +7212,313899.6311853,0.42629,0.77757 +7213,310031.9623779,0.40868,0.76386 +7214,304193.5363141,0.30822,0.67707 +7215,300916.6331479,0.18991,0.5777 +7216,295493.5891756,0.081605,0.48498 +7217,298627.4162881,0.011649,0.47116 +7218,314619.6268106,0,0.47277 +7219,315556.5441947,0,0.4511 +7220,300468.9435604,0,0.40223 +7221,284947.4994082,0,0.33944 +7222,271050.6607694,0,0.28492 +7223,248827.7188745,0,0.25022 +7224,235978.5661777,0,0.24762 +7225,230273.9854545,0,0.29959 +7226,227472.4640152,0,0.39053 +7227,228455.534965,0,0.47957 +7228,235018.5720106,0,0.55662 +7229,244613.8983241,0,0.61189 +7230,275518.3259313,0,0.64128 +7231,306953.5195442,0,0.63951 +7232,312090.411409,0.0059154,0.57045 +7233,306611.9831579,0.11404,0.42699 +7234,305979.6793075,0.20594,0.32844 +7235,306256.6007018,0.29168,0.32122 +7236,300653.5578232,0.34211,0.26717 +7237,294782.8242635,0.35329,0.2066 +7238,288870.5524945,0.27259,0.15471 +7239,283055.2032136,0.1738,0.11371 +7240,280092.1442943,0.079063,0.14903 +7241,285736.725382,0.01015,0.26283 +7242,306939.6734745,0,0.37948 +7243,308928.8921571,0,0.45848 +7244,289996.6994981,0,0.51634 +7245,273386.031195,0,0.56583 +7246,263490.7067043,0,0.60558 +7247,244189.2855195,0,0.59621 +7248,227647.8475649,0,0.56047 +7249,222755.5695984,0,0.53018 +7250,219146.360759,0,0.50524 +7251,217701.7541519,0,0.48082 +7252,218744.8247372,0,0.47759 +7253,217023.2967358,0,0.48927 +7254,217872.5223451,0,0.50928 +7255,232415.5109039,0,0.54108 +7256,245278.5096705,0.0059004,0.53828 +7257,255003.0659681,0.13796,0.46221 +7258,261492.2573085,0.29188,0.41674 +7259,263550.7063397,0.39148,0.46655 +7260,259719.9603848,0.43494,0.53796 +7261,252003.0841962,0.4255,0.57899 +7262,245453.8932203,0.33403,0.58495 +7263,242084.6829226,0.21808,0.59628 +7264,241452.3790722,0.10247,0.67771 +7265,249824.6358941,0.013377,0.76477 +7266,267986.0640055,0,0.81096 +7267,266832.2248625,0,0.82265 +7268,249616.9448484,0,0.81247 +7269,236546.255036,0,0.79174 +7270,232840.1237086,0,0.7562 +7271,215463.3062144,0,0.69494 +7272,201543.3907928,0,0.61145 +7273,193508.0550007,0,0.52489 +7274,189363.4647989,0,0.42537 +7275,187761.9360684,0,0.31047 +7276,192451.1383457,0,0.25913 +7277,192681.9061743,0,0.26283 +7278,190900.3785374,0,0.30566 +7279,190268.074687,0,0.37298 +7280,200218.7834566,0.0021919,0.4316 +7281,212675.6308448,0.098098,0.44427 +7282,222127.8811046,0.17641,0.52716 +7283,229973.9872773,0.24976,0.67849 +7284,239232.392561,0.29027,0.80463 +7285,234123.1928356,0.29592,0.84677 +7286,223235.5666819,0.22124,0.86428 +7287,217443.2941839,0.13366,0.87289 +7288,217180.2188593,0.054507,0.85137 +7289,220803.2737684,0.00506,0.85187 +7290,244267.7465812,0,0.8629 +7291,249501.5609341,0,0.85301 +7292,241724.68511,0,0.81962 +7293,229064.7620326,0,0.76546 +7294,222889.414939,0,0.69591 +7295,220092.5088563,0,0.58252 +7296,202503.3849598,0,0.46016 +7297,192958.8275686,0,0.40488 +7298,189391.1569383,0,0.3564 +7299,191684.9891547,0,0.22273 +7300,194814.2009106,0,0.13838 +7301,200426.4745023,0,0.22368 +7302,201404.9300956,0,0.39867 +7303,213178.7047112,0,0.49438 +7304,226281.7020196,0.0017129,0.51451 +7305,235263.185909,0.094172,0.57167 +7306,240497.0002618,0.17384,0.71348 +7307,243349.2906234,0.25796,0.83602 +7308,249953.8658781,0.31249,0.88783 +7309,247198.4980046,0.33084,0.91102 +7310,243016.9849502,0.26416,0.9044 +7311,239749.3124971,0.17565,0.86364 +7312,240104.6949532,0.083927,0.84178 +7313,247595.4186698,0.0089912,0.83751 +7314,271581.4267752,0,0.8073 +7315,278361.3855797,0,0.74691 +7316,272306.0377571,0,0.6465 +7317,258358.430196,0,0.52891 +7318,246760.0391302,0,0.3639 +7319,239426.237537,0,0.26483 +7320,221177.1176508,0,0.2441 +7321,208157.1967607,0,0.19428 +7322,204077.2215509,0,0.18234 +7323,202318.7706969,0,0.21436 +7324,205974.1331021,0,0.29814 +7325,213321.7807649,0,0.46042 +7326,226267.8559498,0,0.63819 +7327,266901.4552111,0,0.74298 +7328,296952.0418524,0.00078747,0.80698 +7329,307355.055566,0.062503,0.84595 +7330,310576.5744534,0.072059,0.8642 +7331,316013.4644954,0.10521,0.87723 +7332,322456.5022701,0.12377,0.87072 +7333,319331.9058707,0.12554,0.80964 +7334,316345.7701686,0.09635,0.69219 +7335,312191.9492536,0.060192,0.57984 +7336,311679.6446741,0.025421,0.57791 +7337,311693.4907438,0.00054698,0.62319 +7338,327662.6244835,0,0.65996 +7339,328054.9297921,0,0.68442 +7340,319004.2155541,0,0.71114 +7341,300745.8649547,0,0.76531 +7342,284213.6577132,0,0.81435 +7343,268715.2903439,0,0.86183 +7344,245195.4332522,0,0.89826 +7345,225330.9385657,0,0.91509 +7346,223678.6409129,0,0.90455 +7347,222700.1853196,0,0.90097 +7348,226706.3148242,0,0.88886 +7349,234150.8849751,0,0.86351 +7350,243432.3670417,0,0.84318 +7351,279810.6075434,0,0.85027 +7352,308107.3586873,0,0.87595 +7353,317753.4539231,0.11112,0.90747 +7354,317047.3043675,0.25262,0.91952 +7355,320647.2824938,0.32199,0.94278 +7356,327021.08992,0.32843,0.9574 +7357,322331.8876426,0.28497,0.96188 +7358,317305.7643356,0.23898,0.96653 +7359,305407.3750926,0.16862,0.97296 +7360,306385.8306859,0.086057,0.97864 +7361,307110.4416677,0.0076554,0.97991 +7362,322811.8847262,0,0.98067 +7363,322364.1951387,0,0.98369 +7364,312981.1752275,0,0.98478 +7365,296305.8919323,0,0.98435 +7366,280364.4503321,0,0.98095 +7367,266555.3034682,0,0.97725 +7368,239407.7761108,0,0.97174 +7369,221574.038316,0,0.95816 +7370,217295.6027736,0,0.92738 +7371,215910.9958019,0,0.88509 +7372,220572.5059398,0,0.83262 +7373,228289.3821284,0,0.76374 +7374,239121.6240033,0,0.75384 +7375,273104.4944441,0,0.77359 +7376,300459.7128472,0,0.73262 +7377,309242.736404,0.078462,0.60785 +7378,317901.1453334,0.14912,0.42151 +7379,321044.203159,0.20072,0.41807 +7380,325018.0251676,0.21667,0.45208 +7381,324311.8756121,0.20108,0.4659 +7382,323587.2646303,0.14998,0.41178 +7383,320651.8978504,0.089423,0.33788 +7384,317351.9179013,0.034698,0.28557 +7385,320231.9004023,0.0013249,0.31304 +7386,333524.1273301,0,0.34415 +7387,336164.1112894,0,0.36285 +7388,330934.9122931,0,0.40335 +7389,314047.3225956,0,0.4582 +7390,299116.6440847,0,0.52978 +7391,285049.0372528,0,0.59116 +7392,261312.2584022,0,0.63919 +7393,250276.9408382,0,0.65297 +7394,243395.4441891,0,0.64033 +7395,239135.470073,0,0.62861 +7396,239749.3124971,0,0.61061 +7397,246787.7312696,0,0.61487 +7398,261321.4891153,0,0.63195 +7399,295927.4326934,0,0.66484 +7400,318404.2191997,0,0.70249 +7401,322664.1933158,0.096261,0.70399 +7402,318953.4466318,0.22915,0.75965 +7403,321104.2027945,0.28799,0.79807 +7404,324505.7205881,0.28397,0.82271 +7405,318611.9102455,0.23269,0.80773 +7406,313170.4048469,0.18499,0.74715 +7407,307253.5177214,0.12087,0.66533 +7408,305328.9140308,0.054448,0.59333 +7409,306796.5974208,0.0028801,0.53878 +7410,321584.199878,0,0.51007 +7411,320047.2861394,0,0.49154 +7412,308836.5850257,0,0.48532 +7413,288293.632923,0,0.48195 +7414,271050.6607694,0,0.50161 +7415,259258.4247276,0,0.54918 +7416,240880.0748573,0,0.59634 +7417,223023.2602796,0,0.61899 +7418,214046.3917468,0,0.61432 +7419,211618.7141898,0,0.63729 +7420,214263.3135057,0,0.6584 +7421,215837.1500968,0,0.67296 +7422,217424.8327576,0,0.69547 +7423,219294.0521693,0,0.72375 +7424,230458.5997174,0,0.74846 +7425,242693.9099901,0.10601,0.76014 +7426,256133.8283282,0.28151,0.78016 +7427,265586.078588,0.39306,0.84905 +7428,271858.3481696,0.44109,0.88358 +7429,264723.006909,0.42961,0.90412 +7430,256881.5160929,0.29187,0.90394 +7431,250660.0154337,0.14571,0.88558 +7432,249676.9444838,0.035973,0.89528 +7433,256299.9811648,0.00085018,0.90352 +7434,275246.0198935,0,0.91231 +7435,277318.3149944,0,0.9156 +7436,267912.2183004,0,0.9123 +7437,248541.5667671,0,0.90424 +7438,236273.9489983,0,0.90022 +7439,232027.8209519,0,0.89586 +7440,217572.5241679,0,0.88148 +7441,202032.6185895,0,0.85994 +7442,195594.1961713,0,0.83837 +7443,192668.0601046,0,0.81205 +7444,192395.7540668,0,0.75098 +7445,195003.4305301,0,0.6787 +7446,192663.444748,0,0.66415 +7447,189252.6962412,0,0.69 +7448,191348.0681249,0,0.70771 +7449,201714.158986,0.047518,0.67434 +7450,215620.2283379,0.068775,0.6056 +7451,226678.6226848,0.088927,0.52551 +7452,239592.3903736,0.087151,0.47102 +7453,238475.4740832,0.069146,0.4131 +7454,230989.3657232,0.081475,0.32036 +7455,226300.1634458,0.075373,0.24782 +7456,224781.7111336,0.046893,0.24186 +7457,233989.3474951,0.0016808,0.25373 +7458,256553.8257763,0,0.25323 +7459,261884.5626171,0,0.23292 +7460,256392.2882963,0,0.23574 +7461,246150.8120627,0,0.31523 +7462,238143.16841,0,0.40814 +7463,238166.2451929,0,0.46045 +7464,222137.1118178,0,0.49705 +7465,209634.1108638,0,0.49994 +7466,208041.8128464,0,0.46215 +7467,206352.592341,0,0.45516 +7468,210695.6428754,0,0.44835 +7469,215195.6155332,0,0.44966 +7470,227781.6929055,0,0.45291 +7471,267626.0661929,0,0.47701 +7472,297473.577145,0,0.54578 +7473,308933.5075137,0.07518,0.58043 +7474,309118.1217766,0.19936,0.53278 +7475,312122.718905,0.3034,0.52521 +7476,316295.0012463,0.36535,0.59383 +7477,313844.2469065,0.37996,0.67857 +7478,314028.8611693,0.30506,0.6882 +7479,310364.2680511,0.20136,0.69039 +7480,309561.1960075,0.090443,0.75663 +7481,310899.6494134,0.004662,0.82902 +7482,327519.5484297,0,0.85669 +7483,326264.1714421,0,0.85904 +7484,317716.5310705,0,0.85286 +7485,300035.1000426,0,0.84206 +7486,283729.0452732,0,0.83645 +7487,270210.6658733,0,0.8381 +7488,247281.5744229,0,0.82328 +7489,230924.7507311,0,0.79285 +7490,225944.7809898,0,0.77045 +7491,224726.3268547,0,0.7703 +7492,226000.1652687,0,0.76205 +7493,232493.9719657,0,0.74411 +7494,240838.5366481,0,0.73204 +7495,277198.3157236,0,0.72323 +7496,308268.8961673,0,0.69717 +7497,317051.9197241,0.085946,0.60144 +7498,313770.4012013,0.25864,0.47133 +7499,314171.9372231,0.32164,0.41546 +7500,316188.8480451,0.30265,0.44761 +7501,310082.7313002,0.22649,0.45844 +7502,308356.5879422,0.15705,0.41091 +7503,307715.0533786,0.08196,0.27205 +7504,310041.193091,0.022862,0.16253 +7505,315501.1599159,0,0.12633 +7506,332744.1320694,0,0.11453 +7507,331964.1368087,0,0.11174 +7508,322230.3497981,0,0.10543 +7509,303279.6957128,0,0.10397 +7510,286433.6442244,0,0.09731 +7511,272107.5774245,0,0.084905 +7512,247720.0332972,0,0.082698 +7513,235009.3412975,0,0.08935 +7514,229166.2998772,0,0.080036 +7515,226087.8570435,0,0.079021 +7516,227204.773334,0,0.11452 +7517,233084.7376069,0,0.20415 +7518,241733.9158231,0,0.32659 +7519,275869.0930308,0,0.43142 +7520,304031.9988341,0,0.54986 +7521,310761.1887163,0.068452,0.61418 +7522,310488.8826785,0.20721,0.66895 +7523,315182.7003124,0.26433,0.76446 +7524,318805.7552215,0.25432,0.85543 +7525,315722.6970313,0.19609,0.88858 +7526,316082.694844,0.14916,0.91539 +7527,314799.6257169,0.090408,0.93041 +7528,316101.1562702,0.034542,0.94253 +7529,320425.7453783,0,0.94516 +7530,334027.2011965,0,0.94477 +7531,329919.5338473,0,0.93506 +7532,319959.5943646,0,0.90594 +7533,302278.1633366,0,0.85795 +7534,286544.4127822,0,0.78627 +7535,275172.1741884,0,0.70061 +7536,255672.292671,0,0.63266 +7537,240326.2320686,0,0.61976 +7538,235900.1051159,0,0.67077 +7539,229530.9130464,0,0.74861 +7540,229960.1412076,0,0.78586 +7541,236066.2579525,0,0.81752 +7542,245532.354282,0,0.85762 +7543,276150.6297817,0,0.88718 +7544,302319.7015458,0,0.90536 +7545,306662.7520802,0.035596,0.91887 +7546,301608.9366337,0.067991,0.92449 +7547,300321.25215,0.099857,0.92273 +7548,303801.2310054,0.11145,0.9218 +7549,304811.9940948,0.10433,0.91891 +7550,305065.8387062,0.079103,0.91075 +7551,306155.0628572,0.047256,0.90653 +7552,312150.4110445,0.016955,0.92785 +7553,321662.6609397,0,0.95177 +7554,336897.9529844,0,0.96545 +7555,337645.6407491,0,0.97172 +7556,331714.9075538,0,0.9731 +7557,315390.3913581,0,0.97126 +7558,300395.0978552,0,0.96431 +7559,286932.1027342,0,0.95239 +7560,265959.9224704,0,0.94419 +7561,249727.7134061,0,0.93751 +7562,241258.5340962,0,0.92419 +7563,239717.0050011,0,0.91031 +7564,238189.3219757,0,0.90455 +7565,241410.8408631,0,0.8892 +7566,251260.0117881,0,0.85435 +7567,282201.3622478,0,0.80672 +7568,310756.5733597,0,0.72729 +7569,320093.4397052,0.032451,0.6122 +7570,323522.6496383,0.060526,0.46646 +7571,326388.7860696,0.10497,0.35862 +7572,331424.1400898,0.13688,0.3506 +7573,326979.5517108,0.15061,0.35462 +7574,321671.8916528,0.11417,0.33638 +7575,318741.1402295,0.068299,0.29272 +7576,318607.2948889,0.024872,0.22671 +7577,320218.0543326,0,0.2042 +7578,333482.589121,0,0.17935 +7579,330921.0662234,0,0.14613 +7580,321127.2795773,0,0.11206 +7581,300168.9453832,0,0.088456 +7582,280632.1410132,0,0.072267 +7583,267256.8376671,0,0.054372 +7584,244360.0537127,0,0.036851 +7585,227366.310814,0,0.024068 +7586,216958.6817438,0,0.016299 +7587,212163.3262653,0,0.010759 +7588,211720.2520344,0,0.007482 +7589,215910.9958019,0,0.0059216 +7590,215717.1508259,0,0 +7591,219326.3596653,0,0 +7592,231820.1299061,0,0 +7593,249778.4823284,0.047003,0 +7594,259004.5801161,0.15367,0 +7595,264164.5487638,0.25522,0.0067406 +7596,270755.2779488,0.32292,0.012671 +7597,264870.6983193,0.34819,0.019112 +7598,256812.2857444,0.27297,0.020785 +7599,249543.0991432,0.17204,0.016381 +7600,248800.0267351,0.068978,0.015863 +7601,256309.211878,0.00046681,0.019425 +7602,276842.9332675,0,0.026715 +7603,279132.1501273,0,0.038972 +7604,267021.454482,0,0.056783 +7605,247272.3437097,0,0.076137 +7606,237483.1724202,0,0.097573 +7607,235078.5716461,0,0.1039 +7608,214757.1566589,0,0.077531 +7609,199955.708132,0,0.058379 +7610,189460.3872869,0,0.04657 +7611,188569.6234685,0,0.031648 +7612,192372.6772839,0,0.015906 +7613,196318.8071531,0,0.007778 +7614,197375.7238082,0,0 +7615,193826.5146042,0,0 +7616,198312.6411923,0,0.0083135 +7617,205004.9082219,0.050627,0.014611 +7618,211161.7938892,0.18552,0.021429 +7619,216870.9899689,0.2711,0.037515 +7620,224292.483337,0.30042,0.091248 +7621,224832.4800559,0.28046,0.17521 +7622,221329.4244176,0.21201,0.24208 +7623,217138.6806501,0.12601,0.22843 +7624,217890.9837714,0.045278,0.28186 +7625,231460.1320935,0,0.41824 +7626,252944.6169369,0,0.54979 +7627,260255.3417472,0,0.66832 +7628,255764.5998025,0,0.75763 +7629,244780.0511607,0,0.82701 +7630,236287.795068,0,0.87094 +7631,231981.6673862,0,0.89366 +7632,217027.9120924,0,0.92368 +7633,206892.5890599,0,0.94309 +7634,206121.8245124,0,0.95459 +7635,203712.6083817,0,0.96024 +7636,207940.2750018,0,0.9574 +7637,218246.3662274,0,0.95237 +7638,232410.8955474,0,0.94374 +7639,268941.442816,0,0.92612 +7640,297690.4989039,0,0.8909 +7641,303805.846362,0.020832,0.83251 +7642,302615.0843664,0.037444,0.71677 +7643,306851.9816996,0.073519,0.49351 +7644,312542.7163531,0.10368,0.29391 +7645,312464.2552914,0.12042,0.16569 +7646,313784.247271,0.090903,0.18652 +7647,309621.1956429,0.05378,0.19178 +7648,309127.3524897,0.018392,0.16891 +7649,313936.5540379,0,0.18545 +7650,327279.549888,0,0.1793 +7651,324274.9527595,0,0.15862 +7652,315431.9295673,0,0.13861 +7653,296905.8882867,0,0.11533 +7654,282870.5889507,0,0.099148 +7655,268309.1389656,0,0.11977 +7656,247507.7268949,0,0.16011 +7657,232503.2026788,0,0.1904 +7658,228450.9196085,0,0.20389 +7659,227301.695822,0,0.2273 +7660,230629.3679105,0,0.25911 +7661,237926.2466511,0,0.28794 +7662,249686.1751969,0,0.29645 +7663,279062.9197787,0,0.28993 +7664,306381.2153293,0,0.25955 +7665,315870.3884416,0.023242,0.21107 +7666,320162.6700537,0.061126,0.16594 +7667,324473.4130921,0.10684,0.18757 +7668,328105.6987144,0.13757,0.32509 +7669,324865.7184008,0.14793,0.66374 +7670,325054.9480202,0.1438,0.78353 +7671,322761.1158039,0.1125,0.72016 +7672,322890.3457879,0.055421,0.58837 +7673,326407.2474959,0,0.58216 +7674,337327.1811456,0,0.60564 +7675,333164.1295175,0,0.56515 +7676,323130.3443296,0,0.45901 +7677,305864.2953932,0,0.31388 +7678,291339.7682606,0,0.21398 +7679,276949.0864687,0,0.19483 +7680,252796.9255266,0,0.25634 +7681,238258.5523243,0,0.3788 +7682,236509.3321834,0,0.50461 +7683,234898.5727398,0,0.60116 +7684,236292.4104246,0,0.65719 +7685,241180.0730345,0,0.6617 +7686,251860.0081424,0,0.63173 +7687,279205.9958324,0,0.58241 +7688,307096.595598,0,0.54461 +7689,316562.6919275,0.038259,0.50507 +7690,318759.6016558,0.16628,0.42436 +7691,321911.8901946,0.25531,0.38899 +7692,327727.2394755,0.29065,0.43583 +7693,326379.5553564,0.27685,0.42152 +7694,324690.334851,0.20742,0.38402 +7695,321219.5867088,0.12056,0.2974 +7696,319244.2140959,0.040371,0.28122 +7697,321653.4302265,0,0.29091 +7698,332656.4402946,0,0.27242 +7699,328885.6939751,0,0.25049 +7700,318667.2945244,0,0.24083 +7701,301507.3987891,0,0.2499 +7702,286710.5656188,0,0.27007 +7703,275181.4049015,0,0.26059 +7704,251947.6999173,0,0.22085 +7705,237995.4769997,0,0.17921 +7706,230758.5978945,0,0.15046 +7707,228266.3053456,0,0.1279 +7708,231690.8999221,0,0.12033 +7709,237446.2495676,0,0.11717 +7710,251749.2395847,0,0.11279 +7711,282598.282913,0,0.1071 +7712,310368.8834076,0,0.092253 +7713,318874.9855701,0.03933,0.067325 +7714,319507.2894205,0.20166,0.030568 +7715,323601.1107,0.2998,0.015449 +7716,327593.3941349,0.32646,0.014206 +7717,325479.5608249,0.29408,0.014963 +7718,323684.1871183,0.2288,0.015537 +7719,320642.6671372,0.14042,0.018204 +7720,319894.9793725,0.051051,0.022576 +7721,323056.4986245,0,0.020685 +7722,334604.120768,0,0.015092 +7723,331668.7539881,0,0.010463 +7724,322156.5040929,0,0.0073769 +7725,305024.3004971,0,0.0060544 +7726,290555.1576434,0,0.0060082 +7727,278901.3822987,0,0.0066061 +7728,255736.907663,0,0.0075302 +7729,240944.6898493,0,0.007843 +7730,233204.7368778,0,0.0074137 +7731,229553.9898292,0,0.00622 +7732,232129.3587965,0,0 +7733,239333.9304056,0,0 +7734,251527.7024692,0,0 +7735,280341.3735492,0,0 +7736,308305.8190199,0,0.011058 +7737,316682.6911983,0.027427,0.025193 +7738,317721.1464271,0.12878,0.03538 +7739,321630.3534437,0.19117,0.04198 +7740,323836.4938852,0.20283,0.052796 +7741,320056.5168526,0.17388,0.085689 +7742,314545.7811054,0.13137,0.12859 +7743,308471.9718565,0.076757,0.17747 +7744,306856.5970562,0.025015,0.22299 +7745,313756.5551316,0,0.24355 +7746,328022.6222961,0,0.24735 +7747,324325.7216818,0,0.23665 +7748,311942.7199987,0,0.2292 +7749,294381.2882417,0,0.23829 +7750,279492.1479399,0,0.25919 +7751,270132.2048116,0,0.30191 +7752,249270.7931055,0,0.37196 +7753,233186.2754515,0,0.43119 +7754,226470.931639,0,0.46603 +7755,221338.6551308,0,0.50714 +7756,220923.2730393,0,0.55784 +7757,226692.4687545,0,0.60256 +7758,225234.0160777,0,0.62107 +7759,225760.1667269,0,0.61001 +7760,237192.4049561,0,0.57285 +7761,251961.545987,0.01948,0.51598 +7762,262563.0200333,0.09583,0.43147 +7763,267935.2950832,0.15422,0.39074 +7764,270750.6625923,0.1776,0.37015 +7765,266010.6913927,0.1683,0.34097 +7766,258699.9665824,0.12741,0.3191 +7767,252695.387682,0.074284,0.3205 +7768,254481.5306754,0.023745,0.33644 +7769,264861.4676062,0,0.29899 +7770,284153.6580778,0,0.25277 +7771,284005.9666675,0,0.25018 +7772,273967.566123,0,0.28983 +7773,255903.0604996,0,0.32357 +7774,245629.27677,0,0.32736 +7775,243321.5984839,0,0.39375 +7776,226097.0877567,0,0.51635 +7777,207584.8925458,0,0.6037 +7778,199891.09314,0,0.65109 +7779,196351.1146491,0,0.68447 +7780,199535.7106839,0,0.71289 +7781,200292.6291618,0,0.70993 +7782,197135.7252664,0,0.61421 +7783,193161.9032578,0,0.3768 +7784,197652.6452025,0,0.20016 +7785,208872.5770294,0.027595,0.19562 +7786,219437.1282231,0.17956,0.21927 +7787,230763.2132511,0.26103,0.15996 +7788,244364.6690693,0.26385,0.098825 +7789,242809.2939044,0.20842,0.07216 +7790,234501.6520746,0.18263,0.067264 +7791,228921.6859788,0.12768,0.064038 +7792,228044.7682301,0.052791,0.083429 +7793,238858.5486787,0,0.11749 +7794,258709.1972955,0,0.13584 +7795,263855.3198735,0,0.1463 +7796,258252.2769949,0,0.13697 +7797,247733.8793669,0,0.10851 +7798,242680.0639204,0,0.076755 +7799,241895.4533032,0,0.051126 +7800,225021.7096754,0,0.030857 +7801,214923.3094955,0,0.01769 +7802,210603.3357439,0,0.011139 +7803,210077.1850947,0,0.0077216 +7804,213912.5464062,0,0 +7805,223526.334146,0,0 +7806,238000.0923563,0,0 +7807,275213.7123975,0,0 +7808,309219.6596212,0,0 +7809,321450.3545374,0.014463,0.0073912 +7810,324141.1074189,0.071726,0.0087027 +7811,330911.8355103,0.11379,0.010683 +7812,336787.1844266,0.12486,0.011904 +7813,336307.1873431,0.10997,0.011984 +7814,336237.9569945,0.081595,0.011772 +7815,332997.9766809,0.045727,0.011963 +7816,332065.6746533,0.012883,0.013567 +7817,333597.9730353,0,0.013044 +7818,340419.4700489,0,0.010439 +7819,335102.5792778,0,0.0072846 +7820,326005.7114741,0,0 +7821,308808.8928862,0,0 +7822,295470.5123927,0,0 +7823,285681.3411032,0,0 +7824,262669.1732344,0,0.0057949 +7825,246538.5020147,0,0.0060626 +7826,241581.6090562,0,0.0063338 +7827,243658.5195137,0,0.0066279 +7828,245001.5882762,0,0.0063766 +7829,250627.7079377,0,0 +7830,259101.5026041,0,0 +7831,292216.6860093,0,0 +7832,322461.1176267,0,0 +7833,330768.7594565,0.016257,0.0059149 +7834,332291.8271254,0.11694,0.0060718 +7835,335716.4217019,0.17788,0 +7836,342796.3786836,0.18443,0 +7837,341822.5384469,0.14946,0 +7838,340451.7775449,0.11944,0 +7839,338319.4828086,0.074198,0 +7840,336450.2633969,0.025226,0 +7841,338688.7113344,0,0 +7842,348524.0361896,0,0 +7843,344933.2887765,0,0.0066009 +7844,333791.8180113,0,0.009599 +7845,316627.3069195,0,0.014829 +7846,303533.5403243,0,0.021508 +7847,292064.3792425,0,0.03059 +7848,272149.1156336,0,0.041993 +7849,254495.3767451,0,0.049123 +7850,246732.3469908,0,0.056841 +7851,243123.1381513,0,0.067253 +7852,244872.3582922,0,0.077522 +7853,246963.1148194,0,0.087428 +7854,258399.9684052,0,0.10051 +7855,290827.4636811,0,0.12600 +7856,320610.3596412,0,0.17113 +7857,329591.8435306,0.010635,0.25388 +7858,331539.5240041,0.067343,0.39865 +7859,336261.0337774,0.099965,0.5827 +7860,340585.6228855,0.096092,0.75296 +7861,338014.8692748,0.06688,0.85832 +7862,335393.3467418,0.049641,0.92652 +7863,331530.2932909,0.027287,0.96096 +7864,329545.6899649,0.0064174,0.97439 +7865,333754.8951587,0,0.97821 +7866,345551.7465572,0,0.9803 +7867,341804.0770206,0,0.97664 +7868,333205.6677266,0,0.96928 +7869,316594.9994235,0,0.92131 +7870,302153.5487092,0,0.82248 +7871,290130.5448387,0,0.73389 +7872,267902.9875872,0,0.70946 +7873,250876.9371926,0,0.76528 +7874,244470.8222704,0,0.82001 +7875,242412.3732392,0,0.8215 +7876,242458.526805,0,0.78179 +7877,246210.8116981,0,0.69671 +7878,255386.1405636,0,0.64885 +7879,286825.9495331,0,0.6987 +7880,317568.8396602,0,0.80936 +7881,325599.5600957,0.010624,0.90998 +7882,324939.5641059,0.077147,0.96248 +7883,328013.391583,0.13493,0.98563 +7884,331931.8293127,0.16081,0.99447 +7885,331341.0636715,0.15572,0.99718 +7886,330482.6073491,0.11786,0.99789 +7887,328830.3096962,0.067472,0.99808 +7888,329716.4581581,0.018977,0.99746 +7889,335231.8092618,0,0.99678 +7890,344504.0606153,0,0.99581 +7891,340802.5446444,0,0.99419 +7892,332628.7481551,0,0.99088 +7893,316235.0016108,0,0.98571 +7894,302130.4719263,0,0.97962 +7895,290518.2347908,0,0.97197 +7896,269162.9799314,0,0.96025 +7897,254218.4553508,0,0.94354 +7898,247221.5747874,0,0.92523 +7899,244498.5144098,0,0.91414 +7900,245523.1235689,0,0.9003 +7901,251126.1664475,0,0.88159 +7902,260513.8017152,0,0.86751 +7903,288529.0161082,0,0.86149 +7904,316996.5354452,0,0.85873 +7905,323688.8024749,0,0.8357 +7906,324454.9516658,0.019561,0.8052 +7907,327044.1667028,0.026911,0.80236 +7908,329245.6917877,0.019225,0.79896 +7909,327764.1623281,0.0037827,0.77092 +7910,322096.5044575,0.0021476,0.71263 +7911,318422.680626,4.0037e-05,0.65242 +7912,316784.2290429,0,0.59423 +7913,324261.1066898,0,0.49993 +7914,335453.3463773,0,0.40127 +7915,330745.6826737,0,0.34087 +7916,319862.6718765,0,0.28688 +7917,303593.5399597,0,0.23582 +7918,291159.7693543,0,0.19523 +7919,284121.3505818,0,0.15123 +7920,266661.4566693,0,0.10992 +7921,251306.1653538,0,0.07997 +7922,241775.4540323,0,0.064241 +7923,236393.9482691,0,0.054622 +7924,237340.0963664,0,0.05212 +7925,237732.4016751,0,0.050423 +7926,235046.2641501,0,0.045506 +7927,231713.976705,0,0.039406 +7928,245347.7400191,0,0.035897 +7929,259692.2682454,0.011107,0.032796 +7930,272712.1891354,0.12438,0.021789 +7931,277424.4681956,0.19337,0.019175 +7932,278509.07699,0.19448,0.020287 +7933,274775.2535232,0.14664,0.021257 +7934,268313.7543222,0.12779,0.020308 +7935,264635.3151342,0.086609,0.017426 +7936,265867.6153389,0.030703,0.016154 +7937,276801.3950584,0,0.013611 +7938,293375.140509,0,0.0093208 +7939,292045.9178162,0,0 +7940,283142.8949885,0,0 +7941,266712.2255916,0,0 +7942,258049.2013057,0,0 +7943,258524.5830326,0,0.007987 +7944,245850.8138855,0,0.0099243 +7945,234224.7306802,0,0.011191 +7946,223563.2569986,0,0.011182 +7947,218144.8283828,0,0.01157 +7948,218144.8283828,0,0.012754 +7949,218754.0554504,0,0.01487 +7950,213566.3946633,0,0.017695 +7951,206352.592341,0,0.021531 +7952,211946.4045064,0,0.026422 +7953,222870.9535127,0.011801,0.030849 +7954,235383.1851798,0.1466,0.028445 +7955,245661.584266,0.20772,0.02574 +7956,255713.8308802,0.17364,0.025176 +7957,254998.4506115,0.081886,0.025997 +7958,247812.3404287,0.07845,0.028631 +7959,243792.3648543,0.057578,0.032876 +7960,242347.7582472,0.020516,0.03873 +7961,253507.6904387,0,0.044329 +7962,273358.3390555,0,0.047846 +7963,279072.1504918,0,0.052992 +7964,274742.9460272,0,0.060821 +7965,266647.6105996,0,0.073414 +7966,262710.7114436,0,0.089588 +7967,263947.6270049,0,0.1038 +7968,251352.3189195,0,0.11594 +7969,243515.44346,0,0.12443 +7970,237880.0930854,0,0.12431 +7971,237317.0195836,0,0.11772 +7972,237100.0978247,0,0.11024 +7973,242075.4522095,0,0.097007 +7974,254209.2246377,0,0.085635 +7975,289502.8563449,0,0.082102 +7976,323134.9596862,0,0.082893 +7977,333713.3569496,0.0080064,0.084783 +7978,337821.0242988,0.13956,0.09261 +7979,344153.2935158,0.21215,0.1103 +7980,348860.9572194,0.19758,0.13827 +7981,348948.6489943,0.12487,0.17073 +7982,347568.6573792,0.12919,0.20838 +7983,343474.8360997,0.10148,0.25435 +7984,340904.082489,0.039499,0.30183 +7985,345833.2833081,0,0.34158 +7986,354630.1529346,0,0.36027 +7987,350010.1810059,0,0.35209 +7988,343299.4525499,0,0.33037 +7989,326813.3988742,0,0.31157 +7990,312819.6377474,0,0.30201 +7991,302499.7004521,0,0.30017 +7992,284896.7304859,0,0.3125 +7993,273021.4180258,0,0.3415 +7994,265922.9996178,0,0.37221 +7995,259433.8082773,0,0.38751 +7996,257130.7453478,0,0.39286 +7997,258667.6590864,0,0.40606 +7998,265743.0007115,0,0.4313 +7999,295078.2070841,0,0.46142 +8000,324459.5670224,0,0.49577 +8001,330473.3766359,0.0036056,0.50185 +8002,331576.4468567,0.13498,0.49967 +8003,334701.043256,0.23329,0.46497 +8004,339427.1683859,0.25983,0.44339 +8005,336741.0308609,0.22726,0.45159 +8006,335859.4977556,0.19394,0.45435 +8007,332614.9020854,0.12818,0.45421 +8008,331996.4443047,0.042056,0.46421 +8009,339708.7051368,0,0.4385 +8010,351533.2486747,0,0.40454 +8011,349054.8021954,0,0.39603 +8012,340830.2367838,0,0.40676 +8013,326434.9396353,0,0.4199 +8014,312584.2545623,0,0.4137 +8015,304262.7666627,0,0.40393 +8016,284795.1926413,0,0.38801 +8017,277821.3888608,0,0.36456 +8018,273275.2626372,0,0.35471 +8019,268590.6757165,0,0.36576 +8020,268369.138601,0,0.38453 +8021,272430.6523845,0,0.42536 +8022,281762.9033734,0,0.50611 +8023,310599.6512362,0,0.56834 +8024,341328.6952936,0,0.61321 +8025,347536.3498832,0.0033985,0.62518 +8026,347776.3484249,0.13346,0.54882 +8027,349280.9546675,0.2409,0.48236 +8028,354514.7690203,0.28083,0.45483 +8029,355645.5313805,0.26093,0.49288 +8030,356323.9887966,0.19698,0.53738 +8031,352516.3196245,0.11128,0.56494 +8032,353074.7777698,0.029046,0.62102 +8033,359993.1972714,0,0.68376 +8034,368693.1444099,0,0.73717 +8035,364700.860975,0,0.7811 +8036,356605.5255475,0,0.81278 +8037,344610.2138164,0,0.82962 +8038,331253.3718966,0,0.83008 +8039,322821.1154393,0,0.82233 +8040,309478.1195892,0,0.81405 +8041,299084.3365887,0,0.79968 +8042,288302.8636362,0,0.78528 +8043,279949.0682406,0,0.77771 +8044,272993.7258863,0,0.77114 +8045,273589.1068841,0,0.77721 +8046,279898.2993183,0,0.79497 +8047,304539.688057,0,0.82056 +8048,330925.68158,0,0.84792 +8049,336948.7219067,0.00052568,0.8584 +8050,337954.8696394,0.10336,0.86231 +8051,342191.7669726,0.18183,0.88824 +8052,345556.3619137,0.20034,0.9097 +8053,344739.4438005,0.1706,0.92003 +8054,343493.297526,0.13212,0.93626 +8055,338702.5574041,0.076778,0.94444 +8056,337308.7197193,0.020276,0.96034 +8057,342842.5322493,0,0.96791 +8058,353227.0845366,0,0.9754 +8059,349479.4150001,0,0.97623 +8060,343280.9911237,0,0.97531 +8061,330154.9170324,0,0.97093 +8062,318625.7563152,0,0.95652 +8063,310793.4962123,0,0.93182 +8064,296093.58553,0,0.90336 +8065,282496.7450684,0,0.88086 +8066,278942.9205078,0,0.84273 +8067,272910.649468,0,0.8033 +8068,270436.8183454,0,0.7856 +8069,269698.3612938,0,0.80507 +8070,277973.6956277,0,0.8261 +8071,301728.9359046,0,0.84532 +8072,329642.6124529,0,0.84985 +8073,336593.3394506,0.00097525,0.83377 +8074,337728.7171674,0.12306,0.75526 +8075,341407.1563554,0.19446,0.67531 +8076,343488.6821694,0.17767,0.66469 +8077,341421.0024251,0.10342,0.72275 +8078,334391.8143657,0.10119,0.76779 +8079,328082.6219316,0.075116,0.82198 +8080,325507.2529643,0.026409,0.86053 +8081,334174.8926068,0,0.88489 +8082,344910.2119936,0,0.89289 +8083,341933.3070046,0,0.9113 +8084,332734.9013563,0,0.92161 +8085,316516.5383617,0,0.92717 +8086,306833.5202734,0,0.91685 +8087,303732.0006569,0,0.8958 +8088,287675.1751423,0,0.86971 +8089,275453.7109393,0,0.85518 +8090,265793.7696338,0,0.83617 +8091,257772.2799114,0,0.82133 +8092,255021.5273944,0,0.78416 +8093,254735.3752869,0,0.74701 +8094,251823.0852899,0,0.6949 +8095,248846.1803008,0,0.64167 +8096,260832.2613187,0,0.58845 +8097,274530.6396249,0,0.5426 +8098,287564.4065846,0.059144,0.50249 +8099,293398.2172918,0.10956,0.46224 +8100,296799.7350855,0.12196,0.44416 +8101,294681.2864189,0.10297,0.41392 +8102,285999.8007067,0.097251,0.34969 +8103,280373.6810452,0.069838,0.27613 +8104,278675.2298266,0.022525,0.31882 +8105,291348.9989738,0,0.34842 +8106,307867.3601455,0,0.24986 +8107,304655.0719713,0,0.18438 +8108,296296.6612191,0,0.17509 +8109,280821.3706327,0,0.16955 +8110,274599.8699734,0,0.11002 +8111,270584.5097557,0,0.057014 +8112,260615.3395598,0,0.033664 +8113,248186.184311,0,0.022829 +8114,239255.4693439,0,0.018088 +8115,235364.7237536,0,0.020872 +8116,231437.0553107,0,0.037791 +8117,231584.746721,0,0.083482 +8118,230781.6746774,0,0.1603 +8119,223877.1012455,0,0.23608 +8120,228801.686708,0,0.30605 +8121,242033.9140003,0,0.33444 +8122,253189.2308352,0.09368,0.31997 +8123,262443.0207624,0.16013,0.29932 +8124,275449.0955827,0.1576,0.29989 +8125,277133.7007315,0.10661,0.3154 +8126,269998.359471,0.083665,0.33897 +8127,262207.6375772,0.048703,0.36475 +8128,260338.4181655,0.011439,0.41086 +8129,269739.899503,0,0.47271 +8130,282399.8225804,0,0.49402 +8131,284061.3509463,0,0.48956 +8132,279459.8404439,0,0.49864 +8133,269453.7473955,0,0.50909 +8134,264787.621901,0,0.50362 +8135,267482.9901392,0,0.46469 +8136,253392.3065244,0,0.41326 +8137,241203.1498173,0,0.36495 +8138,234483.1906483,0,0.31918 +8139,234418.5756563,0,0.2873 +8140,233352.4282881,0,0.25332 +8141,239541.6214514,0,0.22242 +8142,252160.0063196,0,0.2053 +8143,284772.1158585,0,0.19775 +8144,316258.0783937,0,0.19398 +8145,326554.9389062,0,0.18361 +8146,328304.159047,0.030811,0.15514 +8147,332951.8231152,0.067799,0.12922 +8148,337756.4093068,0.087308,0.11913 +8149,337396.4114942,0.087714,0.10614 +8150,335351.8085327,0.085487,0.096649 +8151,331271.8333229,0.062511,0.098739 +8152,329061.0775248,0.019959,0.11999 +8153,333819.5101507,0,0.14659 +8154,343802.5264163,0,0.16962 +8155,340354.8550569,0,0.18788 +8156,332868.7466969,0,0.20689 +8157,318034.990674,0,0.2216 +8158,306542.7528093,0,0.24599 +8159,300081.2536083,0,0.28002 +8160,280115.2210772,0,0.30938 +8161,266084.5370978,0,0.31841 +8162,258543.0444589,0,0.30781 +8163,255916.9065694,0,0.27332 +8164,254393.8389005,0,0.23136 +8165,257790.7413376,0,0.19293 +8166,265927.6149744,0,0.18062 +8167,292807.4516506,0,0.18969 +8168,323361.1121582,0,0.20748 +8169,332019.5210876,0,0.22176 +8170,331225.6797572,0.079386,0.23053 +8171,334364.1222263,0.14349,0.2284 +8172,338051.7921274,0.14686,0.23748 +8173,337530.2568347,0.10562,0.27797 +8174,335776.4213373,0.10757,0.38313 +8175,333150.2834478,0.081825,0.51821 +8176,331761.0611196,0.02757,0.62416 +8177,339625.6287185,0,0.69987 +8178,346668.6628476,0,0.71555 +8179,342251.7666081,0,0.67809 +8180,333944.1247782,0,0.60771 +8181,319262.6755222,0,0.52072 +8182,306727.3670722,0,0.44898 +8183,296919.7343564,0,0.41319 +8184,277761.3892254,0,0.41555 +8185,262696.8653738,0,0.39683 +8186,257379.9746027,0,0.31051 +8187,253152.3079826,0,0.18674 +8188,252418.4662877,0,0.090655 +8189,255755.3690893,0,0.041257 +8190,260906.1070239,0,0.021271 +8191,290822.8483246,0,0.013157 +8192,323019.5757719,0,0.0081927 +8193,332102.5975059,0,0 +8194,332757.9781391,0.066738,0 +8195,336777.9537135,0.13305,0 +8196,341402.5409988,0.15321,0.0072004 +8197,339104.0934259,0.13379,0.018584 +8198,337834.8703685,0.11755,0.038286 +8199,334927.1957281,0.078204,0.06655 +8200,335430.2695944,0.022828,0.088172 +8201,341213.3113793,0,0.10526 +8202,348810.1882971,0,0.11572 +8203,345325.5940851,0,0.11247 +8204,336930.2604804,0,0.11155 +8205,321085.7413682,0,0.11249 +8206,309681.1952784,0,0.10898 +8207,300205.8682357,0,0.10284 +8208,280378.2964018,0,0.080356 +8209,267012.2237688,0,0.071244 +8210,258935.3497675,0,0.06793 +8211,256161.5204677,0,0.067847 +8212,256881.5160929,0,0.078703 +8213,260730.7234741,0,0.085748 +8214,267515.2976352,0,0.079293 +8215,293093.6037581,0,0.068871 +8216,322142.6580232,0,0.046356 +8217,329919.5338473,0,0.026323 +8218,331774.9071893,0.10305,0.015907 +8219,337581.025757,0.18986,0.009965 +8220,341970.2298572,0.18617,0.0067076 +8221,341005.6203336,0.12163,0 +8222,339533.3215871,0.12709,0 +8223,337987.1771354,0.09885,0 +8224,338342.5595914,0.034206,0 +8225,344028.6788883,0,0 +8226,354574.7686557,0,0 +8227,351099.4051569,0,0 +8228,341697.9238194,0,0 +8229,325142.6397951,0,0 +8230,314255.0136414,0,0 +8231,303192.0039379,0,0 +8232,283825.9677612,0,0 +8233,271562.965349,0,0 +8234,266486.0731196,0,0 +8235,260379.9563746,0,0 +8236,261607.6412228,0,0 +8237,264007.6266403,0,0 +8238,271382.9664426,0,0 +8239,296605.8901095,0,0 +8240,324242.6452635,0,0 +8241,331327.2176018,0,0.007438 +8242,331428.7554464,0.099454,0.0061466 +8243,334899.5035886,0.19564,0.0089112 +8244,336676.4158689,0.20945,0.015344 +8245,334276.4304514,0.16226,0.01505 +8246,328876.463262,0.15104,0.014668 +8247,322391.8872781,0.10677,0.022283 +8248,321658.0455831,0.033761,0.038833 +8249,331507.2165081,0,0.066114 +8250,341490.2327737,0,0.11141 +8251,335554.8842219,0,0.16316 +8252,325045.7173071,0,0.17698 +8253,308379.664725,0,0.18975 +8254,297076.6564798,0,0.21529 +8255,294432.057164,0,0.25129 +8256,277359.8532036,0,0.27591 +8257,260878.4148844,0,0.28686 +8258,258201.5080726,0,0.2932 +8259,253263.0765404,0,0.29166 +8260,250840.01434,0,0.30677 +8261,251933.8538476,0,0.32417 +8262,250627.7079377,0,0.35053 +8263,252132.3141802,0,0.37185 +8264,262027.6386709,0,0.39459 +8265,276233.7062,0,0.41365 +8266,291616.689655,0.096559,0.38971 +8267,300164.3300266,0.18129,0.35968 +8268,303579.69389,0.17505,0.32873 +8269,303427.3871231,0.10782,0.30458 +8270,297833.5749577,0.1242,0.27452 +8271,292581.2991785,0.10288,0.24231 +8272,291482.8443144,0.037202,0.23164 +8273,298299.7259714,0,0.22558 +8274,308804.2775297,0,0.22136 +8275,305688.9118435,0,0.22478 +8276,295572.0502373,0,0.2313 +8277,281167.5223756,0,0.24053 +8278,272670.6509263,0,0.23844 +8279,271636.8110541,0,0.22229 +8280,257970.740244,0,0.21162 +8281,244493.8990533,0,0.20521 +8282,235263.185909,0,0.18797 +8283,228187.8442839,0,0.15663 +8284,225589.3985337,0,0.14093 +8285,223955.5623072,0,0.1346 +8286,221661.7300908,0,0.13847 +8287,211540.2531281,0,0.18375 +8288,215209.461603,0,0.2831 +8289,223397.104162,0,0.35757 +8290,235027.8027238,0.083357,0.36613 +8291,248260.0300162,0.19606,0.38708 +8292,261995.3311749,0.25445,0.42924 +8293,264270.701965,0.25701,0.45161 +8294,254499.9921017,0.2173,0.48001 +8295,250023.0962267,0.13936,0.51183 +8296,249672.3291272,0.039956,0.53508 +8297,260809.1845358,0,0.54254 +8298,274987.5599255,0,0.51411 +8299,278975.2280038,0,0.43586 +8300,273626.0297367,0,0.38508 +8301,265027.6204428,0,0.33253 +8302,262401.4825532,0,0.28276 +8303,262784.5571487,0,0.26985 +8304,251560.0099652,0,0.29033 +8305,244092.3630315,0,0.30924 +8306,239010.8554456,0,0.31445 +8307,235817.0286976,0,0.30479 +8308,234847.8038175,0,0.28064 +8309,240409.3084869,0,0.2693 +8310,251693.8553058,0,0.26881 +8311,283235.2021199,0,0.25934 +8312,316281.1551766,0,0.23404 +8313,325890.3275598,0,0.20686 +8314,325548.7911734,0.058551,0.16024 +8315,328054.9297921,0.12548,0.11272 +8316,331322.6022452,0.13288,0.090949 +8317,330081.0713273,0.097724,0.094143 +8318,329333.3835626,0.076399,0.12366 +8319,325982.6346912,0.043563,0.19147 +8320,326785.7067348,0.0093516,0.28662 +8321,335199.5017658,0,0.37216 +8322,346050.2050669,0,0.37589 +8323,344365.5999181,0,0.37301 +8324,336588.724094,0,0.38838 +8325,321593.4305911,0,0.38039 +8326,308490.4332828,0,0.40618 +8327,301788.93554,0,0.48463 +8328,283272.1249725,0,0.56486 +8329,272762.9580577,0,0.59762 +8330,262498.4050412,0,0.60742 +8331,261427.6423165,0,0.62095 +8332,259590.7304008,0,0.65088 +8333,262738.403583,0,0.68141 +8334,269993.7441144,0,0.65111 +8335,296993.5800615,0,0.57887 +8336,324316.4909687,0,0.52419 +8337,332097.9821493,0,0.46465 +8338,331604.1389961,0.081367,0.3793 +8339,335859.4977556,0.18567,0.31833 +8340,340650.2378775,0.21691,0.30617 +8341,339597.9365791,0.18919,0.33287 +8342,340202.54829,0.15205,0.39625 +8343,337345.6425719,0.091099,0.47527 +8344,338647.1731252,0.023109,0.55659 +8345,345976.3593618,0,0.52793 +8346,356379.3730754,0,0.40704 +8347,353370.1605904,0,0.32727 +8348,346419.4335927,0,0.33454 +8349,334156.4311805,0,0.41079 +8350,322834.961509,0,0.48173 +8351,314735.0107249,0,0.52567 +8352,299670.4868734,0,0.61703 +8353,288865.937138,0,0.70487 +8354,282053.6708375,0,0.79568 +8355,275776.7858993,0,0.85669 +8356,271899.8863787,0,0.91244 +8357,272444.4984542,0,0.96212 +8358,278721.3833924,0,0.98272 +8359,302199.7022749,0,0.98974 +8360,328248.7747681,0,0.99081 +8361,335536.4227956,0,0.98934 +8362,334345.6608,0.041315,0.9872 +8363,338914.8638064,0.10027,0.98499 +8364,342464.0730104,0.1205,0.98475 +8365,339944.088322,0.1075,0.98589 +8366,337202.5665181,0.084865,0.98703 +8367,333565.6655393,0.049047,0.98807 +8368,333261.0520055,0.01097,0.98802 +8369,338601.0195595,0,0.98661 +8370,348976.3411337,0,0.98415 +8371,344900.9812805,0,0.97986 +8372,337479.4879125,0,0.97032 +8373,323693.4178314,0,0.9574 +8374,312713.4845463,0,0.95338 +8375,306551.9835225,0,0.95122 +8376,291228.9997029,0,0.9617 +8377,277946.0034882,0,0.97452 +8378,270819.8929408,0,0.98605 +8379,267510.6822786,0,0.99369 +8380,264718.3915525,0,0.99801 +8381,265124.5429308,0,0.99904 +8382,272379.8834622,0,0.99936 +8383,296993.5800615,0,0.99953 +8384,325405.7151197,0,0.99954 +8385,334613.3514811,0,0.99944 +8386,335974.8816699,0.076333,0.99886 +8387,341661.0009668,0.17734,0.99595 +8388,346696.354987,0.20539,0.98717 +8389,347767.1177118,0.17477,0.97613 +8390,347508.6577437,0.14827,0.97244 +8391,345953.2825789,0.094512,0.9701 +8392,346114.820059,0.026127,0.97113 +8393,351094.7898003,0,0.97317 +8394,356540.9105555,0,0.97288 +8395,351824.0161387,0,0.9709 +8396,341624.0781142,0,0.96789 +8397,325931.8657689,0,0.97246 +8398,314915.0096312,0,0.97984 +8399,307908.8983547,0,0.98777 +8400,290915.155456,0,0.99268 +8401,275435.249513,0,0.99318 +8402,266047.6142452,0,0.99152 +8403,258538.4291023,0,0.98875 +8404,256641.5175512,0,0.98738 +8405,258247.6616383,0,0.9867 +8406,265779.923564,0,0.98456 +8407,287033.6405788,0,0.98515 +8408,315478.083133,0,0.98158 +8409,323984.1852955,0,0.97237 +8410,326596.4771153,0.068082,0.96382 +8411,331604.1389961,0.16603,0.93585 +8412,334304.1225908,0.202000,0.88085 +8413,331359.5250978,0.18387,0.79912 +8414,324214.9531241,0.16329,0.68427 +8415,317790.3767756,0.10958,0.50212 +8416,315948.8495034,0.032692,0.44449 +8417,323578.0339171,0,0.4001 +8418,334853.3500229,0,0.32068 +8419,329148.7692997,0,0.22251 +8420,319318.059801,0,0.18902 +8421,304188.9209575,0,0.26989 +8422,294030.5211422,0,0.43885 +8423,291473.6136012,0,0.57869 +8424,278033.6952631,0,0.63614 +8425,265812.2310601,0,0.64614 +8426,257241.5139056,0,0.65047 +8427,248993.8717111,0,0.67782 +8428,244346.207643,0,0.71454 +8429,244387.7458521,0,0.72899 +8430,241115.4580425,0,0.71347 +8431,234649.3434849,0,0.68918 +8432,246630.8091462,0,0.67725 +8433,259590.7304008,0,0.65679 +8434,272841.4191194,0.075927,0.60486 +8435,281485.9819791,0.18826,0.5502 +8436,286202.8763958,0.23332,0.55007 +8437,285021.3451134,0.21828,0.57933 +8438,278158.3098906,0.18046,0.57305 +8439,271419.8892952,0.11206,0.54138 +8440,271978.3474404,0.03044,0.56434 +8441,284245.9652092,0,0.56857 +8442,301872.0119583,0,0.54473 +8443,298742.8002024,0,0.51946 +8444,291588.9975155,0,0.50566 +8445,275693.709481,0,0.50035 +8446,268530.6760811,0,0.48093 +8447,271899.8863787,0,0.50343 +8448,262923.0178459,0,0.55878 +8449,247835.4172115,0,0.61296 +8450,235203.1862735,0,0.64724 +8451,230218.6011756,0,0.69673 +8452,224541.7125919,0,0.75417 +8453,220729.4280633,0,0.82183 +8454,215287.9226647,0,0.88312 +8455,206864.8969205,0,0.9102 +8456,211946.4045064,0,0.88136 +8457,219446.3589362,0,0.74538 +8458,233703.1953876,0.045248,0.56611 +8459,246510.8098753,0.12007,0.48677 +8460,260356.8795918,0.14798,0.52763 +8461,260629.1856295,0.13591,0.61516 +8462,252086.1606145,0.13195,0.69984 +8463,245236.9714614,0.096281,0.72501 +8464,245130.8182602,0.031187,0.7271 +8465,254458.4538926,0,0.70614 +8466,272402.9602451,0,0.66242 +8467,275596.786993,0,0.62805 +8468,271050.6607694,0,0.61383 +8469,262683.0193041,0,0.63296 +8470,258981.5033333,0,0.70637 +8471,261026.1062947,0,0.76615 +8472,248680.0274642,0,0.78459 +8473,237450.8649242,0,0.76327 +8474,233855.5021545,0,0.71428 +8475,231713.976705,0,0.65872 +8476,231487.8242329,0,0.59742 +8477,241184.688391,0,0.57208 +8478,254523.0688846,0,0.5834 +8479,283890.5827532,0,0.62749 +8480,312335.0253074,0,0.68259 +8481,321279.5863442,0,0.7316 +8482,320693.4360595,0.024353,0.76954 +8483,323799.5710326,0.065545,0.77623 +8484,327621.0862743,0.077279,0.78101 +8485,327071.8588422,0.065167,0.79522 +8486,324801.1034087,0.051955,0.76809 +8487,319248.8294524,0.030084,0.69961 +8488,314402.7050517,0.0061621,0.6351 +8489,320038.0554263,0,0.55894 +8490,331742.5996933,0,0.46071 +8491,328285.6976207,0,0.36955 +8492,322147.2733798,0,0.3233 +8493,309307.351396,0,0.30155 +8494,298133.5731348,0,0.2447 +8495,290495.1580079,0,0.18324 +8496,273219.8783584,0,0.14263 +8497,261003.0295119,0,0.11275 +8498,252607.6959071,0,0.086652 +8499,248112.3386058,0,0.070419 +8500,245527.7389254,0,0.059804 +8501,246289.2727598,0,0.049613 +8502,253143.0772695,0,0.041911 +8503,277701.3895899,0,0.04214 +8504,307470.4394803,0,0.052593 +8505,316987.3047321,0,0.075912 +8506,317554.9935905,0.045748,0.11843 +8507,321118.0488642,0.11429,0.18032 +8508,324574.9509367,0.12678,0.22566 +8509,324487.2591618,0.097124,0.25322 +8510,321242.6634916,0.077854,0.29564 +8511,316825.7672521,0.045819,0.35254 +8512,315247.3153044,0.01058,0.44002 +8513,322179.5808758,0,0.56244 +8514,333034.8995335,0,0.68561 +8515,329577.9974609,0,0.76272 +8516,321464.2006071,0,0.80896 +8517,305273.529752,0,0.82841 +8518,294635.1328532,0,0.80856 +8519,286419.7981547,0,0.74978 +8520,264579.9308553,0,0.6535 +8521,249187.7166872,0,0.55135 +8522,242716.986773,0,0.42815 +8523,236370.8714863,0,0.32046 +8524,236287.795068,0,0.24233 +8525,238877.010105,0,0.18179 +8526,245209.279322,0,0.15812 +8527,268507.5992982,0,0.1505 +8528,298212.0341966,0,0.15184 +8529,310091.9620133,0,0.14103 +8530,311942.7199987,0.065277,0.11975 +8531,312515.0242137,0.16665,0.10668 +8532,315025.7781889,0.19416,0.11196 +8533,312155.026401,0.16285,0.12027 +8534,308148.8968964,0.13111,0.13383 +8535,305508.9129371,0.078325,0.15182 +8536,304096.6138261,0.019805,0.19315 +8537,313659.6326436,0,0.22711 +8538,324367.259891,0,0.25258 +8539,319853.4411634,0,0.24158 +8540,310530.4208877,0,0.2116 +8541,295922.8173368,0,0.17888 +8542,284121.3505818,0,0.15366 +8543,276949.0864687,0,0.15051 +8544,254818.4517052,0,0.18786 +8545,239587.7750171,0,0.26248 +8546,228289.3821284,0,0.32025 +8547,227094.0047763,0,0.32553 +8548,228104.7678656,0,0.31277 +8549,230933.9814443,0,0.28862 +8550,233860.117511,0,0.2638 +8551,247480.0347555,0,0.27388 +8552,275075.2517004,0,0.3123 +8553,291238.230416,0,0.34321 +8554,297321.2703781,0.05211,0.30959 +8555,300196.6375226,0.1361,0.28778 +8556,304313.535585,0.1603,0.3106 +8557,301392.0148748,0.13602,0.36836 +8558,294436.6725205,0.12119,0.45551 +8559,289059.782114,0.081521,0.56423 +8560,289138.2431757,0.024306,0.6716 +8561,299855.1011363,0,0.74986 +8562,310087.3466567,0,0.79456 +8563,307248.9023648,0,0.81097 +8564,297182.809681,0,0.81371 +8565,280087.5289377,0,0.8112 +8566,266393.7659881,0,0.80432 +8567,261492.2573085,0,0.77786 +8568,245915.4288775,0,0.74438 +8569,230001.6794167,0,0.68861 +8570,221592.4997422,0,0.6088 +8571,216714.0678455,0,0.53231 +8572,212269.4794665,0,0.48515 +8573,212024.8655682,0,0.45998 +8574,207409.508996,0,0.47821 +8575,201460.3143745,0,0.50572 +8576,212749.47655,0,0.53254 +8577,230675.5214762,0,0.59212 +8578,246783.1159131,0.063675,0.62789 +8579,257056.8996427,0.15777,0.62765 +8580,261889.1779737,0.17247,0.61365 +8581,261289.1816193,0.12804,0.65175 +8582,253253.8458272,0.12068,0.70579 +8583,245624.6614135,0.085959,0.7405 +8584,240953.9205624,0.027547,0.77856 +8585,246949.2687497,0,0.77204 +8586,256798.4396746,0,0.75867 +8587,248250.799303,0,0.71027 +8588,234220.1153237,0,0.65329 +8589,226009.3959818,0,0.57858 +8590,227047.8512105,0,0.47154 +8591,236698.5618029,0,0.36638 +8592,230066.2944087,0,0.27693 +8593,221361.7319136,0,0.21512 +8594,209421.8044615,0,0.18201 +8595,205124.9074928,0,0.16325 +8596,200274.1677355,0,0.14100 +8597,199964.9388451,0,0.12063 +8598,195958.8093405,0,0.10522 +8599,186778.8651185,0,0.097947 +8600,191375.7602644,0,0.10447 +8601,201081.8551356,0,0.1206 +8602,218130.9823131,0.063984,0.11017 +8603,232830.8929954,0.19387,0.071791 +8604,245329.2785928,0.2738,0.061561 +8605,238627.7808501,0.29569,0.077746 +8606,223540.1802157,0.22196,0.11697 +8607,216801.7596204,0.12099,0.20653 +8608,212500.2472951,0.027366,0.35287 +8609,219598.6657031,0,0.46273 +8610,236066.2579525,0,0.55602 +8611,242458.526805,0,0.62841 +8612,240423.1545566,0,0.65102 +8613,234824.7270346,0,0.63953 +8614,236232.4107891,0,0.61878 +8615,242689.2946336,0,0.60933 +8616,233989.3474951,0,0.58297 +8617,223120.1827676,0,0.53283 +8618,218214.0587314,0,0.46668 +8619,213386.395757,0,0.39982 +8620,205420.2903134,0,0.37805 +8621,200846.4719504,0,0.36777 +8622,197334.185599,0,0.35307 +8623,185385.0274337,0,0.34159 +8624,191191.1460015,0,0.3225 +8625,201340.3151036,0,0.2996 +8626,215574.0747722,0.064027,0.25823 +8627,227818.6157581,0.14662,0.19500 +8628,238964.7018798,0.13784,0.1925 +8629,236952.4064144,0.06818,0.29416 +8630,225727.8592309,0.070788,0.34528 +8631,219764.8185397,0.054534,0.3398 +8632,216044.8411425,0.018459,0.33731 +8633,223812.4862535,0,0.38681 +8634,239389.3146845,0,0.36608 +8635,243838.51842,0,0.31138 +8636,240589.3073932,0,0.25746 +8637,236287.795068,0,0.22313 +8638,236486.2554006,0,0.20664 +8639,241115.4580425,0,0.22518 +8640,229715.5273092,0,0.26415 +8641,219026.3614881,0,0.30766 +8642,209537.1883758,0,0.33184 +8643,207211.0486634,0,0.35929 +8644,205531.0588712,0,0.36813 +8645,207224.8947331,0,0.36421 +8646,208632.5784876,0,0.38852 +8647,216534.0689392,0,0.44478 +8648,237981.63093,0,0.51619 +8649,255252.295223,0,0.60332 +8650,270132.2048116,0.046612,0.68042 +8651,279842.9150394,0.12424,0.74825 +8652,286830.5648896,0.14631,0.80302 +8653,289733.6241735,0.12272,0.83733 +8654,285205.9593762,0.10011,0.86131 +8655,278864.4594461,0.061026,0.87735 +8656,275864.4776742,0.015996,0.90077 +8657,284015.1973806,0,0.91636 +8658,298392.0331029,0,0.91685 +8659,297067.4257667,0,0.91356 +8660,289955.161289,0,0.90915 +8661,276718.3186401,0,0.89818 +8662,268936.8274594,0,0.87597 +8663,266615.3031036,0,0.83612 +8664,250960.0136109,0,0.79475 +8665,236338.5639903,0,0.76672 +8666,230237.0626019,0,0.72886 +8667,224126.3305004,0,0.67771 +8668,219515.5892848,0,0.64271 +8669,220849.4273341,0,0.59687 +8670,223263.2588214,0,0.5557 +8671,228626.3031582,0,0.49997 +8672,245689.2764055,0,0.44273 +8673,260753.800257,0,0.37353 +8674,272666.0355697,0.037407,0.29692 +8675,280650.6024395,0.1009,0.29135 +8676,287564.4065846,0.11862,0.33269 +8677,288436.7089767,0.098787,0.35377 +8678,281832.133722,0.098858,0.33142 +8679,275241.404537,0.074687,0.28486 +8680,272989.1105298,0.027139,0.27703 +8681,281250.5987939,0,0.23802 +8682,295752.0491436,0,0.18858 +8683,294782.8242635,0,0.14537 +8684,286682.8734793,0,0.11542 +8685,272689.1123526,0,0.091385 +8686,265599.9246577,0,0.061062 +8687,263790.7048814,0,0.04769 +8688,251716.9320887,0,0.046672 +8689,241858.5304506,0,0.051768 +8690,233237.0443738,0,0.063255 +8691,229120.1463114,0,0.075377 +8692,224269.4065541,0,0.087094 +8693,225090.9400239,0,0.097178 +8694,225381.707488,0,0.10078 +8695,228358.612477,0,0.097611 +8696,246316.9648993,0,0.098354 +8697,261141.490209,0,0.09919 +8698,272324.4991834,0.064548,0.10126 +8699,279169.0729799,0.16974,0.15607 +8700,284135.1966515,0.20024,0.24481 +8701,285515.1882666,0.16953,0.35195 +8702,279755.2232645,0.14213,0.41438 +8703,273649.1065196,0.09029,0.47966 +8704,271175.2753969,0.027597,0.54822 +8705,279667.5314897,0,0.57122 +8706,295853.5869882,0,0.56937 +8707,294930.5156738,0,0.55682 +8708,286692.1041925,0,0.55564 +8709,272278.3456176,0,0.56069 +8710,264109.1644849,0,0.54781 +8711,264021.4727101,0,0.5256 +8712,249981.5580176,0,0.49985 +8713,237750.8631014,0,0.4802 +8714,230297.0622373,0,0.46002 +8715,227010.928358,0,0.42095 +8716,223586.3337814,0,0.39658 +8717,222787.8770944,0,0.35309 +8718,221444.8083319,0,0.28859 +8719,223535.5648591,0,0.25684 +8720,241198.5344608,0,0.27737 +8721,256313.8272346,0,0.33214 +8722,268041.4482844,0.064234,0.42658 +8723,274724.4846009,0.17387,0.54732 +8724,280124.4517903,0.2134,0.62683 +8725,280969.062043,0.19244,0.72635 +8726,274752.1767403,0.15511,0.80526 +8727,269638.3616584,0.094047,0.87389 +8728,266947.6087768,0.027137,0.93099 +8729,274927.5602901,0,0.95698 +8730,290979.770448,0,0.97053 +8731,290278.236249,0,0.97693 +8732,281855.2105049,0,0.97879 +8733,268110.678633,0,0.98094 +8734,258136.8930806,0,0.98302 +8735,254315.3778388,0,0.98307 +8736,240760.0755864,0,0.97995 +8737,228612.4570885,0,0.96968 +8738,219575.5889202,0,0.9363 +8739,213760.2396393,0,0.8243 +8740,209851.0326227,0,0.71243 +8741,209181.8059197,0,0.8524 +8742,207806.4296612,0,0.94555 +8743,201751.0818386,0,0.93294 +8744,206740.2822931,0,0.87216 +8745,217263.2952776,0,0.79377 +8746,232521.6641051,0.042628,0.77555 +8747,241309.3030185,0.11524,0.85377 +8748,248804.6420917,0.13709,0.93809 +8749,251430.7799812,0.1166,0.95159 +8750,245786.1988935,0.10202,0.94428 +8751,240104.6949532,0.068199,0.92285 +8752,237321.6349401,0.022248,0.90364 +8753,245200.0486088,0,0.8829 +8754,261053.7984342,0,0.86574 +8755,256599.979342,0,0.83926 +8756,243169.2917171,0,0.80387 +8757,228880.1477697,0,0.7667 +8758,226554.0080573,0,0.7523 +8759,236343.1793469,0,0.73739 +8760,235840.1054805,0,0.70744 diff --git a/examples/tuple_as_labels/tuple_as_label.py b/examples/tuple_as_labels/tuple_as_label.py new file mode 100644 index 000000000..a497f4817 --- /dev/null +++ b/examples/tuple_as_labels/tuple_as_label.py @@ -0,0 +1,358 @@ +# -*- coding: utf-8 -*- + +""" +General description +------------------- + +You should have understood the basic_example to understand this one. + +This is an example to show how the label attribute can be used with tuples to +manage the results of large energy system. Even though, the feature is +introduced in a small example it is made for large system. + +In small energy system you normally address the node, you want your results +from, directly. In large systems you may want to group your results and collect +all power plants of a specific region or pv feed-in of all regions. + +Therefore you can use named tuples as label. In a named tuple you need to +specify the fields: + +>>> label = namedtuple('solph_label', ['region', 'tag1', 'tag2']) + +>>> pv_label = label('region_1', 'renewable_source', 'pv') +>>> pp_gas_label = label('region_2', 'power_plant', 'natural_gas') +>>> demand_label = label('region_3', 'electricity', 'demand') + +You always have to address all fields but you can use empty strings or None as +place holders. + +>>> elec_bus = label('region_4', 'electricity', '') +>>> print(elec_bus) +solph_label(region='region_4', tag1='electricity', tag2='') + +>>> elec_bus = label('region_4', 'electricity', None) +>>> print(elec_bus) +solph_label(region='region_4', tag1='electricity', tag2=None) + +Now you can filter the results using the label or the instance: + +>>> for key, value in results.items(): # Loop results (keys are tuples!) +... if isinstance(key[0], comp.Sink) & (key[0].label.tag2 == 'demand'): +... print("elec demand {0}: {1}".format(key[0].label.region, +... value['sequences'].sum())) + +elec demand region_1: 3456 +elec demand region_2: 2467 +... + +In the example below a subclass is created to define ones own string output. +By default the output of a namedtuple is `field1=value1, field2=value2,...`: + +>>> print(str(pv_label)) +solph_label(region='region_1', tag1='renewable_source', tag2='pv') + +With the subclass we created below the output is different, because we defined +our own string representation: + +>>> new_pv_label = Label('region_1', 'renewable_source', 'pv') +>>> print(str(new_pv_label)) +region_1_renewable_source_pv + +You still will be able to get the original string using `repr`: + +>>> print(repr(new_pv_label)) +Label(tag1='region_1', tag2='renewable_source', tag3='pv') + +This a helpful adaption for automatic plots etc.. + +Afterwards you can use `format` to define your own custom string.: +>>> print('{0}+{1}-{2}'.format(pv_label.region, pv_label.tag2, pv_label.tag1)) +region_1+pv-renewable_source + +Data +---- +basic_example.csv + + +Installation requirements +------------------------- + +This example requires oemof.solph (v0.5.x), install by: + + pip install oemof.solph[examples] + +""" + +# **************************************************************************** +# ********** PART 1 - Define and optimise the energy system ****************** +# **************************************************************************** + +import logging +import os +import warnings +from collections import namedtuple + +import pandas as pd +from oemof.network import graph +from oemof.tools import logger + +from oemof.solph import EnergySystem +from oemof.solph import Model +from oemof.solph import buses +from oemof.solph import components as comp +from oemof.solph import create_time_index +from oemof.solph import flows +from oemof.solph import helpers +from oemof.solph import processing + + +# Subclass of the named tuple with its own __str__ method. +# You can add as many tags as you like +# For tag1, tag2 you can define your own fields like region, fuel, type... +class Label(namedtuple("solph_label", ["tag1", "tag2", "tag3"])): + __slots__ = () + + def __str__(self): + """The string is used within solph as an ID, so it hast to be unique""" + return "_".join(map(str, self._asdict().values())) + + +# Read data file +filename = os.path.join(os.getcwd(), "tuple_as_label.csv") +try: + data = pd.read_csv(filename) +except FileNotFoundError: + msg = "Data file not found: {0}. Only one value used!" + warnings.warn(msg.format(filename), ResourceWarning) + data = pd.DataFrame({"pv": [0.3], "wind": [0.6], "demand_el": [500]}) + +solver = "cbc" # 'glpk', 'gurobi',.... +debug = False # Set number_of_timesteps to 3 to get a readable lp-file. +number_of_time_steps = len(data) +solver_verbose = False # show/hide solver output + +# initiate the logger (see the API docs for more information) +logger.define_logging( + logfile="oemof_example.log", + screen_level=logging.INFO, + file_level=logging.WARNING, +) + +logging.info("Initialize the energy system") +energysystem = EnergySystem( + timeindex=create_time_index(2012, number=number_of_time_steps), + infer_last_interval=False, +) + + + +########################################################################## +# Create oemof object +########################################################################## + +logging.info("Create oemof objects") + +# The bus objects were assigned to variables which makes it easier to connect +# components to these buses (see below). + +# create natural gas bus +bgas = buses.Bus(label=Label("bus", "gas", None)) + +# create electricity bus +bel = buses.Bus(label=Label("bus", "electricity", None)) + +# adding the buses to the energy system +energysystem.add(bgas, bel) + +# create excess component for the electricity bus to allow overproduction +energysystem.add( + comp.Sink( + label=Label("sink", "electricity", "excess"), + inputs={bel: flows.Flow()}, + ) +) + +# create source object representing the natural gas commodity (annual limit) +energysystem.add( + comp.Source( + label=Label("commodity_source", "gas", "commodity"), + outputs={ + bgas: flows.Flow() + }, + ) +) + +# create fixed source object representing wind pow er plants +energysystem.add( + comp.Source( + label=Label("ee_source", "electricity", "wind"), + outputs={bel: flows.Flow(fix=data["wind"], nominal_value=2000)}, + ) +) + +# create fixed source object representing pv power plants +energysystem.add( + comp.Source( + label=Label("ee_source", "electricity", "pv"), + outputs={bel: flows.Flow(fix=data["pv"], nominal_value=3000)}, + ) +) + +# create simple sink object representing the electrical demand +energysystem.add( + comp.Sink( + label=Label("sink", "electricity", "demand"), + inputs={bel: flows.Flow(fix=data["demand_el"]/1000, nominal_value=1)}, + ) +) + +# create simple transformer object representing a gas power plant +energysystem.add( + comp.Transformer( + label=Label("power plant", "electricity", "gas"), + inputs={bgas: flows.Flow()}, + outputs={bel: flows.Flow(nominal_value=10000, variable_costs=50)}, + conversion_factors={bel: 0.58}, + ) +) + +# create storage object representing a battery +nominal_storage_capacity = 5000 +storage = comp.GenericStorage( + nominal_storage_capacity=nominal_storage_capacity, + label=Label("storage", "electricity", "battery"), + inputs={bel: flows.Flow(nominal_value=nominal_storage_capacity/6)}, + outputs={ + bel: flows.Flow(nominal_value=nominal_storage_capacity/6) + }, + loss_rate=0.00, + initial_storage_level=None, + inflow_conversion_factor=1, + outflow_conversion_factor=0.8, +) + +energysystem.add(storage) + +########################################################################## +# Optimise the energy system and plot the results +########################################################################## + +logging.info("Optimise the energy system") + +# initialise the operational model +model = Model(energysystem) + +# This is for debugging only. It is not(!) necessary to solve the problem and +# should be set to False to save time and disc space in normal use. For +# debugging the timesteps should be set to 3, to increase the readability of +# the lp-file. +if debug: + filename = os.path.join( + helpers.extend_basic_path("lp_files"), "basic_example.lp" + ) + logging.info("Store lp-file in {0}.".format(filename)) + model.write(filename, io_options={"symbolic_solver_labels": True}) + +# if tee_switch is true solver messages will be displayed +logging.info("Solve the optimization problem") +model.receive_duals() +model.solve(solver=solver, solve_kwargs={"tee": solver_verbose}) + +logging.info("Store the energy system with the results.") + +# The processing module of the outputlib can be used to extract the results +# from the model transfer them into a homogeneous structured dictionary. + +# add results to the energy system to make it possible to store them. +energysystem.results["main"] = processing.results(model) +energysystem.results["meta"] = processing.meta_results(model) +graph.create_nx_graph(energysystem, filename="/home/uwe/tuple_example.graphml") +# The default path is the '.oemof' folder in your $HOME directory. +# The default filename is 'es_dump.oemof'. +# You can omit the attributes (as None is the default value) for testing cases. +# You should use unique names/folders for valuable results to avoid +# overwriting. + +# store energy system with results +energysystem.dump(dpath=None, filename=None) + +# **************************************************************************** +# ********** PART 2 - Processing the results ********************************* +# **************************************************************************** + +logging.info("**** The script can be divided into two parts here.") +logging.info("Restore the energy system and the results.") +energysystem = EnergySystem() +energysystem.restore(dpath=None, filename=None) + +# define an alias for shorter calls below (optional) +results = energysystem.results["main"] +ebus = energysystem.groups["bus_electricity_None"] +battery = energysystem.groups["storage_electricity_battery"] + + +# ****** Create a table with all sequences and store it into a file (csv/xlsx) +flows_to_bus = pd.DataFrame( + { + str(k[0].label): v["sequences"]["flow"] + for k, v in results.items() + if k[1] is not None and k[1] == ebus + } +) +flows_from_bus = pd.DataFrame( + { + str(k[1].label): v["sequences"]["flow"] + for k, v in results.items() + if k[1] is not None and k[0] == ebus + } +) + +storage = pd.DataFrame( + { + str(k[0].label): v["sequences"]["storage_content"] + for k, v in results.items() + if k[1] is None and k[0] == battery + } +) + +duals = pd.DataFrame( + { + str(k[0].label): v["sequences"]["duals"] + for k, v in results.items() + if k[1] is None and isinstance(k[0], buses.Bus) + } +) + +my_flows = pd.concat( + [flows_to_bus, flows_from_bus, storage], + keys=["to_bus", "from_bus", "content", "duals"], + axis=1, +) + +# Store the table to csv or excel file: +home_path = os.path.expanduser("~") +my_flows.to_csv(os.path.join(home_path, "my_flows.csv")) +# my_flows.to_excel(os.path.join(home_path, "my_flows.xlsx")) +print(my_flows.sum()) + +# ********* Use your tuple labels to filter the components +ee_sources = [ + str(f[0].label) for f in results.keys() if f[0].label.tag1 == "ee_source" +] +print(ee_sources) + +# It is possible to filter components by the label tags and the class, so the +# label concepts is a result of the postprocessing. If it is necessary to get +# all components of a region, "region" should be a field of the label. +# To filter only by tags you can add a tag named "class" with the name of the +# class as value. +electricity_buses = list( + set( + [ + str(f[0].label) + for f in results.keys() + if f[0].label.tag2 == "electricity" and isinstance(f[0], buses.Bus) + ] + ) +) +print(electricity_buses) From 423e60c9a1d3955a860eed23ef1e87e3e705037c Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 23 May 2022 14:49:40 +0200 Subject: [PATCH 0253/1363] Add examples to docs --- docs/conf.py | 6 +++++ docs/examples/index.rst | 9 ++++++++ docs/examples/solph.examples.rst | 39 ++++++++++++++++++++++++++++++++ docs/index.rst | 3 ++- docs/reference/oemof.solph.rst | 2 +- docs/requirements.txt | 1 + 6 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 docs/examples/index.rst create mode 100644 docs/examples/solph.examples.rst diff --git a/docs/conf.py b/docs/conf.py index c8091996f..701d8a454 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,8 +1,14 @@ # -*- coding: utf-8 -*- import os +import sys from sphinx.ext.autodoc import between +import matplotlib + + +matplotlib.use('agg') +sys.path.append(os.path.join(os.path.dirname(__file__), '..', "examples")) def setup(app): diff --git a/docs/examples/index.rst b/docs/examples/index.rst new file mode 100644 index 000000000..e64a5dbb4 --- /dev/null +++ b/docs/examples/index.rst @@ -0,0 +1,9 @@ +.. _examples_label: + +Examples +======== + +.. toctree:: + :glob: + + solph.examples* diff --git a/docs/examples/solph.examples.rst b/docs/examples/solph.examples.rst new file mode 100644 index 000000000..edcd14574 --- /dev/null +++ b/docs/examples/solph.examples.rst @@ -0,0 +1,39 @@ +Basic example ++++++++++++++ + +.. automodule:: basic_example.basic_example + :members: + :undoc-members: + :show-inheritance: + +Tuple as label example +++++++++++++++++++++++ + +.. automodule:: tuple_as_labels.tuple_as_label + :members: + :undoc-members: + :show-inheritance: + +Activity costs +============== + +.. automodule:: activity_costs.activity_costs + :members: + :undoc-members: + :show-inheritance: + +Time Index Example +++++++++++++++++++ + +.. automodule:: time_index_example.time_index_example + :members: + :undoc-members: + :show-inheritance: + +Gradient example +++++++++++++++++ + +.. automodule:: gradient_example.gradient_example + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/index.rst b/docs/index.rst index 0d67b5569..5425d0a54 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -8,11 +8,12 @@ Welcome to oemof's documentation! ================================= .. toctree:: - :maxdepth: 2 + :maxdepth: 3 readme usage reference/index + examples/index contributing authors changelog diff --git a/docs/reference/oemof.solph.rst b/docs/reference/oemof.solph.rst index c7bbf159a..2212edf72 100644 --- a/docs/reference/oemof.solph.rst +++ b/docs/reference/oemof.solph.rst @@ -13,7 +13,7 @@ oemof.solph.EnergySystem :show-inheritance: oemof.solph.buses.Bus ---------------- +--------------------- .. automodule:: oemof.solph.network.bus :members: diff --git a/docs/requirements.txt b/docs/requirements.txt index 7077cf6dd..5b3cc1fff 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,4 @@ sphinx>=1.3 sphinx-rtd-theme setuptools<58.3.0 +matplotlib From 5fdd4f90846543c9c248f32fcdf52d0a3ca1effe Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 23 May 2022 14:53:07 +0200 Subject: [PATCH 0254/1363] Fix underline of headlines --- docs/examples/solph.examples.rst | 10 +++++----- examples/activity_costs/activity_costs.py | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/examples/solph.examples.rst b/docs/examples/solph.examples.rst index edcd14574..49e9c0e9b 100644 --- a/docs/examples/solph.examples.rst +++ b/docs/examples/solph.examples.rst @@ -1,5 +1,5 @@ Basic example -+++++++++++++ +------------- .. automodule:: basic_example.basic_example :members: @@ -7,7 +7,7 @@ Basic example :show-inheritance: Tuple as label example -++++++++++++++++++++++ +---------------------- .. automodule:: tuple_as_labels.tuple_as_label :members: @@ -15,7 +15,7 @@ Tuple as label example :show-inheritance: Activity costs -============== +-------------- .. automodule:: activity_costs.activity_costs :members: @@ -23,7 +23,7 @@ Activity costs :show-inheritance: Time Index Example -++++++++++++++++++ +------------------ .. automodule:: time_index_example.time_index_example :members: @@ -31,7 +31,7 @@ Time Index Example :show-inheritance: Gradient example -++++++++++++++++ +---------------- .. automodule:: gradient_example.gradient_example :members: diff --git a/examples/activity_costs/activity_costs.py b/examples/activity_costs/activity_costs.py index 1548cc199..3813c9abb 100644 --- a/examples/activity_costs/activity_costs.py +++ b/examples/activity_costs/activity_costs.py @@ -3,6 +3,7 @@ """ General description ------------------- + This example illustrates the effect of activity_costs. There are the following components: From abd15542beb6d1231c4bc54f0885d703793f1cea Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 23 May 2022 15:08:22 +0200 Subject: [PATCH 0255/1363] Add license, black style and isort style --- examples/activity_costs/activity_costs.py | 44 +++++++++++-------- examples/basic_example/basic_example.py | 9 ++-- examples/gradient_example/gradient_example.py | 15 +++++-- .../time_index_example/time_index_example.py | 9 +++- examples/tuple_as_labels/tuple_as_label.py | 21 ++++----- 5 files changed, 57 insertions(+), 41 deletions(-) diff --git a/examples/activity_costs/activity_costs.py b/examples/activity_costs/activity_costs.py index 3813c9abb..8a2fdcbc3 100644 --- a/examples/activity_costs/activity_costs.py +++ b/examples/activity_costs/activity_costs.py @@ -19,11 +19,13 @@ Installation requirements ------------------------- - This example requires oemof.solph (v0.5.x), install by: pip install oemof.solph[examples] +License +------- +SPDX-License-Identifier: MIT """ import matplotlib.pyplot as plt @@ -32,13 +34,12 @@ from oemof import solph - ########################################################################## # Calculate parameters and initialize the energy system and ########################################################################## periods = 24 -time = pd.date_range('1/1/2018', periods=periods, freq='H') +time = pd.date_range("1/1/2018", periods=periods, freq="H") demand_heat = np.full(periods, 5) demand_heat[:4] = 0 @@ -49,25 +50,30 @@ es = solph.EnergySystem(timeindex=time) -b_heat = solph.Bus(label='b_heat') +b_heat = solph.Bus(label="b_heat") es.add(b_heat) sink_heat = solph.components.Sink( - label='demand', - inputs={b_heat: solph.Flow(fix=demand_heat, nominal_value=1)}) + label="demand", + inputs={b_heat: solph.Flow(fix=demand_heat, nominal_value=1)}, +) fireplace = solph.components.Source( - label='fireplace', - outputs={b_heat: solph.Flow(nominal_value=3, - variable_costs=0, - nonconvex=solph.NonConvex( - activity_costs=activity_costs))}) + label="fireplace", + outputs={ + b_heat: solph.Flow( + nominal_value=3, + variable_costs=0, + nonconvex=solph.NonConvex(activity_costs=activity_costs), + ) + }, +) boiler = solph.components.Source( - label='boiler', - outputs={b_heat: solph.Flow(nominal_value=10, - variable_costs=1)}) + label="boiler", + outputs={b_heat: solph.Flow(nominal_value=10, variable_costs=1)}, +) es.add(sink_heat, fireplace, boiler) @@ -79,7 +85,7 @@ om = solph.Model(es) # solve model -om.solve(solver='cbc', solve_kwargs={'tee': True}) +om.solve(solver="cbc", solve_kwargs={"tee": True}) ########################################################################## # Check and plot the results @@ -88,8 +94,8 @@ results = solph.processing.results(om) # plot data -data = solph.views.node(results, 'b_heat')['sequences'] -ax = data.plot(kind='line', drawstyle='steps-post', grid=True, rot=0) -ax.set_xlabel('Time') -ax.set_ylabel('Heat (arb. units)') +data = solph.views.node(results, "b_heat")["sequences"] +ax = data.plot(kind="line", drawstyle="steps-post", grid=True, rot=0) +ax.set_xlabel("Time") +ax.set_ylabel("Heat (arb. units)") plt.show() diff --git a/examples/basic_example/basic_example.py b/examples/basic_example/basic_example.py index 95c2e2dcc..3448b2d83 100644 --- a/examples/basic_example/basic_example.py +++ b/examples/basic_example/basic_example.py @@ -36,16 +36,15 @@ Installation requirements ------------------------- - This example requires oemof.solph (v0.5.x), install by: pip install oemof.solph[examples] +License +------- +SPDX-License-Identifier: MIT """ -__copyright__ = "oemof developer group" -__license__ = "GPLv3" - # **************************************************************************** # ********** PART 1 - Define and optimise the energy system ****************** # **************************************************************************** @@ -71,7 +70,6 @@ from oemof.solph import processing from oemof.solph import views - # Read data file filename = os.path.join(os.getcwd(), "basic_example.csv") try: @@ -245,7 +243,6 @@ print("") - # get all variables of a specific component/bus custom_storage = views.node(results, "storage") electricity_bus = views.node(results, "electricity") diff --git a/examples/gradient_example/gradient_example.py b/examples/gradient_example/gradient_example.py index cb5a6bd13..f885ebc41 100644 --- a/examples/gradient_example/gradient_example.py +++ b/examples/gradient_example/gradient_example.py @@ -11,18 +11,25 @@ Installation requirements ------------------------- - This example requires oemof.solph (v0.5.x), install by: pip install oemof.solph[examples] + + +License +------- +SPDX-License-Identifier: MIT """ import matplotlib.pyplot as plt import pandas as pd -from oemof.solph import EnergySystem, Model, buses -from oemof.solph import components as cmp -from oemof.solph import flows, processing +from oemof.solph import EnergySystem +from oemof.solph import Model +from oemof.solph import buses +from oemof.solph import components as cmp +from oemof.solph import flows +from oemof.solph import processing # The gradient for the output of the natural gas power plant. # Change the gradient between 0.1 and 0.0001 and check the results. The diff --git a/examples/time_index_example/time_index_example.py b/examples/time_index_example/time_index_example.py index 051c79543..674eb980e 100644 --- a/examples/time_index_example/time_index_example.py +++ b/examples/time_index_example/time_index_example.py @@ -20,14 +20,19 @@ Installation requirements ------------------------- - This example requires oemof.solph (v0.5.x), install by: pip install oemof.solph[examples] + + +License +------- +SPDX-License-Identifier: MIT """ -from oemof import solph import matplotlib.pyplot as plt +from oemof import solph + solver = "cbc" # 'glpk', 'gurobi',... solver_verbose = False # show/hide solver output diff --git a/examples/tuple_as_labels/tuple_as_label.py b/examples/tuple_as_labels/tuple_as_label.py index a497f4817..d7c890c61 100644 --- a/examples/tuple_as_labels/tuple_as_label.py +++ b/examples/tuple_as_labels/tuple_as_label.py @@ -76,11 +76,15 @@ Installation requirements ------------------------- - This example requires oemof.solph (v0.5.x), install by: pip install oemof.solph[examples] + +License +------- +SPDX-License-Identifier: MIT + """ # **************************************************************************** @@ -145,7 +149,6 @@ def __str__(self): ) - ########################################################################## # Create oemof object ########################################################################## @@ -176,9 +179,7 @@ def __str__(self): energysystem.add( comp.Source( label=Label("commodity_source", "gas", "commodity"), - outputs={ - bgas: flows.Flow() - }, + outputs={bgas: flows.Flow()}, ) ) @@ -202,7 +203,9 @@ def __str__(self): energysystem.add( comp.Sink( label=Label("sink", "electricity", "demand"), - inputs={bel: flows.Flow(fix=data["demand_el"]/1000, nominal_value=1)}, + inputs={ + bel: flows.Flow(fix=data["demand_el"] / 1000, nominal_value=1) + }, ) ) @@ -221,10 +224,8 @@ def __str__(self): storage = comp.GenericStorage( nominal_storage_capacity=nominal_storage_capacity, label=Label("storage", "electricity", "battery"), - inputs={bel: flows.Flow(nominal_value=nominal_storage_capacity/6)}, - outputs={ - bel: flows.Flow(nominal_value=nominal_storage_capacity/6) - }, + inputs={bel: flows.Flow(nominal_value=nominal_storage_capacity / 6)}, + outputs={bel: flows.Flow(nominal_value=nominal_storage_capacity / 6)}, loss_rate=0.00, initial_storage_level=None, inflow_conversion_factor=1, From a9083dea19db8821a427abbb4f5f12b2eda916c4 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 23 May 2022 15:25:18 +0200 Subject: [PATCH 0256/1363] Add MIT license with reference --- LICENSE | 2 +- examples/activity_costs/activity_costs.py | 3 ++- examples/basic_example/basic_example.py | 2 +- examples/gradient_example/gradient_example.py | 2 +- examples/time_index_example/time_index_example.py | 2 +- examples/tuple_as_labels/tuple_as_label.py | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/LICENSE b/LICENSE index c700b3123..00db70e6d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2014-2020, oemof developer group +Copyright (c) 2014-2022, oemof developer group Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/examples/activity_costs/activity_costs.py b/examples/activity_costs/activity_costs.py index 8a2fdcbc3..0af16416c 100644 --- a/examples/activity_costs/activity_costs.py +++ b/examples/activity_costs/activity_costs.py @@ -25,7 +25,8 @@ License ------- -SPDX-License-Identifier: MIT +`MIT license `_ + """ import matplotlib.pyplot as plt diff --git a/examples/basic_example/basic_example.py b/examples/basic_example/basic_example.py index 3448b2d83..545f8b310 100644 --- a/examples/basic_example/basic_example.py +++ b/examples/basic_example/basic_example.py @@ -42,7 +42,7 @@ License ------- -SPDX-License-Identifier: MIT +`MIT license `_ """ # **************************************************************************** diff --git a/examples/gradient_example/gradient_example.py b/examples/gradient_example/gradient_example.py index f885ebc41..e2686b192 100644 --- a/examples/gradient_example/gradient_example.py +++ b/examples/gradient_example/gradient_example.py @@ -18,7 +18,7 @@ License ------- -SPDX-License-Identifier: MIT +`MIT license `_ """ import matplotlib.pyplot as plt diff --git a/examples/time_index_example/time_index_example.py b/examples/time_index_example/time_index_example.py index 674eb980e..4effe9403 100644 --- a/examples/time_index_example/time_index_example.py +++ b/examples/time_index_example/time_index_example.py @@ -27,7 +27,7 @@ License ------- -SPDX-License-Identifier: MIT +`MIT license `_ """ import matplotlib.pyplot as plt diff --git a/examples/tuple_as_labels/tuple_as_label.py b/examples/tuple_as_labels/tuple_as_label.py index d7c890c61..8384617e0 100644 --- a/examples/tuple_as_labels/tuple_as_label.py +++ b/examples/tuple_as_labels/tuple_as_label.py @@ -83,7 +83,7 @@ License ------- -SPDX-License-Identifier: MIT +`MIT license `_ """ From 6210d51d8b47853afab741214cdddc9eaa385a01 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 23 May 2022 17:26:08 +0200 Subject: [PATCH 0257/1363] Revise examples --- examples/basic_example/basic_example.py | 56 +++++++++--------- examples/gradient_example/gradient_example.py | 58 ++++++++++--------- examples/tuple_as_labels/tuple_as_label.py | 2 +- 3 files changed, 58 insertions(+), 58 deletions(-) diff --git a/examples/basic_example/basic_example.py b/examples/basic_example/basic_example.py index 545f8b310..cf86cd023 100644 --- a/examples/basic_example/basic_example.py +++ b/examples/basic_example/basic_example.py @@ -66,7 +66,9 @@ from oemof.solph import Model from oemof.solph import buses from oemof.solph import components as cmp +from oemof.solph import create_time_index from oemof.solph import flows +from oemof.solph import helpers from oemof.solph import processing from oemof.solph import views @@ -76,7 +78,7 @@ data = pd.read_csv(filename) except FileNotFoundError: msg = "Data file not found: {0}. Only one value used!" - warnings.warn(msg.format(filename), ResourceWarning) + warnings.warn(msg.format(filename), UserWarning) data = pd.DataFrame({"pv": [0.3], "wind": [0.6], "demand_el": [500]}) solver = "cbc" # 'glpk', 'gurobi',.... @@ -92,11 +94,11 @@ ) logging.info("Initialize the energy system") -date_time_index = pd.date_range( - "1/1/2012", periods=number_of_time_steps, freq="H" -) +date_time_index = create_time_index(2012, number=number_of_time_steps) -energysystem = EnergySystem(timeindex=date_time_index) +energysystem = EnergySystem( + timeindex=date_time_index, infer_last_interval=False +) ########################################################################## # Create oemof object @@ -123,7 +125,7 @@ energysystem.add( cmp.Source( label="rgas", - outputs={bgas: flows.Flow(summed_max=1)}, + outputs={bgas: flows.Flow()}, ) ) @@ -192,7 +194,7 @@ # the lp-file. if debug: filename = os.path.join( - solph.helpers.extend_basic_path("lp_files"), "basic_example.lp" + helpers.extend_basic_path("lp_files"), "basic_example.lp" ) logging.info("Store lp-file in {0}.".format(filename)) model.write(filename, io_options={"symbolic_solver_labels": True}) @@ -248,29 +250,25 @@ electricity_bus = views.node(results, "electricity") # plot the time series (sequences) of a specific component/bus -if plt is not None: - fig, ax = plt.subplots(figsize=(10, 5)) - custom_storage["sequences"].plot( - ax=ax, kind="line", drawstyle="steps-post" - ) - plt.legend( - loc="upper center", - prop={"size": 8}, - bbox_to_anchor=(0.5, 1.25), - ncol=2, - ) - fig.subplots_adjust(top=0.8) - plt.show() - fig, ax = plt.subplots(figsize=(10, 5)) - electricity_bus["sequences"].plot( - ax=ax, kind="line", drawstyle="steps-post" - ) - plt.legend( - loc="upper center", prop={"size": 8}, bbox_to_anchor=(0.5, 1.3), ncol=2 - ) - fig.subplots_adjust(top=0.8) - plt.show() +fig, ax = plt.subplots(figsize=(10, 5)) +custom_storage["sequences"].plot(ax=ax, kind="line", drawstyle="steps-post") +plt.legend( + loc="upper center", + prop={"size": 8}, + bbox_to_anchor=(0.5, 1.25), + ncol=2, +) +fig.subplots_adjust(top=0.8) +plt.show() + +fig, ax = plt.subplots(figsize=(10, 5)) +electricity_bus["sequences"].plot(ax=ax, kind="line", drawstyle="steps-post") +plt.legend( + loc="upper center", prop={"size": 8}, bbox_to_anchor=(0.5, 1.3), ncol=2 +) +fig.subplots_adjust(top=0.8) +plt.show() # print the solver results print("********* Meta results *********") diff --git a/examples/gradient_example/gradient_example.py b/examples/gradient_example/gradient_example.py index e2686b192..6ac78cd88 100644 --- a/examples/gradient_example/gradient_example.py +++ b/examples/gradient_example/gradient_example.py @@ -34,7 +34,7 @@ # The gradient for the output of the natural gas power plant. # Change the gradient between 0.1 and 0.0001 and check the results. The # more flexible the power plant can be run the less the storage will be used. -GRADIENT = 0.001 +GRADIENT = 0.01 date_time_index = pd.date_range("1/1/2012", periods=48, freq="H") print(date_time_index) @@ -116,34 +116,36 @@ # processing the results results = processing.results(model) -print("C", results[(storage, None)]["sequences"]) -print("f", results[(storage, bel)]["sequences"]) -ax = results[(storage, None)]["sequences"].diff().plot() -ax = results[(storage, bel)]["sequences"].mul(-1).plot(ax=ax) -ax = results[(storage, None)]["sequences"].plot(ax=ax) -results[(bel, storage)]["sequences"].plot(ax=ax) -plt.show() -exit(0) -# get all variables of a specific component/bus -custom_storage = solph.views.node(results, "storage") -electricity_bus = solph.views.node(results, "electricity") - -# plotting -fig, ax = plt.subplots(figsize=(10, 5)) -custom_storage["sequences"].plot(ax=ax, kind="line", drawstyle="steps-post") -plt.legend( - loc="upper center", - prop={"size": 8}, - bbox_to_anchor=(0.5, 1.25), - ncol=2, +# ****** Create a table with all sequences and store it into a file (csv/xlsx) +flows_to_bus = pd.DataFrame( + { + str(k[0].label): v["sequences"]["flow"] + for k, v in results.items() + if k[1] is not None and k[1] == bel + } +) +flows_from_bus = pd.DataFrame( + { + str(k[1].label): v["sequences"]["flow"] + for k, v in results.items() + if k[1] is not None and k[0] == bel + } ) -fig.subplots_adjust(top=0.8) -plt.show() -fig, ax = plt.subplots(figsize=(10, 5)) -electricity_bus["sequences"].plot(ax=ax, kind="line", drawstyle="steps-post") -plt.legend( - loc="upper center", prop={"size": 8}, bbox_to_anchor=(0.5, 1.3), ncol=2 +storage = pd.DataFrame( + { + str(k[0].label): v["sequences"]["storage_content"] + for k, v in results.items() + if k[1] is None and k[0] == storage + } ) -fig.subplots_adjust(top=0.8) + +my_flows = pd.concat( + [flows_to_bus, flows_from_bus, storage], + keys=["to_bus", "from_bus", "content", "duals"], + axis=1, +) + +print(my_flows) +my_flows.plot() plt.show() diff --git a/examples/tuple_as_labels/tuple_as_label.py b/examples/tuple_as_labels/tuple_as_label.py index 8384617e0..2c890f390 100644 --- a/examples/tuple_as_labels/tuple_as_label.py +++ b/examples/tuple_as_labels/tuple_as_label.py @@ -127,7 +127,7 @@ def __str__(self): data = pd.read_csv(filename) except FileNotFoundError: msg = "Data file not found: {0}. Only one value used!" - warnings.warn(msg.format(filename), ResourceWarning) + warnings.warn(msg.format(filename), UserWarning) data = pd.DataFrame({"pv": [0.3], "wind": [0.6], "demand_el": [500]}) solver = "cbc" # 'glpk', 'gurobi',.... From 06f0407523f78d6de53645a3a0057caaf189b518 Mon Sep 17 00:00:00 2001 From: uvchik Date: Mon, 23 May 2022 17:26:20 +0200 Subject: [PATCH 0258/1363] Add more examples --- examples/check_examples.py | 107 + .../emission_constraint.py | 107 + examples/excel_reader/dispatch.py | 416 + examples/excel_reader/scenario.xlsx | Bin 0 -> 577434 bytes examples/flow_count_limit/flow_count_limit.py | 125 + examples/min_max_runtimes/min_max_runtimes.py | 100 + examples/simple_dispatch/input_data.csv | 8761 +++++++++++++++++ examples/simple_dispatch/simple_dispatch.py | 247 + .../storage_balanced_unbalanced/storage.py | 162 + .../storage_investment/storage_investment.csv | 8761 +++++++++++++++++ .../v1_invest_optimize_all_technologies.py | 220 + ...v2_invest_optimize_only_gas_and_storage.py | 206 + ...optimize_only_storage_with_fossil_share.py | 218 + ...mize_all_technologies_with_fossil_share.py | 231 + examples/variable_chp/variable_chp.csv | 193 + examples/variable_chp/variable_chp.py | 428 + 16 files changed, 20282 insertions(+) create mode 100644 examples/check_examples.py create mode 100644 examples/emission_constraint/emission_constraint.py create mode 100644 examples/excel_reader/dispatch.py create mode 100755 examples/excel_reader/scenario.xlsx create mode 100644 examples/flow_count_limit/flow_count_limit.py create mode 100644 examples/min_max_runtimes/min_max_runtimes.py create mode 100644 examples/simple_dispatch/input_data.csv create mode 100644 examples/simple_dispatch/simple_dispatch.py create mode 100644 examples/storage_balanced_unbalanced/storage.py create mode 100644 examples/storage_investment/storage_investment.csv create mode 100644 examples/storage_investment/v1_invest_optimize_all_technologies.py create mode 100644 examples/storage_investment/v2_invest_optimize_only_gas_and_storage.py create mode 100644 examples/storage_investment/v3_invest_optimize_only_storage_with_fossil_share.py create mode 100644 examples/storage_investment/v4_invest_optimize_all_technologies_with_fossil_share.py create mode 100644 examples/variable_chp/variable_chp.csv create mode 100644 examples/variable_chp/variable_chp.py diff --git a/examples/check_examples.py b/examples/check_examples.py new file mode 100644 index 000000000..798e94e88 --- /dev/null +++ b/examples/check_examples.py @@ -0,0 +1,107 @@ +import os +import subprocess +import tempfile +import warnings +from datetime import datetime + +import matplotlib +import nbformat +from matplotlib import pyplot as plt +from termcolor import colored + +warnings.filterwarnings("ignore", "", UserWarning) +matplotlib.use("Agg") + +stop_at_error = True # If True script will stop if error is raised +exclude_notebooks = False +exclude_python_scripts = False + + +def notebook_run(path): + """ + Execute a notebook via nbconvert and collect output. + Returns (parsed nb object, execution errors) + """ + dirname, __ = os.path.split(path) + os.chdir(dirname) + with tempfile.NamedTemporaryFile(suffix=".ipynb") as fout: + args = [ + "jupyter", + "nbconvert", + "--to", + "notebook", + "--execute", + "--ExecutePreprocessor.timeout=60", + "--output", + fout.name, + path, + ] + subprocess.check_call(args) + + fout.seek(0) + nb = nbformat.read(fout, nbformat.current_nbformat) + + errors = [ + output + for cell in nb.cells + if "outputs" in cell + for output in cell["outputs"] + if output.output_type == "error" + ] + + return nb, errors + + +fullpath = os.path.join(os.getcwd()) + +checker = {} +number = 0 + +start = datetime.now() + +for root, dirs, files in sorted(os.walk(fullpath)): + for name in sorted(files): + if ( + name[-3:] == ".py" + and not exclude_python_scripts + and not name == "check_examples.py" + ): + fn = os.path.join(root, name) + os.chdir(root) + number += 1 + if stop_at_error is True: + print(fn) + exec(open(fn).read()) + checker[name] = "okay" + else: + try: + exec(open(fn).read()) + checker[name] = "okay" + except: + checker[name] = "failed" + elif name[-6:] == ".ipynb" and not exclude_notebooks: + fn = os.path.join(root, name) + os.chdir(root) + number += 1 + if stop_at_error is True: + print(fn) + notebook_run(fn) + checker[name] = "okay" + else: + try: + notebook_run(fn) + checker[name] = "okay" + except: + checker[name] = "failed" + plt.close() + + +print("******* TEST RESULTS ***********************************") + +print("\n{0} examples tested in {1}.\n".format(number, datetime.now() - start)) + +for k, v in checker.items(): + if v == "failed": + print(k, colored(v, "red")) + else: + print(k, colored(v, "green")) diff --git a/examples/emission_constraint/emission_constraint.py b/examples/emission_constraint/emission_constraint.py new file mode 100644 index 000000000..89a53e9ee --- /dev/null +++ b/examples/emission_constraint/emission_constraint.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- + +""" +General description +------------------- +Example that shows how to add an emission constraint in a model. + +Installation requirements +------------------------- + +This example requires oemof.solph (v0.5.x), install by: + + pip install oemof.solph[examples] + + +License +------- +`MIT license `_ + +""" +import matplotlib.pyplot as plt +import pandas as pd + +from oemof import solph +from oemof.solph import constraints + +# create energy system +energysystem = solph.EnergySystem( + timeindex=pd.date_range("1/1/2012", periods=3, freq="H") +) + +# create gas bus +bgas = solph.Bus(label="gas") + +# create electricity bus +bel = solph.Bus(label="electricity") + +# adding the buses to the energy system +energysystem.add(bel, bgas) + +# create fixed source object representing biomass plants +energysystem.add( + solph.components.Source( + label="biomass", + outputs={ + bel: solph.Flow( + nominal_value=100, + variable_costs=10, + emission_factor=0.01, + fix=[0.1, 0.2, 0.3], + ) + }, + ) +) + +# create source object representing the gas commodity +energysystem.add( + solph.components.Source( + label="gas-source", + outputs={bgas: solph.Flow(variable_costs=10, emission_factor=0.2)}, + ) +) + +energysystem.add( + solph.components.Sink( + label="demand", + inputs={ + bel: solph.Flow( + nominal_value=200, variable_costs=10, fix=[0.1, 0.2, 0.3] + ) + }, + ) +) + +# create simple transformer object representing a gas power plant +energysystem.add( + solph.components.Transformer( + label="pp_gas", + inputs={bgas: solph.Flow()}, + outputs={bel: solph.Flow(nominal_value=200)}, + conversion_factors={bel: 0.58}, + ) +) + +# initialise the operational model +model = solph.Model(energysystem) + +# add the emission constraint +constraints.emission_limit(model, limit=100) + +# print out the emission constraint +model.integral_limit_emission_factor_constraint.pprint() +model.integral_limit_emission_factor.pprint() + +# solve the model +model.solve() + +# print out the amount of emissions from the emission constraint +print(model.integral_limit_emission_factor()) + +results = solph.processing.results(model) + +data = solph.views.node(results, "electricity")["sequences"] +ax = data.plot(kind="line", grid=True) +ax.set_xlabel("Time (h)") +ax.set_ylabel("P (MW)") +plt.show() diff --git a/examples/excel_reader/dispatch.py b/examples/excel_reader/dispatch.py new file mode 100644 index 000000000..745ef2117 --- /dev/null +++ b/examples/excel_reader/dispatch.py @@ -0,0 +1,416 @@ +# -*- coding: utf-8 -*- + +""" +General description +------------------- +As the csv-reader was removed with version 0.2 this example shows how to create +an excel-reader. The example is equivalent to the old csv-reader example. +Following the example one can customise the excel reader to ones own needs. + +The pandas package supports the '.xls' and the '.xlsx' format but one can +create read and adept the files with open source software such as libreoffice, +openoffice, gnumeric,... + +Data +---- +scenario.xlsx + +Installation requirements +------------------------- +This example requires oemof.solph (v0.5.x), install by: + + pip install oemof.solph[examples] + pip3 install openpyxl + pip3 install networkx + +If you want to plot the energy system's graph, you have to install pygraphviz +using: + + pip3 install pygraphviz + +For pygraphviz under Windows, some hints are available in the oemof Wiki: +https://github.com/oemof/oemof/wiki/Windows---general + +License +------- +`MIT license `_ + +Copyright (c) + +Uwe Krien +Jonathan Amme +""" + +import os +import logging +import pandas as pd + +from oemof.tools import logger +from oemof import solph + +from oemof.network.graph import create_nx_graph +from matplotlib import pyplot as plt +import networkx as nx + + +def nodes_from_excel(filename): + """Read node data from Excel sheet + + Parameters + ---------- + filename : :obj:`str` + Path to excel file + + Returns + ------- + :obj:`dict` + Imported nodes data + """ + + # does Excel file exist? + if not filename or not os.path.isfile(filename): + raise FileNotFoundError( + "Excel data file {} not found.".format(filename) + ) + + xls = pd.ExcelFile(filename) + + nodes_data = { + "buses": xls.parse("buses"), + "commodity_sources": xls.parse("commodity_sources"), + "transformers": xls.parse("transformers"), + "renewables": xls.parse("renewables"), + "demand": xls.parse("demand"), + "storages": xls.parse("storages"), + "powerlines": xls.parse("powerlines"), + "timeseries": xls.parse("time_series"), + } + + # set datetime index + nodes_data["timeseries"].set_index("timestamp", inplace=True) + nodes_data["timeseries"].index = pd.to_datetime( + nodes_data["timeseries"].index + ) + + print("Data from Excel file {} imported.".format(filename)) + + return nodes_data + + +def create_nodes(nd=None): + """Create nodes (oemof objects) from node dict + + Parameters + ---------- + nd : :obj:`dict` + Nodes data + + Returns + ------- + nodes : `obj`:dict of :class:`nodes ` + """ + + if not nd: + raise ValueError("No nodes data provided.") + + nodes = [] + + # Create Bus objects from buses table + busd = {} + + for i, b in nd["buses"].iterrows(): + if b["active"]: + bus = solph.Bus(label=b["label"]) + nodes.append(bus) + + busd[b["label"]] = bus + if b["excess"]: + nodes.append( + solph.components.Sink( + label=b["label"] + "_excess", + inputs={ + busd[b["label"]]: solph.Flow( + variable_costs=b["excess costs"] + ) + }, + ) + ) + if b["shortage"]: + nodes.append( + solph.components.Source( + label=b["label"] + "_shortage", + outputs={ + busd[b["label"]]: solph.Flow( + variable_costs=b["shortage costs"] + ) + }, + ) + ) + + # Create Source objects from table 'commodity sources' + for i, cs in nd["commodity_sources"].iterrows(): + if cs["active"]: + nodes.append( + solph.components.Source( + label=cs["label"], + outputs={ + busd[cs["to"]]: solph.Flow( + variable_costs=cs["variable costs"] + ) + }, + ) + ) + + # Create Source objects with fixed time series from 'renewables' table + for i, re in nd["renewables"].iterrows(): + if re["active"]: + # set static outflow values + outflow_args = {"nominal_value": re["capacity"]} + # get time series for node and parameter + for col in nd["timeseries"].columns.values: + if col.split(".")[0] == re["label"]: + outflow_args[col.split(".")[1]] = nd["timeseries"][col] + + # create + nodes.append( + solph.components.Source( + label=re["label"], + outputs={busd[re["to"]]: solph.Flow(**outflow_args)}, + ) + ) + + # Create Sink objects with fixed time series from 'demand' table + for i, de in nd["demand"].iterrows(): + if de["active"]: + # set static inflow values + inflow_args = {"nominal_value": de["nominal value"]} + # get time series for node and parameter + for col in nd["timeseries"].columns.values: + if col.split(".")[0] == de["label"]: + inflow_args[col.split(".")[1]] = nd["timeseries"][col] + + # create + nodes.append( + solph.components.Sink( + label=de["label"], + inputs={busd[de["from"]]: solph.Flow(**inflow_args)}, + ) + ) + + # Create Transformer objects from 'transformers' table + for i, t in nd["transformers"].iterrows(): + if t["active"]: + # set static inflow values + inflow_args = {"variable_costs": t["variable input costs"]} + # get time series for inflow of transformer + for col in nd["timeseries"].columns.values: + if col.split(".")[0] == t["label"]: + inflow_args[col.split(".")[1]] = nd["timeseries"][col] + # create + nodes.append( + solph.components.Transformer( + label=t["label"], + inputs={busd[t["from"]]: solph.Flow(**inflow_args)}, + outputs={ + busd[t["to"]]: solph.Flow(nominal_value=t["capacity"]) + }, + conversion_factors={busd[t["to"]]: t["efficiency"]}, + ) + ) + + for i, s in nd["storages"].iterrows(): + if s["active"]: + nodes.append( + solph.components.GenericStorage( + label=s["label"], + inputs={ + busd[s["bus"]]: solph.Flow( + nominal_value=s["capacity inflow"], + variable_costs=s["variable input costs"], + ) + }, + outputs={ + busd[s["bus"]]: solph.Flow( + nominal_value=s["capacity outflow"], + variable_costs=s["variable output costs"], + ) + }, + nominal_storage_capacity=s["nominal capacity"], + loss_rate=s["capacity loss"], + initial_storage_level=s["initial capacity"], + max_storage_level=s["capacity max"], + min_storage_level=s["capacity min"], + inflow_conversion_factor=s["efficiency inflow"], + outflow_conversion_factor=s["efficiency outflow"], + ) + ) + + for i, p in nd["powerlines"].iterrows(): + if p["active"]: + bus1 = busd[p["bus_1"]] + bus2 = busd[p["bus_2"]] + nodes.append( + solph.components.Transformer( + label="powerline" + "_" + p["bus_1"] + "_" + p["bus_2"], + inputs={bus1: solph.Flow()}, + outputs={bus2: solph.Flow()}, + conversion_factors={bus2: p["efficiency"]}, + )) + nodes.append( + solph.components.Transformer( + label="powerline" + "_" + p["bus_2"] + "_" + p["bus_1"], + inputs={bus2: solph.Flow()}, + outputs={bus1: solph.Flow()}, + conversion_factors={bus1: p["efficiency"]}, + ) + ) + + return nodes + + +def draw_graph( + grph, + edge_labels=True, + node_color="#AFAFAF", + edge_color="#CFCFCF", + plot=True, + node_size=2000, + with_labels=True, + arrows=True, + layout="neato", +): + """ + Parameters + ---------- + grph : networkxGraph + A graph to draw. + edge_labels : boolean + Use nominal values of flow as edge label + node_color : dict or string + Hex color code oder matplotlib color for each node. If string, all + colors are the same. + + edge_color : string + Hex color code oder matplotlib color for edge color. + + plot : boolean + Show matplotlib plot. + + node_size : integer + Size of nodes. + + with_labels : boolean + Draw node labels. + + arrows : boolean + Draw arrows on directed edges. Works only if an optimization_model has + been passed. + layout : string + networkx graph layout, one of: neato, dot, twopi, circo, fdp, sfdp. + """ + if type(node_color) is dict: + node_color = [node_color.get(g, "#AFAFAF") for g in grph.nodes()] + + # set drawing options + options = { + "prog": "dot", + "with_labels": with_labels, + "node_color": node_color, + "edge_color": edge_color, + "node_size": node_size, + "arrows": arrows, + } + + # try to use pygraphviz for graph layout + try: + import pygraphviz + + pos = nx.drawing.nx_agraph.graphviz_layout(grph, prog=layout) + except ImportError: + logging.error("Module pygraphviz not found, I won't plot the graph.") + return + + # draw graph + nx.draw(grph, pos=pos, **options) + + # add edge labels for all edges + if edge_labels is True and plt: + labels = nx.get_edge_attributes(grph, "weight") + nx.draw_networkx_edge_labels(grph, pos=pos, edge_labels=labels) + + # show output + if plot is True: + plt.show() + + +logger.define_logging() +datetime_index = pd.date_range( + "2016-01-01 00:00:00", "2016-01-01 23:00:00", freq="60min" +) + +# model creation and solving +logging.info("Starting optimization") + +# initialisation of the energy system +esys = solph.EnergySystem(timeindex=datetime_index, infer_last_interval=False) + +# read node data from Excel sheet +excel_nodes = nodes_from_excel( + os.path.join( + os.getcwd(), + "scenario.xlsx", + ) +) + +# create nodes from Excel sheet data +my_nodes = create_nodes(nd=excel_nodes) + +# add nodes and flows to energy system +esys.add(*my_nodes) + +print("*********************************************************") +print("The following objects have been created from excel sheet:") +for n in esys.nodes: + oobj = str(type(n)).replace("", "") + print(oobj + ":", n.label) +print("*********************************************************") + +# creation of a least cost model from the energy system +om = solph.Model(esys) +om.receive_duals() + +# solving the linear problem using the given solver +om.solve(solver="cbc") + +# create graph of esys +# You can use argument filename='/home/somebody/my_graph.graphml' +# to dump your graph to disc. You can open it using e.g. yEd or gephi +graph = create_nx_graph(esys) + +# plot esys graph +draw_graph( + grph=graph, + plot=True, + layout="neato", + node_size=1000, + node_color={"R1_bus_el": "#cd3333", "R2_bus_el": "#cd3333"}, +) + +# print and plot some results +results = solph.processing.results(om) + +region2 = solph.views.node(results, "R2_bus_el") +region1 = solph.views.node(results, "R1_bus_el") + +print(region2["sequences"].sum()) +print(region1["sequences"].sum()) + + +fig, ax = plt.subplots(figsize=(10, 5)) +region1["sequences"].plot(ax=ax) +ax.legend( + loc="upper center", prop={"size": 8}, bbox_to_anchor=(0.5, 1.4), ncol=3 +) +fig.subplots_adjust(top=0.7) +plt.show() +logging.info("Done!") diff --git a/examples/excel_reader/scenario.xlsx b/examples/excel_reader/scenario.xlsx new file mode 100755 index 0000000000000000000000000000000000000000..5cb89172fa3625c6d6049e0a202e07bceebf6d9e GIT binary patch literal 577434 zcmbSyW0Ymhwq@G3ZQHhO+qP9{+cqj)X*;vhnU%I}yR*LA-LLzNao>-7{_Hb$M2xle zT(RexD|Uo}G%yGX00aaCz=T-20>JEvSCZ4~g&tAaXP1 z62;dfxr_+T9#-Dj=sb81S3ZE(+%Bmn+A`cCrDp~6fYA7Nb-W&fYuSH| zUy(Gc1s^4s0An}ltP!0=i?aA)E%W1%ecjtoqjSVKOeitc=}Xx8V}35^Gk<2&Lm;NI z#2*5C6NB?3-&iofbovq%O3T{^dnLx@-unu_Aq#A zC}_Wcd6Rp&uESS65mc<_$bdh0c5w>D;lIE#RB!QAU?D8sJhh_SPf zGO5}gZ@A&s)~|ck+Kt~oWB(-1w#DM`)fz>xAJwsufqLg4Wz2v?x@c|Fj@0nV=q8Dy z+0+Oh#i2JYFpB@gs9jZO+7Ha3s;mDs#&ob3G+2LHZabO$SmiQvV|$iTZ^AWDKZw%a zG_L*eYqs2Ba~r++kIH_q(}6ClIv%FCn(Xh&DlkcRp`FRPOm$P3<`C1fNR)b=qF2|O z@|3SRU?w4R{aLrs_FyjHzuK!=1g(2}id*>1UdyL%%nK@-2-8u2C^{Qow_UP8Vwa{rOXJQ&yXnw<#%6Ei1A3;jIN7fXw`kDokLk zo|dXM5Ek5lC6GCgj!bHS`L)OKgc}{9>V0z)oc`&?>{w#CLq7@*1mOEo`f1B}3ABi6x+yo@= zcu9gP2RIFYx>#H9C_CH)ZN*j)-xyd2WbPKUhOv~n)D7=2FDDCt+J-Cd@?OTUS!HvH~%-c|JICID&xu5zIU;S4W7$qUk&1^#sX?XAon0;Gk{-m(j3 z&^mSpHvw?(kKxC2=22c&&x88F&1E97lw;b*_<~_UB$SMMcTw*$UJ~GI;fAg-$zZuD ze86|$Bz~?SeuHwyd=OFC&ts0y0GM6)(Z^YS&)dj=WG0qIbr+FWC`>CH8bQS$eCaN1 zajEfe{og%s=w`>1H9$yG3o-@@i4!$O74`xkBe9-$c$#k(4_igh5-)g73X{qjAS4)s znbSnYNgDsUfsDfX{Nu%DuN-oW;4{Ex%Mdw-r%Y6!6f;wjmW!w=1cntZJHI>-U&bcO zo1Z|Kt`~oXRX#`V0@P{PlIoI47yTC+NLA2bGsDv_+TR(p%0z5eX_#fTG^iZi1Qi6G zk8(but@(|Iuw@jCegp3jQX}^H|H-rF@ALA%PQ*C;d@$y&SJaI zL8O(@Syf6VRBNf68R@eb!i-bLy>?ZxD#mrT8THYXj0>)PL5m{H4MbddG(i?o{BTbe=hJ!S1YQ$@QqXhib< z*-tsuxTX!^hyIz_NRsn&=ufK-mep5(I;iN`n_hPC|0Wk1xR@pM@5Hi&{~sh5_P@!c zNn>1Tj{{|6TD`UNctPf!fZl>!moOfTi>FdpUVT!o{y-zdw#}5&^7*wb@m-8nK{;47 zym-&MW~wXIch~nNXK)y=F?+_vj0<>;#o=^5ptvLBWzX?p=?{xj>>R)l%MkM&=Fwr= z3;t$cLm5whIyM5qq`BFkr<<8(8~zT+0dVFHiki@Gq(Q?`H?>^GXdq8TB<&j8xN4Au zKPre*%icya3?zKr`F;u8$yEd}BjLiYo+1tF+1`meftZ95oMtU70l$P%jxZLPehpwx z_?tN8@Xvv#m#0>@nWvyncLf0aP#7}c$j*R1vT^j@gNhD*m%S#m_hr8g$5V^9L@err zq9E^KLi#BgX_(qrnEfsXFO8)ih)3Y-y@Nkt!P_@V7lgCKvF|Do+l-B4oVS-R?5ux{ zp}{simOAVj3=mcx-Zl#OOi^|C6n{G*6VkvqSe&t~vCDzR%?hh;P#b8#!xDool@t?a zG;{LD4)$t)p-Zyh2#a@Ks1gX*bfD5Am9|9QNJ^whG-aPGxEm8GM)lAD3&QNFbz;ai zZm1+)=Tlm3f1R_#EjnZ^SvJ_-zxK2-5f%iMegWX7wk{s6zwO5u#2olSRfUpr<-N`x zZ|n|Gf{Quaz6y3d7mhRhuknL21x7K=O{!%5{cJLR$=;$$Bt%G{Y%5Dr^B})znRr6N zid9~Jv=oHON`@L{hfty5xDp;pN_m##DH8@4{#+*&B{*xTqvPzoMCYGdrQWYM$w`S$ zg+(}UHb;AuWuylxOZ=AxEH56v=M!<ED?G#%gs?!!RaRdKUNVu|-$KwfV zm+`^t;2s@QfVx&9_s!x$*7#;8!cor^d>$)vH~lG~mfwXQoLx7*H(+ozsEk$xJ58@q zw#WtOvO#&}$Ae^wQGW}*fL;tKbR{%z)#^+-KO!g3Z9>mTO}|58wlWUYooJBk8J6*K zq;pBv%a+D9`!n>@CEXGIPBkpUg;AYr;#XALqM=+zIyS7Cl{@2>3r1O#<{5(>x^vaQ zy-WAJp%>QxwvpyS{kfDye>?JqA9Sj$>n}VP)owTLExpNUR53v%$diheKO&fqRDQF4 z$%Zx^>Q&?J1!|emdV$^DU=IlDScCaX21{4H>&Fbdh&^2yldrNF_FK`8WeK%`<#LT$ zNBYAOzpt22qM{*uz%bf})6TKv=kfsERq*4I6VNlAQ0V(?3>Uek`xu>ed-Up(huTGL z!~-}|r$F&wm1nnPT%A~h+5Jv9#4n3Kf1oGR9?U$pr%0@pkO@do+Q(F{ZlYqQN5VA1 zdiI@S=ATEg5EB}5qO%>;NHpI%DQouKp6n+NtP-V%LU>P;xC_9a7zfXLD*#-Ii3tFl z@R^&5VVZ{rgscpY`fC;UO^08v>S6&3)8ZffDA1e}SzgxRs7E=B#HoE7nK zdpL7Bk$QY)zxGOsZ5WL!yKSr&IDbLCqiXuM!PWhIElIOmx_R(y#>Pc!b4bNBkWdTr z+gs7o(sDCXphbX@*v*7rU&e>!=ZMSnvoo3@4UL09V+Jl{f*!Zrp*V-2W>`sjzs#sd z5D3i6;+RsbVm=ya{k-!=K@x$`MgIV+e-u1g=->$T;SpqMp+mo8G)QrtD9Pmci;Aen zwwi=d;EOt%z$#%RGmJCk>MktWmNF|n-U=`V-29Z!X>!!je1HImC6Xnc2*vK+EhedGPv?z=s8~qq^6g4~`oFX1{2JoQqyTO*=Z{{qiI>u*{is<@5 zM;dl3?e0H*fdB*Dwp~oAHa;l8r4icLn-vS0io2bn^fc-!+X8J2)YHF_ji={Gy4o8g ztdNoHBlp{=oa<;o7IEpEGyaS?QIe#6sDNs^)b_a0DyXO?U7l^hKivFz2@FbO0r)hh znGNdDGiM374}9%YVKN1s*6ti&q=}bgw+U!m4xbNa%meC>>5`6Qlxox}c;`W;c@YFc z#HB(^An5&rp*AElL*H~3JY)2M7w^!F4sd&*l9nihuL5bv4ZQ?{_zCQNg)g; zLnwZQZ?7?oow@tKy;s1cCCaOspe~XBlHAxeq)HpvD|%9;(PUg=syI5G&79IZ_Gz%j zl-o(S(;i9y(qXbecO$3lI7v`T2YY*pVxm9p=jhrl zm{TlRm}HaobB%OUQxe$pYmVX#EE%gzvFQ^3pp%bfvN5pK1(_M6ow#NEF~u6` znOlUr+MEDq8_Vwqz!JB{mh@kLZLgAA|1c?DSYWpJ{892p=NWE;b>`1y6uN8z<`Fha z6}!bE*(9W5l7#r_pIm<>x$;(d)sdk{hQ#VZ<8fne8~5W>ShR&Z%)R{-#lT(CQlPul z`E@vF!S{K{GK)-jg~DqY8&8diFQ|Q zk+^ldzR?U_;6M(i9Do3owXBWG!=U0Ky=X@J{97~I;vymoEe$}iR1)#uW=xB zUDYDE=t~#(0I!KA*))UBm1>k}8`wvSh>B1uO3%3^>2DE|jz?0^J}NMT1z^FMZ*~@# z7q4`_DPC_&lo%DRhgu+o0n#ETG~9Y(`}uah`J#eJB{7leB)edDE1-6-eKd1IzUUq# zp8~)nTVWq+sjDj%CuXCG$cl(cFOLP&gold1;8aK{C9VpC#yQ~77Y5Za{iM|y`!W_b z!IVC(mVg7Lb+r{yl4I#r=d4m`UDJTqH;HGgsdy(*3dljMp*UCF&$z7k)LQZVpy&?R z?!>)dQ*`!dk;L?1_z99|wsy_i-aZUnWW@Suc3;eOCUCIp0xkfbUEat`{jS__pPPbG ztc8cQqrdAy{0yECoPK5ZE?i;(n#g+uaFc7+&RQ`xpX-!qk=gBd=P-lQd9EKx-lq*s zIXN0={R8kF(lZqw@Rp4RwjReO^7nIVL3WnF00Cn=eqWyv8d$9m%3H8QKID+&(Mu!m zW*U~z)mRjVhF+$wvvzLqvz^GWoz0xd00W5n;QCK6Q4EB5Jj)+o)9pacXxA0~wsJMI z3*smkDQwQeg6_UkvpHSD@%N#K620J7$DrOG4cWo~z3-ZTuBoi#u1*r1I1CJW5&%nF zDQ2GZwEqk>l)zndzNhgYMU^B z<^Eg>4c0$HVyC+a7-!9-nXPq?FygWAlnVnAO~PzmsiAD{+(MFd!={uA{MFuc#f4qn zAy?`xVD<1N(It8m43U8Ce|S_#BkHiMlD9{qDtl{2t`e%W= zpbY>TT;ZaO_;%N?(cehC3~auUioxl$aPKChQJ;5hYzNk+CP+nhwIJ!VM3_DenC?%) zdD|m?tTIZD8)@P^!?}wG(-Oxj{~thQV>>&Wa)*H`7}B*;IaRW$NLDgcaV#bL4hz zo_hpQz3cgg4wix7;L(uU?=`g&ZKQCw z4A;}&o9-Krrbs1sU@SKd9**ZkjtOEfUbR+-q$J(X5fm?Tw` zt3#g)8h`N#{>ZrMhOJ2E;(&Uqat6rf|0cD-n*bqXmK2UM+oUC3xNX3A%dfL3$564@<2zd)m%(YDt1LYQ*f1&N*l%* zc6A5#Fb|t^8vmFVBX<7L$?hpC?JimXMDV-()lW~m9O6R6o7<5Og1P7U&QX>qWVO<&u+@FCivW&t++VIbrr;w9A z@`{Dam!k6E{7RF~UMV8LosrP{xA<Q1+o2`|~o#YZbdGik6uS2L-<0C8>0m!9ij}d9oDQ zFBBQQPRLt#i6Za={YsPEGL{x5L6GxJVq^{P98NhLbMqdL!S6O$fuW)7 z1&Ez8!oPTeWxf1WIB1W(Y4nib;8R24N=>fXd zCP>s!Mk5&?1IS68Y%wCj#F;Y^dZh&kkYox=SeVoGI2_nJ$)UXMp)ApV-fI$k!xeic z@vzF3$?LdX0hOU$hzDpxm~KU^V}GNvu&2k+sm>HQCi3OgsY>4=*5d~DmYk#FP|JzH zrWg9+^y6x~c@&_-(=T77I#I;fmBz#VmLw=kjvIcQk#mLB*~6Y-+lQ>GO1Hw;f8Zzo zc~HpMdQn-eV^_IY)kD<&;n13#6&l}V%;Y8p{~#QA#!Ub~9*PJ+(9spLqA-j}G{a&e zo$S~3vymQjN0{B*1SW>73`Hv(wOV9tIdZcMOnYhf%+hV7i06;d212WFcn(gKLqqW| z!ou=fuAdIN(h_1~w>VI$&Y0WD^Rqr_raf2k@S{$0jWVpkDn>G7yS@yUG_W1dz zwlUfk%>1)Z3q5uw{8LP<)VR9xemYYZNwM)N-@0xL7VNv?U6AGXfrPpiq#NTvC+?!|;d--xLy_*&h4dDvKQcB;- zu18oYk#uDgEXvN=VPeN&a}PVxE0*tlw4!lD2x-d9j^SDtyfK|T_`>XRX=DK(y@nXo z2(mDHC*v);~OkNpjk2G!^huJg=LM8#|m5DgSp^4${G=X0rKlkx4L7g^N91;+uI+ECd%enSP)mp+71EfEqPki-N?8LJn zD35fciS8807Ya!mF^sTEL<5xHP8etzz91|@H}4HQS|=n#!+5|BuAHi}Qo#k7G7fdN zi@{<^VL*fDR7*A+DpGb~BGn||b!+*acRr7E#$t+)te!ggkW`wVwTHdhQ;^mq_x*%)5mYugzS)D~iqmJYaC>E~w+UOp0&P5>8Y=jbAi{?6 zxGAE{8ekXN^4L`=A$#9KS??^R>>7)}oA*XR3a7x#jTC(R7xCh*P?rM(?f{s;6PHTG z@9IK#k2p%2nP1$D2!HH`&I~4q734GJtBFNJY?`R(N_Jn^6U%>Iii#zIG&a_OF^lt0 zG&}d7KQ1(0iURzEZ zm8Mp3z-H%|^~_t%#W}GL!do$wj4A7-ltamk^X3PC@u>PMR%1Y)c5z0j!zOA$dDrau z%ILfCG_Oi7mKK=)0l8jJ^emk9b-^eGrfG(qJt^Ukq=6%XW({$C%X))4gg~Ymde15w z#s_G*zNmH?msgY@XtT7*Q6=jmma`g~0TdJs+dk-8$HFSSF_{uk z@f_P;_e6_Xn{|wi>karqoo8Q1EL{g0821kNW4d887-;@TPyoO%_tbh2G${fgbVCV+ zU%S~@OLX}#HdllIEk4>0e9dc}EQN|3lph+DHBRirXo&AiKR0Xr7*n-%D8&uV;7$u#Yc$pNY?a6R|9MD>kij1ZGOXQSMUv!t|HUm8w71m#9x@MLx>wf397spqh4=>4Cs3>xDV z{H)*9lMC}72rbsX)YGK)|5J|yPFclIqNX$3Ko+=D7wGdCG|DIl(Ml3i_Lm24k_S^& z4V0l8_-0O@Z1%g*OvT$r@3Btegh;6F`y$O_?_hHvWj~EqeLQx5owwY%aN|J`a_O%O ziZ9z&Z6~B#Nz`BqcpQhOd+6$_ECK~hL8)T1LLwYDCqn6BLrVC-N)o|kJSt6uupLze zqTjwpJ7S;F@CipO#MTK5q&use3BiQ0J5@nyzvYp~uL-)cnPm6L0}0P@7)1?gg}@t# zU!_f-9*R5wggbDHC1-HtkYxh<%6BCb=|9$v&5*H!LElBkBw;_jD-p_I2Q`_CwcHn< z%Zi_4oq*J6&xW5hv(91*{@+QoU9`k^ejj=%z+kncjUKrf=hWIJIYos|c(8LMV zS7G0bcU71bm(UXXR*3mD@MY`E_V_B$h0QAP4nHUXeYv}01h@0!w7Ozeg@gBMG=YJ` zAc(IqFqFxnpUzt<)Wx|M39lpGfDLU&FoVNJX3OV$N58J zMX=&RTSSd{)M)timeLgV?@l3N#wf84zyZ!H$t-nCWeG!u#&wX&VH%JZVb2619rifZm0V_7#B( ztInT~#PrG*$V=c+ATY0O4y^31qA0Z|Hc4AVxlv5-i9Q7ecA{gQayKb2XUuVrhRpnM zossQr51P_OY%8K4`w!+ z6$IpPK>`YR9-#M{60b!B7y^ubBvlMh{2jHzy*jG26e^x7EN!G=*7x|-F&4f9& z#uu+$>nAl-^|;9#T0x6RVPR=`vn5v>n;HrHJWrUAqb=lXy4D@q-qXk@r8IJ2eb&wi zwTha(+T+_oi`FrF-1TFAr?&_5GmX@s|JI5+MZ`u8s960+n{3c#oa0I1i)b1*kdv3& zjR7wEW}-nl6-~!e%l$uGVRigodi;vF@tACBvg>xl6tzSAsL4E6FV}<;G3JYeBB>35!GhH@Ht)GG$i_0 zCSbw3&kWG+g6l3OKD_)tMl#MMLNtOX%smeIj86R0ztAq)JmN8je**%NncJ1BIPQ2d zO*zEw2cW0-$@~_LMS&SwObY3xV2pA)h4uA6JqTx5qS6Upvbbg;Wz@TEonTZ}6ETQH zDW+#ul5oe%fN<}ZgjCh=IfN|EA);cOK(&Zk((t97=xjf7Hz+Ub^4wRAI#LuO2rAu$ z$evms-!{Uug5SGl@AF=Hy%>$YswJFGB+?wm+|v&tVGlN}qf%5GruXUDz9;@J$SxxM z>$YPA<88sd3o;efL@3+E^j(deeTs=n6_moC_;;{nF+xGgDoQ2J0UkPk9&wAmb;_ZX zU=ljlFpzTP(+OAo;|B;}sqvEME|305I8cdm4{IlD6}On|cRhAHr|?~m4gBPz!k)ta zC0X^_E@p@F_$o;Z`vQO3ejKZ;6LYW0O^Qo(Tyoy6m z?lA;>?LUY`cMJ=@1HuNdZmleuqFu=D5%w)pEb&(W$~Pk2QH=2bc`f-^#Ur6ss8n^N zhVJZ16g73DqDdglx(sC}a6Z!8grCVK?DTn0wLsz@Hcvrz44g_SJBiFpy;Wy=@#QL8 zi#A*P42AeD6Slp@mt6>76mx<@{+z$}_9VqUe5M7ZlgKwifw=q`r=s!c zv--d{WXF#ovM*z<_Nk%DGh8$GIdA+frJtjxV|`P3`Q|0}_5i~}hwGvW=!R=|8Ccp* zB5|Fdxp88B(r+NSFuY8L=ix%}G9s?tJ_zKd25P|=PXT8s_ZN;$5qO!%3A{{l!5RIm z5%%y9j&Kq7(gg2EvjdJ3Jn;tis|96j+>WYogZ;n<_y6|Zn?i2pxvemHH)3s1bXI+^ra0`U_e`T%- zXRR~|bgXqcwh_qx6)`is#RTO~ZQgwP~RD5uj++x52jBhTKZ?`lTWgCEU|%Ok3#( zH5V6Ex#Z=cveQRgT5hB==fPJizM(5n5WSX&AOPuSgA=r^3P6a*QtT*0@M$zJsfY_Y z=-DFqYSwtdQSIAp-DwgVI?Qz`Nc!%LRdJ5WQKo|1Ipyw3=sN}xo`Nolz_0_m?QHSc z!Nrs)k(9{W@yfP(q+@v<0|Sh49*N7?Bk#Dsqn@DeUM{`I7T&yD&m@k}E*bSbVwbP1 zop+EUa#&;gW_)zTHU-6{S8YDa_OXD!h0JBNLkXJaMRk8&o9&eUKYQ`?eF%ZnzlBUa z-2X&Q@c*?J@Bi#*j8WN=Ut>V<+tNpT>Dm`xn^7wcM=d0@WdQskzR!_1JWx-(MtD*B zan;dE`^z-)ac1~&<7p%3VDKf7x5zRK45ZIpmEr0*0E zMm>c(iLe7QU+`g6P*dAV^`$_T>?BwqP3c{fp0=a!KevJK*g-vi)!18vSkjjp4CeKe zeCT>(n$pYW6uJvRQNb7#lvvp}=JnF3iG&d7$TF@NV2#O1c!P zwVzv~Q{-uzVysb)5FMaq=Rr=d&2?lsKI1ukeyrCtRuH|~E(hn~0b^EGrI7FNW2>A! z53pDkX5k|Ht!TouB^>89vuEs5tu+%g{^NNMFZ4NG@JDcJ;XI%4`hbJigu%}_7ta9x zi$3|-8#QIi_Q!s^mA&+P2N`~cYp7gU3%~f`UtvTr+1+l1LO0CkVs?VW1_f9VK``7Q zRutV8IGl6}y4r8rRd!El1T><6c_4xaZk=@v`Z2Cs7Y2w>OZcHt+Zmyg9F9pL9x;cI zFTPmfH%3U}KaPoT;QFtvc%FH1V!%&SGmY?R>Corz;pH$#xCXY2l6vZ~#1C;%o~+OB zRfTHQU2&AdDz0yJ|=@%+G$W<#}4Um8rIo-Ef5ZSJ!Adv_rJP zs}mvL@!IYV<-hK5(q!%2Mbe5GzXvtftad+Db~+BZhBZlC_XQ zl$bZ?KXBw+v>-QKw7{UpuxeBvj{c)eoZ7w+R6V)G$iNoPN@~9t!iECy*_qFI2>^Yj zTD0XJn-l6t8D>c**wCUe#a+%hBGgHa`NrW_fh57Yj`)QycK9NAFSpxTffGfW|35TS8C3kt2p=;lL`T7ZgDp1yWd-p)osi> zEp8EBNJ%iqSh{6AR&ociOJr^!+*{z)6|cZ{=$1YZ%x~hE*S18xCTKQ(jhrpW81x)G z_q>|tN!(THqq54W{^?<8cf?i_&;60v=0B?J(b~6W6~-Vg(&Y&cYm{lat>MRDhY2DE zd}RNXw@P7y8KPB$YaCA!wt84<%Iq~!ifTRA&@sE>8zj?gV2jiwGt8CLaXUdOowGA* zRStKYBT)-C6y9O8%PX^YnqsR~|5>-$CZ_8ZGdBEmkf%rNTFOBNLWWJbWKA~w`usUq zp1{?`o-{dVL44pPdp;gV8M`TlXEwLQ5*b7Q7h(=HebxyJf@(oEHSOUqDf?tix)`gx*}JO*kSS} z8N{vzzW6ZvgFz!%qzqO@)GH3GFf#Xuus{6T$RFt-g2;O;%OY_E5VDx_7_%72z(IpK zkOQB?oS3amN1Z3Rvh$;_e{%KdAGX}??_6EU|348Q^f&SS>j1((hYqR}`{V`~U_!6F zBdMNPHv}LGN;_mEItW?tdP(fCn<9z^f80KbAS1!VGz`Q#`uTqvm2Aq`(QGo1N$W+Z zYDNHcks14z4UT_0deXB^7Wxe%a)>M03sX78$xGc&>&wPv-G#K-aCW8PIKN+NU{m69p)DdXrw zf;A0=^4v7_Z5!XZN`}LPVUUnTOd~wJ97UUNi6#6<9zRJ>M{aLBbaF7d2b=6f3T7~O z@s=g_m5{m2L&as*mx|KLeIGwW{+k~{GFdV&zd@M#&Jq9qcmeWv4->Uuza#wEaJ!HTEP_4ctN|%wv?vF=+o$wW6;d+A zScsL~<^n5WbOI|NBdxB-TQ;>*hbpkLIak2Vr+^Z(ARDbOT^GPFU?dT@6}k8=jdOf! zMt-+6suTa{;?-kYJSko1?6HT^WatUg-9Rwe003Ik1Estqyg3IOwlgTI9Q86vLCDNy`6AF@hZ{pHK*~Q%!Ruz2cDHR?3j8P6bFvq6Buso6Vtv^)_X3JtLBt3i-Ru+|0SY9wYO@3EjK%8Qk{4OwU1ba zmTzfMCdBj#c-rV>`?BRH!K{b*SHK*QMwVIuMnIflmO=|Iac9Srt~Zs`$?Rc3M&gA5 ze{0GR1@$z+$r#e}S)?lL(09OR9&;<@mj`p8{*}&zdxejB&PKZvc>`L(b-b<-(4S|u z9EM+T>4SzmFnhC+gDxETl3r{E<})g6bx9ATW+uxj7kBNp+Fkwt?iVt?yT;_YaJ1_o z{!`3v{}7CxBIbzacic|@$2aHuYs-JVIff1nf5Vm&FK_+LGDBByD1G;t9FW=+LyjvL z+^EgMY;Mps)xYpQ!>W_-*QVFLm)+`1IRs@^J91f6WuYAAs4w7nJ@NH^ zvr-~Kl?C6xc{zoOG{47oiQ*F!hN_qZN%L|Ng~FCd;`K$Un68y7snG5l!m&06n< zc*F>rRWgtZzPjh@Kf8ijRh4~EXsXI_yJPaJ>8S6Zw*wn~??;y?mhJM&Y*I8GKca6=0s!6p;`5z1@J#Xp= zHDK+sIrF`4*L&sC+2+bSfUYxaw(Komo9Yf&tMz93i zOB&a*?wL2`Ba+I?WvrBUsTR+(fn*ROYxZ=8XpeMCXbQ*}_uPRR)t8}Uif0iBYm$?(#jC@;2?VcqjWVMQXESO;N*NDCGGtVNYyKLr3o_T1Ms z5*DMk%R-q}mD=TVgMV!N2N6FF_lEH}JkC$tC);&!#8ccy$7OKj8sFDO%%iFZMZG6K z`YP+lEJyh4M<HH47g{)9|W zqK8?{K0XzraDVGOkYb;M82*2l>hjIjIO02sDF4aY9OvI~c6Qm$pO3ndJZ|&^F>Z-2 zzC{J?4i{ouxdhwTYjn5;_e>_H?GC~n%`po*9_U|HcB3P4Gsgvz3k6Xm?B;ZafHWv_ zq`01D>wlGfy^LJ>-%RKEy`OYU^S^Jff2Lh#>HECywB-3eZNPusyKlX>e7!#T&v(BJ z$@BZa4P149eBQz5eO_kyzueEWf1R*@jn{tNzKnd`%;WRgGoUiqJosY}w{@6I9|G0V62R-^- z`}us~&hK+)8r<`7Gl7xk|Mhm|_jdC6(&GQQSBCHR$u#;s;Op^7@T-j9=cz2O`(?6L zU;UY$|8w_g%lGT94F79sx(EB0|JTJ6zc1F;x%}4We3}3I{hj~Y{8`@DyFUL{+70*{ z|L20e-$zxg{|kH1=Z5?ZuLA@x@Oge;cRg?~cP(EZ`24_QcY^%A%>GFJj5^BR@8;rp-v9GHWODcGwa5RZs^|IS%hR#}M)tj@f^FpU?8{&M z=t{pQkMMaZj1lkS(UNbukM_Ocu+Nw0?jx?7?_gRV?}*>$^+opuBUAr! zSKWT;d9rq-=W=X5v-xE*`QqMx2lnJ>+W)<$`)y;|e>voNz`f^fqYQuFRDbDx zujR{m2IuQ#1HSg_{p85s6OvKm>tLLt`)jW31?AJX7PDKAx!gMRdR*=EqZYGAkNLNC zsebI&rXntyoi!5M3mV_w_mn|`BTml)Mhv+g|1u zV9QF6UIVsM=;xKc>{b6K^{t#7^k*7nC)o%+^PF{NyZ_r=OV7(gT;A7h6n@VH*$@2< zNuP{?&y=Aa**Jk?=+`gvZlC&MZCrT+y$AlUHPex;+^sYJ&&g+z_gD2Vo(exuT7AI_ z*!?X*c1WnJ{0@*I_Zcd|6WseJq~t9@cu193w^;JEUM3{WI)#U%HRI! z;X9&JJ&tGjJLnV7H}W&^^zm<|QyZvT+5u(W$`O!p@r?N7yfN{m-~=tqqz#AtUuB)poaIO;E}UvVnp+oO%`ovsKgy zBsGWXQnCeQAoy1!jD5237~0ni%Xi`1S*-_Fj!_8cmj;wt>s&yYYQzvu$`;=n3^AO} zucb9f;|CYshsBM3xO9RejvXiLnZaCGg`;o24U*9f4Smh-su9wy_bENl92nECko&lz zVhyGMhsNVf0auA6wr3htu$E>!Qu*Q_uipnu9)a+u93l3xyRI#qvK7$rbe4>H%XYWb zhlbZhwo9H2@;67p}Rg)W5X%G<5J}Y3+1#9FF4|T14`zW#x%UM=#p! z2#tN}EE;ddB9U-~#V!!7p4@5JF4(afB;vR=;|;m_=!<(MTykHaf9mRF=myf9U){Lz zl)33MRPma>-WQ*cyJ`=Pb=4MYabcB+KgB{D37}JX58-23XWjA)S|X0a=dTgU4b$0D z?OBJv>qmgL2=}~rZPn2p|8?dtf2~2~%YBJ;+Af`-!haNlS*PKI!-#%*#{dzn4@sBL z4qH8Cnl1Rhhm+s+Qw%J7-K=t8P0zrS0{bpCA*yK&BW%|c*!i$>RrpP@FdG<74cfh5 zE4a$$tDqBC(iOP=l=c=};NQ=C8HZ(|$N3cim$}53P*u=gzg*(3+dne>LvOnSbP{JG zhj|`GBV0e*;g3#X*fyeZmaMRN`AovVUHoasVcMIO9)|U^pl+vI+^Pp*p>u0*yJf30 z4!}CA%g-0#5{=935wl(+4D04xhEp#M9bJ)ev-z2uk7<)+d?#P^52v0<=mA)Dny9p9 zCD&@v&w?!ytcAr?ZEIBXZB=1STiB|u*ZO-o+yy?vjuS#{x0;#YPr|)P{mV{I!!FQY zo1HAFmDqt%&>{Ml#>!_kVK}w^BjT-A6lXsTdCN9OxMGYRkf*Zn8dCH;k-p-e(CD5v zgRVEP-pyIfu3WvszJ!-~OB~4+eDPJ2sojL~XmxkF0 zTSOb?g^_HzcJl?wVAupQjsDW#f2D38viv05DR0DK8bpNNX&-OpIFe zT5QM0_uX+~fWl7Yu`uEAKW z#`?`=t&u$*bYne;oAB4ueV;t)lXt=*8Xdc2P{U+HgC>w1uY+-!N5b)&e?4Lw8dgmn zYqIMiiMM?P*=n`NKaP%I_3ZE+5stWL&_4|aT@N!LXa!C6IWd-`Q>Y%1?NEGUBkP{R zH+>|-_+ww=>{W|evw&Q}&TCZ7xtD#%^PgC3)Df)Lh1+Sw>&c(eMw4n3oeM#Ve}|E{ zyU)OXL1A%4LI|2M`heL#SAN;hYcw@#fLUN#IPr>0-!2T)FwAgj5vh2IxoZ!N`>=Y0 z1j}@h7%Z&$rZVAY10Dlc0x$G z)G1?fwS;2l{qrrg%WNiraX=5)l$wXB!_^`HN!wVLTD?gro-%|}!N$-m!C&L|JJ!>* zjTAMEEZ*o9%~hI~btn>;qFL5E1h~E2VQEk8=YWDh#nIWDL z6L)vUzuB}<|1wT=CvN9uoAflel5hIY3rmaV6o~3qyfzxW`*@+l_;vOvlB<-eds8fV#Qd7OvBJRGTh-~a!9SCm~cvQvq&v-i7%ghXW?q?66D z_X;87nAs~MDw}ZZeNN)o9OIZLd+!`W?$e!4Cl zC+Kc|_-R4v?1@;Im1W}5q4b~QqT@L&JsK^&C)~WtZDq3_Bq5E<)GUxsi0K+Da~G(6 zSWt3M*F^qM<-H~LAD8|1z^&bHmLJtwP6C(CfQ zn^3@;^#`?rv18i0-*>y5wJ$+6)sncc#fl5buMQL;^n#MgBCT*X5TZdQLb0RE*gr!EvJoQ0HqeVz<>Vpb zEckLWzT)oZ{%f83O>z5bAm|zVv3@~yE5HID*x5fae&3$PT|ucu>gdnxR0T6nnKWRp zb+%kO8o-WrZHD06YQj3}?+Piu$++i#W&r;q#ry`{~WL{YxZ;m~Hik6%*3_XnSu|F?^Z zI&_M+)io< zU@1LVb&9qTOV`;#*LvAWWjOxTM)^TtVmpU)4VIBEb?BvB$zd;eDw?C(S`iH^l#P2SLBq4ZU>>Xv-t>PRdJ3-fkiW8vf0y>2d}-(hr2>ZG z9fuPna!O<{(pEN&toNms0cXPzT~-)_7#AzC-w}87(S#f61d+7T{)H;j>D>Sq$2}pd zFeeom&hRMh-~P?g^{?P_mw)#{7+cKmg%!Q}J?~@n9KMXUM6kJqKM;k!&)`y+Avo1> z3xyF2+O?lq-d_jxo~Qj@!3ujN4?2yg%ga*|t)&1#c0yO=Nx!%LtYmJ-!p{%Ov}GJa zY6r+JUzl_Q?iPdAk6B;MQy%#8ehFv@3#yeK5B;*NxFuxyq1y6Z7)<<~(WE2!LssIU zS<+luFxph~irNO-qw(K!rusKbnS8z`WU2VU1^9xOH}|S74zW&c{CfuSP=`Th*P)kM z6Vt~J`B?-8W{vIPgIO>k#cmFb9xNeCneuq0W2-Y{^Wb>oJt;1QRl-rHMFS@BXZP1; zYc06>h02s_4iBA%XMpuIrC1V}nRO?KTUynfR57f)UZ^Z5!|y|3&R{c1{5|YsN2RL2 z5eA!{B+@~xRB}Vg#t~e7UHSW#9J?2%v`GI(5mUF5ieOo-=0*jPG?gb_-6$oj5;W9b zP?)4u%!pnJ+X~2$Pr{p4=&u+A-x|Ss9*l)Y9UGeg<#9jxuSjKJ=0s&Ly>`V#{qfxR14Q`Ys)%a2!H?*vH<}^ruOtIL26TJ;+NxDv z94TL(l`hT7|qF_yo(j7LbMRH>F6 zvxS@q*@LLQ{sDCp^d3f{epUU)$^kQ z)c%Wp0M%**Qd$+XSKrC{+q;EkKz=^C`e+Os>}r0o_GE$fUM@)TeoBmrh^wqc;S6p( z-hK)Pdu)aJQ3VzTYx*i+5mlBk2~%r|JY*B~Zp-;6xk3XO?&^#r|)oQJkY9yHtbkRE zKx+W2iV$fc{D9qEuzba-P*(QHjqjvr%%`k=*Z<9^BvC_>fJQdQ{pdNYK50SImA+aIhx@5l_9wvj};OAGb|Ww zG4zsdLi|&bnY_pHwM_v;ZoFW=gOK7r-!yyng`IRAPPQA~6jbP1`rZ1EfxIJES{b>>=QkM0tFXBt(MxZt5L)+27_K1I8PG!)Zn#Ed zrpEYIg|Nkgq0CH2=yHz9I0;6LO4os-*0bWvx61oTCy77wXXmOb|E8)Sj>;LXczva| z>yaTi>v`8em3M1h6+F4eb2mC6(o|eo&S|BK0@xX z12{CgB>!|yvJhP2P9fP2#m@bNB6QV=e0NffEA}4n!n%eJx{qL|cEZ9|NDT-sG~NgGP= zGo`G&4X|Ar%q zl`xjZ;L9@8#WQtRRtt!*{E!;a(0Pa2R|BmnPYKy3*1oWjk)AWDVnf9L=w&L(1PgAy z8WqEWF})M|!tlink3P>%(W$8nYO!{fSyB7a&H1QrH;VV@nZXfWOoOH{C)RsPA+v;M zir;Yyf0lmnP??q0bRs9R1ekCdxqMt(k~x05cO%Q;9BShW)>@!IvSw^deL;0P57)&l! zsH@m*c|mlWw$7m&660Q$#*Nb^C?>zdeE(R?AB}M`yiVQv6+E{^hvE=J{6aD zEMK3Xm1NzQo+t4zf`!%dDSKq|S7t8`b&JGlmQc^A;goH0(+k$GRs1t{kUXhm^|^Ec z3}^O1i>Bm^`nkgB1woCMrw#$D09P}!l(8tVt?}p8_VU+QNIxjLx|Xi>DBq7Z)GD>` zkR-e7meW)2=UY^|CX)v1arJy5p`OB>E;P1c9+0f@{nX(E%#E7)Z#j!XkNxm6o1D{GC{so2x8RA14@fO$+3umN z<}#*W$=Zzxd(#+X?CSCA(x>-!Pdr#}5>4<|4S)wu^r??)II-2Yk3B6ZdR6zNnH*jF zZ(4?-D%Mbq?p`et9_O90_V_5EXzBRL<95?67GoX`PL>^_J$|uKFnq7iWL)FVTNAM- z5=ymc#6AV_B_AdyJ_KFAPgJo0=5X zpT34-&gJ8=pQ4K~Mu`rHw9-|s4#t#86`=DHbhD|pLyhUljTva^zccAu0y?4JKdBQ_b_f^S@O%8Z;kbD zCuzlA{atLoXGP6)R8H*UMg0AhAJH1Ek^h3A{>yWZscy0=aNwA^~T2Om!5h`=PhIF%)@V{5*nw~^upFQr))ckZI!ZJcc1X=#@~ zcOM0-Ulc)~JlUV(=i1;RX<=@x(%SZ8DChC^N@~@#2e06(QL3;Oo?dmwMkLaZF|J2? zo^0F5cFEm%iKuM1uA7&s)QKw4gL0DmwBTiA#}8Ms@mD!~j)cWi6no7W`wxQ*-4u-g zk*`q6_lPf-T$dt_{Le5_UWVdcBkNA18oRQAGzM8(^Dt$A0`9A?-WA8=^ei>nUnK#ucX*M(*+lP&{VIOz2Pr)=-t@3h{slkd}y9YE}V zJ$u*obsk6FhpusRh%h*n&-!w>H=j;F&&o;xaiN(+>6}M!J$vAD$DsSxASR}iY|Qe; z+jeKl^p!OgAy>`Qr@vF3w^+Y>|J_OC;_|7_TnNkzelb>-y{J9;&Iwx678=f|9=gH^ ztEVN8)C&8DyQr{E)DXq|&36X>Oh#cB$%8v@3D9j-qLlg&Tg$&54@$Vd*yYrW_wM@ zjIzCruL9d>k;t`@*|8#((F*OWR+^`YKrF_*a*C3BpjjKC!7K2Z+u@R_UlUjqYPSmE zfio`2mK3r-Xd+zi z*Nm%DkCq(=+^8Y0vYYbEq*!6CYen|Z`#~TyJ8I!?Y!w^s@+#?e;%O*74vPTTLP7Jws znQw7dY~gtXT=o)S+(Eaj&*Oqa!euu z!Xrg$gWS4q5D`czf|o?zD#>{C`zG^(mk^w(S_Vern&xTvT<%pH_pTKh1Pe9(^kDr< zuy)bat6Ux^RAlQ>DANr7p%ruSclKEfDZX8Y;2;}5r%d}-yi#YX83HJqrHav%A^y`C zDcECi*kMxwOQ8Q%vD`gF_g8ObmF>Vkgr(j@>rxcF#5T2y{?g1H!5@KJKj&0wyVFrm zA8)O&RNZD4>k83xp?Sz^MVa=9#g_4f|4LoO^NB?5%MW*lherzZ1j2rAefB}x4g^#V zGFRM?xlih0v{6MgQ#C_VzsfaOJ14B-WsF`=LB>uizBA~4ZPnC7+75Tg@}cv!S#3zY zU85UtU!!Y{a9XG6K28N408t9{bu1F$?2Z|9)8>wwZPXtTkF1W}P)o|3@gV$LA{s2rh`Ya5q?=1C4R%Z94mn71L={YfgW#>t!i= zA_Q-(p0tkdT8hZRRkLW;{H<~pN;>DCL9Bjv3MVK>{f{wH4jSSPhr_&5p#|up2#+6^ z-Gw$g6qYUQ#7ge2=jxhNxRo4_$)EJUVx$_8AxE&u-Tav?W@Rqd@L_jIE(fnjJqpG> z9AyhxqG|oB71C19v8R&3U9`X5YngNsz-Jmi-AYc!UXF)}zWQKm$JLHw-7$r)-3-j@ zfi`?O-!nC!F=bKI?~KU$W_#sW*3sVQGMl5;@k;tNj^2Okhi03Ptvn06Oa(`&jh9gCIiAW?yN=aHixTORuSr216qAMq2&P7pVJ zJ(gX=;I_b}JOlgBJA~FK4+P?EE6$=nIv(nWxQ$(4Pu=;XAB8Fh?De=ID7e}TVl-cl4^D=hc!gLjW*np z&$@lM=VM6iV|Yi7pP~D;diO!7>^yK$cCEWAuj`#eNTO!PY*dsx;yrm z9hfK&Dt;0ws(K(&eRq5cAXuH7137$PuClz6uL=%XW`I|BHzEE_(!mMsvm2eG#Gb1DyKxiGs)8Ds^nW z&uC~GC-YQV@><2LM>Xu&s*g&S88qk^M5yt?Z|j15*RIa!#Hg(wS$|*eNRIG0GaHp~ z*9*BwtG<^ik(v)qLTKLdtj^{px6l2&7m*B7yn9+eE<|f&K5q%l25d$ONsB>y-Rb<( zMnHHw*wcbgYcZ$Kf`G9sqoo05P>RcgF{=Bo1d%$|nB*KPQ*}4(-KwLaIB;e&`vgON z!BOz*pl01w@w4?(Y4K{U55Wt#4|c~b5wYMj;HC+w(t1`G+sLSsi9p8veR=nR&;9%t zRAZ(GrqriU7yX(^m`V$*b$7}{DSFz7tlJ^g2MHSpKr)`L9zR$_0gWZGaQ^T0jUxFy?+gcmek%@m2HKf)?M5{<5xAch}ynJd?2$sV7GomS@ z=7<@8R*W_(O=c=h9LCE;qczDz6fFPAn&CpQ3g!Gq5va9td=G&fs*aAmvyq)PU9Afp zHnjhZx3l%dV&i23;{s-Hfxqd&Iq&*)TTTh5UlEHaiJ)RXT4LnBf_bVRvQ**uA~ZvA zyr9-KaLj^mrU~QhHf%>V>WEl8K2g$l6T(vRn2yMf1t%OB?krFC$7R}J%flaAeJ8TA zm5B`TpT*SL=pt+av6TgXb0yF6hCZf5id~jt;qS_;VHzoI;)gs}g$8aWZ{)xCda#I* z6Hfmqmi)7uPT<}tO0~+Xekne1Dd=$ra0)69^HvpKK2>pmyB6OSx>qOLE1AVBU0%ir zJW;djX)Y?6Iv_5%U5v8kwX`PGN^S5y&Yj@r{5xCxTWeT=f-Q_fI+Dh!u;g_qLzY}p zFQE0E(GxCk+GDE*A^!gArxfM9l%o<5Nf>cuL{i4wdE(uKV%I-qc7J;1ja+EZ*@B2+ zGA-=}1~)cjP52xp!~(5-V<1C#p1c9KXZu}y(z@?33K{-lRD)5fc4ABMnBA!N+{2fT z%$Kqe2Of{<=+Ufuoik@T{nz^ZS#=-diUSqor6x4+EZMf3cRtEa&;8b6FS17@=1fuw zR`(Y&JRQZq=v!})5NHtG<2b58^Iu~Paa4e>&?Zz8Z5}#Bv6`4u-g};7Wwy2F^Yr^5 z-M;a3KrsVqlH8glpVei967MH#U3C$zhZ5igWYp;$NLYG~r^xH5QlI&XmGFK2pj4!F znR^%G<(Tr~3T>%ZqL)-)eu^JUV?I(XoARh(#i;0DJ9KlqL~;c9Joj8yRHL!xA-bt{ zS&(cS_IO8UePHw&+~kO&ru~S{V1`*YKz_Nfx9=9b@NPj{?>yLmKaG=P+%03Ro`xSaRlhw~EXC_*POPz9;jKC5|?i5Ct%Z3*Vd!O+(;MBhKG$OU89=YUz zLre6xSnN1d;COR&yDaLuJ1pfT?{M5}1$@n{#}DH+!@$e+H>Jf^ zX5(bTd!Xq+H_i-ioUt~%?TmO*Z9_gl2bWd9!EvHgK96V%*pJSVi8Kk zVaIfY8LHLYQI!sd#Iqz%!6tp`FD&cp4{ zR-XglX@PaGG`La8BAbZUr*dYyO_GI=_=h1ahDRl0Y2=nL77#LaDYWOZP0C0!;S61fnGAIa)V&@QnbS~Pjlq+>~RL+HXmW1B|QBXObw(= zNp&(%(vLS+(yH&IN~n4OEuBPlMKYYvBGj6dZP$X(o4ejt?)`0V_ zIhkZB;<|_xYn2aIMNdV6W1@*HJbIDmhfgk;3fZ5}sXoWAC&Yfw*)kyB6#uA+sy~SR z5ke@eEhMcijGoYPw)$k~4ddJCSDjV1FBiEokHEtLm=08iBKZcUJ5uPW*vh(h{( z3ZDkeXa&v$?TO$GULSHL@vew^J>GOvR0$qr1yt!>(Xmfk6O}D443CO>wDbZmjyu;V z+gwkfLHhNRcKI!Kt=N7LenazeOC9odZW4B4M(c>H51g{uF$S-=IEN%xb%Lro?nh%x z31^0)>LIyU*BRIf8~NcV$4u)?{MIXq4Ktj@&T>+Sg_-l2oC+WP*(M}2tg1)MD};A! zEhKHF$gcuRQ;9}O@Q>o{ zg{2}Z_~+W0F_goHg4rf*Fk(?S+dsr%z>01hSFNa3-&d7LMS`1B?E5gYw#6h**hiI? zBrm?!Z%@_v-bHFOiF*j=K2BhEauq7BWaMM3Y>w08*CD=U9b4JVD^%r87H-d9NGZ$tK80OLFQqV5zTQ5}K zlDuwnV>7y^-O_rokZeuPbh%a z^2A=viKpe{uzN@AlyF8j?-u37suK>kcl~`Y87v1F+MQ%^#gDf)pbmedCVncShk)6; zQgw=w&AlVpb|&8HC`3%L!$q<5SMYAJf$bj=<3Gj_ATrn!OXzV%puBjDwp;(WT~8U# zWfJdfC=J)$c_eQR_j&`_W(WvkCf%<9_{3Yg4rD6M#uLW7KUcY4OH-O#v2xE~g;{Aa z0W~oSym#Ry?IN(3;q3P#O`8abtP|ZvOWglBh%P2jH+TjO&b568GJu(%Bgq->X%qMo zLMKGDFLTp~d6f8AocQZL#!CmZH&Lb4ZQ;UUrmwW5x7y?ax9{E69V~SIVQNc=#M$Fz zg5@8$Tfjfuuw4TdqaTc1Ov?oLnfzp12l2n5(JQOJ6qF($Vq;G}zZ|eGeH@yh{B0nh z<|F*Q*2;E46(7gJqnSQ3uT*wWn$Ml&hSAGKTKJD)r0mjI#Fw=}rjLlkh8^HotA`8P zV#gGf5%D7)LpqsN$h}GV0zc3FbdWx~8ZjzJT?Cs3eX}XvS+dGiuDS&+FSys#g;2#k zdJ_r(X+8vJ`Kfr~u_Tc_h;0;%4y~@Zi&wN+S*`3V4FYa>_U?8e?>oj8keJx7RmSxS zKwa_Y0M0!LuW&O9`wLW@@h5Rl$}-^)OjLHm^xM>cG|qS<*1a5_Z{hu~6HgoW{mLS` zf3>jYq2-az~%C18RCzf%8Ai@I}bh)P?(FpSuI@e3!fAzpc(~krvIo0)wIRV zI`p!WzcJUtvy4q5N2}tRt(-%KS^zU5bkooYRHHV~Lbr<9VwIR#^Qtwj*IsSbtsgH_;)j}IR_VrP8-8AHb~C^|cCmUXMqwKDH<9a?PxZon&ApC@ z^l^}7u1iGHx;H(y1$P^{KoiTlLF~fw?auwz0J>hY-DDB+I-srZ3vSci5}GK8mj9A2 z(gUshayEAcply!qF@Ux%d6v%}m_B5)nnrQf&Q|Tg-pW7txo!leZ!~V_%~Sl`CJ}uu zuvv~0(2A@GiugK9QnDau5OwY#wC=3@c`BQs3J7$@gtWyIg@@!`b`{W=-xDn^mXJoq zmzfP3C$RO@tCDRy0-FPLIkU~fBM(F&Te6Wy{-+7Gj$jQ=7rWQEVtiG5*u}8Abqt3B zbaRJk2;lT9?M7nXf&KQ)Zo{h1QM5w~%%+t-UGEDXBezxwc|I!x{>E8?r!)8)XNyB? zT%<>u4jr7>fJX2)e0khNe>WjZsi8+b5-xNT=?*G>q?hD?jh7|LeWnLRDe?-Z?6GHLen9a_07<-g+}=@+jgRFsS6@-jV02&fcW(<+n+V&&gm{$$#le%bAzQW zJ*IG(?Awq{0MC#3UAF){Kk92A*SOahRumpc(^(u@9bv}*PHtkx`HCST44?QlHiODc zhPIdD0jW~Zyy@G8o>0<$mH{yQV3#m_U|eHY{NyL-;F|~RoCp4We`KOb(TF=OT&U!U z5AMD7p`LL(aNY8*(E)O6n{hX;;D%QPLAKx!yy2|e&&JZg64?CumWr=j?N-zo^#)|~ z&VIs3qQid!-uNY4d)966enC*nd&Eh8cGPIxTj(I&4Db%`GmqLHvRbMgWuWxik#n$O z*-RIhXy|F zn!UP;O~L@H%vm@zSpsRnyOCt`_bDiLxNo&c?V#`mdqRdEE8{iY8SzdX`n0S5 zOP)6{*jRBzJ!z3ZVQ{}~|D zeJyF&s%)25C8|a9+JCT#v>EPLZQ{fIZSR<%xWZNI-;4SLHLjm}koAy}QQa*BQGD@{ zN)x;SqDJoTeVtOvMO*2lI*3m zC;L$LHrvCHT}f!~Ry}<1AwY-q6!JD#?t7&l+XpR}h9}7vylz}h>oBIGF#Hn- zWe<42L$GQ%Ja!5RiSR3?i>DE@D~tz4R7V&Al9VfPS>Zw(*HvWj}bP}*vc;kJeYxND;@td z5-@k&*6f%3QaqS>?4-)vF!1;kf8qqqyOPHJmlK62z69%N)Mh}9@zjA;A zpyZ2ts;_l;xyWFfshmu&n(HPBF~vN*RMYr7lmOTCulq2WJ_3^!MJFG$zdA}!@yjra z-Qulf8gXw*rTkA+iHx)VcYs);8co0t>W<3pGZza@xBx6{I+ z%ST|ulX+u@|Aiyxb-zShK8W2qgmiOyiy^*>%p}ZcgAk;61z2*;Eapk~r(>S8YH4)cSP^x<0&T@iy?7cpS3*B`glcMm$;j zv^q7#j{+CmaU)XzhatDMii$yDmV6FZ+Cc}t900dhhw~q zNHqI!m}TQ>Z~oV{UP2}8N8JmvcznQCe6W(Wxl;1Cy)1jvJy%jTH=0|C=}m}yBW!caE-q>4s z!MBHQQreKo2zyjw(`NotFuU}LFS_b*tFhr-!%Az@RWU;gNm{G7sE2GR3)1+If{}99 z5O+Tm<(CTG-M}%iJUTN3%deE)w?Qm=|Mznvif>Uy1H90$F)IIMY#L7+%-!_`{9}i) zg2D%ohoj=Ey{wWw>QN7kT9do``GRlnc?(T<2L^E(-~+Q7%O+=$n6)9o2Cj*EsT? zn!UmE4&)bOFBvz-eS(Zv(+5@u!XLA#e&OFKm^XQgs~A`aSZXhYkjUg# z5{hskx0tdz)__q1buM&R3%Zp&8ThT-u`aRV=rIXGb)2Vhq@;9^kc%Q!D&S(&dD9n; z>l^z{1DYKayFE_$NKg2pz3H7nv5ljKQT*={dqlKr?t4@B0v*p)iIt<%5F}J@4$6jhkk7Q3N30=Yt^umnRk1Sz1~(QOIl=aLsyVQ zafi!N3OKw^`=7?UE~risgt~}%T?aV4jk`IglA((3{nYCIR-T=;Pi5+OFYzE?P>pwg zHQnN`$i-@yU0;)U#k)m7EwT`5S>`b+5CPI+wmhnIz6E9k#ZHgqr|j?_ZQwHt66c0+ z+CS2tl>7-cJVFejx&(YIfF@PD56(?PyK~SZXoNSHo1ph=Ik0Vx$ zMM+VbDi_(o){WxB0k4ZH!g!3`xPT?TM3&wuePke!`2rA$36uz0h!~*jDs&SX$ziXh z%HH0Cu<_`#G{y(q?qEB3aF2=ZQTOC5-mo-S?ydlu{)--9&4Bq=qZs;GH`Ga zaEwKr=f(d*4FT-7QfaYjJ+Q)~Bq0?9Cud8gkYNhzV2>>F^oHD01p`9z&hm$P(9W7fP@8x6>iyr}Q`2_n}wprAD zMKxlm=N8PP_oWbqPVx`Q4^aN&n5i-r2Xge<8~J2tCZZGlp*FOG#`Utq)4V5jU_rC2 z$}DVaRkN%ryWO4Mi(O_iWfAe2lQXTKTPdvKDG$-8tzxeTLRZAx05J5O zBLi2yR};-Cevvr8*%8;vE67+>2*Dept1yF4!XTATaAs<%yNO~elU^2s13QU$3ap$E zsI2`fr}OxOWq3zq&jT}c^HZI_(N+#(C9>k{B&Kii7}>U{hmGB))Y8jNNaMpU<{&CO$r5#+r^Rpgw(@DH^{3mZetaX%nS1F;5!2 zML`PR)O)rS%a?(ycz^4d(+!49%nV773KpK)gM0sL1*7J_uLx>fOz@Ci}$j?VN7H6-4t}(%B4N~4vtBv)}c(3`(0)TlUzd#@K3b4$8w*y74pWH1!^WTrr+Q_!2&$^-9 z8}S;GJ@dqF#LB4x-%(L0#N?TA8_<%7p~h#e;`c!RhV1~Y@j#{ssKL|Z1?#4!gkB7p;jbt=?uN+894RO8zrqEVdNVaxSY)gg`5 z=^CFV9~cR{hpArn$a!*?AipoQQ@$zL6Ka*V+%2}PYm0cgF5CUQ=#HNjVPPzC6-zH< z`-e#VzMk@yP!Q0&id%hf--|LCHdFw+Sqm!S=bzc5>*0Y4?G)I< z7wDM(KKT;BRn4PZisNtsXLH5=u)}U9)Z)3-&{n$*?cW$Y3LgG<^keN|ejor>>nc4a z!E$JV7yiZ#4e1H>?Q+P+=)>OVz{HioL5;_me43Od1w9P|acwNgwSK&OqCbg%eQ#kx z6u%+m@`-h$p8O`QZSH#j&dkTgN%#B~cdI{jZ`L@!3f5NlZzZ0fn>@ufKXVtCLfh5j z6($Pu5olz-kq^)w|bxQFIoNMWnTqu?-+sI?&Ku>CmtlPVr+?Y3g2sl3Tm5cGeWqQ4JeO`Qe22z9#c5 z6-yj$(Q|1VLQxKRZ{O}*bVW?QF`fk93a)bPw|Nz3&M^3abVA|OzW4<87Bvn&S#`p{ zzhW3BiMCg=YE4wUP4Gt9*>RLL7m-TAg_i2OztM3&oCGYG{H7e;T(`ZTG2Hjflg#%P zaCmtL+tqVUUKx14k=RtJ6uNa*V5jMU?j)k>(JLk8^GFHLH4n355;Lj;6!4evsu{Jf zS<;$P{0{o)*Y9SPmJ3T&1qr)FoiS<~P~yZ?;_TVzf~UX1{{R*(!5woR#RvK#(3p?t zU>hwkemuSWWyiHVe3RY+OL}7?Q?Fo>W*{6iRxGcaN*XCXyJ1o3q2m;reQy$^B}sRt zH9=z)zAU(7)w0vNeFdEOub-`Ka@9*t&H7h&zhv5>zu_x-@VjLebai=_)fc|#22(%7 z%tei@yqKiCICd1{w6C{Vo@6$&<{rY?4(B6>@LJ)4;)HMuWR9R%xue`cd54js;C(%d zNj$06fV=HWT{^vXRfhF4zDfgg`ygSToQ5QA-xC~(8vDd6ZPs})Qu4mI-T>i(iR~r> zSx(Wx%W@j+F3K#;Y){C-#iKXV4Jj){$W+0<9*+M)P}c7v!7dvY?Qy}FkHNvU-Qe|3 zZiS^9wjEsqv)t3gd?w|e;<=QAsN8s7G^+HlZuEJF z=PllGZmG7ACMlw-irAydo7TDV%noN~RopQAdu5N{;Dvpb>r^}miuKK^9eYJanBoKF(QG0vPI|2%c!;qW9%8_^(E^kStGw}{+-*qZqpEL_raL1SSXg4q^_6CXehn)ss zeH`JL9_zM1(qBWUmc4Uhx%2%@+9@C@oUUA0H_$hxE5@d>;8+-pFD9;4@vVyO*5hvx zWY+C-wB*htXhXo{YHN?ffT0$%D;?hQhf3q4Wchl-c0=pMs^W$NaY5};;M|2uZ6s&! za1)vkhV{NvNKpes3Iajx^l3zfJvRLDciWK*vpF?9YO4})T3K{++QXqi8&wg^k-j^u zYw4c;{Z765u?;coX}ri>#MUCAH%6GwQho0N-&8s8k5hv_AgRy>xXjR)wC6J5Oe@Tl zG(waJd|@xZoH73oaza;q6I&pt^iKfGVmhHvx>nik!c_PZOtQgb74~u2`nw8#3&ZYf zS4##WNt>N#At@m~5wf%wM;ptH{sLezm+wrW7>SZed^|C}iZ888gI#^Mw>dw_~VX(t@*G$xlW_Ni=Vg=8sVTaNYT*H1O1gqv{@p4xW%>j;Uf~cUaM2?-Sue&!(^WZ zM|_mMXcMTOSW+P8-x89E*1RX;B>5F$@&X!oN{!J|_V&u&l-e#44^Lv2hofsMMwr3x zVf;C{e}2elzq}gY|<(^5bmfs%$PF%b~03cR+EiiHFb+>E8zvU%7~t6onIs zzc>R~N%5%Yf{G2RFBV{$%k&%oU7JVr*Wu0S-_E~ayoPt=*+fLkLXtjd(r@BP@9z6AI?iwZ!By$a5?xo(+8@(CSV8^k=MLNe{Zg|P*0Ct4 zm&ZSP=S!Xgx+Jcucg%wfAvv%99Ejqc$P}a|Otx3C0PXi z2%3i{>BSH_d=m7%2*r=BH7>p7DjjZ)oJrx-D^-%Mnw{72ot(mEW(QFn4}A!Yqe#eR z>lSGIcQ9IC1^eZYU0#)K-lAF*&Tvz14BX{KRQ%`Cc$zToWBf z`6U@8meBPK35ch5)}iK-;w2 zN+6wo_x981_gT_;fLuLf-H|Sw-Mndcp;y6=UibcPDw}1*KwJdd|Eu02fE9bz&9^?JNsQ5XJTq7e^vBM7Of-_{eg4l=H-amS*erYN zb#x(JZSFH|nff42u8SOHD^-*v&78T1un(#rIhjDnlEIiO_q;#7IPtw$*;|jCpW_=B zn}H0}l}wh59hYTK%Wk`c7Y(WoJre=k$xKHj`w~CF_oh~i`+x-L##-Q5Gd}y6#iZC6 zCfXcUfW+nDlm6%#tsJ4TdhQGK`2vm~-{NuTKanu{>a!fqY*jR1F;Ud+5m*(_Y6r2cA~LQr z9_ztFwe)f8PoEq?6%)_LouJYlD3N$tu}{OV9qo2U|5sNYZHih=_+L%&x(gFuB?twz ze{g69BfjM!i)pi3V%12IFZQCznY+*6v^7>WxVxWk(*E)?7w;&VXr>7)_W5=5edDAv z8TdZxeK4>B>E%{Cg4>fx$R0f}L@ofox+7O&6Tu~M!gv{U@z#Enl;LS8r;|28T3BD> zyS;zW?(mVUD#{rS){@&g<^#-=#m_3;0Jd19E%8~LEGol0&0dRf z%?E7ruOjuveRhq=8;;;te5OioH+eOpn~q;E@?i1ROpgJ+YXg+@ym*tZ?^-0u-e_MW zVrEJA{;d-Enq8zXFp567>#M5WTd!|#8gCW8_H`2aA}dwKo{kR&D=c-lnd_cW8VYRp zdZ^=gzm{(hPafS0JXNgyq}gi+$ZeDn=OLPAl3FTdk9cZp;npu3;^7{j$)C!OzwqCX z^pLgmfFJ(`1n#fW%bQG9MhIoEV-uZoWTu3t7?L&P_SiZe{~Db{YU$fv-hkwU*8+3= zB@$A!AUNgv7q_==NVCpL+zzJy7nS^tNNLSqm^Ba+kh^M8O+}ul>>@K7bcF$o zpdel41zPz54VX}vnV9J}0jarL>blk^#6!XtTxzvQ@M#8(hdv8?%QX@yC0kB&py58O zF<(mn-URSan?6uG;a7fG&GtCYE(?-I)v&NFb`lxV%P7HV2o0P)zG7AIAo$kx^9vS` z?;B2f;mZtaxFC{UZUEs;DzO-kf6|iRFa)SNEr-q#{gWGd9rP{B(EsT9-gjvi&wy@# zkN(pynpl|}HEuZ`YT!qr5+AMzOLip2f-2|YC&q8s9>P~LN}rZ-t76@wPJ}#wU21IP zLUayhvo>e#aGrQVni=uUqfPg-D?8}0wy?B^#XSaPu2H9Q60;>1SEmb-pZO*-{p`Z) zp3nuJ1d&DHJ0RTT!c;WGPVkw15L_Xy7c}_tAll$V_4z{@#vru^EfD6ZqbE5BC~T!u zJ%Ru7w}6_-#^`GDL-Q$N=DA@gG5y0JzkFx!{_!U7?~?LrsEe~>j}-V*2|fa82E{zU zn@iH$K&Pjy;_eCrg*eLvGkx)*6KAEO>_qo3cwwfKitbx!1~PcAqsB+mokajb82;abHq{596PE2<*qPB1JVd@xPJx z&sv*mle*PIVi;2#bMHbgzNiR-PzN#B+{It*SN1p&lO6+{JQ=GM;{=6uIQy)b6TZTq zHcBslYbEmI199LqpJes#rTC%0_Z$8X1P@rG5*nj#C5)mpIS%vA`zZKHsG8M1u$&>8lHpimtiWCF!*Ryv%V=>y08)(5LFB zj?-t~#Te>1knCvUy>bGAH95by+&-tVEzD-!TLY&>yRC3|M8X6E?5W~3Vo*uUhB=I9wkfAi}!2yq()6%e}i%3U0cSA>3^>d zXm1J<{Q3&Ko+n)9G!?{`KL;X}|Dj_*Sub*vOkWerYu(N2T@H-L5!|}wv&XkVlWpVa zG<8T$+K*DaN>V;$G+T7)lXJutq?D z;?ZbO?^4jYs8nA;@kBXHh-)zcV-ihM$(9sLs!MF#wBEj2WD1vf^R)dQB=!M;_*ZMg zuf{XLFZR^?-(RO92pj6areT|mmS7?I{H^LKXHiQ0^1i?w^c8l3Tb+5Vz6_|7O%LdXg2%39}qYNh&-7iDj&eySjQsI42XIM%4^ z1*(S}!`{c+vDPuAH|*G>!bo?GzdJ{`mz;e!!4B`lyDCn<(B6lhjMhxD`DYkOfd#`Vrz1IbY$?Ig$P)#! zpdpd0aL-lx3zpLxG6u{Keran?qdS>f9&OIryes;l>{zU6M~8nqjtff^%hmnU1=q5q=xbI`8TqU;@yLC#YJ<=v1f?M3m@Rx58DAi^4z9U3 zSRz_K&amD78qA0h|Gq>eIjLdU4p@vc&KZ9e?iA%Nb(FsHxyp4E%k*m9q>UxR zi%T%q8i=);yt-N8`2Hid6a9*A8!Z3DMcyi7*JmM&k!oGhpIA+Z__L?UIW?CClEwP? zy(z5*TNB^O6J$3a;14Ax-x#WCd@^S-=A+8hSXbpwe_hLP7Rsa1)93NhdbHfp-R^M) zQ`fA~?f!|ma#3?{_Hi<{frL_n@-wmlVN<;k-Yk8x5yO;Ucey57d6o`g>Edoa+sdue+%LkYf*GMm9N zG>dWe|^}j;*6A$2(bd|e_5(475}51Ftr=gH5)T4IRb{wZLzVH zr>o-@V*xPtKkxbn2IS4N0rCEdS)0yPt_|*d&*$F@e?N0GrPP+>9;p9}*cG9+&&D$~wmAWf2`s`}ip8OYpOHI!xp0FcmeY%5}T$(DF8E{gcOjmc0ukzf|8{N79}c%BQ#j zSklOA>y%{KZbjE@CEcQ$D@(-Gew=y$V5GWjTzA z(@U0nZF6XYV8TS;;=L#=OOxHk)8QUAB7LVp%T9j!ZJk`HkUqTtEoctbN?VN|lFkQ( zup%L@GdeVLl&;w%5A}GDuVX(l?MIyiqKde45Q#kiad7$i*3NC-5clyBFAJ9H>>FdI zD6T>#KG>0<pGV*8YNE zuF`!*2OS~~5c!~zcfV4@*SKf!70)JNhC|IW?NLroKxC94I7$a(BZjc>qL|)XizcVt zgkl-ulFzMp;@+{G&?Pb6HGVf0qM{#1n1nS^P_wRqeFcl0Y_$Q-vL5~H{EiD%@T9^A zo$Qd@o&`jO;&e+1Nxp(tPTE-zWWQ#r-@B{c6m|jJu`@mZ0;BOuz3_iXd9nsiP49h| zT7VucyV&k!!gAE0{h}u4zZ(wfNLKi|juWIaxz*)X<+oR)@-^r-_fyjhxMYlc`V? z1+@sav1^|T{}FY`cF{94k2;gStRC{W7eCkSy@(nBVZ0pGCv%dA*%7=7do)t8M3m7i zZ~ZuB(*JN#NY0a@xLY9`tQt>t0ubkyZ<2KWyh_uOV?EK~8(f}X$gBsaLe&VGe00;FF-FoLmSqmo`DBsJr zd!zLALrRds^$#LjBdbeMlZiVC7bkb(E3vYs9AO^t0v_yZ6Q~c0{NQxO^ADAJMqr*3 z&Xz?a1*#Hpq@$fn_<`>54>uXaAyHW12I8uF^Mx z-S}5SJYzV9yH(^n3~A4%Xsr=0I|=nI*SnOC-Cojx7#?@0CCiD+QO3lY8N^jL+N5u; zeV|ac&tgoxABe4z`}7pvJ0Sw+cz76f$??EbDZhv(j`NZu{&%>l$L8jpnH_-O6MB;< zGp9)=d-j`?xRNQOZ-|sJk2NDAKK>TDx7Wh)EPCd=pgdS)9Ji7RKBrEx z5^Zl)qt|N01NZDgns$7Hg+h5T;YzVXVf#ed68h=k2w4sI2CO8?4Iw z7qpyq*KWk``C#+|CP26N2zU2j4NTwExUq)`3LnT?Q&q#abEZybrJefh1C;2*m)7E! zlMY*qmnJD9_=mF0*$qbPUveiF(O`N-y^73P+ZR5h;yXt40&{jNdFhd9cdjdSPRJ^t zq&6)a$UQSB4k-R>-~)pE*Q7;0&(xZkY3Xe^xkD>+4@@4xTk%DA07@-koM~0vQhHjA zl|Z);&r1`l>(12!O&!^xOdXjV*4mcxqBU1L>ZN~1eB=h~u%55@A=7k~!WmhMul>z? zp}eDzEpxVQ{quY#q^!281L0ZAA&Pf2GGp0t4zu&&U&Uxm%Lp)x>rE5*zi*IE!b=A{ z56Mi^siLz2eCi5^kb3Co1PSIG?KPardps`BW^#I2??Ijbz&y{M@&b0(vdW*n{7*yt z4=66s`4a7p1sI9F=_CWvc#H*5plOFY{=K?lyw{4*{-zv^b|+4TWCosawC zo+bW=a?P%=M&{Jx;I!5bMnP$whU(I9J=#VsZOSPQ4=0gX>1r1|eA1=qM!hqsRVhb;tUSP>uFiOt(4J za?+nEsgJsXcZb%|lC*C!bJG*N?jBeOs zdwGVN5A$E#wn7dT@jF@chnvES;jfJ^>wg&9xDdb%FATY^6@9tXatI%72kg0;j3>(u zqnJ~@{|*$#YFw0dJGDG9AdUE%>!0F#tF%CY4oodrLx1ad%jq~uSBYwSseiBQXV$M0 znp7}uf+Q~`EGOlxRR&YmP(i1+r4E0tU8))~Z2aeKlz&Q^yiigI#Nxc2W(z+#Y?jPE zP^x^JDM9FW9pT+t!@I^D(&B^h&cS(scy*7vRsLII`H54w0goCiY&Q;@ zTwm6o>hwjNI{eKRy?k}g&q%?mF;D7J=MHcA>DzUlHtQVkti|)auPYfb7=I{@=HA&RHrrpFD12HypOhdxTSv&HZOCo>XKL|5 zcge1lbGE~3)}SfThyPOc??V`h`GpT>+n1EuvGKZUeDR6$)hAE)_z&H zyHaj!y#;u_q1`5CFHE`O5Pfx8Ot4ssol64W(|16z-jNu6jITpAX_HJgR`J z1zG!T`>EH|>*oQ>Pq;I!G)^U9@A|)-=K_fbiL>@HheEPT8LY``&Os7&(R_%0zgjxI z_AZrVB!*eBrV-qI9^0M9Hu(1LfQZ(XcmeTa)N^^9nDINm8a!(tKEyH}JT;T=QH(O~ zrR&#Io#O%+{5c5wT#!5N)WZIN+y1{}@0RS67362nB`4AtcLIFc&@erx!A+%S0}reI zFy@)hg(DjS6y_|KEIn76Pw0dqp`?&TSK`T=QnskJ(6j$=($d;%Z^I%70Is9jqHXVKAY%L)x z9Mmzs%MGLN*$xeqG(C1eR)JwPo?V42Ginx~e;MhQUv8xRheLbk&8#(@jLvJRow;Z} zzFI;;cte8fWSjD>pF)v}PMuq~0kMSDXvvqvKu>05cABhnM`0p*0(vrZ>@$K)w+`XL zLirT6StQ%CN=y8xBeh)VO6A)aAV?$`T=$^+R|04&6bnoM~D zF&=xUdN%A9Y?9zV zwhHE#jbs#nWv#B6R-A5qXg?x)7zXAfC&*nSD$;%JG9-Gh*W%jzp9;IDVm~SR;5{^s zlEztZFq9S>$~_SL!Flx} zI?!7wCfKWjxa2e7nz=2`u7jnmBxSw^t%I2yXwZx;QX%$+PYG z>vtp_xz&I1ARl=&fO1!_!Sn5M2Ee@HBPH>uF?{uj=8PXyk}*O{PwBXFtFSSmEg|fI zoi3A#9aOvhPmmwpsU! zof6h97$0lJxoB*Z?W@L{Z?paB3q9!?DpNr}9;n`36R1#&HTwHNr_*KO`pZxwA{2>JsXz&>(}0(NOIH|T-| zsLj~I!&r211`87%>coI3dg8nSmE=WDv+p5I0e$e7dqy2!PEV3cwkr!U;~3Mp_FG~n zQrC}?T;lo--5%+?Ns&-+no-0;y3*{4B6SeRXiKyX*}B9xt&@4pyI$z3uYgK^V+LK$DnwxR$Ex=MQKShQPv4*(x*C<7e-{Pw$z$1U zF=a8~J`@>7X9$0dYENwzA2J52EvK2vX>*2C{FD9YtNWd3TI2ER%O31XmA{gbNeR4f zYX}3a4Fio;c_0e3Cdi0tZ$3?Kyxc_S(y$s997X3l`7gmoz9U`q@pfPS00iXuc+Ep`{=>f|J>?GeQ6=J?p(2!RiAts_ zr|r55F(k9NAhVFzUDwRnU(=54XLOBL+#pC+phFw@8FT_g;&Z>0220J0t8>e+RB+9t zXQ%B352w4PHW-Xq7&wJAz_UT_+!kE89J>Oqck%2J|5YzL0%Bwnyw@fok{?e~jn#G%$zGb+OW1;gJd05}O;8Y2TGPAo6qn z*eqPg3amI$q4B$FHNF-ASU)A|P5sq#ch39M6-oom4gC%uvyvRq7aLTK($2Rv>HPZ}_p@qnia+|0 zUDl~V|ESKx!Mui5FOW54C2|;jfm@r+@VbLd&)xq@v`pVeh3cX!Bq%-#ZR1xAoW&Ub zv^WbkAxDFOiIbZpwE)NukLRvbj}D=jeU6CCVzII)Le5IPa)?t?^Uqxw$wcp1FUZ`< zcog)9yoG5moBTR8X>FF1?aF?PV_JHFS|OKCfDb;XZbL!Cne9mxYkYM{b z)g*Y}exWd=u3e#y+X}|t4M7Y>q_LDv{W=}awR4!`XKXjDYc`yYE5!5tt&6x`0*{RU zyFIsJ*0t9B>p{25eZ+G=G)b{}SHCFt|CP*9`%{7$!SFgUQCO6WIf`@55 zIlA~Ed&iV$S))lOb;vfr^MNSo4+QPF^26Z=4Gsb^_3+AItvuU`HZi|B84_|C-^tNBo z);zHN(s*H_YxKTY_Z=QBrTxMM4{S?Ugnq<&Dn`!8?bJE%i0AL=D=lAyJDVn98h_RG zOeXQ8S3QBD5Ch-)F0yCOR0QI8;?O{6NQcCeYe3@Ke9m~{ssRJV4%ePMzzS`^<(ZZc z=A)!eJ=~tR?zXp;jvbt)ZaYCBHEbrM5imF8Xl`&|>Q89r4vp6H zOW?uv48YI?FwT?!GCaZ4G@v^9ObcRs8qc%5cOg(JDYER(_qw|-PL*Q&ufrL8dC<>_ zGxXHe_hefr#`9Iv!io&YJPs*WAuSj#xu^0z4wNq)@Io1p0p8q39?$%kOtQ~x)V@yu zv<56qa@UP_w<@-Q(_D0Oac#YbzYOH(vShK&1QB-7D2zg|`6YgQapa@4CLX-L){D*A z9#yNVBoyZ~6gTR6RB3H<$bYaJ7e^s~kg=2iQpC)MF?QUv4HvfJeW@*g zdX{cUUa7>t+)!-sso?dNHc~2s1uc&grAU&mXi5Rbpnl~xVf*Hx;#~J&U)A8Rz$Kyy zSxzuj!b`x~MIH?91(sBGHiz;+Kp_YSwbO_wiQzNv#_V2#ihxl2p5ul6FQ>B|BuY(? znYl4gTv9HkYnhv$qR?+2WP=ZBvQ`3r>m`;cc(5gj*^kL6`;1G44Ba3|ph0SatN7ca z`A3HTkhi?!vH{0VjbCs6+q=Q2#-^NQY9v6(N+dap$8w*m^H^_|ojX@fA z{Ci5&p0#<;qp(spXlq-8?+?TW%ML9*h_)L=mDO>Qszo1eWUioW{rO?^NGjIi|7%%|04kO1(Be-df!+|Oij;3D^& z?MO>h3!S1RwZ4OdT7XkZx=)5E`qA%if3EoW^+fyi375$YHP&8JyDk^AuJmjmNbK%5 z3iN9iex5*$`DmPvs^UHcdz?SAtOcyiiFOghuYK$$H+|^$O za!F~gocNtfl6jinp~iosflr*!Igzx#NGknS)XnI(FDx&MCi!x_c%!6TN9CQoM~gcT zFR1WZNOqS8BPDRMZYe2MZxr`jGanv`%H!Bpo|MDf6|+?6>H&U=KC%?L^A-3MI7Mc> z{^U#7hV;1Oel&AMWe0I)Q!A{>gAQ=qo_mT<^&6pf@C$yUKXQ66y2Hu&QNoE0lM<3a zV-DfIAWN%bNZV@<7WNjjBBy`<|w&l+$ONmrD0(^w?+qUNp~ho2VQ;AEy%7ep%SqBsr0 zjbgNkDa4PJQU2=rI+X-Sj(K3PtoHtG%&9EF+L$*r8nj9BwgLH;(7S8J6iw;b!>aIn z3Xf-4UQQaQT+nPFopeq4+s=6ErZY~?Nx&>3ETo7+@@AQntAgbr{$%Krl2`kt?=hhN z-22h+UACGv8b`7k;225Z-t^K^yeu_?jA2sIboUV?bzUZd}(AdSrM$cZ|4{%xGvNf6E}UN%y{ z=W&fXl7WD}S3i^I-9gPL4Cw?zm87B^aq7d4N-=xEh_zwO_InrhmEVF{5p-i2^Slt# zpUTH~UIp5DJ{~9z)R_t50l$8&_ z%0~|3^ALCIzH%SjDgHvKOm4PAd?#=xr(5-1^MP3DMv2bcJfMhYYey0&ciAxli6^Ms z?NDzIdSKYr>3h#9Qxme`%>o3Q61g0kF$>~e@QiiDoEQ43*UPWK}{+5vZF`j-sA;ZPO@zOizyWyMIZYcy{#TSL|4A(4kx zRa%qJBlG6s{;lq*AqJz`tsj?VuW=?)wKS#^~GrKs%OE0V(U@S8( z?|C+YLn#H{jZeD_B*0ox4=-XeT~93=d@2r%^52lSc=HCB3qb}d=@r`*m!hJJa~N1_ zq#!*@Z#jjNEy9Ht;6|8ISWsQ|RzBKTbl8qvF;b1~&6en=+Fmt&Env^Z%thAqKQ;RD zs)5Q^P5VTnClD(+Mlo_fPOpG*fF}A5`id@9TGxE@JFbriEK5 zPS7URaN$6w#Ywptq)wM?rMY6B-kx;*`&~b=>3vT`2Q7l3h!bpX9l!aKjIh@Q3u`Y?RdGBmE}Psjk41E^~jL%tIF%uAo< z2PARR+-q;PCp>k1n;9E(*VdXbw&e<>KhgWUecqYv3ma{$lcsiq z9%h5%@-rYAIQ*;1sp46`D z3M5#>!Lx_7iBp}lrx?YOfyTZX#gBQy13Sg3-NF{TAzI*TlkUJE0|__!Bg- zo9g%lG~OvXq;lmrN4DELrn6gbM7GG}N<818eC8HHOntNtE34QsT)fqzpXQkEc^0U= zV89fU;oB5TzYoGWn8A(-;r|vDpcp3Wb63hIUZxz+XQY~$)4ugs{a^9a|KRT}o3j|5 zuF&PaGiE9Yiw2y};C}O{LjL<>F4V#r*uvT}NgLcUgWu-7YJLv_CVb6+>|qNhr0<1x zU>rEatv?*S0bp?n`|8dhrV|v)Hu6o*<#YAh0Vbpo&)+uP>_4xH1m*vO>M{NcTOe1l zZ|p=$@2{WaZ5q7~24!ZewD7P+K~nTHlXK6wgsMP_=heybr+B0*84pn?-c*?3Rhi!q z`?9h+|7zu~z?5CX{Cd1u^2RsyUt*d-iuPg+^a}AX1gU*}vx8L0xx)N0C?V}&=A5z7 z2=3D7Dew4I@~4x+Y9nsk3U+PPtfNZyD;#h~We1numrw#< z!N0hUmmNj*YU2xWlr+H>$tiEx9`GEP+O?iGYc)I70qQGWzw`~}djq{FfH-f^x-G~B z8OJ7GF>c&&N^qO+1gS|^0y*$Y0od|KH@U#J)6#cfn*sTPchvKUQIA)MN+hMMS1rRu zN!DSnwk~ZyG~K36%ofc`E4<;%>^Dj63A#cu3JBy=K>n_7)~`d@4kD-vK&eyJ#}(_2g};sF#KeYbf?$y(+((H~|B8V*L{fy7IGpRDSQy8@o7WT6p8zk44_ z^HXe)67xQkKw7kT9_YJeg86R48TP{$Dr~iq9@cmIR5m^trie+U(if%+Qaj(kjIa1! zowNV2L5WT-E^3#YZ&UD)vaU-B@Vt~ZY<5q%Z!h!;&sX#(Z~rm{b1%9ljdRZ^dxlBV)vB|@Gqw`=(FE1E;Bjo91G zE>X?dW6^3jLu8n2@&brf{@S}w`1#xAul`KvHZ z_0o^2Wx$Z%6*(@kV>(1DL_^DP6bW*nI!lh`4+~7eeT!5I<*#FS6sdaKY5x9K^D6h< z=qK5v5}A;-7Tkmg?KRf-qgKw*Engty-X|TD^X*Hm;~?ao(Tr(-XMeR6wteYQn4ban zDOm;IRXF%;!9XA4woZ!jA%r4?WLIlP=U)$UeQWn*vq;m&a~3P?Zcue+|o$7jZqj)Q2i$UL<2u{1Z>n z2?q)xrYOVi8lx!Y#SA2^kN1YhKC?qMv%K>7S-M>2} zg?g7loPMm)F<2+Rxe4N*$-kQZcm7Wp4q*q<_WH7tuS!-MYRot({PY~AFm%_I6HJ)H zT%EJfUWm_hKpz?~`~JQ9$QMt}kcxU91eSwZTT5JEO9*nuHIuwmO`H1UP71d37FR^} zk^aDl>_(I*5M$bL?S$UK@uoH0B#eDCK$hNyy@%#7&xlebj5WOw%Z-`>(Zg^~!RdEm z*O9t65FGp1Do%z`T*t|kX;UJzZAXy6mzYRIIY4x8!^HH033s9ZBnj8H(Tgv=b)yF~ zuTFA}O*-1}!Ez34(RYIKPE^xLrq9r~zU?V1OfAb80N!-aR$9%2G#Oob zkkS4Nmn(iq{TaNZFx3$$jtId9sTW!Xzi5FjJR4styE{P9F*`~0)dF8k0%|gJH$m8`1>;Sw z{fXfLse28*m(YXUR$lbjyLU3 z?tNvqRYkTngGc${JsIo_T-fY%8E!|@ODun4=tjFP*%b*u{$tbo*xQf-Wo{SwZMc3nfI%s+aSyQSa| zUyP;cLrY0t)&BE|WYm~2UR>}^N3t0x1HXVl?V?%zxB$paEB#%+HL+|LWUUBUgn0uV zx@>5118R9r#4y%G1i(<`bu6RS!rEU0H&ESX(YeP?g?G0a-sSxF`y!`D?C`+Nsh_&H z#0loPqU)mX6g~!b$3ZxTEG6uGv^b#A9{uX1Cq{@!>rVsTq=Z|1x&?z_44^JE0~DbiKF$DrQZH9G4ajU;_@{PYnUGAl-COzp4QStyTR zxC#}xboByt^;*8)dNB69lYn-T^E(Kp#a)?sXqC&oN^J9j@_LQ1MgFLbs%V99oYXsTbQ@6^ zF$4z3O;{_r87xmDnrZRJ|G()LIA}@9t4eEKI*2W2nL{UY1wm9-1*$gH=zS|R7TBz7 zS%v;H<$t<|9Lp5`Zv)(Xdty1W-3sWjGhLp$I6gO?Tn6}TYJ`Xfn!D$;3)L>$z!PJp z)+1HB&aot-rVgLl$z1~uqsc?F{v348Iln`{qU}GP;h3}GjM;dgNT9w4|5g2!<_BzU z9wEBa3RJG1G!wH&f9HD2V+~MwTJ%^<6FfUCF_lF!>vz*6_putjt!uVzX80B8Un?J+ z1c?QAsuV+2#=(rAk8g%s00Adcb$bon7^iF{{6OmqWb5&sn9pgLZ&XHAX?gpm28~{| zRDKLNpO~d({RNtDP}|4UqsSDCY|_u;Dj70n_wg}XJT$p@$i=b-B=GhG7n?+x!}Mjy zfBUoK+}G$ypr4FAs9o|ie#yr|gvx-udy|19{Q(5+<^dnJ*Ybs z^aLD^&~$Hrr`zNbxx`i+#VN1Bd0b^t)ZlYpcKAPexSSIbBU2r@h6Ov#tsr|cy>6?~ zt%oadODR|h_#Q`ubbN*#W#3!Yxr<`}>H|G;IG<*iL9q}#oC!orikAbSyau{zWV;0Z zJBY1(DCWXF>52yb&|r~HH0S{10}l1oH!u$G~v9Qblsi4f+8td{?9otn~{Mes0G6^49n*_FDx&O z&OwAEOlxbTw$Xwz;4C=o#%yH+47@Y7!|Ix0%}`s=!k63M=KZOoA;0Epju*GZU}!wDkqp*csK z?OlBTQ^Egu&+>vA6r_T?jU?KSP+S(FJxH{u!|e#l5c~_tR@0U*9yy%C{gqN?q3Pd0 zzG8z9@9{Hj&ZBOoCw`%TckpDoUj98k0Q+3x$~28q0?_RI4Iui_j~^Q>A_VE|`hapi z0nS+B<$zZ^gG4uvSj43C*mlQ!9m1$>W)Bc!41(mDc>>}J z`<}!+Y9c+c1+c;p{B5VYn^@{4aJUF(iSTD9Hnd zOL8-gcVqhklKb8|>?%w&Y8UBELmW{}c+W1osoom}(&pSctVGTGccV`Exp2+Q;eYNq%+9D;qf?aM1|A9@ z$OqWb*4dg_?`5x`ru*ORR~lmJ&R*@AiH3ZGQ>2Aev~c1i{3L00JK3o59UA{LO7UZu zgD}WUY^CW8Wub?#W^28mX!LVx9=YO@P)Y-=fA!%up%j_F2U9esC;A?UO6AE9^r~Q9 zGL>my44MgpjO*E6=LIM$Q(w;xkI%d|Mk)97sze2O9q+F)Owq7jAr~kvSe?>c9av^f zz5}@lramdU_;_UbS+8BX@05%u-8}QG_A<~ohNTwC@I$yC2D7y#v*&fOaTJhoRc|zr z-p@gp(E}I<@pb4XyuEg-81KXhc;lp`2thqAPw2tP9(FH+`G%$RP+6rA}q7 zUKNL$zhb+TX0kd);c`GPnC85`4T079>hdWKNFla&h7{svfjpC|z5pQu#;~8n5;$O& zICxKh!`=Eu|KX_gJI1Th0r~t36zQh|zP0vHo1>-zrW}`T?Je14=JEzg;*D#CB=cWD zu4Oa}Yj+UK$5kwvqeMai=pHTeB zcfeN^Vi=sIydc@Yw#b}^t2qszT`PTgno7n57kxQQ`HVK4fp6rxIZ#GMzFNjHWwB5M zCx@mO@p$bsU%{I|bg>&WjHFZI@`&S}$2I=Q*kSY)%;()gNo~_M=w>-1SZ5Md!|IAg z1dMDDic$ee4_8=YlO(J_`x&MF8fS#_DJb*Bk*9)(p|>FpKxT2+SDP@)R{~Tq6ti7xN?7|P@b0l+*t9iI zxScs>{_(e6WUHX`q?NubP38TB6-YC7c^a!j+uZ8`ycHU6>W8i&%zQFVUIAXLAo%v7 zB;8#GQ|PG4*V39kHz=^|I0{7cYjV4wbg!u}btCIs$}`Z8-0mK6ps&BokJ>75FCeqS z+q(%cDqC+shP6K&jbG?<)1JT9kDmrYrf=aBmAtR?>C;n%ZhGkXM z>68@2?}#4#*wIg<4|XL_%9E9OJ_qiwXZvp;(wn6MfQ9O79pU|2!+Rw+PoT8)yi?+C zz0FM!SBOaCcH&i}>TjQZ!6pP`YEXP*Z_XRK6>R1s$WUSNCq}fO*0Lkkol?V}trnTJ z4+asCq&VXzBxEy|c>}hOxYLZ&EPDwNRAl9|!=k2irz=y|FOnX7iBAa%K73-s5NiV3u*(wA`W3 zZ8ignUoXyiF|RIb%qT-m8o3}N{Qc2kowToS4RI!j7?K0D4R>KGHbA__|pna2`D*e{5D%evAesV_@)nd@pAX zQhP&@qP+_46T2%Qe=a7}n%`{lqBt`P2*b1u!;R&i0gn%s?j5{)0)qkmIM7EWbpJ=o zu||-znCGjJ@pT(8m7UEY~B$D)Mm5mx+9`701#YEOWBuj9)8m86s|#;j(>z-WWp|w^W&lGRvRQMc0MzD(@@}mW;IcPu zR{%iBSNGpLokvj=d@3({%gDKb%!-hSE;_4jTLs1`R9;o0`N-i*sm>`{y7&V8E9N4+ zpE0aZfz%d+&{+gS_IKzWBVrsD`cGl6EI^^?wwoYv0}E2(YNFU8UOP{GQ7Svo-H09KS~b7r+eFB~=2W;>kpk1V1~ zMuV@~b}tFf(m!l_cffY`3Xsp3HH!*Unv3@G0cv(wxgc81n@opnsf1ictC&tagWkcX zbf=qb6u%$k{7x0fns{?b$BBpN>T+BMRJ9+V-iMwg6^f;_Ej7RjWL7bNvU(zTR#6GI~WW_U%iG9~0A&Hm!%7&F>L2kMK9=~)fzxX`GNu7wa)#sf#X8FSz zp!b;t1n91EaT&K)Qq)4tYW!o=^CAGFTT!r{XR1%l7bu$73j-UV`RMsF#|G7pZ5eLqXO9uh z8vYDI$I9dx)}S*Xo*;$V&@}hnBMxA9NdrX$rZcKm3!0gihnkRJ=FHkbiv{Lm^Lokl z;nVsX2QInEs!#prDZLxf;SJG^YEW&VGohK+K2iL0?dFh$3A^Ur1=#S&-w(UOXz2hH zd8aH2%uc>K<*3G{>%uIl-|68GG z<*yqul#})xa(D4M9~yzp?5W9-_&W~{lE~qzCm5}Pfs-;sNL@WZz&}VV0^L#iGZt_% zPv_GM+NOx+Kbk3*p)*X@4weaM;-mzjq|ktj!=PJ5i(Ui{g1X%L{|*~90|Zb!=@~1+ zX5gADoZNGItY!4|bcfk0JE+Pz`?w(X#=lh+`Ma$x7GxFsr>L)W9Uc3neX9Aq<<_SF zV%Aww^F+e4Ir!0A{GM{$z5+J1F^?~RPNKSL(jPm`^FJ0O23f&Yfd8L&NaRT2TkwX1QS%YB%fRM0CUy6jQqXo{9?ZE}I|#Xs z`N1+UMnzZ6-A3un$bCnrUgbYN6UXS#>tbBn*ykC_YndI~7AsM~`oV0~zWAhVW^XJRgh|o^Q zz8<$frX+=*&&;1b07E5Am#43fPmjL@>B>{Nh(_w1&|GK^4pxE)l<}XyJYcD+a710S@vvLZ#uH+EHvOiOZIiJCG2*S2je&fft(V}?lS9koR~0g_yu z)XE!cQo&()r1mm_HhA(Lq!NhwHuoD&NrAzr_3E9pIq{s(6;%cj+)w~jv7e;Po8$5* zqD!zLOE4_|51_tHI`+0U`p;M3&^%*wiyq(MUwbio;+P!m%`U-KNIaR2`27W|Nh#6# z8nXBr^3(~!&}`bFJeK*q!l2aFjgxpPM+7xUmXU|mwt_P(-}Fe3-+Pu#*o=7-h%TUd zS#EAtKJJgG(vs548c8H^u{J|g`|9g%`@UyWVlhl=%GW?GMJUIe% zZ&U_zgcRw2dk>b*99;CZ;btk*QSpm`QsoG_+8Z(I@HsBzmDT@yybr6YQRys zm3(fCB5$^skGGiJA%gYVLwzz9|HPU$_(V~cp@FvR`?BECVO&S@T^w$*<4(lgsgG`G#uB*m9`px< zUU6Chb@^><(LHne+I-i@STU)#skFyja**RJ$xS)lEnXQ)Ia0Cv&x(jb+U$yc-Uj;; zz2=c0`X2JsMz;=IM1@6!`~{sOCDM@}N~MZ_ZwPViyx9haK3|Ebe*D0sd&R20LJslK zY7yr?`lGpZJ<8wZx)%Qyzz3kE9EG*4HC5H)ZhVjNG2nQq8E@h&zXmQn?5RB9Y1IQ2 z=NLuXij~4Lfg{eKzIY}oC{Of;Z*Y_>t-1KU0NFJQ{rIOx|P>OQWKrvrzguH_c)&0b(Z9JlOE4^k!St<>$fxzzaS z1^I=TJpv93*z(Y-CgX|9uQBe-VA}7r(W5XVq(mYfL=m6d!6T2Wh|4UKk|Cqk2DB_RTrCB_6Ho_8LmNokOcV+>2gY4PO$Q(fK0VL za39ReM~Vu537!gt3UejWdf|;%5&&!WB>i=9QqYWfdu*#)8$gn;970}+%_IJjy#Gt_Fc{w#4OPas1PoVZ$&L0De!bt6nCq6|MxY=Ld%VOni~wA{h(Ny z6XLOwER%|d^q{zq5xVu2cMXo7b*`iHn9P~I&%al=n~!gTt$S{f5uvD^b148Y+8&w}v5%fJR!`GRlm zMr7=QB*5;ePlSrMvPV#z4Tk6RocktG`_d1pcdGYkwf?WrhROlQP2YM4i1FuCRogH2 z|HVLO);(Fi`6U;`nP*0pgi0xfcd1p*l-|S+F3a+VMidNpHN5Bw>kkHW<1muw{MYh^ z`$Zp~eT0YL^hhoLoAN_Z5+?}iw^|WE0!i>v?zZVP_xPs)C{QH9!x5`Wh zeFdlv#07Z9abR;P!bfQE(E_WhDPbF&OR|6<%6^0jbp%kTQ{T3Ae_jFpWrU+G*XQ!G zBy82C)5>nsRIc809KzoU1*$=p_k>X2>(FLXG*nZOo^Eq#;sVDgk!;I&ZQv0WfL$7= zRv0z=(uOlHQV|%(3kMsTe^#d=e4hbxo=;SztvjEW=?*~D929sG9~VU zSn$NJ<}NPqZV0_3$U#i~HhsK5&t`*c-lhQN#sr1}jb>i(1z+*5A^sW_u~$lCXiQdB z_epXwcG|)Hr!M(eo`nQrdqd#dA#BE^zG@~-;eR&E?RmUAtlSEurFVhCk!kavgUaF{ zL>67GpmH^%482XH*g+cTrt`w}SGlQ|yRl_J`mmy!A3>Jx!U(-Lo#2ESkP~V$)tk(~ zFpUR}HqI4&Z~NA2S@6#jzP{U+aTJp3i1zsmxTV`_qW!9UAK;eXmsQoTf53qblEOso z=cB+aSnsmX0cwRjb@@+l_ab@5&o3goIamK5QCHy>RrhuEMKCB82^qp7q)}jKR0I_S zM34>;Nd5Rgt0C8cXXMh59-=%G7i$N>iU&K-Ww^Zfy5?wxzj-Fuz2*V@~} zX1lk3&LOpt@aH5pVyqf4D63YmPhUy8Zj5Od9NJDC5>|zi_Dx?kxdw0C5&@b))R7+B zqe_RhY%AUOsJLz^-^1bAUwJRH02qV3-LqB=vnV(4b@3nh@EsO81l$aBW1~N0`Zx5$ zzxtB}I3DPGukMD&X#1#$1q%F)9n_ScQG6BQd`w>l;Bl1@5$6bwX52nbqMVOdW!*?- zy1V>esoBHcj8Uf-dlCnoUs|u7luksYWm~^xl@JP!Ic59Q&L|&rO%k{37Ioa>o`qT zy916FCUev0E&COkx)TM1sAR=(_epTZ47FQ@kJjW@=b;UpvTY-w)Bmvia&&?#)3dbl z?-HEYX!K<0Qz9uD%rr^Fl;HrFTi-r1G(XNyLxHzkgSRz}X)C_Tcm9a8UX_;Fupn_7 zf>%k?%;;RUIWJ&AJ70MB9*VR53$P!Cvf}ou>O7 zfGQ3){fA|=IAGzcke6fcXb`3SC9{822z&x}M7*YabAVlpz8q4L{)9H;pxvt1v_SZtbTd=psz zB6=$pzqK`=yK8;o;l}(EuQ_=e6&Z2ZX$UY-@{6!WH{Nej4H(SVTIC#qCCohHaU(<(Yoj zwjt!VNJ0(PD32!IpUdAC!O&}q9=C`SOUZj6oISE9}&snI!*I&F(XfaIZ2W$a!;elZFN|J2ae7s}n zDu=^`O~|Qe@z@Fe*#EfM4t6lU?O%)?z&QgDO1$2yRyTdm&;+!A1`}v>+bE(2pk7aO zhXs8?b9yHs+PvId>6H(pMc*CqVfz&9xdrusa1Z8{K}fcN*Ai@X(~47WvcD1XbmbA3`1g39xtY7oh;0toz#h)=SP<{v=;>GO}N zBVal1Lg&QK_|7-#dZBzp6}oN93E`$^zS5N!a!5(mK)^3n-x0i_!|q7H6>}tcFWlDn zBtf>m?EK(~Z_;x;vj>!m#~t5_3Ht=&LYBn>`PsQ-vgw&TH`wy|4U(q0mAVSh7rX#PksXfDKeTc!fKmw+C?yeLz&~$LF}M(%mv3esOj%i{EL50vz!`uvA-g@Bc3T0qLg6!Dfv&|g)yFs_r%Ovv z0@T3lV)_r8pPE_cN^crcaaJbJEhDL6yN#jGczxM@a@nM$3y{9>{ssbB>bNO*1nNsn z6bHhEZ^}|L$5H|~wp|*lWIfqAg6?)<>LZF@HIB7dn#ix#9C$)^;I>y|zxQ9I*Enbw z*1+c5Vl>RXKCxQ@Y8(YgMffodI&kw1u({Xq^pKxzmDFvv4nWVrnSn4^aY^gc?F(ZYtS$_uC7}QRgIppH>JK_1hP1wJMb_< zcr3U>{(@bO{bXtKR!C;nmqMGeF4cXaPxACXq`eB6FQ}HrYScLY9Gmq9Y>OFQ z6k`=o0iWeic_=wtJojn2mGp)Dv;0XYP8v#OFr;(v*+Gix`b;M&fmLZqePxHDNCAA# zUue!A1`1V@vQ7K25Ar}j0@{=gRBmQVj6v-*g@eT9QY84NNTfnB?7FSniAwFRz`46L zc@S0O*|wj5T|2aa)Su(DgKZm&$S)XBNXVF}=6v}4!|pE>eSWki#nhb~*nKD)d_rE= zk!J)gzMJdHdHe+k7CgF)Dvt`mC?gl_$7fD_*V7A#ClNZxb1BD>^jKlG_nB;&DuEQr zE8>~HQnKVIbh=o+KOex&+73;m+O#|SDCI(b+yM!&S3L|mSpEhz=&+*%Q=WtdAoOgc z9EzNXEryC}1RK-p)>eXYT{}}RRW2z%yGSU!?cFDlSLNhj1YO%P$Xr384v&-jv<}0t zW>n0V4SPsc#-^tdUv;@ZNpCYkWyYeB)So3MTa83vUjVPZQ`%u5`BU^LBuvadEUSF_ zIXj=keQb?7Q74|-CiMboUEHkQ`+w@2+!v%~ty1N?Wz8YFhUG7l+^*{AkJfiIzYo@g zxV=AaUeR(XQs_?CqmVHgDd$g17jpYGUvm%>7Tt< zaGY#=i(h74H<#;5ltl!vsQTVC$Um6!XqedpMhVc8Z5``Xu6p>MWHXVe;+B>qDuTp` zt=n|*($M+t4aXtp@2QYeS@S37BOplv{J*)S(A6C;)K9!{W4P0ZWv#w4c_+;Y2Lnx* zjQv*>B6%urwDCjNq-gdhV|9htIEpZSbRzoJ8r=8%k zW^n*G7*GVmDwtr?M5bT4S=h>RNNAu zwG!Yz#jGZ#@0lj`^ft^rHixQ!Koc<-0{Su3;z2d-j?LYyFpXJ}@%9adnq7>ML8IZW z$+dv}cpILq_7ARF#%Ejl*Cf;*nD#zpdIfaO8;#qX##cCvZ$t?)^D~>>_>}SP6Ntn> zHUyjU!D>q)W)sdb(e;qfUTw)q{_Nq$Fy|W51 zJ78@X{~-zgkewg}Xfs&*tp_2ZXRSZ&lfK2Itk^}ic~Z}tr4XQU+XY${_wNF8MZOtY zMF0?5Mc7%#OZ<3l8ZZ(6qqB5dvl|7eqx5JC`~(6)&^YEv9v}&gb;y!9ulU)pIgtR|k$~A~m%Uy|PLt_AODBas9?uo#|c56tA3PFC7TZ z+~9KGFO&y?IF+a5iZ&brl#A$swQmQO0EV`djAtn~WXYQYt-zLqha56F^!jrW8IcUz zq^LRyQg}xX_jojlaRQQ$Yal^s(iz=^%KrH64ui`bEJpeejeK)95gqitzZ$9{hJCD1 zDjFfJ^FpTFnEs|VOS(y$mE$7L&J)T7%>fi{HH#3$o@-a)=h)0W;%rf+)sydF>b8e; zYNk&!T1QCukN5Tz%^c~}0-uN!nt1jHirp30-7oZ=afTA2l%25pdjg4wFcdKRXgJld zVFxkE(`2;Pl_zRu+b244Hck>S}>^Fc8FL@K> z7l88kdr9=3y%<3;AOLy73L_n)7hhlUzFi5P`F{@Qs zjkK5l{qh?e&j{OP`V3|I*GKqalh*G0{Vd!$lXpYThyuxKkLjGg23czrOu*zx;|soo z{eJsM!wB7sd5;YsQQp}e*Jl`qp``(*X}vttq=+u2%fS0kEV79&WY*o{3dbA!AI13di^p(%fr9ec--WZdT_CVqPT3@+ z{t&MAqZM|esw09hsPR1ISlO*(0yfH2YOni*+U>SjO|k_4h3i~I>l4FAgaBm(P4DwE zSR=2I!&KBb2*Dg_Dmz7Jw&DxFj(c)I3vsh40eB7p3@5qqRREYBl9T8crdGc|5wNhe zp%8@yTjQ2G>oAwzHlSl?w2SM6U7Zc89)ppk!>^rc8^}4UYeEH$-Gjz z=Qyj$eB*HJsCUZ_&(D_eQ!V6u#@yMHj%@wfANqrSfpgfu*%k>-9?MEA@n+lTcuMCO z&;ji8*vH%u**SdEqk%OLXSQ+TG%_M%B)f*bmx7MhS3YF=VcvjY=>5KL5CKUmNu$Cd zmlyeF&zM1?MG{0Lva_+o{_uiYoVo1=+wTf_05@q(eNa(z1j1+>I{&{{%(sGs z;1!p2V-E*|DX0#QLD-I`6+9jG`2?|b@iug!Y=De*A)JG{-FpgQ30RK2`k3M!7QQ|H`)DFnvw^03`& z%7v)%Ith=9rWFn7W<9hUPD%axAwzbZZ#5Qvdg4h5bNf6%Yu&A`RM)c9|=cTAHS%}+ntNY%@s!1gKz7G=}JuPxx_B}6eSezE)bEW(Ny$H zQ}Kn^bsegNH9XicCS!}Itx!y47;|_w!yWvnD)q=CCI5U14F@3jy_HQ1;0brSXh0`S zRZa9Zkdx4eJOTope|CSz_l8lQKmEQXBFpgT1zI)4&Y3(6dqTICS~ZlSctb#XU0*yD zr}j{i0*Y?XAH4DlbArDHi2J)B?pN_9t7RIKvCsF*a~)D6vz$IOlFTI`m=7VC=0+>U zqo2FNd;a9|P$kWa=L=4_u$Yg0jf*zq>~ewph5`5Vhb6qxq}7DH;1zNYJuh#i2Y?Ik zu&K?)e8>*k1bz2(jt4i}UV%3D9hOWZ0?EfN+JVVMG&raM9(7gLp_cr$Jr||*!P}0s z*F$Uzn0$NzAGC*od8sgYSrmi%=lA0k&$nNss;!88!SehcL_}QIw)4I`@VcqlDY<*~ zwG)lbh3%5Tj_0$^j}HC;U(fos=;Zcf7AU~ub`$-W@;|=KysY-S^}gvZB|F ziC~TKgbB$Ii%ab$B#)3QN)npCo);r*=YcFb-=IC%C zwHpdUDum|&e{FEV?n)JIrAr$GQLJ}kwD%0#I=GR${q>b~71u?99j)qOK+sO|u*Cuf!o{2R@ulyLCgmoKK$z}o$%>HmWRhbUc`wI|dKPKNVs`L$_>_-|BGt_Lk`Pv zmVjjOqGi;0Dr&+s0HwlkHQs5udDM_{Q+q?!xcQT-$^BSyTaPZGqYt?D6GFQwuXuVu zSl0ol8|vEfC2u4@XaGI_qQxk6@v4VelVWG5mz~an+ z!pK{SN1Is?79GHwAF<`5A@OP<<^H?#ZetH*CA<4j+gH;GHD1ODV(fxdLfe#*|R8g&f;^Tb-< za*%&q^34;dc?I1j0XMy$K?z#391o3FF$9~BQxo|N-dDP?lO-5@B$Sg{(Fpj%JC2H< zbG)fyMyxCUsr%Dguekn$d=Tn$Tgd?^=4fmN2l$DjZ?D8u?fO*f>zj&r>kUT$%RKjE zV;Sr$VpY$m&JT3-3!#8jtBIyhg0C*rTfRDL>byW=d09y;?*haW6TS1MpFY+1BuhzG zUMTQ|Iy*id{R9H*LcwxJX{3kMRY7VbgRxwhBNLC+OZiPr)C?rF8_kE9@(5SZv?ZnZ zV2;knXpO>SfD9Ej+EhO`P?=caM?Ym%#t}YmzT^UQdamrkJKG|KX`M>6nMWzP)pIfW zZh-9G?ydr-N#SEvxad?{3?a6vMSGA5}7` zoujXgGo-%|Rzo7YXFx+YIkYr`c*~c&IER%iA+9~smrlyxE}V$n61+dvGE!YRU?w;m zeMFpg0`5ICLCv0HwX$)7lYT*6;kJD^D8{j-TRB*+9CpnL%9Z0hNzxjF|G^G0XEGgyoez=oyw%e4oNYId}|s}|}Uzg4%> z=ylz9p|Fsz?t6kBUx$Ko9>|SrKRm`QUp8%|d|rpZ`QD*NXD4MRZR`TbPUCW=4&fV` zr_3;jD@L@E5pxwsESwAhmdy}D+(;=6m}aYVxwNleT7H~p3QSqg(v|F{K-EA)SwREY&|tuC zBqjcC&37XzVgT)?zBo- z$qqK;kh6kXMI;za*W9b@grMZZR;J>%fv8MEhz>aaJ1Fvfp+8QDng`SMjKtjs7r|GK zppS2ITw{6hk%Y`;F}Vy+vk?&L&AT~jmq!=c0!mjv8lXONpA?c3+>C%HND3GvHNCt- zYhN#&$^!@TvNEN5SRsipE)BUII24`CH$ti-M)SH{LV&@n1#ZhV_RF}3py=ckA&Xp_ z`U(<%GIWQ;YE&J&`qLz-o(c(ZtF!w!;d?7+#0;Dq1;f!-s6&HE>- zOfD33Gg##(mA-PGem-iUtlvaY{H$f=q`n%Vo$Vh$VmQJR8N*p(UVqXS zfRwZbqpy_)wm1QY{)l!(>Tp3U$)o6WiJk{E>-mMyv8yIGsWw^o2NdUt^yf}UkNKiAXglTMe1W!H#equl_|$BYVTy%R3)mH7pcK2m}B z`@g3I55>qZn+2!k{p*TG_>x6U#6d*`XxmQ)hc1CD+2>QG4XnXzSAme^Pnav!mnD+M z!uDdVRKVvcJ0ci3D!#0~zfa<&Af}k1*^Y7;%`EpSpWF@xdG%eYRtVRNdZ6!WB0gJ9 zSOOa`VFw14TG*VX{Q`0cBqCt|joCsL0A5u6c_2QgoK}%9Jl|w|b|CIp&T}eskXgeVat>Y9%q$wg5_D^=Qg1KcyR=w)SJpW zT=dzlAk_2F%h$H8DKIJk{&SKe;`Hn6M)>CtPm+AK+WvlpJrh|eU`r)Tne37zZEHba zh_~plqo#M^wXx^E_2{G_zb_kvZ{~I^Ee|jj=Un_%z+WkQ)YN-~<*O_lkECpfM15`m z`dMO5Y~+m>>348N=uneF5Zp~WTW*!rwS&=?^|^(X&6N&eeK zZqj8U2|N>zeK9R7Rez*BlV+E)H5B(@JF)*_dO%a3@Hi`(FpiWSA>d}#yd-2(qe~P0 z1`^*wZQAa~xrj@F_iNTGxg*uAVPZm-f^G<5908Y?=VcLwronx(;&_PFyKGhWw@LSG zz>T?3H@dOYDNx^fgY{7b;P@bddbUMpihZWkP>y zFsFm{REr(XW|}r}YEDF~^l5)+GSt4^riR;Ey2c2=tLF8MI3C0z2KPDsUv%G~re|uP zA2fDpF+A*%ieH%p0NWbhRi9)xme0QPyA7D`&%U)ei~%`8Nr`}$va;N`9es}#NlonZ zKF>-N5$liqA(933B`{&%jtId>&MiQ+3wPh&7TMDZri7%=N6Y+3Za(Bg2IIbgp}_;~ zSngsUaJt&%*}~Ao7qFsmB%b}oa&9B|12Qkt&OFd0MvdU)Hf z$9m=5ee@yn&8=i-I8_PTj}DN=s$dbNlt9jHfkYu7cSY=0+AwceWC;} zplCZE0?>pKq+Y!(ion?SjB!WJUpB^o>3@csPbHf4ZnKf}%9oNZ4e7+~e1lYPQy~pg zs2WgS>i+vJQCa0-59Fh6uPt&&b)1GAV(qn8S+ro=evmCqk2a!cg5gBc@3x(8ziqz^ z@Q3DTm|asC#;g&0W*GD`H+b@f{-j9wtM#rKg{dIb(Q{6`vc+AZJE@{x2*_Tk%Oaor~A&!7RPY32~Ln;-N~xTI1O z&I|5TrFtmvL#K_Pic}cP$%fsI+xfOo7o5HAYIxa|HRc*ACMN7^ zkjEYxLe!>N@}swsKdyoXZKAdmYDBFv&`%5!0Wb4;Dy9%fLbCg@Kx^bc2k9doJ0!X} zWi^yQr7%M2!eZGvWR$21dYB~h%L7$61F7CbEn2S(`jQfcr{fcZBVvYhP3|{h2U#?v zA{wDIn%Tv(7-}WiW$S9ddk=jl(EeL%EPJ~rq>hDJe4>| zLjJ|Id77gc6554*&u`vk;W^t&sxXEhkC-sdoIF{CasvYOFAbt_N2ytX{1^@9buy~8 zwvd)_US8crhn?}IGs$kUc4Ge*wU4DL|6faX|0A$(&c3R$F->WNmBYiqy4VN5pml#l zJDz?*^^}4ub+}VKk^)-E(Fuv(jG*xu zWX*)T-usR#2D0&o?RLt*c5;g-W^DtqVU;u(Az?V7eG8v%0+DW-noe2d3=JVyA>k5C5yx?)WI@08v-rZd0=BIn~G-45dL+NNOe z2j4~HRWHL&-BMd$>L^AluQ=QULbsjcj|VXv^A4F9u6Vi{o~m+Oi3bCW=LJz$}5-O2rqY{z)rKU#-5>M}VZBrFe77Zyfm zL5s#n4rX=~VOzZ+6Wy_sX5iBXI+7{g4aJ7uM)IfOefWfhZRUjvMN3*@-w1% z(kuUR7=J!KFfwE?GQ3_O0K4i`ta7pZr4JJ!PPslrT#ZcsLad1J=UilCS%ixc!xD_z zu&FgAa+aD#Mk`;!y_vD^7-<(9U>BszH*!|tU^wMx6mcPTVIgS0Pye2&Z`lNK#8_mV zs?01*H*8ntd1of7exNA%HD)JL@W-(?oggJ0ZTr8~_cyj%+k6=qvqXIOGI^MNz-5(I z5$JM#Ak|p-FiBKv5Yd%lUU#}A8Yo(ZzFL%djOD8(dMgW3D$|awg5`(aS0kjGY86Dr zRovdi9u1SjTFqoXz=2=8oARbBJi-sLD2VsbD$2BW4@*+^J=!~NpJ>Ix3BQF1IF2&! zXSaM)s9)SM{@O!uN2Br-L|o_U@ny!K_Xq{{xoI-Bz+<0wc$hc!Sd{484sug?l>-c^ zI`A=O@L~5!895uEyeKlBStLH%E(6d2yk~$p%X1Yvg?-uwHQ+BA!dGO8bvHl*i^`_| zBB(ldMU5{Wovc0*Dx(cXs>!ht{XDuz!@pd03Qk)+US)_0z&zCGlzX)0Y*2k~ONi5^ z^tos7t|(|^ZiHON(@)|}b_RFucRK_sJMoA9CnEyvSW)4n@{>GS6fsvY6|w^>qO8j@bLgs5M|kn;F+<>Rsk0lSHyL~Jdo8%bC`1nC_%?~y1@(6u%DR`_ zYzr|ErLc$I7Jci4(g$0Goc#H&lD{eeRpN`y=WWPGjn1yVPlwwo%b4-JIP3x}eEFNl znksM0k7OxrOCoT0`EJ7f^e5bPqQd(rkzV5%a7tL0a#j#`j@v5ne&+mcK+i(hBvlSl zd_|$3T^`RF<_BFV$~u)+>kMB##(x!&lCex^Z*KllN4jPdZ$lJ{hYY;al$#Y|%e9WP z5EJj>C&g}AT&@qQeY~Cdu5h}8C-|?@QZkF@c0TmmKo`ZYkJu{=+E*Y!2q3Zu4B+WH ztx;gET)x%cfS!t~8fZv$#kiqz)=SSE%!O`LT*=uGz!^eEx*A6 zZ#zFO%kA;A(0>m5fp=!MJKQFll5P_AG@M5IdzE_7~^cpiL9;<)w+tR!4p%WFO*yw>J{Y{wd z3ntq*De%pdI5(%28!Yk*fCEutFa3Jg(GYbmkiBm?xF)p`&iUkBlnH%e_4BLDwlYWo zyN6{wL@`bKgVcYf#7guE;afhwqrWA4o}w)JU;iwaZ*7MQn=E%bcfM|_dhyA!PGRvH z2;@tw~&DD@Wp=k)$jpzW?}XHgTp4Yo3%en449~bz?$>E=rP8)!=&zy{~#Iq zT&@!)?qDPCi2uEai@}267;$NO+q<1{>ln$U9l#YdG7QBT&nKoqEmwP)+2>89j*nbS zgY^E}r3IM0?u)2)8N&r#yF#tK1SP%-?uXItCPV)JUI;AJ3=s3P#Ciw5|pZ+F~J`WMmxfR=J!e>)a z8;YKj-PwD+Y5;sFb?dV)CTC6Nn@C|cUb~|asWxT$c54O9`GB`pZyxw|_uM6v5epX1 zFBV8dKd-EeMt96%2)3TUW>|2vdN5!_H^4SvWmuZipDoH3M|QN%uFhZzJK3NL04Z{ z7}_W)hz24%~*(vQ}H7Jg6Ht zUv%|2Rt9wq>bimEFJoZ08qtLfg*67x_g0fb9~QN!$p>TsASkeI#CUt6q=ZkqJu#iV z;NU}$94&yVvzfAi|5hJDMvG73yADbn)4VevY6)@XWx3a=?VrTSX{&~5`A%2xX|ZBG z-f}pR%R`FgOM@&g7zZJ>X@Vzm=d(Ev&240)Wj0q{@167)hosn?Z$y`G#HC$e8QYeH zk6~-|I0`FxJ-qltKJL+0CLuk^{35?ZTVR0oyddez5=>WFAgz-%qLXDqu7OO_!KZv% zLMq>a3k2qFB{7d0-%1!{V7W4XEE>q!sC{QOR1Is+Y6gte9fk);C(H+@AgP4a_YIva zO`8}urRUxjAY@fHp_vGwMy8<8{_ha}7`;1g?~Jc`=%gUGnRW1W?k=kUF3=enauwuT zc3LK9Z(o$~)q@&uSgs@uSFS*Ii3I1(dqeb{oU8vqk*@3>m&8f58mr_Sm8eOq^?tS` z@!Y{Qs)6+sZL7m#LR>DA-agLo!G-4#4q^LuVZ(ot8mkW z4HG-}@mK%WObm6^d0K#KAQ!9Dd^mc*_R_URQQPES0nd3En7lyf$$jD*?xv1cCI!g~ z0TyvDCGZJUhTDuYW_g@~UZxA}Cl<@a3Mi;2G0dBtmae*Upf~BxZ>pM?P0dNCi z%SWpUM{z^t-p>dU28*VbRw^EU1LmNrfQN#bFzfPjg`nfUV{@(gw`fvOcP@)fH{a(_ zyGT3;*Eer!mwFVVk5PQi(t4dSM}0~jVsZo(GWb%)8h%eH_KU}&!xL5C8-o;W+Ec`w z8|S|Zc2J}*78?3V-Q4W7tSF!xd_6oHmh|hf^ppJ6oM?rX8oxKqqTRknlw6wp&^Bn2 z&B7Trfe5>|D^uQ)Jn}j5X?cEaZqeO@azC8o47jFa#)d&QF$-LCCPs?sXQ!tABq`Hw zC)xVcfz3XHg^x)l*vdOPj#a6n+FRl=1UyfB#cdzv!Jg-Tcm@C}n59Mv^cP7q8TKnr zKXI4Ip-c{;Ho8ncTW5!K5yJ*<%phGBZ?{i5NVI7>KOe7Xh^L%av9!J_#U^cv<>%wI zn`xi*9gfhCIAl4zcTQjt!mU%1`VPrFc{c1!i*jtOz@5a$;_mqh5ad^?Q5W8gjvk3{P#^N$d7dij?X_%5X982x4P`i1`=Plxmd!(9{q4L zjR;GZrSf`eih0y%CR(=t8Fv{BPF6U@jZ>jzOli~8vla`HloKZ082=#)0B%G$TZ#ViT0 z+;yE%I{d$h@nkRl`4|^Nk7A4TD3Xx0p=`&+DN`TmMck-sHgnQ05kY$njuwY;YLZq) z>(o#$2R615?j-mZHTC(1VWGP*=})YKKOu3dYxkg@;H}qFbG}pVIKWy8d?S-fC3tw@ z1NYVTuLi{tmtr>r$>fY=&NH$HHJYq&;T`rs%5U)<+dbHe%9Y;tVP}$g5`^Q3l-dBm z(a88GtKHerH~`GK3#eOC%3hsMnJS=94iYxcM?R0A6;*piTvo+)ro3+KUOcF`@Rp)W zs7U$&TqBZ;0&ZmpF!6EJ0wHCCU=-?IAO-#Q;&VJ6)etHwNbr>Njii6%v`&^mZqwco z_J+yhfLSfl>TK6p0sq%817?QZLrm$Odp_vhKUq3$d5a~fZpi)Uz%^OhmF;`JH9HeK zM$LV}zfpc#-BHGJ!Qh!IypXT*ejks16_jFF)o!-Iy^J@{G}f(8y3bBd3V4FMj2-($ zDLQd~1d1&ZiwJ^NZe|e_^3{Voxm9Ywx?{vS9k)Y(GjAOut>(X41;LhA$uel4qtm%L z45$Duz7-o47{6eKvb}t*IV|~dR?w968U^Xt5V6}(SqlE%#`=M?(VnxPmt?`=_lXB< z2GM)KwNNRx`KmKI5cDBxF8#DqtzxD5&oN-ZL`9N~nV{%tOh?{ax8CMH+m^;r<^6K& zPOHqoD6apw3lV9c%528T?*5yZ_ZH@xTV1$8=14vWfmS^&DoYT?1ebF#+H)Y>9FY|o zj-7kvJN)VM4COaBbf%B=`+r?C+iZD1Ol`)fkC!2QwBsJ#6JLa{C+1GgJ|EsvmdAOL?(79bFVRv-Uk)YiH zGg1K86HD>vxLDyG2i%?`29NC$_8_6*k*9YiWrnde@MG`w zcHVXBh^R8!(D10a7d?1u^P6Ukp1o03(Uc5-!5M|G65U@^j{Bmf$%tS>Cyq(+>~uyi-8Sou*y+j5YfTXKP*9KSW&h{fgciTe%uUL%95WpT}ZQ3y>V zwc+{Q0JqHiPyQR&@(EB~!GR$qev@y-v-Zf+vwPpe$^?0^*!Q0VtXzn7QXC%XSuL% zbjPoo?xm6IRA_dPAG@3-Pc%-XP@dO|Zk0lF!LG!ARkWS$dr1e#WHsrdgQ!F)4g8`N zXq>zoR-GCuwZi$^`{irD??uP!m#|B;Kz5-Y*~2X=*0!%R(*qp_JZ0RB}2s=f)InW`VnFhHG$I9<3fXKAo` zy*PCxaTpj#fp z_^vA2@B&*AG@=ytV%}`o?;)$b5oqA|TWKsPx6%stZ~Ejof?|wD1<5U}lHEM?Z2=RE zHQ!E{PoKpf!Ffiq>Rmzg5IlZ<%p=s;N`63pQ(jI;lRt9Zsto-5F=XnfEK$M^#2UXc zF<$wNPGgw6td#V!2lHUj1^^>K+~(6>9;&S`;Ky_0o0nng+ReF98N6t`GV+-E9l?83 zkY|%t`}fFMMcO`QJweS?Rg^kH#vD)hH^zUf*|soOrd>6oWK|otaZEgt`+3t|N5f4} zK+Uj+yuZ$lsPKjePO}H~+)wDJT`e|?b}gnE!*dd?0_H&+ zA^Rle5euEHrM%T8ymc^E2la!JN%OXT8b6^nno(8BKk71}V36Z>TV?*^rWGSZSO|O6 z=n+p~)JRCni1N&;!m7IHeD7c$-o5s3)Qn=tKp#G*2%tVk)Cc0~)o`iplk@l+Z5;B? zK|J^4BX77M51h6Rzep})Kp?W&@!sedp&+NYM`t8-Z(%)`AKN$GUj3z5ng1Z zM>dyD7G>*EBy`D60iI48#`cl3fd@Z*he|b(VD7i0jYU9WQQm~DiLRr~kFQ;hKd4iT zPGZn!>F2_|RuJejtomUf$@jO~`z5z~GqR1X=yk8M^b^~e&HiA!e<12Lmj=x>MPGmM z)2;%_A~soyZG+yV=xo=f@TQI9&Rn_!-bsru=WA7n1pt{`+es^h&^pol_MbLkc8~aR z(xDRix3?5(0V1Blg5jteQ-zXEJAP^6Y2@=xbDngNWaf#+PYZJJ@twn_?Tvda*t|D1_9>nwU9}ZkC7s0ETty{0-n5~vG#*zf- z$L<+P3WwUC;-fKMh8-1U5+E!K*#8F($sj6MJ3Og$un-3zqH=mDHo53`7wTPgQ4>n4 zSPj-s6=`Y^}ruu0`0_~ryf+FrL7QB82Y`+HEN_yBfn`O+|lwNuiSjQ78Lo2)lneaze0EVWw_LJdO~KxSQ(oAf)|MVrBeY{I-FLmq_%cvn zboTl-=YRDLW=MGx+{7s%+iq1k9@i}+V+tPu`-i72-V4%}F4EhRVmpno77hDaRXJ_w zf7d$n&pW$MfE1#1$(RuwXF3N8o!f2k;1?WEH>u_Y)$4u*ZTWeOrsSwr5EpyWHb{qn zUFy1As=5b(#$r`v;X$v1S;I;|cK0c}nR==VZEPRVI>0O~ek$~q>~i$@M1p>O%Sw=+ z&9vcTndy^1aNOE*jG}7>JIU}K+wziuP|9=~KC?#bD02F!0YG27iz?mPZ|(4RCTwf^ zaZ=*BIi6GqR~+&?WrHvFsI}z?<-G#_{jM%rOZW;O^CsYG$GN>?Et+d#z#!EP!@yIb zP-mK8bnszg3h|Ltu3>tycTfD{{+Lhh=Xtq6~0eg&D$!&7ca*u-Z@t|qj+=B z_ZzcwAVh=99S(kY_O^oq0$)fYEhL*y6v##Ff4|1Ox?;EWbqkQWKkjQjEIjvlX1J?W zhAQ9&n+y_s+ym^ipYl~8YG(%Pw(;9>!#)K>%&q%#ib(BTCqPI8R4RFc`&C5p*sXh! zR+I(NvN5=?g7-;GOVh8!_{G|SPe^Vg1+Jj2rjM( zCfrRrwGNP`?Vxi$=JH*(Q&2!+=#Jwpr>Z7Br4-(l6cjyS#}A)jlRJ>~h4 zz=N|_kA7^~X3mC5_5F9-V)mum!u$fN&hcumFV5jrD3l`vJAg|^;}VGR??Ia21tcf;!pBnjc0uc0Ws%V|`vJDCe8YP)k9(6^ z`x(gD*w>UfU!UQ$dI6T!9sGJh-72>=%ftrcwF`hcnJs6Q;VqD8i($^JE2CCJf3-xT=BL27gSmQu7*ju74FdMR`OtL`ceg7w+nk3%dIS_l zj@B$4qf8efRzqHyP$;Xv(U-CYpd`Se42CZlSY=(Ic=fVoYpLd<>giO$dbAdv6$F;l+Yt0-Aybs~r>&COfrV1ITHRxAYM&bP;iOCEhPf zHd5LLUQJnDVP4l?ipvB_Jqb_Q#O`Nlb>BydcljTNVq%|^laIQxfhH&B>8qn8ZaBv1 zMGPA<3)-axS<(00y4fjwVoqgSX)^1f(=QkIxdN-URaM=AcMCt_o3nRAN`uKuLz+0- zV-W5(e6i!dTET0(Shqar4NoH}boe?9)2{hIu)EzkI;w_WYow2>Fth$tN9%0Y*CpFxqc0(Qkqt*+V(u*6f5q)ACCY?wYsxdp=7V ze<+5sLT9KtZ||1P6~>>shyy6o-xnKe)e?(RGz4*AzGr>3%!qbWSa@;Pu$z6AkVu+#%^L9OT_Tptgzx5#y(Zxf;Ei&kmGkGkg_n5W%G& zLsz2P55Ae^g<}p*0_>HyO}r^5k2IWq}KVxY8n%a!krV^34O;WqQs*Y z+;n$j|8Bg~ycr#+Q-)Za3rp|;>@whwYFuV(CS4){n0ddY<^5Q*j%YHGv^jWiV0o`e zKyv#07}!I&c-z+6%rq&RK4`G;!=@WFun36O#$k7{RXA+T&Eq`SRZD?g9iiq0lX}B{ zm%I*ct&+lQiEZ89ccEBT3)iaMMqR#sU#>gaNU zMyf~Yx)pHW?S@qegi7)>7kj_V^u?JB7nh{2#_@=2P;k*b`Je;xakn?)3m{m)hg$nuU9;h3pivw~Y9tRjgMLJS8xlOQ+)P z1SE*gH`4&$Y9fC#VNR&fMT?K~@Yrl>dp!YR7>8g>@fbOaUalL`O?3!+5Jnqz`A#xJ zUsl8RV^LszaX}>U_blwJwN8DOp6|4>;73mQOaZE7<`|iV2}qkSx-$gA)TT(-sfE*Y zJ>cqX0@rfu{_ZE{Fdaevo&tN(SA3J=p3~#$ZbkTlNSSL&p?Uv7{JQUdFITFOYwL$c zDm14>2&yc;*``n0E77!8^@yiE|7}|O%djKX8R8o1=SCkmZ0^wh^%Jdc@91JWH@C8x ze9D%bLEMzv3vj`Q2`m>Zh)LoeHCnOr5;6j3^G*wNj)byYU>;>Z-($|8|Cs%`!d)p$RX%&7l%!~7Qdf_i2+w@< zoT`OIOSy{bc`j0+-#ez9jbu4TnV%}Un)~15-6e*9ug-XlF%b_FuAF8|`a}07yx`-}h8#DXbS(hPyn3A|7m(aqBfuzE&p|c>34*Mm0Xt z!2LDpi!eT1*$yOEU_+l`z`lJoQuKJy*6%AJkn2Z651)ffu5NL`x1Jq_iu9e6e`*yG zIPpn-Ri5ovhGNzacJ!%%@r84+g`Z&oFM42kJR4haEF5nH!9q)obdUem$@94nF-i5{ zK6^6g5mST58^vYigJ%V1aJt1+6O?NTZj`y17}<#e%9-vFPyR2^y11Oh+{2Wo^?t`y zl!ODgu4l<4cX(|kx|vZTlj%E6gye=!0t|J7+PHomgEp3xaE*4{v6 zaZ2#B0;n;ZJ~~oiU{|T93rgYWNbzZU{UylHe04Tgpe)meU`3Hio|u>6Q!rJygLzGB zHQaZ(w9Z}dGx_Qv^;(Md8hd=n$fpd2d^L$}C7Z`#r*9#X2fhw>(GIH6@F6J%7W`o$ zkEQ()r2B1p9$WRIj`3eQ0Z#BX+VhTcDNYh6^4+XAclgiXs~mK{CR^Jd&ni_n-~U+9 zd)Yhz_bjKQ(dyHRiAsWb7UR*Zaijv?&Yn=?NU+~418XO|SK>@5;cUWN#6&Nyy%Nb3|rjWgJ_wj(KFg_c`A8^Zr|(=REgw zKlgooukUqT_ro-NwMP;)SfNwTzW;jWyEu0Qr;tU3owK|iUY@ht_{Sb#g5-DX_fJ|5 zPWF#%#2p*l4vKm|7w-3i`_0m(2JnxWw4=uPMmpAq(yxDReEe&|#M8Zm{~Ked^~2xc z-FwxcaT)jxkG*xV)+K!;TK!(ZE90{za_2FNsrf`6Feu#NgnUwxlHB1M{4#mr_x|hx z%9EwT!{C_>UP*U?l%Zo=w#PvQBnpaYKcoCDM_O&qu*3(bR64iS#KiiaseQp0B zql|)H?rKt8wRYT=~>zk_a`$sn1Sf?hrOi&9Kz~H;ta>u6%p9? zjVfy;>bO&iE~_97%i>S>OaiJ$CO9|2FWg^(;Eq1TC9bxsBMXfu?hBdkeOxb5iqWRs zgt)5d$azrq%OpUH{du;Y((lzdAJ|WgP@G}G2iWaYBC_lsXbC#A37)igH3E!bU#*nF zGjx~ne4OIeg)GYMbmWryQ&o>cnUft*>LT3ivhU&F$$S>`aJ~CYd_y%1(~RH)?OZ5!KXD91mR@YCeYhOFUu~+F$)?=OwyhQX))^7A_sJ5vENr4P5O&uGB*%O zaDj75mDs~x0sn<`LA6cPw3+JaXH@7e+p6rNX-TC@Fqm+5YfJ;2Z8fKz>pM8fqaF^m zXMCzi{{Dl8*z#0Tg=7*wbE3Z*s_z6-Yv>dq7!ZlFrGu621jP>D@K6fFHYVhQq+pZz7t)b{W2 zm8o51yk+c~BE#gZWKYc8Z=;_tD6G2<&VmFL~oNU0o2x`|%Xh zk|3Ct3zp6G)SKoVKNew?;taRr@TVzegVGo|0c{7f7PPV8Y;ium7&ef=D zhLdfxtO4A?wJ5M5_Q zTZ2p$iK02R5~x z*Grgq1Gwj`O)=k@e&0Kt6d3*+BIjALx~mr47M956ML5aRH4@X~^qrn1%e&%5m2tyX zacGowCH=9T`eV8La}dq6#aLdJMLO4&hFA{&PD>~Zf?(AnzlP}Waq93@KBR^k*{st; zR7B2lj2u9A^ID>=^iH!Z zQkkPn!qWB3P%d=31BJ-A`xMRe8Y)CkYm~Eo3l-fd348G6I&X~1p6_4oqksFywHYD_ zQneCS&+@UNF}PY)nBT8hcE9!x3L-Yb+DN!~5F6KPq`*8eKDpiExq3Y|8!4>orLO0N z)C0JY#TqQ&W61R%5(VpGcJ>m=qXEB_31z&Yboi}V5~h>B{j=lM!>rh~h6eBmunu~6Y44dz`H!$OP=1JIxu9! zUoN0+Y5me7Nw5mu<>`!P3BOvc1HSa9V8Y9w0-O_%?PKIjc_%YA!2pnURPHR=@|xWW z_D7uNNsKqML^3%9^(H6whNf-r^o*H&b(R#i#XqUoU!mHRQ^|(JazB_Cn&Ga%!}u@O zv8}QP5kt>coSEb1rwgA`B=hJ3TaEPH{&~W|`F^$SgBa$Vp(w|1h${f|JIM_a*_Bdn zW^?u^b>#!qnZTF$$PD~E^{`-X%Zy9Ux-mCK3%3sd$^oERH_}))vN+_u8;ohGzE8)` za9{%cp1Cz3$z8%*%|i05>l==h8llOo;=S^=mJ*CIx%bCJ_-Urw?sxvRKE8{!1i8=0 zai4hHw2rI~*QhC_L6Ff>b%se5pDsNwLK(shMM1R*}{$AYbf zJscSIZQ<#_7c+tGf8L5aQs3_FC4bp#-rG$R7nG-g4SvDti{RgP0a}dIrD_&|4qp^= zcx^i7{;mi+rKBqkGm~s4pio-KL_>(F?HFvsoc%`84%(-Zj2=ckLyw>_IYRogU;=^z zho{dQwJ4kTphy0VEKkK(D>_UjxyW<&pN;d)8`H3v(=4-&fH&~R%GxoLJ#r}M9pg{3 zuiL=;r6IDgRct~bt(c#E6K?f0Jj;+RjIys$F4{zghtWh~{cC%G(%uo)4SS&%)^KZ1 zg-%|^t##606%i`fkz83%iH~E@SKbtLtb)e+f%`(q&%*7+&xN>D08y5 zTZ{N8+~gzw_=x;J+K3lk?^&-Van`%dB#nJbQ=&n=G+zjzY7W&h8iUXEY1dJ3R-HZ) z4PEBv*sAEL#HP68_nztyx@)Sv%S;coe-N|K)b>yBV>knz6IgATbxqnmJ0ws_CgV64 zmjzC;huG0>!#Z5!PP*_!*j3^WVtox5aYLuU;DA9-_XU`U`dLdVl?ww&0*Pn-DvC`9HK>*b>=tokgl z)<}?4u%d4>EpPh^3OhdwPCfnoxOY+>Ivpg3;XVyc(Z2>%KmU_VGchA3-a@R~?txGmKlZeZDYX0kDrO z->KP6Yk2X_KN$J+bz-7(2?dql%{DjVQn-+&2|k15@jc8V9`^@JW%*hl#L=bXhV35U zF})>A(@OjFF0@qDuq^NTzk-G6OPnM>x_w;JI3o6e>xyBh!#n71O48jhN-)~Zcww&> z;tG3ys4GS7h6Qul8`9`LAjPK2V+$rEe!{;XaH>LIxK2J^=1Ncm-3s)|Nc#$vh7Zrn z+KlttKCUF4M)6eIwey7T>dSc)ndkV>czKj=jPD-s*Tm@_k334J>NyuqYJ)j zPjSbK9>w->WJxPc$LtU5m$F{K7gq4NQ^Is4G0FC}$WQs0uN}jP^V5Papz?oyKnac1 z2^l_VEU3`E*ptcF;XnrS;>I0$&T7&KZqIU80L=C~$x9{sX{h*y=%cN(rse7lFaG^- z;CDCIcd&RKx$Qd9v}S-lhtg<@Bpvs`#h|drZA7$!pVTQio%VNB%c4|e_LtodnG9Mh z+PT);Imx+du!5A369&#t^hpxI{OqJePqjAOE$dY9R(7?x{ZB7Erq`(I^r)u)SHwoZ z9;%sxo*1g17@B*PTlEbd^Zl+3ui+B~1x@cm5sQ*MP~S-x8+yefDZFOm+dqA$pR6%^ z_NqtfhFZ7kB({cfq=Jb(oPXr1nyS(T=n+*&S-9Eb2gG&y@_*Ez+Wf|={JwZkod~!Z zy*+2AefsW=cm;65I-op8>O2eQ|^E;)@B@4pqWq3jZ$HG{~mbz}}t*^1V zFJtD$y9c{OsyG?TQ<$})9Py9Y5u zrj`l%l)vPU{SmHKTdCRh_uR4&pOB#Zuxt*(^lZ5_Vt0~*-OR$n|NiK{kcr@pUL;IT z1QnQ%qWBR7ZCb=HK8kMLskIMl;tZ4ihzxhGiDNJdLdq<5>k>R{<;HU3>xuQ9E*IqkBqySO9xj)CD%bFN=K zynY@BF#tZj9II|?7r!!&IE&e_0%zg$n-()qRJ$zbho6*?bE2G4D$wad?sF2&M(l?yI@NigS+A_mZ!gtZq>gV!8D zGb1d^6pP)EBebM-0>7VWaigG6`zYV_Nx7=b5Ya4JozarWyM?NT_Ab14mKjHH;Ul2g zm(xZHOcUcLaWwSCH`j=GD~o!&*MSB1Z>_n~x9q*l32KzHxcG^`YSVOcT3yaX)@BGp zB{Ol#z|1gU1oI}GV(Nji9aPQ{M6|aF(3*b43@2k^sl2E9Xch-QXX%@naHotHRH>9L z%ZmR-cpIK2miZ?=3l461m2-FzN&j$N-mmUKwDA5{A5sg@Jj%LnR?oWq?cUvvoUt0S z(fFI`^rF5F_;Cj<@l>3Ct~%Yn1UvkK%j^%iS;1Q?A{&4FArrBM@$`8rEykW(k<_Cd zlNQSF|HT1r-RG|&bW~Z4+rePa<{I>4D)C2X!3j`M?oFsdM_=*jU70{rBF*B73OgHx z9&p~VsffS`s#fJ!b_+3kOz6;NQPK?9b>$(il{;F8IHHwkFO!h=eg&R4JyBP-NLhgT z7naP@E?}qW&R<>@pg@XGBsrn)ZHF1=#t$04$p-4y(BK`F$<+2QQSlBz7}H4@VeQE% zQnj_glcK;TS%Oh{9C#+7iZ4-@GV#zBbiFipRi1R zF$svA@{RCwW-bGYpEHD~Nr(5e$s{|CuUQ&z0f|Zr# zW&v)R-^b`A3$3_~Gt}5C@%n9qIE&}w_u)KndddbtSLPdPTLEpZfEHoRyDnRaYdi0n zQT1O1(8uX(L_d$3@ya8cp2r4gFG#XM#w57JY~^jWlA$e1`u>x#i?Ss7l@;>|tx^w< zlH36VU|c+(ct_0MCwsC#7);Jn9R)aLQWj%r-?N#SJFR#n483_eOD&i8FE;!B|C=5Ush_4-VHZj*j-W6J*JG-vs6Y_ zV!;N!fo7wG^8-gsD9*dX+e$4AS62KdKp5fSg8ADa!8lBRt(twyJ*c6Frz@tIUkO;2 zSHBPl?fTYh<+yuUPs$w{*qK`V?jEJP1e=yYmm;cQi9{QSL}S>WfOS{jx*y8k8D>=kV=30s0fDeNY?VEbBEKf&|m2m^0TAd-JEQy;(rJ{#cLfMC|f! z2rVqXxxb0Q-X^#+?e44{k70p;8dnd*mpDic>Y3$z`G%(1=kJzB7T@gjD__8^?4>jF zo$H_P-IDaL71pntJ6p03)bp~tKdBEBe+WOksFgdZ_-e?o*Np;(DFb^VBC9 zTv~-(CG~Z9Klq=oLa+KIh?4_aXK2c;i|18eq2RScxUTt6RT4D+tZAd^uIpNrCw|p9 zt>DaiAr)1n;JDBFq*^BtjJ;F~C~fvHr*HK);d{h)jq_U}SMPD4ggf&$(oxfMOI$Sn zJk4lFzmUVz&K#ugBt=HAf!;;Zdl|D?1Xd}vDoS;t?Z~2A{vbV$x-z|PsG6Vh+TJkB zZSnMA}X&Ns*Z1_ z)@=_xKf68-EWQl17f)Rn(M(%~R&h6-%Gf^%Ndx}c>iutge-Pk(@)bmzmEZsL&+q-| z1#~x4XP17x4XnL|v-}?7hm(h(hk5QTxq}pYcuFbQA>)l@Wf5D3nx4x+2NncxKhWo= zy$ol(kRbU?S9*M@l=R)ZCl41$G5HlC5qCLm#%XJ&D&$P7cfsm4f+YMGXB2k*%xh%P zc+bOp&qTdNo_ibt}7rPG>c+~#<4e4Xn-CS92ieYeKpSrSL5?6{NnGzMT9S*nkvU~F-ChN z>>gz35oDDH`V2$DR+!%2N7X<;PO*Nl?~Y@J%FxEmzhTDgd@i`ndiG`c@yE?JILS|w zfS0QD3_>@o68XXK>;v@#xcj(d9gS?)MHA2@HW0T+ydZZ7tK)**#Y<*`X{-uDUkSjZ zUbfJ^uTGkI`MQMa+KT2Mt&(iHlI;6-pr2>uPm(}1%iy4LvLgS`hX+7|b-0>`K43k; z>|1n`TnnL1Nq%uZIcDU0H|B$6F%1u1hmhyDg+;=ih=w6cfTHH}X~EQPdG7hje2dFZ z4pIU4QNT#v{g?Bc&tEb&BgWaCX&1~LR*dvvH)fvz{{BlApq_&!Lxo^Kr6+U=P^q2K z3|>~}&lUU~Ltl4Wlt$sip53W#A0uw#Km{Jg>!tUZhBbfEOK-M!e|%{ON7c+m>cqyd zWMP2PUZKnS-zHA~m=M6JN2h$o-I)(xsRwBhQ0L#aOtAT~1C)C{DVls)2DS~oxR0eD z08V2GH$*;ZjEtY}%^u%awhxsa6!N78Fir5fGVi+iwJrrF%wT}R>K>`6$e^;LC35{ zo3g@7sD-hb1*65R?E}JE2GtFbPiyH<8NsyFEe7@-ue>u!Isg{T%AYxK@MA=9s(V$; z?-;PgCO-t^l$Z0ANI7z+C+BFU;8mY#*5awtE$i}v|Z(oVClZzDe{rxMZgn;Wr~xlxCVTe*kM0+2j=~KG zvKg3?VMEPf`rR_Hy>?ZG-nFDVF5U>$8KbpaPB18;jMx3*%Gl6}66@Y&UOg|!OzJyW zpBP#%DQPV%kyP@ZRiSG%w=N-sC8&nAzuEa?ZIG74rhB1fGr~-(c+Z|}E*`}ofa>89 zDCxL>udjZN9;l>unB4lf=F`4gu`VoMiBWjuzi#^=@&^VTrqe5< zBEfO5fQKO+P>7KxgyEe&X!VRXkn(4l_-wrrK-D<8%#Qd;=`NJ9Bz~W!M5bf&7TpyE z_6qT+fW@Ka@byn)Bk^M+_ZuYKjU3qR#yxjkhF8DeFf9vL!3Khhv=9L@&*PQxhUF;- zkQ}B|vHWO9SwiKX8?r?|Po@S6tDtH@9}3Lw2&ed|r~2t0Isu*dPIaxtk2$x|4%`je zx4;d1=UZed-S8IkEnJu46QZrC;kQq!z&gb(733%!fnc`a1@%CdQfG1DISOxFA$NNwp!*>ZQs;sHQ`8Z=5YZOV{08z~X=tdZ?2Z}T zoH0I|IsWn<@K25gl&dkEJ2tkvX^7W*4clbDbPl_C`hw}}4OpLZ;w^d!gZ)_5S0wfD zhq#w~cCbFY!6fCBMCC9S9e_#r`%IHc<*P5BNHRtV3$QIqa95<=4cAv!3esY?8WxO} zG%G=N_~~Jp4!>Xbx?o`73#a)HZLIM@KR$x~mlpEkbS~vsYj#vof}*BZ=rtswAh2!i zFTTs8Xh&y_gvm-nEM;tR-gV3-sKbL;j%Am5vo@1?{l0s?qEq|3X#{XAwGJolv%z}8Vjyn;Q-iTK&=1p2;{W7t=^(k8SeE=KIe@Ov8RM6-km zh^f%Or%?DJ8Z5ngzj(_l%>i{YLfVkthpV}V)`TNcZA8o~(`4o2Jm$ll=;3Z>n-ykC z;Tz}Q?0|km83nXp_w;;8mjglj3=xTnhoy~`vK<|0CD(4DcTyg>E)Nm4p}OA`ht%`o zXrd@&q0sKh>wvR!2wgDIUSL|7pI4!Ky9cWiAGQ(s(n|Mu_OJDPOX?tz50rA&#`s(` zb6-<=6HUVJHGg7@A?88xFX|AzyCKM4`v-*5ioA!J_1^^UqY48@ROYa!>&GDC#Z6od zPxctn$0$M8W|m1x>me$0fUvK)U0rp%@+(Srt{%)wi7MYfg*l>4gU3uB4$gn=n6l*CMO(oCqEFIW!CeL_U@Gw^u#$pwe0_3;nDeK;e? zHej?sU-359efSJ*7Q=VV*LIgw%}{=({^`4uT+(IUMla7-8=U5w=4&3z_B4V2^{2@%-S^Jw9ZBZR00miz%tr4q_ zq1pk)EVy;lo+r7VE1ukc@3>lCxq_jpBoC&wZ4fmaR|cIO~lom!Qv9@D~m;{ zzM#9X@>ZB%ve>o`@Nt+Ja{%%OA&bMVv2U@0Gouwv(Sw#(&bn|uvc6udrPvVT#!h0c z0pgw^z~Yho=V@o1_;QkrwI06$WuI&i*KLmFpaJjBcJ;{2NH}q5;PA1tN^Ks0he;jX zx*9T1Qukd2Ze$g1W%ZWpG9UT&kFTDOY4BZ-ohm#(6}Ba!kPg!lu`=_B&iRuOTeODN z1NRhoPLq{}TR$oSQ(eJcO^hE3G-{cgB<)6-PGC*y=YC!S)?`LG^M4>h!mc|LRhF;8 z*L-dcrN&PStREmElURp+0Npn#M$yup+ah}b2hxoiI}-(O8O_@Z;^Y|0EH;r}h4gsE zgt<@7jDnoT$~B3oA4(~ON^x!5hK3ouUc^sCy*HA`0}@1UJJ7zg$K5xE?U*bh{$Q*O zsrHe90la*NbO(k^1op}aV()H3;p2SV294ZT0$W7WSc0zr2}d;`T3iEKCEJ{ob`#NA zb}DVf@_8rteFFyX%f{ilZ{<}jQ4tn_4L{5NTzE^Thn!d$7HB{+@t{-6K=&cWJBGwo za>kPEjiS(c*>Qp6TT1MC1w>?nban~|U>C&El24)N7|xWoMz(%q^3cpjUQbf4=d+5B~Vs{Al26j?Fp`ShH( z0j&~qX`6H9{sFl#a_QmU%{&p+5VY9Kw)gEj=Qt31Psp#$so?hi(101;_Ki&r&5pKv zzHRrwTup!Jc@CW;qPTM9Gw49Fi>vjOoLCWzbEo!L&&;v*nQieQNRXuHujoRn0^DCv zg6PTPl7Yvzwv5^tUp8K6%)3?$qtI*iFH5W5XQhjMoeWth9dAOyfo|n(MDim%XjQ+u zPZ`*wAd>N;g7XJ4cOsyv$dy0a`s}%U`QVD6=!zl|pd}h$H45st^bdEjzF`G)giU>jSKB)U4S(_K(+Bn%Op)!0>V*aOUJe{D zFtoON2A%38orVG-lu$}>mp_EKqGYwZqx!^2+vshNWj{do6Pjr+m6hP!6AyGeFwU%t zeh?IfY@g=NZJUAZZU&(JJwKQFq^fUta-+ffdS@Xi=YgrXc&lz?o}bhY4b&EXFAVoU zivmg*O8j*wMT@^2+I!Q2J}7e{;qEG2q0_|dM>&k^OAA!|#4!p>( zbFIU>hT}0aw?86Q?cMv2KwE@x@y+DLvEkmK)D181|0e+DD5DYs2yI$bSvL~6MkEF5TFBpCi(0RS;!xY9*4EedW5=RW~t-5DkcR@QTFe7$F z!oUCQWBI)CfuQ-p68|qytk3C+H$VTkj+8FJ;PKdci1GtpjLFwh14A-fG|MFysD+Ay z0X%o^v2FahU%lgu2C9*Aju9e14j^;~{kLI5$Q1s_(bL~%XeoEeI!HX}5SXEOI6=-) zjo7^3Zc__r6jR5C;?TEW^9zP{G2H8L4Z@mE#0oo~1sRs9EoVY=!|Qdm(HE}$>V`ez zn9aNU6m6jCy#raROmJM)Yf~a!*$LgN>c$L3(2;5HqkXiwj<0i%-wS~Wk9~Eh5Kj-^ zux2`C|7kph&+3NGpp-FF!-S+XREv^dh_Y~sxdDv#mCIG0XEg_$u%z>A$rJDeu?&%j z51%d1amI4hh>+HpObdjw%yf#1j>&RCS}fG*mXdVI^2IViFs{3>Mq?Om)WkLLYwj}V zEEK217+3|91rU1vq;7ycGVHjr6-bR!^!)?eSrK6~~?lduT z?XfTLGT9qC`L_r7d%^%E^eL`wTxIXQ07A!}rP43oS8xRLv-7w06(ET`z~kyDiZs{A zgjymUvpZKjlI2(ffmCoucskTzCKM?Pk{>>$$1(S-9`@#TC*qe`<`ht*u{YCTo!hVK9|LVTBY(DXVeE!>B~1q>Q6v5U2T4q8 zi#x`=eVPlErBGow3g+xXZxl(n>vAEA0wOeW^-akq{ddE`2fKL&WgpptZt_`;tefS?uFC@8jQZ>jW{3*#ga% z+!Gk*DAA(!?9H3N+%Z z{5tBleZNTo?~AZkqBZ5zSMFK$2Rg5ief>tIXpZC}N(!Hhjb_nFYy^Ud-y;6SSsIs= zM*suJV^8)4d{;yg+3Ry_sI-t-%Mdh=GGOrI>eT?38S zMs;@)7dMObRQu;NvDvpj=#sSM!z0J_4kUQVHA&)X1KZyQBsos! zThU5-^J(Xjvc&$))q1TY`l9=`WoY|)g%y`*%XWToJ}l8+r()|XA&;X<~>(Rc7O77Qhc zRkl+#nBMkYTsc2Wqr+T9_&6Y4PAX?zf_M9Yvo|i&#kvL{laAwuDd1Mv0vlU%Bd`6v z4I`gY<1>jZ)mtG|bm-6thzn>;mQD=AS6fg~V}~s2*Ha{}I#9_0(%m_?u3OC_zNQxf zqsAn7APFiTn~Lm)Wj!(5bzlw-l)sP5rFed$DO_(PU4u-LxFb0pca%dRlG;NTOJg`V zGLqIvM#1hw7;?wi+eBhMP3xnZC)x~Q4nM?CiFBX zgb3lb4ws}ccuPD!dt1qG{6S_}>=5Gq0W*mO*e-ZD_k$`wWUXPY1yW;ONIWCW$+k}1 z7Ue(sj7skr=I9tQU6G@-C7WIDhE+r=(8#|aE_#3vj=ybz@2Ef`geIwrUyp*%6KSy4 zxfunph|-=8xx)@_=D>L~$vJGT9-xAe%{PuAH;&qe|3P{Z#4YYgaaZ7%!4EA^G&f2g zhQMZM*WV7jd}KY5C@MdZ8!xk*``;8Qc(+f|3aBbS%cG)%2e;utld7Ms(lgaCq``c$LaUJx}$S1VV6lMT2U7S}wkZ1t`4je=`^vYE2iuo-_ z*uP>AnLgN-S6=>sKD80L-HWFl6pW-njy!glR$W2)NY}p93|YI0h93bfb080LBCjk2 z9o2qHW3fHLK5V`e#{!0a-a!uw36; z7|C#$^}<}M08_S^Dw_&<9gV(n>5ufhh3-XuqKu^7aIM*K#tp@u;$xrUtFobf-%KL? z+V|l25bX(CdseH`6_CFL;fUYC{0$^u4e7Yi<)*q=Gvt4RX%7en9`s!9f&LNOZXq-0 z^DwGqFK@o8W7#4|1$k*Z;*iRm;`?g7;(i|z+orF%cX2od9J-KfoEP5Lu=s@WbF|;IKjuN$4#P`$?e->Hwh@}_cd-7|0 z@+YUcb;X`Xm7)H)p5UJ0IY!#dL^TzoMAh8dcI-C?Od2{%bpPu5b5}Siq5pE+mK3u^QHPAElZEfp)ZX z&_N$!Lp&I;Z>dNYb!ZH0M3V`C7!cIH&ONxUAeaWu3F%fzcfTYqfwyf@#BXz&ZJQk7 z2~^IEI^#Jk&tluChZQsE=^d!zXS(Yt+|}8i=U!=V))LRSN!r<_1 ziU9(6n?+Ms0|+VOJ$;Zc!i`CYlQEc*&{uugec`{d;;1uVgNh!c1gWP6rO=@W+nXZi zDQ>Y*S}cw2!kO`|e+irPedzlO;taha^L)G+AMuA_ed9+sjcY52E3(YN$)B!~v$^8s z4Obp|*wUppd49uXp&~W>1M-3%_;-SwZgd2R6M_|1RsvZ`W3G3c?{bL_v0@l-rzqRj zQI4EFb&`G9;!l+7>{9wgj6Td1w@6uNkrpyLjv0GTue15V4a63Fg8dvec{&bkxdozo z^=aT#(8^Ap%u+0mX;Y`X(ty02Ip{KS_za_cTf7_bK&#Q$%y7t&ML8Yq!@0f!nhkKC zD-2_oRsTO;06vG9fg-sFi?t&cjmf1ti1aGcQNMi}-xl^6b&%!`!DyogNZW`#GT9kw|NB1ir3?lJIoYikt(A4hQlHHaI=x1Unm{d>^Msua0@AGj{-AM6 zVtE+uQ*f#$_{m>MPo+^**4nds0KeuENI|5FTW{$sTv*$rYC4Yju3LVrwxOA%ut@j| z(eMUakb%qOu}FrcRfV@;iu%`1)q5)Dwt>&If|DvljuvbqUngZQ@|9#(_L`q2wchBK zgWX>SWx47>3qJ7hwy40bF+<>O9ZN0@vJJg+B3}Nh)J5;NMzzdqPD6W#nhfTbWu>w+koC{5X=;RR31tZ$3lI6DF3ELfY&U)vZQA@jl!eIb=c% zU*|nYdAE=K@ct8uG4Tz=VJrbN=^>Yzp1SoZZtb zf2(*Su%A{rojQyF_o7PB`J1_ff+#nt>vCx_j+waaiAs7xxty;z9bX@ygbkxkm|>(^J%P`PM-Zw=ufo=!m{eR6Crm-HO4; zAe}btZAw7}K(K_}z4bi2sYk#Y;nr00)z;D(@$GIcrXMfQ0&yzHcQK$Ep^|$)?6x1s z^Xmmw^WNE`1xc%~d65cF&`z1dMU~UVz74Qkd`o|A=y?)-G^3xqHHDkvEG6K6a!9Td zE&wRm`kcN?+s~LCbEo0Xr5}ij4r^_^rK3@FRtxE;cLo3+oJ={Y4N2Ac#XqtHa^9>$ z>N>hX8@2WXRlsUe8%_7ahiH7-`mwtGQ1Cf$x@@IPFeZWcdpp=Eh(R}zA*_fWrT$v+ zuYY|MeC$u`17r9%4%vwK#o&t1CG!O9_eJ#j(^4X%$yY01#6M>>JESVQIYF?k=B*Ip!pkRTcEqL#9Vhkip9f5qUNqQwhh)?JI zZdqfD6~#&iyK)C*T1-$BFR)f~ez0#tZB#h?WuNZ{RWflQLT2&DKwYpJSh;)h+BoL^ zlX3Q>f$wyb?iw&=Nud7%aYH0jUNkf*qdQlTVEECTS&qMI56IAzwezikv*MO_AE;1F zqR`HnCnr<|zccwoP%$Yzo2!J)KHkdBDMlevbs=U87hqqt|B7?Ey;9WS8c5Hlt*M!P zZ5<%1@NKpPQv7^0{jgCjUH&q~gQ^EAHV(xbMDf$?vL~pKyytgmNz|h}e-Pk1M z;R0_l`M=NFs`%nRp2BV3g33cEutpAB`R#J-Gzzf{OSE)NNjGy{ru>Jt8|h%VbWinf zfhMbcGQlk6MU{7?9MG0{`jc@5M2<*5@MXX zp(~b^?3N^p^WgOt6qlRHGgLTjquX~4KeJ@s+0So5AniNrw#!M*%l7MheZY6AN63{5 zylyBGY2*PZ|Dh>h$^)9sHu9{ADDo+^3X^%9&O-}axLLW9U$Gfay73awy|mqTJ24(esfhU$$j2I4mdWr$C?5CkBt*nluB>`bcD`YsPrYyFV>$N;ujaI|| zw|lc^(UJT~Ot5FHrSD4!(Cwqn&doXb7!P^lM~G5y}xr;?8h74?Ze+B~O-%|!(g z{((cj-v~NkA;!O~-}lDOzbiPOKuZla{~G0zwg3^VnNoqbx2-jB`JvB@aF`T?+)OQg z1t&i73TW2~eYRPVsagHApaN}K=eZSbf;`F5Q)ZJa`@%rjpiMY96~jFiXX?B6Fk(%^dsD7t^@)P17$}R+vmm@9n zqooiZaYuGv2mTG2OYp6A0Mu@D;boCOuuwovgVmP++a*zMMpNY9uSjbH(Mr3nmzJJa z3N|p_NEeeyH?~s236_(insH;QS`!c`DGzXVd6aqS{uu#;pOiOVf0r*#(KWDMC#gO( zwHYj&K3|uzsQOG|zy9Cj14{yh;&aJ(%UvV}jg!W<=3|l*Y7lRWxpSe(x zm%fn35H4?nVEgmy^i7fS0{Y@`7L_YOT^xT1_?t@KD18*KGBaJZsmOP}^7PBxs-Y!l z`%2ITgBXq12cN5M@C?*=>0(`!wDrv}&`4+xy_ln!G$z4<@C}#h=C#y=qZp!86(v0; z0exjg6EQ95ob&ut;(VwOI7K_Xb?-0dGs0Pr*BdVuIqU9-$rsCH*k&UmxeeJrrNofN z7gz{Z>gNu{QE3&F$U+?`0j%nu&A`9zmuIL0u|Ibn*62PlibZjG7uvexxO1=faE z5gyloAPXyu%qep+lSak#Tij)mWJE^YbqWnuZhcd!ax;A1=|tWzBe2Og2}FdA63CRl zANwoS&5Z|!T&vF2bG?XNX=xj~Gtc{4{GN6DHU2%0cAjS!*nCB^iOrV3u)f8y*}Wi^ z|F>!TroIE_! z0;w;rJGQqS|I~KOgmhtTnV4wAXgQcJLLm|>i1SC`AgLM}j9b$KtC90!ub3A(fBk+&^c}IMvg?!M1b8~# z3knL&=j;x~hecobW#h^*b%gppPk%Emz>Ga36=I#jw4}u&VvmgGh&(YJbX|cL^Q|9{ zR!M%CPU@UftGp8!*oWQuK3~gD{Z?_-?=A&}m$wI8Y()`5cW_6r9;RJ8a61Uak21tj zi+`jRSBNsG5u#W8hUn}q#p;XI@~XnGNX87MiAcIn)Q^4tiTX)*B?Tv6E9(Yznvb9g zj!LC@vt%o!$nWRwk4?9gOl#8WcVII(x<^@iOf!;dSWy@}Ji~;~Q}lW|xgc^c*RMZC zHaLQ4#5a?dT%-6quNZAIuKi1ck5_zi}zA-9~s&$qm&9M+d}#;A+iCfhaOm@ z!fZitxtGT6$=@^IPLtcPeLm2snmi+756@r^@6Sn?Zlu&6*WEPvIBh_keb~EW%EjXL zoUrFiU&fYo!XqB40}imL3fd}Q{4+7ce^YwMKujb~LiEw@e)YVFNcqERZ?dD@#b69E zqslSC6&S!LxSFJLg!OCJ#7GsuXi~ zU;*@et}nT&!@9d`nbQ=jOm~T=oE`aM1U>rIHvYBBp)O5GnCCD6BmT>vh9jMevE70YlMHci-d_*VE(BYA*CV`>9{cvEc(CfL%i^2$asXxM0geYJa7YDOsaQq6eX8-uPmj5ynB*(-dzC`7q7 z$kvlPu6Ee(@#$Z=Q$_9lxp(OFCTwipVH&xmqmt%4!cE#&y}lwai$&^lP;oJ$ty7N$t7~i?~dn#~Vx^38;qa zW%yW(q5oazSPgM7?FLqYU-_=j6`L5tL@NUw`+BD^Rrc)-9)i&`iPxja#?r%~tB=!p zAJMJorZ&A9*I86VEruHPS17?@*W@@_6;UbB3C|v$p87>Fuy} z=9Lxv)7#k7L)z-uhj@JW7%Cnm?6Qt0Wj(K}97?K6Zp9i%voz}CC)zPyw8Br%(x`7U zA}`_FFEjABRp8Rm93 zkqN^hzC%>e{5kcYVaSQnaw7cfU7Q*+LyUTUu{a34wwzmAa3Vs(3WVO~p%_L;V8dZr zOVTKqKMiMBbC2)wFW4Q1XZliJ)wvZe3^008ZT|p_d@J90v1u>ej{Txl8#MyFY>F%R zwpp~@C#s;?&gMt#D`r1F;gL(!L0ujFz6ZQuxji6#ZOq~zDR6A@V<(ez`;{?6un}J9 z6~@x&e@mlfGXQ;KUAAG%?mryPJD>;C?N9I;JDQfOTDx4?`NFZ~J-m})!&E<_I0qbE z`TU;~8|yT!d2>6|#qaw^Y^Fw7)qDPqu8#ec>=8Tu+jhXNJVzDPjMKS&II(Q9u zfrl3fF0s8~EF0LV69w3@P$GRcq{__GF06!H%Y@vF3@`vy!TiZ@4-%M#!*XO0-;yz# z*CU_D1VwpPfTNfCgT!R5%%1}i6aM@+|OyA(dihM`S z&%HOLdkk@uk{>B|G#lncbbY4g3>4~~9iw*N^0MOxi2@Xai_To*B`s5QQ&bhu2o2@h zt@d8$f8JzJBa?hdW(WPY16!IgaLTbrGpNVMdN0-fc1|Dm?FbX>ck>M2{7918J09YlB$APv z%;L})jFdxlMhwrjT2*f;7S_J8dXF$%_8j;hmd-n#s{fDU_Fmc9S&=-~Jc-k)O=(MPnl z*z%-LrC^V+cyGq;bhEc`Mf<1HY{ECNl>6^W#B#oHl6^533%f8Fn0e9R>1axM>zkf? zvaX9OKPvqm` zkMfAnmy!+*)~qQUcqVi-wctf}8H5BBl+j ztLgI@PHs5s&?`Tmo*jcFnkPNay76?>)jZr*q+}UMnmJdAIBjmJ3J9=Nq@aAEw>=4u zClzngAhgwE+tk9V6v^aaLexUwNNsIN#un!*I}K$FvK%ijzEz{mHAK_{rv1F#7~N#Fe? zVcSNtDZ|>#?{D;Z&$MQUy70Me?6_^zlf`$9T1{<#v9Ir0Tus{vf4r>?hq2_-%-@Vj z*5q{`fo09*L@PEnNiv&J=ta9xz7q0y{#Knl_Q2M-*MQ{ zT+-9M0{&N&79_mo^%rEE+P>qe4 zN%TCa^sZDu$AL?rYBTms4P*WM_~+|LJmM8{9|7T41m2W0QA-hznid9;C07(OC54^h z*NPZSp@2)vF~-F6jcHSr&Dz;u;1!g{0?q{gk-{3OOkmvHkba7i%EcpjA~#% z5ZYV6Y7prAyZ!Q+^7+4`Khh51%^0SN2>-V&1Vj~aWcTMt4WV$in&VWb<1&T;otIC2Dt9fp1GQ@T)GVgK*}VVZ+uGRL+T2Bm#}@Y%K7jX6 zdCmZ7o?jl>FgtZHjFylF#ET?|#HN!8RobxcHTY=qCZ1}IRbRIl{t z$v$#sBKjHdRlYfy{J(HK4u56UKtdws6ZaZWD6SPXfF<#68qdfachuVf zB4cFK=1pIXCOf!X^sCb@#FN|~kdjaS#nih|k9>WGO?xj<+}V4ff`5vLKev7Utdxxe z$z5*U$-B}1`aWSPxT#dEZzC}9%lNEdoGfyWb#{xdwuF~8M~ckIDD{WHtkS?n z^>LpS>--F7jzIid3E+q3j7Fc&^C0D|+*Q$D*85UlOH8ukwy_K86><+9ioM!T8Cjnk zM|Yc8@A>GppSX?o`no0kvs%4Ldi2v!wtly>5U6Sb@V$5SbZMStB4+%Y3L*W+YoI8E zE#an%Nu`gvEr4Ho-Jva{ICO|?g^0(YpsHUR?wF{LQ5V1-`}3{X!UpMc=+E+d1eSe0 z_v11aZn+FGdtY?+ggzYGSN*py&L5sLkSpm%qr4U+PVe`l-MA371kO%>JKWlHpJ&=y zV7vH6JH46t9)OhU_%g_9AW0Pqa2<+qXCJ$@deD%;9?8{h_M51E2;}(q_7lC_5t1I4 z%At_!8OOggh93Q;eSAa8gLnO<$o7|aXBmUnTr_^*YKlu~j&VG7y-B5BScHroY%xC%$XcjaR5|3DfQ2YtX~m6i%s9< z{3?ppXO$Yu8Hnnb$u(Kmm&98`mI}p*>-1@IR5D(DQt}8Aiuh^E+wzW0ajL1n*@_%{ z^^6Y1jI)xo;B|lMtJAXgJ39QzP3CU`7B^yOBa&lZvVr*M?p9bo<3XkYcg~Q%ZD92# zB6urFz!%8j!tF4h%q$-#&fh*O7}#)oe4@ygD?f<#8q0#eT9L6m3p+qTf|4euuKb$X zulS}_Hdw=#Y(r0F6vj@!3!rS$#wgAYx4|f z%v&p#>evzDa)9)AhYh4i_Xky@*_8*|hX4wdxgP~-jkMoxCHwyaU@xp=U(Y1k#OlcD z++UbD%v(mrrgE@1JZK_Dc;F@SdJmtt?*3BR{ynv^FteJH@Rw>c#R8V(@vdx&1=5Vg z(zD7{Aiq#IH2#TzkrTQtZQnp^+jOIb%!;5=fH=p~Q*`Y~3`$ra@o;8v3tanu!GcwU zgR(S0jgx=7Dzp)Q=V!eCZ8bqzp{Gnl%y{Mr6tUi_>p)27U`kpZ_`X-eMod?D8V}f= z9@0Vxar>`YJ97zURy+nER%Xy^B+5QKgx>3G5hIHDCK%@v>?`9>b7cHPYy*%N!!_UVZ6Xaj;18i&p z3PtunCioq6Q~89HE*XkkOz-Am1b5e~$)ea$K?!^7LV~yJA@ngqmU)o$)TQ#!f@qIJ#3l%~deQqugr4pKax`eeCDR;&d^3bu^C2xz-mJZSk(+-* zO7W%u{TMV-ZDpEu%Td}to)FM=&v0NOS_)FG7`^gjje|@FFope+HzXVkz~eGmQ+RS; zi#!+`z5bYF{c+Uxa0LmhL)Ju#%=p1~C=Y47B4k_r73o3J!#DfG=S`EdfaM7z@uUa#^&0DN8m%fcLI?y#0( zQ3NqT`yfI4OFXN!fOPG;wFmr4_duAodch?+Hup1$EoM(%e~}NkP0DOie@_~Jyi6xt z+mC>lH3wDH%;LD?{x)-EKLRSF^rl!B9VFMPHv-vA(VO)%krciG&uY*)*Cr~TYx@*9-kMc)1R6|6~J$_N;+rTOnS`=9rsL>>z)O<5r9!i zhc{9WU9J>?GnUA=7c|s6)Uo^6i|2(j22)zmnp-q7BvKPPT;Y;WWVdQ68!{Vr@0_Va z8jh0`)&082u(b+S#gyw{>V6+>_I4&e&cah`xQf>*xh=0I-ww)UMY>~gdSe-tPWgzve8Is*Ah>?9bKx8&qzLR2{I&~|EIZ9*%D zf#mNyjEz>^pvYr7clPl_YEFkxcX#%Dn(>@vA$usghNb@slU?%$v7Hp=L8E<*Nw-BU zPRgE^2Oi!>c5P%?<{HRQRC#wwb z3p5hid&Xn9hA3;PI~Py@7O+}Tk0*siXmYt*`xd5IDhXk8LW z^j|)i;#TGir{5*AFh^yYJ5`4QCa64>)*Em`RVE)mXXPol+%5NQ)9Yc3-U8y}gd;6Y z%s~+{|IDr)&1ABDiy}!5!Ew*@dg|_@W}cZ#UlpItc>pZm3}Q`|qXizG8^Cj8JKk_0L)_p}nmO zK+dh@U=8NhN}4zB^Le^0nZL9R5hJZ4BqL`h8{&q0Rh3TYuw;?a)@ibn5`;XG_w6@o zVPuAozqN~wb-f-aqU3(O&Q<;ELf(?5+aJAu4MCN`HpCr<*ZM%f3=4nH(l>WcE~D6c zROWl9QA%*ZW>GW&n>B3*BA?=DdYS^{*NjY|5o^s}MPKY_6R1JeXrA?s`06)w(_vtg!$9upJPmb&sTJ zWHYt`uac6GklR0+?SPgJ3OR4|c8UwaqIHHdqY~Yy7#xVKS8Oe$I>W36fVSl# z_TeUWi1xH*(v1mz(Q>Obd=|PxiyfP7b1YpDh}DBPMD8e+Cd^+ohwg50e(>fPNG>?1 z_$=1fS*#n=-++uQQ@!9xRXNLI2I*U`qMGaX|0+Wv_d4CbRt1BB+)hXtPbCghJ^p>E zHUCV<{>j+1h}rCYjBZl295>SB7L-v%?~yl)boU)YDU^y$D0n)j&a5AsA#N?4-96E} zjMr1su~QlB&w_I^s9X@eGjRlb93WmZ-686G8DDW=az`20B|m^MHjNHE4qCOMYQ3G8 zHz~N9E?CS=CaBfpps88X*r5gW-$!U!TkPI^N`FnQ2|u!|=Y9&aSs>5e%~)0HoSf7l ziw36*z6kp?9s4O`b2AP+x_iQc@xB99Lm-j1qvK{`uAhp?^AaxyRc2&0exY0s#y9)#qzKvW?RdQaRF4$y>7$c~ zcm2kLe&mWTahQ(RIcW6)J3P-jt|iScz6t`s_8JcNBK3&!NRGoVLb*9qI$ZnO%z39a zJoqZB3hIDV{AJjxxdF?++T^1*IgXDd?vv3iB^6k0G^hgFx#%1M{z5X&+eK=s#0PEF zhWEPfFX26YJz|{Uuj#In^w}4Zx5-~oUX+v@IiJ!yN%pOubLEmL){G_Xg9={b#})%} zbn&x34&gR2aUqR5>z;5UF9T6w!2J(?g4>Fc(MoV+?R}awLr(g4U{m2iYaO1+Jo?;| z!%3%CTjoIE0%pwyh%pT*$10MO?QU006lN0_RYhSNJMiE{j4+SY$i<8gD0~7(kHXuv zPJa2ft-G0-P9KFhy5RrVD(VC$SH*b>PHr_j3%<1=mPHzorfv;K-f1#oGrKN)BQnk_ z+d}r9YRYpwrW=MIG8IKT8APvJLel!IO&`mux#%HQP3gwCA=GS7PbBE`T#Ce!O!2wP z@~{^!s0}0uqG1+r?eypNG+n}M@gVra4q)H9x9qxC|5?hOo#|2#CQ*jE*IacjeO*bH zLr)gOw&;-E2#7l;PueY@Fz$r+)0|aLNI#bJWZ{zy4Dir$FHe&2z{dVVmSyyFTi&pj zM-g=f>Z2>!ODxY~y0||6K}4kEoDvK~M|aiU*QeCl5Pl2Ws&316ra~L!lngoChXy@?ZxiCPL3eQxvf-2$u z(z0z?+k-o7Zd^ixbv;)e^Sh4}!asUK9Bhe`=Bg6i)lG(E@FKt{T|ceq$M~BNjdY%! z8K4C3Ee9mSG`UE6Vd$k?p@ZT%GpW~OBj-leoP7bz;CoCtu3U-*5&^{$vv!5x_>9I8 zA0zj$I&yppT+0Ko&u%xFA&R$TXEq|zK3N~MKkV6p2>OnRY?Y*U)oJPx!dm5s{`_U5}#k@3@`IO$G`E;@rJD`!`D5< zHluUDY_j!q5BwKx&2c$O@`g*+>j9#iFY^rkrn`^>Uir*4pY?S0!a(jXHA!?HX>?;) zFA(a_e}#P7$9>X+>B0#fW#vkonKHhu0$%vQJVVqI_FG2|)kSUCKIh}N-$s%zW#N1E z(f8_s8=x3GHG3k^@Z&t|GJPQ8WeEItph%u0W2a0Q!_{CN1zLFQi9%1*{@-1d`MwOa zJZ}K*Bk~D4X)!GdxOXhi($mx>Fv2@d+5CV!?lGN#75|A`WHM-TemSB#@Q3 zV^X+dq6>hHglM~kG!j=Bw}SraN#2*px|_^9=Vb?daBs|Kjb}Y9msv}tHB;?t4Cp0* z*6g_=-8p8%6)H~nKC+lM?f%4GuflH_yV@ljK~EO`3T>A7q<)ZQmrITcTEF#p7 z?935Im1RhFj~1dWx&8M_apm%;r9R)DIp)fs+n+(CJaJLJB^gGe_TFf;ow|A$zIUU543dV1-O!v{`(L%4D~Tf4 zE+PN!%-{?t=+VCdITnoqxm$(sraMX>KLnZO@UUO)~tLQTiF@X0~N%y09fM#VBPo>)bd6eA7H(XtoYBwx%*~b-xVX| zD35qxbdAa(nXC3H`oqk6`6q^upxroK9A!S#o#xyABKlBwPMkSY+{tkasyN;{XJ!1Z zQ7RQ@CQq_WjR(v-H`)OWMH-s(*x+Ho6h*Gr`M=y`yMGv2Z#g;>#Yb9|MgG7|7Z*<- z`}YR)-ZvB)<4OZgDHqe#@^oD#?xjVnWR7kQ$3s0j_mRDE4bPskThBFlitP-w^a%4z z#1Qj;eMk7|m+{})*u5=uB}9N%!t{Gdo<^NeD-hi4DeUJ(NCdT92)$fpWcbqjh7s?7 zZIXL$q}Ok9qKT(dSTK`{}ThR-`4bTNb0 zeg$(8rx|#OYkaG|oGE0n_+%AQlSOeWu9OhF=^Hr*H$*Li5J&ZLef>Du#x#r z(~@Pcn3xZBn^1&=3b7f<-y-DGp`W)O(*n22wo9L$e+q)7N0Gayg+Ztxmv%mYfBHd( zCxnK2nX`JG$mIdpqK{(vf>NIoPO!K-^h8GXF{m5-8*_QAq8=qbbg!x-d>GY;c|PFJ zaSz7p?vgNK0GVxoRm46XkQo)Ao3_Ifd<%M?(d~n#&ov}D*E%gix&fz-hQ>AVVzR8; z(nkoWOU#jj-)U|uR!4IQ-{lvcZ5svdi)!{O%8Vq9k^NA3_=K~3^eokx05pumYRQ~2 zmG@k{z#xcGHx+trZR9d22}(!`e(l}yS=r8fTH*h$uE!seTqWJ>an8d4fwrSrmM@MD z#OxV%ZN69ZsjpvHl|}F1?u$$Ak6Ff{Aoc)j%}t4T%Zk;c2?zzpQFf{UxLm$=u1fIz zn7xc~vf~=JprU@t@oH=&qu`;Vs5JQaUCzBdo0e;y4r?*#kAwB{zD1T9^uxV}^mjukkrd6e-lstL#FckWX=-epa`Pu z&QKtgaSFB9CG|caHY=7fib8V3o$aH_znDoojLydK^8K7AI z-bf0a|JAvX=oKN(L8giOW9jiIEfY~VDN#7ifn?yl7cROVOoz0LnMs!qdmQS9FHvR^ zo)n1Ozk#u!ylS{y+8)@|RhNFjt2B19GVo5mwjDdy-d_{Z0kE#at7bqMBeJZmaV95-!4E%? z1#_O7(RCuBG$+$@Ei36h_+6qeXIpjSLxj$B9@e%iF~~*PMXW!uhAf81;!?+Be=?~H# zBg;lBd@rax79B07=aKrJxgg(7( z*V9ipg3KT_cc#RBBl~}gV0@Nf`~d>!-G6V4BRH+}#y`AdjkmZiECx=G5bA>N08LsS zTDi%^xuA`2O=5WNC%6NNdNC*52FZ3?jV z@60#PBJBqQV>e3g4jY>AR9zGmn{tfNlYgV%ybatTX(`WQVAtSpxn*#s_iRckTozk_ zFxw)W34K&rp8E+35XXnX&W#zPmjyOELY)2UMmJDAi?n@ObKb3W^N_%**U0nzH+Ec3 z#!WmmzCy30=?Q`Ki*vj-m1;mfj;WoZqn<_|)Vyf*@N>A3*GC%J#(`a#-t;yky)aM% zRk_p4$;rotv|id+Q$drPFB23#Pl)Tgy6U^)p4j}b%~&AN$N$?#LuyfTj}}miAh9RK zLn&{IQeDOYd0>-`k4+5v8(K>BhIpd0YeG$E3A|vU-o3L=9UdipoR|QOz^$bNs(n_C6?blOm`20~V{$q5c2@0PjGe@^lyKx}Yk>T#uqyZW1#5_qY zhN{#?xRO=bcFXTO;g5{W4tV zq+7$Tdvil;arHe1D*Hk^xLUbj>rwI6Ox!tG`@M-QMY;mIKH#-E4l-5Qd#%5Gg>CFO z&V@Xm%190k_LG{dDNWELQ@Fr3p8vKD{#6R}0Nss(jKTJmd1MPBsxR^()j{1LYY2{9 zW)BWJ2krO~DYMulj$~e%&Vfa)H-jI^icj4ils*t7n+LLBh_(=Hc?L~ZqXHd3$lbAr9-HPfuzbThlq>j46R-sj zlKsg`YXc2|5FNp$5mYUjIv#RP7YHmuen8Q>YCJuG3fL51VNDr1*$C{B;k~2fe@QES zypJjsL$n4f)p_1PWH%B#)Poah+24WFDJD`*mW=AgP4FXHsciNPmVV;TlVjP$k|HB% zi;-uB>R8Oe0#cDd_Agn{AQba~+$R%CC1cf!O21(R+gAz+h`w>wD%nqUi{lf$-)CmK zpAVKhk5%LXQ$#P#)JC5NjcyBpwgv#>=5s+` zDi>OXBK+yTxGh<-C_0bX2vo$y#(52uzR;thX;JeWp3`YI^zzA-zHvM_6R94kYUrY4 zSRaK14{d9$<#$tpS#m%|w(GjNZsYa!(r8`_c+CedU>^O`oB8u#N_gG-5eWemN2{Gd z_6F!K5L5TRN*H+M(+S9HJU-gu0WUmQb79VBNQOn}|JQ zq7V%C0F#=#D4{|W1ekagxg=%zC1VuB#!AS4|Ki~1{1dc|SP;#5B z=S)(>V-gaziP8B(x1PU{)=8nHeCut6^+k^{uzq9M3<4#;>GPI1L1D`VSgq?(Rj)2e z=ME}b8nr$j2?EclX5@aFdEbSNO7^AjjQrdidAqe2JhVA7AwI;h6TF2)KLbVk64toP zh$?Qxu%Ybmy9ujGSSCjg`0cJ8Y5%HG;$oNOXP^BGYSPDx(Q*!<#A3gIlsi15^4vSP z54H_o4;X2PHu#-6_>jYNDL|{L0a;Xj=|{1(f!P-3b^yGPUsCSxQe~Qq`wYNS?+eee z_jd(}LHjb@oj+qW#gzyHnP3JK?wwq@G*Bp`GL@Z1eZi5T+GVasi*tV%pkKJx+Fhy| z-i^CQm-87j;%kuN(nw(X@PRYC@&i8Vm-n*1bde-o%1=lSpbcVFo~q1 zTT-LH{k{~-?^IIE5!j*!OQ++M$YAfWSn5|>VMRB3r zHx8+4i`q~#Qb&Yq>s)LSob}^$>_-z!IB576yiK!o5OkDW*<NAMa5b0gGbkuLa|`i``s=>^bITYfDf!^@A$nf$#!6;yn;b+ zPDHRb{4d`RsPXQZdG!OsTh?|y^y6oouSm$;F(buo7TA}r;Mn`!F+*zxc!t>y-z?A4 zf0dw*R9zQpG2unl1v5XUsL%=x_ZsO*mw?-B3V;HD&~AuV+zu&Npmk@I(D1J@g!eH~*Y82*Ul4AQ4AM#RkvmEM6lsBnod@@QCnsAF?Ko!tbu=6=KZpFl_MbNVe26gYdpHh^i^MuR`|Uc< zK@}Ylq9`om6#*)JOhN<$`AtHr>99EXXQYapU&naG>eo%_k3tdB{}6LuGw=cv`O3hu z7b4{|e)ggl=Fa=gJgchaDtZm!--R2jW^jo!@FRs2%(X zG#yHnZ2J@SONM^J%qTo`j`Q!*yJj;Rl@z_9qRj?LIpptW{}0w*LP(#CL;5pkPH@Fj z46maEqp2WC1hd&a5w)=I^I59)WPq#}$d#DZL?X?uzLzWxZ8mx*H%*WE8-TT~9L7Od zTp;Y-QAEVK|DgosMc08Eb0i{FhnK$D)Z`grc@*;f$f2d@Boe(vk8f;$`6fXu^fu@1 zp6PLm7k6jz`Hv)U+u4}W9 zZ5(_#huhp(Nv1il(fBBLJoEdaPy*IYueCx=mDVdgMY@7W(Kj6srSAs)s;lwvJFyRw z*l2z1@qyZQCFK;+-jQm*G-xO=Vk2DT%Wht`65b-t_k z(Eob4>LQ2$3|7oU*_z&LY(9uw$3~jN0C4Bt?WyUVbv$lK734<;cBk+HE~6=1hj(WYM7UF8-rJ+Rx3gsc7W;ix%K4G%`?#+S4%q7NV^(dP zBrY)>u!yn3dLJL+ES>@N%$x08_XKsR@F&NlCdWimAnvfGzU7vO_ioRm7TCC}KHGWW zK!FDBS@`_gN0LKBXlg^#2SbP1&%TVu3Wbgeg>6?rS#fGMjxx>Q5N`$Cb}t<@wfW@* zwnfSHw<>sJQauGSW2fk{lQV>(pMl<+_|GhHW_{+r<})d}H(CIdE_eR!J0xhr7PIAl zkpv12*>2olkp3Mg9tV4}UB+ShpIP)y)J`CAH0*Jh4JL=|o1}p2+@oxW`vPfv?pE>8 zCnlFB^a4@B+Tqi|iGY8&Yok(YqYDVoBHuq#B+-N=CJu7SsE&G7x&~z-?80!QM!l>@ zW#UyEPBXEQ^tUa!U&Qt;$Us@IJX)`OY6djONFr>9t)Lj|sW)F05=BicVomim2dlLa zU4EB=zlY?ez-9IGAuz8r&^R41#-P2xKb1E<&oqEx-26N1Z^_mrXc?`fsjuvF8#m`Y zGrBo9lXCnoV>vHWb$(B0zODf{>WLHU(q>S@Wa}X2FM4@YH@UnzW`}4$p|OhM)-6o} zkd4b8vTJ%YGd4pQXVbrUbEXayr89l<3-1-n(a8-QOkHeKZh?Z^!nf_241i{)p~(r( zr(C{flC&a0a73N`FzWh+Rm)o%5^8}UxL}a1Z%C_$d+&6Y>0u`?;F10nw$F3h=>1-X z>GpxXZ)Dh#qVhi(x#&j(435zQ$P$9+xTuJBLkJAdGiA;@GQ@-aPp9S=H{nT52j-iZ z@&=}G^RPb0TeE!uP$zQacv0S}q52Qd+ve75?poU@vt`RAtP{j36#g_+b`+Eg?x<{- zbKRuaT|BYcj9c?&Ky12ryV9isypzne%c8Pxr|9#6hA8ArxfP|tfQp(a!UbM818YB3 zG725CcmYD9hj-nlzQ0I%_DLurvpgMUNlQ+-N=<0*futkQ-jOhB?ml?KdspsB@~P3% zRbtb}P~ZhbHymCXejo^FsxoQiBt?|9ZF!P%e;Ce~5&aMjm{In$U<5x}5QB3lpH#8(yyUF*Lr;72u%o!rDkC zENxwcX`5+T@Qr>bgNq+WP;>$>Ine49_^A||ilA7SSNKdz&b#zy&>Ay)!;Ifu&2z*+ zfYl3fkjRgEx(~qg4DWPLS~L7Lgj>AOAKkvh$+!~YxL@7KfK#Aw$w8gkZhmu+%i)9n zwgHMwoBvkyBZi0KdA_%`qI(8Gyxx(%&*m;6F~UY1A7K?2`vX@(LR{i&RU)M1o|Uy9 zyxC@{6(?CKoE=7L!}~&C3)33Q-0AWE^McJNBUC5?;ZkWFAjjZmTv}uF|G5`N{BMk| zo|C}9i#V9AOGIrOGgD9_V|%pWdwx}>rMJT#gSn&5=ygNHO*1feq{{zIFx0YQg)SVv ztsNzyTp)h8SbUb}J9vB)6jv1t+x6|%iAEA0-TK2WCznWheddm*I4MEr4Eo|DttuDE zh&M*cy+;5k;p4fmjWy8l_V)AMJzWDMa*ybOV!B%sa&AInkCsFpnEUQU{Rm-#+lJ{s zlBV#F8NpW`Cu`g+-+c~V0ag4Vbxd4xY}`TobAZ7W4Kbo}foqV-BJd$0f~ZyW7UXtF zeUvL=f_+Up9<8=r6vZ)f>R;h8yh2wNlZ^h^AYQ$xF1rS4=1(NcN4s;*96%5*mRt5A z)|Om)=du28Z$x))+GW5Tx3L>9Jbww?LJBI8Pfm44@1n){=yz?rC=n@4p^v0jkq2Ux z0=$Zr*pWwz!G$uC|M#o9`X|&+d$7YD!I*RdQq;>B5NTUfZ@!DQOCioAAQxcF?wKBWIn6s z^2%LJ$KD*#Lu;vTP!Kz$76?xk3^$$x%=KI(MhW#A+p+Tk@Il2#(cr)el4<87M zW;{kwc#&a8W)ffMP$29W!1H#1TH)l!o6a!|4% zDkmE)^Z8g?6I?99*Tshcn>|BSk6!5<)op<$E%G>@1wNVg6Km<_LyWysO1%Z06W?*e z`B%-`U~Ic{!MoM>6Js;WVhM45`{#P)IV;;%j~ao|e3OB)zpdgpi+eU-FdC7D?KuB! z5xTqVT%@xSEMjSevh=+-fmpjt!5_ZjKQnbX;Q5=+IrY=NHtGTyZLWn!J)?d^O?emn z%8eIxDpxF+EpUoe>A`Q$rGQ-~4FGK{md@=20h_fAp_n;yd^A}Cjx7uyF8lHG?2&&W zzVmkF={SaP`ckBuFAk(#U>lbwmbNFBcZ0<|28R4iY+ruyYn5~tRfQt}^GY55iM!Q1R&aPt!&@syVe4w95{Hh zRk9%~BQcq4_Vr(D)*&!{BYd~K@}K{GFn}U@UL?U6L=`Lnihc9K%-+&Yb_3SS_g)`| z&Jf4-OC`8(@aMx{N2@}a$tJ92E1Mi!#0aYv)-5>52JzH*N&Zd0f2~74!9~R6^5kzR zQrV%I5Defz)-$%uOg5)I9uRr(Z8)Zy$16Soxhq$FoXmhXy_NDx)^#7MvbjqU?@i6> zrDj3!dK^h#o#jzO4YPT*yo9yz(Gb(o5pN2$e{Z&m#FM#yRl&BM-iML2sbmP-jjYt_ zO_L?!KYZPyHZqd>FtP?RCxaH4ppK4Uz4`_q(uD;qM(#mnlx_u|=$l7;+*h+} z%9uP0{#f!->{b}TV{ODs~7l#tT=nnwxzq7L6_ z%`mvFX585q8}%?a9m$LLsE{TsFMQu*f3vQ_Ffi(@gNBm zE^!BbaYlOPy5`C3;`=yl-M;qX3-=t;-)7*UjjwHC2OZVva~D}mKejU{*VDM7a&`|B z?%-x~h6qZn6WjF;+1(A4w{GYuH4vehxStfQMgbkddjzc<*^?i7smS%8ODFW?Zp`t!Q}nzWVB|) zH@9ZKOa}RWaA3mNue59J`FQF6<98_LpX~!Ut+nYn-}}tRa26;eF(pWuPb$*C zInFDO`KUVynt7D0K}5lSf#L)*H7GMH$SEHUI-NhVgV%a*>sp=_&4bWMMGE%sg=V1# z4rHe|88+B2-6AtLrunPPu@pz2LduZB^~MD_bm9vvOR>?!FYh)a0Zr{H@5`mNH6j{-7A(H=6xMUFeiBqh{BxZ(L#jO1?(9$?Yl zYZnw88Y!D`8^ksOT_|9!5uE2(XfTFL{tdTs88n$tNy9-Yr9=?71{QGGIkQXGELL7q z@9rgRH12O=b@^SZ&7{lJl-ALe))6;ob!FDvW6!>}?-aWvWueys4PK{#Sx)&H1uL(( zAt*0}yA0*Mcz@}%M^uX=Mj71zq>#J#_PQ&%HUdKSthj-*fv=&Xw!e8#*uUEbU)#P>{AAREhh8)2Men($Zd_FwOs}!09djkq?V>~nr0L_jHGvKw@g=xZDgP(<6 z;~L~OcI*g+Xf_`8hoGp^Alb(bK-n${nFK3Fizf2Xu2Xus3vkS)af11{FvEju>j>Qj z-wQC}7KL&l>MBJ_BtEBzjkMAVS;HNu79SYpw*$g-y%LR%5aWyscw>tfr;3>TFXiCs$R$o`Tqb5v2ii+>>vj=S_P^! z(Z1K@yV);AU(+l=JbEDS^94*T&pG-;cdQ zhrQwhm2om3%GSAU=5q%aRMgMe4BR%b-r^ZjjAqM<40ke?6w;g4;wN!SLSMZKh=6qe zpWO3E$Ycm+fEbL68DuBOghrdQ?H>j1_}m4i5mG@j39zk)+wH5R8eDhz3%SZD8!avR z4=~4-6zdPZX`xH-qB)inzF&6z25syI3w2i^mvlwvXgkGMH#uOEbM8+m!qXddjv!qp z!y3Ki-^_&FM5XV5M3bY~qL1t>lySlYUi!S_O|#;iGM<5;^`9v^NH^&TpxIf~EByxW z=>t~nTetQ=JWy8ygK1e@;`GfAV%C1^MN!rXb2`v~?NIjOnU&{O_gk;p$ zVl(gzs}~MertVKyZA<+(6_NpZhtJAZWhJ3e8vn=;4zouBmHTg`Szew9hAyELlUBno zQ;)PUzx1r`6v%hyYXQE=`zmWhqVbiy#{x0jVX@M}KTH`()ty{iEpIfKa4n?V`8#0K zO-pqUA@PkqL{NH)II?#z@@^m_u)Uz}k1ShMrYKB8M4Re&0KDP=^1G76em!2Wt+A@P zETdOY?Eej(rB`@_1ge_3=$O@){{>Rv)+6l?ety6N?XJu@<-Ii!%uU5V!er;Q0n47d zf3X7C1k2QDfN8V%<%FLMLPF-NaX!$!7L-He_OK48FCLI9V$>`+ev|F#i!Z?NZGJZ= zg!7wB=^X?#@Nz<9TDNi;(x_R#QM-ChgSPj#g7lv--c~#)PV`itI49-OjTy= zTg)OZulL?|>Z{wAijY~hnAyjOf;HpQtZ8{PSxF}p(!5H=6PrkKYO1{zadIj_DfB?>&>O{s(-A3$`pM}wrt8j&N z>D?KtLDALj0QAz~CHYSxWKAV>eV7Sgd|ML})A*;XXs}BN_%9y`?9Hg6?AuF_EEl&F zKKFnRd>MLfCR81U2of|BMt&{?%gUhHzW__K^y20@~xb}#AbBlqo zNlz1^Qhxy?m{4B|uE|dVFCEZ|5^HSEd~L4?5;n4R)#dn_Z)cwTzc9zY^%Y-j4ooSN zMsYaXz9M+UFOGU606Z^VLeaA{*x^OTj$De^ThHy2l%x8^0h9GFN{;LjTkHH6rihAF zG<7`y(}zox&W2pSZi5;4l|53*GGO*Iud$uHM$Dg#6m-0(UZ%<7Y$kZ?#q%_rO>wR; zRpbBRhS7(c(K!1%n3^~>&E0y6VZT2t=qCPVb1Vi})H-iHKc!?3u%jUSN^bAsWl_Nk zOJiMKDEI&>gcZPUxx%Z=U??+D>hhl5chM`t7pQlaQpPauJPeD}*9qreo&;7i# zemkApRu*`l^pTXxlZgmnCE~uHwXV}(TP282y+*~pHgz?6ev>LhJIll!?^{aqwwzG5 z?Hnx?m55*9;pUhT_~kVh6W2ST?K!rM`M|zbcR4qI%rzj7FJ7v9gjU}GT>d5A2Vm1a z5&?uw3Hk0JbNrzqYI0UPyl;v#@VoX6S$g`KNkubjF>$5B1Vw-1>kk9mG%7YHQE zHSq;N&1BktmQR6GJ)S_H1vu3m*iQd3vcCDiK;-69um6;O$&~F^h#=tUMtke}>I9k? z5&1(L&Ll(6k-aenjUT>RD<~T!8Zw=ye+wWM&ht3TfIda`I-hZ^xVaG>q5F-3 zqE)xa z`x{d9M^89*ACu@=SJIbQX}Bm9jXid4n>czT-{k0_`num=rRU-p7D2^A0h&dxp6%4$ z@gLGFSk1gnAIxD9jwR;&7V!U=kio5@$o`+_<=V@=~(PF$1{+pQ-5 z5lk-_=nnWy0j8id`Jwdsb8=d9a&s^$ICiG^?F{oAC~b^K*Qf0nSCJMFu($l5QF_*b{%4kV}3Kh}mFU zj@^96-Fy)e>|QYjI;9EwT>OL{5tt`+_5z^q#LDO@#|x}-Ancn-3Nc#?#Q5-DndQqT zW78r(*ui$L6u}qB_=#o{e}K5#6feFlDybXB>PMVGYDK1)Vd}H0VF;oW^38Wlw2KsM zanSmWPQB2eAzb5a0;@RCX_POC-*+P4t7Q)XntUW8`ADCcp4eLsNJQ9H*|Jtt?+ZXw)D=Nlg&u~8`~sNuY+CMy&z+;K69qNlNc!wnU=dovwnRp_Tv^TY5vtX z8u06kuOELO`3Wg@JmQ0mT)lzfPvEws(IqR3r|84?I)^o64N}Ss65+Bm?t7x0Uh$KMTT2l4D(DxAK;V zCB${V{y71pQ?E2l`le%Z>MD^3BSEjZCl!a@6#MN$m1(>zMe{Djb-G4MOLQzRl-C0Fp-gT=LIP6(pR6H)s zOj4yuv+#0tfZK4nKb4l&@6*$qX?@M~u)_<=R3prpBiQY)q&kXD5EPtOp3^l#uIz$Y z?(V2<>`0mQ)<^>?HWNvi<_`8Tmf4}LvbXL$a* z^KwUu1h`R+A%MzkronL+ZzsaGn40t6I&YcQpMZwiDX~#^zmY~D8PEjV!G9O-H8K?d z+8(0kyTI4wZv_k-X!b`A&l{Gx2ShBm0-x3Lp@MrZo0PfYlDT73qbN7F4;m^nkTrxr zLm5*c7FI&U_9aM6ly0CAulD|!;5pU4LsR;M4g7n<=!dv$o_iO@*p6O6;z7FS-;O|XJLzg zp0P((?k!lE%WPiFr~bGA8~IpMe#=fTUG^!@n6l0s9f79?{yG0_sJt$20kTJ%AO0Rl zHH*Xw>ycwN&&GQnFS~|^h^gzp+pKPc?BMo@N%o97Xaete?&7@P{wAIWM53#*y(_?X zfg!kcDo>*`^QOq}a@I#+HGR_lLRs(i zu>2m&k;Z3J*nY(R-U*J|Zbu5VDc1Ze&~coG5Nzm|%s-Ayg5@p{a2HG2W@95^gLbyU z`B#3&MdK8kHa{A-w!IFyECas3()nKJd~>?5T}BMrNy>{iRo-R7>taq=QUX}FCf^u)W%qTEiwefSyMxwk)rz#f?L#b zGHz{$Um*^ixSjS7U>RU(4yP9FrdknsI`rhKsZ~mZ#e-oV!~auATpbBW z?DI}cHv>(Gt}3K_qJOdA%iF^zmr01{$Tk<+r@~BP{-gVbO-a}PSi0^&s@^bc@0A@A zLiWraS!Gs2M)p<-5s~d0*?VOtWbYlWy;p^6Un>g9p7Hz6^}GL{anCv5`@Y}vKF{+; zJ;AV2hTQsqI|UAVK8}qJCIwaf^FI#MI$aMBJF-tSx6PN-{W}qFW>{iCm{bCHU`XN; zEcYOV{ z^ej~KjV`FFvuvqv-~?<~8zJGp023N>2|YtVFk{TY5A+>&rcPKRq6>JbLCbcohd#Km zlVNMqUOwEvf{TW0gwsa|`^^;7!^|wNy+_D&?N@I1f#?s4^Dk3UUfMmF*zPgsI1qBP zbW?BOhFz%s?A1iRH~P)`B*qf5&YF&Xmpms!d8e6dTWlvGf&OT?^%^=?)X-3@A(jW6 zf!oDdQY8$fNYHQH%?Vog0z%#8-cic>H9zhO4|{xgT6xQu1xM(L&qJ(x#RW|P}+kqutleyzif}K5}9KPBJe^*8>K>t(RZ49jdMuk5?ipmo(mi}2 z@0PFV{hrDU?}Nh#37=9HL3)qM@O)EJJHKUX~dg-xMrN) zF{GPX?{t5GKP{0}99q@B1XN5#LEnb4;@KjY7%-7}Bh>9=YBACVvtjwjiEF#<^@Z<_x58P`ct0GZ|y-U?o@ z-+`gvj9Y5&(;s{%R}}@Waz*Cf^_dWueb->Eg}IGIj?8UT&+?5|b}#wxpGv5DT-S?M z99Sh&Ma6rymzQrp*V&R^2jDui5aczge9%60Aa5w**8E0Sa?@Mma|}rhq4Zhi;$lXv zi6+sq*-y_m>k?PZPXo(L2Lx7gqa5)Ixpota4lgjB&1{^ry*~oXGBfMEV7L2DYWEK` z<55>Q-tXTz6+``)er!)4!`;sAG3@OS#j7$6vo(Bm0Lyb3}ObDng?V(+6NW(iI07nVPNp> z>)xaBwto%FD(H8jU+lQ2*zcmcq~f+WzN#OfDtbCobGj_5I|z6mx@u|i%%8^ z%VNW=0L~jP2*AKL?^zw{+T0pNZFiP1iB0ooCPsPpaCMj@p(=0iL{gFZrmfMf`6m zfTX7$`<4Y|+P4=y0AsHB{CC&fGHm!K4`^xt`D6B9s{FsxdZ1gbD;Q}xP2~L%BREwB{khkpNTK%C- z=Hq;#`~^=JfK0chY`@_IcNA^i1!Gv%vmVtzR(~Ihjbk(SaD%Z@_pTX!UwnfKee2?Y z_zcj(#N0G$K08v0^_9Ipmu#g(Na5x|LLfkqo0{{=8r~EDhPPm<3>O~m6Lc6}`!ya- z$fH46@&%hZ^PQM%t;m{HTUN6W?Vze-aSDJGyiyGBh6g@-r0H~NBo&!xB=r*~N5|3n zmv+GH4+cR{Ujwy4S9?d7e411(=s0>&1uBS9M_9G=H77?*d@6PFzBXB{e&<@`)B>RB zW&H&N+aM6*B=t=awEZ9$#C&dU{yawpVmR`&+bE{!s-E6(&@AE>oj=KgSC@sB9{lgq zTCE$!of!l=n)@g58f26VJs;C*s;?kww6~mOiH2m2-UC$hJ+4-LS`)gwh)p7 z<&^(mR_+15P%i2@!;k672z9gKn`+w#Y%#dM zAEXQp^}_Qo#6Gm>--PJZBg8TfJBsT1{CTOfT;3?@|HR=8cB1~LTsQ!g;(SG{-%smLEcm2ar^a_%PE3{ z&-gm{Gl}T3i_8$%gJs%F_q=SZ)=SSlxo4701e+wvB{Di_Qyj?lvoEG4;XkgD(wldV z+rz=1riaH~s;wN)!TfI$YFEW49Ei$KC{6=JyB z(?2Gton2c8)z<0#KDRVX-|Lb|y97|$2bTdj{6M};Ho;(s1gHSb`a&YJ2+aUnKrj}w z_i<$xBa#V;skVa&Hhquy+5twOFu9wBjz z>)p@#LcBn2$Tl5348QIPZUiU(3O+IeYxb!Ao|vO?+~pKybZbFwj}gVMS6l!X(MA?D zzdl&Vd;-b9Ve0-!mG+xqGQqZ>mt>OS~KhWhJv0j+1EM%ti^^_TS;$S`H&Y+SNY zy$hhrEVqRw63BnVzem-;mdBcXIdIgGcz%)iez&mU>>p{GeLZCWyxw$wC?v94hWzm< zs_O2J_BQcb?zql0pYitow-D(s7wjLUrUPlp)kn2`t#YP&^#;@@=$JnFZ5Vli@-4Y= zY_v_ByRP(qT6-qAx1y@&wO1Nz)tkV8$YA~W$TUar?Pc6RZ%AY-HoYIj3nxVC?+y># zXc$C+(zbsnU#MOqIcFx8XPLvtU^kGFS!5)f(V(sRYl7)MlmOU9rpDxTKnzqit?#f*o&jO{4+)Sj^=)s z>-~|g4o}9e+7eD?Fv(3V$+Kj#0Uc2-Ut0*)%mO?ns@G{5uLzwoxbH>yKg)aK6t!YV zrBEU94Hzwb3aJP5IL@uO{VUKp;*e%K5n(@>woHTQg_1uf7CbGAqGNj1>iCBNL+pS+ zr1F}VT#)45r@6Pz`$CCyu}K>yHCG3S7HOIrq=2~R9A?dGGKLAJpLq37pJ|}7S8JrD6OvQ5dl^6w}Xa6Z`QFXmWf$t$w z@7bB|f|}FvQDbCnz47<$f$Y`7Jm6$K{@P1kk7^)hGW2#EB&QZe3eIx}i06me^jBM+0=+vy&5o6Ogcofb>gn#+TsXKV{IaV-pd1ES?@dgD%F=s>mG!JpKg* z`pIwHiPt1M{t{j+gHx;uP7y(;LAk1*Y{(oMV9E)xRus0B6avpzKzZ}T)xK)4MD;;F z?l`rF5{ZfMGE&opSt1uWC;7XOWJz(4u;&8)#a6Zq$UuC(cJ*Jbb0}U|242|k<56%5 zx<^?a`nBf|ITRupi7F$YQw(ylR z+EOwKEQXXZa-gvh$=qn&Mci|aS6gOEF75d%cG~=z*(%&4??AhnpqtHsETfvGudqV6 zu;=Hz3FLGx%_Qu`A}nh79r#N$SZ!6C1kJw#ghzXE%-EX};sqm-cu9zA5@+ zw?N%@-LtA!r9|oU@_wMOuUGlQ?3;JttC-!?mP`XsZAG-zZZN)y%ZEjY88-zgA1-qo}#c{$h4)IgX2xZf>?1iN~m3g z@vy{@yJG0rNG*bMm{GeyUVrI5kfD%{xj{xC-d8BYm`^+Mih$p}DCEL3v&HGxYO2al z*%@pSATuitSq{AsJZodb?mFfE1#!c5AL$oeKvd+mWMXi`z#v}~I#$b&b0z1KeIKae z75D?w3_?E3d5!KU981@g=S)kFK?NW2X3y&5h4aP!z=?k?8W6<&J_a8^-1UK1dm3^# zkieETwtPz79P&BxTuA?XqH}Ja91iXVkldSul7lxc7CB+yP$B40FHIca-Nv%FXvf zLw4`cfBp{ULU{9AwWHO_fEWepci(?sSg48hnSqH^4uS!&GJy@vA~)p4)QTIlgNwVK zSN(jziOOKRl*0==|Ki=4W1pu>@$e_bdw$CIQIvaDj_P;3F(M;SA)Wccld)E2G_Q!d z$3BZ*mvIecYe*Pnbo&`3kK9TD>Jp25DC4lF(ND&mKZZOeUylmE9zDVVDb84CN{BvE zuUQFLKZOfU&lvOLAC*qO`+W_a5K7?KG$bH2>^Nukb0~XW#lD79J>Hk~jO}XC8b6*D-nnJScy-MDMh~Jp@*KeMhui;Ow`8 zeCawGj*IJXd^jq6INHhw*8dYg77xUD(EyZ%vUd$dfP005z2IxE5wL?L$6Dw7K6*h# zg#C0vAP5>Z!>St9*_UaTxqCIO#!T4#kN=>rgfEqN&O4xy(`~2emIf*MCzZQ+$oBH( zt&B%X|9F7uNQFGXfd)a%6h{;;u8YDi&Yuq7-5y(O8WoG?{ssPc_xe;CS3v_%>1n%x z47FC=BEfywyu(vR)oJ-UxUGLBaUgM1wrx|kH)%9(h_(U=uPqCy6D-bFQIH60yh;2W z3vqF*AQ{&B!W@s#!+b10pP%xWTAh@(Q%uU2)zCy4oWwEV#IfMRUEng6!CRr_$KF~X zlv2u-tsMj(VV_K)`iuj0L3`(_)NeW7B7k|M*nRJF=%4mN5>6$-^Vv5l7xR`G zdb^atpot@y>Dc>(>5n)9iCE0wy}YIEy!x{%0X^A`Utg1_!1ZZ~~6 zc|x&Be!RmElxMMBssAkf-T1HEFNoMq|G69f`2lx4BPj`sId$re=FX-L&ORTy552e> zA4P?EC>rp4d8F$c;nD^(#Fh6LV)??3Hz0@|_joU{3OsuZOEZkz`5kcOh4R$+ukeR<`5ctNuMn0X;L+K#OJ}o2pd9fLOSvP_$XEC{>sXSL!f+TLNt-PAKp|7yQ&k z+h8Q-8Ro8WArl1JmNdU@RBw=D7SDWQqW_jfK$T+5V%H!Di~S{MMcrg`SUU=#;q+bI z@U91jA{*|&g>lwhbA^g&F$nVJFX2Y#lQtoMdY8QB3-9ej@B9SCc9PYr2)D}%9w6XZ z%)X10e|IJ#b)_AY>HUHyG4>ga+7O3heF<4uNf{u^stexZj zLNEZ%gn+=rSSH3wqmTNox&*HGF>e=aw zj30VIgh)qCTug1;J4G;mLR%Tz^<9=>PE`N$ykGypdig*AgZ@=~0OHe?)U;vzEmI1i z?JF(P2>Jtf-+|oht0i}*$X8D8FgyB;aMVh>!FsazC8xg;JJ6}iOSc{ms9)Cr5w@OHioX>ph7zC? zE0|g-m@4G(hgo>JtpCX7n}`0K!Ulb~^YX>BoF`~kMyvsJ#i*m`)9x-_KropYhgU48 zmu|HIQ`b`ICkw zq&-V-3E5e;g~pu_lbczQXH)e@;fCZ090jc8evq)3V`?S{Sp@(-OU1keI95Ua;CZ2k zS6}vzUxqgWQ>oQKWO3X7b1S%mAG^Ilo-9Ixtj1z)7x}mHQkAw#g$!f)1J<4gQjNZm z+J_TpUgR5x>}yuL|D$~{tM6~}v$SREQzBY#Un*c806REr1akPcG*a1ffLZzbad4y7-p`x zJGtFhP~R1)esBla1TdC<1>?dDCOg?#Odxj2)7N9RG-}MA$__D7jNJ2a`zJ{;>c1c) z2$9Xep3gADdExHlbaqyMPbL^cZ^XtE?Q85%yXDe?eQ~#X6xf!qN<{sdQ4V?bC8+%g zhupS#w?zAc*-?OE;L}@mby;;q2w5yY`=>U%a!&SI8>W5JYB5J;U)?{$INXGwzr$~x zsOkQc=2KNNuXtF;r~1{|-UK-?e;~kOhKYlnonyw{4}=*36_2mTYA=Rh^tRdAu=@T) z@CJ_6RoI?GIEch?3&icdx^q{Q3o8@fSpqWPI4XFuHOq_@kOl@6+Kyc|A7pV>TsVk^NCWo4I`5wVML!LSbL`i_y0 zD)u`k>aPV~k5N;6r{13W3*xg)WaKv;yFUAh1QXzhRK3K3@Bi#+ET-XwVJ)Si8D~oj zDScBl{?jaM0AA8}fuGWB_c4e-$HM3`?+u$aDc#@7=&h>x2k?WKFy%tE>qu{3VpuD8 zq}v&_XuA95lv{7*?5n)Y5Exfux&WT{qSk=>FU<7W%nmfK(U3zI_V$6G00_@mf9Q6G zZ`=ky)XnXg%ch>Sg~~&mMgif*G3pHv7cP>1o^726?Z}A8E$$Px2tKRA)?^qv{xtBZ zM7ssLUXNheu)*1N%p*L%N*er9uh zAl!J>7P3gfVHOZ(9&?xnm4@?+eg`jB6$wcQ#`tD97`t^S4kU+Y%9_M=t14#xTfF@? zB@<@`VDwv?{Q029IS}0JB9RP-qm0p8dtZQ8ENL2f`WncR(6p~RFTQLg06KkX@S{9M zl8)<(HS!BLjt)@u!Z6pHQ(Ow&wA}~*)BJYbvn1^yhyu9L63sSS{SoX$3?n7=@>yNC zxPdtqF@dQKLDq1QBxAbkw-O0PFIId-2wp#g^z3|@6d?8p5c&I{*>)scb3sGh*^s5B$P#)akL{mJ`oi=JGt82TUj z04*q#wN!kM4Fn$k|90No4#s$LO8<(yxxR%;JaT%ZEJqe1KHJA!5yW3@k=|!aCl0D) zD5-?!^cE5+j0W8+LL!Ae#2B)H2{ryMk4p~`#gDK6{d=*f+7?-lQ9zsXVE;PAzce#{ zY4$**!pZ;jR%M>!!3JGiaUs;e3iReaP(67MB%3c_(ezH+|6t6EGPVwh?NO%*!#97G; z4ZwQl)5$%%`e6})#fSBCm$&_SE{61EvhLa$59m`?$cyl3h}67O0$;V~R}-(1R0ird zu*wUJ+h12VS{@i;Z-VO+c>SovDcN@>#K$fpjJj2D_=mSWN1?svI!`Edbtowr#$LHQ zkr5OIEg3@DWN6>o^T{fk^3JVECXk;sk-0q*OCfR}(_CPM6o{{X4Fil}Q27jA0?Ik* zHD(U@3aZL#k-04@?p&~}&2#rn&4>lHI;jJ7K*Id{RMDpYQXc}>L$JX`E!6W&>`a0PV5%!OT88C2+?4L}{PK+7ejF1>D zVyf?m@^)pHST*HLQ~5$~)iYrA&@kekyOY3ploP)te`rDYoY}&BryLpV_=atYF}$6X ztm{h=ei_po3nhI6mH35`fxr040HUWIC$>^3cATICJp0!R_I*!tWm=?y&>#A3aFfGi zezgMAFSNU9T3Kw?s?ImWocP$&%sjZAF61qVF0_nG=znw%faFpKHXeGoBh7X+Nd<6A zybr-FGSjZdAhu(PPe~qqvRqQc3Iuf}otqan^5YqZ4Io}hR0>H{=A`QfF(qklN2%gM zv|_3@lkDD!JPF{f^yNi~v9s#u$A=ixhQw(`{(q#hAjc?r%B}%QrQ%(X%`T~L59*`K z-x-3+?#4bnf|@7I5H=$FTR&WPk@*TDBYdV(YROR? zh1&HdlP{KnK_jNm2)n;DA-bT;NewPeD>i`jy?H?_WNs~#)AGh!8Vp-=-YwIi**5+M zT`=fu!%X=NC!ky0o;XkN@)uHp3O+BKR!>S;t8;DZVm6^}KHzZ99f;&jHT;>om@Ya+>Yu3WTD%CY&v&{ z9&JFSJ`Ib318cFq|M=-Ak3HY+{5~H?&gK}>v>xTS9~_YBo-gb|53ow^?t)akUY0mr zGcJjwgi7e}M6^fp;F`euWKQ8t>07NVHxS>m1>1){XRLZig6ef6)pIVOu z_FdmpJ|PA!PKfD+ZzjfVlqgB%{WYXTEStUYb# zo5lvy15A(|OpW# zCu>U^|87T;W{j%;In`8YV#2Mzq5Bz|7Z(@`%N;KUlO_U>a6u+==Gu!0T>h{f8r;m{K@(rUAtY+%fX(C$1 z2Wn9wn9fAl&!&H;K`)mr|7Y*eqn9Kun2QvRMje%}ZKja2aste;OdRLa2ieoAzeUP54M%K7b!=rmDlfp#Fo)?QQuOVzu0YJM zkI3_WJO2I{JjKKj6BD3O)YrxGJac}*MN2@bA0Mr~!aU79#}q*v(X#WF+bKeJ`jRjU z8~C)I;ryx){xwE$1cDD6f@k2P!I^-Vice;_9EX$BsAW|*abR{p4wIj6RJi%!nJ!LF zvt4~iJ;vzf3(@lbp%>;#VdnAHRd9kkH^m%pDz=Ac-|Rws9B%aGbb3-ctVR zF)-KuHa=Mo9z}w`QyxRg(K0d^1K%+$PYAJF= ztYvvu%d$Q)6l4$F&NmIOxHM=9)K7t8T6X6_JZWc9A5eEh&dVH`xpQN#6O`O_z$Tf$ zTU-F$J1v38g-&7JdacM+TNfJkz=$_n~6*m68Yk zw#3&9pGqp0s@Zr5TECTf5gz1pL>Z9k=1{5kY=M9Ph~O4>Q@(smGktyVPwOM-L&ixJ zLQ(vf{%zOew@PH10B_^&xNpq$7m*5>XdZWCO$a&FSbzl41${iy`a$Gd-i8Dl(D^LX zYaTzjhFuukJ~6n344AKfOWt3x{;m0I3kqMT6nwF$ftC z&)Tmtw8Ysz`~Eot_-z?(-e3L^#@XbJmnoZ;o9`XWjQN9Kv&99qq2+E;RSf?-L-@i! zw<&-lqlQh}k1ruu-M5_h5+ZWj!F++TC{KPAHsJaZAn@${)CA9*ei>UVcq( zSa|sZ8Q&XM3&uMxUU{DGtUCtm$n^O_NA5kw7n55IAL-Bx_^E|5+^RhltPF+kuoczQ zxjL+G?QK-UCz>u7H@!gzz?k&?qA5c|YY@V#@&PqSb7eH8ftKw`=;$`BG#)X`Ys)!R zs{l?f4L-aX&gBasJy)JV;CjGTX6Zbg6sCthY%pQ|2t-x`dk^#qy;#{_L9HXZCf}4g-OxyP-!I7Z+UPAw^?!Q^m ztYl&KbVEBgYWPJtpP8PTxxgTcLFH-E{fgk1Y^W$qaAmRsZU9WPJ^t6-&d%FqU`t3( zg$ENquZ`10?&+&jrBQPV;A4+?*iZUA*Nr_y6G=I&v@g3Th5IB;`R!lr0POUH_$VMtR zLF@%(TwIlUtaZjpjP7^WRd?pEKy+7yQEZu=`4mdz$VefP8UsIVD3%F>fcTTo7J@>d zSR)2m6i%?+#hik3-gY)c+VYT%lvoVV+d8#2EnV{VU`Z4*)GtZ#WtkSIv=|nNnO_B2k`1f`Zc@ z){CX^QU@CU>oY2mm*w(U<0cu7d5O_>*oiK3dIn>^8)BbV$ZH*FXw^6_?;_>SU#%Dh zib{ z5_r!p@H`KoI--+CM_MmEGyCpcnU+eJEIPrm${!Aln^-*DMGt$3PD z=SkgB2You&0cLZCgN@T56vP zGP#H!EwZ!cxmS-jCT=_}nN{&0`FpfKtaS%2h^JP(1Ugf|cFw%;>N%Jc1aQ2pK21@BVgjzyu+&p$nB zHIVp;I@0A`CJDuQdC>;|32&O(@XLS^@Zgd=FYi2WM**M*o$!Os4`=3N7zo6>0_3jf zO`!DHA8aaY|Mc%0XoDF=F+4uwd>HTw4(${sdp$lyj9lee(28;d`*Q}@&2)m-ji!^V z)&NS&10|AaU9Ltb+K)>&PTu(P*=8P!EK;O$l?v0S`_$=x>nIqC?zubZel6r}CTnT& z@`AvJskMQV-z%X)z+l0(K{kfq;O)*g};IWBTDg zo-y&H5ckLgFlfL-qSM1hnfko=eKuN>W(HyVAC2pRR^v`ZqR4$n@*{@l?~m`Vdjvrj zm-;_@GdJ$~u8~yIPU?}C2J%66d^DMPv|r^wE1~xe6E4Pe8ok7bzbv73mIn6Y+2~FsSaRc*HEeQ4|vV5^@QU7fP3fcWfHg^uMcL@lZ6ojpq11LgA_-oe@h5M-h zlp&F_`KR_63nFY{_Tp3>Ki{4R1(X!SxJRL^(cWt>y>5$bc*g$n6z%>4P&F#oRw|w$ z@MQkV+@8cuu82!m_G_hqOCDLZh&xwXK_F1R%KHgl;1Sa%zZkBuSZ!JX>imUYZAsV- z3oAH$O%~BQKsPdfl8H>tf&qE&h^1cYB@%b+n>zfx`-Z~OFF+YDM%p6Enidlpd{ z{nNBK#b?<}b6Sr$sm>Q#6XpL3nxR`@m+7aRgZ3cUqNfT7rH(PC17NGHt+(ox68a#~ zG7cN1qxKjWV^jWimh&PG5JHfr0c>Z|HcrKx;1qU@b>;OK%J$s{2EeJ+9A-nCYLlWV z9I+hcc-zI?UuRMK=FsC@`&9Tu10&{mqy+HxenP{;n~q_#Dfxfx};76 z9q*WA4rwce)-r-69$iX#gA%ZzL66s1L&Lb{48DtGBELW!MqAT@UVwkOVC?xr3<7}cAtirI~zR~Q~qIQb*3+L&?iWA&h$Lz75kWpv=BF8GQ-FF=SrU&u!1 zW_--n$H7(sd4B+7e@J4k{=w7(FKXrJyL>mKQKi=C?_yb`%qc&%kr!v-2EHCzL^g_u z?hWo8+1K7o?oubV5BBqd@CQw@&0!WqB?RFC_JeHE9`F$X2t}Z)i&}Kl-U6FdsNWP8 z${AKS@*VU~LZTm;l_3VhfTfd%%aV>v8k%mN5=pvPwC$nDRsfiykTlA4Zs*u`-MS;Y zK|yh2+fjm%fdLuK7E``(jRp8fHAWfGl~Y4D7#DGGqZ^~2v5y0O-GVdXM@}D9&;gWq zO+avMtmq%8DHh3lBy&8|a8SWBd9jGlE!OP?$p)yho5<9;G|i#;wP0iJtfoW_Id=HM z^i#mLusH!r5Y#liM||#|$iO(^)&?C0=GuQygRHUYwAxq|vF0AM$*Y_ZiQo6}D*3nB zOg0I7KFLh!83^;}FJwld0FNYCdFuO9ORnK0H~=6TGr3dmQ+@cEL4NS>T~+ihr~Wpa&2=wZrZc%FeCx#%+3JTXxad&M?m5)vcjT}$$yh;!s ze@j8AHjAkZi>Z7pfB4AlmXH|x8yVWj@k6g43&=MBLN^-qzj}148v*!=EnuSal;1WU zm`C5b$n?jW9oSA)f|#rPgGS8csa8cMWan)nO8TZ90Y4-_+ejq z{BRZ2Q)RA@Umnz0u6_*i7}evR&HJKWkyb#VczQG~83?o+QvDi|laR!6#3Mfj_X$_8 z)7O2>s32Pj@g9Z}rQPd@vyhy+V5?35J_Ol`tC?r9uYv#s5Y#fS-@3Z7AP<8eQC7T> zoSy0YgdvwwvaP6@pChq=h^ddw@67cUo!sry^tA~ zHDJawX7>FwE_Z4gOr|w|Mxm2#$m2R>5N!Hl-Y8Hbo0B@9b0+d)7YR48#2IajVd@2d;oR=&arjR1(c3YoXwd(uiAPq*EqbZu9+eNS#DF4>rY2OwywAein^ z11A#uX7K3~CK-|NA`(E(Y7k2DXQg!KR3cU11zDdJFQYOr{jb~!FXub%%y|MyH?1hE zByVeo6EmcrGGv!EzKhf>NKKY39-a|z^uO}87UhQ1+j<>v-Y08Mg&8p6rH)409`S6d}w0i2VTHG^Nl8X#Y@;6@Fb^$Sy^ zdr>D<5djON+r$$*|A*}GAkJ{Ne{ZG5^?MFCHuWFGdidQ0i}sTC&gWuKD5Q_Fb(5W+ zblt0CM`08_l{eX6tF?kD-sSW)T>~{rp17dn3Ahs(q%kD#37{#GA4B5%8Zp2WNy4Jx zkQBlVJyf1>v-c=6-tYnY_bEp*Qf>;;?SI+eaXA)>taS(tLUY5pok|}xQ@7m11reA+ z#z$YZeUb{y00S|`<=b7({OkIYkUO4myBSau&JEMkTCBci9H5Z#Plg?@?xVKvuHI0Q{bYW>GPcMH;!|TQ zuWSF#nc37r-LPm!5bCmHSlr*Sw)sF6I(eEm^kW#BI|$IDa;@WRosB%;)QWLZ z?<^?k%Q>nU!<)%_e0@O--F%Fh!$A<%_Ivj=`lSr@PykS7jb6x3e_C776~5V9*#iX! zaz3XPwkfT6nU|pafZ+J^XZh#Tut?Bo-GP-)TYsxaf*_}l3lVqc*582f%ecXro{Wa^ z5hJEeiadRFTPiZ&P<)RM-ie{!b##5GEB^K{L8}ZqhIk^1{=O_+}TLFouk_ zw;?H{Qf}`w%!5IQE)1VHCp>H2mMfd)~@PwSmx!!JIhRL&qo< zILFj$FwB7IuSd&Jp4>9ziee2kx|yn1(5HggS5*ndC-Vf-X~#1`>(@=mA~VY(Gv#L$ zy9{3iJc&Xc>c~52_8?gwe1LhgCjRbjU?sy^VLVIuyB!M%aaOlK@m4^=Bi2aiQVpt@ z{VN3h#~61&#Vk7^e)P7}khL(Ke(3PiydKc+EXZ0e20zQtd=s@&ot5DV%w|{r7?^xO zi;jl1*zaC1eIo>z*!w*r3imji>dw?47dse-w?8gc?p|FDqPFn2_XyWNyDgva3VMI5$xyFRhj>`_t|3Y9=eDZkVT_g7{5AgwIc zyb2Fmsb}!m^4c!!<88lVW1kUq-5N;f>Pb*)mIcXoJ`H$rvM|py2LV(}EWw@{q3+*u zJc*qQ&5RVm@BGSYJ-F7!w&w97RtckETrx7ybRg_CfV5Le?6SV?BJEj!7)Xf64`MUi z=chsB%>avW{aFJVQ0cPBvr6J*Syc6_I;u7*_Z2p*AuTT}JibBwSTa$Sh)C54w*t`h zI`14j`BfOF{+r>p#fQ)IT)5}oZvFZ&*bY%f8rT1E>O@1lo#6CN!>WzOn1sFZghxyV zAo$x7w4TQWUh?n_y3T-aoul(1g`!1gY5<{w#nU&pB=UoCOI`aJdk9M!gua zVy!fC%FOJ_fYHhfv0@ShnW{y2!@3ZuU3Ssq030bU-;J>-{p=%LMkG^Zj8-3i!#jJPJC6oBcNwEWUj?ye zrvRh!r`H?8=|oBDKSy!U!Jv`$)=BU5dQ%>Uc&bXI8Gi2Wl#O$k<+Gyn6q=r%Gk>U3 zhXXD8wqc_l3#XEYTd@e}&=6wcCL9z3>o`Ut^)jUM^vf-`GM~gs!9Qd5fV6VZGPWrB z;cGJBZu7sqdAX%hG;a$uAD~+ABcKG^dE+*Do-Gb)lBryML{Lmp!0km%!{s#q$#`bM z#h3eozq3h@Er!>z{AyE6+BnbCP!zOh@(4C91Q|mUSE>kxZqf)cMo44ym2mX0@k4bF z;N0@FXaWJiiZYsk`MJW;8vVEbv@)4**ELmob(O@Sf+AkPE$nu*cdgO5wk`LLn&yYU z^1i*)%FLmyOHisBL}3r#Pm(THQ*-u{_lPDI%QfBpry}AZo?@l9Y>uDm^bY*GF9Z6D zATxGX-28S3P`iFqJ;Z%J7vN@vZ^Wd0WPPUGb~AQ-Ew|kahrMJALzLf6pleC`t zc-@g68b?9!;aw{p9cla^)qMJhM9A0Z*hhAwpc3LcC+2C#kl9R|-Z9Fg<_CbbPk2|X zi(F&@@$rkN1tKB0G}-$F!GVoAjM3-m4-l%XA6f@*`~(CW&?mhzVY~ncJw6lZ zWw$)*g^q1BqW3v%g28Aa4~<8W3Bx8?m9dcyKGnLef-4DEH!uOCOptN2$-lZyQiu22YtNgRkxp-YiiZy1(^_}*|^d)xaI?jww(X=PAx{2RyGdv zYLwVhZ1?}pB$dS>4cGE33VvNQ0w-_H=$9N98SIYTmzrv3X#7Y5v zCv{oxbt=tnsF{;_KRy!wn}n1!{G!Nl-7k&WNR6^Pf5<>O65QebxsoPYL@iMMw`H}z zp(@D;$qosGAj!WKzWG$3JV1URM&N^ti09N=5)y0pE6C~)k&{Y>f@+)k%-xBDCuo}Z zhB_5K%mYS95sEE7LB0Lr3ThR&|IF54)V|lFFjbb}aEjSk5()!$u94%8W3)2gX)#|$ zm~6NT)>m5gFbJ6=x$i~#a@v0+zL8xT36ZpUwZe8P**)71fIFfOnv_0AMX${=gmr@^ zLu4jrb+bbxyF<-f9 z{|;ec`MZk3!z>*`dvQ=DwI&{>HXaJ{armZ2D_ryFku-!&=n>d6N~&BF z^<+jo2i)}bq02tiRwd@A@dBe3+TD0X~6&!;bNg5*=oTq%T*dmok{M4zh^OzSMY)6S zW-CXyzH-XQo*wXGAyV8W!eG_)hK3P1#{9#G|F^{sFdN!~6->dSl?FsENLqZE-AN_+ zAh>~oyh%FVKZmOe0=3&Cn+fKq@<%aF*oIyt}8 z{qBtQvG;>a4C_887vt4@Y3x-OPLRw28{y-Y;IJ;_{l?wo9n7TWt=NVuqKL z6rA={D#Qw%P22#6$sx1vzkv7+aQ}%l3E%kQU_koHs>n-n#Li5uLG-Lj0S}8eFbvvM z47(@4K_wJj`(w+fl7G06NGpZclnJ!n%qS+*h1vn~O4pGswHYDD>V$beNp`Za{=$1@ zxI!-#;ur#c0_L4~%u*d6=pK4Dg?<{UjL0U+N6r5k02Z^hqdd}>)EX+R9~gQmjroZU zrwL%1(g#|NRN}P??-9Us31A`Ow5j5>PZ=VNwtk!c^b&KX93t*9A(pgmqZAJ!auwiH z4GP3~;#^YQ6dY2M3|wZXX=b~ZfD;k+%Q+EX_yqRiy0odfDVp373g-hNjYI*Lpw;%C zxM`w|RlF`@O@$Rmh%yCJ)(p_aKD54%(;R8%v_W26m-26EQ{UP>fhs|vixhbqQ5nDh ztUhg==m3m9>nzsRAFoH3+PR`;>P$EuSht6is2Ac*4U16)M0F7b&n&NFtz=ZhbNFr}&y5 zs7ZAPNa35n$45r+;&I5x!jyEPJO}=Jix$d;_gShxv$GWxC;bB@2_=HY)wevX>6UvY{dm;j&lR>|C`KTZi--jF(Tv8aYwyr_wXM%#y1apc~8u+Mta78`b_EaFDMF~T^ zNEt2`2!orUm1`a$BZ9?I+6ZkIOOZ1Lz1X zV^dssqschAJB@xDzf-a-;rRuiP86|!n>X9U@PAvhvn!!9V!)hS!7PiW21B%H36$=! zq$yJULX^Ot17O~B+We^ue07JJ!`Xuw7(GyBr&&$QzL3R`d) zUANld0K1fQe!Aj7MD?*Q{2Cb+PA_RaxTZ6bX#ib?_8$nBc-1fSUgd^36zfUpU8-#H zuVpVz-aIGOGB?2x1aP}KiaTke(ClF#>98Qrenbp-W3F1NZ<0P(w3;?401f<$k`sZJ z7%)fOgAw_4KP98SxeA3$(D(ENvXVo7eQojw>KX^tu6`i+Dz92kZu#@Tpg;Png1O!% z2&V3zy6^FGktad)6=7WD!Z#`l`8rBN+kb$Wa^?dYNKP5BEq4H+(Cfag<~|qa?mtKe zpD}D6x-r^5bWEADxj1lR<9r8tDSi9Y=#p2-dnOA^JJ!IPHE$E(u;KSxhpZb2)?{9y zftf{TWCRj8Kf!AuVOY#BR6+3n@C@B7eMr2IBwYIk&5IZN;+b0LdBg#>ViiB0Tu~J~ zTnz8&%87g5g&}=@h1})GttLah2!EZ)RUmh!3k?Iwe7@1EjD7#Yw0{OPZT775_ofrh z0f4ifYRh?Q;74ugN7t}x5#P*)VMLdnj@m?W#9vL2Gwu5!w{pJx2BT(~3#mB%=LBB} zeZL@h>65CiU-c|c;Dl2*Jn;i|1I}cJk|v;}VRct(-%fZvGiCTK<+b~67(VX#M{tU3 z^n<|6hQLf7TOi!NT{iu}Z$hVrP5c=!vjEYYsu>JSn=lEG_Cn8#doL3^%!W=hbI+5~ zE01TOSOFupFtf49_J;B!uB$MqmBW%C`4@juDlVC?tm5W`pFzHeWfCI0;#PT-k5zWm zjkXr=(BEk5MtKd}elxTAmOZZwup7o#sxI+X-?^c3zajGnGc{6nlFCX;{~4Y{Mex<* z;J(jrsrVW)d$(_;63HrDkFLYCR!XXtT{naq{WfuS!NVM z@IVc7`;_9ABv&rXtX}PIPg@3to<6tJ_t0&K1M8agILTlPkq;xCclQA?DBt1fDeK>B+GlOQ#_;EYBZsHcvZBdyIsB;^W(oEqhbyOJ?*O>-0n*b2c6U-(bOp}dvP zaAL0)W0@;SNFA9%ohuZ0u4ZYjmTO!K){5JP>~IEkq3!_joDPBJGbFOHkZpbk3)n*j z?EAhZ>;DXFY|N-W7@8|@1WX=XcV5QyQ^xe4+VM!6j@Ao?NR7P&z#}tUowmv2F217K zGjmF-jI1C6``6p`9a2+E0o|o=ZNAE#PVo5<`vWh>UU@RIN4)2L{NLaEe(6iU`*uI~ zxXwA(xz2Swx+9-~q8rOO-Lbet)9fPAt#jcR``cr9tnyadpY+(D|y{0cI!lY-3W3@)e6m`I`D-c%h;A zp<>OeP)hXr9j|G9s&V~>o@;a_dCbFp#wdpsI;ZIu!&ZOJkIMg6MD@t2_4SFKE8OVDs(MCcFaw2jQR;x z59|Rp`=ULBE?n$WKCz@mAag#H3iL&PrPe>c+DhdoBdjRb`RxB+xwLt&U{iExpS$r| zrj^Ra5tR5kqVVBQi&+_y8$dAr-l93(c%9jR15$vfcobhLVF#K92@jJ+VD~3JgFF-e zlB3LE*)mX*O690omEUm9Kz46ZU!&Hh|Ex8%cP2;@d42c4X7xTd#Xd@57rMV9QEsQL zk=-!BA>O&W2Ie8YO=ajL8*K74v}-yJgNDwT2b@+&Es#V98vpij5AN4X@3$7Qg3sMg zjZW^}Tvf$G-aD3pyA)shSHX}~fBzKebsGM8R3TrROK-=4D~pl&^n?5Fv*ecSA2-Fn z;-?_Qro%V)xa>XUSSfwma@smnLS~0*nruwV`Q9PL^MB#vqnL$)T=yvxMZ25V-Y8Nj zw+K)*eNs4I3=V-=itv*YG_3~YIEP%ggEghLkAN)iW#eGWM_UYL8pg;I6ADsZZ#Uzr zw(2cu5*bL|et-O*j9XY?FdI=+$&Gxo%Zb>Y-?~OT%exi8#x6PfqhEChz1ZuZQJw5t zoxZapw$#)F0m_GvK^wc#RV;5%tTQ;KE4bPO1}stLCHxB$uO6~zbu)!7I_yq?%V6(e z`=2Yg;1-g3s>{^Ab-vjP_2AEsQ(Y5rI@g)@8FYTw+S1;txLORuD_VKSD-I!fP3r=2 z2j71ag3puth{zXB{b3?(>xXHEu%K(aYOthCvQACIOzwy~TBq%$w!idnke zo!}g-FEteCr?N5OJj;+6jJS{IrIbs%^HO>mkTbr^+H%775MiN8jX^7Gm{`TwSv@Gp z>4w`L6q^%X$Q|z~Bx>>Gd>w|5+c6(m{~pRnSZp@7cS6wukR9`TrMO9iNGY5(7+I@E zKxfg`ZgFFEY_3^>vo&3r@fMDdIE~}m-IgC}f#wyJefhb-RGZ7Y$Lpe4Pf}QeYPeTR zV7FJlg4Y4?61a4f?d_Dx1|&c$yycJC`Jq3aAj(2beEIp7Hm^P7-(5oi$R{GY00NQ-ki z^Hi*bbqc4{1IOmTw`WRxoO$m47J6!`GdiX#dN&M^wmQMmNF<0NHHmI})&JTR&%$zZ zo00oK@(XHSG3TGWVc-cnIIYM(Jv{e|TqXriSKt6jLzglI3ww2o508Gsy?>MU=HkOE z4*hS>KMPnsH(Mk-fP69mn%CWmMfHv@pSAq>KeVt1{$GRO&H|*!@)f6|U)b_N%UX_&U)i=JEB zGMzL0n71XFCSe?3Qqs4IUHz&TUAVg!dVA0RH$*6f0!K1&XZWlBJ@!W;Jv({pso#hW z-gb#{M!KqiUc632MNO_CzWDkHT5A4qCF?N{s7e5Lbw|^6ZK96Qgo@zByXH!_p5|(U ztiH+<5!3yc6R9lGCg8UVd~l8v|74ZKsRW zjN;%<7UzbnT?O5(n#qR z>*XoE(R{ph^6$-;$*ueb!t#MR8B3|V3dv6o)t(Y9L$Zk-Z@3ZBe2l7u4caC4+DspHP{ z9ol`GJfC^Bpf56|qItPZWCR5I9Srr)X7k))gq`!1L`n@@84?_+^3P~p!^0zFjcxJv z-t-0;TlCV4>C$vzJlX5s-v9EO8lrFHO%idVu_H{7-38b3CfkzG%J{2g1J_uR@7>f0 zNxq~y4XuGZljJ5`YsOW)pv`Pt?*}k|IXbOSPdSn*#5KAejOY|pf=jMan_lr&>oym# zecv#ZSQ$5YaS<6lOxPH(Y=)n3rP<*AMsxFP&ChgBrLs7($qJ~gQvPQc)Du^F#gKL# znOCg;yVwBbT%vFK*h(cT8j>%s)gk5|^3?dJh;cs;xg6NkcHlli)ibkf!-T&3+rC|x z@E^h7jsK0vEi&C)*#-tA4`iIXD#nNxOLFBz4Y>YPUV|Y%`5%PHIWUW;}o73t%K zvOi}M$a|3VGvq_nkc?p&PL*;a&_t%1wS>dFa-)PutKWG?-PAYMIccTx8gxwMQ%TTg z;JGX(u|FMqW2a6}!GlDRs@NHK4m?y$*UQfIIxPJiA1vCsU>Cf3QZr2c+v4MwvI`() zu>;Ai=ts#NK}w=K^QAI>3@cN6M(J{qhcH|CqlS{758KgVcFP~_a_4&1>XQ|C&wu*~ zdT3fTFGOT`Yk#R?7M@0eiDesZSX|ZXoZ5>@fsdT)#iRuqDMx26Ov0c#Ipsq66Wi=Q ze3{U%_m)jPdZzT~4gI0Tk30y`cDMkRX1i!8UW)G2zp+`!(Wc!e!yH#4P^e&E4t$HK zvD-&GaQTkxkSWY)+v59|YK{Z*#fv_|6=Y~`&|Os?xT^fwQi_4zk3oxp(Z3>W9)~~0 z?#Z|Sem2#HgHav^5=xmdBpeAach9Y)OuwLi{hVL3>Oz^qgKhN(AKr~a82(;do}znY zLh&9>zbWM~LGi53<;$$h6}k#<$=9@tR@T5n*EYw~>U@ zoJz(|YC=;EN!5y;Z9T`>dj`@Cfy3v$vE0L7+FB536npLH#OjZxZpnybDTw~uMnU>q zbIfHpVIPDTXY9?EI&x~hQFUc%So#t5Xt|l=^CK?-m-^wibM+nGZRi2*@R00?5DyY* zWMYk3dEF|Id;6baP~IhvHNL#A@_DeQBBbsws5Vz14JAb6h;eclNoHu8ScB-00#W<7tgJ)0tFh**kE>`@&8=({_E@qmpdN9G)kV!8S)))0+of zVmDR*b}%!ya_X2m9evdL9d+}*Xp-^iehMpPLytXc9${!7p+>+yCwDV0or&Gk7Sirv z-J@@aR$@j_$43(Si`4%P>(0wJ;HKd)+4PFlR=4CH2X=iC+ds2T+7uAn97t&etpjoB zrf3>%C97IAU9|OsyxcU5JNSbQv5-W;!j*gLQYv#CU(YKkIA{Nn29MQ3>}MeHGkK8v zZw7w!+y}Yr-*t4+c|fjf_0Up;OO8^LTJj=;AWRZrn=ia2mOXibY0885_$0%$@)}0b z<{;0To1QBay+CK)^xFKipMP=&)02N7@#iWNpe@BmWG;bju~lsny1SkXy~JjuSrbwe znZ8fNM*d@X@G-yR|LN)lNiwC+a#xuz&D+L6)QHxWsFwojnJfz*vNx_LvV*wHOZW48HqGl!JEgZuw!ReQWI`xgPb=sXyoP_ ztylw<=GsqoHH|e`VVwkNXMI`p;aM=nbELnTMr9Weg$Y`l8X}ZvJ(w+m_9GA7n4Qq@ z@P9C#JMvay&q}YY1}iQUyGsF_Vqp%qL`b;g>n}X`)_E^IdS9zZK^WLUHa(l&)7Eew z-d?h4!=9<56&egR+c2L|p|0loBV@Gvhw1ms}_8XfvBd5rN>u(R)hgQwIA39Tco8~*fd50ls2*rr6iK>VuIoi zwzP@Gmf#iYvP3c%k|U> zl7qMAU_6kO2nfFJHJ&ITcfe;5@fX-7-B+LFo`@_T8ovv&4dzZut^EP4_JRrDdPX*aH>*YmStqYg*coDMOk9XC<4MzMBHIJf+nWBj@v*2B}M-u+J zn}Lx&2#9Z8d8U19k01lHKJ!vGhfVFO;CfeUV+#y|FPH8-I)N=ym{C)osl3SpAJCV| zi`|(A8;)G!X?waV%96N8QgjWboW%sbk!}L+x)^N6{>(F8)}%E~U0(dBch`^5A3(i; zQ2l`4cc4nctbN4--oO5YB~C-(bA_!?ax^Z_A?7n84uEyFbMlILW5q)?uM_Mlu8fv6 zm2xbP{(~N!BD0g`sj&8`@H_u>j;WZPs-E)12NV+~AM-}JU<+8DnJ5gUt8n7=i_|bt zY1V?vPz7DRfx=CufRMl7e}`!rOr8dQ36+d+1%ZQh8ebEH%_o?i;HsU#7yJv;*5(Vy z)v%VNc15tI5+Lsk>8Gy=Ivf)xyo1pQW`|`8)V=D|9~|C-$yMN>HqYSWMI9VA_FKZf zT+W%565YXO7UXzAZhLY^c6v;(1kH-ite1Uh`T~J-=HIgpqiR~Zs=g*$?@N}>2{FsJ z4*sC?rZY1&Bg4IOIfxY}A_2L0+xU&a+J7ZP~KVu?Ph*TG&uV zKH$*ws;@>OS)spcylqD<@Rd^x{Y6)#hLL=F>Q)-_8P`!z&fj@;5%?RPud{uX=rhEV zi6oKzxd~B(j6iNgrh1u^1=us4V83($?AQxWvXBtE@4!Q$@aky+mvdRhJpmV%12oSZ zWM&ogGOGo!fN?eLz-(9Z_zyS;9KO@I<^7Lw+Qh9A9YLR_e-M5&Vmiy&5w@7S(q7W! zh$Y2ku$PR;?4``Dq+Vq;BlH*9Lt4^{pYp(B##R7kYNl{?+d_ixvxe{TY{^|G(G~Yn z>u1})Ek>qzOCvx2-kd;ad!2$!ue!|#w8%fGR*ZpLCljEX=fWrEdP<2yu01n&^J0SB zi1Oy2kKWdDW|)$%OXqE>#nl>LMhCBu_TCU>g8(SfbB9{*S7%jK&ER~gN$w*sOfKgC zsWE|!UrH=(M*OZ^7R$cI)eqiR;TV~OL!L*-NRp-)UQ8%{409vD(bJ?h?p*(50c52m4?_+R3Q2Dy0}eLsBP4)*)*j45k}chinzP3 zz5<7-%c6aK7k}d-wR$Pj-U!R?)*1Cux^2Q1aR&RYW~Qz$HL}sR4SXS#?LBdyZ-51p z9tOkJ^pD6$BSEn*mV0SJrZ~FISZX!u^dCn`Mo2-%LlAz$Vr|DujqrCpde}bs%ZRdm z-j-gn5=rLd$^#l>|JmdGt4eakMY3bqt7o0mt}?-{K|eR_{jPbfE;QbYDTZ!W!e&jN6_oX1Q|0g!0NKtHA7yXV!In)VMwNCi zV69w6xQwk|Z1OQ`nH)E95bmXTwJ>4+FIpn_7Fjba77s7Rw^M!jxB)pg5D0{=)(| z^4>Wr!D<7 zAzEG7^!({tN%B4?#HMOTK6&%&K_lf+Hx7EqaDzL#Jos}bNOEjN2m+$sL3>MY_ao#- z5}FX%{yuFznCgNnWz?T+es37FzKY}iUf6wrT@?bjpBjM|uZIf0svY8e{;WLUN|bNg zdDu58f7nk$C;k+DZ*n5#q|S^7{~B5T&6e$kI_RPN8`RT?zz+qmWp&9$?Lh;j#Jrc% zWpk5zixiosMFQ45^ba{gc4K`$idw7g?2~xIbDvWGQI3BCt>lgpq%dsz~x|tTO?oSX%@8JA( z+0SR!7qN}C1NHCl4!Cmvg0;&o5?9GQbJ?ex7iOxH_w6 z7hjT0xq&etVlV2RO0eRQKi_v9LNmjMGuGcoj?ndeqN#aep2lSuNgvic^t@3Src8~# z+?KGcVUY#>MQ?tmg2ocU!9BIHXFmGtbVK|9>GR0N*vWr}93Zst0D*I@xYn0=n0r|% z%iC2yeBp}NkL)t|X5PGo?pTVVdQHtA3<_8_Hz-_0sb55K5_%Yg2p(Ro#MIus^cJfC3t3$OyBpvpG_upsKk;z+wm({UbE?RS zO&|$wPMD{Utko-`sS&`>k)k*`gY9E?_T#bEuSkP)Qhz~O{{=d(L5~Zp2O{r6=tz=&{(K>t#EjEk^p7R#ysS6Hp{%edVQTxY!YlqH}nm7hsBG$ z!1H*wgIR+QKF;=cBxvg==j#f^32*&^+D1ozVg(rl{;Ts1edCYT7v!F}zdXS{YJjr+ z`P95(xMmVKKb1=x)S>dr=7KS0&+~VMU}4~%P~Kb9ytV}L@Z)CJc8SrW6cw-XGshte z=>&{mjZ6uIimQsFj%G`BJ3hXer07O3BTx}P&K?9d?4@{z^Yw9UyMMiSf=AuTUxD?X z)39y@(tH9{91ZF^O^C2CU@*14Zd%8y4DCy;V+LeOfk7QV&iDV}mlVdwCMd2{*5wZz zdcTA-7LzY>AcL1a7+4J>Hph)h&7b`RD8L+t2sVxk150QX`CyJy`Wok9l+FV?wSX{yAj_KibC!A!^AUlDyeQ-BTV(;M3kW|gWw zb#W0Nre)3;y*n4@%VngHq%KHy{nIPEC%i=MB($z9AdMVOExCe}9W0tL+G_k+X2OXUjHljs=Y|DF$h2L>;~9f-;8NKbzh$9Tsw-Yh6FMF87lOfuuEGt&MfDK^Lb z?%Ht_9$CpKlWPa^{h_>?A7`f!5JsPZj25opg9hKWq01H%QA@83D z?9yD_w;$-aIltA!B|cS_7=bZx4?7}cDkA#VKktN1xQVO*op2KgLpNLI={MiP=Hh!d zDk5&dMdT+baoh+E33_WOk@8VapZSQ7YYFS!YDtqRZ}^|;dFUP}w=`1I`k$~XV}wWh zyL_Zh>sGVo!TuU2@sn*>&9q}_csx)5-p8PqGF$5Pd;%LO&so-R;WmgWyq`ikIUw@4 zI1~8`pR_gW(5aGCJ-|Pp1hb!)W+q`rJV0ld1@uH!G8t^0cY0lli0|eIty1OCR;Z^& zx^OJ4=A}BF-QZncy7t4*3Wn<&mVO%28%X~>5%?W}p>N%vz$mRJVF8x@z=GqvHMZY~$ zGwL)P&7dct2f;7QFe^sRB5br_5TW_eGbcIl7jZLD%m2W&4!OB7Nn6=hjfL}Br)u?s zPJ@K9-wFTlddNt;%$GENC#!?jNN{Y1rv0g%LIxk{^{urlgw$p#(1~hR2Epu+v1*84 z@b%^N>ol5Iw(CTko%niZ<2i9O!DeY-hR}^T6n6XKU<*d;HR@&1qebO+m0nO&gQ3X~ z);}DLK@p9?>@^?=4*g`h#(F3y2tI;r#ddT*2yYoR(-9T-dAhN*0QGx7KSF5Nu zoqMs4Xzx1xvb+3t$tmN_o`oqP3hm~Q-k20p-2;##%h&XJLF zw=;v8=W+wj<-G3!r!2S>0-0!xS z^~X2IK)aZm1a~f_cUn7{fbPx1JFiMVPj?6=)_D3;vOKxUa@0Fx{qL<(WG>a?O>w7W zXlGtFssy1eWu%4zP$*(~FfivAsN&}#3$4)o>T1nLs9jl!%F<><{VO=|V4k8xDM|2s}W2D%m=V}#P zFO!!+Gxb5)y#tp@Z@?+tC3nnqF_E-N$<=&%Lj>HQJW8`j1Ef8ceDwvPKIlt~y8p1jGg`YI8Z6*vPO zkvd)UTVfoS#+uLfgPS8izPXxqNECs^e_@fKGgcv59dP8)oyu|I?(6h94;;jdS9DEM zxy2d}`$utFaPkBN^9HlG;TEPCn`35|Gkhr|>UKV_%Xx8nh0i;{zbiq736J1!{H^0o ztxOI31}e|%iZySb@qso|dKL&9sDzgxkL!|+GS%c9EvR1vF^|HdN4F8hbOZefe`m zEEOubdDL-Q6f@SnYfM@bO=GiB9qpsQ=bIo@;r9g+Jx3KU-kW2;W>OE$z~ci4mQBw0 zfe?kM_5Kd+i2EDiTL6aIJ(8n+!lQ5bTo4>Te*aYVD|KzN%uGd3lYwi$(1ZLxXN{0_ zA;I*o{6p~km6-cisrtt#@*t;JueAP2{$nrb{d_b`t8+S=M4FbOl`NU{QTB{ol5vy4 zCG-1e4$U#1mL-Yy&*lu=!lUU=@0}++xQ~nPg*+AMh?Qi%e+6E^Ynn|`%2`yY%w$+G zFOrhuo$znt+gTyS2&h?vmBHnE7>W^-cMj&M!_%%{7hqMwWd9QMHY}82mtaBJ^z(S) z=z%Dzglo@Ysm>ti`>6ViBBCL}r@P+Xd0qJ^AKls$5vslTme7>|HeK=$x0Q>w4Fn-Z zg2uOZ-94+k4e)2s3P3^+ywfvJKOW@*9`+$Bmd5gN#a-s+gatH^W<}w5&z-gB>f7Y| zN(T?rRKkK#qrti|erH)+3Qc;7&2R(AU(A@^pk&o#V1mSlzq4)L9s_y@fTI$N|JEK{ z`Sul2#$|btw;nX=kj~-k2nz2E{`LmU*H9>ScqZqyg|N_N{8qY2L6B|^=g;W`5A1I2 zqp$KP?Iav#dT82-gG?=g^7t+RY_A@}B7~^V^WorHxK;c6V#i5P@sVz_;(7YmJ^rTp zDunn>{4zmlI{GeR1z#uF>~dW48FqbF%{SW1m0(dk6% zXCBk|k}Wp~pDRozm{-ad_Z&)lcqYoAlYD3tZd)?i@)Hi6a{Kyssf2WxK^vV= z+iTDo(1E{GW{w!Ae*~9*wHNfWrjz7TyqV*FhM?ijog3oY_@{GmO2NCHs>c!K2$W5O-GKR9HK5Xx#4!`lJig!3Pzo4_B6WBNJl_^ z{^fvc0115!)&P0$$3xhU2x!$NAk;RXI}MG;13ri9i1fB2A+7+r5Y1cW6z3UCwe3q@ zBCPd>a!-YC$;*UF7STv-iA59nsQ=)~KVrpSw;yx@A&M&bF>L1#_%{|?iN$6FLN9j) z)!So(pVFJ-91MG>h{oi=0g+}R76s1KIl2h!rLzivNL`)iLyZi8Rl?v?fjL=@q##Yw zqMF&SYJm@JU%#n3hjcyNYwa92P9u&IEjwJ;#sq$g^?pftu0DTyI*57O@>{X&M2@pq zM8inc+rLdNT#BsI;imLHQY*4BK(eNi-mKS8LqPYsx%IU|-A^bS4?+)SeOJ@$L7yli zKxTi13-@!1wAExC6?z8LLYlyltm93q$SRxU&F^2fjB=wPAaJOwbF`Mrb_9#il~H~f z4Jf}wR_pzX9tRx1Ivk(hXnAMY8R-9ADck~fd-01;7wl_Im)mZCEcr++VGh>h#1u)y zo58@3DdiQU6PQ{(&Fs#Nb|Ub826~Qx`)Oi#t{1B-24Ywb%w%R3avX97U(L7r>o~z%m_Z;h9 zb?a8?ZMbiI=CrsKzcmXblbiQ%Q_%XfHqW633TBaT)O;`yn_y2VVeQm`WkfEst65U5|jWb)CfP?ii{}Q;T?F4$V9E=W@NA%Z& zS6cqvzv5kJ`OvQ5!HyAA6nGY~Td}jx?!lZ93m);*(Tr~q(_PF}pS%Au`egEI5FgRx zf!Pxs+WWBI=JH?5hhn4+-hn*8Qa?>ge;WSz^;|P8Hs=z#$Rqok#Kr3?6)v~CSvy+p zjm2X=ik9uuJ8VuSHzlysQlTKgt1fW;&z7h2^0Dye`t4~HY0f2@r7g?3 z(Z}&FIP~h70s&PEiQX(Xo3SfWA9z%MZI=C3f9)bDotb&nnOn#A=9=@i%vViiwp5%R z*_mVwR5iP3wHAff0BKzGbXjg7mvz-S5S5>j_2;JMtiPrucy6>RUD_-rSK=MG82D+P2fw&r~^W~0*1Q-LaC`v z9}_s??|jpy(VyZe7fjeyLdXs&o)j5|LyMm~7(diXeru_Fx;YCt-uz z^r0Nb4@_N~91KU<0c?4v@g;baUfGzw-SW7y8Tucu?P~RW(!ZaP_uk^`4vbm0JeQ;V z3>LjPmQVds0xv8H+@svG?08k1Aju9!1`X$8eXsw$u$nKgW+6~jobfOUiIZ!|#Ir`G zvkcXDpuZt08a8VnT1d|!{`NHE*Qxo{nbZf9aapuo)U%L_E5;w&8ZmULyst{(PW>u@08Z^`fcx*k~tl(zo)im_S9!5Me*2;1@+;ja zfl+1b#!Yr=!VNrr;V*|g*3oAx6&QaFLK@0qnt5RT8?#Vk8mA%omKG2o_nh9HM?P8w z3({w&-7yYv_%~SY(T6@(*$5XAo<#9j6NM5V8k-(6{%Qn0Lx`f?+~lAO9Ux{}rpl@x z*G4f}yY8qHT2->AWi6Xfe>{WJycg@@rqbV#u9N5;C=#MLnTw-afHR8=8yOBBbQCcs z$0p-LD%jRv44Mo&X(<`kdMY4Mr+HV2B;@*aMaA;`hsHOZteE7>e0iX_cK(Mo#8;xL($wRN+SbC$&@t&P4n;r@AZ#%(jZw~Fj} zM|OlEm2W-*_lQHjz3$awZc-q&P9;(8mIW4iiE%V*4 zt4BdImO(r)^);%atMwP1?*eiHMZojcEf|jwB!ApaBi`vH7WyH9sbbeh`RfuqH%d>_ zD#pg~3oV{K{}M92!(Vq^bKdQ$^lcGfYx|_2RjjVndiyQ}n|E4KqO95)V3)Q1U8)5{ zl&h+JoI^;Y3gRx_b-RSye{up+oumV0{;sjlkxJO>*hs9yYVXEU<{f}H{aK6sSoWXR z1IVDAM7=7k=U+S&wN!P23W~`U&zsxLi zI}4Xq#p+hANzhZ+FzDMF9TK*+=^$<&keQ)4Wox$FW%!U##tnzh=}C)8gwCh9VKS<= z`of5dx5%TtvlCbrt6R1jgLJsJM#|`z*;}|jfG(O*oo(SahM{kB_dyL>QNEhfk1a+f z5fv|@YMqL8q@15`92xBp+&P-o8J?8`j5hzyQx`7J#Gq@$>UbNL5C0SsB?PPB;Z>9@ zU3Gi)XXiV{m|7xn#AELLZYq#D=-R96whlmiJmz(md1%fmibLu2P=K9X{5$Uq9~aq= zEwlMct9V1tq)!LP;lP-FL)A$_i?4fhXrG#iNchs^)$Dx5e3+C<-v+wNF(#riR}ME* z5U)Sf!BkphxSn1P8{p?in-xFf8ahtQnwrm=*36pD(T2(1laF=w6qW-JlzYFkU>=-091`IX!96hvtORqu6Il z(DX^h6Z1#PhhqZq^+OeI0T~>5TluhGjLHD`HCC zJP2$5NlyMjM>2AAG)6MSHuf;?KR$s*rcgO)D2xiFzcR`qX1(maBm(jC6G~q&v7imX zY$k(abtMI^t;*}Wi55%OdH;@{<~RHRL)fep@3;5i2ilyJ_Zmr!)~?Ru9Qex$T%9h8 z)4Td~x1@{}3u~eIV}9%HqoJmdbH*NP9GVQZ)SYH{uuVZK>EKiH!KY4!<8%T0r({sL zAL|e-`qZ(0l)iJ=h4=*W@BQNvX#W`)_gW|eXY}hXz05Bx+B?tpFc83fXKekB(eD%{ z$24gI1-;$lyTm3;WW)*C=YYDoHfF(tSp7(TZgjJE;B>ykr)T&tk#W#HZW_=Q)K|o1 z6vTp3Mdv-TW2m?8$T6&ifg7~hqqnP&V`}6zJCdLWaOl5DJc^~3#K*pi=^!=rFwu3d zpmCx88ZC^bGmSKTeQNR27;nbxzqGk<@qcc zkyMsHHvc_Q=`KnC5at~ytlWa*UPj?Qi24R!UBmENyJ=p$4zpwYQ#bwRV38*t6m3D} z2A$Zp+JcLhY~|XCD^?5K+wz;{CUkBwzB@}#L$ho`mH$Dw^jb~XJaT0ahy4Sch2@Z$ zdh}Te6U0H;AI`<$|BH0D;+iulydE=N?~w@!hp7hYq_!dp|#VR2)#F zGyFo_5xj42D(E{_CuIJDj77DvH`56(dgDu0?v^JMiymOvp6&x$kd@JFwD(ZVEv3zAZ#Q;6tf%+mh@R z*O)gKYTwsgCbf=g6es9h?6tR+48$62F9d0DS+#2*vOa2?>G^9k9i6bT9@?JMXv@-! z7vt}u{HW9=b4RM$3Fht7+&ua=6$0vOC`zZ08~{J&Wla67p;EI0Jdx6$y0+-o(O=*y z@ac2}$~MPQl|M-4c*i0>u@Wr`>eS_Wah2VfD>qrYj0J&+#&!0=?XUJXmluMCkD^%MBb)A{~Bfc#V+d%uTElnybX^q-#W*x?QvXut<(ByIJjghrh1op0>VUb(kmpAqeJ%D*Wg<%RZsA@Y6lJ*@3AK{o;1B#*oAhss72EO&&yymc060 zHHHgvIl(ygrMNk5#%RF(pd2I@7-xPRFk`$^ z9~&153G&i+OY`a{!)NH8V_eVSdrKE7S&TEr)*;_)KJW|XT~6l>U)cwLf~B|tec>(H zHoB>;dmY+CbD=E;ff4K2&fHYPY;_bz=sVT0r*3qBPRQ{Bv+)BZN@2;|;!o?kZ8aW` zGA{A5{-v$ZrOO#R|K6SB3~;o+ZSLYJa&mw6X`!XHLUteX!R?EPvT#fN;0xg-0JY4y zDXST^Oz#Un}AQZ}{7AYRwa(fEna=26{(1BCzx$2>|9|x33xjSA3JJ>}#UZuKi zQVd?{4tq$%r9ODjS>1mfp6XK5RWokOSFHYUdlK#MFp#pJMFy_{5ie5l>+pqJzd7ai zAA~gtjxvv3s86+~HSGs}Kwd#Jo$1eRM=j!U$UkwAZK-c5Swb(svun-ZcL{a|^A)0?BI24fP)Zb9UipXAq6s?^-unAyJ-0)eGfPZ3 z?h`sc9)1iZ6PidkC!2I`6CYac6a>t>W1R6J7}R@_do<#3e5kDgea{sIO~<)gqx5St z@`-JD?h`781oXd~e%1v5#6lS!n7tC1HVQ(Gt*Y3|dT*_&VMUtO6RVUlwZM+eZkaYT z9Jr8(Q1lw%xcOv$QB$9jydgPW?YoCz2^Ga`uJtUFl{*NM{E95Gf@t-jATU&~+)8cf zFG=~M%H0Gbh70-+-DQ6=ShO(3l19XptGV*$T}X!s#qf|a1ui0Wu2u`^!I!Ml)+)G^ z&ZK~mTw#slf&<4)*k7s=n#|*Vh1w~Tq!ydHNKK-t<2T%rgPJ&2Xq7@g^pn{YS zGDH>oNLt8o+%BbG_F`#j9si%2-wa-qT^*D z!OItTcp*C7w)EZQ`JHCs3KI1WA<>_j(}mum86}ej2+O5qdHS8C<0{oLXYliv>E8g_ zdmnaK&(}B6g7eBdnzVN`CGTiFrG80R3=iGHP87V=W;v9DkU-}2Q|w4R)TEZ&p_G^G za<2Nba+{Y&UIj$#C*N&0&(c~mB6YJ?S@0tiB#&hDW}Q;u#}^N0=@zs-hETXYxY4i% z#R0j*Qu;s5EZXM%yEwKZsbdye&60Xw4$kg_Es?ryYuMk-X?4!t%yGNJ8Zh%!{hsCn z`@`?A+TOGwgf}QJmc%-Lld+_(l~{4WDy@f2ej25BTX7~;)qFJfDo(-G*=m^8WT$M8 z`cd`l%U$p_xb4y}Ca5{hu(^DytA7#SY(&L5|Ip;<1p#JG(w6}U^L^c6`EzR54vn6 zHi6C{KeRP{xH0h!7&Lh`x;JGsw+IN@45q%{vo3jgIj)eCY)X6D|BC~;ilYAX)53dU ztFmtY1ln3ZDyT=RtGCvSK_OE4TFmI(R(02Y_9dDrDBn+7mXn;inqiIQs(ogrcJt%k zNK(%yo$2&0`HaH(YijYAc@fmaBy72Igv6g}W2KPf)5az3RaQLgV?QulrEeU3-l06i zfO8Q+8At3XmNS%T78GtKTlaw;##)%Gt>_B|_cwxN{nYNCLwi-IA*m*X1AuZV4Eq|*Q`oI^_ z6I2Y}>@9_q6)2d_fP}7qQ9qcnS@t>QNC=SdFD!<%+*Pb32H1hjfv)cI0v-zi6^~3c zLD|DIZtD03`*9v8u>Wog;!U!W$LZM!uhnCnFYS{EJPP=QPlgYFyJW?OTw@}NSG$H_ zKo6nB9vE9ZU@Yhd^r%zrp_d*)k%>5(WC>g%x0;Y^WW%2ICOLL`5wXIv2SGxmv?}BH zB@_WbYJH+k{}d+u5@JMfC1XaG=B>i>;?9nlQze5uP4&{f*Srwe?U(!rmNXv4_~Knd!8QqB&5P=`C?*p7=M2MJn)8)G-wauAff&DNJgSxMwr2v?i(uc(>K2WdGUSKr=6{CT6ZoMs0AXp0yOOeu)iz)AFYCkkqxf zD3*#5Vgy#d#PGBm536(tU5J&nxe;ulOtv8PTZje;_a$}pOTD_6)XfG}Xm1{%$^_?i zaK@@akWrk`9~{LiPk8_#`X!fUO6)(8`af@`j}I<LJt!XdqYftYYI&7gVOM5r#&46U$kInSCVOROPYR4Sc&Q9>j z)a(;;!*958d76(lD=$>_(azx%#_ql4(^;HO54e%o4k>UDjl1yYRNO7OQ=9&|5@kh& ze3=&p{b!{Nm5>nCh0mVQrVqzIouo}NFb6uDQ{vGYpk)2{2Hu`7q~R|$YunabTSl}P zySxfC#$Qr2*Ve=ibe^3-Lr6&Df@KYog}-*0AgPU)OP*kgO=9)tcCIaz4vQQ{&^{ci zWu(0v%YTVcr8dNogN>l+ki>&vD1;y_xi)f?U^PpJ#G*v+4eQx}V>l25fi4N^}}KLL@wxqkkwYO*eKc7pGDb z`&U~7tLJWfjUo$fr6!r;t5dgO8>PVSoegtNbKf%SYh5AAD{;;P##$+|aQ}rQC2#t- zr$^$!&6cK^)QGHft|obHV$|KlU_?hR8cX49`3_#BY!-cGSCxK0`%siNG$EmqR5iC! zW${yk6G(@mtlsmelS(BAy0O?7f9q^+$f(GvLFRDh?$6&=(A*t15BkZ*6=#jnUHZQW z4}U!+1Q~rT8|fyKJn$Cu(0`uT8sv*5#94s+`GPIl~9P zv~<7l7&UlbyPXL|wzy3bU^#p7x>vK4x^fBRWNvcQ(n{$RpG8#UA0V`MM}puqwFjEe z&?FMkCUP+cgYw6If9I0)v`x{t`Rkleh;n}rkjVpqA{k%D*SgTzr-%LCs7^5Ydat(h zURC6!fiJw4;?mSIJ)Z4?8A5v-V0;=;dEy3K2(??xRB3hNXu z_NrfOT?ejx(b2O%nPI>W*cWb|#h!jz+WF+hcen(l^07~>{Qg--tg_Yi|B|ZipXa&1 z4H6yLSWM;wGYVmPoOAmV6(W80cT7!u2Zldfx~w8P4`GF%SDrewA?XSdK3P|_zC3J& z$C7C*cFR9h#Xs#g_?|uXV|@RWz8BS?O+y3pzOPG6^!qu)I;yuSuJVau+hJwvj#g3s zD%m%?Wgb2Fqb2VJOGv#3k(Z%=bNyi)gSH^VY}i3u`J8ZtqXq1$Ug8`MaMB$ivKQ!K z@USawp2dlED)#jmYnaIM%gO&FcC#vhQSQzMx6=4+A(GwlO86J7y94F)7&RUh$2E3Zw%=4(v7wOLP7*<}2`#0S&Fcs-DVSj~eJlJ`yc&={RtwJ2n#+eAFl~!0Y$Qj6sPup-(Z?)G~CF=+FwfvfZnrBc%#(`UX=lWTzkj`d&VPmI>J1=ogzlMg{|v;#Wc z3fsv@x|?4nm`@hf1_{V60auJeqU||fNYz!F-|yr%{mPFd9j|!}MtfH^P?a=&zsT|1 zwav=1I3-8#Sr-&f5=IM=iFG~;?Rw^E2QRqFh2>uvv`ue_Z23`P^@CF#oWiQ` zohZ16mkGKk6g10Wq2*i&RH88|*uScx`Rd?*#Eo9te%0%SWD%>a8|%g~8+0G8x9msk z)l$?XH1#|;aPypn_Csd4;!i5jhLTG-*Yzx3y`rNb|E(#eFS3t8C-Y|XnA<%1AfCC!h&;nNVX*&;kFkrogIR-BE@dA4W?-F#-}15`iu+ zl!GAl>RG;xFxmFHR>gLnf7Sa%oxZ`gAgMdw+OvbLtA%TA$#rgl6ea7=n|7YQH7IFN zRWn4nYJS&pM06`B2$7~MUQ9B6%>F;Ph7iToxk}vQ%aDGseZO{+U*;G4zACcP1kofK zbjwk@l>?D(I#Qf;i|p0?Oj&6k%V?J}1vC-Cos`3(9@ z)3!caku7$rL6{NeN$5`)$2B`V@YE#3e8x6O2tCdH&gfn$p6^tfCD4X+Ysz-Of5;ZY z14VX&>YNC%E0P`xTpzre&BK=v7)}W6t4E!wJx&Wc(^ECGRJ{@jVnU^*AA?Gdtwxp+ z+T=#6lJQ3VAwp`CWPT^r7W>o&`7iLxlQAkS;QIk7;%`&S-^@m^N3)+k%Su#BG&BOq zo0qN_X5@Q~92B+WozoSMJ?`@PbQV91d=?27jhG1go#-gPv{Ncg&_c3~-IN_fcSj;~ z`Y{3KoKFMFlTXjS@<10r?mUrycTe#aHP9Yo(^+GxKzl&wx!%%#U2&oj%K9wh_D3I{ zCN;ny=c>$m?xBrH-O5Gx>OyGNQ5nbrb_3oIhxfWI4q}@E$s$e#D?_Y1>=&Em`df2FdZLf}v(#5v-A#qk6 z8))F3w{FzA+nc&$ep6X05mqqN(|rEc@s;(S&bA!VQnb&UEI~=acsmf=lkbHlN&V>n z7WxI|HYa*te@3XCDCXuZlucISyM34G(dC^qZzgd?cqCQ!+haowXB2#ELwl=S@gF7-q2+*mI~UfIJy0)YE_A$L-W zJwTGu!$(nW(Fs(?waxT40TEd629o;g8h1AHVtG#yeR$FNXox{GcRs4KgWoj6~b#^vBJS9EAf|Z<<>K={Z&YrzQJg#Jap_%F@ z+q7l4G3oO4#wS!^=Hy|1Av!}DIuWKkxasqU;!ms+*i8_Hk6IaN!Jn8Zr7tza)iC$; z3niRGq{)T&Wx3{^~Mk|K*W}LhDR}&(cGl{kScW#QB(%)gHu{tPhtf5#Z=*xckdW* z3&FLTt?Z$d3!1Sk4;j8ijdgWp#Yvgjal$G8fay_Pdj5 z1U~LVK3ZqFspZNBCWgMr#ALf@M)q;Jxx{>)49&&O148~De(pq2a5m-)mB4= z%{e~@QDl^O5o-TYwY(4YR? zmbi3X$upwb2R6!e*x*Sy)5%0+Sh&T;|C;h9QW?{+w=8mw6nP{*2E65l3Ba813+}@f z7A%P>pYL!f9ss*cbkCI^)#JAM#`dC{mc}VyJlj`M?xnB%@EE^d0Q{O2>+|CDVM|6| z$gq&Y4aKbllp)r=|9jQrdsu)Op|j?pN{dO(m8ZZR?8BcgASvvO^BeeXf0g-7EQp{fDB3ge`q z0j{sYH|E>)d!`)MkrAb(bj0=QW_85f`g=;BcU7dsaX-&y-TC3|@v)9z&JV}l4GwVq zyT9@}zU2YGGSS$s8Jd{In5<&ZoB?#2*_zq1*H~1w48*GLu2)JGZ|pBgH^8$YMurU7 zSmJFaJ0;buO{V+(77(Z0bK^bcN=gu()S!E#Bval@Gp&1{IVbeUYV2Bc*dy(Y=*98Z z{Qn98R>=%z)V}G3;n8Rgp2-+2(4$(xtsL2Kw=d#$uvsDYdKL|I#=Tg1@#UoJ{C<+6?1hV7KQSV9Y8 z2K%UB^$rjC--{D_UlAY<|Gfq+ks1EBUci;*5|Q^k_|VsL6o`bEAI&wM$`E9Ynj{A=pd|-%WQx=7chH>iZKw2J;PceO(kk)bg%5 z{$-+Lxjg0dPR$+ABIrz(g#ZKh6n>zD!I?C)6 zcto`!ZY7;zCG<*QQ+J=q1(fzEYsrCk#dDw2jeBbZ?Eg5 zb|J>P{d52dn9p0o$HU&xzZszs^}zDJ!yV%{xE&xlaFIEtH>>Eow?G-iIeH%565zNK zWWAlTS00YfYI*Bl9Xcs3lOjwxSrS@lM}qI%b$9R>X*z3VahA(J z6}aj;%!LaMv%4_zj=G21)w95c{ENlGL>HM$RqZK;QYT+f`mPM8J|%N0u?<~5z6oEM zfhcl%9D19V#_YBz3UsMLN@K}HV{uR#K~H&==KwAZ;;fZch+A8(J^K}|TY6R;4duL2 zKKsYJ18YjOb<9m`N&~(uEBq1R9iEd!65!X$rUSf20}ARHKOERrqg~W{N;N#T9k zlswx+%CvS7$J4QHZLrp;)qzdc=Ln}Y#tUc2wFJU_Zwz7ungExq<^Lcv9-u%oWIctz z;i(M$qZB4UNV9Hfu)Z{Z9(Z9d$Ub2Fqz;x47~t(Mw#=K+z$@I&jYoHjd>Wqa`{*w) zk?=OtXx{LPAC3W+Ku4HNC!vP%a@oH9v!v9ufO-zf<5uzFd#2UD=KVDCWi!k^Q7rT) za9k6o&6WqFA8|PGtMH(ra4%T|(ZZBIF>@+C#LYoj&23FP5m#@moM*9CW-?6+$bmbj zr_+}OJaAhG8cITX$`vzYG#C;et6ggS;WRQCwD;}0q1dmD6y*7tn`$dWS&T4Vx1Wz`Z_Q&t#+WbMl@3-IDZ0P!BFu+>N5wmJ0%I-O~;{>{nCQ*fPie_zO**{Q1hRsoOkklLhd zLA@>+*fa^dXOnl6Qg+V{7XS1b{xq_D_5fL|o*$WWs4X%ATy|lza4kF03bzTEAKEZT zLhq&^te6i>86U#cTzFrwHKy*(|?T}l@(x`;$d*xU9^Ibhm!qu`~rxWRy0(Ex3 zd6~=KFC6CY9wSOR{iV^$wfEi1@hiU)jb3SrhEJ~1&R*q%fn&>qBv=4$9;GMmBw>Di zH^*_yR!{o~w%GhMQ7>c8qsj3RSdMt${sZ1Y;1Y8jUE)2OD1{g~G`AeP_uR{_6SAc4 ztaOh=mcLEO&XrI z(<3p#ptvt7n(za@I5t+g4vn;6`<@bMFY+|lt;_?rW zD>%b6W4oHt{svesQH>^RkigJaUdeYo>_(W5%VDn<<<@&ZmX+4U=hl;?Gvk@9q2!Q; zy}KZQh9Tx^A}D4DXItm)oci<2luK5#E^~ekmKfJFoKc)Omsz`>l1Z1eBi-%!OxhEo z?ax1H(}e*)5y;di-U5_m0VMa$Qwz)HNu6l}UI+G6FEK&eM|87VoQ>=GATDaRw~gAe zwA%*ytx}ylw_{|<7W0;%^pgOt(#);CzYCwmBDAv*08CHcOJCVfPk+71v8KjIy!6n( zYkX!}8bxYyBWBpZ4_doR1Y$b5kz~&Mo$a53dYO$)6!)+GU>+wT3pIC$whyUJPp^(h z&V|w+q%$Y{-SR&1SlIVlQbB9^4HD_T@c=K&(J=NG5vwMfJF!KWg^oHIiCwYU9;tqF z)Qzmat?B8l5lrH+r??*aT~8RTjUjHTD!^dDZ!z#QtGu)G0ZdEYmx8C5=4>J_PnSh%RJr_(OFs2agFW6c3|W}T=-{{s<;pSH7Xzr2%4Lt ziqIhSC(L$=o*G`@stLp~t=p;xDnB=hU21uE!2pdmXnq488&X+Q!@`~gE=D+Rn&?!D zM$*X>Fa;wo+2G^(XgT<%hYkU{t1DtL5*AxK{_%}>LZ0Plf*8s{TBPKd37KSJo5bNf2374$PgP&4!oR!Ms7lsa z82)^z=mdF?`Ckt|r?Wol6~{yn>+yDuV<_Ibq0{>OJU^gRd(PVSN?Xgyt_Xfp=fr;B zHp`|P+{2zic$AiA%+#N>Bdz0DT9aeUykSni!c3 ztC$a5YaCPvxbcU+;g@B}8t+$yfTHYlF-1>h>&F`p5qxXa%y#vRe;zRWntJ6Xx@+(Z zwS5E$u_hNp=3%teAJ1b9<&%?x|7e7m$!$jsp*a-^xfhGb&mZYccc9ekI@MM7z2O!u z-S{1WcU@h@8RHh8ZVw$-cEuw-_-H06ys(rtamQDQ7Z@GYf+F;vtQKmP?oIKm77~s@&_28%TN2gF(ni%3Y-Dc9fbaF#I z1~jkt18}rvd|@_OTkF{gh}HZivf9d`@RAO(RH~J*yq*t0hhb)5X+veP1W8 zAT8f#(n?t-sn4C%2U(%c?mG;gW1-s#hqD;s#%MP3@`gyHjS^zIY1AuOi3@5 zz+iqZWMM@b!5tgB|9&o;iYWg^ML0oE-27CFRZon$077ggr${DWnFCJxBjr$|Q8_E4 zq6E~F;XkdX3dmjlMfhn6lbz6(uNlyz@cOy)HAP>7@U|!sHK)AN0)k}#h30YCzsDaA zKt>dKLN_`isLskb8?g5j&1k-}3?-4=O?gE=; z4j1)r<&>b4R$O<-PqJmV{tkLjhQ&FypcpK9i6Yt9EBM}$tgN(td-?j$tz%ixw+9-+ zETG4x3LPF1NLY7s#`sEX^nlS<)s5#;Frq%uUDG^tmFgvjuFDw`f>kueLiJEtM*X-8 zpkWoFXVo1==68e9?uI2?J{k@dR)M$pWbG8%;peyD@fok?*%!$WZ(AqVsFfL?px%_I zcuWNAC#91PvyeV>)(vK%!LNsuGL65p^_c-tXKbt@{yC@Z!Xd%oP;?ZHmla;o0JF5+ zznwM%fcpa3O}_-K-)xkPVEI4wqNdW-&4{%A0p#xb%Q?Mce}JuI>Bi3bP)>KE-M|j+ zKZZFeDqkLxQ6w#GB+sgJz*`f)K82R|-@mC-0L`NBfXh8v#sZusNUOk(wnMIas_>Yf zH&zwlGlGB{0U<7i87>jm5x)0n7hktqidQZogIV8G{q3rB$tU(FkJ0x5M?@<^Lhff`1R^b+{!^1C{s$jd%Lf>l zBupodhlV6Kc}XTw`-l$dds}OXqSD12gk3`6@+@oa{V^YKN#5YrO0OS~OF(bMYVUNA zBD7t1@B0yJ@6j?sO;=jS3ddU(6T+C%w50?* zr(jxhguSOb^kbbZxA}P;RvVLLl+dTi=O9dd-b*r%)K z018=Y25fu&N!xFg_c8YR5N^`_a2^G8VORgFuU;S(84t@AaQprpsmFAd5@&NttNV9ti z9rA07r%}N3XU00N5sK0MXH2w+KBBZ2x#a5tX9v%sc}sS& zvWPSj_V(e8$Ch7#a94ERlc?SR?>8|~nFJ9{mLoFU?VoIYR+`1jQz%I~^&R~tR9>#? zNqdSo33z_D$Mfu(Tw`p!?A9IDr7&T^lw1V#A%u?joeo0e{QUlFs+!KX6b^Ut??E1#|(_Q?jt~ z1JTd4VG$_qam}j&Z8M8)LZcdAiZ%K93stD8&RNCx0TyJNlpTBG4PzXprL7CF94^R1 z8%n(d(8=>CzfsLx`mVE3t7oUQMPyL2|CD&{^pt;1c&H zVHy|mOHbDJ?|qEa$w&MVDTJkJlVH3!WAg|$RXPx}4&c-YN%zF%_;rr5N1w()9kQmyYSia%JS z76YI-o#p%}9ryPGqiRafTswVEVo2NdqQuvHFlFFyqYR)v$ub_qZbNg?QJ7(ygpI^z z6Wn{h56P0o=8(p+1Wds*xIen$$>06;J$y)SGUQ{RoJ)oGoZ$xlg|MSf4$MLq=oQi1m~ANd$N`WjjTHI>jgl%D}kgl^nf^oEbz zflJ7C{ltMP^}`QoMt=Ke1X~y29RkyfFGG=E?k+|^UzC@>Ih!o>vX}N;nFI$N%?tVZ zdL9xw!rC|9QS_Bt0*`ovc{o;Wup{?=wjKA7y5;n`5>|b%7w}EJ+18_)WQ&DbXU@%0 zfx3GM=!LgOK zLSd^;yB-$%e+}{&V2lrE)g`ZUhmoA9&*j<9I=6%Ry6cPM6ybmy?~aj%1F?U4LHXfj zh0|`3>}DvF>clz$!hl^fe7pD@gG#>@97gW$mZ?mE(58>J^p4r#dI&q*Lo3E!+Xvd> zc2$Os*6-1uFVexZGf_CBDZ4LDwfA_&n#4tlapP=wJ&d6P={`{mD?1;*Hc#u^w10o?~| z^~h{>ZzoHJAqK7-T6Gcyo3wk;EO%&TmcDaGkuG=MXweQlu4}(ZF#(^4>n^07=xG<5 z4qH~7xbGw&qmD;^C>{c?TD$~b(DhMXk9;A@fh)sVwBb#&t$rAS@nH6Q9lo+jt9;`K zWz(gtk;Z1a+<$=V5|&X9&i6bxzYo*hZr{?+ahIK1CW8=+-{V6fbmCef!#;TU(BsOr zjcHw{@|cU4j2KP*Wt!xkRl0zNh+l-Q&A5(cY|g4cJVd^D{fi%bK6=(A#8p$PnqN22 zRG4>bOXL2}SqUz}7YY0qjlUTh5%7A`GWmPm$nn;HGpw0Q ztp7feF?05-JtJ6m6~gC>tw>0yJ%jQYU#CEaygDnt+gdA_#Y@oOl376B~Mz*(1bq5CNCIsG&t{{TEGXyV)Fd7lBewT6X{edy2Ms# z-*q}ZaQ{&W#~;?*&QJd<8xffs8M*C=@Yzb;I}M!~5twE=9et%kD=3G(-gH&du%Cel zd$R+5wftP<7Yiq`u0Tk5YNKwRnwmKV+K?E9<}#Ht*@^#b?a~Lt$EOU4+ZlUN6Zs9@ zdr{|lkDq_qeA}7s%2-(D+qzTcS!&o6Jbw(``;RPsXB|k4&P(t+p6NV(pDi#4UHaDo z(e?bY?%#C|xP?t%P(_s^Ro9y;n`smH`Qmqzd~ZZc-PC_MKypMB#;<0@vn0&~pv* zccR{p-3w8>Bi`Y)lI4BHg?~s4q;!bzXWG(p%FlKw=R|b&>AGE@6eMC?k9^@h)FegW z;PJjqsLJMZdG>tMyOFHvG?Z?{F^y4|60O^8052bl#M2vjSuCOt5k!HL=W*QLHqvT~8A-<_UuO03e2v0UAXP5i zw<`*O8ae)ylemCW<25I-XjvD&<`=e;^jQ6FUL<^~h^z(Rz}f-4Ou# zii~W02JMkycu4VX>91V^CCUY;?BPhTN1 zFY$?C>&)db3KNMFpuSJE_kCpq8;Y+=Q-w=sXIBfqeA3fV^}XWs%69j=F&!s92lF89 zt;TnSN1Ci-X3JpmA7$SPKnrNlpKe_$*qYD+Yl9hnWk)CMT)zeZQ=7L;I}7;8RSFN~ z65xUhD;;DD?!J5MJ;PJp?g@$w#(@U*grgc-k+r&0BJ~ z$1>8>k(v;)T`!xo;z$b|u}~L1RSY7|is83_u26!b4%fL7& zd{FgbSwhW?qP*9WqHukjl25O17i-0ktW^Utw2&aOiTZp0;b9V$*Ye5%^Z=zVEdr2mDRZ#J*J! zagxh?4NGhf^CCb}XHuoBqj-3$FVhgJ)&3(}1L`?jqw=;fgmyLrDaYfvhrNs`N{Rjv zDoV83xX*t)7Jb+B^_^NRK70xCu^aQP)a}}>48%ogfrVYruYwWSm*c~$Iw5=R?(k*S zmn#gF0#zM1jh-43piMiPqC zOPkX_T$S-`_3tKnH~eR$p~hjC9C+)VJ!hA~Z^Y{2{dQ^@RJM11dH001tH6AqXRX-h z<81&leJ`^s1iZ`b>7-jZN9QCWI?ad&P30|o*F$c&>f#+q3pb31$r-7$nLrn4;09>Ot_qd?-A_?o-Q%zW)wERcLO9*S#o&nT};3q47h%eWP}L3xT;?#NB2PWQwX>j5_a_@39<+b59=rNrMn=r#nfN>*`rsw!*TyC|L)ar zRw!T_JE$iRjjYz!1bdy(2Kkm(83VL9v;a(LZI1=%-E$=t;lB~xp~LX)PHo>Kts?Pz z3FPzuJ3dE*HhT)2M^-)IrN%fO^|>#7^hM{e;5;b0)5YiC!I=N;xbM!oFy%Lk(6{qs z0WpaTNuLH!y$6R*{FB$#I`lEIOFP9=+@odJ+Yx1^ym*?<3_a@>-7z@cY#X(Tz(#vi z4i>f!9Jy0as_S~(^-*a5RvZ)a=JO@5N5F}k@g4IPZ!>bg_HwBcybweb-xqwCK3)Uy zI?;H|Ki|L_h<&fnZJkrQQUtC3t;4I*zu=URZ(P~v${Wd}Ft5CWgMY9)LF*iQR}o8? z)eiKk(9P8j-*uNRfgWvN&$rhB$*=i(vlZ1_w~APO4XR@2TgzWsux>!9&rhXN$vRj$ zKJA|l^dGfLkb%1SbG#)T-P@|qA}563jY#)>#gtgR0c_8$v3xgWKYdm7R79_V4WgN0|6LENVhN zZ!GmT?N~7KLrm(&Tf-k>?mymcDtKOCOjZHqcU?hfGMzb{(o@6DWkhmPJUP@ucFVdo z_Ni`W;u>f!r$(+mL8O%oGMa%IR|3=5VG6NUm3sRj3e+W{e(qMbY8TLCan$A??nAJ9 z@K^tRAIUOke6gWL18L2V<97djZeWH^i!D8aEy8>jk*PH&`9IDwz7l~7H6&=K#$a}= znlCRv-3n?5%6~t5c5Kz3`%jX>3_o1`l#{A8*F@T+#TW;*Eibj@W-t8s9!J|<9(sjB z$%rCuC4JE4#pm!Sio9_MB^a{0+8uNKNb}n{MW;URE8mJ3eEmVM#7XzGLN=Xj%&QN`gmS8u=X!7)KMU0T%A059l_GkEd^sx1PW)LE zy21}zcvyF$4AY|`9?pVmrCr4&_AnX8$9xRSLv?SBlBDisZ+dCma&l7+Bx?uR1Wj<8Lcd7<(XJ@0+JrT zPbn;j*o%2nY4f?eS@7q()5NZ|`|TI>TsGfSgntpUX|1?mD-32S!H^U^tUN_ZbYHR> zYWC$mi_>jdK_L`W^zU&5OT42wYi$h^Qr7nMp_*yz+$cA86lbmVDkiKbX8Ro?s!XMc zikDAZ@fvC9>N+>f@6!H8qt|5tS+I5fwB2JP>0~R2pB}-cgQ!2){fi37y%15aq}3fs zF7gFv84@vm>%%e9&^=P&iw^u@yW&xdIe+o|-)NqPOzlGkaSyw*it5u|j#zT%EWl5v z0jtMT1!9x5V2?FZMPqBqGWXyTRD72d=Xu)l80gG|PlT4G>tzrAu0G|8IEhf**@wK9 z-dh!z{r_BU=s^n`)0*TSS!3n;O@K0H4T^Mhf7z{OsA_9OWqU=BJd%RT5JRTNtQ5n< zzBrZS*_*s)Cl_I@;3eX$V!PPfw1pJ4mcEHIU$r5&9j2h`vyP>&ZXHwRm@8Ri{^PS# z3H4IBzVL-QTK9Ge&BirDi+FOGcd~TP_#Zo_$nEiH^h#oJDi~fSu756oE`tP7$1p=@ zs6@xGW^u6XW}9MiEiz*C$@P_^PJelThXmlXh;_!1(K5yylS9HhpKjwyA*uw8YE};d z$Up0;N^X8jVlW|$L$9Zuo|tx72gz~9T21R#;WL3Z`%pyNZS1hNpN5ZcM^cxdshdsW zkot?27vxA{u|**C!o{%fSMq5=(Mho&xqU=zY=jkKWQw-&yPlqf<6hpU!4sm#Tfsoj zyVuw+heC$0Ht%Myng2oHndqy3UAfmjCndB6E5 zeK~U&`?Sr;QC15tYXJYH!8E97({XA@cEqp_(O_%_JQ8>~m$fq9Iw$N1ChxdVz`8~m zB;VXs72^!ZX`jnifSu!MI6`9OQp4~HvrTf7v0t4ioH4CZ)Z>qVn050j_d{@~iU_T~ zfP?N+S=u1&?Wf5@kpcK7Yc711!7k@Fv@>pA#989WeL}~@YPWL#h?jKfFN0nB%AX0J z^<&f%&k2pEzOvxu!c128UZtDB&;b#(SgxYOzFrDIfUw`h1E}s_dhLOT)l<5%y}e5+Cd!rifWN%Z68K~ zcB8LFu7NKec~s?*J<9rhRPb5^krD~;$G+*qv0fy3^(n_Icn z*XN<|EeaK`JpKxb^luzhzg7^TsC;s8rxGoU@UkmW##xeI(&m|4I+Fthf8?v0OI{NE zZx(E61-%;vdbhLBx*Dl%9h!?ME$h2p%6?P*d zkO>+s!SBfhH!E3n?IgAlWA6UD=JIO%>rg%IB)MkH5%{;%-Zr)pxIoOPm8+R- zuFG=i>E#yz`+xRSu4m>-?8{6SU;bfAVo3D4F(?uLAMS2D55?bY5np>NX5Ms5dz|h3VmHS&+{FZSvv2Sr~AS{p_4N~6fvto2APVw%+ZzP zR!kvkCh=Aj+Hnbho8t|x7nfT2X;E_%hgxJ9vE@-+>cI7+C_9B0DMl5Kw$4Yq4=pMwr}%@fXDJiCzZy}F_Qb4FkrzIchpsVAbwn99y)Jr z>*nB-WAKx2+Wx*k9GW^%W}fs3U0A^QB}&;hX>*hF!l4q}Sn0x{iFhmT=}VT}y~m zp!RIf@NaH?5Zd?9(-_am+&&NHC`?r?s)J3{rvJ#nA(1sn;LT^ISI_5;K7U2v^10Zr z06JeTG~Ap264DQjeJ-MzARbBE-PQQdZvCPB1h!w!!m@!27BzPyhM<|34notGua@(Gl@ ztQFVML07n6DSg3I-1lrP6(_wH8r)7#i=oAyVT{2Q*7`KU0NhoO9JWjhX6jRRRntO_ zapvhv^rh!`Y3URy&SP}o*XkbkBkUBqZ6tm=hJ`2XVmD3G?ECd#f0k_B^vNXsU8YH+VYJZs)vvfIg96ZGN!?r3)6iCE1-%XJFA%7D>3Qj z&?W3QzMH?uwpf)(ARD1_OQ{f6sTe}$)bP43_Sz9Gln&3LQEYl9c=4a*N$pt;X8C<~{nqMTIKJ=O-E%8`NARn-vn9@3{CyyydN3H)d z+ke1#bB5-|syBvR{I~S(e+%^wsb{oHSOmy+JiQpD{G9rsAR*H{J!JZ|WE7>`kK{?@ z;NO${P5nEByKPT?P3ZS|42G7p`rqh7L<487(POQlbOFS~_5I~cte5Y)s{!v1g>Mp+ z^*n`<{zQ5xuvJ>AAFx^7Ym>TzF|pQ@B8kTnpAtt{=7cD2jUH|d-!_n3sCEo-HtTh1 zCgGr!Uz5Tt?%difl8JzU0#h}!G5qf2 zwHu#VyPX^!!Uy2nqm`5w`)uRj>f$p)&+5sBoDSH-1JC8sI?xnf_CV81(depy9!~s3 zgyR67&eBNobKq1_Dw|?hbu4m{NB{N6OD&cRotCqxE zWn1F8+Feb+qX!+$NYYl{ym}=gVrdv})2zkiT`fBG13UiXE&^2e^8eHenw znWq_A9r#!U#9dWL26T3XGx?4W+zs^0ULq>~yGzAtZo$ev>j(=UUtwzN=I~U_vH@{7 zPw(%o{Dzve6%wZtC0G4|-O8g>;Vy!AOl5GXE6ZP$|rkBWb`1$ihDWoNO5#exlsL6Z8{@~J&>vfclBJaY?g z_FOAyQWI%RD3y~p`@dLdGhYfg$2# zrvZjVtLDHLC*nPN76Tt2J-MF@kyv^dr0UAqHKlW636vb=X!cVPo>--j`^}RAO0?Mr z!WHkJf=v|-W}IEuIw$U|L&&91xMB*7_kE+@{ZVhJSV#yWT+&tX7I4{Uw-!CtnC!dL zGbf++e<61ZyAlop^Uh-Kq840(NJGL!Pok@W=-5joQgrkxOa;O&Y>uMe!|}0Folg8E z4lsC(m3o1HQWn_idl$`U*pv$KhuxcST)QVm)OE4=#9mG*d;>gbKo0W#V)-mPaD|rSz)HADRF__%0{29m6p`qo15-#fk zcOdl_X{b!~VD)M!>g8Cc=5CtVsBGsL;Fc6bx+U3}9qn
  • IKhY4)&;nB`Qv|A6{K zhQdW7qkn^TcxQMsPugYv9PE;wvKHJ`Ic{Tw>JvYH5UuKmzMFIe%5eHG~))Z z+wu^v+|P~~98-zh%9@?++;n19QZ8iQ({9%edOK!hc@il1x}^%#mGZYn zi2a{jICK93)Ei6^D0Kgk7_mF&uJN%SK$$}#P-}zCJus`-Kt=tNbAXtQr zmgBpjXNC?QgG$g2Afkh}nQdxIh35*Xh#W8nUik?xxQ_ydFO_uROkG}r2GsH@SLy)V zgMH`U+vZ&_ml)59OK_xmSkXIs@F0_@o7blLr?c!aYr4eUEpg9*>s52<$)EpyPi`8n|<5hDTlr z-@XkSI#E-N@9~9ZTyGEa_tom0Tt$+J!wJx!`uyB=_>VE{AFf@VbC=3@Jf105iAz+` zcw`_p;;-F{PUyS*ABp?=-O2qM89Mm-|Hf`G)EqBSc^FZ|6H6eJ|ch4cMf<$T(WK zeb=wvz^pS(MCC{FSn)Pr;7a^fyM91XE%3hUf}#XGkAGgjm_)yrale@BaPfx~dMawJ zBSh-sJKPYOd!zh1{?VMH_D^!AS1>XVf2h>FQE#xbN}M7lm2y&63@W#vd0s|C0e(jj zrN+~QO-VN6=TZwU<8)4H=XEwk+VcGJ$nYI3E(U@{kFt>5?$uLj5aW}U@ijdE%rP=@ zvS#P}$9^}$r8btv0umP&S1$7TI`hCQlrE)W2Q2q&JG4JE&R@Rp@tW&$#Bv15fAB)P z7)Uh($`Cu+?*jQF95MF@H!Gm(MNY|E(UTFShZ5X6oE>MCMv>PR$mIuOv$XshR>)TT z|8vej1_;78@j8`+#JXy0f4PK!1BtY+)~^hAmyLdgH&A!Of?4}l?OMyJh{i6IKa%QP z)Uz1>%rc0KP=V4$V#`Zn*|>;^#$(=|<9<%&vVZ-EDeFu_BVh+I2F!GUd+@N7Jr~6L8{K(^o+X^HHdJ2X(aWry9CRQ z!FFbG(KgUC*{ZG?^&=c*U8@Dh%qpM+oiEw%B^)OXPWPU4x$KLBXP$D{7H~|y_XfDq z=}3`zRD(d)rE6ksD!*ZvNb1f%WZf|MT#@yeYazzEYIQzMbBx;L`h)$2Wi4e$E})m{ zr7x!gkaWAMTF1To8YkHN)#-=3!X%ZUHDtu$o23o1ZnFQ*a-P*zb^DW7%!48~F^?JR zoPuRTjsFSNuH~KGvp75Aw;oE0TBi#o_xv%$MVDW&KsambKzlBDY4h`UZ(Ki}1iONS zHrMbcZ`_w_HF!tw)w#hFd@6OSKx%Sf4)CEt1+&^W=aO!Gp|Yg3b^fUpq$ZhWiYUbz z1O}OBhy-V@2XNcSQhamb=FgBwobiN}7x5scNM9BbsQwx z5sC~8BGIgzQl5i8C$}?qHCd;;Ld6=if|~lc?^PdjORZbKShqUZw4T-5a&YQ;sPqVg z&OGKEe2)GlF^K5}0Xh`U4(MB{Ca+Q=abV?o>kuz zh>bLOZ|vs2KRzO~-M}SBP`HZ{D~$siankpV-N10#FFWD5Z5-i^>$%cm{QuF!??|4V z(M6`m(IlL5x$~2A zh5=gdo}N+cuVQ;YoE4T?%WuDyYa0ksEnf!bY;#0t;wsY}k8h$){~Hww3!{abzdS=~ zH#P+wohYE*2~d#eoN4z^*9^kXAYk@`XL-{T*9^PVweW$++? zD;okaO=gmWPeB7~D%Khns&31QOY@8ivCq9szxN4eOZ9@K{(+dzrNR5(j|6*<$_~J^ zrL*cyhm_69_kRFFdczdyw?^Ie(f`IaeS^TpwsuquP7783Q2J`yF?WCYHG0|c=JIO? z>XlCRH$%evh=|Jarskab5ffVk3}E{e&gfA~HQ~gXPHevw`d%K(npn+kSlM~M!V@{E zt&;ZFp&@h`Mw)qD;^h!+d%O0MrNxa|`s}u6(l4zA8&%;A!MiTkIWq&BXT%9>@-=$0 z39+Q-%6=zK()!fRTaa++b-NbO2^?3BXdl{i#LdaAMt=<1TNS*0=igDlUCmpkFHuTX zcCG0ZD=J?n%YBFMekC|?)aML&$Js!A?SS9E5?jpIExKbLRQ|!FpYGj5VtAGV$8Kee z+5LIF&^r9qW2_*o)!~|JNKYkYEzz_^8iDi=ISBF$PB^f2q(-v_2odI;ozBfAXY=du zbf|^UEFG`&2d~iwm09Hrt)Oi%iK^-(=wc@C;}|&c2?)fWlseSz@Rvjd1_Oh3r(CUJ z1g~-U(q8lOlc8btWHbos3FZj(ENt|iUYi_oX7hc$#xm>sGbHgml^*f%az3PuYoFmj zY!83D`h%61Bqk_iYl}+~KFPpVDET?%xNV@GpiefApa)L1<3J;N>Jp_-=WhTGb|0Z5)+Fre#(jCWhLT|Y zAgj%UyM7`r)z6j#qlO^0jo!Q(MbajzdiGiJ6k%K8?QOO^4p@{k&aZPLK(IiMB}HX0 zQ3iuSrS0EAAHn6Jo9CzE9fK-kLrJ7tTBTd!GA*ab9@#swR`zT@tP={kJ=-cn1j-vn zp2Wkuh^*4xS@h*}<|s>j$^}s>=PKAKYxJ-fguUjm?V=K+)oI?6C z-`AI!vUDn^j3SixPo&DT)QH8ce2>ugQx{J93WZ233QzhFQlFW+$k+o{K@~>wq_y>l z#wXJ;XSPoAnyL)Kix-20Vy)^mjEcxz9=sGd)dt3clXqS6S`D{h6ViMpnQ=s$D%XcE zCe&Oob^$-XTq6I+;_`bGNuoI82bel$_GU1Jp%fFq z4^E>$)LY_Dzj1$AD=*8ebDwBc-!1OlX#1^h8R9eFnNhvWrE+DXkl2#HEii65AaSmN z`DqjH((O{K7JREf0>>zkEdr6UaV_iX=jMmRFB>OJ7;!jekF)DAD(W*X=-&I}x>@fEa80?up9qcz^mZhKD6 z-}%_ae6dV5-xTv>!^BZI=*Fi%7Bk+ub7h`eMW!Kd%wd^niaU9K?xiUOgcP$UP$?!; zEhO51avj*Hi9Naa6tyFY1XG%pxI1f%za@W6Fl6M=c;V;|q`2u*DULh*4sCS|D)BfZ zyn2W4>K%_)44yw0KZKRfg(Ss&5#or_`dbd?=