10000 vectorizing _lambert_i_from_v to handle list of lists of voltages · reepoi/pvlib-python@59cb363 · GitHub
[go: up one dir, main page]

Skip to content

Commit 59cb363

Browse files
committed
vectorizing _lambert_i_from_v to handle list of lists of voltages
1 parent 5f28153 commit 59cb363

File tree

3 files changed

+61
-31
lines changed

3 files changed

+61
-31
lines changed

docs/examples/iv-modeling/plot_singlediode.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
# :py:meth:`pvlib.pvsystem.singlediode` is then used to generate the IV curves.
3333

3434
from pvlib import pvsystem
35+
import numpy as np
3536
import pandas as pd
3637
import matplotlib.pyplot as plt
3738

@@ -88,15 +89,24 @@
8889
)
8990

9091
# plug the parameters into the SDE and solve for IV curves:
91-
curve_info = pvsystem.singlediode(
92-
photocurrent=IL,
93-
saturation_current=I0,
94-
resistance_series=Rs,
95-
resistance_shunt=Rsh,
96-
nNsVth=nNsVth,
97-
ivcurve_pnts=100,
98-
method='lambertw'
92+
SDE_params = {
93+
'photocurrent': IL,
94+
'saturation_current': I0,
95+
'resistance_series': Rs,
96+
'resistance_shunt': Rsh,
97+
'nNsVth': nNsVth
98+
}
99+
curve_info = pvsystem.singlediode(method='lambertw', **SDE_params)
100+
101+
# find points on the IV curve
102+
ivcurve_pnts = 100
103+
linspace = np.linspace(0, 1, ivcurve_pnts)
104+
voltages = pd.DataFrame(
105+
curve_info['v_oc'].to_numpy().reshape(-1, 1) * linspace,
106+
index=curve_info.index
99107
)
108+
currents = pvsystem.i_from_v(voltage=voltages, method='lambertw', **SDE_params)
109+
100110

101111
# plot the calculated curves:
102112
plt.figure()
@@ -105,11 +115,10 @@
105115
"$G_{eff}$ " + f"{case['Geff']} $W/m^2$\n"
106116
"$T_{cell}$ " + f"{case['Tcell']} $\\degree C$"
107117
)
108-
plt.plot(curve_info['v'][i], curve_info['i'][i], label=label)
109-
v_mp = curve_info['v_mp'][i]
110-
i_mp = curve_info['i_mp'][i]
111-
# mark the MPP
112-
plt.plot([v_mp], [i_mp], ls='', marker='o', c='k')
118+
plt.plot(voltages.loc[i], currents.loc[i], label=label)
119+
120+
# mark the MPP
121+
plt.plot(curve_info['v_mp'], curve_info['i_mp'], ls='', marker='o', c='k')
113122

114123
plt.legend(loc=(1.0, 0))
115124
plt.xlabel('Module voltage [V]')

pvlib/pvsystem.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2846,7 +2846,7 @@ def singlediode(photocurrent, saturation_current, resistance_series,
28462846
points = i_sc, v_oc, i_mp, v_mp, p_mp, i_x, i_xx
28472847

28482848
points = np.atleast_1d(*points) # covert scalars to 1d arrays
2849-
points = np.vstack(points).T # create DataFrame rows
2849+
points = np.hstack(points) # collect DataFrame columns
28502850

28512851
index = None # keep pd.Series index, if available
28522852
if isinstance(photocurrent, pd.Series):
@@ -3065,7 +3065,7 @@ def i_from_v(resistance_shunt, resistance_series, nNsVth, voltage,
30653065
30663066
Returns
30673067
-------
3068-
current : np.ndarray or scalar
3068+
current : pd.DataFrame
30693069
30703070
References
30713071
----------
@@ -3074,10 +3074,10 @@ def i_from_v(resistance_shunt, resistance_series, nNsVth, voltage,
30743074
Energy Materials and Solar Cells, 81 (2004) 269-277.
30753075
'''
30763076
if method.lower() == 'lambertw':
3077-
return _singlediode._lambertw_i_from_v(
3077+
return pd.DataFrame(_singlediode._lambertw_i_from_v(
30783078
resistance_shunt, resistance_series, nNsVth, voltage,
30793079
saturation_current, photocurrent
3080-
)
3080+
))
30813081
else:
30823082
# Calculate points on the IV curve using either 'newton' or 'brentq'
30833083
# methods. Voltages are determined by first solving the single diode

pvlib/singlediode.py

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -509,18 +509,28 @@ def _lambertw_v_from_i(resistance_shunt, resistance_series, nNsVth, current,
509509
# Ensure that we are working with read-only views of numpy arrays
510510
# Turns Series into arrays so that we don't have to worry about
511511
# multidimensional broadcasting failing
512-
Gsh, Rs, a, I, I0, IL = \
513-
np.broadcast_arrays(conductance_shunt, resistance_series, nNsVth,
514-
current, saturation_current, photocurrent)
512+
Gsh, Rs, a, I0, IL = [
513+
np.transpose(np.atleast_2d(arr))
514+
for arr in np.broadcast_arrays(conductance_shunt, resistance_series,
515+
nNsVth, saturation_current,
516+
photocurrent)
517+
]
518+
519+
if np.isscalar(current):
520+
I = np.full(Gsh.shape, current)
521+
elif current.ndim == 1:
522+
I = np.tile(current, (Gsh.shape[0], 1))
523+
else:
524+
I = np.asarray(current)
515525

516526
# Intitalize output V (I might not be float64)
517527
V = np.full_like(I, np.nan, dtype=np.float64)
518528

519529
# Determine indices where 0 < Gsh requires implicit model solution
520-
idx_p = 0. < Gsh
530+
idx_p = (0. < Gsh).squeeze()
521531

522532
# Determine indices where 0 = Gsh allows explicit model solution
523-
idx_z = 0. == Gsh
533+
idx_z = (0. == Gsh).squeeze()
524534

525535
# Explicit solutions where Gsh=0
526536
if np.any(idx_z):
@@ -586,18 +596,28 @@ def _lambertw_i_from_v(resistance_shunt, resistance_series, nNsVth, voltage,
586596
# Ensure that we are working with read-only views of numpy arrays
587597
# Turns Series into arrays so that we don't have to worry about
588598
# multidimensional broadcasting failing
589-
Gsh, Rs, a, V, I0, IL = \
590-
np.broadcast_arrays(conductance_shunt, resistance_series, nNsVth,
591-
voltage, saturation_current, photocurrent)
599+
Gsh, Rs, a, I0, IL = [
600+
np.transpose(np.atleast_2d(arr))
601+
for arr in np.broadcast_arrays(conductance_shunt, resistance_series,
602+
nNsVth, saturation_current,
603+
photocurrent)
604+
]
605+
606+
if np.isscalar(voltage):
607+
V = np.full(Gsh.shape, voltage)
608+
elif voltage.ndim == 1:
609+
V = np.tile(voltage, (Gsh.shape[0], 1))
610+
else:
611+
V = np.asarray(voltage)
592612

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

596616
# Determine indices where 0 < Rs requires implicit model solution
597-
idx_p = 0. < Rs
617+
idx_p = ( E4B9 0. < Rs).squeeze()
598618

599619
# Determine indices where 0 = Rs allows explicit model solution
600-
idx_z = 0. == Rs
620+
idx_z = (0. == Rs).squeeze()
601621

602622
# Explicit solutions where Rs=0
603623
if np.any(idx_z):
@@ -648,8 +668,8 @@ def _lambertw(photocurrent, saturation_current, resistance_series,
648668

649669
# Find the voltage, v_mp, where the power is maximized.
650670
# Start the golden section search at v_oc * 1.14
651-
p_mp, v_mp = _golden_sect_DataFrame(params, 0., v_oc * 1.14,
652-
_pwr_optfcn)
671+
p_mp, v_mp = _golden_sect_DataFrame(params, np.zeros_like(v_oc),
672+
v_oc * 1.14, _pwr_optfcn)
653673

654674
# Find Imp using Lambert W
655675
i_mp = _lambertw_i_from_v(resistance_shunt, resistance_series, nNsVth,
@@ -683,8 +703,9 @@ def _pwr_optfcn(df, loc):
683703
'''
684704
Function to find power from ``i_from_v``.
685705
'''
706+
V_per_curve = df[loc]
686707

687708
I = _lambertw_i_from_v(df['r_sh'], df['r_s'], # noqa: E741, N806
688-
df['nNsVth'], df[loc], df['i_0'], df['i_l'])
709+
df['nNsVth'], V_per_curve, df['i_0'], df['i_l'])
689710

690-
return I * df[loc]
711+
return I * V_per_curve

0 commit comments

Comments
 (0)
0