In [1]:
import numpy
from matplotlib import pyplot
from matplotlib import colors
% matplotlib inline
In [2]:
import os, sys
sys.path.append(os.path.split(os.path.split(os.getcwd())[0])[0])
In [3]:
import utils.quadrature as quad
import utils.elems.one_d as elem1d
In [4]:
def f1(x):
"""f1 = x^4"""
return numpy.power(x, 4)
def f2(x):
"""fx = x^5"""
return numpy.power(x, 5)
In [5]:
Q = 7
qd1 = quad.GaussLobattoJacobi(Q)
qd2 = qd1 # warning: qd2 actually points to qd1
print("The exact solution is: {0}".format(0.))
print("The numerical results is: {0}".format(qd1(f1) * qd2(f2)))
For quadrilateral tensorial expansion $\phi_{pq}(\xi_1, \xi_2)=\phi_p^a(\xi_1)\phi_q^a(\xi_2)$, construct the mass matrix with the same order $P$ in both $\xi_1$ and $\xi_2$ directions $$ M[i][j] = M[pq2i(p, q)][pg2i(r, s)] = \int_{-1}^{1} \int_{-1}^{1}\phi_{pq}(\xi_1, \xi_2)\phi_{rs}(\xi_1, \xi_2) d\xi_1 d\xi_2 $$ where the mapping function $pq2i(p, q) = i$ maps the tensorial representation $(p,\ q)$ to the single-value index $i$.
The above integral can be furthermore decomposed into $$ \int_{-1}^{1} \int_{-1}^{1}\phi_{pq}(\xi_1, \xi_2)\phi_{rs}(\xi_1, \xi_2) d\xi_1 d\xi_2 = \\ \int_{-1}^{1} \int_{-1}^{1}\phi_p^a(\xi_1)\phi_q^a(\xi_2)\phi_r^a(\xi_1)\phi_s^a(\xi_2) d\xi_1 d\xi_2 = \int_{-1}^{1} \phi_p^a(\xi_1) \phi_r^a(\xi_1) d\xi_1 \times \int_{-1}^{1} \phi_q^a(\xi_2) \phi_s^a(\xi_2) d\xi_2 $$
Before we develop a 2D standard quadrilateral element in the utils package, we can here use standard 1D element in the utils package to handle the above calculation. That is to say, $$ M[i][j] = M[pq2i(p, q)][pg2i(r, s)] = \\ \int_{-1}^{1} \phi_p^a(\xi_1) \phi_r^a(\xi_1) d\xi_1 \times \int_{-1}^{1} \phi_q^a(\xi_2) \phi_s^a(\xi_2) d\xi_2 = M_{1D}[p][r] \times M_{1D}[q][s] $$
We first investigate the sequential numbering of the mapping: $pq2i(p, q) = p \times (P+1) + q$.
In [6]:
def create_i2pq(P):
"""create sequential mapping of i2pg[i]=[p, q]"""
N = (P + 1)
N *= N
ans = numpy.zeros((N, 2), dtype=numpy.int)
for i in range(N):
ans[i] = [int(i/(P+1)), i%(P+1)]
return ans
In [7]:
def ex_b(P):
"""the wrapper for exercise (b)"""
N = (P + 1) * (P + 1)
i2pq = create_i2pq(P)
e = elem1d.CommonJacobiElem([-1, 1], P+1)
M = numpy.matrix(numpy.zeros((N, N), dtype=numpy.float64))
Mp = numpy.zeros_like(M, dtype=numpy.int) # for plotting matrix structure
for i in range(N):
for j in range(N):
p, q = i2pq[i]
r, s = i2pq[j]
M[i, j] = e.M[p, r] * e.M[q, s]
if M[i, j] != 0:
Mp[i, j] = 1
return Mp
In [8]:
P = 7
Mp = ex_b(P)
pyplot.matshow(Mp, cmap=colors.ListedColormap(['white', 'black']));
If we use decomposed numbering scheme, i.e., number the boundary modes first, the matrix will look like:
In [9]:
def create_i2pq(P):
"""create sequential mapping of i2pg[i]=[p, q]"""
N = (P + 1)
N *= N
ans = numpy.zeros((N, 2), dtype=numpy.int)
ans[0:4] = [[0, 0], [0, P], [P, 0], [P, P]]
ans[4:4+P-1] = [[i, 0] for i in range(1, P)]
ans[4+P-1:4+2*P-2] = [[i, P] for i in range(1, P)]
ans[4+2*P-2:4+3*P-3] = [[0, i] for i in range(1, P)]
ans[4+3*P-3:4+4*P-4] = [[P, i] for i in range(1, P)]
ans[4+4*P-4:] = [[i, j] for i in range(1, P) for j in range(1, P)]
return ans
In [10]:
Mp = ex_b(P)
pyplot.matshow(Mp, cmap=colors.ListedColormap(['white', 'black']));
To mimic the structure mass matrix of standard triangle (Fig. 3.18), we can delete the rows and columns involving the edges of $(\overline{0,P)(P,P)}$. Then the matrix structure should be similar to the Fig. 3.18.
In [11]:
Mp = numpy.delete(Mp, numpy.s_[4+P-1:4+2*P-2], 0)
Mp = numpy.delete(Mp, numpy.s_[4+P-1:4+2*P-2], 1)
pyplot.matshow(Mp, cmap=colors.ListedColormap(['white', 'black']));
The same as the exercise (c) except that now use pure Legendre polynomial expansion, instead of p-type expansion. That is to say, $\phi_p^a(\xi)=\tilde{\psi_p^a}(\xi)=L_{p}^{0,0}(\xi)$
In [12]:
def ex_c(P):
"""the wrapper for exercise (b)"""
N = (P + 1) * (P + 1)
i2pq = create_i2pq(P)
e = elem1d.PureLegendreElem([-1, 1], P+1)
M = numpy.matrix(numpy.zeros((N, N), dtype=numpy.float64))
Mp = numpy.zeros_like(M, dtype=numpy.int) # for plotting matrix structure
for i in range(N):
for j in range(N):
p, q = i2pq[i]
r, s = i2pq[j]
M[i, j] = e.M[p, r] * e.M[q, s]
if M[i, j] != 0:
Mp[i, j] = 1
return Mp
First, the sequential numbering:
In [13]:
def create_i2pq(P):
"""create sequential mapping of i2pg[i]=[p, q]"""
N = (P + 1)
N *= N
ans = numpy.zeros((N, 2), dtype=numpy.int)
for i in range(N):
ans[i] = [int(i/(P+1)), i%(P+1)]
return ans
In [14]:
P = 7
Mp = ex_c(P)
pyplot.matshow(Mp, cmap=colors.ListedColormap(['white', 'black']));
Next, the decomposed numbering:
In [15]:
def create_i2pq(P):
"""create sequential mapping of i2pg[i]=[p, q]"""
N = (P + 1)
N *= N
ans = numpy.zeros((N, 2), dtype=numpy.int)
ans[0:4] = [[0, 0], [0, P], [P, 0], [P, P]]
ans[4:4+P-1] = [[i, 0] for i in range(1, P)]
ans[4+P-1:4+2*P-2] = [[i, P] for i in range(1, P)]
ans[4+2*P-2:4+3*P-3] = [[0, i] for i in range(1, P)]
ans[4+3*P-3:4+4*P-4] = [[P, i] for i in range(1, P)]
ans[4+4*P-4:] = [[i, j] for i in range(1, P) for j in range(1, P)]
return ans
In [16]:
P = 7
Mp = ex_c(P)
pyplot.matshow(Mp, cmap=colors.ListedColormap(['white', 'black']));
Nodal expansions are not in my interest, so I currently skip this part.