You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
BUG: irradiance.dirint raises KeyError with scalar inputs on pandas >= 2.0 #2751
pvlib.irradiance.dirint raises KeyError: 'cannot use a single bool to index into setitem' when called with scalar solar_zenith (or other
scalar inputs) on pandas >= 2.0.
KeyError: 'cannot use a single bool to index into setitem'
File "pvlib/irradiance.py", in _dirint_bins
zenith_bin[(zenith >= 0) & (zenith < 25)] = 1
Root cause
In _dirint_bins, when scalar values are passed, boolean expressions like (zenith >= 0) & (zenith < 25) evaluate to a plain Python bool (e.g. False). In pandas >= 2.0, series[False] = value raises KeyError
because pandas now treats scalar bools as label lookups, not boolean masks.
Expected behavior
Returns NaN or 0.0 cleanly without crashing.
Fix
Convert all scalar inputs to pd.Series at the top of _dirint_bins:
@Omesh37 thanks for the report, and we'd welcome help addressing it.
I think the fix goes beyond #2752 and I'm not sure that casting the inputs to Series internal to dirint is the way to go.
For starters, the docstring for dirint says the type of several inputs (the key one is solar_zenith) is array-like. Some time ago we had a long debate #765 about what array-like meant, which in pvlib means np.array or pd.Series.
If we want to extend dirint to accept float then the type should be numeric. This is the first decision.
If we do want float input, then I think the fix is to make irradiance._dirint_bins work with float, which may involve casting internal to that function.
Based on discussion in use array_like instead of numeric/array-like #765, what I think is that dirint should accept numeric inputs (float/int scalars in addition to array-like), since it's a natural use case to call it with a single value.
My proposed updated approach:
Change the type annotation for ghi, solar_zenith, pressure, and temp_dew in the dirint docstring from array-like to numeric
Keep the fix inside _dirint_bins — cast scalar inputs to pd.Series using the times index so boolean masking always produces a boolean Series (not a scalar bool)
Does that direction sound right to you? Happy to update the PR accordingly :)
@Omesh37 yes, that's what I was thinking. However, please keep in mind that a pvlib function should (not must) return the same type as its inputs. In this case, if the input to dirint is float, then dirint should return dni as float. We try to do that without introducing a lot of if isinstance() kind of statement.
@Omesh37 Now that I've seen what #2752 would look like, I'm reconsidering my guidance. Adding float to accepted types for dirint creates expectations that the closely-related dirindex function also accepts float (all but one of its inputs). I'm not sure we want to do all that.
The current dirint works with arrays (which is not tested by the CI), and that's the intent of specifying array-like: array or Series.
The float case above works correctly as long as solar_zenith is an array or Series:
# works
pvlib.irradiance.dirint(
ghi=np.array([0,]),
solar_zenith=np.array([90,]),
times=pd.DatetimeIndex(['2023-06-21'], tz='UTC')
)
# also works but not by intent
pvlib.irradiance.dirint(
ghi=0,
solar_zenith=np.array([90,]),
times=pd.DatetimeIndex(['2023-06-21'], tz='UTC')
)
I'm inclined to stick with the array-like type, exclude floats, and ask instead that we add array to the types being tested. What do you think about the need/value of supporting float inputs?
Thanks for the clarification @cwhanse ! I agree on keeping array-like is cleaner. The float case is easily handled by the user wrapping in np.array([value]), and officially supporting scalars adds complexity that isn't worth it.
Here is what I can do:
Revert scalar_input check and float return from dirint
Revert docstring back to array-like
Revert _dirint_bins changes (not needed for proper array inputs)
Update the test to use np.array inputs instead of plain int/float
Add a test for checking np.array and pd.Series both work correctly
The new test uncovers two other discrepancies: dirint always returns Series even when no input is Series. Also, the algorithm requires 2 time points, but the code doesn't error when all inputs are length 1. Maybe that's what we want?
I think I should talk with other maintainers before asking to change direction a third time here :)
Thanks @cwhanse for reviewing the code :)
So, basically point of discussion is that:
Should dirint return np.array when given np.array inputs (which will match input-type = output-type convention)?
Should dirint raise a warning or error when only 1 time point is provided, since the algorithm requires 2+ points for delta_kt_prime?
I'll wait for your direction for updating the PR :)
return type. Should dirint return np.array when np.array is input? Currently, pd.Series is returned for any valid inputs, due to some casting in a private function.
should dirint raise a warning or error when inputs are time-length 1? The algorithm requires 2 time points. pvlib's code runs but returns a single NaN.
1. return type. Should `dirint` return `np.array` when `np.array` is input? Currently, `pd.Series` is returned for any valid inputs, due to some casting in a private function.
Since a DatetimeIndex is a required input, it seems almost certain that the user is going to be working with pandas objects here anyway. There are several inputs that could be arrays or series, so what would be the logic? I would not object to returning the pd.Series to keep it simple.
2. should `dirint` raise a warning or error when inputs are time-length 1? The algorithm requires 2 time points. pvlib's code runs but returns a single NaN.
No preference. Would depend on use_delta_kt_prime=True, right?
Closing, as it seems like discussion concluded with not fixing anything with scalar inputs. The linked PR #2752 relates to test coverage for array and series inputs.
Activity
cwhanse commentedon May 8, 2026
@Omesh37 thanks for the report, and we'd welcome help addressing it.
I think the fix goes beyond #2752 and I'm not sure that casting the inputs to Series internal to
dirintis the way to go.For starters, the docstring for
dirintsays the type of several inputs (the key one issolar_zenith) isarray-like. Some time ago we had a long debate #765 about whatarray-likemeant, which in pvlib meansnp.arrayorpd.Series.If we want to extend
dirintto acceptfloatthen the type should benumeric. This is the first decision.If we do want
floatinput, then I think the fix is to makeirradiance._dirint_binswork withfloat, which may involve casting internal to that function.Omesh37 commentedon May 9, 2026
Thanks @cwhanse for the feedback.
dirintshould acceptnumericinputs (float/int scalars in addition to array-like), since it's a natural use case to call it with a single value.ghi,solar_zenith,pressure, andtemp_dewin thedirintdocstring fromarray-liketonumeric_dirint_bins— cast scalar inputs topd.Seriesusing thetimesindex so boolean masking always produces a boolean Series (not a scalar bool)Does that direction sound right to you? Happy to update the PR accordingly :)
cwhanse commentedon May 9, 2026
@Omesh37 yes, that's what I was thinking. However, please keep in mind that a pvlib function should (not must) return the same type as its inputs. In this case, if the input to
dirintisfloat, thendirintshould returndniasfloat. We try to do that without introducing a lot ofif isinstance()kind of statement.Omesh37 commentedon May 10, 2026
Thanks @cwhanse for conformation and guidance.
I have updated pull request #2752.
Please let me know if anything needs to be changed.
cwhanse commentedon May 10, 2026
@Omesh37 Now that I've seen what #2752 would look like, I'm reconsidering my guidance. Adding
floatto accepted types fordirintcreates expectations that the closely-relateddirindexfunction also acceptsfloat(all but one of its inputs). I'm not sure we want to do all that.The current
dirintworks with arrays (which is not tested by the CI), and that's the intent of specifyingarray-like: array or Series.The
floatcase above works correctly as long assolar_zenithis an array or Series:I'm inclined to stick with the
array-liketype, exclude floats, and ask instead that we addarrayto the types being tested. What do you think about the need/value of supportingfloatinputs?Omesh37 commentedon May 11, 2026
Thanks for the clarification @cwhanse ! I agree on keeping
array-likeis cleaner. The float case is easily handled by the user wrapping innp.array([value]), and officially supporting scalars adds complexity that isn't worth it.Here is what I can do:
scalar_inputcheck and float return fromdirintarray-like_dirint_binschanges (not needed for proper array inputs)np.arrayinputs instead of plain int/floatnp.arrayandpd.Seriesboth work correctlyDoes it sound good?
cwhanse commentedon May 11, 2026
@Omesh37 thanks, I agree this is cleaner.
The new test uncovers two other discrepancies:
dirintalways returnsSerieseven when no input isSeries. Also, the algorithm requires 2 time points, but the code doesn't error when all inputs are length 1. Maybe that's what we want?I think I should talk with other maintainers before asking to change direction a third time here :)
Omesh37 commentedon May 11, 2026
Thanks @cwhanse for reviewing the code :)
So, basically point of discussion is that:
dirintreturnnp.arraywhen givennp.arrayinputs (which will match input-type = output-type convention)?dirintraise a warning or error when only 1 time point is provided, since the algorithm requires 2+ points fordelta_kt_prime?I'll wait for your direction for updating the PR :)
cwhanse commentedon May 15, 2026
@pvlib/pvlib-core two points of decision here:
return type. Should
dirintreturnnp.arraywhennp.arrayis input? Currently,pd.Seriesis returned for any valid inputs, due to some casting in a private function.should
dirintraise a warning or error when inputs are time-length 1? The algorithm requires 2 time points. pvlib's code runs but returns a single NaN.adriesse commentedon May 15, 2026
Since a
DatetimeIndexis a required input, it seems almost certain that the user is going to be working with pandas objects here anyway. There are several inputs that could be arrays or series, so what would be the logic? I would not object to returning thepd.Seriesto keep it simple.No preference. Would depend on
use_delta_kt_prime=True, right?Omesh37 commentedon May 16, 2026
Thanks for the input @adriesse!
To confirm my understanding:
pd.Seriesalwaysuse_delta_kt_prime=TrueI'll wait for your instruction before making further changes :)
cwhanse commentedon May 22, 2026
@Omesh37 let's accept current behavior (always returns Series) and change the Returns part of docstring to state that.
kandersolar commentedon Jun 10, 2026
Closing, as it seems like discussion concluded with not fixing anything with scalar inputs. The linked PR #2752 relates to test coverage for array and series inputs.