Problem 3.4

Problem 3.4#

Note

This problem is mostly adapted from the following resources:

Integrated Energy Grids

Problem 3.4


Note

If you have not yet set up Python on your computer, you can execute this tutorial in your browser via Google Colab. Click on the rocket in the top right corner and launch “Colab”. If that doesn’t work download the .ipynb file and import it in Google Colab.

Then install the following packages by executing the following command in a Jupyter cell at the top of the notebook.

!pip install numpy networkx pandas matplotlib

a) Create a network object and calculate the average degree

We load the simplified dataset of the European high-voltage transmission network including nodes and edges and codify it in network object.

In this dataset, HVDC links have been left out, and the data only shows AC transmission lines.

nodes = pd.read_csv('data/nodes.csv', index_col=0)
nodes.head(5)
x y
Bus
8838 -2.169800 53.243852
7157 11.927347 45.403086
1316 14.475861 40.761821
7421 4.520127 50.488619
1317 14.639282 40.688969
edges = pd.read_csv('data/edges.csv', index_col=0)
edges.head(5)
bus0 bus1 s_nom x_pu
Line
8968 1771 1774 491.556019 0.000256
11229 3792 3794 3396.205223 0.000017
11228 3793 3794 3396.205223 0.000012
11227 3793 3796 3396.205223 0.000031
8929 927 929 491.556019 0.000092

networkx provides a utility function nx.from_pandas_edgelist() to build a network from edges listed in a pandas.DataFrame:

N = nx.from_pandas_edgelist(edges, "bus0", "bus1", edge_attr=["x_pu", "s_nom"])

We can get some basic info about the graph:

print(N)
Graph with 3532 nodes and 5157 edges
degrees = [val for node, val in N.degree()]
np.mean(degrees)
np.float64(2.920158550396376)

b) Add the information on the position of each node provided in ‘data/nodes.csv’ and make a plot of the network

The nodes DataFrame provides us with the coordinates of the graph’s nodes. To make the nx.draw() function use these coordinates, we need to bring them into a particular format.

{nodeA: (x, y),
 nodeB: (x, y),
 nodeC: (x, y)}
pos = nodes.apply(tuple, axis=1).to_dict()

Let’s just look at the first 5 elements of the dictionary to check:

{k: pos[k] for k in list(pos.keys())[:5]}
{8838: (-2.16979999999999, 53.243852),
 7157: (11.9273472385104, 45.403085502256),
 1316: (14.475861, 40.761821),
 7421: (4.52012724307074, 50.4886188621382),
 1317: (14.639282, 40.688969)}

Now, we can draw the European transmission network:

fig, ax = plt.subplots(figsize=(10, 10))
nx.draw(N, pos=pos, node_size=0)
../_images/408ab2e94494ce7cd42540137dab21ea441d58fcd476e3125fd157b50f1c25ff.png

c) Determine how many independent networks exist (these are the synchronous zones of the European transmission network)

You can already see that not all parts of the Network are connected with each other. Ireland, Great Britain, Scandinavia, the Baltics and some Islands in the Mediterranean are not connected to the continental grid. At least not via AC transmission lines. They are through HVDC links. These subgraphs denote the different synchronous zones of the European transmission network. Notice that DK1 (Jutland) is connected to the mainland Europen synchronous zone while DK2 (Zealand) is connected to the Scandinavian synchronous zone. DK1 and DK2 are connected through a HVDC link.

len(list(nx.connected_components(N)))
7

Let’s build subgraphs for the synchronous zones by iterating over the connected components:

subgraphs = []
for c in nx.connected_components(N):
    subgraphs.append(N.subgraph(c).copy())

We can now color-code them in the network plot:

fig, ax = plt.subplots(figsize=(10, 10))
colors = ["red", "blue", "green", "orange", "teal", "cyan", "black"]
for i, sub in enumerate(subgraphs):
    sub_pos = {k: v for k, v in pos.items() if k in sub.nodes}
    nx.draw(sub, pos=sub_pos, node_size=0, edge_color=colors[i])
../_images/30a5dab39853567e842e9cb024d7787402c5cf8e9305ae05f1a0d682a195ccc2.png

d) For the synchronous zone corresponding to Scandinavia, calculate the number of nodes and edges. Calculate the Degree, Adjacency, Incidence, and Laplacian matrices. Make a heat map of each of those matrices to evaluate them visually.

SK = subgraphs[6]

Number of nodes and edges

len(SK.nodes)
304
len(SK.edges)
437

Degree matrix:

degrees=[deg for node, deg in SK.degree()]
D = np.diag(degrees)
plt.imshow(D, cmap="Blues")
<matplotlib.image.AxesImage at 0x7f11ae917b10>
../_images/d5e98d642d8418392451bd27ecc198f880cb82a629653f9d31d8a0ec8f61cbb4.png

Adjacency matrix:

A = nx.adjacency_matrix(SK, weight=None).todense()
plt.imshow(A, cmap="Reds")
<matplotlib.image.AxesImage at 0x7f11a6fafc90>
../_images/3632d2555de688975637380f2d9f2a8c133770b0f9fa0dff131f4bede50ada36.png

Incidence matrix:

K = nx.incidence_matrix(SK, oriented=True).todense()
K
array([[-1., -1., -1., ...,  0.,  0.,  0.],
       [ 0.,  0.,  0., ...,  0.,  0.,  0.],
       [ 0.,  0.,  0., ...,  0.,  0.,  0.],
       ...,
       [ 0.,  0.,  0., ...,  1., -1.,  0.],
       [ 0.,  0.,  0., ...,  0.,  0., -1.],
       [ 1.,  0.,  0., ...,  0.,  1.,  1.]], shape=(304, 437))
plt.imshow(K, cmap="PRGn")
<matplotlib.image.AxesImage at 0x7f119988ecd0>
../_images/1e1c50d1f9ca7996a1254547ec6d48c343a2ba71f42ffdfd34909ce5301a811a.png

Unweighted Laplacian:

L = nx.laplacian_matrix(SK, weight=None).todense()
L
array([[ 4,  0,  0, ...,  0,  0, -1],
       [ 0,  3, -1, ...,  0,  0,  0],
       [ 0, -1,  4, ...,  0,  0,  0],
       ...,
       [ 0,  0,  0, ...,  3,  0, -1],
       [ 0,  0,  0, ...,  0,  2, -1],
       [-1,  0,  0, ..., -1, -1,  3]], shape=(304, 304))
plt.imshow(L, cmap="RdBu")
<matplotlib.image.AxesImage at 0x7f1199927b10>
../_images/5f1e1bb812a7090199d4c801957695de5239e733ddaf21fd30be94fb41c728cd.png