Home > matpower7.0 > lib > fmincopf_solver.m

fmincopf_solver

PURPOSE ^

FMINCOPF_SOLVER Solves AC optimal power flow using FMINCON.

SYNOPSIS ^

function [results, success, raw] = fmincopf_solver(om, mpopt)

DESCRIPTION ^

FMINCOPF_SOLVER  Solves AC optimal power flow using FMINCON.

   [RESULTS, SUCCESS, RAW] = FMINCOPF_SOLVER(OM, MPOPT)

   Inputs are an OPF model object and a MATPOWER options struct.

   Outputs are a RESULTS struct, SUCCESS flag and RAW output struct.

   RESULTS is a MATPOWER case struct (mpc) with the usual baseMVA, bus
   branch, gen, gencost fields, along with the following additional
   fields:
       .order      see 'help ext2int' for details of this field
       .x          final value of optimization variables (internal order)
       .f          final objective function value
       .mu         shadow prices on ...
           .var
               .l  lower bounds on variables
               .u  upper bounds on variables
           .nln    (deprecated) 2*nb+2*nl - Pmis, Qmis, Sf, St
               .l  lower bounds on nonlinear constraints
               .u  upper bounds on nonlinear constraints
           .nle    nonlinear equality constraints
           .nli    nonlinear inequality constraints
           .lin
               .l  lower bounds on linear constraints
               .u  upper bounds on linear constraints

   SUCCESS     1 if solver converged successfully, 0 otherwise

   RAW         raw output in form returned by MINOS
       .xr     final value of optimization variables
       .pimul  constraint multipliers
       .info   solver specific termination code
       .output solver specific output information

   See also OPF, FMINCON.

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SOURCE CODE ^

0001 function [results, success, raw] = fmincopf_solver(om, mpopt)
0002 %FMINCOPF_SOLVER  Solves AC optimal power flow using FMINCON.
0003 %
0004 %   [RESULTS, SUCCESS, RAW] = FMINCOPF_SOLVER(OM, MPOPT)
0005 %
0006 %   Inputs are an OPF model object and a MATPOWER options struct.
0007 %
0008 %   Outputs are a RESULTS struct, SUCCESS flag and RAW output struct.
0009 %
0010 %   RESULTS is a MATPOWER case struct (mpc) with the usual baseMVA, bus
0011 %   branch, gen, gencost fields, along with the following additional
0012 %   fields:
0013 %       .order      see 'help ext2int' for details of this field
0014 %       .x          final value of optimization variables (internal order)
0015 %       .f          final objective function value
0016 %       .mu         shadow prices on ...
0017 %           .var
0018 %               .l  lower bounds on variables
0019 %               .u  upper bounds on variables
0020 %           .nln    (deprecated) 2*nb+2*nl - Pmis, Qmis, Sf, St
0021 %               .l  lower bounds on nonlinear constraints
0022 %               .u  upper bounds on nonlinear constraints
0023 %           .nle    nonlinear equality constraints
0024 %           .nli    nonlinear inequality constraints
0025 %           .lin
0026 %               .l  lower bounds on linear constraints
0027 %               .u  upper bounds on linear constraints
0028 %
0029 %   SUCCESS     1 if solver converged successfully, 0 otherwise
0030 %
0031 %   RAW         raw output in form returned by MINOS
0032 %       .xr     final value of optimization variables
0033 %       .pimul  constraint multipliers
0034 %       .info   solver specific termination code
0035 %       .output solver specific output information
0036 %
0037 %   See also OPF, FMINCON.
0038 
0039 %   MATPOWER
0040 %   Copyright (c) 2000-2017, Power Systems Engineering Research Center (PSERC)
0041 %   by Ray Zimmerman, PSERC Cornell
0042 %   and Carlos E. Murillo-Sanchez, PSERC Cornell & Universidad Nacional de Colombia
0043 %
0044 %   This file is part of MATPOWER.
0045 %   Covered by the 3-clause BSD License (see LICENSE file for details).
0046 %   See https://matpower.org for more info.
0047 
0048 %%----- initialization -----
0049 %% define named indices into data matrices
0050 [PQ, PV, REF, NONE, BUS_I, BUS_TYPE, PD, QD, GS, BS, BUS_AREA, VM, ...
0051     VA, BASE_KV, ZONE, VMAX, VMIN, LAM_P, LAM_Q, MU_VMAX, MU_VMIN] = idx_bus;
0052 [GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
0053     MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
0054     QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;
0055 [F_BUS, T_BUS, BR_R, BR_X, BR_B, RATE_A, RATE_B, RATE_C, ...
0056     TAP, SHIFT, BR_STATUS, PF, QF, PT, QT, MU_SF, MU_ST, ...
0057     ANGMIN, ANGMAX, MU_ANGMIN, MU_ANGMAX] = idx_brch;
0058 [PW_LINEAR, POLYNOMIAL, MODEL, STARTUP, SHUTDOWN, NCOST, COST] = idx_cost;
0059 
0060 %% unpack data
0061 mpc = om.get_mpc();
0062 [baseMVA, bus, gen, branch, gencost] = ...
0063     deal(mpc.baseMVA, mpc.bus, mpc.gen, mpc.branch, mpc.gencost);
0064 [vv, ll, nne, nni] = om.get_idx();
0065 
0066 %% problem dimensions
0067 nb = size(bus, 1);          %% number of buses
0068 nl = size(branch, 1);       %% number of branches
0069 ny = om.getN('var', 'y');   %% number of piece-wise linear costs
0070 
0071 %% bounds on optimization vars
0072 [x0, xmin, xmax] = om.params_var();
0073 
0074 %% linear constraints
0075 [A, l, u] = om.params_lin_constraint();
0076 
0077 %% split l <= A*x <= u into less than, equal to, greater than, and
0078 %% doubly-bounded sets
0079 ieq = find( abs(u-l) <= eps );          %% equality
0080 igt = find( u >=  1e10 & l > -1e10 );   %% greater than, unbounded above
0081 ilt = find( l <= -1e10 & u <  1e10 );   %% less than, unbounded below
0082 ibx = find( (abs(u-l) > eps) & (u < 1e10) & (l > -1e10) );
0083 Af  = [ A(ilt, :); -A(igt, :); A(ibx, :); -A(ibx, :) ];
0084 bf  = [ u(ilt);   -l(igt);     u(ibx);    -l(ibx)];
0085 Afeq = A(ieq, :);
0086 bfeq = u(ieq);
0087 
0088 %% build admittance matrices
0089 [Ybus, Yf, Yt] = makeYbus(baseMVA, bus, branch);
0090 
0091 %% try to select an interior initial point, unless requested not to
0092 if mpopt.opf.start < 2
0093     s = 1;                      %% set init point inside bounds by s
0094     lb = xmin; ub = xmax;
0095     lb(xmin == -Inf) = -1e10;   %% replace Inf with numerical proxies
0096     ub(xmax ==  Inf) =  1e10;
0097     x0 = (lb + ub) / 2;         %% set x0 mid-way between bounds
0098     k = find(xmin == -Inf & xmax < Inf);    %% if only bounded above
0099     x0(k) = xmax(k) - s;                    %% set just below upper bound
0100     k = find(xmin > -Inf & xmax == Inf);    %% if only bounded below
0101     x0(k) = xmin(k) + s;                    %% set just above lower bound
0102     Varefs = bus(bus(:, BUS_TYPE) == REF, VA) * (pi/180);
0103     Vmax = min(bus(:, VMAX), 1.5);
0104     Vmin = max(bus(:, VMIN), 0.5);
0105     Vm = (Vmax + Vmin) / 2;
0106     if mpopt.opf.v_cartesian
0107         V = Vm * exp(1j*Varefs(1));
0108         x0(vv.i1.Vr:vv.iN.Vr) = real(V);
0109         x0(vv.i1.Vi:vv.iN.Vi) = imag(V);
0110     else
0111         x0(vv.i1.Va:vv.iN.Va) = Varefs(1);  %% angles set to first reference angle
0112         x0(vv.i1.Vm:vv.iN.Vm) = Vm;         %% voltage magnitudes
0113         if ny > 0
0114             ipwl = find(gencost(:, MODEL) == PW_LINEAR);
0115             c = gencost(sub2ind(size(gencost), ipwl, NCOST+2*gencost(ipwl, NCOST)));    %% largest y-value in CCV data
0116             x0(vv.i1.y:vv.iN.y) = max(c) + 0.1 * abs(max(c));
0117         end
0118     end
0119 end
0120 
0121 %% find branches with flow limits
0122 il = find(branch(:, RATE_A) ~= 0 & branch(:, RATE_A) < 1e10);
0123 nl2 = length(il);           %% number of constrained lines
0124 
0125 %% basic optimset options needed for fmincon
0126 fmoptions = optimset('GradObj', 'on', 'GradConstr', 'on', ...
0127             'TolCon', mpopt.opf.violation, 'TolX', mpopt.fmincon.tol_x, ...
0128             'TolFun', mpopt.fmincon.tol_f );
0129 if mpopt.fmincon.max_it ~= 0
0130     fmoptions = optimset(fmoptions, 'MaxIter', mpopt.fmincon.max_it, ...
0131             'MaxFunEvals', 4 * mpopt.fmincon.max_it);
0132 end
0133 
0134 if mpopt.verbose == 0,
0135   fmoptions.Display = 'off';
0136 elseif mpopt.verbose == 1
0137   fmoptions.Display = 'iter';
0138 else
0139   fmoptions.Display = 'testing';
0140 end
0141 
0142 %% select algorithm
0143 if have_fcn('fmincon_ipm')
0144   switch mpopt.fmincon.alg
0145     case 1              %% active-set (does not use sparse matrices, not suitable for large problems)
0146       fmoptions = optimset(fmoptions, 'Algorithm', 'active-set');
0147       Af = full(Af);
0148       Afeq = full(Afeq);
0149     case 2              %% interior-point, w/ default 'bfgs' Hessian approx
0150       fmoptions = optimset(fmoptions, 'Algorithm', 'interior-point');
0151     case 3              %% interior-point, w/ 'lbfgs' Hessian approx
0152       fmoptions = optimset(fmoptions, 'Algorithm', 'interior-point', 'Hessian','lbfgs');
0153     case 4              %% interior-point, w/ exact user-supplied Hessian
0154       fmc_hessian = @(x, lambda)opf_hessfcn(x, lambda, 1, om, Ybus, Yf(il,:), Yt(il,:), mpopt, il);
0155       fmoptions = optimset(fmoptions, 'Algorithm', 'interior-point', ...
0156           'Hessian', 'user-supplied', 'HessFcn', fmc_hessian);
0157     case 5              %% interior-point, w/ finite-diff Hessian
0158       fmoptions = optimset(fmoptions, 'Algorithm', 'interior-point', 'Hessian','fin-diff-grads', 'SubProblem', 'cg');
0159     case 6              %% sqp (does not use sparse matrices, not suitable for large problems)
0160       fmoptions = optimset(fmoptions, 'Algorithm', 'sqp');
0161       Af = full(Af);
0162       Afeq = full(Afeq);
0163     otherwise
0164       error('fmincopf_solver: unknown algorithm specified in ''fmincon.alg'' option');
0165   end
0166 else
0167   fmoptions = optimset(fmoptions, 'LargeScale', 'off');
0168   Af = full(Af);
0169   Afeq = full(Afeq);
0170 end
0171 % fmoptions = optimset(fmoptions, 'DerivativeCheck', 'on', 'FinDiffType', 'central', 'FunValCheck', 'on');
0172 % fmoptions = optimset(fmoptions, 'Diagnostics', 'on');
0173 
0174 %%-----  run opf  -----
0175 f_fcn = @(x)opf_costfcn(x, om);
0176 gh_fcn = @(x)opf_consfcn(x, om, Ybus, Yf(il,:), Yt(il,:), mpopt, il);
0177 [x, f, info, Output, Lambda] = ...
0178   fmincon(f_fcn, x0, Af, bf, Afeq, bfeq, xmin, xmax, gh_fcn, fmoptions);
0179 success = (info > 0);
0180 
0181 %% update solution data
0182 if mpopt.opf.v_cartesian
0183     Vi = x(vv.i1.Vi:vv.iN.Vi);
0184     Vr = x(vv.i1.Vr:vv.iN.Vr);
0185     V = Vr + 1j*Vi;
0186     Va = angle(V);
0187     Vm = abs(V);
0188 else
0189     Va = x(vv.i1.Va:vv.iN.Va);
0190     Vm = x(vv.i1.Vm:vv.iN.Vm);
0191     V = Vm .* exp(1j*Va);
0192 end
0193 Pg = x(vv.i1.Pg:vv.iN.Pg);
0194 Qg = x(vv.i1.Qg:vv.iN.Qg);
0195 
0196 %%-----  calculate return values  -----
0197 %% update voltages & generator outputs
0198 bus(:, VA) = Va * 180/pi;
0199 bus(:, VM) = Vm;
0200 gen(:, PG) = Pg * baseMVA;
0201 gen(:, QG) = Qg * baseMVA;
0202 gen(:, VG) = Vm(gen(:, GEN_BUS));
0203 
0204 %% compute branch flows
0205 Sf = V(branch(:, F_BUS)) .* conj(Yf * V);  %% cplx pwr at "from" bus, p.u.
0206 St = V(branch(:, T_BUS)) .* conj(Yt * V);  %% cplx pwr at "to" bus, p.u.
0207 branch(:, PF) = real(Sf) * baseMVA;
0208 branch(:, QF) = imag(Sf) * baseMVA;
0209 branch(:, PT) = real(St) * baseMVA;
0210 branch(:, QT) = imag(St) * baseMVA;
0211 
0212 %% line constraint is typically on square of limit
0213 %% so we must fix multipliers
0214 muSf = zeros(nl, 1);
0215 muSt = zeros(nl, 1);
0216 if ~isempty(il)
0217     if upper(mpopt.opf.flow_lim(1)) == 'P'
0218         muSf(il) = Lambda.ineqnonlin(nni.i1.Sf:nni.iN.Sf);
0219         muSt(il) = Lambda.ineqnonlin(nni.i1.St:nni.iN.St);
0220     else
0221         muSf(il) = 2 * Lambda.ineqnonlin(nni.i1.Sf:nni.iN.Sf) .* branch(il, RATE_A) / baseMVA;
0222         muSt(il) = 2 * Lambda.ineqnonlin(nni.i1.St:nni.iN.St) .* branch(il, RATE_A) / baseMVA;
0223     end
0224 end
0225 
0226 %% fix Lambdas
0227 %% (shadow prices on equality variable bounds can come back on wrong limit)
0228 kl = find(Lambda.lower < 0 & Lambda.upper == 0);
0229 Lambda.upper(kl) = -Lambda.lower(kl);
0230 Lambda.lower(kl) = 0;
0231 ku = find(Lambda.upper < 0 & Lambda.lower == 0);
0232 Lambda.lower(ku) = -Lambda.upper(ku);
0233 Lambda.upper(ku) = 0;
0234 
0235 %% update Lagrange multipliers
0236 if mpopt.opf.v_cartesian
0237     if om.userdata.veq
0238         lam = Lambda.eqnonlin(nne.i1.Veq:nne.iN.Veq);
0239         mu_Vmax = zeros(size(lam));
0240         mu_Vmin = zeros(size(lam));
0241         mu_Vmax(lam > 0) =  lam(lam > 0);
0242         mu_Vmin(lam < 0) = -lam(lam < 0);
0243         bus(om.userdata.veq, MU_VMAX) = mu_Vmax;
0244         bus(om.userdata.veq, MU_VMIN) = mu_Vmin;
0245     end
0246     bus(om.userdata.viq, MU_VMAX) = Lambda.ineqnonlin(nni.i1.Vmax:nni.iN.Vmax);
0247     bus(om.userdata.viq, MU_VMIN) = Lambda.ineqnonlin(nni.i1.Vmin:nni.iN.Vmin);
0248 else
0249     bus(:, MU_VMAX)  = Lambda.upper(vv.i1.Vm:vv.iN.Vm);
0250     bus(:, MU_VMIN)  = Lambda.lower(vv.i1.Vm:vv.iN.Vm);
0251 end
0252 gen(:, MU_PMAX)  = Lambda.upper(vv.i1.Pg:vv.iN.Pg) / baseMVA;
0253 gen(:, MU_PMIN)  = Lambda.lower(vv.i1.Pg:vv.iN.Pg) / baseMVA;
0254 gen(:, MU_QMAX)  = Lambda.upper(vv.i1.Qg:vv.iN.Qg) / baseMVA;
0255 gen(:, MU_QMIN)  = Lambda.lower(vv.i1.Qg:vv.iN.Qg) / baseMVA;
0256 if mpopt.opf.current_balance
0257     %% convert current balance shadow prices to equivalent lamP and lamQ
0258     %% P + jQ = (Vr + jVi) * (M - jN)
0259     %% M = (Vr P + Vi Q) / (Vr^2 + Vi^2)
0260     %% N = (Vi P - Vr Q) / (Vr^2 + Vi^2)
0261     %% lamP = df/dP = df/dM * dM/dP + df/dN + dN/dP
0262     %% lamQ = df/dQ = df/dM * dM/dQ + df/dN + dN/dQ
0263     VV = V ./ (V .* conj(V));   %% V / Vm^2
0264     VVr = real(VV);
0265     VVi = imag(VV);
0266     lamM = Lambda.eqnonlin(nne.i1.rImis:nne.iN.rImis);
0267     lamN = Lambda.eqnonlin(nne.i1.iImis:nne.iN.iImis);
0268     bus(:, LAM_P) = (VVr.*lamM + VVi.*lamN) / baseMVA;
0269     bus(:, LAM_Q) = (VVi.*lamM - VVr.*lamN) / baseMVA;
0270 else
0271     bus(:, LAM_P) = Lambda.eqnonlin(nne.i1.Pmis:nne.iN.Pmis) / baseMVA;
0272     bus(:, LAM_Q) = Lambda.eqnonlin(nne.i1.Qmis:nne.iN.Qmis) / baseMVA;
0273 end
0274 branch(:, MU_SF) = muSf / baseMVA;
0275 branch(:, MU_ST) = muSt / baseMVA;
0276 
0277 %% package up results
0278 nlnN = 2*nb + 2*nl;     %% because muSf and muSt are nl x 1, not nl2 x 1
0279 nlt = length(ilt);
0280 ngt = length(igt);
0281 nbx = length(ibx);
0282 
0283 %% extract multipliers for nonlinear constraints
0284 kl = find(Lambda.eqnonlin < 0);
0285 ku = find(Lambda.eqnonlin > 0);
0286 nl_mu_l = zeros(nlnN, 1);
0287 nl_mu_u = [zeros(2*nb, 1); muSf; muSt];
0288 nl_mu_l(kl) = -Lambda.eqnonlin(kl);
0289 nl_mu_u(ku) =  Lambda.eqnonlin(ku);
0290 
0291 %% extract multipliers for linear constraints
0292 kl = find(Lambda.eqlin < 0);
0293 ku = find(Lambda.eqlin > 0);
0294 
0295 mu_l = zeros(size(u));
0296 mu_l(ieq(kl)) = -Lambda.eqlin(kl);
0297 mu_l(igt) = Lambda.ineqlin(nlt+(1:ngt));
0298 mu_l(ibx) = Lambda.ineqlin(nlt+ngt+nbx+(1:nbx));
0299 
0300 mu_u = zeros(size(u));
0301 mu_u(ieq(ku)) = Lambda.eqlin(ku);
0302 mu_u(ilt) = Lambda.ineqlin(1:nlt);
0303 mu_u(ibx) = Lambda.ineqlin(nlt+ngt+(1:nbx));
0304 
0305 mu = struct( ...
0306   'var', struct('l', Lambda.lower, 'u', Lambda.upper), ...
0307   'nln', struct('l', nl_mu_l, 'u', nl_mu_u), ...
0308   'nle', Lambda.eqnonlin, ...
0309   'nli', Lambda.ineqnonlin, ...
0310   'lin', struct('l', mu_l, 'u', mu_u) );
0311 
0312 results = mpc;
0313 [results.bus, results.branch, results.gen, ...
0314     results.om, results.x, results.mu, results.f] = ...
0315         deal(bus, branch, gen, om, x, mu, f);
0316 
0317 pimul = [ ...
0318   results.mu.nln.l - results.mu.nln.u;
0319   results.mu.lin.l - results.mu.lin.u;
0320   -ones(ny>0, 1);
0321   results.mu.var.l - results.mu.var.u;
0322 ];
0323 raw = struct('xr', x, 'pimul', pimul, 'info', info, 'output', Output);

Generated on Mon 24-Jun-2019 15:58:45 by m2html © 2005