Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0b147e8
functions and tests
cwhanse Sep 16, 2024
480c01b
fix test
cwhanse Sep 19, 2024
22d51ee
build doc pages
cwhanse Sep 19, 2024
d65b57f
really fix test
cwhanse Sep 19, 2024
bc01aa7
add kwargs
cwhanse Sep 23, 2024
ae4346b
docstring edits
cwhanse Sep 23, 2024
1901b20
Merge branch 'main' of https://github.com/pvlib/pvlib-python into sdm…
cwhanse Sep 25, 2024
3dbbefd
Merge branch 'main' of https://github.com/pvlib/pvlib-python into sdm…
cwhanse Sep 26, 2024
30cda13
update to main
cwhanse Sep 30, 2025
ef87465
adapt to new organization
cwhanse Sep 30, 2025
5188ee9
paths, names, reference
cwhanse Sep 30, 2025
a181438
fixes
cwhanse Oct 1, 2025
3657298
why does cells_in_series have to be special
cwhanse Oct 1, 2025
11ce601
correct IEC61853 matrix, add comments
cwhanse Oct 1, 2025
c62e6d1
use find_minimum in lambertw for mpp
cwhanse Oct 3, 2025
ac500d5
remove switch between find_minimum and golden mean
cwhanse Oct 3, 2025
4827334
add removal comment
cwhanse Oct 3, 2025
b36b297
whatsnew
cwhanse Oct 3, 2025
edb5535
remove multiplier on voc for initial interval
cwhanse Oct 3, 2025
5c79696
adjust multiplier for initial guess
cwhanse Oct 3, 2025
2bdea49
remake expected test output, add kwargs
cwhanse Oct 6, 2025
facdd7e
indents
cwhanse Oct 6, 2025
ff20bef
format
cwhanse Oct 6, 2025
92dcb4d
add some print statements to debug test failure
cwhanse Oct 6, 2025
1d4d40c
rtol by parameter
cwhanse Oct 6, 2025
75a55f7
format
cwhanse Oct 6, 2025
a754adf
Update docs/sphinx/source/whatsnew/v0.13.2.rst
cwhanse Oct 9, 2025
c748752
Merge branch 'golden_opt' of https://github.com/cwhanse/pvlib-python …
cwhanse Oct 9, 2025
c08abb3
Merge branch 'main' of https://github.com/pvlib/pvlib-python into gol…
cwhanse Oct 9, 2025
9ff3970
undo bad pull
cwhanse Oct 10, 2025
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
6 changes: 4 additions & 2 deletions docs/sphinx/source/whatsnew/v0.13.2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ Enhancements
:py:func:`~pvlib.singlediode.bishop88_mpp`,
:py:func:`~pvlib.singlediode.bishop88_v_from_i`, and
:py:func:`~pvlib.singlediode.bishop88_i_from_v`. (:issue:`2497`, :pull:`2498`)

* Add capability for :py:func:`~pvlib.singlediode._lambertw` to use scipy
find_minimum to get the maximum power point instead of pvlib's golden
mean search. (:issue:`2497`, :pull:`2567`)


Documentation
Expand All @@ -52,4 +54,4 @@ Maintenance

Contributors
~~~~~~~~~~~~

* Cliff Hansen (:ghuser:`cwhanse`)
28 changes: 26 additions & 2 deletions pvlib/singlediode.py
Original file line number Diff line number Diff line change
Expand Up @@ -913,8 +913,23 @@ def _lambertw(photocurrent, saturation_current, resistance_series,
v_oc = 0.

# Find the voltage, v_mp, where the power is maximized.
# Start the golden section search at v_oc * 1.14
p_mp, v_mp = _golden_sect_DataFrame(params, 0., v_oc * 1.14, _pwr_optfcn)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious... I wonder if anyone knows why 1.14 is the magic number. I see it goes back to the early early days of pvlib-python.

Copy link
Member Author

@cwhanse cwhanse Oct 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was 1.14 * Voc_ref, the STC value of Voc. The factor of 1.14 was meant to push the right endpoint out when temperature is lower than STC.

In pvlib, v_oc reflects temperature and irradiance. I included 1.01 * v_oc in case the algorithm needs to start with a negative value of power at the right endpoint, since it is minimizing negative power. Now that I write this, I don't think that's necessary - it is provable that power has a unique maximum between v=0 and v=v_oc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering why it is 1.14 * Voc_ref rather than (for example) 1.15 * Voc_ref or 1.13 * Voc_ref or 1.2 * Voc_ref. Maybe computed from some suitable low temperature and a representative temperature coefficient? Just idle curiosity.

# use scipy.elementwise if available
# remove try/except when scipy>=1.15, and golden mean is retired
try:
from scipy.optimize.elementwise import find_minimum
init = (0., 0.8*v_oc, v_oc)
res = find_minimum(_vmp_opt, init,
args=(params['photocurrent'],
params['saturation_current'],
params['resistance_series'],
params['resistance_shunt'],
params['nNsVth'],))
v_mp = res.x
p_mp = -1.*res.f_x
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test failure is because the test expects i_mp=0 for photocurrent=0, but currently it's getting back i_mp=nan on some platforms. Maybe because photocurrent=0 violates the requirement of init:

The abscissae of a standard scalar minimization bracket. A bracket is
valid if arrays x1, x2, x3 = init satisfy x1 < x2 < x3 and
func(x1) >= func(x2) <= func(x3), where one of the inequalities
is strict.

We could detect photocurrent==0 and skip the optimizer for those cases?

except ModuleNotFoundError:
# switch to old golden section method
p_mp, v_mp = _golden_sect_DataFrame(params, 0., v_oc * 1.14,
_pwr_optfcn)

# Find Imp using Lambert W
i_mp = _lambertw_i_from_v(v_mp, **params)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not really related to this PR, but can't we avoid an unnecessary calculation here and just do i_mp = p_mp / v_mp?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also unrelated: since we removed the ivcurve_pnts kwarg from singlediode (several versions ago), should we also delete that capability from _lambertw?

Expand All @@ -938,6 +953,15 @@ def _lambertw(photocurrent, saturation_current, resistance_series,
return out


def _vmp_opt(v, iph, io, rs, rsh, nNsVth):
'''
Function to find negative of power from ``i_from_v``.
'''
current = _lambertw_i_from_v(v, iph, io, rs, rsh, nNsVth)

return -v * current


def _pwr_optfcn(df, loc):
'''
Function to find power from ``i_from_v``.
Expand Down
Loading