7
7
# Will Holmgren (@wholmgren), University of Arizona, 2014
8
8
9
9
from __future__ import division
10
-
11
10
import logging
12
11
pvl_logger = logging .getLogger ('pvlib' )
12
+ import datetime as dt
13
13
14
- import datetime
15
14
16
15
import numpy as np
17
16
import pandas as pd
18
17
import pytz
19
18
20
- try :
21
- from .spa_c_files .spa_py import spa_calc
22
- except ImportError as e :
23
- pvl_logger .exception ('Could not import built-in SPA calculator. You may need to recompile the SPA code.' )
24
-
25
- try :
26
- import ephem
27
- except ImportError as e :
28
- pvl_logger .warning ('PyEphem not found.' )
29
19
20
+ from .pvl_tools import localize_to_utc , datetime_to_djd , djd_to_datetime
30
21
31
22
32
- def get_solarposition (time , location , method = 'spa ' , pressure = 101325 ,
23
+ def get_solarposition (time , location , method = 'pyephem ' , pressure = 101325 ,
33
24
temperature = 12 ):
34
25
"""
35
26
A convenience wrapper for the solar position calculators.
@@ -48,6 +39,8 @@ def get_solarposition(time, location, method='spa', pressure=101325,
48
39
"""
49
40
50
41
method = method .lower ()
42
+ if isinstance (time , dt .datetime ):
43
+ time = pd .DatetimeIndex ([time ,])
51
44
52
45
if method == 'spa' :
53
46
ephem_df = spa (time , location )
@@ -59,7 +52,6 @@ def get_solarposition(time, location, method='spa', pressure=101325,
59
52
return ephem_df
60
53
61
54
62
-
63
55
def spa (time , location , raw_spa_output = False ):
64
56
'''
65
57
Calculate the solar position using the C implementation of the NREL
@@ -90,10 +82,16 @@ def spa(time, location, raw_spa_output=False):
90
82
91
83
# Added by Rob Andrews (@Calama-Consulting), Calama Consulting, 2014
92
84
# Edited by Will Holmgren (@wholmgren), University of Arizona, 2014
85
+
86
+ try :
87
+ from .spa_c_files .spa_py import spa_calc
88
+ except ImportError as e :
89
+ raise ImportError ('Could not import built-in SPA calculator. ' +
90
+ 'You may need to recompile the SPA code.' )
93
91
94
92
pvl_logger .debug ('using built-in spa code to calculate solar position' )
95
93
96
- time_utc = _localize_to_utc (time , location )
94
+ time_utc = localize_to_utc (time , location )
97
95
98
96
spa_out = []
99
97
@@ -120,6 +118,20 @@ def spa(time, location, raw_spa_output=False):
120
118
return dfout
121
119
122
120
121
+ def _ephem_setup (location , pressure , temperature ):
122
+ import ephem
123
+ # initialize a PyEphem observer
124
+ obs = ephem .Observer ()
125
+ obs .lat = str (location .latitude )
126
+ obs .lon = str (location .longitude )
127
+ obs .elevation = location .altitude
128
+ obs .pressure = pressure / 100. # convert to mBar
129
+ obs .temp = temperature
130
+
131
+ # the PyEphem sun
132
+ sun = ephem .Sun ()
133
+ return obs , sun
134
+
123
135
124
136
def pyephem (time , location , pressure = 101325 , temperature = 12 ):
125
137
"""
@@ -144,24 +156,17 @@ def pyephem(time, location, pressure=101325, temperature=12):
144
156
"""
145
157
146
158
# Written by Will Holmgren (@wholmgren), University of Arizona, 2014
147
-
159
+
160
+ import ephem
161
+
148
162
pvl_logger .debug ('using PyEphem to calculate solar position' )
149
163
150
- time_utc = _localize_to_utc (time , location )
164
+ time_utc = localize_to_utc (time , location )
151
165
152
166
sun_coords = pd .DataFrame (index = time_utc )
153
167
154
- # initialize a PyEphem observer
155
- obs = ephem .Observer ()
156
- obs .lat = str (location .latitude )
157
- obs .lon = str (location .longitude )
158
- obs .elevation = location .altitude
159
- obs .pressure = pressure / 100. # convert to mBar
160
- obs .temp = temperature
161
-
162
- # the PyEphem sun
163
- sun = ephem .Sun ()
164
-
168
+ obs , sun = _ephem_setup (location , pressure , temperature )
169
+
165
170
# make and fill lists of the sun's altitude and azimuth
166
171
# this is the pressure and temperature corrected apparent alt/az.
167
172
alts = []
@@ -197,8 +202,7 @@ def pyephem(time, location, pressure=101325, temperature=12):
197
202
return sun_coords .tz_convert (location .tz )
198
203
except TypeError :
199
204
return sun_coords .tz_localize (location .tz )
200
-
201
-
205
+
202
206
203
207
def ephemeris (time , location , pressure = 101325 , temperature = 12 ):
204
208
'''
@@ -217,7 +221,7 @@ def ephemeris(time, location, pressure=101325, temperature=12):
217
221
pressure : float or DataFrame
218
222
Ambient pressure (Pascals)
219
223
220
- tempreature : float or DataFrame
224
+ temperature : float or DataFrame
221
225
Ambient temperature (C)
222
226
223
227
Returns
@@ -374,30 +378,63 @@ def ephemeris(time, location, pressure=101325, temperature=12):
374
378
DFOut ['solar_time' ] = SolarTime
375
379
376
380
return DFOut
377
-
378
-
379
-
380
- def _localize_to_utc ( time , location ):
381
+
382
+
383
+ def calc_time ( lower_bound , upper_bound , location , attribute , value ,
384
+ pressure = 101325 , temperature = 12 , xtol = 1.0e-12 ):
381
385
"""
382
- Converts or localizes a time series to UTC.
386
+ Calculate the time between lower_bound and upper_bound
387
+ where the attribute is equal to value. Uses PyEphem for
388
+ solar position calculations.
383
389
384
390
Parameters
385
391
----------
386
- time : pandas.DatetimeIndex or pandas.Series/DataFrame with a DatetimeIndex.
392
+ lower_bound : datetime.datetime
393
+ upper_bound : datetime.datetime
387
394
location : pvlib.Location object
395
+ attribute : str
396
+ The attribute of a pyephem.Sun object that
397
+ you want to solve for. Likely options are 'alt'
398
+ and 'az' (which must be given in radians).
399
+ value : int or float
400
+ The value of the attribute to solve for
401
+ pressure : int or float, optional
402
+ Air pressure in Pascals. Set to 0 for no
403
+ atmospheric correction.
404
+ temperature : int or float, optional
405
+ Air temperature in degrees C.
406
+ xtol : float, optional
407
+ The allowed error in the result from value
388
408
389
409
Returns
390
410
-------
391
- pandas object localized to UTC.
411
+ datetime.datetime
412
+
413
+ Raises
414
+ ------
415
+ ValueError
416
+ If the value is not contained between the bounds.
417
+ AttributeError
418
+ If the given attribute is not an attribute of a
419
+ PyEphem.Sun object.
392
420
"""
393
-
421
+
394
422
try :
395
- time_utc = time .tz_convert ('UTC' )
396
- pvl_logger .debug ('tz_convert to UTC' )
397
- except TypeError :
398
- time_utc = time .tz_localize (location .tz ).tz_convert ('UTC' )
399
- pvl_logger .debug ('tz_localize to {} and then tz_convert to UTC'
400
- .format (location .tz ))
423
+ import scipy .optimize as so
424
+ except ImportError as e :
425
+ raise ImportError ('The calc_time function requires scipy' )
426
+
427
+ obs , sun = _ephem_setup (location , pressure , temperature )
428
+
429
+ def compute_attr (thetime , target , attr ):
430
+ obs .date = thetime
431
+ sun .compute (obs )
432
+ return getattr (sun , attr ) - target
401
433
402
- return time_utc
403
-
434
+ lb = datetime_to_djd (lower_bound )
435
+ ub = datetime_to_djd (upper_bound )
436
+
437
+ djd_root = so .brentq (compute_attr , lb , ub ,
438
+ (value , attribute ), xtol = xtol )
439
+
440
+ return djd_to_datetime (djd_root , location .tz )
0 commit comments