-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Allow users to interact with root finders parameters #1764
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
Allow users to interact with root finders parameters #1764
Conversation
I support the initiative. I would be inclined to send **kwargs to optimizer so any other available options can be used. You could set pvlib defaults in this dictionary if they are absent and needed (or override incompatible options). |
Co-Authored-By: Anton Driesse <[email protected]>
Thanks for raising that point @adriesse ! |
Maybe try to describe the test objectives in English here to start? Maybe also play with the kwargs and see if interesting exceptions pop up? |
`maxiter` had to be changed so solutions converge near target value correctly.
Thanks for the insight @adriesse Exception has occurred: TypeError
newton() got an unexpected keyword argument '_inexistent_param'
File "C:\...\pvlib-python\pvlib\singlediode.py", line 297, in bishop88_i_from_v
vd = newton(func=lambda x, *a: fv(x, voltage, *a), x0=v0,
File "C:\...\pvlib-python\docs\examples\test.py", line 73, in <module>
bishop88_i_from_v(0, **bishop88_args, method=method, **kwargs)
TypeError: newton() got an unexpected keyword argument '_inexistent_param' From the traceback I feel it would be easy for a user to spot the error, I mean, if they are using I consider |
Co-Authored-By: Cliff Hansen <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks @echedey-ls
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall @echedey-ls! One minor edit and a bigger comment on testing below.
Co-Authored-By: Kevin Anderson <[email protected]>
This technically isn't a comment on the proposed changes, but whenever you touch an older piece of code other questions tend to come up. In this case:
|
Thanks for bringing that up @adriesse |
I guess that allowing people to play with the options is only one thing; the other is to provide feedback on the effect of those options. Does the full_output option carry through ok? Here's some more scope creep for fun: support the 'secant' method option by running newton with fprime=None. |
Of course not. But that result does not seem very important for an end user. Maybe to determine the pvlib defaults it is. Especially:
We can raise an error if that is passed, or return those results too (doesn't seem very difficult, just unpacking and a conditional return). |
I don't mind if you keep the changes minimal in this PR. I'm just throwing in some ideas that could make it potentially more useful. |
Missing tests Co-Authored-By: Anton Driesse <[email protected]>
Co-Authored-By: Kevin Anderson <[email protected]>
@adriesse I ended up doing your suggestion so that's easier to determine the optimal default params for the method(s) later. I feel tests and docs are right. I think this PR can be reviewed now. |
pvlib/singlediode.py
Outdated
@@ -247,22 +249,29 @@ def bishop88_i_from_v(voltage, photocurrent, saturation_current, | |||
method : str, default 'newton' | |||
Either ``'newton'`` or ``'brentq'``. ''method'' must be ``'newton'`` | |||
if ``breakdown_factor`` is not 0. | |||
method_kwargs : dict |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now this docstring entry is inconsistent with the actual function behavior. The difference is whether the function is used as:
bishop88_i_from_v(..., method_kwargs={'tol': 1e-5})
(matches the docstring)
or
bishop88_i_from_v(..., tol=1e-5)
(matches the function signature)
I think I somewhat prefer the first one. @adriesse?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. It looks a bit weird how kwargs
are documented. Example provided from numpydoc
:
**kwargs : dict, optional
Extra arguments to `metric`: refer to each metric documentation for a
list of all possible arguments.
However, in PVLIB (taken from pvlib.location.Location.get_clearsky) it is usually
kwargs
Extra parameters passed to the relevant functions. Climatological
values are assumed in many cases. See source code for details!
This stackoverflow post does give some more insight that could be useful.
I like the function signature way of passing parameters, given they are well documented, and document kwargs with **kwargs
without type definition.
If no objections are made, I'll add some use examples both for kwargs
and full_output=True
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the function signature way of passing parameters, given they are well documented, and document kwargs with
**kwargs
without type definition.
I didn't justify my opinion. Right now, **kwargs
is well spread in PVLIB (well, in docs it usually is kwargs
, without preceding **
) to pass parameters to other functions, so I feel the dict variant a bit disruptive in that sense. I added the asterisks cause I think they hold an important meaning. Of course I don't have a strong opinion and if you tell me otherwise I'll change it without problems.
Something that puzzles me a bit is using full_output
inside the functions, making it not just a keyword arg passed to the root finder. However, IMO this is a right approach for now. Adding a parameter for that seems redundant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought there might be others with opinions about this...
There's a similar situation in scipy least-squares
. I think having the extra arguments that will be passed to another function reside inside a dictionary (or a list of args
, like newton
does) clearly separates them from those that are used within.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I've changed the behaviour although that still feels like the pattern for a user-provided function.
tests (with few unrelated exceptions I believe), doctest examples are passing and all seems good to me. Do you mind having a look specially at the formatting of docs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess choosing the method name is an indirect way of giving a user-provided function. :)
I'm not that good at the doc formatting so I'll defer to others.
Can you have a look at the docs and tell me if anything is missing or not needed? They are linked in the body of the PR. |
Docs look fine to me. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docs look good to me too. A couple minor suggestions below, otherwise I think LGTM.
One kind of weird thing: using np.vectorize
in the method='brentq'
branch results in an array of scipy result
objects, while the result
for method='newton'
is a single vectorized result. I don't think anything needs to change about this PR, just wanted to point it out.
Co-authored by @kandersolar Co-authored-by: Kevin Anderson <[email protected]>
Thanks for suggesting the pythonic way to do that.
Good point. Honestly, I feel the solution was a bit over engineered, but that it will serve the purpose correctly. Thanks everyone for the reviews. |
Not unusual! :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @echedey-ls! This is a nice addition to pvlib.
- [x] Updates entries indocs/sphinx/source/reference
for API changes.docs/sphinx/source/whatsnew
for all changes. Includes link to the GitHub Issue with:issue:`num`
or this Pull Request with:pull:`num`
. Includes contributor name and/or GitHub username (link with:ghuser:`user`
).remote-data
) and Milestone are assigned to the Pull Request and linked Issue.Implementation details
I decided not to follow @cwhanse guidelines, since those two methods have some minimal difference in the arguments
xtol
(brentq
) andtol
(newton
). I think PVLIB is not responsible for such a use case where it needs to name them the same.kwargs
are passed to the root finders, checking that values for newton method are present and, if not, setting default values to them. This values are from before this PR. See this line of code.No error checking is made to
kwargs
. If any invalid arg is passed, then Python raises an error. See comment.Docs links
bishop88_i_from_v
bishop88_v_from_i
bishop88_mpp