Skip to content

Diagnose invalid angular grid sizes instead of returning zeros - #101

Open
susilehtola wants to merge 1 commit into
wavefunction91:masterfrom
susilehtola:fix/angular-invalid-npts
Open

susilehtola wants to merge 1 commit into
wavefunction91:masterfrom
susilehtola:fix/angular-invalid-npts

Conversation

@susilehtola

Copy link
Copy Markdown
Collaborator

The generate() dispatch for all four S2 quadrature families was a chain
of if/else-if with no terminal else. Requesting a point count that is not
tabulated left the value-initialised containers untouched, so the caller
received a grid whose weights were all zero and which therefore
integrates every function to zero. No exception, assertion or warning was
raised, and the README claims the opposite ("will fail if the grid order
is incompatible").

Add a terminal else to each family that reports the requested size and
points at npts_by_algebraic_order().

Two data problems surfaced once the check was in place:

  • lebedev_laikov_5294.hpp is shipped and included by the grids header but
    had no dispatch branch and no entry in the algebraic order tables, so
    the 5294-point Lebedev-Laikov grid was unreachable. Wire it up (order
    125, between 4802/119 and 5810/131).

  • The tabulated 552-point Ahrens-Beylkin grid is not a valid order-39
    rule. Its weights sum to 0.9632 * 4pi, and after renormalisation it
    still integrates degree-8 monomials to only 5.7e-02 relative accuracy,
    while every neighbouring size is exact to machine precision. Withdraw
    it from the dispatch and the order tables until the table can be
    regenerated from the original Ahrens-Beylkin data; next_algebraic_order
    now rounds order 39 up to 41.

The new test checks both directions: unsupported sizes must throw, and
every size advertised by the order tables must be dispatched and must
integrate even monomials exactly up to its claimed algebraic order. That
second check is what found the Ahrens-Beylkin defect; a sum-of-weights
check alone would not have, since the grid is exact at low order once
renormalised.

Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01FDTFYJMQ76iujDFNHzZyXF

Comment thread include/integratorxx/quadratures/s2/ahrens_beylkin.hpp Outdated
@susilehtola
susilehtola force-pushed the fix/angular-invalid-npts branch from 493a30d to e5bbbd4 Compare September 4, 2026 07:44
The generate() dispatch for all four S2 quadrature families was a chain
of if/else-if with no terminal else. Requesting a point count that is not
tabulated left the value-initialised containers untouched, so the caller
received a grid whose weights were all zero and which therefore
integrates every function to zero. No exception, assertion or warning was
raised, and the README claims the opposite ("will fail if the grid order
is incompatible").

Add a terminal else to each family that reports the requested size and
points at npts_by_algebraic_order().

Two data problems surfaced once the check was in place:

* lebedev_laikov_5294.hpp is shipped and included by the grids header but
  had no dispatch branch and no entry in the algebraic order tables, so
  the 5294-point Lebedev-Laikov grid was unreachable. Wire it up (order
  125, between 4802/119 and 5810/131).

* The tabulated 552-point Ahrens-Beylkin grid is not a valid order-39
  rule -- its weights sum to 0.9632 * 4pi, and after renormalisation it
  still integrates degree-8 monomials to only 5.7e-02 relative accuracy,
  while every neighbouring size is exact to machine precision. Drop it
  from the dispatch and the order tables; next_algebraic_order now rounds
  order 39 up to 41. The following commit restores the size once the
  cause is known.

The new test checks both directions: unsupported sizes must throw, and
every size advertised by the order tables must be dispatched and must
integrate even monomials exactly up to its claimed algebraic order. That
second check is what found the Ahrens-Beylkin defect; a sum-of-weights
check alone would not have, since the grid is exact at low order once
renormalised.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FDTFYJMQ76iujDFNHzZyXF
@susilehtola
susilehtola force-pushed the fix/angular-invalid-npts branch from e5bbbd4 to 583e5ca Compare September 4, 2026 07:54

@wavefunction91 wavefunction91 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

See inline comments.

else if (npts == 492)
detail::copy_grid<ahrens_beylkin_492<RealType>>(points, weights);
else if (npts == 552)
detail::copy_grid<ahrens_beylkin_552<RealType>>(points, weights);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

With the dispatch gone, ahrens_beylkin_552.hpp can't be reached, but it's still #included from ahrens_beylkin_grids.hpp:12, so every TU that includes the Ahrens-Beylkin grids still parses its 1,122 lines. Could we delete the file and that include here? Git keeps the data, and #113 brings order 39 back as 572.

// and must integrate polynomials exactly up to its claimed order. A
// missing dispatch branch would return zero weights; a corrupt table
// would integrate low-order monomials incorrectly.
for( int64_t order = 0; order <= 200; ++order ) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

The loop stops at order 200, but Ahrens-Beylkin advertises 15012 points at order 210, so that size is never dispatched or checked here. Could the bound cover it, e.g. order <= 210, or angular_traits::next_algebraic_order of some large order, which clamps to each family's maximum?


// Total degree 2*(a+b+c) must not exceed the claimed algebraic order.
// Capped for run time; degree 8 already exercises every table.
const int dmax = static_cast<int>(std::min<int64_t>(order / 2, 4));

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

The description says every advertised size must integrate exactly up to its claimed order, but dmax caps the check at degree 8, and that cap hides a second bad table. AB-5472 is advertised as order 127 but is exact only through l = 104. With the addition theorem, Σᵢ wᵢ P_l(n·xᵢ) = 4π δ_l0 at a few fixed unit vectors n, its relative residual reaches 3e-7 for l between 105 and 127. Every other advertised grid is exact through its claimed order to ≤ 6.1e-15, and at degree 8 5472 looks fine (1.1e-14), so the current test can't see it. As with 552, the data match Beylkin's qsph1-127-5472DP.dat bit for bit, so the defect is upstream.

Checking the full claimed order this way is cheap: the Legendre three-term recurrence per grid, O(N·L). All 230 advertised grids take about 3 s in NumPy, including parsing the headers. Could the test check every l up to the claimed order, with 5472 withdrawn (or regenerated) the same way as 552?

else if( npts == 5810 )
detail::copy_grid<lebedev_laikov_5810<RealType>>( points, weights );
else
detail::throw_unsupported_grid_size("LebedevLaikov", npts);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

One input still escapes this: size_t(-1), which is what npts_by_algebraic_order's -1 for an untabulated order becomes when it's passed straight to the constructor. The containers are sized from npts before the dispatch runs (lines 45–46, and the same in the other three families), so it fails with std::length_error from the vector constructor instead. Checking the size before allocating would make the runtime_error hold for every input, which #106's README now promises.

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.

2 participants