% This is LLNCS.DEM the demonstration file of
% the LaTeX macro package from Springer-Verlag
% for Lecture Notes in Computer Science,
% version 2.2 for LaTeX2e
%
\documentclass{llncs}
\usepackage{moreverb,relsize,ttname}
\usepackage{bm}
\usepackage{amssymb}
\usepackage{amsmath}




\newcommand{\mathbfx}[1]{\bm #1}
\newcommand{\FF}{\mathbfx{F}}
\newcommand{\JJ}{\mathbfx{J}}
\newcommand{\uu}{\mathbfx{u}}
\newcommand{\vv}{\mathbfx{v}}
\newcommand{\VV}{\mathbfx{V}}
\newcommand{\GG}{\mathbfx{G}}
\newcommand{\nn}{\mathbfx{n}}
\newcommand{\ttt}{\mathbfx{t}}
\newcommand{\ff}{\mathbfx{f}}
\newcommand{\xx}{\mathbfx{x}}
\newcommand{\xxi}{\mathbfx{\xi}}
\newcommand{\NN}{\mathbfx{N}}
\renewcommand{\P}{{\mathbb P}}
\newcommand{\curl}{\operatorname{curl}}
\renewcommand{\div}{\operatorname{div}}

\long\def\COMMENT#1{\par\vbox{\hrule\vskip1pt#1\vskip1pt\hrule}}
\newcommand{\emp}[1]{{\tt #1}}


%
\begin{document}
%

\pagestyle{headings} 
\mainmatter              % start of the contributions
%
\title{SyFi - An Element Matrix Factory}
%
\titlerunning{SyFi - An Element Matrix Factory}  % abbreviated title (for running head)
%                                     also used for the TOC unless
%                                     \toctitle is used
%
\author{Kent-Andre Mardal}
%
\authorrunning{Kent-Andre Mardal}   % abbreviated author list (for running head)
%
%%%% modified list of authors for the TOC (add the affiliations)
\tocauthor{Kent-Andre Mardal (Simula Research Laboratory)}
%
\institute{Simula Research Laboratory, P.O. Box 134, NO-1325 Lysaker, Norway,\\
\email{kent-and@simula.no},\\ WWW home page:
\texttt{http://www.simula.no/portal\_memberdata/kent-and}
}
\maketitle              % typeset the title of the contribution


\begin{abstract}
SyFi  is an open source C++ library for defining and using 
variational forms and 
finite elements based on  symbolic representations of polygonal domains, 
degrees of freedom and polynomial spaces. Once the finite elements
and variational forms are defined, they are used to generate efficient C/C++ code.    
\end{abstract}
%


\section{Introduction}
\label{sec:intro}

SyFi~\cite{SyFi}, which stands for Symbolic Finite Elements, is 
a C++ library for finite element computations. 
SyFi is equipped with a Python interface by using SWIG~\cite{SWIG}. 
It relies on the symbolic mathematics library GiNaC~\cite{GiNaC}
and the Python interface to GiNaC called Swiginac~\cite{Swiginac}. 
SyFi is open source as its dependencies GiNaC and Swiginac. 

This paper is only a short overview of the SyFi project in the
context of finite element methods for the incompressible Navier-Stokes equations. 
A more comprehensive description of the project can be found on 
its webpage \url{http://syfi.sf.net}, which contains a tutorial, 
a complete reference and the source code. We will show various code snippets 
in this paper. The complete code examples can be found in the 
subdirectory \emp{para06} 
in the SyFi source code tree. 


There are quite a few other projects that are similar in various
respects to SyFi. Within the FEniCS~\cite{FEniCS} project there
are two Python projects: FIAT~\cite{FIAT} and FFC~\cite{FFC}. 
FIAT is a Python module 
for defining finite elements while FFC generates C++ code 
based on a high--level Python description of variational forms.    
The DSEL project~\cite{DSEL}  employs high--level
C++ programming techniques such as expression templates 
and meta-programming for defining variational forms, performing
automatic differentiation, interpolation and more.
Sundance~\cite{sundance} is a C++ library with a powerful symbolic engine 
which supports automatic generation of a discrete system 
for a given variational form. Analysa~\cite{analysa}, GetDP~\cite{getdp}, 
and FreeFem++~\cite{FreeFem}  define domain-specific languages
for finite element computations. 
The main difference between SyFi and the other projects is that
it uses a high level symbolic framework in Python to 
generate efficient C/C++ code. 






A key point in the design of SyFi is, as already mentioned, 
 that we want to employ symbolic
mathematics and code generation in place of the numerics. 
The powerful symbolic engine GiNaC and the combination of the high--level
languages C++ and Python have so far proven to be a solid platform.  
Consider for instance, the computation of an entry in the mass matrix  
\begin{equation}
\label{mass:matrix}
A_{ij} = \int_{\hat{T}} N_i N_j \, D \, dx,  
\end{equation}
where $D$ is the Jacobian of the geometry mapping between the reference
element $\hat{T}$ and the global element $T$, and $\{N_i\}_i$ are the finite element basis functions. 
If the geometry mapping is affine, 
the computation of one matrix entry based on \eqref{mass:matrix} 
will result in a real number times $D$. 
Because everything but the multiplication with $D$ 
(in case of a mass matrix) can be precomputed, 
the generated code will be very efficient compared to  
traditional codes, which typically implements a loop over quadrature points and numerical
evaluation of the finite element basis functions. See also \emp{mass.py} for
a demonstration of such code generation. 


As will be explained later, other advantages of this approach include  
an easy way of defining finite elements, and straightforward computation
of the Jacobian in the case of nonlinear PDEs. 

\section{Using Finite Elements and Evaluating Variational Forms}
\label{sec:variational}

One main goal with SyFi has been that it should be a tool
with \emph{strong support for differentiation and integration of
polynomials on polygonal domains}, which are basic ingredients
both when defining finite elements and using finite elements to define variational forms. 
Many finite elements have been implemented in SyFi. 
Of particular importance for the simulation of incompressible fluids
are the continuous and discontinuous Lagrangian elements of 
arbitrary order and the Crouzeix-Raviart element~\cite{CR}. However, 
also the $H(\div)$-Raviart-Thomas elements~\cite{RT} and the $H(\curl)$- 
Nedelec elements ~\cite{Nedelec:1980:MFE,Nedelec:1986:NFM} of arbitrary order have been implemented. 
We will come back to the construction of finite elements
in Section \ref{sec:element}. In this section we concentrate on the usage of 
already implemented elements.  



We construct the commonly used Taylor--Hood element $\P^2_2-\P_1$ as follows (see also \emp{div.py}),    
%% BEGIN CODE FROM div.py 
\begin{quote}
{
\renewcommand{\baselinestretch}{0.85}\footnotesize
\begin{verbatimtab}[8]
from swiginac import *
from SyFi import *

polygon = ReferenceTriangle()

v_element = VectorLagrangeFE(polygon,2)
v_element.set_size(2)
v_element.compute_basis_functions()

p_element = LagrangeFE(polygon,1)
p_element.compute_basis_functions()
\end{verbatimtab}
}
\end{quote}
%% END CODE FROM div.py 
The polygonal domain here is a reference triangle, but it may be a line, a triangle, a square, a tetrahedron or a box. Furthermore, these geometries are not limited to typical reference geometries. 
For instance, we may construct the elements on a global triangle defined by the 
points $(x_0,y_0)$, $(x_1,y_1)$, and $(x_2,y_2)$ where $x_0, \ldots, y_2$ 
might be both numbers and/or symbols. 
The following code shows the Taylor--Hood element on a triangle
defined in terms of the symbols $x_0, \ldots, y_2$, 
(see also \emp{div\_global.py}),  
%% BEGIN CODE FROM div_global.py 
\begin{quote}
{
\renewcommand{\baselinestretch}{0.85}\footnotesize
\begin{verbatimtab}[8]
x0 = symbol("x0"); y0 = symbol("y0")
x1 = symbol("x1"); y1 = symbol("y1")
x2 = symbol("x2"); y2 = symbol("y2")

p0 = [x0,y0]; p1 = [x1,y1]; p2 = [x2,y2]

polygon = Triangle(p0,p1,p2)
v_element = VectorLagrangeFE(polygon,2)
v_element.set_size(2)
v_element.compute_basis_functions()

p_element = LagrangeFE(polygon,1)
p_element.compute_basis_functions()
\end{verbatimtab}
}
\end{quote}
%% END CODE FROM div_global.py 
The computed basis functions are standard polynomials also in this case, 
although they depend on $x_0, \ldots, y_2$. These
polynomials can be added, multiplied, differentiated, integrated etc. in
the standard way (within a symbolic framework).
Consider for example, the computation of the 
divergence constraint, 
\[
B_{ij} = \int_T \div \NN_i \, L_j \, dx, 
\]
where $\NN_i$ and $L_j$ are the basis functions for the velocity and pressure
elements, respectively, and $T$ is a polygonal domain. 
This matrix can be computed as follows (see also \emp{div\_global.py}): 
%% BEGIN CODE FROM div_global.py 
\begin{quote}
{
\renewcommand{\baselinestretch}{0.85}\footnotesize
\begin{verbatimtab}[8]

... construct the element  

for i in range(0,v_element.nbf()): 
    for j in range(0,p_element.nbf()): 
        integrand = div(v_element.N(i))*p_element.N(j)
        Bij =  polygon.integrate(integrand)
\end{verbatimtab}
}
\end{quote}
%% END CODE FROM div_global.py 



Another example that demonstrates the power of this approach, in which 
we utilize a symbolic mathematics engine, is the computation
of the Jacobian of the nonlinear convection-diffusion equations that typically
appear in incompressible flow simulations. 
Let 
\[
\FF_i = \int_T (\uu\cdot\nabla \uu)\cdot \NN_i + \nabla \uu : \nabla \NN_i \, dx,   
\]
where $\uu = \sum_k u_k \NN_k$.   
Then, 
\begin{equation}
\label{jacobi:comp}
\JJ_{ij} = \frac{\partial \FF_i }{\partial u_j}  
= \frac{\partial}{\partial u_j}
\int_T (\uu\cdot\nabla \uu)\cdot \NN_i + \nabla \uu : \nabla \NN_i \, dx.   
\end{equation}

The computation of such Jacobian matrices and the implementation of 
corresponding simulation software are usually tedious and error-prone. 
It seems that one main reason
for this difficulty is the gap between the computations
done by hand and the corresponding numerical algorithm to 
be implemented.  After all, the computation of \eqref{jacobi:comp} 
only involves straightforward operations. 
SyFi aims at closing this gap. 
We will now show the code for 
computing \eqref{jacobi:comp} with SyFi. The complete source code is in \emp{conv-diffusion.py}. 
First, we compute the finite elements as shown in the previous example. 
Secondly, we compute the $\FF_i$ and differentiate to get the Jacobian:  
%% BEGIN CODE FROM conv-diffusion.py 
\begin{quote}
{
\renewcommand{\baselinestretch}{0.85}\footnotesize
\begin{verbatimtab}[8]

u, ujs = sum("u", fe) 

for i in range(0,fe.nbf()): 

  # compute diffusion term 
  fi_diffusion = inner(grad(u), grad(fe.N(i))) 
  
  # compute convection term 
  uxgradu = (u.transpose()*grad(u)).evalm()  
  fi_convection = inner(uxgradu, fe.N(i), True) 

  # add together diffusion and convection 
  fi = fi_diffusion + fi_convection

  # compute the integral 
  Fi = polygon.integrate(fi) 

  for j in range(0,fe.nbf()): 
    # differentiate to get the Jacobian 
    uj = ujs.op(j) 
    Jij = diff(Fi, uj) 
    #print out the Jacobian 
    print "J[%d,%d]=%s;\n"%(i,j,Jij)
\end{verbatimtab}
}
\end{quote}

\noindent
The output from \emp{conv-diffusion.py} is: 
%% BEGIN CODE FROM conv-diffusion.py 
\begin{quote}
{
\renewcommand{\baselinestretch}{0.85}\footnotesize
\begin{verbatimtab}[8]
J[0,0]=1+1/24*u2-1/12*u1-1/24*u5-1/6*u0-1/24*u4-1/24*u3;
J[0,1]=-1/12*u0+1/12*u4;
J[0,2]=-1/2+1/12*u2+1/24*u0+1/24*u4;
J[0,3]=-1/24*u0+1/24*u4;
J[0,4]=-1/2+1/24*u2+1/12*u1+1/24*u5-1/24*u0+1/24*u3;
...
\end{verbatimtab}
}
\end{quote}
%which is directly compilable given that \emp{J}, \emp{u0} \ldots \emp{u11}
%is defined properly. 







We can now extend the above code such that it also can include the Ostwald--de Waele power-law 
viscosity model, i.e., 
\[
\FF^p_i = \int_T (\uu\cdot\nabla \uu)\cdot \NN_i + \mu(\uu)\nabla \uu : \nabla \NN_i \, dx,   
\]
where $\mu=\mu_0 \|\nabla \uu\|^n$.  
The Jacobian matrix is then 
\[
\JJ^p_{ij} = \frac{\partial \FF^p_i }{\partial u_j} . 
\]

The only thing we need to change then in the above script is the 
diffusion term (see also \emp{conv-diffusion-power-law.py}): 
\begin{quote}
%% BEGIN CODE FROM conv-diffusion-power-law.py 
{
\renewcommand{\baselinestretch}{0.85}\footnotesize
\begin{verbatimtab}[8]
  # nonlinear power-law diffusion term 
  mu = inner(grad(u), grad(u))
  fi_diffusion = mu0*pow(mu,n)*inner(grad(u), grad(fe.N(i))) 
\end{verbatimtab}
}
\end{quote}
%% END CODE FROM conv-diffusion-power-law.py 
\noindent
In addition, we also need to declare $n$ and $\mu_0$ to be either symbols or numbers. 

\section{Code Generation for Quadrature Based FEM systems}



SyFi can also be used to generate C++ code for 
other FEM systems.  We will here consider code generation for finite element 
basis functions in a format specified by the user. 
Other code generation examples can be found in the SyFi tutorial and source code, where
code for creating both PyCC and Epetra matrices for various problems are generated.  
Furthermore, notice that one can print the expressions out in either 
of the formats: ASCII, C, \LaTeX, and
Python. 

The following code demonstrates how C code for the basis functions is
generated (see also \emp{code\_gen\_simple.py}): 
%% BEGIN CODE FROM code_gen_simple.py 
\begin{quote}
{
\renewcommand{\baselinestretch}{0.85}\footnotesize
\begin{verbatimtab}[8]
polygon = ReferenceTriangle()
fe = LagrangeFE(polygon,2)
fe.compute_basis_functions()

N_string = ""
for i in range(0,fe.nbf()): 
    N_string += "    N[%d]=%s;\n"% (i, fe.N(i).printc())

c_code = """
void basis2D(double N[%d], double x, double y) { 
%s
} """ % (fe.nbf(), N_string)

print c_code
\end{verbatimtab}
}
\end{quote}
%% END CODE FROM code_gen_simple.py  
Notice that C code for the expressions is generated with the
function \emp{printc}. 
The output when \emp{code\_gen\_simple.py} is runned is: 
%% BEGIN OUTPUT FROM code_gen_simple.py  
\begin{quote}
{
\renewcommand{\baselinestretch}{0.85}\footnotesize
\begin{verbatimtab}[8]
void basis2D(double N[6], double x, double y) {
    N[0]=pow(-y-x+1.0,2.0)-(-y-x+1.0)*y-(-y-x+1.0)*x;
    N[1]=4.0*(-y-x+1.0)*x;
    N[2]=-y*x+(x*x)-(-y-x+1.0)*x;
    N[3]=4.0*(-y-x+1.0)*y;
    N[4]=4.0*y*x;
    N[5]=-y*x+(y*y)-(-y-x+1.0)*y;
}
\end{verbatimtab}
}
\end{quote}
%% END OUTPUT FROM code_gen_simple.py  
Finally, notice that to change the above code to produce code for, e.g., 5th order elements all you need to 
do is change the degree of the element i.e.,  
%% BEGIN CODE FROM code_gen_simple.py 
\begin{quote}
{
\renewcommand{\baselinestretch}{0.85}\footnotesize
\begin{verbatimtab}[8]
polygon = ReferenceTriangle()
fe = LagrangeFE(polygon,5)
fe.compute_basis_functions()

...

\end{verbatimtab}
}
\end{quote}



%Secondly, we will consider the generation of code for the divergence
%constraint matrix. 
%It is usual defined the global finite elements in terms of reference elements
%and a geometry mapping. Let  
%\[
%\xx \in T: \quad  \xx = \GG \xxi + \xx_0, \quad \xxi \in \hat{T}, 
%\]
%where $\hat{T}$ and $T$ are the reference and the global polygon, respectively.  
%We have the following relation between integrals on 
%the reference and the global polygon,  
%\[
%\int_T f \, dx = \int_{{\hat T}} {\hat f} D \, d{\hat x},
%\]
%where $D$ is the determinant of $\GG$. 
%Furthermore, the derivative of a function $f$ with respect to $dx_j$ is:   
%\[
%\frac{\partial f}{\partial dx_j } = \sum_i \frac{\partial f}{\partial d\xi_i} \frac{\partial d \xi_i }{dx_j}  \frac{\partial d \xi_ii }{dx_j} 
%\]
%where $\GG_{ij} = \frac{\partial d \xi_i }{dx_j}$. 
%Hence, when computing the integral we will need $D$, while when computing
%derivatives we need $\GG$. So far we have only used affine mappings
%between the reference element and the global element. Hence $\GG$ is 
%a matrix and $D$ is a number (although other polynomial mappings can probably
%be included fairly easily). 
%The way we deal with $\GG$ and $D$ is that we keep these as a matrix 
%of symbols and a symbol, respectively, and use $\GG$ when performing
%differentiation such as taking the divergence and use $D$ when 
%computing the integral. The following code demonstrate the computation
%of the divergence matrix and the corresponding code generation (see
%also \emp{div\_code\_gen\_taylorhood.py} or \emp{div\_code\_gen\_robust.py}):
%
%%% BEGIN CODE FROM divG.py 
%\begin{quote}
%{
%\renewcommand{\baselinestretch}{0.85}\footnotesize
%\begin{verbatimtab}[8]
%def compute_divergence_constraint(v_element, p_element, G, D): 
%    B = {}
%    for i in range(0,v_element.nbf()): 
%        for j in range(0,p_element.nbf()): 
%            integrand = div(v_element.N(i),G)*p_element.N(j)
%            B[i,j] =  polygon.integrate(integrand)*D
%    return B 
%
%
%def code_for_divergence_constraint(B):
%    B_string = ""
%    keys = B.keys()
%    keys.sort(integer_sequence_compare)
%
%    for key in keys: 
%        i,j = key 
%        B_string += "  B[%d][%d] = %s;\n" % (i,j,B[i,j].evalf().printc())
%
%    return B_string
%
%
%cfile = open("div_matrix.cc", 'w')
%code = """
%void compute_div_matrix(double B[%d,%d], double pp0[2], 
%		        double pp1[2], double pp2[2]) {  
%
%%s 
%%s
%}
%"""% (v_element.nbf(), p_element.nbf(), initialization_on_2D_triangle, B_string)  
%\end{verbatimtab}
%}
%\end{quote}
%%% END CODE FROM div_code_gen.py 
%
%
%
%We remark that in the above example, the variational form is completely element and polygon independent. 
%
%

\section{Defining a Finite Element in SyFi} 
\label{sec:element}

Defining a finite element may of course be more technical than 
using it, in particular for advanced elements. 
Furthermore, the implementation shown below involves more of GiNaC and SyFi 
than the earlier examples, so the 
reader should have access to both the SyFi and GiNaC tutorial.  
The elements implemented in SyFi so far have mostly been implemented
in C++ since they then will be available in both C++ and Python
(by using SWIG). 

We will describe the implementation of an element recently added to SyFi.
The element was introduced in \cite{m-t-w}. 
The special feature of this element is that it works
well for both Darcy and Stokes types of flow. 

The definition of the element is as follows, 
\[
\VV(T) = \{ \vv \in \P^2_3 \  : \  \div \vv \in \P_0, \ (\vv\cdot\nn_e) |_e  \in \P_1 \  \forall e \in E(T) \},    
\]
where $T$ is a given triangle,  $E(T)$ is the edges of $T$, $\nn_e$ is the 
normal vector on edge $e$, and $\P_k$ is the space of polynomials of degree $k$ and $\P_k^d$ the corresponding vector space. 
The degrees of freedom are, 
\begin{align*}
&\int_e (\vv\cdot \nn) \tau^k \, d\tau, \quad k=0,1,  &\forall e \in E(T), \\
&\int_e (\vv\cdot \ttt) \, d\tau,  &\forall e \in E(T).
\end{align*}

The definition of the element is more complicated 
than most of the common elements.  
Still, we will show that it  can be implemented in SyFi 
in about 100 lines of codes. 
We will compute  this element in four steps: 
\begin{enumerate}
\item Constructing the polynomial space $\VV(T)$. 
\item Specifying the constraints.  
\item Specifying the degrees of freedom.  
\item Solving the resulting linear system of equations. 
\end{enumerate}

Considering the first step, 
SyFi implements the Bernstein polynomials (in barycentric coordinates)
with the functions \emp{bernstein} and \emp{bernsteinv}, for 
scalar and vector polynomials, respectively. 
The \emp{bernstein} functions returns a list (\emp{lst}) with the items:
\begin{itemize}
\item 
The polynomial 
$(a_0 x  + a_1 y + a_2 (1-x-y) + \ldots)$. 
\item 
The variables $(a_0,a_1,a_2,\ldots)$.   
\item 
The polynomial basis $(x,y,1-x-y,\ldots)$.  
\end{itemize}
In the following we construct $\P^2_3$: 

%% BEGIN CODE FROM Robust.cpp 
\begin{quote}
{
\renewcommand{\baselinestretch}{0.85}\footnotesize
\begin{verbatimtab}[8]
        Triangle triangle 
        ex V_space = bernsteinv(2, 3, triangle, "a"); 
        ex V_polynomial = V_space.op(0); 
        ex V_variables = V_space.op(1); 
\end{verbatimtab}
}
\end{quote}
Here \emp{V\_space} is the above mentioned list, \emp{V\_polynomial} contains the polynomial, 
and \emp{V\_variables} contains the variables.   


In the second step we first 
specify the constraint $\div \vv \in \P_0$:
%% BEGIN CODE FROM Robust.cpp 
\begin{quote}
{
\renewcommand{\baselinestretch}{0.85}\footnotesize
\begin{verbatimtab}[8]
        lst equations; 
        ex divV = div(V); 
        ex_ex_map b2c = pol2basisandcoeff(divV);   
        ex_ex_it iter; 
        // div constraints:  
        for (iter = b2c.begin(); iter != b2c.end(); iter++) {   
            ex basis = (*iter).first;  
            ex coeff= (*iter).second;  
            if ( coeff != 0 && ( basis.degree(x) > 0 
				 || basis.degree(y)  > 0 )  ) { 
                equations.append( coeff == 0 ); 
            }
        }
\end{verbatimtab}
}
\end{quote}
Here, the divergence is computed with the \emp{div} function. The divergence of a function in $\P^2_3$ is in $\P_2$. Hence, it is on the form $b_0 + b_1 x + b_2 y + b_3 x y + b_4 x^2 + b_5 y^2$. In the above code we find the coefficients $b_i$, as expressions involving the above mentioned variables $a_i$ and the corresponding polynomial basis, with the function \emp{pol2basisandcoeff}. 
Then we ensure that the only coefficient which is not zero is $b_0$.  

The next constraints $(\vv\cdot\nn_e) |_e  \in \P_1$ are implemented in much of the same
way as the divergence constraint. We create a loop over each edge $e$  
of the triangle and multiply $\vv$ with the normal $\nn_e$. 
Then we substitute the expression  for the edge, i.e., in mathematical notation $|_e$, 
into $\vv\cdot\nn$.  
After substituting  the expression for these lines to get  
$(\vv\cdot\nn_e) |_e$  , we check that the remaining
polynomial is in $\P_1$ in the same way as we did above. 





%% BEGIN CODE FROM Robust.cpp 
\begin{quote}
{
\renewcommand{\baselinestretch}{0.85}\footnotesize
\begin{verbatimtab}[8]


        // constraints on edges: 
        for (int i=1; i<= 3; i++) {
            Line line = triangle.line(i); 
            symbol s("s"); 
            lst normal_vec = normal(triangle, i); 
            ex Vn = inner(V, normal_vec);   
            Vn = Vn.subs(line.repr(s).op(0)).subs(line.repr(s).op(1));
            b2c = pol2basisandcoeff(Vn,s); 
            for (iter = b2c.begin(); iter != b2c.end(); iter++) {   
                ex basis = (*iter).first;  
                ex coeff= (*iter).second;  
                if ( coeff != 0 &&  basis.degree(s) > 1 )  
                { 
                    equations.append( coeff == 0 ); 
                }
            }
        }

\end{verbatimtab}
}
\end{quote}

In the third step we specify the degrees of freedom. 
First, we specify the equations coming from  
$\int_e (\vv\cdot \nn) \tau^k d\tau , k=0,1$ on all edges. To do this we need   
to create a loop over all edges, and on each edge we create the space of linear
Bernstein polynomials in barycentric coordinates on $e$, i.e.,  $\P_1(e)$. 
Then we create a loop
over the basis functions $\tau^k$ in $\P_1(e)$ and compute the integral 
$\int_e (\vv\cdot \nn) \tau^k \, d\tau $. 
 
%% BEGIN CODE FROM Robust.cpp 
\begin{quote}
{
\renewcommand{\baselinestretch}{0.85}\footnotesize
\begin{verbatimtab}[8]


        // dofs related to the normal on the edges  
        for (int i=1; i<= 3; i++) {
            Line line = triangle.line(i); 
            lst normal_vec = normal(triangle, i); 
            ex P1_space = bernstein(1, line, istr("a",i));
            ex P1 = P1_space.op(2); 
            ex Vn = inner(V, normal_vec);  

            ex basis; 
            for (int j=0; j< P1.nops(); j++) {
                basis = P1.op(j); 
                ex integrand = Vn*basis; 
                ex dofi =  line.integrate(integrand);  
                dofs.insert(dofs.end(), lst(line.vertex(0), 
					    line.vertex(1), j)); 
                ex eq = dofi == numeric(0); 
                equations.append(eq); 

            }
        }
\end{verbatimtab}
}
\end{quote}
Finally, the degrees of freedom  $\int_e (\vv\cdot \ttt) d\tau $ can be implemented 
in basically the same fashion as the previously described degrees of freedom.
To summarize, we have now specified 20 equations which is precisely the number of 
unknowns in  $\P^2_3$. Hence, the space $\VV(T)$ is uniquely defined, what remains
is simply to solve a linear system with 20 equations and 20 unknowns. 
The complete source code is in \emp{Robust.cpp}.



\section{Summary}

In this paper we have tried to demonstrate that 
symbolic mathematics combined with code generation 
can be an alternative to the traditional numerical
apporach for implemention finite elements and finite 
element methods. By combining Python, C++ and legacy 
libraries we have created a library which is both easy
to use and powerful enough for advanced methods 
and complicated PDEs. Furthmore, the generated code 
is often efficient compared to the traditional quadrature
based approach. 



\begin{thebibliography}{5}

\bibitem{analysa}
B. Bagheri, L. R. Scott,  Analysa software package, \\
http://people.cs.uchicago.edu/\~{}ridg/al/aa.html

\bibitem{CR}
M.~Crouzeix and P.A. Raviart.
\newblock Conforming and non--conforming finite element methods for solving the
  stationary stokes equations.
\newblock {\em RAIRO Anal. Num\'{e}r.}, 7:33--76, 1973.



\bibitem{DSEL} 
C. Prud'homme,  DSEL software package, \\
http://www.hpc2n.umu.se/para06/papers/paper\_147.pdf

\bibitem{FEniCS}
T. Dupont,  J. Hoffman, J. Jansson, C. Johnson
R. C. Kirby, M. Knepley, M. Larson, A. Logg, R. Scott, G. N. Wells,
FEniCS software package,
http://www.fenics.org

\bibitem{FFC}
A. Logg,
FFC software package,
http://www.fenics.org/ffc/


\bibitem{FIAT}
R. C. Kirby, 
FIAT software package,
http://www.fenics.org/fiat/

\bibitem{FreeFem}
O. Pironneau, F. Hecht, A. L. Hyaric, FreeFEM software package, \\
http://www.freefem.org/ff++/index.htm


\bibitem{getdp}
P. Dular,  C. Geuzaine, GetDP software package, \\
http://www.geuz.org/getdp/


\bibitem{GiNaC}
C. Bauer, C. Dams, A. Frink, V. V. Kisil, R. Kreckel, A. Sheplyakov,  J. Vollinga,
GiNaC - is not a CAS, 
http://www.ginac.de

\bibitem{m-t-w} K.-A. Mardal, X.-C. Tai and R. Winther,
A robust finite element method for Darcy--Stokes flow,
SIAM J. Numer. Anal. 40 (2002), pp. 1605--1631.

\bibitem{Nedelec:1980:MFE}
J.-C. N{\'e}d{\'e}lec.
\newblock Mixed finite elements in ${R}^3$.
\newblock 35(3):315--341, October 1980.

\bibitem{Nedelec:1986:NFM}
J.-C. N{\'e}d{\'e}lec.
\newblock A new family of mixed finite elements in ${R}^3$.
\newblock 50(1):57--81, November 1986.

\bibitem{RT}
P.~A. Raviart and J.~M. Thomas.
\newblock A mixed finite element method for 2-order elliptic problems.
\newblock {\em Matematical Aspects of Finite Element Methods}, 1977.




\bibitem{sundance} 
K. Long,  
Sundance software package, 
http://software.sandia.gov/sundance/

\bibitem{SyFi}
K.-A. Mardal,
SyFi - Symbolic Finite Elements, 
http://syfi.sf.net



\bibitem{SWIG}
D. Beazley et. al., SWIG - Simplified Wrapper and Interface Generator,  \\
http://www.swig.org

\bibitem{Swiginac}
O. Skavhaug, O. Certik, Swiginac - Python interface to GiNaC  \\
http://swiginac.berlios.de/



\bibitem{Trilinos}
M. Heroux et. al., Trilinos, \\ 
http://software.sandia.gov/trilinos/

\end{thebibliography}



\end{document}

