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

Gold I-V dataset with benchmarking #426

Closed
wants to merge 57 commits into from

Conversation

markcampanelli
Copy link
Contributor

@markcampanelli markcampanelli commented Feb 13, 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:

  • Partly addresses issue Test singlediode() implementations using a "gold" function and data set #411
  • 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):

@markcampanelli
Copy link
Contributor Author

Here is some code for others to reproduce computations. To run this do the following:

$ pip install pyinterval # first time only
$ python gold_dataset.py # Will rewrite test.json file with gold dataset

pvlib/data/gold/worst_pvlib_computaitons.json contains the worst computations from my laptop on python 1.0.0 with numpy 1.14.0 and pvlib 0.5.1+34.g329450a.dirty

current


def bootstrap_si_device_bishop(cell_area_cm2=4., N_s=1, ideality_factor=1.5):
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 create_si_device_bishop


def bootstrap_si_device_bishop(cell_area_cm2=4., N_s=1, ideality_factor=1.5):
"""
Create c-Si device based on cell parameters in Bishop's 1988 paper Fig. 6.
Copy link
Member

Choose a reason for hiding this comment

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

Create a De Soto model for a c-Si device...



def bootstrap_si_cell_large():
"""
Copy link
Member

Choose a reason for hiding this comment

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

Prefer create_si_cell_large

def bootstrap_si_cell_large():
"""
Create a single-large-area-cell c-Si device based on a SunPower module.
"""
Copy link
Member

Choose a reason for hiding this comment

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

Add note that we are creating a De Soto model for the cell

device.V_mp_ref = device.V_mp_ref / Ns_module
device.a_ref = device.a_ref / Ns_module
device.R_s = device.R_s / Ns_module
device.R_sh_ref = Ns_module * device.R_sh_ref
Copy link
Member

Choose a reason for hiding this comment

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

Should this be Ns_module / device.R_sh_ref ?

Copy link
Member

Choose a reason for hiding this comment

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

No, you need to multiply that one.

def make_gold_dataset():
"""
Make a gold dataset of I-V curves that accurately solve single-diode model.

Copy link
Member

Choose a reason for hiding this comment

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

that accurately solve the single diode equation.

Copy link
Member

Choose a reason for hiding this comment

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

...that are accurate solutions to...

Returns
-------
gold_dataset : dict
The gold data are returned in a dict with metadata (e.g., timestamp,
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 practical to outline the gold_dataset dict here (i.e., list and describe each key), the device dict, and a list member dict for a device?

returned in a python dict that is in a lossless, machine-interoperable
format, which is the expected input to most of the other functions in this
python module. (Other functions can convert this dict to a human-readable
format.) An explict method of Bishop [1] is used to compute (V, I) pairs
Copy link
Member

Choose a reason for hiding this comment

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

Would help to name the 'other functions'

return gold_dataset


def convert_gold_dataset(gold_dataset):
Copy link
Member

Choose a reason for hiding this comment

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

maybe add _to_pvlib to function name

Copy link
Member

Choose a reason for hiding this comment

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

On second thought, gold_dataset_to_pvlib

return gold_dataset_converted


def pretty_print_gold_dataset(gold_dataset):
Copy link
Member

Choose a reason for hiding this comment

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

gold_dataset_to_csv

# Load G, T, and 5 single-diode model parameters for each I-V curve
# Floats converted from lossless, machine-interoperable hex format

iv_curve["G"] = float.fromhex(iv_curve["G"])
Copy link
Member

Choose a reason for hiding this comment

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

should we use the pvlib keys? effective_irradiance instead of G, temp_cell instead of T, etc.

See ``make_gold_dataset()``.

test_func : dict
A dictionary of functions to guage. Supported keys to functions are
Copy link
Member

Choose a reason for hiding this comment

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

'gauge'

@adriesse
Copy link
Member

Sorry, I'm not too clear on the question here. There's been too much going on!

@thunderfish24 I understand your perspective. The primary reason I requested some refactoring was to make the code easier to understand and review for correctness. I think the code is in good shape otherwise.

I am unlikely to use this feature, so I ask @cwhanse @adriesse @mikofski and any others for their opinion of how to proceed.

@cwhanse
Copy link
Member

cwhanse commented Jun 21, 2018

@adriesse Will is inviting you to review the pull request. OK if you don't have time, since both Will and I have done so. The PR consists of two basic components: a function to precisely calculate sets of IV curves (a number of example devices are included), and a function to compare between two IV curve calculators (think of a function that runs and compares two functions). I think Will's major interest is, do you think this capability is appropriate for and useful to pvlib?

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.

Overall impressions:

There are some really long functions here, and it would be nice to chop them up. I think one way to do this might be to not treat the whole gold set as a single entity but process (and store/read) them one device or even one curve at a time.

The storage format was discussed a while back. Given the size of your tolerance I think the lossy csv format is more than adequate. You can also flatten the data into a 2d table and read/write that with pandas and even put a few header lines with extra info pretty easily. I think multiple files would be appropriate--there won't be hundreds.

Anyway, even if neither of those two ideas is appealing, there are some other ideas for improvements already. It is a big contribution overall, and I think it would be fine to take some extra time to hone it further.

device.PTC = None

return device

Copy link
Member

Choose a reason for hiding this comment

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

This leaves me searching whether you kept attribute of the device you copied. Wouldn't it be cleaner to just create a new device from scratch?

device.V_mp_ref = device.V_mp_ref / Ns_module
device.a_ref = device.a_ref / Ns_module
device.R_s = device.R_s / Ns_module
device.R_sh_ref = Ns_module * device.R_sh_ref
Copy link
Member

Choose a reason for hiding this comment

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

No, you need to multiply that one.

def make_gold_dataset():
"""
Make a gold dataset of I-V curves that accurately solve single-diode model.

Copy link
Member

Choose a reason for hiding this comment

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

...that are accurate solutions to...


From a gold dataset in the lossless, machine-interoperable format, create
and return the dataset as a human-readale, comma-separated-value (csv)
string.
Copy link
Member

Choose a reason for hiding this comment

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

readable

# Don't include entire curve in exception message
del device_dict["iv_curves"][-1]["v_gold"]
del device_dict["iv_curves"][-1]["i_gold"]
raise ValueError("Residual interval {} not in {}, \
Copy link
Member

Choose a reason for hiding this comment

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

Does this ever happen??

interval(nNsVth)) - interval(g_sh) *
(interval(v) + interval(i)*interval(r_s)) -
interval(i) for v, i in zip(v_gold, i_gold)]

Copy link
Member

Choose a reason for hiding this comment

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

What's the advantage of using intervals here, where all the intervals are single numbers?

for v, i, current_sum_residual_interval in zip(
v_gold, i_gold,
current_sum_residual_interval_list):
if current_sum_residual_interval not in tol_interval:
Copy link
Member

Choose a reason for hiding this comment

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

What is the advantage of this over np.isclose()?

device_dict["i_from_v"]["performance"]["time_s"]["std"] = \
numpy.std(device_dict["i_from_v"]
["performance"]["time_s"]["data"], ddof=1)

Copy link
Member

Choose a reason for hiding this comment

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

This block is quite hard for me to parse.

pvlib.pvsystem.retrieve_sam('cecmod').SunPower_SPR_E20_327,
pvlib.pvsystem.retrieve_sam('cecmod').First_Solar_FS_495
]

Copy link
Member

Choose a reason for hiding this comment

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

What do you think about copying the parameters for all 5 devices into golden_devices.csv or something similar? Then you just need a single read.

@@ -17,5 +17,7 @@ dependencies:
- nose
- pip:
- coveralls
- pvfactors==1.0.1
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@anomam Do you know if I'm fixing or breaking something here? (Is pvfactors 1.0.1 really not supported in Python 3.4?)

Copy link
Contributor

Choose a reason for hiding this comment

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

That's a good point @markcampanelli , starting with version 1.0.0 pvfactors should work with earlier versions of pandas, and therefore be compatible with Python 3.4.
Could you post an issue in pvfactors by any chance? A PR would be very much welcome as well :)

Copy link
Member

Choose a reason for hiding this comment

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

Python 3.4 hit "end of life" a few months ago. I don't plan to support it going forward.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@markcampanelli
Copy link
Contributor Author

markcampanelli commented May 16, 2019

It looks like scipy/scipy#8028 may have fixed the significant issue with a scipy.special.wlambert calculation that I saw earlier on my machine (see way above). With scipy 1.2.1, scipy.special.lambertw(1.22045782051e+304) now properly computes 693.6431367052406+0j. I am now investigating the new computations and making sure that remaining ones with large residuals can be reasonably explained as being from numerics and not bugs. After this I will try to improve improve code naming and organization based on earlier suggestions before requesting another review.

@markcampanelli
Copy link
Contributor Author

Closing this in favor of #1573.

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

Successfully merging this pull request may close these issues.

6 participants