Chapter 12
Add new finite element

12.1 Some notation

For a function f taking value in ℝN,N = 1,2,⋅⋅⋅, we define the finite element approximation Πhf of f. Let us denote the number of the degrees of freedom of the finite element by NbDoF. Then the i-th base ωiK (i = 0,⋅⋅⋅,NbDoF -1) of the finite element space has the j-th component ωijK for j = 0,⋅⋅⋅,N -1.

The operator Πh is called the interpolator of the finite element. We have the identity ωiK = ΠhωiK.

Formally, the interpolator Πh is constructed by the following formula:

       kP∑i-1          K
Πhf  =     αkfjk(Ppk)ωik
       k=0
(12.1)

where Pp is a set of npPi points,

In the formula (12.1), the list pk,jk,ik depend just on the type of finite element (not on the element), but the coefficient αk can be depending on the element.

Example 1: classical scalar Lagrange finite element, first we have kPi = npPi = NbOfNodeand

Example 2: The Raviart-Thomas finite element:

                                     |      |
RT 0h = {v ∈ H(div)∕∀K ∈ Th   v|K(x,y) = ||αβK + γK||xy}
                                      K
(12.2)

The degree of freedom are the flux throw an edge e of the mesh, where the flux of the function f : ℝ2-→ℝ2 is ef.ne, ne is the unit normal of edge e (this implies a orientation of all the edges of the mesh, for example we can use the global numbering of the edge vertices and we just go to small to large number).

To compute this flux, we use an quadrature formula with one point, the middle point of the edge. Consider a triangle T with three vertices (a,b,c). Let denote the vertices numbers by ia,ib,ic, and define the three edge vectors e0,e1,e2 by sgn(ib -ic)(b -c), sgn(ic -ia)(c -a), sgn(ia -ib)(a -b),

The three basis functions are:

ωK = sgn(ib --ic)(x- a), ωK = sgn(ic-- ia)(x - b), ωK  = sgn(ia-- ib)(x - c),
 0      2|T|             1      2|T|             2      2|T |
(12.3)

where |T|is the area of the triangle T.

So we have N = 2, kPi = 6; npPi = 3; and:

12.2 Which class of add

Add file FE_ADD.cppin directory src/femlibfor example first to initialize :


#include "error.hpp"
#include "rgraph.hpp"
using namespace std;
#include "RNM.hpp"
#include "fem.hpp"
#include "FESpace.hpp"
#include "AddNewFE.h"
namespace  Fem2D {

Second, you are just a class which derive for public TypeOfFElike:


class TypeOfFE_RTortho : public  TypeOfFE { public:
  static int Data[];  // some numbers
  TypeOfFE_RTortho():
    TypeOfFE( 0+3+0,    // nb degree of freedom on element
       2,       // dimension N of vectorial FE (1 if scalar FE)
       Data,    // the array data
       1,       // nb of subdivision for plotting
       1,       // nb of sub finite element (generaly 1)
       6,       // number kPi of coef to build the interpolator (12.1)
       3,       // number npPi of integration point to build interpolator
       0        // an array to store the coef αk to build interpolator
                // here this array is no constant so we have
                // to rebuilt for each element.
       )
  {
    const R2 Pt[] = { R2(0.5,0.5), R2(0.0,0.5), R2(0.5,0.0) };
     // the set of Point in K^
    for (int p=0,kk=0;p<3;p++) {
      P_Pi_h[p]=Pt[p];
      for (int j=0;j<2;j++)
        pij_alpha[kk++]= IPJ(p,p,j); }}  // definition of ik,pk,jk in (12.1)
  void FB(const bool ⋆ watdd, const Mesh & Th,const Triangle & K,
          const R2 &PHat, RNMK_ & val) const;
  void Pi_h_alpha(const baseFElement & K,KN_<double> & v) const ;
} ;

where the array data is form with the concatenation of five array of size NbDoFand one array of size N.

This array is:


int TypeOfFE_RTortho::Data[]={
               // for each df 0,1,3 :
        3,4,5, // the support of the node of the df
        0,0,0, // the number of the df on the node
        0,1,2, // the node of the df
        0,0,0, // the df come from which FE (generally 0)
        0,1,2, // which are de df on sub FE
        0,0 }; // for each component j = 0,N - 1 it give the sub FE associated

where the support is a number 0,1,2 for vertex support, 3,4,5 for edge support, and finaly 6 for element support.

The function to defined the function ωiK, this function return the value of all the basics function or this derivatives in array val, computed at point PHaton the reference triangle corresponding to point R2 P=K(Phat);on the current triangle K.

The index i,j,k of the array val(i,j,k) corresponding to:

i
is basic function number on finite element i ∈ [0,NoF[
j
is the value of component j ∈ [0,N[
k
is the type of computed value f(P),dx(f)(P),dy(f)(P),... i ∈ [0,last_operatortype[. Remark for optimization, this value is computed only if whatd[k] is true, and the numbering is defined with


enum operatortype { op_id=0,
   op_dx=1,op_dy=2,
   op_dxx=3,op_dyy=4,
   op_dyx=5,op_dxy=5,
   op_dz=6,
   op_dzz=7,
   op_dzx=8,op_dxz=8,
   op_dzy=9,op_dyz=9
   };
const int last_operatortype=10;

The shape function :


 void TypeOfFE_RTortho::FB(const bool ⋆whatd,const Mesh & Th,const Triangle & K,
                           const R2 & PHat,RNMK_ & val) const
{  //
  R2 P(K(PHat));
  R2 A(K[0]), B(K[1]),C(K[2]);
  R l0=1-P.x-P.y,l1=P.x,l2=P.y;
  assert(val.N() >=3);
  assert(val.M()==2 );
  val=0;
  R a=1./(2⋆K.area);
  R a0=   K.EdgeOrientation(0) ⋆ a ;
  R a1=   K.EdgeOrientation(1) ⋆ a  ;
  R a2=   K.EdgeOrientation(2) ⋆ a ;
   // ------------
  if (whatd[op_id])   // value of the function
   {
     assert(val.K()>op_id);
     RN_ f0(val('.',0,0));  // value first component
     RN_ f1(val('.',1,0));  // value second component
     f1[0] =  (P.x-A.x)⋆a0;
     f0[0] = -(P.y-A.y)⋆a0;
     f1[1] =  (P.x-B.x)⋆a1;
     f0[1] = -(P.y-B.y)⋆a1;
     f1[2] =  (P.x-C.x)⋆a2;
     f0[2] = -(P.y-C.y)⋆a2;
    }
   // ----------------
    if (whatd[op_dx])  // value of the dx of function
    {
     assert(val.K()>op_dx);
     val(0,1,op_dx) =  a0;
     val(1,1,op_dx) =  a1;
     val(2,1,op_dx) =  a2;
     }
    if (whatd[op_dy])
    {
     assert(val.K()>op_dy);
     val(0,0,op_dy) =  -a0;
     val(1,0,op_dy) =  -a1;
     val(2,0,op_dy) =  -a2;
    }
  for (int i= op_dy; i< last_operatortype ; i++)
   if (whatd[op_dx])
     assert(op_dy);
}

The function to defined the coefficient αk:


void TypeOfFE_RT::Pi_h_alpha(const baseFElement & K,KN_<double> & v) const
{
  const Triangle & T(K.T);
   for (int i=0,k=0;i<3;i++)
     {
        R2 E(T.Edge(i));
        R signe = T.EdgeOrientation(i) ;
        v[k++]= signe⋆E.y;
        v[k++]=-signe⋆E.x;
     }
}

Now , we just need to add a new key work in FreeFem++, Two way, with static or dynamic link so at the end of the file, we add :

With dynamic link is very simple (see section C of appendix), just add before the end of FEM2d namespaceadd:


static TypeOfFE_RTortho The_TypeOfFE_RTortho;  //
static AddNewFE("RT0Ortho", The_TypeOfFE_RTortho);
}  // FEM2d namespace

Try with ”./load.link” command in examples++-load/and see BernardiRaugel.cppor Morley.cpp new finite element examples.

Otherwise with static link (for expert only), add


// let the 2 globals variables
static TypeOfFE_RTortho The_TypeOfFE_RTortho;  //
// ----- the name in freefem ----
static  ListOfTFE typefemRTOrtho("RT0Ortho", & The_TypeOfFE_RTortho);  //
// link with FreeFem++ do not work with static library .a
// FH so add a extern name to call in init_static_FE
// (see end of FESpace.cpp)
void init_FE_ADD() { };
// --- end --
}  // FEM2d namespace

To inforce in loading of this new finite element, we have to add the two new lines close to the end of files src/femlib/FESpace.cpplike:


// correct Problem of static library link with new make file
void init_static_FE()
{  // list of other FE file.o
   extern void init_FE_P2h() ;
  init_FE_P2h() ;
   extern void init_FE_ADD() ;   // new line 1
   init_FE_ADD();   // new line 2
}

and now you have to change the makefile.

First, create a file FE_ADD.cppcontening all this code, like in file src/femlib/Element_P2h.cpp, after modifier the Makefile.amby adding the name of your file to the variable EXTRA_DISTlike:

# Makefile using Automake + Autoconf  
# ----------------------------------  
# $Id: addfe.tex,v 1.7 2008/11/24 19:10:15 hecht Exp $  
 
# This is not compiled as a separate library because its  
# interconnections with other libraries have not been solved.  
 
EXTRA_DIST=BamgFreeFem.cpp BamgFreeFem.hpp CGNL.hpp CheckPtr.cpp        \  
ConjuguedGradrientNL.cpp DOperator.hpp Drawing.cpp Element_P2h.cpp      \  
Element_P3.cpp Element_RT.cpp fem3.hpp fem.cpp fem.hpp FESpace.cpp      \  
FESpace.hpp FESpace-v0.cpp FQuadTree.cpp FQuadTree.hpp gibbs.cpp        \  
glutdraw.cpp gmres.hpp MatriceCreuse.hpp MatriceCreuse_tpl.hpp          \  
MeshPoint.hpp mortar.cpp mshptg.cpp QuadratureFormular.cpp              \  
QuadratureFormular.hpp RefCounter.hpp RNM.hpp RNM_opc.hpp RNM_op.hpp    \  
RNM_tpl.hpp   FE_ADD.cpp  

and do in the freefem++root directory

  autoreconf  
 ./reconfigure  
 make

For codewarrior compilation add the file in the project an remove the flag in panal PPC linker FreeFEm++ Setting Dead-strip Static Initializition Code Flag.