• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

FEniCS / ffcx / 20180168774

12 Dec 2025 09:18PM UTC coverage: 84.612% (+0.003%) from 84.609%
20180168774

Pull #808

github

schnellerhase
format
Pull Request #808: Centralise common backend routines

80 of 81 new or added lines in 7 files covered. (98.77%)

2 existing lines in 2 files now uncovered.

4168 of 4926 relevant lines covered (84.61%)

0.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.18
/ffcx/codegeneration/common.py
1
# Copyright (C) 2025 Paul T. Kühner
2
#
3
# This file is part of FFCx.(https://www.fenicsproject.org)
4
#
5
# SPDX-License-Identifier:    LGPL-3.0-or-later
6

7
"""Common code for backend implemenations."""
8

9
import string
1✔
10
from functools import singledispatch
1✔
11
from typing import NamedTuple
1✔
12

13
import basix
1✔
14
import numpy as np
1✔
15

16
from ffcx.ir.representation import ExpressionIR, FormIR, IntegralIR
1✔
17

18

19
def template_keys(template: str) -> set[str]:
1✔
20
    """Set of expected data keys of a template."""
21
    return set(fname for _, fname, _, _ in string.Formatter().parse(template) if fname)
1✔
22

23

24
class IntegralData(NamedTuple):
1✔
25
    """Sorted integral data."""
26

27
    names: list[str]
1✔
28
    ids: list[int]
1✔
29
    offsets: list[int]
1✔
30
    domains: list[list[basix.CellType]]
1✔
31

32

33
def integral_data(ir: FormIR) -> IntegralData:
1✔
34
    """Extracts sorted intergral data from form."""
35
    names, ids, domains = [], [], []
1✔
36
    offsets = [0]
1✔
37
    # Note: the order of this list is defined by the enum ufcx_integral_type in ufcx.h
38
    for itg_type in ("cell", "exterior_facet", "interior_facet", "vertex", "ridge"):
1✔
39
        _ids = ir.subdomain_ids[itg_type]
1✔
40
        id_sort = np.argsort(_ids)
1✔
41

42
        ids += [_ids[i] for i in id_sort]
1✔
43
        names += [ir.integral_names[itg_type][i] for i in id_sort]
1✔
44
        domains += [ir.integral_domains[itg_type][i] for i in id_sort]
1✔
45

46
        offsets.append(offsets[-1] + sum(len(d) for d in domains[offsets[-1] :]))
1✔
47

48
    return IntegralData(names, ids, offsets, domains)
1✔
49

50

51
class KernelTensorSizes(NamedTuple):
1✔
52
    """Size information of kernel input data."""
53

54
    A: int
1✔
55
    w: int
1✔
56
    c: int
1✔
57
    coords: int
1✔
58
    local_index: int
1✔
59
    permutation: int
1✔
60

61

62
@singledispatch
1✔
63
def tensor_sizes(ir: IntegralIR | ExpressionIR) -> KernelTensorSizes:
1✔
64
    """Compute tensor sizes given IR type."""
NEW
65
    raise NotImplementedError
×
66

67

68
@tensor_sizes.register
1✔
69
def _(ir: IntegralIR) -> KernelTensorSizes:
1✔
70
    """Compute tensor sizes of integral IR input data."""
71
    A = np.prod(ir.expression.tensor_shape, dtype=int)
1✔
72
    w = sum(coeff.ufl_element().dim for coeff in ir.expression.coefficient_offsets.keys())
1✔
73
    c = sum(
1✔
74
        np.prod(constant.ufl_shape, dtype=int)
75
        for constant in ir.expression.original_constant_offsets.keys()
76
    )
77
    coords = ir.expression.number_coordinate_dofs * 3
1✔
78
    local_index = 2  # TODO: this is just an upper bound, harmful?
1✔
79
    permutation = 2 if ir.expression.needs_facet_permutations else 0
1✔
80

81
    return KernelTensorSizes(A, w, c, coords, local_index, permutation)
1✔
82

83

84
@tensor_sizes.register
1✔
85
def _(ir: ExpressionIR) -> KernelTensorSizes:
1✔
86
    """Compute tensor sizes of expression IR input data."""
87
    num_points = next(iter(ir.expression.integrand))[1].points.shape[0]
1✔
88
    num_components = np.prod(ir.expression.shape, dtype=np.int32)
1✔
89
    num_argument_dofs = np.prod(ir.expression.tensor_shape, dtype=np.int32)
1✔
90
    A = num_points * num_components * num_argument_dofs  # ref. ufcx.h
1✔
91
    w = sum(coeff.ufl_element().dim for coeff in ir.expression.coefficient_offsets.keys())
1✔
92
    c = sum(
1✔
93
        np.prod(constant.ufl_shape, dtype=int)
94
        for constant in ir.expression.original_constant_offsets.keys()
95
    )
96
    coords = ir.expression.number_coordinate_dofs * 3
1✔
97
    local_index = 2  # TODO: this is just an upper bound, harmful?
1✔
98
    permutation = 2 if ir.expression.needs_facet_permutations else 0
1✔
99

100
    return KernelTensorSizes(A, w, c, coords, local_index, permutation)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc