Skip to content

Commit c69164a

Browse files
committed
add optional ivcurve calculation to singlediode
1 parent ceaf104 commit c69164a

File tree

3 files changed

+113
-71
lines changed

3 files changed

+113
-71
lines changed

docs/sphinx/source/whatsnew/v0.4.0.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ Enhancements
4646
* Add solarposition.nrel_earthsun_distance function and option to
4747
calculate extraterrestrial radiation using the NREL solar position
4848
algorithm. (:issue:`211`, :issue:`215`)
49+
* pvsystem.singlediode can now calculate IV curves if a user supplies
50+
an ivcurve_pnts keyword argument. (:issue:`83`)
4951

5052

5153
Bug fixes

pvlib/pvsystem.py

Lines changed: 47 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,8 @@ def sapm_effective_irradiance(self, poa_direct, poa_diffuse,
412412
self.module_parameters, reference_irradiance=reference_irradiance)
413413

414414
def singlediode(self, photocurrent, saturation_current,
415-
resistance_series, resistance_shunt, nNsVth):
415+
resistance_series, resistance_shunt, nNsVth,
416+
ivcurve_pnts=None):
416417
"""Wrapper around the :py:func:`singlediode` function.
417418
418419
Parameters
@@ -424,7 +425,8 @@ def singlediode(self, photocurrent, saturation_current,
424425
See pvsystem.singlediode for details
425426
"""
426427
return singlediode(photocurrent, saturation_current,
427-
resistance_series, resistance_shunt, nNsVth)
428+
resistance_series, resistance_shunt, nNsVth,
429+
ivcurve_pnts=ivcurve_pnts)
428430

429431
def i_from_v(self, resistance_shunt, resistance_series, nNsVth, voltage,
430432
saturation_current, photocurrent):
@@ -1526,7 +1528,7 @@ def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi,
15261528

15271529

15281530
def singlediode(photocurrent, saturation_current, resistance_series,
1529-
resistance_shunt, nNsVth):
1531+
resistance_shunt, nNsVth, ivcurve_pnts=None):
15301532
r'''
15311533
Solve the single-diode model to obtain a photovoltaic IV curve.
15321534
@@ -1573,13 +1575,18 @@ def singlediode(photocurrent, saturation_current, resistance_series,
15731575
temp_cell is the temperature of the p-n junction in Kelvin, and
15741576
q is the charge of an electron (coulombs).
15751577
1578+
ivcurve_pnts : None or int
1579+
Number of points in the desired IV curve. If None or 0, no
1580+
IV curves will be produced.
1581+
15761582
Returns
15771583
-------
1578-
If ``photocurrent`` is a Series, a DataFrame with the following
1579-
columns. All columns have the same number of rows as the largest
1580-
input DataFrame.
1584+
If photocurrent is a Series and ivcurve_pnts is None, a DataFrame
1585+
with the columns described below. All columns have the same number
1586+
of rows as the largest input DataFrame.
15811587
1582-
If ``photocurrent`` is a scalar, a dict with the following keys.
1588+
If photocurrent is a scalar or ivcurve_pnts is not None, an
1589+
OrderedDict with the following keys.
15831590
15841591
* i_sc - short circuit current in amperes.
15851592
* v_oc - open circuit voltage in volts.
@@ -1588,6 +1595,8 @@ def singlediode(photocurrent, saturation_current, resistance_series,
15881595
* p_mp - power at maximum power point in watts.
15891596
* i_x - current, in amperes, at ``v = 0.5*v_oc``.
15901597
* i_xx - current, in amperes, at ``V = 0.5*(v_oc+v_mp)``.
1598+
* i - None or iv curve current.
1599+
* v - None or iv curve voltage.
15911600
15921601
Notes
15931602
-----
@@ -1641,37 +1650,32 @@ def singlediode(photocurrent, saturation_current, resistance_series,
16411650
i_xx = i_from_v(resistance_shunt, resistance_series, nNsVth,
16421651
0.5*(v_oc+v_mp), saturation_current, photocurrent)
16431652

1644-
# @wholmgren: need to move this stuff to a different function
1645-
# If the user says they want a curve of with number of points equal to
1646-
# NumPoints (must be >=2), then create a voltage array where voltage is
1647-
# zero in the first column, and Voc in the last column. Number of columns
1648-
# must equal NumPoints. Each row represents the voltage for one IV curve.
1649-
# Then create a current array where current is Isc in the first column, and
1650-
# zero in the last column, and each row represents the current in one IV
1651-
# curve. Thus the nth (V,I) point of curve m would be found as follows:
1652-
# (Result.V(m,n),Result.I(m,n)).
1653-
# if NumPoints >= 2
1654-
# s = ones(1,NumPoints); # shaping DataFrame to shape the column
1655-
# # DataFrame parameters into 2-D matrices
1656-
# Result.V = (Voc)*(0:1/(NumPoints-1):1);
1657-
# Result.I = I_from_V(Rsh*s, Rs*s, nNsVth*s, Result.V, I0*s, IL*s);
1658-
# end
1659-
1660-
dfout = {}
1661-
dfout['i_sc'] = i_sc
1662-
dfout['i_mp'] = i_mp
1663-
dfout['v_oc'] = v_oc
1664-
dfout['v_mp'] = v_mp
1665-
dfout['p_mp'] = p_mp
1666-
dfout['i_x'] = i_x
1667-
dfout['i_xx'] = i_xx
1653+
# create ivcurve
1654+
if ivcurve_pnts:
1655+
ivcurve_v = (np.asarray(v_oc)[..., np.newaxis] *
1656+
np.linspace(0, 1, ivcurve_pnts))
1657+
ivcurve_i = i_from_v(
1658+
resistance_shunt, resistance_series, nNsVth, ivcurve_v.T,
1659+
saturation_current, photocurrent).T
1660+
else:
1661+
ivcurve_v = None
1662+
ivcurve_i = None
16681663

1669-
try:
1670-
dfout = pd.DataFrame(dfout, index=photocurrent.index)
1671-
except AttributeError:
1672-
pass
1664+
out = OrderedDict()
1665+
out['i_sc'] = i_sc
1666+
out['i_mp'] = i_mp
1667+
out['v_oc'] = v_oc
1668+
out['v_mp'] = v_mp
1669+
out['p_mp'] = p_mp
1670+
out['i_x'] = i_x
1671+
out['i_xx'] = i_xx
1672+
out['i'] = ivcurve_i
1673+
out['v'] = ivcurve_v
1674+
1675+
if isinstance(photocurrent, pd.Series) and not ivcurve_pnts:
1676+
out = pd.DataFrame(out, index=photocurrent.index)
16731677

1674-
return dfout
1678+
return out
16751679

16761680

16771681
# Created April,2014
@@ -1872,11 +1876,13 @@ def i_from_v(resistance_shunt, resistance_series, nNsVth, voltage,
18721876
except ImportError:
18731877
raise ImportError('This function requires scipy')
18741878

1875-
Rsh = resistance_shunt
1876-
Rs = resistance_series
1877-
I0 = saturation_current
1878-
IL = photocurrent
1879-
V = voltage
1879+
# asarray turns Series into arrays so that we don't have to worry
1880+
# about multidimensional broadcasting failing
1881+
Rsh = np.asarray(resistance_shunt)
1882+
Rs = np.asarray(resistance_series)
1883+
I0 = np.asarray(saturation_current)
1884+
IL = np.asarray(photocurrent)
1885+
V = np.asarray(voltage)
18801886

18811887
argW = (Rs*I0*Rsh *
18821888
np.exp(Rsh*(Rs*(IL+I0)+V) / (nNsVth*(Rs+Rsh))) /

pvlib/test/test_pvsystem.py

Lines changed: 64 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from collections import OrderedDict
55

66
import numpy as np
7-
from numpy import nan
7+
from numpy import nan, array
88
import pandas as pd
99

1010
import pytest
@@ -150,6 +150,14 @@ def sapm_module_params(sam_data):
150150
return module_parameters
151151

152152

153+
@pytest.fixture(scope="session")
154+
def cec_module_params(sam_data):
155+
modules = sam_data['cecmod']
156+
module = 'Example_Module'
157+
module_parameters = modules[module]
158+
return module_parameters
159+
160+
153161
def test_sapm(sapm_module_params):
154162

155163
times = pd.DatetimeIndex(start='2015-01-01', periods=5, freq='12H')
@@ -307,17 +315,15 @@ def test_PVSystem_sapm_effective_irradiance(sapm_module_params):
307315
aoi, reference_irradiance=reference_irradiance)
308316

309317

310-
def test_calcparams_desoto(sam_data):
311-
module = 'Example_Module'
312-
module_parameters = sam_data['cecmod'][module]
318+
def test_calcparams_desoto(cec_module_params):
313319
times = pd.DatetimeIndex(start='2015-01-01', periods=2, freq='12H')
314320
poa_data = pd.Series([0, 800], index=times)
315321

316322
IL, I0, Rs, Rsh, nNsVth = pvsystem.calcparams_desoto(
317323
poa_data,
318324
temp_cell=25,
319-
alpha_isc=module_parameters['alpha_sc'],
320-
module_parameters=module_parameters,
325+
alpha_isc=cec_module_params['alpha_sc'],
326+
module_parameters=cec_module_params,
321327
EgRef=1.121,
322328
dEgdT=-0.0002677)
323329

@@ -328,13 +334,11 @@ def test_calcparams_desoto(sam_data):
328334
assert_allclose(nNsVth, 0.473)
329335

330336

331-
def test_PVSystem_calcparams_desoto(sam_data):
332-
module = 'Example_Module'
333-
module_parameters = sam_data['cecmod'][module].copy()
337+
def test_PVSystem_calcparams_desoto(cec_module_params):
338+
module_parameters = cec_module_params.copy()
334339
module_parameters['EgRef'] = 1.121
335340
module_parameters['dEgdT'] = -0.0002677
336-
system = pvsystem.PVSystem(module=module,
337-
module_parameters=module_parameters)
341+
system = pvsystem.PVSystem(module_parameters=module_parameters)
338342
times = pd.DatetimeIndex(start='2015-01-01', periods=2, freq='12H')
339343
poa_data = pd.Series([0, 800], index=times)
340344
temp_cell = 25
@@ -367,26 +371,21 @@ def test_i_from_v():
367371

368372

369373
@requires_scipy
370-
def test_PVSystem_i_from_v(sam_data):
371-
module = 'Example_Module'
372-
module_parameters = sam_data['cecmod'][module]
373-
system = pvsystem.PVSystem(module=module,
374-
module_parameters=module_parameters)
374+
def test_PVSystem_i_from_v():
375+
system = pvsystem.PVSystem()
375376
output = system.i_from_v(20, .1, .5, 40, 6e-7, 7)
376377
assert_allclose(-299.746389916, output, 5)
377378

378379

379380
@requires_scipy
380-
def test_singlediode_series(sam_data):
381-
module = 'Example_Module'
382-
module_parameters = sam_data['cecmod'][module]
381+
def test_singlediode_series(cec_module_params):
383382
times = pd.DatetimeIndex(start='2015-01-01', periods=2, freq='12H')
384383
poa_data = pd.Series([0, 800], index=times)
385384
IL, I0, Rs, Rsh, nNsVth = pvsystem.calcparams_desoto(
386385
poa_data,
387386
temp_cell=25,
388-
alpha_isc=module_parameters['alpha_sc'],
389-
module_parameters=module_parameters,
387+
alpha_isc=cec_module_params['alpha_sc'],
388+
module_parameters=cec_module_params,
390389
EgRef=1.121,
391390
dEgdT=-0.0002677)
392391
out = pvsystem.singlediode(IL, I0, Rs, Rsh, nNsVth)
@@ -424,31 +423,66 @@ def test_singlediode_floats(sam_data):
424423
'p_mp': 38.194165464983037,
425424
'i_x': 6.7556075876880621,
426425
'i_sc': 6.9646747613963198,
427-
'v_mp': 6.221535886625464}
426+
'v_mp': 6.221535886625464,
427+
'i': None,
428+
'v': None}
428429
assert isinstance(out, dict)
429430
for k, v in out.items():
430-
assert_allclose(expected[k], v, atol=3)
431+
if k in ['i', 'v']:
432+
assert v is None
433+
else:
434+
assert_allclose(expected[k], v, atol=3)
431435

432436

433437
@requires_scipy
434-
def test_PVSystem_singlediode_floats(sam_data):
435-
module = 'Example_Module'
436-
module_parameters = sam_data['cecmod'][module]
437-
system = pvsystem.PVSystem(module=module,
438-
module_parameters=module_parameters)
439-
out = system.singlediode(7, 6e-7, .1, 20, .5)
438+
def test_singlediode_floats_ivcurve():
439+
out = pvsystem.singlediode(7, 6e-7, .1, 20, .5, ivcurve_pnts=3)
440440
expected = {'i_xx': 4.2685798754011426,
441441
'i_mp': 6.1390251797935704,
442442
'v_oc': 8.1063001465863085,
443443
'p_mp': 38.194165464983037,
444444
'i_x': 6.7556075876880621,
445445
'i_sc': 6.9646747613963198,
446-
'v_mp': 6.221535886625464}
446+
'v_mp': 6.221535886625464,
447+
'i': np.array([6.965172e+00, 6.755882e+00, 2.575717e-14]),
448+
'v': np.array([0. , 4.05315, 8.1063])}
447449
assert isinstance(out, dict)
448450
for k, v in out.items():
449451
assert_allclose(expected[k], v, atol=3)
450452

451453

454+
@requires_scipy
455+
def test_singlediode_series_ivcurve(cec_module_params):
456+
times = pd.DatetimeIndex(start='2015-01-01', periods=2, freq='12H')
457+
poa_data = pd.Series([0, 800], index=times)
458+
IL, I0, Rs, Rsh, nNsVth = pvsystem.calcparams_desoto(
459+
poa_data,
460+
temp_cell=25,
461+
alpha_isc=cec_module_params['alpha_sc'],
462+
module_parameters=cec_module_params,
463+
EgRef=1.121,
464+
dEgdT=-0.0002677)
465+
466+
out = pvsystem.singlediode(IL, I0, Rs, Rsh, nNsVth, ivcurve_pnts=3)
467+
468+
expected = OrderedDict([('i_sc', array([ nan, 6.00675648])),
469+
('i_mp', array([ nan, 5.6129056])),
470+
('v_oc', array([ nan, 10.29530483])),
471+
('v_mp', array([ nan, 7.25364707])),
472+
('p_mp', array([ nan, 40.71403625])),
473+
('i_x', array([ nan, 5.74622046])),
474+
('i_xx', array([ nan, 4.97138154])),
475+
('i',
476+
array([[ nan, nan, nan],
477+
[ 6.00726296, 5.74622046, 0. ]])),
478+
('v',
479+
array([[ nan, nan, nan],
480+
[ 0. , 5.14765242, 10.29530483]]))])
481+
482+
for k, v in out.items():
483+
assert_allclose(expected[k], v, atol=3)
484+
485+
452486
def test_scale_voltage_current_power(sam_data):
453487
data = pd.DataFrame(
454488
np.array([[2, 1.5, 10, 8, 12, 0.5, 1.5]]),

0 commit comments

Comments
 (0)