def __new__(cls, a, b): # Conversion a = as_ufl(a) b = as_ufl(b) # Type checking if not is_true_ufl_scalar(a): error("Cannot take the power of a non-scalar expression %s." % ufl_err_str(a)) if not is_true_ufl_scalar(b): error("Cannot raise an expression to a non-scalar power %s." % ufl_err_str(b)) # Simplification if isinstance(a, ScalarValue) and isinstance(b, ScalarValue): return as_ufl(a._value**b._value) if isinstance(b, Zero): return IntValue(1) if isinstance(a, Zero) and isinstance(b, ScalarValue): if isinstance(b, ComplexValue): error("Cannot raise zero to a complex power.") bf = float(b) if bf < 0: error("Division by zero, cannot raise 0 to a negative power.") else: return zero() if isinstance(b, ScalarValue) and b._value == 1: return a # Construction self = Operator.__new__(cls) self._init(a, b) return self
def __getitem__(self, key): if key == (): # So that one doesn't have to special case indexing of # expressions without shape. return self error( "Attempting to index with %s, but object is already indexed: %s" % (ufl_err_str(key), ufl_err_str(self)))
def __init__(self, expression, multiindex): # Store operands Operator.__init__(self, (expression, multiindex)) # Error checking if not isinstance(expression, Expr): error("Expecting Expr instance, not %s." % ufl_err_str(expression)) if not isinstance(multiindex, MultiIndex): error("Expecting MultiIndex instance, not %s." % ufl_err_str(multiindex)) shape = expression.ufl_shape # Error checking if len(shape) != len(multiindex): error("Invalid number of indices (%d) for tensor " "expression of rank %d:\n\t%s\n" % (len(multiindex), len( expression.ufl_shape), ufl_err_str(expression))) if any( int(di) >= int(si) or int(di) < 0 for si, di in zip(shape, multiindex) if isinstance(di, FixedIndex)): error("Fixed index out of range!") # Build tuples of free index ids and dimensions if 1: efi = expression.ufl_free_indices efid = expression.ufl_index_dimensions fi = list(zip(efi, efid)) for pos, ind in enumerate(multiindex._indices): if isinstance(ind, Index): fi.append((ind.count(), shape[pos])) fi = unique_sorted_indices(sorted(fi)) if fi: fi, fid = zip(*fi) else: fi, fid = (), () else: mfiid = [(ind.count(), shape[pos]) for pos, ind in enumerate(multiindex._indices) if isinstance(ind, Index)] mfi, mfid = zip(*mfiid) if mfiid else ((), ()) fi, fid = merge_unique_indices(expression.ufl_free_indices, expression.ufl_index_dimensions, mfi, mfid) # Cache free index and dimensions self.ufl_free_indices = fi self.ufl_index_dimensions = fid
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 __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, expression, multiindex): # Store operands Operator.__init__(self, (expression, multiindex)) # Error checking if not isinstance(expression, Expr): error("Expecting Expr instance, not %s." % ufl_err_str(expression)) if not isinstance(multiindex, MultiIndex): error("Expecting MultiIndex instance, not %s." % ufl_err_str(multiindex)) shape = expression.ufl_shape # Error checking if len(shape) != len(multiindex): error("Invalid number of indices (%d) for tensor " "expression of rank %d:\n\t%s\n" % (len(multiindex), len(expression.ufl_shape), ufl_err_str(expression))) if any(int(di) >= int(si) for si, di in zip(shape, multiindex) if isinstance(di, FixedIndex)): error("Fixed index out of range!") # Build tuples of free index ids and dimensions if 1: efi = expression.ufl_free_indices efid = expression.ufl_index_dimensions fi = list(zip(efi, efid)) for pos, ind in enumerate(multiindex._indices): if isinstance(ind, Index): fi.append((ind.count(), shape[pos])) fi = unique_sorted_indices(sorted(fi)) if fi: fi, fid = zip(*fi) else: fi, fid = (), () else: mfiid = [(ind.count(), shape[pos]) for pos, ind in enumerate(multiindex._indices) if isinstance(ind, Index)] mfi, mfid = zip(*mfiid) if mfiid else ((), ()) fi, fid = merge_unique_indices(expression.ufl_free_indices, expression.ufl_index_dimensions, mfi, mfid) # Cache free index and dimensions self.ufl_free_indices = fi self.ufl_index_dimensions = fid
def reference_grad(self, g): # d (grad_X(...(x)) / dx => grad_X(...(Argument(x.function_space())) o = g ngrads = 0 while isinstance(o, ReferenceGrad): o, = o.ufl_operands ngrads += 1 if not (isinstance(o, SpatialCoordinate) or isinstance(o.ufl_operands[0], FormArgument)): error("Expecting gradient of a FormArgument, not %s" % ufl_err_str(o)) def apply_grads(f): for i in range(ngrads): f = ReferenceGrad(f) return f # Find o among all w without any indexing, which makes this # easy for (w, v) in zip(self._w, self._v): if o == w and isinstance(v, ReferenceValue) and isinstance( v.ufl_operands[0], FormArgument): # Case: d/dt [w + t v] return apply_grads(v) return self.independent_terminal(o)
def expr(self, x): """The default is a nonlinear operator not accepting any Arguments among its children.""" if _expr_has_terminal_types(x, Argument): error("Found Argument in %s, this is an invalid expression." % ufl_err_str(x)) return (x, set())
def division(self, x): "Return parts_of_numerator/denominator." # Get numerator and denominator numerator, denominator = x.ufl_operands # Check for Arguments in the denominator if _expr_has_terminal_types(denominator, Argument): error( "Found Argument in denominator of %s , this is an invalid expression." % ufl_err_str(x)) # Visit numerator numerator_parts, provides = self.visit(numerator) # If numerator is zero, return zero. (No need to check whether # it provides too much, already checked by visit.) if isinstance(numerator_parts, Zero): return (zero_expr(x), set()) # Reuse x if possible, otherwise reconstruct from (parts of) # numerator and denominator x = self.reuse_if_possible(x, numerator_parts, denominator) return (x, provides)
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 __new__(cls, a, b): # Conversion a = as_ufl(a) b = as_ufl(b) # Type checking # Make sure everything is scalar if a.ufl_shape or b.ufl_shape: error("Product can only represent products of scalars, " "got\n\t%s\nand\n\t%s" % (ufl_err_str(a), ufl_err_str(b))) # Simplification if isinstance(a, Zero) or isinstance(b, Zero): # Got any zeros? Return zero. fi, fid = merge_unique_indices(a.ufl_free_indices, a.ufl_index_dimensions, b.ufl_free_indices, b.ufl_index_dimensions) return Zero((), fi, fid) sa = isinstance(a, ScalarValue) sb = isinstance(b, ScalarValue) if sa and sb: # const * const = const # FIXME: Handle free indices like with zero? I think # IntValue may be index annotated now? return as_ufl(a._value * b._value) elif sa: # 1 * b = b if a._value == 1: return b # a, b = a, b elif sb: # a * 1 = a if b._value == 1: return a a, b = b, a # elif a == b: # a * a = a**2 # TODO: Why? Maybe just remove this? # if not a.ufl_free_indices: # return a**2 else: # a * b = b * a # Sort operands in a semi-canonical order # (NB! This is fragile! Small changes here can have large effects.) a, b = sorted_expr((a, b)) # Construction self = Operator.__new__(cls) self._init(a, b) return self
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 * Conj(b) # sort operands for unique representation, # must be independent of various counts etc. # as explained in cmp_expr if (a, b) != tuple(sorted_expr((a, b))): return Conj(Inner(b, a)) return CompoundTensorOperator.__new__(cls)
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*Conj(b) # sort operands for unique representation, # must be independent of various counts etc. # as explained in cmp_expr if (a, b) != tuple(sorted_expr((a, b))): return Conj(Inner(b, a)) return CompoundTensorOperator.__new__(cls)
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 __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 __call__(self, *args, **kwargs): """UFL form operator: Evaluate form by replacing arguments and coefficients. Replaces form.arguments() with given positional arguments in same number and ordering. Number of positional arguments must be 0 or equal to the number of Arguments in the form. The optional keyword argument coefficients can be set to a dict to replace Coefficients with expressions of matching shapes. Example: V = FiniteElement("CG", triangle, 1) v = TestFunction(V) u = TrialFunction(V) f = Coefficient(V) g = Coefficient(V) a = g*inner(grad(u), grad(v))*dx M = a(f, f, coefficients={ g: 1 }) Is equivalent to M == grad(f)**2*dx. """ repdict = {} if args: arguments = self.arguments() if len(arguments) != len(args): error("Need %d arguments to form(), got %d." % (len(arguments), len(args))) repdict.update(zip(arguments, args)) coefficients = kwargs.pop("coefficients") if kwargs: error("Unknown kwargs %s." % str(list(kwargs))) if coefficients is not None: coeffs = self.coefficients() for f in coefficients: if f in coeffs: repdict[f] = coefficients[f] else: warning("Coefficient %s is not in form." % ufl_err_str(f)) if repdict: from ufl.formoperators import replace return replace(self, repdict) else: return self
def reference_grad(self, g): # d (grad_X(...(x)) / dx => grad_X(...(Argument(x.function_space())) o = g ngrads = 0 while isinstance(o, ReferenceGrad): o, = o.ufl_operands ngrads += 1 if not (isinstance(o, SpatialCoordinate) or isinstance(o.ufl_operands[0], FormArgument)): error("Expecting gradient of a FormArgument, not %s" % ufl_err_str(o)) def apply_grads(f): for i in range(ngrads): f = ReferenceGrad(f) return f # Find o among all w without any indexing, which makes this # easy for (w, v) in zip(self._w, self._v): if o == w and isinstance(v, ReferenceValue) and isinstance(v.ufl_operands[0], FormArgument): # Case: d/dt [w + t v] return apply_grads(v) return self.independent_terminal(o)
def _handle_derivative_arguments(form, coefficient, argument): # Wrap single coefficient in tuple for uniform treatment below if isinstance(coefficient, (list, tuple, ListTensor)): coefficients = tuple(coefficient) else: coefficients = (coefficient,) if argument is None: # Try to create argument if not provided if not all(isinstance(c, Coefficient) for c in coefficients): error("Can only create arguments automatically for non-indexed coefficients.") # Get existing arguments from form and position the new one # with the next argument number if isinstance(form, Form): form_arguments = form.arguments() else: # To handle derivative(expression), which is at least used # in tests. Remove? form_arguments = extract_arguments(form) numbers = sorted(set(arg.number() for arg in form_arguments)) number = max(numbers + [-1]) + 1 # Don't know what to do with parts, let the user sort it out # in that case parts = set(arg.part() for arg in form_arguments) if len(parts - {None}) != 0: error("Not expecting parts here, provide your own arguments.") part = None # Create argument and split it if in a mixed space function_spaces = [c.ufl_function_space() for c in coefficients] domains = [fs.ufl_domain() for fs in function_spaces] elements = [fs.ufl_element() for fs in function_spaces] if len(function_spaces) == 1: arguments = (Argument(function_spaces[0], number, part),) else: # Create in mixed space over assumed (for now) same domain assert all(fs.ufl_domain() == domains[0] for fs in function_spaces) elm = MixedElement(*elements) fs = FunctionSpace(domains[0], elm) arguments = split(Argument(fs, number, part)) else: # Wrap single argument in tuple for uniform treatment below if isinstance(argument, (list, tuple)): arguments = tuple(argument) else: n = len(coefficients) if n == 1: arguments = (argument,) else: if argument.ufl_shape == (n,): arguments = tuple(argument[i] for i in range(n)) else: arguments = split(argument) # Build mapping from coefficient to argument m = {} for (c, a) in zip(coefficients, arguments): if c.ufl_shape != a.ufl_shape: error("Coefficient and argument shapes do not match!") if isinstance(c, Coefficient) or isinstance(c, SpatialCoordinate): m[c] = a else: if not isinstance(c, Indexed): error("Invalid coefficient type for %s" % ufl_err_str(c)) f, i = c.ufl_operands if not isinstance(f, Coefficient): error("Expecting an indexed coefficient, not %s" % ufl_err_str(f)) if not (isinstance(i, MultiIndex) and all(isinstance(j, FixedIndex) for j in i)): error("Expecting one or more fixed indices, not %s" % ufl_err_str(i)) i = tuple(int(j) for j in i) if f not in m: m[f] = {} m[f][i] = a # Merge coefficient derivatives (arguments) based on indices for c, p in m.items(): if isinstance(p, dict): a = zero_lists(c.ufl_shape) for i, g in p.items(): set_list_item(a, i, g) m[c] = as_tensor(a) # Wrap and return generic tuples items = sorted(m.items(), key=lambda x: x[0].count()) coefficients = ExprList(*[item[0] for item in items]) arguments = ExprList(*[item[1] for item in items]) return coefficients, arguments
def expecting_terminal(v): error("Expecting Terminal instance, not %s." % ufl_err_str(v))
def expecting_true_ufl_scalar(v): error("Expecting UFL scalar expression with no free indices, not %s." % ufl_err_str(v))
def expecting_python_scalar(v): error("Expecting Python scalar, not %s." % ufl_err_str(v))
def expecting_expr(v): error("Expecting Expr instance, not %s." % ufl_err_str(v))
def grad(self, g): # If we hit this type, it has already been propagated to a # coefficient (or grad of a coefficient), # FIXME: Assert # this! so we need to take the gradient of the variation or # return zero. Complications occur when dealing with # derivatives w.r.t. single components... # Figure out how many gradients are around the inner terminal ngrads = 0 o = g while isinstance(o, Grad): o, = o.ufl_operands ngrads += 1 if not isinstance(o, FormArgument): error("Expecting gradient of a FormArgument, not %s" % ufl_err_str(o)) def apply_grads(f): for i in range(ngrads): f = Grad(f) return f # Find o among all w without any indexing, which makes this # easy for (w, v) in zip(self._w, self._v): if o == w and isinstance(v, FormArgument): # Case: d/dt [w + t v] return apply_grads(v) # If o is not among coefficient derivatives, return do/dw=0 gprimesum = Zero(g.ufl_shape) def analyse_variation_argument(v): # Analyse variation argument if isinstance(v, FormArgument): # Case: d/dt [w[...] + t v] vval, vcomp = v, () elif isinstance(v, Indexed): # Case: d/dt [w + t v[...]] # Case: d/dt [w[...] + t v[...]] vval, vcomp = v.ufl_operands vcomp = tuple(vcomp) else: error("Expecting argument or component of argument.") if not all(isinstance(k, FixedIndex) for k in vcomp): error("Expecting only fixed indices in variation.") return vval, vcomp def compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp): # Apply gradients directly to argument vval, and get the # right indexed scalar component(s) kk = indices(ngrads) Dvkk = apply_grads(vval)[vcomp+kk] # Place scalar component(s) Dvkk into the right tensor # positions if wshape: Ejj, jj = unit_indexed_tensor(wshape, wcomp) else: Ejj, jj = 1, () gprimeterm = as_tensor(Ejj*Dvkk, jj+kk) return gprimeterm # Accumulate contributions from variations in different # components for (w, v) in zip(self._w, self._v): # Analyse differentiation variable coefficient if isinstance(w, FormArgument): if not w == o: continue wshape = w.ufl_shape if isinstance(v, FormArgument): # Case: d/dt [w + t v] return apply_grads(v) elif isinstance(v, ListTensor): # Case: d/dt [w + t <...,v,...>] for wcomp, vsub in unwrap_list_tensor(v): if not isinstance(vsub, Zero): vval, vcomp = analyse_variation_argument(vsub) gprimesum = gprimesum + compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp) else: if wshape != (): error("Expecting scalar coefficient in this branch.") # Case: d/dt [w + t v[...]] wval, wcomp = w, () vval, vcomp = analyse_variation_argument(v) gprimesum = gprimesum + compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp) elif isinstance(w, Indexed): # This path is tested in unit tests, but not actually used? # Case: d/dt [w[...] + t v[...]] # Case: d/dt [w[...] + t v] wval, wcomp = w.ufl_operands if not wval == o: continue assert isinstance(wval, FormArgument) if not all(isinstance(k, FixedIndex) for k in wcomp): error("Expecting only fixed indices in differentiation variable.") wshape = wval.ufl_shape vval, vcomp = analyse_variation_argument(v) gprimesum = gprimesum + compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp) else: error("Expecting coefficient or component of coefficient.") # FIXME: Handle other coefficient derivatives: oprimes = # self._cd.get(o) if 0: oprimes = self._cd.get(o) if oprimes is None: if self._cd: # TODO: Make it possible to silence this message # in particular? It may be good to have for # debugging... warning("Assuming d{%s}/d{%s} = 0." % (o, self._w)) else: # Make sure we have a tuple to match the self._v tuple if not isinstance(oprimes, tuple): oprimes = (oprimes,) if len(oprimes) != len(self._v): error("Got a tuple of arguments, expecting a" " matching tuple of coefficient derivatives.") # Compute dg/dw_j = dg/dw_h : v. # Since we may actually have a tuple of oprimes and vs # in a 'mixed' space, sum over them all to get the # complete inner product. Using indices to define a # non-compound inner product. for (oprime, v) in zip(oprimes, self._v): error("FIXME: Figure out how to do this with ngrads") so, oi = as_scalar(oprime) rv = len(v.ufl_shape) oi1 = oi[:-rv] oi2 = oi[-rv:] prod = so*v[oi2] if oi1: gprimesum += as_tensor(prod, oi1) else: gprimesum += prod return gprimesum
def __getitem__(self, key): error( "Attempting to index with %s, but object is already indexed: %s" % (ufl_err_str(key), ufl_err_str(self)))
def validate_form( form ): # TODO: Can we make this return a list of errors instead of raising exception? """Performs all implemented validations on a form. Raises exception if something fails.""" errors = [] if not isinstance(form, Form): msg = "Validation failed, not a Form:\n%s" % ufl_err_str(form) error(msg) # errors.append(msg) # return errors # FIXME: There's a bunch of other checks we should do here. # FIXME: Add back check for multilinearity # Check that form is multilinear # if not is_multilinear(form): # errors.append("Form is not multilinear in arguments.") # FIXME DOMAIN: Add check for consistency between domains somehow domains = set(t.ufl_domain() for e in iter_expressions(form) for t in traverse_unique_terminals(e)) - {None} if not domains: errors.append("Missing domain definition in form.") # Check that cell is the same everywhere cells = set(dom.ufl_cell() for dom in domains) - {None} if not cells: errors.append("Missing cell definition in form.") elif len(cells) > 1: errors.append("Multiple cell definitions in form: %s" % str(cells)) # Check that no Coefficient or Argument instance have the same # count unless they are the same coefficients = {} arguments = {} for e in iter_expressions(form): for f in traverse_unique_terminals(e): if isinstance(f, Coefficient): c = f.count() if c in coefficients: g = coefficients[c] if f is not g: errors.append("Found different Coefficients with " + "same count: %s and %s." % (repr(f), repr(g))) else: coefficients[c] = f elif isinstance(f, Argument): n = f.number() p = f.part() if (n, p) in arguments: g = arguments[(n, p)] if f is not g: if n == 0: msg = "TestFunctions" elif n == 1: msg = "TrialFunctions" else: msg = "Arguments with same number and part" msg = "Found different %s: %s and %s." % (msg, repr(f), repr(g)) errors.append(msg) else: arguments[(n, p)] = f # Check that all integrands are scalar for expression in iter_expressions(form): if not is_true_ufl_scalar(expression): errors.append("Found non-scalar integrand expression: %s\n" % ufl_err_str(expression)) # Check that restrictions are permissible for integral in form.integrals(): # Only allow restrictions on interior facet integrals and # surface measures if integral.integral_type().startswith("interior_facet"): check_restrictions(integral.integrand(), True) else: check_restrictions(integral.integrand(), False) # Raise exception with all error messages # TODO: Return errors list instead, need to collect messages from # all validations above first. if errors: final_msg = 'Found errors in validation of form:\n%s' % '\n\n'.join( errors) error(final_msg)
def validate_form(form): # TODO: Can we make this return a list of errors instead of raising exception? """Performs all implemented validations on a form. Raises exception if something fails.""" errors = [] if not isinstance(form, Form): msg = "Validation failed, not a Form:\n%s" % ufl_err_str(form) error(msg) # errors.append(msg) # return errors # FIXME: There's a bunch of other checks we should do here. # FIXME: Add back check for multilinearity # Check that form is multilinear # if not is_multilinear(form): # errors.append("Form is not multilinear in arguments.") # FIXME DOMAIN: Add check for consistency between domains somehow domains = set(t.ufl_domain() for e in iter_expressions(form) for t in traverse_unique_terminals(e)) - {None} if not domains: errors.append("Missing domain definition in form.") # Check that cell is the same everywhere cells = set(dom.ufl_cell() for dom in domains) - {None} if not cells: errors.append("Missing cell definition in form.") elif len(cells) > 1: errors.append("Multiple cell definitions in form: %s" % str(cells)) # Check that no Coefficient or Argument instance have the same # count unless they are the same coefficients = {} arguments = {} for e in iter_expressions(form): for f in traverse_unique_terminals(e): if isinstance(f, Coefficient): c = f.count() if c in coefficients: g = coefficients[c] if f is not g: errors.append("Found different Coefficients with " + "same count: %s and %s." % (repr(f), repr(g))) else: coefficients[c] = f elif isinstance(f, Argument): n = f.number() p = f.part() if (n, p) in arguments: g = arguments[(n, p)] if f is not g: if n == 0: msg = "TestFunctions" elif n == 1: msg = "TrialFunctions" else: msg = "Arguments with same number and part" msg = "Found different %s: %s and %s." % (msg, repr(f), repr(g)) errors.append(msg) else: arguments[(n, p)] = f # Check that all integrands are scalar for expression in iter_expressions(form): if not is_true_ufl_scalar(expression): errors.append("Found non-scalar integrand expression: %s\n" % ufl_err_str(expression)) # Check that restrictions are permissible for integral in form.integrals(): # Only allow restrictions on interior facet integrals and # surface measures if integral.integral_type().startswith("interior_facet"): check_restrictions(integral.integrand(), True) else: check_restrictions(integral.integrand(), False) # Raise exception with all error messages # TODO: Return errors list instead, need to collect messages from # all validations above first. if errors: final_msg = 'Found errors in validation of form:\n%s' % '\n\n'.join(errors) error(final_msg)
def __init__(self, a): Operator.__init__(self, (a, )) if not isinstance(a, Expr): error("Expecting Expr instance, not %s." % ufl_err_str(a))
def as_form(form): "Convert to form if not a form, otherwise return form." if not isinstance(form, Form): error("Unable to convert object to a UFL form: %s" % ufl_err_str(form)) return form
def sub_forms_by_domain(form): "Return a list of forms each with an integration domain" if not isinstance(form, Form): error("Unable to convert object to a UFL form: %s" % ufl_err_str(form)) return [Form(form.integrals_by_domain(domain)) for domain in form.ufl_domains()]
def expecting_instance(v, c): error("Expecting %s instance, not %s." % (c.__name__, ufl_err_str(v)))
def division(self, x): "Return parts_of_numerator/denominator." # Get numerator and denominator numerator, denominator = x.ufl_operands # Check for Arguments in the denominator if _expr_has_terminal_types(denominator, Argument): error("Found Argument in denominator of %s , this is an invalid expression." % ufl_err_str(x)) # Visit numerator numerator_parts, provides = self.visit(numerator) # If numerator is zero, return zero. (No need to check whether # it provides too much, already checked by visit.) if isinstance(numerator_parts, Zero): return (zero_expr(x), set()) # Reuse x if possible, otherwise reconstruct from (parts of) # numerator and denominator x = self.reuse_if_possible(x, numerator_parts, denominator) return (x, provides)
def grad(self, g): # If we hit this type, it has already been propagated to a # coefficient (or grad of a coefficient), # FIXME: Assert # this! so we need to take the gradient of the variation or # return zero. Complications occur when dealing with # derivatives w.r.t. single components... # Figure out how many gradients are around the inner terminal ngrads = 0 o = g while isinstance(o, Grad): o, = o.ufl_operands ngrads += 1 if not isinstance(o, FormArgument): error("Expecting gradient of a FormArgument, not %s" % ufl_err_str(o)) def apply_grads(f): for i in range(ngrads): f = Grad(f) return f # Find o among all w without any indexing, which makes this # easy for (w, v) in zip(self._w, self._v): if o == w and isinstance(v, FormArgument): # Case: d/dt [w + t v] return apply_grads(v) # If o is not among coefficient derivatives, return do/dw=0 gprimesum = Zero(g.ufl_shape) def analyse_variation_argument(v): # Analyse variation argument if isinstance(v, FormArgument): # Case: d/dt [w[...] + t v] vval, vcomp = v, () elif isinstance(v, Indexed): # Case: d/dt [w + t v[...]] # Case: d/dt [w[...] + t v[...]] vval, vcomp = v.ufl_operands vcomp = tuple(vcomp) else: error("Expecting argument or component of argument.") if not all(isinstance(k, FixedIndex) for k in vcomp): error("Expecting only fixed indices in variation.") return vval, vcomp def compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp): # Apply gradients directly to argument vval, and get the # right indexed scalar component(s) kk = indices(ngrads) Dvkk = apply_grads(vval)[vcomp + kk] # Place scalar component(s) Dvkk into the right tensor # positions if wshape: Ejj, jj = unit_indexed_tensor(wshape, wcomp) else: Ejj, jj = 1, () gprimeterm = as_tensor(Ejj * Dvkk, jj + kk) return gprimeterm # Accumulate contributions from variations in different # components for (w, v) in zip(self._w, self._v): # Analyse differentiation variable coefficient if isinstance(w, FormArgument): if not w == o: continue wshape = w.ufl_shape if isinstance(v, FormArgument): # Case: d/dt [w + t v] return apply_grads(v) elif isinstance(v, ListTensor): # Case: d/dt [w + t <...,v,...>] for wcomp, vsub in unwrap_list_tensor(v): if not isinstance(vsub, Zero): vval, vcomp = analyse_variation_argument(vsub) gprimesum = gprimesum + compute_gprimeterm( ngrads, vval, vcomp, wshape, wcomp) else: if wshape != (): error("Expecting scalar coefficient in this branch.") # Case: d/dt [w + t v[...]] wval, wcomp = w, () vval, vcomp = analyse_variation_argument(v) gprimesum = gprimesum + compute_gprimeterm( ngrads, vval, vcomp, wshape, wcomp) elif isinstance( w, Indexed ): # This path is tested in unit tests, but not actually used? # Case: d/dt [w[...] + t v[...]] # Case: d/dt [w[...] + t v] wval, wcomp = w.ufl_operands if not wval == o: continue assert isinstance(wval, FormArgument) if not all(isinstance(k, FixedIndex) for k in wcomp): error( "Expecting only fixed indices in differentiation variable." ) wshape = wval.ufl_shape vval, vcomp = analyse_variation_argument(v) gprimesum = gprimesum + compute_gprimeterm( ngrads, vval, vcomp, wshape, wcomp) else: error("Expecting coefficient or component of coefficient.") # FIXME: Handle other coefficient derivatives: oprimes = # self._cd.get(o) if 0: oprimes = self._cd.get(o) if oprimes is None: if self._cd: # TODO: Make it possible to silence this message # in particular? It may be good to have for # debugging... warning("Assuming d{%s}/d{%s} = 0." % (o, self._w)) else: # Make sure we have a tuple to match the self._v tuple if not isinstance(oprimes, tuple): oprimes = (oprimes, ) if len(oprimes) != len(self._v): error("Got a tuple of arguments, expecting a" " matching tuple of coefficient derivatives.") # Compute dg/dw_j = dg/dw_h : v. # Since we may actually have a tuple of oprimes and vs # in a 'mixed' space, sum over them all to get the # complete inner product. Using indices to define a # non-compound inner product. for (oprime, v) in zip(oprimes, self._v): error("FIXME: Figure out how to do this with ngrads") so, oi = as_scalar(oprime) rv = len(v.ufl_shape) oi1 = oi[:-rv] oi2 = oi[-rv:] prod = so * v[oi2] if oi1: gprimesum += as_tensor(prod, oi1) else: gprimesum += prod return gprimesum
def __getitem__(self, key): error("Attempting to index with %s, but object is already indexed: %s" % (ufl_err_str(key), ufl_err_str(self)))