From 2ed87aa28e5314965a289d0e1f1ffbe71971a087 Mon Sep 17 00:00:00 2001 From: bfs2021 <101915083+bfs2021@users.noreply.github.com> Date: Sat, 10 Oct 2026 16:28:34 +0800 Subject: [PATCH 1/6] scaling.wvm: return unchanged signal for a single site With one position, pdist returns an empty distance array and the effective-size optimizer converges to n_dist=0, so _compute_vr divided by zero and scaling.wvm returned an all-NaN signal. A single site has no variability reduction, so _compute_vr now returns a reduction factor of 1 for every timescale and wvm passes the input through unchanged. Fixes #2895 Signed-off-by: bfs2021 <101915083+bfs2021@users.noreply.github.com> --- pvlib/scaling.py | 5 +++++ tests/test_scaling.py | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/pvlib/scaling.py b/pvlib/scaling.py index 693d9f6bff..683cb26877 100644 --- a/pvlib/scaling.py +++ b/pvlib/scaling.py @@ -127,6 +127,11 @@ def _compute_vr(positions, cloud_speed, tmscales): # Added by Joe Ranalli (@jranalli), Penn State Hazleton, 2021 pos = np.array(positions) + if len(pos) <= 1: + # A single site has no pairwise distances, so the variability + # reduction is 1; the effective-size optimizer below would converge + # to n_dist=0 and divide by zero. See GH #2895. + return np.ones(tmscales.shape) dist = pdist(pos, 'euclidean') # Find effective length of position vector, 'dist' is full pairwise diff --git a/tests/test_scaling.py b/tests/test_scaling.py index f8638f580a..f778f0e65b 100644 --- a/tests/test_scaling.py +++ b/tests/test_scaling.py @@ -207,3 +207,17 @@ def test_wvm_series_xyaslist(clear_sky_index, time, positions, def test_wvm_invalid(clear_sky_index, positions): with pytest.raises(ValueError): scaling.wvm(clear_sky_index, positions, cloud_speed) + + +def test_compute_vr_single_site(expect_tmscale): + vr = scaling._compute_vr(np.array([(0., 0.)]), cloud_speed, + np.array(expect_tmscale)) + assert_almost_equal(vr, np.ones(len(expect_tmscale))) + + +def test_wvm_single_site(clear_sky_index): + # A single site has no variability reduction: the smoothed signal is the + # input unchanged. Previously returned all NaNs. See GH #2895. + single_site = np.array([(0., 0.)]) + cs_sm, _, _ = scaling.wvm(clear_sky_index, single_site, cloud_speed, dt=dt) + assert_almost_equal(cs_sm[5000:5005], clear_sky_index[5000:5005]) From 15078372d1a3d78884fcc78062757d6a9b7a847e Mon Sep 17 00:00:00 2001 From: bfs2021 <101915083+bfs2021@users.noreply.github.com> Date: Sat, 10 Oct 2026 16:33:36 +0800 Subject: [PATCH 2/6] add whatsnew entry for the wvm single-site fix Signed-off-by: bfs2021 <101915083+bfs2021@users.noreply.github.com> --- docs/sphinx/source/whatsnew/v0.16.2.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/sphinx/source/whatsnew/v0.16.2.rst b/docs/sphinx/source/whatsnew/v0.16.2.rst index f0b52494dc..410c7a4bc1 100644 --- a/docs/sphinx/source/whatsnew/v0.16.2.rst +++ b/docs/sphinx/source/whatsnew/v0.16.2.rst @@ -16,6 +16,8 @@ Bug fixes ~~~~~~~~~ * :py:func:`pvlib.tracking.calc_surface_orientation` returns scalar when input is scalar (:issue:`2881`, :pull:`2882`) +* :py:func:`pvlib.scaling.wvm` no longer returns all NaNs when ``positions`` + contains a single site (:issue:`2895`, :pull:`2896`) by :ghuser:`bfs2021` Enhancements ~~~~~~~~~~~~ From fe0219778b456b7df717be042c6afb860203f1a2 Mon Sep 17 00:00:00 2001 From: bfs2021 <101915083+bfs2021@users.noreply.github.com> Date: Fri, 16 Oct 2026 08:52:55 +0800 Subject: [PATCH 3/6] docs: apply whatsnew suggestions from review Move the contributor credit to the Contributors section per the what's-new convention, and list bfs2021 there. Signed-off-by: bfs2021 <101915083+bfs2021@users.noreply.github.com> --- docs/sphinx/source/whatsnew/v0.16.2.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/source/whatsnew/v0.16.2.rst b/docs/sphinx/source/whatsnew/v0.16.2.rst index 410c7a4bc1..23bb877349 100644 --- a/docs/sphinx/source/whatsnew/v0.16.2.rst +++ b/docs/sphinx/source/whatsnew/v0.16.2.rst @@ -17,7 +17,7 @@ Bug fixes * :py:func:`pvlib.tracking.calc_surface_orientation` returns scalar when input is scalar (:issue:`2881`, :pull:`2882`) * :py:func:`pvlib.scaling.wvm` no longer returns all NaNs when ``positions`` - contains a single site (:issue:`2895`, :pull:`2896`) by :ghuser:`bfs2021` + contains a single site (:issue:`2895`, :pull:`2896`) Enhancements ~~~~~~~~~~~~ @@ -58,6 +58,7 @@ Contributors * Chen Yi (:ghuser:`chenyi6758-source`) * Rajiv Daxini (:ghuser:`rdaxini`) * jason-writer (:ghuser:`jason-writer`) +* :ghuser:`bfs2021` From 8f64f867eaca117c437a7b958e9573ebb2195321 Mon Sep 17 00:00:00 2001 From: bfs2021 <101915083+bfs2021@users.noreply.github.com> Date: Mon, 19 Oct 2026 08:54:55 +0800 Subject: [PATCH 4/6] scaling.wvm: clarify the positions input and guard only one site - positions is documented as an (N, 2) array of (x, y) coordinates in both wvm and _compute_vr, which is the shape pdist consumes. - The single-site guard is len(pos) == 1 now: an empty array is out of scope and falls through to produce an invalid result. Signed-off-by: bfs2021 <101915083+bfs2021@users.noreply.github.com> --- pvlib/scaling.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pvlib/scaling.py b/pvlib/scaling.py index 683cb26877..67d10f17e5 100644 --- a/pvlib/scaling.py +++ b/pvlib/scaling.py @@ -23,10 +23,10 @@ def wvm(clearsky_index, positions, cloud_speed, dt=None): clearsky_index : numeric or pandas.Series Clear Sky Index time series that will be smoothed. - positions : numeric - Array of coordinate distances as (x,y) pairs representing the - easting, northing of the site positions in meters [m]. Distributed - plants could be simulated by gridded points throughout the plant + positions : np.array + Array of shape (N, 2) of coordinates (x, y) representing the + easting, northing in meters [m] of the PV systems. Large PV + plants could be simulated by gridded points over the plant's footprint. cloud_speed : numeric @@ -93,10 +93,10 @@ def _compute_vr(positions, cloud_speed, tmscales): Parameters ---------- - positions : numeric - Array of coordinate distances as (x,y) pairs representing the - easting, northing of the site positions in meters [m]. Distributed - plants could be simulated by gridded points throughout the plant + positions : np.array + Array of shape (N, 2) of coordinates (x, y) representing the + easting, northing in meters [m] of the PV systems. Large PV + plants could be simulated by gridded points over the plant's footprint. cloud_speed : numeric @@ -127,7 +127,7 @@ def _compute_vr(positions, cloud_speed, tmscales): # Added by Joe Ranalli (@jranalli), Penn State Hazleton, 2021 pos = np.array(positions) - if len(pos) <= 1: + if len(pos) == 1: # A single site has no pairwise distances, so the variability # reduction is 1; the effective-size optimizer below would converge # to n_dist=0 and divide by zero. See GH #2895. From 804901de0773c0a1d5495d2c22fa37baa5803a15 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 20 Oct 2026 07:52:46 -0600 Subject: [PATCH 5/6] Apply batched suggestions from code review Co-authored-by: Kevin Anderson --- docs/sphinx/source/whatsnew/v0.16.2.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/source/whatsnew/v0.16.2.rst b/docs/sphinx/source/whatsnew/v0.16.2.rst index 23bb877349..933d581946 100644 --- a/docs/sphinx/source/whatsnew/v0.16.2.rst +++ b/docs/sphinx/source/whatsnew/v0.16.2.rst @@ -59,6 +59,6 @@ Contributors * Rajiv Daxini (:ghuser:`rdaxini`) * jason-writer (:ghuser:`jason-writer`) * :ghuser:`bfs2021` - +* :ghuser:`bfs2021` From 2139a167d3257c8dfff47ed63cc1c3aa0cbb2a8e Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 20 Oct 2026 07:53:21 -0600 Subject: [PATCH 6/6] Update docs/sphinx/source/whatsnew/v0.16.2.rst --- docs/sphinx/source/whatsnew/v0.16.2.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/sphinx/source/whatsnew/v0.16.2.rst b/docs/sphinx/source/whatsnew/v0.16.2.rst index 933d581946..6cea2019e0 100644 --- a/docs/sphinx/source/whatsnew/v0.16.2.rst +++ b/docs/sphinx/source/whatsnew/v0.16.2.rst @@ -59,6 +59,5 @@ Contributors * Rajiv Daxini (:ghuser:`rdaxini`) * jason-writer (:ghuser:`jason-writer`) * :ghuser:`bfs2021` -* :ghuser:`bfs2021`