This jupyter notebooks explains how to access and interpret the internal datastructure with relevant matrices.
We use the simple example network from the create_simple tutorial as an example for how to access internal calculation parameters:
import pandapower as pp
import pandapower.networks as nw
net = nw.example_simple()
print(net)
This pandapower network includes the following parameter tables: - bus (7 elements) - load (1 element) - sgen (1 element) - gen (1 element) - switch (8 elements) - shunt (1 element) - ext_grid (1 element) - line (4 elements) - trafo (1 element)
First, we run a power flow in this network:
pp.runpp(net)
When a power flow is carried out, the element based grid model is translated into a bus-branch model. That bus-branch model is stored in a data structure that is based on the PYPOWER/MATPOWER casefile (with some extensions). This ppc can be accesed after power flow:
net._ppc
{'baseMVA': 1, 'version': 2, 'bus': array([[ 0.00000000e+00, 3.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00, 1.02000000e+00, 0.00000000e+00, 1.10000000e+02, 1.00000000e+00, 2.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], [ 1.00000000e+00, 1.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 9.60000000e-01, 1.00000000e+00, 1.02082951e+00, 3.24135661e-02, 1.10000000e+02, 1.00000000e+00, 2.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], [ 2.00000000e+00, 1.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00, 1.02456241e+00, 1.80284793e+00, 2.00000000e+01, 1.00000000e+00, 2.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], [ 3.00000000e+00, 2.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00, 1.03000000e+00, 1.87045457e+00, 2.00000000e+01, 1.00000000e+00, 2.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], [ 4.00000000e+00, 1.00000000e+00, -8.00000000e-01, 2.90000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00, 1.02320534e+00, 1.95222438e+00, 2.00000000e+01, 1.00000000e+00, 2.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], [ 5.00000000e+00, 1.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00, 1.03000700e+00, 1.86983253e+00, 2.00000000e+01, 1.00000000e+00, 1.10000000e+00, 9.00000000e-01, 0.00000000e+00, 0.00000000e+00], [ 6.00000000e+00, 4.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00, nan, nan, 1.10000000e+02, 1.00000000e+00, 2.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], [ 7.00000000e+00, 4.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00, nan, nan, 2.00000000e+01, 1.00000000e+00, 2.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00]]), 'branch': array([[ 0.00000000e+00+0.j , 1.00000000e+00+0.j , 4.95867769e-05+0.j , 1.19008264e-04+0.j , 5.47391104e+00+0.j , 2.50000000e+02+0.j , 2.50000000e+02+0.j , 2.50000000e+02+0.j , 1.00000000e+00+0.j , 0.00000000e+00+0.j , 1.00000000e+00+0.j , -3.60000000e+02+0.j , 3.60000000e+02+0.j , -6.74111530e+00+0.j , -7.14688297e+00+0.j , 6.74416214e+00+0.j , 1.45450502e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j ], [ 2.00000000e+00+0.j , 3.00000000e+00+0.j , 6.10000000e-04+0.j , 5.60000000e-04+0.j , 7.64035333e-02+0.j , 2.50000000e+02+0.j , 2.50000000e+02+0.j , 2.50000000e+02+0.j , 1.00000000e+00+0.j , 0.00000000e+00+0.j , 1.00000000e+00+0.j , -3.60000000e+02+0.j , 3.60000000e+02+0.j , -5.97238973e+00+0.j , -3.48162502e+00+0.j , 5.99999998e+00+0.j , 3.42634241e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j ], [ 3.00000000e+00+0.j , 5.00000000e+00+0.j , 5.19662500e-03+0.j , 3.25500000e-03+0.j , 4.17831823e-03+0.j , 2.50000000e+02+0.j , 2.50000000e+02+0.j , 2.50000000e+02+0.j , 1.00000000e+00+0.j , 0.00000000e+00+0.j , 1.00000000e+00+0.j , -3.60000000e+02+0.j , 3.60000000e+02+0.j , 2.40626638e-08+0.j , -4.43279288e-03+0.j , 5.75627463e-14+0.j , 3.11693679e-14+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j ], [ 4.00000000e+00+0.j , 2.00000000e+00+0.j , 7.62500000e-04+0.j , 7.00000000e-04+0.j , 9.55044167e-02+0.j , 2.50000000e+02+0.j , 2.50000000e+02+0.j , 2.50000000e+02+0.j , 1.00000000e+00+0.j , 0.00000000e+00+0.j , 1.00000000e+00+0.j , -3.60000000e+02+0.j , 3.60000000e+02+0.j , 8.00000000e-01+0.j , -2.90000000e+00+0.j , -7.93618189e-01+0.j , 2.80573774e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j ], [ 1.00000000e+00+0.j , 2.00000000e+00+0.j , 1.63923679e-04+0.j , 4.79726336e-03+0.j , -1.05000908e-02-0.01399964j, 2.50000000e+02+0.j , 2.50000000e+02+0.j , 2.50000000e+02+0.j , 1.00000000e+00+0.j , 0.00000000e+00+0.j , 1.00000000e+00+0.j , -3.60000000e+02+0.j , 3.60000000e+02+0.j , -6.74416214e+00+0.j , -4.54095852e-01+0.j , 6.76600792e+00+0.j , 6.75887284e-01+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j ]]), 'gen': array([[ 0.00000000e+00, -6.74111530e+00, -7.14688297e+00, 0.00000000e+00, 0.00000000e+00, 1.02000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+09, -1.00000000e+09, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], [ 3.00000000e+00, 6.00000000e+00, 3.42190962e+00, 3.00000000e+00, -3.00000000e+00, 1.03000000e+00, nan, 1.00000000e+00, 1.00000000e+09, -1.00000000e+09, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00]]), 'internal': {'Ybus': <6x6 sparse matrix of type '<class 'numpy.complex128'>' with 16 stored elements in Compressed Sparse Row format>, 'Yf': <5x6 sparse matrix of type '<class 'numpy.complex128'>' with 10 stored elements in Compressed Sparse Row format>, 'Yt': <5x6 sparse matrix of type '<class 'numpy.complex128'>' with 10 stored elements in Compressed Sparse Row format>, 'branch_is': array([ True, True, True, True, True]), 'gen_is': array([ True, True]), 'DLF': array([], dtype=complex128), 'buses_ord_bfs_nets': array([], dtype=float64), 'ref_gens': array([0]), 'J': <9x9 sparse matrix of type '<class 'numpy.float64'>' with 41 stored elements in Compressed Sparse Row format>, 'Vm_it': None, 'Va_it': None, 'bus': array([[ 0.00000000e+00, 3.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00, 1.02000000e+00, 0.00000000e+00, 1.10000000e+02, 1.00000000e+00, 2.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], [ 1.00000000e+00, 1.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 9.60000000e-01, 1.00000000e+00, 1.02082951e+00, 3.24135661e-02, 1.10000000e+02, 1.00000000e+00, 2.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], [ 2.00000000e+00, 1.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00, 1.02456241e+00, 1.80284793e+00, 2.00000000e+01, 1.00000000e+00, 2.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], [ 3.00000000e+00, 2.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00, 1.03000000e+00, 1.87045457e+00, 2.00000000e+01, 1.00000000e+00, 2.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], [ 4.00000000e+00, 1.00000000e+00, -8.00000000e-01, 2.90000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00, 1.02320534e+00, 1.95222438e+00, 2.00000000e+01, 1.00000000e+00, 2.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], [ 5.00000000e+00, 1.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00, 1.03000700e+00, 1.86983253e+00, 2.00000000e+01, 1.00000000e+00, 1.10000000e+00, 9.00000000e-01, 0.00000000e+00, 0.00000000e+00]]), 'gen': array([[ 0.00000000e+00, -6.74111530e+00, -7.14688297e+00, 0.00000000e+00, 0.00000000e+00, 1.02000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+09, -1.00000000e+09, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], [ 3.00000000e+00, 6.00000000e+00, 3.42190962e+00, 3.00000000e+00, -3.00000000e+00, 1.03000000e+00, nan, 1.00000000e+00, 1.00000000e+09, -1.00000000e+09, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00]]), 'branch': array([[ 0.00000000e+00+0.j , 1.00000000e+00+0.j , 4.95867769e-05+0.j , 1.19008264e-04+0.j , 5.47391104e+00+0.j , 2.50000000e+02+0.j , 2.50000000e+02+0.j , 2.50000000e+02+0.j , 1.00000000e+00+0.j , 0.00000000e+00+0.j , 1.00000000e+00+0.j , -3.60000000e+02+0.j , 3.60000000e+02+0.j , -6.74111530e+00+0.j , -7.14688297e+00+0.j , 6.74416214e+00+0.j , 1.45450502e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j ], [ 2.00000000e+00+0.j , 3.00000000e+00+0.j , 6.10000000e-04+0.j , 5.60000000e-04+0.j , 7.64035333e-02+0.j , 2.50000000e+02+0.j , 2.50000000e+02+0.j , 2.50000000e+02+0.j , 1.00000000e+00+0.j , 0.00000000e+00+0.j , 1.00000000e+00+0.j , -3.60000000e+02+0.j , 3.60000000e+02+0.j , -5.97238973e+00+0.j , -3.48162502e+00+0.j , 5.99999998e+00+0.j , 3.42634241e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j ], [ 3.00000000e+00+0.j , 5.00000000e+00+0.j , 5.19662500e-03+0.j , 3.25500000e-03+0.j , 4.17831823e-03+0.j , 2.50000000e+02+0.j , 2.50000000e+02+0.j , 2.50000000e+02+0.j , 1.00000000e+00+0.j , 0.00000000e+00+0.j , 1.00000000e+00+0.j , -3.60000000e+02+0.j , 3.60000000e+02+0.j , 2.40626638e-08+0.j , -4.43279288e-03+0.j , 5.75627463e-14+0.j , 3.11693679e-14+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j ], [ 4.00000000e+00+0.j , 2.00000000e+00+0.j , 7.62500000e-04+0.j , 7.00000000e-04+0.j , 9.55044167e-02+0.j , 2.50000000e+02+0.j , 2.50000000e+02+0.j , 2.50000000e+02+0.j , 1.00000000e+00+0.j , 0.00000000e+00+0.j , 1.00000000e+00+0.j , -3.60000000e+02+0.j , 3.60000000e+02+0.j , 8.00000000e-01+0.j , -2.90000000e+00+0.j , -7.93618189e-01+0.j , 2.80573774e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j ], [ 1.00000000e+00+0.j , 2.00000000e+00+0.j , 1.63923679e-04+0.j , 4.79726336e-03+0.j , -1.05000908e-02-0.01399964j, 2.50000000e+02+0.j , 2.50000000e+02+0.j , 2.50000000e+02+0.j , 1.00000000e+00+0.j , 0.00000000e+00+0.j , 1.00000000e+00+0.j , -3.60000000e+02+0.j , 3.60000000e+02+0.j , -6.74416214e+00+0.j , -4.54095852e-01+0.j , 6.76600792e+00+0.j , 6.75887284e-01+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j , 0.00000000e+00+0.j ]]), 'baseMVA': 1, 'V': array([1.02 +0.j , 1.02082934+0.00057751j, 1.02405525+0.03223318j, 1.0294512 +0.03361899j, 1.02261145+0.03485667j, 1.02945856+0.03360804j]), 'pv': array([3]), 'pq': array([1, 2, 4, 5]), 'ref': array([0]), 'Sbus': array([0. +0.j , 0. +0.j , 0. +0.j , 6. +0.j , 0.8-2.9j, 0. +0.j ])}, 'success': True, 'et': 1.1613223552703857, 'iterations': 3}
For information on how this datastructure is defined, please refer to the MATPOWER documentation.
Note: For linear power flow (DC load flow) 'Ybus' is no longer created, but 'Bbus' as a new 'internal' key.
pp.rundcpp(net)
net._ppc['internal']['Bbus'].A
array([[ 8402.77777778+0.j, -8402.77777778+0.j, 0. +0.j, 0. +0.j, 0. +0.j, 0. +0.j], [-8402.77777778+0.j, 8611.22995659+0.j, -208.45217882+0.j, 0. +0.j, 0. +0.j, 0. +0.j], [ 0. +0.j, -208.45217882+0.j, 3422.7378931 +0.j, -1785.71428571+0.j, -1428.57142857+0.j, 0. +0.j], [ 0. +0.j, 0. +0.j, -1785.71428571+0.j, 2092.93394777+0.j, 0. +0.j, -307.21966206+0.j], [ 0. +0.j, 0. +0.j, -1428.57142857+0.j, 0. +0.j, 1428.57142857+0.j, 0. +0.j], [ 0. +0.j, 0. +0.j, 0. +0.j, -307.21966206+0.j, 0. +0.j, 307.21966206+0.j]])
The nodal point admittance matrix is saved in the ppc and can be accessed directly:
pp.runpp(net)
net._ppc["internal"]["Ybus"].todense()
matrix([[ 2983.234714 -7157.02635809j, -2983.234714 +7159.76331361j, 0. +0.j , 0. +0.j , 0. +0.j , 0. +0.j ], [-2983.234714 +7159.76331361j, 2990.35626947-7364.28068082j, -7.11455564 +208.20907269j, 0. +0.j , 0. +0.j , 0. +0.j ], [ 0. +0.j , -7.11455564 +208.20907269j, 1608.40491554-1678.15899439j, -889.60186671 +816.68368091j, -711.68149336 +653.34694473j, 0. +0.j ], [ 0. +0.j , 0. +0.j , -889.60186671 +816.68368091j, 1027.81021222 -903.21268537j, 0. +0.j , -138.20834551 +86.56929539j], [ 0. +0.j , 0. +0.j , -711.68149336 +653.34694473j, 0. +0.j , 711.68149336 -653.29919252j, 0. +0.j ], [ 0. +0.j , 0. +0.j , 0. +0.j , -138.20834551 +86.56929539j, 0. +0.j , 138.20834551 -86.56720623j]])
Note that the nodal point admittance matrix is given in per unit values.
The jacobian Matrix J in the last iteration step is also stored in the ppc and can be accessed:
net._ppc["internal"]["J"].todense()
matrix([[ 9.54796437e+02, 0.00000000e+00, -8.62952855e+02, 0.00000000e+00, -9.18435819e+01, 0.00000000e+00, -9.15296717e+02, 0.00000000e+00, -1.42353628e+02], [ 0.00000000e+00, 7.67426447e+03, -2.17432759e+02, 0.00000000e+00, 0.00000000e+00, 3.05264392e+03, -1.38258923e+01, 0.00000000e+00, 0.00000000e+00], [-8.60737361e+02, -2.17892547e+02, 1.76161072e+03, -6.82980812e+02, 0.00000000e+00, -6.95203313e-01, 1.64791123e+03, -7.30904816e+02, 0.00000000e+00], [ 0.00000000e+00, 0.00000000e+00, -6.86871047e+02, 6.86871047e+02, 0.00000000e+00, 0.00000000e+00, -7.26450958e+02, 7.28978162e+02, 0.00000000e+00], [-9.18403982e+01, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 9.18403982e+01, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.42355564e+02], [ 0.00000000e+00, -3.11622898e+03, 1.41654896e+01, 0.00000000e+00, 0.00000000e+00, 7.51767502e+03, -2.12220120e+02, 0.00000000e+00, 0.00000000e+00], [ 9.39812512e+02, 7.09684055e-01, -1.68838791e+03, 7.47865712e+02, 0.00000000e+00, -2.13446561e+02, 1.71937864e+03, -6.67491446e+02, 0.00000000e+00], [ 0.00000000e+00, 0.00000000e+00, 7.44294349e+02, -7.44294349e+02, 0.00000000e+00, 0.00000000e+00, -6.70404297e+02, 6.65624993e+02, 0.00000000e+00], [ 1.46627228e+02, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, -1.46627228e+02, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 8.91648289e+01]])
The jacobian matrix is also given in per unit values.
The pandapower indices are not equal to the ppc indices for several reasons. Some buses are fused together in case of closed bus-bus switches and auxiliary buses are created for elements like extended wards or three winding transformers. See here for more details: https://pandapower.readthedocs.io/en/latest/elements/switch.html
There is however a mapping between pandapower indices and ppc indices that is created during the conversion to keep track of the dependencies that is also stored in the net:
net._pd2ppc_lookups["bus"]
array([0, 1, 1, 2, 2, 3, 4])
To get a ppc index from the pandapower index, simply call the lookup like this:
pandapower_bus_idx = 3
ppc_index = net._pd2ppc_lookups["bus"][pandapower_bus_idx]
print(ppc_index)
2
As you can see, pandapower bus index 3 corresponds to ppc bus index 2. So if we would like to find the diagonal entry of the Ybus matrix for bus 2, we could now access it with that internal index:
Ybus = net._ppc["internal"]["Ybus"]
int_idx = net._pd2ppc_lookups["bus"][ppc_index]
Ybus[int_idx, int_idx]
(2990.356269470474-7364.280680821408j)
We can also see that some buses are mapped to the same internal bus, such as bus 1 and bus 2:
print(net._pd2ppc_lookups["bus"][1])
print(net._pd2ppc_lookups["bus"][2])
1 1
That is because buses 1 and 2 are connected by a closed bus-bus switch and are therefore represented internally as the same bus:
net.switch.loc[0]
bus 1 element 2 et b type CB closed True name None z_ohm 0 Name: 0, dtype: object
The pandapower indices are not equal to the ppc indices for several reasons. Some buses are fused together in case of closed bus-bus switches and auxiliary buses are created for elements like extended wards or three winding transformers. There is however a mapping between pandapower indices and ppc indices that is created during the conversion to keep track of the dependencies that is also stored in the net:
As an example we show how to obtain the Jacobian entries of generator buses using the pandapower -> ppc bus mapping. First we get buses of the in-service generators:
gen_buses = net.gen.loc[net.gen.in_service.values, "bus"].values
print(f"pandapower gen bus: {gen_buses}")
pandapower gen bus: [5]
Second, we geht the Jacobian matrix:
J = net._ppc["internal"]["J"]
print(f"Jacobian shape: {J.shape}")
Jacobian shape: (9, 9)
Why has the Jacobian the shape 9x9? It consists of the partial derivatives J11 = dP_dVa, J12 = dP_Vm, J21 = dQ_dVa, J22 = dQ_dVm. Except the reference bus, all PV- and PQ-buses are included in J. Vm is constant for PV nodes and dS/dVm is 0 for PV-buses (gens in pandapower) and Q is a variable.
In our case we have 1 reference bus (at bus 0), 1 gen at bus 5, and 3 pq buses (at buses 1, 2, 4)
This is the reason why J11 to J22 have these shapes:
J11 = pvpq x pvpq (dP_dVa)
J12 = pvpq x pq (dP_dVm)
J21 = pq x pvpq (dQ_dVa)
J22 = pq x pq (dQ_dVm)
Only J11 contains values relevant for gens.
bus_lookup = ppc_index = net._pd2ppc_lookups["bus"]
print(f"pandapower to ppc lookup: {bus_lookup}")
ppc_gen_buses = bus_lookup[gen_buses]
print(f"pandapower gen bus: {gen_buses} maps to ppc gen bus: {ppc_gen_buses}")
pandapower to ppc lookup: [0 1 1 2 2 3 4] pandapower gen bus: [5] maps to ppc gen bus: [3]
Now, we need the pv and pq entries in J to obtain the Jacobian sub-matrices:
import numpy as np
# get pv and pq values from newtonpf()
pv = net._ppc["internal"]["pv"]
pq = net._ppc["internal"]["pq"]
# stack these as done in newtonpf()
pvpq = np.hstack((pv, pq))
print("pv and pq nodes as in the newtonpf() function")
print(f"pv buses: {pv}\npq buses: {pq}\npvpq buses: {pvpq}")
# get len of pv and pq
n_pvpq = len(pvpq)
n_pq = len(pq)
n_pv = len(pv)
# get J11, J12, J21, and J22
j11 = J[:n_pvpq, :n_pvpq]
j12 = J[:n_pvpq, n_pvpq:]
j21 = J[n_pvpq:, :n_pvpq]
j22 = J[n_pvpq:, n_pvpq:]
print("shape of J sub-matrices:")
print(f"j11 = {j11.shape}")
print(f"j12 = {j12.shape}")
print(f"j21 = {j21.shape}")
print(f"j22 = {j22.shape}")
pv and pq nodes as in the newtonpf() function pv buses: [3] pq buses: [1 2 4 5] pvpq buses: [3 1 2 4 5] shape of J sub-matrices: j11 = (5, 5) j12 = (5, 4) j21 = (4, 5) j22 = (4, 4)
Now, we finally get the generator entries in J:
# j11 gen entries
m = np.isin(pvpq, pv)
n = m
j11_gen_entries = j11[m, n]
print(f"J11 indices: m = {m}, n = {n}")
print(f"pandapower gen {gen_buses} entries (ppc PV nodes {ppc_gen_buses}) in J11 (=dP/dVa): {j11_gen_entries}")
J11 indices: m = [ True False False False False], n = [ True False False False False] pandapower gen [5] entries (ppc PV nodes [3]) in J11 (=dP/dVa): [[954.7964366]]
You can easily visualize the matrices with matplotlib and the plot function spy(). Let's look at an example.
First, we define a plot function to visiualize the sparse Ybus matrix (works also with any other matrix like J) including some labels:
import matplotlib.pyplot as mpl
import numpy as np
import pandapower as pp
import pandapower.networks as nw
import pandapower.plotting as plt
from pandapower.plotting import get_collection_sizes
def plot_ybus(net=None, ax=None, ybus=None):
if ax is None:
fig, ax = mpl.subplots(1, 2)
if ybus is None:
ybus = net._ppc["internal"]["Ybus"]
ax.spy(ybus)
ax.set_title("Ybus shape {}\n".format(str(ybus.shape)))
ax.set_xticks(np.arange(ybus.shape[0]))
ax.set_xticklabels(np.arange(ybus.shape[0]))
ax.set_yticklabels(np.arange(ybus.shape[1]))
ax.set_yticks(np.arange(ybus.shape[1]))
ax.grid(which="both", linestyle="dotted")
Second, we define a function to plot the power system including some bus labels and other collections:
def plot_net(net, ax=None):
if ax is None:
fig, ax = mpl.subplots(1, 1, figsize=(10, 8))
sizes = get_collection_sizes(net)
# create collections for elements
collections = list()
collections.append(plt.create_bus_collection(net, size=sizes["bus"]))
collections.append(plt.create_line_collection(net, use_bus_geodata=True))
collections.append(plt.create_trafo_collection(net, size=sizes["trafo"]))
collections.append(plt.create_ext_grid_collection(net, size=sizes["ext_grid"], orientation=1.5))
collections.append(plt.create_bus_bus_switch_collection(net, size=sizes["switch"]))
collections.append(
plt.create_line_switch_collection(net, distance_to_bus=sizes["switch_distance"], size=sizes["switch"]))
collections.append(plt.create_load_collection(net, size=sizes["load"]))
# add labels for each bus
for idx in net.bus_geodata.index:
x = net.bus_geodata.loc[idx, "x"]
y = net.bus_geodata.loc[idx, "y"] + sizes["bus"] * 1.
ax.text(x, y, str(idx), fontsize=12, color="r")
plt.draw_collections(collections, ax=ax)
mpl.tight_layout()
Third, we create a plot function with three subplots. The first subplots shows the power system including the bus labels. The second and third plot shows the Ybus matrix with different settings. If you just call runpp() the buses are fused internally and the Ybus matrix has a lower dimension. You can also use the parameter r_switch
to set impedance values for switches. In this case, the buses are not fused and the Ybus matrix has a higher dimension. This also changes your power flow result.
def plot_overview(net):
fig, axes = mpl.subplots(1, 3, figsize=(16, 8))
pp.runpp(net)
plot_net(net, ax=axes[0])
# plot y bus with max. const shape (no fused switches)
pp.runpp(net, check_connectivity=False, r_switch=0.1, init="flat", neglect_open_switch_branches=True)
plot_ybus(net, ax=axes[1])
# plot ybus for power flow (with fused switches)
pp.runpp(net)
plot_ybus(net, ax=axes[2])
mpl.show()
Finally, we call the overview function with the cigre example power system:
net = nw.create_cigre_network_mv()
plot_overview(net)
Each dot in the spy plot is ea