Passed
Pull Request — dev (#850)
by Uwe
01:35
created

simple_dispatch   A

Complexity

Total Complexity 0

Size/Duplication

Total Lines 261
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 0
eloc 141
dl 0
loc 261
rs 10
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2
3
"""
4
General description
5
-------------------
6
This example shows how to create an energysystem with oemof objects and
7
solve it with the solph module. Results are plotted with solph.
8
9
Dispatch modelling is a typical thing to do with solph. However cost does not
10
have to be monetary but can be emissions etc. In this example a least cost
11
dispatch of different generators that meet an inelastic demand is undertaken.
12
Some of the generators are renewable energies with marginal costs of zero.
13
Additionally, it shows how combined heat and power units may be easily modelled
14
as well.
15
16
Data
17
----
18
input_data.csv
19
20
Installation requirements
21
-------------------------
22
23
This example requires oemof.solph (v0.5.x), install by:
24
25
    pip install oemof.solph[examples]
26
27
28
License
29
-------
30
`MIT license <https://github.com/oemof/oemof-solph/blob/dev/LICENSE>`_
31
"""
32
33
import os
34
import warnings
35
36
import matplotlib.pyplot as plt
37
import pandas as pd
38
39
from oemof.solph import Bus
40
from oemof.solph import EnergySystem
41
from oemof.solph import Flow
42
from oemof.solph import Model
43
from oemof.solph import create_time_index
44
from oemof.solph import views
45
from oemof.solph.components import Sink
46
from oemof.solph.components import Source
47
from oemof.solph.components import Transformer
48
49
# Read data file
50
filename = os.path.join(os.getcwd(), "input_data.csv")
51
try:
52
    data = pd.read_csv(filename)
53
except FileNotFoundError:
54
    msg = "Data file not found: {0}. Only one value used!"
55
    warnings.warn(msg.format(filename), UserWarning)
56
    data = pd.DataFrame(
57
        {
58
            "pv": [0.3, 0.7],
59
            "wind": [0.6, 0.5],
60
            "demand_el": [500, 400],
61
            "demand_th": [400, 300],
62
        }
63
    )
64
65
solver = "cbc"
66
67
# Create an energy system and optimize the dispatch at least costs.
68
# ####################### initialize and provide data #####################
69
datetimeindex = create_time_index(2016, number=len(data))
70
energysystem = EnergySystem(timeindex=datetimeindex, infer_last_interval=False)
71
72
# ######################### create energysystem components ################
73
74
# resource buses
75
bcoal = Bus(label="coal", balanced=False)
76
bgas = Bus(label="gas", balanced=False)
77
boil = Bus(label="oil", balanced=False)
78
blig = Bus(label="lignite", balanced=False)
79
80
# electricity and heat
81
bel = Bus(label="bel")
82
bth = Bus(label="bth")
83
84
energysystem.add(bcoal, bgas, boil, blig, bel, bth)
85
86
# an excess and a shortage variable can help to avoid infeasible problems
87
energysystem.add(Sink(label="excess_el", inputs={bel: Flow()}))
88
# shortage_el = Source(label='shortage_el',
89
#                      outputs={bel: Flow(variable_costs=200)})
90
91
# sources
92
energysystem.add(
93
    Source(
94
        label="wind", outputs={bel: Flow(fix=data["wind"], nominal_value=66.3)}
95
    )
96
)
97
98
energysystem.add(
99
    Source(label="pv", outputs={bel: Flow(fix=data["pv"], nominal_value=65.3)})
100
)
101
102
# demands (electricity/heat)
103
energysystem.add(
104
    Sink(
105
        label="demand_el",
106
        inputs={bel: Flow(nominal_value=85, fix=data["demand_el"])},
107
    )
108
)
109
110
energysystem.add(
111
    Sink(
112
        label="demand_th",
113
        inputs={bth: Flow(nominal_value=40, fix=data["demand_th"])},
114
    )
115
)
116
117
# power plants
118
energysystem.add(
119
    Transformer(
120
        label="pp_coal",
121
        inputs={bcoal: Flow()},
122
        outputs={bel: Flow(nominal_value=20.2, variable_costs=25)},
123
        conversion_factors={bel: 0.39},
124
    )
125
)
126
127
energysystem.add(
128
    Transformer(
129
        label="pp_lig",
130
        inputs={blig: Flow()},
131
        outputs={bel: Flow(nominal_value=11.8, variable_costs=19)},
132
        conversion_factors={bel: 0.41},
133
    )
134
)
135
136
energysystem.add(
137
    Transformer(
138
        label="pp_gas",
139
        inputs={bgas: Flow()},
140
        outputs={bel: Flow(nominal_value=41, variable_costs=40)},
141
        conversion_factors={bel: 0.50},
142
    )
143
)
144
145
energysystem.add(
146
    Transformer(
147
        label="pp_oil",
148
        inputs={boil: Flow()},
149
        outputs={bel: Flow(nominal_value=5, variable_costs=50)},
150
        conversion_factors={bel: 0.28},
151
    )
152
)
153
154
# combined heat and power plant (chp)
155
energysystem.add(
156
    Transformer(
157
        label="pp_chp",
158
        inputs={bgas: Flow()},
159
        outputs={
160
            bel: Flow(nominal_value=30, variable_costs=42),
161
            bth: Flow(nominal_value=40),
162
        },
163
        conversion_factors={bel: 0.3, bth: 0.4},
164
    )
165
)
166
167
# heat pump with a coefficient of performance (COP) of 3
168
b_heat_source = Bus(label="b_heat_source")
169
energysystem.add(b_heat_source)
170
171
energysystem.add(Source(label="heat_source", outputs={b_heat_source: Flow()}))
172
173
cop = 3
174
energysystem.add(
175
    Transformer(
176
        label="heat_pump",
177
        inputs={bel: Flow(), b_heat_source: Flow()},
178
        outputs={bth: Flow(nominal_value=10)},
179
        conversion_factors={bel: 1 / 3, b_heat_source: (cop - 1) / cop},
180
    )
181
)
182
183
# ################################ optimization ###########################
184
185
# create optimization model based on energy_system
186
optimization_model = Model(energysystem=energysystem)
187
188
# solve problem
189
optimization_model.solve(
190
    solver=solver, solve_kwargs={"tee": True, "keepfiles": False}
191
)
192
193
# write back results from optimization object to energysystem
194
optimization_model.results()
195
196
# ################################ results ################################
197
198
# subset of results that includes all flows into and from electrical bus
199
# sequences are stored within a pandas.DataFrames and scalars e.g.
200
# investment values within a pandas.Series object.
201
# in this case the entry data['scalars'] does not exist since no investment
202
# variables are used
203
data = views.node(optimization_model.results(), "bel")
204
data["sequences"].info()
205
print("Optimization successful. Showing some results:")
206
207
# see: https://pandas.pydata.org/pandas-docs/stable/visualization.html
208
node_results_bel = views.node(optimization_model.results(), "bel")
209
node_results_flows = node_results_bel["sequences"]
210
211
fig, ax = plt.subplots(figsize=(10, 5))
212
node_results_flows.plot(ax=ax, kind="bar", stacked=True, linewidth=0, width=1)
213
ax.set_title("Sums for optimization period")
214
ax.legend(loc="upper right", bbox_to_anchor=(1, 1))
215
ax.set_xlabel("Energy (MWh)")
216
ax.set_ylabel("Flow")
217
plt.legend(loc="center left", prop={"size": 8}, bbox_to_anchor=(1, 0.5))
218
fig.subplots_adjust(right=0.8)
219
220
dates = node_results_flows.index
221
tick_distance = int(len(dates) / 7) - 1
222
ax.set_xticks(range(0, len(dates), tick_distance), minor=False)
223
if tick_distance > 0:
224
    ax.set_xticklabels(
225
        [
226
            item.strftime("%d-%m-%Y")
227
            for item in dates.tolist()[0::tick_distance]
228
        ],
229
        rotation=90,
230
        minor=False,
231
    )
232
233
plt.show()
234
235
node_results_bth = views.node(optimization_model.results(), "bth")
236
node_results_flows = node_results_bth["sequences"]
237
238
fig, ax = plt.subplots(figsize=(10, 5))
239
node_results_flows.plot(ax=ax, kind="bar", stacked=True, linewidth=0, width=1)
240
ax.set_title("Sums for optimization period")
241
ax.legend(loc="upper right", bbox_to_anchor=(1, 1))
242
ax.set_xlabel("Energy (MWh)")
243
ax.set_ylabel("Flow")
244
plt.legend(loc="center left", prop={"size": 8}, bbox_to_anchor=(1, 0.5))
245
fig.subplots_adjust(right=0.8)
246
247
dates = node_results_flows.index
248
tick_distance = int(len(dates) / 7) - 1
249
ax.set_xticks(range(0, len(dates), tick_distance), minor=False)
250
if tick_distance > 0:
251
    ax.set_xticklabels(
252
        [
253
            item.strftime("%d-%m-%Y")
254
            for item in dates.tolist()[0::tick_distance]
255
        ],
256
        rotation=90,
257
        minor=False,
258
    )
259
260
plt.show()
261