Skip to content

Commit bc0f0ff

Browse files
reepoicwhansekandersolar
authored
Deprecate pvsystem.singlediode parameter ivcurve_pnts (#1743)
* deprecating pvsystem.singlediode ivcurve_pnts parameter * lowercase v in i_xx description * update whatsnew * Update docs/examples/iv-modeling/plot_singlediode.py Co-authored-by: Cliff Hansen <[email protected]> * add singlediode ivcurve_pnts deprecation warning test * add pytest.warns where pvsystem.singlediode ivcurve_pnts is used in tests * update sde, sdm tests to use pvsystem.i_from_v * Update pvlib/tests/test_pvsystem.py Co-authored-by: Kevin Anderson <[email protected]> --------- Co-authored-by: Cliff Hansen <[email protected]> Co-authored-by: Kevin Anderson <[email protected]>
1 parent 6606af7 commit bc0f0ff

File tree

8 files changed

+122
-89
lines changed

8 files changed

+122
-89
lines changed

docs/examples/iv-modeling/plot_singlediode.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@
2929
# -----------------------
3030
# This example uses :py:meth:`pvlib.pvsystem.calcparams_desoto` to calculate
3131
# the 5 electrical parameters needed to solve the single-diode equation.
32-
# :py:meth:`pvlib.pvsystem.singlediode` is then used to generate the IV curves.
32+
# :py:meth:`pvlib.pvsystem.singlediode` and :py:meth:`pvlib.pvsystem.i_from_v`
33+
# are used to generate the IV curves.
3334

3435
from pvlib import pvsystem
36+
import numpy as np
3537
import pandas as pd
3638
import matplotlib.pyplot as plt
3739

@@ -88,26 +90,27 @@
8890
)
8991

9092
# plug the parameters into the SDE and solve for IV curves:
91-
curve_info = pvsystem.singlediode(
92-
photocurrent=IL,
93-
saturation_current=I0,
94-
resistance_series=Rs,
95-
resistance_shunt=Rsh,
96-
nNsVth=nNsVth,
97-
ivcurve_pnts=100,
98-
method='lambertw'
99-
)
93+
SDE_params = {
94+
'photocurrent': IL,
95+
'saturation_current': I0,
96+
'resistance_series': Rs,
97+
'resistance_shunt': Rsh,
98+
'nNsVth': nNsVth
99+
}
100+
curve_info = pvsystem.singlediode(method='lambertw', **SDE_params)
101+
v = pd.DataFrame(np.linspace(0., curve_info['v_oc'], 100))
102+
i = pd.DataFrame(pvsystem.i_from_v(voltage=v, method='lambertw', **SDE_params))
100103

101104
# plot the calculated curves:
102105
plt.figure()
103-
for i, case in conditions.iterrows():
106+
for idx, case in conditions.iterrows():
104107
label = (
105108
"$G_{eff}$ " + f"{case['Geff']} $W/m^2$\n"
106109
"$T_{cell}$ " + f"{case['Tcell']} $\\degree C$"
107110
)
108-
plt.plot(curve_info['v'][i], curve_info['i'][i], label=label)
109-
v_mp = curve_info['v_mp'][i]
110-
i_mp = curve_info['i_mp'][i]
111+
plt.plot(v[idx], i[idx], label=label)
112+
v_mp = curve_info['v_mp'][idx]
113+
i_mp = curve_info['i_mp'][idx]
111114
# mark the MPP
112115
plt.plot([v_mp], [i_mp], ls='', marker='o', c='k')
113116

docs/sphinx/source/whatsnew/v0.10.0.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ Breaking changes
2222

2323
Deprecations
2424
~~~~~~~~~~~~
25+
* The ``ivcurve_pnts`` parameter of :py:func:`pvlib.pvsystem.singlediode` is
26+
deprecated. Use :py:func:`pvlib.pvsystem.v_from_i` and
27+
:py:func:`pvlib.pvsystem.i_from_v` instead. (:issue:`1626`, :pull:`1743`)
2528

2629

2730
Enhancements

pvlib/pvsystem.py

Lines changed: 50 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from abc import ABC, abstractmethod
1717
from typing import Optional
1818

19-
from pvlib._deprecation import deprecated
19+
from pvlib._deprecation import deprecated, warn_deprecated
2020

2121
from pvlib import (atmosphere, iam, inverter, irradiance,
2222
singlediode as _singlediode, spectrum, temperature)
@@ -2308,7 +2308,7 @@ def singlediode(photocurrent, saturation_current, resistance_series,
23082308
resistance_shunt, nNsVth, ivcurve_pnts=None,
23092309
method='lambertw'):
23102310
r"""
2311-
Solve the single-diode equation to obtain a photovoltaic IV curve.
2311+
Solve the single diode equation to obtain a photovoltaic IV curve.
23122312
23132313
Solves the single diode equation [1]_
23142314
@@ -2321,11 +2321,10 @@ def singlediode(photocurrent, saturation_current, resistance_series,
23212321
\frac{V + I R_s}{R_{sh}}
23222322
23232323
for :math:`I` and :math:`V` when given :math:`I_L, I_0, R_s, R_{sh},` and
2324-
:math:`n N_s V_{th}` which are described later. Returns a DataFrame
2325-
which contains the 5 points on the I-V curve specified in
2326-
[3]_. If all :math:`I_L, I_0, R_s, R_{sh},` and
2327-
:math:`n N_s V_{th}` are scalar, a single curve is returned, if any
2328-
are Series (of the same length), multiple IV curves are calculated.
2324+
:math:`n N_s V_{th}` which are described later. The five points on the I-V
2325+
curve specified in [3]_ are returned. If :math:`I_L, I_0, R_s, R_{sh},` and
2326+
:math:`n N_s V_{th}` are all scalars, a single curve is returned. If any
2327+
are array-like (of the same length), multiple IV curves are calculated.
23292328
23302329
The input parameters can be calculated from meteorological data using a
23312330
function for a single diode model, e.g.,
@@ -2363,35 +2362,33 @@ def singlediode(photocurrent, saturation_current, resistance_series,
23632362
Number of points in the desired IV curve. If None or 0, no points on
23642363
the IV curves will be produced.
23652364
2365+
.. deprecated:: 0.10.0
2366+
Use :py:func:`pvlib.pvsystem.v_from_i` and
2367+
:py:func:`pvlib.pvsystem.i_from_v` instead.
2368+
23662369
method : str, default 'lambertw'
23672370
Determines the method used to calculate points on the IV curve. The
23682371
options are ``'lambertw'``, ``'newton'``, or ``'brentq'``.
23692372
23702373
Returns
23712374
-------
2372-
OrderedDict or DataFrame
2373-
2374-
The returned dict-like object always contains the keys/columns:
2375-
2376-
* i_sc - short circuit current in amperes.
2377-
* v_oc - open circuit voltage in volts.
2378-
* i_mp - current at maximum power point in amperes.
2379-
* v_mp - voltage at maximum power point in volts.
2380-
* p_mp - power at maximum power point in watts.
2381-
* i_x - current, in amperes, at ``v = 0.5*v_oc``.
2382-
* i_xx - current, in amperes, at ``V = 0.5*(v_oc+v_mp)``.
2375+
dict or pandas.DataFrame
2376+
The returned dict-like object always contains the keys/columns:
23832377
2384-
If ivcurve_pnts is greater than 0, the output dictionary will also
2385-
include the keys:
2378+
* i_sc - short circuit current in amperes.
2379+
* v_oc - open circuit voltage in volts.
2380+
* i_mp - current at maximum power point in amperes.
2381+
* v_mp - voltage at maximum power point in volts.
2382+
* p_mp - power at maximum power point in watts.
2383+
* i_x - current, in amperes, at ``v = 0.5*v_oc``.
2384+
* i_xx - current, in amperes, at ``v = 0.5*(v_oc+v_mp)``.
23862385
2387-
* i - IV curve current in amperes.
2388-
* v - IV curve voltage in volts.
2386+
A dict is returned when the input parameters are scalars or
2387+
``ivcurve_pnts > 0``. If ``ivcurve_pnts > 0``, the output dictionary
2388+
will also include the keys:
23892389
2390-
The output will be an OrderedDict if photocurrent is a scalar,
2391-
array, or ivcurve_pnts is not None.
2392-
2393-
The output will be a DataFrame if photocurrent is a Series and
2394-
ivcurve_pnts is None.
2390+
* i - IV curve current in amperes.
2391+
* v - IV curve voltage in volts.
23952392
23962393
See also
23972394
--------
@@ -2438,22 +2435,25 @@ def singlediode(photocurrent, saturation_current, resistance_series,
24382435
photovoltaic cell interconnection circuits" JW Bishop, Solar Cell (1988)
24392436
https://doi.org/10.1016/0379-6787(88)90059-2
24402437
"""
2438+
if ivcurve_pnts:
2439+
warn_deprecated('0.10.0', name='pvlib.pvsystem.singlediode',
2440+
alternative=('pvlib.pvsystem.v_from_i and '
2441+
'pvlib.pvsystem.i_from_v'),
2442+
obj_type='parameter ivcurve_pnts',
2443+
removal='0.11.0')
2444+
args = (photocurrent, saturation_current, resistance_series,
2445+
resistance_shunt, nNsVth) # collect args
24412446
# Calculate points on the IV curve using the LambertW solution to the
24422447
# single diode equation
24432448
if method.lower() == 'lambertw':
2444-
out = _singlediode._lambertw(
2445-
photocurrent, saturation_current, resistance_series,
2446-
resistance_shunt, nNsVth, ivcurve_pnts
2447-
)
2448-
i_sc, v_oc, i_mp, v_mp, p_mp, i_x, i_xx = out[:7]
2449+
out = _singlediode._lambertw(*args, ivcurve_pnts)
2450+
points = out[:7]
24492451
if ivcurve_pnts:
24502452
ivcurve_i, ivcurve_v = out[7:]
24512453
else:
24522454
# Calculate points on the IV curve using either 'newton' or 'brentq'
24532455
# methods. Voltages are determined by first solving the single diode
24542456
# equation for the diode voltage V_d then backing out voltage
2455-
args = (photocurrent, saturation_current, resistance_series,
2456-
resistance_shunt, nNsVth) # collect args
24572457
v_oc = _singlediode.bishop88_v_from_i(
24582458
0.0, *args, method=method.lower()
24592459
)
@@ -2469,6 +2469,7 @@ def singlediode(photocurrent, saturation_current, resistance_series,
24692469
i_xx = _singlediode.bishop88_i_from_v(
24702470
(v_oc + v_mp) / 2.0, *args, method=method.lower()
24712471
)
2472+
points = i_sc, v_oc, i_mp, v_mp, p_mp, i_x, i_xx
24722473

24732474
# calculate the IV curve if requested using bishop88
24742475
if ivcurve_pnts:
@@ -2477,22 +2478,23 @@ def singlediode(photocurrent, saturation_current, resistance_series,
24772478
)
24782479
ivcurve_i, ivcurve_v, _ = _singlediode.bishop88(vd, *args)
24792480

2480-
out = OrderedDict()
2481-
out['i_sc'] = i_sc
2482-
out['v_oc'] = v_oc
2483-
out['i_mp'] = i_mp
2484-
out['v_mp'] = v_mp
2485-
out['p_mp'] = p_mp
2486-
out['i_x'] = i_x
2487-
out['i_xx'] = i_xx
2481+
columns = ('i_sc', 'v_oc', 'i_mp', 'v_mp', 'p_mp', 'i_x', 'i_xx')
24882482

2489-
if ivcurve_pnts:
2483+
if all(map(np.isscalar, args)) or ivcurve_pnts:
2484+
out = {c: p for c, p in zip(columns, points)}
2485+
2486+
if ivcurve_pnts:
2487+
out.update(i=ivcurve_i, v=ivcurve_v)
2488+
2489+
return out
2490+
2491+
points = np.atleast_1d(*points) # convert scalars to 1d-arrays
2492+
points = np.vstack(points).T # collect rows into DataFrame columns
24902493

2491-
out['v'] = ivcurve_v
2492-
out['i'] = ivcurve_i
2494+
# save the first available pd.Series index, otherwise set to None
2495+
index = next((a.index for a in args if isinstance(a, pd.Series)), None)
24932496

2494-
if isinstance(photocurrent, pd.Series) and not ivcurve_pnts:
2495-
out = pd.DataFrame(out, index=photocurrent.index)
2497+
out = pd.DataFrame(points, columns=columns, index=index)
24962498

24972499
return out
24982500

@@ -2533,7 +2535,7 @@ def max_power_point(photocurrent, saturation_current, resistance_series,
25332535
25342536
Returns
25352537
-------
2536-
OrderedDict or pandas.Datafrane
2538+
OrderedDict or pandas.DataFrame
25372539
``(i_mp, v_mp, p_mp)``
25382540
25392541
Notes

pvlib/tests/ivtools/test_sde.py

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import pytest
33
from pvlib import pvsystem
44
from pvlib.ivtools import sde
5+
from pvlib._deprecation import pvlibDeprecationWarning
56

67

78
@pytest.fixture
@@ -11,31 +12,32 @@ def get_test_iv_params():
1112

1213
def test_fit_sandia_simple(get_test_iv_params, get_bad_iv_curves):
1314
test_params = get_test_iv_params
14-
testcurve = pvsystem.singlediode(photocurrent=test_params['IL'],
15-
saturation_current=test_params['I0'],
16-
resistance_shunt=test_params['Rsh'],
17-
resistance_series=test_params['Rs'],
18-
nNsVth=test_params['nNsVth'],
19-
ivcurve_pnts=300)
20-
expected = tuple(test_params[k] for k in ['IL', 'I0', 'Rs', 'Rsh',
21-
'nNsVth'])
22-
result = sde.fit_sandia_simple(voltage=testcurve['v'],
23-
current=testcurve['i'])
15+
test_params = dict(photocurrent=test_params['IL'],
16+
saturation_current=test_params['I0'],
17+
resistance_series=test_params['Rs'],
18+
resistance_shunt=test_params['Rsh'],
19+
nNsVth=test_params['nNsVth'])
20+
testcurve = pvsystem.singlediode(**test_params)
21+
v = np.linspace(0., testcurve['v_oc'], 300)
22+
i = pvsystem.i_from_v(voltage=v, **test_params)
23+
expected = tuple(test_params.values())
24+
25+
result = sde.fit_sandia_simple(voltage=v, current=i)
2426
assert np.allclose(result, expected, rtol=5e-5)
25-
result = sde.fit_sandia_simple(voltage=testcurve['v'],
26-
current=testcurve['i'],
27+
28+
result = sde.fit_sandia_simple(voltage=v, current=i,
2729
v_oc=testcurve['v_oc'],
2830
i_sc=testcurve['i_sc'])
2931
assert np.allclose(result, expected, rtol=5e-5)
30-
result = sde.fit_sandia_simple(voltage=testcurve['v'],
31-
current=testcurve['i'],
32+
33+
result = sde.fit_sandia_simple(voltage=v, current=i,
3234
v_oc=testcurve['v_oc'],
3335
i_sc=testcurve['i_sc'],
3436
v_mp_i_mp=(testcurve['v_mp'],
35-
testcurve['i_mp']))
37+
testcurve['i_mp']))
3638
assert np.allclose(result, expected, rtol=5e-5)
37-
result = sde.fit_sandia_simple(voltage=testcurve['v'],
38-
current=testcurve['i'], vlim=0.1)
39+
40+
result = sde.fit_sandia_simple(voltage=v, current=i, vlim=0.1)
3941
assert np.allclose(result, expected, rtol=5e-5)
4042

4143

pvlib/tests/ivtools/test_sdm.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from pvlib.ivtools import sdm
88
from pvlib import pvsystem
9+
from pvlib._deprecation import pvlibDeprecationWarning
910

1011
from pvlib.tests.conftest import requires_pysam, requires_statsmodels
1112

@@ -100,11 +101,15 @@ def test_fit_desoto_sandia(cec_params_cansol_cs5p_220p):
100101
temp_cell = np.array([15., 25., 35., 45.])
101102
ee = np.tile(effective_irradiance, len(temp_cell))
102103
tc = np.repeat(temp_cell, len(effective_irradiance))
103-
iph, io, rs, rsh, nnsvth = pvsystem.calcparams_desoto(
104+
IL, I0, Rs, Rsh, nNsVth = pvsystem.calcparams_desoto(
104105
ee, tc, alpha_sc=specs['alpha_sc'], **params)
105-
sim_ivcurves = pvsystem.singlediode(iph, io, rs, rsh, nnsvth, 300)
106-
sim_ivcurves['ee'] = ee
107-
sim_ivcurves['tc'] = tc
106+
ivcurve_params = dict(photocurrent=IL, saturation_current=I0,
107+
resistance_series=Rs, resistance_shunt=Rsh,
108+
nNsVth=nNsVth)
109+
sim_ivcurves = pvsystem.singlediode(**ivcurve_params).to_dict('series')
110+
v = np.linspace(0., sim_ivcurves['v_oc'], 300)
111+
i = pvsystem.i_from_v(voltage=v, **ivcurve_params)
112+
sim_ivcurves.update(v=v.T, i=i.T, ee=ee, tc=tc)
108113

109114
result = sdm.fit_desoto_sandia(sim_ivcurves, specs)
110115
modeled = pd.Series(index=params.keys(), data=np.nan)

pvlib/tests/test_pvsystem.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,8 +1389,9 @@ def test_singlediode_floats():
13891389

13901390

13911391
def test_singlediode_floats_ivcurve():
1392-
out = pvsystem.singlediode(7., 6e-7, .1, 20., .5, ivcurve_pnts=3,
1393-
method='lambertw')
1392+
with pytest.warns(pvlibDeprecationWarning, match='ivcurve_pnts'):
1393+
out = pvsystem.singlediode(7., 6e-7, .1, 20., .5, ivcurve_pnts=3,
1394+
method='lambertw')
13941395
expected = {'i_xx': 4.264060478,
13951396
'i_mp': 6.136267360,
13961397
'v_oc': 8.106300147,
@@ -1422,8 +1423,9 @@ def test_singlediode_series_ivcurve(cec_module_params):
14221423
EgRef=1.121,
14231424
dEgdT=-0.0002677)
14241425

1425-
out = pvsystem.singlediode(IL, I0, Rs, Rsh, nNsVth, ivcurve_pnts=3,
1426-
method='lambertw')
1426+
with pytest.warns(pvlibDeprecationWarning, match='ivcurve_pnts'):
1427+
out = pvsystem.singlediode(IL, I0, Rs, Rsh, nNsVth, ivcurve_pnts=3,
1428+
method='lambertw')
14271429

14281430
expected = OrderedDict([('i_sc', array([0., 3.01079860, 6.00726296])),
14291431
('v_oc', array([0., 9.96959733, 10.29603253])),
@@ -1442,7 +1444,8 @@ def test_singlediode_series_ivcurve(cec_module_params):
14421444
for k, v in out.items():
14431445
assert_allclose(v, expected[k], atol=1e-2)
14441446

1445-
out = pvsystem.singlediode(IL, I0, Rs, Rsh, nNsVth, ivcurve_pnts=3)
1447+
with pytest.warns(pvlibDeprecationWarning, match='ivcurve_pnts'):
1448+
out = pvsystem.singlediode(IL, I0, Rs, Rsh, nNsVth, ivcurve_pnts=3)
14461449

14471450
expected['i_mp'] = pvsystem.i_from_v(out['v_mp'], IL, I0, Rs, Rsh, nNsVth,
14481451
method='lambertw')
@@ -1457,6 +1460,14 @@ def test_singlediode_series_ivcurve(cec_module_params):
14571460
assert_allclose(v, expected[k], atol=1e-6)
14581461

14591462

1463+
@fail_on_pvlib_version('0.11')
1464+
@pytest.mark.parametrize('method', ['lambertw', 'brentq', 'newton'])
1465+
def test_singlediode_ivcurvepnts_deprecation_warning(method):
1466+
with pytest.warns(pvlibDeprecationWarning, match='ivcurve_pnts'):
1467+
pvsystem.singlediode(7., 6e-7, .1, 20., .5, ivcurve_pnts=3,
1468+
method=method)
1469+
1470+
14601471
def test_scale_voltage_current_power():
14611472
data = pd.DataFrame(
14621473
np.array([[2, 1.5, 10, 8, 12, 0.5, 1.5]]),

pvlib/tests/test_singlediode.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from pvlib import pvsystem
99
from pvlib.singlediode import (bishop88_mpp, estimate_voc, VOLTAGE_BUILTIN,
1010
bishop88, bishop88_i_from_v, bishop88_v_from_i)
11+
from pvlib._deprecation import pvlibDeprecationWarning
1112
import pytest
1213
from .conftest import DATA_DIR
1314

@@ -176,7 +177,10 @@ def test_ivcurve_pnts_precision(method, precise_iv_curves):
176177
x, pc = precise_iv_curves
177178
pc_i, pc_v = np.stack(pc['Currents']), np.stack(pc['Voltages'])
178179
ivcurve_pnts = len(pc['Currents'][0])
179-
outs = pvsystem.singlediode(method=method, ivcurve_pnts=ivcurve_pnts, **x)
180+
181+
with pytest.warns(pvlibDeprecationWarning, match='ivcurve_pnts'):
182+
outs = pvsystem.singlediode(method=method, ivcurve_pnts=ivcurve_pnts,
183+
**x)
180184

181185
assert np.allclose(pc_i, outs['i'], atol=1e-10, rtol=0)
182186
assert np.allclose(pc_v, outs['v'], atol=1e-10, rtol=0)

0 commit comments

Comments
 (0)