Example #1
0
def test_triangle_variant_spectral_fail_l2():
    ufl_element = ufl.FiniteElement('DP L2',
                                    ufl.triangle,
                                    2,
                                    variant='spectral')
    with pytest.raises(ValueError):
        create_element(ufl_element)
def test_cache_hit_vector(ufl_vector_element):
    A = f.create_element(ufl_vector_element)
    B = f.create_element(ufl_vector_element)

    assert A is B

    assert all(a == A.elements()[0] for a in A.elements())
def test_cache_miss_vector(ufl_vector_element):
    A = f.create_element(ufl_vector_element)
    B = f.create_element(ufl_vector_element, vector_is_mixed=False)

    assert A is not B

    assert A.elements()[0] is not B
def test_tensor_prod_simple(ufl_A, ufl_B):
    tensor_ufl = ufl.TensorProductElement(ufl_A, ufl_B)

    tensor = f.create_element(tensor_ufl)
    A = f.create_element(ufl_A)
    B = f.create_element(ufl_B)

    assert isinstance(tensor, f.supported_elements[tensor_ufl.family()])

    assert tensor.A is A
    assert tensor.B is B
Example #5
0
def test_tensor_prod_simple(ufl_A, ufl_B):
    tensor_ufl = ufl.TensorProductElement(ufl_A, ufl_B)

    tensor = create_element(tensor_ufl)
    A = create_element(ufl_A)
    B = create_element(ufl_B)

    assert isinstance(tensor, supported_elements[tensor_ufl.family()])

    assert tensor.A is A
    assert tensor.B is B
Example #6
0
def test_tensor_prod_simple(ufl_A, ufl_B):
    tensor_ufl = ufl.TensorProductElement(ufl_A, ufl_B)

    tensor = create_element(tensor_ufl)
    A = create_element(ufl_A)
    B = create_element(ufl_B)

    assert isinstance(tensor, FIAT.TensorProductElement)

    assert tensor.A is A
    assert tensor.B is B
Example #7
0
def test_tensor_prod_simple(ufl_A, ufl_B):
    tensor_ufl = ufl.TensorProductElement(ufl_A, ufl_B)

    tensor = create_element(tensor_ufl)
    A = create_element(ufl_A)
    B = create_element(ufl_B)

    assert isinstance(tensor, FIAT.TensorProductElement)

    assert tensor.A is A
    assert tensor.B is B
def test_triangle_vector(mixed, ufl_element, ufl_vector_element):
    scalar = f.create_element(ufl_element)
    vector = f.create_element(ufl_vector_element, vector_is_mixed=mixed)

    if not mixed:
        assert isinstance(scalar, f.supported_elements[ufl_element.family()])
        assert isinstance(vector, f.supported_elements[ufl_element.family()])

    else:
        assert isinstance(vector, f.MixedElement)
        assert isinstance(vector.elements()[0], f.supported_elements[ufl_element.family()])
        assert len(vector.elements()) == ufl_vector_element.num_sub_elements()
Example #9
0
def test_quadrilateral_variant_spectral_rtcf():
    element = create_element(
        ufl.FiniteElement('RTCF', ufl.quadrilateral, 2, variant='spectral'))
    assert isinstance(element.element.A.A, FIAT.GaussLobattoLegendre)
    assert isinstance(element.element.A.B, FIAT.GaussLegendre)
    assert isinstance(element.element.B.A, FIAT.GaussLegendre)
    assert isinstance(element.element.B.B, FIAT.GaussLobattoLegendre)
Example #10
0
def compile_python_kernel(expression, to_pts, to_element, fs, coords):
    """Produce a :class:`PyOP2.Kernel` wrapping the eval method on the
    function provided."""

    coords_space = coords.function_space()
    coords_element = create_element(coords_space.ufl_element(), vector_is_mixed=False)

    X_remap = list(coords_element.tabulate(0, to_pts).values())[0]

    # The par_loop will just pass us arguments, since it doesn't
    # know about keyword args at all so unpack into a dict that we
    # can pass to the user's eval method.
    def kernel(output, x, *args):
        kwargs = {}
        for (slot, _), arg in zip(expression._user_args, args):
            kwargs[slot] = arg
        X = numpy.dot(X_remap.T, x)

        for i in range(len(output)):
            # Pass a slice for the scalar case but just the
            # current vector in the VFS case. This ensures the
            # eval method has a Dolfin compatible API.
            expression.eval(output[i:i+1, ...] if numpy.ndim(output) == 1 else output[i, ...],
                            X[i:i+1, ...] if numpy.ndim(X) == 1 else X[i, ...], **kwargs)

    coefficients = [coords]
    for _, arg in expression._user_args:
        coefficients.append(GlobalWrapper(arg))
    return kernel, False, False, tuple(coefficients)
Example #11
0
def _calculate_values(function, points, dimension, cell_mask=None):
    """Calculate function values at given reference points

    :arg function: function to be sampled
    :arg points: points to be sampled in reference space
    :arg cell_mask: Masks for cell node list
    """
    import numpy.ma as ma
    function_space = function.function_space()
    keys = {1: (0,),
            2: (0, 0)}
    fiat_element = create_element(function_space.ufl_element(), vector_is_mixed=False)
    elem = fiat_element.tabulate(0, points)[keys[dimension]]
    cell_node_list = function_space.cell_node_list
    if cell_mask is not None:
        cell_mask = np.tile(cell_mask.reshape(-1, 1), cell_node_list.shape[1])
        cell_node_list = ma.compress_rows(ma.masked_array(cell_node_list,
                                                          mask=cell_mask))
    data = function.dat.data_ro[cell_node_list]
    if function.ufl_shape == ():
        vec_length = 1
    else:
        vec_length = function.ufl_shape[0]
    if vec_length == 1:
        data = np.reshape(data, data.shape+(1, ))
    return np.einsum("ijk,jl->ilk", data, elem)
Example #12
0
def _calculate_values(function, points, dimension, cell_mask=None):
    """Calculate function values at given reference points

    :arg function: function to be sampled
    :arg points: points to be sampled in reference space
    :arg cell_mask: Masks for cell node list
    """
    import numpy.ma as ma
    function_space = function.function_space()
    keys = {1: (0,),
            2: (0, 0)}
    fiat_element = create_element(function_space.ufl_element(), vector_is_mixed=False)
    elem = fiat_element.tabulate(0, points)[keys[dimension]]
    cell_node_list = function_space.cell_node_list
    if cell_mask is not None:
        cell_mask = np.tile(cell_mask.reshape(-1, 1), cell_node_list.shape[1])
        cell_node_list = ma.compress_rows(ma.masked_array(cell_node_list,
                                                          mask=cell_mask))
    data = function.dat.data_ro[cell_node_list]
    if function.ufl_shape == ():
        vec_length = 1
    else:
        vec_length = function.ufl_shape[0]
    if vec_length == 1:
        data = np.reshape(data, data.shape+(1, ))
    return np.einsum("ijk,jl->ilk", data, elem)
Example #13
0
def get_transfer_kernel(coarse, fine, typ=None):
    ch, level = get_level(coarse.mesh())
    mesh = ch[0]
    assert hasattr(mesh, "_shared_data_cache")
    key = entity_dofs_key(coarse.finat_element.entity_dofs()) + (typ, coarse.dim)
    cache = mesh._shared_data_cache["hierarchy_transfer_kernel"]
    try:
        return cache[key]
    except KeyError:
        if not (coarse.ufl_element() == fine.ufl_element()):
            raise ValueError("Can't transfer between different spaces")
        ch, level = get_level(coarse.mesh())
        fh, fine_level = get_level(fine.mesh())
        refinements_per_level = ch.refinements_per_level
        assert ch is fh
        assert refinements_per_level*level + 1 == refinements_per_level*fine_level
        dim = coarse.dim
        element = create_element(coarse.ufl_element(), vector_is_mixed=False)
        omap = fine.cell_node_map().values
        c2f, vperm = ch._cells_vperm[int(level*refinements_per_level)]
        indices, _ = get_unique_indices(element,
                                        omap[c2f[0, :], ...].reshape(-1),
                                        vperm[0, :],
                                        offset=None)
        if typ == "prolong":
            kernel = get_prolongation_kernel(element, indices, dim)
        elif typ == "inject":
            kernel = get_injection_kernel(element, indices, dim)
        elif typ == "restrict":
            discontinuous = element.entity_dofs() == element.entity_closure_dofs()
            kernel = get_restriction_kernel(element, indices, dim, no_weights=discontinuous)
        else:
            raise ValueError("Unknown transfer type '%s'" % typ)
        return cache.setdefault(key, kernel)
Example #14
0
def convert_finiteelement(element):
    cell = as_fiat_cell(element.cell())
    if element.family() not in supported_elements:
        return fiat_compat(element)
    lmbda = supported_elements.get(element.family())
    if lmbda is None:
        if element.cell().cellname() != "quadrilateral":
            raise ValueError("%s is supported, but handled incorrectly" %
                             element.family())
        # Handle quadrilateral short names like RTCF and RTCE.
        element = element.reconstruct(cell=quad_tpc)
        return finat.QuadrilateralElement(create_element(element))

    kind = element.variant()
    if kind is None:
        kind = 'equispaced'  # default variant

    if element.family() == "Lagrange":
        if kind == 'equispaced':
            lmbda = finat.Lagrange
        elif kind == 'spectral' and element.cell().cellname() == 'interval':
            lmbda = finat.GaussLobattoLegendre
        else:
            raise ValueError("Variant %r not supported on %s" %
                             (kind, element.cell()))
    elif element.family() == "Discontinuous Lagrange":
        kind = element.variant() or 'equispaced'
        if kind == 'equispaced':
            lmbda = finat.DiscontinuousLagrange
        elif kind == 'spectral' and element.cell().cellname() == 'interval':
            lmbda = finat.GaussLegendre
        else:
            raise ValueError("Variant %r not supported on %s" %
                             (kind, element.cell()))
    return lmbda(cell, element.degree())
def fix_coarse_boundaries(cV, fV):
    # With lots of help from Lawrence Mitchell
    c2fmap = coarse_to_fine_node_map(cV, fV)

    cmesh = cV.mesh()
    ch, level = get_level(cmesh)
    refinements_per_level = ch.refinements_per_level
    element = create_element(cV.ufl_element(), vector_is_mixed=False)
    omap = fV.cell_node_map().values
    c2f, vperm = ch._cells_vperm[int(level * refinements_per_level)]
    indices, _ = get_unique_indices(element,
                                    omap[c2f[0, :], ...].reshape(-1),
                                    vperm[0, :],
                                    offset=None)
    weights = restriction_weights(element)[indices]
    sums = (weights > 0).sum(axis=1)
    indices_to_fix = where(sums <= cmesh.topological_dimension())[0]

    nodelist = unique(c2fmap.values[:, indices_to_fix])

    class FixedDirichletBC(DirichletBC):
        def __init__(self, V, g, nodelist):
            self.nodelist = nodelist
            DirichletBC.__init__(self, V, g, "on_boundary")

        @utils.cached_property
        def nodes(self):
            return self.nodelist

    bc = FixedDirichletBC(fV, (0, 0), nodelist)

    return bc
def compile_python_kernel(expression, to_pts, to_element, fs, coords):
    """Produce a :class:`PyOP2.Kernel` wrapping the eval method on the
    function provided."""

    coords_space = coords.function_space()
    coords_element = create_element(coords_space.ufl_element(), vector_is_mixed=False)

    X_remap = list(coords_element.tabulate(0, to_pts).values())[0]

    # The par_loop will just pass us arguments, since it doesn't
    # know about keyword args at all so unpack into a dict that we
    # can pass to the user's eval method.
    def kernel(output, x, *args):
        kwargs = {}
        for (slot, _), arg in zip(expression._user_args, args):
            kwargs[slot] = arg
        X = numpy.dot(X_remap.T, x)

        for i in range(len(output)):
            # Pass a slice for the scalar case but just the
            # current vector in the VFS case. This ensures the
            # eval method has a Dolfin compatible API.
            expression.eval(output[i:i+1, ...] if numpy.ndim(output) == 1 else output[i, ...],
                            X[i:i+1, ...] if numpy.ndim(X) == 1 else X[i, ...], **kwargs)

    coefficients = [coords]
    for _, arg in expression._user_args:
        coefficients.append(GlobalWrapper(arg))
    return kernel, False, False, tuple(coefficients)
Example #17
0
def _interpolator(V, dat, expr, subset):
    to_element = create_element(V.ufl_element(), vector_is_mixed=False)
    to_pts = []

    if V.ufl_element().mapping() != "identity":
        raise NotImplementedError("Can only interpolate onto elements "
                                  "with affine mapping. Try projecting instead")

    for dual in to_element.dual_basis():
        if not isinstance(dual, FIAT.functional.PointEvaluation):
            raise NotImplementedError("Can only interpolate onto point "
                                      "evaluation operators. Try projecting instead")
        to_pts.append(list(iterkeys(dual.pt_dict))[0])

    if len(expr.ufl_shape) != len(V.ufl_element().value_shape()):
        raise RuntimeError('Rank mismatch: Expression rank %d, FunctionSpace rank %d'
                           % (len(expr.ufl_shape), len(V.ufl_element().value_shape())))

    if expr.ufl_shape != V.ufl_element().value_shape():
        raise RuntimeError('Shape mismatch: Expression shape %r, FunctionSpace shape %r'
                           % (expr.ufl_shape, V.ufl_element().value_shape()))

    mesh = V.ufl_domain()
    coords = mesh.coordinates

    if not isinstance(expr, (firedrake.Expression, SubExpression)):
        if expr.ufl_domain() and expr.ufl_domain() != V.mesh():
            raise NotImplementedError("Interpolation onto another mesh not supported.")
        if expr.ufl_shape != V.shape:
            raise ValueError("UFL expression has incorrect shape for interpolation.")
        ast, oriented, coefficients = compile_ufl_kernel(expr, to_pts, coords)
        kernel = op2.Kernel(ast, ast.name)
        indexed = True
    elif hasattr(expr, "eval"):
        kernel, oriented, coefficients = compile_python_kernel(expr, to_pts, to_element, V, coords)
        indexed = False
    elif expr.code is not None:
        kernel, oriented, coefficients = compile_c_kernel(expr, to_pts, to_element, V, coords)
        indexed = True
    else:
        raise RuntimeError("Attempting to evaluate an Expression which has no value.")

    cell_set = coords.cell_set
    if subset is not None:
        assert subset.superset == cell_set
        cell_set = subset
    args = [kernel, cell_set]

    if indexed:
        args.append(dat(op2.WRITE, V.cell_node_map()[op2.i[0]]))
    else:
        args.append(dat(op2.WRITE, V.cell_node_map()))
    if oriented:
        co = mesh.cell_orientations()
        args.append(co.dat(op2.READ, co.cell_node_map()))
    for coefficient in coefficients:
        args.append(coefficient.dat(op2.READ, coefficient.cell_node_map()))

    return partial(op2.par_loop, *args)
Example #18
0
 def prolongation_transfer_kernel_action(Vf, expr):
     from tsfc import compile_expression_dual_evaluation
     from tsfc.fiatinterface import create_element
     coords = Vf.ufl_domain().coordinates
     to_element = create_element(Vf.ufl_element(), vector_is_mixed=False)
     ast, oriented, needs_cell_sizes, coefficients, _ = compile_expression_dual_evaluation(
         expr, to_element, coords, coffee=False)
     return op2.Kernel(ast, ast.name)
def firedrake_local_to_cart(element):
    r"""Gets the list of nodes for an element (provided they exist.)
    :arg element: a ufl element.
    :returns: a list of arrays of floats where each array is a node.
    """
    fiat_element = create_element(element, vector_is_mixed=False)
    # TODO: Surely there is an easier way that I've forgotten?
    return [np.array(list(phi.get_point_dict().keys())[0])
            for phi in fiat_element.dual_basis()]
Example #20
0
def prolongation_transfer_kernel_aij(Pk, P1):
    # Works for Pk, Pm; I just retain the notation
    # P1 to remind you that P1 is of lower degree
    # than Pk
    from tsfc import compile_expression_dual_evaluation
    from tsfc.fiatinterface import create_element
    from firedrake import TestFunction

    expr = TestFunction(P1)
    coords = Pk.ufl_domain().coordinates
    to_element = create_element(Pk.ufl_element(), vector_is_mixed=False)

    ast, oriented, needs_cell_sizes, coefficients, _ = compile_expression_dual_evaluation(expr, to_element, coords, coffee=False)
    kernel = op2.Kernel(ast, ast.name)
    return kernel
Example #21
0
def _bezier_calculate_points(function):
    """Calculate points values for a function used for bezier plotting

    :arg function: 1D Function with 1 < deg < 4
    """
    deg = function.function_space().ufl_element().degree()
    M = np.empty([deg + 1, deg + 1], dtype=float)
    fiat_element = create_element(function.function_space().ufl_element(), vector_is_mixed=False)
    basis = fiat_element.dual_basis()
    for i in range(deg + 1):
        for j in range(deg + 1):
            M[i, j] = _bernstein(list(basis[j].get_point_dict().keys())[0][0],
                                 i, deg)
    M_inv = np.linalg.inv(M)
    cell_node_list = function.function_space().cell_node_list
    return np.dot(function.dat.data_ro[cell_node_list], M_inv)
Example #22
0
def _bezier_calculate_points(function):
    """Calculate points values for a function used for bezier plotting

    :arg function: 1D Function with 1 < deg < 4
    """
    deg = function.function_space().ufl_element().degree()
    M = np.empty([deg + 1, deg + 1], dtype=float)
    fiat_element = create_element(function.function_space().ufl_element(), vector_is_mixed=False)
    basis = fiat_element.dual_basis()
    for i in range(deg + 1):
        for j in range(deg + 1):
            M[i, j] = _bernstein(basis[j].get_point_dict().keys()[0][0],
                                 i, deg)
    M_inv = np.linalg.inv(M)
    cell_node_list = function.function_space().cell_node_list
    return np.dot(function.dat.data_ro[cell_node_list], M_inv)
Example #23
0
    def __init__(self, mesh, element, name=None):
        super(FunctionSpace, self).__init__()
        if type(element) is ufl.MixedElement:
            raise ValueError("Can't create FunctionSpace for MixedElement")
        fiat_element = create_element(element, vector_is_mixed=False)
        sdata = get_shared_data(mesh, fiat_element)
        # The function space shape is the number of dofs per node,
        # hence it is not always the value_shape.  Vector and Tensor
        # element modifiers *must* live on the outside!
        if type(element) is ufl.TensorElement:
            # UFL enforces value_shape of the subelement to be empty
            # on a TensorElement.
            self.shape = element.value_shape()
        elif type(element) is ufl.VectorElement:
            # First dimension of the value_shape is the VectorElement
            # shape.
            self.shape = element.value_shape()[:1]
        else:
            self.shape = ()
        self._ufl_element = element
        self._shared_data = sdata
        self._mesh = mesh

        self.rank = len(self.shape)
        """The rank of this :class:`FunctionSpace`.  Spaces where the
        element is scalar-valued (or intrinsically vector-valued) have
        rank zero.  Spaces built on :class:`~ufl.classes.VectorElement` or
        :class:`~ufl.classes.TensorElement` instances have rank equivalent to
        the number of components of their
        :meth:`~ufl.classes.FiniteElementBase.value_shape`."""
        self.dim = numpy.prod(self.shape, dtype=int)
        """The total number of degrees of freedom at each function
        space node."""
        self.name = name
        """The (optional) descriptive name for this space."""
        self.node_set = sdata.node_set
        """A :class:`pyop2.Set` representing the function space nodes."""
        self.dof_dset = op2.DataSet(self.node_set, self.shape or 1,
                                    name="%s_nodes_dset" % self.name)
        """A :class:`pyop2.DataSet` representing the function space
        degrees of freedom."""

        self.comm = self.node_set.comm
        self.fiat_element = fiat_element
        self.extruded = sdata.extruded
        self.offset = sdata.offset
        self.bt_masks = sdata.bt_masks
    def __init__(self, mesh, element, name=None):
        super(FunctionSpace, self).__init__()
        if type(element) is ufl.MixedElement:
            raise ValueError("Can't create FunctionSpace for MixedElement")
        fiat_element = create_element(element, vector_is_mixed=False)
        sdata = get_shared_data(mesh, fiat_element)
        # The function space shape is the number of dofs per node,
        # hence it is not always the value_shape.  Vector and Tensor
        # element modifiers *must* live on the outside!
        if type(element) is ufl.TensorElement:
            # UFL enforces value_shape of the subelement to be empty
            # on a TensorElement.
            self.shape = element.value_shape()
        elif type(element) is ufl.VectorElement:
            # First dimension of the value_shape is the VectorElement
            # shape.
            self.shape = element.value_shape()[:1]
        else:
            self.shape = ()
        self._ufl_element = element
        self._shared_data = sdata
        self._mesh = mesh

        self.rank = len(self.shape)
        """The rank of this :class:`FunctionSpace`.  Spaces where the
        element is scalar-valued (or intrinsically vector-valued) have
        rank zero.  Spaces built on :class:`~ufl.classes.VectorElement` or
        :class:`~ufl.classes.TensorElement` instances have rank equivalent to
        the number of components of their
        :meth:`~ufl.classes.FiniteElementBase.value_shape`."""
        self.dim = numpy.prod(self.shape, dtype=int)
        """The total number of degrees of freedom at each function
        space node."""
        self.name = name
        """The (optional) descriptive name for this space."""
        self.node_set = sdata.node_set
        """A :class:`pyop2.Set` representing the function space nodes."""
        self.dof_dset = op2.DataSet(self.node_set, self.shape or 1,
                                    name="%s_nodes_dset" % self.name)
        """A :class:`pyop2.DataSet` representing the function space
        degrees of freedom."""

        self.comm = self.node_set.comm
        self.fiat_element = fiat_element
        self.extruded = sdata.extruded
        self.offset = sdata.offset
        self.bt_masks = sdata.bt_masks
Example #25
0
def prepare_arguments(arguments, indices, interior_facet=False):
    """Bridges the kernel interface and the GEM abstraction for
    Arguments.  Vector Arguments are rearranged here for interior
    facet integrals.

    :arg arguments: UFL Arguments
    :arg indices: Argument indices
    :arg interior_facet: interior facet integral?
    :returns: (funarg, prepare, expressions)
         funarg      - :class:`coffee.Decl` function argument
         prepare     - list of COFFEE nodes to be prepended to the
                       kernel body
         expressions - GEM expressions referring to the argument
                       tensor
    """
    funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol("A"), pointers=[()])
    varexp = gem.Variable("A", (None,))

    elements = tuple(create_element(arg.ufl_element()) for arg in arguments)
    space_dimensions = tuple(element.space_dimension() for element in elements)
    for i, sd in zip(indices, space_dimensions):
        i.set_extent(sd)

    if arguments and interior_facet:
        shape = tuple(2 * element.space_dimension() for element in elements)
        strides = cumulative_strides(shape)

        expressions = []
        for restrictions in product((0, 1), repeat=len(arguments)):
            offset = sum(r * sd * stride
                         for r, sd, stride in zip(restrictions, space_dimensions, strides))
            expressions.append(gem.FlexiblyIndexed(
                varexp,
                ((offset,
                  tuple((i, s) for i, s in zip(indices, shape))),)
            ))
    else:
        shape = space_dimensions
        expressions = [gem.FlexiblyIndexed(varexp, ((0, tuple(zip(indices, space_dimensions))),))]

    zero = coffee.FlatBlock(
        str.format("memset({name}, 0, {size} * sizeof(*{name}));\n",
                   name=funarg.sym.gencode(), size=numpy.product(shape, dtype=int))
    )
    return funarg, [zero], expressions
Example #26
0
def prepare_coordinates(coefficient, name, interior_facet=False):
    """Bridges the kernel interface and the GEM abstraction for
    coordinates.

    :arg coefficient: UFL Coefficient
    :arg name: unique name to refer to the Coefficient in the kernel
    :arg interior_facet: interior facet integral?
    :returns: (funarg, expression)
         funarg     - :class:`coffee.Decl` function argument
         expression - GEM expression referring to the Coefficient
                      values
    """
    if not interior_facet:
        funargs = [coffee.Decl(SCALAR_TYPE, coffee.Symbol(name),
                               pointers=[("",)],
                               qualifiers=["const"])]
    else:
        funargs = [coffee.Decl(SCALAR_TYPE, coffee.Symbol(name+"_0"),
                               pointers=[("",)],
                               qualifiers=["const"]),
                   coffee.Decl(SCALAR_TYPE, coffee.Symbol(name+"_1"),
                               pointers=[("",)],
                               qualifiers=["const"])]

    fiat_element = create_element(coefficient.ufl_element())
    shape = (fiat_element.space_dimension(),)
    gdim = coefficient.ufl_element().cell().geometric_dimension()
    assert len(shape) == 1 and shape[0] % gdim == 0
    num_nodes = shape[0] // gdim

    # Translate coords from XYZXYZXYZXYZ into XXXXYYYYZZZZ
    # NOTE: See dolfin/mesh/Cell.h:get_coordinate_dofs for ordering scheme
    indices = list(map(int, numpy.arange(num_nodes * gdim).reshape(num_nodes, gdim).transpose().flat))
    if not interior_facet:
        variable = gem.Variable(name, shape)
        expression = gem.ListTensor([gem.Indexed(variable, (i,)) for i in indices])
    else:
        variable0 = gem.Variable(name+"_0", shape)
        variable1 = gem.Variable(name+"_1", shape)
        expression = gem.ListTensor([[gem.Indexed(variable0, (i,)) for i in indices],
                                     [gem.Indexed(variable1, (i,)) for i in indices]])

    return funargs, expression
Example #27
0
def prepare_coefficients(coefficients, coefficient_numbers, name, interior_facet=False):
    """Bridges the kernel interface and the GEM abstraction for
    Coefficients.  Mixed element Coefficients are rearranged here for
    interior facet integrals.

    :arg coefficient: iterable of UFL Coefficients
    :arg coefficient_numbers: iterable of coefficient indices in the original form
    :arg name: unique name to refer to the Coefficient in the kernel
    :arg interior_facet: interior facet integral?
    :returns: (funarg, expressions)
         funarg     - :class:`coffee.Decl` function argument
         expressions- GEM expressions referring to the Coefficient
                      values
    """
    assert len(coefficients) == len(coefficient_numbers)

    funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol(name),
                         pointers=[("const",), ()],
                         qualifiers=["const"])

    varexp = gem.Variable(name, (None, None))
    expressions = []
    for coefficient_number, coefficient in zip(coefficient_numbers, coefficients):
        if coefficient.ufl_element().family() == 'Real':
            shape = coefficient.ufl_shape
        else:
            fiat_element = create_element(coefficient.ufl_element())
            shape = (fiat_element.space_dimension(),)
            if interior_facet:
                shape = (2,) + shape

        alpha = tuple(gem.Index() for s in shape)
        expressions.append(gem.ComponentTensor(
            gem.FlexiblyIndexed(
                varexp, ((coefficient_number, ()),
                         (0, tuple(zip(alpha, shape))))
            ),
            alpha
        ))

    return funarg, expressions
Example #28
0
def tabulate(ufl_element, order, points, entity, epsilon):
    """Ask FIAT to tabulate ``points`` up to order ``order``, then
    rearranges the result into a series of ``(c, D, table)`` tuples,
    where:

    c: component index (for vector-valued and tensor-valued elements)
    D: derivative tuple (e.g. (1, 2) means d/dx d^2/dy^2)
    table: tabulation matrix for the given component and derivative.
           shape: len(points) x space_dimension

    :arg ufl_element: element to tabulate
    :arg order: FIAT gives all derivatives up to this order
    :arg points: points to tabulate the element on
    :arg entity: particular entity to tabulate on
    """
    element = create_element(ufl_element)
    phi = element.space_dimension()
    C = ufl_element.reference_value_size()
    q = len(points)
    for D, fiat_table in iteritems(element.tabulate(order, points, entity)):
        if isinstance(fiat_table, Exception):
            # In the case an exception is found in the fiat table, do not
            # perform any rounding
            gem_fail = gem.Failure((q, phi), fiat_table)
            for c in range(C):
                yield c, D, gem_fail
        else:
            reordered_table = fiat_table.reshape(phi, C, q).transpose(1, 2, 0)  # (C, q, phi)
            for c, table in enumerate(reordered_table):
                # Copied from FFC (ffc/quadrature/quadratureutils.py)
                table[abs(table) < epsilon] = 0
                table[abs(table - 1.0) < epsilon] = 1.0
                table[abs(table + 1.0) < epsilon] = -1.0
                table[abs(table - 0.5) < epsilon] = 0.5
                table[abs(table + 0.5) < epsilon] = -0.5

                if spanning_degree(ufl_element) <= sum(D) and ufl_element.family() != "HDiv Trace":
                    assert compat.allclose(table, table.mean(axis=0, keepdims=True), equal_nan=True)
                    table = table[0]

                yield c, D, gem.Literal(table)
Example #29
0
def compile_element(ufl_element, cdim):
    """Generates C code for point evaluations.

    :arg ufl_element: UFL element of the function space
    :arg cdim: ``cdim`` of the function space
    :returns: C code as string
    """
    from tsfc.constants import PRECISION
    from firedrake.pointquery_utils import set_float_formatting, format
    from tsfc.fiatinterface import create_element
    from FIAT.reference_element import TensorProductCell as two_product_cell
    import sympy as sp
    import numpy as np

    # Set code generation parameters
    set_float_formatting(PRECISION)

    def calculate_basisvalues(ufl_cell, fiat_element):
        f_component = format["component"]
        f_decl = format["declaration"]
        f_float_decl = format["float declaration"]
        f_tensor = format["tabulate tensor"]
        f_new_line = format["new line"]

        tdim = ufl_cell.topological_dimension()
        gdim = ufl_cell.geometric_dimension()

        code = []

        # Symbolic tabulation
        tabs = fiat_element.tabulate(0, np.array([[sp.Symbol("reference_coords.X[%d]" % i)
                                                   for i in xrange(tdim)]]))
        tabs = tabs[(0,) * tdim]
        tabs = tabs.reshape(tabs.shape[:-1])

        # Generate code for intermediate values
        s_code, (theta,) = ssa_arrays([tabs])
        for name, value in s_code:
            code += [f_decl(f_float_decl, name, c_print(value))]

        # Prepare Jacobian, Jacobian inverse and determinant
        s_detJ = sp.Symbol('detJ')
        s_J = np.array([[sp.Symbol("J[{i}*{tdim} + {j}]".format(i=i, j=j, tdim=tdim))
                         for j in range(tdim)]
                        for i in range(gdim)])
        s_Jinv = np.array([[sp.Symbol("K[{i}*{gdim} + {j}]".format(i=i, j=j, gdim=gdim))
                            for j in range(gdim)]
                           for i in range(tdim)])

        # Apply transformations
        phi = []
        for i, val in enumerate(theta):
            mapping = fiat_element.mapping()[i]
            if mapping == "affine":
                phi.append(val)
            elif mapping == "contravariant piola":
                phi.append(s_J.dot(val) / s_detJ)
            elif mapping == "covariant piola":
                phi.append(s_Jinv.transpose().dot(val))
            else:
                raise ValueError("Unknown mapping: %s" % mapping)
        phi = np.asarray(phi, dtype=object)

        # Dump tables of basis values
        code += ["", "\t// Values of basis functions"]
        code += [f_decl("double", f_component("phi", phi.shape),
                        f_new_line + f_tensor(phi))]

        shape = phi.shape
        if len(shape) <= 1:
            vdim = 1
        elif len(shape) == 2:
            vdim = shape[1]
        return "\n".join(code), vdim

    # Create FIAT element
    element = create_element(ufl_element, vector_is_mixed=False)
    cell = ufl_element.cell()

    calculate_basisvalues, vdim = calculate_basisvalues(cell, element)
    extruded = isinstance(element.get_reference_element(), two_product_cell)

    code = {
        "cdim": cdim,
        "vdim": vdim,
        "geometric_dimension": cell.geometric_dimension(),
        "ndofs": element.space_dimension(),
        "calculate_basisvalues": calculate_basisvalues,
        "extruded_arg": ", int nlayers" if extruded else "",
        "nlayers": ", f->n_layers" if extruded else "",
    }

    evaluate_template_c = """static inline void evaluate_kernel(double *result, double *phi_, double **F)
{
    const int ndofs = %(ndofs)d;
    const int cdim = %(cdim)d;
    const int vdim = %(vdim)d;

    double (*phi)[vdim] = (double (*)[vdim]) phi_;

    // F: ndofs x cdim
    // phi: ndofs x vdim
    // result = F' * phi: cdim x vdim
    //
    // Usually cdim == 1 or vdim == 1.

    for (int q = 0; q < cdim * vdim; q++) {
        result[q] = 0.0;
    }
    for (int i = 0; i < ndofs; i++) {
        for (int c = 0; c < cdim; c++) {
            for (int v = 0; v < vdim; v++) {
                result[c*vdim + v] += F[i][c] * phi[i][v];
            }
        }
    }
}

static inline void wrap_evaluate(double *result, double *phi, double *data, int *map%(extruded_arg)s, int cell);

int evaluate(struct Function *f, double *x, double *result)
{
    struct ReferenceCoords reference_coords;
    int cell = locate_cell(f, x, %(geometric_dimension)d, &to_reference_coords, &reference_coords);
    if (cell == -1) {
        return -1;
    }

    if (!result) {
        return 0;
    }

    double *J = reference_coords.J;
    double *K = reference_coords.K;
    double detJ = reference_coords.detJ;

%(calculate_basisvalues)s

    wrap_evaluate(result, (double *)phi, f->f, f->f_map%(nlayers)s, cell);
    return 0;
}
"""

    return evaluate_template_c % code
Example #30
0
def convert_vectorelement(element):
    scalar_element = create_element(element.sub_elements()[0])
    return finat.TensorFiniteElement(scalar_element,
                                     (element.num_sub_elements(), ))
Example #31
0
def compile_c_kernel(expression, to_pts, to_element, fs, coords):
    """Produce a :class:`PyOP2.Kernel` from the c expression provided."""

    coords_space = coords.function_space()
    coords_element = create_element(coords_space.ufl_element(),
                                    vector_is_mixed=False)

    names = {v[0] for v in expression._user_args}

    X = list(coords_element.tabulate(0, to_pts).values())[0]

    # Produce C array notation of X.
    X_str = "{{" + "},\n{".join([",".join(map(str, x)) for x in X.T]) + "}}"

    A = utils.unique_name("A", names)
    X = utils.unique_name("X", names)
    x_ = utils.unique_name("x_", names)
    k = utils.unique_name("k", names)
    d = utils.unique_name("d", names)
    i_ = utils.unique_name("i", names)
    # x is a reserved name.
    x = "x"
    if "x" in names:
        raise ValueError(
            "cannot use 'x' as a user-defined Expression variable")
    ass_exp = [
        ast.Assign(ast.Symbol(A, (k, ), ((len(expression.code), i), )),
                   ast.FlatBlock("%s" % code))
        for i, code in enumerate(expression.code)
    ]

    dim = coords_space.value_size
    ndof = to_element.space_dimension()
    xndof = coords_element.space_dimension()
    nfdof = to_element.space_dimension() * numpy.prod(fs.value_size, dtype=int)

    init_X = ast.Decl(typ="double",
                      sym=ast.Symbol(X, rank=(ndof, xndof)),
                      qualifiers=["const"],
                      init=X_str)
    init_x = ast.Decl(typ="double",
                      sym=ast.Symbol(x, rank=(coords_space.value_size, )))
    init_pi = ast.Decl(typ="double",
                       sym="pi",
                       qualifiers=["const"],
                       init="3.141592653589793")
    init = ast.Block([init_X, init_x, init_pi])
    incr_x = ast.Incr(
        ast.Symbol(x, rank=(d, )),
        ast.Prod(ast.Symbol(X, rank=(k, i_)),
                 ast.Symbol(x_, rank=(ast.Sum(ast.Prod(i_, dim), d), ))))
    assign_x = ast.Assign(ast.Symbol(x, rank=(d, )), 0)
    loop_x = ast.For(init=ast.Decl("unsigned int", i_, 0),
                     cond=ast.Less(i_, xndof),
                     incr=ast.Incr(i_, 1),
                     body=[incr_x])

    block = ast.For(init=ast.Decl("unsigned int", d, 0),
                    cond=ast.Less(d, dim),
                    incr=ast.Incr(d, 1),
                    body=[assign_x, loop_x])
    loop = ast.c_for(k, ndof, ast.Block([block] + ass_exp, open_scope=True))
    user_args = []
    user_init = []
    for _, arg in expression._user_args:
        if arg.shape == (1, ):
            user_args.append(ast.Decl("double *", "%s_" % arg.name))
            user_init.append(
                ast.FlatBlock("const double %s = *%s_;" %
                              (arg.name, arg.name)))
        else:
            user_args.append(ast.Decl("double *", arg.name))
    kernel_code = ast.FunDecl(
        "void", "expression_kernel", [
            ast.Decl("double", ast.Symbol(A, (nfdof, ))),
            ast.Decl("double*", x_)
        ] + user_args, ast.Block(user_init + [init, loop], open_scope=False))
    coefficients = [coords]
    for _, arg in expression._user_args:
        coefficients.append(GlobalWrapper(arg))
    return op2.Kernel(kernel_code,
                      kernel_code.name), False, tuple(coefficients)
Example #32
0
def _interpolator(V, tensor, expr, subset, arguments, access):
    try:
        to_element = create_element(V.ufl_element(), vector_is_mixed=False)
    except KeyError:
        # FInAT only elements
        raise NotImplementedError("Don't know how to create FIAT element for %s" % V.ufl_element())

    if access is op2.READ:
        raise ValueError("Can't have READ access for output function")

    if len(expr.ufl_shape) != len(V.ufl_element().value_shape()):
        raise RuntimeError('Rank mismatch: Expression rank %d, FunctionSpace rank %d'
                           % (len(expr.ufl_shape), len(V.ufl_element().value_shape())))

    if expr.ufl_shape != V.ufl_element().value_shape():
        raise RuntimeError('Shape mismatch: Expression shape %r, FunctionSpace shape %r'
                           % (expr.ufl_shape, V.ufl_element().value_shape()))

    mesh = V.ufl_domain()
    coords = mesh.coordinates

    if not isinstance(expr, firedrake.Expression):
        if expr.ufl_domain() and expr.ufl_domain() != V.mesh():
            raise NotImplementedError("Interpolation onto another mesh not supported.")
        ast, oriented, needs_cell_sizes, coefficients, _ = compile_expression_dual_evaluation(expr, to_element, coords, coffee=False)
        kernel = op2.Kernel(ast, ast.name)
    elif hasattr(expr, "eval"):
        to_pts = []
        for dual in to_element.dual_basis():
            if not isinstance(dual, FIAT.functional.PointEvaluation):
                raise NotImplementedError("Can only interpolate Python kernels with Lagrange elements")
            pts, = dual.pt_dict.keys()
            to_pts.append(pts)

        kernel, oriented, needs_cell_sizes, coefficients = compile_python_kernel(expr, to_pts, to_element, V, coords)
    else:
        raise RuntimeError("Attempting to evaluate an Expression which has no value.")

    cell_set = coords.cell_set
    if subset is not None:
        assert subset.superset == cell_set
        cell_set = subset
    parloop_args = [kernel, cell_set]

    if tensor in set((c.dat for c in coefficients)):
        output = tensor
        tensor = op2.Dat(tensor.dataset)
        if access is not op2.WRITE:
            copyin = (partial(output.copy, tensor), )
        else:
            copyin = ()
        copyout = (partial(tensor.copy, output), )
    else:
        copyin = ()
        copyout = ()
    if isinstance(tensor, op2.Global):
        parloop_args.append(tensor(access))
    elif isinstance(tensor, op2.Dat):
        parloop_args.append(tensor(access, V.cell_node_map()))
    else:
        assert access == op2.WRITE  # Other access descriptors not done for Matrices.
        parloop_args.append(tensor(op2.WRITE, (V.cell_node_map(),
                                               arguments[0].function_space().cell_node_map())))
    if oriented:
        co = mesh.cell_orientations()
        parloop_args.append(co.dat(op2.READ, co.cell_node_map()))
    if needs_cell_sizes:
        cs = mesh.cell_sizes
        parloop_args.append(cs.dat(op2.READ, cs.cell_node_map()))
    for coefficient in coefficients:
        m_ = coefficient.cell_node_map()
        parloop_args.append(coefficient.dat(op2.READ, m_))

    for o in coefficients:
        domain = o.ufl_domain()
        if domain is not None and domain.topology != mesh.topology:
            raise NotImplementedError("Interpolation onto another mesh not supported.")

    parloop = op2.ParLoop(*parloop_args).compute
    if isinstance(tensor, op2.Mat):
        return parloop, tensor.assemble
    else:
        return copyin + (parloop, ) + copyout
Example #33
0
def fiat_compat(element):
    from tsfc.fiatinterface import create_element
    from finat.fiat_elements import FiatElement

    assert element.cell().is_simplex()
    return FiatElement(create_element(element))
Example #34
0
def compile_coordinate_element(ufl_coordinate_element):
    """Generates C code for changing to reference coordinates.

    :arg ufl_coordinate_element: UFL element of the coordinates
    :returns: C code as string
    """
    from tsfc.constants import PRECISION
    from tsfc.fiatinterface import create_element
    from firedrake.pointeval_utils import ssa_arrays, c_print
    from FIAT.reference_element import TensorProductCell as two_product_cell
    import sympy as sp
    import numpy as np

    # Set code generation parameters
    set_float_formatting(PRECISION)

    def dX_norm_square(topological_dimension):
        return " + ".join("dX[{0}]*dX[{0}]".format(i)
                          for i in xrange(topological_dimension))

    def X_isub_dX(topological_dimension):
        return "\n".join("\tX[{0}] -= dX[{0}];".format(i)
                         for i in xrange(topological_dimension))

    def is_affine(ufl_element):
        return ufl_element.cell().is_simplex() and ufl_element.degree() <= 1 and ufl_element.family() in ["Discontinuous Lagrange", "Lagrange"]

    def inside_check(ufl_cell, fiat_cell):
        dim = ufl_cell.topological_dimension()
        point = tuple(sp.Symbol("X[%d]" % i) for i in xrange(dim))

        return " && ".join("(%s)" % arg for arg in fiat_cell.contains_point(point, epsilon=1e-14).args)

    def init_X(fiat_element):
        f_float = format["floating point"]
        f_assign = format["assign"]

        fiat_cell = fiat_element.get_reference_element()
        vertices = np.array(fiat_cell.get_vertices())
        X = np.average(vertices, axis=0)
        return "\n".join(f_assign("X[%d]" % i, f_float(v)) for i, v in enumerate(X))

    def to_reference_coordinates(ufl_cell, fiat_element):
        f_decl = format["declaration"]
        f_float_decl = format["float declaration"]

        # Get the element cell name and geometric dimension.
        cell = ufl_cell
        gdim = cell.geometric_dimension()
        tdim = cell.topological_dimension()

        code = []

        # Symbolic tabulation
        tabs = fiat_element.tabulate(1, np.array([[sp.Symbol("X[%d]" % i) for i in xrange(tdim)]]))
        tabs = sorted((d, value.reshape(value.shape[:-1])) for d, value in tabs.iteritems())

        # Generate code for intermediate values
        s_code, d_phis = ssa_arrays(map(lambda (k, v): v, tabs), prefix="t")
        phi = d_phis.pop(0)

        for name, value in s_code:
            code += [f_decl(f_float_decl, name, c_print(value))]

        # Cell coordinate data
        C = np.array([[sp.Symbol("C[%d][%d]" % (i, j)) for j in range(gdim)]
                      for i in range(fiat_element.space_dimension())])

        # Generate physical coordinates
        x = phi.dot(C)
        for i, e in enumerate(x):
            code += ["\tx[%d] = %s;" % (i, e)]

        # Generate Jacobian
        grad_phi = np.vstack(reversed(d_phis))
        J = np.transpose(grad_phi.dot(C))
        for i, row in enumerate(J):
            for j, e in enumerate(row):
                code += ["\tJ[%d * %d + %d] = %s;" % (i, tdim, j, e)]

        # Get code snippets for Jacobian, inverse of Jacobian and mapping of
        # coordinates from physical element to the FIAT reference element.
        code += ["compute_jacobian_inverse_%s(K, detJ, J);" % cellname[cell]]
        # FIXME: use cell orientations!
        # if needs_orientation:
        #     code_ += [format["orientation"]["ufc"](tdim, gdim)]

        x = np.array([sp.Symbol("x[%d]" % i) for i in xrange(gdim)])
        x0 = np.array([sp.Symbol("x0[%d]" % i) for i in xrange(gdim)])
        K = np.array([[sp.Symbol("K[%d]" % (i*gdim + j)) for j in range(gdim)]
                      for i in range(tdim)])

        dX = K.dot(x - x0)
        for i, e in enumerate(dX):
            code += ["\tdX[%d] = %s;" % (i, e)]

        return "\n".join(code)

    # Create FIAT element
    element = create_element(ufl_coordinate_element, vector_is_mixed=False)
    cell = ufl_coordinate_element.cell()

    # calculate_basisvalues, vdim = calculate_basisvalues(cell, element)
    extruded = isinstance(element.get_reference_element(), two_product_cell)

    code = {
        "geometric_dimension": cell.geometric_dimension(),
        "topological_dimension": cell.topological_dimension(),
        "inside_predicate": inside_check(cell, element.get_reference_element()),
        "to_reference_coords": to_reference_coordinates(cell, element),
        "init_X": init_X(element),
        "max_iteration_count": 1 if is_affine(ufl_coordinate_element) else 16,
        "convergence_epsilon": 1e-12,
        "dX_norm_square": dX_norm_square(cell.topological_dimension()),
        "X_isub_dX": X_isub_dX(cell.topological_dimension()),
        "extruded_arg": ", int nlayers" if extruded else "",
        "nlayers": ", f->n_layers" if extruded else "",
    }

    evaluate_template_c = """#include <math.h>
#include <firedrake_geometry.h>

struct ReferenceCoords {
    double X[%(geometric_dimension)d];
    double J[%(geometric_dimension)d * %(topological_dimension)d];
    double K[%(topological_dimension)d * %(geometric_dimension)d];
    double detJ;
};

static inline void to_reference_coords_kernel(void *result_, double *x0, int *return_value, double **C)
{
    struct ReferenceCoords *result = (struct ReferenceCoords *) result_;

    const int space_dim = %(geometric_dimension)d;

    /*
     * Mapping coordinates from physical to reference space
     */

    double *X = result->X;
%(init_X)s
    double x[space_dim];
    double *J = result->J;
    double *K = result->K;
    double detJ;

    double dX[%(topological_dimension)d];
    int converged = 0;

    for (int it = 0; !converged && it < %(max_iteration_count)d; it++) {
%(to_reference_coords)s

         if (%(dX_norm_square)s < %(convergence_epsilon)g * %(convergence_epsilon)g) {
             converged = 1;
         }

%(X_isub_dX)s
    }

    result->detJ = detJ;

    // Are we inside the reference element?
    *return_value = %(inside_predicate)s;
}

static inline void wrap_to_reference_coords(void *result_, double *x, int *return_value,
                                            double *coords, int *coords_map%(extruded_arg)s, int cell);

int to_reference_coords(void *result_, struct Function *f, int cell, double *x)
{
    int return_value;
    wrap_to_reference_coords(result_, x, &return_value, f->coords, f->coords_map%(nlayers)s, cell);
    return return_value;
}
"""

    return evaluate_template_c % code
Example #35
0
def _interpolator(V, dat, expr, subset, access):
    to_element = create_element(V.ufl_element(), vector_is_mixed=False)
    to_pts = []

    if access is op2.READ:
        raise ValueError("Can't have READ access for output function")
    if V.ufl_element().mapping() != "identity":
        raise NotImplementedError("Can only interpolate onto elements "
                                  "with affine mapping. Try projecting instead")

    for dual in to_element.dual_basis():
        if not isinstance(dual, FIAT.functional.PointEvaluation):
            raise NotImplementedError("Can only interpolate onto point "
                                      "evaluation operators. Try projecting instead")
        pts, = dual.pt_dict.keys()
        to_pts.append(pts)

    if len(expr.ufl_shape) != len(V.ufl_element().value_shape()):
        raise RuntimeError('Rank mismatch: Expression rank %d, FunctionSpace rank %d'
                           % (len(expr.ufl_shape), len(V.ufl_element().value_shape())))

    if expr.ufl_shape != V.ufl_element().value_shape():
        raise RuntimeError('Shape mismatch: Expression shape %r, FunctionSpace shape %r'
                           % (expr.ufl_shape, V.ufl_element().value_shape()))

    mesh = V.ufl_domain()
    coords = mesh.coordinates

    if not isinstance(expr, firedrake.Expression):
        if expr.ufl_domain() and expr.ufl_domain() != V.mesh():
            raise NotImplementedError("Interpolation onto another mesh not supported.")
        if expr.ufl_shape != V.shape:
            raise ValueError("UFL expression has incorrect shape for interpolation.")
        ast, oriented, needs_cell_sizes, coefficients, _ = compile_ufl_kernel(expr, to_pts, coords, coffee=False)
        kernel = op2.Kernel(ast, ast.name)
    elif hasattr(expr, "eval"):
        kernel, oriented, needs_cell_sizes, coefficients = compile_python_kernel(expr, to_pts, to_element, V, coords)
    else:
        raise RuntimeError("Attempting to evaluate an Expression which has no value.")

    cell_set = coords.cell_set
    if subset is not None:
        assert subset.superset == cell_set
        cell_set = subset
    args = [kernel, cell_set]

    if dat in set((c.dat for c in coefficients)):
        output = dat
        dat = op2.Dat(dat.dataset)
        if access is not op2.WRITE:
            copyin = (partial(output.copy, dat), )
        else:
            copyin = ()
        copyout = (partial(dat.copy, output), )
    else:
        copyin = ()
        copyout = ()
    args.append(dat(access, V.cell_node_map()))
    if oriented:
        co = mesh.cell_orientations()
        args.append(co.dat(op2.READ, co.cell_node_map()))
    if needs_cell_sizes:
        cs = mesh.cell_sizes
        args.append(cs.dat(op2.READ, cs.cell_node_map()))
    for coefficient in coefficients:
        m_ = coefficient.cell_node_map()
        args.append(coefficient.dat(op2.READ, m_))

    for o in coefficients:
        domain = o.ufl_domain()
        if domain is not None and domain.topology != mesh.topology:
            raise NotImplementedError("Interpolation onto another mesh not supported.")

    return copyin + (op2.ParLoop(*args).compute, ) + copyout
Example #36
0
def test_triangle_basic(ufl_element):
    element = create_element(ufl_element)
    assert isinstance(element, supported_elements[ufl_element.family()])
Example #37
0
def test_interval_variant(family, variant, expected_cls):
    ufl_element = ufl.FiniteElement(family, ufl.interval, 3, variant=variant)
    assert isinstance(create_element(ufl_element), expected_cls)
Example #38
0
def compile_element(ufl_element, cdim):
    """Generates C code for point evaluations.

    :arg ufl_element: UFL element of the function space
    :arg cdim: ``cdim`` of the function space
    :returns: C code as string
    """
    from tsfc import default_parameters
    from firedrake.pointquery_utils import set_float_formatting, format
    from tsfc.fiatinterface import create_element
    from FIAT.reference_element import TensorProductCell as two_product_cell
    import sympy as sp
    import numpy as np

    # Set code generation parameters
    set_float_formatting(default_parameters()["precision"])

    def calculate_basisvalues(ufl_cell, fiat_element):
        f_component = format["component"]
        f_decl = format["declaration"]
        f_float_decl = format["float declaration"]
        f_tensor = format["tabulate tensor"]
        f_new_line = format["new line"]

        tdim = ufl_cell.topological_dimension()
        gdim = ufl_cell.geometric_dimension()

        code = []

        # Symbolic tabulation
        tabs = fiat_element.tabulate(
            0,
            np.array([[
                sp.Symbol("reference_coords.X[%d]" % i) for i in range(tdim)
            ]]))
        tabs = tabs[(0, ) * tdim]
        tabs = tabs.reshape(tabs.shape[:-1])

        # Generate code for intermediate values
        s_code, (theta, ) = ssa_arrays([tabs])
        for name, value in s_code:
            code += [f_decl(f_float_decl, name, c_print(value))]

        # Prepare Jacobian, Jacobian inverse and determinant
        s_detJ = sp.Symbol('detJ')
        s_J = np.array([[
            sp.Symbol("J[{i}*{tdim} + {j}]".format(i=i, j=j, tdim=tdim))
            for j in range(tdim)
        ] for i in range(gdim)])
        s_Jinv = np.array([[
            sp.Symbol("K[{i}*{gdim} + {j}]".format(i=i, j=j, gdim=gdim))
            for j in range(gdim)
        ] for i in range(tdim)])

        # Apply transformations
        phi = []
        for i, val in enumerate(theta):
            mapping = fiat_element.mapping()[i]
            if mapping == "affine":
                phi.append(val)
            elif mapping == "contravariant piola":
                phi.append(s_J.dot(val) / s_detJ)
            elif mapping == "covariant piola":
                phi.append(s_Jinv.transpose().dot(val))
            else:
                raise ValueError("Unknown mapping: %s" % mapping)
        phi = np.asarray(phi, dtype=object)

        # Dump tables of basis values
        code += ["", "\t// Values of basis functions"]
        code += [
            f_decl("double", f_component("phi", phi.shape),
                   f_new_line + f_tensor(phi))
        ]

        shape = phi.shape
        if len(shape) <= 1:
            vdim = 1
        elif len(shape) == 2:
            vdim = shape[1]
        return "\n".join(code), vdim

    # Create FIAT element
    element = create_element(ufl_element, vector_is_mixed=False)
    cell = ufl_element.cell()

    calculate_basisvalues, vdim = calculate_basisvalues(cell, element)
    extruded = isinstance(element.get_reference_element(), two_product_cell)

    code = {
        "cdim": cdim,
        "vdim": vdim,
        "geometric_dimension": cell.geometric_dimension(),
        "ndofs": element.space_dimension(),
        "calculate_basisvalues": calculate_basisvalues,
        "extruded_arg": ", %s nlayers" % as_cstr(IntType) if extruded else "",
        "nlayers": ", f->n_layers" if extruded else "",
        "IntType": as_cstr(IntType),
    }

    evaluate_template_c = """static inline void evaluate_kernel(double *result, double *phi_, double **F)
{
    const int ndofs = %(ndofs)d;
    const int cdim = %(cdim)d;
    const int vdim = %(vdim)d;

    double (*phi)[vdim] = (double (*)[vdim]) phi_;

    // F: ndofs x cdim
    // phi: ndofs x vdim
    // result = F' * phi: cdim x vdim
    //
    // Usually cdim == 1 or vdim == 1.

    for (int q = 0; q < cdim * vdim; q++) {
        result[q] = 0.0;
    }
    for (int i = 0; i < ndofs; i++) {
        for (int c = 0; c < cdim; c++) {
            for (int v = 0; v < vdim; v++) {
                result[c*vdim + v] += F[i][c] * phi[i][v];
            }
        }
    }
}

static inline void wrap_evaluate(double *result, double *phi, double *data, %(IntType)s *map%(extruded_arg)s, %(IntType)s cell);

int evaluate(struct Function *f, double *x, double *result)
{
    struct ReferenceCoords reference_coords;
    int cell = locate_cell(f, x, %(geometric_dimension)d, &to_reference_coords, &reference_coords);
    if (cell == -1) {
        return -1;
    }

    if (!result) {
        return 0;
    }

    double *J = reference_coords.J;
    double *K = reference_coords.K;
    double detJ = reference_coords.detJ;

%(calculate_basisvalues)s

    wrap_evaluate(result, (double *)phi, f->f, f->f_map%(nlayers)s, cell);
    return 0;
}
"""

    return evaluate_template_c % code
def test_triangle_basic(ufl_element):
    element = f.create_element(ufl_element)
    assert isinstance(element, f.supported_elements[ufl_element.family()])
Example #40
0
def test_quadrilateral_variant_spectral_rtcf():
    element = create_element(ufl.FiniteElement('RTCF', ufl.quadrilateral, 2, variant='spectral'))
    assert isinstance(element.element._elements[0].A, FIAT.GaussLobattoLegendre)
    assert isinstance(element.element._elements[0].B, FIAT.GaussLegendre)
    assert isinstance(element.element._elements[1].A, FIAT.GaussLegendre)
    assert isinstance(element.element._elements[1].B, FIAT.GaussLobattoLegendre)
Example #41
0
def test_quadrilateral_variant_spectral_dq():
    element = create_element(ufl.FiniteElement('DQ', ufl.quadrilateral, 1, variant='spectral'))
    assert isinstance(element.element.A, FIAT.GaussLegendre)
    assert isinstance(element.element.B, FIAT.GaussLegendre)
Example #42
0
def test_triangle_variant_spectral_fail():
    ufl_element = ufl.FiniteElement('DP', ufl.triangle, 2, variant='spectral')
    with pytest.raises(ValueError):
        create_element(ufl_element)
Example #43
0
def test_cache_hit(ufl_element):
    A = create_element(ufl_element)
    B = create_element(ufl_element)

    assert A is B
def _interpolator(V, dat, expr, subset, access):
    to_element = create_element(V.ufl_element(), vector_is_mixed=False)
    to_pts = []

    if access is op2.READ:
        raise ValueError("Can't have READ access for output function")
    if V.ufl_element().mapping() != "identity":
        raise NotImplementedError("Can only interpolate onto elements "
                                  "with affine mapping. Try projecting instead")

    for dual in to_element.dual_basis():
        if not isinstance(dual, FIAT.functional.PointEvaluation):
            raise NotImplementedError("Can only interpolate onto point "
                                      "evaluation operators. Try projecting instead")
        pts, = dual.pt_dict.keys()
        to_pts.append(pts)

    if len(expr.ufl_shape) != len(V.ufl_element().value_shape()):
        raise RuntimeError('Rank mismatch: Expression rank %d, FunctionSpace rank %d'
                           % (len(expr.ufl_shape), len(V.ufl_element().value_shape())))

    if expr.ufl_shape != V.ufl_element().value_shape():
        raise RuntimeError('Shape mismatch: Expression shape %r, FunctionSpace shape %r'
                           % (expr.ufl_shape, V.ufl_element().value_shape()))

    mesh = V.ufl_domain()
    coords = mesh.coordinates

    if not isinstance(expr, firedrake.Expression):
        if expr.ufl_domain() and expr.ufl_domain() != V.mesh():
            raise NotImplementedError("Interpolation onto another mesh not supported.")
        if expr.ufl_shape != V.shape:
            raise ValueError("UFL expression has incorrect shape for interpolation.")
        ast, oriented, needs_cell_sizes, coefficients, _ = compile_ufl_kernel(expr, to_pts, coords, coffee=False)
        kernel = op2.Kernel(ast, ast.name)
    elif hasattr(expr, "eval"):
        kernel, oriented, needs_cell_sizes, coefficients = compile_python_kernel(expr, to_pts, to_element, V, coords)
    else:
        raise RuntimeError("Attempting to evaluate an Expression which has no value.")

    cell_set = coords.cell_set
    if subset is not None:
        assert subset.superset == cell_set
        cell_set = subset
    args = [kernel, cell_set]

    if dat in set((c.dat for c in coefficients)):
        output = dat
        dat = op2.Dat(dat.dataset)
        if access is not op2.WRITE:
            copyin = (partial(output.copy, dat), )
        else:
            copyin = ()
        copyout = (partial(dat.copy, output), )
    else:
        copyin = ()
        copyout = ()
    args.append(dat(access, V.cell_node_map()))
    if oriented:
        co = mesh.cell_orientations()
        args.append(co.dat(op2.READ, co.cell_node_map()))
    if needs_cell_sizes:
        cs = mesh.cell_sizes
        args.append(cs.dat(op2.READ, cs.cell_node_map()))
    for coefficient in coefficients:
        m_ = coefficient.cell_node_map()
        args.append(coefficient.dat(op2.READ, m_))

    for o in coefficients:
        domain = o.ufl_domain()
        if domain is not None and domain.topology != mesh.topology:
            raise NotImplementedError("Interpolation onto another mesh not supported.")

    return copyin + (partial(op2.par_loop, *args), ) + copyout
Example #45
0
def prepare_arguments(arguments, indices, interior_facet=False):
    """Bridges the kernel interface and the GEM abstraction for
    Arguments.  Vector Arguments are rearranged here for interior
    facet integrals.

    :arg arguments: UFL Arguments
    :arg indices: Argument indices
    :arg interior_facet: interior facet integral?
    :returns: (funarg, prepare, expression, finalise)
         funarg      - :class:`coffee.Decl` function argument
         prepare     - list of COFFEE nodes to be prepended to the
                       kernel body
         expressions - GEM expressions referring to the argument
                       tensor
         finalise    - list of COFFEE nodes to be appended to the
                       kernel body
    """
    assert isinstance(interior_facet, bool)

    if len(arguments) == 0:
        # No arguments
        funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol("A", rank=(1,)))
        expression = gem.Indexed(gem.Variable("A", (1,)), (0,))

        return funarg, [], [expression], []

    elements = tuple(create_element(arg.ufl_element()) for arg in arguments)

    if not interior_facet:
        # Not an interior facet integral
        shape = tuple(element.space_dimension() for element in elements)

        funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol("A", rank=shape))
        expression = gem.Indexed(gem.Variable("A", shape), indices)

        return funarg, [], [expression], []

    if not any(isinstance(element, MixedElement) for element in elements):
        # Interior facet integral, but no vector (mixed) arguments
        shape = tuple(2 * element.space_dimension() for element in elements)

        funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol("A", rank=shape))
        varexp = gem.Variable("A", shape)

        expressions = []
        for restrictions in product((0, 1), repeat=len(arguments)):
            expressions.append(gem.FlexiblyIndexed(
                varexp,
                tuple((r * e.space_dimension(), ((i, e.space_dimension()),))
                      for e, i, r in zip(elements, indices, restrictions))
            ))

        return funarg, [], expressions, []

    # Interior facet integral + vector (mixed) argument(s)
    shape = tuple(element.space_dimension() for element in elements)
    funarg_shape = tuple(s * 2 for s in shape)
    funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol("A", rank=funarg_shape))

    prepare = []
    expressions = []

    references = []
    for restrictions in product((0, 1), repeat=len(arguments)):
        name = "A" + "".join(map(str, restrictions))

        prepare.append(coffee.Decl(SCALAR_TYPE,
                                   coffee.Symbol(name, rank=shape),
                                   init=coffee.ArrayInit(numpy.zeros(1))))
        expressions.append(gem.Indexed(gem.Variable(name, shape), indices))

        for multiindex in numpy.ndindex(shape):
            references.append(coffee.Symbol(name, multiindex))

    restriction_shape = []
    for e in elements:
        if isinstance(e, MixedElement):
            restriction_shape += [len(e.elements()),
                                  e.elements()[0].space_dimension()]
        else:
            restriction_shape += [1, e.space_dimension()]
    restriction_shape = tuple(restriction_shape)

    references = numpy.array(references)
    if len(arguments) == 1:
        references = references.reshape((2,) + restriction_shape)
        references = references.transpose(1, 0, 2)
    elif len(arguments) == 2:
        references = references.reshape((2, 2) + restriction_shape)
        references = references.transpose(2, 0, 3, 4, 1, 5)
    references = references.reshape(funarg_shape)

    finalise = []
    for multiindex in numpy.ndindex(funarg_shape):
        finalise.append(coffee.Assign(coffee.Symbol("A", rank=multiindex),
                                      references[multiindex]))

    return funarg, prepare, expressions, finalise
def test_cache_hit(ufl_element):
    A = f.create_element(ufl_element)
    B = f.create_element(ufl_element)

    assert A is B
Example #47
0
def test_quadrilateral_variant_spectral_dq():
    element = create_element(
        ufl.FiniteElement('DQ', ufl.quadrilateral, 1, variant='spectral'))
    assert isinstance(element.element.A, FIAT.GaussLegendre)
    assert isinstance(element.element.B, FIAT.GaussLegendre)
Example #48
0
def fiat_compat(element):
    from tsfc.fiatinterface import create_element
    return FiatElementWrapper(create_element(element),
                              degree=spanning_degree(element))
Example #49
0
def fiat_compat(element):
    from tsfc.fiatinterface import create_element
    from finat.fiat_elements import FiatElement

    assert element.cell().is_simplex()
    return FiatElement(create_element(element))
Example #50
0
def convert_tensorproductelement(element):
    cell = element.cell()
    if type(cell) is not ufl.TensorProductCell:
        raise ValueError("TensorProductElement not on TensorProductCell?")
    return finat.TensorProductElement(
        [create_element(elem) for elem in element.sub_elements()])
Example #51
0
def prepare_coefficient(coefficient, name, mode=None, interior_facet=False):
    """Bridges the kernel interface and the GEM abstraction for
    Coefficients.  Mixed element Coefficients are rearranged here for
    interior facet integrals.

    :arg coefficient: UFL Coefficient
    :arg name: unique name to refer to the Coefficient in the kernel
    :arg mode: 'manual_loop' or 'list_tensor'; two ways to deal with
               interior facet integrals on mixed elements
    :arg interior_facet: interior facet integral?
    :returns: (funarg, prepare, expression)
         funarg     - :class:`coffee.Decl` function argument
         prepare    - list of COFFEE nodes to be prepended to the
                      kernel body
         expression - GEM expression referring to the Coefficient
                      values
    """
    if mode is None:
        mode = 'manual_loop'

    assert mode in ['manual_loop', 'list_tensor']
    assert isinstance(interior_facet, bool)

    if coefficient.ufl_element().family() == 'Real':
        # Constant
        funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol(name),
                             pointers=[("restrict",)],
                             qualifiers=["const"])

        expression = gem.reshape(gem.Variable(name, (None,)),
                                 coefficient.ufl_shape)

        return funarg, [], expression

    fiat_element = create_element(coefficient.ufl_element())
    size = fiat_element.space_dimension()

    if not interior_facet:
        # Simple case
        funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol(name),
                             pointers=[("const", "restrict"), ("restrict",)],
                             qualifiers=["const"])

        expression = gem.reshape(gem.Variable(name, (size, 1)), (size,), ())

        return funarg, [], expression

    if not isinstance(fiat_element, MixedElement):
        # Interior facet integral
        funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol(name),
                             pointers=[("const", "restrict"), ("restrict",)],
                             qualifiers=["const"])

        expression = gem.reshape(
            gem.Variable(name, (2 * size, 1)), (2, size), ()
        )

        return funarg, [], expression

    # Interior facet integral + mixed / vector element

    # Here we need to reorder the coefficient values.
    #
    # Incoming ordering: E1+ E1- E2+ E2- E3+ E3-
    # Required ordering: E1+ E2+ E3+ E1- E2- E3-
    #
    # Each of E[n]{+,-} is a vector of basis function coefficients for
    # subelement E[n].
    #
    # There are two code generation method to reorder the values.
    # We have not done extensive research yet as to which way yield
    # faster code.

    if mode == 'manual_loop':
        # In this case we generate loops outside the GEM abstraction
        # to reorder the values.  A whole E[n]{+,-} block is copied by
        # a single loop.
        name_ = name + "_"
        shape = (2, size)

        funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol(name_),
                             pointers=[("const", "restrict"), ("restrict",)],
                             qualifiers=["const"])
        prepare = [coffee.Decl(SCALAR_TYPE, coffee.Symbol(name, rank=shape))]
        expression = gem.Variable(name, shape)

        offset = 0
        i = coffee.Symbol("i")
        for element in fiat_element.elements():
            space_dim = element.space_dimension()

            loop_body = coffee.Assign(coffee.Symbol(name, rank=(0, "i"),
                                                    offset=((1, 0), (1, offset))),
                                      coffee.Symbol(name_, rank=("i", 0),
                                                    offset=((1, 2 * offset), (1, 0))))
            prepare.append(coffee_for(i, space_dim, loop_body))

            loop_body = coffee.Assign(coffee.Symbol(name, rank=(1, "i"),
                                                    offset=((1, 0), (1, offset))),
                                      coffee.Symbol(name_, rank=("i", 0),
                                                    offset=((1, 2 * offset + space_dim), (1, 0))))
            prepare.append(coffee_for(i, space_dim, loop_body))

            offset += space_dim

        return funarg, prepare, expression

    elif mode == 'list_tensor':
        # In this case we generate a gem.ListTensor to do the
        # reordering.  Every single element in a E[n]{+,-} block is
        # referenced separately.
        funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol(name),
                             pointers=[("const", "restrict"), ("restrict",)],
                             qualifiers=["const"])

        variable = gem.Variable(name, (2 * size, 1))

        facet_0 = []
        facet_1 = []
        offset = 0
        for element in fiat_element.elements():
            space_dim = element.space_dimension()

            for i in range(offset, offset + space_dim):
                facet_0.append(gem.Indexed(variable, (i, 0)))
            offset += space_dim

            for i in range(offset, offset + space_dim):
                facet_1.append(gem.Indexed(variable, (i, 0)))
            offset += space_dim

        expression = gem.ListTensor(numpy.array([facet_0, facet_1]))
        return funarg, [], expression
Example #52
0
def convert_tensorelement(element):
    scalar_element = create_element(element.sub_elements()[0])
    return finat.TensorFiniteElement(scalar_element,
                                     element.reference_value_shape())
Example #53
0
def test_interval_variant(family, variant, expected_cls):
    ufl_element = ufl.FiniteElement(family, ufl.interval, 3, variant=variant)
    assert isinstance(create_element(ufl_element), expected_cls)
Example #54
0
def compile_coordinate_element(ufl_coordinate_element):
    """Generates C code for changing to reference coordinates.

    :arg ufl_coordinate_element: UFL element of the coordinates
    :returns: C code as string
    """
    from tsfc import default_parameters
    from tsfc.fiatinterface import create_element
    from firedrake.pointeval_utils import ssa_arrays, c_print
    from FIAT.reference_element import TensorProductCell as two_product_cell
    import sympy as sp
    import numpy as np

    # Set code generation parameters
    set_float_formatting(default_parameters()["precision"])

    def dX_norm_square(topological_dimension):
        return " + ".join("dX[{0}]*dX[{0}]".format(i)
                          for i in range(topological_dimension))

    def X_isub_dX(topological_dimension):
        return "\n".join("\tX[{0}] -= dX[{0}];".format(i)
                         for i in range(topological_dimension))

    def is_affine(ufl_element):
        return ufl_element.cell().is_simplex(
        ) and ufl_element.degree() <= 1 and ufl_element.family() in [
            "Discontinuous Lagrange", "Lagrange"
        ]

    def inside_check(ufl_cell, fiat_cell):
        dim = ufl_cell.topological_dimension()
        point = tuple(sp.Symbol("X[%d]" % i) for i in range(dim))

        return " && ".join(
            "(%s)" % arg
            for arg in fiat_cell.contains_point(point, epsilon=1e-14).args)

    def init_X(fiat_element):
        f_float = format["floating point"]
        f_assign = format["assign"]

        fiat_cell = fiat_element.get_reference_element()
        vertices = np.array(fiat_cell.get_vertices())
        X = np.average(vertices, axis=0)
        return "\n".join(
            f_assign("X[%d]" % i, f_float(v)) for i, v in enumerate(X))

    def to_reference_coordinates(ufl_cell, fiat_element):
        f_decl = format["declaration"]
        f_float_decl = format["float declaration"]

        # Get the element cell name and geometric dimension.
        cell = ufl_cell
        gdim = cell.geometric_dimension()
        tdim = cell.topological_dimension()

        code = []

        # Symbolic tabulation
        tabs = fiat_element.tabulate(
            1, np.array([[sp.Symbol("X[%d]" % i) for i in range(tdim)]]))
        tabs = sorted((d, value.reshape(value.shape[:-1]))
                      for d, value in tabs.iteritems())

        # Generate code for intermediate values
        s_code, d_phis = ssa_arrays([v for k, v in tabs], prefix="t")
        phi = d_phis.pop(0)

        for name, value in s_code:
            code += [f_decl(f_float_decl, name, c_print(value))]

        # Cell coordinate data
        C = np.array([[sp.Symbol("C[%d][%d]" % (i, j)) for j in range(gdim)]
                      for i in range(fiat_element.space_dimension())])

        # Generate physical coordinates
        x = phi.dot(C)
        for i, e in enumerate(x):
            code += ["\tx[%d] = %s;" % (i, e)]

        # Generate Jacobian
        grad_phi = np.vstack(reversed(d_phis))
        J = np.transpose(grad_phi.dot(C))
        for i, row in enumerate(J):
            for j, e in enumerate(row):
                code += ["\tJ[%d * %d + %d] = %s;" % (i, tdim, j, e)]

        # Get code snippets for Jacobian, inverse of Jacobian and mapping of
        # coordinates from physical element to the FIAT reference element.
        code += ["compute_jacobian_inverse_%s(K, detJ, J);" % cellname[cell]]
        # FIXME: use cell orientations!
        # if needs_orientation:
        #     code_ += [format["orientation"]["ufc"](tdim, gdim)]

        x = np.array([sp.Symbol("x[%d]" % i) for i in range(gdim)])
        x0 = np.array([sp.Symbol("x0[%d]" % i) for i in range(gdim)])
        K = np.array(
            [[sp.Symbol("K[%d]" % (i * gdim + j)) for j in range(gdim)]
             for i in range(tdim)])

        dX = K.dot(x - x0)
        for i, e in enumerate(dX):
            code += ["\tdX[%d] = %s;" % (i, e)]

        return "\n".join(code)

    # Create FIAT element
    element = create_element(ufl_coordinate_element, vector_is_mixed=False)
    cell = ufl_coordinate_element.cell()

    # calculate_basisvalues, vdim = calculate_basisvalues(cell, element)
    extruded = isinstance(element.get_reference_element(), two_product_cell)

    code = {
        "geometric_dimension": cell.geometric_dimension(),
        "topological_dimension": cell.topological_dimension(),
        "inside_predicate": inside_check(cell,
                                         element.get_reference_element()),
        "to_reference_coords": to_reference_coordinates(cell, element),
        "init_X": init_X(element),
        "max_iteration_count": 1 if is_affine(ufl_coordinate_element) else 16,
        "convergence_epsilon": 1e-12,
        "dX_norm_square": dX_norm_square(cell.topological_dimension()),
        "X_isub_dX": X_isub_dX(cell.topological_dimension()),
        "extruded_arg": ", int nlayers" if extruded else "",
        "nlayers": ", f->n_layers" if extruded else "",
    }

    evaluate_template_c = """#include <math.h>
#include <firedrake_geometry.h>

struct ReferenceCoords {
    double X[%(geometric_dimension)d];
    double J[%(geometric_dimension)d * %(topological_dimension)d];
    double K[%(topological_dimension)d * %(geometric_dimension)d];
    double detJ;
};

static inline void to_reference_coords_kernel(void *result_, double *x0, int *return_value, double **C)
{
    struct ReferenceCoords *result = (struct ReferenceCoords *) result_;

    const int space_dim = %(geometric_dimension)d;

    /*
     * Mapping coordinates from physical to reference space
     */

    double *X = result->X;
%(init_X)s
    double x[space_dim];
    double *J = result->J;
    double *K = result->K;
    double detJ;

    double dX[%(topological_dimension)d];
    int converged = 0;

    for (int it = 0; !converged && it < %(max_iteration_count)d; it++) {
%(to_reference_coords)s

         if (%(dX_norm_square)s < %(convergence_epsilon)g * %(convergence_epsilon)g) {
             converged = 1;
         }

%(X_isub_dX)s
    }

    result->detJ = detJ;

    // Are we inside the reference element?
    *return_value = %(inside_predicate)s;
}

static inline void wrap_to_reference_coords(void *result_, double *x, int *return_value,
                                            double *coords, int *coords_map%(extruded_arg)s, int cell);

int to_reference_coords(void *result_, struct Function *f, int cell, double *x)
{
    int return_value;
    wrap_to_reference_coords(result_, x, &return_value, f->coords, f->coords_map%(nlayers)s, cell);
    return return_value;
}
"""

    return evaluate_template_c % code