Skip to content

Commit 25958a5

Browse files
Interpolate physically-mapped elements into VertexOnlyMesh (#4213)
--------- Co-authored-by: Connor Ward <c.ward20@imperial.ac.uk>
1 parent 1b24e17 commit 25958a5

File tree

6 files changed

+80
-16
lines changed

6 files changed

+80
-16
lines changed

firedrake/assemble.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1114,7 +1114,7 @@ class ZeroFormAssembler(ParloopFormAssembler):
11141114
11151115
Parameters
11161116
----------
1117-
form : ufl.Form or slate.TensorBasehe
1117+
form : ufl.Form or slate.TensorBase
11181118
0-form.
11191119
11201120
Notes

firedrake/interpolation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1194,7 +1194,7 @@ def _interpolator(V, tensor, expr, subset, arguments, access, bcs=None):
11941194
co = target_mesh.cell_orientations()
11951195
parloop_args.append(co.dat(op2.READ, co.cell_node_map()))
11961196
if needs_cell_sizes:
1197-
cs = target_mesh.cell_sizes
1197+
cs = source_mesh.cell_sizes
11981198
parloop_args.append(cs.dat(op2.READ, cs.cell_node_map()))
11991199
for coefficient in coefficients:
12001200
if isinstance(target_mesh.topology, firedrake.mesh.VertexOnlyMeshTopology):

tests/firedrake/regression/test_interpolate_zany.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def expect(V, which):
5151
return a + b
5252

5353

54-
def test_interpolate(V, mesh, which, expect, tolerance):
54+
def test_interpolate_zany_into_cg(V, mesh, which, expect, tolerance):
5555
degree = V.ufl_element().degree()
5656
Vcg = FunctionSpace(mesh, "P", degree)
5757

@@ -71,3 +71,64 @@ def test_interpolate(V, mesh, which, expect, tolerance):
7171
g.interpolate(a + b)
7272

7373
assert numpy.allclose(norm(g - expect), 0, atol=tolerance)
74+
75+
76+
@pytest.fixture
77+
def vom(mesh):
78+
return VertexOnlyMesh(mesh, [(0.5, 0.5), (0.31, 0.72)])
79+
80+
81+
@pytest.fixture
82+
def expr_at_vom(V, which, vom):
83+
mesh = V.mesh()
84+
degree = V.ufl_element().degree()
85+
x, y = SpatialCoordinate(mesh)
86+
expr = (x + y)**degree
87+
88+
if which == "coefficient":
89+
P0 = FunctionSpace(vom, "DG", 0)
90+
elif which == "grad":
91+
expr = ufl.algorithms.expand_derivatives(grad(expr))
92+
P0 = VectorFunctionSpace(vom, "DG", 0)
93+
94+
fvom = Function(P0)
95+
point = Constant([0] * mesh.geometric_dimension())
96+
expr_at_pt = ufl.replace(expr, {SpatialCoordinate(mesh): point})
97+
for i, pt in enumerate(vom.coordinates.dat.data_ro):
98+
point.assign(pt)
99+
fvom.dat.data[i] = numpy.asarray(expr_at_pt, dtype=float)
100+
return fvom
101+
102+
103+
def test_interpolate_zany_into_vom(V, mesh, which, expr_at_vom):
104+
degree = V.ufl_element().degree()
105+
x, y = SpatialCoordinate(mesh)
106+
expr = (x + y)**degree
107+
108+
f = Function(V)
109+
f.project(expr, solver_parameters={"ksp_type": "preonly",
110+
"pc_type": "lu"})
111+
fexpr = f
112+
vexpr = TestFunction(V)
113+
if which == "grad":
114+
fexpr = grad(fexpr)
115+
vexpr = grad(vexpr)
116+
117+
P0 = expr_at_vom.function_space()
118+
119+
# Interpolate a Function into P0(vom)
120+
f_at_vom = assemble(Interpolate(fexpr, P0))
121+
assert numpy.allclose(f_at_vom.dat.data_ro, expr_at_vom.dat.data_ro)
122+
123+
# Construct a Cofunction on P0(vom)*
124+
Fvom = Cofunction(P0.dual()).assign(1)
125+
expected_action = assemble(action(Fvom, expr_at_vom))
126+
127+
# Interpolate a Function into Fvom
128+
f_at_vom = assemble(Interpolate(fexpr, Fvom))
129+
assert numpy.allclose(f_at_vom, expected_action)
130+
131+
# Interpolate a TestFunction into Fvom
132+
expr_vom = assemble(Interpolate(vexpr, Fvom))
133+
f_at_vom = assemble(action(expr_vom, f))
134+
assert numpy.allclose(f_at_vom, expected_action)

tsfc/driver.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,13 @@ def compile_expression_dual_evaluation(expression, to_element, ufl_element, *,
208208
# Collect required coefficients and determine numbering
209209
coefficients = extract_coefficients(expression)
210210
orig_coefficients = extract_coefficients(orig_expression)
211-
coefficient_numbers = tuple(orig_coefficients.index(c) for c in coefficients)
211+
coefficient_numbers = tuple(map(orig_coefficients.index, coefficients))
212212
builder.set_coefficient_numbers(coefficient_numbers)
213213

214+
elements = [f.ufl_element() for f in (*coefficients, *arguments)]
215+
214216
needs_external_coords = False
215-
if has_type(expression, GeometricQuantity) or any(fem.needs_coordinate_mapping(c.ufl_element()) for c in coefficients):
217+
if has_type(expression, GeometricQuantity) or any(map(fem.needs_coordinate_mapping, elements)):
216218
# Create a fake coordinate coefficient for a domain.
217219
coords_coefficient = ufl.Coefficient(ufl.FunctionSpace(domain, domain.ufl_coordinate_element()))
218220
builder.domain_coordinate[domain] = coords_coefficient

tsfc/fem.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,8 @@ class GemPointContext(ContextBase):
336336
def basis_evaluation(self, finat_element, mt, entity_id):
337337
return finat_element.point_evaluation(mt.local_derivatives,
338338
self.point_expr,
339-
(self.integration_dim, entity_id))
339+
(self.integration_dim, entity_id),
340+
CoordinateMapping(mt, self))
340341

341342

342343
class Translator(MultiFunction, ModifiedTerminalMixin, ufl2gem.Mixin):

tsfc/kernel_interface/firedrake_loopy.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@ def _coefficient(self, coefficient, name):
104104
self.coefficient_map[coefficient] = expr
105105
return expr
106106

107+
def set_coordinates(self, domain):
108+
"""Prepare the coordinate field.
109+
110+
:arg domain: :class:`ufl.Domain`
111+
"""
112+
# Create a fake coordinate coefficient for a domain.
113+
f = Coefficient(FunctionSpace(domain, domain.ufl_coordinate_element()))
114+
self.domain_coordinate[domain] = f
115+
self._coefficient(f, "coords")
116+
107117
def set_cell_sizes(self, domain):
108118
"""Setup a fake coefficient for "cell sizes".
109119
@@ -304,16 +314,6 @@ def set_arguments(self, arguments):
304314
self.return_variables = return_variables
305315
self.argument_multiindices = argument_multiindices
306316

307-
def set_coordinates(self, domain):
308-
"""Prepare the coordinate field.
309-
310-
:arg domain: :class:`ufl.Domain`
311-
"""
312-
# Create a fake coordinate coefficient for a domain.
313-
f = Coefficient(FunctionSpace(domain, domain.ufl_coordinate_element()))
314-
self.domain_coordinate[domain] = f
315-
self._coefficient(f, "coords")
316-
317317
def set_coefficients(self, integral_data, form_data):
318318
"""Prepare the coefficients of the form.
319319

0 commit comments

Comments
 (0)