diff --git a/docs/sphinx/source/whatsnew/v0.16.2.rst b/docs/sphinx/source/whatsnew/v0.16.2.rst index f704525a12..29d0c8acd6 100644 --- a/docs/sphinx/source/whatsnew/v0.16.2.rst +++ b/docs/sphinx/source/whatsnew/v0.16.2.rst @@ -14,6 +14,9 @@ Deprecations Bug fixes ~~~~~~~~~ +* :py:func:`~pvlib.pvsystem.singlediode` with ``method='lambertw'`` no longer + emits ``RuntimeWarning: invalid value encountered in divide`` when the open + circuit voltage is zero, e.g. with zero photocurrent. (:issue:`2671`) Enhancements @@ -42,4 +45,5 @@ Maintenance Contributors ~~~~~~~~~~~~ +* Jakub Krasuski (:ghuser:`Blizzeq`) diff --git a/pvlib/singlediode.py b/pvlib/singlediode.py index a511f9ebf4..b988f8a9ec 100644 --- a/pvlib/singlediode.py +++ b/pvlib/singlediode.py @@ -908,16 +908,20 @@ def _lambertw(photocurrent, saturation_current, resistance_series, # remove try/except when scipy>=1.15, and golden mean is retired try: from scipy.optimize.elementwise import find_minimum - # left negative to insure strict inequality - init = (-1., 0.8*v_oc, v_oc) + # left negative to insure strict inequality. Where v_oc is 0 the + # bracket would collapse to x2 == x3 (GH 2671), and 0 is the only + # voltage in [0, v_oc], so v_mp and p_mp are 0 there. Those elements + # get a placeholder bracket whose result is discarded. + voc_zero = v_oc == 0 + init = (-1., 0.8*v_oc, np.where(voc_zero, 1., v_oc)) res = find_minimum(_vmp_opt, init, args=(params['photocurrent'], params['saturation_current'], params['resistance_series'], params['resistance_shunt'], params['nNsVth'],)) - v_mp = res.x - p_mp = -1.*res.f_x + v_mp = np.where(voc_zero, 0., res.x)[()] + p_mp = np.where(voc_zero, 0., -1.*res.f_x)[()] except ModuleNotFoundError: # switch to old golden section method p_mp, v_mp = _golden_sect_DataFrame(params, 0., v_oc * 1.14, diff --git a/tests/test_singlediode.py b/tests/test_singlediode.py index 10b589b1b1..afd13b97f7 100644 --- a/tests/test_singlediode.py +++ b/tests/test_singlediode.py @@ -2,6 +2,8 @@ testing single-diode methods using JW Bishop 1988 """ +import warnings + import numpy as np import pandas as pd import scipy @@ -191,6 +193,26 @@ def test_singlediode_lambert_negative_voc(mocker): assert_array_equal(outs["v_oc"], [0, 0]) +def test_singlediode_lambert_zero_voc(): + """With v_oc = 0 the bracket passed to find_minimum collapsed to + x2 == x3 and scipy raised a RuntimeWarning. See issue #2671. + """ + pytest.importorskip("scipy", minversion="1.15") + with warnings.catch_warnings(): + warnings.simplefilter("error") + out = pvsystem.singlediode(0., 1e-12, 0.5, np.inf, 1.8, + method="lambertw") + outs = pvsystem.singlediode(np.array([0., 5.]), 1e-12, 0.5, np.inf, + 1.8, method="lambertw") + assert out["v_oc"] == 0 + assert out["v_mp"] == 0 + assert out["p_mp"] == 0 + assert outs["v_mp"][0] == 0 + assert outs["p_mp"][0] == 0 + assert 0 < outs["v_mp"][1] < outs["v_oc"][1] + assert outs["p_mp"][1] > 0 + + @pytest.mark.parametrize('method', ['lambertw', 'brentq', 'newton', chandrupatla]) def test_v_from_i_i_from_v_precision(method, precise_iv_curves):