예제 #1
0
    def __init__(self, *elements, **kwargs):
        "Create mixed finite element from given list of elements"

        # Un-nest arguments if we get a single argument with a list of elements
        if len(elements) == 1 and isinstance(elements[0], (tuple, list)):
            elements = elements[0]
        # Interpret nested tuples as sub-mixedelements recursively
        elements = [MixedElement(e) if isinstance(e, (tuple,list)) else e
                    for e in elements]
        self._sub_elements = elements

        # TODO: Figure out proper checks of domain consistency

        # FIXME: Do something if elements are defined on different regions
        domains = set(element.domain() for element in elements) - set((None,))
        top_domains = set(domain.top_domain() for domain in domains)
        if len(top_domains) == 0:
            domain = None
        elif len(top_domains) == 1:
            domain = list(top_domains)[0]
        else:
            # Require that all elements are defined on the same top-level domain
            # TODO: is this too strict? disallows mixed elements on different meshes
            error("Sub elements must live in the same top level domain.")

        # Check that domains have same geometric dimension
        if domain is not None:
            gdim = domain.geometric_dimension()
            ufl_assert(all(domain.geometric_dimension() == gdim for domain in domains),
                       "Sub elements must live in the same geometric dimension.")

        # Check that all elements use the same quadrature scheme
        # TODO: We can allow the scheme not to be defined.
        quad_scheme = elements[0].quadrature_scheme()
        ufl_assert(all(e.quadrature_scheme() == quad_scheme for e in elements),\
            "Quadrature scheme mismatch for sub elements of mixed element.")

        # Compute value shape
        value_size_sum = sum(product(s.value_shape()) for s in self._sub_elements)
        # Default value dimension: Treated simply as all subelement
        #                          values unpacked in a vector.
        value_shape = kwargs.get('value_shape', (value_size_sum,))
        # Validate value_shape
        if type(self) is MixedElement:
            # This is not valid for tensor elements with symmetries,
            # assume subclasses deal with their own validation
            ufl_assert(product(value_shape) == value_size_sum,
                "Provided value_shape doesn't match the total "\
                "value size of all subelements.")

        # Initialize element data
        degree = max(e.degree() for e in self._sub_elements)
        super(MixedElement, self).__init__("Mixed", domain, degree,
                                           quad_scheme, value_shape)

        # Cache repr string
        self._repr = "MixedElement(*%r, **{'value_shape': %r })" %\
            (self._sub_elements, self._value_shape)
예제 #2
0
    def extract_subelement_component(self, i):
        """Extract direct subelement index and subelement relative
        component index for a given component index"""
        if isinstance(i, int):
            i = (i,)
        self._check_component(i)

        # Select between indexing modes
        if len(self.value_shape()) == 1:
            # Indexing into a long vector of flattened subelement shapes
            j, = i

            # Find subelement for this index
            for k, e in enumerate(self._sub_elements):
                sh = e.value_shape()
                si = product(sh)
                if j < si:
                    break
                j -= si
            ufl_assert(j >= 0, "Moved past last value component!")

            # Convert index into a shape tuple
            j = index_to_component(j, sh)
        else:
            # Indexing into a multidimensional tensor
            # where subelement index is first axis
            k = i[0]
            ufl_assert(k < len(self._sub_elements),
                       "Illegal component index (dimension %d)." % k)
            j = i[1:]
        return (k, j)
예제 #3
0
 def symmetry(self):
     """Return the symmetry dict, which is a mapping c0 -> c1
     meaning that component c0 is represented by component c1.
     A component is a tuple of one or more ints."""
     # Build symmetry map from symmetries of subelements
     sm = {}
     # Base index of the current subelement into mixed value
     j = 0
     for e in self._sub_elements:
         sh = e.value_shape()
         # Map symmetries of subelement into index space of this element
         for c0, c1 in e.symmetry().iteritems():
             j0 = component_to_index(c0, sh) + j
             j1 = component_to_index(c1, sh) + j
             sm[(j0,)] = (j1,)
         # Update base index for next element
         j += product(sh)
     ufl_assert(j == product(self.value_shape()),
                "Size mismatch in symmetry algorithm.")
     return sm or EmptyDict
    def tensor_constant(self, o):
        #print("\n\nVisiting TensorConstant: " + repr(o))

        # Map o to object with proper element and numbering
        o = self._function_replace_map[o]

        # Get the components
        components = self.component()

        # Safety checks.
        ffc_assert(len(components) == len(o.shape()), \
                   "The number of components '%s' must be equal to the number of shapes '%s' for TensorConstant." % (repr(components), repr(o.shape())))

        # Let the UFL element handle the component map.
        component = o.element()._sub_element_mapping[components]

        # Handle restriction (offset by value shape).
        if self.restriction == "-":
            component += product(o.shape())

        # Let child class create constant symbol
        coefficient = format["coefficient"](o.count(), component)
        return self._create_symbol(coefficient, CONST)
예제 #5
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
예제 #6
0
def split(v):
    """UFL operator: If v is a Coefficient or Argument in a mixed space, returns
    a tuple with the function components corresponding to the subelements."""
    # Special case: simple element, just return function in a tuple
    element = v.element()
    if not isinstance(element, MixedElement):
        return (v, )

    if isinstance(element, TensorElement):
        s = element.symmetry()
        if s:
            # FIXME: How should this be defined? Should we return one subfunction
            # for each value component or only for those not mapped to another?
            # I think split should ignore the symmetry.
            error("Split not implemented for symmetric tensor elements.")

    # Compute value size
    value_size = product(element.value_shape())
    actual_value_size = value_size

    # Extract sub coefficient
    offset = 0
    sub_functions = []
    for i, e in enumerate(element.sub_elements()):
        shape = e.value_shape()
        rank = len(shape)

        if rank == 0:
            # This subelement is a scalar, always maps to a single value
            subv = v[offset]
            offset += 1

        elif rank == 1:
            # This subelement is a vector, always maps to a sequence of values
            sub_size, = shape
            components = [v[j] for j in range(offset, offset + sub_size)]
            subv = as_vector(components)
            offset += sub_size

        elif rank == 2:
            # This subelement is a tensor, possibly with symmetries, slightly more complicated...

            # Size of this subvalue
            sub_size = product(shape)

            # If this subelement is a symmetric element, subtract symmetric components
            s = None
            if isinstance(e, TensorElement):
                s = e.symmetry()
            s = s or EmptyDict
            # If we do this, we must fix the size computation in MixedElement.__init__ as well
            #actual_value_size -= len(s)
            #sub_size -= len(s)
            #print s
            # Build list of lists of value components
            components = []
            for ii in range(shape[0]):
                row = []
                for jj in range(shape[1]):
                    # Map component (i,j) through symmetry mapping
                    c = (ii, jj)
                    c = s.get(c, c)
                    i, j = c
                    # Extract component c of this subvalue from global tensor v
                    if v.rank() == 1:
                        # Mapping into a flattened vector
                        k = offset + i * shape[1] + j
                        component = v[k]
                        #print "k, offset, i, j, shape, component", k, offset, i, j, shape, component
                    elif v.rank() == 2:
                        # Mapping into a concatenated tensor (is this a figment of my imagination?)
                        error("Not implemented.")
                        row_offset, col_offset = 0, 0  # TODO
                        k = (row_offset + i, col_offset + j)
                        component = v[k]
                    row.append(component)
                components.append(row)

            # Make a matrix of the components
            subv = as_matrix(components)
            offset += sub_size

        else:
            # TODO: Handle rank > 2? Or is there such a thing?
            error(
                "Don't know how to split functions with sub functions of rank %d (yet)."
                % rank)
            #for indices in compute_indices(shape):
            #    #k = offset + sum(i*s for (i,s) in izip(indices, shape[1:] + (1,)))
            #    vs.append(v[indices])

        sub_functions.append(subv)

    ufl_assert(actual_value_size == offset,
               "Logic breach in function splitting.")

    return tuple(sub_functions)
def split(v):
    """UFL operator: If v is a Coefficient or Argument in a mixed space, returns
    a tuple with the function components corresponding to the subelements."""
    # Special case: simple element, just return function in a tuple
    element = v.element()
    if not isinstance(element, MixedElement):
        return (v,)

    if isinstance(element, TensorElement):
        s = element.symmetry()
        if s:
            # FIXME: How should this be defined? Should we return one subfunction
            # for each value component or only for those not mapped to another?
            # I think split should ignore the symmetry.
            error("Split not implemented for symmetric tensor elements.")

    # Compute value size
    value_size = product(element.value_shape())
    actual_value_size = value_size

    # Extract sub coefficient
    offset = 0
    sub_functions = []
    for i, e in enumerate(element.sub_elements()):
        shape = e.value_shape()
        rank = len(shape)

        if rank == 0:
            # This subelement is a scalar, always maps to a single value
            subv = v[offset]
            offset += 1

        elif rank == 1:
            # This subelement is a vector, always maps to a sequence of values
            sub_size, = shape
            components = [v[j] for j in range(offset, offset + sub_size)]
            subv = as_vector(components)
            offset += sub_size

        elif rank == 2:
            # This subelement is a tensor, possibly with symmetries, slightly more complicated...

            # Size of this subvalue
            sub_size = product(shape)

            # If this subelement is a symmetric element, subtract symmetric components
            s = None
            if isinstance(e, TensorElement):
                s = e.symmetry()
            s = s or EmptyDict
            # If we do this, we must fix the size computation in MixedElement.__init__ as well
            #actual_value_size -= len(s)
            #sub_size -= len(s)
            #print s
            # Build list of lists of value components
            components = []
            for ii in range(shape[0]):
                row = []
                for jj in range(shape[1]):
                    # Map component (i,j) through symmetry mapping
                    c = (ii, jj)
                    c = s.get(c, c)
                    i, j = c
                    # Extract component c of this subvalue from global tensor v
                    if v.rank() == 1:
                        # Mapping into a flattened vector
                        k = offset + i*shape[1] + j
                        component = v[k]
                        #print "k, offset, i, j, shape, component", k, offset, i, j, shape, component
                    elif v.rank() == 2:
                        # Mapping into a concatenated tensor (is this a figment of my imagination?)
                        error("Not implemented.")
                        row_offset, col_offset = 0, 0 # TODO
                        k = (row_offset + i, col_offset + j)
                        component = v[k]
                    row.append(component)
                components.append(row)

            # Make a matrix of the components
            subv = as_matrix(components)
            offset += sub_size

        else:
            # TODO: Handle rank > 2? Or is there such a thing?
            error("Don't know how to split functions with sub functions of rank %d (yet)." % rank)
            #for indices in compute_indices(shape):
            #    #k = offset + sum(i*s for (i,s) in izip(indices, shape[1:] + (1,)))
            #    vs.append(v[indices])

        sub_functions.append(subv)

    ufl_assert(actual_value_size == offset, "Logic breach in function splitting.")

    return tuple(sub_functions)