libMesh
Loading...
Searching...
No Matches
adaptivity_ex4.C
Go to the documentation of this file.
1// The libMesh Finite Element Library.
2// Copyright (C) 2002-2026 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
65using 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
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
116 const Parameters &,
117 const std::string &,
118 const std::string &);
119
121 const Parameters &,
122 const std::string &,
123 const std::string &);
124
126 const Parameters &,
127 const std::string &,
128 const std::string &);
129
131
133
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
156int 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 exodus_file = output_file;
236 exodus_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.
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 {
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.
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}
unsigned int dim
Number forcing_function_2D(const Point &p)
Number exact_1D_solution(const Point &p, const Parameters &, const std::string &, const std::string &)
Gradient(* exact_derivative)(const Point &p, const Parameters &, const std::string &, const std::string &)
Number exact_3D_solution(const Point &p, const Parameters &, const std::string &, const std::string &)
Number(* forcing_function)(const Point &p)
Number forcing_function_3D(const Point &p)
bool penalty_dirichlet
Number(* exact_solution)(const Point &p, const Parameters &, const std::string &, const std::string &)
Number exact_2D_solution(const Point &p, const Parameters &, const std::string &, const std::string &)
Tensor exact_3D_hessian(const Point &p, const Parameters &, const std::string &, const std::string &)
void assemble_biharmonic(EquationSystems &es, const std::string &system_name)
Tensor exact_1D_hessian(const Point &p, const Parameters &, const std::string &, const std::string &)
Tensor exact_2D_hessian(const Point &p, const Parameters &, const std::string &, const std::string &)
Gradient exact_2D_derivative(const Point &p, const Parameters &, const std::string &, const std::string &)
Gradient exact_1D_derivative(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 &)
Number forcing_function_1D(const Point &p)
Tensor(* exact_hessian)(const Point &p, const Parameters &, const std::string &, const std::string &)
Defines a dense matrix for use in Finite Element-type computations.
void resize(const unsigned int new_m, const unsigned int new_n)
Resizes the matrix to the specified size and calls zero().
Defines a dense vector for use in Finite Element-type computations.
void resize(const unsigned int n)
Resize the vector.
This class allows one to associate Dirichlet boundary values with a given set of mesh boundary ids an...
This class handles the numbering of degrees of freedom on a mesh.
Definition dof_map.h:181
void add_dirichlet_boundary(const DirichletBoundary &dirichlet_boundary)
Adds a copy of the specified Dirichlet boundary to the system.
This is the EquationSystems class.
virtual void reinit()
Handle any mesh changes and reinitialize all the systems on the updated mesh.
void print_info(std::ostream &os=libMesh::out) const
Prints information about the equation systems, by default to libMesh::out.
std::size_t n_active_dofs() const
const MeshBase & get_mesh() const
Parameters parameters
Data structure holding arbitrary parameters.
virtual void init()
Initialize all the systems.
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.
const T_sys & get_system(std::string_view name) const
The ErrorVector is a specialization of the StatisticsVector for error data computed on a finite eleme...
virtual Real variance() const override
virtual Real mean() const override
This class handles the computation of the L2 and/or H1 error for the Systems in the EquationSystems o...
Real l2_error(std::string_view sys_name, std::string_view unknown_name)
Real h1_error(std::string_view sys_name, std::string_view unknown_name)
Real h2_error(std::string_view sys_name, std::string_view unknown_name)
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...
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...
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 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(...
The ExodusII_IO class implements reading meshes in the ExodusII file format from Sandia National Labs...
Definition exodusII_io.h:53
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.
NumericVector< Number > * rhs
The system matrix.
static std::unique_ptr< FEGenericBase > build(const unsigned int dim, const FEType &type)
Builds a specific finite element type.
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition fe_type.h:197
std::unique_ptr< QBase > default_quadrature_rule(const unsigned int dim, const int extraorder=0) const
Definition fe_type.C:34
const SparseMatrix< Number > & get_system_matrix() const
bool use_unweighted_quadrature_rules
This boolean flag allows you to use "unweighted" quadrature rules (sized to exactly integrate unweigh...
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's jump error estimate formula to estimate the error on each cell...
This class is an error indicator based on laplacian jumps between elements.
The LibMeshInit class, when constructed, initializes the dependent libraries (e.g.
Definition libmesh.h:92
Manages consistently variables, degrees of freedom, coefficient vectors, matrices and linear solvers ...
virtual void solve() override
Assembles & solves the linear system A*x=b.
This is the MeshBase class.
Definition mesh_base.h:81
unsigned int mesh_dimension() const
Definition mesh_base.C:430
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:1803
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:1755
Implements (adaptive) mesh refinement algorithms for a MeshBase.
void uniformly_refine(unsigned int n=1)
Uniformly refines the mesh n times.
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.
unsigned int & max_h_level()
The max_h_level is the greatest refinement level an element should reach.
bool refine_and_coarsen_elements()
Refines and coarsens user-requested elements.
Real & refine_fraction()
The refine_fraction sets either a desired target or a desired maximum number of elements to flag for ...
Real & coarsen_fraction()
The coarsen_fraction sets either a desired target or a desired maximum number of elements to flag for...
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition mesh.h:51
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].
This class provides the ability to map between arbitrary, user-defined strings and several data types...
Definition parameters.h:75
T & set(const std::string &)
Definition parameters.h:494
The PerfLog class allows monitoring of specific events.
Definition perf_log.h:154
void pop(const char *label, const char *header="")
Pop the event label off the stack, resuming any lower event.
Definition perf_log.C:185
void push(const char *label, const char *header="")
Push the event label onto the stack, pausing any active event.
Definition perf_log.C:147
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
Generic sparse matrix.
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.
dof_id_type n_dofs() const
Definition system.C:118
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:1959
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:1344
unsigned int variable_number(std::string_view var) const
Definition system.C:1398
const DofMap & get_dof_map() const
Definition system.h:2417
This class defines a tensor in LIBMESH_DIM dimensional Real or Complex space.
This class defines a vector in LIBMESH_DIM dimensional Real or Complex space.
Wrap a libMesh-style function pointer into a FunctionBase object.
MeshBase & mesh
Gradient exact_grad(const Point &, const Parameters &, const std::string &, const std::string &)
Definition mysystems.h:25
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.
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 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.
void all_tri(MeshBase &mesh)
Subdivides any non-simplex elements in a Mesh to produce simplex (triangular in 2D,...
The libMesh namespace provides an interface to certain functionality in the library.
void libmesh_ignore(const Args &...)
SolverPackage default_solver_package()
Definition libmesh.C:1064
NumberVectorValue Gradient
libmesh_assert(ctx)
OStreamProxy out
NumberTensorValue Tensor
static constexpr Real TOLERANCE
uint8_t dof_id_type
Definition id_types.h:67
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
int main()
static const bool value
Definition xdr_io.C:55