$extrastylesheet
trilinos_nox_nonlinear_solver.C
Go to the documentation of this file.
00001 // The libMesh Finite Element Library.
00002 // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
00003 
00004 // This library is free software; you can redistribute it and/or
00005 // modify it under the terms of the GNU Lesser General Public
00006 // License as published by the Free Software Foundation; either
00007 // version 2.1 of the License, or (at your option) any later version.
00008 
00009 // This library is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012 // Lesser General Public License for more details.
00013 
00014 // You should have received a copy of the GNU Lesser General Public
00015 // License along with this library; if not, write to the Free Software
00016 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017 
00018 
00019 
00020 #include "libmesh/libmesh_common.h"
00021 
00022 #ifdef LIBMESH_HAVE_NOX
00023 
00024 
00025 // C++ includes
00026 
00027 // Local Includes
00028 #include "libmesh/libmesh_logging.h"
00029 #include "libmesh/dof_map.h"
00030 #include "libmesh/nonlinear_implicit_system.h"
00031 #include "libmesh/trilinos_nox_nonlinear_solver.h"
00032 #include "libmesh/system.h"
00033 #include "libmesh/trilinos_epetra_vector.h"
00034 #include "libmesh/trilinos_epetra_matrix.h"
00035 #include "libmesh/trilinos_preconditioner.h"
00036 
00037 // ---------- Standard Includes ----------
00038 #include <iostream>
00039 #include "Epetra_Vector.h"
00040 #include "Epetra_Operator.h"
00041 #include "Epetra_RowMatrix.h"
00042 #include "NOX_Epetra_Interface_Required.H" // base class
00043 #include "NOX_Epetra_Interface_Jacobian.H" // base class
00044 #include "NOX_Epetra_Interface_Preconditioner.H" // base class
00045 #include "NOX_Epetra_MatrixFree.H"
00046 #include "NOX_Epetra_LinearSystem_AztecOO.H"
00047 #include "NOX_Epetra_Group.H"// class definition
00048 #include "NOX_Epetra_Vector.H"
00049 
00050 namespace libMesh
00051 {
00052 
00053 class Problem_Interface : public NOX::Epetra::Interface::Required,
00054                           public NOX::Epetra::Interface::Jacobian,
00055                           public NOX::Epetra::Interface::Preconditioner
00056 {
00057 public:
00058   explicit
00059   Problem_Interface(NoxNonlinearSolver<Number> * solver);
00060 
00061   ~Problem_Interface();
00062 
00064   bool computeF(const Epetra_Vector& x, Epetra_Vector& FVec,
00065                 NOX::Epetra::Interface::Required::FillType fillType);
00066 
00068   bool computeJacobian(const Epetra_Vector& x, Epetra_Operator& Jac);
00069 
00071   bool computePrecMatrix(const Epetra_Vector& x, Epetra_RowMatrix& M);
00072 
00074   bool computePreconditioner(const Epetra_Vector& x, Epetra_Operator& Prec,
00075                              Teuchos::ParameterList* p);
00076 
00077   NoxNonlinearSolver<Number> * _solver;
00078 };
00079 
00080 //-----------------------------------------------------------------------------
00081 Problem_Interface::Problem_Interface(NoxNonlinearSolver<Number> * solver) :
00082   _solver(solver)
00083 { }
00084 
00085 Problem_Interface::~Problem_Interface()
00086 { }
00087 
00088 bool Problem_Interface::computeF(const Epetra_Vector& x, Epetra_Vector& r,
00089                                  NOX::Epetra::Interface::Required::FillType /*fillType*/)
00090 {
00091   START_LOG("residual()", "TrilinosNoxNonlinearSolver");
00092 
00093   NonlinearImplicitSystem &sys = _solver->system();
00094 
00095   EpetraVector<Number> X_global(*const_cast<Epetra_Vector *>(&x), sys.comm()), R(r, sys.comm());
00096   EpetraVector<Number>& X_sys = *cast_ptr<EpetraVector<Number>*>(sys.solution.get());
00097   EpetraVector<Number>& R_sys = *cast_ptr<EpetraVector<Number>*>(sys.rhs);
00098 
00099   // Use the systems update() to get a good local version of the parallel solution
00100   X_global.swap(X_sys);
00101   R.swap(R_sys);
00102 
00103   sys.get_dof_map().enforce_constraints_exactly(sys);
00104   sys.update();
00105 
00106   // Swap back
00107   X_global.swap(X_sys);
00108   R.swap(R_sys);
00109 
00110   R.zero();
00111 
00112   //-----------------------------------------------------------------------------
00113   // if the user has provided both function pointers and objects only the pointer
00114   // will be used, so catch that as an error
00115 
00116   if (_solver->residual && _solver->residual_object)
00117     libmesh_error_msg("ERROR: cannot specifiy both a function and object to compute the Residual!");
00118 
00119   if (_solver->matvec && _solver->residual_and_jacobian_object)
00120     libmesh_error_msg("ERROR: cannot specifiy both a function and object to compute the combined Residual & Jacobian!");
00121 
00122   if      (_solver->residual != NULL)                     _solver->residual                                            (*sys.current_local_solution.get(), R, sys);
00123   else if (_solver->residual_object != NULL)              _solver->residual_object->residual                           (*sys.current_local_solution.get(), R, sys);
00124   else if (_solver->matvec   != NULL)                     _solver->matvec                                              (*sys.current_local_solution.get(), &R, NULL, sys);
00125   else if (_solver->residual_and_jacobian_object != NULL) _solver->residual_and_jacobian_object->residual_and_jacobian (*sys.current_local_solution.get(), &R, NULL, sys);
00126   else return false;
00127 
00128   R.close();
00129   X_global.close();
00130 
00131   STOP_LOG("residual()", "TrilinosNoxNonlinearSolver");
00132 
00133   return true;
00134 }
00135 
00136 bool Problem_Interface::computeJacobian(const Epetra_Vector & x,
00137                                         Epetra_Operator & jac)
00138 {
00139   START_LOG("jacobian()", "TrilinosNoxNonlinearSolver");
00140 
00141   NonlinearImplicitSystem &sys = _solver->system();
00142 
00143   EpetraMatrix<Number> Jac(&dynamic_cast<Epetra_FECrsMatrix &>(jac), sys.comm());
00144   EpetraVector<Number>& X_sys = *cast_ptr<EpetraVector<Number>*>(sys.solution.get());
00145   EpetraVector<Number> X_global(*const_cast<Epetra_Vector *>(&x), sys.comm());
00146 
00147   // Set the dof maps
00148   Jac.attach_dof_map(sys.get_dof_map());
00149 
00150   // Use the systems update() to get a good local version of the parallel solution
00151   X_global.swap(X_sys);
00152 
00153   sys.get_dof_map().enforce_constraints_exactly(sys);
00154   sys.update();
00155 
00156   X_global.swap(X_sys);
00157 
00158   //-----------------------------------------------------------------------------
00159   // if the user has provided both function pointers and objects only the pointer
00160   // will be used, so catch that as an error
00161   if (_solver->jacobian && _solver->jacobian_object)
00162     libmesh_error_msg("ERROR: cannot specify both a function and object to compute the Jacobian!");
00163 
00164   if (_solver->matvec && _solver->residual_and_jacobian_object)
00165     libmesh_error_msg("ERROR: cannot specify both a function and object to compute the combined Residual & Jacobian!");
00166 
00167   if      (_solver->jacobian != NULL)                     _solver->jacobian                                            (*sys.current_local_solution.get(), Jac, sys);
00168   else if (_solver->jacobian_object != NULL)              _solver->jacobian_object->jacobian                           (*sys.current_local_solution.get(), Jac, sys);
00169   else if (_solver->matvec   != NULL)                     _solver->matvec                                              (*sys.current_local_solution.get(), NULL, &Jac, sys);
00170   else if (_solver->residual_and_jacobian_object != NULL) _solver->residual_and_jacobian_object->residual_and_jacobian (*sys.current_local_solution.get(), NULL, &Jac, sys);
00171   else
00172     libmesh_error_msg("Error! Unable to compute residual and/or Jacobian!");
00173 
00174   Jac.close();
00175   X_global.close();
00176 
00177   STOP_LOG("jacobian()", "TrilinosNoxNonlinearSolver");
00178 
00179   return true;
00180 }
00181 
00182 bool Problem_Interface::computePrecMatrix(const Epetra_Vector & /*x*/, Epetra_RowMatrix & /*M*/)
00183 {
00184   //   libMesh::out << "ERROR: Problem_Interface::preconditionVector() - Use Explicit Jacobian only for this test problem!" << endl;
00185   throw 1;
00186 }
00187 
00188 bool Problem_Interface::computePreconditioner(const Epetra_Vector & x,
00189                                               Epetra_Operator & prec,
00190                                               Teuchos::ParameterList * /*p*/)
00191 {
00192   START_LOG("preconditioner()", "TrilinosNoxNonlinearSolver");
00193 
00194   NonlinearImplicitSystem &sys = _solver->system();
00195   TrilinosPreconditioner<Number> & tpc = dynamic_cast<TrilinosPreconditioner<Number> &>(prec);
00196 
00197   EpetraMatrix<Number> Jac(dynamic_cast<Epetra_FECrsMatrix *>(tpc.mat()),sys.comm());
00198   EpetraVector<Number>& X_sys = *cast_ptr<EpetraVector<Number>*>(sys.solution.get());
00199   EpetraVector<Number> X_global(*const_cast<Epetra_Vector *>(&x), sys.comm());
00200 
00201   // Set the dof maps
00202   Jac.attach_dof_map(sys.get_dof_map());
00203 
00204   // Use the systems update() to get a good local version of the parallel solution
00205   X_global.swap(X_sys);
00206 
00207   sys.get_dof_map().enforce_constraints_exactly(sys);
00208   sys.update();
00209 
00210   X_global.swap(X_sys);
00211 
00212   //-----------------------------------------------------------------------------
00213   // if the user has provided both function pointers and objects only the pointer
00214   // will be used, so catch that as an error
00215   if (_solver->jacobian && _solver->jacobian_object)
00216     libmesh_error_msg("ERROR: cannot specify both a function and object to compute the Jacobian!");
00217 
00218   if (_solver->matvec && _solver->residual_and_jacobian_object)
00219     libmesh_error_msg("ERROR: cannot specify both a function and object to compute the combined Residual & Jacobian!");
00220 
00221   if      (_solver->jacobian != NULL)                     _solver->jacobian                                            (*sys.current_local_solution.get(), Jac, sys);
00222   else if (_solver->jacobian_object != NULL)              _solver->jacobian_object->jacobian                           (*sys.current_local_solution.get(), Jac, sys);
00223   else if (_solver->matvec   != NULL)                     _solver->matvec                                              (*sys.current_local_solution.get(), NULL, &Jac, sys);
00224   else if (_solver->residual_and_jacobian_object != NULL) _solver->residual_and_jacobian_object->residual_and_jacobian (*sys.current_local_solution.get(), NULL, &Jac, sys);
00225   else
00226     libmesh_error_msg("Error! Unable to compute residual and/or Jacobian!");
00227 
00228   Jac.close();
00229   X_global.close();
00230 
00231   tpc.compute();
00232 
00233   STOP_LOG("preconditioner()", "TrilinosNoxNonlinearSolver");
00234 
00235   return true;
00236 }
00237 
00238 
00239 //---------------------------------------------------------------------
00240 // NoxNonlinearSolver<> methods
00241 template <typename T>
00242 void NoxNonlinearSolver<T>::clear ()
00243 {
00244 }
00245 
00246 template <typename T>
00247 void NoxNonlinearSolver<T>::init (const char * /*name*/)
00248 {
00249   if (!this->initialized())
00250     _interface = new Problem_Interface(this);
00251 }
00252 
00253 template <typename T>
00254 std::pair<unsigned int, Real>
00255 NoxNonlinearSolver<T>::solve (SparseMatrix<T>&  /* jac_in */,  // System Jacobian Matrix
00256                               NumericVector<T>& x_in,          // Solution vector
00257                               NumericVector<T>& /* r_in */,    // Residual vector
00258                               const double,                    // Stopping tolerance
00259                               const unsigned int)
00260 {
00261   this->init ();
00262 
00263   if (this->user_presolve)
00264     this->user_presolve(this->system());
00265 
00266   EpetraVector<T> * x_epetra = cast_ptr<EpetraVector<T>*>(&x_in);
00267   // Creating a Teuchos::RCP as they do in NOX examples does not work here - we get some invalid memory references
00268   // thus we make a local copy
00269   NOX::Epetra::Vector x(*x_epetra->vec());
00270 
00271   Teuchos::RCP<Teuchos::ParameterList> nlParamsPtr = Teuchos::rcp(new Teuchos::ParameterList);
00272   Teuchos::ParameterList& nlParams = *(nlParamsPtr.get());
00273   nlParams.set("Nonlinear Solver", "Line Search Based");
00274 
00275   //print params
00276   Teuchos::ParameterList& printParams = nlParams.sublist("Printing");
00277   printParams.set("Output Precision", 3);
00278   printParams.set("Output Processor", 0);
00279   printParams.set("Output Information",
00280                   NOX::Utils::OuterIteration +
00281                   NOX::Utils::OuterIterationStatusTest +
00282                   NOX::Utils::InnerIteration +
00283                   NOX::Utils::LinearSolverDetails +
00284                   NOX::Utils::Parameters +
00285                   NOX::Utils::Details +
00286                   NOX::Utils::Warning);
00287 
00288   Teuchos::ParameterList& dirParams = nlParams.sublist("Direction");
00289   dirParams.set("Method", "Newton");
00290   Teuchos::ParameterList& newtonParams = dirParams.sublist("Newton");
00291   newtonParams.set("Forcing Term Method", "Constant");
00292 
00293   Teuchos::ParameterList& lsParams = newtonParams.sublist("Linear Solver");
00294   lsParams.set("Aztec Solver", "GMRES");
00295   lsParams.set("Max Iterations", static_cast<int>(this->max_linear_iterations));
00296   lsParams.set("Tolerance", this->initial_linear_tolerance);
00297   lsParams.set("Output Frequency", 1);
00298   lsParams.set("Size of Krylov Subspace", 1000);
00299 
00300   //create linear system
00301   Teuchos::RCP<NOX::Epetra::Interface::Required> iReq(_interface);
00302   Teuchos::RCP<NOX::Epetra::LinearSystemAztecOO> linSys;
00303   Teuchos::RCP<Epetra_Operator> pc;
00304 
00305   if (this->jacobian || this->jacobian_object || this->residual_and_jacobian_object)
00306     {
00307       if(this->_preconditioner)
00308         {
00309           // PJNFK
00310           lsParams.set("Preconditioner", "User Defined");
00311 
00312           TrilinosPreconditioner<Number> * trilinos_pc =
00313             cast_ptr<TrilinosPreconditioner<Number> *>(this->_preconditioner);
00314           pc = Teuchos::rcp(trilinos_pc);
00315 
00316           Teuchos::RCP<NOX::Epetra::Interface::Preconditioner> iPrec(_interface);
00317           linSys = Teuchos::rcp(new NOX::Epetra::LinearSystemAztecOO(printParams, lsParams, iReq, iPrec, pc, x));
00318         }
00319       else
00320         {
00321           lsParams.set("Preconditioner", "None");
00322           //      lsParams.set("Preconditioner", "Ifpack");
00323           //      lsParams.set("Preconditioner", "AztecOO");
00324 
00325           // full jacobian
00326           NonlinearImplicitSystem & sys = _interface->_solver->system();
00327           EpetraMatrix<Number> & jacSys = *cast_ptr<EpetraMatrix<Number>*>(sys.matrix);
00328           Teuchos::RCP<Epetra_RowMatrix> jacMat = Teuchos::rcp(jacSys.mat());
00329 
00330           Teuchos::RCP<NOX::Epetra::Interface::Jacobian> iJac(_interface);
00331           linSys = Teuchos::rcp(new NOX::Epetra::LinearSystemAztecOO(printParams, lsParams, iReq, iJac, jacMat, x));
00332         }
00333     }
00334   else
00335     {
00336       // matrix free
00337       Teuchos::RCP<NOX::Epetra::MatrixFree> MF = Teuchos::rcp(new NOX::Epetra::MatrixFree(printParams, iReq, x));
00338 
00339       Teuchos::RCP<NOX::Epetra::Interface::Jacobian> iJac(MF);
00340       linSys = Teuchos::rcp(new NOX::Epetra::LinearSystemAztecOO(printParams, lsParams, iReq, iJac, MF, x));
00341     }
00342 
00343   //create group
00344   Teuchos::RCP<NOX::Epetra::Group> grpPtr = Teuchos::rcp(new NOX::Epetra::Group(printParams, iReq, x, linSys));
00345   NOX::Epetra::Group& grp = *(grpPtr.get());
00346 
00347   Teuchos::RCP<NOX::StatusTest::NormF> absresid =
00348     Teuchos::rcp(new NOX::StatusTest::NormF(this->absolute_residual_tolerance, NOX::StatusTest::NormF::Unscaled));
00349   Teuchos::RCP<NOX::StatusTest::NormF> relresid =
00350     Teuchos::rcp(new NOX::StatusTest::NormF(grp, this->relative_residual_tolerance));
00351   Teuchos::RCP<NOX::StatusTest::MaxIters> maxiters =
00352     Teuchos::rcp(new NOX::StatusTest::MaxIters(this->max_nonlinear_iterations));
00353   Teuchos::RCP<NOX::StatusTest::FiniteValue> finiteval =
00354     Teuchos::rcp(new NOX::StatusTest::FiniteValue());
00355   Teuchos::RCP<NOX::StatusTest::NormUpdate> normupdate =
00356     Teuchos::rcp(new NOX::StatusTest::NormUpdate(this->absolute_step_tolerance));
00357   Teuchos::RCP<NOX::StatusTest::Combo> combo =
00358     Teuchos::rcp(new NOX::StatusTest::Combo(NOX::StatusTest::Combo::OR));
00359   combo->addStatusTest(absresid);
00360   combo->addStatusTest(relresid);
00361   combo->addStatusTest(maxiters);
00362   combo->addStatusTest(finiteval);
00363   combo->addStatusTest(normupdate);
00364 
00365   Teuchos::RCP<Teuchos::ParameterList> finalPars = nlParamsPtr;
00366 
00367   Teuchos::RCP<NOX::Solver::Generic> solver = NOX::Solver::buildSolver(grpPtr, combo, nlParamsPtr);
00368   NOX::StatusTest::StatusType status = solver->solve();
00369   this->converged = (status == NOX::StatusTest::Converged);
00370 
00371   const NOX::Epetra::Group& finalGroup = dynamic_cast<const NOX::Epetra::Group&>(solver->getSolutionGroup());
00372   const NOX::Epetra::Vector& noxFinalSln = dynamic_cast<const NOX::Epetra::Vector&>(finalGroup.getX());
00373 
00374   *x_epetra->vec() = noxFinalSln.getEpetraVector();
00375   x_in.close();
00376 
00377   Real residual_norm = finalGroup.getNormF();
00378   unsigned int total_iters = solver->getNumIterations();
00379   _n_linear_iterations = finalPars->sublist("Direction").sublist("Newton").sublist("Linear Solver").sublist("Output").get("Total Number of Linear Iterations", -1);
00380 
00381   // do not let Trilinos to deallocate what we allocated
00382   pc.release();
00383   iReq.release();
00384 
00385   return std::make_pair(total_iters, residual_norm);
00386 }
00387 
00388 template <typename T>
00389 int
00390 NoxNonlinearSolver<T>::get_total_linear_iterations()
00391 {
00392   return _n_linear_iterations;
00393 }
00394 
00395 
00396 
00397 //------------------------------------------------------------------
00398 // Explicit instantiations
00399 template class NoxNonlinearSolver<Number>;
00400 
00401 } // namespace libMesh
00402 
00403 
00404 
00405 #endif // #ifdef LIBMESH_HAVE_NOX