-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Expose first_solar_spectral_correction #466
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
Conversation
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.
This is great!
'fs_spectral_coefficients'
or 'first_solar_spectral_coefficients'
? The latter is pretty long, but users that are less familiar with the library might be tripped up by the abbreviation. I lean towards spelling it out for consistency with the function/method calls.
The PVSystem._infer_cell_type
is a nice addition. I think you're right to make it a private method for now. But I would reconsider if anyone reading this says that they would make use of it now.
We could call self._cell_type = self._infer_cell_type()
as part of the PVSystem
init. Or self.cell_type
. Probably not a great idea, just thinking aloud.
Should be relatively straight forward to add a few simple tests for PVSystem.first_solar_spectral_correction
. Updating the ModelChain tests is going to be a little painful. Many of the values will change now that this correction is applied by default. I can look at that more carefully later.
pvlib/pvsystem.py
Outdated
coefficients = self.module_parameters['fs_spectral_coefficients'] | ||
module_type = None | ||
else: | ||
module_type = self._infer_cell_type() |
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.
module vs. cell is unfortunate here. No objection to what you've done, but we might need a separate discussion to try to clean up the terminology within the library.
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.
Here I'm inclined to break the API and change the keyword argument from module_type to cell_type. module_type is only used by the first_solar function.
pvlib/pvsystem.py
Outdated
module_type = self._infer_cell_type() | ||
coefficients = None | ||
|
||
if (module_type is not None and coefficients is None) or \ |
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.
Recommend removing this if/else statement. If this method doesn't have the information necessary to successfully compute the spectral correction, then this method should say so rather than defaulting to 1.
Consistent with Python's style, the atmosphere.first_solar_spectral_correction
function throws a TypeError
with some hint as to what the user needs to supply. We could (ideally/optionally) catch that error here and reraise it with a more specific message for how to modify module_parameters
to make this work.
I am guessing that raising an exception here will cause issues in a ModelChain calculation for the few module/cell types for which _infer_cell_type
mapping returns None
. One approach to avoid that may be to make the ModelChain.infer_spectral_model
preemptively call pvsystem._infer_cell_type
.
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 idea. I'll move the test to ModelChain.infer_spectral_model and put a warning in place of that check in the actual functions.
pvlib/pvsystem.py
Outdated
Returns | ||
------- | ||
modifier: array-like | ||
spectral mismatch factor (unitless) which is can be multiplied |
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.
typo (is can) that is carried over from the original function's doc string.
I looked it over and didn't find anything worth bothering you about. :) |
I'm tripping over function name conventions again. Related to this PR, but maybe it merits its own Issue and PRs. For spectral modifier models, we have I have a strong preference to have the model purpose as the prefix, followed by the model name, e.g., Thoughts? |
Good point. I also prefer the naming schemes you describe. Perhaps the easiest way forward for this PR is to continue with This is also related to the discussion in #436. And #427 adds machinery for deprecating function names without immediately breaking user code. |
I remember that discussion. I'll complete this PR with function names as is, let's tackle the naming issue in a separate PR. @wholmgren I'm going to need some guidance on implementing the tests. Looks like we need a new I do not understand the code in the current |
If you want to compare to matlab, I recommend ensuring that the For |
@wholmgren when you can, a little help to understand why these tests are failing? I have the decorator wrong but I don't understand why. |
Traveling this weekend. Will check Tuesday.
…On Sat, May 26, 2018 at 2:26 PM Cliff Hansen ***@***.***> wrote:
@wholmgren <https://github.com/wholmgren> when you can, a little help to
understand why these tests are failing? I have the decorator wrong but I
don't understand why.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#466 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AELiR5FQxTEmiW8poM3lPrCHfDCFPZl9ks5t2ch3gaJpZM4UKygZ>
.
|
Progress. I grok the test now. The code I submitted is following the pattern for When we test individual functions like the spectral models we have to know the expected output. So I made up values for I see three ways to resolve the problem, neither of which are appealing:
What is preferred here? I'll proceed with the first option, but perhaps there's a fourth option I don't see. |
Perhaps it is not necessary to check the precise values that are returned using the ModelChain. If the spectral calculation function is already verified to be correct through its own tests, then you really just need to test/exercise the additional code related to the ModelChain integration. It might be enough to check that you get a Series of floats or something like that, I'm not sure. I suppose the inference code would need to be tested somehow too. That might be easier if the result of any inferences were explicitly stored in the ModelChain, so a test (or a user) could check what happened. |
Let me first try to state my current thinking about how to structure unit tests in pvlib in the context of this pull request.
That thinking has evolved from the many mistakes that I have made in writing pvlib tests, too many of which are still with us. The function-level tests in item 1 are relatively straight forward so long as we avoid the dependency issue @cwhanse pointed out in #394. I used to be bad and lazy about that, so there are some older tests that have dependencies that they shouldn't have. The Moving forward to comments from @cwhanse and @adriesse
Agreed.
We will want tests that only use a
Agreed -- I think we can come up with a better way.
Agreed, though I do want to emphasize the importance (in my mind) of separating tests at the level of
The inferences are stored in the sense that a specific model's corresponding method e.g. How to move forward? I will start by learning more about pytest and mock in an effort to design test templates that let us address items 2 and 3 while avoiding unintended dependencies. I'll look into that this week, though it may take longer to come up with a good solution. I am also ok moving forward with something like @cwhanse's option 1, but it's complicated by the fact that some of the existing model chain tests break due to the corrections applied by this new feature. Finally, we should add some text and examples to the contributing section of the documentation if we can come to some agreement about testing principles and practice. |
This might be easier than I thought... See this commit for an example of using pytest-mock with the I don't fully understand the pros/cons of using pytest-mock vs. pure mock, but this seems doable either way. |
@wholmgren many thanks for your thoughts. I haven't yet looked at the pytest-mock example. In the interim, in the interest of getting something working, I changed the pvsystem module test to verify that the PVSystem method returns one correct (precalculated) value, and the modelchain module test to only verify that the ModelChain method returns a Series, float or int types. I think this is the spirit of the test distinctions you outline, and if this works, we can certainly add more conditions to each test. |
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 think this is nearly ready. I made some minor comments below. We'll also need an entry in the whats new file.
pvlib/test/test_pvsystem.py
Outdated
@@ -266,6 +266,17 @@ def test_PVSystem_sapm_spectral_loss(sapm_module_params): | |||
out = system.sapm_spectral_loss(airmass) | |||
|
|||
|
|||
@pytest.mark.parametrize("expected", [1.03173953]) | |||
|
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.
python convention is no blank lines between decorators and the function.
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.
Fixed
pvlib/test/test_modelchain.py
Outdated
expected = pd.Series(np.array(expected), index=times) | ||
assert_series_equal(ac, expected, check_less_precise=2) | ||
spectral_modifier = mc.run_model(times=times, weather=weather).spectral_modifier | ||
print(spectral_modifier) |
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 try to avoid putting print functions in the final tests. In case you are not already aware of it, the pytest --pdb
option can be useful for avoiding print in development e.g. pytest pvlib/test/test_modelchain.py::test_spectral_models --pdb
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.
Removed, I had that print to debug the test.
pvlib/test/test_modelchain.py
Outdated
(constant_spectral_loss, [163.061569511, -2e-2]) | ||
]) | ||
def test_spectral_models(system, location, spectral_model, expected): | ||
@pytest.mark.parametrize('spectral_model', ['sapm', 'first_solar', 'no_loss']) |
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.
please keep constant_spectral_loss
in the list of parameters. This tests that a user-supplied function can be passed in as an argument. To be clear, the list should be: ['sapm', 'first_solar', 'no_loss', constant_spectral_loss]
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.
constant_spectral_loss --> constant_loss ?
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 put the keyword back. I tend to agree with @adriesse about 'loss' but the terminology is common. Maybe the time to alter usage is when (if) we revise function naming patterns, because changing 'loss' will alter the API.
pvlib/pvsystem.py
Outdated
|
||
""" | ||
Use the :py:func:`first_solar_spectral_correction` function to | ||
calculate the spectral loss modifier. |
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.
Doc string should have some kind of reference to self.module_parameters
. Most of the other PVSystem
method doc strings have some examples. This method is a little more complicated though, so might be worth being more explicit about what is looked for. For example...
Use the :py:func:first_solar_spectral_correction
function to calculate the spectral loss modifier. The spectral loss modifier is determined by searching for one of the following keys in self.module_parameters
(in order): 'first_solar_spectral_coefficients'
, 'Technology'
, 'Material'
.
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.
Use the :py:func:`first_solar_spectral_correction` function to
calculate the spectral loss modifier. The model coefficients are
specific to the module's cell type, and are determined by searching
for one of the following keys in self.module_parameters (in order):
'first_solar_spectral_coefficients (user-supplied coefficients)
'Technology' - a string describing the cell type, can be read from
the CEC module parameter database
'Material' - a string describing the cell type, can be read from
the Sandia module database.
While browsing the code, it struck me that the word "loss" is used quite a lot. In contrast to many other loss types, this one can be a gain also and pvlib should perhaps reflect this by using a more neutral word (or "gain/loss"). Only where it is convenient to do so of course. |
…t implemented in modelchain.py docstring
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.
Minor typo, then we are ready to merge.
pvlib/pvsystem.py
Outdated
calculate the spectral loss modifier. The model coefficients are | ||
specific to the module's cell type, and are determined by searching | ||
for one of the following keys in self.module_parameters (in order): | ||
'first_solar_spectral_coefficients (user-supplied coefficients) |
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.
missing ' -
@adriesse I am open to discussing and changing the "loss" terminology as part of a separate issue. |
pvlib python pull request guidelines
Thank you for your contribution to pvlib python!
You may submit a pull request with your code at any stage of completion, however, before the code can be merged the following items must be addressed:
git diff upstream/master -u -- "*.py" | flake8 --diff
and/or landscape.io linting service.docs/sphinx/source/api.rst
for API changes.docs/sphinx/source/whatsnew
file for all changes.Please don't hesitate to ask for help if you're unsure of how to accomplish any of the above. You may delete all of these instructions except for the list above.
Brief description of the problem and proposed solution (if not already fully described in the issue linked to above):
#359 complete