diff --git a/docs/sphinx/source/whatsnew/v0.10.0.rst b/docs/sphinx/source/whatsnew/v0.10.0.rst index ab58828653..c5e02ffbc3 100644 --- a/docs/sphinx/source/whatsnew/v0.10.0.rst +++ b/docs/sphinx/source/whatsnew/v0.10.0.rst @@ -49,6 +49,9 @@ Enhancements Bug fixes ~~~~~~~~~ +* 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 ~~~~~~~ @@ -71,3 +74,5 @@ Contributors * Adam R. Jensen (:ghuser:`AdamRJensen`) * Echedey Luis (:ghuser:`echedey-ls`) * Cliff Hansen (:ghuser:`cwhanse`) +* Cédric Leroy (:ghuser:`cedricleroy`) +* Jean-Baptiste Pasquier (:ghuser:`pasquierjb`) diff --git a/pvlib/inverter.py b/pvlib/inverter.py index 03fb394772..b5280e3616 100644 --- a/pvlib/inverter.py +++ b/pvlib/inverter.py @@ -335,7 +335,7 @@ def pvwatts(pdc, pdc0, eta_inv_nom=0.96, eta_inv_ref=0.9637): NREL's PVWatts inverter model. The PVWatts inverter model [1]_ calculates inverter efficiency :math:`\eta` - as a function of input DC power + as a function of input DC power :math:`P_{dc}` .. math:: @@ -369,6 +369,10 @@ def pvwatts(pdc, pdc0, eta_inv_nom=0.96, eta_inv_ref=0.9637): Notes ----- + When sourcing ``pdc`` from pvlib functions + (e.g. :py:func:`pvlib.pvsystem.pvwatts_dc`) their DC power output is in W, + and ``pdc0`` should have the same unit (W). + Note that ``pdc0`` is also used as a symbol in :py:func:`pvlib.pvsystem.pvwatts_dc`. ``pdc0`` in this function refers to the DC power input limit of the inverter. ``pdc0`` in @@ -393,6 +397,7 @@ def pvwatts(pdc, pdc0, eta_inv_nom=0.96, eta_inv_ref=0.9637): pdc_neq_0 = ~np.equal(pdc, 0) # eta < 0 if zeta < 0.006. power_ac is forced to be >= 0 below. GH 541 + # In some published versions of [1] the parentheses are missing eta = eta_inv_nom / eta_inv_ref * ( -0.0162 * zeta - np.divide(0.0059, zeta, out=eta, where=pdc_neq_0) + 0.9858) # noQA: W503 diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index ae3bc4d001..1cf4d8138c 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -2809,9 +2809,9 @@ def pvwatts_dc(g_poa_effective, temp_cell, pdc0, gamma_pdc, temp_ref=25.): P_{dc} = \frac{G_{poa eff}}{1000} P_{dc0} ( 1 + \gamma_{pdc} (T_{cell} - T_{ref})) - Note that the pdc0 is also used as a symbol in - :py:func:`pvlib.inverter.pvwatts`. pdc0 in this function refers to the DC - power of the modules at reference conditions. pdc0 in + Note that ``pdc0`` is also used as a symbol in + :py:func:`pvlib.inverter.pvwatts`. ``pdc0`` in this function refers to the DC + power of the modules at reference conditions. ``pdc0`` in :py:func:`pvlib.inverter.pvwatts` refers to the DC power input limit of the inverter. @@ -2836,7 +2836,7 @@ def pvwatts_dc(g_poa_effective, temp_cell, pdc0, gamma_pdc, temp_ref=25.): Returns ------- pdc : numeric - DC power. + DC power. [W] References ---------- diff --git a/pvlib/singlediode.py b/pvlib/singlediode.py index 0e652125f0..76f367e3bc 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 small elements <0 in v_oc to 0 + if isinstance(v_oc, np.ndarray): + v_oc[(v_oc < 0) & (v_oc > -1e-12)] = 0. + elif isinstance(v_oc, (float, int)): + if v_oc < 0 and 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 c9d4f64c9d..3d7d85b195 100644 --- a/pvlib/tests/test_singlediode.py +++ b/pvlib/tests/test_singlediode.py @@ -168,6 +168,19 @@ 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(): + + # 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 + + # 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]) + + @pytest.mark.parametrize('method', ['lambertw']) def test_ivcurve_pnts_precision(method, precise_iv_curves): """