deal.II version GIT relicensing-2173-gae8fc9d14b 2024-11-24 06:40:00+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
Quasi_static_Finite_strain_Compressible_Elasticity.h
Go to the documentation of this file.
1
191 *  
192 *  
193 * @endcode
194 *
195 * We start by including all the necessary deal.II header files and some C++
196 * related ones. They have been discussed in detail in previous tutorial
197 * programs, so you need only refer to past tutorials for details.
198 *
199 * @code
200 *   #include <deal.II/base/function.h>
201 *   #include <deal.II/base/parameter_handler.h>
202 *   #include <deal.II/base/point.h>
203 *   #include <deal.II/base/quadrature_lib.h>
204 *   #include <deal.II/base/symmetric_tensor.h>
205 *   #include <deal.II/base/tensor.h>
206 *   #include <deal.II/base/timer.h>
207 *   #include <deal.II/base/work_stream.h>
208 *   #include <deal.II/base/quadrature_point_data.h>
209 *  
210 *   #include <deal.II/dofs/dof_renumbering.h>
211 *   #include <deal.II/dofs/dof_tools.h>
212 *  
213 *   #include <deal.II/grid/grid_generator.h>
214 *   #include <deal.II/grid/grid_tools.h>
215 *   #include <deal.II/grid/grid_in.h>
216 *   #include <deal.II/grid/tria.h>
217 *  
218 *   #include <deal.II/fe/fe_dgp_monomial.h>
219 *   #include <deal.II/fe/fe_q.h>
220 *   #include <deal.II/fe/fe_system.h>
221 *   #include <deal.II/fe/fe_tools.h>
222 *   #include <deal.II/fe/fe_values.h>
223 *   #include <deal.II/fe/mapping_q_eulerian.h>
224 *   #include <deal.II/fe/mapping_q.h>
225 *  
226 *   #include <deal.II/lac/affine_constraints.h>
227 *   #include <deal.II/lac/block_sparse_matrix.h>
228 *   #include <deal.II/lac/block_vector.h>
229 *   #include <deal.II/lac/dynamic_sparsity_pattern.h>
230 *   #include <deal.II/lac/full_matrix.h>
231 *   #include <deal.II/lac/precondition_selector.h>
232 *   #include <deal.II/lac/solver_cg.h>
233 *   #include <deal.II/lac/sparse_direct.h>
234 *  
235 *   #include <deal.II/numerics/data_out.h>
236 *   #include <deal.II/numerics/vector_tools.h>
237 *  
238 *   #include <deal.II/base/config.h>
239 *   #if DEAL_II_VERSION_MAJOR >= 9 && defined(DEAL_II_WITH_TRILINOS)
240 *   #include <deal.II/differentiation/ad.h>
241 *   #define ENABLE_SACADO_FORMULATION
242 *   #endif
243 *  
244 * @endcode
245 *
246 * These must be included below the AD headers so that
247 * their math functions are available for use in the
248 * definition of tensors and kinematic quantities
249 *
250 * @code
251 *   #include <deal.II/physics/elasticity/kinematics.h>
252 *   #include <deal.II/physics/elasticity/standard_tensors.h>
253 *  
254 *   #include <iostream>
255 *   #include <fstream>
256 *   #include <memory>
257 *  
258 *  
259 * @endcode
260 *
261 * We then stick everything that relates to this tutorial program into a
262 * namespace of its own, and import all the deal.II function and class names
263 * into it:
264 *
265 * @code
266 *   namespace Cook_Membrane
267 *   {
268 *   using namespace dealii;
269 *  
270 * @endcode
271 *
272 *
273 * <a name="cook_membrane.cc-Runtimeparameters"></a>
274 * <h3>Run-time parameters</h3>
275 *
276
277 *
278 * There are several parameters that can be set in the code so we set up a
279 * ParameterHandler object to read in the choices at run-time.
280 *
281 * @code
282 *   namespace Parameters
283 *   {
284 * @endcode
285 *
286 *
287 * <a name="cook_membrane.cc-Assemblymethod"></a>
288 * <h4>Assembly method</h4>
289 *
290
291 *
292 * Here we specify whether automatic differentiation is to be used to assemble
293 * the linear system, and if so then what order of differentiation is to be
294 * employed.
295 *
296 * @code
297 *   struct AssemblyMethod
298 *   {
299 *   unsigned int automatic_differentiation_order;
300 *  
301 *   static void
302 *   declare_parameters(ParameterHandler &prm);
303 *  
304 *   void
305 *   parse_parameters(ParameterHandler &prm);
306 *   };
307 *  
308 *  
309 *   void AssemblyMethod::declare_parameters(ParameterHandler &prm)
310 *   {
311 *   prm.enter_subsection("Assembly method");
312 *   {
313 *   prm.declare_entry("Automatic differentiation order", "0",
314 *   Patterns::Integer(0,2),
315 *   "The automatic differentiation order to be used in the assembly of the linear system.\n"
316 *   "# Order = 0: Both the residual and linearisation are computed manually.\n"
317 *   "# Order = 1: The residual is computed manually but the linearisation is performed using AD.\n"
318 *   "# Order = 2: Both the residual and linearisation are computed using AD.");
319 *   }
320 *   prm.leave_subsection();
321 *   }
322 *  
323 *   void AssemblyMethod::parse_parameters(ParameterHandler &prm)
324 *   {
325 *   prm.enter_subsection("Assembly method");
326 *   {
327 *   automatic_differentiation_order = prm.get_integer("Automatic differentiation order");
328 *   }
329 *   prm.leave_subsection();
330 *   }
331 *  
332 * @endcode
333 *
334 *
335 * <a name="cook_membrane.cc-FiniteElementsystem"></a>
336 * <h4>Finite Element system</h4>
337 *
338
339 *
340 * Here we specify the polynomial order used to approximate the solution.
341 * The quadrature order should be adjusted accordingly.
342 *
343 * @code
344 *   struct FESystem
345 *   {
346 *   unsigned int poly_degree;
347 *   unsigned int quad_order;
348 *  
349 *   static void
350 *   declare_parameters(ParameterHandler &prm);
351 *  
352 *   void
353 *   parse_parameters(ParameterHandler &prm);
354 *   };
355 *  
356 *  
357 *   void FESystem::declare_parameters(ParameterHandler &prm)
358 *   {
359 *   prm.enter_subsection("Finite element system");
360 *   {
361 *   prm.declare_entry("Polynomial degree", "2",
362 *   Patterns::Integer(0),
363 *   "Displacement system polynomial order");
364 *  
365 *   prm.declare_entry("Quadrature order", "3",
366 *   Patterns::Integer(0),
367 *   "Gauss quadrature order");
368 *   }
369 *   prm.leave_subsection();
370 *   }
371 *  
372 *   void FESystem::parse_parameters(ParameterHandler &prm)
373 *   {
374 *   prm.enter_subsection("Finite element system");
375 *   {
376 *   poly_degree = prm.get_integer("Polynomial degree");
377 *   quad_order = prm.get_integer("Quadrature order");
378 *   }
379 *   prm.leave_subsection();
380 *   }
381 *  
382 * @endcode
383 *
384 *
385 * <a name="cook_membrane.cc-Geometry"></a>
386 * <h4>Geometry</h4>
387 *
388
389 *
390 * Make adjustments to the problem geometry and its discretisation.
391 *
392 * @code
393 *   struct Geometry
394 *   {
395 *   unsigned int elements_per_edge;
396 *   double scale;
397 *  
398 *   static void
399 *   declare_parameters(ParameterHandler &prm);
400 *  
401 *   void
402 *   parse_parameters(ParameterHandler &prm);
403 *   };
404 *  
405 *   void Geometry::declare_parameters(ParameterHandler &prm)
406 *   {
407 *   prm.enter_subsection("Geometry");
408 *   {
409 *   prm.declare_entry("Elements per edge", "32",
410 *   Patterns::Integer(0),
411 *   "Number of elements per long edge of the beam");
412 *  
413 *   prm.declare_entry("Grid scale", "1e-3",
414 *   Patterns::Double(0.0),
415 *   "Global grid scaling factor");
416 *   }
417 *   prm.leave_subsection();
418 *   }
419 *  
420 *   void Geometry::parse_parameters(ParameterHandler &prm)
421 *   {
422 *   prm.enter_subsection("Geometry");
423 *   {
424 *   elements_per_edge = prm.get_integer("Elements per edge");
425 *   scale = prm.get_double("Grid scale");
426 *   }
427 *   prm.leave_subsection();
428 *   }
429 *  
430 * @endcode
431 *
432 *
433 * <a name="cook_membrane.cc-Materials"></a>
434 * <h4>Materials</h4>
435 *
436
437 *
438 * We also need the shear modulus @f$ \mu @f$ and Poisson ration @f$ \nu @f$ for the
439 * neo-Hookean material.
440 *
441 * @code
442 *   struct Materials
443 *   {
444 *   double nu;
445 *   double mu;
446 *  
447 *   static void
448 *   declare_parameters(ParameterHandler &prm);
449 *  
450 *   void
451 *   parse_parameters(ParameterHandler &prm);
452 *   };
453 *  
454 *   void Materials::declare_parameters(ParameterHandler &prm)
455 *   {
456 *   prm.enter_subsection("Material properties");
457 *   {
458 *   prm.declare_entry("Poisson's ratio", "0.3",
459 *   Patterns::Double(-1.0,0.5),
460 *   "Poisson's ratio");
461 *  
462 *   prm.declare_entry("Shear modulus", "0.4225e6",
463 *   Patterns::Double(),
464 *   "Shear modulus");
465 *   }
466 *   prm.leave_subsection();
467 *   }
468 *  
469 *   void Materials::parse_parameters(ParameterHandler &prm)
470 *   {
471 *   prm.enter_subsection("Material properties");
472 *   {
473 *   nu = prm.get_double("Poisson's ratio");
474 *   mu = prm.get_double("Shear modulus");
475 *   }
476 *   prm.leave_subsection();
477 *   }
478 *  
479 * @endcode
480 *
481 *
482 * <a name="cook_membrane.cc-Linearsolver"></a>
483 * <h4>Linear solver</h4>
484 *
485
486 *
487 * Next, we choose both solver and preconditioner settings. The use of an
488 * effective preconditioner is critical to ensure convergence when a large
489 * nonlinear motion occurs within a Newton increment.
490 *
491 * @code
492 *   struct LinearSolver
493 *   {
494 *   std::string type_lin;
495 *   double tol_lin;
496 *   double max_iterations_lin;
497 *   std::string preconditioner_type;
498 *   double preconditioner_relaxation;
499 *  
500 *   static void
501 *   declare_parameters(ParameterHandler &prm);
502 *  
503 *   void
504 *   parse_parameters(ParameterHandler &prm);
505 *   };
506 *  
507 *   void LinearSolver::declare_parameters(ParameterHandler &prm)
508 *   {
509 *   prm.enter_subsection("Linear solver");
510 *   {
511 *   prm.declare_entry("Solver type", "CG",
512 *   Patterns::Selection("CG|Direct"),
513 *   "Type of solver used to solve the linear system");
514 *  
515 *   prm.declare_entry("Residual", "1e-6",
516 *   Patterns::Double(0.0),
517 *   "Linear solver residual (scaled by residual norm)");
518 *  
519 *   prm.declare_entry("Max iteration multiplier", "1",
520 *   Patterns::Double(0.0),
521 *   "Linear solver iterations (multiples of the system matrix size)");
522 *  
523 *   prm.declare_entry("Preconditioner type", "ssor",
524 *   Patterns::Selection("jacobi|ssor"),
525 *   "Type of preconditioner");
526 *  
527 *   prm.declare_entry("Preconditioner relaxation", "0.65",
528 *   Patterns::Double(0.0),
529 *   "Preconditioner relaxation value");
530 *   }
531 *   prm.leave_subsection();
532 *   }
533 *  
534 *   void LinearSolver::parse_parameters(ParameterHandler &prm)
535 *   {
536 *   prm.enter_subsection("Linear solver");
537 *   {
538 *   type_lin = prm.get("Solver type");
539 *   tol_lin = prm.get_double("Residual");
540 *   max_iterations_lin = prm.get_double("Max iteration multiplier");
541 *   preconditioner_type = prm.get("Preconditioner type");
542 *   preconditioner_relaxation = prm.get_double("Preconditioner relaxation");
543 *   }
544 *   prm.leave_subsection();
545 *   }
546 *  
547 * @endcode
548 *
549 *
550 * <a name="cook_membrane.cc-Nonlinearsolver"></a>
551 * <h4>Nonlinear solver</h4>
552 *
553
554 *
555 * A Newton-Raphson scheme is used to solve the nonlinear system of governing
556 * equations. We now define the tolerances and the maximum number of
557 * iterations for the Newton-Raphson nonlinear solver.
558 *
559 * @code
560 *   struct NonlinearSolver
561 *   {
562 *   unsigned int max_iterations_NR;
563 *   double tol_f;
564 *   double tol_u;
565 *  
566 *   static void
567 *   declare_parameters(ParameterHandler &prm);
568 *  
569 *   void
570 *   parse_parameters(ParameterHandler &prm);
571 *   };
572 *  
573 *   void NonlinearSolver::declare_parameters(ParameterHandler &prm)
574 *   {
575 *   prm.enter_subsection("Nonlinear solver");
576 *   {
577 *   prm.declare_entry("Max iterations Newton-Raphson", "10",
578 *   Patterns::Integer(0),
579 *   "Number of Newton-Raphson iterations allowed");
580 *  
581 *   prm.declare_entry("Tolerance force", "1.0e-9",
582 *   Patterns::Double(0.0),
583 *   "Force residual tolerance");
584 *  
585 *   prm.declare_entry("Tolerance displacement", "1.0e-6",
586 *   Patterns::Double(0.0),
587 *   "Displacement error tolerance");
588 *   }
589 *   prm.leave_subsection();
590 *   }
591 *  
592 *   void NonlinearSolver::parse_parameters(ParameterHandler &prm)
593 *   {
594 *   prm.enter_subsection("Nonlinear solver");
595 *   {
596 *   max_iterations_NR = prm.get_integer("Max iterations Newton-Raphson");
597 *   tol_f = prm.get_double("Tolerance force");
598 *   tol_u = prm.get_double("Tolerance displacement");
599 *   }
600 *   prm.leave_subsection();
601 *   }
602 *  
603 * @endcode
604 *
605 *
606 * <a name="cook_membrane.cc-Time"></a>
607 * <h4>Time</h4>
608 *
609
610 *
611 * Set the timestep size @f$ \varDelta t @f$ and the simulation end-time.
612 *
613 * @code
614 *   struct Time
615 *   {
616 *   double delta_t;
617 *   double end_time;
618 *  
619 *   static void
620 *   declare_parameters(ParameterHandler &prm);
621 *  
622 *   void
623 *   parse_parameters(ParameterHandler &prm);
624 *   };
625 *  
626 *   void Time::declare_parameters(ParameterHandler &prm)
627 *   {
628 *   prm.enter_subsection("Time");
629 *   {
630 *   prm.declare_entry("End time", "1",
631 *   Patterns::Double(),
632 *   "End time");
633 *  
634 *   prm.declare_entry("Time step size", "0.1",
635 *   Patterns::Double(),
636 *   "Time step size");
637 *   }
638 *   prm.leave_subsection();
639 *   }
640 *  
641 *   void Time::parse_parameters(ParameterHandler &prm)
642 *   {
643 *   prm.enter_subsection("Time");
644 *   {
645 *   end_time = prm.get_double("End time");
646 *   delta_t = prm.get_double("Time step size");
647 *   }
648 *   prm.leave_subsection();
649 *   }
650 *  
651 * @endcode
652 *
653 *
654 * <a name="cook_membrane.cc-Allparameters"></a>
655 * <h4>All parameters</h4>
656 *
657
658 *
659 * Finally we consolidate all of the above structures into a single container
660 * that holds all of our run-time selections.
661 *
662 * @code
663 *   struct AllParameters :
664 *   public AssemblyMethod,
665 *   public FESystem,
666 *   public Geometry,
667 *   public Materials,
668 *   public LinearSolver,
669 *   public NonlinearSolver,
670 *   public Time
671 *  
672 *   {
673 *   AllParameters(const std::string &input_file);
674 *  
675 *   static void
676 *   declare_parameters(ParameterHandler &prm);
677 *  
678 *   void
679 *   parse_parameters(ParameterHandler &prm);
680 *   };
681 *  
682 *   AllParameters::AllParameters(const std::string &input_file)
683 *   {
684 *   ParameterHandler prm;
685 *   declare_parameters(prm);
686 *   prm.parse_input(input_file);
687 *   parse_parameters(prm);
688 *   }
689 *  
690 *   void AllParameters::declare_parameters(ParameterHandler &prm)
691 *   {
692 *   AssemblyMethod::declare_parameters(prm);
693 *   FESystem::declare_parameters(prm);
694 *   Geometry::declare_parameters(prm);
695 *   Materials::declare_parameters(prm);
696 *   LinearSolver::declare_parameters(prm);
697 *   NonlinearSolver::declare_parameters(prm);
698 *   Time::declare_parameters(prm);
699 *   }
700 *  
701 *   void AllParameters::parse_parameters(ParameterHandler &prm)
702 *   {
703 *   AssemblyMethod::parse_parameters(prm);
704 *   FESystem::parse_parameters(prm);
705 *   Geometry::parse_parameters(prm);
706 *   Materials::parse_parameters(prm);
707 *   LinearSolver::parse_parameters(prm);
708 *   NonlinearSolver::parse_parameters(prm);
709 *   Time::parse_parameters(prm);
710 *   }
711 *   }
712 *  
713 *  
714 * @endcode
715 *
716 *
717 * <a name="cook_membrane.cc-Timeclass"></a>
718 * <h3>Time class</h3>
719 *
720
721 *
722 * A simple class to store time data. Its functioning is transparent so no
723 * discussion is necessary. For simplicity we assume a constant time step
724 * size.
725 *
726 * @code
727 *   class Time
728 *   {
729 *   public:
730 *   Time (const double time_end,
731 *   const double delta_t)
732 *   :
733 *   timestep(0),
734 *   time_current(0.0),
735 *   time_end(time_end),
736 *   delta_t(delta_t)
737 *   {}
738 *  
739 *   virtual ~Time()
740 *   {}
741 *  
742 *   double current() const
743 *   {
744 *   return time_current;
745 *   }
746 *   double end() const
747 *   {
748 *   return time_end;
749 *   }
750 *   double get_delta_t() const
751 *   {
752 *   return delta_t;
753 *   }
754 *   unsigned int get_timestep() const
755 *   {
756 *   return timestep;
757 *   }
758 *   void increment()
759 *   {
760 *   time_current += delta_t;
761 *   ++timestep;
762 *   }
763 *  
764 *   private:
765 *   unsigned int timestep;
766 *   double time_current;
767 *   const double time_end;
768 *   const double delta_t;
769 *   };
770 *  
771 * @endcode
772 *
773 *
774 * <a name="cook_membrane.cc-CompressibleneoHookeanmaterialwithinaonefieldformulation"></a>
775 * <h3>Compressible neo-Hookean material within a one-field formulation</h3>
776 *
777
778 *
779 * As discussed in the literature and @ref step_44 "step-44", Neo-Hookean materials are a type
780 * of hyperelastic materials. The entire domain is assumed to be composed of a
781 * compressible neo-Hookean material. This class defines the behaviour of
782 * this material within a one-field formulation. Compressible neo-Hookean
783 * materials can be described by a strain-energy function (SEF) @f$ \Psi =
784 * \Psi_{\text{iso}}(\overline{\mathbf{b}}) + \Psi_{\text{vol}}(J)
785 * @f$.
786 *
787
788 *
789 * The isochoric response is given by @f$
790 * \Psi_{\text{iso}}(\overline{\mathbf{b}}) = c_{1} [\overline{I}_{1} - 3] @f$
791 * where @f$ c_{1} = \frac{\mu}{2} @f$ and @f$\overline{I}_{1}@f$ is the first
792 * invariant of the left- or right-isochoric Cauchy-Green deformation tensors.
793 * That is @f$\overline{I}_1 :=\textrm{tr}(\overline{\mathbf{b}})@f$. In this
794 * example the SEF that governs the volumetric response is defined as @f$
795 * \Psi_{\text{vol}}(J) = \kappa \frac{1}{4} [ J^2 - 1
796 * - 2\textrm{ln}\; J ]@f$, where @f$\kappa:= \lambda + 2/3 \mu@f$ is
797 * the <a href="http://en.wikipedia.org/wiki/Bulk_modulus">bulk modulus</a>
798 * and @f$\lambda@f$ is <a
799 * href="http://en.wikipedia.org/wiki/Lam%C3%A9_parameters">Lame's first
800 * parameter</a>.
801 *
802
803 *
804 * The following class will be used to characterize the material we work with,
805 * and provides a central point that one would need to modify if one were to
806 * implement a different material model. For it to work, we will store one
807 * object of this type per quadrature point, and in each of these objects
808 * store the current state (characterized by the values or measures of the
809 * displacement field) so that we can compute the elastic coefficients
810 * linearized around the current state.
811 *
812 * @code
813 *   template <int dim,typename NumberType>
814 *   class Material_Compressible_Neo_Hook_One_Field
815 *   {
816 *   public:
817 *   Material_Compressible_Neo_Hook_One_Field(const double mu,
818 *   const double nu)
819 *   :
820 *   kappa((2.0 * mu * (1.0 + nu)) / (3.0 * (1.0 - 2.0 * nu))),
821 *   c_1(mu / 2.0)
822 *   {
823 *   Assert(kappa > 0, ExcInternalError());
824 *   }
825 *  
826 *   ~Material_Compressible_Neo_Hook_One_Field()
827 *   {}
828 *  
829 * @endcode
830 *
831 * The first function is the total energy
832 * @f$\Psi = \Psi_{\textrm{iso}} + \Psi_{\textrm{vol}}@f$.
833 *
834 * @code
835 *   NumberType
836 *   get_Psi(const NumberType &det_F,
837 *   const SymmetricTensor<2,dim,NumberType> &b_bar) const
838 *   {
839 *   return get_Psi_vol(det_F) + get_Psi_iso(b_bar);
840 *   }
841 *  
842 * @endcode
843 *
844 * The second function determines the Kirchhoff stress @f$\boldsymbol{\tau}
845 * = \boldsymbol{\tau}_{\textrm{iso}} + \boldsymbol{\tau}_{\textrm{vol}}@f$
846 *
847 * @code
848 *   SymmetricTensor<2,dim,NumberType>
849 *   get_tau(const NumberType &det_F,
850 *   const SymmetricTensor<2,dim,NumberType> &b_bar)
851 *   {
852 * @endcode
853 *
854 * See Holzapfel p231 eq6.98 onwards
855 *
856 * @code
857 *   return get_tau_vol(det_F) + get_tau_iso(b_bar);
858 *   }
859 *  
860 * @endcode
861 *
862 * The fourth-order elasticity tensor in the spatial setting
863 * @f$\mathfrak{c}@f$ is calculated from the SEF @f$\Psi@f$ as @f$ J
864 * \mathfrak{c}_{ijkl} = F_{iA} F_{jB} \mathfrak{C}_{ABCD} F_{kC} F_{lD}@f$
865 * where @f$ \mathfrak{C} = 4 \frac{\partial^2 \Psi(\mathbf{C})}{\partial
866 * \mathbf{C} \partial \mathbf{C}}@f$
867 *
868 * @code
869 *   SymmetricTensor<4,dim,NumberType>
870 *   get_Jc(const NumberType &det_F,
871 *   const SymmetricTensor<2,dim,NumberType> &b_bar) const
872 *   {
873 *   return get_Jc_vol(det_F) + get_Jc_iso(b_bar);
874 *   }
875 *  
876 *   private:
877 * @endcode
878 *
879 * Define constitutive model parameters @f$\kappa@f$ (bulk modulus) and the
880 * neo-Hookean model parameter @f$c_1@f$:
881 *
882 * @code
883 *   const double kappa;
884 *   const double c_1;
885 *  
886 * @endcode
887 *
888 * Value of the volumetric free energy
889 *
890 * @code
891 *   NumberType
892 *   get_Psi_vol(const NumberType &det_F) const
893 *   {
894 *   return (kappa / 4.0) * (det_F*det_F - 1.0 - 2.0*std::log(det_F));
895 *   }
896 *  
897 * @endcode
898 *
899 * Value of the isochoric free energy
900 *
901 * @code
902 *   NumberType
903 *   get_Psi_iso(const SymmetricTensor<2,dim,NumberType> &b_bar) const
904 *   {
905 *   return c_1 * (trace(b_bar) - dim);
906 *   }
907 *  
908 * @endcode
909 *
910 * Derivative of the volumetric free energy with respect to
911 * @f$J@f$ return @f$\frac{\partial
912 * \Psi_{\text{vol}}(J)}{\partial J}@f$
913 *
914 * @code
915 *   NumberType
916 *   get_dPsi_vol_dJ(const NumberType &det_F) const
917 *   {
918 *   return (kappa / 2.0) * (det_F - 1.0 / det_F);
919 *   }
920 *  
921 * @endcode
922 *
923 * The following functions are used internally in determining the result
924 * of some of the public functions above. The first one determines the
925 * volumetric Kirchhoff stress @f$\boldsymbol{\tau}_{\textrm{vol}}@f$.
926 * Note the difference in its definition when compared to @ref step_44 "step-44".
927 *
928 * @code
929 *   SymmetricTensor<2,dim,NumberType>
930 *   get_tau_vol(const NumberType &det_F) const
931 *   {
932 *   return NumberType(get_dPsi_vol_dJ(det_F) * det_F) * Physics::Elasticity::StandardTensors<dim>::I;
933 *   }
934 *  
935 * @endcode
936 *
937 * Next, determine the isochoric Kirchhoff stress
938 * @f$\boldsymbol{\tau}_{\textrm{iso}} =
939 * \mathcal{P}:\overline{\boldsymbol{\tau}}@f$:
940 *
941 * @code
942 *   SymmetricTensor<2,dim,NumberType>
943 *   get_tau_iso(const SymmetricTensor<2,dim,NumberType> &b_bar) const
944 *   {
945 *   return Physics::Elasticity::StandardTensors<dim>::dev_P * get_tau_bar(b_bar);
946 *   }
947 *  
948 * @endcode
949 *
950 * Then, determine the fictitious Kirchhoff stress
951 * @f$\overline{\boldsymbol{\tau}}@f$:
952 *
953 * @code
954 *   SymmetricTensor<2,dim,NumberType>
955 *   get_tau_bar(const SymmetricTensor<2,dim,NumberType> &b_bar) const
956 *   {
957 *   return 2.0 * c_1 * b_bar;
958 *   }
959 *  
960 * @endcode
961 *
962 * Second derivative of the volumetric free energy wrt @f$J@f$. We
963 * need the following computation explicitly in the tangent so we make it
964 * public. We calculate @f$\frac{\partial^2
965 * \Psi_{\textrm{vol}}(J)}{\partial J \partial
966 * J}@f$
967 *
968 * @code
969 *   NumberType
970 *   get_d2Psi_vol_dJ2(const NumberType &det_F) const
971 *   {
972 *   return ( (kappa / 2.0) * (1.0 + 1.0 / (det_F * det_F)));
973 *   }
974 *  
975 * @endcode
976 *
977 * Calculate the volumetric part of the tangent @f$J
978 * \mathfrak{c}_\textrm{vol}@f$. Again, note the difference in its
979 * definition when compared to @ref step_44 "step-44". The extra terms result from two
980 * quantities in @f$\boldsymbol{\tau}_{\textrm{vol}}@f$ being dependent on
981 * @f$\boldsymbol{F}@f$.
982 *
983 * @code
984 *   SymmetricTensor<4,dim,NumberType>
985 *   get_Jc_vol(const NumberType &det_F) const
986 *   {
987 * @endcode
988 *
989 * See Holzapfel p265
990 *
991 * @code
992 *   return det_F
993 *   * ( (get_dPsi_vol_dJ(det_F) + det_F * get_d2Psi_vol_dJ2(det_F))*Physics::Elasticity::StandardTensors<dim>::IxI
994 *   - (2.0 * get_dPsi_vol_dJ(det_F))*Physics::Elasticity::StandardTensors<dim>::S );
995 *   }
996 *  
997 * @endcode
998 *
999 * Calculate the isochoric part of the tangent @f$J
1000 * \mathfrak{c}_\textrm{iso}@f$:
1001 *
1002 * @code
1003 *   SymmetricTensor<4,dim,NumberType>
1004 *   get_Jc_iso(const SymmetricTensor<2,dim,NumberType> &b_bar) const
1005 *   {
1006 *   const SymmetricTensor<2, dim> tau_bar = get_tau_bar(b_bar);
1007 *   const SymmetricTensor<2, dim> tau_iso = get_tau_iso(b_bar);
1008 *   const SymmetricTensor<4, dim> tau_iso_x_I
1009 *   = outer_product(tau_iso,
1010 *   Physics::Elasticity::StandardTensors<dim>::I);
1011 *   const SymmetricTensor<4, dim> I_x_tau_iso
1012 *   = outer_product(Physics::Elasticity::StandardTensors<dim>::I,
1013 *   tau_iso);
1014 *   const SymmetricTensor<4, dim> c_bar = get_c_bar();
1015 *  
1016 *   return (2.0 / dim) * trace(tau_bar)
1017 *   * Physics::Elasticity::StandardTensors<dim>::dev_P
1018 *   - (2.0 / dim) * (tau_iso_x_I + I_x_tau_iso)
1019 *   + Physics::Elasticity::StandardTensors<dim>::dev_P * c_bar
1020 *   * Physics::Elasticity::StandardTensors<dim>::dev_P;
1021 *   }
1022 *  
1023 * @endcode
1024 *
1025 * Calculate the fictitious elasticity tensor @f$\overline{\mathfrak{c}}@f$.
1026 * For the material model chosen this is simply zero:
1027 *
1028 * @code
1029 *   SymmetricTensor<4,dim,double>
1030 *   get_c_bar() const
1031 *   {
1032 *   return SymmetricTensor<4, dim>();
1033 *   }
1034 *   };
1035 *  
1036 * @endcode
1037 *
1038 *
1039 * <a name="cook_membrane.cc-Quadraturepointhistory"></a>
1040 * <h3>Quadrature point history</h3>
1041 *
1042
1043 *
1044 * As seen in @ref step_18 "step-18", the <code> PointHistory </code> class offers a method
1045 * for storing data at the quadrature points. Here each quadrature point
1046 * holds a pointer to a material description. Thus, different material models
1047 * can be used in different regions of the domain. Among other data, we
1048 * choose to store the Kirchhoff stress @f$\boldsymbol{\tau}@f$ and the tangent
1049 * @f$J\mathfrak{c}@f$ for the quadrature points.
1050 *
1051 * @code
1052 *   template <int dim,typename NumberType>
1053 *   class PointHistory
1054 *   {
1055 *   public:
1056 *   PointHistory()
1057 *   {}
1058 *  
1059 *   virtual ~PointHistory()
1060 *   {}
1061 *  
1062 * @endcode
1063 *
1064 * The first function is used to create a material object and to
1065 * initialize all tensors correctly: The second one updates the stored
1066 * values and stresses based on the current deformation measure
1067 * @f$\textrm{Grad}\mathbf{u}_{\textrm{n}}@f$.
1068 *
1069 * @code
1070 *   void setup_lqp (const Parameters::AllParameters &parameters)
1071 *   {
1072 *   material.reset(new Material_Compressible_Neo_Hook_One_Field<dim,NumberType>(parameters.mu,
1073 *   parameters.nu));
1074 *   }
1075 *  
1076 * @endcode
1077 *
1078 * We offer an interface to retrieve certain data.
1079 * This is the strain energy:
1080 *
1081 * @code
1082 *   NumberType
1083 *   get_Psi(const NumberType &det_F,
1084 *   const SymmetricTensor<2,dim,NumberType> &b_bar) const
1085 *   {
1086 *   return material->get_Psi(det_F,b_bar);
1087 *   }
1088 *  
1089 * @endcode
1090 *
1091 * Here are the kinetic variables. These are used in the material and
1092 * global tangent matrix and residual assembly operations:
1093 * First is the Kirchhoff stress:
1094 *
1095 * @code
1096 *   SymmetricTensor<2,dim,NumberType>
1097 *   get_tau(const NumberType &det_F,
1098 *   const SymmetricTensor<2,dim,NumberType> &b_bar) const
1099 *   {
1100 *   return material->get_tau(det_F,b_bar);
1101 *   }
1102 *  
1103 * @endcode
1104 *
1105 * And the tangent:
1106 *
1107 * @code
1108 *   SymmetricTensor<4,dim,NumberType>
1109 *   get_Jc(const NumberType &det_F,
1110 *   const SymmetricTensor<2,dim,NumberType> &b_bar) const
1111 *   {
1112 *   return material->get_Jc(det_F,b_bar);
1113 *   }
1114 *  
1115 * @endcode
1116 *
1117 * In terms of member functions, this class stores for the quadrature
1118 * point it represents a copy of a material type in case different
1119 * materials are used in different regions of the domain, as well as the
1120 * inverse of the deformation gradient...
1121 *
1122 * @code
1123 *   private:
1124 *   std::shared_ptr< Material_Compressible_Neo_Hook_One_Field<dim,NumberType> > material;
1125 *   };
1126 *  
1127 *  
1128 * @endcode
1129 *
1130 *
1131 * <a name="cook_membrane.cc-Quasistaticcompressiblefinitestrainsolid"></a>
1132 * <h3>Quasi-static compressible finite-strain solid</h3>
1133 *
1134
1135 *
1136 * Forward declarations for classes that will
1137 * perform assembly of the linear system.
1138 *
1139 * @code
1140 *   template <int dim,typename NumberType>
1141 *   struct Assembler_Base;
1142 *   template <int dim,typename NumberType>
1143 *   struct Assembler;
1144 *  
1145 * @endcode
1146 *
1147 * The Solid class is the central class in that it represents the problem at
1148 * hand. It follows the usual scheme in that all it really has is a
1149 * constructor, destructor and a <code>run()</code> function that dispatches
1150 * all the work to private functions of this class:
1151 *
1152 * @code
1153 *   template <int dim,typename NumberType>
1154 *   class Solid
1155 *   {
1156 *   public:
1157 *   Solid(const Parameters::AllParameters &parameters);
1158 *  
1159 *   virtual
1160 *   ~Solid();
1161 *  
1162 *   void
1163 *   run();
1164 *  
1165 *   private:
1166 *  
1167 * @endcode
1168 *
1169 * We start the collection of member functions with one that builds the
1170 * grid:
1171 *
1172 * @code
1173 *   void
1174 *   make_grid();
1175 *  
1176 * @endcode
1177 *
1178 * Set up the finite element system to be solved:
1179 *
1180 * @code
1181 *   void
1182 *   system_setup();
1183 *  
1184 * @endcode
1185 *
1186 * Several functions to assemble the system and right hand side matrices
1187 * using multithreading. Each of them comes as a wrapper function, one
1188 * that is executed to do the work in the WorkStream model on one cell,
1189 * and one that copies the work done on this one cell into the global
1190 * object that represents it:
1191 *
1192 * @code
1193 *   void
1194 *   assemble_system(const BlockVector<double> &solution_delta);
1195 *  
1196 * @endcode
1197 *
1198 * We use a separate data structure to perform the assembly. It needs access
1199 * to some low-level data, so we simply befriend the class instead of
1200 * creating a complex interface to provide access as necessary.
1201 *
1202 * @code
1203 *   friend struct Assembler_Base<dim,NumberType>;
1204 *   friend struct Assembler<dim,NumberType>;
1205 *  
1206 * @endcode
1207 *
1208 * Apply Dirichlet boundary conditions on the displacement field
1209 *
1210 * @code
1211 *   void
1212 *   make_constraints(const int &it_nr);
1213 *  
1214 * @endcode
1215 *
1216 * Create and update the quadrature points. Here, no data needs to be
1217 * copied into a global object, so the copy_local_to_global function is
1218 * empty:
1219 *
1220 * @code
1221 *   void
1222 *   setup_qph();
1223 *  
1224 * @endcode
1225 *
1226 * Solve for the displacement using a Newton-Raphson method. We break this
1227 * function into the nonlinear loop and the function that solves the
1228 * linearized Newton-Raphson step:
1229 *
1230 * @code
1231 *   void
1232 *   solve_nonlinear_timestep(BlockVector<double> &solution_delta);
1233 *  
1234 *   std::pair<unsigned int, double>
1235 *   solve_linear_system(BlockVector<double> &newton_update);
1236 *  
1237 * @endcode
1238 *
1239 * Solution retrieval as well as post-processing and writing data to file :
1240 *
1241 * @code
1242 *   BlockVector<double>
1243 *   get_total_solution(const BlockVector<double> &solution_delta) const;
1244 *  
1245 *   void
1246 *   output_results() const;
1247 *  
1248 * @endcode
1249 *
1250 * Finally, some member variables that describe the current state: A
1251 * collection of the parameters used to describe the problem setup...
1252 *
1253 * @code
1254 *   const Parameters::AllParameters &parameters;
1255 *  
1256 * @endcode
1257 *
1258 * ...the volume of the reference and current configurations...
1259 *
1260 * @code
1261 *   double vol_reference;
1262 *   double vol_current;
1263 *  
1264 * @endcode
1265 *
1266 * ...and description of the geometry on which the problem is solved:
1267 *
1268 * @code
1269 *   Triangulation<dim> triangulation;
1270 *  
1271 * @endcode
1272 *
1273 * Also, keep track of the current time and the time spent evaluating
1274 * certain functions
1275 *
1276 * @code
1277 *   Time time;
1278 *   TimerOutput timer;
1279 *  
1280 * @endcode
1281 *
1282 * A storage object for quadrature point information. As opposed to
1283 * @ref step_18 "step-18", deal.II's native quadrature point data manager is employed here.
1284 *
1285 * @code
1287 *   PointHistory<dim,NumberType> > quadrature_point_history;
1288 *  
1289 * @endcode
1290 *
1291 * A description of the finite-element system including the displacement
1292 * polynomial degree, the degree-of-freedom handler, number of DoFs per
1293 * cell and the extractor objects used to retrieve information from the
1294 * solution vectors:
1295 *
1296 * @code
1297 *   const unsigned int degree;
1298 *   const FESystem<dim> fe;
1299 *   DoFHandler<dim> dof_handler_ref;
1300 *   const unsigned int dofs_per_cell;
1301 *   const FEValuesExtractors::Vector u_fe;
1302 *  
1303 * @endcode
1304 *
1305 * Description of how the block-system is arranged. There is just 1 block,
1306 * that contains a vector DOF @f$\mathbf{u}@f$.
1307 * There are two reasons that we retain the block system in this problem.
1308 * The first is pure laziness to perform further modifications to the
1309 * code from which this work originated. The second is that a block system
1310 * would typically necessary when extending this code to multiphysics
1311 * problems.
1312 *
1313 * @code
1314 *   static const unsigned int n_blocks = 1;
1315 *   static const unsigned int n_components = dim;
1316 *   static const unsigned int first_u_component = 0;
1317 *  
1318 *   enum
1319 *   {
1320 *   u_dof = 0
1321 *   };
1322 *  
1323 *   std::vector<types::global_dof_index> dofs_per_block;
1324 *  
1325 * @endcode
1326 *
1327 * Rules for Gauss-quadrature on both the cell and faces. The number of
1328 * quadrature points on both cells and faces is recorded.
1329 *
1330 * @code
1331 *   const QGauss<dim> qf_cell;
1332 *   const QGauss<dim - 1> qf_face;
1333 *   const unsigned int n_q_points;
1334 *   const unsigned int n_q_points_f;
1335 *  
1336 * @endcode
1337 *
1338 * Objects that store the converged solution and right-hand side vectors,
1339 * as well as the tangent matrix. There is a AffineConstraints object used
1340 * to keep track of constraints. We make use of a sparsity pattern
1341 * designed for a block system.
1342 *
1343 * @code
1344 *   AffineConstraints<double> constraints;
1345 *   BlockSparsityPattern sparsity_pattern;
1346 *   BlockSparseMatrix<double> tangent_matrix;
1347 *   BlockVector<double> system_rhs;
1348 *   BlockVector<double> solution_n;
1349 *  
1350 * @endcode
1351 *
1352 * Then define a number of variables to store norms and update norms and
1353 * normalisation factors.
1354 *
1355 * @code
1356 *   struct Errors
1357 *   {
1358 *   Errors()
1359 *   :
1360 *   norm(1.0), u(1.0)
1361 *   {}
1362 *  
1363 *   void reset()
1364 *   {
1365 *   norm = 1.0;
1366 *   u = 1.0;
1367 *   }
1368 *   void normalise(const Errors &rhs)
1369 *   {
1370 *   if (rhs.norm != 0.0)
1371 *   norm /= rhs.norm;
1372 *   if (rhs.u != 0.0)
1373 *   u /= rhs.u;
1374 *   }
1375 *  
1376 *   double norm, u;
1377 *   };
1378 *  
1379 *   Errors error_residual, error_residual_0, error_residual_norm, error_update,
1380 *   error_update_0, error_update_norm;
1381 *  
1382 * @endcode
1383 *
1384 * Methods to calculate error measures
1385 *
1386 * @code
1387 *   void
1388 *   get_error_residual(Errors &error_residual);
1389 *  
1390 *   void
1391 *   get_error_update(const BlockVector<double> &newton_update,
1392 *   Errors &error_update);
1393 *  
1394 * @endcode
1395 *
1396 * Print information to screen in a pleasing way...
1397 *
1398 * @code
1399 *   static
1400 *   void
1401 *   print_conv_header();
1402 *  
1403 *   void
1404 *   print_conv_footer();
1405 *  
1406 *   void
1407 *   print_vertical_tip_displacement();
1408 *   };
1409 *  
1410 * @endcode
1411 *
1412 *
1413 * <a name="cook_membrane.cc-ImplementationofthecodeSolidcodeclass"></a>
1414 * <h3>Implementation of the <code>Solid</code> class</h3>
1415 *
1416
1417 *
1418 *
1419 * <a name="cook_membrane.cc-Publicinterface"></a>
1420 * <h4>Public interface</h4>
1421 *
1422
1423 *
1424 * We initialise the Solid class using data extracted from the parameter file.
1425 *
1426 * @code
1427 *   template <int dim,typename NumberType>
1428 *   Solid<dim,NumberType>::Solid(const Parameters::AllParameters &parameters)
1429 *   :
1430 *   parameters(parameters),
1431 *   vol_reference (0.0),
1432 *   vol_current (0.0),
1434 *   time(parameters.end_time, parameters.delta_t),
1435 *   timer(std::cout,
1438 *   degree(parameters.poly_degree),
1439 * @endcode
1440 *
1441 * The Finite Element System is composed of dim continuous displacement
1442 * DOFs.
1443 *
1444 * @code
1445 *   fe(FE_Q<dim>(parameters.poly_degree), dim), // displacement
1446 *   dof_handler_ref(triangulation),
1447 *   dofs_per_cell (fe.dofs_per_cell),
1448 *   u_fe(first_u_component),
1449 *   dofs_per_block(n_blocks),
1450 *   qf_cell(parameters.quad_order),
1451 *   qf_face(parameters.quad_order),
1452 *   n_q_points (qf_cell.size()),
1453 *   n_q_points_f (qf_face.size())
1454 *   {
1455 *  
1456 *   }
1457 *  
1458 * @endcode
1459 *
1460 * The class destructor simply clears the data held by the DOFHandler
1461 *
1462 * @code
1463 *   template <int dim,typename NumberType>
1464 *   Solid<dim,NumberType>::~Solid()
1465 *   {
1466 *   dof_handler_ref.clear();
1467 *   }
1468 *  
1469 *  
1470 * @endcode
1471 *
1472 * In solving the quasi-static problem, the time becomes a loading parameter,
1473 * i.e. we increasing the loading linearly with time, making the two concepts
1474 * interchangeable. We choose to increment time linearly using a constant time
1475 * step size.
1476 *
1477
1478 *
1479 * We start the function with preprocessing, and then output the initial grid
1480 * before starting the simulation proper with the first time (and loading)
1481 * increment.
1482 *
1483
1484 *
1485 *
1486 * @code
1487 *   template <int dim,typename NumberType>
1488 *   void Solid<dim,NumberType>::run()
1489 *   {
1490 *   make_grid();
1491 *   system_setup();
1492 *   output_results();
1493 *   time.increment();
1494 *  
1495 * @endcode
1496 *
1497 * We then declare the incremental solution update @f$\varDelta
1498 * \mathbf{\Xi}:= \{\varDelta \mathbf{u}\}@f$ and start the loop over the
1499 * time domain.
1500 *
1501
1502 *
1503 * At the beginning, we reset the solution update for this time step...
1504 *
1505 * @code
1506 *   BlockVector<double> solution_delta(dofs_per_block);
1507 *   while (time.current() <= time.end())
1508 *   {
1509 *   solution_delta = 0.0;
1510 *  
1511 * @endcode
1512 *
1513 * ...solve the current time step and update total solution vector
1514 * @f$\mathbf{\Xi}_{\textrm{n}} = \mathbf{\Xi}_{\textrm{n-1}} +
1515 * \varDelta \mathbf{\Xi}@f$...
1516 *
1517 * @code
1518 *   solve_nonlinear_timestep(solution_delta);
1519 *   solution_n += solution_delta;
1520 *  
1521 * @endcode
1522 *
1523 * ...and plot the results before moving on happily to the next time
1524 * step:
1525 *
1526 * @code
1527 *   output_results();
1528 *   time.increment();
1529 *   }
1530 *  
1531 * @endcode
1532 *
1533 * Lastly, we print the vertical tip displacement of the Cook cantilever
1534 * after the full load is applied
1535 *
1536 * @code
1537 *   print_vertical_tip_displacement();
1538 *   }
1539 *  
1540 *  
1541 * @endcode
1542 *
1543 *
1544 * <a name="cook_membrane.cc-Privateinterface"></a>
1545 * <h3>Private interface</h3>
1546 *
1547
1548 *
1549 *
1550 * <a name="cook_membrane.cc-Solidmake_grid"></a>
1551 * <h4>Solid::make_grid</h4>
1552 *
1553
1554 *
1555 * On to the first of the private member functions. Here we create the
1556 * triangulation of the domain, for which we choose a scaled an anisotripically
1557 * discretised rectangle which is subsequently transformed into the correct
1558 * of the Cook cantilever. Each relevant boundary face is then given a boundary
1559 * ID number.
1560 *
1561
1562 *
1563 * We then determine the volume of the reference configuration and print it
1564 * for comparison.
1565 *
1566
1567 *
1568 *
1569 * @code
1570 *   template <int dim>
1571 *   Point<dim> grid_y_transform (const Point<dim> &pt_in)
1572 *   {
1573 *   const double &x = pt_in[0];
1574 *   const double &y = pt_in[1];
1575 *  
1576 *   const double y_upper = 44.0 + (16.0/48.0)*x; // Line defining upper edge of beam
1577 *   const double y_lower = 0.0 + (44.0/48.0)*x; // Line defining lower edge of beam
1578 *   const double theta = y/44.0; // Fraction of height along left side of beam
1579 *   const double y_transform = (1-theta)*y_lower + theta*y_upper; // Final transformation
1580 *  
1581 *   Point<dim> pt_out = pt_in;
1582 *   pt_out[1] = y_transform;
1583 *  
1584 *   return pt_out;
1585 *   }
1586 *  
1587 *   template <int dim,typename NumberType>
1588 *   void Solid<dim,NumberType>::make_grid()
1589 *   {
1590 * @endcode
1591 *
1592 * Divide the beam, but only along the x- and y-coordinate directions
1593 *
1594 * @code
1595 *   std::vector< unsigned int > repetitions(dim, parameters.elements_per_edge);
1596 * @endcode
1597 *
1598 * Only allow one element through the thickness
1599 * (modelling a plane strain condition)
1600 *
1601 * @code
1602 *   if (dim == 3)
1603 *   repetitions[dim-1] = 1;
1604 *  
1605 *   const Point<dim> bottom_left = (dim == 3 ? Point<dim>(0.0, 0.0, -0.5) : Point<dim>(0.0, 0.0));
1606 *   const Point<dim> top_right = (dim == 3 ? Point<dim>(48.0, 44.0, 0.5) : Point<dim>(48.0, 44.0));
1607 *  
1609 *   repetitions,
1610 *   bottom_left,
1611 *   top_right);
1612 *  
1613 * @endcode
1614 *
1615 * Since we wish to apply a Neumann BC to the right-hand surface, we
1616 * must find the cell faces in this part of the domain and mark them with
1617 * a distinct boundary ID number. The faces we are looking for are on the
1618 * +x surface and will get boundary ID 11.
1619 * Dirichlet boundaries exist on the left-hand face of the beam (this fixed
1620 * boundary will get ID 1) and on the +Z and -Z faces (which correspond to
1621 * ID 2 and we will use to impose the plane strain condition)
1622 *
1623 * @code
1624 *   const double tol_boundary = 1e-6;
1625 *   typename Triangulation<dim>::active_cell_iterator cell =
1626 *   triangulation.begin_active(), endc = triangulation.end();
1627 *   for (; cell != endc; ++cell)
1628 *   for (unsigned int face = 0;
1629 *   face < GeometryInfo<dim>::faces_per_cell; ++face)
1630 *   if (cell->face(face)->at_boundary() == true)
1631 *   {
1632 *   if (std::abs(cell->face(face)->center()[0] - 0.0) < tol_boundary)
1633 *   cell->face(face)->set_boundary_id(1); // -X faces
1634 *   else if (std::abs(cell->face(face)->center()[0] - 48.0) < tol_boundary)
1635 *   cell->face(face)->set_boundary_id(11); // +X faces
1636 *   else if (dim == 3 && std::abs(std::abs(cell->face(face)->center()[2]) - 0.5) < tol_boundary)
1637 *   cell->face(face)->set_boundary_id(2); // +Z and -Z faces
1638 *   }
1639 *  
1640 * @endcode
1641 *
1642 * Transform the hyper-rectangle into the beam shape
1643 *
1644 * @code
1645 *   GridTools::transform(&grid_y_transform<dim>, triangulation);
1646 *  
1647 *   GridTools::scale(parameters.scale, triangulation);
1648 *  
1649 *   vol_reference = GridTools::volume(triangulation);
1650 *   vol_current = vol_reference;
1651 *   std::cout << "Grid:\n\t Reference volume: " << vol_reference << std::endl;
1652 *   }
1653 *  
1654 *  
1655 * @endcode
1656 *
1657 *
1658 * <a name="cook_membrane.cc-Solidsystem_setup"></a>
1659 * <h4>Solid::system_setup</h4>
1660 *
1661
1662 *
1663 * Next we describe how the FE system is setup. We first determine the number
1664 * of components per block. Since the displacement is a vector component, the
1665 * first dim components belong to it.
1666 *
1667 * @code
1668 *   template <int dim,typename NumberType>
1669 *   void Solid<dim,NumberType>::system_setup()
1670 *   {
1671 *   timer.enter_subsection("Setup system");
1672 *  
1673 *   std::vector<unsigned int> block_component(n_components, u_dof); // Displacement
1674 *  
1675 * @endcode
1676 *
1677 * The DOF handler is then initialised and we renumber the grid in an
1678 * efficient manner. We also record the number of DOFs per block.
1679 *
1680 * @code
1681 *   dof_handler_ref.distribute_dofs(fe);
1682 *   DoFRenumbering::Cuthill_McKee(dof_handler_ref);
1683 *   DoFRenumbering::component_wise(dof_handler_ref, block_component);
1684 *   dofs_per_block = DoFTools::count_dofs_per_fe_block(dof_handler_ref, block_component);
1685 *  
1686 *   std::cout << "Triangulation:"
1687 *   << "\n\t Number of active cells: " << triangulation.n_active_cells()
1688 *   << "\n\t Number of degrees of freedom: " << dof_handler_ref.n_dofs()
1689 *   << std::endl;
1690 *  
1691 * @endcode
1692 *
1693 * Setup the sparsity pattern and tangent matrix
1694 *
1695 * @code
1696 *   tangent_matrix.clear();
1697 *   {
1698 *   const types::global_dof_index n_dofs_u = dofs_per_block[u_dof];
1699 *  
1700 *   BlockDynamicSparsityPattern csp(n_blocks, n_blocks);
1701 *  
1702 *   csp.block(u_dof, u_dof).reinit(n_dofs_u, n_dofs_u);
1703 *   csp.collect_sizes();
1704 *  
1705 * @endcode
1706 *
1707 * Naturally, for a one-field vector-valued problem, all of the
1708 * components of the system are coupled.
1709 *
1710 * @code
1711 *   Table<2, DoFTools::Coupling> coupling(n_components, n_components);
1712 *   for (unsigned int ii = 0; ii < n_components; ++ii)
1713 *   for (unsigned int jj = 0; jj < n_components; ++jj)
1714 *   coupling[ii][jj] = DoFTools::always;
1715 *   DoFTools::make_sparsity_pattern(dof_handler_ref,
1716 *   coupling,
1717 *   csp,
1718 *   constraints,
1719 *   false);
1720 *   sparsity_pattern.copy_from(csp);
1721 *   }
1722 *  
1723 *   tangent_matrix.reinit(sparsity_pattern);
1724 *  
1725 * @endcode
1726 *
1727 * We then set up storage vectors
1728 *
1729 * @code
1730 *   system_rhs.reinit(dofs_per_block);
1731 *   system_rhs.collect_sizes();
1732 *  
1733 *   solution_n.reinit(dofs_per_block);
1734 *   solution_n.collect_sizes();
1735 *  
1736 * @endcode
1737 *
1738 * ...and finally set up the quadrature
1739 * point history:
1740 *
1741 * @code
1742 *   setup_qph();
1743 *  
1744 *   timer.leave_subsection();
1745 *   }
1746 *  
1747 *  
1748 * @endcode
1749 *
1750 *
1751 * <a name="cook_membrane.cc-Solidsetup_qph"></a>
1752 * <h4>Solid::setup_qph</h4>
1753 * The method used to store quadrature information is already described in
1754 * @ref step_18 "step-18" and @ref step_44 "step-44". Here we implement a similar setup for a SMP machine.
1755 *
1756
1757 *
1758 * Firstly the actual QPH data objects are created. This must be done only
1759 * once the grid is refined to its finest level.
1760 *
1761 * @code
1762 *   template <int dim,typename NumberType>
1763 *   void Solid<dim,NumberType>::setup_qph()
1764 *   {
1765 *   std::cout << " Setting up quadrature point data..." << std::endl;
1766 *  
1767 *   quadrature_point_history.initialize(triangulation.begin_active(),
1768 *   triangulation.end(),
1769 *   n_q_points);
1770 *  
1771 * @endcode
1772 *
1773 * Next we setup the initial quadrature point data. Note that when
1774 * the quadrature point data is retrieved, it is returned as a vector
1775 * of smart pointers.
1776 *
1777 * @code
1778 *   for (typename Triangulation<dim>::active_cell_iterator cell =
1779 *   triangulation.begin_active(); cell != triangulation.end(); ++cell)
1780 *   {
1781 *   const std::vector<std::shared_ptr<PointHistory<dim,NumberType> > > lqph =
1782 *   quadrature_point_history.get_data(cell);
1783 *   Assert(lqph.size() == n_q_points, ExcInternalError());
1784 *  
1785 *   for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
1786 *   lqph[q_point]->setup_lqp(parameters);
1787 *   }
1788 *   }
1789 *  
1790 *  
1791 * @endcode
1792 *
1793 *
1794 * <a name="cook_membrane.cc-Solidsolve_nonlinear_timestep"></a>
1795 * <h4>Solid::solve_nonlinear_timestep</h4>
1796 *
1797
1798 *
1799 * The next function is the driver method for the Newton-Raphson scheme. At
1800 * its top we create a new vector to store the current Newton update step,
1801 * reset the error storage objects and print solver header.
1802 *
1803 * @code
1804 *   template <int dim,typename NumberType>
1805 *   void
1806 *   Solid<dim,NumberType>::solve_nonlinear_timestep(BlockVector<double> &solution_delta)
1807 *   {
1808 *   std::cout << std::endl << "Timestep " << time.get_timestep() << " @ "
1809 *   << time.current() << "s" << std::endl;
1810 *  
1811 *   BlockVector<double> newton_update(dofs_per_block);
1812 *  
1813 *   error_residual.reset();
1814 *   error_residual_0.reset();
1815 *   error_residual_norm.reset();
1816 *   error_update.reset();
1817 *   error_update_0.reset();
1818 *   error_update_norm.reset();
1819 *  
1820 *   print_conv_header();
1821 *  
1822 * @endcode
1823 *
1824 * We now perform a number of Newton iterations to iteratively solve the
1825 * nonlinear problem. Since the problem is fully nonlinear and we are
1826 * using a full Newton method, the data stored in the tangent matrix and
1827 * right-hand side vector is not reusable and must be cleared at each
1828 * Newton step. We then initially build the right-hand side vector to
1829 * check for convergence (and store this value in the first iteration).
1830 * The unconstrained DOFs of the rhs vector hold the out-of-balance
1831 * forces. The building is done before assembling the system matrix as the
1832 * latter is an expensive operation and we can potentially avoid an extra
1833 * assembly process by not assembling the tangent matrix when convergence
1834 * is attained.
1835 *
1836 * @code
1837 *   unsigned int newton_iteration = 0;
1838 *   for (; newton_iteration < parameters.max_iterations_NR;
1839 *   ++newton_iteration)
1840 *   {
1841 *   std::cout << " " << std::setw(2) << newton_iteration << " " << std::flush;
1842 *  
1843 * @endcode
1844 *
1845 * If we have decided that we want to continue with the iteration, we
1846 * assemble the tangent, make and impose the Dirichlet constraints,
1847 * and do the solve of the linearized system:
1848 *
1849 * @code
1850 *   make_constraints(newton_iteration);
1851 *   assemble_system(solution_delta);
1852 *  
1853 *   get_error_residual(error_residual);
1854 *  
1855 *   if (newton_iteration == 0)
1856 *   error_residual_0 = error_residual;
1857 *  
1858 * @endcode
1859 *
1860 * We can now determine the normalised residual error and check for
1861 * solution convergence:
1862 *
1863 * @code
1864 *   error_residual_norm = error_residual;
1865 *   error_residual_norm.normalise(error_residual_0);
1866 *  
1867 *   if (newton_iteration > 0 && error_update_norm.u <= parameters.tol_u
1868 *   && error_residual_norm.u <= parameters.tol_f)
1869 *   {
1870 *   std::cout << " CONVERGED! " << std::endl;
1871 *   print_conv_footer();
1872 *  
1873 *   break;
1874 *   }
1875 *  
1876 *   const std::pair<unsigned int, double>
1877 *   lin_solver_output = solve_linear_system(newton_update);
1878 *  
1879 *   get_error_update(newton_update, error_update);
1880 *   if (newton_iteration == 0)
1881 *   error_update_0 = error_update;
1882 *  
1883 * @endcode
1884 *
1885 * We can now determine the normalised Newton update error, and
1886 * perform the actual update of the solution increment for the current
1887 * time step, update all quadrature point information pertaining to
1888 * this new displacement and stress state and continue iterating:
1889 *
1890 * @code
1891 *   error_update_norm = error_update;
1892 *   error_update_norm.normalise(error_update_0);
1893 *  
1894 *   solution_delta += newton_update;
1895 *  
1896 *   std::cout << " | " << std::fixed << std::setprecision(3) << std::setw(7)
1897 *   << std::scientific << lin_solver_output.first << " "
1898 *   << lin_solver_output.second << " " << error_residual_norm.norm
1899 *   << " " << error_residual_norm.u << " "
1900 *   << " " << error_update_norm.norm << " " << error_update_norm.u
1901 *   << " " << std::endl;
1902 *   }
1903 *  
1904 * @endcode
1905 *
1906 * At the end, if it turns out that we have in fact done more iterations
1907 * than the parameter file allowed, we raise an exception that can be
1908 * caught in the main() function. The call <code>AssertThrow(condition,
1909 * exc_object)</code> is in essence equivalent to <code>if (!cond) throw
1910 * exc_object;</code> but the former form fills certain fields in the
1911 * exception object that identify the location (filename and line number)
1912 * where the exception was raised to make it simpler to identify where the
1913 * problem happened.
1914 *
1915 * @code
1916 *   AssertThrow (newton_iteration <= parameters.max_iterations_NR,
1917 *   ExcMessage("No convergence in nonlinear solver!"));
1918 *   }
1919 *  
1920 *  
1921 * @endcode
1922 *
1923 *
1924 * <a name="cook_membrane.cc-Solidprint_conv_headerSolidprint_conv_footerandSolidprint_vertical_tip_displacement"></a>
1925 * <h4>Solid::print_conv_header, Solid::print_conv_footer and Solid::print_vertical_tip_displacement</h4>
1926 *
1927
1928 *
1929 * This program prints out data in a nice table that is updated
1930 * on a per-iteration basis. The next two functions set up the table
1931 * header and footer:
1932 *
1933 * @code
1934 *   template <int dim,typename NumberType>
1935 *   void Solid<dim,NumberType>::print_conv_header()
1936 *   {
1937 *   static const unsigned int l_width = 87;
1938 *  
1939 *   for (unsigned int i = 0; i < l_width; ++i)
1940 *   std::cout << "_";
1941 *   std::cout << std::endl;
1942 *  
1943 *   std::cout << " SOLVER STEP "
1944 *   << " | LIN_IT LIN_RES RES_NORM "
1945 *   << " RES_U NU_NORM "
1946 *   << " NU_U " << std::endl;
1947 *  
1948 *   for (unsigned int i = 0; i < l_width; ++i)
1949 *   std::cout << "_";
1950 *   std::cout << std::endl;
1951 *   }
1952 *  
1953 *  
1954 *  
1955 *   template <int dim,typename NumberType>
1956 *   void Solid<dim,NumberType>::print_conv_footer()
1957 *   {
1958 *   static const unsigned int l_width = 87;
1959 *  
1960 *   for (unsigned int i = 0; i < l_width; ++i)
1961 *   std::cout << "_";
1962 *   std::cout << std::endl;
1963 *  
1964 *   std::cout << "Relative errors:" << std::endl
1965 *   << "Displacement:\t" << error_update.u / error_update_0.u << std::endl
1966 *   << "Force: \t\t" << error_residual.u / error_residual_0.u << std::endl
1967 *   << "v / V_0:\t" << vol_current << " / " << vol_reference
1968 *   << std::endl;
1969 *   }
1970 *  
1971 * @endcode
1972 *
1973 * At the end we also output the result that can be compared to that found in
1974 * the literature, namely the displacement at the upper right corner of the
1975 * beam.
1976 *
1977 * @code
1978 *   template <int dim,typename NumberType>
1979 *   void Solid<dim,NumberType>::print_vertical_tip_displacement()
1980 *   {
1981 *   static const unsigned int l_width = 87;
1982 *  
1983 *   for (unsigned int i = 0; i < l_width; ++i)
1984 *   std::cout << "_";
1985 *   std::cout << std::endl;
1986 *  
1987 * @endcode
1988 *
1989 * The measurement point, as stated in the reference paper, is at the midway
1990 * point of the surface on which the traction is applied.
1991 *
1992 * @code
1993 *   const Point<dim> soln_pt = (dim == 3 ?
1994 *   Point<dim>(48.0*parameters.scale, 52.0*parameters.scale, 0.5*parameters.scale) :
1995 *   Point<dim>(48.0*parameters.scale, 52.0*parameters.scale));
1996 *   double vertical_tip_displacement = 0.0;
1997 *   double vertical_tip_displacement_check = 0.0;
1998 *  
1999 *   typename DoFHandler<dim>::active_cell_iterator cell =
2000 *   dof_handler_ref.begin_active(), endc = dof_handler_ref.end();
2001 *   for (; cell != endc; ++cell)
2002 *   {
2003 * @endcode
2004 *
2005 * if (cell->point_inside(soln_pt) == true)
2006 *
2007 * @code
2008 *   for (unsigned int v=0; v<GeometryInfo<dim>::vertices_per_cell; ++v)
2009 *   if (cell->vertex(v).distance(soln_pt) < 1e-6)
2010 *   {
2011 * @endcode
2012 *
2013 * Extract y-component of solution at the given point
2014 * This point is coindicent with a vertex, so we can
2015 * extract it directly as we're using FE_Q finite elements
2016 * that have support at the vertices
2017 *
2018 * @code
2019 *   vertical_tip_displacement = solution_n(cell->vertex_dof_index(v,u_dof+1));
2020 *  
2021 * @endcode
2022 *
2023 * Sanity check using alternate method to extract the solution
2024 * at the given point. To do this, we must create an FEValues instance
2025 * to help us extract the solution value at the desired point
2026 *
2027 * @code
2028 *   const MappingQ<dim> mapping (parameters.poly_degree);
2029 *   const Point<dim> qp_unit = mapping.transform_real_to_unit_cell(cell,soln_pt);
2030 *   const Quadrature<dim> soln_qrule (qp_unit);
2031 *   AssertThrow(soln_qrule.size() == 1, ExcInternalError());
2032 *   FEValues<dim> fe_values_soln (fe, soln_qrule, update_values);
2033 *   fe_values_soln.reinit(cell);
2034 *  
2035 * @endcode
2036 *
2037 * Extract y-component of solution at given point
2038 *
2039 * @code
2040 *   std::vector< Tensor<1,dim> > soln_values (soln_qrule.size());
2041 *   fe_values_soln[u_fe].get_function_values(solution_n,
2042 *   soln_values);
2043 *   vertical_tip_displacement_check = soln_values[0][u_dof+1];
2044 *  
2045 *   break;
2046 *   }
2047 *   }
2048 *   AssertThrow(vertical_tip_displacement > 0.0, ExcMessage("Found no cell with point inside!"));
2049 *  
2050 *   std::cout << "Vertical tip displacement: " << vertical_tip_displacement
2051 *   << "\t Check: " << vertical_tip_displacement_check
2052 *   << std::endl;
2053 *   }
2054 *  
2055 *  
2056 * @endcode
2057 *
2058 *
2059 * <a name="cook_membrane.cc-Solidget_error_residual"></a>
2060 * <h4>Solid::get_error_residual</h4>
2061 *
2062
2063 *
2064 * Determine the true residual error for the problem. That is, determine the
2065 * error in the residual for the unconstrained degrees of freedom. Note that to
2066 * do so, we need to ignore constrained DOFs by setting the residual in these
2067 * vector components to zero.
2068 *
2069 * @code
2070 *   template <int dim,typename NumberType>
2071 *   void Solid<dim,NumberType>::get_error_residual(Errors &error_residual)
2072 *   {
2073 *   BlockVector<double> error_res(dofs_per_block);
2074 *  
2075 *   for (unsigned int i = 0; i < dof_handler_ref.n_dofs(); ++i)
2076 *   if (!constraints.is_constrained(i))
2077 *   error_res(i) = system_rhs(i);
2078 *  
2079 *   error_residual.norm = error_res.l2_norm();
2080 *   error_residual.u = error_res.block(u_dof).l2_norm();
2081 *   }
2082 *  
2083 *  
2084 * @endcode
2085 *
2086 *
2087 * <a name="cook_membrane.cc-Solidget_error_udpate"></a>
2088 * <h4>Solid::get_error_udpate</h4>
2089 *
2090
2091 *
2092 * Determine the true Newton update error for the problem
2093 *
2094 * @code
2095 *   template <int dim,typename NumberType>
2096 *   void Solid<dim,NumberType>::get_error_update(const BlockVector<double> &newton_update,
2097 *   Errors &error_update)
2098 *   {
2099 *   BlockVector<double> error_ud(dofs_per_block);
2100 *   for (unsigned int i = 0; i < dof_handler_ref.n_dofs(); ++i)
2101 *   if (!constraints.is_constrained(i))
2102 *   error_ud(i) = newton_update(i);
2103 *  
2104 *   error_update.norm = error_ud.l2_norm();
2105 *   error_update.u = error_ud.block(u_dof).l2_norm();
2106 *   }
2107 *  
2108 *  
2109 *  
2110 * @endcode
2111 *
2112 *
2113 * <a name="cook_membrane.cc-Solidget_total_solution"></a>
2114 * <h4>Solid::get_total_solution</h4>
2115 *
2116
2117 *
2118 * This function provides the total solution, which is valid at any Newton step.
2119 * This is required as, to reduce computational error, the total solution is
2120 * only updated at the end of the timestep.
2121 *
2122 * @code
2123 *   template <int dim,typename NumberType>
2124 *   BlockVector<double>
2125 *   Solid<dim,NumberType>::get_total_solution(const BlockVector<double> &solution_delta) const
2126 *   {
2127 *   BlockVector<double> solution_total(solution_n);
2128 *   solution_total += solution_delta;
2129 *   return solution_total;
2130 *   }
2131 *  
2132 *  
2133 * @endcode
2134 *
2135 *
2136 * <a name="cook_membrane.cc-Solidassemble_system"></a>
2137 * <h4>Solid::assemble_system</h4>
2138 *
2139
2140 *
2141 *
2142 * @code
2143 *   template <int dim,typename NumberType>
2144 *   struct Assembler_Base
2145 *   {
2146 *   virtual ~Assembler_Base() {}
2147 *  
2148 * @endcode
2149 *
2150 * Here we deal with the tangent matrix assembly structures. The
2151 * PerTaskData object stores local contributions.
2152 *
2153 * @code
2154 *   struct PerTaskData_ASM
2155 *   {
2156 *   const Solid<dim,NumberType> *solid;
2157 *   FullMatrix<double> cell_matrix;
2158 *   Vector<double> cell_rhs;
2159 *   std::vector<types::global_dof_index> local_dof_indices;
2160 *  
2161 *   PerTaskData_ASM(const Solid<dim,NumberType> *solid)
2162 *   :
2163 *   solid (solid),
2164 *   cell_matrix(solid->dofs_per_cell, solid->dofs_per_cell),
2165 *   cell_rhs(solid->dofs_per_cell),
2166 *   local_dof_indices(solid->dofs_per_cell)
2167 *   {}
2168 *  
2169 *   void reset()
2170 *   {
2171 *   cell_matrix = 0.0;
2172 *   cell_rhs = 0.0;
2173 *   }
2174 *   };
2175 *  
2176 * @endcode
2177 *
2178 * On the other hand, the ScratchData object stores the larger objects such as
2179 * the shape-function values array (<code>Nx</code>) and a shape function
2180 * gradient and symmetric gradient vector which we will use during the
2181 * assembly.
2182 *
2183 * @code
2184 *   struct ScratchData_ASM
2185 *   {
2186 *   const BlockVector<double> &solution_total;
2187 *   std::vector<Tensor<2, dim,NumberType> > solution_grads_u_total;
2188 *  
2189 *   FEValues<dim> fe_values_ref;
2190 *   FEFaceValues<dim> fe_face_values_ref;
2191 *  
2192 *   std::vector<std::vector<Tensor<2, dim,NumberType> > > grad_Nx;
2193 *   std::vector<std::vector<SymmetricTensor<2,dim,NumberType> > > symm_grad_Nx;
2194 *  
2195 *   ScratchData_ASM(const FiniteElement<dim> &fe_cell,
2196 *   const QGauss<dim> &qf_cell,
2197 *   const UpdateFlags uf_cell,
2198 *   const QGauss<dim-1> & qf_face,
2199 *   const UpdateFlags uf_face,
2200 *   const BlockVector<double> &solution_total)
2201 *   :
2202 *   solution_total(solution_total),
2203 *   solution_grads_u_total(qf_cell.size()),
2204 *   fe_values_ref(fe_cell, qf_cell, uf_cell),
2205 *   fe_face_values_ref(fe_cell, qf_face, uf_face),
2206 *   grad_Nx(qf_cell.size(),
2207 *   std::vector<Tensor<2,dim,NumberType> >(fe_cell.dofs_per_cell)),
2208 *   symm_grad_Nx(qf_cell.size(),
2209 *   std::vector<SymmetricTensor<2,dim,NumberType> >
2210 *   (fe_cell.dofs_per_cell))
2211 *   {}
2212 *  
2213 *   ScratchData_ASM(const ScratchData_ASM &rhs)
2214 *   :
2215 *   solution_total (rhs.solution_total),
2216 *   solution_grads_u_total(rhs.solution_grads_u_total),
2217 *   fe_values_ref(rhs.fe_values_ref.get_fe(),
2218 *   rhs.fe_values_ref.get_quadrature(),
2219 *   rhs.fe_values_ref.get_update_flags()),
2220 *   fe_face_values_ref(rhs.fe_face_values_ref.get_fe(),
2221 *   rhs.fe_face_values_ref.get_quadrature(),
2222 *   rhs.fe_face_values_ref.get_update_flags()),
2223 *   grad_Nx(rhs.grad_Nx),
2224 *   symm_grad_Nx(rhs.symm_grad_Nx)
2225 *   {}
2226 *  
2227 *   void reset()
2228 *   {
2229 *   const unsigned int n_q_points = fe_values_ref.get_quadrature().size();
2230 *   const unsigned int n_dofs_per_cell = fe_values_ref.dofs_per_cell;
2231 *   for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
2232 *   {
2233 *   Assert( grad_Nx[q_point].size() == n_dofs_per_cell,
2234 *   ExcInternalError());
2235 *   Assert( symm_grad_Nx[q_point].size() == n_dofs_per_cell,
2236 *   ExcInternalError());
2237 *  
2238 *   solution_grads_u_total[q_point] = Tensor<2,dim,NumberType>();
2239 *   for (unsigned int k = 0; k < n_dofs_per_cell; ++k)
2240 *   {
2241 *   grad_Nx[q_point][k] = Tensor<2,dim,NumberType>();
2242 *   symm_grad_Nx[q_point][k] = SymmetricTensor<2,dim,NumberType>();
2243 *   }
2244 *   }
2245 *   }
2246 *  
2247 *   };
2248 *  
2249 * @endcode
2250 *
2251 * Of course, we still have to define how we assemble the tangent matrix
2252 * contribution for a single cell.
2253 *
2254 * @code
2255 *   void
2256 *   assemble_system_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
2257 *   ScratchData_ASM &scratch,
2258 *   PerTaskData_ASM &data)
2259 *   {
2260 * @endcode
2261 *
2262 * Due to the C++ specialization rules, we need one more
2263 * level of indirection in order to define the assembly
2264 * routine for all different number. The next function call
2265 * is specialized for each NumberType, but to prevent having
2266 * to specialize the whole class along with it we have inlined
2267 * the definition of the other functions that are common to
2268 * all implementations.
2269 *
2270 * @code
2271 *   assemble_system_tangent_residual_one_cell(cell, scratch, data);
2272 *   assemble_neumann_contribution_one_cell(cell, scratch, data);
2273 *   }
2274 *  
2275 * @endcode
2276 *
2277 * This function adds the local contribution to the system matrix.
2278 *
2279 * @code
2280 *   void
2281 *   copy_local_to_global_ASM(const PerTaskData_ASM &data)
2282 *   {
2283 *   const AffineConstraints<double> &constraints = data.solid->constraints;
2284 *   BlockSparseMatrix<double> &tangent_matrix = const_cast<Solid<dim,NumberType> *>(data.solid)->tangent_matrix;
2285 *   BlockVector<double> &system_rhs = const_cast<Solid<dim,NumberType> *>(data.solid)->system_rhs;
2286 *  
2287 *   constraints.distribute_local_to_global(
2288 *   data.cell_matrix, data.cell_rhs,
2289 *   data.local_dof_indices,
2290 *   tangent_matrix, system_rhs);
2291 *   }
2292 *  
2293 *   protected:
2294 *  
2295 * @endcode
2296 *
2297 * This function needs to exist in the base class for
2298 * Workstream to work with a reference to the base class.
2299 *
2300 * @code
2301 *   virtual void
2302 *   assemble_system_tangent_residual_one_cell(const typename DoFHandler<dim>::active_cell_iterator &/*cell*/,
2303 *   ScratchData_ASM &/*scratch*/,
2304 *   PerTaskData_ASM &/*data*/)
2305 *   {
2306 *   AssertThrow(false, ExcPureFunctionCalled());
2307 *   }
2308 *  
2309 *   void
2310 *   assemble_neumann_contribution_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
2311 *   ScratchData_ASM &scratch,
2312 *   PerTaskData_ASM &data)
2313 *   {
2314 * @endcode
2315 *
2316 * Aliases for data referenced from the Solid class
2317 *
2318 * @code
2319 *   const unsigned int &n_q_points_f = data.solid->n_q_points_f;
2320 *   const unsigned int &dofs_per_cell = data.solid->dofs_per_cell;
2321 *   const Parameters::AllParameters &parameters = data.solid->parameters;
2322 *   const Time &time = data.solid->time;
2323 *   const FESystem<dim> &fe = data.solid->fe;
2324 *   const unsigned int &u_dof = data.solid->u_dof;
2325 *  
2326 * @endcode
2327 *
2328 * Next we assemble the Neumann contribution. We first check to see it the
2329 * cell face exists on a boundary on which a traction is applied and add
2330 * the contribution if this is the case.
2331 *
2332 * @code
2333 *   for (unsigned int face = 0; face < GeometryInfo<dim>::faces_per_cell;
2334 *   ++face)
2335 *   if (cell->face(face)->at_boundary() == true
2336 *   && cell->face(face)->boundary_id() == 11)
2337 *   {
2338 *   scratch.fe_face_values_ref.reinit(cell, face);
2339 *  
2340 *   for (unsigned int f_q_point = 0; f_q_point < n_q_points_f;
2341 *   ++f_q_point)
2342 *   {
2343 * @endcode
2344 *
2345 * We specify the traction in reference configuration.
2346 * For this problem, a defined total vertical force is applied
2347 * in the reference configuration.
2348 * The direction of the applied traction is assumed not to
2349 * evolve with the deformation of the domain.
2350 *
2351
2352 *
2353 * Note that the contributions to the right hand side vector we
2354 * compute here only exist in the displacement components of the
2355 * vector.
2356 *
2357 * @code
2358 *   const double time_ramp = (time.current() / time.end());
2359 *   const double magnitude = (1.0/(16.0*parameters.scale*1.0*parameters.scale))*time_ramp; // (Total force) / (RHS surface area)
2360 *   Tensor<1,dim> dir;
2361 *   dir[1] = 1.0;
2362 *   const Tensor<1, dim> traction = magnitude*dir;
2363 *  
2364 *   for (unsigned int i = 0; i < dofs_per_cell; ++i)
2365 *   {
2366 *   const unsigned int i_group =
2367 *   fe.system_to_base_index(i).first.first;
2368 *  
2369 *   if (i_group == u_dof)
2370 *   {
2371 *   const unsigned int component_i =
2372 *   fe.system_to_component_index(i).first;
2373 *   const double Ni =
2374 *   scratch.fe_face_values_ref.shape_value(i,
2375 *   f_q_point);
2376 *   const double JxW = scratch.fe_face_values_ref.JxW(
2377 *   f_q_point);
2378 *  
2379 *   data.cell_rhs(i) += (Ni * traction[component_i])
2380 *   * JxW;
2381 *   }
2382 *   }
2383 *   }
2384 *   }
2385 *   }
2386 *  
2387 *   };
2388 *  
2389 *   template <int dim>
2390 *   struct Assembler<dim,double> : Assembler_Base<dim,double>
2391 *   {
2392 *   typedef double NumberType;
2393 *   using typename Assembler_Base<dim,NumberType>::ScratchData_ASM;
2394 *   using typename Assembler_Base<dim,NumberType>::PerTaskData_ASM;
2395 *  
2396 *   virtual ~Assembler() {}
2397 *  
2398 *   virtual void
2399 *   assemble_system_tangent_residual_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
2400 *   ScratchData_ASM &scratch,
2401 *   PerTaskData_ASM &data) override
2402 *   {
2403 * @endcode
2404 *
2405 * Aliases for data referenced from the Solid class
2406 *
2407 * @code
2408 *   const unsigned int &n_q_points = data.solid->n_q_points;
2409 *   const unsigned int &dofs_per_cell = data.solid->dofs_per_cell;
2410 *   const FESystem<dim> &fe = data.solid->fe;
2411 *   const unsigned int &u_dof = data.solid->u_dof;
2412 *   const FEValuesExtractors::Vector &u_fe = data.solid->u_fe;
2413 *  
2414 *   data.reset();
2415 *   scratch.reset();
2416 *   scratch.fe_values_ref.reinit(cell);
2417 *   cell->get_dof_indices(data.local_dof_indices);
2418 *  
2419 *   const std::vector<std::shared_ptr<const PointHistory<dim,NumberType> > > lqph =
2420 *   const_cast<const Solid<dim,NumberType> *>(data.solid)->quadrature_point_history.get_data(cell);
2421 *   Assert(lqph.size() == n_q_points, ExcInternalError());
2422 *  
2423 * @endcode
2424 *
2425 * We first need to find the solution gradients at quadrature points
2426 * inside the current cell and then we update each local QP using the
2427 * displacement gradient:
2428 *
2429 * @code
2430 *   scratch.fe_values_ref[u_fe].get_function_gradients(scratch.solution_total,
2431 *   scratch.solution_grads_u_total);
2432 *  
2433 * @endcode
2434 *
2435 * Now we build the local cell stiffness matrix. Since the global and
2436 * local system matrices are symmetric, we can exploit this property by
2437 * building only the lower half of the local matrix and copying the values
2438 * to the upper half.
2439 *
2440
2441 *
2442 * In doing so, we first extract some configuration dependent variables
2443 * from our QPH history objects for the current quadrature point.
2444 *
2445 * @code
2446 *   for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
2447 *   {
2448 *   const Tensor<2,dim,NumberType> &grad_u = scratch.solution_grads_u_total[q_point];
2449 *   const Tensor<2,dim,NumberType> F = Physics::Elasticity::Kinematics::F(grad_u);
2450 *   const NumberType det_F = determinant(F);
2451 *   const Tensor<2,dim,NumberType> F_bar = Physics::Elasticity::Kinematics::F_iso(F);
2452 *   const SymmetricTensor<2,dim,NumberType> b_bar = Physics::Elasticity::Kinematics::b(F_bar);
2453 *   const Tensor<2,dim,NumberType> F_inv = invert(F);
2454 *   Assert(det_F > NumberType(0.0), ExcInternalError());
2455 *  
2456 *   for (unsigned int k = 0; k < dofs_per_cell; ++k)
2457 *   {
2458 *   const unsigned int k_group = fe.system_to_base_index(k).first.first;
2459 *  
2460 *   if (k_group == u_dof)
2461 *   {
2462 *   scratch.grad_Nx[q_point][k] = scratch.fe_values_ref[u_fe].gradient(k, q_point) * F_inv;
2463 *   scratch.symm_grad_Nx[q_point][k] = symmetrize(scratch.grad_Nx[q_point][k]);
2464 *   }
2465 *   else
2466 *   Assert(k_group <= u_dof, ExcInternalError());
2467 *   }
2468 *  
2469 *   const SymmetricTensor<2,dim,NumberType> tau = lqph[q_point]->get_tau(det_F,b_bar);
2470 *   const SymmetricTensor<4,dim,NumberType> Jc = lqph[q_point]->get_Jc(det_F,b_bar);
2471 *   const Tensor<2,dim,NumberType> tau_ns (tau);
2472 *  
2473 * @endcode
2474 *
2475 * Next we define some aliases to make the assembly process easier to
2476 * follow
2477 *
2478 * @code
2479 *   const std::vector<SymmetricTensor<2, dim> > &symm_grad_Nx = scratch.symm_grad_Nx[q_point];
2480 *   const std::vector<Tensor<2, dim> > &grad_Nx = scratch.grad_Nx[q_point];
2481 *   const double JxW = scratch.fe_values_ref.JxW(q_point);
2482 *  
2483 *   for (unsigned int i = 0; i < dofs_per_cell; ++i)
2484 *   {
2485 *   const unsigned int component_i = fe.system_to_component_index(i).first;
2486 *   const unsigned int i_group = fe.system_to_base_index(i).first.first;
2487 *  
2488 *   if (i_group == u_dof)
2489 *   data.cell_rhs(i) -= (symm_grad_Nx[i] * tau) * JxW;
2490 *   else
2491 *   Assert(i_group <= u_dof, ExcInternalError());
2492 *  
2493 *   for (unsigned int j = 0; j <= i; ++j)
2494 *   {
2495 *   const unsigned int component_j = fe.system_to_component_index(j).first;
2496 *   const unsigned int j_group = fe.system_to_base_index(j).first.first;
2497 *  
2498 * @endcode
2499 *
2500 * This is the @f$\mathsf{\mathbf{k}}_{\mathbf{u} \mathbf{u}}@f$
2501 * contribution. It comprises a material contribution, and a
2502 * geometrical stress contribution which is only added along
2503 * the local matrix diagonals:
2504 *
2505 * @code
2506 *   if ((i_group == j_group) && (i_group == u_dof))
2507 *   {
2508 *   data.cell_matrix(i, j) += symm_grad_Nx[i] * Jc // The material contribution:
2509 *   * symm_grad_Nx[j] * JxW;
2510 *   if (component_i == component_j) // geometrical stress contribution
2511 *   data.cell_matrix(i, j) += grad_Nx[i][component_i] * tau_ns
2512 *   * grad_Nx[j][component_j] * JxW;
2513 *   }
2514 *   else
2515 *   Assert((i_group <= u_dof) && (j_group <= u_dof),
2516 *   ExcInternalError());
2517 *   }
2518 *   }
2519 *   }
2520 *  
2521 *  
2522 * @endcode
2523 *
2524 * Finally, we need to copy the lower half of the local matrix into the
2525 * upper half:
2526 *
2527 * @code
2528 *   for (unsigned int i = 0; i < dofs_per_cell; ++i)
2529 *   for (unsigned int j = i + 1; j < dofs_per_cell; ++j)
2530 *   data.cell_matrix(i, j) = data.cell_matrix(j, i);
2531 *   }
2532 *  
2533 *   };
2534 *  
2535 *   #ifdef ENABLE_SACADO_FORMULATION
2536 *  
2537 *  
2538 *   template <int dim>
2539 *   struct Assembler<dim,Sacado::Fad::DFad<double> > : Assembler_Base<dim,Sacado::Fad::DFad<double> >
2540 *   {
2541 *   typedef Sacado::Fad::DFad<double> ADNumberType;
2542 *   using typename Assembler_Base<dim,ADNumberType>::ScratchData_ASM;
2543 *   using typename Assembler_Base<dim,ADNumberType>::PerTaskData_ASM;
2544 *  
2545 *   virtual ~Assembler() {}
2546 *  
2547 *   virtual void
2548 *   assemble_system_tangent_residual_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
2549 *   ScratchData_ASM &scratch,
2550 *   PerTaskData_ASM &data) override
2551 *   {
2552 * @endcode
2553 *
2554 * Aliases for data referenced from the Solid class
2555 *
2556 * @code
2557 *   const unsigned int &n_q_points = data.solid->n_q_points;
2558 *   const unsigned int &dofs_per_cell = data.solid->dofs_per_cell;
2559 *   const FESystem<dim> &fe = data.solid->fe;
2560 *   const unsigned int &u_dof = data.solid->u_dof;
2561 *   const FEValuesExtractors::Vector &u_fe = data.solid->u_fe;
2562 *  
2563 *   data.reset();
2564 *   scratch.reset();
2565 *   scratch.fe_values_ref.reinit(cell);
2566 *   cell->get_dof_indices(data.local_dof_indices);
2567 *  
2568 *   const std::vector<std::shared_ptr<const PointHistory<dim,ADNumberType> > > lqph =
2569 *   const_cast<const Solid<dim,ADNumberType> *>(data.solid)->quadrature_point_history.get_data(cell);
2570 *   Assert(lqph.size() == n_q_points, ExcInternalError());
2571 *  
2572 *   const unsigned int n_independent_variables = data.local_dof_indices.size();
2573 *   std::vector<double> local_dof_values(n_independent_variables);
2574 *   cell->get_dof_values(scratch.solution_total,
2575 *   local_dof_values.begin(),
2576 *   local_dof_values.end());
2577 *  
2578 * @endcode
2579 *
2580 * We now retrieve a set of degree-of-freedom values that
2581 * have the operations that are performed with them tracked.
2582 *
2583 * @code
2584 *   std::vector<ADNumberType> local_dof_values_ad (n_independent_variables);
2585 *   for (unsigned int i=0; i<n_independent_variables; ++i)
2586 *   local_dof_values_ad[i] = ADNumberType(n_independent_variables, i, local_dof_values[i]);
2587 *  
2588 * @endcode
2589 *
2590 * Compute all values, gradients etc. based on sensitive
2591 * AD degree-of-freedom values.
2592 *
2593 * @code
2594 *   scratch.fe_values_ref[u_fe].get_function_gradients_from_local_dof_values(
2595 *   local_dof_values_ad,
2596 *   scratch.solution_grads_u_total);
2597 *  
2598 * @endcode
2599 *
2600 * Accumulate the residual value for each degree of freedom.
2601 * Note: Its important that the vectors is initialised (zero'd) correctly.
2602 *
2603 * @code
2604 *   std::vector<ADNumberType> residual_ad (dofs_per_cell, ADNumberType(0.0));
2605 *   for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
2606 *   {
2607 *   const Tensor<2,dim,ADNumberType> &grad_u = scratch.solution_grads_u_total[q_point];
2609 *   const ADNumberType det_F = determinant(F);
2612 *   const Tensor<2,dim,ADNumberType> F_inv = invert(F);
2613 *   Assert(det_F > ADNumberType(0.0), ExcInternalError());
2614 *  
2615 *   for (unsigned int k = 0; k < dofs_per_cell; ++k)
2616 *   {
2617 *   const unsigned int k_group = fe.system_to_base_index(k).first.first;
2618 *  
2619 *   if (k_group == u_dof)
2620 *   {
2621 *   scratch.grad_Nx[q_point][k] = scratch.fe_values_ref[u_fe].gradient(k, q_point) * F_inv;
2622 *   scratch.symm_grad_Nx[q_point][k] = symmetrize(scratch.grad_Nx[q_point][k]);
2623 *   }
2624 *   else
2625 *   Assert(k_group <= u_dof, ExcInternalError());
2626 *   }
2627 *  
2628 *   const SymmetricTensor<2,dim,ADNumberType> tau = lqph[q_point]->get_tau(det_F,b_bar);
2629 *  
2630 * @endcode
2631 *
2632 * Next we define some position-dependent aliases, again to
2633 * make the assembly process easier to follow.
2634 *
2635 * @code
2636 *   const std::vector<SymmetricTensor<2, dim,ADNumberType> > &symm_grad_Nx = scratch.symm_grad_Nx[q_point];
2637 *   const double JxW = scratch.fe_values_ref.JxW(q_point);
2638 *  
2639 *   for (unsigned int i = 0; i < dofs_per_cell; ++i)
2640 *   {
2641 *   const unsigned int i_group = fe.system_to_base_index(i).first.first;
2642 *  
2643 *   if (i_group == u_dof)
2644 *   residual_ad[i] += (symm_grad_Nx[i] * tau) * JxW;
2645 *   else
2646 *   Assert(i_group <= u_dof, ExcInternalError());
2647 *   }
2648 *   }
2649 *  
2650 *   for (unsigned int I=0; I<n_independent_variables; ++I)
2651 *   {
2652 *   const ADNumberType &residual_I = residual_ad[I];
2653 *   data.cell_rhs(I) = -residual_I.val(); // RHS = - residual
2654 *   for (unsigned int J=0; J<n_independent_variables; ++J)
2655 *   {
2656 * @endcode
2657 *
2658 * Compute the gradients of the residual entry [forward-mode]
2659 *
2660 * @code
2661 *   data.cell_matrix(I,J) = residual_I.dx(J); // linearisation_IJ
2662 *   }
2663 *   }
2664 *   }
2665 *  
2666 *   };
2667 *  
2668 *  
2669 *   template <int dim>
2670 *   struct Assembler<dim,Sacado::Rad::ADvar<Sacado::Fad::DFad<double> > > : Assembler_Base<dim,Sacado::Rad::ADvar<Sacado::Fad::DFad<double> > >
2671 *   {
2672 *   typedef Sacado::Fad::DFad<double> ADDerivType;
2673 *   typedef Sacado::Rad::ADvar<ADDerivType> ADNumberType;
2674 *   using typename Assembler_Base<dim,ADNumberType>::ScratchData_ASM;
2675 *   using typename Assembler_Base<dim,ADNumberType>::PerTaskData_ASM;
2676 *  
2677 *   virtual ~Assembler() {}
2678 *  
2679 *   virtual void
2680 *   assemble_system_tangent_residual_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
2681 *   ScratchData_ASM &scratch,
2682 *   PerTaskData_ASM &data) override
2683 *   {
2684 * @endcode
2685 *
2686 * Aliases for data referenced from the Solid class
2687 *
2688 * @code
2689 *   const unsigned int &n_q_points = data.solid->n_q_points;
2690 *   const FEValuesExtractors::Vector &u_fe = data.solid->u_fe;
2691 *  
2692 *   data.reset();
2693 *   scratch.reset();
2694 *   scratch.fe_values_ref.reinit(cell);
2695 *   cell->get_dof_indices(data.local_dof_indices);
2696 *  
2697 *   const std::vector<std::shared_ptr<const PointHistory<dim,ADNumberType> > > lqph =
2698 *   data.solid->quadrature_point_history.get_data(cell);
2699 *   Assert(lqph.size() == n_q_points, ExcInternalError());
2700 *  
2701 *   const unsigned int n_independent_variables = data.local_dof_indices.size();
2702 *   std::vector<double> local_dof_values(n_independent_variables);
2703 *   cell->get_dof_values(scratch.solution_total,
2704 *   local_dof_values.begin(),
2705 *   local_dof_values.end());
2706 *  
2707 * @endcode
2708 *
2709 * We now retrieve a set of degree-of-freedom values that
2710 * have the operations that are performed with them tracked.
2711 *
2712 * @code
2713 *   std::vector<ADNumberType> local_dof_values_ad (n_independent_variables);
2714 *   for (unsigned int i=0; i<n_independent_variables; ++i)
2715 *   local_dof_values_ad[i] = ADDerivType(n_independent_variables, i, local_dof_values[i]);
2716 *  
2717 * @endcode
2718 *
2719 * Compute all values, gradients etc. based on sensitive
2720 * AD degree-of-freedom values.
2721 *
2722 * @code
2723 *   scratch.fe_values_ref[u_fe].get_function_gradients_from_local_dof_values(
2724 *   local_dof_values_ad,
2725 *   scratch.solution_grads_u_total);
2726 *  
2727 * @endcode
2728 *
2729 * Next we compute the total potential energy of the element.
2730 * This is defined as follows:
2731 * Total energy = (internal - external) energies
2732 * Note: Its important that this value is initialised (zero'd) correctly.
2733 *
2734 * @code
2735 *   ADNumberType cell_energy_ad = ADNumberType(0.0);
2736 *   for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
2737 *   {
2738 *   const Tensor<2,dim,ADNumberType> &grad_u = scratch.solution_grads_u_total[q_point];
2739 *   const Tensor<2,dim,ADNumberType> F = Physics::Elasticity::Kinematics::F(grad_u);
2740 *   const ADNumberType det_F = determinant(F);
2741 *   const Tensor<2,dim,ADNumberType> F_bar = Physics::Elasticity::Kinematics::F_iso(F);
2742 *   const SymmetricTensor<2,dim,ADNumberType> b_bar = Physics::Elasticity::Kinematics::b(F_bar);
2743 *   Assert(det_F > ADNumberType(0.0), ExcInternalError());
2744 *  
2745 * @endcode
2746 *
2747 * Next we define some position-dependent aliases, again to
2748 * make the assembly process easier to follow.
2749 *
2750 * @code
2751 *   const double JxW = scratch.fe_values_ref.JxW(q_point);
2752 *  
2753 *   const ADNumberType Psi = lqph[q_point]->get_Psi(det_F,b_bar);
2754 *  
2755 * @endcode
2756 *
2757 * We extract the configuration-dependent material energy
2758 * from our QPH history objects for the current quadrature point
2759 * and integrate its contribution to increment the total
2760 * cell energy.
2761 *
2762 * @code
2763 *   cell_energy_ad += Psi * JxW;
2764 *   }
2765 *  
2766 * @endcode
2767 *
2768 * Compute derivatives of reverse-mode AD variables
2769 *
2770 * @code
2771 *   ADNumberType::Gradcomp();
2772 *  
2773 *   for (unsigned int I=0; I<n_independent_variables; ++I)
2774 *   {
2775 * @endcode
2776 *
2777 * This computes the adjoint df/dX_{i} [reverse-mode]
2778 *
2779 * @code
2780 *   const ADDerivType residual_I = local_dof_values_ad[I].adj();
2781 *   data.cell_rhs(I) = -residual_I.val(); // RHS = - residual
2782 *   for (unsigned int J=0; J<n_independent_variables; ++J)
2783 *   {
2784 * @endcode
2785 *
2786 * Compute the gradients of the residual entry [forward-mode]
2787 *
2788 * @code
2789 *   data.cell_matrix(I,J) = residual_I.dx(J); // linearisation_IJ
2790 *   }
2791 *   }
2792 *   }
2793 *  
2794 *   };
2795 *  
2796 *  
2797 *   #endif
2798 *  
2799 *  
2800 * @endcode
2801 *
2802 * Since we use TBB for assembly, we simply setup a copy of the
2803 * data structures required for the process and pass them, along
2804 * with the memory addresses of the assembly functions to the
2805 * WorkStream object for processing. Note that we must ensure that
2806 * the matrix is reset before any assembly operations can occur.
2807 *
2808 * @code
2809 *   template <int dim,typename NumberType>
2810 *   void Solid<dim,NumberType>::assemble_system(const BlockVector<double> &solution_delta)
2811 *   {
2812 *   timer.enter_subsection("Assemble linear system");
2813 *   std::cout << " ASM " << std::flush;
2814 *  
2815 *   tangent_matrix = 0.0;
2816 *   system_rhs = 0.0;
2817 *  
2818 *   const UpdateFlags uf_cell(update_gradients |
2819 *   update_JxW_values);
2820 *   const UpdateFlags uf_face(update_values |
2821 *   update_JxW_values);
2822 *  
2823 *   const BlockVector<double> solution_total(get_total_solution(solution_delta));
2824 *   typename Assembler_Base<dim,NumberType>::PerTaskData_ASM per_task_data(this);
2825 *   typename Assembler_Base<dim,NumberType>::ScratchData_ASM scratch_data(fe, qf_cell, uf_cell, qf_face, uf_face, solution_total);
2826 *   Assembler<dim,NumberType> assembler;
2827 *  
2828 *   WorkStream::run(dof_handler_ref.begin_active(),
2829 *   dof_handler_ref.end(),
2830 *   static_cast<Assembler_Base<dim,NumberType>&>(assembler),
2831 *   &Assembler_Base<dim,NumberType>::assemble_system_one_cell,
2832 *   &Assembler_Base<dim,NumberType>::copy_local_to_global_ASM,
2833 *   scratch_data,
2834 *   per_task_data);
2835 *  
2836 *   timer.leave_subsection();
2837 *   }
2838 *  
2839 *  
2840 * @endcode
2841 *
2842 *
2843 * <a name="cook_membrane.cc-Solidmake_constraints"></a>
2844 * <h4>Solid::make_constraints</h4>
2845 * The constraints for this problem are simple to describe.
2846 * However, since we are dealing with an iterative Newton method,
2847 * it should be noted that any displacement constraints should only
2848 * be specified at the zeroth iteration and subsequently no
2849 * additional contributions are to be made since the constraints
2850 * are already exactly satisfied.
2851 *
2852 * @code
2853 *   template <int dim,typename NumberType>
2854 *   void Solid<dim,NumberType>::make_constraints(const int &it_nr)
2855 *   {
2856 *   std::cout << " CST " << std::flush;
2857 *  
2858 * @endcode
2859 *
2860 * Since the constraints are different at different Newton iterations, we
2861 * need to clear the constraints matrix and completely rebuild
2862 * it. However, after the first iteration, the constraints remain the same
2863 * and we can simply skip the rebuilding step if we do not clear it.
2864 *
2865 * @code
2866 *   if (it_nr > 1)
2867 *   return;
2868 *   const bool apply_dirichlet_bc = (it_nr == 0);
2869 *  
2870 * @endcode
2871 *
2872 * The boundary conditions for the indentation problem are as follows: On
2873 * the -x face (ID = 1), we set up a zero-displacement condition, -y and +y traction
2874 * free boundary condition (don't need to take care); -z and +z faces (ID = 2) are
2875 * not allowed to move along z axis so that it is a plane strain problem.
2876 * Finally, as described earlier, +x face (ID = 11) has an the applied
2877 * distributed shear force (converted by total force per unit area) which
2878 * needs to be taken care as an inhomogeneous Newmann boundary condition.
2879 *
2880
2881 *
2882 * In the following, we will have to tell the function interpolation
2883 * boundary values which components of the solution vector should be
2884 * constrained (i.e., whether it's the x-, y-, z-displacements or
2885 * combinations thereof). This is done using ComponentMask objects (see
2886 * @ref GlossComponentMask) which we can get from the finite element if we
2887 * provide it with an extractor object for the component we wish to
2888 * select. To this end we first set up such extractor objects and later
2889 * use it when generating the relevant component masks:
2890 *
2891
2892 *
2893 *
2894 * @code
2895 *   if (apply_dirichlet_bc)
2896 *   {
2897 *   constraints.clear();
2898 *  
2899 * @endcode
2900 *
2901 * Fixed left hand side of the beam
2902 *
2903 * @code
2904 *   {
2905 *   const int boundary_id = 1;
2906 *   VectorTools::interpolate_boundary_values(dof_handler_ref,
2907 *   boundary_id,
2908 *   Functions::ZeroFunction<dim>(n_components),
2909 *   constraints,
2910 *   fe.component_mask(u_fe));
2911 *   }
2912 *  
2913 * @endcode
2914 *
2915 * Zero Z-displacement through thickness direction
2916 * This corresponds to a plane strain condition being imposed on the beam
2917 *
2918 * @code
2919 *   if (dim == 3)
2920 *   {
2921 *   const int boundary_id = 2;
2922 *   const FEValuesExtractors::Scalar z_displacement(2);
2923 *   VectorTools::interpolate_boundary_values(dof_handler_ref,
2924 *   boundary_id,
2925 *   Functions::ZeroFunction<dim>(n_components),
2926 *   constraints,
2927 *   fe.component_mask(z_displacement));
2928 *   }
2929 *   }
2930 *   else
2931 *   {
2932 *   if (constraints.has_inhomogeneities())
2933 *   {
2934 *   AffineConstraints<double> homogeneous_constraints(constraints);
2935 *   for (unsigned int dof = 0; dof != dof_handler_ref.n_dofs(); ++dof)
2936 *   if (homogeneous_constraints.is_inhomogeneously_constrained(dof))
2937 *   homogeneous_constraints.set_inhomogeneity(dof, 0.0);
2938 *   constraints.clear();
2939 *   constraints.copy_from(homogeneous_constraints);
2940 *   }
2941 *   }
2942 *  
2943 *   constraints.close();
2944 *   }
2945 *  
2946 * @endcode
2947 *
2948 *
2949 * <a name="cook_membrane.cc-Solidsolve_linear_system"></a>
2950 * <h4>Solid::solve_linear_system</h4>
2951 * As the system is composed of a single block, defining a solution scheme
2952 * for the linear problem is straight-forward.
2953 *
2954 * @code
2955 *   template <int dim,typename NumberType>
2956 *   std::pair<unsigned int, double>
2957 *   Solid<dim,NumberType>::solve_linear_system(BlockVector<double> &newton_update)
2958 *   {
2959 *   BlockVector<double> A(dofs_per_block);
2960 *   BlockVector<double> B(dofs_per_block);
2961 *  
2962 *   unsigned int lin_it = 0;
2963 *   double lin_res = 0.0;
2964 *  
2965 * @endcode
2966 *
2967 * We solve for the incremental displacement @f$d\mathbf{u}@f$.
2968 *
2969 * @code
2970 *   {
2971 *   timer.enter_subsection("Linear solver");
2972 *   std::cout << " SLV " << std::flush;
2973 *   if (parameters.type_lin == "CG")
2974 *   {
2975 *   const int solver_its = static_cast<unsigned int>(
2976 *   tangent_matrix.block(u_dof, u_dof).m()
2977 *   * parameters.max_iterations_lin);
2978 *   const double tol_sol = parameters.tol_lin
2979 *   * system_rhs.block(u_dof).l2_norm();
2980 *  
2981 *   SolverControl solver_control(solver_its, tol_sol);
2982 *  
2984 *   SolverCG<Vector<double> > solver_CG(solver_control, GVM);
2985 *  
2986 * @endcode
2987 *
2988 * We've chosen by default a SSOR preconditioner as it appears to
2989 * provide the fastest solver convergence characteristics for this
2990 * problem on a single-thread machine. However, for multicore
2991 * computing, the Jacobi preconditioner which is multithreaded may
2992 * converge quicker for larger linear systems.
2993 *
2994 * @code
2995 *   PreconditionSelector<SparseMatrix<double>, Vector<double> >
2996 *   preconditioner (parameters.preconditioner_type,
2997 *   parameters.preconditioner_relaxation);
2998 *   preconditioner.use_matrix(tangent_matrix.block(u_dof, u_dof));
2999 *  
3000 *   solver_CG.solve(tangent_matrix.block(u_dof, u_dof),
3001 *   newton_update.block(u_dof),
3002 *   system_rhs.block(u_dof),
3003 *   preconditioner);
3004 *  
3005 *   lin_it = solver_control.last_step();
3006 *   lin_res = solver_control.last_value();
3007 *   }
3008 *   else if (parameters.type_lin == "Direct")
3009 *   {
3010 * @endcode
3011 *
3012 * Otherwise if the problem is small
3013 * enough, a direct solver can be
3014 * utilised.
3015 *
3016 * @code
3017 *   SparseDirectUMFPACK A_direct;
3018 *   A_direct.initialize(tangent_matrix.block(u_dof, u_dof));
3019 *   A_direct.vmult(newton_update.block(u_dof), system_rhs.block(u_dof));
3020 *  
3021 *   lin_it = 1;
3022 *   lin_res = 0.0;
3023 *   }
3024 *   else
3025 *   Assert (false, ExcMessage("Linear solver type not implemented"));
3026 *  
3027 *   timer.leave_subsection();
3028 *   }
3029 *  
3030 * @endcode
3031 *
3032 * Now that we have the displacement update, distribute the constraints
3033 * back to the Newton update:
3034 *
3035 * @code
3036 *   constraints.distribute(newton_update);
3037 *  
3038 *   return std::make_pair(lin_it, lin_res);
3039 *   }
3040 *  
3041 * @endcode
3042 *
3043 *
3044 * <a name="cook_membrane.cc-Solidoutput_results"></a>
3045 * <h4>Solid::output_results</h4>
3046 * Here we present how the results are written to file to be viewed
3047 * using ParaView or Visit. The method is similar to that shown in the
3048 * tutorials so will not be discussed in detail.
3049 *
3050 * @code
3051 *   template <int dim,typename NumberType>
3052 *   void Solid<dim,NumberType>::output_results() const
3053 *   {
3054 *   DataOut<dim> data_out;
3055 *   std::vector<DataComponentInterpretation::DataComponentInterpretation>
3056 *   data_component_interpretation(dim,
3057 *   DataComponentInterpretation::component_is_part_of_vector);
3058 *  
3059 *   std::vector<std::string> solution_name(dim, "displacement");
3060 *  
3061 *   data_out.attach_dof_handler(dof_handler_ref);
3062 *   data_out.add_data_vector(solution_n,
3063 *   solution_name,
3064 *   DataOut<dim>::type_dof_data,
3065 *   data_component_interpretation);
3066 *  
3067 * @endcode
3068 *
3069 * Since we are dealing with a large deformation problem, it would be nice
3070 * to display the result on a displaced grid! The MappingQEulerian class
3071 * linked with the DataOut class provides an interface through which this
3072 * can be achieved without physically moving the grid points in the
3073 * Triangulation object ourselves. We first need to copy the solution to
3074 * a temporary vector and then create the Eulerian mapping. We also
3075 * specify the polynomial degree to the DataOut object in order to produce
3076 * a more refined output data set when higher order polynomials are used.
3077 *
3078 * @code
3079 *   Vector<double> soln(solution_n.size());
3080 *   for (unsigned int i = 0; i < soln.size(); ++i)
3081 *   soln(i) = solution_n(i);
3082 *   MappingQEulerian<dim> q_mapping(degree, dof_handler_ref, soln);
3083 *   data_out.build_patches(q_mapping, degree);
3084 *  
3085 *   std::ostringstream filename;
3086 *   filename << "solution-" << time.get_timestep() << ".vtk";
3087 *  
3088 *   std::ofstream output(filename.str().c_str());
3089 *   data_out.write_vtk(output);
3090 *   }
3091 *  
3092 *   }
3093 *  
3094 *  
3095 * @endcode
3096 *
3097 *
3098 * <a name="cook_membrane.cc-Mainfunction"></a>
3099 * <h3>Main function</h3>
3100 * Lastly we provide the main driver function which appears
3101 * no different to the other tutorials.
3102 *
3103 * @code
3104 *   int main (int argc, char *argv[])
3105 *   {
3106 *   using namespace dealii;
3107 *   using namespace Cook_Membrane;
3108 *  
3109 *   const unsigned int dim = 3;
3110 *   try
3111 *   {
3112 *   deallog.depth_console(0);
3113 *   Parameters::AllParameters parameters("parameters.prm");
3114 *   if (parameters.automatic_differentiation_order == 0)
3115 *   {
3116 *   std::cout << "Assembly method: Residual and linearisation are computed manually." << std::endl;
3117 *  
3118 * @endcode
3119 *
3120 * Allow multi-threading
3121 *
3122 * @code
3123 *   Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv,
3124 *   ::numbers::invalid_unsigned_int);
3125 *  
3126 *   typedef double NumberType;
3127 *   Solid<dim,NumberType> solid_3d(parameters);
3128 *   solid_3d.run();
3129 *   }
3130 *   #ifdef ENABLE_SACADO_FORMULATION
3131 *   else if (parameters.automatic_differentiation_order == 1)
3132 *   {
3133 *   std::cout << "Assembly method: Residual computed manually; linearisation performed using AD." << std::endl;
3134 *  
3135 * @endcode
3136 *
3137 * Allow multi-threading
3138 *
3139 * @code
3140 *   Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv,
3141 *   ::numbers::invalid_unsigned_int);
3142 *  
3143 *   typedef Sacado::Fad::DFad<double> NumberType;
3144 *   Solid<dim,NumberType> solid_3d(parameters);
3145 *   solid_3d.run();
3146 *   }
3147 *   else if (parameters.automatic_differentiation_order == 2)
3148 *   {
3149 *   std::cout << "Assembly method: Residual and linearisation computed using AD." << std::endl;
3150 *  
3151 * @endcode
3152 *
3153 * Sacado Rad-Fad is not thread-safe, so disable threading.
3154 * Parallelisation using MPI would be possible though.
3155 *
3156 * @code
3157 *   Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv,
3158 *   1);
3159 *  
3160 *   typedef Sacado::Rad::ADvar< Sacado::Fad::DFad<double> > NumberType;
3161 *   Solid<dim,NumberType> solid_3d(parameters);
3162 *   solid_3d.run();
3163 *   }
3164 *   #endif
3165 *   else
3166 *   {
3167 *   AssertThrow(false,
3168 *   ExcMessage("The selected assembly method is not supported. "
3169 *   "You need deal.II 9.0 and Trilinos with the Sacado package "
3170 *   "to enable assembly using automatic differentiation."));
3171 *   }
3172 *   }
3173 *   catch (std::exception &exc)
3174 *   {
3175 *   std::cerr << std::endl << std::endl
3176 *   << "----------------------------------------------------"
3177 *   << std::endl;
3178 *   std::cerr << "Exception on processing: " << std::endl << exc.what()
3179 *   << std::endl << "Aborting!" << std::endl
3180 *   << "----------------------------------------------------"
3181 *   << std::endl;
3182 *  
3183 *   return 1;
3184 *   }
3185 *   catch (...)
3186 *   {
3187 *   std::cerr << std::endl << std::endl
3188 *   << "----------------------------------------------------"
3189 *   << std::endl;
3190 *   std::cerr << "Unknown exception!" << std::endl << "Aborting!"
3191 *   << std::endl
3192 *   << "----------------------------------------------------"
3193 *   << std::endl;
3194 *   return 1;
3195 *   }
3196 *  
3197 *   return 0;
3198 *   }
3199 * @endcode
3200
3201
3202*/
Definition fe_q.h:554
Definition point.h:111
numbers::NumberTraits< Number >::real_type norm() const
constexpr void clear()
@ wall_times
Definition timer.h:651
#define DEAL_II_VERSION_MAJOR
Definition config.h:27
Point< 2 > second
Definition grid_out.cc:4624
Point< 2 > first
Definition grid_out.cc:4623
unsigned int level
Definition grid_out.cc:4626
#define Assert(cond, exc)
#define AssertThrow(cond, exc)
typename ActiveSelector::active_cell_iterator active_cell_iterator
void loop(IteratorType begin, std_cxx20::type_identity_t< IteratorType > end, DOFINFO &dinfo, INFOBOX &info, const std::function< void(std_cxx20::type_identity_t< DOFINFO > &, typename INFOBOX::CellInfo &)> &cell_worker, const std::function< void(std_cxx20::type_identity_t< DOFINFO > &, typename INFOBOX::CellInfo &)> &boundary_worker, const std::function< void(std_cxx20::type_identity_t< DOFINFO > &, std_cxx20::type_identity_t< DOFINFO > &, typename INFOBOX::CellInfo &, typename INFOBOX::CellInfo &)> &face_worker, AssemblerType &assembler, const LoopControl &lctrl=LoopControl())
Definition loop.h:564
void make_sparsity_pattern(const DoFHandler< dim, spacedim > &dof_handler, SparsityPatternBase &sparsity_pattern, const AffineConstraints< number > &constraints={}, const bool keep_constrained_dofs=true, const types::subdomain_id subdomain_id=numbers::invalid_subdomain_id)
std::vector< index_type > data
Definition mpi.cc:735
std::size_t size
Definition mpi.cc:734
const Event initial
Definition event.cc:64
void approximate(const SynchronousIterators< std::tuple< typename DoFHandler< dim, spacedim >::active_cell_iterator, Vector< float >::iterator > > &cell, const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof_handler, const InputVector &solution, const unsigned int component)
void component_wise(DoFHandler< dim, spacedim > &dof_handler, const std::vector< unsigned int > &target_component=std::vector< unsigned int >())
void Cuthill_McKee(DoFHandler< dim, spacedim > &dof_handler, const bool reversed_numbering=false, const bool use_constraints=false, const std::vector< types::global_dof_index > &starting_indices=std::vector< types::global_dof_index >())
std::vector< types::global_dof_index > count_dofs_per_fe_block(const DoFHandler< dim, spacedim > &dof, const std::vector< unsigned int > &target_block=std::vector< unsigned int >())
void subdivided_hyper_rectangle(Triangulation< dim, spacedim > &tria, const std::vector< unsigned int > &repetitions, const Point< dim > &p1, const Point< dim > &p2, const bool colorize=false)
void scale(const double scaling_factor, Triangulation< dim, spacedim > &triangulation)
void transform(const Transformation &transformation, Triangulation< dim, spacedim > &triangulation)
double volume(const Triangulation< dim, spacedim > &tria)
@ matrix
Contents is actually a matrix.
double norm(const FEValuesBase< dim > &fe, const ArrayView< const std::vector< Tensor< 1, dim > > > &Du)
Definition divergence.h:471
std::enable_if_t< IsBlockVector< VectorType >::value, unsigned int > n_blocks(const VectorType &vector)
Definition operators.h:49
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
Definition utilities.cc:191
SymmetricTensor< 2, dim, Number > C(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
Tensor< 2, dim, Number > F_iso(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > d(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
Tensor< 2, dim, Number > F(const Tensor< 2, dim, Number > &Grad_u)
void apply(const Kokkos::TeamPolicy< MemorySpace::Default::kokkos_space::execution_space >::member_type &team_member, const Kokkos::View< Number *, MemorySpace::Default::kokkos_space > shape_data, const ViewTypeIn in, ViewTypeOut out)
constexpr ReturnType< rank, T >::value_type & extract(T &t, const ArrayType &indices)
VectorType::value_type * end(VectorType &V)
void interpolate_boundary_values(const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof, const std::map< types::boundary_id, const Function< spacedim, number > * > &function_map, std::map< types::global_dof_index, number > &boundary_values, const ComponentMask &component_mask={})
void run(const Iterator &begin, const std_cxx20::type_identity_t< Iterator > &end, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const unsigned int queue_length, const unsigned int chunk_size)
bool check(const ConstraintKinds kind_in, const unsigned int dim)
int(&) functions(const void *v1, const void *v2)
void assemble(const MeshWorker::DoFInfoBox< dim, DOFINFO > &dinfo, A *assembler)
Definition loop.h:70
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
unsigned int boundary_id
Definition types.h:144
const ::parallel::distributed::Triangulation< dim, spacedim > * triangulation
DEAL_II_HOST constexpr Number determinant(const SymmetricTensor< 2, dim, Number > &)
DEAL_II_HOST constexpr SymmetricTensor< 2, dim, Number > symmetrize(const Tensor< 2, dim, Number > &t)
DEAL_II_HOST constexpr SymmetricTensor< 2, dim, Number > invert(const SymmetricTensor< 2, dim, Number > &)