Skip to content

Commit b565bcf

Browse files
authored
Merge branch 'main' into dokken/real_handling
2 parents 1934003 + 712a57d commit b565bcf

File tree

9 files changed

+152
-36
lines changed

9 files changed

+152
-36
lines changed

.github/workflows/pythonapp.yml

+10-4
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ jobs:
2121
strategy:
2222
matrix:
2323
os: [ubuntu-latest]
24-
python-version: ['3.9', '3.10', '3.11', '3.12']
24+
python-version: ["3.9", "3.10", "3.11", "3.12"]
2525
include:
2626
- os: windows-2022
27-
python-version: '3.11'
27+
python-version: "3.11"
2828
- os: macos-latest
29-
python-version: '3.12'
29+
python-version: "3.12"
3030

3131
steps:
3232
- name: Checkout FFCx
@@ -45,13 +45,19 @@ jobs:
4545
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
4646
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
4747
48+
- name: Set up CMake
49+
if: runner.os == 'Windows'
50+
uses: lukka/get-cmake@latest
51+
with:
52+
cmakeVersion: "~3.30.0"
53+
4854
- name: Install dependencies (non-Python, Linux)
4955
if: runner.os == 'Linux'
5056
run: |
5157
sudo apt-get install -y graphviz libgraphviz-dev ninja-build pkg-config
5258
- name: Install dependencies (non-Python, macOS)
5359
if: runner.os == 'macOS'
54-
run: brew install ninja pkg-config
60+
run: brew install ninja
5561

5662
- name: Install FEniCS dependencies (Python, Unix)
5763
if: runner.os == 'Linux' || runner.os == 'macOS'

ffcx/codegeneration/access.py

+6-7
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ def reference_normal(self, mt, tabledata, access):
228228
"""Access a reference normal."""
229229
cellname = ufl.domain.extract_unique_domain(mt.terminal).ufl_cell().cellname()
230230
if cellname in ("interval", "triangle", "tetrahedron", "quadrilateral", "hexahedron"):
231-
table = L.Symbol(f"{cellname}_reference_facet_normals", dtype=L.DataType.REAL)
231+
table = L.Symbol(f"{cellname}_reference_normals", dtype=L.DataType.REAL)
232232
facet = self.symbols.entity("facet", mt.restriction)
233233
return table[facet][mt.component[0]]
234234
else:
@@ -238,7 +238,7 @@ def cell_facet_jacobian(self, mt, tabledata, num_points):
238238
"""Access a cell facet jacobian."""
239239
cellname = ufl.domain.extract_unique_domain(mt.terminal).ufl_cell().cellname()
240240
if cellname in ("triangle", "tetrahedron", "quadrilateral", "hexahedron"):
241-
table = L.Symbol(f"{cellname}_reference_facet_jacobian", dtype=L.DataType.REAL)
241+
table = L.Symbol(f"{cellname}_cell_facet_jacobian", dtype=L.DataType.REAL)
242242
facet = self.symbols.entity("facet", mt.restriction)
243243
return table[facet][mt.component[0]][mt.component[1]]
244244
elif cellname == "interval":
@@ -250,7 +250,7 @@ def reference_cell_edge_vectors(self, mt, tabledata, num_points):
250250
"""Access a reference cell edge vector."""
251251
cellname = ufl.domain.extract_unique_domain(mt.terminal).ufl_cell().cellname()
252252
if cellname in ("triangle", "tetrahedron", "quadrilateral", "hexahedron"):
253-
table = L.Symbol(f"{cellname}_reference_edge_vectors", dtype=L.DataType.REAL)
253+
table = L.Symbol(f"{cellname}_reference_cell_edge_vectors", dtype=L.DataType.REAL)
254254
return table[mt.component[0]][mt.component[1]]
255255
elif cellname == "interval":
256256
raise RuntimeError(
@@ -263,9 +263,8 @@ def reference_facet_edge_vectors(self, mt, tabledata, num_points):
263263
"""Access a reference facet edge vector."""
264264
cellname = ufl.domain.extract_unique_domain(mt.terminal).ufl_cell().cellname()
265265
if cellname in ("tetrahedron", "hexahedron"):
266-
table = L.Symbol(f"{cellname}_reference_edge_vectors", dtype=L.DataType.REAL)
267-
facet = self.symbols.entity("facet", mt.restriction)
268-
return table[facet][mt.component[0]][mt.component[1]]
266+
table = L.Symbol(f"{cellname}_reference_facet_edge_vectors", dtype=L.DataType.REAL)
267+
return table[mt.component[0]][mt.component[1]]
269268
elif cellname in ("interval", "triangle", "quadrilateral"):
270269
raise RuntimeError(
271270
"The reference cell facet edge vectors doesn't make sense for interval "
@@ -280,7 +279,7 @@ def facet_orientation(self, mt, tabledata, num_points):
280279
if cellname not in ("interval", "triangle", "tetrahedron"):
281280
raise RuntimeError(f"Unhandled cell types {cellname}.")
282281

283-
table = L.Symbol(f"{cellname}_facet_orientations", dtype=L.DataType.INT)
282+
table = L.Symbol(f"{cellname}_facet_orientation", dtype=L.DataType.INT)
284283
facet = self.symbols.entity("facet", mt.restriction)
285284
return table[facet]
286285

ffcx/codegeneration/expression_generator.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,14 @@ def generate(self):
6161

6262
def generate_geometry_tables(self):
6363
"""Generate static tables of geometry data."""
64-
# Currently we only support circumradius
6564
ufl_geometry = {
65+
ufl.geometry.FacetEdgeVectors: "facet_edge_vectors",
66+
ufl.geometry.CellFacetJacobian: "cell_facet_jacobian",
6667
ufl.geometry.ReferenceCellVolume: "reference_cell_volume",
67-
ufl.geometry.ReferenceNormal: "reference_facet_normals",
68+
ufl.geometry.ReferenceFacetVolume: "reference_facet_volume",
69+
ufl.geometry.ReferenceCellEdgeVectors: "reference_cell_edge_vectors",
70+
ufl.geometry.ReferenceFacetEdgeVectors: "reference_facet_edge_vectors",
71+
ufl.geometry.ReferenceNormal: "reference_normals",
6872
}
6973

7074
cells: dict[Any, set[Any]] = {t: set() for t in ufl_geometry.keys()} # type: ignore
@@ -312,7 +316,7 @@ def get_arg_factors(self, blockdata, block_rank, indices):
312316

313317
def new_temp_symbol(self, basename):
314318
"""Create a new code symbol named basename + running counter."""
315-
name = "%s%d" % (basename, self.symbol_counters[basename])
319+
name = f"{basename}{self.symbol_counters[basename]:d}"
316320
self.symbol_counters[basename] += 1
317321
return L.Symbol(name, dtype=L.DataType.SCALAR)
318322

ffcx/codegeneration/geometry.py

+13-13
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,18 @@ def write_table(tablename, cellname):
1515
"""Write a table."""
1616
if tablename == "facet_edge_vertices":
1717
return facet_edge_vertices(tablename, cellname)
18-
if tablename == "reference_facet_jacobian":
19-
return reference_facet_jacobian(tablename, cellname)
18+
if tablename == "cell_facet_jacobian":
19+
return cell_facet_jacobian(tablename, cellname)
2020
if tablename == "reference_cell_volume":
2121
return reference_cell_volume(tablename, cellname)
2222
if tablename == "reference_facet_volume":
2323
return reference_facet_volume(tablename, cellname)
24-
if tablename == "reference_edge_vectors":
25-
return reference_edge_vectors(tablename, cellname)
26-
if tablename == "facet_reference_edge_vectors":
27-
return facet_reference_edge_vectors(tablename, cellname)
28-
if tablename == "reference_facet_normals":
29-
return reference_facet_normals(tablename, cellname)
24+
if tablename == "reference_cell_edge_vectors":
25+
return reference_cell_edge_vectors(tablename, cellname)
26+
if tablename == "reference_facet_edge_vectors":
27+
return reference_facet_edge_vectors(tablename, cellname)
28+
if tablename == "reference_normals":
29+
return reference_normals(tablename, cellname)
3030
if tablename == "facet_orientation":
3131
return facet_orientation(tablename, cellname)
3232
raise ValueError(f"Unknown geometry table name: {tablename}")
@@ -56,7 +56,7 @@ def facet_edge_vertices(tablename, cellname):
5656
return L.ArrayDecl(symbol, values=out, const=True)
5757

5858

59-
def reference_facet_jacobian(tablename, cellname):
59+
def cell_facet_jacobian(tablename, cellname):
6060
"""Write a reference facet jacobian."""
6161
celltype = getattr(basix.CellType, cellname)
6262
out = basix.cell.facet_jacobians(celltype)
@@ -83,7 +83,7 @@ def reference_facet_volume(tablename, cellname):
8383
return L.VariableDecl(symbol, volumes[0])
8484

8585

86-
def reference_edge_vectors(tablename, cellname):
86+
def reference_cell_edge_vectors(tablename, cellname):
8787
"""Write reference edge vectors."""
8888
celltype = getattr(basix.CellType, cellname)
8989
topology = basix.topology(celltype)
@@ -94,7 +94,7 @@ def reference_edge_vectors(tablename, cellname):
9494
return L.ArrayDecl(symbol, values=out, const=True)
9595

9696

97-
def facet_reference_edge_vectors(tablename, cellname):
97+
def reference_facet_edge_vectors(tablename, cellname):
9898
"""Write facet reference edge vectors."""
9999
celltype = getattr(basix.CellType, cellname)
100100
topology = basix.topology(celltype)
@@ -121,7 +121,7 @@ def facet_reference_edge_vectors(tablename, cellname):
121121
return L.ArrayDecl(symbol, values=out, const=True)
122122

123123

124-
def reference_facet_normals(tablename, cellname):
124+
def reference_normals(tablename, cellname):
125125
"""Write reference facet normals."""
126126
celltype = getattr(basix.CellType, cellname)
127127
out = basix.cell.facet_outward_normals(celltype)
@@ -134,4 +134,4 @@ def facet_orientation(tablename, cellname):
134134
celltype = getattr(basix.CellType, cellname)
135135
out = basix.cell.facet_orientations(celltype)
136136
symbol = L.Symbol(f"{cellname}_{tablename}", dtype=L.DataType.REAL)
137-
return L.ArrayDecl(symbol, values=out, const=True)
137+
return L.ArrayDecl(symbol, values=np.asarray(out), const=True)

ffcx/codegeneration/integral_generator.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def get_var(self, quadrature_rule, v):
114114

115115
def new_temp_symbol(self, basename):
116116
"""Create a new code symbol named basename + running counter."""
117-
name = "%s%d" % (basename, self.symbol_counters[basename])
117+
name = f"{basename}{self.symbol_counters[basename]:d}"
118118
self.symbol_counters[basename] += 1
119119
return L.Symbol(name, dtype=L.DataType.SCALAR)
120120

@@ -197,12 +197,12 @@ def generate_geometry_tables(self):
197197
"""Generate static tables of geometry data."""
198198
ufl_geometry = {
199199
ufl.geometry.FacetEdgeVectors: "facet_edge_vertices",
200-
ufl.geometry.CellFacetJacobian: "reference_facet_jacobian",
200+
ufl.geometry.CellFacetJacobian: "cell_facet_jacobian",
201201
ufl.geometry.ReferenceCellVolume: "reference_cell_volume",
202202
ufl.geometry.ReferenceFacetVolume: "reference_facet_volume",
203-
ufl.geometry.ReferenceCellEdgeVectors: "reference_edge_vectors",
204-
ufl.geometry.ReferenceFacetEdgeVectors: "facet_reference_edge_vectors",
205-
ufl.geometry.ReferenceNormal: "reference_facet_normals",
203+
ufl.geometry.ReferenceCellEdgeVectors: "reference_cell_edge_vectors",
204+
ufl.geometry.ReferenceFacetEdgeVectors: "reference_facet_edge_vectors",
205+
ufl.geometry.ReferenceNormal: "reference_normals",
206206
ufl.geometry.FacetOrientation: "facet_orientation",
207207
}
208208
cells: dict[Any, set[Any]] = {t: set() for t in ufl_geometry.keys()} # type: ignore

ffcx/codegeneration/symbols.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def coefficient_dof_access_blocked(
166166
def coefficient_value(self, mt):
167167
"""Symbol for variable holding value or derivative component of coefficient."""
168168
c = self.coefficient_numbering[mt.terminal]
169-
return L.Symbol(format_mt_name("w%d" % (c,), mt), dtype=L.DataType.SCALAR)
169+
return L.Symbol(format_mt_name(f"w{c:d}", mt), dtype=L.DataType.SCALAR)
170170

171171
def constant_index_access(self, constant, index):
172172
"""Constant index access."""

ffcx/ir/analysis/visualise.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def visualise_graph(Gx, filename):
4444
label = ex.value()
4545
elif isinstance(ex, (Indexed, ReferenceValue)):
4646
label = str(ex)
47-
G.add_node(nd, label="[%d] %s" % (nd, label))
47+
G.add_node(nd, label=f"[{nd:d}] {label}")
4848

4949
arg = strip_modified_terminal(ex)
5050
if isinstance(arg, Argument):

ffcx/ir/elementtables.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,9 @@ def generate_psi_table_name(
194194
- Q unique ID of quadrature rule, to distinguish between tables
195195
in a mixed quadrature rule setting
196196
"""
197-
name = "FE%d" % element_counter
197+
name = f"FE{element_counter:d}"
198198
if flat_component is not None:
199-
name += "_C%d" % flat_component
199+
name += f"_C{flat_component:d}"
200200
if any(derivative_counts):
201201
name += "_D" + "".join(str(d) for d in derivative_counts)
202202
name += {None: "", "cell": "_AC", "facet": "_AF"}[averaged]

test/test_jit_expression.py

+107
Original file line numberDiff line numberDiff line change
@@ -323,3 +323,110 @@ def test_facet_expression(compile_args):
323323

324324
# Check that facet normal is pointing out of the cell
325325
assert np.dot(midpoint - coords[i], output) > 0
326+
327+
328+
def test_facet_geometry_expressions(compile_args):
329+
"""Test various geometrical quantities for facet expressions."""
330+
cell = basix.CellType.triangle
331+
mesh = ufl.Mesh(basix.ufl.element("Lagrange", cell, 1, shape=(2,)))
332+
dtype = np.float64
333+
points = np.array([[0.5]], dtype=dtype)
334+
c_type = "double"
335+
c_xtype = "double"
336+
ffi = cffi.FFI()
337+
338+
# Prepare reference geometry and working arrays
339+
coords = np.array([[1, 0, 0], [3, 0, 0], [0, 2, 0]], dtype=dtype)
340+
u_coeffs = np.array([], dtype=dtype)
341+
consts = np.array([], dtype=dtype)
342+
entity_index = np.empty(1, dtype=np.intc)
343+
quad_perm = np.array([0], dtype=np.dtype("uint8"))
344+
ffi_data = {
345+
"const": ffi.cast(f"{c_type} *", consts.ctypes.data),
346+
"coeff": ffi.cast(f"{c_type} *", u_coeffs.ctypes.data),
347+
"coords": ffi.cast(f"{c_xtype} *", coords.ctypes.data),
348+
"entity_index": ffi.cast("int *", entity_index.ctypes.data),
349+
"quad_perm": ffi.cast("uint8_t *", quad_perm.ctypes.data),
350+
}
351+
352+
def check_expression(expression_class, output_shape, entity_values, reference_values):
353+
obj = ffcx.codegeneration.jit.compile_expressions(
354+
[(expression_class(mesh), points)], cffi_extra_compile_args=compile_args
355+
)[0][0]
356+
output = np.zeros(output_shape, dtype=dtype)
357+
for i, ref_val in enumerate(reference_values):
358+
output[:] = 0
359+
entity_index[0] = i
360+
obj.tabulate_tensor_float64(
361+
ffi.cast(f"{c_type} *", output.ctypes.data),
362+
ffi_data["coeff"],
363+
ffi_data["const"],
364+
ffi_data["coords"],
365+
ffi_data["entity_index"],
366+
ffi_data["quad_perm"],
367+
)
368+
np.testing.assert_allclose(output, ref_val)
369+
370+
check_expression(
371+
ufl.geometry.CellFacetJacobian, (2, 1), entity_index, basix.cell.facet_jacobians(cell)
372+
)
373+
check_expression(
374+
ufl.geometry.ReferenceFacetVolume,
375+
(1,),
376+
entity_index,
377+
basix.cell.facet_reference_volumes(cell),
378+
)
379+
check_expression(
380+
ufl.geometry.ReferenceCellEdgeVectors,
381+
(3, 2),
382+
entity_index,
383+
np.array(
384+
[
385+
[
386+
basix.geometry(cell)[j] - basix.geometry(cell)[i]
387+
for i, j in basix.topology(cell)[1]
388+
]
389+
]
390+
),
391+
)
392+
393+
394+
def test_facet_geometry_expressions_3D(compile_args):
395+
cell = basix.CellType.tetrahedron
396+
c_el = basix.ufl.element("Lagrange", cell, 1, shape=(3,))
397+
mesh = ufl.Mesh(c_el)
398+
dtype = np.float64
399+
points = np.array([[0.33, 0.33]], dtype=dtype)
400+
c_type = "double"
401+
c_xtype = "double"
402+
ffi = cffi.FFI()
403+
404+
# Prepare reference geometry and working arrays
405+
coords = np.array([[1, 0, 0], [3, 0, 0], [0, 2, 0], [0, 0, 1]], dtype=dtype)
406+
u_coeffs = np.array([], dtype=dtype)
407+
consts = np.array([], dtype=dtype)
408+
entity_index = np.empty(1, dtype=np.intc)
409+
quad_perm = np.array([0], dtype=np.dtype("uint8"))
410+
411+
# Check ReferenceFacetEdgeVectors
412+
output = np.zeros((3, 3))
413+
triangle_edges = basix.topology(basix.CellType.triangle)[1]
414+
ref_fev = []
415+
topology = basix.topology(cell)
416+
geometry = basix.geometry(cell)
417+
for facet in topology[-2]:
418+
ref_fev += [geometry[facet[j]] - geometry[facet[i]] for i, j in triangle_edges]
419+
420+
ref_fev_code = ffcx.codegeneration.jit.compile_expressions(
421+
[(ufl.geometry.ReferenceFacetEdgeVectors(mesh), points)],
422+
cffi_extra_compile_args=compile_args,
423+
)[0][0]
424+
ref_fev_code.tabulate_tensor_float64(
425+
ffi.cast(f"{c_type} *", output.ctypes.data),
426+
ffi.cast(f"{c_type} *", u_coeffs.ctypes.data),
427+
ffi.cast(f"{c_type} *", consts.ctypes.data),
428+
ffi.cast(f"{c_xtype} *", coords.ctypes.data),
429+
ffi.cast("int *", entity_index.ctypes.data),
430+
ffi.cast("uint8_t *", quad_perm.ctypes.data),
431+
)
432+
np.testing.assert_allclose(output, np.asarray(ref_fev)[:3, :])

0 commit comments

Comments
 (0)