def __init__(self, *cells): "Create a ProductCell from a given list of cells." self._cells = cells ufl_assert(len(self._cells) > 0, "Expecting at least one cell") self._cellname = self._cells[0].cellname()#" x ".join([c.cellname() for c in cells]) self._topological_dimension = sum(c.topological_dimension() for c in cells) self._geometric_dimension = sum(c.geometric_dimension() for c in cells) self._repr = "ProductCell(*%r)" % list(self._cells) self._n = None self._x = SpatialCoordinate(self) # For now self._xi = None # ? self._J = None # ? self._Jinv = None # ? self._detJ = None # ? self._volume = None self._circumradius = None self._cellsurfacearea = None self._facetarea = None # Not defined self._facetdiameter = None # Not defined
def restricted(self, o): ufl_assert(self.current_restriction is None, "Not expecting twice restricted expression.") self.current_restriction = o._side e, = o.operands() self.visit(e) self.current_restriction = None
def __init__(self, arg1, arg2): Operator.__init__(self) ufl_assert(is_true_ufl_scalar(arg1), "Expecting scalar argument 1.") ufl_assert(is_true_ufl_scalar(arg2), "Expecting scalar argument 2.") self._name = "atan_2" self._arg1 = arg1 self._arg2 = arg2
def __init__(self, shape=(), free_indices=(), index_dimensions=None): if not hasattr(self, '_shape'): ufl_assert( isinstance(free_indices, tuple), "Expecting tuple of free indices, not %s" % str(free_indices)) IndexAnnotated.__init__(self, shape, free_indices, index_dimensions)
def diag(A): """UFL operator: Take the diagonal part of rank 2 tensor A _or_ make a diagonal rank 2 tensor from a rank 1 tensor. Always returns a rank 2 tensor. See also diag_vector.""" # TODO: Make a compound type or two for this operator # Get and check dimensions r = A.rank() if r == 1: n, = A.shape() elif r == 2: m, n = A.shape() ufl_assert(m == n, "Can only take diagonal of square tensors.") else: error("Expecting rank 1 or 2 tensor.") # Build matrix row by row rows = [] for i in range(n): row = [0]*n row[i] = A[i] if r == 1 else A[i,i] rows.append(row) return as_matrix(rows)
def reuse_if_possible(self, o, *operands): "Reuse Expr if possible, otherwise reconstruct from given operands." # FIXME: Use hashes of operands instead for a faster probability based version? #if all(a is b for (a, b) in izip(operands, o.operands())): ufl_assert( len(operands) == len(o.operands()), "Expecting number of operands to match") if operands == o.operands(): return o #return o.reconstruct(*operands) # Debugging version: try: r = o.reconstruct(*operands) except: print print "FAILURE in reuse_if_possible:" print "type(o) =", type(o) print "operands =" print print "\n\n".join(map(str, operands)) print print "stack =" self.print_visit_stack() print raise return r
def geometric_quantity(self, v): "Most geometric quantities are cellwise constant." ufl_assert( v.is_cellwise_constant(), "Missing handler for non-constant geometry type %s." % v._uflclass.__name__) return 0
def _square_matrix_shape(self, A): sh = A.shape() if self._dim is not None: sh = complete_shape(sh, self._dim) # FIXME: Does this ever happen? Pretty sure other places assume shapes are always "complete". ufl_assert(sh[0] == sh[1], "Expecting square matrix.") ufl_assert(sh[0] is not None, "Unknown dimension.") return sh
def __init__(self, element, cell_restriction): ufl_assert(isinstance(element, FiniteElementBase), "Expecting a finite element instance.") ufl_assert( isinstance(cell_restriction, Cell) or cell_restriction == "facet", "Expecting a Cell instance, or the string 'facet'.") super(RestrictedElement, self).__init__("RestrictedElement", element.cell(), element.degree(), element.quadrature_scheme(), element.value_shape()) self._element = element if isinstance(cell_restriction, Cell): # Just attach cell_restriction if it is a Cell self._cell_restriction = cell_restriction elif cell_restriction == "facet": # Create a cell cell = element.cell() self._cell_restriction = Cell( cellname2facetname[cell.cellname()], geometric_dimension=cell.geometric_dimension()) # Cache repr string self._repr = "RestrictedElement(%r, %r)" % (self._element, self._cell_restriction)
def list_tensor(self, f): # TODO: Is this right? Fix for higher order tensors too. "d/dx_i [x_0, ..., x_n-1] = e_i (unit vector)" ops = f.operands() n = len(ops) s = ops[0].shape() ufl_assert(s == (), "TODO: Assuming a vector, i.e. scalar operands.") return unit_vectors(n) # TODO: Non-scalars
def component_tensor(self, f): x, i = f.operands() s = f.shape() ufl_assert(len(s) == 1, "TODO: Assuming a vector, i.e. scalar operands.") n, = s d = ListTensor([1]*n) # TODO: Non-scalars return (d, None)
def _variable_derivative(self, o, f, v): f, fp = f v, vp = v ufl_assert(isinstance(vp, Zero), "TODO: What happens if vp != 0, i.e. v depends the differentiation variable?") # Are there any issues with indices here? Not sure, think through it... oprime = o.reconstruct(fp, v) return (o, oprime)
def diag(A): """UFL operator: Take the diagonal part of rank 2 tensor A _or_ make a diagonal rank 2 tensor from a rank 1 tensor. Always returns a rank 2 tensor. See also diag_vector.""" # TODO: Make a compound type or two for this operator # Get and check dimensions r = A.rank() if r == 1: n, = A.shape() elif r == 2: m, n = A.shape() ufl_assert(m == n, "Can only take diagonal of square tensors.") else: error("Expecting rank 1 or 2 tensor.") # Build matrix row by row rows = [] for i in range(n): row = [0] * n row[i] = A[i] if r == 1 else A[i, i] rows.append(row) return as_matrix(rows)
def __init__(self, *cells): "Create a ProductCell from a given list of cells." self._cells = cells ufl_assert(len(self._cells) > 0, "Expecting at least one cell") self._cellname = self._cells[0].cellname( ) #" x ".join([c.cellname() for c in cells]) self._topological_dimension = sum(c.topological_dimension() for c in cells) self._geometric_dimension = sum(c.geometric_dimension() for c in cells) self._repr = "ProductCell(*%r)" % list(self._cells) self._n = None self._x = SpatialCoordinate(self) # For now self._xi = None # ? self._J = None # ? self._Jinv = None # ? self._detJ = None # ? self._volume = None self._circumradius = None self._cellsurfacearea = None self._facetarea = None # Not defined self._facetdiameter = None # Not defined
def __init__(self, *elements): "Create TensorProductElement from a given list of elements." self._sub_elements = list(elements) ufl_assert( len(self._sub_elements) > 0, "Cannot create TensorProductElement from empty list.") self._repr = "TensorProductElement(*%r)" % self._sub_elements family = "TensorProductElement" # Define cell as the product of each subcell cell = ProductCell(*[e.cell() for e in self._sub_elements]) domain = as_domain( cell) # FIXME: figure out what this is supposed to mean :) # Define polynomial degree as the maximal of each subelement degree = max(e.degree() for e in self._sub_elements) # No quadrature scheme defined quad_scheme = None # For now, check that all subelements have the same value # shape, and use this. # TODO: Not sure if this makes sense, what kind of product is used to build the basis? value_shape = self._sub_elements[0].value_shape() ufl_assert( all(e.value_shape() == value_shape for e in self._sub_elements), "All subelements in must have same value shape") super(TensorProductElement, self).__init__(family, domain, degree, quad_scheme, value_shape)
def __init__(self, *elements): "Create TensorProductElement from a given list of elements." self._sub_elements = list(elements) ufl_assert(len(self._sub_elements) > 0, "Cannot create TensorProductElement from empty list.") self._repr = "TensorProductElement(*%r)" % self._sub_elements family = "TensorProductElement" # Define cell as the product of each subcell cell = ProductCell(*[e.cell() for e in self._sub_elements]) domain = as_domain(cell) # FIXME: figure out what this is supposed to mean :) # Define polynomial degree as the maximal of each subelement degree = max(e.degree() for e in self._sub_elements) # No quadrature scheme defined quad_scheme = None # For now, check that all subelements have the same value # shape, and use this. # TODO: Not sure if this makes sense, what kind of product is used to build the basis? value_shape = self._sub_elements[0].value_shape() ufl_assert(all(e.value_shape() == value_shape for e in self._sub_elements), "All subelements in must have same value shape") super(TensorProductElement, self).__init__(family, domain, degree, quad_scheme, value_shape)
def extract_subelement_component(self, i): """Extract direct subelement index and subelement relative component index for a given component index""" if isinstance(i, int): i = (i,) self._check_component(i) # Select between indexing modes if len(self.value_shape()) == 1: # Indexing into a long vector of flattened subelement shapes j, = i # Find subelement for this index for k, e in enumerate(self._sub_elements): sh = e.value_shape() si = product(sh) if j < si: break j -= si ufl_assert(j >= 0, "Moved past last value component!") # Convert index into a shape tuple j = index_to_component(j, sh) else: # Indexing into a multidimensional tensor # where subelement index is first axis k = i[0] ufl_assert(k < len(self._sub_elements), "Illegal component index (dimension %d)." % k) j = i[1:] return (k, j)
def reconstruct(self, **kwargs): kwargs["family"] = kwargs.get("family", self.family()) kwargs["domain"] = kwargs.get("domain", self.domain()) kwargs["degree"] = kwargs.get("degree", self.degree()) ufl_assert("dim" not in kwargs, "Cannot change dim in reconstruct.") kwargs["dim"] = len(self._sub_elements) kwargs["quad_scheme"] = kwargs.get("quad_scheme", self.quadrature_scheme()) return VectorElement(**kwargs)
def reconstruct_from_elements(self, *elements): "Reconstruct a mixed element from new subelements." if all(a == b for (a,b) in izip(elements, self._sub_elements)): return self ufl_assert(all(a.value_shape() == b.value_shape() for (a,b) in izip(elements, self._sub_elements)), "Expecting new elements to have same value shape as old ones.") return MixedElement(*elements, value_shape=self.value_shape())
def _call(self, arg, mapping=None, component=()): # Taking the restriction or evaluating depending on argument if arg in ("+", "-"): ufl_assert(mapping is None, "Not expecting a mapping when taking restriction.") return _restrict(self, arg) else: return _eval(self, arg, mapping, component)
def d(self): """The dimension of the cell. Only valid if the geometric and topological dimensions are the same.""" ufl_assert(self._topological_dimension == self._geometric_dimension, "Cell.d is undefined when geometric and"+\ "topological dimensions are not the same.") return self._geometric_dimension
def __add__(self, other): "Add two elements, creating an enriched element" ufl_assert(isinstance(other, FiniteElementBase), "Can't add element and %s." % other.__class__) warning_blue("WARNING: Creating an EnrichedElement,\n " +\ "if you intended to create a MixedElement use '*' instead of '+'.") from ufl.finiteelement import EnrichedElement return EnrichedElement(self, other)
def register_element(family, short_name, value_rank, degree_range, cellnames): "Register new finite element family" ufl_assert(family not in ufl_elements, 'Finite element \"%s\" has already been registered.' % family) ufl_elements[family] = (family, short_name, value_rank, degree_range, cellnames) ufl_elements[short_name] = (family, short_name, value_rank, degree_range, cellnames)
def __init__(self, integrals): self._dintegrals = integral_sequence_to_dict(integrals) ufl_assert(all(isinstance(itg, Integral) for itg in integrals), "Expecting list of integrals.") self._signature = None self._hash = None self._form_data = None self._is_preprocessed = False
def __init__(self, spatial_dim, coefficients, arguments, coefficient_derivatives, cache=None): ForwardAD.__init__(self, spatial_dim, var_shape=(), var_free_indices=(), var_index_dimensions={}, cache=cache) self._v = arguments self._w = coefficients self._cd = coefficient_derivatives ufl_assert(isinstance(self._w, Tuple), "Expecting a Tuple.") ufl_assert(isinstance(self._v, Tuple), "Expecting a Tuple.")
def __new__(cls, A): sh = A.shape() ufl_assert(len(sh) == 2, "Symmetric part of tensor with rank != 2 is undefined.") if sh[0] != sh[1]: error("Cannot take symmetric part of rectangular matrix with dimensions %s." % repr(sh)) ufl_assert(not A.free_indices(), "Not expecting free indices in Sym.") if isinstance(A, Zero): return Zero(A.shape(), A.free_indices(), A.index_dimensions()) return CompoundTensorOperator.__new__(cls)
def facet_normal(self, o): if self.require_restriction: ufl_assert( self.current_restriction is not None, "Facet normal must be restricted in interior facet integrals.") else: ufl_assert( self.current_restriction is None, "Restrictions are only allowed for interior facet integrals.")
def relabel(A, indexmap): "UFL operator: Relabel free indices of A with new indices, using the given mapping." ii = tuple(sorted(indexmap.keys())) jj = tuple(indexmap[i] for i in ii) ufl_assert(all(isinstance(i, Index) for i in ii), "Expecting Index objects.") ufl_assert(all(isinstance(j, Index) for j in jj), "Expecting Index objects.") return as_tensor(A, ii)[jj]
def scalar_value(self, x): if len(x.shape()) != len(self.component()): self.print_visit_stack() ufl_assert(len(x.shape()) == len(self.component()), "Component size mismatch.") s = set(x.free_indices()) - set(self._index2value.keys()) if s: error("Free index set mismatch, these indices have no value assigned: %s." % str(s)) return x._uflclass(x.value())
def extract_element_map(elements): "Build map from elements to element index in ordered tuple." element_map = {} unique_elements = unique_tuple(elements) for element in elements: indices = [i for (i, e) in enumerate(unique_elements) if e == element] ufl_assert(len(indices) == 1, "Unable to find unique index for element.") element_map[element] = i return element_map
def conditional(self, f): # TODO: Is this right? What about non-scalars? c, a, b = f.operands() s = f.shape() ufl_assert(s == (), "TODO: Assuming scalar valued expressions.") _0 = Zero() _1 = IntValue(1) da = conditional(c, _1, _0) db = conditional(c, _0, _1) return (None, da, db)
def __new__(cls, a, b): ash, bsh = a.shape(), b.shape() ufl_assert(ash == bsh, "Shape mismatch.") if isinstance(a, Zero) or isinstance(b, Zero): free_indices, index_dimensions = merge_indices(a, b) return Zero((), free_indices, index_dimensions) if ash == (): return a*b return CompoundTensorOperator.__new__(cls)
def integral_info(integral): ufl_assert(isinstance(integral, Integral), "Expecting an Integral.") s = " Integral:\n" s += " Measure representation:\n" s += " %r\n" % integral.measure() s += " Integrand expression representation:\n" s += " %r\n" % integral.integrand() s += " Integrand expression short form:\n" s += " %s" % integral.integrand() return s
def __init__(self, expression, label=None): WrapperType.__init__(self) expression = as_ufl(expression) ufl_assert(isinstance(expression, Expr), "Expecting Expr.") self._expression = expression if label is None: label = Label() ufl_assert(isinstance(label, Label), "Expecting a Label.") self._label = label
def division(self, f): """f = x/y d/dx x/y = 1/y d/dy x/y = -x/y**2 = -f/y""" x, y = f.operands() # Nonscalar x not supported ufl_assert(x.shape() == (), "Expecting scalars in division.") ufl_assert(y.shape() == (), "Expecting scalars in division.") d = 1 / y return (d, -f*d)
def reconstruct(self, op): "Return a new object of the same type with new operands." if op.is_cellwise_constant(): ufl_assert(op.shape() == self._f.shape(), "Operand shape mismatch in NablaGrad reconstruct.") ufl_assert(self._f.free_indices() == op.free_indices(), "Free index mismatch in NablaGrad reconstruct.") return Zero(self.shape(), self.free_indices(), self.index_dimensions()) return self.__class__._uflclass(op)
def __new__(cls, f): ufl_assert(not f.free_indices(), \ "TODO: Taking divergence of an expression with free indices,"\ "should this be a valid expression? Please provide examples!") # Return zero if expression is trivially constant if f.is_cellwise_constant(): return Zero(f.shape()[1:]) # No free indices asserted above return CompoundDerivative.__new__(cls)
def __init__(self, *expressions): WrapperType.__init__(self) e0 = expressions[0] sh = e0.shape() self._shape = (len(expressions), ) + sh self._expressions = tuple(expressions) indexset = set(e0.free_indices()) ufl_assert(all(not (indexset ^ set(e.free_indices())) for e in self._expressions),\ "Can't combine subtensor expressions with different sets of free indices.")
def evaluate(self, x, mapping, component, index_values, derivatives=()): ufl_assert(len(component) == len(self._shape), "Can only evaluate scalars, expecting a component "\ "tuple of length %d, not %s." % (len(self._shape), component)) a = self._expressions[component[0]] component = component[1:] if derivatives: return a.evaluate(x, mapping, component, index_values, derivatives) else: return a.evaluate(x, mapping, component, index_values)
def register_domain_type(domain_type, measure_name): domain_type = domain_type.replace(" ", "_") if domain_type in Measure._domain_types: ufl_assert(Measure._domain_types[domain_type] == measure_name, "Domain type already added with different measure name!") # Nothing to do else: ufl_assert(measure_name not in Measure._domain_types.values(), "Measure name already used for another domain type!") Measure._domain_types[domain_type] = measure_name
def extract_element_map(elements): "Build map from elements to element index in ordered tuple." element_map = {} unique_elements = unique_tuple(elements) for element in elements: indices = [i for (i, e) in enumerate(unique_elements) if e == element] ufl_assert( len(indices) == 1, "Unable to find unique index for element.") element_map[element] = i return element_map
def __new__(cls, a, b): ash, bsh = a.shape(), b.shape() ufl_assert(len(ash) == 1 and ash == bsh, "Cross product requires arguments of rank 1.") if isinstance(a, Zero) or isinstance(b, Zero): free_indices, index_dimensions = merge_indices(a, b) return Zero(ash, free_indices, index_dimensions) return CompoundTensorOperator.__new__(cls)
def extract_duplications(expression): "Build a set of all repeated expressions in expression." # TODO: Handle indices in a canonical way, maybe create a transformation that does this to apply before extract_duplications? ufl_assert(isinstance(expression, Expr), "Expecting UFL expression.") handled = set() duplicated = set() for o in post_traversal(expression): if o in handled: duplicated.add(o) handled.add(o) return duplicated
def elem_op(op, *args): "UFL operator: Take the elementwise application of operator op on scalar values from one or more tensor arguments." args = map(as_ufl, args) sh = args[0].shape() ufl_assert(all(sh == x.shape() for x in args), "Cannot take elementwise operation with different shapes.") if sh == (): return op(*args) def op_ind(ind, *args): return op(*[x[ind] for x in args]) return as_tensor(elem_op_items(op_ind, (), *args))
def sensitivity_rhs(a, u, L, v): """UFL form operator: Compute the right hand side for a sensitivity calculation system. The derivation behind this computation is as follows. Assume a, L to be bilinear and linear forms corresponding to the assembled linear system Ax = b. Where x is the vector of the discrete function corresponding to u. Let v be some scalar variable this equation depends on. Then we can write 0 = d/dv[Ax-b] = dA/dv x + A dx/dv - db/dv, A dx/dv = db/dv - dA/dv x, and solve this system for dx/dv, using the same bilinear form a and matrix A from the original system. Assume the forms are written v = variable(v_expression) L = IL(v)*dx a = Ia(v)*dx where IL and Ia are integrand expressions. Define a Coefficient u representing the solution to the equations. Then we can compute db/dv and dA/dv from the forms da = diff(a, v) dL = diff(L, v) and the action of da on u by dau = action(da, u) In total, we can build the right hand side of the system to compute du/dv with the single line dL = diff(L, v) - action(diff(a, v), u) or, using this function dL = sensitivity_rhs(a, u, L, v) """ msg = "Expecting (a, u, L, v), (bilinear form, function, linear form and scalar variable)." ufl_assert(isinstance(a, Form), msg) ufl_assert(isinstance(u, Coefficient), msg) ufl_assert(isinstance(L, Form), msg) ufl_assert(isinstance(v, Variable), msg) ufl_assert(is_true_ufl_scalar(v), "Expecting scalar variable.") from ufl.operators import diff return diff(L, v) - action(diff(a, v), u)