Peano
Loading...
Searching...
No Matches
nodal_basis.py
Go to the documentation of this file.
1from abc import ABC, abstractmethod
2import itertools
3import numpy as np
4from scipy.interpolate import lagrange
5from scipy.special import legendre
6from numpy.polynomial.legendre import leggauss
7from functools import lru_cache
8
9class NodalBasis(ABC):
10 """Abstract base class for nodal basis functions"""
11
12 def __init__(self, dim):
13 """Initialise a new instance
14 :arg dim: dimension
15 """
16 self.dim = dim
17
18 @abstractmethod
19 def node1d(self, degree, j):
20 """Return the j-th nodal point of a Lagrange polynomial of given degree
21 :arg degree: polynomial degree
22 :arg j: index of node
23 """
24
25
26class NodalBasisGaussLegendre(NodalBasis):
27 """Concrete implementation for Gauss Legendre nodes"""
28
29 def get_Gauss_Lobatto_nodes(self, poly_degree):
30 """
31 Return coordinates of Gauss-Lobatto nodes in [-1, 1] for given polynomial degree.
32 Roots of deriv(P_{n-1}), where P_{n-1} is the Legendre polynomial and n is the number of nodal points;
33 plus endpoints of the interval.
34 """
35
36 use_explicit_nodes = False # set to True to use explicit nodes below, otherwise compute using Scipy's legendre
37
38 if poly_degree == 0:
39 return np.asarray([0.0])
40
41 if not use_explicit_nodes:
42 poly = legendre(poly_degree)
43 deriv = poly.deriv()
44 inter_points = np.sort(np.roots(deriv)) # interior nodal points excluding -1 and 1
45 return np.concatenate(([-1.0], inter_points, [1.0]))
46
47 else:
48 if poly_degree == 1:
49 return np.asarray([-1.0, 1.0])
50 elif poly_degree == 2:
51 return np.asarray([-1.0, 0.0, 1.0])
52 elif poly_degree == 3:
53 return np.asarray([-1.0, -0.4472135954999579, 0.4472135954999579, 1.0])
54 elif poly_degree == 4:
55 return np.asarray([-1.0, -0.6546536707079771, 0.0, 0.6546536707079771, 1.0])
56 elif poly_degree == 5:
57 return np.asarray([-1.0, -0.7650553239294647, -0.28523151648064504, 0.28523151648064504, 0.7650553239294647, 1.0])
58 elif poly_degree == 6:
59 return np.asarray([-1.0, -0.8302238962785669, -0.4688487934707142, 0.0, 0.4688487934707142, 0.8302238962785669, 1.0])
60 elif poly_degree == 7:
61 return np.asarray([-1.0, -0.87174014850960668, -0.59170018143314218, -0.20929921790247885, 0.20929921790247885, 0.59170018143314218, 0.87174014850960668, 1.0])
62 else:
63 raise ValueError(f"Add Gauss-Lobatto nodes for polynomial degree {poly_degree} or set use_explicit_nodes to False to compute them")
64
65 def nodal_points(self, degree):
66 """Set nodal points xi_0, xi_1, ..., xi_p"""
67
68 assert degree >= 0, "Negative degree"
69
70 # Equidistant points
71 # return np.linspace(-1.0, 1.0, degree + 1)
72
73 # Gauss-Legendre points
74 # print("Gauss-Legendre points are used")
75 # return leggauss(degree + 1)[0]
76
77 return self.get_Gauss_Lobatto_nodes(degree)
78
79 def node1d(self, degree, j):
80 """Return the j-th nodal point of a Lagrange polynomial of given degree
81 :arg degree: polynomial degree
82 :arg j: index of node
83 """
84 assert j in range(degree+1), f'j must be in range [0, m], m = {degree}'
85 return self.nodal_points(degree)[j]
86
87 @lru_cache(maxsize=None)
88 def lagrange_polynomial(self, degree, k):
89 """Returns k-th basis function L^{(m)}_k(xi) of degree m as a numpy polynomial object
90 :arg degree: polynomial degree
91 :arg k: index of basis function (= index of node)
92 """
93 assert k in range(degree+1), f'k must be in [0, m], m = {degree}'
94
95 x = self.nodal_points(degree)
96 y = np.zeros(degree+1)
97 y[k] = 1.0
98
99 return lagrange(x, y) # scipy.interpolate
100
101 @lru_cache(maxsize=None)
102 def evaluate(self, degree, k, xi):
103 """Evaluate the k-th basis function L^{(m)}_k(xi) of degree m at point xi in [-1,+1],
104 return L^{(m)}_k(xi)
105 :arg degree: polynomial degree
106 :arg k: index of basis function
107 :arg xi: position xi
108 """
109 # assert -1.0 <= xi.all() <= 1.0, f'xi must be in [-1,+1]'
110 poly = self.lagrange_polynomial(degree, k)
111 return poly(xi)
112
113 @lru_cache(maxsize=None)
114 def evaluate_derivative(self, degree, k, xi):
115 """Evaluate the derivative of the k-basis function L^{(m)}_k(xi) of degree m at point xi in [-1,+1]
116 return dL^{(m)}_k(xi)/dxi
117 :arg degree: polynomial degree
118 :arg k: index of basis function
119 :arg xi: position xi
120 """
121 # assert -1.0 <= xi.all() <= 1.0, f'xi must be in [-1,+1]'
122
123 poly = self.lagrange_polynomial(degree, k)
124 deriv = np.polyder(poly)
125 return deriv(xi)
126
128 """Implement the below code in a method"""
129 # ### Plot basis funtions ###
130 # import matplotlib.pyplot as plt
131 # import matplotlib
132 # matplotlib.use('TkAgg')
133
134 # m = 1
135 # L = Basis1d(m)
136 # x = np.arange(-1.0, 1.0, 0.01)
137
138 # for i in range(m+1):
139 # plt.scatter(x, L.evaluate(i, x), label = f'L_{i}')
140 # #plt.scatter(x, L.evaluate_derivative(i, x), label = f'L_{i}')
141
142 # plt.legend()
143 # plt.show()
144 # pass
145 # ############################
evaluate_derivative(self, degree, k, xi)
Evaluate the derivative of the k-basis function L^{(m)}_k(xi) of degree m at point xi in [-1,...
evaluate(self, degree, k, xi)
Evaluate the k-th basis function L^{(m)}_k(xi) of degree m at point xi in [-1,+1],...
node1d(self, degree, j)
Return the j-th nodal point of a Lagrange polynomial of given degree :arg degree: polynomial degree :...
nodal_points(self, degree)
Set nodal points xi_0, xi_1, ..., xi_p.
lagrange_polynomial(self, degree, k)
Returns k-th basis function L^{(m)}_k(xi) of degree m as a numpy polynomial object :arg degree: polyn...
get_Gauss_Lobatto_nodes(self, poly_degree)
Return coordinates of Gauss-Lobatto nodes in [-1, 1] for given polynomial degree.
Abstract base class for nodal basis functions.
Definition nodal_basis.py:9
node1d(self, degree, j)
Return the j-th nodal point of a Lagrange polynomial of given degree :arg degree: polynomial degree :...
__init__(self, dim)
Initialise a new instance :arg dim: dimension.
dim
Initialise a new instance :arg dim: dimension.