Skip to content

Improve pvlib.spa speed #1748

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 20 commits into from
May 30, 2023
Merged

Improve pvlib.spa speed #1748

merged 20 commits into from
May 30, 2023

Conversation

kandersolar
Copy link
Member

@kandersolar kandersolar commented May 25, 2023

  • I am familiar with the contributing guidelines
  • 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`).
  • Pull request is nearly complete and ready for detailed review.
  • Maintainer: Appropriate GitHub Labels (including remote-data) and Milestone are assigned to the Pull Request and linked Issue.

Some functions in pvlib.spa are doing quite a lot of unnecessary computation:

  • For the table-based computations, our code combines arrays of different lengths into a single 2d array, thus padding the shorter arrays with zero, and creating a bunch of unnecessary busy work to process those dummy entries.
  • The longitude_nutation and obliquity_nutation functions perform the same expensive calculation
  • To much lesser effect, there are several instances of a single function containing multiple calls to np.radians(x).

This PR prevents those unnecessary computations and reduces overall calculation time by 40-50% for both numpy and numba. The table below compares runtimes in seconds:

n numpy, PR numpy, main numba, PR numba, main
10 0.002 0.004 0 0
100 0.003 0.004 0 0.001
1000 0.006 0.011 0.001 0.002
10000 0.041 0.069 0.009 0.017
100000 0.4 0.657 0.083 0.161
1000000 4.654 8.492 0.864 1.633

Regarding the table-length issue, it might be better to do away with the combined HELIO_LONG_TABLE et al. entirely and just keep the arrays as separate module-level variables.

@kandersolar kandersolar marked this pull request as ready for review May 26, 2023 23:02
@kandersolar
Copy link
Member Author

Ready for review. The drop in reported coverage should be ignored; it's in the part of pvlib.spa that is run in numba-mode only, and coverage tracking doesn't work with numba functions.

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.

Great work @kandersolar!

@@ -468,60 +456,32 @@ def julian_ephemeris_millennium(julian_ephemeris_century):
return jme


@jcompile(nopython=True)
Copy link
Member

Choose a reason for hiding this comment

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

Do we need to declare types in this jcompile like we do in most of the other functions in this module? I haven't kept up with numba development.

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 not an expert here either, but I think specifying types is not necessary in general; when using numba for JIT compiling like we do, it can infer types as needed.

In this particular case, I did try to specify types just for consistency with the rest of the code. However it seems that specifying the appropriate type for module-level array constants is not possible using the shorthand string notation. It is possible by importing numba, but I didn't see a clean way to no-op the type declaration when numba isn't installed, so I opted to just leave the type signature empty here instead. I've added a comment along these lines (should have done that originally).

Comment on lines 33 to 35
* :py:mod:`pvlib.spa`, and :py:func:`pvlib.solarposition.get_solarposition`/
:py:meth:`pvlib.location.Location.get_solarposition` with ``method='nrel_numpy'``
or ``method='nrel_numba'``, are now 50-100% faster than before. (:pull:`1748`)
Copy link
Member

Choose a reason for hiding this comment

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

There's also pvlib.solarposition.spa_python and ModelChain.run_model*. Maybe less is more in this situation: The default solar position algorithm (NREL SPA) is now 50-100% faster.

kandersolar and others added 2 commits May 30, 2023 12:27
Co-Authored-By: Will Holmgren <william.holmgren@gmail.com>
@kandersolar kandersolar merged commit 1c60a5d into pvlib:main May 30, 2023
@kandersolar kandersolar deleted the spa branch May 30, 2023 16:35
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.

3 participants