Skip to content

Commit e6267a5

Browse files
jason-rpktcwhanse
andauthored
Replace interp1d #2394 (#2741)
* Replace interp1d #2394 * Replace CubicSpline by make_interp_spline k=3 * clean up/ simplify/move import to top * remove list input * Remove ifinstance check/Use np.interp * linting * Remove test to check for lists * Update pvlib/iam.py Co-authored-by: Cliff Hansen <cwhanse@sandia.gov> * Update pvlib/iam.py with warn_deprecated Co-authored-by: Cliff Hansen <cwhanse@sandia.gov> * add imports for interp1d and warn_deprecate * Add tests for invalid/deprecated interp methods * Linting * Update pvlib/iam.py Co-authored-by: Cliff Hansen <cwhanse@sandia.gov> * Add whatsnew for Deprecate scipy.interpolate * Apply suggestion from @cwhanse --------- Co-authored-by: Cliff Hansen <cwhanse@sandia.gov>
1 parent 1e6f870 commit e6267a5

5 files changed

Lines changed: 100 additions & 28 deletions

File tree

docs/examples/shading/plot_partial_module_shading_simple.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
from pvlib import pvsystem, singlediode
3939
import pandas as pd
4040
import numpy as np
41-
from scipy.interpolate import interp1d
4241
import matplotlib.pyplot as plt
4342

4443
from scipy.constants import e as qe, k as kB
@@ -178,10 +177,8 @@ def plot_curves(dfs, labels, title):
178177

179178

180179
def interpolate(df, i):
181-
"""convenience wrapper around scipy.interpolate.interp1d"""
182-
f_interp = interp1d(np.flipud(df['i']), np.flipud(df['v']), kind='linear',
183-
fill_value='extrapolate')
184-
return f_interp(i)
180+
"""convenience wrapper around numpy.interp"""
181+
return np.interp(i, np.flipud(df['i']), np.flipud(df['v']))
185182

186183

187184
def combine_series(dfs):

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Breaking Changes
1010

1111
Deprecations
1212
~~~~~~~~~~~~
13-
13+
* Deprecate non-polynomial interpolation options in :py:func:`pvlib.iam.interp` (:issue:`2394`, :pull:`2741`)
1414

1515
Bug fixes
1616
~~~~~~~~~
@@ -71,4 +71,5 @@ Contributors
7171
* Mathias Aschwanden (:ghuser:`maschwanden`)
7272
* Yonry Zhu (:ghuser:`yonryzhu`)
7373
* Darshan Gowda (:ghuser:`dgowdaan-cmyk`)
74+
* Jason Lun Leung (:ghuser:`jason-rpkt`)
7475
* Leonardo Scappatura (:ghuser:`Leonard013`)

pvlib/iam.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import functools
1414
from scipy.optimize import minimize
1515
from pvlib.tools import cosd, sind, acosd
16+
from pvlib._deprecation import warn_deprecated
17+
from scipy.interpolate import make_interp_spline, interp1d
1618

1719
# a dict of required parameter names for each IAM model
1820
# keys are the function names for the IAM models
@@ -440,7 +442,6 @@ def interp(aoi, theta_ref, iam_ref, method='linear', normalize=True):
440442
method : str, default 'linear'
441443
Specifies the interpolation method.
442444
Useful options are: 'linear', 'quadratic', 'cubic'.
443-
See scipy.interpolate.interp1d for more options.
444445
445446
normalize : boolean, default True
446447
When true, the interpolated values are divided by the interpolated
@@ -469,9 +470,6 @@ def interp(aoi, theta_ref, iam_ref, method='linear', normalize=True):
469470
pvlib.iam.sapm
470471
'''
471472
# Contributed by Anton Driesse (@adriesse), PV Performance Labs. July, 2019
472-
473-
from scipy.interpolate import interp1d
474-
475473
# Scipy doesn't give the clearest feedback, so check number of points here.
476474
MIN_REF_VALS = {'linear': 2, 'quadratic': 3, 'cubic': 4, 1: 2, 2: 3, 3: 4}
477475

@@ -483,10 +481,25 @@ def interp(aoi, theta_ref, iam_ref, method='linear', normalize=True):
483481
raise ValueError("Negative value(s) found in 'iam_ref'. "
484482
"This is not physically possible.")
485483

486-
interpolator = interp1d(theta_ref, iam_ref, kind=method,
487-
fill_value='extrapolate')
488-
aoi_input = aoi
484+
kvals = {'linear': 1, 'quadratic': 2, 'cubic': 3}
485+
if method in kvals:
486+
interpolator = make_interp_spline(
487+
theta_ref, iam_ref, k=kvals[method])
488+
489+
elif method in {'nearest', 'nearest-up', 'zero',
490+
'slinear', 'previous', 'next'}:
491+
msg = (
492+
f"Interpolation method {method} is deprecated in pvlib"
493+
)
494+
warn_deprecated(since="0.15.3", removal="0.16.0", addendum=msg)
495+
interpolator = interp1d(theta_ref, iam_ref, kind=method,
496+
fill_value='extrapolate')
497+
else:
498+
raise ValueError(
499+
f"Interpolation method '{method}' is not supported"
500+
" in pvlib-python.")
489501

502+
aoi_input = aoi
490503
aoi = np.asanyarray(aoi)
491504
aoi = np.abs(aoi)
492505
iam = interpolator(aoi)

pvlib/spectrum/response.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import numpy as np
77
import pandas as pd
88
import scipy.constants
9-
from scipy.interpolate import interp1d
9+
from scipy.interpolate import make_interp_spline
1010

1111

1212
_PLANCK_BY_LIGHT_SPEED_OVER_ELEMENTAL_CHARGE_BY_BILLION = (
@@ -66,16 +66,14 @@ def get_example_spectral_response(wavelength=None):
6666
if wavelength is None:
6767
resolution = 5.0
6868
wavelength = np.arange(280, 1200 + resolution, resolution)
69+
x = SR_DATA[0]
70+
y = SR_DATA[1]
71+
spline = make_interp_spline(x, y, k=3)
6972

70-
interpolator = interp1d(SR_DATA[0], SR_DATA[1],
71-
kind='cubic',
72-
bounds_error=False,
73-
fill_value=0.0,
74-
copy=False,
75-
assume_sorted=True)
76-
77-
sr = pd.Series(data=interpolator(wavelength), index=wavelength)
73+
values = spline(wavelength)
74+
values[(wavelength < x[0]) | (wavelength > x[-1])] = 0.0
7875

76+
sr = pd.Series(data=values, index=wavelength)
7977
sr.index.name = 'wavelength'
8078
sr.name = 'spectral_response'
8179

tests/test_iam.py

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from numpy.testing import assert_allclose
1313

1414
from pvlib import iam as _iam
15+
from pvlib._deprecation import pvlibDeprecationWarning
1516

1617

1718
def test_ashrae():
@@ -171,8 +172,8 @@ def test_martin_ruiz_diffuse():
171172

172173
def test_iam_interp():
173174

174-
aoi_meas = [0.0, 45.0, 65.0, 75.0]
175-
iam_meas = [1.0, 0.9, 0.8, 0.6]
175+
aoi_meas = np.array([0.0, 45.0, 65.0, 75.0])
176+
iam_meas = np.array([1.0, 0.9, 0.8, 0.6])
176177

177178
# simple default linear method
178179
aoi = 55.0
@@ -200,18 +201,80 @@ def test_iam_interp():
200201
assert_series_equal(iam, expected)
201202

202203
# check beyond reference values
203-
aoi = [-45, 0, 45, 85, 90, 95, 100, 105, 110]
204-
expected = [0.9, 1.0, 0.9, 0.4, 0.3, 0.2, 0.1, 0.0, 0.0]
204+
aoi = np.array([-45, 0, 45, 85, 90, 95, 100, 105, 110])
205+
expected = np.array([0.9, 1.0, 0.9, 0.4, 0.3, 0.2, 0.1, 0.0, 0.0])
205206
iam = _iam.interp(aoi, aoi_meas, iam_meas)
206207
assert_allclose(iam, expected)
207208

208209
# check exception clause
209210
with pytest.raises(ValueError):
210-
_iam.interp(0.0, [0], [1])
211+
_iam.interp(0.0, np.array([0]), np.array([1]))
211212

212213
# check exception clause
213214
with pytest.raises(ValueError):
214-
_iam.interp(0.0, [0, 90], [1, -1])
215+
_iam.interp(0.0, np.array([0, 90]), np.array([1, -1]))
216+
217+
# check linear after updating interp1d
218+
theta_ref = np.array([0, 60, 90])
219+
iam_ref = np.array([1.0, 0.8, 0.0])
220+
221+
aoi = np.array([0, 30, 60])
222+
iam = _iam.interp(
223+
aoi, theta_ref, iam_ref,
224+
method="linear", normalize=False)
225+
expected = np.array([1.0, 0.9, 0.8])
226+
np.testing.assert_allclose(iam, expected)
227+
228+
# check quadratic
229+
theta_ref = np.array([0, 30, 60, 90])
230+
iam_ref = 1.0 - 1e-4 * theta_ref**2
231+
aoi = np.array([15, 45, 75])
232+
iam = _iam.interp(
233+
aoi,
234+
theta_ref,
235+
iam_ref,
236+
method="quadratic",
237+
normalize=False
238+
)
239+
240+
expected = 1.0 - 1e-4 * aoi**2
241+
np.testing.assert_allclose(iam, expected, rtol=1e-12)
242+
243+
244+
@pytest.mark.parametrize(
245+
"method",
246+
["nearest", "nearest-up", "zero", "slinear", "previous", "next"]
247+
)
248+
def test_iam_interp_deprecated_methods(method):
249+
theta_ref = np.array([0, 60, 90])
250+
iam_ref = np.array([1.0, 0.8, 0.0])
251+
252+
with pytest.warns(
253+
pvlibDeprecationWarning,
254+
match=f"Interpolation method {method} is deprecated in pvlib"
255+
):
256+
_iam.interp(
257+
30,
258+
theta_ref,
259+
iam_ref,
260+
method=method
261+
)
262+
263+
264+
def test_iam_interp_invalid_method():
265+
theta_ref = np.array([0, 60, 90])
266+
iam_ref = np.array([1.0, 0.8, 0.0])
267+
268+
with pytest.raises(
269+
ValueError,
270+
match="is not supported"
271+
):
272+
_iam.interp(
273+
30,
274+
theta_ref,
275+
iam_ref,
276+
method="unsupported"
277+
)
215278

216279

217280
@pytest.mark.parametrize('aoi,expected', [

0 commit comments

Comments
 (0)