Skip to content

Add support for Clenshaw-Curtis and Fejér second rule quadrature / sampling schemes#381

Draft
matt-graham wants to merge 42 commits into
mainfrom
mmg/clenshaw-curtis-quadrature
Draft

Add support for Clenshaw-Curtis and Fejér second rule quadrature / sampling schemes#381
matt-graham wants to merge 42 commits into
mainfrom
mmg/clenshaw-curtis-quadrature

Conversation

@matt-graham

@matt-graham matt-graham commented May 21, 2026

Copy link
Copy Markdown
Collaborator

Should resolve #251 and also does some work towards #340

Adds implementations of the Clenshaw-Curtis and Fejér's (second) quadrature rules and corresponding sampling schemes.

The quadrature weights are computed using the FFT based approach described in

Waldvogel, J. (2006). Fast construction of the Fejer and Clenshaw-Curtis quadrature rules.
BIT Numerical Mathematics, 46(1), 195–202. https://doi.org/10.1007/s10543-006-0045-4.

Following the definitions in that paper the Clenshaw-Curtis scheme includes samples at both poles ($\theta = \pm \pi$) while the Fejér's second rule scheme excludes both poles. The Clenshaw-Curtis scheme is selected with sampling="cc" and Fejér's second rule with sampling="f2" arguments to relevant functions.

To facilitate adding these new schemes, this PR also does some light refactoring of the existing quadrature and sample related modules to try to make the code more DRY and with a more standardized interfaced across the different schemes. In some cases the rationale for special casing code paths on different sampling schemes was a bit unclear, particularly around handling of pole singularities and setting m_offset, so I've done a best efforts attempt at trying to rationalise this but this probably could do with some careful checking.

Currently for a bandlimit $L$ both schemes have $N_\theta = 2L - 1$ and $N_\phi = 2L$. The former appears to be required to be $\sim 2L$ to give round-trip errors close to machine precision; I believe this related to the comment

An important difference [of Clenshaw-Curtis and Fejér rules] from Gaussian quadrature is that the number of nodes, given the truncation total wavenumber, $N$ required for alias-free exact meridional integration, is
$J \geq 2N + 1$,
which is about twice that in the Gaussian case. This is because Clenshaw–Curtis quadrature expands the integrand into Chebyshev polynomials of up to $(J −1)\text{th}$ degree and thus $J − 1$ needs to be no less than $2N$, the maximum degree of the integrand polynomial.

in the paper Hotte and Ujiie (2018) A nestable, multigrid-friendly grid on a sphere for global spectral models based on Clenshaw–Curtis quadrature with their definition of $N$ corresponding to $L - 1$ and $J$ to $N_\theta$ in our notation and hence we require $N_\theta \geq 2 L - 1$.

Choosing $N_\theta = 2L - 1$ gives an odd number of nodes in the latitude axis and so includes the equator as a node, this matching the co-latitude points used in Hotte and Ujiie (2018). This still leaves $N_\phi$ undetermined - here I have chosen $N_\phi = 2L$ which is sufficient to avoid aliasing in FFT operations (which requires $N_\phi > 2L - 2$), and matches the MW scheme, but gives $N_\theta \approx N_\phi$ while it seems quite common in grids used in practice in climate applications to have $N_\phi \approx 2N_\theta$ which might suggest using $N_\phi = 4L$.

@jasonmcewen I'm tagging you as you mentioned you would be interested in looking through the details of this.

TODO:

  • Decide if we want to adjust spherical grid dimensions for given bandlimit $L$ for new schemes
  • Add details of new schemes to sampling scheme documentation overview page
  • Check if new schemes can be carried through to Wigner transforms + document

@matt-graham matt-graham requested a review from jasonmcewen May 21, 2026 14:32
@matt-graham matt-graham marked this pull request as draft May 21, 2026 14:32
@codecov

codecov Bot commented May 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.37%. Comparing base (95c8d35) to head (87fb759).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #381      +/-   ##
==========================================
+ Coverage   96.10%   96.37%   +0.26%     
==========================================
  Files          34       34              
  Lines        3543     3612      +69     
==========================================
+ Hits         3405     3481      +76     
+ Misses        138      131       -7     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment on lines +34 to +37
elif sampling == "cc":
return 2 * n_theta - 2
elif sampling == "f2":
return 2 * n_theta + 2

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@jasonmcewen I arrived at these by trial and error as I could not see the pattern in how the values for the other schemes was derived - if there some underlying relationship here it would be good to document. It might also be worth moving this to one of modules under s2fft.sampling

@@ -1,5 +1,10 @@
import numpy as np

M_OFFSET_1_SCHEMES = frozenset(("mwss", "healpix", "cc", "f2"))

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I am not sure if there is a more descriptive / explanatory name for what unites these schemes. I found that including cc and f2 in this set was required to get tests passing but this more by trial and error than due to having a strong handle on what property of schemes requires us to offset m indices like this.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Related to whether number of $\phi$ samples is even / oversampling in $\phi$.

Comment thread s2fft/utils/quadrature.py

sampling (str, optional): Sampling scheme. Supported sampling schemes include
{"mwss", "dh", "gl", "healpix}. Defaults to "mwss".
{"mwss", "dh", "gl", "healpix", "cc"}. Defaults to "mwss".

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Add "f2" to list here

Comment thread tests/test_quadrature.py
],
)
@pytest.mark.parametrize("rule", ["cc", "f2", "mw", "mwss", "gl", "dh"])
@pytest.mark.parametrize("n_points", [6, 8, 16])

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Check whether minimum number of points here corresponds to critical threshold

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.

Support additional cylindrical (aka equiangular) sampling schemes

1 participant