Skip to content

Conversation

@leo-collins
Copy link
Contributor

@leo-collins leo-collins commented Sep 26, 2025

Simplifies interpolation code and introduces new features. So far:

  • Implemented assembly of cross-mesh interpolation operators, both forward and adjoint.
  • We can now matfree adjoint interpolate cross-mesh and VomOntoVom. The renumbering malarkey is completely removed.
  • Removed interp_data dictionary in favour of the InterpolateOptions dataclass. We get type hinting, better IDE support, single source of truth for these options.
  • Interpolator becomes an internal object. I removed the __new__ method and dispatch instead with a get_interpolator function.
  • Removed frozen_interpolator logic

@leo-collins leo-collins changed the title Leo/refactor interpolate refactor interpolation Sep 26, 2025
@connorjward
Copy link
Contributor

@leo-collins this looks like it includes changes from the other ongoing interpolation PRs. This will be much easier to review if you point this PR at that branch instead of main. Then we are only seeing the new changes.

@leo-collins leo-collins force-pushed the leo/refactor_interpolate branch from deb0a8c to 4097207 Compare September 26, 2025 13:19
@pbrubeck pbrubeck changed the base branch from main to pbrubeck/interp-adjoint-explicit September 26, 2025 13:33
@pbrubeck
Copy link
Contributor

  • Interpolator becomes an internal object. I removed the __new__ method and dispatch instead with a _get_interpolator function.

This is fine, but we might want to preserve a user-facing interface for reusable interpolators.

@pbrubeck pbrubeck force-pushed the pbrubeck/interp-adjoint-explicit branch 7 times, most recently from 210bae0 to 8d631f8 Compare September 30, 2025 21:56
else:
return self._interpolate(*cofunctions, output=tensor, adjoint=needs_adjoint,
default_missing_val=default_missing_val)
assert isinstance(tensor, Function | Cofunction | None)
Copy link
Contributor

@pbrubeck pbrubeck Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the 0-form interpolate we should also allow Constant here, it also supports assign.

@pbrubeck
Copy link
Contributor

pbrubeck commented Nov 5, 2025

The commit history includes commits from other PRs. Would it be much of a problem to drop them?


if isinstance(target_topology, VertexOnlyMeshTopology):
if isinstance(source_topology, VertexOnlyMeshTopology):
return VomOntoVomInterpolator(expr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does VomOntoVomInterpolator support MixedFunctionSpace, or are we never expecting to build one for this case? There should be a ValueError in the constructor if we go for the latter.

Parameters
----------
expr : ufl.Interpolate | ufl.ZeroBaseForm
The symbolic interpolation expression, or a zero form. Zero forms
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"zero form" is too close to 0-form, which is not necesarily equal to 0.

Suggested change
The symbolic interpolation expression, or a zero form. Zero forms
The symbolic interpolation expression, or a ZeroBaseForm. ZeroBaseForms

if access == op2.INC:
copyin += (tensor.zero,)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the 1-form adjoint on a mixed space, we might be adding different sub-interpolates onto the same tensor. The zeroing should be handled outside the for loop of line 633

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tensor is already the indexed sub-tensor in this case. Can you write a test for this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do that

@leo-collins leo-collins force-pushed the leo/refactor_interpolate branch from 89550eb to 9fd2344 Compare November 10, 2025 14:21
@leo-collins leo-collins marked this pull request as ready for review November 10, 2025 14:29
Copy link
Contributor

@connorjward connorjward left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't reviewed this in great detail but this seems really good!

)


class Interpolate(ufl.Interpolate):
@dataclass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@dataclass
@dataclass(kw_only=True)

I think we should disallow positional arguments for this.

"""
from firedrake.assemble import assemble
# If assembling the operator, we need the concrete permutation matrix
matfree = False if self.rank == 2 else self.matfree
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems bizarre. A user could pass a matfree option that gets ignored.

Copy link
Contributor Author

@leo-collins leo-collins Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't assemble the cross-mesh interpolation matrix if matfree=True. By doing this the user can do assemble(interpolate(TrialFunction(V), U)) and get the operator without having to pass an additional matfree=False.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In which case I think we should change (and document) the interface to

def interpolate(expr, ..., *, matfree=None):
  if matfree is None:
    if some_condition:
      matfree = False
    else:
      matfree = True

because currently the user could run

assemble(interpolate(TrialFunction(V), U, matfree=True))

and the option would be silently ignored.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same could be said about the allow_missing_dofs and default_missing_val parameters if the user is doing same-mesh interpolation. I think it's fine if the docstring says something like "this parameter only applies if interpolating between a VertexOnlyMesh and its input_ordering".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well the right answer there is to disallow those options for same-mesh interpolation. I don't want to force you to make big changes here though so I'd be happy provided that this behaviour is made clear in the docstring and you open an issue about it.

for indices, form in firedrake.formmanipulation.split_form(expr):
if isinstance(form, ufl.ZeroBaseForm):
# Get sub-interpolators and sub-bcs for each block
Isub: dict[tuple[int, int], tuple[Interpolator, list[DirichletBC]]] = {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great idea. Big fan.

Copy link
Contributor

@connorjward connorjward left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.

Copy link
Member

@dham dham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

subject to checking that adjoint interpolation on mixed spaces is really correct, this is great. thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants