def inner(a, b): "UFL operator: Take the inner product of a and b." a = as_ufl(a) b = as_ufl(b) if a.shape() == () and b.shape() == (): return a*b return Inner(a, b)
def inner(a, b): "UFL operator: Take the inner product of *a* and *b*. The complex conjugate of the second argument is taken." a = as_ufl(a) b = as_ufl(b) if a.ufl_shape == () and b.ufl_shape == (): return a * Conj(b) return Inner(a, b)
def dot(a, b): "UFL operator: Take the dot product of *a* and *b*. The complex conjugate of the second argument is taken." a = as_ufl(a) b = as_ufl(b) if a.ufl_shape == () and b.ufl_shape == (): return a * b return Dot(a, b)
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 dot(a, b): "UFL operator: Take the dot product of a and b." a = as_ufl(a) b = as_ufl(b) if a.shape() == () and b.shape() == (): return a*b return Dot(a, b)
def __new__(cls, a, b): a = as_ufl(a) b = as_ufl(b) # Assertions # TODO: Enabled workaround for nonscalar division in __div__, # so maybe we can keep this assertion. Some algorithms may need updating. if not is_ufl_scalar(a): error("Expecting scalar nominator in Division.") if not is_true_ufl_scalar(b): error("Division by non-scalar is undefined.") if isinstance(b, Zero): error("Division by zero!") # Simplification a/b -> a if isinstance(a, Zero) or b == 1: return a # Simplification "literal a / literal b" -> "literal value of a/b" # Avoiding integer division by casting to float if isinstance(a, ScalarValue) and isinstance(b, ScalarValue): return as_ufl(float(a._value) / float(b._value)) # Simplification "a / a" -> "1" if not a.free_indices() and not a.shape() and a == b: return as_ufl(1) # construct and initialize a new Division object self = AlgebraOperator.__new__(cls) self._init(a, b) return self
def cross(a, b): "UFL operator: Take the cross product of a and b." a = as_ufl(a) b = as_ufl(b) #ufl_assert(a.shape() == (3,) and b.shape() == (3,), # "Expecting 3D vectors in cross product.") return Cross(a, b)
def atan_2(f1,f2): "UFL operator: Take the inverse tangent of f." f1 = as_ufl(f1) f2 = as_ufl(f2) r = Atan2(f1, f2) if isinstance(r, (ScalarValue, Zero, int, float)): return float(r) return r
def derivative(form, coefficient, argument=None, coefficient_derivatives=None): """UFL form operator: Compute the Gateaux derivative of *form* w.r.t. *coefficient* in direction of *argument*. If the argument is omitted, a new ``Argument`` is created in the same space as the coefficient, with argument number one higher than the highest one in the form. The resulting form has one additional ``Argument`` in the same finite element space as the coefficient. A tuple of ``Coefficient`` s may be provided in place of a single ``Coefficient``, in which case the new ``Argument`` argument is based on a ``MixedElement`` created from this tuple. An indexed ``Coefficient`` from a mixed space may be provided, in which case the argument should be in the corresponding subspace of the coefficient space. If provided, *coefficient_derivatives* should be a mapping from ``Coefficient`` instances to their derivatives w.r.t. *coefficient*. """ coefficients, arguments = _handle_derivative_arguments(form, coefficient, argument) if coefficient_derivatives is None: coefficient_derivatives = ExprMapping() else: cd = [] for k in sorted_expr(coefficient_derivatives.keys()): cd += [as_ufl(k), as_ufl(coefficient_derivatives[k])] coefficient_derivatives = ExprMapping(*cd) # Got a form? Apply derivatives to the integrands in turn. if isinstance(form, Form): integrals = [] for itg in form.integrals(): if not isinstance(coefficient, SpatialCoordinate): fd = CoefficientDerivative(itg.integrand(), coefficients, arguments, coefficient_derivatives) else: fd = CoordinateDerivative(itg.integrand(), coefficients, arguments, coefficient_derivatives) integrals.append(itg.reconstruct(fd)) return Form(integrals) elif isinstance(form, Expr): # What we got was in fact an integrand if not isinstance(coefficient, SpatialCoordinate): return CoefficientDerivative(form, coefficients, arguments, coefficient_derivatives) else: return CoordinateDerivative(form, coefficients, arguments, coefficient_derivatives) error("Invalid argument type %s." % str(type(form)))
def atan_2(f1, f2): "UFL operator: Take the inverse tangent with two the arguments *f1* and *f2*." f1 = as_ufl(f1) f2 = as_ufl(f2) if isinstance(f1, (ComplexValue, complex)) or isinstance(f2, (ComplexValue, complex)): raise TypeError('atan_2 is incompatible with complex numbers.') r = Atan2(f1, f2) if isinstance(r, (RealValue, Zero, int, float)): return float(r) if isinstance(r, (ComplexValue, complex)): return complex(r) return r
def __init__(self, name, classname, nu, argument): Operator.__init__(self) ufl_assert(is_true_ufl_scalar(nu), "Expecting scalar nu.") ufl_assert(is_true_ufl_scalar(argument), "Expecting scalar argument.") fnu = float(nu) inu = int(nu) if fnu == inu: nu = as_ufl(inu) else: nu = as_ufl(fnu) self._classname = classname self._name = name self._nu = nu self._argument = argument
def test_float(self): f1 = as_ufl(1) f2 = as_ufl(1.0) f3 = FloatValue(1) f4 = FloatValue(1.0) f5 = 3 - FloatValue(1) - 1 f6 = 3 * FloatValue(2) / 6 assert f1 == f1 self.assertNotEqual(f1, f2) # IntValue vs FloatValue, == compares representations! assert f2 == f3 assert f2 == f4 assert f2 == f5 assert f2 == f6
def test_int(self): f1 = as_ufl(1) f2 = as_ufl(1.0) f3 = IntValue(1) f4 = IntValue(1.0) f5 = 3 - IntValue(1) - 1 f6 = 3 * IntValue(2) / 6 assert f1 == f1 self.assertNotEqual(f1, f2) # IntValue vs FloatValue, == compares representations! assert f1 == f3 assert f1 == f4 assert f1 == f5 assert f2 == f6 # Division produces a FloatValue
def outer(*operands): "UFL operator: Take the outer product of two or more operands. The complex conjugate of the first argument is taken." n = len(operands) if n == 1: return operands[0] elif n == 2: a, b = operands else: a = outer(*operands[:-1]) b = operands[-1] a = as_ufl(a) b = as_ufl(b) if a.ufl_shape == () and b.ufl_shape == (): return Conj(a) * b return Outer(a, b)
def outer(*operands): "UFL operator: Take the outer product of two or more operands." n = len(operands) if n == 1: return operands[0] elif n == 2: a, b = operands else: a = outer(*operands[:-1]) b = operands[-1] a = as_ufl(a) b = as_ufl(b) if a.shape() == () and b.shape() == (): return a*b return Outer(a, b)
def Dn(f): """UFL operator: Take the directional derivative of *f* in the facet normal direction, Dn(f) := dot(grad(f), n).""" f = as_ufl(f) if is_cellwise_constant(f): return Zero(f.ufl_shape, f.ufl_free_indices, f.ufl_index_dimensions) return dot(grad(f), FacetNormal(f.ufl_domain()))
def test_scalar_sums(self): n = 10 s = [as_ufl(i) for i in range(n)] for i in range(n): self.assertNotEqual(s[i], i+1) for i in range(n): assert s[i] == i for i in range(n): assert 0 + s[i] == i for i in range(n): assert s[i] + 0 == i for i in range(n): assert 0 + s[i] + 0 == i for i in range(n): assert 1 + s[i] - 1 == i assert s[1] + s[1] == 2 assert s[1] + s[2] == 3 assert s[1] + s[2] + s[3] == s[6] assert s[5] - s[2] == 3 assert 1*s[5] == 5 assert 2*s[5] == 10 assert s[6]/3 == 2
def test_complex(self): f1 = as_ufl(1 + 1j) f2 = as_ufl(1) f3 = as_ufl(1j) f4 = ComplexValue(1 + 1j) f5 = ComplexValue(1.0 + 1.0j) f6 = as_ufl(1.0) f7 = as_ufl(1.0j) assert f1 == f1 assert f1 == f4 assert f1 == f5 # ComplexValue uses floats assert f1 == f2 + f3 # Type promotion of IntValue to ComplexValue with arithmetic assert f4 == f2 + f3 assert f5 == f2 + f3 assert f4 == f5 assert f6 + f7 == f2 + f3
def Dn(f): """UFL operator: Take the directional derivative of f in the facet normal direction, Dn(f) := dot(grad(f), n).""" f = as_ufl(f) cell = f.cell() if cell is None: # FIXME: Rather if f.is_cellwise_constant()? return Zero(f.shape(), f.free_indices(), f.index_dimensions()) return dot(grad(f), cell.n)
def _mathfunction(f, cls): f = as_ufl(f) r = cls(f) if isinstance(r, (RealValue, Zero, int, float)): return float(r) if isinstance(r, (ComplexValue, complex)): return complex(r) return r
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 __init__(self, condition, true_value, false_value): Operator.__init__(self) ufl_assert(isinstance(condition, Condition), "Expectiong condition as first argument.") true_value = as_ufl(true_value) false_value = as_ufl(false_value) tsh = true_value.shape() fsh = false_value.shape() ufl_assert(tsh == fsh, "Shape mismatch between conditional branches.") tfi = true_value.free_indices() ffi = false_value.free_indices() ufl_assert(tfi == ffi, "Free index mismatch between conditional branches.") if isinstance(condition, (EQ,NE)): ufl_assert(condition._left.shape() == () and condition._left.free_indices() == () and condition._right.shape() == () and condition._right.free_indices() == (), "Non-scalar == or != is not allowed.") self._condition = condition self._true_value = true_value self._false_value = false_value
def __init__(self, condition, true_value, false_value): if not isinstance(condition, Condition): error("Expectiong condition as first argument.") true_value = as_ufl(true_value) false_value = as_ufl(false_value) tsh = true_value.ufl_shape fsh = false_value.ufl_shape if tsh != fsh: error("Shape mismatch between conditional branches.") tfi = true_value.ufl_free_indices ffi = false_value.ufl_free_indices if tfi != ffi: error("Free index mismatch between conditional branches.") if isinstance(condition, (EQ, NE)): if not all((condition.ufl_operands[0].ufl_shape == (), condition.ufl_operands[0].ufl_free_indices == (), condition.ufl_operands[1].ufl_shape == (), condition.ufl_operands[1].ufl_free_indices == ())): error("Non-scalar == or != is not allowed.") Operator.__init__(self, (condition, true_value, false_value))
def elem_op(op, *args): "UFL operator: Take the elementwise application of operator *op* on scalar values from one or more tensor arguments." args = [as_ufl(arg) for arg in args] sh = args[0].ufl_shape if not all(sh == x.ufl_shape for x in args): error("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 nabla_div(f): """UFL operator: Take the divergence of f. This operator follows the div convention where nabla_div(v) = v[i].dx(i) nabla_div(T)[:] = T[i,:].dx(i) for vector expressions v, and arbitrary rank tensor expressions T. """ f = as_ufl(f) return NablaDiv(f)
def __new__(cls, a, b): a = as_ufl(a) b = as_ufl(b) if not is_true_ufl_scalar(a): error("Cannot take the power of a non-scalar expression.") if not is_true_ufl_scalar(b): error("Cannot raise an expression to a non-scalar power.") if isinstance(a, ScalarValue) and isinstance(b, ScalarValue): return as_ufl(a._value ** b._value) if a == 0 and isinstance(b, ScalarValue): bf = float(b) if bf < 0: error("Division by zero, annot raise 0 to a negative power.") else: return zero() if b == 1: return a if b == 0: return IntValue(1) # construct and initialize a new Power object self = AlgebraOperator.__new__(cls) self._init(a, b) return self
def test_zero(self): z1 = Zero(()) z2 = Zero(()) z3 = as_ufl(0) z4 = as_ufl(0.0) z5 = FloatValue(0) z6 = FloatValue(0.0) # self.assertTrue(z1 is z2) # self.assertTrue(z1 is z3) # self.assertTrue(z1 is z4) # self.assertTrue(z1 is z5) # self.assertTrue(z1 is z6) assert z1 == z1 assert int(z1) == 0 assert float(z1) == 0.0 assert complex(z1) == 0.0 + 0.0j self.assertNotEqual(z1, 1.0) self.assertFalse(z1) # If zero() == 0 is to be allowed, it must not have the same hash or it will collide with 0 as key in dicts... self.assertNotEqual(hash(z1), hash(0.0)) self.assertNotEqual(hash(z1), hash(0))
def __init__(self, name, left, right): Operator.__init__(self) self._name = name self._left = as_ufl(left) self._right = as_ufl(right) 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 ufl_assert(isinstance(self._left, Condition), "Expecting a Condition, not a %s." % self._left._uflclass) ufl_assert(isinstance(self._right, Condition), "Expecting a Condition, not a %s." % self._right._uflclass) else: # Binary operators acting on non-boolean expressions allow only scalars ufl_assert(self._left.shape() == () \ and self._right.shape() == (), "Expecting scalar arguments.") ufl_assert(self._left.free_indices() == () \ and self._right.free_indices() == (), "Expecting scalar arguments.")
def __init__(self, expression, label=None): # Conversion expression = as_ufl(expression) if label is None: label = Label() # Checks if not isinstance(expression, Expr): error("Expecting Expr.") if not isinstance(label, Label): error("Expecting a Label.") if expression.ufl_free_indices: error("Variable cannot wrap an expression with free indices.") Operator.__init__(self, (expression, label))
def test_dolfin_expression_compilation_of_scalar_literal(): # Define some literal ufl expression uexpr = as_ufl(3.14) # Define expected output from compilation expected_lines = ['double s[1];', 's[0] = 3.14;', 'values[0] = s[0];'] # Define expected evaluation values: [(x,value), (x,value), ...] expected_values = [((0.0, 0.0), (3.14,)), ((0.6, 0.7), (3.14,)), ] # Execute all tests check_dolfin_expression_compilation(uexpr, expected_lines, expected_values)
def Dx(f, *i): """UFL operator: Take the partial derivative of f with respect to spatial variable number i. Equivalent to f.dx(\*i).""" f = as_ufl(f) return f.dx(*i)