From f39bcf95094222b56066350cbcc917feca9fbbcb Mon Sep 17 00:00:00 2001 From: Cedric Leroy Date: Mon, 26 Jun 2023 10:40:56 -0400 Subject: [PATCH 1/4] Prevent `v_oc` to be negative in `_lambertw` --- docs/sphinx/source/whatsnew/v0.10.0.rst | 3 +++ pvlib/singlediode.py | 7 +++++++ pvlib/tests/test_singlediode.py | 15 +++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/docs/sphinx/source/whatsnew/v0.10.0.rst b/docs/sphinx/source/whatsnew/v0.10.0.rst index ab58828653..ee9388b29b 100644 --- a/docs/sphinx/source/whatsnew/v0.10.0.rst +++ b/docs/sphinx/source/whatsnew/v0.10.0.rst @@ -49,6 +49,8 @@ Enhancements Bug fixes ~~~~~~~~~ +* Prevent `v_oc` to be negative in `_lambertw` (:pull:`1782`) + Testing ~~~~~~~ @@ -71,3 +73,4 @@ Contributors * Adam R. Jensen (:ghuser:`AdamRJensen`) * Echedey Luis (:ghuser:`echedey-ls`) * Cliff Hansen (:ghuser:`cwhanse`) +* Cédric Leroy (:ghuser:`cedricleroy`) diff --git a/pvlib/singlediode.py b/pvlib/singlediode.py index a4a760f1b9..e70bb02f36 100644 --- a/pvlib/singlediode.py +++ b/pvlib/singlediode.py @@ -794,6 +794,13 @@ def _lambertw(photocurrent, saturation_current, resistance_series, # Compute open circuit voltage v_oc = _lambertw_v_from_i(0., **params) + # Set elements <0 or close to 0 in v_oc to 0 + if isinstance(v_oc, np.ndarray): + v_oc[(v_oc < 0) | ((v_oc > 0) & (v_oc < 1e-12))] = 0. + elif isinstance(v_oc, (float, int)): + if v_oc < 0 or 0 < v_oc < 1e-12: + v_oc = 0. + # Find the voltage, v_mp, where the power is maximized. # Start the golden section search at v_oc * 1.14 p_mp, v_mp = _golden_sect_DataFrame(params, 0., v_oc * 1.14, _pwr_optfcn) diff --git a/pvlib/tests/test_singlediode.py b/pvlib/tests/test_singlediode.py index 1223e73e8e..aad7f19979 100644 --- a/pvlib/tests/test_singlediode.py +++ b/pvlib/tests/test_singlediode.py @@ -168,6 +168,21 @@ def test_singlediode_precision(method, precise_iv_curves): assert np.allclose(pc['i_xx'], outs['i_xx'], atol=1e-6, rtol=0) +def test_singlediode_lambert_negative_voc(): + + x1 = np.array([0., 1.480501e-11, 0.178, 8000., 1.797559]) + outs = pvsystem.singlediode(*x1, method='lambertw') + assert outs['v_oc'] == 0 + + x2 = np.array([0., 1.456894e-11, 0.178, 8000., 1.797048]) + outs = pvsystem.singlediode(*x2, method='lambertw') + assert outs['v_oc'] == 0 + + x = np.array([x1, x2]).T + outs = pvsystem.singlediode(*x, method='lambertw') + assert np.array_equal(outs['v_oc'], [0, 0]) + + @pytest.mark.parametrize('method', ['lambertw']) def test_ivcurve_pnts_precision(method, precise_iv_curves): """ From 6a0058b4fbcfd79eabf4d764bdfe0efed6599e67 Mon Sep 17 00:00:00 2001 From: Cedric Leroy Date: Mon, 26 Jun 2023 10:27:40 -0500 Subject: [PATCH 2/4] Update docs/sphinx/source/whatsnew/v0.10.0.rst Co-authored-by: Cliff Hansen --- docs/sphinx/source/whatsnew/v0.10.0.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/source/whatsnew/v0.10.0.rst b/docs/sphinx/source/whatsnew/v0.10.0.rst index ee9388b29b..c1fa41874a 100644 --- a/docs/sphinx/source/whatsnew/v0.10.0.rst +++ b/docs/sphinx/source/whatsnew/v0.10.0.rst @@ -49,7 +49,8 @@ Enhancements Bug fixes ~~~~~~~~~ -* Prevent `v_oc` to be negative in `_lambertw` (:pull:`1782`) +* Prevent small negative values of `v_oc` in :py:func:`pvlib.singlediode._lambertw` + which result from accumulated roundoff error. (:issue:`1780`, :issue:`1673`, :pull:`1782`) Testing From 0c9b70733cd1409dba322da371e86c96f98b39a0 Mon Sep 17 00:00:00 2001 From: Cedric Leroy Date: Mon, 26 Jun 2023 11:42:48 -0400 Subject: [PATCH 3/4] Target small negative elements only --- pvlib/singlediode.py | 6 +++--- pvlib/tests/test_singlediode.py | 12 +++++------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/pvlib/singlediode.py b/pvlib/singlediode.py index e70bb02f36..cfc63f3591 100644 --- a/pvlib/singlediode.py +++ b/pvlib/singlediode.py @@ -794,11 +794,11 @@ def _lambertw(photocurrent, saturation_current, resistance_series, # Compute open circuit voltage v_oc = _lambertw_v_from_i(0., **params) - # Set elements <0 or close to 0 in v_oc to 0 + # Set small elements <0 in v_oc to 0 if isinstance(v_oc, np.ndarray): - v_oc[(v_oc < 0) | ((v_oc > 0) & (v_oc < 1e-12))] = 0. + v_oc[(v_oc < 0) & (v_oc > -1e-12)] = 0. elif isinstance(v_oc, (float, int)): - if v_oc < 0 or 0 < v_oc < 1e-12: + if v_oc < 0 and v_oc > -1e-12: v_oc = 0. # Find the voltage, v_mp, where the power is maximized. diff --git a/pvlib/tests/test_singlediode.py b/pvlib/tests/test_singlediode.py index aad7f19979..16b93cbf77 100644 --- a/pvlib/tests/test_singlediode.py +++ b/pvlib/tests/test_singlediode.py @@ -170,15 +170,13 @@ def test_singlediode_precision(method, precise_iv_curves): def test_singlediode_lambert_negative_voc(): - x1 = np.array([0., 1.480501e-11, 0.178, 8000., 1.797559]) - outs = pvsystem.singlediode(*x1, method='lambertw') - assert outs['v_oc'] == 0 - - x2 = np.array([0., 1.456894e-11, 0.178, 8000., 1.797048]) - outs = pvsystem.singlediode(*x2, method='lambertw') + # Those values result in a negative v_oc out of `_lambertw_v_from_i` + x = np.array([0., 1.480501e-11, 0.178, 8000., 1.797559]) + outs = pvsystem.singlediode(*x, method='lambertw') assert outs['v_oc'] == 0 - x = np.array([x1, x2]).T + # Testing for an array + x = np.array([x, x]).T outs = pvsystem.singlediode(*x, method='lambertw') assert np.array_equal(outs['v_oc'], [0, 0]) From 15f8210a14f54e1966aae01bfe83024704df0cad Mon Sep 17 00:00:00 2001 From: Kevin Anderson Date: Tue, 27 Jun 2023 10:59:33 -0400 Subject: [PATCH 4/4] Update docs/sphinx/source/whatsnew/v0.10.0.rst --- docs/sphinx/source/whatsnew/v0.10.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/sphinx/source/whatsnew/v0.10.0.rst b/docs/sphinx/source/whatsnew/v0.10.0.rst index c1fa41874a..c5e02ffbc3 100644 --- a/docs/sphinx/source/whatsnew/v0.10.0.rst +++ b/docs/sphinx/source/whatsnew/v0.10.0.rst @@ -75,3 +75,4 @@ Contributors * Echedey Luis (:ghuser:`echedey-ls`) * Cliff Hansen (:ghuser:`cwhanse`) * Cédric Leroy (:ghuser:`cedricleroy`) +* Jean-Baptiste Pasquier (:ghuser:`pasquierjb`)