Source code for ufl.adjoint

"""This module defines the Adjoint class."""

# Copyright (C) 2021 India Marsden
#
# This file is part of UFL (https://www.fenicsproject.org)
#
# SPDX-License-Identifier:    LGPL-3.0-or-later
#
# Modified by Nacime Bouziani, 2021-2022.

from ufl.argument import Coargument
from ufl.core.ufl_type import ufl_type
from ufl.form import BaseForm, FormSum, ZeroBaseForm

# --- The Adjoint class represents the adjoint of a numerical object that
#     needs to be computed at assembly time ---


[docs]@ufl_type() class Adjoint(BaseForm): """UFL base form type: represents the adjoint of an object. Adjoint objects will result when the adjoint of an assembled object (e.g. a Matrix) is taken. This delays the evaluation of the adjoint until assembly occurs. """ __slots__ = ( "_form", "_repr", "_arguments", "_coefficients", "_domains", "ufl_operands", "_hash", ) def __new__(cls, *args, **kw): """Create a new Adjoint.""" form = args[0] # Check trivial case: This is not a ufl.Zero but a ZeroBaseForm! if form == 0: # Swap the arguments return ZeroBaseForm(form.arguments()[::-1]) if isinstance(form, Adjoint): return form._form elif isinstance(form, FormSum): # Adjoint distributes over sums return FormSum(*[(Adjoint(component), 1) for component in form.components()]) elif isinstance(form, Coargument): # The adjoint of a coargument `c: V* -> V*` is the identity # matrix mapping from V to V (i.e. V x V* -> R). # Equivalently, the adjoint of `c` is its first argument, # which is a ufl.Argument defined on the primal space of # `c`. primal_arg, _ = form.arguments() # Returning the primal argument avoids explicit argument # reconstruction, making it a robust strategy for handling # subclasses of `ufl.Coargument`. return primal_arg return super(Adjoint, cls).__new__(cls) def __init__(self, form): """Initialise.""" BaseForm.__init__(self) if len(form.arguments()) != 2: raise ValueError("Can only take Adjoint of a 2-form.") self._form = form self.ufl_operands = (self._form,) self._domains = None self._hash = None self._repr = "Adjoint(%s)" % repr(self._form)
[docs] def ufl_function_spaces(self): """Get the tuple of function spaces of the underlying form.""" return self._form.ufl_function_spaces()
[docs] def form(self): """Return the form.""" return self._form
def _analyze_form_arguments(self): """The arguments of adjoint are the reverse of the form arguments.""" self._arguments = self._form.arguments()[::-1] self._coefficients = self._form.coefficients() def _analyze_domains(self): """Analyze which domains can be found in Adjoint.""" from ufl.domain import join_domains # Collect unique domains self._domains = join_domains([e.ufl_domain() for e in self.ufl_operands])
[docs] def equals(self, other): """Check if two Adjoints are equal.""" if type(other) is not Adjoint: return False if self is other: return True # Make sure we are returning a boolean as the equality can # result in a `ufl.Equation` if the underlying objects are # `ufl.BaseForm`. return bool(self._form == other._form)
def __str__(self): """Format as a string.""" return f"Adjoint({self._form})" def __repr__(self): """Representation.""" return self._repr def __hash__(self): """Hash.""" if self._hash is None: self._hash = hash(("Adjoint", hash(self._form))) return self._hash