Repository navigation
Conversation
Solving derivative(J, u) == 0 for a functional J that contains interpolate(u, P), with P a space on a vertex-only mesh, failed in two places in the base form assembler: - The residual assembler of the solver uses mat_type="matfree", which skipped the expansion of derivatives. The chain rule through the interpolation turns the residual into a FormSum, which the 1-form assembler rejects. The mat_type only applies to 2-forms, so derivatives of 0-forms and 1-forms are now always expanded. - Every inner matrix of the DAG was allocated with the integral types of the output matrix. The mass matrix on the vertex-only mesh then asked for interior facets, which that mesh does not have. Only the output matrix now uses those integral types. The test also needs FEniCS/ufl#525, which fixes the symbolic Hessian. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Install UFL from danshapero/fix-bfo-hessian (FEniCS/ufl#525) in the shared and documentation CI environments, so that CI tests this PR against the matching UFL fix. Drop this commit once #525 merges. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…late Reconstructing an Interpolate now accepts the argument_slots keyword, and replaces a plain UFL Coargument in the dual slot with a Firedrake Argument on the dual of the target space. Also expand derivatives of matfree 0- and 1-forms that contain base form operators, so that the DAG traversal can assemble them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Expanding the derivatives of a Form with base form operators may turn it into another BaseForm, which determines how it is assembled. Plain Forms and Slate tensors keep their type, so TSFC expands their derivatives. A 'matfree' matrix is still expanded when its action is taken. `preprocess_base_form` used `mat_type` to decide, so a 1-form with base form operators, whose default `mat_type` is 'matfree', was not expanded. `ufl.replace` then expanded it inside the assembly visitor, turning it into a FormSum. Expand it in preprocessing instead, and drop the second traversal in the visitor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FEniCS/ufl#525 was split into #530, #531 and #532. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UFL now simplifies Action(N(u; v*), F) to N(u; F) for any base form operator N and 1-form F, so the assembler can no longer split an operator from its dual slot by building an Action: restructure case (5) and the Interpolate split folded straight back. Instead, when an operator cannot consume its assembled dual slot, assemble N(u; vhat) with a coargument vhat and contract it with the assembled dual slot: - An Interpolate whose dual slot assembles to a Matrix. - An ExternalOperator with a Cofunction or Matrix in its dual slot and no assembly method registered for it. `assembly_method()` looks this up. The numerics of the Action branch move to `contract`, which both paths use. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An assembled Function has no arguments, so the contraction of an external operator with its assembled dual slot failed. Use the arguments of N(u; vhat) instead. Rename dual_slot_coargument to replace_dual_slot_by_coargument, which is what it does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s integrals UFL returns a ZeroBaseForm for a vanishing derivative, and a derivative with base form operators may be a FormSum, neither of which has integrals(). The Hessian of a solve whose residual is linear in its solution raised AttributeError in _assemble_soa_eq_rhs. is_zero checks for a ZeroBaseForm or an empty Form, as AssembleBlock already does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UFL no longer simplifies Actions and Adjoints when they are constructed. preprocess_base_form calls ufl's restructure_base_form after expanding derivatives, and the Interpolators restructure the Actions they build when splitting a Cofunction dual argument. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UFL now splits a rank 2 dual slot off a base form operator into an Action, so an Interpolate or ExternalOperator never sees an assembled Matrix in its dual slot. An ExternalOperator without an assembly method for its assembled Cofunction dual slot is now reconstructed once, with a coargument in place of the Cofunction, and its result is contracted with the Cofunction. This removes replace_dual_slot_by_coargument and contracted_arguments. contract becomes a static method of BaseFormAssembler. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It now sits next to _matrix_free_assembler, the assembler it selects. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sian # Conflicts: # firedrake/assemble.py
pbrubeck
commented
Oct 23, 2026
pbrubeck
commented
Oct 23, 2026
Co-authored-by: Pablo Brubeck <brubeck@protonmail.com>
This branch has not been deployed
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.
Resolves #5477: solving
derivative(J, u) == 0failed for a functionalJcontaininginterpolate(u, P)withPon aVertexOnlyMesh. The Hessian of the data term isIᵀ M I.Depends on FEniCS/ufl#532, which computes that Hessian symbolically. The
DROP BEFORE MERGEcommit installs that branch in CI.Derivative expansion
preprocess_base_formexpands the derivatives of a Form only if it has base form operators, whatever itsmat_type. Plain Forms and Slate tensors are left to TSFC. Before, the"matfree"residual assembler skipped the expansion, so the chain rule through the interpolation produced aFormSumthatOneFormAssemblerrejects."matfree"2-form is still expanded only when its action is taken.MatrixFreeAssembleraccepts any 2-form BaseForm, such as a FormSum, and keeps the tree intact so that a single implicit matrix assembles its action.Dual slot of base form operators
UFL now simplifies
Action(N(u; v̂), F)toN(u; F)for any base form operator and 1-formF, so the assembler can no longer split an operator from its dual slot by building anAction. Restructure case (5) and theInterpolate(v, 2-form)split are removed. Instead, an operator that cannot consume its assembled dual slot is assembled asN(u; v̂)and contracted with it:Interpolatewhose dual slot assembles to a Matrix;ExternalOperatorwith a Cofunction or Matrix in its dual slot and no assembly method registered for it (assembly_method()).The numerics of the
Actionbranch move tocontract, which both paths use.Vertex-only meshes
Only the output matrix uses
allocation_integral_types. An inner matrix may live on a mesh without them, for example a vertex-only mesh.Tests
test_solve_interp_hessian: a Newton solve with the Hessian of an interpolation onto a vertex-only mesh.test_matfree_form_sum_uses_implicit_matrixandtest_matfree_slate_sum_product_uses_implicit_matrix.test_reduced_functional.py::test_interpolateruns a second-order Taylor test with the Hessian.🤖 Generated with Claude Code
Moved from fork PR #5478 to the upstream branch. Prepared with Codex.