-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Perform dayofyear-based calculations according to UTC, not local time #2055
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6c4c32a
9ce02d5
0ad7a68
05961d2
45a36cb
b00a2d4
7bb7e02
6d1ab57
3fed282
6f98df7
fe5b5c1
97afcb8
defddfd
a08ca79
6baa1ed
4515366
83a61ba
745579c
b6d238a
fa62003
5d80876
3e95694
59cb82b
6e1c251
0baa338
cd45c74
1e6914d
dd78f3e
35e0756
758ea98
5102347
1c940fb
12c6a49
ab7d307
82aa0df
c4862b5
57d052e
85eae01
3fc5579
f255e8b
63df590
639ce7f
33f778f
16f0e5a
2745954
eb2707f
ab953b5
b8246c0
7da25f4
70d06cb
dea79d3
2143c94
80a6afb
87cd719
c4b65e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -23,7 +23,7 @@ | |||||||||||||||||||||
import scipy.optimize as so | ||||||||||||||||||||||
import warnings | ||||||||||||||||||||||
|
||||||||||||||||||||||
from pvlib import atmosphere | ||||||||||||||||||||||
from pvlib import atmosphere, tools | ||||||||||||||||||||||
from pvlib.tools import datetime_to_djd, djd_to_datetime | ||||||||||||||||||||||
|
||||||||||||||||||||||
|
||||||||||||||||||||||
|
@@ -199,11 +199,7 @@ | |||||||||||||||||||||
raise ImportError('Could not import built-in SPA calculator. ' + | ||||||||||||||||||||||
'You may need to recompile the SPA code.') | ||||||||||||||||||||||
|
||||||||||||||||||||||
# if localized, convert to UTC. otherwise, assume UTC. | ||||||||||||||||||||||
try: | ||||||||||||||||||||||
time_utc = time.tz_convert('UTC') | ||||||||||||||||||||||
except TypeError: | ||||||||||||||||||||||
time_utc = time | ||||||||||||||||||||||
time_utc = tools._pandas_to_utc(time) | ||||||||||||||||||||||
|
||||||||||||||||||||||
spa_out = [] | ||||||||||||||||||||||
|
||||||||||||||||||||||
|
@@ -378,7 +374,9 @@ | |||||||||||||||||||||
|
||||||||||||||||||||||
spa = _spa_python_import(how) | ||||||||||||||||||||||
|
||||||||||||||||||||||
delta_t = delta_t or spa.calculate_deltat(time.year, time.month) | ||||||||||||||||||||||
cwhanse marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||
if not delta_t: | ||||||||||||||||||||||
time_utc = tools._pandas_to_utc(time) | ||||||||||||||||||||||
delta_t = spa.calculate_deltat(time_utc.year, time_utc.month) | ||||||||||||||||||||||
|
||||||||||||||||||||||
app_zenith, zenith, app_elevation, elevation, azimuth, eot = \ | ||||||||||||||||||||||
spa.solar_position(unixtime, lat, lon, elev, pressure, temperature, | ||||||||||||||||||||||
|
@@ -452,12 +450,13 @@ | |||||||||||||||||||||
raise ValueError('times must be localized') | ||||||||||||||||||||||
|
||||||||||||||||||||||
# must convert to midnight UTC on day of interest | ||||||||||||||||||||||
utcday = pd.DatetimeIndex(times.date).tz_localize('UTC') | ||||||||||||||||||||||
unixtime = _datetime_to_unixtime(utcday) | ||||||||||||||||||||||
times_utc = times.tz_convert('UTC') | ||||||||||||||||||||||
unixtime = _datetime_to_unixtime(times_utc.normalize()) | ||||||||||||||||||||||
|
||||||||||||||||||||||
spa = _spa_python_import(how) | ||||||||||||||||||||||
|
||||||||||||||||||||||
delta_t = delta_t or spa.calculate_deltat(times.year, times.month) | ||||||||||||||||||||||
if not delta_t: | ||||||||||||||||||||||
delta_t = spa.calculate_deltat(times_utc.year, times_utc.month) | ||||||||||||||||||||||
|
||||||||||||||||||||||
transit, sunrise, sunset = spa.transit_sunrise_sunset( | ||||||||||||||||||||||
unixtime, lat, lon, delta_t, numthreads) | ||||||||||||||||||||||
|
@@ -581,12 +580,11 @@ | |||||||||||||||||||||
sunrise = [] | ||||||||||||||||||||||
sunset = [] | ||||||||||||||||||||||
trans = [] | ||||||||||||||||||||||
for thetime in times: | ||||||||||||||||||||||
thetime = thetime.to_pydatetime() | ||||||||||||||||||||||
for thetime in tools._pandas_to_utc(times): | ||||||||||||||||||||||
# older versions of pyephem ignore timezone when converting to its | ||||||||||||||||||||||
# internal datetime format, so convert to UTC here to support | ||||||||||||||||||||||
# all versions. GH #1449 | ||||||||||||||||||||||
obs.date = ephem.Date(thetime.astimezone(dt.timezone.utc)) | ||||||||||||||||||||||
obs.date = ephem.Date(thetime) | ||||||||||||||||||||||
sunrise.append(_ephem_to_timezone(rising(sun), tzinfo)) | ||||||||||||||||||||||
sunset.append(_ephem_to_timezone(setting(sun), tzinfo)) | ||||||||||||||||||||||
trans.append(_ephem_to_timezone(transit(sun), tzinfo)) | ||||||||||||||||||||||
|
@@ -644,11 +642,7 @@ | |||||||||||||||||||||
except ImportError: | ||||||||||||||||||||||
raise ImportError('PyEphem must be installed') | ||||||||||||||||||||||
|
||||||||||||||||||||||
# if localized, convert to UTC. otherwise, assume UTC. | ||||||||||||||||||||||
try: | ||||||||||||||||||||||
time_utc = time.tz_convert('UTC') | ||||||||||||||||||||||
except TypeError: | ||||||||||||||||||||||
time_utc = time | ||||||||||||||||||||||
time_utc = tools._pandas_to_utc(time) | ||||||||||||||||||||||
|
||||||||||||||||||||||
sun_coords = pd.DataFrame(index=time) | ||||||||||||||||||||||
|
||||||||||||||||||||||
|
@@ -765,11 +759,7 @@ | |||||||||||||||||||||
# the SPA algorithm needs time to be expressed in terms of | ||||||||||||||||||||||
# decimal UTC hours of the day of the year. | ||||||||||||||||||||||
|
||||||||||||||||||||||
# if localized, convert to UTC. otherwise, assume UTC. | ||||||||||||||||||||||
try: | ||||||||||||||||||||||
time_utc = time.tz_convert('UTC') | ||||||||||||||||||||||
except TypeError: | ||||||||||||||||||||||
time_utc = time | ||||||||||||||||||||||
time_utc = tools._pandas_to_utc(time) | ||||||||||||||||||||||
|
||||||||||||||||||||||
# strip out the day of the year and calculate the decimal hour | ||||||||||||||||||||||
DayOfYear = time_utc.dayofyear | ||||||||||||||||||||||
|
@@ -956,7 +946,10 @@ | |||||||||||||||||||||
|
||||||||||||||||||||||
sun = ephem.Sun() | ||||||||||||||||||||||
earthsun = [] | ||||||||||||||||||||||
for thetime in time: | ||||||||||||||||||||||
for thetime in tools._pandas_to_utc(time): | ||||||||||||||||||||||
# older versions of pyephem ignore timezone when converting to its | ||||||||||||||||||||||
# internal datetime format, so convert to UTC here to support | ||||||||||||||||||||||
# all versions. GH #1449 | ||||||||||||||||||||||
sun.compute(ephem.Date(thetime)) | ||||||||||||||||||||||
earthsun.append(sun.earth_distance) | ||||||||||||||||||||||
|
||||||||||||||||||||||
|
@@ -1013,7 +1006,9 @@ | |||||||||||||||||||||
|
||||||||||||||||||||||
spa = _spa_python_import(how) | ||||||||||||||||||||||
|
||||||||||||||||||||||
delta_t = delta_t or spa.calculate_deltat(time.year, time.month) | ||||||||||||||||||||||
if not delta_t: | ||||||||||||||||||||||
time_utc = tools._pandas_to_utc(time) | ||||||||||||||||||||||
delta_t = spa.calculate_deltat(time_utc.year, time_utc.month) | ||||||||||||||||||||||
|
||||||||||||||||||||||
dist = spa.earthsun_distance(unixtime, delta_t, numthreads) | ||||||||||||||||||||||
|
||||||||||||||||||||||
|
@@ -1386,22 +1381,26 @@ | |||||||||||||||||||||
equation_of_time_spencer71 | ||||||||||||||||||||||
equation_of_time_pvcdrom | ||||||||||||||||||||||
""" | ||||||||||||||||||||||
|
||||||||||||||||||||||
# times must be localized | ||||||||||||||||||||||
if not times.tz: | ||||||||||||||||||||||
raise ValueError('times must be localized') | ||||||||||||||||||||||
|
||||||||||||||||||||||
# hours - timezone = (times - normalized_times) - (naive_times - times) | ||||||||||||||||||||||
if times.tz is None: | ||||||||||||||||||||||
times = times.tz_localize('utc') | ||||||||||||||||||||||
Comment on lines
+1385
to
-1391
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure I agree with this change. The docstring says the input timestamps "must be localized to the timezone for the longitude", which is inconsistent with the previous behavior (localize to UTC if not already localized), so I can see some change being needed here. But why is this the correct change? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was added in accordance with pvlib-python/pvlib/solarposition.py Lines 448 to 452 in 524fa55
pvlib-python/pvlib/solarposition.py Lines 560 to 564 in 524fa55
|
||||||||||||||||||||||
tzs = np.array([ts.utcoffset().total_seconds() for ts in times]) / 3600 | ||||||||||||||||||||||
|
||||||||||||||||||||||
hrs_minus_tzs = (times - times.normalize()) / pd.Timedelta('1h') - tzs | ||||||||||||||||||||||
hrs_minus_tzs = _times_to_hours_after_local_midnight(times) - tzs | ||||||||||||||||||||||
|
||||||||||||||||||||||
# ensure array return instead of a version-dependent pandas <T>Index | ||||||||||||||||||||||
return np.asarray( | ||||||||||||||||||||||
15. * (hrs_minus_tzs - 12.) + longitude + equation_of_time / 4.) | ||||||||||||||||||||||
return 15. * (hrs_minus_tzs - 12.) + longitude + equation_of_time / 4. | ||||||||||||||||||||||
|
||||||||||||||||||||||
|
||||||||||||||||||||||
def _hour_angle_to_hours(times, hourangle, longitude, equation_of_time): | ||||||||||||||||||||||
"""converts hour angles in degrees to hours as a numpy array""" | ||||||||||||||||||||||
if times.tz is None: | ||||||||||||||||||||||
times = times.tz_localize('utc') | ||||||||||||||||||||||
|
||||||||||||||||||||||
# times must be localized | ||||||||||||||||||||||
cwhanse marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||
if not times.tz: | ||||||||||||||||||||||
raise ValueError('times must be localized') | ||||||||||||||||||||||
|
||||||||||||||||||||||
tzs = np.array([ts.utcoffset().total_seconds() for ts in times]) / 3600 | ||||||||||||||||||||||
hours = (hourangle - longitude - equation_of_time / 4.) / 15. + 12. + tzs | ||||||||||||||||||||||
return np.asarray(hours) | ||||||||||||||||||||||
|
@@ -1411,18 +1410,26 @@ | |||||||||||||||||||||
""" | ||||||||||||||||||||||
converts hours since midnight from an array of floats to localized times | ||||||||||||||||||||||
""" | ||||||||||||||||||||||
tz_info = times.tz # pytz timezone info | ||||||||||||||||||||||
naive_times = times.tz_localize(None) # naive but still localized | ||||||||||||||||||||||
# normalize local, naive times to previous midnight and add the hours until | ||||||||||||||||||||||
|
||||||||||||||||||||||
# times must be localized | ||||||||||||||||||||||
if not times.tz: | ||||||||||||||||||||||
raise ValueError('times must be localized') | ||||||||||||||||||||||
|
||||||||||||||||||||||
# normalize local times to previous local midnight and add the hours until | ||||||||||||||||||||||
# sunrise, sunset, and transit | ||||||||||||||||||||||
return pd.DatetimeIndex( | ||||||||||||||||||||||
naive_times.normalize() + pd.to_timedelta(hours, unit='h'), tz=tz_info) | ||||||||||||||||||||||
return times.normalize() + pd.to_timedelta(hours, unit='h') | ||||||||||||||||||||||
|
||||||||||||||||||||||
|
||||||||||||||||||||||
def _times_to_hours_after_local_midnight(times): | ||||||||||||||||||||||
"""convert local pandas datetime indices to array of hours as floats""" | ||||||||||||||||||||||
times = times.tz_localize(None) | ||||||||||||||||||||||
|
||||||||||||||||||||||
# times must be localized | ||||||||||||||||||||||
if not times.tz: | ||||||||||||||||||||||
raise ValueError('times must be localized') | ||||||||||||||||||||||
|
||||||||||||||||||||||
hrs = (times - times.normalize()) / pd.Timedelta('1h') | ||||||||||||||||||||||
|
||||||||||||||||||||||
# ensure array return instead of a version-dependent pandas <T>Index | ||||||||||||||||||||||
return np.array(hrs) | ||||||||||||||||||||||
|
||||||||||||||||||||||
|
||||||||||||||||||||||
|
@@ -1468,6 +1475,11 @@ | |||||||||||||||||||||
CRC Press (2012) | ||||||||||||||||||||||
|
||||||||||||||||||||||
""" | ||||||||||||||||||||||
|
||||||||||||||||||||||
# times must be localized | ||||||||||||||||||||||
if not times.tz: | ||||||||||||||||||||||
raise ValueError('times must be localized') | ||||||||||||||||||||||
|
||||||||||||||||||||||
latitude_rad = np.radians(latitude) # radians | ||||||||||||||||||||||
sunset_angle_rad = np.arccos(-np.tan(declination) * np.tan(latitude_rad)) | ||||||||||||||||||||||
sunset_angle = np.degrees(sunset_angle_rad) # degrees | ||||||||||||||||||||||
|
Uh oh!
There was an error while loading. Please reload this page.