Skip to content

Commit 853b121

Browse files
authored
Improve lookup_linke_turbidity speed (#369)
* refactor lookup_linke_turbidity for speed * update whatsnew * fix flake8 issues, add comment * clean up whatsnew
1 parent c8b8086 commit 853b121

File tree

2 files changed

+85
-42
lines changed

2 files changed

+85
-42
lines changed
Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
1-
.. _whatsnew_0500:
1+
.. _whatsnew_0510:
22

3-
v0.5.0 (August 11, 2017)
3+
v0.5.1 (?, 2017)
44
------------------------
55

66
API Changes
77
~~~~~~~~~~~
8-
*
8+
*
99

1010
Bug fixes
1111
~~~~~~~~~
1212
* Remove condition causing Overflow warning from clearsky.haurwitz
13-
* modelchain.basic_chain now correctly passes 'solar_position_method' arg to solarposition.get_solarposition
14-
* Doc string of modelchain.basic_chain was updated to describe args more accurately
13+
* modelchain.basic_chain now correctly passes 'solar_position_method'
14+
arg to solarposition.get_solarposition
1515

1616
Enhancements
1717
~~~~~~~~~~~~
18-
*
18+
* Improve clearsky.lookup_linke_turbidity speed. (:issue:`368`)
1919

2020
Documentation
2121
~~~~~~~~~~~~~
22-
*
22+
* Doc string of modelchain.basic_chain was updated to describe args
23+
more accurately
2324

2425
Testing
2526
~~~~~~~
@@ -29,3 +30,4 @@ Contributors
2930
~~~~~~~~~~~~
3031
* Cliff Hansen
3132
* KonstantinTr
33+
* Will Holmgren

pvlib/clearsky.py

Lines changed: 76 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,8 @@ def lookup_linke_turbidity(time, latitude, longitude, filepath=None,
196196
try:
197197
import scipy.io
198198
except ImportError:
199-
raise ImportError('The Linke turbidity lookup table requires scipy. ' +
200-
'You can still use clearsky.ineichen if you ' +
199+
raise ImportError('The Linke turbidity lookup table requires scipy. '
200+
'You can still use clearsky.ineichen if you '
201201
'supply your own turbidities.')
202202

203203
if filepath is None:
@@ -214,54 +214,95 @@ def lookup_linke_turbidity(time, latitude, longitude, filepath=None,
214214
np.around(_linearly_scale(longitude, -180, 180, 0, 4320))
215215
.astype(np.int64))
216216

217-
g = linke_turbidity_table[latitude_index][longitude_index]
217+
lts = linke_turbidity_table[latitude_index][longitude_index]
218218

219219
if interp_turbidity:
220-
# Data covers 1 year. Assume that data corresponds to the value at the
221-
# middle of each month. This means that we need to add previous Dec and
222-
# next Jan to the array so that the interpolation will work for
223-
# Jan 1 - Jan 15 and Dec 16 - Dec 31.
224-
g2 = np.concatenate([[g[-1]], g, [g[0]]])
225-
# Then we map the month value to the day of year value.
226-
isleap = [calendar.isleap(t.year) for t in time]
227-
if all(isleap):
228-
days = _calendar_month_middles(2016) # all years are leap
229-
elif not any(isleap):
230-
days = _calendar_month_middles(2015) # none of the years are leap
231-
else:
232-
days = None # some of the years are leap years and some are not
233-
if days is None:
234-
# Loop over different years, might be slow for large timeserires
235-
linke_turbidity = pd.Series([
236-
np.interp(t.dayofyear, _calendar_month_middles(t.year), g2)
237-
for t in time
238-
], index=time)
239-
else:
240-
linke_turbidity = pd.Series(np.interp(time.dayofyear, days, g2),
241-
index=time)
220+
linke_turbidity = _interpolate_turbidity(lts, time)
242221
else:
243-
linke_turbidity = pd.DataFrame(time.month, index=time)
244-
# apply monthly data
245-
linke_turbidity = linke_turbidity.apply(lambda x: g[x[0]-1], axis=1)
222+
months = time.month - 1
223+
linke_turbidity = pd.Series(lts[months], index=time)
246224

247225
linke_turbidity /= 20.
248226

249227
return linke_turbidity
250228

251229

230+
def _is_leap_year(year):
231+
"""Determine if a year is leap year.
232+
233+
Parameters
234+
----------
235+
year : numeric
236+
237+
Returns
238+
-------
239+
isleap : array of bools
240+
"""
241+
isleap = ((np.mod(year, 4) == 0) &
242+
((np.mod(year, 100) != 0) | (np.mod(year, 400) == 0)))
243+
return isleap
244+
245+
246+
def _interpolate_turbidity(lts, time):
247+
"""
248+
Interpolated monthly Linke turbidity onto daily values.
249+
250+
Parameters
251+
----------
252+
lts : np.array
253+
Monthly Linke turbidity values.
254+
time : pd.DatetimeIndex
255+
Times to be interpolated onto.
256+
257+
Returns
258+
-------
259+
linke_turbidity : pd.Series
260+
The interpolated turbidity.
261+
"""
262+
# Data covers 1 year. Assume that data corresponds to the value at the
263+
# middle of each month. This means that we need to add previous Dec and
264+
# next Jan to the array so that the interpolation will work for
265+
# Jan 1 - Jan 15 and Dec 16 - Dec 31.
266+
lts_concat = np.concatenate([[lts[-1]], lts, [lts[0]]])
267+
268+
# handle leap years
269+
try:
270+
isleap = time.is_leap_year
271+
except AttributeError:
272+
year = time.year
273+
isleap = _is_leap_year(year)
274+
275+
dayofyear = time.dayofyear
276+
days_leap = _calendar_month_middles(2016)
277+
days_no_leap = _calendar_month_middles(2015)
278+
279+
# Then we map the month value to the day of year value.
280+
# Do it for both leap and non-leap years.
281+
lt_leap = np.interp(dayofyear, days_leap, lts_concat)
282+
lt_no_leap = np.interp(dayofyear, days_no_leap, lts_concat)
283+
linke_turbidity = np.where(isleap, lt_leap, lt_no_leap)
284+
285+
linke_turbidity = pd.Series(linke_turbidity, index=time)
286+
287+
return linke_turbidity
288+
289+
252290
def _calendar_month_middles(year):
253-
"""list of middle day of each month, used by Linke turbidity lookup"""
291+
"""List of middle day of each month, used by Linke turbidity lookup"""
254292
# remove mdays[0] since January starts at mdays[1]
255-
# make local copy of mdays since we need to change February for leap years
293+
# make local copy of mdays since we need to change
294+
# February for leap years
256295
mdays = np.array(calendar.mdays[1:])
257296
ydays = 365
258297
# handle leap years
259298
if calendar.isleap(year):
260299
mdays[1] = mdays[1] + 1
261300
ydays = 366
262-
return np.concatenate([[-calendar.mdays[-1] / 2.0], # Dec last year
263-
np.cumsum(mdays) - np.array(mdays) / 2., # this year
264-
[ydays + calendar.mdays[1] / 2.0]]) # Jan next year
301+
middles = np.concatenate(
302+
[[-calendar.mdays[-1] / 2.0], # Dec last year
303+
np.cumsum(mdays) - np.array(mdays) / 2., # this year
304+
[ydays + calendar.mdays[1] / 2.0]]) # Jan next year
305+
return middles
265306

266307

267308
def _linearly_scale(inputmatrix, inputmin, inputmax, outputmin, outputmax):
@@ -294,8 +335,8 @@ def haurwitz(apparent_zenith):
294335
295336
Implements the Haurwitz clear sky model for global horizontal
296337
irradiance (GHI) as presented in [1, 2]. A report on clear
297-
sky models found the Haurwitz model to have the best performance
298-
in terms of average monthly error among models which require only
338+
sky models found the Haurwitz model to have the best performance
339+
in terms of average monthly error among models which require only
299340
zenith angle [3].
300341
301342
Parameters

0 commit comments

Comments
 (0)