GetFEM++  5.3
getfem_model_solvers.h
Go to the documentation of this file.
1 /* -*- c++ -*- (enables emacs c++ mode) */
2 /*===========================================================================
3 
4  Copyright (C) 2004-2017 Yves Renard
5 
6  This file is a part of GetFEM++
7 
8  GetFEM++ is free software; you can redistribute it and/or modify it
9  under the terms of the GNU Lesser General Public License as published
10  by the Free Software Foundation; either version 3 of the License, or
11  (at your option) any later version along with the GCC Runtime Library
12  Exception either version 3.1 or (at your option) any later version.
13  This program is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16  License and GCC Runtime Library Exception for more details.
17  You should have received a copy of the GNU Lesser General Public License
18  along with this program; if not, write to the Free Software Foundation,
19  Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
20 
21  As a special exception, you may use this file as it is a part of a free
22  software library without restriction. Specifically, if other files
23  instantiate templates or use macros or inline functions from this file,
24  or you compile this file and link it with other files to produce an
25  executable, this file does not by itself cause the resulting executable
26  to be covered by the GNU Lesser General Public License. This exception
27  does not however invalidate any other reasons why the executable file
28  might be covered by the GNU Lesser General Public License.
29 
30 ===========================================================================*/
31 
32 /**
33  @file getfem_model_solvers.h
34  @author Yves Renard <Yves.Renard@insa-lyon.fr>
35  @date June 15, 2004.
36  @brief Standard solvers for model bricks
37  @see getfem_modeling.h
38 */
39 
40 #ifndef GETFEM_MODEL_SOLVERS_H__
41 #define GETFEM_MODEL_SOLVERS_H__
42 #include "getfem_models.h"
44 #include "gmm/gmm_iter.h"
45 #include "gmm/gmm_iter_solvers.h"
46 #include "gmm/gmm_dense_qr.h"
47 
48 //#include "gmm/gmm_inoutput.h"
49 
50 
51 namespace getfem {
52 
53  template <typename T> struct sort_abs_val_
54  { bool operator()(T x, T y) { return (gmm::abs(x) < gmm::abs(y)); } };
55 
56  template <typename MAT> void print_eigval(const MAT &M) {
57  // just to test a stiffness matrix if needing
58  typedef typename gmm::linalg_traits<MAT>::value_type T;
59  size_type n = gmm::mat_nrows(M);
60  gmm::dense_matrix<T> MM(n, n), Q(n, n);
61  std::vector<T> eigval(n);
62  gmm::copy(M, MM);
63  // gmm::symmetric_qr_algorithm(MM, eigval, Q);
64  gmm::implicit_qr_algorithm(MM, eigval, Q);
65  std::sort(eigval.begin(), eigval.end(), sort_abs_val_<T>());
66  cout << "eival = " << eigval << endl;
67 // cout << "vectp : " << gmm::mat_col(Q, n-1) << endl;
68 // cout << "vectp : " << gmm::mat_col(Q, n-2) << endl;
69 // double emax, emin;
70 // cout << "condition number" << condition_number(MM,emax,emin) << endl;
71 // cout << "emin = " << emin << endl;
72 // cout << "emax = " << emax << endl;
73  }
74 
75 
76  /* ***************************************************************** */
77  /* Linear solvers definition */
78  /* ***************************************************************** */
79 
80  template <typename MAT, typename VECT>
81  struct abstract_linear_solver {
82  virtual void operator ()(const MAT &, VECT &, const VECT &,
83  gmm::iteration &) const = 0;
84  virtual ~abstract_linear_solver() {}
85  };
86 
87  template <typename MAT, typename VECT>
88  struct linear_solver_cg_preconditioned_ildlt
89  : public abstract_linear_solver<MAT, VECT> {
90  void operator ()(const MAT &M, VECT &x, const VECT &b,
91  gmm::iteration &iter) const {
93  gmm::cg(M, x, b, P, iter);
94  if (!iter.converged()) GMM_WARNING2("cg did not converge!");
95  }
96  };
97 
98  template <typename MAT, typename VECT>
99  struct linear_solver_gmres_preconditioned_ilu
100  : public abstract_linear_solver<MAT, VECT> {
101  void operator ()(const MAT &M, VECT &x, const VECT &b,
102  gmm::iteration &iter) const {
104  gmm::gmres(M, x, b, P, 500, iter);
105  if (!iter.converged()) GMM_WARNING2("gmres did not converge!");
106  }
107  };
108 
109  template <typename MAT, typename VECT>
110  struct linear_solver_gmres_unpreconditioned
111  : public abstract_linear_solver<MAT, VECT> {
112  void operator ()(const MAT &M, VECT &x, const VECT &b,
113  gmm::iteration &iter) const {
114  gmm::identity_matrix P;
115  gmm::gmres(M, x, b, P, 500, iter);
116  if (!iter.converged()) GMM_WARNING2("gmres did not converge!");
117  }
118  };
119 
120  template <typename MAT, typename VECT>
121  struct linear_solver_gmres_preconditioned_ilut
122  : public abstract_linear_solver<MAT, VECT> {
123  void operator ()(const MAT &M, VECT &x, const VECT &b,
124  gmm::iteration &iter) const {
125  gmm::ilut_precond<MAT> P(M, 40, 1E-7);
126  gmm::gmres(M, x, b, P, 500, iter);
127  if (!iter.converged()) GMM_WARNING2("gmres did not converge!");
128  }
129  };
130 
131  template <typename MAT, typename VECT>
132  struct linear_solver_gmres_preconditioned_ilutp
133  : public abstract_linear_solver<MAT, VECT> {
134  void operator ()(const MAT &M, VECT &x, const VECT &b,
135  gmm::iteration &iter) const {
136  gmm::ilutp_precond<MAT> P(M, 20, 1E-7);
137  gmm::gmres(M, x, b, P, 500, iter);
138  if (!iter.converged()) GMM_WARNING2("gmres did not converge!");
139  }
140  };
141 
142  template <typename MAT, typename VECT>
143  struct linear_solver_superlu
144  : public abstract_linear_solver<MAT, VECT> {
145  void operator ()(const MAT &M, VECT &x, const VECT &b,
146  gmm::iteration &iter) const {
147  double rcond;
148  /*gmm::HarwellBoeing_IO::write("test.hb", M);
149  std::fstream f("bbb", std::ios::out);
150  for (unsigned i=0; i < gmm::vect_size(b); ++i) f << b[i] << "\n";*/
151  int info = SuperLU_solve(M, x, b, rcond);
152  iter.enforce_converged(info == 0);
153  if (iter.get_noisy()) cout << "condition number: " << 1.0/rcond<< endl;
154  }
155  };
156 
157  template <typename MAT, typename VECT>
158  struct linear_solver_dense_lu : public abstract_linear_solver<MAT, VECT> {
159  void operator ()(const MAT &M, VECT &x, const VECT &b,
160  gmm::iteration &iter) const {
161  typedef typename gmm::linalg_traits<MAT>::value_type T;
162  gmm::dense_matrix<T> MM(gmm::mat_nrows(M),gmm::mat_ncols(M));
163  gmm::copy(M, MM);
164  gmm::lu_solve(MM, x, b);
165  iter.enforce_converged(true);
166  }
167  };
168 
169 #ifdef GMM_USES_MUMPS
170  template <typename MAT, typename VECT>
171  struct linear_solver_mumps : public abstract_linear_solver<MAT, VECT> {
172  void operator ()(const MAT &M, VECT &x, const VECT &b,
173  gmm::iteration &iter) const {
174  bool ok = gmm::MUMPS_solve(M, x, b, false);
175  iter.enforce_converged(ok);
176  }
177  };
178  template <typename MAT, typename VECT>
179  struct linear_solver_mumps_sym : public abstract_linear_solver<MAT, VECT> {
180  void operator ()(const MAT &M, VECT &x, const VECT &b,
181  gmm::iteration &iter) const {
182  bool ok = gmm::MUMPS_solve(M, x, b, true);
183  iter.enforce_converged(ok);
184  }
185  };
186 #endif
187 
188 #if GETFEM_PARA_LEVEL > 1 && GETFEM_PARA_SOLVER == MUMPS_PARA_SOLVER
189  template <typename MAT, typename VECT>
190  struct linear_solver_distributed_mumps
191  : public abstract_linear_solver<MAT, VECT> {
192  void operator ()(const MAT &M, VECT &x, const VECT &b,
193  gmm::iteration &iter) const {
194  double tt_ref=MPI_Wtime();
195  bool ok = MUMPS_distributed_matrix_solve(M, x, b, false);
196  iter.enforce_converged(ok);
197  if (MPI_IS_MASTER()) cout<<"UNSYMMETRIC MUMPS time "<< MPI_Wtime() - tt_ref<<endl;
198  }
199  };
200 
201  template <typename MAT, typename VECT>
202  struct linear_solver_distributed_mumps_sym
203  : public abstract_linear_solver<MAT, VECT> {
204  void operator ()(const MAT &M, VECT &x, const VECT &b,
205  gmm::iteration &iter) const {
206  double tt_ref=MPI_Wtime();
207  bool ok = MUMPS_distributed_matrix_solve(M, x, b, true);
208  iter.enforce_converged(ok);
209  if (MPI_IS_MASTER()) cout<<"SYMMETRIC MUMPS time "<< MPI_Wtime() - tt_ref<<endl;
210  }
211  };
212 #endif
213 
214 
215  /* ***************************************************************** */
216  /* Newton Line search definition */
217  /* ***************************************************************** */
218 
219  struct abstract_newton_line_search {
220  double conv_alpha, conv_r;
221  size_t it, itmax, glob_it;
222  // size_t tot_it;
223  virtual void init_search(double r, size_t git, double R0 = 0.0) = 0;
224  virtual double next_try(void) = 0;
225  virtual bool is_converged(double, double R1 = 0.0) = 0;
226  virtual double converged_value(void) {
227  // tot_it += it; cout << "tot_it = " << tot_it << endl; it = 0;
228  return conv_alpha;
229  };
230  virtual double converged_residual(void) { return conv_r; };
231  virtual ~abstract_newton_line_search() { }
232  };
233 
234  // Dummy linesearch for the newton with step control
235  struct newton_search_with_step_control : public abstract_newton_line_search {
236 
237  virtual void init_search(double /*r*/, size_t /*git*/, double /*R0*/ = 0.0)
238  { GMM_ASSERT1(false, "Not to be used"); }
239 
240  virtual double next_try(void)
241  { GMM_ASSERT1(false, "Not to be used"); }
242 
243  virtual bool is_converged(double /*r*/, double /*R1*/ = 0.0)
244  { GMM_ASSERT1(false, "Not to be used"); }
245 
246  newton_search_with_step_control() {}
247  };
248 
249 
250  struct quadratic_newton_line_search : public abstract_newton_line_search {
251  double R0_, R1_;
252 
253  double alpha, alpha_mult, first_res, alpha_max_ratio, alpha_min;
254  virtual void init_search(double r, size_t git, double R0 = 0.0) {
255  GMM_ASSERT1(R0 != 0.0, "You have to specify R0");
256  glob_it = git;
257  conv_alpha = alpha = double(1); conv_r = first_res = r; it = 0;
258  R0_ = R0;
259  }
260  virtual double next_try(void) {
261  ++it;
262  if (it == 1) return double(1);
263  GMM_ASSERT1(R1_ != 0.0, "You have to specify R1");
264  double a = R0_ / R1_;
265  return (a < 0) ? (a*0.5 + sqrt(a*a*0.25-a)) : a*0.5;
266  }
267  virtual bool is_converged(double r, double R1 = 0.0) {
268  conv_r = r;
269  R1_ = R1; return ((gmm::abs(R1_) < gmm::abs(R0_*0.5)) || it >= itmax);
270  }
271  quadratic_newton_line_search(size_t imax = size_t(-1)) { itmax = imax; }
272  };
273 
274 
275  struct simplest_newton_line_search : public abstract_newton_line_search {
276  double alpha, alpha_mult, first_res, alpha_max_ratio, alpha_min,
277  alpha_threshold_res;
278  virtual void init_search(double r, size_t git, double = 0.0) {
279  glob_it = git;
280  conv_alpha = alpha = double(1); conv_r = first_res = r; it = 0;
281  }
282  virtual double next_try(void)
283  { conv_alpha = alpha; alpha *= alpha_mult; ++it; return conv_alpha; }
284  virtual bool is_converged(double r, double = 0.0) {
285  conv_r = r;
286  return ((it <= 1 && r < first_res)
287  || (r <= first_res * alpha_max_ratio && r <= alpha_threshold_res)
288  || (conv_alpha <= alpha_min && r < first_res * 1e5)
289  || it >= itmax);
290  }
291  simplest_newton_line_search
292  (size_t imax = size_t(-1), double a_max_ratio = 6.0/5.0,
293  double a_min = 1.0/1000.0, double a_mult = 3.0/5.0,
294  double a_threshold_res = 1e50)
295  : alpha_mult(a_mult), alpha_max_ratio(a_max_ratio), alpha_min(a_min),
296  alpha_threshold_res(a_threshold_res)
297  { itmax = imax; }
298  };
299 
300  struct default_newton_line_search : public abstract_newton_line_search {
301  // This line search try to detect where is the minimum, dividing the step
302  // by a factor two each time.
303  // - it stops if the residual is less than the previous residual
304  // times alpha_min_ratio (= 0.9).
305  // - if the minimal step is reached with a residual greater than
306  // the previous residual times alpha_min_ratio then it decides
307  // between two options :
308  // - return with the step corresponding to the smallest residual
309  // - return with a greater residual corresponding to a residual
310  // less than the previous residual times alpha_max_ratio.
311  // the decision is taken regarding the previous iterations.
312  // - in order to shorten the line search, the process stops when
313  // the residual increases three times consecutively.
314  // possible improvment : detect the curvature at the origin
315  // (only one evaluation) and take it into account.
316  // Fitted to some experiments in contrib/tests_newton
317 
318  double alpha, alpha_old, alpha_mult, first_res, alpha_max_ratio;
319  double alpha_min_ratio, alpha_min;
320  size_type count, count_pat;
321  bool max_ratio_reached;
322  double alpha_max_ratio_reached, r_max_ratio_reached;
323  size_type it_max_ratio_reached;
324 
325  virtual void init_search(double r, size_t git, double = 0.0);
326  virtual double next_try(void);
327  virtual bool is_converged(double r, double = 0.0);
328  default_newton_line_search(void) { count_pat = 0; }
329  };
330 
331  /* the former default_newton_line_search */
332  struct basic_newton_line_search : public abstract_newton_line_search {
333  double alpha, alpha_mult, first_res, alpha_max_ratio;
334  double alpha_min, prev_res, alpha_max_augment;
335  virtual void init_search(double r, size_t git, double = 0.0) {
336  glob_it = git;
337  conv_alpha = alpha = double(1);
338  prev_res = conv_r = first_res = r; it = 0;
339  }
340  virtual double next_try(void)
341  { conv_alpha = alpha; alpha *= alpha_mult; ++it; return conv_alpha; }
342  virtual bool is_converged(double r, double = 0.0) {
343  if (glob_it == 0 || (r < first_res / double(2))
344  || (conv_alpha <= alpha_min && r < first_res * alpha_max_augment)
345  || it >= itmax)
346  { conv_r = r; return true; }
347  if (it > 1 && r > prev_res && prev_res < alpha_max_ratio * first_res)
348  return true;
349  prev_res = conv_r = r;
350  return false;
351  }
352  basic_newton_line_search
353  (size_t imax = size_t(-1),
354  double a_max_ratio = 5.0/3.0,
355  double a_min = 1.0/1000.0, double a_mult = 3.0/5.0, double a_augm = 2.0)
356  : alpha_mult(a_mult), alpha_max_ratio(a_max_ratio),
357  alpha_min(a_min), alpha_max_augment(a_augm) { itmax = imax; }
358  };
359 
360 
361  struct systematic_newton_line_search : public abstract_newton_line_search {
362  double alpha, alpha_mult, first_res;
363  double alpha_min, prev_res;
364  bool first;
365  virtual void init_search(double r, size_t git, double = 0.0) {
366  glob_it = git;
367  conv_alpha = alpha = double(1);
368  prev_res = conv_r = first_res = r; it = 0; first = true;
369  }
370  virtual double next_try(void)
371  { double a = alpha; alpha *= alpha_mult; ++it; return a; }
372  virtual bool is_converged(double r, double = 0.0) {
373  // cout << "a = " << alpha / alpha_mult << " r = " << r << endl;
374  if (r < conv_r || first)
375  { conv_r = r; conv_alpha = alpha / alpha_mult; first = false; }
376  if ((alpha <= alpha_min*alpha_mult) || it >= itmax) return true;
377  return false;
378  }
379  systematic_newton_line_search
380  (size_t imax = size_t(-1),
381  double a_min = 1.0/10000.0, double a_mult = 3.0/5.0)
382  : alpha_mult(a_mult), alpha_min(a_min) { itmax = imax; }
383  };
384 
385 
386  /* ***************************************************************** */
387  /* Newton(-Raphson) algorithm with step control. */
388  /* ***************************************************************** */
389  /* Still solves a problem F(X) = 0 sarting at X0 but setting */
390  /* B0 = F(X0) and solving */
391  /* F(X) = (1-alpha)B0 (1) */
392  /* with alpha growing from 0 to 1. */
393  /* A very basic line search is applied. */
394  /* */
395  /* Step 0 : set alpha0 = 0, alpha = 1, X0 given and B0 = F(X0). */
396  /* Step 1 : Set Ri = F(Xi) - (1-alpha)B0 */
397  /* If ||Ri|| < rho, Xi+1 = Xi and go to step 2 */
398  /* Perform Newton step on problem (1) */
399  /* If the Newton do not converge (stagnation) */
400  /* alpha <- max(alpha0+1E-4, (alpha+alpha0)/2) */
401  /* Loop on step 1 with Xi unchanged */
402  /* Step 2 : if alpha=1 stop */
403  /* else alpha0 <- alpha, */
404  /* alpha <- min(1,alpha0+2*(alpha-alpha0)), */
405  /* Go to step 1 with Xi+1 */
406  /* being the result of Newton iteraitons of step1. */
407  /* */
408  /*********************************************************************/
409 
410  template <typename PB>
411  void Newton_with_step_control(PB &pb, gmm::iteration &iter,
412  const abstract_linear_solver<typename PB::MATRIX,
413  typename PB::VECTOR> &linear_solver) {
414  typedef typename gmm::linalg_traits<typename PB::VECTOR>::value_type T;
415  typedef typename gmm::number_traits<T>::magnitude_type R;
416  gmm::iteration iter_linsolv0 = iter;
417  iter_linsolv0.reduce_noisy();
418  iter_linsolv0.set_resmax(iter.get_resmax()/20.0);
419  iter_linsolv0.set_maxiter(10000); // arbitrary
420 
421  pb.compute_residual();
422  R approx_eln = pb.approx_external_load_norm();
423  if (approx_eln == R(0)) approx_eln = R(1);
424 
425  typename PB::VECTOR b0(gmm::vect_size(pb.residual()));
426  gmm::copy(pb.residual(), b0);
427  typename PB::VECTOR Xi(gmm::vect_size(pb.residual()));
428  gmm::copy(pb.state_vector(), Xi);
429 
430  typename PB::VECTOR dr(gmm::vect_size(pb.residual()));
431  typename PB::VECTOR b(gmm::vect_size(pb.residual()));
432 
433  R alpha0(0), alpha(1), res0(gmm::vect_norm1(b0)), minres(res0);
434  // const newton_search_with_step_control *ls
435  // = dynamic_cast<const newton_search_with_step_control *>(&(pb.ls));
436  // GMM_ASSERT1(ls, "Internal error");
437  size_type nit = 0, stagn = 0;
438  R coeff = R(2);
439 
440  scalar_type crit = pb.residual_norm() / approx_eln;
441  if (iter.finished(crit)) return;
442  for(;;) {
443 
444  crit = gmm::vect_dist1(pb.residual(), gmm::scaled(b0, R(1)-alpha))
445  / approx_eln;
446  if (!iter.converged(crit)) {
447  gmm::iteration iter_linsolv = iter_linsolv0;
448  if (iter.get_noisy() > 1)
449  cout << "starting tangent matrix computation" << endl;
450 
451  int is_singular = 1;
452  while (is_singular) { // Linear system solve
453  pb.compute_tangent_matrix();
454  gmm::clear(dr);
455  gmm::copy(gmm::scaled(pb.residual(), pb.scale_residual()), b);
456  gmm::add(gmm::scaled(b0,alpha-R(1)), b);
457  if (iter.get_noisy() > 1) cout << "starting linear solver" << endl;
458  iter_linsolv.init();
459  linear_solver(pb.tangent_matrix(), dr, b, iter_linsolv);
460  if (!iter_linsolv.converged()) {
461  is_singular++;
462  if (is_singular <= 4) {
463  if (iter.get_noisy())
464  cout << "Singular tangent matrix:"
465  " perturbation of the state vector." << endl;
466  pb.perturbation();
467  pb.compute_residual();
468  } else {
469  if (iter.get_noisy())
470  cout << "Singular tangent matrix: perturbation failed, "
471  << "aborting." << endl;
472  return;
473  }
474  }
475  else is_singular = 0;
476  }
477  if (iter.get_noisy() > 1) cout << "linear solver done" << endl;
478 
479 
480  gmm::add(dr, pb.state_vector());
481  pb.compute_residual();
482  R res = gmm::vect_dist1(pb.residual(), gmm::scaled(b0, R(1)-alpha));
483  R dec = R(1)/R(2), coeff2 = coeff * R(1.5);
484 
485  while (dec > R(1E-5) && res >= res0 * coeff) {
486  gmm::add(gmm::scaled(dr, -dec), pb.state_vector());
487  pb.compute_residual();
488  R res2 = gmm::vect_dist1(pb.residual(), gmm::scaled(b0, R(1)-alpha));
489  if (res2 < res*R(0.95) || res2 >= res0 * coeff2) {
490  dec /= R(2); res = res2; coeff2 *= R(1.5);
491  } else {
492  gmm::add(gmm::scaled(dr, dec), pb.state_vector());
493  break;
494  }
495  }
496  dec *= R(2);
497 
498  nit++;
499  coeff = std::max(R(1.05), coeff*R(0.93));
500  bool near_end = (iter.get_iteration() > iter.get_maxiter()/2);
501  bool cut = (alpha < R(1)) && near_end;
502  if ((res > minres && nit > 4) || cut) {
503  stagn++;
504 
505  if ((stagn > 10 && alpha > alpha0 + R(5E-2)) || cut) {
506  alpha = (alpha + alpha0) / R(2);
507  if (near_end) alpha = R(1);
508  gmm::copy(Xi, pb.state_vector());
509  pb.compute_residual();
510  res = gmm::vect_dist1(pb.residual(), gmm::scaled(b0, R(1)-alpha));
511  nit = 0;
512  stagn = 0; coeff = R(2);
513  }
514  }
515  if (res < minres || (alpha == R(1) && nit < 10)) stagn = 0;
516  res0 = res;
517  if (nit < 5) minres = res0; else minres = std::min(minres, res0);
518 
519  if (iter.get_noisy())
520  cout << "step control [" << std::setw(8) << alpha0 << ","
521  << std::setw(8) << alpha << "," << std::setw(10) << dec << "]";
522  ++iter;
523  // crit = std::min(res / approx_eln,
524  // gmm::vect_norm1(dr) / std::max(1E-25,pb.state_norm()));
525  crit = res / approx_eln;
526  }
527 
528  if (iter.finished(crit)) {
529  if (iter.converged() && alpha < R(1)) {
530  R a = alpha;
531  alpha = std::min(R(1), alpha*R(3) - alpha0*R(2));
532  alpha0 = a;
533  gmm::copy(pb.state_vector(), Xi);
534  nit = 0; stagn = 0; coeff = R(2);
535  } else return;
536  }
537  }
538  }
539 
540 
541 
542  /* ***************************************************************** */
543  /* Classicel Newton(-Raphson) algorithm. */
544  /* ***************************************************************** */
545 
546  template <typename PB>
547  void classical_Newton(PB &pb, gmm::iteration &iter,
548  const abstract_linear_solver<typename PB::MATRIX,
549  typename PB::VECTOR> &linear_solver) {
550  typedef typename gmm::linalg_traits<typename PB::VECTOR>::value_type T;
551  typedef typename gmm::number_traits<T>::magnitude_type R;
552  gmm::iteration iter_linsolv0 = iter;
553  iter_linsolv0.reduce_noisy();
554  iter_linsolv0.set_resmax(iter.get_resmax()/20.0);
555  iter_linsolv0.set_maxiter(10000); // arbitrary
556 
557  pb.compute_residual();
558  R approx_eln = pb.approx_external_load_norm();
559  if (approx_eln == R(0)) approx_eln = R(1);
560 
561  typename PB::VECTOR dr(gmm::vect_size(pb.residual()));
562  typename PB::VECTOR b(gmm::vect_size(pb.residual()));
563 
564  scalar_type crit = pb.residual_norm() / approx_eln;
565  while (!iter.finished(crit)) {
566  gmm::iteration iter_linsolv = iter_linsolv0;
567  if (iter.get_noisy() > 1)
568  cout << "starting computing tangent matrix" << endl;
569 
570  int is_singular = 1;
571  while (is_singular) {
572  pb.compute_tangent_matrix();
573  gmm::clear(dr);
574  gmm::copy(gmm::scaled(pb.residual(), pb.scale_residual()), b);
575  if (iter.get_noisy() > 1) cout << "starting linear solver" << endl;
576  iter_linsolv.init();
577  linear_solver(pb.tangent_matrix(), dr, b, iter_linsolv);
578  if (!iter_linsolv.converged()) {
579  is_singular++;
580  if (is_singular <= 4) {
581  if (iter.get_noisy())
582  cout << "Singular tangent matrix:"
583  " perturbation of the state vector." << endl;
584  pb.perturbation();
585  pb.compute_residual();
586  } else {
587  if (iter.get_noisy())
588  cout << "Singular tangent matrix: perturbation failed, aborting."
589  << endl;
590  return;
591  }
592  }
593  else is_singular = 0;
594  }
595 
596  if (iter.get_noisy() > 1) cout << "linear solver done" << endl;
597  R alpha = pb.line_search(dr, iter); //it is assumed that the linesearch
598  //executes a pb.compute_residual();
599  if (iter.get_noisy()) cout << "alpha = " << std::setw(6) << alpha << " ";
600  ++iter;
601  crit = std::min(pb.residual_norm() / approx_eln,
602  gmm::vect_norm1(dr) / std::max(1E-25, pb.state_norm()));
603  }
604  }
605 
606 
607  /* ***************************************************************** */
608  /* Intermediary structure for Newton algorithms with getfem::model. */
609  /* ***************************************************************** */
610 
611  #define TRACE_SOL 0
612 
613  template <typename MAT, typename VEC>
614  struct model_pb {
615 
616  typedef MAT MATRIX;
617  typedef VEC VECTOR;
618  typedef typename gmm::linalg_traits<VECTOR>::value_type T;
619  typedef typename gmm::number_traits<T>::magnitude_type R;
620 
621  model &md;
622  abstract_newton_line_search &ls;
623  VECTOR stateinit, &state;
624  const VECTOR &rhs;
625  const MATRIX &K;
626 
627  void compute_tangent_matrix(void) {
628  md.to_variables(state);
629  md.assembly(model::BUILD_MATRIX);
630  }
631 
632  const MATRIX &tangent_matrix(void) { return K; }
633 
634  inline T scale_residual(void) const { return T(1); }
635 
636  void compute_residual(void) {
637  md.to_variables(state);
638  md.assembly(model::BUILD_RHS);
639  }
640 
641  void perturbation(void) {
642  R res = gmm::vect_norm2(state), ampl = std::max(res*R(1E-20), R(1E-50));
643  std::vector<R> V(gmm::vect_size(state));
644  gmm::fill_random(V);
645  gmm::add(gmm::scaled(V, ampl), state);
646  }
647 
648  const VECTOR &residual(void) const { return rhs; }
649  const VECTOR &state_vector(void) const { return state; }
650  VECTOR &state_vector(void) { return state; }
651 
652  R state_norm(void) const
653  { return gmm::vect_norm1(state); }
654 
655  R approx_external_load_norm(void)
656  { return md.approx_external_load(); }
657 
658  R residual_norm(void) { // A norm1 seems to be better than a norm2
659  // at least for contact problems.
660  return gmm::vect_norm1(rhs);
661  }
662 
663  R compute_res(bool comp = true) {
664  if (comp) compute_residual();
665  return residual_norm();
666  }
667 
668 
669  R line_search(VECTOR &dr, const gmm::iteration &iter) {
670  size_type nit = 0;
671  gmm::resize(stateinit, md.nb_dof());
672  gmm::copy(state, stateinit);
673  R alpha(1), res, /* res_init, */ R0;
674 
675  /* res_init = */ res = compute_res(false);
676  // cout << "first residual = " << residual() << endl << endl;
677  R0 = gmm::real(gmm::vect_sp(dr, rhs));
678 
679 #if TRACE_SOL
680  static int trace_number = 0;
681  int trace_iter = 0;
682  {
683  std::stringstream trace_name;
684  trace_name << "line_search_state" << std::setfill('0')
685  << std::setw(3) << trace_number << "_000_init";
686  gmm::vecsave(trace_name.str(),stateinit);
687  }
688  trace_number++;
689 #endif
690 
691  ls.init_search(res, iter.get_iteration(), R0);
692  do {
693  alpha = ls.next_try();
694  gmm::add(stateinit, gmm::scaled(dr, alpha), state);
695 #if TRACE_SOL
696  {
697  trace_iter++;
698  std::stringstream trace_name;
699  trace_name << "line_search_state" << std::setfill('0')
700  << std::setw(3) << trace_number << "_"
701  << std::setfill('0') << std::setw(3) << trace_iter;
702  gmm::vecsave(trace_name.str(), state);
703  }
704 #endif
705  res = compute_res();
706  // cout << "residual = " << residual() << endl << endl;
707  R0 = gmm::real(gmm::vect_sp(dr, rhs));
708 
709  ++ nit;
710  } while (!ls.is_converged(res, R0));
711 
712  if (alpha != ls.converged_value()) {
713  alpha = ls.converged_value();
714  gmm::add(stateinit, gmm::scaled(dr, alpha), state);
715  res = ls.converged_residual();
716  compute_residual();
717  }
718 
719  return alpha;
720  }
721 
722  model_pb(model &m, abstract_newton_line_search &ls_, VECTOR &st,
723  const VECTOR &rhs_, const MATRIX &K_)
724  : md(m), ls(ls_), state(st), rhs(rhs_), K(K_) {}
725 
726  };
727 
728  //---------------------------------------------------------------------
729  // Default linear solver.
730  //---------------------------------------------------------------------
731 
732  typedef abstract_linear_solver<model_real_sparse_matrix,
733  model_real_plain_vector> rmodel_linear_solver;
734  typedef std::shared_ptr<rmodel_linear_solver> rmodel_plsolver_type;
735  typedef abstract_linear_solver<model_complex_sparse_matrix,
736  model_complex_plain_vector>
737  cmodel_linear_solver;
738  typedef std::shared_ptr<cmodel_linear_solver> cmodel_plsolver_type;
739 
740 
741  template<typename MATRIX, typename VECTOR>
742  std::shared_ptr<abstract_linear_solver<MATRIX, VECTOR>>
743  default_linear_solver(const model &md) {
744 
745 #if GETFEM_PARA_LEVEL == 1 && GETFEM_PARA_SOLVER == MUMPS_PARA_SOLVER
746  if (md.is_symmetric())
747  return std::make_shared<linear_solver_mumps_sym<MATRIX, VECTOR>>();
748  else
749  return std::make_shared<linear_solver_mumps<MATRIX, VECTOR>>();
750 #elif GETFEM_PARA_LEVEL > 1 && GETFEM_PARA_SOLVER == MUMPS_PARA_SOLVER
751  if (md.is_symmetric())
752  return std::make_shared
753  <linear_solver_distributed_mumps_sym<MATRIX, VECTOR>>();
754  else
755  return std::make_shared
756  <linear_solver_distributed_mumps<MATRIX, VECTOR>>();
757 #else
758  size_type ndof = md.nb_dof(), max3d = 15000, dim = md.leading_dimension();
759 # ifdef GMM_USES_MUMPS
760  max3d = 250000;
761 # endif
762  if ((ndof<300000 && dim<=2) || (ndof<max3d && dim<=3) || (ndof<1000)) {
763 # ifdef GMM_USES_MUMPS
764  if (md.is_symmetric())
765  return std::make_shared<linear_solver_mumps_sym<MATRIX, VECTOR>>();
766  else
767  return std::make_shared<linear_solver_mumps<MATRIX, VECTOR>>();
768 # else
769  return std::make_shared<linear_solver_superlu<MATRIX, VECTOR>>();
770 # endif
771  }
772  else {
773  if (md.is_coercive())
774  return std::make_shared
775  <linear_solver_cg_preconditioned_ildlt<MATRIX, VECTOR>>();
776  else {
777  if (dim <= 2)
778  return std::make_shared
779  <linear_solver_gmres_preconditioned_ilut<MATRIX,VECTOR>>();
780  else
781  return std::make_shared
782  <linear_solver_gmres_preconditioned_ilu<MATRIX,VECTOR>>();
783  }
784  }
785 #endif
786  return std::shared_ptr<abstract_linear_solver<MATRIX, VECTOR>>();
787  }
788 
789  template <typename MATRIX, typename VECTOR>
790  std::shared_ptr<abstract_linear_solver<MATRIX, VECTOR>>
791  select_linear_solver(const model &md, const std::string &name) {
792  std::shared_ptr<abstract_linear_solver<MATRIX, VECTOR>> p;
793  if (bgeot::casecmp(name, "superlu") == 0)
794  return std::make_shared<linear_solver_superlu<MATRIX, VECTOR>>();
795  else if (bgeot::casecmp(name, "dense_lu") == 0)
796  return std::make_shared<linear_solver_dense_lu<MATRIX, VECTOR>>();
797  else if (bgeot::casecmp(name, "mumps") == 0) {
798 #ifdef GMM_USES_MUMPS
799 # if GETFEM_PARA_LEVEL <= 1
800  return std::make_shared<linear_solver_mumps<MATRIX, VECTOR>>();
801 # else
802  return std::make_shared
803  <linear_solver_distributed_mumps<MATRIX, VECTOR>>();
804 # endif
805 #else
806  GMM_ASSERT1(false, "Mumps is not interfaced");
807 #endif
808  }
809  else if (bgeot::casecmp(name, "cg/ildlt") == 0)
810  return std::make_shared
811  <linear_solver_cg_preconditioned_ildlt<MATRIX, VECTOR>>();
812  else if (bgeot::casecmp(name, "gmres/ilu") == 0)
813  return std::make_shared
814  <linear_solver_gmres_preconditioned_ilu<MATRIX, VECTOR>>();
815  else if (bgeot::casecmp(name, "gmres/ilut") == 0)
816  return std::make_shared
817  <linear_solver_gmres_preconditioned_ilut<MATRIX, VECTOR>>();
818  else if (bgeot::casecmp(name, "gmres/ilutp") == 0)
819  return std::make_shared
820  <linear_solver_gmres_preconditioned_ilutp<MATRIX, VECTOR>>();
821  else if (bgeot::casecmp(name, "auto") == 0)
822  return default_linear_solver<MATRIX, VECTOR>(md);
823  else
824  GMM_ASSERT1(false, "Unknown linear solver");
825  return std::shared_ptr<abstract_linear_solver<MATRIX, VECTOR>>();
826  }
827 
828  inline rmodel_plsolver_type rselect_linear_solver(const model &md,
829  const std::string &name) {
830  return select_linear_solver<model_real_sparse_matrix,
831  model_real_plain_vector>(md, name);
832  }
833 
834  inline cmodel_plsolver_type cselect_linear_solver(const model &md,
835  const std::string &name) {
836  return select_linear_solver<model_complex_sparse_matrix,
837  model_complex_plain_vector>(md, name);
838  }
839 
840  //---------------------------------------------------------------------
841  // Standard solve.
842  //---------------------------------------------------------------------
843 
844 
845  /** A default solver for the model brick system.
846  Of course it could be not very well suited for a particular
847  problem, so it could be copied and adapted to change solvers,
848  add a special traitement on the problem, etc ... This is in
849  fact a model for your own solver.
850 
851  For small problems, a direct solver is used
852  (getfem::SuperLU_solve), for larger problems, a conjugate
853  gradient gmm::cg (if the problem is coercive) or a gmm::gmres is
854  used (preconditioned with an incomplete factorization).
855 
856  When MPI/METIS is enabled, a partition is done via METIS, and a parallel
857  solver can be used.
858 
859  Note that it is possible to disable some variables
860  (see the md.disable_variable(varname) method) in order to
861  solve the problem only with respect to a subset of variables (the
862  disabled variables are the considered as data) for instance to
863  replace the global Newton strategy with a fixed point one.
864 
865  @ingroup bricks
866  */
867  void standard_solve(model &md, gmm::iteration &iter,
868  rmodel_plsolver_type lsolver,
869  abstract_newton_line_search &ls);
870 
871  void standard_solve(model &md, gmm::iteration &iter,
872  cmodel_plsolver_type lsolver,
873  abstract_newton_line_search &ls);
874 
875  void standard_solve(model &md, gmm::iteration &iter,
876  rmodel_plsolver_type lsolver);
877 
878  void standard_solve(model &md, gmm::iteration &iter,
879  cmodel_plsolver_type lsolver);
880 
881  void standard_solve(model &md, gmm::iteration &iter);
882 
883 } /* end of namespace getfem. */
884 
885 
886 #endif /* GETFEM_MODEL_SOLVERS_H__ */
Incomplete LU without fill-in Preconditioner.
The Iteration object calculates whether the solution has reached the desired accuracy, or whether the maximum number of iterations has been reached.
Definition: gmm_iter.h:53
Dense QR factorization.
void standard_solve(model &md, gmm::iteration &iter, rmodel_plsolver_type lsolver, abstract_newton_line_search &ls)
A default solver for the model brick system.
void fill_random(L &l, double cfill)
*/
Definition: gmm_blas.h:154
number_traits< typename linalg_traits< V >::value_type >::magnitude_type vect_norm2(const V &v)
Euclidean norm of a vector.
Definition: gmm_blas.h:557
number_traits< typename linalg_traits< V >::value_type >::magnitude_type vect_norm1(const V &v)
1-norm of a vector
Definition: gmm_blas.h:646
size_t size_type
used as the common size type in the library
Definition: bgeot_poly.h:49
Model representation in Getfem.
Interface with MUMPS (LU direct solver for sparse matrices).
GEneric Tool for Finite Element Methods.
number_traits< typename linalg_traits< V1 >::value_type >::magnitude_type vect_dist1(const V1 &v1, const V2 &v2)
1-distance between two vectors
Definition: gmm_blas.h:658
Iteration object.
void clear(L &l)
clear (fill with zeros) a vector or matrix.
Definition: gmm_blas.h:59
Include standard gmm iterative solvers (cg, gmres, ...)
ILUTP: Incomplete LU with threshold and K fill-in Preconditioner and column pivoting.
size_type alpha(short_type n, short_type d)
Return the value of which is the number of monomials of a polynomial of variables and degree ...
Definition: bgeot_poly.cc:46
void resize(M &v, size_type m, size_type n)
*/
Definition: gmm_blas.h:231
Incomplete LU with threshold and K fill-in Preconditioner.
Incomplete Level 0 LDLT Preconditioner.