Skip to content

Commit 161522c

Browse files
committed
removing ivcurve_pnts from pvsystem.singlediode
1 parent 275e671 commit 161522c

File tree

7 files changed

+95
-171
lines changed

7 files changed

+95
-171
lines changed

docs/examples/iv-modeling/plot_singlediode.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
# :py:meth:`pvlib.pvsystem.singlediode` is then used to generate the IV curves.
3333

3434
from pvlib import pvsystem
35+
import numpy as np
3536
import pandas as pd
3637
import matplotlib.pyplot as plt
3738

@@ -88,26 +89,27 @@
8889
)
8990

9091
# 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-
)
92+
SDE_params = {
93+
'photocurrent': IL,
94+
'saturation_current': I0,
95+
'resistance_series': Rs,
96+
'resistance_shunt': Rsh,
97+
'nNsVth': nNsVth
98+
}
99+
curve_info = pvsystem.singlediode(method='lambertw', **SDE_params)
100+
v = pd.DataFrame(np.linspace(0., curve_info['v_oc'], 100))
101+
i = pd.DataFrame(pvsystem.i_from_v(voltage=v, method='lambertw', **SDE_params))
100102

101103
# plot the calculated curves:
102104
plt.figure()
103-
for i, case in conditions.iterrows():
105+
for idx, case in conditions.iterrows():
104106
label = (
105107
"$G_{eff}$ " + f"{case['Geff']} $W/m^2$\n"
106108
"$T_{cell}$ " + f"{case['Tcell']} $\\degree C$"
107109
)
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]
110+
plt.plot(v[idx], i[idx], label=label)
111+
v_mp = curve_info['v_mp'][idx]
112+
i_mp = curve_info['i_mp'][idx]
111113
# mark the MPP
112114
plt.plot([v_mp], [i_mp], ls='', marker='o', c='k')
113115

pvlib/pvsystem.py

Lines changed: 31 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -934,15 +934,13 @@ def _spectral_correction(array, pw):
934934
)
935935

936936
def singlediode(self, photocurrent, saturation_current,
937-
resistance_series, resistance_shunt, nNsVth,
938-
ivcurve_pnts=None):
937+
resistance_series, resistance_shunt, nNsVth):
939938
"""Wrapper around the :py:func:`pvlib.pvsystem.singlediode` function.
940939
941940
See :py:func:`pvsystem.singlediode` for details
942941
"""
943942
return singlediode(photocurrent, saturation_current,
944-
resistance_series, resistance_shunt, nNsVth,
945-
ivcurve_pnts=ivcurve_pnts)
943+
resistance_series, resistance_shunt, nNsVth)
946944

947945
def i_from_v(self, resistance_shunt, resistance_series, nNsVth, voltage,
948946
saturation_current, photocurrent):
@@ -2708,10 +2706,9 @@ def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi,
27082706

27092707

27102708
def singlediode(photocurrent, saturation_current, resistance_series,
2711-
resistance_shunt, nNsVth, ivcurve_pnts=None,
2712-
method='lambertw'):
2709+
resistance_shunt, nNsVth, method='lambertw'):
27132710
r"""
2714-
Solve the single-diode equation to obtain a photovoltaic IV curve.
2711+
Solve the single diode equation to obtain a photovoltaic IV curve.
27152712
27162713
Solves the single diode equation [1]_
27172714
@@ -2724,11 +2721,10 @@ def singlediode(photocurrent, saturation_current, resistance_series,
27242721
\frac{V + I R_s}{R_{sh}}
27252722
27262723
for :math:`I` and :math:`V` when given :math:`I_L, I_0, R_s, R_{sh},` and
2727-
:math:`n N_s V_{th}` which are described later. Returns a DataFrame
2728-
which contains the 5 points on the I-V curve specified in
2729-
[3]_. If all :math:`I_L, I_0, R_s, R_{sh},` and
2730-
:math:`n N_s V_{th}` are scalar, a single curve is returned, if any
2731-
are Series (of the same length), multiple IV curves are calculated.
2724+
:math:`n N_s V_{th}` which are described later. The five points on the I-V
2725+
curve specified in [3]_ are returned. If :math:`I_L, I_0, R_s, R_{sh},`
2726+
and :math:`n N_s V_{th}` are all scalar, a single curve is returned. If any
2727+
are array-like (of the same length), multiple IV curves are calculated.
27322728
27332729
The input parameters can be calculated from meteorological data using a
27342730
function for a single diode model, e.g.,
@@ -2762,39 +2758,25 @@ def singlediode(photocurrent, saturation_current, resistance_series,
27622758
junction in Kelvin, and :math:`q` is the charge of an electron
27632759
(coulombs). ``0 < nNsVth``. [V]
27642760
2765-
ivcurve_pnts : None or int, default None
2766-
Number of points in the desired IV curve. If None or 0, no points on
2767-
the IV curves will be produced.
2768-
27692761
method : str, default 'lambertw'
27702762
Determines the method used to calculate points on the IV curve. The
27712763
options are ``'lambertw'``, ``'newton'``, or ``'brentq'``.
27722764
27732765
Returns
27742766
-------
2775-
OrderedDict or DataFrame
2767+
dict or pandas.DataFrame
2768+
2769+
A dict is returned when the input parameters are scalars.
27762770
2777-
The returned dict-like object always contains the keys/columns:
2771+
The result contains the keys/columns:
27782772
27792773
* i_sc - short circuit current in amperes.
27802774
* v_oc - open circuit voltage in volts.
27812775
* i_mp - current at maximum power point in amperes.
27822776
* v_mp - voltage at maximum power point in volts.
27832777
* p_mp - power at maximum power point in watts.
27842778
* i_x - current, in amperes, at ``v = 0.5*v_oc``.
2785-
* i_xx - current, in amperes, at ``V = 0.5*(v_oc+v_mp)``.
2786-
2787-
If ivcurve_pnts is greater than 0, the output dictionary will also
2788-
include the keys:
2789-
2790-
* i - IV curve current in amperes.
2791-
* v - IV curve voltage in volts.
2792-
2793-
The output will be an OrderedDict if photocurrent is a scalar,
2794-
array, or ivcurve_pnts is not None.
2795-
2796-
The output will be a DataFrame if photocurrent is a Series and
2797-
ivcurve_pnts is None.
2779+
* i_xx - current, in amperes, at ``v = 0.5*(v_oc+v_mp)``.
27982780
27992781
See also
28002782
--------
@@ -2818,13 +2800,6 @@ def singlediode(photocurrent, saturation_current, resistance_series,
28182800
that guarantees convergence by bounding the voltage between zero and
28192801
open-circuit.
28202802
2821-
If the method is either ``'newton'`` or ``'brentq'`` and ``ivcurve_pnts``
2822-
are indicated, then :func:`pvlib.singlediode.bishop88` [4]_ is used to
2823-
calculate the points on the IV curve points at diode voltages from zero to
2824-
open-circuit voltage with a log spacing that gets closer as voltage
2825-
increases. If the method is ``'lambertw'`` then the calculated points on
2826-
the IV curve are linearly spaced.
2827-
28282803
References
28292804
----------
28302805
.. [1] S.R. Wenham, M.A. Green, M.E. Watt, "Applied Photovoltaics" ISBN
@@ -2841,22 +2816,17 @@ def singlediode(photocurrent, saturation_current, resistance_series,
28412816
photovoltaic cell interconnection circuits" JW Bishop, Solar Cell (1988)
28422817
https://doi.org/10.1016/0379-6787(88)90059-2
28432818
"""
2819+
args = (photocurrent, saturation_current, resistance_series,
2820+
resistance_shunt, nNsVth) # collect args
28442821
# Calculate points on the IV curve using the LambertW solution to the
28452822
# single diode equation
28462823
if method.lower() == 'lambertw':
2847-
out = _singlediode._lambertw(
2848-
photocurrent, saturation_current, resistance_series,
2849-
resistance_shunt, nNsVth, ivcurve_pnts
2850-
)
2851-
i_sc, v_oc, i_mp, v_mp, p_mp, i_x, i_xx = out[:7]
2852-
if ivcurve_pnts:
2853-
ivcurve_i, ivcurve_v = out[7:]
2824+
out = _singlediode._lambertw(*args)
2825+
points = out[:7]
28542826
else:
28552827
# Calculate points on the IV curve using either 'newton' or 'brentq'
28562828
# methods. Voltages are determined by first solving the single diode
28572829
# equation for the diode voltage V_d then backing out voltage
2858-
args = (photocurrent, saturation_current, resistance_series,
2859-
resistance_shunt, nNsVth) # collect args
28602830
v_oc = _singlediode.bishop88_v_from_i(
28612831
0.0, *args, method=method.lower()
28622832
)
@@ -2872,30 +2842,24 @@ def singlediode(photocurrent, saturation_current, resistance_series,
28722842
i_xx = _singlediode.bishop88_i_from_v(
28732843
(v_oc + v_mp) / 2.0, *args, method=method.lower()
28742844
)
2845+
points = i_sc, v_oc, i_mp, v_mp, p_mp, i_x, i_xx
28752846

2876-
# calculate the IV curve if requested using bishop88
2877-
if ivcurve_pnts:
2878-
vd = v_oc * (
2879-
(11.0 - np.logspace(np.log10(11.0), 0.0, ivcurve_pnts)) / 10.0
2880-
)
2881-
ivcurve_i, ivcurve_v, _ = _singlediode.bishop88(vd, *args)
2847+
columns = ['i_sc', 'v_oc', 'i_mp', 'v_mp', 'p_mp', 'i_x', 'i_xx']
28822848

2883-
out = OrderedDict()
2884-
out['i_sc'] = i_sc
2885-
out['v_oc'] = v_oc
2886-
out['i_mp'] = i_mp
2887-
out['v_mp'] = v_mp
2888-
out['p_mp'] = p_mp
2889-
out['i_x'] = i_x
2890-
out['i_xx'] = i_xx
2849+
if all(map(np.isscalar, args)):
2850+
return {c: p for c, p in zip(columns, points)}
28912851

2892-
if ivcurve_pnts:
2852+
points = np.atleast_1d(*points) # convert scalars to 1d arrays
2853+
points = np.vstack(points).T # collect rows into DataFrame columns
28932854

2894-
out['v'] = ivcurve_v
2895-
out['i'] = ivcurve_i
2855+
# save the first available pd.Series index, otherwise set to None
2856+
index = next((a.index for a in args if isinstance(a, pd.Series)), None)
28962857

2897-
if isinstance(photocurrent, pd.Series) and not ivcurve_pnts:
2898-
out = pd.DataFrame(out, index=photocurrent.index)
2858+
out = pd.DataFrame(
2859+
points,
2860+
columns=['i_sc', 'v_oc', 'i_mp', 'v_mp', 'p_mp', 'i_x', 'i_xx'],
2861+
index=index
2862+
)
28992863

29002864
return out
29012865

@@ -2936,7 +2900,7 @@ def max_power_point(photocurrent, saturation_current, resistance_series,
29362900
29372901
Returns
29382902
-------
2939-
OrderedDict or pandas.Datafrane
2903+
OrderedDict or pandas.DataFrame
29402904
``(i_mp, v_mp, p_mp)``
29412905
29422906
Notes

pvlib/tests/ivtools/test_sde.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,31 +11,32 @@ def get_test_iv_params():
1111

1212
def test_fit_sandia_simple(get_test_iv_params, get_bad_iv_curves):
1313
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'])
14+
test_params = dict(photocurrent=test_params['IL'],
15+
saturation_current=test_params['I0'],
16+
resistance_series=test_params['Rs'],
17+
resistance_shunt=test_params['Rsh'],
18+
nNsVth=test_params['nNsVth'])
19+
testcurve = pvsystem.singlediode(**test_params)
20+
v = np.linspace(0., testcurve['v_oc'], 300)
21+
i = pvsystem.i_from_v(voltage=v, **test_params)
22+
expected = tuple(test_params.values())
23+
24+
result = sde.fit_sandia_simple(voltage=v, current=i)
2425
assert np.allclose(result, expected, rtol=5e-5)
25-
result = sde.fit_sandia_simple(voltage=testcurve['v'],
26-
current=testcurve['i'],
26+
27+
result = sde.fit_sandia_simple(voltage=v, current=i,
2728
v_oc=testcurve['v_oc'],
2829
i_sc=testcurve['i_sc'])
2930
assert np.allclose(result, expected, rtol=5e-5)
30-
result = sde.fit_sandia_simple(voltage=testcurve['v'],
31-
current=testcurve['i'],
31+
32+
result = sde.fit_sandia_simple(voltage=v, current=i,
3233
v_oc=testcurve['v_oc'],
3334
i_sc=testcurve['i_sc'],
3435
v_mp_i_mp=(testcurve['v_mp'],
35-
testcurve['i_mp']))
36+
testcurve['i_mp']))
3637
assert np.allclose(result, expected, rtol=5e-5)
37-
result = sde.fit_sandia_simple(voltage=testcurve['v'],
38-
current=testcurve['i'], vlim=0.1)
38+
39+
result = sde.fit_sandia_simple(voltage=v, current=i, vlim=0.1)
3940
assert np.allclose(result, expected, rtol=5e-5)
4041

4142

pvlib/tests/ivtools/test_sdm.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,15 @@ def test_fit_desoto_sandia(cec_params_cansol_cs5p_220p):
100100
temp_cell = np.array([15., 25., 35., 45.])
101101
ee = np.tile(effective_irradiance, len(temp_cell))
102102
tc = np.repeat(temp_cell, len(effective_irradiance))
103-
iph, io, rs, rsh, nnsvth = pvsystem.calcparams_desoto(
103+
IL, I0, Rs, Rsh, nNsVth = pvsystem.calcparams_desoto(
104104
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
105+
ivcurve_params = dict(photocurrent=IL, saturation_current=I0,
106+
resistance_series=Rs, resistance_shunt=Rsh,
107+
nNsVth=nNsVth)
108+
sim_ivcurves = pvsystem.singlediode(**ivcurve_params).to_dict('series')
109+
v = np.linspace(0., sim_ivcurves['v_oc'], 300)
110+
i = pvsystem.i_from_v(voltage=v, **ivcurve_params)
111+
sim_ivcurves.update(v=v.T, i=i.T, ee=ee, tc=tc)
108112

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

0 commit comments

Comments
 (0)