示例#1
0
def accumulate_integrands_with_same_metadata(integrals):
    """
    Taking input on the form:
        integrals = [integral0, integral1, ...]

    Return result on the form:
        integrands_by_id = [(integrand0, metadata0),
                            (integrand1, metadata1), ...]

    where integrand0 < integrand1 by the canonical ufl expression ordering criteria.
    """
    # Group integrals by compiler data hash
    by_cdid = {}
    for itg in integrals:
        cd = itg.metadata()
        cdid = hash(canonicalize_metadata(cd))
        if cdid not in by_cdid:
            by_cdid[cdid] = ([], cd)
        by_cdid[cdid][0].append(itg)

    # Accumulate integrands separately for each compiler data object
    # id
    for cdid in by_cdid:
        integrals, cd = by_cdid[cdid]
        # Ensure canonical sorting of more than two integrands
        integrands = sorted_expr((itg.integrand() for itg in integrals))
        integrands_sum = sum(integrands[1:], integrands[0])
        by_cdid[cdid] = (integrands_sum, cd)

    # Sort integrands canonically by integrand first then compiler
    # data
    return sorted(by_cdid.values(), key=ExprTupleKey)
示例#2
0
def accumulate_integrands_with_same_metadata(integrals):
    """
    Taking input on the form:
        integrals = [integral0, integral1, ...]

    Return result on the form:
        integrands_by_id = [(integrand0, metadata0),
                            (integrand1, metadata1), ...]

    where integrand0 < integrand1 by the canonical ufl expression ordering criteria.
    """
    # Group integrals by compiler data hash
    by_cdid = {}
    for itg in integrals:
        cd = itg.metadata()
        cdid = hash(canonicalize_metadata(cd))
        if cdid not in by_cdid:
            by_cdid[cdid] = ([], cd)
        by_cdid[cdid][0].append(itg)

    # Accumulate integrands separately for each compiler data object
    # id
    for cdid in by_cdid:
        integrals, cd = by_cdid[cdid]
        # Ensure canonical sorting of more than two integrands
        integrands = sorted_expr((itg.integrand() for itg in integrals))
        integrands_sum = sum(integrands[1:], integrands[0])
        by_cdid[cdid] = (integrands_sum, cd)

    # Sort integrands canonically by integrand first then compiler
    # data
    return sorted(by_cdid.values(), key=ExprTupleKey)
示例#3
0
def canonicalize_sub_integral_data(sub_integrals):
    for did in sub_integrals:
        # Group integrals by compiler data object id
        by_cdid = {}
        for itg in sub_integrals[did]:
            cd = itg.compiler_data()
            cdid = id(cd)
            if cdid in by_cdid:
                by_cdid[cdid][0].append(itg)
            else:
                by_cdid[cdid] = ([itg], cd)

        # Accumulate integrands separately for each compiler data object id
        for cdid in by_cdid:
            integrals, cd = by_cdid[cdid]
            # Ensure canonical sorting of more than two integrands
            integrands = sorted_expr((itg.integrand() for itg in integrals))
            integrands_sum = sum(integrands[1:], integrands[0])
            by_cdid[cdid] = (integrands_sum, cd)

        # Sort integrands canonically by integrand first then compiler data
        sub_integrals[did] = sorted(by_cdid.values(), key=expr_tuple_key)
        # i.e. the result is on the form:
        #sub_integrals[did][:] = [(integrand0, compiler_data0), (integrand1, compiler_data1), ...]
        # where integrand0 < integrand1 by the canonical ufl expression ordering criteria.

    return sub_integrals
示例#4
0
def derivative(form, coefficient, argument=None, coefficient_derivatives=None):
    """UFL form operator:
    Compute the Gateaux derivative of *form* w.r.t. *coefficient* in direction
    of *argument*.

    If the argument is omitted, a new ``Argument`` is created
    in the same space as the coefficient, with argument number
    one higher than the highest one in the form.

    The resulting form has one additional ``Argument``
    in the same finite element space as the coefficient.

    A tuple of ``Coefficient`` s may be provided in place of
    a single ``Coefficient``, in which case the new ``Argument``
    argument is based on a ``MixedElement`` created from this tuple.

    An indexed ``Coefficient`` from a mixed space may be provided,
    in which case the argument should be in the corresponding
    subspace of the coefficient space.

    If provided, *coefficient_derivatives* should be a mapping from
    ``Coefficient`` instances to their derivatives w.r.t. *coefficient*.
    """

    coefficients, arguments = _handle_derivative_arguments(form, coefficient,
                                                           argument)

    if coefficient_derivatives is None:
        coefficient_derivatives = ExprMapping()
    else:
        cd = []
        for k in sorted_expr(coefficient_derivatives.keys()):
            cd += [as_ufl(k), as_ufl(coefficient_derivatives[k])]
        coefficient_derivatives = ExprMapping(*cd)

    # Got a form? Apply derivatives to the integrands in turn.
    if isinstance(form, Form):
        integrals = []
        for itg in form.integrals():
            if not isinstance(coefficient, SpatialCoordinate):
                fd = CoefficientDerivative(itg.integrand(), coefficients,
                                           arguments, coefficient_derivatives)
            else:
                fd = CoordinateDerivative(itg.integrand(), coefficients,
                                          arguments, coefficient_derivatives)
            integrals.append(itg.reconstruct(fd))
        return Form(integrals)

    elif isinstance(form, Expr):
        # What we got was in fact an integrand
        if not isinstance(coefficient, SpatialCoordinate):
            return CoefficientDerivative(form, coefficients,
                                         arguments, coefficient_derivatives)
        else:
            return CoordinateDerivative(form, coefficients,
                                        arguments, coefficient_derivatives)

    error("Invalid argument type %s." % str(type(form)))
示例#5
0
def derivative(form, coefficient, argument=None, coefficient_derivatives=None):
    """UFL form operator:
    Compute the Gateaux derivative of *form* w.r.t. *coefficient* in direction
    of *argument*.

    If the argument is omitted, a new ``Argument`` is created
    in the same space as the coefficient, with argument number
    one higher than the highest one in the form.

    The resulting form has one additional ``Argument``
    in the same finite element space as the coefficient.

    A tuple of ``Coefficient`` s may be provided in place of
    a single ``Coefficient``, in which case the new ``Argument``
    argument is based on a ``MixedElement`` created from this tuple.

    An indexed ``Coefficient`` from a mixed space may be provided,
    in which case the argument should be in the corresponding
    subspace of the coefficient space.

    If provided, *coefficient_derivatives* should be a mapping from
    ``Coefficient`` instances to their derivatives w.r.t. *coefficient*.
    """

    coefficients, arguments = _handle_derivative_arguments(form, coefficient,
                                                           argument)

    if coefficient_derivatives is None:
        coefficient_derivatives = ExprMapping()
    else:
        cd = []
        for k in sorted_expr(coefficient_derivatives.keys()):
            cd += [as_ufl(k), as_ufl(coefficient_derivatives[k])]
        coefficient_derivatives = ExprMapping(*cd)

    # Got a form? Apply derivatives to the integrands in turn.
    if isinstance(form, Form):
        integrals = []
        for itg in form.integrals():
            if not isinstance(coefficient, SpatialCoordinate):
                fd = CoefficientDerivative(itg.integrand(), coefficients,
                                           arguments, coefficient_derivatives)
            else:
                fd = CoordinateDerivative(itg.integrand(), coefficients,
                                          arguments, coefficient_derivatives)
            integrals.append(itg.reconstruct(fd))
        return Form(integrals)

    elif isinstance(form, Expr):
        # What we got was in fact an integrand
        if not isinstance(coefficient, SpatialCoordinate):
            return CoefficientDerivative(form, coefficients,
                                         arguments, coefficient_derivatives)
        else:
            return CoordinateDerivative(form, coefficients,
                                        arguments, coefficient_derivatives)

    error("Invalid argument type %s." % str(type(form)))
示例#6
0
    def __init__(self, a, b):
        # sort operands for unique representation, must be independent
        # of various counts etc.  as explained in cmp_expr
        a, b = sorted_expr((a, b))

        CompoundTensorOperator.__init__(self, (a, b))

        fi, fid = merge_nonoverlapping_indices(a, b)
        self.ufl_free_indices = fi
        self.ufl_index_dimensions = fid
示例#7
0
    def __init__(self, a, b):
        CompoundTensorOperator.__init__(self)

        # sort operands for unique representation,
        # must be independent of various counts etc.
        # as explained in cmp_expr
        a, b = sorted_expr((a,b))

        # old version, slow and unsafe:
        #a, b = sorted((a,b), key = lambda x: repr(x))

        self._a = a
        self._b = b
        self._free_indices, self._index_dimensions = merge_indices(a, b)
示例#8
0
    def __new__(cls, a, b):
        # Make sure everything is an Expr
        a = as_ufl(a)
        b = as_ufl(b)

        # Assert consistent tensor properties
        sh = a.ufl_shape
        fi = a.ufl_free_indices
        fid = a.ufl_index_dimensions
        if b.ufl_shape != sh:
            error("Can't add expressions with different shapes.")
        if b.ufl_free_indices != fi:
            error("Can't add expressions with different free indices.")
        if b.ufl_index_dimensions != fid:
            error("Can't add expressions with different index dimensions.")

        # Skip adding zero
        if isinstance(a, Zero):
            return b
        elif isinstance(b, Zero):
            return a

        # Handle scalars specially and sort operands
        sa = isinstance(a, ScalarValue)
        sb = isinstance(b, ScalarValue)
        if sa and sb:
            # Apply constant propagation
            return as_ufl(a._value + b._value)
        elif sa:
            # Place scalar first
            # operands = (a, b)
            pass  # a, b = a, b
        elif sb:
            # Place scalar first
            # operands = (b, a)
            a, b = b, a
        # elif a == b:
        #    # Replace a+b with 2*foo
        #    return 2*a
        else:
            # Otherwise sort operands in a canonical order
            # operands = (b, a)
            a, b = sorted_expr((a, b))

        # construct and initialize a new Sum object
        self = Operator.__new__(cls)
        self._init(a, b)
        return self
示例#9
0
    def __new__(cls, a, b):
        # Conversion
        a = as_ufl(a)
        b = as_ufl(b)

        # Type checking
        # Make sure everything is scalar
        if a.ufl_shape or b.ufl_shape:
            error("Product can only represent products of scalars, "
                  "got\n\t%s\nand\n\t%s" % (ufl_err_str(a), ufl_err_str(b)))

        # Simplification
        if isinstance(a, Zero) or isinstance(b, Zero):
            # Got any zeros? Return zero.
            fi, fid = merge_unique_indices(a.ufl_free_indices,
                                           a.ufl_index_dimensions,
                                           b.ufl_free_indices,
                                           b.ufl_index_dimensions)
            return Zero((), fi, fid)
        sa = isinstance(a, ScalarValue)
        sb = isinstance(b, ScalarValue)
        if sa and sb:  # const * const = const
            # FIXME: Handle free indices like with zero? I think
            # IntValue may be index annotated now?
            return as_ufl(a._value * b._value)
        elif sa:  # 1 * b = b
            if a._value == 1:
                return b
            # a, b = a, b
        elif sb:  # a * 1 = a
            if b._value == 1:
                return a
            a, b = b, a
        # elif a == b: # a * a = a**2 # TODO: Why? Maybe just remove this?
        #    if not a.ufl_free_indices:
        #        return a**2
        else:  # a * b = b * a
            # Sort operands in a semi-canonical order
            # (NB! This is fragile! Small changes here can have large effects.)
            a, b = sorted_expr((a, b))

        # Construction
        self = Operator.__new__(cls)
        self._init(a, b)
        return self
示例#10
0
    def __new__(cls, a, b):
        # Checks
        ash, bsh = a.ufl_shape, b.ufl_shape
        if ash != bsh:
            error("Shapes do not match: %s and %s." % (ufl_err_str(a), ufl_err_str(b)))

        # Simplification
        if isinstance(a, Zero) or isinstance(b, Zero):
            fi, fid = merge_nonoverlapping_indices(a, b)
            return Zero((), fi, fid)
        elif ash == ():
            return a*Conj(b)
        # sort operands for unique representation,
        # must be independent of various counts etc.
        # as explained in cmp_expr
        if (a, b) != tuple(sorted_expr((a, b))):
            return Conj(Inner(b, a))

        return CompoundTensorOperator.__new__(cls)
示例#11
0
    def __new__(cls, a, b):
        # Checks
        ash, bsh = a.ufl_shape, b.ufl_shape
        if ash != bsh:
            error("Shapes do not match: %s and %s." % (ufl_err_str(a), ufl_err_str(b)))

        # Simplification
        if isinstance(a, Zero) or isinstance(b, Zero):
            fi, fid = merge_nonoverlapping_indices(a, b)
            return Zero((), fi, fid)
        elif ash == ():
            return a * Conj(b)
        # sort operands for unique representation,
        # must be independent of various counts etc.
        # as explained in cmp_expr
        if (a, b) != tuple(sorted_expr((a, b))):
            return Conj(Inner(b, a))

        return CompoundTensorOperator.__new__(cls)
示例#12
0
    def __new__(cls, *operands): # TODO: This whole thing seems a bit complicated... Can it be simplified? Maybe we can merge some loops for efficiency?
        ufl_assert(operands, "Can't take sum of nothing.")
        #if not operands:
        #    return Zero() # Allowing this leads to zeros with invalid type information in other places, need indices and shape

        # make sure everything is an Expr
        operands = [as_ufl(o) for o in operands]

        # Got one operand only? Do nothing then.
        if len(operands) == 1:
            return operands[0]

        # assert consistent tensor properties
        sh = operands[0].shape()
        fi = operands[0].free_indices()
        fid = operands[0].index_dimensions()
        #ufl_assert(all(sh == o.shape() for o in operands[1:]),
        #    "Shape mismatch in Sum.")
        #ufl_assert(not any((set(fi) ^ set(o.free_indices())) for o in operands[1:]),
        #    "Can't add expressions with different free indices.")
        if any(sh != o.shape() for o in operands[1:]):
            error("Shape mismatch in Sum.")
        if any((set(fi) ^ set(o.free_indices())) for o in operands[1:]):
            error("Can't add expressions with different free indices.")

        # sort operands in a canonical order
        operands = sorted_expr(operands)

        # purge zeros
        operands = [o for o in operands if not isinstance(o, Zero)]

        # sort scalars to beginning and merge them
        scalars = [o for o in operands if isinstance(o, ScalarValue)]
        if scalars:
            # exploiting Pythons built-in coersion rules
            f = as_ufl(sum(f._value for f in scalars))
            nonscalars = [o for o in operands if not isinstance(o, ScalarValue)]
            if not nonscalars:
                return f
            if isinstance(f, Zero):
                operands = nonscalars
            else:
                operands = [f] + nonscalars

        # have we purged everything?
        if not operands:
            return Zero(sh, fi, fid)

        # left with one operand only?
        if len(operands) == 1:
            return operands[0]

        # Replace n-repeated operands foo with n*foo
        newoperands = []
        op = operands[0]
        n = 1
        for o in operands[1:] + [None]:
            if o == op:
                n += 1
            else:
                newoperands.append(op if n == 1 else n*op)
                op = o
                n = 1
        operands = newoperands

        # left with one operand only?
        if len(operands) == 1:
            return operands[0]

        # construct and initialize a new Sum object
        self = AlgebraOperator.__new__(cls)
        self._init(*operands)
        return self
示例#13
0
    def __new__(cls, *operands):
        # Make sure everything is an Expr
        operands = [as_ufl(o) for o in operands]

        # Make sure everything is scalar
        #ufl_assert(not any(o.shape() for o in operands),
        #    "Product can only represent products of scalars.")
        if any(o.shape() for o in operands):
            error("Product can only represent products of scalars.")

        # No operands? Return one.
        if not operands:
            return IntValue(1)

        # Got one operand only? Just return it.
        if len(operands) == 1:
            return operands[0]

        # Got any zeros? Return zero.
        if any(isinstance(o, Zero) for o in operands):
            free_indices     = unique_indices(tuple(chain(*(o.free_indices() for o in operands))))
            index_dimensions = subdict(mergedicts([o.index_dimensions() for o in operands]), free_indices)
            return Zero((), free_indices, index_dimensions)

        # Merge scalars, but keep nonscalars sorted
        scalars = []
        nonscalars = []
        for o in operands:
            if isinstance(o, ScalarValue):
                scalars.append(o)
            else:
                nonscalars.append(o)
        if scalars:
            # merge scalars
            p = as_ufl(product(s._value for s in scalars))
            # only scalars?
            if not nonscalars:
                return p
            # merged scalar is unity?
            if p == 1:
                scalars = []
                # Left with one nonscalar operand only after merging scalars?
                if len(nonscalars) == 1:
                    return nonscalars[0]
            else:
                scalars = [p]

        # Sort operands in a canonical order (NB! This is fragile! Small changes here can have large effects.)
        operands = scalars + sorted_expr(nonscalars)

        # Replace n-repeated operands foo with foo**n
        newoperands = []
        op, nop = operands[0], 1
        for o in operands[1:] + [None]:
            if o == op:
                # op is repeated, count number of repetitions
                nop += 1
            else:
                if nop == 1:
                    # op is not repeated
                    newoperands.append(op)
                elif op.free_indices():
                    # We can't simplify products to powers if the operands has
                    # free indices, because of complications in differentiation.
                    # op repeated, but has free indices, so we don't simplify
                    newoperands.extend([op]*nop)
                else:
                    # op repeated, make it a power
                    newoperands.append(op**nop)
                # Reset op as o
                op, nop = o, 1
        operands = newoperands

        # Left with one operand only after simplifications?
        if len(operands) == 1:
            return operands[0]

        # Construct and initialize a new Product object
        self = AlgebraOperator.__new__(cls)
        self._init(*operands)
        return self