Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate pvsystem.singlediode parameter ivcurve_pnts #1743

Merged
merged 9 commits into from Jun 23, 2023
28 changes: 15 additions & 13 deletions docs/examples/iv-modeling/plot_singlediode.py
Expand Up @@ -32,6 +32,7 @@
# :py:meth:`pvlib.pvsystem.singlediode` is then used to generate the IV curves.
reepoi marked this conversation as resolved.
Show resolved Hide resolved

from pvlib import pvsystem
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

Expand Down Expand Up @@ -88,26 +89,27 @@
)

# plug the parameters into the SDE and solve for IV curves:
curve_info = pvsystem.singlediode(
photocurrent=IL,
saturation_current=I0,
resistance_series=Rs,
resistance_shunt=Rsh,
nNsVth=nNsVth,
ivcurve_pnts=100,
method='lambertw'
)
SDE_params = {
'photocurrent': IL,
'saturation_current': I0,
'resistance_series': Rs,
'resistance_shunt': Rsh,
'nNsVth': nNsVth
}
curve_info = pvsystem.singlediode(method='lambertw', **SDE_params)
v = pd.DataFrame(np.linspace(0., curve_info['v_oc'], 100))
i = pd.DataFrame(pvsystem.i_from_v(voltage=v, method='lambertw', **SDE_params))

# plot the calculated curves:
plt.figure()
for i, case in conditions.iterrows():
for idx, case in conditions.iterrows():
label = (
"$G_{eff}$ " + f"{case['Geff']} $W/m^2$\n"
"$T_{cell}$ " + f"{case['Tcell']} $\\degree C$"
)
plt.plot(curve_info['v'][i], curve_info['i'][i], label=label)
v_mp = curve_info['v_mp'][i]
i_mp = curve_info['i_mp'][i]
plt.plot(v[idx], i[idx], label=label)
v_mp = curve_info['v_mp'][idx]
i_mp = curve_info['i_mp'][idx]
# mark the MPP
plt.plot([v_mp], [i_mp], ls='', marker='o', c='k')

Expand Down
3 changes: 3 additions & 0 deletions docs/sphinx/source/whatsnew/v0.10.0.rst
Expand Up @@ -17,6 +17,9 @@ Breaking changes

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


Enhancements
Expand Down
98 changes: 50 additions & 48 deletions pvlib/pvsystem.py
Expand Up @@ -16,7 +16,7 @@
from abc import ABC, abstractmethod
from typing import Optional

from pvlib._deprecation import deprecated
from pvlib._deprecation import deprecated, warn_deprecated

from pvlib import (atmosphere, iam, inverter, irradiance,
singlediode as _singlediode, temperature)
Expand Down Expand Up @@ -2714,7 +2714,7 @@ def singlediode(photocurrent, saturation_current, resistance_series,
resistance_shunt, nNsVth, ivcurve_pnts=None,
method='lambertw'):
r"""
Solve the single-diode equation to obtain a photovoltaic IV curve.
Solve the single diode equation to obtain a photovoltaic IV curve.

Solves the single diode equation [1]_

Expand All @@ -2727,11 +2727,10 @@ def singlediode(photocurrent, saturation_current, resistance_series,
\frac{V + I R_s}{R_{sh}}

for :math:`I` and :math:`V` when given :math:`I_L, I_0, R_s, R_{sh},` and
:math:`n N_s V_{th}` which are described later. Returns a DataFrame
which contains the 5 points on the I-V curve specified in
[3]_. If all :math:`I_L, I_0, R_s, R_{sh},` and
:math:`n N_s V_{th}` are scalar, a single curve is returned, if any
are Series (of the same length), multiple IV curves are calculated.
:math:`n N_s V_{th}` which are described later. The five points on the I-V
curve specified in [3]_ are returned. If :math:`I_L, I_0, R_s, R_{sh},` and
:math:`n N_s V_{th}` are all scalars, a single curve is returned. If any
are array-like (of the same length), multiple IV curves are calculated.

The input parameters can be calculated from meteorological data using a
function for a single diode model, e.g.,
Expand Down Expand Up @@ -2769,35 +2768,33 @@ def singlediode(photocurrent, saturation_current, resistance_series,
Number of points in the desired IV curve. If None or 0, no points on
the IV curves will be produced.

.. deprecated:: 0.10.0
Use :py:func:`pvlib.pvsystem.v_from_i` and
:py:func:`pvlib.pvsystem.i_from_v` instead.

method : str, default 'lambertw'
Determines the method used to calculate points on the IV curve. The
options are ``'lambertw'``, ``'newton'``, or ``'brentq'``.

Returns
-------
OrderedDict or DataFrame

The returned dict-like object always contains the keys/columns:

* i_sc - short circuit current in amperes.
* v_oc - open circuit voltage in volts.
* i_mp - current at maximum power point in amperes.
* v_mp - voltage at maximum power point in volts.
* p_mp - power at maximum power point in watts.
* i_x - current, in amperes, at ``v = 0.5*v_oc``.
* i_xx - current, in amperes, at ``V = 0.5*(v_oc+v_mp)``.
dict or pandas.DataFrame
The returned dict-like object always contains the keys/columns:

If ivcurve_pnts is greater than 0, the output dictionary will also
include the keys:
* i_sc - short circuit current in amperes.
* v_oc - open circuit voltage in volts.
* i_mp - current at maximum power point in amperes.
* v_mp - voltage at maximum power point in volts.
* p_mp - power at maximum power point in watts.
* i_x - current, in amperes, at ``v = 0.5*v_oc``.
* i_xx - current, in amperes, at ``v = 0.5*(v_oc+v_mp)``.

* i - IV curve current in amperes.
* v - IV curve voltage in volts.
A dict is returned when the input parameters are scalars or
``ivcurve_pnts > 0``. If ``ivcurve_pnts > 0``, the output dictionary
will also include the keys:

The output will be an OrderedDict if photocurrent is a scalar,
array, or ivcurve_pnts is not None.

The output will be a DataFrame if photocurrent is a Series and
ivcurve_pnts is None.
* i - IV curve current in amperes.
* v - IV curve voltage in volts.

See also
--------
Expand Down Expand Up @@ -2844,22 +2841,25 @@ def singlediode(photocurrent, saturation_current, resistance_series,
photovoltaic cell interconnection circuits" JW Bishop, Solar Cell (1988)
https://doi.org/10.1016/0379-6787(88)90059-2
"""
if ivcurve_pnts:
cwhanse marked this conversation as resolved.
Show resolved Hide resolved
warn_deprecated('0.10.0', name='pvlib.pvsystem.singlediode',
alternative=('pvlib.pvsystem.v_from_i and '
'pvlib.pvsystem.i_from_v'),
obj_type='parameter ivcurve_pnts',
removal='0.11.0')
args = (photocurrent, saturation_current, resistance_series,
resistance_shunt, nNsVth) # collect args
# Calculate points on the IV curve using the LambertW solution to the
# single diode equation
if method.lower() == 'lambertw':
out = _singlediode._lambertw(
photocurrent, saturation_current, resistance_series,
resistance_shunt, nNsVth, ivcurve_pnts
)
i_sc, v_oc, i_mp, v_mp, p_mp, i_x, i_xx = out[:7]
out = _singlediode._lambertw(*args, ivcurve_pnts)
points = out[:7]
if ivcurve_pnts:
ivcurve_i, ivcurve_v = out[7:]
else:
# Calculate points on the IV curve using either 'newton' or 'brentq'
# methods. Voltages are determined by first solving the single diode
# equation for the diode voltage V_d then backing out voltage
args = (photocurrent, saturation_current, resistance_series,
resistance_shunt, nNsVth) # collect args
v_oc = _singlediode.bishop88_v_from_i(
0.0, *args, method=method.lower()
)
Expand All @@ -2875,6 +2875,7 @@ def singlediode(photocurrent, saturation_current, resistance_series,
i_xx = _singlediode.bishop88_i_from_v(
(v_oc + v_mp) / 2.0, *args, method=method.lower()
)
points = i_sc, v_oc, i_mp, v_mp, p_mp, i_x, i_xx

# calculate the IV curve if requested using bishop88
if ivcurve_pnts:
Expand All @@ -2883,22 +2884,23 @@ def singlediode(photocurrent, saturation_current, resistance_series,
)
ivcurve_i, ivcurve_v, _ = _singlediode.bishop88(vd, *args)

out = OrderedDict()
out['i_sc'] = i_sc
out['v_oc'] = v_oc
out['i_mp'] = i_mp
out['v_mp'] = v_mp
out['p_mp'] = p_mp
out['i_x'] = i_x
out['i_xx'] = i_xx
columns = ('i_sc', 'v_oc', 'i_mp', 'v_mp', 'p_mp', 'i_x', 'i_xx')

if ivcurve_pnts:
if all(map(np.isscalar, args)) or ivcurve_pnts:
out = {c: p for c, p in zip(columns, points)}

if ivcurve_pnts:
out.update(i=ivcurve_i, v=ivcurve_v)

return out

points = np.atleast_1d(*points) # convert scalars to 1d-arrays
points = np.vstack(points).T # collect rows into DataFrame columns

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

if isinstance(photocurrent, pd.Series) and not ivcurve_pnts:
out = pd.DataFrame(out, index=photocurrent.index)
out = pd.DataFrame(points, columns=columns, index=index)

return out

Expand Down Expand Up @@ -2939,7 +2941,7 @@ def max_power_point(photocurrent, saturation_current, resistance_series,

Returns
-------
OrderedDict or pandas.Datafrane
OrderedDict or pandas.DataFrame
``(i_mp, v_mp, p_mp)``

Notes
Expand Down
3 changes: 3 additions & 0 deletions pvlib/tools.py
Expand Up @@ -377,6 +377,9 @@ def _golden_sect_DataFrame(params, lower, upper, func, atol=1e-8):
df['max'] = 0.5 * (df['V1'] + df['V2'])
func_result = func(df, 'max')
x = np.where(np.isnan(func_result), np.nan, df['max'])
if np.isscalar(df['max']):
# np.where always returns an ndarray, converting scalars to 0d-arrays
x = x.item()

return func_result, x

Expand Down