Spectral Methods#
Our general goal is to find an efficient basis for representing our functions:
The idea is to then choose the coefficients \(a_n\) so that \(f(x)\) is as close as possible to the solution we desire – e.g., solving a differential equation, or minimizing an energy functional.
Some Terminology
The term spectral method applies to expansions such as those above where the functions \(\phi_{n}(x)\) are generally analytic – often polynomials. If the basis functions have compact support, then such an expansion this is referred to as a finite-element method (FEM). The FEM approach is often better for systems with discontinuities, irregular boundaries, and sharp features like shockwaves, but lacks the exponential convergence enjoyed by spectral methods for analytic functions.
The term pseudo-spectral method, also known as a discrete variable representation (DVR) methods, associates a quadrature grid with the basis functions, and reorganize the basis into a set of cardinal functions which vanish on all but one of the grid points. These are the representation of the Dirac delta function in the basis, and greatly facilitate the expression of problems.
Literature (How I learned about this.)
The Wikipedia entry about spectral methods provides a good overview of the relationship between spectral methods, finite element methods, pseudo-spectral methods (DVR) and other related methods.
The following two textbooks provide a fairly comprehensive starting point for learning about spectral methods.
[Boyd, 1989] – “Chebyshev and Fourier Spectral Methods”: This is an excellent resource, although the writing is quite idiosyncratic. Start with the introduction to get a very hands-on and practical introduction. The rest of the book is quite difficult to read, but as you learn more, keep coming back. There is a lot of practical wisdom here, and many references to real-world applications.
[Trefethen, 2000] – “Spectral Methods in Matlab”: This is a little easier to read than [Boyd, 1989], with many concrete examples, but is not as thorough or deep.
For applications in quantum mechanics, and my first exposure to the idea, see the following papers:
[Baye and Heenen, 1986] – “Generalized meshes for quantum mechanical problems”: Demonstrates the essential features of DVR bases for solving quantum mechanics problems.
[Littlejohn et al., 2002] – “A general framework for discrete variable representation basis sets”: This presents arguments about why DVR bases work so well in terms of phase-space coverage, and discusses many standard sets.
[Littlejohn and Cargo, 2002] – “Bessel discrete variable representation bases”: Go here if you need the formulae and theory for spherically symmetric problems in various dimensions (i.e., cylindrical or spherical geometries).
[Schneider and Nygaard, 2004] – “Discrete variable representation for singular Hamiltonians”: I really enjoyed this presentation. It was the first time I understood the generalized relationship between quadrature and DVR bases, with a clever but simple generalization for dealing with singularities like the Coulomb potential.
Background / Prerequisites#
This section requires the following concepts:
Orthonormal functions as basis vectors: A set of functions \(P_{n}(x)\) are said to be orthonormal with respect to an integration weight \(w(x)\) iff
\[\begin{gather*} \braket{P_{m}|P_{n}} \equiv \int w(x)\d{x}\; P_{m}(x)P_{n}(x) = \delta_{mn}. \end{gather*}\](Gaussian) Quadrature: An \(N\)-point quadrature rule is a set of \(N\) abscissa \(x_{n}\) and weights \(w_{n}\) for \(n \in \{0, 1, \dots, N-1\}\) such that
\[\begin{gather*} \int w(x)\d{x}\; f(x) = \sum_{n=0}^{N-1} w_n f(x_n) \end{gather*}\]is exact for all \(f(x) = P_{n}(x)\) with \(n < N\). The term Gaussian quadrature refers to a set of abscissa and points such that the integration formula is exact for all polynomials of degree \(2N-1\). This is consistent with there being \(2N\) degrees of freedom – the weights and the abscissa – and that there are \(2N\) independent monomials \(x^0\), \(x^1\), … \(x^{2N-1}\).
Fourier Transform and Projections: You should also be familiar with the Fourier transform and projecting functions onto states of low momentum.
Dirac Delta Functions: The most general approach is in terms of \(\delta(x)\).
Paths to a DVR#
TL;DR#
The best exposition I have seen of DVR bases is from [] which expresses the following theorem:
The simplest path to a DVR bases is from a set of \(N\) orthonormal polynomial functions \(P_{n}(x)\) and an \(N\)-point Gaussian quadrature rule – see e.g. [Schneider and Nygaard, 2004]. The more general approach, however, is to consider projected delta-functions [Littlejohn et al., 2002], so we will start here.
As Projected Delta Functions#
A general framework for DVR bases [Littlejohn et al., 2002] starts with a projection operator \(\op{P}\) which projects a Hilbert space onto states of low momentum. The DVR basis is formed from projected delta-functions \(\Delta_{n}(x) = \braket{x|\op{P}|x_n}\) with abscissa \(x_m\) chosen so that they sit in the nodes:
From these, we define the orthonormal basis \(\ket{F_m}\):
Note that this implies the following locality property
Thus, if such a basis and set of abscissa can be found, then for functions that live in the space spanned by the basis, the coefficients of the expansion can be found by simply evaluating the functions at the abscissa:
Example: Sinc-function Basis
Consider for example, 1-D where we have
Here we have the position basis vectors \(\ket{x}\) (denoted by variables \(x\), \(y\), \(z\), etc.) and momentum basis vectors \(\ket{k}\) (denoted by variables \(k\), \(q\), \(p\), etc.) In the position basis, \(\ket{x}\) are Dirac delta-functions, while \(\ket{k}\) are plane waves.
We can now form the projection operator onto states with \(\abs{k} \leq K\):
We use this to form the projected delta-functions:
Let’s start with an abscissa \(x_0 = 0\) so that
where the abscissa are chosen to be the nodes. One can easily verify that locality property is satisfied, and use the fact that \(\op{P}^\dagger \op{P} = \op{P}^2 = \op{P}\) to show that
Note that this says that the projected delta-functions are orthogonal iff they are zero at all other abscissa. Evaluating the non-zero overlap, we find the normalization factors \(K_{m} = \Delta_{m}(x_m) = K/\pi\), and can construct the orthonormal basis states \(\ket{F_n}\):
Do It!: Construct the DVR basis for periodic function \(x\in [0, L)\).
Hint: Note that periodicity \(f(x+L) = f(x)\) requires a specific form of \(k\) for the basis states, so the projection operator becomes a sum rather than an integral. As a check, your results should be a linear combination of the sines and cosines the provide a basis for periodic functions (a.k.a. Fourier series).
To use this basis, one needs to compute the kinetic energy operator (Laplacian). This can be approximated using the locality property, e.g.:
A nice property is that, for many physically cases, the matrix for the potential can be well approximated by a diagonal matrix
See [Littlejohn et al., 2002] and references therein for the conditions under which this approximation is good.
Example: Sinc-function Basis Cont.
These integrals are a bit nasty, but if we are acting on functions well represented in the basis, such that \(\op{\nabla}^2\ket{f}\) is also well represented in the basis, then we can use the locality property:
If we consider \(n \neq m\), then the first term will vanish
When \(n=m\) we must consider all the terms and take the limit using l’Hopital’s rule:
Thus,
It turns out that this is exactly the matrix you would get by computing the overlap integrals.
As a quick check, we consider the spectrum of a Harmonic oscillator with exact energies
If we want 20 states, then we must choose a basis where \(\hbar^2 K^2/2m > \hbar\omega (20.5)\) giving an estimate of \(K > 6.4/a_0\) where \(a_0 = \sqrt{\hbar/m\omega}\) is the oscillator length. We also need to make sure that the basis is large enough in position space. This state will have turning point \(R = \sqrt{2 E_{20}/m\omega^2} \approx 6.4a_0\) which means we need \(n \pi / K > R\). A bit of playing shows that \(Ka_0 = 10\) works well, which requires \(n > 6.4 K/\pi \approx 20\). Again, playing, we find that \(n = 30\) works well. Thus, we can get 20 states to machine precision with about 61 basis vectors.
def get_err_HO(Ka=10.0, N=30):
hbar = m = w = 1
a0 = np.sqrt(hbar/m/w)
K = Ka/a0
n = np.arange(-N, N+1)
x = np.pi * n / Ka
V = np.diag(m*(w*x)**2/2)
n_, m_ = n[None, :], n[:, None]
D2 = K**2 * np.ma.divide(2*(-1.0)**(n_ - m_)/np.pi**2, (m_-n_)**2).filled(1/3)
K = hbar**2/2/m * D2
H = K + V
assert np.allclose(H, H.T)
E = np.linalg.eigvalsh(H)
exact = hbar*w * (np.arange(len(n)) + 0.5)
err = abs(E/exact - 1)
return err
fig, ax = plt.subplots(figsize=(4,3))
ax.semilogy(get_err_HO(Ka=10.0, N=32))
ax.set(xlabel="$n$", ylabel="Energy error (rel)");
From Orthogonal Polynomials#
If the basis functions \(P_n(x)\) are orthogonal polynomials of degree \(n\), then we have an alternative and simpler path to a DVR basis. This section follows the ideas in [Schneider and Nygaard, 2004] but using a consistent notation with the previous section. Start with a basis and \(N\)-point Gaussian quadrature rule accurate for polynomials of degree \(2N-1\):
Specifically, the quadrature rule is valid for \(f(x) = P_{n}^*(x)P_{m}(x)\) where \(n+m < 2N\).
The DVR basis is then
For this to hold, the orthonormality and quadrature rules imply
Hence
Since \(F_{n}(x)\) is a polynomial of degree \(N-1\) with \(N-1\) roots \(\{x_{m\neq n}\}\), we can express it as a Lagrange polynomial:
Some Technical Details#
Let’s consider trying to construct a DVR basis from a general set of \(N\) orthogonal functions \(\ket{P_n}\) and abscissa \(x_n\):
For these to be consistent we need
In matrix notation, we need
Thus, consistency requires that the matrix \(\mat{M}\) can be diagonalized by right-multiplying by a unitary matrix \(\mat{U}\).
This requires that
This means that the abscissa must be chosen so that the rows of \(\mat{M}\). These are the vectors \(P_n(x)\) consisting of the basis functions evaluated at each colocation point \(x=x_{i}\).
It is not clear we can always do this: We have \(N\)-degrees of freedom – the locations of the abscissa \(x_{n}\) – but must satisfy \(N(N-1)/2\) conditions for the upper triangular portion of \(\mat{M}\mat{M}^{\dagger}\) to be zero.
For orthonormal polynomials, this orthogonality follows from the Christoffel–Darboux formula
if we choose the abscissa to be the roots of \(P_{N}(x_n)=0\). Other functions satisfy a similar identity, including the Bessel functions [],
and Airy functions
Basis Functions#
We start with basis functions – typically some set of orthogonal polynomials \(P_n(x)\), but these can be more general, including Fourier bases (sines and cosines). Most convenient are functions orthogonal with respect to some integration weight \(w(x)\):
The Bessel function basis is suitable for the radial coordinate in cylindrical and spherical geometries.
close relationship with Fourier series gives rise to many interesting properties of this basis.
The Fourier basis consists of plane waves, orthogonal on a periodic interval:
The Chebyshev basis is a transformed cosine basis to the interval \(x\in [-1, 1]\). The close relationship with Fourier series gives rise to many interesting properties of this basis.
The idea is to express your functions in terms of this basis:
Then the properties of the basis functions – such as their derivatives – can be used to calculate properties of \(f(x)\).
Do It! Use a Fourier Basis to do the first example in [Boyd, 1989].
In §1.2, [Boyd, 1989] solves the following equation using a low-order polynomial expansion:
Read the solution there, then repeat that analysis using a Fourier basis using everything you learn, including appropriately chosen collocation points, and considering the discussion about \(a_1 = 0\).
{solution}
As Boyd points out, the solutions must be even (prove this!), hence we need only consider a cosine series. The boundary conditions can be enforced with a series of the form
Considering two terms \(a_0\) and \(a_1\), we choose the Gauss-Chebyshev collocation points: the roots of \(\phi_{1}(x) = \cos(k_1 x) = \cos(\pi x/2) = 0\):
Plugging the solution into the equation gives us the residual function, which we set to zero at each of the collocation points
import sympy
def get_spectral_approx(N, show=False):
an = sympy.symbols(f'a:{N}', real=True)
kn = tuple(sympy.pi * (2*n + 1)/2 for n in sympy.S(np.arange(N)))
xi = tuple((2*n+1)/(2*N+1) for n in sympy.S(np.arange(N)))
print(xi)
#xi = tuple(sympy.cos(sympy.pi * (N-n)/2/N) for n in sympy.S(np.arange(N)))
x = sympy.symbols('x')
u = 1 + sum(a_ * sympy.cos(k_ * x) for a_, k_ in zip(an, kn))
Eqs = [(u.diff(x,x) - (x**6 + 3*x**2)*u).subs([(x, x_)]).evalf() for x_ in xi]
sol = sympy.solve(Eqs, an)
u_x = u.subs(sol)
if show:
display(sol)
display(u.evalf())
u_ = sympy.lambdify(x, u_x, "numpy")
return u_
fig, ax = plt.subplots()
x = np.linspace(-1, 1)
u_exact = np.exp((x**4-1)/4)
ax.plot(x, u_exact, label="Exact: $u(x) = e^{(x^4-1)/4}$")
for N in [2, 3, 4, 5, 6]:
u = get_spectral_approx(N=N)
ax.plot(x, u(x), label=f"Spectral: ${N=}$")
fig.legend()
(1/5, 3/5)
(1/7, 3/7, 5/7)
(1/9, 1/3, 5/9, 7/9)
(1/11, 3/11, 5/11, 7/11, 9/11)
(1/13, 3/13, 5/13, 7/13, 9/13, 11/13)
<matplotlib.legend.Legend at 0x7bb3f8cc0980>
Numerical Example: Chebyshev Basis#
We will explore these more in Spectral Methods: Chebyshev Basis, but for now, we give a couple of simplex examples using the Chebyshev polynomials \(T_{n}(x)\), which form a basis for functions on the interval \(x\in [-1, 1]\):
Example 1: Derivatives#
We start by using the basis the approximate a function \(f(x)\) and then to compute its derivative. Let’s consider \(f(x) = e^{x}\): to determine the coefficients \(a_{n}\) we define a residual which we minimize. There are many ways of doing this – for example:
To better work with this, we introduce the vectors \(\ket{f}\) (the function) an \(\ket{f_{\vect{a}}}\) (the approximation):
The residue, can then be expressed:
Approximate the ground state energy and wavefunction for a particle in a box with a Gaussian barrier:
DVR Bases#
To construct a DVR basis, one needs in addition to the basis, a quadrature rule. Thus, we need:
A set of \(N\) orthonormal basis functions \(P_{n}(x)\).
An \(N\)-point Gaussian quadrature rule exact for all linear and quadratic products of the basis functions.
As before, orthonormality is defined with respect to an inner product
and the \(N\)-point quadrature rule consists of \(N\) points \(x_i\) and weights \(w_i\) such that
For a DVR basis, we need this quadrature formula to be exact for:
The functions: \(P_n(x)\).
Products: \(P_m^*(x)P_n(x)\).
Products with an additional factor of \(x\): \(xP_m^*(x)P_n(x)\). (This is not strictly needed, but ensures that keeping the potential diagonal works reasonably well.)
This will be the case for the orthogonal polynomials, which is why we use the notation \(P_n(x)\), but also holds for the Fourier basis and some other sets with appropriately chosen quadrature rules.
From these ingredients, we construct a set of coordinate basis functions \(F_n(x)\) that are local in the sense that \(F_n(x_m)\) vanishes unless \(m=n\), and that are orthonormal with respect to the coordinate \(x\):
Using the orthonormality and evaluating the integrals with the quadrature rule, these properties give the relationship:
Other Notations and the Cardinal Functions
To compare with the literature, note that [Schneider and Nygaard, 2004] uses the notation \(u_n(x) = \sqrt{w(x)}F_n(x)\) so that expectation values are simple
This is convenient for numerical work.
In the notation of [Littlejohn et al., 2002], \(K_m = 1/w_m\). They use the following three functions, which differ only by normalization:
The functions \(L_{m}(x)\) here are denoted \(C_{m}(x)\) in [Boyd, 1989] and are called cardinal functions. These act like a Dirac delta function in the basis and can often be obtained by projection. For example, the cardinal functions for the Fourier basis can be constructed from (for even \(N\))
This is geometric series:
Thus,
The phase factor is somewhat inconsequential. (It results from the fact that, for even \(N\), there is a mismatch of positive and negative frequencies, so that the complex plane-wave \(P_{-N/2}(x)\) is unpaired. This issue can be removed, e.g., by shifting \(k_n \rightarrow k_n + \pi/L\), or by choosing \(N\) to be odd.) What remains are the cardinal functions for a periodic sine basis.
If we take the limit \(L \rightarrow \infty\) holding \(\d{x} = L/N\) fixed, then we recover the Whitaker cardinal functions (see the Whittaker-Shannon interpolation formula) for the real line:
These can be solved, taking \(f_m\) to be real:
If the last quadrature condition is satisfied, then we have \(\braket{F_m|x|F_n} = x_m\delta_{mn}\), which gets to the essence of these basis: that the potential can be expressed as a diagonal matrix:
To compute the kinetic energy, we must include factors of \(\sqrt{w(x)}\) in the basis functions:
Then, the kinetic energy can be evaluated using integration by parts and the quadrature rule. See below for details.
Orthogonal Polynomials#
One way of constructing a DVR basis is from a set of \(N\) polynomials \(P_n(x)\) (i.e. of maximum degree \(N-1\)) that are orthonormal with respect to a measure \(\alpha(x)\):
An \(N\)-point Gaussian quadrature rule is a set of \(N\) points \(x_i\) and weights \(w_i\) such that
is exact for all polynomials of degree \(2N-1\) or less. To be used in constructing the DVR basis, the quadrature rule must be exact for polynomials of order \(2(N-1) + 1 = 2N-1\), thus, the Gaussian quadrature provides an acceptable set of \(N\) abscissa and weights for constructing the DVR basis using the formulae above.
Aliasing and Conservation Laws#
In the continuum limit with constant external potential, the GPE conserves particle number, energy, and momentum:
To see this, use the GPE
Consider the conservation of momentum:
The contributions from the kinetic energies poses will cancel since derivatives commute, but the non-linear potential requires an application of the product rule:
Nx = 64
Lx = 10.0
dx = Lx/Nx
k = 2*np.pi * np.fft.fftfreq(Nx, dx)
k_max = abs(k).max()
x = np.arange(Nx) * dx - Lx//2
Dk = 1j*k
Dk[Nx//2] = 0
D2k = -k**2
def P(f, kc):
return np.fft.ifft(np.where(abs(k)<kc, np.fft.fft(f), 0))
def D(f):
return np.fft.ifft(Dk*np.fft.fft(f))
def D2(f):
return np.fft.ifft(D2k*np.fft.fft(f))
rng = np.random.default_rng(seed=2)
psi = rng.normal(size=2*Nx).view(complex)
f = np.exp(-x**2/2)
df = -x*f
d2f = (x**2-1)*f
plt.plot(x, df)
plt.plot(x, D(f))
plt.plot(x, d2f)
plt.plot(x, D2(f))
/home/docs/checkouts/readthedocs.org/user_builds/gpe/conda/latest/lib/python3.14/site-packages/matplotlib/cbook.py:1719: ComplexWarning: Casting complex values to real discards the imaginary part
return math.isfinite(val)
/home/docs/checkouts/readthedocs.org/user_builds/gpe/conda/latest/lib/python3.14/site-packages/matplotlib/cbook.py:1355: ComplexWarning: Casting complex values to real discards the imaginary part
return np.asarray(x, float)
[<matplotlib.lines.Line2D at 0x7bb3f8b7c6e0>]
Consider
References#
[] has the best overview I have seen for the general formulation of DVR bases.