-
Notifications
You must be signed in to change notification settings - Fork 124
/
basic_example.py
312 lines (251 loc) · 10.4 KB
/
basic_example.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# -*- 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(Converter) |<---------| |
|------------------>|
| | |
storage(Storage) |<------------------|
|------------------>|
Code
----
Download source code: :download:`basic_example.py </../examples/basic_example/basic_example.py>`
.. dropdown:: Click to display code
.. literalinclude:: /../examples/basic_example/basic_example.py
:language: python
:lines: 61-
Data
----
Download data: :download:`basic_example.csv </../examples/basic_example/basic_example.csv>`
Installation requirements
-------------------------
This example requires oemof.solph (v0.5.x), install by:
.. code:: bash
pip install oemof.solph[examples]
License
-------
`MIT license <https://github.com/oemof/oemof-solph/blob/dev/LICENSE>`_
"""
###########################################################################
# imports
###########################################################################
import logging
import os
import pprint as pp
import warnings
from datetime import datetime
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 create_time_index
from oemof.solph import flows
from oemof.solph import helpers
from oemof.solph import processing
from oemof.solph import views
def main(dump_and_restore=False):
# For models that need a long time to optimise, saving and loading the
# EnergySystem might be advised. By default, we do not do this here. Feel
# free to experiment with this once you understood the rest of the code.
dump_results = restore_results = dump_and_restore
# *************************************************************************
# ********** PART 1 - Define and optimise the energy system ***************
# *************************************************************************
# 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}. Values for one timestep created!"
warnings.warn(msg.format(filename), UserWarning)
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 = create_time_index(2012, number=number_of_time_steps)
# create the energysystem and assign the time index
energysystem = EnergySystem(
timeindex=date_time_index, infer_last_interval=False
)
##########################################################################
# Create oemof objects
##########################################################################
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 gas commodity
energysystem.add(
cmp.Source(
label="rgas",
outputs={bgas: flows.Flow()},
)
)
# 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
# nominal_value is set to 1 because demand_el is not a normalised series
energysystem.add(
cmp.Sink(
label="demand",
inputs={bel: flows.Flow(fix=data["demand_el"], nominal_value=1)},
)
)
# create simple converter object representing a gas power plant
energysystem.add(
cmp.Converter(
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(
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.
if dump_results:
energysystem.dump(dpath=None, filename=None)
# *************************************************************************
# ********** PART 2 - Processing the results ******************************
# *************************************************************************
# Saved data can be restored in a second script. So you can work on the
# data analysis without re-running the optimisation every time. If you do
# so, make sure that you really load the results you want. For example,
# if dumping fails, you might exidentially load outdated results.
if restore_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"][
datetime(2012, 2, 25, 8, 0, 0) : datetime(2012, 2, 25, 17, 0, 0)
]
)
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
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))
if __name__ == "__main__":
main()