1 /* ---------------------------------------------------------------------
2  *
3  * Copyright (C) 2001 - 2020 by the deal.II authors
4  *
5  * This file is part of the deal.II library.
6  *
7  * The deal.II library is free software; you can use it, redistribute
8  * it, and/or modify it under the terms of the GNU Lesser General
9  * Public License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  * The full text of the license can be found in the file LICENSE.md at
12  * the top level directory of deal.II.
13  *
14  * ---------------------------------------------------------------------
15 
16  *
17  * Authors: Wolfgang Bangerth, Ralf Hartmann, University of Heidelberg, 2001
18  */
19 
20 
21 // The first of the following include files are probably well-known by now and
22 // need no further explanation.
23 #include <deal.II/base/quadrature_lib.h>
24 #include <deal.II/base/convergence_table.h>
25 #include <deal.II/grid/grid_generator.h>
26 #include <deal.II/grid/tria_accessor.h>
27 #include <deal.II/grid/tria_iterator.h>
28 #include <deal.II/grid/manifold_lib.h>
29 #include <deal.II/grid/tria.h>
30 #include <deal.II/grid/grid_out.h>
31 #include <deal.II/dofs/dof_handler.h>
32 #include <deal.II/dofs/dof_accessor.h>
33 #include <deal.II/fe/fe_values.h>
34 
35 // This include file is new. Even if we are not solving a PDE in this tutorial,
36 // we want to use a dummy finite element with zero degrees of freedoms provided
37 // by the FE_Nothing class.
38 #include <deal.II/fe/fe_nothing.h>
39 
40 // The following header file is also new: in it, we declare the MappingQ class
41 // which we will use for polynomial mappings of arbitrary order:
42 #include <deal.II/fe/mapping_q.h>
43 
44 // And this again is C++:
45 #include <iostream>
46 #include <fstream>
47 #include <cmath>
48 
49 // The last step is as in previous programs:
50 namespace Step10
51 {
52   using namespace dealii;
53 
54   // Now, as we want to compute the value of $\pi$, we have to compare to
55   // something. These are the first few digits of $\pi$, which we define
56   // beforehand for later use. Since we would like to compute the difference
57   // between two numbers which are quite accurate, with the accuracy of the
58   // computed approximation to $\pi$ being in the range of the number of
59   // digits which a double variable can hold, we rather declare the reference
60   // value as a <code>long double</code> and give it a number of extra digits:
61   const long double pi = 3.141592653589793238462643L;
62 
63 
64 
65   // Then, the first task will be to generate some output. Since this program
66   // is so small, we do not employ object oriented techniques in it and do not
67   // declare classes (although, of course, we use the object oriented features
68   // of the library). Rather, we just pack the functionality into separate
69   // functions. We make these functions templates on the number of space
70   // dimensions to conform to usual practice when using deal.II, although we
71   // will only use them for two space dimensions.
72   //
73   // The first of these functions just generates a triangulation of a circle
74   // (hyperball) and outputs the $Q_p$ mapping of its cells for different values
75   // of <code>p</code>. Then, we refine the grid once and do so again.
76   template <int dim>
gnuplot_output()77   void gnuplot_output()
78   {
79     std::cout << "Output of grids into gnuplot files:" << std::endl
80               << "===================================" << std::endl;
81 
82     // So first generate a coarse triangulation of the circle and associate a
83     // suitable boundary description to it. By default,
84     // GridGenerator::hyper_ball attaches a SphericalManifold to the boundary
85     // (and uses FlatManifold for the interior) so we simply call that
86     // function and move on:
87     Triangulation<dim> triangulation;
88     GridGenerator::hyper_ball(triangulation);
89 
90     // Then alternate between generating output on the current mesh
91     // for $Q_1$, $Q_2$, and $Q_3$ mappings, and (at the end of the
92     // loop body) refining the mesh once globally.
93     for (unsigned int refinement = 0; refinement < 2; ++refinement)
94       {
95         std::cout << "Refinement level: " << refinement << std::endl;
96 
97         std::string filename_base = "ball_" + std::to_string(refinement);
98 
99         for (unsigned int degree = 1; degree < 4; ++degree)
100           {
101             std::cout << "Degree = " << degree << std::endl;
102 
103             // For this, first set up an object describing the mapping. This
104             // is done using the MappingQ class, which takes as
105             // argument to the constructor the polynomial degree which it
106             // shall use.
107             const MappingQ<dim> mapping(degree);
108             // As a side note, for a piecewise linear mapping, you
109             // could give a value of <code>1</code> to the constructor
110             // of MappingQ, but there is also a class MappingQ1 that
111             // achieves the same effect. Historically, it did a lot of
112             // things in a simpler way than MappingQ but is today just
113             // a wrapper around the latter. It is, however, still the
114             // class that is used implicitly in many places of the
115             // library if you do not specify another mapping
116             // explicitly.
117 
118 
119             // In order to actually write out the present grid with this
120             // mapping, we set up an object which we will use for output. We
121             // will generate Gnuplot output, which consists of a set of lines
122             // describing the mapped triangulation. By default, only one line
123             // is drawn for each face of the triangulation, but since we want
124             // to explicitly see the effect of the mapping, we want to have
125             // the faces in more detail. This can be done by passing the
126             // output object a structure which contains some flags. In the
127             // present case, since Gnuplot can only draw straight lines, we
128             // output a number of additional points on the faces so that each
129             // face is drawn by 30 small lines instead of only one. This is
130             // sufficient to give us the impression of seeing a curved line,
131             // rather than a set of straight lines.
132             GridOut               grid_out;
133             GridOutFlags::Gnuplot gnuplot_flags(false, 60);
134             grid_out.set_flags(gnuplot_flags);
135 
136             // Finally, generate a filename and a file for output:
137             std::string filename =
138               filename_base + "_mapping_q_" + std::to_string(degree) + ".dat";
139             std::ofstream gnuplot_file(filename);
140 
141             // Then write out the triangulation to this file. The last
142             // argument of the function is a pointer to a mapping object. This
143             // argument has a default value, and if no value is given a simple
144             // MappingQ1 object is taken, which we briefly
145             // described above. This would then result in a piecewise linear
146             // approximation of the true boundary in the output.
147             grid_out.write_gnuplot(triangulation, gnuplot_file, &mapping);
148           }
149         std::cout << std::endl;
150 
151         // At the end of the loop, refine the mesh globally.
152         triangulation.refine_global();
153       }
154   }
155 
156   // Now we proceed with the main part of the code, the approximation of
157   // $\pi$. The area of a circle is of course given by $\pi r^2$, so having a
158   // circle of radius 1, the area represents just the number that is searched
159   // for. The numerical computation of the area is performed by integrating
160   // the constant function of value 1 over the whole computational domain,
161   // i.e. by computing the areas $\int_K 1 dx=\int_{\hat K} 1
162   // \ \textrm{det}\ J(\hat x) d\hat x \approx \sum_i \textrm{det}
163   // \ J(\hat x_i)w(\hat x_i)$,
164   // where the sum extends over all quadrature points on all active cells in
165   // the triangulation, with $w(x_i)$ being the weight of quadrature point
166   // $x_i$. The integrals on each cell are approximated by numerical
167   // quadrature, hence the only additional ingredient we need is to set up a
168   // FEValues object that provides the corresponding `JxW' values of each
169   // cell. (Note that `JxW' is meant to abbreviate <i>Jacobian determinant
170   // times weight</i>; since in numerical quadrature the two factors always
171   // occur at the same places, we only offer the combined quantity, rather
172   // than two separate ones.) We note that here we won't use the FEValues
173   // object in its original purpose, i.e. for the computation of values of
174   // basis functions of a specific finite element at certain quadrature
175   // points. Rather, we use it only to gain the `JxW' at the quadrature
176   // points, irrespective of the (dummy) finite element we will give to the
177   // constructor of the FEValues object. The actual finite element given to
178   // the FEValues object is not used at all, so we could give any.
179   template <int dim>
compute_pi_by_area()180   void compute_pi_by_area()
181   {
182     std::cout << "Computation of Pi by the area:" << std::endl
183               << "==============================" << std::endl;
184 
185     // For the numerical quadrature on all cells we employ a quadrature rule
186     // of sufficiently high degree. We choose QGauss that is of order 8 (4
187     // points), to be sure that the errors due to numerical quadrature are of
188     // higher order than the order (maximal 6) that will occur due to the
189     // order of the approximation of the boundary, i.e. the order of the
190     // mappings employed. Note that the integrand, the Jacobian determinant,
191     // is not a polynomial function (rather, it is a rational one), so we do
192     // not use Gauss quadrature in order to get the exact value of the
193     // integral as done often in finite element computations, but could as
194     // well have used any quadrature formula of like order instead.
195     const QGauss<dim> quadrature(4);
196 
197     // Now start by looping over polynomial mapping degrees=1..4:
198     for (unsigned int degree = 1; degree < 5; ++degree)
199       {
200         std::cout << "Degree = " << degree << std::endl;
201 
202         // First generate the triangulation, the boundary and the mapping
203         // object as already seen.
204         Triangulation<dim> triangulation;
205         GridGenerator::hyper_ball(triangulation);
206 
207         const MappingQ<dim> mapping(degree);
208 
209         // We now create a finite element. Unlike the rest of the example
210         // programs, we do not actually need to do any computations with shape
211         // functions; we only need the `JxW' values from an FEValues
212         // object. Hence we use the special finite element class FE_Nothing
213         // which has exactly zero degrees of freedom per cell (as the name
214         // implies, the local basis on each cell is the empty set). A more
215         // typical usage of FE_Nothing is shown in step-46.
216         const FE_Nothing<dim> fe;
217 
218         // Likewise, we need to create a DoFHandler object. We do not actually
219         // use it, but it will provide us with `active_cell_iterators' that
220         // are needed to reinitialize the FEValues object on each cell of the
221         // triangulation.
222         DoFHandler<dim> dof_handler(triangulation);
223 
224         // Now we set up the FEValues object, giving the Mapping, the dummy
225         // finite element and the quadrature object to the constructor,
226         // together with the update flags asking for the `JxW' values at the
227         // quadrature points only. This tells the FEValues object that it
228         // needs not compute other quantities upon calling the
229         // <code>reinit</code> function, thus saving computation time.
230         //
231         // The most important difference in the construction of the FEValues
232         // object compared to previous example programs is that we pass a
233         // mapping object as first argument, which is to be used in the
234         // computation of the mapping from unit to real cell. In previous
235         // examples, this argument was omitted, resulting in the implicit use
236         // of an object of type MappingQ1.
237         FEValues<dim> fe_values(mapping, fe, quadrature, update_JxW_values);
238 
239         // We employ an object of the ConvergenceTable class to store all
240         // important data like the approximated values for $\pi$ and the error
241         // with respect to the true value of $\pi$. We will also use functions
242         // provided by the ConvergenceTable class to compute convergence rates
243         // of the approximations to $\pi$.
244         ConvergenceTable table;
245 
246         // Now we loop over several refinement steps of the triangulation.
247         for (unsigned int refinement = 0; refinement < 6;
248              ++refinement, triangulation.refine_global(1))
249           {
250             // In this loop we first add the number of active cells of the
251             // current triangulation to the table. This function automatically
252             // creates a table column with superscription `cells', in case
253             // this column was not created before.
254             table.add_value("cells", triangulation.n_active_cells());
255 
256             // Then we distribute the degrees of freedom for the dummy finite
257             // element. Strictly speaking we do not need this function call in
258             // our special case but we call it to make the DoFHandler happy --
259             // otherwise it would throw an assertion in the FEValues::reinit
260             // function below.
261             dof_handler.distribute_dofs(fe);
262 
263             // We define the variable area as `long double' like we did for
264             // the pi variable before.
265             long double area = 0;
266 
267             // Now we loop over all cells, reinitialize the FEValues object
268             // for each cell, and add up all the `JxW' values for this cell to
269             // `area'...
270             for (const auto &cell : dof_handler.active_cell_iterators())
271               {
272                 fe_values.reinit(cell);
273                 for (unsigned int i = 0; i < fe_values.n_quadrature_points; ++i)
274                   area += static_cast<long double>(fe_values.JxW(i));
275               }
276 
277             // ...and store the resulting area values and the errors in the
278             // table. We need a static cast to double as there is no
279             // add_value(string, long double) function implemented. Note that
280             // this also concerns the second call as the <code>fabs</code>
281             // function in the <code>std</code> namespace is overloaded on its
282             // argument types, so there exists a version taking and returning
283             // a <code>long double</code>, in contrast to the global namespace
284             // where only one such function is declared (which takes and
285             // returns a double).
286             table.add_value("eval.pi", static_cast<double>(area));
287             table.add_value("error", static_cast<double>(std::fabs(area - pi)));
288           }
289 
290         // We want to compute the convergence rates of the `error'
291         // column. Therefore we need to omit the other columns from the
292         // convergence rate evaluation before calling
293         // `evaluate_all_convergence_rates'
294         table.omit_column_from_convergence_rate_evaluation("cells");
295         table.omit_column_from_convergence_rate_evaluation("eval.pi");
296         table.evaluate_all_convergence_rates(
297           ConvergenceTable::reduction_rate_log2);
298 
299         // Finally we set the precision and scientific mode for output of some
300         // of the quantities...
301         table.set_precision("eval.pi", 16);
302         table.set_scientific("error", true);
303 
304         // ...and write the whole table to std::cout.
305         table.write_text(std::cout);
306 
307         std::cout << std::endl;
308       }
309   }
310 
311 
312   // The following, second function also computes an approximation of $\pi$
313   // but this time via the perimeter $2\pi r$ of the domain instead of the
314   // area. This function is only a variation of the previous function. So we
315   // will mainly give documentation for the differences.
316   template <int dim>
compute_pi_by_perimeter()317   void compute_pi_by_perimeter()
318   {
319     std::cout << "Computation of Pi by the perimeter:" << std::endl
320               << "===================================" << std::endl;
321 
322     // We take the same order of quadrature but this time a `dim-1'
323     // dimensional quadrature as we will integrate over (boundary) lines
324     // rather than over cells.
325     const QGauss<dim - 1> quadrature(4);
326 
327     // We loop over all degrees, create the triangulation, the boundary, the
328     // mapping, the dummy finite element and the DoFHandler object as seen
329     // before.
330     for (unsigned int degree = 1; degree < 5; ++degree)
331       {
332         std::cout << "Degree = " << degree << std::endl;
333         Triangulation<dim> triangulation;
334         GridGenerator::hyper_ball(triangulation);
335 
336         const MappingQ<dim>   mapping(degree);
337         const FE_Nothing<dim> fe;
338 
339         DoFHandler<dim> dof_handler(triangulation);
340 
341         // Then we create a FEFaceValues object instead of a FEValues object
342         // as in the previous function. Again, we pass a mapping as first
343         // argument.
344         FEFaceValues<dim> fe_face_values(mapping,
345                                          fe,
346                                          quadrature,
347                                          update_JxW_values);
348         ConvergenceTable  table;
349 
350         for (unsigned int refinement = 0; refinement < 6;
351              ++refinement, triangulation.refine_global(1))
352           {
353             table.add_value("cells", triangulation.n_active_cells());
354 
355             dof_handler.distribute_dofs(fe);
356 
357             // Now we run over all cells and over all faces of each cell. Only
358             // the contributions of the `JxW' values on boundary faces are
359             // added to the long double variable `perimeter'.
360             long double perimeter = 0;
361             for (const auto &cell : dof_handler.active_cell_iterators())
362               for (const auto &face : cell->face_iterators())
363                 if (face->at_boundary())
364                   {
365                     // We reinit the FEFaceValues object with the cell
366                     // iterator and the number of the face.
367                     fe_face_values.reinit(cell, face);
368                     for (unsigned int i = 0;
369                          i < fe_face_values.n_quadrature_points;
370                          ++i)
371                       perimeter +=
372                         static_cast<long double>(fe_face_values.JxW(i));
373                   }
374             // Then store the evaluated values in the table...
375             table.add_value("eval.pi", static_cast<double>(perimeter / 2.0L));
376             table.add_value(
377               "error", static_cast<double>(std::fabs(perimeter / 2.0L - pi)));
378           }
379 
380         // ...and end this function as we did in the previous one:
381         table.omit_column_from_convergence_rate_evaluation("cells");
382         table.omit_column_from_convergence_rate_evaluation("eval.pi");
383         table.evaluate_all_convergence_rates(
384           ConvergenceTable::reduction_rate_log2);
385 
386         table.set_precision("eval.pi", 16);
387         table.set_scientific("error", true);
388 
389         table.write_text(std::cout);
390 
391         std::cout << std::endl;
392       }
393   }
394 } // namespace Step10
395 
396 
397 // The following main function just calls the above functions in the order of
398 // their appearance. Apart from this, it looks just like the main functions of
399 // previous tutorial programs.
main()400 int main()
401 {
402   try
403     {
404       std::cout.precision(16);
405 
406       Step10::gnuplot_output<2>();
407 
408       Step10::compute_pi_by_area<2>();
409       Step10::compute_pi_by_perimeter<2>();
410     }
411   catch (std::exception &exc)
412     {
413       std::cerr << std::endl
414                 << std::endl
415                 << "----------------------------------------------------"
416                 << std::endl;
417       std::cerr << "Exception on processing: " << std::endl
418                 << exc.what() << std::endl
419                 << "Aborting!" << std::endl
420                 << "----------------------------------------------------"
421                 << std::endl;
422 
423       return 1;
424     }
425   catch (...)
426     {
427       std::cerr << std::endl
428                 << std::endl
429                 << "----------------------------------------------------"
430                 << std::endl;
431       std::cerr << "Unknown exception!" << std::endl
432                 << "Aborting!" << std::endl
433                 << "----------------------------------------------------"
434                 << std::endl;
435       return 1;
436     }
437 
438   return 0;
439 }
440