Source code for ufl.algorithms.coordinate_derivative_helpers

"""Tools to strip away and reattach coordinate derivatives.

This is used in compute_form_data.
"""

# Copyright (C) 2018 Florian Wechsung
#
# This file is part of UFL (https://www.fenicsproject.org)
#
# SPDX-License-Identifier:    LGPL-3.0-or-later

from ufl.classes import Integral
from ufl.corealg.map_dag import map_expr_dags
from ufl.corealg.multifunction import MultiFunction
from ufl.differentiation import CoordinateDerivative


[docs]class CoordinateDerivativeIsOutermostChecker(MultiFunction): """Traverses the tree to make sure that CoordinateDerivatives are only on the outside. The visitor returns False as long as no CoordinateDerivative has been seen. """
[docs] def multi_index(self, o): """Apply to multi_index.""" return False
[docs] def terminal(self, o): """Apply to terminal.""" return False
[docs] def expr(self, o, *operands): """Apply to expr. If we have already seen a CoordinateDerivative, then no other expressions apart from more CoordinateDerivatives are allowed to wrap around it. """ if any(operands): raise ValueError("CoordinateDerivative(s) must be outermost") return False
[docs] def coordinate_derivative(self, o, expr, *_): """Apply to coordinate derivative.""" return True
[docs]def strip_coordinate_derivatives(integrals): """Strip coordinate derivatives.""" if isinstance(integrals, list): if len(integrals) == 0: return integrals, None stripped_integrals_and_cds = [] for integral in integrals: (si, cd) = strip_coordinate_derivatives(integral) stripped_integrals_and_cds.append((si, cd)) return stripped_integrals_and_cds elif isinstance(integrals, Integral): integral = integrals integrand = integral.integrand() checker = CoordinateDerivativeIsOutermostChecker() map_expr_dags(checker, [integrand]) coordinate_derivatives = [] def take_top_coordinate_derivatives(o): """Get all coordinate derivatives and store them. So we can apply them later again. """ o_ = o.ufl_operands if isinstance(o, CoordinateDerivative): coordinate_derivatives.append((o_[1], o_[2], o_[3])) return take_top_coordinate_derivatives(o_[0]) else: return o newintegrand = take_top_coordinate_derivatives(integrand) return (integral.reconstruct(integrand=newintegrand), coordinate_derivatives) else: raise ValueError(f"Invalid type {integrals.__class__.__name__}")
[docs]def attach_coordinate_derivatives(integral, coordinate_derivatives): """Attach coordinate derivatives.""" if coordinate_derivatives is None: return integral if isinstance(integral, Integral): integrand = integral.integrand() # apply the stored coordinate derivatives back onto the integrand for tup in reversed(coordinate_derivatives): integrand = CoordinateDerivative(integrand, tup[0], tup[1], tup[2]) return integral.reconstruct(integrand=integrand) else: raise ValueError(f"Invalid type {integral.__class__.__name__}")