1
|
|
|
# -*- coding: utf-8 -*- |
2
|
|
|
|
3
|
|
|
""" |
4
|
|
|
General description |
5
|
|
|
------------------- |
6
|
|
|
This script shows how to add an individual constraint to the oemof solph |
7
|
|
|
OperationalModel. |
8
|
|
|
The constraint we add forces a flow to be greater or equal a certain share |
9
|
|
|
of all inflows of its target bus. Moreover we will set an emission constraint. |
10
|
|
|
|
11
|
|
|
Code |
12
|
|
|
---- |
13
|
|
|
Download source code: :download:`add_constraints.py </../examples/flexible_modelling/add_constraints.py>` |
14
|
|
|
|
15
|
|
|
.. dropdown:: Click to display code |
16
|
|
|
|
17
|
|
|
.. literalinclude:: /../examples/flexible_modelling/add_constraints.py |
18
|
|
|
:language: python |
19
|
|
|
:lines: 41-158 |
20
|
|
|
|
21
|
|
|
Installation requirements |
22
|
|
|
------------------------- |
23
|
|
|
This example requires oemof.solph (v0.5.x), install by: |
24
|
|
|
|
25
|
|
|
.. code:: bash |
26
|
|
|
|
27
|
|
|
pip install oemof.solph[examples] |
28
|
|
|
|
29
|
|
|
To draw the graph pygraphviz is required, installed by: |
30
|
|
|
|
31
|
|
|
.. code:: bash |
32
|
|
|
|
33
|
|
|
pip install pygraphviz |
34
|
|
|
|
35
|
|
|
License |
36
|
|
|
------- |
37
|
|
|
Simon Hilpert - 31.10.2016 - [email protected] |
38
|
|
|
|
39
|
|
|
`MIT license <https://github.com/oemof/oemof-solph/blob/dev/LICENSE>`_ |
40
|
|
|
""" |
41
|
|
|
import logging |
42
|
|
|
|
43
|
|
|
import pandas as pd |
44
|
|
|
import pyomo.environ as po |
45
|
|
|
|
46
|
|
|
from oemof.solph import Bus |
47
|
|
|
from oemof.solph import EnergySystem |
48
|
|
|
from oemof.solph import Flow |
49
|
|
|
from oemof.solph import Model |
50
|
|
|
from oemof.solph import components as cmp |
51
|
|
|
|
52
|
|
|
|
53
|
|
|
def main(solver="cbc", nologg=False, optimize=True): |
54
|
|
|
if not nologg: |
55
|
|
|
logging.basicConfig(level=logging.INFO) |
56
|
|
|
# ##### creating an oemof solph optimization model, nothing special here ## |
57
|
|
|
# create an energy system object for the oemof solph nodes |
58
|
|
|
es = EnergySystem( |
59
|
|
|
timeindex=pd.date_range("1/1/2017", periods=5, freq="h"), |
60
|
|
|
infer_last_interval=False, |
61
|
|
|
) |
62
|
|
|
# add some nodes |
63
|
|
|
|
64
|
|
|
boil = Bus(label="oil", balanced=False) |
65
|
|
|
blig = Bus(label="lignite", balanced=False) |
66
|
|
|
b_el = Bus(label="b_el") |
67
|
|
|
|
68
|
|
|
es.add(boil, blig, b_el) |
69
|
|
|
|
70
|
|
|
sink = cmp.Sink( |
71
|
|
|
label="Sink", |
72
|
|
|
inputs={b_el: Flow(nominal_capacity=40, fix=[0.5, 0.4, 0.3, 1])}, |
73
|
|
|
) |
74
|
|
|
pp_oil = cmp.Converter( |
75
|
|
|
label="pp_oil", |
76
|
|
|
inputs={boil: Flow()}, |
77
|
|
|
outputs={b_el: Flow(nominal_capacity=50, variable_costs=25)}, |
78
|
|
|
conversion_factors={b_el: 0.39}, |
79
|
|
|
) |
80
|
|
|
pp_lig = cmp.Converter( |
81
|
|
|
label="pp_lig", |
82
|
|
|
inputs={blig: Flow()}, |
83
|
|
|
outputs={b_el: Flow(nominal_capacity=50, variable_costs=10)}, |
84
|
|
|
conversion_factors={b_el: 0.41}, |
85
|
|
|
) |
86
|
|
|
|
87
|
|
|
es.add(sink, pp_oil, pp_lig) |
88
|
|
|
|
89
|
|
|
if optimize is False: |
90
|
|
|
return es |
91
|
|
|
|
92
|
|
|
# create the model |
93
|
|
|
om = Model(energysystem=es) |
94
|
|
|
|
95
|
|
|
# add specific emission values to flow objects if source is a commodity bus |
96
|
|
|
for s, t in om.flows.keys(): |
97
|
|
|
if s is boil: |
98
|
|
|
om.flows[s, t].emission_factor = 0.27 # t/MWh |
99
|
|
|
if s is blig: |
100
|
|
|
om.flows[s, t].emission_factor = 0.39 # t/MWh |
101
|
|
|
emission_limit = 60e3 |
102
|
|
|
|
103
|
|
|
# add the outflow share |
104
|
|
|
om.flows[(boil, pp_oil)].outflow_share = [1, 0.5, 0, 0.3] |
105
|
|
|
|
106
|
|
|
# Now we are going to add a 'sub-model' and add a user specific constraint |
107
|
|
|
# first we add a pyomo Block() instance that we can use to add our |
108
|
|
|
# constraints. Then, we add this Block to our previous defined |
109
|
|
|
# Model instance and add the constraints. |
110
|
|
|
myblock = po.Block() |
111
|
|
|
|
112
|
|
|
# create a pyomo set with the flows (i.e. list of tuples), |
113
|
|
|
# there will of course be only one flow inside this set, the one we used to |
114
|
|
|
# add outflow_share |
115
|
|
|
myblock.MYFLOWS = po.Set( |
116
|
|
|
initialize=[ |
117
|
|
|
k for (k, v) in om.flows.items() if hasattr(v, "outflow_share") |
118
|
|
|
] |
119
|
|
|
) |
120
|
|
|
|
121
|
|
|
# pyomo does not need a po.Set, we can use a simple list as well |
122
|
|
|
myblock.COMMODITYFLOWS = [ |
123
|
|
|
k for (k, v) in om.flows.items() if hasattr(v, "emission_factor") |
124
|
|
|
] |
125
|
|
|
|
126
|
|
|
# add the sub-model to the oemof Model instance |
127
|
|
|
om.add_component("MyBlock", myblock) |
128
|
|
|
|
129
|
|
|
def _inflow_share_rule(m, s, e, t): |
130
|
|
|
"""pyomo rule definition: Here we can use all objects from the block or |
131
|
|
|
the om object, in this case we don't need anything from the block |
132
|
|
|
except the newly defined set MYFLOWS. |
133
|
|
|
""" |
134
|
|
|
expr = om.flow[s, e, t] >= om.flows[s, e].outflow_share[t] * sum( |
|
|
|
|
135
|
|
|
om.flow[i, o, t] for (i, o) in om.FLOWS if o == e |
136
|
|
|
) |
137
|
|
|
return expr |
138
|
|
|
|
139
|
|
|
myblock.inflow_share = po.Constraint( |
140
|
|
|
myblock.MYFLOWS, om.TIMESTEPS, rule=_inflow_share_rule |
141
|
|
|
) |
142
|
|
|
# add emission constraint |
143
|
|
|
myblock.emission_constr = po.Constraint( |
144
|
|
|
expr=( |
145
|
|
|
sum( |
146
|
|
|
om.flow[i, o, t] |
147
|
|
|
for (i, o) in myblock.COMMODITYFLOWS |
148
|
|
|
for t in om.TIMESTEPS |
149
|
|
|
) |
150
|
|
|
<= emission_limit |
151
|
|
|
) |
152
|
|
|
) |
153
|
|
|
|
154
|
|
|
# solve and write results to dictionary |
155
|
|
|
# you may print the model with om.pprint() |
156
|
|
|
om.solve(solver=solver) |
157
|
|
|
logging.info("Successfully finished.") |
158
|
|
|
|
159
|
|
|
|
160
|
|
|
if __name__ == "__main__": |
161
|
|
|
main() |
162
|
|
|
|