Source code for ufl.core.external_operator
"""External operator.
This module defines the ``ExternalOperator`` class, which symbolically
represents operators that are not straightforwardly expressible in UFL.
Subclasses of ``ExternalOperator`` must define how this operator should
be evaluated as well as its derivatives from a given set of operands.
"""
# Copyright (C) 2019 Nacime Bouziani
#
# This file is part of UFL (https://www.fenicsproject.org)
#
# SPDX-License-Identifier: LGPL-3.0-or-later
#
# Modified by Nacime Bouziani, 2023
from ufl.core.base_form_operator import BaseFormOperator
from ufl.core.ufl_type import ufl_type
[docs]
@ufl_type(num_ops="varying", is_differential=True)
class ExternalOperator(BaseFormOperator):
"""External operator."""
# Slots are disabled here because they cause trouble in PyDOLFIN
# multiple inheritance pattern:
_ufl_noslots_ = True
def __init__(self, *operands, function_space, derivatives=None, argument_slots=()):
"""Initialise.
Args:
operands: operands on which acts the ExternalOperator.
function_space: the FunctionSpace, or MixedFunctionSpace on
which to build this Function.
derivatives: tuple specifying the derivative multiindex.
argument_slots: tuple composed containing expressions with
ufl.Argument or ufl.Coefficient objects.
argument_slots: TODO
"""
# -- Derivatives -- #
if derivatives is not None:
if not isinstance(derivatives, tuple):
raise TypeError(f"Expecting a tuple for derivatives and not {derivatives}.")
if not len(derivatives) == len(operands):
raise ValueError(f"Expecting a size of {len(operands)} for {derivatives}.")
if not all(isinstance(d, int) for d in derivatives) or any(d < 0 for d in derivatives):
raise ValueError(
"Expecting a derivative multi-index with nonnegative indices, "
f"not {derivatives}."
)
else:
derivatives = (0,) * len(operands)
BaseFormOperator.__init__(
self,
*operands,
function_space=function_space,
derivatives=derivatives,
argument_slots=argument_slots,
)
[docs]
def ufl_element(self):
"""Shortcut to get the finite element of the function space of the external operator."""
# Useful when applying split on an ExternalOperator
return self.arguments()[0].ufl_element()
[docs]
def grad(self):
"""Returns the symbolic grad of the external operator."""
# By default, differential rules produce `grad(assembled_o)`
# `where assembled_o` is the `Coefficient` resulting from
# assembling the external operator since the external operator
# may not be smooth enough for chain rule to hold. Symbolic
# gradient (`grad(ExternalOperator)`) depends on the operator
# considered and its implementation may be needed in some cases
# (e.g. convolution operator).
raise NotImplementedError(
"Symbolic gradient not defined for the external operator considered."
)
[docs]
def assemble(self, *args, **kwargs):
"""Assemble the external operator."""
raise NotImplementedError(
f"Symbolic evaluation of {self._ufl_class_.__name__} not available."
)
def _ufl_expr_reconstruct_(
self, *operands, function_space=None, derivatives=None, argument_slots=None, add_kwargs={}
):
"""Return a new object of the same type with new operands."""
return type(self)(
*operands,
function_space=function_space or self.ufl_function_space(),
derivatives=derivatives or self.derivatives,
argument_slots=argument_slots or self.argument_slots(),
**add_kwargs,
)
def __str__(self):
"""Default str string for ExternalOperator operators."""
d = "\N{PARTIAL DIFFERENTIAL}"
derivatives = self.derivatives
d_ops = "".join(d + "o" + str(i + 1) for i, di in enumerate(derivatives) for j in range(di))
e = "e("
e += ", ".join(str(op) for op in self.ufl_operands)
e += "; "
e += ", ".join(str(arg) for arg in reversed(self.argument_slots()))
e += ")"
return d + e + "/" + d_ops if sum(derivatives) > 0 else e
def __eq__(self, other):
"""Check for equality."""
if self is other:
return True
return (
type(self) is type(other)
and all(a == b for a, b in zip(self.ufl_operands, other.ufl_operands))
and all(a == b for a, b in zip(self._argument_slots, other._argument_slots))
and self.derivatives == other.derivatives
and self.ufl_function_space() == other.ufl_function_space()
)