Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/sphinx/source/whatsnew/v0.15.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ Deprecations

Bug fixes
~~~~~~~~~
* Fix incorrect ``delta_kt_prime`` calculation in
:py:func:`pvlib.irradiance.dirint` for series containing internal NaN
values (e.g. multi-day data with nighttime gaps). Edge positions with
only one valid neighbor now correctly use that single delta value
(Perez eqn 3) instead of halving it.
(:issue:`1847`, :pull:`2698`)
* Fix a division-by-zero condition in
:py:func:`pvlib.transformer.simple_efficiency` when ``load_loss = 0``.
(:issue:`2645`, :pull:`2646`)
Expand Down Expand Up @@ -57,6 +63,7 @@ Maintenance

Contributors
~~~~~~~~~~~~
* Ishaan Arora (:ghuser:`ishaan-arora-1`)
* Aman Srivastava (:ghuser:`aman-coder03`)
* Rajiv Daxini (:ghuser:`RDaxini`)
* Echedey Luis (:ghuser:`echedey-ls`)
Expand Down
15 changes: 7 additions & 8 deletions pvlib/irradiance.py
Original file line number Diff line number Diff line change
Expand Up @@ -2026,16 +2026,15 @@ def _delta_kt_prime_dirint(kt_prime, use_delta_kt_prime, times):
for use with :py:func:`_dirint_bins`.
"""
if use_delta_kt_prime:
# Perez eqn 2
# Perez eqn 2 (both neighbors) and eqn 3 (one neighbor).
# mean(axis=1) skips NaN so that edge positions with only one
# valid neighbor return that single delta instead of halving it.
kt_next = kt_prime.shift(-1)
kt_previous = kt_prime.shift(1)
# replace nan with values that implement Perez Eq 3 for first and last
# positions. Use kt_previous and kt_next to handle series of length 1
kt_next.iloc[-1] = kt_previous.iloc[-1]
kt_previous.iloc[0] = kt_next.iloc[0]
delta_kt_prime = 0.5 * ((kt_prime - kt_next).abs().add(
(kt_prime - kt_previous).abs(),
fill_value=0))
delta_kt_prime = pd.DataFrame({
'next': (kt_prime - kt_next).abs(),
'prev': (kt_prime - kt_previous).abs(),
}).mean(axis=1)
else:
# do not change unless also modifying _dirint_bins
delta_kt_prime = pd.Series(-1, index=times)
Expand Down
32 changes: 32 additions & 0 deletions tests/test_irradiance.py
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,38 @@ def test_dirint_no_delta_kt():
np.array([861.9, 670.4]), 1)


def test_delta_kt_prime_dirint_multiday():
# GH 1847: _delta_kt_prime_dirint mishandled NaN boundaries between
# days, halving the delta at edge positions instead of using the
# single available neighbor value (Perez eqn 3).
times = pd.date_range('2014-01-01T05', periods=15, freq='1h',
tz='Etc/GMT+0')
kt_prime = pd.Series(
[np.nan, np.nan, np.nan,
0.29458475, 0.21863506, 0.37650014,
0.41238529, 0.23375275, 0.23363453,
0.26348652, 0.25412631, 0.43794681,
np.nan, np.nan, np.nan],
index=times)
result = irradiance._delta_kt_prime_dirint(kt_prime, True, times)
# first valid value (index 3): only forward neighbor exists,
# delta_kt_prime == |kt[3] - kt[4]| (Perez eqn 3)
expected_first = abs(kt_prime.iloc[3] - kt_prime.iloc[4])
assert_almost_equal(result.iloc[3], expected_first)
# last valid value (index 11): only backward neighbor exists,
# delta_kt_prime == |kt[11] - kt[10]| (Perez eqn 3)
expected_last = abs(kt_prime.iloc[11] - kt_prime.iloc[10])
assert_almost_equal(result.iloc[11], expected_last)
# interior valid value (index 6): both neighbors exist,
# delta_kt_prime == mean of both deltas (Perez eqn 2)
expected_mid = 0.5 * (abs(kt_prime.iloc[6] - kt_prime.iloc[7])
+ abs(kt_prime.iloc[6] - kt_prime.iloc[5]))
assert_almost_equal(result.iloc[6], expected_mid)
# NaN positions should remain NaN
assert np.isnan(result.iloc[0])
assert np.isnan(result.iloc[-1])


def test_dirint_coeffs():
coeffs = irradiance._get_dirint_coeffs()
assert coeffs[0, 0, 0, 0] == 0.385230
Expand Down