deal.II version GIT relicensing-6809-ge913b9bb34 2026-09-25 17:20:01+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
step-42.h
Go to the documentation of this file.
1
1634 *   {
1635 *   TimerOutput::Scope t(computing_timer, "Setup: distribute DoFs");
1636 *   dof_handler.distribute_dofs(fe);
1637 *  
1638 *   locally_owned_dofs = dof_handler.locally_owned_dofs();
1639 *   locally_relevant_dofs =
1641 *   }
1642 *  
1643 *   /* setup hanging nodes and Dirichlet constraints */
1644 *   {
1645 *   TimerOutput::Scope t(computing_timer, "Setup: constraints");
1646 *   constraints_hanging_nodes.reinit(locally_owned_dofs,
1647 *   locally_relevant_dofs);
1649 *   constraints_hanging_nodes);
1650 *   constraints_hanging_nodes.close();
1651 *  
1652 *   pcout << " Number of active cells: "
1653 *   << triangulation.n_global_active_cells() << std::endl
1654 *   << " Number of degrees of freedom: " << dof_handler.n_dofs()
1655 *   << std::endl;
1656 *  
1657 *   compute_dirichlet_constraints();
1658 *   }
1659 *  
1660 *   /* initialization of vectors and the active set */
1661 *   {
1662 *   TimerOutput::Scope t(computing_timer, "Setup: vectors");
1663 *   solution.reinit(locally_relevant_dofs, mpi_communicator);
1664 *   newton_rhs.reinit(locally_owned_dofs, mpi_communicator);
1665 *   newton_rhs_uncondensed.reinit(locally_owned_dofs, mpi_communicator);
1666 *   diag_mass_matrix_vector.reinit(locally_owned_dofs, mpi_communicator);
1667 *   fraction_of_plastic_q_points_per_cell.reinit(
1668 *   triangulation.n_active_cells());
1669 *  
1670 *   active_set.clear();
1671 *   active_set.set_size(dof_handler.n_dofs());
1672 *   }
1673 *  
1674 * @endcode
1675 *
1676 * Finally, we set up sparsity patterns and matrices.
1677 * We temporarily (ab)use the system matrix to also build the (diagonal)
1678 * matrix that we use in eliminating degrees of freedom that are in contact
1679 * with the obstacle, but we then immediately set the Newton matrix back
1680 * to zero.
1681 *
1682 * @code
1683 *   {
1684 *   TimerOutput::Scope t(computing_timer, "Setup: matrix");
1685 *   DynamicSparsityPattern dsp(locally_relevant_dofs);
1686 *  
1687 *   DoFTools::make_sparsity_pattern(dof_handler,
1688 *   dsp,
1689 *   constraints_dirichlet_and_hanging_nodes,
1690 *   false,
1692 *   MPI_COMM_WORLD));
1694 *   locally_owned_dofs,
1695 *   MPI_COMM_WORLD,
1696 *   locally_relevant_dofs);
1697 *  
1698 *   newton_matrix.reinit(locally_owned_dofs,
1699 *   locally_owned_dofs,
1700 *   dsp,
1701 *   MPI_COMM_WORLD);
1702 *  
1703 *  
1705 *  
1706 *   assemble_mass_matrix_diagonal(mass_matrix);
1707 *  
1708 *   const unsigned int start = (newton_rhs.local_range().first),
1709 *   end = (newton_rhs.local_range().second);
1710 *   for (unsigned int j = start; j < end; ++j)
1711 *   diag_mass_matrix_vector(j) = mass_matrix.diag_element(j);
1712 *   diag_mass_matrix_vector.compress(VectorOperation::insert);
1713 *  
1714 *   mass_matrix = 0;
1715 *   }
1716 *   }
1717 *  
1718 *  
1719 * @endcode
1720 *
1721 *
1722 * <a name="step_42-PlasticityContactProblemcompute_dirichlet_constraints"></a>
1723 * <h4>PlasticityContactProblem::compute_dirichlet_constraints</h4>
1724 *
1725
1726 *
1727 * This function, broken out of the preceding one, computes the constraints
1728 * associated with Dirichlet-type boundary conditions and puts them into the
1729 * <code>constraints_dirichlet_and_hanging_nodes</code> variable by merging
1730 * with the constraints that come from hanging nodes.
1731 *
1732
1733 *
1734 * As laid out in the introduction, we need to distinguish between two
1735 * cases:
1736 * - If the domain is a box, we set the displacement to zero at the bottom,
1737 * and allow vertical movement in z-direction along the sides. As
1738 * shown in the <code>make_grid()</code> function, the former corresponds
1739 * to boundary indicator 6, the latter to 8.
1740 * - If the domain is a half sphere, then we impose zero displacement along
1741 * the curved part of the boundary, associated with boundary indicator zero.
1742 *
1743 * @code
1744 *   template <int dim>
1745 *   void PlasticityContactProblem<dim>::compute_dirichlet_constraints()
1746 *   {
1747 *   constraints_dirichlet_and_hanging_nodes.reinit(locally_owned_dofs,
1748 *   locally_relevant_dofs);
1749 *   constraints_dirichlet_and_hanging_nodes.merge(constraints_hanging_nodes);
1750 *  
1751 *   if (base_mesh == "box")
1752 *   {
1753 * @endcode
1754 *
1755 * interpolate all components of the solution
1756 *
1757 * @code
1759 *   dof_handler,
1760 *   6,
1761 *   EquationData::BoundaryValues<dim>(),
1762 *   constraints_dirichlet_and_hanging_nodes,
1763 *   ComponentMask());
1764 *  
1765 * @endcode
1766 *
1767 * interpolate x- and y-components of the
1768 * solution (this is a bit mask, so apply
1769 * operator| )
1770 *
1771 * @code
1772 *   const FEValuesExtractors::Scalar x_displacement(0);
1773 *   const FEValuesExtractors::Scalar y_displacement(1);
1775 *   dof_handler,
1776 *   8,
1777 *   EquationData::BoundaryValues<dim>(),
1778 *   constraints_dirichlet_and_hanging_nodes,
1779 *   (fe.component_mask(x_displacement) |
1780 *   fe.component_mask(y_displacement)));
1781 *   }
1782 *   else
1784 *   dof_handler,
1785 *   0,
1786 *   EquationData::BoundaryValues<dim>(),
1787 *   constraints_dirichlet_and_hanging_nodes,
1788 *   ComponentMask());
1789 *  
1790 *   constraints_dirichlet_and_hanging_nodes.close();
1791 *   }
1792 *  
1793 *  
1794 *  
1795 * @endcode
1796 *
1797 *
1798 * <a name="step_42-PlasticityContactProblemassemble_mass_matrix_diagonal"></a>
1799 * <h4>PlasticityContactProblem::assemble_mass_matrix_diagonal</h4>
1800 *
1801
1802 *
1803 * The next helper function computes the (diagonal) @ref GlossMassMatrix "mass matrix" that
1804 * is used to determine the active set of the active set method we use in
1805 * the contact algorithm. This matrix is of mass matrix type, but unlike
1806 * the standard mass matrix, we can make it diagonal (even in the case of
1807 * higher order elements) by using a quadrature formula that has its
1808 * quadrature points at exactly the same locations as the interpolation points
1809 * for the finite element are located. We achieve this by using a
1810 * QGaussLobatto quadrature formula here, along with initializing the finite
1811 * element with a set of interpolation points derived from the same quadrature
1812 * formula. The remainder of the function is relatively straightforward: we
1813 * put the resulting matrix into the given argument; because we know the
1814 * matrix is diagonal, it is sufficient to have a loop over only @f$i@f$ and
1815 * not over @f$j@f$. Strictly speaking, we could even avoid multiplying the
1816 * shape function's values at quadrature point <code>q_point</code> by itself
1817 * because we know the shape value to be a vector with exactly one one which
1818 * when dotted with itself yields one. Since this function is not time
1819 * critical we add this term for clarity.
1820 *
1821 * @code
1822 *   template <int dim>
1823 *   void PlasticityContactProblem<dim>::assemble_mass_matrix_diagonal(
1824 *   TrilinosWrappers::SparseMatrix &mass_matrix)
1825 *   {
1826 *   const QGaussLobatto<dim - 1> face_quadrature_formula(fe.degree + 1);
1827 *  
1828 *   FEFaceValues<dim> fe_values_face(fe,
1829 *   face_quadrature_formula,
1830 *   update_values | update_JxW_values);
1831 *  
1832 *   const unsigned int dofs_per_cell = fe.n_dofs_per_cell();
1833 *   const unsigned int n_face_q_points = face_quadrature_formula.size();
1834 *  
1835 *   FullMatrix<double> cell_matrix(dofs_per_cell, dofs_per_cell);
1836 *   std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
1837 *  
1838 *   const FEValuesExtractors::Vector displacement(0);
1839 *  
1840 *   for (const auto &cell : dof_handler.active_cell_iterators())
1841 *   if (cell->is_locally_owned())
1842 *   for (const auto &face : cell->face_iterators())
1843 *   if (face->at_boundary() && face->boundary_id() == 1)
1844 *   {
1845 *   fe_values_face.reinit(cell, face);
1846 *   cell_matrix = 0;
1847 *  
1848 *   for (unsigned int q_point = 0; q_point < n_face_q_points;
1849 *   ++q_point)
1850 *   for (unsigned int i = 0; i < dofs_per_cell; ++i)
1851 *   cell_matrix(i, i) +=
1852 *   (fe_values_face[displacement].value(i, q_point) *
1853 *   fe_values_face[displacement].value(i, q_point) *
1854 *   fe_values_face.JxW(q_point));
1855 *  
1856 *   cell->get_dof_indices(local_dof_indices);
1857 *  
1858 *   for (unsigned int i = 0; i < dofs_per_cell; ++i)
1859 *   mass_matrix.add(local_dof_indices[i],
1860 *   local_dof_indices[i],
1861 *   cell_matrix(i, i));
1862 *   }
1863 *   mass_matrix.compress(VectorOperation::add);
1864 *   }
1865 *  
1866 *  
1867 * @endcode
1868 *
1869 *
1870 * <a name="step_42-PlasticityContactProblemupdate_solution_and_constraints"></a>
1871 * <h4>PlasticityContactProblem::update_solution_and_constraints</h4>
1872 *
1873
1874 *
1875 * The following function is the first function we call in each Newton
1876 * iteration in the <code>solve_newton()</code> function. What it does is
1877 * to project the solution onto the feasible set and update the active set
1878 * for the degrees of freedom that touch or penetrate the obstacle.
1879 *
1880
1881 *
1882 * In order to function, we first need to do some bookkeeping: We need
1883 * to write into the solution vector (which we can only do with fully
1884 * distributed vectors without ghost elements) and we need to read
1885 * the Lagrange multiplier and the elements of the diagonal mass matrix
1886 * from their respective vectors (which we can only do with vectors that
1887 * do have ghost elements), so we create the respective vectors. We then
1888 * also initialize the constraints object that will contain constraints
1889 * from contact and all other sources, as well as an object that contains
1890 * an index set of all locally owned degrees of freedom that are part of
1891 * the contact:
1892 *
1893 * @code
1894 *   template <int dim>
1895 *   void PlasticityContactProblem<dim>::update_solution_and_constraints()
1896 *   {
1897 *   std::vector<bool> dof_touched(dof_handler.n_dofs(), false);
1898 *  
1899 *   TrilinosWrappers::MPI::Vector distributed_solution(locally_owned_dofs,
1900 *   mpi_communicator);
1901 *   distributed_solution = solution;
1902 *  
1903 *   TrilinosWrappers::MPI::Vector lambda(locally_relevant_dofs,
1904 *   mpi_communicator);
1905 *   lambda = newton_rhs_uncondensed;
1906 *  
1907 *   TrilinosWrappers::MPI::Vector diag_mass_matrix_vector_relevant(
1908 *   locally_relevant_dofs, mpi_communicator);
1909 *   diag_mass_matrix_vector_relevant = diag_mass_matrix_vector;
1910 *  
1911 *  
1912 *   all_constraints.reinit(locally_owned_dofs, locally_relevant_dofs);
1913 *   active_set.clear();
1914 *  
1915 * @endcode
1916 *
1917 * The second part is a loop over all cells in which we look at each
1918 * point where a degree of freedom is defined whether the active set
1919 * condition is true and we need to add this degree of freedom to
1920 * the active set of contact nodes. As we always do, if we want to
1921 * evaluate functions at individual points, we do this with an
1922 * FEValues object (or, here, an FEFaceValues object since we need to
1923 * check contact at the surface) with an appropriately chosen quadrature
1924 * object. We create this face quadrature object by choosing the
1925 * "support points" of the shape functions defined on the faces
1926 * of cells (for more on support points, see this
1927 * @ref GlossSupport "glossary entry"). As a consequence, we have as
1928 * many quadrature points as there are shape functions per face and
1929 * looping over quadrature points is equivalent to looping over shape
1930 * functions defined on a face. With this, the code looks as follows:
1931 *
1932 * @code
1933 *   const Quadrature<dim - 1> face_quadrature(
1934 *   fe.get_unit_face_support_points());
1935 *   FEFaceValues<dim> fe_values_face(fe,
1936 *   face_quadrature,
1937 *   update_quadrature_points);
1938 *  
1939 *   const unsigned int dofs_per_face = fe.n_dofs_per_face();
1940 *   const unsigned int n_face_q_points = face_quadrature.size();
1941 *  
1942 *   std::vector<types::global_dof_index> dof_indices(dofs_per_face);
1943 *  
1944 *   for (const auto &cell : dof_handler.active_cell_iterators())
1945 *   if (!cell->is_artificial())
1946 *   for (const auto &face : cell->face_iterators())
1947 *   if (face->at_boundary() && face->boundary_id() == 1)
1948 *   {
1949 *   fe_values_face.reinit(cell, face);
1950 *   face->get_dof_indices(dof_indices);
1951 *  
1952 *   for (unsigned int q_point = 0; q_point < n_face_q_points;
1953 *   ++q_point)
1954 *   {
1955 * @endcode
1956 *
1957 * At each quadrature point (i.e., at each support point of a
1958 * degree of freedom located on the contact boundary), we then
1959 * ask whether it is part of the @f$z@f$-displacement degrees of
1960 * freedom and if we haven't encountered this degree of
1961 * freedom yet (which can happen for those on the edges
1962 * between faces), we need to evaluate the gap between the
1963 * deformed object and the obstacle. If the active set
1964 * condition is true, then we add a constraint to the
1965 * AffineConstraints object that the next Newton update needs
1966 * to satisfy, set the solution vector's corresponding element
1967 * to the correct value, and add the index to the IndexSet
1968 * object that stores which degree of freedom is part of the
1969 * contact:
1970 *
1971 * @code
1972 *   const FEValuesExtractors::Scalar z_displacement(2);
1973 *  
1974 *   const unsigned int index_z = dof_indices[q_point];
1975 *  
1976 *   if (fe.shape_function_belongs_to(q_point, z_displacement) &&
1977 *   (dof_touched[index_z] == false))
1978 *   {
1979 *   dof_touched[index_z] = true;
1980 *  
1981 *   const Point<dim> this_support_point =
1982 *   fe_values_face.quadrature_point(q_point);
1983 *  
1984 *   const double obstacle_value =
1985 *   obstacle->value(this_support_point, 2);
1986 *   const double solution_here = solution(index_z);
1987 *   const double undeformed_gap =
1988 *   obstacle_value - this_support_point[2];
1989 *  
1990 *   const double c = 100.0 * e_modulus;
1991 *   if ((lambda(index_z) /
1992 *   diag_mass_matrix_vector_relevant(index_z) +
1993 *   c * (solution_here - undeformed_gap) >
1994 *   0) &&
1995 *   !constraints_hanging_nodes.is_constrained(index_z))
1996 *   {
1997 *   all_constraints.add_constraint(index_z,
1998 *   {},
1999 *   undeformed_gap);
2000 *   distributed_solution(index_z) = undeformed_gap;
2001 *  
2002 *   active_set.add_index(index_z);
2003 *   }
2004 *   }
2005 *   }
2006 *   }
2007 *  
2008 * @endcode
2009 *
2010 * At the end of this function, we exchange data between processors updating
2011 * those ghost elements in the <code>solution</code> variable that have been
2012 * written by other processors. We then merge the Dirichlet constraints and
2013 * those from hanging nodes into the AffineConstraints object that already
2014 * contains the active set. We finish the function by outputting the total
2015 * number of actively constrained degrees of freedom for which we sum over
2016 * the number of actively constrained degrees of freedom owned by each
2017 * of the processors. This number of locally owned constrained degrees of
2018 * freedom is of course the number of elements of the intersection of the
2019 * active set and the set of locally owned degrees of freedom, which
2020 * we can get by using <code>operator&</code> on two IndexSets:
2021 *
2022 * @code
2023 *   distributed_solution.compress(VectorOperation::insert);
2024 *   solution = distributed_solution;
2025 *  
2026 *   all_constraints.close();
2027 *   all_constraints.merge(constraints_dirichlet_and_hanging_nodes);
2028 *  
2029 *   pcout << " Size of active set: "
2030 *   << Utilities::MPI::sum((active_set & locally_owned_dofs).n_elements(),
2031 *   mpi_communicator)
2032 *   << std::endl;
2033 *   }
2034 *  
2035 *  
2036 * @endcode
2037 *
2038 *
2039 * <a name="step_42-PlasticityContactProblemassemble_newton_system"></a>
2040 * <h4>PlasticityContactProblem::assemble_newton_system</h4>
2041 *
2042
2043 *
2044 * Given the complexity of the problem, it may come as a bit of a surprise
2045 * that assembling the linear system we have to solve in each Newton iteration
2046 * is actually fairly straightforward. The following function builds the
2047 * Newton right hand side and Newton matrix. It looks fairly innocent because
2048 * the heavy lifting happens in the call to
2049 * <code>ConstitutiveLaw::get_linearized_stress_strain_tensors()</code> and in
2050 * particular in AffineConstraints::distribute_local_to_global(), using the
2051 * constraints we have previously computed.
2052 *
2053 * @code
2054 *   template <int dim>
2055 *   void PlasticityContactProblem<dim>::assemble_newton_system(
2056 *   const TrilinosWrappers::MPI::Vector &linearization_point)
2057 *   {
2058 *   TimerOutput::Scope t(computing_timer, "Assembling");
2059 *  
2060 *   const QGauss<dim> quadrature_formula(fe.degree + 1);
2061 *   const QGauss<dim - 1> face_quadrature_formula(fe.degree + 1);
2062 *  
2063 *   FEValues<dim> fe_values(fe,
2064 *   quadrature_formula,
2065 *   update_values | update_gradients |
2066 *   update_JxW_values);
2067 *  
2068 *   FEFaceValues<dim> fe_values_face(fe,
2069 *   face_quadrature_formula,
2070 *   update_values | update_quadrature_points |
2071 *   update_JxW_values);
2072 *  
2073 *   const unsigned int dofs_per_cell = fe.n_dofs_per_cell();
2074 *   const unsigned int n_q_points = quadrature_formula.size();
2075 *   const unsigned int n_face_q_points = face_quadrature_formula.size();
2076 *  
2077 *   const EquationData::BoundaryForce<dim> boundary_force;
2078 *   std::vector<Vector<double>> boundary_force_values(n_face_q_points,
2079 *   Vector<double>(dim));
2080 *  
2081 *   FullMatrix<double> cell_matrix(dofs_per_cell, dofs_per_cell);
2082 *   Vector<double> cell_rhs(dofs_per_cell);
2083 *  
2084 *   std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
2085 *  
2086 *   const FEValuesExtractors::Vector displacement(0);
2087 *  
2088 *   for (const auto &cell : dof_handler.active_cell_iterators())
2089 *   if (cell->is_locally_owned())
2090 *   {
2091 *   fe_values.reinit(cell);
2092 *   cell_matrix = 0;
2093 *   cell_rhs = 0;
2094 *  
2095 *   std::vector<SymmetricTensor<2, dim>> strain_tensor(n_q_points);
2096 *   fe_values[displacement].get_function_symmetric_gradients(
2097 *   linearization_point, strain_tensor);
2098 *  
2099 *   for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
2100 *   {
2101 *   SymmetricTensor<4, dim> stress_strain_tensor_linearized;
2102 *   SymmetricTensor<4, dim> stress_strain_tensor;
2103 *   constitutive_law.get_linearized_stress_strain_tensors(
2104 *   strain_tensor[q_point],
2105 *   stress_strain_tensor_linearized,
2106 *   stress_strain_tensor);
2107 *  
2108 *   for (unsigned int i = 0; i < dofs_per_cell; ++i)
2109 *   {
2110 * @endcode
2111 *
2112 * Having computed the stress-strain tensor and its
2113 * linearization, we can now put together the parts of the
2114 * matrix and right hand side. In both, we need the linearized
2115 * stress-strain tensor times the symmetric gradient of
2116 * @f$\varphi_i@f$, i.e. the term @f$I_\Pi\varepsilon(\varphi_i)@f$,
2117 * so we introduce an abbreviation of this term. Recall that
2118 * the matrix corresponds to the bilinear form
2119 * @f$A_{ij}=(I_\Pi\varepsilon(\varphi_i),\varepsilon(\varphi_j))@f$
2120 * in the notation of the accompanying publication, whereas
2121 * the right hand side is @f$F_i=([I_\Pi-P_\Pi
2122 * C]\varepsilon(\varphi_i),\varepsilon(\mathbf u))@f$ where @f$u@f$
2123 * is the current linearization points (typically the last
2124 * solution). This might suggest that the right hand side will
2125 * be zero if the material is completely elastic (where
2126 * @f$I_\Pi=P_\Pi@f$) but this ignores the fact that the right
2127 * hand side will also contain contributions from
2128 * non-homogeneous constraints due to the contact.
2129 *
2130
2131 *
2132 * The code block that follows this adds contributions that
2133 * are due to boundary forces, should there be any.
2134 *
2135 * @code
2136 *   const SymmetricTensor<2, dim> stress_phi_i =
2137 *   stress_strain_tensor_linearized *
2138 *   fe_values[displacement].symmetric_gradient(i, q_point);
2139 *  
2140 *   for (unsigned int j = 0; j < dofs_per_cell; ++j)
2141 *   cell_matrix(i, j) +=
2142 *   (stress_phi_i *
2143 *   fe_values[displacement].symmetric_gradient(j, q_point) *
2144 *   fe_values.JxW(q_point));
2145 *  
2146 *   cell_rhs(i) +=
2147 *   ((stress_phi_i -
2148 *   stress_strain_tensor *
2149 *   fe_values[displacement].symmetric_gradient(i,
2150 *   q_point)) *
2151 *   strain_tensor[q_point] * fe_values.JxW(q_point));
2152 *   }
2153 *   }
2154 *  
2155 *   for (const auto &face : cell->face_iterators())
2156 *   if (face->at_boundary() && face->boundary_id() == 1)
2157 *   {
2158 *   fe_values_face.reinit(cell, face);
2159 *  
2160 *   boundary_force.vector_value_list(
2161 *   fe_values_face.get_quadrature_points(),
2162 *   boundary_force_values);
2163 *  
2164 *   for (unsigned int q_point = 0; q_point < n_face_q_points;
2165 *   ++q_point)
2166 *   {
2167 *   Tensor<1, dim> rhs_values;
2168 *   rhs_values[2] = boundary_force_values[q_point][2];
2169 *   for (unsigned int i = 0; i < dofs_per_cell; ++i)
2170 *   cell_rhs(i) +=
2171 *   (fe_values_face[displacement].value(i, q_point) *
2172 *   rhs_values * fe_values_face.JxW(q_point));
2173 *   }
2174 *   }
2175 *  
2176 *   cell->get_dof_indices(local_dof_indices);
2177 *   all_constraints.distribute_local_to_global(cell_matrix,
2178 *   cell_rhs,
2179 *   local_dof_indices,
2180 *   newton_matrix,
2181 *   newton_rhs,
2182 *   true);
2183 *   }
2184 *  
2185 *   newton_matrix.compress(VectorOperation::add);
2186 *   newton_rhs.compress(VectorOperation::add);
2187 *   }
2188 *  
2189 *  
2190 *  
2191 * @endcode
2192 *
2193 *
2194 * <a name="step_42-PlasticityContactProblemcompute_nonlinear_residual"></a>
2195 * <h4>PlasticityContactProblem::compute_nonlinear_residual</h4>
2196 *
2197
2198 *
2199 * The following function computes the nonlinear residual of the equation
2200 * given the current solution (or any other linearization point). This
2201 * is needed in the linear search algorithm where we need to try various
2202 * linear combinations of previous and current (trial) solution to
2203 * compute the (real, globalized) solution of the current Newton step.
2204 *
2205
2206 *
2207 * That said, in a slight abuse of the name of the function, it actually
2208 * does significantly more. For example, it also computes the vector
2209 * that corresponds to the Newton residual but without eliminating
2210 * constrained degrees of freedom. We need this vector to compute contact
2211 * forces and, ultimately, to compute the next active set. Likewise, by
2212 * keeping track of how many quadrature points we encounter on each cell
2213 * that show plastic yielding, we also compute the
2214 * <code>fraction_of_plastic_q_points_per_cell</code> vector that we
2215 * can later output to visualize the plastic zone. In both of these cases,
2216 * the results are not necessary as part of the line search, and so we may
2217 * be wasting a small amount of time computing them. At the same time, this
2218 * information appears as a natural by-product of what we need to do here
2219 * anyway, and we want to collect it once at the end of each Newton
2220 * step, so we may as well do it here.
2221 *
2222
2223 *
2224 * The actual implementation of this function should be rather obvious:
2225 *
2226 * @code
2227 *   template <int dim>
2228 *   void PlasticityContactProblem<dim>::compute_nonlinear_residual(
2229 *   const TrilinosWrappers::MPI::Vector &linearization_point)
2230 *   {
2231 *   const QGauss<dim> quadrature_formula(fe.degree + 1);
2232 *   const QGauss<dim - 1> face_quadrature_formula(fe.degree + 1);
2233 *  
2234 *   FEValues<dim> fe_values(fe,
2235 *   quadrature_formula,
2236 *   update_values | update_gradients |
2237 *   update_JxW_values);
2238 *  
2239 *   FEFaceValues<dim> fe_values_face(fe,
2240 *   face_quadrature_formula,
2241 *   update_values | update_quadrature_points |
2242 *   update_JxW_values);
2243 *  
2244 *   const unsigned int dofs_per_cell = fe.n_dofs_per_cell();
2245 *   const unsigned int n_q_points = quadrature_formula.size();
2246 *   const unsigned int n_face_q_points = face_quadrature_formula.size();
2247 *  
2248 *   const EquationData::BoundaryForce<dim> boundary_force;
2249 *   std::vector<Vector<double>> boundary_force_values(n_face_q_points,
2250 *   Vector<double>(dim));
2251 *  
2252 *   Vector<double> cell_rhs(dofs_per_cell);
2253 *  
2254 *   std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
2255 *  
2256 *   const FEValuesExtractors::Vector displacement(0);
2257 *  
2258 *   newton_rhs = 0;
2259 *   newton_rhs_uncondensed = 0;
2260 *  
2261 *   fraction_of_plastic_q_points_per_cell = 0;
2262 *  
2263 *   for (const auto &cell : dof_handler.active_cell_iterators())
2264 *   if (cell->is_locally_owned())
2265 *   {
2266 *   fe_values.reinit(cell);
2267 *   cell_rhs = 0;
2268 *  
2269 *   std::vector<SymmetricTensor<2, dim>> strain_tensors(n_q_points);
2270 *   fe_values[displacement].get_function_symmetric_gradients(
2271 *   linearization_point, strain_tensors);
2272 *  
2273 *   for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
2274 *   {
2275 *   SymmetricTensor<4, dim> stress_strain_tensor;
2276 *   const bool q_point_is_plastic =
2277 *   constitutive_law.get_stress_strain_tensor(
2278 *   strain_tensors[q_point], stress_strain_tensor);
2279 *   if (q_point_is_plastic)
2280 *   ++fraction_of_plastic_q_points_per_cell(
2281 *   cell->active_cell_index());
2282 *  
2283 *   for (unsigned int i = 0; i < dofs_per_cell; ++i)
2284 *   {
2285 *   cell_rhs(i) -=
2286 *   (strain_tensors[q_point] * stress_strain_tensor *
2287 *   fe_values[displacement].symmetric_gradient(i, q_point) *
2288 *   fe_values.JxW(q_point));
2289 *  
2290 *   Tensor<1, dim> rhs_values;
2291 *   rhs_values = 0;
2292 *   cell_rhs(i) += (fe_values[displacement].value(i, q_point) *
2293 *   rhs_values * fe_values.JxW(q_point));
2294 *   }
2295 *   }
2296 *  
2297 *   for (const auto &face : cell->face_iterators())
2298 *   if (face->at_boundary() && face->boundary_id() == 1)
2299 *   {
2300 *   fe_values_face.reinit(cell, face);
2301 *  
2302 *   boundary_force.vector_value_list(
2303 *   fe_values_face.get_quadrature_points(),
2304 *   boundary_force_values);
2305 *  
2306 *   for (unsigned int q_point = 0; q_point < n_face_q_points;
2307 *   ++q_point)
2308 *   {
2309 *   Tensor<1, dim> rhs_values;
2310 *   rhs_values[2] = boundary_force_values[q_point][2];
2311 *   for (unsigned int i = 0; i < dofs_per_cell; ++i)
2312 *   cell_rhs(i) +=
2313 *   (fe_values_face[displacement].value(i, q_point) *
2314 *   rhs_values * fe_values_face.JxW(q_point));
2315 *   }
2316 *   }
2317 *  
2318 *   cell->get_dof_indices(local_dof_indices);
2319 *   constraints_dirichlet_and_hanging_nodes.distribute_local_to_global(
2320 *   cell_rhs, local_dof_indices, newton_rhs);
2321 *  
2322 *   for (unsigned int i = 0; i < dofs_per_cell; ++i)
2323 *   newton_rhs_uncondensed(local_dof_indices[i]) += cell_rhs(i);
2324 *   }
2325 *  
2326 *   fraction_of_plastic_q_points_per_cell /= quadrature_formula.size();
2327 *   newton_rhs.compress(VectorOperation::add);
2328 *   newton_rhs_uncondensed.compress(VectorOperation::add);
2329 *   }
2330 *  
2331 *  
2332 *  
2333 * @endcode
2334 *
2335 *
2336 * <a name="step_42-PlasticityContactProblemsolve_newton_system"></a>
2337 * <h4>PlasticityContactProblem::solve_newton_system</h4>
2338 *
2339
2340 *
2341 * The last piece before we can discuss the actual Newton iteration
2342 * on a single mesh is the solver for the linear systems. There are
2343 * a couple of complications that slightly obscure the code, but
2344 * mostly it is just setup then solve. Among the complications are:
2345 *
2346
2347 *
2348 * - For the hanging nodes we have to apply
2349 * the AffineConstraints::set_zero function to newton_rhs.
2350 * This is necessary if a hanging node with solution value @f$x_0@f$
2351 * has one neighbor with value @f$x_1@f$ which is in contact with the
2352 * obstacle and one neighbor @f$x_2@f$ which is not in contact. Because
2353 * the update for the former will be prescribed, the hanging node constraint
2354 * will have an inhomogeneity and will look like @f$x_0 = x_1/2 +
2355 * \text{gap}/2@f$. So the corresponding entries in the right-hand-side are
2356 * non-zero with a meaningless value. These values we have to set to zero.
2357 * - Like in @ref step_40 "step-40", we need to shuffle between vectors that do and do
2358 * not have ghost elements when solving or using the solution.
2359 *
2360
2361 *
2362 * The rest of the function is similar to @ref step_40 "step-40" and
2363 * @ref step_41 "step-41" except that we use a BiCGStab solver
2364 * instead of CG. This is due to the fact that for very small hardening
2365 * parameters @f$\gamma@f$, the linear system becomes almost semidefinite though
2366 * still symmetric. BiCGStab appears to have an easier time with such linear
2367 * systems.
2368 *
2369 * @code
2370 *   template <int dim>
2371 *   void PlasticityContactProblem<dim>::solve_newton_system()
2372 *   {
2373 *   TimerOutput::Scope t(computing_timer, "Solve");
2374 *  
2375 *   TrilinosWrappers::MPI::Vector distributed_solution(locally_owned_dofs,
2376 *   mpi_communicator);
2377 *   distributed_solution = solution;
2378 *  
2379 *   constraints_hanging_nodes.set_zero(distributed_solution);
2380 *   constraints_hanging_nodes.set_zero(newton_rhs);
2381 *  
2382 *   TrilinosWrappers::PreconditionAMG preconditioner;
2383 *   {
2384 *   TimerOutput::Scope t(computing_timer, "Solve: setup preconditioner");
2385 *  
2386 *   const std::vector<std::vector<bool>> constant_modes =
2387 *   DoFTools::extract_constant_modes(dof_handler);
2388 *  
2389 *   TrilinosWrappers::PreconditionAMG::AdditionalData additional_data;
2390 *   additional_data.constant_modes = constant_modes;
2391 *   additional_data.elliptic = true;
2392 *   #ifdef DEAL_II_TRILINOS_WITH_EPETRA
2393 *   additional_data.n_cycles = 1;
2394 *   #endif
2395 *   additional_data.w_cycle = false;
2396 *   additional_data.output_details = false;
2397 *   additional_data.smoother_sweeps = 2;
2398 *   additional_data.aggregation_threshold = 1e-2;
2399 *  
2400 *   preconditioner.initialize(newton_matrix, additional_data);
2401 *   }
2402 *  
2403 *   {
2404 *   TimerOutput::Scope t(computing_timer, "Solve: iterate");
2405 *  
2406 *   TrilinosWrappers::MPI::Vector tmp(locally_owned_dofs, mpi_communicator);
2407 *  
2408 *   const double relative_accuracy = 1e-8;
2409 *   const double solver_tolerance =
2410 *   relative_accuracy *
2411 *   newton_matrix.residual(tmp, distributed_solution, newton_rhs);
2412 *  
2413 *   SolverControl solver_control(newton_matrix.m(), solver_tolerance);
2414 *   SolverBicgstab<TrilinosWrappers::MPI::Vector> solver(solver_control);
2415 *   solver.solve(newton_matrix,
2416 *   distributed_solution,
2417 *   newton_rhs,
2418 *   preconditioner);
2419 *  
2420 *   pcout << " Error: " << solver_control.initial_value() << " -> "
2421 *   << solver_control.last_value() << " in "
2422 *   << solver_control.last_step() << " Bicgstab iterations."
2423 *   << std::endl;
2424 *   }
2425 *  
2426 *   all_constraints.distribute(distributed_solution);
2427 *  
2428 *   solution = distributed_solution;
2429 *   }
2430 *  
2431 *  
2432 * @endcode
2433 *
2434 *
2435 * <a name="step_42-PlasticityContactProblemsolve_newton"></a>
2436 * <h4>PlasticityContactProblem::solve_newton</h4>
2437 *
2438
2439 *
2440 * This is, finally, the function that implements the damped Newton method
2441 * on the current mesh. There are two nested loops: the outer loop for the
2442 * Newton iteration and the inner loop for the line search which will be used
2443 * only if necessary. To obtain a good and reasonable starting value we solve
2444 * an elastic problem in the very first Newton step on each mesh (or only on
2445 * the first mesh if we transfer solutions between meshes). We do so by
2446 * setting the yield stress to an unreasonably large value in these iterations
2447 * and then setting it back to the correct value in subsequent iterations.
2448 *
2449
2450 *
2451 * Other than this, the top part of this function should be
2452 * reasonably obvious. We initialize the variable
2453 * <code>previous_residual_norm</code> to the most negative value
2454 * representable with double precision numbers so that the
2455 * comparison whether the current residual is less than that of the
2456 * previous step will always fail in the first step.
2457 *
2458 * @code
2459 *   template <int dim>
2460 *   void PlasticityContactProblem<dim>::solve_newton()
2461 *   {
2462 *   TrilinosWrappers::MPI::Vector old_solution(locally_owned_dofs,
2463 *   mpi_communicator);
2464 *   TrilinosWrappers::MPI::Vector residual(locally_owned_dofs,
2465 *   mpi_communicator);
2466 *   TrilinosWrappers::MPI::Vector tmp_vector(locally_owned_dofs,
2467 *   mpi_communicator);
2468 *   TrilinosWrappers::MPI::Vector locally_relevant_tmp_vector(
2469 *   locally_relevant_dofs, mpi_communicator);
2470 *   TrilinosWrappers::MPI::Vector distributed_solution(locally_owned_dofs,
2471 *   mpi_communicator);
2472 *  
2473 *   double residual_norm;
2474 *   double previous_residual_norm = std::numeric_limits<double>::lowest();
2475 *  
2476 *   const double correct_sigma = sigma_0;
2477 *  
2478 *   IndexSet old_active_set(active_set);
2479 *  
2480 *   for (unsigned int newton_step = 1; newton_step <= 100; ++newton_step)
2481 *   {
2482 *   if (newton_step == 1 &&
2483 *   ((transfer_solution && current_refinement_cycle == 0) ||
2484 *   !transfer_solution))
2485 *   constitutive_law.set_sigma_0(1e+10);
2486 *   else if (newton_step == 2 || current_refinement_cycle > 0 ||
2487 *   !transfer_solution)
2488 *   constitutive_law.set_sigma_0(correct_sigma);
2489 *  
2490 *   pcout << ' ' << std::endl;
2491 *   pcout << " Newton iteration " << newton_step << std::endl;
2492 *   pcout << " Updating active set..." << std::endl;
2493 *  
2494 *   {
2495 *   TimerOutput::Scope t(computing_timer, "update active set");
2496 *   update_solution_and_constraints();
2497 *   }
2498 *  
2499 *   pcout << " Assembling system... " << std::endl;
2500 *   newton_matrix = 0;
2501 *   newton_rhs = 0;
2502 *   assemble_newton_system(solution);
2503 *  
2504 *   pcout << " Solving system... " << std::endl;
2505 *   solve_newton_system();
2506 *  
2507 * @endcode
2508 *
2509 * It gets a bit more hairy after we have computed the
2510 * trial solution @f$\tilde{\mathbf u}@f$ of the current Newton step.
2511 * We handle a highly nonlinear problem so we have to damp
2512 * Newton's method using a line search. To understand how we do this,
2513 * recall that in our formulation, we compute a trial solution
2514 * in each Newton step and not the update between old and new solution.
2515 * Since the solution set is a convex set, we will use a line
2516 * search that tries linear combinations of the
2517 * previous and the trial solution to guarantee that the
2518 * damped solution is in our solution set again.
2519 * At most we apply 5 damping steps.
2520 *
2521
2522 *
2523 * There are exceptions to when we use a line search. First,
2524 * if this is the first Newton step on any mesh, then we don't have
2525 * any point to compare the residual to, so we always accept a full
2526 * step. Likewise, if this is the second Newton step on the first mesh
2527 * (or the second on any mesh if we don't transfer solutions from mesh
2528 * to mesh), then we have computed the first of these steps using just
2529 * an elastic model (see how we set the yield stress sigma to an
2530 * unreasonably large value above). In this case, the first Newton
2531 * solution was a purely elastic one, the second one a plastic one,
2532 * and any linear combination would not necessarily be expected to
2533 * lie in the feasible set -- so we just accept the solution we just
2534 * got.
2535 *
2536
2537 *
2538 * In either of these two cases, we bypass the line search and just
2539 * update residual and other vectors as necessary.
2540 *
2541 * @code
2542 *   if ((newton_step == 1) ||
2543 *   (transfer_solution && newton_step == 2 &&
2544 *   current_refinement_cycle == 0) ||
2545 *   (!transfer_solution && newton_step == 2))
2546 *   {
2547 *   compute_nonlinear_residual(solution);
2548 *   old_solution = solution;
2549 *  
2550 *   residual = newton_rhs;
2551 *   const unsigned int start_res = (residual.local_range().first),
2552 *   end_res = (residual.local_range().second);
2553 *   for (unsigned int n = start_res; n < end_res; ++n)
2554 *   if (all_constraints.is_inhomogeneously_constrained(n))
2555 *   residual(n) = 0;
2556 *  
2557 *   residual.compress(VectorOperation::insert);
2558 *  
2559 *   residual_norm = residual.l2_norm();
2560 *  
2561 *   pcout << " Accepting Newton solution with residual: "
2562 *   << residual_norm << std::endl;
2563 *   }
2564 *   else
2565 *   {
2566 *   for (unsigned int i = 0; i < 5; ++i)
2567 *   {
2568 *   distributed_solution = solution;
2569 *  
2570 *   const double alpha = std::pow(0.5, static_cast<double>(i));
2571 *   tmp_vector = old_solution;
2572 *   tmp_vector.sadd(1 - alpha, alpha, distributed_solution);
2573 *  
2574 *   TimerOutput::Scope t(computing_timer, "Residual and lambda");
2575 *  
2576 *   locally_relevant_tmp_vector = tmp_vector;
2577 *   compute_nonlinear_residual(locally_relevant_tmp_vector);
2578 *   residual = newton_rhs;
2579 *  
2580 *   const unsigned int start_res = (residual.local_range().first),
2581 *   end_res = (residual.local_range().second);
2582 *   for (unsigned int n = start_res; n < end_res; ++n)
2583 *   if (all_constraints.is_inhomogeneously_constrained(n))
2584 *   residual(n) = 0;
2585 *  
2586 *   residual.compress(VectorOperation::insert);
2587 *  
2588 *   residual_norm = residual.l2_norm();
2589 *  
2590 *   pcout
2591 *   << " Residual of the non-contact part of the system: "
2592 *   << residual_norm << std::endl
2593 *   << " with a damping parameter alpha = " << alpha
2594 *   << std::endl;
2595 *  
2596 *   if (residual_norm < previous_residual_norm)
2597 *   break;
2598 *   }
2599 *  
2600 *   solution = tmp_vector;
2601 *   old_solution = solution;
2602 *   }
2603 *  
2604 *   previous_residual_norm = residual_norm;
2605 *  
2606 *  
2607 * @endcode
2608 *
2609 * The final step is to check for convergence. If the active set
2610 * has not changed across all processors and the residual is
2611 * less than a threshold of @f$10^{-10}@f$, then we terminate
2612 * the iteration on the current mesh:
2613 *
2614 * @code
2615 *   if (Utilities::MPI::sum((active_set == old_active_set) ? 0 : 1,
2616 *   mpi_communicator) == 0)
2617 *   {
2618 *   pcout << " Active set did not change!" << std::endl;
2619 *   if (residual_norm < 1e-10)
2620 *   break;
2621 *   }
2622 *  
2623 *   old_active_set = active_set;
2624 *   }
2625 *   }
2626 *  
2627 * @endcode
2628 *
2629 *
2630 * <a name="step_42-PlasticityContactProblemrefine_grid"></a>
2631 * <h4>PlasticityContactProblem::refine_grid</h4>
2632 *
2633
2634 *
2635 * If you've made it this far into the deal.II tutorial, the following
2636 * function refining the mesh should not pose any challenges to you
2637 * any more. It refines the mesh, either globally or using the Kelly
2638 * error estimator, and if so asked also transfers the solution from
2639 * the previous to the next mesh. In the latter case, we also need
2640 * to compute the active set and other quantities again, for which we
2641 * need the information computed by <code>compute_nonlinear_residual()</code>.
2642 *
2643 * @code
2644 *   template <int dim>
2645 *   void PlasticityContactProblem<dim>::refine_grid()
2646 *   {
2647 *   if (refinement_strategy == RefinementStrategy::refine_global)
2648 *   {
2649 *   for (typename Triangulation<dim>::active_cell_iterator cell =
2650 *   triangulation.begin_active();
2651 *   cell != triangulation.end();
2652 *   ++cell)
2653 *   if (cell->is_locally_owned())
2654 *   cell->set_refine_flag();
2655 *   }
2656 *   else
2657 *   {
2658 *   Vector<float> estimated_error_per_cell(triangulation.n_active_cells());
2659 *   KellyErrorEstimator<dim>::estimate(
2660 *   dof_handler,
2661 *   QGauss<dim - 1>(fe.degree + 2),
2662 *   std::map<types::boundary_id, const Function<dim> *>(),
2663 *   solution,
2664 *   estimated_error_per_cell);
2665 *  
2666 *   parallel::distributed::GridRefinement ::refine_and_coarsen_fixed_number(
2667 *   triangulation, estimated_error_per_cell, 0.3, 0.03);
2668 *   }
2669 *  
2670 *   triangulation.prepare_coarsening_and_refinement();
2671 *  
2672 *   SolutionTransfer<dim, TrilinosWrappers::MPI::Vector> solution_transfer(
2673 *   dof_handler);
2674 *   if (transfer_solution)
2675 *   solution_transfer.prepare_for_coarsening_and_refinement(solution);
2676 *  
2677 *   triangulation.execute_coarsening_and_refinement();
2678 *  
2679 *   setup_system();
2680 *  
2681 *   if (transfer_solution)
2682 *   {
2683 *   TrilinosWrappers::MPI::Vector distributed_solution(locally_owned_dofs,
2684 *   mpi_communicator);
2685 *   solution_transfer.interpolate(distributed_solution);
2686 *  
2687 * @endcode
2688 *
2689 * enforce constraints to make the interpolated solution conforming on
2690 * the new mesh:
2691 *
2692 * @code
2693 *   constraints_hanging_nodes.distribute(distributed_solution);
2694 *  
2695 *   solution = distributed_solution;
2696 *   compute_nonlinear_residual(solution);
2697 *   }
2698 *   }
2699 *  
2700 *  
2701 * @endcode
2702 *
2703 *
2704 * <a name="step_42-PlasticityContactProblemoutput_results"></a>
2705 * <h4>PlasticityContactProblem::output_results</h4>
2706 *
2707
2708 *
2709 * Next is the function we use to actually generate graphical output. The
2710 * function is a bit tedious, but not actually particularly complicated.
2711 * It computes the contact forces along the contact surface. We can do
2712 * so (as shown in the accompanying paper) by taking the untreated
2713 * residual vector and identifying which degrees of freedom
2714 * correspond to those with contact by asking whether they have an
2715 * inhomogeneous constraints associated with them. As always, we need
2716 * to be mindful that we can only write into completely distributed
2717 * vectors (i.e., vectors without ghost elements) but that when we
2718 * want to generate output, we need vectors that do indeed have
2719 * ghost entries for all locally relevant degrees of freedom.
2720 *
2721
2722 *
2723 * In order to more easily visualize the deformation of the object due
2724 * to the external obstacle, we output the mesh not in the reference
2725 * (undeformed) configuration, but in the *deformed* configuration that
2726 * is obtained by adding to each vertex location that computed deformation
2727 * vector. This is easily done via the MappingQEulerian class that
2728 * represents the mapping from the reference cell to a cell that is
2729 * described by the cell's vertices' reference coordinates *plus* a
2730 * previously computed deformation vector -- here simply the computed
2731 * solution of the problem. This mapping is given to the call of
2732 * DataOut::build_patches().
2733 *
2734 * @code
2735 *   template <int dim>
2736 *   void PlasticityContactProblem<dim>::output_results(
2737 *   const unsigned int current_refinement_cycle)
2738 *   {
2739 *   TimerOutput::Scope t(computing_timer, "Graphical output");
2740 *  
2741 *   pcout << " Writing graphical output... " << std::flush;
2742 *  
2743 * @endcode
2744 *
2745 * Calculation of the contact forces
2746 *
2747 * @code
2748 *   TrilinosWrappers::MPI::Vector distributed_lambda(locally_owned_dofs,
2749 *   mpi_communicator);
2750 *   const unsigned int start_res = (newton_rhs_uncondensed.local_range().first),
2751 *   end_res = (newton_rhs_uncondensed.local_range().second);
2752 *   for (unsigned int n = start_res; n < end_res; ++n)
2753 *   if (all_constraints.is_inhomogeneously_constrained(n))
2754 *   distributed_lambda(n) =
2755 *   newton_rhs_uncondensed(n) / diag_mass_matrix_vector(n);
2756 *   distributed_lambda.compress(VectorOperation::insert);
2757 *   constraints_hanging_nodes.distribute(distributed_lambda);
2758 *  
2759 *   TrilinosWrappers::MPI::Vector lambda(locally_relevant_dofs,
2760 *   mpi_communicator);
2761 *   lambda = distributed_lambda;
2762 *  
2763 *   TrilinosWrappers::MPI::Vector distributed_active_set_vector(
2764 *   locally_owned_dofs, mpi_communicator);
2765 *   distributed_active_set_vector = 0.;
2766 *   for (const auto index : active_set)
2767 *   distributed_active_set_vector[index] = 1.;
2768 *   distributed_active_set_vector.compress(VectorOperation::insert);
2769 *  
2770 *   TrilinosWrappers::MPI::Vector active_set_vector(locally_relevant_dofs,
2771 *   mpi_communicator);
2772 *   active_set_vector = distributed_active_set_vector;
2773 *  
2774 *   DataOut<dim> data_out;
2775 *  
2776 *   data_out.attach_dof_handler(dof_handler);
2777 *  
2778 *   const std::vector<DataComponentInterpretation::DataComponentInterpretation>
2779 *   data_component_interpretation(
2780 *   dim, DataComponentInterpretation::component_is_part_of_vector);
2781 *   data_out.add_data_vector(solution,
2782 *   std::vector<std::string>(dim, "displacement"),
2783 *   DataOut<dim>::type_dof_data,
2784 *   data_component_interpretation);
2785 *   data_out.add_data_vector(lambda,
2786 *   std::vector<std::string>(dim, "contact_force"),
2787 *   DataOut<dim>::type_dof_data,
2788 *   data_component_interpretation);
2789 *   data_out.add_data_vector(active_set_vector,
2790 *   std::vector<std::string>(dim, "active_set"),
2791 *   DataOut<dim>::type_dof_data,
2792 *   data_component_interpretation);
2793 *  
2794 *   Vector<float> subdomain(triangulation.n_active_cells());
2795 *   for (unsigned int i = 0; i < subdomain.size(); ++i)
2796 *   subdomain(i) = triangulation.locally_owned_subdomain();
2797 *   data_out.add_data_vector(subdomain, "subdomain");
2798 *  
2799 *   data_out.add_data_vector(fraction_of_plastic_q_points_per_cell,
2800 *   "fraction_of_plastic_q_points");
2801 *  
2802 *   data_out.build_patches(MappingQEulerian<dim, TrilinosWrappers::MPI::Vector>(
2803 *   fe.degree, dof_handler, solution));
2804 *  
2805 * @endcode
2806 *
2807 * In the remainder of the function, we generate one VTU file on
2808 * every processor, indexed by the subdomain id of this processor.
2809 * On the first processor, the call below also creates a <code>.pvtu</code>
2810 * file that indexes <i>all</i> of the VTU files so that the entire
2811 * set of output files can be read at once. These <code>.pvtu</code>
2812 * are used by Paraview to describe an entire parallel computation's
2813 * output files. The principal competitor of Paraview, the VisIt
2814 * visualization program, can also read these files.
2815 *
2816 * @code
2817 *   const std::string pvtu_filename = data_out.write_vtu_with_pvtu_record(
2818 *   output_dir, "solution", current_refinement_cycle, mpi_communicator, 2);
2819 *   pcout << output_dir << pvtu_filename << std::endl;
2820 *   }
2821 *  
2822 *  
2823 * @endcode
2824 *
2825 *
2826 * <a name="step_42-PlasticityContactProblemoutput_contact_force"></a>
2827 * <h4>PlasticityContactProblem::output_contact_force</h4>
2828 *
2829
2830 *
2831 * This last auxiliary function computes the contact force by
2832 * calculating an integral over the contact pressure in z-direction
2833 * over the contact area. For this purpose we set the contact
2834 * pressure lambda to 0 for all inactive dofs (whether a degree
2835 * of freedom is part of the contact is determined just as
2836 * we did in the previous function). For all
2837 * active dofs, lambda contains the quotient of the nonlinear
2838 * residual (newton_rhs_uncondensed) and corresponding diagonal entry
2839 * of the mass matrix (diag_mass_matrix_vector). Because it is
2840 * not unlikely that hanging nodes show up in the contact area
2841 * it is important to apply constraints_hanging_nodes.distribute
2842 * to the distributed_lambda vector.
2843 *
2844 * @code
2845 *   template <int dim>
2846 *   void PlasticityContactProblem<dim>::output_contact_force() const
2847 *   {
2848 *   TrilinosWrappers::MPI::Vector distributed_lambda(locally_owned_dofs,
2849 *   mpi_communicator);
2850 *   const unsigned int start_res = (newton_rhs_uncondensed.local_range().first),
2851 *   end_res = (newton_rhs_uncondensed.local_range().second);
2852 *   for (unsigned int n = start_res; n < end_res; ++n)
2853 *   if (all_constraints.is_inhomogeneously_constrained(n))
2854 *   distributed_lambda(n) =
2855 *   newton_rhs_uncondensed(n) / diag_mass_matrix_vector(n);
2856 *   else
2857 *   distributed_lambda(n) = 0;
2858 *   distributed_lambda.compress(VectorOperation::insert);
2859 *   constraints_hanging_nodes.distribute(distributed_lambda);
2860 *  
2861 *   TrilinosWrappers::MPI::Vector lambda(locally_relevant_dofs,
2862 *   mpi_communicator);
2863 *   lambda = distributed_lambda;
2864 *  
2865 *   double contact_force = 0.0;
2866 *  
2867 *   const QGauss<dim - 1> face_quadrature_formula(fe.degree + 1);
2868 *   FEFaceValues<dim> fe_values_face(fe,
2869 *   face_quadrature_formula,
2871 *  
2872 *   const unsigned int n_face_q_points = face_quadrature_formula.size();
2873 *  
2874 *   const FEValuesExtractors::Vector displacement(0);
2875 *  
2876 *   for (const auto &cell : dof_handler.active_cell_iterators())
2877 *   if (cell->is_locally_owned())
2878 *   for (const auto &face : cell->face_iterators())
2879 *   if (face->at_boundary() && face->boundary_id() == 1)
2880 *   {
2881 *   fe_values_face.reinit(cell, face);
2882 *  
2883 *   std::vector<Tensor<1, dim>> lambda_values(n_face_q_points);
2884 *   fe_values_face[displacement].get_function_values(lambda,
2885 *   lambda_values);
2886 *  
2887 *   for (unsigned int q_point = 0; q_point < n_face_q_points;
2888 *   ++q_point)
2889 *   contact_force +=
2890 *   lambda_values[q_point][2] * fe_values_face.JxW(q_point);
2891 *   }
2892 *   contact_force = Utilities::MPI::sum(contact_force, MPI_COMM_WORLD);
2893 *  
2894 *   pcout << "Contact force = " << contact_force << std::endl;
2895 *   }
2896 *  
2897 *  
2898 * @endcode
2899 *
2900 *
2901 * <a name="step_42-PlasticityContactProblemrun"></a>
2902 * <h4>PlasticityContactProblem::run</h4>
2903 *
2904
2905 *
2906 * As in all other tutorial programs, the <code>run()</code> function contains
2907 * the overall logic. There is not very much to it here: in essence, it
2908 * performs the loops over all mesh refinement cycles, and within each, hands
2909 * things over to the Newton solver in <code>solve_newton()</code> on the
2910 * current mesh and calls the function that creates graphical output for
2911 * the so-computed solution. It then outputs some statistics concerning both
2912 * run times and memory consumption that has been collected over the course of
2913 * computations on this mesh.
2914 *
2915 * @code
2916 *   template <int dim>
2917 *   void PlasticityContactProblem<dim>::run()
2918 *   {
2919 *   computing_timer.reset();
2920 *   for (; current_refinement_cycle < n_refinement_cycles;
2921 *   ++current_refinement_cycle)
2922 *   {
2923 *   {
2924 *   TimerOutput::Scope t(computing_timer, "Setup");
2925 *  
2926 *   pcout << std::endl;
2927 *   pcout << "Cycle " << current_refinement_cycle << ':' << std::endl;
2928 *  
2929 *   if (current_refinement_cycle == 0)
2930 *   {
2931 *   make_grid();
2932 *   setup_system();
2933 *   }
2934 *   else
2935 *   {
2936 *   TimerOutput::Scope t(computing_timer, "Setup: refine mesh");
2937 *   refine_grid();
2938 *   }
2939 *   }
2940 *  
2941 *   solve_newton();
2942 *  
2943 *   output_results(current_refinement_cycle);
2944 *  
2945 *   computing_timer.print_summary();
2946 *   computing_timer.reset();
2947 *  
2950 *   pcout << "Peak virtual memory used, resident in kB: " << stats.VmSize
2951 *   << ' ' << stats.VmRSS << std::endl;
2952 *  
2953 *   if (base_mesh == "box")
2954 *   output_contact_force();
2955 *   }
2956 *   }
2957 *   } // namespace Step42
2958 *  
2959 * @endcode
2960 *
2961 *
2962 * <a name="step_42-Thecodemaincodefunction"></a>
2963 * <h3>The <code>main</code> function</h3>
2964 *
2965
2966 *
2967 * There really isn't much to the <code>main()</code> function. It looks
2968 * like they always do:
2969 *
2970 * @code
2971 *   int main(int argc, char *argv[])
2972 *   {
2973 *   using namespace dealii;
2974 *   using namespace Step42;
2975 *  
2976 *   try
2977 *   {
2978 *   ParameterHandler prm;
2979 *   PlasticityContactProblem<3>::declare_parameters(prm);
2980 *   if (argc != 2)
2981 *   {
2982 *   std::cerr << "*** Call this program as <./step-42 input.prm>"
2983 *   << std::endl;
2984 *   return 1;
2985 *   }
2986 *  
2987 *   prm.parse_input(argv[1]);
2988 *   Utilities::MPI::MPI_InitFinalize mpi_initialization(
2989 *   argc, argv, numbers::invalid_unsigned_int);
2990 *   {
2991 *   PlasticityContactProblem<3> problem(prm);
2992 *   problem.run();
2993 *   }
2994 *   }
2995 *   catch (std::exception &exc)
2996 *   {
2997 *   std::cerr << std::endl
2998 *   << std::endl
2999 *   << "----------------------------------------------------"
3000 *   << std::endl;
3001 *   std::cerr << "Exception on processing: " << std::endl
3002 *   << exc.what() << std::endl
3003 *   << "Aborting!" << std::endl
3004 *   << "----------------------------------------------------"
3005 *   << std::endl;
3006 *  
3007 *   return 1;
3008 *   }
3009 *   catch (...)
3010 *   {
3011 *   std::cerr << std::endl
3012 *   << std::endl
3013 *   << "----------------------------------------------------"
3014 *   << std::endl;
3015 *   std::cerr << "Unknown exception!" << std::endl
3016 *   << "Aborting!" << std::endl
3017 *   << "----------------------------------------------------"
3018 *   << std::endl;
3019 *   return 1;
3020 *   }
3021 *  
3022 *   return 0;
3023 *   }
3024 * @endcode
3025<a name="step_42-Results"></a><h1>Results</h1>
3026
3027
3028The directory that contains this program also contains a number of input
3029parameter files that can be used to create various different
3030simulations. For example, running the program with the
3031<code>p1_adaptive.prm</code> parameter file (using a ball as obstacle and the
3032box as domain) on 16 cores produces output like this:
3033@code
3034 Using output directory 'p1adaptive/'
3035 FE degree 1
3036 transfer solution false
3037
3038Cycle 0:
3039 Number of active cells: 512
3040 Number of degrees of freedom: 2187
3041
3042 Newton iteration 1
3043 Updating active set...
3044 Size of active set: 1
3045 Assembling system...
3046 Solving system...
3047 Error: 173.076 -> 1.64265e-06 in 7 Bicgstab iterations.
3048 Accepting Newton solution with residual: 1.64265e-06
3049
3050 Newton iteration 2
3051 Updating active set...
3052 Size of active set: 1
3053 Assembling system...
3054 Solving system...
3055 Error: 57.3622 -> 3.23721e-07 in 8 Bicgstab iterations.
3056 Accepting Newton solution with residual: 24.9028
3057 Active set did not change!
3058
3059 Newton iteration 3
3060 Updating active set...
3061 Size of active set: 1
3062 Assembling system...
3063 Solving system...
3064 Error: 24.9028 -> 9.94326e-08 in 7 Bicgstab iterations.
3065 Residual of the non-contact part of the system: 1.63333
3066 with a damping parameter alpha = 1
3067 Active set did not change!
3068
3069...
3070
3071 Newton iteration 6
3072 Updating active set...
3073 Size of active set: 1
3074 Assembling system...
3075 Solving system...
3076 Error: 1.43188e-07 -> 3.56218e-16 in 8 Bicgstab iterations.
3077 Residual of the non-contact part of the system: 4.298e-14
3078 with a damping parameter alpha = 1
3079 Active set did not change!
3080 Writing graphical output... p1_adaptive/solution-00.pvtu
3081
3082
3083+---------------------------------------------+------------+------------+
3084| Total wallclock time elapsed since start | 1.13s | |
3085| | | |
3086| Section | no. calls | wall time | % of total |
3087+---------------------------------+-----------+------------+------------+
3088| Assembling | 6 | 0.463s | 41% |
3089| Graphical output | 1 | 0.0257s | 2.3% |
3090| Residual and lambda | 4 | 0.0754s | 6.7% |
3091| Setup | 1 | 0.227s | 20% |
3092| Setup: constraints | 1 | 0.0347s | 3.1% |
3093| Setup: distribute DoFs | 1 | 0.0441s | 3.9% |
3094| Setup: matrix | 1 | 0.0119s | 1.1% |
3095| Setup: vectors | 1 | 0.00155s | 0.14% |
3096| Solve | 6 | 0.246s | 22% |
3097| Solve: iterate | 6 | 0.0631s | 5.6% |
3098| Solve: setup preconditioner | 6 | 0.167s | 15% |
3099| update active set | 6 | 0.0401s | 3.6% |
3100+---------------------------------+-----------+------------+------------+
3101
3102Peak virtual memory used, resident in kB: 541884 77464
3103Contact force = 37.3058
3104
3105...
3106
3107Cycle 3:
3108 Number of active cells: 14652
3109 Number of degrees of freedom: 52497
3110
3111 Newton iteration 1
3112 Updating active set...
3113 Size of active set: 145
3114 Assembling system...
3115 Solving system...
3116 Error: 296.309 -> 2.72484e-06 in 10 Bicgstab iterations.
3117 Accepting Newton solution with residual: 2.72484e-06
3118
3119...
3120
3121 Newton iteration 10
3122 Updating active set...
3123 Size of active set: 145
3124 Assembling system...
3125 Solving system...
3126 Error: 2.71541e-07 -> 1.5428e-15 in 27 Bicgstab iterations.
3127 Residual of the non-contact part of the system: 1.89261e-13
3128 with a damping parameter alpha = 1
3129 Active set did not change!
3130 Writing graphical output... p1_adaptive/solution-03.pvtu
3131
3132
3133+---------------------------------------------+------------+------------+
3134| Total wallclock time elapsed since start | 38.4s | |
3135| | | |
3136| Section | no. calls | wall time | % of total |
3137+---------------------------------+-----------+------------+------------+
3138| Assembling | 10 | 22.5s | 58% |
3139| Graphical output | 1 | 0.327s | 0.85% |
3140| Residual and lambda | 9 | 3.75s | 9.8% |
3141| Setup | 1 | 4.83s | 13% |
3142| Setup: constraints | 1 | 0.578s | 1.5% |
3143| Setup: distribute DoFs | 1 | 0.71s | 1.8% |
3144| Setup: matrix | 1 | 0.111s | 0.29% |
3145| Setup: refine mesh | 1 | 4.83s | 13% |
3146| Setup: vectors | 1 | 0.00548s | 0.014% |
3147| Solve | 10 | 5.49s | 14% |
3148| Solve: iterate | 10 | 3.5s | 9.1% |
3149| Solve: setup preconditioner | 10 | 1.84s | 4.8% |
3150| update active set | 10 | 0.662s | 1.7% |
3151+---------------------------------+-----------+------------+------------+
3152
3153Peak virtual memory used, resident in kB: 566052 105788
3154Contact force = 56.794
3155
3156...
3157@endcode
3158
3159The tables at the end of each cycle show information about computing time
3160(these numbers are of course specific to the machine on which this output
3161was produced)
3162and the number of calls of different parts of the program like assembly or
3163calculating the residual, for the most recent mesh refinement cycle. Some of
3164the numbers above can be improved by transferring the solution from one mesh to
3165the next, an option we have not exercised here. Of course, you can also make
3166the program run faster, especially on the later refinement cycles, by just
3167using more processors: the accompanying paper shows good scaling to at least
31681000 cores.
3169
3170In a typical run, you can observe that for every refinement step, the active
3171set - the contact points - are iterated out at first. After that the Newton
3172method has only to resolve the plasticity. For the finer meshes,
3173quadratic convergence can be observed for the last 4 or 5 Newton iterations.
3174
3175We will not discuss here in all detail what happens with each of the input
3176files. Rather, let us just show pictures of the solution (the left half of the
3177domain is omitted if cells have zero quadrature points at which the plastic
3178inequality is active):
3179
3180<table align="center">
3181 <tr>
3182 <td>
3183 <img src="https://dealii.org/images/steps/developer/step-42.CellConstitutionColorbar.png">
3184 </td>
3185 <td>
3186 <img src="https://dealii.org/images/steps/developer/step-42.CellConstitutionBall2.png" alt="" width="70%">
3187 </td>
3188 <td valign="top">
3189 &nbsp;
3190 </td>
3191 <td>
3192 <img src="https://dealii.org/images/steps/developer/step-42.CellConstitutionLi2.png" alt="" alt="" width="70%">
3193 </td>
3194 </tr>
3195</table>
3196
3197The picture shows the adaptive refinement and as well how much a cell is
3198plastified during the contact with the ball. Remember that we consider the
3199norm of the deviator part of the stress in each quadrature point to
3200see if there is elastic or plastic behavior.
3201The blue
3202color means that this cell contains only elastic quadrature points in
3203contrast to the red cells in which all quadrature points are plastified.
3204In the middle of the top surface -
3205where the mesh is finest - a very close look shows the dimple caused by the
3206obstacle. This is the result of showing the mesh in the *deformed* configuration;
3207we have obtained this deformed mesh by using the MappingQEulerian class
3208in the call to DataOut::build_patches() in the `output_results()` function.
3209
3210Further discussion of results that can be obtained using this program is
3211provided in the publication mentioned at the very top of this page.
3212
3213
3214<a name="step-42-extensions"></a>
3215<a name="step_42-Possibilitiesforextensions"></a><h1>Possibilities for extensions</h1>
3216
3217
3218There are, as always, multiple possibilities for extending this program. From
3219an algorithmic perspective, this program goes about as far as one can at the
3220time of writing, using the best available algorithms for the contact
3221inequality, the plastic nonlinearity, and the linear solvers. However, there
3222are things one would like to do with this program as far as more realistic
3223situations are concerned:
3224<ul>
3225<li> Extend the program from a static to a quasi-static situation, perhaps by
3226choosing a backward-Euler-scheme for the time discretization. Some theoretical
3227results can be found in the PhD thesis by Jörg Frohne, <i>FEM-Simulation
3228der Umformtechnik metallischer Oberfl&auml;chen im Mikrokosmos</i>, University
3229of Siegen, Germany, 2011.
3230
3231<li> It would also be an interesting advance to consider a contact problem
3232with friction. In almost every mechanical process friction has a big
3233influence. To model this situation, we have to take into account tangential
3234stresses at the contact surface. Friction also adds another inequality to
3235our problem since body and obstacle will typically stick together as long as
3236the tangential stress does not exceed a certain limit, beyond which the two
3237bodies slide past each other.
3238
3239<li> If we already simulate a frictional contact, the next step to consider
3240is heat generation over the contact zone. The heat that is
3241caused by friction between two bodies raises the temperature in the
3242deformable body and entails an change of some material parameters.
3243
3244<li> It might be of interest to implement more accurate, problem-adapted error
3245estimators for contact as well as for the plasticity.
3246</ul>
3247 *
3248 *
3249<a name="step_42-PlainProg"></a>
3250<h1> The plain program</h1>
3251@include "step-42.cc"
3252*/
*  iterator end()
*  *  for(const auto &cell :triangulation.active_cell_iterators())
*  *  int main(int argc, char **argv)
*  x_component_mask set(0, true)
*  *  *  struct InterferenceTaperTransform *  
Point< 2 > second
Definition grid_out.cc:4640
Point< 2 > first
Definition grid_out.cc:4639
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:562
void make_hanging_node_constraints(const DoFHandler< dim, spacedim > &dof_handler, AffineConstraints< number > &constraints)
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)
@ update_values
Shape function values.
@ update_JxW_values
Transformed quadrature weights.
IndexSet extract_locally_relevant_dofs(const DoFHandler< dim, spacedim > &dof_handler)
void interpolate(const DoFHandler< dim, spacedim > &dof1, const InVector &u1, const DoFHandler< dim, spacedim > &dof2, OutVector &u2)
@ matrix
Contents is actually a matrix.
@ diagonal
Matrix is diagonal.
constexpr types::blas_int zero
constexpr types::blas_int one
void mass_matrix(FullMatrix< double > &M, const FEValuesBase< dim > &fe, const double factor=1.)
Definition l2.h:55
*  *  if(update_pressure &update_flags) *  compute_pressure(constitutive_request
*  *  *  *  std::vector< Number > ThermoPlasticMaterial< dim, ViscoplasticYieldLaw, Number >::get_state_parameters   const
void apply(const Kokkos::TeamPolicy< MemorySpace::Default::kokkos_space::execution_space >::member_type &team_member, const Kokkos::View< Number *, ShapeDataMemorySpace > shape_data, const ViewTypeIn in, ViewTypeOut out)
void distribute_sparsity_pattern(DynamicSparsityPattern &dsp, const IndexSet &locally_owned_rows, const MPI_Comm mpi_comm, const IndexSet &locally_relevant_rows)
T sum(const T &t, const MPI_Comm mpi_communicator)
unsigned int this_mpi_process(const MPI_Comm mpi_communicator)
Definition mpi.cc:118
void get_memory_stats(MemoryStats &stats)
Definition utilities.cc:948
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)
void reinit(MatrixBlock< MatrixType > &v, const BlockSparsityPattern &p)
STL namespace.
::VectorizedArray< Number, width > pow(const ::VectorizedArray< Number, width > &, const Number p)
unsigned int boundary_id
Definition types.h:159