libMesh
adaptivity_ex4.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 // <h1>Adaptivity Example 4 - Biharmonic Equation</h1>
19 // \author Benjamin S. Kirk
20 // \date 2004
21 //
22 // This example solves the Biharmonic equation on a square or cube,
23 // using a Galerkin formulation with C1 elements approximating the
24 // H^2_0 function space.
25 // The initial mesh contains two TRI6, one QUAD9 or one HEX27
26 // An input file named "ex15.in"
27 // is provided which allows the user to set several parameters for
28 // the solution so that the problem can be re-run without a
29 // re-compile. The solution technique employed is to have a
30 // refinement loop with a linear solve inside followed by a
31 // refinement of the grid and projection of the solution to the new grid
32 // In the final loop iteration, there is no additional
33 // refinement after the solve. In the input file "ex15.in", the variable
34 // "max_r_steps" controls the number of refinement steps, and
35 // "max_r_level" controls the maximum element refinement level.
36 
37 // LibMesh include files.
38 #include "libmesh/mesh.h"
39 #include "libmesh/equation_systems.h"
40 #include "libmesh/linear_implicit_system.h"
41 #include "libmesh/exodusII_io.h"
42 #include "libmesh/fe.h"
43 #include "libmesh/quadrature.h"
44 #include "libmesh/dense_matrix.h"
45 #include "libmesh/dense_vector.h"
46 #include "libmesh/sparse_matrix.h"
47 #include "libmesh/mesh_generation.h"
48 #include "libmesh/mesh_modification.h"
49 #include "libmesh/mesh_refinement.h"
50 #include "libmesh/error_vector.h"
51 #include "libmesh/fourth_error_estimators.h"
52 #include "libmesh/getpot.h"
53 #include "libmesh/exact_solution.h"
54 #include "libmesh/dof_map.h"
55 #include "libmesh/numeric_vector.h"
56 #include "libmesh/elem.h"
57 #include "libmesh/tensor_value.h"
58 #include "libmesh/perf_log.h"
59 #include "libmesh/string_to_enum.h"
60 #include "libmesh/enum_solver_package.h"
61 #include "libmesh/dirichlet_boundaries.h"
62 #include "libmesh/wrapped_function.h"
63 
64 // Bring in everything from the libMesh namespace
65 using namespace libMesh;
66 
67 // Function prototype. This is the function that will assemble
68 // the linear system for our Biharmonic problem. Note that the
69 // function will take the EquationSystems object and the
70 // name of the system we are assembling as input. From the
71 // EquationSystems object we have access to the Mesh and
72 // other objects we might need.
74  const std::string & system_name);
75 
76 // Global parameter, to be accessible to the above function
77 bool penalty_dirichlet = true;
78 
79 
80 // Prototypes for calculation of the exact solution. Necessary
81 // for setting boundary conditions.
83  const Parameters &,
84  const std::string &,
85  const std::string &);
86 
88  const Parameters &, // parameters, not needed
89  const std::string &, // sys_name, not needed
90  const std::string &); // unk_name, not needed);
91 
93  const Parameters &,
94  const std::string &,
95  const std::string &);
96 
97 // Prototypes for calculation of the gradient of the exact solution.
98 // Necessary for setting boundary conditions in H^2_0 and testing
99 // H^1 convergence of the solution
101  const Parameters &,
102  const std::string &,
103  const std::string &);
104 
106  const Parameters &,
107  const std::string &,
108  const std::string &);
109 
111  const Parameters &,
112  const std::string &,
113  const std::string &);
114 
115 Tensor exact_1D_hessian(const Point & p,
116  const Parameters &,
117  const std::string &,
118  const std::string &);
119 
120 Tensor exact_2D_hessian(const Point & p,
121  const Parameters &,
122  const std::string &,
123  const std::string &);
124 
125 Tensor exact_3D_hessian(const Point & p,
126  const Parameters &,
127  const std::string &,
128  const std::string &);
129 
130 Number forcing_function_1D(const Point & p);
131 
132 Number forcing_function_2D(const Point & p);
133 
134 Number forcing_function_3D(const Point & p);
135 
136 // Pointers to dimension-independent functions
138  const Parameters &,
139  const std::string &,
140  const std::string &);
141 
143  const Parameters &,
144  const std::string &,
145  const std::string &);
146 
148  const Parameters &,
149  const std::string &,
150  const std::string &);
151 
153 
154 
155 
156 int main(int argc, char ** argv)
157 {
158  // Initialize libMesh.
159  LibMeshInit init (argc, argv);
160 
161  // This example requires a linear solver package.
162  libmesh_example_requires(libMesh::default_solver_package() != INVALID_SOLVER_PACKAGE,
163  "--enable-petsc, --enable-trilinos, or --enable-eigen");
164 
165  // Adaptive constraint calculations for fine Hermite elements seems
166  // to require half-decent precision
167 #ifdef LIBMESH_DEFAULT_SINGLE_PRECISION
168  libmesh_example_requires(false, "double precision");
169 #endif
170 
171  // This example requires Adaptive Mesh Refinement support
172 #ifndef LIBMESH_ENABLE_AMR
173  libmesh_example_requires(false, "--enable-amr");
174 #else
175 
176  // This example requires second derivative calculation support
177 #ifndef LIBMESH_ENABLE_SECOND_DERIVATIVES
178  libmesh_example_requires(false, "--enable-second");
179 #else
180 
181  // Parse the input file
182  GetPot input_file("adaptivity_ex4.in");
183 
184  // But allow the command line to override it.
185  input_file.parse_command_line(argc, argv);
186 
187  // Read in parameters from the input file
188  const unsigned int max_r_level = input_file("max_r_level", 10);
189  const unsigned int max_r_steps = input_file("max_r_steps", 4);
190  const std::string approx_type = input_file("approx_type", "HERMITE");
191  const std::string approx_order_string = input_file("approx_order", "THIRD");
192  const unsigned int uniform_refine = input_file("uniform_refine", 0);
193  const Real refine_percentage = input_file("refine_percentage", 0.5);
194  const Real coarsen_percentage = input_file("coarsen_percentage", 0.5);
195  const unsigned int dim = input_file("dimension", 2);
196  const unsigned int max_linear_iterations = input_file("max_linear_iterations", 10000);
197  penalty_dirichlet = input_file("penalty_dirichlet", penalty_dirichlet);
198 
199  // Skip higher-dimensional examples on a lower-dimensional libMesh build
200  libmesh_example_requires(dim <= LIBMESH_DIM, "2D/3D support");
201 
202  // Currently only the Hermite cubics give a 1D or 3D C^1 basis
203  libmesh_assert (dim == 2 || approx_type == "HERMITE");
204 
205  // Create a mesh, with dimension to be overridden later, on the
206  // default MPI communicator.
207  Mesh mesh(init.comm());
208 
209  // Output file for plotting the error
210  std::string output_file = "";
211 
212  if (dim == 1)
213  output_file += "1D_";
214  else if (dim == 2)
215  output_file += "2D_";
216  else if (dim == 3)
217  output_file += "3D_";
218 
219  if (approx_type == "HERMITE")
220  output_file += "hermite_";
221  else if (approx_type == "SECOND")
222  output_file += "reducedclough_";
223  else
224  output_file += "clough_";
225 
226  output_file += approx_order_string;
227 
228  if (uniform_refine == 0)
229  output_file += "_adaptive";
230  else
231  output_file += "_uniform";
232 
233 #ifdef LIBMESH_HAVE_EXODUS_API
234  // If we have Exodus, use the same base output filename
235  std::string exd_file = output_file;
236  exd_file += ".e";
237 #endif // #ifdef LIBMESH_HAVE_EXODUS_API
238 
239  output_file += ".m";
240 
241  std::ofstream out (output_file.c_str());
242  out << "% dofs L2-error H1-error H2-error\n"
243  << "e = [\n";
244 
245  // Set up the dimension-dependent coarse mesh and solution
246  if (dim == 1)
247  {
253  }
254 
255  if (dim == 2)
256  {
262  }
263  else if (dim == 3)
264  {
270  }
271 
272  // Convert the mesh to second order: necessary for computing with
273  // Clough-Tocher elements, useful for getting slightly less
274  // broken visualization output with Hermite elements
276 
277  // Convert it to triangles if necessary
278  if (approx_type != "HERMITE")
280 
281  // Mesh Refinement object
282  MeshRefinement mesh_refinement(mesh);
283  mesh_refinement.refine_fraction() = refine_percentage;
284  mesh_refinement.coarsen_fraction() = coarsen_percentage;
285  mesh_refinement.max_h_level() = max_r_level;
286 
287  // Create an equation systems object.
288  EquationSystems equation_systems (mesh);
289 
290  // Declare the system and its variables.
291  // Create a system named "Biharmonic"
292  LinearImplicitSystem & system =
293  equation_systems.add_system<LinearImplicitSystem> ("Biharmonic");
294 
295  Order approx_order = approx_type == "SECOND" ? SECOND :
296  Utility::string_to_enum<Order>(approx_order_string);
297 
298  // Adds the variable "u" to "Biharmonic". "u" will be approximated
299  // using Hermite tensor product squares or Clough-Tocher triangles
300 
301  if (approx_type == "HERMITE")
302  system.add_variable("u", approx_order, HERMITE);
303  else if (approx_type == "SECOND")
304  system.add_variable("u", SECOND, CLOUGH);
305  else if (approx_type == "CLOUGH")
306  system.add_variable("u", approx_order, CLOUGH);
307  else
308  libmesh_error_msg("Invalid approx_type = " << approx_type);
309 
310  // Give the system a pointer to the matrix assembly
311  // function.
313 
314  // Give the system Dirichlet boundary conditions, if we want to
315  // enforce those strongly.
316  if (!penalty_dirichlet)
317  {
318  const std::vector<unsigned int>
319  all_vars(1, system.variable_number("u"));
320  std::set<boundary_id_type> all_bdys;
321  for (unsigned int i=0; i != dim; ++i)
322  {
323  all_bdys.insert(2*i);
324  all_bdys.insert(2*i+1);
325  }
326 
327  WrappedFunction<Number> exact_val(system, exact_solution);
329  DirichletBoundary exact_bc(all_bdys, all_vars, exact_val,
330  exact_grad);
331  system.get_dof_map().add_dirichlet_boundary(exact_bc);
332  }
333 
334  // Initialize the data structures for the equation system.
335  equation_systems.init();
336 
337  // Set linear solver max iterations
338  equation_systems.parameters.set<unsigned int>
339  ("linear solver maximum iterations") = max_linear_iterations;
340 
341  // Linear solver tolerance.
342  equation_systems.parameters.set<Real>
343  ("linear solver tolerance") = TOLERANCE * TOLERANCE;
344 
345  // Prints information about the system to the screen.
346  equation_systems.print_info();
347 
348  // Construct ExactSolution object and attach function to compute exact solution
349  ExactSolution exact_sol(equation_systems);
353 
354  // Construct zero solution object, useful for computing solution norms
355  // Attaching "zero_solution" functions is unnecessary
356  ExactSolution zero_sol(equation_systems);
357 
358  // A refinement loop.
359  for (unsigned int r_step=0; r_step<max_r_steps; r_step++)
360  {
361  mesh.print_info();
362  equation_systems.print_info();
363 
364  libMesh::out << "Beginning Solve " << r_step << std::endl;
365 
366  // Solve the system "Biharmonic", just like example 2.
367  system.solve();
368 
369  libMesh::out << "Linear solver converged at step: "
370  << system.n_linear_iterations()
371  << ", final residual: "
372  << system.final_linear_residual()
373  << std::endl;
374 
375  // Compute the error.
376  exact_sol.compute_error("Biharmonic", "u");
377  // Compute the norm.
378  zero_sol.compute_error("Biharmonic", "u");
379 
380  // Print out the error values
381  libMesh::out << "L2-Norm is: "
382  << zero_sol.l2_error("Biharmonic", "u")
383  << std::endl;
384  libMesh::out << "H1-Norm is: "
385  << zero_sol.h1_error("Biharmonic", "u")
386  << std::endl;
387  libMesh::out << "H2-Norm is: "
388  << zero_sol.h2_error("Biharmonic", "u")
389  << std::endl
390  << std::endl;
391  libMesh::out << "L2-Error is: "
392  << exact_sol.l2_error("Biharmonic", "u")
393  << std::endl;
394  libMesh::out << "H1-Error is: "
395  << exact_sol.h1_error("Biharmonic", "u")
396  << std::endl;
397  libMesh::out << "H2-Error is: "
398  << exact_sol.h2_error("Biharmonic", "u")
399  << std::endl
400  << std::endl;
401 
402  // Print to output file
403  out << equation_systems.n_active_dofs() << " "
404  << exact_sol.l2_error("Biharmonic", "u") << " "
405  << exact_sol.h1_error("Biharmonic", "u") << " "
406  << exact_sol.h2_error("Biharmonic", "u") << std::endl;
407 
408  // Assert that we're not outright diverging here, for regression
409  // testing. These bounds just come from eyeballing some graphs
410  // and tacking on a huge tolerance.
411  const dof_id_type n_dofs = system.n_dofs();
412 
413  if (exact_sol.l2_error("Biharmonic", "u") > 200*std::pow(n_dofs,-4./3.))
414  libmesh_error_msg("Unacceptably high L2-error!");
415 
416  if (exact_sol.h1_error("Biharmonic", "u") > 200./n_dofs)
417  libmesh_error_msg("Unacceptably high H1-error!");
418 
419  if (exact_sol.h2_error("Biharmonic", "u") > 400*std::pow(n_dofs,-2./3.))
420  libmesh_error_msg("Unacceptably high H2-error!");
421 
422  // Possibly refine the mesh
423  if (r_step+1 != max_r_steps)
424  {
425  libMesh::out << " Refining the mesh..." << std::endl;
426 
427  if (uniform_refine == 0)
428  {
429  ErrorVector error;
430  LaplacianErrorEstimator error_estimator;
431 
432  // This is another subclass of JumpErrorEstimator, based
433  // on measuring discontinuities across sides between
434  // elements, and we can tell it to use a cheaper
435  // "unweighted" quadrature rule when numerically
436  // integrating those discontinuities.
437  error_estimator.use_unweighted_quadrature_rules = true;
438 
439  error_estimator.estimate_error(system, error);
440  mesh_refinement.flag_elements_by_elem_fraction (error);
441 
442  libMesh::out << "Mean Error: " << error.mean() << std::endl;
443  libMesh::out << "Error Variance: " << error.variance() << std::endl;
444 
445  mesh_refinement.refine_and_coarsen_elements();
446  }
447  else
448  {
449  mesh_refinement.uniformly_refine(1);
450  }
451 
452  // This call reinitializes the EquationSystems object for
453  // the newly refined mesh. One of the steps in the
454  // reinitialization is projecting the solution,
455  // old_solution, etc... vectors from the old mesh to
456  // the current one.
457  equation_systems.reinit ();
458  }
459  }
460 
461 #ifdef LIBMESH_HAVE_EXODUS_API
462  // After solving the system write the solution
463  // to a ExodusII-formatted plot file.
465  equation_systems);
466 #endif // #ifdef LIBMESH_HAVE_EXODUS_API
467 
468  // Close up the output file.
469  out << "];\n"
470  << "hold on\n"
471  << "loglog(e(:,1), e(:,2), 'bo-');\n"
472  << "loglog(e(:,1), e(:,3), 'ro-');\n"
473  << "loglog(e(:,1), e(:,4), 'go-');\n"
474  << "xlabel('log(dofs)');\n"
475  << "ylabel('log(error)');\n"
476  << "title('C1 " << approx_type << " elements');\n"
477  << "legend('L2-error', 'H1-error', 'H2-error');\n";
478 
479  // All done.
480  return 0;
481 #endif // #ifndef LIBMESH_ENABLE_SECOND_DERIVATIVES
482 #endif // #ifndef LIBMESH_ENABLE_AMR
483 }
484 
485 
486 
488  const Parameters &, // parameters, not needed
489  const std::string &, // sys_name, not needed
490  const std::string &) // unk_name, not needed
491 {
492  // x coordinate in space
493  const Real x = p(0);
494 
495  // analytic solution value
496  return 256.*(x-x*x)*(x-x*x);
497 }
498 
499 
500 // We now define the gradient of the exact solution
502  const Parameters &, // parameters, not needed
503  const std::string &, // sys_name, not needed
504  const std::string &) // unk_name, not needed
505 {
506  // x coordinate in space
507  const Real x = p(0);
508 
509  // First derivatives to be returned.
510  Gradient gradu;
511 
512  gradu(0) = 256.*2.*(x-x*x)*(1-2*x);
513 
514  return gradu;
515 }
516 
517 
518 // We now define the hessian of the exact solution
520  const Parameters &, // parameters, not needed
521  const std::string &, // sys_name, not needed
522  const std::string &) // unk_name, not needed
523 {
524  // Second derivatives to be returned.
525  Tensor hessu;
526 
527  // x coordinate in space
528  const Real x = p(0);
529 
530  hessu(0,0) = 256.*2.*(1-6.*x+6.*x*x);
531 
532  return hessu;
533 }
534 
535 
536 
538 {
539  // Equals laplacian(laplacian(u)), u'''' in 1D
540  return 256. * 2. * 12.;
541 }
542 
543 
545  const Parameters &, // parameters, not needed
546  const std::string &, // sys_name, not needed
547  const std::string &) // unk_name, not needed
548 {
549  // x and y coordinates in space
550  const Real x = p(0);
551  const Real y = p(1);
552 
553  // analytic solution value
554  return 256.*(x-x*x)*(x-x*x)*(y-y*y)*(y-y*y);
555 }
556 
557 
558 // We now define the gradient of the exact solution
560  const Parameters &, // parameters, not needed
561  const std::string &, // sys_name, not needed
562  const std::string &) // unk_name, not needed
563 {
564  // x and y coordinates in space
565  const Real x = p(0);
566  const Real y = p(1);
567 
568  // First derivatives to be returned.
569  Gradient gradu;
570 
571  gradu(0) = 256.*2.*(x-x*x)*(1-2*x)*(y-y*y)*(y-y*y);
572  gradu(1) = 256.*2.*(x-x*x)*(x-x*x)*(y-y*y)*(1-2*y);
573 
574  return gradu;
575 }
576 
577 
578 // We now define the hessian of the exact solution
580  const Parameters &, // parameters, not needed
581  const std::string &, // sys_name, not needed
582  const std::string &) // unk_name, not needed
583 {
584  // Second derivatives to be returned.
585  Tensor hessu;
586 
587  // x and y coordinates in space
588  const Real x = p(0);
589  const Real y = p(1);
590 
591  hessu(0,0) = 256.*2.*(1-6.*x+6.*x*x)*(y-y*y)*(y-y*y);
592  hessu(0,1) = 256.*4.*(x-x*x)*(1.-2.*x)*(y-y*y)*(1.-2.*y);
593  hessu(1,1) = 256.*2.*(x-x*x)*(x-x*x)*(1.-6.*y+6.*y*y);
594 
595  // Hessians are always symmetric
596  hessu(1,0) = hessu(0,1);
597  return hessu;
598 }
599 
600 
601 
603 {
604  // x and y coordinates in space
605  const Real x = p(0);
606  const Real y = p(1);
607 
608  // Equals laplacian(laplacian(u))
609  return 256. * 8. * (3.*((y-y*y)*(y-y*y)+(x-x*x)*(x-x*x))
610  + (1.-6.*x+6.*x*x)*(1.-6.*y+6.*y*y));
611 }
612 
613 
614 
616  const Parameters &, // parameters, not needed
617  const std::string &, // sys_name, not needed
618  const std::string &) // unk_name, not needed
619 {
620  // xyz coordinates in space
621  const Real x = p(0);
622  const Real y = p(1);
623  const Real z = p(2);
624 
625  // analytic solution value
626  return 4096.*(x-x*x)*(x-x*x)*(y-y*y)*(y-y*y)*(z-z*z)*(z-z*z);
627 }
628 
629 
631  const Parameters &, // parameters, not needed
632  const std::string &, // sys_name, not needed
633  const std::string &) // unk_name, not needed
634 {
635  // First derivatives to be returned.
636  Gradient gradu;
637 
638  // xyz coordinates in space
639  const Real x = p(0);
640  const Real y = p(1);
641  const Real z = p(2);
642 
643  gradu(0) = 4096.*2.*(x-x*x)*(1.-2.*x)*(y-y*y)*(y-y*y)*(z-z*z)*(z-z*z);
644  gradu(1) = 4096.*2.*(x-x*x)*(x-x*x)*(y-y*y)*(1.-2.*y)*(z-z*z)*(z-z*z);
645  gradu(2) = 4096.*2.*(x-x*x)*(x-x*x)*(y-y*y)*(y-y*y)*(z-z*z)*(1.-2.*z);
646 
647  return gradu;
648 }
649 
650 
651 // We now define the hessian of the exact solution
653  const Parameters &, // parameters, not needed
654  const std::string &, // sys_name, not needed
655  const std::string &) // unk_name, not needed
656 {
657  // Second derivatives to be returned.
658  Tensor hessu;
659 
660  // xyz coordinates in space
661  const Real x = p(0);
662  const Real y = p(1);
663  const Real z = p(2);
664 
665  hessu(0,0) = 4096.*(2.-12.*x+12.*x*x)*(y-y*y)*(y-y*y)*(z-z*z)*(z-z*z);
666  hessu(0,1) = 4096.*4.*(x-x*x)*(1.-2.*x)*(y-y*y)*(1.-2.*y)*(z-z*z)*(z-z*z);
667  hessu(0,2) = 4096.*4.*(x-x*x)*(1.-2.*x)*(y-y*y)*(y-y*y)*(z-z*z)*(1.-2.*z);
668  hessu(1,1) = 4096.*(x-x*x)*(x-x*x)*(2.-12.*y+12.*y*y)*(z-z*z)*(z-z*z);
669  hessu(1,2) = 4096.*4.*(x-x*x)*(x-x*x)*(y-y*y)*(1.-2.*y)*(z-z*z)*(1.-2.*z);
670  hessu(2,2) = 4096.*(x-x*x)*(x-x*x)*(y-y*y)*(y-y*y)*(2.-12.*z+12.*z*z);
671 
672  // Hessians are always symmetric
673  hessu(1,0) = hessu(0,1);
674  hessu(2,0) = hessu(0,2);
675  hessu(2,1) = hessu(1,2);
676 
677  return hessu;
678 }
679 
680 
681 
683 {
684  // xyz coordinates in space
685  const Real x = p(0);
686  const Real y = p(1);
687  const Real z = p(2);
688 
689  // Equals laplacian(laplacian(u))
690  return 4096. * 8. * (3.*((y-y*y)*(y-y*y)*(x-x*x)*(x-x*x) +
691  (z-z*z)*(z-z*z)*(x-x*x)*(x-x*x) +
692  (z-z*z)*(z-z*z)*(y-y*y)*(y-y*y)) +
693  (1.-6.*x+6.*x*x)*(1.-6.*y+6.*y*y)*(z-z*z)*(z-z*z) +
694  (1.-6.*x+6.*x*x)*(1.-6.*z+6.*z*z)*(y-y*y)*(y-y*y) +
695  (1.-6.*y+6.*y*y)*(1.-6.*z+6.*z*z)*(x-x*x)*(x-x*x));
696 }
697 
698 
699 
700 // We now define the matrix assembly function for the
701 // Biharmonic system. We need to first compute element
702 // matrices and right-hand sides, and then take into
703 // account the boundary conditions, which will be handled
704 // via a penalty method.
706  const std::string & system_name)
707 {
708  // Ignore unused parameter warnings when libmesh is configured without certain options.
709  libmesh_ignore(es, system_name);
710 
711 #ifdef LIBMESH_ENABLE_AMR
712 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
713 
714  // It is a good idea to make sure we are assembling
715  // the proper system.
716  libmesh_assert_equal_to (system_name, "Biharmonic");
717 
718  // Declare a performance log. Give it a descriptive
719  // string to identify what part of the code we are
720  // logging, since there may be many PerfLogs in an
721  // application.
722  PerfLog perf_log ("Matrix Assembly", false);
723 
724  // Get a constant reference to the mesh object.
725  const MeshBase & mesh = es.get_mesh();
726 
727  // The dimension that we are running
728  const unsigned int dim = mesh.mesh_dimension();
729 
730  // Get a reference to the LinearImplicitSystem we are solving
731  LinearImplicitSystem & system = es.get_system<LinearImplicitSystem>("Biharmonic");
732 
733  // A reference to the DofMap object for this system. The DofMap
734  // object handles the index translation from node and element numbers
735  // to degree of freedom numbers. We will talk more about the DofMap
736  // in future examples.
737  const DofMap & dof_map = system.get_dof_map();
738 
739  // Get a constant reference to the Finite Element type
740  // for the first (and only) variable in the system.
741  FEType fe_type = dof_map.variable_type(0);
742 
743  // Build a Finite Element object of the specified type. Since the
744  // FEBase::build() member dynamically creates memory we will
745  // store the object as a std::unique_ptr<FEBase>. This can be thought
746  // of as a pointer that will clean up after itself.
747  std::unique_ptr<FEBase> fe (FEBase::build(dim, fe_type));
748 
749  // Quadrature rule for numerical integration.
750  // With 2D triangles, the Clough quadrature rule puts a Gaussian
751  // quadrature rule on each of the 3 subelements
752  std::unique_ptr<QBase> qrule(fe_type.default_quadrature_rule(dim));
753 
754  // Tell the finite element object to use our quadrature rule.
755  fe->attach_quadrature_rule (qrule.get());
756 
757  // Declare a special finite element object for
758  // boundary integration.
759  std::unique_ptr<FEBase> fe_face (FEBase::build(dim, fe_type));
760 
761  // Boundary integration requires another quadrature rule,
762  // with dimensionality one less than the dimensionality
763  // of the element.
764  // In 1D, the Clough and Gauss quadrature rules are identical.
765  std::unique_ptr<QBase> qface(fe_type.default_quadrature_rule(dim-1));
766 
767  // Tell the finite element object to use our
768  // quadrature rule.
769  fe_face->attach_quadrature_rule (qface.get());
770 
771  // Here we define some references to cell-specific data that
772  // will be used to assemble the linear system.
773  // We begin with the element Jacobian * quadrature weight at each
774  // integration point.
775  const std::vector<Real> & JxW = fe->get_JxW();
776 
777  // The physical XY locations of the quadrature points on the element.
778  // These might be useful for evaluating spatially varying material
779  // properties at the quadrature points.
780  const std::vector<Point> & q_point = fe->get_xyz();
781 
782  // The element shape functions evaluated at the quadrature points.
783  const std::vector<std::vector<Real>> & phi = fe->get_phi();
784 
785  // The element shape function second derivatives evaluated at the
786  // quadrature points. Note that for the simple biharmonic, shape
787  // function first derivatives are unnecessary.
788  const std::vector<std::vector<RealTensor>> & d2phi = fe->get_d2phi();
789 
790  // For efficiency we will compute shape function laplacians n times,
791  // not n^2
792  std::vector<Real> shape_laplacian;
793 
794  // Define data structures to contain the element matrix
795  // and right-hand-side vector contribution. Following
796  // basic finite element terminology we will denote these
797  // "Ke" and "Fe". More detail is in example 3.
800 
801  // This vector will hold the degree of freedom indices for
802  // the element. These define where in the global system
803  // the element degrees of freedom get mapped.
804  std::vector<dof_id_type> dof_indices;
805 
806  // The global system matrix
807  SparseMatrix<Number> & matrix = system.get_system_matrix();
808 
809  // Now we will loop over all the elements in the mesh. We will
810  // compute the element matrix and right-hand-side contribution. See
811  // example 3 for a discussion of the element iterators.
812  for (const auto & elem : mesh.active_local_element_ptr_range())
813  {
814  // Start logging the shape function initialization.
815  // This is done through a simple function call with
816  // the name of the event to log.
817  perf_log.push("elem init");
818 
819  // Get the degree of freedom indices for the
820  // current element. These define where in the global
821  // matrix and right-hand-side this element will
822  // contribute to.
823  dof_map.dof_indices (elem, dof_indices);
824 
825  // Compute the element-specific data for the current
826  // element. This involves computing the location of the
827  // quadrature points (q_point) and the shape functions
828  // (phi, dphi) for the current element.
829  fe->reinit (elem);
830 
831  const unsigned int n_dofs =
832  cast_int<unsigned int>(dof_indices.size());
833  libmesh_assert_equal_to (n_dofs, phi.size());
834 
835  // Zero the element matrix and right-hand side before
836  // summing them.
837  Ke.resize (n_dofs, n_dofs);
838 
839  Fe.resize (n_dofs);
840 
841  // Make sure there is enough room in this cache
842  shape_laplacian.resize(n_dofs);
843 
844  // Stop logging the shape function initialization.
845  // If you forget to stop logging an event the PerfLog
846  // object will probably catch the error and abort.
847  perf_log.pop("elem init");
848 
849  // Now we will build the element matrix. This involves
850  // a double loop to integrate laplacians of the test functions
851  // (i) against laplacians of the trial functions (j).
852  //
853  // This step is why we need the Clough-Tocher elements -
854  // these C1 differentiable elements have square-integrable
855  // second derivatives.
856  //
857  // Now start logging the element matrix computation
858  perf_log.push ("Ke");
859 
860  for (unsigned int qp=0; qp<qrule->n_points(); qp++)
861  {
862  for (unsigned int i=0; i != n_dofs; i++)
863  {
864  shape_laplacian[i] = d2phi[i][qp](0,0);
865  if (dim > 1)
866  shape_laplacian[i] += d2phi[i][qp](1,1);
867  if (dim == 3)
868  shape_laplacian[i] += d2phi[i][qp](2,2);
869  }
870  for (unsigned int i=0; i != n_dofs; i++)
871  for (unsigned int j=0; j != n_dofs; j++)
872  Ke(i,j) += JxW[qp]*
873  shape_laplacian[i]*shape_laplacian[j];
874  }
875 
876  // Stop logging the matrix computation
877  perf_log.pop ("Ke");
878 
879 
880  // At this point the interior element integration has
881  // been completed. However, we may have not yet addressed
882  // boundary conditions, if we didn't add a DirichletBoundary to
883  // our system earlier. In that case, we will demonstrate the
884  // imposition of simple Dirichlet boundary conditions imposed
885  // via the penalty method. Note that this is a fourth-order
886  // problem: Dirichlet boundary conditions include *both*
887  // boundary values and boundary normal fluxes.
888  if (penalty_dirichlet)
889  {
890  // Start logging the boundary condition computation. We use a
891  // macro to log everything in this scope.
892  LOG_SCOPE_WITH("BCs", "", perf_log);
893 
894  // The penalty values, for solution boundary trace and flux.
895  const Real penalty = 1e10;
896  const Real penalty2 = 1e10;
897 
898  // The following loops over the sides of the element.
899  // If the element has no neighbor on a side then that
900  // side MUST live on a boundary of the domain.
901  for (auto s : elem->side_index_range())
902  if (elem->neighbor_ptr(s) == nullptr)
903  {
904  // The value of the shape functions at the quadrature
905  // points.
906  const std::vector<std::vector<Real>> & phi_face =
907  fe_face->get_phi();
908 
909  // The value of the shape function derivatives at the
910  // quadrature points.
911  const std::vector<std::vector<RealGradient>> & dphi_face =
912  fe_face->get_dphi();
913 
914  // The Jacobian * Quadrature Weight at the quadrature
915  // points on the face.
916  const std::vector<Real> & JxW_face = fe_face->get_JxW();
917 
918  // The XYZ locations (in physical space) of the
919  // quadrature points on the face. This is where
920  // we will interpolate the boundary value function.
921  const std::vector<Point> & qface_point = fe_face->get_xyz();
922  const std::vector<Point> & face_normals = fe_face->get_normals();
923 
924  // Compute the shape function values on the element
925  // face.
926  fe_face->reinit(elem, s);
927 
928  libmesh_assert_equal_to (n_dofs, phi_face.size());
929 
930  // Loop over the face quadrature points for integration.
931  for (unsigned int qp=0; qp<qface->n_points(); qp++)
932  {
933  // The boundary value.
934  Number value = exact_solution(qface_point[qp],
935  es.parameters, "null",
936  "void");
937  Gradient flux = exact_2D_derivative(qface_point[qp],
938  es.parameters,
939  "null", "void");
940 
941  // Matrix contribution of the L2 projection.
942  // Note that the basis function values are
943  // integrated against test function values while
944  // basis fluxes are integrated against test function
945  // fluxes.
946  for (unsigned int i=0; i != n_dofs; i++)
947  for (unsigned int j=0; j != n_dofs; j++)
948  Ke(i,j) += JxW_face[qp] *
949  (penalty * phi_face[i][qp] *
950  phi_face[j][qp] + penalty2
951  * (dphi_face[i][qp] *
952  face_normals[qp]) *
953  (dphi_face[j][qp] *
954  face_normals[qp]));
955 
956  // Right-hand-side contribution of the L2
957  // projection.
958  for (unsigned int i=0; i != n_dofs; i++)
959  Fe(i) += JxW_face[qp] *
960  (penalty * value * phi_face[i][qp]
961  + penalty2 *
962  (flux * face_normals[qp])
963  * (dphi_face[i][qp]
964  * face_normals[qp]));
965 
966  }
967  }
968  }
969 
970  for (unsigned int qp=0; qp<qrule->n_points(); qp++)
971  for (unsigned int i=0; i != n_dofs; i++)
972  Fe(i) += JxW[qp]*phi[i][qp]*forcing_function(q_point[qp]);
973 
974  // The element matrix and right-hand-side are now built
975  // for this element. Add them to the global matrix and
976  // right-hand-side vector. The SparseMatrix::add_matrix()
977  // and NumericVector::add_vector() members do this for us.
978  // Start logging the insertion of the local (element)
979  // matrix and vector into the global matrix and vector
980  LOG_SCOPE_WITH("matrix insertion", "", perf_log);
981 
982  dof_map.constrain_element_matrix_and_vector(Ke, Fe, dof_indices);
983  matrix.add_matrix (Ke, dof_indices);
984  system.rhs->add_vector (Fe, dof_indices);
985  }
986 
987  // That's it. We don't need to do anything else to the
988  // PerfLog. When it goes out of scope (at this function return)
989  // it will print its log to the screen. Pretty easy, huh?
990 
991 #else
992 
993 #endif // #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
994 #endif // #ifdef LIBMESH_ENABLE_AMR
995 }
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition: fe_type.h:196
This class handles the computation of the L2 and/or H1 error for the Systems in the EquationSystems o...
Wrap a libMesh-style function pointer into a FunctionBase object.
void assemble_biharmonic(EquationSystems &es, const std::string &system_name)
This is the EquationSystems class.
void pop(const char *label, const char *header="")
Pop the event label off the stack, resuming any lower event.
Definition: perf_log.C:168
Order
defines an enum for polynomial orders.
Definition: enum_order.h:40
Gradient(* exact_derivative)(const Point &p, const Parameters &, const std::string &, const std::string &)
Real h2_error(std::string_view sys_name, std::string_view unknown_name)
This class provides the ability to map between arbitrary, user-defined strings and several data types...
Definition: parameters.h:67
Number(* exact_solution)(const Point &p, const Parameters &, const std::string &, const std::string &)
static constexpr Real TOLERANCE
unsigned int dim
The ErrorVector is a specialization of the StatisticsVector for error data computed on a finite eleme...
Definition: error_vector.h:50
The ExodusII_IO class implements reading meshes in the ExodusII file format from Sandia National Labs...
Definition: exodusII_io.h:52
Manages consistently variables, degrees of freedom, coefficient vectors, matrices and linear solvers ...
Gradient exact_1D_derivative(const Point &p, const Parameters &, const std::string &, const std::string &)
void resize(const unsigned int n)
Resize the vector.
Definition: dense_vector.h:396
virtual void add_vector(const T *v, const std::vector< numeric_index_type > &dof_indices)
Computes , where v is a pointer and each dof_indices[i] specifies where to add value v[i]...
Gradient exact_2D_derivative(const Point &p, const Parameters &, const std::string &, const std::string &)
Real & coarsen_fraction()
The coarsen_fraction sets either a desired target or a desired maximum number of elements to flag for...
void print_info(std::ostream &os=libMesh::out) const
Prints information about the equation systems, by default to libMesh::out.
MeshBase & mesh
NumericVector< Number > * rhs
The system matrix.
This class allows one to associate Dirichlet boundary values with a given set of mesh boundary ids an...
Tensor exact_1D_hessian(const Point &p, const Parameters &, const std::string &, const std::string &)
virtual Real variance() const override
Definition: error_vector.h:115
Real & refine_fraction()
The refine_fraction sets either a desired target or a desired maximum number of elements to flag for ...
void build_square(UnstructuredMesh &mesh, const unsigned int nx, const unsigned int ny, const Real xmin=0., const Real xmax=1., const Real ymin=0., const Real ymax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
A specialized build_cube() for 2D meshes.
This class defines a vector in LIBMESH_DIM dimensional Real or Complex space.
The LibMeshInit class, when constructed, initializes the dependent libraries (e.g.
Definition: libmesh.h:90
The libMesh namespace provides an interface to certain functionality in the library.
virtual void solve() override
Assembles & solves the linear system A*x=b.
const T_sys & get_system(std::string_view name) const
unsigned int & max_h_level()
The max_h_level is the greatest refinement level an element should reach.
dof_id_type n_dofs() const
Definition: system.C:121
This is the MeshBase class.
Definition: mesh_base.h:75
Gradient exact_grad(const Point &, const Parameters &, const std::string &, const std::string &)
Definition: mysystems.h:25
unsigned int variable_number(std::string_view var) const
Definition: system.C:1609
SolverPackage default_solver_package()
Definition: libmesh.C:1117
The PerfLog class allows monitoring of specific events.
Definition: perf_log.h:145
This class handles the numbering of degrees of freedom on a mesh.
Definition: dof_map.h:179
Number exact_1D_solution(const Point &p, const Parameters &, const std::string &, const std::string &)
std::unique_ptr< QBase > default_quadrature_rule(const unsigned int dim, const int extraorder=0) const
Definition: fe_type.C:34
void all_tri(MeshBase &mesh)
Subdivides any non-simplex elements in a Mesh to produce simplex (triangular in 2D, tetrahedral in 3D) elements.
void libmesh_ignore(const Args &...)
virtual void add_matrix(const DenseMatrix< T > &dm, const std::vector< numeric_index_type > &rows, const std::vector< numeric_index_type > &cols)=0
Add the full matrix dm to the SparseMatrix.
T pow(const T &x)
Definition: utility.h:328
Implements (adaptive) mesh refinement algorithms for a MeshBase.
Tensor exact_3D_hessian(const Point &p, const Parameters &, const std::string &, const std::string &)
unsigned int n_linear_iterations() const
virtual void write_equation_systems(const std::string &fname, const EquationSystems &es, const std::set< std::string > *system_names=nullptr) override
Writes out the solution for no specific time or timestep.
Definition: exodusII_io.C:2033
void print_info(std::ostream &os=libMesh::out, const unsigned int verbosity=0, const bool global=true) const
Prints relevant information about the mesh.
Definition: mesh_base.C:1562
void compute_error(std::string_view sys_name, std::string_view unknown_name)
Computes and stores the error in the solution value e = u-u_h, the gradient grad(e) = grad(u) - grad(...
virtual void reinit()
Handle any mesh changes and reinitialize all the systems on the updated mesh.
NumberVectorValue Gradient
void init(triangulateio &t)
Initializes the fields of t to nullptr/0 as necessary.
static std::unique_ptr< FEGenericBase > build(const unsigned int dim, const FEType &type)
Builds a specific finite element type.
libmesh_assert(ctx)
Tensor(* exact_hessian)(const Point &p, const Parameters &, const std::string &, const std::string &)
unsigned int add_variable(std::string_view var, const FEType &type, const std::set< subdomain_id_type > *const active_subdomains=nullptr)
Adds the variable var to the list of variables for this system.
Definition: system.C:1357
void push(const char *label, const char *header="")
Push the event label onto the stack, pausing any active event.
Definition: perf_log.C:138
Tensor exact_2D_hessian(const Point &p, const Parameters &, const std::string &, const std::string &)
Gradient exact_3D_derivative(const Point &p, const Parameters &, const std::string &, const std::string &)
void attach_assemble_function(void fptr(EquationSystems &es, const std::string &name))
Register a user function to use in assembling the system matrix and RHS.
Definition: system.C:2161
Real l2_error(std::string_view sys_name, std::string_view unknown_name)
Number forcing_function_2D(const Point &p)
Number(* forcing_function)(const Point &p)
void attach_exact_hessian(unsigned int sys_num, FunctionBase< Tensor > *h)
Clone and attach an arbitrary functor which computes the exact second derivatives of the system sys_n...
void flag_elements_by_elem_fraction(const ErrorVector &error_per_cell, const Real refine_fraction=0.3, const Real coarsen_fraction=0.0, const unsigned int max_level=libMesh::invalid_uint)
Flags elements for coarsening and refinement based on the computed error passed in error_per_cell...
bool penalty_dirichlet
void attach_exact_deriv(unsigned int sys_num, FunctionBase< Gradient > *g)
Clone and attach an arbitrary functor which computes the exact gradient of the system sys_num solutio...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
NumberTensorValue Tensor
T & set(const std::string &)
Definition: parameters.h:469
OStreamProxy out
bool refine_and_coarsen_elements()
Refines and coarsens user-requested elements.
const MeshBase & get_mesh() const
static const bool value
Definition: xdr_io.C:54
void resize(const unsigned int new_m, const unsigned int new_n)
Resizes the matrix to the specified size and calls zero().
Definition: dense_matrix.h:895
std::size_t n_active_dofs() const
bool use_unweighted_quadrature_rules
This boolean flag allows you to use "unweighted" quadrature rules (sized to exactly integrate unweigh...
void all_second_order(const bool full_ordered=true)
Calls the range-based version of this function with a range consisting of all elements in the mesh...
Definition: mesh_base.C:1608
Number forcing_function_1D(const Point &p)
unsigned int mesh_dimension() const
Definition: mesh_base.C:372
Parameters parameters
Data structure holding arbitrary parameters.
void build_line(UnstructuredMesh &mesh, const unsigned int nx, const Real xmin=0., const Real xmax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
A specialized build_cube() for 1D meshes.
void add_dirichlet_boundary(const DirichletBoundary &dirichlet_boundary)
Adds a copy of the specified Dirichlet boundary to the system.
int main(int argc, char **argv)
Number exact_2D_solution(const Point &p, const Parameters &, const std::string &, const std::string &)
void attach_exact_value(unsigned int sys_num, FunctionBase< Number > *f)
Clone and attach an arbitrary functor which computes the exact value of the system sys_num solution a...
virtual void estimate_error(const System &system, ErrorVector &error_per_cell, const NumericVector< Number > *solution_vector=nullptr, bool estimate_parent_error=false) override
This function uses the derived class&#39;s jump error estimate formula to estimate the error on each cell...
Number exact_3D_solution(const Point &p, const Parameters &, const std::string &, const std::string &)
Number forcing_function_3D(const Point &p)
virtual void init()
Initialize all the systems.
This class is an error indicator based on laplacian jumps between elements.
virtual System & add_system(std::string_view system_type, std::string_view name)
Add the system of type system_type named name to the systems array.
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition: mesh.h:50
Real h1_error(std::string_view sys_name, std::string_view unknown_name)
const DofMap & get_dof_map() const
Definition: system.h:2374
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
const SparseMatrix< Number > & get_system_matrix() const
void build_cube(UnstructuredMesh &mesh, const unsigned int nx=0, const unsigned int ny=0, const unsigned int nz=0, const Real xmin=0., const Real xmax=1., const Real ymin=0., const Real ymax=1., const Real zmin=0., const Real zmax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
Builds a (elements) cube.
This class defines a tensor in LIBMESH_DIM dimensional Real or Complex space.
uint8_t dof_id_type
Definition: id_types.h:67
virtual Real mean() const override
Definition: error_vector.C:69
void uniformly_refine(unsigned int n=1)
Uniformly refines the mesh n times.