Skip to content
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

Perform dayofyear-based calculations according to UTC, not local time #2055

Merged
merged 55 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
6c4c32a
Add clearsky.ipynb (Remove before merge)
yhkee0404 May 19, 2024
9ce02d5
Fix clearsky.lookup_linke_turbidity to be timezone-aware
yhkee0404 May 19, 2024
0ad7a68
Fix irradiance.get_extra_radiation to be timezone-aware
yhkee0404 May 19, 2024
05961d2
Rerun and clear errors in clearsky.ipynb
yhkee0404 May 19, 2024
45a36cb
Remove clearsky.ipynb
yhkee0404 May 19, 2024
b00a2d4
Update what's new
yhkee0404 May 19, 2024
7bb7e02
Merge branch 'main' into clearsky-timezone
yhkee0404 May 23, 2024
6d1ab57
Merge branch 'main' into clearsky-timezone
yhkee0404 May 26, 2024
3fed282
Add docstring Notes to lookup_linke_turbidity
yhkee0404 Jun 1, 2024
6f98df7
Add tools._pandas_to_utc
yhkee0404 Jun 1, 2024
fe5b5c1
Update what's new
yhkee0404 Jun 4, 2024
97afcb8
Add docstring Notes to _pandas_to_doy
yhkee0404 Jun 5, 2024
defddfd
Revert _datetimelike_scalar_to_datetimeindex
yhkee0404 Jun 5, 2024
a08ca79
Revert tests.test_irradiance.times
yhkee0404 Jun 5, 2024
6baa1ed
Replace lowercase utc with uppercase
yhkee0404 Jun 5, 2024
4515366
Fix clearsky lookup_linke_turbidity and _interpolate_turbidity
yhkee0404 Jun 5, 2024
83a61ba
Fix linter
yhkee0404 Jun 8, 2024
745579c
Fix clearsky._interpolate_turbidity to keep timezone
yhkee0404 Jun 8, 2024
b6d238a
Revert irradiance.py
yhkee0404 Jun 8, 2024
fa62003
Clarify UTC input of ephem.Date
yhkee0404 Jun 8, 2024
5d80876
Fix tz-naive delta_t of dni_extra nrel
yhkee0404 Jun 8, 2024
3e95694
Fix tz-naive delta_t of get_solarposition nrel_numpy
yhkee0404 Jun 8, 2024
59cb82b
Avoid future midnight of location.get_sun_rise_set_transit spa
yhkee0404 Jun 8, 2024
6e1c251
Fix tz-naive delta_t of location.get_sun_rise_set_transit spa
yhkee0404 Jun 8, 2024
0baa338
Fix local midnight of hour_angle and sun_rise_set_transit
yhkee0404 Jun 8, 2024
cd45c74
Fix dayofyear of solarposition decl and eot, i.e. clearsky.bird
yhkee0404 Jun 8, 2024
1e6914d
Fix dayofyear of spectrum.spectrl2 dni_extra spencer
yhkee0404 Jun 8, 2024
dd78f3e
Fix monthly SeasonalTiltMount orientation
yhkee0404 Jun 8, 2024
35e0756
Fix year and dayofyear of solrad
yhkee0404 Jun 8, 2024
758ea98
Fix flake8
yhkee0404 Jun 15, 2024
5102347
Merge branch 'main' into clearsky-timezone
yhkee0404 Jun 15, 2024
1c940fb
Merge branch 'main' of https://github.com/pvlib/pvlib-python into cle…
cwhanse Jul 1, 2024
12c6a49
resolve whatsnew
cwhanse Jul 1, 2024
ab7d307
don't change v0.11.0
cwhanse Jul 1, 2024
82aa0df
Merge branch 'main' into clearsky-timezone
cwhanse Aug 12, 2024
c4862b5
minor adjustment to test_clearsky
cwhanse Aug 12, 2024
57d052e
undo edit to get_solrad
cwhanse Aug 12, 2024
85eae01
undo edit to get_solrad
cwhanse Aug 12, 2024
3fc5579
Merge branch 'main' of https://github.com/pvlib/pvlib-python into cle…
cwhanse Sep 4, 2024
f255e8b
Update pvlib/clearsky.py
cwhanse Sep 4, 2024
63df590
Update docs/sphinx/source/whatsnew/v0.11.1.rst
cwhanse Sep 4, 2024
639ce7f
Update docs/sphinx/source/whatsnew/v0.11.1.rst
cwhanse Sep 4, 2024
33f778f
Update docs/examples/irradiance-transposition/plot_seasonal_tilt.py
yhkee0404 Sep 6, 2024
16f0e5a
Revert benchmarks/benchmarks/solarposition.py
yhkee0404 Sep 9, 2024
2745954
Revert test_lookup_linke_turbidity_nointerp
yhkee0404 Sep 9, 2024
eb2707f
Revert solrad.py
yhkee0404 Sep 9, 2024
ab953b5
Revert plot_seasonal_tilt.py except notes
yhkee0404 Sep 9, 2024
b8246c0
Update test_sun_rise_set_transit_spa with delta_t
yhkee0404 Sep 12, 2024
7da25f4
Update test_nrel_earthsun_distance with delta_t
yhkee0404 Sep 12, 2024
70d06cb
Update test_sun_rise_set_transit_geometric with naive tz
yhkee0404 Sep 12, 2024
dea79d3
Update test_hour_angle with inversion and naive tz
yhkee0404 Sep 12, 2024
2143c94
Remove _is_leap_year from _interpolate_turbidity
yhkee0404 Sep 12, 2024
80a6afb
Merge branch 'main' into clearsky-timezone
yhkee0404 Sep 12, 2024
87cd719
Merge branch 'main' into clearsky-timezone
yhkee0404 Sep 18, 2024
c4b65e1
Update docs/sphinx/source/whatsnew/v0.11.1.rst
yhkee0404 Sep 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/examples/irradiance-transposition/plot_seasonal_tilt.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ class SeasonalTiltMount(pvsystem.AbstractMount):
surface_azimuth: float = 180.0

def get_orientation(self, solar_zenith, solar_azimuth):
# note: determining tilt based on month may produce different
# results depending on the time zone of the timestamps
tilts = [self.monthly_tilts[m-1] for m in solar_zenith.index.month]
return pd.DataFrame({
'surface_tilt': tilts,
Expand Down
15 changes: 14 additions & 1 deletion docs/sphinx/source/whatsnew/v0.11.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,19 @@ Enhancements

Bug fixes
~~~~~~~~~

* To prevent simulation output from differing slightly based on the time zone
of the time stamps, models that use day of year for sun position and
irradiance calculations now determine the day of year according to the UTC
equivalent of the specified time stamps. The following functions are affected:
:py:func:`~pvlib.clearsky.lookup_linke_turbidity`,
:py:func:`~pvlib.irradiance.get_extra_radiation`,
:py:func:`~pvlib.irradiance.disc`,
:py:func:`~pvlib.irradiance.dirint`,
:py:func:`~pvlib.spectrum.spectrl2`. (:issue:`2054`, :pull:`2055`)
* :py:func:`~pvlib.solarposition.hour_angle` and
:py:func:`~pvlib.solarposition.sun_rise_set_transit_geometric` now raise
``ValueError`` when given timezone-naive inputs, instead of assuming UTC.
(:pull:`2055`)

Testing
~~~~~~~
Expand Down Expand Up @@ -61,6 +73,7 @@ Requirements
Contributors
~~~~~~~~~~~~
* Echedey Luis (:ghuser:`echedey-ls`)
* Yunho Kee (:ghuser:`yhkee0404`)
* Chris Deline (:ghuser:`cdeline`)
* Ioannis Sifnaios (:ghuser:`IoannisSifnaios`)
* Leonardo Micheli (:ghuser:`lmicheli`)
Expand Down
24 changes: 13 additions & 11 deletions docs/tutorials/irradiance.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"This tutorial requires pvlib >= 0.6.0.\n",
"\n",
"Authors:\n",
"* Will Holmgren (@wholmgren), University of Arizona. July 2014, April 2015, July 2015, March 2016, July 2016, February 2017, August 2018."
"* Will Holmgren (@wholmgren), University of Arizona. July 2014, April 2015, July 2015, March 2016, July 2016, February 2017, August 2018.\n",
"* Yunho Kee (@yhkee0404), Haezoom. Jun 2024."
]
},
{
Expand Down Expand Up @@ -393,6 +394,7 @@
"source": [
"tus = pvlib.location.Location(32.2, -111, 'US/Arizona', 700, 'Tucson')\n",
"times = pd.date_range(start='2016-01-01', end='2016-01-02', freq='1min', tz=tus.tz)\n",
"times_utc = times.tz_convert('UTC')\n",
"ephem_data = tus.get_solarposition(times)\n",
"irrad_data = tus.get_clearsky(times)\n",
"irrad_data.plot()\n",
Expand Down Expand Up @@ -810,7 +812,7 @@
" ephem_data['apparent_zenith'], ephem_data['azimuth'])\n",
"klucher_diffuse.plot(label='klucher diffuse')\n",
"\n",
"dni_et = pvlib.irradiance.get_extra_radiation(times.dayofyear)\n",
"dni_et = pvlib.irradiance.get_extra_radiation(times_utc.dayofyear)\n",
"reindl_diffuse = pvlib.irradiance.reindl(surf_tilt, surf_az, \n",
" irrad_data['dhi'], irrad_data['dni'], irrad_data['ghi'], dni_et,\n",
" ephem_data['apparent_zenith'], ephem_data['azimuth'])\n",
Expand Down Expand Up @@ -858,7 +860,7 @@
" ephem_data['apparent_zenith'], ephem_data['azimuth'])\n",
"klucher_diffuse.plot(label='klucher diffuse')\n",
"\n",
"dni_et = pvlib.irradiance.get_extra_radiation(times.dayofyear)\n",
"dni_et = pvlib.irradiance.get_extra_radiation(times_utc.dayofyear)\n",
"reindl_diffuse = pvlib.irradiance.reindl(surf_tilt, surf_az, \n",
" irrad_data['dhi'], irrad_data['dni'], irrad_data['ghi'], dni_et,\n",
" ephem_data['apparent_zenith'], ephem_data['azimuth'])\n",
Expand Down Expand Up @@ -913,7 +915,7 @@
" ephem_data['apparent_zenith'], ephem_data['azimuth'])\n",
"klucher_diffuse.plot(label='klucher diffuse')\n",
"\n",
"dni_et = pvlib.irradiance.get_extra_radiation(times.dayofyear)\n",
"dni_et = pvlib.irradiance.get_extra_radiation(times_utc.dayofyear)\n",
"\n",
"haydavies_diffuse = pvlib.irradiance.haydavies(surf_tilt, surf_az, \n",
" irrad_data['dhi'], irrad_data['dni'], dni_et,\n",
Expand Down Expand Up @@ -967,7 +969,7 @@
" ephem_data['apparent_zenith'], ephem_data['azimuth'])\n",
"klucher_diffuse.plot(label='klucher diffuse')\n",
"\n",
"dni_et = pvlib.irradiance.get_extra_radiation(times.dayofyear)\n",
"dni_et = pvlib.irradiance.get_extra_radiation(times_utc.dayofyear)\n",
"\n",
"haydavies_diffuse = pvlib.irradiance.haydavies(surf_tilt, surf_az, \n",
" irrad_data['dhi'], irrad_data['dni'], dni_et,\n",
Expand Down Expand Up @@ -1028,7 +1030,7 @@
" ephem_data['apparent_zenith'], ephem_data['azimuth'])\n",
"klucher_diffuse.plot(label='klucher diffuse')\n",
"\n",
"dni_et = pvlib.irradiance.get_extra_radiation(times.dayofyear)\n",
"dni_et = pvlib.irradiance.get_extra_radiation(times_utc.dayofyear)\n",
"\n",
"haydavies_diffuse = pvlib.irradiance.haydavies(surf_tilt, surf_az, \n",
" irrad_data['dhi'], irrad_data['dni'], dni_et,\n",
Expand Down Expand Up @@ -1067,7 +1069,7 @@
"sun_az = ephem_data['azimuth']\n",
"DNI = irrad_data['dni']\n",
"DHI = irrad_data['dhi']\n",
"DNI_ET = pvlib.irradiance.get_extra_radiation(times.dayofyear)\n",
"DNI_ET = pvlib.irradiance.get_extra_radiation(times_utc.dayofyear)\n",
"AM = pvlib.atmosphere.get_relative_airmass(sun_zen)\n",
"\n",
"surf_tilt = 32\n",
Expand Down Expand Up @@ -1316,7 +1318,7 @@
"sun_az = ephem_data['azimuth']\n",
"DNI = irrad_data['dni']\n",
"DHI = irrad_data['dhi']\n",
"DNI_ET = pvlib.irradiance.get_extra_radiation(times.dayofyear)\n",
"DNI_ET = pvlib.irradiance.get_extra_radiation(times_utc.dayofyear)\n",
"AM = pvlib.atmosphere.get_relative_airmass(sun_zen)\n",
"\n",
"surf_tilt = 32\n",
Expand All @@ -1330,7 +1332,7 @@
" ephem_data['apparent_zenith'], ephem_data['azimuth'])\n",
"klucher_diffuse.plot(label='klucher diffuse')\n",
"\n",
"dni_et = pvlib.irradiance.get_extra_radiation(times.dayofyear)\n",
"dni_et = pvlib.irradiance.get_extra_radiation(times_utc.dayofyear)\n",
"\n",
"haydavies_diffuse = pvlib.irradiance.haydavies(surf_tilt, surf_az, \n",
" irrad_data['dhi'], irrad_data['dni'], dni_et,\n",
Expand Down Expand Up @@ -1371,7 +1373,7 @@
"sun_az = ephem_data['azimuth']\n",
"DNI = irrad_data['dni']\n",
"DHI = irrad_data['dhi']\n",
"DNI_ET = pvlib.irradiance.get_extra_radiation(times.dayofyear)\n",
"DNI_ET = pvlib.irradiance.get_extra_radiation(times_utc.dayofyear)\n",
"AM = pvlib.atmosphere.get_relative_airmass(sun_zen)\n",
"\n",
"surf_tilt = 32\n",
Expand All @@ -1385,7 +1387,7 @@
" ephem_data['apparent_zenith'], ephem_data['azimuth'])\n",
"klucher_diffuse.plot(label='klucher diffuse')\n",
"\n",
"dni_et = pvlib.irradiance.get_extra_radiation(times.dayofyear)\n",
"dni_et = pvlib.irradiance.get_extra_radiation(times_utc.dayofyear)\n",
"\n",
"haydavies_diffuse = pvlib.irradiance.haydavies(surf_tilt, surf_az, \n",
" irrad_data['dhi'], irrad_data['dni'], dni_et,\n",
Expand Down
20 changes: 12 additions & 8 deletions pvlib/clearsky.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,13 @@ def lookup_linke_turbidity(time, latitude, longitude, filepath=None,
Returns
-------
turbidity : Series

Notes
-----
Linke turbidity is obtained from a file of historical monthly averages.
The returned value for each time is either the monthly value or an
interpolated value to smooth the transition between months.
Interpolation is done on the day of year as determined by UTC.
"""

# The .h5 file 'LinkeTurbidities.h5' contains a single 2160 x 4320 x 12
Expand Down Expand Up @@ -201,7 +208,7 @@ def lookup_linke_turbidity(time, latitude, longitude, filepath=None,
if interp_turbidity:
linke_turbidity = _interpolate_turbidity(lts, time)
else:
months = time.month - 1
months = tools._pandas_to_utc(time).month - 1
linke_turbidity = pd.Series(lts[months], index=time)

linke_turbidity /= 20.
Expand Down Expand Up @@ -247,14 +254,11 @@ def _interpolate_turbidity(lts, time):
# Jan 1 - Jan 15 and Dec 16 - Dec 31.
lts_concat = np.concatenate([[lts[-1]], lts, [lts[0]]])

# handle leap years
try:
isleap = time.is_leap_year
except AttributeError:
year = time.year
isleap = _is_leap_year(year)
time_utc = tools._pandas_to_utc(time)

isleap = time_utc.is_leap_year

dayofyear = time.dayofyear
dayofyear = time_utc.dayofyear
days_leap = _calendar_month_middles(2016)
days_no_leap = _calendar_month_middles(2015)

Expand Down
90 changes: 51 additions & 39 deletions pvlib/solarposition.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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)

Check warning on line 202 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L202

Added line #L202 was not covered by tests
cwhanse marked this conversation as resolved.
Show resolved Hide resolved

spa_out = []

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The 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?

Copy link
Contributor Author

@yhkee0404 yhkee0404 Sep 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was added in accordance with sun_rise_set_transit_spa and sun_rise_set_transit_ephem as well as the docstring. I agree with the previous convention of raising a ValueError since it prevents users from unwittingly calling the functions naively without assuming any timezones and ignoring the fact that an hour angle is counted up from zero at noon in a predefined local timezone. Please refer to the followings:

# times must be localized
if times.tz:
tzinfo = times.tz
else:
raise ValueError('times must be localized')

# times must be localized
if times.tz:
tzinfo = times.tz
else:
raise ValueError('times must be localized')

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)
Expand All @@ -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)


Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion pvlib/spectrum/spectrl2.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ def spectrl2(apparent_zenith, aoi, surface_tilt, ground_albedo,
aerosol_turbidity_500nm, scattering_albedo_400nm, alpha,
wavelength_variation_factor, aerosol_asymmetry_factor]))

dayofyear = original_index.dayofyear.values
dayofyear = pvlib.tools._pandas_to_doy(original_index).values

if not is_pandas and dayofyear is None:
raise ValueError('dayofyear must be specified if not using pandas '
Expand Down
Loading
Loading