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

basic_example   A

Complexity

Total Complexity 0

Size/Duplication

Total Lines 283
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 0
eloc 122
dl 0
loc 283
rs 10
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2
3
"""
4
General description
5
-------------------
6
7
A basic example to show how to model a simple energy system with oemof.solph.
8
9
The following energy system is modeled:
10
11
.. code-block:: text
12
13
                     input/output  bgas     bel
14
                         |          |        |
15
                         |          |        |
16
     wind(FixedSource)   |------------------>|
17
                         |          |        |
18
     pv(FixedSource)     |------------------>|
19
                         |          |        |
20
     rgas(Commodity)     |--------->|        |
21
                         |          |        |
22
     demand(Sink)        |<------------------|
23
                         |          |        |
24
                         |          |        |
25
     pp_gas(Transformer) |<---------|        |
26
                         |------------------>|
27
                         |          |        |
28
     storage(Storage)    |<------------------|
29
                         |------------------>|
30
31
32
Data
33
----
34
basic_example.csv
35
36
37
Installation requirements
38
-------------------------
39
This example requires oemof.solph (v0.5.x), install by:
40
41
    pip install oemof.solph[examples]
42
43
License
44
-------
45
`MIT license <https://github.com/oemof/oemof-solph/blob/dev/LICENSE>`_
46
"""
47
48
# ****************************************************************************
49
# ********** PART 1 - Define and optimise the energy system ******************
50
# ****************************************************************************
51
52
###############################################################################
53
# imports
54
###############################################################################
55
56
import logging
57
import os
58
import pprint as pp
59
import warnings
60
from datetime import datetime
61
62
import matplotlib.pyplot as plt
63
import pandas as pd
64
from oemof.tools import logger
65
66
from oemof.solph import EnergySystem
67
from oemof.solph import Model
68
from oemof.solph import buses
69
from oemof.solph import components as cmp
70
from oemof.solph import create_time_index
71
from oemof.solph import flows
72
from oemof.solph import helpers
73
from oemof.solph import processing
74
from oemof.solph import views
75
76
# Read data file
77
78
filename = os.path.join(os.getcwd(), "basic_example.csv")
79
try:
80
    data = pd.read_csv(filename)
81
except FileNotFoundError:
82
    msg = "Data file not found: {0}. Only one value used!"
83
    warnings.warn(msg.format(filename), UserWarning)
84
    data = pd.DataFrame({"pv": [0.3], "wind": [0.6], "demand_el": [500]})
85
86
solver = "cbc"  # 'glpk', 'gurobi',....
87
debug = False  # Set number_of_timesteps to 3 to get a readable lp-file.
88
number_of_time_steps = len(data)
89
solver_verbose = False  # show/hide solver output
90
91
# initiate the logger (see the API docs for more information)
92
logger.define_logging(
93
    logfile="oemof_example.log",
94
    screen_level=logging.INFO,
95
    file_level=logging.INFO,
96
)
97
98
logging.info("Initialize the energy system")
99
date_time_index = create_time_index(2012, number=number_of_time_steps)
100
101
energysystem = EnergySystem(
102
    timeindex=date_time_index, infer_last_interval=False
103
)
104
105
##########################################################################
106
# Create oemof object
107
##########################################################################
108
109
logging.info("Create oemof objects")
110
111
# The bus objects were assigned to variables which makes it easier to connect
112
# components to these buses (see below).
113
114
# create natural gas bus
115
bgas = buses.Bus(label="natural_gas")
116
117
# create electricity bus
118
bel = buses.Bus(label="electricity")
119
120
# adding the buses to the energy system
121
energysystem.add(bgas, bel)
122
123
# create excess component for the electricity bus to allow overproduction
124
energysystem.add(cmp.Sink(label="excess_bel", inputs={bel: flows.Flow()}))
125
126
# create source object representing the natural gas commodity (annual limit)
127
energysystem.add(
128
    cmp.Source(
129
        label="rgas",
130
        outputs={bgas: flows.Flow()},
131
    )
132
)
133
134
# create fixed source object representing wind power plants
135
energysystem.add(
136
    cmp.Source(
137
        label="wind",
138
        outputs={bel: flows.Flow(fix=data["wind"], nominal_value=1000000)},
139
    )
140
)
141
142
# create fixed source object representing pv power plants
143
energysystem.add(
144
    cmp.Source(
145
        label="pv",
146
        outputs={bel: flows.Flow(fix=data["pv"], nominal_value=582000)},
147
    )
148
)
149
150
# create simple sink object representing the electrical demand
151
energysystem.add(
152
    cmp.Sink(
153
        label="demand",
154
        inputs={bel: flows.Flow(fix=data["demand_el"], nominal_value=1)},
155
    )
156
)
157
158
# create simple transformer object representing a gas power plant
159
energysystem.add(
160
    cmp.Transformer(
161
        label="pp_gas",
162
        inputs={bgas: flows.Flow()},
163
        outputs={bel: flows.Flow(nominal_value=10e10, variable_costs=50)},
164
        conversion_factors={bel: 0.58},
165
    )
166
)
167
168
# create storage object representing a battery
169
storage = cmp.GenericStorage(
170
    nominal_storage_capacity=10077997,
171
    label="storage",
172
    inputs={bel: flows.Flow(nominal_value=10077997 / 6)},
173
    outputs={
174
        bel: flows.Flow(nominal_value=10077997 / 6, variable_costs=0.001)
175
    },
176
    loss_rate=0.00,
177
    initial_storage_level=None,
178
    inflow_conversion_factor=1,
179
    outflow_conversion_factor=0.8,
180
)
181
182
energysystem.add(storage)
183
184
##########################################################################
185
# Optimise the energy system and plot the results
186
##########################################################################
187
188
logging.info("Optimise the energy system")
189
190
# initialise the operational model
191
model = Model(energysystem)
192
193
# This is for debugging only. It is not(!) necessary to solve the problem and
194
# should be set to False to save time and disc space in normal use. For
195
# debugging the timesteps should be set to 3, to increase the readability of
196
# the lp-file.
197
if debug:
198
    filename = os.path.join(
199
        helpers.extend_basic_path("lp_files"), "basic_example.lp"
200
    )
201
    logging.info("Store lp-file in {0}.".format(filename))
202
    model.write(filename, io_options={"symbolic_solver_labels": True})
203
204
# if tee_switch is true solver messages will be displayed
205
logging.info("Solve the optimization problem")
206
model.solve(solver=solver, solve_kwargs={"tee": solver_verbose})
207
208
logging.info("Store the energy system with the results.")
209
210
# The processing module of the outputlib can be used to extract the results
211
# from the model transfer them into a homogeneous structured dictionary.
212
213
# add results to the energy system to make it possible to store them.
214
energysystem.results["main"] = processing.results(model)
215
energysystem.results["meta"] = processing.meta_results(model)
216
217
# The default path is the '.oemof' folder in your $HOME directory.
218
# The default filename is 'es_dump.oemof'.
219
# You can omit the attributes (as None is the default value) for testing cases.
220
# You should use unique names/folders for valuable results to avoid
221
# overwriting.
222
223
# store energy system with results
224
energysystem.dump(dpath=None, filename=None)
225
226
# ****************************************************************************
227
# ********** PART 2 - Processing the results *********************************
228
# ****************************************************************************
229
230
logging.info("**** The script can be divided into two parts here.")
231
logging.info("Restore the energy system and the results.")
232
energysystem = EnergySystem()
233
energysystem.restore(dpath=None, filename=None)
234
235
# define an alias for shorter calls below (optional)
236
results = energysystem.results["main"]
237
storage = energysystem.groups["storage"]
238
239
# print a time slice of the state of charge
240
print("")
241
print("********* State of Charge (slice) *********")
242
print(
243
    results[(storage, None)]["sequences"][
244
        datetime(2012, 2, 25, 8, 0, 0) : datetime(2012, 2, 25, 17, 0, 0)
245
    ]
246
)
247
print("")
248
249
250
# get all variables of a specific component/bus
251
custom_storage = views.node(results, "storage")
252
electricity_bus = views.node(results, "electricity")
253
254
# plot the time series (sequences) of a specific component/bus
255
256
fig, ax = plt.subplots(figsize=(10, 5))
257
custom_storage["sequences"].plot(ax=ax, kind="line", drawstyle="steps-post")
258
plt.legend(
259
    loc="upper center",
260
    prop={"size": 8},
261
    bbox_to_anchor=(0.5, 1.25),
262
    ncol=2,
263
)
264
fig.subplots_adjust(top=0.8)
265
plt.show()
266
267
fig, ax = plt.subplots(figsize=(10, 5))
268
electricity_bus["sequences"].plot(ax=ax, kind="line", drawstyle="steps-post")
269
plt.legend(
270
    loc="upper center", prop={"size": 8}, bbox_to_anchor=(0.5, 1.3), ncol=2
271
)
272
fig.subplots_adjust(top=0.8)
273
plt.show()
274
275
# print the solver results
276
print("********* Meta results *********")
277
pp.pprint(energysystem.results["meta"])
278
print("")
279
280
# print the sums of the flows around the electricity bus
281
print("********* Main results *********")
282
print(electricity_bus["sequences"].sum(axis=0))
283