HermesCommon  2.0
newton_solver_nox.cpp
Go to the documentation of this file.
1 // This file is part of HermesCommon
2 //
3 // Hermes2D is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 2 of the License, or
6 // (at your option) any later version.
7 //
8 // Hermes2D is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with Hermes2D. If not, see <http://www.gnu.org/licenses/>.
18 #include "newton_solver_nox.h"
19 #if(defined HAVE_NOX && defined HAVE_EPETRA && defined HAVE_TEUCHOS)
20 
21 namespace Hermes
22 {
23  namespace Solvers
24  {
25  static Epetra_SerialComm seq_comm;
26 
27  template<typename Scalar>
28  DiscreteProblemNOX<Scalar>::DiscreteProblemNOX(DiscreteProblemInterface<Scalar>* problem) : dp(problem)
29  {
30  this->precond = Teuchos::null;
31  if(!this->dp->is_matrix_free())
32  this->dp->create_sparse_structure(&jacobian);
33  }
34 
35  template<typename Scalar>
36  bool DiscreteProblemNOX<Scalar>::computeF(const Epetra_Vector &x, Epetra_Vector &f, FillType flag)
37  {
38  EpetraVector<Scalar> xx(x); // wrap our structures around core Epetra objects
39  EpetraVector<Scalar> rhs(f);
40 
41  rhs.zero();
42 
43  Scalar* coeff_vec = new Scalar[xx.length()];
44  xx.extract(coeff_vec);
45  this->dp->assemble(coeff_vec, NULL, &rhs); // NULL is for the global matrix.
46  delete [] coeff_vec;
47 
48  return true;
49  }
50 
51  template<typename Scalar>
52  bool DiscreteProblemNOX<Scalar>::computeJacobian(const Epetra_Vector &x, Epetra_Operator &op)
53  {
54  Epetra_RowMatrix *jac = dynamic_cast<Epetra_RowMatrix *>(&op);
55  assert(jac != NULL);
56 
57  EpetraVector<Scalar> xx(x); // wrap our structures around core Epetra objects
58  EpetraMatrix<Scalar> jacob(*jac);
59 
60  jacob.zero();
61 
62  Scalar* coeff_vec = new Scalar[xx.length()];
63  xx.extract(coeff_vec);
64  this->dp->assemble(coeff_vec, &jacob, NULL); // NULL is for the right-hand side.
65  delete [] coeff_vec;
66  //jacob.finish();
67 
68  return true;
69  }
70 
71  template<typename Scalar>
72  bool DiscreteProblemNOX<Scalar>::computePreconditioner(const Epetra_Vector &x, Epetra_Operator &m,
73  Teuchos::ParameterList *precParams)
74  {
75  assert(precond != Teuchos::null);
76  EpetraVector<Scalar> xx(x); // wrap our structures around core Epetra objects
77 
78  jacobian.zero();
79 
80  Scalar* coeff_vec = new Scalar[xx.length()];
81  xx.extract(coeff_vec);
82  this->dp->assemble(coeff_vec, &jacobian, NULL); // NULL is for the right-hand side.
83  delete [] coeff_vec;
84  //jacobian.finish();
85 
86  precond->create(&jacobian);
87  precond->compute();
88  m = *precond->get_obj();
89 
90  return true;
91  }
92 
93  template<typename Scalar>
94  Teuchos::RCP<Precond<Scalar> > DiscreteProblemNOX<Scalar>::get_precond()
95  {
96  return precond;
97  }
98 
99  template<typename Scalar>
100  EpetraMatrix<Scalar> *DiscreteProblemNOX<Scalar>::get_jacobian()
101  {
102  return &jacobian;
103  }
104 
105  template<typename Scalar>
106  NewtonSolverNOX<Scalar>::NewtonSolverNOX(DiscreteProblemInterface<Scalar>* problem) : NonlinearSolver<Scalar>(problem), ndp(problem)
107  {
108  // default values
109  // convergence test
110  conv.max_iters = 10;
111  conv.abs_resid = 1.0e-6;
112  conv.rel_resid = 1.0e-2;
113  conv.norm_type = NOX::Abstract::Vector::TwoNorm;
114  conv.stype = NOX::StatusTest::NormF::Scaled;
115  conv.update = 1.0e-5;
116  conv.wrms_rtol = 1.0e-2;
117  conv.wrms_atol = 1.0e-8;
118 
119  conv_flag.absresid = 1;
120  conv_flag.relresid = 0;
121  conv_flag.update = 0;
122  conv_flag.wrms = 0;
123 
124  nl_pars = Teuchos::rcp(new Teuchos::ParameterList);
125  // Set the nonlinear solver method
126  nl_pars->set("Nonlinear Solver", "Line Search Based");
127  nl_pars->sublist("Printing").set("Output Information", NOX::Utils::Error);
128 
129  // Sublist for line search
130  Teuchos::ParameterList &search_pars = nl_pars->sublist("Line Search");
131  search_pars.set("Method", "Full Step");
132 
133  // Sublist for direction
134  Teuchos::ParameterList &dir_pars = nl_pars->sublist("Direction");
135  dir_pars.set("Method", "Newton");
136  Teuchos::ParameterList &newton_pars = dir_pars.sublist("Newton");
137 
138  newton_pars.set("Forcing Term Method", "Constant");
139 
140  // Sublist for linear solver for the Newton method
141  Teuchos::ParameterList &ls_pars = newton_pars.sublist("Linear Solver");
142  ls_pars.set("Aztec Solver", "GMRES");
143  ls_pars.set("Max Iterations", 800);
144  ls_pars.set("Tolerance", 1e-8);
145  ls_pars.set("Size of Krylov Subspace", 50);
146  ls_pars.set("Preconditioner Reuse Policy", "Recompute");
147  ls_pars.set("Output Frequency", AZ_all);
148  ls_pars.set("Max Age Of Prec", 999);
149  }
150 
151  template<typename Scalar>
152  void NewtonSolverNOX<Scalar>::set_time(double time)
153  {
154  }
155 
156  template<typename Scalar>
157  void NewtonSolverNOX<Scalar>::set_time_step(double time_step)
158  {
159  }
160 
161  template<typename Scalar>
162  NewtonSolverNOX<Scalar>::~NewtonSolverNOX()
163  {
164  // FIXME: this does not destroy the "interface_", and Trilinos
165  // complains at closing main.cpp.
166  }
167 
168  template<typename Scalar>
169  void DiscreteProblemNOX<Scalar>::set_precond(Teuchos::RCP<Precond<Scalar> > &pc)
170  {
171  precond = pc;
172  this->dp->create_sparse_structure(&jacobian);
173  }
174 
175  template<typename Scalar>
176  void NewtonSolverNOX<Scalar>::set_precond(Precond<Scalar> &pc)
177  {
178  Teuchos::RCP<Precond<Scalar> > tpc = Teuchos::rcpFromRef(pc);
179  ndp.set_precond(tpc);
180  nl_pars->sublist("Direction").sublist("Newton").sublist("Linear Solver").set("Preconditioner", "User Defined");
181  }
182 
183  template<typename Scalar>
184  void NewtonSolverNOX<Scalar>::set_precond(const char *pc)
185  {
186  nl_pars->sublist("Direction").sublist("Newton").sublist("Linear Solver").set("Preconditioner", pc);
187  }
188 
189  template<typename Scalar>
190  void NewtonSolverNOX<Scalar>::set_output_flags(int flags)
191  {
192  nl_pars->sublist("Printing").set("Output Information", flags);
193  }
194 
195  template<typename Scalar>
196  void NewtonSolverNOX<Scalar>::set_ls_type(const char *type)
197  {
198  nl_pars->sublist("Direction").sublist("Newton").sublist("Linear Solver").set("Aztec Solver", type);
199  }
200 
201  template<typename Scalar>
202  void NewtonSolverNOX<Scalar>::set_ls_max_iters(int iters)
203  {
204  nl_pars->sublist("Direction").sublist("Newton").sublist("Linear Solver").set("Max Iterations", iters);
205  }
206 
207  template<typename Scalar>
208  void NewtonSolverNOX<Scalar>::set_ls_tolerance(double tolerance)
209  {
210  nl_pars->sublist("Direction").sublist("Newton").sublist("Linear Solver").set("Tolerance", tolerance);
211  }
212 
213  template<typename Scalar>
214  void NewtonSolverNOX<Scalar>::set_ls_sizeof_krylov_subspace(int size)
215  {
216  nl_pars->sublist("Direction").sublist("Newton").sublist("Linear Solver").set("Size of Krylov Subspace", size);
217  }
218 
219  template<typename Scalar>
220  void NewtonSolverNOX<Scalar>::set_precond_reuse(const char * pc_reuse)
221  {
222  nl_pars->sublist("Direction").sublist("Newton").sublist("Linear Solver").set("Preconditioner Reuse Policy", pc_reuse);
223  }
224  template<typename Scalar>
225  void NewtonSolverNOX<Scalar>::set_precond_max_age(int max_age)
226  {
227  nl_pars->sublist("Direction").sublist("Newton").sublist("Linear Solver").set("Max Age Of Prec", max_age);
228  }
229 
230  template<typename Scalar>
231  void NewtonSolverNOX<Scalar>::solve(Scalar* coeff_vec)
232  {
233  // Put the initial coeff_vec into the inner structure for the initial guess.
234  Hermes::Algebra::EpetraVector<Scalar> temp_init_sln;
235  temp_init_sln.alloc(this->dp->get_num_dofs());
236  for (int i = 0; i < this->dp->get_num_dofs(); i++)
237  temp_init_sln.set(i, coeff_vec[i]);
238  NOX::Epetra::Vector init_sln(*temp_init_sln.vec);
239 
240  // prepare variables
241  // print settings
242  Teuchos::ParameterList &print_pars = nl_pars->sublist("Printing");
243  // linear system settings
244  Teuchos::ParameterList &ls_pars = nl_pars->sublist("Direction").sublist("Newton").sublist("Linear Solver");
245  // preconditioner
246  Teuchos::RCP<Precond<Scalar> > precond = ndp.get_precond();
247  //linear system
248  Teuchos::RCP<NOX::Epetra::LinearSystemAztecOO> lin_sys;
249  // problem
250  Teuchos::RCP<NOX::Epetra::Interface::Required> i_req = Teuchos::rcpFromRef(ndp);
251  // jacobian matrix
252  Teuchos::RCP<Epetra_RowMatrix> jac_mat;
253 
254  // Create linear system
255  if(this->dp->is_matrix_free())
256  {
257  if(precond == Teuchos::null)
258  { //Matrix free without preconditioner
259  lin_sys = Teuchos::rcp(new NOX::Epetra::LinearSystemAztecOO(print_pars, ls_pars, i_req, init_sln));
260  }
261  else
262  { //Matrix free with preconditioner
263  Teuchos::RCP<NOX::Epetra::Interface::Preconditioner> i_prec = Teuchos::rcpFromRef(ndp);
264  lin_sys = Teuchos::rcp(new NOX::Epetra::LinearSystemAztecOO(print_pars, ls_pars, i_req,
265  i_prec, precond, init_sln));
266  }
267  }
268  else
269  { // not matrix Free (with jacobian)
270  Teuchos::RCP<NOX::Epetra::Interface::Jacobian> i_jac = Teuchos::rcpFromRef(ndp);
271  jac_mat = Teuchos::rcp(ndp.get_jacobian()->mat);
272  if(precond == Teuchos::null)
273  { //Matrix without preconditioner
274  lin_sys = Teuchos::rcp(new NOX::Epetra::LinearSystemAztecOO(print_pars, ls_pars, i_req,
275  i_jac, jac_mat, init_sln));
276  }
277  else
278  { //Matrix with preconditioner
279  Teuchos::RCP<NOX::Epetra::Interface::Preconditioner> i_prec = Teuchos::rcpFromRef(ndp);
280  lin_sys = Teuchos::rcp(new NOX::Epetra::LinearSystemAztecOO(print_pars, ls_pars,
281  i_jac, jac_mat, i_prec, precond, init_sln));
282  }
283  }
284 
285  // Create the Group
286  Teuchos::RCP<NOX::Epetra::Group> grp =
287  Teuchos::rcp(new NOX::Epetra::Group(print_pars, i_req, init_sln, lin_sys));
288 
289  // Create convergence tests
290  Teuchos::RCP<NOX::StatusTest::Combo> converged =
291  Teuchos::rcp(new NOX::StatusTest::Combo(NOX::StatusTest::Combo::AND));
292 
293  if(conv_flag.absresid)
294  {
295  Teuchos::RCP<NOX::StatusTest::NormF> absresid =
296  Teuchos::rcp(new NOX::StatusTest::NormF(conv.abs_resid, conv.norm_type, conv.stype));
297  converged->addStatusTest(absresid);
298  }
299 
300  if(conv_flag.relresid)
301  {
302  Teuchos::RCP<NOX::StatusTest::NormF> relresid =
303  Teuchos::rcp(new NOX::StatusTest::NormF(*grp.get(), conv.rel_resid));
304  converged->addStatusTest(relresid);
305  }
306 
307  if(conv_flag.update)
308  {
309  Teuchos::RCP<NOX::StatusTest::NormUpdate> update =
310  Teuchos::rcp(new NOX::StatusTest::NormUpdate(conv.update));
311  converged->addStatusTest(update);
312  }
313 
314  if(conv_flag.wrms)
315  {
316  Teuchos::RCP<NOX::StatusTest::NormWRMS> wrms =
317  Teuchos::rcp(new NOX::StatusTest::NormWRMS(conv.wrms_rtol, conv.wrms_atol));
318  converged->addStatusTest(wrms);
319  }
320 
321  Teuchos::RCP<NOX::StatusTest::MaxIters> maxiters =
322  Teuchos::rcp(new NOX::StatusTest::MaxIters(conv.max_iters));
323 
324  Teuchos::RCP<NOX::StatusTest::FiniteValue> fv =
325  Teuchos::rcp(new NOX::StatusTest::FiniteValue);
326 
327  Teuchos::RCP<NOX::StatusTest::Combo> cmb =
328  Teuchos::rcp(new NOX::StatusTest::Combo(NOX::StatusTest::Combo::OR));
329 
330  cmb->addStatusTest(fv);
331  cmb->addStatusTest(converged);
332  cmb->addStatusTest(maxiters);
333 
334  // Output parameters
335  Teuchos::RCP<Teuchos::ParameterList> final_pars = nl_pars;
336 
337  // Create the solver
338  Teuchos::RCP<NOX::Solver::Generic> solver = NOX::Solver::buildSolver(grp, cmb, final_pars);
339 
341  NOX::StatusTest::StatusType status = solver->solve();
342 
343  if(!this->dp->is_matrix_free())
344  jac_mat.release(); // release the ownership (we take care of jac_mat by ourselves)
345 
346  if(status == NOX::StatusTest::Converged)
347  {
348  // get result informations
349  num_iters = solver->getNumIterations();
350  residual = solver->getSolutionGroup().getNormF();
351  num_lin_iters = final_pars->sublist("Direction").sublist("Newton").sublist("Linear Solver").sublist("Output").get("Total Number of Linear Iterations", -1);
352  achieved_tol = final_pars->sublist("Direction").sublist("Newton").sublist("Linear Solver").sublist("Output").get("Achieved Tolerance", 0.0);
353 
354  // Get the Epetra_Vector with the final solution from the solver
355  const NOX::Epetra::Group &f_grp =
356  dynamic_cast<const NOX::Epetra::Group &>(solver->getSolutionGroup());
357  const Epetra_Vector &f_sln =
358  (dynamic_cast<const NOX::Epetra::Vector &>(f_grp.getX())).getEpetraVector();
359  // extract solution
360  int n = this->dp->get_num_dofs();
361  delete [] this->sln_vector;
362  this->sln_vector = new double[n];
363  memset(this->sln_vector, 0, n * sizeof(double));
364  f_sln.ExtractCopy(this->sln_vector);
365  }
366  else // not converged
367  {
368  num_iters = -1;
369  throw Exceptions::Exception("Nox solver did not converge");
370  }
371  }
372 
373  template<typename Scalar>
374  int NewtonSolverNOX<Scalar>::get_num_iters()
375  {
376  return num_iters;
377  }
378 
379  template<typename Scalar>
380  double NewtonSolverNOX<Scalar>::get_residual()
381  {
382  return residual;
383  }
384 
385  template<typename Scalar>
386  int NewtonSolverNOX<Scalar>::get_num_lin_iters()
387  {
388  return num_lin_iters;
389  }
390 
391  template<typename Scalar>
392  double NewtonSolverNOX<Scalar>::get_achieved_tol()
393  {
394  return achieved_tol;
395  }
396 
397  template<typename Scalar>
398  void NewtonSolverNOX<Scalar>::set_norm_type(NOX::Abstract::Vector::NormType type)
399  {
400  conv.norm_type = type;
401  }
402 
403  template<typename Scalar>
404  void NewtonSolverNOX<Scalar>::set_scale_type(NOX::StatusTest::NormF::ScaleType type)
405  {
406  conv.stype = type;
407  }
408 
409  template<typename Scalar>
410  void NewtonSolverNOX<Scalar>::set_conv_iters(int iters)
411  {
412  conv.max_iters = iters;
413  }
414 
415  template<typename Scalar>
416  void NewtonSolverNOX<Scalar>::set_conv_abs_resid(double resid)
417  {
418  conv_flag.absresid = 1;
419  conv.abs_resid = resid;
420  }
421 
422  template<typename Scalar>
423  void NewtonSolverNOX<Scalar>::set_conv_rel_resid(double resid)
424  {
425  conv_flag.relresid = 1;
426  conv.rel_resid = resid;
427  }
428 
429  template<typename Scalar>
430  void NewtonSolverNOX<Scalar>::disable_abs_resid()
431  {
432  conv_flag.absresid = 0;
433  }
434 
435  template<typename Scalar>
436  void NewtonSolverNOX<Scalar>::disable_rel_resid()
437  {
438  conv_flag.relresid = 0;
439  }
440 
441  template<typename Scalar>
442  void NewtonSolverNOX<Scalar>::set_conv_update(double update)
443  {
444  conv_flag.update = 1;
445  conv.update = update;
446  }
447 
448  template<typename Scalar>
449  void NewtonSolverNOX<Scalar>::set_conv_wrms(double rtol, double atol)
450  {
451  conv_flag.wrms = 1;
452  conv.wrms_rtol = rtol;
453  conv.wrms_atol = atol;
454  }
455 
456  template class HERMES_API DiscreteProblemNOX<double>;
457  // template class HERMES_API DiscreteProblemNOX<std::complex<double> >; //complex version of nox solver is not implemented
458  template class HERMES_API NewtonSolverNOX<double>;
459  // template class HERMES_API NewtonSolverNOX<std::complex<double> >; //complex version of nox solver is not implemented
460  }
461 }
462 #endif