1
|
|
|
# -*- coding: utf-8 -*- |
2
|
|
|
|
3
|
|
|
"""Modules for creating and analysing energy system graphs. |
4
|
|
|
|
5
|
|
|
This file is part of project oemof (github.com/oemof/oemof). It's copyrighted |
6
|
|
|
by the contributors recorded in the version control history of the file, |
7
|
|
|
available from its original location oemof/oemof/graph.py |
8
|
|
|
|
9
|
|
|
SPDX-FileCopyrightText: Simon Hilpert <> |
10
|
|
|
SPDX-FileCopyrightText: Uwe Krien <[email protected]> |
11
|
|
|
|
12
|
|
|
SPDX-License-Identifier: MIT |
13
|
|
|
""" |
14
|
|
|
|
15
|
|
|
import networkx as nx |
16
|
|
|
|
17
|
|
|
|
18
|
|
|
def create_nx_graph( |
19
|
|
|
energy_system=None, |
20
|
|
|
remove_nodes=None, |
21
|
|
|
filename=None, |
22
|
|
|
remove_nodes_with_substrings=None, |
23
|
|
|
remove_edges=None, |
24
|
|
|
): |
25
|
|
|
""" |
26
|
|
|
Create a `networkx.DiGraph` for the passed energy system and plot it. |
27
|
|
|
See http://networkx.readthedocs.io/en/latest/ for more information. |
28
|
|
|
|
29
|
|
|
Parameters |
30
|
|
|
---------- |
31
|
|
|
energy_system : `oemof.solph.network.EnergySystem` |
32
|
|
|
|
33
|
|
|
filename : str |
34
|
|
|
Absolute filename (with path) to write your graph in the graphml |
35
|
|
|
format. If no filename is given no file will be written. |
36
|
|
|
|
37
|
|
|
remove_nodes: list of strings |
38
|
|
|
Nodes to be removed e.g. ['node1', node2')] |
39
|
|
|
|
40
|
|
|
remove_nodes_with_substrings: list of strings |
41
|
|
|
Nodes that contain substrings to be removed e.g. ['elec', 'heat')] |
42
|
|
|
|
43
|
|
|
remove_edges: list of string tuples |
44
|
|
|
Edges to be removed e.g. [('resource_gas', 'gas_balance')] |
45
|
|
|
|
46
|
|
|
Examples |
47
|
|
|
-------- |
48
|
|
|
>>> import os |
49
|
|
|
>>> import pandas as pd |
50
|
|
|
>>> from oemof.network.network import Bus, Sink, Transformer |
51
|
|
|
>>> from oemof.network.energy_system import EnergySystem |
52
|
|
|
>>> import oemof.network.graph as grph |
53
|
|
|
>>> datetimeindex = pd.date_range('1/1/2017', periods=3, freq='H') |
54
|
|
|
>>> es = EnergySystem(timeindex=datetimeindex) |
55
|
|
|
>>> b_gas = Bus(label='b_gas', balanced=False) |
56
|
|
|
>>> bel1 = Bus(label='bel1') |
57
|
|
|
>>> bel2 = Bus(label='bel2') |
58
|
|
|
>>> demand_el = Sink(label='demand_el', inputs = [bel1]) |
59
|
|
|
>>> pp_gas = Transformer(label=('pp', 'gas'), |
60
|
|
|
... inputs=[b_gas], |
61
|
|
|
... outputs=[bel1], |
62
|
|
|
... conversion_factors={bel1: 0.5}) |
63
|
|
|
>>> line_to2 = Transformer(label='line_to2', inputs=[bel1], outputs=[bel2]) |
64
|
|
|
>>> line_from2 = Transformer(label='line_from2', |
65
|
|
|
... inputs=[bel2], outputs=[bel1]) |
66
|
|
|
>>> es.add(b_gas, bel1, demand_el, pp_gas, bel2, line_to2, line_from2) |
67
|
|
|
>>> my_graph = grph.create_nx_graph(es) |
68
|
|
|
>>> # export graph as .graphml for programs like Yed where it can be |
69
|
|
|
>>> # sorted and customized. this is especially helpful for large graphs |
70
|
|
|
>>> # grph.create_nx_graph(es, filename="my_graph.graphml") |
71
|
|
|
>>> [my_graph.has_node(n) |
72
|
|
|
... for n in ['b_gas', 'bel1', "('pp', 'gas')", 'demand_el', 'tester']] |
73
|
|
|
[True, True, True, True, False] |
74
|
|
|
>>> list(nx.attracting_components(my_graph)) |
75
|
|
|
[{'demand_el'}] |
76
|
|
|
>>> sorted(list(nx.strongly_connected_components(my_graph))[1]) |
77
|
|
|
['bel1', 'bel2', 'line_from2', 'line_to2'] |
78
|
|
|
>>> new_graph = grph.create_nx_graph(energy_system=es, |
79
|
|
|
... remove_nodes_with_substrings=['b_'], |
80
|
|
|
... remove_nodes=["('pp', 'gas')"], |
81
|
|
|
... remove_edges=[('bel2', 'line_from2')], |
82
|
|
|
... filename='test_graph') |
83
|
|
|
>>> [new_graph.has_node(n) |
84
|
|
|
... for n in ['b_gas', 'bel1', "('pp', 'gas')", 'demand_el', 'tester']] |
85
|
|
|
[False, True, False, True, False] |
86
|
|
|
>>> my_graph.has_edge("('pp', 'gas')", 'bel1') |
87
|
|
|
True |
88
|
|
|
>>> new_graph.has_edge('bel2', 'line_from2') |
89
|
|
|
False |
90
|
|
|
>>> os.remove('test_graph.graphml') |
91
|
|
|
|
92
|
|
|
Notes |
93
|
|
|
----- |
94
|
|
|
Needs graphviz and networkx (>= v.1.11) to work properly. |
95
|
|
|
Tested on Ubuntu 16.04 x64 and solydxk (debian 9). |
96
|
|
|
""" |
97
|
|
|
# construct graph from nodes and flows |
98
|
|
|
grph = nx.DiGraph() |
99
|
|
|
|
100
|
|
|
# add nodes |
101
|
|
|
for n in energy_system.nodes: |
102
|
|
|
grph.add_node(str(n.label), label=str(n.label)) |
103
|
|
|
|
104
|
|
|
# add labeled flows on directed edge if an optimization_model has been |
105
|
|
|
# passed or undirected edge otherwise |
106
|
|
|
for n in energy_system.nodes: |
107
|
|
|
for i in n.inputs.keys(): |
108
|
|
|
weight = getattr( |
109
|
|
|
energy_system.flows()[(i, n)], "nominal_value", None |
110
|
|
|
) |
111
|
|
|
if weight is None: |
112
|
|
|
grph.add_edge(str(i.label), str(n.label)) |
113
|
|
|
else: |
114
|
|
|
grph.add_edge( |
115
|
|
|
str(i.label), str(n.label), weigth=format(weight, ".2f") |
116
|
|
|
) |
117
|
|
|
|
118
|
|
|
# remove nodes and edges based on precise labels |
119
|
|
|
if remove_nodes is not None: |
120
|
|
|
grph.remove_nodes_from(remove_nodes) |
121
|
|
|
if remove_edges is not None: |
122
|
|
|
grph.remove_edges_from(remove_edges) |
123
|
|
|
|
124
|
|
|
# remove nodes based on substrings |
125
|
|
|
if remove_nodes_with_substrings is not None: |
126
|
|
|
for i in remove_nodes_with_substrings: |
127
|
|
|
remove_nodes = [ |
128
|
|
|
str(v.label) for v in energy_system.nodes if i in str(v.label) |
129
|
|
|
] |
130
|
|
|
grph.remove_nodes_from(remove_nodes) |
131
|
|
|
|
132
|
|
|
if filename is not None: |
133
|
|
|
if filename[-8:] != ".graphml": |
134
|
|
|
filename = filename + ".graphml" |
135
|
|
|
nx.write_graphml(grph, filename) |
136
|
|
|
|
137
|
|
|
return grph |
138
|
|
|
|