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

dispatch.optimise_scenario()   B

Complexity

Conditions 2

Size

Total Lines 75
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 42
dl 0
loc 75
rs 8.872
c 0
b 0
f 0
cc 2
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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