9
9
10
10
from __future__ import division
11
11
import os
12
+ import warnings
12
13
import logging
13
14
pvl_logger = logging .getLogger ('pvlib' )
14
15
import datetime as dt
29
30
from pvlib .tools import localize_to_utc , datetime_to_djd , djd_to_datetime
30
31
31
32
32
- def get_solarposition (time , location , method = 'pyephem ' , pressure = 101325 ,
33
- temperature = 12 ):
33
+ def get_solarposition (time , location , method = 'nrel_numpy ' , pressure = 101325 ,
34
+ temperature = 12 , ** kwargs ):
34
35
"""
35
36
A convenience wrapper for the solar position calculators.
36
37
@@ -39,51 +40,63 @@ def get_solarposition(time, location, method='pyephem', pressure=101325,
39
40
time : pandas.DatetimeIndex
40
41
location : pvlib.Location object
41
42
method : string
42
- 'pyephem' uses the PyEphem package (default) : :func:`pyephem`
43
+ 'pyephem' uses the PyEphem package: :func:`pyephem`
43
44
44
- 'spa' or 'spa_c' uses the spa code: :func:`spa`
45
+ 'nrel_c' uses the NREL SPA C code [3] : :func:`spa_c`
45
46
46
- 'spa_numpy' uses SPA code translated to python: :func: `spa_python`
47
+ 'nrel_numpy' uses an implementation of the NREL SPA algorithm
48
+ described in [1] (default): :func:`spa_python`
47
49
48
- 'spa_numba' uses SPA code translated to python: :func: `spa_python`
50
+ 'nrel_numba' uses an implementation of the NREL SPA algorithm
51
+ described in [1], but also compiles the code first: :func:`spa_python`
49
52
50
53
'ephemeris' uses the pvlib ephemeris code: :func:`ephemeris`
51
54
pressure : float
52
55
Pascals.
53
56
temperature : float
54
57
Degrees C.
58
+
59
+ Other keywords are passed to the underlying solar position function.
60
+
61
+ References
62
+ ----------
63
+ [1] I. Reda and A. Andreas, Solar position algorithm for solar radiation applications. Solar Energy, vol. 76, no. 5, pp. 577-589, 2004.
64
+ [2] I. Reda and A. Andreas, Corrigendum to Solar position algorithm for solar radiation applications. Solar Energy, vol. 81, no. 6, p. 838, 2007.
65
+ [3] NREL SPA code: http://rredc.nrel.gov/solar/codesandalgorithms/spa/
55
66
"""
56
67
57
68
method = method .lower ()
58
69
if isinstance (time , dt .datetime ):
59
70
time = pd .DatetimeIndex ([time , ])
60
71
61
- if method == 'spa' or method == 'spa_c' :
62
- ephem_df = spa (time , location , pressure , temperature )
63
- elif method == 'pyephem' :
64
- ephem_df = pyephem (time , location , pressure , temperature )
65
- elif method == 'spa_numpy' :
72
+ if method == 'nrel_c' :
73
+ ephem_df = spa_c (time , location , pressure , temperature , ** kwargs )
74
+ elif method == 'nrel_numba' :
66
75
ephem_df = spa_python (time , location , pressure , temperature ,
67
- how = 'numpy' )
68
- elif method == 'spa_numba ' :
76
+ how = 'numba' , ** kwargs )
77
+ elif method == 'nrel_numpy ' :
69
78
ephem_df = spa_python (time , location , pressure , temperature ,
70
- how = 'numba' )
79
+ how = 'numpy' , ** kwargs )
80
+ elif method == 'pyephem' :
81
+ ephem_df = pyephem (time , location , pressure , temperature , ** kwargs )
71
82
elif method == 'ephemeris' :
72
- ephem_df = ephemeris (time , location , pressure , temperature )
83
+ ephem_df = ephemeris (time , location , pressure , temperature , ** kwargs )
73
84
else :
74
85
raise ValueError ('Invalid solar position method' )
75
86
76
87
return ephem_df
77
88
78
89
79
- def spa (time , location , pressure = 101325 , temperature = 12 , delta_t = 67.0 ,
90
+ def spa_c (time , location , pressure = 101325 , temperature = 12 , delta_t = 67.0 ,
80
91
raw_spa_output = False ):
81
92
'''
82
93
Calculate the solar position using the C implementation of the NREL
83
94
SPA code
84
95
85
96
The source files for this code are located in './spa_c_files/', along with
86
- a README file which describes how the C code is wrapped in Python.
97
+ a README file which describes how the C code is wrapped in Python.
98
+ Due to license restrictions, the C code must be downloaded seperately
99
+ and used in accordance with it's license.
87
100
88
101
Parameters
89
102
----------
@@ -113,6 +126,10 @@ def spa(time, location, pressure=101325, temperature=12, delta_t=67.0,
113
126
----------
114
127
NREL SPA code: http://rredc.nrel.gov/solar/codesandalgorithms/spa/
115
128
USNO delta T: http://www.usno.navy.mil/USNO/earth-orientation/eo-products/long-term
129
+
130
+ See also
131
+ --------
132
+ pyephem, spa_python, ephemeris
116
133
'''
117
134
118
135
# Added by Rob Andrews (@Calama-Consulting), Calama Consulting, 2014
@@ -161,11 +178,42 @@ def spa(time, location, pressure=101325, temperature=12, delta_t=67.0,
161
178
return dfout
162
179
163
180
181
+ def _spa_python_import (how ):
182
+ """Compile spa.py appropriately"""
183
+
184
+ from pvlib import spa
185
+
186
+ # check to see if the spa module was compiled with numba
187
+ using_numba = spa .USE_NUMBA
188
+
189
+ if how == 'numpy' and using_numba :
190
+ # the spa module was compiled to numba code, so we need to
191
+ # reload the module without compiling
192
+ # the PVLIB_USE_NUMBA env variable is used to tell the module
193
+ # to not compile with numba
194
+ os .environ ['PVLIB_USE_NUMBA' ] = '0'
195
+ pvl_logger .debug ('Reloading spa module without compiling' )
196
+ spa = reload (spa )
197
+ del os .environ ['PVLIB_USE_NUMBA' ]
198
+ elif how == 'numba' and not using_numba :
199
+ # The spa module was not compiled to numba code, so set
200
+ # PVLIB_USE_NUMBA so it does compile to numba on reload.
201
+ os .environ ['PVLIB_USE_NUMBA' ] = '1'
202
+ pvl_logger .debug ('Reloading spa module, compiling with numba' )
203
+ spa = reload (spa )
204
+ del os .environ ['PVLIB_USE_NUMBA' ]
205
+ elif how != 'numba' and how != 'numpy' :
206
+ raise ValueError ("how must be either 'numba' or 'numpy'" )
207
+
208
+ return spa
209
+
210
+
211
+
164
212
def spa_python (time , location , pressure = 101325 , temperature = 12 , delta_t = None ,
165
213
atmos_refract = None , how = 'numpy' , numthreads = 4 ):
166
214
"""
167
- Calculate the solar position using a python translation of the
168
- NREL SPA code .
215
+ Calculate the solar position using a python implementation of the
216
+ NREL SPA algorithm described in [1] .
169
217
170
218
If numba is installed, the functions can be compiled to
171
219
machine code and the function can be multithreaded.
@@ -182,33 +230,42 @@ def spa_python(time, location, pressure=101325, temperature=12, delta_t=None,
182
230
avg. yearly air temperature in degrees C.
183
231
delta_t : float, optional
184
232
Difference between terrestrial time and UT1.
185
- By default, use USNO historical data and predictions
233
+ The USNO has historical and forecasted delta_t [3].
234
+ atmos_refrac : float, optional
235
+ The approximate atmospheric refraction (in degrees)
236
+ at sunrise and sunset.
186
237
how : str, optional
187
- If numba >= 0.17.0 is installed, how='numba' will
188
- compile the spa functions to machine code and
189
- run them multithreaded. Otherwise, numpy calculations
190
- are used.
238
+ Options are 'numpy' or 'numba'. If numba >= 0.17.0
239
+ is installed, how='numba' will compile the spa functions
240
+ to machine code and run them multithreaded.
191
241
numthreads : int, optional
192
242
Number of threads to use if how == 'numba'.
193
243
194
244
Returns
195
245
-------
196
246
DataFrame
197
247
The DataFrame will have the following columns:
198
- apparent_zenith, zenith,
199
- apparent_elevation, elevation,
200
- azimuth.
248
+ apparent_zenith (degrees),
249
+ zenith (degrees),
250
+ apparent_elevation (degrees),
251
+ elevation (degrees),
252
+ azimuth (degrees),
253
+ equation_of_time (minutes).
201
254
202
255
203
256
References
204
257
----------
205
258
[1] I. Reda and A. Andreas, Solar position algorithm for solar radiation applications. Solar Energy, vol. 76, no. 5, pp. 577-589, 2004.
206
259
[2] I. Reda and A. Andreas, Corrigendum to Solar position algorithm for solar radiation applications. Solar Energy, vol. 81, no. 6, p. 838, 2007.
260
+ [3] USNO delta T: http://www.usno.navy.mil/USNO/earth-orientation/eo-products/long-term
261
+
262
+ See also
263
+ --------
264
+ pyephem, spa_c, ephemeris
207
265
"""
208
266
209
267
# Added by Tony Lorenzo (@alorenzo175), University of Arizona, 2015
210
268
211
- from pvlib import spa
212
269
pvl_logger .debug ('Calculating solar position with spa_python code' )
213
270
214
271
lat = location .latitude
@@ -226,26 +283,16 @@ def spa_python(time, location, pressure=101325, temperature=12, delta_t=None,
226
283
227
284
unixtime = localize_to_utc (time , location ).astype (int )/ 10 ** 9
228
285
229
- using_numba = spa .USE_NUMBA
230
- if how == 'numpy' and using_numba :
231
- os .environ ['PVLIB_USE_NUMBA' ] = '0'
232
- pvl_logger .debug ('Reloading spa module without compiling' )
233
- spa = reload (spa )
234
- del os .environ ['PVLIB_USE_NUMBA' ]
235
- elif how == 'numba' and not using_numba :
236
- os .environ ['PVLIB_USE_NUMBA' ] = '1'
237
- pvl_logger .debug ('Reloading spa module, compiling with numba' )
238
- spa = reload (spa )
239
- del os .environ ['PVLIB_USE_NUMBA' ]
240
- elif how != 'numba' and how != 'numpy' :
241
- raise ValueError ("how must be either 'numba' or 'numpy'" )
242
-
243
- app_zenith , zenith , app_elevation , elevation , azimuth = spa .solar_position (
286
+ spa = _spa_python_import (how )
287
+
288
+ app_zenith , zenith , app_elevation , elevation , azimuth , eot = spa .solar_position (
244
289
unixtime , lat , lon , elev , pressure , temperature , delta_t , atmos_refract ,
245
290
numthreads )
291
+
246
292
result = pd .DataFrame ({'apparent_zenith' :app_zenith , 'zenith' :zenith ,
247
293
'apparent_elevation' :app_elevation ,
248
- 'elevation' :elevation , 'azimuth' :azimuth },
294
+ 'elevation' :elevation , 'azimuth' :azimuth ,
295
+ 'equation_of_time' :eot },
249
296
index = time )
250
297
251
298
try :
@@ -254,6 +301,85 @@ def spa_python(time, location, pressure=101325, temperature=12, delta_t=None,
254
301
result = result .tz_localize (location .tz )
255
302
256
303
return result
304
+
305
+
306
+ def get_sun_rise_set_transit (time , location , how = 'numpy' , delta_t = None ,
307
+ numthreads = 4 ):
308
+ """
309
+ Calculate the sunrise, sunset, and sun transit times using the
310
+ NREL SPA algorithm described in [1].
311
+
312
+ If numba is installed, the functions can be compiled to
313
+ machine code and the function can be multithreaded.
314
+ Without numba, the function evaluates via numpy with
315
+ a slight performance hit.
316
+
317
+ Parameters
318
+ ----------
319
+ time : pandas.DatetimeIndex
320
+ Only the date part is used
321
+ location : pvlib.Location object
322
+ delta_t : float, optional
323
+ Difference between terrestrial time and UT1.
324
+ By default, use USNO historical data and predictions
325
+ how : str, optional
326
+ Options are 'numpy' or 'numba'. If numba >= 0.17.0
327
+ is installed, how='numba' will compile the spa functions
328
+ to machine code and run them multithreaded.
329
+ numthreads : int, optional
330
+ Number of threads to use if how == 'numba'.
331
+
332
+ Returns
333
+ -------
334
+ DataFrame
335
+ The DataFrame will have the following columns:
336
+ sunrise, sunset, transit
337
+
338
+ References
339
+ ----------
340
+ [1] Reda, I., Andreas, A., 2003. Solar position algorithm for solar radiation applications. Technical report: NREL/TP-560- 34302. Golden, USA, http://www.nrel.gov.
341
+ """
342
+ # Added by Tony Lorenzo (@alorenzo175), University of Arizona, 2015
343
+
344
+ pvl_logger .debug ('Calculating sunrise, set, transit with spa_python code' )
345
+
346
+ lat = location .latitude
347
+ lon = location .longitude
348
+ delta_t = delta_t or 67.0
349
+
350
+ if not isinstance (time , pd .DatetimeIndex ):
351
+ try :
352
+ time = pd .DatetimeIndex (time )
353
+ except (TypeError , ValueError ):
354
+ time = pd .DatetimeIndex ([time ,])
355
+
356
+ # must convert to midnight UTC on day of interest
357
+ utcday = pd .DatetimeIndex (time .date ).tz_localize ('UTC' )
358
+ unixtime = utcday .astype (int )/ 10 ** 9
359
+
360
+ spa = _spa_python_import (how )
361
+
362
+ transit , sunrise , sunset = spa .transit_sunrise_sunset (
363
+ unixtime , lat , lon , delta_t , numthreads )
364
+
365
+ # arrays are in seconds since epoch format, need to conver to timestamps
366
+ transit = pd .to_datetime (transit , unit = 's' , utc = True ).tz_convert (
367
+ location .tz ).tolist ()
368
+ sunrise = pd .to_datetime (sunrise , unit = 's' , utc = True ).tz_convert (
369
+ location .tz ).tolist ()
370
+ sunset = pd .to_datetime (sunset , unit = 's' , utc = True ).tz_convert (
371
+ location .tz ).tolist ()
372
+
373
+ result = pd .DataFrame ({'transit' :transit ,
374
+ 'sunrise' :sunrise ,
375
+ 'sunset' :sunset }, index = time )
376
+
377
+ try :
378
+ result = result .tz_convert (location .tz )
379
+ except TypeError :
380
+ result = result .tz_localize (location .tz )
381
+
382
+ return result
257
383
258
384
259
385
def _ephem_setup (location , pressure , temperature ):
@@ -291,11 +417,18 @@ def pyephem(time, location, pressure=101325, temperature=12):
291
417
apparent_elevation, elevation,
292
418
apparent_azimuth, azimuth,
293
419
apparent_zenith, zenith.
420
+
421
+ See also
422
+ --------
423
+ spa_python, spa_c, ephemeris
424
+
294
425
"""
295
426
296
427
# Written by Will Holmgren (@wholmgren), University of Arizona, 2014
297
-
298
- import ephem
428
+ try :
429
+ import ephem
430
+ except ImportError :
431
+ raise ImportError ('PyEphem must be installed' )
299
432
300
433
pvl_logger .debug ('using PyEphem to calculate solar position' )
301
434
@@ -394,7 +527,7 @@ def ephemeris(time, location, pressure=101325, temperature=12):
394
527
395
528
See also
396
529
--------
397
- pyephem, spa
530
+ pyephem, spa_c, spa_python
398
531
399
532
'''
400
533
@@ -610,3 +743,5 @@ def pyephem_earthsun_distance(time):
610
743
earthsun .append (sun .earth_distance )
611
744
612
745
return pd .Series (earthsun , index = time )
746
+
747
+
0 commit comments