Skip to content

Commit 2314467

Browse files
committed
Merge pull request #98 from wholmgren/refactor-turbidity
Refactor clearsky.py
2 parents f435b6e + 0302b00 commit 2314467

File tree

3 files changed

+194
-66
lines changed

3 files changed

+194
-66
lines changed

docs/sphinx/source/whatsnew/v0.2.2.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,15 @@ Enhancements
1010
~~~~~~~~~~~~
1111

1212
* Adds Python 3.5 compatibility (:issue:`87`)
13+
* Moves the Linke turbidity lookup into ``clearsky.lookup_linke_turbidity``.
14+
The API for ``clearsky.ineichen`` remains the same. (:issue:`95`)
1315

1416

1517
Bug fixes
1618
~~~~~~~~~
1719

20+
* Fixes an import and KeyError in the IPython notebook tutorials
21+
(:issue:`94`).
1822
* Uses the ``logging`` module properly by replacing ``format``
1923
calls with ``args``. This results in a 5x speed increase for
2024
``tracking.singleaxis`` (:issue:`89`).
@@ -24,4 +28,5 @@ Contributors
2428
~~~~~~~~~~~~
2529

2630
* Will Holmgren
31+
* jetheurer
2732
* dacoex

pvlib/clearsky.py

Lines changed: 84 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -115,45 +115,9 @@ def ineichen(time, location, linke_turbidity=None,
115115

116116

117117
if linke_turbidity is None:
118-
# The .mat file 'LinkeTurbidities.mat' contains a single 2160 x 4320 x 12
119-
# matrix of type uint8 called 'LinkeTurbidity'. The rows represent global
120-
# latitudes from 90 to -90 degrees; the columns represent global longitudes
121-
# from -180 to 180; and the depth (third dimension) represents months of
122-
# the year from January (1) to December (12). To determine the Linke
123-
# turbidity for a position on the Earth's surface for a given month do the
124-
# following: LT = LinkeTurbidity(LatitudeIndex, LongitudeIndex, month).
125-
# Note that the numbers within the matrix are 20 * Linke Turbidity,
126-
# so divide the number from the file by 20 to get the
127-
# turbidity.
128-
129-
try:
130-
import scipy.io
131-
except ImportError:
132-
raise ImportError('The Linke turbidity lookup table requires scipy. ' +
133-
'You can still use clearsky.ineichen if you ' +
134-
'supply your own turbidities.')
135-
136-
# consider putting this code at module level
137-
this_path = os.path.dirname(os.path.abspath(__file__))
138-
logger.debug('this_path=%s', this_path)
139-
140-
mat = scipy.io.loadmat(os.path.join(this_path, 'data', 'LinkeTurbidities.mat'))
141-
linke_turbidity = mat['LinkeTurbidity']
142-
LatitudeIndex = np.round_(_linearly_scale(location.latitude,90,- 90,1,2160))
143-
LongitudeIndex = np.round_(_linearly_scale(location.longitude,- 180,180,1,4320))
144-
g = linke_turbidity[LatitudeIndex][LongitudeIndex]
145-
if interp_turbidity:
146-
logger.info('interpolating turbidity to the day')
147-
g2 = np.concatenate([[g[-1]], g, [g[0]]]) # wrap ends around
148-
days = np.linspace(-15, 380, num=14) # map day of year onto month (approximate)
149-
LT = pd.Series(np.interp(time.dayofyear, days, g2), index=time)
150-
else:
151-
logger.info('using monthly turbidity')
152-
ApplyMonth = lambda x:g[x[0]-1]
153-
LT = pd.DataFrame(time.month, index=time)
154-
LT = LT.apply(ApplyMonth, axis=1)
155-
TL = LT / 20.
156-
logger.info('using TL=\n%s', TL)
118+
TL = lookup_linke_turbidity(time, location.latitude,
119+
location.longitude,
120+
interp_turbidity=interp_turbidity)
157121
else:
158122
TL = linke_turbidity
159123

@@ -221,6 +185,87 @@ def ineichen(time, location, linke_turbidity=None,
221185
return df_out
222186

223187

188+
def lookup_linke_turbidity(time, latitude, longitude, filepath=None,
189+
interp_turbidity=True):
190+
"""
191+
Look up the Linke Turibidity from the ``LinkeTurbidities.mat``
192+
data file supplied with pvlib.
193+
194+
Parameters
195+
----------
196+
time : pandas.DatetimeIndex
197+
198+
latitude : float
199+
200+
longitude : float
201+
202+
filepath : string
203+
The path to the ``.mat`` file.
204+
205+
interp_turbidity : bool
206+
If ``True``, interpolates the monthly Linke turbidity values
207+
found in ``LinkeTurbidities.mat`` to daily values.
208+
209+
Returns
210+
-------
211+
turbidity : Series
212+
"""
213+
214+
# The .mat file 'LinkeTurbidities.mat' contains a single 2160 x 4320 x 12
215+
# matrix of type uint8 called 'LinkeTurbidity'. The rows represent global
216+
# latitudes from 90 to -90 degrees; the columns represent global longitudes
217+
# from -180 to 180; and the depth (third dimension) represents months of
218+
# the year from January (1) to December (12). To determine the Linke
219+
# turbidity for a position on the Earth's surface for a given month do the
220+
# following: LT = LinkeTurbidity(LatitudeIndex, LongitudeIndex, month).
221+
# Note that the numbers within the matrix are 20 * Linke Turbidity,
222+
# so divide the number from the file by 20 to get the
223+
# turbidity.
224+
225+
try:
226+
import scipy.io
227+
except ImportError:
228+
raise ImportError('The Linke turbidity lookup table requires scipy. ' +
229+
'You can still use clearsky.ineichen if you ' +
230+
'supply your own turbidities.')
231+
232+
if filepath is None:
233+
pvlib_path = os.path.dirname(os.path.abspath(__file__))
234+
filepath = os.path.join(pvlib_path, 'data', 'LinkeTurbidities.mat')
235+
236+
mat = scipy.io.loadmat(filepath)
237+
linke_turbidity_table = mat['LinkeTurbidity']
238+
239+
latitude_index = np.around(_linearly_scale(latitude, 90, -90, 1, 2160))
240+
longitude_index = np.around(_linearly_scale(longitude, -180, 180, 1, 4320))
241+
242+
g = linke_turbidity_table[latitude_index][longitude_index]
243+
244+
if interp_turbidity:
245+
logger.info('interpolating turbidity to the day')
246+
# Cata covers 1 year.
247+
# Assume that data corresponds to the value at
248+
# the middle of each month.
249+
# This means that we need to add previous Dec and next Jan
250+
# to the array so that the interpolation will work for
251+
# Jan 1 - Jan 15 and Dec 16 - Dec 31.
252+
# Then we map the month value to the day of year value.
253+
# This is approximate and could be made more accurate.
254+
g2 = np.concatenate([[g[-1]], g, [g[0]]])
255+
days = np.linspace(-15, 380, num=14)
256+
linke_turbidity = pd.Series(np.interp(time.dayofyear, days, g2),
257+
index=time)
258+
else:
259+
logger.info('using monthly turbidity')
260+
apply_month = lambda x: g[x[0]-1]
261+
linke_turbidity = pd.DataFrame(time.month, index=time)
262+
linke_turbidity = linke_turbidity.apply(apply_month, axis=1)
263+
264+
linke_turbidity /= 20.
265+
266+
return linke_turbidity
267+
268+
224269
def haurwitz(apparent_zenith):
225270
'''
226271
Determine clear sky GHI from Haurwitz model.

pvlib/test/test_clearsky.py

Lines changed: 105 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,137 @@
11
import logging
22
pvl_logger = logging.getLogger('pvlib')
33

4-
import datetime
5-
64
import numpy as np
75
import pandas as pd
86

97
from nose.tools import raises
10-
118
from numpy.testing import assert_almost_equal
9+
from pandas.util.testing import assert_frame_equal, assert_series_equal
1210

1311
from pvlib.location import Location
1412
from pvlib import clearsky
1513
from pvlib import solarposition
1614

1715
# setup times and location to be tested.
18-
times = pd.date_range(start=datetime.datetime(2014,6,24),
19-
end=datetime.datetime(2014,6,26), freq='1Min')
20-
2116
tus = Location(32.2, -111, 'US/Arizona', 700)
22-
17+
times = pd.date_range(start='2014-06-24', end='2014-06-25', freq='3h')
2318
times_localized = times.tz_localize(tus.tz)
2419

2520
ephem_data = solarposition.get_solarposition(times, tus)
2621

2722

28-
29-
# test the ineichen clear sky model implementation in a few ways
30-
3123
def test_ineichen_required():
32-
# the clearsky function should lookup the linke turbidity on its own
24+
# the clearsky function should call lookup_linke_turbidity by default
3325
# will fail without scipy
34-
clearsky.ineichen(times, tus)
26+
expected = pd.DataFrame(np.array([[0.,0.,0.],
27+
[0.,0.,0.],
28+
[40.53660309,302.47614235,78.1470311],
29+
[98.88372629,865.98938602,699.93403875],
30+
[122.57870881,931.83716051,1038.62116584],
31+
[109.30270612,899.88002304,847.68806472],
32+
[64.25699595,629.91187925,254.53048144],
33+
[0.,0.,0.],
34+
[0.,0.,0.]]),
35+
columns=['dhi', 'dni', 'ghi'],
36+
index=times_localized)
37+
out = clearsky.ineichen(times, tus)
38+
assert_frame_equal(expected, out)
3539

40+
3641
def test_ineichen_supply_linke():
37-
clearsky.ineichen(times, tus, linke_turbidity=3)
42+
expected = pd.DataFrame(np.array([[0.,0.,0.],
43+
[0.,0.,0.],
44+
[40.18673553,322.0649964,80.23287692],
45+
[95.14405816,876.49507151,703.48596755],
46+
[118.45873721,939.81653473,1042.34531752],
47+
[105.36671577,909.113377,851.3283881],
48+
[61.91607984,647.40869542,257.47471759],
49+
[0.,0.,0.],
50+
[0.,0.,0.]]),
51+
columns=['dhi', 'dni', 'ghi'],
52+
index=times_localized)
53+
out = clearsky.ineichen(times, tus, linke_turbidity=3)
54+
assert_frame_equal(expected, out)
55+
3856

3957
def test_ineichen_solpos():
4058
clearsky.ineichen(times, tus, linke_turbidity=3,
41-
solarposition_method='pyephem')
59+
solarposition_method='ephemeris')
60+
4261

4362
def test_ineichen_airmass():
44-
clearsky.ineichen(times, tus, linke_turbidity=3,
45-
airmass_model='simple')
63+
expected = pd.DataFrame(np.array([[0.,0.,0.],
64+
[0.,0.,0.],
65+
[41.70761136,293.72203458,78.22953786],
66+
[95.20590465,876.1650047,703.31872722],
67+
[118.46089555,939.8078753,1042.33896321],
68+
[105.39577655,908.97804342,851.24640259],
69+
[62.35382269,642.91022293,256.55363539],
70+
[0.,0.,0.],
71+
[0.,0.,0.]]),
72+
columns=['dhi', 'dni', 'ghi'],
73+
index=times_localized)
74+
out = clearsky.ineichen(times, tus, linke_turbidity=3,
75+
airmass_model='simple')
76+
assert_frame_equal(expected, out)
77+
78+
79+
def test_lookup_linke_turbidity():
80+
times = pd.date_range(start='2014-06-24', end='2014-06-25',
81+
freq='12h', tz=tus.tz)
82+
# expect same value on 2014-06-24 0000 and 1200, and
83+
# diff value on 2014-06-25
84+
expected = pd.Series(np.array([3.10126582, 3.10126582, 3.11443038]),
85+
index=times)
86+
out = clearsky.lookup_linke_turbidity(times, tus.latitude, tus.longitude)
87+
assert_series_equal(expected, out)
88+
89+
90+
def test_lookup_linke_turbidity_nointerp():
91+
times = pd.date_range(start='2014-06-24', end='2014-06-25',
92+
freq='12h', tz=tus.tz)
93+
# expect same value for all days
94+
expected = pd.Series(np.array([3., 3., 3.]), index=times)
95+
out = clearsky.lookup_linke_turbidity(times, tus.latitude, tus.longitude,
96+
interp_turbidity=False)
97+
assert_series_equal(expected, out)
98+
99+
100+
def test_lookup_linke_turbidity_months():
101+
times = pd.date_range(start='2014-04-01', end='2014-07-01',
102+
freq='1M', tz=tus.tz)
103+
expected = pd.Series(np.array([2.8943038, 2.97316456, 3.18025316]),
104+
index=times)
105+
out = clearsky.lookup_linke_turbidity(times, tus.latitude,
106+
tus.longitude)
107+
assert_series_equal(expected, out)
108+
109+
110+
def test_lookup_linke_turbidity_nointerp_months():
111+
times = pd.date_range(start='2014-04-10', end='2014-07-10',
112+
freq='1M', tz=tus.tz)
113+
expected = pd.Series(np.array([2.85, 2.95, 3.]), index=times)
114+
out = clearsky.lookup_linke_turbidity(times, tus.latitude, tus.longitude,
115+
interp_turbidity=False)
116+
assert_series_equal(expected, out)
117+
# changing the dates shouldn't matter if interp=False
118+
times = pd.date_range(start='2014-04-05', end='2014-07-05',
119+
freq='1M', tz=tus.tz)
120+
out = clearsky.lookup_linke_turbidity(times, tus.latitude, tus.longitude,
121+
interp_turbidity=False)
122+
assert_series_equal(expected, out)
46123

47-
def test_ineichen_keys():
48-
clearsky_data = clearsky.ineichen(times, tus, linke_turbidity=3)
49-
assert 'ghi' in clearsky_data.columns
50-
assert 'dni' in clearsky_data.columns
51-
assert 'dhi' in clearsky_data.columns
52124

53-
# test the haurwitz clear sky implementation
54125
def test_haurwitz():
55-
clearsky.haurwitz(ephem_data['zenith'])
56-
57-
def test_haurwitz_keys():
58-
clearsky_data = clearsky.haurwitz(ephem_data['zenith'])
59-
assert 'ghi' in clearsky_data.columns
126+
expected = pd.DataFrame(np.array([[0.],
127+
[0.],
128+
[82.85934048],
129+
[699.74514735],
130+
[1016.50198354],
131+
[838.32103769],
132+
[271.90853863],
133+
[0.],
134+
[0.]]),
135+
columns=['ghi'], index=times_localized)
136+
out = clearsky.haurwitz(ephem_data['zenith'])
137+
assert_frame_equal(expected, out)

0 commit comments

Comments
 (0)