diff --git a/docs/sphinx/source/whatsnew/v0.15.2.rst b/docs/sphinx/source/whatsnew/v0.15.2.rst index 37f8692280..f1bc3742f0 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:`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 increase slightly. diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index 5a4a76df25..bb737560bc 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,6 +1983,7 @@ 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) @@ -2001,6 +2002,8 @@ 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 @@ -2109,6 +2112,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. diff --git a/tests/test_irradiance.py b/tests/test_irradiance.py index a416636ae9..10f7ee9697 100644 --- a/tests/test_irradiance.py +++ b/tests/test_irradiance.py @@ -1140,6 +1140,36 @@ 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""" + 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]) dhi = pd.Series([100, 90, 50, 50, 50])