# Source code for qgs.basis.base

```"""
Basis definition module (base class)
====================================

Abstract base classes defining the functions (modes) of the basis of the model and used to configure it.
(see :ref:`files/model/oro_model:Projecting the equations on a set of basis functions`).

Description of the classes
--------------------------

* :class:`Basis`: General base class.
* :class:`SymbolicBasis`: Base class for symbolic functions basis.

Warnings
--------

These are `abstract base class`_, they must be subclassed to create new basis!

.. _abstract base class: https://docs.python.org/3/glossary.html#term-abstract-base-class

"""

# TODO: define setters and init arguments
# TODO: define NumericBasis class

import sys

from abc import ABC
from sympy import symbols, lambdify, diff

[docs]class Basis(ABC):
"""General base class for a basis of functions.

Attributes
----------
functions: list
List of functions of the basis.
"""

def __init__(self):

self.functions = list()

def __getitem__(self, index):
return self.functions[index]

def __repr__(self):
return self.functions.__repr__()

def __str__(self):
return self.functions.__str__()

def __len__(self):
return self.functions.__len__()

def append(self, item):
self.functions.append(item)

[docs]class SymbolicBasis(Basis):
"""General base class for a basis of symbolic functions.

Attributes
----------
substitutions: list(tuple)
List of 2-tuples containing the substitutions to be made with the functions. The 2-tuples contain first
a `Sympy`_  expression and then the value to substitute.

.. _Sympy: https://www.sympy.org/

"""

def __init__(self):

Basis.__init__(self)
self.substitutions = list()

[docs]    def subs_functions(self, extra_subs=None):
"""Return the basis functions with the substitutions stored in the object being applied.

Parameters
----------
extra_subs: list(tuple), optional
List of 2-tuples containing extra substitutions to be made with the functions. The 2-tuples contain first
a `Sympy`_  expression and then the value to substitute.

Returns
-------
list
List of the substituted basis functions
"""

sf = list()

for f in self.functions:
if extra_subs is not None:
ff = f.subs(extra_subs)
else:
ff = f
ff = ff.subs(self.substitutions)
sf.append(ff)

return sf

[docs]    def num_functions(self, extra_subs=None):
"""Return the basis functions with as python callable.

Parameters
----------
extra_subs: list(tuple), optional
List of 2-tuples containing extra substitutions to be made with the functions before transforming them into
python callable. The 2-tuples contain first a `Sympy`_  expression and then the value to substitute.

Returns
-------
list(callable)
List of callable basis functions
"""

x, y = symbols('x y')

nf = list()
sf = self.subs_functions(extra_subs=extra_subs)

for f in sf:
try:
nf.append(lambdify([x, y], f))
except:
tb = sys.exc_info()[2]
raise Exception.with_traceback(tb)

return nf

[docs]    def derivative(self, symbol, order=1):
"""Return the basis functions differentiated with respect to `symbol` as a new basis.

Parameters
----------
symbol: Sympy symbol
The symbol with respect to which the basis is to be differentiated.
order: int, optional
The order of the derivative. Default to first order.

Returns
-------
SymbolicBasis:
A new basis object with the differentiated basis function.
"""

dfunc = list(map(lambda func: diff(func, symbol, order), self.functions))
dbasis = SymbolicBasis()
dbasis.functions = dfunc
dbasis.substitutions = self.substitutions

return dbasis

[docs]    def x_derivative(self, order=1):
"""Return the basis functions differentiated with respect to the :math:`x` coordinate.

Parameters
----------
order: int, optional
The order of the derivative. Default to first order.

Returns
-------
SymbolicBasis:
A new basis object with the differentiated basis function.
"""
x = symbols('x')
return self.derivative(x, order)

[docs]    def y_derivative(self, order=1):
"""SymbolicBasis: Basis functions differentiated with respect to the :math:`y` coordinate.

Parameters
----------
order: int, optional
The order of the derivative. Default to first order.

Returns
-------
SymbolicBasis:
A new basis object with the differentiated basis function.
"""
y = symbols('y')
return self.derivative(y, order)

# Rem: Class not used currently in the model.
[docs]class NumericBasis(Basis):
"""General base class for a basis of numeric functions.

"""

def __init__(self):

Basis.__init__(self)

[docs]    def num_functions(self):
"""Return the basis functions with as python callable.

Returns
-------
list(callable)
List of callable basis functions
"""

return self.functions

if __name__=="__main__":
from sympy import symbols, sin, exp

basis = SymbolicBasis()

x, y = symbols('x y')  # x and y coordinates on the model's spatial domain
n, al = symbols('n al')  # aspect ratio and alpha coefficients
for i in range(1, 3):
for j in range(1, 3):
basis.append(2 * exp(- al * x) * sin(j * n * x / 2) * sin(i * y))

basis.substitutions.append(('n', 1.))
basis.substitutions.append(('al', 1.))

```