Skip to content

Add function to fit Sandia inverter model #1011

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

Merged
merged 17 commits into from
Aug 14, 2020
Merged

Conversation

cwhanse
Copy link
Member

@cwhanse cwhanse commented Jul 24, 2020

  • Closes Implement a function to calculate inverter model parameters #975
  • 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.

Inputs are: efficiency curves with DC voltage and AC power, rated AC power and night tare. Returns dict ready for the Sandia inverter model function. The algorithm reference is on the PVPMC site.

@cwhanse cwhanse added this to the 0.8.0 milestone Jul 24, 2020
Columns must be ``'fraction_of_rated_power'``, ``'dc_voltage_level'``,
``'dc_voltage'``, ``'ac_power'``, ``'efficiency'``. See notes for the
definition and unit for each column.
p_ac_0 : numeric
Copy link
Member

Choose a reason for hiding this comment

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

Is it intentional that p_ac_0 and p_nt are spelled differently here than in inverter.sandia? Seems like these two functions would benefit from a consistent interface.

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, which would you prefer? p_ac_0 is more appealing than Pac0 as a program variable. The inertia is with Pac0, which is the column key for this parameter read from the SAM files.

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 clear to me where the balance should be struck between (1) presenting a consistent interface across functions or (2) maintaining as clear a connection as possible to the original model definitions. I think I would lean towards p_ac_0 over Pac0 with the hope that existing Pac0s would be changed to match.

Copy link
Member Author

Choose a reason for hiding this comment

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

That is my preference as well. The function returns a dict ready for pvlib.inverter.sandia so at least in one direction the two are compatible.

Parameters
----------
curves : DataFrame
Columns must be ``'fraction_of_rated_power'``, ``'dc_voltage_level'``,
Copy link
Member

Choose a reason for hiding this comment

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

Is fraction_of_rated_power actually required? I don't see it getting used anywhere.

Copy link
Member Author

@cwhanse cwhanse Aug 4, 2020

Choose a reason for hiding this comment

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

The values are not used explicitly, but I think it would be confusing to describe the needed input without fraction_of_rated_power. If working from a datasheet, efficiency in usually given in terms of fraction_of_rated_power rather than ac_power.

@cwhanse
Copy link
Member Author

cwhanse commented Aug 5, 2020

Test failures are due to #1018

Copy link
Member

@kandersolar kandersolar left a comment

Choose a reason for hiding this comment

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

LGTM. Idea for a future PR: I think pairing the fit functions with the associated models would make for good gallery examples.

@cwhanse
Copy link
Member Author

cwhanse commented Aug 12, 2020

@Peque any comments or edits to this before we merge?

@wholmgren
Copy link
Member

I'm not going to make much use of this function so my comments come from an API design/consistency point of view...

I generally want to avoid DataFrame input to functions because the API should be as self-documenting as possible. Here there's already an issue with an unused column that, to me, is a red flag for the API design. I also recommend refactoring a lot of the logic into a function that works on one curve at a time curve.

@cwhanse
Copy link
Member Author

cwhanse commented Aug 12, 2020

I think a Dataframe is a convenient way for a user to organize the data input, but I'm open to different ideas.

I can remove the fraction_of_rated_power column, which isn't used by this code. I thought to include it as a convenience for a user who is preparing the data input from the table typically found in a datasheet. The rated power levels are an index in that table, without the values in the Dataframe it's less easy to see if you have omitted a point, or to check that the values are typed correctly.

The data are a set of efficiency curves, where a curve is set of points (efficiency, ac power) (the docstring is incorrect on this point and I'll fix that). The curves do not have to be, but usually are, aligned on a set of common efficiency values. The algorithm needs to know one fact about each curve, the DC voltage level. If working from a datasheet there are typically 3 curves, one at each voltage level. From lab data, several curves are measured at each voltage level, and the algorithm needs to accommodate these repeated measurements.

Data = a list of tuples (voltage level, curve) where curve is a 2d array? May be awkward both for the user and for this function.

The algorithm doesn't work one curve at a time. The parameters are gotten from regressions across DC voltage levels (i.e., across subsets of the curves). Here's a diagram that illustrates how the 6 parameters are gotten from the set of curves. Three coefficients come from fitting a quadratic to the curve(s)

image

# independent variable for regressions, x_d
x_d = v_d - v_nom

curves['dc_power'] = curves['ac_power'] / curves['efficiency']
Copy link
Member

Choose a reason for hiding this comment

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

Doesn't this modify the input DataFrame?

@wholmgren
Copy link
Member

Sorry I now see I misread the loop and assumed something about the structure of the rows of the DataFrame that is not true.

I think a Dataframe is a convenient way for a user to organize the data input

Agreed, but the same is true for many of the other low level functions in pvlib and they still require arguments for API clarity. ModelChain takes DataFrame input and we see a lot more user confusion there about what the input needs to contain (granted it's a confusing thing for many reasons).

Apologies if I'm still missing something, but is there a reason to avoid an api like voltage_level, dc_voltage, efficiency, ac_power where each is an array? Of course they can be Series if the user starts with a DataFrame.

I can remove the fraction_of_rated_power column, which isn't used by this code. I thought to include it as a convenience for a user who is preparing the data input from the table typically found in a datasheet. The rated power levels are an index in that table, without the values in the Dataframe it's less easy to see if you have omitted a point, or to check that the values are typed correctly.

This doesn't sound like the function's problem to solve. Perhaps would be good to include in an example in the gallery.

@cwhanse
Copy link
Member Author

cwhanse commented Aug 13, 2020

Apologies if I'm still missing something, but is there a reason to avoid an api like voltage_level, dc_voltage, efficiency, ac_power

No you aren't missing something, that's a good idea - easy for a user to get to those inputs if a datasheet table is transcribed to a Dataframe, and makes the inputs explicit. Thanks.

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.

one nit, but otherwise LGTM assuming the tests pass when the syntax error is fixed.

Comment on lines 325 to 331
ac_power : Series
AC power output at each data point [W].
dc_power : Series
DC power input at each data point [W].
dc_voltage : Series
DC input voltage at each data point [V].
dc_voltage_level : Series
Copy link
Member

Choose a reason for hiding this comment

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

nit: these should be array_like, not Series.

@Peque
Copy link
Contributor

Peque commented Aug 14, 2020

@cwhanse Looks great. 😊

@cwhanse cwhanse merged commit 49da031 into pvlib:master Aug 14, 2020
@cwhanse cwhanse deleted the inverter_fit branch October 27, 2020 21:50
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.

Implement a function to calculate inverter model parameters
4 participants