def test_slope_aware_backtracking(): """ Test validation data set from https://www.nrel.gov/docs/fy20osti/76626.pdf """ index = pd.date_range('2019-01-01T08:00', '2019-01-01T17:00', freq='h') index = index.tz_localize('Etc/GMT+5') expected_data = pd.DataFrame(index=index, data=[ (2.404287, 122.79177, -84.440, -10.899), (11.263058, 133.288729, -72.604, -25.747), (18.733558, 145.285552, -59.861, -59.861), (24.109076, 158.939435, -45.578, -45.578), (26.810735, 173.931802, -28.764, -28.764), (26.482495, 189.371536, -8.475, -8.475), (23.170447, 204.13681, 15.120, 15.120), (17.296785, 217.446538, 39.562, 39.562), (9.461862, 229.102218, 61.587, 32.339), (0.524817, 239.330401, 79.530, 5.490), ], columns=[ 'ApparentElevation', 'SolarAzimuth', 'TrueTracking', 'Backtracking' ]) expected_axis_tilt = 9.666 expected_slope_angle = -2.576 slope_azimuth, slope_tilt = 180.0, 10.0 axis_azimuth = 195.0 axis_tilt = tracking.calc_axis_tilt(slope_azimuth, slope_tilt, axis_azimuth) assert np.isclose(axis_tilt, expected_axis_tilt, rtol=1e-3, atol=1e-3) cross_axis_tilt = tracking.calc_cross_axis_tilt(slope_azimuth, slope_tilt, axis_azimuth, axis_tilt) assert np.isclose(cross_axis_tilt, expected_slope_angle, rtol=1e-3, atol=1e-3) sat = tracking.singleaxis(90.0 - expected_data['ApparentElevation'], expected_data['SolarAzimuth'], axis_tilt, axis_azimuth, max_angle=90.0, backtrack=True, gcr=0.5, cross_axis_tilt=cross_axis_tilt) assert_series_equal(sat['tracker_theta'], expected_data['Backtracking'].rename('tracker_theta'), check_less_precise=True) truetracking = tracking.singleaxis(90.0 - expected_data['ApparentElevation'], expected_data['SolarAzimuth'], axis_tilt, axis_azimuth, max_angle=90.0, backtrack=False, gcr=0.5, cross_axis_tilt=cross_axis_tilt) assert_series_equal(truetracking['tracker_theta'], expected_data['TrueTracking'].rename('tracker_theta'), check_less_precise=True)
def test_slope_aware_backtracking(): """ Test validation data set from https://www.nrel.gov/docs/fy20osti/76626.pdf """ expected_data = np.array( [('2019-01-01T08:00-0500', 2.404287, 122.79177, -84.440, -10.899), ('2019-01-01T09:00-0500', 11.263058, 133.288729, -72.604, -25.747), ('2019-01-01T10:00-0500', 18.733558, 145.285552, -59.861, -59.861), ('2019-01-01T11:00-0500', 24.109076, 158.939435, -45.578, -45.578), ('2019-01-01T12:00-0500', 26.810735, 173.931802, -28.764, -28.764), ('2019-01-01T13:00-0500', 26.482495, 189.371536, -8.475, -8.475), ('2019-01-01T14:00-0500', 23.170447, 204.13681, 15.120, 15.120), ('2019-01-01T15:00-0500', 17.296785, 217.446538, 39.562, 39.562), ('2019-01-01T16:00-0500', 9.461862, 229.102218, 61.587, 32.339), ('2019-01-01T17:00-0500', 0.524817, 239.330401, 79.530, 5.490)], dtype=[('Time', '<M8[h]'), ('ApparentElevation', '<f8'), ('SolarAzimuth', '<f8'), ('TrueTracking', '<f8'), ('Backtracking', '<f8')]) expected_axis_tilt = 9.666 expected_slope_angle = -2.576 slope_azimuth, slope_tilt = 180.0, 10.0 axis_azimuth = 195.0 axis_tilt = tracking.calc_axis_tilt(slope_azimuth, slope_tilt, axis_azimuth) assert np.isclose(axis_tilt, expected_axis_tilt, rtol=1e-3, atol=1e-3) cross_axis_tilt = tracking.calc_cross_axis_tilt(slope_azimuth, slope_tilt, axis_azimuth, axis_tilt) assert np.isclose(cross_axis_tilt, expected_slope_angle, rtol=1e-3, atol=1e-3) sat = tracking.singleaxis(90.0 - expected_data['ApparentElevation'], expected_data['SolarAzimuth'], axis_tilt, axis_azimuth, max_angle=90.0, backtrack=True, gcr=0.5, cross_axis_tilt=cross_axis_tilt) np.testing.assert_allclose(sat['tracker_theta'], expected_data['Backtracking'], rtol=1e-3, atol=1e-3) truetracking = tracking.singleaxis(90.0 - expected_data['ApparentElevation'], expected_data['SolarAzimuth'], axis_tilt, axis_azimuth, max_angle=90.0, backtrack=False, gcr=0.5, cross_axis_tilt=cross_axis_tilt) np.testing.assert_allclose(truetracking['tracker_theta'], expected_data['TrueTracking'], rtol=1e-3, atol=1e-3)
def test_calc_axis_tilt(): # expected values expected_axis_tilt = 2.239 # [degrees] expected_side_slope = 9.86649274360294 # [degrees] expected = DATA_DIR / 'singleaxis_tracker_wslope.csv' expected = pd.read_csv(expected, index_col='timestamp', parse_dates=True) # solar positions starttime = '2017-01-01T00:30:00-0300' stoptime = '2017-12-31T23:59:59-0300' lat, lon = -27.597300, -48.549610 times = pd.DatetimeIndex(pd.date_range(starttime, stoptime, freq='H')) solpos = pvlib.solarposition.get_solarposition(times, lat, lon) # singleaxis tracker w/slope data slope_azimuth, slope_tilt = 77.34, 10.1149 axis_azimuth = 0.0 max_angle = 75.0 # Note: GCR is relative to horizontal distance between rows gcr = 0.33292759 # GCR = length / horizontal_pitch = 1.64 / 5 / cos(9.86) # calculate tracker axis zenith axis_tilt = tracking.calc_axis_tilt(slope_azimuth, slope_tilt, axis_azimuth=axis_azimuth) assert np.isclose(axis_tilt, expected_axis_tilt) # calculate cross-axis tilt and relative rotation cross_axis_tilt = tracking.calc_cross_axis_tilt(slope_azimuth, slope_tilt, axis_azimuth, axis_tilt) assert np.isclose(cross_axis_tilt, expected_side_slope) sat = tracking.singleaxis(solpos.apparent_zenith, solpos.azimuth, axis_tilt, axis_azimuth, max_angle, backtrack=True, gcr=gcr, cross_axis_tilt=cross_axis_tilt) np.testing.assert_allclose(sat['tracker_theta'], expected['tracker_theta'], atol=1e-7) np.testing.assert_allclose(sat['aoi'], expected['aoi'], atol=1e-7) np.testing.assert_allclose(sat['surface_azimuth'], expected['surface_azimuth'], atol=1e-7) np.testing.assert_allclose(sat['surface_tilt'], expected['surface_tilt'], atol=1e-7)
# isn't purely parallel or perpendicular to the axes, the axis tilt and # cross-axis tilt angles are not immediately obvious. We can use pvlib # to calculate them for us: # terrain slopes 10 degrees downward to the south-south-east. note: because # slope_azimuth is defined in the direction of falling slope, slope_tilt is # always positive. slope_azimuth = 155 slope_tilt = 10 axis_azimuth = 180 # tracker axis is still N-S # calculate the tracker axis tilt, assuming that the axis follows the terrain: axis_tilt = tracking.calc_axis_tilt(slope_azimuth, slope_tilt, axis_azimuth) # calculate the cross-axis tilt: cross_axis_tilt = tracking.calc_cross_axis_tilt(slope_azimuth, slope_tilt, axis_azimuth, axis_tilt) print('Axis tilt:', '{:0.01f}°'.format(axis_tilt)) print('Cross-axis tilt:', '{:0.01f}°'.format(cross_axis_tilt)) # %% # And now we can pass use these values to generate the tracker curve as # before: tracker_data = tracking.singleaxis( apparent_zenith=solpos['apparent_zenith'], apparent_azimuth=solpos['azimuth'], axis_tilt=axis_tilt, # no longer flat because the terrain imparts a tilt axis_azimuth=axis_azimuth, max_angle=90, backtrack=True,