Skip to content

Commit a12e9e7

Browse files
ksagiyamindiamai
authored andcommitted
hex: add tests for interior facet integration of geometric quantities (#3878)
1 parent dbf22d4 commit a12e9e7

File tree

4 files changed

+75
-21
lines changed

4 files changed

+75
-21
lines changed

tests/firedrake/regression/test_integral_hex.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,42 @@ def test_integral_hex_interior_facet_solve(mesh_from_file):
5353
solve(a == L, sol, bcs=[bc])
5454
err = assemble((sol - f)**2 * dx)**0.5
5555
assert err < 1.e-14
56+
57+
58+
def make_nonuniform_box_mesh():
59+
mesh = BoxMesh(2, 1, 1, 2., 1., 1., hexahedral=True)
60+
coordV = mesh.coordinates.function_space()
61+
coords = Function(coordV).assign(mesh.coordinates)
62+
bc = DirichletBC(coordV.sub(0), 3., 2)
63+
bc.apply(coords)
64+
return Mesh(coords)
65+
66+
67+
@pytest.mark.parametrize('GQ_expected', [(CellSize, sqrt(6.)),
68+
(CellVolume, 2.),
69+
(FacetArea, 1.)])
70+
def test_integral_hex_interior_facet_geometric_quantities(GQ_expected):
71+
GQ, expected = GQ_expected
72+
mesh = make_nonuniform_box_mesh()
73+
x, y, z = SpatialCoordinate(mesh)
74+
e = y('+') * z('-')**2
75+
E = assemble(e * dS)
76+
assert abs(E - 1. / 6.) < 1.e-14
77+
a = GQ(mesh)('-')
78+
A = assemble(a * dS)
79+
assert abs(A - expected) < 1.e-14
80+
EA = assemble(e * a * dS)
81+
assert abs(EA - E * A) < 1.e-14
82+
83+
84+
def test_integral_hex_interior_facet_facet_avg():
85+
mesh = make_nonuniform_box_mesh()
86+
x, y, z = SpatialCoordinate(mesh)
87+
e = y('+') * z('-')**2
88+
E = assemble(e * dS)
89+
assert abs(E - 1. / 6.) < 1.e-14
90+
a = facet_avg(y('-')**3 * z('+')**4)
91+
A = assemble(a * dS)
92+
assert abs(A - 1. / 20.) < 1.e-14
93+
EA = assemble(e * a * dS)
94+
assert abs(EA - E * A) < 1.e-14

tsfc/fem.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,18 @@
4747
class ContextBase(ProxyKernelInterface):
4848
"""Common UFL -> GEM translation context."""
4949

50-
keywords = ('ufl_cell',
51-
'fiat_cell',
52-
'integral_type',
53-
'integration_dim',
54-
'entity_ids',
55-
'argument_multiindices',
56-
'facetarea',
57-
'index_cache',
58-
'scalar_type')
50+
keywords = (
51+
'ufl_cell',
52+
'fiat_cell',
53+
'integral_type',
54+
'integration_dim',
55+
'entity_ids',
56+
'argument_multiindices',
57+
'facetarea',
58+
'index_cache',
59+
'scalar_type',
60+
'use_canonical_quadrature_point_ordering',
61+
)
5962

6063
def __init__(self, interface, **kwargs):
6164
ProxyKernelInterface.__init__(self, interface)
@@ -113,6 +116,9 @@ def translator(self):
113116

114117
@cached_property
115118
def use_canonical_quadrature_point_ordering(self):
119+
# Directly set use_canonical_quadrature_point_ordering = False in context
120+
# for translation of special nodes, e.g., CellVolume, FacetArea, CellOrigin, and CellVertices,
121+
# as quadrature point ordering is not relevant for those node types.
116122
return isinstance(self.fiat_cell, UFCHexahedron) and self.integral_type in ['exterior_facet', 'interior_facet']
117123

118124

@@ -162,6 +168,7 @@ def jacobian_at(self, point):
162168
expr = NegativeRestricted(expr)
163169
config = {"point_set": PointSingleton(point)}
164170
config.update(self.config)
171+
config.update(use_canonical_quadrature_point_ordering=False) # quad point ordering not relevant.
165172
context = PointSetContext(**config)
166173
expr = self.preprocess(expr, context)
167174
return map_expr_dag(context.translator, expr)
@@ -174,6 +181,7 @@ def detJ_at(self, point):
174181
expr = NegativeRestricted(expr)
175182
config = {"point_set": PointSingleton(point)}
176183
config.update(self.config)
184+
config.update(use_canonical_quadrature_point_ordering=False) # quad point ordering not relevant.
177185
context = PointSetContext(**config)
178186
expr = self.preprocess(expr, context)
179187
return map_expr_dag(context.translator, expr)
@@ -222,6 +230,7 @@ def physical_edge_lengths(self):
222230
expr = ufl.as_vector([ufl.sqrt(ufl.dot(expr[i, :], expr[i, :])) for i in range(num_edges)])
223231
config = {"point_set": PointSingleton(cell.make_points(sd, 0, sd+1)[0])}
224232
config.update(self.config)
233+
config.update(use_canonical_quadrature_point_ordering=False) # quad point ordering not relevant.
225234
context = PointSetContext(**config)
226235
expr = self.preprocess(expr, context)
227236
return map_expr_dag(context.translator, expr)
@@ -244,6 +253,7 @@ def physical_points(self, point_set, entity=None):
244253
if entity is not None:
245254
config.update({name: getattr(self.interface, name)
246255
for name in ["integration_dim", "entity_ids"]})
256+
config.update(use_canonical_quadrature_point_ordering=False) # quad point ordering not relevant.
247257
context = PointSetContext(**config)
248258
expr = self.preprocess(expr, context)
249259
mapped = map_expr_dag(context.translator, expr)
@@ -539,7 +549,7 @@ def translate_cellvolume(terminal, mt, ctx):
539549

540550
config = {name: getattr(ctx, name)
541551
for name in ["ufl_cell", "index_cache", "scalar_type"]}
542-
config.update(interface=interface, quadrature_degree=degree)
552+
config.update(interface=interface, quadrature_degree=degree, use_canonical_quadrature_point_ordering=False)
543553
expr, = compile_ufl(integrand, PointSetContext(**config), point_sum=True)
544554
return expr
545555

@@ -553,7 +563,7 @@ def translate_facetarea(terminal, mt, ctx):
553563
config = {name: getattr(ctx, name)
554564
for name in ["ufl_cell", "integration_dim", "scalar_type",
555565
"entity_ids", "index_cache"]}
556-
config.update(interface=ctx, quadrature_degree=degree)
566+
config.update(interface=ctx, quadrature_degree=degree, use_canonical_quadrature_point_ordering=False)
557567
expr, = compile_ufl(integrand, PointSetContext(**config), point_sum=True)
558568
return expr
559569

@@ -567,7 +577,7 @@ def translate_cellorigin(terminal, mt, ctx):
567577

568578
config = {name: getattr(ctx, name)
569579
for name in ["ufl_cell", "index_cache", "scalar_type"]}
570-
config.update(interface=ctx, point_set=point_set)
580+
config.update(interface=ctx, point_set=point_set, use_canonical_quadrature_point_ordering=False)
571581
context = PointSetContext(**config)
572582
return context.translator(expression)
573583

@@ -580,7 +590,7 @@ def translate_cell_vertices(terminal, mt, ctx):
580590

581591
config = {name: getattr(ctx, name)
582592
for name in ["ufl_cell", "index_cache", "scalar_type"]}
583-
config.update(interface=ctx, point_set=ps)
593+
config.update(interface=ctx, point_set=ps, use_canonical_quadrature_point_ordering=False)
584594
context = PointSetContext(**config)
585595
expr = context.translator(ufl_expr)
586596

tsfc/kernel_interface/common.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ def compile_gem(self, ctx):
215215
# Let the kernel interface inspect the optimised IR to register
216216
# what kind of external data is required (e.g., cell orientations,
217217
# cell sizes, etc.).
218-
oriented, needs_cell_sizes, tabulations = self.register_requirements(expressions)
218+
oriented, needs_cell_sizes, tabulations, need_facet_orientation = self.register_requirements(expressions)
219219

220220
# Extract Variables that are actually used
221221
active_variables = gem.extract_type(expressions, gem.Variable)
@@ -226,7 +226,7 @@ def compile_gem(self, ctx):
226226
impero_c = impero_utils.compile_gem(assignments, index_ordering, remove_zeros=True)
227227
except impero_utils.NoopError:
228228
impero_c = None
229-
return impero_c, oriented, needs_cell_sizes, tabulations, active_variables
229+
return impero_c, oriented, needs_cell_sizes, tabulations, active_variables, need_facet_orientation
230230

231231
def fem_config(self):
232232
"""Return a dictionary used with fem.compile_ufl.
@@ -429,6 +429,7 @@ def check_requirements(ir):
429429
in one pass."""
430430
cell_orientations = False
431431
cell_sizes = False
432+
facet_orientation = False
432433
rt_tabs = {}
433434
for node in traversal(ir):
434435
if isinstance(node, gem.Variable):
@@ -438,7 +439,9 @@ def check_requirements(ir):
438439
cell_sizes = True
439440
elif node.name.startswith("rt_"):
440441
rt_tabs[node.name] = node.shape
441-
return cell_orientations, cell_sizes, tuple(sorted(rt_tabs.items()))
442+
elif node.name == "facet_orientation":
443+
facet_orientation = True
444+
return cell_orientations, cell_sizes, tuple(sorted(rt_tabs.items())), facet_orientation
442445

443446

444447
def prepare_constant(constant, number):

tsfc/kernel_interface/firedrake_loopy.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
import loopy as lp
1313

14-
from tsfc import kernel_args, fem
14+
from tsfc import kernel_args
1515
from finat.element_factory import create_element
1616
from tsfc.kernel_interface.common import KernelBuilderBase as _KernelBuilderBase, KernelBuilderMixin, get_index_names, check_requirements, prepare_coefficient, prepare_arguments, prepare_constant
1717
from tsfc.loopy import generate as generate_loopy
@@ -190,7 +190,7 @@ def set_coefficient_numbers(self, coefficient_numbers):
190190
def register_requirements(self, ir):
191191
"""Inspect what is referenced by the IR that needs to be
192192
provided by the kernel interface."""
193-
self.oriented, self.cell_sizes, self.tabulations = check_requirements(ir)
193+
self.oriented, self.cell_sizes, self.tabulations, _ = check_requirements(ir)
194194

195195
def set_output(self, o):
196196
"""Produce the kernel return argument"""
@@ -368,7 +368,7 @@ def construct_kernel(self, name, ctx, log=False):
368368
:arg log: bool if the Kernel should be profiled with Log events
369369
:returns: :class:`Kernel` object
370370
"""
371-
impero_c, oriented, needs_cell_sizes, tabulations, active_variables = self.compile_gem(ctx)
371+
impero_c, oriented, needs_cell_sizes, tabulations, active_variables, need_facet_orientation = self.compile_gem(ctx)
372372
if impero_c is None:
373373
return self.construct_empty_kernel(name)
374374
info = self.integral_data_info
@@ -418,8 +418,10 @@ def construct_kernel(self, name, ctx, log=False):
418418
elif info.integral_type in ["interior_facet", "interior_facet_vert"]:
419419
int_loopy_arg = lp.GlobalArg("facet", numpy.uint32, shape=(2,))
420420
args.append(kernel_args.InteriorFacetKernelArg(int_loopy_arg))
421-
# Will generalise this in the submesh PR.
422-
if fem.PointSetContext(**self.fem_config()).use_canonical_quadrature_point_ordering:
421+
# The submesh PR will introduce a robust mechanism to check if a Variable
422+
# is actually used in the final form of the expression, so there will be
423+
# no need to get "need_facet_orientation" from self.compile_gem().
424+
if need_facet_orientation:
423425
if info.integral_type == "exterior_facet":
424426
ext_ornt_loopy_arg = lp.GlobalArg("facet_orientation", gem.uint_type, shape=(1,))
425427
args.append(kernel_args.ExteriorFacetOrientationKernelArg(ext_ornt_loopy_arg))

0 commit comments

Comments
 (0)