Skip to content

Commit 308e5f6

Browse files
committed
Had to use main instead of master xd
1 parent c185834 commit 308e5f6

File tree

2 files changed

+168
-47
lines changed

2 files changed

+168
-47
lines changed

pvlib/solarposition.py

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,11 @@
2222
import pandas as pd
2323
import scipy.optimize as so
2424
import warnings
25-
import datetime
2625

2726
from pvlib import atmosphere
2827
from pvlib.tools import datetime_to_djd, djd_to_datetime
2928

3029

31-
NS_PER_HR = 1.e9 * 3600. # nanoseconds per hour
32-
33-
3430
def get_solarposition(time, latitude, longitude,
3531
altitude=None, pressure=None,
3632
method='nrel_numpy',
@@ -51,13 +47,13 @@ def get_solarposition(time, latitude, longitude,
5147
Longitude in decimal degrees. Positive east of prime meridian,
5248
negative to west.
5349
54-
altitude : None or float, default None
55-
If None, computed from pressure. Assumed to be 0 m
56-
if pressure is also None.
50+
altitude : float, optional
51+
If not specified, computed from ``pressure``. Assumed to be 0 m
52+
if ``pressure`` is not supplied.
5753
58-
pressure : None or float, default None
59-
If None, computed from altitude. Assumed to be 101325 Pa
60-
if altitude is also None.
54+
pressure : float, optional
55+
If not specified, computed from ``altitude``. Assumed to be 101325 Pa
56+
if ``altitude`` is not supplied.
6157
6258
method : string, default 'nrel_numpy'
6359
'nrel_numpy' uses an implementation of the NREL SPA algorithm
@@ -89,7 +85,7 @@ def get_solarposition(time, latitude, longitude,
8985
solar radiation applications. Solar Energy, vol. 81, no. 6, p. 838,
9086
2007.
9187
92-
.. [3] NREL SPA code: http://rredc.nrel.gov/solar/codesandalgorithms/spa/
88+
.. [3] NREL SPA code: https://midcdmz.nrel.gov/spa/
9389
"""
9490

9591
if altitude is None and pressure is None:
@@ -132,7 +128,7 @@ def get_solarposition(time, latitude, longitude,
132128
def spa_c(time, latitude, longitude, pressure=101325, altitude=0,
133129
temperature=12, delta_t=67.0,
134130
raw_spa_output=False):
135-
"""
131+
r"""
136132
Calculate the solar position using the C implementation of the NREL
137133
SPA code.
138134
@@ -161,7 +157,7 @@ def spa_c(time, latitude, longitude, pressure=101325, altitude=0,
161157
Temperature in C
162158
delta_t : float, default 67.0
163159
Difference between terrestrial time and UT1.
164-
USNO has previous values and predictions.
160+
USNO has previous values and predictions [3]_.
165161
raw_spa_output : bool, default False
166162
If true, returns the raw SPA output.
167163
@@ -177,17 +173,16 @@ def spa_c(time, latitude, longitude, pressure=101325, altitude=0,
177173
178174
References
179175
----------
180-
.. [1] NREL SPA reference:
181-
http://rredc.nrel.gov/solar/codesandalgorithms/spa/
182-
NREL SPA C files: https://midcdmz.nrel.gov/spa/
176+
.. [1] NREL SPA reference: https://midcdmz.nrel.gov/spa/
183177
184178
Note: The ``timezone`` field in the SPA C files is replaced with
185179
``time_zone`` to avoid a nameclash with the function ``__timezone`` that is
186180
redefined by Python>=3.5. This issue is
187181
`Python bug 24643 <https://bugs.python.org/issue24643>`_.
188182
189-
.. [2] USNO delta T:
190-
http://www.usno.navy.mil/USNO/earth-orientation/eo-products/long-term
183+
.. [2] Delta T: https://en.wikipedia.org/wiki/%CE%94T_(timekeeping)
184+
185+
.. [3] USNO delta T: https://maia.usno.navy.mil/products/deltaT
191186
192187
See also
193188
--------
@@ -274,6 +269,19 @@ def _spa_python_import(how):
274269
return spa
275270

276271

272+
def _datetime_to_unixtime(dtindex):
273+
# convert a pandas datetime index to unixtime, making sure to handle
274+
# different pandas units (ns, us, etc) and time zones correctly
275+
if dtindex.tz is not None:
276+
# epoch is 1970-01-01 00:00 UTC, but we need to match the input tz
277+
# for compatibility with older pandas versions (e.g. v1.3.5)
278+
epoch = pd.Timestamp("1970-01-01", tz="UTC").tz_convert(dtindex.tz)
279+
else:
280+
epoch = pd.Timestamp("1970-01-01")
281+
282+
return np.array((dtindex - epoch) / pd.Timedelta("1s"))
283+
284+
277285
def spa_python(time, latitude, longitude,
278286
altitude=0, pressure=101325, temperature=12, delta_t=67.0,
279287
atmos_refract=None, how='numpy', numthreads=4):
@@ -312,7 +320,7 @@ def spa_python(time, latitude, longitude,
312320
*Note: delta_t = None will break code using nrel_numba,
313321
this will be fixed in a future version.*
314322
The USNO has historical and forecasted delta_t [3]_.
315-
atmos_refrac : None or float, optional, default None
323+
atmos_refrac : float, optional
316324
The approximate atmospheric refraction (in degrees)
317325
at sunrise and sunset.
318326
how : str, optional, default 'numpy'
@@ -344,7 +352,7 @@ def spa_python(time, latitude, longitude,
344352
2007.
345353
346354
.. [3] USNO delta T:
347-
http://www.usno.navy.mil/USNO/earth-orientation/eo-products/long-term
355+
https://maia.usno.navy.mil/products/deltaT
348356
349357
See also
350358
--------
@@ -366,7 +374,7 @@ def spa_python(time, latitude, longitude,
366374
except (TypeError, ValueError):
367375
time = pd.DatetimeIndex([time, ])
368376

369-
unixtime = np.array(time.view(np.int64)/10**9)
377+
unixtime = _datetime_to_unixtime(time)
370378

371379
spa = _spa_python_import(how)
372380

@@ -445,7 +453,7 @@ def sun_rise_set_transit_spa(times, latitude, longitude, how='numpy',
445453

446454
# must convert to midnight UTC on day of interest
447455
utcday = pd.DatetimeIndex(times.date).tz_localize('UTC')
448-
unixtime = np.array(utcday.view(np.int64)/10**9)
456+
unixtime = _datetime_to_unixtime(utcday)
449457

450458
spa = _spa_python_import(how)
451459

@@ -578,7 +586,7 @@ def sun_rise_set_transit_ephem(times, latitude, longitude,
578586
# older versions of pyephem ignore timezone when converting to its
579587
# internal datetime format, so convert to UTC here to support
580588
# all versions. GH #1449
581-
obs.date = ephem.Date(thetime.astimezone(datetime.timezone.utc))
589+
obs.date = ephem.Date(thetime.astimezone(dt.timezone.utc))
582590
sunrise.append(_ephem_to_timezone(rising(sun), tzinfo))
583591
sunset.append(_ephem_to_timezone(setting(sun), tzinfo))
584592
trans.append(_ephem_to_timezone(transit(sun), tzinfo))
@@ -832,7 +840,7 @@ def ephemeris(time, latitude, longitude, pressure=101325, temperature=12):
832840
# Calculate refraction correction
833841
Elevation = SunEl
834842
TanEl = pd.Series(np.tan(np.radians(Elevation)), index=time_utc)
835-
Refract = pd.Series(0, index=time_utc)
843+
Refract = pd.Series(0., index=time_utc)
836844

837845
Refract[(Elevation > 5) & (Elevation <= 85)] = (
838846
58.1/TanEl - 0.07/(TanEl**3) + 8.6e-05/(TanEl**5))
@@ -1001,7 +1009,7 @@ def nrel_earthsun_distance(time, how='numpy', delta_t=67.0, numthreads=4):
10011009
except (TypeError, ValueError):
10021010
time = pd.DatetimeIndex([time, ])
10031011

1004-
unixtime = np.array(time.view(np.int64)/10**9)
1012+
unixtime = _datetime_to_unixtime(time)
10051013

10061014
spa = _spa_python_import(how)
10071015

@@ -1327,9 +1335,9 @@ def solar_zenith_analytical(latitude, hourangle, declination):
13271335
.. [4] `Wikipedia: Solar Zenith Angle
13281336
<https://en.wikipedia.org/wiki/Solar_zenith_angle>`_
13291337
1330-
.. [5] `PVCDROM: Sun's Position
1331-
<http://www.pveducation.org/pvcdrom/2-properties-sunlight/
1332-
suns-position>`_
1338+
.. [5] `PVCDROM: Elevation Angle
1339+
<https://www.pveducation.org/pvcdrom/properties-of-sunlight/
1340+
elevation-angle>`_
13331341
13341342
See Also
13351343
--------
@@ -1378,21 +1386,23 @@ def hour_angle(times, longitude, equation_of_time):
13781386
equation_of_time_spencer71
13791387
equation_of_time_pvcdrom
13801388
"""
1381-
naive_times = times.tz_localize(None) # naive but still localized
13821389
# hours - timezone = (times - normalized_times) - (naive_times - times)
1383-
hrs_minus_tzs = 1 / NS_PER_HR * (
1384-
2 * times.view(np.int64) - times.normalize().view(np.int64) -
1385-
naive_times.view(np.int64))
1390+
if times.tz is None:
1391+
times = times.tz_localize('utc')
1392+
tzs = np.array([ts.utcoffset().total_seconds() for ts in times]) / 3600
1393+
1394+
hrs_minus_tzs = (times - times.normalize()) / pd.Timedelta('1h') - tzs
1395+
13861396
# ensure array return instead of a version-dependent pandas <T>Index
13871397
return np.asarray(
13881398
15. * (hrs_minus_tzs - 12.) + longitude + equation_of_time / 4.)
13891399

13901400

13911401
def _hour_angle_to_hours(times, hourangle, longitude, equation_of_time):
13921402
"""converts hour angles in degrees to hours as a numpy array"""
1393-
naive_times = times.tz_localize(None) # naive but still localized
1394-
tzs = 1 / NS_PER_HR * (
1395-
naive_times.view(np.int64) - times.view(np.int64))
1403+
if times.tz is None:
1404+
times = times.tz_localize('utc')
1405+
tzs = np.array([ts.utcoffset().total_seconds() for ts in times]) / 3600
13961406
hours = (hourangle - longitude - equation_of_time / 4.) / 15. + 12. + tzs
13971407
return np.asarray(hours)
13981408

@@ -1406,16 +1416,13 @@ def _local_times_from_hours_since_midnight(times, hours):
14061416
# normalize local, naive times to previous midnight and add the hours until
14071417
# sunrise, sunset, and transit
14081418
return pd.DatetimeIndex(
1409-
(naive_times.normalize().view(np.int64) +
1410-
(hours * NS_PER_HR).astype(np.int64)).astype('datetime64[ns]'),
1411-
tz=tz_info)
1419+
naive_times.normalize() + pd.to_timedelta(hours, unit='h'), tz=tz_info)
14121420

14131421

14141422
def _times_to_hours_after_local_midnight(times):
14151423
"""convert local pandas datetime indices to array of hours as floats"""
14161424
times = times.tz_localize(None)
1417-
hrs = 1 / NS_PER_HR * (
1418-
times.view(np.int64) - times.normalize().view(np.int64))
1425+
hrs = (times - times.normalize()) / pd.Timedelta('1h')
14191426
return np.array(hrs)
14201427

14211428

0 commit comments

Comments
 (0)