Skip to content

Commit bc6646b

Browse files
committed
_prepare_newton_inputs only changes shape of x0; refactoring to remove singlediode._get_size_and_shape
1 parent cc3700d commit bc6646b

File tree

2 files changed

+34
-78
lines changed

2 files changed

+34
-78
lines changed

pvlib/pvsystem.py

Lines changed: 14 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2651,28 +2651,19 @@ def v_from_i(current, photocurrent, saturation_current, resistance_series,
26512651
parameters of real solar cells using Lambert W-function", Solar
26522652
Energy Materials and Solar Cells, 81 (2004) 269-277.
26532653
'''
2654+
args = (current, photocurrent, saturation_current,
2655+
resistance_series, resistance_shunt, nNsVth)
26542656
if method.lower() == 'lambertw':
2655-
return _singlediode._lambertw_v_from_i(
2656-
current, photocurrent, saturation_current, resistance_series,
2657-
resistance_shunt, nNsVth
2658-
)
2657+
return _singlediode._lambertw_v_from_i(*args)
26592658
else:
26602659
# Calculate points on the IV curve using either 'newton' or 'brentq'
26612660
# methods. Voltages are determined by first solving the single diode
26622661
# equation for the diode voltage V_d then backing out voltage
2663-
args = (current, photocurrent, saturation_current,
2664-
resistance_series, resistance_shunt, nNsVth)
26652662
V = _singlediode.bishop88_v_from_i(*args, method=method.lower())
2666-
# find the right size and shape for returns
2667-
size, shape = _singlediode._get_size_and_shape(args)
2668-
if size <= 1:
2669-
if shape is not None:
2670-
V = np.tile(V, shape)
2671-
if np.isnan(V).any() and size <= 1:
2672-
V = np.repeat(V, size)
2673-
if shape is not None:
2674-
V = V.reshape(shape)
2675-
return V
2663+
if all(map(np.isscalar, args)):
2664+
return V
2665+
shape = _singlediode._shape_of_max_size(*args)
2666+
return np.broadcast_to(V, shape)
26762667

26772668

26782669
def i_from_v(voltage, photocurrent, saturation_current, resistance_series,
@@ -2742,28 +2733,19 @@ def i_from_v(voltage, photocurrent, saturation_current, resistance_series,
27422733
parameters of real solar cells using Lambert W-function", Solar
27432734
Energy Materials and Solar Cells, 81 (2004) 269-277.
27442735
'''
2736+
args = (voltage, photocurrent, saturation_current,
2737+
resistance_series, resistance_shunt, nNsVth)
27452738
if method.lower() == 'lambertw':
2746-
return _singlediode._lambertw_i_from_v(
2747-
voltage, photocurrent, saturation_current, resistance_series,
2748-
resistance_shunt, nNsVth
2749-
)
2739+
return _singlediode._lambertw_i_from_v(*args)
27502740
else:
27512741
# Calculate points on the IV curve using either 'newton' or 'brentq'
27522742
# methods. Voltages are determined by first solving the single diode
27532743
# equation for the diode voltage V_d then backing out voltage
2754-
args = (voltage, photocurrent, saturation_current, resistance_series,
2755-
resistance_shunt, nNsVth)
27562744
current = _singlediode.bishop88_i_from_v(*args, method=method.lower())
2757-
# find the right size and shape for returns
2758-
size, shape = _singlediode._get_size_and_shape(args)
2759-
if size <= 1:
2760-
if shape is not None:
2761-
current = np.tile(current, shape)
2762-
if np.isnan(current).any() and size <= 1:
2763-
current = np.repeat(current, size)
2764-
if shape is not None:
2765-
current = current.reshape(shape)
2766-
return current
2745+
if all(map(np.isscalar, args)):
2746+
return current
2747+
shape = _singlediode._shape_of_max_size(*args)
2748+
return np.broadcast_to(current, shape)
27672749

27682750

27692751
def scale_voltage_current_power(data, voltage=1, current=1):

pvlib/singlediode.py

Lines changed: 20 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -287,10 +287,9 @@ def bishop88_i_from_v(voltage, photocurrent, saturation_current,
287287
... method_kwargs={'full_output': True})
288288
"""
289289
# collect args
290-
args_numeric = (photocurrent, saturation_current, resistance_series,
291-
resistance_shunt, nNsVth, d2mutau, NsVbi)
292-
args_scalar = (breakdown_factor, breakdown_voltage, breakdown_exp)
293-
args = args_numeric + args_scalar
290+
args = (photocurrent, saturation_current,
291+
resistance_series, resistance_shunt, nNsVth, d2mutau, NsVbi,
292+
breakdown_factor, breakdown_voltage, breakdown_exp)
294293
method = method.lower()
295294

296295
# method_kwargs create dict if not provided
@@ -320,10 +319,8 @@ def vd_from_brent(voc, v, iph, isat, rs, rsh, gamma, d2mutau, NsVbi,
320319
vd_from_brent_vectorized = np.vectorize(vd_from_brent)
321320
vd = vd_from_brent_vectorized(voc_est, voltage, *args)
322321
elif method == 'newton':
323-
x0, (voltage, *args_numeric), method_kwargs = \
324-
_prepare_newton_inputs(voltage, [voltage, *args_numeric],
325-
method_kwargs)
326-
args = tuple(args_numeric) + args_scalar
322+
x0, (voltage, *args), method_kwargs = \
323+
_prepare_newton_inputs(voltage, (voltage, *args), method_kwargs)
327324
vd = newton(func=lambda x, *a: fv(x, voltage, *a), x0=x0,
328325
fprime=lambda x, *a: bishop88(x, *a, gradients=True)[4],
329326
args=args, **method_kwargs)
@@ -422,10 +419,9 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
422419
... method_kwargs={'full_output': True})
423420
"""
424421
# collect args
425-
args_numeric = (photocurrent, saturation_current, resistance_series,
426-
resistance_shunt, nNsVth, d2mutau, NsVbi)
427-
args_scalar = (breakdown_factor, breakdown_voltage, breakdown_exp)
428-
args = args_numeric + args_scalar
422+
args = (photocurrent, saturation_current,
423+
resistance_series, resistance_shunt, nNsVth, d2mutau, NsVbi,
424+
breakdown_factor, breakdown_voltage, breakdown_exp)
429425
method = method.lower()
430426

431427
# method_kwargs create dict if not provided
@@ -455,10 +451,8 @@ def vd_from_brent(voc, i, iph, isat, rs, rsh, gamma, d2mutau, NsVbi,
455451
vd_from_brent_vectorized = np.vectorize(vd_from_brent)
456452
vd = vd_from_brent_vectorized(voc_est, current, *args)
457453
elif method == 'newton':
458-
x0, (current, *args_numeric), method_kwargs = \
459-
_prepare_newton_inputs(voc_est, [current, *args_numeric],
460-
method_kwargs)
461-
args = tuple(args_numeric) + args_scalar
454+
x0, (current, *args), method_kwargs = \
455+
_prepare_newton_inputs(voc_est, (current, *args), method_kwargs)
462456
vd = newton(func=lambda x, *a: fi(x, current, *a), x0=x0,
463457
fprime=lambda x, *a: bishop88(x, *a, gradients=True)[3],
464458
args=args, **method_kwargs)
@@ -555,10 +549,9 @@ def bishop88_mpp(photocurrent, saturation_current, resistance_series,
555549
... method='newton', method_kwargs={'full_output': True})
556550
"""
557551
# collect args
558-
args_numeric = (photocurrent, saturation_current, resistance_series,
559-
resistance_shunt, nNsVth, d2mutau, NsVbi)
560-
args_scalar = (breakdown_factor, breakdown_voltage, breakdown_exp)
561-
args = args_numeric + args_scalar
552+
args = (photocurrent, saturation_current,
553+
resistance_series, resistance_shunt, nNsVth, d2mutau, NsVbi,
554+
breakdown_factor, breakdown_voltage, breakdown_exp)
562555
method = method.lower()
563556

564557
# method_kwargs create dict if not provided
@@ -585,9 +578,8 @@ def fmpp(x, *a):
585578
elif method == 'newton':
586579
# make sure all args are numpy arrays if max size > 1
587580
# if voc_est is an array, then make a copy to use for initial guess, v0
588-
x0, args_numeric, method_kwargs = \
589-
_prepare_newton_inputs(voc_est, args_numeric, method_kwargs)
590-
args = tuple(args_numeric) + args_scalar
581+
x0, args, method_kwargs = \
582+
_prepare_newton_inputs(voc_est, args, method_kwargs)
591583
vd = newton(func=fmpp, x0=x0,
592584
fprime=lambda x, *a: bishop88(x, *a, gradients=True)[7],
593585
args=args, **method_kwargs)
@@ -604,28 +596,9 @@ def fmpp(x, *a):
604596
return bishop88(vd, *args)
605597

606598

607-
def _get_size_and_shape(args):
608-
# find the right size and shape for returns
609-
size, shape = 0, None # 0 or None both mean scalar
610-
for arg in args:
611-
try:
612-
this_shape = arg.shape # try to get shape
613-
except AttributeError:
614-
this_shape = None
615-
try:
616-
this_size = len(arg) # try to get the size
617-
except TypeError:
618-
this_size = 0
619-
else:
620-
this_size = arg.size # if it has shape then it also has size
621-
if shape is None:
622-
shape = this_shape # set the shape if None
623-
# update size and shape
624-
if this_size > size:
625-
size = this_size
626-
if this_shape is not None:
627-
shape = this_shape
628-
return size, shape
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]
629602

630603

631604
def _prepare_newton_inputs(x0, args, method_kwargs):
@@ -651,7 +624,8 @@ def _prepare_newton_inputs(x0, args, method_kwargs):
651624
The updated initial guess, arguments, and options for newton.
652625
"""
653626
if not (np.isscalar(x0) and all(map(np.isscalar, args))):
654-
x0, *args = np.broadcast_arrays(x0, *args)
627+
args = tuple(map(np.asarray, args))
628+
x0 = np.broadcast_to(x0, _shape_of_max_size(x0, *args))
655629

656630
# set abs tolerance and maxiter from method_kwargs if not provided
657631
# apply defaults, but giving priority to user-specified values

0 commit comments

Comments
 (0)