Commit 9627014c authored by Thomas Witkowski's avatar Thomas Witkowski
Browse files

* Added Tutorial

parent 8a8a4025
\section{Composite finite elements}
\subsection{Source code}
\subsection{Parameter file}
\subsection{Macro file}
\subsection{Output}
\ No newline at end of file
\section{Coupled problems}
\label{s:couple}
In this example, we solve the same problem as in Section \ref{s:vecellipt}, but here we treat the two equations as two coupled problems. The main difference is that the equations now aren't assembled into the same large system of equations, but into two separated systems of equations, that have to be solved separately. We define the two problems
\begin{eqnarray}
-\Delta u &=& f ~~~\mbox{in } \Omega \subset \mathbb{R}^{dim}\\
u &=& g ~~~ \mbox{on } \partial\Omega
\end{eqnarray}
and
\begin{equation}
v = u.
\end{equation}
We first solve the first problem and then use its solution to solve the second problem. This happens in every iteration of the adaptation loop. Both problems should use the same mesh. Mesh adaptation is done by the first problem. So one iteration now looks like illustrated in Figure \ref{f:coupled iteration}.
\begin{figure}
\center
\includegraphics[width=0.9\textwidth]{fig/coupled_iteration.pdf}
\caption{State diagram of the coupled iteration.}
\label{f:coupled iteration}
\end{figure}
\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 couple.cc} \\
\hline
{\bf parameter file} & \tt init/ & \tt couple.dat.1d & \tt couple.dat.2d & \tt couple.dat.3d \\
\hline
{\bf macro file} & \tt macro/ & \tt macro.stand.1d & \tt macro.stand.2d & \tt macro.stand.3d \\
\hline
{\bf output files} & \tt output/ & \multicolumn{3}{|c|}{\tt couple.mesh, couple.dat} \\
\hline
\end{tabular}
\caption{Files of the {\tt couple} example.}
\end{table}
\subsection{Source code}
In the previous examples, the iteration was implemented always by the corresponding problem class. In this example, we want to couple two problems within one iteration, so the default implementation can't be used. For that reason, we define our own coupled iteration class\\ \verb+MyCoupledIteration+ which implements the \verb+ProblemIterationInterface+:
\begin{lstlisting}{}
class MyCoupledIteration : public ProblemIterationInterface
{
public:
MyCoupledIteration(ProblemStatBase *prob1,
ProblemStatBase *prob2)
: problem1(prob1),
problem2(prob2)
{};
\end{lstlisting}
In the constructor pointers to the two problems are assigned to the private members \verb+problem1+ and \verb+problem2+. Note that the pointers point to the interface \verb+ProblemStatBase+ and not to\\ \verb+ProblemScal+. This leads to a more general implementation. If e.g. two vector valued problems should be coupled in the future, we could use our iteration class without modifications.
Now, we implement the needed interface methods:
\begin{lstlisting}{}
void beginIteration(AdaptInfo *adaptInfo)
{
FUNCNAME("StandardProblemIteration::beginIteration()");
MSG("\n");
MSG("begin of iteration %d\n", adaptInfo->getSpaceIteration()+1);
MSG("=============================\n");
};
void endIteration(AdaptInfo *adaptInfo) {
FUNCNAME("StandardProblemIteration::endIteration()");
MSG("\n");
MSG("end of iteration %d\n", adaptInfo->getSpaceIteration()+1);
MSG("=============================\n");
};
\end{lstlisting}
These two functions are called at the beginning and at the end of each iteration. Here, we only prompt some output.
The method \verb+oneIteration+ is the crucial part of our implementation:
\begin{lstlisting}{}
Flag oneIteration(AdaptInfo *adaptInfo, Flag toDo = FULL_ITERATION)
{
Flag flag, markFlag;
if(toDo.isSet(MARK)) markFlag = problem1->markElements(adaptInfo);
if(toDo.isSet(ADAPT) && markFlag.isSet(MESH_REFINED)) {
flag = problem1->refineMesh(adaptInfo);
}
if(toDo.isSet(BUILD)) problem1->buildAfterCoarsen(adaptInfo, markFlag);
if(toDo.isSet(SOLVE)) problem1->solve(adaptInfo);
if(toDo.isSet(BUILD)) problem2->buildAfterCoarsen(adaptInfo, markFlag);
if(toDo.isSet(SOLVE)) problem2->solve(adaptInfo);
if(toDo.isSet(ESTIMATE)) problem1->estimate(adaptInfo);
return flag;
};
\end{lstlisting}
The \verb+toDo+ flag is used by the adaptation loop to determine which parts of the iteration should be performed. The first iteration is always an iteration without mesh adaptation (see Figure \ref{f:stationary loop}). So we start our iteration by marking and adapting the mesh. The mesh and its adaptation is managed by the first problem. So we call \verb+markElements+ and \verb+refineMesh+ of \verb+problem1+. Note that no mesh coarsenings have to be performed in our example. Afterwards, \verb+problem1+ assembles its system of equations by \verb+buildAfterCoarsen+. Assemblage and mesh adaptation are nested operations in AMDiS (\verb+buildBeforeRefine+, \verb+refineMesh+, \verb+buildBeforeCoarsen+,\\\verb+coarsenMesh+,\verb+buildAfterCoarsen+). Here, we implement a simplified version.
After \verb+problem1+ has solved its system of equations, \verb+problem2+ can assemble and solve its equations system using the solution of the first problem as right hand side. In the method \verb+oneIteration+, only the order of method calls is determined. The dependency to the solution of the first problem is created later when the operator for the right hand side of \verb+problem2+ is created.
After also the second problem computed its solution, \verb+problem1+ does the error estimation (remember: mesh adaptation is managed by \verb+problem1+).
\begin{figure}
\center
\includegraphics[width=0.9\textwidth]{fig/stationary_loop.pdf}
\caption{Stationary adaptation loop.}
\label{f:stationary loop}
\end{figure}
Now, the access to the coupled problems is implemented and the member variables are defined:
\begin{lstlisting}{}
int getNumProblems()
{
return 2;
};
ProblemStatBase *getProblem(int number = 0)
{
FUNCNAME("CoupledIteration::getProblem()");
if(number == 0) return problem1;
if(number == 1) return problem2;
ERROR_EXIT("invalid problem number\n");
return NULL;
};
private:
ProblemStatBase *problem1;
ProblemStatBase *problem2;
};
\end{lstlisting}
The class \verb+MyCoupledIteration+ is finished now.
The next class, \verb+Identity+, implements the identity $I(x)=x$ for \verb+double+ variables. An arbitrary degree can be given to it. The class is used later to determine the quadrature degree used for the right hand side of \verb+problem2+.
\begin{lstlisting}{}
class Identity : public AbstractFunction<double, double>
{
public:
MEMORY_MANAGED(Identity);
Identity(int degree) : AbstractFunction<double, double>(degree) {};
const double& operator()(const double& x) const {
static double result;
result = x;
return result;
};
};
\end{lstlisting}
Now, we start with the main program:
\begin{lstlisting}{}
int main(int argc, char* argv[])
{
FUNCNAME("main");
TEST_EXIT(argc == 2)("usage: couple initfile\n");
Parameters::init(true, argv[1]);
// ===== create and init the first problem =====
ProblemScal problem1("problem1");
problem1.initialize(INIT_ALL);
// ===== add boundary conditions for problem1 =====
problem1.addDirichletBC(1, NEW G);
\end{lstlisting}
So far, we created and initialized \verb+problem1+ and its boundary conditions.
Now, we create \verb+problem2+. It should have its own finite element space, system, solver and file writer, but the mesh should be adopted from \verb+problem1+.
\begin{lstlisting}{}
// ===== create and init the second problem =====
Flag initFlag =
INIT_FE_SPACE |
INIT_SYSTEM |
INIT_SOLVER |
INIT_FILEWRITER;
Flag adoptFlag =
CREATE_MESH |
INIT_MESH;
ProblemScal problem2("problem2");
problem2.initialize(initFlag,
&problem1,
adoptFlag);
\end{lstlisting}
The operators for the first problem are defined like in Section \ref{s:ellipt code}. Here, we only show the operators of \verb+problem2+.
\begin{lstlisting}{}
// ===== create operators for problem2 =====
Operator matrixOperator2(Operator::MATRIX_OPERATOR,
problem2.getFESpace());
matrixOperator2.addZeroOrderTerm(NEW Simple_ZOT);
problem2.addMatrixOperator(&matrixOperator2);
Operator rhsOperator2(Operator::VECTOR_OPERATOR, problem2.getFESpace());
rhsOperator2.addZeroOrderTerm(NEW VecAtQP_ZOT(problem1.getSolution(),
NEW Identity(degree)));
problem2.addVectorOperator(&rhsOperator2);
\end{lstlisting}
At the left hand side, we have just an ordinary \verb+Simple_ZOT+. At the right hand side, we have a zero order term of the form $f(u)$ with $f=I$ the identity. $u$ is given by the solution DOF vector of \verb+problem1+. $I$ maps the values of the DOF vector evaluated at quadrature points to itself. The function degree is used to determine the needed quadrature degree in the assembler.
Now, the adaptation loop is created:
\begin{lstlisting}{}
// ===== create adaptation loop and iteration interface =====
AdaptInfo *adaptInfo = NEW AdaptInfo("couple->adapt", 1);
MyCoupledIteration coupledIteration(&problem1, &problem2);
AdaptStationary *adapt = NEW AdaptStationary("couple->adapt",
&coupledIteration,
adaptInfo);
\end{lstlisting}
Note that not a pointer to one of the problems is passed to the adaptation loop, but a pointer to the \verb+coupledIteration+ object, which in turn knows both problems.
The adaptation loop is now started. After it is finished, the solutions of both problems are written.
\begin{lstlisting}{}
// ===== start adaptation loop =====
adapt->adapt();
// ===== write solution =====
problem1.writeFiles(adaptInfo, true);
problem2.writeFiles(adaptInfo, true);
}
\end{lstlisting}
\subsection{Parameter file}
We have one adaptation loop called \verb+couple->adapt+:
\begin{lstlisting}{}
couple->adapt->tolerance: 1e-8
couple->adapt->max iteration: 10
couple->adapt->refine bisections: 2
\end{lstlisting}
The coupled problem consits of two sub problems. The first problem creates the mesh, solves its linear system of equations, estimates the error, adapts the mesh, and finally writes its output:
\begin{lstlisting}{}
coupleMesh->macro file name: ./macro/macro.stand.2d
coupleMesh->global refinements: 0
problem1->mesh: coupleMesh
problem1->dim: 2
problem1->polynomial degree: 1
problem1->solver: cg % no, bicgstab, cg, gmres, odir, ores
problem1->solver->max iteration: 1000
problem1->solver->tolerance: 1.e-8
problem1->solver->left precon: diag
problem1->estimator: residual
problem1->estimator->C0: 0.1 % constant of element residual
problem1->estimator->C1: 0.1 % constant of jump residual
problem1->marker->strategy: 2 % 0: no 1: GR 2: MS 3: ES 4:GERS
problem1->marker->MSGamma: 0.5
problem1->output->filename: output/problem1
problem1->output->AMDiS format: 1
problem1->output->AMDiS mesh ext: .mesh
problem1->output->AMDiS data ext: .dat
\end{lstlisting}
The second problem uses the mesh of \verb+problem1+. So it creates no mesh, no estimator, and no marker. But a solver is needed to solve \verb+problem2+s linear system of equations, and a file writer to write the solution:
\begin{lstlisting}{}
problem2->dim: 2
problem2->polynomial degree: 1
problem2->solver: cg % no, bicgstab, cg, gmres, odir, ores
problem2->solver->max iteration: 1000
problem2->solver->tolerance: 1.e-8
problem2->solver->left precon: diag
problem2->estimator: no
problem2->marker->strategy: 0
problem2->output->filename: output/problem2
problem2->output->AMDiS format: 1
problem2->output->AMDiS mesh ext: .mesh
problem2->output->AMDiS data ext: .dat
\end{lstlisting}
\subsection{Macro file}
We again use the macro file \verb+macro/macro.stand.2d+, which was described in Section \ref{s:ellipt macro}.
\subsection{Output}
The solution of the first problem is written to the files \verb+output/problem1.mesh+ and\\ \verb+output/problem1.dat+.
The solution of the second problem is written to the files\\ \verb+output/problem2.mesh+ and \verb+output/problem2.dat+.
We don't visualize the results here, because they conform with the results showed in Section \ref{s:vecellipt output}.
\section{Stationary problem with Dirichlet boundary condition}
\label{s:ellipt}
As example for a stationary problem, we choose the Poisson equation
\begin{eqnarray}
\label{eq:ellipt}
-\Delta u &=& f ~~~\mbox{in } \Omega \subset \mathbb{R}^{dim}\\
u &=& g ~~~ \mbox{on } \partial\Omega
\end{eqnarray}
with
\begin{eqnarray}
f(x) &=& -\left( 400x^2 - 20 dow \right) e^{-10x^2}\\
g(x) &=& e^{-10x^2}.
\end{eqnarray}
$dim$ is the problem dimension and thus the dimension of the mesh the problem will be discretized on. $dow$ is the dimension of the world the problem lives in. So world coordinates are always real valued vectors of size $dow$. Note that the problem is defined in a dimension independent way. Furthermore, $dow$ has not to be equal to $dim$ as long as $1 \leq dim \leq dow \leq 3$ holds.
Although the implementation described in Section \ref{s:ellipt code} is dimension independent, we focus on the case $dim = dow = 2$ for the rest of this section. The analytical solution on $\Omega = [0,1] \times [0,1]$ is shown in Figure \ref{f:ellipt solution}.
\begin{figure}
\center
\includegraphics[width=0.3\textwidth]{fig/ellipt.jpg}
\caption{Solution of the Poisson equation on the unit square.}
\label{f:ellipt solution}
\end{figure}
\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 ellipt.cc} \\
\hline
{\bf parameter file} & \tt init/ & \tt ellipt.dat.1d & \tt ellipt.dat.2d & \tt ellipt.dat.3d \\
\hline
{\bf macro file} & \tt macro/ & \tt macro.stand.1d & \tt macro.stand.2d & \tt macro.stand.3d \\
\hline
{\bf output files} & \tt output/ & \multicolumn{3}{|c|}{\tt ellipt.mesh, ellipt.dat} \\
\hline
\end{tabular}
\caption{Files of the {\tt ellipt} example.}
\label{t:ellipt}
\end{table}
\subsection{Source code}
\label{s:ellipt code}
For this first example, we give the complete source code. But to avoid loosing the overview, we sometimes interrupt the code to give some explaining comment. The first three lines of the application code are:
\begin{lstlisting}{}
#include "AMDiS.h"
using namespace std;
using namespace AMDiS;
\end{lstlisting}
In the first line, the AMDiS header is included. In line 2 and 3, used namespaces are introduced. \verb+std+ is the C++ standard library namespace, used e.g.\ for the STL classes. AMDiS provides its own namespace \verb+AMDiS+ to avoid potential naming conflicts with other libraries.
Now, the functions $f$ and $g$ will be defined by the classes \verb+F+ and \verb+G+:
\begin{lstlisting}{}
// ===== function definitions =====
class G : public AbstractFunction<double, WorldVector<double> >
{
public:
MEMORY_MANAGED(G);
double operator()(const WorldVector<double>& x) const
{
return exp(-10.0 * (x * x));
}
};
\end{lstlisting}
\verb+G+ is a sub class of the templated class
\verb+AbstractFunction<R, T>+ that represents a mapping from type
\verb+T+ to type \verb+R+. Here, we want to define a mapping from
$\mathbb{R}^{dow}$, implemented by the class
\verb+WorldVector<double>+, to $\mathbb{R}$, represented by the data
type \verb+double+. The actual mapping is defined by overloading the
\verb+operator()+. \verb+x*x+ stands for the scalar product of vector
\verb+x+ with itself.
Using the macro call \verb+MEMORY_MANAGED(G)+, the class will be
managed by the memory management of AMDiS. This memory management
provides memory monitoring to locate memory leaks and block memory
allocation to accelerate memory access. Objects of a memory managed
class can now be allocated through the memory manager by the macro
\verb+NEW+ and deallocated by the macro \verb+DELETE+.
The class \verb+F+ is defined in a similar way:
\begin{lstlisting}{}
class F : public AbstractFunction<double, WorldVector<double> >
{
public:
MEMORY_MANAGED(F);
F(int degree) : AbstractFunction<double, WorldVector<double> >(degree) {};
double operator()(const WorldVector<double>& x) const {
int dow = Global::getGeo(WORLD);
double r2 = (x * x);
double ux = exp(-10.0 * r2);
return -(400.0 * r2 - 20.0 * dow) * ux;
}
};
\end{lstlisting}
\verb+F+ gets the world dimension from the class \verb+Global+ by a call of the static function \verb+getGeo(WORLD)+. The degree handed to the constructor determines the polynomial degree with which the function should be considered in the numerical integration. A higher degree leads to a quadrature of higher order in the assembling process.
Now, we start with the main program:
\begin{lstlisting}{}
// ===== main program =====
int main(int argc, char* argv[])
{
FUNCNAME("main");
// ===== check for init file =====
TEST_EXIT(argc == 2)("usage: ellipt initfile\n");
// ===== init parameters =====
Parameters::init(true, argv[1]);
\end{lstlisting}
The macro \verb+FUNCNAME+ defines the current function name that is used for command line output, e.g. in error messages. The macro \verb+TEST_EXIT+ tests for the condition within the first pair of brackets. If the condition does not hold, an error message given in the second bracket pair is prompted and the program exits. Here the macro is used to check, whether the parameter file was specified by the user as command line argument. If this is the case, the parameters are initialized by \verb+Parameters::init(true, argv[1])+. The first argument specifies, whether the initialized parameters should be printed after initialization for debug reasons. The second argument is the name of the parameter file.
Now, a scalar problem with name \verb+ellipt+ is created and initialized:
\begin{lstlisting}{}
// ===== create and init the scalar problem =====
ProblemScal ellipt("ellipt");
ellipt.initialize(INIT_ALL);
\end{lstlisting}
The name argument of the problem is used to identify parameters in the parameter file that belong to this problem. In this case, all parameters with prefix \verb+ellipt->+ are associated to this problem. The initialization argument \verb+INIT_ALL+ means that all problem modules are created in a standard way. Those are: The finite element space including the corresponding mesh, needed system matrices and vectors, an iterative solver, an estimator, a marker, and a file writer for the computed solution. The initialization of these components can be controlled through the parameter file (see Section \ref{s:ellipt parameter}).
The next steps are the creation of the adaptation loop and the corresponding \verb+AdaptInfo+:
\begin{lstlisting}{}
// === create adapt info ===
AdaptInfo *adaptInfo = NEW AdaptInfo("ellipt->adapt", 1);
// === create adapt ===
AdaptStationary *adapt = NEW AdaptStationary("ellipt->adapt", &ellipt,
adaptInfo);
\end{lstlisting}
The \verb+AdaptInfo+ object contains information about the current state of the adaptation loop as well as user given parameters concerning the adaptation loop, like desired tolerances or maximal iteration numbers. Using \verb+adaptInfo+, the adaptation loop can be inspected and controlled at runtime.
Now, a stationary adaptation loop is created, which implements the standard {\it assemble-solve-estimate-adapt} loop. Arguments are the name, again used as parameter prefix, the problem as implementation of an iteration interface, and the \verb+AdaptInfo+ object. The adaptation loop only knows when to perform which part of an iteration. The implementation and execution of the single steps is delegated to an iteration interface, here implemented by the scalar problem \verb+ellipt+.
Now, we define boundary conditions:
\begin{lstlisting}{}
// ===== add boundary conditions =====
ellipt.addDirichletBC(1, NEW G);
\end{lstlisting}
We have one Dirichlet boundary condition associated with identifier $1$. All nodes belonging to this boundary are set to the value of function \verb+G+ at the corresponding coordinates. In the macro file (see Section \ref{s:ellipt macro}) the Dirichlet boundary is marked with identifier $1$, too. So the nodes can be uniquely determined.
The operators now are defined as follows:
\begin{lstlisting}{}
// ===== create matrix operator =====
Operator matrixOperator(Operator::MATRIX_OPERATOR, ellipt.getFESpace());
matrixOperator.addSecondOrderTerm(NEW Laplace_SOT);
ellipt.addMatrixOperator(&matrixOperator);
// ===== create rhs operator =====
int degree = ellipt.getFESpace()->getBasisFcts()->getDegree();
Operator rhsOperator(Operator::VECTOR_OPERATOR, ellipt.getFESpace());
rhsOperator.addZeroOrderTerm(NEW CoordsAtQP_ZOT(NEW F(degree)));
ellipt.addVectorOperator(&rhsOperator);
\end{lstlisting}
First, we define a matrix operator (left hand side operator) on the finite element space of the problem. Now, we add the term $-\Delta u$ to it. Note that the minus sign isn't explicitly given, but implicitly contained in \verb+Laplace_SOT+. With \verb+addMatrixOperator+ we add the operator to the problem. The definition of the right hand side is done in a similar way. We choose the degree of our function to be equal to the current basis function degree.
Finally we start the adaptation loop and afterwards write out the results:
\begin{lstlisting}{}
// ===== start adaption loop =====
adapt->adapt();
// ===== write result =====
ellipt.writeFiles(adaptInfo, true);
}
\end{lstlisting}
The second argument of \verb+writeFiles+ forces the file writer to print out the results. In time dependent problems it can be useful to write the results only every $i$-th timestep. To allow this behavior the second argument has to be \verb+false+.
\subsection{Parameter file}
\label{s:ellipt parameter}
The name of the parameter file must be given as command line argument. In the 2d case we call:
\begin{lstlisting}{}
> ./ellipt init/ellipt.dat.2d
\end{lstlisting}
In the following, the content of file \verb+init/ellipt.dat.2d+ is described:
\begin{lstlisting}{}
dimension of world: 2
elliptMesh->macro file name: ./macro/macro.stand.2d
elliptMesh->global refinements: 0
\end{lstlisting}
The dimension of the world is 2, the macro mesh with name \verb+elliptMesh+ is defined in file \\\verb+./macro/macro.stand.2d+ (see Section \ref{s:ellipt macro}). The mesh is not globally refined before the adaptation loop. A value of $n$ for \verb+elliptMesh->global refinements+ means $n$ bisections of every macro element. Global refinements before the adaptation loop can be useful to save computation time by starting adaptive computations with a finer mesh.
\begin{lstlisting}{}
ellipt->mesh: elliptMesh
ellipt->dim: 2
ellipt->polynomial degree: 3
\end{lstlisting}
Now, we construct the finite element space for the problem \verb+ellipt+ (see Section \ref{s:ellipt code}). We use the mesh \verb+elliptMesh+, set the problem dimension to 2, and choose Lagrange basis functions of degree 3.
\begin{lstlisting}{}
ellipt->solver: cg % no bicgstab cg gmres odir ores
ellipt->solver->max iteration: 1000
ellipt->solver->tolerance: 1.e-8
ellipt->solver->left precon: diag % no, diag
\end{lstlisting}
We use the {\it conjugate gradient method} as iterative solver. The solving process stops after maximal $1000$ iterations or when a tolerance of $10^{-8}$ is reached. Furthermore, we apply diagonal pre-conditioning.
\begin{lstlisting}{}
ellipt->estimator: residual % residual, recovery
ellipt->estimator->error norm: 1
ellipt->estimator->C0: 0.1
ellipt->estimator->C1: 0.1
\end{lstlisting}
As error estimator we use the residual method. The used error norm is the H1-norm (instead of the L2-norm: 2). Element residuals (C0) and jump residuals (C1) both are weighted by factor $0.1$.
\begin{lstlisting}{}
ellipt->marker->strategy: 2 % 0: no 1: GR 2: MS 3: ES 4:GERS
ellipt->marker->MSGamma: 0.5
\end{lstlisting}
After error estimation, elements are marked for refinement and coarsening. Here, we use the maximum strategy with $\gamma = 0.5$.
\begin{lstlisting}{}
ellipt->adapt->tolerance: 1e-4
ellipt->adapt->max iteration: 100
ellipt->adapt->refine bisections: 2
\end{lstlisting}
The adaptation loop stops, when an error tolerance of $10^{-4}$ is reached, or after maximal $100$ iterations. An element that is marked for refinement, is bisected twice within one iteration. Analog elements that are marked for coarsening are coarsened twice per iteration.
\begin{lstlisting}{}
ellipt->output->filename: output/ellipt
ellipt->output->AMDiS format: 1
ellipt->output->AMDiS mesh ext: .mesh
ellipt->output->AMDiS data ext: .dat
\end{lstlisting}
The result is written in AMDiS-format to the files \verb+output/ellipt.mesh+ and \verb+output/ellipt.dat+. The first contains the final mesh, the second contains the corresponding solution values.
\subsection{Macro file}
\label{s:ellipt macro}
\begin{figure}
\center
\includegraphics[width=0.4\textwidth]{fig/ellipt_macro.pdf}
\caption{Two dimensional macro mesh}
\label{f:ellipt macro}
\end{figure}
In Figure \ref{f:ellipt macro} one can see the macro mesh which is described by the file \verb+macro/macro.stand.2d+.
First, the dimension of the mesh and of the world are defined:
\begin{lstlisting}{}
DIM: 2
DIM_OF_WORLD: 2
\end{lstlisting}
Then the total number of elements and vertices are given:
\begin{lstlisting}{}
number of elements: 4
number of vertices: 5
\end{lstlisting}
The next block describes the two dimensional coordinates of the five vertices:
\begin{lstlisting}{}
vertex coordinates:
0.0 0.0
1.0 0.0
1.0 1.0
0.0 1.0
0.5 0.5
\end{lstlisting}
The first two numbers are interpreted as the coordinates of vertex 0, and so on.
Corresponding to these vertex indices now the four triangles are given:
\begin{lstlisting}{}
element vertices:
0 1 4
1 2 4
2 3 4
3 0 4
\end{lstlisting}
Element 0 consists in the vertices 0, 1 and 4. The numbering is done anticlockwise starting with the vertices of the longest edge.
It follows the definition of boundary conditions:
\begin{lstlisting}{}
element boundaries:
0 0 1
0 0 1
0 0 1
0 0 1
\end{lstlisting}
The first number line means that element 0 has no boundaries at edge 0 and 1, and a boundary with identifier 1 at edge 2. The edge with number $i$ is the edge opposite to vertex number $i$. The boundary identifier 1 corresponds to the identifier 1 we defined within the source code for the Dirichlet boundary. Since all elements of the macro mesh have a Dirichlet boundary at edge 2, the line \verb+0 0 1+ is repeated three times.
The next block defines element neighborships. \verb+-1+ means there is no neighbor at the corresponding edge. A non-negative number determines the index of the neighbor element.
\begin{lstlisting}{}
element neighbours:
1 3 -1
2 0 -1
3 1 -1
0 2 -1
\end{lstlisting}
This block is optional. If it isn't given in the macro file, element neighborships are computed automatically.