Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into test_script
Browse files Browse the repository at this point in the history
  • Loading branch information
joshua-cogliati-inl committed May 6, 2024
2 parents 904ac30 + a8ad195 commit c73a85b
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 124 deletions.
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@

![FORCE](assets/FORCE_logo-color.png)

The Framework for Optimization of ResourCes and Economics is a collection of software tools, models, and datasets acquired and developed under the Integrated Energy Systems (IES) program to enable analysis of technical and economic viability of myriad IES configurations. FORCE is the consolidating interface and data repository for all the IES toolsets ranging from macro technoeconomic analysis to transient process modeling and experimental validation for integrated energy systems.

# FORCE Use Cases
# Tools
The FORCE framework makes use of several distinct tools to enable technical and economic system analysis of integrated energy systems. The three main tools are:

![HYBRID](https://github.com/idaholab/HYBRID/blob/devel/doc/logos/HYBRID_Logo_color.png)

- [HYBRID](https://github.com/idaholab/HYBRID), which includes technical models of energy systems,

![ORCA](https://github.com/idaholab/ORCA/blob/main/docs/logo/ORCA_transparent.png)

- [ORCA](https://github.com/idaholab/ORCA), which provides tools for the real-time optimization of integrated system control, and

![HERON](https://github.com/idaholab/HERON/blob/devel/logos/HERON_logo_full.png)

- [HERON](https://github.com/idaholab/HERON), for long-term system cost, portfolio sizing, and dispatch optimization.

While FORCE is a framework that connects these tools, currently each tool can be used independently for different parts of the integrated energy systems technical and economic analysis.

## FORCE Use Cases

In addition to the tools available as part of FORCE, several existing use cases that demonstrate use of FORCE are included by way of example in this code base. These are listed in brief below:

| SHORT NAME | DESCRIPTION | REPORT |
|:-----------------------------------------:|:---------------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------:|
Expand All @@ -15,5 +34,5 @@



# Notes
## Testing
The FORCE repo should contain several IES tools. For the FORCE tests in "FORCE/tests/integrations_tests" to work, raven, TEAL, and HERON should be under FORCE. The FORCE tests depend on raven libraries which can be activated as follows: "conda activate raven_libraries"
10 changes: 5 additions & 5 deletions src/heron.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def create_componentsets_in_HERON(comp_sets_folder, heron_input_xml):
heron_comp_list = [] # The list of compoenents
for comp in component:
heron_comp_list.append(comp.attrib["name"]) # The list of components found in the HERON input XML file
print(f" \n The following components are already in the HERON input XML file:'{heron_comp_list}'")
# print(f" \n The following components are already in the HERON input XML file:'{heron_comp_list}'")

comp_set_files_list = os.listdir(comp_sets_folder)

Expand All @@ -67,11 +67,11 @@ def create_componentsets_in_HERON(comp_sets_folder, heron_input_xml):
scaling_factor = comp_set_dict.get('Scaling Factor')

fit_error = comp_set_dict.get('Fitting Average Error (%)')
print(f" \n The FORCE component set '{comp_set_name}' is found")
# print(f" \n The FORCE component set '{comp_set_name}' is found")

# if the component is already in the HERON file, it gets updated
if comp_set_name in heron_comp_list:
print(f"The component set {comp_set_name} already exists in the HERON XML input file. The {comp_set_name} info will be updated")
#print(f"The component set {comp_set_name} already exists in the HERON XML input file. The {comp_set_name} info will be updated")
for component in components_list:
for comp in component:
if comp.attrib["name"] == comp_set_name:
Expand All @@ -80,7 +80,7 @@ def create_componentsets_in_HERON(comp_sets_folder, heron_input_xml):
if node.tag == "economics":
ECO_NODE_FOUND = "True"
print(f"The 'economics' node is found in the component {comp.attrib['name']} and will be updated.")

for subnode in node:
# If the cashflow node is found
if subnode.tag == "CashFlow":
Expand Down Expand Up @@ -127,7 +127,7 @@ def create_componentsets_in_HERON(comp_sets_folder, heron_input_xml):

else:
# if the component is not already in the HERON file, it is created.
print(f"The component set '{comp_set_name}' is not found in the HERON XMl input file. The componnent node '{comp_set_name}' will be created")
# print(f"The component set '{comp_set_name}' is not found in the HERON XMl input file. The componnent node '{comp_set_name}' will be created")
comp_name_dict = {'name': comp_set_name}
for components in components_list:
new_comp_node = ET.SubElement(components, "Component", comp_name_dict)
Expand Down
Empty file added tests/__init__.py
Empty file.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
source_capacity,sink_capacity,mean_NPV,std_NPV,med_NPV,max_NPV,min_NPV,perc_5_NPV,perc_95_NPV,samp_NPV,var_NPV,mean_TotalActivity__source__production__a,mean_TotalActivity__sink__production__a,std_TotalActivity__source__production__a,max_TotalActivity__source__production__a,min_TotalActivity__source__production__a,perc_5_TotalActivity__source__production__a,perc_95_TotalActivity__source__production__a,samp_TotalActivity__source__production__a,var_TotalActivity__source__production__a,var_TotalActivity__sink__production__a,samp_TotalActivity__sink__production__a,perc_95_TotalActivity__sink__production__a,perc_5_TotalActivity__sink__production__a,min_TotalActivity__sink__production__a,max_TotalActivity__sink__production__a,std_TotalActivity__sink__production__a,med_TotalActivity__source__production__a,med_TotalActivity__sink__production__a,ProbabilityWeight,ProbabilityWeight-source_capacity,prefix,PointProbability
1.0,-2.0,-1027240.07054,1.42579068359e-10,-1027240.07054,-1027240.07054,-1027240.07054,-1027240.07054,-1027240.07054,3.0,2.03287907341e-20,40.0,-40.0,0.0,40.0,40.0,40.0,40.0,3.0,0.0,0.0,3.0,-40.0,-40.0,-40.0,-40.0,0.0,40.0,-40.0,0.5,0.5,1,1.0
2.0,-2.0,-1450800.44634,0.0,-1450800.44634,-1450800.44634,-1450800.44634,-1450800.44634,-1450800.44634,3.0,0.0,80.0,-80.0,0.0,80.0,80.0,80.0,80.0,3.0,0.0,0.0,3.0,-80.0,-80.0,-80.0,-80.0,0.0,80.0,-80.0,0.5,0.5,2,1.0
source_capacity,sink_capacity,mean_NPV,std_NPV,med_NPV,max_NPV,min_NPV,perc_5_NPV,perc_95_NPV,samp_NPV,var_NPV,mean_TotalActivity__source__production__a,mean_TotalActivity__sink__production__a,std_TotalActivity__source__production__a,max_TotalActivity__source__production__a,min_TotalActivity__source__production__a,perc_5_TotalActivity__source__production__a,perc_95_TotalActivity__source__production__a,samp_TotalActivity__source__production__a,var_TotalActivity__source__production__a,var_TotalActivity__sink__production__a,samp_TotalActivity__sink__production__a,perc_95_TotalActivity__sink__production__a,perc_5_TotalActivity__sink__production__a,min_TotalActivity__sink__production__a,max_TotalActivity__sink__production__a,std_TotalActivity__sink__production__a,med_TotalActivity__source__production__a,med_TotalActivity__sink__production__a,ProbabilityWeight-source_capacity,ProbabilityWeight,PointProbability,prefix
1.0,-2.0,-1027240.07276,1.19439600045e-10,-1027240.07276,-1027240.07276,-1027240.07276,-1027240.07276,-1027240.07276,20.0,1.4265818059e-20,40.0,-40.0,0.0,40.0,40.0,40.0,40.0,20.0,0.0,0.0,20.0,-40.0,-40.0,-40.0,-40.0,0.0,40.0,-40.0,0.5,0.5,1.0,1
2.0,-2.0,-1450800.44949,2.38879200091e-10,-1450800.44949,-1450800.44949,-1450800.44949,-1450800.44949,-1450800.44949,20.0,5.70632722361e-20,80.0,-80.0,0.0,80.0,80.0,80.0,80.0,20.0,0.0,0.0,20.0,-80.0,-80.0,-80.0,-80.0,0.0,80.0,-80.0,0.5,0.5,1.0,2
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

<Case name="Runs">
<mode>sweep</mode>
<num_arma_samples>3</num_arma_samples>
<num_arma_samples>20</num_arma_samples>
<time_discretization>
<time_variable>Time</time_variable>
<end_time>2</end_time>
Expand Down Expand Up @@ -58,7 +58,7 @@
<!--Units : MW-->
</reference_driver>
<reference_price>
<fixed_value>-1428015.4590150523</fixed_value>
<fixed_value>-1428015.4587681948</fixed_value>
<!--Reference Price (USD)-->
</reference_price>
<scaling_factor_x>
Expand Down
222 changes: 111 additions & 111 deletions tests/integration_tests/Aspen_HERON_FullTest/heron_input.xml
Original file line number Diff line number Diff line change
@@ -1,111 +1,111 @@
<HERON>
<TestInfo>
<name>Cashflows</name>
<author>talbpaul</author>
<created>2020-10-16</created>
<description>
Tests various kinds of cashflows
</description>
<classesTested>HERON</classesTested>
</TestInfo>

<Case name="Runs">
<mode>sweep</mode>
<num_arma_samples>3</num_arma_samples>
<time_discretization>
<time_variable>Time</time_variable>
<end_time>2</end_time>
<num_steps>21</num_steps>
</time_discretization>
<economics>
<ProjectTime>20</ProjectTime>
<DiscountRate>0.08</DiscountRate>
<tax>0.25</tax>
<inflation>0.00</inflation>
<verbosity>50</verbosity>
</economics>
<dispatcher>
<pyomo/>
</dispatcher>
</Case>

<Components>
<Component name="source">
<produces resource="a" dispatch="fixed">
<capacity resource="a">
<sweep_values>1, 2</sweep_values>
</capacity>
</produces>
<economics>
<lifetime>30</lifetime>
<CashFlow name="capex" type="one-time" taxable="False" inflation="none" mult_target="False">
<driver>
<Function method="capacity">functions</Function>
</driver>
<reference_price>
<fixed_value>10000.0</fixed_value>
<multiplier>-1</multiplier>
</reference_price>
<reference_driver>
<fixed_value>10.0</fixed_value>
</reference_driver>
<scaling_factor_x>
<fixed_value>0.999</fixed_value>
</scaling_factor_x>
<!-- <depreciate>5</depreciate> -->
</CashFlow>
<CashFlow name="FOM" type="repeating" period='year' taxable="False" inflation="none" mult_target="False">
<driver>
<Function method="capacity">functions</Function>
</driver>
<reference_price>
<fixed_value>100.0</fixed_value>
<multiplier>-1</multiplier>
</reference_price>
<reference_driver>
<fixed_value>10.0</fixed_value>
</reference_driver>
<scaling_factor_x>
<fixed_value>0.999</fixed_value>
</scaling_factor_x>
</CashFlow>
<CashFlow name="VOM" type="repeating" taxable="True" inflation="none" mult_target="False">
<driver>
<activity>a</activity>
</driver>
<reference_price>
<fixed_value>-1</fixed_value>
</reference_price>
</CashFlow>
</economics>
</Component>

<Component name="sink">
<demands resource="a" dispatch="independent">
<capacity>
<fixed_value>-2</fixed_value>
</capacity>
</demands>
<economics>
<lifetime>30</lifetime>
<CashFlow name="capex" type="one-time" taxable="False" inflation="none" mult_target="False">
<driver>
<activity>a</activity>
</driver>
<reference_price>
<fixed_value>10.0</fixed_value>
<multiplier>-3.14</multiplier>
</reference_price>
</CashFlow>
</economics>
</Component>
</Components>

<DataGenerators>
<ARMA name='flex' variable="Signal">arma_30yr.pk</ARMA>
<Function name="functions">functions.py</Function>
</DataGenerators>
</HERON>



<HERON>
<TestInfo>
<name>Cashflows</name>
<author>talbpaul</author>
<created>2020-10-16</created>
<description>
Tests various kinds of cashflows
</description>
<classesTested>HERON</classesTested>
</TestInfo>

<Case name="Runs">
<mode>sweep</mode>
<num_arma_samples>20</num_arma_samples>
<time_discretization>
<time_variable>Time</time_variable>
<end_time>2</end_time>
<num_steps>21</num_steps>
</time_discretization>
<economics>
<ProjectTime>20</ProjectTime>
<DiscountRate>0.08</DiscountRate>
<tax>0.25</tax>
<inflation>0.00</inflation>
<verbosity>50</verbosity>
</economics>
<dispatcher>
<pyomo/>
</dispatcher>
</Case>

<Components>
<Component name="source">
<produces resource="a" dispatch="fixed">
<capacity resource="a">
<sweep_values>1, 2</sweep_values>
</capacity>
</produces>
<economics>
<lifetime>30</lifetime>
<CashFlow name="capex" type="one-time" taxable="False" inflation="none" mult_target="False">
<driver>
<Function method="capacity">functions</Function>
</driver>
<reference_price>
<fixed_value>10000.0</fixed_value>
<multiplier>-1</multiplier>
</reference_price>
<reference_driver>
<fixed_value>10.0</fixed_value>
</reference_driver>
<scaling_factor_x>
<fixed_value>0.999</fixed_value>
</scaling_factor_x>
<!-- <depreciate>5</depreciate> -->
</CashFlow>
<CashFlow name="FOM" type="repeating" period='year' taxable="False" inflation="none" mult_target="False">
<driver>
<Function method="capacity">functions</Function>
</driver>
<reference_price>
<fixed_value>100.0</fixed_value>
<multiplier>-1</multiplier>
</reference_price>
<reference_driver>
<fixed_value>10.0</fixed_value>
</reference_driver>
<scaling_factor_x>
<fixed_value>0.999</fixed_value>
</scaling_factor_x>
</CashFlow>
<CashFlow name="VOM" type="repeating" taxable="True" inflation="none" mult_target="False">
<driver>
<activity>a</activity>
</driver>
<reference_price>
<fixed_value>-1</fixed_value>
</reference_price>
</CashFlow>
</economics>
</Component>

<Component name="sink">
<demands resource="a" dispatch="independent">
<capacity>
<fixed_value>-2</fixed_value>
</capacity>
</demands>
<economics>
<lifetime>30</lifetime>
<CashFlow name="capex" type="one-time" taxable="False" inflation="none" mult_target="False">
<driver>
<activity>a</activity>
</driver>
<reference_price>
<fixed_value>10.0</fixed_value>
<multiplier>-3.14</multiplier>
</reference_price>
</CashFlow>
</economics>
</Component>
</Components>

<DataGenerators>
<ARMA name='flex' variable="Signal">arma_30yr.pk</ARMA>
<Function name="functions">functions.py</Function>
</DataGenerators>
</HERON>



4 changes: 3 additions & 1 deletion tests/integration_tests/Aspen_HERON_FullTest/tests
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@
[./run_RAVEN]
type = OrderedCSV
output = 'Runs_o/sweep.csv'
rel_err = 5e-2
rel_err = 0.01
zero_threshold = 1e-9

[../]
[../]
[]
Empty file added tests/unit_tests/__init__.py
Empty file.
61 changes: 61 additions & 0 deletions tests/unit_tests/test_heron.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import unittest
from unittest.mock import mock_open, patch, MagicMock
import xml.etree.ElementTree as ET

from FORCE.src.heron import create_componentsets_in_HERON

class TestCreateComponentSetsInHERON(unittest.TestCase):

def setUp(self):
# Example of a minimal XML structure
self.heron_xml = """<HERON>
<Components>
<Component name="ExistingComponent">
<economics>
<CashFlow name="ExistingComponent_capex">
<fixed_value>100</fixed_value>
</CashFlow>
</economics>
</Component>
</Components>
</HERON>"""
self.tree = ET.ElementTree(ET.fromstring(self.heron_xml))

@patch('xml.etree.ElementTree.parse')
@patch('os.listdir')
@patch('builtins.open',
new_callable=mock_open,
read_data="""{
"Component Set Name": "NewComponent",
"Reference Driver": 1000,
"Reference Driver Power Units": "kW",
"Reference Price (USD)": 2000,
"Scaling Factor": 0.5
}""")
def test_new_component_creation(self, mock_file, mock_listdir, mock_parse):
# Setup the mock to return an XML tree
mock_parse.return_value = self.tree
mock_listdir.return_value = ['componentSet1.json']

# Call the function
result_tree = create_componentsets_in_HERON("/fake/folder", "/fake/heron_input.xml")

# Verify the XML was updated correctly
components = result_tree.findall('.//Component[@name="NewComponent"]')
self.assertEqual(len(components), 1)
economics = components[0].find('economics')
self.assertIsNotNone(economics)

# Verify the CashFlow node was created
cashflows = economics.findall('CashFlow')
self.assertEqual(len(cashflows), 1)
self.assertEqual(cashflows[0].attrib['name'], 'NewComponent_capex')

# Verify the reference driver and price updates
ref_driver = cashflows[0].find('./reference_driver/fixed_value')
self.assertEqual(ref_driver.text, '1.0') # The driver should have been converted from kW to MW

# Add more tests here to cover other conditions and edge cases

if __name__ == '__main__':
unittest.main()

0 comments on commit c73a85b

Please sign in to comment.