Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[DEMO: DO NOT MERGE] Brentq and Halley's method algorithm illustrations #412

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9ab2e95
Add draft i_from_v_alt function with tests
Jun 15, 2017
a66abc4
Merge branch 'master' of github.com:pvlib/pvlib-python
Jun 15, 2017
a19447b
Better comments and more explicit typing
Jun 15, 2017
76616d5
Use transform from shunt resistance to shunt conductance
Jun 15, 2017
e35412f
Add v_from_i_alt() with initial tests and use np.where
Jun 16, 2017
82217fd
Use test fixtures
Jun 21, 2017
135ce4c
Add @requires_scipy to test fixtures
Jun 23, 2017
f927aef
More current_sum_at_diode_node() tests and using fixture
Jun 23, 2017
741a7f4
Naming, documentation, and formatting
Jun 23, 2017
c9129dc
Deprecate replaced functions and flake8
Jul 27, 2017
3aa150d
Merge branch 'master' of github.com:pvlib/pvlib-python
Jul 27, 2017
85e957d
Add release documentation and flake8 again
Jul 27, 2017
fb22171
Replace deprecated function usages and update test_singlediode_series…
Jul 27, 2017
275eccc
Merge branch 'master' of github.com:pvlib/pvlib-python
Sep 10, 2017
cdd2bd9
Conform to existing API
Sep 11, 2017
223275a
Run flake8
Sep 11, 2017
6b50ad9
Implement some code quality suggestions
Sep 11, 2017
e4e09be
Remove extraneous print statements
Sep 11, 2017
37484f1
Better docstrings
Sep 11, 2017
f9c4c82
Fix parameter ranges in docstrings
Sep 11, 2017
26eabde
Add test that overflows lambertw arg
Sep 11, 2017
4d7754d
Add test for mixed solution types logic
Sep 11, 2017
03ae51e
Use broadcast_arrays for cleaner code
Sep 12, 2017
6c410d2
One more simplification
Sep 12, 2017
19af023
Better use of broadcast_arrays
Sep 12, 2017
0cbf4bd
Merge branch 'master' of github.com:pvlib/pvlib-python
Jan 30, 2018
8d79e84
Use brentq() for i_from_v()
Jan 30, 2018
dfccd00
Get tests passing locally
Jan 30, 2018
357267c
Illustrate Newton's method for v_from_i()
Jan 31, 2018
fd6717f
brentq in first quadrant only
Feb 1, 2018
329450a
Merge branch 'master' of github.com:pvlib/pvlib-python into gold_dataset
Feb 13, 2018
c96795d
Initial code with example output
Feb 13, 2018
8d1ecd9
Initial polishing and comments
Feb 14, 2018
3899de2
Merge branch 'gold_dataset' of github.com:thunderfish24/pvlib-python …
Feb 14, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Naming, documentation, and formatting
  • Loading branch information
Mark Campanelli committed Jun 23, 2017
commit 741a7f47a7b20330cc33f865788de55ea32f491a
204 changes: 135 additions & 69 deletions 204 pvlib/pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -1851,16 +1851,17 @@ def v_from_i(resistance_shunt, resistance_series, nNsVth, current,
I = np.asarray(current, np.float64)
I0 = np.asarray(saturation_current, np.float64)
IL = np.asarray(photocurrent, np.float64)

# This transforms any ideal Rsh=np.inf into Gsh=0., which is generally more numerically stable
Gsh = 1./Rsh

# argW cannot be float128
argW = I0/(Gsh*nNsVth)*np.exp((-I + IL + I0)/(Gsh*nNsVth))
lambertwterm = lambertw(argW).real

# Calculate using log(argW) in case argW is really big
logargW = (np.log(I0) - np.log(Gsh) - np.log(nNsVth) + (-I + IL + I0)/(Gsh*nNsVth))
logargW = \
(np.log(I0) - np.log(Gsh) - np.log(nNsVth) + (-I + IL + I0)/(Gsh*nNsVth))

# Three iterations of Newton-Raphson method to solve
# w+log(w)=logargW. The initial guess is w=logargW. Where direct
Expand All @@ -1871,7 +1872,8 @@ def v_from_i(resistance_shunt, resistance_series, nNsVth, current,
w = w * (1 - np.log(w) + logargW) / (1 + w)
lambertwterm_log = w

lambertwterm = np.where(np.isfinite(lambertwterm), lambertwterm, lambertwterm_log)
lambertwterm = \
np.where(np.isfinite(lambertwterm), lambertwterm, lambertwterm_log)

# Eqn. 3 in Jain and Kapoor, 2004
V = (IL + I0 - I)/Gsh - I*Rs - nNsVth*lambertwterm
Expand Down Expand Up @@ -1937,153 +1939,212 @@ def i_from_v(resistance_shunt, resistance_series, nNsVth, voltage,
V = np.asarray(voltage, np.float64)
I0 = np.asarray(saturation_current, np.float64)
IL = np.asarray(photocurrent, np.float64)

# This transforms any ideal Rsh=np.inf into Gsh=0., which is generally more numerically stable
Gsh = 1./Rsh

# argW cannot be float128
argW = Rs*I0/(nNsVth*(Rs*Gsh + 1.))*np.exp((Rs*(IL + I0) + V)/(nNsVth*(Rs*Gsh + 1.)))
argW = \
Rs*I0/(nNsVth*(Rs*Gsh + 1.))*np.exp((Rs*(IL + I0) + V)/(nNsVth*(Rs*Gsh + 1.)))
lambertwterm = lambertw(argW).real

# Eqn. 4 in Jain and Kapoor, 2004
I = (IL + I0 - V*Gsh)/(Rs*Gsh + 1.) - (nNsVth/Rs)*lambertwterm

return I


def current_sum_at_diode_node(V, I, IL, I0, nNsVth, Rs, Rsh):
def sdm_current_sum(V, I, IL, I0, nNsVth, Rs, Rsh):
'''
TODO Description
Computes the sum of currents at the diode node at electrical steady state
using the standard single diode model (SDM) as described in, e.g., Jain and
Kapoor 2004 [1]. An ideal device is specified by Rs=0 and Rsh=numpy.inf.
This function behaves as a numpy ufunc, including pandas.Series inputs.

Parameters
----------
V : float64 numpy.ndarray, scalar, or pandas.series
V : numeric
Device voltage [V]
I : float64 numpy.ndarray, scalar, or pandas.series

I : numeric
Device current [A]
IL : float64 numpy.ndarray, scalar, or pandas.series

IL : numeric
Device photocurrent [A]
I0 : float64 numpy.ndarray, scalar, or pandas.series

I0 : numeric
Device saturation current [A]
nNsVth : float64 numpy.ndarray, scalar, or pandas.series

nNsVth : numeric
Device thermal voltage [V]
Rs : float64 numpy.ndarray, scalar, or pandas.series

Rs : numeric
Device series resistance [Ohm]
Rsh : float64 numpy.ndarray, scalar, or pandas.series

Rsh : numeric
Device shunt resistance [Ohm]

Returns
-------
current_sum_at_diode_node : float64 numpy.ndarray, scalar, or pandas.series
sdm_current_sum : numeric
Sum of currents at the diode node in equivalent circuit model [A]

References
----------
[1] A. Jain, A. Kapoor, "Exact analytical solutions of the parameters of
real solar cells using Lambert W-function", Solar Energy Materials and
Solar Cells, 81 (2004) 269-277.
'''

# TODO Ensure that this qualifies as a numpy ufunc

# current_sum_at_diode_node

# sdm_current_sum
return IL - I0*np.expm1((V + I*Rs)/nNsVth) - (V + I*Rs)/Rsh - I


def v_from_i_alt(I, IL, I0, nNsVth, Rs, Rsh, return_meta_dict=False):
def sdm_v_from_i(I, IL, I0, nNsVth, Rs, Rsh, return_meta_dict=False):
'''
TODO Description
Computes the device voltage at the given device current using the standard
single diode model (SDM) as described in, e.g., Jain and Kapoor 2004 [1].
An ideal device is specified by Rs=0 and Rsh=numpy.inf and the solution is
per Eq 3 of [1] unless Rsh=numpy.inf gives a more accurate (closed form)
solution as determined by the sum of currents at the diode node, which
should be zero at electrical steady state.
Inputs to this function can include scalars and pandas.Series, but it
always outputs a float64 numpy.ndarray regardless of input type(s).

Parameters
----------
I : float64 numpy.ndarray or scalar
I : numeric
Device current [A]
IL : float64 numpy.ndarray or scalar

IL : numeric
Device photocurrent [A]
I0 : float64 numpy.ndarray or scalar

I0 : numeric
Device saturation current [A]
nNsVth : float64 numpy.ndarray or scalar

nNsVth : numeric
Device thermal voltage [V]
Rs : float64 numpy.ndarray or scalar

Rs : numeric
Device series resistance [Ohm]
Rsh : float64 numpy.ndarray or scalar

Rsh : numeric
Device shunt resistance [Ohm]

return_meta_dict : boolean scalar
Return additional computation metadata dictionary

Returns
-------
V : float64 numpy.ndarray
Device voltage [V]
meta_dict : python dictionary (optional, returned when return_meta_dict=True)

meta_dict : dictionary (optional, returned when return_meta_dict=True)
Metadata for computation
meta_dict['current_sum_at_diode_node'] : float64 numpy.ndarray like V
Sum of currents at the diode node in equivalent circuit model [A]

meta_dict['sdm_current_sum'] : float64 numpy.ndarray like V
Sum of currents at diode node in equivalent circuit model [A]

meta_dict['inf_Rsh_idx'] : boolean numpy.ndarray like V
Indices where infinite shunt resistance gives best solution

References
----------
[1] A. Jain, A. Kapoor, "Exact analytical solutions of the parameters of
real solar cells using Lambert W-function", Solar Energy Materials and
Solar Cells, 81 (2004) 269-277.
'''

# TODO Check if this qualifies as a numpy ufunc

# Ensure inputs are all np.ndarray with np.float64 type
I = np.asarray(I, np.float64)
IL = np.asarray(IL, np.float64)
I0 = np.asarray(I0, np.float64)
nNsVth = np.asarray(nNsVth, np.float64)
Rs = np.asarray(Rs, np.float64)
Rsh = np.asarray(Rsh, np.float64)

# Default computation of V using zero_term keeps Rsh shape info
zero_term = np.zeros_like(I*Rs/Rsh)
V = nNsVth*(np.log(IL - I - zero_term + I0) - np.log(I0)) - I*Rs
current_sum_at_diode_node_out = current_sum_at_diode_node(V=V, I=I, IL=IL, I0=I0, nNsVth=nNsVth, Rs=Rs, Rsh=Rsh)

sdm_current_sum_out = \
sdm_current_sum(V=V, I=I, IL=IL, I0=I0, nNsVth=nNsVth, Rs=Rs, Rsh=Rsh)

# Computation of V using LambertW for provided Rsh
V_LambertW = v_from_i(Rsh, Rs, nNsVth, I, I0, IL)
current_sum_at_diode_node_LambertW = current_sum_at_diode_node(V=V_LambertW, I=I, IL=IL, I0=I0, nNsVth=nNsVth, Rs=Rs, Rsh=Rsh)

sdm_current_sum_LambertW = \
sdm_current_sum(V=V_LambertW, I=I, IL=IL, I0=I0, nNsVth=nNsVth, Rs=Rs, Rsh=Rsh)

# Compute selection indices (may be a scalar boolean)
finite_Rsh_idx = np.logical_and(np.isfinite(current_sum_at_diode_node_LambertW), np.absolute(current_sum_at_diode_node_LambertW) <= np.absolute(current_sum_at_diode_node_out))

finite_Rsh_idx = np.logical_and(np.isfinite(sdm_current_sum_LambertW), \
np.absolute(sdm_current_sum_LambertW) <= np.absolute(sdm_current_sum_out))

# These are always np.ndarray
V = np.where(finite_Rsh_idx, V_LambertW, V)
current_sum_at_diode_node_out = np.where(finite_Rsh_idx, current_sum_at_diode_node_LambertW, current_sum_at_diode_node_out)

sdm_current_sum_out = \
np.where(finite_Rsh_idx, sdm_current_sum_LambertW, sdm_current_sum_out)

if return_meta_dict:
return V, {'current_sum_at_diode_node' : current_sum_at_diode_node_out, 'inf_Rsh_idx' : np.array(np.logical_not(finite_Rsh_idx))}
return V, {'sdm_current_sum' : sdm_current_sum_out, \
'inf_Rsh_idx' : np.array(np.logical_not(finite_Rsh_idx))}
else:
return V


def i_from_v_alt(V, IL, I0, nNsVth, Rs, Rsh, return_meta_dict=False):
def sdm_i_from_v(V, IL, I0, nNsVth, Rs, Rsh, return_meta_dict=False):
'''
TODO Description

Computes the device current at the given device voltage using the standard
single diode model (SDM) as described in, e.g., Jain and Kapoor 2004 [1].
An ideal device is specified by Rs=0 and Rsh=numpy.inf and the solution is
per Eq 2 of [1] unless Rs=0 gives a more accurate (closed form)
solution as determined by the sum of currents at the diode node, which
should be zero at electrical steady state.
Inputs to this function can include scalars and pandas.Series, but it
always outputs a float64 numpy.ndarray regardless of input type(s).

Parameters
----------
V : float64 numpy.ndarray or scalar
V : numeric
Device voltage [V]
IL : float64 numpy.ndarray or scalar

IL : numeric
Device photocurrent [A]
I0 : float64 numpy.ndarray or scalar

I0 : numeric
Device saturation current [A]
nNsVth : float64 numpy.ndarray or scalar

nNsVth : numeric
Device thermal voltage [V]
Rs : float64 numpy.ndarray or scalar

Rs : numeric
Device series resistance [Ohm]
Rsh : float64 numpy.ndarray or scalar

Rsh : numeric
Device shunt resistance [Ohm]

return_meta_dict : boolean scalar
Return additional computation metadata dictionary

Returns
-------
I : float64 numpy.ndarray
Device current [A]
meta_dict : python dictionary (optional, returned when return_meta_dict=True)

meta_dict : dictionary (optional, returned when return_meta_dict=True)
Metadata for computation
meta_dict['current_sum_at_diode_node'] : float64 numpy.ndarray like I
Sum of currents at the diode node in equivalent circuit model [A]

meta_dict['sdm_current_sum'] : float64 numpy.ndarray like I
Sum of currents at diode node in equivalent circuit model [A]

meta_dict['zero_Rs_idx'] : boolean numpy.ndarray like I
Indices where zero series resistance gives best solution

References
----------
[1] A. Jain, A. Kapoor, "Exact analytical solutions of the parameters of
real solar cells using Lambert W-function", Solar Energy Materials and
Solar Cells, 81 (2004) 269-277.
'''

# TODO Check if this qualifies as a numpy ufunc

# Ensure inputs are all np.ndarray with np.float64 type
V = np.asarray(V, np.float64)
IL = np.asarray(IL, np.float64)
Expand All @@ -2095,21 +2156,26 @@ def i_from_v_alt(V, IL, I0, nNsVth, Rs, Rsh, return_meta_dict=False):
# Default computation of I using zero_term keeps Rs shape info
zero_term = np.zeros_like(Rs)
I = IL - I0*np.expm1((V + zero_term)/nNsVth) - (V + zero_term)/Rsh
current_sum_at_diode_node_out = current_sum_at_diode_node(V=V, I=I, IL=IL, I0=I0, nNsVth=nNsVth, Rs=Rs, Rsh=Rsh)
sdm_current_sum_out = \
sdm_current_sum(V=V, I=I, IL=IL, I0=I0, nNsVth=nNsVth, Rs=Rs, Rsh=Rsh)

# Computation of I using LambertW for provided Rs
I_LambertW = i_from_v(Rsh, Rs, nNsVth, V, I0, IL)
current_sum_at_diode_node_LambertW = current_sum_at_diode_node(V=V, I=I_LambertW, IL=IL, I0=I0, nNsVth=nNsVth, Rs=Rs, Rsh=Rsh)
sdm_current_sum_LambertW = \
sdm_current_sum(V=V, I=I_LambertW, IL=IL, I0=I0, nNsVth=nNsVth, Rs=Rs, Rsh=Rsh)

# Compute selection indices (may be a scalar boolean)
nonzero_Rs_idx = np.logical_and(np.isfinite(current_sum_at_diode_node_LambertW), np.absolute(current_sum_at_diode_node_LambertW) <= np.absolute(current_sum_at_diode_node_out))

nonzero_Rs_idx = np.logical_and(np.isfinite(sdm_current_sum_LambertW), \
np.absolute(sdm_current_sum_LambertW) <= np.absolute(sdm_current_sum_out))

# These are always np.ndarray
I = np.where(nonzero_Rs_idx, I_LambertW, I)
current_sum_at_diode_node_out = np.where(nonzero_Rs_idx, current_sum_at_diode_node_LambertW, current_sum_at_diode_node_out)

sdm_current_sum_out = \
np.where(nonzero_Rs_idx, sdm_current_sum_LambertW, sdm_current_sum_out)

if return_meta_dict:
return I, {'current_sum_at_diode_node' : current_sum_at_diode_node_out, 'zero_Rs_idx' : np.array(np.logical_not(nonzero_Rs_idx))}
return I, {'sdm_current_sum' : sdm_current_sum_out, \
'zero_Rs_idx' : np.array(np.logical_not(nonzero_Rs_idx))}
else:
return I

Expand Down
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.