@@ -109,13 +109,13 @@ def bishop88(diode_voltage, photocurrent, saturation_current,
109
109
(a-Si) modules that is the product of the PV module number of series
110
110
cells :math:`N_{s}` and the builtin voltage :math:`V_{bi}` of the
111
111
intrinsic layer. [V].
112
- breakdown_factor : float , default 0
112
+ breakdown_factor : numeric , default 0
113
113
fraction of ohmic current involved in avalanche breakdown :math:`a`.
114
114
Default of 0 excludes the reverse bias term from the model. [unitless]
115
- breakdown_voltage : float , default -5.5
115
+ breakdown_voltage : numeric , default -5.5
116
116
reverse breakdown voltage of the photovoltaic junction :math:`V_{br}`
117
117
[V]
118
- breakdown_exp : float , default 3.28
118
+ breakdown_exp : numeric , default 3.28
119
119
avalanche breakdown exponent :math:`m` [unitless]
120
120
gradients : bool
121
121
False returns only I, V, and P. True also returns gradients
@@ -162,12 +162,11 @@ def bishop88(diode_voltage, photocurrent, saturation_current,
162
162
# calculate temporary values to simplify calculations
163
163
v_star = diode_voltage / nNsVth # non-dimensional diode voltage
164
164
g_sh = 1.0 / resistance_shunt # conductance
165
- if breakdown_factor > 0 : # reverse bias is considered
166
- brk_term = 1 - diode_voltage / breakdown_voltage
167
- brk_pwr = np .power (brk_term , - breakdown_exp )
168
- i_breakdown = breakdown_factor * diode_voltage * g_sh * brk_pwr
169
- else :
170
- i_breakdown = 0.
165
+
166
+ brk_term = 1 - diode_voltage / breakdown_voltage
167
+ brk_pwr = np .power (brk_term , - breakdown_exp )
168
+ i_breakdown = breakdown_factor * diode_voltage * g_sh * brk_pwr
169
+
171
170
i = (photocurrent - saturation_current * np .expm1 (v_star ) # noqa: W503
172
171
- diode_voltage * g_sh - i_recomb - i_breakdown ) # noqa: W503
173
172
v = diode_voltage - i * resistance_series
@@ -177,18 +176,14 @@ def bishop88(diode_voltage, photocurrent, saturation_current,
177
176
grad_i_recomb = np .where (is_recomb , i_recomb / v_recomb , 0 )
178
177
grad_2i_recomb = np .where (is_recomb , 2 * grad_i_recomb / v_recomb , 0 )
179
178
g_diode = saturation_current * np .exp (v_star ) / nNsVth # conductance
180
- if breakdown_factor > 0 : # reverse bias is considered
181
- brk_pwr_1 = np .power (brk_term , - breakdown_exp - 1 )
182
- brk_pwr_2 = np .power (brk_term , - breakdown_exp - 2 )
183
- brk_fctr = breakdown_factor * g_sh
184
- grad_i_brk = brk_fctr * (brk_pwr + diode_voltage *
185
- - breakdown_exp * brk_pwr_1 )
186
- grad2i_brk = (brk_fctr * - breakdown_exp # noqa: W503
187
- * (2 * brk_pwr_1 + diode_voltage # noqa: W503
188
- * (- breakdown_exp - 1 ) * brk_pwr_2 )) # noqa: W503
189
- else :
190
- grad_i_brk = 0.
191
- grad2i_brk = 0.
179
+ brk_pwr_1 = np .power (brk_term , - breakdown_exp - 1 )
180
+ brk_pwr_2 = np .power (brk_term , - breakdown_exp - 2 )
181
+ brk_fctr = breakdown_factor * g_sh
182
+ grad_i_brk = brk_fctr * (brk_pwr + diode_voltage *
183
+ - breakdown_exp * brk_pwr_1 )
184
+ grad2i_brk = (brk_fctr * - breakdown_exp # noqa: W503
185
+ * (2 * brk_pwr_1 + diode_voltage # noqa: W503
186
+ * (- breakdown_exp - 1 ) * brk_pwr_2 )) # noqa: W503
192
187
grad_i = - g_diode - g_sh - grad_i_recomb - grad_i_brk # di/dvd
193
188
grad_v = 1.0 - grad_i * resistance_series # dv/dvd
194
189
# dp/dv = d(iv)/dv = v * di/dv + i
@@ -247,12 +242,19 @@ def bishop88_i_from_v(voltage, photocurrent, saturation_current,
247
242
breakdown_exp : float, default 3.28
248
243
avalanche breakdown exponent :math:`m` [unitless]
249
244
method : str, default 'newton'
250
- Either ``'newton'`` or ``'brentq'``. ''method'' must be ``'newton'``
251
- if ``breakdown_factor`` is not 0.
245
+ Either ``'newton'``, ``'brentq'``, or ``'chandrupatla'``.
246
+ ''method'' must be ``'newton'`` if ``breakdown_factor`` is not 0.
247
+
248
+ .. note::
249
+ ``'chandrupatla'`` requires scipy 1.15 or greater.
250
+
252
251
method_kwargs : dict, optional
253
- Keyword arguments passed to root finder method. See
254
- :py:func:`scipy:scipy.optimize.brentq` and
255
- :py:func:`scipy:scipy.optimize.newton` parameters.
252
+ Keyword arguments passed to the root finder. For options, see:
253
+
254
+ * ``method='brentq'``: :py:func:`scipy:scipy.optimize.brentq`
255
+ * ``method='newton'``: :py:func:`scipy:scipy.optimize.newton`
256
+ * ``method='chandrupatla'``: :py:func:`scipy:scipy.optimize.elementwise.find_root`
257
+
256
258
``'full_output': True`` is allowed, and ``optimizer_output`` would be
257
259
returned. See examples section.
258
260
@@ -291,7 +293,7 @@ def bishop88_i_from_v(voltage, photocurrent, saturation_current,
291
293
.. [1] "Computer simulation of the effects of electrical mismatches in
292
294
photovoltaic cell interconnection circuits" JW Bishop, Solar Cell (1988)
293
295
:doi:`10.1016/0379-6787(88)90059-2`
294
- """
296
+ """ # noqa: E501
295
297
# collect args
296
298
args = (photocurrent , saturation_current ,
297
299
resistance_series , resistance_shunt , nNsVth , d2mutau , NsVbi ,
@@ -333,6 +335,30 @@ def vd_from_brent(voc, v, iph, isat, rs, rsh, gamma, d2mutau, NsVbi,
333
335
vd = newton (func = lambda x , * a : fv (x , voltage , * a ), x0 = x0 ,
334
336
fprime = lambda x , * a : bishop88 (x , * a , gradients = True )[4 ],
335
337
args = args , ** method_kwargs )
338
+ elif method == 'chandrupatla' :
339
+ try :
340
+ from scipy .optimize .elementwise import find_root
341
+ except ModuleNotFoundError as e :
342
+ # TODO remove this when our minimum scipy version is >=1.15
343
+ msg = (
344
+ "method='chandrupatla' requires scipy v1.15 or greater "
345
+ "(available for Python 3.10+). "
346
+ "Select another method, or update your version of scipy."
347
+ )
348
+ raise ImportError (msg ) from e
349
+
350
+ voc_est = estimate_voc (photocurrent , saturation_current , nNsVth )
351
+ shape = _shape_of_max_size (voltage , voc_est )
352
+ vlo = np .zeros (shape )
353
+ vhi = np .full (shape , voc_est )
354
+ bounds = (vlo , vhi )
355
+ kwargs_trimmed = method_kwargs .copy ()
356
+ kwargs_trimmed .pop ("full_output" , None ) # not valid for find_root
357
+
358
+ result = find_root (fv , bounds , args = (voltage , * args ), ** kwargs_trimmed )
359
+ vd = result .x
360
+ if method_kwargs .get ('full_output' ):
361
+ vd = (vd , result ) # mimic the other methods
336
362
else :
337
363
raise NotImplementedError ("Method '%s' isn't implemented" % method )
338
364
@@ -388,12 +414,19 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
388
414
breakdown_exp : float, default 3.28
389
415
avalanche breakdown exponent :math:`m` [unitless]
390
416
method : str, default 'newton'
391
- Either ``'newton'`` or ``'brentq'``. ''method'' must be ``'newton'``
392
- if ``breakdown_factor`` is not 0.
417
+ Either ``'newton'``, ``'brentq'``, or ``'chandrupatla'``.
418
+ ''method'' must be ``'newton'`` if ``breakdown_factor`` is not 0.
419
+
420
+ .. note::
421
+ ``'chandrupatla'`` requires scipy 1.15 or greater.
422
+
393
423
method_kwargs : dict, optional
394
- Keyword arguments passed to root finder method. See
395
- :py:func:`scipy:scipy.optimize.brentq` and
396
- :py:func:`scipy:scipy.optimize.newton` parameters.
424
+ Keyword arguments passed to the root finder. For options, see:
425
+
426
+ * ``method='brentq'``: :py:func:`scipy:scipy.optimize.brentq`
427
+ * ``method='newton'``: :py:func:`scipy:scipy.optimize.newton`
428
+ * ``method='chandrupatla'``: :py:func:`scipy:scipy.optimize.elementwise.find_root`
429
+
397
430
``'full_output': True`` is allowed, and ``optimizer_output`` would be
398
431
returned. See examples section.
399
432
@@ -432,7 +465,7 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
432
465
.. [1] "Computer simulation of the effects of electrical mismatches in
433
466
photovoltaic cell interconnection circuits" JW Bishop, Solar Cell (1988)
434
467
:doi:`10.1016/0379-6787(88)90059-2`
435
- """
468
+ """ # noqa: E501
436
469
# collect args
437
470
args = (photocurrent , saturation_current ,
438
471
resistance_series , resistance_shunt , nNsVth , d2mutau , NsVbi ,
@@ -474,6 +507,29 @@ def vd_from_brent(voc, i, iph, isat, rs, rsh, gamma, d2mutau, NsVbi,
474
507
vd = newton (func = lambda x , * a : fi (x , current , * a ), x0 = x0 ,
475
508
fprime = lambda x , * a : bishop88 (x , * a , gradients = True )[3 ],
476
509
args = args , ** method_kwargs )
510
+ elif method == 'chandrupatla' :
511
+ try :
512
+ from scipy .optimize .elementwise import find_root
513
+ except ModuleNotFoundError as e :
514
+ # TODO remove this when our minimum scipy version is >=1.15
515
+ msg = (
516
+ "method='chandrupatla' requires scipy v1.15 or greater "
517
+ "(available for Python 3.10+). "
518
+ "Select another method, or update your version of scipy."
519
+ )
520
+ raise ImportError (msg ) from e
521
+
522
+ shape = _shape_of_max_size (current , voc_est )
523
+ vlo = np .zeros (shape )
524
+ vhi = np .full (shape , voc_est )
525
+ bounds = (vlo , vhi )
526
+ kwargs_trimmed = method_kwargs .copy ()
527
+ kwargs_trimmed .pop ("full_output" , None ) # not valid for find_root
528
+
529
+ result = find_root (fi , bounds , args = (current , * args ), ** kwargs_trimmed )
530
+ vd = result .x
531
+ if method_kwargs .get ('full_output' ):
532
+ vd = (vd , result ) # mimic the other methods
477
533
else :
478
534
raise NotImplementedError ("Method '%s' isn't implemented" % method )
479
535
@@ -526,12 +582,19 @@ def bishop88_mpp(photocurrent, saturation_current, resistance_series,
526
582
breakdown_exp : numeric, default 3.28
527
583
avalanche breakdown exponent :math:`m` [unitless]
528
584
method : str, default 'newton'
529
- Either ``'newton'`` or ``'brentq'``. ''method'' must be ``'newton'``
530
- if ``breakdown_factor`` is not 0.
585
+ Either ``'newton'``, ``'brentq'``, or ``'chandrupatla'``.
586
+ ''method'' must be ``'newton'`` if ``breakdown_factor`` is not 0.
587
+
588
+ .. note::
589
+ ``'chandrupatla'`` requires scipy 1.15 or greater.
590
+
531
591
method_kwargs : dict, optional
532
- Keyword arguments passed to root finder method. See
533
- :py:func:`scipy:scipy.optimize.brentq` and
534
- :py:func:`scipy:scipy.optimize.newton` parameters.
592
+ Keyword arguments passed to the root finder. For options, see:
593
+
594
+ * ``method='brentq'``: :py:func:`scipy:scipy.optimize.brentq`
595
+ * ``method='newton'``: :py:func:`scipy:scipy.optimize.newton`
596
+ * ``method='chandrupatla'``: :py:func:`scipy:scipy.optimize.elementwise.find_root`
597
+
535
598
``'full_output': True`` is allowed, and ``optimizer_output`` would be
536
599
returned. See examples section.
537
600
@@ -571,7 +634,7 @@ def bishop88_mpp(photocurrent, saturation_current, resistance_series,
571
634
.. [1] "Computer simulation of the effects of electrical mismatches in
572
635
photovoltaic cell interconnection circuits" JW Bishop, Solar Cell (1988)
573
636
:doi:`10.1016/0379-6787(88)90059-2`
574
- """
637
+ """ # noqa: E501
575
638
# collect args
576
639
args = (photocurrent , saturation_current ,
577
640
resistance_series , resistance_shunt , nNsVth , d2mutau , NsVbi ,
@@ -611,6 +674,31 @@ def fmpp(x, *a):
611
674
vd = newton (func = fmpp , x0 = x0 ,
612
675
fprime = lambda x , * a : bishop88 (x , * a , gradients = True )[7 ],
613
676
args = args , ** method_kwargs )
677
+ elif method == 'chandrupatla' :
678
+ try :
679
+ from scipy .optimize .elementwise import find_root
680
+ except ModuleNotFoundError as e :
681
+ # TODO remove this when our minimum scipy version is >=1.15
682
+ msg = (
683
+ "method='chandrupatla' requires scipy v1.15 or greater "
684
+ "(available for Python 3.10+). "
685
+ "Select another method, or update your version of scipy."
686
+ )
687
+ raise ImportError (msg ) from e
688
+
689
+ vlo = np .zeros_like (photocurrent )
690
+ vhi = np .full_like (photocurrent , voc_est )
691
+ kwargs_trimmed = method_kwargs .copy ()
692
+ kwargs_trimmed .pop ("full_output" , None ) # not valid for find_root
693
+
694
+ result = find_root (fmpp ,
695
+ (vlo , vhi ),
696
+ args = args ,
697
+ ** kwargs_trimmed )
698
+ vd = result .x
699
+ if method_kwargs .get ('full_output' ):
700
+ vd = (vd , result ) # mimic the other methods
701
+
614
702
else :
615
703
raise NotImplementedError ("Method '%s' isn't implemented" % method )
616
704
0 commit comments