Repository: idaholab/TEAL (language: python)

Issue:
[TASK] Visualization
--------
Issue Description
--------

It will be great to visualize the cash flows. 

----------------
For Change Control Board: Issue Review
----------------
This review should occur before any development is performed as a response to this issue.
- [x] 1. Is it tagged with a type: defect or task?
- [x] 2. Is it tagged with a priority: critical, normal or minor?
- [x] 3. If it will impact requirements or requirements tests, is it tagged with requirements?
- [x] 4. If it is a defect, can it cause wrong results for users? If so an email needs to be sent to the users.
- [x] 5. 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 a defect, does it impact the latest release branch? If yes, is there any issue tagged with release (create if needed)?
- [ ] 5. If the issue is being closed without a pull request, has an explanation of why it is being closed been provided?

implemented plotter
--------
Pull Request Description
--------
##### What issue does this change request address?

Closes #38 

##### What are the significant changes in functionality due to this change request?

Adding three plots to visualize the cash flows:
- Stacked bar chart
- Grouped bar chart
- Donut chart
----------------
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 and xsd schema. If the input syntax change deprecates existing input files, a conversion script needs to be added (see Conversion Scripts).
- [ ] 3. Make sure the Python code and commenting standards are respected (camelBack, etc.) - See on the [wiki](https://github.com/idaholab/HERON/wiki/Code-Standards) for details.
- [ ] 4. Automated Tests should pass.
- [ ] 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 tes.
- [ ] 6. If the change modifies or adds a requirement or a requirement based test case, the Change Control Board's Chair or designee also needs to approve the change.  The requirements and the requirements test shall be in sync.
- [ ] 7. The merge request must reference an issue.  If the issue is closed, the issue close checklist shall be done.
- [ ] 8. If an analytic test is changed/added, the the analytic documentation must be updated/added.
- [ ] 9. If any test used as a basis for documentation examples have been changed, the associated documentation must be reviewed and assured the text matches the example.




Discussion / hints:

Fix patch:
diff --git a/__init__.py b/__init__.py
index 347544412..ff9d9a22f 100644
--- a/__init__.py
+++ b/__init__.py
@@ -13,8 +13,11 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+
 from .src import Amortization
 from .src import CashFlow_ExtMod
 from .src import CashFlows
 from .src import CashFlowUser
 from .src import main as CashFlow
+from .src import CashFlowPlot
+
diff --git a/src/CashFlowPlot.py b/src/CashFlowPlot.py
new file mode 100644
index 000000000..1571c23ff
--- /dev/null
+++ b/src/CashFlowPlot.py
@@ -0,0 +1,232 @@
+# Copyright 2020, Battelle Energy Alliance, LLC
+#
+# All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+  This module is TEAL outout visualization
+  Author:  baoh
+  Date  :  2021-11-09
+"""
+import os
+import matplotlib.pyplot as plt
+import pandas as pd
+import numpy as np
+
+try:
+  from ravenframework.PluginBaseClasses.OutStreamPlotPlugin import PlotPlugin, InputTypes, InputData
+except:
+  raise IOError('TEAL ERROR (initialization): RAVEN needs to be installed and ' +
+                'TEAL needs to be installed as a plugin to work!')
+
+class CashFlowPlot(PlotPlugin):
+  """
+    Cash flow plot
+  """
+  @classmethod
+  def getInputSpecification(cls):
+    """
+      Define the acceptable user inputs for this class.
+      @ In, None
+      @ Out, specs, InputData.ParameterInput, specification
+    """
+    specs = super().getInputSpecification()
+    specs.addSub(InputData.parameterInputFactory('source', contentType=InputTypes.StringType,
+        descr=r"""specifies which DataObject to take data from"""))
+    specs.addSub(InputData.parameterInputFactory('startYear', contentType=InputTypes.IntegerType,
+        descr=r"""specifies the beginning year for plots"""))
+    specs.addSub(InputData.parameterInputFactory('endYear', contentType=InputTypes.IntegerType,
+        descr=r"""specifies the ending year for plots"""))
+    return specs
+
+  def __init__(self):
+    """
+      Constructor.
+      @ In, None
+      @ Out, None
+    """
+    super().__init__()
+    self.printTag = 'TEAL.CashFlowPlot'
+    self._sourceName = None # name of source data object
+    self._source = None     # actual source data object
+    self._startYear = None  # start year for plotting
+    self._endYear = None    # end year for plotting
+
+  def handleInput(self, spec):
+    """
+      Reads in data from the input file
+      @ In, spec, InputData.ParameterInput, input information
+      @ Out, None
+    """
+    super().handleInput(spec)
+    for node in spec.subparts:
+      if node.getName() == 'source':
+        self._sourceName = node.value
+      elif node.getName() == 'startYear':
+        self._startYear = node.value
+      elif node.getName() == 'endYear':
+        self._endYear = node.value
+    if self._sourceName is None:
+      self.raiseAnError(IOError, 'Input missing the <source> node!')
+
+  def initialize(self, stepEntities):
+    """
+      Set up plotter for each run
+      @ In, stepEntities, dict, entities from the Step
+      @ Out, None
+    """
+    super().initialize(stepEntities)
+    src = self.findSource(self._sourceName, stepEntities)
+    if src is None:
+      self.raiseAnError(IOError, f'Source DataObject {self._sourceName} was not found in the Step!')
+    self._source = src
+
+  def run(self):
+    """
+      Generate the plot
+      @ In, None
+      @ Out, None
+    """
+    data = self._source.asDataset()
+    df = data.to_dataframe().reset_index(level=['cfYears']).reset_index(drop=True)
+    if self._startYear is None:
+      startYear = 0
+    if self._endYear is None:
+      endYear = len(df) - 1
+    dfYear = df[startYear:endYear + 1]
+    dfCashFlow = dfYear.loc[:, dfYear.columns.str.contains("_")]
+
+    # sort data by comp name and cum for color setting
+    cumCashFlow = dfCashFlow.sum().sort_values(ascending=True)
+    labelCompAll = [cumCashFlow.index.tolist()[i].partition('_')[0] for i, _ in enumerate(cumCashFlow)]
+    # Comp name: Comp count
+    compCount = pd.Series({i:labelCompAll.count(i) for i in labelCompAll})
+    cumCashFlow = cumCashFlow.set_axis([cumCashFlow.index.tolist(), labelCompAll])
+    # Sorted by Comp names and values
+    cumCashFlow = cumCashFlow.reset_index(level=1).sort_values('level_1').drop('level_1', axis=1).squeeze()
+
+    # Color setting by comp names
+    defaultStartColor = 60 # default starting color #. 256 selections in total
+    defaultEndColor = 240 # default ending color #. 256 selections in total
+    colorLib = ['Reds', 'Blues', 'Greens', 'Purples', 'Oranges', 'ocean'] # color library is changable
+    compColor = []
+    for i, _ in enumerate(compCount):
+      _cmap = plt.get_cmap(colorLib[i])
+      _rawCmapList = [_cmap(k) for k in range(_cmap.N)]
+      compColor += _rawCmapList[defaultEndColor:defaultStartColor:-(defaultEndColor-defaultStartColor)//compCount[i]]
+    compColor = pd.DataFrame(compColor).set_axis([cumCashFlow.index.tolist()]).T
+    cumCashFlow = cumCashFlow.sort_values(ascending=True)
+    compColor = compColor[cumCashFlow.index[:len(cumCashFlow)]]
+    compColor = compColor.T.values.tolist()
+    negVar = len(list(filter(lambda lableLoc: (lableLoc < 0), cumCashFlow))) # count # of outflows
+    posVar = len(list(filter(lambda lableLoc: (lableLoc >= 0), cumCashFlow))) # count # of inflows
+
+    dfCashFlow = dfCashFlow[cumCashFlow.index[:len(cumCashFlow)]] # Sorted cash flows
+    yearNetCashFlow = dfCashFlow.sum(axis=1) # Sorted yearly net cash flow
+    cumNetCashFlow = yearNetCashFlow.cumsum() # Cumulative net cash flow
+    labels = pd.DataFrame(dfCashFlow.columns) # Sorted labels by CompName and sum of values
+    ## Plot 1: Inflows, outflows and Net Cash Flow
+    widthFlowbar = 0.35
+    _, ax1 = plt.subplots(1, figsize = (15,8))
+    for i, _ in enumerate(cumCashFlow[0:negVar]):
+      if i == 0:
+        ax1.bar(dfYear['cfYears'], dfCashFlow.iloc[:,i], widthFlowbar, color = compColor[i],
+          label = labels.iloc[i,0], edgecolor='white')
+      else:
+        ax1.bar(dfYear['cfYears'], dfCashFlow.iloc[:,i], widthFlowbar, color = compColor[i],
+          bottom = dfCashFlow.iloc[:,0:i-1].sum(axis=1), label = labels.iloc[i,0], edgecolor='white')
+
+    for i, _ in enumerate(cumCashFlow[negVar:negVar+posVar]):
+      if i == 0:
+        ax1.bar(dfYear['cfYears'], dfCashFlow.iloc[:,i+negVar], widthFlowbar,
+          color = compColor[i+negVar], label = labels.iloc[i+negVar,0], edgecolor='white')
+      else:
+        ax1.bar(dfYear['cfYears'], dfCashFlow.iloc[:,i+negVar], widthFlowbar,
+          color = compColor[i+negVar], bottom = dfCashFlow.iloc[:,negVar:negVar+i].sum(axis=1),
+          label = labels.iloc[i+negVar,0], edgecolor='white')
+
+    # grid
+    ax1.set_axisbelow(True)
+    ax1.yaxis.grid(color='gray', linestyle='dashed', alpha=0.7)
+    ax1.set_xlabel('Project Year', fontsize = 15, fontweight='bold')
+    ax1.set_ylabel('Yearly Cash Flows($)', fontsize = 15, fontweight='bold')
+    # total cashflow
+    plt.plot(dfYear['cfYears'], yearNetCashFlow, color='darkolivegreen', marker='o',label='Yearly Net Cash Flow')
+    # Shrink current axis by 20% for legends
+    box = ax1.get_position()
+    ax1.set_position([box.x0, box.y0, box.width * 0.8, box.height])
+    plt.legend(loc='center left', bbox_to_anchor=(1.0,0.5), fancybox=True, ncol=1,fontsize = 15)
+    plt.title('Yearly Inflows, Outflows and Net Cash Flow from Project Year {} to {}'.format(startYear, endYear),
+      y=1.05, fontsize = 20, fontweight='bold')
+
+    fName = os.path.abspath('Cash_Flows_from_Project_Year_{}_to_{}.png'.format(startYear, endYear))
+    plt.savefig(fName,bbox_inches="tight")
+    self.raiseAMessage(f'Saved figure to "{fName}"')
+
+    ## Plot 2: Cumulative discounted free cash flow
+    labelLoc = np.arange(startYear, endYear + 1)  # the label location
+    widthCumBar = 0.2
+    _, ax1 = plt.subplots(1, figsize = (25,12))
+    for i, _ in enumerate(cumCashFlow):
+      ax1.bar(labelLoc + widthCumBar * (i - (len(cumCashFlow) + 1) / 2), dfCashFlow.iloc[:,i],
+        widthCumBar, color = compColor[i], label = labels.iloc[i,0], edgecolor='white')
+    #grid
+    ax1.set_axisbelow(True)
+    ax1.yaxis.grid(color='gray', linestyle='dashed', alpha=0.7)
+    ax1.set_xlabel('Project Year', fontsize = 15, fontweight='bold')
+    ax1.set_ylabel('Yearly Inflows and Outflows ($)', fontsize = 15, fontweight='bold')
+    # cumulative cash flow
+    ax2 = ax1.twinx()
+    ax2.plot(dfYear['cfYears'], cumNetCashFlow, color='darkolivegreen', marker='o',label='Cumulative Net Cash Flow')
+    ax2.set_ylabel('Cumulative Net Cash Flow', color='darkolivegreen', fontsize = 15, fontweight='bold')
+    ax2.tick_params(axis='y', labelcolor='darkolivegreen')
+    # legends
+    ax1.legend(loc='upper center', bbox_to_anchor=(0.5, -0.09),fancybox=True, ncol=negVar+posVar,fontsize = 15)
+    ax2.legend(loc='upper center', bbox_to_anchor=(0.5, -0.05),fancybox=True, ncol=1,fontsize = 15)
+    plt.title('Cumulative Cash Flow from Project Year {} to {}'.format(startYear, endYear),
+      y=1.05, fontsize = 20, fontweight='bold')
+    fName = os.path.abspath('Cumulative_from_Project_Year_{}_to_{}.png'.format(startYear, endYear))
+    plt.savefig(fName,bbox_inches="tight")
+    self.raiseAMessage(f'Saved figure to "{fName}"')
+
+    ## Plot 3: pie chart with inflows and outflows
+    cumulativeCashFlowAbs = [abs(ele) for ele in cumCashFlow]
+    cumOutFlow = cumulativeCashFlowAbs[0:negVar] # donut for outflows when inflows are lower
+    cumInFlow = cumulativeCashFlowAbs[-posVar:] # donut for inflows when outflows are lower
+    diff = sum(cumCashFlow)
+    cumIn = cumInFlow + [abs(diff)]  # donut for inflows when inflows are lower
+    cumOut = cumOutFlow + [abs(diff)] # donut for outflows when outflows are lower
+    widthPie = 0.2
+    radiusPie = 0.8
+    _, ax1 = plt.subplots(1, figsize=[12,10])
+    ax1.axis('equal')
+    ax2 = ax1.twinx()
+    if diff >= 0:
+      pieOut, _ = ax1.pie(cumOut, radius=radiusPie, colors = compColor[0:negVar]+[(0, 0, 0, 0)], startangle=90)
+      pieIn, _ = ax2.pie(cumInFlow, radius=radiusPie - widthPie, colors = compColor[-posVar:], startangle=90)
+      ax1.text(-0.55,0.86,"Inflows - Outflows = {}".format(f'${abs(diff): 1.3e}'),
+        color="black",fontsize=16, fontweight='bold')
+    else:
+      pieOut, _ = ax1.pie(cumOutFlow, radius=radiusPie,colors = compColor[0:negVar], startangle=90)
+      pieIn, _ = ax2.pie(cumIn, radius=radiusPie-widthPie, colors = compColor[-posVar:]+[(0, 0, 0, 0)], startangle=90)
+      ax1.text(-0.55,0.86,"Outflows - Inflows = {}".format(f'${abs(diff): 1.3e}'),
+        color="black",fontsize=16, fontweight='bold')
+
+    ax1.legend(labels.iloc[0:negVar,0], loc='lower right',fontsize = 15)
+    ax2.legend(labels.iloc[-posVar:,0], loc='lower left',fontsize = 15)
+    plt.setp([pieOut, pieIn], width=widthPie, edgecolor='white')
+    plt.title('Cumulative Inflows vs. Outflows from Project Year {} to {}'.format(startYear, endYear),
+      y=1, fontsize = 20, fontweight='bold')
+    fName = os.path.abspath('Cumulative_pie_from_Project_Year_{}_to_{}.png'.format(startYear, endYear))
+    plt.savefig(fName)
+    self.raiseAMessage(f'Saved figure to "{fName}"')
diff --git a/src/CashFlowUser.py b/src/CashFlowUser.py
index dda228687..75d661b10 100644
--- a/src/CashFlowUser.py
+++ b/src/CashFlowUser.py
@@ -19,9 +19,6 @@
 Base module for objects that want to access the functionality of the CashFlow objects.
 """
 
-import os
-import sys
-
 from ..src.CashFlows import Component
 
 # This plugin imports RAVEN modules. if run in stand-alone, RAVEN needs to be installed and this file
diff --git a/src/CashFlow_ExtMod.py b/src/CashFlow_ExtMod.py
index 4ba222b3a..e7af25780 100644
--- a/src/CashFlow_ExtMod.py
+++ b/src/CashFlow_ExtMod.py
@@ -20,13 +20,11 @@
 """
 from __future__ import division, print_function, unicode_literals, absolute_import
 import os
-import sys
 import numpy as np
 import warnings
 warnings.simplefilter('default', DeprecationWarning)
 
 from ..src import main
-from ..src import CashFlows
 
 # 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.
@@ -35,7 +33,7 @@
   from ravenframework.utils.graphStructure import graphObject
   from ravenframework.PluginBaseClasses.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!'")
+  raise IOError("TEAL ERROR (Initialisation): RAVEN needs to be installed and TEAL needs to be installed as a plugin to work!'")
 
 
 class CashFlow(ExternalModelPluginBase):
diff --git a/src/CashFlows.py b/src/CashFlows.py
index ab157f2ab..e3f0290d5 100644
--- a/src/CashFlows.py
+++ b/src/CashFlows.py
@@ -19,18 +19,22 @@
 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
-#from typing_extensions import Required #Where did this line come from??
 import xml.etree.ElementTree as ET
+import itertools as it
+from collections import defaultdict
 
 import numpy as np
-import time
-import itertools as it
 
 from ..src import Amortization
+from ..src import _utils as tutils
+
+# load RAVEN if available (e.g. pip-installed), otherwise add to env
+try:
+  import ravenframework
+except ModuleNotFoundError:
+  loc = tutils.get_raven_loc()
+  sys.path.append(loc)
 
 from ravenframework.utils import mathUtils
 from ravenframework.utils import InputData, InputTypes, TreeStructure, xmlUtils
diff --git a/src/_utils.py b/src/_utils.py
new file mode 100644
index 000000000..e537c203e
--- /dev/null
+++ b/src/_utils.py
@@ -0,0 +1,41 @@
+# Copyright 2020, Battelle Energy Alliance, LLC
+#
+# All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+  utilities for use within TEAL
+
+"""
+import xml.etree.ElementTree as ET
+from os import path
+
+def get_raven_loc():
+  """
+    Return RAVEN location
+    hopefully this is read from TEAL/.ravenconfig.xml
+    @ In, None
+    @ Out, loc, string, absolute location of RAVEN
+  """
+  config = path.abspath(path.join(path.dirname(__file__),'..','.ravenconfig.xml'))
+  if not path.isfile(config):
+    raise IOError(
+        f'HERON config file not found at "{config}"! Has HERON been installed as a plugin in a RAVEN installation?'
+    )
+  loc = ET.parse(config).getroot().find('FrameworkLocation')
+  assert loc is not None and loc.text is not None
+  # The addition of ravenframework as an installable package requires
+  # adding the raven directory to the PYTHONPATH instead of adding
+  # ravenframework. We will expect '.ravenconfig.xml' to point to
+  # raven/ravenframework always, so this is why we grab the parent dir.
+  return path.abspath(path.dirname(loc.text))
diff --git a/src/main.py b/src/main.py
index fba2e8903..8960bbc8e 100644
--- a/src/main.py
+++ b/src/main.py
@@ -19,9 +19,6 @@
 Execution for TEAL (Tool for Economic AnaLysis)
 """
 
-import os
-import sys
-import copy
 import functools
 from collections import defaultdict, OrderedDict
 


Test patch:
diff --git a/tests/CashFlow_test_Output.xml b/tests/CashFlow_test_Output.xml
index 22367877a..582f8e095 100644
--- a/tests/CashFlow_test_Output.xml
+++ b/tests/CashFlow_test_Output.xml
@@ -76,6 +76,7 @@
      <Input class="DataObjects" type="HistorySet">SET_all_data</Input>
      <Output class="OutStreams" type="Print">dumpIRR</Output>
      <Output class="OutStreams" type="Print">dumpData</Output>
+     <Output class="OutStreams" type="Plot">pluginPlot</Output>
     </IOStep>
   </Steps>
 
@@ -89,5 +90,8 @@
       <type>csv</type>
       <source>SET_all_data</source>
     </Print>
+    <Plot name="pluginPlot" subType="TEAL.CashFlowPlot">
+      <source>SET_all_data</source>
+    </Plot>
   </OutStreams>
 </Simulation>
diff --git a/tests/HourlyObjectOrientedTest.py b/tests/HourlyObjectOrientedTest.py
index 36947cc5b..d2680923e 100644
--- a/tests/HourlyObjectOrientedTest.py
+++ b/tests/HourlyObjectOrientedTest.py
@@ -23,11 +23,16 @@
 import sys
 import numpy as np
 import pandas as pd
-sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))) #Plugins (including TEAL)
-sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))) #RAVEN
-sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'raven'))) #RAVEN (if TEAL and RAVEN in same directory)
-from TEAL import CashFlows
-from TEAL import CashFlow as RunCashFlow
+
+# load TEAL if available (e.g. pip-installed), otherwise add to env
+try:
+  import TEAL.src
+except ModuleNotFoundError:
+  tealPath = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
+  sys.path.append(tealPath)
+
+from TEAL.src import CashFlows
+from TEAL.src import main as RunCashFlow
 
 def run(df):
   """
diff --git a/tests/PyomoTest.py b/tests/PyomoTest.py
index a3b39a586..1bf53cc77 100644
--- a/tests/PyomoTest.py
+++ b/tests/PyomoTest.py
@@ -19,15 +19,20 @@
 from functools import partial
 
 import numpy as np
-import pandas as pd
 import pyomo.environ as pyo
 
-sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')))
-sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..','..'))) # Path to access ravenframework
-sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..','raven'))) # Path to access ravenframework
-from TEAL import CashFlows
-from TEAL import CashFlow as RunCashFlow
 
+# load TEAL if available (e.g. pip-installed), otherwise add to env
+try:
+  import TEAL.src
+except ModuleNotFoundError:
+  tealPath = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
+  sys.path.append(tealPath)
+from TEAL.src import CashFlows
+from TEAL.src import main as RunCashFlow
+
+#sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..','..'))) # Path to access ravenframework
+#sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..','raven'))) # Path to access ravenframework
 
 # *** HELPER FUNCTIONS ***
 def build_econ_settings(cfs, life=5, dr=0.1, tax=0.21, infl=0.02184):
diff --git a/tests/tests b/tests/tests
index 2291346e3..6a53fbebb 100644
--- a/tests/tests
+++ b/tests/tests
@@ -110,6 +110,7 @@
   type = 'RavenFramework'
   input = 'CashFlow_test_Output.xml'
   csv = 'dumpData.csv dumpData_0.csv'
+  output = 'Cash_Flows_from_Project_Year_0_to_120.png Cumulative_from_Project_Year_0_to_120.png Cumulative_pie_from_Project_Year_0_to_120.png'
   max_time = 500
   rel_err = 0.001
  [../]