196 * We start by including all the necessary deal.II header files and some
C++
197 * related ones. They have been discussed in detail in previous tutorial
198 * programs, so you need only refer to past tutorials
for details.
201 * #include <deal.II/base/function.h>
202 * #include <deal.II/base/parameter_handler.h>
203 * #include <deal.II/base/
point.h>
204 * #include <deal.II/base/quadrature_lib.h>
205 * #include <deal.II/base/symmetric_tensor.h>
206 * #include <deal.II/base/tensor.h>
207 * #include <deal.II/base/timer.h>
208 * #include <deal.II/base/work_stream.h>
209 * #include <deal.II/base/quadrature_point_data.h>
211 * #include <deal.II/dofs/dof_renumbering.h>
212 * #include <deal.II/dofs/dof_tools.h>
214 * #include <deal.II/grid/grid_generator.h>
215 * #include <deal.II/grid/grid_tools.h>
216 * #include <deal.II/grid/grid_in.h>
217 * #include <deal.II/grid/tria.h>
219 * #include <deal.II/fe/fe_dgp_monomial.h>
220 * #include <deal.II/fe/fe_q.h>
221 * #include <deal.II/fe/fe_system.h>
222 * #include <deal.II/fe/fe_tools.h>
223 * #include <deal.II/fe/fe_values.h>
224 * #include <deal.II/fe/mapping_q_eulerian.h>
225 * #include <deal.II/fe/mapping_q.h>
227 * #include <deal.II/lac/affine_constraints.h>
228 * #include <deal.II/lac/block_sparse_matrix.h>
229 * #include <deal.II/lac/block_vector.h>
230 * #include <deal.II/lac/dynamic_sparsity_pattern.h>
231 * #include <deal.II/lac/full_matrix.h>
232 * #include <deal.II/lac/precondition_selector.h>
233 * #include <deal.II/lac/solver_cg.h>
234 * #include <deal.II/lac/sparse_direct.h>
236 * #include <deal.II/numerics/data_out.h>
237 * #include <deal.II/numerics/vector_tools.h>
239 * #include <deal.II/base/config.h>
241 * #include <deal.II/differentiation/ad.h>
242 * #define ENABLE_SACADO_FORMULATION
247 * These must be included below the AD headers so that
248 * their math
functions are available
for use in the
249 * definition of tensors and kinematic quantities
252 * #include <deal.II/physics/elasticity/kinematics.h>
253 * #include <deal.II/physics/elasticity/standard_tensors.h>
255 * #include <iostream>
262 * We then stick everything that relates to
this tutorial program into a
263 *
namespace of its own, and import all the deal.II function and class names
267 *
namespace Cook_Membrane
274 * <a name=
"cook_membrane.cc-Runtimeparameters"></a>
275 * <h3>Run-time parameters</h3>
279 * There are several parameters that can be
set in the code so we
set up a
283 *
namespace Parameters
288 * <a name=
"cook_membrane.cc-Assemblymethod"></a>
289 * <h4>Assembly method</h4>
293 * Here we specify whether automatic differentiation is to be used to
assemble
294 * the linear system, and
if so then what order of differentiation is to be
298 *
struct AssemblyMethod
300 *
unsigned int automatic_differentiation_order;
312 * prm.enter_subsection(
"Assembly method");
314 * prm.declare_entry(
"Automatic differentiation order",
"0",
316 *
"The automatic differentiation order to be used in the assembly of the linear system.\n"
317 *
"# Order = 0: Both the residual and linearisation are computed manually.\n"
318 *
"# Order = 1: The residual is computed manually but the linearisation is performed using AD.\n"
319 *
"# Order = 2: Both the residual and linearisation are computed using AD.");
321 * prm.leave_subsection();
326 * prm.enter_subsection(
"Assembly method");
328 * automatic_differentiation_order = prm.get_integer(
"Automatic differentiation order");
330 * prm.leave_subsection();
336 * <a name=
"cook_membrane.cc-FiniteElementsystem"></a>
337 * <h4>Finite Element system</h4>
341 * Here we specify the polynomial order used to
approximate the solution.
342 * The quadrature order should be adjusted accordingly.
347 *
unsigned int poly_degree;
348 *
unsigned int quad_order;
360 * prm.enter_subsection(
"Finite element system");
362 * prm.declare_entry(
"Polynomial degree",
"2",
364 *
"Displacement system polynomial order");
366 * prm.declare_entry(
"Quadrature order",
"3",
368 *
"Gauss quadrature order");
370 * prm.leave_subsection();
375 * prm.enter_subsection(
"Finite element system");
377 * poly_degree = prm.get_integer(
"Polynomial degree");
378 * quad_order = prm.get_integer(
"Quadrature order");
380 * prm.leave_subsection();
386 * <a name=
"cook_membrane.cc-Geometry"></a>
391 * Make adjustments to the problem geometry and its discretisation.
396 *
unsigned int elements_per_edge;
408 * prm.enter_subsection(
"Geometry");
410 * prm.declare_entry(
"Elements per edge",
"32",
412 *
"Number of elements per long edge of the beam");
414 * prm.declare_entry(
"Grid scale",
"1e-3",
416 *
"Global grid scaling factor");
418 * prm.leave_subsection();
423 * prm.enter_subsection(
"Geometry");
425 * elements_per_edge = prm.get_integer(
"Elements per edge");
426 *
scale = prm.get_double(
"Grid scale");
428 * prm.leave_subsection();
434 * <a name=
"cook_membrane.cc-Materials"></a>
439 * We also need the shear modulus @f$ \mu @f$ and Poisson ration @f$ \nu @f$
for the
440 * neo-Hookean material.
457 * prm.enter_subsection(
"Material properties");
459 * prm.declare_entry(
"Poisson's ratio",
"0.3",
461 *
"Poisson's ratio");
463 * prm.declare_entry(
"Shear modulus",
"0.4225e6",
467 * prm.leave_subsection();
472 * prm.enter_subsection(
"Material properties");
474 * nu = prm.get_double(
"Poisson's ratio");
475 * mu = prm.get_double(
"Shear modulus");
477 * prm.leave_subsection();
483 * <a name=
"cook_membrane.cc-Linearsolver"></a>
484 * <h4>Linear solver</h4>
488 * Next, we choose both solver and preconditioner settings. The use of an
489 * effective preconditioner is critical to ensure convergence when a large
490 * nonlinear motion occurs within a Newton increment.
493 *
struct LinearSolver
495 * std::string type_lin;
497 *
double max_iterations_lin;
498 * std::string preconditioner_type;
499 *
double preconditioner_relaxation;
510 * prm.enter_subsection(
"Linear solver");
512 * prm.declare_entry(
"Solver type",
"CG",
514 *
"Type of solver used to solve the linear system");
516 * prm.declare_entry(
"Residual",
"1e-6",
518 *
"Linear solver residual (scaled by residual norm)");
520 * prm.declare_entry(
"Max iteration multiplier",
"1",
522 *
"Linear solver iterations (multiples of the system matrix size)");
524 * prm.declare_entry(
"Preconditioner type",
"ssor",
526 *
"Type of preconditioner");
528 * prm.declare_entry(
"Preconditioner relaxation",
"0.65",
530 *
"Preconditioner relaxation value");
532 * prm.leave_subsection();
537 * prm.enter_subsection(
"Linear solver");
539 * type_lin = prm.get(
"Solver type");
540 * tol_lin = prm.get_double(
"Residual");
541 * max_iterations_lin = prm.get_double(
"Max iteration multiplier");
542 * preconditioner_type = prm.get(
"Preconditioner type");
543 * preconditioner_relaxation = prm.get_double(
"Preconditioner relaxation");
545 * prm.leave_subsection();
551 * <a name=
"cook_membrane.cc-Nonlinearsolver"></a>
552 * <h4>Nonlinear solver</h4>
556 * A Newton-Raphson scheme is used to solve the nonlinear system of governing
557 * equations. We now define the tolerances and the maximum number of
558 * iterations
for the Newton-Raphson nonlinear solver.
561 *
struct NonlinearSolver
563 *
unsigned int max_iterations_NR;
576 * prm.enter_subsection(
"Nonlinear solver");
578 * prm.declare_entry(
"Max iterations Newton-Raphson",
"10",
580 *
"Number of Newton-Raphson iterations allowed");
582 * prm.declare_entry(
"Tolerance force",
"1.0e-9",
584 *
"Force residual tolerance");
586 * prm.declare_entry(
"Tolerance displacement",
"1.0e-6",
588 *
"Displacement error tolerance");
590 * prm.leave_subsection();
595 * prm.enter_subsection(
"Nonlinear solver");
597 * max_iterations_NR = prm.get_integer(
"Max iterations Newton-Raphson");
598 * tol_f = prm.get_double(
"Tolerance force");
599 * tol_u = prm.get_double(
"Tolerance displacement");
601 * prm.leave_subsection();
607 * <a name=
"cook_membrane.cc-Time"></a>
612 * Set the timestep size @f$ \varDelta t @f$ and the simulation
end-time.
629 * prm.enter_subsection(
"Time");
631 * prm.declare_entry(
"End time",
"1",
635 * prm.declare_entry(
"Time step size",
"0.1",
639 * prm.leave_subsection();
644 * prm.enter_subsection(
"Time");
646 * end_time = prm.get_double(
"End time");
647 * delta_t = prm.get_double(
"Time step size");
649 * prm.leave_subsection();
655 * <a name=
"cook_membrane.cc-Allparameters"></a>
656 * <h4>All parameters</h4>
660 * Finally we consolidate all of the above structures into a single container
661 * that holds all of our
run-time selections.
664 *
struct AllParameters :
665 *
public AssemblyMethod,
669 *
public LinearSolver,
670 *
public NonlinearSolver,
674 * AllParameters(
const std::string &input_file);
683 * AllParameters::AllParameters(
const std::string &input_file)
686 * declare_parameters(prm);
687 * prm.parse_input(input_file);
688 * parse_parameters(prm);
693 * AssemblyMethod::declare_parameters(prm);
694 * FESystem::declare_parameters(prm);
695 * Geometry::declare_parameters(prm);
696 * Materials::declare_parameters(prm);
697 * LinearSolver::declare_parameters(prm);
698 * NonlinearSolver::declare_parameters(prm);
699 * Time::declare_parameters(prm);
704 * AssemblyMethod::parse_parameters(prm);
705 * FESystem::parse_parameters(prm);
706 * Geometry::parse_parameters(prm);
707 * Materials::parse_parameters(prm);
708 * LinearSolver::parse_parameters(prm);
709 * NonlinearSolver::parse_parameters(prm);
710 * Time::parse_parameters(prm);
718 * <a name=
"cook_membrane.cc-Timeclass"></a>
719 * <h3>Time
class</h3>
723 * A simple
class to store time data. Its functioning is transparent so no
724 * discussion is necessary. For simplicity we assume a
constant time step
731 * Time (
const double time_end,
732 *
const double delta_t)
736 * time_end(time_end),
743 *
double current() const
745 *
return time_current;
751 *
double get_delta_t() const
755 *
unsigned int get_timestep() const
761 * time_current += delta_t;
766 *
unsigned int timestep;
767 *
double time_current;
768 *
const double time_end;
769 *
const double delta_t;
775 * <a name=
"cook_membrane.cc-CompressibleneoHookeanmaterialwithinaonefieldformulation"></a>
776 * <h3>Compressible neo-Hookean material within a one-field formulation</h3>
780 * As discussed in the literature and @ref step_44
"step-44", Neo-Hookean materials are a type
781 * of hyperelastic materials. The entire domain is assumed to be composed of a
782 * compressible neo-Hookean material. This
class defines the behaviour of
783 *
this material within a one-field formulation. Compressible neo-Hookean
784 * materials can be described by a strain-energy function (SEF) @f$ \Psi =
785 * \Psi_{\text{iso}}(\overline{\mathbf{
b}}) + \Psi_{\text{vol}}(J)
790 * The isochoric response is given by @f$
791 * \Psi_{\text{iso}}(\overline{\mathbf{
b}}) = c_{1} [\overline{I}_{1} - 3] @f$
792 * where @f$ c_{1} = \frac{\mu}{2} @f$ and @f$\overline{I}_{1}@f$ is the
first
793 * invariant of the left- or right-isochoric Cauchy-Green deformation tensors.
794 * That is @f$\overline{I}_1 :=\textrm{tr}(\overline{\mathbf{
b}})@f$. In
this
795 * example the SEF that governs the volumetric response is defined as @f$
796 * \Psi_{\text{vol}}(J) = \kappa \frac{1}{4} [ J^2 - 1
797 * - 2\textrm{ln}\; J ]@f$, where @f$\kappa:= \
lambda + 2/3 \mu@f$ is
798 * the <a href=
"http://en.wikipedia.org/wiki/Bulk_modulus">bulk modulus</a>
799 * and @f$\lambda@f$ is <a
800 * href=
"http://en.wikipedia.org/wiki/Lam%C3%A9_parameters">Lame
's first
805 * The following class will be used to characterize the material we work with,
806 * and provides a central point that one would need to modify if one were to
807 * implement a different material model. For it to work, we will store one
808 * object of this type per quadrature point, and in each of these objects
809 * store the current state (characterized by the values or measures of the
810 * displacement field) so that we can compute the elastic coefficients
811 * linearized around the current state.
814 * template <int dim,typename NumberType>
815 * class Material_Compressible_Neo_Hook_One_Field
818 * Material_Compressible_Neo_Hook_One_Field(const double mu,
821 * kappa((2.0 * mu * (1.0 + nu)) / (3.0 * (1.0 - 2.0 * nu))),
824 * Assert(kappa > 0, ExcInternalError());
827 * ~Material_Compressible_Neo_Hook_One_Field()
832 * The first function is the total energy
833 * @f$\Psi = \Psi_{\textrm{iso}} + \Psi_{\textrm{vol}}@f$.
837 * get_Psi(const NumberType &det_F,
838 * const SymmetricTensor<2,dim,NumberType> &b_bar) const
840 * return get_Psi_vol(det_F) + get_Psi_iso(b_bar);
845 * The second function determines the Kirchhoff stress @f$\boldsymbol{\tau}
846 * = \boldsymbol{\tau}_{\textrm{iso}} + \boldsymbol{\tau}_{\textrm{vol}}@f$
849 * SymmetricTensor<2,dim,NumberType>
850 * get_tau(const NumberType &det_F,
851 * const SymmetricTensor<2,dim,NumberType> &b_bar)
855 * See Holzapfel p231 eq6.98 onwards
858 * return get_tau_vol(det_F) + get_tau_iso(b_bar);
863 * The fourth-order elasticity tensor in the spatial setting
864 * @f$\mathfrak{c}@f$ is calculated from the SEF @f$\Psi@f$ as @f$ J
865 * \mathfrak{c}_{ijkl} = F_{iA} F_{jB} \mathfrak{C}_{ABCD} F_{kC} F_{lD}@f$
866 * where @f$ \mathfrak{C} = 4 \frac{\partial^2 \Psi(\mathbf{C})}{\partial
867 * \mathbf{C} \partial \mathbf{C}}@f$
870 * SymmetricTensor<4,dim,NumberType>
871 * get_Jc(const NumberType &det_F,
872 * const SymmetricTensor<2,dim,NumberType> &b_bar) const
874 * return get_Jc_vol(det_F) + get_Jc_iso(b_bar);
880 * Define constitutive model parameters @f$\kappa@f$ (bulk modulus) and the
881 * neo-Hookean model parameter @f$c_1@f$:
884 * const double kappa;
889 * Value of the volumetric free energy
893 * get_Psi_vol(const NumberType &det_F) const
895 * return (kappa / 4.0) * (det_F*det_F - 1.0 - 2.0*std::log(det_F));
900 * Value of the isochoric free energy
904 * get_Psi_iso(const SymmetricTensor<2,dim,NumberType> &b_bar) const
906 * return c_1 * (trace(b_bar) - dim);
911 * Derivative of the volumetric free energy with respect to
912 * @f$J@f$ return @f$\frac{\partial
913 * \Psi_{\text{vol}}(J)}{\partial J}@f$
917 * get_dPsi_vol_dJ(const NumberType &det_F) const
919 * return (kappa / 2.0) * (det_F - 1.0 / det_F);
924 * The following functions are used internally in determining the result
925 * of some of the public functions above. The first one determines the
926 * volumetric Kirchhoff stress @f$\boldsymbol{\tau}_{\textrm{vol}}@f$.
927 * Note the difference in its definition when compared to @ref step_44 "step-44".
930 * SymmetricTensor<2,dim,NumberType>
931 * get_tau_vol(const NumberType &det_F) const
933 * return NumberType(get_dPsi_vol_dJ(det_F) * det_F) * Physics::Elasticity::StandardTensors<dim>::I;
938 * Next, determine the isochoric Kirchhoff stress
939 * @f$\boldsymbol{\tau}_{\textrm{iso}} =
940 * \mathcal{P}:\overline{\boldsymbol{\tau}}@f$:
943 * SymmetricTensor<2,dim,NumberType>
944 * get_tau_iso(const SymmetricTensor<2,dim,NumberType> &b_bar) const
946 * return Physics::Elasticity::StandardTensors<dim>::dev_P * get_tau_bar(b_bar);
951 * Then, determine the fictitious Kirchhoff stress
952 * @f$\overline{\boldsymbol{\tau}}@f$:
955 * SymmetricTensor<2,dim,NumberType>
956 * get_tau_bar(const SymmetricTensor<2,dim,NumberType> &b_bar) const
958 * return 2.0 * c_1 * b_bar;
963 * Second derivative of the volumetric free energy wrt @f$J@f$. We
964 * need the following computation explicitly in the tangent so we make it
965 * public. We calculate @f$\frac{\partial^2
966 * \Psi_{\textrm{vol}}(J)}{\partial J \partial
971 * get_d2Psi_vol_dJ2(const NumberType &det_F) const
973 * return ( (kappa / 2.0) * (1.0 + 1.0 / (det_F * det_F)));
978 * Calculate the volumetric part of the tangent @f$J
979 * \mathfrak{c}_\textrm{vol}@f$. Again, note the difference in its
980 * definition when compared to @ref step_44 "step-44". The extra terms result from two
981 * quantities in @f$\boldsymbol{\tau}_{\textrm{vol}}@f$ being dependent on
982 * @f$\boldsymbol{F}@f$.
985 * SymmetricTensor<4,dim,NumberType>
986 * get_Jc_vol(const NumberType &det_F) const
994 * * ( (get_dPsi_vol_dJ(det_F) + det_F * get_d2Psi_vol_dJ2(det_F))*Physics::Elasticity::StandardTensors<dim>::IxI
995 * - (2.0 * get_dPsi_vol_dJ(det_F))*Physics::Elasticity::StandardTensors<dim>::S );
1000 * Calculate the isochoric part of the tangent @f$J
1001 * \mathfrak{c}_\textrm{iso}@f$:
1004 * SymmetricTensor<4,dim,NumberType>
1005 * get_Jc_iso(const SymmetricTensor<2,dim,NumberType> &b_bar) const
1007 * const SymmetricTensor<2, dim> tau_bar = get_tau_bar(b_bar);
1008 * const SymmetricTensor<2, dim> tau_iso = get_tau_iso(b_bar);
1009 * const SymmetricTensor<4, dim> tau_iso_x_I
1010 * = outer_product(tau_iso,
1011 * Physics::Elasticity::StandardTensors<dim>::I);
1012 * const SymmetricTensor<4, dim> I_x_tau_iso
1013 * = outer_product(Physics::Elasticity::StandardTensors<dim>::I,
1015 * const SymmetricTensor<4, dim> c_bar = get_c_bar();
1017 * return (2.0 / dim) * trace(tau_bar)
1018 * * Physics::Elasticity::StandardTensors<dim>::dev_P
1019 * - (2.0 / dim) * (tau_iso_x_I + I_x_tau_iso)
1020 * + Physics::Elasticity::StandardTensors<dim>::dev_P * c_bar
1021 * * Physics::Elasticity::StandardTensors<dim>::dev_P;
1026 * Calculate the fictitious elasticity tensor @f$\overline{\mathfrak{c}}@f$.
1027 * For the material model chosen this is simply zero:
1030 * SymmetricTensor<4,dim,double>
1033 * return SymmetricTensor<4, dim>();
1040 * <a name="cook_membrane.cc-Quadraturepointhistory"></a>
1041 * <h3>Quadrature point history</h3>
1045 * As seen in @ref step_18 "step-18", the <code> PointHistory </code> class offers a method
1046 * for storing data at the quadrature points. Here each quadrature point
1047 * holds a pointer to a material description. Thus, different material models
1048 * can be used in different regions of the domain. Among other data, we
1049 * choose to store the Kirchhoff stress @f$\boldsymbol{\tau}@f$ and the tangent
1050 * @f$J\mathfrak{c}@f$ for the quadrature points.
1053 * template <int dim,typename NumberType>
1054 * class PointHistory
1060 * virtual ~PointHistory()
1065 * The first function is used to create a material object and to
1066 * initialize all tensors correctly: The second one updates the stored
1067 * values and stresses based on the current deformation measure
1068 * @f$\textrm{Grad}\mathbf{u}_{\textrm{n}}@f$.
1071 * void setup_lqp (const Parameters::AllParameters ¶meters)
1073 * material.reset(new Material_Compressible_Neo_Hook_One_Field<dim,NumberType>(parameters.mu,
1079 * We offer an interface to retrieve certain data.
1080 * This is the strain energy:
1084 * get_Psi(const NumberType &det_F,
1085 * const SymmetricTensor<2,dim,NumberType> &b_bar) const
1087 * return material->get_Psi(det_F,b_bar);
1092 * Here are the kinetic variables. These are used in the material and
1093 * global tangent matrix and residual assembly operations:
1094 * First is the Kirchhoff stress:
1097 * SymmetricTensor<2,dim,NumberType>
1098 * get_tau(const NumberType &det_F,
1099 * const SymmetricTensor<2,dim,NumberType> &b_bar) const
1101 * return material->get_tau(det_F,b_bar);
1109 * SymmetricTensor<4,dim,NumberType>
1110 * get_Jc(const NumberType &det_F,
1111 * const SymmetricTensor<2,dim,NumberType> &b_bar) const
1113 * return material->get_Jc(det_F,b_bar);
1118 * In terms of member functions, this class stores for the quadrature
1119 * point it represents a copy of a material type in case different
1120 * materials are used in different regions of the domain, as well as the
1121 * inverse of the deformation gradient...
1125 * std::shared_ptr< Material_Compressible_Neo_Hook_One_Field<dim,NumberType> > material;
1132 * <a name="cook_membrane.cc-Quasistaticcompressiblefinitestrainsolid"></a>
1133 * <h3>Quasi-static compressible finite-strain solid</h3>
1137 * Forward declarations for classes that will
1138 * perform assembly of the linear system.
1141 * template <int dim,typename NumberType>
1142 * struct Assembler_Base;
1143 * template <int dim,typename NumberType>
1148 * The Solid class is the central class in that it represents the problem at
1149 * hand. It follows the usual scheme in that all it really has is a
1150 * constructor, destructor and a <code>run()</code> function that dispatches
1151 * all the work to private functions of this class:
1154 * template <int dim,typename NumberType>
1158 * Solid(const Parameters::AllParameters ¶meters);
1170 * We start the collection of member functions with one that builds the
1179 * Set up the finite element system to be solved:
1187 * Several functions to assemble the system and right hand side matrices
1188 * using multithreading. Each of them comes as a wrapper function, one
1189 * that is executed to do the work in the WorkStream model on one cell,
1190 * and one that copies the work done on this one cell into the global
1191 * object that represents it:
1195 * assemble_system(const BlockVector<double> &solution_delta);
1199 * We use a separate data structure to perform the assembly. It needs access
1200 * to some low-level data, so we simply befriend the class instead of
1201 * creating a complex interface to provide access as necessary.
1204 * friend struct Assembler_Base<dim,NumberType>;
1205 * friend struct Assembler<dim,NumberType>;
1209 * Apply Dirichlet boundary conditions on the displacement field
1213 * make_constraints(const int &it_nr);
1217 * Create and update the quadrature points. Here, no data needs to be
1218 * copied into a global object, so the copy_local_to_global function is
1227 * Solve for the displacement using a Newton-Raphson method. We break this
1228 * function into the nonlinear loop and the function that solves the
1229 * linearized Newton-Raphson step:
1233 * solve_nonlinear_timestep(BlockVector<double> &solution_delta);
1235 * std::pair<unsigned int, double>
1236 * solve_linear_system(BlockVector<double> &newton_update);
1240 * Solution retrieval as well as post-processing and writing data to file :
1243 * BlockVector<double>
1244 * get_total_solution(const BlockVector<double> &solution_delta) const;
1247 * output_results() const;
1251 * Finally, some member variables that describe the current state: A
1252 * collection of the parameters used to describe the problem setup...
1255 * const Parameters::AllParameters ¶meters;
1259 * ...the volume of the reference and current configurations...
1262 * double vol_reference;
1263 * double vol_current;
1267 * ...and description of the geometry on which the problem is solved:
1270 * Triangulation<dim> triangulation;
1274 * Also, keep track of the current time and the time spent evaluating
1279 * TimerOutput timer;
1283 * A storage object for quadrature point information. As opposed to
1284 * @ref step_18 "step-18", deal.II's native quadrature
point data manager is employed here.
1288 * PointHistory<dim,NumberType> > quadrature_point_history;
1292 * A description of the finite-element system including the displacement
1293 * polynomial degree, the degree-of-freedom handler, number of DoFs per
1294 * cell and the extractor objects used to retrieve information from the
1298 *
const unsigned int degree;
1301 *
const unsigned int dofs_per_cell;
1306 * Description of how the block-system is arranged. There is just 1 block,
1307 * that contains a vector DOF @f$\mathbf{u}@f$.
1308 * There are two reasons that we retain the block system in
this problem.
1309 * The
first is pure laziness to perform further modifications to the
1310 * code from which
this work originated. The
second is that a block system
1311 * would typically necessary when extending
this code to multiphysics
1315 *
static const unsigned int n_blocks = 1;
1316 *
static const unsigned int n_components = dim;
1317 *
static const unsigned int first_u_component = 0;
1324 * std::vector<types::global_dof_index> dofs_per_block;
1328 * Rules
for Gauss-quadrature on both the cell and faces. The number of
1329 * quadrature points on both cells and faces is recorded.
1333 *
const QGauss<dim - 1> qf_face;
1334 *
const unsigned int n_q_points;
1335 *
const unsigned int n_q_points_f;
1339 * Objects that store the converged solution and right-hand side vectors,
1341 * to keep track of constraints. We make use of a sparsity pattern
1342 * designed
for a block system.
1353 * Then define a number of variables to store norms and update norms and
1354 * normalisation factors.
1369 *
void normalise(
const Errors &rhs)
1371 *
if (rhs.norm != 0.0)
1380 * Errors error_residual, error_residual_0, error_residual_norm, error_update,
1381 * error_update_0, error_update_norm;
1385 * Methods to calculate error measures
1389 * get_error_residual(Errors &error_residual);
1393 * Errors &error_update);
1397 * Print information to screen in a pleasing way...
1402 * print_conv_header();
1405 * print_conv_footer();
1408 * print_vertical_tip_displacement();
1414 * <a name=
"cook_membrane.cc-ImplementationofthecodeSolidcodeclass"></a>
1415 * <h3>Implementation of the <code>Solid</code>
class</h3>
1420 * <a name=
"cook_membrane.cc-Publicinterface"></a>
1421 * <h4>Public interface</h4>
1425 * We initialise the Solid
class using data extracted from the parameter file.
1428 *
template <
int dim,
typename NumberType>
1429 * Solid<dim,NumberType>::Solid(
const Parameters::AllParameters ¶meters)
1431 * parameters(parameters),
1432 * vol_reference (0.0),
1433 * vol_current (0.0),
1435 * time(parameters.end_time, parameters.delta_t),
1439 * degree(parameters.poly_degree),
1442 * The Finite Element System is composed of dim continuous displacement
1446 * fe(
FE_Q<dim>(parameters.poly_degree), dim),
1448 * dofs_per_cell (fe.dofs_per_cell),
1449 * u_fe(first_u_component),
1450 * dofs_per_block(n_blocks),
1451 * qf_cell(parameters.quad_order),
1452 * qf_face(parameters.quad_order),
1453 * n_q_points (qf_cell.size()),
1454 * n_q_points_f (qf_face.size())
1461 * The
class destructor simply clears the data held by the DOFHandler
1464 *
template <
int dim,
typename NumberType>
1465 * Solid<dim,NumberType>::~Solid()
1467 * dof_handler_ref.
clear();
1473 * In solving the quasi-
static problem, the time becomes a loading parameter,
1474 * i.e. we increasing the loading linearly with time, making the two
concepts
1475 * interchangeable. We choose to increment time linearly
using a
constant time
1480 * We start the function with preprocessing, and then output the
initial grid
1481 * before starting the simulation proper with the
first time (and loading)
1488 *
template <
int dim,
typename NumberType>
1489 *
void Solid<dim,NumberType>::run()
1498 * We then declare the incremental solution update @f$\varDelta
1499 * \mathbf{\Xi}:= \{\varDelta \mathbf{u}\}@f$ and start the
loop over the
1504 * At the beginning, we reset the solution update
for this time step...
1508 *
while (time.current() <= time.end())
1510 * solution_delta = 0.0;
1514 * ...solve the current time step and update total solution vector
1515 * @f$\mathbf{\Xi}_{\textrm{n}} = \mathbf{\Xi}_{\textrm{n-1}} +
1516 * \varDelta \mathbf{\Xi}@f$...
1519 * solve_nonlinear_timestep(solution_delta);
1520 * solution_n += solution_delta;
1524 * ...and plot the results before moving on happily to the next time
1534 * Lastly, we print the vertical tip displacement of the Cook cantilever
1535 * after the full load is applied
1538 * print_vertical_tip_displacement();
1545 * <a name=
"cook_membrane.cc-Privateinterface"></a>
1546 * <h3>Private interface</h3>
1551 * <a name=
"cook_membrane.cc-Solidmake_grid"></a>
1552 * <h4>Solid::make_grid</h4>
1556 * On to the
first of the
private member
functions. Here we create the
1557 *
triangulation of the domain,
for which we choose a scaled an anisotripically
1558 * discretised rectangle which is subsequently transformed into the correct
1559 * of the Cook cantilever. Each relevant boundary face is then given a boundary
1564 * We then determine the
volume of the reference configuration and print it
1571 *
template <
int dim>
1574 *
const double &x = pt_in[0];
1575 *
const double &y = pt_in[1];
1577 *
const double y_upper = 44.0 + (16.0/48.0)*x;
1578 *
const double y_lower = 0.0 + (44.0/48.0)*x;
1579 *
const double theta = y/44.0;
1580 *
const double y_transform = (1-theta)*y_lower + theta*y_upper;
1583 * pt_out[1] = y_transform;
1588 *
template <
int dim,
typename NumberType>
1589 *
void Solid<dim,NumberType>::make_grid()
1593 * Divide the beam, but only along the x- and y-coordinate directions
1596 * std::vector< unsigned int > repetitions(dim, parameters.elements_per_edge);
1599 * Only allow one element through the thickness
1600 * (modelling a plane strain condition)
1604 * repetitions[dim-1] = 1;
1616 * Since we wish to apply a Neumann BC to the right-hand surface, we
1617 * must find the cell faces in
this part of the domain and mark them with
1618 * a distinct boundary ID number. The faces we are looking
for are on the
1619 * +x surface and will get boundary ID 11.
1620 * Dirichlet boundaries exist on the left-hand face of the beam (
this fixed
1621 * boundary will get ID 1) and on the +Z and -Z faces (which correspond to
1622 * ID 2 and we will use to impose the plane strain condition)
1625 *
const double tol_boundary = 1
e-6;
1628 *
for (; cell != endc; ++cell)
1629 *
for (
unsigned int face = 0;
1630 * face < GeometryInfo<dim>::faces_per_cell; ++face)
1631 *
if (cell->face(face)->at_boundary() ==
true)
1633 *
if (
std::abs(cell->face(face)->center()[0] - 0.0) < tol_boundary)
1634 * cell->face(face)->set_boundary_id(1);
1635 *
else if (
std::abs(cell->face(face)->center()[0] - 48.0) < tol_boundary)
1636 * cell->face(face)->set_boundary_id(11);
1637 *
else if (dim == 3 &&
std::abs(
std::abs(cell->face(face)->center()[2]) - 0.5) < tol_boundary)
1638 * cell->face(face)->set_boundary_id(2);
1643 * Transform the hyper-rectangle into the beam shape
1651 * vol_current = vol_reference;
1652 * std::cout <<
"Grid:\n\t Reference volume: " << vol_reference << std::endl;
1659 * <a name=
"cook_membrane.cc-Solidsystem_setup"></a>
1660 * <h4>Solid::system_setup</h4>
1664 * Next we describe how the FE system is setup. We
first determine the number
1665 * of components per block. Since the displacement is a vector component, the
1666 *
first dim components belong to it.
1669 *
template <
int dim,
typename NumberType>
1670 *
void Solid<dim,NumberType>::system_setup()
1672 * timer.enter_subsection(
"Setup system");
1674 * std::vector<unsigned int> block_component(n_components, u_dof);
1678 * The DOF handler is then initialised and we renumber the grid in an
1679 * efficient manner. We also record the number of DOFs per block.
1682 * dof_handler_ref.distribute_dofs(fe);
1687 * std::cout <<
"Triangulation:"
1689 * <<
"\n\t Number of degrees of freedom: " << dof_handler_ref.n_dofs()
1694 * Setup the sparsity pattern and tangent
matrix
1697 * tangent_matrix.clear();
1703 * csp.block(u_dof, u_dof).reinit(n_dofs_u, n_dofs_u);
1704 * csp.collect_sizes();
1708 * Naturally,
for a one-field vector-valued problem, all of the
1709 * components of the system are coupled.
1713 *
for (
unsigned int ii = 0; ii < n_components; ++ii)
1714 *
for (
unsigned int jj = 0; jj < n_components; ++jj)
1721 * sparsity_pattern.copy_from(csp);
1724 * tangent_matrix.reinit(sparsity_pattern);
1728 * We then
set up storage vectors
1731 * system_rhs.reinit(dofs_per_block);
1732 * system_rhs.collect_sizes();
1734 * solution_n.reinit(dofs_per_block);
1735 * solution_n.collect_sizes();
1739 * ...and
finally set up the quadrature
1745 * timer.leave_subsection();
1752 * <a name=
"cook_membrane.cc-Solidsetup_qph"></a>
1753 * <h4>Solid::setup_qph</h4>
1754 * The method used to store quadrature information is already described in
1755 * @ref step_18
"step-18" and @ref step_44
"step-44". Here we implement a similar setup
for a SMP machine.
1759 * Firstly the actual QPH data objects are created. This must be done only
1760 * once the grid is refined to its finest
level.
1763 *
template <
int dim,
typename NumberType>
1764 *
void Solid<dim,NumberType>::setup_qph()
1766 * std::cout <<
" Setting up quadrature point data..." << std::endl;
1774 * Next we setup the
initial quadrature
point data. Note that when
1775 * the quadrature
point data is retrieved, it is returned as a vector
1776 * of smart pointers.
1782 *
const std::vector<std::shared_ptr<PointHistory<dim,NumberType> > > lqph =
1783 * quadrature_point_history.get_data(cell);
1784 *
Assert(lqph.size() == n_q_points, ExcInternalError());
1786 *
for (
unsigned int q_point = 0; q_point < n_q_points; ++q_point)
1787 * lqph[q_point]->setup_lqp(parameters);
1795 * <a name=
"cook_membrane.cc-Solidsolve_nonlinear_timestep"></a>
1796 * <h4>Solid::solve_nonlinear_timestep</h4>
1800 * The next function is the driver method
for the Newton-Raphson scheme. At
1801 * its top we create a
new vector to store the current Newton update step,
1802 * reset the error storage objects and print solver header.
1805 *
template <
int dim,
typename NumberType>
1809 * std::cout << std::endl <<
"Timestep " << time.get_timestep() <<
" @ "
1810 * << time.current() <<
"s" << std::endl;
1814 * error_residual.reset();
1815 * error_residual_0.reset();
1816 * error_residual_norm.reset();
1817 * error_update.reset();
1818 * error_update_0.reset();
1819 * error_update_norm.reset();
1821 * print_conv_header();
1825 * We now perform a number of Newton iterations to iteratively solve the
1826 * nonlinear problem. Since the problem is fully nonlinear and we are
1827 *
using a full Newton method, the data stored in the tangent
matrix and
1828 * right-hand side vector is not reusable and must be cleared at each
1829 * Newton step. We then initially build the right-hand side vector to
1830 *
check for convergence (and store
this value in the
first iteration).
1831 * The
unconstrained DOFs of the rhs vector hold the out-of-balance
1832 * forces. The building is done before assembling the system
matrix as the
1833 * latter is an expensive operation and we can potentially avoid an extra
1834 * assembly process by not assembling the tangent
matrix when convergence
1838 *
unsigned int newton_iteration = 0;
1839 *
for (; newton_iteration < parameters.max_iterations_NR;
1840 * ++newton_iteration)
1842 * std::cout <<
" " << std::setw(2) << newton_iteration <<
" " << std::flush;
1846 * If we have decided that we want to
continue with the iteration, we
1847 *
assemble the tangent, make and impose the Dirichlet constraints,
1848 * and
do the solve of the linearized system:
1851 * make_constraints(newton_iteration);
1852 * assemble_system(solution_delta);
1854 * get_error_residual(error_residual);
1856 *
if (newton_iteration == 0)
1857 * error_residual_0 = error_residual;
1861 * We can now determine the normalised residual error and
check for
1862 * solution convergence:
1865 * error_residual_norm = error_residual;
1866 * error_residual_norm.normalise(error_residual_0);
1868 *
if (newton_iteration > 0 && error_update_norm.u <= parameters.tol_u
1869 * && error_residual_norm.u <= parameters.tol_f)
1871 * std::cout <<
" CONVERGED! " << std::endl;
1872 * print_conv_footer();
1877 *
const std::pair<unsigned int, double>
1878 * lin_solver_output = solve_linear_system(newton_update);
1880 * get_error_update(newton_update, error_update);
1881 *
if (newton_iteration == 0)
1882 * error_update_0 = error_update;
1886 * We can now determine the normalised Newton update error, and
1887 * perform the actual update of the solution increment
for the current
1888 * time step, update all quadrature
point information pertaining to
1889 *
this new displacement and stress state and
continue iterating:
1892 * error_update_norm = error_update;
1893 * error_update_norm.normalise(error_update_0);
1895 * solution_delta += newton_update;
1897 * std::cout <<
" | " << std::fixed << std::setprecision(3) << std::setw(7)
1898 * << std::scientific << lin_solver_output.first <<
" "
1899 * << lin_solver_output.second <<
" " << error_residual_norm.
norm
1900 * <<
" " << error_residual_norm.u <<
" "
1901 * <<
" " << error_update_norm.norm <<
" " << error_update_norm.u
1902 * <<
" " << std::endl;
1907 * At the
end,
if it turns out that we have in fact done more iterations
1908 * than the parameter file allowed, we raise an exception that can be
1909 * caught in the main() function. The call <code>
AssertThrow(condition,
1910 * exc_object)</code> is in essence equivalent to <code>if (!cond) throw
1911 * exc_object;</code> but the former form fills certain fields in the
1912 * exception
object that identify the location (filename and line number)
1913 * where the exception was raised to make it simpler to identify where the
1917 *
AssertThrow (newton_iteration <= parameters.max_iterations_NR,
1918 * ExcMessage("No convergence in nonlinear solver!"));
1925 * <a name="cook_membrane.cc-Solidprint_conv_headerSolidprint_conv_footerandSolidprint_vertical_tip_displacement"></a>
1926 * <h4>Solid::print_conv_header, Solid::print_conv_footer and Solid::print_vertical_tip_displacement</h4>
1930 * This program prints out data in a nice table that is updated
1931 * on a per-iteration basis. The next two functions set up the table
1932 * header and footer:
1935 * template <
int dim,typename NumberType>
1936 *
void Solid<dim,NumberType>::print_conv_header()
1938 *
static const unsigned int l_width = 87;
1940 *
for (
unsigned int i = 0; i < l_width; ++i)
1942 * std::cout << std::endl;
1944 * std::cout <<
" SOLVER STEP "
1945 * <<
" | LIN_IT LIN_RES RES_NORM "
1946 * <<
" RES_U NU_NORM "
1947 * <<
" NU_U " << std::endl;
1949 *
for (
unsigned int i = 0; i < l_width; ++i)
1951 * std::cout << std::endl;
1956 *
template <
int dim,
typename NumberType>
1957 *
void Solid<dim,NumberType>::print_conv_footer()
1959 *
static const unsigned int l_width = 87;
1961 *
for (
unsigned int i = 0; i < l_width; ++i)
1963 * std::cout << std::endl;
1965 * std::cout <<
"Relative errors:" << std::endl
1966 * <<
"Displacement:\t" << error_update.u / error_update_0.u << std::endl
1967 * <<
"Force: \t\t" << error_residual.u / error_residual_0.u << std::endl
1968 * <<
"v / V_0:\t" << vol_current <<
" / " << vol_reference
1974 * At the
end we also output the result that can be compared to that found in
1975 * the literature, namely the displacement at the upper right corner of the
1979 *
template <
int dim,
typename NumberType>
1980 *
void Solid<dim,NumberType>::print_vertical_tip_displacement()
1982 *
static const unsigned int l_width = 87;
1984 *
for (
unsigned int i = 0; i < l_width; ++i)
1986 * std::cout << std::endl;
1990 * The measurement
point, as stated in the reference paper, is at the midway
1991 *
point of the surface on which the traction is applied.
1995 *
Point<dim>(48.0*parameters.scale, 52.0*parameters.scale, 0.5*parameters.scale) :
1997 *
double vertical_tip_displacement = 0.0;
1998 *
double vertical_tip_displacement_check = 0.0;
2001 * dof_handler_ref.begin_active(), endc = dof_handler_ref.end();
2002 *
for (; cell != endc; ++cell)
2006 *
if (cell->point_inside(soln_pt) ==
true)
2009 *
for (
unsigned int v=0; v<GeometryInfo<dim>::vertices_per_cell; ++v)
2010 *
if (cell->vertex(v).distance(soln_pt) < 1e-6)
2014 * Extract y-component of solution at the given
point
2015 * This
point is coindicent with a vertex, so we can
2016 *
extract it directly as we
're using FE_Q finite elements
2017 * that have support at the vertices
2020 * vertical_tip_displacement = solution_n(cell->vertex_dof_index(v,u_dof+1));
2024 * Sanity check using alternate method to extract the solution
2025 * at the given point. To do this, we must create an FEValues instance
2026 * to help us extract the solution value at the desired point
2029 * const MappingQ<dim> mapping (parameters.poly_degree);
2030 * const Point<dim> qp_unit = mapping.transform_real_to_unit_cell(cell,soln_pt);
2031 * const Quadrature<dim> soln_qrule (qp_unit);
2032 * AssertThrow(soln_qrule.size() == 1, ExcInternalError());
2033 * FEValues<dim> fe_values_soln (fe, soln_qrule, update_values);
2034 * fe_values_soln.reinit(cell);
2038 * Extract y-component of solution at given point
2041 * std::vector< Tensor<1,dim> > soln_values (soln_qrule.size());
2042 * fe_values_soln[u_fe].get_function_values(solution_n,
2044 * vertical_tip_displacement_check = soln_values[0][u_dof+1];
2049 * AssertThrow(vertical_tip_displacement > 0.0, ExcMessage("Found no cell with point inside!"));
2051 * std::cout << "Vertical tip displacement: " << vertical_tip_displacement
2052 * << "\t Check: " << vertical_tip_displacement_check
2060 * <a name="cook_membrane.cc-Solidget_error_residual"></a>
2061 * <h4>Solid::get_error_residual</h4>
2065 * Determine the true residual error for the problem. That is, determine the
2066 * error in the residual for the unconstrained degrees of freedom. Note that to
2067 * do so, we need to ignore constrained DOFs by setting the residual in these
2068 * vector components to zero.
2071 * template <int dim,typename NumberType>
2072 * void Solid<dim,NumberType>::get_error_residual(Errors &error_residual)
2074 * BlockVector<double> error_res(dofs_per_block);
2076 * for (unsigned int i = 0; i < dof_handler_ref.n_dofs(); ++i)
2077 * if (!constraints.is_constrained(i))
2078 * error_res(i) = system_rhs(i);
2080 * error_residual.norm = error_res.l2_norm();
2081 * error_residual.u = error_res.block(u_dof).l2_norm();
2088 * <a name="cook_membrane.cc-Solidget_error_udpate"></a>
2089 * <h4>Solid::get_error_udpate</h4>
2093 * Determine the true Newton update error for the problem
2096 * template <int dim,typename NumberType>
2097 * void Solid<dim,NumberType>::get_error_update(const BlockVector<double> &newton_update,
2098 * Errors &error_update)
2100 * BlockVector<double> error_ud(dofs_per_block);
2101 * for (unsigned int i = 0; i < dof_handler_ref.n_dofs(); ++i)
2102 * if (!constraints.is_constrained(i))
2103 * error_ud(i) = newton_update(i);
2105 * error_update.norm = error_ud.l2_norm();
2106 * error_update.u = error_ud.block(u_dof).l2_norm();
2114 * <a name="cook_membrane.cc-Solidget_total_solution"></a>
2115 * <h4>Solid::get_total_solution</h4>
2119 * This function provides the total solution, which is valid at any Newton step.
2120 * This is required as, to reduce computational error, the total solution is
2121 * only updated at the end of the timestep.
2124 * template <int dim,typename NumberType>
2125 * BlockVector<double>
2126 * Solid<dim,NumberType>::get_total_solution(const BlockVector<double> &solution_delta) const
2128 * BlockVector<double> solution_total(solution_n);
2129 * solution_total += solution_delta;
2130 * return solution_total;
2137 * <a name="cook_membrane.cc-Solidassemble_system"></a>
2138 * <h4>Solid::assemble_system</h4>
2144 * template <int dim,typename NumberType>
2145 * struct Assembler_Base
2147 * virtual ~Assembler_Base() {}
2151 * Here we deal with the tangent matrix assembly structures. The
2152 * PerTaskData object stores local contributions.
2155 * struct PerTaskData_ASM
2157 * const Solid<dim,NumberType> *solid;
2158 * FullMatrix<double> cell_matrix;
2159 * Vector<double> cell_rhs;
2160 * std::vector<types::global_dof_index> local_dof_indices;
2162 * PerTaskData_ASM(const Solid<dim,NumberType> *solid)
2165 * cell_matrix(solid->dofs_per_cell, solid->dofs_per_cell),
2166 * cell_rhs(solid->dofs_per_cell),
2167 * local_dof_indices(solid->dofs_per_cell)
2172 * cell_matrix = 0.0;
2179 * On the other hand, the ScratchData object stores the larger objects such as
2180 * the shape-function values array (<code>Nx</code>) and a shape function
2181 * gradient and symmetric gradient vector which we will use during the
2185 * struct ScratchData_ASM
2187 * const BlockVector<double> &solution_total;
2188 * std::vector<Tensor<2, dim,NumberType> > solution_grads_u_total;
2190 * FEValues<dim> fe_values_ref;
2191 * FEFaceValues<dim> fe_face_values_ref;
2193 * std::vector<std::vector<Tensor<2, dim,NumberType> > > grad_Nx;
2194 * std::vector<std::vector<SymmetricTensor<2,dim,NumberType> > > symm_grad_Nx;
2196 * ScratchData_ASM(const FiniteElement<dim> &fe_cell,
2197 * const QGauss<dim> &qf_cell,
2198 * const UpdateFlags uf_cell,
2199 * const QGauss<dim-1> & qf_face,
2200 * const UpdateFlags uf_face,
2201 * const BlockVector<double> &solution_total)
2203 * solution_total(solution_total),
2204 * solution_grads_u_total(qf_cell.size()),
2205 * fe_values_ref(fe_cell, qf_cell, uf_cell),
2206 * fe_face_values_ref(fe_cell, qf_face, uf_face),
2207 * grad_Nx(qf_cell.size(),
2208 * std::vector<Tensor<2,dim,NumberType> >(fe_cell.dofs_per_cell)),
2209 * symm_grad_Nx(qf_cell.size(),
2210 * std::vector<SymmetricTensor<2,dim,NumberType> >
2211 * (fe_cell.dofs_per_cell))
2214 * ScratchData_ASM(const ScratchData_ASM &rhs)
2216 * solution_total (rhs.solution_total),
2217 * solution_grads_u_total(rhs.solution_grads_u_total),
2218 * fe_values_ref(rhs.fe_values_ref.get_fe(),
2219 * rhs.fe_values_ref.get_quadrature(),
2220 * rhs.fe_values_ref.get_update_flags()),
2221 * fe_face_values_ref(rhs.fe_face_values_ref.get_fe(),
2222 * rhs.fe_face_values_ref.get_quadrature(),
2223 * rhs.fe_face_values_ref.get_update_flags()),
2224 * grad_Nx(rhs.grad_Nx),
2225 * symm_grad_Nx(rhs.symm_grad_Nx)
2230 * const unsigned int n_q_points = fe_values_ref.get_quadrature().size();
2231 * const unsigned int n_dofs_per_cell = fe_values_ref.dofs_per_cell;
2232 * for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
2234 * Assert( grad_Nx[q_point].size() == n_dofs_per_cell,
2235 * ExcInternalError());
2236 * Assert( symm_grad_Nx[q_point].size() == n_dofs_per_cell,
2237 * ExcInternalError());
2239 * solution_grads_u_total[q_point] = Tensor<2,dim,NumberType>();
2240 * for (unsigned int k = 0; k < n_dofs_per_cell; ++k)
2242 * grad_Nx[q_point][k] = Tensor<2,dim,NumberType>();
2243 * symm_grad_Nx[q_point][k] = SymmetricTensor<2,dim,NumberType>();
2252 * Of course, we still have to define how we assemble the tangent matrix
2253 * contribution for a single cell.
2257 * assemble_system_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
2258 * ScratchData_ASM &scratch,
2259 * PerTaskData_ASM &data)
2263 * Due to the C++ specialization rules, we need one more
2264 * level of indirection in order to define the assembly
2265 * routine for all different number. The next function call
2266 * is specialized for each NumberType, but to prevent having
2267 * to specialize the whole class along with it we have inlined
2268 * the definition of the other functions that are common to
2269 * all implementations.
2272 * assemble_system_tangent_residual_one_cell(cell, scratch, data);
2273 * assemble_neumann_contribution_one_cell(cell, scratch, data);
2278 * This function adds the local contribution to the system matrix.
2282 * copy_local_to_global_ASM(const PerTaskData_ASM &data)
2284 * const AffineConstraints<double> &constraints = data.solid->constraints;
2285 * BlockSparseMatrix<double> &tangent_matrix = const_cast<Solid<dim,NumberType> *>(data.solid)->tangent_matrix;
2286 * BlockVector<double> &system_rhs = const_cast<Solid<dim,NumberType> *>(data.solid)->system_rhs;
2288 * constraints.distribute_local_to_global(
2289 * data.cell_matrix, data.cell_rhs,
2290 * data.local_dof_indices,
2291 * tangent_matrix, system_rhs);
2298 * This function needs to exist in the base class for
2299 * Workstream to work with a reference to the base class.
2303 * assemble_system_tangent_residual_one_cell(const typename DoFHandler<dim>::active_cell_iterator &/*cell*/,
2304 * ScratchData_ASM &/*scratch*/,
2305 * PerTaskData_ASM &/*data*/)
2307 * AssertThrow(false, ExcPureFunctionCalled());
2311 * assemble_neumann_contribution_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
2312 * ScratchData_ASM &scratch,
2313 * PerTaskData_ASM &data)
2317 * Aliases for data referenced from the Solid class
2320 * const unsigned int &n_q_points_f = data.solid->n_q_points_f;
2321 * const unsigned int &dofs_per_cell = data.solid->dofs_per_cell;
2322 * const Parameters::AllParameters ¶meters = data.solid->parameters;
2323 * const Time &time = data.solid->time;
2324 * const FESystem<dim> &fe = data.solid->fe;
2325 * const unsigned int &u_dof = data.solid->u_dof;
2329 * Next we assemble the Neumann contribution. We first check to see it the
2330 * cell face exists on a boundary on which a traction is applied and add
2331 * the contribution if this is the case.
2334 * for (unsigned int face = 0; face < GeometryInfo<dim>::faces_per_cell;
2336 * if (cell->face(face)->at_boundary() == true
2337 * && cell->face(face)->boundary_id() == 11)
2339 * scratch.fe_face_values_ref.reinit(cell, face);
2341 * for (unsigned int f_q_point = 0; f_q_point < n_q_points_f;
2346 * We specify the traction in reference configuration.
2347 * For this problem, a defined total vertical force is applied
2348 * in the reference configuration.
2349 * The direction of the applied traction is assumed not to
2350 * evolve with the deformation of the domain.
2354 * Note that the contributions to the right hand side vector we
2355 * compute here only exist in the displacement components of the
2359 * const double time_ramp = (time.current() / time.end());
2360 * const double magnitude = (1.0/(16.0*parameters.scale*1.0*parameters.scale))*time_ramp; // (Total force) / (RHS surface area)
2361 * Tensor<1,dim> dir;
2363 * const Tensor<1, dim> traction = magnitude*dir;
2365 * for (unsigned int i = 0; i < dofs_per_cell; ++i)
2367 * const unsigned int i_group =
2368 * fe.system_to_base_index(i).first.first;
2370 * if (i_group == u_dof)
2372 * const unsigned int component_i =
2373 * fe.system_to_component_index(i).first;
2375 * scratch.fe_face_values_ref.shape_value(i,
2377 * const double JxW = scratch.fe_face_values_ref.JxW(
2380 * data.cell_rhs(i) += (Ni * traction[component_i])
2390 * template <int dim>
2391 * struct Assembler<dim,double> : Assembler_Base<dim,double>
2393 * typedef double NumberType;
2394 * using typename Assembler_Base<dim,NumberType>::ScratchData_ASM;
2395 * using typename Assembler_Base<dim,NumberType>::PerTaskData_ASM;
2397 * virtual ~Assembler() {}
2400 * assemble_system_tangent_residual_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
2401 * ScratchData_ASM &scratch,
2402 * PerTaskData_ASM &data) override
2406 * Aliases for data referenced from the Solid class
2409 * const unsigned int &n_q_points = data.solid->n_q_points;
2410 * const unsigned int &dofs_per_cell = data.solid->dofs_per_cell;
2411 * const FESystem<dim> &fe = data.solid->fe;
2412 * const unsigned int &u_dof = data.solid->u_dof;
2413 * const FEValuesExtractors::Vector &u_fe = data.solid->u_fe;
2417 * scratch.fe_values_ref.reinit(cell);
2418 * cell->get_dof_indices(data.local_dof_indices);
2420 * const std::vector<std::shared_ptr<const PointHistory<dim,NumberType> > > lqph =
2421 * const_cast<const Solid<dim,NumberType> *>(data.solid)->quadrature_point_history.get_data(cell);
2422 * Assert(lqph.size() == n_q_points, ExcInternalError());
2426 * We first need to find the solution gradients at quadrature points
2427 * inside the current cell and then we update each local QP using the
2428 * displacement gradient:
2431 * scratch.fe_values_ref[u_fe].get_function_gradients(scratch.solution_total,
2432 * scratch.solution_grads_u_total);
2436 * Now we build the local cell stiffness matrix. Since the global and
2437 * local system matrices are symmetric, we can exploit this property by
2438 * building only the lower half of the local matrix and copying the values
2439 * to the upper half.
2443 * In doing so, we first extract some configuration dependent variables
2444 * from our QPH history objects for the current quadrature point.
2447 * for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
2449 * const Tensor<2,dim,NumberType> &grad_u = scratch.solution_grads_u_total[q_point];
2450 * const Tensor<2,dim,NumberType> F = Physics::Elasticity::Kinematics::F(grad_u);
2451 * const NumberType det_F = determinant(F);
2452 * const Tensor<2,dim,NumberType> F_bar = Physics::Elasticity::Kinematics::F_iso(F);
2453 * const SymmetricTensor<2,dim,NumberType> b_bar = Physics::Elasticity::Kinematics::b(F_bar);
2454 * const Tensor<2,dim,NumberType> F_inv = invert(F);
2455 * Assert(det_F > NumberType(0.0), ExcInternalError());
2457 * for (unsigned int k = 0; k < dofs_per_cell; ++k)
2459 * const unsigned int k_group = fe.system_to_base_index(k).first.first;
2461 * if (k_group == u_dof)
2463 * scratch.grad_Nx[q_point][k] = scratch.fe_values_ref[u_fe].gradient(k, q_point) * F_inv;
2464 * scratch.symm_grad_Nx[q_point][k] = symmetrize(scratch.grad_Nx[q_point][k]);
2467 * Assert(k_group <= u_dof, ExcInternalError());
2470 * const SymmetricTensor<2,dim,NumberType> tau = lqph[q_point]->get_tau(det_F,b_bar);
2471 * const SymmetricTensor<4,dim,NumberType> Jc = lqph[q_point]->get_Jc(det_F,b_bar);
2472 * const Tensor<2,dim,NumberType> tau_ns (tau);
2476 * Next we define some aliases to make the assembly process easier to
2480 * const std::vector<SymmetricTensor<2, dim> > &symm_grad_Nx = scratch.symm_grad_Nx[q_point];
2481 * const std::vector<Tensor<2, dim> > &grad_Nx = scratch.grad_Nx[q_point];
2482 * const double JxW = scratch.fe_values_ref.JxW(q_point);
2484 * for (unsigned int i = 0; i < dofs_per_cell; ++i)
2486 * const unsigned int component_i = fe.system_to_component_index(i).first;
2487 * const unsigned int i_group = fe.system_to_base_index(i).first.first;
2489 * if (i_group == u_dof)
2490 * data.cell_rhs(i) -= (symm_grad_Nx[i] * tau) * JxW;
2492 * Assert(i_group <= u_dof, ExcInternalError());
2494 * for (unsigned int j = 0; j <= i; ++j)
2496 * const unsigned int component_j = fe.system_to_component_index(j).first;
2497 * const unsigned int j_group = fe.system_to_base_index(j).first.first;
2501 * This is the @f$\mathsf{\mathbf{k}}_{\mathbf{u} \mathbf{u}}@f$
2502 * contribution. It comprises a material contribution, and a
2503 * geometrical stress contribution which is only added along
2504 * the local matrix diagonals:
2507 * if ((i_group == j_group) && (i_group == u_dof))
2509 * data.cell_matrix(i, j) += symm_grad_Nx[i] * Jc // The material contribution:
2510 * * symm_grad_Nx[j] * JxW;
2511 * if (component_i == component_j) // geometrical stress contribution
2512 * data.cell_matrix(i, j) += grad_Nx[i][component_i] * tau_ns
2513 * * grad_Nx[j][component_j] * JxW;
2516 * Assert((i_group <= u_dof) && (j_group <= u_dof),
2517 * ExcInternalError());
2525 * Finally, we need to copy the lower half of the local matrix into the
2529 * for (unsigned int i = 0; i < dofs_per_cell; ++i)
2530 * for (unsigned int j = i + 1; j < dofs_per_cell; ++j)
2531 * data.cell_matrix(i, j) = data.cell_matrix(j, i);
2536 * #ifdef ENABLE_SACADO_FORMULATION
2539 * template <int dim>
2540 * struct Assembler<dim,Sacado::Fad::DFad<double> > : Assembler_Base<dim,Sacado::Fad::DFad<double> >
2542 * typedef Sacado::Fad::DFad<double> ADNumberType;
2543 * using typename Assembler_Base<dim,ADNumberType>::ScratchData_ASM;
2544 * using typename Assembler_Base<dim,ADNumberType>::PerTaskData_ASM;
2546 * virtual ~Assembler() {}
2549 * assemble_system_tangent_residual_one_cell(const typename DoFHandler<dim>::active_cell_iterator &cell,
2550 * ScratchData_ASM &scratch,
2551 * PerTaskData_ASM &data) override
2555 * Aliases for data referenced from the Solid class
2558 * const unsigned int &n_q_points = data.solid->n_q_points;
2559 * const unsigned int &dofs_per_cell = data.solid->dofs_per_cell;
2560 * const FESystem<dim> &fe = data.solid->fe;
2561 * const unsigned int &u_dof = data.solid->u_dof;
2562 * const FEValuesExtractors::Vector &u_fe = data.solid->u_fe;
2566 * scratch.fe_values_ref.reinit(cell);
2567 * cell->get_dof_indices(data.local_dof_indices);
2569 * const std::vector<std::shared_ptr<const PointHistory<dim,ADNumberType> > > lqph =
2570 * const_cast<const Solid<dim,ADNumberType> *>(data.solid)->quadrature_point_history.get_data(cell);
2571 * Assert(lqph.size() == n_q_points, ExcInternalError());
2573 * const unsigned int n_independent_variables = data.local_dof_indices.size();
2574 * std::vector<double> local_dof_values(n_independent_variables);
2575 * cell->get_dof_values(scratch.solution_total,
2576 * local_dof_values.begin(),
2577 * local_dof_values.end());
2581 * We now retrieve a set of degree-of-freedom values that
2582 * have the operations that are performed with them tracked.
2585 * std::vector<ADNumberType> local_dof_values_ad (n_independent_variables);
2586 * for (unsigned int i=0; i<n_independent_variables; ++i)
2587 * local_dof_values_ad[i] = ADNumberType(n_independent_variables, i, local_dof_values[i]);
2591 * Compute all values, gradients etc. based on sensitive
2592 * AD degree-of-freedom values.
2595 * scratch.fe_values_ref[u_fe].get_function_gradients_from_local_dof_values(
2596 * local_dof_values_ad,
2597 * scratch.solution_grads_u_total);
2601 * Accumulate the residual value for each degree of freedom.
2602 * Note: Its important that the vectors is initialised (zero'd) correctly.
2605 * std::vector<ADNumberType> residual_ad (dofs_per_cell, ADNumberType(0.0));
2606 *
for (
unsigned int q_point = 0; q_point < n_q_points; ++q_point)
2614 *
Assert(det_F > ADNumberType(0.0), ExcInternalError());
2616 *
for (
unsigned int k = 0; k < dofs_per_cell; ++k)
2618 *
const unsigned int k_group = fe.system_to_base_index(k).first.first;
2620 *
if (k_group == u_dof)
2622 * scratch.grad_Nx[q_point][k] = scratch.fe_values_ref[u_fe].gradient(k, q_point) * F_inv;
2623 * scratch.symm_grad_Nx[q_point][k] =
symmetrize(scratch.grad_Nx[q_point][k]);
2626 *
Assert(k_group <= u_dof, ExcInternalError());
2633 * Next we define some position-dependent aliases, again to
2634 * make the assembly process easier to follow.
2637 *
const std::vector<SymmetricTensor<2, dim,ADNumberType> > &symm_grad_Nx = scratch.symm_grad_Nx[q_point];
2638 *
const double JxW = scratch.fe_values_ref.JxW(q_point);
2640 *
for (
unsigned int i = 0; i < dofs_per_cell; ++i)
2642 *
const unsigned int i_group = fe.system_to_base_index(i).first.first;
2644 *
if (i_group == u_dof)
2645 * residual_ad[i] += (symm_grad_Nx[i] * tau) * JxW;
2647 *
Assert(i_group <= u_dof, ExcInternalError());
2651 *
for (
unsigned int I=0; I<n_independent_variables; ++I)
2653 *
const ADNumberType &residual_I = residual_ad[I];
2654 * data.cell_rhs(I) = -residual_I.val();
2655 *
for (
unsigned int J=0; J<n_independent_variables; ++J)
2659 * Compute the
gradients of the residual entry [forward-mode]
2662 * data.cell_matrix(I,J) = residual_I.dx(J);
2670 *
template <
int dim>
2671 *
struct Assembler<dim,Sacado::Rad::ADvar<Sacado::Fad::DFad<double> > > : Assembler_Base<dim,Sacado::Rad::ADvar<Sacado::Fad::DFad<double> > >
2673 *
typedef Sacado::Fad::DFad<double> ADDerivType;
2674 *
typedef Sacado::Rad::ADvar<ADDerivType> ADNumberType;
2675 *
using typename Assembler_Base<dim,ADNumberType>::ScratchData_ASM;
2676 *
using typename Assembler_Base<dim,ADNumberType>::PerTaskData_ASM;
2678 *
virtual ~Assembler() {}
2682 * ScratchData_ASM &scratch,
2683 * PerTaskData_ASM &data)
override
2687 * Aliases
for data referenced from the Solid
class
2690 *
const unsigned int &n_q_points = data.solid->n_q_points;
2695 * scratch.fe_values_ref.reinit(cell);
2696 * cell->get_dof_indices(data.local_dof_indices);
2698 *
const std::vector<std::shared_ptr<const PointHistory<dim,ADNumberType> > > lqph =
2699 * data.solid->quadrature_point_history.get_data(cell);
2700 *
Assert(lqph.size() == n_q_points, ExcInternalError());
2702 *
const unsigned int n_independent_variables = data.local_dof_indices.size();
2703 * std::vector<double> local_dof_values(n_independent_variables);
2704 * cell->get_dof_values(scratch.solution_total,
2705 * local_dof_values.begin(),
2706 * local_dof_values.end());
2710 * We now retrieve a
set of degree-of-freedom values that
2711 * have the operations that are performed with them tracked.
2714 * std::vector<ADNumberType> local_dof_values_ad (n_independent_variables);
2715 *
for (
unsigned int i=0; i<n_independent_variables; ++i)
2716 * local_dof_values_ad[i] = ADDerivType(n_independent_variables, i, local_dof_values[i]);
2720 * Compute all values,
gradients etc. based on sensitive
2721 * AD degree-of-freedom values.
2724 * scratch.fe_values_ref[u_fe].get_function_gradients_from_local_dof_values(
2725 * local_dof_values_ad,
2726 * scratch.solution_grads_u_total);
2730 * Next we compute the total potential energy of the element.
2731 * This is defined as follows:
2732 * Total energy = (
internal - external) energies
2733 * Note: Its important that this
value is initialised (zero
'd) correctly.
2736 * ADNumberType cell_energy_ad = ADNumberType(0.0);
2737 * for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
2739 * const Tensor<2,dim,ADNumberType> &grad_u = scratch.solution_grads_u_total[q_point];
2740 * const Tensor<2,dim,ADNumberType> F = Physics::Elasticity::Kinematics::F(grad_u);
2741 * const ADNumberType det_F = determinant(F);
2742 * const Tensor<2,dim,ADNumberType> F_bar = Physics::Elasticity::Kinematics::F_iso(F);
2743 * const SymmetricTensor<2,dim,ADNumberType> b_bar = Physics::Elasticity::Kinematics::b(F_bar);
2744 * Assert(det_F > ADNumberType(0.0), ExcInternalError());
2748 * Next we define some position-dependent aliases, again to
2749 * make the assembly process easier to follow.
2752 * const double JxW = scratch.fe_values_ref.JxW(q_point);
2754 * const ADNumberType Psi = lqph[q_point]->get_Psi(det_F,b_bar);
2758 * We extract the configuration-dependent material energy
2759 * from our QPH history objects for the current quadrature point
2760 * and integrate its contribution to increment the total
2764 * cell_energy_ad += Psi * JxW;
2769 * Compute derivatives of reverse-mode AD variables
2772 * ADNumberType::Gradcomp();
2774 * for (unsigned int I=0; I<n_independent_variables; ++I)
2778 * This computes the adjoint df/dX_{i} [reverse-mode]
2781 * const ADDerivType residual_I = local_dof_values_ad[I].adj();
2782 * data.cell_rhs(I) = -residual_I.val(); // RHS = - residual
2783 * for (unsigned int J=0; J<n_independent_variables; ++J)
2787 * Compute the gradients of the residual entry [forward-mode]
2790 * data.cell_matrix(I,J) = residual_I.dx(J); // linearisation_IJ
2803 * Since we use TBB for assembly, we simply setup a copy of the
2804 * data structures required for the process and pass them, along
2805 * with the memory addresses of the assembly functions to the
2806 * WorkStream object for processing. Note that we must ensure that
2807 * the matrix is reset before any assembly operations can occur.
2810 * template <int dim,typename NumberType>
2811 * void Solid<dim,NumberType>::assemble_system(const BlockVector<double> &solution_delta)
2813 * timer.enter_subsection("Assemble linear system");
2814 * std::cout << " ASM " << std::flush;
2816 * tangent_matrix = 0.0;
2819 * const UpdateFlags uf_cell(update_gradients |
2820 * update_JxW_values);
2821 * const UpdateFlags uf_face(update_values |
2822 * update_JxW_values);
2824 * const BlockVector<double> solution_total(get_total_solution(solution_delta));
2825 * typename Assembler_Base<dim,NumberType>::PerTaskData_ASM per_task_data(this);
2826 * typename Assembler_Base<dim,NumberType>::ScratchData_ASM scratch_data(fe, qf_cell, uf_cell, qf_face, uf_face, solution_total);
2827 * Assembler<dim,NumberType> assembler;
2829 * WorkStream::run(dof_handler_ref.begin_active(),
2830 * dof_handler_ref.end(),
2831 * static_cast<Assembler_Base<dim,NumberType>&>(assembler),
2832 * &Assembler_Base<dim,NumberType>::assemble_system_one_cell,
2833 * &Assembler_Base<dim,NumberType>::copy_local_to_global_ASM,
2837 * timer.leave_subsection();
2844 * <a name="cook_membrane.cc-Solidmake_constraints"></a>
2845 * <h4>Solid::make_constraints</h4>
2846 * The constraints for this problem are simple to describe.
2847 * However, since we are dealing with an iterative Newton method,
2848 * it should be noted that any displacement constraints should only
2849 * be specified at the zeroth iteration and subsequently no
2850 * additional contributions are to be made since the constraints
2851 * are already exactly satisfied.
2854 * template <int dim,typename NumberType>
2855 * void Solid<dim,NumberType>::make_constraints(const int &it_nr)
2857 * std::cout << " CST " << std::flush;
2861 * Since the constraints are different at different Newton iterations, we
2862 * need to clear the constraints matrix and completely rebuild
2863 * it. However, after the first iteration, the constraints remain the same
2864 * and we can simply skip the rebuilding step if we do not clear it.
2869 * const bool apply_dirichlet_bc = (it_nr == 0);
2873 * The boundary conditions for the indentation problem are as follows: On
2874 * the -x face (ID = 1), we set up a zero-displacement condition, -y and +y traction
2875 * free boundary condition (don't need to take care); -z and +z faces (ID = 2) are
2876 * not allowed to move along z axis so that it is a plane strain problem.
2877 * Finally, as described earlier, +x face (ID = 11) has an the applied
2878 * distributed shear force (converted by total force per unit area) which
2879 * needs to be taken care as an inhomogeneous Newmann boundary condition.
2883 * In the following, we will have to tell the function interpolation
2884 * boundary values which components of the solution vector should be
2885 * constrained (i.e., whether it's the x-, y-, z-displacements or
2886 * combinations thereof). This is done using
ComponentMask objects (see
2887 * @ref GlossComponentMask) which we can get from the finite element if we
2888 * provide it with an extractor
object for the component we wish to
2889 * select. To this end we
first set up such extractor objects and later
2890 * use it when generating the relevant component masks:
2896 * if (apply_dirichlet_bc)
2898 * constraints.
clear();
2902 * Fixed left hand side of the beam
2911 * fe.component_mask(u_fe));
2916 * Zero Z-displacement through thickness direction
2917 * This corresponds to a plane strain condition being imposed on the beam
2928 * fe.component_mask(z_displacement));
2933 *
if (constraints.has_inhomogeneities())
2936 *
for (
unsigned int dof = 0; dof != dof_handler_ref.n_dofs(); ++dof)
2937 *
if (homogeneous_constraints.is_inhomogeneously_constrained(dof))
2938 * homogeneous_constraints.set_inhomogeneity(dof, 0.0);
2939 * constraints.clear();
2940 * constraints.copy_from(homogeneous_constraints);
2944 * constraints.close();
2950 * <a name=
"cook_membrane.cc-Solidsolve_linear_system"></a>
2951 * <h4>Solid::solve_linear_system</h4>
2952 * As the system is composed of a single block, defining a solution scheme
2953 *
for the linear problem is straight-forward.
2956 *
template <
int dim,
typename NumberType>
2957 * std::pair<unsigned int, double>
2963 *
unsigned int lin_it = 0;
2964 *
double lin_res = 0.0;
2968 * We solve
for the incremental displacement @f$d\mathbf{u}@f$.
2972 * timer.enter_subsection(
"Linear solver");
2973 * std::cout <<
" SLV " << std::flush;
2974 *
if (parameters.type_lin ==
"CG")
2976 *
const int solver_its =
static_cast<unsigned int>(
2977 * tangent_matrix.block(u_dof, u_dof).m()
2978 * * parameters.max_iterations_lin);
2979 *
const double tol_sol = parameters.tol_lin
2980 * * system_rhs.block(u_dof).l2_norm();
2989 * We
've chosen by default a SSOR preconditioner as it appears to
2990 * provide the fastest solver convergence characteristics for this
2991 * problem on a single-thread machine. However, for multicore
2992 * computing, the Jacobi preconditioner which is multithreaded may
2993 * converge quicker for larger linear systems.
2996 * PreconditionSelector<SparseMatrix<double>, Vector<double> >
2997 * preconditioner (parameters.preconditioner_type,
2998 * parameters.preconditioner_relaxation);
2999 * preconditioner.use_matrix(tangent_matrix.block(u_dof, u_dof));
3001 * solver_CG.solve(tangent_matrix.block(u_dof, u_dof),
3002 * newton_update.block(u_dof),
3003 * system_rhs.block(u_dof),
3006 * lin_it = solver_control.last_step();
3007 * lin_res = solver_control.last_value();
3009 * else if (parameters.type_lin == "Direct")
3013 * Otherwise if the problem is small
3014 * enough, a direct solver can be
3018 * SparseDirectUMFPACK A_direct;
3019 * A_direct.initialize(tangent_matrix.block(u_dof, u_dof));
3020 * A_direct.vmult(newton_update.block(u_dof), system_rhs.block(u_dof));
3026 * Assert (false, ExcMessage("Linear solver type not implemented"));
3028 * timer.leave_subsection();
3033 * Now that we have the displacement update, distribute the constraints
3034 * back to the Newton update:
3037 * constraints.distribute(newton_update);
3039 * return std::make_pair(lin_it, lin_res);
3045 * <a name="cook_membrane.cc-Solidoutput_results"></a>
3046 * <h4>Solid::output_results</h4>
3047 * Here we present how the results are written to file to be viewed
3048 * using ParaView or Visit. The method is similar to that shown in the
3049 * tutorials so will not be discussed in detail.
3052 * template <int dim,typename NumberType>
3053 * void Solid<dim,NumberType>::output_results() const
3055 * DataOut<dim> data_out;
3056 * std::vector<DataComponentInterpretation::DataComponentInterpretation>
3057 * data_component_interpretation(dim,
3058 * DataComponentInterpretation::component_is_part_of_vector);
3060 * std::vector<std::string> solution_name(dim, "displacement");
3062 * data_out.attach_dof_handler(dof_handler_ref);
3063 * data_out.add_data_vector(solution_n,
3065 * DataOut<dim>::type_dof_data,
3066 * data_component_interpretation);
3070 * Since we are dealing with a large deformation problem, it would be nice
3071 * to display the result on a displaced grid! The MappingQEulerian class
3072 * linked with the DataOut class provides an interface through which this
3073 * can be achieved without physically moving the grid points in the
3074 * Triangulation object ourselves. We first need to copy the solution to
3075 * a temporary vector and then create the Eulerian mapping. We also
3076 * specify the polynomial degree to the DataOut object in order to produce
3077 * a more refined output data set when higher order polynomials are used.
3080 * Vector<double> soln(solution_n.size());
3081 * for (unsigned int i = 0; i < soln.size(); ++i)
3082 * soln(i) = solution_n(i);
3083 * MappingQEulerian<dim> q_mapping(degree, dof_handler_ref, soln);
3084 * data_out.build_patches(q_mapping, degree);
3086 * std::ostringstream filename;
3087 * filename << "solution-" << time.get_timestep() << ".vtk";
3089 * std::ofstream output(filename.str().c_str());
3090 * data_out.write_vtk(output);
3099 * <a name="cook_membrane.cc-Mainfunction"></a>
3100 * <h3>Main function</h3>
3101 * Lastly we provide the main driver function which appears
3102 * no different to the other tutorials.
3105 * int main (int argc, char *argv[])
3107 * using namespace dealii;
3108 * using namespace Cook_Membrane;
3110 * const unsigned int dim = 3;
3113 * deallog.depth_console(0);
3114 * Parameters::AllParameters parameters("parameters.prm");
3115 * if (parameters.automatic_differentiation_order == 0)
3117 * std::cout << "Assembly method: Residual and linearisation are computed manually." << std::endl;
3121 * Allow multi-threading
3124 * Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv,
3125 * ::numbers::invalid_unsigned_int);
3127 * typedef double NumberType;
3128 * Solid<dim,NumberType> solid_3d(parameters);
3131 * #ifdef ENABLE_SACADO_FORMULATION
3132 * else if (parameters.automatic_differentiation_order == 1)
3134 * std::cout << "Assembly method: Residual computed manually; linearisation performed using AD." << std::endl;
3138 * Allow multi-threading
3141 * Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv,
3142 * ::numbers::invalid_unsigned_int);
3144 * typedef Sacado::Fad::DFad<double> NumberType;
3145 * Solid<dim,NumberType> solid_3d(parameters);
3148 * else if (parameters.automatic_differentiation_order == 2)
3150 * std::cout << "Assembly method: Residual and linearisation computed using AD." << std::endl;
3154 * Sacado Rad-Fad is not thread-safe, so disable threading.
3155 * Parallelisation using MPI would be possible though.
3158 * Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv,
3161 * typedef Sacado::Rad::ADvar< Sacado::Fad::DFad<double> > NumberType;
3162 * Solid<dim,NumberType> solid_3d(parameters);
3168 * AssertThrow(false,
3169 * ExcMessage("The selected assembly method is not supported. "
3170 * "You need deal.II 9.0 and Trilinos with the Sacado package "
3171 * "to enable assembly using automatic differentiation."));
3174 * catch (std::exception &exc)
3176 * std::cerr << std::endl << std::endl
3177 * << "----------------------------------------------------"
3179 * std::cerr << "Exception on processing: " << std::endl << exc.what()
3180 * << std::endl << "Aborting!" << std::endl
3181 * << "----------------------------------------------------"
3188 * std::cerr << std::endl << std::endl
3189 * << "----------------------------------------------------"
3191 * std::cerr << "Unknown exception!" << std::endl << "Aborting!"
3193 * << "----------------------------------------------------"
numbers::NumberTraits< Number >::real_type norm() const
unsigned int n_active_cells() const
cell_iterator end() const
active_cell_iterator begin_active(const unsigned int level=0) const
virtual void clear() override
#define DEAL_II_VERSION_MAJOR
__global__ void set(Number *val, const Number s, const size_type N)
#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())
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)
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 >())
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)
@ matrix
Contents is actually a matrix.
double norm(const FEValuesBase< dim > &fe, const ArrayView< const std::vector< Tensor< 1, dim > > > &Du)
std::enable_if_t< IsBlockVector< VectorType >::value, unsigned int > n_blocks(const VectorType &vector)
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
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)
constexpr ReturnType< rank, T >::value_type & extract(T &t, const ArrayType &indices)
VectorType::value_type * end(VectorType &V)
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)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
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 > &)