├── PowerData.xlsx ├── README.md ├── network-reconfiguration.py └── readData.py /PowerData.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sometimesstudy/distribution-network-reconfiguration/b8fb09200f05d7a598c2de220dc4f98122a965f2/PowerData.xlsx -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Distribution-Network-Reconfiguration 2 | Distribution network dynamic reconfiguration using spanning trees and Distflow, the objective is to minimize the power losses between lines. The distribution network radial constraints are programmed according to "R. A. Jabr, R. Singh and B. C. Pal, 'Minimum Loss Network Reconfiguration Using Mixed-Integer Convex Programming,' IEEE Transactions on Power Systems, vol. 27, no. 2, pp. 1106-1115, May 2012, doi: 10.1109/TPWRS.2011.2180406." 3 | 4 | Feel free to contact me (wyx_coder@foxmail.com) if you have any quetions. 5 | 6 | Reconfiguration result 7 | 8 | ![image](https://user-images.githubusercontent.com/51228607/228150710-83714555-5f8b-437d-ba72-9511d124d2fc.png) 9 | 10 | 11 | 基于Spanning Trees(生成树)的配电网动态优化重构方法,潮流约束采用Distflow,以最小化线路功率损耗为优化目标,基于生成树的配电网辐射约束根据"R. A. Jabr, R. Singh and B. C. Pal, 'Minimum Loss Network Reconfiguration Using Mixed-Integer Convex Programming,' IEEE Transactions on Power Systems, vol. 27, no. 2, pp. 1106-1115, May 2012, doi: 10.1109/TPWRS.2011.2180406."这篇论文编写 12 | -------------------------------------------------------------------------------- /network-reconfiguration.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Mar 27 19:54:58 2023 4 | 5 | @author: wyx 6 | """ 7 | #%% 定义变量 8 | from gurobipy import * 9 | from readData import * 10 | import networkx as nx 11 | model = Model('distflow') 12 | # DG出力 13 | P_dg_it = model.addVars(dgNode,T_set,name='P_dg_it') 14 | Q_dg_it = model.addVars(dgNode,T_set,name='Q_dg_it') 15 | # 外部购电 16 | P_buy_it = model.addVars(buy_node,T_set,lb=-GRB.INFINITY,name='P_buy_it') 17 | Q_buy_it = model.addVars(buy_node,T_set,lb=-GRB.INFINITY,name='Q_buy_it') 18 | # 支路电流 19 | L_ijt = model.addVars(branch_ij,T_set,ub=I_ijmax,name='L_ijt') 20 | # 节点电压 21 | v_it = model.addVars(B_set,T_set,lb=vmin,ub=vmax,name='v_it') 22 | # 支路有功 23 | P_ijt = model.addVars(branch_ij, T_set,lb=-GRB.INFINITY,name='P_ijt') 24 | # 支路无功 25 | Q_ijt = model.addVars(branch_ij, T_set,lb=-GRB.INFINITY,name='Q_ijt') 26 | # 支路开断 27 | alpha_ij = model.addVars(branch_ij,T_set,lb=0,ub=1,name='alpha') 28 | beta_ij = model.addVars(branch_ij,T_set,vtype=GRB.BINARY,name='beta_ij') 29 | beta_ji = model.addVars(branch_ji,T_set,vtype=GRB.BINARY,name='beta_ji') 30 | #%% 约束 31 | # DG出力约束 32 | model.addConstrs((P_dg_it[i,t]<=dgPmax_it[i] for i in dgNode for t in T_set),name='DGPmax') 33 | model.addConstrs((P_dg_it[i,t]>=dgPmin_it[i] for i in dgNode for t in T_set),name='DGPmin') 34 | model.addConstrs((Q_dg_it[i,t]<=dgQmax_it[i] for i in dgNode for t in T_set),name='DGQmax') 35 | model.addConstrs((Q_dg_it[i,t]>=dgQmin_it[i] for i in dgNode for t in T_set),name='DGQmin') 36 | # 潮流方程 37 | # DG节点 38 | model.addConstrs((P_dg_it[i,t]-P_in_it[i,t]==quicksum(P_ijt[i,j,t] for j in Ninsert_set[i]) 39 | - quicksum(P_ijt[k,i,t] - r_ij[k,i]*L_ijt[k,i,t] for k in Nout_set[i]) 40 | for t in T_set for i in dgNode ),name='nodePbalance1') 41 | model.addConstrs((Q_dg_it[i,t]-Q_in_it[i,t]==quicksum(Q_ijt[i,j,t] for j in Ninsert_set[i]) 42 | - quicksum(Q_ijt[k,i,t] - x_ij[k,i]*L_ijt[k,i,t]for k in Nout_set[i]) 43 | for t in T_set for i in dgNode ),name='nodeQbalance2') 44 | # 普通节点 45 | model.addConstrs((-P_in_it[i,t]==quicksum(P_ijt[i,j,t] for j in Ninsert_set[i] if (i,j) in branch_ij) 46 | - quicksum(P_ijt[k,i,t] - r_ij[k,i]*L_ijt[k,i,t] for k in Nout_set[i]if (k,i) in branch_ij) 47 | for t in T_set for i in comment_set[1:]) ,name='nodePbalance3') 48 | model.addConstrs((-Q_in_it[i,t]==quicksum(Q_ijt[i,j,t] for j in Ninsert_set[i]) 49 | - quicksum(Q_ijt[k,i,t]- x_ij[k,i]*L_ijt[k,i,t] for k in Nout_set[i]) 50 | for t in T_set for i in comment_set[1:]),name='nodeQbalance4') 51 | # 对外购电 52 | model.addConstrs((P_buy_it[i,t]-P_in_it[i,t]==quicksum(P_ijt[i,j,t] for j in Ninsert_set[i]) 53 | - quicksum(P_ijt[k,i,t]- r_ij[k,i]*L_ijt[k,i,t] for k in Nout_set[i]) 54 | for t in T_set for i in buy_node ),name='firstnode1') 55 | model.addConstrs((Q_buy_it[i,t]-P_in_it[i,t]==quicksum(Q_ijt[i,j,t] for j in Ninsert_set[i]) 56 | - quicksum(Q_ijt[k,i,t] - x_ij[k,i]*L_ijt[k,i,t] for k in Nout_set[i]) 57 | for t in T_set for i in buy_node ),name='firstnode2') 58 | # 电压约束 59 | model.addConstrs((v_it[j,t]<=bigM*(1-alpha_ij[i,j,t])+v_it[i,t]-2*(r_ij[i,j]*P_ijt[i,j,t] 60 | +x_ij[i,j]*Q_ijt[i,j,t]+(r_ij[i,j]**2+x_ij[i,j]**2)*L_ijt[i,j,t] ) for (i,j) in 61 | branch_ij for t in T_set),name='V-2') 62 | model.addConstrs((v_it[j,t]>=-bigM*(1-alpha_ij[i,j,t])+v_it[i,t]-2*(r_ij[i,j]*P_ijt[i,j,t] 63 | +x_ij[i,j]*Q_ijt[i,j,t]+(r_ij[i,j]**2+x_ij[i,j]**2)*L_ijt[i,j,t] ) for (i,j) in 64 | branch_ij for t in T_set),name='V-3') 65 | model.addConstrs((v_it[0,t]==1 for t in T_set),name='balancenode') 66 | # 电流约束 67 | model.addConstrs((L_ijt[i,j,t]*v_it[i,t]>=(P_ijt[i,j,t]**2+Q_ijt[i,j,t]**2) for (i,j) in branch_ij 68 | for t in T_set),name='SOC') 69 | model.addConstrs((L_ijt[i,j,t]<=alpha_ij[i,j,t]*I_ijmax for (i,j) in branch_ij for t in T_set),name='Iconstrs') 70 | # 辐射网约束 71 | model.addConstrs((quicksum(alpha_ij[i,j,t] for (i,j) in branch_ij) ==B_num-1 for t in T_set),name='radical-1') 72 | model.addConstrs((beta_ij[i,j,t]+beta_ji[j,i,t]==alpha_ij[i,j,t] for (i,j) in branch_ij for t in T_set),name='radical-2') 73 | model.addConstrs((quicksum(beta_ij[j,i,t] for j in Nout_set[i])==1 for i in B_set if i not in [0] for t in T_set),name='radical-3') 74 | # model.addConstrs((beta_ij[0,j,t]==0 for j in Ninsert_set[0] for t in T_set),name='redical-4') 75 | 76 | #%% 目标函数 77 | obj = quicksum(L_ijt[i,j,t]*r_ij[i,j] for (i,j) in branch_ij for t in T_set) 78 | model.update() 79 | model.setObjective(obj,GRB.MINIMIZE) 80 | # model.Params.MIPGap = 0.01 81 | model.update() 82 | model.optimize() 83 | # # model.write('abf.lp') 84 | # model.computeIIS() 85 | # model.write('abc.ilp') 86 | #%% 输出数据 87 | o = t 88 | if model.status == GRB.Status.OPTIMAL: 89 | # dictalpha = {'line_index': branch_ij} 90 | dictalpha = {} 91 | dictv = {'bus_index': B_set} 92 | dictpij = {'line_index': branch_ij} 93 | dictbeta_ij = {'line_index': branch_ij} 94 | dictbeta_ji = {'line_index': branch_ji} 95 | dictL_ij = {'line_index': branch_ij} 96 | for t in T_set: 97 | dictalpha['t={}'.format(t)] = [alpha_ij[L+(t,) ].x for L in branch_ij] 98 | dictv['t={}'.format(t)] = [v_it[i,t].x for i in B_set] 99 | dictpij['t={}'.format(t)] = [P_ijt[L+(t,)].x for L in branch_ij] 100 | dictbeta_ij['t={}'.format(t)] = [beta_ij[L+(t,) ].x for L in branch_ij] 101 | dictbeta_ji['t={}'.format(t)] = [beta_ji[L+(t,) ].x for L in branch_ji] 102 | dictL_ij['t={}'.format(t)] = [L_ijt[L+(t,) ].x for L in branch_ij] 103 | dfalpha = pd.DataFrame(dictalpha) 104 | dL_ij = pd.DataFrame(dictL_ij) 105 | dfalpha1 = dfalpha 106 | dfalpha.index = branch_ij 107 | dfalpha1.to_excel('alpha.xlsx') 108 | dfalpha.iloc[:,0] = f 109 | dfalpha.iloc[:,1] = o 110 | flag = 0 111 | for i in T_set[3:]: 112 | frm = dfalpha[dfalpha.iloc[:,i]==1].iloc[:,0].values.tolist() 113 | to = dfalpha[dfalpha.iloc[:,i]==1].iloc[:,1].values.tolist() 114 | df = pd.DataFrame({'Source': frm, 'Target': to}) 115 | G = nx.Graph() 116 | G.add_edges_from(df.values.tolist()) 117 | # pos = nx.spring_layout(G) 118 | # nx.draw_networkx(G, pos) 119 | if not nx.is_directed_acyclic_graph(G): # 判断是否有环网 120 | flag += 1 121 | if flag: 122 | print("重构后的电网为辐射网") 123 | dfalpha1.to_excel('电网重构结果.xlsx') 124 | else: 125 | print("重构错误,重构后的电网存在环网") 126 | # pos = nx.spring_layout(G) 127 | # nx.draw_networkx(G, pos) 128 | # nx.draw(G) -------------------------------------------------------------------------------- /readData.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Mar 26 10:14:21 2023 4 | 5 | @author: wyx 6 | """ 7 | 8 | #%% 读取IEEE 33配电网数据 9 | import numpy as np 10 | import pandas as pd 11 | # bigM 12 | bigM = 100 13 | basekV = 12.66 14 | baseMVA = 10 15 | baseI = 789.89 16 | # 节点数据 17 | T_set = np.arange(24) 18 | dT = 1 19 | B_num = 33 20 | B_set = np.arange(33) 21 | # 电压上下限 22 | vmin = 0.9*0.9 23 | vmax = 1.05*1.05 24 | # 支路数据 25 | # line 26 | branchData = pd.read_excel('powerData.xlsx',sheet_name='Line').values 27 | f = branchData[:, 0].astype('int') 28 | t = branchData[:, 1].astype('int') 29 | branch_num = len(f) 30 | r = branchData[:,2]/(basekV ** 2 / baseMVA) 31 | x = branchData[:,3]/(basekV ** 2 / baseMVA) 32 | branch_ij = list(zip(f,t)) 33 | branch_ji = list(zip(t,f)) 34 | branch_ij_all = branch_ij+branch_ji 35 | I_ijmax = (500*500)/baseI**2 36 | # r 37 | r_ij = dict(zip(branch_ij,r)) 38 | # x 39 | x_ij = dict(zip(branch_ij,x)) 40 | 41 | # 读取有功无功数据 42 | PData = pd.read_excel('powerData.xlsx',sheet_name='ActiveLoad').values 43 | # activa load 44 | P_in_it = {(i,t):PData[i,t+1]/baseMVA for i in range(B_num) for t in T_set} 45 | # reactive load 46 | QData = pd.read_excel('powerData.xlsx',sheet_name='ReactiveLoad').values 47 | Q_in_it = {(i,t):QData[i,t+1]/baseMVA for i in range(B_num) for t in T_set} 48 | 49 | # 读取DG数据 50 | dgData = pd.read_excel('powerData.xlsx',sheet_name='Dg').values 51 | dgNode = dgData[:,1].astype('int') 52 | dgPmax_it = dict(zip(dgNode,dgData[:,2]/baseMVA)) 53 | dgPmin_it = dict(zip(dgNode,dgData[:,3]/baseMVA)) 54 | dgQmax_it = dict(zip(dgNode,dgData[:,4]/baseMVA)) 55 | dgQmin_it = dict(zip(dgNode,dgData[:,5]/baseMVA)) 56 | dgSmax_it = dict(zip(dgNode,dgData[:,6]/baseMVA)) 57 | comment_set = list(set(B_set)-set(dgNode)) 58 | buy_node = [0] 59 | # 节点连接关系 60 | Ninsert_set = {node:branchData[branchData[:,0]==node][:,1].astype('int').tolist() for node in B_set } 61 | Nout_set = {node:branchData[branchData[:,1]==node][:,0].astype('int').tolist() for node in B_set } 62 | N_all_set = {node:branchData[branchData[:,0]==node][:,1].astype('int').tolist()+branchData[branchData[:,1]==node][:,0].astype('int').tolist()for node in B_set} 63 | # # 储能数据 64 | # P_char_max = 0.2/baseMVA 65 | # P_dis_max = 0.2/baseMVA 66 | # eta = 0.9 67 | # N_max = 2 68 | 69 | --------------------------------------------------------------------------------