From c01e99f948f39439f90d1f40d3cf0704ea915d92 Mon Sep 17 00:00:00 2001 From: alfoa Date: Wed, 5 Feb 2020 10:51:12 -0700 Subject: [PATCH] new address --- .gitmodules | 3 + plugins/CashFlow/.gitlab-ci.yml | 37 + .../.gitlab/issue_templates/ISSUE_TEMPLATE.md | 25 + .../PULL_REQUEST_TEMPLATE.md | 19 + plugins/CashFlow/README.md | 5 + plugins/CashFlow/__init__.py | 5 + plugins/CashFlow/doc/00README.txt | 3 + .../CashFlow/doc/CashFlow_RAVEN_plugin.pptx | Bin 0 -> 223180 bytes plugins/CashFlow/doc/Installation.tex | 52 + plugins/CashFlow/doc/Makefile | 22 + plugins/CashFlow/doc/include/CashFlow.tex | 268 ++++ plugins/CashFlow/doc/user_manual.bib | 23 + plugins/CashFlow/doc/user_manual.tex | 266 ++++ plugins/CashFlow/src/Amortization.py | 35 + plugins/CashFlow/src/CashFlowUser.py | 87 ++ plugins/CashFlow/src/CashFlow_ExtMod.py | 170 +++ plugins/CashFlow/src/CashFlows.py | 1081 +++++++++++++++++ plugins/CashFlow/src/__init__.py | 0 plugins/CashFlow/src/main.py | 599 +++++++++ plugins/CashFlow/tests/CashFlow_test_IRR.xml | 79 ++ plugins/CashFlow/tests/CashFlow_test_NPV.xml | 79 ++ .../tests/CashFlow_test_NPV_RunAsCode.py | 16 + .../tests/CashFlow_test_NPV_componentTax.xml | 80 ++ .../tests/CashFlow_test_NPV_customTime.xml | 82 ++ .../tests/CashFlow_test_NPV_customTime2.xml | 82 ++ .../tests/CashFlow_test_NPV_custom_MACRS.xml | 77 ++ .../CashFlow_test_NPV_expandedDriver.xml | 115 ++ .../tests/CashFlow_test_NPV_multiplier.xml | 80 ++ .../tests/CashFlow_test_NPVsearch.xml | 79 ++ plugins/CashFlow/tests/CashFlow_test_PI.xml | 79 ++ .../CashFlow/tests/Cash_Flow_input_IRR.xml | 50 + .../CashFlow/tests/Cash_Flow_input_NPV.xml | 56 + .../Cash_Flow_input_NPV_componentTax.xml | 50 + .../tests/Cash_Flow_input_NPV_customTime.xml | 51 + .../tests/Cash_Flow_input_NPV_customTime2.xml | 50 + .../tests/Cash_Flow_input_NPVsearch.xml | 49 + plugins/CashFlow/tests/Cash_Flow_input_PI.xml | 49 + .../Cash_Flow_input_custom_macrs_NPV.xml | 57 + plugins/CashFlow/tests/RevenueData.py | 152 +++ plugins/CashFlow/tests/VarInp.txt | 5 + plugins/CashFlow/tests/tests | 86 ++ 41 files changed, 4203 insertions(+) create mode 100644 plugins/CashFlow/.gitlab-ci.yml create mode 100644 plugins/CashFlow/.gitlab/issue_templates/ISSUE_TEMPLATE.md create mode 100644 plugins/CashFlow/.gitlab/merge_request_templates/PULL_REQUEST_TEMPLATE.md create mode 100644 plugins/CashFlow/README.md create mode 100644 plugins/CashFlow/__init__.py create mode 100644 plugins/CashFlow/doc/00README.txt create mode 100644 plugins/CashFlow/doc/CashFlow_RAVEN_plugin.pptx create mode 100644 plugins/CashFlow/doc/Installation.tex create mode 100644 plugins/CashFlow/doc/Makefile create mode 100644 plugins/CashFlow/doc/include/CashFlow.tex create mode 100644 plugins/CashFlow/doc/user_manual.bib create mode 100644 plugins/CashFlow/doc/user_manual.tex create mode 100644 plugins/CashFlow/src/Amortization.py create mode 100644 plugins/CashFlow/src/CashFlowUser.py create mode 100644 plugins/CashFlow/src/CashFlow_ExtMod.py create mode 100644 plugins/CashFlow/src/CashFlows.py create mode 100644 plugins/CashFlow/src/__init__.py create mode 100644 plugins/CashFlow/src/main.py create mode 100644 plugins/CashFlow/tests/CashFlow_test_IRR.xml create mode 100644 plugins/CashFlow/tests/CashFlow_test_NPV.xml create mode 100644 plugins/CashFlow/tests/CashFlow_test_NPV_RunAsCode.py create mode 100644 plugins/CashFlow/tests/CashFlow_test_NPV_componentTax.xml create mode 100644 plugins/CashFlow/tests/CashFlow_test_NPV_customTime.xml create mode 100644 plugins/CashFlow/tests/CashFlow_test_NPV_customTime2.xml create mode 100644 plugins/CashFlow/tests/CashFlow_test_NPV_custom_MACRS.xml create mode 100644 plugins/CashFlow/tests/CashFlow_test_NPV_expandedDriver.xml create mode 100644 plugins/CashFlow/tests/CashFlow_test_NPV_multiplier.xml create mode 100644 plugins/CashFlow/tests/CashFlow_test_NPVsearch.xml create mode 100644 plugins/CashFlow/tests/CashFlow_test_PI.xml create mode 100644 plugins/CashFlow/tests/Cash_Flow_input_IRR.xml create mode 100644 plugins/CashFlow/tests/Cash_Flow_input_NPV.xml create mode 100644 plugins/CashFlow/tests/Cash_Flow_input_NPV_componentTax.xml create mode 100644 plugins/CashFlow/tests/Cash_Flow_input_NPV_customTime.xml create mode 100644 plugins/CashFlow/tests/Cash_Flow_input_NPV_customTime2.xml create mode 100644 plugins/CashFlow/tests/Cash_Flow_input_NPVsearch.xml create mode 100644 plugins/CashFlow/tests/Cash_Flow_input_PI.xml create mode 100644 plugins/CashFlow/tests/Cash_Flow_input_custom_macrs_NPV.xml create mode 100644 plugins/CashFlow/tests/RevenueData.py create mode 100644 plugins/CashFlow/tests/VarInp.txt create mode 100644 plugins/CashFlow/tests/tests diff --git a/.gitmodules b/.gitmodules index e69de29bb2..1842c7ec23 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "plugins/CashFlow"] + path = plugins/CashFlow + url = https://hpcgitlab.hpc.inl.gov/RAVEN_PLUGINS/CashFlow.git diff --git a/plugins/CashFlow/.gitlab-ci.yml b/plugins/CashFlow/.gitlab-ci.yml new file mode 100644 index 0000000000..8e66321f01 --- /dev/null +++ b/plugins/CashFlow/.gitlab-ci.yml @@ -0,0 +1,37 @@ +before_script: + # Print out python version for debugging + - python -V + # set up proxy for runner user + - export http_proxy=http://webbalance.inl.gov:8080; + export https_proxy=http://webbalance.inl.gov:8080; + export ftp_proxy=http://webbalance.inl.gov:8080; + export ftps_proxy=http://webbalance.inl.gov:8080; + # clone raven + - git clone https://github.com/idaholab/raven.git + - cd raven + - git checkout master + - git clean -xfd + # check that we have conda + - export PATH=$HOME/miniconda2/bin:$PATH + - if which conda 2> /dev/null; then + echo "Conda found!"; + conda update conda; + else + echo "ERROR Conda not found!"; + exit 404; + fi + # create libraries or create them + - ./scripts/establish_conda_env.sh --install --conda-defs $HOME/miniconda2/etc/profile.d/conda.sh + # check python installation + - which python + - python -V + - ./build_raven + # Install CashFlow branch ... + - python ./scripts/install_plugins.py -s ../ + # return to project path main dir + - cd .. + +test: + script: + - cd raven + - ./run_tests -j4 --plugins --no-color diff --git a/plugins/CashFlow/.gitlab/issue_templates/ISSUE_TEMPLATE.md b/plugins/CashFlow/.gitlab/issue_templates/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..34e1bc7cc8 --- /dev/null +++ b/plugins/CashFlow/.gitlab/issue_templates/ISSUE_TEMPLATE.md @@ -0,0 +1,25 @@ +-------- +Issue Description +-------- +##### Please describe your issue here. + + + + +---------------- +For Change Control Board: Issue Review +---------------- +This review should occur before any development is performed as a response to this issue. +- [ ] 1. Is it tagged with a type: defect or improvement? +- [ ] 2. Is it tagged with a priority: critical, normal or minor? +- [ ] 3. If it is a defect, can it cause wrong results for users? If so an email needs to be sent to the users. +- [ ] 4. Is a rationale provided? (Such as explaining why the improvement is needed or why current code is wrong.) + +------- +For Change Control Board: Issue Closure +------- +This review should occur when the issue is imminently going to be closed. +- [ ] 1. If the issue is a defect, is the defect fixed? +- [ ] 2. If the issue is a defect, is the defect tested for in the regression test system? (If not explain why not.) +- [ ] 3. If the issue can impact users, has an email to the users group been written (the email should specify if the defect impacts stable or master)? +- [ ] 4. If the issue is being closed without a merge request, has an explanation of why it is being closed been provided? diff --git a/plugins/CashFlow/.gitlab/merge_request_templates/PULL_REQUEST_TEMPLATE.md b/plugins/CashFlow/.gitlab/merge_request_templates/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..2938900c52 --- /dev/null +++ b/plugins/CashFlow/.gitlab/merge_request_templates/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,19 @@ +-------- +Pull Request Description +-------- +##### What issue does this change request address? (Use "#" before the issue to link it, i.e., #42.) + + +##### What are the significant changes in functionality due to this change request? + + +---------------- +For Change Control Board: Change Request Review +---------------- +The following review must be completed by an authorized member of the Change Control Board. +- [ ] 1. Review all computer code. +- [ ] 2. If any changes occur to the input syntax, there must be an accompanying change to the user manual. If the input syntax change deprecates existing input files, a conversion script needs to be added. +- [ ] 3. Make sure the Python code and commenting standards are respected (camelBack, etc.) - See on the [wiki](https://github.com/idaholab/raven/wiki/RAVEN-Code-Standards#python) for details. +- [ ] 4. Regression tests have to complete successfully. +- [ ] 5. If significant functionality is added, there must be tests added to check this. Tests should cover all possible options. Multiple short tests are preferred over one large test. +- [ ] 6. The merge request must reference an issue. If the issue is closed, the issue close checklist shall be done. diff --git a/plugins/CashFlow/README.md b/plugins/CashFlow/README.md new file mode 100644 index 0000000000..87e92fc4ca --- /dev/null +++ b/plugins/CashFlow/README.md @@ -0,0 +1,5 @@ +This is the git repository for CashFlow. +CashFlow is a plugin for the RAVEN code. + +The plugin enables the capability to compute the NPV (Net Present Value), the IRR (Internal Rate of Return) and the PI (Profitability Index) with RAVEN. Furthermore, it is possible to do an NPV, IRR or PI search, i.e. CashFlow will compute a multiplicative value (for example the production cost) so that the NPV, IRR or PI has a desired value. The plugin allows for a generic definition of cash flows which drivers are provided by RAVEN. Furthermore, CashFlow includes flexible options to deal with taxes, inflation, discounting and offers capabilities to compute a combined cash flow for components with different component lives. + diff --git a/plugins/CashFlow/__init__.py b/plugins/CashFlow/__init__.py new file mode 100644 index 0000000000..7434cd27f4 --- /dev/null +++ b/plugins/CashFlow/__init__.py @@ -0,0 +1,5 @@ +from CashFlow.src import Amortization +from CashFlow.src import CashFlow_ExtMod +from CashFlow.src import CashFlows +from CashFlow.src import CashFlowUser +from CashFlow.src import main as CashFlow diff --git a/plugins/CashFlow/doc/00README.txt b/plugins/CashFlow/doc/00README.txt new file mode 100644 index 0000000000..6cd991af49 --- /dev/null +++ b/plugins/CashFlow/doc/00README.txt @@ -0,0 +1,3 @@ +To compile the manual, type +>make + diff --git a/plugins/CashFlow/doc/CashFlow_RAVEN_plugin.pptx b/plugins/CashFlow/doc/CashFlow_RAVEN_plugin.pptx new file mode 100644 index 0000000000000000000000000000000000000000..6d60b37d9a2f2bf617789a952a259a7bcd942606 GIT binary patch literal 223180 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

;qDQZ@^6Y-&VAyLvQoo~Vphww%lt<%! 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 ax_vGU10F~LfT!RecJTDI|4Z;t4fsEHcbUKd literal 0 HcmV?d00001 diff --git a/plugins/CashFlow/doc/Installation.tex b/plugins/CashFlow/doc/Installation.tex new file mode 100644 index 0000000000..ee0f2a9a2a --- /dev/null +++ b/plugins/CashFlow/doc/Installation.tex @@ -0,0 +1,52 @@ + +\section{Installation} +\label{sec:Installation} + +This section of the manual describes how to install the 'Economics for RAVEN' plug-in and use it from within RAVEN \cite{RAVEN}. + +\subsection{Installing the plug-in} +The CashFlow plugin is distributed with RAVEN. If you just want to use the plugin and are not interested in developing additional features or bug-fixes for it, just make sure that you have access to the plugin repository and you will automatically get it when installing RAVEN. +For more information, please refere to the RAVEN manual. + +\subsection{Accessing the plug-in from within RAVEN} +The plugin can be accessed as a special subtype of the External model. The syntax is given in Listing \ref{lst:CashFlowfromRAVEN}. + +\begin{lstlisting}[style=XML,morekeywords={anAttribute},caption=Call CashFlow from RAVEN input., label=lst:CashFlowfromRAVEN] + + Input and output variables needed by CashFlow + + +\end{lstlisting} + +\subsection{Running the plugin as a stand-alone python code} + +In addition to accessing the plug-in from within RAVEN, it can also be run as a stand-alone python program. This is useful for example for testing. However, since CashFlow is still a RAVEN plugin, RAVEN needs to be installed and the plugin needs to be in the plugin-folder for that to work. + +Assuming RAVEN is installed and the plugin is in the proper directory (execution will generate an error if its not), one can run it using the command shown in Listing \ref{lst:CashFlowAsCode}. + +\small +\begin{lstlisting}[caption=CashFlow run as stand-alone python code, label=lst:CashFlowAsCode] +~/raven --> python plugins/CashFlow/src/CashFlow_ExtMode.py -h +usage: Cash_Flow.py [-h] -iXML inp_file -iINP inp_file -o out_file + +Run RAVEN CashFlow plugin as stand-alone code + +arguments: + -h, --help show this help message and exit + -iXML inp_file XML CashFlow input file name + -iINP inp_file CashFlow input file name with the input + variable list + -o out_file Output file name +\end{lstlisting} +\normalsize + +The XML CashFlow input is the one that contains the component and cash flow definitions, i.e. the one in \xmlAttr{xmlToLoad} in Listing \ref{lst:CashFlowfromRAVEN}. + +The CashFlow input file contains the variables that RAVEN provides when using the CashFlow as a plugin, i.e. the cash flow drivers and multipliers. Listing \ref{lst:CashFlowInputFile} shows an example of the CashFlow input file for the cash flow definitions given in Listing \ref{lst:InputExample}. + +\begin{lstlisting}[caption=CashFlow run as stand-alone python code, label=lst:CashFlowInputFile] +Cfdriver1 5.5 +multiplier1 1.0 +Cfdriver2 10.8 +multiplier2 2.0 +\end{lstlisting} diff --git a/plugins/CashFlow/doc/Makefile b/plugins/CashFlow/doc/Makefile new file mode 100644 index 0000000000..a9c1ad896f --- /dev/null +++ b/plugins/CashFlow/doc/Makefile @@ -0,0 +1,22 @@ +SRCFILE = user_manual +MANUAL_FILES = user_manual.tex +LATEX_FLAGS=-interaction=nonstopmode + +all: Economics_for_RAVEN_user_manual.pdf + +Economics_for_RAVEN_user_manual.pdf: $(MANUAL_FILES) version.tex + pdflatex $(LATEX_FLAGS) $(SRCFILE).tex + bibtex $(SRCFILE) + pdflatex $(LATEX_FLAGS) $(SRCFILE).tex + pdflatex $(LATEX_FLAGS) $(SRCFILE).tex + +.PHONY: clean + +clean: + @rm -f *~ *.aux *.bbl *.blg *.log *.out *.toc *.lot *.lof $(SRCFILE).pdf + +#creating version file +version.tex : $(MANUAL_FILES) + git log -1 --format="%H %an %aD" .. > version.tex + + diff --git a/plugins/CashFlow/doc/include/CashFlow.tex b/plugins/CashFlow/doc/include/CashFlow.tex new file mode 100644 index 0000000000..99f9ef6a32 --- /dev/null +++ b/plugins/CashFlow/doc/include/CashFlow.tex @@ -0,0 +1,268 @@ +\section{CashFlow for RAVEN} +A generalized module (called CashFlow) for economic analysisi within RAVEN has been developed \cite{MSApril2017}. The module is able to compute +the NPV (Net Present Value), the IRR (Internal Rate of Return) and the PI (Profitability Index). Furthermore, it is possible to +do an NPV, IRR or PI search, i.e. CashFlow will compute a multiplicative value (for example the production cost) so that the +NPV, IRR or PI has a desired value (for details see \ref{subsec:NPV_search}, NPV\_search). This CashFlow module has been written using the script language Python. +The Python code can be used as an “external model” in RAVEN (for installation and usage instructions, see \ref{sec:Installation}). + +The input of CashFlow is an XML file. An example of the input structure is given in Listing \ref{lst:InputExample}. The following section will discuss the + different keywords in the input and describe how they are used in the CashFlow module. + +\begin{lstlisting}[style=XML,morekeywords={anAttribute},caption=Economics input example., label=lst:InputExample] + + + + Component1|Cfname1 + Component1|Cfname2 + ... + + 0.08 + 0.392 + 0.04 + 100 + + + + 20 + 10 + 3 + 0.3 + 0.07 + + + Cfdriver1 + -4000000000 + 1000000000 + 1.0 + + + + ... + + ... + + + + + ... + + ... + +\end{lstlisting} + +As one can see, all the specifications of the CashFlow module are given in the \xmlNode{Economics} block. The block accepts an attribute called \xmlAttr{verbosity}, +which can range from 0 to 100, 0 meaning maximum debug verbosity and 100 meaning errors only. Setting the verbosity to 50 will output (in addition to errors) the NPV, IRR, PI or NPV\_mult. Inside the \xmlNode{Economics} block, there are two types of blocks: \xmlNode{Global} and \xmlNode{Component}. + +\subsection{\xmlNode{Global}} +Exactly one \xmlNode{Global} block has to be provided. The \xmlNode{Global} block does not have any attributes. The following sub-blocks can be given in the \xmlNode{Global} block: + +\begin{enumerate} +\item[\xmlNode{Indicator}] List of cash flows considered in the computation of the economic indicator. See later for the definition + of the cash flows. Only cash flows listed here are considered, additional cash flows defined, but not listed are ignored. +\xmlNode{Indicator} can have two attributes: + \begin{enumerate} + \item[\xmlAttr{name}] The names of the economic indicators that should be computed. So far, \textbf{'NPV'}, \textbf{'NPV\_search'}, \textbf{'IRR'} and \textbf{'PI'} are supported. More than one indicator can be asked for. +The \xmlAttr{name} attribute can contain a comma-separated list as shown in the example in Listing \ref{lst:InputExample}. + +\textbf{NPV}: computes the NPV according to Eq. \ref{eq:NPV}. +\begin{equation}\label{eq:NPV} +NPV=\sum_{y=0}^{N}\frac{CF_{y}}{(1+DiscountRate)^{y}} +\end{equation} + +The sum runs over the years $y=0$ to $N$. The net cash flows $CF_{y}$ are the sum of all cash flows defined in the indicator block (see later for how to define these cash flows). +$N$ is the least common multiple (LCM) of all component life times involved. This guarantees that the NPV is computed for a time span so that all components reach their end of life in the same year. +The individual component cash flows are repeated until the LCM is reached. For example, lets assume the calculation involves two components \textit{Component1} and \textit{Component2} + with life times of 60 years and 40 years respectively. $N$ will be 120 years where 2 successive \textit{Component1} and 3 successive \textit{Component2} will be build. For every ‘building year’, +the cash flow for the last year (of the old component) and the year zero (for the newly built component) will be summed. Table \ref{tbl:cashflows} shows an example for illustration. +The variable sent back to RAVEN, i.e. what needs to be added to the output data object is 'NPV'. + +\begin{table}[] +\centering +\caption{Example cash flows for NPV calculation.} +\label{tbl:cashflows} +\begin{tabular}{ll|l|l|l|l|l|ll} +\cline{3-4} \cline{6-7} + & & \multicolumn{2}{l|}{Compo 1} & & \multicolumn{2}{l|}{Compo 2} & & \\ \cline{1-1} \cline{3-4} \cline{6-7} \cline{9-9} +\multicolumn{1}{|l|}{Year} & & \begin{tabular}[c]{@{}l@{}}Comp. \\ lifetime\end{tabular} & \begin{tabular}[c]{@{}l@{}}Cash Flow\\ (year)\end{tabular} & & \begin{tabular}[c]{@{}l@{}}Compo. \\ Lifetime\end{tabular} & \begin{tabular}[c]{@{}l@{}} Cash Flow \\ (year) \end{tabular} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{\begin{tabular}[c]{@{}l@{}}Total Net Cash flow \\ ($CF_{y}$) \end{tabular}} \\ \cline{1-1} \cline{3-4} \cline{6-7} \cline{9-9} +\multicolumn{1}{|l|}{0} & & 0 & $CF^{comp1}_{0}$ & & 0 & $CF^{comp2}_{0}$ & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{$CF^{comp1}_{0} + CF^{comp2}_{0}$ } \\ \cline{1-1} \cline{3-4} \cline{6-7} \cline{9-9} +\multicolumn{1}{|l|}{1} & & 1 & $CF^{comp1}_{1}$ & & 1 & $CF^{comp2}_{1}$ & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{$CF^{comp1}_{1} + CF^{comp2}_{1}$ } \\ \cline{1-1} \cline{3-4} \cline{6-7} \cline{9-9} +\multicolumn{1}{|l|}{…} & & & & & & & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} \\ \cline{1-1} \cline{3-4} \cline{6-7} \cline{9-9} +\multicolumn{1}{|l|}{39} & & 39 & $CF^{comp1}_{39}$ & & 39 & $CF^{comp2}_{39}$ & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{$CF^{comp1}_{39} + CF^{comp2}_{39}$ } \\ \cline{1-1} \cline{3-4} \cline{6-7} \cline{9-9} +\multicolumn{1}{|l|}{40} & & 40 & $CF^{comp1}_{40}$ & & 40 and 0 & \begin{tabular}[c]{@{}l@{}}$CF^{comp2}_{40}$ \\ $+ CF^{comp2}_{0}$ \end{tabular} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{\begin{tabular}[c]{@{}l@{}}$CF^{comp1}_{40} + CF^{comp2}_{40}$ \\ $+ CF^{comp2}_{0}$ \end{tabular}} \\ \cline{1-1} \cline{3-4} \cline{6-7} \cline{9-9} +\multicolumn{1}{|l|}{41} & & 41 & $CF^{comp1}_{41}$ & & 1 & $CF^{comp2}_{1}$ & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{$CF^{comp1}_{41} + CF^{comp2}_{1}$ } \\ \cline{1-1} \cline{3-4} \cline{6-7} \cline{9-9} +\multicolumn{1}{|l|}{…} & & & & & & & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} \\ \cline{1-1} \cline{3-4} \cline{6-7} \cline{9-9} +\multicolumn{1}{|l|}{59} & & 59 & $CF^{comp1}_{59}$ & & 19 & $CF^{comp2}_{19}$ & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{$CF^{comp1}_{59} + CF^{comp2}_{19}$ } \\ \cline{1-1} \cline{3-4} \cline{6-7} \cline{9-9} +\multicolumn{1}{|l|}{60} & & 60 and 0 & \begin{tabular}[c]{@{}l@{}}$CF^{comp1}_{60}$ \\ $+ CF^{comp1}_{0}$ \end{tabular} & & 20 & $CF^{comp2}_{20}$ & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{\begin{tabular}[c]{@{}l@{}}$CF^{comp1}_{60} + CF^{comp1}_{0}$ \\ $+ CF^{comp2}_{20}$ \end{tabular}} \\ \cline{1-1} \cline{3-4} \cline{6-7} \cline{9-9} +\multicolumn{1}{|l|}{61} & & 1 & $CF^{comp1}_{1}$ & & 21 & $CF^{comp2}_{21}$ & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{$CF^{comp1}_{1} + CF^{comp2}_{21}$ } \\ \cline{1-1} \cline{3-4} \cline{6-7} \cline{9-9} +\multicolumn{1}{|l|}{…} & & & & & & & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} \\ \cline{1-1} \cline{3-4} \cline{6-7} \cline{9-9} +\multicolumn{1}{|l|}{79} & & 19 & $CF^{comp1}_{19}$ & & 39 & $CF^{comp2}_{39}$ & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{$CF^{comp1}_{19} + CF^{comp2}_{39}$ } \\ \cline{1-1} \cline{3-4} \cline{6-7} \cline{9-9} +\multicolumn{1}{|l|}{80} & & 20 & $CF^{comp1}_{20}$ & & 40 and 0 & \begin{tabular}[c]{@{}l@{}}$CF^{comp2}_{40}$ \\ $+ CF^{comp2}_{0}$ \end{tabular} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{\begin{tabular}[c]{@{}l@{}}$CF^{comp1}_{20} + CF^{comp2}_{40}$ \\ $+ CF^{comp2}_{0}$ \end{tabular}} \\ \cline{1-1} \cline{3-4} \cline{6-7} \cline{9-9} +\multicolumn{1}{|l|}{81} & & 21 & $CF^{comp1}_{21}$ & & 1 & $CF^{comp2}_{1}$ & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{$CF^{comp1}_{21} + CF^{comp2}_{1}$ } \\ \cline{1-1} \cline{3-4} \cline{6-7} \cline{9-9} +\multicolumn{1}{|l|}{…} & & & & & & & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} \\ \cline{1-1} \cline{3-4} \cline{6-7} \cline{9-9} +\multicolumn{1}{|l|}{119} & & 59 & $CF^{comp1}_{59}$ & & 39 & $CF^{comp2}_{39}$ & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{$CF^{comp1}_{59} + CF^{comp2}_{39}$ } \\ \cline{1-1} \cline{3-4} \cline{6-7} \cline{9-9} +\multicolumn{1}{|l|}{120} & & 60 & $CF^{comp1}_{60}$ & & 40 & $CF^{comp2}_{40}$ & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{$CF^{comp1}_{60} + CF^{comp2}_{40}$} \\ \cline{1-1} \cline{3-4} \cline{6-7} \cline{9-9} +\end{tabular} +\end{table} + +\textbf{PI}: computes the PI according to Eq. \ref{eq:PI}. +\begin{equation}\label{eq:PI} +PI=\frac{NPV}{Initial\_investment} +\end{equation} +where the NPV is calculated as explained above and the $Initial\_investment$ is the Total Net Cash flow at year zero, i.e. $CF_{0}$ in the example above. +The variable sent back to RAVEN, i.e. what needs to be added to the output data object is 'PI'. + +\textbf{IRR}: computes the IRR according to Eq. \ref{eq:IRR}. +\begin{equation}\label{eq:IRR} +0=\sum_{y=0}^{N}\frac{CF_{y}}{(1+IRR)^{y}} +\end{equation} +Same as for the NPV, the sum runs over the years $y=0$ to $N$. The net cash flows $CF_{y}$ are the sum of all cash flows defined in the indicator block +(see explanation of NPV above for details). $N$ is the least common multiple (LCM) of all component life times involved. +The variable sent back to RAVEN, i.e. what needs to be added to the output data object is 'IRR'. + +\textbf{NPV\_search}: The NPV search finds a multiplier '$x$' that multiplies some of the cash flows, so that the NPV has a desired value (defined by the \xmlAttr{target} attribute). The equation solved is shown in Eq. \ref{eq:NPV_search}. +\label{subsec:NPV_search} +\begin{equation}\label{eq:NPV_search} +'target'=\sum_{y=0}^{N}\frac{CF^{dep\_on\_x}_{y}}{(1+DiscountRate)^{y}}x + \sum_{y=0}^{N}\frac{CF^{not\_dep\_on\_x}_{y}}{(1+DiscountRate)^{y}} +\end{equation} + +The cash flows that multiply ‘$x$’ have to have the \xmlAttr{mult\_target} attribute equal \textbf{'true'} (see later in cash flow definition). This functionality can be used for + example to find a commodity price so that the NPV is zero. In this case, the \xmlAttr{target} will be set to \textbf{'0'} and all cash flows that depend (linearly) on the price will take + \xmlAttr{mult\_target}$=$\textbf{'true'}, i.e. for example the revenue, while cash flows that do not depend on the price will have \xmlAttr{mult\_target}$=$\textbf{'false'}, i.e. for example the capital cost. +The variable sent back to RAVEN, i.e. what needs to be added to the output data object is 'NPV\_mult'. + +\textbf{Note on IRR and PI search}: It should be noted that although the only search keyword allowed in \xmlAttr{name} is \textbf{NPV\_search}, it is possible to perform IRR and PI searches as well. + + \begin{itemize} + \item To do an IRR search, the DiscountRate is set to the desired IRR and a NPV search with the target of ‘0’ is performed. + \item To perform a PI search, an NPV search can be performed where the target PI is multiplied with the initial investment. + \end{itemize} + + + \item[\xmlAttr{Target}] Target value for the NPV search, i.e. \textbf{'0'} will look for ‘$x$’ so that $NPV(x) = 0$. + + \end{enumerate} + +\item[\xmlNode{DiscountRate}] The discount rate used to compute the NPV and PI. Not used for the computation of the IRR (although it must be input). +\item[\xmlNode{tax}] The standard tax rate used to compute the taxes if no other tax rate is specified in the componet blocks. This is a required input. If a tax rate is specified inside a component block, the componet will use that tax rate. If no tax rate is specified in a component, this standard tax rate is used for the component. See later in the definition of the cash flows for more details how the tax rate is used. +\item[\xmlNode{inflation}] The standard inflation rate used to compute the inflation if no other inflation rate is specified in the componet blocks. This is a required input. If a inflation rate is specified inside a component block, the componet will use that inflation rate. If no inflation rate is specified in a component, this standard inflation rate is used for the component. See later in the definition of the cash flows for more details how the inflation rate is used. + +\item[\xmlNode{ProjectTime}] This is a optional input. If it is included in the input, the global project time is not the LCM of all components (see \xmlNode{Indicator} attribute \xmlAttr{name} for more information), but the time indicated here. + +\end{enumerate} + + +\subsection{\xmlNode{Component}} + +The user can define as many \xmlNode{Component} blocks as needed. A component is typically a part of the system that has the same lifetime and +the same cash flows, i.e. for example a gas turbine, a battery or a nuclear plant. Each component needs to have a \xmlAttr{name} attribute that is unique. +Each \xmlNode{Component} has to have one \xmlNode{Life\_time} block and as many \xmlNode{CashFlow} blocks as needed. + +\begin{enumerate} + \item[\xmlNode{Life\_time}] The lifetime of the component in years. This is used to compute the least common multiple (LCM) of all components involved in the + computation of the economics indicator. For more details see NPV, IRR and PI explanations above. + \item[\xmlNode{tax}] This is a optional input. If the tax rate is specified here, i.e. inside the component block, the componet will use this tax rate. + If no tax rate is specified in the component, the standard tax rate from the \xmlNode{Global} block is used for the component. + \item[\xmlNode{inflation}] This is a optional input. If the inflation rate is specified here, i.e. inside the component block, + the componet will use this inflation rate. If no inflation rate is specified in the component, the standard inflation rate from the \xmlNode{Global} + block is used for the component. + \item[\xmlNode{StartTime}] This is a optional input. If this input is specified for one or more components, the \xmlNode{Global} + input \xmlNode{ProjectTime} is required. This input specifies the year in which this component is going to be build for the first time, + i.e. is going to be included in the cash flows. The default is 0 and the componet is build at the start of the project, i.e. at project year 0. + For example, if the \xmlNode{ProjectTime} is 100 years, and for this component, the \xmlNode{StartTime} is 20 years, the cash flows for this + component are going to be zero for years 0 to 19 of the project. Year 20 of the project will be year 0 of this component and so on + (project year 21 will be component year 1 etc.). + \item[\xmlNode{Repetitions}] This is a optional input. If this input is specified for one or more components, the \xmlNode{Global} + input \xmlNode{ProjectTime} is required. This input specifies the number of times this component is going to be rebuilt. The default is 0, + which indicates that the component is going to be rebuild indefinitely until the project end (\xmlNode{ProjectTime}) is reached. + Lets assume the \xmlNode{ProjectTime} is 100 years, and the component \xmlNode{Life\_time} is 20 years. Specifying 3 repetitions of this + component will build 3 components in succession, at years 0, 20 and 40. For years 61 to 100 of the project, the cash flows for this component will be zero. + + \item[\xmlNode{CashFlows}] The user can define any number of 'cash flows' for a component. Each cash flow is of the form given in + Eq. \ref{eq:CF} where $y$ is the year from 0 (capital investment) to the end of the \xmlNode{Life\_time} of the component. + \begin{equation}\label{eq:CF} + CF_{y}=mult\cdot\alpha_{y}\left ( \frac{driver_{y}}{ref} \right )^{X} + \end{equation} + + The \xmlNode{CashFlows} currently can accept the following subnodes: + \begin{enumerate} + \item[\xmlNode{Capex}] The cash flow for capital expenditures, and this node will accept the following child nodes: + \begin{enumerate} + \item[\xmlNode{driver}] The $driver$ in Eq. \ref{eq:CF} of the cash flow. This can be any variable passed in from RAVEN or the name + of another cash flow. If it is passed in from RAVEN, it has to be either a scalar or a vector with length \xmlNode{Life\_time} + 1. + If its a scalar, all $driver_{y}$ in Eq. \ref{eq:CF} are the same for all years of the project life. If it is a vector instead, each + year of the project \xmlNode{Life\_time} will have its corresponding value for the driver. If the driver is another + cash flow, the project \xmlNode{Life\_time} of the component to which the driving cash flow belongs has to be the same than the project + \xmlNode{Life\_time} of the component to which this + cash flow belongs. No loops of cash flows are allowed. The code will error out if there are loops of cash flows, i.e. A is the driver + of B and B the driver of A. + \item[\xmlNode{alpha}] $\alpha_{y}$ multiplier of the cash flow (see Eq. \ref{eq:CF}). Similar to \xmlNode{driver}, can be + either scalar or vector. If a vector, exactly \xmlNode{Life\_time}$ + 1$ + values are expected. One for $y=0$ to $y=$\xmlNode{Life\_time}. If a scalar, we assume alpha is zero for all years of the lifetime + of the component except the year zero (the provided scalar value will be used for year zero), which is the construction year. + \item[\xmlNode{reference}] The $ref$ value of the cash flow (see Eq. \ref{eq:CF}). + \item[\xmlNode{X}] The $X$ exponent (economy of scale factor) of the cash flow (see Eq. \ref{eq:CF}). + \end{enumerate} + \item[\xmlNode{Recurring}] The cash flow for recurring cost, such as operation and maintenance cost. + \begin{enumerate} + \item[\xmlNode{driver}] The $driver$ in Eq. \ref{eq:CF} of the cash flow. This can be any variable passed in from RAVEN or the name + of another cash flow. If it is passed in from RAVEN, it has to be either a scalar or a vector with length \xmlNode{Life\_time} + 1. + If its a scalar, all $driver_{y}$ in Eq. \ref{eq:CF} are the same for all years of the project life. If it is a vector instead, each + year of the project \xmlNode{Life\_time} will have its corresponding value for the driver. If the driver is another + cash flow, the project \xmlNode{Life\_time} of the component to which the driving cash flow belongs has to be the same than the project + \xmlNode{Life\_time} of the component to which this + cash flow belongs. No loops of cash flows are allowed. The code will error out if there are loops of cash flows, i.e. A is the driver + of B and B the driver of A. + \item[\xmlNode{alpha}] $\alpha_{y}$ multiplier of the cash flow (see Eq. \ref{eq:CF}). Similar to \xmlNode{driver}, can be + either scalar or vector. If a vector, exactly \xmlNode{Life\_time}$ + 1$ + values are expected. One for $y=0$ to $y=$\xmlNode{Life\_time}. + If a scalar, we assume alpha is nonzero, i.e. the provided scalar value, for all years of the lifetime + of the component except the year zero. + \end{enumerate} + \end{enumerate} + + These subnodes, such as \xmlNode{Capex} and \xmlNode{Recurring} will accept the following attributes: + \begin{enumerate} + \item[\xmlAttr{name}] The name of the Cash flow. Has to be unique across all components. This is the name that can be listed in the + \xmlNode{Indicator} node of the \xmlNode{Global} block. + \item[\xmlAttr{tax}] Can be \textbf{true} or \textbf{false}. If it is \textbf{true}, the cash flow is multiplied by $(1-tax)$, where tax + is the tax rate given in \xmlNode{tax} in the \xmlNode{Global} + block. As an example, the cash flow of \textit{comp2} for year 119 in Listing \ref{lst:InputExample} would become $CF^{comp2}_{39}(1-tax)$. + If a cash flow with \xmlAttr{tax}$=$\textbf{true} is the driver of another cash flow, the cash flow without the tax is used as driver for the new cash flow. + The limitation of having a global tax rate will be lifted in future version of the CashFlow module. It is planned to have the possibility to + input different tax rates for each component, since they might be in different tax regions. + \item[\xmlAttr{inflation}] Can be \textbf{real, nominal} or \textbf{none}. If it is \textbf{real}, the cash flow is multiplied by + $(1+inflation)^{-y}$. If it is \textbf{nominal}, the cash flow is multiplied by $(1+inflation)^y$. + In both cases, inflation is given by \xmlNode{inflation} in the \xmlNode{Global} block. Furthermore, $y$ goes from year 0 (capital investment) + to the LCM of all component lifetimes. + This means that the cash flows as expressed in Listing \ref{lst:InputExample} are multiplied with the infloation seen from today, i.e. the cash + flow for \textit{comp2} for year 119 assuming it includes \textbf{real} + inflation would be $CF^{comp2}_{39}(1+inflation)^{-119}$ + If a cash flow with \xmlAttr{inflation} equal \textbf{real} or \textbf{nominal} is the driver of another cash flow, the cash flow without + the inflation is used as driver for the new cash flow. + \item[\xmlAttr{multiply}] This is an optional attribute. This can be the name of any scalar variable passed in from RAVEN. This number + is $mult$ in Eq. \ref{eq:CF} that multiplies the cash flow. + \item[\xmlAttr{mult\_target}] Can be \textbf{true} or \textbf{false}. If \textbf{true}, it means that this cash flow multiplies + the search variable '$x$' as explained in the NPV\_search option above. + If the NPV\_search option is used, al least one cash flow has to have \xmlAttr{mult\_target}$=$\textbf{true}. + \end{enumerate} + + + + +\end{enumerate} + +An example of a cash flow is shown in Listing \ref{lst:CashFlowExample}. In the example, a cash flow called CAPEX is defined. +In the example, the capital expenditure for a reference plant of capacity + 1'000'000'000 W is \$4 billion. The driver of this cash flow is the actual plant capacity in Watts. Building this plat has some economy + of scale, so that a plant double the size does cost +less than double the money ($X=0.64$). The example assums an overnight building cost of the reactor, i.e. $\alpha$ is zero for +all years of the lifetime of the reactor except year zero, which is the construction year. + +\begin{lstlisting}[style=XML,morekeywords={anAttribute},caption=CashFlow input example., label=lst:CashFlowExample] + + Plant_capacity + -4000000000 + 1000000000 + 0.64 + +\end{lstlisting} diff --git a/plugins/CashFlow/doc/user_manual.bib b/plugins/CashFlow/doc/user_manual.bib new file mode 100644 index 0000000000..ed1fe5205d --- /dev/null +++ b/plugins/CashFlow/doc/user_manual.bib @@ -0,0 +1,23 @@ + +@techreport{RAVEN, + Author = {C. Rabiti and A. Alfonsi and J. Cogliati and D. Mandelli and R. Kinoshita and S. Sen and C. Wang and J. Chen}, + Publisher = {Idaho National laboratory (INL)}, + Number = {INL/EXT-15-34123}, + Source = {https://raven.inl.gov}, + Title = {RAVEN user manual}, + Year = {March 2017}} + +@techreport{MSApril2017, + Author = {A. Epiney and C. Rabiti and A. Alfonsi and P. Talbot and F. Ganda}, + Publisher = {Idaho National laboratory (INL) }, + Number = {INL/EXT-17-41915}, + Title = {Report on the Economic Optimization of a Demonstration Case for a Static N-R HES Configuration using RAVEN}, + Year = {April 2017}} + +@techreport{TEST, + Address = {Knoxville, TN, USA}, + Author = {Forum, Message P}, + Publisher = {University of Tennessee}, + Source = {http://www.ncstrl.org:8900/ncstrl/servlet/search?formname=detail\&id=oai%3Ancstrlh%3Autk_cs%3Ancstrl.utk_cs%2F%2FUT-CS-94-230}, + Title = {MPI: A Message-Passing Interface Standard}, + Year = {1994}} diff --git a/plugins/CashFlow/doc/user_manual.tex b/plugins/CashFlow/doc/user_manual.tex new file mode 100644 index 0000000000..1cd60260ab --- /dev/null +++ b/plugins/CashFlow/doc/user_manual.tex @@ -0,0 +1,266 @@ +% +\documentclass[pdf,12pt]{article} + +%\usepackage{times} +%\usepackage[FIGBOTCAP,normal,bf,tight]{subfigure} +\usepackage{amsmath} +\usepackage{amssymb} +%\usepackage{pifont} +\usepackage{enumerate} +\usepackage{listings} +\usepackage{fullpage} +\usepackage{xcolor} % Using xcolor for more robust color specification +%\usepackage{ifthen} % For simple checking in newcommand blocks +%\usepackage{textcomp} +%\usepackage{authblk} % For making the author list look prettier +%\renewcommand\Authsep{,~\,} + +% Custom colors +\definecolor{deepblue}{rgb}{0,0,0.5} +\definecolor{deepred}{rgb}{0.6,0,0} +\definecolor{deepgreen}{rgb}{0,0.5,0} +\definecolor{forestgreen}{RGB}{34,139,34} +\definecolor{orangered}{RGB}{239,134,64} +\definecolor{darkblue}{rgb}{0.0,0.0,0.6} +\definecolor{gray}{rgb}{0.4,0.4,0.4} + +\lstset { + basicstyle=\ttfamily, + frame=single +} + +\lstdefinestyle{XML} { + language=XML, + extendedchars=true, + breaklines=true, + breakatwhitespace=true, +% emph={name,dim,interactive,overwrite}, + emphstyle=\color{red}, + basicstyle=\ttfamily, +% columns=fullflexible, + commentstyle=\color{gray}\upshape, + morestring=[b]", + morecomment=[s]{}, + morecomment=[s][\color{forestgreen}]{}, + keywordstyle=\color{cyan}, + stringstyle=\ttfamily\color{black}, + tagstyle=\color{darkblue}\bf\ttfamily, + morekeywords={name,type}, +% morekeywords={name,attribute,source,variables,version,type,release,x,z,y,xlabel,ylabel,how,text,param1,param2,color,label}, +} +\lstset{language=xml} + +\usepackage{titlesec} +\newcommand{\sectionbreak}{\clearpage} +\setcounter{secnumdepth}{4} + + +%%%%%%%% Begin comands definition to input python code into document +\usepackage[utf8]{inputenc} + +% Default fixed font does not support bold face +\DeclareFixedFont{\ttb}{T1}{txtt}{bx}{n}{9} % for bold +\DeclareFixedFont{\ttm}{T1}{txtt}{m}{n}{9} % for normal + +\usepackage{listings} + +% Python style for highlighting +%\newcommand\pythonstyle{\lstset{ +%language=Python, +%basicstyle=\ttm, +%otherkeywords={self, none, return}, % Add keywords here +%keywordstyle=\ttb\color{deepblue}, +%emph={MyClass,__init__}, % Custom highlighting +%emphstyle=\ttb\color{deepred}, % Custom highlighting style +%stringstyle=\color{deepgreen}, +%frame=tb, % Any extra options here +%showstringspaces=false % +%}} + + +% Python environment +%\lstnewenvironment{python}[1][] +%{ +%$\pythonstyle +%\lstset{#1} +%} +%{} + +% Python for external files +%\newcommand\pythonexternal[2][]{{ +%\pythonstyle +%\lstinputlisting[#1]{#2}}} +% +%\lstnewenvironment{xml} +%{} +%{} + +% Python for inline +%\newcommand\pythoninline[1]{{\pythonstyle\lstinline!#1!}} + +%\def\DRAFT{} % Uncomment this if you want to see the notes people have been adding +% Comment command for developers (Should only be used under active development) +%\ifdefined\DRAFT +% \newcommand{\nameLabeler}[3]{\textcolor{#2}{[[#1: #3]]}} +%\else +% \newcommand{\nameLabeler}[3]{} +%\fi +% Commands for making the LaTeX a bit more uniform and cleaner +%\newcommand{\TODO}[1] {\textcolor{red}{\textit{(#1)}}} +\newcommand{\xmlAttrRequired}[1] {\textcolor{red}{\textbf{\texttt{#1}}}} +\newcommand{\xmlAttr}[1] {\textcolor{cyan}{\textbf{\texttt{#1}}}} +\newcommand{\xmlNodeRequired}[1] {\textcolor{deepblue}{\textbf{\texttt{<#1>}}}} +\newcommand{\xmlNode}[1] {\textcolor{darkblue}{\textbf{\texttt{<#1>}}}} +\newcommand{\xmlString}[1] {\textcolor{black}{\textbf{\texttt{'#1'}}}} +\newcommand{\xmlDesc}[1] {\textbf{\textit{#1}}} % Maybe a misnomer, but I am + % using this to detail the data + % type and necessity of an XML + % node or attribute, + % xmlDesc = XML description +\newcommand{\default}[1]{~\\*\textit{Default: #1}} +\newcommand{\nb} {\textcolor{deepgreen}{\textbf{~Note:}}~} + +% The bm package provides \bm for bold math fonts. Apparently +% \boldsymbol, which I used to always use, is now considered +% obsolete. Also, \boldsymbol doesn't even seem to work with +% the fonts used in this particular document... +\usepackage{bm} + +% Define tensors to be in bold math font. +\newcommand{\tensor}[1]{{\bm{#1}}} + +% Override the formatting used by \vec. Instead of a little arrow +% over the letter, this creates a bold character. +\renewcommand{\vec}{\bm} + +% Define unit vector notation. If you don't override the +% behavior of \vec, you probably want to use the second one. +\newcommand{\unit}[1]{\hat{\bm{#1}}} + +% Use this to refer to a single component of a unit vector. +\newcommand{\scalarunit}[1]{\hat{#1}} + +% \toprule, \midrule, \bottomrule for tables +\usepackage{booktabs} + +% \llbracket, \rrbracket +\usepackage{stmaryrd} + +\usepackage{hyperref} +\hypersetup{ + colorlinks, + citecolor=black, + filecolor=black, + linkcolor=black, + urlcolor=black +} + +% Compress lists of citations like [33,34,35,36,37] to [33-37] +\usepackage{cite} + +% If you want to relax some of the SAND98-0730 requirements, use the "relax" +% option. It adds spaces and boldface in the table of contents, and does not +% force the page layout sizes. +% e.g. \documentclass[relax,12pt]{SANDreport} +% +% You can also use the "strict" option, which applies even more of the +% SAND98-0730 guidelines. It gets rid of section numbers which are often +% useful; e.g. \documentclass[strict]{SANDreport} + +% The INLreport class uses \flushbottom formatting by default (since +% it's intended to be two-sided document). \flushbottom causes +% additional space to be inserted both before and after paragraphs so +% that no matter how much text is actually available, it fills up the +% page from top to bottom. My feeling is that \raggedbottom looks much +% better, primarily because most people will view the report +% electronically and not in a two-sided printed format where some argue +% \raggedbottom looks worse. If we really want to have the original +% behavior, we can comment out this line... +\raggedbottom +\setcounter{secnumdepth}{5} % show 5 levels of subsection +\setcounter{tocdepth}{5} % include 5 levels of subsection in table of contents + +% ---------------------------------------------------------------------------- % +% +% Set the title, author, and date +% +\title{'CashFlow' User Manual \\ + \large Economics plugin for RAVEN \\ +} +%\author{% +%\begin{tabular}{c} Author 1 \\ University1 \\ Mail1 \\ \\ +%Author 3 \\ University3 \\ Mail3 \end{tabular} \and +%\begin{tabular}{c} Author 2 \\ University2 \\ Mail2 \\ \\ +%Author 4 \\ University4 \\ Mail4\\ +%\end{tabular} } + + +\author{ + \\Aaron S. Epiney, Paul Talbot, Congjian Wang\\ +} + +% There is a "Printed" date on the title page of a SAND report, so +% the generic \date should [WorkingDir:]generally be empty. +\date{\today} + +%\def\component#1{\texttt{#1}} + +% ---------------------------------------------------------------------------- % +%\newcommand{\systemtau}{\tensor{\tau}_{\!\text{SUPG}}} + +% Added by Sonat +%\usepackage{placeins} +%\usepackage{array} + +%\newcolumntype{L}[1]{>{\raggedright\let\newline\\\arraybackslash\hspace{0pt}}m{#1}} +%\newcolumntype{C}[1]{>{\centering\let\newline\\\arraybackslash\hspace{0pt}}m{#1}} +%\newcolumntype{R}[1]{>{\raggedleft\let\newline\\\arraybackslash\hspace{0pt}}m{#1}} + +% end added by Sonat +% ---------------------------------------------------------------------------- % +% +% Start the document +% + +\begin{document} + \maketitle + + % ------------------------------------------------------------------------ % + % The table of contents and list of figures and tables + % Comment out \listoffigures and \listoftables if there are no + % figures or tables. Make sure this starts on an odd numbered page + % + \cleardoublepage % TOC needs to start on an odd page + \tableofcontents + %\listoffigures + %\listoftables + % ---------------------------------------------------------------------- % + + % ---------------------------------------------------------------------- % + % This is where the body of the report begins; usually with an Introduction + % + \input{Installation.tex} + + \input{include/CashFlow.tex} + + \section*{Document Version Information} + This document has been compiled using the following version of the plug-in git repository: + \newline + \input{version.tex} + + % ---------------------------------------------------------------------- % + % References + % + \clearpage + % If hyperref is included, then \phantomsection is already defined. + % If not, we need to define it. + \providecommand*{\phantomsection}{} + \phantomsection + \addcontentsline{toc}{section}{References} + \bibliographystyle{ieeetr} + \bibliography{user_manual} + + + % ---------------------------------------------------------------------- % + +\end{document} diff --git a/plugins/CashFlow/src/Amortization.py b/plugins/CashFlow/src/Amortization.py new file mode 100644 index 0000000000..efae365e22 --- /dev/null +++ b/plugins/CashFlow/src/Amortization.py @@ -0,0 +1,35 @@ +import numpy as np + +# AMORTAZIATION SCHEMES + + +MACRS = { 20: 0.01 * np.array([3.750, 7.219, 6.677, 6.177, 5.713, 5.285, 4.888, 4.522, 4.462 , 4.461, 4.462, \ + 4.461, 4.462, 4.461, 4.462, 4.461, 4.462, 4.461, 4.462, 4.461, 2.231]), + 15: 0.01 * np.array([5.0, 9.5, 8.55, 7.7, 6.93, 6.23, 5.9, 5.9, 5.91, 5.9, 5.91, 5.9, 5.91, 5.9, 5.91, 2.95]), + 10: 0.01 * np.array([10.00, 18.00, 14.40, 11.52, 9.22, 7.37, 6.55, 6.55, 6.56, 6.55, 3.28]), + 7: 0.01 * np.array([14.29, 24.49, 17.49, 12.49, 8.93, 8.92, 8.93, 4.46]), + 5: 0.01 * np.array([20.00, 32.00, 19.20, 11.52, 11.52, 5.76]), + 3: 0.01 * np.array([33.33, 44.45, 14.81, 7.41])} + +def amortize(scheme, plan, start_value, component_life): + """ + return the amortization plan + @ In, scheme, str, 'macrs' or 'custom' + @ In, plan, list or array like, list of provided MACRS values + @ In, start_value, float, the given initial Capex value + @ In, component_life, int, the life of component + @ Out, alpha, numpy.array, array of alpha values for given scheme + """ + alpha = np.zeros(component_life + 1, dtype=float) + lscheme = scheme.lower() + if lscheme == 'macrs': + ys = plan[0] + pcts = MACRS.get(ys, None) + if pcts is None: + raise IOError('Provided MACRS "{}" is not allowed.'.format(ys)) + alpha[1:len(pcts)+1] = pcts * start_value + elif lscheme == 'custom': + alpha[1:len(plan)+1] = np.asarray(plan)/100. * start_value + else: + raise NotImplementedError('Amortization scheme "{}" not yet implemented.'.format(scheme)) + return alpha diff --git a/plugins/CashFlow/src/CashFlowUser.py b/plugins/CashFlow/src/CashFlowUser.py new file mode 100644 index 0000000000..f9eaf5fd8a --- /dev/null +++ b/plugins/CashFlow/src/CashFlowUser.py @@ -0,0 +1,87 @@ + +import os +import sys + +# NOTE this import exception is ONLY to allow RAVEN to directly import this module. +try: + from CashFlow.src.CashFlows import Component +except ImportError: + from CashFlows import Component + +# This plugin imports RAVEN modules. if run in stand-alone, RAVEN needs to be installed and this file +# needs to be in the propoer plugin directory. +raven_path = os.path.dirname(__file__) + '/../../../framework' +sys.path.append(os.path.expanduser(raven_path)) +from utils import InputData + +class CashFlowUser: + """ + Base class for objects that want to access the functionality of the CashFlow objects. + Generally this means the CashFlowUser will have an "economics" xml node used to define it, + and will have a group of cash flows associated with it (e.g. a "component") + + In almost all cases, initialization methods should be called as part of the inheritor's method call. + """ + @classmethod + def get_input_specs(cls, spec): + """ + Collects input specifications for this class. + Note this needs to be called as part of an inheriting class's specification definition + @ In, spec, InputData, specifications that need cash flow added to it + @ Out, input_specs, InputData, specs + """ + # this unit probably has some economics + spec.addSub(Component.get_input_specs()) + return spec + + def __init__(self): + """ + Constructor + @ In, kwargs, dict, optional, arguments to pass to other constructors + @ Out, None + """ + self._economics = None # CashFlowGroup + + def read_input(self, specs): + """ + Sets settings from input file + @ In, specs, InputData params, input from user + @ Out, None + """ + self._economics = Component(self) + self._economics.read_input(specs) + + def get_crossrefs(self): + """ + Collect the required value entities needed for this component to function. + @ In, None + @ Out, crossrefs, dict, mapping of dictionaries with information about the entities required. + """ + return self._economics.get_crossrefs() + + def set_crossrefs(self, refs): + """ + Connect cross-reference material from other entities to the ValuedParams in this component. + @ In, refs, dict, dictionary of entity information + @ Out, None + """ + self._economics.set_crossrefs(refs) + + def get_incremental_cost(self, activity, raven_vars, meta, t): + """ + get the cost given particular activities + @ In, activity, pandas.Series, scenario variable values to evaluate cost of + @ In, raven_vars, dict, additional variables (presumably from raven) that might be needed + @ In, meta, dict, further dictionary of information that might be needed + @ In, t, int, time step at which cost needs to be evaluated + @ Out, cost, float, cost of activity + """ + return self._economics.incremental_cost(activity, raven_vars, meta, t) + + def get_economics(self): + """ + Accessor for economics. + @ In, None + @ Out, econ, CashFlowGroup, cash flows for this cash flow user + """ + return self._economics diff --git a/plugins/CashFlow/src/CashFlow_ExtMod.py b/plugins/CashFlow/src/CashFlow_ExtMod.py new file mode 100644 index 0000000000..61b6b84266 --- /dev/null +++ b/plugins/CashFlow/src/CashFlow_ExtMod.py @@ -0,0 +1,170 @@ +""" + Author: A. S. Epiney, P. Talbot, C. Wang + Date : 02/23/2017 +""" + +from __future__ import division, print_function, unicode_literals, absolute_import +import os +import sys +import numpy as np +import warnings +warnings.simplefilter('default', DeprecationWarning) + +# NOTE this import exception is ONLY to allow RAVEN to directly import this module. +try: + from CashFlow.src import main +except ImportError: + import main + +# This plugin imports RAVEN modules. if run in stand-alone, RAVEN needs to be installed and this file +# needs to be in the propoer plugin directory. +dir_path = os.path.dirname(os.path.realpath(__file__)) +# TODO fix with plugin relative path +raven_path = os.path.dirname(__file__) + '/../../../framework' +sys.path.append(os.path.expanduser(raven_path)) + +try: + from utils.graphStructure import graphObject + from PluginsBaseClasses.ExternalModelPluginBase import ExternalModelPluginBase +except: + raise IOError("CashFlow ERROR (Initialisation): RAVEN needs to be installed and CashFlow needs to be in its plugin directory for the plugin to work!'") + + +class CashFlow(ExternalModelPluginBase): + """ + This class contains the plugin class for cash flow analysis within the RAVEN framework. + """ + # ===================================================================================================================== + def _readMoreXML(self, container, xmlNode): + """ + Read XML inputs from RAVEN input file needed by the CashFlow plugin. + Note that the following is read/put from/into the container: + - Out, verbosity, integer, The verbosity level of the CashFlow plugin + - Out, container.cashFlowParameters, dict, contains all the information read from the XML input, i.e. components and cash flow definitions + @ In, container, object, external 'self' + @ In, xmlNode, xml.etree.ElementTree.Element, Xml element node + @ Out, None + """ + # read in XML to global settings, component list + settings, components = main.read_from_xml(xmlNode) + container._global_settings = settings + container._components = components + + # ===================================================================================================================== + + # ===================================================================================================================== + def initialize(self, container, runInfoDict, inputFiles): + """ + Method to initialize the CashFlow plugin. + @ In, container, object, external 'self' + @ In, runInfoDict, dict, the dictionary containing the runInfo (read in the XML input file) + @ In, inputFiles, list, not used + @ Out, None + """ + settings = container._global_settings + components = container._components + main.check_run_settings(settings, components) + # ===================================================================================================================== + + # ===================================================================================================================== + def run(self, container, Inputs): + """ + Computes economic key figures (NPV, IRR, PI as well as NPV serach) + @ In, container, object, external 'self' + @ In, Inputs, dict, contains the inputs needed by the CashFlow plugin as specified in the RAVEN input file + @ Out, None + """ + global_settings = container._global_settings + components = container._components + metrics = main.run(global_settings, components, Inputs) + for k, v in metrics.items(): + setattr(container, k, v) + + # ===================================================================================================================== + + +################################# +# Run the plugin in stand alone # +################################# +if __name__ == "__main__": + # emulate RAVEN container + class FakeSelf: + """ + Mimics RAVEN variable holder + """ + def __init__(self): + """ + Constructor. + @ In, None + @ Out, None + """ + pass + import xml.etree.ElementTree as ET + import argparse + import csv + # read and process input arguments + # ================================ + inp_par = argparse.ArgumentParser(description = 'Run RAVEN CashFlow plugin as stand-alone code') + inp_par.add_argument('-iXML', nargs=1, required=True, help='XML CashFlow input file name', metavar='inp_file') + inp_par.add_argument('-iINP', nargs=1, required=True, help='CashFlow input file name with the input variable list', metavar='inp_file') + inp_par.add_argument('-o', nargs=1, required=True, help='Output file name', metavar='out_file') + inp_opt = inp_par.parse_args() + + # check if files exist + print ("CashFlow INFO (Run as Code): XML input file: %s" %inp_opt.iXML[0]) + print ("CashFlow INFO (Run as Code): Variable input file: %s" %inp_opt.iINP[0]) + print ("CashFlow INFO (Run as Code): Output file: %s" %inp_opt.o[0]) + if not os.path.exists(inp_opt.iXML[0]) : + raise IOError('\033[91m' + "CashFlow INFO (Run as Code): : XML input file " + inp_opt.iXML[0] + " does not exist.. " + '\033[0m') + if not os.path.exists(inp_opt.iINP[0]) : + raise IOError('\033[91m' + "CashFlow INFO (Run as Code): : Variable input file " + inp_opt.iINP[0] + " does not exist.. " + '\033[0m') + if os.path.exists(inp_opt.o[0]) : + print ("CashFlow WARNING (Run as Code): Output file %s already exists. Will be overwritten. " %inp_opt.o[0]) + + # Initialise run + # ================================ + # create a CashFlow class instance + MyCashFlow = CashFlow() + # read the XML input file inp_opt.iXML[0] + MyContainer = FakeSelf() + notroot = ET.parse(open(inp_opt.iXML[0], 'r')).getroot() + root = ET.Element('ROOT') + root.append(notroot) + MyCashFlow._readMoreXML(MyContainer, root) + MyCashFlow.initialize(MyContainer, {}, []) + #if Myverbosity < 2: + print("CashFlow INFO (Run as Code): XML input read ") + # read the values from input file into dictionary inp_opt.iINP[0] + MyInputs = {} + with open(inp_opt.iINP[0]) as f: + for l in f: + (key, val) = l.split(' ', 1) + MyInputs[key] = np.array([float(n) for n in val.split(",")]) + #if Myverbosity < 2: + print("CashFlow INFO (Run as Code): Variable input read ") + #if Myverbosity < 1: + print("CashFlow INFO (Run as Code): Inputs dict %s" %MyInputs) + + # run the stuff + # ================================ + #if Myverbosity < 2: + print("CashFlow INFO (Run as Code): Running the code") + MyCashFlow.run(MyContainer, MyInputs) + + # create output file + # ================================ + #if Myverbosity < 2: + print("CashFlow INFO (Run as Code): Writing output file") + outDict = {} + for indicator in ['NPV_mult', 'NPV', 'IRR', 'PI']: + try: + outDict[indicator] = getattr(MyContainer, indicator) + #if Myverbosity < 2: + print("CashFlow INFO (Run as Code): %s written to file" %indicator) + except (KeyError, AttributeError): + #if Myverbosity < 2: + print("CashFlow INFO (Run as Code): %s not found" %indicator) + with open(inp_opt.o[0], 'w') as out: + CSVwrite = csv.DictWriter(out, outDict.keys()) + CSVwrite.writeheader() + CSVwrite.writerow(outDict) diff --git a/plugins/CashFlow/src/CashFlows.py b/plugins/CashFlow/src/CashFlows.py new file mode 100644 index 0000000000..57078ad316 --- /dev/null +++ b/plugins/CashFlow/src/CashFlows.py @@ -0,0 +1,1081 @@ +""" + Defines the Economics entity. + Each component (or source?) can have one of these to describe its economics. +""" +from __future__ import unicode_literals, print_function +import os +import sys +from collections import defaultdict +import xml.etree.ElementTree as ET + +import numpy as np +import time + +# NOTE this import exception is ONLY to allow RAVEN to directly import this module. +try: + from CashFlow.src import Amortization +except ImportError: + import Amortization +# TODO fix with plugin relative path +path1 = os.path.dirname(__file__) +path2 = '/../raven/framework' +path3=os.path.abspath(os.path.expanduser(path1+'/..'+path2)) +path4=os.path.abspath(os.path.expanduser(path1+path2)) +path5=os.path.abspath(os.path.expanduser(path1+'/../../../framework')) +sys.path.extend([path3,path4,path5]) + +from utils import mathUtils as utils +from utils import InputData, xmlUtils, TreeStructure + + +class GlobalSettings: + """ + Stores general settings for a CashFlow calculation. + """ + ################## + # INITIALIZATION # + ################## + @classmethod + def get_input_specs(cls): + """ + Collects input specifications for this class. + @ In, None + @ Out, input_specs, InputData, specs + """ + glob = InputData.parameterInputFactory('Global') + glob.addSub(InputData.parameterInputFactory('DiscountRate', contentType=InputData.FloatType)) + glob.addSub(InputData.parameterInputFactory('tax', contentType=InputData.FloatType)) + glob.addSub(InputData.parameterInputFactory('inflation', contentType=InputData.FloatType)) + glob.addSub(InputData.parameterInputFactory('ProjectTime', contentType=InputData.IntegerType)) + ind = InputData.parameterInputFactory('Indicator', contentType=InputData.StringListType) + ind.addParam('name', param_type=InputData.StringListType, required=True) + ind.addParam('target', param_type=InputData.FloatType) + glob.addSub(ind) + return glob + + def __init__(self, verbosity=100, **kwargs): + """ + Constructor. + @ In, kwargs, dict, general keyword arguments: verbosity + @ In, verbosity, int, used to control the output information + @ Out, None + """ + self._verbosity = verbosity + self._metrics = None + self._discount_rate = None + self._tax = None + self._inflation = None + self._project_time = None + self._indicators = None + self._active_components = None + self._metric_target = None + self._components = [] + + def read_input(self, source): + """ + Sets settings from input file + @ In, source, InputData.ParameterInput, input from user + @ Out, None + """ + # TODO make read_input call set_params so there's a uniform place to change things! + if isinstance(source, (ET.Element, TreeStructure.InputNode)): + specs = self.get_input_specs()() + specs.parseNode(source) + else: + specs = source + for node in specs.subparts: + name = node.getName() + val = node.value + if name == 'DiscountRate': + self._discount_rate = val + elif name == 'tax': + self._tax = val + elif name == 'inflation': + self._inflation = val + elif name == 'ProjectTime': + self._project_time = val + 1 # one for the construction year! + elif name == 'Indicator': + self._indicators = node.parameterValues['name'] + self._metric_target = node.parameterValues.get('target', None) + active_cf = val + self._active_components = defaultdict(list) + for request in active_cf: + try: + comp, cf = request.split('|') + except ValueError: + raise IOError('Expected active components in to be formatted as Component|Cashflow, but got {}'.format(request)) + self._active_components[comp].append(cf) + self.check_initialization() + + def set_params(self, param_dict): + """ + Sets the settings from a dictionary, instead of via an input file. + @ In, param_dict, dict, settings + @ Out, None + """ + for name, val in param_dict.items(): + if name == 'DiscountRate': + self._discount_rate = val + elif name == 'tax': + self._tax = val + elif name == 'inflation': + self._inflation = val + elif name == 'ProjectTime': + self._project_time = val + 1 # one for the construction year! + elif name == 'Indicator': + self._indicators = val['name'] + self._metric_target = val.get('target', None) + active_cf = val['active'] + self._active_components = defaultdict(list) + for request in active_cf: + try: + comp, cf = request.split('|') + except ValueError: + raise IOError('Expected active components in to be formatted as Component|Cashflow, but got {}'.format(request)) + self._active_components[comp].append(cf) + self.check_initialization() + + def check_initialization(self): + """ + Checks that the reading in of inputs resulted in a sensible + set of global data. Should be checked whenever a new GlobalSetting is created + and initialized. + @ In, None + @ Out, None + """ + # required entries + if self._discount_rate is None: + raise IOError('Missing from global parameters!') + if self._tax is None: + raise IOError('Missing from global parameters!') + if self._inflation is None: + raise IOError('Missing from global parameters!') + if self._indicators is None: + raise IOError('Missing from global parameters!') + # specialized + if 'NPV_search' in self._indicators and self._metric_target is None: + raise IOError('"NPV_search is an indicator and is missing from global parameter!') + for ind in self._indicators: + if ind not in ['NPV_search', 'NPV', 'IRR', 'PI']: + raise IOError('Unrecognized indicator type: "{}"'.format(ind)) + + ####### + # API # + ####### + def get_active_components(self): + """ + Get the active components for the whole project + @ In, None + @ Out, self._active_components, dict, {componentName: listOfCashFlows}, the dict of active components + """ + return self._active_components + + def get_discount_rate(self): + """ + Get the global discount rate + @ In, None + @ Out, self._discount_rate, float, discount rate + """ + return self._discount_rate + + def get_inflation(self): + """ + Get the global inflation + @ In, None + @ Out, self._inflation, None or float, the inflation for the whole project + """ + return self._inflation + + def get_indicators(self): + """ + Get the indicators + @ In, None + @ Out, self._indicators, string, string list of indicators, such as NPV, IRR. + """ + return self._indicators + + def get_metric_target(self): + """ + Get the metric target + @ In, None + @ Out, self._metric_target, float, the target metric + """ + return self._metric_target + + def get_project_time(self): + """ + Get whole project time + @ In, None + @ Out, self._project_time, int, the project time + """ + return self._project_time + + def get_tax(self): + """ + Get the global tax rate + @ In, None + @ Out, self._tax, float, tax rate + """ + return self._tax + + +class Component: + """ + Just a holder for multiple cash flows, and methods for doing stuff with them + Note the class can be constructed by reading from the XML (read_input) or directly TODO consistency + """ + node_var_map = {'Life_time': '_lifetime', + 'StartTime': '_start_time', + 'Repetitions': '_repetitions', + 'tax': '_specific_tax', + 'inflation': '_specific_inflation', + } + ################## + # INITIALIZATION # + ################## + @classmethod + def get_input_specs(cls): + """ + Collects input specifications for this class. + @ In, None + @ Out, input_specs, InputData, specs + """ + comp = InputData.parameterInputFactory('Component') + comp.addParam('name', param_type=InputData.StringType, required=True) + comp.addSub(InputData.parameterInputFactory('Life_time', contentType=InputData.IntegerType)) + comp.addSub(InputData.parameterInputFactory('StartTime', contentType=InputData.IntegerType)) + comp.addSub(InputData.parameterInputFactory('Repetitions', contentType=InputData.IntegerType)) + comp.addSub(InputData.parameterInputFactory('tax', contentType=InputData.FloatType)) + comp.addSub(InputData.parameterInputFactory('inflation', contentType=InputData.FloatType)) + #cf = CashFlow.get_input_specs() + #comp.addSub(cf) + cfs = InputData.parameterInputFactory('CashFlows') + cfs.addSub(Capex.get_input_specs()) + cfs.addSub(Recurring.get_input_specs()) + comp.addSub(cfs) + return comp + + def __init__(self, verbosity=100, **kwargs): + """ + Constructor. + @ In, kwargs, dict, general keyword arguments: verbosity + @ In, verbosity, int, used to control the output information + @ Out, None + """ + #self._owner = owner # cash flow user that uses this group + self._verbosity = verbosity + self._lifetime = None # lifetime of the component + self.name = None + self._cash_flows = [] + self._start_time = None + self._repetitions = None + self._specific_tax = None + self._specific_inflation = None + + def read_input(self, source): + """ + Sets settings from input file + @ In, source, InputData.ParameterInput, input from user + @ Out, None + """ + print(' ... loading economics ...') + # allow read_input argument to be either xml or input specs + if isinstance(source, (ET.Element, TreeStructure.InputNode)): + specs = self.get_input_specs()() + specs.parseNode(source) + else: + specs = source + self.name = specs.parameterValues['name'] + # read in specs + ## since all of these are simple value setters, use a mapping + for item_name, attr in self.node_var_map.items(): + item = specs.findFirst(item_name) + if item is not None: + setattr(self, attr, item.value) + cfs = specs.findFirst('CashFlows') + if cfs is not None: + for sub in cfs.subparts: + new_cfs = self._cash_flow_factory(sub) #CashFlow(self.name, verbosity=self._verbosity) + self.add_cashflows(new_cfs) + self.check_initialization() + + def set_params(self, param_dict): + """ + Sets the settings from a dictionary, instead of via an input file. + @ In, param_dict, dict, settings + @ Out, None + """ + for name, value in param_dict.items(): + if name == 'name': + self.name = value + elif name == 'cash_flows': + self._cash_flows = value + else: + # remainder are mapped + attr_name = self.node_var_map.get(name, None) + if attr_name is None: + continue + setattr(self, attr_name, value) + self.check_initialization() + + def check_initialization(self): + """ + Checks that the reading in of inputs resulted in a sensible + set of data. Should be checked whenever a new Component is created + and initialized. + @ In, None + @ Out, None + """ + missing = 'Component "{comp}" is missing the <{node}> node!' + if self._lifetime is None: + raise IOError(missing.format(comp=self.name, node='Life_time')) + # check cashflows + for cf in self._cash_flows: + # set up parameters to match this component's lifetime + params = dict((attr, cf.get_param(attr)) for attr in ['alpha', 'driver']) + params = cf.extend_parameters(params, self._lifetime+1) + cf.set_params(params) + # alpha needs to be either: a variable (Recurring type cash flow) or a lifetime+1 length array + # TODO move check to specific cashflows! + cf.check_param_lengths(self._lifetime+1) + # TODO this isn't a check, this is setting defaults. Should this be a different method? + if self._start_time is None: + self._start_time = 0 + if self._repetitions is None: + self._repetitions = 0 # NOTE that 0 means infinite repetitions! + + ####### + # API # + ####### + def add_cashflows(self, cf): + """ + Add the cashflows for this component + @ In, cf, list, list of CashFlow objects + @ Out, None + """ + self._cash_flows.extend(cf) + + def count_multtargets(self): + """ + Get the number of targets this component + @ In, None + @ Out, count_multtargets, int, the number of cash flows + """ + return sum(cf._mult_target is not None for cf in self._cash_flows) + + def get_cashflow(self, name): + """ + Get the cash flow with provided name for this component + @ In, name, string, the name of cash flow object + @ Out, cf, CashFlow Object, the cash flow object + """ + for cf in self._cash_flows: + if cf.name == name: + return cf + + def get_cashflows(self): + """ + Get the for this component + @ In, None + @ Out, self._cash_flows, list, list of cash flow objects + """ + return self._cash_flows + + def get_inflation(self): + """ + Get the inflation for this component + @ In, None + @ Out, self._specific_inflation, None or float, the inflation of this component + """ + return self._specific_inflation + + def get_lifetime(self): + """ + Provides the lifetime of this cash flow user. + @ In, None + @ Out, lifetime, int, lifetime + """ + return self._lifetime + + def get_multipliers(self): + """ + Get the multipliers for this component + @ In, None + @ Out, multipliers, list, list of multipliers + """ + return list(cf.get_multiplier() for cf in self._cash_flows) + + def get_repetitions(self): + """ + Get the repetitions for this component + @ In, None + @ Out, repetitions, int, the number of repetitions + """ + return self._repetitions + + def get_start_time(self): + """ + Get the start_time for this component + @ In, None + @ Out, start_time, int, the start time of this component + """ + return self._start_time + + def get_tax(self): + """ + Get the tax rate for this component + @ In, None + @ Out, self._tax, float, tax rate + """ + return self._specific_tax + + ############# + # UTILITIES # + ############# + def _cash_flow_factory(self, specs): + """ + based on the InputData specs provided, returns the appropriate CashFlow + @ In, specs, instant of InputData.ParameterInput, specs of provided InputData + @ Out, created, list, list of cash flow objects + """ + created = [] + # get the type of this node, whether we're talking XML or RAVEN.InputData + if not isinstance(specs, InputData.ParameterInput): + raise TypeError('Unrecognized source specifications type: {}'.format(type(specs))) + # create the appropriate cash flows + typ = specs.getName() + if typ == 'Recurring': + # this is simple, only need one cash flow to be created + new = Recurring(component=self.name, verbosity=self._verbosity) + new.read_input(specs) + created.append(new) + elif typ == 'Capex': + # in addition to the node itself, need to add depreciation if requested + new = Capex(component=self.name, verbosity=self._verbosity) + new.read_input(specs) + deprs = self._create_depreciation(new) + created.append(new) + created.extend(deprs) + #elif typ == 'Custom': + # new = CashFlow(self.name, self._verbosity) + # created.append(new) + else: + raise TypeError('Unrecognized cash flow type:', typ) + return created + + def _create_depreciation(self, ocf): + """ + creates amortization cash flows depending on the originating capex cash flow + @ In, ocf, instant of CashFlow, instant of CashFlow object + @ Out, depreciation, list, [pos, neg], list amortization and depreciation objects + """ + # use the reference plant price + amort = ocf.get_amortization() + if amort is None: + return [] + print('DEBUGG amortizing cf:', ocf.name) + original_value = ocf.get_param('alpha') * -1.0 #start with a positive value + scheme, plan = amort + alpha = Amortization.amortize(scheme, plan, 1.0, self._lifetime) + # first cash flow is POSITIVE on the balance sheet, is not taxed, and is a percent of the target + pos = Amortizor(component=self.name, verbosity=self._verbosity) + params = {'name': '{}_{}_{}'.format(self.name, 'amortize', ocf.name), + 'driver': '{}|{}'.format(self.name, ocf.name), + 'tax': False, + 'inflation': 'real', + 'alpha': alpha, + # TODO is this reference and X right???? + 'reference': 1.0, #ocf.get_param('reference'), + 'X': 1.0, #ocf.get_param('scale') + } + pos.set_params(params) + # second cash flow is as the first, except negative and taxed + neg = Amortizor(component=self.name, verbosity=self._verbosity) + n_alpha = np.zeros(len(alpha)) + n_alpha[alpha != 0] = -1 + print('DEBUGG amort alpha:', alpha) + print('DEBUGG depre alpha:', n_alpha) + params = {'name': '{}_{}_{}'.format(self.name, 'depreciate', ocf.name), + 'driver': '{}|{}'.format(self.name, pos.name), + 'tax': True, + 'inflation': 'real', + 'alpha': n_alpha, + 'reference': 1.0, + 'X': 1.0} + neg.set_params(params) + return [pos, neg] + + +class CashFlow: + """ + Hold the economics for a single cash flow, C = m * a * (D/D')^x + where: + C is the cashflow ($) + m is a scalar multiplier + a is the value of the widget, based on the D' volume sold + D is the amount of widgets sold + D' is the nominal amount of widgets sold + x is the scaling factor + """ + ################## + # INITIALIZATION # + ################## + missing_node_template = 'Component "{comp}" CashFlow "{cf}" is missing the <{node}> node!' + + @classmethod + def get_input_specs(cls, specs): + """ + Collects input specifications for this class. + @ In, specs, InputData, specs + @ Out, specs, InputData, specs + """ + # ONLY appends to existinc specs! + #cf = InputData.parameterInputFactory('CashFlow') + + specs.addParam('name', param_type=InputData.StringType, required=True) + specs.addParam('tax', param_type=InputData.BoolType, required=False) + infl = InputData.makeEnumType('inflation_types', 'inflation_type', ['real', 'none']) # "nominal" not yet implemented + specs.addParam('inflation', param_type=infl, required=False) + specs.addParam('mult_target', param_type=InputData.BoolType, required=False) + specs.addParam('multiply', param_type=InputData.StringType, required=False) + + specs.addSub(InputData.parameterInputFactory('driver', contentType=InputData.InterpretedListType)) + specs.addSub(InputData.parameterInputFactory('alpha', contentType=InputData.InterpretedListType)) + return specs + + def __init__(self, component=None, verbosity=100, **kwargs): + """ + Constructor + @ In, component, CashFlowUser instance, optional, cash flow user to which this cash flow belongs + @ Out, None + """ + # assert component is not None # TODO is this necessary? What if it's not a component-based cash flow? + self.type = 'generic' + self._component = component # component instance to whom this cashflow belongs, if any + self._verbosity = verbosity + # equation values + self._driver = None # "quantity produced", D + self._alpha = None # "price per produced", a + self._reference = None + self._scale = None + + # other params + self.name = None # base name of cash flow + self.type = None # Capex, Recurring, Custom + self._taxable = None # apply tax or not + self._inflation = None # apply inflation or not + self._mult_target = None # true if this cash flow gets multiplied by a global multiplier (e.g. NPV=0 search) (?) + self._multiplier = None # arbitrary scalar multiplier (variable name) + self._depreciate = None + + def read_input(self, item): + """ + Sets settings from input file + @ In, item, InputData.ParameterInput, parsed specs from user + @ Out, None + """ + self.name = item.parameterValues['name'] + print(' ... ... loading cash flow "{}"'.format(self.name)) + # driver and alpha are specific to cashflow types # self._driver = item.parameterValues['driver'] + for key, value in item.parameterValues.items(): + if key == 'tax': + self._taxable = value + elif key == 'inflation': + self._inflation = value + elif key == 'mult_target': + self._mult_target = value + elif key == 'multiply': + self._multiplier = value + for sub in item.subparts: + if sub.getName() == 'alpha': + self._alpha = self.set_variable_or_floats(sub.value) + elif sub.getName() == 'driver': + self._driver = self.set_variable_or_floats(sub.value) + if sub.getName() == 'reference': + self._reference = sub.value + elif sub.getName() == 'X': + self._scale = sub.value + self.check_initialization() + + def set_params(self, param_dict): + """ + Sets the settings from a dictionary, instead of via an input file. + @ In, param_dict, dict, settings + @ Out, None + """ + for name, val in param_dict.items(): + if name == 'name': + self.name = val + elif name == 'driver': + self._driver = val + elif name == 'tax': + self._taxable = val + elif name == 'inflation': + self._inflation = val + elif name == 'mult_target': + self._mult_target = val + elif name == 'multiply': + self._multiplier = val + elif name == 'alpha': + self._alpha = np.atleast_1d(val) + elif name == 'reference': + self._reference = val + elif name == 'X': + self._scale = val + elif name == 'depreciate': + self._depreciate = val + self.check_initialization() + + def check_initialization(self): + """ + Checks that the reading in of inputs resulted in a sensible + set of data. Should be checked whenever a new CashFlow is created + and initialized. + @ In, None + @ Out, None + """ + pass # nothing specific to check in base + + def get_multiplier(self): + """ + Get the multiplier + @ In, None + @ Out, multiplier, string or float, the multiplier of this cash flow + """ + return self._multiplier + + def get_param(self, param): + """ + Get the parameter value + @ In, param, string, the name of requested parameter + @ Out, get_param, float or list, the value of param + """ + param = param.lower() + if param in ['alpha', 'reference_price']: + return self._alpha + elif param in ['driver', 'amount_sold']: + return self._driver + elif param in ['reference', 'reference_driver']: + return self._reference + elif param in ['x', 'scale', 'economy of scale', 'scale_factor']: + return self._scale + else: + raise RuntimeError('Unrecognized parameter request:', param) + + def get_amortization(self): + """ + Get amortization + @ In, None + @ Out, None + """ + return None + + def is_inflated(self): + """ + Check inflation + @ In, None + @ Out, is_inflated, Bool, True if inflated otherwise False + """ + # right now only 'none' and 'real' are options, so this is boolean + ## when nominal is implemented, might need to extend this method a bit + return self._inflation != 'none' + + def is_mult_target(self): + """ + Check if multiple targets + @ In, None + @ Out, is_mult_target, Bool, True if multiple targets else False + """ + return self._mult_target + + def is_taxable(self): + """ + Check is taxable + @ In, None + @ Out, is_taxable, Bool, True if taxable otherwise False + """ + return self._taxable + + def set_variable_or_floats(self, value): + """ + Set variable + @ In, value, str or float or list, the value of given variable + @ Out, ret, str or float or numpy.array, the recasted value + """ + ret = None + # multi-entry or single-entry? + if len(value) == 1: + # single entry should be either a float (price) or string (raven variable) + value = value[0] + if utils.isAString(value) or utils.isAFloatOrInt(value): + ret = value + else: + raise IOError('Unrecognized alpha/driver type: "{}" with type "{}"'.format(value, type(value))) + else: + # should be floats; InputData assures the entries are the same type already + if not utils.isAFloatOrInt(value[0]): + raise IOError('Multiple non-number entries for alpha/driver found, but require either a single variable name or multiple float entries: {}'.format(value)) + ret = np.asarray(value) + return ret + + def load_from_variables(self, need, variables, cashflows, lifetime): + """ + Load the values of parameters from variables + @ In, need, dict, the dict of parameters + @ In, variables, dict, the dict of parameters that is provided from other sources + @ In, cashflows, dict, dict of cashflows + @ In, lifetime, int, the given life time + @ Out, need, dict, the dict of parameters updated with variables + """ + # load variable values from variables or other cash flows, as needed (ha!) + for name, source in need.items(): + if utils.isAString(source): + # as a string, this is either from the variables or other cashflows + # look in variables first + value = variables.get(source, None) + if value is None: + # since not found in variables, try among cashflows + if '|' not in source: + raise KeyError('Looking for variable "{}" to fill "{}" but not found among variables or other cashflows!'.format(source, name)) + comp, cf = source.split('|') + value = cashflows[comp][cf][:] + need[name] = np.atleast_1d(value) + # now, each is already a float or an array, so in case they're a float expand them + ## NOTE this expects the correct keys (namely alpha, driver) to expand, right? + need = self.extend_parameters(need, lifetime) + return need + + def extend_parameters(self, need, lifetime): + """ + Extend values of parameters to the length of lifetime + @ In, need, dict, the dict of parameters that need to extend + @ In, lifetime, int, the given life time + @ Out, None + """ + # should be overwritten in the inheriting classes! + raise NotImplementedError + + +class Capex(CashFlow): + """ + Particular cashflow for infrequent large single expenditures + """ + @classmethod + def get_input_specs(cls): + """ + Collects input specifications for this class. + @ In, specs, InputData, specs + @ Out, specs, InputData, specs + """ + specs = InputData.parameterInputFactory('Capex') + specs = CashFlow.get_input_specs(specs) + specs.addSub(InputData.parameterInputFactory('reference', contentType=InputData.FloatType)) + specs.addSub(InputData.parameterInputFactory('X', contentType=InputData.FloatType)) + deprec = InputData.parameterInputFactory('depreciation', contentType=InputData.InterpretedListType) + deprec_schemes = InputData.makeEnumType('deprec_types', 'deprec_types', ['MACRS', 'custom']) + deprec.addParam('scheme', param_type=deprec_schemes, required=True) + specs.addSub(deprec) + return specs + + def __init__(self, **kwargs): + """ + Constructor + @ In, kwargs, dict, general keyword arguments + @ Out, None + """ + CashFlow.__init__(self, **kwargs) + # new variables + self.type = 'Capex' + self._amort_scheme = None # amortization scheme for depreciating this capex + self._amort_plan = None # if scheme is MACRS, this is the years to recovery. Otherwise, vector percentages. + # set defaults different from base class + self.type = 'Capex' + self._taxable = False + self._inflation = False + + def read_input(self, item): + """ + Sets settings from input file + @ In, item, InputData.ParameterInput, input from user + @ Out, None + """ + for sub in item.subparts: + if sub.getName() == 'depreciation': + self._amort_scheme = sub.parameterValues['scheme'] + self._amort_plan = sub.value + CashFlow.read_input(self, item) + + def check_initialization(self): + """ + Checks that the reading in of inputs resulted in a sensible + set of data. + @ In, None + @ Out, None + """ + CashFlow.check_initialization(self) + if self._reference is None: + raise IOError(self.missing_node_template.format(comp=self._component, cf=self.name, node='reference')) + if self._scale is None: + raise IOError(self.missing_node_template.format(comp=self._component, cf=self.name, node='X')) + if self._driver is None: + raise IOError(self.missing_node_template.format(comp=self._component, cf=self.name, node='driver')) + if self._alpha is None: + raise IOError(self.missing_node_template.format(comp=self._component, cf=self.name, node='alpha')) + + def init_params(self, lifetime): + """ + Initialize some parameters + @ In, lifetime, int, the given life time + @ Out, None + """ + self._alpha = np.zeros(1 + lifetime) + self._driver = np.zeros(1 + lifetime) + + def get_amortization(self): + """ + Get amortization + @ In, None + @ Out, amortization, None or tuple, (amortizationScheme, amortizationPlan) + """ + if self._amort_scheme is None: + return None + else: + return self._amort_scheme, self._amort_plan + + def set_amortization(self, scheme, plan): + """ + Set amortization + @ In, scheme, str, 'MACRS' or 'custom' + @ In, plan, list, list of amortization values + @ Out, None + """ + self._amort_scheme = scheme + self._amort_plan = np.atleast_1d(plan) + + def extend_parameters(self, to_extend, t): + """ + Extend values of parameters to the length of lifetime t + @ In, to_extend, dict, the dict of parameters that need to extend + @ In, t, int, the given life time + @ Out, None + """ + # for capex, both the Driver and Alpha are nonzero in year 1 and zero thereafter + for name, value in to_extend.items(): + if name.lower() in ['alpha', 'driver']: + if utils.isAFloatOrInt(value) or (len(value) == 1 and utils.isAFloatOrInt(value[0])): + new = np.zeros(t) + new[0] = float(value) + to_extend[name] = new + return to_extend + + def calculate_cashflow(self, variables, lifetime_cashflows, lifetime, verbosity): + """ + sets up the COMPONENT LIFETIME cashflows, and calculates yearly for the comp life + @ In, variables, dict, the dict of parameters that is provided from other sources + @ In, lifetime_cashflows, dict, dict of cashflows + @ In, lifetime, int, the given life time + @ In, verbosity, int, used to control the output information + @ Out, ret, dict, the dict of caculated cashflow + """ + ## FIXME what if I have set the values already? + # get variable values, if needed + need = {'alpha': self._alpha, 'driver': self._driver} + # load alpha, driver from variables if need be + need = self.load_from_variables(need, variables, lifetime_cashflows, lifetime) + # for Capex, use m * alpha * (D/D')^X + alpha = need['alpha'] + driver = need['driver'] + reference = self.get_param('reference') + if reference is None: + reference = 1.0 + scale = self.get_param('scale') + if scale is None: + scale = 1.0 + mult = self.get_multiplier() + if mult is None: + mult = 1.0 + elif utils.isAString(mult): + mult = float(variables[mult]) + result = mult * alpha * (driver / reference) ** scale + if verbosity > 1: + ret = {'result': result} + else: + ret = {'result': result, + 'alpha': alpha, + 'driver': driver, + 'reference': reference, + 'scale': scale, + 'mult': mult} + return ret + + def check_param_lengths(self, lifetime, comp_name=None): + """ + Check the length of some parameters + @ In, lifetime, int, the given life time + @ In, comp_name, str, name of component + @ Out, None + """ + for param in ['alpha', 'driver']: + val = self.get_param(param) + # if a string, then it's probably a variable, so don't check it now + if utils.isAString(val): + continue + # if it's valued, then it better be the same length as the lifetime (which is comp lifetime + 1) + elif len(val) != lifetime: + pre_msg = 'Component "{comp}" '.format(comp_name) if comp_name is not None else '' + raise IOError((pre_msg + 'cashflow "{cf}" node <{param}> should have {correct} '+\ + 'entries (1 + lifetime), but only found {found}!') + .format(cf=self.name, + correct=lifetime, + param=param, + found=len(val))) + + +class Recurring(CashFlow): + """ + Particular cashflow for yearly-consistent repeating expenditures + """ + + @classmethod + def get_input_specs(cls): + """ + Collects input specifications for this class. + @ In, specs, InputData, specs + @ Out, specs, InputData, specs + """ + specs = InputData.parameterInputFactory('Recurring') + specs = CashFlow.get_input_specs(specs) + # nothing new to add + return specs + + def __init__(self, **kwargs): + """ + Constructor + @ In, kwargs, dict, general keyword arguments + @ Out, None + """ + CashFlow.__init__(self, **kwargs) + # set defaults different from base class + self.type = 'Recurring' + self._taxable = True + self._inflation = True + self._yearly_cashflow = None + + def init_params(self, lifetime): + """ + Initialize some parameters + @ In, lifetime, int, the given life time + @ Out, None + """ + # Recurring doesn't use m alpha D/D' X, it uses integral(alpha * D)dt for each year + self._yearly_cashflow = np.zeros(lifetime+1) + + def compute_yearly_cashflow(self, year, alpha, driver): + """ + Computes the yearly summary of recurring interactions, and sets them to self._yearly_cashflow + @ In, year, int, the index of the project year for this summary + @ In, alpha, np.array, array of "prices" + @ In, driver, np.array, array of "quantities sold" + @ Out, None + """ + mult = self.get_multiplier() + if mult is None: + mult = 1.0 + elif utils.isAString(mult): + raise NotImplementedError + try: + self._yearly_cashflow[year] = mult * (alpha * driver).sum() # +1 is for initial construct year + except ValueError as e: + print('Error while computing yearly cash flow! Check alpha shape ({}) and driver shape ({})'.format(alpha.shape, driver.shape)) + raise e + + def compute_yearly_cashflowzj(self, year, alpha, driver): + """ + Computes the yearly summary of recurring interactions, and sets them to self._yearly_cashflow + @ In, year, int, the index of the project year for this summary + @ In, alpha, np.array, array of "prices" + @ In, driver, np.array, array of "quantities sold" + @ Out, None + """ + mult = self.get_multiplier() + + if mult is None: + mult = 1.0 + elif utils.isAString(mult): + raise NotImplementedError + try: + self._yearly_cashflow = mult * (alpha * driver) + except ValueError as e: + print('Error while computing yearly cash flow! Check alpha shape ({}) and driver shape ({})'.format(alpha.shape, driver.shape)) + raise e + + def calculate_cashflow(self, variables, lifetime_cashflows, lifetime, verbosity): + """ + sets up the COMPONENT LIFETIME cashflows, and calculates yearly for the comp life + @ In, variables, dict, the dict of parameters that is provided from other sources + @ In, lifetime_cashflows, dict, dict of cashflows + @ In, lifetime, int, the given life time + @ In, verbosity, int, used to control the output information + @ Out, calculate_cashflow, dict, the dict of caculated cashflow + """ + # by now, self._yearly_cashflow should have been filled with appropriate values + # TODO reference, scale? we've already used mult (I think) + self.init_params(lifetime-1) + y = lifetime - 1 + self.compute_yearly_cashflowzj(y, self._alpha, variables[self._driver]) + #assert self._yearly_cashflow is not None + return {'result': self._yearly_cashflow} + + def check_param_lengths(self, lifetime, comp_name=None): + """ + Check the length of some parameters + @ In, lifetime, int, the given life time + @ In, comp_name, str, name of component + @ Out, None + """ + pass # nothing to do here, we don't check lengths since they'll be integrated intrayear + + def extend_parameters(self, to_extend, t): + """ + Extend values of parameters to the length of lifetime t + @ In, to_extend, dict, the dict of parameters that need to extend + @ In, t, int, the given life time + @ Out, None + """ + # for recurring, both the Driver and Alpha are zero in year 1 and nonzero thereafter + # FIXME: we're going to integrate alpha * D over time (not year time, intrayear time) + for name, value in to_extend.items(): + if name.lower() in ['alpha']: + if utils.isAFloatOrInt(value) or (len(value) == 1 and utils.isAFloatOrInt(value[0])): + new = np.ones(t) * float(value) + new[0] = 0 + to_extend[name] = new + return to_extend + + +class Amortizor(Capex): + """ + Particular cashflow for depreciation of capital expenditures + """ + def extend_parameters(self, to_extend, t): + """ + Extend values of parameters to the length of lifetime t + @ In, to_extend, dict, the dict of parameters that need to extend + @ In, t, int, the given life time + @ Out, None + """ + # unlike normal capex, for amortization we expand the driver to all nonzero entries and keep alpha as is + # TODO forced driver values for now + driver = to_extend['driver'] + # how we treat the driver depends on if this is the amortizer or the depreciator + if self.name.split('_')[-2] == 'amortize': + if not utils.isAString(driver): + to_extend['driver'] = np.ones(t) * driver[0] * -1.0 + to_extend['driver'][0] = 0.0 + for name, value in to_extend.items(): + if name.lower() in ['driver']: + if utils.isAFloatOrInt(value) or (len(value) == 1 and utils.isAFloatOrInt(value[0])): + new = np.zeros(t) + new[1:] = float(value) + to_extend[name] = new + return to_extend diff --git a/plugins/CashFlow/src/__init__.py b/plugins/CashFlow/src/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/CashFlow/src/main.py b/plugins/CashFlow/src/main.py new file mode 100644 index 0000000000..3bd41d5114 --- /dev/null +++ b/plugins/CashFlow/src/main.py @@ -0,0 +1,599 @@ +""" + Execution for CashFlow +""" +import os +import sys +import functools +from collections import defaultdict + +import numpy as np +try: + from CashFlow.src import CashFlows + # NOTE this import exception is ONLY to allow RAVEN to directly import this extmod. + # In general, this should not exist, and RAVEN should import CashFlow.CashFlow_ExtMod instead of importing CashFlow_ExtMod directly, implicitly. +except (ImportError, ModuleNotFoundError): + import CashFlows + +raven_path= os.path.abspath(os.path.dirname(__file__)) + '/../../raven/framework' +sys.path.append(raven_path) #'~/projects/raven/framework') # TODO generic RAVEN location + +from utils.graphStructure import graphObject +from utils import mathUtils as utils + +#===================== +# UTILITIES +#===================== +def read_from_xml(xml): + """ + reads in cash flow from XML + @ In, xml, xml.etree.ElementTree.Element, "Economics" node from input + @ Out, global_settings, CashFlows.GlobalSettings instance, settings for a run (None if none provided) + @ Out, components, list, CashFlows.Components instances for a run + """ + # read in XML to global settings, component list + attr = xml.attrib + global_settings = None + components = [] + econ = xml.find('Economics') + verb = int(econ.attrib.get('verbosity', 100)) + for node in econ: + if node.tag == 'Global': + global_settings = CashFlows.GlobalSettings(**attr) + global_settings.read_input(node) + global_settings._verbosity = verb + elif node.tag == 'Component': + new = CashFlows.Component(**attr) + new.read_input(node) + components.append(new) + else: + raise IOError('Unrecognized node under : {}'.format(node.tag)) + return global_settings, components + +def check_run_settings(settings, components): + """ + Checks that basic settings between global and components are satisfied. + Errors out if any problems are found. + @ In, settings, CashFlows.GlobalSettings, global settings + @ In, components, list, list of CashFlows.Component instances + @ Out, None + """ + comp_by_name = dict((c.name, c) for c in components) + # perform final checks for the global settings and components + for find, find_cf in settings.get_active_components().items(): + if find not in comp_by_name: + raise IOError('Requested active component "{}" but not found! Options are: {}'.format(find, list(comp_by_name.keys()))) + # check cash flow is in comp + # check that StartTime/Repetitions triggers a ProjectTime node + ## if projecttime is not given, then error if start time/repetitions given (otherwise answer is misleading) + if settings.get_project_time() is None: + for comp in components: + warn = 'CashFlow: <{node}> given for component "{comp}" but no in global settings!' + if comp.get_start_time() != 0: + raise IOError(warn.format(node='StartTime', comp=comp.name)) + if comp.get_repetitions() != 0: + raise IOError(warn.format(node='Repetitions', comp=comp.name)) + # check that if NPV_search is an indicator, then mult_target is on at least one cashflow. + if 'NPV_search' in settings.get_indicators() and sum(comp.count_multtargets() for comp in components) < 1: + raise IOError('NPV_search in "name" but no cash flows have "mult_target=True"!') + +def check_drivers(settings, components, variables, v=100): + """ + checks if all drivers needed are present in variables + @ In, settings, CashFlows.GlobalSettings, global settings + @ In, components, list, list of CashFlows.Component instances + @ In, variables, dict, variable-value map from RAVEN + @ In, v, int, verbosity level + @ Out, ordered, list, list of ordered cashflows to evaluate (in order) + """ + m = 'check_drivers' + #active = _get_active_drivers(settings, components) + active = list(comp for comp in components if comp.name in settings.get_active_components()) + vprint(v, 0, m, '... creating evaluation sequence ...') + ordered = _create_eval_process(active, variables) + vprint(v, 0, m, '... evaluation sequence:', ordered) + return ordered + +def _create_eval_process(components, variables): + """ + Sorts the cashflow evaluation process so sensible evaluation order is used + @ In, components, list, list of CashFlows.Component instances + @ In, variables, dict, variable-value map from RAVEN + @ Out, ordered, list, list of ordered cashflows to evaluate (in order) + """ + # TODO does this work with float drivers (e.g. already-evaluated drivers)? + # storage for creating graph sequence + driver_graph = defaultdict(list) + driver_graph['EndNode'] = [] + evaluated = [] # for cashflows that have already been evaluated and don't need more treatment + for comp in components: + lifetime = comp.get_lifetime() + # find multiplier variables + multipliers = comp.get_multipliers() + for mult in multipliers: + if mult is None: + continue + if mult not in variables.keys(): + raise RuntimeError('CashFlow: multiplier "{}" required for Component "{}" but not found among variables!'.format(mult, comp.name)) + # find order in which to evaluate cash flow components + for c, cf in enumerate(comp.get_cashflows()): + # keys for graph are drivers, cash flow names + driver = cf.get_param('driver') + # does the driver come from the variable list, or from another cashflow, or is it already evaluated? + cfn = '{}|{}'.format(comp.name, cf.name) + found = False + if driver is None or utils.isAFloatOrInt(driver) or isinstance(driver, np.ndarray): + found = True + # TODO assert it's already filled? + evaluated.append(cfn) + continue + elif driver in variables: + found = True + # check length of driver + n = len(np.atleast_1d(variables[driver])) + if n > 1 and n != lifetime+1: + raise RuntimeError(('Component "{c}" CashFlow {cf} driver variable "{d}" has "{n}" entries, '+\ + 'but "{c}" has a lifetime of {el}!') + .format(c=comp.name, + cf=cf.name, + d=driver, + n=n, + el=lifetime)) + else: + # driver should be in cash flows if not in variables + driver_comp, driver_cf = driver.split('|') + for match_comp in components: + if match_comp.name == driver_comp: + # for cross-referencing, component lifetimes have to be the same! + if match_comp.get_lifetime() != comp.get_lifetime(): + raise RuntimeError(('Lifetimes for Component "{d}" and cross-referenced Component {m} ' +\ + 'do not match, so no cross-reference possible!') + .format(d=driver_comp, m=match_comp.name)) + found = True # here this means that so far the component was found, not the specific cash flow. + break + else: + found = False + # if the component was found, check the cash flow is part of the component + if found: + if driver_cf not in list(m_cf.name for m_cf in match_comp.get_cashflows()): + found = False + if not found: + raise RuntimeError(('Component "{c}" CashFlow {cf} driver variable "{d}" was not found ' +\ + 'among variables or other cashflows!') + .format(c=comp.name, + cf=cf.name, + d=driver)) + + # assure each cashflow is in the mix, and has an EndNode to rely on (helps graph construct accurately) + driver_graph[cfn].append('EndNode') + # each driver depends on its cashflow + driver_graph[driver].append(cfn) + return evaluated + graphObject(driver_graph).createSingleListOfVertices() + +def component_life_cashflow(comp, cf, variables, lifetime_cashflows, v=100): + """ + Calcualtes the annual lifetime-based cashflow for a cashflow of a component + @ In, comp, CashFlows.Component, component whose cashflow is being analyzed + @ In, cf, CashFlows.CashFlow, cashflow who is being analyzed + @ In, variables, dict, RAVEN variables as name: value + @ In, v, int, verbosity + @ Out, life_cashflow, np.array, array of cashflow values with length of component life + """ + m = 'comp_life' + vprint(v, 1, m, "-"*75) + print('DEBUGG comp:', comp.name, cf) + vprint(v, 1, m, 'Computing LIFETIME cash flow for Component "{}" CashFlow "{}" ...'.format(comp.name, cf.name)) + param_text = '... {:^10.10s}: {: 1.9e}' + # do cashflow + results = cf.calculate_cashflow(variables, lifetime_cashflows, comp.get_lifetime()+1, v) + life_cashflow = results['result'] + + if v < 1: + # print out all of the parts of the cashflow calc + for item, value in results.items(): + if item == 'result': + continue + if utils.isAFloatOrInt(value): + vprint(v, 1, m, param_text.format(item, value)) + else: + orig = cf.get_param(item) + if utils.isSingleValued(orig): + name = orig + else: + name = '(from input)' + vprint(v, 1, m, '... {:^10.10s}: {}'.format(item, name)) + vprint(v, 1, m, '... mean: {: 1.9e}'.format(value.mean())) + vprint(v, 1, m, '... std : {: 1.9e}'.format(value.std())) + vprint(v, 1, m, '... min : {: 1.9e}'.format(value.min())) + vprint(v, 1, m, '... max : {: 1.9e}'.format(value.max())) + vprint(v, 1, m, '... nonz: {:d}'.format(np.count_nonzero(value))) + + yx = max(len(str(len(life_cashflow))),4) + vprint(v, 0, m, 'LIFETIME cash flow summary by year:') + vprint(v, 0, m, ' {y:^{yx}.{yx}s}, {a:^10.10s}, {d:^10.10s}, {c:^15.15s}'.format(y='year', + yx=yx, + a='alpha', + d='driver', + c='cashflow')) + for y, cash in enumerate(life_cashflow): + if cf.type in ['Capex']: + vprint(v, 1, m, ' {y:^{yx}d}, {a: 1.3e}, {d: 1.3e}, {c: 1.9e}'.format(y=y, + yx=yx, + a=results['alpha'][y], + d=results['driver'][y], + c=cash)) + elif cf.type == 'Recurring': + vprint(v, 1, m, ' {y:^{yx}d}, -- N/A -- , -- N/A -- , {c: 1.9e}'.format(y=y, + yx=yx, + c=cash)) + return life_cashflow + +def get_project_length(settings, components, v=100): + """ + checks if all drivers needed are present in variables + @ In, settings, CashFlows.GlobalSettings, global settings + @ In, components, list, list of CashFlows.Component instances + @ In, v, int, verbosity level + @ Out, project_length, int, length of project (explicit or implicit) + """ + m = 'get_project_length' + project_length = settings.get_project_time() + if not project_length: + vprint(v, 0, m, 'Because project length was not specified, using least common multiple of component lifetimes.') + lifetimes = list(c.get_lifetime() for c in components) + project_length = lcmm(*lifetimes) + 1 + return int(project_length) + +def project_life_cashflows(settings, components, lifetime_cashflows, project_length, v=100): + """ + creates all cashflows for life of project, for all components + @ In, settings, CashFlows.GlobalSettings, global settings + @ In, components, list, list of CashFlows.Component instances + @ In, lifetime_cashflows, dict, component: cashflow: np.array of annual economic values + @ In, project_length, int, project years + @ In, v, int, verbosity level + @ Out, project_cashflows, dict, dictionary of project-length cashflows (same structure as lifetime dict) + """ + m = 'proj_life' + # apply tax, inflation + project_cashflows = {} # same keys as lifetime_cashflows + for comp in components: + tax = comp.get_tax() if comp.get_tax() is not None else settings.get_tax() + inflation = comp.get_inflation() if comp.get_inflation() is not None else settings.get_inflation() + comp_proj_cashflows = project_component_cashflows(comp, tax, inflation, lifetime_cashflows[comp.name], project_length, v=v) + project_cashflows[comp.name] = comp_proj_cashflows + return project_cashflows + +def project_component_cashflows(comp, tax, inflation, life_cashflows, project_length, v=100): + """ + does all the cashflows for a SINGLE COMPONENT for the life of the project + @ In, comp, CashFlows.Component, component to run numbers for + @ In, tax, float, tax rate for component as decimal + @ In, inflation, float, inflation rate as decimal + @ In, life_cashflows, dict, dictionary of component lifetime cash flows + @ In, project_length, int, project years + @ In, v, int, verbosity level + @ Out, cashflows, dict, dictionary of cashflows for this component, taken to project life + """ + m = 'proj comp' + vprint(v, 1, m, "-"*75) + vprint(v, 1, m, 'Computing PROJECT cash flow for Component "{}" ...'.format(comp.name)) + cashflows = {} + # what is the first project year this component will be in existence? + comp_start = comp.get_start_time() + # how long does each build of this component last? + comp_life = comp.get_lifetime() + # what is the last project year this component will be in existence? + ## TODO will this work properly if start time is negative? Initial tests say yes ... + ## note that we use project_length as the default END of the component's cashflow life, NOT a decomission year! + comp_end = project_length if comp.get_repetitions() == 0 else comp_start + comp_life * comp.get_repetitions() + vprint(v, 1, m, ' ... component start: {}'.format(comp_start)) + vprint(v, 1, m, ' ... component end: {}'.format(comp_end)) + for cf in comp.get_cashflows(): + if cf.is_taxable(): + tax_mult = 1.0 - tax + else: + tax_mult = 1.0 + if cf.is_inflated(): + infl_rate = inflation + 1.0 + else: + infl_rate = 1.0 # TODO nominal inflation rate? + vprint(v, 1, m, ' ... inflation rate: {}'.format(infl_rate)) + vprint(v, 1, m, ' ... tax rate: {}'.format(tax_mult)) + life_cf = life_cashflows[cf.name] + single_cashflow = project_single_cashflow(cf, comp_start, comp_end, comp_life, life_cf, tax_mult, infl_rate, project_length, v=v) + vprint(v, 0, m, 'Project Cashflow for Component "{}" CashFlow "{}":'.format(comp.name, cf.name)) + if v < 1: + vprint(v, 0, m, 'Year, Time-Adjusted Value') + for y, val in enumerate(single_cashflow): + vprint(v, 0, m, '{:4d}: {: 1.9e}'.format(y, val)) + cashflows[cf.name] = single_cashflow + return cashflows + +def project_single_cashflow(cf, start, end, life, life_cf, tax_mult, infl_rate, project_length, v=100): + """ + does a single cashflow for the life of the project + @ In, cf, CashFlows.CashFlow, cash flow to extend to full project life + @ In, start, int, project year in which component begins operating + @ In, end, int, project year in which component ends operating + @ In, life, int, lifetime of component + @ In, life_cf, np.array, cashflow for lifetime of component + @ In, tax_mult, float, tax rate multiplyer (1 - tax) + @ In, infl_rate, float, inflation rate multiplier (1 - inflation) + @ In, project_length, int, total years of analysis + @ In, v, int, verbosity + @ Out, proj_cf, np.array, cashflow for project life of component + """ + m = 'proj c_fl' + vprint(v, 1, m, "-"*50) + vprint(v, 1, m, 'Computing PROJECT cash flow for CashFlow "{}" ...'.format(cf.name)) + proj_cf = np.zeros(project_length) + years = np.arange(project_length) # years in project time, year 0 is first year # TODO just indices, pandas? + # before the project starts, after it ends are zero; we want the working part + operating_mask = np.logical_and(years >= start, years <= end) + operating_years = years[operating_mask] + start_shift = operating_years - start # y_shift + # what year realative to production is this component in, for each operating year? + relative_operation = start_shift % life # yReal + # handle new builds + ## three types of new builds: + ### 1) first ever build (only construction cost) + ### 2) decomission after last year ever running (assuming said decomission is inside the operational years) + ### 3) years with both a decomissioning and a construction + ## this is all years in which construction will occur (covers 1 and half of 3) + new_build_mask = [a[relative_operation==0] for a in np.where(operating_mask)] + # NOTE make the decomission_mask BEFORE removing the last-year-rebuild, if present. + ## This lets us do smoother numpy operations. + decomission_mask = [new_build_mask[0][1:]] + # if the last year is a rebuild year, don't rebuild, as it won't be operated. + if new_build_mask[0][-1] == years[-1]: + new_build_mask[0] = new_build_mask[0][:-1] + ## add construction costs for all of these new build years + proj_cf[new_build_mask] = life_cf[0] * tax_mult * np.power(infl_rate, -1*years[new_build_mask]) + #print(proj_cf) + ## this is all the years in which decomissioning happens + ### note that the [0] index is sort of a dummy dimension to help the numpy handshakes + ### if last decomission is within project life, include that too + if operating_years[-1] < years[-1]: + decomission_mask[0] = np.hstack((decomission_mask[0],np.atleast_1d(operating_years[-1]+1))) + proj_cf[decomission_mask] += life_cf[-1] * tax_mult * np.power(infl_rate, -1*years[decomission_mask]) + #print(proj_cf) + ## handle the non-build operational years + non_build_mask = [a[relative_operation!=0] for a in np.where(operating_mask)] + proj_cf[non_build_mask] += life_cf[relative_operation[relative_operation!=0]] * tax_mult * np.power(infl_rate, -1*years[non_build_mask]) + return proj_cf + +def npv_search(settings, components, cash_flows, project_length, v=100): + """ + Performs NPV matching search + TODO is the target value required to be 0? + @ In, settings, CashFlows.GlobalSettings, global settings + @ In, components, list, list of CashFlows.Component instances + @ In, cash_flows, dict, component: cashflow: np.array of annual economic values + @ In, project_length, int, project years + @ In, v, int, verbosity level + @ Out, mult, float, multiplier that causes the NPV to match the target value + """ + m = 'npv search' + multiplied = 0.0 # cash flows that are meant to include the multiplier + others = 0.0 # cash flows without the multiplier + years = np.arange(project_length) + for comp in components: + for cf in comp.get_cashflows(): + data = cash_flows[comp.name][cf.name] + discount_rates = np.power(1.0 + settings.get_discount_rate(), years) + discounted = np.sum(data/discount_rates) + if cf.is_mult_target(): + multiplied += discounted + else: + others += discounted + target_val = settings.get_metric_target() + mult = (target_val - others)/multiplied # TODO div zero possible? + vprint(v, 0, m, '... NPV multiplier: {: 1.9e}'.format(mult)) + # SANITY CHECL -> FCFF with the multiplier, re-calculate NPV + if v < 1: + npv = NPV(components, cash_flows, project_length, settings.get_discount_rate(), mult=mult, v=v) + if npv != target_val: + vprint(v, 1, m, 'NPV mismatch warning! Calculated NPV with mult: {: 1.9e}, target: {: 1.9e}'.format(npv, target_val)) + return mult + +def FCFF(components, cash_flows, project_length, mult=None, v=100): + """ + Calculates "free cash flow to the firm" (FCFF) + @ In, settings, CashFlows.GlobalSettings, global settings + @ In, cash_flows, dict, component: cashflow: np.array of annual economic values + @ In, project_length, int, project years + @ In, mult, float, optional, if provided then scale target cash flow by value + @ In, v, int, verbosity level + @ Out, fcff, float, free cash flow to the firm + """ + m = 'FCFF' + # FCFF_R for each year + fcff = np.zeros(project_length) + for comp in components: + for cf in comp.get_cashflows(): + data = cash_flows[comp.name][cf.name] + if mult is not None and cf.is_mult_target(): + fcff += data * mult + else: + fcff += data + vprint(v, 1, m, 'FCFF yearly (not discounted):\n{}'.format(fcff)) + return fcff + +def NPV(components, cash_flows, project_length, discount_rate, mult=None, v=100, return_fcff=False): + """ + Calculates net present value of cash flows + @ In, components, list, list of CashFlows.Component instances + @ In, cash_flows, dict, component: cashflow: np.array of annual economic values + @ In, project_length, int, project years + @ In, discount_rate, float, firm discount rate to use in discounting future dollars value + @ In, mult, float, optional, if provided then scale target cash flow by value + @ In, return_fcff, bool, optional, if True then provide calculated FCFF as well + @ In, v, int, verbosity level + @ Out, npv, float, net-present value of system + @ Out, fcff, float, optional, free cash flow to the firm for same system + """ + m = 'NPV' + fcff = FCFF(components, cash_flows, project_length, mult=mult, v=v) + npv = np.npv(discount_rate, fcff) + vprint(v, 0, m, '... NPV: {: 1.9e}'.format(npv)) + if not return_fcff: + return npv + else: + return npv, fcff + +def IRR(components, cash_flows, project_length, v=100): + """ + Calculates internal rate of return for system of cash flows + @ In, components, list, list of CashFlows.Component instances + @ In, cash_flows, dict, component: cashflow: np.array of annual economic values + @ In, project_length, int, project years + @ In, v, int, verbosity level + @ Out, irr, float, internal rate of return + """ + m = 'IRR' + fcff = FCFF(components, cash_flows, project_length, mult=None, v=v) # TODO mult is none always? + # this method can crash if no solution exists! + #try: + irr = np.irr(fcff) + vprint(v, 1, m, '... IRR: {: 1.9e}'.format(irr)) + #except: # TODO what kind of crash? General catching is bad practice. + # vprint(v, 99, m, 'IRR search failed! No solution found. Setting IRR to -10 for debugging.') + # irr = -10.0 + return irr + +def PI(components, cash_flows, project_length, discount_rate, mult=None, v=100): + """ + Calculates the profitability index for system + @ In, components, list, list of CashFlows.Component instances + @ In, cash_flows, dict, component: cashflow: np.array of annual economic values + @ In, project_length, int, project years + @ In, discount_rate, float, firm discount rate to use in discounting future dollars value + @ In, mult, float, optional, if provided then scale target cash flow by value + @ In, v, int, verbosity level + @ Out, pi, float, profitability index + """ + m = 'PI' + npv, fcff = NPV(components, cash_flows, project_length, discount_rate, mult=mult, v=v, return_fcff=True) + pi = -1.0 * npv / fcff[0] # yes, really! This seems strange, but it also seems to be right. + vprint(v, 1, m, '... PI: {: 1.9e}'.format(pi)) + return pi + +def gcd(a, b): + """ + Find greatest common denominator + @ In, a, int, first value + @ In, b, int, sescond value + @ Out, gcd, int, greatest common denominator + """ + while b: + a, b = b, a % b + return a + +def lcm(a, b): + """ + Find least common multiple + @ In, a, int, first value + @ In, b, int, sescond value + @ Out, lcm, int, least common multiple + """ + return a * b // gcd(a, b) + +def lcmm(*args): + """ + Find the least common multiple of many values + @ In, args, list, list of integers to find lcm for + @ Out, lcmm, int, least common multiple of collection + """ + return functools.reduce(lcm, args) + +#===================== +# MAIN METHOD +#===================== +def run(settings, components, variables): + """ + @ In, settings, CashFlows.GlobalSettings, global settings + @ In, components, list, list of CashFlows.Component instances + @ In, variables, dict, variables from RAVEN + @ Out, results, dict, economic metric results + """ + # make a dictionary mapping component names to components + comps_by_name = dict((c.name, c) for c in components) + v = settings._verbosity + m = 'run' + vprint(v, 0, m, 'Starting CashFlow Run ...') + # check mapping of drivers and determine order in which they should be evaluated + vprint(v, 0, m, '... Checking if all drivers present ...') + ordered = check_drivers(settings, components, variables, v=v) + + # compute project cashflows + ## this comes in multiple styles! + ## -> for the "capex/amortization" cashflows, as follows: + ## - compute the COMPONENT LIFE cashflow for the component + ## - loop the COMPONENT LIFE cashflow until it's as long as the PROJECT LIFE cashflow + ## -> for the "recurring" sales-type cashflow, as follows: + ## - there should already be enough information for the entire PROJECT LIFE + ## - if not, and there's only one entry, repeat that entry for the entire project life + vprint(v, 0, m, '='*90) + vprint(v, 0, m, 'Component Lifetime Cashflow Calculations') + vprint(v, 0, m, '='*90) + lifetime_cashflows = defaultdict(dict) # keys are component, cashflow, then indexed by lifetime + for ocf in ordered: + if ocf in variables or ocf == 'EndNode': # TODO why this check for ocf in variables? Should it be comp, or cf? + continue + comp_name, cf_name = ocf.split('|') + comp = comps_by_name[comp_name] + print('DEBUGG getting', cf_name) + cf = comp.get_cashflow(cf_name) + # if this component is a "recurring" type, then we don't need to do the lifetime cashflow bit + #if cf.type == 'Recurring': + # raise NotImplementedError # FIXME how to do this right? + # calculate cash flow for component's lifetime for this cash flow + print('jz is comp_name',comp_name) + print('jz is cf_name',cf_name) + life_cf = component_life_cashflow(comp, cf, variables, lifetime_cashflows, v=0) + + print('jz is lifecf',life_cf) + lifetime_cashflows[comp_name][cf_name] = life_cf + + vprint(v, 0, m, '='*90) + vprint(v, 0, m, 'Project Lifetime Cashflow Calculations') + vprint(v, 0, m, '='*90) + # determine how the project life is calculated. + project_length = get_project_length(settings, components, v=v) + vprint(v, 0, m, ' ... project length: {} years'.format(project_length)) + project_cashflows = project_life_cashflows(settings, components, lifetime_cashflows, project_length, v=v) + + vprint(v, 0, m, '='*90) + vprint(v, 0, m, 'Economic Indicator Calculations') + vprint(v, 0, m, '='*90) + indicators = settings.get_indicators() + results = {} + if 'NPV_search' in indicators: + metric = npv_search(settings, components, project_cashflows, project_length, v=v) + results['NPV_mult'] = metric + if 'NPV' in indicators: + metric = NPV(components, project_cashflows, project_length, settings.get_discount_rate(), v=v) + results['NPV'] = metric + if 'IRR' in indicators: + metric = IRR(components, project_cashflows, project_length, v=v) + results['IRR'] = metric + if 'PI' in indicators: + metric = PI(components, project_cashflows, project_length, settings.get_discount_rate(), v=v) + results['PI'] = metric + return results + + +#===================== +# PRINTING STUFF +#===================== +def vprint(threshold, desired, method, *msg): + """ + Light wrapper for printing that considers verbosity levels + @ In, threshold, int, cutoff verbosity + @ In, desired, int, requested message verbosity level + @ In, method, str, name of method raising print + @ In, msg, list(str), messages to print + @ Out, None + """ + if desired >= threshold: + print('CashFlow INFO ({}):'.format(method), *msg) diff --git a/plugins/CashFlow/tests/CashFlow_test_IRR.xml b/plugins/CashFlow/tests/CashFlow_test_IRR.xml new file mode 100644 index 0000000000..301afc6978 --- /dev/null +++ b/plugins/CashFlow/tests/CashFlow_test_IRR.xml @@ -0,0 +1,79 @@ + + + CashFlow_test_IRR + A. Epiney + 2017-10-25 + + This input tests the RAVEN plugin CashFlow. + The tested functionality is called IRR. + + Models.ExternalModel.CashFlow + + Added classTested node + update ExternalXML with new structure cash flow input + Added Plugin name extension + + + + + . + MCrun,printTOfile + + + + BOP_capacity, BOP_TOT_revenueEL, IP_capacity, IP_TOT_revenueBY, Multiplier + IRR + + + + + GRO_CashFlow_in, GRO_CashFlow_out + + + + + + + + 1 + + 300.0E6 + 350.0E6 + 51.0E6 + 31.5E6 + 1.0 + + + + + + GRO_CashFlow_in + OutputPlaceHolder + + + GRO_CashFlow_in + GRO_CashFlow_out + + + + + + SET_CashFlow_in + Cash_Flow + test_MC + SET_CashFlow_out + + + SET_CashFlow_out + dumpIRR + + + + + + csv + SET_CashFlow_out + input,output + + + diff --git a/plugins/CashFlow/tests/CashFlow_test_NPV.xml b/plugins/CashFlow/tests/CashFlow_test_NPV.xml new file mode 100644 index 0000000000..45fddb42f0 --- /dev/null +++ b/plugins/CashFlow/tests/CashFlow_test_NPV.xml @@ -0,0 +1,79 @@ + + + CashFlow_test_NPV + A. Epiney + 2017-10-25 + + This input tests the RAVEN plugin CashFlow. + The tested functionality is called NPV. + + Models.ExternalModel.CashFlow + + Added classTested node + update ExternalXML with new structure cash flow input + Added Plugin name extension + + + + + . + MCrun,printTOfile + + + + BOP_capacity, BOP_TOT_revenueEL, IP_capacity, IP_TOT_revenueBY, Multiplier + NPV + + + + + GRO_CashFlow_in, GRO_CashFlow_out + + + + + + + + 1 + + 300.0E6 + 350.0E6 + 51.0E6 + 31.5E6 + 1.0 + + + + + + GRO_CashFlow_in + OutputPlaceHolder + + + GRO_CashFlow_in + GRO_CashFlow_out + + + + + + SET_CashFlow_in + Cash_Flow + test_MC + SET_CashFlow_out + + + SET_CashFlow_out + dumpNPV + + + + + + csv + SET_CashFlow_out + input,output + + + diff --git a/plugins/CashFlow/tests/CashFlow_test_NPV_RunAsCode.py b/plugins/CashFlow/tests/CashFlow_test_NPV_RunAsCode.py new file mode 100644 index 0000000000..9920fd3da6 --- /dev/null +++ b/plugins/CashFlow/tests/CashFlow_test_NPV_RunAsCode.py @@ -0,0 +1,16 @@ + +import os +import sys +# run the Cash Flow plugin as stand alone code +os.system('python ../src/CashFlow_ExtMod.py -iXML Cash_Flow_input_NPV.xml -iINP VarInp.txt -o out.out') + +# read out.out and compare with gold +with open("out.out") as out: + for l in out: + pass + +gold = float(l) +if (gold - 630614140.519) < 0.01: + sys.exit(0) +else: + sys.exit(1) diff --git a/plugins/CashFlow/tests/CashFlow_test_NPV_componentTax.xml b/plugins/CashFlow/tests/CashFlow_test_NPV_componentTax.xml new file mode 100644 index 0000000000..e9add4343f --- /dev/null +++ b/plugins/CashFlow/tests/CashFlow_test_NPV_componentTax.xml @@ -0,0 +1,80 @@ + + + CashFlow_test_NPV + A. Epiney + 2018-03-23 + + This input tests the RAVEN plugin CashFlow. + The tested functionality is called NPV. + The functionality of different tax and inflation reates for different components is tested here. + + Models.ExternalModel.CashFlow + + Added classTested node + update ExternalXML with new structure cash flow input + Added Plugin name extension + + + + + . + MCrun,printTOfile + + + + BOP_capacity, BOP_TOT_revenueEL, IP_capacity, IP_TOT_revenueBY, Multiplier + NPV + + + + + GRO_CashFlow_in, GRO_CashFlow_out + + + + + + + + 1 + + 300.0E6 + 350.0E6 + 51.0E6 + 31.5E6 + 1.0 + + + + + + GRO_CashFlow_in + OutputPlaceHolder + + + GRO_CashFlow_in + GRO_CashFlow_out + + + + + + SET_CashFlow_in + Cash_Flow + test_MC + SET_CashFlow_out + + + SET_CashFlow_out + dumpNPV_componentTax + + + + + + csv + SET_CashFlow_out + input,output + + + diff --git a/plugins/CashFlow/tests/CashFlow_test_NPV_customTime.xml b/plugins/CashFlow/tests/CashFlow_test_NPV_customTime.xml new file mode 100644 index 0000000000..909984a0cd --- /dev/null +++ b/plugins/CashFlow/tests/CashFlow_test_NPV_customTime.xml @@ -0,0 +1,82 @@ + + + CashFlow_test_NPV + A. Epiney + 2018-04-06 + + This input tests the RAVEN plugin CashFlow. + The tested functionality is called NPV. + Tests the ProjectTime, StartTime and Repetition functionalities. + All sub-projects, i.e. components lifetimes (including repetition) + finish before the ProjectTime finishes. + + Models.ExternalModel.CashFlow + + Added classTested node + update ExternalXML with new structure cash flow input + Added Plugin name extension + + + + + . + MCrun,printTOfile + + + + BOP_capacity, BOP_TOT_revenueEL, IP_capacity, IP_TOT_revenueBY, Multiplier + NPV + + + + + GRO_CashFlow_in, GRO_CashFlow_out + + + + + + + + 1 + + 300.0E6 + 350.0E6 + 51.0E6 + 31.5E6 + 1.0 + + + + + + GRO_CashFlow_in + OutputPlaceHolder + + + GRO_CashFlow_in + GRO_CashFlow_out + + + + + + SET_CashFlow_in + Cash_Flow + test_MC + SET_CashFlow_out + + + SET_CashFlow_out + dumpNPV_customTime + + + + + + csv + SET_CashFlow_out + input,output + + + diff --git a/plugins/CashFlow/tests/CashFlow_test_NPV_customTime2.xml b/plugins/CashFlow/tests/CashFlow_test_NPV_customTime2.xml new file mode 100644 index 0000000000..71b662f705 --- /dev/null +++ b/plugins/CashFlow/tests/CashFlow_test_NPV_customTime2.xml @@ -0,0 +1,82 @@ + + + CashFlow_test_NPV + A. Epiney + 2018-04-06 + + This input tests the RAVEN plugin CashFlow. + The tested functionality is called NPV. + Tests the ProjectTime, StartTime and Repetition functionalities. + No sub-project, i.e. component lifetime (including repetition) + finish before the ProjectTime finishes. + + Models.ExternalModel.CashFlow + + Added classTested node + update ExternalXML with new structure cash flow input + Added Plugin name extension + + + + + . + MCrun,printTOfile + + + + BOP_capacity, BOP_TOT_revenueEL, IP_capacity, IP_TOT_revenueBY, Multiplier + NPV + + + + + GRO_CashFlow_in, GRO_CashFlow_out + + + + + + + + 1 + + 300.0E6 + 350.0E6 + 51.0E6 + 31.5E6 + 1.0 + + + + + + GRO_CashFlow_in + OutputPlaceHolder + + + GRO_CashFlow_in + GRO_CashFlow_out + + + + + + SET_CashFlow_in + Cash_Flow + test_MC + SET_CashFlow_out + + + SET_CashFlow_out + dumpNPV_customTime2 + + + + + + csv + SET_CashFlow_out + input,output + + + diff --git a/plugins/CashFlow/tests/CashFlow_test_NPV_custom_MACRS.xml b/plugins/CashFlow/tests/CashFlow_test_NPV_custom_MACRS.xml new file mode 100644 index 0000000000..af3bc82da5 --- /dev/null +++ b/plugins/CashFlow/tests/CashFlow_test_NPV_custom_MACRS.xml @@ -0,0 +1,77 @@ + + + CashFlow_test_NPV_custom_MACRS + wangc + 2019-11-08 + + This input tests the RAVEN plugin CashFlow. + The tested functionality is called NPV with custom macrs. + + Models.ExternalModel.CashFlow + + Added Plugin name extension + + + + + . + MCrun,printTOfile + + + + BOP_capacity, BOP_TOT_revenueEL, IP_capacity, IP_TOT_revenueBY, Multiplier + NPV + + + + + GRO_CashFlow_in, GRO_CashFlow_out + + + + + + + + 1 + + 300.0E6 + 350.0E6 + 51.0E6 + 31.5E6 + 1.0 + + + + + + GRO_CashFlow_in + OutputPlaceHolder + + + GRO_CashFlow_in + GRO_CashFlow_out + + + + + + SET_CashFlow_in + Cash_Flow + test_MC + SET_CashFlow_out + + + SET_CashFlow_out + dumpNPV_custom_macrs + + + + + + csv + SET_CashFlow_out + input,output + + + diff --git a/plugins/CashFlow/tests/CashFlow_test_NPV_expandedDriver.xml b/plugins/CashFlow/tests/CashFlow_test_NPV_expandedDriver.xml new file mode 100644 index 0000000000..2baa3b1965 --- /dev/null +++ b/plugins/CashFlow/tests/CashFlow_test_NPV_expandedDriver.xml @@ -0,0 +1,115 @@ + + + CashFlow_test_NPV + A. Epiney + 2018-03-23 + + This input tests the RAVEN plugin CashFlow. + The tested functionality is called NPV. + The BOP revenue is an expanded driver, i.e. values for every year are provided + instead of just one value that is repeated for all years. + + Models.ExternalModel.CashFlow + + Added classTested node + update ExternalXML with new structure cash flow input + Added Plugin name extension + + + + + . + MCrun,printTOfile + + + + input_scaling + BOP_TOT_revenueEL + BOP_capacity, IP_capacity, IP_TOT_revenueBY, Multiplier + BOP_TOT_revenueEL + NPV + + + + + GRO_RevenueData_in_scalar, GRO_RevenueData_out_year, year + + + GRO_CashFlow_in_scalar, GRO_CashFlow_in_year, GRO_CashFlow_out_scalar + + + + + RevenueData + SET_RevenueData_in + SET_RevenueData_out + + + Cash_Flow + SET_CashFlow_in + SET_CashFlow_out + + + + + + + + 1 + + 2.0 + 300.0E6 + 51.0E6 + 31.5E6 + 1.0 + + + + + + GRO_RevenueData_in_scalar + OutputPlaceHolder + + + GRO_RevenueData_in_scalar + GRO_RevenueData_out_year + GRO_RevenueData_out_year + + + GRO_CashFlow_in_scalar + OutputPlaceHolder + + + GRO_CashFlow_in_scalar, GRO_CashFlow_in_year + GRO_CashFlow_out_scalar + GRO_CashFlow_in_year + + + GRO_CashFlow_in_scalar + GRO_CashFlow_out_scalar + + + + + + SET_CashFlow_in + SET_RevenueData_in + MyEnsemble + test_MC + SET_CashFlow_out + SET_output + + + SET_output + dumpNPV_expandedDriver + + + + + + csv + SET_output + input,output + + + diff --git a/plugins/CashFlow/tests/CashFlow_test_NPV_multiplier.xml b/plugins/CashFlow/tests/CashFlow_test_NPV_multiplier.xml new file mode 100644 index 0000000000..b5b65f5eca --- /dev/null +++ b/plugins/CashFlow/tests/CashFlow_test_NPV_multiplier.xml @@ -0,0 +1,80 @@ + + + CashFlow_test_NPV_multiply + A. Epiney + 2018-03-22 + + This input tests the RAVEN plugin CashFlow. + The tested functionality is called NPV. + This test uses the "Multiplier" functionality of CashFlow. + + Models.ExternalModel.CashFlow + + Added classTested node + update ExternalXML with new structure cash flow input + Added Plugin name extension + + + + + . + MCrun,printTOfile + + + + BOP_capacity, BOP_TOT_revenueEL, IP_capacity, IP_TOT_revenueBY, Multiplier + NPV + + + + + GRO_CashFlow_in, GRO_CashFlow_out + + + + + + + + 1 + + 300.0E6 + 350.0E6 + 51.0E6 + 31.5E6 + 2.0 + + + + + + GRO_CashFlow_in + OutputPlaceHolder + + + GRO_CashFlow_in + GRO_CashFlow_out + + + + + + SET_CashFlow_in + Cash_Flow + test_MC + SET_CashFlow_out + + + SET_CashFlow_out + dumpNPV_multiplier + + + + + + csv + SET_CashFlow_out + input,output + + + diff --git a/plugins/CashFlow/tests/CashFlow_test_NPVsearch.xml b/plugins/CashFlow/tests/CashFlow_test_NPVsearch.xml new file mode 100644 index 0000000000..96361a06c3 --- /dev/null +++ b/plugins/CashFlow/tests/CashFlow_test_NPVsearch.xml @@ -0,0 +1,79 @@ + + + CashFlow_test_NPVsearch + A. Epiney + 2017-10-25 + + This input tests the RAVEN plugin CashFlow. + The tested functionality is called NPV search. + + Models.ExternalModel.CashFlow + + Added classTested node + update ExternalXML with new structure cash flow input + Added Plugin name extension + + + + + . + MCrun,printTOfile + + + + BOP_capacity, BOP_TOT_revenueEL, IP_capacity, IP_TOT_revenueBY, Multiplier + NPV_mult + + + + + GRO_CashFlow_in, GRO_CashFlow_out + + + + + + + + 1 + + 300.0E6 + 350.0E6 + 51.0E6 + 31.5E6 + 1.0 + + + + + + GRO_CashFlow_in + OutputPlaceHolder + + + GRO_CashFlow_in + GRO_CashFlow_out + + + + + + SET_CashFlow_in + Cash_Flow + test_MC + SET_CashFlow_out + + + SET_CashFlow_out + dumpNPVsearch + + + + + + csv + SET_CashFlow_out + input,output + + + diff --git a/plugins/CashFlow/tests/CashFlow_test_PI.xml b/plugins/CashFlow/tests/CashFlow_test_PI.xml new file mode 100644 index 0000000000..bad5770667 --- /dev/null +++ b/plugins/CashFlow/tests/CashFlow_test_PI.xml @@ -0,0 +1,79 @@ + + + CashFlow_test_PI + A. Epiney + 2017-10-25 + + This input tests the RAVEN plugin CashFlow. + The tested functionality is called PI. + + Models.ExternalModel.CashFlow + + Added classTested node + update ExternalXML with new structure cash flow input + Added Plugin name extension + + + + + . + MCrun,printTOfile + + + + BOP_capacity, BOP_TOT_revenueEL, IP_capacity, IP_TOT_revenueBY, Multiplier + PI + + + + + GRO_CashFlow_in, GRO_CashFlow_out + + + + + + + + 1 + + 300.0E6 + 350.0E6 + 51.0E6 + 31.5E6 + 1.0 + + + + + + GRO_CashFlow_in + OutputPlaceHolder + + + GRO_CashFlow_in + GRO_CashFlow_out + + + + + + SET_CashFlow_in + Cash_Flow + test_MC + SET_CashFlow_out + + + SET_CashFlow_out + dumpPI + + + + + + csv + SET_CashFlow_out + input,output + + + diff --git a/plugins/CashFlow/tests/Cash_Flow_input_IRR.xml b/plugins/CashFlow/tests/Cash_Flow_input_IRR.xml new file mode 100644 index 0000000000..8c72e077f8 --- /dev/null +++ b/plugins/CashFlow/tests/Cash_Flow_input_IRR.xml @@ -0,0 +1,50 @@ + + + 0.05 + 0.392 + 0.04 + + BOP|CA + BOP|RE + IP|CA + IP|RE + + + + + 60 + + + + BOP_capacity + -4510000000 + 1100000000 + 0.64 + 15 + + + + BOP_TOT_revenueEL + 1.0 + + + + + + 40 + + + + IP_capacity + -153000000 + 231000000 + 1.0 + + + + IP_TOT_revenueBY + 1.0 + + + + diff --git a/plugins/CashFlow/tests/Cash_Flow_input_NPV.xml b/plugins/CashFlow/tests/Cash_Flow_input_NPV.xml new file mode 100644 index 0000000000..9799b0a67f --- /dev/null +++ b/plugins/CashFlow/tests/Cash_Flow_input_NPV.xml @@ -0,0 +1,56 @@ + + + 0.05 + 0.392 + 0.04 + + BOP|CA + BOP|RE + IP|CA + IP|RE + + + + + 60 + + + + + BOP_capacity + -4510000000 + 1100000000 + 0.64 + 15 + + + + + + BOP_TOT_revenueEL + 1.0 + + + + + + 40 + + + + + -153000000 + IP_capacity + 231000000 + 1.0 + + + + + + IP_TOT_revenueBY + 1.0 + + + + diff --git a/plugins/CashFlow/tests/Cash_Flow_input_NPV_componentTax.xml b/plugins/CashFlow/tests/Cash_Flow_input_NPV_componentTax.xml new file mode 100644 index 0000000000..f446b272bc --- /dev/null +++ b/plugins/CashFlow/tests/Cash_Flow_input_NPV_componentTax.xml @@ -0,0 +1,50 @@ + + + 0.05 + 0.392 + 0.04 + + BOP|CA + BOP|RE + IP|CA + IP|RE + + + + + 60 + 0.3 + 0.07 + + + BOP_capacity + -4510000000 + 1100000000 + 0.64 + 15 + + + + 1.0 + BOP_TOT_revenueEL + + + + + + 40 + + + IP_capacity + -153000000 + 231000000 + 1.0 + + + + IP_TOT_revenueBY + 1.0 + + + + diff --git a/plugins/CashFlow/tests/Cash_Flow_input_NPV_customTime.xml b/plugins/CashFlow/tests/Cash_Flow_input_NPV_customTime.xml new file mode 100644 index 0000000000..d29a5c877f --- /dev/null +++ b/plugins/CashFlow/tests/Cash_Flow_input_NPV_customTime.xml @@ -0,0 +1,51 @@ + + + 0.05 + 0.392 + 0.04 + 120 + + BOP|CA + BOP|RE + IP|CA + IP|RE + + + + + 60 + + + BOP_capacity + -4510000000 + 1100000000 + 0.64 + 15 + + + + 1.0 + BOP_TOT_revenueEL + + + + + + 40 + 15 + 2 + + + IP_capacity + -153000000 + 231000000 + 1.0 + + + + IP_TOT_revenueBY + 1.0 + + + + diff --git a/plugins/CashFlow/tests/Cash_Flow_input_NPV_customTime2.xml b/plugins/CashFlow/tests/Cash_Flow_input_NPV_customTime2.xml new file mode 100644 index 0000000000..d96f851272 --- /dev/null +++ b/plugins/CashFlow/tests/Cash_Flow_input_NPV_customTime2.xml @@ -0,0 +1,50 @@ + + + 0.05 + 0.392 + 0.04 + 80 + + BOP|CA + BOP|RE + IP|CA + IP|RE + + + + 60 + + + BOP_capacity + -4510000000 + 1100000000 + 0.64 + 15 + + + + 1.0 + BOP_TOT_revenueEL + + + + + + 40 + 15 + 2 + + + IP_capacity + -153000000 + 231000000 + 1.0 + + + + IP_TOT_revenueBY + 1.0 + + + + diff --git a/plugins/CashFlow/tests/Cash_Flow_input_NPVsearch.xml b/plugins/CashFlow/tests/Cash_Flow_input_NPVsearch.xml new file mode 100644 index 0000000000..0d16a9d981 --- /dev/null +++ b/plugins/CashFlow/tests/Cash_Flow_input_NPVsearch.xml @@ -0,0 +1,49 @@ + + + 0.05 + 0.392 + 0.04 + + BOP|CA + BOP|RE + IP|CA + IP|RE + + + + 60 + + + + BOP_capacity + -4510000000 + 1100000000 + 0.64 + 15 + + + + BOP_TOT_revenueEL + 1.0 + + + + + + 40 + + + + IP_capacity + -153000000 + 231000000 + 1.0 + + + + IP_TOT_revenueBY + 1.0 + + + + diff --git a/plugins/CashFlow/tests/Cash_Flow_input_PI.xml b/plugins/CashFlow/tests/Cash_Flow_input_PI.xml new file mode 100644 index 0000000000..62633e93a3 --- /dev/null +++ b/plugins/CashFlow/tests/Cash_Flow_input_PI.xml @@ -0,0 +1,49 @@ + + + 0.05 + 0.392 + 0.04 + + BOP|CA + BOP|RE + IP|CA + IP|RE + + + + 60 + + + + BOP_capacity + -4510000000 + 1100000000 + 0.64 + 15 + + + + BOP_TOT_revenueEL + 1.0 + + + + + + 40 + + + + IP_capacity + -153000000 + 231000000 + 1.0 + + + + IP_TOT_revenueBY + 1.0 + + + + diff --git a/plugins/CashFlow/tests/Cash_Flow_input_custom_macrs_NPV.xml b/plugins/CashFlow/tests/Cash_Flow_input_custom_macrs_NPV.xml new file mode 100644 index 0000000000..917c121496 --- /dev/null +++ b/plugins/CashFlow/tests/Cash_Flow_input_custom_macrs_NPV.xml @@ -0,0 +1,57 @@ + + + 0.05 + 0.392 + 0.04 + + BOP|CA + BOP|RE + IP|CA + IP|RE + + + + + 60 + + + + + BOP_capacity + -4510000000 + 1100000000 + 0.64 + + 5.0 9.5 8.55 7.7 6.93 6.23 5.9 + 5.90 5.91 5.90 5.91 5.90 5.91 5.90 5.91 2.95 + + + + + + BOP_TOT_revenueEL + 1.0 + + + + + + 40 + + + + + -153000000 + IP_capacity + 231000000 + 1.0 + + + + + IP_TOT_revenueBY + 1.0 + + + + diff --git a/plugins/CashFlow/tests/RevenueData.py b/plugins/CashFlow/tests/RevenueData.py new file mode 100644 index 0000000000..698c8e778a --- /dev/null +++ b/plugins/CashFlow/tests/RevenueData.py @@ -0,0 +1,152 @@ +""" + Author: A. S. Epiney + Date : 03/23/2018 +""" + +# This file contains the year dependent data for BOP_TOT_revenueEL to be used in the CashFlow test inputs. + +import numpy as np + +# ===================================================================================================================== +def initialize(self,runInfoDict,inputFiles): + pass + + + +# ===================================================================================================================== +def run(self, Inputs): + """ + Passes back BOP_TOT_revenueEL scaled with input_scaling + - In, input_scaling, float, used to scale the data + - Out, BOP_TOT_revenueEL, vector with scaled data + """ + + self.BOP_TOT_revenueEL = np.array(Inputs["input_scaling"] * [ + 1.249121E+08, + 1.635489E+08, + 2.852712E+08, + 2.372164E+08, + 3.475519E+07, + 1.192678E+08, + 9.489876E+07, + 9.939405E+07, + 8.203958E+07, + 3.915993E+07, + 2.463487E+08, + 3.286362E+08, + 1.408756E+08, + 2.688309E+08, + 3.274537E+08, + 2.163818E+08, + 1.364193E+08, + 1.448559E+07, + 1.605577E+08, + 1.528224E+08, + 8.456209E+07, + 6.846224E+07, + 2.651716E+08, + 1.509490E+08, + 1.475763E+08, + 2.441756E+08, + 1.347031E+08, + 3.749905E+06, + 3.070918E+08, + 1.673575E+07, + 1.627071E+08, + 2.061307E+08, + 2.692807E+08, + 3.371990E+08, + 1.691349E+08, + 1.416603E+08, + 6.760374E+07, + 7.506250E+07, + 7.651417E+07, + 9.176476E+07, + 4.838345E+07, + 4.244242E+07, + 2.303090E+08, + 2.461599E+08, + 1.873789E+08, + 1.508936E+08, + 1.215092E+08, + 4.185443E+07, + 3.041879E+07, + 2.910637E+08, + 4.434337E+07, + 2.344291E+08, + 2.105549E+08, + 7.681092E+07, + 3.022794E+08, + 1.262402E+08, + 4.201486E+07, + 3.007466E+08, + 2.105868E+08, + 8.829858E+07, + 1.779761E+08, +]) + + self.year = np.array([ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60 +]) + + diff --git a/plugins/CashFlow/tests/VarInp.txt b/plugins/CashFlow/tests/VarInp.txt new file mode 100644 index 0000000000..17041bb10f --- /dev/null +++ b/plugins/CashFlow/tests/VarInp.txt @@ -0,0 +1,5 @@ +BOP_capacity 300.0E6 +BOP_TOT_revenueEL 1.249121E+08, 1.635489E+08, 2.852712E+08, 2.372164E+08, 3.475519E+07, 1.192678E+08, 9.489876E+07, 9.939405E+07, 8.203958E+07, 3.915993E+07, 2.463487E+08, 3.286362E+08, 1.408756E+08, 2.688309E+08, 3.274537E+08, 2.163818E+08, 1.364193E+08, 1.448559E+07, 1.605577E+08, 1.528224E+08, 8.456209E+07, 6.846224E+07, 2.651716E+08, 1.509490E+08, 1.475763E+08, 2.441756E+08, 1.347031E+08, 3.749905E+06, 3.070918E+08, 1.673575E+07, 1.627071E+08, 2.061307E+08, 2.692807E+08, 3.371990E+08, 1.691349E+08, 1.416603E+08, 6.760374E+07, 7.506250E+07, 7.651417E+07, 9.176476E+07, 4.838345E+07, 4.244242E+07, 2.303090E+08, 2.461599E+08, 1.873789E+08, 1.508936E+08, 1.215092E+08, 4.185443E+07, 3.041879E+07, 2.910637E+08, 4.434337E+07, 2.344291E+08, 2.105549E+08, 7.681092E+07, 3.022794E+08, 1.262402E+08, 4.201486E+07, 3.007466E+08, 2.105868E+08, 8.829858E+07, 1.779761E+08 +IP_capacity 51.0E6 +IP_TOT_revenueBY 31.5E6 +Multiplier 1.0 diff --git a/plugins/CashFlow/tests/tests b/plugins/CashFlow/tests/tests new file mode 100644 index 0000000000..f8f84527f6 --- /dev/null +++ b/plugins/CashFlow/tests/tests @@ -0,0 +1,86 @@ +[Tests] + [./CashFlow_PI] + type = 'RavenFramework' + input = 'CashFlow_test_PI.xml' + UnorderedCsv = 'dumpPI.csv' + max_time = 500 + rel_err = 0.001 + [../] + + [./CashFlow_NPV] + type = 'RavenFramework' + input = 'CashFlow_test_NPV.xml' + UnorderedCsv = 'dumpNPV.csv' + max_time = 500 + rel_err = 0.001 + [../] + + [./CashFlow_NPVsearch] + type = 'RavenFramework' + input = 'CashFlow_test_NPVsearch.xml' + UnorderedCsv = 'dumpNPVsearch.csv' + max_time = 500 + rel_err = 0.001 + [../] + + [./CashFlow_IRR] + type = 'RavenFramework' + input = 'CashFlow_test_IRR.xml' + UnorderedCsv = 'dumpIRR.csv' + max_time = 500 + rel_err = 0.001 + [../] + + [./CashFlow_NPV_multiplier ] + type = 'RavenFramework' + input = 'CashFlow_test_NPV_multiplier.xml' + UnorderedCsv = 'dumpNPV_multiplier.csv' + max_time = 500 + rel_err = 0.001 + [../] + + [./CashFlow_NPV_expandedDriver ] + type = 'RavenFramework' + input = 'CashFlow_test_NPV_expandedDriver.xml' + UnorderedCsv = 'dumpNPV_expandedDriver.csv' + max_time = 500 + rel_err = 0.001 + [../] + + [./CashFlow_NPV_componentTax ] + type = 'RavenFramework' + input = 'CashFlow_test_NPV_componentTax.xml' + UnorderedCsv = 'dumpNPV_componentTax.csv' + max_time = 500 + rel_err = 0.001 + [../] + + [./CashFlow_NPV_RunAsCode] + type = 'RavenPython' + input = 'CashFlow_test_NPV_RunAsCode.py' + [../] + + [./CashFlow_NPV_customTime] + type = 'RavenFramework' + input = 'CashFlow_test_NPV_customTime.xml' + UnorderedCsv = 'dumpNPV_customTime.csv' + max_time = 500 + rel_err = 0.001 + [../] + + [./CashFlow_NPV_customTime2] + type = 'RavenFramework' + input = 'CashFlow_test_NPV_customTime2.xml' + UnorderedCsv = 'dumpNPV_customTime2.csv' + max_time = 500 + rel_err = 0.001 + [../] + + [./CashFlow_NPV_custom_MACRS] + type = 'RavenFramework' + input = 'CashFlow_test_NPV_custom_MACRS.xml' + UnorderedCsv = 'dumpNPV_custom_macrs.csv' + max_time = 500 + rel_err = 0.001 + [../] +[]