pandapower最优潮流计算tutorial

pandapower最优潮流计算[pandapower Optimal Power Flow]

This is an introduction into the usage of the pandapower optimal power flow. It shows how to set the constraints and the cost factors into the pandapower element tables.

该notebook主要是介绍pandapower最优潮流计算的使用。它展示了如何将约束和成本因子设置进入pandapower的元件列表。

示例网络[Example Network]

We use the following four bus example network for this tutorial:

我们的教程使用下图的4节点示例网络:

example_opf.png

We first create this network in pandapower:

我们首先在pandapower中创建网络:

import pandapower as pp
import numpy as np
net = pp.create_empty_network()

#create buses
bus1 = pp.create_bus(net, vn_kv=220.)
bus2 = pp.create_bus(net, vn_kv=110.)
bus3 = pp.create_bus(net, vn_kv=110.)
bus4 = pp.create_bus(net, vn_kv=110.)

#create 220/110 kV transformer
pp.create_transformer(net, bus1, bus2, std_type="100 MVA 220/110 kV")

#create 110 kV lines
pp.create_line(net, bus2, bus3, length_km=70., std_type='149-AL1/24-ST1A 110.0')
pp.create_line(net, bus3, bus4, length_km=50., std_type='149-AL1/24-ST1A 110.0')
pp.create_line(net, bus4, bus2, length_km=40., std_type='149-AL1/24-ST1A 110.0')

#create loads
pp.create_load(net, bus2, p_kw=60e3, controllable = False)
pp.create_load(net, bus3, p_kw=70e3, controllable = False)
pp.create_load(net, bus4, p_kw=10e3, controllable = False)

#create generators
eg = pp.create_ext_grid(net, bus1, min_p_kw = -1e9, max_p_kw = 1e9)
g0 = pp.create_gen(net, bus3, p_kw=-80*1e3, min_p_kw=-80e3, max_p_kw=0,vm_pu=1.01, controllable=True)
g1 = pp.create_gen(net, bus4, p_kw=-100*1e3, min_p_kw=-100e3, max_p_kw=0, vm_pu=1.01, controllable=True)

网损最小化[Loss Minimization]

We specify the same costs for the power at the external grid and all generators to minimize the overall power feed in. This equals an overall loss minimization:

我们特别指出外部电网和所有的发电机具有相同的电能成本,并最小化总的电能供应。这相当于整体网损的最小化。

costeg = pp.create_polynomial_cost(net, 0, 'ext_grid', np.array([-1, 0]))
costgen1 = pp.create_polynomial_cost(net, 0, 'gen', np.array([-1, 0]))
costgen2 = pp.create_polynomial_cost(net, 1, 'gen', np.array([-1, 0]))
print(costeg, costgen1, costgen2)
0 1 2

pp.create_polynomial_cost()为元件的多项式成本创建一个条目,返回已经创建的成本条目的ID

We run an OPF:

我们运行一个最优潮流计算:

pp.runopp(net, verbose=True)
The OPF cost definition has changed! Please check out the tutorial 'opf_changes-may18.ipynb' or the documentation!


PYPOWER Version 5.1.4, 27-June-2018 -- AC Optimal Power Flow
Python Interior Point Solver - PIPS, Version 1.0, 07-Feb-2011
Converged!

let's check the results:

让我们看下结果:

print(net.res_ext_grid)
           p_kw       q_kvar
0 -56530.125753 -1974.470643
print(net.res_gen)
           p_kw       q_kvar  va_degree     vm_pu
0 -71313.391997  1969.654771  -3.712810  1.000009
1 -12299.771985  1451.159898  -3.712777  1.000010

Since all costs were specified the same, the OPF minimizes overall power generation, which is equal to a loss minimization in the network. The loads at buses 3 and 4 are supplied by generators at the same bus, the load at Bus 2 is provided by a combination of the other generators so that the power transmission leads to minimal losses.

因为所有的成本都看成相同的,最优潮流计算最小化总的发电量后的结果和网络的损失最小化结果相同。母线3和母线4的负荷由各自母线上的发电机供应,而母线2上的负荷是由其他发电机联合供应因此电能的传输达到了最小的损失。

个体发电机成本[Individual Generator Costs]

Let's now assign individual costs to each generator.

现在给每台发电机赋予各自的发电成本。

We assign a cost of 10 ct/kW for the external grid, 15 ct/kw for the generator g0 and 12 ct/kw for generator g1:

我们给外部电网赋予10ct/kW的成本,发电机g0赋予15ct/kW的成本以及发电机g1赋予12ct/kW的成本:

net.polynomial_cost.c.at[costeg] = np.array([[-0.1, 0]])
net.polynomial_cost.c.at[costgen1] = np.array([[-0.15, 0]])
net.polynomial_cost.c.at[costgen2] = np.array([[-0.12, 0]])

And now run an OPF:

现在运行最优潮流运算:

pp.runopp(net, verbose=True)
The OPF cost definition has changed! Please check out the tutorial 'opf_changes-may18.ipynb' or the documentation!


PYPOWER Version 5.1.4, 27-June-2018 -- AC Optimal Power Flow
Python Interior Point Solver - PIPS, Version 1.0, 07-Feb-2011
Converged!
print(net.res_ext_grid)
            p_kw       q_kvar
0 -144559.444871 -9193.066456
print(net.res_gen)
       p_kw        q_kvar  va_degree     vm_pu
0 -0.011910  -8601.802715 -16.426869  0.967619
1 -0.033788 -10594.678350 -13.481042  0.989756

We can see that all active power is provided by the external grid. This makes sense, because the external grid has the lowest cost of all generators and we did not define any constraints.

我们可以看出所有的有功都是由外部电网提供。这是合理的,因为外部电网拥有最低的发电成本,而且我们没有定义任何的约束。

The dispatch costs are given in net.res_cost:

dispatch成本可以由net.res_cost给出:

print(net.res_cost)
14455.950328285478

变压器约束[Transformer Constraint]

Since all active power comes from the external grid and subsequently flows through the transformer, the transformer is overloaded with a loading of about 145%:

因为所有的有功功率都来自与外部电网,接着潮流流过了变压器,变压器过载运行,负载约为145%:

print(net.res_trafo)
         p_hv_kw    q_hv_kvar        p_lv_kw     q_lv_kvar       pl_kw  \
0  144559.444871  9193.066457 -143959.516332  15993.742384  599.928539   

        ql_kvar   i_hv_ka   i_lv_ka  loading_percent  
0  25186.808841  0.380136  0.759988        144.85146  

We now limit the transformer loading to 50%:

现在设置变压器的负载约束为50%:

net.trafo["max_loading_percent"] = 50

(the max_loading_percent parameter can also be specified directly when creating the transformer)
and run the OPF:

(max_loading_percent参数也可以在创建变压器时直接指定)
然后运行最优潮流计算:

pp.runopp(net)
The OPF cost definition has changed! Please check out the tutorial 'opf_changes-may18.ipynb' or the documentation!

We can see that the transformer complies with the maximum loading:

我们可以看到变压器以最大负荷运行:

print(net.res_trafo)
        p_hv_kw    q_hv_kvar       p_lv_kw    q_lv_kvar       pl_kw  \
0  49953.864488 -2147.313884 -49833.813812  5167.453489  120.050676   

       ql_kvar   i_hv_ka   i_lv_ka  loading_percent  
0  3020.139605  0.131216  0.262153        49.999995  

And power generation is now split between the external grid and generator 1 (which is the second cheapest generation unit):

现在有功功率的产生主要来自于外部电网和发电机g1(g1是发电成本第二低的发电机组)

print(net.res_ext_grid)
           p_kw       q_kvar
0 -49953.864488  2147.313884
print(net.res_gen)
           p_kw       q_kvar  va_degree     vm_pu
0     -0.005754 -2993.145845  -6.232829  0.985230
1 -93304.077572 -3453.343461  -1.237884  1.025709

This comes of course with an increase in dispatch costs:

这样的结果就导致了dispatch成本的上升:

print(net.res_cost)
16191.876620556373

线路负载约束[Line Loading Constraints]

We now look at the line loadings:

我们现在关注于线路负载:

print(net.res_line)
      p_from_kw  q_from_kvar       p_to_kw    q_to_kvar        pl_kw  \
0  19780.009515 -2479.435419 -19341.695290  1104.393239   438.314225   
1 -50658.298957  1888.752606  52783.697109   921.064101  2125.398152   
2  30520.380464  2532.279360 -29946.195703 -2688.018070   574.184761   

       ql_kvar  i_from_ka   i_to_ka      i_ka  loading_percent  
0 -1375.042180   0.104309  0.103207  0.104309        22.193320  
1  2809.816707   0.270061  0.270140  0.270140        57.476548  
2  -155.738710   0.156712  0.157323  0.157323        33.472994  

and run the OPF with a 50% loading constraint:

然后在线路50%负载水平条件下运行最优潮流计算:

net.line["max_loading_percent"] = 50
pp.runopp(net, verbose=True)
The OPF cost definition has changed! Please check out the tutorial 'opf_changes-may18.ipynb' or the documentation!


PYPOWER Version 5.1.4, 27-June-2018 -- AC Optimal Power Flow
Python Interior Point Solver - PIPS, Version 1.0, 07-Feb-2011
Converged!

Now the line loading constraint is complied with:

现在线路的负载约束编译后:

print(net.res_line)
      p_from_kw  q_from_kvar       p_to_kw    q_to_kvar        pl_kw  \
0  16727.519781 -3194.974086 -16412.725022  1534.133994   314.794759   
1 -44451.195382   868.876229  46059.937188   830.815836  1608.741806   
2  27533.080431  4051.080417 -27060.140775 -4429.078165   472.939657   

       ql_kvar  i_from_ka   i_to_ka      i_ka  loading_percent  
0 -1660.840092   0.088849  0.087130  0.088849        18.903945  
1  1699.692065   0.234999  0.235000  0.235000        49.999998  
2  -377.997748   0.141964  0.143057  0.143057        30.437636  

And all generators are involved in supplying the loads:

所有的发电机都开始供应负荷:

print(net.res_ext_grid)
          p_kw       q_kvar
0 -49787.59354  4603.760721
print(net.res_gen)
           p_kw       q_kvar  va_degree     vm_pu
0  -9136.079301 -2403.009062  -5.814954  0.992994
1 -83593.017290 -4881.895570  -1.511656  1.028899

This of course comes with a once again rising dispatch cost:

这导致了dispatch成本又一次的提升:

print(net.res_cost)
16380.333324014753

电压约束[Voltage Constraints]

Finally, we have a look at the bus voltage:

最后,我们在关注一下电压:

print(net.res_bus)
      vm_pu  va_degree          p_kw       q_kvar       lam_p         lam_q
0  1.000000   0.000000 -49787.593540  4603.760721  100.000000 -2.642153e-21
1  1.006025  -3.408832  60000.000000     0.000000  130.952263 -5.410268e-01
2  0.992994  -5.814954  60863.920699 -2403.009062  149.999973  1.192009e-21
3  1.028899  -1.511656 -73593.017290 -4881.895570  120.000014  2.988530e-21

and constrain it:

加以约束,并运行最优潮流计算:

net.bus["min_vm_pu"] = 1.0
net.bus["max_vm_pu"] = 1.02
pp.runopp(net)
The OPF cost definition has changed! Please check out the tutorial 'opf_changes-may18.ipynb' or the documentation!

We can see that all voltages are within the voltage band:

print(net.res_bus)
      vm_pu  va_degree          p_kw        q_kvar       lam_p         lam_q
0  1.000000   0.000000 -49906.847219   3050.617054  100.000000 -6.408104e-22
1  1.004168  -3.421015  60000.000000      0.000000  131.268596 -2.133663e-01
2  1.000000  -5.976094  59278.204655 -14858.933373  149.999992  3.120534e-21
3  1.020000  -1.366892 -71863.491321   9172.691847  120.000005 -1.926105e-21

And all generators are once again involved in supplying the loads:

所有的发电机又一次都参与了负荷的供应:

print(net.res_ext_grid)
           p_kw       q_kvar
0 -49906.847219  3050.617054
print(net.res_gen)
           p_kw        q_kvar  va_degree  vm_pu
0 -10721.795345 -14858.933373  -5.976094   1.00
1 -81863.491321   9172.691847  -1.366892   1.02

This of course comes once again with rising dispatch costs:

又一次导致了dipatch成本的提升:

print(net.res_cost)
16422.572982172846

直流最优潮流计算[DC OPF]

pandapower also provides the possibility of running a DC Optimal Power Flow:

pandapower也可以提供了运行直流最优潮流计算的可能:

pp.rundcopp(net)

Since voltage magnitudes are not included in the DC power flow formulation, voltage constraints canot be considered in the DC OPF:

因为直流潮流公式中不考虑电压幅值,所以在直流潮流计算中并不考虑电压约束:

print(net.res_bus)
   vm_pu  va_degree          p_kw  q_kvar       lam_p  lam_q
0    NaN   0.000000 -49999.999965     NaN  100.000000    0.0
1    NaN  -3.436967  60000.000000     NaN  130.909091    0.0
2    NaN  -5.708566  61488.746680     NaN  150.000000    0.0
3    NaN  -1.362340 -71488.746715     NaN  120.000000    0.0

Line and transformer loading limits are however complied with:

线路和变压器负载限制编译后为:

print(net.res_line)
      p_from_kw  q_from_kvar       p_to_kw  q_to_kvar  pl_kw  ql_kvar  \
0  16715.233329          0.0 -16715.233329        0.0    0.0      0.0   
1 -44773.513351          0.0  44773.513351        0.0    0.0      0.0   
2  26715.233363          0.0 -26715.233363        0.0    0.0      0.0   

   i_from_ka   i_to_ka      i_ka  loading_percent  
0   0.087732  0.087732  0.087732        18.666430  
1   0.235000  0.235000  0.235000        50.000000  
2   0.140219  0.140219  0.140219        29.833747  
print(net.res_trafo)
        p_hv_kw  q_hv_kvar       p_lv_kw  q_lv_kvar  pl_kw  ql_kvar   i_hv_ka  \
0  49999.999965        0.0 -49999.999965        0.0    0.0      0.0  0.131216   

    i_lv_ka  loading_percent  
0  0.262432             50.0  

As are generator limits:

发电机限制为:

print(net.gen)
   name  bus      p_kw  vm_pu  sn_kva  min_q_kvar  max_q_kvar  scaling  \
0  None    2  -80000.0   1.01     NaN         NaN         NaN      1.0   
1  None    3 -100000.0   1.01     NaN         NaN         NaN      1.0   

   in_service  type  min_p_kw  max_p_kw  controllable  
0        True  None  -80000.0       0.0          True  
1        True  None -100000.0       0.0          True  
print(net.res_gen)
           p_kw  q_kvar  va_degree  vm_pu
0  -8511.253320     NaN  -5.708566    1.0
1 -81488.746715     NaN  -1.362340    1.0

The cost function is the same for the linearized OPF as for the non-linear one:

线性最优潮流计算和非线性潮流计算的成本函数一致:

print(net.res_cost)
16055.337600298368

Piecewise linear cost functions
The OPF also offers us piecewise linear cost functions. Let us first check the actual cost function setup:

分段线性成本函数
最优潮流九三也提供了分段线性成本函数。我们首先看一下实际的成本函数设置:

print(net.polynomial_cost)
  type element element_type               c
0    p       0     ext_grid   [[-0.1, 0.0]]
1    p       0          gen  [[-0.15, 0.0]]
2    p       1          gen  [[-0.12, 0.0]]

An element can either have polynomial costs or piecewise linear costs at the same time. So let us first delete the polynomial costs in order to avoid confusion and errors:

一个元件的成本函数只能是多项式或者分段线性中的一种。因此,让我们首先删除多项式成本函数,以免产生困惑和错误:

net.polynomial_cost= net.polynomial_cost.drop(net.polynomial_cost.index.values)

The results above have been produced with polynomial cost functions, that were linear. Let's try to reproduce the results using piecewise linear cost functions. Note: The cost functions need to have the same gradient!

上面的计算结果是采用多项式成本函数计算的,是线性的。现在我们使用分段线性函数重新计算结果。需要强调的是:成本函数需要拥有相同的梯度!

pp.create_piecewise_linear_cost(net, 0, "gen", np.array([[-1 , 0.15] ,[0, 0]]))
pp.create_piecewise_linear_cost(net, 1, "gen", np.array([[-1, 0.12], [0, 0]]))
pp.create_piecewise_linear_cost(net, 0, "ext_grid", np.array([[-1, 0.1], [0, 0]]))
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

 in 
----> 1 pp.create_piecewise_linear_cost(net, 0, "gen", np.array([[-1 , 0.15] ,[0, 0]]))
      2 pp.create_piecewise_linear_cost(net, 1, "gen", np.array([[-1, 0.12], [0, 0]]))
      3 pp.create_piecewise_linear_cost(net, 0, "ext_grid", np.array([[-1, 0.1], [0, 0]]))


~/.local/lib/python3.5/site-packages/pandapower/create.py in create_piecewise_linear_cost(net, element, element_type, data_points, type, index)
   2469             if not (net[element_type].max_p_kw.at[element] <= max(p) and
   2470                     net[element_type].min_p_kw.at[element] >= min(p)):
-> 2471                 raise ValueError("Cost function must be defined for whole power range of the "
   2472                                  "generator")
   2473         if type == "q":


ValueError: Cost function must be defined for whole power range of the generator

What we forgot is that the piecewise linear function should be defined for the whole range of the generator. The range is determined by p_max and p_min. Let's check:

额...错误信息是ValueError: Cost function must be defined for whole power range of the generator,让我们检查一下:

print(net.gen.max_p_kw)
0    0.0
1    0.0
Name: max_p_kw, dtype: float64
print(net.gen.min_p_kw)
0    -80000.0
1   -100000.0
Name: min_p_kw, dtype: float64

We try again:

我们再试一下:

pp.create_piecewise_linear_cost(net, 0, "gen", np.array([[-80000* 1 , 80000*0.15], [0, 0]]))
pp.create_piecewise_linear_cost(net, 1, "gen", np.array([[-100000*1, 100000*0.12], [0, 0]]))
1

An external grid usually has no operational limits, but this is a problem for the OPF:

外部电网通常没有运行限制,但这是OPF的一个问题:

pp.create_piecewise_linear_cost(net, 0, "ext_grid", np.array([[0, 0], [1, 0.1]]))
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

 in 
----> 1 pp.create_piecewise_linear_cost(net, 0, "ext_grid", np.array([[0, 0], [1, 0.1]]))


~/.local/lib/python3.5/site-packages/pandapower/create.py in create_piecewise_linear_cost(net, element, element_type, data_points, type, index)
   2469             if not (net[element_type].max_p_kw.at[element] <= max(p) and
   2470                     net[element_type].min_p_kw.at[element] >= min(p)):
-> 2471                 raise ValueError("Cost function must be defined for whole power range of the "
   2472                                  "generator")
   2473         if type == "q":


ValueError: Cost function must be defined for whole power range of the generator

So we set imaginary constraints, that we can choose very broad:

因此,我们虚构一个约束,将这个约束的范围设置的很广:

net.ext_grid["max_p_kw"] = 1e9
net.ext_grid["min_p_kw"] = -1e9
print(net.ext_grid)
   name  bus  vm_pu  va_degree  in_service      min_p_kw      max_p_kw
0  None    0    1.0        0.0        True -1.000000e+09  1.000000e+09
pp.create_piecewise_linear_cost(net, 0, "ext_grid", np.array([[-1e9, 1e9*.1], [1e9, -1e9*0.1]]))
2

Let us check the results from the previous OPF again!

让我们再看一下之前的OPF运行结果:

print(net.res_bus)
   vm_pu  va_degree          p_kw  q_kvar       lam_p  lam_q
0    NaN   0.000000 -49999.999965     NaN  100.000000    0.0
1    NaN  -3.436967  60000.000000     NaN  130.909091    0.0
2    NaN  -5.708566  61488.746680     NaN  150.000000    0.0
3    NaN  -1.362340 -71488.746715     NaN  120.000000    0.0
print(net.res_cost)
16055.337600298368

We run the same OPF now with different cost function setup. We should get the exact same results:

我们运行了相同的OPF但是采用不同的成本函数,我们应该获得完全相同的结果:

pp.rundcopp(net)
print(net.res_cost)
16055.337600298368
print(net.res_bus)
   vm_pu  va_degree          p_kw  q_kvar       lam_p  lam_q
0    NaN   0.000000 -49999.999965     NaN  100.000000    0.0
1    NaN  -3.436967  60000.000000     NaN  130.909091    0.0
2    NaN  -5.708566  61488.746680     NaN  150.000000    0.0
3    NaN  -1.362340 -71488.746715     NaN  120.000000    0.0

你可能感兴趣的:(pandapower最优潮流计算tutorial)