Skip to content

Interpolate into a Boundary Quadrature space on a quadrilateral - #5323

Open
pbrubeck wants to merge 8 commits into
pbrubeck/hdivcurl-deltafrom
pbrubeck/tensor-facet-quadrature
Open

Interpolate into a Boundary Quadrature space on a quadrilateral#5323
pbrubeck wants to merge 8 commits into
pbrubeck/hdivcurl-deltafrom
pbrubeck/tensor-facet-quadrature

Conversation

@pbrubeck

@pbrubeck pbrubeck commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Paired with firedrakeproject/fiat#274firedrakeproject/fiat#274

FunctionSpace(quad_mesh, "Boundary Quadrature") could be assembled but not
interpolated into:

NotImplementedError: How to tabulate TensorProductElement on FacetPointSet?

The element's dual basis names the points of every facet at once, and the
facets of a product cell lie in several directions, so those points have no
single product structure for a TensorProductElement to factor. fiat#274 fixes
that by dual evaluating one facet direction at a time — each direction is a
product, since d(A x B) = dA x B u A x dB.

Here

  • tsfc: propagate zeros through dual evaluation — apply to dual
    evaluation the zero handling assembly already does. Fold zero-valued
    Literals into symbolic Zero first so gem's algebraic identities can
    propagate them; drop pairs whose expression collapsed to Zero once the
    blocks are contracted, since a Delta-cancelled cross-component block is a
    real zero contribution rather than one merely absent from the sum; and emit a
    Zero temporary as a zero-filled read-only array if one does reach loopy,
    as gem.Zero has no .array for the Constant branch to use.

  • Test Boundary Quadrature on quadrilaterals — the quadrature element
    tests now run on a quadrilateral mesh as well as a triangular one, plus a new
    interpolation test checking that the result reproduces the expression at the
    points and that both cells sharing a facet see the same values there. Scalar,
    vector and tensor valued spaces each take a different path through dual
    evaluation, so all three are covered.

  • DROP BEFORE MERGE — points the existing FIAT install step at
    pbrubeck/tensor-facet-quadrature so CI builds against fiat#274.

Verification

Against the base FIAT commit the three new quadrilateral cases fail with the
NotImplementedError above; with fiat#274 the file is 25/25. Kernel cache was
cleared for both runs — with a warm cache the failing cases pass spuriously.

Serial test_interpolate, test_interpolate_zany, test_quadrature and
test_interpolate_vs_project: 106 passed, 0 failed. Parallel-marked tests were
not run locally.

Still unsupported, unchanged by this PR: hexahedra, which check_element
rejects for this family, and extruded meshes, where the facets are not all the
same shape so the flat facet rule does not exist.

🤖 Generated with Claude Code

pbrubeck and others added 6 commits July 29, 2026 11:32
…ient

compile_expression_dual_evaluation contracts the dual evaluation against
the dual coefficient with an IndexSum over the basis indices when the dual
argument is a Cofunction.  When the target element has a block-structured
dual basis (e.g. a RestrictedElement, whose dual evaluation is a
gem.Concatenate), this hid the concatenation index from the subsequent
unconcatenate() call, which can only split along indices that are still
free in the assignment.

Resolve the concatenation of the dual basis before the basis indices are
summed over, following the same pattern already used for coefficient
evaluation in tsfc.fem.  This makes matrix-free adjoint interpolation
(MatMultTranspose of a two-form Interpolate) work for restricted elements.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
EnrichedElement has no dual basis without it, so interpolating into the
facet space of a hexahedron raises NotImplementedError, and gem cannot
unconcatenate a dual basis whose blocks have already been contracted with
a Cofunction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
compile_expression_dual_evaluation carried a TODO to apply the GEM
optimisations that assembly applies.  Apply one of them: contract each
assignment once unconcatenate has split the Concatenate nodes.

This matters for H(div) and H(curl).  Those elements are a sum of blocks,
each mapping into one vector component, and with firedrakeproject/fiat#270
each block selects its component with a Delta.  Before unconcatenation the
blocks are branches of a Concatenate, so the Deltas never meet; afterwards
each pair of blocks is its own assignment, the Deltas multiply, and delta
elimination cancels every pair mapping to different components.

Coarse to fine interpolation, kernel temporaries and wall clock:

    NCF 1->4  832K -> 68K   3.07x faster
    NCE 1->4  307K -> 110K  2.23x faster
    NCE 1->3  131K -> 61K   2.23x faster

Form assembly does not go through this function and is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Apply to dual evaluation the zero handling assembly already does.

Fold zero-valued Literals into symbolic Zero before anything else, so the
algebraic identities built into gem (Product(_, Zero) -> Zero, and so on)
can propagate them and the later steps can prune what they reach.

Then drop any pair whose expression has collapsed to Zero once the blocks
are contracted: a cross-component block that a Delta has just cancelled is
a real zero contribution rather than one merely absent from the sum, and it
must not reach codegen as a temporary.

Emit a Zero temporary as a zero-filled read-only array when one does reach
loopy, as gem.Zero carries no .array for the Constant branch to use.
Run the quadrature element tests on a quadrilateral mesh as well as a
triangular one, and check that interpolation into a facet quadrature space
reproduces the expression at the points and gives both cells sharing a
facet the same values there.

Scalar, vector and tensor valued spaces each take a different path through
dual evaluation, so cover all three.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Point the existing FIAT install step at pbrubeck/tensor-facet-quadrature,
which is pbrubeck/fix/dual-enriched plus the facet quadrature work.  A
Boundary Quadrature space on a quadrilateral cannot be interpolated into
without it: the dual basis names the points of every facet at once, and a
TensorProductElement cannot tabulate on those.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@pbrubeck pbrubeck added the base:main Run this PR using a main (dev) build label Aug 4, 2026
Comment thread tests/firedrake/regression/test_quadrature.py Outdated
Comment thread tsfc/driver.py Outdated
Comment thread tsfc/driver.py Outdated
Co-authored-by: Pablo Brubeck <brubeck@protonmail.com>
Comment thread tsfc/loopy.py Outdated
@pbrubeck
pbrubeck force-pushed the pbrubeck/hdivcurl-delta branch from a992610 to 537d1b5 Compare August 18, 2026 22:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

base:main Run this PR using a main (dev) build

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant