├── 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 |
--------------------------------------------------------------------------------