@@ -69,42 +69,65 @@ def estimate_voc(photocurrent, saturation_current, nNsVth):
6969
7070def bishop88 (diode_voltage , photocurrent , saturation_current ,
7171 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"""
7475 Explicit calculation of points on the IV curve described by the single
7576 diode equation. Values are calculated as described in [1]_.
7677
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+
7791 .. warning::
92+ * Usage of ``d2mutau`` is required with PVSyst
93+ coefficients for cadmium-telluride (CdTe) and amorphous-silicon
94+ (a:Si) PV modules only.
7895 * 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.
8196
8297 Parameters
8398 ----------
8499 diode_voltage : numeric
85100 diode voltages [V]
86101 photocurrent : numeric
87- photo-generated current [A]
102+ photo-generated current :math:`I_{L}` [A]
88103 saturation_current : numeric
89- diode reverse saturation current [A]
104+ diode reverse saturation current :math:`I_{0}` [A]
90105 resistance_series : numeric
91- series resistance [ohms]
106+ series resistance :math:`R_{s}` [ohms]
92107 resistance_shunt: numeric
93- shunt resistance [ohms]
108+ shunt resistance :math:`R_{sh}` [ohms]
94109 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} `
97112 d2mutau : numeric, default 0
98113 PVsyst parameter for cadmium-telluride (CdTe) and amorphous-silicon
99114 (a-Si) modules that accounts for recombination current in the
100115 intrinsic layer. The value is the ratio of intrinsic layer thickness
101116 squared :math:`d^2` to the diffusion length of charge carriers
102- :math:`\\ mu \ \ tau`. [V]
117+ :math:`\mu \tau`. [V]
103118 NsVbi : numeric, default np.inf
104119 PVsyst parameter for cadmium-telluride (CdTe) and amorphous-silicon
105120 (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]
108131 gradients : bool
109132 False returns only I, V, and P. True also returns gradients
110133
@@ -150,21 +173,39 @@ def bishop88(diode_voltage, photocurrent, saturation_current,
150173 # calculate temporary values to simplify calculations
151174 v_star = diode_voltage / nNsVth # non-dimensional diode voltage
152175 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
155184 v = diode_voltage - i * resistance_series
156185 retval = (i , v , i * v )
157186 if gradients :
158187 # calculate recombination loss current gradients where d2mutau > 0
159188 grad_i_recomb = np .where (is_recomb , i_recomb / v_recomb , 0 )
160189 grad_2i_recomb = np .where (is_recomb , 2 * grad_i_recomb / v_recomb , 0 )
161190 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
163204 grad_v = 1.0 - grad_i * resistance_series # dv/dvd
164205 # dp/dv = d(iv)/dv = v * di/dv + i
165206 grad = grad_i / grad_v # di/dv
166207 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
168209 grad2v = - grad2i * resistance_series # d2v/dvd
169210 grad2p = (
170211 grad_v * grad + v * (grad2i / grad_v - grad_i * grad2v / grad_v ** 2 )
@@ -176,7 +217,9 @@ def bishop88(diode_voltage, photocurrent, saturation_current,
176217
177218def bishop88_i_from_v (voltage , photocurrent , saturation_current ,
178219 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' ):
180223 """
181224 Find current given any voltage.
182225
@@ -185,13 +228,13 @@ def bishop88_i_from_v(voltage, photocurrent, saturation_current,
185228 voltage : numeric
186229 voltage (V) in volts [V]
187230 photocurrent : numeric
188- photogenerated current (Iph or IL) in amperes [A]
231+ photogenerated current (Iph or IL) [A]
189232 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]
191234 resistance_series : numeric
192- series resistance (Rs) in ohms
235+ series resistance (Rs) in [Ohm]
193236 resistance_shunt : numeric
194- shunt resistance (Rsh) in ohms
237+ shunt resistance (Rsh) [Ohm]
195238 nNsVth : numeric
196239 product of diode ideality factor (n), number of series cells (Ns), and
197240 thermal voltage (Vth = k_b * T / q_e) in volts [V]
@@ -206,18 +249,27 @@ def bishop88_i_from_v(voltage, photocurrent, saturation_current,
206249 (a-Si) modules that is the product of the PV module number of series
207250 cells ``Ns`` and the builtin voltage ``Vbi`` of the intrinsic layer.
208251 [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.
212263
213264 Returns
214265 -------
215266 current : numeric
216- current (I) at the specified voltage (V) in amperes [A]
267+ current (I) at the specified voltage (V). [A]
217268 """
218269 # collect args
219270 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 )
221273
222274 def fv (x , v , * a ):
223275 # calculate voltage residual given diode voltage "x"
@@ -230,9 +282,12 @@ def fv(x, v, *a):
230282 # brentq only works with scalar inputs, so we need a set up function
231283 # and np.vectorize to repeatedly call the optimizer with the right
232284 # 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 ):
234287 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 ))
236291
237292 vd_from_brent_vectorized = np .vectorize (vd_from_brent )
238293 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):
250305
251306def bishop88_v_from_i (current , photocurrent , saturation_current ,
252307 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' ):
254311 """
255312 Find voltage given any current.
256313
@@ -259,13 +316,13 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
259316 current : numeric
260317 current (I) in amperes [A]
261318 photocurrent : numeric
262- photogenerated current (Iph or IL) in amperes [A]
319+ photogenerated current (Iph or IL) [A]
263320 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]
265322 resistance_series : numeric
266- series resistance (Rs) in ohms
323+ series resistance (Rs) in [Ohm]
267324 resistance_shunt : numeric
268- shunt resistance (Rsh) in ohms
325+ shunt resistance (Rsh) [Ohm]
269326 nNsVth : numeric
270327 product of diode ideality factor (n), number of series cells (Ns), and
271328 thermal voltage (Vth = k_b * T / q_e) in volts [V]
@@ -280,9 +337,17 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
280337 (a-Si) modules that is the product of the PV module number of series
281338 cells ``Ns`` and the builtin voltage ``Vbi`` of the intrinsic layer.
282339 [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.
286351
287352 Returns
288353 -------
@@ -291,7 +356,8 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
291356 """
292357 # collect args
293358 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 )
295361 # first bound the search using voc
296362 voc_est = estimate_voc (photocurrent , saturation_current , nNsVth )
297363
@@ -303,9 +369,12 @@ def fi(x, i, *a):
303369 # brentq only works with scalar inputs, so we need a set up function
304370 # and np.vectorize to repeatedly call the optimizer with the right
305371 # 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 ):
307374 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 ))
309378
310379 vd_from_brent_vectorized = np .vectorize (vd_from_brent )
311380 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):
323392
324393def bishop88_mpp (photocurrent , saturation_current , resistance_series ,
325394 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' ):
327397 """
328398 Find max power point.
329399
330400 Parameters
331401 ----------
332402 photocurrent : numeric
333- photogenerated current (Iph or IL) in amperes [A]
403+ photogenerated current (Iph or IL) [A]
334404 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]
336406 resistance_series : numeric
337- series resistance (Rs) in ohms
407+ series resistance (Rs) in [Ohm]
338408 resistance_shunt : numeric
339- shunt resistance (Rsh) in ohms
409+ shunt resistance (Rsh) [Ohm]
340410 nNsVth : numeric
341411 product of diode ideality factor (n), number of series cells (Ns), and
342412 thermal voltage (Vth = k_b * T / q_e) in volts [V]
@@ -351,9 +421,17 @@ def bishop88_mpp(photocurrent, saturation_current, resistance_series,
351421 (a-Si) modules that is the product of the PV module number of series
352422 cells ``Ns`` and the builtin voltage ``Vbi`` of the intrinsic layer.
353423 [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.
357435
358436 Returns
359437 -------
@@ -363,7 +441,8 @@ def bishop88_mpp(photocurrent, saturation_current, resistance_series,
363441 """
364442 # collect args
365443 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 )
367446 # first bound the search using voc
368447 voc_est = estimate_voc (photocurrent , saturation_current , nNsVth )
369448
@@ -373,9 +452,10 @@ def fmpp(x, *a):
373452 if method .lower () == 'brentq' :
374453 # break out arguments for numpy.vectorize to handle broadcasting
375454 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 ))
379459 )
380460 vd = vec_fun (voc_est , * args )
381461 elif method .lower () == 'newton' :
0 commit comments