@@ -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 \ \ 
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