Skip to content

fixed Curl2D evaluation for H1 elements (v1.7.2) - #85

Merged
chmerdon merged 2 commits into
masterfrom
chmerdon/fix_curl2_h1
Oct 9, 2026
Merged

chmerdon merged 2 commits into
masterfrom
chmerdon/fix_curl2_h1

Conversation

@chmerdon

@chmerdon chmerdon commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Fixes the Curl2D operator evaluation on H1 elements. Two independent defects were found while investigating an inconsistency of rotation-form convection terms in NumCompressibleFlows (velocity space H1BR).

Bug 1: wrong sign for all 2D H1 elements (regression, introduced in v1.7.0)

update_basis! for Curl2D + AbstractH1FiniteElement computed du2/dx1 + du1/dx2 instead of du2/dx1 - du1/dx2:

9689350 — "Better operator evaluations + more operator tests" (#69), 2026-05-26, released as v1.7.0

turned

cvals[1, dof_i, i] -= L2GAinv[2, j] * refbasisderivvals[subset[dof_i], j, i]  # -du1/dy

into

cvals[1, dof_i, i] = dot(L2GAinv[2, :], refbasisderivvals[subset[dof_i] + offsets2[1], :, i])  # -du1/dy

thereby dropping the minus sign. The code was correct from the initial state (v0.1.0) up to v1.6.1 and is wrong in v1.7.0 and v1.7.1.

Bug 2: coefficients ignored for AbstractH1FiniteElementWithCoefficients (present since H1BR was introduced)

There was no Curl2D method for AbstractH1FiniteElementWithCoefficients, so H1BR silently used the plain H1 method, which never applies _update_coefficients!. Consequently the normal-weighted face bubble dofs of H1BR were evaluated without their face-normal weights. Identity, Gradient, Divergence and NormalFlux received coefficient-aware methods in 0fa8ce9 (2023-09-05), Curl2D was overlooked, so this defect exists since H1BR was added in the initial commit (82d73e5, v0.1.0).

The same gap existed for Curl3D, CurlScalar, Laplacian, Hessian, SymmetricHessian and TangentialGradient, which is also fixed here (second commit).

Why the existing tests did not catch it

test_derivatives2D(H1P2{2, 2}, 2) is the only exact Curl2D test on an H1 element, and its test function u = (x1^2, 3*x2^2 + x1*x2) has du1/dx2 = 0 at the test midpoint, so both signs produce the same expected value 1/3. H1BR was not covered by the operator tests at all (it appeared only in test_reconstructions, which does not evaluate Curl2D).

Extension: the remaining operators (second commit)

The identical problem existed for all other derivative operators without a coefficient-aware method. Added Curl3D, CurlScalar, Laplacian, Hessian, SymmetricHessian and TangentialGradient methods for AbstractH1FiniteElementWithCoefficients, each just multiplying by _update_coefficients! like the existing Identity/Gradient/Divergence/NormalFlux methods.

New checks test_bubble_operators2D/3D compare the operator cvals of the H1BR bubble dofs against analytic values (b * n with the face bubble b and the face normal n read from the grid), evaluated at the midpoint of a single non-reference triangle/tetrahedron: Gradient, Curl2, Laplacian, Hessian, SymmetricHessian{1} and SymmetricHessian{sqrt(2)} in 2D, Gradient and Curl3 in 3D. Without the coefficient multiplication these produce errors of O(1)..O(10), with it they agree to machine precision.

Two caveats found on the way (both pre-existing, not touched here):

  • TangentialGradient could not be tested: update_basis! of an ON_FACES evaluator fails for items > 1 on a 2D grid (update_trafo! bounds error), and its plain implementation only fills cvals[1, ...]
  • the second-order operators of H1BR{3} are covered only indirectly (same code pattern as the analytically verified 2D case)

Changes

  • restore the minus sign in the H1 Curl2D evaluation
  • add a coefficient-aware Curl2D method for AbstractH1FiniteElementWithCoefficients
  • new tests: test_derivatives2D(H1P1{2}, 1) and test_derivatives2D(H1BR{2}, 1) (exact operator values incl. Curl2 = -2), test_derivatives2D(H1BR{2}, 2) and a Curl2 vs. Gradient consistency check for all H1 elements. Since the BR space is a proper subspace of P2, a quadratic test function is not reproduced exactly by interpolation; there the identity curl(u_h) = du_h2/dx1 - du_h1/dx2 is checked instead, which is exact and exercises the bubble dofs.
  • version bump 1.7.1 -> 1.7.2, changelog entry

Verification

  • all three new checks fail on the unfixed code (2.0 and 2.707 instead of < 1e-14), so they pin the bug
  • runic --check is clean for the changed files
  • full local test suite passes (for both commits): Aqua 11, ExplicitImports 2, UndocumentedNames 1, examples 10, FEMatrixVector 10, FEBasis 17, Operators 17, QuadratureRules 80, Interpolations 116, Grid/Space Interpolation Matrix 116/33, SegmentIntegrator 2, PointEvaluator 10

- restored missing minus sign of the -du1/dx2 part in the H1 Curl2D
  evaluation (regression introduced with the operator restructuring in
  v1.7.0, commit 9689350), which affected all H1 elements in 2D
- added Curl2D evaluation for AbstractH1FiniteElementWithCoefficients
  elements (e.g. H1BR), which so far ignored the coefficients of the
  normal-weighted bubble dofs (missing since the introduction of H1BR)
- added operator tests for H1P1 and H1BR and a consistency check between
  Curl2D and Gradient on H1 elements
@chmerdon
chmerdon requested a review from pjaap October 8, 2026 12:25

@pjaap pjaap left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would tell Qwen to be less verbose next time :)

- added Curl3D, CurlScalar, Laplacian, Hessian, SymmetricHessian and
  TangentialGradient evaluations for AbstractH1FiniteElementWithCoefficients
  elements (e.g. H1BR, H1P1TEB), which so far fell back to the plain H1
  methods and ignored the coefficients of the normal-weighted bubble dofs
- added analytic checks of the derivative operators on the bubble dofs of
  H1BR{2} (Gradient, Curl2D, Laplacian, Hessian, SymmetricHessian) and
  H1BR{3} (Gradient, Curl3D)
@chmerdon
chmerdon merged commit 1f8bbd6 into master Oct 9, 2026
12 checks passed
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