\documentclass[11pt,a4paper,DIV11,%draft,%
notitlepage,oneside,abstracton,%
bibtotoc]{scrartcl}

%encoding
\usepackage[utf8]{inputenc}

% only for the article version
\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amsthm}
\usepackage{fullpage}
%\setlength{\parindent}{0pt}
%\setlength{\parskip}{1.3ex plus 0.5ex minus 0.2ex}


% all after
\usepackage{graphicx}
%\usepackage{multimedia}
\usepackage{psfrag}
\usepackage{listings}
\lstset{language=C++, basicstyle=\small\ttfamily,
  stringstyle=\ttfamily, commentstyle=\it, extendedchars=true}
\usepackage{curves}
\usepackage{epic}
\usepackage{calc}
\usepackage{fancybox}
\usepackage{xspace}
\usepackage{enumerate}
\usepackage{algorithmic}
\usepackage{algorithm}
\usepackage{hyperref}

\title{Multi-Step Methods}
\author{\textsc{Jö Fahlke}\\
Interdisziplinäres Zentrum für Wissenschaftliches Rechnen,\\
 Universität Heidelberg, Im Neuenheimer Feld 368, \\
D-69120 Heidelberg\\
email: \texttt{jorrit@jorrit.de}
}
\date{Version: \today}

\begin{document}

\maketitle

%\begin{abstract}
%\end{abstract}

\tableofcontents

\section{Framework}

Consider the $p$'th order ODE
\begin{equation}
  f(t,u,\partial_tu,\partial_t^2u,\ldots,\partial_t^pu)=0.
\end{equation}
Often, it can be written as a sum of residuals
\begin{equation}
  \sum_{j=0}^pr_j(t,\partial_t^ju)=0
\end{equation}
We consider $m$-step schemes ($m\geq p$) that can be written in the following
way to solve for $u^n$:
\begin{equation}
  \sum_{i=0}^m\sum_{j=0}^p{\alpha_{ij} \over (\Delta t)^j}r_j(t^{n-i},u^{n-i})=0
\end{equation}

\section{Particular Schemes}

\subsection{Central Differences}

We consider the vector wave equation from Jin, a second order ODE:
\begin{equation}
  T\partial_t^2u+T_\sigma\partial_tu+Su+f=0
\end{equation}
Jin gives the central differences time stepping scheme (a two-step scheme) as
follows:
\begin{equation}
  \left\{{1 \over (\Delta t)^2} T + {1 \over 2\Delta t} T_\sigma \right\} u^n
  +\left\{-{2 \over (\Delta t)^2} T + S \right\} u^{n-1}
  +\left\{{1 \over (\Delta t)^2} T + {1 \over 2\Delta t} T_\sigma \right\} u^{n-2}
  +f^{n-1} = 0
\end{equation}
We can set the residuals as follows:
\begin{align}
  r_0(t,x)&=S(t)x+f(t) \\
  r_1(t,x)&=T_\sigma(t)x \\
  r_2(t,x)&=T(t)x
\end{align}
The coefficients $\alpha_{ij}$ then have to be
\begin{align}
  \alpha_{00}&=0 & \alpha_{01}&=\frac12 & \alpha_{02}&=1 \\
  \alpha_{10}&=1 & \alpha_{11}&=0       & \alpha_{12}&=-2 \\
  \alpha_{20}&=0 & \alpha_{21}&=\frac12 & \alpha_{22}&=1
\end{align}

\subsection{Newmark $\beta$ Method}

We consider the same ODE as for central differences.  The residuals are also
the same.  Jin gives the Newmark-$\beta$ scheme as follows:
\begin{multline}
  \left\{{1 \over (\Delta t)^2}T + {1 \over 2\Delta t}T_\sigma + \beta S \right\} u^n
  +\left\{-{2 \over (\Delta t)^2}T + (1-2\beta)S \right\} u^{n-1} \\
  +\left\{{1 \over (\Delta t)^2}T + {1 \over 2\Delta t}T_\sigma + \beta S \right\} u^{n-2}
  +[\beta f^n + (1-2\beta)f^{n-1} + \beta f^{n-2}] = 0
\end{multline}
The coefficients $\alpha_{ij}$ then have to be
\begin{align}
  \alpha_{00}&=\beta    & \alpha_{01}&=\frac12 & \alpha_{02}&=1 \\
  \alpha_{10}&=1-2\beta & \alpha_{11}&=0       & \alpha_{12}&=-2 \\
  \alpha_{20}&=\beta    & \alpha_{21}&=\frac12 & \alpha_{22}&=1
\end{align}
Central differences is identical to Newmark-$\beta$ with $\beta=0$!

\subsection{BDF}

We consider an $m$-step BDF method as given by Wikipedia
$<$\url{http://en.wikipedia.org/wiki/Backward_differentiation_formula}$>$:
\begin{equation}
  {1 \over \Delta t} \sum_{i=0}^m a_i u^{n-i} = b_0 f(t^n,u^n)
\end{equation}
for the first oder ODE
\begin{equation}
  \partial_tu = f(t,u)
\end{equation}
We chose the residuals
\begin{align}
  r_0(t,x)&=f(t,x) \\
  r_1(t,x)&=x
\end{align}
and the coefficients $\alpha_{ij}$:
\begin{equation}\begin{aligned}
  \alpha_{00}&=b_0 \\
  \alpha_{i0}&=0    && 1 \leq i \leq m \\
  \alpha_{i1}&=-a_i && 0 \leq i \leq m
\end{aligned}\end{equation}

\section{Implementation}

\subsection{\tt MultiStepParameters}

Parameter class providing
\begin{enumerate}
\item the number of stages $m$,
\item the order of the ODE $p \leq m$, and
\item the coefficients $\alpha_{ij}$, $0 \leq i \leq m$, $0 \leq j \leq p$.
\end{enumerate}

\subsection{\tt MultiStepGridOperatorSpace}

{\tt GridOperatorSpace} taking a {\tt MultiStepParameters} object and $p+1$
local operators for the residuals $r_0,\ldots,r_p$.
\begin{enumerate}
\item $\Delta t$ and current time $t^n$ are stored internally.
\item (Private) method {\tt residualAtStep()} takes a step number $i>0$ and a
  vector of dofs $u^{n-i}$ and computes
  \begin{equation}
    \sum_{j=0}^p{\alpha_{ij} \over (\Delta t)^j}r_j(t^{n-i},u^{n-i})
  \end{equation}
\item {\tt setTime()} updates the current time $t^n$.
\item Method {\tt preStep()} receives the dof vectors $u^{n-m},\ldots,u^{n-1}$
  to compute the constant part of the residual
  \begin{equation}
    \sum_{i=1}^m\sum_{j=0}^p{\alpha_{ij} \over (\Delta t)^j}r_j(t^{n-i},u^{n-i})
  \end{equation}
  It must be called before calling {\tt residual()} but after {\tt setTime()}.
\item The {\tt MultiStepGridOperatorSpace} calls {\tt setTime()} on the local
  operators as necessary.  The local operators should call {\tt setTime()} on
  any parameter functions they evaluate, as apropriate.
\item Initially the {\tt MultiStepGridOperatorSpace} may be limited to a
  certain ODE order $p$, because handling an arbitrary number of local
  operators of probably different C++ types will be involved.
\end{enumerate}

\subsection{\tt MultiStepMethod}

This class takes a {\tt MultiStepGridOperatorSpace} object, (possibly) a {\tt
  MultiStepParameters} object and a solver object (linear or newton) and feed
the {\tt MultiStepGridOperatorSpace} to the solver.  It will hold the vectors
of dofs $u^{n-1},\ldots,u^{n-m}$ as shared pointers in a queue and
automatically handle the management of the queue.

\section{Caching}

Caching is done in concert by three classes: A {\tt MultiStepCache}, a {\tt
  Cached\-Multi\-Step\-Grid\-Operator\-Space} and a {\tt MultiStepCachePolicy}
object.  The first two are provided generically by PDELab, but the {\tt
  MultiStepCachePolicy} must be provided by the user und is closely coupled to
the problem to solve and the local operators used to solve it.  In a sense,
the {\tt MultiStepCache} provides storage, the {\tt MultiStepCachePolicy}
determines when it is advisable to store anything and the {\tt
  Cached\-Multi\-Step\-Grid\-Operator\-Space} does all the work.  The {\tt
  MultiStepCache} object holds a reference to the {\tt MultiStepCachePolicy}
and the {\tt Cached\-Multi\-Step\-Grid\-Operator\-Space} holds a reference to
the {\tt MultiStepCache} object.  This implies that any given instance of {\tt
  Cached\-Multi\-Step\-Grid\-Operator\-Space} is fixed with respect to the C++
type of the coefficients.

The Following methods apply to the overall management of the cache and policy
objects:
\begin{lstlisting}
template<class GFSU, class GFSV, class Coeffs, class Step, class Time, ...>
struct CachedMultiStepGridOperatorSpace {
  typedef MultiStepCache<
    typename GFSU::template VectorContainer<Coeffs>::Type,
    typename GFSV::template VectorContaineR<Coeffs>::Type,
    typename MatrixContainer<Coeffs>::Type,
    Step,
    Time> Cache;

  shared_ptr<Cache> getCache() const;
  void setCache(const shared_ptr<Cache> &cache);
};

template<class VectorU, class VectorV, class Matrix,
         class Step = int, class Time = double>
struct MultiStepCache {
  typedef MultiStepCachePolicy<Step, Time> Policy;

  MultiStepCache(const shared_ptr<Policy> &policy = 
                       shared_ptr<Policy>(new Policy));

  shared_ptr<Policy> getPolicy();
  shared_ptr<const Policy> getPolicy() const;
  void setPolicy(const shared_ptr<Policy>& policy);
};

template<class Step = int, class Time = double>
struct MultiStepCachePolicy {
  // any virtual base needs a virtual destructor
  virtual ~MultiStepCachePolicy() {}
};
\end{lstlisting}

\subsection{Caching across Time Steps}

Consider time step $n$ and $n+1$: In step $n$ we solve
$\sum_{j=0}^pr_j(t^n,u^n)$ and evaluate $r_j(t^k,u^k)$ for $n<k\leq n-m$.  In
step $n+1$ we solve $\sum_{j=0}^pr_j(t^{n+1},u^{n+1})$ and evaluate
$r_j(t^k,u^k)$ for $n+1<k\leq n+1-m$.  In step $n+1$ we can reuse the results
of the evaluations $r_j(t^k,u^k)$ for $n<k<n-m$ from step $n$, i.e. $m-1$
evaluations of the operators $r_j$.  This is possible even for non-linear
operators.

From the {\tt MultiStepCache} and the {\tt MultiStepCachePolicy} the following
methods are applicable:
\begin{lstlisting}
template<class VectorU, class VectorV, class Matrix,
         class Step = int, class Time = double>
struct MultiStepCache {
  // methods for non-linear caching
  shared_ptr<const VectorV>
  getResidualValue(std::size_t order, Step step) const;

  void setResidualValue(std::size_t order, Step step,
                        const shared_ptr<const VectorV> &residualValue);
};

template<class Step = int, class Time = double>
struct MultiStepCachePolicy {
  virtual bool cacheResidualValue(std::size_t order, Step step) const;
};
\end{lstlisting}
If {\tt cacheResidualValue()} returns {\tt false} the {\tt
  Cached\-Multi\-Step\-Grid\-Operator\-Space} will not attempt to store the
denoted residual value in the cache.  It may still attempt to extract it,
however.

\subsection{Caching for Affine Operators}

Affine operators can be split into a purely linear and a constant part:
\begin{equation}
  r_j(t,u)=J(r_j|_t)\cdot u+r_j(t,0)
\end{equation}
We call the constant part $r_j(t,0)$ the zero-residual.  The Jacobian
$J(r_j|_t)$ may depend on time $t$ but not on the unknowns $u$.  It is
meaningful to cache Jacobians and zero-residuals, since they are usually
needed first in the solver for calculating many applications of $r_j(t_n,u)$
to find $u_n$, and are then needed in the next time step to compute
$r_j(t_n,u_n)$.  If the {\tt MultiStepCachePolicy} object decides not to store
certain residual values, they can also be used to recompute those values
without another grid traversal.

From the {\tt MultiStepCache} and the {\tt MultiStepCachePolicy} the following
methods are applicable:
\begin{lstlisting}
template<class VectorU, class VectorV, class Matrix,
         class Step = int, class Time = double>
struct MultiStepCache {
  // methods for the Jacobian of affine operators
  shared_ptr<const Matrix>
  getJacobian(std::size_t order, Step step);
 
 void setJacobian(std::size_t order, Step step,
                   const shared_ptr<const Matrix> &jacobian);

  // methods for the zero-residual of affine operators
  shared_ptr<const VectorV>
  getZeroResidual(std::size_t order, Step step);

  void setZeroResidual(std::size_t order, Step step,
                       const shared_ptr<const VectorV> &zeroResidual);
};

template<class Step = int, class Time = double>
struct MultiStepCachePolicy {
  virtual bool isAffine(std::size_t order, Step step) const;
  virtual bool hasPureLinearAlpha(std::size_t order, Step step) const;

  virtual bool cacheJacobian(std::size_t order, Step step) const;
  virtual bool cacheZeroResidual(std::size_t order, Step step) const;
};
\end{lstlisting}
If {\tt cacheJacobian()} or {\tt cacheZeroResidual()} return {\tt false}, the
{\tt MultiStepCache} object will silently refuse to store the denoted Jacobian
or zero-residual in the cache.  It may still provide the denoted values when
they happen to be in the cache and are extracted, however.

{\tt isAffine(j, n)} determines whether $r_j(t_n,u)$ will be treated as affine
in the argument $u$ by the {\tt CachedMultiStepGridOperatorSpace}.  If the
given $r_j(t_n,u)$ is not affine, {\em the} Jacobian and {\em the}
zero-residual do not exist as such, so {\tt CachedMultiStepGridOperatorSpace}
will use a totally different method to compute the residual value.  As a
consequence it will not even attempt to store the Jacobian and the
zero-residual.

{\tt hasPureLinearAlpha(j, n)} determines whether the methods {\tt
  alpha\_volume()}, {\tt alpha\_skeleton()}, {\tt alpha\_boundary()} and {\tt
  alpha\_volume\_post\_skeleton()} on the local operators are purely linear.
In this case, the {\tt CachedMultiStepGridOperatorSpace} can use a somewhat
more efficient method to compute the zero-residual.

When {\tt isAffine(j, n)} returns {\tt false}, the return values of {\tt
  hasPureLinearAlpha(j, n)}, {\tt cacheJacobian(j, n)}, and {\tt
  cache\-Zero\-Residual(j, n)} have no effect.

\subsection{Caching Stationary Parts of Affine Operators}

In the case of affine operators the Jacobian often does not change over time.
Temporal variability usually comes from boundary conditions, which are either
applied outside of the operator (Dirichlet) or contribute to the
$\lambda_j$-term (Neumann).  However, even the Jacobian may show changes over
time, for instance when material properties change or when it is coupled to a
different equation.  Even if changes do occur, they may occur apruptly,
seperated by long periods without changes, as in switching some inflow on or
off.

To take advantage of stationary periods we may re-use a Jacobian or
zero-residual from another time-step for the current time-step.  Again, there
is a method on the {\tt MultiStepCachePolicy} object which tells us whether we
can actually do this:
\begin{lstlisting}
template<class Step = int, class Time = double>
struct MultiStepCachePolicy {
  virtual bool canReuseJacobian(std::size_t order,
                                Step requested, Step available) const;
  virtual bool canReuseZeroResidual(std::size_t order,
                                    Step requested, Step available) const;
};
\end{lstlisting}
When a Jacobian or zero-residual is needed but not already present in the
cache, these methods will be consulted with the {\tt available} parameter set
to the time step of each other Jacobian or zero-residual for that order in
turn.  Note that the answer of these methods should be symmetric in the {\tt
  requested} and {\tt available} arguments -- they form an equivalence
relation.  Note also that not only the latest available value before the
requested is considered -- every available value for the given order is a
potential candidate for re-use, that includes values from later time steps
than the requested.  This can happen for instance when filling the cache
initially or when switching between different multi-step methods.

\subsection{Caching for the Complete System Matrix}

\begin{gather}
  \sum_{j=0}^p\frac{\alpha_{0j}}{(\Delta t)^j}r_j(t^n,u^n)+
    \sum_{i=1}^m\sum_{j=0}^p\frac{\alpha_{ij}}{(\Delta t)^j}
             r_j(t^{n-i},u^{n-i})=0 \\
  \sum_{j=0}^p\frac{\alpha_{0j}}{(\Delta t)^j}
             \{J(r_j|_{t^n})\cdot u^n+r_j(t_n,0)\}+
    \sum_{i=1}^m\sum_{j=0}^p\frac{\alpha_{ij}}{(\Delta t)^j}
             r_j(t^{n-i},u^{n-i})=0 \\
  \left\{\sum_{j=0}^p\frac{\alpha_{0j}}{(\Delta t)^j}
             J(r_j|_{t^n})\right\}\cdot u^n+
    \sum_{j=0}^p\frac{\alpha_{0j}}{(\Delta t)^j}r_j(t_n,0)+
    \sum_{i=1}^m\sum_{j=0}^p\frac{\alpha_{ij}}{(\Delta t)^j}
             r_j(t^{n-i},u^{n-i})=0
\end{gather}
The part in curly braces is the matrix passed to the solver backend.  When
none of the Jacobians change from time step $n$ to $n+1$, we can reuse this
matrix in the next time step.  The {\tt MultiStepCache} does not know however
how many operators are involved, so the {\tt MultiStepCachePolicy} has to
provide extra functions to determine this.

Caching the constant part does not make that much sense, since usually the old
values of the unknowns change from time step to time step and the constant
part has to be recomputed anyway.
\begin{lstlisting}
template<class VectorU, class VectorV, class Matrix,
         class Step = int, class Time = double>
struct MultiStepCache {
  // methods for the composed Jacobian of affine operators
  shared_ptr<const Matrix>
  getComposedJacobian(Step step);

  void setComposedJacobian(std::size_t order,
                           const shared_ptr<const Matrix> &jacobian);
};

template<class Step = int, class Time = double>
struct MultiStepCachePolicy {
  virtual bool isComposedAffine(Step step) const;

  virtual bool cacheComposedJacobian(Step step) const;
  virtual bool canReuseComposedJacobian(Step requested,
                                        Step available) const;
};
\end{lstlisting}

\subsection{Old Values for the Unknowns}

The cache is a convenient place to store old values for the vectors of
unknowns.\footnote{Of course, ``unknowns'' is a bit of a misnomer here since
  the actual values are known by the time the vectors aquire the adjective
  ``old''.}  No policy applies to storing vectors of unknowns, since this is
done by the user or the {\tt MultiStepMethod}.
\begin{lstlisting}
template<class VectorU, class VectorV, class Matrix,
         class Step = int, class Time = double>
struct MultiStepCache {
  // methods to access old values of the unknowns
  shared_ptr<const VectorU>
  getUnknowns(Step step) const;

  void setUnknowns(Step step,
                   const shared_ptr<const VectorU> &unknowns);
};
\end{lstlisting}

\subsection{Flushing the Cache}

At certain points between time steps is is advisable to evict data from the
cache, since we won't possibly need that data anymore and computer memory is
limited.  This is done by the {\tt MultiStepCache} at the beginning of the
time step and triggered by its {\tt preStep()} method.  There is also a {\tt
  postStep()} method for symmetry.  Both methods are forwarded to the {\tt
  Multi\-Step\-Cache\-Policy}: {\tt MultiStepCache::preStep()} calls {\tt
  Multi\-Step\-Cache\-Policy\discretionary{}{}{}::\discretionary{}{}{}pre\-Step()}
and {\tt MultiStepCache::postStep()} calls {\tt
  MultiStepCachePolicy::postStep()}.  After the call to {\tt
  MultiStepCachePolicy::preStep()}, {\tt MultiStepCache::preStep()} will
iterate over all items in the cache and ask the {\tt MultiStepCachePolicy}
whether they can be removed.  Finally, there is also a method {\tt flushAll()}
which unconditionally clears the whole cache, which can be useful in the case
of grid adaption.
\begin{lstlisting}
template<class VectorU, class VectorV, class Matrix,
         class Step = int, class Time = double>
struct MultiStepCache {
  // methods to flush the cache
  void flushAll();

  // methods for pre-/post-processing
  void preStep(Step step, Step stepsOfScheme,
               Time endTime, Time dt);
  void postStep();
};
template<class Step = int, class Time = double>
struct MultiStepCachePolicy {
  // methods for pre-/post-processing
  virtual void preStep(Step step, Step stepsOfScheme,
                       Time endTime, Time dt);
  virtual void postStep();

  // methods to determine stale items
  virtual bool canEvictResidualValue(std::size_t order,
                                     Step step) const;
  virtual bool canEvictJacobian(std::size_t order, Step step) const;
  virtual bool canEvictZeroResidual(std::size_t order,
                                    Step step) const;
  virtual bool canEvictUnknowns(Step step) const;
  virtual bool canEvictComposedJacobian(Step step) const;
};
\end{lstlisting}

\subsection{Interface Summary}

\begin{lstlisting}
template<class GFSU, class GFSV, class Coeffs, class Step, class Time, ...>
struct CachedMultiStepGridOperatorSpace {
  typedef MultiStepCache<
    typename GFSU::template VectorContainer<Coeffs>::Type,
    typename GFSV::template VectorContaineR<Coeffs>::Type,
    typename MatrixContainer<Coeffs>::Type,
    Step,
    Time> Cache;

  shared_ptr<Cache> getCache() const;
  void setCache(const shared_ptr<Cache> &cache);
};

template<class VectorU, class VectorV, class Matrix,
         class Step = int, class Time = double>
struct MultiStepCache {
  typedef MultiStepCachePolicy<Step, Time> Policy;

  MultiStepCache(const shared_ptr<Policy> &policy =
                       shared_ptr<Policy>(new Policy));

  shared_ptr<Policy> getPolicy();
  shared_ptr<const Policy> getPolicy() const;
  void setPolicy(const shared_ptr<Policy>& policy);

  // methods for non-linear caching
  shared_ptr<const VectorV>
  getResidualValue(std::size_t order, Step step) const;

  void setResidualValue(std::size_t order, Step step,
                        const shared_ptr<const VectorV> &residualValue);

  // methods for the Jacobian of affine operators
  shared_ptr<const Matrix>
  getJacobian(std::size_t order, Step step);

  void setJacobian(std::size_t order, Step step,
                   const shared_ptr<const Matrix> &jacobian);

  // methods for the zero-residual of affine operators
  shared_ptr<const VectorV>
  getZeroResidual(std::size_t order, Step step);

  void setZeroResidual(std::size_t order, Step step,
                       const shared_ptr<const VectorV> &zeroResidual);

  // methods for the composed Jacobian of affine operators
  shared_ptr<const Matrix>
  getComposedJacobian(Step step);

  void setComposedJacobian(std::size_t order,
                           const shared_ptr<const Matrix> &jacobian);

  // methods to access old values of the unknowns
  shared_ptr<const VectorU>
  getUnknowns(Step step) const;

  void setUnknowns(Step step,
                   const shared_ptr<const VectorU> &unknowns_);

  // methods to flush the cache
  void flushAll();

  // methods for pre-/post-processing
  void preStep(Step step, Step stepsOfScheme,
               Time endTime, Time dt);
  void postStep();
};

template<class Step = int, class Time = double>
struct MultiStepCachePolicy {
  // any virtual base needs a virtual destructor
  virtual ~MultiStepCachePolicy() {}

  // methods to determine what to cache
  virtual bool cacheResidualValue(std::size_t order, Step step) const;
  virtual bool cacheJacobian(std::size_t order, Step step) const;
  virtual bool cacheZeroResidual(std::size_t order, Step step) const;
  virtual bool cacheComposedJacobian(Step step) const;

  // methods to determine properties of the LOPs
  virtual bool isAffine(std::size_t order, Step step) const;
  virtual bool isComposedAffine(Step step) const;
  virtual bool hasPureLinearAlpha(std::size_t order, Step step) const;
  virtual bool canReuseJacobian(std::size_t order,
                                Step requested, Step available) const;
  virtual bool canReuseZeroResidual(std::size_t order,
                                    Step requested, Step available) const;
  virtual bool canReuseComposedJacobian(Step requested,
                                        Step available) const;

  // methods for pre-/post-processing
  virtual void preStep(Step step, Step stepsOfScheme,
                       Time endTime, Time dt);
  virtual void postStep();

  // methods to determine stale items
  virtual bool canEvictResidualValue(std::size_t order,
                                     Step step) const;
  virtual bool canEvictJacobian(std::size_t order, Step step) const;
  virtual bool canEvictZeroResidual(std::size_t order,
                                    Step step) const;
  virtual bool canEvictUnknowns(Step step) const;
  virtual bool canEvictComposedJacobian(Step step) const;
};
\end{lstlisting}

\end{document}
