@@ -287,8 +287,8 @@ def bishop88_i_from_v(voltage, photocurrent, saturation_current,
287
287
... method_kwargs={'full_output': True})
288
288
"""
289
289
# collect args
290
- args = (photocurrent , saturation_current , resistance_series ,
291
- resistance_shunt , nNsVth , d2mutau , NsVbi ,
290
+ args = (photocurrent , saturation_current ,
291
+ resistance_series , resistance_shunt , nNsVth , d2mutau , NsVbi ,
292
292
breakdown_factor , breakdown_voltage , breakdown_exp )
293
293
method = method .lower ()
294
294
@@ -319,14 +319,11 @@ def vd_from_brent(voc, v, iph, isat, rs, rsh, gamma, d2mutau, NsVbi,
319
319
vd_from_brent_vectorized = np .vectorize (vd_from_brent )
320
320
vd = vd_from_brent_vectorized (voc_est , voltage , * args )
321
321
elif method == 'newton' :
322
- # make sure all args are numpy arrays if max size > 1
323
- # if voltage is an array, then make a copy to use for initial guess, v0
324
- args , v0 , method_kwargs = \
325
- _prepare_newton_inputs ((voltage ,), args , voltage , method_kwargs )
326
- vd = newton (func = lambda x , * a : fv (x , voltage , * a ), x0 = v0 ,
322
+ x0 , (voltage , * args ), method_kwargs = \
323
+ _prepare_newton_inputs (voltage , (voltage , * args ), method_kwargs )
324
+ vd = newton (func = lambda x , * a : fv (x , voltage , * a ), x0 = x0 ,
327
325
fprime = lambda x , * a : bishop88 (x , * a , gradients = True )[4 ],
328
- args = args ,
329
- ** method_kwargs )
326
+ args = args , ** method_kwargs )
330
327
else :
331
328
raise NotImplementedError ("Method '%s' isn't implemented" % method )
332
329
@@ -422,9 +419,9 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
422
419
... method_kwargs={'full_output': True})
423
420
"""
424
421
# collect args
425
- args = (photocurrent , saturation_current , resistance_series ,
426
- resistance_shunt , nNsVth , d2mutau , NsVbi , breakdown_factor ,
427
- breakdown_voltage , breakdown_exp )
422
+ args = (photocurrent , saturation_current ,
423
+ resistance_series , resistance_shunt , nNsVth , d2mutau , NsVbi ,
424
+ breakdown_factor , breakdown_voltage , breakdown_exp )
428
425
method = method .lower ()
429
426
430
427
# method_kwargs create dict if not provided
@@ -454,14 +451,11 @@ def vd_from_brent(voc, i, iph, isat, rs, rsh, gamma, d2mutau, NsVbi,
454
451
vd_from_brent_vectorized = np .vectorize (vd_from_brent )
455
452
vd = vd_from_brent_vectorized (voc_est , current , * args )
456
453
elif method == 'newton' :
457
- # make sure all args are numpy arrays if max size > 1
458
- # if voc_est is an array, then make a copy to use for initial guess, v0
459
- args , v0 , method_kwargs = \
460
- _prepare_newton_inputs ((current ,), args , voc_est , method_kwargs )
461
- vd = newton (func = lambda x , * a : fi (x , current , * a ), x0 = v0 ,
454
+ x0 , (current , * args ), method_kwargs = \
455
+ _prepare_newton_inputs (voc_est , (current , * args ), method_kwargs )
456
+ vd = newton (func = lambda x , * a : fi (x , current , * a ), x0 = x0 ,
462
457
fprime = lambda x , * a : bishop88 (x , * a , gradients = True )[3 ],
463
- args = args ,
464
- ** method_kwargs )
458
+ args = args , ** method_kwargs )
465
459
else :
466
460
raise NotImplementedError ("Method '%s' isn't implemented" % method )
467
461
@@ -555,9 +549,9 @@ def bishop88_mpp(photocurrent, saturation_current, resistance_series,
555
549
... method='newton', method_kwargs={'full_output': True})
556
550
"""
557
551
# collect args
558
- args = (photocurrent , saturation_current , resistance_series ,
559
- resistance_shunt , nNsVth , d2mutau , NsVbi , breakdown_factor ,
560
- breakdown_voltage , breakdown_exp )
552
+ args = (photocurrent , saturation_current ,
553
+ resistance_series , resistance_shunt , nNsVth , d2mutau , NsVbi ,
554
+ breakdown_factor , breakdown_voltage , breakdown_exp )
561
555
method = method .lower ()
562
556
563
557
# method_kwargs create dict if not provided
@@ -584,12 +578,11 @@ def fmpp(x, *a):
584
578
elif method == 'newton' :
585
579
# make sure all args are numpy arrays if max size > 1
586
580
# if voc_est is an array, then make a copy to use for initial guess, v0
587
- args , v0 , method_kwargs = \
588
- _prepare_newton_inputs ((), args , voc_est , method_kwargs )
589
- vd = newton (
590
- func = fmpp , x0 = v0 ,
591
- fprime = lambda x , * a : bishop88 (x , * a , gradients = True )[7 ], args = args ,
592
- ** method_kwargs )
581
+ x0 , args , method_kwargs = \
582
+ _prepare_newton_inputs (voc_est , args , method_kwargs )
583
+ vd = newton (func = fmpp , x0 = x0 ,
584
+ fprime = lambda x , * a : bishop88 (x , * a , gradients = True )[7 ],
585
+ args = args , ** method_kwargs )
593
586
else :
594
587
raise NotImplementedError ("Method '%s' isn't implemented" % method )
595
588
@@ -603,46 +596,42 @@ def fmpp(x, *a):
603
596
return bishop88 (vd , * args )
604
597
605
598
606
- def _get_size_and_shape (args ):
607
- # find the right size and shape for returns
608
- size , shape = 0 , None # 0 or None both mean scalar
609
- for arg in args :
610
- try :
611
- this_shape = arg .shape # try to get shape
612
- except AttributeError :
613
- this_shape = None
614
- try :
615
- this_size = len (arg ) # try to get the size
616
- except TypeError :
617
- this_size = 0
618
- else :
619
- this_size = arg .size # if it has shape then it also has size
620
- if shape is None :
621
- shape = this_shape # set the shape if None
622
- # update size and shape
623
- if this_size > size :
624
- size = this_size
625
- if this_shape is not None :
626
- shape = this_shape
627
- return size , shape
628
-
629
-
630
- def _prepare_newton_inputs (i_or_v_tup , args , v0 , method_kwargs ):
631
- # broadcast arguments for newton method
632
- # the first argument should be a tuple, eg: (i,), (v,) or ()
633
- size , shape = _get_size_and_shape (i_or_v_tup + args )
634
- if size > 1 :
635
- args = [np .asarray (arg ) for arg in args ]
636
- # newton uses initial guess for the output shape
637
- # copy v0 to a new array and broadcast it to the shape of max size
638
- if shape is not None :
639
- v0 = np .broadcast_to (v0 , shape ).copy ()
599
+ def _shape_of_max_size (* args ):
600
+ return max (((np .size (a ), np .shape (a )) for a in args ),
601
+ key = lambda t : t [0 ])[1 ]
602
+
603
+
604
+ def _prepare_newton_inputs (x0 , args , method_kwargs ):
605
+ """
606
+ Make inputs compatible with Scipy's newton by:
607
+ - converting all arugments (`x0` and `args`) into numpy.ndarrays if any
608
+ argument is not a scalar.
609
+ - broadcasting the initial guess `x0` to the shape of the argument with
610
+ the greatest size.
611
+
612
+ Parameters
613
+ ----------
614
+ x0: numeric
615
+ Initial guess for newton.
616
+ args: Iterable(numeric)
617
+ Iterable of additional arguments to use in SciPy's newton.
618
+ method_kwargs: dict
619
+ Options to pass to newton.
620
+
621
+ Returns
622
+ -------
623
+ tuple
624
+ The updated initial guess, arguments, and options for newton.
625
+ """
626
+ if not (np .isscalar (x0 ) and all (map (np .isscalar , args ))):
627
+ args = tuple (map (np .asarray , args ))
628
+ x0 = np .broadcast_to (x0 , _shape_of_max_size (x0 , * args ))
640
629
641
630
# set abs tolerance and maxiter from method_kwargs if not provided
642
631
# apply defaults, but giving priority to user-specified values
643
632
method_kwargs = {** NEWTON_DEFAULT_PARAMS , ** method_kwargs }
644
633
645
- return args , v0 , method_kwargs
634
+ return x0 , args , method_kwargs
646
635
647
636
648
637
def _lambertw_v_from_i (current , photocurrent , saturation_current ,
0 commit comments