コード例 #1
0
    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
コード例 #2
0
 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
コード例 #3
0
 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
コード例 #4
0
 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
コード例 #5
0
 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)
コード例 #6
0
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)
コード例 #7
0
    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
コード例 #8
0
 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
コード例 #9
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
コード例 #10
0
    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)
コード例 #11
0
 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
コード例 #12
0
 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)
コード例 #13
0
 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)
コード例 #14
0
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)
コード例 #15
0
    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
コード例 #16
0
    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)
コード例 #17
0
    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)
コード例 #18
0
    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)
コード例 #19
0
 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)
コード例 #20
0
 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())
コード例 #21
0
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)
コード例 #22
0
    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
コード例 #23
0
 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)
コード例 #24
0
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)
コード例 #25
0
ファイル: form.py プロジェクト: maciekswat/dolfin_python_deps
 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
コード例 #26
0
 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)
コード例 #27
0
 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.")
コード例 #28
0
    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
コード例 #29
0
ファイル: form.py プロジェクト: maciekswat/dolfin_python_deps
 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
コード例 #30
0
 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)
コード例 #31
0
 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.")
コード例 #32
0
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]
コード例 #33
0
    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())
コード例 #34
0
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
コード例 #35
0
 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)
コード例 #36
0
 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)
コード例 #37
0
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
コード例 #38
0
    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
コード例 #39
0
 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)
コード例 #40
0
 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)
コード例 #41
0
    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)
コード例 #42
0
    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.")
コード例 #43
0
 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)
コード例 #44
0
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
コード例 #45
0
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
コード例 #46
0
    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)
コード例 #47
0
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
コード例 #48
0
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))
コード例 #49
0
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)