Repository navigation
Conversation
|
This PR should reimplement |
a844911 to
f7f5ee3
Compare
f6d360f to
3537776
Compare
Co-authored-by: Leo Collins <leocollins511@gmail.com>
|
Hi Pablo, I ran G-ADOPT on #5566 and repeated interpolations no longer build new loops, forward and adjoint. One case still does. A MIN/MAX interpolation into a new output builds a new loop on every call in |
# Conflicts: # firedrake/assemble.py # firedrake/interpolation.py # tests/firedrake/regression/test_interp_dual.py
UFL's restructure_base_form no longer preprocesses Forms nor takes complex_mode, so preprocess_base_form no longer needs the scalar type. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the target A MIN or MAX interpolation into a newly allocated output built a pyop3 loop on every call to set the identity of the reduction on the reached nodes. When the interpolation reaches every target cell, every node is reached, so the identity is written directly into the dat, including the ghost nodes. SameMeshInterpolator.covers_target holds the structural check on the subset and the submesh hierarchy, and _can_fuse_operator uses it too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Done. |
|
Thank you. For the record, in the G-ADOPT regression tests the loop cost about 6 ms per call against 0.7 ms before, which made each time step of forward cases such as |
|
A small heads-up. With current |
What are the before and after here? What is the local change? |
restructure_base_form moves every operator of rank above max_composed_rank out of its integrand, and the default of 1 moves the interpolation in inner(interpolate(u, V), v)*dx out of its Form. Pass max_composed_rank=2 when all of the operators of the expression can be fused into its kernel, as base_form_operands reports, and keep the default otherwise, so that an interpolation that cannot be fused is still assembled on its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…erpolation test_assemble_interp_pullback_of_action needs the operands of each Action in the restructured pullback to be numbered from zero (FEniCS/ufl#554). test_form_interp_mapped_derivative_mixed needs join_domains to let a MeshSequence subsume its component meshes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Install pbrubeck/join-domains-mesh-sequence, which stacks the join_domains fix for MeshSequence on FEniCS/ufl#554 (FEniCS/ufl#554). Drop this commit once both have merged into UFL main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # firedrake/interpolation.py # firedrake/mg/kernels.py
Expose needs_petsc on the TSFC Kernel that survives PR #5258. Leave ExpressionKernel unchanged and check its event directly in the interpolation path. Remove the stale kernel comment and unconditional stdio include, and clarify the compiler options documentation. Check logging-enabled and logging-disabled kernels in serial and MPI. AI assistance: Codex.
|
Before your fix one call of this interpolation took about 6 ms in a small serial benchmark of the expression in the G-ADOPT time step adaptor. With your fix it takes 0.5 ms. The code without this PR took 0.6 ms. It wrote the identity with |
connorjward
left a comment
There was a problem hiding this comment.
I think that this is fundamentally there. Please do go over all of the tests and make sure that they actually test useful things and don't do the antipatterns I pointed out.
And in future please please please make smaller PRs. Reviewing this now just took me 40 minutes (and I have reviewed it a bunch of times before).
| # its domain as a MeshSequence rather than a single mesh: every | ||
| # mesh it sequences is the same iteration, so they must agree. | ||
| if not isinstance(domain, MeshSequence): | ||
| raise |
There was a problem hiding this comment.
when you raise inside an except, you raise whatever brought you there.
| # An interpolation kernel never reads the output, it only reduces its | ||
| # results into it. Therefore, MIN and MAX are the WRITE reductions. | ||
| access = expr.options.access | ||
| if access is op3.MIN_RW: |
There was a problem hiding this comment.
This should just be an error no? Otherwise we are silently doing something the user doesn't expect and explicitly didn't ask for.
There was a problem hiding this comment.
The problem is the aliases for MIN == MIN_RW.
There was a problem hiding this comment.
I am open to redefining the alias. And whatever happens we're making a breaking change with pyop3. So we could just error if someone does op2.MIN -> op3.MIN and tell them to DTRT.
We could also allow this to accept strings ("min") instead of pyop3 intents. Intents in pyop3 are a more general concept than what we need here.
There was a problem hiding this comment.
I would rather open an issue and address it in a separate PR. For now, this does not break the API.
For now this is doing whatever the user expected with MIN before the introduction of MIN_RW / MIN_WRITE.
| if _FORM_CACHE_KEY not in form._cache: | ||
| form._cache[_FORM_CACHE_KEY] = {} | ||
| form._cache[_FORM_CACHE_KEY][key] = self | ||
| if key is not None: |
There was a problem hiding this comment.
Why can this be None? I would prefer something more explicit to disable assembler caching.
There was a problem hiding this comment.
The key was None for 0- and 2-forms (we do not cache assemblers for them). I added a _cache_assemblers property (True for 1-form, False otherwise).
Thanks. I always aim to keep them short but something quite pervasive like this can't be split into multiple PRs, as you might understand (pyop3). |
Co-authored-by: Connor Ward <c.ward20@imperial.ac.uk>
…pbrubeck/form-interp-tsfc
…uction FormAssembler subclasses now opt in to assembler caching with the _cache_assemblers flag, instead of returning a None cache key. The MIN/MAX reduction for cross-mesh interpolation is defined where it is used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BiE9EeaHedoaoiJ2UZAYKU
Summary
TSFC now compiles same-mesh
ufl.Interpolatenodes in normal Form kernels, and this PR ports the feature from PyOP2 to pyop3.InterpolateandForm(Interpolate)share one code generation path.MixedFunctionSpacesupportsmat_type="aij"natively.VertexOnlyMeshuses the same TSFC code generation.BaseFormAssembler.SameMeshInterpolatorno longer builds its ownop3.loops. It sends the interpolation through the form assembler:MINandMAXare reduced into the output asMIN_WRITEandMAX_WRITE. TSFC kernels add their result to the output argument, so pyop3 starts these temporaries from zero (see #5543).TwoFormAssembler._apply_bcnow takes the field label of a BC from the BC's own space. Before, a rectangular form such as an interpolation fromW = V * VintoVfailed.quadrature_spaceis now a cached property. The adjoint weight, the reciprocal of the number of cells in the iteration set whose closure contains each node, is cached on the mesh, keyed by the dual space and the subset.Stacked on #5543, which adds MIN/MAX reductions to pyop3 kernel calls.
Depends on firedrakeproject/fiat#294.
Fixes #5468
Evidence
mainwithout the fixes): 7 failures intests/firedrake/regression/test_interpolate.pytest_subset_adjoint_interpolation(new, 1 and 2 ranks) compares the matrix-free adjoint with the transposedaijmatrix, with and without a subset. It fails if the subset is left out of the cache key.Repeated interpolation on BDM2 16×16 through
Vq = V.quadrature_space, per call:Function(V).interpolate(q)Cofunction(Vq.dual()).interpolate(r)Merge Danger
Door: two-way
Blast Radius: interpolation
All same-mesh interpolation, and the Form kernels that contain it, now go through TSFC and the form assembler.
op3.Functiongets a new attribute, so kernel caches from before this change must be cleared withfiredrake-clean.V.quadrature_space()is now the propertyV.quadrature_space.The CI commits marked
DROP BEFORE MERGEinstall the FIAT branch and must be dropped before merging.Contribution assisted by Claude Code (Claude Opus 5.5).
🤖 Generated with Claude Code