<br>

<i>This program was contributed by Toby D. Young and Wolfgang
Bangerth.  </i>

<a name="Preamble"></a> 
<h1>Preamble</h1>

The problem we want to solve in this example is an eigenspectrum
problem. Eigenvalue problems appear in a wide context of problems, for
example in the computation of electromagnetic standing waves in
cavities, vibration modes of drum membranes, or oscillations of lakes
and estuaries. One of the most enigmatic applications is probably the
computation of stationary or quasi-static wave functions in quantum
mechanics. The latter application is what we would like to investigate
here, though the general techniques outlined in this program are of
course equally applicable to the other applications above.

Eigenspectrum problems have the general form
@f{align*}
	L \Psi &= \varepsilon \Psi \qquad &&\text{in}\ \Omega\quad,
	\\
	\Psi &= 0 &&\text{on}\ \partial\Omega\quad,
@f}
where the Dirichlet boundary condition on $\Psi=\Psi(\mathbf x)$ could also be
replaced by Neumann or Robin conditions; $L$ is an operator that generally
also contains differential operators.

Under suitable conditions, the above equations have a set of solutions
$\Psi_\ell,\varepsilon_\ell$, $\ell\in {\cal I}$, where $\cal I$ can
be a finite or infinite set (and in the latter case it may be a discrete or
sometimes at least in part a continuous set). In either case, let us note that
there is 
no longer just a single solution, but a set of solutions (the various
eigenfunctions and corresponding eigenvalues) that we want to
compute. The problem of numerically finding all eigenvalues
(eigenfunctions) of such eigenvalue problems is a formidable
challenge. In fact, if the set $\cal I$ is infinite, the challenge is
of course intractable.  Most of the time however we are really only
interested in a small subset of these values (functions); and
fortunately, the interface to the SLEPc library that we will use for
this tutorial program allows us to select which portion of the
eigenspectrum and how many solutions we want to solve for.

In this program, the eigenspectrum solvers we use are classes provided
by deal.II that wrap around the linear algebra implementation of the
<a href="http://www.grycap.upv.es/slepc/" target="_top">SLEPc</a>
library; SLEPc itself builds on the <a
href="http://www.mcs.anl.gov/petsc/" target="_top">PETSc</a> library
for linear algebra contents.

<a name="Intro"></a>
<h1>Introduction</h1>

The basic equation of stationary quantum mechanics is the
Schrödinger equation which models the motion of particles in an
external potential $V(\mathbf x)$. The particle is described by a wave
function $\Psi(\mathbf x)$ that satisfies a relation of the
(nondimensionalized) form
@f{align*} [-\Delta + V(\mathbf x)]
\Psi(\mathbf x) &= \varepsilon \Psi(\mathbf x) \qquad &&\text{in}\
\Omega\quad, \\ \Psi &= 0 &&\text{on}\ \partial\Omega\quad.  
@f} 
As a consequence, this particle can only exist in a certain number of
eigenstates that correspond to the energy eigenvalues
$\varepsilon_\ell$ admitted as solutions of this equation. The
orthodox (Copenhagen) interpretation of quantum mechanics posits that, if a
particle has energy $\varepsilon_\ell$ then the probability of finding
it at location $\mathbf x$ is proportional to $|\Psi_\ell(\mathbf
x)|^2$ where $\Psi_\ell$ is the eigenfunction that corresponds to this
eigenvalue.

In order to numerically find solutions to this equation, i.e. a set of
pairs of eigenvalues/eigenfunctions, we use the usual finite element
approach of multiplying the equation from the left with test functions,
integrating by parts, and searching for solutions in finite
dimensional spaces by approximating $\Psi(\mathbf
x)\approx\Psi_h(\mathbf x)=\sum_{j}\phi_j(\mathbf x)\tilde\psi_j$,
where $\tilde\psi$ is a vector of expansion coefficients. We then
immediately arrive at the following equation that discretizes the
continuous eigenvalue problem: @f[ \sum_j [(\nabla\phi_i,
\nabla\phi_j)+(V(\mathbf x)\phi_i,\phi_j)] \tilde{\psi}_j =
\varepsilon_h \sum_j (\phi_i, \phi_j) \tilde{\psi}_j\quad.  @f] In
matrix and vector notation, this equation then reads: @f[ A
\tilde{\Psi} = \varepsilon_h M \tilde{\Psi} \quad, @f] where $A$ is
the stiffness matrix arising from the differential operator $L$, and
$M$ is the mass matrix. The solution to the eigenvalue problem is an
eigenspectrum $\varepsilon_{h,\ell}$, with associated eigenfunctions
$\Psi_\ell=\sum_j \phi_j\tilde{\psi}_j$.

It is this form of the eigenvalue problem that involves both matrices
$A$ and $M$ that we will solve in the current tutorial program. We
will want to solve it for the lowermost few eigenvalue/eigenfunction
pairs.

<h3>Implementation details</h3>

The program below is essentially just a slightly modified version of 
step-4. The things that are different are the following:

<ul> 

<li>The main class (named <code>EigenvalueProblem</code>) now no
longer has a single solution vector, but a whole set of vectors for
the various eigenfunctions we want to compute. Moreover, the
<code>main</code> function, which has the top-level control over
everything here, initializes and finalizes the interface to SLEPc and
PETSc simultaneously via <code>SlepcInitialize</code> and
<code>SlepFinalize</code>.</li>

<li>We use PETSc matrices and vectors as in step-17 and
step-18 since that is what the SLEPc eigenvalue solvers
require.</li>

<li>The function <code>EigenvalueProblem::solve</code> is entirely
different from anything seen so far in the tutorial, as it does not
just solve a linear system but actually solves the eigenvalue problem.
It is built on the SLEPc library, and more immediately on the deal.II
SLEPc wrappers in the class SLEPcWrappers::SolverKrylovSchur.</li>

<li>We use the ParameterHandler class to describe a few input
parameters, such as the exact form of the potential $V({\mathbf
x})$, the number of global refinement steps of the mesh,
or the number of eigenvalues we want to solve for. We could go much
further with this but stop at making only a few of the things that one
could select at run time actual input file parameters. In order to see
what could be done in this regard, take a look at @ref step_29
"step-29", step-33, and in particular @ref step_19
"step-19".</li>

<li>We use the FunctionParser class to make the potential $V(\mathbf
x)$ a run-time parameter that can be specified in the input file as a
formula.</li>

</ul>

The rest of the program follows in a pretty straightforward way from 
step-4.
