Skip to content

Commit 5ee82b1

Browse files
mikofskiwholmgren
authored andcommitted
add test for hour_angle, vectorize (#599)
* add test for hour_angle, vectorize * closes #598 vectorize to make it more efficient * closes #597 add test Signed-off-by: Mark Mikofski <[email protected]> * BUG: use np.int64 works better for older numpy version than python int * also converting times to int before subtracting works better for older pandas versions which were not calculating the timedeltas correctly * remove comment with missing space after hash, add FIXME that explains why the expected values are slightly different than the SPA calculator output * fix hanging indent * BUG: replace utcoffset() with reliable, efficient approach ... * ... suggested by @wholmgren (thx!) * utcoffset() is unpredictable when used with pandas datetime indices * only predictable with Python datetime objects or pandas Timestamps * instead replace tzinfo with None to get naive local times, and calculate difference from tz-aware times to get timezones * BUG: combine arithmetic to make calculation more efficient * also use asarray wrapper at return to ensure consistency * add comments to explain to future maintainers * stickler * remove try-except, doesn't work anyway, wait for pandas >=0.15.0
1 parent 0c245a9 commit 5ee82b1

File tree

2 files changed

+32
-5
lines changed

2 files changed

+32
-5
lines changed

pvlib/solarposition.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,8 +1327,11 @@ def hour_angle(times, longitude, equation_of_time):
13271327
equation_of_time_Spencer71
13281328
equation_of_time_pvcdrom
13291329
"""
1330-
hours = np.array([(t - t.tz.localize(
1331-
dt.datetime(t.year, t.month, t.day)
1332-
)).total_seconds() / 3600. for t in times])
1333-
timezone = times.tz.utcoffset(times).total_seconds() / 3600.
1334-
return 15. * (hours - 12. - timezone) + longitude + equation_of_time / 4.
1330+
naive_times = times.tz_localize(None) # naive but still localized
1331+
# hours - timezone = (times - normalized_times) - (naive_times - times)
1332+
hrs_minus_tzs = 1 / (3600. * 1.e9) * (
1333+
2 * times.astype(np.int64) - times.normalize().astype(np.int64) -
1334+
naive_times.astype(np.int64))
1335+
# ensure array return instead of a version-dependent pandas <T>Index
1336+
return np.asarray(
1337+
15. * (hrs_minus_tzs - 12.) + longitude + equation_of_time / 4.)

pvlib/test/test_solarposition.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,3 +694,27 @@ def test_analytical_azimuth():
694694
azimuths = solarposition.solar_azimuth_analytical(*test_angles.T, zenith=zeniths)
695695

696696
assert not np.isnan(azimuths).any()
697+
698+
699+
def test_hour_angle():
700+
"""
701+
Test conversion from hours to hour angles in degrees given the following
702+
inputs from NREL SPA calculator at Golden, CO
703+
date,times,eot,sunrise,sunset
704+
1/2/2015,7:21:55,-3.935172,-70.699400,70.512721
705+
1/2/2015,16:47:43,-4.117227,-70.699400,70.512721
706+
1/2/2015,12:04:45,-4.026295,-70.699400,70.512721
707+
"""
708+
longitude = -105.1786 # degrees
709+
times = pd.DatetimeIndex([
710+
'2015-01-02 07:21:55.2132',
711+
'2015-01-02 16:47:42.9828',
712+
'2015-01-02 12:04:44.6340'
713+
]).tz_localize('Etc/GMT+7')
714+
eot = np.array([-3.935172, -4.117227, -4.026295])
715+
hours = solarposition.hour_angle(times, longitude, eot)
716+
expected = (-70.682338, 70.72118825000001, 0.000801250)
717+
# FIXME: there are differences from expected NREL SPA calculator values
718+
# sunrise: 4 seconds, sunset: 48 seconds, transit: 0.2 seconds
719+
# but the differences may be due to other SPA input parameters
720+
assert np.allclose(hours, expected)

0 commit comments

Comments
 (0)