TSFC: support same-mesh ufl.Interpolate in Form kernels - #5258
Open
pbrubeck wants to merge 72 commits into
Open
Conversation
pbrubeck
commented
Jul 16, 2026
pbrubeck
commented
Jul 16, 2026
pbrubeck
commented
Jul 16, 2026
pbrubeck
commented
Jul 16, 2026
pbrubeck
marked this pull request as draft
July 16, 2026 11:18
Contributor
Author
|
This PR should reimplement |
pbrubeck
commented
Jul 16, 2026
pbrubeck
commented
Jul 16, 2026
pbrubeck
commented
Jul 16, 2026
pbrubeck
commented
Jul 16, 2026
pbrubeck
commented
Jul 16, 2026
pbrubeck
commented
Jul 16, 2026
pbrubeck
commented
Jul 16, 2026
pbrubeck
commented
Jul 16, 2026
pbrubeck
commented
Jul 16, 2026
pbrubeck
commented
Jul 16, 2026
pbrubeck
commented
Jul 16, 2026
pbrubeck
commented
Jul 17, 2026
pbrubeck
commented
Jul 17, 2026
pbrubeck
force-pushed
the
pbrubeck/form-interp-tsfc
branch
from
July 17, 2026 14:42
a844911 to
f7f5ee3
Compare
pbrubeck
commented
Jul 17, 2026
pbrubeck
commented
Jul 17, 2026
pbrubeck
commented
Jul 17, 2026
pbrubeck
commented
Jul 22, 2026
pbrubeck
commented
Jul 22, 2026
pbrubeck
commented
Aug 10, 2026
This was referenced Aug 19, 2026
pbrubeck
commented
Aug 24, 2026
pbrubeck
force-pushed
the
pbrubeck/form-interp-tsfc
branch
from
September 10, 2026 16:38
37fe40f to
4cce203
Compare
The UFL reference-lowering branch has merged into main, so CI no longer needs to install it separately. Keep the FIAT override until its dual-enriched fixes are merged.
…ct/firedrake into pbrubeck/form-interp-tsfc
Contributor
Author
|
Commit vs main:
|
translate_element contracted an interpolation's target basis index where no assignment variable carries it, so gem.unconcatenate could not split the Concatenate that a direct sum tabulates into, and compiling a form over such an interpolation failed in gem.refactorise. Contract through split_contraction instead, which splits the contraction itself. The summands of a direct sum evaluate on points of their own, but translate_interpolate contracted the union of every summand's points, so each block picked up a factor of the other blocks' point counts. Leave those points free and contract them block by block, once the sum is split. test_form_interp_direct_sum covers this: an interpolation into a nodal space is the identity on that space, which the union of the points is not. spectral.flatten ordered the quadrature indices for sum factorisation and dropped every other sum index, which left the interpolation's basis contraction dense. Order the others after the quadrature indices, so that sum factorisation covers them too. The flop count of the new test form then grows as p^5.7 over degrees 3 to 8, rather than p^8, which tracks the p^5.8 of the same form written without an interpolation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KWajdMc5VFPbPuB1cupu9F
A fused kernel iterates over the cells of the expression that holds it, and a Form takes its subdomain data from its integrals alone, so the subset that a nested Interpolate carries never reaches the parloop iteration set. Fusing an interpolation whose source mesh covers only part of the target therefore read through invalid map entries: submesh cases segfaulted or returned wrong values, and a nested interpolation silently dropped the zero fill of the intermediate space. Fuse only an interpolation that maps cells to cells and that needs no subset. is_submesh_domain, is_same_mesh_interp and interp_cell_subset state that rule once, and SameMeshInterpolator, Interpolate._interpolator and _domain_is_compatible now share it instead of each restating it. An interpolation that crosses a codimension no longer fuses, so a trace onto a facet submesh assembles through its own interpolator rather than failing in code generation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016XyPBm8Hscju5UoWM7ojhU
validate_domains now takes a ufl.Interpolate as well as a Form, and compile_expression_dual_evaluation calls it before it builds anything. It admits the same mesh, submeshes of one family, and a point cloud immersed in the source mesh, and raises MismatchingDomainError for any other pair, which BaseFormAssembler owns. compile_form lowers an interpolation through compile_expression_dual_evaluation directly. BaseFormAssembler's visitor selects its assembler through get_form_assembler rather than repeating the rank dispatch. One name and one resolver serve the runtime tabulation that both kernel-argument dispatchers ask for, in place of a configurable prefix that the Firedrake callers rejected. get_assembly_entity_node_map serves assembly and interpolation, so the VertexOnlyMesh map policy no longer has a second copy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xw8suJa8EppFtunEvxQfq8
pbrubeck
commented
Sep 14, 2026
pbrubeck
commented
Sep 14, 2026
Comment on lines
+677
to
+688
| source_mesh = self.source_mesh.unique() | ||
| target_mesh = self.target_mesh.unique() | ||
| target, source = target_mesh.topology, source_mesh.topology | ||
| if target is not source and is_same_dim_submesh(source_mesh, target_mesh): | ||
| composed_map, result_integral_type = source.trans_mesh_entity_map(target, "cell", "everywhere", None) | ||
| if result_integral_type != "cell": | ||
| raise AssertionError("Only cell-cell interpolation supported.") | ||
| indices_active = composed_map.indices_active_with_halo | ||
| make_subset = not indices_active.all() | ||
| make_subset = target.comm.allreduce(make_subset, op=MPI.LOR) | ||
| if make_subset: | ||
| if not self.allow_missing_dofs: | ||
| raise ValueError("Iteration (sub)set unclear: run with `allow_missing_dofs=True`.") | ||
| if not target.comm.allreduce(indices_active.all(), op=MPI.LAND): | ||
| subset = op2.Subset(target.cell_set, numpy.where(indices_active)) | ||
| else: | ||
| # Do not need subset as target <= source. | ||
| pass | ||
| if subset is not None and not self.allow_missing_dofs: | ||
| raise ValueError("Iteration (sub)set unclear: run with `allow_missing_dofs=True`.") |
pbrubeck
commented
Sep 14, 2026
| with self._adjoint_weight.vec_ro as weight, self._weighted_dual_arg.dat.vec as dual: | ||
| dual.pointwiseMult(dual, weight) | ||
|
|
||
| def _get_tensor(self) -> Function: |
Contributor
Author
There was a problem hiding this comment.
move before _needs_adjoint_weighting
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Depends on
firedrakeproject/fiat#294