INSOLVABLEPF_LIMITQ A sufficient condition for power flow insolvability considering generator reactive power limits [INSOLVABLE,VSLACK_MIN,SIGMA,ETA,MINEIGRATIO] = INSOLVABLEPF_LIMITQ(MPC,MPOPT) Evaluates a sufficient condition for insolvability of the power flow equations considering generator reactive power limits. This function uses a mixed-integer semidefinite programming formulation of the power flow equations with generator reactive power limits that maximizes the power injections in a uniform, constant power factor profile. eta indicates the factor by which the power injections can be increased in this profile. If eta < 1, no power flow solution exists that satisfies the generator reactive power limits. The converse does not necessarily hold; a power flow solution may not exist even for cases that this function does not indicate are insolvable. See [1] for further details. Note that this function is only suitable for small systems due to the computational requirements of the mixed-integer semidefinite programming solver in YALMIP. Inputs: MPC : A MATPOWER case specifying the desired power flow equations. MPOPT : A MATPOWER options struct. If not specified, it is assumed to be the default mpoption. Outputs: INSOLVABLE : Binary variable. A value of 1 indicates that the specified power flow equations are insolvable, while a value of 0 means that the insolvability condition is indeterminant (a solution may or may not exist). ETA : Power injection margin to the power flow solvability boundary in the profile of a uniform, constant power factor change in power injections. MINEIGRATIO : A measure of satisfaction of the rank relaxation. Large values indicate satisfaction. (Note that satisfaction of the rank relaxation is not required for correctness of the insolvability condition). [1] D.K. Molzahn, V. Dawar, B.C. Lesieutre, and C.L. DeMarco, "Sufficient Conditions for Power Flow Insolvability Considering Reactive Power Limited Generators with Applications to Voltage Stability Margins," in Bulk Power System Dynamics and Control - IX. Optimization, Security and Control of the Emerging Power Grid, 2013 IREP Symposium, 25-30 August 2013.
0001 function [insolvable,eta,mineigratio] = insolvablepf_limitQ(mpc,mpopt) 0002 %INSOLVABLEPF_LIMITQ A sufficient condition for power flow insolvability 0003 %considering generator reactive power limits 0004 % 0005 % [INSOLVABLE,VSLACK_MIN,SIGMA,ETA,MINEIGRATIO] = 0006 % INSOLVABLEPF_LIMITQ(MPC,MPOPT) 0007 % 0008 % Evaluates a sufficient condition for insolvability of the power flow 0009 % equations considering generator reactive power limits. This function 0010 % uses a mixed-integer semidefinite programming formulation of the power 0011 % flow equations with generator reactive power limits that maximizes the 0012 % power injections in a uniform, constant power factor profile. eta 0013 % indicates the factor by which the power injections can be increased in 0014 % this profile. If eta < 1, no power flow solution exists that satisfies 0015 % the generator reactive power limits. The converse does not 0016 % necessarily hold; a power flow solution may not exist even for cases 0017 % that this function does not indicate are insolvable. See [1] for 0018 % further details. 0019 % 0020 % Note that this function is only suitable for small systems due to the 0021 % computational requirements of the mixed-integer semidefinite 0022 % programming solver in YALMIP. 0023 % 0024 % Inputs: 0025 % MPC : A MATPOWER case specifying the desired power flow equations. 0026 % MPOPT : A MATPOWER options struct. If not specified, it is 0027 % assumed to be the default mpoption. 0028 % 0029 % Outputs: 0030 % INSOLVABLE : Binary variable. A value of 1 indicates that the 0031 % specified power flow equations are insolvable, while a value of 0032 % 0 means that the insolvability condition is indeterminant (a 0033 % solution may or may not exist). 0034 % ETA : Power injection margin to the power flow solvability 0035 % boundary in the profile of a uniform, constant power factor 0036 % change in power injections. 0037 % MINEIGRATIO : A measure of satisfaction of the rank relaxation. 0038 % Large values indicate satisfaction. (Note that satisfaction of 0039 % the rank relaxation is not required for correctness of the 0040 % insolvability condition). 0041 % 0042 % [1] D.K. Molzahn, V. Dawar, B.C. Lesieutre, and C.L. DeMarco, "Sufficient 0043 % Conditions for Power Flow Insolvability Considering Reactive Power 0044 % Limited Generators with Applications to Voltage Stability Margins," 0045 % in Bulk Power System Dynamics and Control - IX. Optimization, 0046 % Security and Control of the Emerging Power Grid, 2013 IREP Symposium, 0047 % 25-30 August 2013. 0048 0049 % MATPOWER 0050 % Copyright (c) 2013-2015 by Power System Engineering Research Center (PSERC) 0051 % by Daniel Molzahn, PSERC U of Wisc, Madison 0052 % 0053 % $Id: insolvablepf_limitQ.m 2644 2015-03-11 19:34:22Z ray $ 0054 % 0055 % This file is part of MATPOWER. 0056 % Covered by the 3-clause BSD License (see LICENSE file for details). 0057 % See http://www.pserc.cornell.edu/matpower/ for more info. 0058 0059 if nargin < 2 0060 mpopt = mpoption; 0061 end 0062 0063 mpc = loadcase(mpc); 0064 0065 % Unpack options 0066 ignore_angle_lim = mpopt.opf.ignore_angle_lim; 0067 verbose = mpopt.verbose; 0068 ndisplay = mpopt.sdp_pf.ndisplay; %% Determine display frequency of diagonastic information 0069 0070 maxSystemSize = 57; 0071 fixPVbusInjection = 0; % If equal to 1, don't allow changes in active power injections at PV buses. 0072 0073 if ~have_fcn('yalmip') 0074 error('insolvablepf_limitQ: The software package YALMIP is required to run insolvablepf_limitQ. See http://users.isy.liu.se/johanl/yalmip/'); 0075 end 0076 0077 % set YALMIP options struct in SDP_PF (for details, see help sdpsettings) 0078 sdpopts = yalmip_options([], mpopt); 0079 0080 % Change solver to YALMIP's branch-and-bound algorithm 0081 sdpopts = sdpsettings(sdpopts,'solver','bnb','bnb.solver',sdpopts.solver); 0082 0083 if strcmp(sdpopts.solver, 'sedumi') || strcmp(sdpopts.solver,'sdpt3') 0084 sdpopts = sdpsettings(sdpopts,'solver','bnb'); 0085 end 0086 0087 %% define named indices into data matrices 0088 [PQ, PV, REF, NONE, BUS_I, BUS_TYPE, PD, QD, GS, BS, BUS_AREA, VM, ... 0089 VA, BASE_KV, ZONE, VMAX, VMIN, LAM_P, LAM_Q, MU_VMAX, MU_VMIN] = idx_bus; 0090 [GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ... 0091 MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ... 0092 QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen; 0093 [F_BUS, T_BUS, BR_R, BR_X, BR_B, RATE_A, RATE_B, RATE_C, ... 0094 TAP, SHIFT, BR_STATUS, PF, QF, PT, QT, MU_SF, MU_ST, ... 0095 ANGMIN, ANGMAX, MU_ANGMIN, MU_ANGMAX] = idx_brch; 0096 [PW_LINEAR, POLYNOMIAL, MODEL, STARTUP, SHUTDOWN, NCOST, COST] = idx_cost; 0097 0098 0099 %% Load mpc data 0100 0101 mpc = ext2int(mpc); 0102 0103 if toggle_dcline(mpc, 'status') 0104 error('insolvablepf_limitQ: DC lines are not implemented in insolvablepf'); 0105 end 0106 0107 nbus = size(mpc.bus,1); 0108 ngen = size(mpc.gen,1); 0109 0110 if nbus > maxSystemSize 0111 error('insolvablepf_limitQ: System is too large (more than 57 buses) to solve with insolvablepf_limitQ'); 0112 end 0113 0114 if ~ignore_angle_lim && (any(mpc.branch(:,ANGMIN) ~= -360) || any(mpc.branch(:,ANGMAX) ~= 360)) 0115 warning('insolvablepf_limitQ: Angle difference constraints are not implemented in SDP_PF. Ignoring angle difference constraints.'); 0116 end 0117 0118 % Some of the larger system (e.g., case2746wp) have generators 0119 % corresponding to buses that have bustype == PQ. Change these 0120 % to PV buses. 0121 for i=1:ngen 0122 busidx = find(mpc.bus(:,BUS_I) == mpc.gen(i,GEN_BUS)); 0123 if isempty(busidx) || ~(mpc.bus(busidx,BUS_TYPE) == PV || mpc.bus(busidx,BUS_TYPE) == REF) 0124 mpc.bus(busidx,BUS_TYPE) = PV; 0125 if verbose >= 1 0126 warning('insolvablepf_limitQ: Bus %s has generator(s) but is listed as a PQ bus. Changing to a PV bus.',int2str(busidx)); 0127 end 0128 end 0129 end 0130 0131 % Buses may be listed as PV buses without associated generators. Change 0132 % these buses to PQ. 0133 for i=1:nbus 0134 if mpc.bus(i,BUS_TYPE) == PV 0135 genidx = find(mpc.gen(:,GEN_BUS) == mpc.bus(i,BUS_I), 1); 0136 if isempty(genidx) 0137 mpc.bus(i,BUS_TYPE) = PQ; 0138 if verbose >= 1 0139 warning('insolvablepf_limitQ: PV bus %i has no associated generator! Changing these buses to PQ.',i); 0140 end 0141 end 0142 end 0143 end 0144 0145 0146 Sbase = mpc.baseMVA; 0147 0148 % Create vectors of power injections and voltage magnitudes 0149 Qd = mpc.bus(:,QD) / Sbase; 0150 Qinj = -Qd; 0151 Vmag = mpc.bus(:,VM); 0152 0153 Pd = mpc.bus(:,PD) / Sbase; 0154 Pg = zeros(nbus,1); 0155 Qmin = zeros(nbus,1); 0156 Qmax = zeros(nbus,1); 0157 for i=1:nbus 0158 genidx = find(mpc.gen(:,GEN_BUS) == i); 0159 if ~isempty(genidx) 0160 Pg(i) = sum(mpc.gen(genidx,PG)) / Sbase; 0161 Vmag(i) = mpc.gen(genidx(1),VG); 0162 Qmin(i) = sum(mpc.gen(genidx,QMIN)) / Sbase - Qd(i); 0163 Qmax(i) = sum(mpc.gen(genidx,QMAX)) / Sbase - Qd(i); 0164 end 0165 end 0166 Pinj = Pg - Pd; 0167 0168 0169 %% Functions to build matrices 0170 0171 [Yk,Yk_,Mk,Ylineft,Ylinetf,Y_lineft,Y_linetf] = makesdpmat(mpc); 0172 0173 %% Create primal SDP variables 0174 0175 [junk1,uniqueGenIdx,junk2] = unique(mpc.gen(:,GEN_BUS)); 0176 mpc.gen = mpc.gen(uniqueGenIdx,:); 0177 ngen = size(mpc.gen,1); 0178 0179 W = sdpvar(2*nbus,2*nbus); 0180 eta = sdpvar(1,1); 0181 constraints = []; 0182 0183 % Binary variables 0184 yL = binvar(ngen,1); 0185 yU = binvar(ngen,1); 0186 0187 % We need a number greater than any plausible value of V^2, but too large 0188 % of a value causes numerical problems 0189 bigM = 10*max(Vmag)^2; 0190 0191 %% Build primal problem 0192 0193 for k=1:nbus 0194 0195 if ~fixPVbusInjection 0196 % PQ and PV buses have uniform active power injection changes 0197 if mpc.bus(k,BUS_TYPE) == PQ || mpc.bus(k,BUS_TYPE) == PV 0198 constraints = [constraints; 0199 trace(Yk(k)*W) == eta*Pinj(k)]; 0200 end 0201 else 0202 % Alternatively, we can fix PV bus active power injections for a 0203 % different power injection profile. 0204 if mpc.bus(k,BUS_TYPE) == PQ 0205 constraints = [constraints; 0206 trace(Yk(k)*W) == eta*Pinj(k)]; 0207 end 0208 if mpc.bus(k,BUS_TYPE) == PV 0209 constraints = [constraints; 0210 trace(Yk(k)*W) == Pinj(k)]; 0211 end 0212 end 0213 0214 % PQ buses have uniform reactive power injection changes 0215 if mpc.bus(k,BUS_TYPE) == PQ 0216 constraints = [constraints; 0217 trace(Yk_(k)*W) == eta*Qinj(k)]; 0218 end 0219 0220 % Mixed integer formulation for generator reactive power limits 0221 if mpc.bus(k,BUS_TYPE) == PV || mpc.bus(k,BUS_TYPE) == REF 0222 0223 genidx = find(mpc.gen(:,GEN_BUS) == k,1); 0224 0225 constraints = [constraints; 0226 trace(Yk_(k)*W) <= Qmin(k)*yL(genidx) + Qmax(k)*(1-yL(genidx)); 0227 trace(Yk_(k)*W) >= Qmax(k)*yU(genidx) + Qmin(k)*(1-yU(genidx))]; 0228 0229 constraints = [constraints; 0230 trace(Mk(k)*W) >= Vmag(k)^2*(1-yU(genidx)); 0231 trace(Mk(k)*W) <= Vmag(k)^2*(1-yL(genidx)) + bigM*(yL(genidx))]; 0232 0233 constraints = [constraints; 0234 yU(genidx) + yL(genidx) <= 1]; 0235 0236 end 0237 0238 if verbose >= 2 && mod(k,ndisplay) == 0 0239 fprintf('SDP creation: Bus %i of %i\n',k,nbus); 0240 end 0241 0242 end % Loop through all buses 0243 0244 % Force at least one bus to supply reactive power balance 0245 % genbuses = (mpc.bus(:,BUS_TYPE) == PV | mpc.bus(:,BUS_TYPE) == REF); 0246 % constraints = [constraints; 0247 % sum(yL(genbuses)+yU(genbuses)) <= ngen - 1]; 0248 constraints = [constraints; 0249 sum(yL + yU) <= ngen - 1]; 0250 0251 constraints = [constraints; W >= 0]; 0252 0253 0254 0255 %% Solve the SDP 0256 0257 % Preserve warning settings 0258 S = warning; 0259 0260 % Run sdp solver 0261 sdpinfo = solvesdp(constraints, -eta, sdpopts); 0262 warning(S); 0263 0264 if verbose >= 2 0265 fprintf('Solver exit message: %s\n',sdpinfo.info); 0266 end 0267 0268 0269 %% Calculate rank characteristics of the solution 0270 0271 evl = sort(eig(double(W))); 0272 mineigratio = abs(evl(end) / evl(end-2)); 0273 0274 0275 %% Output results 0276 0277 eta = double(eta); 0278 insolvable = eta < 1; % Is this right? I think it should be the other way around...