diff --git a/docs/sphinx/source/whatsnew/v0.10.0.rst b/docs/sphinx/source/whatsnew/v0.10.0.rst new file mode 100644 index 0000000000..a4406072f8 --- /dev/null +++ b/docs/sphinx/source/whatsnew/v0.10.0.rst @@ -0,0 +1,47 @@ +.. _whatsnew_01000: + + +v0.10.0 +------- + + +Breaking changes +~~~~~~~~~~~~~~~~ +* Reorder arguments of :py:func:`pvlib.pvsystem.PVSystem.i_from_v`, + :py:func:`pvlib.pvsystem.i_from_v`, :py:func:`pvlib.pvsystem.v_from_i`, + :py:func:`pvlib.singlediode._lambertw_i_from_v`, and + :py:func:`pvlib.singlediode._lambertw_v_from_i` to match + :py:func:`pvlib.pvsystem.singlediode`. + (:issue:`1718`, :pull:`1719`) + + +Deprecations +~~~~~~~~~~~~ + + +Enhancements +~~~~~~~~~~~~ + + +Bug fixes +~~~~~~~~~ + + +Testing +~~~~~~~ + + +Documentation +~~~~~~~~~~~~~ + +Benchmarking +~~~~~~~~~~~~~ + + +Requirements +~~~~~~~~~~~~ + + +Contributors +~~~~~~~~~~~~ +* Taos Transue (:ghuser:`reepoi`) diff --git a/pvlib/ivtools/sdm.py b/pvlib/ivtools/sdm.py index 34f5c66c0b..082c0552c6 100644 --- a/pvlib/ivtools/sdm.py +++ b/pvlib/ivtools/sdm.py @@ -942,7 +942,7 @@ def _update_io(voc, iph, io, rs, rsh, nnsvth): while maxerr > eps and k < niter: # Predict Voc - pvoc = v_from_i(rsh, rs, nnsvth, 0., tio, iph) + pvoc = v_from_i(0., iph, tio, rs, rsh, nnsvth) # Difference in Voc dvoc = pvoc - voc diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 0cf034e9b0..d136089b10 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -944,14 +944,17 @@ def singlediode(self, photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth, ivcurve_pnts=ivcurve_pnts) - def i_from_v(self, resistance_shunt, resistance_series, nNsVth, voltage, - saturation_current, photocurrent): + def i_from_v(self, voltage, photocurrent, saturation_current, + resistance_series, resistance_shunt, nNsVth): """Wrapper around the :py:func:`pvlib.pvsystem.i_from_v` function. - See :py:func:`pvsystem.i_from_v` for details + See :py:func:`pvlib.pvsystem.i_from_v` for details. + + .. versionchanged:: 0.10.0 + The function's arguments have been reordered. """ - return i_from_v(resistance_shunt, resistance_series, nNsVth, voltage, - saturation_current, photocurrent) + return i_from_v(voltage, photocurrent, saturation_current, + resistance_series, resistance_shunt, nNsVth) def get_ac(self, model, p_dc, v_dc=None): r"""Calculates AC power from p_dc using the inverter model indicated @@ -2962,8 +2965,8 @@ def max_power_point(photocurrent, saturation_current, resistance_series, return out -def v_from_i(resistance_shunt, resistance_series, nNsVth, current, - saturation_current, photocurrent, method='lambertw'): +def v_from_i(current, photocurrent, saturation_current, resistance_series, + resistance_shunt, nNsVth, method='lambertw'): ''' Device voltage at the given device current for the single diode model. @@ -2977,18 +2980,34 @@ def v_from_i(resistance_shunt, resistance_series, nNsVth, current, the caller's responsibility to ensure that the arguments are all float64 and within the proper ranges. + .. versionchanged:: 0.10.0 + The function's arguments have been reordered. + Parameters ---------- - resistance_shunt : numeric - Shunt resistance in ohms under desired IV curve conditions. - Often abbreviated ``Rsh``. - 0 < resistance_shunt <= numpy.inf + current : numeric + The current in amperes under desired IV curve conditions. + + photocurrent : numeric + Light-generated current (photocurrent) in amperes under desired + IV curve conditions. Often abbreviated ``I_L``. + 0 <= photocurrent + + saturation_current : numeric + Diode saturation current in amperes under desired IV curve + conditions. Often abbreviated ``I_0``. + 0 < saturation_current resistance_series : numeric Series resistance in ohms under desired IV curve conditions. Often abbreviated ``Rs``. 0 <= resistance_series < numpy.inf + resistance_shunt : numeric + Shunt resistance in ohms under desired IV curve conditions. + Often abbreviated ``Rsh``. + 0 < resistance_shunt <= numpy.inf + nNsVth : numeric The product of three components. 1) The usual diode ideal factor (n), 2) the number of cells in series (Ns), and 3) the cell @@ -2999,19 +3018,6 @@ def v_from_i(resistance_shunt, resistance_series, nNsVth, current, q is the charge of an electron (coulombs). 0 < nNsVth - current : numeric - The current in amperes under desired IV curve conditions. - - saturation_current : numeric - Diode saturation current in amperes under desired IV curve - conditions. Often abbreviated ``I_0``. - 0 < saturation_current - - photocurrent : numeric - Light-generated current (photocurrent) in amperes under desired - IV curve conditions. Often abbreviated ``I_L``. - 0 <= photocurrent - method : str Method to use: ``'lambertw'``, ``'newton'``, or ``'brentq'``. *Note*: ``'brentq'`` is limited to 1st quadrant only. @@ -3028,8 +3034,8 @@ def v_from_i(resistance_shunt, resistance_series, nNsVth, current, ''' if method.lower() == 'lambertw': return _singlediode._lambertw_v_from_i( - resistance_shunt, resistance_series, nNsVth, current, - saturation_current, photocurrent + current, photocurrent, saturation_current, resistance_series, + resistance_shunt, nNsVth ) else: # Calculate points on the IV curve using either 'newton' or 'brentq' @@ -3050,33 +3056,49 @@ def v_from_i(resistance_shunt, resistance_series, nNsVth, current, return V -def i_from_v(resistance_shunt, resistance_series, nNsVth, voltage, - saturation_current, photocurrent, method='lambertw'): +def i_from_v(voltage, photocurrent, saturation_current, resistance_series, + resistance_shunt, nNsVth, method='lambertw'): ''' Device current at the given device voltage for the single diode model. Uses the single diode model (SDM) as described in, e.g., - Jain and Kapoor 2004 [1]_. + Jain and Kapoor 2004 [1]_. The solution is per Eq 2 of [1] except when resistance_series=0, - in which case the explict solution for current is used. + in which case the explict solution for current is used. Ideal device parameters are specified by resistance_shunt=np.inf and - resistance_series=0. + resistance_series=0. Inputs to this function can include scalars and pandas.Series, but it is - the caller's responsibility to ensure that the arguments are all float64 - and within the proper ranges. + the caller's responsibility to ensure that the arguments are all float64 + and within the proper ranges. + + .. versionchanged:: 0.10.0 + The function's arguments have been reordered. Parameters ---------- - resistance_shunt : numeric - Shunt resistance in ohms under desired IV curve conditions. - Often abbreviated ``Rsh``. - 0 < resistance_shunt <= numpy.inf + voltage : numeric + The voltage in Volts under desired IV curve conditions. + + photocurrent : numeric + Light-generated current (photocurrent) in amperes under desired + IV curve conditions. Often abbreviated ``I_L``. + 0 <= photocurrent + + saturation_current : numeric + Diode saturation current in amperes under desired IV curve + conditions. Often abbreviated ``I_0``. + 0 < saturation_current resistance_series : numeric Series resistance in ohms under desired IV curve conditions. Often abbreviated ``Rs``. 0 <= resistance_series < numpy.inf + resistance_shunt : numeric + Shunt resistance in ohms under desired IV curve conditions. + Often abbreviated ``Rsh``. + 0 < resistance_shunt <= numpy.inf + nNsVth : numeric The product of three components. 1) The usual diode ideal factor (n), 2) the number of cells in series (Ns), and 3) the cell @@ -3087,19 +3109,6 @@ def i_from_v(resistance_shunt, resistance_series, nNsVth, voltage, q is the charge of an electron (coulombs). 0 < nNsVth - voltage : numeric - The voltage in Volts under desired IV curve conditions. - - saturation_current : numeric - Diode saturation current in amperes under desired IV curve - conditions. Often abbreviated ``I_0``. - 0 < saturation_current - - photocurrent : numeric - Light-generated current (photocurrent) in amperes under desired - IV curve conditions. Often abbreviated ``I_L``. - 0 <= photocurrent - method : str Method to use: ``'lambertw'``, ``'newton'``, or ``'brentq'``. *Note*: ``'brentq'`` is limited to 1st quadrant only. @@ -3116,8 +3125,8 @@ def i_from_v(resistance_shunt, resistance_series, nNsVth, voltage, ''' if method.lower() == 'lambertw': return _singlediode._lambertw_i_from_v( - resistance_shunt, resistance_series, nNsVth, voltage, - saturation_current, photocurrent + voltage, photocurrent, saturation_current, resistance_series, + resistance_shunt, nNsVth ) else: # Calculate points on the IV curve using either 'newton' or 'brentq' diff --git a/pvlib/singlediode.py b/pvlib/singlediode.py index 9f5fd336ef..81d6ce3761 100644 --- a/pvlib/singlediode.py +++ b/pvlib/singlediode.py @@ -495,12 +495,12 @@ def _prepare_newton_inputs(i_or_v_tup, args, v0): return args, v0 -def _lambertw_v_from_i(resistance_shunt, resistance_series, nNsVth, current, - saturation_current, photocurrent): +def _lambertw_v_from_i(current, photocurrent, saturation_current, + resistance_series, resistance_shunt, nNsVth): # Record if inputs were all scalar output_is_scalar = all(map(np.isscalar, - [resistance_shunt, resistance_series, nNsVth, - current, saturation_current, photocurrent])) + (current, photocurrent, saturation_current, + resistance_series, resistance_shunt, nNsVth))) # This transforms Gsh=1/Rsh, including ideal Rsh=np.inf into Gsh=0., which # is generally more numerically stable @@ -509,9 +509,9 @@ def _lambertw_v_from_i(resistance_shunt, resistance_series, nNsVth, current, # Ensure that we are working with read-only views of numpy arrays # Turns Series into arrays so that we don't have to worry about # multidimensional broadcasting failing - Gsh, Rs, a, I, I0, IL = \ - np.broadcast_arrays(conductance_shunt, resistance_series, nNsVth, - current, saturation_current, photocurrent) + I, IL, I0, Rs, Gsh, a = \ + np.broadcast_arrays(current, photocurrent, saturation_current, + resistance_series, conductance_shunt, nNsVth) # Intitalize output V (I might not be float64) V = np.full_like(I, np.nan, dtype=np.float64) @@ -572,12 +572,12 @@ def _lambertw_v_from_i(resistance_shunt, resistance_series, nNsVth, current, return V -def _lambertw_i_from_v(resistance_shunt, resistance_series, nNsVth, voltage, - saturation_current, photocurrent): +def _lambertw_i_from_v(voltage, photocurrent, saturation_current, + resistance_series, resistance_shunt, nNsVth): # Record if inputs were all scalar output_is_scalar = all(map(np.isscalar, - [resistance_shunt, resistance_series, nNsVth, - voltage, saturation_current, photocurrent])) + (voltage, photocurrent, saturation_current, + resistance_series, resistance_shunt, nNsVth))) # This transforms Gsh=1/Rsh, including ideal Rsh=np.inf into Gsh=0., which # is generally more numerically stable @@ -586,9 +586,9 @@ def _lambertw_i_from_v(resistance_shunt, resistance_series, nNsVth, voltage, # Ensure that we are working with read-only views of numpy arrays # Turns Series into arrays so that we don't have to worry about # multidimensional broadcasting failing - Gsh, Rs, a, V, I0, IL = \ - np.broadcast_arrays(conductance_shunt, resistance_series, nNsVth, - voltage, saturation_current, photocurrent) + V, IL, I0, Rs, Gsh, a = \ + np.broadcast_arrays(voltage, photocurrent, saturation_current, + resistance_series, conductance_shunt, nNsVth) # Intitalize output I (V might not be float64) I = np.full_like(V, np.nan, dtype=np.float64) # noqa: E741, N806 @@ -632,36 +632,29 @@ def _lambertw_i_from_v(resistance_shunt, resistance_series, nNsVth, voltage, def _lambertw(photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth, ivcurve_pnts=None): + # collect args + params = {'photocurrent': photocurrent, + 'saturation_current': saturation_current, + 'resistance_series': resistance_series, + 'resistance_shunt': resistance_shunt, 'nNsVth': nNsVth} + # Compute short circuit current - i_sc = _lambertw_i_from_v(resistance_shunt, resistance_series, nNsVth, 0., - saturation_current, photocurrent) + i_sc = _lambertw_i_from_v(0., **params) # Compute open circuit voltage - v_oc = _lambertw_v_from_i(resistance_shunt, resistance_series, nNsVth, 0., - saturation_current, photocurrent) - - params = {'r_sh': resistance_shunt, - 'r_s': resistance_series, - 'nNsVth': nNsVth, - 'i_0': saturation_current, - 'i_l': photocurrent} + v_oc = _lambertw_v_from_i(0., **params) # 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) + p_mp, v_mp = _golden_sect_DataFrame(params, 0., v_oc * 1.14, _pwr_optfcn) # Find Imp using Lambert W - i_mp = _lambertw_i_from_v(resistance_shunt, resistance_series, nNsVth, - v_mp, saturation_current, photocurrent) + i_mp = _lambertw_i_from_v(v_mp, **params) # Find Ix and Ixx using Lambert W - i_x = _lambertw_i_from_v(resistance_shunt, resistance_series, nNsVth, - 0.5 * v_oc, saturation_current, photocurrent) + i_x = _lambertw_i_from_v(0.5 * v_oc, **params) - i_xx = _lambertw_i_from_v(resistance_shunt, resistance_series, nNsVth, - 0.5 * (v_oc + v_mp), saturation_current, - photocurrent) + i_xx = _lambertw_i_from_v(0.5 * (v_oc + v_mp), **params) out = (i_sc, v_oc, i_mp, v_mp, p_mp, i_x, i_xx) @@ -670,9 +663,7 @@ def _lambertw(photocurrent, saturation_current, resistance_series, ivcurve_v = (np.asarray(v_oc)[..., np.newaxis] * np.linspace(0, 1, ivcurve_pnts)) - ivcurve_i = _lambertw_i_from_v(resistance_shunt, resistance_series, - nNsVth, ivcurve_v.T, saturation_current, - photocurrent).T + ivcurve_i = _lambertw_i_from_v(ivcurve_v.T, **params).T out += (ivcurve_i, ivcurve_v) @@ -684,7 +675,9 @@ def _pwr_optfcn(df, loc): Function to find power from ``i_from_v``. ''' - I = _lambertw_i_from_v(df['r_sh'], df['r_s'], # noqa: E741, N806 - df['nNsVth'], df[loc], df['i_0'], df['i_l']) + current = _lambertw_i_from_v(df[loc], df['photocurrent'], + df['saturation_current'], + df['resistance_series'], + df['resistance_shunt'], df['nNsVth']) - return I * df[loc] + return current * df[loc] diff --git a/pvlib/tests/test_pvsystem.py b/pvlib/tests/test_pvsystem.py index 7fa013d0dc..2966aa55d6 100644 --- a/pvlib/tests/test_pvsystem.py +++ b/pvlib/tests/test_pvsystem.py @@ -1079,11 +1079,12 @@ def test_v_from_i(fixture_v_from_i, method, atol): IL = fixture_v_from_i['IL'] V_expected = fixture_v_from_i['V_expected'] - V = pvsystem.v_from_i(Rsh, Rs, nNsVth, I, I0, IL, method=method) - assert(isinstance(V, type(V_expected))) - if isinstance(V, type(np.ndarray)): - assert(isinstance(V.dtype, type(V_expected.dtype))) - assert(V.shape == V_expected.shape) + V = pvsystem.v_from_i(I, IL, I0, Rs, Rsh, nNsVth, method=method) + + assert isinstance(V, type(V_expected)) + if isinstance(V, np.ndarray): + assert isinstance(V.dtype, type(V_expected.dtype)) + assert V.shape == V_expected.shape assert_allclose(V, V_expected, atol=atol) @@ -1092,7 +1093,7 @@ def test_i_from_v_from_i(fixture_v_from_i): Rsh = fixture_v_from_i['Rsh'] Rs = fixture_v_from_i['Rs'] nNsVth = fixture_v_from_i['nNsVth'] - I = fixture_v_from_i['I'] + current = fixture_v_from_i['I'] I0 = fixture_v_from_i['I0'] IL = fixture_v_from_i['IL'] V = fixture_v_from_i['V_expected'] @@ -1100,15 +1101,17 @@ def test_i_from_v_from_i(fixture_v_from_i): # Convergence criteria atol = 1.e-11 - I_expected = pvsystem.i_from_v(Rsh, Rs, nNsVth, V, I0, IL, + I_expected = pvsystem.i_from_v(V, IL, I0, Rs, Rsh, nNsVth, method='lambertw') - assert_allclose(I, I_expected, atol=atol) - I = pvsystem.i_from_v(Rsh, Rs, nNsVth, V, I0, IL) - assert(isinstance(I, type(I_expected))) - if isinstance(I, type(np.ndarray)): - assert(isinstance(I.dtype, type(I_expected.dtype))) - assert(I.shape == I_expected.shape) - assert_allclose(I, I_expected, atol=atol) + assert_allclose(current, I_expected, atol=atol) + + current = pvsystem.i_from_v(V, IL, I0, Rs, Rsh, nNsVth) + + assert isinstance(current, type(I_expected)) + if isinstance(current, np.ndarray): + assert isinstance(current.dtype, type(I_expected.dtype)) + assert current.shape == I_expected.shape + assert_allclose(current, I_expected, atol=atol) @pytest.fixture(params=[ @@ -1197,41 +1200,42 @@ def test_i_from_v(fixture_i_from_v, method, atol): IL = fixture_i_from_v['IL'] I_expected = fixture_i_from_v['I_expected'] - I = pvsystem.i_from_v(Rsh, Rs, nNsVth, V, I0, IL, method=method) - assert(isinstance(I, type(I_expected))) - if isinstance(I, type(np.ndarray)): - assert(isinstance(I.dtype, type(I_expected.dtype))) - assert(I.shape == I_expected.shape) - assert_allclose(I, I_expected, atol=atol) + current = pvsystem.i_from_v(V, IL, I0, Rs, Rsh, nNsVth, method=method) + + assert isinstance(current, type(I_expected)) + if isinstance(current, np.ndarray): + assert isinstance(current.dtype, type(I_expected.dtype)) + assert current.shape == I_expected.shape + assert_allclose(current, I_expected, atol=atol) def test_PVSystem_i_from_v(mocker): system = pvsystem.PVSystem() m = mocker.patch('pvlib.pvsystem.i_from_v', autospec=True) - args = (20, 0.1, 0.5, 7.5049875193450521, 6e-7, 7) + args = (7.5049875193450521, 7, 6e-7, 0.1, 20, 0.5) system.i_from_v(*args) m.assert_called_once_with(*args) def test_i_from_v_size(): with pytest.raises(ValueError): - pvsystem.i_from_v(20, [0.1] * 2, 0.5, [7.5] * 3, 6.0e-7, 7.0) + pvsystem.i_from_v([7.5] * 3, 7., 6e-7, [0.1] * 2, 20, 0.5) with pytest.raises(ValueError): - pvsystem.i_from_v(20, [0.1] * 2, 0.5, [7.5] * 3, 6.0e-7, 7.0, + pvsystem.i_from_v([7.5] * 3, 7., 6e-7, [0.1] * 2, 20, 0.5, method='brentq') with pytest.raises(ValueError): - pvsystem.i_from_v(20, 0.1, 0.5, [7.5] * 3, 6.0e-7, np.array([7., 7.]), + pvsystem.i_from_v([7.5] * 3, np.array([7., 7.]), 6e-7, 0.1, 20, 0.5, method='newton') def test_v_from_i_size(): with pytest.raises(ValueError): - pvsystem.v_from_i(20, [0.1] * 2, 0.5, [3.0] * 3, 6.0e-7, 7.0) + pvsystem.v_from_i([3.] * 3, 7., 6e-7, [0.1] * 2, 20, 0.5) with pytest.raises(ValueError): - pvsystem.v_from_i(20, [0.1] * 2, 0.5, [3.0] * 3, 6.0e-7, 7.0, + pvsystem.v_from_i([3.] * 3, 7., 6e-7, [0.1] * 2, 20, 0.5, method='brentq') with pytest.raises(ValueError): - pvsystem.v_from_i(20, [0.1], 0.5, [3.0] * 3, 6.0e-7, np.array([7., 7.]), + pvsystem.v_from_i([3.] * 3, np.array([7., 7.]), 6e-7, [0.1], 20, 0.5, method='newton') @@ -1328,8 +1332,8 @@ def test_singlediode_array(): sd = pvsystem.singlediode(photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth) - expected = pvsystem.i_from_v(resistance_shunt, resistance_series, nNsVth, - sd['v_mp'], saturation_current, photocurrent, + expected = pvsystem.i_from_v(sd['v_mp'], photocurrent, saturation_current, + resistance_series, resistance_shunt, nNsVth, method='lambertw') assert_allclose(sd['i_mp'], expected, atol=1e-8) @@ -1404,20 +1408,19 @@ def test_singlediode_series_ivcurve(cec_module_params): [3.0107985972, 2.8841320056, 0.], [6.0072629615, 5.7462022810, 0.]]))]) - for k, v in out.items(): assert_allclose(v, expected[k], atol=1e-2) out = pvsystem.singlediode(IL, I0, Rs, Rsh, nNsVth, ivcurve_pnts=3) - expected['i_mp'] = pvsystem.i_from_v(Rsh, Rs, nNsVth, out['v_mp'], I0, IL, + expected['i_mp'] = pvsystem.i_from_v(out['v_mp'], IL, I0, Rs, Rsh, nNsVth, method='lambertw') - expected['v_mp'] = pvsystem.v_from_i(Rsh, Rs, nNsVth, out['i_mp'], I0, IL, + expected['v_mp'] = pvsystem.v_from_i(out['i_mp'], IL, I0, Rs, Rsh, nNsVth, method='lambertw') - expected['i'] = pvsystem.i_from_v(Rsh, Rs, nNsVth, out['v'].T, I0, IL, - method='lambertw').T - expected['v'] = pvsystem.v_from_i(Rsh, Rs, nNsVth, out['i'].T, I0, IL, - method='lambertw').T + expected['i'] = pvsystem.i_from_v(out['v'].T, IL, I0, Rs, Rsh, nNsVth, + method='lambertw').T + expected['v'] = pvsystem.v_from_i(out['i'].T, IL, I0, Rs, Rsh, nNsVth, + method='lambertw').T for k, v in out.items(): assert_allclose(v, expected[k], atol=1e-6) diff --git a/pvlib/tests/test_singlediode.py b/pvlib/tests/test_singlediode.py index 447ed4e6ad..4b2a9e9e66 100644 --- a/pvlib/tests/test_singlediode.py +++ b/pvlib/tests/test_singlediode.py @@ -25,22 +25,16 @@ def test_method_spr_e20_327(method, cec_module_spr_e20_327): I_L_ref=spr_e20_327['I_L_ref'], I_o_ref=spr_e20_327['I_o_ref'], R_sh_ref=spr_e20_327['R_sh_ref'], R_s=spr_e20_327['R_s'], EgRef=1.121, dEgdT=-0.0002677) - il, io, rs, rsh, nnsvt = x pvs = pvsystem.singlediode(*x, method='lambertw') out = pvsystem.singlediode(*x, method=method) - isc, voc, imp, vmp, pmp, ix, ixx = out.values() - assert np.isclose(pvs['i_sc'], isc) - assert np.isclose(pvs['v_oc'], voc) - # the singlediode method doesn't actually get the MPP correct - pvs_imp = pvsystem.i_from_v(rsh, rs, nnsvt, vmp, io, il, method='lambertw') - pvs_vmp = pvsystem.v_from_i(rsh, rs, nnsvt, imp, io, il, method='lambertw') - assert np.isclose(pvs_imp, imp) - assert np.isclose(pvs_vmp, vmp) - assert np.isclose(pvs['p_mp'], pmp) - assert np.isclose(pvs['i_x'], ix) - pvs_ixx = pvsystem.i_from_v(rsh, rs, nnsvt, (voc + vmp)/2, io, il, - method='lambertw') - assert np.isclose(pvs_ixx, ixx) + + assert np.isclose(pvs['i_sc'], out['i_sc']) + assert np.isclose(pvs['v_oc'], out['v_oc']) + assert np.isclose(pvs['i_mp'], out['i_mp']) + assert np.isclose(pvs['v_mp'], out['v_mp']) + assert np.isclose(pvs['p_mp'], out['p_mp']) + assert np.isclose(pvs['i_x'], out['i_x']) + assert np.isclose(pvs['i_xx'], out['i_xx']) @pytest.mark.parametrize('method', ['brentq', 'newton']) @@ -53,23 +47,16 @@ def test_newton_fs_495(method, cec_module_fs_495): I_L_ref=fs_495['I_L_ref'], I_o_ref=fs_495['I_o_ref'], R_sh_ref=fs_495['R_sh_ref'], R_s=fs_495['R_s'], EgRef=1.475, dEgdT=-0.0003) - il, io, rs, rsh, nnsvt = x - x += (101, ) pvs = pvsystem.singlediode(*x, method='lambertw') out = pvsystem.singlediode(*x, method=method) - isc, voc, imp, vmp, pmp, ix, ixx, i, v = out.values() - assert np.isclose(pvs['i_sc'], isc) - assert np.isclose(pvs['v_oc'], voc) - # the singlediode method doesn't actually get the MPP correct - pvs_imp = pvsystem.i_from_v(rsh, rs, nnsvt, vmp, io, il, method='lambertw') - pvs_vmp = pvsystem.v_from_i(rsh, rs, nnsvt, imp, io, il, method='lambertw') - assert np.isclose(pvs_imp, imp) - assert np.isclose(pvs_vmp, vmp) - assert np.isclose(pvs['p_mp'], pmp) - assert np.isclose(pvs['i_x'], ix) - pvs_ixx = pvsystem.i_from_v(rsh, rs, nnsvt, (voc + vmp)/2, io, il, - method='lambertw') - assert np.isclose(pvs_ixx, ixx) + + assert np.isclose(pvs['i_sc'], out['i_sc']) + assert np.isclose(pvs['v_oc'], out['v_oc']) + assert np.isclose(pvs['i_mp'], out['i_mp']) + assert np.isclose(pvs['v_mp'], out['v_mp']) + assert np.isclose(pvs['p_mp'], out['p_mp']) + assert np.isclose(pvs['i_x'], out['i_x']) + assert np.isclose(pvs['i_xx'], out['i_xx']) def build_precise_iv_curve_dataframe(file_csv, file_json): diff --git a/pvlib/tools.py b/pvlib/tools.py index f1a569bb1f..f6974cf3d3 100644 --- a/pvlib/tools.py +++ b/pvlib/tools.py @@ -346,7 +346,7 @@ def _golden_sect_DataFrame(params, lower, upper, func, atol=1e-8): phim1 = (np.sqrt(5) - 1) / 2 - df = params + df = params.copy() # shallow copy to avoid modifying caller's dict df['VH'] = upper df['VL'] = lower