From 90d4da2b54c780078fa6af633ca052764db530ff Mon Sep 17 00:00:00 2001 From: cdeline Date: Fri, 17 Sep 2021 15:15:43 -0600 Subject: [PATCH] Revert fixed_tilt_azimuth input parameter. Minor docs cleanup. (#330) * Revert fixed_tilt_azimuth input parameter. * deprecate axis_azimuth in set1axis. Replace calls to axis_azimuth with azimuth in _set1axis and _getTrackingAngles --- bifacial_radiance/main.py | 117 ++++++++++++++------------------ bifacial_radiance/modelchain.py | 13 ++-- tests/test_bifacial_radiance.py | 4 +- 3 files changed, 60 insertions(+), 74 deletions(-) diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py index f7be991f..0254e8e5 100644 --- a/bifacial_radiance/main.py +++ b/bifacial_radiance/main.py @@ -1244,8 +1244,8 @@ def _readSOLARGIS(self, solargisfile=None, label = 'center'): def getSingleTimestampTrackerAngle(self, metdata, timeindex, gcr=None, - axis_azimuth=180, axis_tilt=0, - limit_angle=60, backtrack=True): + azimuth=180, axis_tilt=0, + limit_angle=45, backtrack=True): """ Helper function to calculate a tracker's angle for use with the fixed tilt routines of bifacial_radiance. It calculates tracker angle for @@ -1262,7 +1262,7 @@ def getSingleTimestampTrackerAngle(self, metdata, timeindex, gcr=None, Index between 0 to 8760 indicating hour to simulate. gcr : float Ground coverage ratio for calculation backtracking. Defualt [1.0/3.0] - axis_azimuth : float or int + azimuth : float or int Orientation axis of tracker torque tube. Default North-South (180 deg) axis_tilt : float or int Default 0. Axis tilt -- not implemented in sensors locations so it's pointless @@ -1287,7 +1287,7 @@ def getSingleTimestampTrackerAngle(self, metdata, timeindex, gcr=None, sunaz = float(solpos.azimuth) # not substracting the 180 trackingdata = pvlib.tracking.singleaxis(sunzen, sunaz, - axis_tilt, axis_azimuth, + axis_tilt, azimuth, limit_angle, backtrack, gcr) tracker_theta = float(np.round(trackingdata['tracker_theta'],2)) @@ -1587,9 +1587,9 @@ def genCumSky(self, temp_metdatafile=None, savefile=None): return skyname - def set1axis(self, metdata=None, axis_azimuth=180, limit_angle=45, + def set1axis(self, metdata=None, azimuth=180, limit_angle=45, angledelta=5, backtrack=True, gcr=1.0 / 3, cumulativesky=True, - fixed_tilt_angle=None, fixed_tilt_azimuth=None): + fixed_tilt_angle=None, axis_azimuth=None): """ Set up geometry for 1-axis tracking. Pull in tracking angle details from pvlib, create multiple 8760 metdata sub-files where datetime of met data @@ -1603,8 +1603,9 @@ def set1axis(self, metdata=None, axis_azimuth=180, limit_angle=45, Meterological object to set up geometry. Usually set automatically by `bifacial_radiance` after running :py:class:`bifacial_radiance.readepw`. Default = self.metdata - axis_azimuth : numeric - Orientation axis of tracker torque tube. Default North-South (180 deg) + azimuth : numeric + Orientation axis of tracker torque tube. Default North-South (180 deg). + For fixed-tilt configuration, input is fixed azimuth (180 is south) limit_angle : numeric Limit angle (+/-) of the 1-axis tracker in degrees. Default 45 angledelta : numeric @@ -1620,14 +1621,11 @@ def set1axis(self, metdata=None, axis_azimuth=180, limit_angle=45, created with constant tilt angle for the cumulativesky approach. if false, the gendaylit tracking approach must be used. fixed_tilt_angle : numeric - If passed, this changes to a fixed tilt - simulation where each hour uses fixed_tilt_angle - and axis_azimuth as the tilt and azimuth - fixed_tilt_azimuth : numeric - If fixed_tilt_angle passed, this sets the azimuth angle of the - modules' surface for a fixed tilt simulation. South = 180. - - + If passed, this changes to a fixed tilt simulation where each hour + uses fixed_tilt_angle and axis_azimuth as the tilt and azimuth + axis_azimuth : numeric + DEPRECATED. returns deprecation warning. Pass the tracker + axis_azimuth through to azimuth input instead. Returns ------- @@ -1646,6 +1644,7 @@ def set1axis(self, metdata=None, axis_azimuth=180, limit_angle=45, # metdata.surface_azimuth list of tracker azimuth data # metdata.surface_tilt list of tracker surface tilt data # metdata.tracker_theta list of tracker tilt angle + import warnings if metdata == None: metdata = self.metdata @@ -1654,25 +1653,27 @@ def set1axis(self, metdata=None, axis_azimuth=180, limit_angle=45, raise Exception("metdata doesnt exist yet. "+ "Run RadianceObj.readWeatherFile() ") - + if axis_azimuth: + azimuth = axis_azimuth + warnings.warn("axis_azimuth is deprecated in set1axis; use azimuth " + "input instead.", DeprecationWarning) + #backtrack = True # include backtracking support in later version #gcr = 1.0/3.0 # default value - not used if backtrack = False. # get 1-axis tracker angles for this location, rounded to nearest 'angledelta' trackerdict = metdata._set1axis(cumulativesky=cumulativesky, - axis_azimuth=axis_azimuth, + azimuth=azimuth, limit_angle=limit_angle, angledelta=angledelta, backtrack=backtrack, gcr=gcr, - fixed_tilt_angle=fixed_tilt_angle, - fixed_tilt_azimuth = fixed_tilt_azimuth + fixed_tilt_angle=fixed_tilt_angle ) self.trackerdict = trackerdict self.cumulativesky = cumulativesky - return trackerdict def gendaylit1axis(self, metdata=None, trackerdict=None, startdate=None, @@ -1711,7 +1712,7 @@ def gendaylit1axis(self, metdata=None, trackerdict=None, startdate=None, print('No trackerdict value passed or available in self') if startdate is not None or enddate is not None: - print("Deprecation WArning: gendyalit1axis no longer downselects"+ + print("Deprecation Warning: gendyalit1axis no longer downselects"+ "Entries by stardate and enddate. Downselect your data"+ "when loading with readWeatherFile") @@ -3579,12 +3580,10 @@ def _makeSceneNxR(self, moduletype=None, sceneDict=None, radname=None, hpc=False Separation between rows tilt : numeric Valid input ranges -90 to 90 degrees - axis_azimuth : numeric + azimuth : numeric A value denoting the compass direction along which the axis of rotation lies. Measured in decimal degrees East of North. [0 to 180) possible. - If azimuth is passed, a warning is given and Axis Azimuth and - tilt are re-calculated. nMods : int Number of modules per row (default = 20) nRows : int @@ -3904,12 +3903,12 @@ def __init__(self, tmydata, metadata, label = 'right'): self.solpos = pvlib.irradiance.solarposition.get_solarposition(sunup['corrected_timestamp'],lat,lon,elev) self.sunrisesetdata=sunup - - - def _set1axis(self, axis_azimuth=180, limit_angle=45, angledelta=None, + + + def _set1axis(self, azimuth=180, limit_angle=45, angledelta=None, backtrack=True, gcr = 1.0/3.0, cumulativesky=True, - fixed_tilt_angle=None, fixed_tilt_azimuth=None, - axis_tilt = 0): + fixed_tilt_angle=None, axis_tilt = 0): + """ Set up geometry for 1-axis tracking cumulativesky. Solpos data already stored in `metdata.solpos`. Pull in tracking angle details from @@ -3922,12 +3921,11 @@ def _set1axis(self, axis_azimuth=180, limit_angle=45, angledelta=None, Whether individual csv files are created with constant tilt angle for the cumulativesky approach. if false, the gendaylit tracking approach must be used. - axis_azimuth : numerical + azimuth : numerical orientation axis of tracker torque tube. Default North-South (180 deg) - For fixed tilt simulations (angledelta=0) this is the orientation azimuth + For fixed tilt simulations this is the orientation azimuth limit_angle : numerical +/- limit angle of the 1-axis tracker in degrees. Default 45 - For fixed tilt simulations (angledelta=0) this is the tilt angle angledelta : numerical Degree of rotation increment to parse irradiance bins. Default 5 degrees (0.4 % error for DNI). @@ -3942,12 +3940,8 @@ def _set1axis(self, axis_azimuth=180, limit_angle=45, angledelta=None, the scene geometry creation of the trackers does not support tilte axis_trackers yet (but can be done manuallyish. See Tutorials) fixed_tilt_angle : numeric - If passed, this changes to a fixed tilt - simulation where each hour uses fixed_tilt_angle - and axis_azimuth as the tilt and azimuth - fixed_tilt_azimuth : numeric - If fixed_tilt_angle passed, this sets the azimuth angle of the - modules' surface for a fixed tilt simulation. South = 180. + If passed, this changes to a fixed tilt simulation where each hour + uses fixed_tilt_angle and azimuth as the tilt and azimuth Returns ------- @@ -3956,7 +3950,7 @@ def _set1axis(self, axis_azimuth=180, limit_angle=45, angledelta=None, list of csv metfile, and datetimes at that angle trackerdict[angle]['csvfile';'surf_azm';'surf_tilt';'UTCtime'] metdata.solpos : dataframe - Pandas dataframe with output from pvlib solar position for each timestep + Dataframe with output from pvlib solar position for each timestep metdata.sunrisesetdata : Pandas dataframe with sunrise, sunset and adjusted time data. metdata.tracker_theta : list @@ -3975,14 +3969,13 @@ def _set1axis(self, axis_azimuth=180, limit_angle=45, angledelta=None, # get 1-axis tracker angles for this location, # round to nearest 'angledelta' - trackingdata = self._getTrackingAngles(axis_azimuth, + trackingdata = self._getTrackingAngles(azimuth, limit_angle, angledelta, axis_tilt = axis_tilt, backtrack = backtrack, gcr = gcr, - fixed_tilt_angle=fixed_tilt_angle, - fixed_tilt_azimuth=fixed_tilt_azimuth) + fixed_tilt_angle=fixed_tilt_angle) # get list of unique rounded tracker angles theta_list = trackingdata.dropna()['theta_round'].unique() @@ -4012,10 +4005,9 @@ def _set1axis(self, axis_azimuth=180, limit_angle=45, angledelta=None, return trackerdict - def _getTrackingAngles(self, axis_azimuth=180, limit_angle=45, + def _getTrackingAngles(self, azimuth=180, limit_angle=45, angledelta=None, axis_tilt=0, backtrack=True, - gcr = 1.0/3.0, fixed_tilt_angle=None, - fixed_tilt_azimuth=None): + gcr = 1.0/3.0, fixed_tilt_angle=None): ''' Helper subroutine to return 1-axis tracker tilt and azimuth data. @@ -4027,26 +4019,23 @@ def _getTrackingAngles(self, axis_azimuth=180, limit_angle=45, cumulativesky simulations. Other input options: None (no rounding of tracker angle) fixed_tilt_angle : (Optional) degrees - This changes to a fixed tilt simulation where each hour uses fixed_tilt_angle - and axis_azimuth as the tilt and azimuth - fixed_tilt_azimuth : numeric - If fixed_tilt_angle passed, this sets the azimuth angle of the - modules' surface for a fixed tilt simulation. South = 180. + This changes to a fixed tilt simulation where each hour uses + fixed_tilt_angle and azimuth as the tilt and azimuth Returns ------- DataFrame with the following columns: * tracker_theta: The rotation angle of the tracker. - tracker_theta = 0 is horizontal, and positive rotation angles are - clockwise. + tracker_theta = 0 is horizontal, and positive rotation angles + are clockwise. * aoi: The angle-of-incidence of direct irradiance onto the rotated panel surface. * surface_tilt: The angle between the panel surface and the earth surface, accounting for panel rotation. * surface_azimuth: The azimuth of the rotated panel, determined by - projecting the vector normal to the panel's surface to the earth's - surface. - * 'theta_round' : tracker_theta rounded to the nearest 'angledelta'. + projecting the vector normal to the panel's surface to the + earth's surface. + * 'theta_round' : tracker_theta rounded to the nearest 'angledelta' If no angledelta is specified, it is rounded to the nearest degree. ''' import pvlib @@ -4059,22 +4048,18 @@ def _getTrackingAngles(self, axis_azimuth=180, limit_angle=45, #New as of 0.3.2: pass fixed_tilt_angle and switches to FIXED TILT mode if fixed_tilt_angle is not None: - if fixed_tilt_azimuth is None: - print("No Azimuth passed for fixed tilt routine in TrackerDict"+ - "Setting module surface to South azimuth (180)") - fixed_tilt_azimuth = 180 # fixed tilt system with tilt = fixed_tilt_angle and - # azimuth = axis_azimuth + # azimuth = azimuth pvsystem = pvlib.pvsystem.PVSystem(arrays=None, surface_tilt=fixed_tilt_angle, - surface_azimuth=axis_azimuth) + surface_azimuth=azimuth) # trackingdata keys: 'tracker_theta', 'aoi', 'surface_azimuth', 'surface_tilt' trackingdata = pd.DataFrame({'tracker_theta':fixed_tilt_angle, 'aoi':pvsystem.get_aoi( solpos['zenith'], solpos['azimuth']), - 'surface_azimuth':fixed_tilt_azimuth, + 'surface_azimuth':azimuth, 'surface_tilt':fixed_tilt_angle}) else: # get 1-axis tracker tracker_theta, surface_tilt and surface_azimuth @@ -4083,7 +4068,7 @@ def _getTrackingAngles(self, axis_azimuth=180, limit_angle=45, trackingdata = pvlib.tracking.singleaxis(solpos['zenith'], solpos['azimuth'], axis_tilt, - axis_azimuth, + azimuth, limit_angle, backtrack, gcr) @@ -4585,8 +4570,8 @@ def moduleAnalysis(self, scene, modWanted=None, rowWanted=None, sensorsy_back=None, sensorsy_front=None, sensorsx_back=1.0, sensorsx_front=None, frontsurfaceoffset=0.001, backsurfaceoffset=0.001, - modscanfront=None, modscanback=None, relative=False, debug=False, - sensorsy=None): + modscanfront=None, modscanback=None, relative=False, + debug=False, sensorsy=None): """ Handler function that decides how to handle different number of front diff --git a/bifacial_radiance/modelchain.py b/bifacial_radiance/modelchain.py index fa6b9920..fe9c809c 100644 --- a/bifacial_radiance/modelchain.py +++ b/bifacial_radiance/modelchain.py @@ -148,13 +148,14 @@ def runModelChain(simulationParamsDict, sceneParamsDict, timeControlParamsDict=N trackerdict = demo.set1axis(metdata, cumulativesky=simulationParamsDict["cumulativeSky"], fixed_tilt_angle=sceneParamsDict['tilt'], - fixed_tilt_azimuth=sceneParamsDict['azimuth']) + azimuth=sceneParamsDict['azimuth']) else: trackerdict = demo.set1axis(metdata, gcr=sceneParamsDict['gcr'], - limit_angle=trackingParamsDict['limit_angle'], - angledelta=trackingParamsDict['angle_delta'], - backtrack=trackingParamsDict['backtrack'], - cumulativesky=simulationParamsDict["cumulativeSky"]) + azimuth=sceneParamsDict['axis_azimuth'], + limit_angle=trackingParamsDict['limit_angle'], + angledelta=trackingParamsDict['angle_delta'], + backtrack=trackingParamsDict['backtrack'], + cumulativesky=simulationParamsDict["cumulativeSky"]) @@ -168,7 +169,7 @@ def runModelChain(simulationParamsDict, sceneParamsDict, timeControlParamsDict=N sceneDict=sceneParamsDict, cumulativesky=simulationParamsDict['cumulativeSky']) - trackerdict = demo.makeOct1axis(trackerdict=trackerdict,) + trackerdict = demo.makeOct1axis(trackerdict=trackerdict) trackerdict = demo.analysis1axis(trackerdict=trackerdict, modWanted=analysisParamsDict['modWanted'], diff --git a/tests/test_bifacial_radiance.py b/tests/test_bifacial_radiance.py index 61b67aa9..99e63de3 100644 --- a/tests/test_bifacial_radiance.py +++ b/tests/test_bifacial_radiance.py @@ -140,13 +140,13 @@ def test_RadianceObj_1axis_gendaylit_end_to_end(): 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. - metdata = demo.readWeatherFile(MET_FILENAME, starttime='01_01_01_01_00', endtime = '01_01_01_23_00', coerce_year = 2001) # read in the weather data from above + metdata = demo.readWeatherFile(MET_FILENAME, starttime='2001-01-01_0100', endtime = '2001-01-01_2300', coerce_year = 2001) # read in the weather data from above #metdata = demo.readEPW(MET_FILENAME, starttime='01_01_01', endtime = '01_01_23') # read in the EPW weather data from above # set module type to be used and passed into makeScene1axis # test modules with gap and rear tube moduleDict=demo.makeModule(name='test',x=0.984,y=1.95,torquetube = True, numpanels = 2, ygap = 0.1) sceneDict = {'pitch': np.round(moduleDict['sceney'] / gcr,3),'height':hub_height, 'nMods':10, 'nRows':3} - key = '01_01_01_11_00' + key = '2001-01-01_1100' # create metdata files for each condition. keys are timestamps for gendaylit workflow trackerdict = demo.set1axis(cumulativesky = False, gcr=gcr) # create the skyfiles needed for 1-axis tracking