Skip to content

Commit

Permalink
rename variables I,V to current,voltage
Browse files Browse the repository at this point in the history
  • Loading branch information
reepoi committed May 23, 2023
1 parent 9710b01 commit 2336b17
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 82 deletions.
12 changes: 6 additions & 6 deletions pvlib/pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -3043,17 +3043,17 @@ def v_from_i(current, photocurrent, saturation_current, resistance_series,
# equation for the diode voltage V_d then backing out voltage
args = (current, photocurrent, saturation_current,
resistance_series, resistance_shunt, nNsVth)
V = _singlediode.bishop88_v_from_i(*args, method=method.lower())
voltage = _singlediode.bishop88_v_from_i(*args, method=method.lower())
# find the right size and shape for returns
size, shape = _singlediode._get_size_and_shape(args)
if size <= 1:
if shape is not None:
V = np.tile(V, shape)
if np.isnan(V).any() and size <= 1:
V = np.repeat(V, size)
voltage = np.tile(voltage, shape)
if np.isnan(voltage).any() and size <= 1:
voltage = np.repeat(voltage, size)
if shape is not None:
V = V.reshape(shape)
return V
voltage = voltage.reshape(shape)
return voltage


def i_from_v(voltage, photocurrent, saturation_current, resistance_series,
Expand Down
82 changes: 48 additions & 34 deletions pvlib/singlediode.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,12 +509,12 @@ def _lambertw_v_from_i(current, photocurrent, saturation_current,
# Ensure that we are working with read-only views of numpy arrays
# Turns Series into arrays so that we don't have to worry about
# multidimensional broadcasting failing
I, IL, I0, Rs, Gsh, a = \
current, IL, I0, Rs, Gsh, a = \
np.broadcast_arrays(current, photocurrent, saturation_current,
resistance_series, conductance_shunt, nNsVth)

# Intitalize output V (I might not be float64)
V = np.full_like(I, np.nan, dtype=np.float64)
# Intitalize output voltage (current might not be float64)
voltage = np.full_like(current, np.nan, dtype=np.float64)

# Determine indices where 0 < Gsh requires implicit model solution
idx_p = 0. < Gsh
Expand All @@ -524,17 +524,21 @@ def _lambertw_v_from_i(current, photocurrent, saturation_current,

# Explicit solutions where Gsh=0
if np.any(idx_z):
V[idx_z] = a[idx_z] * np.log1p((IL[idx_z] - I[idx_z]) / I0[idx_z]) - \
I[idx_z] * Rs[idx_z]
voltage[idx_z] = (
a[idx_z] * np.log1p((IL[idx_z] - current[idx_z]) / I0[idx_z]) -
current[idx_z] * Rs[idx_z]
)

# Only compute using LambertW if there are cases with Gsh>0
if np.any(idx_p):
# LambertW argument, cannot be float128, may overflow to np.inf
# overflow is explicitly handled below, so ignore warnings here
with np.errstate(over='ignore'):
argW = (I0[idx_p] / (Gsh[idx_p] * a[idx_p]) *
np.exp((-I[idx_p] + IL[idx_p] + I0[idx_p]) /
(Gsh[idx_p] * a[idx_p])))
argW = (
I0[idx_p] / (Gsh[idx_p] * a[idx_p]) *
np.exp((-current[idx_p] + IL[idx_p] + I0[idx_p]) /
(Gsh[idx_p] * a[idx_p]))
)

# lambertw typically returns complex value with zero imaginary part
# may overflow to np.inf
Expand All @@ -546,10 +550,12 @@ def _lambertw_v_from_i(current, photocurrent, saturation_current,
# Only re-compute LambertW if it overflowed
if np.any(idx_inf):
# Calculate using log(argW) in case argW is really big
logargW = (np.log(I0[idx_p]) - np.log(Gsh[idx_p]) -
np.log(a[idx_p]) +
(-I[idx_p] + IL[idx_p] + I0[idx_p]) /
(Gsh[idx_p] * a[idx_p]))[idx_inf]
logargW = (
np.log(I0[idx_p]) - np.log(Gsh[idx_p]) -
np.log(a[idx_p]) +
(-current[idx_p] + IL[idx_p] + I0[idx_p]) /
(Gsh[idx_p] * a[idx_p])
)[idx_inf]

# Three iterations of Newton-Raphson method to solve
# w+log(w)=logargW. The initial guess is w=logargW. Where direct
Expand All @@ -563,13 +569,15 @@ def _lambertw_v_from_i(current, photocurrent, saturation_current,
# Eqn. 3 in Jain and Kapoor, 2004
# V = -I*(Rs + Rsh) + IL*Rsh - a*lambertwterm + I0*Rsh
# Recast in terms of Gsh=1/Rsh for better numerical stability.
V[idx_p] = (IL[idx_p] + I0[idx_p] - I[idx_p]) / Gsh[idx_p] - \
I[idx_p] * Rs[idx_p] - a[idx_p] * lambertwterm
voltage[idx_p] = (
(IL[idx_p] + I0[idx_p] - current[idx_p]) / Gsh[idx_p] -
current[idx_p] * Rs[idx_p] - a[idx_p] * lambertwterm
)

if output_is_scalar:
return V.item()
return voltage.item()
else:
return V
return voltage


def _lambertw_i_from_v(voltage, photocurrent, saturation_current,
Expand All @@ -586,12 +594,12 @@ def _lambertw_i_from_v(voltage, photocurrent, saturation_current,
# Ensure that we are working with read-only views of numpy arrays
# Turns Series into arrays so that we don't have to worry about
# multidimensional broadcasting failing
V, IL, I0, Rs, Gsh, a = \
voltage, IL, I0, Rs, Gsh, a = \
np.broadcast_arrays(voltage, photocurrent, saturation_current,
resistance_series, conductance_shunt, nNsVth)

# Intitalize output I (V might not be float64)
I = np.full_like(V, np.nan, dtype=np.float64) # noqa: E741, N806
# Intitalize output current (voltage might not be float64)
current = np.full_like(voltage, np.nan, dtype=np.float64) # noqa: E741, N806

# Determine indices where 0 < Rs requires implicit model solution
idx_p = 0. < Rs
Expand All @@ -601,17 +609,20 @@ def _lambertw_i_from_v(voltage, photocurrent, saturation_current,

# Explicit solutions where Rs=0
if np.any(idx_z):
I[idx_z] = IL[idx_z] - I0[idx_z] * np.expm1(V[idx_z] / a[idx_z]) - \
Gsh[idx_z] * V[idx_z]
current[idx_z] = (
IL[idx_z] - I0[idx_z] * np.expm1(voltage[idx_z] / a[idx_z]) -
Gsh[idx_z] * voltage[idx_z]
)

# Only compute using LambertW if there are cases with Rs>0
# Does NOT handle possibility of overflow, github issue 298
if np.any(idx_p):
# LambertW argument, cannot be float128, may overflow to np.inf
argW = Rs[idx_p] * I0[idx_p] / (
a[idx_p] * (Rs[idx_p] * Gsh[idx_p] + 1.)) * \
np.exp((Rs[idx_p] * (IL[idx_p] + I0[idx_p]) + V[idx_p]) /
(a[idx_p] * (Rs[idx_p] * Gsh[idx_p] + 1.)))
argW = (
Rs[idx_p] * I0[idx_p] / (a[idx_p] * (Rs[idx_p] * Gsh[idx_p] + 1.))
* np.exp((Rs[idx_p] * (IL[idx_p] + I0[idx_p]) + voltage[idx_p]) /
(a[idx_p] * (Rs[idx_p] * Gsh[idx_p] + 1.)))
)

# lambertw typically returns complex value with zero imaginary part
# may overflow to np.inf
Expand All @@ -620,14 +631,16 @@ def _lambertw_i_from_v(voltage, photocurrent, saturation_current,
# Eqn. 2 in Jain and Kapoor, 2004
# I = -V/(Rs + Rsh) - (a/Rs)*lambertwterm + Rsh*(IL + I0)/(Rs + Rsh)
# Recast in terms of Gsh=1/Rsh for better numerical stability.
I[idx_p] = (IL[idx_p] + I0[idx_p] - V[idx_p] * Gsh[idx_p]) / \
(Rs[idx_p] * Gsh[idx_p] + 1.) - (
a[idx_p] / Rs[idx_p]) * lambertwterm
current[idx_p] = (
(IL[idx_p] + I0[idx_p] - voltage[idx_p] * Gsh[idx_p]) /
(Rs[idx_p] * Gsh[idx_p] + 1.) -
(a[idx_p] / Rs[idx_p]) * lambertwterm
)

if output_is_scalar:
return I.item()
return current.item()
else:
return I
return current


def _lambertw(photocurrent, saturation_current, resistance_series,
Expand Down Expand Up @@ -675,8 +688,9 @@ def _pwr_optfcn(df, loc):
Function to find power from ``i_from_v``.
'''

I = _lambertw_i_from_v(df[loc], df['photocurrent'],
df['saturation_current'], df['resistance_series'],
df['resistance_shunt'], df['nNsVth'])
current = _lambertw_i_from_v(df[loc], df['photocurrent'],
df['saturation_current'],
df['resistance_series'],
df['resistance_shunt'], df['nNsVth'])

return I * df[loc]
return current * df[loc]
Loading

0 comments on commit 2336b17

Please sign in to comment.