def conditional(self, o, *operands):
        # Get condition and return values; and do safety check.
        cond, true, false = operands
        ffc_assert(len(cond) == 1 and firstkey(cond) == (),
                   "Condtion should only be one function: " + repr(cond))
        ffc_assert(len(true) == 1 and firstkey(true) == (),
                   "True value of Condtional should only be one function: " + repr(true))
        ffc_assert(len(false) == 1 and firstkey(false) == (),
                   "False value of Condtional should only be one function: " + repr(false))

        # Get values and test for None
        t_val = true[()]
        f_val = false[()]

        # Get the minimum type and number of operations
        # TODO: conditionals are currently always located inside the
        # ip loop, therefore the type has to be at least IP (fix bug
        # #1082048). This can be optimised.
        t = min([cond[()].t, t_val.t, f_val.t, IP])
        ops = sum([cond[()].ops(), t_val.ops(), f_val.ops()])

        # Create expression for conditional
        # TODO: Handle this differently to expose the variables which
        # are used to create the expressions.
        expr = create_symbol(format["evaluate conditional"](cond[()], t_val,
                                                            f_val), t)
        num = len(self.conditionals)
        name = create_symbol(format["conditional"](num), t)
        if expr not in self.conditionals:
            self.conditionals[expr] = (t, ops, num)
        else:
            num = self.conditionals[expr][2]
            name = create_symbol(format["conditional"](num), t)
        return {(): name}
    def conditional(self, o, *operands):
        # Get condition and return values; and do safety check.
        cond, true, false = operands
        ffc_assert(len(cond) == 1 and firstkey(cond) == (),\
            "Condtion should only be one function: " + repr(cond))
        ffc_assert(len(true) == 1 and firstkey(true) == (),\
            "True value of Condtional should only be one function: " + repr(true))
        ffc_assert(len(false) == 1 and firstkey(false) == (),\
            "False value of Condtional should only be one function: " + repr(false))

        # Get values and test for None
        t_val = true[()]
        f_val = false[()]

        # Get the minimum type and number of operations
        # TODO: conditionals are currently always located inside the ip loop,
        # therefore the type has to be at least IP (fix bug #1082048). This can
        # be optimised.
        t = min([cond[()].t, t_val.t, f_val.t, IP])
        ops = sum([cond[()].ops(), t_val.ops(), f_val.ops()])

        # Create expression for conditional
        # TODO: Handle this differently to expose the variables which are used
        # to create the expressions.
        expr = create_symbol(
            format["evaluate conditional"](cond[()], t_val, f_val), t)
        num = len(self.conditionals)
        name = create_symbol(format["conditional"](num), t)
        if not expr in self.conditionals:
            self.conditionals[expr] = (t, ops, num)
        else:
            num = self.conditionals[expr][2]
            name = create_symbol(format["conditional"](num), t)
        return {(): name}
    def power(self, o):
        # Get base and exponent.
        base, expo = o.ufl_operands

        # Visit base to get base code.
        base_code = self.visit(base)

        # TODO: Are these safety checks needed?
        ffc_assert(() in base_code and len(base_code) == 1,
                   "Only support function type base: " + repr(base_code))

        # Get the base code and create power.
        val = base_code[()]

        # Handle different exponents
        if isinstance(expo, IntValue):
            return {(): create_product([val] * expo.value())}
        elif isinstance(expo, FloatValue):
            exp = format["floating point"](expo.value())
            sym = create_symbol(format["std power"](str(val), exp), val.t,
                                val, 1)
            return {(): sym}
        elif isinstance(expo, (Coefficient, Operator)):
            exp = self.visit(expo)[()]
            sym = create_symbol(format["std power"](str(val), exp), val.t,
                                val, 1)
            return {(): sym}
        else:
            error("power does not support this exponent: " + repr(expo))
    def power(self, o):
        # Get base and exponent.
        base, expo = o.ufl_operands

        # Visit base to get base code.
        base_code = self.visit(base)

        # TODO: Are these safety checks needed?
        ffc_assert(() in base_code and len(base_code) == 1,
                   "Only support function type base: " + repr(base_code))

        # Get the base code and create power.
        val = base_code[()]

        # Handle different exponents
        if isinstance(expo, IntValue):
            return {(): create_product([val] * expo.value())}
        elif isinstance(expo, FloatValue):
            exp = format["floating point"](expo.value())
            sym = create_symbol(format["std power"](str(val), exp), val.t, val,
                                1)
            return {(): sym}
        elif isinstance(expo, (Coefficient, Operator)):
            exp = self.visit(expo)[()]
            sym = create_symbol(format["std power"](str(val), exp), val.t, val,
                                1)
            return {(): sym}
        else:
            error("power does not support this exponent: " + repr(expo))
    def _bessel_function(self, operands, format_function):
        # TODO: Are these safety checks needed?
        # TODO: work on reference instead of copies? (like
        # math_function)
        ffc_assert(
            len(operands) == 2,
            "BesselFunctions expect two operands of function type: " +
            repr(operands))
        nu, x = operands
        ffc_assert(
            len(nu) == 1 and () in nu,
            "Expecting one operand of function type as first argument to BesselFunction : "
            + repr(nu))
        ffc_assert(
            len(x) == 1 and () in x,
            "Expecting one operand of function type as second argument to BesselFunction : "
            + repr(x))
        nu = nu[()]
        x = x[()]
        if nu is None:
            nu = format["floating point"](0.0)
        if x is None:
            x = format["floating point"](0.0)

        sym = create_symbol(format_function(x, nu), x.t, x, 1)
        return {(): sym}
    def _create_entry_data(self, val, integral_type):
        # Multiply value by weight and determinant
        ACCESS = GEO
        weight = format["weight"](self.points)
        if self.points > 1:
            weight += format["component"]("", format["integration points"])
            ACCESS = IP
        weight = self._create_symbol(weight, ACCESS)[()]

        # Create value.
        if integral_type in (point_integral_types + custom_integral_types):
            trans_set = set()
            value = create_product([val, weight])
        else:
            f_scale_factor = format["scale factor"]
            trans_set = set([f_scale_factor])
            value = create_product(
                [val, weight, create_symbol(f_scale_factor, GEO)])

        # Update sets of used variables (if they will not be used
        # because of optimisations later, they will be reset).
        trans_set.update([str(x) for x in value.get_unique_vars(GEO)])
        used_points = set([self.points])
        ops = self._count_operations(value)
        used_psi_tables = set(
            [self.psi_tables_map[b] for b in value.get_unique_vars(BASIS)])

        return (value, ops, [trans_set, used_points, used_psi_tables])
 def _create_symbol(self, symbol, domain, _loop_index=[], _iden=None):
     return {
         (): create_symbol(symbol,
                           domain,
                           loop_index=_loop_index,
                           iden=_iden)
     }
    def _create_entry_data(self, val, integral_type):
        # Multiply value by weight and determinant
        ACCESS = GEO
        weight = format["weight"](self.points)
        if self.points > 1:
            weight += format["component"]("", format["integration points"])
            ACCESS = IP
        weight = self._create_symbol(weight, ACCESS)[()]

        # Create value.
        if integral_type in (point_integral_types + custom_integral_types):
            trans_set = set()
            value = create_product([val, weight])
        else:
            f_scale_factor = format["scale factor"]
            trans_set = set([f_scale_factor])
            value = create_product([val, weight,
                                    create_symbol(f_scale_factor, GEO)])

        # Update sets of used variables (if they will not be used
        # because of optimisations later, they will be reset).
        trans_set.update([str(x) for x in value.get_unique_vars(GEO)])
        used_points = set([self.points])
        ops = self._count_operations(value)
        used_psi_tables = set([self.psi_tables_map[b]
                               for b in value.get_unique_vars(BASIS)])

        return (value, ops, [trans_set, used_points, used_psi_tables])
    def binary_condition(self, o, *operands):

        # Get LHS and RHS expressions and do safety checks.  Might be
        # a bit too strict?
        lhs, rhs = operands
        ffc_assert(
            len(lhs) == 1 and firstkey(lhs) == (),
            "LHS of Condtion should only be one function: " + repr(lhs))
        ffc_assert(
            len(rhs) == 1 and firstkey(rhs) == (),
            "RHS of Condtion should only be one function: " + repr(rhs))

        # Map names from UFL to cpp.py.
        name_map = {
            "==": "is equal",
            "!=": "not equal",
            "<": "less than",
            ">": "greater than",
            "<=": "less equal",
            ">=": "greater equal",
            "&&": "and",
            "||": "or"
        }

        # Get the minimum type
        t = min(lhs[()].t, rhs[()].t)
        ops = lhs[()].ops() + rhs[()].ops() + 1
        cond = str(lhs[()]) + format[name_map[o._name]] + str(rhs[()])
        sym = create_symbol(format["grouping"](cond), t, base_op=ops)
        return {(): sym}
 def max_value(self, o, *operands):
     # Take maximum value of operands.
     val0 = operands[0][()]
     val1 = operands[1][()]
     t = min(val0.t, val1.t)
     # FIXME: I don't know how to implement this the optimized way. Is this right?
     new_val = create_symbol(format["max value"](str(val0), str(val1)), t)
     return {(): new_val}
Example #11
0
def _reduce_expression(expr, symbols, const_dict, f_name, use_expr_type=False):
    if use_expr_type:
        if expr not in const_dict:
            const_dict[expr] = len(const_dict)
        return create_symbol(f_name(const_dict[expr]), expr.t)
    # Only something to be done if we have more than one symbol.
    if len(symbols) > 1:
        sym_type = symbols[0].t
        # Create new symbol.
        if expr._prec == 2:
            new_sym = create_product(symbols)
        elif expr._prec == 3:
            new_sym = create_sum(symbols)
        if new_sym not in const_dict:
            const_dict[new_sym] = len(const_dict)
        s = create_symbol(f_name(const_dict[new_sym]), sym_type)
        return [s]
    return symbols
def _reduce_expression(expr, symbols, const_dict, f_name, use_expr_type=False):
    if use_expr_type:
        if expr not in const_dict:
            const_dict[expr] = len(const_dict)
        return create_symbol(f_name(const_dict[expr]), expr.t)
    # Only something to be done if we have more than one symbol.
    if len(symbols) > 1:
        sym_type = symbols[0].t
        # Create new symbol.
        if expr._prec == 2:
            new_sym = create_product(symbols)
        elif expr._prec == 3:
            new_sym = create_sum(symbols)
        if new_sym not in const_dict:
            const_dict[new_sym] = len(const_dict)
        s = create_symbol(f_name(const_dict[new_sym]), sym_type)
        return [s]
    return symbols
 def max_value(self, o, *operands):
     # Take maximum value of operands.
     val0 = operands[0][()]
     val1 = operands[1][()]
     t = min(val0.t, val1.t)
     # FIXME: I don't know how to implement this the optimized
     # way. Is this right?
     new_val = create_symbol(format["max value"](str(val0), str(val1)), t)
     return {(): new_val}
    def circumradius(self, o,  *operands):
        # Safety check.
        ffc_assert(not operands, "Didn't expect any operands for Circumradius: " + repr(operands))

        # FIXME: KBO: This has to change for higher order elements
        circumradius = format["circumradius"](self.restriction)
        self.trans_set.add(circumradius)

        return {():create_symbol(circumradius, GEO)}
 def not_condition(self, o, *operands):
     # This is a Condition but not a BinaryCondition, and the operand will be another Condition
     # Get condition expression and do safety checks.
     # Might be a bit too strict?
     c, = operands
     ffc_assert(len(c) == 1 and c.keys()[0] == (),\
         "Condition for NotCondition should only be one function: " + repr(c))
     sym = create_symbol(format["not"](str(c[()])), c[()].t, base_op=c[()].ops()+1)
     return {(): sym}
    def facet_area(self, o):
        # FIXME: KBO: This has to change for higher order elements
        # NOTE: Omitting restriction because the area of a facet is
        # the same on both sides.
        # FIXME: Since we use the scale factor, facet area has no
        # meaning for cell integrals. (Need check in FFC or UFL).
        area = format["facet area"]
        self.trans_set.add(area)

        return {(): create_symbol(area, GEO)}
    def max_facet_edge_length(self, o):
        # FIXME: this has no meaning for cell integrals. (Need check in FFC or UFL).

        if self.tdim < 3:
            return self.facet_area(o)

        edgelen = format["max facet edge length"](self.restriction)
        self.trans_set.add(edgelen)

        return {():create_symbol(edgelen, GEO)}
    def facet_area(self, o):
        # FIXME: KBO: This has to change for higher order elements
        # NOTE: Omitting restriction because the area of a facet is
        # the same on both sides.
        # FIXME: Since we use the scale factor, facet area has no
        # meaning for cell integrals. (Need check in FFC or UFL).
        area = format["facet area"]
        self.trans_set.add(area)

        return {(): create_symbol(area, GEO)}
    def cell_volume(self, o):
        # FIXME: KBO: This has to change for higher order elements
        # detJ = format["det(J)"](self.restriction)
        # volume = format["absolute value"](detJ)
        # self.trans_set.add(detJ)

        volume = format["cell volume"](self.restriction)
        self.trans_set.add(volume)

        return {(): create_symbol(volume, GEO)}
    def cell_volume(self, o):
        # FIXME: KBO: This has to change for higher order elements
        # detJ = format["det(J)"](self.restriction)
        # volume = format["absolute value"](detJ)
        # self.trans_set.add(detJ)

        volume = format["cell volume"](self.restriction)
        self.trans_set.add(volume)

        return {(): create_symbol(volume, GEO)}
    def power(self, o):
        #print("\n\nVisiting Power: " + repr(o))

        # Get base and exponent.
        base, expo = o.ufl_operands

        # Visit base to get base code.
        base_code = self.visit(base)

        # TODO: Are these safety checks needed?
        ffc_assert(() in base_code and len(base_code) == 1,
                   "Only support function type base: " + repr(base_code))

        # Get the base code and create power.
        val = base_code[()]

        # Handle different exponents
        if isinstance(expo, IntValue):
            return {(): create_product([val] * expo.value())}
        elif isinstance(expo, FloatValue):
            if self.parameters["format"] == "pyop2":
                exp = create_float(expo.value())
            else:
                exp = format["floating point"](expo.value())
            var = format["std power"][self.parameters["format"]](str(val), exp)
            if self.parameters["format"] == "pyop2":
                sym = create_funcall(var, [val, exp])
            else:
                sym = create_symbol(var, val.t, val, 1, iden=var)
            return {(): sym}
        elif isinstance(expo, (Coefficient, Operator)):
            exp = self.visit(expo)[()]
            #            print "pow exp: ", exp
            #            print "pow val: ", val
            var = format["std power"][self.parameters["format"]](str(val), exp)
            if self.parameters["format"] == "pyop2":
                sym = create_funcall(var, [val, exp])
            else:
                sym = create_symbol(var, val.t, val, 1, iden=var)
            return {(): sym}
        else:
            error("power does not support this exponent: " + repr(expo))
    def abs(self, o, *operands):

        # TODO: Are these safety checks needed?
        ffc_assert(len(operands) == 1 and () in operands[0] and len(operands[0]) == 1,
                   "Abs expects one operand of function type: " + repr(operands))

        # Take absolute value of operand.
        val = operands[0][()]
        new_val = create_symbol(format["absolute value"](str(val)), val.t,
                                val, 1)
        return {(): new_val}
 def _math_function(self, operands, format_function):
     # TODO: Are these safety checks needed?
     ffc_assert(len(operands) == 1 and () in operands[0] and len(operands[0]) == 1,
                "MathFunctions expect one operand of function type: " + repr(operands))
     # Use format function on value of operand.
     operand = operands[0]
     for key, val in list(operand.items()):
         new_val = create_symbol(format_function(str(val)), val.t, val, 1)
         operand[key] = new_val
     # raise Exception("pause")
     return operand
    def abs(self, o, *operands):
        #print("\n\nVisiting Abs: " + repr(o) + "with operands: " + "\n".join(map(repr,operands)))

        # TODO: Are these safety checks needed?
        ffc_assert(len(operands) == 1 and () in operands[0] and len(operands[0]) == 1, \
                   "Abs expects one operand of function type: " + repr(operands))

        # Take absolute value of operand.
        val = operands[0][()]
        new_val = create_symbol(format["absolute value"](str(val)), val.t, val, 1)
        return {():new_val}
    def min_facet_edge_length(self, o):
        # FIXME: this has no meaning for cell integrals. (Need check in FFC or UFL).

        tdim = self.tdim  # FIXME: o.domain().topological_dimension() ???
        if tdim < 3:
            return self.facet_area(o)

        edgelen = format["min facet edge length"](self.restriction)
        self.trans_set.add(edgelen)

        return {(): create_symbol(edgelen, GEO)}
    def __apply_transform(self, function, derivatives, multi, tdim, gdim):
        "Apply transformation (from derivatives) to basis or function."
        f_transform     = format["transform"]

        # Add transformation if needed.
        transforms = []
        for i, direction in enumerate(derivatives):
            ref = multi[i]
            t = f_transform("JINV", ref, direction, tdim, gdim, self.restriction)
            transforms.append(create_symbol(t, GEO))
        transforms.append(function)
        return create_product(transforms)
 def max_value(self, o, *operands):
     # Take maximum value of operands.
     val0 = operands[0][()]
     val1 = operands[1][()]
     t = min(val0.t, val1.t)
     # FIXME: I don't know how to implement this the optimized way. Is this right?
     if self.parameters["format"] == "pyop2":
         new_val = create_funcall("fmax", [val0, val1])
     else:
         new_val = create_symbol(format["max value"](str(val0), str(val1)),
                                 t)
     return {(): new_val}
 def _math_function(self, operands, format_function):
     #print("Calling _math_function() of optimisedquadraturetransformer.")
     # TODO: Are these safety checks needed?
     ffc_assert(len(operands) == 1 and () in operands[0] and len(operands[0]) == 1, \
                "MathFunctions expect one operand of function type: " + repr(operands))
     # Use format function on value of operand.
     operand = operands[0]
     for key, val in list(operand.items()):
         new_val = create_symbol(format_function(str(val)), val.t, val, 1)
         operand[key] = new_val
     #raise Exception("pause")
     return operand
    def abs(self, o, *operands):

        # TODO: Are these safety checks needed?
        ffc_assert(
            len(operands) == 1 and () in operands[0] and len(operands[0]) == 1,
            "Abs expects one operand of function type: " + repr(operands))

        # Take absolute value of operand.
        val = operands[0][()]
        new_val = create_symbol(format["absolute value"](str(val)), val.t, val,
                                1)
        return {(): new_val}
 def not_condition(self, o, *operands):
     # This is a Condition but not a BinaryCondition, and the operand will be another Condition
     # Get condition expression and do safety checks.
     # Might be a bit too strict?
     c, = operands
     ffc_assert(len(c) == 1 and firstkey(c) == (),\
         "Condition for NotCondition should only be one function: " + repr(c))
     if self.parameters["format"] == "pyop2":
         return {(): create_expression(ast.Not, [c[()]], c[()].t)}
     var = format["not"](str(c[()]))
     sym = create_symbol(var, c[()].t, base_op=c[()].ops() + 1, iden=var)
     return {(): sym}
    def cell_volume(self, o,  *operands):
        # Safety check.
        ffc_assert(not operands, "Didn't expect any operands for CellVolume: " + repr(operands))

        # FIXME: KBO: This has to change for higher order elements
#        detJ = format["det(J)"](self.restriction)
#        volume = format["absolute value"](detJ)
#        self.trans_set.add(detJ)

        volume = format["cell volume"](self.restriction)
        self.trans_set.add(volume)

        return {():create_symbol(volume, GEO)}
    def facet_normal(self, o):
        components = self.component()

        # Safety check.
        ffc_assert(len(components) == 1,
                   "FacetNormal expects 1 component index: " + repr(components))

        # Handle 1D as a special case.
        # FIXME: KBO: This has to change for mD elements in R^n : m <
        # n
        if self.gdim == 1:  # FIXME: MSA UFL uses shape (1,) now, can we remove the special case here then?
            normal_component = format["normal component"](self.restriction, "")
        else:
            normal_component = format["normal component"](self.restriction,
                                                          components[0])
        self.trans_set.add(normal_component)

        return {(): create_symbol(normal_component, GEO)}
    def facet_normal(self, o):
        components = self.component()

        # Safety check.
        ffc_assert(
            len(components) == 1,
            "FacetNormal expects 1 component index: " + repr(components))

        # Handle 1D as a special case.
        # FIXME: KBO: This has to change for mD elements in R^n : m < n
        if self.gdim == 1:  # FIXME: MSA UFL uses shape (1,) now, can we remove the special case here then?
            normal_component = format["normal component"](self.restriction, "")
        else:
            normal_component = format["normal component"](self.restriction,
                                                          components[0])
        self.trans_set.add(normal_component)

        return {(): create_symbol(normal_component, GEO)}
    def _bessel_function(self, operands, format_function):
        # TODO: Are these safety checks needed?
        # TODO: work on reference instead of copies? (like math_function)
        ffc_assert(len(operands) == 2,\
          "BesselFunctions expect two operands of function type: " + repr(operands))
        nu, x = operands
        ffc_assert(len(nu) == 1 and () in nu,\
          "Expecting one operand of function type as first argument to BesselFunction : " + repr(nu))
        ffc_assert(len(x) == 1 and () in x,\
          "Expecting one operand of function type as second argument to BesselFunction : " + repr(x))
        nu = nu[()]
        x = x[()]
        if nu is None:
            nu = format["floating point"](0.0)
        if x is None:
            x = format["floating point"](0.0)

        sym = create_symbol(format_function(x,nu), x.t, x, 1)
        return {():sym}
    def binary_condition(self, o, *operands):

        # Get LHS and RHS expressions and do safety checks.  Might be
        # a bit too strict?
        lhs, rhs = operands
        ffc_assert(len(lhs) == 1 and firstkey(lhs) == (),
                   "LHS of Condtion should only be one function: " + repr(lhs))
        ffc_assert(len(rhs) == 1 and firstkey(rhs) == (),
                   "RHS of Condtion should only be one function: " + repr(rhs))

        # Map names from UFL to cpp.py.
        name_map = {"==": "is equal", "!=": "not equal",
                    "<": "less than", ">": "greater than",
                    "<=": "less equal", ">=": "greater equal",
                    "&&": "and", "||": "or"}

        # Get the minimum type
        t = min(lhs[()].t, rhs[()].t)
        ops = lhs[()].ops() + rhs[()].ops() + 1
        cond = str(lhs[()]) + format[name_map[o._name]] + str(rhs[()])
        sym = create_symbol(format["grouping"](cond), t, base_op=ops)
        return {(): sym}
    def binary_condition(self, o, *operands):

        # Get LHS and RHS expressions and do safety checks.
        # Might be a bit too strict?
        lhs, rhs = operands
        ffc_assert(len(lhs) == 1 and firstkey(lhs) == (),\
            "LHS of Condtion should only be one function: " + repr(lhs))
        ffc_assert(len(rhs) == 1 and firstkey(rhs) == (),\
            "RHS of Condtion should only be one function: " + repr(rhs))

        # Map names from UFL to cpp.py.
        name_map = {"==":"is equal", "!=":"not equal",\
                    "<":"less than", ">":"greater than",\
                    "<=":"less equal", ">=":"greater equal",\
                    "&&": "and", "||": "or"}

        # Get the minimum type
        t = min(lhs[()].t, rhs[()].t)
        ops = lhs[()].ops() + rhs[()].ops() + 1
        if self.parameters["format"] == "pyop2":
            name_map = {
                "==": ast.Eq,
                "!=": ast.NEq,
                "<": ast.Less,
                ">": ast.Greater,
                "<=": ast.LessEq,
                ">=": ast.GreaterEq,
                "&&": ast.And,
                "||": ast.Or
            }
            expr = create_expression(name_map[o._name], [lhs[()], rhs[()]], t)
            return {(): expr}
        cond = str(lhs[()]) + format[name_map[o._name]] + str(rhs[()])
        var = format["grouping"](cond)
        sym = create_symbol(var, t, base_op=ops, iden=var)
        return {(): sym}
    def create_function(self, ufl_function, derivatives, component, local_comp,
                        local_offset, ffc_element, is_quad_element,
                        transformation, multiindices, tdim, gdim, avg):
        "Create code for basis functions, and update relevant tables of used basis."
        ffc_assert(ufl_function in self._function_replace_values,
                   "Expecting ufl_function to have been mapped prior to this call.")

        # Prefetch formats to speed up code generation.
        f_transform = format["transform"]
        f_detJ = format["det(J)"]

        # Reset code
        code = []

        # Handle affine mappings.
        if transformation == "affine":
            # Loop derivatives and get multi indices.
            for multi in multiindices:
                deriv = [multi.count(i) for i in range(tdim)]
                if not any(deriv):
                    deriv = []

                # Create function name.
                function_name = self._create_function_name(component, deriv,
                                                           avg, is_quad_element,
                                                           ufl_function,
                                                           ffc_element)
                if function_name:
                    # Add transformation if needed.
                    code.append(self.__apply_transform(function_name,
                                                       derivatives, multi, tdim, gdim))

        # Handle non-affine mappings.
        else:
            ffc_assert(avg is None,
                       "Taking average is not supported for non-affine mappings.")

            # Loop derivatives and get multi indices.
            for multi in multiindices:
                deriv = [multi.count(i) for i in range(tdim)]
                if not any(deriv):
                    deriv = []

                if transformation in ["covariant piola",
                                      "contravariant piola"]:
                    for c in range(tdim):
                        function_name = self._create_function_name(c + local_offset, deriv, avg, is_quad_element, ufl_function, ffc_element)
                        if function_name:
                            # Multiply basis by appropriate transform.
                            if transformation == "covariant piola":
                                dxdX = create_symbol(f_transform("JINV", c,
                                                                 local_comp, tdim,
                                                                 gdim,
                                                                 self.restriction),
                                                     GEO)
                                function_name = create_product([dxdX, function_name])
                            elif transformation == "contravariant piola":
                                detJ = create_fraction(create_float(1),
                                                       create_symbol(f_detJ(self.restriction),
                                                                     GEO))
                                dXdx = create_symbol(f_transform("J", local_comp,
                                                                 c, gdim, tdim,
                                                                 self.restriction),
                                                     GEO)
                                function_name = create_product([detJ, dXdx,
                                                                function_name])
                            # Add transformation if needed.
                            code.append(self.__apply_transform(function_name,
                                                               derivatives, multi,
                                                               tdim, gdim))
                elif transformation == "double covariant piola":
                    # g_ij = (Jinv)_ki G_kl (Jinv)lj
                    i = local_comp // tdim
                    j = local_comp % tdim
                    for k in range(tdim):
                        for l in range(tdim):
                            # Create mapping and basis name.
                            function_name = self._create_function_name(
                                k * tdim + l + local_offset, deriv, avg,
                                is_quad_element, ufl_function, ffc_element)
                            J1 = create_symbol(
                                f_transform("JINV", k, i, tdim, gdim,
                                            self.restriction), GEO)
                            J2 = create_symbol(
                                f_transform("JINV", l, j, tdim, gdim,
                                            self.restriction), GEO)
                            function_name = create_product([J1, function_name,
                                                            J2])
                            # Add transformation if needed.
                            code.append(self.__apply_transform(
                                function_name, derivatives, multi, tdim, gdim))
                elif transformation == "double contravariant piola":
                    # g_ij = (detJ)^(-2) J_ik G_kl J_jl
                    i = local_comp // tdim
                    j = local_comp % tdim
                    for k in range(tdim):
                        for l in range(tdim):
                            # Create mapping and basis name.
                            function_name = self._create_function_name(
                                k * tdim + l + local_offset,
                                deriv, avg, is_quad_element,
                                ufl_function, ffc_element)
                            J1 = create_symbol(
                                f_transform("J", i, k, tdim, gdim,
                                            self.restriction), GEO)
                            J2 = create_symbol(
                                f_transform("J", j, l, tdim, gdim,
                                            self.restriction), GEO)
                            invdetJ = create_fraction(
                                create_float(1),
                                create_symbol(f_detJ(self.restriction), GEO))
                            function_name = create_product([invdetJ, invdetJ,
                                                            J1, function_name,
                                                            J2])
                            # Add transformation if needed.
                            code.append(self.__apply_transform(function_name,
                                                               derivatives,
                                                               multi, tdim,
                                                               gdim))
                else:
                    error("Transformation is not supported: ",
                          repr(transformation))

        if not code:
            return create_float(0.0)
        elif len(code) > 1:
            code = create_sum(code)
        else:
            code = code[0]

        return code
    def create_argument(self, ufl_argument, derivatives, component, local_comp,
                        local_offset, ffc_element, transformation,
                        multiindices, tdim, gdim, avg):
        "Create code for basis functions, and update relevant tables of used basis."

        # Prefetch formats to speed up code generation.
        f_transform = format["transform"]
        f_detJ = format["det(J)"]

        # Reset code
        code = {}

        # Affine mapping
        if transformation == "affine":
            # Loop derivatives and get multi indices.
            for multi in multiindices:
                deriv = [multi.count(i) for i in range(tdim)]
                if not any(deriv):
                    deriv = []

                # Create mapping and basis name.
                mapping, basis = self._create_mapping_basis(component, deriv,
                                                            avg, ufl_argument,
                                                            ffc_element)
                if mapping not in code:
                    code[mapping] = []

                if basis is not None:
                    # Add transformation if needed.
                    code[mapping].append(self.__apply_transform(basis,
                                                                derivatives,
                                                                multi, tdim,
                                                                gdim))

        # Handle non-affine mappings.
        else:
            ffc_assert(avg is None,
                       "Taking average is not supported for non-affine mappings.")

            # Loop derivatives and get multi indices.
            for multi in multiindices:
                deriv = [multi.count(i) for i in range(tdim)]
                if not any(deriv):
                    deriv = []

                if transformation in ["covariant piola",
                                      "contravariant piola"]:
                    for c in range(tdim):
                        # Create mapping and basis name.
                        mapping, basis = self._create_mapping_basis(c + local_offset, deriv, avg, ufl_argument, ffc_element)
                        if mapping not in code:
                            code[mapping] = []

                        if basis is not None:
                            # Multiply basis by appropriate transform.
                            if transformation == "covariant piola":
                                dxdX = create_symbol(f_transform("JINV", c,
                                                                 local_comp, tdim,
                                                                 gdim,
                                                                 self.restriction),
                                                     GEO)
                                basis = create_product([dxdX, basis])
                            elif transformation == "contravariant piola":
                                detJ = create_fraction(create_float(1),
                                                       create_symbol(f_detJ(self.restriction), GEO))
                                dXdx = create_symbol(f_transform("J", local_comp,
                                                                 c, gdim, tdim,
                                                                 self.restriction),
                                                     GEO)
                                basis = create_product([detJ, dXdx, basis])
                        # Add transformation if needed.
                        code[mapping].append(self.__apply_transform(basis,
                                                                    derivatives,
                                                                    multi, tdim,
                                                                    gdim))
                elif transformation == "double covariant piola":
                    # g_ij = (Jinv)_ki G_kl (Jinv)lj
                    i = local_comp // tdim
                    j = local_comp % tdim
                    for k in range(tdim):
                        for l in range(tdim):
                            # Create mapping and basis name.
                            mapping, basis = self._create_mapping_basis(
                                k * tdim + l + local_offset,
                                deriv, avg, ufl_argument, ffc_element)
                            if mapping not in code:
                                code[mapping] = []
                            if basis is not None:
                                J1 = create_symbol(
                                    f_transform("JINV", k, i, tdim, gdim,
                                                self.restriction), GEO)
                                J2 = create_symbol(
                                    f_transform("JINV", l, j, tdim, gdim,
                                                 self.restriction), GEO)
                                basis = create_product([J1, basis, J2])
                                # Add transformation if needed.
                                code[mapping].append(
                                    self.__apply_transform(
                                        basis, derivatives, multi,
                                        tdim, gdim))
                elif transformation == "double contravariant piola":
                    # g_ij = (detJ)^(-2) J_ik G_kl J_jl
                    i = local_comp // tdim
                    j = local_comp % tdim
                    for k in range(tdim):
                        for l in range(tdim):
                            # Create mapping and basis name.
                            mapping, basis = self._create_mapping_basis(
                                k * tdim + l + local_offset,
                                deriv, avg, ufl_argument, ffc_element)
                            if mapping not in code:
                                code[mapping] = []
                            if basis is not None:
                                J1 = create_symbol(
                                    f_transform("J", i, k, gdim, tdim,
                                                self.restriction), GEO)
                                J2 = create_symbol(
                                    f_transform("J", j, l, gdim, tdim,
                                                self.restriction), GEO)
                                invdetJ = create_fraction(
                                    create_float(1),
                                    create_symbol(f_detJ(self.restriction),
                                                  GEO))
                                basis = create_product([invdetJ, invdetJ, J1,
                                                        basis, J2])
                                # Add transformation if needed.
                                code[mapping].append(
                                    self.__apply_transform(
                                        basis, derivatives, multi,
                                        tdim, gdim))
                else:
                    error("Transformation is not supported: " + repr(transformation))


        # Add sums and group if necessary.
        for key, val in list(code.items()):
            if len(val) > 1:
                code[key] = create_sum(val)
            else:
                code[key] = val[0]

        return code
 def _create_symbol(self, symbol, domain):
     return {(): create_symbol(symbol, domain)}
    def create_argument(self, ufl_argument, derivatives, component, local_comp,
                        local_offset, ffc_element, transformation, multiindices,
                        tdim, gdim, avg):
        "Create code for basis functions, and update relevant tables of used basis."
        ffc_assert(ufl_argument in self._function_replace_values, "Expecting ufl_argument to have been mapped prior to this call.")

        # Prefetch formats to speed up code generation.
        f_transform     = format["transform"]
        f_detJ          = format["det(J)"]

        # Reset code
        code = {}

        # Affine mapping
        if transformation == "affine":
            # Loop derivatives and get multi indices.
            for multi in multiindices:
                deriv = [multi.count(i) for i in range(self.tdim)]
                if not any(deriv):
                    deriv = []

                # Create mapping and basis name.
                mapping, basis = self._create_mapping_basis(component, deriv, avg, ufl_argument, ffc_element)
                if not mapping in code:
                    code[mapping] = []

                if basis is not None:
                    # Add transformation if needed.
                    code[mapping].append(self.__apply_transform(basis, derivatives, multi, tdim, gdim))

        # Handle non-affine mappings.
        else:
            ffc_assert(avg is None, "Taking average is not supported for non-affine mappings.")

            # Loop derivatives and get multi indices.
            for multi in multiindices:
                deriv = [multi.count(i) for i in range(self.tdim)]
                if not any(deriv):
                    deriv = []

                for c in range(self.tdim):
                    # Create mapping and basis name.
                    mapping, basis = self._create_mapping_basis(c + local_offset, deriv, avg, ufl_argument, ffc_element)
                    if not mapping in code:
                        code[mapping] = []

                    if basis is not None:
                        # Multiply basis by appropriate transform.
                        if transformation == "covariant piola":
                            dxdX = create_symbol(f_transform("JINV", c, local_comp, tdim, gdim, self.restriction), GEO)
                            basis = create_product([dxdX, basis])
                        elif transformation == "contravariant piola":
                            detJ = create_fraction(create_float(1), create_symbol(f_detJ(self.restriction), GEO))
                            dXdx = create_symbol(f_transform("J", local_comp, c, gdim, tdim, self.restriction), GEO)
                            basis = create_product([detJ, dXdx, basis])
                        else:
                            error("Transformation is not supported: " + repr(transformation))

                        # Add transformation if needed.
                        code[mapping].append(self.__apply_transform(basis, derivatives, multi, tdim, gdim))

        # Add sums and group if necessary.
        for key, val in code.items():
            if len(val) > 1:
                code[key] = create_sum(val)
            else:
                code[key] = val[0]

        return code
    def create_function(self, ufl_function, derivatives, component, local_comp,
                        local_offset, ffc_element, is_quad_element,
                        transformation, multiindices, tdim, gdim, avg):
        "Create code for basis functions, and update relevant tables of used basis."
        ffc_assert(
            ufl_function in self._function_replace_values,
            "Expecting ufl_function to have been mapped prior to this call.")

        # Prefetch formats to speed up code generation.
        f_transform = format["transform"]
        f_detJ = format["det(J)"]

        # Reset code
        code = []

        # Handle affine mappings.
        if transformation == "affine":
            # Loop derivatives and get multi indices.
            for multi in multiindices:
                deriv = [multi.count(i) for i in range(tdim)]
                if not any(deriv):
                    deriv = []

                # Create function name.
                function_name = self._create_function_name(
                    component, deriv, avg, is_quad_element, ufl_function,
                    ffc_element)
                if function_name:
                    # Add transformation if needed.
                    code.append(
                        self.__apply_transform(function_name, derivatives,
                                               multi, tdim, gdim))

        # Handle non-affine mappings.
        else:
            ffc_assert(
                avg is None,
                "Taking average is not supported for non-affine mappings.")

            # Loop derivatives and get multi indices.
            for multi in multiindices:
                deriv = [multi.count(i) for i in range(tdim)]
                if not any(deriv):
                    deriv = []

                if transformation in [
                        "covariant piola", "contravariant piola"
                ]:
                    for c in range(tdim):
                        function_name = self._create_function_name(
                            c + local_offset, deriv, avg, is_quad_element,
                            ufl_function, ffc_element)
                        if function_name:
                            # Multiply basis by appropriate transform.
                            if transformation == "covariant piola":
                                dxdX = create_symbol(
                                    f_transform("JINV", c, local_comp, tdim,
                                                gdim, self.restriction), GEO)
                                function_name = create_product(
                                    [dxdX, function_name])
                            elif transformation == "contravariant piola":
                                detJ = create_fraction(
                                    create_float(1),
                                    create_symbol(f_detJ(self.restriction),
                                                  GEO))
                                dXdx = create_symbol(
                                    f_transform("J", local_comp, c, gdim, tdim,
                                                self.restriction), GEO)
                                function_name = create_product(
                                    [detJ, dXdx, function_name])
                            # Add transformation if needed.
                            code.append(
                                self.__apply_transform(function_name,
                                                       derivatives, multi,
                                                       tdim, gdim))
                elif transformation == "double covariant piola":
                    # g_ij = (Jinv)_ki G_kl (Jinv)lj
                    i = local_comp // tdim
                    j = local_comp % tdim
                    for k in range(tdim):
                        for l in range(tdim):
                            # Create mapping and basis name.
                            function_name = self._create_function_name(
                                k * tdim + l + local_offset, deriv, avg,
                                is_quad_element, ufl_function, ffc_element)
                            J1 = create_symbol(
                                f_transform("JINV", k, i, tdim, gdim,
                                            self.restriction), GEO)
                            J2 = create_symbol(
                                f_transform("JINV", l, j, tdim, gdim,
                                            self.restriction), GEO)
                            function_name = create_product(
                                [J1, function_name, J2])
                            # Add transformation if needed.
                            code.append(
                                self.__apply_transform(function_name,
                                                       derivatives, multi,
                                                       tdim, gdim))
                elif transformation == "double contravariant piola":
                    # g_ij = (detJ)^(-2) J_ik G_kl J_jl
                    i = local_comp // tdim
                    j = local_comp % tdim
                    for k in range(tdim):
                        for l in range(tdim):
                            # Create mapping and basis name.
                            function_name = self._create_function_name(
                                k * tdim + l + local_offset, deriv, avg,
                                is_quad_element, ufl_function, ffc_element)
                            J1 = create_symbol(
                                f_transform("J", i, k, tdim, gdim,
                                            self.restriction), GEO)
                            J2 = create_symbol(
                                f_transform("J", j, l, tdim, gdim,
                                            self.restriction), GEO)
                            invdetJ = create_fraction(
                                create_float(1),
                                create_symbol(f_detJ(self.restriction), GEO))
                            function_name = create_product(
                                [invdetJ, invdetJ, J1, function_name, J2])
                            # Add transformation if needed.
                            code.append(
                                self.__apply_transform(function_name,
                                                       derivatives, multi,
                                                       tdim, gdim))
                else:
                    error("Transformation is not supported: ",
                          repr(transformation))

        if not code:
            return create_float(0.0)
        elif len(code) > 1:
            code = create_sum(code)
        else:
            code = code[0]

        return code
    def circumradius(self, o):
        # FIXME: KBO: This has to change for higher order elements
        circumradius = format["circumradius"](self.restriction)
        self.trans_set.add(circumradius)

        return {(): create_symbol(circumradius, GEO)}
    def create_function(self, ufl_function, derivatives, component, local_comp,
                        local_offset, ffc_element, is_quad_element,
                        transformation, multiindices, tdim, gdim, avg):
        "Create code for basis functions, and update relevant tables of used basis."
        ffc_assert(
            ufl_function in self._function_replace_values,
            "Expecting ufl_function to have been mapped prior to this call.")

        # Prefetch formats to speed up code generation.
        f_transform = format["transform"]
        f_detJ = format["det(J)"]

        # Reset code
        code = []

        # Handle affine mappings.
        if transformation == "affine":
            # Loop derivatives and get multi indices.
            for multi in multiindices:
                deriv = [multi.count(i) for i in range(tdim)]
                if not any(deriv):
                    deriv = []

                # Create function name.
                function_name = self._create_function_name(
                    component, deriv, avg, is_quad_element, ufl_function,
                    ffc_element)
                if function_name:
                    # Add transformation if needed.
                    code.append(
                        self.__apply_transform(function_name, derivatives,
                                               multi, tdim, gdim))

        # Handle non-affine mappings.
        else:
            ffc_assert(
                avg is None,
                "Taking average is not supported for non-affine mappings.")

            # Loop derivatives and get multi indices.
            for multi in multiindices:
                deriv = [multi.count(i) for i in range(tdim)]
                if not any(deriv):
                    deriv = []

                for c in range(tdim):
                    function_name = self._create_function_name(
                        c + local_offset, deriv, avg, is_quad_element,
                        ufl_function, ffc_element)
                    if function_name:
                        # Multiply basis by appropriate transform.
                        if transformation == "covariant piola":
                            dxdX = create_symbol(
                                f_transform("JINV", c, local_comp, tdim, gdim,
                                            self.restriction),
                                GEO,
                                loop_index=[c * gdim + local_comp],
                                iden="K%s" % _choose_map(self.restriction))
                            function_name = create_product(
                                [dxdX, function_name])
                        elif transformation == "contravariant piola":
                            detJ = create_fraction(
                                create_float(1),
                                create_symbol(f_detJ(self.restriction),
                                              GEO,
                                              iden=f_detJ(self.restriction)))
                            dXdx = create_symbol(f_transform("J", local_comp, c, gdim, tdim, self.restriction), GEO, \
                                        loop_index=[local_comp*tdim + c], iden="J%s" % _choose_map(self.restriction))
                            function_name = create_product(
                                [detJ, dXdx, function_name])
                        else:
                            error("Transformation is not supported: ",
                                  repr(transformation))

                        # Add transformation if needed.
                        code.append(
                            self.__apply_transform(function_name, derivatives,
                                                   multi, tdim, gdim))

        if not code:
            return create_float(0.0)
        elif len(code) > 1:
            code = create_sum(code)
        else:
            code = code[0]

        return code
    def create_argument(self, ufl_argument, derivatives, component, local_comp,
                        local_offset, ffc_element, transformation,
                        multiindices, tdim, gdim, avg):
        "Create code for basis functions, and update relevant tables of used basis."

        # Prefetch formats to speed up code generation.
        f_transform = format["transform"]
        f_detJ = format["det(J)"]

        # Reset code
        code = {}

        # Affine mapping
        if transformation == "affine":
            # Loop derivatives and get multi indices.
            for multi in multiindices:
                deriv = [multi.count(i) for i in range(tdim)]
                if not any(deriv):
                    deriv = []

                # Create mapping and basis name.
                mapping, basis = self._create_mapping_basis(
                    component, deriv, avg, ufl_argument, ffc_element)
                if mapping not in code:
                    code[mapping] = []

                if basis is not None:
                    # Add transformation if needed.
                    code[mapping].append(
                        self.__apply_transform(basis, derivatives, multi, tdim,
                                               gdim))

        # Handle non-affine mappings.
        else:
            ffc_assert(
                avg is None,
                "Taking average is not supported for non-affine mappings.")

            # Loop derivatives and get multi indices.
            for multi in multiindices:
                deriv = [multi.count(i) for i in range(tdim)]
                if not any(deriv):
                    deriv = []

                if transformation in [
                        "covariant piola", "contravariant piola"
                ]:
                    for c in range(tdim):
                        # Create mapping and basis name.
                        mapping, basis = self._create_mapping_basis(
                            c + local_offset, deriv, avg, ufl_argument,
                            ffc_element)
                        if mapping not in code:
                            code[mapping] = []

                        if basis is not None:
                            # Multiply basis by appropriate transform.
                            if transformation == "covariant piola":
                                dxdX = create_symbol(
                                    f_transform("JINV", c, local_comp, tdim,
                                                gdim, self.restriction), GEO)
                                basis = create_product([dxdX, basis])
                            elif transformation == "contravariant piola":
                                detJ = create_fraction(
                                    create_float(1),
                                    create_symbol(f_detJ(self.restriction),
                                                  GEO))
                                dXdx = create_symbol(
                                    f_transform("J", local_comp, c, gdim, tdim,
                                                self.restriction), GEO)
                                basis = create_product([detJ, dXdx, basis])
                        # Add transformation if needed.
                        code[mapping].append(
                            self.__apply_transform(basis, derivatives, multi,
                                                   tdim, gdim))
                elif transformation == "double covariant piola":
                    # g_ij = (Jinv)_ki G_kl (Jinv)lj
                    i = local_comp // tdim
                    j = local_comp % tdim
                    for k in range(tdim):
                        for l in range(tdim):
                            # Create mapping and basis name.
                            mapping, basis = self._create_mapping_basis(
                                k * tdim + l + local_offset, deriv, avg,
                                ufl_argument, ffc_element)
                            if mapping not in code:
                                code[mapping] = []
                            if basis is not None:
                                J1 = create_symbol(
                                    f_transform("JINV", k, i, tdim, gdim,
                                                self.restriction), GEO)
                                J2 = create_symbol(
                                    f_transform("JINV", l, j, tdim, gdim,
                                                self.restriction), GEO)
                                basis = create_product([J1, basis, J2])
                                # Add transformation if needed.
                                code[mapping].append(
                                    self.__apply_transform(
                                        basis, derivatives, multi, tdim, gdim))
                elif transformation == "double contravariant piola":
                    # g_ij = (detJ)^(-2) J_ik G_kl J_jl
                    i = local_comp // tdim
                    j = local_comp % tdim
                    for k in range(tdim):
                        for l in range(tdim):
                            # Create mapping and basis name.
                            mapping, basis = self._create_mapping_basis(
                                k * tdim + l + local_offset, deriv, avg,
                                ufl_argument, ffc_element)
                            if mapping not in code:
                                code[mapping] = []
                            if basis is not None:
                                J1 = create_symbol(
                                    f_transform("J", i, k, gdim, tdim,
                                                self.restriction), GEO)
                                J2 = create_symbol(
                                    f_transform("J", j, l, gdim, tdim,
                                                self.restriction), GEO)
                                invdetJ = create_fraction(
                                    create_float(1),
                                    create_symbol(f_detJ(self.restriction),
                                                  GEO))
                                basis = create_product(
                                    [invdetJ, invdetJ, J1, basis, J2])
                                # Add transformation if needed.
                                code[mapping].append(
                                    self.__apply_transform(
                                        basis, derivatives, multi, tdim, gdim))
                else:
                    error("Transformation is not supported: " +
                          repr(transformation))

        # Add sums and group if necessary.
        for key, val in list(code.items()):
            if len(val) > 1:
                code[key] = create_sum(val)
            else:
                code[key] = val[0]

        return code
    def circumradius(self, o):
        # FIXME: KBO: This has to change for higher order elements
        circumradius = format["circumradius"](self.restriction)
        self.trans_set.add(circumradius)

        return {(): create_symbol(circumradius, GEO)}
 def _create_symbol(self, symbol, domain):
     return {(): create_symbol(symbol, domain)}
Example #47
0
def _extract_variables(val, basis_consts, ip_consts, geo_consts, t_set,
                       optimisation):
    f_G = format["geometry constant"]
    f_I = format["ip constant"]
    f_B = format["basis constant"]

    if val._prec == 0:
        return val
    elif val._prec == 1:
        if val.base_expr is None:
            return val
        new_base = _extract_variables(val.base_expr, basis_consts, ip_consts,
                                      geo_consts, t_set, optimisation)
        new_sym = create_symbol(val.v, val.t, new_base, val.base_op)
        if new_sym.t == BASIS:
            return _reduce_expression(new_sym, [], basis_consts, f_B, True)
        elif new_sym.t == IP:
            return _reduce_expression(new_sym, [], ip_consts, f_I, True)
        elif new_sym.t == GEO:
            return _reduce_expression(new_sym, [], geo_consts, f_G, True)
    # First handle child classes of product and sum.
    elif val._prec in (2, 3):
        new_vars = []
        for v in val.vrs:
            new_vars.append(
                _extract_variables(v, basis_consts, ip_consts, geo_consts,
                                   t_set, optimisation))
        if val._prec == 2:
            new_val = create_product(new_vars)
        if val._prec == 3:
            new_val = create_sum(new_vars)
    elif val._prec == 4:
        num = _extract_variables(val.num, basis_consts, ip_consts, geo_consts,
                                 t_set, optimisation)
        denom = _extract_variables(val.denom, basis_consts, ip_consts,
                                   geo_consts, t_set, optimisation)
        return create_fraction(num, denom)
    else:
        error("Unknown symbolic type: %s" % repr(val))

    # Sort variables of product and sum.
    b_c, i_c, g_c = [], [], []
    for v in new_val.vrs:
        if v.t == BASIS:
            if optimisation == "precompute_basis_const":
                b_c.append(v)
        elif v.t == IP:
            i_c.append(v)
        else:
            g_c.append(v)
    vrs = new_val.vrs[:]
    for v in g_c + i_c + b_c:
        vrs.remove(v)
    i_c.extend(_reduce_expression(new_val, g_c, geo_consts, f_G))
    vrs.extend(_reduce_expression(new_val, i_c, ip_consts, f_I))
    vrs.extend(_reduce_expression(new_val, b_c, basis_consts, f_B))

    #    print "b_c: "
    #    for b in b_c:
    #        print b
    #    print "basis"
    #    for k,v in basis_consts.items():
    #        print "k: ", k
    #        print "v: ", v
    #    print "geo"
    #    for k,v in geo_consts.items():
    #        print "k: ", k
    #        print "v: ", v
    #    print "ret val: ", val

    if len(vrs) > 1:
        if new_val._prec == 2:
            new_object = create_product(vrs)
        elif new_val._prec == 3:
            new_object = create_sum(vrs)
        else:
            error("Must have product or sum here: %s" % repr(new_val))
        if new_object.t == BASIS:
            if optimisation == "precompute_ip_const":
                return new_object
            elif optimisation == "precompute_basis_const":
                return _reduce_expression(new_object, [], basis_consts, f_B,
                                          True)
        elif new_object.t == IP:
            return _reduce_expression(new_object, [], ip_consts, f_I, True)
        elif new_object.t == GEO:
            return _reduce_expression(new_object, [], geo_consts, f_G, True)
    return vrs[0]
Example #48
0
def _compute_basisvalues(data, dof_data):
    """From FIAT_NEW.expansions."""

    UNROLL = True

    # Prefetch formats to speed up code generation.
    f_comment     = format["comment"]
    f_add         = format["add"]
    f_mul         = format["mul"]
    f_imul        = format["imul"]
    f_sub         = format["sub"]
    f_group       = format["grouping"]
    f_assign      = format["assign"]
    f_sqrt        = format["sqrt"]["ufc"]
    f_x           = format["x coordinate"]
    f_y           = format["y coordinate"]
    f_z           = format["z coordinate"]
    f_double      = format["float declaration"]
    f_basisvalue  = format["basisvalues"]
    f_component   = format["component"]
    f_float       = format["floating point"]
    f_uint        = format["uint declaration"]
    f_tensor      = format["tabulate tensor"]
    f_loop        = format["generate loop"]
    f_decl        = format["declaration"]
    f_tmp         = format["tmp value"]
    f_int         = format["int"]

    f_r = format["free indices"][0]

    # Create temporary values.
    f1, f2, f3, f4, f5  = [create_symbol(f_tmp(i), CONST) for i in range(0,5)]

    # Get embedded degree.
    embedded_degree = dof_data["embedded_degree"]

    # Create helper symbols.
    symbol_p    = create_symbol(f_r, CONST)
    symbol_x    = create_symbol(f_x, CONST)
    symbol_y    = create_symbol(f_y, CONST)
    symbol_z    = create_symbol(f_z, CONST)
    int_n1   = f_int(embedded_degree + 1)
    float_1 = create_float(1)
    float_2 = create_float(2)
    float_0_5   = create_float(0.5)
    float_0_25  = create_float(0.25)

    # Initialise return code.
    code = [""]

    # Create zero array for basisvalues.
    # Get number of members of the expansion set.
    num_mem = dof_data["num_expansion_members"]
    code += [f_comment("Array of basisvalues")]
    code += [f_decl(f_double, f_component(f_basisvalue, num_mem), f_tensor([0.0]*num_mem))]

    # Declare helper variables, will be removed if not used.
    code += ["", f_comment("Declare helper variables")] # Keeping this here to avoid changing references

    # Get the element cell name
    element_cellname = data["cellname"]

    def _jrc(a, b, n):
        an = float( ( 2*n+1+a+b)*(2*n+2+a+b))/ float( 2*(n+1)*(n+1+a+b))
        bn = float( (a*a-b*b) * (2*n+1+a+b))/ float( 2*(n+1)*(2*n+a+b)*(n+1+a+b) )
        cn = float( (n+a)*(n+b)*(2*n+2+a+b))/ float( (n+1)*(n+1+a+b)*(2*n+a+b) )
        return (an,bn,cn)

    # 1D
    if (element_cellname == "interval"):
        # FIAT_NEW.expansions.LineExpansionSet.
        # FIAT_NEW code
        # psitilde_as = jacobi.eval_jacobi_batch(0,0,n,ref_pts)
        # FIAT_NEW.jacobi.eval_jacobi_batch(a,b,n,xs)
        # The initial value basisvalue 0 is always 1.0
        # FIAT_NEW code
        # for ii in range(result.shape[1]):
        #    result[0,ii] = 1.0 + xs[ii,0] - xs[ii,0]
        code += ["", f_comment("Compute basisvalues")]
        code += [f_assign(f_component(f_basisvalue, 0), f_float(1.0))]

        # Only continue if the embedded degree is larger than zero.
        if embedded_degree > 0:

            # FIAT_NEW.jacobi.eval_jacobi_batch(a,b,n,xs).
            # result[1,:] = 0.5 * ( a - b + ( a + b + 2.0 ) * xsnew )
            # The initial value basisvalue 1 is always x
            code += [f_assign(f_component(f_basisvalue, 1), f_x)]

            # Only active is embedded_degree > 1.
            if embedded_degree > 1:
                # FIAT_NEW.jacobi.eval_jacobi_batch(a,b,n,xs).
                # apb = a + b (equal to 0 because of function arguments)
                # for k in range(2,n+1):
                #    a1 = 2.0 * k * ( k + apb ) * ( 2.0 * k + apb - 2.0 )
                #    a2 = ( 2.0 * k + apb - 1.0 ) * ( a * a - b * b )
                #    a3 = ( 2.0 * k + apb - 2.0 )  \
                #        * ( 2.0 * k + apb - 1.0 ) \
                #        * ( 2.0 * k + apb )
                #    a4 = 2.0 * ( k + a - 1.0 ) * ( k + b - 1.0 ) \
                #        * ( 2.0 * k + apb )
                #    a2 = a2 / a1
                #    a3 = a3 / a1
                #    a4 = a4 / a1
                #    result[k,:] = ( a2 + a3 * xsnew ) * result[k-1,:] \
                #        - a4 * result[k-2,:]

                # The below implements the above (with a = b = apb = 0)
                for r in range(2, embedded_degree+1):

                    # Define helper variables
                    a1 = 2.0*r*r*(2.0*r - 2.0)
                    a3 = ((2.0*r - 2.0)*(2.0*r - 1.0 )*(2.0*r))/a1
                    a4 = (2.0*(r - 1.0)*(r - 1.0)*(2.0*r))/a1

                    assign_to = f_component(f_basisvalue, r)
                    assign_from = f_sub([f_mul([f_x, f_component(f_basisvalue, r-1), f_float(a3)]),
                                         f_mul([f_component(f_basisvalue, r-2), f_float(a4)])])
                    code += [f_assign(assign_to, assign_from)]

        # Scale values.
        # FIAT_NEW.expansions.LineExpansionSet.
        # FIAT_NEW code
        # results = numpy.zeros( ( n+1 , len(pts) ) , type( pts[0][0] ) )
        # for k in range( n + 1 ):
        #    results[k,:] = psitilde_as[k,:] * math.sqrt( k + 0.5 )
        lines = []
        loop_vars = [(str(symbol_p), 0, int_n1)]
        # Create names.
        basis_k = create_symbol(f_component(f_basisvalue, str(symbol_p)), CONST)
        # Compute value.
        fac1 = create_symbol( f_sqrt(str(symbol_p + float_0_5)), CONST )
        lines += [format["imul"](str(basis_k), str(fac1))]
        # Create loop (block of lines).
        code += f_loop(lines, loop_vars)
    # 2D
    elif (element_cellname == "triangle"):
        # FIAT_NEW.expansions.TriangleExpansionSet.

        # Compute helper factors
        # FIAT_NEW code
        # f1 = (1.0+2*x+y)/2.0
        # f2 = (1.0 - y) / 2.0
        # f3 = f2**2
        fac1 = create_fraction(float_1 + float_2*symbol_x + symbol_y, float_2)
        fac2 = create_fraction(float_1 - symbol_y, float_2)
        code += [f_decl(f_double, str(f1), fac1)]
        code += [f_decl(f_double, str(f2), fac2)]
        code += [f_decl(f_double, str(f3), f2*f2)]

        code += ["", f_comment("Compute basisvalues")]
        # The initial value basisvalue 0 is always 1.0.
        # FIAT_NEW code
        # for ii in range( results.shape[1] ):
        #    results[0,ii] = 1.0 + apts[ii,0]-apts[ii,0]+apts[ii,1]-apts[ii,1]
        code += [f_assign(f_component(f_basisvalue, 0), f_float(1.0))]

        def _idx2d(p, q):
            return (p + q)*(p + q + 1)//2 + q

        # Only continue if the embedded degree is larger than zero.
        if embedded_degree > 0:

            # The initial value of basisfunction 1 is equal to f1.
            # FIAT_NEW code
            # results[idx(1,0),:] = f1
            code += [f_assign(f_component(f_basisvalue, 1), str(f1))]

            # NOTE: KBO: The order of the loops is VERY IMPORTANT!!
            # Only active is embedded_degree > 1.
            if embedded_degree > 1:
                # FIAT_NEW code (loop 1 in FIAT)
                # for p in range(1,n):
                #    a = (2.0*p+1)/(1.0+p)
                #    b = p / (p+1.0)
                #    results[idx(p+1,0)] = a * f1 * results[idx(p,0),:] \
                #        - p/(1.0+p) * f3 *results[idx(p-1,0),:]
                # FIXME: KBO: Is there an error in FIAT? why is b not used?

                for r in range(1, embedded_degree):
                    rr = _idx2d((r + 1), 0)
                    assign_to = f_component(f_basisvalue, rr)
                    ss = _idx2d(r, 0)
                    tt = _idx2d((r - 1), 0)
                    A = (2*r + 1.0)/(r + 1)
                    B = r/(1.0 + r)
                    v1 = f_mul([f_component(f_basisvalue, ss), f_float(A),
                                str(f1)])
                    v2 = f_mul([f_component(f_basisvalue, tt), f_float(B),
                                str(f3)])
                    assign_from = f_sub([v1, v2])
                    code += [f_assign(assign_to, assign_from)]

            # FIAT_NEW code (loop 2 in FIAT).
            # for p in range(n):
            #    results[idx(p,1),:] = 0.5 * (1+2.0*p+(3.0+2.0*p)*y) \
            #        * results[idx(p,0)]

            for r in range(0, embedded_degree):
                # (p+q)*(p+q+1)//2 + q
                rr = _idx2d(r, 1)
                assign_to = f_component(f_basisvalue, rr)
                ss = _idx2d(r, 0)
                A = 0.5*(1 + 2*r)
                B = 0.5*(3 + 2*r)
                C = f_add([f_float(A), f_mul([f_float(B), str(symbol_y)])])
                assign_from = f_mul([f_component(f_basisvalue, ss),
                                     f_group(C)])
                code += [f_assign(assign_to, assign_from)]

            # Only active is embedded_degree > 1.
            if embedded_degree > 1:
                # FIAT_NEW code (loop 3 in FIAT).
                # for p in range(n-1):
                #    for q in range(1,n-p):
                #        (a1,a2,a3) = jrc(2*p+1,0,q)
                #        results[idx(p,q+1),:] \
                #            = ( a1 * y + a2 ) * results[idx(p,q)] \
                #            - a3 * results[idx(p,q-1)]

                for r in range(0, embedded_degree - 1):
                    for s in range(1, embedded_degree - r):
                        rr = _idx2d(r, (s + 1))
                        ss = _idx2d(r, s)
                        tt = _idx2d(r, s - 1)
                        A, B, C = _jrc(2*r + 1, 0, s)
                        assign_to = f_component(f_basisvalue, rr)
                        assign_from = f_sub([f_mul([f_component(f_basisvalue, ss), f_group(f_add([f_float(B), f_mul([str(symbol_y), f_float(A)])]))]),
                                             f_mul([f_component(f_basisvalue, tt), f_float(C)])])
                        code += [f_assign(assign_to, assign_from)]

            # FIAT_NEW code (loop 4 in FIAT).
            # for p in range(n+1):
            #    for q in range(n-p+1):
            #        results[idx(p,q),:] *= math.sqrt((p+0.5)*(p+q+1.0))
            n1 = embedded_degree + 1
            for r in range(0, n1):
                for s in range(0, n1 - r):
                    rr = _idx2d(r, s)
                    A = (r + 0.5)*(r + s + 1)
                    assign_to = f_component(f_basisvalue, rr)
                    code += [f_imul(assign_to, f_sqrt(A))]

    # 3D
    elif (element_cellname == "tetrahedron"):

        # FIAT_NEW code (compute index function) TetrahedronExpansionSet.
        # def idx(p,q,r):
        #     return (p+q+r)*(p+q+r+1)*(p+q+r+2)//6 + (q+r)*(q+r+1)//2 + r
        def _idx3d(p, q, r):
            return (p+q+r)*(p+q+r+1)*(p+q+r+2)//6 + (q+r)*(q+r+1)//2 + r

        # FIAT_NEW.expansions.TetrahedronExpansionSet.

        # Compute helper factors.
        # FIAT_NEW code
        # factor1 = 0.5 * ( 2.0 + 2.0*x + y + z )
        # factor2 = (0.5*(y+z))**2
        # factor3 = 0.5 * ( 1 + 2.0 * y + z )
        # factor4 = 0.5 * ( 1 - z )
        # factor5 = factor4 ** 2
        fac1 = create_product([float_0_5, float_2 + float_2*symbol_x + symbol_y + symbol_z])
        fac2 = create_product([float_0_25, symbol_y + symbol_z, symbol_y + symbol_z])
        fac3 = create_product([float_0_5, float_1 + float_2*symbol_y + symbol_z])
        fac4 = create_product([float_0_5, float_1 - symbol_z])
        code += [f_decl(f_double, str(f1), fac1)]
        code += [f_decl(f_double, str(f2), fac2)]
        code += [f_decl(f_double, str(f3), fac3)]
        code += [f_decl(f_double, str(f4), fac4)]
        code += [f_decl(f_double, str(f5), f4*f4)]

        code += ["", f_comment("Compute basisvalues")]
        # The initial value basisvalue 0 is always 1.0.
        # FIAT_NEW code
        # for ii in range( results.shape[1] ):
        #    results[0,ii] = 1.0 + apts[ii,0]-apts[ii,0]+apts[ii,1]-apts[ii,1]
        code += [f_assign(f_component(f_basisvalue, 0), f_float(1.0))]

        # Only continue if the embedded degree is larger than zero.
        if embedded_degree > 0:

            # The initial value of basisfunction 1 is equal to f1.
            # FIAT_NEW code
            # results[idx(1,0),:] = f1
            code += [f_assign(f_component(f_basisvalue, 1), str(f1))]

            # NOTE: KBO: The order of the loops is VERY IMPORTANT!!
            # Only active is embedded_degree > 1
            if embedded_degree > 1:

                # FIAT_NEW code (loop 1 in FIAT).
                # for p in range(1,n):
                #    a1 = ( 2.0 * p + 1.0 ) / ( p + 1.0 )
                #    a2 = p / (p + 1.0)
                #    results[idx(p+1,0,0)] = a1 * factor1 * results[idx(p,0,0)] \
                #        -a2 * factor2 * results[ idx(p-1,0,0) ]
                for r in range(1, embedded_degree):
                    rr = _idx3d((r + 1), 0, 0)
                    ss = _idx3d(r, 0, 0)
                    tt = _idx3d((r - 1), 0, 0)
                    A = (2*r + 1.0)/(r + 1)
                    B = r/(r + 1.0)
                    assign_to = f_component(f_basisvalue, rr)
                    assign_from = f_sub([f_mul([f_float(A), str(f1), f_component(f_basisvalue, ss)]), f_mul([f_float(B), str(f2), f_component(f_basisvalue, tt)])])
                    code += [f_assign(assign_to, assign_from)]

            # FIAT_NEW code (loop 2 in FIAT).
            # q = 1
            # for p in range(0,n):
            #    results[idx(p,1,0)] = results[idx(p,0,0)] \
            #        * ( p * (1.0 + y) + ( 2.0 + 3.0 * y + z ) / 2 )

            for r in range(0, embedded_degree):
                rr = _idx3d(r, 1, 0)
                ss = _idx3d(r, 0, 0)
                assign_to = f_component(f_basisvalue, rr)
                term0 = f_mul([f_float(0.5), f_group(f_add([f_float(2), f_mul([f_float(3), str(symbol_y)]), str(symbol_z)]))])
                if r == 0:
                    assign_from = f_mul([term0, f_component(f_basisvalue, ss)])
                else:
                    term1 = f_mul([f_float(r), f_group(f_add([f_float(1), str(symbol_y)]))])
                    assign_from = f_mul([f_group(f_add([term0, term1])), f_component(f_basisvalue, ss)])
                code += [f_assign(assign_to, assign_from)]

            # Only active is embedded_degree > 1.
            if embedded_degree > 1:
                # FIAT_NEW code (loop 3 in FIAT).
                # for p in range(0,n-1):
                #    for q in range(1,n-p):
                #        (aq,bq,cq) = jrc(2*p+1,0,q)
                #        qmcoeff = aq * factor3 + bq * factor4
                #        qm1coeff = cq * factor5
                #        results[idx(p,q+1,0)] = qmcoeff * results[idx(p,q,0)] \
                #            - qm1coeff * results[idx(p,q-1,0)]

                for r in range(0, embedded_degree - 1):
                    for s in range(1, embedded_degree - r):
                        rr = _idx3d(r, (s + 1), 0)
                        ss = _idx3d(r, s, 0)
                        tt = _idx3d(r, s - 1, 0)
                        (A, B, C) = _jrc(2*r + 1, 0, s)
                        assign_to = f_component(f_basisvalue, rr)
                        term0 = f_mul([f_group(f_add([f_mul([f_float(A), str(f3)]), f_mul([f_float(B), str(f4)])])), f_component(f_basisvalue, ss)])
                        term1 = f_mul([f_float(C), str(f5), f_component(f_basisvalue, tt)])
                        assign_from = f_sub([term0, term1])
                        code += [f_assign(assign_to, assign_from)]

            # FIAT_NEW code (loop 4 in FIAT).
            # now handle r=1
            # for p in range(n):
            #    for q in range(n-p):
            #        results[idx(p,q,1)] = results[idx(p,q,0)] \
            #            * ( 1.0 + p + q + ( 2.0 + q + p ) * z )
            for r in range(0, embedded_degree):
                for s in range(0, embedded_degree - r):
                    rr = _idx3d(r, s, 1)
                    ss = _idx3d(r, s, 0)
                    assign_to = f_component(f_basisvalue, rr)
                    A = f_add([f_mul([f_float(2 + r + s), str(symbol_z)]), f_float(1 + r + s)])
                    assign_from = f_mul([f_group(A), f_component(f_basisvalue, ss)])
                    code += [f_assign(assign_to, assign_from)]

            # Only active is embedded_degree > 1.
            if embedded_degree > 1:
                # FIAT_NEW code (loop 5 in FIAT).
                # general r by recurrence
                # for p in range(n-1):
                #     for q in range(0,n-p-1):
                #         for r in range(1,n-p-q):
                #             ar,br,cr = jrc(2*p+2*q+2,0,r)
                #             results[idx(p,q,r+1)] = \
                #                         (ar * z + br) * results[idx(p,q,r) ] \
                #                         - cr * results[idx(p,q,r-1) ]
                for r in range(embedded_degree - 1):
                    for s in range(0, embedded_degree - r - 1):
                        for t in range(1, embedded_degree - r - s):
                            rr = _idx3d(r, s, ( t + 1))
                            ss = _idx3d(r, s, t)
                            tt = _idx3d(r, s, t - 1)

                            (A, B, C) = _jrc(2*r + 2*s + 2, 0, t)
                            assign_to = f_component(f_basisvalue, rr)
                            az_b = f_group(f_add([f_float(B), f_mul([f_float(A), str(symbol_z)])]))
                            assign_from = f_sub([f_mul([f_component(f_basisvalue, ss), az_b]), f_mul([f_float(C), f_component(f_basisvalue, tt)])])
                            code += [f_assign(assign_to, assign_from)]

            # FIAT_NEW code (loop 6 in FIAT).
            # for p in range(n+1):
            #    for q in range(n-p+1):
            #        for r in range(n-p-q+1):
            #            results[idx(p,q,r)] *= math.sqrt((p+0.5)*(p+q+1.0)*(p+q+r+1.5))
            for r in range(embedded_degree + 1):
                for s in range(embedded_degree - r + 1):
                    for t in range(embedded_degree - r - s + 1):
                        rr = _idx3d(r, s, t)
                        A = (r + 0.5)*(r + s + 1)*(r + s + t + 1.5)
                        assign_to = f_component(f_basisvalue, rr)
                        multiply_by = f_sqrt(A)
                        myline = f_imul(assign_to, multiply_by)
                        code += [myline]

    else:
        error("Cannot compute basis values for shape: %d" % elemet_cell_domain)

    return code + [""]
def _extract_variables(val, basis_consts, ip_consts, geo_consts, t_set, optimisation):
    f_G = format["geometry constant"]
    f_I = format["ip constant"]
    f_B = format["basis constant"]

    if val._prec == 0:
        return val
    elif val._prec == 1:
        if val.base_expr is None:
            return val
        new_base = _extract_variables(val.base_expr, basis_consts, ip_consts, geo_consts, t_set, optimisation)
        new_sym = create_symbol(val.v, val.t, new_base, val.base_op)
        if new_sym.t == BASIS:
            return _reduce_expression(new_sym, [], basis_consts, f_B, True)
        elif new_sym.t == IP:
            return _reduce_expression(new_sym, [], ip_consts, f_I, True)
        elif new_sym.t == GEO:
            return _reduce_expression(new_sym, [], geo_consts, f_G, True)
    # First handle child classes of product and sum.
    elif val._prec in (2, 3):
        new_vars = []
        for v in val.vrs:
            new_vars.append(_extract_variables(v, basis_consts, ip_consts, geo_consts, t_set, optimisation))
        if val._prec == 2:
            new_val = create_product(new_vars)
        if val._prec == 3:
            new_val = create_sum(new_vars)
    elif val._prec == 4:
        num = _extract_variables(val.num, basis_consts, ip_consts, geo_consts, t_set, optimisation)
        denom = _extract_variables(val.denom, basis_consts, ip_consts, geo_consts, t_set, optimisation)
        return create_fraction(num, denom)
    else:
        error("Unknown symbolic type: %s" % repr(val))

    # Sort variables of product and sum.
    b_c, i_c, g_c = [], [], []
    for v in new_val.vrs:
        if v.t == BASIS:
            if optimisation == "precompute_basis_const":
                b_c.append(v)
        elif v.t == IP:
            i_c.append(v)
        else:
            g_c.append(v)
    vrs = new_val.vrs[:]
    for v in g_c + i_c + b_c:
        vrs.remove(v)
    i_c.extend(_reduce_expression(new_val, g_c, geo_consts, f_G))
    vrs.extend(_reduce_expression(new_val, i_c, ip_consts, f_I))
    vrs.extend(_reduce_expression(new_val, b_c, basis_consts, f_B))

#    print "b_c: "
#    for b in b_c:
#        print b
#    print "basis"
#    for k,v in basis_consts.items():
#        print "k: ", k
#        print "v: ", v
#    print "geo"
#    for k,v in geo_consts.items():
#        print "k: ", k
#        print "v: ", v
#    print "ret val: ", val

    if len(vrs) > 1:
        if new_val._prec == 2:
            new_object = create_product(vrs)
        elif new_val._prec == 3:
            new_object = create_sum(vrs)
        else:
            error("Must have product or sum here: %s" % repr(new_val))
        if new_object.t == BASIS:
            if optimisation == "precompute_ip_const":
                return new_object
            elif optimisation == "precompute_basis_const":
                return _reduce_expression(new_object, [], basis_consts, f_B, True)
        elif new_object.t == IP:
            return _reduce_expression(new_object, [], ip_consts, f_I, True)
        elif new_object.t == GEO:
            return _reduce_expression(new_object, [], geo_consts, f_G, True)
    return vrs[0]