Skip to content
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

Function to calculate temperature coefficient of power for pvsyst #1190

Merged
merged 14 commits into from
Mar 18, 2021

Conversation

cwhanse
Copy link
Member

@cwhanse cwhanse commented Mar 9, 2021

  • [ ] Closes #xxxx
  • I am familiar with the contributing guidelines
  • Tests added
  • Updates entries to docs/sphinx/source/api.rst for API changes.
  • Adds description and name entries in the appropriate "what's new" file in 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`).
  • New code is fully documented. Includes numpydoc compliant docstrings, examples, and comments where necessary.
  • Pull request is nearly complete and ready for detailed review.
  • Maintainer: Appropriate GitHub Labels and Milestone are assigned to the Pull Request and linked Issue.

The temperature coefficient of power is not an input to the pvsyst single diode model. Rather, it is a result of the model, the derivative of power with respect to cell temperature, at the maximum power point, at reference conditions.

@cwhanse cwhanse added this to the 0.9.0 milestone Mar 15, 2021
@cwhanse cwhanse added enhancement SPI DOE SETO Solar Performance Insight project labels Mar 15, 2021
@cwhanse
Copy link
Member Author

cwhanse commented Mar 15, 2021

Ready for review

Comment on lines 16 to 17
from pvlib.pvsystem import singlediode, v_from_i
from pvlib.pvsystem import calcparams_pvsyst
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
from pvlib.pvsystem import singlediode, v_from_i
from pvlib.pvsystem import calcparams_pvsyst
from pvlib.pvsystem import calcparams_pvsyst, singlediode, v_from_i

-------
gamma_pmp : float
Temperature coefficient of power at maximum power point at reference
conditions. [%/C]
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 surprised to see %/C since the units of gamma_pdc are 1/C in pvwatts_dc. But I see we do use %/C in sdm.py. So either is fine with me at this point.

Copy link
Member Author

Choose a reason for hiding this comment

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

+1 to 1/C, more consistent with pvlib and the downstream PV Insight use. I probably cut/pasted from sdm.fit_cec_sam then didn't think about the interface again. We use %/C in sdm.fit_cec_sam because that's the quantity SAM requests.

Temperature coefficient of power at maximum power point at reference
conditions. [%/C]

p_mp_ref : float
Copy link
Member

Choose a reason for hiding this comment

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

A little strange to see gamma_pmp (p m p) and then p_mp_ref (p underscore m p)

Copy link
Member

Choose a reason for hiding this comment

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

Also a little strange to see pmp returned by a function that is ostensibly for finding temperature coefficient. I might argue that it would make more sense to either just return gamma_pmp or change the name of the function and also return vmp and imp. But I don't feel strongly about it.

Copy link
Member Author

Choose a reason for hiding this comment

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

Pvsyst doesn't name this parameter, so I'll call it gamma_pdc with units of 1/C, same as is used by the PVWatts model. If the function is returning a value with units of 1/C, then p_mp_ref isn't needed (it is if the unit is %/C).

res_hiT = pvsystem.singlediode(*params_hiT)
expected = (res_hiT['p_mp'] - res_lowT['p_mp']) / 2.
# convert to %/C
expected = expected * 100 / (0.5 * (res_hiT['p_mp'] + res_lowT['p_mp']))
Copy link
Member

Choose a reason for hiding this comment

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

I'd prefer for this value to be hard coded and for the function calls above to be removed.

expected = (res_hiT['p_mp'] - res_lowT['p_mp']) / 2.
# convert to %/C
expected = expected * 100 / (0.5 * (res_hiT['p_mp'] + res_lowT['p_mp']))
gamma_pdc, pdc_0 = sdm.pvsyst_temperature_coeff(**params)
Copy link
Member

Choose a reason for hiding this comment

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

I'm going to be a stickler again for passing args as args and kwargs as kwargs because it helps guard against unintended API changes.

Copy link
Member

Choose a reason for hiding this comment

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

or we can make them keyword-only arguments without defaults, though I don't think we've introduced that pattern in pvlib.

Comment on lines 104 to 109
* Added :py:func:`~pvlib.temperature.noct_sam`, a cell temperature model
implemented in SAM. (:pull:`1177`)
* Added :py:func:`~pvlib.ivtools.sdm.pvsyst_temperature_coeff` to calculate
the temperature coefficient of power for the pvsyst module model.
(:pull:`1190`)
implemented in SAM (:pull:`1177`, :pull:`1195`)
Copy link
Member

Choose a reason for hiding this comment

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

bad merge

params = {'alpha_sc': 0., 'gamma_ref': 1.1, 'mu_gamma': 0.,
'I_L_ref': 6., 'I_o_ref': 5.e-9, 'R_sh_ref': 200.,
'R_sh_0': 2000., 'R_s': 0.5, 'cells_in_series': 60}
expected = -0.7489980887568493
Copy link
Member

Choose a reason for hiding this comment

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

Seems a bit large for a realistic module. I don't have enough intuition with the params values to say more. I just want to double check that this really is the expected value and the function really is returning the right thing.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch. The value was in W/C. Fixed now.

Copy link
Member

@wholmgren wholmgren left a comment

Choose a reason for hiding this comment

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

merging tomorrow morning unless there is further discussion.

params = {'alpha_sc': 0., 'gamma_ref': 1.1, 'mu_gamma': 0.,
'I_L_ref': 6., 'I_o_ref': 5.e-9, 'R_sh_ref': 200.,
'R_sh_0': 2000., 'R_s': 0.5, 'cells_in_series': 60}
expected = -0.004886706494879083
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
expected = -0.004886706494879083
expected = -0.004887

Copy link
Member

Choose a reason for hiding this comment

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

In the past @adriesse has gently nudged us towards using reasonable precision when testing expected. While it can be a pain, I do think it makes the tests more understandable and makes it easier to catch issues like the one you fixed in the last commit.

Copy link
Member Author

@cwhanse cwhanse Mar 16, 2021

Choose a reason for hiding this comment

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

I don't object on principle to the suggestion. But here I think more precision is useful, in case the test goes wonky in the future, and someone is reconstructing the expected value to know they have that calculation the same as when the test was created.

Copy link
Member

Choose a reason for hiding this comment

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

I only vaguely recall the nudging occasion, but I have a general pet peeve about people producing too many digits of noise (e.g. NSRDB). You can't even compress it!

For tests I think it depends on what the test is aiming to accomplish. In this case you might see what happens if someone passes in float32 or maybe even float16. I often use float32.

Looking in the code now it seems like lots of potential changes/improvements in the functions that are called could affect the numeric result without breaking the fundamental calculation. Do you want that detected?

One thing you could perhaps consider would be to not rely on defaults when calling derivative. Or do away with derivative and call pmax twice yourself. Then the user doesn't have to go to scipy documentation to figure out what is actually being done.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good points about the options in scipy.misc.derivative. I didn't see an advantage to explicitly choosing options, or alternatively, hardcoding a particular derivative rule, but I'm open to doing either.

Peeve acknowledged and shared. The precision for expected is to enable reconstructing this value and being confident that the reconstruction is the same as the test originator's calculation.

I wasn't thinking about detecting small deviations in the output of pvsyst_temperature_coeff, only producing a value that is "good enough" in some sense. That's why I set rtol=0.0005; I think 4 digits agreement is enough to say two temperature coefficients are the same.

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, I hadn't noticed the rtol. I got mislead by all those digit thinking the must be there because you wanted them to be there.

Copy link
Member

Choose a reason for hiding this comment

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

@cwhanse do you want to make revisions or go ahead and merge? I agree that @adriesse makes good points, but I am also fine with saying go read the scipy documentation if you want that level of detail.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm OK merging this and improving it later, if issues arise. The only options are point spacing and order of the derivative rule, and the defaults (dx=1.0C, order=3) are reasonable for this case.

@wholmgren
Copy link
Member

thanks @cwhanse and @adriesse

@wholmgren wholmgren merged commit 3269dc7 into pvlib:master Mar 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement SPI DOE SETO Solar Performance Insight project
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants