Skip to content

Clement interpolant #4244

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open

Clement interpolant #4244

wants to merge 5 commits into from

Conversation

jwallwork23
Copy link
Contributor

I've been meaning to upstream some functionality from Animate that might be useful for the wider Firedrake community, so am starting with this. Let me know if it's of interest!

This PR adds functionality for the Clément interpolant, which transfers fields from P1 space to P0 space via local volume averaging. We've found this particularly useful for gradient/Hessian recovery in P1 space (can upstream those later).

I've included tests on simplices in 1D, 2D, and 3D, and for scalar-, vector-, and matrix-valued functions.

A limitation of the approach is that the local averaging breaks down on the boundary, but it does a good job in the domain interior. I also have an implementation of local facet averaging on the boundary that I can provide, if it'd be of use.

@article{Clement:1975,
  title={Approximation by finite element functions using local regularization},
  author={Cl{\'e}ment, Ph},
  journal={Revue fran{\c{c}}aise d'automatique, informatique, recherche op{\'e}rationnelle. Analyse num{\'e}rique},
  volume={9},
  number={R2},
  pages={77--84},
  year={1975},
  publisher={EDP Sciences}
}

http://www.numdam.org/item/M2AN_1975__9_2_77_0.pdf

For arguments, see :class:`.Interpolator`.
"""

def __new__(cls, expr, V, **kwargs):
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this check go to __init__ ?

if rank != len(self.V.value_shape):
raise ValueError(f"Rank-{rank} input inconsistent with target space.")
mesh = self.V.mesh()
dim = mesh.topological_dimension()
Copy link
Contributor

Choose a reason for hiding this comment

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

This dim should be the number of copies of the element V.block_size. I think a VectorFunctionSpace and TensorFunctionSpace could be indexed with a double index (flat_dof_id, component_id). So rank could possibly be unrestricted and there wouldn't have to be special cases if you treat the rank-0 as the general case.


@pytest.mark.parametrize("rank", range(3))
@pytest.mark.parametrize("dim", range(1, 4))
def test_clement_interpolator_simplex(dim, rank):
Copy link
Contributor

@pbrubeck pbrubeck Apr 22, 2025

Choose a reason for hiding this comment

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

We should test a vector with 3 components in a 2D mesh.

domain = "{[i]: 0 <= i < patch.dofs}"
instructions = "patch[i] = patch[i] + vol[0]"
keys = {"vol": (volume, op2.READ), "patch": (patch_volume, op2.RW)}
parloops.par_loop((domain, instructions), dX, keys)
Copy link
Contributor

Choose a reason for hiding this comment

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

The patch volume here could become a cached_property so it is not recomputed every time. Maybe we could attach this property to the mesh, so it can be reused more widely across different Interpolators.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea. I've addressed this in 34ceb67, using the cell_sizes cached property as a template.

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

Successfully merging this pull request may close these issues.

2 participants