From 8773810d313eb9b017c9752549bdeb9cd9c06b6a Mon Sep 17 00:00:00 2001 From: Jason Lun Leung Date: Thu, 23 Apr 2026 17:26:51 +0800 Subject: [PATCH 1/2] Replace interp1d #2394 --- .../plot_partial_module_shading_simple.py | 8 ++--- pvlib/iam.py | 31 ++++++++++++++++--- pvlib/spectrum/response.py | 20 ++++++------ tests/test_iam.py | 26 ++++++++++++++++ 4 files changed, 66 insertions(+), 19 deletions(-) diff --git a/docs/examples/shading/plot_partial_module_shading_simple.py b/docs/examples/shading/plot_partial_module_shading_simple.py index ce031eff10..c298c68033 100644 --- a/docs/examples/shading/plot_partial_module_shading_simple.py +++ b/docs/examples/shading/plot_partial_module_shading_simple.py @@ -38,7 +38,7 @@ from pvlib import pvsystem, singlediode import pandas as pd import numpy as np -from scipy.interpolate import interp1d +from scipy.interpolate import make_interp_spline import matplotlib.pyplot as plt from scipy.constants import e as qe, k as kB @@ -178,9 +178,9 @@ def plot_curves(dfs, labels, title): def interpolate(df, i): - """convenience wrapper around scipy.interpolate.interp1d""" - f_interp = interp1d(np.flipud(df['i']), np.flipud(df['v']), kind='linear', - fill_value='extrapolate') + """convenience wrapper around scipy.interpolate""" + f_interp = make_interp_spline(np.flipud(df['i']), np.flipud(df['v']), k=1) + return f_interp(i) diff --git a/pvlib/iam.py b/pvlib/iam.py index 161de84589..bd8483c591 100644 --- a/pvlib/iam.py +++ b/pvlib/iam.py @@ -440,7 +440,7 @@ def interp(aoi, theta_ref, iam_ref, method='linear', normalize=True): method : str, default 'linear' Specifies the interpolation method. Useful options are: 'linear', 'quadratic', 'cubic'. - See scipy.interpolate.interp1d for more options. + See scipy.interpolate for more options. normalize : boolean, default True When true, the interpolated values are divided by the interpolated @@ -470,7 +470,7 @@ def interp(aoi, theta_ref, iam_ref, method='linear', normalize=True): ''' # Contributed by Anton Driesse (@adriesse), PV Performance Labs. July, 2019 - from scipy.interpolate import interp1d + from scipy.interpolate import CubicSpline, make_interp_spline # Scipy doesn't give the clearest feedback, so check number of points here. MIN_REF_VALS = {'linear': 2, 'quadratic': 3, 'cubic': 4, 1: 2, 2: 3, 3: 4} @@ -483,10 +483,31 @@ def interp(aoi, theta_ref, iam_ref, method='linear', normalize=True): raise ValueError("Negative value(s) found in 'iam_ref'. " "This is not physically possible.") - interpolator = interp1d(theta_ref, iam_ref, kind=method, - fill_value='extrapolate') - aoi_input = aoi + theta_ref = np.asarray(theta_ref) + iam_ref = np.asarray(iam_ref) + + if method == "linear": + spline = make_interp_spline(theta_ref, iam_ref, k=1) + + def interpolator(x): + return spline(x) + + elif method == "quadratic": + spline = make_interp_spline(theta_ref, iam_ref, k=2) + + def interpolator(x): + return spline(x) + elif method == "cubic": + spline = CubicSpline(theta_ref, iam_ref, extrapolate=True) + + def interpolator(x): + return spline(x) + + else: + raise ValueError(f"Invalid interpolation method '{method}'.") + + aoi_input = aoi aoi = np.asanyarray(aoi) aoi = np.abs(aoi) iam = interpolator(aoi) diff --git a/pvlib/spectrum/response.py b/pvlib/spectrum/response.py index 4da92bb32a..9d45eb3cf1 100644 --- a/pvlib/spectrum/response.py +++ b/pvlib/spectrum/response.py @@ -6,7 +6,7 @@ import numpy as np import pandas as pd import scipy.constants -from scipy.interpolate import interp1d +from scipy.interpolate import CubicSpline _PLANCK_BY_LIGHT_SPEED_OVER_ELEMENTAL_CHARGE_BY_BILLION = ( @@ -66,16 +66,16 @@ def get_example_spectral_response(wavelength=None): if wavelength is None: resolution = 5.0 wavelength = np.arange(280, 1200 + resolution, resolution) + x = SR_DATA[0] + y = SR_DATA[1] + spline = CubicSpline( + x, y, + extrapolate=False + ) + values = spline(wavelength) + values[(wavelength < x[0]) | (wavelength > x[-1])] = 0.0 - interpolator = interp1d(SR_DATA[0], SR_DATA[1], - kind='cubic', - bounds_error=False, - fill_value=0.0, - copy=False, - assume_sorted=True) - - sr = pd.Series(data=interpolator(wavelength), index=wavelength) - + sr = pd.Series(data=values, index=wavelength) sr.index.name = 'wavelength' sr.name = 'spectral_response' diff --git a/tests/test_iam.py b/tests/test_iam.py index f5ca231bd4..42c65ae5e6 100644 --- a/tests/test_iam.py +++ b/tests/test_iam.py @@ -213,6 +213,32 @@ def test_iam_interp(): with pytest.raises(ValueError): _iam.interp(0.0, [0, 90], [1, -1]) + # check linear after updating interp1d + theta_ref = np.array([0, 60, 90]) + iam_ref = np.array([1.0, 0.8, 0.0]) + + aoi = np.array([0, 30, 60]) + iam = _iam.interp( + aoi, theta_ref, iam_ref, + method="linear", normalize=False) + expected = np.array([1.0, 0.9, 0.8]) + np.testing.assert_allclose(iam, expected) + + # check quadratic + theta_ref = np.array([0, 30, 60, 90]) + iam_ref = 1.0 - 1e-4 * theta_ref**2 + aoi = np.array([15, 45, 75]) + iam = _iam.interp( + aoi, + theta_ref, + iam_ref, + method="quadratic", + normalize=False + ) + + expected = 1.0 - 1e-4 * aoi**2 + np.testing.assert_allclose(iam, expected, rtol=1e-12) + @pytest.mark.parametrize('aoi,expected', [ (45, 0.9975036250000002), From ab6c74f5db0111aa5968db24c189377c0a4324f7 Mon Sep 17 00:00:00 2001 From: Jason Lun Leung Date: Fri, 24 Apr 2026 10:24:29 +0800 Subject: [PATCH 2/2] Replace CubicSpline by make_interp_spline k=3 --- pvlib/iam.py | 4 ++-- pvlib/spectrum/response.py | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pvlib/iam.py b/pvlib/iam.py index bd8483c591..031ede6f1e 100644 --- a/pvlib/iam.py +++ b/pvlib/iam.py @@ -470,7 +470,7 @@ def interp(aoi, theta_ref, iam_ref, method='linear', normalize=True): ''' # Contributed by Anton Driesse (@adriesse), PV Performance Labs. July, 2019 - from scipy.interpolate import CubicSpline, make_interp_spline + from scipy.interpolate import make_interp_spline # Scipy doesn't give the clearest feedback, so check number of points here. MIN_REF_VALS = {'linear': 2, 'quadratic': 3, 'cubic': 4, 1: 2, 2: 3, 3: 4} @@ -499,7 +499,7 @@ def interpolator(x): return spline(x) elif method == "cubic": - spline = CubicSpline(theta_ref, iam_ref, extrapolate=True) + spline = make_interp_spline(theta_ref, iam_ref, k=3) def interpolator(x): return spline(x) diff --git a/pvlib/spectrum/response.py b/pvlib/spectrum/response.py index 9d45eb3cf1..673917be83 100644 --- a/pvlib/spectrum/response.py +++ b/pvlib/spectrum/response.py @@ -6,7 +6,7 @@ import numpy as np import pandas as pd import scipy.constants -from scipy.interpolate import CubicSpline +from scipy.interpolate import make_interp_spline _PLANCK_BY_LIGHT_SPEED_OVER_ELEMENTAL_CHARGE_BY_BILLION = ( @@ -68,10 +68,9 @@ def get_example_spectral_response(wavelength=None): wavelength = np.arange(280, 1200 + resolution, resolution) x = SR_DATA[0] y = SR_DATA[1] - spline = CubicSpline( - x, y, - extrapolate=False - ) + spline = make_interp_spline( + x, y, k=3) + values = spline(wavelength) values[(wavelength < x[0]) | (wavelength > x[-1])] = 0.0