From 677d424c54d2b3b0ab46c30a3c75a944bad61815 Mon Sep 17 00:00:00 2001 From: Ray Zimmerman Date: Wed, 9 Oct 2024 09:54:23 -0600 Subject: [PATCH] git subrepo pull --branch=master most subrepo: subdir: "most" merged: "0e7620dc" upstream: origin: "git@github.com:MATPOWER/most" branch: "master" commit: "0e7620dc" git-subrepo: version: "0.4.9" origin: "https://github.com/ingydotnet/git-subrepo" commit: "b00d41b" --- most/.gitrepo | 6 +- most/CHANGES.md | 18 ++++++ most/docs/sphinx/source/conf.py | 1 + most/docs/src/MOST-manual/MOST-manual.tex | 40 ++++++++++-- most/lib/most.m | 78 +++++++++++++---------- most/lib/most_summary.m | 41 ++++++------ most/lib/mostver.m | 4 +- most/lib/t/t_most_uc.m | 7 +- 8 files changed, 130 insertions(+), 65 deletions(-) diff --git a/most/.gitrepo b/most/.gitrepo index 7b09ab30..bc1a1897 100644 --- a/most/.gitrepo +++ b/most/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = git@github.com:MATPOWER/most branch = master - commit = 9387fc7e0152fe1d782236dbea280fd3bd6ee611 - parent = 650721826ff477ee4cf393a028ea7d497fcd9b7a + commit = 0e7620dc1d6e8be0ed36a49bcd4b64d5aa9abef8 + parent = c1ecfc9c19123886b8f40105f0515409dbe14fbc method = merge - cmdver = 0.4.6 + cmdver = 0.4.9 diff --git a/most/CHANGES.md b/most/CHANGES.md index 8974262e..4ff1de0a 100644 --- a/most/CHANGES.md +++ b/most/CHANGES.md @@ -2,6 +2,23 @@ Change history for MOST ======================= +since 1.3 +--------- + +#### 10/7/24 + - Tweak tests (again) to avoid warnings and presolve bug with HiGHS-based + `linprog` and `intlinprog` in Optimization Toolbox R2024b. + +#### 9/12/24 + - Fix issue with `most_summary()` when ramp results are missing. + +#### 5/29/24 + - Fix [issue #45][12] where `most()` does not properly handle cases with + contingencies defined only in some periods/scenarios. + *Thanks to Stefano Nicolin.* + - Update `most_summary()` to skip display of non-existent contingencies. + + Version 1.3 - *May 10, 2024* ---------------------------- @@ -338,3 +355,4 @@ Version 1.0 - *Jun 1, 2016* [9]: https://github.com/MATPOWER/most/issues/37 [10]: https://github.com/MATPOWER/most/issues/39 [11]: https://github.com/MATPOWER/mp-opt-model +[12]: https://github.com/MATPOWER/most/issues/45 diff --git a/most/docs/sphinx/source/conf.py b/most/docs/sphinx/source/conf.py index 320da382..b7093d61 100644 --- a/most/docs/sphinx/source/conf.py +++ b/most/docs/sphinx/source/conf.py @@ -71,6 +71,7 @@ # matlab_auto_link = "basic" matlab_auto_link = "all" matlab_show_property_default_value = True +matlab_show_property_specs = True # autoclass_content = 'both' # 'class', 'init', 'both' autodoc_member_order = 'bysource' # 'alphabetical', 'groupwise', 'bysource' napoleon_use_param = False diff --git a/most/docs/src/MOST-manual/MOST-manual.tex b/most/docs/src/MOST-manual/MOST-manual.tex index aa5eafa0..36a05d6c 100644 --- a/most/docs/src/MOST-manual/MOST-manual.tex +++ b/most/docs/src/MOST-manual/MOST-manual.tex @@ -159,7 +159,7 @@ \newcommand{\mostname}[0]{{{\bf M}{\sc atpower} \textbf{O}ptimal \textbf{S}cheduling \textbf{T}ool}} \newcommand{\mosturl}[0]{https://github.com/MATPOWER/most} \newcommand{\mostlink}[0]{\href{\mosturl}{\most{}}} -\newcommand{\mostver}[0]{1.3} +\newcommand{\mostver}[0]{1.3.1-dev} \newcommand{\md}[0]{{\most{} Data struct}} \newcommand{\powerweb}[0]{{\sc PowerWeb}} \newcommand{\pserc}[0]{{\sc PSerc}} @@ -240,7 +240,7 @@ %%% BEFORE PUBLISHING A NEW VERSION: %%% Update the publication year for \bibitem{matpower} and %%% \bibitem{matpower_manual} to the year of the latest release -\date{May 10, 2024} % comment this line to display the current date +%\date{May 10, 2024} % comment this line to display the current date %\date{December 14, 2011\thanks{Second revision. First revision was December 13, 2011}} % comment this line to display the current date %%% BEGIN DOCUMENT @@ -1532,7 +1532,7 @@ \subsubsection{{\tt sd} -- Storage Data ({\tt StorageData})} \subsubsection{{\tt contab} -- Contingency Table} \label{sec:contab} -The optional \code{contab} argument is a contingency table with a master set of contingencies used for security throughout entire horizon. It is a matrix in the form of a \emph{change table} recognized by \code{apply\_changes}, described in Section~\ref{MUM-sec:apply_changes} in the \mum{}. The probabilities defined in this contingency table correspond to the conditional probabilities $\psi_0^{tjk}$ of contingency~$k$ occuring conditioned on being in base scenario~$j$. While the \md{} (\code{md}) itself allows for contingencies to be defined independently for all scenarios and time periods, \code{loadmd} applies a single set of contingencies and conditional probabilities (single \code{contab}) to all. +The optional \code{contab} argument is a contingency table with a master set of contingencies used for security throughout the entire horizon. It is a matrix in the form of a \emph{change table} recognized by \code{apply\_changes}, described in Section~\ref{MUM-sec:apply_changes} in the \mum{}. The probabilities defined in this contingency table correspond to the conditional probabilities $\psi_0^{tjk}$ of contingency~$k$ occuring conditioned on being in base scenario~$j$. While the \md{} (\code{md}) itself allows for contingencies to be defined independently for all scenarios and time periods, \code{loadmd} applies a single set of contingencies and conditional probabilities (single \code{contab}) to all. \clearpage @@ -3459,10 +3459,36 @@ \subsubsection*{Incompatible Changes} \end{itemize} -% \subsection{Version 1.2 -- released ??? ?, 2022} -% \label{app:v12} +\subsection{Version 1.3.1-dev -- released ??? ?, 202?} +\label{app:v12} + +The \href{https://matpower.org/docs/MOST-manual-1.3.1.pdf}{\most{} 1.3.1 User's Manual} is available online.\footnote{\url{https://matpower.org/docs/MOST-manual-1.3.1.pdf}} + +\subsubsection*{Changes} +\begin{itemize} +\item \code{most\_summary()} now skips display of non-existent contingencies. +\item + +\end{itemize} + +\subsubsection*{Bugs Fixed} +\begin{itemize} +\item Fix issue \#45 where \code{most()} does not properly handle cases with contingencies defined only in some periods/scenarios. +\emph{Thanks to Stefano Nicolin.} +\item Fix issue with \code{most\_summary()} when ramp results are missing. +\item Tweak tests to work around bug in HiGHS-based \code{linprog} and \code{intlinprog} in Optimization Toolbox R2024a and R2024b. +\end{itemize} + +\subsubsection*{Incompatible Changes} +\begin{itemize} +\item +\end{itemize} + + +% \subsection{Version 1.4 -- released ??? ?, 202?} +% \label{app:v14} % -% The \href{https://matpower.org/docs/MOST-manual-1.2.pdf}{\most{} 1.2 User's Manual} is available online.\footnote{\url{https://matpower.org/docs/MOST-manual-1.2.pdf}} +% The \href{https://matpower.org/docs/MOST-manual-1.4.pdf}{\most{} 1.4 User's Manual} is available online.\footnote{\url{https://matpower.org/docs/MOST-manual-1.4.pdf}} % % \subsubsection*{Changes} % \begin{itemize} @@ -3495,7 +3521,7 @@ \subsubsection*{Incompatible Changes} \doi{10.1109/TPWRS.2010.2051168} \bibitem{matpower} -R.~D. Zimmerman, C.~E. Murillo-S{\'a}nchez (2022). \matpower{}\\~ +R.~D. Zimmerman, C.~E. Murillo-S{\'a}nchez (2024). \matpower{}\\~ [Software]. Available: \url{https://matpower.org}\\ \doi{10.5281/zenodo.3236535} diff --git a/most/lib/most.m b/most/lib/most.m index 0c3cd990..b085fcce 100644 --- a/most/lib/most.m +++ b/most/lib/most.m @@ -144,9 +144,19 @@ mo.IncludeFixedReserves = 0; end end +if mo.SecurityConstrained % check that at least some contingency is specified + have_contingency = 0; + for t = 1:nt + for j = 1:mdi.idx.nj(t) + if isfield(mdi, 'cont') && isfield(mdi.cont(t,j), 'contab') && ... + ~isempty(mdi.cont(t,j).contab) + have_contingency = 1; % found a contingency + end + end + end +end if mo.SecurityConstrained == -1 - if isfield(mdi, 'cont') && isfield(mdi.cont(1,1), 'contab') && ... - ~isempty(mdi.cont(1,1).contab) + if have_contingency mo.SecurityConstrained = 1; else mo.SecurityConstrained = 0; @@ -165,9 +175,8 @@ isfield(mdi.FixedReserves(1,1,1), 'req')) error('most: MDI.FixedReserves(t,j,k) must be specified when MPOPT.most.fixed_res = 1'); end -if mo.SecurityConstrained && ~(isfield(mdi, 'cont') && ... - isfield(mdi.cont(1,1), 'contab') && ~isempty(mdi.cont(1,1).contab)) - error('most: MDI.cont(t,j).contab cannot be empty when MPOPT.most.security_constraints = 1'); +if mo.SecurityConstrained && ~have_contingency + error('most: MDI.cont(t,j).contab cannot be empty for all t, j when MPOPT.most.security_constraints = 1'); end if mo.IncludeFixedReserves && mo.SecurityConstrained warning('most: Using MPOPT.most.fixed_res = 1 and MPOPT.most.security_constraints = 1 together is not recommended.'); @@ -446,35 +455,40 @@ mdi.StepProb(t) = sum(scenario_probs); % probability of making it to the t-th step if mdi.SecurityConstrained for j = 1:mdi.idx.nj(t) - [tmp, ii] = sort(mdi.cont(t,j).contab(:, CT_LABEL)); %sort in ascending contingency label - contab = mdi.cont(t,j).contab(ii, :); - rowdecomlist = ones(size(contab,1), 1); - for l = 1:size(contab, 1) - if contab(l, CT_TABLE) == CT_TGEN && contab(l, CT_COL) == GEN_STATUS ... - && contab(l, CT_CHGTYPE) == CT_REP && contab(l, CT_NEWVAL) == 0 ... % gen turned off - && mdi.flow(t,j,1).mpc.gen(contab(l, CT_ROW), GEN_STATUS) <= 0 % but it was off on input - rowdecomlist(l) = 0; - elseif contab(l, CT_TABLE) == CT_TBRCH && contab(l, CT_COL) == BR_STATUS ... - && contab(l, CT_CHGTYPE) == CT_REP && contab(l, CT_NEWVAL) == 0 ... % branch taken out - && mdi.flow(t,j,1).mpc.branch(contab(l, CT_ROW), BR_STATUS) <= 0 % but it was off on input - rowdecomlist(l) = 0; + if isempty(mdi.cont(t,j).contab) + mdi.idx.nc(t, j) = 0; + mdi.CostWeights(1, j, t) = 1; + else + [tmp, ii] = sort(mdi.cont(t,j).contab(:, CT_LABEL)); %sort in ascending contingency label + contab = mdi.cont(t,j).contab(ii, :); + rowdecomlist = ones(size(contab,1), 1); + for l = 1:size(contab, 1) + if contab(l, CT_TABLE) == CT_TGEN && contab(l, CT_COL) == GEN_STATUS ... + && contab(l, CT_CHGTYPE) == CT_REP && contab(l, CT_NEWVAL) == 0 ... % gen turned off + && mdi.flow(t,j,1).mpc.gen(contab(l, CT_ROW), GEN_STATUS) <= 0 % but it was off on input + rowdecomlist(l) = 0; + elseif contab(l, CT_TABLE) == CT_TBRCH && contab(l, CT_COL) == BR_STATUS ... + && contab(l, CT_CHGTYPE) == CT_REP && contab(l, CT_NEWVAL) == 0 ... % branch taken out + && mdi.flow(t,j,1).mpc.branch(contab(l, CT_ROW), BR_STATUS) <= 0 % but it was off on input + rowdecomlist(l) = 0; + end end + contab = contab(rowdecomlist ~= 0, :); + mdi.cont(t, j).contab = contab; + clist = unique(contab(:, CT_LABEL)); + mdi.idx.nc(t, j) = length(clist); + k = 2; + for label = clist' + mdi.flow(t, j, k).mpc = apply_changes(label, mdi.flow(t, j, 1).mpc, contab); + ii = find( label == contab(:, CT_LABEL) ); + mdi.CostWeights(k, j, t) = contab(ii(1), CT_PROB); + mdi.idx.nb(t, j, k) = size(mdi.flow(t, j, k).mpc.bus, 1); + mdi.idx.ny(t, j, k) = length(find(mdi.flow(t, j, 1).mpc.gencost(:, MODEL) == PW_LINEAR)); + k = k + 1; + end + mdi.CostWeights(1, j, t) = 1 - sum(mdi.CostWeights(2:mdi.idx.nc(t,j)+1, j, t)); + 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); end - contab = contab(rowdecomlist ~= 0, :); - mdi.cont(t, j).contab = contab; - clist = unique(contab(:, CT_LABEL)); - mdi.idx.nc(t, j) = length(clist); - k = 2; - for label = clist' - mdi.flow(t, j, k).mpc = apply_changes(label, mdi.flow(t, j, 1).mpc, contab); - ii = find( label == contab(:, CT_LABEL) ); - mdi.CostWeights(k, j, t) = contab(ii(1), CT_PROB); - mdi.idx.nb(t, j, k) = size(mdi.flow(t, j, k).mpc.bus, 1); - mdi.idx.ny(t, j, k) = length(find(mdi.flow(t, j, 1).mpc.gencost(:, MODEL) == PW_LINEAR)); - k = k + 1; - end - mdi.CostWeights(1, j, t) = 1 - sum(mdi.CostWeights(2:mdi.idx.nc(t,j)+1, j, t)); - 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); end else for j = 1:mdi.idx.nj(t) diff --git a/most/lib/most_summary.m b/most/lib/most_summary.m index 0b62e09b..f91eacec 100644 --- a/most/lib/most_summary.m +++ b/most/lib/most_summary.m @@ -68,28 +68,30 @@ nl = size(mpc.branch, 1); ng = size(mpc.gen, 1); nt = mdo.idx.nt; -nj_max = max(mdo.idx.nj); -nc_max = max(max(mdo.idx.nc)); +nj = mdo.idx.nj; +nc = mdo.idx.nc; +nj_max = max(nj); +nc_max = max(max(nc)); ns = mdo.idx.ns; %% summarize results -psi = zeros(nt, nj_max, nc_max+1); -Pg = zeros(ng, nt, nj_max, nc_max+1); -Pd = zeros(nb, nt, nj_max, nc_max+1); -if mdo.idx.ntramp +psi = NaN(nt, nj_max, nc_max+1); +Pg = NaN(ng, nt, nj_max, nc_max+1); +Pd = NaN(nb, nt, nj_max, nc_max+1); +if mdo.idx.ntramp && isfield(mdo.results, 'Rrp') Rup = mdo.results.Rrp; Rdn = mdo.results.Rrm; else Rup = []; Rdn = []; end -u = zeros(ng, nt); -lamP = zeros(nb, nt, nj_max, nc_max+1); -muF = zeros(nl, nt, nj_max, nc_max+1); -Pf = zeros(nl, nt, nj_max, nc_max+1); +u = NaN(ng, nt); +lamP = NaN(nb, nt, nj_max, nc_max+1); +muF = NaN(nl, nt, nj_max, nc_max+1); +Pf = NaN(nl, nt, nj_max, nc_max+1); for t = 1:nt - for j = 1:mdo.idx.nj(t) - for k = 1:mdo.idx.nc(t,j)+1 + for j = 1:nj(t) + for k = 1:nc(t,j)+1 rr = mdo.flow(t,j,k).mpc; psi(t, j, k) = mdo.CostWeightsAdj(k, j, t); u(:, t) = rr.gen(:, GEN_STATUS); @@ -159,19 +161,19 @@ end fprintf('\n'); - print_most_summary_section('PG', 'Gen', nt, nj_max, nc_max, Pg); - if mdo.idx.ntramp + print_most_summary_section('PG', 'Gen', nt, nj_max, nc, Pg); + if mdo.idx.ntramp && isfield(mdo.results, 'Rrp') print_most_summary_section('RAMP UP', 'Gen', nt, 1, 0, Rup); print_most_summary_section('RAMP DOWN', 'Gen', nt, 1, 0, Rdn); end - print_most_summary_section('FIXED LOAD', 'Bus', nt, nj_max, nc_max, Pd); + print_most_summary_section('FIXED LOAD', 'Bus', nt, nj_max, nc, Pd); if ns print_most_summary_section('ESS E[SoC]', 'ESS', nt, 1, 0, SoC); end if mdo.DCMODEL - print_most_summary_section('LAM_P', 'Bus', nt, nj_max, nc_max, lamP); - print_most_summary_section('PF', 'Brch', nt, nj_max, nc_max, Pf); - print_most_summary_section('MU_F', 'Brch', nt, nj_max, nc_max, muF); + print_most_summary_section('LAM_P', 'Bus', nt, nj_max, nc, lamP); + print_most_summary_section('PF', 'Brch', nt, nj_max, nc, Pf); + print_most_summary_section('MU_F', 'Brch', nt, nj_max, nc, muF); end end @@ -180,7 +182,7 @@ end %%--------------------------------------------------------- -function print_most_summary_section(label, section_type, nt, nj_max, nc_max, data, tol) +function print_most_summary_section(label, section_type, nt, nj_max, nc, data, tol) if nargin < 7 tol = 1e-4; end @@ -189,6 +191,7 @@ function print_most_summary_section(label, section_type, nt, nj_max, nc_max, dat fprintf('\n==========%-12s==========\n', sprintf('%s%s', bl, label)); if any(data(:)) for j = 1:nj_max + nc_max = max(nc(:,j)); for k = 1:nc_max+1 if nj_max > 1 || nc_max > 0 fprintf('\nSCENARIO %d', j); diff --git a/most/lib/mostver.m b/most/lib/mostver.m index 559beaf7..d77291ba 100644 --- a/most/lib/mostver.m +++ b/most/lib/mostver.m @@ -24,9 +24,9 @@ % See https://github.com/MATPOWER/most for more info. v = struct( 'Name', 'MOST', ... - 'Version', '1.3', ... + 'Version', '1.3.1-dev', ... 'Release', '', ... - 'Date', '10-May-2024' ); + 'Date', '12-Sep-2024' ); if nargout > 0 if nargin > 0 rv = v; diff --git a/most/lib/t/t_most_uc.m b/most/lib/t/t_most_uc.m index 7b62ec2d..9a108a94 100644 --- a/most/lib/t/t_most_uc.m +++ b/most/lib/t/t_most_uc.m @@ -135,9 +135,12 @@ function t_most_uc(quiet, create_plots, create_pdfs, savedir) % (except actually in this case it triggers it rather than working % around it, so we comment it out) %mpopt = mpoption(mpopt, 'intlinprog.LPPreprocess', 'none'); - else mpopt = mpoption(mpopt, 'intlinprog.LPPreprocess', 'none'); + elseif have_feature('intlinprog', 'vnum') == 24.001 + mpopt = mpoption(mpopt, 'intlinprog.LPPreprocess', 'none'); s2 = warning('query', 'optim:intlinprog:IgnoreOptions'); warning('off', 'optim:intlinprog:IgnoreOptions'); + elseif have_feature('intlinprog', 'vnum') == 24.002 + mpopt = mpoption(mpopt, 'intlinprog.Presolve', 'off'); end end if ~verbose @@ -413,7 +416,7 @@ function t_most_uc(quiet, create_plots, create_pdfs, savedir) if have_feature('octave') warning(s1.state, file_in_path_warn_id); -elseif have_feature('intlinprog') && have_feature('intlinprog', 'vnum') >= 24 +elseif have_feature('intlinprog') && have_feature('intlinprog', 'vnum') == 24.001 warning(s2.state, 'optim:intlinprog:IgnoreOptions'); end