Source code for simulations.tests.test_modules

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Apr  1 14:36:02 2021

@author: gabrielsoto
"""

from modules.NuclearTES import NuclearTES
import unittest, os, math
import numpy as np


[docs]class TestPySAMModules(unittest.TestCase): """ Unit tests for PySAM module setup This testing suite is meant to test the methods in parent class of all modules, that being GenericSSCModule. Only non-abstract methods will be tested. All child classes will share common methods and attributes which will be tested here. Dispatch is NOT tested here. For each individual class, there will be bespoke test classes. """
[docs] def setUp(self): """ Creating instances of modules upon start of each test """ nuctes = NuclearTES(json_name='tests/test_nuctes',is_dispatch=False) #saving list of modules self.mod_list = [nuctes]
[docs] def tearDown(self): """ Deleting instances of modules at end of each test """ # deleting each module for mod in self.mod_list: del mod # deleting module list. this might be overkill? del self.mod_list
[docs] def test__init__(self): """ Testing the shared processes in __init__ of all modules """ # defining necessary attributes to be declared in __init__ attr_list = ['json_name' , 'plant_name', 'ssc_horizon', 'pyomo_horizon', 'SSC_dict'] # looping through all defined modules + attributes for mod in self.mod_list: for attr in attr_list: # checking that all attributes exist in every module self.assertTrue(hasattr(mod,attr) , "{0} does not have '{1}' attribute".format(mod.__class__.__name__,attr)) # checking that the self.store_csv_arrays( ) method was called correctly self.assertTrue(hasattr(mod,"solar_resource_file") , "Something went wrong when {0} called 'store_csv_arrays' method".format(mod.__class__.__name__) )
[docs] def test_store_csv_arrays(self): """ Testing the storage of csv arrays in all modules NOTE: this assumes that the method was already called in __init__ """ # looping through all modules for mod in self.mod_list: # checking that the solar_resource_file path attribute exists self.assertTrue(hasattr(mod,"solar_resource_file") , "Something went wrong when {0} called 'store_csv_arrays' method".format(mod.__class__.__name__) ) # checking that the actual filepath exists in stated directory self.assertTrue(os.path.exists(mod.solar_resource_file) , "Solar Resource file path could not be found for {0}".format(mod.__class__.__name__) )
[docs] def test_create_Plant(self): """ Testing the create_Plant method """ # looping through all modules for mod in self.mod_list: mod.create_Plant() # checking that Plant object was created self.assertTrue(hasattr(mod,'Plant') , "Plant object not created for {0}".format(mod.__class__.__name__) )
[docs] def test_create_Grid(self): """ Testing the create_Grid method """ # looping through all modules for mod in self.mod_list: mod.create_Plant() mod.create_Grid() # checking that Grid object was created self.assertTrue(hasattr(mod,'Grid') , "Grid object not created for {0}".format(mod.__class__.__name__) )
[docs] def test_create_SO(self): """ Testing the create_SO method """ # looping through all modules for mod in self.mod_list: mod.create_Plant() mod.create_SO() # checking that SingleOwner object was created self.assertTrue(hasattr(mod,'SO') , "SO object not created for {0}".format(mod.__class__.__name__) )
[docs] def test_initialize_arrays(self): """ Testing the initialize_arrays method """ # running in time segments versus running full year at once loop_modes = [True, False] # looping through all modules for mod in self.mod_list: for mode in loop_modes: # ===================================================== # run the method for given looping mode mod.run_loop = mode mod.initialize_arrays() # assert that a Log dictionary was created self.assertTrue( hasattr(mod, 'Log_Arrays') , "Log_Arrays dictionary not created in {0} module.".format(mod.__class__.__name__ ) ) # check that individual log arrays were saved to the NE2 module keys = mod.Log_Arrays.keys() # if NOT running time segments, should only be saving the 'gen_long' array condition = len(keys) == 1 if mode is False else len(keys) > 1 self.assertTrue( condition , "Improper amount of keys created in {0} module when run_loop is {1}.".format(mod.__class__.__name__ , mode ) ) for k in keys: self.assertTrue( hasattr(mod, k) , "{0} Plant module doesn't have Output {1}.".format(mod.__class__.__name__ , k) ) mod.reset_all()
[docs] def test_reset_all(self): """ Testing the reset_all method """ try_all_PySAM_mods = [True, False] # looping through all modules for mod in self.mod_list: for try_all in try_all_PySAM_mods: # create PySAM modules mod.create_Plant() # testing safe deletion, will it throw error if Grid/SO was never created? if try_all: mod.create_Grid() mod.create_SO() # initialize arrays mod.run_loop = True mod.initialize_arrays() keys = mod.Log_Arrays.keys() # run the method mod.reset_all() # check that method deleted PySAM modules self.assertFalse( hasattr(mod,"Plant") , "Plant not deleted in {0} module.".format(mod.__class__.__name__ ) ) self.assertFalse( hasattr(mod,"Grid") , "Grid not deleted in {0} module. Grid was {1} beforehand".format(mod.__class__.__name__ , "created" if try_all else "NOT created") ) self.assertFalse( hasattr(mod,"SO") , "SO not deleted in {0} module. SO was {1} beforehand".format(mod.__class__.__name__ , "created" if try_all else "NOT created") ) # check that method deleted output and logging arrays self.assertFalse( hasattr(mod,"Log_Arrays") , "Log_Arrays not deleted in {0} module.".format(mod.__class__.__name__ ) ) for k in keys: self.assertFalse( hasattr(mod, k) , "{0} Plant module still has Output {1}".format(mod.__class__.__name__ , k) )
[docs] def test_log_SSC_arrays(self): """ Testing the log_SSC_arrays method Here we test both instances of the method call, during simulations and during the last simulation segment. """ # running in time segments versus running full year at once loop_modes = [True, False] final_attrs = ['gen_log', 'capacity_factor', 'annual_energy'] # looping through all modules for mod in self.mod_list: # looping through all loop modes for log_final being true for mode in loop_modes: # ================================================================ # test that method behaves correctly for given loop mode mod.run_loop = mode #--- create Plant object and execute it mod.create_Plant( ) mod.simulate_Plant( ) # log final results from looping simulations mod.log_SSC_arrays(log_final=True) listoflists = [list(mod.Log_Arrays.keys() ) , final_attrs ] total_attrs = [item for sublist in listoflists for item in sublist] #'flatten'-ing the list of lists # check that PySAM_Outputs exists as a subclass of Plant self.assertTrue( hasattr(mod.Plant, 'PySAM_Outputs') , "{0} Plant module does not have PySAM_Outputs".format(mod.__class__.__name__ ) ) # check attributes for f in total_attrs: # check that attributes exist self.assertTrue( hasattr(mod, f) , "{0} Plant module does not have output {1} when log_final is True".format(mod.__class__.__name__ , f) ) # check that attributes exist in PySAM_Outputs fake lambda subclass if f not in final_attrs: self.assertTrue( hasattr(mod.Plant.PySAM_Outputs, mod.Log_Arrays[f]) , "{0} PySAM_Outputs module does not have output {1} when log_final is True".format(mod.__class__.__name__ , f) ) # check that attribute is non-zero self.assertTrue( getattr(mod, f).sum() != 0 , "Output {0} sums up to 0 in module {1} when log_final is True".format(f, mod.__class__.__name__ ) ) mod.reset_all() # ================================================================ # test that method behaves correctly for running loop mod.run_loop = True #--- create Plant object and execute it mod.create_Plant( ) # start and end times for full simulation time_start = mod.SSC_dict['time_start'] * mod.u.s time_next = mod.ssc_horizon.to('s') # setting up empty log array for log arrays mod.initialize_arrays() # first execution of Plant through SSC mod.run_Plant_through_SSC( mod.Plant, time_start , time_next ) mod.log_SSC_arrays() # logged arrays keys = mod.Log_Arrays.keys() for k in keys: # check that attributes exist in **module** self.assertTrue( hasattr(mod, k) , "{0} Plant module does not have output {1} when log_final is False".format(mod.__class__.__name__ , k) ) mod.reset_all()
[docs] def test_run_Plant_through_SSC(self): """ Testing the run_Plant_through_SSC method """ # some key attributes to check for in outputs attrs = ['time_hr', 'gen', 'P_cycle', 'q_dot_nuc_inc' ] # looping through all modules for mod in self.mod_list: # create Plant mod.create_Plant( ) # start and end times for full simulation time_start = mod.SSC_dict['time_start'] * mod.u.s time_next = mod.ssc_horizon.to('s') # set up run loop mod.run_loop = True # setting up empty log array for log arrays mod.initialize_arrays() # first execution of Plant through SSC mod.run_Plant_through_SSC( mod.Plant, time_start , time_next ) # check attributes for a in attrs: # check that attributes exist in module.Plant.Outputs after run self.assertTrue( hasattr(mod.Plant.Outputs, a) , "{0} Plant.Outputs does not have output {1} when log_final is True".format(mod.__class__.__name__ , a) ) # check that attribute is non-zero self.assertTrue( np.sum( getattr(mod.Plant.Outputs, a) ) != 0 , "Output {0} sums up to 0 in module {1} when log_final is True".format(a, mod.__class__.__name__ ) )
[docs] def test_simulate_Plant(self): """ Testing the simulate_Plant method Here we test the basic functionality of simulate Plant """ # looping through all modules for mod in self.mod_list: # ===================================================== #---first test what happens when not running in loop mod.create_Plant( ) mod.run_loop = False # simulate Plant mod.simulate_Plant( ) # assert that sizing of time arrays are consistent t_ind = mod.t_ind * mod.u.hr time_stop = mod.SSC_dict['time_stop'] * mod.u.s self.assertTrue( t_ind == time_stop.to('hr') , "Time index not set to time_stop when running full sim time." ) # reset submodules del mod.Plant # ===================================================== #---next test what happens when running in loop mod.create_Plant( ) mod.run_loop = True # simulate Plant mod.simulate_Plant( ) # assert that sizing of time arrays are consistent t_ind = mod.t_ind * mod.u.hr time_stop = mod.ssc_horizon.to('s') self.assertTrue( t_ind.to('s') == time_stop , "Time index not set to SSC Horizon time when running sim in loop." )
[docs] def test_run_sim(self): """ Testing run_sim for all modules NOTE: this doesn't test for accuracy of individual results, just that the processes run in the correct order and output something. """ # list of important attributes for each submodule plant_output_attr = ['annual_energy', 'gen'] grid_output_attr = ['annual_energy_pre_curtailment_ac', 'gen'] so_output_attr = ['ppa', 'lppa_nom', 'lppa_real', 'project_return_aftertax_npv'] # looping through all modules for mod in self.mod_list: # ====================================== #---run full simulation for entire year mod.run_sim(run_loop=False) # check that Plant have written outputs for p_attr in plant_output_attr: self.assertTrue(hasattr(mod.Plant.Outputs, p_attr) , "{0} Plant doesn't have Output {1}".format(mod.__class__.__name__ , p_attr) ) # check that Grid have written outputs for g_attr in grid_output_attr: self.assertTrue(hasattr(mod.Grid.Outputs, g_attr) , "{0} Grid doesn't have Output {1}".format(mod.__class__.__name__ , g_attr) ) # check that SO have written outputs for s_attr in so_output_attr: self.assertTrue(hasattr(mod.SO.Outputs, s_attr) , "{0} SO doesn't have Output {1}".format(mod.__class__.__name__ , s_attr) ) # ====================================== #---run looped-simulation # store results from full simulation annual_energy = mod.Grid.SystemOutput.annual_energy ppa = mod.SO.Outputs.ppa # reset submodules mod.reset_all() # ====================================== #---run simulation in a loop mod.run_sim(run_loop=True) # check that results are in the same ballpark self.assertTrue( math.isclose (mod.Grid.SystemOutput.annual_energy, annual_energy, rel_tol=1e-2) , "Plant and Grid outputs are not within tolerance.") self.assertTrue( math.isclose (mod.SO.Outputs.ppa, ppa , rel_tol=1e-2) , "Grid and SingleOwner outputs are not within tolerance.")
if __name__ == "__main__": unittest.main()