--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010 - 2015 by the deal.II authors
+ * and Salvador Flores.
+ *
+ *
+ *
+ * This is free software; you can use it, redistribute
+ * it, and/or modify it under the terms of the GNU Lesser General
+ * Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * The full text of the license can be found in the file LICENSE at
+ * the top level of the deal.II distribution.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Author: Salvador Flores,
+ * Center for Mathematical Modelling,
+ * Universidad de Chile, 2015.
+ */
+
+
+/*
+ This piece of software solves the elliptic p-laplacian
+ boundary-value problems:
+
+ Min {∫ 1/2 W(|Du|²)+ 1/p |Du|^p -fu : u=g on ∂S } (1)
+ u
+
+ for large values of p, which approximates (see Alvarez & Flores 2015)
+
+ Min {∫ 1/2 W(|Du|²) -fu : |Du|<1 a.s. on S, u=g on ∂S }
+ u
+
+ By default W(t)=t and S=unit disk.
+
+ Large portions of this code are borrowed from the deal.ii tutorials
+
+ step-15 step-29.
+
+ For further details see the technical report
+ "Solving variational problems with uniform gradient bounds by p-Laplacian
+ approximation: Elastoplastic torsion implementation using the deal.II
+ library"
+ available at the documentation and at http://www.dim.uchile.cl/~sflores.
+
+*/
+
+// Include files
+
+#include <deal.II/base/quadrature_lib.h>
+#include <deal.II/base/function.h>
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/base/convergence_table.h>
+#include <deal.II/base/smartpointer.h>
+#include <deal.II/base/parameter_handler.h>
+#include <deal.II/base/timer.h>
+
+#include <deal.II/lac/vector.h>
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/sparse_matrix.h>
+#include <deal.II/lac/compressed_sparsity_pattern.h>
+#include <deal.II/lac/solver_cg.h>
+#include <deal.II/lac/precondition.h>
+#include <deal.II/lac/constraint_matrix.h>
+
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/tria_accessor.h>
+#include <deal.II/grid/tria_iterator.h>
+#include <deal.II/grid/tria_boundary_lib.h>
+#include <deal.II/grid/grid_refinement.h>
+#include <deal.II/grid/grid_in.h>
+
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/dofs/dof_accessor.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/dofs/dof_renumbering.h>
+
+#include <deal.II/fe/fe_values.h>
+#include <deal.II/fe/fe_q.h>
+
+#include <deal.II/numerics/vector_tools.h>
+#include <deal.II/numerics/matrix_tools.h>
+#include <deal.II/numerics/data_out.h>
+#include <deal.II/numerics/error_estimator.h>
+#include <deal.II/numerics/solution_transfer.h>
+
+#include <typeinfo>
+#include <fstream>
+#include <iostream>
+
+#include <deal.II/numerics/solution_transfer.h>
+
+// Open a namespace for this program and import everything from the
+// dealii namespace into it.
+namespace nsp
+{
+ using namespace dealii;
+
+// ********************************************************//
+ class ParameterReader : public Subscriptor
+ {
+ public:
+ ParameterReader(ParameterHandler &);
+ void read_parameters(const std::string);
+ private:
+ void declare_parameters();
+ ParameterHandler &prm;
+ };
+// Constructor
+ ParameterReader::ParameterReader(ParameterHandler ¶mhandler):
+ prm(paramhandler)
+ {}
+
+ void ParameterReader::declare_parameters()
+ {
+
+ prm.enter_subsection ("Global Parameters");
+ {
+ prm.declare_entry("p", "100",Patterns::Double(2.1),
+ "Penalization parameter");
+ prm.declare_entry("known_solution", "true",Patterns::Bool(),
+ "Whether the exact solution is known");
+ }
+ prm.leave_subsection ();
+
+ prm.enter_subsection ("Mesh & Refinement Parameters");
+ {
+ prm.declare_entry("Code for the domain", "0",Patterns::Integer(0,2),
+ "Number identifying the domain in which we solve the problem");
+ prm.declare_entry("No of initial refinements", "4",Patterns::Integer(0),
+ "Number of global mesh refinement steps applied to initial coarse grid");
+ prm.declare_entry("No of adaptive refinements", "8",Patterns::Integer(0),
+ "Number of global adaptive mesh refinements");
+ prm.declare_entry("top_fraction_of_cells", "0.25",Patterns::Double(0),
+ "refinement threshold");
+ prm.declare_entry("bottom_fraction_of_cells", "0.05",Patterns::Double(0),
+ "coarsening threshold");
+ }
+ prm.leave_subsection ();
+
+
+ prm.enter_subsection ("Algorithm Parameters");
+ {
+ prm.declare_entry("Descent_direction", "0",Patterns::Integer(0,1),
+ "0: Preconditioned descent, 1: Newton Method");
+ prm.declare_entry("init_p", "10",Patterns::Double(2),
+ "Initial p");
+ prm.declare_entry("delta_p", "50",Patterns::Double(0),
+ "increase of p");
+ prm.declare_entry("Max_CG_it", "1500",Patterns::Integer(1),
+ "Maximum Number of CG iterations");
+ prm.declare_entry("CG_tol", "1e-10",Patterns::Double(0),
+ "Tolerance for CG iterations");
+ prm.declare_entry("max_LS_it", "45",Patterns::Integer(1),
+ "Maximum Number of LS iterations");
+ prm.declare_entry("line_search_tolerence", "1e-6",Patterns::Double(0),
+ "line search tolerance constant (c1 in Nocedal-Wright)");
+ prm.declare_entry("init_step_length", "1e-2",Patterns::Double(0),
+ "initial step length in line-search");
+ prm.declare_entry("Max_inner", "800",Patterns::Integer(1),
+ "Maximum Number of inner iterations");
+ prm.declare_entry("eps", "1.0e-8",Patterns::Double(0),
+ "Threshold on norm of the derivative to declare optimality achieved");
+ prm.declare_entry("hi_eps", "1.0e-9",Patterns::Double(0),
+ "Threshold on norm of the derivative to declare optimality achieved in highly refined mesh");
+ prm.declare_entry("hi_th", "8",Patterns::Integer(0),
+ "Number of adaptive refinement before change convergence threshold");
+ }
+ prm.leave_subsection ();
+
+ }
+ void ParameterReader::read_parameters (const std::string parameter_file)
+ {
+ declare_parameters();
+ prm.read_input (parameter_file);
+ }
+
+// ******************************************************************************************//
+// The solution of the elastoplastic torsion problem on the unit disk with rhs=4.
+
+ template <int dim>
+ class Solution : public Function<dim>
+ {
+ public:
+ Solution () : Function<dim>() {}
+ virtual double value (const Point<dim> &pto, const unsigned int component = 0) const;
+ virtual Tensor<1,dim> gradient (const Point<dim> &pto, const unsigned int component = 0) const;
+ };
+
+ template <int dim>
+ double Solution<dim>::value (const Point<dim> &pto,const unsigned int) const
+ {
+ double r=sqrt(pto.square());
+ if (r<0.5)
+ return -1.0*std::pow(r,2.0)+0.75;
+ else
+ return 1.0-r;
+ }
+
+
+
+ template <int dim>
+ Tensor<1,dim> Solution<dim>::gradient (const Point<dim> &pto,const unsigned int) const
+ {
+ double r=sqrt(pto.square());
+ if (r<0.5)
+ return -2.0*pto;
+ else
+ return -1.0*pto/r;
+ }
+
+
+
+
+// ****************************************************************************************** //
+ /* Compute the Lagrange multiplier (as a derived quantity) */
+
+
+ template <int dim>
+ class ComputeMultiplier : public DataPostprocessor<dim>
+ {
+ private:
+ double p;
+ public:
+ ComputeMultiplier (double pe);
+
+ virtual
+ void compute_derived_quantities_scalar (
+ const std::vector< double > &,
+ const std::vector< Tensor< 1, dim > > &,
+ const std::vector< Tensor< 2, dim > > &,
+ const std::vector< Point< dim > > &,
+ const std::vector< Point< dim > > &,
+ std::vector< Vector< double > > &
+ ) const;
+
+ virtual std::vector<std::string> get_names () const;
+
+ virtual
+ std::vector<DataComponentInterpretation::DataComponentInterpretation>
+ get_data_component_interpretation () const;
+ virtual UpdateFlags get_needed_update_flags () const;
+ };
+
+
+ template <int dim>
+ ComputeMultiplier<dim>::ComputeMultiplier (double pe): p(pe)
+ {}
+
+
+ template <int dim>
+ void ComputeMultiplier<dim>::compute_derived_quantities_scalar(
+ const std::vector< double > & /*uh*/,
+ const std::vector< Tensor< 1, dim > > &duh,
+ const std::vector< Tensor< 2, dim > > & /*dduh*/,
+ const std::vector< Point< dim > > & /* normals*/,
+ const std::vector< Point< dim > > & /*evaluation_points*/,
+ std::vector< Vector< double > > &computed_quantities ) const
+ {
+ const unsigned int n_quadrature_points = duh.size();
+
+ for (unsigned int q=0; q<n_quadrature_points; ++q)
+ {
+ long double sqrGrad=duh[q]* duh[q]; //squared norm of the gradient
+ long double exponent=(p-2.0)/2*std::log(sqrGrad);
+ computed_quantities[q](0) = std::sqrt(sqrGrad); // norm of the gradient
+ computed_quantities[q](1)= std::exp(exponent); // multiplier
+ }
+ }
+
+
+
+
+
+ template <int dim>
+ std::vector<std::string>
+ ComputeMultiplier<dim>::get_names() const
+ {
+ std::vector<std::string> solution_names;
+ solution_names.push_back ("Gradient norm");
+ solution_names.push_back ("Lagrange multiplier");
+ return solution_names;
+ }
+
+
+ template <int dim>
+ UpdateFlags
+ ComputeMultiplier<dim>::get_needed_update_flags () const
+ {
+ return update_gradients;
+ }
+
+
+
+ template <int dim>
+ std::vector<DataComponentInterpretation::DataComponentInterpretation>
+ ComputeMultiplier<dim>:: get_data_component_interpretation () const
+ {
+ std::vector<DataComponentInterpretation::DataComponentInterpretation>
+ interpretation;
+ // norm of the gradient
+ interpretation.push_back (DataComponentInterpretation::component_is_scalar);
+ // Lagrange multiplier
+ interpretation.push_back (DataComponentInterpretation::component_is_scalar);
+ return interpretation;
+ }
+
+
+
+
+
+// *************************************************************************************** //
+ template <int dim>
+ class ElastoplasticTorsion
+ {
+ public:
+ ElastoplasticTorsion (ParameterHandler &);
+ ~ElastoplasticTorsion ();
+ void run ();
+
+ private:
+ void setup_system (const bool initial_step);
+ void assemble_system ();
+ bool solve (const int inner_it);
+ void init_mesh ();
+ void refine_mesh ();
+ void set_boundary_values ();
+ double phi (const double alpha) const;
+ bool checkWolfe(double &alpha, double &phi_alpha) const;
+ bool determine_step_length (const int inner_it);
+ void print_it_message (const int counter, bool ks);
+ void output_results (unsigned int refinement) const;
+ void format_convergence_tables();
+ void process_solution (const unsigned int cycle);
+ void process_multiplier (const unsigned int cycle,const int iter,double time);
+ double dual_error () const;
+ double dual_infty_error () const;
+ double W (double Du2) const;
+ double Wp (double Du2) const;
+ double G (double Du2) const;
+
+
+
+ ParameterHandler &prm;
+ Triangulation<dim> triangulation;
+ DoFHandler<dim> dof_handler;
+ ConstraintMatrix hanging_node_constraints;
+ SparsityPattern sparsity_pattern;
+ SparseMatrix<double> system_matrix;
+ ConvergenceTable convergence_table;
+ ConvergenceTable dual_convergence_table;
+ Vector<double> present_solution;
+ Vector<double> newton_update;
+ Vector<double> system_rhs;
+ Vector<double> grad_norm;
+ Vector<double> lambda;
+
+
+ double step_length,phi_zero,phi_alpha,phip,phip_zero;
+ double old_step,old_phi_zero,old_phip;
+ double L2_error;
+ double H1_error;
+ double Linfty_error;
+ double dual_L1_error;
+ double dual_L_infty_error;
+ FE_Q<dim> fe;
+ double p;
+ double line_search_tolerence; // c_1 in Nocedal & Wright
+ unsigned int dir_id;
+ std::string elements;
+ std::string Method;
+
+ };
+
+ /*******************************************************************************************/
+// Boundary condition
+
+ template <int dim>
+ class BoundaryValues : public Function<dim>
+ {
+ public:
+ BoundaryValues () : Function<dim>() {}
+
+ virtual double value (const Point<dim> &p,
+ const unsigned int component = 0) const;
+ };
+
+
+ template <int dim>
+ double BoundaryValues<dim>::value (const Point<dim> &pto,
+ const unsigned int /*component*/) const
+ {
+ // could be anything else (theory works provided |Dg|_infty < 1/2)
+ return 0.0;
+
+ /* A challenging BC leading to overdetermined problems
+ it is regulated by the parameter 0<eta<1.
+ eta closer to 1 leads to more difficult problems.
+
+ double pii=numbers::PI;
+ double theta=std::atan2(p[1],p[0])+pii;
+ double eta=0.9;
+
+ if (theta <= 0.5)
+ return eta*(theta*theta);
+ else if ((theta >0.5) & (theta<= pii-0.5))
+ return eta*(theta-0.25);
+ else if ((theta>pii-0.5)&(theta<= pii+0.5))
+ return eta*(pii-0.75-(theta-(pii-0.5))*(theta-(pii+0.5)));
+ else if ((theta>pii+0.5)&(theta<= 2*pii-0.5))
+ return eta*((2*pii-theta)-0.25);
+ else
+ return eta*((theta-2*pii)*(theta-2*pii) );*/
+ }
+
+
+
+ /******************************************************************************/
+// Right-Hand Side
+ template <int dim>
+ class RightHandSide : public Function<dim>
+ {
+ public:
+ RightHandSide () : Function<dim>() {}
+ virtual double value (const Point<dim> &p,
+ const unsigned int component = 0) const;
+ };
+
+ template <int dim>
+ double RightHandSide<dim>::value (const Point<dim> &p,
+ const unsigned int /*component*/) const
+ {
+ // set to constant = 4, for which explicit solution to compare exists
+ // could be anything
+ double return_value = 4.0;
+ return return_value;
+ }
+
+
+
+ /*******************************************************************/
+// The ElastoplasticTorsion class implementation
+
+// Constructor of the class
+ template <int dim>
+ ElastoplasticTorsion<dim>::ElastoplasticTorsion (ParameterHandler ¶m):
+ prm(param),
+ dof_handler (triangulation),
+ L2_error(1.0),
+ H1_error(1.0),
+ Linfty_error(1.0),
+ dual_L1_error(1.0),
+ dual_L_infty_error(1.0),
+ fe(2)
+ {
+ prm.enter_subsection ("Global Parameters");
+ p=prm.get_double("p");
+ prm.leave_subsection ();
+ prm.enter_subsection ("Algorithm Parameters");
+ line_search_tolerence=prm.get_double("line_search_tolerence");
+ dir_id=prm.get_integer("Descent_direction");
+ prm.leave_subsection ();
+ if (fe.degree==1)
+ elements="P1";
+ else elements="P2";
+
+ if (dir_id==0)
+ Method="Precond";
+ else
+ Method="Newton";
+ }
+
+
+
+ template <int dim>
+ ElastoplasticTorsion<dim>::~ElastoplasticTorsion ()
+ {
+ dof_handler.clear ();
+ }
+
+ /*****************************************************************************************/
+// print iteration message
+
+ template <int dim>
+ void ElastoplasticTorsion<dim>::print_it_message (const int counter, bool ks)
+ {
+ if (ks)
+ {
+ process_solution (counter);
+ std::cout << "iteration="<< counter+1 << " J(u_h)= "<< phi_zero << ", H1 error: "
+ << H1_error <<", W0-1,infty error: "<< Linfty_error<< " J'(u_h)(w)= "<< phip
+ << ", |J'(u_h)|= "<< system_rhs.l2_norm()<<std::endl;
+ }
+ else
+ {
+ std::cout << "iteration= " << counter+1 << " J(u_h)= "
+ << phi_alpha << " J'(u_h)= "<< phip<<std::endl;
+ }
+ }
+
+
+ /*****************************************************************************************/
+// Convergence Tables
+
+
+ /*************************************************************/
+// formating
+
+ template <int dim>
+ void ElastoplasticTorsion<dim>::format_convergence_tables()
+ {
+ convergence_table.set_precision("L2", 3);
+ convergence_table.set_precision("H1", 3);
+ convergence_table.set_precision("Linfty", 3);
+ convergence_table.set_precision("function value", 3);
+ convergence_table.set_precision("derivative", 3);
+ dual_convergence_table.set_precision("dual_L1", 3);
+ dual_convergence_table.set_precision("dual_Linfty", 3);
+ dual_convergence_table.set_precision("L2", 3);
+ dual_convergence_table.set_precision("H1", 3);
+ dual_convergence_table.set_precision("Linfty", 3);
+ convergence_table.set_scientific("L2", true);
+ convergence_table.set_scientific("H1", true);
+ convergence_table.set_scientific("Linfty", true);
+ convergence_table.set_scientific("function value", true);
+ convergence_table.set_scientific("derivative", true);
+ dual_convergence_table.set_scientific("dual_L1", true);
+ dual_convergence_table.set_scientific("dual_Linfty", true);
+ dual_convergence_table.set_scientific("L2", true);
+ dual_convergence_table.set_scientific("H1", true);
+ dual_convergence_table.set_scientific("Linfty", true);
+
+ }
+
+ /****************************************/
+// fill-in entry for the solution
+ template <int dim>
+ void ElastoplasticTorsion<dim>::process_solution (const unsigned int it)
+ {
+ Vector<float> difference_per_cell (triangulation.n_active_cells());
+
+ // compute L2 error (save to difference_per_cell)
+ VectorTools::integrate_difference (dof_handler,present_solution,
+ Solution<dim>(),difference_per_cell,QGauss<dim>(3),VectorTools::L2_norm);
+ L2_error = difference_per_cell.l2_norm();
+
+ // compute H1 error (save to difference_per_cell)
+ VectorTools::integrate_difference (dof_handler,present_solution,Solution<dim>(),
+ difference_per_cell,QGauss<dim>(3),VectorTools::H1_seminorm);
+ H1_error = difference_per_cell.l2_norm();
+
+ // compute W1infty error (save to difference_per_cell)
+ const QTrapez<1> q_trapez;
+ const QIterated<dim> q_iterated (q_trapez, 5);
+ VectorTools::integrate_difference (dof_handler,present_solution,Solution<dim>(),
+ difference_per_cell,q_iterated,VectorTools::W1infty_seminorm);
+ Linfty_error = difference_per_cell.linfty_norm();
+
+
+ convergence_table.add_value("cycle", it);
+ convergence_table.add_value("p", p);
+ convergence_table.add_value("L2", L2_error);
+ convergence_table.add_value("H1", H1_error);
+ convergence_table.add_value("Linfty", Linfty_error);
+ convergence_table.add_value("function value", phi_alpha);
+ convergence_table.add_value("derivative", phip);
+ }
+
+
+ /***************************************/
+// fill-in entry for the multiplier
+ template <int dim>
+ void ElastoplasticTorsion<dim>::process_multiplier (const unsigned int cycle, const int iter,double time)
+ {
+ const unsigned int n_active_cells=triangulation.n_active_cells();
+ const unsigned int n_dofs=dof_handler.n_dofs();
+ dual_L1_error=dual_error();
+ dual_L_infty_error=dual_infty_error();
+
+
+ dual_convergence_table.add_value("cycle", cycle);
+ dual_convergence_table.add_value("p", p);
+ dual_convergence_table.add_value("iteration_number", iter);
+ dual_convergence_table.add_value("cpu_time", time);
+ dual_convergence_table.add_value("cells", n_active_cells);
+ dual_convergence_table.add_value("dofs", n_dofs);
+ dual_convergence_table.add_value("L2", L2_error);
+ dual_convergence_table.add_value("H1", H1_error);
+ dual_convergence_table.add_value("Linfty", Linfty_error);
+ dual_convergence_table.add_value("dual_L1", dual_L1_error);
+ dual_convergence_table.add_value("dual_Linfty", dual_L_infty_error);
+
+ }
+
+
+
+
+ /****************************************************************************************/
+// ElastoplasticTorsion::setup_system
+// unchanged from step-15
+
+ template <int dim>
+ void ElastoplasticTorsion<dim>::setup_system (const bool initial_step)
+ {
+ if (initial_step)
+ {
+ dof_handler.distribute_dofs (fe);
+ present_solution.reinit (dof_handler.n_dofs());
+ grad_norm.reinit (dof_handler.n_dofs());
+ lambda.reinit (dof_handler.n_dofs());
+
+ hanging_node_constraints.clear ();
+ DoFTools::make_hanging_node_constraints (dof_handler,
+ hanging_node_constraints);
+ hanging_node_constraints.close ();
+ }
+
+
+ // The remaining parts of the function
+
+ newton_update.reinit (dof_handler.n_dofs());
+ system_rhs.reinit (dof_handler.n_dofs());
+ CompressedSparsityPattern c_sparsity(dof_handler.n_dofs());
+ DoFTools::make_sparsity_pattern (dof_handler, c_sparsity);
+ hanging_node_constraints.condense (c_sparsity);
+ sparsity_pattern.copy_from(c_sparsity);
+ system_matrix.reinit (sparsity_pattern);
+ }
+
+ /***************************************************************************************/
+ /* the coeffcients W, W' and G defining the problem.
+
+ Min_u \int W(|Du|^2) dx
+
+ They must be consistent as G(s)=W'(s)+2s W''(s) for any s>0.
+ recall that they receive the SQUARED gradient. */
+
+ template <int dim>
+ double ElastoplasticTorsion<dim>::W (double Du2) const
+ {
+ return Du2;
+ }
+
+ template <int dim>
+ double ElastoplasticTorsion<dim>::Wp (double Du2) const
+ {
+ return 1.0;
+ }
+
+ template <int dim>
+ double ElastoplasticTorsion<dim>::G (double Du2) const
+ {
+ return 1.0;
+ }
+ /***************************************************************************************/
+
+ template <int dim>
+ void ElastoplasticTorsion<dim>::assemble_system ()
+ {
+
+
+ const QGauss<dim> quadrature_formula(3);
+ const RightHandSide<dim> right_hand_side;
+ system_matrix = 0;
+ system_rhs = 0;
+
+ FEValues<dim> fe_values (fe, quadrature_formula,
+ update_gradients |
+ update_values |
+ update_quadrature_points |
+ update_JxW_values);
+
+ const unsigned int dofs_per_cell = fe.dofs_per_cell;
+ const unsigned int n_q_points = quadrature_formula.size();
+
+ FullMatrix<double> cell_matrix (dofs_per_cell, dofs_per_cell);
+ Vector<double> cell_rhs (dofs_per_cell);
+
+ std::vector<Tensor<1, dim> > old_solution_gradients(n_q_points);
+ std::vector<types::global_dof_index> local_dof_indices (dofs_per_cell);
+
+
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = dof_handler.begin_active(),
+ endc = dof_handler.end();
+ for (; cell!=endc; ++cell)
+ {
+ cell_matrix = 0;
+ cell_rhs = 0;
+
+ fe_values.reinit (cell);
+ fe_values.get_function_gradients(present_solution,
+ old_solution_gradients);
+
+ for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+ {
+ long double coeff=0.0;
+ long double a=old_solution_gradients[q_point] * old_solution_gradients[q_point];
+ long double exponent=(p-2.0)/2*std::log(a);
+ coeff= std::exp( exponent);
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ {
+ for (unsigned int j=0; j<dofs_per_cell; ++j)
+ {
+ if (dir_id==1)
+ {
+ cell_matrix(i, j) += fe_values.shape_grad(i, q_point) * fe_values.shape_grad(j, q_point)
+ * (G(a)+(p-1.0)*coeff) * fe_values.JxW(q_point);
+ }
+ else
+ {
+ cell_matrix(i, j) += fe_values.shape_grad(i, q_point) * fe_values.shape_grad(j, q_point)
+ * (Wp(a)+coeff)
+ * fe_values.JxW(q_point);
+ }
+ }
+
+ cell_rhs(i) -= ( fe_values.shape_grad(i, q_point)
+ * old_solution_gradients[q_point]
+ * (Wp(a)+coeff)
+ -right_hand_side.value(fe_values.quadrature_point(q_point))
+ *fe_values.shape_value(i, q_point)
+ )
+ * fe_values.JxW(q_point);
+ }
+ }
+
+ cell->get_dof_indices (local_dof_indices);
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ {
+ for (unsigned int j=0; j<dofs_per_cell; ++j)
+ system_matrix.add (local_dof_indices[i],
+ local_dof_indices[j],
+ cell_matrix(i,j));
+
+ system_rhs(local_dof_indices[i]) += cell_rhs(i);
+ }
+ }
+
+ hanging_node_constraints.condense (system_matrix);
+ hanging_node_constraints.condense (system_rhs);
+
+ std::map<types::global_dof_index,double> boundary_values;
+ VectorTools::interpolate_boundary_values (dof_handler,
+ 0,
+ ZeroFunction<dim>(),
+ boundary_values);
+ MatrixTools::apply_boundary_values (boundary_values,
+ system_matrix,
+ newton_update,
+ system_rhs);
+ }
+
+
+
+
+ /********************************** Refine Mesh ****************************************/
+// unchanged from step-15
+
+ template <int dim>
+ void ElastoplasticTorsion<dim>::refine_mesh ()
+ {
+ Vector<float> estimated_error_per_cell (triangulation.n_active_cells());
+ KellyErrorEstimator<dim>::estimate (dof_handler,
+ QGauss<dim-1>(3),
+ typename FunctionMap<dim>::type(),
+ present_solution,
+ estimated_error_per_cell);
+
+ prm.enter_subsection ("Mesh & Refinement Parameters");
+ const double top_fraction=prm.get_double("top_fraction_of_cells");
+ const double bottom_fraction=prm.get_double("bottom_fraction_of_cells");
+ prm.leave_subsection ();
+ GridRefinement::refine_and_coarsen_fixed_number (triangulation,
+ estimated_error_per_cell,
+ top_fraction, bottom_fraction);
+
+ triangulation.prepare_coarsening_and_refinement ();
+ SolutionTransfer<dim> solution_transfer(dof_handler);
+ solution_transfer.prepare_for_coarsening_and_refinement(present_solution);
+ triangulation.execute_coarsening_and_refinement();
+ dof_handler.distribute_dofs(fe);
+ Vector<double> tmp(dof_handler.n_dofs());
+ solution_transfer.interpolate(present_solution, tmp);
+ present_solution = tmp;
+ set_boundary_values ();
+ hanging_node_constraints.clear();
+
+ DoFTools::make_hanging_node_constraints(dof_handler,
+ hanging_node_constraints);
+ hanging_node_constraints.close();
+ hanging_node_constraints.distribute (present_solution);
+ setup_system (false);
+ }
+
+
+ /*******************************************************************************************/
+// Dump the norm of the gradient and the lagrange multiplier in vtu format for visualization
+ template <int dim>
+ void ElastoplasticTorsion<dim>::output_results (unsigned int counter) const
+ {
+ // multiplier object contains both |Du| and lambda.
+ ComputeMultiplier<dim> multiplier(p);
+ DataOut<dim> data_out;
+
+ data_out.attach_dof_handler (dof_handler);
+ data_out.add_data_vector (present_solution, "solution");
+ data_out.add_data_vector (present_solution, multiplier);
+ data_out.build_patches ();
+ std::ostringstream p_str;
+ p_str << p<<"-cycle-"<<counter;
+ std::string str = p_str.str();
+ const std::string filename = "solution-" + str+".vtu";
+ std::ofstream output (filename.c_str());
+ data_out.write_vtu (output);
+ }
+
+ /********************************************************************************************/
+// unchanged from step-15
+ template <int dim>
+ void ElastoplasticTorsion<dim>::set_boundary_values ()
+ {
+ std::map<types::global_dof_index, double> boundary_values;
+ VectorTools::interpolate_boundary_values (dof_handler,
+ 0,
+ BoundaryValues<dim>(),
+ boundary_values);
+ for (std::map<types::global_dof_index, double>::const_iterator
+ bp = boundary_values.begin();
+ bp != boundary_values.end(); ++bp)
+ present_solution(bp->first) = bp->second;
+ }
+
+
+ /****************************************************************************************/
+// COMPUTE \phi(\alpha)=J_p(u_h+\alpha w)
+ template <int dim>
+ double ElastoplasticTorsion<dim>::phi (const double alpha) const
+ {
+ double obj = 0.0;
+ const RightHandSide<dim> right_hand_side;
+ Vector<double> evaluation_point (dof_handler.n_dofs());
+ evaluation_point = present_solution; // copy of u_h
+ evaluation_point.add (alpha, newton_update); // u_{n+1}=u_n+alpha w_n
+
+ const QGauss<dim> quadrature_formula(3);
+ FEValues<dim> fe_values (fe, quadrature_formula,
+ update_gradients |
+ update_values |
+ update_quadrature_points |
+ update_JxW_values);
+
+ const unsigned int dofs_per_cell = fe.dofs_per_cell;
+ const unsigned int n_q_points = quadrature_formula.size();
+
+ Vector<double> cell_residual (dofs_per_cell);
+ std::vector<Tensor<1, dim> > gradients(n_q_points);
+ std::vector<double> values(n_q_points);
+
+
+ std::vector<types::global_dof_index> local_dof_indices (dofs_per_cell);
+
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = dof_handler.begin_active(),
+ endc = dof_handler.end();
+ for (; cell!=endc; ++cell)
+ {
+ cell_residual = 0;
+ fe_values.reinit (cell);
+ fe_values.get_function_gradients (evaluation_point, gradients);
+ fe_values.get_function_values (evaluation_point, values);
+
+
+ for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
+ {
+ double Du2=gradients[q_point] * gradients[q_point]; // Du2=|Du|^2
+ double penalty;
+ if (Du2<1.0e-10)
+ penalty=0.0;
+ else
+ penalty=std::pow(Du2,p/2.0); // penalty=|Du|^p
+
+ // obj+= 1/2 W(|Du|^2)+1/p |Du|^p -fu (see (1))
+ obj+=(
+ (0.5*W(Du2)+penalty/p)- right_hand_side.value(fe_values.quadrature_point(q_point))*values[q_point]
+ ) * fe_values.JxW(q_point);
+ }
+
+ }
+
+ return obj;
+ }
+
+
+ /***************************************************************************************************/
+// Compute L^1 error norm of Lagrange Multiplier
+// with respect to exact solution (cf. Alvarez & Flores, 2015)
+
+ template <int dim>
+ double ElastoplasticTorsion<dim>::dual_error () const
+ {
+ double obj = 0.0;
+
+ const QGauss<dim> quadrature_formula(3);
+ FEValues<dim> fe_values (fe, quadrature_formula,
+ update_gradients |
+ update_quadrature_points |
+ update_JxW_values);
+
+ const unsigned int dofs_per_cell = fe.dofs_per_cell;
+ const unsigned int n_q_points = quadrature_formula.size();
+
+ Vector<double> cell_residual (dofs_per_cell);
+ std::vector<Tensor<1, dim> > gradients(n_q_points);
+
+ std::vector<types::global_dof_index> local_dof_indices (dofs_per_cell);
+
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = dof_handler.begin_active(),
+ endc = dof_handler.end();
+ for (; cell!=endc; ++cell)
+ {
+ cell_residual = 0;
+ fe_values.reinit (cell);
+ fe_values.get_function_gradients (present_solution, gradients);
+
+ for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
+ {
+ double coeff=gradients[q_point] * gradients[q_point] ;
+ if (coeff<1.0e-15)
+ coeff=0.0;
+ else
+ coeff=std::pow(coeff,(p-2.0)/2.0); // |Du_p|^(p-2)
+
+ double r=std::sqrt(fe_values.quadrature_point(q_point).square());
+ double exact=0;
+ if (r>0.5)
+ exact= 2*r-1;
+
+ obj+=( std::abs(coeff-exact) ) * fe_values.JxW(q_point);
+ }
+
+ }
+
+ return obj;
+ }
+
+ /*******************************************************************************************/
+// Compute L^infinity error norm of Lagrange Multiplier
+// with respect to exact solution (cf. Alvarez & Flores, 2015)
+
+ template <int dim>
+ double ElastoplasticTorsion<dim>::dual_infty_error () const
+ {
+ double obj = 0.0;
+ const QTrapez<1> q_trapez;
+ const QIterated<dim> quadrature_formula (q_trapez, 10);
+
+ FEValues<dim> fe_values (fe, quadrature_formula,
+ update_gradients |
+ update_quadrature_points );
+
+ const unsigned int dofs_per_cell = fe.dofs_per_cell;
+ const unsigned int n_q_points = quadrature_formula.size();
+
+ Vector<double> cell_residual (dofs_per_cell);
+ std::vector<Tensor<1, dim> > gradients(n_q_points);
+
+ std::vector<types::global_dof_index> local_dof_indices (dofs_per_cell);
+
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = dof_handler.begin_active(),
+ endc = dof_handler.end();
+ for (; cell!=endc; ++cell)
+ {
+ cell_residual = 0;
+ fe_values.reinit (cell);
+ fe_values.get_function_gradients (present_solution, gradients);
+
+ for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
+ {
+ long double sqdGrad=gradients[q_point] * gradients[q_point] ;
+ double r=std::sqrt(fe_values.quadrature_point(q_point).square());
+ double exact=0;
+ if (r>0.5)
+ exact= 2*r-1.0;
+ // compute |Du|^(p-2) as exp(p-2/2*log(Du^2))
+ long double exponent=(p-2.0)/2*std::log(sqdGrad);
+ long double coeff=std::exp(exponent);
+
+ if (std::abs(coeff-exact)>obj )
+ obj=std::abs(coeff-exact);
+ }
+
+ }
+
+ return obj;
+ }
+
+ /*****************************************************************************************/
+// check whether putative step-length satisfies sufficient decrease conditions
+ template <int dim>
+ bool ElastoplasticTorsion<dim>::checkWolfe(double &alpha, double &phi_alpha) const
+ {
+ if (phi_alpha< phi_zero+line_search_tolerence*phip*alpha )
+ return true;
+ else
+ return false;
+ }
+
+
+ /*****************************************************************************************/
+// Find a step-length satisfying sufficient decrease condition by line-search
+// uses quadratic interpolation
+
+ template <int dim>
+ bool ElastoplasticTorsion<dim>::determine_step_length(const int inner_it)
+ {
+ unsigned int it=0;
+ bool done;
+ double alpha,nalpha;
+ prm.enter_subsection ("Algorithm Parameters");
+ const unsigned int max_LS_it=prm.get_integer("max_LS_it");
+ double init_SL=prm.get_double("init_step_length");
+ prm.leave_subsection ();
+ if (inner_it==0)
+ alpha=init_SL;
+ else
+ {
+ alpha=std::min(1.45*old_step*old_phip/phip,1.0);
+ }
+ phi_alpha=phi(alpha);
+ std::cerr << "Step length=" << alpha << ", Value= " << phi_alpha;
+ // check if step-size satisfies sufficient decrease condition
+ done=checkWolfe(alpha,phi_alpha);
+ if (done)
+ std::cerr << " accepted" << std::endl;
+ else
+ std::cerr << " rejected" ;
+
+ while ((!done) & (it<max_LS_it))
+ {
+ // new try obtained by quadratic interpolation
+ nalpha=-(phip*alpha*alpha)/(2*(phi_alpha-phi_zero-phip*alpha));
+
+ if (nalpha<1e-3*alpha || std::abs(nalpha-alpha)/alpha<1e-8)
+ nalpha=alpha/2;
+ else if ( phi_alpha-phi_zero>1e3*std::abs(phi_zero) )
+ nalpha=alpha/10;
+ alpha=nalpha;
+ phi_alpha=phi(alpha);
+ done=checkWolfe(alpha,phi_alpha);
+ if (done)
+ std::cerr << ", finished with steplength= "<< alpha<< ", fcn value= "<< phi_alpha<<std::endl;
+ it=it+1;
+ }
+ if (!done)
+ {
+ std::cerr << ", max. no. of iterations reached wiht steplength= "<< alpha
+ << ", fcn value= "<< phi_alpha<<std::endl;
+ return false;
+ }
+ else
+ {
+ step_length=alpha;
+ return true;
+ }
+
+ }
+
+ /**************************************************************************************************/
+ // ElastoplasticTorsion::init_mesh()
+
+ template <int dim>
+ void ElastoplasticTorsion<dim>::init_mesh ()
+ {
+ // get parameters
+ prm.enter_subsection ("Mesh & Refinement Parameters");
+ const int domain_id=prm.get_integer("Code for the domain");
+ const int init_ref=prm.get_integer("No of initial refinements");
+ prm.leave_subsection ();
+
+
+ if (domain_id==0)
+ {
+ // For the unit disk around the origin
+ GridGenerator::hyper_ball (triangulation);
+ static const HyperBallBoundary<dim> boundary;
+ triangulation.set_boundary (0, boundary);
+ }
+ else if (domain_id==1)
+ {
+ // For the unit square
+ GridGenerator::hyper_cube (triangulation, 0, 1);
+ }
+ else if (domain_id==2)
+ {
+ /* For Glowinski's domain
+ ___ ___ __ 1
+ | |__| | __ .8
+ | |
+ | |
+ |__________| __ 0
+
+ | | | |
+ 0 .4 .6 1
+
+ */
+ Triangulation<dim> tria1;
+ Triangulation<dim> tria2;
+ Triangulation<dim> tria3;
+ Triangulation<dim> tria4;
+ Triangulation<dim> tria5;
+ Triangulation<dim> tria6;
+ GridGenerator::hyper_rectangle(tria1, Point<2>(0.0,0.0), Point<2>(0.4,0.8));
+ GridGenerator::hyper_rectangle(tria2, Point<2>(0.0,0.8), Point<2>(0.4,1.0));
+ GridGenerator::hyper_rectangle(tria3, Point<2>(0.4,0.0), Point<2>(0.6,0.8));
+ GridGenerator::hyper_rectangle(tria4, Point<2>(0.6,0.0), Point<2>(1.0,0.8));
+ GridGenerator::hyper_rectangle(tria5, Point<2>(0.6,0.8), Point<2>(1.0,1.0));
+ GridGenerator::merge_triangulations (tria1, tria2, tria6);
+ GridGenerator::merge_triangulations (tria6, tria3, tria6);
+ GridGenerator::merge_triangulations (tria6, tria4, tria6);
+ GridGenerator::merge_triangulations (tria6, tria5, triangulation);
+ }
+ // perform initial refinements
+ triangulation.refine_global(init_ref);
+ }
+
+ /**************************************************************************************************/
+ // ElastoplasticTorsion::solve(inner_it)
+ // Performs one inner iteration
+
+ template <int dim>
+ bool ElastoplasticTorsion<dim>::solve (const int inner_it)
+ {
+ prm.enter_subsection ("Algorithm Parameters");
+ const unsigned int max_CG_it=prm.get_integer("Max_CG_it");
+ const double CG_tol=prm.get_double("CG_tol");
+ prm.leave_subsection ();
+
+ SolverControl solver_control (max_CG_it,CG_tol);
+ SolverCG<> solver (solver_control);
+
+ PreconditionSSOR<> preconditioner;
+ preconditioner.initialize(system_matrix,0.25);
+
+ solver.solve (system_matrix, newton_update, system_rhs,
+ preconditioner);
+ hanging_node_constraints.distribute (newton_update);
+ /****** save current quantities for line-search **** */
+ // Recall that phi(alpha)=J(u+alpha w)
+ old_step=step_length;
+ old_phi_zero=phi_zero;
+ phi_zero=phi(0); // phi(0)=J(u)
+ old_phip=phip;
+ phip=-1.0*(newton_update*system_rhs); //phi'(0)=J'(u) *w, rhs=-J'(u).
+ if (inner_it==0)
+ phip_zero=phip;
+
+ if (phip>0) // this should not happen, step back
+ {
+ std::cout << "Not a descent direction!" <<std::endl;
+ present_solution.add (-1.0*step_length, newton_update);
+ step_length=step_length/2;
+ phip=old_phip;
+ return false;
+ }
+ else
+ {
+ if (determine_step_length(inner_it))
+ {
+ // update u_{n+1}=u_n+alpha w_n
+ present_solution.add (step_length, newton_update);
+ return true;
+ }
+ else return false;
+ }
+ }
+
+
+
+ /*************************************************************************************************************/
+// ElastoplasticTorsion::run
+ template <int dim>
+ void ElastoplasticTorsion<dim>::run ()
+ {
+
+ // get parameters
+ prm.enter_subsection ("Mesh & Refinement Parameters");
+ const int adapt_ref=prm.get_integer("No of adaptive refinements");
+ prm.leave_subsection ();
+ prm.enter_subsection ("Algorithm Parameters");
+ const int max_inner=prm.get_integer("Max_inner");
+ const double eps=prm.get_double("eps");
+ const double hi_eps=prm.get_double("hi_eps");
+ const int hi_th=prm.get_integer("hi_th");
+ const double init_p=prm.get_double("init_p");
+ const double delta_p=prm.get_double("delta_p");
+ prm.leave_subsection ();
+ prm.enter_subsection ("Global Parameters");
+ bool known_solution=prm.get_bool("known_solution");
+ double actual_p=prm.get_double("p");
+ prm.leave_subsection ();
+ /************************/
+
+ // init Timer
+ Timer timer;
+ double ptime=0.0;
+ timer.start ();
+
+ // initalize mesh for the selected domain
+ init_mesh();
+
+ // setup FE space
+ setup_system (true);
+ set_boundary_values ();
+
+ // init counters
+ int global_it=0; // Total inner iterations (counting both loops)
+ int cycle=0; // Total outer iterations (counting both loops)
+ int refinement = 0; // Refinements performed (adaptive) = outer iterations 2nd loop
+
+
+ // prepare to start first loop
+ p=init_p;
+ bool well_solved=true;
+
+ /***************************** First loop ***********************************/
+ /****************** Prepare initial condition using increasing p *************************/
+ while (p<actual_p) // outer iteration, increasing p.
+ {
+ std::cout <<"--Preparing initial condition with p="<<p<<" iter.= " << global_it<< " .-- "<< std::endl;
+ timer.restart();
+ for (int inner_iteration=0; inner_iteration<max_inner; ++inner_iteration,++global_it)
+ {
+ assemble_system ();
+ well_solved=solve (inner_iteration);
+ print_it_message (global_it, known_solution);
+ if (
+ ((system_rhs.l2_norm()/std::sqrt(system_rhs.size()) <1e-4) & (cycle<1)) |
+ ((system_rhs.l2_norm()/std::sqrt(system_rhs.size()) <1e-5) & (cycle>=1)) |
+ !well_solved
+ )
+ break;
+ }
+ ptime=timer();
+ if (well_solved)
+ output_results (cycle);
+
+ if (known_solution)
+ {
+ process_multiplier(cycle,global_it,ptime);
+ //dual_convergence_table.write_tex(dual_error_table_file);
+ }
+ refine_mesh();
+ cycle++;
+ p+=delta_p;
+ }
+ /*************************** first loop finished ********************/
+
+
+ // prepare for second loop
+ p=actual_p;
+ well_solved=true;
+
+
+ /***************************** Second loop *********************************/
+ /**************************** Solve problem for target p *********************************/
+
+ std::cout << "============ Solving problem with p=" <<p << " ==================" << std::endl;
+ /***** Outer iteration - refining mesh ******************/
+ while ((cycle<adapt_ref) & well_solved)
+ {
+ timer.restart();
+ // inner iteration
+ for (int inner_iteration=0; inner_iteration<max_inner; ++inner_iteration,++global_it)
+ {
+ assemble_system ();
+ well_solved=solve (inner_iteration);
+ print_it_message (global_it, known_solution);
+
+ if (
+ ((system_rhs.l2_norm()/std::sqrt(system_rhs.size()) < eps) & (refinement<hi_th)) |
+ (( system_rhs.l2_norm()/ std::sqrt (system_rhs.size()) <hi_eps) | (!well_solved))
+ )
+ break;
+ }
+ //inner iterations finished
+ ptime=timer();
+ if (well_solved)
+ output_results (cycle);
+
+ // compute and display error, if the explicit solution is known
+ if (known_solution)
+ {
+ process_multiplier(cycle,global_it,ptime);
+ std::cout << "finished with H1 error: " << H1_error << ", dual error (L1): "
+ << dual_L1_error << "dual error (L infty): "<<dual_L_infty_error <<std::endl;
+ }
+
+ // update counters
+ ++refinement;
+ ++cycle;
+ // refine mesh
+ std::cout << "******** Refined mesh " << cycle << " ********" << std::endl;
+ refine_mesh();
+ }// second loop
+
+ // write convergence tables to file
+ if (known_solution)
+ {
+ format_convergence_tables();
+ std::string error_filename = "error"+Method+elements+".tex";
+ std::ofstream error_table_file(error_filename.c_str());
+ std::string dual_error_filename = "dual_error"+Method+elements+".tex";
+ std::ofstream dual_error_table_file(dual_error_filename.c_str());
+ convergence_table.write_tex(error_table_file);
+ dual_convergence_table.write_tex(dual_error_table_file);
+ }
+ }//run()
+
+}//namespace
+
+/**********************************************************************************************/
+// The main function
+int main ()
+{
+ try
+ {
+ using namespace dealii;
+ using namespace nsp;
+ deallog.depth_console (0);
+
+ ParameterHandler prm;
+ ParameterReader param(prm);
+ param.read_parameters("EPT.prm");
+ ElastoplasticTorsion<2> ElastoplasticTorsionProblem(prm);
+ ElastoplasticTorsionProblem .run ();
+ }
+ catch (std::exception &exc)
+ {
+ std::cerr << std::endl << std::endl
+ << "----------------------------------------------------" << std::endl;
+ std::cerr << "Exception on processing: " << std::endl
+ << exc.what() << std::endl
+ << "Aborting!" << std::endl
+ << "----------------------------------------------------"
+ << std::endl;
+
+ return 1;
+ }
+ catch (...)
+ {
+ std::cerr << std::endl << std::endl
+ << "----------------------------------------------------"
+ << std::endl;
+ std::cerr << "Unknown exception!" << std::endl
+ << "Aborting!" << std::endl
+ << "----------------------------------------------------"
+ << std::endl;
+ return 1;
+ }
+ return 0;
+}