├── Example.m ├── Grid Data ├── BaranGrid.mat ├── FiveNodeGrid.mat ├── FourNodeGrid.mat ├── KekatosGrid.mat └── TwoNodeGrid.mat ├── README.md └── SolveDistFlow.m /Example.m: -------------------------------------------------------------------------------- 1 | %% Baran & Wu 32 Bus Test Distribution Grid (1989) 2 | % This distribution grid is based on the following paper. 3 | % M. E. Baran and F. F. Wu, "Network reconfiguration in distribution systems for loss reduction and load balancing," 4 | % Grid parameters are described below 5 | 6 | % pN and qN are node active and reactive powers in Watts 7 | % rk and xk are line resistances and reactances in Ohms 8 | % Vo is the substation voltage in Volts 9 | 10 | % BranchTable describes which node is connected to which node 11 | % Substation node is represented by node 0 and its voltage is constant (Vo) 12 | 13 | Grid.pN = 1e3*[100;90;120;60;60;200;200;60;60;45;60;60;120;60;60;60;90;90;90;90;90;90;420;420;60;60;60;120;200;150;210;60]; 14 | Grid.qN = 1e3*[60;40;80;30;20;100;100;20;20;30;35;35;80;10;20;20;40;40;40;40;40;50;200;200;25;25;20;70;600;70;100;40]; 15 | Grid.rk = [0.0922;0.4930;0.3660;0.38110;0.8190;0.1872;0.7114;1.0300;1.0440;0.1966;0.3744;1.4680;0.5416;0.5910;0.7463;1.2890;0.7320;0.1640;1.5042;0.4095;0.7089;0.4512;0.8980;0.8960;0.2030;0.2842;1.0590;0.8042;0.5075;0.9744;0.3105;0.3410]; 16 | Grid.xk = [0.0470;0.2511;0.1864;0.1941;0.7070;0.6188;0.2351;0.7400;0.7400;0.0650;0.1238;1.1550;0.7129;0.5260;0.5450;1.7210;0.5740;0.1565;1.3554;0.4784;0.9373;0.3083;0.7091;0.7011;0.1034;0.1447;0.9337;0.7006;0.2585;0.9630;0.3619;0.5302]; 17 | Grid.Vo = 12660; 18 | 19 | BranchTable = [1 0; ... 20 | 2 1; ... 21 | 3 2; ... 22 | 4 3; ... 23 | 5 4; ... 24 | 6 5; ... 25 | 7 6; ... 26 | 8 7; ... 27 | 9 8; ... 28 | 10 9; ... 29 | 11 10; ... 30 | 12 11; ... 31 | 13 12; ... 32 | 14 13; ... 33 | 15 14; ... 34 | 16 15; ... 35 | 17 16; ... 36 | 18 1; ... 37 | 19 18; ... 38 | 20 19; ... 39 | 21 20; ... 40 | 22 2; ... 41 | 23 22; ... 42 | 24 23; ... 43 | 25 5; ... 44 | 26 25; ... 45 | 27 26; ... 46 | 28 27; ... 47 | 29 28; ... 48 | 30 29; ... 49 | 31 30; ... 50 | 32 31]; 51 | 52 | %% Construct the grid 53 | NumOfLines = length(BranchTable); 54 | Grid.N = NumOfLines; 55 | Grid.L = eye(NumOfLines); 56 | 57 | for node=2:NumOfLines 58 | BrNode = BranchTable(node,1); 59 | RcNode = BranchTable(node,2); 60 | Grid.L(BrNode,RcNode) = 1; 61 | end 62 | 63 | Grid.L = abs(inv(Grid.L)); 64 | 65 | %% Solve the grid 66 | 67 | %Grid = load('Grid Data\BaranGrid.mat'); 68 | %Grid = load('Grid Data\KekatosGrid.mat'); 69 | %Grid = load('Grid Data\TwoNodeGrid.mat'); 70 | %Grid = load('Grid Data\FiveNodeGrid.mat'); 71 | %Grid = load('Grid Data\FourNodeGrid.mat'); 72 | 73 | y = SolveDistFlow(Grid,1,10,0.0001); 74 | -------------------------------------------------------------------------------- /Grid Data/BaranGrid.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eminucer/DistFlow/44a1129dd273744a395323e6a8beb75f0262545e/Grid Data/BaranGrid.mat -------------------------------------------------------------------------------- /Grid Data/FiveNodeGrid.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eminucer/DistFlow/44a1129dd273744a395323e6a8beb75f0262545e/Grid Data/FiveNodeGrid.mat -------------------------------------------------------------------------------- /Grid Data/FourNodeGrid.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eminucer/DistFlow/44a1129dd273744a395323e6a8beb75f0262545e/Grid Data/FourNodeGrid.mat -------------------------------------------------------------------------------- /Grid Data/KekatosGrid.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eminucer/DistFlow/44a1129dd273744a395323e6a8beb75f0262545e/Grid Data/KekatosGrid.mat -------------------------------------------------------------------------------- /Grid Data/TwoNodeGrid.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eminucer/DistFlow/44a1129dd273744a395323e6a8beb75f0262545e/Grid Data/TwoNodeGrid.mat -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DistFlow Solver 2 | This function solves the distribution power flow equations for a given, generic, radial, single-phase distribution network based on the DistFlow equations introduced by Baran&Wu (1989). 3 | 4 | -Example.m file showcases the solution for a 33-bus distribution grid network. (Run `Example.m`)
5 | -The following are the resulting plots for the network graph and node-voltage profiles
6 | -The solver function `SolveDistFlow(Grid,PlotGraph,MaxIter,tol)` takes 4 inputs
7 | -`Grid` is a structure that contains grid-related parameters, i.e., number of nodes `(N)`, grid graph `(L)`, line impedences `(rk, xk)`, node powers `(pN, qN)` and substation voltage `(Vo)`
8 | -`PlotGraph (1,0)` enables plotting of the results after execution
9 | -`MaxIter` sets the maximum number of iterations for the Newton-Raphson method
10 | -`tol` is the error tolerance
11 | 12 | -There are also different test feeders under `\Grid Data`. You can uncomment the corresponding lines in `Example.m` and run it to see the results for yourself
13 | -You can use the first code section in `Example.m` to create your own custom distribution grid and solve it
14 | 15 | 16 | 17 | 18 | ``` 19 | Iteration: 9 20 | Terminal node error: 0.000002< tolerance = 0.000100 -> Y 21 | 22 | Total Num of Iterations: 9 23 | 24 | Node voltages and phase angles: 25 | V[0] = 1.0000 /_ 0.00 26 | V[1] = 0.9970 /_ 0.01 27 | V[2] = 0.9829 /_ 0.10 28 | V[3] = 0.9755 /_ 0.16 29 | V[4] = 0.9681 /_ 0.23 30 | V[5] = 0.9497 /_ 0.13 31 | V[6] = 0.9462 /_ -0.10 32 | V[7] = 0.9413 /_ -0.06 33 | V[8] = 0.9351 /_ -0.13 34 | V[9] = 0.9292 /_ -0.20 35 | V[10] = 0.9284 /_ -0.19 36 | V[11] = 0.9269 /_ -0.18 37 | V[12] = 0.9208 /_ -0.27 38 | V[13] = 0.9185 /_ -0.35 39 | V[14] = 0.9171 /_ -0.38 40 | V[15] = 0.9157 /_ -0.41 41 | V[16] = 0.9137 /_ -0.49 42 | V[17] = 0.9131 /_ -0.50 43 | V[18] = 0.9965 /_ -0.51 44 | V[19] = 0.9929 /_ -0.57 45 | V[20] = 0.9922 /_ -0.59 46 | V[21] = 0.9916 /_ -0.61 47 | V[22] = 0.9794 /_ -0.65 48 | V[23] = 0.9727 /_ -0.73 49 | V[24] = 0.9694 /_ -0.78 50 | V[25] = 0.9477 /_ -0.74 51 | V[26] = 0.9452 /_ -0.68 52 | V[27] = 0.9337 /_ -0.60 53 | V[28] = 0.9255 /_ -0.52 54 | V[29] = 0.9220 /_ -0.42 55 | V[30] = 0.9178 /_ -0.50 56 | V[31] = 0.9169 /_ -0.52 57 | V[32] = 0.9166 /_ -0.53 58 | ``` 59 | 60 | -------------------------------------------------------------------------------- /SolveDistFlow.m: -------------------------------------------------------------------------------- 1 | 2 | function [y] = SolveDistFlow(Grid, PlotGraph, MaxIter, tol) 3 | % This function solves the distribution power flow equations for a given 4 | % radial distribution network based on DistFlow equations introduced in the Baran&Wu paper(1989). 5 | % 6 | % Inputs 7 | % ->Grid : a structure contaning grid parameters 8 | % ->PlotGraph : Binary variable to display informative figures 9 | % ->MaxIter : Maximum number of iterations 10 | % ->tol : error tolerance (0.0001 recommended) 11 | % 12 | % Outputs 13 | % ->y : output contaning the solved line powers, voltages and phase angles. 14 | 15 | function [x_next] = F(x_prev,rk,xk,pk,qk) 16 | P_prev = x_prev(1); 17 | Q_prev = x_prev(2); 18 | V_prev = x_prev(3); 19 | 20 | P_next = P_prev-pk-rk*(P_prev^2 + Q_prev^2)/V_prev^2; 21 | Q_next = Q_prev-qk-xk*(P_prev^2 + Q_prev^2)/V_prev^2; 22 | V_next = V_prev^2-2*rk*P_prev-2*xk*Q_prev+(rk^2+xk^2)*(P_prev^2 + Q_prev^2)/V_prev^2; 23 | 24 | x_next(1) = P_next; 25 | x_next(2) = Q_next; 26 | x_next(3) = sqrt(V_next); 27 | end 28 | 29 | function J = Jacobian(x_prev,rk,xk) 30 | P_prev = x_prev(1); 31 | Q_prev = x_prev(2); 32 | V_prev = x_prev(3); 33 | 34 | zk = rk^2 + xk^2; 35 | 36 | J = [1-2*rk*P_prev/(V_prev^2) -2*rk*Q_prev/(V_prev^2) rk*(P_prev^2 + Q_prev^2)/(V_prev^4); ... 37 | -2*xk*P_prev/(V_prev^2) 1-2*xk*Q_prev/(V_prev^2) xk*(P_prev^2 + Q_prev^2)/(V_prev^4); ... 38 | -2*(rk-zk*P_prev/(V_prev^2)) -2*(xk-zk*Q_prev/(V_prev^2)) 1-zk*(P_prev^2 + Q_prev^2)/(V_prev^4)]; 39 | end 40 | 41 | N = Grid.N; 42 | L = Grid.L; 43 | pN = Grid.pN; 44 | qN = Grid.qN; 45 | rk = Grid.rk; 46 | xk = Grid.xk; 47 | Vo = Grid.Vo; 48 | 49 | Phase = cell(N+1,1); 50 | Phase{1} = 0; 51 | 52 | Ainv = -L; 53 | A = inv(Ainv); 54 | 55 | FromNode = []; 56 | ToNode = []; 57 | FromNode(1) = 0; 58 | ToNode(1) = 1; 59 | 60 | for i=1:N-1 61 | a = find(A(:,i) == 1); 62 | FromNode = [FromNode repmat(i,1,length(a))]; 63 | ToNode = [ToNode a']; 64 | end 65 | 66 | FromNode = FromNode +1; 67 | ToNode = ToNode + 1; 68 | GR = digraph(FromNode,ToNode); 69 | eLabels = cell(1,N); 70 | nLabels = cell(1,N+1); 71 | for i=1:N 72 | eLabels{i} = strcat('l_{',num2str(FromNode(i)-1),'-',num2str(ToNode(i)-1),'}'); 73 | end 74 | for i=1:N+1 75 | nLabels{i} = strcat(num2str(i-1)); 76 | end 77 | 78 | if PlotGraph 79 | figure('Renderer', 'painters', 'Position', [250 250 900 350]) 80 | p=plot(GR,'NodeLabel',nLabels,'LineW',2); 81 | % p.NodeFontSize = 14; 82 | % p.EdgeFontSize = 14; 83 | p.Marker = 's'; 84 | p.NodeColor = 'r'; 85 | end 86 | 87 | FinalNodes = find(sum(abs(A),1) == 1); % Nodes with no further branches/children (terminal nodes) 88 | Paths = cell(length(FinalNodes),1); 89 | for i=1:length(FinalNodes) 90 | Paths{i} = [0, find(Ainv(FinalNodes(i),:) == -1)]; 91 | end 92 | 93 | NumOfPaths = length(Paths); 94 | len = zeros(NumOfPaths,2); 95 | 96 | for i=1:NumOfPaths 97 | len(i,:) = [length(Paths{i}),i]; 98 | end 99 | 100 | [~,idx] = sort(len,'descend'); 101 | 102 | pL = zeros(N,1); 103 | qL = zeros(N,1); 104 | 105 | LinDistFlow_solution_V = Vo^2*ones(N,1) - 2*Ainv*diag(rk)*(Ainv)'*pN - 2*Ainv*diag(xk)*(Ainv)'*qN; 106 | LinDistFlow_solution_V = [Vo^2 ; LinDistFlow_solution_V]; 107 | LinDistFlow_solution_V = sqrt(LinDistFlow_solution_V); 108 | 109 | P_terminal = zeros(NumOfPaths,1); 110 | 111 | for iter = 1:MaxIter 112 | UpdatedVoltages = nan(N+1,1); 113 | UpdatedVoltages(1) = Vo; 114 | 115 | counter = 0; 116 | 117 | for i= idx(:,1)' 118 | counter = counter + 1; 119 | 120 | UpdatedNodes = Paths{i}(find(~isnan(UpdatedVoltages(Paths{i}(2:end)+1)))+1); 121 | if isempty(UpdatedNodes) 122 | LastNode = Paths{i}(1); 123 | else 124 | LastNode = UpdatedNodes(end); 125 | end 126 | RemainingNodes = Paths{i}(find(Paths{i} == LastNode)+1:end); 127 | % If LastNode == RemainingNodes => We already solved this branch 128 | 129 | NN = length(RemainingNodes); 130 | 131 | FirstNode = RemainingNodes(1); 132 | 133 | B = abs(Ainv); 134 | B = B + -diag(ones(N,1)); 135 | 136 | % Initialize 137 | ConnectedNodes = cell(length(Paths{i})-1,1); 138 | AggregatedNodes = cell(length(Paths{i})-1,1); 139 | 140 | for j=1:length(Paths{i})-1 141 | ConnectedNodes{j} = find(B(:,Paths{i}(j+1))==1); 142 | end 143 | 144 | for j=1:length(Paths{i})-1 145 | if Paths{i}(j+1) == Paths{i}(end) 146 | AggregatedNodes{j} = []; 147 | else 148 | AggregatedNodes{j} = setdiff(ConnectedNodes{j},[ConnectedNodes{j+1}; Paths{i}(j+2)]); 149 | end 150 | AggregatedNodes{j,2} = Paths{i}(j+1); 151 | end 152 | 153 | StartFrom = find(cell2mat(AggregatedNodes(:,2)) == FirstNode); 154 | %We assume FirstNode's voltage is known/updated. 155 | pNN = zeros(NN,1); 156 | qNN = zeros(NN,1); 157 | 158 | NumOfNodesToSolve = StartFrom:StartFrom+NN-1; 159 | for j=1:length(NumOfNodesToSolve) 160 | ContributingNodes = AggregatedNodes{NumOfNodesToSolve(j),1}; 161 | if isempty(ContributingNodes) 162 | pNN(j) = pN(AggregatedNodes{NumOfNodesToSolve(j),2}); 163 | qNN(j) = qN(AggregatedNodes{NumOfNodesToSolve(j),2}); 164 | else 165 | if pL(ContributingNodes(1)) == 0 %if not updated (include qL as well) 166 | for k=1:length(ContributingNodes) 167 | pNN(j) = pNN(j) + pN(ContributingNodes(k)); 168 | qNN(j) = qNN(j) + qN(ContributingNodes(k)); 169 | end 170 | %add itself 171 | pNN(j) = pNN(j) + pN(AggregatedNodes{NumOfNodesToSolve(j),2}); 172 | qNN(j) = qNN(j) + qN(AggregatedNodes{NumOfNodesToSolve(j),2}); 173 | else 174 | pNN(j) = pNN(j) + pL(ContributingNodes(1)) + pN(AggregatedNodes{NumOfNodesToSolve(j),2}); 175 | qNN(j) = qNN(j) + qL(ContributingNodes(1)) + qN(AggregatedNodes{NumOfNodesToSolve(j),2}); 176 | end 177 | end 178 | end 179 | 180 | xx = cell(NN+1,1); % State variable (N+1)x1 181 | J = cell(NN,1); % Jacobian matrix NxN 182 | 183 | if pL(FirstNode) == 0 184 | xx{1} = [sum(pNN); ... 185 | sum(qNN); ... 186 | UpdatedVoltages(LastNode+1)]; % LastNode = FirstNode in un-updated path 187 | else 188 | xx{1} = [pL(FirstNode); 189 | qL(FirstNode); 190 | UpdatedVoltages(LastNode+1)]; 191 | end 192 | 193 | % Forward sweep to find PLs and QLs 194 | rkk = rk(RemainingNodes); 195 | xkk = xk(RemainingNodes); 196 | 197 | for ii=1:NN 198 | xx{ii+1} = (F(xx{ii},rkk(ii),xkk(ii),pNN(ii),qNN(ii)))'; 199 | pL(RemainingNodes(ii)) = xx{ii}(1); 200 | qL(RemainingNodes(ii)) = xx{ii}(2); 201 | UpdatedVoltages(RemainingNodes(ii)+1) = xx{ii+1}(3); 202 | J{ii} = Jacobian(xx{ii},rkk(ii),xkk(ii)); 203 | end 204 | P_terminal(counter) = xx{ii+1}(1); 205 | 206 | JJ = J{NN}(1:2,1:3); % J(N) -> 2x3 matrix 207 | 208 | for ii=NN-1:-1:1 209 | JJ = JJ * J{ii}; 210 | end 211 | 212 | %x_next = xx{1}-JJ\xx{NN+1}; 213 | x_next = xx{1}(1:3)-JJ\xx{NN+1}(1:2); 214 | 215 | pL(FirstNode) = x_next(1); 216 | qL(FirstNode) = x_next(2); 217 | 218 | end 219 | 220 | error = norm(abs(P_terminal),1); 221 | 222 | fprintf('Iteration: %d\nTerminal node error: %f', iter, error) 223 | if error > tol 224 | fprintf(' > tolerance = %f -> N\n', tol); 225 | else 226 | fprintf('< tolerance = %f -> Y\n\nTotal Num of Iterations: %d\n', tol, iter); 227 | if 1 228 | fprintf('\nNode voltages and phase angles:\n'); 229 | for i=1:N+1 230 | if i<= N 231 | Phase{i+1} = Phase{i}-angle(UpdatedVoltages(i)^2 - (conj(rk(i)+1j*xk(i)))*(pL(i)+1j*qL(i))); 232 | end 233 | fprintf('V[%d] = %.4f /_ %.2f\n',i-1, (abs(UpdatedVoltages(i))/Vo), rad2deg(Phase{i})); 234 | end 235 | end 236 | break; 237 | end 238 | fprintf('\n'); 239 | 240 | end 241 | 242 | figure('Renderer', 'painters', 'Position', [250 250 900 350]) 243 | hold on; 244 | grid on; 245 | plot(0:N,UpdatedVoltages/Vo,'-o','LineW',2) 246 | plot(0:N,LinDistFlow_solution_V/Vo, 'LineW',2) 247 | xticks([0:N]) 248 | xlabel('Nodes') 249 | ylabel('Voltage') 250 | set(gca,'FontSize',13) 251 | legend('DistFlow','LinDistFlow') 252 | xlim([0,N]); 253 | 254 | y = cell(4,1); 255 | y{1} = pL; 256 | y{2} = qL; 257 | y{3} = UpdatedVoltages; 258 | y{4} = cell2mat(Phase); 259 | y{5} = P_terminal; 260 | 261 | end 262 | --------------------------------------------------------------------------------