Esempio n. 1
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
Esempio n. 2
0
def construct_modified_terminal(mt, terminal):
    """Construct a modified terminal given terminal modifiers from an
    analysed modified terminal and a terminal."""
    expr = terminal

    if mt.reference_value:
        expr = ReferenceValue(expr)

    dim = expr.ufl_domain().topological_dimension()
    for n in range(mt.local_derivatives):
        # Return zero if expression is trivially constant. This has to
        # happen here because ReferenceGrad has no access to the
        # topological dimension of a literal zero.
        if is_cellwise_constant(expr):
            expr = Zero(expr.ufl_shape + (dim, ), expr.ufl_free_indices,
                        expr.ufl_index_dimensions)
        else:
            expr = ReferenceGrad(expr)

    # No need to apply restrictions to ConstantValue terminals
    if not isinstance(expr, ConstantValue):
        if mt.restriction == '+':
            expr = PositiveRestricted(expr)
        elif mt.restriction == '-':
            expr = NegativeRestricted(expr)

    return expr
Esempio n. 3
0
 def jacobian(self, o):
     # d (grad_X(x))/d x => grad_X(Argument(x.function_space())
     for (w, v) in zip(self._w, self._v):
         if o.ufl_domain() == w.ufl_domain() and isinstance(
                 v.ufl_operands[0], FormArgument):
             return ReferenceGrad(v)
     return self.independent_terminal(o)
Esempio n. 4
0
 def reference_grad(self, o):
     "Represent ref_grad(ref_grad(f)) as RefGrad(RefGrad(f))."
     # Check that o is a "differential terminal"
     if not isinstance(o.ufl_operands[0],
                       (ReferenceGrad, ReferenceValue, Terminal)):
         error("Expecting only grads applied to a terminal.")
     return ReferenceGrad(o)
Esempio n. 5
0
 def geometric_quantity(self, o):
     "dg/dX = 0 if piecewise constant, otherwise ReferenceGrad(g)"
     if is_cellwise_constant(o):
         return self.independent_terminal(o)
     else:
         # TODO: Which types does this involve? I don't think the
         # form compilers will handle this.
         return ReferenceGrad(o)
Esempio n. 6
0
def test_index_simplification_reference_grad(self):
    mesh = Mesh(VectorElement("P", quadrilateral, 1))
    i, = indices(1)
    A = as_tensor(Indexed(Jacobian(mesh), MultiIndex((i, i))), (i,))
    expr = apply_derivatives(apply_geometry_lowering(
        apply_algebra_lowering(A[0])))
    assert expr == ReferenceGrad(SpatialCoordinate(mesh))[0, 0]
    assert expr.ufl_free_indices == ()
    assert expr.ufl_shape == ()
Esempio n. 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
Esempio n. 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
Esempio n. 9
0
 def jacobian(self, o):
     if self._preserve_types[o._ufl_typecode_]:
         return o
     domain = o.ufl_domain()
     if domain.ufl_coordinate_element().mapping() != "identity":
         error("Piola mapped coordinates are not implemented.")
     # Note: No longer supporting domain.coordinates(), always
     # preserving SpatialCoordinate object.  However if Jacobians
     # are not preserved, using
     # ReferenceGrad(SpatialCoordinate(domain)) to represent them.
     x = self.spatial_coordinate(SpatialCoordinate(domain))
     return ReferenceGrad(x)
Esempio n. 10
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
Esempio n. 11
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)
        Do = grad_to_reference_grad(o, K)
        return Do
Esempio n. 12
0
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
Esempio n. 13
0
def construct_modified_terminal(mt, terminal):
    """Construct a modified terminal given terminal modifiers from an
    analysed modified terminal and a terminal."""
    expr = terminal

    if mt.reference_value:
        expr = ReferenceValue(expr)

    for n in range(mt.local_derivatives):
        expr = ReferenceGrad(expr)

    if mt.averaged == "cell":
        expr = CellAvg(expr)
    elif mt.averaged == "facet":
        expr = FacetAvg(expr)

    # No need to apply restrictions to ConstantValue terminals
    if not isinstance(expr, ConstantValue):
        if mt.restriction == '+':
            expr = PositiveRestricted(expr)
        elif mt.restriction == '-':
            expr = NegativeRestricted(expr)

    return expr
Esempio n. 14
0
 def reference_value(self, o):
     if not o.ufl_operands[0]._ufl_is_terminal_:
         error("ReferenceValue can only wrap a terminal")
     return ReferenceGrad(o)
Esempio n. 15
0
 def spatial_coordinate(self, o):
     "dx/dX = J"
     # Don't convert back to J, otherwise we get in a loop
     return ReferenceGrad(o)
Esempio n. 16
0
    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
Esempio n. 17
0
    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
Esempio n. 18
0
 def apply_grads(f):
     for i in range(ngrads):
         f = ReferenceGrad(f)
     return f
Esempio n. 19
0
def test_change_to_reference_grad():
    cell = triangle
    domain = Mesh(cell)
    U = FunctionSpace(domain, FiniteElement("CG", cell, 1))
    V = FunctionSpace(domain, VectorElement("CG", cell, 1))
    u = Coefficient(U)
    v = Coefficient(V)
    Jinv = JacobianInverse(domain)
    i, j, k = indices(3)
    q, r, s = indices(3)
    t, = indices(1)

    # Single grad change on a scalar function
    expr = grad(u)
    actual = change_to_reference_grad(expr)
    expected = as_tensor(Jinv[k, i] * ReferenceGrad(u)[k], (i, ))
    assert renumber_indices(actual) == renumber_indices(expected)

    # Single grad change on a vector valued function
    expr = grad(v)
    actual = change_to_reference_grad(expr)
    expected = as_tensor(Jinv[k, j] * ReferenceGrad(v)[i, k], (i, j))
    assert renumber_indices(actual) == renumber_indices(expected)

    # Multiple grads should work fine for affine domains:
    expr = grad(grad(u))
    actual = change_to_reference_grad(expr)
    expected = as_tensor(
        Jinv[s, j] * (Jinv[r, i] * ReferenceGrad(ReferenceGrad(u))[r, s]),
        (i, j))
    assert renumber_indices(actual) == renumber_indices(expected)

    expr = grad(grad(grad(u)))
    actual = change_to_reference_grad(expr)
    expected = as_tensor(
        Jinv[s, k] *
        (Jinv[r, j] *
         (Jinv[q, i] *
          ReferenceGrad(ReferenceGrad(ReferenceGrad(u)))[q, r, s])), (i, j, k))
    assert renumber_indices(actual) == renumber_indices(expected)

    # Multiple grads on a vector valued function
    expr = grad(grad(v))
    actual = change_to_reference_grad(expr)
    expected = as_tensor(
        Jinv[s, j] * (Jinv[r, i] * ReferenceGrad(ReferenceGrad(v))[t, r, s]),
        (t, i, j))
    assert renumber_indices(actual) == renumber_indices(expected)

    expr = grad(grad(grad(v)))
    actual = change_to_reference_grad(expr)
    expected = as_tensor(
        Jinv[s, k] *
        (Jinv[r, j] *
         (Jinv[q, i] *
          ReferenceGrad(ReferenceGrad(ReferenceGrad(v)))[t, q, r, s])),
        (t, i, j, k))
    assert renumber_indices(actual) == renumber_indices(expected)