Source code for qgs.inner_products.definition


"""
    Inner products definition module
    ================================

    Module containing classes to define the `inner products`_ used by the model.

    .. _inner products: https://en.wikipedia.org/wiki/Inner_product_space
    
"""

from abc import ABC, abstractmethod

# from sympy.simplify import trigsimp
from sympy.simplify.fu import TR8, TR10
from sympy import diff, integrate, symbols, pi, Integral

_n = symbols('n', real=True, nonnegative=True)
_x, _y = symbols('x y')


[docs]class InnerProductDefinition(ABC): """Base class to define the model's basis inner products. Parameters ---------- optimizer: None or callable, optional A function to optimize the computation of the integrals or the integrand. If `None`, does not optimize. Attributes ---------- optimizer: None or callable A function to optimize the computation of the integrals or the integrand. If `None`, does not optimize the computation. """ def __init__(self, optimizer=None): self.optimizer = None if optimizer is not None: self.set_optimizer(optimizer) else: self.set_optimizer(self._no_optimizer)
[docs] def set_optimizer(self, optimizer): """Function to set the optimizer. Parameters ---------- optimizer: callable A function to optimize the computation of the integrals or the integrand. """ self.optimizer = optimizer
@staticmethod def _no_optimizer(expr): return expr
[docs] @staticmethod @abstractmethod def jacobian(S, G): """Jacobian present in the advection terms: Parameters ---------- S: 1st argument the Jacobian. G: 2nd argument the Jacobian. """ pass
[docs] @staticmethod @abstractmethod def laplacian(S): """Laplacian :math:`\\nabla^2 S` of a function :math:`S`.""" pass
[docs] @abstractmethod def ip_lap(self, S, G, symbolic_expr=False): """Function to compute the inner product :math:`(S, \\nabla^2 G)`. Parameters ---------- S: Left-hand side function of the product. G: Right-hand side function of the product. symbolic_expr: bool, optional If `True`, return the integral as a symbolic expression object. Else, return the integral performed symbolically. """ pass
[docs] @abstractmethod def ip_diff_x(self, S, G, symbolic_expr=False): """Function to compute the inner product :math:`(S, \partial_x G)`. Parameters ---------- S: Left-hand side function of the product. G: Right-hand side function of the product. symbolic_expr: bool, optional If `True`, return the integral as a symbolic expression object. Else, return the integral performed symbolically. """ pass
[docs] @abstractmethod def ip_jac(self, S, G, H, symbolic_expr=False): """Function to compute the inner product :math:`(S, J(G, H))`, where :math:`J` is the :meth:`jacobian`. Parameters ---------- S: Left-hand side function of the product. G: 1st argument of the right-hand side function of the product. H: 2nd argument of the right-hand side function of the product. symbolic_expr: bool, optional If `True`, return the integral as a symbolic expression object. Else, return the integral performed symbolically. """ pass
[docs] @abstractmethod def ip_jac_lap(self, S, G, H, symbolic_expr=False): """Function to compute the inner product :math:`(S, J(G, \\nabla^2 H))`, where :math:`J` is the :meth:`jacobian`. Parameters ---------- S: Left-hand side function of the product. G: 1st argument of the right-hand side function of the product. H: 2nd argument of the right-hand side function of the product. symbolic_expr: bool, optional If `True`, return the integral as a symbolic expression object. Else, return the integral performed symbolically. """ pass
[docs]class SymbolicInnerProductDefinition(InnerProductDefinition): """Base class to define symbolic inner products using `Sympy`_. Parameters ---------- optimizer: None or callable, optional A function to simplify the integrand to optimize the computation of the integrals. Should return a `Sympy`_ expression. If `None`, does not optimize the computation. Attributes ---------- optimizer: None or callable A function to simplify the integrand to optimize the computation of the integrals. Should return a `Sympy`_ expression. If `None`, does not optimize the computation. .. _Sympy: https://www.sympy.org/ """ def __init__(self, optimizer=None): InnerProductDefinition.__init__(self, optimizer)
[docs] @abstractmethod def symbolic_inner_product(self, S, G, symbolic_expr=False, integrand=False): """Symbolic definition of the inner product :math:`(S, G)`. Parameters ---------- S: Sympy expression Left-hand side function of the product. G: Sympy expression Right-hand side function of the product. symbolic_expr: bool, optional If `True`, return the integral as a symbolic expression object. Else, return the integral performed symbolically. integrand: bool, optional If `True`, return the integrand of the integral and its integration limits as a list of symbolic expression object. Else, return the integral performed symbolically. Returns ------- Sympy expression The symbolic result of the inner product. """ pass
[docs] def ip_lap(self, S, G, symbolic_expr=False, integrand=False): """Function to compute the inner product :math:`(S, \\nabla^2 G)`. Parameters ---------- S: Sympy expression Left-hand side function of the product. G: Sympy expression Right-hand side function of the product. symbolic_expr: bool, optional If `True`, return the integral as a symbolic expression object. Else, return the integral performed symbolically. integrand: bool, optional If `True`, return the integrand of the integral and its integration limits as a list of symbolic expression object. Else, return the integral performed symbolically. Returns ------- Sympy expression The symbolic result of the inner product. """ return self.symbolic_inner_product(S, self.laplacian(G), symbolic_expr=symbolic_expr, integrand=integrand)
[docs] def ip_diff_x(self, S, G, symbolic_expr=False, integrand=False): """Function to compute the inner product :math:`(S, \partial_x G)`. Parameters ---------- S: Sympy expression Left-hand side function of the product. G: Sympy expression Right-hand side function of the product. symbolic_expr: bool, optional If `True`, return the integral as a symbolic expression object. Else, return the integral performed symbolically. integrand: bool, optional If `True`, return the integrand of the integral and its integration limits as a list of symbolic expression object. Else, return the integral performed symbolically. Returns ------- Sympy expression The symbolic result of the inner product. """ return self.symbolic_inner_product(S, diff(G, _x), symbolic_expr=symbolic_expr, integrand=integrand)
[docs] def ip_jac(self, S, G, H, symbolic_expr=False, integrand=False): """Function to compute the inner product :math:`(S, J(G, H))`, where :math:`J` is the :meth:`jacobian`. Parameters ---------- S: Sympy expression Left-hand side function of the product. G: Sympy expression 1st argument of the right-hand side function of the product. H: Sympy expression 2nd argument of the right-hand side function of the product. symbolic_expr: bool, optional If `True`, return the integral as a symbolic expression object. Else, return the integral performed symbolically. integrand: bool, optional If `True`, return the integrand of the integral and its integration limits as a list of symbolic expression object. Else, return the integral performed symbolically. Returns ------- Sympy expression The symbolic result of the inner product. """ return self.symbolic_inner_product(S, self.jacobian(G, H), symbolic_expr=symbolic_expr, integrand=integrand)
[docs] def ip_jac_lap(self, S, G, H, symbolic_expr=False, integrand=False): """Function to compute the inner product :math:`(S, J(G, \\nabla^2 H))`, where :math:`J` is the :meth:`jacobian`. Parameters ---------- S: Sympy expression Left-hand side function of the product. G: Sympy expression 1st argument of the right-hand side function of the product. H: Sympy expression 2nd argument of the right-hand side function of the product. symbolic_expr: bool, optional If `True`, return the integral as a symbolic expression object. Else, return the integral performed symbolically. integrand: bool, optional If `True`, return the integrand of the integral and its integration limits as a list of symbolic expression object. Else, return the integral performed symbolically. Returns ------- Sympy expression The symbolic result of the inner product. """ return self.symbolic_inner_product(S, self.jacobian(G, self.laplacian(H)), symbolic_expr=symbolic_expr, integrand=integrand)
[docs]class StandardSymbolicInnerProductDefinition(SymbolicInnerProductDefinition): """Standard `qgs` class to define symbolic inner products using `Sympy`_. Parameters ---------- optimizer: None or callable, optional A function to simplify the integrand to optimize the computation of the integrals. Should return a `Sympy`_ expression. If `None`, does not optimize the computation. Attributes ---------- optimizer: None or callable A function to simplify the integrand to optimize the computation of the integrals. Should return a `Sympy`_ expression. If `None`, does not optimize the computation. .. _Sympy: https://www.sympy.org/ """ def __init__(self, optimizer=None): if optimizer is None: SymbolicInnerProductDefinition.__init__(self, self._trig_optimizer) else: SymbolicInnerProductDefinition.__init__(self, optimizer) @staticmethod def _trig_optimizer(expr): return TR10(TR8(expr))
[docs] @staticmethod def jacobian(S, G): """Jacobian present in the advection terms: .. math: J(S, G) = \partial_x S\, \partial_y G - \partial_y S\, \partial_x G Parameters ---------- S: Sympy expression 1st argument the Jacobian. G: Sympy expression 2nd argument the Jacobian. Returns ------- Sympy expression The Jacobian. """ return diff(S, _x) * diff(G, _y) - diff(G, _x) * diff(S, _y)
[docs] @staticmethod def laplacian(S): """Laplacian :math:`\\nabla^2 S` of a given function :math:`S` in 2D cartesian coordinates: :math:`\\nabla^2 S = \\partial^2_x S + \\partial^2_y S`. Parameters ---------- S: Sympy expression Functions to take the Laplacian of. Returns ------- Sympy expression The Laplacian. """ return diff(S, _x, 2) + diff(S, _y, 2)
[docs] @staticmethod def integrate_over_domain(expr, symbolic_expr=False): """Definition of the normalized integrals over the spatial domain used by the inner products: :math:`\\frac{n}{2\\pi^2}\\int_0^\\pi\\int_0^{2\\pi/n} \, \\mathrm{expr}(x, y) \, \\mathrm{d} x \, \\mathrm{d} y`. Parameters ---------- expr: Sympy expression The expression to integrate. symbolic_expr: bool, optional If `True`, return the integral as a symbolic expression object. Else, return the integral performed symbolically. Returns ------- Sympy expression The result of the symbolic integration. """ if symbolic_expr: return Integral(expr, (_x, 0, 2 * pi / _n), (_y, 0, pi)) else: return integrate(expr, (_x, 0, 2 * pi / _n), (_y, 0, pi))
[docs] def symbolic_inner_product(self, S, G, symbolic_expr=False, integrand=False): """Function defining the inner product to be computed symbolically: :math:`(S, G) = \\frac{n}{2\\pi^2}\\int_0^\\pi\\int_0^{2\\pi/n} S(x,y)\, G(x,y)\, \\mathrm{d} x \, \\mathrm{d} y`. Parameters ---------- S: Sympy expression Left-hand side function of the product. G: Sympy expression Right-hand side function of the product. symbolic_expr: bool, optional If `True`, return the integral as a symbolic expression object. Else, return the integral performed symbolically. integrand: bool, optional If `True`, return the integrand of the integral and its integration limits as a list of symbolic expression object. Else, return the integral performed symbolically. Returns ------- Sympy expression The result of the symbolic integration """ expr = (_n / (2 * pi ** 2)) * S * G if integrand: return expr, (_x, 0, 2 * pi / _n), (_y, 0, pi) else: return self.integrate_over_domain(self.optimizer(expr), symbolic_expr=symbolic_expr)