diff --git a/most/.gitrepo b/most/.gitrepo index 265634ea..f2ebda01 100644 --- a/most/.gitrepo +++ b/most/.gitrepo @@ -5,7 +5,7 @@ ; [subrepo] remote = git@github.com:MATPOWER/most - branch = master - commit = 3378b9996f3d006692cbcc528a30353c701537f7 - parent = b9cda2ec96444bac77be396cb886389f5fd3929d + branch = release + commit = 9fcde1505da5d4ead091a8abc21530ad6ef383b7 + parent = b3add98e9784c61f7025a4d70540189cb41ed7f3 cmdver = 0.3.1 diff --git a/most/CHANGES.md b/most/CHANGES.md index 042105cb..9f3f72bb 100644 --- a/most/CHANGES.md +++ b/most/CHANGES.md @@ -2,8 +2,17 @@ Change history for MOST ======================= -Since last release ------------------- +Version 1.0.1 - *Oct 30, 2016* +------------------------------ + +#### 10/30/18 + - Release 1.0.1. + +#### 10/26/18 + - **INCOMPATIBLE CHANGE**: Failure of the optimization no longer + halts execution and jumps to the debugger. + - Add `success` flag to `md.results` output MOST Data struct to + indicate success or failure of optimization. #### 3/19/18 - Fix bugs in `plot_uc_data()` resulting in incorrect legends. diff --git a/most/LICENSE b/most/LICENSE index a3987ff6..30ba6510 100644 --- a/most/LICENSE +++ b/most/LICENSE @@ -19,7 +19,7 @@ The following is the official license notice: -------------------------------------------------------------------------- -Copyright (c) 1996-2016, Power Systems Engineering Research Center (PSERC) +Copyright (c) 1996-2018, Power Systems Engineering Research Center (PSERC) and individual contributors (see AUTHORS file for details). All rights reserved. diff --git a/most/README.md b/most/README.md index 04748b0a..a14a6977 100644 --- a/most/README.md +++ b/most/README.md @@ -26,12 +26,12 @@ power flow and optimal power flow problems. System Requirements ------------------- -* [MATLAB][3] version 7.3 (R2006b) or later, or -* [GNU Octave][4] version 4.0 or later -* [MATPOWER][5] version 6 or later, -* [MP-Test][6], for running the MOST test suite (included with [MATPOWER][5]) -* A good LP/MILP, QP/MIQP solver, such as Gurobi, CPLEX, MOSEK, MATLAB's - Optimization Toolbox, or GLPK (included with Octave). +This version of MOST requires: +* [MATPOWER][2] version 7.x or later, _(see MATPOWER system requirements + for details of required versions of [MATLAB][4] or [GNU Octave][5])_ +* _(highly recommended)_ A high-performance LP/MILP, QP/MIQP solver, + such as Gurobi, CPLEX, MOSEK, MATLAB's Optimization Toolbox, or GLPK + _(included with Octave)_. Installation @@ -41,14 +41,14 @@ The preferred method of installation is simply to install [MATPOWER][3], which is a prerequisite for MOST and also includes its own copy of MOST. If you have followed the directions for installing MATPOWER found in -the [MATPOWER User's Manual][7], then MOST should already be installed and +the [MATPOWER User's Manual][6], then MOST should already be installed and the appropriate paths added to your MATLAB path. To run the test suite and verify that MOST is properly installed and functioning, at the MATLAB prompt, type `test_most`. The result should resemble the following, possibly including extra tests, depending on the availablility of optional packages: -```matlab +``` >> test_most t_most_3b_1_1_0........ok t_most_3b_3_1_0........ok @@ -59,14 +59,14 @@ t_most_30b_3_1_0.......ok t_most_30b_1_1_17......ok t_most_30b_3_1_17......ok t_most_fixed_res.......ok -t_most_w_ds............ok t_most_30b_1_1_0_uc....ok t_most_sp..............ok -t_most_spuc............ok (576 of 720 skipped) -t_most_uc..............ok (208 of 260 skipped) -t_most_suc.............ok (148 of 185 skipped) -All tests successful (762 passed, 932 skipped of 1694) -Elapsed time 93.13 seconds. +t_most_spuc............ok (432 of 720 skipped) +t_most_uc..............ok (156 of 260 skipped) +t_most_suc.............ok (111 of 185 skipped) +t_most_w_ds............ok +All tests successful (995 passed, 699 skipped of 1694) +Elapsed time 84.68 seconds. ``` If, for some reason, you prefer to install your own copy of MOST directly @@ -85,7 +85,7 @@ Documentation ------------- There are two primary sources of documentation for MOST. The first is -the [MOST User's Manual][8], which gives an overview of the capabilities +the [MOST User's Manual][7], which gives an overview of the capabilities and structure of MOST and describes the problem formulation. It can be found in your MATPOWER distribution at `/most/docs/MOST-manual.pdf` and the latest version is always available at: @@ -104,50 +104,57 @@ Publications 1. R. D. Zimmerman, C. E. Murillo-Sanchez, and R. J. Thomas, ["MATPOWER: Steady-State Operations, Planning and Analysis Tools - for Power Systems Research and Education,"][12] *Power Systems, IEEE + for Power Systems Research and Education,"][9] *Power Systems, IEEE Transactions on*, vol. 26, no. 1, pp. 12–19, Feb. 2011. - DOI: [10.1109/TPWRS.2010.2051168][11]. + DOI: [10.1109/TPWRS.2010.2051168][9]. 2. C. E. Murillo-Sanchez, R. D. Zimmerman, C. L. Anderson, and - R. J. Thomas, ["Secure Planning and Operations of Systems with - Stochastic Sources, Energy Storage and Active Demand,"][12] - *Smart Grid, IEEE Transactions on*, vol. 4, no. 4, pp. 2220–2229, - Dec. 2013. - DOI: [10.1109/TSG.2013.2281001][12]. + R. J. Thomas, ["Secure Planning and Operations of Systems with + Stochastic Sources, Energy Storage and Active Demand,"][10] + *Smart Grid, IEEE Transactions on*, vol. 4, no. 4, pp. 2220–2229, + Dec. 2013. + DOI: [10.1109/TSG.2013.2281001][10]. + +3. A. J. Lamadrid, D. Munoz-Alvarez, C. E. Murillo-Sanchez, + R. D. Zimmerman, H. D. Shin and R. J. Thomas, ["Using the MATPOWER + Optimal Scheduling Tool to Test Power System Operation Methodologies + Under Uncertainty,"][11] *Sustainable Energy, IEEE Transactions on*, + 2018. + DOI: [10.1109/TSTE.2018.2865454][11]. Citing MATPOWER and MOST ------------------------ We request that publications derived from the use of MATPOWER explicitly -acknowledge that fact by citing [reference \[1\]][11] above, namely: +acknowledge that fact by citing [reference \[1\]][9] above, namely: > R. D. Zimmerman, C. E. Murillo-Sanchez, and R. J. Thomas, "MATPOWER: Steady-State Operations, Planning and Analysis Tools - for Power Systems Research and Education," Power Systems, IEEE - Transactions on, vol. 26, no. 1, pp. 12–19, Feb. 2011. + for Power Systems Research and Education," *Power Systems, IEEE + Transactions on*, vol. 26, no. 1, pp. 12–19, Feb. 2011. Additionally, we request that publications derived from the use of the [MATPOWER Optimal Scheduling Tool (MOST)][1], explicitly -acknowledge that fact by citing [reference \[2\]][12] as well as [\[1\]][11]. +acknowledge that fact by citing [reference \[2\]][10] as well as [\[1\]][9]. > C. E. Murillo-Sanchez, R. D. Zimmerman, C. L. Anderson, and R. J. Thomas, "Secure Planning and Operations of Systems with - Stochastic Sources, Energy Storage and Active Demand," Smart Grid, - IEEE Transactions on, vol. 4, no. 4, pp. 2220–2229, Dec. 2013. + Stochastic Sources, Energy Storage and Active Demand," *Smart Grid, + IEEE Transactions on*, vol. 4, no. 4, pp. 2220–2229, Dec. 2013. Contributing ------------ -Please see our [contributing guidelines][9] for details on how to +Please see our [contributing guidelines][8] for details on how to contribute to the project or report issues. License ------- -MOST is distributed under the [3-clause BSD license][10]. +MOST is distributed under the [3-clause BSD license][12]. ---- [1]: https://github.com/MATPOWER/most @@ -155,10 +162,10 @@ MOST is distributed under the [3-clause BSD license][10]. [3]: https://github.com/MATPOWER/matpower [4]: http://www.mathworks.com/ [5]: https://www.gnu.org/software/octave/ -[6]: https://github.com/MATPOWER/mptest -[7]: https://github.com/MATPOWER/matpower/blob/master/docs/MATPOWER-manual.pdf -[8]: https://github.com/MATPOWER/most/blob/master/docs/MOST-manual.pdf -[9]: CONTRIBUTING.md -[10]: LICENSE -[11]: https://doi.org/10.1109/TPWRS.2010.2051168 -[12]: https://doi.org/10.1109/TSG.2013.2281001 +[6]: https://github.com/MATPOWER/matpower/blob/master/docs/MATPOWER-manual.pdf +[7]: https://github.com/MATPOWER/most/blob/master/docs/MOST-manual.pdf +[8]: CONTRIBUTING.md +[9]: https://doi.org/10.1109/TPWRS.2010.2051168 +[10]: https://doi.org/10.1109/TSG.2013.2281001 +[11]: https://doi.org/10.1109/TSTE.2018.2865454 +[12]: LICENSE diff --git a/most/docs/MOST-manual.pdf b/most/docs/MOST-manual.pdf index 727cbf1d..9983216d 100644 Binary files a/most/docs/MOST-manual.pdf and b/most/docs/MOST-manual.pdf differ diff --git a/most/docs/relnotes/MOST-Release-Notes-1.0.1.md b/most/docs/relnotes/MOST-Release-Notes-1.0.1.md new file mode 100644 index 00000000..8d76e425 --- /dev/null +++ b/most/docs/relnotes/MOST-Release-Notes-1.0.1.md @@ -0,0 +1,31 @@ +What's New in MOST 1.0.1 +------------------------ + +#### Released Oct 30, 2018 + +Below is a summary of the changes since version 1.0 of MOST. See the +[`CHANGES.md`][1] file for all the gory details. For release notes for +previous versions, see Appendix B of the [MOST User's Manual][2]. + +#### Bugs Fixed: + - Fix bugs in `plot_uc_data()` resulting in incorrect legends. + - Fix dimension of `RampWear` cost indexing if `mdi.OpenEnded` is true. + - Add missing constant term to objective function value reported by + `most_summary`. + +#### Other Changes: + - LaTeX source code for [MOST User's Manual][2] included in `docs/src`. + - Updated to use OOP notation for `opt_model` object, and avoid calls + to deprecated methods, using `init_indexed_name()` and + `add_lin_constraint()` instead. + - Updated to use MATPOWER's new quadratic costs in `opt_model` in + place of the legacy cost model. + +#### Incompatible Changes: + - Failure of the optimization no longer halts execution and jumps to + the debugger. + - Requires MATPOWER 7.x or later. + + +[1]: ../../CHANGES.md +[2]: ../MOST-manual.pdf diff --git a/most/docs/src/MOST-manual/MOST-manual.tex b/most/docs/src/MOST-manual/MOST-manual.tex index 4a627595..7387d021 100644 --- a/most/docs/src/MOST-manual/MOST-manual.tex +++ b/most/docs/src/MOST-manual/MOST-manual.tex @@ -129,7 +129,7 @@ %\hyphenation{matpower} -\newcommand{\mpver}[0]{6.1-dev} +\newcommand{\mpver}[0]{7.0b1} %\newcommand{\matlab}[0]{{Matlab}} %\newcommand{\matlab}[0]{{\sc Matlab}\textsuperscript{\tiny \textregistered}} \newcommand{\matlab}[0]{{\sc Matlab}} @@ -144,11 +144,11 @@ \newcommand{\mipsurl}[0]{https://github.com/MATPOWER/mips} \newcommand{\mipslink}[0]{\href{\mipsurl}{\mips{}}} \newcommand{\mipsname}[0]{{{\bf M}{\sc atpower} \textbf{I}nterior \textbf{P}oint \textbf{S}olver}} -\newcommand{\mipsver}[0]{1.2.2} +\newcommand{\mipsver}[0]{1.3} \newcommand{\most}[0]{{MOST}} \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{\mostver}[0]{1.0.1-dev} +\newcommand{\mostver}[0]{1.0.1} \newcommand{\md}[0]{{\most{} Data struct}} \newcommand{\powerweb}[0]{{\sc PowerWeb}} \newcommand{\pserc}[0]{{\sc PSerc}} @@ -191,7 +191,7 @@ \newcommand{\mipsmanurl}[0]{http://www.pserc.cornell.edu/matpower/docs/MIPS-manual-\mipsver.pdf} \newcommand{\mostmanurl}[0]{http://www.pserc.cornell.edu/matpower/docs/MOST-manual-\mostver.pdf} \newcommand{\currentmumurl}[0]{http://www.pserc.cornell.edu/matpower/MATPOWER-manual.pdf} -\newcommand{\currentmipsmanurl}[0]{\mipsurl/blob/master/MIPS-manual.pdf} +\newcommand{\currentmipsmanurl}[0]{\mipsurl/blob/master/docs/MIPS-manual.pdf} \newcommand{\currentmostmanurl}[0]{http://www.pserc.cornell.edu/matpower/MOST-manual.pdf} \newcommand{\mipsman}[0]{\href{\mipsmanurl}{\mips{} User's Manual}} \newcommand{\mostman}[0]{\href{\mostmanurl}{\most{} User's Manual}} @@ -222,7 +222,7 @@ \title{\mostname{}\\\most{} \mostver{}\\ User's Manual} \author{Ray~D.~Zimmerman \and Carlos~E.~Murillo-S\'anchez} -%\date{December 16, 2016} % comment this line to display the current date +\date{October 30, 2018} % 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 @@ -232,7 +232,7 @@ \vfill \begin{center} {\scriptsize -\copyright~2011, 2012, 2013, 2014, 2015, 2016~\PSERC{}\\ +\copyright~2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018~\PSERC{}\\ All Rights Reserved} \end{center} @@ -247,7 +247,7 @@ \clearpage \section{Introduction} -Beginning with version 6, \matpower{}~\cite{zimmerman2011} includes a framework for solving generalized steady-state electric power scheduling problems. This framework is known as \most{}, for \mostname{}~\cite{murillo-sanchez2013a}. +Beginning with version 6, \matpower{}~\cite{zimmerman2011} includes a framework for solving generalized steady-state electric power scheduling problems. This framework is known as \most{}, for \mostname{}~\cite{murillo-sanchez2013a,lamadrid2018}. \most{} can be used to solve problems as simple as a deterministic, single period economic dispatch problem with no transmission constraints or as complex as a stochastic, security-constrained, combined unit-commitment and multiperiod optimal power flow problem with locational contingency and load-following reserves, ramping costs and constraints, deferrable demands, lossy storage resources and uncertain renewable generation. @@ -334,7 +334,13 @@ \section{Getting Started} \subsection{System Requirements} \label{sec:sysreq} -To use \most{} you will need a working installation of \matpower{}~\mpver{} or later. See the corresponding section in the \mum{} for more information on the system requirements. +To use \most{}~\mostver{} you will need: +\begin{itemize} +\item \matpower{} version 7.x or later\footnote{\most{}~1.0 required \matpower{}~6.} +\item \emph{(highly recommended)} a high-performance LP/MILP, QP/MIQP solver such as \gurobi{}, \cplex{}, \mosek{}, \matlab{}'s \ot{} or \glpk{} \emph{(included with Octave)}. +\end{itemize} + +\most{} requires a working installation of \matpower{}. See the corresponding section in the \mum{} for more information on the system requirements for \matpower{}. \matpower{}~6 and later includes a full version of \most{} in the \mostpath{} directory. It is also highly recommended that you install a high-performance solver such as \gurobi{}, \cplex{}, \mosek{}, \matlab{}'s \ot{} or \glpk{}, described in Appendix~\ref{MUM-app:optional_packages} in the \mum{}. @@ -361,14 +367,14 @@ \subsection{Installation} t_most_30b_1_1_17......ok t_most_30b_3_1_17......ok t_most_fixed_res.......ok -t_most_w_ds............ok t_most_30b_1_1_0_uc....ok t_most_sp..............ok -t_most_spuc............ok (576 of 720 skipped) -t_most_uc..............ok (208 of 260 skipped) -t_most_suc.............ok (148 of 185 skipped) -All tests successful (762 passed, 932 skipped of 1694) -Elapsed time 93.13 seconds. +t_most_spuc............ok (432 of 720 skipped) +t_most_uc..............ok (156 of 260 skipped) +t_most_suc.............ok (111 of 185 skipped) +t_most_w_ds............ok +All tests successful (995 passed, 699 skipped of 1694) +Elapsed time 84.68 seconds. \end{Code} If, for some reason, you prefer to install your own copy of MOST directly @@ -431,12 +437,12 @@ \subsubsection{Solving the Case} \begin{Code} ============================================================================= - MATPOWER Optimal Scheduling Tool -- MOST Version 1.0 + MATPOWER Optimal Scheduling Tool -- MOST Version 1.0.1 A multiperiod stochastic secure OPF with unit commitment ----- Built on MATPOWER ----- by Carlos E. Murillo-Sanchez, Universidad Nacional de Colombia--Manizales and Ray D. Zimmerman, Cornell University - (c) 2012-2016 Power Systems Engineering Research Center (PSERC) + (c) 2012-2018 Power Systems Engineering Research Center (PSERC) ============================================================================= - Building indexing structures. - Building expected storage-tracking mechanism. @@ -456,9 +462,9 @@ \subsubsection{Solving the Case} ============================================================================ -Gurobi Version 7.0.1 -- automatic MILP solver +Gurobi Version 8.1.0 -- automatic MILP solver --- Integer stage complete, starting price computation stage --- -Gurobi Version 7.0.1 -- automatic LP solver +Gurobi Version 8.1.0 -- automatic LP solver ============================================================================ - MOST: MILP solved successfully. @@ -496,7 +502,7 @@ \subsubsection{Setting Options} \subsection{Documentation} -There are two primary sources of documentation for \most{}. The first is this manual, which gives an overview of the capabilities and structure and describes the problem formulation. The \most{} User's Manual can be found in your \matpower{} distribution at \mppath{/most/docs/MOST-manual.pdf}. +There are two primary sources of documentation for \most{}. The first is \href{\mostmanurl}{this manual}, which gives an overview of the capabilities and structure and describes the problem formulation. The \mostman{} can be found in your \matpower{} distribution at \mppath{/most/docs/MOST-manual.pdf} and the \href{\currentmostmanurl}{latest version} is always available at: \url{\currentmostmanurl}. The second is the built-in \code{help} command. As with \matlab{}'s built-in functions and toolbox routines, you can type \code{help} followed by the name of a command or M-file to get help on that particular function. Many of the \most{} related M-files have such documentation and this should be considered the main reference for the calling options for each individual function. See Appendix~\ref{app:functions} for a list of \most{} functions. @@ -712,7 +718,7 @@ \subsection{Nomenclature} \item[$Q_{\rm min}^{tijk}, Q_{\rm max}^{tijk}$] Limits on reactive injection for unit~$i$ in post-contingency state~$k$ of scenario~$j$ at time~$t$. \item[$R_{\rm max+}^{ti}, R_{\rm max-}^{ti}$] Upward/downward contingency (or zonal) reserve capacity limits for unit~$i$ at time~$t$. \item[$R_l^{tjk}$] MW reserve requirement for zone~$l$ in post-contingency state~$k$ of scenario~$j$ at time~$t$. -\item[$\delta^{i}_{\rm max+}, \delta^i_{\rm max-}$] Upward/downward load-following ramping reserve limits for unit~$i$. +\item[$\delta^{ti}_{\rm max+}, \delta^{ti}_{\rm max-}$] Upward/downward load-following ramping reserve limits for unit~$i$ at time $t$ for transition to time $t+1$. \item[$\Delta_+^i, \Delta_-^i$] Upward/downward physical ramping limits for unit~$i$ for transitions from base ($k=0$) to contingency cases. \item[$\rho^{ti}$] Parameter for storage unit~$i$ at period $t$ that determines the weighting in storage constraints, where the storage dispatch bounds are computed relative to a weighted average of previous period endogenous bounds $s_+^{(t-1)i}$, $s_-^{(t-1)i}$ ($\rho^{ti} = 1$) and period- and scenario-specific initial expected stored energy ($\rho^{ti} = 0$). \item[$S^{ti}_{\rm max},S^{ti}_{\rm min}$] Stored energy (in MWh) max/min limits for storage unit~$i$ at time~$t$. @@ -1979,6 +1985,7 @@ \subsubsection{Output Data} name & type\tnote{*} & description \\ \midrule \code{f} & O & value of objective function $f(x)$ in \eqref{eq:mostobjective} at solution (same as \code{md.QP.f}) \\ +\code{success} & O & optimization success flag, 1 = succeeded, 0 = failed \\ \code{Pc(i,t)} & O & $n_g \times n_t$, active power contract quantity, $p_c^{ti}$ \\ \code{Rpp(i,t)} & O & $n_g \times n_t$, upward active contingency reserve quantity $r_+^{ti}$ \\ \code{Rpm(i,t)} & O & $n_g \times n_t$, downward active contingency reserve quantity $r_-^{ti}$ \\ @@ -3090,8 +3097,8 @@ \subsection{\most{} Documentation Files} name & description \\ \midrule \code{docs/} & \most{} documentation \\ -\code{~~MOST-manual.pdf} & \emph{\most{} User's Manual} \\ -\code{~~src/} & \LaTeX{} source for \emph{\most{} User's Manual} \\ +\code{~~MOST-manual.pdf} & \mostman{} \\ +\code{~~src/} & \LaTeX{} source for \mostman{} \\ \code{lib/} & \most{} software (see Table~\ref{tab:mostsw}) \\ \code{~~t/} & \most{} tests and examples (see Tables~\ref{tab:mosttestdata} and \ref{tab:mosttests}) \\ \code{AUTHORS} & list of \most{} authors \\ @@ -3236,24 +3243,6 @@ \section{Release History} The full release history can be found in \mostpath{/docs/CHANGES}. -\subsection{Version 1.0.1-dev -- not yet released} -\label{app:v101} - -The \href{http://www.pserc.cornell.edu/matpower/docs/MOST-manual-1.0.1.pdf}{\most{} 1.0.1 User's Manual} is available online.\footnote{\url{http://www.pserc.cornell.edu/matpower/docs/MOST-manual-1.0.1.pdf}} - -\subsubsection*{Bugs Fixed} -\begin{itemize} -\item Fix bugs in \code{plot\_uc\_data()} resulting in incorrect legends. -\item Fix dimension of \code{RampWear} cost indexing if \code{mdi.OpenEnded} is true. -\item Add missing constant term to objective function value reported by \code{most\_summary}. -\end{itemize} - -\subsubsection*{Other Changes} -\begin{itemize} -\item Updated to use OOP notation for \code{opt\_model} object, and avoid calls to deprecated methods, using \code{init\_indexed\_name()} and \code{add\_lin\_constraint()} instead. -\item Updated to use \matpower{}'s new quadratic costs in \code{opt\_model} in place of the legacy cost model. -\end{itemize} - \subsection{Version 1.0 -- released Dec 16, 2016} \label{app:v10} @@ -3274,6 +3263,32 @@ \subsubsection*{Other Changes} \item No significant changes since first public beta release.\footnote{Version 1.0b1 was released on Jun 1, 2016 and 1.0b2 on Nov 1, 2016} \end{itemize} +\subsection{Version 1.0.1 -- released Oct 30, 2018} +\label{app:v101} + +The \href{http://www.pserc.cornell.edu/matpower/docs/MOST-manual-1.0.1.pdf}{\most{} 1.0.1 User's Manual} is available online.\footnote{\url{http://www.pserc.cornell.edu/matpower/docs/MOST-manual-1.0.1.pdf}} + +\subsubsection*{Bugs Fixed} +\begin{itemize} +\item Fix bugs in \code{plot\_uc\_data()} resulting in incorrect legends. +\item Fix dimension of \code{RampWear} cost indexing if \code{mdi.OpenEnded} is true. +\item Add missing constant term to objective function value reported by \code{most\_summary}. +\end{itemize} + +\subsubsection*{Other Changes} +\begin{itemize} +\item \LaTeX{} source code for \mostman{} included in \code{docs/src}. +\item Updated to use OOP notation for \code{opt\_model} object, and avoid calls to deprecated methods, using \code{init\_indexed\_name()} and \code{add\_lin\_constraint()} instead. +\item Updated to use \matpower{}'s new quadratic costs in \code{opt\_model} in place of the legacy cost model. +\item Add \code{success} flag to \code{md.results} output \md{} to indicate success or failure of optimization. +\end{itemize} + +\subsubsection*{Incompatible Changes} +\begin{itemize} +\item Failure of the optimization no longer halts execution and jumps to the debugger. +\item Requires \matpower{} 7.x or later. +\end{itemize} + \end{appendices} @@ -3290,6 +3305,10 @@ \subsubsection*{Other Changes} C.~E. Murillo-S{\'a}nchez, R.~D. Zimmerman, C.~L. Anderson, and R.~J. Thomas, ``Secure Planning and Operations of Systems with Stochastic Sources, Energy Storage and Active Demand,'' \emph{Smart Grid, IEEE Transactions on}, vol.~4, no.~4, pp.~2220--2229, Dec.~2013. DOI:~\href{https://doi.org/10.1109/TSG.2013.2281001}{10.1109/TSG.2013.2281001} +\bibitem{lamadrid2018} +A.~J. Lamadrid, D. Mu{\~n}oz-{\'A}lvarez, C.~E. Murillo-S{\'a}nchez, R.~D. Zimmerman, H.~D. Shin and R.~J. Thomas, ``Using the \matpower{} Optimal Scheduling Tool to Test Power System Operation Methodologies Under Uncertainty,'' \emph{Sustainable Energy, IEEE Transactions on}, 2018. +DOI:~\href{https://doi.org/10.1109/TSTE.2018.2865454}{10.1109/TSTE.2018.2865454} + \bibitem{zimmerman2009} R.~D. Zimmerman, C.~E. Murillo-S{\'a}nchez, and R.~J. Thomas, ``\matpower{}'s Extensible Optimal Power Flow Architecture,'' \emph{Power and Energy Society diff --git a/most/lib/Contents.m b/most/lib/Contents.m new file mode 100644 index 00000000..28e5322a --- /dev/null +++ b/most/lib/Contents.m @@ -0,0 +1,39 @@ +% MOST +% Version 1.0.1 30-Oct-2018 +% +% The MATPOWER Optimal Scheduling Tool (MOST) is framework for solving +% generalized steady-state electric power scheduling problems. +% +% MOST can be used to solve problems as simple as a deterministic, +% single period economic dispatch problem with no transmission +% constraints or as complex as a stochastic, security-constrained, +% combined unit-commitment and multiperiod optimal power flow problem +% with locational contingency and load-following reserves, ramping +% costs and constraints, deferrable demands, lossy storage resources +% and uncertain renewable generation. +% +% While the problem formulation is general and incorporates a full +% nonlinear AC network model, the current implementation is limited +% to DC power flow modeling of the network. Some work has been done +% on an AC implementation, but it is not yet ready for release. +% +% The primary developers of MOST are Carlos E. Murillo-Sanchez and +% Ray D. Zimmerman, with significant contributions from +% Daniel Munoz-Alvarez and Alberto J. Lamadrid. It is built on top of +% MATPOWER, a package of MATLAB/Octave M-files for solving power flow +% and optimal power flow problems. +% +% The latest version of MOST can be found on GitHub at: +% +% https://github.com/MATPOWER/most +% +% MOST is covered by the 3-clause BSD License (see LICENSE for details). + +% MOST +% Copyright (c) 2010-2018, Power Systems Engineering Research Center (PSERC) +% by Carlos E. Murillo-Sanchez, PSERC Cornell & Universidad Nacional de Colombia +% and Ray Zimmerman, PSERC Cornell +% +% This file is part of MOST. +% Covered by the 3-clause BSD License (see LICENSE file for details). +% See https://github.com/MATPOWER/most for more info. diff --git a/most/lib/most.m b/most/lib/most.m index 2f5e5aba..fa2253ad 100644 --- a/most/lib/most.m +++ b/most/lib/most.m @@ -51,9 +51,14 @@ % most.price_stage_warn_tol (1e-7) - tolerance on the objective fcn % value and primal variable relative match required to avoid % mis-match warning message, see 'help miqps_matpower' for details +% +% Outputs: +% MDO MOST data structure, output +% (see MOST User's Manual for details) + % MOST -% Copyright (c) 2010-2016, Power Systems Engineering Research Center (PSERC) +% Copyright (c) 2010-2018, Power Systems Engineering Research Center (PSERC) % by Carlos E. Murillo-Sanchez, PSERC Cornell & Universidad Nacional de Colombia % and Ray Zimmerman, PSERC Cornell % @@ -77,7 +82,7 @@ fprintf( ' ----- Built on MATPOWER -----\n'); fprintf( ' by Carlos E. Murillo-Sanchez, Universidad Nacional de Colombia--Manizales\n'); fprintf( ' and Ray D. Zimmerman, Cornell University\n'); - fprintf( ' (c) 2012-2016 Power Systems Engineering Research Center (PSERC) \n'); + fprintf( ' (c) 2012-2018 Power Systems Engineering Research Center (PSERC) \n'); fprintf( '=============================================================================\n'); end @@ -2026,265 +2031,274 @@ [], mdo.QP.opt); end if mdo.QP.exitflag > 0 + success = 1; if verbose fprintf('\n============================================================================\n'); fprintf('- MOST: %s solved successfully.\n', model); end else - fprintf('\n============================================================================\n'); - fprintf('- MOST: %s solver ''%s'' failed with exit flag = %d\n', model, mdo.QP.opt.alg, mdo.QP.exitflag); - fprintf(' You can query the workspace to debug.\n') - fprintf(' When finished, type the word "return" to continue.\n\n'); - keyboard; + success = 0; + if verbose + fprintf('\n============================================================================\n'); + fprintf('- MOST: %s solver ''%s'' failed with exit flag = %d\n', model, mdo.QP.opt.alg, mdo.QP.exitflag); + end +% fprintf('\n============================================================================\n'); +% fprintf('- MOST: %s solver ''%s'' failed with exit flag = %d\n', model, mdo.QP.opt.alg, mdo.QP.exitflag); +% fprintf(' You can query the workspace to debug.\n') +% fprintf(' When finished, type the word "dbcont" to continue.\n\n'); +% keyboard; end % Unpack results if verbose fprintf('- Post-processing results.\n'); end - for t = 1:nt - if UC - mdo.UC.CommitSched(:, t) = mdo.QP.x(vv.i1.u(t):vv.iN.u(t)); - end - for j = 1:mdi.idx.nj(t) - for k = 1:mdi.idx.nc(t,j)+1 - mpc = mdo.flow(t,j,k).mpc; %% pull mpc from output struct - % Some initialization of data - if mdo.DCMODEL - mpc.bus(:, VM) = 1; - end - % Injections and shadow prices - mpc.gen(:, PG) = baseMVA * mdo.QP.x(vv.i1.Pg(t,j,k):vv.iN.Pg(t,j,k)); - %% need to update Qg for loads consistent w/constant power factor - Pmin = mpc.gen(:, PMIN); - Qmin = mpc.gen(:, QMIN); - Qmax = mpc.gen(:, QMAX); - ivl = find( isload(mpc.gen) & (Qmin ~= 0 | Qmax ~= 0) ); - Qlim = (Qmin(ivl) == 0) .* Qmax(ivl) + (Qmax(ivl) == 0) .* Qmin(ivl); - mpc.gen(ivl, QG) = mpc.gen(ivl, PG) .* Qlim ./ Pmin(ivl); - if mdo.DCMODEL - %% bus angles - mpc.bus(:, VA) = (180/pi) * mdo.QP.x(vv.i1.Va(t,j,k):vv.iN.Va(t,j,k)); + if success + for t = 1:nt + if UC + mdo.UC.CommitSched(:, t) = mdo.QP.x(vv.i1.u(t):vv.iN.u(t)); + end + for j = 1:mdi.idx.nj(t) + for k = 1:mdi.idx.nc(t,j)+1 + mpc = mdo.flow(t,j,k).mpc; %% pull mpc from output struct + % Some initialization of data + if mdo.DCMODEL + mpc.bus(:, VM) = 1; + end + % Injections and shadow prices + mpc.gen(:, PG) = baseMVA * mdo.QP.x(vv.i1.Pg(t,j,k):vv.iN.Pg(t,j,k)); + %% need to update Qg for loads consistent w/constant power factor + Pmin = mpc.gen(:, PMIN); + Qmin = mpc.gen(:, QMIN); + Qmax = mpc.gen(:, QMAX); + ivl = find( isload(mpc.gen) & (Qmin ~= 0 | Qmax ~= 0) ); + Qlim = (Qmin(ivl) == 0) .* Qmax(ivl) + (Qmax(ivl) == 0) .* Qmin(ivl); + mpc.gen(ivl, QG) = mpc.gen(ivl, PG) .* Qlim ./ Pmin(ivl); + if mdo.DCMODEL + %% bus angles + mpc.bus(:, VA) = (180/pi) * mdo.QP.x(vv.i1.Va(t,j,k):vv.iN.Va(t,j,k)); - %% nodal prices - 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; - mpc.bus(:, LAM_P) = price; + %% nodal prices + 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; + mpc.bus(:, LAM_P) = price; - %% line flows and line limit shadow prices - mpc.branch(:, PF) = 0; - mpc.branch(:, QF) = 0; - mpc.branch(:, PT) = 0; - mpc.branch(:, QT) = 0; - mpc.branch(:, MU_SF) = 0; - mpc.branch(:, MU_ST) = 0; - ion = find(mpc.branch(:, BR_STATUS)); - rows = ll.i1.Pf(t,j,k):ll.iN.Pf(t,j,k); - cols = vv.i1.Va(t,j,k):vv.iN.Va(t,j,k); - lf = baseMVA * (mdo.QP.A(rows,cols) * mdo.QP.x(cols) + mdo.flow(t,j,k).PLsh); - mpc.branch(ion, PF) = lf; - mpc.branch(ion, PT) = -lf; - mpc.branch(ion, MU_SF) = mdo.QP.lambda.mu_u(rows) / baseMVA; - mpc.branch(ion, MU_ST) = mdo.QP.lambda.mu_l(rows) / baseMVA; - else - %% system price - 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; - mpc.bus(:, LAM_P) = price; - end - if UC - % igenon does not contain gens ousted because of a contingency or - % a forced-off UC.CommitKey - igenon = find(mpc.gen(:, GEN_STATUS)); - u = mdo.QP.x(vv.i1.u(t):vv.iN.u(t)); - mpc.gen(igenon, GEN_STATUS) = u(igenon); - gs = mpc.gen(igenon, GEN_STATUS) > 0; % gen status - mpc.gen(:, MU_PMAX) = 0; - mpc.gen(:, MU_PMIN) = 0; - mpc.gen(igenon, MU_PMAX) = gs .* ... - mdo.QP.lambda.mu_u(ll.i1.uPmax(t,j,k):ll.iN.uPmax(t,j,k)) / baseMVA; - mpc.gen(igenon, MU_PMIN) = gs .* ... - mdo.QP.lambda.mu_u(ll.i1.uPmin(t,j,k):ll.iN.uPmin(t,j,k)) / baseMVA; - if mdo.QCoordination - mpc.gen(:, MU_QMAX) = 0; - mpc.gen(:, MU_QMIN) = 0; - mpc.gen(igenon, MU_QMAX) = gs .* ... - mdo.QP.lambda.mu_u(ll.i1.uQmax(t,j,k):ll.iN.uQmax(t,j,k)) / baseMVA; - mpc.gen(igenon, MU_QMIN) = gs .* ... - mdo.QP.lambda.mu_u(ll.i1.uQmin(t,j,k):ll.iN.uQmin(t,j,k)) / baseMVA; + %% line flows and line limit shadow prices + mpc.branch(:, PF) = 0; + mpc.branch(:, QF) = 0; + mpc.branch(:, PT) = 0; + mpc.branch(:, QT) = 0; + mpc.branch(:, MU_SF) = 0; + mpc.branch(:, MU_ST) = 0; + ion = find(mpc.branch(:, BR_STATUS)); + rows = ll.i1.Pf(t,j,k):ll.iN.Pf(t,j,k); + cols = vv.i1.Va(t,j,k):vv.iN.Va(t,j,k); + lf = baseMVA * (mdo.QP.A(rows,cols) * mdo.QP.x(cols) + mdo.flow(t,j,k).PLsh); + mpc.branch(ion, PF) = lf; + mpc.branch(ion, PT) = -lf; + mpc.branch(ion, MU_SF) = mdo.QP.lambda.mu_u(rows) / baseMVA; + mpc.branch(ion, MU_ST) = mdo.QP.lambda.mu_l(rows) / baseMVA; + else + %% system price + 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; + mpc.bus(:, LAM_P) = price; end - else - gs = mpc.gen(:, GEN_STATUS) > 0; % gen status - mpc.gen(:, MU_PMAX) = gs .* ... - mdo.QP.lambda.upper(vv.i1.Pg(t,j,k):vv.iN.Pg(t,j,k)) / baseMVA; - mpc.gen(:, MU_PMIN) = gs .* ... - mdo.QP.lambda.lower(vv.i1.Pg(t,j,k):vv.iN.Pg(t,j,k)) / baseMVA; - if mdo.QCoordination - mpc.gen(:, MU_QMAX) = gs .* ... - mdo.QP.lambda.upper(vv.i1.Qg(t,j,k):vv.iN.Qg(t,j,k)) / baseMVA; - mpc.gen(:, MU_QMIN) = gs .* ... - mdo.QP.lambda.lower(vv.i1.Qg(t,j,k):vv.iN.Qg(t,j,k)) / baseMVA; + if UC + % igenon does not contain gens ousted because of a contingency or + % a forced-off UC.CommitKey + igenon = find(mpc.gen(:, GEN_STATUS)); + u = mdo.QP.x(vv.i1.u(t):vv.iN.u(t)); + mpc.gen(igenon, GEN_STATUS) = u(igenon); + gs = mpc.gen(igenon, GEN_STATUS) > 0; % gen status + mpc.gen(:, MU_PMAX) = 0; + mpc.gen(:, MU_PMIN) = 0; + mpc.gen(igenon, MU_PMAX) = gs .* ... + mdo.QP.lambda.mu_u(ll.i1.uPmax(t,j,k):ll.iN.uPmax(t,j,k)) / baseMVA; + mpc.gen(igenon, MU_PMIN) = gs .* ... + mdo.QP.lambda.mu_u(ll.i1.uPmin(t,j,k):ll.iN.uPmin(t,j,k)) / baseMVA; + if mdo.QCoordination + mpc.gen(:, MU_QMAX) = 0; + mpc.gen(:, MU_QMIN) = 0; + mpc.gen(igenon, MU_QMAX) = gs .* ... + mdo.QP.lambda.mu_u(ll.i1.uQmax(t,j,k):ll.iN.uQmax(t,j,k)) / baseMVA; + mpc.gen(igenon, MU_QMIN) = gs .* ... + mdo.QP.lambda.mu_u(ll.i1.uQmin(t,j,k):ll.iN.uQmin(t,j,k)) / baseMVA; + end + else + gs = mpc.gen(:, GEN_STATUS) > 0; % gen status + mpc.gen(:, MU_PMAX) = gs .* ... + mdo.QP.lambda.upper(vv.i1.Pg(t,j,k):vv.iN.Pg(t,j,k)) / baseMVA; + mpc.gen(:, MU_PMIN) = gs .* ... + mdo.QP.lambda.lower(vv.i1.Pg(t,j,k):vv.iN.Pg(t,j,k)) / baseMVA; + if mdo.QCoordination + mpc.gen(:, MU_QMAX) = gs .* ... + mdo.QP.lambda.upper(vv.i1.Qg(t,j,k):vv.iN.Qg(t,j,k)) / baseMVA; + mpc.gen(:, MU_QMIN) = gs .* ... + mdo.QP.lambda.lower(vv.i1.Qg(t,j,k):vv.iN.Qg(t,j,k)) / baseMVA; + end end - end - if mdi.IncludeFixedReserves - z = zeros(ng, 1); - r = mdo.FixedReserves(t,j,k); - r.R = z; - r.prc = z; - r.mu = struct('l', z, 'u', z, 'Pmax', z); - r.totalcost = sum(om.eval_quad_cost(mdo.QP.x, 'Rcost', {t,j,k})); - r.R(r.igr) = mdo.QP.x(vv.i1.R(t,j,k):vv.iN.R(t,j,k)) * baseMVA; - for gg = r.igr - iz = find(r.zones(:, gg)); - kk = ll.i1.Rreq(t,j,k):ll.iN.Rreq(t,j,k); - r.prc(gg) = sum(mdo.QP.lambda.mu_l(kk(iz))) / baseMVA; + if mdi.IncludeFixedReserves + z = zeros(ng, 1); + r = mdo.FixedReserves(t,j,k); + r.R = z; + r.prc = z; + r.mu = struct('l', z, 'u', z, 'Pmax', z); + r.totalcost = sum(om.eval_quad_cost(mdo.QP.x, 'Rcost', {t,j,k})); + r.R(r.igr) = mdo.QP.x(vv.i1.R(t,j,k):vv.iN.R(t,j,k)) * baseMVA; + for gg = r.igr + iz = find(r.zones(:, gg)); + kk = ll.i1.Rreq(t,j,k):ll.iN.Rreq(t,j,k); + r.prc(gg) = sum(mdo.QP.lambda.mu_l(kk(iz))) / baseMVA; + end + r.mu.l(r.igr) = mdo.QP.lambda.lower(vv.i1.R(t,j,k):vv.iN.R(t,j,k)) / baseMVA; + r.mu.u(r.igr) = mdo.QP.lambda.upper(vv.i1.R(t,j,k):vv.iN.R(t,j,k)) / baseMVA; + 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; + mpc.reserves = r; end - r.mu.l(r.igr) = mdo.QP.lambda.lower(vv.i1.R(t,j,k):vv.iN.R(t,j,k)) / baseMVA; - r.mu.u(r.igr) = mdo.QP.lambda.upper(vv.i1.R(t,j,k):vv.iN.R(t,j,k)) / baseMVA; - 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; - mpc.reserves = r; + mdo.flow(t,j,k).mpc = mpc; %% stash modified mpc in output struct end - mdo.flow(t,j,k).mpc = mpc; %% stash modified mpc in output struct + end + % Contract, contingency reserves, energy limits + mdo.results.Pc(:,t) = baseMVA * mdo.QP.x(vv.i1.Pc(t):vv.iN.Pc(t)); + mdo.results.Rpp(:,t) = baseMVA * mdo.QP.x(vv.i1.Rpp(t):vv.iN.Rpp(t)); + mdo.results.Rpm(:,t) = baseMVA * mdo.QP.x(vv.i1.Rpm(t):vv.iN.Rpm(t)); + if ns + mdo.results.Sm(:,t) = baseMVA * mdo.QP.x(vv.i1.Sm(t):vv.iN.Sm(t)); + mdo.results.Sp(:,t) = baseMVA * mdo.QP.x(vv.i1.Sp(t):vv.iN.Sp(t)); end end - % Contract, contingency reserves, energy limits - mdo.results.Pc(:,t) = baseMVA * mdo.QP.x(vv.i1.Pc(t):vv.iN.Pc(t)); - mdo.results.Rpp(:,t) = baseMVA * mdo.QP.x(vv.i1.Rpp(t):vv.iN.Rpp(t)); - mdo.results.Rpm(:,t) = baseMVA * mdo.QP.x(vv.i1.Rpm(t):vv.iN.Rpm(t)); - if ns - mdo.results.Sm(:,t) = baseMVA * mdo.QP.x(vv.i1.Sm(t):vv.iN.Sm(t)); - mdo.results.Sp(:,t) = baseMVA * mdo.QP.x(vv.i1.Sp(t):vv.iN.Sp(t)); + % Ramping reserves + for t = 1:mdo.idx.ntramp + mdo.results.Rrp(:,t) = baseMVA * mdo.QP.x(vv.i1.Rrp(t):vv.iN.Rrp(t)); + mdo.results.Rrm(:,t) = baseMVA * mdo.QP.x(vv.i1.Rrm(t):vv.iN.Rrm(t)); end - end - % Ramping reserves - for t = 1:mdo.idx.ntramp - mdo.results.Rrp(:,t) = baseMVA * mdo.QP.x(vv.i1.Rrp(t):vv.iN.Rrp(t)); - mdo.results.Rrm(:,t) = baseMVA * mdo.QP.x(vv.i1.Rrm(t):vv.iN.Rrm(t)); - end - % Expected energy prices for generators, per generator and per period, - % both absolute and conditional on making it to that period - mdo.results.GenPrices = zeros(ng, nt); - mdo.results.CondGenPrices = zeros(ng, nt); - for t = 1:nt - pp = zeros(ng,1); - for j = 1:mdo.idx.nj(t) - for k = 1:mdo.idx.nc(t,j)+1 - pp = pp + mdo.flow(t,j,k).mpc.bus(mdo.flow(t,j,k).mpc.gen(:,GEN_BUS), LAM_P); + % Expected energy prices for generators, per generator and per period, + % both absolute and conditional on making it to that period + mdo.results.GenPrices = zeros(ng, nt); + mdo.results.CondGenPrices = zeros(ng, nt); + for t = 1:nt + pp = zeros(ng,1); + for j = 1:mdo.idx.nj(t) + for k = 1:mdo.idx.nc(t,j)+1 + pp = pp + mdo.flow(t,j,k).mpc.bus(mdo.flow(t,j,k).mpc.gen(:,GEN_BUS), LAM_P); + end end + mdo.results.GenPrices(:,t) = pp; + mdo.results.CondGenPrices(:, t) = pp / mdo.StepProb(t); end - mdo.results.GenPrices(:,t) = pp; - mdo.results.CondGenPrices(:, t) = pp / mdo.StepProb(t); - end - % Obtain contingency reserve prices, per generator and period - mdo.results.RppPrices = zeros(ng, nt); - mdo.results.RpmPrices = zeros(ng, nt); - for t = 1:nt - mdo.results.RppPrices(:, t) = mdo.QP.lambda.lower(vv.i1.Rpp(t):vv.iN.Rpp(t)) / baseMVA; - mdo.results.RpmPrices(:, t) = mdo.QP.lambda.lower(vv.i1.Rpm(t):vv.iN.Rpm(t)) / baseMVA; - for j = 1:mdi.idx.nj(t); - for k = 1:mdi.idx.nc(t,j)+1 - ii = find(mdi.flow(t,j,k).mpc.gen(:, GEN_STATUS) > 0); - 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; - 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; + % Obtain contingency reserve prices, per generator and period + mdo.results.RppPrices = zeros(ng, nt); + mdo.results.RpmPrices = zeros(ng, nt); + for t = 1:nt + mdo.results.RppPrices(:, t) = mdo.QP.lambda.lower(vv.i1.Rpp(t):vv.iN.Rpp(t)) / baseMVA; + mdo.results.RpmPrices(:, t) = mdo.QP.lambda.lower(vv.i1.Rpm(t):vv.iN.Rpm(t)) / baseMVA; + for j = 1:mdi.idx.nj(t); + for k = 1:mdi.idx.nc(t,j)+1 + ii = find(mdi.flow(t,j,k).mpc.gen(:, GEN_STATUS) > 0); + 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; + 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; + end end end - end - % Obtain ramping reserve prices, per generator and period - mdo.results.RrpPrices = zeros(ng, mdo.idx.ntramp); - mdo.results.RrmPrices = zeros(ng, mdo.idx.ntramp); - % First, 1:nt-1 - for t = 1:nt-1 - for j1 = 1:mdo.idx.nj(t) - for j2 = 1:mdo.idx.nj(t+1) - if mdi.tstep(t+1).TransMask(j2,j1) - 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; - 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; + % Obtain ramping reserve prices, per generator and period + mdo.results.RrpPrices = zeros(ng, mdo.idx.ntramp); + mdo.results.RrmPrices = zeros(ng, mdo.idx.ntramp); + % First, 1:nt-1 + for t = 1:nt-1 + for j1 = 1:mdo.idx.nj(t) + for j2 = 1:mdo.idx.nj(t+1) + if mdi.tstep(t+1).TransMask(j2,j1) + 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; + 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; + end end end end - end - % then last period only if specified for with terminal state - if ~mdo.OpenEnded - for j1 = 1:mdo.idx.nj(nt) - 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; - 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; - end - end - % Expected wear and tear costs per gen and period - mdo.results.ExpectedRampCost = zeros(ng, mdo.idx.ntramp+1); - % First do first period wrt to InitialPg. - for j = 1:mdi.idx.nj(1) - w = mdo.tstep(1).TransMat(j,1); % the probability of going from initial state to jth - mdo.results.ExpectedRampCost(:, 1) = mdo.results.ExpectedRampCost(:, 1) ... - + 0.5 * w * mdo.RampWearCostCoeff(:,1) .* (mdo.flow(1,j,1).mpc.gen(:,PG) - mdo.InitialPg).^2; - end - % Then the remaining periods - for t = 2:nt - for j2 = 1:mdo.idx.nj(t) - for j1 = 1:mdo.idx.nj(t-1) - w = mdo.tstep(t).TransMat(j2,j1) * mdo.CostWeights(1, j1, t-1); - mdo.results.ExpectedRampCost(:, t) = mdo.results.ExpectedRampCost(:, t) ... - + 0.5 * w * mdo.RampWearCostCoeff(:,t) .* (mdo.flow(t,j2,1).mpc.gen(:,PG) - mdo.flow(t-1,j1,1).mpc.gen(:,PG)) .^2; + % then last period only if specified for with terminal state + if ~mdo.OpenEnded + for j1 = 1:mdo.idx.nj(nt) + 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; + 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; end end - end - % Finally, if there is a terminal state problem, apply cost to - if ~mdo.OpenEnded - for j = 1:mdi.idx.nj(nt) - w = mdi.tstep(t+1).TransMat(1, j) * mdi.CostWeights(1, j, nt); - mdo.results.ExpectedRampCost(:, nt+1) = 0.5 * w * mdo.RampWearCostCoeff(:,nt+1) .* (mdo.TerminalPg - mdo.flow(nt,j,1).mpc.gen(:,PG)) .^2; + % Expected wear and tear costs per gen and period + mdo.results.ExpectedRampCost = zeros(ng, mdo.idx.ntramp+1); + % First do first period wrt to InitialPg. + for j = 1:mdi.idx.nj(1) + w = mdo.tstep(1).TransMat(j,1); % the probability of going from initial state to jth + mdo.results.ExpectedRampCost(:, 1) = mdo.results.ExpectedRampCost(:, 1) ... + + 0.5 * w * mdo.RampWearCostCoeff(:,1) .* (mdo.flow(1,j,1).mpc.gen(:,PG) - mdo.InitialPg).^2; end - end - % Compute expected dispatch, conditional on making it to the - % corresponding period - mdo.results.ExpectedDispatch = zeros(ng, nt); - for t = 1:nt - pp = sum(mdo.CostWeights(1,1:mdo.idx.nj(t),t)'); % gamma(t+1) - for j = 1:mdo.idx.nj(t) - mdo.results.ExpectedDispatch(:,t) = mdo.results.ExpectedDispatch(:,t) + ... - mdo.CostWeights(1,j,t)/pp * mdo.flow(t,j,1).mpc.gen(:,PG); + % Then the remaining periods + for t = 2:nt + for j2 = 1:mdo.idx.nj(t) + for j1 = 1:mdo.idx.nj(t-1) + w = mdo.tstep(t).TransMat(j2,j1) * mdo.CostWeights(1, j1, t-1); + mdo.results.ExpectedRampCost(:, t) = mdo.results.ExpectedRampCost(:, t) ... + + 0.5 * w * mdo.RampWearCostCoeff(:,t) .* (mdo.flow(t,j2,1).mpc.gen(:,PG) - mdo.flow(t-1,j1,1).mpc.gen(:,PG)) .^2; + end + end end - end - % If Cyclic storage, pull InitialStorage value out of x - if ns && mdo.Storage.ForceCyclicStorage - mdo.Storage.InitialStorage = baseMVA * mdo.QP.x(vv.i1.S0:vv.iN.S0); - end - % Compute expected storage state trajectory - mdo.Storage.ExpectedStorageState = zeros(ns,nt); - if ns + % Finally, if there is a terminal state problem, apply cost to + if ~mdo.OpenEnded + for j = 1:mdi.idx.nj(nt) + w = mdi.tstep(t+1).TransMat(1, j) * mdi.CostWeights(1, j, nt); + mdo.results.ExpectedRampCost(:, nt+1) = 0.5 * w * mdo.RampWearCostCoeff(:,nt+1) .* (mdo.TerminalPg - mdo.flow(nt,j,1).mpc.gen(:,PG)) .^2; + end + end + % Compute expected dispatch, conditional on making it to the + % corresponding period + mdo.results.ExpectedDispatch = zeros(ng, nt); for t = 1:nt - pp = sum(mdo.CostWeights(1,1:mdo.idx.nj(t),t)'); %% gamma(t+1) + pp = sum(mdo.CostWeights(1,1:mdo.idx.nj(t),t)'); % gamma(t+1) for j = 1:mdo.idx.nj(t) - Lfj = mdo.tstep(t).Lf( j:mdo.idx.nj(t):(ns-1)*mdo.idx.nj(t)+j, :); - Ngj = mdo.tstep(t).Ng( j:mdo.idx.nj(t):(ns-1)*mdo.idx.nj(t)+j, :); - Nhj = mdo.tstep(t).Nh( j:mdo.idx.nj(t):(ns-1)*mdo.idx.nj(t)+j, :); - mdo.Storage.ExpectedStorageState(:,t) = ... - mdo.Storage.ExpectedStorageState(:,t) + ... - baseMVA * mdo.CostWeights(1,j,t)/pp * ... - ( Lfj * mdo.Storage.InitialStorage/baseMVA + ... - (Ngj + Nhj) * mdo.QP.x ); + mdo.results.ExpectedDispatch(:,t) = mdo.results.ExpectedDispatch(:,t) + ... + mdo.CostWeights(1,j,t)/pp * mdo.flow(t,j,1).mpc.gen(:,PG); end end - mdo.Storage.ExpectedStorageDispatch = ... - mdo.results.ExpectedDispatch(mdo.Storage.UnitIdx, :); - end - % If there is a dynamical system, extract the state vectors and outputs - % from the solution - if ntds - if nzds - mdo.results.Z = zeros(nzds, ntds); - for t = 1:ntds - mdo.results.Z(:,t) = mdo.QP.x(vv.i1.Z(t):vv.iN.Z(t)); + % If Cyclic storage, pull InitialStorage value out of x + if ns && mdo.Storage.ForceCyclicStorage + mdo.Storage.InitialStorage = baseMVA * mdo.QP.x(vv.i1.S0:vv.iN.S0); + end + % Compute expected storage state trajectory + mdo.Storage.ExpectedStorageState = zeros(ns,nt); + if ns + for t = 1:nt + pp = sum(mdo.CostWeights(1,1:mdo.idx.nj(t),t)'); %% gamma(t+1) + for j = 1:mdo.idx.nj(t) + Lfj = mdo.tstep(t).Lf( j:mdo.idx.nj(t):(ns-1)*mdo.idx.nj(t)+j, :); + Ngj = mdo.tstep(t).Ng( j:mdo.idx.nj(t):(ns-1)*mdo.idx.nj(t)+j, :); + Nhj = mdo.tstep(t).Nh( j:mdo.idx.nj(t):(ns-1)*mdo.idx.nj(t)+j, :); + mdo.Storage.ExpectedStorageState(:,t) = ... + mdo.Storage.ExpectedStorageState(:,t) + ... + baseMVA * mdo.CostWeights(1,j,t)/pp * ... + ( Lfj * mdo.Storage.InitialStorage/baseMVA + ... + (Ngj + Nhj) * mdo.QP.x ); + end end + mdo.Storage.ExpectedStorageDispatch = ... + mdo.results.ExpectedDispatch(mdo.Storage.UnitIdx, :); end - mdo.results.Y = zeros(nyds, ntds); - if nyds - for t = 1:ntds - mdo.results.Y(:, t) = ... - mdo.QP.A(ll.i1.DSy(t):ll.iN.DSy(t), :) * mdo.QP.x; + % If there is a dynamical system, extract the state vectors and outputs + % from the solution + if ntds + if nzds + mdo.results.Z = zeros(nzds, ntds); + for t = 1:ntds + mdo.results.Z(:,t) = mdo.QP.x(vv.i1.Z(t):vv.iN.Z(t)); + end + end + mdo.results.Y = zeros(nyds, ntds); + if nyds + for t = 1:ntds + mdo.results.Y(:, t) = ... + mdo.QP.A(ll.i1.DSy(t):ll.iN.DSy(t), :) * mdo.QP.x; + end end end - end - mdo.results.f = mdo.QP.f; -end % if mpopt.most.solve_model + mdo.results.f = mdo.QP.f; + end % if success +end % if mpopt.most.solve_model +mdo.results.success = success; mdo.results.SetupTime = et_setup; mdo.results.SolveTime = toc(t0); diff --git a/most/lib/mostver.m b/most/lib/mostver.m index 3f7b300e..08f6548a 100644 --- a/most/lib/mostver.m +++ b/most/lib/mostver.m @@ -9,7 +9,7 @@ % See also MPVER. % MOST -% Copyright (c) 2010-2017, Power Systems Engineering Research Center (PSERC) +% Copyright (c) 2010-2018, Power Systems Engineering Research Center (PSERC) % by Ray Zimmerman, PSERC Cornell % % This file is part of MOST. @@ -17,9 +17,9 @@ % See https://github.com/MATPOWER/most for more info. v = struct( 'Name', 'MOST', ... - 'Version', '1.0.1-dev', ... + 'Version', '1.0.1', ... 'Release', '', ... - 'Date', '27-Sep-2017' ); + 'Date', '30-Oct-2018' ); if nargout > 0 if nargin > 0 rv = v; diff --git a/most/lib/t/t_most_fixed_res.m b/most/lib/t/t_most_fixed_res.m index 97910e94..fc4c66dc 100644 --- a/most/lib/t/t_most_fixed_res.m +++ b/most/lib/t/t_most_fixed_res.m @@ -30,6 +30,9 @@ function t_most_fixed_res(quiet) if have_fcn('gurobi') mpopt = mpoption(mpopt, 'gurobi.method', 1); %% dual-simplex end +if have_fcn('cplex') + mpopt = mpoption(mpopt, 'cplex.qpmethod', 2); %% dual-simplex +end % if have_fcn('mosek') % sc = mosek_symbcon; % mpopt = mpoption(mpopt, 'mosek.lp_alg', sc.MSK_OPTIMIZER_DUAL_SIMPLEX); %% dual simplex diff --git a/most/lib/t/t_most_w_ds.m b/most/lib/t/t_most_w_ds.m index e42fa072..950728e8 100644 --- a/most/lib/t/t_most_w_ds.m +++ b/most/lib/t/t_most_w_ds.m @@ -46,20 +46,39 @@ function t_most_w_ds(quiet) mpopt = mpoption('verbose', 0); + %% choose solver if have_fcn('cplex') mpopt = mpoption(mpopt, 'most.solver', 'CPLEX'); - mpopt = mpoption(mpopt, 'cplex.opts.threads', 2); % set this manually here elseif have_fcn('gurobi') mpopt = mpoption(mpopt, 'most.solver', 'GUROBI'); + elseif have_fcn('quadprog_ls') + mpopt = mpoption(mpopt, 'most.solver', 'OT'); + elseif have_fcn('mosek') + mpopt = mpoption(mpopt, 'most.solver', 'MOSEK'); + else + mpopt = mpoption(mpopt, 'most.solver', 'MIPS'); + if have_fcn('pardiso') + mpopt = mpoption(mpopt, 'mips.linsolver', 'PARDISO'); + end + end + + %% set options + if have_fcn('cplex') + mpopt = mpoption(mpopt, 'cplex.opts.threads', 2); % set this manually here + end + if have_fcn('gurobi') + mpopt = mpoption(mpopt, 'most.solver', 'GUROBI'); mpopt = mpoption(mpopt, 'gurobi.method', 2); %% barrier mpopt = mpoption(mpopt, 'gurobi.threads', 2); - mpopt = mpoption(mpopt, 'gurobi.opts.BarConvTol', 1e-7); %% 1e-8 - mpopt = mpoption(mpopt, 'gurobi.opts.FeasibilityTol', 1e-5); %% 1e-6 + mpopt = mpoption(mpopt, 'gurobi.opts.BarConvTol', 1e-6); %% 1e-8 + mpopt = mpoption(mpopt, 'gurobi.opts.FeasibilityTol', 1e-4); %% 1e-6 mpopt = mpoption(mpopt, 'gurobi.opts.OptimalityTol', 1e-5); %% 1e-6 - elseif have_fcn('quadprog_ls') + end + if have_fcn('quadprog_ls') mpopt = mpoption(mpopt, 'most.solver', 'OT'); mpopt = mpoption(mpopt, 'quadprog.TolFun', 1e-13); - elseif have_fcn('mosek') + end + if have_fcn('mosek') mpopt = mpoption(mpopt, 'most.solver', 'MOSEK'); mpopt = mpoption(mpopt, 'mosek.num_threads', 2); else @@ -69,6 +88,7 @@ function t_most_w_ds(quiet) mpopt = mpoption(mpopt, 'mips.linsolver', 'PARDISO'); end end + % mpopt = mpoption(mpopt, 'most.solver', 'GUROBI'); % mpopt = mpoption(mpopt, 'most.solver', 'CLP'); % mpopt = mpoption(mpopt, 'most.solver', 'IPOPT'); % mpopt = mpoption(mpopt, 'most.solver', 'MIPS'); diff --git a/most/lib/t/test_most.m b/most/lib/t/test_most.m index 001f4f83..14a03dd4 100644 --- a/most/lib/t/test_most.m +++ b/most/lib/t/test_most.m @@ -44,7 +44,6 @@ tests{end+1} = 't_most_30b_3_1_17'; end tests{end+1} = 't_most_fixed_res'; -tests{end+1} = 't_most_w_ds'; tests{end+1} = 't_most_30b_1_1_0_uc'; if have_c3sopf tests{end+1} = 't_most_sp'; @@ -52,6 +51,7 @@ end tests{end+1} = 't_most_uc'; tests{end+1} = 't_most_suc'; +tests{end+1} = 't_most_w_ds'; %% run the tests all_ok = t_run_tests( tests, verbose );