@@ -69,42 +69,65 @@ def estimate_voc(photocurrent, saturation_current, nNsVth):
69
69
70
70
def bishop88 (diode_voltage , photocurrent , saturation_current ,
71
71
resistance_series , resistance_shunt , nNsVth , d2mutau = 0 ,
72
- NsVbi = np .Inf , gradients = False ):
73
- """
72
+ NsVbi = np .Inf , breakdown_factor = 0. , breakdown_voltage = - 5.5 ,
73
+ breakdown_exp = 3.28 , gradients = False ):
74
+ r"""
74
75
Explicit calculation of points on the IV curve described by the single
75
76
diode equation. Values are calculated as described in [1]_.
76
77
78
+ The single diode equation with recombination current and reverse bias
79
+ breakdown is
80
+
81
+ .. math::
82
+
83
+ I = I_{L} - I_{0} (\exp \frac{V_{d}}{nNsVth} - 1)
84
+ - \frac{V_{d}}{R_{sh}}
85
+ - \frac{I_{L} \frac{d^{2}}{\mu \tau}{N_{s} V_{bi} - V_{d}}
86
+ - a \frac{V_{d}{R_{sh}} (1 - \frac{V_{d}}{V_{br}})^-m
87
+
88
+ The input `diode_voltage` must be :math:`V + I R_{s}`.
89
+
90
+
77
91
.. warning::
92
+ * Usage of ``d2mutau`` is required with PVSyst
93
+ coefficients for cadmium-telluride (CdTe) and amorphous-silicon
94
+ (a:Si) PV modules only.
78
95
* Do not use ``d2mutau`` with CEC coefficients.
79
- * Usage of ``d2mutau`` with PVSyst coefficients is required for cadmium-
80
- telluride (CdTe) and amorphous-silicon (a:Si) PV modules only.
81
96
82
97
Parameters
83
98
----------
84
99
diode_voltage : numeric
85
100
diode voltages [V]
86
101
photocurrent : numeric
87
- photo-generated current [A]
102
+ photo-generated current :math:`I_{L}` [A]
88
103
saturation_current : numeric
89
- diode reverse saturation current [A]
104
+ diode reverse saturation current :math:`I_{0}` [A]
90
105
resistance_series : numeric
91
- series resistance [ohms]
106
+ series resistance :math:`R_{s}` [ohms]
92
107
resistance_shunt: numeric
93
- shunt resistance [ohms]
108
+ shunt resistance :math:`R_{sh}` [ohms]
94
109
nNsVth : numeric
95
- product of thermal voltage ``Vth`` [V], diode ideality factor ``n``,
96
- and number of series cells ``Ns` `
110
+ product of thermal voltage :math:`V_{th}` [V], diode ideality factor
111
+ ``n``, and number of series cells :math:`N_{s} `
97
112
d2mutau : numeric, default 0
98
113
PVsyst parameter for cadmium-telluride (CdTe) and amorphous-silicon
99
114
(a-Si) modules that accounts for recombination current in the
100
115
intrinsic layer. The value is the ratio of intrinsic layer thickness
101
116
squared :math:`d^2` to the diffusion length of charge carriers
102
- :math:`\\ mu \ \ tau`. [V]
117
+ :math:`\mu \tau`. [V]
103
118
NsVbi : numeric, default np.inf
104
119
PVsyst parameter for cadmium-telluride (CdTe) and amorphous-silicon
105
120
(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].
121
+ cells :math:`N_{s}` and the builtin voltage :math:`V_{bi}` of the
122
+ intrinsic layer. [V].
123
+ breakdown_factor : numeric, default 0
124
+ fraction of ohmic current involved in avalanche breakdown :math:`a`.
125
+ Default of 0 excludes the reverse bias term from the model. [unitless]
126
+ breakdown_voltage : numeric, default -5.5
127
+ reverse breakdown voltage of the photovoltaic junction :math:`V_{br}`
128
+ [V]
129
+ breakdown_exp : numeric, default 3.28
130
+ avalanche breakdown exponent :math:`m` [unitless]
108
131
gradients : bool
109
132
False returns only I, V, and P. True also returns gradients
110
133
@@ -150,21 +173,39 @@ def bishop88(diode_voltage, photocurrent, saturation_current,
150
173
# calculate temporary values to simplify calculations
151
174
v_star = diode_voltage / nNsVth # non-dimensional diode voltage
152
175
g_sh = 1.0 / resistance_shunt # conductance
153
- i = (photocurrent - saturation_current * np .expm1 (v_star )
154
- - diode_voltage * g_sh - i_recomb )
176
+ if breakdown_factor > 0 : # reverse bias is considered
177
+ brk_term = 1 - diode_voltage / breakdown_voltage
178
+ brk_pwr = np .power (brk_term , - breakdown_exp )
179
+ i_breakdown = breakdown_factor * diode_voltage * g_sh * brk_pwr
180
+ else :
181
+ i_breakdown = 0.
182
+ i = (photocurrent - saturation_current * np .expm1 (v_star ) # noqa: W503
183
+ - diode_voltage * g_sh - i_recomb - i_breakdown ) # noqa: W503
155
184
v = diode_voltage - i * resistance_series
156
185
retval = (i , v , i * v )
157
186
if gradients :
158
187
# calculate recombination loss current gradients where d2mutau > 0
159
188
grad_i_recomb = np .where (is_recomb , i_recomb / v_recomb , 0 )
160
189
grad_2i_recomb = np .where (is_recomb , 2 * grad_i_recomb / v_recomb , 0 )
161
190
g_diode = saturation_current * np .exp (v_star ) / nNsVth # conductance
162
- grad_i = - g_diode - g_sh - grad_i_recomb # di/dvd
191
+ if breakdown_factor > 0 : # reverse bias is considered
192
+ brk_pwr_1 = np .power (brk_term , - breakdown_exp - 1 )
193
+ brk_pwr_2 = np .power (brk_term , - breakdown_exp - 2 )
194
+ brk_fctr = breakdown_factor * g_sh
195
+ grad_i_brk = brk_fctr * (brk_pwr + diode_voltage *
196
+ - breakdown_exp * brk_pwr_1 )
197
+ grad2i_brk = (brk_fctr * - breakdown_exp # noqa: W503
198
+ * (2 * brk_pwr_1 + diode_voltage # noqa: W503
199
+ * (- breakdown_exp - 1 ) * brk_pwr_2 )) # noqa: W503
200
+ else :
201
+ grad_i_brk = 0.
202
+ grad2i_brk = 0.
203
+ grad_i = - g_diode - g_sh - grad_i_recomb - grad_i_brk # di/dvd
163
204
grad_v = 1.0 - grad_i * resistance_series # dv/dvd
164
205
# dp/dv = d(iv)/dv = v * di/dv + i
165
206
grad = grad_i / grad_v # di/dv
166
207
grad_p = v * grad + i # dp/dv
167
- grad2i = - g_diode / nNsVth - grad_2i_recomb # d2i/dvd
208
+ grad2i = - g_diode / nNsVth - grad_2i_recomb - grad2i_brk # d2i/dvd
168
209
grad2v = - grad2i * resistance_series # d2v/dvd
169
210
grad2p = (
170
211
grad_v * grad + v * (grad2i / grad_v - grad_i * grad2v / grad_v ** 2 )
@@ -176,7 +217,9 @@ def bishop88(diode_voltage, photocurrent, saturation_current,
176
217
177
218
def bishop88_i_from_v (voltage , photocurrent , saturation_current ,
178
219
resistance_series , resistance_shunt , nNsVth ,
179
- d2mutau = 0 , NsVbi = np .Inf , method = 'newton' ):
220
+ d2mutau = 0 , NsVbi = np .Inf , breakdown_factor = 0. ,
221
+ breakdown_voltage = - 5.5 , breakdown_exp = 3.28 ,
222
+ method = 'newton' ):
180
223
"""
181
224
Find current given any voltage.
182
225
@@ -185,13 +228,13 @@ def bishop88_i_from_v(voltage, photocurrent, saturation_current,
185
228
voltage : numeric
186
229
voltage (V) in volts [V]
187
230
photocurrent : numeric
188
- photogenerated current (Iph or IL) in amperes [A]
231
+ photogenerated current (Iph or IL) [A]
189
232
saturation_current : numeric
190
- diode dark or saturation current (Io or Isat) in amperes [A]
233
+ diode dark or saturation current (Io or Isat) [A]
191
234
resistance_series : numeric
192
- series resistance (Rs) in ohms
235
+ series resistance (Rs) in [Ohm]
193
236
resistance_shunt : numeric
194
- shunt resistance (Rsh) in ohms
237
+ shunt resistance (Rsh) [Ohm]
195
238
nNsVth : numeric
196
239
product of diode ideality factor (n), number of series cells (Ns), and
197
240
thermal voltage (Vth = k_b * T / q_e) in volts [V]
@@ -206,18 +249,27 @@ def bishop88_i_from_v(voltage, photocurrent, saturation_current,
206
249
(a-Si) modules that is the product of the PV module number of series
207
250
cells ``Ns`` and the builtin voltage ``Vbi`` of the intrinsic layer.
208
251
[V].
209
- method : str
210
- one of two optional search methods: either ``'brentq'``, a reliable and
211
- bounded method or ``'newton'`` which is the default.
252
+ breakdown_factor : numeric, default 0
253
+ fraction of ohmic current involved in avalanche breakdown :math:`a`.
254
+ Default of 0 excludes the reverse bias term from the model. [unitless]
255
+ breakdown_voltage : numeric, default -5.5
256
+ reverse breakdown voltage of the photovoltaic junction :math:`V_{br}`
257
+ [V]
258
+ breakdown_exp : numeric, default 3.28
259
+ avalanche breakdown exponent :math:`m` [unitless]
260
+ method : str, default 'newton'
261
+ Either ``'newton'`` or ``'brentq'``. ''method'' must be ``'newton'``
262
+ if ``breakdown_factor`` is not 0.
212
263
213
264
Returns
214
265
-------
215
266
current : numeric
216
- current (I) at the specified voltage (V) in amperes [A]
267
+ current (I) at the specified voltage (V). [A]
217
268
"""
218
269
# collect args
219
270
args = (photocurrent , saturation_current , resistance_series ,
220
- resistance_shunt , nNsVth , d2mutau , NsVbi )
271
+ resistance_shunt , nNsVth , d2mutau , NsVbi ,
272
+ breakdown_factor , breakdown_voltage , breakdown_exp )
221
273
222
274
def fv (x , v , * a ):
223
275
# calculate voltage residual given diode voltage "x"
@@ -230,9 +282,12 @@ def fv(x, v, *a):
230
282
# brentq only works with scalar inputs, so we need a set up function
231
283
# and np.vectorize to repeatedly call the optimizer with the right
232
284
# arguments for possible array input
233
- def vd_from_brent (voc , v , iph , isat , rs , rsh , gamma , d2mutau , NsVbi ):
285
+ def vd_from_brent (voc , v , iph , isat , rs , rsh , gamma , d2mutau , NsVbi ,
286
+ breakdown_factor , breakdown_voltage , breakdown_exp ):
234
287
return brentq (fv , 0.0 , voc ,
235
- args = (v , iph , isat , rs , rsh , gamma , d2mutau , NsVbi ))
288
+ args = (v , iph , isat , rs , rsh , gamma , d2mutau , NsVbi ,
289
+ breakdown_factor , breakdown_voltage ,
290
+ breakdown_exp ))
236
291
237
292
vd_from_brent_vectorized = np .vectorize (vd_from_brent )
238
293
vd = vd_from_brent_vectorized (voc_est , voltage , * args )
@@ -250,7 +305,9 @@ def vd_from_brent(voc, v, iph, isat, rs, rsh, gamma, d2mutau, NsVbi):
250
305
251
306
def bishop88_v_from_i (current , photocurrent , saturation_current ,
252
307
resistance_series , resistance_shunt , nNsVth ,
253
- d2mutau = 0 , NsVbi = np .Inf , method = 'newton' ):
308
+ d2mutau = 0 , NsVbi = np .Inf , breakdown_factor = 0. ,
309
+ breakdown_voltage = - 5.5 , breakdown_exp = 3.28 ,
310
+ method = 'newton' ):
254
311
"""
255
312
Find voltage given any current.
256
313
@@ -259,13 +316,13 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
259
316
current : numeric
260
317
current (I) in amperes [A]
261
318
photocurrent : numeric
262
- photogenerated current (Iph or IL) in amperes [A]
319
+ photogenerated current (Iph or IL) [A]
263
320
saturation_current : numeric
264
- diode dark or saturation current (Io or Isat) in amperes [A]
321
+ diode dark or saturation current (Io or Isat) [A]
265
322
resistance_series : numeric
266
- series resistance (Rs) in ohms
323
+ series resistance (Rs) in [Ohm]
267
324
resistance_shunt : numeric
268
- shunt resistance (Rsh) in ohms
325
+ shunt resistance (Rsh) [Ohm]
269
326
nNsVth : numeric
270
327
product of diode ideality factor (n), number of series cells (Ns), and
271
328
thermal voltage (Vth = k_b * T / q_e) in volts [V]
@@ -280,9 +337,17 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
280
337
(a-Si) modules that is the product of the PV module number of series
281
338
cells ``Ns`` and the builtin voltage ``Vbi`` of the intrinsic layer.
282
339
[V].
283
- method : str
284
- one of two optional search methods: either ``'brentq'``, a reliable and
285
- bounded method or ``'newton'`` which is the default.
340
+ breakdown_factor : numeric, default 0
341
+ fraction of ohmic current involved in avalanche breakdown :math:`a`.
342
+ Default of 0 excludes the reverse bias term from the model. [unitless]
343
+ breakdown_voltage : numeric, default -5.5
344
+ reverse breakdown voltage of the photovoltaic junction :math:`V_{br}`
345
+ [V]
346
+ breakdown_exp : numeric, default 3.28
347
+ avalanche breakdown exponent :math:`m` [unitless]
348
+ method : str, default 'newton'
349
+ Either ``'newton'`` or ``'brentq'``. ''method'' must be ``'newton'``
350
+ if ``breakdown_factor`` is not 0.
286
351
287
352
Returns
288
353
-------
@@ -291,7 +356,8 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
291
356
"""
292
357
# collect args
293
358
args = (photocurrent , saturation_current , resistance_series ,
294
- resistance_shunt , nNsVth , d2mutau , NsVbi )
359
+ resistance_shunt , nNsVth , d2mutau , NsVbi , breakdown_factor ,
360
+ breakdown_voltage , breakdown_exp )
295
361
# first bound the search using voc
296
362
voc_est = estimate_voc (photocurrent , saturation_current , nNsVth )
297
363
@@ -303,9 +369,12 @@ def fi(x, i, *a):
303
369
# brentq only works with scalar inputs, so we need a set up function
304
370
# and np.vectorize to repeatedly call the optimizer with the right
305
371
# arguments for possible array input
306
- def vd_from_brent (voc , i , iph , isat , rs , rsh , gamma , d2mutau , NsVbi ):
372
+ def vd_from_brent (voc , i , iph , isat , rs , rsh , gamma , d2mutau , NsVbi ,
373
+ breakdown_factor , breakdown_voltage , breakdown_exp ):
307
374
return brentq (fi , 0.0 , voc ,
308
- args = (i , iph , isat , rs , rsh , gamma , d2mutau , NsVbi ))
375
+ args = (i , iph , isat , rs , rsh , gamma , d2mutau , NsVbi ,
376
+ breakdown_factor , breakdown_voltage ,
377
+ breakdown_exp ))
309
378
310
379
vd_from_brent_vectorized = np .vectorize (vd_from_brent )
311
380
vd = vd_from_brent_vectorized (voc_est , current , * args )
@@ -323,20 +392,21 @@ def vd_from_brent(voc, i, iph, isat, rs, rsh, gamma, d2mutau, NsVbi):
323
392
324
393
def bishop88_mpp (photocurrent , saturation_current , resistance_series ,
325
394
resistance_shunt , nNsVth , d2mutau = 0 , NsVbi = np .Inf ,
326
- method = 'newton' ):
395
+ breakdown_factor = 0. , breakdown_voltage = - 5.5 ,
396
+ breakdown_exp = 3.28 , method = 'newton' ):
327
397
"""
328
398
Find max power point.
329
399
330
400
Parameters
331
401
----------
332
402
photocurrent : numeric
333
- photogenerated current (Iph or IL) in amperes [A]
403
+ photogenerated current (Iph or IL) [A]
334
404
saturation_current : numeric
335
- diode dark or saturation current (Io or Isat) in amperes [A]
405
+ diode dark or saturation current (Io or Isat) [A]
336
406
resistance_series : numeric
337
- series resistance (Rs) in ohms
407
+ series resistance (Rs) in [Ohm]
338
408
resistance_shunt : numeric
339
- shunt resistance (Rsh) in ohms
409
+ shunt resistance (Rsh) [Ohm]
340
410
nNsVth : numeric
341
411
product of diode ideality factor (n), number of series cells (Ns), and
342
412
thermal voltage (Vth = k_b * T / q_e) in volts [V]
@@ -351,9 +421,17 @@ def bishop88_mpp(photocurrent, saturation_current, resistance_series,
351
421
(a-Si) modules that is the product of the PV module number of series
352
422
cells ``Ns`` and the builtin voltage ``Vbi`` of the intrinsic layer.
353
423
[V].
354
- method : str
355
- one of two optional search methods: either ``'brentq'``, a reliable and
356
- bounded method or ``'newton'`` which is the default.
424
+ breakdown_factor : numeric, default 0
425
+ fraction of ohmic current involved in avalanche breakdown :math:`a`.
426
+ Default of 0 excludes the reverse bias term from the model. [unitless]
427
+ breakdown_voltage : numeric, default -5.5
428
+ reverse breakdown voltage of the photovoltaic junction :math:`V_{br}`
429
+ [V]
430
+ breakdown_exp : numeric, default 3.28
431
+ avalanche breakdown exponent :math:`m` [unitless]
432
+ method : str, default 'newton'
433
+ Either ``'newton'`` or ``'brentq'``. ''method'' must be ``'newton'``
434
+ if ``breakdown_factor`` is not 0.
357
435
358
436
Returns
359
437
-------
@@ -363,7 +441,8 @@ def bishop88_mpp(photocurrent, saturation_current, resistance_series,
363
441
"""
364
442
# collect args
365
443
args = (photocurrent , saturation_current , resistance_series ,
366
- resistance_shunt , nNsVth , d2mutau , NsVbi )
444
+ resistance_shunt , nNsVth , d2mutau , NsVbi , breakdown_factor ,
445
+ breakdown_voltage , breakdown_exp )
367
446
# first bound the search using voc
368
447
voc_est = estimate_voc (photocurrent , saturation_current , nNsVth )
369
448
@@ -373,9 +452,10 @@ def fmpp(x, *a):
373
452
if method .lower () == 'brentq' :
374
453
# break out arguments for numpy.vectorize to handle broadcasting
375
454
vec_fun = np .vectorize (
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 ))
455
+ lambda voc , iph , isat , rs , rsh , gamma , d2mutau , NsVbi , vbr_a , vbr ,
456
+ vbr_exp : brentq (fmpp , 0.0 , voc ,
457
+ args = (iph , isat , rs , rsh , gamma , d2mutau , NsVbi ,
458
+ vbr_a , vbr , vbr_exp ))
379
459
)
380
460
vd = vec_fun (voc_est , * args )
381
461
elif method .lower () == 'newton' :
0 commit comments