dispatch   A
last analyzed

Complexity

Total Complexity 36

Size/Duplication

Total Lines 443
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 36
eloc 208
dl 0
loc 443
rs 9.52
c 0
b 0
f 0

4 Functions

Rating   Name   Duplication   Size   Complexity  
A nodes_from_excel() 0 42 3
B draw_graph() 0 72 6
F create_nodes() 0 172 24
B main() 0 78 3
1
# -*- coding: utf-8 -*-
2
3
"""
4
General description
5
-------------------
6
As the csv-reader was removed with version 0.2 this example shows how to create
7
an excel-reader. The example is equivalent to the old csv-reader example.
8
Following the example one can customise the excel reader to ones own needs.
9
10
The pandas package supports the '.xls' and the '.xlsx' format but one can
11
create read and adept the files with open source software such as libreoffice,
12
openoffice, gnumeric, ...
13
14
Code
15
----
16
Download source code: :download:`dispatch.py </../examples/excel_reader/dispatch.py>`
17
18
.. dropdown:: Click to display code
19
20
    .. literalinclude:: /../examples/excel_reader/dispatch.py
21
        :language: python
22
        :lines: 57-437
23
24
Data
25
----
26
Download data: :download:`scenario.xlsx </../examples/excel_reader/scenario.xlsx>`
27
28
Installation requirements
29
-------------------------
30
This example requires oemof.solph (v0.5.x), install by:
31
32
.. code:: bash
33
34
    pip install oemof.solph[examples]
35
    pip install openpyxl
36
37
38
If you want to plot the energy system's graph, you have to install pygraphviz
39
using:
40
41
.. code:: bash
42
43
    pip install pygraphviz
44
45
For pygraphviz under Windows, some hints are available in the oemof Wiki:
46
https://github.com/oemof/oemof/wiki/Windows---general
47
48
License
49
-------
50
Uwe Krien <[email protected]>
51
Jonathan Amme <[email protected]>
52
53
`MIT license <https://github.com/oemof/oemof-solph/blob/dev/LICENSE>`_
54
55
"""
56
57
import logging
58
import os
59
60
import networkx as nx
61
import pandas as pd
62
from matplotlib import pyplot as plt
63
from oemof.network.graph import create_nx_graph
64
from oemof.tools import logger
65
66
from oemof import solph
67
68
69
def nodes_from_excel(filename):
70
    """Read node data from Excel sheet
71
72
    Parameters
73
    ----------
74
    filename : :obj:`str`
75
        Path to excel file
76
77
    Returns
78
    -------
79
    :obj:`dict`
80
        Imported nodes data
81
    """
82
83
    # does Excel file exist?
84
    if not filename or not os.path.isfile(filename):
85
        raise FileNotFoundError(
86
            "Excel data file {} not found.".format(filename)
87
        )
88
89
    xls = pd.ExcelFile(filename)
90
91
    nodes_data = {
92
        "buses": xls.parse("buses"),
93
        "commodity_sources": xls.parse("commodity_sources"),
94
        "converters": xls.parse("converters"),
95
        "renewables": xls.parse("renewables"),
96
        "demand": xls.parse("demand"),
97
        "storages": xls.parse("storages"),
98
        "powerlines": xls.parse("powerlines"),
99
        "timeseries": xls.parse("time_series"),
100
    }
101
102
    # set datetime index
103
    nodes_data["timeseries"].set_index("timestamp", inplace=True)
104
    nodes_data["timeseries"].index = pd.to_datetime(
105
        nodes_data["timeseries"].index
106
    )
107
108
    print("Data from Excel file {} imported.".format(filename))
109
110
    return nodes_data
111
112
113
def create_nodes(nd=None):
114
    """Create nodes (oemof objects) from node dict
115
116
    Parameters
117
    ----------
118
    nd : :obj:`dict`
119
        Nodes data
120
121
    Returns
122
    -------
123
    nodes : `obj`:dict of :class:`nodes <oemof.network.Node>`
124
    """
125
126
    if not nd:
127
        raise ValueError("No nodes data provided.")
128
129
    nodes = []
130
131
    # Create Bus objects from buses table
132
    busd = {}
133
134
    for i, b in nd["buses"].iterrows():
135
        if b["active"]:
136
            bus = solph.Bus(label=b["label"])
137
            nodes.append(bus)
138
139
            busd[b["label"]] = bus
140
            if b["excess"]:
141
                nodes.append(
142
                    solph.components.Sink(
143
                        label=b["label"] + "_excess",
144
                        inputs={
145
                            busd[b["label"]]: solph.Flow(
146
                                variable_costs=b["excess costs"]
147
                            )
148
                        },
149
                    )
150
                )
151
            if b["shortage"]:
152
                nodes.append(
153
                    solph.components.Source(
154
                        label=b["label"] + "_shortage",
155
                        outputs={
156
                            busd[b["label"]]: solph.Flow(
157
                                variable_costs=b["shortage costs"]
158
                            )
159
                        },
160
                    )
161
                )
162
163
    # Create Source objects from table 'commodity sources'
164
    for i, cs in nd["commodity_sources"].iterrows():
165
        if cs["active"]:
166
            nodes.append(
167
                solph.components.Source(
168
                    label=cs["label"],
169
                    outputs={
170
                        busd[cs["to"]]: solph.Flow(
171
                            variable_costs=cs["variable costs"]
172
                        )
173
                    },
174
                )
175
            )
176
177
    # Create Source objects with fixed time series from 'renewables' table
178
    for i, re in nd["renewables"].iterrows():
179
        if re["active"]:
180
            # set static outflow values
181
            outflow_args = {"nominal_capacity": re["capacity"]}
182
            # get time series for node and parameter
183
            for col in nd["timeseries"].columns.values:
184
                if col.split(".")[0] == re["label"]:
185
                    outflow_args[col.split(".")[1]] = nd["timeseries"][col]
186
187
            # create
188
            nodes.append(
189
                solph.components.Source(
190
                    label=re["label"],
191
                    outputs={busd[re["to"]]: solph.Flow(**outflow_args)},
192
                )
193
            )
194
195
    # Create Sink objects with fixed time series from 'demand' table
196
    for i, de in nd["demand"].iterrows():
197
        if de["active"]:
198
            # set static inflow values
199
            inflow_args = {"nominal_capacity": de["nominal value"]}
200
            # get time series for node and parameter
201
            for col in nd["timeseries"].columns.values:
202
                if col.split(".")[0] == de["label"]:
203
                    inflow_args[col.split(".")[1]] = nd["timeseries"][col]
204
205
            # create
206
            nodes.append(
207
                solph.components.Sink(
208
                    label=de["label"],
209
                    inputs={busd[de["from"]]: solph.Flow(**inflow_args)},
210
                )
211
            )
212
213
    # Create Converter objects from 'converters' table
214
    for i, t in nd["converters"].iterrows():
215
        if t["active"]:
216
            # set static inflow values
217
            inflow_args = {"variable_costs": t["variable input costs"]}
218
            # get time series for inflow of converter
219
            for col in nd["timeseries"].columns.values:
220
                if col.split(".")[0] == t["label"]:
221
                    inflow_args[col.split(".")[1]] = nd["timeseries"][col]
222
            # create
223
            nodes.append(
224
                solph.components.Converter(
225
                    label=t["label"],
226
                    inputs={busd[t["from"]]: solph.Flow(**inflow_args)},
227
                    outputs={
228
                        busd[t["to"]]: solph.Flow(
229
                            nominal_capacity=t["capacity"]
230
                        )
231
                    },
232
                    conversion_factors={busd[t["to"]]: t["efficiency"]},
233
                )
234
            )
235
236
    for i, s in nd["storages"].iterrows():
237
        if s["active"]:
238
            nodes.append(
239
                solph.components.GenericStorage(
240
                    label=s["label"],
241
                    inputs={
242
                        busd[s["bus"]]: solph.Flow(
243
                            nominal_capacity=s["capacity inflow"],
244
                            variable_costs=s["variable input costs"],
245
                        )
246
                    },
247
                    outputs={
248
                        busd[s["bus"]]: solph.Flow(
249
                            nominal_capacity=s["capacity outflow"],
250
                            variable_costs=s["variable output costs"],
251
                        )
252
                    },
253
                    nominal_capacity=s["nominal capacity"],
254
                    loss_rate=s["capacity loss"],
255
                    initial_storage_level=s["initial capacity"],
256
                    max_storage_level=s["capacity max"],
257
                    min_storage_level=s["capacity min"],
258
                    inflow_conversion_factor=s["efficiency inflow"],
259
                    outflow_conversion_factor=s["efficiency outflow"],
260
                )
261
            )
262
263
    for i, p in nd["powerlines"].iterrows():
264
        if p["active"]:
265
            bus1 = busd[p["bus_1"]]
266
            bus2 = busd[p["bus_2"]]
267
            nodes.append(
268
                solph.components.Converter(
269
                    label="powerline" + "_" + p["bus_1"] + "_" + p["bus_2"],
270
                    inputs={bus1: solph.Flow()},
271
                    outputs={bus2: solph.Flow()},
272
                    conversion_factors={bus2: p["efficiency"]},
273
                )
274
            )
275
            nodes.append(
276
                solph.components.Converter(
277
                    label="powerline" + "_" + p["bus_2"] + "_" + p["bus_1"],
278
                    inputs={bus2: solph.Flow()},
279
                    outputs={bus1: solph.Flow()},
280
                    conversion_factors={bus1: p["efficiency"]},
281
                )
282
            )
283
284
    return nodes
285
286
287
def draw_graph(
288
    grph,
289
    edge_labels=True,
290
    node_color="#AFAFAF",
291
    edge_color="#CFCFCF",
292
    plot=True,
293
    node_size=2000,
294
    with_labels=True,
295
    arrows=True,
296
    layout="neato",
297
):
298
    """
299
    Parameters
300
    ----------
301
    grph : networkxGraph
302
        A graph to draw.
303
    edge_labels : boolean
304
        Use nominal capacities of flow as edge label
305
    node_color : dict or string
306
        Hex color code oder matplotlib color for each node. If string, all
307
        colors are the same.
308
309
    edge_color : string
310
        Hex color code oder matplotlib color for edge color.
311
312
    plot : boolean
313
        Show matplotlib plot.
314
315
    node_size : integer
316
        Size of nodes.
317
318
    with_labels : boolean
319
        Draw node labels.
320
321
    arrows : boolean
322
        Draw arrows on directed edges. Works only if an optimization_model has
323
        been passed.
324
    layout : string
325
        networkx graph layout, one of: neato, dot, twopi, circo, fdp, sfdp.
326
    """
327
    if isinstance(node_color, dict):
328
        node_color = [node_color.get(g, "#AFAFAF") for g in grph.nodes()]
329
330
    # set drawing options
331
    options = {
332
        "with_labels": with_labels,
333
        "node_color": node_color,
334
        "edge_color": edge_color,
335
        "node_size": node_size,
336
        "arrows": arrows,
337
    }
338
339
    # try to use pygraphviz for graph layout
340
    try:
341
        import pygraphviz
342
343
        pos = nx.drawing.nx_agraph.graphviz_layout(grph, prog=layout)
344
    except ImportError:
345
        logging.error("Module pygraphviz not found, I won't plot the graph.")
346
        return
347
348
    # draw graph
349
    nx.draw(grph, pos=pos, **options)
350
351
    # add edge labels for all edges
352
    if edge_labels is True and plt:
353
        labels = nx.get_edge_attributes(grph, "weight")
354
        nx.draw_networkx_edge_labels(grph, pos=pos, edge_labels=labels)
355
356
    # show output
357
    if plot is True:
358
        plt.show()
359
360
361
def main(optimize=True):
362
    logger.define_logging()
363
    datetime_index = pd.date_range(
364
        "2016-01-01 00:00:00", "2016-01-01 23:00:00", freq="60min"
365
    )
366
367
    # model creation and solving
368
    logging.info("Starting optimization")
369
370
    # initialisation of the energy system
371
    esys = solph.EnergySystem(
372
        timeindex=datetime_index, infer_last_interval=False
373
    )
374
375
    # read node data from Excel sheet
376
    excel_nodes = nodes_from_excel(
377
        os.path.join(
378
            os.path.dirname(os.path.abspath(__file__)),
379
            "scenario.xlsx",
380
        )
381
    )
382
383
    # create nodes from Excel sheet data
384
    my_nodes = create_nodes(nd=excel_nodes)
385
386
    # add nodes and flows to energy system
387
    esys.add(*my_nodes)
388
389
    print("*********************************************************")
390
    print("The following objects have been created from excel sheet:")
391
    for n in esys.nodes:
392
        oobj = (
393
            str(type(n)).replace("<class 'oemof.solph.", "").replace("'>", "")
394
        )
395
        print(oobj + ":", n.label)
396
    print("*********************************************************")
397
398
    if optimize is False:
399
        return esys
400
401
    # creation of a least cost model from the energy system
402
    om = solph.Model(esys)
403
    om.receive_duals()
404
405
    # solving the linear problem using the given solver
406
    om.solve(solver="cbc")
407
408
    # create graph of esys
409
    # You can use argument filename='/home/somebody/my_graph.graphml'
410
    # to dump your graph to disc. You can open it using e.g. yEd or gephi
411
    graph = create_nx_graph(esys)
412
413
    # plot esys graph
414
    draw_graph(
415
        grph=graph,
416
        plot=True,
417
        layout="neato",
418
        node_size=1000,
419
        node_color={"R1_bus_el": "#cd3333", "R2_bus_el": "#cd3333"},
420
    )
421
422
    # print and plot some results
423
    results = solph.processing.results(om)
424
425
    region2 = solph.views.node(results, "R2_bus_el")
426
    region1 = solph.views.node(results, "R1_bus_el")
427
428
    print(region2["sequences"].sum())
429
    print(region1["sequences"].sum())
430
431
    fig, ax = plt.subplots(figsize=(10, 5))
432
    region1["sequences"].plot(ax=ax)
433
    ax.legend(
434
        loc="upper center", prop={"size": 8}, bbox_to_anchor=(0.5, 1.4), ncol=3
435
    )
436
    fig.subplots_adjust(top=0.7)
437
    plt.show()
438
    logging.info("Done!")
439
440
441
if __name__ == "__main__":
442
    main()
443