Home > matpower7.1 > lib > toggle_reserves.m

toggle_reserves

PURPOSE ^

TOGGLE_RESERVES Enable, disable or check status of fixed reserve requirements.

SYNOPSIS ^

function mpc = toggle_reserves(mpc, on_off)

DESCRIPTION ^

TOGGLE_RESERVES Enable, disable or check status of fixed reserve requirements.
   MPC = TOGGLE_RESERVES(MPC, 'on')
   MPC = TOGGLE_RESERVES(MPC, 'off')
   T_F = TOGGLE_RESERVES(MPC, 'status')

   Enables, disables or checks the status of a set of OPF userfcn
   callbacks to implement co-optimization of reserves with fixed zonal
   reserve requirements.

   These callbacks expect to find a 'reserves' field in the input MPC,
   where MPC.reserves is a struct with the following fields:
       zones   nrz x ng, zone(i, j) = 1, if gen j belongs to zone i
                                      0, otherwise
       req     nrz x 1, zonal reserve requirement in MW
       cost    (ng or ngr) x 1, cost of reserves in $/MW
       qty     (ng or ngr) x 1, max quantity of reserves in MW (optional)
   where nrz is the number of reserve zones and ngr is the number of
   generators belonging to at least one reserve zone and ng is the total
   number of generators.

   The 'int2ext' callback also packages up results and stores them in
   the following output fields of results.reserves:
       R       - ng x 1, reserves provided by each gen in MW
       Rmin    - ng x 1, lower limit on reserves provided by each gen, (MW)
       Rmax    - ng x 1, upper limit on reserves provided by each gen, (MW)
       mu.l    - ng x 1, shadow price on reserve lower limit, ($/MW)
       mu.u    - ng x 1, shadow price on reserve upper limit, ($/MW)
       mu.Pmax - ng x 1, shadow price on Pg + R <= Pmax constraint, ($/MW)
       prc     - ng x 1, reserve price for each gen equal to maximum of the
                         shadow prices on the zonal requirement constraint
                         for each zone the generator belongs to

   See also RUNOPF_W_RES, ADD_USERFCN, REMOVE_USERFCN, RUN_USERFCN,
   T_CASE30_USERFCNS.

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SUBFUNCTIONS ^

SOURCE CODE ^

0001 function mpc = toggle_reserves(mpc, on_off)
0002 %TOGGLE_RESERVES Enable, disable or check status of fixed reserve requirements.
0003 %   MPC = TOGGLE_RESERVES(MPC, 'on')
0004 %   MPC = TOGGLE_RESERVES(MPC, 'off')
0005 %   T_F = TOGGLE_RESERVES(MPC, 'status')
0006 %
0007 %   Enables, disables or checks the status of a set of OPF userfcn
0008 %   callbacks to implement co-optimization of reserves with fixed zonal
0009 %   reserve requirements.
0010 %
0011 %   These callbacks expect to find a 'reserves' field in the input MPC,
0012 %   where MPC.reserves is a struct with the following fields:
0013 %       zones   nrz x ng, zone(i, j) = 1, if gen j belongs to zone i
0014 %                                      0, otherwise
0015 %       req     nrz x 1, zonal reserve requirement in MW
0016 %       cost    (ng or ngr) x 1, cost of reserves in $/MW
0017 %       qty     (ng or ngr) x 1, max quantity of reserves in MW (optional)
0018 %   where nrz is the number of reserve zones and ngr is the number of
0019 %   generators belonging to at least one reserve zone and ng is the total
0020 %   number of generators.
0021 %
0022 %   The 'int2ext' callback also packages up results and stores them in
0023 %   the following output fields of results.reserves:
0024 %       R       - ng x 1, reserves provided by each gen in MW
0025 %       Rmin    - ng x 1, lower limit on reserves provided by each gen, (MW)
0026 %       Rmax    - ng x 1, upper limit on reserves provided by each gen, (MW)
0027 %       mu.l    - ng x 1, shadow price on reserve lower limit, ($/MW)
0028 %       mu.u    - ng x 1, shadow price on reserve upper limit, ($/MW)
0029 %       mu.Pmax - ng x 1, shadow price on Pg + R <= Pmax constraint, ($/MW)
0030 %       prc     - ng x 1, reserve price for each gen equal to maximum of the
0031 %                         shadow prices on the zonal requirement constraint
0032 %                         for each zone the generator belongs to
0033 %
0034 %   See also RUNOPF_W_RES, ADD_USERFCN, REMOVE_USERFCN, RUN_USERFCN,
0035 %   T_CASE30_USERFCNS.
0036 
0037 %   MATPOWER
0038 %   Copyright (c) 2009-2016, Power Systems Engineering Research Center (PSERC)
0039 %   by Ray Zimmerman, PSERC Cornell
0040 %
0041 %   This file is part of MATPOWER.
0042 %   Covered by the 3-clause BSD License (see LICENSE file for details).
0043 %   See https://matpower.org for more info.
0044 
0045 if strcmp(upper(on_off), 'ON')
0046     %% check for proper reserve inputs
0047     if ~isfield(mpc, 'reserves') || ~isstruct(mpc.reserves) || ...
0048             ~isfield(mpc.reserves, 'zones') || ...
0049             ~isfield(mpc.reserves, 'req') || ...
0050             ~isfield(mpc.reserves, 'cost')
0051         error('toggle_reserves: case must contain a ''reserves'' field, a struct defining ''zones'', ''req'' and ''cost''');
0052     end
0053     
0054     %% add callback functions
0055     %% note: assumes all necessary data included in 1st arg (mpc, om, results)
0056     %%       so, no additional explicit args are needed
0057     mpc = add_userfcn(mpc, 'ext2int', @userfcn_reserves_ext2int);
0058     mpc = add_userfcn(mpc, 'formulation', @userfcn_reserves_formulation);
0059     mpc = add_userfcn(mpc, 'int2ext', @userfcn_reserves_int2ext);
0060     mpc = add_userfcn(mpc, 'printpf', @userfcn_reserves_printpf);
0061     mpc = add_userfcn(mpc, 'savecase', @userfcn_reserves_savecase);
0062     mpc.userfcn.status.reserves = 1;
0063 elseif strcmp(upper(on_off), 'OFF')
0064     mpc = remove_userfcn(mpc, 'savecase', @userfcn_reserves_savecase);
0065     mpc = remove_userfcn(mpc, 'printpf', @userfcn_reserves_printpf);
0066     mpc = remove_userfcn(mpc, 'int2ext', @userfcn_reserves_int2ext);
0067     mpc = remove_userfcn(mpc, 'formulation', @userfcn_reserves_formulation);
0068     mpc = remove_userfcn(mpc, 'ext2int', @userfcn_reserves_ext2int);
0069     mpc.userfcn.status.reserves = 0;
0070 elseif strcmp(upper(on_off), 'STATUS')
0071     if isfield(mpc, 'userfcn') && isfield(mpc.userfcn, 'status') && ...
0072             isfield(mpc.userfcn.status, 'reserves')
0073         mpc = mpc.userfcn.status.reserves;
0074     else
0075         mpc = 0;
0076     end
0077 else
0078     error('toggle_reserves: 2nd argument must be ''on'', ''off'' or ''status''');
0079 end
0080 
0081 
0082 %%-----  ext2int  ------------------------------------------------------
0083 function mpc = userfcn_reserves_ext2int(mpc, mpopt, args)
0084 %
0085 %   mpc = userfcn_reserves_ext2int(mpc, mpopt, args)
0086 %
0087 %   This is the 'ext2int' stage userfcn callback that prepares the input
0088 %   data for the formulation stage. It expects to find a 'reserves' field
0089 %   in mpc as described above. The optional args are not currently used.
0090 
0091 %% initialize some things
0092 r = mpc.reserves;
0093 o = mpc.order;
0094 ng0 = size(o.ext.gen, 1);   %% number of original gens (+ disp loads)
0095 nrz = size(r.req, 1);       %% number of reserve zones
0096 if nrz > 1
0097     mpc.reserves.rgens = any(r.zones);  %% mask of gens available to provide reserves
0098 else
0099     mpc.reserves.rgens = r.zones;
0100 end
0101 igr = find(mpc.reserves.rgens); %% indices of gens available to provide reserves
0102 ngr = length(igr);              %% number of gens available to provide reserves
0103 
0104 %% check data for consistent dimensions
0105 if size(r.zones, 1) ~= nrz
0106     error('userfcn_reserves_ext2int: the number of rows in mpc.reserves.req (%d) and mpc.reserves.zones (%d) must match', nrz, size(r.zones, 1));
0107 end
0108 if size(r.cost, 1) ~= ng0 && size(r.cost, 1) ~= ngr
0109     error('userfcn_reserves_ext2int: the number of rows in mpc.reserves.cost (%d) must equal the total number of generators (%d) or the number of generators able to provide reserves (%d)', size(r.cost, 1), ng0, ngr);
0110 end
0111 if isfield(r, 'qty') && size(r.qty, 1) ~= size(r.cost, 1)
0112     error('userfcn_reserves_ext2int: mpc.reserves.cost (%d x 1) and mpc.reserves.qty (%d x 1) must be the same dimension', size(r.cost, 1), size(r.qty, 1));
0113 end
0114 
0115 %% convert both cost and qty from ngr x 1 to full ng x 1 vectors if necessary
0116 if size(r.cost, 1) < ng0
0117     mpc.reserves.original.cost = r.cost;    %% save original
0118     cost = zeros(ng0, 1);
0119     cost(igr) = r.cost;
0120     mpc.reserves.cost = cost;
0121     if isfield(r, 'qty')
0122         mpc.reserves.original.qty = r.qty;  %% save original
0123         qty = zeros(ng0, 1);
0124         qty(igr) = r.qty;
0125         mpc.reserves.qty = qty;
0126     end
0127 end
0128 
0129 %%-----  convert stuff to internal indexing  -----
0130 %% convert all reserve parameters (zones, costs, qty, rgens)
0131 if isfield(r, 'qty')
0132     mpc = e2i_field(mpc, {'reserves', 'qty'}, 'gen');
0133 end
0134 mpc = e2i_field(mpc, {'reserves', 'cost'}, 'gen');
0135 mpc = e2i_field(mpc, {'reserves', 'zones'}, 'gen', 2);
0136 mpc = e2i_field(mpc, {'reserves', 'rgens'}, 'gen', 2);
0137 
0138 %% save indices of gens available to provide reserves
0139 mpc.order.ext.reserves.igr = igr;               %% external indexing
0140 mpc.reserves.igr = find(mpc.reserves.rgens);    %% internal indexing
0141 
0142 
0143 %%-----  formulation  --------------------------------------------------
0144 function om = userfcn_reserves_formulation(om, mpopt, args)
0145 %
0146 %   om = userfcn_reserves_formulation(om, mpopt, args)
0147 %
0148 %   This is the 'formulation' stage userfcn callback that defines the
0149 %   user costs and constraints for fixed reserves. It expects to find
0150 %   a 'reserves' field in the mpc stored in om, as described above.
0151 %   By the time it is passed to this callback, mpc.reserves should
0152 %   have two additional fields:
0153 %       igr     1 x ngr, indices of generators available for reserves
0154 %       rgens   1 x ng, 1 if gen avaiable for reserves, 0 otherwise
0155 %   It is also assumed that if cost or qty were ngr x 1, they have been
0156 %   expanded to ng x 1 and that everything has been converted to
0157 %   internal indexing, i.e. all gens are on-line (by the 'ext2int'
0158 %   callback). The optional args are not currently used.
0159 
0160 %% define named indices into data matrices
0161 [GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
0162     MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
0163     QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;
0164 
0165 %% initialize some things
0166 mpc = om.get_mpc();
0167 r = mpc.reserves;
0168 igr = r.igr;                %% indices of gens available to provide reserves
0169 ngr = length(igr);          %% number of gens available to provide reserves
0170 ng  = size(mpc.gen, 1);     %% number of on-line gens (+ disp loads)
0171 
0172 %% variable bounds
0173 Rmin = zeros(ngr, 1);               %% bound below by 0
0174 Rmax = Inf(ngr, 1);                 %% bound above by ...
0175 k = find(mpc.gen(igr, RAMP_10));
0176 Rmax(k) = mpc.gen(igr(k), RAMP_10); %% ... ramp rate and ...
0177 if isfield(r, 'qty')
0178     k = find(r.qty(igr) < Rmax);
0179     Rmax(k) = r.qty(igr(k));        %% ... stated max reserve qty
0180 end
0181 Rmax = Rmax / mpc.baseMVA;
0182 
0183 %% constraints
0184 I = speye(ngr);                     %% identity matrix
0185 Ar = [sparse(1:ngr, igr, 1, ngr, ng) I];
0186 ur = mpc.gen(igr, PMAX) / mpc.baseMVA;
0187 lreq = r.req / mpc.baseMVA;
0188 
0189 %% cost
0190 Cw = r.cost(igr) * mpc.baseMVA;     %% per unit cost coefficients
0191 
0192 %% add them to the model
0193 om.add_var('R', ngr, [], Rmin, Rmax);
0194 om.add_lin_constraint('Pg_plus_R', Ar, [], ur, {'Pg', 'R'});
0195 om.add_lin_constraint('Rreq', r.zones(:, igr), lreq, [], {'R'});
0196 om.add_quad_cost('Rcost', [], Cw, 0, {'R'});
0197 
0198 
0199 %%-----  int2ext  ------------------------------------------------------
0200 function results = userfcn_reserves_int2ext(results, mpopt, args)
0201 %
0202 %   results = userfcn_reserves_int2ext(results, mpopt, args)
0203 %
0204 %   This is the 'int2ext' stage userfcn callback that converts everything
0205 %   back to external indexing and packages up the results. It expects to
0206 %   find a 'reserves' field in the results struct as described for mpc
0207 %   above, including the two additional fields 'igr' and 'rgens'. It also
0208 %   expects the results to contain a variable 'R' and linear constraints
0209 %   'Pg_plus_R' and 'Rreq' which are used to populate output fields in
0210 %   results.reserves. The optional args are not currently used.
0211 
0212 %% initialize some things
0213 r = results.reserves;
0214 
0215 %% grab some info in internal indexing order
0216 igr = r.igr;                %% indices of gens available to provide reserves
0217 ng  = size(results.gen, 1); %% number of on-line gens (+ disp loads)
0218 
0219 %%-----  convert stuff back to external indexing  -----
0220 %% convert all reserve parameters (zones, costs, qty, rgens)
0221 if isfield(r, 'qty')
0222     results = i2e_field(results, {'reserves', 'qty'}, 'gen');
0223 end
0224 results = i2e_field(results, {'reserves', 'cost'}, 'gen');
0225 results = i2e_field(results, {'reserves', 'zones'}, 'gen', 2);
0226 results = i2e_field(results, {'reserves', 'rgens'}, 'gen', 2);
0227 results.order.int.reserves.igr = results.reserves.igr;  %% save internal version
0228 results.reserves.igr = results.order.ext.reserves.igr;  %% use external version
0229 r = results.reserves;       %% update
0230 o = results.order;          %% update
0231 
0232 %% grab same info in external indexing order
0233 igr0 = r.igr;               %% indices of gens available to provide reserves
0234 ng0  = size(o.ext.gen, 1);  %% number of gens (+ disp loads)
0235 
0236 %%-----  results post-processing  -----
0237 %% get the results (per gen reserves, multipliers) with internal gen indexing
0238 %% and convert from p.u. to per MW units
0239 [R0, Rl, Ru] = results.om.params_var('R');
0240 R       = zeros(ng, 1);
0241 Rmin    = zeros(ng, 1);
0242 Rmax    = zeros(ng, 1);
0243 mu_l    = zeros(ng, 1);
0244 mu_u    = zeros(ng, 1);
0245 mu_Pmax = zeros(ng, 1);
0246 R(igr)       = results.var.val.R * results.baseMVA;
0247 Rmin(igr)    = Rl * results.baseMVA;
0248 Rmax(igr)    = Ru * results.baseMVA;
0249 mu_l(igr)    = results.var.mu.l.R / results.baseMVA;
0250 mu_u(igr)    = results.var.mu.u.R / results.baseMVA;
0251 mu_Pmax(igr) = results.lin.mu.u.Pg_plus_R / results.baseMVA;
0252 
0253 %% store in results in results struct
0254 z = zeros(ng0, 1);
0255 results.reserves.R       = i2e_data(results, R, z, 'gen');
0256 results.reserves.Rmin    = i2e_data(results, Rmin, z, 'gen');
0257 results.reserves.Rmax    = i2e_data(results, Rmax, z, 'gen');
0258 results.reserves.mu.l    = i2e_data(results, mu_l, z, 'gen');
0259 results.reserves.mu.u    = i2e_data(results, mu_u, z, 'gen');
0260 results.reserves.mu.Pmax = i2e_data(results, mu_Pmax, z, 'gen');
0261 results.reserves.prc     = z;
0262 for k = igr0
0263     iz = find(r.zones(:, k));
0264     results.reserves.prc(k) = sum(results.lin.mu.l.Rreq(iz)) / results.baseMVA;
0265 end
0266 results.reserves.totalcost = sum(results.qdc.Rcost);
0267 
0268 %% replace ng x 1 cost, qty with ngr x 1 originals
0269 if isfield(r, 'original')
0270     if isfield(r, 'qty')
0271         results.reserves.qty = r.original.qty;
0272     end
0273     results.reserves.cost = r.original.cost;
0274     results.reserves = rmfield(results.reserves, 'original');
0275 end
0276 
0277 
0278 %%-----  printpf  ------------------------------------------------------
0279 function results = userfcn_reserves_printpf(results, fd, mpopt, args)
0280 %
0281 %   results = userfcn_reserves_printpf(results, fd, mpopt, args)
0282 %
0283 %   This is the 'printpf' stage userfcn callback that pretty-prints the
0284 %   results. It expects a results struct, a file descriptor and a MATPOWER
0285 %   options struct. The optional args are not currently used.
0286 
0287 %% define named indices into data matrices
0288 [GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
0289     MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
0290     QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;
0291 
0292 %%-----  print results  -----
0293 r = results.reserves;
0294 nrz = size(r.req, 1);
0295 isOPF           = isfield(results, 'f') && ~isempty(results.f);
0296 SUPPRESS        = mpopt.out.suppress_detail;
0297 if SUPPRESS == -1
0298     if size(results.bus, 1) > 500
0299         SUPPRESS = 1;
0300     else
0301         SUPPRESS = 0;
0302     end
0303 end
0304 OUT_ALL         = mpopt.out.all;
0305 OUT_FORCE       = mpopt.out.force;
0306 OUT_RES         = OUT_ALL == 1 || (OUT_ALL == -1 && ~SUPPRESS && (mpopt.out.bus || mpopt.out.gen));
0307 
0308 if isOPF && OUT_RES && (results.success || OUT_FORCE)
0309     fprintf(fd, '\n================================================================================');
0310     fprintf(fd, '\n|     Reserves                                                                 |');
0311     fprintf(fd, '\n================================================================================');
0312     fprintf(fd, '\n Gen   Bus   Status  Reserves   Price');
0313     fprintf(fd, '\n  #     #              (MW)     ($/MW)     Included in Zones ...');
0314     fprintf(fd, '\n----  -----  ------  --------  --------   ------------------------');
0315     for k = r.igr
0316         iz = find(r.zones(:, k));
0317         fprintf(fd, '\n%3d %6d     %2d ', k, results.gen(k, GEN_BUS), results.gen(k, GEN_STATUS));
0318         if results.gen(k, GEN_STATUS) > 0 && abs(results.reserves.R(k)) > 1e-6
0319             fprintf(fd, '%10.2f', results.reserves.R(k));
0320         else
0321             fprintf(fd, '       -  ');
0322         end
0323         fprintf(fd, '%10.2f     ', results.reserves.prc(k));
0324         for i = 1:length(iz)
0325             if i ~= 1
0326                 fprintf(fd, ', ');
0327             end
0328             fprintf(fd, '%d', iz(i));
0329         end
0330     end
0331     fprintf(fd, '\n                     --------');
0332     fprintf(fd, '\n            Total:%10.2f              Total Cost: $%.2f', ...
0333         sum(results.reserves.R(r.igr)), results.reserves.totalcost);
0334     fprintf(fd, '\n');
0335     
0336     fprintf(fd, '\nZone  Reserves   Price  ');
0337     fprintf(fd, '\n  #     (MW)     ($/MW) ');
0338     fprintf(fd, '\n----  --------  --------');
0339     for k = 1:nrz
0340         iz = find(r.zones(k, :));     %% gens in zone k
0341         fprintf(fd, '\n%3d%10.2f%10.2f', k, sum(results.reserves.R(iz)), ...
0342                     results.lin.mu.l.Rreq(k) / results.baseMVA);
0343     end
0344     fprintf(fd, '\n');
0345     
0346     fprintf(fd, '\n================================================================================');
0347     fprintf(fd, '\n|     Reserve Limits                                                           |');
0348     fprintf(fd, '\n================================================================================');
0349     fprintf(fd, '\n Gen   Bus   Status  Rmin mu     Rmin    Reserves    Rmax    Rmax mu   Pmax mu ');
0350     fprintf(fd, '\n  #     #             ($/MW)     (MW)      (MW)      (MW)     ($/MW)    ($/MW) ');
0351     fprintf(fd, '\n----  -----  ------  --------  --------  --------  --------  --------  --------');
0352     for k = r.igr
0353         fprintf(fd, '\n%3d %6d     %2d ', k, results.gen(k, GEN_BUS), results.gen(k, GEN_STATUS));
0354         if results.gen(k, GEN_STATUS) > 0 && results.reserves.mu.l(k) > 1e-6
0355             fprintf(fd, '%10.2f', results.reserves.mu.l(k));
0356         else
0357             fprintf(fd, '       -  ');
0358         end
0359         fprintf(fd, '%10.2f', results.reserves.Rmin(k));
0360         if results.gen(k, GEN_STATUS) > 0 && abs(results.reserves.R(k)) > 1e-6
0361             fprintf(fd, '%10.2f', results.reserves.R(k));
0362         else
0363             fprintf(fd, '       -  ');
0364         end
0365         fprintf(fd, '%10.2f', results.reserves.Rmax(k));
0366         if results.gen(k, GEN_STATUS) > 0 && results.reserves.mu.u(k) > 1e-6
0367             fprintf(fd, '%10.2f', results.reserves.mu.u(k));
0368         else
0369             fprintf(fd, '       -  ');
0370         end
0371         if results.gen(k, GEN_STATUS) > 0 && results.reserves.mu.Pmax(k) > 1e-6
0372             fprintf(fd, '%10.2f', results.reserves.mu.Pmax(k));
0373         else
0374             fprintf(fd, '       -  ');
0375         end
0376     end
0377     fprintf(fd, '\n                                         --------');
0378     fprintf(fd, '\n                                Total:%10.2f', sum(results.reserves.R(r.igr)));
0379     fprintf(fd, '\n');
0380 end
0381 
0382 
0383 %%-----  savecase  -----------------------------------------------------
0384 function mpc = userfcn_reserves_savecase(mpc, fd, prefix, args)
0385 %
0386 %   mpc = userfcn_reserves_savecase(mpc, fd, prefix, args)
0387 %
0388 %   This is the 'savecase' stage userfcn callback that prints the M-file
0389 %   code to save the 'reserves' field in the case file. It expects a
0390 %   MATPOWER case struct (mpc), a file descriptor and variable prefix
0391 %   (usually 'mpc.'). The optional args are not currently used.
0392 
0393 r = mpc.reserves;
0394 
0395 fprintf(fd, '\n%%%%-----  Reserve Data  -----%%%%\n');
0396 fprintf(fd, '%%%% reserve zones, element i, j is 1 if gen j is in zone i, 0 otherwise\n');
0397 fprintf(fd, '%sreserves.zones = [\n', prefix);
0398 template = '';
0399 for i = 1:size(r.zones, 2)
0400     template = [template, '\t%d'];
0401 end
0402 template = [template, ';\n'];
0403 fprintf(fd, template, r.zones.');
0404 fprintf(fd, '];\n');
0405 
0406 fprintf(fd, '\n%%%% reserve requirements for each zone in MW\n');
0407 fprintf(fd, '%sreserves.req = [\t%g', prefix, r.req(1));
0408 if length(r.req) > 1
0409     fprintf(fd, ';\t%g', r.req(2:end));
0410 end
0411 fprintf(fd, '\t];\n');
0412 
0413 fprintf(fd, '\n%%%% reserve costs in $/MW for each gen that belongs to at least 1 zone\n');
0414 fprintf(fd, '%%%% (same order as gens, but skipping any gen that does not belong to any zone)\n');
0415 fprintf(fd, '%sreserves.cost = [\t%g', prefix, r.cost(1));
0416 if length(r.cost) > 1
0417     fprintf(fd, ';\t%g', r.cost(2:end));
0418 end
0419 fprintf(fd, '\t];\n');
0420 
0421 if isfield(r, 'qty')
0422     fprintf(fd, '\n%%%% OPTIONAL max reserve quantities for each gen that belongs to at least 1 zone\n');
0423     fprintf(fd, '%%%% (same order as gens, but skipping any gen that does not belong to any zone)\n');
0424     fprintf(fd, '%sreserves.qty = [\t%g', prefix, r.qty(1));
0425     if length(r.qty) > 1
0426         fprintf(fd, ';\t%g', r.qty(2:end));
0427     end
0428     fprintf(fd, '\t];\n');
0429 end
0430 
0431 %% save output fields for solved case
0432 if isfield(r, 'R')
0433     if exist('serialize', 'file') == 2
0434         fprintf(fd, '\n%%%% solved values\n');
0435         fprintf(fd, '%sreserves.R = %s\n', prefix, serialize(r.R));
0436         fprintf(fd, '%sreserves.Rmin = %s\n', prefix, serialize(r.Rmin));
0437         fprintf(fd, '%sreserves.Rmax = %s\n', prefix, serialize(r.Rmax));
0438         fprintf(fd, '%sreserves.mu.l = %s\n', prefix, serialize(r.mu.l));
0439         fprintf(fd, '%sreserves.mu.u = %s\n', prefix, serialize(r.mu.u));
0440         fprintf(fd, '%sreserves.prc = %s\n', prefix, serialize(r.prc));
0441         fprintf(fd, '%sreserves.totalcost = %s\n', prefix, serialize(r.totalcost));
0442     else
0443         url = 'https://www.mathworks.com/matlabcentral/fileexchange/12063-serialize';
0444         warning('MATPOWER:serialize', ...
0445             'userfcn_reserves_savecase: Cannot save the ''reserves'' output fields without the ''serialize'' function, which is available as a free download from:\n<%s>\n\n', url);
0446     end
0447 end

Generated on Fri 09-Oct-2020 11:21:31 by m2html © 2005