Skip to content

Commit 544b868

Browse files
adriessewholmgren
authored andcommitted
Add recombination current params to all bishop88 functions (#763)
* Pass through extra recombination parameters to allow use in max_power_point(). * Add recombination current parameters to the other bishop88 functions. * Update whatsnew. * Add tests. * Tests for both newton and brentq, and some formatting. * Delete blank line. * Add scipy requirement triggered by brentq tests. * Add blank line. * docstring edits * redo docstring edits * add parameterize * remove blank lines
1 parent 0a14b27 commit 544b868

File tree

4 files changed

+122
-29
lines changed

4 files changed

+122
-29
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ Enhancements
1414
~~~~~~~~~~~~
1515
* Created two new incidence angle modifier functions: :py:func:`pvlib.pvsystem.iam_martin_ruiz`
1616
and :py:func:`pvlib.pvsystem.iam_interp`. (:issue:`751`)
17+
* Added recombination current parameters to bishop88 single-diode functions and also
18+
to :py:func:`pvlib.pvsystem.max_power_point`. (:issue:`762`)
1719

1820
Bug fixes
1921
~~~~~~~~~

pvlib/pvsystem.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2473,7 +2473,8 @@ def singlediode(photocurrent, saturation_current, resistance_series,
24732473

24742474

24752475
def max_power_point(photocurrent, saturation_current, resistance_series,
2476-
resistance_shunt, nNsVth, method='brentq'):
2476+
resistance_shunt, nNsVth, d2mutau=0, NsVbi=np.Inf,
2477+
method='brentq'):
24772478
"""
24782479
Given the single diode equation coefficients, calculates the maximum power
24792480
point (MPP).
@@ -2491,6 +2492,17 @@ def max_power_point(photocurrent, saturation_current, resistance_series,
24912492
nNsVth : numeric
24922493
product of thermal voltage ``Vth`` [V], diode ideality factor ``n``,
24932494
and number of serices cells ``Ns``
2495+
d2mutau : numeric, default 0
2496+
PVsyst parameter for cadmium-telluride (CdTe) and amorphous-silicon
2497+
(a-Si) modules that accounts for recombination current in the
2498+
intrinsic layer. The value is the ratio of intrinsic layer thickness
2499+
squared :math:`d^2` to the diffusion length of charge carriers
2500+
:math:`\\mu \\tau`. [V]
2501+
NsVbi : numeric, default np.inf
2502+
PVsyst parameter for cadmium-telluride (CdTe) and amorphous-silicon
2503+
(a-Si) modules that is the product of the PV module number of series
2504+
cells ``Ns`` and the builtin voltage ``Vbi`` of the intrinsic layer.
2505+
[V].
24942506
method : str
24952507
either ``'newton'`` or ``'brentq'``
24962508
@@ -2508,7 +2520,8 @@ def max_power_point(photocurrent, saturation_current, resistance_series,
25082520
"""
25092521
i_mp, v_mp, p_mp = _singlediode.bishop88_mpp(
25102522
photocurrent, saturation_current, resistance_series,
2511-
resistance_shunt, nNsVth, method=method.lower()
2523+
resistance_shunt, nNsVth, d2mutau=0, NsVbi=np.Inf,
2524+
method=method.lower()
25122525
)
25132526
if isinstance(photocurrent, pd.Series):
25142527
ivp = {'i_mp': i_mp, 'v_mp': v_mp, 'p_mp': p_mp}

pvlib/singlediode.py

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,17 @@ def bishop88(diode_voltage, photocurrent, saturation_current,
9494
nNsVth : numeric
9595
product of thermal voltage ``Vth`` [V], diode ideality factor ``n``,
9696
and number of series cells ``Ns``
97-
d2mutau : numeric
98-
PVSyst thin-film recombination parameter that is the ratio of thickness
99-
of the intrinsic layer squared :math:`d^2` and the diffusion length of
100-
charge carriers :math:`\\mu \\tau`, in volts [V], defaults to 0[V]
101-
NsVbi : numeric
102-
PVSyst thin-film recombination parameter that is the product of the PV
103-
module number of series cells ``Ns`` and the builtin voltage ``Vbi`` of
104-
the intrinsic layer, in volts [V], defaults to ``np.inf``
97+
d2mutau : numeric, default 0
98+
PVsyst parameter for cadmium-telluride (CdTe) and amorphous-silicon
99+
(a-Si) modules that accounts for recombination current in the
100+
intrinsic layer. The value is the ratio of intrinsic layer thickness
101+
squared :math:`d^2` to the diffusion length of charge carriers
102+
:math:`\\mu \\tau`. [V]
103+
NsVbi : numeric, default np.inf
104+
PVsyst parameter for cadmium-telluride (CdTe) and amorphous-silicon
105+
(a-Si) modules that is the product of the PV module number of series
106+
cells ``Ns`` and the builtin voltage ``Vbi`` of the intrinsic layer.
107+
[V].
105108
gradients : bool
106109
False returns only I, V, and P. True also returns gradients
107110
@@ -116,8 +119,8 @@ def bishop88(diode_voltage, photocurrent, saturation_current,
116119
Notes
117120
-----
118121
The PVSyst thin-film recombination losses parameters ``d2mutau`` and
119-
``NsVbi`` are only applied to cadmium-telluride (CdTe) and amorphous-
120-
silicon (a:Si) PV modules, [2]_, [3]_. The builtin voltage :math:`V_{bi}`
122+
``NsVbi`` should only be applied to cadmium-telluride (CdTe) and amorphous-
123+
silicon (a-Si) PV modules, [2]_, [3]_. The builtin voltage :math:`V_{bi}`
121124
should account for all junctions. For example: tandem and triple junction
122125
cells would have builtin voltages of 1.8[V] and 2.7[V] respectively, based
123126
on the default of 0.9[V] for a single junction. The parameter ``NsVbi``
@@ -173,7 +176,7 @@ def bishop88(diode_voltage, photocurrent, saturation_current,
173176

174177
def bishop88_i_from_v(voltage, photocurrent, saturation_current,
175178
resistance_series, resistance_shunt, nNsVth,
176-
method='newton'):
179+
d2mutau=0, NsVbi=np.Inf, method='newton'):
177180
"""
178181
Find current given any voltage.
179182
@@ -192,6 +195,17 @@ def bishop88_i_from_v(voltage, photocurrent, saturation_current,
192195
nNsVth : numeric
193196
product of diode ideality factor (n), number of series cells (Ns), and
194197
thermal voltage (Vth = k_b * T / q_e) in volts [V]
198+
d2mutau : numeric, default 0
199+
PVsyst parameter for cadmium-telluride (CdTe) and amorphous-silicon
200+
(a-Si) modules that accounts for recombination current in the
201+
intrinsic layer. The value is the ratio of intrinsic layer thickness
202+
squared :math:`d^2` to the diffusion length of charge carriers
203+
:math:`\\mu \\tau`. [V]
204+
NsVbi : numeric, default np.inf
205+
PVsyst parameter for cadmium-telluride (CdTe) and amorphous-silicon
206+
(a-Si) modules that is the product of the PV module number of series
207+
cells ``Ns`` and the builtin voltage ``Vbi`` of the intrinsic layer.
208+
[V].
195209
method : str
196210
one of two optional search methods: either ``'brentq'``, a reliable and
197211
bounded method or ``'newton'`` which is the default.
@@ -203,7 +217,7 @@ def bishop88_i_from_v(voltage, photocurrent, saturation_current,
203217
"""
204218
# collect args
205219
args = (photocurrent, saturation_current, resistance_series,
206-
resistance_shunt, nNsVth)
220+
resistance_shunt, nNsVth, d2mutau, NsVbi)
207221

208222
def fv(x, v, *a):
209223
# calculate voltage residual given diode voltage "x"
@@ -216,8 +230,9 @@ def fv(x, v, *a):
216230
# brentq only works with scalar inputs, so we need a set up function
217231
# and np.vectorize to repeatedly call the optimizer with the right
218232
# arguments for possible array input
219-
def vd_from_brent(voc, v, iph, isat, rs, rsh, gamma):
220-
return brentq(fv, 0.0, voc, args=(v, iph, isat, rs, rsh, gamma))
233+
def vd_from_brent(voc, v, iph, isat, rs, rsh, gamma, d2mutau, NsVbi):
234+
return brentq(fv, 0.0, voc,
235+
args=(v, iph, isat, rs, rsh, gamma, d2mutau, NsVbi))
221236

222237
vd_from_brent_vectorized = np.vectorize(vd_from_brent)
223238
vd = vd_from_brent_vectorized(voc_est, voltage, *args)
@@ -235,7 +250,7 @@ def vd_from_brent(voc, v, iph, isat, rs, rsh, gamma):
235250

236251
def bishop88_v_from_i(current, photocurrent, saturation_current,
237252
resistance_series, resistance_shunt, nNsVth,
238-
method='newton'):
253+
d2mutau=0, NsVbi=np.Inf, method='newton'):
239254
"""
240255
Find voltage given any current.
241256
@@ -254,6 +269,17 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
254269
nNsVth : numeric
255270
product of diode ideality factor (n), number of series cells (Ns), and
256271
thermal voltage (Vth = k_b * T / q_e) in volts [V]
272+
d2mutau : numeric, default 0
273+
PVsyst parameter for cadmium-telluride (CdTe) and amorphous-silicon
274+
(a-Si) modules that accounts for recombination current in the
275+
intrinsic layer. The value is the ratio of intrinsic layer thickness
276+
squared :math:`d^2` to the diffusion length of charge carriers
277+
:math:`\\mu \\tau`. [V]
278+
NsVbi : numeric, default np.inf
279+
PVsyst parameter for cadmium-telluride (CdTe) and amorphous-silicon
280+
(a-Si) modules that is the product of the PV module number of series
281+
cells ``Ns`` and the builtin voltage ``Vbi`` of the intrinsic layer.
282+
[V].
257283
method : str
258284
one of two optional search methods: either ``'brentq'``, a reliable and
259285
bounded method or ``'newton'`` which is the default.
@@ -265,7 +291,7 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
265291
"""
266292
# collect args
267293
args = (photocurrent, saturation_current, resistance_series,
268-
resistance_shunt, nNsVth)
294+
resistance_shunt, nNsVth, d2mutau, NsVbi)
269295
# first bound the search using voc
270296
voc_est = estimate_voc(photocurrent, saturation_current, nNsVth)
271297

@@ -277,8 +303,9 @@ def fi(x, i, *a):
277303
# brentq only works with scalar inputs, so we need a set up function
278304
# and np.vectorize to repeatedly call the optimizer with the right
279305
# arguments for possible array input
280-
def vd_from_brent(voc, i, iph, isat, rs, rsh, gamma):
281-
return brentq(fi, 0.0, voc, args=(i, iph, isat, rs, rsh, gamma))
306+
def vd_from_brent(voc, i, iph, isat, rs, rsh, gamma, d2mutau, NsVbi):
307+
return brentq(fi, 0.0, voc,
308+
args=(i, iph, isat, rs, rsh, gamma, d2mutau, NsVbi))
282309

283310
vd_from_brent_vectorized = np.vectorize(vd_from_brent)
284311
vd = vd_from_brent_vectorized(voc_est, current, *args)
@@ -295,7 +322,8 @@ def vd_from_brent(voc, i, iph, isat, rs, rsh, gamma):
295322

296323

297324
def bishop88_mpp(photocurrent, saturation_current, resistance_series,
298-
resistance_shunt, nNsVth, method='newton'):
325+
resistance_shunt, nNsVth, d2mutau=0, NsVbi=np.Inf,
326+
method='newton'):
299327
"""
300328
Find max power point.
301329
@@ -312,6 +340,17 @@ def bishop88_mpp(photocurrent, saturation_current, resistance_series,
312340
nNsVth : numeric
313341
product of diode ideality factor (n), number of series cells (Ns), and
314342
thermal voltage (Vth = k_b * T / q_e) in volts [V]
343+
d2mutau : numeric, default 0
344+
PVsyst parameter for cadmium-telluride (CdTe) and amorphous-silicon
345+
(a-Si) modules that accounts for recombination current in the
346+
intrinsic layer. The value is the ratio of intrinsic layer thickness
347+
squared :math:`d^2` to the diffusion length of charge carriers
348+
:math:`\\mu \\tau`. [V]
349+
NsVbi : numeric, default np.inf
350+
PVsyst parameter for cadmium-telluride (CdTe) and amorphous-silicon
351+
(a-Si) modules that is the product of the PV module number of series
352+
cells ``Ns`` and the builtin voltage ``Vbi`` of the intrinsic layer.
353+
[V].
315354
method : str
316355
one of two optional search methods: either ``'brentq'``, a reliable and
317356
bounded method or ``'newton'`` which is the default.
@@ -324,7 +363,7 @@ def bishop88_mpp(photocurrent, saturation_current, resistance_series,
324363
"""
325364
# collect args
326365
args = (photocurrent, saturation_current, resistance_series,
327-
resistance_shunt, nNsVth)
366+
resistance_shunt, nNsVth, d2mutau, NsVbi)
328367
# first bound the search using voc
329368
voc_est = estimate_voc(photocurrent, saturation_current, nNsVth)
330369

@@ -334,8 +373,9 @@ def fmpp(x, *a):
334373
if method.lower() == 'brentq':
335374
# break out arguments for numpy.vectorize to handle broadcasting
336375
vec_fun = np.vectorize(
337-
lambda voc, iph, isat, rs, rsh, gamma:
338-
brentq(fmpp, 0.0, voc, args=(iph, isat, rs, rsh, gamma))
376+
lambda voc, iph, isat, rs, rsh, gamma, d2mutau, NsVbi:
377+
brentq(fmpp, 0.0, voc,
378+
args=(iph, isat, rs, rsh, gamma, d2mutau, NsVbi))
339379
)
340380
vd = vec_fun(voc_est, *args)
341381
elif method.lower() == 'newton':

pvlib/test/test_singlediode.py

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
import numpy as np
66
from pvlib import pvsystem
7-
from pvlib.singlediode import bishop88, estimate_voc, VOLTAGE_BUILTIN
7+
from pvlib.singlediode import (bishop88_mpp, estimate_voc, VOLTAGE_BUILTIN,
8+
bishop88, bishop88_i_from_v, bishop88_v_from_i)
89
import pytest
910
from conftest import requires_scipy
1011

@@ -153,9 +154,13 @@ def get_pvsyst_fs_495():
153154
'temp_ref': 25, 'irrad_ref': 1000, 'I_L_ref': 1.5743233463848496
154155
}
155156

157+
# DeSoto @(888[W/m**2], 55[degC]) = {Pmp: 72.71, Isc: 1.402, Voc: 75.42)
156158

159+
160+
@requires_scipy
157161
@pytest.mark.parametrize(
158162
'poa, temp_cell, expected, tol', [
163+
# reference conditions
159164
(
160165
get_pvsyst_fs_495()['irrad_ref'],
161166
get_pvsyst_fs_495()['temp_ref'],
@@ -167,9 +172,21 @@ def get_pvsyst_fs_495():
167172
},
168173
(5e-4, 0.04)
169174
),
170-
(POA, TCELL, {'pmp': 76.26, 'isc': 1.387, 'voc': 79.29}, (1e-3, 1e-3))]
171-
) # DeSoto @(888[W/m**2], 55[degC]) = {Pmp: 72.71, Isc: 1.402, Voc: 75.42)
172-
def test_pvsyst_recombination_loss(poa, temp_cell, expected, tol):
175+
# other conditions
176+
(
177+
POA,
178+
TCELL,
179+
{
180+
'pmp': 76.262,
181+
'isc': 1.3868,
182+
'voc': 79.292
183+
},
184+
(1e-4, 1e-4)
185+
)
186+
]
187+
)
188+
@pytest.mark.parametrize('method', ['newton', 'brentq'])
189+
def test_pvsyst_recombination_loss(method, poa, temp_cell, expected, tol):
173190
"""test PVSst recombination loss"""
174191
pvsyst_fs_495 = get_pvsyst_fs_495()
175192
# first evaluate PVSyst model with thin-film recombination loss current
@@ -199,9 +216,30 @@ def test_pvsyst_recombination_loss(poa, temp_cell, expected, tol):
199216
)
200217
# test max power
201218
assert np.isclose(max(pvsyst[2]), expected['pmp'], *tol)
219+
202220
# test short circuit current
203221
isc_pvsyst = np.interp(0, pvsyst[1], pvsyst[0])
204222
assert np.isclose(isc_pvsyst, expected['isc'], *tol)
205-
# test open circuit current
223+
224+
# test open circuit voltage
206225
voc_pvsyst = np.interp(0, pvsyst[0][::-1], pvsyst[1][::-1])
207226
assert np.isclose(voc_pvsyst, expected['voc'], *tol)
227+
228+
# repeat tests as above with specialized bishop88 functions
229+
y = dict(d2mutau=pvsyst_fs_495['d2mutau'],
230+
NsVbi=VOLTAGE_BUILTIN*pvsyst_fs_495['cells_in_series'])
231+
232+
mpp_88 = bishop88_mpp(*x, **y, method=method)
233+
assert np.isclose(mpp_88[2], expected['pmp'], *tol)
234+
235+
isc_88 = bishop88_i_from_v(0, *x, **y, method=method)
236+
assert np.isclose(isc_88, expected['isc'], *tol)
237+
238+
voc_88 = bishop88_v_from_i(0, *x, **y, method=method)
239+
assert np.isclose(voc_88, expected['voc'], *tol)
240+
241+
ioc_88 = bishop88_i_from_v(voc_88, *x, **y, method=method)
242+
assert np.isclose(ioc_88, 0.0, *tol)
243+
244+
vsc_88 = bishop88_v_from_i(isc_88, *x, **y, method=method)
245+
assert np.isclose(vsc_88, 0.0, *tol)

0 commit comments

Comments
 (0)