FQuick.dox 16.1 KB
Newer Older
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
1 2 3
/*! \page quick Quick Start

 * In this section, we present the data structure organization and the 
4
 * classes design to understand fully ScalFmm and to customize it.
BRAMAS Berenger's avatar
BRAMAS Berenger committed
5
 
6
 * Remark 1.2 : There is a big difference between the versions 1.0 and 1.2
BRAMAS Berenger's avatar
BRAMAS Berenger committed
7 8
 * since we do not store array of particles anymore but rather several arrays.
 * This was needed in order to be able to vectorize the P2P code. 
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
9
 
10 11 12 13 14 15
 * Remark 1.3 : There is a big difference between the versions 1.2 and 1.3
 * The precision is choosen with template (usually called FReal) and no more
 * during the configure. 
 
 * We would like to inform users who want to create a kernel and who are not familiar with 'C++'
 * but are familiar with 'C' that an C API have been made for them.
16
 * In order to get access to this API, go in Addons/CKernelApi.
17
 * (To compile, enable the addons and then the CKernelApi in the CMake stage).
18 19 20
 * However, to have access to all the features of ScalFmm it is required to use C++
 * as described in this QuickStart.
 
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
21 22 23 24
 * \tableofcontents 
 
 * \section prerequisite Prerequisite 
 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
25 26
 * It is recommanded to have built the library or at least to have 
 * downloaded the source code. The user needs to be comfortable with 'C++' 
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
27 28
 * language and if possible templates. 

29 30
 * If you want to browse the code, you may want to see first our \ref
   rules.
31

PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
32 33 34 35 36 37 38
 * \section classes Overview of general architecture
 *
 * \image html Classes.png "General architecture"
 
 * \section data What Data 
 
 * In ScalFmm we proceed the Fast Multipole Method. New users should see 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
39
 * this process as a way to estimate far interactions and compute 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
40 41
 * accurately the close interactions in a group of particles. We start 
 * with some particles that we insert in a octree. The octree stores the 
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
42 43 44 45
 * particles in its leaves. From the root to the leaves there are the 
 * cells. At this point we only express primitives classes which hold 
 * data or primitives classes. 
 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
46
 * Then, we need a kernel which is the computational part of the FMM. It is a 
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
47
 * class that is able to compute the interactions between particles or 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
48
 * cells, etc. There are several possible kernels depending on what we 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
49
 * want to compute and it is easy to implement your own.
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
50 51 52 53
 
 * Finally, the FMM Core algorithm is a class that takes the primitives 
 * classes and calls the kernel with the correct arguments. In our 
 * implementation, the user has to choose between sequential FMM or 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
54
 * OpenMP FMM or even MPI FMM. 
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
55 56 57 58 59
 
 * \section primitivesclasses Primitives Classes
 
 * \subsection particles Particles
 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
60
 * In order to put the particles in the right leaf, the octree needs to know its spatial position.
BRAMAS Berenger's avatar
BRAMAS Berenger committed
61
 * Then, once the right leaf is found, it is allocated (using the given template LeafClass of the octree),
BRAMAS Berenger's avatar
BRAMAS Berenger committed
62
 * and the particles is pushed into the leaf. If a basic leaf is used, this one only push to a particles container
BRAMAS Berenger's avatar
BRAMAS Berenger committed
63 64
 * what it has received. So a particle container is nothing more than a class that has a push method
 * which matches the one you call on the octree. To ensure that, a particle container should inherit from FAbstractParticleContainer.
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
65 66

 * <pre class='brush: cpp'>
BRAMAS Berenger's avatar
BRAMAS Berenger committed
67
 * template <class FReal>
BRAMAS Berenger's avatar
BRAMAS Berenger committed
68
 * class FAbstractParticleContainer{ 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
69 70 71 72
 *   template<typename... Args>
 *   void push(const FPoint<FReal>& /*inParticlePosition*/, Args ... /*args*/){
 *       
 *   }
BRAMAS Berenger's avatar
BRAMAS Berenger committed
73 74
 * </pre>
 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
75
 * Here is how we can print the index of the particles that is inserted from a particle containers :
BRAMAS Berenger's avatar
BRAMAS Berenger committed
76 77 78
 
 
 * <pre class='brush: cpp'>
BRAMAS Berenger's avatar
BRAMAS Berenger committed
79 80
 * template <class FReal>
 * class MyCustomContainer : public FAbstractParticleContainer<FReal> { 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
81
 * template<typename... Args>
BRAMAS Berenger's avatar
BRAMAS Berenger committed
82
 * void push(const FPoint<FReal>& , int particleIndex, double anythingElse){
BRAMAS Berenger's avatar
BRAMAS Berenger committed
83 84 85 86
 *      std::cout << "The particle " << particleIndex << " has just been inserted with " << anythingElse << "\n";
 * };
 *
 * // In the main
BRAMAS Berenger's avatar
BRAMAS Berenger committed
87 88 89
 *     typedef MyCustomContainer<double>      ContainerClass;
 *     typedef FSimpleLeaf<double, ContainerClass >                     LeafClass;
 *     typedef FOctree<double, FBasicCell, ContainerClass , LeafClass >  OctreeClass;
BRAMAS Berenger's avatar
BRAMAS Berenger committed
90 91 92 93 94 95 96 97 98 99 100
 * // From your system properties
 * OctreeClass tree(treeHeight, subHeight, loader.getBoxWidth(), loader.getCenterOfBox());
 *
 * // Add a particle
 * tree.push(FPoint(x, y, z), particleIndex, anythingElse);
 * // The octree will push in the FSimpleLeaf which will push in the MyCustomContainer which will print the message.
 * </pre>
 
 * In the same way you can sort your particles in different buffer by passing a flag which will be passed to your container:
 
  * <pre class='brush: cpp'>
BRAMAS Berenger's avatar
BRAMAS Berenger committed
101 102
 * template <class FReal>
 * class MyCustomContainer : public FAbstractParticleContainer<FReal>{ 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
103 104 105
 * std::vector<int> bigParticles;
 * std::vector<int> smallParticles;
 * template<typename... Args>
BRAMAS Berenger's avatar
BRAMAS Berenger committed
106
 * void push(const FPoint<FReal>& , bool isBig, int particleIndex){
BRAMAS Berenger's avatar
BRAMAS Berenger committed
107 108
 *      if(isBig) bigParticles.push_back(particleIndex);
 *      else smallParticles.push_back(particleIndex);        
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
109
 * };
BRAMAS Berenger's avatar
BRAMAS Berenger committed
110 111
 *
 * // In the main
BRAMAS Berenger's avatar
BRAMAS Berenger committed
112 113 114
 *     typedef MyCustomContainer<double>      ContainerClass;
 *     typedef FSimpleLeaf<double, ContainerClass >                     LeafClass;
 *     typedef FOctree<double, FBasicCell, ContainerClass , LeafClass >  OctreeClass;
BRAMAS Berenger's avatar
BRAMAS Berenger committed
115 116 117 118 119 120
 * // From your system properties
 * OctreeClass tree(treeHeight, subHeight, loader.getBoxWidth(), loader.getCenterOfBox());
 *
 * // Add a particle
 * tree.push(FPoint(x, y, z), boolIsBigParticle, particleIndex);
 * // The octree will push in the FSimpleLeaf which will push in the MyCustomContainer which will store idx in the correct vector
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
121 122
 * </pre>

BRAMAS Berenger's avatar
BRAMAS Berenger committed
123
 * The FBasicParticleContainer class is given for those who would like to store one or several data type
BRAMAS Berenger's avatar
BRAMAS Berenger committed
124 125
 * of the same kind per particles (and their positions).
 * For example if some one wants to store one (or several) integers for the particles or
BRAMAS Berenger's avatar
BRAMAS Berenger committed
126 127 128 129 130 131 132 133 134
 * one (or several) double values per particles.
 
 
  * <pre class='brush: cpp'>
 *  // Declaration
 * template <unsigned NbAttributesPerParticle, class AttributeClass = FReal >
 * class FBasicParticleContainer : public FAbstractParticleContainer, public FAbstractSerializable;
  * </pre>
  
BRAMAS Berenger's avatar
BRAMAS Berenger committed
135 136
 * If for example you would like to store 2 doubles per particles (one initialized during the push
 * whereas the other if set to 0) you can use the following code: 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
 
  * <pre class='brush: cpp'>
 *     typedef FBasicParticleContainer< 2, double>      ContainerClass;
 *     typedef FSimpleLeaf< ContainerClass >                     LeafClass;
 *     typedef FOctree< FBasicCell, ContainerClass , LeafClass >  OctreeClass;
 * // From your system properties
 * OctreeClass tree(treeHeight, subHeight, loader.getBoxWidth(), loader.getCenterOfBox());
 *
 * // Add a particle
 * tree.push(FPoint(x, y, z), myFirstDouble); //, mySecondValueIfNotZero);
 *
 * // Then to print all the doubles value :
 * tree.forEachLeaf([&](LeafClass* lf){
 *      ContainerClass* container = lf->getSrc();
 *      int nbParticlesInLeaf = container->getNbParticles();
 *      double* x_pos = container->getPositions()[0];
 *      double* y_pos = container->getPositions()[1];
 *      double* z_pos = container->getPositions()[2];
 *      double* firstDoubleArray = container->getAttribute(0); // same as getAttribute<0>()
 *      double* secondDoubleArray = container->getAttribute(1); // same as getAttribute<1>()
 *
 *      for(int idxPart = 0 ; idxPart < nbParticlesInLeaf ; ++idxPart){
 *          std::cout << "Particle inserted " << idxPart << " in the leaf\n";
 *          std::cout << "Has position " << x_pos[idxPart] << " " << y_pos[idxPart] << " " << z_part[idxPart] << "\n";
 *          std::cout << "And values " << firstDoubleArray[idxPart] << " and " << secondDoubleArray[idxPart] << "\n";
 *      }
 * });
 * </pre>
 
 
 * Therefor, we propose a particle container called FP2PParticleContainer to store the position,
 * a force vector, a potential and a physical value per particle.
 * This container is one used in our kernels and you can read our P2P (or P2M/L2P) in order to catch
 * the way it works.
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
171 172 173 174 175 176

 * \subsection cells Cells

 * The same principle apply to cells. There is a minimum sets of
 * methods that must propose a cell class to be able to be used in the
 * octree. And then, there are some other methods that you can add to
BRAMAS Berenger's avatar
BRAMAS Berenger committed
177
 * make it usable per your kernel.
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
178 179 180 181 182 183 184 185 186 187 188 189 190 191

 * The class Src/Components/FAbstractCell.hpp shows what should
 * implement a cell:

 * <pre class='brush: cpp'> 
  * class FAbstractCell{ 
  *  public: 
  *  virtual ~FAbstractCell(){ 
  *  } 
  *  virtual MortonIndex getMortonIndex() const = 0; 
  *  virtual void setMortonIndex(const MortonIndex inIndex) = 0; 
  *  virtual void setPosition(const FPoint& inPosition) = 0; 
  *  virtual const FTreeCoordinate& getCoordinate() const = 0; 
  *  virtual void setCoordinate(const long inX, const long inY, const long inZ) = 0; 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
192 193 194 195
  *  virtual bool hasSrcChild() const = 0;  // Needed if TSM (target source model) is used
  *  virtual bool hasTargetsChild() const = 0;   // Needed if TSM (target source model) is used
  *  virtual void setSrcChildTrue() = 0;   // Needed if TSM (target source model) is used
  *  virtual void setTargetsChildTrue() = 0;   // Needed if TSM (target source model) is used
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
  *  }; 
 * </pre>
 
 * The FBasicCell class provides an implementation of all these
 * methods.

 * \subsection leaves Leaves 

 * The leaf is the class responsible of hosting the particles. The
 * octree uses this class and order to store a particle. Behind the
 * scene, the leaf does what it wants. But, the octree also needs a way
 * to get the particles it has inserted which can be targets or
 * sources

 * In the following class, FAbstractLeaf, one can see what is required
 * by the algorithm :

BRAMAS Berenger's avatar
BRAMAS Berenger committed
213
 * <pre class='brush: cpp'> 
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
214 215 216 217 218
 * template< class ParticleClass, class ContainerClass > 
 *   class FAbstractLeaf { 
 *   public: 
 *   // Default destructor
 *   virtual ~FAbstractLeaf(){ 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
219 220 221 222 223
 *   }     
 *  template<typename... Args>
 *   void push(const FPoint& inParticlePosition, Args ... args){
 *       FLOG( FLog::Controller.write("Warning, push is not implemented!").write(FLog::Flush) );
 *   }
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
224 225 226 227 228
 *   virtual ContainerClass* getSrc() = 0; 
 *   virtual ContainerClass* getTargets() = 0; 
 *   }; 
 * </pre>

BRAMAS Berenger's avatar
BRAMAS Berenger committed
229
 * The FSimpleLeaf class provides an implementation of all these methods.
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
230 231 232
 
 * \subsection loading Loading Particle

BRAMAS Berenger's avatar
BRAMAS Berenger committed
233 234 235
 * In most of our examples, we are using "loaders" which are classes used to manage the files.
 * They returned the physical properties (box width, center of box, ...) which are used to build the octree.
 * Then they are used to get the particle positions (and their physical values if appropriate).
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
236

BRAMAS Berenger's avatar
BRAMAS Berenger committed
237
 * <pre class='brush: cpp'> 
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
238 239 240 241 242 243 244 245 246 247
 * template <class ParticleClass> 
 *   class FAbstractLoader { 
 *   public:	 
 *   // Default destructor 
 *   virtual ~FAbstractLoader(){ 
 *   } 
 *   virtual FSize getNumberOfParticles() const = 0; 
 *   virtual FPoint getCenterOfBox() const = 0; 
 *   virtual FReal getBoxWidth() const = 0; 
 *   virtual bool isOpen() const = 0; 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
248
 *   void fillTree(FPoint& particlesPos);
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
249 250 251
 *  }; 
 * </pre>

BRAMAS Berenger's avatar
BRAMAS Berenger committed
252 253 254 255
 * There exist several loaders; one per file format.
 * Usually we do as the following:
 * <pre class='brush: cpp'> 
 * FRandomLoader loader(NbPart, 1, FPoint(0.5,0.5,0.5), 1);
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
256
 * OctreeClass tree(10, 3, loader.getBoxWidth(), loader.getCenterOfBox());
BRAMAS Berenger's avatar
BRAMAS Berenger committed
257 258 259 260 261
 * FPoint particlePosition;
 * for(int idxPart = 0 ; idxPart < loader.getNumberOfParticles() ; ++idxPart){
 *     loader.fillParticle(&particlePosition);
 *     tree.insert(particlePosition);
 * }
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
262 263 264 265
 * </pre>

 * \subsection octreeIterator Iterating on an Octree
 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
266 267
 * There are two ways to iterate on the data of an octree :
 * Using an iterator, or using a lambda function.
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
268

BRAMAS Berenger's avatar
BRAMAS Berenger committed
269
 * This next sample is taken from Tests/Utils/testOctreeIter.cpp and count the leaves :
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
270
 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
271
 * <pre class='brush: cpp'> 
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
272 273 274 275 276 277 278
 * OctreeClass::Iterator octreeIterator(&tree);
 *     octreeIterator.gotoBottomLeft();
 *     int counter = 0;
 *     do{
 *             ++counter;
 *     } while(octreeIterator.moveRight());
 * </pre>
BRAMAS Berenger's avatar
BRAMAS Berenger committed
279 280 281 282 283
 * But here is the equivalent using lambda function:
 * long counter = 0;
 * tree.forEachLeaf([&](LeafClass* leaf){
 *      ++counter;
 * });
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
284 285

 * To iterate on the cells we can proceed as follow :
BRAMAS Berenger's avatar
BRAMAS Berenger committed
286
 * <pre class='brush: cpp'> 
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
287 288 289 290 291 292 293 294 295 296 297 298
 * OctreeClass::Iterator octreeIterator(&tree);
 * octreeIterator.gotoBottomLeft();
 * for(int idxLevel = NbLevels - 1 ; idxLevel >= 1 ; --idxLevel ){
 *    int counter = 0;
 *    do{
 *       ++counter;
 *    } while(octreeIterator.moveRight());
 *    octreeIterator.moveUp();
 *    octreeIterator.gotoLeft();
 *    std::cout << "Cells at level " << idxLevel << " = " << counter << " ...\n";
 * }
 * </pre>
BRAMAS Berenger's avatar
BRAMAS Berenger committed
299 300 301 302 303 304
 * Here is an equivalent:
  * <pre class='brush: cpp'> 
  *  long nbCells[TreeHeight];
  *  tree.forEachCellWithLevel([&nbCells](CellClass* cell, int idxLevel){
  *      nbCells[idxLevel] += 1;
  *  });
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
305 306 307

 * \section kernel The kernel
 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
308
 * Kernel refers to the class that perform the computation.
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
309 310 311 312

 * An empty kernel can be found in Src/Components/FBasicKernels.hpp,
 * it implements the class definition FAbstractKernels :

BRAMAS Berenger's avatar
BRAMAS Berenger committed
313 314
 * <pre class='brush: cpp'> 
 * template< class CellClass, class ContainerClass> class FBasicKernels : public FAbstractKernels<CellClass,ContainerClass> { 
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
 * public: 
 *
 * // Default destructor
 * virtual ~FBasicKernels(){}
 * virtual void P2M(CellClass* const , const ContainerClass* const ) {}
 * virtual void M2M(CellClass* const FRestrict , const CellClass*const FRestrict *const FRestrict , const int ) {} 
 * virtual void M2L(CellClass* const FRestrict , const CellClass* [], const int , const int ) {}
 * virtual void L2L(const CellClass* const FRestrict , CellClass* FRestrict *const FRestrict  , const int ) {}
 * virtual void L2P(const CellClass* const , ContainerClass* const ){}
 * virtual void P2P(const FTreeCoordinate& , 
 *                  ContainerClass* const FRestrict , const ContainerClass* const FRestrict , 
 *                  ContainerClass* const [27], const int ){}
 * virtual void P2PRemote(const FTreeCoordinate& , 
 *                  ContainerClass* const FRestrict , const ContainerClass* const FRestrict , 
 *                  ContainerClass* const [27], const int ){}
 * </pre>

 * One example of kernel is the 'test' kernel called
 * FTestKernels. This kernels simply sum the particles (one particle
 * weigh = 1) so at the end of the simulation each particles should be
 * have a weigh of N. We just declare this kernel based on the
 * components type but usually do not call any method manually since
 * this is performed per the FMM core.

BRAMAS Berenger's avatar
BRAMAS Berenger committed
339 340
 * <pre class='brush: cpp'> 
 * typedef FTestKernels<CellClass, ContainerClass >         KernelClass;
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
341 342 343 344 345 346 347 348 349 350 351 352 353
 * KernelClass kernels;
 * </pre>

 * \section coreFMM The FMM Core
 
 * We showed how to have an octree and a kernel. Now, we show how to use
 * a Fmm Algorithm on the data. Remember, the FMM algorithm simply
 * takes the data from the octree and call the method of the
 * kernel. The goal is to have a FMM independent from the data.

 * The next sample is taken from Tests/Utils/testFmmAlgorithm.cpp and
 * use the basic sequential FMM :
 
BRAMAS Berenger's avatar
BRAMAS Berenger committed
354 355
 * <pre class='brush: cpp'> 
 * typedef FFmmAlgorithm<OctreeClass, CellClass, ContainerClass, KernelClass, LeafClass >     FmmClass;
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
356 357 358 359 360 361 362
 * FmmClass algo(&tree,&kernels);
 * algo.execute();
 * </pre>

 * To move to the OpenMP threaded FMM we can use the fallowing code by
 * changing 'FFmmAlgorithm' per 'FFmmAlgorithmThread' :

BRAMAS Berenger's avatar
BRAMAS Berenger committed
363 364
 * <pre class='brush: cpp'> 
 * typedef FFmmAlgorithmThread<OctreeClass, CellClass, ContainerClass, KernelClass, LeafClass >     FmmClass;
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
365 366 367 368 369 370 371 372 373 374 375 376 377 378
 * FmmClass algo(&tree,&kernels);
 * algo.execute();
 * </pre>

 \section reasons The reasons why ...
 
 * Of course the library is changing and re-factorized usually but
 * lets discuss about 'The reasons why' : 
 
 * <ul>
 * <li> Every things is templatized : 
 * <blockquote>
 * The reason is to avoid the use of virtual and abstract class. In
 * this page we present some abstract classes, but they are not really
BRAMAS Berenger's avatar
BRAMAS Berenger committed
379 380
 * use. They only define what we need, the minimum required to implement a
 * particle container or a cell. But the kernels should not work on an abstract
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
381 382 383 384 385 386 387 388 389 390
 * type but on the real data. This enable lots of compiler
 * optimizations and avoid the use of V-Table.
 * </blockquote>
 * </li>
 
 * <li>
 * Typedef is used like this : 
 * <blockquote>
 * It can take some time to understand how it works. But all our users
 * finally like the way of using typedef and template. As you will see
BRAMAS Berenger's avatar
BRAMAS Berenger committed
391 392
 * in most of the examples the struct is the same and you will not be
 * lost.
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
393 394 395 396 397 398 399
 * </blockquote>
 * </li>

 
 * </ul>

*/