Extract linear combination from ufl-expression - #504
Conversation
Written with the help of Gemini (August 2026). I take full responsibility for the correctness and testing of the code.
Co-authored-by: Jørgen Schartum Dokken <dokken92@gmail.com>
|
Wouldn't |
I would need to sub-class it to detect a |
But the UFL standard can identify piecewise and global constants. I think we should try to do that here, since Firedrake also has a Real element |
Great point! I've adjusted the code, and it passes my test suite in DOLFINx with real element coefficients. |
| @process.register(ufl.coefficient.BaseCoefficient) | ||
| def _(self, o, **kwargs): | ||
| raise NotImplementedError(f"Unsupported UFL node type for linear combinations: {type(o)}") |
There was a problem hiding this comment.
Why aren't we supporting this case?
There was a problem hiding this comment.
In the same way that we are not supporting ufl.core.expr.Expr as a general term.
The only two classes that for now uses BaseCoefficient is Cofunction and Function, which have specializations for in the code. This would be a good check if someone implements a new class based on BaseCoefficient, which they would have to check if the current implementation is correct.
There was a problem hiding this comment.
Wouldn't we want linear combinations to just work on newly implemented subclasses of BaseCoefficient?
There was a problem hiding this comment.
I guess it depends on how it should be handled.
Currently there is a split between how Coefficient and CoFunction is handled, as coefficient checks for
ufl.checks.is_scalar_constant_expression(o) to factor it out of a linear combination, while this is not the case for the CoFunction, as it is always a scalar_constant_expression.
There was a problem hiding this comment.
I don't see why would one want distinct treatment for Coefficient, Cofunction and Matrix.
… they are scalar constant expressions
| if ufl.checks.is_scalar_constant_expression(o): | ||
| return o |
There was a problem hiding this comment.
What are the assumptions here? Are we allowing affine expressions, i.e. sum_i a_i v_i + Constant?
There was a problem hiding this comment.
The assumption here is that if we have (sum a_i v_i), then in some cases a_i might be a scalar valued real space coefficient, and should therefore be returned as a float rather than the tuple (1, a_i).
We are not allowing for a_i v_i + constant in this PR.
There was a problem hiding this comment.
Why is this unique to Coefficient? We should give no special treatment to Coefficient vs Cofunction
It is often common to extract linear combinations$\sum_i c_i u_i$ or a set of scalar weights $c_i$ and function $u_i\in V$ for quick assignment without having to call interpolation or projection, ref #486.
AI assistance
The code and documentation was generated by prompting Gemini (August 2026) based on my own initial implementations without a DAGTraverser.
I reviewed, tested, and take responsibility for the final contribution.
Deprecated example after discussions in this PR
Initially to catch scalar valued real elements, one would have to overload the dag-traverser. This is no longer true, as we use
ufl.checks.is_scalar_constant_expressionto have a common base for both Firedrake and FEniCS.The old example can be found below: