LifeV
lifev/eta/tutorials/2_ETA_ADR/main.cpp
Go to the documentation of this file.
1 //@HEADER
2 /*
3 *******************************************************************************
4 
5  Copyright (C) 2004, 2005, 2007 EPFL, Politecnico di Milano, INRIA
6  Copyright (C) 2010 EPFL, Politecnico di Milano, Emory University
7 
8  This file is part of LifeV.
9 
10  LifeV is free software; you can redistribute it and/or modify
11  it under the terms of the GNU Lesser General Public License as published by
12  the Free Software Foundation, either version 3 of the License, or
13  (at your option) any later version.
14 
15  LifeV is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  Lesser General Public License for more details.
19 
20  You should have received a copy of the GNU Lesser General Public License
21  along with LifeV. If not, see <http://www.gnu.org/licenses/>.
22 
23 *******************************************************************************
24 */
25 //@HEADER
26 
27 /*!
28  @file
29  @brief Tutorial for the assembly of the ADR problem and comparison with classical assembly
30 
31  @author Samuel Quinodoz <samuel.quinodoz@epfl.ch>
32  @date 08-10-2010
33 
34  This tutorial aims at presenting further expressions and compare the performances with
35  the ones obtained with the classical assembly.
36 
37  Tutorials that should be read before: 1
38 
39  */
40 
41 // ---------------------------------------------------------------
42 // We start by including the same files as in the tutorial 1, with
43 // the two "eta" files being specific to the ET assembly. We also
44 // need to use vectors so the VectorEpetra file is included as
45 // well.
46 // ---------------------------------------------------------------
47 
48 
49 #include <Epetra_ConfigDefs.h>
50 #ifdef EPETRA_MPI
51 #include <mpi.h>
52 #include <Epetra_MpiComm.h>
53 #else
54 #include <Epetra_SerialComm.h>
55 #endif
56 
57 
58 #include <lifev/core/LifeV.hpp>
59 
60 #include <lifev/core/mesh/MeshPartitioner.hpp>
61 #include <lifev/core/mesh/RegionMesh3DStructured.hpp>
62 #include <lifev/core/mesh/RegionMesh.hpp>
63 
64 #include <lifev/core/array/MatrixEpetra.hpp>
65 #include <lifev/core/array/VectorEpetra.hpp>
66 
67 #include <lifev/eta/fem/ETFESpace.hpp>
68 #include <lifev/eta/expression/Integrate.hpp>
69 
70 #include <boost/shared_ptr.hpp>
71 
72 
73 // ---------------------------------------------------------------
74 // We include two additional files which are useful to make the
75 // assembly using the "classical way". We also include a file to
76 // monitor the different timings.
77 // ---------------------------------------------------------------
78 
79 #include <lifev/core/fem/FESpace.hpp>
80 #include <lifev/core/solver/ADRAssembler.hpp>
81 
82 #include <lifev/core/util/LifeChrono.hpp>
83 
84 
85 // ---------------------------------------------------------------
86 // We work in the LifeV namespace and define the mesh, matrix and
87 // vector types that we will need several times.
88 // ---------------------------------------------------------------
89 
90 using namespace LifeV;
91 
93 typedef MatrixEpetra<Real> matrix_Type;
94 typedef VectorEpetra vector_Type;
95 typedef FESpace<mesh_Type, MapEpetra>::function_Type function_Type;
96 
97 // ---------------------------------------------------------------
98 // We define then a function, which represents a velocity field,
99 // of magnitude 1 in the y direction (supposing an (x,y,z)
100 // reference frame).
101 // ---------------------------------------------------------------
102 
103 
104 Real betaFctRaw ( const Real& /* t */, const Real& /* x */, const Real& /* y */, const Real& /* z */, const ID& i )
105 {
106  if (i == 1)
107  {
108  return 1;
109  }
110  return 0;
111 }
112 function_Type betaFct (betaFctRaw);
113 
114 
115 
116 // ---------------------------------------------------------------
117 // As in the tutorial 1, we start with the definition of the
118 // MPI communicator and boolean for the outputs.
119 // ---------------------------------------------------------------
120 
121 int main ( int argc, char** argv )
122 {
123 
124 #ifdef HAVE_MPI
125  MPI_Init (&argc, &argv);
126  std::shared_ptr<Epetra_Comm> Comm (new Epetra_MpiComm (MPI_COMM_WORLD) );
127 #else
128  std::shared_ptr<Epetra_Comm> Comm (new Epetra_SerialComm);
129 #endif
130 
131  const bool verbose (Comm->MyPID() == 0);
132 
133 
134  // ---------------------------------------------------------------
135  // We define the mesh and parition it. We use again the domain
136  // (-1,1)x(-1,1)x(-1,1) and a structured mesh.
137  // ---------------------------------------------------------------
138 
139  if (verbose)
140  {
141  std::cout << " -- Building and partitioning the mesh ... " << std::flush;
142  }
143 
144  const UInt Nelements (10);
145 
146  std::shared_ptr< mesh_Type > fullMeshPtr (new mesh_Type ( Comm ) );
147 
148  regularMesh3D ( *fullMeshPtr, 1, Nelements, Nelements, Nelements, false,
149  2.0, 2.0, 2.0,
150  -1.0, -1.0, -1.0);
151 
152  std::shared_ptr< mesh_Type > meshPtr;
153  {
154  MeshPartitioner< mesh_Type > meshPart (fullMeshPtr, Comm);
155  meshPtr = meshPart.meshPartition();
156  }
157 
158  fullMeshPtr.reset();
159 
160  if (verbose)
161  {
162  std::cout << " done ! " << std::endl;
163  }
164 
165  // ---------------------------------------------------------------
166  // We start by defining the finite element spaces. We use the type
167  // FESpace for the classical way and copy it in an ETFESpace for
168  // the ET assembly.
169  //
170  // We also build similar spaces for the advection field. This
171  // space does not need to be the same as the solution space, even
172  // if this is the case here.
173  //
174  // We remark here two details:
175  // 1. The spaces for the advection (betaSpace and ETbetaSpace) are
176  // vectorial.
177  // 2. The constructor for the ETFESpace structures use an
178  // additional arguement, the geometric mapping. In the
179  // tutorial 1, this argument was omitted, so the geometric
180  // mapping was guessed from the mesh type.
181  //
182  // In the end, both solution spaces display their respective
183  // number of degrees of freedom, which must be the same.
184  // ---------------------------------------------------------------
185 
186  // ---------------------------------------------------------------
187  // We start by defining the finite element spaces. We use the type
188  // FESpace for the classical way and copy it in an ETFESpace for
189  // the ET assembly.
190  //
191  // We also build similar spaces for the advection field. This
192  // space does not need to be the same as the solution space, even
193  // if this is the case here.
194  //
195  // We remark here two details:
196  // 1. The spaces for the advection (betaSpace and ETbetaSpace) are
197  // vectorial.
198  // 2. The constructor for the ETFESpace structures use an
199  // additional arguement, the geometric mapping. In the
200  // tutorial 1, this argument was omitted, so the geometric
201  // mapping was guessed from the mesh type.
202  //
203  // In the end, both solution spaces display their respective
204  // number of degrees of freedom, which must be the same.
205  // ---------------------------------------------------------------
206 
207  if (verbose)
208  {
209  std::cout << " -- Building FESpaces ... " << std::flush;
210  }
211 
212  std::string uOrder ("P1");
213  std::string bOrder ("P1");
214 
215  std::shared_ptr<FESpace< mesh_Type, MapEpetra > > uSpace
216  ( new FESpace< mesh_Type, MapEpetra > (meshPtr, uOrder, 1, Comm) );
217 
218  std::shared_ptr<FESpace< mesh_Type, MapEpetra > > betaSpace
219  ( new FESpace< mesh_Type, MapEpetra > (meshPtr, bOrder, 3, Comm) );
220 
221  if (verbose)
222  {
223  std::cout << " done ! " << std::endl;
224  }
225  if (verbose)
226  {
227  std::cout << " ---> Dofs: " << uSpace->dof().numTotalDof() << std::endl;
228  }
229 
230  if (verbose)
231  {
232  std::cout << " -- Building ETFESpaces ... " << std::flush;
233  }
234 
235  std::shared_ptr<ETFESpace< mesh_Type, MapEpetra, 3, 1 > > ETuSpace
236  ( new ETFESpace< mesh_Type, MapEpetra, 3, 1 > (meshPtr, & (uSpace->refFE() ), & (uSpace->fe().geoMap() ), Comm) );
237 
238  std::shared_ptr<ETFESpace< mesh_Type, MapEpetra, 3, 3 > > ETbetaSpace
239  ( new ETFESpace< mesh_Type, MapEpetra, 3, 3 > (meshPtr, & (betaSpace->refFE() ), & (betaSpace->fe().geoMap() ), Comm) );
240 
241  if (verbose)
242  {
243  std::cout << " done ! " << std::endl;
244  }
245  if (verbose)
246  {
247  std::cout << " ---> Dofs: " << ETuSpace->dof().numTotalDof() << std::endl;
248  }
249 
250  // ---------------------------------------------------------------
251  // We interpolate then the advection function of the mesh at hand.
252  // This is performed with the classical FESpace only.
253  //
254  // Indeed, the interpolation has not yet been implemented for the
255  // ETFESpace and vector of values for the FESpace and ETFESpace
256  // are fully compatible (they use the same degrees of freedom
257  // numbering). Therefore, they can be exchanged at will. This is
258  // very important since some features have been implemented only
259  // for regular FESpace but not yet for ETFESpace (e.g. output
260  // functionalities).
261  // ---------------------------------------------------------------
262 
263  // ---------------------------------------------------------------
264  // We interpolate then the advection function of the mesh at hand.
265  // This is performed with the classical FESpace only.
266  //
267  // Indeed, the interpolation has not yet been implemented for the
268  // ETFESpace and vector of values for the FESpace and ETFESpace
269  // are fully compatible (they use the same degrees of freedom
270  // numbering). Therefore, they can be exchanged at will. This is
271  // very important since some features have been implemented only
272  // for regular FESpace but not yet for ETFESpace (e.g. output
273  // functionalities).
274  // ---------------------------------------------------------------
275 
276  if (verbose)
277  {
278  std::cout << " -- Interpolating the advection field ... " << std::flush;
279  }
280 
281  vector_Type beta (betaSpace->map(), Repeated);
282  betaSpace->interpolate (betaFct, beta, 0.0);
283 
284  if (verbose)
285  {
286  std::cout << " done! " << std::endl;
287  }
288 
289 
290  // ---------------------------------------------------------------
291  // We build now two matrices, one for each assembly procedure in
292  // order to compare them in the end. There is no difference in the
293  // construction of the matrices.
294  // ---------------------------------------------------------------
295 
296  if (verbose)
297  {
298  std::cout << " -- Defining the matrices ... " << std::flush;
299  }
300 
301  std::shared_ptr<matrix_Type> systemMatrix (new matrix_Type ( uSpace->map() ) );
302  *systemMatrix *= 0.0;
303 
304  std::shared_ptr<matrix_Type> ETsystemMatrix (new matrix_Type ( ETuSpace->map() ) );
305  *ETsystemMatrix *= 0.0;
306 
307  if (verbose)
308  {
309  std::cout << " done! " << std::endl;
310  }
311 
312 
313  // ---------------------------------------------------------------
314  // We come now to the assembly itself. We start with the classical
315  // way, which consists in instentiating an ADRAssembler and call
316  // the different methods from that class.
317  //
318  // Using a LifeChrono, we monitor the time required for the
319  // assembly.
320  //
321  // The terms assembled correspond, in the order of appearance, to
322  // - a laplacian term
323  // - an advection term
324  // - a reaction term (with coefficient 2.0), aka mass term.
325  // ---------------------------------------------------------------
326 
327  LifeChrono StdChrono;
328  StdChrono.start();
329 
330  if (verbose)
331  {
332  std::cout << " -- Classical assembly ... " << std::flush;
333  }
334 
335  ADRAssembler<mesh_Type, matrix_Type, vector_Type> adrAssembler;
336 
337  adrAssembler.setup (uSpace, betaSpace);
338 
339  adrAssembler.addDiffusion (systemMatrix, 1.0);
340 
341  adrAssembler.addAdvection (systemMatrix, beta);
342 
343  adrAssembler.addMass (systemMatrix, 2.0);
344 
345  StdChrono.stop();
346 
347  if (verbose)
348  {
349  std::cout << " done ! " << std::endl;
350  }
351  if (verbose)
352  {
353  std::cout << " Time : " << StdChrono.diff() << std::endl;
354  }
355 
356 
357  // ---------------------------------------------------------------
358  // We perform now the same assembly with the ET assembly and still
359  // monitor the timings required.
360  //
361  // As in tutorial 1, we need to use the special namespace. The
362  // arguments of the integrate function still have the same
363  // meaning, but the expression is not a bit different.
364  // Remark that to ensure a fair comparison, we use the quadrature
365  // rule used for the classical way (stored in the uSpace).
366  //
367  // One of the differences between the two assembly is that with
368  // the ET way, the weak formulation is immediately visible, while
369  // it is not with the classical way.
370  //
371  // For the advective term, we remark that a new expression is used
372  // for the interpolation of the velocity field. The value
373  // function is used with, as first argument the ETFESpace in
374  // which the velocity is given and in second argument the vector
375  // of the values.
376  //
377  // Remark also that the Real constants and VectorSmall constants
378  // can be used directly in the expressions to integrate.
379  // ---------------------------------------------------------------
380 
381  LifeChrono ETChrono;
382  ETChrono.start();
383 
384  if (verbose)
385  {
386  std::cout << " -- ET assembly ... " << std::flush;
387  }
388 
389 
390  {
391  using namespace ExpressionAssembly;
392 
393  integrate ( elements (ETuSpace->mesh() ),
394  uSpace->qr(),
395  ETuSpace,
396  ETuSpace,
397 
398  dot ( grad (phi_i) , grad (phi_j) )
399  + dot ( grad (phi_j) , value (ETbetaSpace, beta) ) *phi_i
400  + 2.0 * phi_i * phi_j
401 
402  )
403  >> ETsystemMatrix;
404  }
405 
406  ETChrono.stop();
407 
408  if (verbose)
409  {
410  std::cout << " done! " << std::endl;
411  }
412  if (verbose)
413  {
414  std::cout << " Time : " << ETChrono.diff() << std::endl;
415  }
416 
417 
418  // ---------------------------------------------------------------
419  // The timings displayed should indicate that the ET way is faster
420  // than the classical way. Indeed, the classical way loops over
421  // the elements for each term added (3 times here), assembles
422  // different local contributions for each term and adds them for
423  // each term. With the ET way, only one loop over the elements
424  // is required, computations are reused and only one local
425  // contribution is computed and added to the global matrix.
426  //
427  // In general, the longer is the expression, the better is the
428  // performance of the ET with respect to the classical way.
429  //
430  // We finally need to check that both yield the same matrix. In
431  // that aim, we need to finalize both matrices.
432  // ---------------------------------------------------------------
433 
434  if (verbose)
435  {
436  std::cout << " -- Closing the matrices ... " << std::flush;
437  }
438 
439  systemMatrix->globalAssemble();
440  ETsystemMatrix->globalAssemble();
441 
442  if (verbose)
443  {
444  std::cout << " done ! " << std::endl;
445  }
446 
447 
448  // ---------------------------------------------------------------
449  // We compute now the matrix of the difference and finally the
450  // norm of the difference. This should be very low if the two
451  // matrices are identical.
452  // ---------------------------------------------------------------
453 
454  if (verbose)
455  {
456  std::cout << " -- Computing the error ... " << std::flush;
457  }
458 
459  std::shared_ptr<matrix_Type> checkMatrix (new matrix_Type ( ETuSpace->map() ) );
460  *checkMatrix *= 0.0;
461 
462  *checkMatrix += *systemMatrix;
463  *checkMatrix += (*ETsystemMatrix) * (-1);
464 
465  checkMatrix->globalAssemble();
466 
467  Real errorNorm ( checkMatrix->normInf() );
468 
469  if (verbose)
470  {
471  std::cout << " done ! " << std::endl;
472  }
473 
474 
475  // ---------------------------------------------------------------
476  // We finalize the MPI if needed.
477  // ---------------------------------------------------------------
478 
479 #ifdef HAVE_MPI
480  MPI_Finalize();
481 #endif
482 
483 
484  // ---------------------------------------------------------------
485  // We finally display the error norm and compare with the
486  // tolerance of the test.
487  // ---------------------------------------------------------------
488 
489  if (verbose)
490  {
491  std::cout << " Matrix Error : " << errorNorm << std::endl;
492  }
493 
494  Real testTolerance (1e-10);
495 
496  if (errorNorm < testTolerance)
497  {
498  return ( EXIT_SUCCESS );
499  }
500  return ( EXIT_FAILURE );
501 }
int main(int argc, char **argv)
Real betaFctRaw(const Real &, const Real &, const Real &, const Real &, const ID &i)
MatrixEpetra< Real > matrix_Type
function_Type betaFct(betaFctRaw)
uint32_type ID
IDs.
Definition: LifeV.hpp:194
double Real
Generic real data.
Definition: LifeV.hpp:175
RegionMesh< LinearTetra > mesh_Type
FESpace< mesh_Type, MapEpetra >::function_Type function_Type