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

PV models speed up #1212

Open
wants to merge 23 commits into
base: develop
Choose a base branch
from
Open

PV models speed up #1212

wants to merge 23 commits into from

Conversation

dguittet
Copy link
Collaborator

@dguittet dguittet commented Sep 30, 2024

Speed up pvsamv1, pvwattsv7 and pvwattsv8 by speeding up irradproc and sssky_diffuse_table.

irradproc:

  • for intra-annual solarpos_spa calculation speed up, introduce a hash table that tracks the intermediate outputs of calculate_spa that reduces the number of times that calculate_spa is called per timestep
  • for lifetime calculation, inter-annual speed up is done by storing the results of solarpos_spa in a vector so that the values are not recalculated each year

sssky_diffuse_table:

  • change the keys of the hash table to numeric key instead of string since sprintf takes a long time
  • reduce the number of steps/samples required to compute the self-shading loss in sssky_diffuse_table::compute

pvwattsv8 and pvwattsv7:

  • move irrad proc outside the time loop

pvsamv1:

  • move irrad proc outside the time loop
  • change the constructor used for irradproc

tests:

  • pvsamv1 test values are changed (shows how the actual value changed)
  • pvwattsv8 test tolerances are changed (shows how much the relative error increased in order to use the old expected values). A potential additional change in this PR is to change the expected values to the new values

Speed changes

Before is the original code, "Spa Hash" is what is implemented in this PR

image

image

Copy link
Collaborator

@mjprilliman mjprilliman left a comment

Choose a reason for hiding this comment

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

This looks great! I want to double check seasonal tilt behavior, so I may need to do a bit more debugging.

shared/lib_irradproc.cpp Show resolved Hide resolved
test/ssc_test/cmod_pvsamv1_test.cpp Outdated Show resolved Hide resolved
Copy link
Collaborator

@mjprilliman mjprilliman 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. Just one merge conflict from recent merges to address

@janinefreeman
Copy link
Collaborator

This looks good to me! I think it makes sense to update the pvwattsv8 expected test values to the new values as well. I like scaling the error tolerance by annual energy, but I think I'd like to make the error tolerance itself smaller if we're keeping that approach.

Requesting an additional review from @sjanzou , particularly on the hash table, just because this is a big change for pvwatts and pvsam.

Super excited for these results @dguittet !!! Nice job!!!

@dguittet
Copy link
Collaborator Author

dguittet commented Oct 2, 2024

Need tests for Alaska and South Africa

Copy link
Collaborator

@sjanzou sjanzou 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 overall, just a few comments and questions on some of the implementation values and sensitivities.

Thanks for tackling this!

@@ -91,7 +91,7 @@ TEST_F(CMPvwattsV5Integration_cmod_pvwattsv5, DifferentTechnologyInputs)
{
// std::vector<double> annual_energy_expected = { 6909.79, 7123.32, 7336.478, 6909.79, 6804.376, 8711.946, 8727.704, 9690.735 };
// single axis tracking reduction due to pull request 280
std::vector<double> annual_energy_expected = { 6908.13, 7121.63, 7334.83, 6908.13, 6802.72, 8633.37, 8721.21, 9687.06 };
std::vector<double> annual_energy_expected = { 6908.13, 7121.63, 7334.83, 6908.13, 6802.72, 8632.33, 8721.21, 9687.06 };
Copy link
Collaborator

Choose a reason for hiding this comment

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

Seems like the general trend is for lower annual energy in all the tests and the default configuration results.

ascension_and_declination[0], ascension_and_declination[1]};

if (use_table)
spa_table[spa_key_inputs] = spa_outputs;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Have you run with use_table = false to verify previous results?

}

bool irrad::getStoredSolarposOutputs() {
if (!solarpos_outputs_for_lifetime.size()) return false;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is it clearer for maintenance to use if (solarpos_outputs_for_lifetime.size() == 0) return false;

spa_table_key(double j, double dt, double p, double t, double a, double d):
jd(j), delta_t(dt), ascension(a), declination(d)
{
int pressure_bucket = 10;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you have a sensitivity plot based on pressure and temp bucket size?

// Compute individual hash values for first, second, etc and combine them using XOR and bit shifting:
return
((((
((((hash<double>()(k.jd)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Have you tried hash_combine in C++17 for more robustness (i.e. less collisions)?

image

sprintf(buf, "%.3f", surface_tilt);
if (derates_table.find(buf) != derates_table.end())
return derates_table[buf];
int surface_tilt_key = int(surface_tilt * derates_table_digits_multiplier);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems like it would be faster - nice approach!

return compute(surface_tilt);
}

double sssky_diffuse_table::compute(double surface_tilt) {
if (gcr == 0)
throw std::runtime_error("sssky_diffuse_table::compute error: gcr required in initialization");
// sky diffuse reduction
double step = 1.0 / 1000.0;
const size_t n_steps = 250;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you have a sensitivity analysis on some different n_steps values?

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.

4 participants