-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
The issue was raised in some comments in #410 and #409 that the rules for broadcasting outputs are inconsistent among the single diode methods
singlediode
will only broadcast the output ifphotocurrent
is an sequence, array or series- float -> OrderedDict of float
- array or sequence -> OrderedDict of array or sequence
- Series -> DataFrame
i_from_v
will broadcast the smallest size of eithervoltage
,photocurrent
, orresistance_shunt
v_from_i
will broadcast the smallest size of eithercurrent
,photocurrent
, orresistance_shunt
In those same comments @cwhanse proposed:
to expand singletons when any parameter input is a vector, and to fail (gracefully) if two parameters are vectors of unequal length
I agree with this idea, and both i_from_v
and v_from_i
already have some boilerplate code to address this (note this snippet is lifted from v_from_i
, with variable names suited for that function, change the
arguments to other methods like singlediode or mpp):
# find the right size and shape for returns
args = (current, photocurrent, saturation_current,
resistance_series, resistance_shunt, nNsVth) <-- list the args allowed to broadcast here
size, shape = 0, None # 0 or None both mean scalar
for arg in args:
try:
this_shape = arg.shape # try to get shape
except AttributeError:
this_shape = None
try:
this_size = len(arg) # try to get the size
except TypeError:
this_size = 0
else:
this_size = sum(this_shape) # calc size from shape
if shape is None:
shape = this_shape # set the shape if None
# update size and shape
if this_size > size:
size = this_size
if this_shape is not None:
shape = this_shape
Now knowing the size
and/or the shape
one can let numpy.vectorize
cast the output as proposed:
if size <= 1:
V = v_from_i_fun(*args)
if shape is not None:
V = np.tile(V, shape) <-- hacky way to make either array(V), array([V]), array([[V]]), ...
else:
# np.vectorize handles broadcasting, raises ValueError
vecfun = np.vectorize(v_from_i_fun)
V = vecfun(*args)
if np.isnan(V).any() and size <= 1:
V = np.repeat(V, size)
if shape is not None:
V = V.reshape(shape)
On a related note: @cwhanse I realize now I misread your comment and made i_from_v
and v_from_i
fail silently instead of gracefully, oops!. This might cause some confusion, in the short term, but maybe we can address it in this issue here?
@wholmgren is there a PVLibException
class? What exception would you expect here, maybe ValueError('All arguments must be either scalar or the same size.')
?