@@ -287,8 +287,8 @@ def bishop88_i_from_v(voltage, photocurrent, saturation_current,
287287 ... method_kwargs={'full_output': True})
288288 """
289289 # 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 ,
292292 breakdown_factor , breakdown_voltage , breakdown_exp )
293293 method = method .lower ()
294294
@@ -319,14 +319,11 @@ def vd_from_brent(voc, v, iph, isat, rs, rsh, gamma, d2mutau, NsVbi,
319319 vd_from_brent_vectorized = np .vectorize (vd_from_brent )
320320 vd = vd_from_brent_vectorized (voc_est , voltage , * args )
321321 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 ,
327325 fprime = lambda x , * a : bishop88 (x , * a , gradients = True )[4 ],
328- args = args ,
329- ** method_kwargs )
326+ args = args , ** method_kwargs )
330327 else :
331328 raise NotImplementedError ("Method '%s' isn't implemented" % method )
332329
@@ -422,9 +419,9 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
422419 ... method_kwargs={'full_output': True})
423420 """
424421 # 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 )
428425 method = method .lower ()
429426
430427 # method_kwargs create dict if not provided
@@ -454,14 +451,11 @@ def vd_from_brent(voc, i, iph, isat, rs, rsh, gamma, d2mutau, NsVbi,
454451 vd_from_brent_vectorized = np .vectorize (vd_from_brent )
455452 vd = vd_from_brent_vectorized (voc_est , current , * args )
456453 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 ,
462457 fprime = lambda x , * a : bishop88 (x , * a , gradients = True )[3 ],
463- args = args ,
464- ** method_kwargs )
458+ args = args , ** method_kwargs )
465459 else :
466460 raise NotImplementedError ("Method '%s' isn't implemented" % method )
467461
@@ -555,9 +549,9 @@ def bishop88_mpp(photocurrent, saturation_current, resistance_series,
555549 ... method='newton', method_kwargs={'full_output': True})
556550 """
557551 # 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 )
561555 method = method .lower ()
562556
563557 # method_kwargs create dict if not provided
@@ -584,12 +578,11 @@ def fmpp(x, *a):
584578 elif method == 'newton' :
585579 # make sure all args are numpy arrays if max size > 1
586580 # 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 )
593586 else :
594587 raise NotImplementedError ("Method '%s' isn't implemented" % method )
595588
@@ -603,46 +596,42 @@ def fmpp(x, *a):
603596 return bishop88 (vd , * args )
604597
605598
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 ))
640629
641630 # set abs tolerance and maxiter from method_kwargs if not provided
642631 # apply defaults, but giving priority to user-specified values
643632 method_kwargs = {** NEWTON_DEFAULT_PARAMS , ** method_kwargs }
644633
645- return args , v0 , method_kwargs
634+ return x0 , args , method_kwargs
646635
647636
648637def _lambertw_v_from_i (current , photocurrent , saturation_current ,
0 commit comments