def generic_pseudo_inverse_expr(A):
    """Compute the Penrose-Moore pseudo-inverse of A: (A.T*A)^-1 * A.T."""
    i, j, k = indices(3)
    ATA = as_tensor(A[k, i] * A[k, j], (i, j))
    ATAinv = inverse_expr(ATA)
    q, r, s = indices(3)
    return as_tensor(ATAinv[r, q] * A[s, q], (r, s))
Exemple #2
0
 def dot(self, o, a, b):
     ai = indices(len(a.ufl_shape)-1)
     bi = indices(len(b.ufl_shape)-1)
     k = (Index(),)
     # Creates a single IndexSum over a Product
     s = a[ai+k]*b[k+bi]
     return as_tensor(s, ai+bi)
Exemple #3
0
def generic_pseudo_inverse_expr(A):
    """Compute the Penrose-Moore pseudo-inverse of A: (A.T*A)^-1 * A.T."""
    i, j, k = indices(3)
    ATA = as_tensor(A[k, i] * A[k, j], (i, j))
    ATAinv = inverse_expr(ATA)
    q, r, s = indices(3)
    return as_tensor(ATAinv[r, q] * A[s, q], (r, s))
 def dot(self, o, a, b):
     ai = indices(len(a.ufl_shape) - 1)
     bi = indices(len(b.ufl_shape) - 1)
     k = (Index(),)
     # Creates a single IndexSum over a Product
     s = a[ai + k] * b[k + bi]
     return as_tensor(s, ai + bi)
    def circumradius(self, o):
        if self._preserve_types[o._ufl_typecode_]:
            return o

        domain = o.ufl_domain()
        if not domain.is_piecewise_linear_simplex_domain():
            # Don't lower for non-affine cells, instead leave it to
            # form compiler
            warning(
                "Only know how to compute the circumradius of an affine cell.")
            return o

        cellname = domain.ufl_cell().cellname()
        cellvolume = self.cell_volume(CellVolume(domain))

        if cellname == "interval":
            r = 0.5 * cellvolume

        elif cellname == "triangle":
            J = self.jacobian(Jacobian(domain))
            trev = CellEdgeVectors(domain)
            num_edges = 3
            i, j, k = indices(3)
            elen = [
                sqrt((J[i, j] * trev[edge, j]) * (J[i, k] * trev[edge, k]))
                for edge in range(num_edges)
            ]

            r = (elen[0] * elen[1] * elen[2]) / (4.0 * cellvolume)

        elif cellname == "tetrahedron":
            J = self.jacobian(Jacobian(domain))
            trev = CellEdgeVectors(domain)
            num_edges = 6
            i, j, k = indices(3)
            elen = [
                sqrt((J[i, j] * trev[edge, j]) * (J[i, k] * trev[edge, k]))
                for edge in range(num_edges)
            ]

            # elen[3] = length of edge 3
            # la, lb, lc = lengths of the sides of an intermediate triangle
            la = elen[3] * elen[2]
            lb = elen[4] * elen[1]
            lc = elen[5] * elen[0]
            # p = perimeter
            p = (la + lb + lc)
            # s = semiperimeter
            s = p / 2
            # area of intermediate triangle with Herons formula
            triangle_area = sqrt(s * (s - la) * (s - lb) * (s - lc))
            r = triangle_area / (6.0 * cellvolume)

        else:
            error("Unhandled cell type %s." % cellname)

        return r
Exemple #6
0
 def jacobian_inverse(self, o):
     # grad(K) == K_ji rgrad(K)_rj
     if is_cellwise_constant(o):
         return self.independent_terminal(o)
     if not o._ufl_is_terminal_:
         error("ReferenceValue can only wrap a terminal")
     r = indices(len(o.ufl_shape))
     i, j = indices(2)
     Do = as_tensor(o[j, i] * ReferenceGrad(o)[r + (j, )], r + (i, ))
     return Do
Exemple #7
0
 def jacobian_inverse(self, o):
     # grad(K) == K_ji rgrad(K)_rj
     if is_cellwise_constant(o):
         return self.independent_terminal(o)
     if not o._ufl_is_terminal_:
         error("ReferenceValue can only wrap a terminal")
     r = indices(len(o.ufl_shape))
     i, j = indices(2)
     Do = as_tensor(o[j, i]*ReferenceGrad(o)[r + (j,)], r + (i,))
     return Do
Exemple #8
0
 def reference_value(self, o):
     # grad(o) == grad(rv(f)) -> K_ji*rgrad(rv(f))_rj
     f = o.ufl_operands[0]
     if not f._ufl_is_terminal_:
         error("ReferenceValue can only wrap a terminal")
     domain = f.ufl_domain()
     K = JacobianInverse(domain)
     r = indices(len(o.ufl_shape))
     i, j = indices(2)
     Do = as_tensor(K[j, i] * ReferenceGrad(o)[r + (j, )], r + (i, ))
     return Do
Exemple #9
0
 def reference_value(self, o):
     # grad(o) == grad(rv(f)) -> K_ji*rgrad(rv(f))_rj
     f = o.ufl_operands[0]
     if not f._ufl_is_terminal_:
         error("ReferenceValue can only wrap a terminal")
     domain = f.ufl_domain()
     K = JacobianInverse(domain)
     r = indices(len(o.ufl_shape))
     i, j = indices(2)
     Do = as_tensor(K[j, i]*ReferenceGrad(o)[r + (j,)], r + (i,))
     return Do
Exemple #10
0
def apply_known_single_pullback(r, element):
    """Apply pullback with given mapping.

    :arg r: Expression wrapped in ReferenceValue
    :arg element: The element defining the mapping
    """
    # Need to pass in r rather than the physical space thing, because
    # the latter may be a ListTensor or similar, rather than a
    # Coefficient/Argument (in the case of mixed elements, see below
    # in apply_single_function_pullbacks), to which we cannot apply ReferenceValue
    mapping = element.mapping()
    domain = r.ufl_domain()
    if mapping == "physical":
        return r
    elif mapping == "identity":
        return r
    elif mapping == "contravariant Piola":
        J = Jacobian(domain)
        detJ = JacobianDeterminant(J)
        transform = (1.0 / detJ) * J
        # Apply transform "row-wise" to TensorElement(PiolaMapped, ...)
        *k, i, j = indices(len(r.ufl_shape) + 1)
        kj = (*k, j)
        f = as_tensor(transform[i, j] * r[kj], (*k, i))
        return f
    elif mapping == "covariant Piola":
        K = JacobianInverse(domain)
        # Apply transform "row-wise" to TensorElement(PiolaMapped, ...)
        *k, i, j = indices(len(r.ufl_shape) + 1)
        kj = (*k, j)
        f = as_tensor(K[j, i] * r[kj], (*k, i))
        return f
    elif mapping == "L2 Piola":
        detJ = JacobianDeterminant(domain)
        return r / detJ
    elif mapping == "double contravariant Piola":
        J = Jacobian(domain)
        detJ = JacobianDeterminant(J)
        transform = (1.0 / detJ) * J
        # Apply transform "row-wise" to TensorElement(PiolaMapped, ...)
        *k, i, j, m, n = indices(len(r.ufl_shape) + 2)
        kmn = (*k, m, n)
        f = as_tensor((1.0 / detJ)**2 * J[i, m] * r[kmn] * J[j, n], (*k, i, j))
        return f
    elif mapping == "double covariant Piola":
        K = JacobianInverse(domain)
        # Apply transform "row-wise" to TensorElement(PiolaMapped, ...)
        *k, i, j, m, n = indices(len(r.ufl_shape) + 2)
        kmn = (*k, m, n)
        f = as_tensor(K[m, i] * r[kmn] * K[n, j], (*k, i, j))
        return f
    else:
        error("Should never be reached!")
Exemple #11
0
 def reference_grad(self, o):
     # grad(o) == grad(rgrad(rv(f))) -> K_ji*rgrad(rgrad(rv(f)))_rj
     f = o.ufl_operands[0]
     valid_operand = f._ufl_is_in_reference_frame_ or isinstance(f, (JacobianInverse, SpatialCoordinate))
     if not valid_operand:
         error("ReferenceGrad can only wrap a reference frame type!")
     domain = f.ufl_domain()
     K = JacobianInverse(domain)
     r = indices(len(o.ufl_shape))
     i, j = indices(2)
     Do = as_tensor(K[j, i]*ReferenceGrad(o)[r + (j,)], r + (i,))
     return Do
Exemple #12
0
 def reference_grad(self, o):
     # grad(o) == grad(rgrad(rv(f))) -> K_ji*rgrad(rgrad(rv(f)))_rj
     f = o.ufl_operands[0]
     valid_operand = f._ufl_is_in_reference_frame_ or isinstance(f, (JacobianInverse, SpatialCoordinate))
     if not valid_operand:
         error("ReferenceGrad can only wrap a reference frame type!")
     domain = f.ufl_domain()
     K = JacobianInverse(domain)
     r = indices(len(o.ufl_shape))
     i, j = indices(2)
     Do = as_tensor(K[j, i]*ReferenceGrad(o)[r + (j,)], r + (i,))
     return Do
 def altenative_dot(self, o, a, b):  # TODO: Test this
     ash = a.ufl_shape
     bsh = b.ufl_shape
     ai = indices(len(ash) - 1)
     bi = indices(len(bsh) - 1)
     # Simplification for tensors where the dot-sum dimension has
     # length 1
     if ash[-1] == 1:
         k = (FixedIndex(0),)
     else:
         k = (Index(),)
     # Potentially creates a single IndexSum over a Product
     s = a[ai + k] * b[k + bi]
     return as_tensor(s, ai + bi)
Exemple #14
0
 def altenative_dot(self, o, a, b):  # TODO: Test this
     ash = a.ufl_shape
     bsh = b.ufl_shape
     ai = indices(len(ash) - 1)
     bi = indices(len(bsh) - 1)
     # Simplification for tensors where the dot-sum dimension has
     # length 1
     if ash[-1] == 1:
         k = (FixedIndex(0),)
     else:
         k = (Index(),)
     # Potentially creates a single IndexSum over a Product
     s = a[ai+k]*b[k+bi]
     return as_tensor(s, ai+bi)
Exemple #15
0
    def reference_value(self, o):
        # grad(o) == grad(rv(f)) -> K_ji*rgrad(rv(f))_rj
        f = o.ufl_operands[0]
        if f.ufl_element().mapping() == "physical":
            # TODO: Do we need to be more careful for immersed things?
            return ReferenceGrad(o)

        if not f._ufl_is_terminal_:
            error("ReferenceValue can only wrap a terminal")
        domain = f.ufl_domain()
        K = JacobianInverse(domain)
        r = indices(len(o.ufl_shape))
        i, j = indices(2)
        Do = as_tensor(K[j, i] * ReferenceGrad(o)[r + (j, )], r + (i, ))
        return Do
Exemple #16
0
    def indexed(self, o, Ap,
                ii):  # TODO: (Partially) duplicated in nesting rules
        # Propagate zeros
        if isinstance(Ap, Zero):
            return self.independent_operator(o)

        # Untangle as_tensor(C[kk], jj)[ii] -> C[ll] to simplify
        # resulting expression
        if isinstance(Ap, ComponentTensor):
            B, jj = Ap.ufl_operands
            if isinstance(B, Indexed):
                C, kk = B.ufl_operands
                kk = list(kk)
                if all(j in kk for j in jj):
                    rep = dict(zip(jj, ii))
                    Cind = [rep.get(k, k) for k in kk]
                    expr = Indexed(C, MultiIndex(tuple(Cind)))
                    assert expr.ufl_free_indices == o.ufl_free_indices
                    assert expr.ufl_shape == o.ufl_shape
                    return expr

        # Otherwise a more generic approach
        r = len(Ap.ufl_shape) - len(ii)
        if r:
            kk = indices(r)
            op = Indexed(Ap, MultiIndex(ii.indices() + kk))
            op = as_tensor(op, kk)
        else:
            op = Indexed(Ap, ii)
        return op
Exemple #17
0
    def indexed(self, o, Ap,
                ii):  # TODO: (Partially) duplicated in generic rules
        # Reuse if untouched
        if Ap is o.ufl_operands[0]:
            return o

        # Untangle as_tensor(C[kk], jj)[ii] -> C[ll] to simplify
        # resulting expression
        if isinstance(Ap, ComponentTensor):
            B, jj = Ap.ufl_operands
            if isinstance(B, Indexed):
                C, kk = B.ufl_operands

                kk = list(kk)
                if all(j in kk for j in jj):
                    Cind = list(kk)
                    for i, j in zip(ii, jj):
                        Cind[kk.index(j)] = i
                    return Indexed(C, MultiIndex(tuple(Cind)))

        # Otherwise a more generic approach
        r = len(Ap.ufl_shape) - len(ii)
        if r:
            kk = indices(r)
            op = Indexed(Ap, MultiIndex(ii.indices() + kk))
            op = as_tensor(op, kk)
        else:
            op = Indexed(Ap, ii)
        return op
Exemple #18
0
 def _make_identity(self, sh):
     "Create a higher order identity tensor to represent dv/dv."
     res = None
     if sh == ():
         # Scalar dv/dv is scalar
         return FloatValue(1.0)
     elif len(sh) == 1:
         # Vector v makes dv/dv the identity matrix
         return Identity(sh[0])
     else:
         # TODO: Add a type for this higher order identity?
         # II[i0,i1,i2,j0,j1,j2] = 1 if all((i0==j0, i1==j1, i2==j2)) else 0
         # Tensor v makes dv/dv some kind of higher rank identity tensor
         ind1 = ()
         ind2 = ()
         for d in sh:
             i, j = indices(2)
             dij = Identity(d)[i, j]
             if res is None:
                 res = dij
             else:
                 res *= dij
             ind1 += (i, )
             ind2 += (j, )
         fp = as_tensor(res, ind1 + ind2)
     return fp
    def max_facet_edge_length(self, o):
        if self._preserve_types[o._ufl_typecode_]:
            return o

        domain = o.ufl_domain()
        if not domain.is_piecewise_linear_simplex_domain():
            # Don't lower for non-affine cells, instead leave it to
            # form compiler
            warning(
                "Only know how to compute the max_facet_edge_length of an affine cell."
            )
            return o

        cellname = domain.ufl_cell().cellname()

        if cellname == "triangle":
            return self.facet_area(FacetArea(domain))
        elif cellname == "tetrahedron":
            J = self.jacobian(Jacobian(domain))
            trev = FacetEdgeVectors(domain)
            num_edges = 3
            i, j, k = indices(3)
            elen = [
                sqrt((J[i, j] * trev[edge, j]) * (J[i, k] * trev[edge, k]))
                for edge in range(num_edges)
            ]
            return max_value(elen[0], max_value(elen[1], elen[2]))
        else:
            error("Unhandled cell type %s." % cellname)
Exemple #20
0
 def _make_identity(self, sh):
     "Create a higher order identity tensor to represent dv/dv."
     res = None
     if sh == ():
         # Scalar dv/dv is scalar
         return FloatValue(1.0)
     elif len(sh) == 1:
         # Vector v makes dv/dv the identity matrix
         return Identity(sh[0])
     else:
         # TODO: Add a type for this higher order identity?
         # II[i0,i1,i2,j0,j1,j2] = 1 if all((i0==j0, i1==j1, i2==j2)) else 0
         # Tensor v makes dv/dv some kind of higher rank identity tensor
         ind1 = ()
         ind2 = ()
         for d in sh:
             i, j = indices(2)
             dij = Identity(d)[i, j]
             if res is None:
                 res = dij
             else:
                 res *= dij
             ind1 += (i,)
             ind2 += (j,)
         fp = as_tensor(res, ind1 + ind2)
     return fp
Exemple #21
0
    def indexed(self, o, Ap, ii):  # TODO: (Partially) duplicated in nesting rules
        # Propagate zeros
        if isinstance(Ap, Zero):
            return self.independent_operator(o)

        # Untangle as_tensor(C[kk], jj)[ii] -> C[ll] to simplify
        # resulting expression
        if isinstance(Ap, ComponentTensor):
            B, jj = Ap.ufl_operands
            if isinstance(B, Indexed):
                C, kk = B.ufl_operands
                kk = list(kk)
                if all(j in kk for j in jj):
                    Cind = list(kk)
                    for i, j in zip(ii, jj):
                        Cind[kk.index(j)] = i
                    return Indexed(C, MultiIndex(tuple(Cind)))

        # Otherwise a more generic approach
        r = len(Ap.ufl_shape) - len(ii)
        if r:
            kk = indices(r)
            op = Indexed(Ap, MultiIndex(ii.indices() + kk))
            op = as_tensor(op, kk)
        else:
            op = Indexed(Ap, ii)
        return op
 def nabla_grad(self, o, a):
     sh = a.ufl_shape
     if sh == ():
         return Grad(a)
     else:
         j = Index()
         ii = tuple(indices(len(sh)))
         return as_tensor(a[ii].dx(j), (j,) + ii)
Exemple #23
0
 def nabla_grad(self, o, a):
     sh = a.ufl_shape
     if sh == ():
         return Grad(a)
     else:
         j = Index()
         ii = tuple(indices(len(sh)))
         return as_tensor(a[ii].dx(j), (j,) + ii)
 def inner(self, o, a, b):
     ash = a.ufl_shape
     bsh = b.ufl_shape
     if ash != bsh:
         error("Nonmatching shapes.")
     ii = indices(len(ash))
     # Creates multiple IndexSums over a Product
     s = a[ii] * Conj(b[ii])
     return s
Exemple #25
0
 def inner(self, o, a, b):
     ash = a.ufl_shape
     bsh = b.ufl_shape
     if ash != bsh:
         error("Nonmatching shapes.")
     ii = indices(len(ash))
     # Creates multiple IndexSums over a Product
     s = a[ii]*Conj(b[ii])
     return s
    def facet_jacobian(self, o):
        if self._preserve_types[o._ufl_typecode_]:
            return o

        domain = o.ufl_domain()
        J = self.jacobian(Jacobian(domain))
        RFJ = CellFacetJacobian(domain)
        i, j, k = indices(3)
        return as_tensor(J[i, k] * RFJ[k, j], (i, j))
Exemple #27
0
def _div(self, o):
    if not isinstance(o, _valid_types):
        return NotImplemented
    sh = self.ufl_shape
    if sh:
        ii = indices(len(sh))
        d = Division(self[ii], o)
        return as_tensor(d, ii)
    return Division(self, o)
Exemple #28
0
    def facet_jacobian(self, o):
        if self._preserve_types[o._ufl_typecode_]:
            return o

        domain = o.ufl_domain()
        J = self.jacobian(Jacobian(domain))
        RFJ = CellFacetJacobian(domain)
        i, j, k = indices(3)
        return as_tensor(J[i, k]*RFJ[k, j], (i, j))
def grad_to_reference_grad(o, K):
    """Relates grad(o) to reference_grad(o) using the Jacobian inverse.

    Args
    ----
    o: Operand
    K: Jacobian inverse

    Returns
    -------
    Do: grad(o) written in terms of reference_grad(o) and K

    """
    r = indices(len(o.ufl_shape))
    i, j = indices(2)
    # grad(o) == K_ji rgrad(o)_rj
    Do = as_tensor(K[j, i] * ReferenceGrad(o)[r + (j,)], r + (i,))
    return Do
Exemple #30
0
def _div(self, o):
    if not isinstance(o, _valid_types):
        return NotImplemented
    sh = self.ufl_shape
    if sh:
        ii = indices(len(sh))
        d = Division(self[ii], o)
        return as_tensor(d, ii)
    return Division(self, o)
Exemple #31
0
def pseudo_inverse_expr(A):
    """Compute the Penrose-Moore pseudo-inverse of A: (A.T*A)^-1 * A.T."""
    m, n = A.ufl_shape
    if n == 1:
        # Simpler special case for 1d
        i, j, k = indices(3)
        return as_tensor(A[i, j], (j, i)) / (A[k, 0] * A[k, 0])
    else:
        # Generic formulation
        return generic_pseudo_inverse_expr(A)
def pseudo_inverse_expr(A):
    """Compute the Penrose-Moore pseudo-inverse of A: (A.T*A)^-1 * A.T."""
    m, n = A.ufl_shape
    if n == 1:
        # Simpler special case for 1d
        i, j, k = indices(3)
        return as_tensor(A[i, j], (j, i)) / (A[k, 0] * A[k, 0])
    else:
        # Generic formulation
        return generic_pseudo_inverse_expr(A)
Exemple #33
0
def as_scalars(*expressions):
    """Given multiple scalar or tensor valued expressions A, returns either of the tuples::

      (a,b) = (A, ())
      (a,b) = ([A[0][indices], ..., A[-1][indices]], indices)

    such that a is always a list of scalar valued expressions."""
    ii = indices(len(expressions[0].ufl_shape))
    if ii:
        expressions = [expression[ii] for expression in expressions]
    return expressions, ii
Exemple #34
0
def as_scalar(expression):
    """Given a scalar or tensor valued expression A, returns either of the tuples::

      (a,b) = (A, ())
      (a,b) = (A[indices], indices)

    such that a is always a scalar valued expression."""
    ii = indices(len(expression.ufl_shape))
    if ii:
        expression = expression[ii]
    return expression, ii
Exemple #35
0
def as_scalars(*expressions):
    """Given multiple scalar or tensor valued expressions A, returns either of the tuples::

      (a,b) = (A, ())
      (a,b) = ([A[0][indices], ..., A[-1][indices]], indices)

    such that a is always a list of scalar valued expressions."""
    ii = indices(len(expressions[0].ufl_shape))
    if ii:
        expressions = [expression[ii] for expression in expressions]
    return expressions, ii
Exemple #36
0
def as_scalar(expression):
    """Given a scalar or tensor valued expression A, returns either of the tuples::

      (a,b) = (A, ())
      (a,b) = (A[indices], indices)

    such that a is always a scalar valued expression."""
    ii = indices(len(expression.ufl_shape))
    if ii:
        expression = expression[ii]
    return expression, ii
Exemple #37
0
    def cell_coordinate(self, o):
        "Compute from physical coordinates if they are known, using the appropriate mappings."
        if self._preserve_types[o._ufl_typecode_]:
            return o

        domain = o.ufl_domain()
        K = self.jacobian_inverse(JacobianInverse(domain))
        x = self.spatial_coordinate(SpatialCoordinate(domain))
        x0 = CellOrigin(domain)
        i, j = indices(2)
        X = as_tensor(K[i, j] * (x[j] - x0[j]), (i,))
        return X
    def cell_coordinate(self, o):
        "Compute from physical coordinates if they are known, using the appropriate mappings."
        if self._preserve_types[o._ufl_typecode_]:
            return o

        domain = o.ufl_domain()
        K = self.jacobian_inverse(JacobianInverse(domain))
        x = self.spatial_coordinate(SpatialCoordinate(domain))
        x0 = CellOrigin(domain)
        i, j = indices(2)
        X = as_tensor(K[i, j] * (x[j] - x0[j]), (i, ))
        return X
Exemple #39
0
def contraction(a, a_axes, b, b_axes):
    "UFL operator: Take the contraction of a and b over given axes."
    ai, bi = a_axes, b_axes
    if len(ai) != len(bi):
        error("Contraction must be over the same number of axes.")
    ash = a.ufl_shape
    bsh = b.ufl_shape
    aii = indices(len(a.ufl_shape))
    bii = indices(len(b.ufl_shape))
    cii = indices(len(ai))
    shape = [None] * len(ai)
    for i, j in enumerate(ai):
        aii[j] = cii[i]
        shape[i] = ash[j]
    for i, j in enumerate(bi):
        bii[j] = cii[i]
        if shape[i] != bsh[j]:
            error("Shape mismatch in contraction.")
    s = a[aii] * b[bii]
    cii = set(cii)
    ii = tuple(i for i in (aii + bii) if i not in cii)
    return as_tensor(s, ii)
Exemple #40
0
 def compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp):
     # Apply gradients directly to argument vval, and get the
     # right indexed scalar component(s)
     kk = indices(ngrads)
     Dvkk = apply_grads(vval)[vcomp+kk]
     # Place scalar component(s) Dvkk into the right tensor
     # positions
     if wshape:
         Ejj, jj = unit_indexed_tensor(wshape, wcomp)
     else:
         Ejj, jj = 1, ()
     gprimeterm = as_tensor(Ejj*Dvkk, jj+kk)
     return gprimeterm
Exemple #41
0
def contraction(a, a_axes, b, b_axes):
    "UFL operator: Take the contraction of a and b over given axes."
    ai, bi = a_axes, b_axes
    if len(ai) != len(bi):
        error("Contraction must be over the same number of axes.")
    ash = a.ufl_shape
    bsh = b.ufl_shape
    aii = indices(len(a.ufl_shape))
    bii = indices(len(b.ufl_shape))
    cii = indices(len(ai))
    shape = [None] * len(ai)
    for i, j in enumerate(ai):
        aii[j] = cii[i]
        shape[i] = ash[j]
    for i, j in enumerate(bi):
        bii[j] = cii[i]
        if shape[i] != bsh[j]:
            error("Shape mismatch in contraction.")
    s = a[aii] * b[bii]
    cii = set(cii)
    ii = tuple(i for i in (aii + bii) if i not in cii)
    return as_tensor(s, ii)
Exemple #42
0
 def compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp):
     # Apply gradients directly to argument vval, and get the
     # right indexed scalar component(s)
     kk = indices(ngrads)
     Dvkk = apply_grads(vval)[vcomp + kk]
     # Place scalar component(s) Dvkk into the right tensor
     # positions
     if wshape:
         Ejj, jj = unit_indexed_tensor(wshape, wcomp)
     else:
         Ejj, jj = 1, ()
     gprimeterm = as_tensor(Ejj * Dvkk, jj + kk)
     return gprimeterm
Exemple #43
0
def unit_indexed_tensor(shape, component):
    from ufl.constantvalue import Identity
    from ufl.operators import outer  # a bit of circular dependency issue here
    r = len(shape)
    if r == 0:
        return 0, ()
    jj = indices(r)
    es = []
    for i in range(r):
        s = shape[i]
        c = component[i]
        j = jj[i]
        e = Identity(s)[c, j]
        es.append(e)
    E = es[0]
    for e in es[1:]:
        E = outer(E, e)
    return E, jj
Exemple #44
0
def unit_indexed_tensor(shape, component):
    from ufl.constantvalue import Identity
    from ufl.operators import outer  # a bit of circular dependency issue here
    r = len(shape)
    if r == 0:
        return 0, ()
    jj = indices(r)
    es = []
    for i in range(r):
        s = shape[i]
        c = component[i]
        j = jj[i]
        e = Identity(s)[c, j]
        es.append(e)
    E = es[0]
    for e in es[1:]:
        E = outer(E, e)
    return E, jj
    def facet_normal(self, o):
        if self._preserve_types[o._ufl_typecode_]:
            return o

        domain = o.ufl_domain()
        tdim = domain.topological_dimension()

        if tdim == 1:
            # Special-case 1D (possibly immersed), for which we say
            # that n is just in the direction of J.
            J = self.jacobian(Jacobian(domain))  # dx/dX
            ndir = J[:, 0]

            gdim = domain.geometric_dimension()
            if gdim == 1:
                nlen = abs(ndir[0])
            else:
                i = Index()
                nlen = sqrt(ndir[i] * ndir[i])

            rn = ReferenceNormal(domain)  # +/- 1.0 here
            n = rn[0] * ndir / nlen
            r = n
        else:
            # Recall that the covariant Piola transform u -> J^(-T)*u
            # preserves tangential components. The normal vector is
            # characterised by having zero tangential component in
            # reference and physical space.
            Jinv = self.jacobian_inverse(JacobianInverse(domain))
            i, j = indices(2)

            rn = ReferenceNormal(domain)
            # compute signed, unnormalised normal; note transpose
            ndir = as_vector(Jinv[j, i] * rn[j], i)

            # normalise
            i = Index()
            n = ndir / sqrt(ndir[i] * ndir[i])
            r = n

        if r.ufl_shape != o.ufl_shape:
            error("Inconsistent dimensions (in=%d, out=%d)." %
                  (o.ufl_shape[0], r.ufl_shape[0]))
        return r
Exemple #46
0
    def facet_normal(self, o):
        if self._preserve_types[o._ufl_typecode_]:
            return o

        domain = o.ufl_domain()
        tdim = domain.topological_dimension()

        if tdim == 1:
            # Special-case 1D (possibly immersed), for which we say
            # that n is just in the direction of J.
            J = self.jacobian(Jacobian(domain))  # dx/dX
            ndir = J[:, 0]

            gdim = domain.geometric_dimension()
            if gdim == 1:
                nlen = abs(ndir[0])
            else:
                i = Index()
                nlen = sqrt(ndir[i]*ndir[i])

            rn = ReferenceNormal(domain)  # +/- 1.0 here
            n = rn[0] * ndir / nlen
            r = n
        else:
            # Recall that the covariant Piola transform u -> J^(-T)*u
            # preserves tangential components. The normal vector is
            # characterised by having zero tangential component in
            # reference and physical space.
            Jinv = self.jacobian_inverse(JacobianInverse(domain))
            i, j = indices(2)

            rn = ReferenceNormal(domain)
            # compute signed, unnormalised normal; note transpose
            ndir = as_vector(Jinv[j, i] * rn[j], i)

            # normalise
            i = Index()
            n = ndir / sqrt(ndir[i]*ndir[i])
            r = n

        if r.ufl_shape != o.ufl_shape:
            error("Inconsistent dimensions (in=%d, out=%d)." % (o.ufl_shape[0], r.ufl_shape[0]))
        return r
def create_slice_indices(component, shape, fi):
    all_indices = []
    slice_indices = []
    repeated_indices = []
    free_indices = []

    for ind in component:
        if isinstance(ind, Index):
            all_indices.append(ind)
            if ind.count() in fi or ind in free_indices:
                repeated_indices.append(ind)
            free_indices.append(ind)
        elif isinstance(ind, FixedIndex):
            if int(ind) >= shape[len(all_indices)]:
                error("Index out of bounds.")
            all_indices.append(ind)
        elif isinstance(ind, int):
            if int(ind) >= shape[len(all_indices)]:
                error("Index out of bounds.")
            all_indices.append(FixedIndex(ind))
        elif isinstance(ind, slice):
            if ind != slice(None):
                error("Only full slices (:) allowed.")
            i = Index()
            slice_indices.append(i)
            all_indices.append(i)
        elif ind == Ellipsis:
            er = len(shape) - len(component) + 1
            ii = indices(er)
            slice_indices.extend(ii)
            all_indices.extend(ii)
        else:
            error("Not expecting {0}.".format(ind))

    if len(all_indices) != len(shape):
        error("Component and shape length don't match.")

    return tuple(all_indices), tuple(slice_indices), tuple(repeated_indices)
Exemple #48
0
def create_slice_indices(component, shape, fi):
    all_indices = []
    slice_indices = []
    repeated_indices = []
    free_indices = []

    for ind in component:
        if isinstance(ind, Index):
            all_indices.append(ind)
            if ind.count() in fi or ind in free_indices:
                repeated_indices.append(ind)
            free_indices.append(ind)
        elif isinstance(ind, FixedIndex):
            if int(ind) >= shape[len(all_indices)]:
                error("Index out of bounds.")
            all_indices.append(ind)
        elif isinstance(ind, int):
            if int(ind) >= shape[len(all_indices)]:
                error("Index out of bounds.")
            all_indices.append(FixedIndex(ind))
        elif isinstance(ind, slice):
            if ind != slice(None):
                error("Only full slices (:) allowed.")
            i = Index()
            slice_indices.append(i)
            all_indices.append(i)
        elif ind == Ellipsis:
            er = len(shape) - len(component) + 1
            ii = indices(er)
            slice_indices.extend(ii)
            all_indices.extend(ii)
        else:
            error("Not expecting {0}.".format(ind))

    if len(all_indices) != len(shape):
        error("Component and shape length don't match.")

    return tuple(all_indices), tuple(slice_indices), tuple(repeated_indices)
    def _mapped(self, t):
        # Check that we have a valid input object
        if not isinstance(t, Terminal):
            error("Expecting a Terminal.")

        # Get modifiers accumulated by previous handler calls
        ngrads = self._ngrads
        restricted = self._restricted
        avg = self._avg
        if avg != "":
            error("Averaging not implemented.")  # FIXME

        # These are the global (g) and reference (r) values
        if isinstance(t, FormArgument):
            g = t
            r = ReferenceValue(g)
        elif isinstance(t, GeometricQuantity):
            g = t
            r = g
        else:
            error("Unexpected type {0}.".format(type(t).__name__))

        # Some geometry mapping objects we may need multiple times below
        domain = t.ufl_domain()
        J = Jacobian(domain)
        detJ = JacobianDeterminant(domain)
        K = JacobianInverse(domain)

        # Restrict geometry objects if applicable
        if restricted:
            J = J(restricted)
            detJ = detJ(restricted)
            K = K(restricted)

        # Create Hdiv mapping from possibly restricted geometry objects
        Mdiv = (1.0 / detJ) * J

        # Get component indices of global and reference terminal objects
        gtsh = g.ufl_shape
        # rtsh = r.ufl_shape
        gtcomponents = compute_indices(gtsh)
        # rtcomponents = compute_indices(rtsh)

        # Create core modified terminal, with eventual
        # layers of grad applied directly to the terminal,
        # then eventual restriction applied last
        for i in range(ngrads):
            g = Grad(g)
            r = ReferenceGrad(r)
        if restricted:
            g = g(restricted)
            r = r(restricted)

        # Get component indices of global and reference objects with
        # grads applied
        gsh = g.ufl_shape
        # rsh = r.ufl_shape
        # gcomponents = compute_indices(gsh)
        # rcomponents = compute_indices(rsh)

        # Get derivative component indices
        dsh = gsh[len(gtsh):]
        dcomponents = compute_indices(dsh)

        # Create nested array to hold expressions for global
        # components mapped from reference values
        def ndarray(shape):
            if len(shape) == 0:
                return [None]
            elif len(shape) == 1:
                return [None] * shape[-1]
            else:
                return [ndarray(shape[1:]) for i in range(shape[0])]
        global_components = ndarray(gsh)

        # Compute mapping from reference values for each global component
        for gtc in gtcomponents:

            if isinstance(t, FormArgument):

                # Find basic subelement and element-local component
                # ec, element, eoffset = t.ufl_element().extract_component2(gtc) # FIXME: Translate this correctly
                eoffset = 0
                ec, element = t.ufl_element().extract_reference_component(gtc)

                # Select mapping M from element, pick row emapping =
                # M[ec,:], or emapping = [] if no mapping
                if isinstance(element, MixedElement):
                    error("Expecting a basic element here.")
                mapping = element.mapping()
                if mapping == "contravariant Piola":  # S == HDiv:
                    # Handle HDiv elements with contravariant piola
                    # mapping contravariant_hdiv_mapping = (1/det J) *
                    # J * PullbackOf(o)
                    ec, = ec
                    emapping = Mdiv[ec, :]
                elif mapping == "covariant Piola":  # S == HCurl:
                    # Handle HCurl elements with covariant piola mapping
                    # covariant_hcurl_mapping = JinvT * PullbackOf(o)
                    ec, = ec
                    emapping = K[:, ec]  # Column of K is row of K.T
                elif mapping == "identity":
                    emapping = None
                else:
                    error("Unknown mapping {0}".format(mapping))

            elif isinstance(t, GeometricQuantity):
                eoffset = 0
                emapping = None

            else:
                error("Unexpected type {0}.".format(type(t).__name__))

            # Create indices
            # if rtsh:
            #     i = Index()
            if len(dsh) != ngrads:
                error("Mismatch between derivative shape and ngrads.")
            if ngrads:
                ii = indices(ngrads)
            else:
                ii = ()

            # Apply mapping row to reference object
            if emapping:  # Mapped, always nonscalar terminal Not
                # using IndexSum for the mapping row dot product to
                # keep it simple, because we don't have a slice type
                emapped_ops = [emapping[s] * Indexed(r, MultiIndex((FixedIndex(eoffset + s),) + ii))
                               for s in range(len(emapping))]
                emapped = sum(emapped_ops[1:], emapped_ops[0])
            elif gtc:  # Nonscalar terminal, unmapped
                emapped = Indexed(r, MultiIndex((FixedIndex(eoffset),) + ii))
            elif ngrads:  # Scalar terminal, unmapped, with derivatives
                emapped = Indexed(r, MultiIndex(ii))
            else:  # Scalar terminal, unmapped, no derivatives
                emapped = r

            for di in dcomponents:
                # Multiply derivative mapping rows, parameterized by
                # free column indices
                dmapping = as_ufl(1)
                for j in range(ngrads):
                    dmapping *= K[ii[j], di[j]]  # Row of K is column of JinvT

                # Compute mapping from reference values for this
                # particular global component
                global_value = dmapping * emapped

                # Apply index sums
                # if rtsh:
                #     global_value = IndexSum(global_value, MultiIndex((i,)))
                # for j in range(ngrads): # Applied implicitly in the dmapping * emapped above
                #     global_value = IndexSum(global_value, MultiIndex((ii[j],)))

                # This is the component index into the full object
                # with grads applied
                gc = gtc + di

                # Insert in nested list
                comp = global_components
                for i in gc[:-1]:
                    comp = comp[i]
                comp[0 if gc == () else gc[-1]] = global_value

        # Wrap nested list in as_tensor unless we have a scalar
        # expression
        if gsh:
            tensor = as_tensor(global_components)
        else:
            tensor, = global_components
        return tensor
    def grad(self, o):
        # Peel off the Grads and count them, and get restriction if
        # it's between the grad and the terminal
        ngrads = 0
        restricted = ''
        rv = False
        while not o._ufl_is_terminal_:
            if isinstance(o, Grad):
                o, = o.ufl_operands
                ngrads += 1
            elif isinstance(o, Restricted):
                restricted = o.side()
                o, = o.ufl_operands
            elif isinstance(o, ReferenceValue):
                rv = True
                o, = o.ufl_operands
            else:
                error("Invalid type %s" % o._ufl_class_.__name__)
        f = o
        if rv:
            f = ReferenceValue(f)

        # Get domain and create Jacobian inverse object
        domain = o.ufl_domain()
        Jinv = JacobianInverse(domain)

        if is_cellwise_constant(Jinv):
            # Optimise slightly by turning Grad(Grad(...)) into
            # J^(-T)J^(-T)RefGrad(RefGrad(...))
            # rather than J^(-T)RefGrad(J^(-T)RefGrad(...))

            # Create some new indices
            ii = indices(len(f.ufl_shape))  # Indices to get to the scalar component of f
            jj = indices(ngrads)  # Indices to sum over the local gradient axes with the inverse Jacobian
            kk = indices(ngrads)  # Indices for the leftover inverse Jacobian axes

            # Preserve restricted property
            if restricted:
                Jinv = Jinv(restricted)
                f = f(restricted)

            # Apply the same number of ReferenceGrad without mappings
            lgrad = f
            for i in range(ngrads):
                lgrad = ReferenceGrad(lgrad)

            # Apply mappings with scalar indexing operations (assumes
            # ReferenceGrad(Jinv) is zero)
            jinv_lgrad_f = lgrad[ii + jj]
            for j, k in zip(jj, kk):
                jinv_lgrad_f = Jinv[j, k] * jinv_lgrad_f

            # Wrap back in tensor shape, derivative axes at the end
            jinv_lgrad_f = as_tensor(jinv_lgrad_f, ii + kk)

        else:
            # J^(-T)RefGrad(J^(-T)RefGrad(...))

            # Preserve restricted property
            if restricted:
                Jinv = Jinv(restricted)
                f = f(restricted)

            jinv_lgrad_f = f
            for foo in range(ngrads):
                ii = indices(len(jinv_lgrad_f.ufl_shape))  # Indices to get to the scalar component of f
                j, k = indices(2)

                lgrad = ReferenceGrad(jinv_lgrad_f)
                jinv_lgrad_f = Jinv[j, k] * lgrad[ii + (j,)]

                # Wrap back in tensor shape, derivative axes at the end
                jinv_lgrad_f = as_tensor(jinv_lgrad_f, ii + (k,))

        return jinv_lgrad_f
 def sym(self, o, A):
     i, j = indices(2)
     return as_matrix((A[i, j] + A[j, i]) / 2, (i, j))
Exemple #52
0
 def transposed(self, o, A):
     i, j = indices(2)
     return as_tensor(A[i, j], (j, i))
def generic_pseudo_determinant_expr(A):
    """Compute the pseudo-determinant of A: sqrt(det(A.T*A))."""
    i, j, k = indices(3)
    ATA = as_tensor(A[k, i] * A[k, j], (i, j))
    return sqrt(determinant_expr(ATA))
 def outer(self, o, a, b):
     ii = indices(len(a.ufl_shape))
     jj = indices(len(b.ufl_shape))
     # Create a Product with no shared indices
     s = Conj(a[ii]) * b[jj]
     return as_tensor(s, ii + jj)
Exemple #55
0
 def outer(self, o, a, b):
     ii = indices(len(a.ufl_shape))
     jj = indices(len(b.ufl_shape))
     # Create a Product with no shared indices
     s = Conj(a[ii])*b[jj]
     return as_tensor(s, ii+jj)
Exemple #56
0
def analyse_key(ii, rank):
    """Takes something the user might input as an index tuple
    inside [], which could include complete slices (:) and
    ellipsis (...), and returns tuples of actual UFL index objects.

    The return value is a tuple (indices, axis_indices),
    each being a tuple of IndexBase instances.

    The return value 'indices' corresponds to all
    input objects of these types:
    - Index
    - FixedIndex
    - int => Wrapped in FixedIndex

    The return value 'axis_indices' corresponds to all
    input objects of these types:
    - Complete slice (:) => Replaced by a single new index
    - Ellipsis (...) => Replaced by multiple new indices
    """
    # Wrap in tuple
    if not isinstance(ii, (tuple, MultiIndex)):
        ii = (ii,)
    else:
        # Flatten nested tuples, happens with f[...,ii] where ii is a
        # tuple of indices
        jj = []
        for j in ii:
            if isinstance(j, (tuple, MultiIndex)):
                jj.extend(j)
            else:
                jj.append(j)
        ii = tuple(jj)

    # Convert all indices to Index or FixedIndex objects.  If there is
    # an ellipsis, split the indices into before and after.
    axis_indices = set()
    pre = []
    post = []
    indexlist = pre
    for i in ii:
        if i == Ellipsis:
            # Switch from pre to post list when an ellipsis is
            # encountered
            if indexlist is not pre:
                error("Found duplicate ellipsis.")
            indexlist = post
        else:
            # Convert index to a proper type
            if isinstance(i, numbers.Integral):
                idx = FixedIndex(i)
            elif isinstance(i, IndexBase):
                idx = i
            elif isinstance(i, slice):
                if i == slice(None):
                    idx = Index()
                    axis_indices.add(idx)
                else:
                    # TODO: Use ListTensor to support partial slices?
                    error("Partial slices not implemented, only complete slices like [:]")
            else:
                error("Can't convert this object to index: %s" % (i,))

            # Store index in pre or post list
            indexlist.append(idx)

    # Handle ellipsis as a number of complete slices, that is create a
    # number of new axis indices
    num_axis = rank - len(pre) - len(post)
    if indexlist is post:
        ellipsis_indices = indices(num_axis)
        axis_indices.update(ellipsis_indices)
    else:
        ellipsis_indices = ()

    # Construct final tuples to return
    all_indices = tuple(chain(pre, ellipsis_indices, post))
    axis_indices = tuple(i for i in all_indices if i in axis_indices)
    return all_indices, axis_indices
 def transposed(self, o, A):
     i, j = indices(2)
     return as_tensor(A[i, j], (j, i))
Exemple #58
0
def _mult(a, b):
    # Discover repeated indices, which results in index sums
    afi = a.ufl_free_indices
    bfi = b.ufl_free_indices
    afid = a.ufl_index_dimensions
    bfid = b.ufl_index_dimensions
    fi, fid, ri, rid = merge_overlapping_indices(afi, afid, bfi, bfid)

    # Pick out valid non-scalar products here (dot products):
    # - matrix-matrix (A*B, M*grad(u)) => A . B
    # - matrix-vector (A*v) => A . v
    s1, s2 = a.ufl_shape, b.ufl_shape
    r1, r2 = len(s1), len(s2)

    if r1 == 0 and r2 == 0:
        # Create scalar product
        p = Product(a, b)
        ti = ()

    elif r1 == 0 or r2 == 0:
        # Scalar - tensor product
        if r2 == 0:
            a, b = b, a

        # Check for zero, simplifying early if possible
        if isinstance(a, Zero) or isinstance(b, Zero):
            shape = s1 or s2
            return Zero(shape, fi, fid)

        # Repeated indices are allowed, like in:
        # v[i]*M[i,:]

        # Apply product to scalar components
        ti = indices(len(b.ufl_shape))
        p = Product(a, b[ti])

    elif r1 == 2 and r2 in (1, 2):  # Matrix-matrix or matrix-vector
        if ri:
            error("Not expecting repeated indices in non-scalar product.")

        # Check for zero, simplifying early if possible
        if isinstance(a, Zero) or isinstance(b, Zero):
            shape = s1[:-1] + s2[1:]
            return Zero(shape, fi, fid)

        # Return dot product in index notation
        ai = indices(len(a.ufl_shape) - 1)
        bi = indices(len(b.ufl_shape) - 1)
        k = indices(1)

        p = a[ai + k] * b[k + bi]
        ti = ai + bi

    else:
        error("Invalid ranks {0} and {1} in product.".format(r1, r2))

    # TODO: I think applying as_tensor after index sums results in
    # cleaner expression graphs.
    # Wrap as tensor again
    if ti:
        p = as_tensor(p, ti)

    # If any repeated indices were found, apply implicit summation
    # over those
    for i in ri:
        mi = MultiIndex((Index(count=i),))
        p = IndexSum(p, mi)

    return p
Exemple #59
0
 def sym(self, o, A):
     i, j = indices(2)
     return as_matrix((A[i, j] + A[j, i]) / 2, (i, j))