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)
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))
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))
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]
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: 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 def __str__(self): return "%s(%s)" % (self._name, self.ufl_operands[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 __str__(self): return "{\n " + "\n + ".join(map(str, self._measures)) + "\n}"
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))
class Index(IndexBase): """UFL value: An index with no value assigned. Used to represent free indices in Einstein indexing notation.""" __slots__ = as_native_strings(("_count", )) _globalcount = 0 def __init__(self, count=None): IndexBase.__init__(self) counted_init(self, count, Index) def count(self): return self._count def __hash__(self): return hash(("Index", self._count)) def __eq__(self, other): return isinstance(other, Index) and (self._count == other._count) def __str__(self): c = str(self._count) if len(c) > 1: c = "{%s}" % c return "i_%s" % c def __repr__(self): r = "Index(%d)" % self._count return as_native_str(r)
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))
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
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 __complex__(self): return complex(self._value) def __neg__(self): return type(self)(-self._value) def __abs__(self): return type(self)(abs(self._value)) def real(self): return self._value.real def imag(self): return self._value.imag
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 __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
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_()
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
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 ImportError: 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])
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
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
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]
class FixedIndex(IndexBase): """UFL value: An index with a specific value assigned.""" __slots__ = as_native_strings(("_value", "_hash")) _cache = {} def __getnewargs__(self): return (self._value, ) def __new__(cls, value): self = FixedIndex._cache.get(value) if self is None: if not isinstance(value, int): error("Expecting integer value for fixed index.") self = IndexBase.__new__(cls) self._init(value) FixedIndex._cache[value] = self return self def _init(self, value): IndexBase.__init__(self) self._value = value self._hash = hash(("FixedIndex", self._value)) def __init__(self, value): pass def __hash__(self): return self._hash def __eq__(self, other): return isinstance(other, FixedIndex) and (self._value == other._value) def __int__(self): return self._value def __str__(self): return "%d" % self._value def __repr__(self): r = "FixedIndex(%d)" % self._value return as_native_str(r)
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))
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
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 sobolev_space(self): "Return the underlying Sobolev space." return HCurl 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())
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
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 ()
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]
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))
# along with UFL. If not, see <http://www.gnu.org/licenses/>. # # Modified by Anders Logg, 2008-2009 # Modified by Massimiliano Leoni, 2016. 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.str import as_native_str from ufl.utils.str import as_native_strings # Export list for ufl.classes __all_classes__ = as_native_strings(["Integral"]) 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):
# (at your option) any later version. # # UFL is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with UFL. If not, see <http://www.gnu.org/licenses/>. from ufl.utils.str import as_native_strings from ufl.log import error from ufl.finiteelement import FiniteElement, VectorElement, TensorElement, \ MixedElement, EnrichedElement, NodalEnrichedElement __all__ = as_native_strings(['increase_order', 'tear']) def increase_order(element): "Return element of same family, but a polynomial degree higher." return _increase_degree(element, +1) def change_regularity(element, family): """ For a given finite element, return the corresponding space specified by 'family'. """ return element.reconstruct(family=family)
# Modified by Kristian B. Oelgaard, 2009 # Modified by Marie E. Rognes 2012 # Modified by Andrew T. T. McRae, 2014 # Modified by Massimiliano Leoni, 2016 import numbers import functools from ufl.utils.str import as_native_str from ufl.utils.str import as_native_strings from ufl.log import error from ufl.core.ufl_type import attach_operators_from_hash_data # Export list for ufl.classes __all_classes__ = as_native_strings(["AbstractCell", "Cell", "TensorProductCell"]) # --- The most abstract cell class, base class for other cell types 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):
# (at your option) any later version. # # UFL is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with UFL. If not, see <http://www.gnu.org/licenses/>. from ufl.log import error from ufl.utils.str import as_native_str from ufl.utils.str import as_native_strings # Export list for ufl.classes __all_classes__ = as_native_strings(["Equation"]) class Equation(object): """This class is used to represent equations expressed by the "==" operator. Examples include a == L and F == 0 where a, L and F are Form objects.""" def __init__(self, lhs, rhs): "Create equation lhs == rhs" self.lhs = lhs self.rhs = rhs def __bool__(self): """Evaluate bool(lhs_form == rhs_form).
import logging from logging import DEBUG, INFO, WARNING, ERROR, CRITICAL # noqa: F401 from ufl.utils.str import as_native_strings log_functions = ["log", "debug", "info", "deprecate", "warning", "error", "begin", "end", "set_level", "push_level", "pop_level", "set_indent", "add_indent", "set_handler", "get_handler", "get_logger", "add_logfile", "set_prefix", "info_red", "info_green", "info_blue", "warning_red", "warning_green", "warning_blue"] __all__ = as_native_strings( log_functions + ["Logger", "log_functions"] + ["DEBUG", "INFO", "DEPRECATE", "WARNING", "ERROR", "CRITICAL"] ) DEPRECATE = (INFO + WARNING) // 2 # This is used to override emit() in StreamHandler for printing # without newline def emit(self, record): message = self.format(record) format_string = "%s" if getattr(record, "continued", False) else "%s\n" self.stream.write(format_string % message) self.flush()
class IndexSum(Operator): __slots__ = as_native_strings(( "_dimension", "ufl_free_indices", "ufl_index_dimensions", )) def __new__(cls, summand, index): # Error checks if not isinstance(summand, Expr): error("Expecting Expr instance, got %s" % ufl_err_str(summand)) if not isinstance(index, MultiIndex): error("Expecting MultiIndex instance, got %s" % ufl_err_str(index)) if len(index) != 1: error("Expecting a single Index but got %d." % len(index)) # Simplification to zero if isinstance(summand, Zero): sh = summand.ufl_shape j, = index fi = summand.ufl_free_indices fid = summand.ufl_index_dimensions pos = fi.index(j.count()) fi = fi[:pos] + fi[pos + 1:] fid = fid[:pos] + fid[pos + 1:] return Zero(sh, fi, fid) return Operator.__new__(cls) def __init__(self, summand, index): j, = index fi = summand.ufl_free_indices fid = summand.ufl_index_dimensions pos = fi.index(j.count()) self._dimension = fid[pos] self.ufl_free_indices = fi[:pos] + fi[pos + 1:] self.ufl_index_dimensions = fid[:pos] + fid[pos + 1:] Operator.__init__(self, (summand, index)) def index(self): return self.ufl_operands[1][0] def dimension(self): return self._dimension @property def ufl_shape(self): return self.ufl_operands[0].ufl_shape def evaluate(self, x, mapping, component, index_values): i, = self.ufl_operands[1] tmp = 0 for k in range(self._dimension): index_values.push(i, k) tmp += self.ufl_operands[0].evaluate(x, mapping, component, index_values) index_values.pop() return tmp def __str__(self): return "sum_{%s} %s " % (str( self.ufl_operands[1]), parstr(self.ufl_operands[0], self))
# # You should have received a copy of the GNU Lesser General Public License # along with UFL. If not, see <http://www.gnu.org/licenses/>. # # Modified by Massimiliano Leoni, 2016. from ufl.utils.str import as_native_str from ufl.utils.str import as_native_strings from ufl.log import error from ufl.utils.counted import counted_init from ufl.core.ufl_type import ufl_type from ufl.core.terminal import Terminal # Export list for ufl.classes __all_classes__ = as_native_strings(["IndexBase", "FixedIndex", "Index"]) class IndexBase(object): """Base class for all indices.""" __slots__ = () def __init__(self): pass class FixedIndex(IndexBase): """UFL value: An index with a specific value assigned.""" __slots__ = as_native_strings(("_value", "_hash")) _cache = {}
import sys import types import logging from logging import DEBUG, INFO, WARNING, ERROR, CRITICAL # noqa: F401 from ufl.utils.str import as_native_strings log_functions = [ "log", "debug", "info", "deprecate", "warning", "error", "begin", "end", "set_level", "push_level", "pop_level", "set_indent", "add_indent", "set_handler", "get_handler", "get_logger", "add_logfile", "set_prefix", "info_red", "info_green", "info_blue", "warning_red", "warning_green", "warning_blue" ] __all__ = as_native_strings( log_functions + ["Logger", "log_functions"] + ["DEBUG", "INFO", "DEPRECATE", "WARNING", "ERROR", "CRITICAL"]) DEPRECATE = (INFO + WARNING) // 2 # This is used to override emit() in StreamHandler for printing # without newline def emit(self, record): message = self.format(record) format_string = "%s" if getattr(record, "continued", False) else "%s\n" self.stream.write(format_string % message) self.flush() # Colors if the terminal supports it (disabled e.g. when piped to
# Modified by Marie E. Rognes 2012 import numbers from ufl.utils.str import as_native_str from ufl.utils.str import as_native_strings from ufl.core.ufl_type import attach_operators_from_hash_data from ufl.core.ufl_id import attach_ufl_id from ufl.corealg.traversal import traverse_unique_terminals from ufl.log import error from ufl.cell import as_cell, AbstractCell, TensorProductCell from ufl.finiteelement.tensorproductelement import TensorProductElement # Export list for ufl.classes __all_classes__ = as_native_strings(["AbstractDomain", "Mesh", "MeshView", "TensorProductMesh"]) class AbstractDomain(object): """Symbolic representation of a geometric domain with only a geometric and topological dimension. """ def __init__(self, topological_dimension, geometric_dimension): # Validate dimensions if not isinstance(geometric_dimension, numbers.Integral): error("Expecting integer geometric dimension, not %s" % (geometric_dimension.__class__,)) if not isinstance(topological_dimension, numbers.Integral): error("Expecting integer topological dimension, not %s" % (topological_dimension.__class__,)) if topological_dimension > geometric_dimension:
__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', 'CellDiameter', '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', 'conj', 'real', 'imag', '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', ])
class Measure(object): __slots__ = as_native_strings(("_integral_type", "_domain", "_subdomain_id", "_metadata", "_subdomain_data")) """Representation of an integration measure. The Measure object holds information about integration properties to be transferred to a Form on multiplication with a scalar expression. """ def __init__(self, integral_type, # "dx" etc domain=None, subdomain_id="everywhere", metadata=None, subdomain_data=None): """ integral_type: str, one of "cell", etc., or short form "dx", etc. domain: an AbstractDomain object (most often a Mesh) subdomain_id: either string "everywhere", a single subdomain id int, or tuple of ints metadata: dict, with additional compiler-specific parameters affecting how code is generated, including parameters for optimization or debugging of generated code. subdomain_data: object representing data to interpret subdomain_id with. """ # Map short name to long name and require a valid one self._integral_type = as_integral_type(integral_type) # Check that we either have a proper AbstractDomain or none self._domain = None if domain is None else as_domain(domain) if not (self._domain is None or isinstance(self._domain, AbstractDomain)): error("Invalid domain.") # Store subdomain data self._subdomain_data = subdomain_data # FIXME: Cannot require this (yet) because we currently have # no way to implement ufl_id for dolfin SubDomain # if not (self._subdomain_data is None or hasattr(self._subdomain_data, "ufl_id")): # error("Invalid domain data, missing ufl_id() implementation.") # Accept "everywhere", single subdomain, or multiple # subdomains if isinstance(subdomain_id, tuple): for did in subdomain_id: if not isinstance(did, numbers.Integral): error("Invalid subdomain_id %s." % (did,)) else: if not (subdomain_id in ("everywhere",) or isinstance(subdomain_id, numbers.Integral)): error("Invalid subdomain_id %s." % (subdomain_id,)) self._subdomain_id = subdomain_id # Validate compiler options are None or dict if metadata is not None and not isinstance(metadata, dict): error("Invalid metadata.") self._metadata = metadata or EmptyDict def integral_type(self): """Return the domain type. Valid domain types are "cell", "exterior_facet", "interior_facet", etc. """ return self._integral_type def ufl_domain(self): """Return the domain associated with this measure. This may be None or a Domain object. """ return self._domain def subdomain_id(self): "Return the domain id of this measure (integer)." return self._subdomain_id def metadata(self): """Return the integral metadata. This data is not interpreted by UFL. It is passed to the form compiler which can ignore it or use it to compile each integral of a form in a different way. """ return self._metadata def reconstruct(self, integral_type=None, subdomain_id=None, domain=None, metadata=None, subdomain_data=None): """Construct a new Measure object with some properties replaced with new values. Example: <dm = Measure instance> b = dm.reconstruct(subdomain_id=2) c = dm.reconstruct(metadata={ "quadrature_degree": 3 }) Used by the call operator, so this is equivalent: b = dm(2) c = dm(0, { "quadrature_degree": 3 }) """ if subdomain_id is None: subdomain_id = self.subdomain_id() if domain is None: domain = self.ufl_domain() if metadata is None: metadata = self.metadata() if subdomain_data is None: subdomain_data = self.subdomain_data() return Measure(self.integral_type(), domain=domain, subdomain_id=subdomain_id, metadata=metadata, subdomain_data=subdomain_data) def subdomain_data(self): """Return the integral subdomain_data. This data is not interpreted by UFL. Its intension is to give a context in which the domain id is interpreted. """ return self._subdomain_data # Note: Must keep the order of the first two arguments here # (subdomain_id, metadata) for backwards compatibility, because # some tutorials write e.g. dx(0, {...}) to set metadata. def __call__(self, subdomain_id=None, metadata=None, domain=None, subdomain_data=None, degree=None, scheme=None, rule=None): """Reconfigure measure with new domain specification or metadata.""" # Deprecation of 'rule' in favour of 'scheme' if rule is not None: deprecate("Measure argument 'rule' has been renamed to 'scheme'.") assert scheme is None or scheme == rule scheme = rule # Let syntax dx() mean integral over everywhere all_args = (subdomain_id, metadata, domain, subdomain_data, degree, scheme) if all(arg is None for arg in all_args): return self.reconstruct(subdomain_id="everywhere") # Let syntax dx(domain) or dx(domain, metadata) mean integral # over entire domain. To do this we need to hijack the first # argument: if subdomain_id is not None and (isinstance(subdomain_id, AbstractDomain) or hasattr(subdomain_id, 'ufl_domain')): if domain is not None: error("Ambiguous: setting domain both as keyword argument and first argument.") subdomain_id, domain = "everywhere", as_domain(subdomain_id) # If degree or scheme is set, inject into metadata. This is a # quick fix to enable the dx(..., degree=3) notation. # TODO: Make degree and scheme properties of integrals instead of adding to metadata. if (degree, scheme) != (None, None): metadata = {} if metadata is None else metadata.copy() if degree is not None: metadata["quadrature_degree"] = degree if scheme is not None: metadata["quadrature_rule"] = scheme # If we get any keywords, use them to reconstruct Measure. # Note that if only one argument is given, it is the # subdomain_id, e.g. dx(3) == dx(subdomain_id=3) return self.reconstruct(subdomain_id=subdomain_id, domain=domain, metadata=metadata, subdomain_data=subdomain_data) def __getitem__(self, data): """This operator supports legacy syntax in python dolfin programs. The old documentation reads: Return a new Measure for same integration type with an attached context for interpreting domain ids. By default this new Measure integrates over everywhere, but it can be restricted with a domain id as usual. Example: dx = dx[boundaries]; L = f*v*dx + g*v+dx(1). """ deprecate("Notation dx[meshfunction] is deprecated. Please use dx(subdomain_data=meshfunction) instead.") return self(subdomain_data=data) def __str__(self): global integral_type_to_measure_name name = integral_type_to_measure_name[self._integral_type] args = [] if self._subdomain_id is not None: args.append("subdomain_id=%s" % (self._subdomain_id,)) if self._domain is not None: args.append("domain=%s" % (self._domain,)) if self._metadata: # Stored as EmptyDict if None args.append("metadata=%s" % (self._metadata,)) if self._subdomain_data is not None: args.append("subdomain_data=%s" % (self._subdomain_data,)) return "%s(%s)" % (name, ', '.join(args)) def __repr__(self): "Return a repr string for this Measure." global integral_type_to_measure_name args = [] args.append(repr(self._integral_type)) if self._subdomain_id is not None: args.append("subdomain_id=%s" % repr(self._subdomain_id)) if self._domain is not None: args.append("domain=%s" % repr(self._domain)) if self._metadata: # Stored as EmptyDict if None args.append("metadata=%s" % repr(self._metadata)) if self._subdomain_data is not None: args.append("subdomain_data=%s" % repr(self._subdomain_data)) r = "%s(%s)" % (type(self).__name__, ', '.join(args)) return as_native_str(r) def __hash__(self): "Return a hash value for this Measure." hashdata = (self._integral_type, self._subdomain_id, hash(self._domain), metadata_hashdata(self._metadata), id_or_none(self._subdomain_data)) return hash(hashdata) def __eq__(self, other): "Checks if two Measures are equal." return (isinstance(other, Measure) and self._integral_type == other._integral_type and self._subdomain_id == other._subdomain_id and self._domain == other._domain and id_or_none(self._subdomain_data) == id_or_none(other._subdomain_data) and metadata_equal(self._metadata, other._metadata)) def __add__(self, other): """Add two measures (self+other). Creates an intermediate object used for the notation expr * (dx(1) + dx(2)) := expr * dx(1) + expr * dx(2) """ if isinstance(other, Measure): # Let dx(1) + dx(2) equal dx((1,2)) return MeasureSum(self, other) else: # Can only add Measures return NotImplemented def __mul__(self, other): """Multiply two measures (self*other). Creates an intermediate object used for the notation expr * (dm1 * dm2) := expr * dm1 * dm2 This is work in progress and not functional. """ if isinstance(other, Measure): # Tensor product measure support return MeasureProduct(self, other) else: # Can't multiply Measure from the right with non-Measure type return NotImplemented def __rmul__(self, integrand): """Multiply a scalar expression with measure to construct a form with a single integral. This is to implement the notation form = integrand * self Integration properties are taken from this Measure object. """ # Avoid circular imports from ufl.integral import Integral from ufl.form import Form # Allow python literals: 1*dx and 1.0*dx if isinstance(integrand, (int, float)): integrand = as_ufl(integrand) # Let other types implement multiplication with Measure if # they want to (to support the dolfin-adjoint TimeMeasure) if not isinstance(integrand, Expr): return NotImplemented # Allow only scalar integrands if not is_true_ufl_scalar(integrand): error("Can only integrate scalar expressions. The integrand is a " "tensor expression with value shape %s and free indices with labels %s." % (integrand.ufl_shape, integrand.ufl_free_indices)) # If we have a tuple of domain ids, delegate composition to # Integral.__add__: subdomain_id = self.subdomain_id() if isinstance(subdomain_id, tuple): return sum(integrand*self.reconstruct(subdomain_id=d) for d in subdomain_id) # Check that we have an integer subdomain or a string # ("everywhere" or "otherwise", any more?) if not isinstance(subdomain_id, (str, numbers.Integral,)): error("Expecting integer or string domain id.") # If we don't have an integration domain, try to find one in # integrand domain = self.ufl_domain() if domain is None: domains = extract_domains(integrand) if len(domains) == 1: domain, = domains elif len(domains) == 0: error("This integral is missing an integration domain.") else: error("Multiple domains found, making the choice of integration domain ambiguous.") # Otherwise create and return a one-integral form integral = Integral(integrand=integrand, integral_type=self.integral_type(), domain=domain, subdomain_id=subdomain_id, metadata=self.metadata(), subdomain_data=self.subdomain_data()) return Form([integral])
import numbers from ufl.utils.str import as_native_strings from ufl.utils.str import as_native_str from ufl.log import error, deprecate from ufl.core.expr import Expr from ufl.checks import is_true_ufl_scalar from ufl.constantvalue import as_ufl from ufl.utils.dicts import EmptyDict from ufl.domain import as_domain, AbstractDomain, extract_domains from ufl.protocols import id_or_none, metadata_equal, metadata_hashdata # Export list for ufl.classes __all_classes__ = as_native_strings(["Measure", "MeasureSum", "MeasureProduct"]) # TODO: Design a class IntegralType(name, shortname, codim, num_cells, ...)? # TODO: Improve descriptions below: # Enumeration of valid domain types _integral_types = [ # === Integration over full topological dimension: ("cell", "dx"), # Over cells of a mesh # ("macro_cell", "dE"), # Over a group of adjacent cells (TODO: Arbitrary cell group? Not currently used.) # === Integration over topological dimension - 1: ("exterior_facet", "ds"), # Over one-sided exterior facets of a mesh ("interior_facet", "dS"), # Over two-sided facets between pairs of adjacent cells of a mesh
import ufl.finiteelement # noqa E401 __all__ += populate_namespace_with_module_classes(ufl.finiteelement, locals()) import ufl.domain # noqa E401 __all__ += populate_namespace_with_module_classes(ufl.domain, locals()) import ufl.functionspace # noqa E401 __all__ += populate_namespace_with_module_classes(ufl.functionspace, locals()) import ufl.core.multiindex # noqa E401 __all__ += populate_namespace_with_module_classes(ufl.core.multiindex, locals()) import ufl.argument # noqa E401 __all__ += populate_namespace_with_module_classes(ufl.argument, locals()) import ufl.measure # noqa E401 __all__ += populate_namespace_with_module_classes(ufl.measure, locals()) import ufl.integral # noqa E401 __all__ += populate_namespace_with_module_classes(ufl.integral, locals()) import ufl.form # noqa E401 __all__ += populate_namespace_with_module_classes(ufl.form, locals()) import ufl.equation # noqa E401 __all__ += populate_namespace_with_module_classes(ufl.equation, locals()) __all__ = as_native_strings(__all__)
from itertools import chain from collections import defaultdict from ufl.log import error, warning from ufl.domain import sort_domains from ufl.integral import Integral from ufl.checks import is_scalar_constant_expression from ufl.equation import Equation from ufl.core.expr import Expr from ufl.core.expr import ufl_err_str from ufl.constantvalue import Zero from ufl.utils.str import as_native_strings, as_native_str # Export list for ufl.classes __all_classes__ = as_native_strings(["Form"]) # --- The Form class, representing a complete variational form or functional --- def _sorted_integrals(integrals): """Sort integrals by domain id, integral type, subdomain id for a more stable signature computation.""" # Group integrals in multilevel dict by keys # [domain][integral_type][subdomain_id] integrals_dict = defaultdict(lambda: defaultdict(lambda: defaultdict(list))) for integral in integrals: d = integral.ufl_domain() if d is None: error("Each integral in a form must have a uniquely defined integration domain.")
__all__ = as_native_strings([ "estimate_total_polynomial_degree", "sort_elements", "compute_form_data", "purge_list_tensors", "apply_transformer", "ReuseTransformer", "load_ufl_file", "Transformer", "MultiFunction", "extract_unique_elements", "extract_type", "extract_elements", "extract_sub_elements", "preprocess_expression", "expand_indices", "replace", "expand_derivatives", "extract_coefficients", "strip_variables", "post_traversal", "change_to_reference_grad", "expand_compounds", "validate_form", "ufl2latex", "FormSplitter", "extract_arguments", "compute_form_adjoint", "compute_form_action", "compute_energy_norm", "compute_form_lhs", "compute_form_rhs", "compute_form_functional", "compute_form_signature", "tree_format", ])
# Modified by Anders Logg, 2008-2009. # Modified by Massimiliano Leoni, 2016. import numbers from ufl.utils.str import as_native_str from ufl.utils.str import as_native_strings from ufl.log import error from ufl.core.ufl_type import ufl_type from ufl.core.terminal import FormArgument from ufl.split_functions import split from ufl.finiteelement import FiniteElementBase from ufl.domain import default_domain from ufl.functionspace import AbstractFunctionSpace, FunctionSpace # Export list for ufl.classes (TODO: not actually classes: drop? these are in ufl.*) __all_classes__ = as_native_strings(["TestFunction", "TrialFunction", "TestFunctions", "TrialFunctions"]) # --- Class representing an argument (basis function) in a form --- @ufl_type() class Argument(FormArgument): """UFL value: Representation of an argument to a form.""" __slots__ = as_native_strings(( "_ufl_function_space", "_ufl_shape", "_number", "_part", "_repr", ))