From b8f2634643cefa2ebec2e44274ed08e1b01a437c Mon Sep 17 00:00:00 2001 From: Omesh37 Date: Fri, 8 May 2026 13:10:52 +0530 Subject: [PATCH 1/5] BUG: fix dirint KeyError with scalar inputs on pandas >= 2.0 --- pvlib/irradiance.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index 5a4a76df25..abdd565779 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -2109,6 +2109,14 @@ def _dirint_bins(times, kt_prime, zenith, w, delta_kt_prime): ------- tuple of kt_prime_bin, zenith_bin, w_bin, delta_kt_prime_bin """ + # Ensure scalar inputs are converted to Series so that boolean masks + # produce a boolean Series rather than a scalar bool. + # Scalar bools cause KeyError in pandas >= 2.0. GH #XXXX + kt_prime = pd.Series(kt_prime, index=times, dtype=float) + zenith = pd.Series(zenith, index=times, dtype=float) + w = pd.Series(w, index=times, dtype=float) + delta_kt_prime = pd.Series(delta_kt_prime, index=times, dtype=float) + # @wholmgren: the following bin assignments use MATLAB's 1-indexing. # Later, we'll subtract 1 to conform to Python's 0-indexing. From 33f8c49bd868cdd2a47aed9d809410be295455f2 Mon Sep 17 00:00:00 2001 From: Omesh37 Date: Fri, 8 May 2026 13:24:40 +0530 Subject: [PATCH 2/5] TST,CHANGELOG: add test and whatsnew entry for dirint scalar fix GH#2751 --- docs/sphinx/source/whatsnew/v0.15.2.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/sphinx/source/whatsnew/v0.15.2.rst b/docs/sphinx/source/whatsnew/v0.15.2.rst index 37f8692280..5d7b456d24 100644 --- a/docs/sphinx/source/whatsnew/v0.15.2.rst +++ b/docs/sphinx/source/whatsnew/v0.15.2.rst @@ -14,6 +14,9 @@ Deprecations Bug fixes ~~~~~~~~~ +* Fixed :py:func:`pvlib.irradiance.dirint` raising ``KeyError`` with + scalar inputs on pandas >= 2.0. (:pull:`XXXX`, :issue:`2751`) + By :ghuser:`Omesh37`. * Corrects a bug in :py:func:`pvlib.temperature.fuentes`. If inputs were data type integer, users can expect modeled cell temperature values to increase slightly. From 343fbe903b7716452a1f7eca10024dd811d44b8f Mon Sep 17 00:00:00 2001 From: Omesh37 Date: Sat, 9 May 2026 20:34:19 +0530 Subject: [PATCH 3/5] BUG: fix dirint KeyError for scalar inputs on pandas >= 2.0 --- docs/sphinx/source/whatsnew/v0.15.2.rst | 2 +- pvlib/irradiance.py | 17 +++++++++------ tests/test_irradiance.py | 28 +++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.15.2.rst b/docs/sphinx/source/whatsnew/v0.15.2.rst index 5d7b456d24..f1bc3742f0 100644 --- a/docs/sphinx/source/whatsnew/v0.15.2.rst +++ b/docs/sphinx/source/whatsnew/v0.15.2.rst @@ -15,7 +15,7 @@ Deprecations Bug fixes ~~~~~~~~~ * Fixed :py:func:`pvlib.irradiance.dirint` raising ``KeyError`` with - scalar inputs on pandas >= 2.0. (:pull:`XXXX`, :issue:`2751`) + scalar inputs on pandas >= 2.0. (:pull:`2752`, :issue:`2751`) By :ghuser:`Omesh37`. * Corrects a bug in :py:func:`pvlib.temperature.fuentes`. If inputs were data type integer, users can expect modeled cell temperature values to diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index abdd565779..7937b6f443 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -1927,10 +1927,10 @@ def dirint(ghi, solar_zenith, times, pressure=101325., use_delta_kt_prime=True, Parameters ---------- - ghi : array-like + ghi : numeric Global horizontal irradiance. See :term:`ghi`. [Wm⁻²] - solar_zenith : array-like + solar_zenith : numeric True (not refraction-corrected) solar zenith angles. See :term:`solar_zenith`. [°] @@ -1964,9 +1964,9 @@ def dirint(ghi, solar_zenith, times, pressure=101325., use_delta_kt_prime=True, Returns ------- - dni : array-like - The modeled direct normal irradiance, as provided by the - DIRINT model. [Wm⁻²] + dni : numeric or pd.Series + Estimated direct normal irradiance. Returns float if all inputs + are scalar, pd.Series otherwise. [Wm⁻²] Notes ----- @@ -1983,7 +1983,8 @@ def dirint(ghi, solar_zenith, times, pressure=101325., use_delta_kt_prime=True, Global Horizontal to Direct Normal Insolation", Technical Report No. SERI/TR-215-3087, Golden, CO: Solar Energy Research Institute, 1987. """ - + scalar_input = np.isscalar(solar_zenith) + disc_out = disc(ghi, solar_zenith, times, pressure=pressure, min_cos_zenith=min_cos_zenith, max_zenith=max_zenith) airmass = disc_out['airmass'] @@ -1998,9 +1999,12 @@ def dirint(ghi, solar_zenith, times, pressure=101325., use_delta_kt_prime=True, dirint_coeffs = _dirint_coeffs(times, kt_prime, solar_zenith, w, delta_kt_prime) + # Perez eqn 5 dni = disc_out['dni'] * dirint_coeffs + if scalar_input: + return float(dni.iloc[0]) return dni @@ -2160,6 +2164,7 @@ def _dirint_bins(times, kt_prime, zenith, w, delta_kt_prime): return kt_prime_bin, zenith_bin, w_bin, delta_kt_prime_bin + def dirindex(ghi, ghi_clear, dni_clear, zenith, times, pressure=101325., use_delta_kt_prime=True, temp_dew=None, min_cos_zenith=0.065, max_zenith=87): diff --git a/tests/test_irradiance.py b/tests/test_irradiance.py index a416636ae9..ea5a410d7f 100644 --- a/tests/test_irradiance.py +++ b/tests/test_irradiance.py @@ -1139,6 +1139,34 @@ def test_dirindex_min_cos_zenith_max_zenith(): expected = pd.Series([nan, 5.], index=times) assert_series_equal(out, expected) +def test_dirint_scalar_inputs(): + """Scalar numeric inputs return scalar float output. GH #2751""" + times = pd.DatetimeIndex(['2023-06-21 12:00'], tz='UTC') + + # scalar int input → should return float (not Series) + result = irradiance.dirint( + ghi=500, solar_zenith=45, times=times + ) + assert np.isscalar(result), "scalar input should return scalar output" + assert isinstance(result, float) + + # scalar float with delta_kt_prime disabled → non-NaN value check + result = irradiance.dirint( + ghi=500.0, solar_zenith=45.0, times=times, + use_delta_kt_prime=False + ) + assert np.isscalar(result) + assert isinstance(result, float) + assert result > 0 # should be positive DNI + + # Series input → should still return Series (regression check) + times2 = pd.date_range('2023-06-21 10:00', periods=3, freq='h', tz='UTC') + result_series = irradiance.dirint( + ghi=pd.Series([400, 500, 300], index=times2), + solar_zenith=pd.Series([50, 40, 60], index=times2), + times=times2 + ) + assert isinstance(result_series, pd.Series) def test_dni(): ghi = pd.Series([90, 100, 100, 100, 100]) From fe8055c223e17e66645bbd58e90e723bf138c3b9 Mon Sep 17 00:00:00 2001 From: Omesh37 Date: Sat, 9 May 2026 20:59:40 +0530 Subject: [PATCH 4/5] STY: fix flake8 whitespace and blank line errors --- pvlib/irradiance.py | 4 +--- tests/test_irradiance.py | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index 7937b6f443..bb737560bc 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -1984,7 +1984,7 @@ def dirint(ghi, solar_zenith, times, pressure=101325., use_delta_kt_prime=True, SERI/TR-215-3087, Golden, CO: Solar Energy Research Institute, 1987. """ scalar_input = np.isscalar(solar_zenith) - + disc_out = disc(ghi, solar_zenith, times, pressure=pressure, min_cos_zenith=min_cos_zenith, max_zenith=max_zenith) airmass = disc_out['airmass'] @@ -1999,7 +1999,6 @@ def dirint(ghi, solar_zenith, times, pressure=101325., use_delta_kt_prime=True, dirint_coeffs = _dirint_coeffs(times, kt_prime, solar_zenith, w, delta_kt_prime) - # Perez eqn 5 dni = disc_out['dni'] * dirint_coeffs @@ -2164,7 +2163,6 @@ def _dirint_bins(times, kt_prime, zenith, w, delta_kt_prime): return kt_prime_bin, zenith_bin, w_bin, delta_kt_prime_bin - def dirindex(ghi, ghi_clear, dni_clear, zenith, times, pressure=101325., use_delta_kt_prime=True, temp_dew=None, min_cos_zenith=0.065, max_zenith=87): diff --git a/tests/test_irradiance.py b/tests/test_irradiance.py index ea5a410d7f..10f7ee9697 100644 --- a/tests/test_irradiance.py +++ b/tests/test_irradiance.py @@ -1139,6 +1139,7 @@ def test_dirindex_min_cos_zenith_max_zenith(): expected = pd.Series([nan, 5.], index=times) assert_series_equal(out, expected) + def test_dirint_scalar_inputs(): """Scalar numeric inputs return scalar float output. GH #2751""" times = pd.DatetimeIndex(['2023-06-21 12:00'], tz='UTC') @@ -1168,6 +1169,7 @@ def test_dirint_scalar_inputs(): ) assert isinstance(result_series, pd.Series) + def test_dni(): ghi = pd.Series([90, 100, 100, 100, 100]) dhi = pd.Series([100, 90, 50, 50, 50]) From 242830c4d0ed2145049f5e3dca3dfae5b042c6e2 Mon Sep 17 00:00:00 2001 From: Omesh37 Date: Mon, 11 May 2026 08:37:02 +0530 Subject: [PATCH 5/5] BUG: add test coverage for dirint with array-like inputs GH#2751 --- docs/sphinx/source/whatsnew/v0.15.2.rst | 6 ++--- pvlib/irradiance.py | 13 +++-------- tests/test_irradiance.py | 29 ++++++++++--------------- 3 files changed, 18 insertions(+), 30 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.15.2.rst b/docs/sphinx/source/whatsnew/v0.15.2.rst index f1bc3742f0..ac56307b23 100644 --- a/docs/sphinx/source/whatsnew/v0.15.2.rst +++ b/docs/sphinx/source/whatsnew/v0.15.2.rst @@ -14,9 +14,9 @@ Deprecations Bug fixes ~~~~~~~~~ -* Fixed :py:func:`pvlib.irradiance.dirint` raising ``KeyError`` with - scalar inputs on pandas >= 2.0. (:pull:`2752`, :issue:`2751`) - By :ghuser:`Omesh37`. +* Added test coverage for :py:func:`pvlib.irradiance.dirint` with + ``np.array`` and ``pd.Series`` inputs. + (:issue:`2751`, :pull:`2752`) * Corrects a bug in :py:func:`pvlib.temperature.fuentes`. If inputs were data type integer, users can expect modeled cell temperature values to increase slightly. diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index bb737560bc..5f4a28cb2f 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -1927,10 +1927,10 @@ def dirint(ghi, solar_zenith, times, pressure=101325., use_delta_kt_prime=True, Parameters ---------- - ghi : numeric + ghi : array-like Global horizontal irradiance. See :term:`ghi`. [Wm⁻²] - solar_zenith : numeric + solar_zenith : array-like True (not refraction-corrected) solar zenith angles. See :term:`solar_zenith`. [°] @@ -1964,7 +1964,7 @@ def dirint(ghi, solar_zenith, times, pressure=101325., use_delta_kt_prime=True, Returns ------- - dni : numeric or pd.Series + dni : pd.Series Estimated direct normal irradiance. Returns float if all inputs are scalar, pd.Series otherwise. [Wm⁻²] @@ -1983,7 +1983,6 @@ def dirint(ghi, solar_zenith, times, pressure=101325., use_delta_kt_prime=True, Global Horizontal to Direct Normal Insolation", Technical Report No. SERI/TR-215-3087, Golden, CO: Solar Energy Research Institute, 1987. """ - scalar_input = np.isscalar(solar_zenith) disc_out = disc(ghi, solar_zenith, times, pressure=pressure, min_cos_zenith=min_cos_zenith, max_zenith=max_zenith) @@ -2002,8 +2001,6 @@ def dirint(ghi, solar_zenith, times, pressure=101325., use_delta_kt_prime=True, # Perez eqn 5 dni = disc_out['dni'] * dirint_coeffs - if scalar_input: - return float(dni.iloc[0]) return dni @@ -2115,10 +2112,6 @@ def _dirint_bins(times, kt_prime, zenith, w, delta_kt_prime): # Ensure scalar inputs are converted to Series so that boolean masks # produce a boolean Series rather than a scalar bool. # Scalar bools cause KeyError in pandas >= 2.0. GH #XXXX - kt_prime = pd.Series(kt_prime, index=times, dtype=float) - zenith = pd.Series(zenith, index=times, dtype=float) - w = pd.Series(w, index=times, dtype=float) - delta_kt_prime = pd.Series(delta_kt_prime, index=times, dtype=float) # @wholmgren: the following bin assignments use MATLAB's 1-indexing. # Later, we'll subtract 1 to conform to Python's 0-indexing. diff --git a/tests/test_irradiance.py b/tests/test_irradiance.py index 10f7ee9697..8654443722 100644 --- a/tests/test_irradiance.py +++ b/tests/test_irradiance.py @@ -1140,34 +1140,29 @@ def test_dirindex_min_cos_zenith_max_zenith(): assert_series_equal(out, expected) -def test_dirint_scalar_inputs(): - """Scalar numeric inputs return scalar float output. GH #2751""" +def test_dirint_array_inputs(): + """np.array inputs work correctly. GH #2751""" times = pd.DatetimeIndex(['2023-06-21 12:00'], tz='UTC') - # scalar int input → should return float (not Series) + # np.array input (1-element) -- return pd.Series result = irradiance.dirint( - ghi=500, solar_zenith=45, times=times - ) - assert np.isscalar(result), "scalar input should return scalar output" - assert isinstance(result, float) - - # scalar float with delta_kt_prime disabled → non-NaN value check - result = irradiance.dirint( - ghi=500.0, solar_zenith=45.0, times=times, + ghi=np.array([500.0]), + solar_zenith=np.array([45.0]), + times=times, use_delta_kt_prime=False ) - assert np.isscalar(result) - assert isinstance(result, float) - assert result > 0 # should be positive DNI + assert isinstance(result, pd.Series) + assert result.iloc[0] > 0 - # Series input → should still return Series (regression check) + # pd.Series input -- return pd.Series times2 = pd.date_range('2023-06-21 10:00', periods=3, freq='h', tz='UTC') - result_series = irradiance.dirint( + result2 = irradiance.dirint( ghi=pd.Series([400, 500, 300], index=times2), solar_zenith=pd.Series([50, 40, 60], index=times2), times=times2 ) - assert isinstance(result_series, pd.Series) + assert isinstance(result2, pd.Series) + assert (result2 >= 0).all() def test_dni():