Repository navigation
Revise and document the one-dimensional quadrature code - #1095
garth-wells wants to merge 2 commits into
Conversation
Document each of the helpers that build the one-dimensional rules, stating the domain, the weight function, the degree of exactness and the source of the algorithm, and remove several functions that duplicate each other. - Remove compute_jacobi_deriv. Its only caller asked for one derivative and read a single row of the result, which jacobi_value_deriv already computes without allocating (nderiv + 1, n + 1, npoints) arrays. - Add gauss_jacobi_rule_01, the rule on [0, 1], so that make_quadrature_line and quadrature::gauss_jacobi_rule share one implementation of the map from [-1, 1] rather than carrying two slightly different copies of it. get_gl_points reuses it too, instead of repeating the map a third time. - Fold compute_gll_rule into make_gll_line, its only caller. - Add tensor_product and use it for the quadrilateral and hexahedron cases of the Gauss-Jacobi and GLL rules, which were four copies of the same nested loops. - Drop the unused mdarray_t alias, an unused local and a commented-out line, return by move rather than by copy from the GLL rule, and replace the POSIX M_PI with std::numbers::pi. Also document the ordering of the points returned by get_gll_points, and add tests that the quadrilateral and hexahedron rules are the tensor product of the interval rule and that get_gl_points agrees with the interval rule. The points and weights are unchanged. Every rule, for all cell types, quadrature types, polyset types and degrees up to 24, along with get_gl_points, get_gll_points and gauss_jacobi_rule, is bit-identical to before in both single and double precision. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
State the formulas the one-dimensional routines implement, rather than
only naming the algorithms, so that the code can be checked against the
documentation line by line.
- rec_jacobi: give the closed forms of alpha_k and beta_k, and the
zeroth moment of the weight that fixes beta_0. Note that these are
the monic polynomials, scaled differently from the P_k^(a,b) that
jacobi_value_deriv evaluates.
- gauss: write out the Jacobi matrix and the weight formula, and say
that beta[0] does not appear in the matrix.
- lobatto: give the continued fraction g(y) = -pi_{n-2}(y)/pi_{n-1}(y)
and the replacements for the last pair of coefficients.
- jacobi_value_deriv: give the three-term recurrence with its
coefficients and the differentiated recurrence, and rename the local
variables to match.
- compute_gauss_jacobi_points: derive the deflated Newton update and
say why it cannot return to a root already found.
- compute_gauss_jacobi_rule, gauss_jacobi_rule_01, make_quadrature_line,
make_gll_line: state the identity each rule satisfies and the degree
it holds to, and derive the scaling of the weights under the map to
[0, 1].
- The collapsed triangle and tetrahedron rules: write out the change of
variables and count the degree of exactness, which is 2m - 1 and no
higher.
- The public interval functions in quadrature.h likewise.
The claims were checked against the code in 50-digit arithmetic: the
recurrence against mpmath's Jacobi polynomials, the quadrature weight
formula for integer and non-integer exponents, and each stated degree
of exactness, confirming it is attained and not exceeded.
That check found an off-by-one in lobatto, which used alpha[i] where
the recurrence for the ratio of consecutive polynomials needs
alpha[i - 1]. The prescribed points were then not reproduced unless
alpha is identically zero. Fixed here. The only caller passes the
Legendre coefficients, for which every alpha is zero, so no rule
changes: the points and weights remain bit-identical for all cells,
rules and degrees, in single and double precision.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Pushed a second commit expanding the mathematics in the documentation, and checking it. Each routine now states the formula it implements rather than only naming the algorithm, so the code can be read against the documentation line by line: the closed forms of the recurrence coefficients and the zeroth moment that fixes The statements were checked, not just written. In 50-digit arithmetic: the recurrence against mpmath's Jacobi polynomials; the quadrature weight formula That check turned up a real off-by-one in
So the prescribed points were not reproduced for any weight that is not symmetric about the origin. The only caller passes the Legendre coefficients, where every With the fix the continued fraction equals Doxygen builds clean with |
| /// for every polynomial \f$f\f$ of degree at most \f$2 m - 3\f$. | ||
| /// Prescribing the two endpoints costs two degrees relative to the | ||
| /// Gauss-Legendre rule of ::make_quadrature_line on the same number of | ||
| /// points. It is what makes the rule useful for spectral element |
| /// d\f$, which is of degree at most \f$d\f$ in \f$u\f$ and at most | ||
| /// \f$d\f$ in \f$v\f$. Both one-dimensional rules are exact to | ||
| /// degree \f$2 m - 1\f$, so the \f$m^2\f$-point rule is exact for | ||
| /// polynomials of total degree \f$2 m - 1\f$, and no higher. It |
There was a problem hiding this comment.
It - Duffy transformed rules therefore require more points than a rule designed specifically for the triangle.
| /// alongside a Gauss-Legendre rule in \f$u\f$. By the same counting | ||
| /// as for the triangle, the \f$m^3\f$-point rule is exact for | ||
| /// polynomials of total degree \f$2 m - 1\f$, and no higher. | ||
| /// |
There was a problem hiding this comment.
Add: Collapsed rules therefore require more points than a rule designed specifically for the tetrahedron.
Closes #570.
The helpers that build the one-dimensional rules are documented, and the functions that duplicated each other are removed. The points and weights are unchanged.
Duplication removed
compute_jacobi_derivevaluated (P_n^{a,0}) and its derivatives at an array of points, allocating(nderiv + 1, n + 1, npoints)and(n + 1, npoints)arrays. Its only caller asked for one derivative and read a single row of the result.jacobi_value_derivalready computes exactly that from the same recurrence, keeping only the last two values of each sequence, socompute_jacobi_derivis deleted.[-1, 1]to[0, 1]existed in three places:make_quadrature_line,quadrature::gauss_jacobi_ruleandquadrature::get_gl_points, each written slightly differently. They now share one helper,gauss_jacobi_rule_01.compute_gll_ruleis folded intomake_gll_line, its only caller.tensor_producthelper.Also dropped: the now-unused
mdarray_talias, an unused local incompute_jacobi_deriv, a commented-out line incompute_gll_rule, and two returns by copy that should have been moves.M_PI, which is POSIX rather than standard C++, is replaced bystd::numbers::pi.Documentation
Every helper now states the domain it works on, the weight function, how many points it uses and the degree it is exact to, and cites the algorithm it implements. The collapsed triangle and tetrahedron rules get the Duffy map written out, along with its Jacobian determinant and the reason the Gauss-Jacobi weight exponents are 1 and 2. The
@todoongaussis resolved. On the public side,get_gll_pointsnow documents the ordering of the points it returns, which was not recorded anywhere: the two endpoints come first, in the order 0, 1, followed by the interior points in increasing order.The Newton iteration in
compute_gauss_jacobi_pointskeeps its tolerance of1e-8, now with a comment explaining it: quadratic convergence means an update that small leaves a root accurate to roughly1e-16. The comment also records that in single precision the update can stagnate at the level of roundoff without reaching the tolerance, so the iteration runs tomax_iterwith the root already accurate to single precision.The numbers are unchanged
Checked by dumping every rule from the library built before and after, as hex float:
get_gl_points,get_gll_pointsandgauss_jacobi_rulewithafrom 0 to 3,floatanddouble.That is 3,142,000 double values and 3,133,542 float values, every one of them bit-identical.
Several changes had to be written carefully to keep that true, because the literals in an expression decide whether it is evaluated in double: the original code mixes
1.0and0.5literals intofloatexpressions, so the intermediate arithmetic is done in double and only the result is narrowed. The literals are therefore left as they were.Tests
Two cases added to
cpp/test/quadrature.cpp, covering the two paths that changed shape rather than just gaining comments:get_gl_points(m)agrees with the interval rule that usesmpoints.🤖 Generated with Claude Code