From cea2415e8e7e04127f099354cdefd822b4762d8c Mon Sep 17 00:00:00 2001 From: cdeline Date: Mon, 11 Mar 2024 15:39:19 -0600 Subject: [PATCH 01/33] pass a copy of sceneDict to _makeSceneNxR. fixes #502. --- bifacial_radiance/main.py | 86 +++++++++++++++---------- docs/sphinx/source/whatsnew/pending.rst | 1 + tests/test_bifacial_radiance.py | 3 +- 3 files changed, 54 insertions(+), 36 deletions(-) diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py index 574ca27c..f6ea95e9 100644 --- a/bifacial_radiance/main.py +++ b/bifacial_radiance/main.py @@ -2302,6 +2302,8 @@ def makeScene1axis(self, trackerdict=None, module=None, sceneDict=None, hubheight = sceneDict['hub_height'] simplefix = 1 + # we no longer need sceneDict['hub_height'] - it'll be replaced by 'clearance_height' below + sceneDict.pop('hub_height',None) if cumulativesky is True: # cumulativesky workflow print('\nMaking .rad files for cumulativesky 1-axis workflow') for theta in trackerdict: @@ -2319,24 +2321,30 @@ def makeScene1axis(self, trackerdict=None, module=None, sceneDict=None, #trackerdict[theta]['clearance_height'] = height try: - sceneDict2 = {'tilt':trackerdict[theta]['surf_tilt'], - 'pitch':sceneDict['pitch'], - 'clearance_height':height, - 'azimuth':trackerdict[theta]['surf_azm'], - 'nMods': sceneDict['nMods'], - 'nRows': sceneDict['nRows'], - 'modulez': scene.module.z} - except KeyError: + sceneDict.update({'tilt' : trackerdict[theta]['surf_tilt'], + 'clearance_height' : height, + 'azimuth' : trackerdict[theta]['surf_azm'], + 'modulez' : scene.module.z}) + + # sceneDict2 = {'tilt':trackerdict[theta]['surf_tilt'], + # 'pitch':sceneDict['pitch'], + # 'clearance_height':height, + # 'azimuth':trackerdict[theta]['surf_azm'], + # 'nMods': sceneDict['nMods'], + # 'nRows': sceneDict['nRows'], + # 'modulez': scene.module.z} + except KeyError as err: #maybe gcr is passed, not pitch - sceneDict2 = {'tilt':trackerdict[theta]['surf_tilt'], - 'gcr':sceneDict['gcr'], - 'clearance_height':height, - 'azimuth':trackerdict[theta]['surf_azm'], - 'nMods': sceneDict['nMods'], - 'nRows': sceneDict['nRows'], - 'modulez': scene.module.z} - - radfile = scene._makeSceneNxR(sceneDict=sceneDict2, + # sceneDict2 = {'tilt':trackerdict[theta]['surf_tilt'], + # 'gcr':sceneDict['gcr'], + # 'clearance_height':height, + # 'azimuth':trackerdict[theta]['surf_azm'], + # 'nMods': sceneDict['nMods'], + # 'nRows': sceneDict['nRows'], + # 'modulez': scene.module.z} + raise err + + radfile = scene._makeSceneNxR(sceneDict=(sceneDict), radname=radname) trackerdict[theta]['radfile'] = radfile trackerdict[theta]['scene'] = scene @@ -2363,24 +2371,30 @@ def makeScene1axis(self, trackerdict=None, module=None, sceneDict=None, if trackerdict[time]['ghi'] > 0: #trackerdict[time]['clearance_height'] = height try: - sceneDict2 = {'tilt':trackerdict[time]['surf_tilt'], - 'pitch':sceneDict['pitch'], - 'clearance_height': height, - 'azimuth':trackerdict[time]['surf_azm'], - 'nMods': sceneDict['nMods'], - 'nRows': sceneDict['nRows'], - 'modulez': scene.module.z} - except KeyError: + sceneDict.update({'tilt' : trackerdict[time]['surf_tilt'], + 'clearance_height' : height, + 'azimuth' : trackerdict[time]['surf_azm'], + 'modulez' : scene.module.z}) + # sceneDict2 = {'tilt':trackerdict[time]['surf_tilt'], + # 'pitch':sceneDict['pitch'], + # 'clearance_height': height, + # 'azimuth':trackerdict[time]['surf_azm'], + # 'nMods': sceneDict['nMods'], + # 'nRows': sceneDict['nRows'], + # 'modulez': scene.module.z} + + except KeyError as err: #maybe gcr is passed instead of pitch - sceneDict2 = {'tilt':trackerdict[time]['surf_tilt'], - 'gcr':sceneDict['gcr'], - 'clearance_height': height, - 'azimuth':trackerdict[time]['surf_azm'], - 'nMods': sceneDict['nMods'], - 'nRows': sceneDict['nRows'], - 'modulez': scene.module.z} - - radfile = scene._makeSceneNxR(sceneDict=sceneDict2, + # sceneDict2 = {'tilt':trackerdict[time]['surf_tilt'], + # 'gcr':sceneDict['gcr'], + # 'clearance_height': height, + # 'azimuth':trackerdict[time]['surf_azm'], + # 'nMods': sceneDict['nMods'], + # 'nRows': sceneDict['nRows'], + # 'modulez': scene.module.z} + raise err + # if sceneDict isn't copied, it will change inside the SceneObj since dicts are mutable! + radfile = scene._makeSceneNxR(sceneDict=(sceneDict), radname=radname) trackerdict[time]['radfile'] = radfile trackerdict[time]['scene'] = scene @@ -2894,7 +2908,8 @@ def _makeSceneNxR(self, modulename=None, sceneDict=None, radname=None): Returns a `SceneObject` 'scene' with configuration details """ - + import copy + if modulename is None: modulename = self.module.name @@ -2902,6 +2917,7 @@ def _makeSceneNxR(self, modulename=None, sceneDict=None, radname=None): print('makeScene(modulename, sceneDict, nMods, nRows). sceneDict' ' inputs: .tilt .azimuth .nMods .nRows' ' AND .tilt or .gcr ; AND .hub_height or .clearance_height') + else: sceneDict = copy.deepcopy(sceneDict) if 'orientation' in sceneDict: diff --git a/docs/sphinx/source/whatsnew/pending.rst b/docs/sphinx/source/whatsnew/pending.rst index b0542c83..f74151b2 100644 --- a/docs/sphinx/source/whatsnew/pending.rst +++ b/docs/sphinx/source/whatsnew/pending.rst @@ -18,6 +18,7 @@ Enhancements Bug fixes ~~~~~~~~~ +* Fixed error passing all of `sceneDict` into :py:class:`~bifacial_radiance.makeScene1axis`. (:issue:`502`) * Fixed Pandas 2.0 errors by re-factoring ``mismatch.mad_fn`` (:issue:`449`) Documentation diff --git a/tests/test_bifacial_radiance.py b/tests/test_bifacial_radiance.py index 3c4e2a3b..b792c325 100644 --- a/tests/test_bifacial_radiance.py +++ b/tests/test_bifacial_radiance.py @@ -223,10 +223,11 @@ def test_1axis_gencumSky(): sceneDict = {'pitch': pitch,'height':hub_height, 'clearance_height':hub_height, 'nMods':10, 'nRows':3} # testing height filter too trackerdict = demo.makeScene1axis(sceneDict=sceneDict, module = 'test-module') # 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} # testing height filter too + sceneDict = {'pitch': pitch,'height':hub_height, 'hub_height':hub_height, 'nMods':10, 'nRows':3, 'originy':1} # testing height filter too trackerdict = demo.makeScene1axis(sceneDict=sceneDict, module = 'test-module') demo.exportTrackerDict(trackerdict, savefile = 'results\exportedTrackerDict2.csv') assert trackerdict[-5.0]['radfile'][0:7] == 'objects' + assert trackerdict[-5]['scene'].sceneDict['originy'] == 1 #assert trackerdict[-5.0]['radfile'] == 'objects\\1axis-5.0_1.825_11.42_5.0_10x3_origin0,0.rad' minitrackerdict = {} minitrackerdict[list(trackerdict)[0]] = trackerdict[list(trackerdict.keys())[0]] From 5770393f7b63313c120f39435495be811c152f0c Mon Sep 17 00:00:00 2001 From: cdeline Date: Mon, 11 Mar 2024 15:47:13 -0600 Subject: [PATCH 02/33] Improve pytest coverage by removing extraneous try:except KeyError check. --- bifacial_radiance/main.py | 56 +++++++-------------------------------- 1 file changed, 10 insertions(+), 46 deletions(-) diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py index f6ea95e9..26e03aec 100644 --- a/bifacial_radiance/main.py +++ b/bifacial_radiance/main.py @@ -2320,29 +2320,10 @@ def makeScene1axis(self, trackerdict=None, module=None, sceneDict=None, # Calculate the ground clearance height based on the hub height. Add abs(theta) to avoid negative tilt angle errors #trackerdict[theta]['clearance_height'] = height - try: - sceneDict.update({'tilt' : trackerdict[theta]['surf_tilt'], - 'clearance_height' : height, - 'azimuth' : trackerdict[theta]['surf_azm'], - 'modulez' : scene.module.z}) - - # sceneDict2 = {'tilt':trackerdict[theta]['surf_tilt'], - # 'pitch':sceneDict['pitch'], - # 'clearance_height':height, - # 'azimuth':trackerdict[theta]['surf_azm'], - # 'nMods': sceneDict['nMods'], - # 'nRows': sceneDict['nRows'], - # 'modulez': scene.module.z} - except KeyError as err: - #maybe gcr is passed, not pitch - # sceneDict2 = {'tilt':trackerdict[theta]['surf_tilt'], - # 'gcr':sceneDict['gcr'], - # 'clearance_height':height, - # 'azimuth':trackerdict[theta]['surf_azm'], - # 'nMods': sceneDict['nMods'], - # 'nRows': sceneDict['nRows'], - # 'modulez': scene.module.z} - raise err + sceneDict.update({'tilt' : trackerdict[theta]['surf_tilt'], + 'clearance_height' : height, + 'azimuth' : trackerdict[theta]['surf_azm'], + 'modulez' : scene.module.z}) radfile = scene._makeSceneNxR(sceneDict=(sceneDict), radname=radname) @@ -2370,29 +2351,12 @@ def makeScene1axis(self, trackerdict=None, module=None, sceneDict=None, if trackerdict[time]['ghi'] > 0: #trackerdict[time]['clearance_height'] = height - try: - sceneDict.update({'tilt' : trackerdict[time]['surf_tilt'], - 'clearance_height' : height, - 'azimuth' : trackerdict[time]['surf_azm'], - 'modulez' : scene.module.z}) - # sceneDict2 = {'tilt':trackerdict[time]['surf_tilt'], - # 'pitch':sceneDict['pitch'], - # 'clearance_height': height, - # 'azimuth':trackerdict[time]['surf_azm'], - # 'nMods': sceneDict['nMods'], - # 'nRows': sceneDict['nRows'], - # 'modulez': scene.module.z} - - except KeyError as err: - #maybe gcr is passed instead of pitch - # sceneDict2 = {'tilt':trackerdict[time]['surf_tilt'], - # 'gcr':sceneDict['gcr'], - # 'clearance_height': height, - # 'azimuth':trackerdict[time]['surf_azm'], - # 'nMods': sceneDict['nMods'], - # 'nRows': sceneDict['nRows'], - # 'modulez': scene.module.z} - raise err + + sceneDict.update({'tilt' : trackerdict[time]['surf_tilt'], + 'clearance_height' : height, + 'azimuth' : trackerdict[time]['surf_azm'], + 'modulez' : scene.module.z}) + # if sceneDict isn't copied, it will change inside the SceneObj since dicts are mutable! radfile = scene._makeSceneNxR(sceneDict=(sceneDict), radname=radname) From 1cfc40ab094cdaf8527da22cc35ff5ebc7f2bd00 Mon Sep 17 00:00:00 2001 From: cdeline Date: Wed, 21 Aug 2024 15:06:01 -0600 Subject: [PATCH 03/33] Enclose file names used for radfiles in quotes. Fixes #523. CEC performance bugfix. Fixes #516. --- bifacial_radiance/main.py | 16 ++++++++-------- bifacial_radiance/performance.py | 28 +++++++++++++++++++--------- tests/test_bifacial_radiance.py | 6 +++--- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py index 574ca27c..aa58a588 100644 --- a/bifacial_radiance/main.py +++ b/bifacial_radiance/main.py @@ -3002,11 +3002,11 @@ def _makeSceneNxR(self, modulename=None, sceneDict=None, radname=None): f'{nMods}modsx{nRows}rows_origin{originx},{originy}.rad' ) if self.hpc: - text += f'"{os.path.join(os.getcwd(), self.modulefile)}"' - radfile = os.path.join(os.getcwd(), 'objects', filename) + text += f'"{os.path.join(os.getcwd(), self.modulefile)}"' + radfile = os.path.join(os.getcwd(), 'objects', filename) else: - text += os.path.join(self.modulefile) - radfile = os.path.join('objects',filename ) + text += f'"{os.path.join(self.modulefile)}"' + radfile = os.path.join('objects',filename) # py2 and 3 compatible: binary write, encode text first with open(radfile, 'wb') as f: @@ -3214,10 +3214,10 @@ def __init__(self, tmydata, metadata, label = 'right'): sunup['minutedelta']= int(interval.seconds/2/60) # default sun angle 30 minutes before timestamp # vector update of minutedelta at sunrise sunrisemask = sunup.index.hour-1==sunup['sunrise'].dt.hour - sunup['minutedelta'].mask(sunrisemask,np.floor((60-(sunup['sunrise'].dt.minute))/2),inplace=True) + sunup['minutedelta'] = sunup['minutedelta'].mask(sunrisemask,np.floor((60-(sunup['sunrise'].dt.minute))/2)) # vector update of minutedelta at sunset sunsetmask = sunup.index.hour-1==sunup['sunset'].dt.hour - sunup['minutedelta'].mask(sunsetmask,np.floor((60-(sunup['sunset'].dt.minute))/2),inplace=True) + sunup['minutedelta'] = sunup['minutedelta'].mask(sunsetmask,np.floor((60-(sunup['sunset'].dt.minute))/2)) # save corrected timestamp sunup['corrected_timestamp'] = sunup.index-pd.to_timedelta(sunup['minutedelta'], unit='m') @@ -3228,10 +3228,10 @@ def __init__(self, tmydata, metadata, label = 'right'): sunup['minutedelta']= int(interval.seconds/2/60) # default sun angle 30 minutes after timestamp # vector update of minutedelta at sunrise sunrisemask = sunup.index.hour==sunup['sunrise'].dt.hour - sunup['minutedelta'].mask(sunrisemask,np.ceil((60+sunup['sunrise'].dt.minute)/2),inplace=True) + sunup['minutedelta'] = sunup['minutedelta'].mask(sunrisemask,np.ceil((60+sunup['sunrise'].dt.minute)/2)) # vector update of minutedelta at sunset sunsetmask = sunup.index.hour==sunup['sunset'].dt.hour - sunup['minutedelta'].mask(sunsetmask,np.ceil((60+sunup['sunset'].dt.minute)/2),inplace=True) + sunup['minutedelta'] = sunup['minutedelta'].mask(sunsetmask,np.ceil((60+sunup['sunset'].dt.minute)/2)) # save corrected timestamp sunup['corrected_timestamp'] = sunup.index+pd.to_timedelta(sunup['minutedelta'], unit='m') else: raise ValueError('Error: invalid weather label passed. Valid inputs: right, left or center') diff --git a/bifacial_radiance/performance.py b/bifacial_radiance/performance.py index d1e0d14a..d7d41fc2 100644 --- a/bifacial_radiance/performance.py +++ b/bifacial_radiance/performance.py @@ -6,7 +6,7 @@ """ import pvlib - +import pandas as pd def calculatePerformance(effective_irradiance, CECMod, temp_air=None, wind_speed=1, temp_cell=None, glassglass=False): ''' @@ -50,17 +50,27 @@ def calculatePerformance(effective_irradiance, CECMod, temp_air=None, wind_speed 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(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) + 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( diff --git a/tests/test_bifacial_radiance.py b/tests/test_bifacial_radiance.py index 3c4e2a3b..5524d6d9 100644 --- a/tests/test_bifacial_radiance.py +++ b/tests/test_bifacial_radiance.py @@ -146,7 +146,7 @@ def test_Radiance_1axis_gendaylit_modelchains(): #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(np.mean(demo2.Wm2Front) == pytest.approx(205.0, 0.01) ) # was 214 in v0.2.3 # was 205 in early v0.2.4 assert(np.mean(demo2.Wm2Back) == pytest.approx(43.0, 0.1) ) - assert demo2.trackerdict['2001-01-01_1100']['scene'].text.__len__() == 132 + assert demo2.trackerdict['2001-01-01_1100']['scene'].text.__len__() == 134 assert demo2.trackerdict['2001-01-01_1100']['scene'].text[23:28] == " 2.0 " demo2.exportTrackerDict(savefile = 'results\exportedTrackerDict.csv', reindex=True) @@ -267,7 +267,7 @@ def test_SceneObj_makeSceneNxR_lowtilt(): 'sx_xinc': 0.0, 'sx_yinc':0.0, 'sx_zinc':0.0}) # zstart was 0.01 and zinc was 0 in v0.2.2 #assert scene.text == '!xform -rz -90 -t -0.795 0.475 0 -rx 10 -t 0 0 0.2 -a 20 -t 1.6 0 0 -a 7 -t 0 1.5 0 -i 1 -t -15.9 -4.5 0 -rz 0 objects\\simple_panel.rad' - assert scene.text[0:116] == '!xform -rx 10 -t 0 0 0.2824828843917919 -a 20 -t 1.6 0 0 -a 7 -t 0 1.5 0 -i 1 -t -14.4 -4.5 0 -rz 0 -t 0 0 0 objects' #linux has different directory structure and will error here. + assert scene.text[0:117] == '!xform -rx 10 -t 0 0 0.2824828843917919 -a 20 -t 1.6 0 0 -a 7 -t 0 1.5 0 -i 1 -t -14.4 -4.5 0 -rz 0 -t 0 0 0 "objects' #linux has different directory structure and will error here. def test_SceneObj_makeSceneNxR_hightilt(): # test _makeSceneNxR(tilt, height, pitch, orientation = None, azimuth = 180, nMods = 20, nRows = 7, radname = None) @@ -311,7 +311,7 @@ def test_SceneObj_makeSceneNxR_hightilt(): 'zinc': 0.08609923976848174, 'zstart': 0.28567662150674106, 'sx_xinc': 0.0, 'sx_yinc':0.0, 'sx_zinc':0.0}) #assert scene.text == '!xform -rz -90 -t -0.795 0.475 0 -rx 65 -t 0 0 0.2 -a 20 -t 1.6 0 0 -a 7 -t 0 1.5 0 -i 1 -t -15.9 -4.5 0 -rz 91 objects\\simple_panel.rad' - assert scene.text[0:117] == '!xform -rx 65 -t 0 0 0.6304961988424087 -a 20 -t 1.6 0 0 -a 7 -t 0 1.5 0 -i 1 -t -14.4 -4.5 0 -rz 91 -t 0 0 0 objects' + assert scene.text[0:118] == '!xform -rx 65 -t 0 0 0.6304961988424087 -a 20 -t 1.6 0 0 -a 7 -t 0 1.5 0 -i 1 -t -14.4 -4.5 0 -rz 91 -t 0 0 0 "objects' From 137969182fadadf82e57bf74bc17ff396c6cc634 Mon Sep 17 00:00:00 2001 From: cdeline Date: Wed, 21 Aug 2024 15:22:50 -0600 Subject: [PATCH 04/33] More pytests --- tests/test_performance.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_performance.py b/tests/test_performance.py index cc5bfb82..72b4ecec 100644 --- a/tests/test_performance.py +++ b/tests/test_performance.py @@ -50,6 +50,13 @@ def test_calculatePerformance(): 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 + + 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() def test_MBD(): from bifacial_radiance import performance From cef5ceb3395582a43730735cd8891759d1330183 Mon Sep 17 00:00:00 2001 From: cdeline Date: Wed, 21 Aug 2024 15:31:41 -0600 Subject: [PATCH 05/33] Check for 1D series in mismatch_fit2 and mismatch_fit3. --- bifacial_radiance/mismatch.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/bifacial_radiance/mismatch.py b/bifacial_radiance/mismatch.py index be3440d0..2def94f8 100644 --- a/bifacial_radiance/mismatch.py +++ b/bifacial_radiance/mismatch.py @@ -201,7 +201,10 @@ def mismatch_fit3(data): fit3 = 0.054*mad + 0.068*mad2 if fit3.__len__() == 1: - fit3 = float(fit3) + if isinstance(fit3, pd.Series): + fit3 = float(fit3.iloc[0]) + else: + fit3 = float(fit3) return fit3 @@ -240,7 +243,10 @@ def mismatch_fit2(data): fit2 = 0.142*mad + 0.032*mad2 if fit2.__len__() == 1: - fit2 = float(fit2) + if isinstance(fit2, pd.Series): + fit2 = float(fit2.iloc[0]) + else: + fit2 = float(fit2) return fit2 From 9c9f57bb260215dd426f7782b892342cc73666e7 Mon Sep 17 00:00:00 2001 From: cdeline Date: Thu, 22 Aug 2024 15:24:22 -0600 Subject: [PATCH 06/33] add hubheight back into sceneDict --- bifacial_radiance/main.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py index 171c178d..71796e56 100644 --- a/bifacial_radiance/main.py +++ b/bifacial_radiance/main.py @@ -2326,7 +2326,7 @@ def makeScene1axis(self, trackerdict=None, module=None, sceneDict=None, 'modulez' : scene.module.z}) radfile = scene._makeSceneNxR(sceneDict=(sceneDict), - radname=radname) + radname=radname, addhubheight=True) trackerdict[theta]['radfile'] = radfile trackerdict[theta]['scene'] = scene @@ -2359,7 +2359,7 @@ def makeScene1axis(self, trackerdict=None, module=None, sceneDict=None, # if sceneDict isn't copied, it will change inside the SceneObj since dicts are mutable! radfile = scene._makeSceneNxR(sceneDict=(sceneDict), - radname=radname) + radname=radname, addhubheight=True) trackerdict[time]['radfile'] = radfile trackerdict[time]['scene'] = scene count+=1 @@ -2828,7 +2828,7 @@ def __init__(self, module=None, name=None): else: self.name = name - def _makeSceneNxR(self, modulename=None, sceneDict=None, radname=None): + def _makeSceneNxR(self, modulename=None, sceneDict=None, radname=None, addhubheight=False): """ Arrange module defined in :py:class:`bifacial_radiance.SceneObj` into a N x R array. Returns a :py:class:`bifacial_radiance.SceneObj` which contains details @@ -2862,6 +2862,9 @@ def _makeSceneNxR(self, modulename=None, sceneDict=None, radname=None): Number of rows in system (default = 7) radname : str String for name for radfile. + addhubheight : Bool, default False + Add hubheight back to the sceneDict since it was stripped out + by makeScene1axis Returns @@ -2929,6 +2932,8 @@ def _makeSceneNxR(self, modulename=None, sceneDict=None, radname=None): * self.module.sceney - self.module.offsetfromaxis*np.sin(abs(tilt)*np.pi/180) title_clearance_height = sceneDict['clearance_height'] + if addhubheight: + sceneDict['hub_height'] = np.round(hubheight,3) else: hubheight = sceneDict['hub_height'] # this calculates clearance_height, used for the title From 981491ef879eaa6517391f4d412cd43a8b36e6fa Mon Sep 17 00:00:00 2001 From: cdeline Date: Thu, 22 Aug 2024 15:34:03 -0600 Subject: [PATCH 07/33] Reduce digits in scene radfile titles to 2 digits for clearance and pitch, 0 digits for tilt angle. --- bifacial_radiance/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py index 71796e56..0912c64e 100644 --- a/bifacial_radiance/main.py +++ b/bifacial_radiance/main.py @@ -2983,7 +2983,7 @@ def _makeSceneNxR(self, modulename=None, sceneDict=None, radname=None, addhubhei self.module.scenex*(round(nMods/1.99)*1.0-1)*np.sin( axis_tilt * np.pi/180) ) ) - filename = (f'{radname}_C_{title_clearance_height:0.5f}_rtr_{pitch:0.5f}_tilt_{tilt:0.5f}_' + filename = (f'{radname}_C_{title_clearance_height:0.2f}_rtr_{pitch:0.2f}_tilt_{tilt:0.0f}_' f'{nMods}modsx{nRows}rows_origin{originx},{originy}.rad' ) if self.hpc: From 2b3981007ceb4f1386de14b8715c8d7846d65f6f Mon Sep 17 00:00:00 2001 From: cdeline Date: Thu, 22 Aug 2024 15:37:04 -0600 Subject: [PATCH 08/33] update whatsnew/pending.rst --- docs/sphinx/source/whatsnew/pending.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/sphinx/source/whatsnew/pending.rst b/docs/sphinx/source/whatsnew/pending.rst index e028414e..01848b83 100644 --- a/docs/sphinx/source/whatsnew/pending.rst +++ b/docs/sphinx/source/whatsnew/pending.rst @@ -29,6 +29,8 @@ Documentation ~~~~~~~~~~~~~~ * Edge effects evaluation tutorial 23, with the new functionality of multiple modules/rows on the same analysis scan. * Updates to example notebooks +* Reduce number of digits in makeScene .rad file titles. (:pull:`503`) +* In the sceneDict reported in the trackerdict, save both `clearance_height` and `hub_height` parameters. (:pull:`503`) Contributors ~~~~~~~~~~~~ From 5ac0068cd854633ecdfd3de3cae902512d405e3b Mon Sep 17 00:00:00 2001 From: cdeline Date: Wed, 22 May 2024 15:54:06 -0600 Subject: [PATCH 09/33] change how timestamps are created in _saveTempTMY. Fixes #515. --- bifacial_radiance/main.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py index aa58a588..702fa6ae 100644 --- a/bifacial_radiance/main.py +++ b/bifacial_radiance/main.py @@ -996,12 +996,22 @@ def _saveTempTMY(self, tmydata, filename=None, starttime=None, endtime=None, elif coerce_year is None: coerce_year = 2021 print(f"Coercing year to {coerce_year}") - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - tmydata.index.values[:] = tmydata.index[:] + pd.DateOffset(year=(coerce_year)) - # Correcting last index to next year. - tmydata.index.values[-1] = tmydata.index[-1] + pd.DateOffset(year=(coerce_year+1)) - + #with warnings.catch_warnings(): + # warnings.simplefilter("ignore") # can't get rid of vectorized + #tmydata.index.values[:] = tmydata.index[:] + pd.DateOffset(year=(coerce_year)) + #tmydata.index.values[-1] = tmydata.index[-1] + pd.DateOffset(year=(coerce_year+1)) + tz = tmydata.index.tz + year_vector = np.full(shape=tmydata.__len__(), fill_value=coerce_year) + year_vector[-1] = coerce_year+1 + tmydata.index = pd.to_datetime({ + 'year': year_vector, + 'month': tmydata.index.month, + 'day': tmydata.index.day, + 'hour': tmydata.index.hour}) + tmydata = tmydata.tz_localize(tz) + + + # FilterDates filterdates = None if starttime is not None and endtime is not None: From 66406061c9abd23ce9b0c166fbe054efececc806 Mon Sep 17 00:00:00 2001 From: cdeline Date: Wed, 22 May 2024 16:24:11 -0600 Subject: [PATCH 10/33] update github actions to avoid node.js node12 and node16 warnings # Conflicts: # docs/sphinx/source/whatsnew/pending.rst --- docs/sphinx/source/whatsnew/pending.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/sphinx/source/whatsnew/pending.rst b/docs/sphinx/source/whatsnew/pending.rst index 916a980b..cde6ef4d 100644 --- a/docs/sphinx/source/whatsnew/pending.rst +++ b/docs/sphinx/source/whatsnew/pending.rst @@ -23,6 +23,8 @@ Bug fixes * Switch from un-supported Versioneer to setuptools_scm (:issue:`519`) * Numpy 2.0 compatibility bug (:issue:`521`) * Fixed bug in :func:`bifacial_radiance.mismatch.mismatch_fit3` where the function was not returning the correct values. It has also been deprecated in favour of :func:`bifacial_radiance.mismatch.mismatch_fit2` which has a greater agreement with anual energy yield data (:issue:`520`) +* Updated Github Actions to checkout@v4 and setup-python@v5 (:pull:`517`) +* Fix PerformanceWarning and SettingWithCopyWarning (:issue:`515`) Documentation ~~~~~~~~~~~~~~ From 788626c41b1280aa91fcc9f5503119045add822f Mon Sep 17 00:00:00 2001 From: cdeline Date: Wed, 22 May 2024 16:32:00 -0600 Subject: [PATCH 11/33] switch to github actions coactions/setup-xvfb for node20 support # Conflicts: # docs/sphinx/source/whatsnew/pending.rst --- docs/sphinx/source/whatsnew/pending.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/source/whatsnew/pending.rst b/docs/sphinx/source/whatsnew/pending.rst index cde6ef4d..fc01f030 100644 --- a/docs/sphinx/source/whatsnew/pending.rst +++ b/docs/sphinx/source/whatsnew/pending.rst @@ -23,7 +23,7 @@ Bug fixes * Switch from un-supported Versioneer to setuptools_scm (:issue:`519`) * Numpy 2.0 compatibility bug (:issue:`521`) * Fixed bug in :func:`bifacial_radiance.mismatch.mismatch_fit3` where the function was not returning the correct values. It has also been deprecated in favour of :func:`bifacial_radiance.mismatch.mismatch_fit2` which has a greater agreement with anual energy yield data (:issue:`520`) -* Updated Github Actions to checkout@v4 and setup-python@v5 (:pull:`517`) +* Updated Github Actions to use Node20: checkout@v4, setup-python@v5, coactions/setup-xvfb (:pull:`517`) * Fix PerformanceWarning and SettingWithCopyWarning (:issue:`515`) Documentation From d9dc4333f982fd4bac200d2e341d93e8ad7cbd69 Mon Sep 17 00:00:00 2001 From: cdeline Date: Wed, 22 May 2024 16:45:56 -0600 Subject: [PATCH 12/33] update docker.yml for node20 # Conflicts: # docs/sphinx/source/whatsnew/pending.rst --- .github/workflows/docker.yml | 10 +++++----- docs/sphinx/source/whatsnew/pending.rst | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1a2334db..902da4d2 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -25,30 +25,30 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Print triggering event info run: | echo "${{ github.actor }} triggered run #${{ github.run_number }} with event type ${{ github.event_name }}" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_NREL_USER }} password: ${{ secrets.DOCKERHUB_NREL_TOKEN }} - name: Login to GitHub Package Registry - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: context: . file: ./docker/Dockerfile diff --git a/docs/sphinx/source/whatsnew/pending.rst b/docs/sphinx/source/whatsnew/pending.rst index fc01f030..df27647f 100644 --- a/docs/sphinx/source/whatsnew/pending.rst +++ b/docs/sphinx/source/whatsnew/pending.rst @@ -23,7 +23,7 @@ Bug fixes * Switch from un-supported Versioneer to setuptools_scm (:issue:`519`) * Numpy 2.0 compatibility bug (:issue:`521`) * Fixed bug in :func:`bifacial_radiance.mismatch.mismatch_fit3` where the function was not returning the correct values. It has also been deprecated in favour of :func:`bifacial_radiance.mismatch.mismatch_fit2` which has a greater agreement with anual energy yield data (:issue:`520`) -* Updated Github Actions to use Node20: checkout@v4, setup-python@v5, coactions/setup-xvfb (:pull:`517`) +* Updated Github Actions to use Node20: checkout@v4, setup-python@v5, coactions/setup-xvfb, setup-buildx-action@v3 (:pull:`517`) * Fix PerformanceWarning and SettingWithCopyWarning (:issue:`515`) Documentation From 4ba7e04cbb54745861fde3165ddb1176456adf93 Mon Sep 17 00:00:00 2001 From: cdeline Date: Wed, 22 May 2024 16:57:47 -0600 Subject: [PATCH 13/33] pin coactions to commit hash that uses node20 until the developers cut a new tagged release. https://github.com/coactions/setup-xvfb/issues/29 --- .github/workflows/pytest.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index 81275235..9c903f82 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -1,4 +1,4 @@ -name: test +name: test on: [pull_request, push] @@ -60,7 +60,7 @@ jobs: echo "/home/runner/work/bifacial_radiance/bifacial_radiance/SMARTS_295_Linux" >> $GITHUB_PATH - name: Test with pytest ${{ matrix.env }} - uses: coactions/setup-xvfb@v1 # GUI testing requires xvfb + uses: coactions/setup-xvfb@6b00cf1889f4e1d5a48635647013c0508128ee1a # GUI testing requires xvfb with: run: | pytest --cov=bifacial_radiance From b1acf985aa3d0e9f8750a0c01178029b1164bcd0 Mon Sep 17 00:00:00 2001 From: cdeline Date: Thu, 22 Aug 2024 19:56:05 -0600 Subject: [PATCH 14/33] add continue-on-error=True for coveralls step in github actions --- .github/workflows/pytest.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index 9c903f82..36248555 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -69,6 +69,7 @@ jobs: SMARTSPATH: /home/runner/work/bifacial_radiance/bifacial_radiance/SMARTS_295_Linux - name: Coveralls + continue-on-error: true #prevent coveralls from blowing the test report if: matrix.python-version == 3.11 # && ${{ matrix.env }} == '-r requirements.txt .[all]' run: | coveralls --service=github From 7ea8133dd3526c8667adb72c624c7542f8af8a93 Mon Sep 17 00:00:00 2001 From: cdeline Date: Thu, 22 Aug 2024 20:45:14 -0600 Subject: [PATCH 15/33] update whatsnew/pending.rst --- docs/sphinx/source/whatsnew/pending.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/sphinx/source/whatsnew/pending.rst b/docs/sphinx/source/whatsnew/pending.rst index e60280e9..3346a38b 100644 --- a/docs/sphinx/source/whatsnew/pending.rst +++ b/docs/sphinx/source/whatsnew/pending.rst @@ -25,7 +25,9 @@ Bug fixes * Numpy 2.0 compatibility bug (:issue:`521`) * Fixed bug in :func:`bifacial_radiance.mismatch.mismatch_fit3` where the function was not returning the correct values. It has also been deprecated in favour of :func:`bifacial_radiance.mismatch.mismatch_fit2` which has a greater agreement with anual energy yield data (:issue:`520`) * Updated Github Actions to use Node20: checkout@v4, setup-python@v5, coactions/setup-xvfb, setup-buildx-action@v3 (:pull:`517`) +* Updated Github Actions to make Coveralls fail silently if it has an internal server error (:pull:`517`) * Fix PerformanceWarning and SettingWithCopyWarning (:issue:`515`) +* Switch from Versioneer to setuptools_scm (:pull:`522`) Documentation ~~~~~~~~~~~~~~ From 7af79b1814a2459d5705b994cb5410ed637ae60e Mon Sep 17 00:00:00 2001 From: cdeline Date: Fri, 23 Aug 2024 15:00:49 -0600 Subject: [PATCH 16/33] Enable coerce_year=None. Fixes #526 --- bifacial_radiance/main.py | 35 +++++++++++-------------- docs/sphinx/source/whatsnew/pending.rst | 1 + 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py index 702fa6ae..b4c167e0 100644 --- a/bifacial_radiance/main.py +++ b/bifacial_radiance/main.py @@ -981,8 +981,7 @@ def _saveTempTMY(self, tmydata, filename=None, starttime=None, endtime=None, if filename is None: filename = 'temp.csv' - - + gencumskydata = None gencumdict = None if len(tmydata) == 8760: @@ -991,24 +990,22 @@ def _saveTempTMY(self, tmydata, filename=None, starttime=None, endtime=None, " temporary weather files in EPW folder.") if coerce_year is None and starttime is not None: coerce_year = starttime.year - # SILVANA: If user doesn't pass starttime, and doesn't select - # coerce_year, then do we really need to coerce it? - elif coerce_year is None: + + elif coerce_year is None and len(tmydata.index[:-1].year.unique())>1: coerce_year = 2021 - print(f"Coercing year to {coerce_year}") - #with warnings.catch_warnings(): - # warnings.simplefilter("ignore") # can't get rid of vectorized - #tmydata.index.values[:] = tmydata.index[:] + pd.DateOffset(year=(coerce_year)) - #tmydata.index.values[-1] = tmydata.index[-1] + pd.DateOffset(year=(coerce_year+1)) - tz = tmydata.index.tz - year_vector = np.full(shape=tmydata.__len__(), fill_value=coerce_year) - year_vector[-1] = coerce_year+1 - tmydata.index = pd.to_datetime({ - 'year': year_vector, - 'month': tmydata.index.month, - 'day': tmydata.index.day, - 'hour': tmydata.index.hour}) - tmydata = tmydata.tz_localize(tz) + + if coerce_year: + print(f"Coercing year to {coerce_year}") + tz = tmydata.index.tz + year_vector = np.full(shape=len(tmydata), fill_value=coerce_year) + year_vector[-1] = coerce_year+1 + tmydata.index = pd.to_datetime({ + 'year': year_vector, + 'month': tmydata.index.month, + 'day': tmydata.index.day, + 'hour': tmydata.index.hour}) + + tmydata = tmydata.tz_localize(tz) diff --git a/docs/sphinx/source/whatsnew/pending.rst b/docs/sphinx/source/whatsnew/pending.rst index df27647f..c3bf800e 100644 --- a/docs/sphinx/source/whatsnew/pending.rst +++ b/docs/sphinx/source/whatsnew/pending.rst @@ -25,6 +25,7 @@ Bug fixes * Fixed bug in :func:`bifacial_radiance.mismatch.mismatch_fit3` where the function was not returning the correct values. It has also been deprecated in favour of :func:`bifacial_radiance.mismatch.mismatch_fit2` which has a greater agreement with anual energy yield data (:issue:`520`) * Updated Github Actions to use Node20: checkout@v4, setup-python@v5, coactions/setup-xvfb, setup-buildx-action@v3 (:pull:`517`) * Fix PerformanceWarning and SettingWithCopyWarning (:issue:`515`) +* Enable `coerce_year`=None if the TMYfile is all the same year (:issue:`526`) Documentation ~~~~~~~~~~~~~~ From 673f678605d783ae8c2d4fa13dd831d8f7420eb7 Mon Sep 17 00:00:00 2001 From: cdeline Date: Fri, 23 Aug 2024 15:34:02 -0600 Subject: [PATCH 17/33] Suppress new warnings from pvlib v0.11.0 tmy3 import. --- bifacial_radiance/main.py | 12 ++++++++---- requirements.txt | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py index b4c167e0..f48e12fe 100644 --- a/bifacial_radiance/main.py +++ b/bifacial_radiance/main.py @@ -1174,10 +1174,14 @@ def _convertTMYdate(data, meta): import pvlib - #(tmydata, metadata) = pvlib.tmy.readtmy3(filename=tmyfile) #pvlib<=0.6 - (tmydata, metadata) = pvlib.iotools.tmy.read_tmy3(filename=tmyfile, - coerce_year=coerce_year) + try: + (tmydata, metadata) = pvlib.iotools.tmy.read_tmy3(filename=tmyfile, + coerce_year=coerce_year, + map_variables=False) + except TypeError: # pvlib < 0.10 + (tmydata, metadata) = pvlib.iotools.tmy.read_tmy3(filename=tmyfile, + coerce_year=coerce_year) try: tmydata = _convertTMYdate(tmydata, metadata) @@ -1221,7 +1225,7 @@ def _readEPW(self, epwfile=None, label = 'right', coerce_year=None): #(tmydata, metadata) = readepw(epwfile) # (tmydata, metadata) = pvlib.iotools.epw.read_epw(epwfile, coerce_year=coerce_year) #pvlib>0.6.1 - #pvlib uses -1hr offset that needs to be un-done. Why did they do this? + #pvlib uses -1hr offset that needs to be un-done. tmydata.index = tmydata.index+pd.Timedelta(hours=1) # rename different field parameters to match output from diff --git a/requirements.txt b/requirements.txt index 2556d7b5..fa3d21bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ coverage==7.2.1 cycler==0.11.0 idna==3.4 importlib-metadata==6.0.0 -ipython==8.10.0 +ipython==8.13.0 kiwisolver==1.4.4 matplotlib==3.5.1 more-itertools==9.1.0 From a75b26f781fd215de96f85d50c3c4e39256722b7 Mon Sep 17 00:00:00 2001 From: cdeline Date: Fri, 23 Aug 2024 15:48:38 -0600 Subject: [PATCH 18/33] fix some deprecation warnings --- bifacial_radiance/main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py index f48e12fe..2bc86a1a 100644 --- a/bifacial_radiance/main.py +++ b/bifacial_radiance/main.py @@ -265,9 +265,9 @@ def _subhourlydatatoGencumskyformat(gencumskydata, label='right'): #Resample to hourly. Gencumsky wants right-labeled data. try: - gencumskydata = gencumskydata.resample('60T', closed='right', label='right').mean() + gencumskydata = gencumskydata.resample('60min', closed='right', label='right').mean() except TypeError: # Pandas 2.0 error - gencumskydata = gencumskydata.resample('60T', closed='right', label='right').mean(numeric_only=True) + gencumskydata = gencumskydata.resample('60min', closed='right', label='right').mean(numeric_only=True) if label == 'left': #switch from left to right labeled by adding an hour gencumskydata.index = gencumskydata.index + pd.to_timedelta('1H') @@ -294,7 +294,7 @@ def _subhourlydatatoGencumskyformat(gencumskydata, label='right'): gencumskydata.loc[padend]=0 gencumskydata=gencumskydata.sort_index() # Fill empty timestamps with zeros - gencumskydata = gencumskydata.resample('60T').asfreq().fillna(0) + gencumskydata = gencumskydata.resample('60min').asfreq().fillna(0) # Mask leap year leapmask = ~(_is_leap_and_29Feb(gencumskydata)) gencumskydata = gencumskydata[leapmask] From 83e95447cb1a6afdc3d4e5f56d350e287b095a73 Mon Sep 17 00:00:00 2001 From: cdeline Date: Fri, 23 Aug 2024 16:01:27 -0600 Subject: [PATCH 19/33] change pvlib v0.11 column names for read_tmy3 --- bifacial_radiance/main.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py index 2bc86a1a..9e47c10b 100644 --- a/bifacial_radiance/main.py +++ b/bifacial_radiance/main.py @@ -1178,7 +1178,7 @@ def _convertTMYdate(data, meta): try: (tmydata, metadata) = pvlib.iotools.tmy.read_tmy3(filename=tmyfile, coerce_year=coerce_year, - map_variables=False) + map_variables=True) except TypeError: # pvlib < 0.10 (tmydata, metadata) = pvlib.iotools.tmy.read_tmy3(filename=tmyfile, coerce_year=coerce_year) @@ -1187,7 +1187,14 @@ def _convertTMYdate(data, meta): tmydata = _convertTMYdate(tmydata, metadata) except KeyError: print('PVLib >= 0.8.0 is required for sub-hourly data input') - + + tmydata.rename(columns={'dni':'DNI', + 'dhi':'DHI', + 'temp_air':'DryBulb', + 'wind_speed':'Wspd', + 'ghi':'GHI', + 'albedo':'Alb' + }, inplace=True) #as of v0.11, PVLib changed tmy3 column names.. return tmydata, metadata From adafbbea55fdc11e29d1110d2ad7dd85ff08a43f Mon Sep 17 00:00:00 2001 From: cdeline Date: Tue, 27 Aug 2024 12:51:45 -0600 Subject: [PATCH 20/33] add pyRadiance to setup.py install_requires to possibly help with RADIANCE install issues.. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 73ef7f25..9e5e54a0 100644 --- a/setup.py +++ b/setup.py @@ -99,7 +99,7 @@ 'pvmismatch', 'configparser', 'requests', - + 'pyradiance', ], # List additional groups of dependencies here (e.g. development From 4f3138ce232c07c47524d1d6a0d8a85fd9b3188a Mon Sep 17 00:00:00 2001 From: cdeline Date: Tue, 27 Aug 2024 12:54:22 -0600 Subject: [PATCH 21/33] Update whatsnew/pending.rst --- docs/sphinx/source/whatsnew/pending.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/sphinx/source/whatsnew/pending.rst b/docs/sphinx/source/whatsnew/pending.rst index c3bf800e..89af1623 100644 --- a/docs/sphinx/source/whatsnew/pending.rst +++ b/docs/sphinx/source/whatsnew/pending.rst @@ -16,6 +16,7 @@ API Changes Enhancements ~~~~~~~~~~~~ * Added :func:`bifacial_radiance.mismatch.mismatch_fit2`, similar to :func:`bifacial_radiance.mismatch.mismatch_fit3`, with the recommended coefficients of the original publication. (:pull:`520`) +* Including `pyRadiance` as a requirement to help streamline RADIANCE installation and calls in a future release. (:pull:`532`) Bug fixes ~~~~~~~~~ From b88f13f31da1e6ffe0035501be5ba69df4053863 Mon Sep 17 00:00:00 2001 From: cdeline Date: Tue, 27 Aug 2024 17:13:24 -0600 Subject: [PATCH 22/33] files saved to \results\ with 3 decimal floats. --- bifacial_radiance/main.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py index 98a9722c..4088d6b6 100644 --- a/bifacial_radiance/main.py +++ b/bifacial_radiance/main.py @@ -3877,8 +3877,8 @@ def _saveResults(self, data=None, reardata=None, savefile=None, RGB = False): setattr(self, col, list(df[col])) # only save a subset df = df.drop(columns=['rearX','rearY','backRatio'], errors='ignore') - df.to_csv(os.path.join("results", savefile), sep = ',', - index = False) + df.to_csv(os.path.join("results", savefile), sep=',', + index=False, float_format='%0.3f') print('Saved: %s'%(os.path.join("results", savefile))) @@ -3922,12 +3922,12 @@ def _saveResultsCumulative(self, data, reardata=None, savefile=None): df.to_csv(savefile, sep = ',', columns = ['x','y','z','rearZ','mattype','rearMat', 'Wm2Front','Wm2Back','Back/FrontRatio'], - index = False) # new in 0.2.3 + index=False, float_format='%0.3f') # new in 0.2.3 else: df = pd.DataFrame.from_dict(data_sub) - df.to_csv(savefile, sep = ',', - columns = ['x','y','z', 'mattype','Wm2'], index = False) + df.to_csv(savefile, sep=',', float_format='%0.3f', + columns=['x','y','z', 'mattype','Wm2'], index=False) print('Saved: %s'%(savefile)) return (savefile) From 156a2bfa9ff243ef88c419567a8f7c3da53842aa Mon Sep 17 00:00:00 2001 From: cdeline Date: Tue, 27 Aug 2024 20:48:19 -0600 Subject: [PATCH 23/33] update whatsnew. rename pending to v0.4.3.rst --- docs/sphinx/source/whatsnew.rst | 1 + docs/sphinx/source/whatsnew/{pending.rst => v0.4.3.rst} | 1 + 2 files changed, 2 insertions(+) rename docs/sphinx/source/whatsnew/{pending.rst => v0.4.3.rst} (97%) diff --git a/docs/sphinx/source/whatsnew.rst b/docs/sphinx/source/whatsnew.rst index d5323735..80497386 100644 --- a/docs/sphinx/source/whatsnew.rst +++ b/docs/sphinx/source/whatsnew.rst @@ -6,6 +6,7 @@ What's New These are new features and improvements of note in each release. +.. include:: whatsnew/v0.4.3.rst .. include:: whatsnew/v0.4.2.rst .. include:: whatsnew/v0.4.1.rst .. include:: whatsnew/v0.4.0.rst diff --git a/docs/sphinx/source/whatsnew/pending.rst b/docs/sphinx/source/whatsnew/v0.4.3.rst similarity index 97% rename from docs/sphinx/source/whatsnew/pending.rst rename to docs/sphinx/source/whatsnew/v0.4.3.rst index a15aba58..24ac8e03 100644 --- a/docs/sphinx/source/whatsnew/pending.rst +++ b/docs/sphinx/source/whatsnew/v0.4.3.rst @@ -36,6 +36,7 @@ Documentation * Edge effects evaluation tutorial 23, with the new functionality of multiple modules/rows on the same analysis scan. * Updates to example notebooks * Reduce number of digits in makeScene .rad file titles. (:pull:`503`) +* Reduce number of digits saved to files in \results (:pull:`534`) * In the sceneDict reported in the trackerdict, save both `clearance_height` and `hub_height` parameters. (:pull:`503`) Contributors From 51e48c671d13ebc5349fad50df3db9cd52793cd7 Mon Sep 17 00:00:00 2001 From: cdeline Date: Wed, 28 Aug 2024 14:35:58 -0600 Subject: [PATCH 24/33] Add checks for RAYPATH. Fixes #525 --- bifacial_radiance/main.py | 20 +++++++++++++++++- docs/sphinx/source/whatsnew/pending.rst | 28 +++++++++++++++++++++++++ tests/test_bifacial_radiance.py | 16 +++++++++++++- 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 docs/sphinx/source/whatsnew/pending.rst diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py index 4088d6b6..20a5be82 100644 --- a/bifacial_radiance/main.py +++ b/bifacial_radiance/main.py @@ -302,7 +302,24 @@ def _subhourlydatatoGencumskyformat(gencumskydata, label='right'): if (gencumskydata.index.year[-1] == gencumskydata.index.year[-2]+1) and len(gencumskydata)>8760: gencumskydata = gencumskydata[:-1] return gencumskydata - # end _subhourlydatatoGencumskyformat + # end _subhourlydatatoGencumskyformat + +def _checkRaypath(): + # Ensure that os.environ['RAYPATH'] exists and contains current directory '.' + if os.name == 'nt': + splitter = ';' + else: + splitter = ':' + try: + raypath = os.getenv('RAYPATH', default=None) + if not raypath: + raise KeyError() + raysplit = raypath.split(splitter) + if not '.' in raysplit: + os.environ['RAYPATH'] = splitter.join(filter(None, raysplit + ['.'+splitter])) + except (KeyError, AttributeError, TypeError): + raise Exception('No RAYPATH set for RADIANCE. Please check your RADIANCE installation.') + class RadianceObj: @@ -361,6 +378,7 @@ def __init__(self, name=None, path=None, hpc=False): now = datetime.datetime.now() self.nowstr = str(now.date())+'_'+str(now.hour)+str(now.minute)+str(now.second) + _checkRaypath() # make sure we have RADIANCE path set up correctly # DEFAULTS diff --git a/docs/sphinx/source/whatsnew/pending.rst b/docs/sphinx/source/whatsnew/pending.rst new file mode 100644 index 00000000..a086decc --- /dev/null +++ b/docs/sphinx/source/whatsnew/pending.rst @@ -0,0 +1,28 @@ +.. _whatsnew_0440: + +v0.4.4 (XX / XX / 2024) +------------------------ +Bugfix Release ... + + +API Changes +~~~~~~~~~~~~ +* + +Enhancements +~~~~~~~~~~~~ +* Conduct an automated check for proper radiance RAYPATH setting (:issue:`525`) + + +Bug fixes +~~~~~~~~~ +* + +Documentation +~~~~~~~~~~~~~~ +* + +Contributors +~~~~~~~~~~~~ +* Silvana Ayala (:ghuser:`shirubana`) +* Chris Deline (:ghuser:`cdeline`) diff --git a/tests/test_bifacial_radiance.py b/tests/test_bifacial_radiance.py index 7df11da3..2bad7492 100644 --- a/tests/test_bifacial_radiance.py +++ b/tests/test_bifacial_radiance.py @@ -575,4 +575,18 @@ def test_customTrackerAngles(): trackerdict = demo.set1axis(azimuth=90, useMeasuredTrackerAngle=True) assert trackerdict[-20]['count'] == 3440 trackerdict = demo.set1axis(azimuth=90, useMeasuredTrackerAngle=False) - assert trackerdict[-20]['count'] == 37 \ No newline at end of file + assert trackerdict[-20]['count'] == 37 + +def test_raypath(): + # test errors and raypath updates + import re + raypath0 = os.getenv('RAYPATH', default=None) + + os.environ['RAYPATH'] = '' + with pytest.raises(Exception): + bifacial_radiance.main._checkRaypath() + os.environ['RAYPATH'] = 'test' + bifacial_radiance.main._checkRaypath() + assert '.' in re.split(':|;', os.environ['RAYPATH']) + + os.environ['RAYPATH'] = raypath0 \ No newline at end of file From e924ddefeaa995f3f6b91fc1641f861d971d7575 Mon Sep 17 00:00:00 2001 From: cdeline Date: Wed, 28 Aug 2024 15:42:35 -0600 Subject: [PATCH 25/33] Update call to scm_setuptools to avoid git errors. Fixes #535 --- docs/sphinx/source/whatsnew/pending.rst | 2 +- setup.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/sphinx/source/whatsnew/pending.rst b/docs/sphinx/source/whatsnew/pending.rst index a086decc..9c633aa0 100644 --- a/docs/sphinx/source/whatsnew/pending.rst +++ b/docs/sphinx/source/whatsnew/pending.rst @@ -16,7 +16,7 @@ Enhancements Bug fixes ~~~~~~~~~ -* +* pip install setup no longer crashes if command line git is not present. (:issue:`535`) Documentation ~~~~~~~~~~~~~~ diff --git a/setup.py b/setup.py index 9e5e54a0..37909a0a 100644 --- a/setup.py +++ b/setup.py @@ -39,9 +39,7 @@ # the version across setup.py and the project code, see # https://packaging.python.org/en/latest/single_source_version.html # version='0.3.4', - #version=versioneer.get_version(), - #cmdclass=versioneer.get_cmdclass(), - use_scm_version=True, + use_scm_version={"fallback_version":"0.4.3"}, description='Tools to interface with Radiance for the PV researcher', long_description=long_description, long_description_content_type="text/markdown", From 950946007f7afa4d106e3b4d834e10a56e636aef Mon Sep 17 00:00:00 2001 From: cdeline Date: Wed, 28 Aug 2024 16:00:24 -0600 Subject: [PATCH 26/33] update whatsnew/pending.rst --- docs/sphinx/source/whatsnew/pending.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sphinx/source/whatsnew/pending.rst b/docs/sphinx/source/whatsnew/pending.rst index 9c633aa0..9a9bcb95 100644 --- a/docs/sphinx/source/whatsnew/pending.rst +++ b/docs/sphinx/source/whatsnew/pending.rst @@ -11,12 +11,12 @@ API Changes Enhancements ~~~~~~~~~~~~ -* Conduct an automated check for proper radiance RAYPATH setting (:issue:`525`) +* Conduct an automated check for proper radiance RAYPATH setting (:issue:`525`)(:pull:`537`) Bug fixes ~~~~~~~~~ -* pip install setup no longer crashes if command line git is not present. (:issue:`535`) +* 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 ~~~~~~~~~~~~~~ From 0e1647ce2e12dbc098c3be9d70c734f6b6679094 Mon Sep 17 00:00:00 2001 From: cdeline Date: Wed, 28 Aug 2024 16:40:58 -0600 Subject: [PATCH 27/33] Suppress hub-height warnings when both clearance_height and hub_height are passed to internal functions. Fixes #536 --- bifacial_radiance/main.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py index 20a5be82..b10107f1 100644 --- a/bifacial_radiance/main.py +++ b/bifacial_radiance/main.py @@ -168,7 +168,8 @@ def _modDict(originaldict, moddict, relative=False): return newdict -def _heightCasesSwitcher(sceneDict, preferred='hub_height', nonpreferred='clearance_height'): +def _heightCasesSwitcher(sceneDict, preferred='hub_height', nonpreferred='clearance_height', + suppress_warning=False): """ Parameters @@ -184,7 +185,9 @@ def _heightCasesSwitcher(sceneDict, preferred='hub_height', nonpreferred='cleara nonpreferred : TYPE, optional When sceneDict has hub_height and clearance_height, it wil ldelete this nonpreferred option. The default is 'clearance_height'. - + suppress_warning : Bool, default False + If both heights passed in SceneDict, suppress the warning + Returns ------- sceneDict : TYPE @@ -239,9 +242,10 @@ def _heightCasesSwitcher(sceneDict, preferred='hub_height', nonpreferred='cleara del sceneDict[nonpreferred] elif heightCases == '_clearance_height__hub_height__': - print("sceneDict Warning: 'hub_height' and 'clearance_height'"+ - " are being passed. Using "+preferred+ - " and removing "+ nonpreferred) + if not suppress_warning: + print("sceneDict Warning: 'hub_height' and 'clearance_height'"+ + " are being passed. Using "+preferred+ + " and removing "+ nonpreferred) del sceneDict[nonpreferred] else: @@ -4101,7 +4105,8 @@ def _checkSensors(sensors): sceneDict, use_clearanceheight = _heightCasesSwitcher(sceneDict, preferred = 'hub_height', - nonpreferred = 'clearance_height') + nonpreferred = 'clearance_height', + suppress_warning=True) if use_clearanceheight : height = sceneDict['clearance_height'] + 0.5* \ From 14d0c8d950d3689da371d0125ff4a54b9572ff99 Mon Sep 17 00:00:00 2001 From: cdeline Date: Fri, 30 Aug 2024 14:36:26 -0600 Subject: [PATCH 28/33] update whatsnew/pending.rst about removed warnings in moduleAnalysis --- docs/sphinx/source/whatsnew/pending.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/source/whatsnew/pending.rst b/docs/sphinx/source/whatsnew/pending.rst index 9a9bcb95..43d4e7a2 100644 --- a/docs/sphinx/source/whatsnew/pending.rst +++ b/docs/sphinx/source/whatsnew/pending.rst @@ -20,7 +20,7 @@ Bug fixes 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 ~~~~~~~~~~~~ From 4b13d48d68fa1210f03605f13421fec94a7938a5 Mon Sep 17 00:00:00 2001 From: cdeline Date: Fri, 30 Aug 2024 16:15:04 -0600 Subject: [PATCH 29/33] bump to pandas 1.4.4 and numba 0.58.1 in requirements.txt --- docs/sphinx/source/conf.py | 4 ++-- requirements.txt | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/sphinx/source/conf.py b/docs/sphinx/source/conf.py index a7d5a544..03adb108 100644 --- a/docs/sphinx/source/conf.py +++ b/docs/sphinx/source/conf.py @@ -42,9 +42,9 @@ def __getattr__(cls, name): MOCK_MODULES = [] sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES) """ -# import distutils before calling pd.show_versions() +# import distutils before calling pd.show_versions(). not needed for pd >= 1.4.x # https://github.com/pypa/setuptools/issues/3044 -import distutils # noqa: F401 +#import distutils # noqa: F401 import pandas as pd pd.show_versions() diff --git a/requirements.txt b/requirements.txt index fa3d21bb..77b53b66 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,8 +7,9 @@ ipython==8.13.0 kiwisolver==1.4.4 matplotlib==3.5.1 more-itertools==9.1.0 +numba==0.58.1 numpy==1.24.2 -pandas==1.3.5 +pandas==1.4.4 pluggy==1.0.0 pvlib==0.9.4 pvmismatch==4.1 From 585602adc249682ecfcaa13ecbc65d39c532132c Mon Sep 17 00:00:00 2001 From: cdeline Date: Thu, 5 Sep 2024 13:56:35 -0600 Subject: [PATCH 30/33] Update repr for AnalysisObj, start on RadianceObj. --- bifacial_radiance/main.py | 30 +++++++++++++++++++------ docs/sphinx/source/whatsnew/pending.rst | 1 + 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py index b10107f1..81949ea7 100644 --- a/bifacial_radiance/main.py +++ b/bifacial_radiance/main.py @@ -323,10 +323,24 @@ def _checkRaypath(): os.environ['RAYPATH'] = splitter.join(filter(None, raysplit + ['.'+splitter])) except (KeyError, AttributeError, TypeError): raise Exception('No RAYPATH set for RADIANCE. Please check your RADIANCE installation.') - + +class SuperClass: + def __repr__(self): + return str({key: self.__dict__[key] for key in self.columns}) + #return str(self.__dict__) + + @property + def columns(self): + return [attr for attr in dir(self) if not (attr.startswith('_') or attr.startswith('methods') + or attr.startswith('columns') or callable(getattr(self,attr)))] + @property + def methods(self): + return [attr for attr in dir(self) if (not (attr.startswith('_') or attr.startswith('methods') + or attr.startswith('columns')) and callable(getattr(self,attr)))] + -class RadianceObj: +class RadianceObj(SuperClass): """ The RadianceObj top level class is used to work on radiance objects, keep track of filenames, sky values, PV module configuration, etc. @@ -345,7 +359,8 @@ class RadianceObj: """ def __repr__(self): - return str(self.__dict__) + #return str(self.__dict__) + return str({key: self.__dict__[key] for key in self.columns if key != 'trackerdict'}) def __init__(self, name=None, path=None, hpc=False): ''' initialize RadianceObj with path of Radiance materials and objects, @@ -3116,7 +3131,7 @@ def saveImage(self, filename=None, view=None): -class MetObj: +class MetObj(SuperClass): """ Meteorological data from EPW file. @@ -3379,6 +3394,7 @@ def _set1axis(self, azimuth=180, limit_angle=45, angledelta=None, 'surf_azm':self.surface_azimuth[i], 'surf_tilt':self.surface_tilt[i], 'theta':self.tracker_theta[i], + 'dni':self.dni[i], 'ghi':self.ghi[i], 'dhi':self.dhi[i], 'temp_air':self.temp_air[i], @@ -3540,6 +3556,7 @@ def _makeTrackerCSV(self, theta_list, trackingdata): trackerdict[theta]['count'] = datetimetemp.__len__() #Create new temp csv file with zero values for all times not equal to datetimetemp # write 8760 2-column csv: GHI,DHI + dni_temp = [] ghi_temp = [] dhi_temp = [] for g, d, time in zip(self.ghi, self.dhi, @@ -3573,13 +3590,12 @@ def _makeTrackerCSV(self, theta_list, trackingdata): return trackerdict -class AnalysisObj: +class AnalysisObj(SuperClass): """ Analysis class for performing raytrace to obtain irradiance measurements at the array, as well plotting and reporting results. """ - def __repr__(self): - return str(self.__dict__) + def __init__(self, octfile=None, name=None, hpc=False): """ Initialize AnalysisObj by pointing to the octfile. Scan information diff --git a/docs/sphinx/source/whatsnew/pending.rst b/docs/sphinx/source/whatsnew/pending.rst index 43d4e7a2..8ebb8014 100644 --- a/docs/sphinx/source/whatsnew/pending.rst +++ b/docs/sphinx/source/whatsnew/pending.rst @@ -21,6 +21,7 @@ Bug fixes 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`) +* More useful __repr__ output in :py:class:`~bifacial_radiance.AnalysisObj and :py:class:`~bifacial_radiance.MetObj (:issue:`471`) Contributors ~~~~~~~~~~~~ From 28d40c0e114a2ae8621744f22dcafc705326dad1 Mon Sep 17 00:00:00 2001 From: cdeline Date: Thu, 5 Sep 2024 17:02:56 -0600 Subject: [PATCH 31/33] Make a nice __repr__ for MetObj class. fixes #471. --- bifacial_radiance/main.py | 40 +++++++++++++++++++++++++++++---- bifacial_radiance/module.py | 3 ++- tests/test_bifacial_radiance.py | 4 ++-- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py index 81949ea7..d6a44a78 100644 --- a/bifacial_radiance/main.py +++ b/bifacial_radiance/main.py @@ -328,7 +328,6 @@ class SuperClass: def __repr__(self): return str({key: self.__dict__[key] for key in self.columns}) #return str(self.__dict__) - @property def columns(self): return [attr for attr in dir(self) if not (attr.startswith('_') or attr.startswith('methods') @@ -2833,7 +2832,7 @@ def _makeGroundString(self, index=0, cumulativesky=False): -class SceneObj: +class SceneObj(SuperClass): ''' Scene information including PV module type, bifaciality, array info pv module orientation defaults: Azimuth = 180 (south) @@ -2855,7 +2854,7 @@ class SceneObj: ''' def __repr__(self): - return str(self.__dict__) + return 'SceneObj:\n'+str({key: self.__dict__[key] for key in self.columns}) def __init__(self, module=None, name=None): ''' initialize SceneObj ''' @@ -3155,6 +3154,28 @@ class MetObj(SuperClass): SAM and PVSyst use left-labeled interval data and NSRDB uses centered. """ + @property + def tmydata(self): + keys = ['ghi', 'dhi', 'dni', 'albedo', 'dewpoint', 'pressure', + 'temp_air', 'wind_speed', 'meastracker_angle', 'tracker_theta', + 'surface_tilt', 'surface_azimuth'] + return pd.DataFrame({key:self.__dict__.get(key, None) for key in keys }, + index = self.__dict__['datetime']).dropna(axis=1) + + @property + def metadata(self): + keys = ['latitude', 'longitude', 'elevation', 'timezone', 'city', 'label', + 'timezone'] + return {key:self.__dict__.get(key, None) for key in keys} + + def __repr__(self): + # return metadata and tmydata stats... + import io + buf = io.StringIO() + self.tmydata.info(memory_usage=False, buf=buf) + tmyinfo = buf.getvalue() + buf.close() + return f'\nMetObj.metadata:\n {self.metadata}\nMetObj.tmydata:\n {tmyinfo}\n' def __init__(self, tmydata, metadata, label = 'right'): @@ -3595,7 +3616,18 @@ class AnalysisObj(SuperClass): Analysis class for performing raytrace to obtain irradiance measurements at the array, as well plotting and reporting results. """ - + def __printval__(self, attr): + try: + t = type(getattr(self,attr, None)[0]) + except TypeError: + t = None + if t is float: + return np.array(getattr(self,attr)).round(3).tolist() + else: + return getattr(self,attr) + + def __repr__(self): + return 'AnalysisObj:\n' + str({key: self.__printval__(key) for key in self.columns}) def __init__(self, octfile=None, name=None, hpc=False): """ Initialize AnalysisObj by pointing to the octfile. Scan information diff --git a/bifacial_radiance/module.py b/bifacial_radiance/module.py index fc615a18..2504ff99 100644 --- a/bifacial_radiance/module.py +++ b/bifacial_radiance/module.py @@ -27,7 +27,8 @@ class ModuleObj(SuperClass): Pass this object into makeScene or makeScene1axis. """ - + def __repr__(self): + return 'ModuleObj:\n' + str(self.getDataDict()) def __init__(self, name=None, x=None, y=None, z=None, bifi=1, modulefile=None, text=None, customtext='', xgap=0.01, ygap=0.0, zgap=0.1, numpanels=1, rewriteModulefile=True, cellModule=None, diff --git a/tests/test_bifacial_radiance.py b/tests/test_bifacial_radiance.py index 2bad7492..0b114fd0 100644 --- a/tests/test_bifacial_radiance.py +++ b/tests/test_bifacial_radiance.py @@ -48,7 +48,7 @@ def test_RadianceObj_set1axis(): # test set1axis. requires metdata for boulder. name = "_test_set1axis" demo = bifacial_radiance.RadianceObj(name) - assert str(demo)[-16:-2]==name #this depends on the insertion order of the dictionary repr of demo - may not be consistent + assert len(str(demo)) > 300 # Make sure something is printed out here for demo.__repr__ #try: # epwfile = demo.getEPW(lat=40.01667, lon=-105.25) # From EPW: {N 40° 1'} {W 105° 15'} #except: # adding an except in case the internet connection in the lab forbids the epw donwload. @@ -322,7 +322,7 @@ def test_AnalysisObj_linePtsMake3D(): linepts = analysis._linePtsMake3D(0,0,0,1,1,1,0,0,0,1,2,3,'0 1 0') assert linepts == '0 0 0 0 1 0 \r1 1 1 0 1 0 \r0 0 0 0 1 0 \r1 1 1 0 1 0 \r0 0 0 0 1 0 \r1 1 1 0 1 0 \r' # v2.5.0 new linepts because now x and z also increase not only y. #assert linepts == '0 0 0 0 1 0 \r0 1 0 0 1 0 \r0 0 1 0 1 0 \r0 1 1 0 1 0 \r0 0 2 0 1 0 \r0 1 2 0 1 0 \r' - assert str(analysis)[12:16]=='None' + assert str(analysis)[-5:-1]=='None' # this depends on the order of the dict. but generally aligns with 'octfile' in alphabetical order.. def test_gendaylit2manual(): From c26715047ea7cf78cc6297fd9e4d254e18e7915e Mon Sep 17 00:00:00 2001 From: cdeline Date: Fri, 6 Sep 2024 14:39:31 -0600 Subject: [PATCH 32/33] More consistent __repr__ for different classes. more pytests --- bifacial_radiance/main.py | 14 +++++++------- bifacial_radiance/module.py | 2 +- tests/test_bifacial_radiance.py | 9 +++++++++ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py index d6a44a78..f312129d 100644 --- a/bifacial_radiance/main.py +++ b/bifacial_radiance/main.py @@ -326,7 +326,7 @@ def _checkRaypath(): class SuperClass: def __repr__(self): - return str({key: self.__dict__[key] for key in self.columns}) + return str(type(self)) + ' : ' + str({key: self.__dict__[key] for key in self.columns}) #return str(self.__dict__) @property def columns(self): @@ -359,7 +359,7 @@ class RadianceObj(SuperClass): """ def __repr__(self): #return str(self.__dict__) - return 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'}) def __init__(self, name=None, path=None, hpc=False): ''' initialize RadianceObj with path of Radiance materials and objects, @@ -2636,7 +2636,7 @@ def analysis1axis(self, trackerdict=None, singleindex=None, accuracy='low', # End RadianceObj definition -class GroundObj: +class GroundObj(SuperClass): """ Class to set and return details for the ground surface materials and reflectance. If 1 albedo value is passed, it is used as default. @@ -2853,8 +2853,7 @@ class SceneObj(SuperClass): ------- ''' - def __repr__(self): - return 'SceneObj:\n'+str({key: self.__dict__[key] for key in self.columns}) + def __init__(self, module=None, name=None): ''' initialize SceneObj ''' @@ -3175,7 +3174,8 @@ def __repr__(self): self.tmydata.info(memory_usage=False, buf=buf) tmyinfo = buf.getvalue() buf.close() - return f'\nMetObj.metadata:\n {self.metadata}\nMetObj.tmydata:\n {tmyinfo}\n' + return f".metadata:\n"\ + f"{self.metadata}\n.tmydata:\n {tmyinfo}\n" def __init__(self, tmydata, metadata, label = 'right'): @@ -3627,7 +3627,7 @@ def __printval__(self, attr): return getattr(self,attr) def __repr__(self): - return 'AnalysisObj:\n' + str({key: self.__printval__(key) for key in self.columns}) + return str(type(self)) + ' : ' + str({key: self.__printval__(key) for key in self.columns}) def __init__(self, octfile=None, name=None, hpc=False): """ Initialize AnalysisObj by pointing to the octfile. Scan information diff --git a/bifacial_radiance/module.py b/bifacial_radiance/module.py index 2504ff99..32fccfa9 100644 --- a/bifacial_radiance/module.py +++ b/bifacial_radiance/module.py @@ -28,7 +28,7 @@ class ModuleObj(SuperClass): """ def __repr__(self): - return 'ModuleObj:\n' + str(self.getDataDict()) + 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='', xgap=0.01, ygap=0.0, zgap=0.1, numpanels=1, rewriteModulefile=True, cellModule=None, diff --git a/tests/test_bifacial_radiance.py b/tests/test_bifacial_radiance.py index 0b114fd0..db7c5d8c 100644 --- a/tests/test_bifacial_radiance.py +++ b/tests/test_bifacial_radiance.py @@ -196,6 +196,7 @@ def test_1axis_gencumSky(): demo = bifacial_radiance.RadianceObj(name) # Create a RadianceObj 'object' demo.setGround(albedo) # input albedo number or material name like 'concrete'. To see options, run this without any input. + assert demo.ground.methods == ['printGroundMaterials'] metdata = demo.readWeatherFile(weatherFile=MET_FILENAME, starttime='01_01_01', endtime = '01_01_23', coerce_year=2001) # read in the EPW weather data from above moduleText = '! genbox black test-module 0.98 1.95 0.02 | xform -t -0.49 -2.0 0 -a 2 -t 0 2.05 0' module=demo.makeModule(name='test-module',x=0.984,y=1.95, numpanels = 2, ygap = 0.1, text=moduleText) @@ -374,6 +375,7 @@ def test_left_label_metdata(): # right labeled MetObj import pvlib import pandas as pd + import unittest (tmydata, metadata) = pvlib.iotools.epw.read_epw(MET_FILENAME, coerce_year=2001) # rename different field parameters to match output from # pvlib.tmy.readtmy: DNI, DHI, DryBulb, Wspd @@ -385,6 +387,12 @@ def test_left_label_metdata(): 'albedo':'Alb' }, inplace=True) metdata1 = bifacial_radiance.MetObj(tmydata, metadata, label='left') + columnlist = ['ghi', 'dhi', 'dni', 'albedo', 'dewpoint', 'pressure', 'temp_air','wind_speed'] + assert all([col in list(metdata1.tmydata.columns) for col in columnlist]) + metadatalist = ['city', 'elevation', 'label', 'latitude', 'longitude', 'timezone'] + assert all([col in list(metdata1.metadata.keys()) for col in metadatalist]) + + demo = bifacial_radiance.RadianceObj('test') metdata2 = demo.readWeatherFile(weatherFile=MET_FILENAME, label='right', coerce_year=2001) pd.testing.assert_frame_equal(metdata1.solpos[:-1], metdata2.solpos[:-1]) @@ -417,6 +425,7 @@ def test_analyzeRow(): assert rowscan[rowscan.keys()[2]][0][0] == rowscan[rowscan.keys()[2]][1][0] # Assert Y is different for two different modules assert rowscan[rowscan.keys()[1]][0][0]+2 == rowscan[rowscan.keys()[1]][1][0] + assert (analysis.__printval__('x')[1] == 0) & (analysis.x[1] != 0) def test_addMaterialGroundRad(): From 0fc5040a056363ce182559b23aab797a5e2bec38 Mon Sep 17 00:00:00 2001 From: cdeline Date: Fri, 6 Sep 2024 14:59:11 -0600 Subject: [PATCH 33/33] more pytests... --- tests/test_module.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_module.py b/tests/test_module.py index c820668a..ba17d9f1 100644 --- a/tests/test_module.py +++ b/tests/test_module.py @@ -59,12 +59,14 @@ def test_CellLevelModule(): module.addCellModule(**cellParams, centerJB=0.01) #centerJB simulations still under development. # assert module.text == '! genbox black cellPVmodule 0.156 0.156 0.02 | xform -t -0.44 -0.87 0 -a 6 -t 0.176 0 0 -a 5.0 -t 0 0.176 0 -a 2 -t 0 0.772 0 | xform -t 0 0.181 0 -a 1 -t 0 1.73 0' - + assert len(module.cellModule.__repr__()) == 119 + assert len(module.__repr__()) > 490 def test_TorqueTubes_Module(): name = "_test_TorqueTubes" demo = bifacial_radiance.RadianceObj(name) # Create a RadianceObj 'object' - module = demo.makeModule(name='square', y=0.95,x=1.59, tubeParams={'tubetype':'square', 'axisofrotation':False}, hpc=True) #suppress saving .json + # test pre-0.4.0 compatibility keys 'bool' and 'torqueTubeMaterial'. Remove these when it's deprecated.. + module = demo.makeModule(name='square', y=0.95,x=1.59, tubeParams={'torqueTubeMaterial':'Metal_Grey','bool':True, 'tubetype':'square', 'axisofrotation':False}, hpc=True) #suppress saving .json assert module.x == 1.59 assert module.text == '! genbox black square 1.59 0.95 0.02 | xform -t -0.795 -0.475 0 -a 1 -t 0 0.95 0\r\n! genbox Metal_Grey tube1 1.6 0.1 0.1 | xform -t -0.8 -0.05 -0.2' module = demo.makeModule(name='round', y=0.95,x=1.59, tubeParams={'tubetype':'round', 'axisofrotation':False}, hpc=True)