You are on page 1of 4

Perturbation methods using a fast Chebyshev integrator

object
28/5/2012
0 Comments

One problem with any global spectral methods is that the discretization can usually be
chosen to make the either the stiffness matrix sparse, such as with modal expansion,
or the variable coefficient matrices sparse, such as with pseudospectral methods, but
not both. Even when we have a simple first order problem such as

u+q(x)u=f(x),u(1)=0,x[1,1]
If we would apply a Petrov Galerkin discretization

k=0n[(j,k)+(j,qk)]u^k=(j,f)
Then we could either make K tridiagonal with the choices

j(x)=(1x)Tj(x),k(x)=(1+x)Tk(x)
but then Q would be full for general functions q(x) or
we could choose

j(x)=(xxj),k(x)=k(x)
which would make Q diagonal, but K full.
One possible solution to this problem is instead of solving the
system (K+Q)u^=f^ directly, we could use an iterative method. One possibility
(maybe not the best) is to use a perturbation expansion. We can treat the variable
coefficient as a perturbation since it is a lower order term than the derivative term.

u+qu=f
The solution is now going be a perturbation series in

u=u0+u1+2u2+
If we plug this into the differential equation and compare powers of , we get the
iterates

u0=fu1=qu0u2=qu1uk+1=quk

So we only need to solve the same kind of problem (integration) repeatedly.


This means that choosing the test and trial functions

j(x)=(1x)Tj(x),k(x)=(1+x)Tk(x)

is a good idea since we will just be solving a tridiagonal system many times with
different right hand sides. Furthermore, we can economize by computing the LU
factors of the stiffness matrix K and use those. It turns out that we can find an explicit
formula for the LU factors for this choice of basis.

L=11211111
U=1240213243
So we need to use a banded solver to solve the systems Ax=y

Ly=b,Ux=y
This seems like a good excuse to create an integrator class, so that we can store the
LU factors as member data.
import numpy as np
from scipy.linalg import solve_banded
from chebtran import fcgt, ifcgt
class integrator(object):
def __init__(self,n):
# Store LU factors of K matrix in band form
L0 = np.ones(n)
L1 = -np.hstack((0.5,np.ones(n-1)))
self.Lbands = np.vstack((L0,L1))
U0 = np.hstack((4,np.arange(2,n+1)))*0.5
U1 = np.hstack((0,np.arange(n-1)))*0.5
self.Ubands = np.vstack((U1,U0))
k = np.arange(n)
theta = np.pi*(2*k+1)/(2*n)
self.x = np.cos(theta)
def getx(self):
return self.x
def solve(self,f): # Solves u'=f for u(-1)=0
fhat = ifcgt(f*(1-self.x)) # Compute (psi_j,f)
fhat[0] *= 2
y = solve_banded((1,0),self.Lbands,fhat,\
overwrite_ab=False,overwrite_b=True)
uhat = solve_banded((0,1),self.Ubands,y,\
overwrite_ab=False,overwrite_b=True)
u = fcgt(uhat)*(1+self.x) # u=phi*uhat
return u

Download File

Now we need a function for computing the terms in the perturbation series
import numpy as np
from chebint import integrator
def perturb_solve(qfun,ffun,n,maxit,tol):
K = integrator(n)
x = K.getx()
f = ffun(x); q = qfun(x)
u0 = K.solve(f)
u = u0
U = np.zeros((n,maxit))
U[:,0] = u
for it in xrange(1,maxit):
unew = -K.solve(u0*q)
u += unew
U[:,it] = u
if max(abs(unew))<tol:
break
u0 = unew
U = U[:,:it+1]
return x,U,it
And a demo function to test this out
if __name__ == '__main__':
from matplotlib.pyplot import plot, show, title
def f(x):
return np.exp(np.cos(3*np.pi*x))
def q(x):
return4*np.tanh(15*x)+np.sin(7*np.pi*x)
n = 300
maxit = 30
tol = 1e-8
x,U,it = perturb_solve(q,f,n,maxit,tol)
s = "Solution for the first " + str(it) + " iterations"
plot(x,U)

title(s)
show()

Download File

0 Comments

Your comment will be posted after it is approved.

Leave a Reply.

You might also like