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

Implement calcparams_pvsyst and tests #486

Merged
merged 16 commits into from
Jul 9, 2018
Merged

Implement calcparams_pvsyst and tests #486

merged 16 commits into from
Jul 9, 2018

Conversation

cwhanse
Copy link
Member

@cwhanse cwhanse commented Jun 20, 2018

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:

  • Closes Implement PVsyst single diode model #470
  • Fully tested. Added and/or modified tests to ensure correct behavior for all reasonable inputs. Tests must pass on the TravisCI and Appveyor testing services.
  • Code quality and style is sufficient. Passes git diff upstream/master -u -- "*.py" | flake8 --diff and/or landscape.io linting service.
  • New code is fully documented. Includes sphinx/numpydoc compliant docstrings and comments in the code where necessary.
  • Updates entries to docs/sphinx/source/api.rst for API changes.
  • Adds description and name entries in the appropriate 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):

Implement calcparams_pvsyst for use with singlediode

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.

Looks good to me except for a few doc string issues. I don't have any experience with the pvsyst model, so I'll leave it to others to comment on the details.


gamma_ref : float
The diode ideality factor

Copy link
Member

Choose a reason for hiding this comment

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

missing mugamma documentation here. mugamma or mu_gamma?

Copy link
Member Author

Choose a reason for hiding this comment

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

added. changed to mu_gamma

Copy link
Member

Choose a reason for hiding this comment

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

it appears that the change was not applied here.

The average cell temperature of cells within a module in C.

**kwargs
See pvsystem.calcparams_desoto for details
Copy link
Member

Choose a reason for hiding this comment

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

pvsyst, not desoto.

@cwhanse cwhanse changed the title initial version of calcparams_pvsyst and test Implement calcparams_pvsyst and tests Jun 20, 2018
@cwhanse
Copy link
Member Author

cwhanse commented Jun 20, 2018

@mikofski would appreciate your review of calcparams_pvsyst

Copy link
Member

@adriesse adriesse left a comment

Choose a reason for hiding this comment

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

We will certainly exercise this code during the next few months!

photocurrent : numeric
Light-generated current in amperes at irradiance=S and
cell temperature=Tcell.

Copy link
Member

Choose a reason for hiding this comment

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

temp_cell

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 edited the output description (and in pvsystem.calcparams_desoto) to remove the phrase 'at irradiance....=Tcell' I think the context is clear.

saturation_current : numeric
Diode saturation curent in amperes at irradiance
S and cell temperature Tcell.

Copy link
Member

Choose a reason for hiding this comment

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

temp_cell

resistance_series : float
Series resistance in ohms at irradiance S and cell temperature
Tcell.

Copy link
Member

Choose a reason for hiding this comment

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

temp_cell

resistance_shunt : numeric
Shunt resistance in ohms at irradiance S and cell temperature
Tcell.

Copy link
Member

Choose a reason for hiding this comment

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

temp_cell

# reference temperature
Tref_K = temp_ref + 273.15
Tcell_K = temp_cell + 273.15

Copy link
Member

Choose a reason for hiding this comment

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

temp_ref_K ?
temp_cell_K ?

Copy link
Member Author

Choose a reason for hiding this comment

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

these are local variables, so I think its OK that they are different in style.

Copy link
Member Author

Choose a reason for hiding this comment

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

So I changed them as you suggested :)

R_s : float
The series resistance at reference conditions, in ohms.

cells_in_series : integer
Copy link
Contributor

Choose a reason for hiding this comment

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

Ns would seem more consistent with the desoto model and the nNsVth variable.

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 agree but we use cells_in_series as the key for the SAPM parameters, and some have a preference for descriptive variable names.

R_sh_0 : float
The shunt resistance at zero irradiance conditions, in ohms.

R_s : float
Copy link
Contributor

Choose a reason for hiding this comment

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

R_s_ref seems more consistent with other variables at reference conditions.

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 agree. But here we run into an unfortunate linkage to the SAM database for the CEC model parameters. R_s is the heading in that file, which is used to define the keys for the module parameter dict. Although in my opinion R_s for PVsyst is a different parameter than R_s for De Soto or CEC (because parameter values are determined jointly, they are intrinsically linked and can't be regarded as correct for a different model) I do think it's useful to maintain a common nomenclature.

I think changing R_s to R_s_ref will involve some work in pvsystem.retrieve_sam so I'm not going to take that on in this PR.

gamma_ref : float
The diode ideality factor

mugamma : float
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps mu_gamma?

Copy link
Member Author

Choose a reason for hiding this comment

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

yes

Tuple of the following results:

photocurrent : numeric
Light-generated current in amperes at irradiance=S and
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure about irradiance=S here and below.

Copy link
Member Author

Choose a reason for hiding this comment

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

nuked

@markcampanelli
Copy link
Contributor

@cwhanse Are you not planning to integrate the pvsyst model into ModelChain in this PR?

@markcampanelli
Copy link
Contributor

@mikofski This was by no means a criticism. Rather, I was just wondering if complete integration was expected right away. LIkewise, #478 is much more easily implemented if I can worry about the ModelChain integration as a followup step.

@cwhanse I do propose this PR adds a usage example in a subsection of docs/tutorials/pvsystem.ipynb.

@mikofski
Copy link
Member

I'm so sorry @thunderfish24 my comment was snarky. It was a fair question.

@markcampanelli
Copy link
Contributor

@mikofski No worries mate! My question could have been phrased better, and it's often easy to misinterpret typings when we are not face-to-face.

@cwhanse
Copy link
Member Author

cwhanse commented Jun 21, 2018

@mikofski @thunderfish24 yes I'll open a new Issue and PR for extending ModelChain for new single diode models.

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.

A couple more minor things but otherwise looks good to me.

@@ -13,6 +13,7 @@ Enhancements
* Add sea surface albedo in irradiance.py (:issue:`458`)
* Implement first_solar_spectral_loss in modelchain.py (:issue:`359`)
* Clarify arguments Egref and dEgdT for calcparams_desoto (:issue:`462`)
* Add pvsystem.calcparams_pvsyst to compute values for the single diode equation using the PVsyst v6 model.
Copy link
Member

Choose a reason for hiding this comment

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

(:issue:470)


gamma_ref : float
The diode ideality factor

Copy link
Member

Choose a reason for hiding this comment

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

it appears that the change was not applied here.

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.

Can you look into setting your python file editor/IDE to strip extra white space from the end of lines in your new code? This makes diffs much easier to understand in the long run.

def pvsyst_module_params():
module_parameters = {}
module_parameters['gamma_ref'] = 1.05
module_parameters['mugamma'] = 0.001
Copy link
Member

Choose a reason for hiding this comment

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

test fixture and the two tests below need mugamma --> mu_gamma

@cwhanse
Copy link
Member Author

cwhanse commented Jun 27, 2018

I changed a setting in Spyder, hopefully that improves the white space issue. Test failures are for read_tmy3

@@ -307,6 +307,36 @@ def calcparams_desoto(self, effective_irradiance, temp_cell, **kwargs):

return calcparams_desoto(effective_irradiance, temp_cell, **kwargs)

def calcparams_pvsyst(self, effective_irradiance, temp_cell, **kwargs):
Copy link
Member

Choose a reason for hiding this comment

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

similar to what was pointed out by @thunderfish24 in #478... the **kwargs in this method signature don't do anything, so we should remove **kwargs from the method signature. To be clear, we need to keep them in the function call below.

Copy link
Member Author

Choose a reason for hiding this comment

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

done

kwargs = _build_kwargs(['gamma_ref', 'mu_gamma', 'I_L_ref', 'I_o_ref',
'R_sh_ref', 'R_sh_0', 'R_sh_exp',
'R_s', 'alpha_sc', 'EgRef',
'cells_in_series'],
Copy link
Member

Choose a reason for hiding this comment

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

should 'irrad_ref' and 'temp_ref' be added to this lookup list? I'd say yes because they seem intrinsic to the module specification.

Copy link
Member Author

Choose a reason for hiding this comment

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

done. I also added these keywords to calcparams_desoto

The average cell temperature of cells within a module in C.

**kwargs
See pvsystem.calcparams_pvsyst for details
Copy link
Member

Choose a reason for hiding this comment

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

and delete **kwargs from the docstring.

Copy link
Member Author

Choose a reason for hiding this comment

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

done

@wholmgren
Copy link
Member

I think this should be merged after resolving the minor issues around **kwargs

@wholmgren wholmgren added this to the 0.6.0 milestone Jul 5, 2018
@wholmgren
Copy link
Member

wholmgren commented Jul 5, 2018

@cwhanse ok with me if you go ahead and "squash and merge" and then delete the branch from the organization's repository. It should give you a delete option right here once the branch is merged.

Copy link
Contributor

@markcampanelli markcampanelli left a comment

Choose a reason for hiding this comment

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

Sorry for my late review. I did have one question about the model that I'd like clarification on. Everything else is small potatoes.

model described in [1,2,3]. The five values returned by calcparams_pvsyst
can be used by singlediode to calculate an IV curve.

Parameters
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the difference between numeric and float?

Should a user NOT expect this function to be vectorized over the "float" parameters?

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 pretty sure the function will work on array or Series inputs. Is the issue the type description float implies a singleton?

We aren't using numeric or float consistently in the comments. For me to fix that, requires that I learn the distinction. New issue, I think.

Copy link
Member

Choose a reason for hiding this comment

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

I've used numeric for input that can be scalar, np.array, or pd.Series. float is specific to scalar. Whether or not this is a good idea could be discussed in a separate issue, if necessary.

Copy link
Member

Choose a reason for hiding this comment

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

for more context, note that some functions require "array-like" (np.array or pd.Series) and others require pd.Series.

Copy link
Member Author

Choose a reason for hiding this comment

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

So, numeric unless there's a reason to specify a scalar float?

Copy link
Member

Choose a reason for hiding this comment

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

or unless there's a reason to specify array-like or Series.

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 looked at both calcparams_desoto and calcparams_pvsyst and I'm going to leave the docstrings as is for now. The typical use of either function is to pass model parameters for one module and a list of conditions (irradiance and temperature). That use is reflected in the docstring's use of numeric and float, where each model parameter is a single value for a module. The functions are more flexible that this use case.

If we want to overhaul the docstrings, let's open a new issue for it.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think an issue is wise in order to define what we mean to ourselves/users and make everything consistent.

saturation_current : numeric
Diode saturation current in amperes

resistance_series : float
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like this should be "numeric" too.


resistance_series : float
Series resistance in ohms at irradiance S and cell temperature
Tcell.
Series resistance in ohms
Copy link
Contributor

Choose a reason for hiding this comment

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

While passing through this territory, seems like this should be changed to "numeric" from "float".

Copy link
Member Author

Choose a reason for hiding this comment

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

Cut and paste inheritance? I don't think there is a difference here. numeric would admit long, complex and int types as well as float. Probably should change to float unless there's a reason otherwise.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm good with float instead of numeric, because that seems to be most accurate.

'cells_in_series'],
self.module_parameters)

return calcparams_pvsyst(effective_irradiance, temp_cell, **kwargs)
Copy link
Contributor

Choose a reason for hiding this comment

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

This could be done in a separate issue (see #476), but I think the PVSystem methods should scale the outputs to the modules_per_string and strings_per_inverter.

Copy link
Member Author

Choose a reason for hiding this comment

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

We've chosen not to scale inside dc_model functions to offer flexibility, e.g., model a system with strings of different lengths, to implement module-to-module mismatch models, etc. Granted some of these capabilities are not presently in pvlib.

'''

# Boltzmann constant in J/K
k = 1.3806488e-23
Copy link
Contributor

Choose a reason for hiding this comment

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

Appears to differ a bit from scipy.constants.Boltzmann, which is 1.38064852e-23

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. That's a typo.

Copy link
Member

Choose a reason for hiding this comment

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

ref #483.

(np.exp((q * EgRef) / (k * gamma) * (1 / Tref_K - 1 / Tcell_K)))

Rsh_tmp = (R_sh_ref - R_sh_0 * np.exp(-R_sh_exp)) / (1.0 - np.exp(-R_sh_exp))
Rsh_base = np.maximum(0.0, Rsh_tmp)
Copy link
Contributor

Choose a reason for hiding this comment

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

In looking over http://files.pvsyst.com/help/pvmodule_rshexp.htm, it is not clear to me why Rsh_base is not simply R_sh_ref. What am I missing?

Copy link
Member Author

Choose a reason for hiding this comment

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

You're not, PVsyst documentation is missing that definition of Rsh_base. Ken Sauer and Thomas Roessler are my sources, they got it verbally from PVsyst.

nNsVth = gamma * k / q * cells_in_series * Tcell_K

IL = effective_irradiance / irrad_ref * \
(I_L_ref + alpha_sc * (Tcell_K - Tref_K))
Copy link
Contributor

Choose a reason for hiding this comment

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

Optimization possibility: Tcell_K - Tref_K is computed twice.

Rsh_base = np.maximum(0.0, Rsh_tmp)

Rsh = Rsh_base + (R_sh_0 - Rsh_base) * \
np.exp(-R_sh_exp * effective_irradiance / irrad_ref)
Copy link
Contributor

Choose a reason for hiding this comment

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

Optimization possibility: effective_irradiance / irrad_ref is computed twice.

assert_series_equal(np.round(Rsh, 3),
pd.Series([1000.0, 305.757], index=times))
assert_series_equal(np.round(nNsVth, 4),
pd.Series([1.6186, 1.7961], index=times))
Copy link
Contributor

Choose a reason for hiding this comment

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

My preference is to compute the expected values directly in the test. I'm curious if you have particular reasons not to do that?

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 don't other than it was easy (for me) to compute the values in MATLAB and hardcode them here. My way is certainly less transparent.

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 confused. I don't understand how general test values can be computed inside a test without duplicating the function itself.

Copy link
Contributor

Choose a reason for hiding this comment

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

Some view including a separate (re)computation as redundant, others view it as transparent. In this case I don't think any of the computations require an implicit solver, so where the values come from should not leave too much to question.

@@ -386,6 +403,35 @@ def test_calcparams_desoto(cec_module_params):
assert_allclose(nNsVth, 0.473)


def test_calcparams_pvsyst(pvsyst_module_params):
Copy link
Contributor

Choose a reason for hiding this comment

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

Not critical, but you might consider a simple "API" test that passes the five computed coefficients into singlediode just to make sure the computation runs. This makes sure that pvsyst gets updated if the API changes.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fair point. I'm not going to put a new test in here because I don't clearly see how to separate issues being tested, and to avoid redundant tests. The current test_calcparams_pvsyst verifies correct output of pvsystem.calcparams_pvsyst. Adding a call to singlediode within test_calcparams_pvsyst adds a new purpose (API check) and I prefer to keep tests to a single purpose. Adding a new test that calls calcparams_pvsyst and then singlediode would duplicate the test of PVSystem.calcparams_pvsyst method.

Do you have an example in the campanelli pull request?

Copy link
Contributor

Choose a reason for hiding this comment

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

No, I haven't written any tests yet and I admit that I am not familiar with this particular mocker setup. If a mock call can verify the API, then that would be great. I don't mean to hold up this PR and I'll look into it further with the Campanelli model.

@@ -174,6 +174,23 @@ def cec_module_params(sam_data):
return module_parameters


@pytest.fixture(scope="session")
Copy link
Member

Choose a reason for hiding this comment

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

not a big deal, but if we're still making changes this should not have scope="session"

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 copied that. scope="module"?

Copy link
Member

@wholmgren wholmgren Jul 6, 2018

Choose a reason for hiding this comment

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

no scope argument. @pytest.fixture and nothing else

Copy link
Member

Choose a reason for hiding this comment

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

The reasoning for this is that this function returns a mutable dictionary. In principle, one test could inadvertently modify the dictionary, and a second test could pass or fail because of that modification. @pytest.fixture will call the function and generate a new dictionary each time the data is needed. Adding scope="module" or scope="session" instructs pytest to recreate the data only once per test module (the fixture could be imported into a new module and data would be regenerated) or once per test session (this defeats a large part of the purpose of fixtures).

Copy link
Member Author

@cwhanse cwhanse Jul 6, 2018

Choose a reason for hiding this comment

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

OK. I'll remove this instance of scope=session from the pvsyst module parameter fixture. Thanks for the explanation.

@cwhanse
Copy link
Member Author

cwhanse commented Jul 6, 2018

Looks like errors related to pulling tmy3 data from NREL's site.

Copy link
Contributor

@markcampanelli markcampanelli left a comment

Choose a reason for hiding this comment

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

LGTM

@wholmgren
Copy link
Member

@thunderfish24 thanks for your careful review -- it's a big help.

@cwhanse ready to merge?

@cwhanse
Copy link
Member Author

cwhanse commented Jul 9, 2018

Yes, ready to merge.

@wholmgren wholmgren merged commit ee03ed4 into master Jul 9, 2018
@wholmgren wholmgren deleted the calcparams_pvsyst branch July 9, 2018 20:35
@@ -13,6 +13,7 @@ Enhancements
* Add sea surface albedo in irradiance.py (:issue:`458`)
* Implement first_solar_spectral_loss in modelchain.py (:issue:`359`)
* Clarify arguments Egref and dEgdT for calcparams_desoto (:issue:`462`)
* Add pvsystem.calcparams_pvsyst to compute values for the single diode equation using the PVsyst v6 model (:issue:'470')
Copy link
Member

@mikofski mikofski Aug 3, 2018

Choose a reason for hiding this comment

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

please change single spaces around issue #470 to backticks, and also surround pvsystem.calcparams_pvsyst with backticks - @wholmgren or @cwhanse , can you do this directly in master, thx

Add `pvsystem.calcparams_pvsyst` to compute values for the single diode equation using the PVsyst v6 model (:issue:`470`)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants