diff --git a/.gitignore b/.gitignore index a6c50eec..fa98e411 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ tests/simulation.ini # bifacial_radiance temp folder bifacial_radiance/TEMP/ +TEMP/ # bifacial_radiance other bifacial_radiance/data/source/ diff --git a/bifacial_radiance/data/module.json b/bifacial_radiance/data/module.json index f75b3954..61c80902 100644 --- a/bifacial_radiance/data/module.json +++ b/bifacial_radiance/data/module.json @@ -62,51 +62,38 @@ "z": 0.02, "zgap": 0.1 }, + "test": { + "bifi": 1, + "glass": false, + "modulefile": "objects\\test.rad", + "modulematerial": "black", + "numpanels": 1, + "offsetfromaxis": 0, + "scenex": 1.6, + "sceney": 0.95, + "scenez": 0.1, + "text": "! genbox black test 1.59 0.95 0.02 | xform -t -0.795 -0.475 0 -a 1 -t 0 0.95 0", + "x": 1.59, + "xgap": 0.01, + "y": 0.95, + "ygap": 0.0, + "z": 0.02, + "zgap": 0.1 + }, "test-module": { "bifi": 1, - "cellModule": { - "centerJB": null, - "numcellsx": 6, - "numcellsy": 10, - "xcell": 0.156, - "xcellgap": 0.02, - "ycell": 0.156, - "ycellgap": 0.02 - }, - "frameParams": { - "frame_material": "Metal_Grey", - "frame_thickness": 0.003, - "frame_width": 0.05, - "frame_z": 0.03, - "nSides_frame": 4 - }, - "glass": true, + "glass": false, "modulefile": "objects\\test-module.rad", "modulematerial": "black", "numpanels": 1, - "offsetfromaxis": 0.18, - "omegaParams": { - "inverted": true, - "mod_overlap": 0.003, - "omega_material": "Metal_Grey", - "omega_thickness": 0.004, - "x_omega1": 0.005, - "x_omega3": 0.0015, - "y_omega": 0.5 - }, - "scenex": 1.046, - "sceney": 1.74, - "scenez": 0.15, - "text": "! genbox black cellPVmodule 0.156 0.156 0.02 | xform -t -0.44 -0.87 0.18 -a 6 -t 0.176 0 0 -a 10 -t 0 0.176 0 -a 1 -t 0 1.74 0\r\n! genrev Metal_Grey tube1 t*1.046 0.05 32 | xform -ry 90 -t -0.445 0 0\r\n! genbox stock_glass test-module_Glass 1.046 1.75 0.01 | xform -t -0.445 -0.875 0.175 -a 1 -t 0 1.74 0\r\n! genbox Metal_Grey frameside 0.003 1.74 0.03 | xform -t -0.518 -0.87 0.145 -a 1 -t 0 1.74 0 | xform -rz 0\r\n! genbox Metal_Grey frameleg 0.047 1.74 0.003 | xform -t -0.515 -0.87 0.145 -a 1 -t 0 1.74 0 | xform -rz 0\r\n! genbox Metal_Grey frameside 0.003 1.74 0.03 | xform -t 0.515 -0.87 0.145 -a 1 -t 0 1.74 0 | xform -rz 0\r\n! genbox Metal_Grey frameleg 0.047 1.74 0.003 | xform -t 0.468 -0.87 0.145 -a 1 -t 0 1.74 0 | xform -rz 0\r\n! genbox Metal_Grey frameside 1.03 0.003 0.027 | xform -t -0.515 0.867 0.148 -a 1 -t 0 1.74 0\r\n! genbox Metal_Grey frameleg 0.936 0.05 0.003 | xform -t -0.468 0.82 0.145 -a 1 -t 0 1.74 0\r\n! genbox Metal_Grey frameside 1.03 0.003 0.027 | xform -t -0.515 -0.87 0.148 -a 1 -t 0 1.74 0\r\n! genbox Metal_Grey frameleg 0.936 0.05 0.003 | xform -t -0.468 -0.87 0.145 -a 1 -t 0 1.74 0\r\n! genbox Metal_Grey mod_adj 0.005 0.5 0.004 | xform -t 0.515 -0.25 0.141\r\n! genbox Metal_Grey verti 0.004 0.5 0.1 | xform -t 0.5150000000000001 -0.25 0.04499999999999999\r\n! genbox Metal_Grey tt_adj 0.0015 0.5 0.004 | xform -t 0.5135000000000002 -0.25 0.04499999999999999\r\n! genbox Metal_Grey mod_adj 0.005 0.5 0.004 | xform -t -0.52 -0.25 0.141\r\n! genbox Metal_Grey verti 0.004 0.5 0.1 | xform -t -0.5190000000000001 -0.25 0.04499999999999999\r\n! genbox Metal_Grey tt_adj 0.0015 0.5 0.004 | xform -t -0.5150000000000001 -0.25 0.04499999999999999", - "torquetube": { - "diameter": 0.1, - "material": "Metal_Grey", - "tubetype": "Round", - "visible": true - }, - "x": 1.036, + "offsetfromaxis": 0, + "scenex": 1.01, + "sceney": 2.0, + "scenez": 0.1, + "text": "! genbox black test-module 1 2 0.02 | xform -t -0.5 -1.0 0 -a 1 -t 0 2.0 0", + "x": 1, "xgap": 0.01, - "y": 1.74, + "y": 2, "ygap": 0.0, "z": 0.02, "zgap": 0.1 diff --git a/bifacial_radiance/load.py b/bifacial_radiance/load.py index 35d6846a..90671b2f 100644 --- a/bifacial_radiance/load.py +++ b/bifacial_radiance/load.py @@ -319,9 +319,7 @@ def _printRow(analysisobj, key): else: keyname = 'timestamp' return pd.concat([pd.DataFrame({keyname:key},index=[0]), - analysisobj.getResults(), - analysisobj.power_data - ], axis=1) + analysisobj.results], axis=1) for key in trackerdict: try: diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py index 2e187d4b..1b75fe21 100644 --- a/bifacial_radiance/main.py +++ b/bifacial_radiance/main.py @@ -356,9 +356,27 @@ class RadianceObj(SuperClass): _setPath : change the working directory """ + @property + def results(self): + """ + Iterate over trackerdict and return irradiance results + following analysis1axis runs + + Returns + ------- + results : Pandas.DataFrame + dataframe containing irradiance scan results. + """ + from bifacial_radiance.load import getResults + + if getattr(self, 'trackerdict', None) is None: + return None + + return getResults(self.trackerdict, self.cumulativesky) + def __repr__(self): #return str(self.__dict__) - return str(type(self)) + ' : ' + str({key: self.__dict__[key] for key in self.columns if key != 'trackerdict'}) + return str(type(self)) + ' : ' + str({key: self.__dict__[key] for key in self.columns if (key != 'trackerdict') & (key != 'results') }) def __init__(self, name=None, path=None, hpc=False): ''' initialize RadianceObj with path of Radiance materials and objects, @@ -393,10 +411,10 @@ def __init__(self, name=None, path=None, hpc=False): #self.nMods = None # number of modules per row #self.nRows = None # number of rows per scene self.hpc = hpc # HPC simulation is being run. Some read/write functions are modified - self.CompiledResults = None # DataFrame of cumulative results, output from self.calculateResults() - + self.compiledResults = pd.DataFrame(None) # DataFrame of cumulative results, output from self.calculatePerformance1axis() + now = datetime.datetime.now() - self.nowstr = str(now.date())+'_'+str(now.hour)+str(now.minute)+str(now.second) + self.nowstr = str(now.date())+'_'+str(now.hour).zfill(2)+str(now.minute).zfill(2)+str(now.second).zfill(2) _checkRaypath() # make sure we have RADIANCE path set up correctly # DEFAULTS @@ -711,8 +729,8 @@ def returnMaterialFiles(self, material_path=None): self.materialfiles = materialfilelist return materialfilelist - - def getResults(self, trackerdict=None): + ''' + def getResults(self, trackerdict=None): #DEPRECATED IN FAVOR OF self.results """ Iterate over trackerdict and return irradiance results following analysis1axis runs @@ -734,7 +752,7 @@ def getResults(self, trackerdict=None): trackerdict = self.trackerdict return getResults(trackerdict, self.cumulativesky) - + ''' def sceneNames(self, scenes=None): if scenes is None: scenes = self.scenes @@ -2250,7 +2268,7 @@ def makeModule(self, name=None, x=None, y=None, z=None, modulefile=None, zgap=0.1, numpanels=1, rewriteModulefile=True, glass=False, modulematerial=None, bifi=1, **kwargs): """ - pass module generation details into ModuleObj(). See ModuleObj() + pass module generation details into ModuleObj(). See ModuleObj docstring for more details """ from bifacial_radiance import ModuleObj @@ -2538,7 +2556,8 @@ def makeScene1axis(self, trackerdict=None, module=None, sceneDict=None, module : str or ModuleObj Name or ModuleObj created with makeModule() sceneDict : - Dictionary with keys:`tilt`, `hub_height`, `pitch`, `azimuth` + Dictionary with keys:`tilt`, `hub_height`, `pitch` (or GCR), `azimuth`, + optional: 'originx', 'originy' cumulativesky : bool Defines if sky will be generated with cumulativesky or gendaylit. customtext : str @@ -2740,7 +2759,7 @@ def analysis1axis(self, trackerdict=None, singleindex=None, accuracy='low', Loop through trackerdict and runs linescans for each scene and scan in there. If multiple scenes exist in the trackerdict, only ONE scene can be analyzed at a time. - Todo: how to run calculateResults with array of multiple results + TODO: how to run calculateResults with array of multiple results Parameters ---------------- @@ -2799,8 +2818,9 @@ def analysis1axis(self, trackerdict=None, singleindex=None, accuracy='low', for each timestamp: trackerdict.key.'AnalysisObj' : analysis object for this tracker theta - to get a dictionary of results, run :py:class:`bifacial_radiance.AnalysisObj`.getResults - :py:class:`bifacial_radiance.AnalysisObj`.getResults returns the following keys: + to get a dictionary of results, run :py:class:`bifacial_radiance.AnalysisObj`.results + :py:class:`bifacial_radiance.AnalysisObj`.results returns the following df: + 'name', 'modNum', 'rowNum', 'sceneNum', 'x','y','z', 'mattype', 'rearMat', 'Wm2Front' : np.array of front Wm-2 irradiances, len=sensorsy_back 'Wm2Back' : np.array of rear Wm-2 irradiances, len=sensorsy_back 'backRatio' : np.array of rear irradiance ratios, len=sensorsy_back @@ -2927,8 +2947,8 @@ def analysis1axisground(self, trackerdict=None, singleindex=None, accuracy='low' for each timestamp: trackerdict.key.'AnalysisObj' : analysis object for this tracker theta - to get a dictionary of results, run :py:class:`bifacial_radiance.AnalysisObj`.getResults - :py:class:`bifacial_radiance.AnalysisObj`.getResults returns the following keys: + to get a dictionary of results, run :py:class:`bifacial_radiance.AnalysisObj`.results + :py:class:`bifacial_radiance.AnalysisObj`.results returns the following keys: 'Wm2Ground' : np.array of Wm-2 irradiances along the ground, len=sensorsground 'sensorsground' : int of number of ground scan points @@ -2973,11 +2993,11 @@ def analysis1axisground(self, trackerdict=None, singleindex=None, accuracy='low' sensorsground=sensorsground) analysis.analysis(octfile=octfile,name=name, frontscan=groundscanid, accuracy=accuracy) - #Results['AnalysisObj']=analysis # try to push Wm2Ground and sensorsground into the AnalysisObj... - analysis.Wm2Ground = analysis.Wm2Front - del analysis.Wm2Front - analysis.sensorsground = analysis.Wm2Ground.__len__() + #analysis.Wm2Ground = analysis.Wm2Front + #del analysis.Wm2Front + #analysis.sensorsground = analysis.Wm2Ground.__len__() + analysis.sensorsground = len(analysis.Wm2Front) trackerdict[index]['AnalysisObj'].append(analysis) except Exception as e: # problem with file. TODO: only catch specific error types here. warnings.warn('Index: {}. Problem with file. Error: {}. Skipping'.format(index,e), Warning) @@ -2993,14 +3013,14 @@ def analysis1axisground(self, trackerdict=None, singleindex=None, accuracy='low' """ try: print('Index: {}. Wm2Ground: {}. sensorsground: {}'.format(index, - np.mean(analysis.Wm2Ground), sensorsground)) + np.mean(analysis.Wm2Front), analysis.sensorsground)) except AttributeError: #no Wm2Front warnings.warn('AnalysisObj not successful.') return trackerdict - def calculateResults(self, CECMod=None, glassglass=False, bifacialityfactor=None, - CECMod2=None, agriPV=False): + def calculatePerformance1axis(self, trackerdict=None, module=None, + CECMod2=None): ''' Loops through all results in trackerdict and calculates performance, considering electrical mismatch, using @@ -3008,19 +3028,12 @@ def calculateResults(self, CECMod=None, glassglass=False, bifacialityfactor=None Parameters - ---------- - CECMod : Dict - Dictionary with CEC Module PArameters for the module selected. Must - contain at minimum alpha_sc, a_ref, I_L_ref, I_o_ref, R_sh_ref, - R_s, Adjust. If 'None' passed, a default module type is selected - glassglass : boolean, optional - If True, module packaging is set to glass-glass for thermal - coefficients for module temperature calculation. Else it is - assumes it is a glass-polymer package. - bifacialityfactor : float, optional - bifaciality factor to be used on calculations, range 0 to 1. If - not passed, it uses the module object's stored bifaciality factor. - CEcMod2 : Dict + ---------- + module: ModuleObj from scene.module + It's best to set this in advance in the ModuleObj. + If passed in here, it overrides the value that may be set in the + trackerdict already. + CECMod2 : Dict Dictionary with CEC Module Parameters for a Monofacial module. If None, same module as CECMod is used for the BGE calculations, but just using the front irradiance (Gfront). @@ -3038,12 +3051,14 @@ def calculateResults(self, CECMod=None, glassglass=False, bifacialityfactor=None Pout_raw: power output calculated from POA_total, considers wind speed and temp_amb if in trackerdict. Pout: power output considering electrical mismatch + ''' from bifacial_radiance import performance import pandas as pd - trackerdict = self.trackerdict + if trackerdict is None: + trackerdict = self.trackerdict keys = list(trackerdict.keys()) @@ -3057,9 +3072,7 @@ def _printRow(analysisobj, key): else: keyname = 'timestamp' return pd.concat([pd.DataFrame({keyname:key},index=[0]), - analysisobj.getResults(), - analysisobj.power_data - ], axis=1) + analysisobj.results], axis=1) @@ -3072,48 +3085,28 @@ def _printRow(analysisobj, key): #else: # loop over module and row values in 'Results' keys_all = [] - self.CompiledResults = pd.DataFrame(None) - bifi_factor_internal = None + self.compiledResults = pd.DataFrame(None) if not self.cumulativesky: - - if CECMod is None: - print("No CECModule data passed; using default for Prism Solar BHC72-400") - #url = 'https://raw.githubusercontent.com/NREL/SAM/patch/deploy/libraries/CEC%20Modules.csv' - url = os.path.join(DATA_PATH,'CEC Modules.csv') - db = pd.read_csv(url, index_col=0) # Reading this might take 1 min or so, the database is big. - modfilter2 = db.index.str.startswith('Pr') & db.index.str.endswith('BHC72-400') - CECMod = db[modfilter2] - + for key in keys: meteo_data = _trackerMeteo(trackerdict[key]) - - - + # TODO HERE: SUM all keys for rows that have the same rowWanted/modWanted try: for analysis in trackerdict[key]['AnalysisObj']: # loop over multiple row & module in trackerDict['AnalysisObj'] keys_all.append(key) - # Search for module object bifaciality - if (bifacialityfactor is None) & (bifi_factor_internal is None): - try: - bifi_factor_internal = trackerdict[key]['scenes'][analysis.sceneNum].module.bifi - print("Bifaciality factor of module stored is ", bifi_factor_internal) - except(TypeError, KeyError): - bifi_factor_internal = 1 - elif (bifacialityfactor is None) : - try: - bifi_factor_internal = trackerdict[key]['scenes'][analysis.sceneNum].module.bifi - except(TypeError, KeyError): - bifi_factor_internal = 1 + # Search for module object + if module is None: + module_local = trackerdict[key]['scenes'][analysis.sceneNum].module else: - bifi_factor_internal = bifacialityfactor - power_data = analysis.calc_performance(meteo_data=meteo_data, CECMod=CECMod, - cumulativesky=self.cumulativesky, glassglass=glassglass, - bifacialityfactor=bifi_factor_internal, CECMod2=CECMod2, - agriPV=agriPV) - self.CompiledResults = pd.concat([self.CompiledResults, + module_local = module + analysis.calculatePerformance(meteo_data=meteo_data, + module=module_local, + cumulativesky=self.cumulativesky, + CECMod2=CECMod2) + self.compiledResults = pd.concat([self.compiledResults, _printRow(analysis, key)], ignore_index=True) except KeyError: pass @@ -3122,25 +3115,25 @@ def _printRow(analysisobj, key): else: - # TODO HERE: SUM all keys for rows that have the same rowWanted/modWanted - for key in keys: - try: - for analysis in trackerdict[key]['AnalysisObj']: # loop over multiple row & module in trackerDict['AnalysisObj'] - keys_all.append(key) - self.CompiledResults = pd.concat([self.CompiledResults, - _printRow(analysis, key)], ignore_index=True) - except KeyError: - pass - - - self.CompiledResults = performance.calculateResultsGencumsky1axis(results=self.CompiledResults, - bifacialityfactor=1.0, - fillcleanedSensors=True, agriPV=False) - - self.CompiledResults.to_csv(os.path.join('results', 'Cumulative_Results.csv')) + if module is None: + for key in keys: # loop over trackerdict to find first available module + try: + for analysis in trackerdict[key]['AnalysisObj']: + module_local = trackerdict[key]['scenes'][analysis.sceneNum].module + break + except (KeyError, AttributeError, IndexError): + pass + else: + module_local = module + self.compiledResults = performance.calculatePerformanceGencumsky(results=self.results, + bifacialityfactor=module_local.bifi, + fillcleanedSensors=False) + + self.compiledResults.to_csv(os.path.join('results', 'Cumulative_Results.csv'), + float_format='%0.3f', index=False) self.trackerdict = trackerdict - return self.CompiledResults + return self.compiledResults def generate_spectra(self, metdata=None, simulation_path=None, ground_material=None, scale_spectra=False, @@ -3476,7 +3469,7 @@ def __init__(self, module=None, name=None, hpc=False): self.module = ModuleObj(name=module) - elif type(module) == ModuleObj: # try moduleObj + elif str(type(module)) == "": # try moduleObj self.module = module #self.moduleDict = self.module.getDataDict() @@ -4366,12 +4359,31 @@ def _makeTrackerCSV(self, theta_list, trackingdata): class AnalysisObj(SuperClass): """ Analysis class for performing raytrace to obtain irradiance measurements - at the array, as well plotting and reporting results. + at the array, as well as plotting and reporting results. """ + @property + def results(self): + """ + go through the AnalysisObj and return a DF of irradiance result keys. + """ + try: + keylist = ['rowWanted', 'modWanted', 'sceneNum', 'name', 'x', 'y','z', + 'Wm2Front', 'Wm2Back', 'Wm2Ground', 'backRatio', 'mattype', 'rearMat' ] + resultdict = {k: v for k, v in self.__dict__.items() if k in keylist} + results = pd.DataFrame.from_dict(resultdict, orient='index').T.rename( + columns={'modWanted':'modNum', 'rowWanted':'rowNum'}) + if getattr(self, 'power_data', None) is not None: + results = pd.concat([results, self.power_data], axis=1) + return results.loc[:,~results.columns.duplicated()] + else: + return results.loc[:,~results.columns.duplicated()] + except AttributeError: + return None + def __printval__(self, attr): try: t = getattr(self,attr, None)[0] - except TypeError: + except (TypeError, KeyError): t = None if isinstance(t, (np.floating, float)) : return np.array(getattr(self,attr)).round(3).tolist() @@ -4379,7 +4391,7 @@ def __printval__(self, attr): return getattr(self,attr) def __repr__(self): - return str(type(self)) + ' : ' + str({key: self.__printval__(key) for key in self.columns}) + return str(type(self)) + ' : ' + str({key: self.__printval__(key) for key in self.columns if key != 'results'}) def __init__(self, octfile=None, name=None, hpc=False): """ Initialize AnalysisObj by pointing to the octfile. Scan information @@ -4403,26 +4415,18 @@ def __init__(self, octfile=None, name=None, hpc=False): self.modWanted = None self.rowWanted = None self.sceneNum = 0 # should this be 0 or None by default?? - self.power_data = None # results from self.calc_performance() stored here - + self.power_data = None # results from self.calculatePerformance() stored here + """ + def getResults(self): ### REPLACED BY `results` PROPERTY - - def getResults(self): - """ - go through the AnalysisObj and return a dict of irraidance result keys, - This can be passed into CompileResults - - Returns - ------- - Results : dict. irradiance scan results - """ + #TODO (optional?) Merge power_data to returned values?? keylist = ['rowWanted', 'modWanted', 'sceneNum', 'name', 'x', 'y','z', 'Wm2Front', 'Wm2Back', 'Wm2Ground', 'backRatio', 'mattype', 'rearMat' ] resultdict = {k: v for k, v in self.__dict__.items() if k in keylist} return pd.DataFrame.from_dict(resultdict, orient='index').T.rename( columns={'modWanted':'modNum', 'rowWanted':'rowNum'}) - + """ def makeImage(self, viewfile, octfile=None, name=None): @@ -5316,7 +5320,7 @@ def _checkPath(rowpath): # create the file structure if it doesn't exist savefile = 'compiledRow_{}.csv'.format(rowWanted) df_row.to_csv(os.path.join(rowpath, savefile), sep = ',', - index = False) + index=False, float_format='%0.3f') return df_row @@ -5374,7 +5378,7 @@ def analyzeField(self, octfile, scene, name=None, savefile = 'compiledField_{}.csv'.format(name) result.to_csv(os.path.join(fieldpath, savefile), sep = ',', - index = False) + index=False, float_format='%0.3f') return result @@ -5450,29 +5454,20 @@ def analysis(self, octfile, name, frontscan, backscan=None, return frontDict, backDict - def calc_performance(self, meteo_data, CECMod, cumulativesky, glassglass=False, bifacialityfactor=1, - CECMod2=None, agriPV=False): + def calculatePerformance(self, meteo_data, cumulativesky, module, + CECMod2=None): """ - For a given AnalysisObj, use performance.calculateResults to calculate performance, + For a given AnalysisObj, use performance.calculatePerformance to calculate performance, considering electrical mismatch, using PVLib. Cell temperature is calculated Parameters - ---------- + ---------- meteo_data : Dict Dictionary with meteorological data needed to run CEC model. Keys: 'temp_air', 'wind_speed', 'dni', 'dhi', 'ghi' - CECMod : Dict - Dictionary with CEC Module PArameters for the module selected. Must - contain at minimum alpha_sc, a_ref, I_L_ref, I_o_ref, R_sh_ref, - R_s, Adjust. If 'None' passed, a default module type is selected - glassglass : boolean, optional - If True, module packaging is set to glass-glass for thermal - coefficients for module temperature calculation. Else it is - assumes it is a glass-polymer package. - bifacialityfactor : float, optional - bifaciality factor to be used on calculations, range 0 to 1. If - not passed, it uses the module object's stored bifaciality factor. - CEcMod2 : Dict + module: ModuleObj from scene.module + Requires CEC Module parameters to be set. If None, default to Prism Solar. + CECMod2 : Dict Dictionary with CEC Module Parameters for a Monofacial module. If None, same module as CECMod is used for the BGE calculations, but just using the front irradiance (Gfront). @@ -5487,41 +5482,33 @@ def calc_performance(self, meteo_data, CECMod, cumulativesky, glassglass=False, 'Mismatch': mismatch calculated from the MAD distribution of POA_total 'Pout_raw': power output calculated from POA_total, considers wind speed and temp_amb if in trackerdict. 'Pout': power output considering electrical mismatch - + """ from bifacial_radiance import performance + #TODO: make this operate on the MetObj class, not special dictionary! #TODO: Check that meteo_data only includes correct kwargs # 'dni', 'ghi', 'dhi', 'temp_air', 'wind_speed' if cumulativesky is False: # If CECMod details aren't passed, use a default Prism Solar value. - if CECMod is None: - print("No CECModule data passed; using default for Prism Solar BHC72-400") - #url = 'https://raw.githubusercontent.com/NREL/SAM/patch/deploy/libraries/CEC%20Modules.csv' - url = os.path.join(DATA_PATH,'CEC Modules.csv') - db = pd.read_csv(url, index_col=0) # Reading this might take 1 min or so, the database is big. - modfilter2 = db.index.str.startswith('Pr') & db.index.str.endswith('BHC72-400') - CECMod = db[modfilter2] - - # Search for module object bifaciality - + #if type(module) is not ModuleObj: # not working for some reason.. + if str(type(module)) != "": + raise TypeError('ModuleObj input required for AnalysisObj.calculatePerformance. '+\ + f'type passed: {type(module)}') - self.power_data = performance.calculateResults(CECMod=CECMod, results=self.getResults(), - bifacialityfactor=bifacialityfactor, - CECMod2=CECMod2, agriPV=agriPV, - **meteo_data) + self.power_data = performance.calculatePerformance(module=module, results=self.results, + CECMod2=CECMod2, **meteo_data) else: # TODO HERE: SUM all keys for rows that have the same rowWanted/modWanted - self.power_data = performance.calculateResultsGencumsky1axis(results=self.getResults(), - agriPV=agriPV) + self.power_data = performance.calculatePerformanceGencumsky(results=self.results) #results.to_csv(os.path.join('results', 'Cumulative_Results.csv')) - #CompiledResults = results + #compiledResults = results #trackerdict = trackerdict def quickExample(testfolder=None): diff --git a/bifacial_radiance/modelchain.py b/bifacial_radiance/modelchain.py index 36fbc12f..381035f8 100644 --- a/bifacial_radiance/modelchain.py +++ b/bifacial_radiance/modelchain.py @@ -44,6 +44,7 @@ def runModelChain(simulationParamsDict, sceneParamsDict, timeControlParamsDict=N import bifacial_radiance import os import numpy as np + import pandas as pd print("\nNew bifacial_radiance simulation starting. ") print("Version: ", bifacial_radiance.__version__) @@ -147,6 +148,10 @@ def runModelChain(simulationParamsDict, sceneParamsDict, timeControlParamsDict=N omegaParams=omegaParamsDict, cellModule=cellModule, **kwargs) + # module CEC params + if CECModParamsDict: + module.addCEC(pd.DataFrame(CECModParamsDict, index=[0])) + customObject = None if "customObject" in sceneParamsDict: @@ -236,14 +241,7 @@ def runModelChain(simulationParamsDict, sceneParamsDict, timeControlParamsDict=N print("\n--> Calculating Performance values") - #CEC Module - import pandas as pd - - if CECModParamsDict: - CECMod = pd.DataFrame(CECModParamsDict, index=[0]) - else: - CECMod = None - demo.calculateResults(CECMod = CECMod) + demo.calculatePerformance1axis() demo.exportTrackerDict(savefile=os.path.join('results','Final_Results.csv'),reindex=False) # Save example image files diff --git a/bifacial_radiance/module.py b/bifacial_radiance/module.py index 608f1b86..11e52246 100644 --- a/bifacial_radiance/module.py +++ b/bifacial_radiance/module.py @@ -7,6 +7,8 @@ """ import os import numpy as np +import pvlib +import pandas as pd from bifacial_radiance.main import _missingKeyWarning, _popen, DATA_PATH @@ -28,22 +30,21 @@ class ModuleObj(SuperClass): Pass this object into makeScene or makeScene1axis. """ - + def __repr__(self): return str(type(self)) + ' : ' + str(self.getDataDict()) def __init__(self, name=None, x=None, y=None, z=None, bifi=1, modulefile=None, text=None, customtext='', customObject='', xgap=0.01, ygap=0.0, zgap=0.1, numpanels=1, rewriteModulefile=True, cellModule=None, glass=False, modulematerial='black', tubeParams=None, - frameParams=None, omegaParams=None, hpc=False, Efficiency=None, - Temp_coeff=None, Peak_Power=None, Module_name=None): + frameParams=None, omegaParams=None, CECMod=None, hpc=False): """ Add module details to the .JSON module config file module.json. Module definitions assume that the module .rad file is defined with zero tilt, centered along the x-axis and y-axis for the center of rotation of the module (+X/2, -X/2, +Y/2, -Y/2 on each side). Tip: to define a module that is in 'portrait' mode, y > x. - + Parameters ------------ name : str @@ -53,7 +54,7 @@ def __init__(self, name=None, x=None, y=None, z=None, bifi=1, modulefile=None, y : numeric Length of module (meters) bifi : numeric - Bifaciality of the panel (not currently used). Between 0 (monofacial) + Bifaciality of the panel (used for calculatePerformance). Between 0 (monofacial) and 1, default 1. modulefile : str Existing radfile location in \objects. Otherwise a default value is used @@ -66,7 +67,7 @@ def __init__(self, name=None, x=None, y=None, z=None, bifi=1, modulefile=None, generated module (unlike "text"), but adds to it at the end. customObject : str Append to the module object file a pre-genereated radfile. This - must start with the file path\name. Does not overwrite + must start with the file path name. Does not overwrite generated module (unlike "text"), but adds to it at the end. It automatically inserts radiance's text before the object name so its inserted into scene properly ('!xform -rz 0') @@ -97,29 +98,20 @@ def __init__(self, name=None, x=None, y=None, z=None, bifi=1, modulefile=None, omegaParams : dict Dictionary with input parameters for creating a omega or module support structure. Shortcut for ModuleObj.addOmega() + CECMod : Dictionary with performance parameters needed for self.calculatePerformance() + lpha_sc, a_ref, I_L_ref, I_o_ref, R_sh_ref, R_s, Adjust hpc : bool (default False) Set up module in HPC mode. Namely turn off read/write to module.json and just pass along the details in the module object. Note that calling e.g. addTorquetube() after this will tend to write to the module.json so pass all geometry parameters at once in to makeModule for best response. - Efficiency : float (default None) - Information about module efficiency in percentage. Not currently - used to calculate performance. - Temp_coeff : float (default None) - Information about module temperature coefficient in %. Not - currently used to calculate performance. - Peak_Power : float (default None) - Information about module Peak Power in Watts. Not currently used to - calculate performance. - Module name : string (default None) - Information about module's name. - - '""" - + + """ + self.keys = ['x', 'y', 'z', 'modulematerial', 'scenex','sceney', 'scenez','numpanels','bifi','text','modulefile', 'glass', - 'offsetfromaxis','xgap','ygap','zgap' ] + 'offsetfromaxis','xgap','ygap','zgap'] #replace whitespace with underlines. what about \n and other weird characters? # TODO: Address above comment? @@ -128,6 +120,7 @@ def __init__(self, name=None, x=None, y=None, z=None, bifi=1, modulefile=None, self.customObject = customObject self._manual_text = text + """ if Efficiency is not None: self.Efficiency = Efficiency if Temp_coeff is not None: @@ -136,6 +129,7 @@ def __init__(self, name=None, x=None, y=None, z=None, bifi=1, modulefile=None, self.Peak_Power = Peak_Power if Module_name is not None: self.Module_name = Module_name + """ # are we writing to JSON with passed data or just reading existing? if (x is None) & (y is None) & (cellModule is None) & (text is None): @@ -177,6 +171,8 @@ def __init__(self, name=None, x=None, y=None, z=None, bifi=1, modulefile=None, if cellModule: self.addCellModule(**cellModule, recompile=False) + self.addCEC(CECMod, glass, bifi=bifi) + if self._manual_text: print('Warning: Module text manually passed and not ' f'generated: {self._manual_text}') @@ -218,7 +214,10 @@ def compileText(self, rewriteModulefile=True, json=True): if hasattr(self,'omega'): saveDict = {**saveDict, 'omegaParams':self.omega.getDataDict()} if hasattr(self,'frame'): - saveDict = {**saveDict, 'frameParams':self.frame.getDataDict()} + saveDict = {**saveDict, 'frameParams':self.frame.getDataDict()} + if getattr(self, 'CECMod', None) is not None: + saveDict = {**saveDict, 'CECMod':self.CECMod.getDataDict()} + self._makeModuleFromDict(**saveDict) #write JSON data out and write radfile if it doesn't exist @@ -280,7 +279,9 @@ def readModule(self, name=None): if moduleDict.get('omegaParams'): self.addOmega(**moduleDict['omegaParams'], recompile=False) if moduleDict.get('frameParams'): - self.addFrame(**moduleDict['frameParams'], recompile=False) + self.addFrame(**moduleDict['frameParams'], recompile=False) + if moduleDict.get('CECMod'): + self.addCEC(moduleDict['CECMod'], moduleDict['glass']) return moduleDict @@ -692,7 +693,159 @@ def _makeModuleFromDict(self, x=None, y=None, z=None, xgap=None, ygap=None, self.text = text return text #End of makeModuleFromDict() - + + def addCEC(self, CECMod, glassglass=None, bifi=None): + """ + + + Parameters + ---------- + CECMod : Dictionary or pandas.DataFrame including: + alpha_sc, a_ref, I_L_ref, I_o_ref, R_sh_ref, R_s, Adjust + glassglass : Bool, optional. Create a glass-glass module w 5mm glass on each side + bifi : Float, bifaciality coefficient < 1 + + """ + keys = ['alpha_sc', 'a_ref', 'I_L_ref', 'I_o_ref', 'R_sh_ref', 'R_s', 'Adjust'] + + if glassglass is not None: + self.glassglass = glassglass + if bifi: + self.bifi = bifi + + + if type(CECMod) == pd.DataFrame: + # Check for attributes along the index and transpose + if 'alpha_sc' in CECMod.index: + CECMod = CECMod.T + if len(CECMod) > 1: + print('Warning: DataFrame with multiple rows passed to module.addCEC. '\ + 'Taking the first entry') + CECModDict = CECMod.iloc[0].to_dict() + try: + CECModDict['name'] = CECMod.iloc[0].name + except AttributeError: + CECModDict['name'] = None + elif type(CECMod) == pd.Series: + CECModDict = CECMod.to_dict() + elif type(CECMod) == dict: + CECModDict = CECMod + elif type(CECMod) == str: + raise Exception('Error: string-based module selection is not yet enabled. '\ + 'Try back later!') + return + elif CECMod is None: + self.CECMod = None + return + else: + raise Exception(f"Unrecognized type '{type(CECMod)}' passed into addCEC ") + + for key in keys: + if key not in CECModDict: + raise KeyError(f"Error: required key '{key}' not passed into module.addCEC") + + + self.CECMod = CECModule(**CECModDict) + + + + def calculatePerformance(self, effective_irradiance, CECMod=None, + temp_air=None, wind_speed=1, temp_cell=None, glassglass=None): + ''' + The module parameters are given at the reference condition. + Use pvlib.pvsystem.calcparams_cec() to generate the five SDM + parameters at your desired irradiance and temperature to use + with pvlib.pvsystem.singlediode() to calculate the IV curve information.: + + Inputs + ------ + effective_irradiance : numeric + Dataframe or single value. Must be same length as temp_cell + CECMod : Dict + Dictionary with CEC Module PArameters for the module selected. Must + contain at minimum alpha_sc, a_ref, I_L_ref, I_o_ref, R_sh_ref, + R_s, Adjust + temp_air : numeric + Ambient temperature in Celsius. Dataframe or single value to calculate. + Must be same length as effective_irradiance. Default = 20C + wind_speed : numeric + Wind speed at a height of 10 meters [m/s]. Default = 1 m/s + temp_cell : numeric + Back of module temperature. If provided, overrides temp_air and + wind_speed calculation. Default = None + glassglass : boolean + If module is glass-glass package (vs glass-polymer) to select correct + thermal coefficients for module temperature calculation + + ''' + + if CECMod is None: + if getattr(self, 'CECMod', None) is not None: + CECMod = self.CECMod + else: + print("No CECModule data passed; using default for Prism Solar BHC72-400") + #url = 'https://raw.githubusercontent.com/NREL/SAM/patch/deploy/libraries/CEC%20Modules.csv' + url = os.path.join(DATA_PATH,'CEC Modules.csv') + db = pd.read_csv(url, index_col=0) # Reading this might take 1 min or so, the database is big. + modfilter2 = db.index.str.startswith('Pr') & db.index.str.endswith('BHC72-400') + CECMod = db[modfilter2] + self.addCEC(CECMod) + + if hasattr(self, 'glassglass') and glassglass is None: + glassglass = self.glassglass + + from pvlib.temperature import TEMPERATURE_MODEL_PARAMETERS + + # Setting temperature_model_parameters + if glassglass: + temp_model_params = ( + TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_glass']) + else: + temp_model_params = ( + TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_polymer']) + + if temp_cell is None: + if temp_air is None: + temp_air = 25 # STC + + temp_cell = pvlib.temperature.sapm_cell(effective_irradiance, temp_air, + wind_speed, + temp_model_params['a'], + temp_model_params['b'], + temp_model_params['deltaT']) + + if isinstance(CECMod, pd.DataFrame): + #CECMod.to_pickle("CECMod.pkl") + if len(CECMod) == 1: + CECMod = CECMod.iloc[0] + else: + print("More than one Module passed. Error, using 1st one") + CECMod = CECMod.iloc[0] + + + IL, I0, Rs, Rsh, nNsVth = pvlib.pvsystem.calcparams_cec( + effective_irradiance=effective_irradiance, + temp_cell=temp_cell, + alpha_sc=float(CECMod.alpha_sc), + a_ref=float(CECMod.a_ref), + I_L_ref=float(CECMod.I_L_ref), + I_o_ref=float(CECMod.I_o_ref), + R_sh_ref=float(CECMod.R_sh_ref), + R_s=float(CECMod.R_s), + Adjust=float(CECMod.Adjust) + ) + + IVcurve_info = pvlib.pvsystem.singlediode( + photocurrent=IL, + saturation_current=I0, + resistance_series=Rs, + resistance_shunt=Rsh, + nNsVth=nNsVth + ) + + return IVcurve_info['p_mp'] + + # end of ModuleObj @@ -1251,6 +1404,29 @@ def _makeCellLevelModule(self, module, z, Ny, ygap, return(text, x, y, _cc) +class CECModule(SuperClass): + + def __init__(self, alpha_sc, a_ref, I_L_ref, I_o_ref, R_sh_ref, R_s, Adjust, **kwargs): + """ + For storing module performance parameters to be fed into pvlib.pvsystem.singlediode() + Required passed parameters: alpha_sc, a_ref, I_L_ref, I_o_ref, R_sh_ref, R_s, Adjust + Usage: pass in **dictionary with at minimum these keys: 'alpha_sc', 'a_ref', 'I_L_ref', + 'I_o_ref', 'R_sh_ref', 'R_s', 'Adjust', 'name' (opt) + + """ + self.keys = ['alpha_sc', 'a_ref', 'I_L_ref', 'I_o_ref', 'R_sh_ref', 'R_s', + 'Adjust', 'name'] + name = kwargs.get('name') + if name is None: + name = kwargs.get('Name') + + + # set data object attributes from datakey list. + for key in self.keys: + setattr(self, key, eval(key)) + + + # deal with Int32 JSON incompatibility # https://www.programmerall.com/article/57461489186/ import json diff --git a/bifacial_radiance/performance.py b/bifacial_radiance/performance.py index 90c5b0e3..e0a378e9 100644 --- a/bifacial_radiance/performance.py +++ b/bifacial_radiance/performance.py @@ -7,85 +7,7 @@ import pvlib import pandas as pd - -def calculatePerformance(effective_irradiance, CECMod, temp_air=None, - wind_speed=1, temp_cell=None, glassglass=False): - ''' - The module parameters are given at the reference condition. - Use pvlib.pvsystem.calcparams_cec() to generate the five SDM - parameters at your desired irradiance and temperature to use - with pvlib.pvsystem.singlediode() to calculate the IV curve information.: - - Inputs - ------ - effective_irradiance : numeric - Dataframe or single value. Must be same length as temp_cell - CECMod : Dict - Dictionary with CEC Module Parameters for the module selected. Must - contain at minimum alpha_sc, a_ref, I_L_ref, I_o_ref, R_sh_ref, - R_s, Adjust - temp_air : numeric - Ambient temperature in Celsius. Dataframe or single value to calculate. - Must be same length as effective_irradiance. Default = 20C - wind_speed : numeric - Wind speed at a height of 10 meters [m/s]. Default = 1 m/s - temp_cell : numeric - Back of module temperature. If provided, overrides temp_air and - wind_speed calculation. Default = None - glassglass : boolean - If module is glass-glass package (vs glass-polymer) to select correct - thermal coefficients for module temperature calculation - - ''' - - from pvlib.temperature import TEMPERATURE_MODEL_PARAMETERS - - # Setting temperature_model_parameters - if glassglass: - temp_model_params = ( - TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_glass']) - else: - temp_model_params = ( - TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_polymer']) - - if temp_cell is None: - if temp_air is None: - temp_air = 25 # STC - - temp_cell = pvlib.temperature.sapm_cell(effective_irradiance, temp_air, wind_speed, - temp_model_params['a'], temp_model_params['b'], temp_model_params['deltaT']) - - if isinstance(CECMod, pd.DataFrame): - #CECMod.to_pickle("CECMod.pkl") - if len(CECMod) == 1: - CECMod1 = CECMod.iloc[0] - else: - print("More than one Module passed. Error, using 1st one") - CECMod1 = CECMod.iloc[0] - else: - CECMod1 = CECMod - - IL, I0, Rs, Rsh, nNsVth = pvlib.pvsystem.calcparams_cec( - effective_irradiance=effective_irradiance, - temp_cell=temp_cell, - alpha_sc=float(CECMod1.alpha_sc), - a_ref=float(CECMod1.a_ref), - I_L_ref=float(CECMod1.I_L_ref), - I_o_ref=float(CECMod1.I_o_ref), - R_sh_ref=float(CECMod1.R_sh_ref), - R_s=float(CECMod1.R_s), - Adjust=float(CECMod1.Adjust) - ) - - IVcurve_info = pvlib.pvsystem.singlediode( - photocurrent=IL, - saturation_current=I0, - resistance_series=Rs, - resistance_shunt=Rsh, - nNsVth=nNsVth - ) - - return IVcurve_info['p_mp'] +import numpy as np def MBD(meas, model): @@ -109,7 +31,7 @@ def MBD(meas, model): data. """ - import pandas as pd + df = pd.DataFrame({'model': model, 'meas': meas}) # rudimentary filtering of modeled irradiance df = df.dropna() @@ -142,8 +64,8 @@ def RMSE(meas, model): """ - import numpy as np - import pandas as pd + + df = pd.DataFrame({'model': model, 'meas': meas}) df = df.dropna() minirr = meas.min() @@ -176,7 +98,6 @@ def MBD_abs(meas, model): """ - import pandas as pd df = pd.DataFrame({'model': model, 'meas': meas}) # rudimentary filtering of modeled irradiance df = df.dropna() @@ -210,8 +131,7 @@ def RMSE_abs(meas, model): """ # - import numpy as np - import pandas as pd + df = pd.DataFrame({'model': model, 'meas': meas}) df = df.dropna() minirr = meas.min() @@ -222,48 +142,63 @@ def RMSE_abs(meas, model): def _cleanDataFrameResults(mattype, rearMat, Wm2Front, Wm2Back, - fillcleanedSensors=False, agriPV=False): - - import numpy as np - - if agriPV: + fillcleanedSensors=False): + + # 2D array of values. if a row of rearMat is nan then it's single-sided and + # agriPV is true for that row + _matchAgriPV = ['sky', 'pole', 'tube', 'bar', '3267', '1540', '1540'] + _match = ['sky', 'pole', 'tube', 'bar', 'ground', '3267', '1540'] + matcharray = np.row_stack([_matchAgriPV if row.isna().all() else _match + for (n,row) in rearMat.iterrows()]) + """ + if Wm2Front.size != Wm2Back.size: + agriPV = True matchers = ['sky', 'pole', 'tube', 'bar', '3267', '1540'] else: + agriPV = False matchers = ['sky', 'pole', 'tube', 'bar', 'ground', '3267', '1540'] + """ - maskfront = np.column_stack([mattype[col].str.contains('|'.join(matchers), - na=False) for col in - mattype]) + maskfront = np.row_stack([row.str.contains('|'.join(matcharray[index]), + na=False) for (index, row) in + mattype.iterrows()]) + Wm2Front[maskfront] = np.nan - maskback = np.column_stack([rearMat[col].str.contains('|'.join(matchers), - na=False) for col in - rearMat]) - Wm2Back[maskback] = np.nan + try: + maskback = np.row_stack([row.str.contains('|'.join(matcharray[index]), + na=False) for (index, row) in + rearMat.iterrows()]) + Wm2Back[maskback] = np.nan + except AttributeError: # rearMat is empty + pass # Filling Nans... filledFront = Wm2Front.mean(axis=1) if fillcleanedSensors: - filledBack = Wm2Back.copy().interpolate() + frontcopy = Wm2Front.copy().interpolate(axis=1) + filledBack = Wm2Back.copy().interpolate(axis=1) else: + frontcopy = Wm2Front.copy() filledBack = Wm2Back.copy() # interpolate() - return filledFront, filledBack + return filledFront, filledBack, frontcopy -def calculateResults(CECMod, csvfile=None, results=None, +def calculatePerformance(module, csvfile=None, results=None, temp_air=None, wind_speed=1, temp_cell=None, - glassglass=False, bifacialityfactor=1.0, CECMod2=None, - fillcleanedSensors=False, agriPV=False, **kwargs): + CECMod2=None, + fillcleanedSensors=False, **kwargs): ''' - Calculate Performance and Mismatch for timestamped data. This routine + Calculate Performance and Mismatch for timestamped data. This routine requires + CECMod details to have been set with the module using ModuleObj.addCEC. Parameters ---------- - CECMod : dict - CEC Module data as dictionary + module : bifacial_radiance.module.ModuleObj + module object with CEC Module data as dictionary csvfile : numeric list Compiled Results data results : numeric list @@ -275,18 +210,11 @@ def calculateResults(CECMod, csvfile=None, results=None, temp_cell : value or list Cell temperature for calculating module performance. If none, module temperature is calculated using temp_air and wind_speed - glassglass : Bool - PAckaging of the module, used when calculating module temperature - bifacialityfactor : float - Bifaciality factor, used for calculating of effective rear irradiance - and subsequently module performance - CECMod2 : dict + CECMod2 : dict, optional CEC Module data as dictionary, for a monofacial module to be used as comparison for Bifacial Gain in Energy using only the calculated front Irradiance. If none, same module as CECMod is used. - agriPV : Bool - Turns off cleaning for ground material Returns ------- @@ -311,22 +239,22 @@ def calculateResults(CECMod, csvfile=None, results=None, from bifacial_radiance import mismatch - import pandas as pd - - if CECMod2 is None: - CECMod2 = CECMod - dfst = pd.DataFrame() if csvfile is not None: data = pd.read_csv(csvfile) Wm2Front = data['Wm2Front'].str.strip( '[]').str.split(',', expand=True).astype(float) - Wm2Back = data['Wm2Back'].str.strip( - '[]').str.split(',', expand=True).astype(float) mattype = data['mattype'].str.strip('[]').str.split(',', expand=True) - rearMat = data['rearMat'].str.strip('[]').str.split(',', expand=True) - + if 'Wm2Back' in data: + Wm2Back = data['Wm2Back'].str.strip( + '[]').str.split(',', expand=True).astype(float) + else: + Wm2Back = pd.DataFrame([0]) + if 'rearMat' in data: + rearMat = data['rearMat'].str.strip('[]').str.split(',', expand=True) + else: + rearMat = pd.DataFrame([np.nan]) if 'timestamp' in data: dfst['timestamp'] = data['timestamp'] if 'modNum' in data: @@ -337,14 +265,21 @@ def calculateResults(CECMod, csvfile=None, results=None, dfst['sceneNum'] = data['sceneNum'] else: if results is not None: + results = results.loc[:,~results.columns.duplicated()] Wm2Front = pd.DataFrame.from_dict(dict(zip( results.index, results['Wm2Front']))).T - Wm2Back = pd.DataFrame.from_dict(dict(zip( - results.index, results['Wm2Back']))).T mattype = pd.DataFrame.from_dict(dict(zip( results.index, results['mattype']))).T - rearMat = pd.DataFrame.from_dict(dict(zip( - results.index, results['rearMat']))).T + if 'Wm2Back' in results: + Wm2Back = pd.DataFrame.from_dict(dict(zip( + results.index, results['Wm2Back']))).T + else: + Wm2Back = pd.DataFrame([0]) + if 'rearMat' in results: + rearMat = pd.DataFrame.from_dict(dict(zip( + results.index, results['rearMat']))).T + else: + rearMat = pd.DataFrame([np.nan]) if 'timestamp' in results: dfst['timestamp'] = results['timestamp'] @@ -359,35 +294,32 @@ def calculateResults(CECMod, csvfile=None, results=None, print("Data or file not passed. Ending arrayResults") return - filledFront, filledBack = _cleanDataFrameResults( + filledFront, filledBack, frontcopy = _cleanDataFrameResults( mattype, rearMat, Wm2Front, Wm2Back, - fillcleanedSensors=fillcleanedSensors, agriPV=agriPV) + fillcleanedSensors=fillcleanedSensors) - POA = filledBack.apply(lambda x: x*bifacialityfactor + filledFront) + POA = filledBack.apply(lambda x: x*module.bifi + filledFront) # Statistics Calculations - # dfst['MAD/G_Total'] = bifacial_radiance.mismatch.mad_fn(POA.T) - # 'MAD/G_Total + dfst['POA_eff'] = POA.mean(axis=1) - dfst['Grear_mean'] = Wm2Back.mean(axis=1) + dfst['Gfront_mean'] = Wm2Front.mean(axis=1) - # dfst['MAD/G_Total**2'] = dfst['MAD/G_Total']**2 - # dfst['stdev'] = POA.std(axis=1)/ dfst['poat'] - - dfst['Pout_raw'] = calculatePerformance( - effective_irradiance=dfst['POA_eff'], CECMod=CECMod, - temp_air=temp_air, wind_speed=wind_speed, temp_cell=temp_cell, - glassglass=glassglass) - dfst['Pout_Gfront'] = calculatePerformance( - effective_irradiance=dfst['Gfront_mean'], CECMod=CECMod2, - temp_air=temp_air, wind_speed=wind_speed, temp_cell=temp_cell, - glassglass=glassglass) - dfst['BGG'] = dfst['Grear_mean']*100*bifacialityfactor/dfst['Gfront_mean'] - dfst['BGE'] = ((dfst['Pout_raw'] - dfst['Pout_Gfront']) * 100 / - dfst['Pout_Gfront']) - dfst['Mismatch'] = mismatch.mismatch_fit3(POA.T) - dfst['Pout'] = dfst['Pout_raw']*(1-dfst['Mismatch']/100) + if len(rearMat) > 0: + dfst['Grear_mean'] = Wm2Back.mean(axis=1) + dfst['Pout_raw'] = module.calculatePerformance( + effective_irradiance=dfst['POA_eff'], + temp_air=temp_air, wind_speed=wind_speed, temp_cell=temp_cell) + dfst['Pout_Gfront'] = module.calculatePerformance( + effective_irradiance=dfst['Gfront_mean'], CECMod=CECMod2, + temp_air=temp_air, wind_speed=wind_speed, temp_cell=temp_cell) + dfst['BGG'] = dfst['Grear_mean']*100*module.bifi/dfst['Gfront_mean'] + dfst['BGE'] = ((dfst['Pout_raw'] - dfst['Pout_Gfront']) * 100 / + dfst['Pout_Gfront']) + dfst['Mismatch'] = mismatch.mismatch_fit2(POA.T) # value in percentage [%] + dfst['Pout'] = dfst['Pout_raw']*(1-dfst['Mismatch']/100) + dfst['Wind Speed'] = wind_speed if "dni" in kwargs: dfst['DNI'] = kwargs['dni'] @@ -400,9 +332,9 @@ def calculateResults(CECMod, csvfile=None, results=None, return dfst -def calculateResultsGencumsky1axis(csvfile=None, results=None, +def calculatePerformanceGencumsky(csvfile=None, results=None, bifacialityfactor=1.0, - fillcleanedSensors=True, agriPV=False): + fillcleanedSensors=False): ''' Compile calculate results for cumulative 1 axis tracking routine @@ -429,40 +361,46 @@ def calculateResultsGencumsky1axis(csvfile=None, results=None, ''' import pandas as pd + + def _dict2DF(df, key): + return pd.DataFrame(dict([ (k,pd.Series(v)) + for k,v in dict(zip(df.index, df[key])).items() ])).T dfst = pd.DataFrame() if csvfile is not None: - data = pd.read_csv(csvfile) - Wm2Front = data['Wm2Front' + results = pd.read_csv(csvfile) + Wm2Front = results['Wm2Front' ].str.strip('[]').str.split(',', expand=True).astype(float) - Wm2Back = data['Wm2Back' + Wm2Back = results['Wm2Back' ].str.strip('[]').str.split(',', expand=True).astype(float) - mattype = data['mattype' + mattype = results['mattype' ].str.strip('[]').str.split(',', expand=True) - rearMat = data['rearMat' + rearMat = results['rearMat' ].str.strip('[]').str.split(',', expand=True) - if 'modNum' in data: - dfst['module'] = data['modNum'] - if 'rowNum' in data: - dfst['row'] = data['rowNum'] - if 'sceneNum' in data: - dfst['sceneNum'] = data['sceneNum'] + if 'modNum' in results: + dfst['module'] = results['modNum'] + if 'rowNum' in results: + dfst['row'] = results['rowNum'] + if 'sceneNum' in results: + dfst['sceneNum'] = results['sceneNum'] else: if results is not None: - Wm2Front = pd.DataFrame.from_dict(dict(zip( - results.index, results['Wm2Front']))).T - Wm2Back = pd.DataFrame.from_dict(dict(zip( - results.index, results['Wm2Back']))).T - mattype = pd.DataFrame.from_dict(dict(zip( - results.index, results['mattype']))).T - rearMat = pd.DataFrame.from_dict(dict(zip( - results.index, results['rearMat']))).T + Wm2Front = _dict2DF(results, 'Wm2Front') + mattype = _dict2DF(results, 'mattype') + if 'Wm2Back' in results: + Wm2Back = _dict2DF(results,'Wm2Back') + else: + Wm2Back = pd.DataFrame([0]) + if 'rearMat' in results: + rearMat = _dict2DF(results, 'rearMat') + else: + rearMat = pd.DataFrame(None) if 'modNum' in results: dfst['module'] = results['modNum'] @@ -470,54 +408,59 @@ def calculateResultsGencumsky1axis(csvfile=None, results=None, dfst['row'] = results['rowNum'] if 'sceneNum' in results: dfst['sceneNum'] = results['sceneNum'] - else: - print("Data or file not passed. Ending calculateResults") + print("Data or file not passed. Ending calculatePerformanceGencumsky") return # Data gets cleaned but need to maintain same number of sensors # due to adding for the various tracker angles. - filledFront, filledBack = _cleanDataFrameResults( + filledFront, filledBack, frontcopy = _cleanDataFrameResults( mattype, rearMat, Wm2Front, Wm2Back, - fillcleanedSensors=fillcleanedSensors, agriPV=agriPV) + fillcleanedSensors=fillcleanedSensors) cumFront = [] + cumWM2 = [] cumBack = [] cumRow = [] cumMod = [] + cumScene = [] Grear_mean = [] # Gfront_mean = [] POA_eff = [] # NOTE change 26.07.22 'row' -> 'rowNum' and 'mod' -> 'ModNumber # NOTE change March 13 2024 ModNumber -> modNum - for rownum in results['rowNum'].unique(): - for modnum in results['modNum'].unique(): - mask = (results['rowNum'] == rownum) & ( - results['modNum'] == modnum) - cumBack.append(list(filledBack[mask].sum(axis=0))) - cumFront.append(filledFront[mask].sum(axis=0)) - cumRow.append(rownum) - cumMod.append(modnum) - - # Maybe this would be faster by first doing the DF with the above, - # exploding the column and calculating. - POA_eff.append(list( - (filledBack[mask].apply(lambda x: x*bifacialityfactor + - filledFront[mask])).sum(axis=0))) - Grear_mean.append(filledBack[mask].sum(axis=0).mean()) - # Gfront_mean.append(filledFront[mask].sum(axis=0).mean()) - - dfst = pd.DataFrame(zip(cumRow, cumMod, cumFront, cumBack, Grear_mean, - POA_eff), columns=('row', 'module', 'Gfront_mean', - 'Wm2Back', 'Grear_mean', + #for rownum in results['rowNum'].unique(): + # for modnum in results['modNum'].unique(): + for i, df in results.groupby(['rowNum','modNum','sceneNum']): + #mask = (results['rowNum'] == rownum) & ( + # results['modNum'] == modnum) + cumBack.append(list(filledBack.loc[df.index].sum(axis=0, min_count=1))) + cumFront.append(filledFront.loc[df.index].sum(axis=0, min_count=1)) + cumWM2.append(list(frontcopy.loc[df.index].sum(axis=0, min_count=1))) + cumRow.append(i[0]) + cumMod.append(i[1]) + cumScene.append(i[2]) + + + # Maybe this would be faster by first doing the DF with the above, + # exploding the column and calculating. + POA_eff.append(list( + (filledBack.loc[df.index].apply(lambda x: x*bifacialityfactor + + filledFront.loc[df.index])).sum(axis=0))) + Grear_mean.append(filledBack.loc[df.index].sum(axis=0, min_count=1).mean()) + # Gfront_mean.append(filledFront[mask].sum(axis=0).mean()) + + dfst = pd.DataFrame(zip(cumRow, cumMod, cumScene, cumFront, cumWM2, cumBack, Grear_mean, + POA_eff), columns=('rowNum', 'modNum','sceneNum', 'Gfront_mean', + 'Wm2Front', 'Wm2Back', 'Grear_mean', 'POA_eff')) dfst['BGG'] = dfst['Grear_mean']*100*bifacialityfactor/dfst['Gfront_mean'] # Reordering columns - cols = ['row', 'module', 'BGG', 'Gfront_mean', 'Grear_mean', 'POA_eff', - 'Wm2Back'] + cols = ['rowNum', 'modNum','sceneNum', 'BGG', 'Gfront_mean', 'Grear_mean', 'POA_eff', + 'Wm2Front','Wm2Back'] dfst = dfst[cols] return dfst diff --git a/docs/sphinx/source/manualapi.rst b/docs/sphinx/source/manualapi.rst index 50dfe828..88e874f7 100644 --- a/docs/sphinx/source/manualapi.rst +++ b/docs/sphinx/source/manualapi.rst @@ -74,6 +74,7 @@ Functions and methods to generate modules :toctree: generated/ :caption: Modules + ModuleObj.__init__ RadianceObj.makeModule ModuleObj.addTorquetube ModuleObj.addCellModule @@ -123,7 +124,7 @@ Methods for irradiance calculations AnalysisObj.moduleAnalysis AnalysisObj.analysis RadianceObj.analysis1axis - RadianceObj.getResults + RadianceObj.results Power and Mismatch ------------------ @@ -132,8 +133,10 @@ Power and Mismatch :toctree: generated/ :caption: Power and Mismatch Analysis - AnalysisObj.calc_performance - mismatch.analysisIrradianceandPowerMismatch + RadianceObj.calculatePerformance1axis + AnalysisObj.calculatePerformance + ModuleObj.addCEC + mismatch.mismatch_fit2 AgriPV Ground Scans ------------------- diff --git a/docs/sphinx/source/whatsnew/v0.4.3.rst b/docs/sphinx/source/whatsnew/v0.4.3.rst index 24ac8e03..10a41452 100644 --- a/docs/sphinx/source/whatsnew/v0.4.3.rst +++ b/docs/sphinx/source/whatsnew/v0.4.3.rst @@ -1,15 +1,15 @@ .. _whatsnew_0430: -v0.4.3 (XX / XX / 2023) +v0.4.3 (08 / 27 / 2024) ------------------------ Bugfix Release ... API Changes ~~~~~~~~~~~~ -*A new function can now be called to compile results and report out final irradiance and performance data: :py:class:`~bifacial_radiance.RadianceObj.compileResults`. -*Multiple modules and rows can now be selected in a single analysis scan. ``modWanted`` and ``rowWanted`` inputs in :py:class:`~bifacial_radiance.RadianceObj.analysis1axis` can now be a list, to select multiple rows and modules for scans. (:issue:`405`)(:pull:`408`) -*To support multiple modules and row scans for 1axis simulations, outputs like Wm2Front are now stored in ``trackerdict``.``Results`` (:issue:`405`)(:pull:`408`) +* A new function can now be called to compile results and report out final irradiance and performance data: :py:class:`~bifacial_radiance.RadianceObj.compileResults`. +* Multiple modules and rows can now be selected in a single analysis scan. ``modWanted`` and ``rowWanted`` inputs in :py:class:`~bifacial_radiance.RadianceObj.analysis1axis` can now be a list, to select multiple rows and modules for scans. (:issue:`405`)(:pull:`408`) +* To support multiple modules and row scans for 1axis simulations, outputs like Wm2Front are now stored in ``trackerdict``.``Results`` (:issue:`405`)(:pull:`408`) * ``mismatch.mad_fn`` has new functionality and input parameter `axis`. If a 2D matrix or dataframe is passed in as data, MAD is calculated along the row (default) or along the columns by passing 'axis=1' * :func:`bifacial_radiance.mismatch.mismatch_fit3` has been deprecated in favour of :func:`bifacial_radiance.mismatch.mismatch_fit2` which has a greater agreement with anual energy yield data (:issue:`520`) diff --git a/docs/sphinx/source/whatsnew/v0.5.0.rst b/docs/sphinx/source/whatsnew/v0.5.0.rst index 945a49c8..691efb65 100644 --- a/docs/sphinx/source/whatsnew/v0.5.0.rst +++ b/docs/sphinx/source/whatsnew/v0.5.0.rst @@ -1,34 +1,6 @@ -.. _whatsnew_0440: - -v0.4.4 (XX / XX / 2024) ------------------------- -Bugfix Release ... - - -API Changes -~~~~~~~~~~~~ -* - -Enhancements -~~~~~~~~~~~~ -* Conduct an automated check for proper radiance RAYPATH setting (:issue:`525`)(:pull:`537`) - - -Bug fixes -~~~~~~~~~ -* versioning with setuptools_scm- set fallback_version to bifirad v0.4.3 to prevent crashes if git is not present (:issue:`535`)(:pull:`539`) - -Documentation -~~~~~~~~~~~~~~ -* No longer provide a warning message when both `hub_height` and `clearance_height` are passed to :py:class:`~bifacial_radiance.AnalysisObj.moduleAnalysis` (:pull:`540`) - -Contributors -~~~~~~~~~~~~ -* Silvana Ayala (:ghuser:`shirubana`) -* Chris Deline (:ghuser:`cdeline`) .. _whatsnew_050: -v0.5.0 (4 / XX / 2024) +v0.5.0 (XX / XX / 2024) ------------------------ Bugfix Release ... @@ -37,11 +9,11 @@ Deprecations * :py:class:`~bifacial_radiance.RadianceObj.appendtoScene` is deprecated in favor of :py:class:`~bifacial_radiance.SceneObj.appendtoScene` * :py:class:`~bifacial_radiance.RadianceObj.makeScene`.`appendtoScene` is deprecated in favor of :py:class:`~bifacial_radiance.makeScene`.`customtext` * :py:class:`~bifacial_radiance.RadianceObj.makeScene1axis`.`appendtoScene` is deprecated in favor of :py:class:`~bifacial_radiance.makeScene1axis`.`customtext` -* Results `Wm2Front` and `Wm2Back` no longer tracked in top-level :py:class:`~bifacial_radiance.RadianceObj` object. Results are now tracked in :py:class:`~bifacial_radiance.AnalysisObj` +* `Wm2Front` and `Wm2Back` are no longer attributes of :py:class:`~bifacial_radiance.RadianceObj` object. Results are now tracked in :py:class:`~bifacial_radiance.AnalysisObj.results` and :py:class:`~bifacial_radiance.RadianceObj.results` for trackerDict simulations. API Changes ~~~~~~~~~~~~ -* A new function can now be called to compile results and report out final irradiance and performance data: :py:class:`~bifacial_radiance.RadianceObj.getResults`. +* Final irradiance and performance data are stored in: :py:class:`~bifacial_radiance.AnalysisObj.results` and :py:class:`~bifacial_radiance.RadianceObj.results`. * Results generated with the above can be saved with the :py:class:`~bifacial_radiance.RadianceObj.exportTrackerDict`, which saves an Hourly, Monthly and Yearly .csvs in the results folder. * NSRDB weather data can now be loaded using :py:class:`~bifacial_radiance.RadianceObj.NSRDBWeatherData`. * :py:class:`~bifacial_radiance.AnalysisObj.analysis` updated to allow single (front-only) scans in support of AgriPV modeling. Pass `None` to `backscan` for single-sided scan. (:pull:`499`) @@ -56,9 +28,10 @@ Enhancements ~~~~~~~~~~~~ * :py:class:`~bifacial_radiance.RadianceObj` and :py:class:`~bifacial_radiance.GroundObj` and :py:class:`~bifacial_radiance.MetObj` now have `self.columns` and `self.methods` introspection to list data columsn and methods available. (:pull:`495`) * multiple sceneObjects are tracked by the RadianceObj now. New function :py:class:`~bifacial_radiance.RadianceObj.sceneNames` will return the list of scenes being tracked. (:pull:`487`) -* New function :py:class:`~bifacial_radiance.AnalysisObj.calc_performance` to call CEC performance calculation from within the AnalysisObj +* New function :py:class:`~bifacial_radiance.AnalysisObj.calculatePerformance` and :py:class:`~bifacial_radiance.ModuleObj.calculatePerformance` to call CEC performance calculation from within the AnalysisObj +* New function :py:class:`~bifacial_radiance.RadianceObj.calculatePerformance1axis` to call CEC performance calculation for every entry of a trackerdict * :py:class:`~bifacial_radiance.AnalysisObj` has new attribute `power_data` to store CEC performance data -* :py:class:`~bifacial_radiance.AnalysisObj` has new function `getResults` to bundle and return irradiance scan results in dataframe form. +* :py:class:`~bifacial_radiance.AnalysisObj` has new property `results` to bundle and return irradiance scan results in dataframe form. * :py:class:`~bifacial_radiance.AnalysisObj` has new function `groundAnalysis` to run a ground scan under the row-row pitch of the scene to support AgriPV applications. (:pull:`499`) * :py:class:`~bifacial_radiance.RadianceObj` has new function `analysis1axisground` to run a ground scan under the row-row pitch of the scene for 1-axis tracked scenes. (:pull:`499`) * :py:class:`~bifacial_radiance.RadianceObj` has new intermediate function `readWeatherData` which can take raw metdata time series and metadata dict to generate a :py:class:`~bifacial_radiance.MetObj`. Useful for e.g. bringing in raw NSRDB data. (:pull:`496`) diff --git a/docs/tutorials/2 - Single Axis Tracking Yearly Simulation.ipynb b/docs/tutorials/2 - Single Axis Tracking Yearly Simulation.ipynb index 2528ce77..7a7b1952 100644 --- a/docs/tutorials/2 - Single Axis Tracking Yearly Simulation.ipynb +++ b/docs/tutorials/2 - Single Axis Tracking Yearly Simulation.ipynb @@ -862,8 +862,7 @@ "\n", "
\n", "Important: If you have torquetubes and y-gap values, make sure you clean your results.\n", - "
\n", - "" + "\n" ] }, { @@ -1311,7 +1310,7 @@ "metadata": {}, "outputs": [], "source": [ - "res = demo.calculateResults(bifacialityfactor=1.0)" + "res = demo.calculatePerformance1axis()" ] }, { diff --git a/docs/tutorials/2 - Single Axis Tracking Yearly Simulation.py b/docs/tutorials/2 - Single Axis Tracking Yearly Simulation.py index 6b20d009..0e0ae567 100644 --- a/docs/tutorials/2 - Single Axis Tracking Yearly Simulation.py +++ b/docs/tutorials/2 - Single Axis Tracking Yearly Simulation.py @@ -363,7 +363,7 @@ # In[21]: -res = demo.calculateResults(bifacialityfactor=1.0) +res = demo.calculatePerformance1axis() # In[22]: diff --git a/docs/tutorials/21 - Weather to Module Performance.ipynb b/docs/tutorials/21 - Weather to Module Performance.ipynb index 906000da..daea08bc 100644 --- a/docs/tutorials/21 - Weather to Module Performance.ipynb +++ b/docs/tutorials/21 - Weather to Module Performance.ipynb @@ -11,9 +11,9 @@ "output_type": "stream", "text": [ "Working on a Windows 10\n", - "Python version 3.11.7 | packaged by Anaconda, Inc. | (main, Dec 15 2023, 18:05:47) [MSC v.1916 64 bit (AMD64)]\n", - "Pandas version 2.1.4\n", - "bifacial_radiance version 0+untagged.1554.g980a0b9.dirty\n" + "Python version 3.9.13 (main, Aug 25 2022, 23:51:50) [MSC v.1916 64 bit (AMD64)]\n", + "Pandas version 1.5.3\n", + "bifacial_radiance version 0.4.3.dev341+g93e0ec5.d20240830\n" ] } ], @@ -49,7 +49,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Your simulation will be stored in C:\\Users\\mprillim\\sam_dev\\bifacial_radiance\\bifacial_radiance\\TEMP\\Tutorial_21\n" + "Your simulation will be stored in C:\\Users\\cdeline\\Documents\\Python Scripts\\Bifacial_Radiance\\bifacial_radiance\\TEMP\\Tutorial_21\n" ] } ], @@ -76,7 +76,7 @@ { "data": { "text/plain": [ - "'0+untagged.1554.g980a0b9.dirty'" + "'0.4.3.dev341+g93e0ec5.d20240830'" ] }, "execution_count": 3, @@ -93,9 +93,68 @@ "bifacial_radiance.__version__" ] }, + { + "cell_type": "markdown", + "id": "2469642f", + "metadata": {}, + "source": [ + "## Geting a CEC Module to pass into demo.makeModule" + ] + }, { "cell_type": "code", "execution_count": 4, + "id": "1423a9f2", + "metadata": {}, + "outputs": [], + "source": [ + "url = 'https://raw.githubusercontent.com/NREL/SAM/patch/deploy/libraries/CEC%20Modules.csv'\n", + "db = pd.read_csv(url, index_col=0) # Reading this might take 1 min or so, the database is big." + ] + }, + { + "cell_type": "markdown", + "id": "d1715477", + "metadata": {}, + "source": [ + "Find the module that you want. In this case we know it's a SunPower of model SPR-E19-310-COM. \n", + "\n", + "Make sure you select only 1 module from the database -- sometimes there are similar names." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "fd6a686c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[False False False ... False False False]\n", + "1 modules selected. Name of 1st entry: SunPower SPR-E19-310-COM\n" + ] + } + ], + "source": [ + "modfilter2 = db.index.str.startswith('SunPower') & db.index.str.endswith('SPR-E19-310-COM')\n", + "print(modfilter2)\n", + "CECMod = db[modfilter2]\n", + "print(len(CECMod), \" modules selected. Name of 1st entry: \", CECMod.index[0])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d2db8630", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 6, "id": "250eb585", "metadata": {}, "outputs": [ @@ -103,7 +162,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "path = C:\\Users\\mprillim\\sam_dev\\bifacial_radiance\\bifacial_radiance\\TEMP\\Tutorial_21\n", + "path = C:\\Users\\cdeline\\Documents\\Python Scripts\\Bifacial_Radiance\\bifacial_radiance\\TEMP\\Tutorial_21\n", "Getting weather file: USA_VA_Richmond.724010_TMY2.epw\n", " ... OK!\n", "8760 line in WeatherFile. Assuming this is a standard hourly WeatherFile for the year for purposes of saving Gencumulativesky temporary weather files in EPW folder.\n", @@ -112,35 +171,7 @@ "Saving file EPWs\\metdata_temp.csv, # points: 8760\n", "Calculating Sun position for Metdata that is right-labeled with a delta of -30 mins. i.e. 12 is 11:30 sunpos\n", "Loading albedo, 1 value(s), 0.200 avg\n", - "1 nonzero albedo values.\n", - "\n", - "Module Name: test-module\n", - "Module test-module updated in module.json\n", - "Pre-existing .rad file objects\\test-module.rad will be overwritten\n", - "\n", - "Creating ~2 skyfiles. \n", - "Created 2 skyfiles in /skies/\n", - "Warning: input `moduletype` is deprecated. Use kwarg `module` instead\n", - "\n", - "Making ~2 .rad files for gendaylit 1-axis workflow (this takes a minute..)\n", - "2 Radfiles created in /objects/\n", - "\n", - "Making 2 octfiles in root directory.\n", - "Created 1axis_2021-01-13_1100.oct\n", - "Created 1axis_2021-01-13_1200.oct\n", - "Linescan in process: 1axis_2021-01-13_1100_Front\n", - "Linescan in process: 1axis_2021-01-13_1100_Back\n", - "Saved: results\\irr_1axis_2021-01-13_1100.csv\n", - "Index: 2021-01-13_1100. Wm2Front: 255.36876666666663. Wm2Back: 45.574796666666664\n", - "Linescan in process: 1axis_2021-01-13_1200_Front\n", - "Linescan in process: 1axis_2021-01-13_1200_Back\n", - "Saved: results\\irr_1axis_2021-01-13_1200.csv\n", - "Index: 2021-01-13_1200. Wm2Front: 254.47736666666665. Wm2Back: 43.65842333333333\n", - "Saving a cumulative-results file in the main simulation folder.This adds up by sensor location the irradiance over all hours or configurations considered.\n", - "Warning: This file saving routine does not clean results, so if your setup has ygaps, or 2+modules or torque tubes, doing a deeper cleaning and working with the individual results files in the results folder is highly suggested.\n", - "\n", - "Saving Cumulative results\n", - "Saved: cumulative_results_.csv\n" + "1 nonzero albedo values.\n" ] } ], @@ -150,140 +181,211 @@ "demo = bifacial_radiance.RadianceObj('tutorial_21', path = testfolder) # Create a RadianceObj 'object'\n", "weatherfile = demo.getEPW(lat = 37.5, lon = -77.6) # This location corresponds to Richmond, VA.\n", "metdata = demo.readWeatherFile(weatherFile=weatherfile, starttime=starttime, endtime=endtime)\n", - "demo.setGround(0.2)\n", - "mymodule = demo.makeModule(name='test-module', x=1, y=2, bifi=0.9) \n", - "sceneDict = {'tilt': 10, 'azimuth': 180, 'pitch': 5,'hub_height':1.5, 'nMods':3, 'nRows': 2}\n", - "trackerdict = demo.set1axis(metdata = metdata, cumulativesky = False)\n", - "trackerdict = demo.gendaylit1axis()\n", - "trackerdict = demo.makeScene1axis(moduletype = mymodule, sceneDict = sceneDict)\n", - "trackerdict = demo.makeOct1axis()\n", - "trackerdict = demo.analysis1axis(sensorsy=3)\n" + "demo.setGround(0.2)" ] }, { "cell_type": "markdown", - "id": "57a30274", + "id": "84a1a789", "metadata": {}, "source": [ - "## Geting a CEC Module" + "The CEC data should be passed into the ModuleObj, either at time of creation, or sometime before it is passed into makeScene." ] }, { "cell_type": "code", - "execution_count": 5, - "id": "b0d57ebe", + "execution_count": 7, + "id": "9370c9c2", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Module Name: test-module\n", + "Module test-module updated in module.json\n", + "Pre-existing .rad file objects\\test-module.rad will be overwritten\n", + "\n" + ] + } + ], + "source": [ + "mymodule = demo.makeModule(name='test-module', x=1, y=2, bifi=0.9, CECMod=CECMod) " + ] + }, + { + "cell_type": "markdown", + "id": "27d8bd56", + "metadata": {}, + "source": [ + "The same data could instead be passed after the ModuleObj's definition, or at time of performance analysis:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "111bdbd2", "metadata": {}, "outputs": [], "source": [ - "url = 'https://raw.githubusercontent.com/NREL/SAM/patch/deploy/libraries/CEC%20Modules.csv'\n", - "db = pd.read_csv(url, index_col=0) # Reading this might take 1 min or so, the database is big.\n" + "mymodule.addCEC(CECMod)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "1f2e3cd4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Module Name: test\n", + "Module test updated in module.json\n", + "Pre-existing .rad file objects\\test.rad will be overwritten\n", + "\n" + ] + } + ], + "source": [ + "# Let's make a second module, and set it to the default Prism Solar module type\n", + "mymodule2 = demo.makeModule(name='test', x=1, y=2, bifi=0.8, CECMod=None) " ] }, { "cell_type": "markdown", - "id": "686125df", + "id": "3ba61504", "metadata": {}, "source": [ - "Find the module that you want. In this case we know it's a SunPower of model SPR-E19-310-COM. \n", - "\n", - "Make sure you select only 1 module from the database -- sometimes there are similar names." + "We're going to set up two scenes, each with a different module type!" ] }, { "cell_type": "code", - "execution_count": 6, - "id": "dcb5646f", + "execution_count": 10, + "id": "10e35e7c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "[False False False ... False False False]\n", - "1 modules selected. Name of 1st entry: SunPower SPR-E19-310-COM\n" + "Creating ~2 skyfiles. \n", + "Created 2 skyfiles in /skies/\n", + "\n", + "Making ~2 .rad files for gendaylit 1-axis workflow (this takes a minute..)\n", + "2 Radfiles created in /objects/\n" ] } ], "source": [ - "modfilter2 = db.index.str.startswith('SunPower') & db.index.str.endswith('SPR-E19-310-COM')\n", - "print(modfilter2)\n", - "CECMod = db[modfilter2]\n", - "print(len(CECMod), \" modules selected. Name of 1st entry: \", CECMod.index[0])" + "sceneDict = {'tilt': 0, 'azimuth': 180, 'pitch': 5,'hub_height':1.5, 'nMods':5, 'nRows': 2}\n", + "sceneDict2 = {'tilt': 0, 'azimuth': 180, 'pitch': 5,'hub_height':2.5, 'nMods':2, 'nRows': 1, 'originx': -15}\n", + "trackerdict = demo.set1axis(metdata = metdata, cumulativesky = False)\n", + "trackerdict = demo.gendaylit1axis()\n", + "trackerdict = demo.makeScene1axis(trackerdict, module = mymodule, sceneDict = sceneDict)" ] }, { "cell_type": "markdown", - "id": "7ec0639a", + "id": "211eddf7", "metadata": {}, "source": [ - "## Calculating the Performance and Exporting the Results to a CSV" + "Make a second scene with the other module type" ] }, { "cell_type": "code", - "execution_count": 16, - "id": "7bbf6db6", + "execution_count": 11, + "id": "29c4a111", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "{'2021-01-13_1100': {'surf_azm': 90.0, 'surf_tilt': 44.14, 'theta': -44.14, 'ghi': 211, 'dhi': 149, 'temp_air': 4.6, 'wind_speed': 3.8, 'skyfile': 'skies\\\\sky2_37.5_-77.33_2021-01-13_1100.rad', 'radfile': 'objects\\\\1axis2021-01-13_1100__C_0.80359_rtr_5.00000_tilt_44.14000_3modsx2rows_origin0,0.rad', 'scene': {'module': {'x': 1, 'y': 2, 'z': 0.02, 'modulematerial': 'black', 'scenex': 1.01, 'sceney': 2.0, 'scenez': 0.1, 'numpanels': 1, 'bifi': 0.9, 'text': '! genbox black test-module 1 2 0.02 | xform -t -0.5 -1.0 0 -a 1 -t 0 2.0 0', 'modulefile': 'objects\\\\test-module.rad', 'glass': False, 'offsetfromaxis': 0, 'xgap': 0.01, 'ygap': 0.0, 'zgap': 0.1}, 'modulefile': 'objects\\\\test-module.rad', 'hpc': False, 'name': 'Scene0', 'gcr': 0.4, 'text': '!xform -rx 44.14 -t 0 0 1.5 -a 3 -t 1.01 0 0 -a 2 -t 0 5 0 -i 1 -t -1.01 -0.0 0 -rz 90.0 -t 0 0 0 objects\\\\test-module.rad', 'radfiles': 'objects\\\\1axis2021-01-13_1100__C_0.80359_rtr_5.00000_tilt_44.14000_3modsx2rows_origin0,0.rad', 'sceneDict': {'tilt': 0, 'pitch': 5, 'clearance_height': 1.5, 'azimuth': 90.0, 'nMods': 3, 'nRows': 2, 'modulez': 0.02, 'axis_tilt': 0, 'originx': 0, 'originy': 0}}, 'octfile': '1axis_2021-01-13_1100.oct', 'AnalysisObj': {'octfile': '1axis_2021-01-13_1100.oct', 'name': '1axis_2021-01-13_1100', 'hpc': False, 'x': [0.3734448, 0.01462469, -0.3441954], 'y': [2.28669e-17, 8.955042e-19, -2.107589e-17], 'z': [1.166863, 1.51507, 1.863277], 'rearZ': [1.151075, 1.499282, 1.847489], 'mattype': ['a1.0.a0.test-module.6457', 'a1.0.a0.test-module.6457', 'a1.0.a0.test-module.6457'], 'rearMat': ['a1.0.a0.test-module.2310', 'a1.0.a0.test-module.2310', 'a1.0.a0.test-module.2310'], 'Wm2Front': [254.9108, 255.7401, 255.45539999999997], 'Wm2Back': [45.16279, 45.26495, 46.29665], 'Back/FrontRatio': [0.17717026045871553, 0.1769952111725491, 0.1812311220231711], 'backRatio': [0.17717026045871553, 0.1769952111725491, 0.1812311220231711], 'rearX': [0.3581237, -0.000696414, -0.3595166], 'rearY': [2.192875e-17, -4.264306e-20, -2.201404e-17]}, 'Wm2Front': [254.9108, 255.7401, 255.45539999999997], 'Wm2Back': [45.16279, 45.26495, 46.29665], 'backRatio': [0.17717026045871553, 0.1769952111725491, 0.1812311220231711]}, '2021-01-13_1200': {'surf_azm': 90.0, 'surf_tilt': 21.2, 'theta': -21.2, 'ghi': 249, 'dhi': 200, 'temp_air': 6.5, 'wind_speed': 3.9, 'skyfile': 'skies\\\\sky2_37.5_-77.33_2021-01-13_1200.rad', 'radfile': 'objects\\\\1axis2021-01-13_1200__C_1.13838_rtr_5.00000_tilt_21.20000_3modsx2rows_origin0,0.rad', 'scene': {'module': {'x': 1, 'y': 2, 'z': 0.02, 'modulematerial': 'black', 'scenex': 1.01, 'sceney': 2.0, 'scenez': 0.1, 'numpanels': 1, 'bifi': 0.9, 'text': '! genbox black test-module 1 2 0.02 | xform -t -0.5 -1.0 0 -a 1 -t 0 2.0 0', 'modulefile': 'objects\\\\test-module.rad', 'glass': False, 'offsetfromaxis': 0, 'xgap': 0.01, 'ygap': 0.0, 'zgap': 0.1}, 'modulefile': 'objects\\\\test-module.rad', 'hpc': False, 'name': 'Scene0', 'gcr': 0.4, 'text': '!xform -rx 21.2 -t 0 0 1.5 -a 3 -t 1.01 0 0 -a 2 -t 0 5 0 -i 1 -t -1.01 -0.0 0 -rz 90.0 -t 0 0 0 objects\\\\test-module.rad', 'radfiles': 'objects\\\\1axis2021-01-13_1200__C_1.13838_rtr_5.00000_tilt_21.20000_3modsx2rows_origin0,0.rad', 'sceneDict': {'tilt': 21.2, 'pitch': 5, 'clearance_height': 1.1383754299179079, 'azimuth': 90.0, 'nMods': 3, 'nRows': 2, 'modulez': 0.02, 'axis_tilt': 0, 'originx': 0, 'originy': 0}}, 'octfile': '1axis_2021-01-13_1200.oct', 'AnalysisObj': {'octfile': '1axis_2021-01-13_1200.oct', 'name': '1axis_2021-01-13_1200', 'hpc': False, 'x': [0.473756, 0.007594116, -0.4585678], 'y': [2.900919e-17, 4.650055e-19, -2.807918e-17], 'z': [1.338767, 1.519579, 1.700391], 'rearZ': [1.318255, 1.499068, 1.67988], 'mattype': ['a1.0.a0.test-module.6457', 'a1.0.a0.test-module.6457', 'a1.0.a0.test-module.6457'], 'rearMat': ['a1.0.a0.test-module.2310', 'a1.0.a0.test-module.2310', 'a1.0.a0.test-module.2310'], 'Wm2Front': [254.43140000000002, 254.5634, 254.43729999999996], 'Wm2Back': [43.32023999999999, 43.42238, 44.23265], 'Back/FrontRatio': [0.17026227791743498, 0.1705752257581971, 0.17384430724462474], 'backRatio': [0.17026227791743498, 0.1705752257581971, 0.17384430724462474], 'rearX': [0.4658003, -0.0003616246, -0.4665235], 'rearY': [2.852204e-17, -2.214312e-20, -2.856633e-17]}, 'Wm2Front': [254.43140000000002, 254.5634, 254.43729999999996], 'Wm2Back': [43.32023999999999, 43.42238, 44.23265], 'backRatio': [0.17026227791743498, 0.1705752257581971, 0.17384430724462474]}}\n" + "\n", + "Making ~2 .rad files for gendaylit 1-axis workflow (this takes a minute..)\n", + "2 Radfiles created in /objects/\n", + "\n", + "Making 2 octfiles in root directory.\n", + "Created 1axis_2021-01-13_1100.oct\n", + "Created 1axis_2021-01-13_1200.oct\n", + "Linescan in process: 1axis_2021-01-13_1100_Scene0_Row1_Module3_Front\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "c:\\users\\mprillim\\sam_dev\\bifacial_radiance\\bifacial_radiance\\performance.py:57: FutureWarning: Calling float on a single element Series is deprecated and will raise a TypeError in the future. Use float(ser.iloc[0]) instead\n", - " alpha_sc=float(CECMod.alpha_sc),\n", - "c:\\users\\mprillim\\sam_dev\\bifacial_radiance\\bifacial_radiance\\performance.py:58: FutureWarning: Calling float on a single element Series is deprecated and will raise a TypeError in the future. Use float(ser.iloc[0]) instead\n", - " a_ref=float(CECMod.a_ref),\n", - "c:\\users\\mprillim\\sam_dev\\bifacial_radiance\\bifacial_radiance\\performance.py:59: FutureWarning: Calling float on a single element Series is deprecated and will raise a TypeError in the future. Use float(ser.iloc[0]) instead\n", - " I_L_ref=float(CECMod.I_L_ref),\n", - "c:\\users\\mprillim\\sam_dev\\bifacial_radiance\\bifacial_radiance\\performance.py:60: FutureWarning: Calling float on a single element Series is deprecated and will raise a TypeError in the future. Use float(ser.iloc[0]) instead\n", - " I_o_ref=float(CECMod.I_o_ref),\n", - "c:\\users\\mprillim\\sam_dev\\bifacial_radiance\\bifacial_radiance\\performance.py:61: FutureWarning: Calling float on a single element Series is deprecated and will raise a TypeError in the future. Use float(ser.iloc[0]) instead\n", - " R_sh_ref=float(CECMod.R_sh_ref),\n", - "c:\\users\\mprillim\\sam_dev\\bifacial_radiance\\bifacial_radiance\\performance.py:62: FutureWarning: Calling float on a single element Series is deprecated and will raise a TypeError in the future. Use float(ser.iloc[0]) instead\n", - " R_s=float(CECMod.R_s),\n", - "c:\\users\\mprillim\\sam_dev\\bifacial_radiance\\bifacial_radiance\\performance.py:63: FutureWarning: Calling float on a single element Series is deprecated and will raise a TypeError in the future. Use float(ser.iloc[0]) instead\n", - " Adjust=float(CECMod.Adjust)\n" + "c:\\users\\cdeline\\documents\\python scripts\\bifacial_radiance\\bifacial_radiance\\main.py:2842: UserWarning: Append=False. Over-writing any existing `AnalysisObj` in trackerdict.\n", + " warnings.warn('Append=False. Over-writing any existing `AnalysisObj` in trackerdict.')\n" ] }, { - "data": { - "text/plain": [ - "73.50428000429895" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "Linescan in process: 1axis_2021-01-13_1100_Scene0_Row1_Module3_Back\n", + "Saved: results\\irr_1axis_2021-01-13_1100_Scene0_Row1_Module3.csv\n", + "Index: 2021-01-13_1100. Wm2Front: 254.58006666666665. Wm2Back: 39.917336666666664\n", + "Linescan in process: 1axis_2021-01-13_1200_Scene0_Row1_Module3_Front\n", + "Linescan in process: 1axis_2021-01-13_1200_Scene0_Row1_Module3_Back\n", + "Saved: results\\irr_1axis_2021-01-13_1200_Scene0_Row1_Module3.csv\n", + "Index: 2021-01-13_1200. Wm2Front: 253.3529. Wm2Back: 38.14312\n", + "Linescan in process: 1axis_2021-01-13_1100_Scene1_Row1_Module1_Front\n", + "Linescan in process: 1axis_2021-01-13_1100_Scene1_Row1_Module1_Back\n", + "Saved: results\\irr_1axis_2021-01-13_1100_Scene1_Row1_Module1.csv\n", + "Index: 2021-01-13_1100. Wm2Front: 253.20913333333337. Wm2Back: 52.77725\n", + "Linescan in process: 1axis_2021-01-13_1200_Scene1_Row1_Module1_Front\n", + "Linescan in process: 1axis_2021-01-13_1200_Scene1_Row1_Module1_Back\n", + "Saved: results\\irr_1axis_2021-01-13_1200_Scene1_Row1_Module1.csv\n", + "Index: 2021-01-13_1200. Wm2Front: 252.71443333333332. Wm2Back: 50.74358\n" + ] } ], "source": [ - "print(trackerdict)\n", - "tracker_dict_sample = trackerdict['2021-01-13_1100']\n", - "eff_irr = tracker_dict_sample['Wm2Front'] + tracker_dict_sample['Wm2Back']\n", - "bifacial_radiance.performance.calculatePerformance(eff_irr[0],CECMod=CECMod)\n", - "#calculatePerformanceModule -> calculcateResults()" + "trackerdict = demo.makeScene1axis(trackerdict, module = mymodule2, sceneDict=sceneDict2, append=True)\n", + "trackerdict = demo.makeOct1axis()\n", + "trackerdict = demo.analysis1axis(sensorsy=3, append=False)\n", + "trackerdict = demo.analysis1axis(sensorsy=3, sceneNum=1)" ] }, { "cell_type": "code", - "execution_count": 17, - "id": "fe18ea62", + "execution_count": 12, + "id": "a3a10f10", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Exporting TrackerDict\n" + "Linescan in process: 1axis_groundscan_2021-01-13_1100_Row1_Module1_Front\n", + "Saved: results\\irr_1axis_groundscan_2021-01-13_1100_Row1_Module1.csv\n", + "Index: 2021-01-13_1100. Wm2Ground: 204.88420000000002. sensorsground: 10\n", + "Linescan in process: 1axis_groundscan_2021-01-13_1200_Row1_Module1_Front\n", + "Saved: results\\irr_1axis_groundscan_2021-01-13_1200_Row1_Module1.csv\n", + "Index: 2021-01-13_1200. Wm2Ground: 237.60913. sensorsground: 10\n" ] - }, + } + ], + "source": [ + "# Include an AgriPV groundscan too\n", + "trackerdict = demo.analysis1axisground(sceneNum=1, sensorsground=10)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "6296cabe", + "metadata": {}, + "outputs": [ { "data": { "text/html": [ @@ -305,87 +407,1213 @@ " \n", " \n", " \n", - " Unnamed: 0\n", - " dhi\n", - " ghi\n", - " Wm2Back\n", + " timestamp\n", + " name\n", + " modNum\n", + " rowNum\n", + " sceneNum\n", + " x\n", + " y\n", + " z\n", + " mattype\n", + " rearMat\n", " Wm2Front\n", - " theta\n", - " surf_tilt\n", + " Wm2Back\n", + " backRatio\n", " surf_azm\n", - " clearance_height\n", - " effective_irradiance\n", - " Pout_module\n", - " Wm2BackAvg\n", - " Wm2FrontAvg\n", - " BifiRatio\n", + " surf_tilt\n", + " theta\n", + " temp_air\n", " \n", " \n", " \n", " \n", " 0\n", " 2021-01-13_1100\n", - " 149\n", - " 211\n", - " [45.16279, 45.26495, 46.29665]\n", - " [254.9108, 255.7401, 255.45539999999997]\n", - " -44.14\n", + " 1axis_2021-01-13_1100_Scene0\n", + " 3\n", + " 1\n", + " 0\n", + " [0.3734448, 0.01462469, -0.3441954]\n", + " [2.28669e-17, 8.955042e-19, -2.107589e-17]\n", + " [1.166863, 1.51507, 1.863277]\n", + " [a2.0.a0.test-module.6457, a2.0.a0.test-module...\n", + " [a2.0.a0.test-module.2310, a2.0.a0.test-module...\n", + " [254.39559999999997, 255.37389999999996, 253.9...\n", + " [39.44632, 39.78179, 40.5239]\n", + " [0.15505836162904693, 0.15577799540988566, 0.1...\n", + " 90.0\n", " 44.14\n", + " -44.14\n", + " 4.6\n", + " \n", + " \n", + " 1\n", + " 2021-01-13_1100\n", + " 1axis_2021-01-13_1100_Scene1\n", + " 1\n", + " 1\n", + " 1\n", + " [-14.62656, -14.98538, -15.3442]\n", + " [2.28669e-17, 8.955042e-19, -2.107589e-17]\n", + " [2.166863, 2.51507, 2.863277]\n", + " [a0.0.a0.test.6457, a0.0.a0.test.6457, a0.0.a0...\n", + " [a0.0.a0.test.2310, a0.0.a0.test.2310, a0.0.a0...\n", + " [252.61250000000004, 253.4609, 253.554]\n", + " [52.48218, 52.95898, 52.89059]\n", + " [0.20775683009815385, 0.20894256691045082, 0.2...\n", " 90.0\n", + " 44.14\n", + " -44.14\n", + " 4.6\n", + " \n", + " \n", + " 2\n", + " 2021-01-13_1100\n", + " 1axis_groundscan_2021-01-13_1100\n", + " 1\n", + " 1\n", + " 1\n", + " [-15.0, -14.44444, -13.88889, -13.33333, -12.7...\n", + " [0.0, 3.401797e-17, 6.803593e-17, 1.020539e-16...\n", + " [0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.0...\n", + " [groundplane, groundplane, groundplane, ground...\n", " NaN\n", + " [199.1851, 201.577, 204.3959, 206.5729, 207.20...\n", " NaN\n", " NaN\n", - " 45.574797\n", - " 255.368767\n", - " 0.160620\n", + " 90.0\n", + " 44.14\n", + " -44.14\n", + " 4.6\n", " \n", " \n", - " 1\n", + " 3\n", " 2021-01-13_1200\n", - " 200\n", - " 249\n", - " [43.32023999999999, 43.42238, 44.23265]\n", - " [254.43140000000002, 254.5634, 254.43729999999...\n", - " -21.20\n", + " 1axis_2021-01-13_1200_Scene0\n", + " 3\n", + " 1\n", + " 0\n", + " [0.473756, 0.007594116, -0.4585678]\n", + " [2.900919e-17, 4.650055e-19, -2.807918e-17]\n", + " [1.338767, 1.519579, 1.700391]\n", + " [a2.0.a0.test-module.6457, a2.0.a0.test-module...\n", + " [a2.0.a0.test-module.2310, a2.0.a0.test-module...\n", + " [253.5427, 253.6665, 252.8495]\n", + " [37.92271, 37.79234, 38.71431]\n", + " [0.14957070516837925, 0.1489837681216553, 0.15...\n", + " 90.0\n", " 21.20\n", + " -21.20\n", + " 6.5\n", + " \n", + " \n", + " 4\n", + " 2021-01-13_1200\n", + " 1axis_2021-01-13_1200_Scene1\n", + " 1\n", + " 1\n", + " 1\n", + " [-14.52624, -14.99241, -15.45857]\n", + " [2.900919e-17, 4.650055e-19, -2.807918e-17]\n", + " [2.338767, 2.519579, 2.700391]\n", + " [a0.0.a0.test.6457, a0.0.a0.test.6457, a0.0.a0...\n", + " [a0.0.a0.test.2310, a0.0.a0.test.2310, a0.0.a0...\n", + " [252.5916, 252.71439999999998, 252.8373]\n", + " [50.603010000000005, 50.97375, 50.65398]\n", + " [0.20033449119253693, 0.20170416998726634, 0.2...\n", " 90.0\n", + " 21.20\n", + " -21.20\n", + " 6.5\n", + " \n", + " \n", + " 5\n", + " 2021-01-13_1200\n", + " 1axis_groundscan_2021-01-13_1200\n", + " 1\n", + " 1\n", + " 1\n", + " [-15.0, -14.44444, -13.88889, -13.33333, -12.7...\n", + " [0.0, 3.401797e-17, 6.803593e-17, 1.020539e-16...\n", + " [0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.0...\n", + " [groundplane, groundplane, groundplane, ground...\n", " NaN\n", + " [228.1314, 230.3008, 233.4675, 236.7338, 239.4...\n", " NaN\n", " NaN\n", - " 43.658423\n", - " 254.477367\n", - " 0.154405\n", + " 90.0\n", + " 21.20\n", + " -21.20\n", + " 6.5\n", " \n", " \n", "\n", "" ], "text/plain": [ - " Unnamed: 0 dhi ghi Wm2Back \\\n", - "0 2021-01-13_1100 149 211 [45.16279, 45.26495, 46.29665] \n", - "1 2021-01-13_1200 200 249 [43.32023999999999, 43.42238, 44.23265] \n", + " timestamp name modNum rowNum sceneNum \\\n", + "0 2021-01-13_1100 1axis_2021-01-13_1100_Scene0 3 1 0 \n", + "1 2021-01-13_1100 1axis_2021-01-13_1100_Scene1 1 1 1 \n", + "2 2021-01-13_1100 1axis_groundscan_2021-01-13_1100 1 1 1 \n", + "3 2021-01-13_1200 1axis_2021-01-13_1200_Scene0 3 1 0 \n", + "4 2021-01-13_1200 1axis_2021-01-13_1200_Scene1 1 1 1 \n", + "5 2021-01-13_1200 1axis_groundscan_2021-01-13_1200 1 1 1 \n", + "\n", + " x \\\n", + "0 [0.3734448, 0.01462469, -0.3441954] \n", + "1 [-14.62656, -14.98538, -15.3442] \n", + "2 [-15.0, -14.44444, -13.88889, -13.33333, -12.7... \n", + "3 [0.473756, 0.007594116, -0.4585678] \n", + "4 [-14.52624, -14.99241, -15.45857] \n", + "5 [-15.0, -14.44444, -13.88889, -13.33333, -12.7... \n", + "\n", + " y \\\n", + "0 [2.28669e-17, 8.955042e-19, -2.107589e-17] \n", + "1 [2.28669e-17, 8.955042e-19, -2.107589e-17] \n", + "2 [0.0, 3.401797e-17, 6.803593e-17, 1.020539e-16... \n", + "3 [2.900919e-17, 4.650055e-19, -2.807918e-17] \n", + "4 [2.900919e-17, 4.650055e-19, -2.807918e-17] \n", + "5 [0.0, 3.401797e-17, 6.803593e-17, 1.020539e-16... \n", + "\n", + " z \\\n", + "0 [1.166863, 1.51507, 1.863277] \n", + "1 [2.166863, 2.51507, 2.863277] \n", + "2 [0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.0... \n", + "3 [1.338767, 1.519579, 1.700391] \n", + "4 [2.338767, 2.519579, 2.700391] \n", + "5 [0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.0... \n", + "\n", + " mattype \\\n", + "0 [a2.0.a0.test-module.6457, a2.0.a0.test-module... \n", + "1 [a0.0.a0.test.6457, a0.0.a0.test.6457, a0.0.a0... \n", + "2 [groundplane, groundplane, groundplane, ground... \n", + "3 [a2.0.a0.test-module.6457, a2.0.a0.test-module... \n", + "4 [a0.0.a0.test.6457, a0.0.a0.test.6457, a0.0.a0... \n", + "5 [groundplane, groundplane, groundplane, ground... \n", + "\n", + " rearMat \\\n", + "0 [a2.0.a0.test-module.2310, a2.0.a0.test-module... \n", + "1 [a0.0.a0.test.2310, a0.0.a0.test.2310, a0.0.a0... \n", + "2 NaN \n", + "3 [a2.0.a0.test-module.2310, a2.0.a0.test-module... \n", + "4 [a0.0.a0.test.2310, a0.0.a0.test.2310, a0.0.a0... \n", + "5 NaN \n", + "\n", + " Wm2Front \\\n", + "0 [254.39559999999997, 255.37389999999996, 253.9... \n", + "1 [252.61250000000004, 253.4609, 253.554] \n", + "2 [199.1851, 201.577, 204.3959, 206.5729, 207.20... \n", + "3 [253.5427, 253.6665, 252.8495] \n", + "4 [252.5916, 252.71439999999998, 252.8373] \n", + "5 [228.1314, 230.3008, 233.4675, 236.7338, 239.4... \n", "\n", - " Wm2Front theta surf_tilt \\\n", - "0 [254.9108, 255.7401, 255.45539999999997] -44.14 44.14 \n", - "1 [254.43140000000002, 254.5634, 254.43729999999... -21.20 21.20 \n", + " Wm2Back \\\n", + "0 [39.44632, 39.78179, 40.5239] \n", + "1 [52.48218, 52.95898, 52.89059] \n", + "2 NaN \n", + "3 [37.92271, 37.79234, 38.71431] \n", + "4 [50.603010000000005, 50.97375, 50.65398] \n", + "5 NaN \n", "\n", - " surf_azm clearance_height effective_irradiance Pout_module Wm2BackAvg \\\n", - "0 90.0 NaN NaN NaN 45.574797 \n", - "1 90.0 NaN NaN NaN 43.658423 \n", + " backRatio surf_azm surf_tilt \\\n", + "0 [0.15505836162904693, 0.15577799540988566, 0.1... 90.0 44.14 \n", + "1 [0.20775683009815385, 0.20894256691045082, 0.2... 90.0 44.14 \n", + "2 NaN 90.0 44.14 \n", + "3 [0.14957070516837925, 0.1489837681216553, 0.15... 90.0 21.20 \n", + "4 [0.20033449119253693, 0.20170416998726634, 0.2... 90.0 21.20 \n", + "5 NaN 90.0 21.20 \n", "\n", - " Wm2FrontAvg BifiRatio \n", - "0 255.368767 0.160620 \n", - "1 254.477367 0.154405 " + " theta temp_air \n", + "0 -44.14 4.6 \n", + "1 -44.14 4.6 \n", + "2 -44.14 4.6 \n", + "3 -21.20 6.5 \n", + "4 -21.20 6.5 \n", + "5 -21.20 6.5 " ] }, - "execution_count": 17, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "demo.exportTrackerDict(savefile=os.path.join('results','Final_Results.csv'),reindex=False)\n", - "pd.read_csv(os.path.join('results','Final_Results.csv'))" + "# show the initial irradiance results before continuing:\n", + "demo.results" + ] + }, + { + "cell_type": "markdown", + "id": "7ec0639a", + "metadata": {}, + "source": [ + "## Calculating the Performance and Exporting the Results to a CSV" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "7bbf6db6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "No CECModule data passed; using default for Prism Solar BHC72-400\n", + "\n", + "Compiled results:\n", + "\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
timestampnamemodNumrowNumsceneNumxyzmattyperearMat...Pout_rawPout_GfrontBGGBGEMismatchPoutWind SpeedDNIDHIGHI
02021-01-13_11001axis_2021-01-13_1100_Scene0310[0.3734448, 0.01462469, -0.3441954][2.28669e-17, 8.955042e-19, -2.107589e-17][1.166863, 1.51507, 1.863277][a2.0.a0.test-module.6457, a2.0.a0.test-module...[a2.0.a0.test-module.2310, a2.0.a0.test-module......91.85890780.41059614.11171114.2373170.02177391.8389063.8144149211
12021-01-13_11001axis_2021-01-13_1100_Scene1111[-14.62656, -14.98538, -15.3442][2.28669e-17, 8.955042e-19, -2.107589e-17][2.166863, 2.51507, 2.863277][a0.0.a0.test.6457, a0.0.a0.test.6457, a0.0.a0...[a0.0.a0.test.2310, a0.0.a0.test.2310, a0.0.a0......123.556838105.92948316.67467516.6406510.008254123.5466403.8144149211
22021-01-13_11001axis_groundscan_2021-01-13_1100111[-15.0, -14.44444, -13.88889, -13.33333, -12.7...[0.0, 3.401797e-17, 6.803593e-17, 1.020539e-16...[0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.0...[groundplane, groundplane, groundplane, ground...NaN...85.62299585.6229950.0000000.0000000.00000085.6229953.8144149211
32021-01-13_12001axis_2021-01-13_1200_Scene0310[0.473756, 0.007594116, -0.4585678][2.900919e-17, 4.650055e-19, -2.807918e-17][1.338767, 1.519579, 1.700391][a2.0.a0.test-module.6457, a2.0.a0.test-module...[a2.0.a0.test-module.2310, a2.0.a0.test-module......90.29263279.42654413.54979913.6806760.01872990.2757213.997200249
42021-01-13_12001axis_2021-01-13_1200_Scene1111[-14.52624, -14.99241, -15.45857][2.900919e-17, 4.650055e-19, -2.807918e-17][2.338767, 2.519579, 2.700391][a0.0.a0.test.6457, a0.0.a0.test.6457, a0.0.a0...[a0.0.a0.test.2310, a0.0.a0.test.2310, a0.0.a0......121.853928105.00948416.06353216.0408790.006446121.8460733.997200249
52021-01-13_12001axis_groundscan_2021-01-13_1200111[-15.0, -14.44444, -13.88889, -13.33333, -12.7...[0.0, 3.401797e-17, 6.803593e-17, 1.020539e-16...[0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.0...[groundplane, groundplane, groundplane, ground...NaN...98.71568098.7156800.0000000.0000000.00000098.7156803.997200249
\n", + "

6 rows × 28 columns

\n", + "
" + ], + "text/plain": [ + " timestamp name modNum rowNum sceneNum \\\n", + "0 2021-01-13_1100 1axis_2021-01-13_1100_Scene0 3 1 0 \n", + "1 2021-01-13_1100 1axis_2021-01-13_1100_Scene1 1 1 1 \n", + "2 2021-01-13_1100 1axis_groundscan_2021-01-13_1100 1 1 1 \n", + "3 2021-01-13_1200 1axis_2021-01-13_1200_Scene0 3 1 0 \n", + "4 2021-01-13_1200 1axis_2021-01-13_1200_Scene1 1 1 1 \n", + "5 2021-01-13_1200 1axis_groundscan_2021-01-13_1200 1 1 1 \n", + "\n", + " x \\\n", + "0 [0.3734448, 0.01462469, -0.3441954] \n", + "1 [-14.62656, -14.98538, -15.3442] \n", + "2 [-15.0, -14.44444, -13.88889, -13.33333, -12.7... \n", + "3 [0.473756, 0.007594116, -0.4585678] \n", + "4 [-14.52624, -14.99241, -15.45857] \n", + "5 [-15.0, -14.44444, -13.88889, -13.33333, -12.7... \n", + "\n", + " y \\\n", + "0 [2.28669e-17, 8.955042e-19, -2.107589e-17] \n", + "1 [2.28669e-17, 8.955042e-19, -2.107589e-17] \n", + "2 [0.0, 3.401797e-17, 6.803593e-17, 1.020539e-16... \n", + "3 [2.900919e-17, 4.650055e-19, -2.807918e-17] \n", + "4 [2.900919e-17, 4.650055e-19, -2.807918e-17] \n", + "5 [0.0, 3.401797e-17, 6.803593e-17, 1.020539e-16... \n", + "\n", + " z \\\n", + "0 [1.166863, 1.51507, 1.863277] \n", + "1 [2.166863, 2.51507, 2.863277] \n", + "2 [0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.0... \n", + "3 [1.338767, 1.519579, 1.700391] \n", + "4 [2.338767, 2.519579, 2.700391] \n", + "5 [0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.0... \n", + "\n", + " mattype \\\n", + "0 [a2.0.a0.test-module.6457, a2.0.a0.test-module... \n", + "1 [a0.0.a0.test.6457, a0.0.a0.test.6457, a0.0.a0... \n", + "2 [groundplane, groundplane, groundplane, ground... \n", + "3 [a2.0.a0.test-module.6457, a2.0.a0.test-module... \n", + "4 [a0.0.a0.test.6457, a0.0.a0.test.6457, a0.0.a0... \n", + "5 [groundplane, groundplane, groundplane, ground... \n", + "\n", + " rearMat ... Pout_raw \\\n", + "0 [a2.0.a0.test-module.2310, a2.0.a0.test-module... ... 91.858907 \n", + "1 [a0.0.a0.test.2310, a0.0.a0.test.2310, a0.0.a0... ... 123.556838 \n", + "2 NaN ... 85.622995 \n", + "3 [a2.0.a0.test-module.2310, a2.0.a0.test-module... ... 90.292632 \n", + "4 [a0.0.a0.test.2310, a0.0.a0.test.2310, a0.0.a0... ... 121.853928 \n", + "5 NaN ... 98.715680 \n", + "\n", + " Pout_Gfront BGG BGE Mismatch Pout Wind Speed DNI \\\n", + "0 80.410596 14.111711 14.237317 0.021773 91.838906 3.8 144 \n", + "1 105.929483 16.674675 16.640651 0.008254 123.546640 3.8 144 \n", + "2 85.622995 0.000000 0.000000 0.000000 85.622995 3.8 144 \n", + "3 79.426544 13.549799 13.680676 0.018729 90.275721 3.9 97 \n", + "4 105.009484 16.063532 16.040879 0.006446 121.846073 3.9 97 \n", + "5 98.715680 0.000000 0.000000 0.000000 98.715680 3.9 97 \n", + "\n", + " DHI GHI \n", + "0 149 211 \n", + "1 149 211 \n", + "2 149 211 \n", + "3 200 249 \n", + "4 200 249 \n", + "5 200 249 \n", + "\n", + "[6 rows x 28 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Calculate performance.\n", + "compiledResults = demo.calculatePerformance1axis()\n", + "print(f'\\nCompiled results:\\n')\n", + "display(compiledResults)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "fe18ea62", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exporting TrackerDict\n", + "3\n", + "3\n", + "1\n", + "1\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Unnamed: 0timestamprowNummodNumsceneNumnameWm2FrontWm2BackDNIDHI...thetasurf_tiltsurf_azmPOA_effGfront_meanGrear_meanPout_rawMismatchPoutPout_Gfront
002021-01-13_11001301axis_2021-01-13_1100_Scene0[254.3956 255.3739 253.9707][39.44632 39.78179 40.5239 ]144149...-44.1444.1490.0290.505670254.58006739.91733791.8589070.02177391.83890680.410596
112021-01-13_11001111axis_2021-01-13_1100_Scene1[252.6125 253.4609 253.554 ][52.48218 52.95898 52.89059]144149...-44.1444.1490.0295.430933253.20913352.777250123.5568380.008254123.546640105.929483
222021-01-13_11001111axis_groundscan_2021-01-13_1100[199.1851 201.577 204.3959 206.5729 207.2027 ...NaN144149...-44.1444.1490.0204.884200204.8842000.00000085.6229950.00000085.62299585.622995
332021-01-13_12001301axis_2021-01-13_1200_Scene0[253.5427 253.6665 252.8495][37.92271 37.79234 38.71431]97200...-21.2021.2090.0287.681708253.35290038.14312090.2926320.01872990.27572179.426544
442021-01-13_12001111axis_2021-01-13_1200_Scene1[252.5916 252.7144 252.8373][50.60301 50.97375 50.65398]97200...-21.2021.2090.0293.309297252.71443350.743580121.8539280.006446121.846073105.009484
552021-01-13_12001111axis_groundscan_2021-01-13_1200[228.1314 230.3008 233.4675 236.7338 239.4279 ...NaN97200...-21.2021.2090.0237.609130237.6091300.00000098.7156800.00000098.71568098.715680
\n", + "

6 rows × 23 columns

\n", + "
" + ], + "text/plain": [ + " Unnamed: 0 timestamp rowNum modNum sceneNum \\\n", + "0 0 2021-01-13_1100 1 3 0 \n", + "1 1 2021-01-13_1100 1 1 1 \n", + "2 2 2021-01-13_1100 1 1 1 \n", + "3 3 2021-01-13_1200 1 3 0 \n", + "4 4 2021-01-13_1200 1 1 1 \n", + "5 5 2021-01-13_1200 1 1 1 \n", + "\n", + " name \\\n", + "0 1axis_2021-01-13_1100_Scene0 \n", + "1 1axis_2021-01-13_1100_Scene1 \n", + "2 1axis_groundscan_2021-01-13_1100 \n", + "3 1axis_2021-01-13_1200_Scene0 \n", + "4 1axis_2021-01-13_1200_Scene1 \n", + "5 1axis_groundscan_2021-01-13_1200 \n", + "\n", + " Wm2Front \\\n", + "0 [254.3956 255.3739 253.9707] \n", + "1 [252.6125 253.4609 253.554 ] \n", + "2 [199.1851 201.577 204.3959 206.5729 207.2027 ... \n", + "3 [253.5427 253.6665 252.8495] \n", + "4 [252.5916 252.7144 252.8373] \n", + "5 [228.1314 230.3008 233.4675 236.7338 239.4279 ... \n", + "\n", + " Wm2Back DNI DHI ... theta surf_tilt surf_azm \\\n", + "0 [39.44632 39.78179 40.5239 ] 144 149 ... -44.14 44.14 90.0 \n", + "1 [52.48218 52.95898 52.89059] 144 149 ... -44.14 44.14 90.0 \n", + "2 NaN 144 149 ... -44.14 44.14 90.0 \n", + "3 [37.92271 37.79234 38.71431] 97 200 ... -21.20 21.20 90.0 \n", + "4 [50.60301 50.97375 50.65398] 97 200 ... -21.20 21.20 90.0 \n", + "5 NaN 97 200 ... -21.20 21.20 90.0 \n", + "\n", + " POA_eff Gfront_mean Grear_mean Pout_raw Mismatch Pout \\\n", + "0 290.505670 254.580067 39.917337 91.858907 0.021773 91.838906 \n", + "1 295.430933 253.209133 52.777250 123.556838 0.008254 123.546640 \n", + "2 204.884200 204.884200 0.000000 85.622995 0.000000 85.622995 \n", + "3 287.681708 253.352900 38.143120 90.292632 0.018729 90.275721 \n", + "4 293.309297 252.714433 50.743580 121.853928 0.006446 121.846073 \n", + "5 237.609130 237.609130 0.000000 98.715680 0.000000 98.715680 \n", + "\n", + " Pout_Gfront \n", + "0 80.410596 \n", + "1 105.929483 \n", + "2 85.622995 \n", + "3 79.426544 \n", + "4 105.009484 \n", + "5 98.715680 \n", + "\n", + "[6 rows x 23 columns]" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "demo.exportTrackerDict(savefile=os.path.join('results','Final_Results.csv'),reindex=False)\n", + "pd.read_csv(os.path.join('results','Final_Results.csv'))" + ] + }, + { + "cell_type": "markdown", + "id": "eb2202f4", + "metadata": {}, + "source": [ + "## Now look at gencumulativesky tracking workflow" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "7cd42dfc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "path = C:\\Users\\cdeline\\Documents\\Python Scripts\\Bifacial_Radiance\\bifacial_radiance\\TEMP\\Tutorial_21\n", + "Getting weather file: USA_VA_Richmond.724010_TMY2.epw\n", + " ... OK!\n", + "8760 line in WeatherFile. Assuming this is a standard hourly WeatherFile for the year for purposes of saving Gencumulativesky temporary weather files in EPW folder.\n", + "Coercing year to 2021\n", + "Filtering dates\n", + "Saving file EPWs\\metdata_temp.csv, # points: 8760\n", + "Calculating Sun position for Metdata that is right-labeled with a delta of -30 mins. i.e. 12 is 11:30 sunpos\n", + "Loading albedo, 1 value(s), 0.200 avg\n", + "1 nonzero albedo values.\n", + "\n", + "Module Name: test-module\n", + "Module test-module updated in module.json\n", + "Pre-existing .rad file objects\\test-module.rad will be overwritten\n", + "\n" + ] + } + ], + "source": [ + "starttime = '01_13_11'; endtime = '12_13_12'\n", + "demo = bifacial_radiance.RadianceObj('tutorial_21', path = testfolder) # Create a RadianceObj 'object'\n", + "weatherfile = demo.getEPW(lat = 37.5, lon = -77.6) # This location corresponds to Richmond, VA.\n", + "metdata = demo.readWeatherFile(weatherFile=weatherfile, starttime=starttime, endtime=endtime)\n", + "#metdata = demo.readWeatherFile(weatherFile=weatherfile)\n", + "demo.setGround(0.2)\n", + "mymodule = demo.makeModule(name='test-module', x=1, y=2, bifi=0.9, CECMod=CECMod) " + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "893d060e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Saving file EPWs\\1axis_-15.0.csv, # points: 1875\n", + "Saving file EPWs\\1axis_-10.0.csv, # points: 241\n", + "Saving file EPWs\\1axis_0.0.csv, # points: 2\n", + "Saving file EPWs\\1axis_5.0.csv, # points: 242\n", + "Saving file EPWs\\1axis_10.0.csv, # points: 33\n", + "Saving file EPWs\\1axis_15.0.csv, # points: 1683\n", + "message: There were 1833 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_-15.0.rad\n", + "message: There were 238 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_-10.0.rad\n", + "message: There were 2 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_0.0.rad\n", + "message: There were 239 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_5.0.rad\n", + "message: There were 33 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_10.0.rad\n", + "message: There were 1661 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_15.0.rad\n", + "\n", + "Making .rad files for cumulativesky 1-axis workflow\n", + "6 Radfiles created in /objects/\n", + "\n", + "Making 6 octfiles in root directory.\n", + "Created 1axis_-15.0.oct\n", + "Created 1axis_-10.0.oct\n", + "Created 1axis_0.0.oct\n", + "Created 1axis_5.0.oct\n", + "Created 1axis_10.0.oct\n", + "Created 1axis_15.0.oct\n", + "Linescan in process: 1axis_-15.0_Scene0_Row1_Module2_Front\n", + "Linescan in process: 1axis_-15.0_Scene0_Row1_Module2_Back\n", + "Saved: results\\irr_1axis_-15.0_Scene0_Row1_Module2.csv\n", + "Index: -15.0. Wm2Front: 630970.4. Wm2Back: 80688.0\n", + "Linescan in process: 1axis_-15.0_Scene0_Row1_Module4_Front\n", + "Linescan in process: 1axis_-15.0_Scene0_Row1_Module4_Back\n", + "Saved: results\\irr_1axis_-15.0_Scene0_Row1_Module4.csv\n", + "Index: -15.0. Wm2Front: 631662.8999999999. Wm2Back: 73938.52666666666\n", + "Linescan in process: 1axis_-10.0_Scene0_Row1_Module2_Front\n", + "Linescan in process: 1axis_-10.0_Scene0_Row1_Module2_Back\n", + "Saved: results\\irr_1axis_-10.0_Scene0_Row1_Module2.csv\n", + "Index: -10.0. Wm2Front: 149183.96666666667. Wm2Back: 19569.62\n", + "Linescan in process: 1axis_-10.0_Scene0_Row1_Module4_Front\n", + "Linescan in process: 1axis_-10.0_Scene0_Row1_Module4_Back\n", + "Saved: results\\irr_1axis_-10.0_Scene0_Row1_Module4.csv\n", + "Index: -10.0. Wm2Front: 146034.69999999998. Wm2Back: 17333.7\n", + "Linescan in process: 1axis_0.0_Scene0_Row1_Module2_Front\n", + "Linescan in process: 1axis_0.0_Scene0_Row1_Module2_Back\n", + "Saved: results\\irr_1axis_0.0_Scene0_Row1_Module2.csv\n", + "Index: 0.0. Wm2Front: 998.3703666666667. Wm2Back: 149.42143333333334\n", + "Linescan in process: 1axis_0.0_Scene0_Row1_Module4_Front\n", + "Linescan in process: 1axis_0.0_Scene0_Row1_Module4_Back\n", + "Saved: results\\irr_1axis_0.0_Scene0_Row1_Module4.csv\n", + "Index: 0.0. Wm2Front: 1016.0163333333334. Wm2Back: 120.74649999999998\n", + "Linescan in process: 1axis_5.0_Scene0_Row1_Module2_Front\n", + "Linescan in process: 1axis_5.0_Scene0_Row1_Module2_Back\n", + "Saved: results\\irr_1axis_5.0_Scene0_Row1_Module2.csv\n", + "Index: 5.0. Wm2Front: 147161.56666666665. Wm2Back: 19982.453333333335\n", + "Linescan in process: 1axis_5.0_Scene0_Row1_Module4_Front\n", + "Linescan in process: 1axis_5.0_Scene0_Row1_Module4_Back\n", + "Saved: results\\irr_1axis_5.0_Scene0_Row1_Module4.csv\n", + "Index: 5.0. Wm2Front: 146850.23333333334. Wm2Back: 17386.960000000003\n", + "Linescan in process: 1axis_10.0_Scene0_Row1_Module2_Front\n", + "Linescan in process: 1axis_10.0_Scene0_Row1_Module2_Back\n", + "Saved: results\\irr_1axis_10.0_Scene0_Row1_Module2.csv\n", + "Index: 10.0. Wm2Front: 17289.416666666668. Wm2Back: 2395.402333333333\n", + "Linescan in process: 1axis_10.0_Scene0_Row1_Module4_Front\n", + "Linescan in process: 1axis_10.0_Scene0_Row1_Module4_Back\n", + "Saved: results\\irr_1axis_10.0_Scene0_Row1_Module4.csv\n", + "Index: 10.0. Wm2Front: 17132.626666666667. Wm2Back: 2030.0349999999999\n", + "Linescan in process: 1axis_15.0_Scene0_Row1_Module2_Front\n", + "Linescan in process: 1axis_15.0_Scene0_Row1_Module2_Back\n", + "Saved: results\\irr_1axis_15.0_Scene0_Row1_Module2.csv\n", + "Index: 15.0. Wm2Front: 720878.2666666667. Wm2Back: 86095.06\n", + "Linescan in process: 1axis_15.0_Scene0_Row1_Module4_Front\n", + "Linescan in process: 1axis_15.0_Scene0_Row1_Module4_Back\n", + "Saved: results\\irr_1axis_15.0_Scene0_Row1_Module4.csv\n", + "Index: 15.0. Wm2Front: 718676.1999999998. Wm2Back: 79382.87000000001\n", + "Linescan in process: 1axis_groundscan_-15.0_Row1_Module3_Front\n", + "Saved: results\\irr_1axis_groundscan_-15.0_Row1_Module3.csv\n", + "Index: -15.0. Wm2Ground: 495781.45. sensorsground: 10\n", + "Linescan in process: 1axis_groundscan_-10.0_Row1_Module3_Front\n", + "Saved: results\\irr_1axis_groundscan_-10.0_Row1_Module3.csv\n", + "Index: -10.0. Wm2Ground: 119156.24300000002. sensorsground: 10\n", + "Linescan in process: 1axis_groundscan_0.0_Row1_Module3_Front\n", + "Saved: results\\irr_1axis_groundscan_0.0_Row1_Module3.csv\n", + "Index: 0.0. Wm2Ground: 749.7224600000001. sensorsground: 10\n", + "Linescan in process: 1axis_groundscan_5.0_Row1_Module3_Front\n", + "Saved: results\\irr_1axis_groundscan_5.0_Row1_Module3.csv\n", + "Index: 5.0. Wm2Ground: 109405.975. sensorsground: 10\n", + "Linescan in process: 1axis_groundscan_10.0_Row1_Module3_Front\n", + "Saved: results\\irr_1axis_groundscan_10.0_Row1_Module3.csv\n", + "Index: 10.0. Wm2Ground: 12364.2408. sensorsground: 10\n", + "Linescan in process: 1axis_groundscan_15.0_Row1_Module3_Front\n", + "Saved: results\\irr_1axis_groundscan_15.0_Row1_Module3.csv\n", + "Index: 15.0. Wm2Ground: 389240.1. sensorsground: 10\n" + ] + } + ], + "source": [ + "sceneDict = {'tilt': 0, 'azimuth': 180, 'pitch': 5,'hub_height':1.5, 'nMods':5, 'nRows': 2}\n", + "trackerdict = demo.set1axis(metdata=metdata, cumulativesky=True, limit_angle=15, backtrack=False)\n", + "trackerdict = demo.genCumSky1axis()\n", + "trackerdict = demo.makeScene1axis(trackerdict, module = mymodule, sceneDict = sceneDict)\n", + "trackerdict = demo.makeOct1axis()\n", + "trackerdict = demo.analysis1axis(modWanted = [2,4], sensorsy=3)\n", + "trackerdict = demo.analysis1axisground(sensorsground=10)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "d6340420", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Compiled results:\n", + "\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
rowNummodNumsceneNumBGGGfront_meanGrear_meanPOA_effWm2FrontWm2Back
012011.2807681.666482e+06208879.957100[1856092.1003633335, 1852229.9246933332, 18550...[1670939.5391, 1670103.7703999998, 1658402.651...[210677.9037, 206386.5974, 209575.3702]
1130NaN1.126698e+06NaN[0.0, 0.0, 0.0][628995.037, 704936.1219, 865078.2239000001, 1...[nan, nan, nan]
214010.3031401.661373e+06190192.838167[1834699.018923333, 1829820.722463333, 1833118...[1665089.0180000002, 1665383.926, 1653645.085,...[192584.82510000005, 187164.4957, 190829.1937]
\n", + "
" + ], + "text/plain": [ + " rowNum modNum sceneNum BGG Gfront_mean Grear_mean \\\n", + "0 1 2 0 11.280768 1.666482e+06 208879.957100 \n", + "1 1 3 0 NaN 1.126698e+06 NaN \n", + "2 1 4 0 10.303140 1.661373e+06 190192.838167 \n", + "\n", + " POA_eff \\\n", + "0 [1856092.1003633335, 1852229.9246933332, 18550... \n", + "1 [0.0, 0.0, 0.0] \n", + "2 [1834699.018923333, 1829820.722463333, 1833118... \n", + "\n", + " Wm2Front \\\n", + "0 [1670939.5391, 1670103.7703999998, 1658402.651... \n", + "1 [628995.037, 704936.1219, 865078.2239000001, 1... \n", + "2 [1665089.0180000002, 1665383.926, 1653645.085,... \n", + "\n", + " Wm2Back \n", + "0 [210677.9037, 206386.5974, 209575.3702] \n", + "1 [nan, nan, nan] \n", + "2 [192584.82510000005, 187164.4957, 190829.1937] " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "results = demo.calculatePerformance1axis() # saves to demo.compiledResults and results/Cumulative_Results.csv\n", + "print('\\nCompiled results:\\n')\n", + "display(demo.compiledResults)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "5d891480", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
rowNummodNumsceneNumBGGGfront_meanGrear_meanPOA_effWm2FrontWm2Back
012011.2811666481.987208879.957[1856092.1003633335, 1852229.9246933332, 18550...[1670939.5391, 1670103.7703999998, 1658402.651...[210677.9037, 206386.5974, 209575.3702]
1130NaN1126697.731NaN[0.0, 0.0, 0.0][628995.037, 704936.1219, 865078.2239000001, 1...[nan, nan, nan]
214010.3031661372.676190192.838[1834699.018923333, 1829820.722463333, 1833118...[1665089.0180000002, 1665383.926, 1653645.085,...[192584.82510000005, 187164.4957, 190829.1937]
\n", + "
" + ], + "text/plain": [ + " rowNum modNum sceneNum BGG Gfront_mean Grear_mean \\\n", + "0 1 2 0 11.281 1666481.987 208879.957 \n", + "1 1 3 0 NaN 1126697.731 NaN \n", + "2 1 4 0 10.303 1661372.676 190192.838 \n", + "\n", + " POA_eff \\\n", + "0 [1856092.1003633335, 1852229.9246933332, 18550... \n", + "1 [0.0, 0.0, 0.0] \n", + "2 [1834699.018923333, 1829820.722463333, 1833118... \n", + "\n", + " Wm2Front \\\n", + "0 [1670939.5391, 1670103.7703999998, 1658402.651... \n", + "1 [628995.037, 704936.1219, 865078.2239000001, 1... \n", + "2 [1665089.0180000002, 1665383.926, 1653645.085,... \n", + "\n", + " Wm2Back \n", + "0 [210677.9037, 206386.5974, 209575.3702] \n", + "1 [nan, nan, nan] \n", + "2 [192584.82510000005, 187164.4957, 190829.1937] " + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Results are also automatically saved in \\results\\Cumulative_Results.csv\n", + "pd.read_csv(os.path.join('results','Cumulative_Results.csv'))" ] } ], @@ -405,7 +1633,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.9.13" } }, "nbformat": 4, diff --git a/docs/tutorials/21 - Weather to Module Performance.py b/docs/tutorials/21 - Weather to Module Performance.py index 0ae93bfc..19e8c4ae 100644 --- a/docs/tutorials/21 - Weather to Module Performance.py +++ b/docs/tutorials/21 - Weather to Module Performance.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # coding: utf-8 -# In[2]: +# In[1]: # This information helps with debugging and getting support :) @@ -20,7 +20,7 @@ # This tutorial shows how to use the new function on bifacial_radiance calculatePerformanceModule performance, as well as how to find CEC Module parameters. # -# In[3]: +# In[2]: import os @@ -36,7 +36,7 @@ print ("Your simulation will be stored in %s" % testfolder) -# In[4]: +# In[3]: import bifacial_radiance @@ -47,58 +47,160 @@ bifacial_radiance.__version__ +# ## Geting a CEC Module to pass into demo.makeModule + +# In[4]: + + +url = 'https://raw.githubusercontent.com/NREL/SAM/patch/deploy/libraries/CEC%20Modules.csv' +db = pd.read_csv(url, index_col=0) # Reading this might take 1 min or so, the database is big. + + +# Find the module that you want. In this case we know it's a SunPower of model SPR-E19-310-COM. +# +# Make sure you select only 1 module from the database -- sometimes there are similar names. + # In[5]: +modfilter2 = db.index.str.startswith('SunPower') & db.index.str.endswith('SPR-E19-310-COM') +print(modfilter2) +CECMod = db[modfilter2] +print(len(CECMod), " modules selected. Name of 1st entry: ", CECMod.index[0]) + + +# In[ ]: + + + + + +# In[6]: + + # Selecting only two times as examples starttime = '01_13_11'; endtime = '01_13_12' demo = bifacial_radiance.RadianceObj('tutorial_21', path = testfolder) # Create a RadianceObj 'object' weatherfile = demo.getEPW(lat = 37.5, lon = -77.6) # This location corresponds to Richmond, VA. metdata = demo.readWeatherFile(weatherFile=weatherfile, starttime=starttime, endtime=endtime) demo.setGround(0.2) -mymodule = demo.makeModule(name='test-module', x=1, y=2, bifi=0.9) -sceneDict = {'tilt': 10, 'azimuth': 180, 'pitch': 5,'hub_height':1.5, 'nMods':3, 'nRows': 2} + + +# The CEC data should be passed into the ModuleObj, either at time of creation, or sometime before it is passed into makeScene. + +# In[7]: + + +mymodule = demo.makeModule(name='test-module', x=1, y=2, bifi=0.9, CECMod=CECMod) + + +# The same data could instead be passed after the ModuleObj's definition, or at time of performance analysis: + +# In[8]: + + +mymodule.addCEC(CECMod) + + +# In[9]: + + +# Let's make a second module, and set it to the default Prism Solar module type +mymodule2 = demo.makeModule(name='test', x=1, y=2, bifi=0.8, CECMod=None) + + +# We're going to set up two scenes, each with a different module type! + +# In[10]: + + +sceneDict = {'tilt': 0, 'azimuth': 180, 'pitch': 5,'hub_height':1.5, 'nMods':5, 'nRows': 2} +sceneDict2 = {'tilt': 0, 'azimuth': 180, 'pitch': 5,'hub_height':2.5, 'nMods':2, 'nRows': 1, 'originx': -15} trackerdict = demo.set1axis(metdata = metdata, cumulativesky = False) trackerdict = demo.gendaylit1axis() -trackerdict = demo.makeScene1axis(moduletype = mymodule, sceneDict = sceneDict) -trackerdict = demo.makeOct1axis() -trackerdict = demo.analysis1axis(sensorsy=3) +trackerdict = demo.makeScene1axis(trackerdict, module = mymodule, sceneDict = sceneDict) -# ## Geting a CEC Module +# Make a second scene with the other module type -# In[ ]: +# In[11]: -url = 'https://raw.githubusercontent.com/NREL/SAM/patch/deploy/libraries/CEC%20Modules.csv' -db = pd.read_csv(url, index_col=0) # Reading this might take 1 min or so, the database is big. +trackerdict = demo.makeScene1axis(trackerdict, module = mymodule2, sceneDict=sceneDict2, append=True) +trackerdict = demo.makeOct1axis() +trackerdict = demo.analysis1axis(sensorsy=3, append=False) +trackerdict = demo.analysis1axis(sensorsy=3, sceneNum=1) -# Find the module that you want. In this case we know it's a SunPower of model SPR-E19-310-COM. -# -# Make sure you select only 1 module from the database -- sometimes there are similar names. +# In[12]: -# In[ ]: +# Include an AgriPV groundscan too +trackerdict = demo.analysis1axisground(sceneNum=1, sensorsground=10) -modfilter2 = db.index.str.startswith('SunPower') & db.index.str.endswith('SPR-E19-310-COM') -print(modfilter2) -CECMod = db[modfilter2] -print(len(CECMod), " modules selected. Name of 1st entry: ", CECMod.index[0]) + +# In[13]: + + +# show the initial irradiance results before continuing: +demo.results # ## Calculating the Performance and Exporting the Results to a CSV -# In[ ]: +# In[14]: -demo.calculateResults(CECMod=CECMod) -#calculatePerformanceModule -> calculcateResults() +# Calculate performance. +compiledResults = demo.calculatePerformance1axis() +print(f'\nCompiled results:\n') +display(compiledResults) -# In[ ]: +# In[15]: + + +demo.exportTrackerDict(savefile=os.path.join('results','Final_Results.csv'),reindex=False) +pd.read_csv(os.path.join('results','Final_Results.csv')) + + +# ## Now look at gencumulativesky tracking workflow + +# In[16]: + + +starttime = '01_13_11'; endtime = '12_13_12' +demo = bifacial_radiance.RadianceObj('tutorial_21', path = testfolder) # Create a RadianceObj 'object' +weatherfile = demo.getEPW(lat = 37.5, lon = -77.6) # This location corresponds to Richmond, VA. +metdata = demo.readWeatherFile(weatherFile=weatherfile, starttime=starttime, endtime=endtime) +#metdata = demo.readWeatherFile(weatherFile=weatherfile) +demo.setGround(0.2) +mymodule = demo.makeModule(name='test-module', x=1, y=2, bifi=0.9, CECMod=CECMod) + + +# In[17]: + + +sceneDict = {'tilt': 0, 'azimuth': 180, 'pitch': 5,'hub_height':1.5, 'nMods':5, 'nRows': 2} +trackerdict = demo.set1axis(metdata=metdata, cumulativesky=True, limit_angle=15, backtrack=False) +trackerdict = demo.genCumSky1axis() +trackerdict = demo.makeScene1axis(trackerdict, module = mymodule, sceneDict = sceneDict) +trackerdict = demo.makeOct1axis() +trackerdict = demo.analysis1axis(modWanted = [2,4], sensorsy=3) +trackerdict = demo.analysis1axisground(sensorsground=10) + + +# In[18]: + + +results = demo.calculatePerformance1axis() # saves to demo.compiledResults and results/Cumulative_Results.csv +print('\nCompiled results:\n') +display(demo.compiledResults) + + +# In[19]: -#demo.exportTrackerDict(savefile=os.path.join('results','Final_Results.csv'),reindex=False) -#pd.read_csv(os.path.join('results','Final_Results.csv')) +# Results are also automatically saved in \results\Cumulative_Results.csv +pd.read_csv(os.path.join('results','Cumulative_Results.csv')) diff --git a/docs/tutorials/3 - Single Axis Tracking Hourly.ipynb b/docs/tutorials/3 - Single Axis Tracking Hourly.ipynb index 204b39ab..cafe21a6 100644 --- a/docs/tutorials/3 - Single Axis Tracking Hourly.ipynb +++ b/docs/tutorials/3 - Single Axis Tracking Hourly.ipynb @@ -673,7 +673,7 @@ "source": [ "demo.makeOct1axis(singleindex='2021-01-13_0800')\n", "results = demo.analysis1axis(singleindex='2021-01-13_0800')\n", - "temp = results['2021-01-13_0800']['AnalysisObj'][0].getResults()\n", + "temp = results['2021-01-13_0800']['AnalysisObj'][0].results\n", "print('\\n\\nHourly bifi gain: {:0.3}'.format(sum(temp['Wm2Back'][0]) / sum(temp['Wm2Front'][0])))" ] }, @@ -931,7 +931,7 @@ "metadata": {}, "outputs": [], "source": [ - "results = demo.getResults()" + "results = demo.results" ] }, { @@ -1312,7 +1312,7 @@ ], "source": [ "print('Accumulated hourly bifi gain for all the trackerdict: {:0.3}'.format(\n", - " demo.getResults().loc[:,'Wm2Back'].sum().sum() / demo.getResults().loc[:,'Wm2Front'].sum().sum()))" + " demo.results.loc[:,'Wm2Back'].sum().sum() / demo.results.loc[:,'Wm2Front'].sum().sum()))" ] }, { @@ -1429,7 +1429,7 @@ "demo.makeScene1axis(module=mymodule,sceneDict=sceneDict) #makeScene creates a .rad file with 20 modules per row, 7 rows.\n", "demo.makeOct1axis()\n", "demo.analysis1axis()\n", - "demo.getResults()" + "print(demo.results)" ] } ], diff --git a/docs/tutorials/3 - Single Axis Tracking Hourly.py b/docs/tutorials/3 - Single Axis Tracking Hourly.py index ae7993a4..1b38f773 100644 --- a/docs/tutorials/3 - Single Axis Tracking Hourly.py +++ b/docs/tutorials/3 - Single Axis Tracking Hourly.py @@ -301,7 +301,7 @@ demo.makeOct1axis(singleindex='2021-01-13_0800') results = demo.analysis1axis(singleindex='2021-01-13_0800') -temp = results['2021-01-13_0800']['AnalysisObj'][0].getResults() +temp = results['2021-01-13_0800']['AnalysisObj'][0].results print('\n\nHourly bifi gain: {:0.3}'.format(sum(temp['Wm2Back'][0]) / sum(temp['Wm2Front'][0]))) @@ -337,7 +337,7 @@ # In[19]: -results = demo.getResults() +results = demo.results # In[20]: @@ -394,7 +394,7 @@ print('Accumulated hourly bifi gain for all the trackerdict: {:0.3}'.format( - demo.getResults().loc[:,'Wm2Back'].sum().sum() / demo.getResults().loc[:,'Wm2Front'].sum().sum())) + demo.results.loc[:,'Wm2Back'].sum().sum() / demo.results.loc[:,'Wm2Front'].sum().sum())) # In[ ]: @@ -478,5 +478,5 @@ demo.makeScene1axis(module=mymodule,sceneDict=sceneDict) #makeScene creates a .rad file with 20 modules per row, 7 rows. demo.makeOct1axis() demo.analysis1axis() -demo.getResults() +print(demo.results) diff --git a/docs/tutorials/4 - Debugging with Custom Objects.ipynb b/docs/tutorials/4 - Debugging with Custom Objects.ipynb index 324073d8..1f64c974 100644 --- a/docs/tutorials/4 - Debugging with Custom Objects.ipynb +++ b/docs/tutorials/4 - Debugging with Custom Objects.ipynb @@ -857,7 +857,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.9.13" } }, "nbformat": 4, diff --git a/docs/tutorials/4 - Debugging with Custom Objects.py b/docs/tutorials/4 - Debugging with Custom Objects.py index 7e11d24a..61fa7e4c 100644 --- a/docs/tutorials/4 - Debugging with Custom Objects.py +++ b/docs/tutorials/4 - Debugging with Custom Objects.py @@ -141,7 +141,7 @@ # # -# In[4]: +# In[5]: # Some tracking parameters that won't be needed after getting this angle: @@ -149,7 +149,7 @@ axis_tilt = 0 limit_angle = 60 backtrack = True -tilt = demo.getSingleTimestampTrackerAngle(metdata, timestamp, gcr, axis_azimuth, axis_tilt,limit_angle, backtrack) +tilt = demo.getSingleTimestampTrackerAngle(timeindex=timestamp, gcr=gcr, azimuth=axis_azimuth, axis_tilt=axis_tilt, limit_angle=limit_angle, backtrack=backtrack) print ("\n NEW Calculated Tilt: %s " % tilt) @@ -158,7 +158,7 @@ # ## 5. Making the Module & the Scene, Visualize and run Analysis -# In[5]: +# In[6]: # Making module with all the variables @@ -185,7 +185,8 @@ # ***rvu -vf views\front.vp -e .01 -pe 0.02 -vp -2 -12 14.5 tutorial_4.oct**** # -# In[6]: +# In[ ]: + ## Comment the line below to run rvu from the Jupyter notebook instead of your terminal. @@ -196,7 +197,7 @@ # And then proceed happily with your analysis: -# In[7]: +# In[ ]: analysis = bifacial_radiance.AnalysisObj(octfile, demo.name) # return an analysis object including the scan dimensions for back irradiance @@ -218,7 +219,7 @@ # Although we could calculate a bifacial ratio average at this point, this value would be misleading, since some of the sensors generated will fall on the torque tube, the sky, and/or the ground since we have torquetube and ygap in the scene. To calculate the real bifacial ratio average, we must use the clean routines. # -# In[8]: +# In[ ]: resultFile='results/irr_tutorial_4.csv' @@ -227,7 +228,7 @@ results_loaded -# In[9]: +# In[ ]: print("Looking at only 1 sensor in the middle -- position 100 out of the 200 sensors sampled:") @@ -238,7 +239,7 @@ # # This might take some time in the current version. -# In[10]: +# In[ ]: # Cleaning Results: @@ -246,20 +247,19 @@ clean_results = bifacial_radiance.load.cleanResult(results_loaded) -# In[11]: +# In[ ]: print("Sampling the same location as before to see what the results are now:") clean_results.loc[100] -# In[12]: +# In[ ]: print('CORRECT Annual bifacial ratio average: %0.3f' %( clean_results['Wm2Back'].sum() / clean_results['Wm2Front'].sum() )) -print ("\n(If we had not done the cleaning routine, the bifacial ratio would have been ", \ - "calculated to %0.3f <-- THIS VALUE IS WRONG)" %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) )) +print ("\n(If we had not done the cleaning routine, the bifacial ratio would have been ", "calculated to %0.3f <-- THIS VALUE IS WRONG)" %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) )) # @@ -275,7 +275,7 @@ # Its sides are going to be 0.5x0.5x0.5 m # and We are going to leave its bottom surface coincident with the plane z=0, but going to center on X and Y. -# In[13]: +# In[ ]: name='MyMarker' @@ -289,7 +289,7 @@ # # I am passing a rotation 0 because xform has to have something (I think) otherwise it gets confused. -# In[14]: +# In[ ]: demo.appendtoScene(scene.radfiles, customObject, '!xform -rz 0') @@ -301,7 +301,8 @@ # # At this point you should be able to go into a command window (cmd.exe) and check the geometry, and the marker should be there. -# In[15]: +# In[ ]: + ## Comment the line below to run rvu from the Jupyter notebook instead of your terminal. diff --git a/docs/tutorials/5 - Bifacial Carports and Canopies.ipynb b/docs/tutorials/5 - Bifacial Carports and Canopies.ipynb index 2cac0796..2106721b 100644 --- a/docs/tutorials/5 - Bifacial Carports and Canopies.ipynb +++ b/docs/tutorials/5 - Bifacial Carports and Canopies.ipynb @@ -505,7 +505,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.9.13" } }, "nbformat": 4, diff --git a/docs/tutorials/5 - Bifacial Carports and Canopies.py b/docs/tutorials/5 - Bifacial Carports and Canopies.py index 5a83ac69..4198fc8a 100644 --- a/docs/tutorials/5 - Bifacial Carports and Canopies.py +++ b/docs/tutorials/5 - Bifacial Carports and Canopies.py @@ -198,7 +198,6 @@ print("") - # # This is the module analysis and an image of the results file # ![This is the module analysed.](../images_wiki/Carport_analysis.PNG) @@ -284,6 +283,7 @@ # In[10]: + ## Comment the ! line below to run rvu from the Jupyter notebook instead of your terminal. ## Simulation will stop until you close the rvu window diff --git a/docs/tutorials/6 - Exploring Trackerdict Structure.ipynb b/docs/tutorials/6 - Exploring Trackerdict Structure.ipynb index 64edf476..f79ece42 100644 --- a/docs/tutorials/6 - Exploring Trackerdict Structure.ipynb +++ b/docs/tutorials/6 - Exploring Trackerdict Structure.ipynb @@ -414,7 +414,10 @@ "demo.Wm2Back # this value is the addition of every individual irradiance result for each hour simulated.\n", "\n", "# Access module values\n", - "demo.trackerdict[trackerkeys[0]]['scene'].module.scenex\n" + "demo.trackerdict[trackerkeys[0]]['scene'].module.scenex\n", + "\n", + "# A pretty DataFrame of all results:\n", + "demo.trackerdict[trackerkeys[0]]['scene'].results\n" ] }, { @@ -469,7 +472,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.9.13" } }, "nbformat": 4, diff --git a/docs/tutorials/6 - Exploring Trackerdict Structure.py b/docs/tutorials/6 - Exploring Trackerdict Structure.py index 6cfc55bc..12c360cf 100644 --- a/docs/tutorials/6 - Exploring Trackerdict Structure.py +++ b/docs/tutorials/6 - Exploring Trackerdict Structure.py @@ -136,6 +136,9 @@ # Access module values demo.trackerdict[trackerkeys[0]]['scene'].module.scenex +# A pretty DataFrame of all results: +demo.trackerdict[trackerkeys[0]]['scene'].results + # diff --git a/tests/test_bifacial_radiance.py b/tests/test_bifacial_radiance.py index f1fda0d3..4035c5eb 100644 --- a/tests/test_bifacial_radiance.py +++ b/tests/test_bifacial_radiance.py @@ -46,7 +46,8 @@ def test_RadianceObj_set1axis(): # test set1axis. requires metdata for boulder. - name = "_test_set1axis" + #name = "_test_set1axis" + name = None demo = bifacial_radiance.RadianceObj(name) assert len(str(demo)) > 300 # Make sure something is printed out here for demo.__repr__ @@ -60,6 +61,8 @@ def test_RadianceObj_set1axis(): trackerdict = demo.set1axis() assert trackerdict[0]['count'] == 78 #80 assert trackerdict[45]['count'] == 822 # + assert len(demo.name) == 17 + def test_RadianceObj_fixed_tilt_end_to_end(): # just run the demo example. Rear irradiance fraction roughly 11.8% for 0.95m landscape panel @@ -88,7 +91,7 @@ def test_RadianceObj_fixed_tilt_end_to_end(): def test_Radiance_high_azimuth_modelchains(): # duplicate next example using modelchain - # high azimuth .ini file + # high azimuth .ini file, includes CEC performance import time HIGH_AZIMUTH_INI = os.path.join(TESTDIR, "ini_highAzimuth.ini") @@ -98,10 +101,12 @@ def test_Radiance_high_azimuth_modelchains(): Params[0]['testfolder'] = TESTDIR # unpack the Params tuple with *Params demo2, analysis = bifacial_radiance.modelchain.runModelChain(*Params ) - results = demo2.getResults() + results = demo2.results #assert np.round(np.mean(analysis.backRatio),2) == 0.20 # bifi ratio was == 0.22 in v0.2.2 assert np.mean(results.Wm2Front[0]) == pytest.approx(899, rel = 0.005) # was 912 in v0.2.3 assert np.mean(results.Wm2Back[0]) == pytest.approx(189, rel = 0.03) # was 182 in v0.2.2 + assert results.Pout[0] == demo2.compiledResults.Pout[0] == pytest.approx(369, abs= 1) + assert results.Mismatch[0] == pytest.approx(2.82, abs = .1) # assert that .hdr image files were created in the last 5 minutes mtime_module = os.path.getmtime(os.path.join('images','test-module_XYZ.hdr')) @@ -149,8 +154,8 @@ def test_Radiance_1axis_gendaylit_modelchains(): # unpack the Params tuple with *Params demo2, analysis = bifacial_radiance.modelchain.runModelChain(*Params) #V 0.2.5 fixed the gcr passed to set1axis. (since gcr was not being passd to set1axis, gcr was default 0.33 default). - assert(demo2.CompiledResults.Gfront_mean[0] == pytest.approx(205.0, 0.01) ) # was 214 in v0.2.3 # was 205 in early v0.2.4 - assert(demo2.CompiledResults.Grear_mean[0] == pytest.approx(43.0, 0.1) ) + assert(demo2.compiledResults.Gfront_mean[0] == pytest.approx(205.0, 0.01) ) # was 214 in v0.2.3 # was 205 in early v0.2.4 + assert(demo2.compiledResults.Grear_mean[0] == pytest.approx(43.0, 0.1) ) assert demo2.trackerdict['2001-01-01_1100']['scenes'][0].text.__len__() == 134 assert demo2.trackerdict['2001-01-01_1100']['scenes'][0].text[23:28] == " 2.0 " demo2.exportTrackerDict(savefile = 'results\exportedTrackerDict.csv', reindex=True) @@ -191,8 +196,8 @@ def test_RadianceObj_1axis_gendaylit_end_to_end(): trackerdict = demo.calculateResults() demo.exportTrackerDict(savefile=os.path.join('results','Final_Results.csv'),reindex=False) #V 0.2.5 fixed the gcr passed to set1axis. (since gcr was not being passd to set1axis, gcr was default 0.33 default). - assert(demo.CompiledResults.Gfront_mean[0] == pytest.approx(205.0, 0.01) ) # was 214 in v0.2.3 # was 205 in early v0.2.4 - assert(demo.CompiledResults.Grear_mean[0] == pytest.approx(43.0, 0.1) ) + assert(demo.compiledResults.Gfront_mean[0] == pytest.approx(205.0, 0.01) ) # was 214 in v0.2.3 # was 205 in early v0.2.4 + assert(demo.compiledResults.Grear_mean[0] == pytest.approx(43.0, 0.1) ) assert demo.trackerdict['2001-01-01_1100']['scene'].text.__len__() == 132 assert demo.trackerdict['2001-01-01_1100']['scene'].text[23:28] == " 2.0 " demo.exportTrackerDict(savefile = 'results\exportedTrackerDict.csv', reindex=True) @@ -241,8 +246,7 @@ def test_1axis_gencumSky(): # assert trackerdict[-5.0]['radfile'] == 'objects\\1axis-5.0_1.825_11.42_5.0_10x3_origin0,0.rad' sceneDict = {'pitch': pitch,'height':hub_height, 'hub_height':hub_height, 'nMods':10, 'nRows':3, 'originy':1} # testing height filter too customObject = demo.makeCustomObject('whiteblock','! genbox white_EPDM whiteblock 1.6 4.5 0.5 | xform -t -0.8 -2.25 0') - #demo.appendtoScene(scene.radfiles, customObject, '!xform -rz 0') - trackerdict = demo.makeScene1axis(sceneDict=sceneDict, module = 'test-module', customtext='!xform -rz 90 '+customObject, append=True)# + trackerdict = demo.makeScene1axis(sceneDict=sceneDict, module = 'test-module', customtext=' -rz 90 '+customObject, append=True)# assert trackerdict[-5.0]['scenes'].__len__() == 4 fname = trackerdict[-5.0]['scenes'][3].radfiles with open(fname, 'r') as f: @@ -264,16 +268,21 @@ def test_1axis_gencumSky(): modscanfront = {'xstart': -5} trackerdict = demo.analysis1axis( sensorsy=2, modscanfront=modscanfront, sceneNum=0, customname='_test2') assert trackerdict[-5.0]['AnalysisObj'][1].x[0] == -5 + trackerdict = demo.analysis1axisground(sensorsground=10,modWanted=1, rowWanted=1) + demo.exportTrackerDict(trackerdict, savefile = 'results\exportedTrackerDict2.csv') CECMod = pd.read_csv(os.path.join(TESTDIR, 'Canadian_Solar_Inc__CS5P_220M.csv'), index_col=0).iloc[:,0] - results = demo.calculateResults(CECMod=CECMod) - pd.testing.assert_frame_equal(results, demo.CompiledResults) - assert results.iloc[0].Grear_mean == pytest.approx(210, abs=30) #gencumsky has lots of variability - assert results.__len__() == 4 - assert results.iloc[3].Grear_mean == pytest.approx(np.mean(results.iloc[3].Wm2Back), abs=0.1) - + module.addCEC(CECMod) + results = demo.calculatePerformance1axis(module=module) + results = demo.calculatePerformance1axis(module=module) #make sure running this twice doesn't error.. + pd.testing.assert_frame_equal(results, demo.compiledResults) + assert results[results.modNum==7].Grear_mean.iloc[0] == pytest.approx(210, abs=30) #gencumsky has lots of variability + assert len(results) == 3 + assert results[results.modNum==5].iloc[0].Grear_mean == pytest.approx(np.mean(results[results.modNum==5].iloc[0].Wm2Back), abs=0.1) + assert len(results[results.modNum==1]['Wm2Front'][0]) == 10 + assert np.isnan((results[results.modNum==1]['Wm2Back'][0])).all() def test_SceneObj_makeSceneNxR_lowtilt(): # test _makeSceneNxR(tilt, height, pitch, azimuth = 180, nMods = 20, nRows = 7, radname = None) @@ -688,7 +697,7 @@ def test_customObj(): f.readline() line = f.readline() assert(line == '!xform -rx 0 -t 2 1 0 objects/Marker.rad') or (line == '!xform -rx 0 -t 2 1 0 objects\Marker.rad') - #assert trackerdict[-20]['count'] == 37 + def test_raypath(): # test errors and raypath updates diff --git a/tests/test_mismatch.py b/tests/test_mismatch.py index 9cdd6fae..56c90f07 100644 --- a/tests/test_mismatch.py +++ b/tests/test_mismatch.py @@ -74,12 +74,7 @@ def test_MAD(): def test_analysisIrradianceandPowerMismatch(): - #analysisIrradianceandPowerMismatch(testfolder, writefiletitle, - # portraitorlandscape, bififactor, - # numcells=72, downsamplingmethod='byCenter'): - - #testfolder = r'C:\Users\cdeline\Documents\Python Scripts\Bifacial_Radiance\tests\results_mismatch' - #writefiletitle = r'C:\Users\cdeline\Documents\Python Scripts\Bifacial_Radiance\tests\mismatch.txt' + testfolder = os.path.join(TESTDIR,'results_mismatch') writefiletitle = os.path.join(TESTDIR,'mismatch.txt') bifacial_radiance.mismatch.analysisIrradianceandPowerMismatch(testfolder, writefiletitle, @@ -88,6 +83,15 @@ def test_analysisIrradianceandPowerMismatch(): df_all = pd.read_csv(writefiletitle) assert df_all.Mismatch_rel[0] == pytest.approx(0.376, abs = 0.001) assert df_all["MAD/G_Total"][0] == pytest.approx(1.987, abs = 0.001) + bifacial_radiance.mismatch.analysisIrradianceandPowerMismatch(testfolder, writefiletitle, + 'portrait', bififactor=1, + numcells=96, downsamplingmethod='byAverage') + df_all = pd.read_csv(writefiletitle) + assert df_all.Mismatch_rel[0] == pytest.approx(0.342, abs = 0.001) + assert df_all["MAD/G_Total"][0] == pytest.approx(1.9175, abs = 0.001) + # need case: sensorsy < cellsy + # need case: sensorsy == cellsy + def test_mismatch_fit3(): diff --git a/tests/test_module.py b/tests/test_module.py index fd439169..6b50f9dd 100644 --- a/tests/test_module.py +++ b/tests/test_module.py @@ -25,7 +25,7 @@ except: pass -#TESTDIR = os.path.dirname(__file__) # this folder +TESTDIR = os.path.dirname(__file__) # this folder cellParams = {'xcell':0.156, 'ycell':0.156, 'numcellsx':6, 'numcellsy':10, 'xcellgap':0.02, 'ycellgap':0.02} @@ -45,6 +45,8 @@ 'omega_thickness' : 0.01, 'inverted' : False} + + def test_CellLevelModule(): # test the cell-level module generation name = "_test_CellLevelModule" @@ -146,4 +148,32 @@ def test_moduleFrameandOmegas(): scene = demo.makeScene(module, sceneDict) analysis = bifacial_radiance.AnalysisObj() # return an analysis object including the scan dimensions for back irradiance frontscan, backscan = analysis.moduleAnalysis(scene, sensorsy=10) # Gives us the dictionaries with coordinates - assert backscan['xstart'] == pytest.approx(0.792) \ No newline at end of file + assert backscan['xstart'] == pytest.approx(0.792) + +def test_CECmodule(): + # Test adding CEC module in various ways + CECMod1 = pd.read_csv(os.path.join(TESTDIR, 'Canadian_Solar_Inc__CS5P_220M.csv'), + index_col=0) #1D dataframe + CECMod2 = CECMod1.iloc[:,0] #pd.Series + CECMod3 = CECMod2.to_dict() + CECMod4 = pd.concat([CECMod1.T, CECMod1.T]) + module = bifacial_radiance.ModuleObj(name='test-module',x=2, y=1, CECMod=CECMod1 ) + module.addCEC(CECMod2) + module3 = bifacial_radiance.ModuleObj(name='test-module',x=2, y=1, CECMod=CECMod3 ) + module4 = bifacial_radiance.ModuleObj(name='test-module',x=2, y=1, CECMod=CECMod4 ) + assert module4.CECMod.name=='Canadian_Solar_Inc__CS5P_220M' + # check for exceptions + with pytest.raises(Exception): + CECMod3.pop('alpha_sc') + module.addCEC(CECMod3) + with pytest.raises(Exception): #when module search function is enabled, this can be updated.. + module.addCEC('Canadian_Solar_Inc__CS5P_220M') + with pytest.raises(Exception): + module.addCEC(1) + # check that CECMod is loaded in from module.json + module2 = bifacial_radiance.ModuleObj(name='test-module' ) + assert module.CECMod.alpha_sc == module2.CECMod.alpha_sc == module3.CECMod.alpha_sc == module4.CECMod.alpha_sc + +def test_modulePerformance(): + module = bifacial_radiance.ModuleObj(name='test-module',x=2, y=1) + \ No newline at end of file diff --git a/tests/test_performance.py b/tests/test_performance.py index 72b4ecec..998f7532 100644 --- a/tests/test_performance.py +++ b/tests/test_performance.py @@ -28,6 +28,8 @@ T0 = 25 # degC +# TODO: write for line 245-265 and 372-391 if csvfile is not None: + def test_calculatePerformance(): # set the IEC61853 test matrix @@ -42,21 +44,23 @@ def test_calculatePerformance(): #CECMODS = pvlib.pvsystem.retrieve_sam(name='CECMod') #CECMod = CECMODS['Canadian_Solar_Inc__CS5P_220M'] CECMod = pd.read_csv(os.path.join(TESTDIR, 'Canadian_Solar_Inc__CS5P_220M.csv'), - index_col=0).iloc[:,0] - p_mp_celltemp = bifacial_radiance.performance.calculatePerformance(s1, CECMod=CECMod, - temp_cell=s2) - - assert p_mp_celltemp[0] == pytest.approx(219.96093865) - p_mp_tamb = bifacial_radiance.performance.calculatePerformance(s1, CECMod=CECMod, - temp_air=s3, wind_speed=1, glassglass=True) - assert p_mp_tamb[0] == pytest.approx(190.4431, abs=.0001) - # test passing CECMod as a DF + index_col=0) + module = bifacial_radiance.ModuleObj('test-module', x=1, y=2) + # check default Prism data + p_mp_celltemp = module.calculatePerformance(s1, temp_cell=s2) + assert module.CECMod.name == 'Prism Solar Technologies Inc. BHC72-400' + assert module.CECMod.I_L_ref == '10.5308' + assert p_mp_celltemp[0] == pytest.approx(400.000, abs=0.001) - p_mp_celltemp2 = bifacial_radiance.performance.calculatePerformance(s1, pd.DataFrame([CECMod]), - temp_cell=s2) - p_mp_celltemp3 = bifacial_radiance.performance.calculatePerformance(s1, pd.DataFrame([CECMod, CECMod]), - temp_cell=s2) - assert p_mp_celltemp3.all()==p_mp_celltemp2.all()==p_mp_celltemp.all() + module.addCEC(CECMod=CECMod) + p_mp_celltemp = module.calculatePerformance(s1, temp_cell=s2) + assert module.CECMod.name == 'Canadian_Solar_Inc__CS5P_220M' + assert p_mp_celltemp[0] == pytest.approx(219.9609, abs=0.0001) + p_mp_tamb = module.calculatePerformance(s1, temp_air=s3, wind_speed=1, glassglass=True) + assert p_mp_tamb[0] == pytest.approx(190.4431, abs=.0001) + module.addCEC(CECMod = None) # default Prism data + assert module.calculatePerformance(s1, temp_cell=s2)[0] == pytest.approx(399.99, abs = 0.03) + def test_MBD(): from bifacial_radiance import performance