Ejemplo n.º 1
0
    def tabulate_reference_dof_coordinates(self, L, ir, parameters):
        # TODO: Change signature to avoid copy? E.g.
        # virtual const std::vector<double> & tabulate_reference_dof_coordinates() const = 0;
        # See integral::enabled_coefficients for example

        # TODO: ensure points is a numpy array,
        #   get tdim from points.shape[1],
        #   place points in ir directly instead of the subdict
        ir = ir["tabulate_dof_coordinates"]

        # Raise error if tabulate_reference_dof_coordinates is ill-defined
        if not ir:
            msg = "tabulate_reference_dof_coordinates is not defined for this element"
            return generate_error(L, msg, parameters["convert_exceptions_to_warnings"])

        # Extract coordinates and cell dimension
        tdim = ir["tdim"]
        points = ir["points"]

        # Output argument
        reference_dof_coordinates = L.Symbol("reference_dof_coordinates")

        # Reference coordinates
        dof_X = L.Symbol("dof_X")
        dof_X_values = [X[jj] for X in points for jj in range(tdim)]
        decl = L.ArrayDecl("static const double", dof_X,
                           (len(points) * tdim,), values=dof_X_values)
        copy = L.MemCopy(dof_X, reference_dof_coordinates, tdim*len(points))

        code = [decl, copy]
        return code
Ejemplo n.º 2
0
    def evaluate_reference_basis_derivatives(self, L, ir, parameters):
        data = ir["evaluate_basis"]
        if isinstance(data, str):
            msg = "evaluate_reference_basis_derivatives: %s" % data
            return generate_error(L, msg, parameters["convert_exceptions_to_warnings"])

        return generate_evaluate_reference_basis_derivatives(L, data, parameters)
Ejemplo n.º 3
0
    def tabulate_reference_dof_coordinates(self, L, ir, parameters):
        # TODO: Change signature to avoid copy? E.g.
        # virtual const std::vector<double> & tabulate_reference_dof_coordinates() const = 0;
        # See integral::enabled_coefficients for example

        # TODO: ensure points is a numpy array,
        #   get tdim from points.shape[1],
        #   place points in ir directly instead of the subdict
        ir = ir["tabulate_dof_coordinates"]

        # Raise error if tabulate_reference_dof_coordinates is ill-defined
        if not ir:
            msg = "tabulate_reference_dof_coordinates is not defined for this element"
            return generate_error(L, msg,
                                  parameters["convert_exceptions_to_warnings"])

        # Extract coordinates and cell dimension
        tdim = ir["tdim"]
        points = ir["points"]

        # Output argument
        reference_dof_coordinates = L.Symbol("reference_dof_coordinates")

        # Reference coordinates
        dof_X = L.Symbol("dof_X")
        dof_X_values = [X[jj] for X in points for jj in range(tdim)]
        decl = L.ArrayDecl("static const double",
                           dof_X, (len(points) * tdim, ),
                           values=dof_X_values)
        copy = L.MemCopy(dof_X, reference_dof_coordinates, tdim * len(points))

        code = [decl, copy]
        return code
Ejemplo n.º 4
0
    def evaluate_reference_basis(self, L, ir, parameters):
        data = ir["evaluate_basis"]
        if isinstance(data, str):
            msg = "evaluate_reference_basis: %s" % data
            return generate_error(L, msg,
                                  parameters["convert_exceptions_to_warnings"])

        return generate_evaluate_reference_basis(L, data, parameters)
Ejemplo n.º 5
0
    def evaluate_basis_all(self, L, ir, parameters):
        data=ir["evaluate_basis"]

        # Handle unsupported elements.
        if isinstance(data, str):
            msg = "evaluate_basis_all: %s" % data
            return [generate_error(L, msg, parameters["convert_exceptions_to_warnings"])]

        physical_value_size = data["physical_value_size"]
        space_dimension = data["space_dimension"]

        x = L.Symbol("x")
        coordinate_dofs = L.Symbol("coordinate_dofs")
        cell_orientation = L.Symbol("cell_orientation")
        values = L.Symbol("values")

        # Special case where space dimension is one (constant elements).
        if space_dimension == 1:
            code = [L.Comment("Element is constant, calling evaluate_basis."),
                    L.Call("evaluate_basis",
                           (0, values, x, coordinate_dofs, cell_orientation))]
            return code

        r = L.Symbol("r")
        dof_values = L.Symbol("dof_values")
        if physical_value_size == 1:
            code = [ L.Comment("Helper variable to hold value of a single dof."),
                     L.VariableDecl("double", dof_values, 0.0),
                     L.Comment("Loop dofs and call evaluate_basis"),
                     L.ForRange(r, 0, space_dimension, index_type=index_type,
                                body=[L.Call("evaluate_basis",
                                             (r, L.AddressOf(dof_values), x,
                                              coordinate_dofs, cell_orientation)),
                                      L.Assign(values[r], dof_values)]
                               )
                   ]
        else:
            s = L.Symbol("s")
            code = [L.Comment("Helper variable to hold values of a single dof."),
                    L.ArrayDecl("double", dof_values, physical_value_size, 0.0),
                    L.Comment("Loop dofs and call evaluate_basis"),
                    L.ForRange(r, 0, space_dimension, index_type=index_type,
                               body=[L.Call("evaluate_basis",
                                             (r, dof_values, x,
                                              coordinate_dofs, cell_orientation)),
                                     L.ForRange(s, 0, physical_value_size,
                                                index_type=index_type,
                                                body=[L.Assign(values[r*physical_value_size+s], dof_values[s])])
                                    ]
                              )
                   ]

        return code
Ejemplo n.º 6
0
def _generate_body(L, i, dof, mapping, gdim, tdim, cell_shape, offset=0):
    "Generate code for a single dof."

    # EnrichedElement is handled by having [None, ..., None] dual basis
    if not dof:
        msg = "evaluate_dof(s) for enriched element not implemented."
        return ([generate_error(L, msg, True)], 0.0)

    points = list(dof.keys())

    # Generate different code if multiple points. (Otherwise ffc
    # compile time blows up.)
    if len(points) > 1:
        return _generate_multiple_points_body(L, i, dof, mapping, gdim, tdim,
                                              offset)

    # Get weights for mapping reference point to physical
    x = points[0]
    w = reference_to_physical_map(cell_shape)(x)

    # Map point onto physical element: y = F_K(x)
    code = []

    y = L.Symbol("y")
    coordinate_dofs = L.Symbol("coordinate_dofs")
    vals = L.Symbol("vals")
    c = L.Symbol("c")
    for j in range(gdim):
        yy = 0.0
        for k in range(len(w)):
            yy += w[k]*coordinate_dofs[k*gdim + j]
        code += [L.Assign(y[j], yy)]

    # Evaluate function at physical point
    code += [L.Call("f.evaluate", (vals, y, c))]

    # Map function values to the reference element
    F = _change_variables(L, mapping, gdim, tdim, offset)

    # Simple affine functions deserve special case:
    if len(F) == 1:
        return (code, dof[x][0][0]*F[0])

    # Flatten multi-indices
    (index_map, _) = build_component_numbering([tdim] * len(dof[x][0][1]), ())
    # Take inner product between components and weights
    value = 0.0
    for (w, k) in dof[x]:
        value += w*F[index_map[k]]

    # Return eval code and value
    return (code, value)
Ejemplo n.º 7
0
def _generate_body(L, i, dof, mapping, gdim, tdim, cell_shape, offset=0):
    "Generate code for a single dof."

    # EnrichedElement is handled by having [None, ..., None] dual basis
    if not dof:
        msg = "evaluate_dof(s) for enriched element not implemented."
        return ([generate_error(L, msg, True)], 0.0)

    points = list(dof.keys())

    # Generate different code if multiple points. (Otherwise ffc
    # compile time blows up.)
    if len(points) > 1:
        return _generate_multiple_points_body(L, i, dof, mapping, gdim, tdim,
                                              offset)

    # Get weights for mapping reference point to physical
    x = points[0]
    w = reference_to_physical_map(cell_shape)(x)

    # Map point onto physical element: y = F_K(x)
    code = []

    y = L.Symbol("y")
    coordinate_dofs = L.Symbol("coordinate_dofs")
    vals = L.Symbol("vals")
    c = L.Symbol("c")
    for j in range(gdim):
        yy = 0.0
        for k in range(len(w)):
            yy += w[k] * coordinate_dofs[k * gdim + j]
        code += [L.Assign(y[j], yy)]

    # Evaluate function at physical point
    code += [L.Call("f.evaluate", (vals, y, c))]

    # Map function values to the reference element
    F = _change_variables(L, mapping, gdim, tdim, offset)

    # Simple affine functions deserve special case:
    if len(F) == 1:
        return (code, dof[x][0][0] * F[0])

    # Flatten multi-indices
    (index_map, _) = build_component_numbering([tdim] * len(dof[x][0][1]), ())
    # Take inner product between components and weights
    value = 0.0
    for (w, k) in dof[x]:
        value += w * F[index_map[k]]

    # Return eval code and value
    return (code, value)
Ejemplo n.º 8
0
def generate_evaluate_reference_basis_derivatives(L, data, parameters):
    # Cutoff for feature to disable generation of this code (consider removing after benchmarking final result)
    if isinstance(data, str):
        msg = "evaluate_reference_basis_derivatives: %s" % (data, )
        return generate_error(L, msg,
                              parameters["convert_exceptions_to_warnings"])

    # Get some known dimensions
    element_cellname = data["cellname"]
    tdim = data["topological_dimension"]
    max_degree = data["max_degree"]
    reference_value_size = data["reference_value_size"]
    num_dofs = len(data["dofs_data"])

    # Output argument
    reference_values = L.Symbol("reference_values")

    # Input arguments
    order = L.Symbol("order")
    num_points = L.Symbol("num_points")
    X = L.Symbol("X")

    # Loop indices
    ip = L.Symbol("ip")  # point
    idof = L.Symbol("i")  # dof
    c = L.Symbol("c")  # component
    r = L.Symbol("r")  # derivative number

    # Define symbol for number of derivatives of given order
    num_derivatives = L.Symbol("num_derivatives")
    reference_values_size = num_points * num_dofs * num_derivatives * reference_value_size

    # FIXME: validate these dimensions
    ref_values = L.FlattenedArray(reference_values,
                                  dims=(num_points, num_dofs, num_derivatives,
                                        reference_value_size))
    # From evaluatebasis.py:
    #ref_values = L.FlattenedArray(reference_values, dims=(num_points, num_dofs, reference_value_size))

    # Initialization (zeroing) and cutoffs outside valid range of orders
    setup_code = [
        # Cutoff to evaluate_basis for order 0
        L.If(L.EQ(order, 0), [
            L.Call("evaluate_reference_basis",
                   (reference_values, num_points, X)),
            L.Return()
        ]),
        # Compute number of derivatives of this order
        L.VariableDecl("const " + index_type,
                       num_derivatives,
                       value=L.Call("std::pow", (tdim, order))),
        # Reset output values to zero
        L.MemZero(reference_values, reference_values_size),
        # Cutoff for higher order than we have
        L.If(L.GT(order, max_degree), L.Return()),
    ]

    # If max_degree is zero, we don't need to generate any more code
    if max_degree == 0:
        return setup_code

    # Tabulate dmats tables for all dofs and all derivative directions
    dmats_names, dmats_code = generate_tabulate_dmats(L, data["dofs_data"])

    # Generate code with static tables of expansion coefficients
    tables_code, coefficients_for_dof = generate_expansion_coefficients(
        L, data["dofs_data"])

    # Generate code to compute tables of basisvalues
    basisvalues_code, basisvalues_for_degree, need_fiat_coordinates = \
        generate_compute_basisvalues(L, data["dofs_data"], element_cellname, tdim, X, ip)

    # Generate all possible combinations of derivatives.
    combinations_code, combinations = _generate_combinations(
        L, tdim, max_degree, order, num_derivatives)

    # Define symbols for variables populated inside dof switch
    derivatives = L.Symbol("derivatives")
    reference_offset = L.Symbol("reference_offset")
    num_components = L.Symbol("num_components")

    # Get number of components of each basis function (>1 for dofs of piola mapped subelements)
    num_components_values = [
        dof_data["num_components"] for dof_data in data["dofs_data"]
    ]

    # Offset into parent mixed element to first component of each basis function
    reference_offset_values = [
        dof_data["reference_offset"] for dof_data in data["dofs_data"]
    ]

    # Max dimensions for the reference derivatives for each dof
    max_num_derivatives = tdim**max_degree
    max_num_components = max(num_components_values)

    # Add constant tables of these numbers
    tables_code += [
        L.ArrayDecl("const " + index_type,
                    reference_offset,
                    num_dofs,
                    values=reference_offset_values),
        L.ArrayDecl("const " + index_type,
                    num_components,
                    num_dofs,
                    values=num_components_values),
    ]

    # Access reference derivatives compactly
    derivs = L.FlattenedArray(derivatives,
                              dims=(num_components[idof], num_derivatives))

    # Create code for all basis values (dofs).
    dof_cases = []
    for i_dof, dof_data in enumerate(data["dofs_data"]):

        embedded_degree = dof_data["embedded_degree"]
        basisvalues = basisvalues_for_degree[embedded_degree]

        shape_dmats = numpy.shape(dof_data["dmats"][0])
        if shape_dmats[0] != shape_dmats[1]:
            error("Something is wrong with the dmats:\n%s" %
                  str(dof_data["dmats"]))

        aux = L.Symbol("aux")
        dmats = L.Symbol("dmats")
        dmats_old = L.Symbol("dmats_old")
        dmats_name = dmats_names[i_dof]

        # Create dmats matrix by multiplication
        comb = L.Symbol("comb")
        s = L.Symbol("s")
        t = L.Symbol("t")
        u = L.Symbol("u")
        tu = L.Symbol("tu")
        aux_computation_code = [
            L.ArrayDecl("double", aux, shape_dmats[0], values=0),
            L.Comment("Declare derivative matrix (of polynomial basis)."),
            L.ArrayDecl("double", dmats, shape_dmats, values=0),
            L.Comment("Initialize dmats."),
            L.VariableDecl(index_type, comb, combinations[r, 0]),
            L.MemCopy(L.AddressOf(dmats_name[comb][0][0]),
                      L.AddressOf(dmats[0][0]),
                      shape_dmats[0] * shape_dmats[1]),
            L.Comment("Looping derivative order to generate dmats."),
            L.ForRange(s,
                       1,
                       order,
                       index_type=index_type,
                       body=[
                           L.Comment("Store previous dmats matrix."),
                           L.ArrayDecl("double", dmats_old, shape_dmats),
                           L.MemCopy(L.AddressOf(dmats[0][0]),
                                     L.AddressOf(dmats_old[0][0]),
                                     shape_dmats[0] * shape_dmats[1]),
                           L.Comment("Resetting dmats."),
                           L.MemZero(L.AddressOf(dmats[0][0]),
                                     shape_dmats[0] * shape_dmats[1]),
                           L.Comment("Update dmats using an inner product."),
                           L.Assign(comb, combinations[r, s]),
                           L.ForRange(t,
                                      0,
                                      shape_dmats[0],
                                      index_type=index_type,
                                      body=L.ForRange(
                                          u,
                                          0,
                                          shape_dmats[1],
                                          index_type=index_type,
                                          body=L.ForRange(
                                              tu,
                                              0,
                                              shape_dmats[0],
                                              index_type=index_type,
                                              body=L.AssignAdd(
                                                  dmats[t, u],
                                                  dmats_name[comb, t, tu] *
                                                  dmats_old[tu, u]))))
                       ]),
            L.ForRange(s,
                       0,
                       shape_dmats[0],
                       index_type=index_type,
                       body=L.ForRange(t,
                                       0,
                                       shape_dmats[1],
                                       index_type=index_type,
                                       body=L.AssignAdd(
                                           aux[s],
                                           dmats[s, t] * basisvalues[t])))
        ]

        # Unrolled loop over components of basis function
        n = dof_data["num_components"]
        compute_ref_derivs_code = [
            L.Assign(derivs[cc][r], 0.0) for cc in range(n)
        ]

        compute_ref_derivs_code += [
            L.ForRange(s,
                       0,
                       shape_dmats[0],
                       index_type=index_type,
                       body=[
                           L.AssignAdd(
                               derivs[cc][r],
                               coefficients_for_dof[i_dof][cc][s] * aux[s])
                           for cc in range(n)
                       ])
        ]

        embedded_degree = dof_data["embedded_degree"]
        basisvalues = basisvalues_for_degree[embedded_degree]

        # Compute the derivatives of the basisfunctions on the reference (FIAT) element,
        # as the dot product of the new coefficients and basisvalues.

        case_code = [
            L.Comment("Compute reference derivatives for dof %d." % i_dof),
            # Accumulate sum_s coefficients[s] * aux[s]
            L.ForRange(r,
                       0,
                       num_derivatives,
                       index_type=index_type,
                       body=[aux_computation_code, compute_ref_derivs_code])
        ]

        dof_cases.append((i_dof, case_code))

    # Loop over all dofs, entering a different switch case in each iteration.
    # This is a legacy from the previous implementation where the loop
    # was in a separate function and all the setup above was also repeated
    # in a call for each dof.
    # TODO: Further refactoring is needed to improve on this situation,
    # but at least it's better than before. There's probably more code and
    # computations that can be shared between dofs, and this would probably
    # be easier to fix if mixed elements were handled separately!
    dof_loop_code = [
        L.Comment("Loop over all dofs"),
        L.ForRange(
            idof,
            0,
            num_dofs,
            index_type=index_type,
            body=[
                L.ArrayDecl("double", derivatives,
                            max_num_components * max_num_derivatives, 0.0),
                L.Switch(idof, dof_cases),
                L.ForRange(
                    r,
                    0,
                    num_derivatives,
                    index_type=index_type,
                    body=[
                        L.ForRange(
                            c,
                            0,
                            num_components[idof],
                            index_type=index_type,
                            body=[
                                L.Assign(
                                    ref_values[ip][idof][r][
                                        reference_offset[idof] + c], derivs[c]
                                    [r]),  # FIXME: validate ref_values dims
                            ]),
                    ])
            ]),
    ]

    # Define loop over points
    final_loop_code = [
        L.ForRange(ip,
                   0,
                   num_points,
                   index_type=index_type,
                   body=basisvalues_code + dof_loop_code)
    ]

    # Stitch it all together
    code = (setup_code + dmats_code + tables_code + combinations_code +
            final_loop_code)

    return code
Ejemplo n.º 9
0
    def transform_reference_basis_derivatives(self, L, ir, parameters):
        data = ir["evaluate_basis"]
        if isinstance(data, str):
            msg = "transform_reference_basis_derivatives: %s" % data
            return generate_error(L, msg, parameters["convert_exceptions_to_warnings"])

        # Get some known dimensions
        #element_cellname = data["cellname"]
        gdim = data["geometric_dimension"]
        tdim = data["topological_dimension"]
        max_degree = data["max_degree"]
        reference_value_size = data["reference_value_size"]
        physical_value_size = data["physical_value_size"]
        num_dofs = len(data["dofs_data"])

        max_g_d = gdim**max_degree
        max_t_d = tdim**max_degree

        # Output arguments
        values_symbol = L.Symbol("values")

        # Input arguments
        order = L.Symbol("order")
        num_points = L.Symbol("num_points")  # FIXME: Currently assuming 1 point?
        reference_values = L.Symbol("reference_values")
        J = L.Symbol("J")
        detJ = L.Symbol("detJ")
        K = L.Symbol("K")

        # Internal variables
        transform = L.Symbol("transform")

        # Indices, I've tried to use these for a consistent purpose
        ip = L.Symbol("ip") # point
        i = L.Symbol("i")   # physical component
        j = L.Symbol("j")   # reference component
        k = L.Symbol("k")   # order
        r = L.Symbol("r")   # physical derivative number
        s = L.Symbol("s")   # reference derivative number
        d = L.Symbol("d")   # dof

        combinations_code = []
        if max_degree == 0:
            # Don't need combinations
            num_derivatives_t = 1  # TODO: I think this is the right thing to do to make this still work for order=0?
            num_derivatives_g = 1
        elif tdim == gdim:
            num_derivatives_t = L.Symbol("num_derivatives")
            num_derivatives_g = num_derivatives_t
            combinations_code += [
                L.VariableDecl("const " + index_type, num_derivatives_t,
                               L.Call("std::pow", (tdim, order))),
            ]

            # Add array declarations of combinations
            combinations_code_t, combinations_t = _generate_combinations(L, tdim, max_degree, order, num_derivatives_t)
            combinations_code += combinations_code_t
            combinations_g = combinations_t
        else:
            num_derivatives_t = L.Symbol("num_derivatives_t")
            num_derivatives_g = L.Symbol("num_derivatives_g")
            combinations_code += [
                L.VariableDecl("const " + index_type, num_derivatives_t,
                               L.Call("std::pow", (tdim, order))),
                L.VariableDecl("const " + index_type, num_derivatives_g,
                               L.Call("std::pow", (gdim, order))),
            ]
            # Add array declarations of combinations
            combinations_code_t, combinations_t = _generate_combinations(L, tdim, max_degree, order, num_derivatives_t, suffix="_t")
            combinations_code_g, combinations_g = _generate_combinations(L, gdim, max_degree, order, num_derivatives_g, suffix="_g")
            combinations_code += combinations_code_t
            combinations_code += combinations_code_g

        # Define expected dimensions of argument arrays
        J = L.FlattenedArray(J, dims=(num_points, gdim, tdim))
        detJ = L.FlattenedArray(detJ, dims=(num_points,))
        K = L.FlattenedArray(K, dims=(num_points, tdim, gdim))

        values = L.FlattenedArray(values_symbol,
            dims=(num_points, num_dofs, num_derivatives_g, physical_value_size))
        reference_values = L.FlattenedArray(reference_values,
            dims=(num_points, num_dofs, num_derivatives_t, reference_value_size))

        # Generate code to compute the derivative transform matrix
        transform_matrix_code = [
            # Initialize transform matrix to all 1.0
            L.ArrayDecl("double", transform, (max_g_d, max_t_d)),
            L.ForRanges(
                (r, 0, num_derivatives_g),
                (s, 0, num_derivatives_t),
                index_type=index_type,
                body=L.Assign(transform[r, s], 1.0)
            ),
            ]
        if max_degree > 0:
            transform_matrix_code += [
                # Compute transform matrix entries, each a product of K entries
                L.ForRanges(
                    (r, 0, num_derivatives_g),
                    (s, 0, num_derivatives_t),
                    (k, 0, order),
                    index_type=index_type,
                    body=L.AssignMul(transform[r, s],
                                     K[ip, combinations_t[s, k], combinations_g[r, k]])
                ),
            ]

        # Initialize values to 0, will be added to inside loops
        values_init_code = [
            L.MemZero(values_symbol, num_points * num_dofs * num_derivatives_g * physical_value_size),
            ]

        # Make offsets available in generated code
        reference_offsets = L.Symbol("reference_offsets")
        physical_offsets = L.Symbol("physical_offsets")
        dof_attributes_code = [
            L.ArrayDecl("const " + index_type, reference_offsets, (num_dofs,),
                        values=[dof_data["reference_offset"] for dof_data in data["dofs_data"]]),
            L.ArrayDecl("const " + index_type, physical_offsets, (num_dofs,),
                        values=[dof_data["physical_offset"] for dof_data in data["dofs_data"]]),
            ]

        # Build dof lists for each mapping type
        mapping_dofs = defaultdict(list)
        for idof, dof_data in enumerate(data["dofs_data"]):
            mapping_dofs[dof_data["mapping"]].append(idof)

        # Generate code for each mapping type
        d = L.Symbol("d")
        transform_apply_code = []
        for mapping in sorted(mapping_dofs):
            # Get list of dofs using this mapping
            idofs = mapping_dofs[mapping]

            # Select iteration approach over dofs
            if idofs == list(range(idofs[0], idofs[-1]+1)):
                # Contiguous
                dofrange = (d, idofs[0], idofs[-1]+1)
                idof = d
            else:
                # Stored const array of dof indices
                idofs_symbol = L.Symbol("%s_dofs" % mapping.replace(" ", "_"))
                dof_attributes_code += [
                    L.ArrayDecl("const " + index_type, idofs_symbol,
                                (len(idofs),), values=idofs),
                ]
                dofrange = (d, 0, len(idofs))
                idof = idofs_symbol[d]

            # NB! Array access to offsets, these are not Python integers
            reference_offset = reference_offsets[idof]
            physical_offset = physical_offsets[idof]

            # How many components does each basis function with this mapping have?
            # This should be uniform, i.e. there should be only one element in this set:
            num_reference_components, = set(data["dofs_data"][i]["num_components"] for i in idofs)

            M_scale, M_row, num_physical_components = generate_element_mapping(
                mapping, i,
                num_reference_components, tdim, gdim,
                J[ip], detJ[ip], K[ip]
            )

#            transform_apply_body = [
#                L.AssignAdd(values[ip, idof, r, physical_offset + k],
#                            transform[r, s] * reference_values[ip, idof, s, reference_offset + k])
#                for k in range(num_physical_components)
#            ]

            msg = "Using %s transform to map values back to the physical element." % mapping.replace("piola", "Piola")

            mapped_value = L.Symbol("mapped_value")
            transform_apply_code += [
                L.ForRanges(
                    dofrange,
                    (s, 0, num_derivatives_t),
                    (i, 0, num_physical_components),
                    index_type=index_type, body=[
                        # Unrolled application of mapping to one physical component,
                        # for affine this automatically reduces to
                        #   mapped_value = reference_values[..., reference_offset]
                        L.Comment(msg),
                        L.VariableDecl("const double", mapped_value,
                                       M_scale * sum(M_row[jj] * reference_values[ip, idof, s, reference_offset + jj]
                                                     for jj in range(num_reference_components))),
                        # Apply derivative transformation, for order=0 this reduces to
                        # values[ip,idof,0,physical_offset+i] = transform[0,0]*mapped_value
                        L.Comment("Mapping derivatives back to the physical element"),
                        L.ForRanges(
                            (r, 0, num_derivatives_g),
                            index_type=index_type, body=[
                                L.AssignAdd(values[ip, idof, r, physical_offset + i],
                                            transform[r, s] * mapped_value)
                        ])
                ])
            ]

        # Transform for each point
        point_loop_code = [
            L.ForRange(ip, 0, num_points, index_type=index_type, body=(
                transform_matrix_code
                + transform_apply_code
            ))
        ]

        # Join code
        code = (
            combinations_code
            + values_init_code
            + dof_attributes_code
            + point_loop_code
        )
        return code
Ejemplo n.º 10
0
def generate_evaluate_reference_basis(L, data, parameters):
    """Generate code to evaluate element basisfunctions at an arbitrary
    point on the reference element.

    The value(s) of the basisfunction is/are computed as in FIAT as
    the dot product of the coefficients (computed at compile time) and
    basisvalues which are dependent on the coordinate and thus have to
    be computed at run time.

    The function should work for all elements supported by FIAT, but
    it remains untested for tensor valued elements.

    This code is adapted from code in FFC which computed the basis
    from physical coordinates, and also to use UFLACS utilities.

    The FFC code has a comment "From FIAT_NEW.polynomial_set.tabulate()".

    """
    # Cutoff for feature to disable generation of this code (consider
    # removing after benchmarking final result)
    if isinstance(data, str):
        msg = "evaluate_reference_basis: %s" % (data,)
        return generate_error(L, msg, parameters["convert_exceptions_to_warnings"])

    # Get some known dimensions
    element_cellname = data["cellname"]
    tdim = data["topological_dimension"]
    reference_value_size = data["reference_value_size"]
    num_dofs = len(data["dofs_data"])

    # Input geometry
    num_points = L.Symbol("num_points")
    X = L.Symbol("X")

    # Output values
    reference_values = L.Symbol("reference_values")
    ref_values = L.FlattenedArray(reference_values, dims=(num_points, num_dofs, reference_value_size))

    # Loop indices
    ip = L.Symbol("ip")
    k = L.Symbol("k")
    c = L.Symbol("c")
    r = L.Symbol("r")

    # Generate code with static tables of expansion coefficients
    tables_code, coefficients_for_dof = generate_expansion_coefficients(L, data["dofs_data"])

    # Reset reference_values[:] to 0
    reset_values_code = [
        L.ForRange(k, 0, num_points*num_dofs*reference_value_size,
                   index_type=index_type,
                   body=L.Assign(reference_values[k], 0.0))
        ]
    setup_code = tables_code + reset_values_code

    # Generate code to compute tables of basisvalues
    basisvalues_code, basisvalues_for_degree, need_fiat_coordinates = \
        generate_compute_basisvalues(L, data["dofs_data"], element_cellname, tdim, X, ip)

    # Accumulate products of basisvalues and coefficients into values
    accumulation_code = [
        L.Comment("Accumulate products of coefficients and basisvalues"),
        ]
    for idof, dof_data in enumerate(data["dofs_data"]):
        embedded_degree = dof_data["embedded_degree"]
        num_components = dof_data["num_components"]
        num_members = dof_data["num_expansion_members"]

        # In ffc representation, this is extracted per dof
        # (but will coincide for some dofs of piola mapped elements):
        reference_offset = dof_data["reference_offset"]

        # Select the right basisvalues for this dof
        basisvalues = basisvalues_for_degree[embedded_degree]

        # Select the right coefficients for this dof
        coefficients = coefficients_for_dof[idof]

        # Generate basis accumulation loop
        if num_components > 1:
            # Could just simplify by using this generic code
            # and dropping the below two special cases
            accumulation_code += [
                L.ForRange(c, 0, num_components, index_type=index_type,
                           body=L.ForRange(r, 0, num_members,
                                           index_type=index_type,
                                           body=L.AssignAdd(ref_values[ip, idof, reference_offset + c],
                                                            coefficients[c, r] * basisvalues[r])))
                ]
        elif num_members > 1:
            accumulation_code += [
                L.ForRange(r, 0, num_members, index_type=index_type,
                           body=L.AssignAdd(ref_values[ip, idof, reference_offset], coefficients[0, r] * basisvalues[r]))
                ]
        else:
            accumulation_code += [
                L.AssignAdd(ref_values[ip, idof, reference_offset], coefficients[0, 0] * basisvalues[0])
                ]

        # TODO: Move this mapping to its own ufc function
        # e.g. finite_element::apply_element_mapping(reference_values,
        # J, K)
        # code += _generate_apply_mapping_to_computed_values(L, dof_data) # Only works for affine (no-op)

    # Stitch it all together
    code = [
        setup_code,
        L.ForRange(ip, 0, num_points, index_type=index_type,
                   body=basisvalues_code + accumulation_code)
        ]
    return code
Ejemplo n.º 11
0
def generate_evaluate_basis_derivatives(L, data):
    """Evaluate the derivatives of an element basisfunction at a
    point. The values are computed as in FIAT as the matrix product of
    the coefficients (computed at compile time), basisvalues which are
    dependent on the coordinate and thus have to be computed at run
    time and combinations (depending on the order of derivative) of
    dmats tables which hold the derivatives of the expansion
    coefficients.

    """

    if isinstance(data, str):
        msg = "evaluate_basis_derivatives: %s" % data
        return [generate_error(L, msg, True)]

    # Initialise return code.
    code = []

    # Get the element cell domain, geometric and topological dimension.
    element_cellname = data["cellname"]
    gdim = data["geometric_dimension"]
    tdim = data["topological_dimension"]
    max_degree = data["max_degree"]
    physical_value_size = data["physical_value_size"]

    # Compute number of derivatives that has to be computed, and
    # declare an array to hold the values of the derivatives on the
    # reference element.
    values = L.Symbol("values")
    n = L.Symbol("n")
    dofs = L.Symbol("dofs")
    x = L.Symbol("x")
    coordinate_dofs = L.Symbol("coordinate_dofs")
    cell_orientation = L.Symbol("cell_orientation")

    if tdim == gdim:
        _t, _g = ("", "")
        num_derivatives_t = L.Symbol("num_derivatives")
        num_derivatives_g = L.Symbol("num_derivatives")
        code += [L.VariableDecl("std::size_t", num_derivatives_t, L.Call("std::pow", (tdim, n)))]
    else:
        _t, _g = ("_t", "_g")
        num_derivatives_t = L.Symbol("num_derivatives_t")
        num_derivatives_g = L.Symbol("num_derivatives_g")
        if max_degree > 0:
            code += [L.VariableDecl("std::size_t", num_derivatives_t, L.Call("std::pow", (tdim, n)))]
        code += [L.VariableDecl("std::size_t", num_derivatives_g, L.Call("std::pow", (gdim, n)))]

    # Reset all values.
    code += [L.MemZero(values, physical_value_size*num_derivatives_g)]

    # Handle values of argument 'n'.
    code += [L.Comment("Call evaluate_basis_all if order of derivatives is equal to zero.")]
    code += [L.If(L.EQ(n, 0), [L.Call("evaluate_basis", (L.Symbol("i"), values, x, coordinate_dofs, cell_orientation)), L.Return()])]

    # If max_degree is zero, return code (to avoid declarations such as
    # combinations[1][0]) and because there's nothing to compute.)
    if max_degree == 0:
        return code

    code += [L.Comment("If order of derivatives is greater than the maximum polynomial degree, return zeros.")]
    code += [L.If(L.GT(n, max_degree), [L.Return()])]

    # Generate geo code.
    code += jacobian(L, gdim, tdim, element_cellname)
    code += inverse_jacobian(L, gdim, tdim, element_cellname)
    if data["needs_oriented"] and tdim != gdim:
        code += orientation(L)

    code += fiat_coordinate_mapping(L, element_cellname, gdim)

    # Generate all possible combinations of derivatives.
    combinations_code_t, combinations_t = _generate_combinations(L, tdim, max_degree, n, num_derivatives_t, _t)
    code += combinations_code_t
    if tdim != gdim:
        combinations_code_g, combinations_g = _generate_combinations(L, gdim, max_degree, n, num_derivatives_g, "_g")
        code += combinations_code_g

    # Generate the transformation matrix.
    code += _generate_transform(L, element_cellname, gdim, tdim, max_degree)

    # Create code for all basis values (dofs).
    dof_cases = []
    for i, dof_data in enumerate(data["dofs_data"]):
        dof_cases.append((i, _generate_dof_code(L, data, dof_data)))
    code += [L.Switch(L.Symbol("i"), dof_cases)]
    return code
Ejemplo n.º 12
0
    def tabulate_dof_coordinates(self, L, ir, parameters):
        ir = ir["tabulate_dof_coordinates"]

        # Raise error if tabulate_dof_coordinates is ill-defined
        if not ir:
            msg = "tabulate_dof_coordinates is not defined for this element"
            return generate_error(L, msg, parameters["convert_exceptions_to_warnings"])

        # Extract coordinates and cell dimension
        gdim = ir["gdim"]
        tdim = ir["tdim"]
        points = ir["points"]

        # Extract cellshape
        cell_shape = ir["cell_shape"]

        # Output argument
        dof_coordinates = L.FlattenedArray(L.Symbol("dof_coordinates"),
                                           dims=(len(points), gdim))

        # Input argument
        coordinate_dofs = L.Symbol("coordinate_dofs")

        # Loop indices
        i = L.Symbol("i")
        k = L.Symbol("k")
        ip = L.Symbol("ip")

        # Basis symbol
        phi = L.Symbol("phi")

        # TODO: Get rid of all places that use reference_to_physical_map, it is restricted to a basis of degree 1
        # Create code for evaluating coordinate mapping
        num_scalar_xdofs = _num_vertices(cell_shape)
        cg1_basis = reference_to_physical_map(cell_shape)
        phi_values = numpy.asarray([phi_comp for X in points for phi_comp in cg1_basis(X)])
        assert len(phi_values) == len(points) * num_scalar_xdofs

        # TODO: Use precision parameter here
        phi_values = clamp_table_small_numbers(phi_values)

        code = [
            L.Assign(
                dof_coordinates[ip][i],
                sum(phi_values[ip*num_scalar_xdofs + k] * coordinate_dofs[gdim*k + i]
                    for k in range(num_scalar_xdofs))
            )
            for ip in range(len(points))
            for i in range(gdim)
        ]

        # FIXME: This code assumes an affine coordinate field.
        #        To get around that limitation, make this function take another argument
        #            const ufc::coordinate_mapping * cm
        #        and generate code like this:
        """
        index_type X[tdim*num_dofs];
        tabulate_dof_coordinates(X);
        cm->compute_physical_coordinates(x, X, coordinate_dofs);
        """

        return code
Ejemplo n.º 13
0
    def interpolate_vertex_values(self, L, ir, parameters):
        irdata = ir["interpolate_vertex_values"]

        # Raise error if interpolate_vertex_values is ill-defined
        if not irdata:
            msg = "interpolate_vertex_values is not defined for this element"
            return [generate_error(L, msg, parameters["convert_exceptions_to_warnings"])]

        # Handle unsupported elements.
        if isinstance(irdata, str):
            msg = "interpolate_vertex_values: %s" % irdata
            return [generate_error(L, msg, parameters["convert_exceptions_to_warnings"])]

        # Add code for Jacobian if necessary
        code = []
        gdim = irdata["geometric_dimension"]
        tdim = irdata["topological_dimension"]
        cell_shape = ir["cell_shape"]
        if irdata["needs_jacobian"]:
            code += jacobian(L, gdim, tdim, cell_shape)
            code += inverse_jacobian(L, gdim, tdim, cell_shape)
            if irdata["needs_oriented"] and tdim != gdim:
                code += orientation(L)

        # Compute total value dimension for (mixed) element
        total_dim = irdata["physical_value_size"]

        # Generate code for each element
        value_offset = 0
        space_offset = 0
        for data in irdata["element_data"]:
            # Add vertex interpolation for this element
            code += [L.Comment("Evaluate function and change variables")]

            # Extract vertex values for all basis functions
            vertex_values = data["basis_values"]
            value_size = data["physical_value_size"]
            space_dim = data["space_dim"]
            mapping = data["mapping"]

            J = L.Symbol("J")
            J = L.FlattenedArray(J, dims=(gdim, tdim))
            detJ = L.Symbol("detJ")
            K = L.Symbol("K")
            K = L.FlattenedArray(K, dims=(tdim, gdim))

            # Create code for each value dimension:
            for k in range(value_size):
                # Create code for each vertex x_j
                for (j, values_at_vertex) in enumerate(vertex_values):

                    if value_size == 1:
                        values_at_vertex = [values_at_vertex]

                    values = clamp_table_small_numbers(values_at_vertex)

                    # Map basis functions using appropriate mapping
                    # FIXME: sort out all non-affine mappings and make into a function
                    # components = change_of_variables(values_at_vertex, k)

                    w = []
                    if mapping == 'affine':
                        w = values[k]
                    elif mapping == 'contravariant piola':
                        for index in range(space_dim):
                            w += [sum(J[k, p]*values[p][index]
                                      for p in range(tdim))/detJ]
                    elif mapping == 'covariant piola':
                        for index in range(space_dim):
                            w += [sum(K[p, k]*values[p][index]
                                      for p in range(tdim))]
                    elif mapping == 'double covariant piola':
                        for index in range(space_dim):
                            w += [sum(K[p, k//tdim]*values[p][q][index]*K[q, k % tdim]
                                      for q in range(tdim) for p in range(tdim))]
                    elif mapping == 'double contravariant piola':
                        for index in range(space_dim):
                            w += [sum(J[k//tdim, p]*values[p][q][index]*J[k % tdim, q]
                                      for q in range(tdim) for p in range(tdim))/(detJ*detJ)]
                    else:
                        error("Unknown mapping: %s" % mapping)

                    # Contract coefficients and basis functions
                    dof_values = L.Symbol("dof_values")
                    dof_list = [dof_values[i + space_offset] for i in range(space_dim)]
                    value = sum(p*q for (p, q) in zip(dof_list, w))

                    # Assign value to correct vertex
                    index = j * total_dim + (k + value_offset)
                    v_values = L.Symbol("vertex_values")
                    code += [L.Assign(v_values[index], value)]

            # Update offsets for value- and space dimension
            value_offset += data["physical_value_size"]
            space_offset += data["space_dim"]

        return code
Ejemplo n.º 14
0
def generate_evaluate_basis_derivatives_all(L, data):
    """Like evaluate_basis, but return the values of all basis
    functions (dofs)."""

    if isinstance(data, str):
        msg = "evaluate_basis_derivatives_all: %s" % data
        return [generate_error(L, msg, True)]

    # Initialise return code
    code = []

    # FIXME: KBO: Figure out which return format to use, either:
    # [dN0[0]/dx, dN0[0]/dy, dN0[1]/dx, dN0[1]/dy, dN1[0]/dx,
    # dN1[0]/dy, dN1[1]/dx, dN1[1]/dy, ...]
    # or
    # [dN0[0]/dx, dN1[0]/dx, ..., dN0[1]/dx, dN1[1]/dx, ...,
    # dN0[0]/dy, dN1[0]/dy, ..., dN0[1]/dy, dN1[1]/dy, ...]
    # or
    # [dN0[0]/dx, dN0[1]/dx, ..., dN1[0]/dx, dN1[1]/dx, ...,
    # dN0[0]/dy, dN0[1]/dy, ..., dN1[0]/dy, dN1[1]/dy, ...]
    # for vector (tensor elements), currently returning option 1.

    # FIXME: KBO: For now, just call evaluate_basis_derivatives and
    # map values accordingly, this will keep the amount of code at a
    # minimum. If it turns out that speed is an issue (overhead from
    # calling evaluate_basis), we can easily generate all the code.

    # Get total value shape and space dimension for entire element
    # (possibly mixed).
    physical_value_size = data["physical_value_size"]
    space_dimension = data["space_dimension"]
    max_degree = data["max_degree"]
    gdim = data["geometric_dimension"]
    tdim = data["topological_dimension"]

    n = L.Symbol("n")
    x = L.Symbol("x")
    coordinate_dofs = L.Symbol("coordinate_dofs")
    cell_orientation = L.Symbol("cell_orientation")
    values = L.Symbol("values")

    # Special case where space dimension is one (constant elements).
    if space_dimension == 1:
        code += [L.Comment("Element is constant, calling evaluate_basis_derivatives.")]
        code += [L.Call("evaluate_basis_derivatives", (0, n, values, x, coordinate_dofs, cell_orientation))]
        return code

    # Compute number of derivatives.
    if tdim == gdim:
        num_derivatives = L.Symbol("num_derivatives")
    else:
        num_derivatives = L.Symbol("num_derivatives_g")

    # If n == 0, call evaluate_basis.
    code += [L.Comment("Call evaluate_basis_all if order of derivatives is equal to zero.")]
    code += [L.If(L.EQ(n, 0), [L.Call("evaluate_basis_all", (values, x, coordinate_dofs, cell_orientation)), L.Return()])]

    code += [L.VariableDecl("unsigned int", num_derivatives, L.Call("std::pow", (gdim, n)))]

    num_vals = physical_value_size * num_derivatives

    # Reset values.
    code += [L.Comment("Set values equal to zero.")]
    code += [L.MemZero(values, num_vals*space_dimension)]

    # If n > max_degree, return zeros.
    code += [L.Comment("If order of derivatives is greater than the maximum polynomial degree, return zeros.")]
    code += [L.If(L.GT(n, max_degree), [L.Return()])]

    # Declare helper value to hold single dof values and reset.
    code += [L.Comment("Helper variable to hold values of a single dof.")]
    nds = gdim**max_degree * physical_value_size
    dof_values = L.Symbol("dof_values")
    code += [L.ArrayDecl("double", dof_values, (nds,), 0.0)]

    # Create loop over dofs that calls evaluate_basis_derivatives for
    # a single dof and inserts the values into the global array.
    code += [L.Comment("Loop dofs and call evaluate_basis_derivatives.")]

    values = L.FlattenedArray(values, dims=(space_dimension, num_vals))
    r = L.Symbol("r")
    s = L.Symbol("s")
    loop_s = L.ForRange(s, 0, num_vals, index_type=index_type, body=[L.Assign(values[r, s], dof_values[s])])

    code += [L.ForRange(r, 0, space_dimension, index_type=index_type, body=[L.Call("evaluate_basis_derivatives", (r, n, dof_values, x, coordinate_dofs, cell_orientation)),
                                                                            loop_s])]
    return code
Ejemplo n.º 15
0
    def evaluate_basis(self, L, ir, parameters):
        data = ir["evaluate_basis"]

        # FIXME: does this make sense?
        if not data:
            msg = "evaluate_basis is not defined for this element"
            return generate_error(L, msg, parameters["convert_exceptions_to_warnings"])

        # Handle unsupported elements.
        if isinstance(data, str):
            msg = "evaluate_basis: %s" % data
            return [generate_error(L, msg, parameters["convert_exceptions_to_warnings"])]

        # Get the element cell name and geometric dimension.
        element_cellname = data["cellname"]
        gdim = data["geometric_dimension"]
        tdim = data["topological_dimension"]

        # Generate run time code to evaluate an element basisfunction
        # at an arbitrary point. The value(s) of the basisfunction
        # is/are computed as in FIAT as the dot product of the
        # coefficients (computed at compile time) and basisvalues
        # which are dependent on the coordinate and thus have to be
        # computed at run time.

        # The function should work for all elements supported by FIAT,
        # but it remains untested for tensor valued elements.

        # Get code snippets for Jacobian, Inverse of Jacobian and
        # mapping of coordinates from physical element to the FIAT
        # reference element.

        cm = L.Symbol("cm")
        X = L.Symbol("X")
        code = [L.ArrayDecl("double", X, (tdim), values=0)]

        J = L.Symbol("J")
        code += [L.ArrayDecl("double", J, (gdim*tdim,))]

        detJ = L.Symbol("detJ")
        code += [L.VariableDecl("double", detJ)]

        K = L.Symbol("K")
        code += [L.ArrayDecl("double", K, (gdim*tdim,))]

        x = L.Symbol("x")
        coordinate_dofs = L.Symbol("coordinate_dofs")
        cell_orientation = L.Symbol("cell_orientation")

        no_cm_code = [  L.Call("compute_jacobian_"+element_cellname+"_"+str(gdim)+"d",
                               (J, coordinate_dofs)),
                        L.Call("compute_jacobian_inverse_"+element_cellname+"_"+str(gdim)+"d",
                               (K, detJ, J))]

        if data["needs_oriented"] and tdim != gdim:
            no_cm_code += orientation(L)

        if any((d["embedded_degree"] > 0) for d in data["dofs_data"]):
            k = L.Symbol("k")
            Y = L.Symbol("Y")
            no_cm_code += fiat_coordinate_mapping(L, element_cellname, gdim)
            if element_cellname in ('interval', 'triangle', 'tetrahedron'):
                no_cm_code += [L.Comment("Map to FFC reference coordinate"),
                               L.ForRange(k, 0, tdim, index_type=index_type, body=[L.Assign(X[k], (Y[k] + 1.0)/2.0)])]
            else:
                no_cm_code += [L.ForRange(k, 0, tdim, index_type=index_type, body=[L.Assign(X[k], Y[k])])]

        code += [L.If(cm, L.Call("cm->compute_reference_geometry",
                                (X, J, L.AddressOf(detJ), K, 1, x, coordinate_dofs, cell_orientation))),
                 L.Else(no_cm_code)]

        reference_value_size = data["reference_value_size"]
        num_dofs = len(data["dofs_data"])
        ref_values = L.Symbol("ref_values")
        code += [L.Comment("Evaluate basis on reference element"),
                 L.ArrayDecl("double", ref_values, num_dofs*reference_value_size),
                 L.Call("evaluate_reference_basis",(ref_values, 1, X))]

        physical_value_size = data["physical_value_size"]
        physical_values = L.Symbol("physical_values")
        i = L.Symbol("i")
        k = L.Symbol("k")
        values = L.Symbol("values")
        code += [L.Comment("Push forward"),
                 L.ArrayDecl("double", physical_values, num_dofs*physical_value_size),
                 L.Call("transform_reference_basis_derivatives",(physical_values, 0, 1,
                                                                 ref_values, X, J, L.AddressOf(detJ), K,
                                                                 cell_orientation)),
                 L.ForRange(k, 0, physical_value_size, index_type=index_type, body=[
                     L.Assign(values[k], physical_values[physical_value_size*i + k])
                 ])]

        return code
Ejemplo n.º 16
0
    def transform_reference_basis_derivatives(self, L, ir, parameters):
        data = ir["evaluate_basis"]
        if isinstance(data, str):
            msg = "transform_reference_basis_derivatives: %s" % data
            return generate_error(L, msg,
                                  parameters["convert_exceptions_to_warnings"])

        # Get some known dimensions
        #element_cellname = data["cellname"]
        gdim = data["geometric_dimension"]
        tdim = data["topological_dimension"]
        max_degree = data["max_degree"]
        reference_value_size = data["reference_value_size"]
        physical_value_size = data["physical_value_size"]
        num_dofs = len(data["dofs_data"])

        max_g_d = gdim**max_degree
        max_t_d = tdim**max_degree

        # Output arguments
        values_symbol = L.Symbol("values")

        # Input arguments
        order = L.Symbol("order")
        num_points = L.Symbol(
            "num_points")  # FIXME: Currently assuming 1 point?
        reference_values = L.Symbol("reference_values")
        J = L.Symbol("J")
        detJ = L.Symbol("detJ")
        K = L.Symbol("K")

        # Internal variables
        transform = L.Symbol("transform")

        # Indices, I've tried to use these for a consistent purpose
        ip = L.Symbol("ip")  # point
        i = L.Symbol("i")  # physical component
        j = L.Symbol("j")  # reference component
        k = L.Symbol("k")  # order
        r = L.Symbol("r")  # physical derivative number
        s = L.Symbol("s")  # reference derivative number
        d = L.Symbol("d")  # dof

        combinations_code = []
        if max_degree == 0:
            # Don't need combinations
            num_derivatives_t = 1  # TODO: I think this is the right thing to do to make this still work for order=0?
            num_derivatives_g = 1
        elif tdim == gdim:
            num_derivatives_t = L.Symbol("num_derivatives")
            num_derivatives_g = num_derivatives_t
            combinations_code += [
                L.VariableDecl("const " + index_type, num_derivatives_t,
                               L.Call("std::pow", (tdim, order))),
            ]

            # Add array declarations of combinations
            combinations_code_t, combinations_t = _generate_combinations(
                L, tdim, max_degree, order, num_derivatives_t)
            combinations_code += combinations_code_t
            combinations_g = combinations_t
        else:
            num_derivatives_t = L.Symbol("num_derivatives_t")
            num_derivatives_g = L.Symbol("num_derivatives_g")
            combinations_code += [
                L.VariableDecl("const " + index_type, num_derivatives_t,
                               L.Call("std::pow", (tdim, order))),
                L.VariableDecl("const " + index_type, num_derivatives_g,
                               L.Call("std::pow", (gdim, order))),
            ]
            # Add array declarations of combinations
            combinations_code_t, combinations_t = _generate_combinations(
                L, tdim, max_degree, order, num_derivatives_t, suffix="_t")
            combinations_code_g, combinations_g = _generate_combinations(
                L, gdim, max_degree, order, num_derivatives_g, suffix="_g")
            combinations_code += combinations_code_t
            combinations_code += combinations_code_g

        # Define expected dimensions of argument arrays
        J = L.FlattenedArray(J, dims=(num_points, gdim, tdim))
        detJ = L.FlattenedArray(detJ, dims=(num_points, ))
        K = L.FlattenedArray(K, dims=(num_points, tdim, gdim))

        values = L.FlattenedArray(values_symbol,
                                  dims=(num_points, num_dofs,
                                        num_derivatives_g,
                                        physical_value_size))
        reference_values = L.FlattenedArray(reference_values,
                                            dims=(num_points, num_dofs,
                                                  num_derivatives_t,
                                                  reference_value_size))

        # Generate code to compute the derivative transform matrix
        transform_matrix_code = [
            # Initialize transform matrix to all 1.0
            L.ArrayDecl("double", transform, (max_g_d, max_t_d)),
            L.ForRanges((r, 0, num_derivatives_g), (s, 0, num_derivatives_t),
                        index_type=index_type,
                        body=L.Assign(transform[r, s], 1.0)),
        ]
        if max_degree > 0:
            transform_matrix_code += [
                # Compute transform matrix entries, each a product of K entries
                L.ForRanges((r, 0, num_derivatives_g),
                            (s, 0, num_derivatives_t), (k, 0, order),
                            index_type=index_type,
                            body=L.AssignMul(
                                transform[r, s], K[ip, combinations_t[s, k],
                                                   combinations_g[r, k]])),
            ]

        # Initialize values to 0, will be added to inside loops
        values_init_code = [
            L.MemZero(
                values_symbol, num_points * num_dofs * num_derivatives_g *
                physical_value_size),
        ]

        # Make offsets available in generated code
        reference_offsets = L.Symbol("reference_offsets")
        physical_offsets = L.Symbol("physical_offsets")
        dof_attributes_code = [
            L.ArrayDecl("const " + index_type,
                        reference_offsets, (num_dofs, ),
                        values=[
                            dof_data["reference_offset"]
                            for dof_data in data["dofs_data"]
                        ]),
            L.ArrayDecl("const " + index_type,
                        physical_offsets, (num_dofs, ),
                        values=[
                            dof_data["physical_offset"]
                            for dof_data in data["dofs_data"]
                        ]),
        ]

        # Build dof lists for each mapping type
        mapping_dofs = defaultdict(list)
        for idof, dof_data in enumerate(data["dofs_data"]):
            mapping_dofs[dof_data["mapping"]].append(idof)

        # Generate code for each mapping type
        d = L.Symbol("d")
        transform_apply_code = []
        for mapping in sorted(mapping_dofs):
            # Get list of dofs using this mapping
            idofs = mapping_dofs[mapping]

            # Select iteration approach over dofs
            if idofs == list(range(idofs[0], idofs[-1] + 1)):
                # Contiguous
                dofrange = (d, idofs[0], idofs[-1] + 1)
                idof = d
            else:
                # Stored const array of dof indices
                idofs_symbol = L.Symbol("%s_dofs" % mapping.replace(" ", "_"))
                dof_attributes_code += [
                    L.ArrayDecl("const " + index_type,
                                idofs_symbol, (len(idofs), ),
                                values=idofs),
                ]
                dofrange = (d, 0, len(idofs))
                idof = idofs_symbol[d]

            # NB! Array access to offsets, these are not Python integers
            reference_offset = reference_offsets[idof]
            physical_offset = physical_offsets[idof]

            # How many components does each basis function with this mapping have?
            # This should be uniform, i.e. there should be only one element in this set:
            num_reference_components, = set(
                data["dofs_data"][i]["num_components"] for i in idofs)

            M_scale, M_row, num_physical_components = generate_element_mapping(
                mapping, i, num_reference_components, tdim, gdim, J[ip],
                detJ[ip], K[ip])

            #            transform_apply_body = [
            #                L.AssignAdd(values[ip, idof, r, physical_offset + k],
            #                            transform[r, s] * reference_values[ip, idof, s, reference_offset + k])
            #                for k in range(num_physical_components)
            #            ]

            msg = "Using %s transform to map values back to the physical element." % mapping.replace(
                "piola", "Piola")

            mapped_value = L.Symbol("mapped_value")
            transform_apply_code += [
                L.ForRanges(
                    dofrange,
                    (s, 0, num_derivatives_t),
                    (i, 0, num_physical_components),
                    index_type=index_type,
                    body=[
                        # Unrolled application of mapping to one physical component,
                        # for affine this automatically reduces to
                        #   mapped_value = reference_values[..., reference_offset]
                        L.Comment(msg),
                        L.VariableDecl(
                            "const double", mapped_value,
                            M_scale *
                            sum(M_row[jj] *
                                reference_values[ip, idof, s,
                                                 reference_offset + jj]
                                for jj in range(num_reference_components))),
                        # Apply derivative transformation, for order=0 this reduces to
                        # values[ip,idof,0,physical_offset+i] = transform[0,0]*mapped_value
                        L.Comment(
                            "Mapping derivatives back to the physical element"
                        ),
                        L.ForRanges((r, 0, num_derivatives_g),
                                    index_type=index_type,
                                    body=[
                                        L.AssignAdd(
                                            values[ip, idof, r,
                                                   physical_offset + i],
                                            transform[r, s] * mapped_value)
                                    ])
                    ])
            ]

        # Transform for each point
        point_loop_code = [
            L.ForRange(ip,
                       0,
                       num_points,
                       index_type=index_type,
                       body=(transform_matrix_code + transform_apply_code))
        ]

        # Join code
        code = (combinations_code + values_init_code + dof_attributes_code +
                point_loop_code)
        return code
Ejemplo n.º 17
0
def generate_evaluate_basis_derivatives(L, data):
    """Evaluate the derivatives of an element basisfunction at a
    point. The values are computed as in FIAT as the matrix product of
    the coefficients (computed at compile time), basisvalues which are
    dependent on the coordinate and thus have to be computed at run
    time and combinations (depending on the order of derivative) of
    dmats tables which hold the derivatives of the expansion
    coefficients.

    """

    if isinstance(data, str):
        msg = "evaluate_basis_derivatives: %s" % data
        return [generate_error(L, msg, True)]

    # Initialise return code.
    code = []

    # Get the element cell domain, geometric and topological dimension.
    element_cellname = data["cellname"]
    gdim = data["geometric_dimension"]
    tdim = data["topological_dimension"]
    max_degree = data["max_degree"]
    physical_value_size = data["physical_value_size"]

    # Compute number of derivatives that has to be computed, and
    # declare an array to hold the values of the derivatives on the
    # reference element.
    values = L.Symbol("values")
    n = L.Symbol("n")
    dofs = L.Symbol("dofs")
    x = L.Symbol("x")
    coordinate_dofs = L.Symbol("coordinate_dofs")
    cell_orientation = L.Symbol("cell_orientation")

    if tdim == gdim:
        _t, _g = ("", "")
        num_derivatives_t = L.Symbol("num_derivatives")
        num_derivatives_g = L.Symbol("num_derivatives")
        code += [
            L.VariableDecl("std::size_t", num_derivatives_t,
                           L.Call("std::pow", (tdim, n)))
        ]
    else:
        _t, _g = ("_t", "_g")
        num_derivatives_t = L.Symbol("num_derivatives_t")
        num_derivatives_g = L.Symbol("num_derivatives_g")
        if max_degree > 0:
            code += [
                L.VariableDecl("std::size_t", num_derivatives_t,
                               L.Call("std::pow", (tdim, n)))
            ]
        code += [
            L.VariableDecl("std::size_t", num_derivatives_g,
                           L.Call("std::pow", (gdim, n)))
        ]

    # Reset all values.
    code += [L.MemZero(values, physical_value_size * num_derivatives_g)]

    # Handle values of argument 'n'.
    code += [
        L.Comment(
            "Call evaluate_basis_all if order of derivatives is equal to zero."
        )
    ]
    code += [
        L.If(L.EQ(n, 0), [
            L.Call(
                "evaluate_basis",
                (L.Symbol("i"), values, x, coordinate_dofs, cell_orientation)),
            L.Return()
        ])
    ]

    # If max_degree is zero, return code (to avoid declarations such as
    # combinations[1][0]) and because there's nothing to compute.)
    if max_degree == 0:
        return code

    code += [
        L.Comment(
            "If order of derivatives is greater than the maximum polynomial degree, return zeros."
        )
    ]
    code += [L.If(L.GT(n, max_degree), [L.Return()])]

    # Generate geo code.
    code += jacobian(L, gdim, tdim, element_cellname)
    code += inverse_jacobian(L, gdim, tdim, element_cellname)
    if data["needs_oriented"] and tdim != gdim:
        code += orientation(L)

    code += fiat_coordinate_mapping(L, element_cellname, gdim)

    # Generate all possible combinations of derivatives.
    combinations_code_t, combinations_t = _generate_combinations(
        L, tdim, max_degree, n, num_derivatives_t, _t)
    code += combinations_code_t
    if tdim != gdim:
        combinations_code_g, combinations_g = _generate_combinations(
            L, gdim, max_degree, n, num_derivatives_g, "_g")
        code += combinations_code_g

    # Generate the transformation matrix.
    code += _generate_transform(L, element_cellname, gdim, tdim, max_degree)

    # Create code for all basis values (dofs).
    dof_cases = []
    for i, dof_data in enumerate(data["dofs_data"]):
        dof_cases.append((i, _generate_dof_code(L, data, dof_data)))
    code += [L.Switch(L.Symbol("i"), dof_cases)]
    return code
Ejemplo n.º 18
0
    def tabulate_dof_coordinates(self, L, ir, parameters):
        ir = ir["tabulate_dof_coordinates"]

        # Raise error if tabulate_dof_coordinates is ill-defined
        if not ir:
            msg = "tabulate_dof_coordinates is not defined for this element"
            return generate_error(L, msg,
                                  parameters["convert_exceptions_to_warnings"])

        # Extract coordinates and cell dimension
        gdim = ir["gdim"]
        tdim = ir["tdim"]
        points = ir["points"]

        # Extract cellshape
        cell_shape = ir["cell_shape"]

        # Output argument
        dof_coordinates = L.FlattenedArray(L.Symbol("dof_coordinates"),
                                           dims=(len(points), gdim))

        # Input argument
        coordinate_dofs = L.Symbol("coordinate_dofs")

        # Loop indices
        i = L.Symbol("i")
        k = L.Symbol("k")
        ip = L.Symbol("ip")

        # Basis symbol
        phi = L.Symbol("phi")

        # TODO: Get rid of all places that use reference_to_physical_map, it is restricted to a basis of degree 1
        # Create code for evaluating coordinate mapping
        num_scalar_xdofs = _num_vertices(cell_shape)
        cg1_basis = reference_to_physical_map(cell_shape)
        phi_values = numpy.asarray(
            [phi_comp for X in points for phi_comp in cg1_basis(X)])
        assert len(phi_values) == len(points) * num_scalar_xdofs

        # TODO: Use precision parameter here
        phi_values = clamp_table_small_numbers(phi_values)

        code = [
            L.Assign(
                dof_coordinates[ip][i],
                sum(phi_values[ip * num_scalar_xdofs + k] *
                    coordinate_dofs[gdim * k + i]
                    for k in range(num_scalar_xdofs)))
            for ip in range(len(points)) for i in range(gdim)
        ]

        # FIXME: This code assumes an affine coordinate field.
        #        To get around that limitation, make this function take another argument
        #            const ufc::coordinate_mapping * cm
        #        and generate code like this:
        """
        index_type X[tdim*num_dofs];
        tabulate_dof_coordinates(X);
        cm->compute_physical_coordinates(x, X, coordinate_dofs);
        """

        return code
Ejemplo n.º 19
0
    def interpolate_vertex_values(self, L, ir, parameters):
        irdata = ir["interpolate_vertex_values"]

        # Raise error if interpolate_vertex_values is ill-defined
        if not irdata:
            msg = "interpolate_vertex_values is not defined for this element"
            return [
                generate_error(L, msg,
                               parameters["convert_exceptions_to_warnings"])
            ]

        # Handle unsupported elements.
        if isinstance(irdata, str):
            msg = "interpolate_vertex_values: %s" % irdata
            return [
                generate_error(L, msg,
                               parameters["convert_exceptions_to_warnings"])
            ]

        # Add code for Jacobian if necessary
        code = []
        gdim = irdata["geometric_dimension"]
        tdim = irdata["topological_dimension"]
        cell_shape = ir["cell_shape"]
        if irdata["needs_jacobian"]:
            code += jacobian(L, gdim, tdim, cell_shape)
            code += inverse_jacobian(L, gdim, tdim, cell_shape)
            if irdata["needs_oriented"] and tdim != gdim:
                code += orientation(L)

        # Compute total value dimension for (mixed) element
        total_dim = irdata["physical_value_size"]

        # Generate code for each element
        value_offset = 0
        space_offset = 0
        for data in irdata["element_data"]:
            # Add vertex interpolation for this element
            code += [L.Comment("Evaluate function and change variables")]

            # Extract vertex values for all basis functions
            vertex_values = data["basis_values"]
            value_size = data["physical_value_size"]
            space_dim = data["space_dim"]
            mapping = data["mapping"]

            J = L.Symbol("J")
            J = L.FlattenedArray(J, dims=(gdim, tdim))
            detJ = L.Symbol("detJ")
            K = L.Symbol("K")
            K = L.FlattenedArray(K, dims=(tdim, gdim))

            # Create code for each value dimension:
            for k in range(value_size):
                # Create code for each vertex x_j
                for (j, values_at_vertex) in enumerate(vertex_values):

                    if value_size == 1:
                        values_at_vertex = [values_at_vertex]

                    values = clamp_table_small_numbers(values_at_vertex)

                    # Map basis functions using appropriate mapping
                    # FIXME: sort out all non-affine mappings and make into a function
                    # components = change_of_variables(values_at_vertex, k)

                    w = []
                    if mapping == 'affine':
                        w = values[k]
                    elif mapping == 'contravariant piola':
                        for index in range(space_dim):
                            w += [
                                sum(J[k, p] * values[p][index]
                                    for p in range(tdim)) / detJ
                            ]
                    elif mapping == 'covariant piola':
                        for index in range(space_dim):
                            w += [
                                sum(K[p, k] * values[p][index]
                                    for p in range(tdim))
                            ]
                    elif mapping == 'double covariant piola':
                        for index in range(space_dim):
                            w += [
                                sum(K[p, k // tdim] * values[p][q][index] *
                                    K[q, k % tdim] for q in range(tdim)
                                    for p in range(tdim))
                            ]
                    elif mapping == 'double contravariant piola':
                        for index in range(space_dim):
                            w += [
                                sum(J[k // tdim, p] * values[p][q][index] *
                                    J[k % tdim, q] for q in range(tdim)
                                    for p in range(tdim)) / (detJ * detJ)
                            ]
                    else:
                        error("Unknown mapping: %s" % mapping)

                    # Contract coefficients and basis functions
                    dof_values = L.Symbol("dof_values")
                    dof_list = [
                        dof_values[i + space_offset] for i in range(space_dim)
                    ]
                    value = sum(p * q for (p, q) in zip(dof_list, w))

                    # Assign value to correct vertex
                    index = j * total_dim + (k + value_offset)
                    v_values = L.Symbol("vertex_values")
                    code += [L.Assign(v_values[index], value)]

            # Update offsets for value- and space dimension
            value_offset += data["physical_value_size"]
            space_offset += data["space_dim"]

        return code
Ejemplo n.º 20
0
    def evaluate_basis_all(self, L, ir, parameters):
        data = ir["evaluate_basis"]

        # Handle unsupported elements.
        if isinstance(data, str):
            msg = "evaluate_basis_all: %s" % data
            return [
                generate_error(L, msg,
                               parameters["convert_exceptions_to_warnings"])
            ]

        physical_value_size = data["physical_value_size"]
        space_dimension = data["space_dimension"]

        x = L.Symbol("x")
        coordinate_dofs = L.Symbol("coordinate_dofs")
        cell_orientation = L.Symbol("cell_orientation")
        values = L.Symbol("values")

        # Special case where space dimension is one (constant elements).
        if space_dimension == 1:
            code = [
                L.Comment("Element is constant, calling evaluate_basis."),
                L.Call("evaluate_basis",
                       (0, values, x, coordinate_dofs, cell_orientation))
            ]
            return code

        r = L.Symbol("r")
        dof_values = L.Symbol("dof_values")
        if physical_value_size == 1:
            code = [
                L.Comment("Helper variable to hold value of a single dof."),
                L.VariableDecl("double", dof_values, 0.0),
                L.Comment("Loop dofs and call evaluate_basis"),
                L.ForRange(r,
                           0,
                           space_dimension,
                           index_type=index_type,
                           body=[
                               L.Call("evaluate_basis",
                                      (r, L.AddressOf(dof_values), x,
                                       coordinate_dofs, cell_orientation)),
                               L.Assign(values[r], dof_values)
                           ])
            ]
        else:
            s = L.Symbol("s")
            code = [
                L.Comment("Helper variable to hold values of a single dof."),
                L.ArrayDecl("double", dof_values, physical_value_size, 0.0),
                L.Comment("Loop dofs and call evaluate_basis"),
                L.ForRange(r,
                           0,
                           space_dimension,
                           index_type=index_type,
                           body=[
                               L.Call("evaluate_basis",
                                      (r, dof_values, x, coordinate_dofs,
                                       cell_orientation)),
                               L.ForRange(
                                   s,
                                   0,
                                   physical_value_size,
                                   index_type=index_type,
                                   body=[
                                       L.Assign(
                                           values[r * physical_value_size + s],
                                           dof_values[s])
                                   ])
                           ])
            ]

        return code
Ejemplo n.º 21
0
    def evaluate_basis(self, L, ir, parameters):
        data = ir["evaluate_basis"]

        # FIXME: does this make sense?
        if not data:
            msg = "evaluate_basis is not defined for this element"
            return generate_error(L, msg,
                                  parameters["convert_exceptions_to_warnings"])

        # Handle unsupported elements.
        if isinstance(data, str):
            msg = "evaluate_basis: %s" % data
            return [
                generate_error(L, msg,
                               parameters["convert_exceptions_to_warnings"])
            ]

        # Get the element cell name and geometric dimension.
        element_cellname = data["cellname"]
        gdim = data["geometric_dimension"]
        tdim = data["topological_dimension"]

        # Generate run time code to evaluate an element basisfunction
        # at an arbitrary point. The value(s) of the basisfunction
        # is/are computed as in FIAT as the dot product of the
        # coefficients (computed at compile time) and basisvalues
        # which are dependent on the coordinate and thus have to be
        # computed at run time.

        # The function should work for all elements supported by FIAT,
        # but it remains untested for tensor valued elements.

        # Get code snippets for Jacobian, Inverse of Jacobian and
        # mapping of coordinates from physical element to the FIAT
        # reference element.

        cm = L.Symbol("cm")
        X = L.Symbol("X")
        code = [L.ArrayDecl("double", X, (tdim), values=0)]

        J = L.Symbol("J")
        code += [L.ArrayDecl("double", J, (gdim * tdim, ))]

        detJ = L.Symbol("detJ")
        code += [L.VariableDecl("double", detJ)]

        K = L.Symbol("K")
        code += [L.ArrayDecl("double", K, (gdim * tdim, ))]

        x = L.Symbol("x")
        coordinate_dofs = L.Symbol("coordinate_dofs")
        cell_orientation = L.Symbol("cell_orientation")

        no_cm_code = [
            L.Call(
                "compute_jacobian_" + element_cellname + "_" + str(gdim) + "d",
                (J, coordinate_dofs)),
            L.Call(
                "compute_jacobian_inverse_" + element_cellname + "_" +
                str(gdim) + "d", (K, detJ, J))
        ]

        if data["needs_oriented"] and tdim != gdim:
            no_cm_code += orientation(L)

        if any((d["embedded_degree"] > 0) for d in data["dofs_data"]):
            k = L.Symbol("k")
            Y = L.Symbol("Y")
            no_cm_code += fiat_coordinate_mapping(L, element_cellname, gdim)
            if element_cellname in ('interval', 'triangle', 'tetrahedron'):
                no_cm_code += [
                    L.Comment("Map to FFC reference coordinate"),
                    L.ForRange(k,
                               0,
                               tdim,
                               index_type=index_type,
                               body=[L.Assign(X[k], (Y[k] + 1.0) / 2.0)])
                ]
            else:
                no_cm_code += [
                    L.ForRange(k,
                               0,
                               tdim,
                               index_type=index_type,
                               body=[L.Assign(X[k], Y[k])])
                ]

        code += [
            L.If(
                cm,
                L.Call("cm->compute_reference_geometry",
                       (X, J, L.AddressOf(detJ), K, 1, x, coordinate_dofs,
                        cell_orientation))),
            L.Else(no_cm_code)
        ]

        reference_value_size = data["reference_value_size"]
        num_dofs = len(data["dofs_data"])
        ref_values = L.Symbol("ref_values")
        code += [
            L.Comment("Evaluate basis on reference element"),
            L.ArrayDecl("double", ref_values, num_dofs * reference_value_size),
            L.Call("evaluate_reference_basis", (ref_values, 1, X))
        ]

        physical_value_size = data["physical_value_size"]
        physical_values = L.Symbol("physical_values")
        i = L.Symbol("i")
        k = L.Symbol("k")
        values = L.Symbol("values")
        code += [
            L.Comment("Push forward"),
            L.ArrayDecl("double", physical_values,
                        num_dofs * physical_value_size),
            L.Call("transform_reference_basis_derivatives",
                   (physical_values, 0, 1, ref_values, X, J, L.AddressOf(detJ),
                    K, cell_orientation)),
            L.ForRange(k,
                       0,
                       physical_value_size,
                       index_type=index_type,
                       body=[
                           L.Assign(
                               values[k],
                               physical_values[physical_value_size * i + k])
                       ])
        ]

        return code
Ejemplo n.º 22
0
def generate_evaluate_basis_derivatives_all(L, data):
    """Like evaluate_basis, but return the values of all basis
    functions (dofs)."""

    if isinstance(data, str):
        msg = "evaluate_basis_derivatives_all: %s" % data
        return [generate_error(L, msg, True)]

    # Initialise return code
    code = []

    # FIXME: KBO: Figure out which return format to use, either:
    # [dN0[0]/dx, dN0[0]/dy, dN0[1]/dx, dN0[1]/dy, dN1[0]/dx,
    # dN1[0]/dy, dN1[1]/dx, dN1[1]/dy, ...]
    # or
    # [dN0[0]/dx, dN1[0]/dx, ..., dN0[1]/dx, dN1[1]/dx, ...,
    # dN0[0]/dy, dN1[0]/dy, ..., dN0[1]/dy, dN1[1]/dy, ...]
    # or
    # [dN0[0]/dx, dN0[1]/dx, ..., dN1[0]/dx, dN1[1]/dx, ...,
    # dN0[0]/dy, dN0[1]/dy, ..., dN1[0]/dy, dN1[1]/dy, ...]
    # for vector (tensor elements), currently returning option 1.

    # FIXME: KBO: For now, just call evaluate_basis_derivatives and
    # map values accordingly, this will keep the amount of code at a
    # minimum. If it turns out that speed is an issue (overhead from
    # calling evaluate_basis), we can easily generate all the code.

    # Get total value shape and space dimension for entire element
    # (possibly mixed).
    physical_value_size = data["physical_value_size"]
    space_dimension = data["space_dimension"]
    max_degree = data["max_degree"]
    gdim = data["geometric_dimension"]
    tdim = data["topological_dimension"]

    n = L.Symbol("n")
    x = L.Symbol("x")
    coordinate_dofs = L.Symbol("coordinate_dofs")
    cell_orientation = L.Symbol("cell_orientation")
    values = L.Symbol("values")

    # Special case where space dimension is one (constant elements).
    if space_dimension == 1:
        code += [
            L.Comment(
                "Element is constant, calling evaluate_basis_derivatives.")
        ]
        code += [
            L.Call("evaluate_basis_derivatives",
                   (0, n, values, x, coordinate_dofs, cell_orientation))
        ]
        return code

    # Compute number of derivatives.
    if tdim == gdim:
        num_derivatives = L.Symbol("num_derivatives")
    else:
        num_derivatives = L.Symbol("num_derivatives_g")

    # If n == 0, call evaluate_basis.
    code += [
        L.Comment(
            "Call evaluate_basis_all if order of derivatives is equal to zero."
        )
    ]
    code += [
        L.If(L.EQ(n, 0), [
            L.Call("evaluate_basis_all",
                   (values, x, coordinate_dofs, cell_orientation)),
            L.Return()
        ])
    ]

    code += [
        L.VariableDecl("unsigned int", num_derivatives,
                       L.Call("std::pow", (gdim, n)))
    ]

    num_vals = physical_value_size * num_derivatives

    # Reset values.
    code += [L.Comment("Set values equal to zero.")]
    code += [L.MemZero(values, num_vals * space_dimension)]

    # If n > max_degree, return zeros.
    code += [
        L.Comment(
            "If order of derivatives is greater than the maximum polynomial degree, return zeros."
        )
    ]
    code += [L.If(L.GT(n, max_degree), [L.Return()])]

    # Declare helper value to hold single dof values and reset.
    code += [L.Comment("Helper variable to hold values of a single dof.")]
    nds = gdim**max_degree * physical_value_size
    dof_values = L.Symbol("dof_values")
    code += [L.ArrayDecl("double", dof_values, (nds, ), 0.0)]

    # Create loop over dofs that calls evaluate_basis_derivatives for
    # a single dof and inserts the values into the global array.
    code += [L.Comment("Loop dofs and call evaluate_basis_derivatives.")]

    values = L.FlattenedArray(values, dims=(space_dimension, num_vals))
    r = L.Symbol("r")
    s = L.Symbol("s")
    loop_s = L.ForRange(s,
                        0,
                        num_vals,
                        index_type=index_type,
                        body=[L.Assign(values[r, s], dof_values[s])])

    code += [
        L.ForRange(r,
                   0,
                   space_dimension,
                   index_type=index_type,
                   body=[
                       L.Call("evaluate_basis_derivatives",
                              (r, n, dof_values, x, coordinate_dofs,
                               cell_orientation)), loop_s
                   ])
    ]
    return code