0001 function mdo = most(mdi, mpopt)
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069 t0 = tic;
0070
0071
0072 if nargin < 2
0073 mpopt = mpoption;
0074 end
0075
0076 verbose = mpopt.verbose;
0077
0078 if verbose
0079 fprintf('\n=============================================================================\n');
0080 fprintf( ' MATPOWER Optimal Scheduling Tool -- MOST Version %s\n', mostver());
0081 fprintf( ' A multiperiod stochastic secure OPF with unit commitment\n');
0082 fprintf( ' ----- Built on MATPOWER -----\n');
0083 fprintf( ' by Carlos E. Murillo-Sanchez, Universidad Nacional de Colombia--Manizales\n');
0084 fprintf( ' and Ray D. Zimmerman, Cornell University\n');
0085 fprintf( ' (c) 2012-2019 Power Systems Engineering Research Center (PSERC) \n');
0086 fprintf( '=============================================================================\n');
0087 end
0088
0089
0090 if mpopt.most.solve_model && ~mpopt.most.resolve_new_cost
0091 mpopt = mpoption(mpopt, 'most.build_model', 1);
0092 end
0093 if ~mpopt.most.build_model && ~mpopt.most.solve_model
0094 error('most: Ah ... are you sure you want to do nothing? (either ''most.build_model'' or ''most.solve_model'' must be true)');
0095 end
0096
0097
0098 ng = size(mdi.mpc.gen, 1);
0099 nt = mdi.idx.nt;
0100 ns = length(mdi.Storage.UnitIdx);
0101 mdi.idx.ng = ng;
0102 mdi.idx.ns = ns;
0103 baseMVA = mdi.mpc.baseMVA;
0104 for t = 1:nt
0105 mdi.idx.nj(t) = length(mdi.tstep(t).OpCondSched);
0106 end
0107 if ~isfield(mdi, 'UC') || ~isfield(mdi.UC, 'CommitSched') || ...
0108 isempty(mdi.UC.CommitSched)
0109 if isfield(mdi, 'CommitSched') && ~isempty(mdi.CommitSched)
0110 warning('----- most: MDI.CommitSched has moved to MDI.UC.CommitSched, please update your code. -----');
0111 mdi.UC.CommitSched = mdi.CommitSched;
0112 else
0113 error('most: commitment schedule must be provided in MSPD_IN.UC.CommitSched');
0114 end
0115 end
0116
0117
0118 UC = mpopt.most.uc.run;
0119 if UC == -1
0120 if isempty(mdi.UC.CommitKey)
0121 UC = 0;
0122 else
0123 UC = 1;
0124 end
0125 end
0126 mo = struct(...
0127 'DCMODEL', mpopt.most.dc_model, ...
0128 'IncludeFixedReserves', mpopt.most.fixed_res, ...
0129 'SecurityConstrained', mpopt.most.security_constraints, ...
0130 'QCoordination', mpopt.most.q_coordination, ...
0131 'ForceCyclicStorage', mpopt.most.storage.cyclic, ...
0132 'CyclicCommitment', mpopt.most.uc.cyclic, ...
0133 'ForceExpectedTerminalStorage', mpopt.most.storage.terminal_target, ...
0134 'alpha', mpopt.most.alpha ...
0135 );
0136 if mo.IncludeFixedReserves == -1
0137 if isfield(mdi, 'FixedReserves') && isfield(mdi.FixedReserves(1,1,1), 'req')
0138 mo.IncludeFixedReserves = 1;
0139 else
0140 mo.IncludeFixedReserves = 0;
0141 end
0142 end
0143 if mo.SecurityConstrained == -1
0144 if isfield(mdi, 'cont') && isfield(mdi.cont(1,1), 'contab') && ...
0145 ~isempty(mdi.cont(1,1).contab)
0146 mo.SecurityConstrained = 1;
0147 else
0148 mo.SecurityConstrained = 0;
0149 end
0150 end
0151 if mo.ForceExpectedTerminalStorage == -1
0152 if isempty(mdi.Storage.ExpectedTerminalStorageAim) && ...
0153 isempty(mdi.Storage.ExpectedTerminalStorageMin) && ...
0154 isempty(mdi.Storage.ExpectedTerminalStorageMax)
0155 mo.ForceExpectedTerminalStorage = 0;
0156 else
0157 mo.ForceExpectedTerminalStorage = 1;
0158 end
0159 end
0160 if mo.IncludeFixedReserves && ~(isfield(mdi, 'FixedReserves') && ...
0161 isfield(mdi.FixedReserves(1,1,1), 'req'))
0162 error('most: MDI.FixedReserves(t,j,k) must be specified when MPOPT.most.fixed_res = 1');
0163 end
0164 if mo.SecurityConstrained && ~(isfield(mdi, 'cont') && ...
0165 isfield(mdi.cont(1,1), 'contab') && ~isempty(mdi.cont(1,1).contab))
0166 error('most: MDI.cont(t,j).contab cannot be empty when MPOPT.most.security_constraints = 1');
0167 end
0168 if mo.IncludeFixedReserves && mo.SecurityConstrained
0169 warning('most: Using MPOPT.most.fixed_res = 1 and MPOPT.most.security_constraints = 1 together is not recommended.');
0170 end
0171 if mo.ForceExpectedTerminalStorage == 1;
0172 if mo.ForceCyclicStorage
0173 error('most: storage model cannot be both cyclic and include a terminal target value; must change MPOPT.most.storage.cyclic or MPOPT.most.storage.terminal_target');
0174 end
0175 if ns && isempty(mdi.Storage.ExpectedTerminalStorageAim) && ...
0176 isempty(mdi.Storage.ExpectedTerminalStorageMin) && ...
0177 isempty(mdi.Storage.ExpectedTerminalStorageMax)
0178 error('most: MDI.Storage.ExpectedTerminalStorageAim|Min|Max cannot all be empty when MPOPT.most.storage.terminal_target = 1');
0179 end
0180 if ~isempty(mdi.Storage.ExpectedTerminalStorageAim)
0181 mdi.Storage.ExpectedTerminalStorageMin = mdi.Storage.ExpectedTerminalStorageAim;
0182 mdi.Storage.ExpectedTerminalStorageMax = mdi.Storage.ExpectedTerminalStorageAim;
0183 end
0184 end
0185 if UC && (~isfield(mdi.UC, 'CommitKey') || isempty(mdi.UC.CommitKey))
0186 error('most: cannot run unit commitment without specifying MDI.UC.CommitKey');
0187 end
0188
0189 if ns
0190 if isempty(mdi.Storage.InitialStorage)
0191 error('most: Storage.InitialStorage must be specified');
0192 end
0193 if isempty(mdi.Storage.TerminalChargingPrice0)
0194 mdi.Storage.TerminalChargingPrice0 = mdi.Storage.TerminalStoragePrice;
0195 end
0196 if isempty(mdi.Storage.TerminalDischargingPrice0)
0197 mdi.Storage.TerminalDischargingPrice0 = mdi.Storage.TerminalStoragePrice;
0198 end
0199 if isempty(mdi.Storage.TerminalChargingPriceK)
0200 mdi.Storage.TerminalChargingPriceK = mdi.Storage.TerminalStoragePrice;
0201 end
0202 if isempty(mdi.Storage.TerminalDischargingPriceK)
0203 mdi.Storage.TerminalDischargingPriceK = mdi.Storage.TerminalStoragePrice;
0204 end
0205 if isempty(mdi.Storage.MinStorageLevel)
0206 error('most: Storage.MinStorageLevel must be specified');
0207 else
0208 MinStorageLevel = mdi.Storage.MinStorageLevel;
0209 end
0210 if size(MinStorageLevel, 1) == 1 && ns > 1
0211 MinStorageLevel = ones(ns, 1) * MinStorageLevel;
0212 end
0213 if size(MinStorageLevel, 2) == 1 && nt > 1
0214 MinStorageLevel = MinStorageLevel * ones(1, nt);
0215 end
0216 if isempty(mdi.Storage.MaxStorageLevel)
0217 error('most: Storage.MaxStorageLevel must be specified');
0218 else
0219 MaxStorageLevel = mdi.Storage.MaxStorageLevel;
0220 end
0221 if size(MaxStorageLevel, 1) == 1 && ns > 1
0222 MaxStorageLevel = ones(ns, 1) * MaxStorageLevel;
0223 end
0224 if size(MaxStorageLevel, 2) == 1 && nt > 1
0225 MaxStorageLevel = MaxStorageLevel * ones(1, nt);
0226 end
0227 if isempty(mdi.Storage.InEff)
0228 InEff = 1;
0229 else
0230 InEff = mdi.Storage.InEff;
0231 end
0232 if size(InEff, 1) == 1 && ns > 1
0233 InEff = ones(ns, 1) * InEff;
0234 end
0235 if size(InEff, 2) == 1 && nt > 1
0236 InEff = InEff * ones(1, nt);
0237 end
0238 if isempty(mdi.Storage.OutEff)
0239 OutEff = 1;
0240 else
0241 OutEff = mdi.Storage.OutEff;
0242 end
0243 if size(OutEff, 1) == 1 && ns > 1
0244 OutEff = ones(ns, 1) * OutEff;
0245 end
0246 if size(OutEff, 2) == 1 && nt > 1
0247 OutEff = OutEff * ones(1, nt);
0248 end
0249 if isempty(mdi.Storage.LossFactor)
0250 LossFactor = 0;
0251 else
0252 LossFactor = mdi.Storage.LossFactor;
0253 end
0254 if size(LossFactor, 1) == 1 && ns > 1
0255 LossFactor = ones(ns, 1) * LossFactor;
0256 end
0257 if size(LossFactor, 2) == 1 && nt > 1
0258 LossFactor = LossFactor * ones(1, nt);
0259 end
0260 if isempty(mdi.Storage.rho)
0261 rho = 1;
0262 else
0263 rho = mdi.Storage.rho;
0264 end
0265 if size(rho, 1) == 1 && ns > 1
0266 rho = ones(ns, 1) * rho;
0267 end
0268 if size(rho, 2) == 1 && nt > 1
0269 rho = rho * ones(1, nt);
0270 end
0271 if isempty(mdi.Storage.InitialStorageLowerBound)
0272 if mo.ForceCyclicStorage
0273 mdi.Storage.InitialStorageLowerBound = MinStorageLevel(:, 1);
0274 else
0275 mdi.Storage.InitialStorageLowerBound = mdi.Storage.InitialStorage;
0276 end
0277 end
0278 if isempty(mdi.Storage.InitialStorageUpperBound)
0279 if mo.ForceCyclicStorage
0280 mdi.Storage.InitialStorageUpperBound = MaxStorageLevel(:, 1);
0281 else
0282 mdi.Storage.InitialStorageUpperBound = mdi.Storage.InitialStorage;
0283 end
0284 end
0285
0286 LossCoeff = mdi.Delta_T * LossFactor/2;
0287 beta1 = (1-LossCoeff) ./ (1+LossCoeff);
0288 beta2 = 1 ./ (1+LossCoeff);
0289 beta3 = 1 ./ (1/(1-mo.alpha) + LossCoeff);
0290 beta4 = mo.alpha/(1-mo.alpha) * beta2 .* beta3;
0291 beta5 = beta1 ./ beta2 .* (beta3 + beta4);
0292 beta2EtaIn = mdi.Delta_T * beta2 .* InEff;
0293 beta2overEtaOut = mdi.Delta_T * beta2 ./ OutEff;
0294 beta3EtaIn = mdi.Delta_T * beta3 .* InEff;
0295 beta3overEtaOut = mdi.Delta_T * beta3 ./ OutEff;
0296 beta4EtaIn = mdi.Delta_T * beta4 .* InEff;
0297 beta4overEtaOut = mdi.Delta_T * beta4 ./ OutEff;
0298 diagBeta2EtaIn1 = spdiags(beta2EtaIn(:,1), 0, ns, ns);
0299 diagBeta2overEtaOut1 = spdiags(beta2overEtaOut(:,1), 0, ns, ns);
0300 diagBeta3EtaIn1 = spdiags(beta3EtaIn(:,1), 0, ns, ns);
0301 diagBeta3overEtaOut1 = spdiags(beta3overEtaOut(:,1), 0, ns, ns);
0302 diagBeta4EtaIn1 = spdiags(beta4EtaIn(:,1), 0, ns, ns);
0303 diagBeta4overEtaOut1 = spdiags(beta4overEtaOut(:,1), 0, ns, ns);
0304 end
0305 if ~isfield(mdi.idx, 'ntds') || isempty(mdi.idx.ntds) || ~mdi.idx.ntds
0306 ntds = 0;
0307 nzds = 0;
0308 nyds = 0;
0309 else
0310 ntds = mdi.idx.ntds;
0311 nzds = size(mdi.dstep(1).A, 1);
0312 nyds = size(mdi.dstep(1).D, 1);
0313 end
0314 mdi.idx.ntds = ntds;
0315 mdi.idx.nzds = nzds;
0316 mdi.idx.nyds = nyds;
0317
0318
0319 [PQ, PV, REF, NONE, BUS_I, BUS_TYPE, PD, QD, GS, BS, BUS_AREA, VM, ...
0320 VA, BASE_KV, ZONE, VMAX, VMIN, LAM_P, LAM_Q, MU_VMAX, MU_VMIN] = idx_bus;
0321 [GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
0322 MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
0323 QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;
0324 [F_BUS, T_BUS, BR_R, BR_X, BR_B, RATE_A, RATE_B, RATE_C, ...
0325 TAP, SHIFT, BR_STATUS, PF, QF, PT, QT, MU_SF, MU_ST, ...
0326 ANGMIN, ANGMAX, MU_ANGMIN, MU_ANGMAX] = idx_brch;
0327 [PW_LINEAR, POLYNOMIAL, MODEL, STARTUP, SHUTDOWN, NCOST, COST] = idx_cost;
0328 [CT_LABEL, CT_PROB, CT_TABLE, CT_TBUS, CT_TGEN, CT_TBRCH, CT_TAREABUS, ...
0329 CT_TAREAGEN, CT_TAREABRCH, CT_ROW, CT_COL, CT_CHGTYPE, CT_REP, ...
0330 CT_REL, CT_ADD, CT_NEWVAL, CT_TLOAD, CT_TAREALOAD, CT_LOAD_ALL_PQ, ...
0331 CT_LOAD_FIX_PQ, CT_LOAD_DIS_PQ, CT_LOAD_ALL_P, CT_LOAD_FIX_P, ...
0332 CT_LOAD_DIS_P, CT_TGENCOST, CT_TAREAGENCOST, CT_MODCOST_F, ...
0333 CT_MODCOST_X] = idx_ct;
0334
0335
0336 nb = size(mdi.mpc.bus, 1);
0337 if any(mdi.mpc.bus(:, BUS_I) ~= (1:nb)')
0338 error('most: buses must be numbered consecutively in bus matrix; use ext2int() to convert to internal ordering')
0339 end
0340
0341
0342
0343 mdi.mpc.bus(:, MU_VMIN) = 0;
0344 mdi.mpc.gen(:, MU_QMIN) = 0;
0345 mdi.mpc.branch(:,MU_ANGMAX) = 0;
0346 mdi.mpc.f = 0;
0347 mdi.mpc.et = 0;
0348 mdi.mpc.success = 1;
0349
0350 if mpopt.most.build_model
0351 if verbose
0352 fprintf('- Building indexing structures.\n');
0353 end
0354
0355
0356 mdi.DCMODEL = mo.DCMODEL;
0357 mdi.IncludeFixedReserves = mo.IncludeFixedReserves;
0358 mdi.SecurityConstrained = mo.SecurityConstrained;
0359 mdi.QCoordination = mo.QCoordination;
0360 mdi.Storage.ForceCyclicStorage = mo.ForceCyclicStorage;
0361 mdi.Storage.ForceExpectedTerminalStorage = mo.ForceExpectedTerminalStorage;
0362 mdi.UC.run = UC;
0363 mdi.UC.CyclicCommitment = mo.CyclicCommitment;
0364 mdi.alpha = mo.alpha;
0365 if ~isfield(mdi, 'OpenEnded'), mdi.OpenEnded = 1; end
0366
0367 if UC
0368
0369 if any(mdi.UC.MinUp < 1) && any(mdi.UC.MinUp < 1)
0370 error('most: UC.MinUp and UC.MinDown must all be >= 1');
0371 end
0372
0373
0374
0375
0376 mdi.UC.CommitSched = (mdi.UC.CommitKey >= 0);
0377 if ~mdi.UC.CyclicCommitment
0378 for i = 1:ng
0379 if mdi.UC.InitialState(i) < 0
0380 nn = mdi.UC.MinDown(i) + mdi.UC.InitialState(i);
0381 if nn > 0
0382 mdi.UC.CommitSched(i, 1:nn) = 0;
0383 end
0384 elseif mdi.UC.InitialState(i) > 0
0385 nn = mdi.UC.MinUp(i) - mdi.UC.InitialState(i);
0386 if nn > 0
0387 mdi.UC.CommitSched(i, 1:nn) = 1;
0388 end
0389 end
0390 end
0391 end
0392 end
0393
0394
0395
0396
0397 for t = 1:nt
0398 for j = 1:mdi.idx.nj(t)
0399 mpc = mdi.mpc;
0400 mpc.gen(:, GEN_STATUS) = mdi.UC.CommitSched(:, t);
0401
0402
0403
0404 if isfield(mdi.offer(t), 'gencost') && ~isempty(mdi.offer(t).gencost)
0405 mpc.gencost = mdi.offer(t).gencost;
0406 end
0407
0408 if ~isempty(mdi.tstep(t).OpCondSched(j).tab)
0409 changelist = unique(mdi.tstep(t).OpCondSched(j).tab(:, CT_LABEL));
0410 for label = changelist'
0411 mpc = apply_changes(label, mpc, mdi.tstep(t).OpCondSched(j).tab);
0412 end
0413 end
0414 mdi.flow(t,j,1).mpc = mpc;
0415 mdi.idx.nb(t,j,1) = size(mdi.flow(t,j,1).mpc.bus, 1);
0416 mdi.idx.ny(t,j,1) = length(find(mdi.flow(t,j,1).mpc.gencost(:, MODEL) == PW_LINEAR));
0417 end
0418 end
0419
0420
0421
0422
0423 for t = 1:nt
0424
0425 if ~isfield(mdi.tstep(t), 'TransMask') || isempty(mdi.tstep(t).TransMask)
0426 mdi.tstep(t).TransMask = ones(size(mdi.tstep(t).TransMat));
0427 end
0428
0429 if t == 1
0430 scenario_probs = mdi.tstep(1).TransMat;
0431 else
0432 scenario_probs = mdi.tstep(t).TransMat * mdi.CostWeights(1, 1:mdi.idx.nj(t-1), t-1)';
0433 end
0434 mdi.StepProb(t) = sum(scenario_probs);
0435 if mdi.SecurityConstrained
0436 for j = 1:mdi.idx.nj(t)
0437 [tmp, ii] = sort(mdi.cont(t,j).contab(:, CT_LABEL));
0438 contab = mdi.cont(t,j).contab(ii, :);
0439 rowdecomlist = ones(size(contab,1), 1);
0440 for l = 1:size(contab, 1)
0441 if contab(l, CT_TABLE) == CT_TGEN && contab(l, CT_COL) == GEN_STATUS ...
0442 && contab(l, CT_CHGTYPE) == CT_REP && contab(l, CT_NEWVAL) == 0 ...
0443 && mdi.flow(t,j,1).mpc.gen(contab(l, CT_ROW), GEN_STATUS) <= 0
0444 rowdecomlist(l) = 0;
0445 elseif contab(l, CT_TABLE) == CT_TBRCH && contab(l, CT_COL) == BR_STATUS ...
0446 && contab(l, CT_CHGTYPE) == CT_REP && contab(l, CT_NEWVAL) == 0 ...
0447 && mdi.flow(t,j,1).mpc.branch(contab(l, CT_ROW), BR_STATUS) <= 0
0448 rowdecomlist(l) = 0;
0449 end
0450 end
0451 contab = contab(rowdecomlist ~= 0, :);
0452 mdi.cont(t, j).contab = contab;
0453 clist = unique(contab(:, CT_LABEL));
0454 mdi.idx.nc(t, j) = length(clist);
0455 k = 2;
0456 for label = clist'
0457 mdi.flow(t, j, k).mpc = apply_changes(label, mdi.flow(t, j, 1).mpc, contab);
0458 ii = find( label == contab(:, CT_LABEL) );
0459 mdi.CostWeights(k, j, t) = contab(ii(1), CT_PROB);
0460 mdi.idx.nb(t, j, k) = size(mdi.flow(t, j, k).mpc.bus, 1);
0461 mdi.idx.ny(t, j, k) = length(find(mdi.flow(t, j, 1).mpc.gencost(:, MODEL) == PW_LINEAR));
0462 k = k + 1;
0463 end
0464 mdi.CostWeights(1, j, t) = 1 - sum(mdi.CostWeights(2:mdi.idx.nc(t,j)+1, j, t));
0465 mdi.CostWeights(1:mdi.idx.nc(t,j)+1, j, t) = scenario_probs(j) * mdi.CostWeights(1:mdi.idx.nc(t,j)+1, j, t);
0466 end
0467 else
0468 for j = 1:mdi.idx.nj(t)
0469 mdi.idx.nc(t, j) = 0;
0470 mdi.CostWeights(1, j, t) = scenario_probs(j);
0471 end
0472 end
0473 end
0474
0475
0476 if mdi.SecurityConstrained && mdi.alpha ~= 0
0477 for t = 1:nt
0478 for j = 1:mdi.idx.nj(t)
0479 mdi.CostWeightsAdj(1, j, t) = mdi.CostWeights(1, j, t);
0480 for k = 2:mdi.idx.nc(t,j)+1
0481 mdi.CostWeightsAdj(k, j, t) = (1-mdi.alpha) * mdi.CostWeights(k, j, t);
0482 mdi.CostWeightsAdj(1, j, t) = mdi.CostWeightsAdj(1, j, t) + mdi.alpha * mdi.CostWeights(k, j, t);
0483 end
0484 end
0485 end
0486 else
0487 mdi.CostWeightsAdj = mdi.CostWeights;
0488 end
0489
0490
0491
0492
0493
0494
0495
0496 if UC
0497 if ~isfield(mdi.UC, 'c00') || isempty(mdi.UC.c00)
0498 mdi.UC.c00 = zeros(ng, nt);
0499 for t = 1:nt
0500 for j = 1:mdi.idx.nj(t)
0501 for k = 1:mdi.idx.nc(t,j)+1
0502 mpc = mdi.flow(t,j,k).mpc;
0503 c00tjk = totcost(mpc.gencost, zeros(ng,1));
0504 mdi.UC.c00(:, t) = mdi.UC.c00(:, t) + mdi.CostWeightsAdj(k, j, t) * c00tjk;
0505 c0col = COST + mpc.gencost(:,NCOST) - 1;
0506 ipoly = find(mpc.gencost(:, MODEL) == POLYNOMIAL);
0507 ipwl = find(mpc.gencost(:, MODEL) == PW_LINEAR);
0508 ii = sub2ind(size(mpc.gencost), ipoly, c0col(ipoly));
0509 mpc.gencost(ii) = mpc.gencost(ii) - c00tjk(ipoly);
0510 for i = ipwl'
0511 jj = COST+1:2:COST+2*mpc.gencost(i,NCOST)-1;
0512 mpc.gencost(i, jj) = mpc.gencost(i, jj) - c00tjk(i);
0513 end
0514 mpc.fixed_gencost = c00tjk;
0515 mdi.flow(t,j,k).mpc = mpc;
0516 end
0517 end
0518 end
0519 end
0520 end
0521
0522
0523
0524 mdi.idx.nf_total = 0;
0525 mdi.idx.nb_total = 0;
0526 for t = 1:nt
0527 for j = 1:mdi.idx.nj(t)
0528 mdi.idx.nf_total = mdi.idx.nf_total + (1 + mdi.idx.nc(t,j));
0529 for k = 1:mdi.idx.nc(t,j)+1
0530 mdi.idx.nb_total = mdi.idx.nb_total + size(mdi.flow(t, j, k).mpc.bus, 1);
0531 ii = find(mdi.flow(t,j,k).mpc.gencost(:, MODEL) == PW_LINEAR);
0532 mdi.idx.ny(t,j,k) = length(ii);
0533 end
0534 end
0535 end
0536 mdi.idx.ns_total = ns * mdi.idx.nf_total;
0537
0538
0539
0540
0541
0542
0543
0544
0545 om = opt_model;
0546 nj_max = max(mdi.idx.nj);
0547 nc_max = max(max(mdi.idx.nc));
0548 Ing = speye(ng);
0549 Ins = speye(ns);
0550 if mdi.DCMODEL
0551 om.init_indexed_name('var', 'Va', {nt, nj_max, nc_max+1});
0552 end
0553 om.init_indexed_name('var', 'Pg', {nt, nj_max, nc_max+1});
0554 om.init_indexed_name('var', 'dPp', {nt, nj_max, nc_max+1});
0555 om.init_indexed_name('var', 'dPm', {nt, nj_max, nc_max+1});
0556 om.init_indexed_name('var', 'y', {nt, nj_max, nc_max+1});
0557 if mdi.IncludeFixedReserves
0558 om.init_indexed_name('var', 'R', {nt, nj_max, nc_max+1});
0559 end
0560 for t = 1:nt
0561 for j = 1:mdi.idx.nj(t)
0562
0563 for k = 1:mdi.idx.nc(t,j)+1
0564 if mdi.DCMODEL
0565 iref = find(mdi.flow(t,j,k).mpc.bus(:,BUS_TYPE) == REF);
0566 if verbose && length(iref) > 1
0567 errstr = ['\nmost: Warning: Multiple reference buses.\n', ...
0568 ' For a system with islands, a reference bus in each island\n', ...
0569 ' may help convergence, but in a fully connected system such\n', ...
0570 ' a situation is probably not reasonable.\n\n' ];
0571 fprintf(errstr);
0572 end
0573 Va0 = mdi.flow(t,j,k).mpc.bus(:,VA)*pi/180;
0574 Va_max = Inf(mdi.idx.nb(t,j,k), 1);
0575 Va_min = -Va_max;
0576 Va_min(iref) = mdi.flow(t,j,k).mpc.bus(iref,VA)*pi/180;
0577 Va_max(iref) = Va_min(iref);
0578
0579 om.add_var('Va', {t,j,k}, mdi.idx.nb(t,j,k), Va0, Va_min, Va_max);
0580 end
0581 end
0582
0583 for k = 1:mdi.idx.nc(t,j)+1
0584 mpc = mdi.flow(t,j,k).mpc;
0585 genmask = mpc.gen(:,GEN_STATUS) > 0;
0586 p0 = genmask .* mpc.gen(:,PG) / baseMVA;
0587 if UC
0588 pmin = genmask .* (min(mpc.gen(:, PMIN) / baseMVA, 0) - 1);
0589 pmax = genmask .* (max(mpc.gen(:, PMAX) / baseMVA, 0) + 1);
0590 else
0591 pmin = genmask .* mpc.gen(:, PMIN) / baseMVA;
0592 pmax = genmask .* mpc.gen(:, PMAX) / baseMVA;
0593 end
0594 om.add_var('Pg', {t,j,k}, ng, p0, pmin, pmax);
0595 end
0596 if mdi.IncludeFixedReserves
0597 for k = 1:mdi.idx.nc(t,j)+1
0598 mpc = mdi.flow(t,j,k).mpc;
0599 r = mdi.FixedReserves(t,j,k);
0600 nrz = size(r.req, 1);
0601 if nrz > 1
0602 r.rgens = any(r.zones);
0603 else
0604 r.rgens = r.zones;
0605 end
0606 r.igr = find(r.rgens);
0607 ngr = length(r.igr);
0608
0609 if size(r.zones, 1) ~= nrz
0610 error('most: the number of rows in FixedReserves(%d,%d,%d).req (%d) and FixedReserves(%d,%d,%d).zones (%d) must match', t, j, k, nrz, t, j, k, size(r.zones, 1));
0611 end
0612 if size(r.cost, 1) ~= ng && size(r.cost, 1) ~= ngr
0613 error('most: the number of rows in FixedReserves(%d,%d,%d).cost (%d) must equal the total number of generators (%d) or the number of generators able to provide reserves (%d)', t, j, k, size(r.cost, 1), ng, ngr);
0614 end
0615 if isfield(r, 'qty') && size(r.qty, 1) ~= size(r.cost, 1)
0616 error('most: FixedReserves(%d,%d,%d).cost (%d x 1) and FixedReserves(%d,%d,%d).qty (%d x 1) must be the same dimension', t, j, k, size(r.cost, 1), t, j, k, size(r.qty, 1));
0617 end
0618
0619 if size(r.cost, 1) < ng
0620 r.original.cost = r.cost;
0621 cost = zeros(ng, 1);
0622 cost(r.igr) = r.cost;
0623 r.cost = cost;
0624 if isfield(r, 'qty')
0625 r.original.qty = r.qty;
0626 qty = zeros(ng, 1);
0627 qty(r.igr) = r.qty;
0628 r.qty = qty;
0629 end
0630 end
0631 mdi.FixedReserves(t,j,k).rgens = r.rgens;
0632 mdi.FixedReserves(t,j,k).igr = r.igr;
0633 if isfield(r, 'original')
0634 mdi.FixedReserves(t,j,k).original = r.original;
0635 end
0636 mdi.FixedReserves(t,j,k) = r;
0637 Rmax = Inf(ngr, 1);
0638 kk = find(mpc.gen(r.igr, RAMP_10));
0639 Rmax(kk) = mpc.gen(r.igr(kk), RAMP_10);
0640 kk = find(r.qty(r.igr) < Rmax);
0641 Rmax(kk) = r.qty(r.igr(kk));
0642 Rmax = Rmax / baseMVA;
0643 om.add_var('R', {t,j,k}, ngr, [], zeros(ngr, 1), Rmax);
0644 end
0645 end
0646
0647 for k = 1:mdi.idx.nc(t,j)+1
0648 om.add_var('dPp', {t,j,k}, ng, [], zeros(ng,1), []);
0649 end
0650
0651 for k = 1:mdi.idx.nc(t,j)+1
0652 om.add_var('dPm', {t,j,k}, ng, [], zeros(ng,1), []);
0653 end
0654
0655
0656
0657
0658
0659 for k = 1:mdi.idx.nc(t,j)+1
0660 om.add_var('y', {t,j,k}, mdi.idx.ny(t,j,k), [], [], []);
0661 end
0662 end
0663 end
0664
0665 om.init_indexed_name('var', 'Pc', {nt});
0666 om.init_indexed_name('var', 'Rpp', {nt});
0667 om.init_indexed_name('var', 'Rpm', {nt});
0668 for t = 1:nt
0669 om.add_var('Pc', {t}, ng);
0670
0671
0672 Rpmin = -Inf(ng,1);
0673 off = ones(ng,1);
0674 for j = 1:mdi.idx.nj(t);
0675 for k = 1:mdi.idx.nc(t,j)+1
0676 off = off & mdi.flow(t,j,k).mpc.gen(:, GEN_STATUS) <= 0;
0677 end
0678 end
0679 Rpmin(off == 1) = 0;
0680 om.add_var('Rpp', {t}, ng, [], Rpmin, mdi.offer(t).PositiveActiveReserveQuantity/baseMVA);
0681 om.add_var('Rpm', {t}, ng, [], Rpmin, mdi.offer(t).NegativeActiveReserveQuantity/baseMVA);
0682 end
0683
0684
0685
0686
0687
0688
0689 if ~mdi.OpenEnded
0690 mdi.idx.ntramp = nt;
0691 else
0692 mdi.idx.ntramp = nt - 1;
0693 end
0694 om.init_indexed_name('var', 'Rrp', {mdi.idx.ntramp});
0695 om.init_indexed_name('var', 'Rrm', {mdi.idx.ntramp});
0696 for t = 1:mdi.idx.ntramp
0697 ramp30 = mdi.flow(t,1,1).mpc.gen(:,RAMP_30)*2*mdi.Delta_T;
0698 om.add_var('Rrp', {t}, ng, [], zeros(ng,1), ...
0699 min(mdi.offer(t).PositiveLoadFollowReserveQuantity, ramp30)/baseMVA);
0700 end
0701 for t = 1:mdi.idx.ntramp
0702 ramp30 = mdi.flow(t,1,1).mpc.gen(:,RAMP_30)*2*mdi.Delta_T;
0703 om.add_var('Rrm', {t}, ng, [], zeros(ng,1), ...
0704 min(mdi.offer(t).NegativeLoadFollowReserveQuantity, ramp30)/baseMVA);
0705 end
0706
0707
0708
0709 om.init_indexed_name('var', 'Psc', {nt, nj_max, nc_max+1});
0710 om.init_indexed_name('var', 'Psd', {nt, nj_max, nc_max+1});
0711 for t = 1:nt
0712 for j = 1:mdi.idx.nj(t)
0713 for k = 1:mdi.idx.nc(t,j)+1
0714 if ns
0715 om.add_var('Psc', {t,j,k}, ns, [], [], zeros(ns,1));
0716 end
0717 end
0718 end
0719 end
0720 if ns
0721 for t = 1:nt
0722 for j = 1:mdi.idx.nj(t)
0723 for k = 1:mdi.idx.nc(t,j)+1
0724 om.add_var('Psd', {t,j,k}, ns, [], zeros(ns,1), []);
0725 end
0726 end
0727 end
0728 end
0729
0730
0731 om.init_indexed_name('var', 'Sp', {nt});
0732 om.init_indexed_name('var', 'Sm', {nt});
0733 if ns
0734 for t = 1:nt
0735 om.add_var('Sp', {t}, ns, [], [], MaxStorageLevel(:,t)/baseMVA);
0736 end
0737 for t = 1:nt
0738 om.add_var('Sm', {t}, ns, [], MinStorageLevel(:,t)/baseMVA, []);
0739 end
0740 end
0741
0742
0743 if ns && mdi.Storage.ForceCyclicStorage
0744 om.add_var('S0', ns, [], ...
0745 mdi.Storage.InitialStorageLowerBound / baseMVA, ...
0746 mdi.Storage.InitialStorageUpperBound / baseMVA);
0747 end
0748
0749
0750 if nzds
0751 om.init_indexed_name('var', 'Z', {ntds});
0752 for t = 1:ntds
0753 if t == 1
0754 zmin = mdi.z1;
0755 zmax = mdi.z1;
0756 else
0757 zmin = mdi.dstep(t).zmin;
0758 zmax = mdi.dstep(t).zmax;
0759 end
0760 z0 = (zmax - zmin) / 2;
0761 om.add_var('Z', {t}, nzds, z0, zmin, zmax);
0762 end
0763 end
0764
0765 if UC
0766 om.init_indexed_name('var', 'u', {nt});
0767 om.init_indexed_name('var', 'v', {nt});
0768 om.init_indexed_name('var', 'w', {nt});
0769 vt0 = char('B' * ones(1, ng));
0770 for t = 1:nt
0771 umin = zeros(ng, 1);
0772 umax = ones(ng, 1);
0773
0774 if ~mdi.UC.CyclicCommitment
0775
0776 umin( (mdi.UC.InitialState > 0) & ...
0777 (t+mdi.UC.InitialState-mdi.UC.MinUp <= 0) ) = 1;
0778
0779 umax( (mdi.UC.InitialState < 0) & ...
0780 (t-mdi.UC.InitialState-mdi.UC.MinDown <= 0) ) = 0;
0781 end
0782
0783 iON = find(mdi.UC.CommitKey(:,t) == 2);
0784 iOFF = find(mdi.UC.CommitKey(:,t) == -1);
0785 umin(iON) = 1;
0786 umax(iOFF) = 0;
0787
0788
0789 vt = vt0;
0790 vt(umin == umax) = 'C';
0791
0792 om.add_var('u', {t}, ng, zeros(ng, 1), umin, umax, vt);
0793 end
0794
0795 for t = 1:nt
0796 om.add_var('v', {t}, ng, zeros(ng, 1), zeros(ng, 1), ones(ng, 1));
0797 end
0798
0799 for t = 1:nt
0800 om.add_var('w', {t}, ng, zeros(ng, 1), zeros(ng, 1), ones(ng, 1));
0801 end
0802 end
0803
0804
0805
0806 if mdi.QCoordination
0807 om.init_indexed_name('var', 'Qg', {nt, nj_max, nc_max+1});
0808 for t = 1:nt
0809 for j = 1:mdi.idx.nj(t)
0810 for k = 1:mdi.idx.nc(t,j)+1
0811 mpc = mdi.flow(t,j,k).mpc;
0812 genmask = mpc.gen(:,GEN_STATUS) > 0;
0813 q0 = genmask .* mpc.gen(:, QG) / baseMVA;
0814 if UC
0815 qmin = genmask .* (min(mpc.gen(:, QMIN) / baseMVA, 0) - 1);
0816 qmax = genmask .* (max(mpc.gen(:, QMAX) / baseMVA, 0) + 1);
0817 else
0818 qmin = genmask .* mpc.gen(:, QMIN) / baseMVA;
0819 qmax = genmask .* mpc.gen(:, QMAX) / baseMVA;
0820 end
0821 om.add_var('Qg', {t,j,k}, ng, q0, qmin, qmax);
0822 end
0823 end
0824 end
0825 end
0826 nvars = om.getN('var');
0827 mdi.idx.nvars = nvars;
0828
0829
0830
0831
0832
0833
0834
0835
0836
0837
0838
0839
0840
0841
0842
0843
0844
0845
0846
0847
0848
0849
0850
0851
0852
0853
0854
0855
0856
0857
0858
0859
0860
0861
0862
0863
0864
0865
0866
0867
0868 if verbose
0869 fprintf('- Building expected storage-tracking mechanism.\n');
0870 end
0871 if ns
0872
0873 vv = om.get_idx();
0874 for t = 1:nt
0875 nsxnjt = ns*mdi.idx.nj(t);
0876
0877 G = sparse(nsxnjt, nvars);
0878 H = sparse(nsxnjt, nvars);
0879 B1 = sparse(nsxnjt, nsxnjt);
0880 B2 = sparse(nsxnjt, nsxnjt);
0881 for j = 1:mdi.idx.nj(t)
0882 ii = ((1:ns)'-1)*mdi.idx.nj(t)+j;
0883 jj1 = (vv.i1.Psc(t,j,1):vv.iN.Psc(t,j,1))';
0884 jj2 = (vv.i1.Psd(t,j,1):vv.iN.Psd(t,j,1))';
0885 G = G + sparse(ii, jj1, -mdi.Delta_T * InEff(:,t), nsxnjt, nvars);
0886 H = H + sparse(ii, jj2, -mdi.Delta_T ./ OutEff(:,t), nsxnjt, nvars);
0887 B1 = B1 + sparse(ii, ii, beta1(:,t), nsxnjt, nsxnjt);
0888 B2 = B2 + sparse(ii, ii, beta2(:,t), nsxnjt, nsxnjt);
0889 end
0890 if t == 1
0891
0892 jlist = [];
0893 for i=1:ns
0894 jlist = [ jlist; i*ones(mdi.idx.nj(t),1) ];
0895 end
0896 mdi.tstep(t).Li = sparse((1:nsxnjt)', jlist, 1, nsxnjt, ns);
0897 mdi.tstep(t).Lf = B1 * mdi.tstep(t).Li;
0898 mdi.tstep(t).Mg = sparse(nsxnjt, nvars);
0899 mdi.tstep(t).Mh = sparse(nsxnjt, nvars);
0900 mdi.tstep(t).Ng = B2 * G;
0901 mdi.tstep(t).Nh = B2 * H;
0902 else
0903
0904 D = sparse(nsxnjt, ns*mdi.idx.nj(t-1));
0905 p1 = mdi.CostWeights(1,1:mdi.idx.nj(t-1),t-1)';
0906 p1 = p1 / sum(p1);
0907 p2 = mdi.tstep(t).TransMat * p1;
0908 Di = spdiags(1./p2, 0, mdi.idx.nj(t), mdi.idx.nj(t)) * ...
0909 sparse(mdi.tstep(t).TransMat) * ...
0910 spdiags(p1, 0, mdi.idx.nj(t-1), mdi.idx.nj(t-1));
0911 for i = 1:ns
0912 D((i-1)*mdi.idx.nj(t)+1:i*mdi.idx.nj(t), (i-1)*mdi.idx.nj(t-1)+1:i*mdi.idx.nj(t-1)) = Di;
0913 end
0914
0915 mdi.tstep(t).Li = D * mdi.tstep(t-1).Lf;
0916 mdi.tstep(t).Lf = B1 * mdi.tstep(t).Li;
0917 mdi.tstep(t).Mg = D * mdi.tstep(t-1).Ng;
0918 mdi.tstep(t).Mh = D * mdi.tstep(t-1).Nh;
0919 mdi.tstep(t).Ng = B1 * mdi.tstep(t).Mg + B2 * G;
0920 mdi.tstep(t).Nh = B1 * mdi.tstep(t).Mh + B2 * H;
0921 end
0922 mdi.tstep(t).G = G;
0923 mdi.tstep(t).H = H;
0924 end
0925 end
0926
0927
0928 if verbose
0929 fprintf('- Building constraint submatrices.\n');
0930 end
0931 baseMVA = mdi.mpc.baseMVA;
0932 om.init_indexed_name('lin', 'Pmis', {nt, nj_max, nc_max+1});
0933 if mdi.DCMODEL
0934
0935 if verbose
0936 fprintf(' - Building DC flow constraints.\n');
0937 end
0938 om.init_indexed_name('lin', 'Pf', {nt, nj_max, nc_max+1});
0939 for t = 1:nt
0940 for j = 1:mdi.idx.nj(t)
0941 for k = 1:mdi.idx.nc(t,j)+1
0942
0943 mpc = mdi.flow(t,j,k).mpc;
0944 ion = find(mpc.branch(:, BR_STATUS));
0945 [Bdc, Bl, Psh, PLsh] = makeBdc(baseMVA, mpc.bus, mpc.branch(ion,:));
0946 mdi.flow(t,j,k).PLsh = PLsh;
0947 negCg = sparse(mpc.gen(:,GEN_BUS), (1:ng)', -1, ...
0948 mdi.idx.nb(t,j,k), ng);
0949 A = [Bdc negCg];
0950 b = -(mpc.bus(:,PD)+mpc.bus(:,GS))/baseMVA-Psh;
0951 vs = struct('name', {'Va', 'Pg'}, 'idx', {{t,j,k}, {t,j,k}});
0952 om.add_lin_constraint('Pmis', {t,j,k}, A, b, b, vs);
0953
0954 tmp = mpc.branch(ion,RATE_A)/baseMVA;
0955 iuncon = find(~tmp);
0956 tmp(iuncon) = Inf(size(iuncon));
0957 vs = struct('name', {'Va'}, 'idx', {{t,j,k}});
0958 om.add_lin_constraint('Pf', {t,j,k}, Bl, -tmp-PLsh, tmp-PLsh, vs);
0959 end
0960 end
0961 end
0962 else
0963 if verbose
0964 fprintf(' - Building load balance constraints.\n');
0965 end
0966
0967 for t = 1:nt
0968 for j = 1:mdi.idx.nj(t)
0969 for k = 1:mdi.idx.nc(t,j)+1
0970 mpc = mdi.flow(t,j,k).mpc;
0971 A = sparse(ones(1, ng));
0972 b = 1.0*sum(mpc.bus(:, PD)+mpc.bus(:,GS))/baseMVA;
0973 vs = struct('name', {'Pg'}, 'idx', {{t,j,k}});
0974 om.add_lin_constraint('Pmis', {t,j,k}, A, b, b, vs);
0975 end
0976 end
0977 end
0978 end
0979 if mdi.IncludeFixedReserves
0980 if verbose
0981 fprintf(' - Building fixed zonal reserve constraints.\n');
0982 end
0983 om.init_indexed_name('lin', 'Pg_plus_R', {nt, nj_max, nc_max+1});
0984 om.init_indexed_name('lin', 'Rreq', {nt, nj_max, nc_max+1});
0985 for t = 1:nt
0986 for j = 1:mdi.idx.nj(t)
0987 for k = 1:mdi.idx.nc(t,j)+1
0988
0989 mpc = mdi.flow(t,j,k).mpc;
0990 r = mdi.FixedReserves(t,j,k);
0991 ngr = length(r.igr);
0992 I = speye(ngr);
0993 Ar = sparse(1:ngr, r.igr, 1, ngr, ng);
0994 if UC
0995 A = [Ar I ...
0996 sparse(1:ngr, r.igr, -mpc.gen(r.igr, PMAX) / baseMVA, ngr, ng)];
0997 u = zeros(ngr, 1);
0998 vs = struct('name', {'Pg', 'R', 'u'}, 'idx', {{t,j,k}, {t,j,k}, {t}});
0999 else
1000 A = [Ar I];
1001 u = mpc.gen(r.igr, PMAX) / baseMVA;
1002 vs = struct('name', {'Pg', 'R'}, 'idx', {{t,j,k}, {t,j,k}});
1003 end
1004 om.add_lin_constraint('Pg_plus_R', {t,j,k}, A, [], u, vs);
1005 A = r.zones(:, r.igr);
1006 l = r.req / mpc.baseMVA;
1007 vs = struct('name', {'R'}, 'idx', {{t,j,k}});
1008 om.add_lin_constraint('Rreq', {t,j,k}, A, l, [], vs);
1009 end
1010 end
1011 end
1012 end
1013
1014
1015
1016 if verbose && ~isempty(mdi.Storage.UnitIdx)
1017 fprintf(' - Splitting storage injections into charge/discharge.\n');
1018 end
1019 om.init_indexed_name('lin', 'Ps', {nt, nj_max, nc_max+1});
1020 for t = 1:nt
1021 for j = 1:mdi.idx.nj(t)
1022 for k = 1:mdi.idx.nc(t,j)+1
1023 A = [sparse((1:ns)', mdi.Storage.UnitIdx, -1, ns, ng) Ins Ins];
1024 b = zeros(ns, 1);
1025 vs = struct('name', {'Pg', 'Psc', 'Psd'}, 'idx', {{t,j,k}, {t,j,k}, {t,j,k}});
1026 om.add_lin_constraint('Ps', {t,j,k}, A, b, b, vs);
1027 end
1028 end
1029 end
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039 if verbose
1040 fprintf(' - Building CCV constraints for piecewise-linear costs.\n');
1041 end
1042 om.init_indexed_name('lin', 'ycon', {nt, nj_max, nc_max+1});
1043 for t = 1:nt,
1044 for j = 1:mdi.idx.nj(t)
1045 for k = 1:mdi.idx.nc(t,j)+1
1046 mpc = mdi.flow(t,j,k).mpc;
1047 [A, u] = makeAy(baseMVA, ng, mpc.gencost, 1, [], ng+1);
1048 vs = struct('name', {'Pg', 'y'}, 'idx', {{t,j,k}, {t,j,k}});
1049 om.add_lin_constraint('ycon', {t,j,k}, A, [], u, vs);
1050 end
1051 end
1052 end
1053
1054
1055
1056
1057
1058
1059
1060
1061 if verbose
1062 fprintf(' - Building contingency reserve constraints.\n');
1063 end
1064 om.init_indexed_name('lin', 'rampcont', {nt, nj_max, nc_max+1});
1065 for t =1:nt
1066 for j = 1:mdi.idx.nj(t)
1067 for k = 2:mdi.idx.nc(t,j)+1
1068 ii = find(mdi.flow(t,j,k).mpc.gen(:, GEN_STATUS) > 0);
1069 ngtmp = length(ii);
1070 A = sparse((1:ngtmp)', ii, 1, ngtmp, ng);
1071 u = mdi.flow(t,j,k).mpc.gen(ii,RAMP_10)/baseMVA;
1072 vs = struct('name', {'Pg', 'Pg'}, 'idx', {{t,j,1}, {t,j,k}});
1073 om.add_lin_constraint('rampcont', {t,j,k}, [-A A], -u, u, vs);
1074 end
1075 end
1076 end
1077
1078
1079
1080
1081
1082
1083
1084 om.init_indexed_name('lin', 'dPpRp', {nt, nj_max, nc_max+1});
1085 for t = 1:nt
1086 for j = 1:mdi.idx.nj(t);
1087 for k = 1:mdi.idx.nc(t,j)+1
1088 ii = find(mdi.flow(t,j,k).mpc.gen(:, GEN_STATUS) > 0);
1089 ngtmp = length(ii);
1090 A = sparse((1:ngtmp)', ii, 1, ngtmp, ng);
1091 l = zeros(ngtmp, 1);
1092 vs = struct('name', {'dPp', 'Rpp'}, 'idx', {{t,j,k}, {t}});
1093 om.add_lin_constraint('dPpRp', {t,j,k}, [-A A], l, [], vs);
1094 end
1095 end
1096 end
1097
1098
1099
1100
1101 om.init_indexed_name('lin', 'dPmRm', {nt, nj_max, nc_max+1});
1102 for t = 1:nt
1103 for j = 1:mdi.idx.nj(t);
1104 for k = 1:mdi.idx.nc(t,j)+1
1105 ii = find(mdi.flow(t,j,k).mpc.gen(:, GEN_STATUS) > 0);
1106 ngtmp = length(ii);
1107 A = sparse((1:ngtmp)', ii, 1, ngtmp, ng);
1108 l = zeros(ngtmp, 1);
1109 vs = struct('name', {'dPm', 'Rpm'}, 'idx', {{t,j,k}, {t}});
1110 om.add_lin_constraint('dPmRm', {t,j,k}, [-A A], l, [], vs);
1111 end
1112 end
1113 end
1114
1115
1116
1117 om.init_indexed_name('lin', 'dPdef', {nt, nj_max, nc_max+1});
1118 for t = 1:nt
1119 for j = 1:mdi.idx.nj(t);
1120 for k = 1:mdi.idx.nc(t,j)+1
1121 ii = find(mdi.flow(t,j,k).mpc.gen(:, GEN_STATUS) > 0);
1122 ngtmp = length(ii);
1123 A = sparse((1:ngtmp)', ii, 1, ngtmp, ng);
1124 b = zeros(ngtmp, 1);
1125 vs = struct('name', {'Pg', 'Pc', 'dPp', 'dPm'}, ...
1126 'idx', {{t,j,k}, {t}, {t,j,k}, {t,j,k}});
1127 om.add_lin_constraint('dPdef', {t,j,k}, [A -A -A A], b, b, vs);
1128 end
1129 end
1130 end
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144 if verbose
1145 fprintf(' - Building ramping transitions and reserve constraints.\n');
1146 end
1147 om.init_indexed_name('lin', 'Rrp', {nt, nj_max, nj_max});
1148
1149
1150 for t = 1:nt-1
1151 for j1 = 1:mdi.idx.nj(t)
1152 for j2 = 1:mdi.idx.nj(t+1)
1153 if mdi.tstep(t+1).TransMask(j2,j1)
1154 A = [Ing -Ing Ing];
1155 l = zeros(ng, 1);
1156 vs = struct('name', {'Pg', 'Pg', 'Rrp'}, ...
1157 'idx', {{t,j1,1}, {t+1,j2,1}, {t}});
1158 om.add_lin_constraint('Rrp', {t,j1,j2}, A, l, [], vs);
1159 end
1160 end
1161 end
1162 end
1163
1164
1165
1166
1167
1168 if ~mdi.OpenEnded
1169
1170 for j1 = 1:mdi.idx.nj(nt)
1171 A = [Ing Ing];
1172 l = mdi.TerminalPg/baseMVA;
1173 vs = struct('name', {'Pg', 'Rrp'}, ...
1174 'idx', {{nt,j1,1}, {nt}});
1175 om.add_lin_constraint('Rrp', {nt,j1,1}, A, l, [], vs);
1176 end
1177 end
1178
1179
1180
1181 om.init_indexed_name('lin', 'Rrm', {nt, nj_max, nj_max});
1182
1183
1184 for t = 1:nt-1
1185 for j1 = 1:mdi.idx.nj(t)
1186 for j2 = 1:mdi.idx.nj(t+1)
1187 if mdi.tstep(t+1).TransMask(j2,j1)
1188 A = [-Ing Ing Ing];
1189 l = zeros(ng, 1);
1190 vs = struct('name', {'Pg', 'Pg', 'Rrm'}, ...
1191 'idx', {{t,j1,1}, {t+1,j2,1}, {t}});
1192 om.add_lin_constraint('Rrm', {t,j1,j2}, A, l, [], vs);
1193 end
1194 end
1195 end
1196 end
1197
1198
1199
1200
1201
1202 if ~mdi.OpenEnded
1203
1204 for j1 = 1:mdi.idx.nj(nt)
1205 A = [-Ing Ing];
1206 l = -mdi.TerminalPg/baseMVA;
1207 vs = struct('name', {'Pg', 'Rrm'}, ...
1208 'idx', {{nt,j1,1}, {nt}});
1209 om.add_lin_constraint('Rrm', {nt,j1,1}, A, l, [], vs);
1210 end
1211 end
1212
1213
1214
1215 if ns
1216 if verbose
1217 fprintf(' - Building storage constraints.\n');
1218 end
1219
1220
1221 om.init_indexed_name('lin', 'Sm', {nt, nj_max});
1222 if mdi.Storage.ForceCyclicStorage
1223
1224 for j = 1:mdi.idx.nj(1)
1225 A = [ diagBeta2EtaIn1 diagBeta2overEtaOut1 Ins -spdiags(beta1(:,1), 0, ns, ns)];
1226 u = zeros(ns, 1);
1227 vs = struct('name', {'Psc', 'Psd', 'Sm', 'S0'}, 'idx', {{1,j,1}, {1,j,1}, {1}, {}});
1228 om.add_lin_constraint('Sm', {1,j}, A, [], u, vs);
1229 end
1230 else
1231
1232 for j = 1:mdi.idx.nj(1)
1233 A = [ diagBeta2EtaIn1 diagBeta2overEtaOut1 Ins ];
1234 u = beta1(:,1).*mdi.Storage.InitialStorageLowerBound/baseMVA;
1235 vs = struct('name', {'Psc', 'Psd', 'Sm'}, 'idx', {{1,j,1}, {1,j,1}, {1}});
1236 om.add_lin_constraint('Sm', {1,j}, A, [], u, vs);
1237 end
1238 end
1239
1240
1241
1242 for t = 2:nt
1243 for j = 1:mdi.idx.nj(t)
1244 Mj = mdi.tstep(t).Mg( j:mdi.idx.nj(t):(ns-1)*mdi.idx.nj(t)+j, :) + ...
1245 mdi.tstep(t).Mh( j:mdi.idx.nj(t):(ns-1)*mdi.idx.nj(t)+j, :);
1246 Lij = mdi.tstep(t).Li( j:mdi.idx.nj(t):(ns-1)*mdi.idx.nj(t)+j, :);
1247 diag1minusRhoBeta1 = spdiags((1-rho(:,t)) .* beta1(:,t), 0, ns, ns);
1248 A = sparse([1:ns,1:ns,1:ns,1:ns]', ...
1249 [vv.i1.Psc(t,j,1):vv.iN.Psc(t,j,1), vv.i1.Psd(t,j,1):vv.iN.Psd(t,j,1), vv.i1.Sm(t-1):vv.iN.Sm(t-1), vv.i1.Sm(t):vv.iN.Sm(t)]', ...
1250 [beta2EtaIn(:,t); beta2overEtaOut(:,t); -beta1(:,t).*rho(:,t); ones(ns,1)], ...
1251 ns, nvars) ...
1252 - diag1minusRhoBeta1 * Mj;
1253 if mdi.Storage.ForceCyclicStorage
1254 As0 = sparse(ns, nvars);
1255 As0(:, vv.i1.S0:vv.iN.S0) = -diag1minusRhoBeta1 * Lij;
1256 A = A + As0;
1257 u = zeros(ns, 1);
1258 else
1259 u = full(diag1minusRhoBeta1 * Lij * mdi.Storage.InitialStorage/baseMVA);
1260 end
1261 om.add_lin_constraint('Sm', {t,j}, A, [], u);
1262 end
1263 end
1264
1265 om.init_indexed_name('lin', 'Sp', {nt, nj_max});
1266 if mdi.Storage.ForceCyclicStorage
1267
1268 for j = 1:mdi.idx.nj(1)
1269 A = [ -diagBeta2EtaIn1 -diagBeta2overEtaOut1 -Ins spdiags(beta1(:,1), 0, ns, ns) ];
1270 u = zeros(ns, 1);
1271 vs = struct('name', {'Psc', 'Psd', 'Sp', 'S0'}, 'idx', {{1,j,1}, {1,j,1}, {1}, {}});
1272 om.add_lin_constraint('Sp', {1,j}, A, [], u, vs);
1273 end
1274 else
1275
1276 for j = 1:mdi.idx.nj(1)
1277 A = [ -diagBeta2EtaIn1 -diagBeta2overEtaOut1 -Ins ];
1278 u = -beta1(:,1).*mdi.Storage.InitialStorageUpperBound/baseMVA;
1279 vs = struct('name', {'Psc', 'Psd', 'Sp'}, 'idx', {{1,j,1}, {1,j,1}, {1}});
1280 om.add_lin_constraint('Sp', {1,j}, A, [], u, vs);
1281 end
1282 end
1283
1284
1285
1286 for t = 2:nt
1287 for j = 1:mdi.idx.nj(t)
1288 Mj = mdi.tstep(t).Mg( j:mdi.idx.nj(t):(ns-1)*mdi.idx.nj(t)+j, :) + ...
1289 mdi.tstep(t).Mh( j:mdi.idx.nj(t):(ns-1)*mdi.idx.nj(t)+j, :);
1290 Lij = mdi.tstep(t).Li( j:mdi.idx.nj(t):(ns-1)*mdi.idx.nj(t)+j, :);
1291 diag1minusRhoBeta1 = spdiags((1-rho(:,t)) .* beta1(:,t), 0, ns, ns);
1292 A = sparse([1:ns,1:ns,1:ns,1:ns]', ...
1293 [vv.i1.Psc(t,j,1):vv.iN.Psc(t,j,1), vv.i1.Psd(t,j,1):vv.iN.Psd(t,j,1), vv.i1.Sp(t-1):vv.iN.Sp(t-1), vv.i1.Sp(t):vv.iN.Sp(t)]', ...
1294 [-beta2EtaIn(:,t); -beta2overEtaOut(:,t); beta1(:,t).*rho(:,t); -ones(ns,1)], ...
1295 ns, nvars) ...
1296 + diag1minusRhoBeta1 * Mj;
1297 if mdi.Storage.ForceCyclicStorage
1298 As0 = sparse(ns, nvars);
1299 As0(:, vv.i1.S0:vv.iN.S0) = diag1minusRhoBeta1 * Lij;
1300 A = A + As0;
1301 u = zeros(ns, 1);
1302 else
1303 u = full(-diag1minusRhoBeta1 * Lij * mdi.Storage.InitialStorage/baseMVA);
1304 end
1305 om.add_lin_constraint('Sp', {t,j}, A, [], u);
1306 end
1307 end
1308
1309
1310
1311 om.init_indexed_name('lin', 'contSm', {nt, nj_max, nc_max+1});
1312 for j = 1:mdi.idx.nj(1)
1313 for k = 2:mdi.idx.nc(1,j)+1
1314 if mdi.Storage.ForceCyclicStorage
1315
1316 A = [ diagBeta4EtaIn1 diagBeta4overEtaOut1 diagBeta3EtaIn1 diagBeta3overEtaOut1 -spdiags(beta5(:,1), 0, ns, ns) ];
1317 u = -MinStorageLevel(:,1)/baseMVA;
1318 vs = struct('name', {'Psc', 'Psd', 'Psc', 'Psd', 'S0'}, ...
1319 'idx', {{1,j,1}, {1,j,1}, {1,j,k}, {1,j,k}, {}});
1320 else
1321
1322 A = [ diagBeta4EtaIn1 diagBeta4overEtaOut1 diagBeta3EtaIn1 diagBeta3overEtaOut1 ];
1323 u = (beta5(:,1).*mdi.Storage.InitialStorageLowerBound - MinStorageLevel(:,1))/baseMVA;
1324 vs = struct('name', {'Psc', 'Psd', 'Psc', 'Psd'}, ...
1325 'idx', {{1,j,1}, {1,j,1}, {1,j,k}, {1,j,k}});
1326 end
1327 om.add_lin_constraint('contSm', {1,j,k}, A, [], u, vs);
1328 end
1329 end
1330
1331
1332
1333 for t = 2:nt
1334 for j = 1:mdi.idx.nj(t)
1335 for k = 2:mdi.idx.nc(t,j)+1
1336 Mj = mdi.tstep(t).Mg( j:mdi.idx.nj(t):(ns-1)*mdi.idx.nj(t)+j, :) + ...
1337 mdi.tstep(t).Mh( j:mdi.idx.nj(t):(ns-1)*mdi.idx.nj(t)+j, :);
1338 Lij = mdi.tstep(t).Li( j:mdi.idx.nj(t):(ns-1)*mdi.idx.nj(t)+j, :);
1339 diag1minusRhoBeta5 = spdiags((1-rho(:,t)) .* beta5(:,t), 0, ns, ns);
1340 A = sparse([1:ns,1:ns,1:ns,1:ns,1:ns]', ...
1341 [vv.i1.Psc(t,j,1):vv.iN.Psc(t,j,1), vv.i1.Psd(t,j,1):vv.iN.Psd(t,j,1), vv.i1.Psc(t,j,k):vv.iN.Psc(t,j,k), vv.i1.Psd(t,j,k):vv.iN.Psd(t,j,k), vv.i1.Sm(t-1):vv.iN.Sm(t-1)]', ...
1342 [beta4EtaIn(:,t); beta4overEtaOut(:,t); beta3EtaIn(:,t); beta3overEtaOut(:,t); -beta5(:,t).*rho(:,t)], ...
1343 ns, nvars) ...
1344 - diag1minusRhoBeta5 * Mj;
1345 u = -MinStorageLevel(:,t)/baseMVA;
1346 if mdi.Storage.ForceCyclicStorage
1347 As0 = sparse(ns, nvars);
1348 As0(:, vv.i1.S0:vv.iN.S0) = -diag1minusRhoBeta5 * Lij;
1349 A = A + As0;
1350 else
1351 u = u + diag1minusRhoBeta5 * Lij * mdi.Storage.InitialStorageLowerBound/baseMVA;
1352 end
1353 om.add_lin_constraint('contSm', {t,j,k}, A, [], u);
1354 end
1355 end
1356 end
1357
1358
1359 om.init_indexed_name('lin', 'contSp', {nt, nj_max, nc_max+1});
1360 for j = 1:mdi.idx.nj(1)
1361 for k = 2:mdi.idx.nc(1,j)+1
1362 if mdi.Storage.ForceCyclicStorage
1363
1364 A = [ -diagBeta4EtaIn1 -diagBeta4overEtaOut1 -diagBeta3EtaIn1 -diagBeta3overEtaOut1 spdiags(beta5(:,1), 0, ns, ns)];
1365 u = MaxStorageLevel(:,1)/baseMVA;
1366 vs = struct('name', {'Psc', 'Psd', 'Psc', 'Psd', 'S0'}, ...
1367 'idx', {{1,j,1}, {1,j,1}, {1,j,k}, {1,j,k}, {}});
1368 else
1369
1370 A = [ -diagBeta4EtaIn1 -diagBeta4overEtaOut1 -diagBeta3EtaIn1 -diagBeta3overEtaOut1 ];
1371 u = (MaxStorageLevel(:,1) - beta5(:,1).*mdi.Storage.InitialStorageUpperBound)/baseMVA;
1372 vs = struct('name', {'Psc', 'Psd', 'Psc', 'Psd'}, ...
1373 'idx', {{1,j,1}, {1,j,1}, {1,j,k}, {1,j,k}});
1374 end
1375 om.add_lin_constraint('contSp', {1,j,k}, A, [], u, vs);
1376 end
1377 end
1378
1379
1380
1381 for t = 2:nt
1382 for j = 1:mdi.idx.nj(t)
1383 for k = 2:mdi.idx.nc(t,j)+1
1384 Mj = mdi.tstep(t).Mg( j:mdi.idx.nj(t):(ns-1)*mdi.idx.nj(t)+j, :) + ...
1385 mdi.tstep(t).Mh( j:mdi.idx.nj(t):(ns-1)*mdi.idx.nj(t)+j, :);
1386 Lij = mdi.tstep(t).Li( j:mdi.idx.nj(t):(ns-1)*mdi.idx.nj(t)+j, :);
1387 diag1minusRhoBeta5 = spdiags((1-rho(:,t)) .* beta5(:,t), 0, ns, ns);
1388 A = sparse([1:ns,1:ns,1:ns,1:ns,1:ns]', ...
1389 [vv.i1.Psc(t,j,1):vv.iN.Psc(t,j,1), vv.i1.Psd(t,j,1):vv.iN.Psd(t,j,1), vv.i1.Psc(t,j,k):vv.iN.Psc(t,j,k), vv.i1.Psd(t,j,k):vv.iN.Psd(t,j,k), vv.i1.Sp(t-1):vv.iN.Sp(t-1)]', ...
1390 [-beta4EtaIn(:,t); -beta4overEtaOut(:,t); -beta3EtaIn(:,t); -beta3overEtaOut(:,t); beta5(:,t).*rho(:,t)], ...
1391 ns, nvars) ...
1392 + diag1minusRhoBeta5 * Mj;
1393 u = MaxStorageLevel(:,t)/baseMVA;
1394 if mdi.Storage.ForceCyclicStorage
1395 As0 = sparse(ns, nvars);
1396 As0(:, vv.i1.S0:vv.iN.S0) = diag1minusRhoBeta5 * Lij;
1397 A = A + As0;
1398 else
1399 u = u - diag1minusRhoBeta5 * Lij * mdi.Storage.InitialStorageUpperBound/baseMVA;
1400 end
1401 om.add_lin_constraint('contSp', {t,j,k}, A, [], u);
1402 end
1403 end
1404 end
1405 end
1406
1407
1408
1409 if mdi.Storage.ForceExpectedTerminalStorage && mdi.Storage.ForceCyclicStorage
1410 error('most: ForceExpectedTerminalStorage and ForceCyclicStorage cannot be simultaneously true.');
1411 end
1412 if ns
1413
1414 if mdi.Storage.ForceExpectedTerminalStorage
1415
1416 A = sparse(ns, nvars);
1417 b = zeros(ns, 1);
1418 for j = 1:mdi.idx.nj(nt)
1419 Ngj = mdi.tstep(nt).Ng( j:mdi.idx.nj(nt):(ns-1)*mdi.idx.nj(nt)+j, :);
1420 Nhj = mdi.tstep(nt).Nh( j:mdi.idx.nj(nt):(ns-1)*mdi.idx.nj(nt)+j, :);
1421 Lfj = mdi.tstep(nt).Lf( j:mdi.idx.nj(nt):(ns-1)*mdi.idx.nj(nt)+j, :);
1422 A = A + mdi.CostWeights(1,j,nt) * (Ngj + Nhj);
1423 b = b + mdi.CostWeights(1,j,nt) * (Lfj * mdi.Storage.InitialStorage) / baseMVA;
1424 end
1425 endprob = sum(mdi.CostWeights(1,1:mdi.idx.nj(nt),nt)');
1426 A = (1/endprob) * A;
1427 b = (1/endprob) * b;
1428 l = mdi.Storage.ExpectedTerminalStorageMin / baseMVA - b;
1429 u = mdi.Storage.ExpectedTerminalStorageMax / baseMVA - b;
1430 om.add_lin_constraint('ESnt', A, l, u);
1431 elseif mdi.Storage.ForceCyclicStorage
1432
1433 A = sparse(ns, nvars);
1434 for j = 1:mdi.idx.nj(nt)
1435 Ngj = mdi.tstep(nt).Ng( j:mdi.idx.nj(nt):(ns-1)*mdi.idx.nj(nt)+j, :);
1436 Nhj = mdi.tstep(nt).Nh( j:mdi.idx.nj(nt):(ns-1)*mdi.idx.nj(nt)+j, :);
1437 A = A + mdi.CostWeights(1,j,nt) * (Ngj + Nhj);
1438 end
1439 endprob = sum(mdi.CostWeights(1,1:mdi.idx.nj(nt),nt)');
1440 A = (1/endprob) * A;
1441 for j = 1:mdi.idx.nj(nt)
1442 Lfj = mdi.tstep(nt).Lf(j:mdi.idx.nj(nt):(ns-1)*mdi.idx.nj(nt)+j, :);
1443 A(:, vv.i1.S0:vv.iN.S0) = A(:, vv.i1.S0:vv.iN.S0) ...
1444 + mdi.CostWeights(1,j,nt) * Lfj;
1445 end
1446 A(:, vv.i1.S0:vv.iN.S0) = (1/endprob) * A(:, vv.i1.S0:vv.iN.S0) - speye(ns);
1447 b = zeros(ns, 1);
1448 om.add_lin_constraint('ESnt', A, b, b);
1449 end
1450 end
1451
1452
1453 if nzds || nyds
1454 if verbose
1455 fprintf(' - Building dynamical system constraints.\n');
1456 end
1457
1458
1459
1460 for t = 1:nt
1461 E = sparse(ng, nvars);
1462 for j = 1:mdi.idx.nj(t)
1463 for k = 1:mdi.idx.nc(t,j)+1;
1464 E = E + (mdi.CostWeightsAdj(k,j,t)/mdi.StepProb(t)) * sparse((1:ng)', (vv.i1.Pg(t,j,k):vv.iN.Pg(t,j,k))', 1, ng, nvars);
1465 end
1466 end
1467 mdi.tstep(t).E = E;
1468 end
1469 end
1470
1471
1472
1473 if nzds
1474 om.init_indexed_name('lin', 'DSz', {ntds-1});
1475 b = zeros(nzds, 1);
1476 for t = 1:ntds-1
1477 if t <= nt
1478
1479 A = mdi.dstep(t).B * mdi.tstep(t).E;
1480 else
1481
1482
1483
1484
1485 A = sparse(nzds, nvars);
1486 end
1487 A(:, vv.i1.Z(t):vv.iN.Z(t)) = mdi.dstep(t).A;
1488 A(:, vv.i1.Z(t+1):vv.iN.Z(t+1)) = -speye(nzds);
1489 om.add_lin_constraint('DSz', {t}, A, b, b);
1490 end
1491 end
1492
1493
1494 if nyds
1495 om.init_indexed_name('lin', 'DSy', {ntds});
1496 for t = 1:ntds
1497 if t <= nt
1498 A = mdi.dstep(t).D * mdi.tstep(t).E;
1499 else
1500 A = sparse(nyds, nvars);
1501 end
1502 if nzds
1503 A(:, vv.i1.Z(t):vv.iN.Z(t)) = mdi.dstep(t).C;
1504 end
1505 l = mdi.dstep(t).ymin;
1506 u = mdi.dstep(t).ymax;
1507 om.add_lin_constraint('DSy', {t}, A, l, u);
1508 end
1509 end
1510
1511
1512 if UC
1513 if verbose
1514 fprintf(' - Building unit commitment constraints.\n');
1515 end
1516
1517 om.init_indexed_name('lin', 'uvw', {nt});
1518 for t = 1:nt
1519 if t == 1
1520
1521 if mdi.UC.CyclicCommitment
1522 vs = struct('name', {'u', 'u', 'v', 'w'}, 'idx', {{1}, {nt}, {1}, {1}});
1523 A = [Ing -Ing -Ing Ing];
1524 b = zeros(ng, 1);
1525 else
1526 vs = struct('name', {'u', 'v', 'w'}, 'idx', {{1}, {1}, {1}});
1527 A = [Ing -Ing Ing];
1528 b = (mdi.UC.InitialState > 0);
1529 end
1530 else
1531
1532 vs = struct('name', {'u', 'u', 'v', 'w'}, 'idx', {{t}, {t-1}, {t}, {t}});
1533 A = [Ing -Ing -Ing Ing];
1534 b = zeros(ng, 1);
1535 end
1536 om.add_lin_constraint('uvw', {t}, A, b, b, vs);
1537 end
1538
1539
1540 om.init_indexed_name('lin', 'minup', {nt, ng});
1541 for t = 1:nt
1542 for i = 1:ng
1543 ti = t-mdi.UC.MinUp(i)+1:t;
1544 if mdi.UC.CyclicCommitment
1545 for tt = 1:length(ti)
1546 if ti(tt) < 1
1547 ti(tt) = nt + ti(tt);
1548 end
1549 end
1550 end
1551
1552
1553
1554 ti = ti(ti>0);
1555 vs = struct('name', {'u'}, 'idx', {{t}});
1556 A = sparse(1, i, -1, 1, ng);
1557 for tt = 1:length(ti)
1558 vs(end+1).name = 'v';
1559 vs(end).idx = {ti(tt)};
1560 A = [A sparse(1, i, 1, 1, ng)];
1561 end
1562 om.add_lin_constraint('minup', {t, i}, A, [], 0, vs);
1563 end
1564 end
1565
1566
1567 om.init_indexed_name('lin', 'mindown', {nt, ng});
1568 for t = 1:nt
1569 for i = 1:ng
1570 ti = t-mdi.UC.MinDown(i)+1:t;
1571 if mdi.UC.CyclicCommitment
1572 for tt = 1:length(ti)
1573 if ti(tt) < 1
1574 ti(tt) = nt + ti(tt);
1575 end
1576 end
1577 end
1578
1579
1580
1581 ti = ti(ti>0);
1582 vs = struct('name', {'u'}, 'idx', {{t}});
1583 A = sparse(1, i, 1, 1, ng);
1584 for tt = 1:length(ti)
1585 vs(end+1).name = 'w';
1586 vs(end).idx = {ti(tt)};
1587 A = [A sparse(1, i, 1, 1, ng)];
1588 end
1589 om.add_lin_constraint('mindown', {t, i}, A, [], 1, vs);
1590 end
1591 end
1592
1593
1594
1595
1596 om.init_indexed_name('lin', 'uPmax', {nt, nj_max, nc_max+1});
1597 for t = 1:nt
1598 for j = 1:mdi.idx.nj(t)
1599 for k = 1:mdi.idx.nc(t,j)+1
1600 mpc = mdi.flow(t,j,k).mpc;
1601 ii = find(mpc.gen(:, GEN_STATUS));
1602 nii = length(ii);
1603 vs = struct('name', {'Pg', 'u'}, 'idx', {{t,j,k}, {t}});
1604 A = [ sparse(1:nii, ii, 1, nii, ng) ...
1605 sparse(1:nii, ii, -mpc.gen(ii, PMAX)/baseMVA, nii, ng) ];
1606 u = zeros(nii, 1);
1607 om.add_lin_constraint('uPmax', {t,j,k}, A, [], u, vs);
1608 end
1609 end
1610 end
1611
1612 om.init_indexed_name('lin', 'uPmin', {nt, nj_max, nc_max+1});
1613 for t = 1:nt
1614 for j = 1:mdi.idx.nj(t)
1615 for k = 1:mdi.idx.nc(t,j)+1
1616 mpc = mdi.flow(t,j,k).mpc;
1617 ii = find(mpc.gen(:, GEN_STATUS));
1618 nii = length(ii);
1619 vs = struct('name', {'Pg', 'u'}, 'idx', {{t,j,k}, {t}});
1620 A = [ sparse(1:nii, ii, -1, nii, ng) ...
1621 sparse(1:nii, ii, mpc.gen(ii, PMIN)/baseMVA, nii, ng) ];
1622 u = zeros(nii, 1);
1623 om.add_lin_constraint('uPmin', {t,j,k}, A, [], u, vs);
1624 end
1625 end
1626 end
1627
1628
1629
1630
1631 if mdi.QCoordination
1632 om.init_indexed_name('lin', 'uQmax', {nt, nj_max, nc_max+1});
1633 for t = 1:nt
1634 for j = 1:mdi.idx.nj(t)
1635 for k = 1:mdi.idx.nc(t,j)+1
1636 mpc = mdi.flow(t,j,k).mpc;
1637 ii = find(mpc.gen(:, GEN_STATUS));
1638 nii = length(ii);
1639 vs = struct('name', {'Qg', 'u'}, 'idx', {{t,j,k}, {t}});
1640 A = [ sparse(1:nii, ii, 1, nii, ng) ...
1641 sparse(1:nii, ii, -mpc.gen(ii, QMAX)/baseMVA, nii, ng) ];
1642 u = zeros(nii, 1);
1643 om.add_lin_constraint('uQmax', {t,j,k}, A, [], u, vs);
1644 end
1645 end
1646 end
1647
1648 om.init_indexed_name('lin', 'uQmin', {nt, nj_max, nc_max+1});
1649 for t = 1:nt
1650 for j = 1:mdi.idx.nj(t)
1651 for k = 1:mdi.idx.nc(t,j)+1
1652 mpc = mdi.flow(t,j,k).mpc;
1653 ii = find(mpc.gen(:, GEN_STATUS));
1654 nii = length(ii);
1655 vs = struct('name', {'Qg', 'u'}, 'idx', {{t,j,k}, {t}});
1656 A = [ sparse(1:nii, ii, -1, nii, ng) ...
1657 sparse(1:nii, ii, mpc.gen(ii, QMIN)/baseMVA, nii, ng) ];
1658 u = zeros(nii, 1);
1659 om.add_lin_constraint('uQmin', {t,j,k}, A, [], u, vs);
1660 end
1661 end
1662 end
1663 end
1664 end
1665
1666 if verbose
1667 fprintf('- Building cost structures.\n');
1668 end
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682 if mdi.OpenEnded
1683 om.init_indexed_name('qdc', 'RampWear', {nt, nj_max, nj_max});
1684 else
1685 om.init_indexed_name('qdc', 'RampWear', {nt+1, nj_max, nj_max});
1686 end
1687 for j = 1:mdi.idx.nj(1)
1688 w = mdi.tstep(1).TransMat(j,1);
1689 Q = spdiags(w * baseMVA^2 * mdi.RampWearCostCoeff(:,1), 0, ng, ng);
1690 c = -w * baseMVA * mdi.RampWearCostCoeff(:,1) .* mdi.InitialPg;
1691 vs = struct('name', {'Pg'}, 'idx', {{1,j,1}});
1692 k0 = w * 0.5 * mdi.RampWearCostCoeff(:,1)' * mdi.InitialPg.^2;
1693 om.add_quad_cost('RampWear', {1,j,1}, Q, c, k0, vs);
1694 end
1695
1696 for t = 2:nt
1697 for j2 = 1:mdi.idx.nj(t)
1698 for j1 = 1:mdi.idx.nj(t-1)
1699 w = mdi.tstep(t).TransMat(j2,j1) * mdi.CostWeights(1, j1, t-1);
1700 h = w * baseMVA^2 * mdi.RampWearCostCoeff(:,t);
1701 i = (1:ng)';
1702 j = ng+(1:ng)';
1703 Q = sparse([i;j;i;j], [i;i;j;j], [h;-h;-h;h], 2*ng, 2*ng);
1704 vs = struct('name', {'Pg', 'Pg'}, 'idx', {{t-1,j1,1}, {t,j2,1}});
1705 om.add_quad_cost('RampWear', {t,j1,j2}, Q, zeros(2*ng,1), 0, vs);
1706 end
1707 end
1708 end
1709
1710
1711
1712
1713 if ~mdi.OpenEnded
1714 for j = 1:mdi.idx.nj(nt)
1715 w = mdi.tstep(nt+1).TransMat(1, j) * mdi.CostWeights(1, j, nt);
1716 Q = spdiags(w * baseMVA^2 * mdi.RampWearCostCoeff(:,nt+1), 0, ng, ng);
1717 c = -w * baseMVA * mdi.RampWearCostCoeff(:,nt+1) .* mdi.TerminalPg;
1718 vs = struct('name', {'Pg'}, 'idx', {{nt,j,1}});
1719 k0 = w * 0.5 * mdi.RampWearCostCoeff(:,nt+1)' * mdi.TerminalPg.^2;
1720 om.add_quad_cost('RampWear', {nt+1,j,1}, Q, c, k0, vs);
1721 end
1722 end
1723
1724
1725
1726 om.init_indexed_name('qdc', 'Cp', {nt, nj_max, nc_max+1});
1727 om.init_indexed_name('qdc', 'Cy', {nt, nj_max, nc_max+1});
1728 om.init_indexed_name('qdc', 'Cpp', {nt, nj_max, nc_max+1});
1729 om.init_indexed_name('qdc', 'Cpm', {nt, nj_max, nc_max+1});
1730 if mdi.IncludeFixedReserves
1731 om.init_indexed_name('qdc', 'Rcost', {nt, nj_max, nc_max+1});
1732 end
1733 om.init_indexed_name('qdc', 'Crpp', {nt});
1734 om.init_indexed_name('qdc', 'Crpm', {nt});
1735 for t = 1:nt
1736 for j = 1:mdi.idx.nj(t)
1737 for k = 1:mdi.idx.nc(t,j)+1
1738 w = mdi.CostWeightsAdj(k,j,t);
1739
1740
1741 gc = mdi.flow(t,j,k).mpc.gencost;
1742 ipol = find(gc(:, MODEL) == POLYNOMIAL);
1743 if ~isempty(ipol)
1744 ncost = gc(ipol(1), NCOST);
1745 if all(gc(ipol, NCOST) == ncost)
1746
1747 if ncost > 3
1748 error('most: polynomial generator costs of order higher than quadratic not supported');
1749 elseif ncost == 3
1750 Q = sparse(ipol, ipol, 2 * w * baseMVA^2*gc(ipol, COST), ng, ng);
1751 else
1752 Q = sparse(ng,ng);
1753 end
1754 c = zeros(ng, 1);
1755 if ncost >= 2
1756 c(ipol) = w * baseMVA*gc(ipol, COST+ncost-2);
1757 end
1758 k0 = w * sum(gc(ipol, COST+ncost-1));
1759 else
1760
1761 Q = sparse(ng,ng);
1762 c = zeros(ng, 1);
1763 for i = ipol'
1764 ncost = gc(i, NCOST);
1765 if ncost > 3
1766 error('most: polynomial generator costs of order higher than quadratic not supported');
1767 elseif ncost == 3
1768 Q(i,i) = 2 * w * baseMVA^2*gc(i, COST);
1769 end
1770 if ncost >= 2
1771 c(i) = w * baseMVA*gc(i, COST+ncost-2);
1772 end
1773 k0 = w * gc(i, COST+ncost-1);
1774 end
1775 end
1776 vs = struct('name', {'Pg'}, 'idx', {{t,j,k}});
1777 om.add_quad_cost('Cp', {t,j,k}, Q, c, k0, vs);
1778 end
1779
1780
1781
1782 if mdi.idx.ny(t,j,k)
1783 c = w * ones(mdi.idx.ny(t,j,k),1);
1784 vs = struct('name', {'y'}, 'idx', {{t,j,k}});
1785 om.add_quad_cost('Cy', {t,j,k}, [], c, 0, vs);
1786 end
1787
1788
1789 c = w * baseMVA * mdi.offer(t).PositiveActiveDeltaPrice(:);
1790 vs = struct('name', {'dPp'}, 'idx', {{t,j,k}});
1791 om.add_quad_cost('Cpp', {t,j,k}, [], c, 0, vs);
1792 c = w * baseMVA * mdi.offer(t).NegativeActiveDeltaPrice(:);
1793 vs = struct('name', {'dPm'}, 'idx', {{t,j,k}});
1794 om.add_quad_cost('Cpm', {t,j,k}, [], c, 0, vs);
1795
1796
1797 if mdi.IncludeFixedReserves
1798 c = w * mdi.FixedReserves(t,j,k).cost(r.igr) * baseMVA;
1799 vs = struct('name', {'R'}, 'idx', {{t,j,k}});
1800 om.add_quad_cost('Rcost', {t,j,k}, [], c, 0, vs);
1801 end
1802 end
1803 end
1804
1805
1806 c = baseMVA * mdi.StepProb(t) * mdi.offer(t).PositiveActiveReservePrice(:);
1807 vs = struct('name', {'Rpp'}, 'idx', {{t}});
1808 om.add_quad_cost('Crpp', {t}, [], c, 0, vs);
1809 c = baseMVA * mdi.StepProb(t) * mdi.offer(t).NegativeActiveReservePrice(:);
1810 vs = struct('name', {'Rpm'}, 'idx', {{t}});
1811 om.add_quad_cost('Crpm', {t}, [], c, 0, vs);
1812 end
1813
1814 om.init_indexed_name('qdc', 'Crrp', {mdi.idx.ntramp});
1815 om.init_indexed_name('qdc', 'Crrm', {mdi.idx.ntramp});
1816 for t = 1:nt-1,
1817 c = baseMVA * mdi.StepProb(t+1) * mdi.offer(t).PositiveLoadFollowReservePrice(:);
1818 vs = struct('name', {'Rrp'}, 'idx', {{t}});
1819 om.add_quad_cost('Crrp', {t}, [], c, 0, vs);
1820 c = baseMVA * mdi.StepProb(t+1) * mdi.offer(t).NegativeLoadFollowReservePrice(:);
1821 vs = struct('name', {'Rrm'}, 'idx', {{t}});
1822 om.add_quad_cost('Crrm', {t}, [], c, 0, vs);
1823 end
1824
1825 if ~mdi.OpenEnded
1826
1827 c = baseMVA * mdi.offer(nt).PositiveLoadFollowReservePrice(:);
1828 vs = struct('name', {'Rrp'}, 'idx', {{nt}});
1829 om.add_quad_cost('Crrp', {nt}, [], c, 0, vs);
1830 c = baseMVA * mdi.offer(nt).NegativeLoadFollowReservePrice(:);
1831 vs = struct('name', {'Rrm'}, 'idx', {{nt}});
1832 om.add_quad_cost('Crrm', {nt}, [], c, 0, vs);
1833 end
1834
1835 if UC
1836 om.init_indexed_name('qdc', 'c00', {nt});
1837 om.init_indexed_name('qdc', 'startup', {nt});
1838 om.init_indexed_name('qdc', 'shutdown', {nt});
1839 for t = 1:nt
1840 ww = zeros(ng, 1);
1841 for j = 1:mdi.idx.nj(t)
1842 for k = 1:mdi.idx.nc(t)+1
1843 ww = ww + mdi.CostWeightsAdj(k,j,t) * mdi.flow(t,j,k).mpc.gen(:, GEN_STATUS);
1844 end
1845 end
1846 c = ww.*mdi.UC.c00(:,t);
1847 vs = struct('name', {'u'}, 'idx', {{t}});
1848 om.add_quad_cost('c00', {t}, [], c, 0, vs);
1849 c = mdi.StepProb(t)*mdi.flow(t,1,1).mpc.gencost(:, STARTUP);
1850 vs = struct('name', {'v'}, 'idx', {{t}});
1851 om.add_quad_cost('startup', {t}, [], c, 0, vs);
1852 c = mdi.StepProb(t)*mdi.flow(t,1,1).mpc.gencost(:, SHUTDOWN);
1853 vs = struct('name', {'w'}, 'idx', {{t}});
1854 om.add_quad_cost('shutdown', {t}, [], c, 0, vs);
1855 end
1856 end
1857
1858 if ns
1859 A1 = sparse(ns, ns);
1860 A2 = sparse(ns, nvars);
1861 A3 = sparse(ns, nvars);
1862 A4 = sparse(ns, nvars);
1863 A5 = sparse(ns, nvars);
1864 A6 = sparse(ns, nvars);
1865 A7 = sparse(ns, nvars);
1866
1867 vv = om.get_idx();
1868 for t = 1:nt
1869
1870
1871 for j = 1:mdi.idx.nj(t)
1872
1873 Gtj0 = mdi.tstep(t).G( j:mdi.idx.nj(t):(ns-1)*mdi.idx.nj(t)+j, :);
1874 Htj0 = mdi.tstep(t).H( j:mdi.idx.nj(t):(ns-1)*mdi.idx.nj(t)+j, :);
1875 Litj0 = mdi.tstep(t).Li( j:mdi.idx.nj(t):(ns-1)*mdi.idx.nj(t)+j, :);
1876 Mgtj0 = mdi.tstep(t).Mg( j:mdi.idx.nj(t):(ns-1)*mdi.idx.nj(t)+j, :);
1877 Mhtj0 = mdi.tstep(t).Mh( j:mdi.idx.nj(t):(ns-1)*mdi.idx.nj(t)+j, :);
1878 sum_psi_tjk = sum(mdi.CostWeights(2:mdi.idx.nc(t,j)+1,j,t));
1879 if t == nt
1880 A1 = A1 + mdi.CostWeights(1,j,t) * spdiags(OutEff(:,t) .* beta1(:,t), 0, ns, ns) * Litj0;
1881 A2 = A2 + mdi.CostWeights(1,j,t) * spdiags(OutEff(:,t) .* beta1(:,t), 0, ns, ns) * Mgtj0;
1882 A3 = A3 + mdi.CostWeights(1,j,t) * spdiags(OutEff(:,t) .* beta1(:,t), 0, ns, ns) * Mhtj0;
1883 A4 = A4 + mdi.CostWeights(1,j,t) * spdiags(OutEff(:,t) .* beta2(:,t), 0, ns, ns) * Gtj0;
1884 A5 = A5 + mdi.CostWeights(1,j,t) * spdiags(OutEff(:,t) .* beta2(:,t), 0, ns, ns) * Htj0;
1885 end
1886 A1 = A1 + sum_psi_tjk * spdiags(OutEff(:,t) .* beta5(:,t), 0, ns, ns) * Litj0;
1887 A2 = A2 + sum_psi_tjk * (spdiags(OutEff(:,t) .* beta5(:,t), 0, ns, ns) * Mgtj0 + spdiags(OutEff(:,t) .* beta4(:,t), 0, ns, ns) * Gtj0);
1888 A3 = A3 + sum_psi_tjk * (spdiags(OutEff(:,t) .* beta5(:,t), 0, ns, ns) * Mhtj0 + spdiags(OutEff(:,t) .* beta4(:,t), 0, ns, ns) * Htj0);
1889 for k = 2:mdi.idx.nc(t,j)+1
1890 ii = (1:ns)';
1891 jj1 = (vv.i1.Psc(t,j,k):vv.iN.Psc(t,j,k))';
1892 jj2 = (vv.i1.Psd(t,j,k):vv.iN.Psd(t,j,k))';
1893 Gtjk = sparse(ii, jj1, -mdi.Delta_T * InEff(:,t), ns, nvars);
1894 Htjk = sparse(ii, jj2, -mdi.Delta_T ./ OutEff(:,t), ns, nvars);
1895 A6 = A6 + mdi.CostWeights(k,j,t) * spdiags(OutEff(:,t) .* beta3(:,t), 0, ns, ns) * Gtjk;
1896 A7 = A7 + mdi.CostWeights(k,j,t) * spdiags(OutEff(:,t) .* beta3(:,t), 0, ns, ns) * Htjk;
1897 end
1898 end
1899 end
1900 Cfstor = -baseMVA * ...
1901 (mdi.Storage.TerminalStoragePrice' * (A2 + A3) + ...
1902 mdi.Storage.TerminalChargingPrice0' * A4 + ...
1903 mdi.Storage.TerminalDischargingPrice0' * A5 + ...
1904 mdi.Storage.TerminalChargingPriceK' * A6 + ...
1905 mdi.Storage.TerminalDischargingPriceK' * A7);
1906 if mdi.Storage.ForceCyclicStorage
1907
1908
1909
1910
1911 Cfstor(vv.i1.S0:vv.iN.S0) = ...
1912 Cfstor(vv.i1.S0:vv.iN.S0) + ...
1913 baseMVA * mdi.Storage.InitialStorageCost';
1914
1915
1916 Cfstor(vv.i1.S0:vv.iN.S0) = ...
1917 Cfstor(vv.i1.S0:vv.iN.S0) - ...
1918 baseMVA * mdi.Storage.TerminalStoragePrice' * A1;
1919 end
1920 om.add_quad_cost('fstor', [], Cfstor', 0);
1921
1922
1923
1924 om.init_indexed_name('qdc', 'SpSmFudge', {nt});
1925 c = 1e-2 * [-ones(ns,1); ones(ns,1)];
1926 for t = 1:nt
1927 vs = struct('name', {'Sm', 'Sp'}, 'idx', {{t}, {t}});
1928 om.add_quad_cost('SpSmFudge', {t}, [], c, 0, vs);
1929 end
1930 else
1931 Cfstor = sparse(1, nvars);
1932 end
1933
1934
1935 if verbose
1936 fprintf('- Assembling full set of costs.\n');
1937 end
1938 [Q, c, k0] = om.params_quad_cost();
1939 mdi.QP.Cfstor = Cfstor;
1940 mdi.QP.H1 = Q;
1941 mdi.QP.C1 = c;
1942 mdi.QP.c1 = k0;
1943 end
1944
1945
1946
1947 mdi.QP.H = mdi.QP.H1;
1948 mdi.QP.C = mdi.QP.C1;
1949 mdi.QP.c = mdi.QP.c1;
1950 if isfield(mdi, 'CoordCost') && ...
1951 (~isempty(mdi.CoordCost.Cuser) || ~isempty(mdi.CoordCost.Huser))
1952 if verbose
1953 fprintf('- Adding coordination cost to standard cost.\n');
1954 end
1955 nvuser = length(mdi.CoordCost.Cuser);
1956 nvars = mdi.idx.nvars;
1957 mdi.QP.H = mdi.QP.H + ...
1958 [ mdi.CoordCost.Huser sparse(nvuser,nvars-nvuser) ;
1959 sparse(nvars-nvuser,nvuser) sparse(nvars-nvuser,nvars-nvuser) ];
1960 mdi.QP.C(1:nvuser) = mdi.QP.C(1:nvuser) + mdi.CoordCost.Cuser(:);
1961 mdi.QP.c = mdi.QP.c + mdi.CoordCost.cuser;
1962
1963
1964
1965
1966
1967 end
1968
1969 mdi.om = om;
1970 [vv, ll] = om.get_idx();
1971 if verbose
1972 fprintf('- Assembling full set of constraints.\n');
1973 end
1974 [mdi.QP.A, mdi.QP.l, mdi.QP.u] = om.params_lin_constraint();
1975 if verbose
1976 fprintf('- Assembling full set of variable bounds.\n');
1977 end
1978 [mdi.QP.x0, mdi.QP.xmin, mdi.QP.xmax, mdi.QP.vtype] = om.params_var();
1979
1980 et_setup = toc(t0);
1981 t0 = tic;
1982
1983
1984 mdo = mdi;
1985 if mpopt.most.solve_model
1986
1987 if mdi.DCMODEL ~= mo.DCMODEL
1988 error('MDI.DCMODEL inconsistent with MPOPT.most.dc_model');
1989 end
1990 if mdi.IncludeFixedReserves ~= mo.IncludeFixedReserves
1991 error('MDI.IncludeFixedReserves inconsistent with MPOPT.most.fixed_res (and possible presence of MDI.FixedReserves(t,j,k))');
1992 end
1993 if mdi.SecurityConstrained ~= mo.SecurityConstrained
1994 error('MDI.SecurityConstrained inconsistent with MPOPT.most.security_constraints (and possible presence of MDI.cont(t,j).contab)');
1995 end
1996 if mdi.QCoordination ~= mo.QCoordination
1997 error('MDI.QCoordination inconsistent with MPOPT.most.q_coordination');
1998 end
1999 if mdi.Storage.ForceCyclicStorage ~= mo.ForceCyclicStorage
2000 error('MDI.Storage.ForceCyclicStorage inconsistent with MPOPT.most.storage.cyclic');
2001 end
2002 if mdi.Storage.ForceExpectedTerminalStorage ~= mo.ForceExpectedTerminalStorage
2003 error('MDI.Storage.ForceExpectedTerminalStorage inconsistent with MPOPT.most.storage.terminal_target (and possible presence of MDI.Storage.ExpectedTerminalStorageAim|Min|Max)');
2004 end
2005 if mdi.UC.run ~= UC
2006 error('MDI.UC.run inconsistent with MPOPT.most.uc.run (and possible presence of MDI.UC.CommitKey)');
2007 end
2008
2009 if any(any(mdi.QP.H))
2010 model = 'QP';
2011 else
2012 model = 'LP';
2013 end
2014 if UC
2015 model = ['MI' model];
2016 end
2017 mdo.QP.opt = mpopt2qpopt(mpopt, model, 'most');
2018 if verbose
2019 fprintf('- Calling %s solver.\n\n', model);
2020 fprintf('============================================================================\n\n');
2021 end
2022 if UC
2023 [mdo.QP.x, mdo.QP.f, mdo.QP.exitflag, mdo.QP.output, ...
2024 mdo.QP.lambda ] = miqps_matpower( mdi.QP.H, mdi.QP.C, ...
2025 mdi.QP.A, mdi.QP.l, mdi.QP.u, mdi.QP.xmin, mdi.QP.xmax, ...
2026 [], mdi.QP.vtype, mdo.QP.opt);
2027 else
2028 [mdo.QP.x, mdo.QP.f, mdo.QP.exitflag, mdo.QP.output, ...
2029 mdo.QP.lambda ] = qps_matpower( mdi.QP.H, mdi.QP.C, ...
2030 mdi.QP.A, mdi.QP.l, mdi.QP.u, mdi.QP.xmin, mdi.QP.xmax, ...
2031 [], mdo.QP.opt);
2032 end
2033 if mdo.QP.exitflag > 0
2034 success = 1;
2035 if verbose
2036 fprintf('\n============================================================================\n');
2037 fprintf('- MOST: %s solved successfully.\n', model);
2038 end
2039 else
2040 success = 0;
2041 if verbose
2042 fprintf('\n============================================================================\n');
2043 fprintf('- MOST: %s solver ''%s'' failed with exit flag = %d\n', model, mdo.QP.opt.alg, mdo.QP.exitflag);
2044 end
2045
2046
2047
2048
2049
2050 end
2051
2052 if verbose
2053 fprintf('- Post-processing results.\n');
2054 end
2055 if success
2056 for t = 1:nt
2057 if UC
2058 mdo.UC.CommitSched(:, t) = mdo.QP.x(vv.i1.u(t):vv.iN.u(t));
2059 end
2060 for j = 1:mdi.idx.nj(t)
2061 for k = 1:mdi.idx.nc(t,j)+1
2062 mpc = mdo.flow(t,j,k).mpc;
2063
2064 if mdo.DCMODEL
2065 mpc.bus(:, VM) = 1;
2066 end
2067
2068 mpc.gen(:, PG) = baseMVA * mdo.QP.x(vv.i1.Pg(t,j,k):vv.iN.Pg(t,j,k));
2069
2070 Pmin = mpc.gen(:, PMIN);
2071 Qmin = mpc.gen(:, QMIN);
2072 Qmax = mpc.gen(:, QMAX);
2073 ivl = find( isload(mpc.gen) & (Qmin ~= 0 | Qmax ~= 0) );
2074 Qlim = (Qmin(ivl) == 0) .* Qmax(ivl) + (Qmax(ivl) == 0) .* Qmin(ivl);
2075 mpc.gen(ivl, QG) = mpc.gen(ivl, PG) .* Qlim ./ Pmin(ivl);
2076 if mdo.DCMODEL
2077
2078 mpc.bus(:, VA) = (180/pi) * mdo.QP.x(vv.i1.Va(t,j,k):vv.iN.Va(t,j,k));
2079
2080
2081 price = (mdo.QP.lambda.mu_u(ll.i1.Pmis(t,j,k):ll.iN.Pmis(t,j,k))-mdo.QP.lambda.mu_l(ll.i1.Pmis(t,j,k):ll.iN.Pmis(t,j,k))) / baseMVA;
2082 mpc.bus(:, LAM_P) = price;
2083
2084
2085 mpc.branch(:, PF) = 0;
2086 mpc.branch(:, QF) = 0;
2087 mpc.branch(:, PT) = 0;
2088 mpc.branch(:, QT) = 0;
2089 mpc.branch(:, MU_SF) = 0;
2090 mpc.branch(:, MU_ST) = 0;
2091 ion = find(mpc.branch(:, BR_STATUS));
2092 rows = ll.i1.Pf(t,j,k):ll.iN.Pf(t,j,k);
2093 cols = vv.i1.Va(t,j,k):vv.iN.Va(t,j,k);
2094 lf = baseMVA * (mdo.QP.A(rows,cols) * mdo.QP.x(cols) + mdo.flow(t,j,k).PLsh);
2095 mpc.branch(ion, PF) = lf;
2096 mpc.branch(ion, PT) = -lf;
2097 mpc.branch(ion, MU_SF) = mdo.QP.lambda.mu_u(rows) / baseMVA;
2098 mpc.branch(ion, MU_ST) = mdo.QP.lambda.mu_l(rows) / baseMVA;
2099 else
2100
2101 price = (mdo.QP.lambda.mu_l(ll.i1.Pmis(t,j,k):ll.iN.Pmis(t,j,k))-mdo.QP.lambda.mu_u(ll.i1.Pmis(t,j,k):ll.iN.Pmis(t,j,k))) / baseMVA;
2102 mpc.bus(:, LAM_P) = price;
2103 end
2104 if UC
2105
2106
2107 igenon = find(mpc.gen(:, GEN_STATUS));
2108 u = mdo.QP.x(vv.i1.u(t):vv.iN.u(t));
2109 mpc.gen(igenon, GEN_STATUS) = u(igenon);
2110 gs = mpc.gen(igenon, GEN_STATUS) > 0;
2111 mpc.gen(:, MU_PMAX) = 0;
2112 mpc.gen(:, MU_PMIN) = 0;
2113 mpc.gen(igenon, MU_PMAX) = gs .* ...
2114 mdo.QP.lambda.mu_u(ll.i1.uPmax(t,j,k):ll.iN.uPmax(t,j,k)) / baseMVA;
2115 mpc.gen(igenon, MU_PMIN) = gs .* ...
2116 mdo.QP.lambda.mu_u(ll.i1.uPmin(t,j,k):ll.iN.uPmin(t,j,k)) / baseMVA;
2117 if mdo.QCoordination
2118 mpc.gen(:, MU_QMAX) = 0;
2119 mpc.gen(:, MU_QMIN) = 0;
2120 mpc.gen(igenon, MU_QMAX) = gs .* ...
2121 mdo.QP.lambda.mu_u(ll.i1.uQmax(t,j,k):ll.iN.uQmax(t,j,k)) / baseMVA;
2122 mpc.gen(igenon, MU_QMIN) = gs .* ...
2123 mdo.QP.lambda.mu_u(ll.i1.uQmin(t,j,k):ll.iN.uQmin(t,j,k)) / baseMVA;
2124 end
2125 else
2126 gs = mpc.gen(:, GEN_STATUS) > 0;
2127 mpc.gen(:, MU_PMAX) = gs .* ...
2128 mdo.QP.lambda.upper(vv.i1.Pg(t,j,k):vv.iN.Pg(t,j,k)) / baseMVA;
2129 mpc.gen(:, MU_PMIN) = gs .* ...
2130 mdo.QP.lambda.lower(vv.i1.Pg(t,j,k):vv.iN.Pg(t,j,k)) / baseMVA;
2131 if mdo.QCoordination
2132 mpc.gen(:, MU_QMAX) = gs .* ...
2133 mdo.QP.lambda.upper(vv.i1.Qg(t,j,k):vv.iN.Qg(t,j,k)) / baseMVA;
2134 mpc.gen(:, MU_QMIN) = gs .* ...
2135 mdo.QP.lambda.lower(vv.i1.Qg(t,j,k):vv.iN.Qg(t,j,k)) / baseMVA;
2136 end
2137 end
2138 if mdi.IncludeFixedReserves
2139 z = zeros(ng, 1);
2140 r = mdo.FixedReserves(t,j,k);
2141 r.R = z;
2142 r.prc = z;
2143 r.mu = struct('l', z, 'u', z, 'Pmax', z);
2144 r.totalcost = sum(om.eval_quad_cost(mdo.QP.x, 'Rcost', {t,j,k}));
2145 r.R(r.igr) = mdo.QP.x(vv.i1.R(t,j,k):vv.iN.R(t,j,k)) * baseMVA;
2146 for gg = r.igr
2147 iz = find(r.zones(:, gg));
2148 kk = ll.i1.Rreq(t,j,k):ll.iN.Rreq(t,j,k);
2149 r.prc(gg) = sum(mdo.QP.lambda.mu_l(kk(iz))) / baseMVA;
2150 end
2151 r.mu.l(r.igr) = mdo.QP.lambda.lower(vv.i1.R(t,j,k):vv.iN.R(t,j,k)) / baseMVA;
2152 r.mu.u(r.igr) = mdo.QP.lambda.upper(vv.i1.R(t,j,k):vv.iN.R(t,j,k)) / baseMVA;
2153 r.mu.Pmax(r.igr) = mdo.QP.lambda.mu_u(ll.i1.Pg_plus_R(t,j,k):ll.iN.Pg_plus_R(t,j,k)) / baseMVA;
2154 mpc.reserves = r;
2155 end
2156 mdo.flow(t,j,k).mpc = mpc;
2157 end
2158 end
2159
2160 mdo.results.Pc(:,t) = baseMVA * mdo.QP.x(vv.i1.Pc(t):vv.iN.Pc(t));
2161 mdo.results.Rpp(:,t) = baseMVA * mdo.QP.x(vv.i1.Rpp(t):vv.iN.Rpp(t));
2162 mdo.results.Rpm(:,t) = baseMVA * mdo.QP.x(vv.i1.Rpm(t):vv.iN.Rpm(t));
2163 if ns
2164 mdo.results.Sm(:,t) = baseMVA * mdo.QP.x(vv.i1.Sm(t):vv.iN.Sm(t));
2165 mdo.results.Sp(:,t) = baseMVA * mdo.QP.x(vv.i1.Sp(t):vv.iN.Sp(t));
2166 end
2167 end
2168
2169 for t = 1:mdo.idx.ntramp
2170 mdo.results.Rrp(:,t) = baseMVA * mdo.QP.x(vv.i1.Rrp(t):vv.iN.Rrp(t));
2171 mdo.results.Rrm(:,t) = baseMVA * mdo.QP.x(vv.i1.Rrm(t):vv.iN.Rrm(t));
2172 end
2173
2174
2175 mdo.results.GenPrices = zeros(ng, nt);
2176 mdo.results.CondGenPrices = zeros(ng, nt);
2177 for t = 1:nt
2178 pp = zeros(ng,1);
2179 for j = 1:mdo.idx.nj(t)
2180 for k = 1:mdo.idx.nc(t,j)+1
2181 pp = pp + mdo.flow(t,j,k).mpc.bus(mdo.flow(t,j,k).mpc.gen(:,GEN_BUS), LAM_P);
2182 end
2183 end
2184 mdo.results.GenPrices(:,t) = pp;
2185 mdo.results.CondGenPrices(:, t) = pp / mdo.StepProb(t);
2186 end
2187
2188 mdo.results.RppPrices = zeros(ng, nt);
2189 mdo.results.RpmPrices = zeros(ng, nt);
2190 for t = 1:nt
2191 mdo.results.RppPrices(:, t) = mdo.QP.lambda.lower(vv.i1.Rpp(t):vv.iN.Rpp(t)) / baseMVA;
2192 mdo.results.RpmPrices(:, t) = mdo.QP.lambda.lower(vv.i1.Rpm(t):vv.iN.Rpm(t)) / baseMVA;
2193 for j = 1:mdi.idx.nj(t);
2194 for k = 1:mdi.idx.nc(t,j)+1
2195 ii = find(mdi.flow(t,j,k).mpc.gen(:, GEN_STATUS) > 0);
2196 mdo.results.RppPrices(ii, t) = mdo.results.RppPrices(ii, t) + mdo.QP.lambda.mu_l(ll.i1.dPpRp(t,j,k):ll.iN.dPpRp(t,j,k)) / baseMVA;
2197 mdo.results.RpmPrices(ii, t) = mdo.results.RpmPrices(ii, t) + mdo.QP.lambda.mu_l(ll.i1.dPmRm(t,j,k):ll.iN.dPmRm(t,j,k)) / baseMVA;
2198 end
2199 end
2200 end
2201
2202 mdo.results.RrpPrices = zeros(ng, mdo.idx.ntramp);
2203 mdo.results.RrmPrices = zeros(ng, mdo.idx.ntramp);
2204
2205 for t = 1:nt-1
2206 for j1 = 1:mdo.idx.nj(t)
2207 for j2 = 1:mdo.idx.nj(t+1)
2208 if mdi.tstep(t+1).TransMask(j2,j1)
2209 mdo.results.RrpPrices(:, t) = mdo.results.RrpPrices(:, t) + mdo.QP.lambda.mu_l(ll.i1.Rrp(t,j1,j2):ll.iN.Rrp(t,j1,j2)) / baseMVA;
2210 mdo.results.RrmPrices(:, t) = mdo.results.RrmPrices(:, t) + mdo.QP.lambda.mu_l(ll.i1.Rrm(t,j1,j2):ll.iN.Rrm(t,j1,j2)) / baseMVA;
2211 end
2212 end
2213 end
2214 end
2215
2216 if ~mdo.OpenEnded
2217 for j1 = 1:mdo.idx.nj(nt)
2218 mdo.results.RrpPrices(:, nt) = mdo.results.RrpPrices(:, nt) + mdo.QP.lambda.mu_l(ll.i1.Rrp(nt,j1,1):ll.iN.Rrp(nt,j1,1)) / baseMVA;
2219 mdo.results.RrmPrices(:, nt) = mdo.results.RrmPrices(:, nt) + mdo.QP.lambda.mu_l(ll.i1.Rrm(nt,j1,1):ll.iN.Rrm(nt,j1,1)) / baseMVA;
2220 end
2221 end
2222
2223 mdo.results.ExpectedRampCost = zeros(ng, mdo.idx.ntramp+1);
2224
2225 for j = 1:mdi.idx.nj(1)
2226 w = mdo.tstep(1).TransMat(j,1);
2227 mdo.results.ExpectedRampCost(:, 1) = mdo.results.ExpectedRampCost(:, 1) ...
2228 + 0.5 * w * mdo.RampWearCostCoeff(:,1) .* (mdo.flow(1,j,1).mpc.gen(:,PG) - mdo.InitialPg).^2;
2229 end
2230
2231 for t = 2:nt
2232 for j2 = 1:mdo.idx.nj(t)
2233 for j1 = 1:mdo.idx.nj(t-1)
2234 w = mdo.tstep(t).TransMat(j2,j1) * mdo.CostWeights(1, j1, t-1);
2235 mdo.results.ExpectedRampCost(:, t) = mdo.results.ExpectedRampCost(:, t) ...
2236 + 0.5 * w * mdo.RampWearCostCoeff(:,t) .* (mdo.flow(t,j2,1).mpc.gen(:,PG) - mdo.flow(t-1,j1,1).mpc.gen(:,PG)) .^2;
2237 end
2238 end
2239 end
2240
2241 if ~mdo.OpenEnded
2242 for j = 1:mdi.idx.nj(nt)
2243 w = mdi.tstep(t+1).TransMat(1, j) * mdi.CostWeights(1, j, nt);
2244 mdo.results.ExpectedRampCost(:, nt+1) = 0.5 * w * mdo.RampWearCostCoeff(:,nt+1) .* (mdo.TerminalPg - mdo.flow(nt,j,1).mpc.gen(:,PG)) .^2;
2245 end
2246 end
2247
2248
2249 mdo.results.ExpectedDispatch = zeros(ng, nt);
2250 for t = 1:nt
2251 pp = sum(mdo.CostWeights(1,1:mdo.idx.nj(t),t)');
2252 for j = 1:mdo.idx.nj(t)
2253 mdo.results.ExpectedDispatch(:,t) = mdo.results.ExpectedDispatch(:,t) + ...
2254 mdo.CostWeights(1,j,t)/pp * mdo.flow(t,j,1).mpc.gen(:,PG);
2255 end
2256 end
2257
2258 if ns && mdo.Storage.ForceCyclicStorage
2259 mdo.Storage.InitialStorage = baseMVA * mdo.QP.x(vv.i1.S0:vv.iN.S0);
2260 end
2261
2262 mdo.Storage.ExpectedStorageState = zeros(ns,nt);
2263 if ns
2264 for t = 1:nt
2265 pp = sum(mdo.CostWeights(1,1:mdo.idx.nj(t),t)');
2266 for j = 1:mdo.idx.nj(t)
2267 Lfj = mdo.tstep(t).Lf( j:mdo.idx.nj(t):(ns-1)*mdo.idx.nj(t)+j, :);
2268 Ngj = mdo.tstep(t).Ng( j:mdo.idx.nj(t):(ns-1)*mdo.idx.nj(t)+j, :);
2269 Nhj = mdo.tstep(t).Nh( j:mdo.idx.nj(t):(ns-1)*mdo.idx.nj(t)+j, :);
2270 mdo.Storage.ExpectedStorageState(:,t) = ...
2271 mdo.Storage.ExpectedStorageState(:,t) + ...
2272 baseMVA * mdo.CostWeights(1,j,t)/pp * ...
2273 ( Lfj * mdo.Storage.InitialStorage/baseMVA + ...
2274 (Ngj + Nhj) * mdo.QP.x );
2275 end
2276 end
2277 mdo.Storage.ExpectedStorageDispatch = ...
2278 mdo.results.ExpectedDispatch(mdo.Storage.UnitIdx, :);
2279 end
2280
2281
2282 if ntds
2283 if nzds
2284 mdo.results.Z = zeros(nzds, ntds);
2285 for t = 1:ntds
2286 mdo.results.Z(:,t) = mdo.QP.x(vv.i1.Z(t):vv.iN.Z(t));
2287 end
2288 end
2289 mdo.results.Y = zeros(nyds, ntds);
2290 if nyds
2291 for t = 1:ntds
2292 mdo.results.Y(:, t) = ...
2293 mdo.QP.A(ll.i1.DSy(t):ll.iN.DSy(t), :) * mdo.QP.x;
2294 end
2295 end
2296 end
2297 mdo.results.f = mdo.QP.f;
2298 end
2299 end
2300
2301 mdo.results.success = success;
2302 mdo.results.SetupTime = et_setup;
2303 mdo.results.SolveTime = toc(t0);
2304
2305 if verbose
2306 fprintf('- MOST: Done.\n\n');
2307 end