Liebe Gitlab-Nutzer, lieber Gitlab-Nutzer, es ist nun möglich sich mittels des ZIH-Logins/LDAP an unserem Dienst anzumelden. Ein Anmelden über dieses erzeugt ein neues Konto. Das alte Konto ist über den Reiter "Standard" erreichbar. Die Administratoren

Dear Gitlab user, it is now possible to log in to our service using the ZIH login/LDAP. Logging in via this will create a new account. The old account can be accessed via the "Standard" tab. The administrators

Commit a7908f2e authored by Thomas Witkowski's avatar Thomas Witkowski
Browse files

Work on heat demo and updated the documentation.

parent ef5ea94a
......@@ -42,7 +42,7 @@ heat->space->marker->ESThetaC: 0.05
heat->space->output->filename: output/heat
heat->space->output->ParaView format: 1
heat->space->output->ParaView animation: 1
heat->space->output->write every i-th timestep: 1
heat->space->output->write every i-th timestep: 10
heat->space->output->append index: 1
heat->space->output->index length: 6
heat->space->output->index decimals: 3
......
......@@ -42,7 +42,7 @@ heat->space->marker->MSGammaC: 0.1
heat->space->output->filename: output/heat
heat->space->output->ParaView format: 1
heat->space->output->ParaView animation: 1
heat->space->output->write every i-th timestep: 1
heat->space->output->write every i-th timestep: 10
heat->space->output->append index: 1
heat->space->output->index length: 6
heat->space->output->index decimals: 3
......
......@@ -43,7 +43,7 @@ heat->adapt->coarsen bisections: 3
heat->space->output->filename: output/heat
heat->space->output->ParaView format: 1
heat->space->output->ParaView animation: 1
heat->space->output->write every i-th timestep: 1
heat->space->output->write every i-th timestep: 10
heat->space->output->append index: 1
heat->space->output->index length: 6
heat->space->output->index decimals: 3
......
......@@ -67,9 +67,7 @@ public:
void setTime(AdaptInfo *adaptInfo)
{
ProblemInstat::setTime(adaptInfo);
rhsTime = adaptInfo->getTime() - (1 - theta) * adaptInfo->getTimestep();
boundaryTime = adaptInfo->getTime();
}
void closeTimestep(AdaptInfo *adaptInfo)
......@@ -120,17 +118,11 @@ public:
}
/// Returns pointer to \ref rhsTime.
double *getRHSTimePtr()
double *getRhsTimePtr()
{
return &rhsTime;
}
/// Returns pointer to \ref theta1.
double *getBoundaryTimePtr()
{
return &boundaryTime;
}
private:
/// Used for theta scheme.
double theta;
......@@ -141,9 +133,6 @@ private:
/// time for right hand side functions.
double rhsTime;
/// time for boundary functions.
double boundaryTime;
/// Pointer to boundary function. Needed for initial problem.
AbstractFunction<double, WorldVector<double> > *exactSolution;
};
......@@ -187,7 +176,7 @@ int main(int argc, char** argv)
// ===== create boundary functions =====
G *boundaryFct = new G;
boundaryFct->setTimePtr(heat.getBoundaryTimePtr());
boundaryFct->setTimePtr(heat.getTime());
heat.setExactSolution(boundaryFct);
heatSpace.addDirichletBC(1, boundaryFct);
......@@ -195,7 +184,7 @@ int main(int argc, char** argv)
// ===== create rhs functions =====
int degree = heatSpace.getFeSpace()->getBasisFcts()->getDegree();
F *rhsFct = new F(degree);
rhsFct->setTimePtr(heat.getRHSTimePtr());
rhsFct->setTimePtr(heat.getRhsTimePtr());
// ===== create operators =====
......
......@@ -190,11 +190,11 @@ The operators for the first problem are defined like in Section
\begin{lstlisting}{}
// ===== create operators for problem2 =====
Operator matrixOperator2(problem2.getFESpace());
Operator matrixOperator2(problem2.getFeSpace());
matrixOperator2.addZeroOrderTerm(new Simple_ZOT);
problem2.addMatrixOperator(&matrixOperator2);
Operator rhsOperator2(problem2.getFESpace());
Operator rhsOperator2(problem2.getFeSpace());
rhsOperator2.addZeroOrderTerm(new VecAtQP_ZOT(problem1.getSolution(),
new Identity(degree)));
problem2.addVectorOperator(&rhsOperator2);
......
......@@ -202,13 +202,13 @@ The operators now are defined as follows:
\begin{lstlisting}{}
// ===== create matrix operator =====
Operator matrixOperator(ellipt.getFESpace());
Operator matrixOperator(ellipt.getFeSpace());
matrixOperator.addSecondOrderTerm(new Laplace_SOT);
ellipt.addMatrixOperator(&matrixOperator);
// ===== create rhs operator =====
int degree = ellipt.getFESpace()->getBasisFcts()->getDegree();
Operator rhsOperator(ellipt.getFESpace());
int degree = ellipt.getFeSpace()->getBasisFcts()->getDegree();
Operator rhsOperator(ellipt.getFeSpace());
rhsOperator.addZeroOrderTerm(new CoordsAtQP_ZOT(new F(degree)));
ellipt.addVectorOperator(&rhsOperator);
\end{lstlisting}
......
......@@ -38,13 +38,6 @@ to the right hand side, the equation reads
\frac{u^{new}}{\tau} - \theta\Delta u^{new} = \frac{u^{old}}{\tau} + (1-\theta)\Delta u^{old} + f(\cdot, t^{old}+\theta\tau).
\end{equation}
%\begin{figure}[h]
%\center
%\includegraphics[width=0.5\textwidth]{fig/theta.pdf}
%\caption{Time discretization with the $\theta$ scheme.}
%\label{f:theta}
%\end{figure}
\begin{table}
\center
\begin{tabular}{|cc|c|c|c|}
......@@ -64,7 +57,17 @@ to the right hand side, the equation reads
\end{table}
\subsection{Source code}
Now, we describe the crucial parts of the source code. First, the functions $f$ and $g$ are defined. In contrast to the ellipt example, the functions now are time dependent. This is implemented by deriving the function classes also from class \verb+TimedObject+. This class provides a pointer to the current time, as well as corresponding setting and getting methods. The usage of a pointer to a real value allows to manage the current time in one location. All objects that deal with the same time, point to the same value. In our example, $f$ is evaluated at $t=t^{old}+\theta\tau$, while $g$ (the Dirichlet boundary function for $u^{new}$) is evaluated at $t=t^{new}$. Function $g$ is implemented as follows:
Now, we describe the crucial parts of the source code. First, the
functions $f$ and $g$ are defined. In contrast to the ellipt example,
the functions now are time dependent. This is implemented by deriving
the function classes also from class \verb+TimedObject+. This class
provides a pointer to the current time, as well as corresponding
setting and getting methods. The usage of a pointer to a real value
allows to manage the current time in one location. All objects that
deal with the same time, point to the same value. In our example, $f$
is evaluated at $t=t^{old}+\theta\tau$, while $g$ (the Dirichlet
boundary function for $u^{new}$) is evaluated at $t=t^{new}$. Function
$g$ is implemented as follows:
\begin{lstlisting}{}
class G : public AbstractFunction<double, WorldVector<double> >,
......@@ -78,7 +81,10 @@ public:
};
\end{lstlisting}
The variable $timePtr$ is a base class member of \verb+TimedObject+. This pointer has to be set once before $g$ is evaluated the first time. Implementation of function $f$ is done in the same way.
The variable $timePtr$ is a base class member of
\verb+TimedObject+. This pointer has to be set once before $g$ is
evaluated the first time. Implementation of function $f$ is done in
the same way.
\begin{figure}
\center
......@@ -86,11 +92,26 @@ The variable $timePtr$ is a base class member of \verb+TimedObject+. This pointe
\caption{UML diagram for class Heat.}
\label{f:heat_uml}
\end{figure}
Now, we begin with the implementation of class \verb+Heat+, that represents the instationary problem. In Figure \ref{f:heat_uml}, its class diagram is shown. \verb+Heat+ is derived from class \verb+ProblemInstatScal+ which leads to following properties:
Now, we begin with the implementation of class \verb+Heat+, that
represents the instationary problem. In Figure \ref{f:heat_uml}, its
class diagram is shown. \verb+Heat+ is derived from class
\verb+ProblemInstatScal+ which leads to following properties:
\begin{description}
\item \verb+Heat+ implements the \verb+ProblemTimeInterface+, so the adaptation loop can set the current time and schedule timesteps.
\item \verb+Heat+ implements \verb+ProblemStatBase+ in the role as initial (stationary) problem. The adaptation loop can compute the initial solution through this interface. The single iteration steps can be overloaded by sub classes of \verb+ProblemInstatScal+. Actually, the initial solution is computed through the method \verb+solveInitialProblem+ of \verb+ProblemTimeInterface+. But this method is implemented by \verb+ProblemInstatScal+ interpreting itself as initial stationary problem.
\item \verb+Heat+ knows another implementation of \verb+ProblemStatBase+: This other implementation represents a stationary problem which is solved within each timestep.\end{description}
\item \verb+Heat+ implements the \verb+ProblemTimeInterface+, so the
adaptation loop can set the current time and schedule timesteps.
\item \verb+Heat+ implements \verb+ProblemStatBase+ in the role as
initial (stationary) problem. The adaptation loop can compute the
initial solution through this interface. The single iteration steps
can be overloaded by sub classes of
\verb+ProblemInstatScal+. Actually, the initial solution is computed
through the method \verb+solveInitialProblem+ of
\verb+ProblemTimeInterface+. But this method is implemented by
\verb+ProblemInstatScal+ interpreting itself as initial stationary
problem.
\item \verb+Heat+ knows another implementation of
\verb+ProblemStatBase+: This other implementation represents a
stationary problem which is solved within each
timestep.\end{description}
The first lines of class \verb+Heat+ are:
......@@ -108,15 +129,21 @@ public:
}
\end{lstlisting}
The argument \verb+heatSpace+ is a pointer to the stationary problem which is solved each timestep. It is directly handed to the base class constructor of \verb+ProblemInstatScal+. In the body of the constructor, $\theta$ is read from the parameter file and stored in a member variable. The member variable \verb+theta1+ stores the value of $\theta - 1$. A pointer to this value is used later as factor in the $\theta$-scheme.
The argument \verb+heatSpace+ is a pointer to the stationary problem
which is solved each timestep. It is directly handed to the base class
constructor of \verb+ProblemInstatScal+. In the body of the
constructor, $\theta$ is read from the parameter file and stored in a
member variable. The member variable \verb+theta1+ stores the value of
$\theta - 1$. A pointer to this value is used later as factor in the
$\theta$-scheme.
The next lines show the implementation of the time interface.
\begin{lstlisting}{}
void setTime(AdaptInfo *adaptInfo)
{
ProblemInstat::setTime(adaptInfo);
rhsTime = adaptInfo->getTime() - (1 - theta) * adaptInfo->getTimestep();
boundaryTime = adaptInfo->getTime();
}
void closeTimestep(AdaptInfo *adaptInfo)
......@@ -127,10 +154,12 @@ The next lines show the implementation of the time interface.
\end{lstlisting}
The method \verb+setTime+ is called by the adaptation loop to inform
the problem about the current time. The right hand side function $f$
will be evaluated at $t^{old}+\theta\tau = t^{new} - (1-\theta)\tau$,
the Dirichlet boundary function $g$ at $t^{new}$, which is the
current time.
the problem about the current time. When this function is
reimplemented, one should always call the function in the parent
class, such that all time relavant variables in \verb+ProblemInstat+
will be updated. The right hand side function $f$ will be evaluated at
$t^{old}+\theta\tau = t^{new} - (1-\theta)\tau$, the Dirichlet
boundary function $g$ at $t^{new}$, which is the current time.
The method \verb+closeTimestep+ is called at the end of each timestep
by the adaptation loop. In the default implementation of
......@@ -190,13 +219,11 @@ Now, we define some getting functions and the private member variables:
double *getThetaPtr() { return &theta; };
double *getTheta1Ptr() { return &theta1; };
double *getRHSTimePtr() { return &rhsTime; };
double *getBoundaryTimePtr() { return &boundaryTime; };
private:
double theta;
double theta1;
double rhsTime;
double boundaryTime;
AbstractFunction<double, WorldVector<double> > *exactSolution;
};
\end{lstlisting}
......@@ -212,6 +239,7 @@ int main(int argc, char** argv)
// ===== init parameters =====
Parameters::init(false, argv[1]);
Parameters::readArgv(argc, argv);
// ===== create and init stationary problem =====
ProblemScal heatSpace("heat->space");
......@@ -257,14 +285,14 @@ The functions $f$ and $g$ are declared in the following way:
\begin{lstlisting}{}
// ===== create boundary functions =====
G *boundaryFct = new G;
boundaryFct->setTimePtr(heat.getBoundaryTimePtr());
boundaryFct->setTimePtr(heat.getTime());
heat.setExactSolution(boundaryFct);
heatSpace.addDirichletBC(1, boundaryFct);
// ===== create rhs functions =====
int degree = heatSpace.getFESpace()->getBasisFcts()->getDegree();
int degree = heatSpace.getFeSpace()->getBasisFcts()->getDegree();
F *rhsFct = new F(degree);
rhsFct->setTimePtr(heat.getRHSTimePtr());
rhsFct->setTimePtr(heat.getRhsTimePtr());
\end{lstlisting}
The functions interpreted as \verb+TimedObject+s are linked with the
corresponding time pointers by \verb+setTimePtr+. The boundary
......@@ -279,7 +307,7 @@ Now, we define the operators:
double zero = 0.0;
// create laplace
Operator A(heatSpace.getFESpace());
Operator A(heatSpace.getFeSpace());
A.addSecondOrderTerm(new Laplace_SOT);
A.setUhOld(heat.getOldSolution());
if (*(heat.getThetaPtr()) != 0.0)
......@@ -301,7 +329,7 @@ operator by \verb+setUhOld+.
\begin{lstlisting}{}
// create zero order operator
Operator C(heatSpace.getFESpace());
Operator C(heatSpace.getFeSpace());
C.addZeroOrderTerm(new Simple_ZOT);
C.setUhOld(heat.getOldSolution());
heatSpace.addMatrixOperator(C, heat.getInvTau(), heat.getInvTau());
......@@ -320,7 +348,7 @@ and the adaptation loop is started:
\begin{lstlisting}{}
// create RHS operator
Operator F(heatSpace.getFESpace());
Operator F(heatSpace.getFeSpace());
F.addZeroOrderTerm(new CoordsAtQP_ZOT(rhsFct));
heatSpace.addVectorOperator(F);
......@@ -356,39 +384,20 @@ heat->adapt->start time: 0.0
heat->adapt->end time: 1.0
\end{lstlisting}
Now, tolerances are determined:
Now, tolerances for the space and the time error are determined:
\begin{lstlisting}{}
heat->adapt->tolerance: 0.01
heat->adapt->rel space error: 0.5
heat->adapt->rel time error: 0.5
heat->adapt->time theta 1: 1.0
heat->adapt->time theta 2: 0.3
heat->adapt->tolerance: 0.05
heat->adapt->time tolerance: 0.05
\end{lstlisting}
The total tolerance is divided in a space tolerance and a time
tolerance. The space tolerance is the maximal allowed space error,
given by the product of \verb+tolerance+ and \verb+rel space error+. It is reached by adaptive mesh refinements. The time tolerance
is the maximal allowed error, due to the timestep size. It is given by
the product of \verb+tolerance+ and \verb+rel time error+ and
\verb+time theta 1+. It is relevant, only if an implicit time strategy
with adaptive timestep size is used. The parameter \verb+time theta 2+
is used to enlarge the timestep, if the estimated time error falls
beneath a given threshold.
\begin{lstlisting}{}
heat->adapt->strategy: 1
heat->adapt->time delta 1: 0.7071
heat->adapt->time delta 2: 1.4142
\end{lstlisting}
If \verb+strategy+ is $0$, an explicit time strategy with fixed
timestep size is used. A value of $1$ stands for the implicit
strategy. The time tolerance is reached by successively multiplying
the timestep with \verb+time delta 1+. If the estimated timestep error
is smaller than the product of \verb+tolerance+ and
\verb+rel time error+ and \verb+time theta 2+ at the end of a
timestep, the timestep size is multiplied by \verb+time delta 2+.
strategy.
The following lines determine, whether coarsening is allowed in
regions with sufficient small errors, and how many refinements or
......@@ -402,33 +411,34 @@ heat->adapt->coarsen bisections: 2
Now, the output behavior is determined:
\begin{lstlisting}{}
heat->space->output->filename: output/heat
heat->space->output->AMDiS format: 1
heat->space->output->AMDiS mesh ext: .mesh
heat->space->output->AMDiS data ext: .dat
heat->space->output->write every i-th timestep: 10
heat->space->output->append index: 1
heat->space->output->index length: 6
heat->space->output->index decimals: 3
heat->space->output->filename: output/heat
heat->space->output->ParaView format: 1
heat->space->output->ParaView animation: 1
heat->space->output->write every i-th timestep: 1
heat->space->output->append index: 1
heat->space->output->index length: 6
heat->space->output->index decimals: 3
\end{lstlisting}
In this example, all output filenames start with prefix
\verb+output/heat+ and end with the extensions \verb+.mesh+ and
\verb+.dat+. Output is written after every $10$th timestep. The time
of the single solution is added after the filename prefix with 6
letters, three of them are decimals. The solution for $t=0$ e.g. would
be written in the files \verb+output/heat00.000.mesh+ and
\verb+output/heat00.000.dat+.
Finally, we set parameter \verb+WAIT+ to $1$. So each call of the
macro \verb+WAIT+ in the application will lead to an interruption of
the program, until the \verb+return+ key is pressed.
\verb+output/heat+ and end with the extension \verb+.vtu+. Output is
written after every $10$th timestep. The time of the single solution
is added after the filename prefix with 6 letters, three of them are
decimals. The solution for $t=0$ e.g.\ would be written to the file
\verb+output/heat00.000.vtu+. If the parameter
\verb+ParaView animation+ is enabled, AMDiS writes for the whole
simulation one ParaView \verb+pvd+ file (in this case
\verb+output/heat.pvd+) including the names of all \verb+vtu+ files
that were created. This file makes it very easy to view and analyze
all the results of an instationary problem in ParaView.
Finally, we set parameter \verb+WAIT+ to $0$. If the variable is set
to $1$, each call of the macro \verb+WAIT+ in the application will
lead to an interruption of the program, until the \verb+return+ key is
pressed.
\begin{lstlisting}{}
WAIT: 1
WAIT: 0
\end{lstlisting}
\subsection{Macro file}
......@@ -437,8 +447,7 @@ described in Section \ref{s:ellipt macro}.
\subsection{Output}
As mentioned above, the output files look like
\verb+output/heat00.000.mesh+ and
\verb+output/heat00.000.dat+. Depending on the corresponding value in
\verb+output/heat00.000.vtu+. Depending on the corresponding value in
the parameter file only the solution after every $i$-th timestep is
written. In Figure \ref{f:heat}, the solution at three timesteps is
visualized.
......
\section{Nonlinear problem}
\label{ss:nonlinear problem}
We define the nonlinear problem
\begin{eqnarray}
-\Delta u + u^4 &=& f~~~\mbox{in } \Omega \subset \mathbb{R}^{dim}\\
u &=& g~~~ \mbox{on } \partial\Omega.
\end{eqnarray}
We choose the functions $f$ and $g$ so that the exact solution again is $u(x)=e^{-10x^2}$. This leads to
\begin{eqnarray}
f(x)&=& -\left(400-20dow\right)e^{-10x^2} + \left(e^{-10x^2}\right)^4\\
g(x)&=&e^{-10x^2},
\end{eqnarray}
with $dow$ the world dimension.
We linearize the problem using the Newton method. First, we define an
initial guess $u_0$ of the solution which is $0$ for the first
adaptation loop iteration. In later iterations we can use the solution
of the last iteration interpolated to the current mesh as initial
guess. In each Newton step, a correction $d$ for the solution of the
last step is computed by solving
\begin{eqnarray}
\label{eq:newton step}
DF(u_n)(d) = F(u_n)
\end{eqnarray}
for $d$, where $F(u):=-\Delta u + u^4 - f$ and
\begin{eqnarray}
DF(u_n)(d) &=& \lim_{h \rightarrow 0} \frac{F(u_n+hd)-F(u_n)}{h}\\
&=& \lim_{h \rightarrow 0} \frac{-\Delta u_n - h \Delta d + \Delta u_n}{h} + \lim_{h \rightarrow 0}\frac{(u_n+hd)^4-u_n^4}{h}\\
&=& -\Delta d + 4 u_n^3 d
\end{eqnarray}
the directional derivative of $F$ at $u_n$ along $d$.
Then the solution is updated:
\begin{equation}
u_{n+1} := u_n - d.
\end{equation}
We repeat this procedure until $||d||_{L^2} < tol$ with $tol$ a given tolerance for the Newton method.
In our example, equation (\ref{eq:newton step}) reads:
\begin{equation}
-\Delta d + 4 u_n^3 d = - \Delta u_n + u_n^4 - f.
\end{equation}
\begin{figure}
\center
\includegraphics[width=0.7\textwidth]{fig/nonlin_iteration.pdf}
\caption{Solve step of the nonlinear problem.}
\label{f:nonlin iteration}
\end{figure}
In Figure \ref{f:nonlin iteration}, the Newton method is illustrated.
\begin{table}
\center
\begin{tabular}{|cc|c|c|c|}
\hline
& & {\bf 1d} & {\bf 2d} & {\bf 3d} \\
\hline
{\bf source code} & \tt src/ & \multicolumn{3}{|c|}{\tt nonlin.cc} \\
\hline
{\bf parameter file} & \tt init/ & \tt nonlin.dat.1d & \tt nonlin.dat.2d & \tt nonlin.dat.3d \\
\hline
{\bf macro file} & \tt macro/ & \tt macro\_big.stand.1d & \tt macro\_big.stand.2d & \tt macro\_big.stand.3d \\
\hline
{\bf output files} & \tt output/ & \multicolumn{3}{|c|}{\tt nonlin.mesh, nonlin.dat} \\
\hline
\end{tabular}
\caption{Files of the {\tt nonlin} example.}
\end{table}
\subsection{Source code}
The main idea is to realize the Newton method as implementation of the {\it ProblemIterationInterface}, which replaces the standard iteration in the adaptation loop. The Newton method has to know the problem which has to be solved, as well as the implementation of one Newton step. Estimation and adaptation is done by the problem object. The assemble step and solve step are delegated to a Newton-step object.
Now, we describe the code step by step. The function $g$ is defined like in the previous examples. In the following, we define a zero-function which is later used to implement the Dirichlet boundary condition for the Newton-step implementation (at domain boundaries no correction has to be done). The function \verb+F+ implements the right hand side function $f$.
\begin{lstlisting}{}
class Zero : public AbstractFunction<double, WorldVector<double> >
{
public:
double operator()(const WorldVector<double>& x) const
{
return 0.0;
}
};
class F : public AbstractFunction<double, WorldVector<double> >
{
public:
/// Constructor
F(int degree)
: AbstractFunction<double, WorldVector<double> >(degree)
{}
/// Implementation of AbstractFunction::operator().
double operator()(const WorldVector<double>& x) const
{
int dow = x.getSize();
double r2 = x * x;
double ux = exp(-10.0 * r2);
double ux4 = ux * ux * ux * ux;
return ux4 -(400.0 * r2 - 20.0 * dow) * ux;
}
};
\end{lstlisting}
The class \verb+X3+ implements the function $u^3(x)$ used within the Newton step.
\begin{lstlisting}{}
class X3 : public AbstractFunction<double, double>
{
public:
X3() : AbstractFunction<double, double>(3) {}
/// Implementation of AbstractFunction::operator().
double operator()(const double& x) const
{
return x * x * x;
}
};
\end{lstlisting}
In the following, we define an interface which has to be implemented by the Newton-step object.
\begin{lstlisting}{}
class NewtonStepInterface
{
public:
virtual void initNewtonStep(AdaptInfo *adaptInfo) = 0;
virtual void exitNewtonStep(AdaptInfo *adaptInfo) = 0;
virtual void assembleNewtonStep(AdaptInfo *adaptInfo, Flag flag) = 0;
virtual void solveNewtonStep(AdaptInfo *adaptInfo) = 0;
virtual DOFVector<double> *getCorrection() = 0;
};
\end{lstlisting}
The \verb+initNewtonStep+ method is called before each Newton step, the method \verb+exitNewtonStep+ after each Newton step. \verb+assembleNewtonStep+ assembles the linear system of equations needed for the next step, \verb+solveNewtonStep+ solves this system of equations. The solution is the correction $d$. The method \verb+getCorrection+ returns a pointer to the vector storing the correction.
Now, the Newton method will be implemented. Actually, the class \verb+NewtonMethod+ replaces the whole iteration in the adaptation loop, including mesh adaptation and error estimation. The Newton method, which is a loop over Newton steps, is one part of this iteration.
\begin{lstlisting}{}
class NewtonMethod : public ProblemIterationInterface
{
public:
NewtonMethod(const char *name,
ProblemScal *problem,
NewtonStepInterface *step)
: problemNonlin(problem),
newtonStep(step),
newtonTolerance(1e-8),
newtonMaxIter(100)
{
GET_PARAMETER(0, std::string(name) + "->tolerance", "%f",
&newtonTolerance);
GET_PARAMETER(0, std::string(name) + "->max iteration", "%d",
&newtonMaxIter);
solution = problemNonlin->getSolution();
correction = newtonStep->getCorrection();
}
\end{lstlisting}
In the constructor, pointers to the nonlinear problem and to the Newton-step object are stored to the class members \verb+problemNonlin+ and \verb+newtonStep+. Furthermore, the parameters \verb+newtonTolerance+ and \verb+newtonMaxIter+ are initialized, and pointers to the nonlinear solution and to the correction vector are stored.
The following methods define one iteration in the adaptation loop.
\begin{lstlisting}{}
void beginIteration(AdaptInfo *adaptInfo)
{
FUNCNAME("NewtonMethod::beginIteration()");
MSG("\n");
MSG("begin of iteration %d\n", adaptInfo->getSpaceIteration()+1);
MSG("=============================\n");
}
Flag oneIteration(AdaptInfo *adaptInfo, Flag toDo = FULL_ITERATION)
{
Flag flag = 0, markFlag = 0;
if (toDo.isSet(MARK)) markFlag = problemNonlin->markElements(adaptInfo);
if (toDo.isSet(ADAPT) && markFlag.isSet(MESH_REFINED))
flag = problemNonlin->refineMesh(adaptInfo);
if (toDo.isSet(ADAPT) && markFlag.isSet(MESH_COARSENED))
flag |= problemNonlin->coarsenMesh(adaptInfo);
if (toDo.isSet(SOLVE)) {
newtonStep->initNewtonStep(adaptInfo);
int newtonIteration = 0;
double res = 0.0;
do {
newtonIteration++;
newtonStep->assembleNewtonStep(adaptInfo, flag);
newtonStep->solveNewtonStep(adaptInfo);
res = correction->L2Norm();
*solution -= *correction;
MSG("newton iteration %d: residual %f (tol: %f)\n",
newtonIteration, res, newtonTolerance);
} while ((res > newtonTolerance) && (newtonIteration < newtonMaxIter));
newtonStep->exitNewtonStep(adaptInfo);
}
if (toDo.isSet(ESTIMATE)) problemNonlin->estimate(adaptInfo);
return flag;
}
void endIteration(AdaptInfo *adaptInfo)
{
FUNCNAME("NewtonMethod::endIteration()");
MSG("\n");
MSG("end of iteration number: %d\n", adaptInfo->getSpaceIteration()+1);
MSG("=============================\n");
}
\end{lstlisting}
The methods \verb+beginIteration+ and \verb+endIteration+ only print some information to the standard output. In \verb+oneIteration+, the iteration, including the loop over the Newton steps, is defined.
Finally, the methods \verb+getNumProblems+ and \verb+getProblem+ are implemented to complete the \\\verb+ProblemIterationInterface+, and the private class members are defined.
\begin{lstlisting}{}
int getNumProblems() { return 1; };
ProblemStatBase *getProblem(int number = 0)
{
FUNCNAME("NewtonMethod::getProblem()");
if (number == 0) return problemNonlin;
ERROR_EXIT("invalid problem number\n");
return NULL;
}
private:
ProblemScal *problemNonlin;
NewtonStepInterface *newtonStep;
double newtonTolerance;