Skip to content
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

Update and extend ufc_expression #432

Merged
merged 38 commits into from
Feb 3, 2022
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
5c9bbdc
Expression via c++
michalhabera Dec 17, 2020
f552b64
Merge branch 'master' into michal/expressions-cpp
michalhabera Feb 1, 2021
d05573f
Work on c++ pipeline for ufc_expression
michalhabera Feb 18, 2021
6567b12
Merge branch 'master' into michal/expressions-cpp
michalhabera Feb 19, 2021
cc3fdca
Add missing expression info
michalhabera Mar 19, 2021
ca8f51f
Merge with main
michalhabera Mar 19, 2021
ae1e975
Rename expression rank
michalhabera Mar 19, 2021
12b6b82
Point to branches in CI
michalhabera Mar 19, 2021
6172fc9
Update ufc_expression
michalhabera Mar 29, 2021
7627127
Remove cpp demo file
michalhabera Mar 29, 2021
a83f823
Update docs
michalhabera Mar 29, 2021
f482358
Stash working tree
michalhabera Aug 10, 2021
a15ce11
More updates for expressions
michalhabera Nov 13, 2021
8fe1c86
Merge branch 'main' into michal/expressions-cpp
michalhabera Nov 13, 2021
b14995a
Add number of argument dofs
michalhabera Nov 26, 2021
0ac7b4c
Remove old changes
michalhabera Nov 26, 2021
cdbaf53
Use f-strings
michalhabera Nov 26, 2021
1625892
More f-strings
michalhabera Nov 29, 2021
9a9c185
Rename to match ufc_form
michalhabera Nov 29, 2021
e7b048c
Merge branch 'main' into michal/expressions-cpp
michalhabera Nov 30, 2021
e44faa0
Handle more expressions in UFL file at once
michalhabera Dec 1, 2021
6bb4dec
Merge branch 'main' into michal/expressions-cpp
michalhabera Dec 1, 2021
dba3258
Add empty form_data
michalhabera Dec 1, 2021
b9f295c
Merge branch 'main' into michal/expressions-cpp
jhale Jan 19, 2022
50058e4
ufcx changes
jhale Jan 19, 2022
1aec005
Stash recent changes
michalhabera Jan 19, 2022
3d880d2
Expression updates for Argument handling
michalhabera Jan 24, 2022
42cae85
Update recent test
michalhabera Jan 24, 2022
a89580c
Fix naming uniqueness
michalhabera Jan 24, 2022
b8dea91
Handle all objects in common pass
michalhabera Jan 25, 2022
11106e7
Fix unique obj handling
michalhabera Jan 25, 2022
aa5edbe
Remove print
michalhabera Jan 25, 2022
81acc85
Point to dolfinx branch
michalhabera Jan 25, 2022
fe44d16
Merge branch 'main' into michal/expressions-cpp
michalhabera Jan 25, 2022
8e2d6e5
Add missing expression struct members
michalhabera Jan 26, 2022
d6633db
Merge branch 'michal/expressions-cpp' of github.com:FEniCS/ffcx into …
michalhabera Jan 26, 2022
0b49981
Add in geometry table
jorgensd Jan 26, 2022
1e1b0e2
Fix flake
michalhabera Jan 27, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/dolfin-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
python3 -m pip install --upgrade pip
- name: Install UFL and Basix
run: |
python3 -m pip install git+https://github.com/FEniCS/ufl.git
python3 -m pip install git+https://github.com/FEniCS/ufl.git@michal/expressions
python3 -m pip install git+https://github.com/FEniCS/basix.git

- name: Install FFCx
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pythonapp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
- name: Install FEniCS dependencies (Python)
run: |
python -m pip install --upgrade pip setuptools
python -m pip install git+https://github.com/FEniCS/ufl.git
python -m pip install git+https://github.com/FEniCS/ufl.git@michal/expressions
python -m pip install git+https://github.com/FEniCS/basix.git

- name: Install FFCx
Expand Down
34 changes: 34 additions & 0 deletions demo/GradExpression.ufl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright (C) 2021 Michal Habera
#
# This file is part of FFCX.
#
# FFC is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# FFC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with FFC. If not, see <http://www.gnu.org/licenses/>.
#
# Defines an Expression which evaluates gradient of an Argument
# at predefined set of points.
#
# Compile this form with FFC: ffcx GradExpression.ufl

element = FiniteElement("Lagrange", triangle, 2)
coordinate_el = VectorElement("Lagrange", triangle, 1)
mesh = Mesh(coordinate_el)

V = FunctionSpace(mesh, element)

u = TrialFunction(V)
f = Coefficient(V)
c = Constant(mesh)

expressions = [(c * f * grad(u), [[0.0, 0.1], [0.2, 0.3]]),
(c * f * u, [[0.0, 1.0], [1.0, 0.0]])]
6 changes: 4 additions & 2 deletions ffcx/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,12 @@ def analyze_ufl_objects(ufl_objects: typing.Union[typing.List[ufl.form.Form], ty
meshes = ufl_objects
unique_coordinate_elements = set(mesh.ufl_coordinate_element() for mesh in meshes)
elif isinstance(ufl_objects[0], tuple) and isinstance(ufl_objects[0][0], ufl.core.expr.Expr):
form_data = ()
if not all(isinstance(expression[0], ufl.core.expr.Expr) for expression in ufl_objects):
raise RuntimeError("All objects in the list must be UFL Expressions")

for expression in ufl_objects:
original_expression = expression[0]
points = expression[1]
points = numpy.asarray(expression[1])
expression = expression[0]

unique_elements.update(ufl.algorithms.extract_elements(expression))
Expand Down
2 changes: 1 addition & 1 deletion ffcx/codegeneration/C/format_lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def iter_indented_lines(snippets, level=0):
- tuple,list: Yield lines from recursive application of this function to list items.

"""
tabsize = 4
tabsize = 2
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This change is unrelated, but makes complicated kernels more readable.

indentation = ' ' * (tabsize * level)
if isinstance(snippets, str):
for line in snippets.split("\n"):
Expand Down
11 changes: 11 additions & 0 deletions ffcx/codegeneration/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from ffcx.codegeneration.C.format_lines import format_indented_lines
from ffcx.codegeneration.C.cnodes import CNode
from ffcx.ir.representation import ir_expression
from ffcx.naming import cdtype_to_numpy

logger = logging.getLogger("ffcx")

Expand Down Expand Up @@ -68,6 +69,16 @@ def generator(ir, parameters):
d["topological_dimension"] = ir.points.shape[1]
d["needs_facet_permutations"] = "true" if ir.needs_facet_permutations else "false"
d["scalar_type"] = parameters["scalar_type"]
d["np_scalar_type"] = cdtype_to_numpy(parameters["scalar_type"])
d["rank"] = len(ir.tensor_shape)

if len(ir.tensor_shape) > 0:
d["num_argument_dofs_init"] = L.ArrayDecl(
"static int", f"num_argument_dofs_{ir.name}", values=ir.tensor_shape, sizes=len(ir.tensor_shape))
d["num_argument_dofs"] = f"num_argument_dofs_{ir.name}"
else:
d["num_argument_dofs_init"] = ""
d["num_argument_dofs"] = L.Null()

# Check that no keys are redundant or have been missed
from string import Formatter
Expand Down
17 changes: 11 additions & 6 deletions ffcx/codegeneration/expressions_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,34 @@
factory = """
// Code for expression {factory_name}

void tabulate_expression_{factory_name}({scalar_type}* restrict A,
const {scalar_type}* restrict w,
const {scalar_type}* restrict c,
const double* restrict coordinate_dofs)
void tabulate_tensor_{factory_name}({scalar_type}* restrict A,
const {scalar_type}* restrict w,
const {scalar_type}* restrict c,
const double* restrict coordinate_dofs,
const int* restrict entity_local_index,
const uint8_t* restrict quadrature_permutation)
{{
{tabulate_expression}
}}

{points_init}
{value_shape_init}
{original_coefficient_positions_init}
{num_argument_dofs_init}

ufc_expression {factory_name} =
{{
.tabulate_expression = tabulate_expression_{factory_name},
.tabulate_tensor_{np_scalar_type} = tabulate_tensor_{factory_name},
.num_coefficients = {num_coefficients},
.num_points = {num_points},
.topological_dimension = {topological_dimension},
.needs_facet_permutations = {needs_facet_permutations},
.points = {points},
.value_shape = {value_shape},
.num_components = {num_components},
.original_coefficient_positions = {original_coefficient_positions}
.original_coefficient_positions = {original_coefficient_positions},
.rank = {rank},
.num_argument_dofs = {num_argument_dofs}
}};

// End of code for expression {factory_name}
Expand Down
52 changes: 34 additions & 18 deletions ffcx/codegeneration/ufc.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/// This is UFC (Unified Form-assembly Code)
/// This code is released into the public domain.
///
/// The FEniCS Project (http://www.fenicsproject.org/) 2006-2019.
/// The FEniCS Project (http://www.fenicsproject.org/) 2006-2021.
///
/// UFC defines the interface between code generated by FFCx and the
/// DOLFINx C++ library. Changes here must be reflected both in the FFCx
Expand Down Expand Up @@ -272,29 +272,37 @@ extern "C"

typedef struct ufc_expression
{

/// Evaluate expression into tensor A with compiled evaluation
/// points
/// Evaluate expression into tensor A with compiled evaluation points
///
/// @param[out] A
/// @param[in] w Coefficients attached to the expression.
/// Dimensions: w[coefficient][dof].
/// @param[in] c Constants attached to the expression. Dimensions:
/// c[constant][dim].
/// @param[in] coordinate_dofs Values of degrees of freedom of
/// coordinate element. Defines the geometry of the cell.
/// Dimensions: coordinate_dofs[num_dofs][3].
void (*tabulate_expression)(double* restrict A,
const double* restrict w,
const double* restrict c,
const double* restrict coordinate_dofs);

/// Positions of coefficients in original expression
const int* original_coefficient_positions;
/// Dimensions: A[num_points][num_components][num_argument_dofs]
///
/// @see ufc_tabulate_tensor
///
ufc_tabulate_tensor_float32* tabulate_tensor_float32;
ufc_tabulate_tensor_float64* tabulate_tensor_float64;
ufc_tabulate_tensor_longdouble* tabulate_tensor_longdouble;
ufc_tabulate_tensor_complex64* tabulate_tensor_complex64;
ufc_tabulate_tensor_complex128* tabulate_tensor_complex128;

/// String identifying the expression
const char* signature;

/// Number of coefficients
int num_coefficients;

/// Number of constants
int num_constants;

/// Original coefficient position for each coefficient
const int* original_coefficient_positions;

/// List of names of coefficients
const char** coefficient_names;

/// List of names of constants
const char** constant_names;

/// Number of evaluation points
int num_points;

Expand All @@ -314,6 +322,14 @@ extern "C"

/// Number of components of return_shape
int num_components;

/// Rank, i.e. number of arguments
int rank;

/// Number of dofs for Arguments
/// Dimensions: num_argument_dofs[rank].
const int* num_argument_dofs;

} ufc_expression;

/// This class defines the interface for the assembly of the global
Expand Down
16 changes: 11 additions & 5 deletions ffcx/ir/representation.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
'name', 'element_dimensions', 'params', 'unique_tables', 'unique_table_types', 'integrand',
'table_dofmaps', 'coefficient_numbering', 'coefficient_offsets',
'integral_type', 'entitytype', 'tensor_shape', 'expression_shape', 'original_constant_offsets',
'original_coefficient_positions', 'points', 'needs_facet_permutations'])
'original_coefficient_positions', 'points', 'needs_facet_permutations', 'coefficient_names', 'constant_names'])

ir_data = namedtuple('ir_data', ['elements', 'dofmaps', 'integrals', 'forms', 'expressions'])

Expand Down Expand Up @@ -104,7 +104,7 @@ def compute_ir(analysis, object_names, prefix, parameters, visualise):
for (i, fd) in enumerate(analysis.form_data)
]

ir_expressions = [_compute_expression_ir(expr, i, prefix, analysis, parameters, visualise)
ir_expressions = [_compute_expression_ir(expr, i, prefix, analysis, parameters, visualise, object_names)
for i, expr in enumerate(analysis.expressions)]

return ir_data(elements=ir_elements, dofmaps=ir_dofmaps,
Expand Down Expand Up @@ -388,10 +388,10 @@ def _compute_form_ir(form_data, form_id, prefix, form_names, integral_names, ele
ir["num_coefficients"] = len(form_data.reduced_coefficients)
ir["num_constants"] = len(form_data.original_form.constants())

ir["coefficient_names"] = [object_names.get(id(obj), "w%d" % j)
ir["coefficient_names"] = [object_names.get(id(obj), f"w{j}")
for j, obj in enumerate(form_data.reduced_coefficients)]

ir["constant_names"] = [object_names.get(id(obj), "c%d" % j)
ir["constant_names"] = [object_names.get(id(obj), f"c{j}")
for j, obj in enumerate(form_data.original_form.constants())]

ir["original_coefficient_position"] = form_data.original_coefficient_positions
Expand Down Expand Up @@ -451,7 +451,7 @@ def _compute_form_ir(form_data, form_id, prefix, form_names, integral_names, ele
return ir_form(**ir)


def _compute_expression_ir(expression, index, prefix, analysis, parameters, visualise):
def _compute_expression_ir(expression, index, prefix, analysis, parameters, visualise, object_names):
"""Compute intermediate representation of expression."""
logger.info(f"Computing IR for expression {index}")

Expand Down Expand Up @@ -505,6 +505,12 @@ def _compute_expression_ir(expression, index, prefix, analysis, parameters, visu
for coeff in coefficients:
original_coefficient_positions.append(original_coefficients.index(coeff))

ir["coefficient_names"] = [object_names.get(id(obj), f"w{j}")
for j, obj in enumerate(coefficients)]

ir["constant_names"] = [object_names.get(id(obj), f"c{j}")
for j, obj in enumerate(ufl.algorithms.analysis.extract_constants(expression))]

ir["original_coefficient_positions"] = original_coefficient_positions

coefficient_elements = tuple(f.ufl_element() for f in coefficients)
Expand Down
3 changes: 3 additions & 0 deletions ffcx/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ def main(args=None):
if len(ufd.forms) > 0:
code_h, code_c = compiler.compile_ufl_objects(
ufd.forms, ufd.object_names, prefix=prefix, parameters=parameters, visualise=xargs.visualise)
elif len(ufd.expressions) > 0:
code_h, code_c = compiler.compile_ufl_objects(
ufd.expressions, ufd.object_names, prefix=prefix, parameters=parameters, visualise=xargs.visualise)
else:
code_h, code_c = compiler.compile_ufl_objects(
ufd.elements, ufd.object_names, prefix=prefix, parameters=parameters, visualise=xargs.visualise)
Expand Down
16 changes: 12 additions & 4 deletions test/test_jit_expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,20 @@ def test_matvec(compile_args):
# Coefficient storage XYXYXY
w = np.array(f_mat.T.flatten(), dtype=np_type)
c = np.array([0.5], dtype=np_type)
entity_index = np.array([0], dtype=np.intc)
quad_perm = np.array([0], dtype=np.dtype("uint8"))

# Coords storage XYZXYZXYZ
coords = np.array([[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0]], dtype=np.float64)
expression.tabulate_expression(
expression.tabulate_tensor_float64(
ffi.cast('{type} *'.format(type=c_type), A.ctypes.data),
ffi.cast('{type} *'.format(type=c_type), w.ctypes.data),
ffi.cast('{type} *'.format(type=c_type), c.ctypes.data),
ffi.cast('double *', coords.ctypes.data))
ffi.cast('double *', coords.ctypes.data),
ffi.cast('int *', entity_index.ctypes.data),
ffi.cast('uint8_t *', quad_perm.ctypes.data))

# Check the computation against correct NumPy value
assert np.allclose(A, 0.5 * np.dot(a_mat, f_mat).T)
Expand Down Expand Up @@ -118,15 +122,19 @@ def test_rank1(compile_args):
# Coefficient storage XYXYXY
w = np.array([0.0], dtype=np_type)
c = np.array([0.0], dtype=np_type)
entity_index = np.array([0], dtype=np.intc)
quad_perm = np.array([0], dtype=np.dtype("uint8"))

# Coords storage XYZXYZXYZ
coords = np.zeros((points.shape[0], 3), dtype=np.float64)
coords[:, :2] = points
expression.tabulate_expression(
expression.tabulate_tensor_float64(
ffi.cast('{type} *'.format(type=c_type), A.ctypes.data),
ffi.cast('{type} *'.format(type=c_type), w.ctypes.data),
ffi.cast('{type} *'.format(type=c_type), c.ctypes.data),
ffi.cast('double *', coords.ctypes.data))
ffi.cast('double *', coords.ctypes.data),
ffi.cast('int *', entity_index.ctypes.data),
ffi.cast('uint8_t *', quad_perm.ctypes.data))

f = np.array([[1.0, 2.0, 3.0], [-4.0, -5.0, 6.0]])

Expand Down