Example #1
0
def compute_form_with_arity(form, arity, arguments=None):
    """Compute parts of form of given arity."""

    # Extract all arguments in form
    if arguments is None:
        arguments = form.arguments()

    parts = [arg.part() for arg in arguments]
    if set(parts) - {None}:
        error("compute_form_with_arity cannot handle parts.")

    if len(arguments) < arity:
        warning("Form has no parts with arity %d." % arity)
        return 0*form

    # Assuming that the form is not a sum of terms
    # that depend on different arguments, e.g. (u+v)*dx
    # would result in just v*dx. But that doesn't make
    # any sense anyway.
    sub_arguments = set(arguments[:arity])
    pe = PartExtracter(sub_arguments)

    def _transform(e):
        e, provides = pe.visit(e)
        if provides == sub_arguments:
            return e
        return Zero()
    return map_integrands(_transform, form)
Example #2
0
def compute_form_with_arity(form, arity, arguments=None):
    """Compute parts of form of given arity."""

    # Extract all arguments in form
    if arguments is None:
        arguments = form.arguments()

    parts = [arg.part() for arg in arguments]
    if set(parts) - {None}:
        error("compute_form_with_arity cannot handle parts.")

    if len(arguments) < arity:
        warning("Form has no parts with arity %d." % arity)
        return 0*form

    # Assuming that the form is not a sum of terms
    # that depend on different arguments, e.g. (u+v)*dx
    # would result in just v*dx. But that doesn't make
    # any sense anyway.
    sub_arguments = set(arguments[:arity])
    pe = PartExtracter(sub_arguments)

    def _transform(e):
        e, provides = pe.visit(e)
        if provides == sub_arguments:
            return e
        return Zero()
    return map_integrands(_transform, form)
Example #3
0
    def _reduce_facet_edge_length(self, o, reduction_op):
        if self._preserve_types[o._ufl_typecode_]:
            return o

        domain = o.ufl_domain()

        if domain.ufl_cell().topological_dimension() < 3:
            error(
                "Facet edge lengths only make sense for topological dimension >= 3."
            )

        elif not domain.ufl_coordinate_element().degree() == 1:
            # Don't lower bendy cells, instead leave it to form compiler
            warning(
                "Only know how to compute facet edge lengths of P1 or Q1 cell."
            )
            return o

        else:
            # P1 tetrahedron or Q1 hexahedron
            edges = FacetEdgeVectors(domain)
            num_edges = edges.ufl_shape[0]
            j = Index()
            elen2 = [edges[e, j] * edges[e, j] for e in range(num_edges)]
            return sqrt(reduce(reduction_op, elen2))
    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)
Example #5
0
def canonicalize_metadata(metadata):
    """Assuming metadata to be a dict with string keys and builtin python types as values.

    Transform dict to a tuple of (key, value) item tuples ordered by key,
    with dict, list and tuple values converted the same way recursively.
    Lists and tuples are converted to tuples. Other values are converted using str().
    This is such that the end result can be hashed and sorted using regular <,
    because python 3 doesn't allow e.g. (3 < "auto") which occurs regularly in metadata.
    """
    if metadata is None:
        return ()

    if isinstance(metadata, dict):
        keys = sorted(metadata.keys())
        assert all(isinstance(key, str) for key in keys)
        values = [metadata[key] for key in keys]
    elif isinstance(metadata, (tuple, list)):
        values = metadata

    newvalues = []
    for value in values:
        if isinstance(value, (dict, list, tuple)):
            value = canonicalize_metadata(value)
        elif isinstance(value, (int, float, str)) or value is None:
            value = str(value)
        else:
            warning("Applying str() to a metadata value of type {0}, don't know if this is safe.".format(type(value).__name__))
            value = str(value)
        newvalues.append(value)

    if isinstance(metadata, dict):
        return tuple(zip(keys, newvalues))
    else:
        return tuple(newvalues)
 def is_undefined(self):
     """Return whether this cell is undefined,
     in which case no dimensions are available."""
     warning(
         "cell.is_undefined() is deprecated, undefined cells are no longer allowed."
     )
     return False
Example #7
0
 def evaluate(self, x, mapping, component, index_values):
     a = self._argument.evaluate(x, mapping, component, index_values)
     try:
         res = getattr(math, self._name)(a)
     except ValueError:
         warning('Value error in evaluation of function %s with argument %s.' % (self._name, a))
         raise
     return res
Example #8
0
def assign_precedences(precedence_list):
    "Given a precedence list, assign ints to class._precedence."
    pm, missing = build_precedence_mapping(precedence_list)
    for c, p in sorted(pm.items(), key=lambda x: x[0].__name__):
        c._precedence = p
    if missing:
        msg = "Missing precedence levels for classes:\n" +\
            "\n".join('  %s' % c for c in sorted(missing))
        warning(msg)
Example #9
0
 def evaluate(self, x, mapping, component, index_values):
     a = self._arg1.evaluate(x, mapping, component, index_values)
     b = self._arg2.evaluate(x, mapping, component, index_values)
     try:
         res = math.atan2(a,b)
     except ValueError:
         warning('Value error in evaluation of function %s with arguments %s, %s.' % (self._name, a,b))
         raise
     return res
def flatten(
    e
):  # TODO: Fix or remove! Maybe this works better now with IndexSum marking implicit summations.
    """Convert an UFL expression to a new UFL expression, with sums
    and products flattened from binary tree nodes to n-ary tree nodes."""
    warning(
        "flatten doesn't work correctly for some indexed products, like (u[i]*v[i])*(q[i]*r[i])"
    )
    return apply_transformer(e, TreeFlattener())
Example #11
0
def assign_precedences(precedence_list):
    "Given a precedence list, assign ints to class._precedence."
    pm, missing = build_precedence_mapping(precedence_list)
    for c, p in sorted(pm.items(), key=lambda x: x[0].__name__):
        c._precedence = p
    if missing:
        msg = "Missing precedence levels for classes:\n" +\
            "\n".join('  %s' % c for c in sorted(missing))
        warning(msg)
    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
Example #13
0
 def evaluate(self, x, mapping, component, index_values):
     a, b = self.ufl_operands
     a = a.evaluate(x, mapping, component, index_values)
     b = b.evaluate(x, mapping, component, index_values)
     try:
         res = max(a, b)
     except ValueError:
         warning('Value error in evaluation of max() of %s and %s.' % self.ufl_operands)
         raise
     return res
Example #14
0
def execute_ufl_code(uflcode, filename):
    # Execute code
    namespace = {}
    namespace.update(vars(ufl))
    try:
        exec(uflcode, namespace)
    except Exception as e:
        warning(infostring)
        raise e
    return namespace
Example #15
0
def execute_ufl_code(uflcode, filename):
    # Execute code
    namespace = {}
    namespace.update(vars(ufl))
    try:
        exec(uflcode, namespace)
    except Exception as e:
        warning(infostring)
        raise e
    return namespace
Example #16
0
 def evaluate(self, x, mapping, component, index_values):
     a, b = self.ufl_operands
     a = a.evaluate(x, mapping, component, index_values)
     b = b.evaluate(x, mapping, component, index_values)
     try:
         res = max(a, b)
     except ValueError:
         warning('Value error in evaluation of max() of %s and %s.' % self.ufl_operands)
         raise
     return res
Example #17
0
 def evaluate(self, x, mapping, component, index_values):
     a = self.ufl_operands[0].evaluate(x, mapping, component, index_values)
     try:
         if isinstance(a, numbers.Real):
             res = getattr(math, self._name)(a)
         else:
             res = getattr(cmath, self._name)(a)
     except ValueError:
         warning('Value error in evaluation of function %s with argument %s.' % (self._name, a))
         raise
     return res
Example #18
0
 def evaluate(self, x, mapping, component, index_values):
     a = self.ufl_operands[0].evaluate(x, mapping, component, index_values)
     b = self.ufl_operands[1].evaluate(x, mapping, component, index_values)
     try:
         res = math.atan2(a, b)
     except ValueError:
         warning(
             'Value error in evaluation of function atan_2 with arguments %s, %s.'
             % (a, b))
         raise
     return res
Example #19
0
 def evaluate(self, x, mapping, component, index_values):
     a = self.ufl_operands[0].evaluate(x, mapping, component, index_values)
     b = self.ufl_operands[1].evaluate(x, mapping, component, index_values)
     try:
         res = math.atan2(a, b)
     except TypeError:
         error('Atan2 does not support complex numbers.')
     except ValueError:
         warning('Value error in evaluation of function atan_2 with arguments %s, %s.' % (a, b))
         raise
     return res
Example #20
0
 def evaluate(self, x, mapping, component, index_values):
     a = self.ufl_operands[0].evaluate(x, mapping, component, index_values)
     try:
         if isinstance(a, numbers.Real):
             res = getattr(math, self._name)(a)
         else:
             res = getattr(cmath, self._name)(a)
     except ValueError:
         warning(
             'Value error in evaluation of function %s with argument %s.' %
             (self._name, a))
         raise
     return res
Example #21
0
    def cell_volume(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 cell volume of an affine cell.")
            return o

        r = self.jacobian_determinant(JacobianDeterminant(domain))
        r0 = ReferenceCellVolume(domain)
        return abs(r * r0)
Example #22
0
    def cell_volume(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 cell volume of an affine cell.")
            return o

        r = self.jacobian_determinant(JacobianDeterminant(domain))
        r0 = ReferenceCellVolume(domain)
        return abs(r * r0)
Example #23
0
    def __call__(self, *args, **kwargs):
        """UFL form operator: Evaluate form by replacing arguments and
        coefficients.

        Replaces form.arguments() with given positional arguments in
        same number and ordering. Number of positional arguments must
        be 0 or equal to the number of Arguments in the form.

        The optional keyword argument coefficients can be set to a dict
        to replace Coefficients with expressions of matching shapes.

        Example:

          V = FiniteElement("CG", triangle, 1)
          v = TestFunction(V)
          u = TrialFunction(V)
          f = Coefficient(V)
          g = Coefficient(V)
          a = g*inner(grad(u), grad(v))*dx
          M = a(f, f, coefficients={ g: 1 })

        Is equivalent to M == grad(f)**2*dx.

        """
        repdict = {}

        if args:
            arguments = self.arguments()
            if len(arguments) != len(args):
                error("Need %d arguments to form(), got %d." %
                      (len(arguments), len(args)))
            repdict.update(zip(arguments, args))

        coefficients = kwargs.pop("coefficients")
        if kwargs:
            error("Unknown kwargs %s." % str(list(kwargs)))

        if coefficients is not None:
            coeffs = self.coefficients()
            for f in coefficients:
                if f in coeffs:
                    repdict[f] = coefficients[f]
                else:
                    warning("Coefficient %s is not in form." % ufl_err_str(f))
        if repdict:
            from ufl.formoperators import replace
            return replace(self, repdict)
        else:
            return self
    def coefficient(self, o):
        # Define dw/dw := d/ds [w + s v] = v

        debug("In CoefficientAD.coefficient:")
        debug("o = %s" % o)
        debug("self._w = %s" % self._w)
        debug("self._v = %s" % self._v)

        # Find o among w
        for (w, v) in izip(self._w, self._v):
            if o == w:
                return (w, v)

        # If o is not among coefficient derivatives, return do/dw=0
        oprimesum = Zero(o.shape())
        oprimes = self._cd._data.get(o)
        if oprimes is None:
            if self._cd._data:
                # TODO: Make it possible to silence this message in particular?
                #       It may be good to have for debugging...
                warning("Assuming d{%s}/d{%s} = 0." % (o, self._w))
        else:
            # Make sure we have a tuple to match the self._v tuple
            if not isinstance(oprimes, tuple):
                oprimes = (oprimes,)
                ufl_assert(len(oprimes) == len(self._v), "Got a tuple of arguments, "+\
                               "expecting a matching tuple of coefficient derivatives.")

            # Compute do/dw_j = do/dw_h : v.
            # Since we may actually have a tuple of oprimes and vs in a
            # 'mixed' space, sum over them all to get the complete inner
            # product. Using indices to define a non-compound inner product.
            for (oprime, v) in izip(oprimes, self._v):
                so, oi = as_scalar(oprime)
                rv = len(v.shape())
                oi1 = oi[:-rv]
                oi2 = oi[-rv:]
                prod = so*v[oi2]
                if oi1:
                    oprimesum += as_tensor(prod, oi1)
                else:
                    oprimesum += prod

        # Example:
        # (f : g) -> (dfdu : v) : g + ditto
        # shape(f) == shape(g) == shape(dfdu : v)
        # shape(dfdu) == shape(f) + shape(v)

        return (o, oprimesum)
    def coefficient(self, o):
        # Define dw/dw := d/ds [w + s v] = v

        debug("In CoefficientAD.coefficient:")
        debug("o = %s" % o)
        debug("self._w = %s" % self._w)
        debug("self._v = %s" % self._v)

        # Find o among w
        for (w, v) in izip(self._w, self._v):
            if o == w:
                return (w, v)

        # If o is not among coefficient derivatives, return do/dw=0
        oprimesum = Zero(o.shape())
        oprimes = self._cd._data.get(o)
        if oprimes is None:
            if self._cd._data:
                # TODO: Make it possible to silence this message in particular?
                #       It may be good to have for debugging...
                warning("Assuming d{%s}/d{%s} = 0." % (o, self._w))
        else:
            # Make sure we have a tuple to match the self._v tuple
            if not isinstance(oprimes, tuple):
                oprimes = (oprimes, )
                ufl_assert(len(oprimes) == len(self._v), "Got a tuple of arguments, "+\
                               "expecting a matching tuple of coefficient derivatives.")

            # Compute do/dw_j = do/dw_h : v.
            # Since we may actually have a tuple of oprimes and vs in a
            # 'mixed' space, sum over them all to get the complete inner
            # product. Using indices to define a non-compound inner product.
            for (oprime, v) in izip(oprimes, self._v):
                so, oi = as_scalar(oprime)
                rv = len(v.shape())
                oi1 = oi[:-rv]
                oi2 = oi[-rv:]
                prod = so * v[oi2]
                if oi1:
                    oprimesum += as_tensor(prod, oi1)
                else:
                    oprimesum += prod

        # Example:
        # (f : g) -> (dfdu : v) : g + ditto
        # shape(f) == shape(g) == shape(dfdu : v)
        # shape(dfdu) == shape(f) + shape(v)

        return (o, oprimesum)
Example #26
0
    def __call__(self, *args, **kwargs):
        """UFL form operator: Evaluate form by replacing arguments and
        coefficients.

        Replaces form.arguments() with given positional arguments in
        same number and ordering. Number of positional arguments must
        be 0 or equal to the number of Arguments in the form.

        The optional keyword argument coefficients can be set to a dict
        to replace Coefficients with expressions of matching shapes.

        Example:

          V = FiniteElement("CG", triangle, 1)
          v = TestFunction(V)
          u = TrialFunction(V)
          f = Coefficient(V)
          g = Coefficient(V)
          a = g*inner(grad(u), grad(v))*dx
          M = a(f, f, coefficients={ g: 1 })

        Is equivalent to M == grad(f)**2*dx.

        """
        repdict = {}

        if args:
            arguments = self.arguments()
            if len(arguments) != len(args):
                error("Need %d arguments to form(), got %d." % (len(arguments),
                                                                len(args)))
            repdict.update(zip(arguments, args))

        coefficients = kwargs.pop("coefficients")
        if kwargs:
            error("Unknown kwargs %s." % str(list(kwargs)))

        if coefficients is not None:
            coeffs = self.coefficients()
            for f in coefficients:
                if f in coeffs:
                    repdict[f] = coefficients[f]
                else:
                    warning("Coefficient %s is not in form." % ufl_err_str(f))
        if repdict:
            from ufl.formoperators import replace
            return replace(self, repdict)
        else:
            return self
Example #27
0
def execute_ufl_code(uflcode, filename):
    # Execute code
    namespace = {}
    try:
        pycode = "from ufl import *\n" + uflcode
        exec pycode in namespace
    except:
        # Dump python code for debugging if this fails
        basename = os.path.splitext(os.path.basename(filename))[0]
        basename = "%s_debug" % basename
        pyname = "%s.py" % basename
        pycode = "#!/usr/bin/env python\nfrom ufl import *\nset_level(DEBUG)\n" + uflcode
        with file(pyname, "w") as f:
            f.write(pycode)
        warning(infostring % pyname)
        m = __import__(basename)
        error("An error occured, aborting load_forms.")
    return namespace
def execute_ufl_code(uflcode, filename):
    # Execute code
    namespace = {}
    try:
        pycode = "from ufl import *\n" + uflcode
        exec pycode in namespace
    except:
        # Dump python code for debugging if this fails
        basename = os.path.splitext(os.path.basename(filename))[0]
        basename = "%s_debug" % basename
        pyname = "%s.py" % basename
        pycode = "#!/usr/bin/env python\nfrom ufl import *\nset_level(DEBUG)\n" + uflcode
        with file(pyname, "w") as f:
            f.write(pycode)
        warning(infostring % pyname)
        m = __import__(basename)
        error("An error occured, aborting load_forms.")
    return namespace
def jump(v, n=None):
    "UFL operator: Take the jump of v across a facet."
    v = as_ufl(v)
    cell = v.cell()
    if cell is None:
        warning("TODO: Not all expressions have a cell. Is it right to return zero from jump then?")
        # TODO: Is this right? If v has no cell, it doesn't depend on
        # anything spatially variable or any form arguments, and thus
        # the jump is zero. In other words, I'm assuming that
        # "v.cell() is None" is equivalent with "v is a constant".
        return Zero(v.shape(), v.free_indices(), v.index_dimensions())
    else:
        if n is None:
            return v('+') - v('-')
        r = v.rank()
        if r == 0:
            return v('+')*n('+') + v('-')*n('-')
        else:
            return dot(v('+'), n('+')) + dot(v('-'), n('-'))
Example #30
0
    def facet_area(self, o):
        if self._preserve_types[o._ufl_typecode_]:
            return o

        domain = o.ufl_domain()
        tdim = domain.topological_dimension()
        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 facet area of an affine cell.")
            return o

        # Area of "facet" of interval (i.e. "area" of a vertex) is defined as 1.0
        if tdim == 1:
            return FloatValue(1.0)

        r = self.facet_jacobian_determinant(FacetJacobianDeterminant(domain))
        r0 = ReferenceFacetVolume(domain)
        return abs(r * r0)
Example #31
0
    def facet_area(self, o):
        if self._preserve_types[o._ufl_typecode_]:
            return o

        domain = o.ufl_domain()
        tdim = domain.topological_dimension()
        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 facet area of an affine cell.")
            return o

        # Area of "facet" of interval (i.e. "area" of a vertex) is defined as 1.0
        if tdim == 1:
            return FloatValue(1.0)

        r = self.facet_jacobian_determinant(FacetJacobianDeterminant(domain))
        r0 = ReferenceFacetVolume(domain)
        return abs(r * r0)
Example #32
0
File: ad.py Project: FEniCS/ufl
def expand_derivatives(form, **kwargs):
    """Expand all derivatives of expr.

    In the returned expression g which is mathematically
    equivalent to expr, there are no VariableDerivative
    or CoefficientDerivative objects left, and Grad
    objects have been propagated to Terminal nodes.
    """
    # For a deprecation period (I see that dolfin-adjoint passes some
    # args here)
    if kwargs:
        warning("Deprecation: expand_derivatives no longer takes any keyword arguments")

    # Lower abstractions for tensor-algebra types into index notation
    form = apply_algebra_lowering(form)

    # Apply differentiation
    form = apply_derivatives(form)

    return form
Example #33
0
    def evaluate(self, x, mapping, component, index_values, derivatives=()):
        "Get *self* from *mapping* and return the component asked for."
        f = mapping.get(self)
        # No mapping, trying to evaluate self as a constant
        if f is None:
            try:
                try:
                    f = float(self)
                except TypeError:
                    f = complex(self)
                if derivatives:
                    f = 0.0
                return f
            except Exception:
                pass
            # If it has an ufl_evaluate function, call it
            if hasattr(self, 'ufl_evaluate'):
                return self.ufl_evaluate(x, component, derivatives)
            # Take component if any
            warning(
                "Couldn't map '%s' to a float, returning ufl object without evaluation."
                % str(self))
            f = self
            if component:
                f = f[component]
            return f

        # Found a callable in the mapping
        if callable(f):
            if derivatives:
                f = f(x, derivatives)
            else:
                f = f(x)
        else:
            if derivatives:
                return 0.0

        # Take component if any (expecting nested tuple)
        for c in component:
            f = f[c]
        return f
Example #34
0
def jump(v, n=None):
    "UFL operator: Take the jump of *v* across a facet."
    v = as_ufl(v)
    is_constant = len(extract_domains(v)) > 0
    if is_constant:
        if n is None:
            return v('+') - v('-')
        r = len(v.ufl_shape)
        if r == 0:
            return v('+') * n('+') + v('-') * n('-')
        else:
            return dot(v('+'), n('+')) + dot(v('-'), n('-'))
    else:
        warning("Returning zero from jump of expression without a domain. This may be erroneous if a dolfin.Expression is involved.")
        # FIXME: Is this right? If v has no domain, it doesn't depend
        # on anything spatially variable or any form arguments, and
        # thus the jump is zero. In other words, I'm assuming that "v
        # has no geometric domains" is equivalent with "v is a spatial
        # constant".  Update: This is NOT true for
        # jump(Expression("x[0]")) from dolfin.
        return Zero(v.ufl_shape, v.ufl_free_indices, v.ufl_index_dimensions)
Example #35
0
def jump(v, n=None):
    "UFL operator: Take the jump of *v* across a facet."
    v = as_ufl(v)
    is_constant = len(extract_domains(v)) > 0
    if is_constant:
        if n is None:
            return v('+') - v('-')
        r = len(v.ufl_shape)
        if r == 0:
            return v('+') * n('+') + v('-') * n('-')
        else:
            return dot(v('+'), n('+')) + dot(v('-'), n('-'))
    else:
        warning("Returning zero from jump of expression without a domain. This may be erroneous if a dolfin.Expression is involved.")
        # FIXME: Is this right? If v has no domain, it doesn't depend
        # on anything spatially variable or any form arguments, and
        # thus the jump is zero. In other words, I'm assuming that "v
        # has no geometric domains" is equivalent with "v is a spatial
        # constant".  Update: This is NOT true for
        # jump(Expression("x[0]")) from dolfin.
        return Zero(v.ufl_shape, v.ufl_free_indices, v.ufl_index_dimensions)
Example #36
0
def jump(v, n=None):
    "UFL operator: Take the jump of v across a facet."
    v = as_ufl(v)
    cell = v.cell()
    if cell is None:
        warning(
            "TODO: Not all expressions have a cell. Is it right to return zero from jump then?"
        )
        # TODO: Is this right? If v has no cell, it doesn't depend on
        # anything spatially variable or any form arguments, and thus
        # the jump is zero. In other words, I'm assuming that
        # "v.cell() is None" is equivalent with "v is a constant".
        return Zero(v.shape(), v.free_indices(), v.index_dimensions())
    else:
        if n is None:
            return v('+') - v('-')
        r = v.rank()
        if r == 0:
            return v('+') * n('+') + v('-') * n('-')
        else:
            return dot(v('+'), n('+')) + dot(v('-'), n('-'))
Example #37
0
    def _reduce_facet_edge_length(self, o, reduction_op):
        if self._preserve_types[o._ufl_typecode_]:
            return o

        domain = o.ufl_domain()

        if domain.ufl_cell().topological_dimension() < 3:
            error("Facet edge lengths only make sense for topological dimension >= 3.")

        elif not domain.ufl_coordinate_element().degree() == 1:
            # Don't lower bendy cells, instead leave it to form compiler
            warning("Only know how to compute facet edge lengths of P1 or Q1 cell.")
            return o

        else:
            # P1 tetrahedron or Q1 hexahedron
            edges = FacetEdgeVectors(domain)
            num_edges = edges.ufl_shape[0]
            j = Index()
            elen2 = [edges[e, j]*edges[e, j] for e in range(num_edges)]
            return sqrt(reduce(reduction_op, elen2))
Example #38
0
    def evaluate(self, x, mapping, component, index_values, derivatives=()):
        "Get *self* from *mapping* and return the component asked for."
        f = mapping.get(self)
        # No mapping, trying to evaluate self as a constant
        if f is None:
            try:
                try:
                    f = float(self)
                except TypeError:
                    f = complex(self)
                if derivatives:
                    f = 0.0
                return f
            except Exception:
                pass
            # If it has an ufl_evaluate function, call it
            if hasattr(self, 'ufl_evaluate'):
                return self.ufl_evaluate(x, component, derivatives)
            # Take component if any
            warning("Couldn't map '%s' to a float, returning ufl object without evaluation." % str(self))
            f = self
            if component:
                f = f[component]
            return f

        # Found a callable in the mapping
        if callable(f):
            if derivatives:
                f = f(x, derivatives)
            else:
                f = f(x)
        else:
            if derivatives:
                return 0.0

        # Take component if any (expecting nested tuple)
        for c in component:
            f = f[c]
        return f
Example #39
0
def expand_derivatives(form, **kwargs):
    """Expand all derivatives of expr.

    In the returned expression g which is mathematically
    equivalent to expr, there are no VariableDerivative
    or CoefficientDerivative objects left, and Grad
    objects have been propagated to Terminal nodes.
    """
    # For a deprecation period (I see that dolfin-adjoint passes some
    # args here)
    if kwargs:
        warning(
            "Deprecation: expand_derivatives no longer takes any keyword arguments"
        )

    # Lower abstractions for tensor-algebra types into index notation
    form = apply_algebra_lowering(form)

    # Apply differentiation
    form = apply_derivatives(form)

    return form
Example #40
0
    def cell_diameter(self, o):
        if self._preserve_types[o._ufl_typecode_]:
            return o

        domain = o.ufl_domain()

        if not domain.ufl_coordinate_element().degree() in {1, (1, 1)}:
            # Don't lower bendy cells, instead leave it to form compiler
            warning("Only know how to compute cell diameter of P1 or Q1 cell.")
            return o

        elif domain.is_piecewise_linear_simplex_domain():
            # Simplices
            return self.max_cell_edge_length(MaxCellEdgeLength(domain))

        else:
            # Q1 cells, maximal distance between any two vertices
            verts = CellVertices(domain)
            verts = [verts[v, ...] for v in range(verts.ufl_shape[0])]
            j = Index()
            elen2 = (real((v0 - v1)[j] * conj((v0 - v1)[j])) for v0, v1 in combinations(verts, 2))
            return real(sqrt(reduce(max_value, elen2)))
Example #41
0
    def _reduce_cell_edge_length(self, o, reduction_op):
        if self._preserve_types[o._ufl_typecode_]:
            return o

        domain = o.ufl_domain()

        if not domain.ufl_coordinate_element().degree() == 1:
            # Don't lower bendy cells, instead leave it to form compiler
            warning("Only know how to compute cell edge lengths of P1 or Q1 cell.")
            return o

        elif domain.ufl_cell().cellname() == "interval":
            # Interval optimization, square root not needed
            return self.cell_volume(CellVolume(domain))

        else:
            # Other P1 or Q1 cells
            edges = CellEdgeVectors(domain)
            num_edges = edges.ufl_shape[0]
            j = Index()
            elen2 = [real(edges[e, j] * conj(edges[e, j])) for e in range(num_edges)]
            return real(sqrt(reduce(reduction_op, elen2)))
def is_multilinear(form):
    "Check if form is multilinear in arguments."
    # An attempt at implementing is_multilinear using extract_argument_dependencies.
    # TODO: This has some false negatives for "multiple configurations". (Does it still? Needs testing!)
    # TODO: FFC probably needs a variant of this which checks for some sorts of linearity
    #       in Coefficients as well, this should be a fairly simple extension of the current algorithm.
    try:
        for e in iter_expressions(form):
            deps = extract_argument_dependencies(e)
            nargs = [len(d) for d in deps]
            if len(nargs) == 0:
                debug("This form is a functional.")
            if len(nargs) == 1:
                debug("This form is linear in %d arguments." % nargs[0])
            if len(nargs) > 1:
                warning("This form has more than one argument "\
                    "'configuration', it has terms that are linear in %s "\
                    "arguments respectively." % str(nargs))

    except NotMultiLinearException, msg:
        warning("Form is not multilinear, the offending term is: %s" % msg)
        return False
Example #43
0
    def cell_diameter(self, o):
        if self._preserve_types[o._ufl_typecode_]:
            return o

        domain = o.ufl_domain()

        if not domain.ufl_coordinate_element().degree() == 1:
            # Don't lower bendy cells, instead leave it to form compiler
            warning("Only know how to compute cell diameter of P1 or Q1 cell.")
            return o

        elif domain.is_piecewise_linear_simplex_domain():
            # Simplices
            return self.max_cell_edge_length(MaxCellEdgeLength(domain))

        else:
            # Q1 cells, maximal distance between any two vertices
            verts = CellVertices(domain)
            verts = [verts[v, ...] for v in range(verts.ufl_shape[0])]
            j = Index()
            elen2 = ((v0-v1)[j]*(v0-v1)[j] for v0, v1 in combinations(verts, 2))
            return sqrt(reduce(max_value, elen2))
Example #44
0
    def _reduce_cell_edge_length(self, o, reduction_op):
        if self._preserve_types[o._ufl_typecode_]:
            return o

        domain = o.ufl_domain()

        if not domain.ufl_coordinate_element().degree() == 1:
            # Don't lower bendy cells, instead leave it to form compiler
            warning("Only know how to compute cell edge lengths of P1 or Q1 cell.")
            return o

        elif domain.ufl_cell().cellname() == "interval":
            # Interval optimization, square root not needed
            return self.cell_volume(CellVolume(domain))

        else:
            # Other P1 or Q1 cells
            edges = CellEdgeVectors(domain)
            num_edges = edges.ufl_shape[0]
            j = Index()
            elen2 = [edges[e, j]*edges[e, j] for e in range(num_edges)]
            return sqrt(reduce(reduction_op, elen2))
def is_multilinear(form):
    "Check if form is multilinear in arguments."
    # An attempt at implementing is_multilinear using extract_argument_dependencies.
    # TODO: This has some false negatives for "multiple configurations". (Does it still? Needs testing!)
    # TODO: FFC probably needs a variant of this which checks for some sorts of linearity
    #       in Coefficients as well, this should be a fairly simple extension of the current algorithm.
    try:
        for e in iter_expressions(form):
            deps = extract_argument_dependencies(e)
            nargs = [len(d) for d in deps]
            if len(nargs) == 0:
                debug("This form is a functional.")
            if len(nargs) == 1:
                debug("This form is linear in %d arguments." % nargs[0])
            if len(nargs) > 1:
                warning("This form has more than one argument "\
                    "'configuration', it has terms that are linear in %s "\
                    "arguments respectively." % str(nargs))

    except NotMultiLinearException, msg:
        warning("Form is not multilinear, the offending term is: %s" % msg)
        return False
Example #46
0
def canonicalize_metadata(metadata):
    """Assuming metadata to be a dict with string keys and builtin python types as values.

    Transform dict to a tuple of (key, value) item tuples ordered by key,
    with dict, list and tuple values converted the same way recursively.
    Lists and tuples are converted to tuples. Other values are converted using str().
    This is such that the end result can be hashed and sorted using regular <,
    because python 3 doesn't allow e.g. (3 < "auto") which occurs regularly in metadata.
    """
    if metadata is None:
        return ()

    if isinstance(metadata, dict):
        keys = sorted(metadata.keys())
        assert all(isinstance(key, str) for key in keys)
        values = [metadata[key] for key in keys]
    elif isinstance(metadata, (tuple, list)):
        values = metadata

    newvalues = []
    for value in values:
        if isinstance(value, (dict, list, tuple)):
            value = canonicalize_metadata(value)
        elif isinstance(value, (int, float, str)) or value is None:
            value = str(value)
        else:
            warning(
                "Applying str() to a metadata value of type {0}, don't know if this is safe."
                .format(type(value).__name__))
            value = str(value)
        newvalues.append(value)

    if isinstance(metadata, dict):
        return tuple(zip(keys, newvalues))
    else:
        return tuple(newvalues)
Example #47
0
def interpret_ufl_namespace(namespace):
    "Takes a namespace dict from an executed ufl file and converts it to a FileData object."
    # Object to hold all returned data
    ufd = FileData()

    # Extract object names for Form, Coefficient and FiniteElementBase objects
    # The use of id(obj) as key in object_names is necessary
    # because we need to distinguish between instances,
    # and not just between objects with different values.
    for name, value in sorted_by_key(namespace):
        # Store objects by reserved name OR instance id
        reserved_names = ("unknown", )  # Currently only one reserved name
        if name in reserved_names:
            # Store objects with reserved names
            ufd.reserved_objects[name] = value
            # FIXME: Remove after FFC is updated to use reserved_objects:
            ufd.object_names[name] = value
            ufd.object_by_name[name] = value
        elif isinstance(
                value, (FiniteElementBase, Coefficient, Argument, Form, Expr)):
            # Store instance <-> name mappings for important objects
            # without a reserved name
            ufd.object_names[id(value)] = name
            ufd.object_by_name[name] = value

    # Get list of exported forms
    forms = namespace.get("forms")
    if forms is None:
        # Get forms from object_by_name, which has already mapped
        # tuple->Form where needed
        def get_form(name):
            form = ufd.object_by_name.get(name)
            return form if isinstance(form, Form) else None

        a_form = get_form("a")
        L_form = get_form("L")
        M_form = get_form("M")
        forms = [a_form, L_form, M_form]
        # Add forms F and J if not "a" and "L" are used
        if a_form is None or L_form is None:
            F_form = get_form("F")
            J_form = get_form("J")
            forms += [F_form, J_form]
        # Remove Nones
        forms = [f for f in forms if isinstance(f, Form)]
    ufd.forms = forms

    # Validate types
    if not isinstance(ufd.forms, (list, tuple)):
        error("Expecting 'forms' to be a list or tuple, not '%s'." %
              type(ufd.forms))
    if not all(isinstance(a, Form) for a in ufd.forms):
        error("Expecting 'forms' to be a list of Form instances.")

    # Get list of exported elements
    elements = namespace.get("elements")
    if elements is None:
        elements = [ufd.object_by_name.get(name) for name in ("element", )]
        elements = [e for e in elements if e is not None]
    ufd.elements = elements

    # Validate types
    if not isinstance(ufd.elements, (list, tuple)):
        error("Expecting 'elements' to be a list or tuple, not '%s'." %
              type(ufd.elements))
    if not all(isinstance(e, FiniteElementBase) for e in ufd.elements):
        error(
            "Expecting 'elements' to be a list of FiniteElementBase instances."
        )

    # Get list of exported coefficients
    # TODO: Temporarily letting 'coefficients' override 'functions',
    # but allow 'functions' for compatibility
    functions = namespace.get("functions", [])
    if functions:
        warning(
            "Deprecation warning: Rename 'functions' to 'coefficients' to export coefficients."
        )
    ufd.coefficients = namespace.get("coefficients", functions)

    # Validate types
    if not isinstance(ufd.coefficients, (list, tuple)):
        error("Expecting 'coefficients' to be a list or tuple, not '%s'." %
              type(ufd.coefficients))
    if not all(isinstance(e, Coefficient) for e in ufd.coefficients):
        error(
            "Expecting 'coefficients' to be a list of Coefficient instances.")

    # Get list of exported expressions
    ufd.expressions = namespace.get("expressions", [])

    # Validate types
    if not isinstance(ufd.expressions, (list, tuple)):
        error("Expecting 'expressions' to be a list or tuple, not '%s'." %
              type(ufd.expressions))
    if not all(isinstance(e, Expr) for e in ufd.expressions):
        error("Expecting 'expressions' to be a list of Expr instances.")

    # Return file data
    return ufd
Example #48
0
def interpret_ufl_namespace(namespace):
    "Takes a namespace dict from an executed ufl file and converts it to a FileData object."
    # Object to hold all returned data
    ufd = FileData()

    # Extract object names for Form, Coefficient and FiniteElementBase objects
    # The use of id(obj) as key in object_names is necessary
    # because we need to distinguish between instances,
    # and not just between objects with different values.
    for name, value in sorted_by_key(namespace):
        # Store objects by reserved name OR instance id
        reserved_names = ("unknown",)  # Currently only one reserved name
        if name in reserved_names:
            # Store objects with reserved names
            ufd.reserved_objects[name] = value
            # FIXME: Remove after FFC is updated to use reserved_objects:
            ufd.object_names[name] = value
            ufd.object_by_name[name] = value
        elif isinstance(value, (FiniteElementBase, Coefficient, Argument, Form, Expr)):
            # Store instance <-> name mappings for important objects
            # without a reserved name
            ufd.object_names[id(value)] = name
            ufd.object_by_name[name] = value

    # Get list of exported forms
    forms = namespace.get("forms")
    if forms is None:
        # Get forms from object_by_name, which has already mapped
        # tuple->Form where needed
        def get_form(name):
            form = ufd.object_by_name.get(name)
            return form if isinstance(form, Form) else None
        a_form = get_form("a")
        L_form = get_form("L")
        M_form = get_form("M")
        forms = [a_form, L_form, M_form]
        # Add forms F and J if not "a" and "L" are used
        if a_form is None or L_form is None:
            F_form = get_form("F")
            J_form = get_form("J")
            forms += [F_form, J_form]
        # Remove Nones
        forms = [f for f in forms if isinstance(f, Form)]
    ufd.forms = forms

    # Validate types
    if not isinstance(ufd.forms, (list, tuple)):
        error("Expecting 'forms' to be a list or tuple, not '%s'." % type(ufd.forms))
    if not all(isinstance(a, Form) for a in ufd.forms):
        error("Expecting 'forms' to be a list of Form instances.")

    # Get list of exported elements
    elements = namespace.get("elements")
    if elements is None:
        elements = [ufd.object_by_name.get(name) for name in ("element",)]
        elements = [e for e in elements if e is not None]
    ufd.elements = elements

    # Validate types
    if not isinstance(ufd.elements, (list, tuple)):
        error("Expecting 'elements' to be a list or tuple, not '%s'." % type(ufd.elements))
    if not all(isinstance(e, FiniteElementBase) for e in ufd.elements):
        error("Expecting 'elements' to be a list of FiniteElementBase instances.")

    # Get list of exported coefficients
    # TODO: Temporarily letting 'coefficients' override 'functions',
    # but allow 'functions' for compatibility
    functions = namespace.get("functions", [])
    if functions:
        warning("Deprecation warning: Rename 'functions' to 'coefficients' to export coefficients.")
    ufd.coefficients = namespace.get("coefficients", functions)

    # Validate types
    if not isinstance(ufd.coefficients, (list, tuple)):
        error("Expecting 'coefficients' to be a list or tuple, not '%s'." % type(ufd.coefficients))
    if not all(isinstance(e, Coefficient) for e in ufd.coefficients):
        error("Expecting 'coefficients' to be a list of Coefficient instances.")

    # Get list of exported expressions
    ufd.expressions = namespace.get("expressions", [])

    # Validate types
    if not isinstance(ufd.expressions, (list, tuple)):
        error("Expecting 'expressions' to be a list or tuple, not '%s'." % type(ufd.expressions))
    if not all(isinstance(e, Expr) for e in ufd.expressions):
        error("Expecting 'expressions' to be a list of Expr instances.")

    # Return file data
    return ufd
Example #49
0
    def grad(self, g):
        # If we hit this type, it has already been propagated to a
        # coefficient (or grad of a coefficient), # FIXME: Assert
        # this!  so we need to take the gradient of the variation or
        # return zero.  Complications occur when dealing with
        # derivatives w.r.t. single components...

        # Figure out how many gradients are around the inner terminal
        ngrads = 0
        o = g
        while isinstance(o, Grad):
            o, = o.ufl_operands
            ngrads += 1
        if not isinstance(o, FormArgument):
            error("Expecting gradient of a FormArgument, not %s" %
                  ufl_err_str(o))

        def apply_grads(f):
            for i in range(ngrads):
                f = Grad(f)
            return f

        # Find o among all w without any indexing, which makes this
        # easy
        for (w, v) in zip(self._w, self._v):
            if o == w and isinstance(v, FormArgument):
                # Case: d/dt [w + t v]
                return apply_grads(v)

        # If o is not among coefficient derivatives, return do/dw=0
        gprimesum = Zero(g.ufl_shape)

        def analyse_variation_argument(v):
            # Analyse variation argument
            if isinstance(v, FormArgument):
                # Case: d/dt [w[...] + t v]
                vval, vcomp = v, ()
            elif isinstance(v, Indexed):
                # Case: d/dt [w + t v[...]]
                # Case: d/dt [w[...] + t v[...]]
                vval, vcomp = v.ufl_operands
                vcomp = tuple(vcomp)
            else:
                error("Expecting argument or component of argument.")
            if not all(isinstance(k, FixedIndex) for k in vcomp):
                error("Expecting only fixed indices in variation.")
            return vval, vcomp

        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

        # Accumulate contributions from variations in different
        # components
        for (w, v) in zip(self._w, self._v):

            # Analyse differentiation variable coefficient
            if isinstance(w, FormArgument):
                if not w == o:
                    continue
                wshape = w.ufl_shape

                if isinstance(v, FormArgument):
                    # Case: d/dt [w + t v]
                    return apply_grads(v)

                elif isinstance(v, ListTensor):
                    # Case: d/dt [w + t <...,v,...>]
                    for wcomp, vsub in unwrap_list_tensor(v):
                        if not isinstance(vsub, Zero):
                            vval, vcomp = analyse_variation_argument(vsub)
                            gprimesum = gprimesum + compute_gprimeterm(
                                ngrads, vval, vcomp, wshape, wcomp)

                else:
                    if wshape != ():
                        error("Expecting scalar coefficient in this branch.")
                    # Case: d/dt [w + t v[...]]
                    wval, wcomp = w, ()

                    vval, vcomp = analyse_variation_argument(v)
                    gprimesum = gprimesum + compute_gprimeterm(
                        ngrads, vval, vcomp, wshape, wcomp)

            elif isinstance(
                    w, Indexed
            ):  # This path is tested in unit tests, but not actually used?
                # Case: d/dt [w[...] + t v[...]]
                # Case: d/dt [w[...] + t v]
                wval, wcomp = w.ufl_operands
                if not wval == o:
                    continue
                assert isinstance(wval, FormArgument)
                if not all(isinstance(k, FixedIndex) for k in wcomp):
                    error(
                        "Expecting only fixed indices in differentiation variable."
                    )
                wshape = wval.ufl_shape

                vval, vcomp = analyse_variation_argument(v)
                gprimesum = gprimesum + compute_gprimeterm(
                    ngrads, vval, vcomp, wshape, wcomp)

            else:
                error("Expecting coefficient or component of coefficient.")

        # FIXME: Handle other coefficient derivatives: oprimes =
        # self._cd.get(o)

        if 0:
            oprimes = self._cd.get(o)
            if oprimes is None:
                if self._cd:
                    # TODO: Make it possible to silence this message
                    #       in particular?  It may be good to have for
                    #       debugging...
                    warning("Assuming d{%s}/d{%s} = 0." % (o, self._w))
            else:
                # Make sure we have a tuple to match the self._v tuple
                if not isinstance(oprimes, tuple):
                    oprimes = (oprimes, )
                    if len(oprimes) != len(self._v):
                        error("Got a tuple of arguments, expecting a"
                              " matching tuple of coefficient derivatives.")

                # Compute dg/dw_j = dg/dw_h : v.
                # Since we may actually have a tuple of oprimes and vs
                # in a 'mixed' space, sum over them all to get the
                # complete inner product. Using indices to define a
                # non-compound inner product.
                for (oprime, v) in zip(oprimes, self._v):
                    error("FIXME: Figure out how to do this with ngrads")
                    so, oi = as_scalar(oprime)
                    rv = len(v.ufl_shape)
                    oi1 = oi[:-rv]
                    oi2 = oi[-rv:]
                    prod = so * v[oi2]
                    if oi1:
                        gprimesum += as_tensor(prod, oi1)
                    else:
                        gprimesum += prod

        return gprimesum
Example #50
0
 def expr(self, v, *ops):
     "For most operators we take the max degree of its operands."
     warning("Missing degree estimation handler for type %s" %
             v._ufl_class_.__name__)
     return self._add_degrees(v, *ops)
def flatten(e): # TODO: Fix or remove! Maybe this works better now with IndexSum marking implicit summations.
    """Convert an UFL expression to a new UFL expression, with sums
    and products flattened from binary tree nodes to n-ary tree nodes."""
    warning("flatten doesn't work correctly for some indexed products, like (u[i]*v[i])*(q[i]*r[i])")
    return apply_transformer(e, TreeFlattener())
 def expr(self, v, *ops):
     "For most operators we take the max degree of its operands."
     warning("Missing degree estimation handler for type %s" % v._uflclass.__name__)
     return self._add_degrees(v, *ops)
Example #53
0
 def domain(self):
     warning("Cell.domain() is deprecated, use cell.cellname() instead.")
     return self.cellname()
Example #54
0
 def is_undefined(self):
     """Return whether this cell is undefined,
     in which case no dimensions are available."""
     warning("cell.is_undefined() is deprecated, undefined cells are no longer allowed.")
     return False
Example #55
0
def canonical_element_description(family, cell, order, form_degree):
    """Given basic element information, return corresponding element information on canonical form.

    Input: family, cell, (polynomial) order, form_degree
    Output: family (canonical), short_name (for printing), order, value shape,
    reference value shape, sobolev_space.

    This is used by the FiniteElement constructor to ved input
    data against the element list and aliases defined in ufl.
    """

    # Get domain dimensions
    if cell is not None:
        tdim = cell.topological_dimension()
        gdim = cell.geometric_dimension()
        if isinstance(cell, Cell):
            cellname = cell.cellname()
        else:
            cellname = None
    else:
        tdim = None
        gdim = None
        cellname = None

    # Catch general FEEC notation "P" and "S"
    if form_degree is not None and family in ("P", "S"):
        family, order = feec_element(family, tdim, order, form_degree)

    if form_degree is not None and family in ("P L2", "S L2"):
        family, order = feec_element_l2(family, tdim, order, form_degree)

    # Check whether this family is an alias for something else
    while family in aliases:
        if tdim is None:
            error("Need dimension to handle element aliases.")
        (family, order) = aliases[family](family, tdim, order, form_degree)

    # Check that the element family exists
    if family not in ufl_elements:
        error('Unknown finite element "%s".' % family)

    # Check that element data is valid (and also get common family
    # name)
    (family, short_name, value_rank, sobolev_space, mapping, krange, cellnames) = ufl_elements[family]

    # Accept CG/DG on all kind of cells, but use Q/DQ on "product" cells
    if cellname in set(cubes) - set(simplices) or isinstance(cell, TensorProductCell):
        if family == "Lagrange":
            family = "Q"
        elif family == "Discontinuous Lagrange":
            if order >= 1:
                warning("Discontinuous Lagrange element requested on %s, creating DQ element." % cell.cellname())
            family = "DQ"
        elif family == "Discontinuous Lagrange L2":
            if order >= 1:
                warning("Discontinuous Lagrange L2 element requested on %s, creating DQ L2 element." % cell.cellname())
            family = "DQ L2"

    # Validate cellname if a valid cell is specified
    if not (cellname is None or cellname in cellnames):
        error('Cellname "%s" invalid for "%s" finite element.' % (cellname, family))

    # Validate order if specified
    if order is not None:
        if krange is None:
            error('Order "%s" invalid for "%s" finite element, '
                  'should be None.' % (order, family))
        kmin, kmax = krange
        if not (kmin is None or (asarray(order) >= kmin).all()):
            error('Order "%s" invalid for "%s" finite element.' %
                  (order, family))
        if not (kmax is None or (asarray(order) <= kmax).all()):
            error('Order "%s" invalid for "%s" finite element.' %
                  (istr(order), family))

    # Override sobolev_space for piecewise constants (TODO: necessary?)
    if order == 0:
        sobolev_space = L2
    if value_rank == 2:
        # Tensor valued fundamental elements in HEin have this shape
        if gdim is None or tdim is None:
            error("Cannot infer shape of element without topological and geometric dimensions.")
        reference_value_shape = (tdim, tdim)
        value_shape = (gdim, gdim)
    elif value_rank == 1:
        # Vector valued fundamental elements in HDiv and HCurl have a shape
        if gdim is None or tdim is None:
            error("Cannot infer shape of element without topological and geometric dimensions.")
        reference_value_shape = (tdim,)
        value_shape = (gdim,)
    elif value_rank == 0:
        # All other elements are scalar values
        reference_value_shape = ()
        value_shape = ()
    else:
        error("Invalid value rank %d." % value_rank)

    return family, short_name, order, value_shape, reference_value_shape, sobolev_space, mapping
Example #56
0
    def grad(self, g):
        # If we hit this type, it has already been propagated to a
        # coefficient (or grad of a coefficient), # FIXME: Assert
        # this!  so we need to take the gradient of the variation or
        # return zero.  Complications occur when dealing with
        # derivatives w.r.t. single components...

        # Figure out how many gradients are around the inner terminal
        ngrads = 0
        o = g
        while isinstance(o, Grad):
            o, = o.ufl_operands
            ngrads += 1
        if not isinstance(o, FormArgument):
            error("Expecting gradient of a FormArgument, not %s" % ufl_err_str(o))

        def apply_grads(f):
            for i in range(ngrads):
                f = Grad(f)
            return f

        # Find o among all w without any indexing, which makes this
        # easy
        for (w, v) in zip(self._w, self._v):
            if o == w and isinstance(v, FormArgument):
                # Case: d/dt [w + t v]
                return apply_grads(v)

        # If o is not among coefficient derivatives, return do/dw=0
        gprimesum = Zero(g.ufl_shape)

        def analyse_variation_argument(v):
            # Analyse variation argument
            if isinstance(v, FormArgument):
                # Case: d/dt [w[...] + t v]
                vval, vcomp = v, ()
            elif isinstance(v, Indexed):
                # Case: d/dt [w + t v[...]]
                # Case: d/dt [w[...] + t v[...]]
                vval, vcomp = v.ufl_operands
                vcomp = tuple(vcomp)
            else:
                error("Expecting argument or component of argument.")
            if not all(isinstance(k, FixedIndex) for k in vcomp):
                error("Expecting only fixed indices in variation.")
            return vval, vcomp

        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

        # Accumulate contributions from variations in different
        # components
        for (w, v) in zip(self._w, self._v):

            # Analyse differentiation variable coefficient
            if isinstance(w, FormArgument):
                if not w == o:
                    continue
                wshape = w.ufl_shape

                if isinstance(v, FormArgument):
                    # Case: d/dt [w + t v]
                    return apply_grads(v)

                elif isinstance(v, ListTensor):
                    # Case: d/dt [w + t <...,v,...>]
                    for wcomp, vsub in unwrap_list_tensor(v):
                        if not isinstance(vsub, Zero):
                            vval, vcomp = analyse_variation_argument(vsub)
                            gprimesum = gprimesum + compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp)

                else:
                    if wshape != ():
                        error("Expecting scalar coefficient in this branch.")
                    # Case: d/dt [w + t v[...]]
                    wval, wcomp = w, ()

                    vval, vcomp = analyse_variation_argument(v)
                    gprimesum = gprimesum + compute_gprimeterm(ngrads, vval,
                                                               vcomp, wshape,
                                                               wcomp)

            elif isinstance(w, Indexed):  # This path is tested in unit tests, but not actually used?
                # Case: d/dt [w[...] + t v[...]]
                # Case: d/dt [w[...] + t v]
                wval, wcomp = w.ufl_operands
                if not wval == o:
                    continue
                assert isinstance(wval, FormArgument)
                if not all(isinstance(k, FixedIndex) for k in wcomp):
                    error("Expecting only fixed indices in differentiation variable.")
                wshape = wval.ufl_shape

                vval, vcomp = analyse_variation_argument(v)
                gprimesum = gprimesum + compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp)

            else:
                error("Expecting coefficient or component of coefficient.")

        # FIXME: Handle other coefficient derivatives: oprimes =
        # self._cd.get(o)

        if 0:
            oprimes = self._cd.get(o)
            if oprimes is None:
                if self._cd:
                    # TODO: Make it possible to silence this message
                    #       in particular?  It may be good to have for
                    #       debugging...
                    warning("Assuming d{%s}/d{%s} = 0." % (o, self._w))
            else:
                # Make sure we have a tuple to match the self._v tuple
                if not isinstance(oprimes, tuple):
                    oprimes = (oprimes,)
                    if len(oprimes) != len(self._v):
                        error("Got a tuple of arguments, expecting a"
                              " matching tuple of coefficient derivatives.")

                # Compute dg/dw_j = dg/dw_h : v.
                # Since we may actually have a tuple of oprimes and vs
                # in a 'mixed' space, sum over them all to get the
                # complete inner product. Using indices to define a
                # non-compound inner product.
                for (oprime, v) in zip(oprimes, self._v):
                    error("FIXME: Figure out how to do this with ngrads")
                    so, oi = as_scalar(oprime)
                    rv = len(v.ufl_shape)
                    oi1 = oi[:-rv]
                    oi2 = oi[-rv:]
                    prod = so*v[oi2]
                    if oi1:
                        gprimesum += as_tensor(prod, oi1)
                    else:
                        gprimesum += prod

        return gprimesum
Example #57
0
def canonical_element_description(family, cell, order, form_degree):
    """Given basic element information, return corresponding element information on canonical form.

    Input: family, cell, (polynomial) order, form_degree
    Output: family (canonical), short_name (for printing), order, value shape,
    reference value shape, sobolev_space.

    This is used by the FiniteElement constructor to ved input
    data against the element list and aliases defined in ufl.
    """

    # Get domain dimensions
    if cell is not None:
        tdim = cell.topological_dimension()
        gdim = cell.geometric_dimension()
        if isinstance(cell, Cell):
            cellname = cell.cellname()
        else:
            cellname = None
    else:
        tdim = None
        gdim = None
        cellname = None

    # Catch general FEEC notation "P" and "S"
    if form_degree is not None and family in ("P", "S"):
        family, order = feec_element(family, tdim, order, form_degree)

    # Check whether this family is an alias for something else
    while family in aliases:
        if tdim is None:
            error("Need dimension to handle element aliases.")
        (family, order) = aliases[family](family, tdim, order, form_degree)

    # Check that the element family exists
    if family not in ufl_elements:
        error('Unknown finite element "%s".' % family)

    # Check that element data is valid (and also get common family
    # name)
    (family, short_name, value_rank, sobolev_space, mapping, krange, cellnames) = ufl_elements[family]

    # Accept CG/DG on all kind of cells, but use Q/DQ on "product" cells
    if cellname in set(cubes) - set(simplices) or isinstance(cell, TensorProductCell):
        if family == "Lagrange":
            family = "Q"
        elif family == "Discontinuous Lagrange":
            if order >= 1:
                warning("Discontinuous Lagrange element requested on %s, creating DQ element." % cell.cellname())
            family = "DQ"

    # Validate cellname if a valid cell is specified
    if not (cellname is None or cellname in cellnames):
        error('Cellname "%s" invalid for "%s" finite element.' % (cellname, family))

    # Validate order if specified
    if order is not None:
        if krange is None:
            error('Order "%s" invalid for "%s" finite element, '
                  'should be None.' % (order, family))
        kmin, kmax = krange
        if not (kmin is None or (asarray(order) >= kmin).all()):
            error('Order "%s" invalid for "%s" finite element.' %
                  (order, family))
        if not (kmax is None or (asarray(order) <= kmax).all()):
            error('Order "%s" invalid for "%s" finite element.' %
                  (istr(order), family))

    # Override sobolev_space for piecewise constants (TODO: necessary?)
    if order == 0:
        sobolev_space = L2
    if value_rank == 2:
        # Tensor valued fundamental elements in HEin have this shape
        if gdim is None or tdim is None:
            error("Cannot infer shape of element without topological and geometric dimensions.")
        reference_value_shape = (tdim, tdim)
        value_shape = (gdim, gdim)
    elif value_rank == 1:
        # Vector valued fundamental elements in HDiv and HCurl have a shape
        if gdim is None or tdim is None:
            error("Cannot infer shape of element without topological and geometric dimensions.")
        reference_value_shape = (tdim,)
        value_shape = (gdim,)
    elif value_rank == 0:
        # All other elements are scalar values
        reference_value_shape = ()
        value_shape = ()
    else:
        error("Invalid value rank %d." % value_rank)

    return family, short_name, order, value_shape, reference_value_shape, sobolev_space, mapping