Skip to content

Commit 16bdde9

Browse files
alexandermorgancwhanse
authored andcommitted
fix rounding issue in linke turbidity lookup function (pvlib#755)
* Resolve merge conflicts. The upstream what's new file had been updated. * Copy whatsnew file from upstream. * Remove unnecessary np.around(). Instead of rounding to one decimal place, then comparing to .5, just compare to .55 right away which preserves the same behavior as before. If you want to stay true to the comment on line 314, use <= .5 in comparison instead as before, but still drop the np.around(). * Use <= .5 as comparisons in _linearly_scale. * Correct previous commit, use <= .5. * Update what's new file. Closes pvlib#754 * Compare to 0.500001 for margin of error. * Rename _linearly_scale -> _degrees_to_index, refactor. This commit reduces inputs to just two, refactors some calculations, and mainly tries to be much clearer with the documentation. * Add test for clearsky._degrees_to_index(). Tests that an invalid value for the degree_type argument raises an error. * Remove try/except/finally. The exception that is being checked for here is already checked for in the _degrees_to_index method, so there's no need to do the check again. So this removes the except portion, and just keeps the try and finally parts. * Update parameter type description. * Fix linting errors. * Fix typo. * Give more detail in what's new description. * Rename arg, use context manager, and add more tech. documentation. * Add missing zero in documentation. * Break up line so it's < 80 characters. * Change arg type to float or int. * Update argument name in test_degrees_to_index_1().
1 parent 2f6376e commit 16bdde9

File tree

3 files changed

+69
-31
lines changed

3 files changed

+69
-31
lines changed

docs/sphinx/source/whatsnew/v0.7.0.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ Bug fixes
2020
* Fix handling of keyword arguments in `forecasts.get_processed_data`.
2121
(:issue:`745`)
2222
* Fix output as Series feature in :py:func:`pvlib.pvsystem.ashraeiam`.
23+
* Fix rounding issue in `clearsky._linearly_scale`, a function that converts
24+
longitude or latitude degree to an index number in a Linke turbidity lookup
25+
table. Also rename the function to `clearsky._degrees_to_index`.
26+
(:issue:`754`)
2327

2428
Testing
2529
~~~~~~~

pvlib/clearsky.py

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,9 @@ def lookup_linke_turbidity(time, latitude, longitude, filepath=None,
149149
----------
150150
time : pandas.DatetimeIndex
151151
152-
latitude : float
152+
latitude : float or int
153153
154-
longitude : float
154+
longitude : float or int
155155
156156
filepath : None or string, default None
157157
The path to the ``.h5`` file.
@@ -193,22 +193,12 @@ def lookup_linke_turbidity(time, latitude, longitude, filepath=None,
193193
pvlib_path = os.path.dirname(os.path.abspath(__file__))
194194
filepath = os.path.join(pvlib_path, 'data', 'LinkeTurbidities.h5')
195195

196-
latitude_index = (
197-
np.around(_linearly_scale(latitude, 90, -90, 0, 2160))
198-
.astype(np.int64))
199-
longitude_index = (
200-
np.around(_linearly_scale(longitude, -180, 180, 0, 4320))
201-
.astype(np.int64))
196+
latitude_index = _degrees_to_index(latitude, coordinate='latitude')
197+
longitude_index = _degrees_to_index(longitude, coordinate='longitude')
202198

203-
lt_h5_file = tables.open_file(filepath)
204-
try:
199+
with tables.open_file(filepath) as lt_h5_file:
205200
lts = lt_h5_file.root.LinkeTurbidity[latitude_index,
206201
longitude_index, :]
207-
except IndexError:
208-
raise IndexError('Latitude should be between 90 and -90, '
209-
'longitude between -180 and 180.')
210-
finally:
211-
lt_h5_file.close()
212202

213203
if interp_turbidity:
214204
linke_turbidity = _interpolate_turbidity(lts, time)
@@ -299,28 +289,65 @@ def _calendar_month_middles(year):
299289
return middles
300290

301291

302-
def _linearly_scale(inputmatrix, inputmin, inputmax, outputmin, outputmax):
303-
"""linearly scale input to output, used by Linke turbidity lookup"""
292+
def _degrees_to_index(degrees, coordinate):
293+
"""Transform input degrees to an output index integer. The Linke
294+
turbidity lookup tables have three dimensions, latitude, longitude, and
295+
month. Specify a degree value and either 'latitude' or 'longitude' to get
296+
the appropriate index number for the first two of these index numbers.
297+
298+
Parameters
299+
----------
300+
degrees : float or int
301+
Degrees of either latitude or longitude.
302+
coordinate : string
303+
Specify whether degrees arg is latitude or longitude. Must be set to
304+
either 'latitude' or 'longitude' or an error will be raised.
305+
306+
Returns
307+
-------
308+
index : np.int16
309+
The latitude or longitude index number to use when looking up values
310+
in the Linke turbidity lookup table.
311+
"""
312+
# Assign inputmin, inputmax, and outputmax based on degree type.
313+
if coordinate == 'latitude':
314+
inputmin = 90
315+
inputmax = -90
316+
outputmax = 2160
317+
elif coordinate == 'longitude':
318+
inputmin = -180
319+
inputmax = 180
320+
outputmax = 4320
321+
else:
322+
raise IndexError("coordinate must be 'latitude' or 'longitude'.")
323+
304324
inputrange = inputmax - inputmin
305-
outputrange = outputmax - outputmin
306-
delta = outputrange/inputrange # number of indices per input unit
307-
inputmin = inputmin + 1.0 / delta / 2.0 # shift to center of index
308-
outputmax = outputmax - 1 # shift index to zero indexing
309-
outputmatrix = (inputmatrix - inputmin) * delta + outputmin
325+
scale = outputmax/inputrange # number of indices per degree
326+
center = inputmin + 1 / scale / 2 # shift to center of index
327+
outputmax -= 1 # shift index to zero indexing
328+
index = (degrees - center) * scale
310329
err = IndexError('Input, %g, is out of range (%g, %g).' %
311-
(inputmatrix, inputmax - inputrange, inputmax))
312-
# round down if input is within half an index or else raise index error
313-
if outputmatrix > outputmax:
314-
if np.around(outputmatrix - outputmax, 1) <= 0.5:
315-
outputmatrix = outputmax
330+
(degrees, inputmin, inputmax))
331+
332+
# If the index is still out of bounds after rounding, raise an error.
333+
# 0.500001 is used in comparisons instead of 0.5 to allow for a small
334+
# margin of error which can occur when dealing with floating point numbers.
335+
if index > outputmax:
336+
if index - outputmax <= 0.500001:
337+
index = outputmax
316338
else:
317339
raise err
318-
elif outputmatrix < outputmin:
319-
if np.around(outputmin - outputmatrix, 1) <= 0.5:
320-
outputmatrix = outputmin
340+
elif index < 0:
341+
if -index <= 0.500001:
342+
index = 0
321343
else:
322344
raise err
323-
return outputmatrix
345+
# If the index wasn't set to outputmax or 0, round it and cast it as an
346+
# integer so it can be used in integer-based indexing.
347+
else:
348+
index = int(np.around(index))
349+
350+
return index
324351

325352

326353
def haurwitz(apparent_zenith):

pvlib/test/test_clearsky.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,13 @@ def monthly_lt_nointerp(lat, lon, time=months):
519519
monthly_lt_nointerp(38.2, -181) # exceeds min longitude
520520

521521

522+
def test_degrees_to_index_1():
523+
"""Test that _degrees_to_index raises an error when something other than
524+
'latitude' or 'longitude' is passed."""
525+
with pytest.raises(IndexError): # invalid value for coordinate argument
526+
clearsky._degrees_to_index(degrees=22.0, coordinate='width')
527+
528+
522529
@pytest.fixture
523530
def detect_clearsky_data():
524531
test_dir = os.path.dirname(os.path.abspath(

0 commit comments

Comments
 (0)