Exemple #1
0
class Outer(CompoundTensorOperator):
    __slots__ = as_native_strings((
        "ufl_free_indices",
        "ufl_index_dimensions",
    ))

    def __new__(cls, a, b):
        ash, bsh = a.ufl_shape, b.ufl_shape
        if isinstance(a, Zero) or isinstance(b, Zero):
            fi, fid = merge_nonoverlapping_indices(a, b)
            return Zero(ash + bsh, fi, fid)
        if ash == () or bsh == ():
            return a * b
        return CompoundTensorOperator.__new__(cls)

    def __init__(self, a, b):
        CompoundTensorOperator.__init__(self, (a, b))
        fi, fid = merge_nonoverlapping_indices(a, b)
        self.ufl_free_indices = fi
        self.ufl_index_dimensions = fid

    @property
    def ufl_shape(self):
        return self.ufl_operands[0].ufl_shape + self.ufl_operands[1].ufl_shape

    def __str__(self):
        return "%s (X) %s" % (parstr(self.ufl_operands[0],
                                     self), parstr(self.ufl_operands[1], self))
Exemple #2
0
class MeasureSum(object):
    """Represents a sum of measures.

    This is a notational intermediate object to translate the notation

        f*(ds(1)+ds(3))

    into

        f*ds(1) + f*ds(3)
    """
    __slots__ = as_native_strings(("_measures", ))

    def __init__(self, *measures):
        self._measures = measures

    def __rmul__(self, other):
        integrals = [other * m for m in self._measures]
        return sum(integrals)

    def __add__(self, other):
        if isinstance(other, Measure):
            return MeasureSum(*(self._measures + (other, )))
        elif isinstance(other, MeasureSum):
            return MeasureSum(*(self._measures + other._measures))
        return NotImplemented

    def __unicode__(self):
        # Only in python 2
        return str(self).decode("utf-8")

    def __str__(self):
        return "{\n    " + "\n  + ".join(map(str, self._measures)) + "\n}"
Exemple #3
0
class Operator(Expr):
    "Base class for all operators, i.e. non-terminal expression types."
    __slots__ = as_native_strings(("ufl_operands", ))

    def __init__(self, operands=None):
        Expr.__init__(self)

        # If operands is None, the type sets this itself. This is to
        # get around some tricky too-fancy __new__/__init__ design in
        # algebra.py, for now.  It would be nicer to make the classes
        # in algebra.py pass operands here.
        if operands is not None:
            self.ufl_operands = operands

    def _ufl_expr_reconstruct_(self, *operands):
        "Return a new object of the same type with new operands."
        return self._ufl_class_(*operands)

    def _ufl_signature_data_(self):
        return self._ufl_typecode_

    def _ufl_compute_hash_(self):
        "Compute a hash code for this expression. Used by sets and dicts."
        return hash((self._ufl_typecode_, ) +
                    tuple(hash(o) for o in self.ufl_operands))

    def __repr__(self):
        "Default repr string construction for operators."
        # This should work for most cases
        r = "%s(%s)" % (self._ufl_class_.__name__, ", ".join(
            repr(op) for op in self.ufl_operands))
        return as_native_str(r)
Exemple #4
0
class HCurlElement(FiniteElementBase):
    """A curl-conforming version of an outer product element, assuming
    this makes mathematical sense."""
    __slots__ = as_native_strings(("_element", ))

    def __init__(self, element):
        self._element = element
        self._repr = as_native_str("HCurlElement(%s)" % repr(element))

        family = "TensorProductElement"
        cell = element.cell()
        degree = element.degree()
        quad_scheme = element.quadrature_scheme()
        cell = element.cell()
        value_shape = (cell.geometric_dimension(), )
        reference_value_shape = (cell.topological_dimension(),
                                 )  # TODO: Is this right?
        # Skipping TensorProductElement constructor! Bad code smell,
        # refactor to avoid this non-inheritance somehow.
        FiniteElementBase.__init__(self, family, cell, degree, quad_scheme,
                                   value_shape, reference_value_shape)

    def mapping(self):
        return "covariant Piola"

    def reconstruct(self, **kwargs):
        return HCurlElement(self._element.reconstruct(**kwargs))

    def __str__(self):
        return "HCurlElement(%s)" % str(self._element)

    def shortstr(self):
        "Format as string for pretty printing."
        return "HCurlElement(%s)" % str(self._element.shortstr())
Exemple #5
0
class BinaryCondition(Condition):
    __slots__ = as_native_strings(('_name',))

    def __init__(self, name, left, right):
        left = as_ufl(left)
        right = as_ufl(right)

        Condition.__init__(self, (left, right))

        self._name = name

        if name in ('!=', '=='):
            # Since equals and not-equals are used for comparing
            # representations, we have to allow any shape here. The
            # scalar properties must be checked when used in
            # conditional instead!
            pass
        elif name in ('&&', '||'):
            # Binary operators acting on boolean expressions allow
            # only conditions
            for arg in (left, right):
                if not isinstance(arg, Condition):
                    error("Expecting a Condition, not %s." % ufl_err_str(arg))
        else:
            # Binary operators acting on non-boolean expressions allow
            # only scalars
            if left.ufl_shape != () or right.ufl_shape != ():
                error("Expecting scalar arguments.")
            if left.ufl_free_indices != () or right.ufl_free_indices != ():
                error("Expecting scalar arguments.")

    def __str__(self):
        return "%s %s %s" % (parstr(self.ufl_operands[0], self),
                             self._name, parstr(self.ufl_operands[1], self))
Exemple #6
0
class Identity(ConstantValue):
    "UFL literal type: Representation of an identity matrix."
    __slots__ = as_native_strings(("_dim", "ufl_shape"))

    def __init__(self, dim):
        ConstantValue.__init__(self)
        self._dim = dim
        self.ufl_shape = (dim, dim)

    def evaluate(self, x, mapping, component, index_values):
        "Evaluates the identity matrix on the given components."
        a, b = component
        return 1 if a == b else 0

    def __getitem__(self, key):
        if len(key) != 2:
            error("Size mismatch for Identity.")
        if all(isinstance(k, (int, FixedIndex)) for k in key):
            return IntValue(1) if (int(key[0]) == int(key[1])) else Zero()
        return Expr.__getitem__(self, key)

    def __str__(self):
        return "I"

    def __repr__(self):
        r = "Identity(%d)" % self._dim
        return as_native_str(r)

    def __eq__(self, other):
        return isinstance(other, Identity) and self._dim == other._dim
Exemple #7
0
class NablaGrad(CompoundDerivative):
    __slots__ = as_native_strings(("_dim",))

    def __new__(cls, f):
        # Return zero if expression is trivially constant
        if is_cellwise_constant(f):
            dim = find_geometric_dimension(f)
            return Zero((dim,) + f.ufl_shape, f.ufl_free_indices,
                        f.ufl_index_dimensions)
        return CompoundDerivative.__new__(cls)

    def __init__(self, f):
        CompoundDerivative.__init__(self, (f,))
        self._dim = find_geometric_dimension(f)

    def _ufl_expr_reconstruct_(self, op):
        "Return a new object of the same type with new operands."
        if is_cellwise_constant(op):
            if op.ufl_shape != self.ufl_operands[0].ufl_shape:
                error("Operand shape mismatch in NablaGrad reconstruct.")
            if self.ufl_operands[0].ufl_free_indices != op.ufl_free_indices:
                error("Free index mismatch in NablaGrad reconstruct.")
            return Zero(self.ufl_shape, self.ufl_free_indices,
                        self.ufl_index_dimensions)
        return self._ufl_class_(op)

    @property
    def ufl_shape(self):
        return (self._dim,) + self.ufl_operands[0].ufl_shape

    def __str__(self):
        return "nabla_grad(%s)" % self.ufl_operands[0]
Exemple #8
0
class Cross(CompoundTensorOperator):
    __slots__ = as_native_strings((
        "ufl_free_indices",
        "ufl_index_dimensions",
    ))

    def __new__(cls, a, b):
        ash = a.ufl_shape
        bsh = b.ufl_shape

        # Checks
        if not (len(ash) == 1 and ash == bsh):
            error(
                "Cross product requires arguments of rank 1, got %s and %s." %
                (ufl_err_str(a), ufl_err_str(b)))

        # Simplification
        if isinstance(a, Zero) or isinstance(b, Zero):
            fi, fid = merge_nonoverlapping_indices(a, b)
            return Zero(ash, fi, fid)

        return CompoundTensorOperator.__new__(cls)

    def __init__(self, a, b):
        CompoundTensorOperator.__init__(self, (a, b))
        fi, fid = merge_nonoverlapping_indices(a, b)
        self.ufl_free_indices = fi
        self.ufl_index_dimensions = fid

    ufl_shape = (3, )

    def __str__(self):
        return "%s x %s" % (parstr(self.ufl_operands[0],
                                   self), parstr(self.ufl_operands[1], self))
Exemple #9
0
class IntegralData(object):
    """Utility class with the members
        (domain, integral_type, subdomain_id, integrals, metadata)

    where metadata is an empty dictionary that may be used for
    associating metadata with each object.
    """
    __slots__ = as_native_strings(
        ('domain', 'integral_type', 'subdomain_id', 'integrals', 'metadata',
         'integral_coefficients', 'enabled_coefficients'))

    def __init__(self, domain, integral_type, subdomain_id, integrals,
                 metadata):
        if 1 != len(set(itg.ufl_domain() for itg in integrals)):
            error("Multiple domains mismatch in integral data.")
        if not all(integral_type == itg.integral_type() for itg in integrals):
            error("Integral type mismatch in integral data.")
        if not all(subdomain_id == itg.subdomain_id() for itg in integrals):
            error("Subdomain id mismatch in integral data.")

        self.domain = domain
        self.integral_type = integral_type
        self.subdomain_id = subdomain_id

        self.integrals = integrals

        # This is populated in preprocess using data not available at
        # this stage:
        self.integral_coefficients = None
        self.enabled_coefficients = None

        # TODO: I think we can get rid of this with some refactoring
        # in ffc:
        self.metadata = metadata

    def __lt__(self, other):
        # To preserve behaviour of extract_integral_data:
        return ((self.integral_type, self.subdomain_id, self.integrals,
                 self.metadata) < (other.integral_type, other.subdomain_id,
                                   other.integrals, other.metadata))

    def __eq__(self, other):
        # Currently only used for tests:
        return (self.integral_type == other.integral_type
                and self.subdomain_id == other.subdomain_id
                and self.integrals == other.integrals
                and self.metadata == other.metadata)

    def __unicode__(self):
        # Only in python 2
        return str(self).decode("utf-8")

    def __str__(self):
        s = "IntegralData over domain(%s, %s), with integrals:\n%s\nand metadata:\n%s" % (
            self.integral_type, self.subdomain_id, '\n\n'.join(
                map(str, self.integrals)), self.metadata)
        return s
Exemple #10
0
class AbstractCell(object):
    "Representation of an abstract finite element cell with only the dimensions known."
    __slots__ = as_native_strings((
        "_topological_dimension",
        "_geometric_dimension",
        ))

    def __init__(self, topological_dimension, geometric_dimension):
        # Validate dimensions
        if not isinstance(geometric_dimension, numbers.Integral):
            error("Expecting integer geometric_dimension.")
        if not isinstance(topological_dimension, numbers.Integral):
            error("Expecting integer topological_dimension.")
        if topological_dimension > geometric_dimension:
            error("Topological dimension cannot be larger than geometric dimension.")

        # Store validated dimensions
        self._topological_dimension = topological_dimension
        self._geometric_dimension = geometric_dimension

    def topological_dimension(self):
        "Return the dimension of the topology of this cell."
        return self._topological_dimension

    def geometric_dimension(self):
        "Return the dimension of the space this cell is embedded in."
        return self._geometric_dimension

    def is_simplex(self):
        "Return True if this is a simplex cell."
        raise NotImplementedError("Implement this to allow important checks and optimizations.")

    def has_simplex_facets(self):
        "Return True if all the facets of this cell are simplex cells."
        raise NotImplementedError("Implement this to allow important checks and optimizations.")

    def __lt__(self, other):
        "Define an arbitrarily chosen but fixed sort order for all cells."
        if not isinstance(other, AbstractCell):
            return NotImplemented
        # Sort by gdim first, tdim next, then whatever's left
        # depending on the subclass
        s = (self.geometric_dimension(), self.topological_dimension())
        o = (other.geometric_dimension(), other.topological_dimension())
        if s != o:
            return s < o
        return self._ufl_hash_data_() < other._ufl_hash_data_()

    def __unicode__(self):
        # Only in python 2
        return str(self).decode("utf-8")
Exemple #11
0
class PermutationSymbol(ConstantValue):
    """UFL literal type: Representation of a permutation symbol.

    This is also known as the Levi-Civita symbol, antisymmetric symbol,
    or alternating symbol."""
    __slots__ = as_native_strings(("ufl_shape", "_dim"))

    def __init__(self, dim):
        ConstantValue.__init__(self)
        self._dim = dim
        self.ufl_shape = (dim,)*dim

    def evaluate(self, x, mapping, component, index_values):
        "Evaluates the permutation symbol."
        return self.__eps(component)

    def __getitem__(self, key):
        if len(key) != self._dim:
            error("Size mismatch for PermutationSymbol.")
        if all(isinstance(k, (int, FixedIndex)) for k in key):
            return self.__eps(key)
        return Expr.__getitem__(self, key)

    def __str__(self):
        return "eps"

    def __repr__(self):
        r = "PermutationSymbol(%d)" % self._dim
        return as_native_str(r)

    def __eq__(self, other):
        return isinstance(other, PermutationSymbol) and self._dim == other._dim

    def __eps(self, x):
        """This function body is taken from
        http://www.mathkb.com/Uwe/Forum.aspx/math/29865/N-integer-Levi-Civita

        """
        result = IntValue(1)
        for i, x1 in enumerate(x):
            for j in range(i + 1, len(x)):
                x2 = x[j]
                if x1 > x2:
                    result = -result
                elif x1 == x2:
                    return Zero()
        return result
Exemple #12
0
class ScalarValue(ConstantValue):
    "A constant scalar value."
    __slots__ = as_native_strings(("_value",))

    def __init__(self, value):
        ConstantValue.__init__(self)
        self._value = value

    def value(self):
        return self._value

    def evaluate(self, x, mapping, component, index_values):
        return self._value

    def __eq__(self, other):
        """This is implemented to allow comparison with python scalars.

        Note that this will make IntValue(1) != FloatValue(1.0),
        but ufl-python comparisons like
            IntValue(1) == 1.0
            FloatValue(1.0) == 1
        can still succeed. These will however not have the same
        hash value and therefore not collide in a dict.
        """
        if isinstance(other, self._ufl_class_):
            return self._value == other._value
        elif isinstance(other, (int, float)):
            # FIXME: Disallow this, require explicit 'expr ==
            # IntValue(3)' instead to avoid ambiguities!
            return other == self._value
        else:
            return False

    def __str__(self):
        return str(self._value)

    def __float__(self):
        return float(self._value)

    def __int__(self):
        return int(self._value)

    def __neg__(self):
        return type(self)(-self._value)

    def __abs__(self):
        return type(self)(abs(self._value))
Exemple #13
0
class BesselFunction(Operator):
    "Base class for all bessel functions"
    __slots__ = as_native_strings(("_name", "_classname"))

    def __init__(self, name, classname, nu, argument):
        if not is_true_ufl_scalar(nu):
            error("Expecting scalar nu.")
        if not is_true_ufl_scalar(argument):
            error("Expecting scalar argument.")

        # Use integer representation if suitable
        fnu = float(nu)
        inu = int(nu)
        if fnu == inu:
            nu = as_ufl(inu)
        else:
            nu = as_ufl(fnu)

        Operator.__init__(self, (nu, argument))

        self._classname = classname
        self._name = name

    def evaluate(self, x, mapping, component, index_values):
        a = self.ufl_operands[1].evaluate(x, mapping, component, index_values)
        try:
            import scipy.special
        except:
            error(
                "You must have scipy installed to evaluate bessel functions in python."
            )
        name = self._name[-1]
        if isinstance(self.ufl_operands[0], IntValue):
            nu = int(self.ufl_operands[0])
            functype = 'n' if name != 'i' else 'v'
        else:
            nu = self.ufl_operands[0].evaluate(x, mapping, component,
                                               index_values)
            functype = 'v'
        func = getattr(scipy.special, name + functype)
        return func(nu, a)

    def __str__(self):
        return "%s(%s, %s)" % (self._name, self.ufl_operands[0],
                               self.ufl_operands[1])
Exemple #14
0
class ExprTupleKey(object):
    __slots__ = as_native_strings(('x', ))

    def __init__(self, x):
        self.x = x

    def __lt__(self, other):
        # Comparing expression first
        c = cmp_expr(self.x[0], other.x[0])
        if c < 0:
            return True
        elif c > 0:
            return False
        else:
            # Comparing form compiler data
            mds = canonicalize_metadata(self.x[1])
            mdo = canonicalize_metadata(other.x[1])
            return mds < mdo
Exemple #15
0
class ExampleCounted(object):
    """An example class for classes of objects identified by a global counter.

    Mimic this class to create globally counted objects within a single type.
    """
    # Store the count for each object
    __slots__ = as_native_strings(("_count", ))

    # Store a global counter with the class
    _globalcount = 0

    # Call counted_init with an optional constructor argument and the class
    def __init__(self, count=None):
        counted_init(self, count, ExampleCounted)

    # Make the count accessible
    def count(self):
        return self._count
Exemple #16
0
class Dot(CompoundTensorOperator):
    __slots__ = as_native_strings((
        "ufl_free_indices",
        "ufl_index_dimensions",
    ))

    def __new__(cls, a, b):
        ash = a.ufl_shape
        bsh = b.ufl_shape
        ar, br = len(ash), len(bsh)
        scalar = (ar == 0 and br == 0)

        # Checks
        if not ((ar >= 1 and br >= 1) or scalar):
            error("Dot product requires non-scalar arguments, "
                  "got arguments with ranks %d and %d." % (ar, br))
        if not (scalar or ash[-1] == bsh[0]):
            error("Dimension mismatch in dot product.")

        # Simplification
        if isinstance(a, Zero) or isinstance(b, Zero):
            shape = ash[:-1] + bsh[1:]
            fi, fid = merge_nonoverlapping_indices(a, b)
            return Zero(shape, fi, fid)
        elif scalar:  # TODO: Move this to def dot()?
            return a * b

        return CompoundTensorOperator.__new__(cls)

    def __init__(self, a, b):
        CompoundTensorOperator.__init__(self, (a, b))
        fi, fid = merge_nonoverlapping_indices(a, b)
        self.ufl_free_indices = fi
        self.ufl_index_dimensions = fid

    @property
    def ufl_shape(self):
        return self.ufl_operands[0].ufl_shape[:-1] + self.ufl_operands[
            1].ufl_shape[1:]

    def __str__(self):
        return "%s . %s" % (parstr(self.ufl_operands[0],
                                   self), parstr(self.ufl_operands[1], self))
Exemple #17
0
class ReferenceGrad(CompoundDerivative):
    __slots__ = as_native_strings(("_dim",))

    def __new__(cls, f):
        # Return zero if expression is trivially constant
        if is_cellwise_constant(f):
            dim = f.ufl_domain().topological_dimension()
            return Zero(f.ufl_shape + (dim,), f.ufl_free_indices,
                        f.ufl_index_dimensions)
        return CompoundDerivative.__new__(cls)

    def __init__(self, f):
        CompoundDerivative.__init__(self, (f,))
        self._dim = f.ufl_domain().topological_dimension()

    def _ufl_expr_reconstruct_(self, op):
        "Return a new object of the same type with new operands."
        if is_cellwise_constant(op):
            if op.ufl_shape != self.ufl_operands[0].ufl_shape:
                error("Operand shape mismatch in ReferenceGrad reconstruct.")
            if self.ufl_operands[0].ufl_free_indices != op.ufl_free_indices:
                error("Free index mismatch in ReferenceGrad reconstruct.")
            return Zero(self.ufl_shape, self.ufl_free_indices,
                        self.ufl_index_dimensions)
        return self._ufl_class_(op)

    def evaluate(self, x, mapping, component, index_values, derivatives=()):
        "Get child from mapping and return the component asked for."
        component, i = component[:-1], component[-1]
        derivatives = derivatives + (i,)
        result = self.ufl_operands[0].evaluate(x, mapping, component,
                                               index_values,
                                               derivatives=derivatives)
        return result

    @property
    def ufl_shape(self):
        return self.ufl_operands[0].ufl_shape + (self._dim,)

    def __str__(self):
        return "reference_grad(%s)" % self.ufl_operands[0]
Exemple #18
0
class Inner(CompoundTensorOperator):
    __slots__ = as_native_strings((
        "ufl_free_indices",
        "ufl_index_dimensions",
    ))

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

        # Simplification
        if isinstance(a, Zero) or isinstance(b, Zero):
            fi, fid = merge_nonoverlapping_indices(a, b)
            return Zero((), fi, fid)
        elif ash == ():
            return a * b

        return CompoundTensorOperator.__new__(cls)

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

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

        fi, fid = merge_nonoverlapping_indices(a, b)
        self.ufl_free_indices = fi
        self.ufl_index_dimensions = fid

    ufl_shape = ()

    def __str__(self):
        return "%s : %s" % (parstr(self.ufl_operands[0],
                                   self), parstr(self.ufl_operands[1], self))
Exemple #19
0
class MeasureProduct(object):
    """Represents a product of measures.

    This is a notational intermediate object to handle the notation

        f*(dm1*dm2)

    This is work in progress and not functional. It needs support
    in other parts of ufl and the rest of the code generation chain.
    """
    __slots__ = as_native_strings(("_measures", ))

    def __init__(self, *measures):
        "Create MeasureProduct from given list of measures."
        self._measures = measures
        if len(self._measures) < 2:
            error("Expecting at least two measures.")

    def __mul__(self, other):
        """Flatten multiplication of product measures.

        This is to ensure that (dm1*dm2)*dm3 is stored as a
        simple list (dm1,dm2,dm3) in a single MeasureProduct.
        """
        if isinstance(other, Measure):
            measures = self.sub_measures() + [other]
            return MeasureProduct(*measures)
        else:
            return NotImplemented

    def __rmul__(self, integrand):
        error(
            "TODO: Implement MeasureProduct.__rmul__ to construct integral and form somehow."
        )

    def sub_measures(self):
        "Return submeasures."
        return self._measures
Exemple #20
0
class GeometricQuantity(Terminal):
    __slots__ = as_native_strings(("_domain", ))

    def __init__(self, domain):
        Terminal.__init__(self)
        self._domain = as_domain(domain)

    def ufl_domains(self):
        return (self._domain, )

    def is_cellwise_constant(self):
        "Return whether this expression is spatially constant over each cell (or over each facet for facet quantities)."
        # NB! Geometric quantities are piecewise constant by
        # default. Override if needed.
        return True

    # NB! Geometric quantities are scalar by default. Override if
    # needed.
    ufl_shape = ()

    def _ufl_signature_data_(self, renumbering):
        "Signature data of geometric quantities depend on the domain numbering."
        return (self._ufl_class_.__name__,
                ) + self._domain._ufl_signature_data_(renumbering)

    def __str__(self):
        return self._ufl_class_.name

    def __repr__(self):
        r = "%s(%s)" % (self._ufl_class_.__name__, repr(self._domain))
        return as_native_str(r)

    def _ufl_compute_hash_(self):
        return hash((type(self).__name__, ) + self._domain._ufl_hash_data_())

    def __eq__(self, other):
        return isinstance(other,
                          self._ufl_class_) and other._domain == self._domain
Exemple #21
0
class Label(Terminal):
    __slots__ = as_native_strings(("_count", ))

    _globalcount = 0

    def __init__(self, count=None):
        Terminal.__init__(self)
        counted_init(self, count, Label)

    def count(self):
        return self._count

    def __str__(self):
        return "Label(%d)" % self._count

    def __repr__(self):
        r = "Label(%d)" % self._count
        return as_native_str(r)

    @property
    def ufl_shape(self):
        error("Label has no shape (it is not a tensor expression).")

    @property
    def ufl_free_indices(self):
        error("Label has no free indices (it is not a tensor expression).")

    @property
    def ufl_index_dimensions(self):
        error("Label has no free indices (it is not a tensor expression).")

    def is_cellwise_constant(self):
        return True

    def ufl_domains(self):
        "Return tuple of domains related to this terminal object."
        return ()
Exemple #22
0
class ReferenceCurl(CompoundDerivative):
    __slots__ = as_native_strings(("ufl_shape",))

    def __new__(cls, f):
        # Validate input
        sh = f.ufl_shape
        if f.ufl_shape not in ((), (2,), (3,)):
            error("Expecting a scalar, 2D vector or 3D vector.")
        if f.ufl_free_indices:
            error("Free indices in the curl argument is not allowed.")

        # Return zero if expression is trivially constant
        if is_cellwise_constant(f):
            sh = {(): (2,), (2,): (), (3,): (3,)}[sh]
            return Zero(sh)  # No free indices asserted above
        return CompoundDerivative.__new__(cls)

    def __init__(self, f):
        global _curl_shapes
        CompoundDerivative.__init__(self, (f,))
        self.ufl_shape = _curl_shapes[f.ufl_shape]

    def __str__(self):
        return "reference_curl(%s)" % self.ufl_operands[0]
Exemple #23
0
class MathFunction(Operator):
    "Base class for all unary scalar math functions."
    # Freeze member variables for objects in this class
    __slots__ = as_native_strings(("_name", ))

    def __init__(self, name, argument):
        Operator.__init__(self, (argument, ))
        if not is_true_ufl_scalar(argument):
            error("Expecting scalar argument.")
        self._name = name

    def evaluate(self, x, mapping, component, index_values):
        a = self.ufl_operands[0].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

    def __str__(self):
        return "%s(%s)" % (self._name, self.ufl_operands[0])
Exemple #24
0
class VariableDerivative(Derivative):
    __slots__ = as_native_strings((
        "ufl_shape",
        "ufl_free_indices",
        "ufl_index_dimensions",
    ))

    def __new__(cls, f, v):
        # Checks
        if not isinstance(f, Expr):
            error("Expecting an Expr in VariableDerivative.")
        if not isinstance(v, (Variable, Coefficient)):
            error("Expecting a Variable in VariableDerivative.")
        if v.ufl_free_indices:
            error("Differentiation variable cannot have free indices.")

        # Simplification
        # Return zero if expression is trivially independent of variable
        if f._ufl_is_terminal_ and f != v:
            return Zero(f.ufl_shape + v.ufl_shape, f.ufl_free_indices,
                        f.ufl_index_dimensions)

        # Construction
        return Derivative.__new__(cls)

    def __init__(self, f, v):
        Derivative.__init__(self, (f, v))
        self.ufl_free_indices = f.ufl_free_indices
        self.ufl_index_dimensions = f.ufl_index_dimensions
        self.ufl_shape = f.ufl_shape + v.ufl_shape

    def __str__(self):
        if isinstance(self.ufl_operands[0], Terminal):
            return "d%s/d[%s]" % (self.ufl_operands[0], self.ufl_operands[1])
        return "d/d[%s] %s" % (self.ufl_operands[1],
                               parstr(self.ufl_operands[0], self))
Exemple #25
0
class ComponentTensor(Operator):
    """UFL operator type: Maps the free indices of a scalar valued expression to tensor axes."""
    __slots__ = as_native_strings(
        ("ufl_shape", "ufl_free_indices", "ufl_index_dimensions"))

    def __new__(cls, expression, indices):

        # Simplify
        if isinstance(expression, Zero):
            fi, fid, sh = remove_indices(expression.ufl_free_indices,
                                         expression.ufl_index_dimensions,
                                         [ind.count() for ind in indices])
            return Zero(sh, fi, fid)

        # Construct
        return Operator.__new__(cls)

    def __init__(self, expression, indices):
        if not isinstance(expression, Expr):
            error("Expecting ufl expression.")
        if expression.ufl_shape != ():
            error("Expecting scalar valued expression.")
        if not isinstance(indices, MultiIndex):
            error("Expecting a MultiIndex.")
        if not all(isinstance(i, Index) for i in indices):
            error("Expecting sequence of Index objects, not %s." %
                  indices._ufl_err_str_())

        Operator.__init__(self, (expression, indices))

        fi, fid, sh = remove_indices(expression.ufl_free_indices,
                                     expression.ufl_index_dimensions,
                                     [ind.count() for ind in indices])
        self.ufl_free_indices = fi
        self.ufl_index_dimensions = fid
        self.ufl_shape = sh

    def _ufl_expr_reconstruct_(self, expressions, indices):
        # Special case for simplification as_tensor(A[ii], ii) -> A
        if isinstance(expressions, Indexed):
            A, ii = expressions.ufl_operands
            if indices == ii:
                return A
        return Operator._ufl_expr_reconstruct_(self, expressions, indices)

    def indices(self):
        return self.ufl_operands[1]

    def evaluate(self, x, mapping, component, index_values):
        indices = self.ufl_operands[1]
        a = self.ufl_operands[0]

        if len(indices) != len(component):
            error("Expecting a component matching the indices tuple.")

        # Map component to indices
        for i, c in zip(indices, component):
            index_values.push(i, c)

        a = a.evaluate(x, mapping, (), index_values)

        for _ in component:
            index_values.pop()

        return a

    def __str__(self):
        return "{ A | A_{%s} = %s }" % (self.ufl_operands[1],
                                        self.ufl_operands[0])
Exemple #26
0
class TensorProductElement(FiniteElementBase):
    r"""The tensor product of :math:`d` element spaces:

    .. math:: V = V_1 \otimes V_2 \otimes ...  \otimes V_d

    Given bases :math:`\{\phi_{j_i}\}` of the spaces :math:`V_i` for :math:`i = 1, ...., d`,
    :math:`\{ \phi_{j_1} \otimes \phi_{j_2} \otimes \cdots \otimes \phi_{j_d}
    \}` forms a basis for :math:`V`.
    """
    __slots__ = as_native_strings(("_sub_elements", "_cell"))

    def __init__(self, *elements, **kwargs):
        "Create TensorProductElement from a given list of elements."
        if not elements:
            error("Cannot create TensorProductElement from empty list.")

        keywords = list(kwargs.keys())
        if keywords and keywords != ["cell"]:
            raise ValueError(
                "TensorProductElement got an unexpected keyword argument '%s'"
                % keywords[0])
        cell = kwargs.get("cell")

        family = "TensorProductElement"

        if cell is None:
            # Define cell as the product of each elements cell
            cell = TensorProductCell(*[e.cell() for e in elements])
        else:
            cell = as_cell(cell)

        # Define polynomial degree as a tuple of sub-degrees
        degree = tuple(e.degree() for e in elements)

        # No quadrature scheme defined
        quad_scheme = None

        # match FIAT implementation
        value_shape = tuple(chain(*[e.value_shape() for e in elements]))
        reference_value_shape = tuple(
            chain(*[e.reference_value_shape() for e in elements]))
        if len(value_shape) > 1:
            error("Product of vector-valued elements not supported")
        if len(reference_value_shape) > 1:
            error("Product of vector-valued elements not supported")

        FiniteElementBase.__init__(self, family, cell, degree, quad_scheme,
                                   value_shape, reference_value_shape)
        self._sub_elements = elements
        self._cell = cell
        self._repr = "TensorProductElement(%s, cell=%s)" % (", ".join(
            repr(e) for e in elements), repr(cell))

    def mapping(self):
        if all(e.mapping() == "identity" for e in self._sub_elements):
            return "identity"
        else:
            return "undefined"

    def num_sub_elements(self):
        "Return number of subelements."
        return len(self._sub_elements)

    def sub_elements(self):
        "Return subelements (factors)."
        return self._sub_elements

    def reconstruct(self, cell=None):
        return TensorProductElement(*self.sub_elements(), cell=cell)

    def __str__(self):
        "Pretty-print."
        return "TensorProductElement(%s, cell=%s)" \
            % (', '.join([str(e) for e in self._sub_elements]), str(self._cell))

    def shortstr(self):
        "Short pretty-print."
        return "TensorProductElement(%s, cell=%s)" \
            % (', '.join([e.shortstr() for e in self._sub_elements]), str(self._cell))
Exemple #27
0
class Integral(object):
    "An integral over a single domain."
    __slots__ = as_native_strings((
        "_integrand",
        "_integral_type",
        "_ufl_domain",
        "_subdomain_id",
        "_metadata",
        "_subdomain_data",
    ))

    def __init__(self, integrand, integral_type, domain, subdomain_id,
                 metadata, subdomain_data):
        if not isinstance(integrand, Expr):
            error("Expecting integrand to be an Expr instance.")
        self._integrand = integrand
        self._integral_type = integral_type
        self._ufl_domain = domain
        self._subdomain_id = subdomain_id
        self._metadata = metadata
        self._subdomain_data = subdomain_data

    def reconstruct(self,
                    integrand=None,
                    integral_type=None,
                    domain=None,
                    subdomain_id=None,
                    metadata=None,
                    subdomain_data=None):
        """Construct a new Integral object with some properties replaced with new values.

        Example:
            <a = Integral instance>
            b = a.reconstruct(expand_compounds(a.integrand()))
            c = a.reconstruct(metadata={'quadrature_degree':2})
        """
        if integrand is None:
            integrand = self.integrand()
        if integral_type is None:
            integral_type = self.integral_type()
        if domain is None:
            domain = self.ufl_domain()
        if subdomain_id is None:
            subdomain_id = self.subdomain_id()
        if metadata is None:
            metadata = self.metadata()
        if subdomain_data is None:
            subdomain_data = self._subdomain_data
        return Integral(integrand, integral_type, domain, subdomain_id,
                        metadata, subdomain_data)

    def integrand(self):
        "Return the integrand expression, which is an ``Expr`` instance."
        return self._integrand

    def integral_type(self):
        "Return the domain type of this integral."
        return self._integral_type

    #def domain(self):
    #    "Deprecated, please use .ufl_domain() instead."
    #    deprecate("Integral.domain() is deprecated, please use .ufl_domain() instead.")
    #    return self.ufl_domain()

    def ufl_domain(self):
        "Return the integration domain of this integral."
        return self._ufl_domain

    def subdomain_id(self):
        "Return the subdomain id of this integral."
        return self._subdomain_id

    def metadata(self):
        "Return the compiler metadata this integral has been annotated with."
        return self._metadata

    def subdomain_data(self):
        "Return the domain data of this integral."
        return self._subdomain_data

    def __neg__(self):
        return self.reconstruct(-self._integrand)

    def __mul__(self, scalar):
        if not is_python_scalar(scalar):
            error("Cannot multiply an integral with non-constant values.")
        return self.reconstruct(scalar * self._integrand)

    def __rmul__(self, scalar):
        if not is_scalar_constant_expression(scalar):
            error("An integral can only be multiplied by a "
                  "globally constant scalar expression.")
        return self.reconstruct(scalar * self._integrand)

    def __unicode__(self):
        # Only in python 2
        return str(self).decode("utf-8")

    def __str__(self):
        fmt = "{ %s } * %s(%s[%s], %s)"
        mname = ufl.measure.integral_type_to_measure_name[self._integral_type]
        s = fmt % (self._integrand, mname, self._ufl_domain,
                   self._subdomain_id, self._metadata)
        return s

    def __repr__(self):
        r = "Integral(%s, %s, %s, %s, %s, %s)" % (
            repr(self._integrand),
            repr(self._integral_type),
            repr(self._ufl_domain),
            repr(self._subdomain_id),
            repr(self._metadata),
            repr(self._subdomain_data),
        )
        return as_native_str(r)

    def __eq__(self, other):
        return (isinstance(other, Integral)
                and self._integral_type == other._integral_type
                and self._ufl_domain == other._ufl_domain
                and self._subdomain_id == other._subdomain_id
                and self._integrand == other._integrand
                and self._metadata == other._metadata and id_or_none(
                    self._subdomain_data) == id_or_none(other._subdomain_data))

    def __hash__(self):
        # Assuming few collisions by ignoring hash(self._metadata)
        # (a dict is not hashable but we assume it is immutable in practice)
        hashdata = (hash(self._integrand), self._integral_type,
                    hash(self._ufl_domain), self._subdomain_id,
                    id_or_none(self._subdomain_data))
        return hash(hashdata)
Exemple #28
0
#
# Modified by Anders Logg, 2008-2009
# Modified by Massimiliano Leoni, 2016.

# import six
import ufl
from ufl.log import error
from ufl.core.expr import Expr
from ufl.checks import is_python_scalar, is_scalar_constant_expression
from ufl.measure import Measure  # noqa
from ufl.protocols import id_or_none
from ufl.utils.py23 import as_native_str
from ufl.utils.py23 import as_native_strings

# Export list for ufl.classes
__all_classes__ = as_native_strings(["Integral"])


# @six.python_2_unicode_compatible
class Integral(object):
    "An integral over a single domain."
    __slots__ = as_native_strings((
        "_integrand",
        "_integral_type",
        "_ufl_domain",
        "_subdomain_id",
        "_metadata",
        "_subdomain_data",
    ))

    def __init__(self, integrand, integral_type, domain, subdomain_id,
Exemple #29
0
__all__ = as_native_strings([
    'product',
    'get_handler', 'get_logger', 'set_handler', 'set_level', 'add_logfile',
    'UFLException', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL',
    'as_cell', 'AbstractCell', 'Cell', 'TensorProductCell',
    'as_domain', 'AbstractDomain', 'Mesh', 'MeshView', 'TensorProductMesh',
    'L2', 'H1', 'H2', 'HCurl', 'HDiv',
    'SpatialCoordinate',
    'CellVolume', 'Circumradius', 'MinCellEdgeLength', 'MaxCellEdgeLength',
    'FacetArea', 'MinFacetEdgeLength', 'MaxFacetEdgeLength',
    'FacetNormal', 'CellNormal',
    'Jacobian', 'JacobianDeterminant', 'JacobianInverse',
    'FiniteElementBase', 'FiniteElement',
    'MixedElement', 'VectorElement', 'TensorElement', 'EnrichedElement',
    'NodalEnrichedElement', 'RestrictedElement', 'TensorProductElement',
    'HDivElement', 'HCurlElement',
    'BrokenElement', 'FacetElement', 'InteriorElement',
    'register_element', 'show_elements',
    'FunctionSpace',
    'Argument', 'TestFunction', 'TrialFunction',
    'Arguments', 'TestFunctions', 'TrialFunctions',
    'Coefficient', 'Coefficients',
    'Constant', 'VectorConstant', 'TensorConstant',
    'split',
    'PermutationSymbol', 'Identity', 'zero', 'as_ufl',
    'Index', 'indices',
    'as_tensor', 'as_vector', 'as_matrix', 'relabel',
    'unit_vector', 'unit_vectors', 'unit_matrix', 'unit_matrices',
    'rank', 'shape',
    'outer', 'inner', 'dot', 'cross', 'perp',
    'det', 'inv', 'cofac',
    'transpose', 'tr', 'diag', 'diag_vector', 'dev', 'skew', 'sym',
    'sqrt', 'exp', 'ln', 'erf',
    'cos', 'sin', 'tan',
    'acos', 'asin', 'atan', 'atan_2',
    'cosh', 'sinh', 'tanh',
    'bessel_J', 'bessel_Y', 'bessel_I', 'bessel_K',
    'eq', 'ne', 'le', 'ge', 'lt', 'gt', 'And', 'Or', 'Not',
    'conditional', 'sign', 'max_value', 'min_value', 'Max', 'Min',
    'variable', 'diff',
    'Dx', 'grad', 'div', 'curl', 'rot', 'nabla_grad', 'nabla_div', 'Dn', 'exterior_derivative',
    'jump', 'avg', 'cell_avg', 'facet_avg',
    'elem_mult', 'elem_div', 'elem_pow', 'elem_op',
    'Form',
    'Integral', 'Measure', 'register_integral_type', 'integral_types', 'custom_integral_types',
    'replace', 'replace_integral_domains', 'derivative', 'action', 'energy_norm', 'rhs', 'lhs', 'block_split',
    'system', 'functional', 'adjoint', 'sensitivity_rhs',
    'dx', 'ds', 'dS', 'dP',
    'dc', 'dC', 'dO', 'dI', 'dX',
    'ds_b', 'ds_t', 'ds_tb', 'ds_v', 'dS_h', 'dS_v',
    'vertex', 'interval', 'triangle', 'tetrahedron',
    'quadrilateral', 'hexahedron', 'facet',
    'i', 'j', 'k', 'l', 'p', 'q', 'r', 's',
    'e', 'pi',
    ])
Exemple #30
0
class Expr(object):
    """Base class for all UFL expression types.

    *Instance properties*
        Every ``Expr`` instance will have certain properties.
        The most important ones are ``ufl_operands``, ``ufl_shape``,
        ``ufl_free_indices``, and ``ufl_index_dimensions`` properties.
        Expressions are immutable and hashable.

    *Type traits*
        The ``Expr`` API defines a number of type traits that each subclass
        needs to provide. Most of these are specified indirectly via
        the arguments to the ``ufl_type`` class decorator, allowing UFL
        to do some consistency checks and automate most of the traits
        for most types. Type traits are accessed via a class or
        instance object of the form ``obj._ufl_traitname_``. See the source
        code for description of each type trait.

    *Operators*
        Some Python special functions are implemented in this class,
        some are implemented in subclasses, and some are attached to
        this class in the ``ufl_type`` class decorator.

    *Defining subclasses*
        To define a new expression class, inherit from either
        ``Terminal`` or ``Operator``, and apply the ``ufl_type`` class
        decorator with suitable arguments.  See the docstring of
        ``ufl_type`` for details on its arguments.  Looking at existing
        classes similar to the one you wish to add is a good
        idea. Looking through the comments in the ``Expr`` class and
        ``ufl_type`` to understand all the properties that may need to
        be specified is also a good idea. Note that many algorithms in
        UFL and form compilers will need handlers implemented for each
        new type::.

        .. code-block:: python

            @ufl_type()
            class MyOperator(Operator):
                pass

    *Type collections*
        All ``Expr`` subclasses are collected by ``ufl_type`` in global
        variables available via ``Expr``.

    *Profiling*
        Object creation statistics can be collected by doing

        .. code-block:: python

            Expr.ufl_enable_profiling()
            # ... run some code
            initstats, delstats = Expr.ufl_disable_profiling()

        Giving a list of creation and deletion counts for each typecode.
    """

    # --- Each Expr subclass must define __slots__ or _ufl_noslots_ at
    # --- the top ---
    # This is to freeze member variables for objects of this class and
    # save memory by skipping the per-instance dict.

    __slots__ = as_native_strings(("_hash", ))

    # _ufl_noslots_ = True

    # --- Basic object behaviour ---

    def __getnewargs__(self):
        """The tuple returned here is passed to as args to cls.__new__(cls, *args).

        This implementation passes the operands, which is () for terminals.

        May be necessary to override if __new__ is implemented in a subclass.
        """
        return self.ufl_operands

    def __init__(self):
        self._hash = None

    def __del__(self):
        pass

    # This shows the principal behaviour of the hash function attached
    # in ufl_type:
    # def __hash__(self):
    #     if self._hash is None:
    #         self._hash = self._ufl_compute_hash_()
    #     return self._hash

    # --- Type traits are added to subclasses by the ufl_type class
    # --- decorator ---

    # Note: Some of these are modified after the Expr class definition
    # because Expr is not defined yet at this point.  Note: Boolean
    # type traits that categorize types are mostly set to None for
    # Expr but should be True or False for any non-abstract type.

    # A reference to the UFL class itself.  This makes it possible to
    # do type(f)._ufl_class_ and be sure you get the actual UFL class
    # instead of a subclass from another library.
    _ufl_class_ = None

    # The handler name.  This is the name of the handler function you
    # implement for this type in a multifunction.
    _ufl_handler_name_ = "expr"

    # The integer typecode, a contiguous index different for each
    # type.  This is used for fast lookup into e.g. multifunction
    # handler tables.
    _ufl_typecode_ = 0

    # Number of operands, "varying" for some types, or None if not
    # applicable for abstract types.
    _ufl_num_ops_ = None

    # Type trait: If the type is abstract.  An abstract class cannot
    # be instantiated and does not need all properties specified.
    _ufl_is_abstract_ = True

    # Type trait: If the type is terminal.
    _ufl_is_terminal_ = None

    # Type trait: If the type is a literal.
    _ufl_is_literal_ = None

    # Type trait: If the type is classified as a 'terminal modifier',
    # for form compiler use.
    _ufl_is_terminal_modifier_ = None

    # Type trait: If the type is a shaping operator.  Shaping
    # operations include indexing, slicing, transposing, i.e. not
    # introducing computation of a new value.
    _ufl_is_shaping_ = False

    # Type trait: If the type is in reference frame.
    _ufl_is_in_reference_frame_ = None

    # Type trait: If the type is a restriction to a geometric entity.
    _ufl_is_restriction_ = None

    # Type trait: If the type is evaluation in a particular way.
    _ufl_is_evaluation_ = None

    # Type trait: If the type is a differential operator.
    _ufl_is_differential_ = None

    # Type trait: If the type is purely scalar, having no shape or
    # indices.
    _ufl_is_scalar_ = None

    # Type trait: If the type never has free indices.
    _ufl_is_index_free_ = False

    # --- All subclasses must define these object attributes ---

    # Each subclass of Expr is checked to have these properties in
    # ufl_type
    _ufl_required_properties_ = (
        # A tuple of operands, all of them Expr instances.
        "ufl_operands",

        # A tuple of ints, the value shape of the expression.
        "ufl_shape",

        # A tuple of free index counts.
        "ufl_free_indices",

        # A tuple providing the int dimension for each free index.
        "ufl_index_dimensions",
    )

    # Each subclass of Expr is checked to have these methods in
    # ufl_type
    # FIXME: Add more and enable all
    _ufl_required_methods_ = (
        # To compute the hash on demand, this method is called.
        "_ufl_compute_hash_",

        # The data returned from this method is used to compute the
        # signature of a form
        "_ufl_signature_data_",

        # The == operator must be implemented to compare for identical
        # representation, used by set() and dict(). The __hash__
        # operator is added by ufl_type.
        "__eq__",

        # To reconstruct an object of the same type with operands or
        # properties changed.
        "_ufl_expr_reconstruct_",  # Implemented in Operator and Terminal so this should never fail
        "ufl_domains",
        # "ufl_cell",
        # "ufl_domain",

        # "__str__",
        # "__repr__",

        # TODO: Add checks for methods/properties of terminals only?
        # Required for terminals:
        # "is_cellwise_constant", # TODO: Rename to ufl_is_cellwise_constant?
    )

    # --- Global variables for collecting all types ---

    # A global counter of the number of typecodes assigned
    _ufl_num_typecodes_ = 1

    # A global set of all handler names added
    _ufl_all_handler_names_ = set()

    # A global array of all Expr subclasses, indexed by typecode
    _ufl_all_classes_ = []

    # A global dict mapping language_operator_name to the type it
    # produces
    _ufl_language_operators_ = {}

    # List of all terminal modifier types
    _ufl_terminal_modifiers_ = []

    # --- Mechanism for profiling object creation and deletion ---

    # A global array of the number of initialized objects for each
    # typecode
    _ufl_obj_init_counts_ = [0]

    # A global array of the number of deleted objects for each
    # typecode
    _ufl_obj_del_counts_ = [0]

    # Backup of default init and del
    _ufl_regular__init__ = __init__
    _ufl_regular__del__ = __del__

    def _ufl_profiling__init__(self):
        "Replacement constructor with object counting."
        Expr._ufl_regular__init__(self)
        Expr._ufl_obj_init_counts_[self._ufl_typecode_] += 1

    def _ufl_profiling__del__(self):
        "Replacement destructor with object counting."
        Expr._ufl_regular__del__(self)
        Expr._ufl_obj_del_counts_[self._ufl_typecode_] -= 1

    @staticmethod
    def ufl_enable_profiling():
        "Turn on the object counting mechanism and reset counts to zero."
        Expr.__init__ = Expr._ufl_profiling__init__
        Expr.__del__ = Expr._ufl_profiling__del__
        for i in range(len(Expr._ufl_obj_init_counts_)):
            Expr._ufl_obj_init_counts_[i] = 0
            Expr._ufl_obj_del_counts_[i] = 0

    @staticmethod
    def ufl_disable_profiling():
        "Turn off the object counting mechanism. Return object init and del counts."
        Expr.__init__ = Expr._ufl_regular__init__
        Expr.__del__ = Expr._ufl_regular__del__
        return (Expr._ufl_obj_init_counts_, Expr._ufl_obj_del_counts_)

    # === Abstract functions that must be implemented by subclasses ===

    # --- Functions for reconstructing expression ---

    def _ufl_expr_reconstruct_(self, *operands):
        "Return a new object of the same type with new operands."
        raise NotImplementedError(self.__class__._ufl_expr_reconstruct_)

    # --- Functions for geometric properties of expression ---

    def ufl_domains(
            self):  # TODO: Deprecate this and use extract_domains(expr)
        "Return all domains this expression is defined on."
        from ufl.domain import extract_domains
        return extract_domains(self)

    def ufl_domain(
            self):  # TODO: Deprecate this and use extract_unique_domain(expr)
        "Return the single unique domain this expression is defined on, or throw an error."
        from ufl.domain import extract_unique_domain
        return extract_unique_domain(self)

    #def is_cellwise_constant(self):  # TODO: Deprecate this and use is_cellwise_constant(expr)
    #    "Return whether this expression is spatially constant over each cell."
    #    from ufl.checks import is_cellwise_constant
    #    deprecate("Expr.is_cellwise_constant() is deprecated, please use is_cellwise_constant(expr) instead.")
    #    return is_cellwise_constant(self)

    # --- Functions for float evaluation ---

    def evaluate(self, x, mapping, component, index_values):
        """Evaluate expression at given coordinate with given values for terminals."""
        error("Symbolic evaluation of %s not available." %
              self._ufl_class_.__name__)

    def _ufl_evaluate_scalar_(self):
        if self.ufl_shape or self.ufl_free_indices:
            raise TypeError(
                "Cannot evaluate a nonscalar expression to a scalar value.")
        return self(())  # No known x

    def __float__(self):
        "Try to evaluate as scalar and cast to float."
        try:
            v = float(self._ufl_evaluate_scalar_())
        except:
            v = NotImplemented
        return v

    def __bool__(self):
        "By default, all Expr are nonzero/False."
        return True

    def __nonzero__(self):
        "By default, all Expr are nonzero/False."
        return self.__bool__()

    @staticmethod
    def _ufl_coerce_(value):
        "Convert any value to a UFL type."
        # Quick skip for most types
        if isinstance(value, Expr):
            return value

        # Conversion from non-ufl types
        # (the _ufl_from_*_ functions are attached to Expr by ufl_type)
        ufl_from_type = "_ufl_from_{0}_".format(value.__class__.__name__)
        return getattr(Expr, ufl_from_type)(value)

        # if hasattr(Expr, ufl_from_type):
        #     return getattr(Expr, ufl_from_type)(value)
        # Fail gracefully if no valid type conversion found
        # raise TypeError("Cannot convert a {0.__class__.__name__} to UFL type.".format(value))

    # --- Special functions for string representations ---

    # All subclasses must implement _ufl_signature_data_
    def _ufl_signature_data_(self, renumbering):
        "Return data that uniquely identifies form compiler relevant aspects of this object."
        raise NotImplementedError(self.__class__._ufl_signature_data_)

    # All subclasses must implement __repr__
    def __repr__(self):
        "Return string representation this object can be reconstructed from."
        raise NotImplementedError(self.__class__.__repr__)

    # All subclasses must implement __str__
    def __str__(self):
        "Return pretty print string representation of this object."
        raise NotImplementedError(self.__class__.__str__)

    def __unicode__(self):
        # Only in python 2
        return str(self).decode("utf-8")

    def _ufl_err_str_(self):
        "Return a short string to represent this Expr in an error message."
        return "<%s id=%d>" % (self._ufl_class_.__name__, id(self))

    def _repr_latex_(self):
        from ufl.algorithms import ufl2latex
        return "$%s$" % ufl2latex(self)

    def _repr_png_(self):
        from IPython.lib.latextools import latex_to_png
        return latex_to_png(self._repr_latex_())

    # --- Special functions used for processing expressions ---

    def __eq__(self, other):
        """Checks whether the two expressions are represented the
        exact same way. This does not check if the expressions are
        mathematically equal or equivalent! Used by sets and dicts."""
        raise NotImplementedError(self.__class__.__eq__)

    def __len__(self):
        "Length of expression. Used for iteration over vector expressions."
        s = self.ufl_shape
        if len(s) == 1:
            return s[0]
        raise NotImplementedError(
            "Cannot take length of non-vector expression.")

    def __iter__(self):
        "Iteration over vector expressions."
        for i in range(len(self)):
            yield self[i]

    def __floordiv__(self, other):
        "UFL does not support integer division."
        raise NotImplementedError(self.__class__.__floordiv__)

    def __pos__(self):
        "Unary + is a no-op."
        return self

    def __round__(self, n=None):
        "Round to nearest integer or to nearest nth decimal."
        return round(float(self), n)

    # --- Deprecated functions

    #def reconstruct(self, *operands):
    #    """Return a new object of the same type with new operands.
    #    Deprecated, please use Expr._ufl_expr_reconstruct_() instead."""
    #    deprecate("Expr.reconstruct() is deprecated, please use Expr._ufl_expr_reconstruct_() instead.")
    #    return self._ufl_expr_reconstruct_(*operands)

    def geometric_dimension(self):
        "Return the geometric dimension this expression lives in."
        from ufl.domain import find_geometric_dimension
        return find_geometric_dimension(self)