40 #ifndef GETFEM_MODEL_SOLVERS_H__
41 #define GETFEM_MODEL_SOLVERS_H__
53 template <
typename T>
struct sort_abs_val_
54 {
bool operator()(T x, T y) {
return (gmm::abs(x) < gmm::abs(y)); } };
56 template <
typename MAT>
void print_eigval(
const MAT &M) {
58 typedef typename gmm::linalg_traits<MAT>::value_type T;
60 gmm::dense_matrix<T> MM(n, n), Q(n, n);
61 std::vector<T> eigval(n);
64 gmm::implicit_qr_algorithm(MM, eigval, Q);
65 std::sort(eigval.begin(), eigval.end(), sort_abs_val_<T>());
66 cout <<
"eival = " << eigval << endl;
80 template <
typename MAT,
typename VECT>
81 struct abstract_linear_solver {
84 virtual void operator ()(
const MAT &, VECT &,
const VECT &,
86 virtual ~abstract_linear_solver() {}
89 template <
typename MAT,
typename VECT>
90 struct linear_solver_cg_preconditioned_ildlt
91 :
public abstract_linear_solver<MAT, VECT> {
92 void operator ()(
const MAT &M, VECT &x,
const VECT &b,
95 gmm::cg(M, x, b, P, iter);
96 if (!iter.converged()) GMM_WARNING2(
"cg did not converge!");
100 template <
typename MAT,
typename VECT>
101 struct linear_solver_gmres_preconditioned_ilu
102 :
public abstract_linear_solver<MAT, VECT> {
103 void operator ()(
const MAT &M, VECT &x,
const VECT &b,
106 gmm::gmres(M, x, b, P, 500, iter);
107 if (!iter.converged()) GMM_WARNING2(
"gmres did not converge!");
111 template <
typename MAT,
typename VECT>
112 struct linear_solver_gmres_unpreconditioned
113 :
public abstract_linear_solver<MAT, VECT> {
114 void operator ()(
const MAT &M, VECT &x,
const VECT &b,
116 gmm::identity_matrix P;
117 gmm::gmres(M, x, b, P, 500, iter);
118 if (!iter.converged()) GMM_WARNING2(
"gmres did not converge!");
122 template <
typename MAT,
typename VECT>
123 struct linear_solver_gmres_preconditioned_ilut
124 :
public abstract_linear_solver<MAT, VECT> {
125 void operator ()(
const MAT &M, VECT &x,
const VECT &b,
128 gmm::gmres(M, x, b, P, 500, iter);
129 if (!iter.converged()) GMM_WARNING2(
"gmres did not converge!");
133 template <
typename MAT,
typename VECT>
134 struct linear_solver_gmres_preconditioned_ilutp
135 :
public abstract_linear_solver<MAT, VECT> {
136 void operator ()(
const MAT &M, VECT &x,
const VECT &b,
139 gmm::gmres(M, x, b, P, 500, iter);
140 if (!iter.converged()) GMM_WARNING2(
"gmres did not converge!");
144 template <
typename MAT,
typename VECT>
145 struct linear_solver_superlu
146 :
public abstract_linear_solver<MAT, VECT> {
147 void operator ()(
const MAT &M, VECT &x,
const VECT &b,
153 int info = SuperLU_solve(M, x, b, rcond);
154 iter.enforce_converged(info == 0);
155 if (iter.get_noisy()) cout <<
"condition number: " << 1.0/rcond<< endl;
159 template <
typename MAT,
typename VECT>
160 struct linear_solver_dense_lu :
public abstract_linear_solver<MAT, VECT> {
161 void operator ()(
const MAT &M, VECT &x,
const VECT &b,
163 typedef typename gmm::linalg_traits<MAT>::value_type T;
164 gmm::dense_matrix<T> MM(gmm::mat_nrows(M),gmm::mat_ncols(M));
166 gmm::lu_solve(MM, x, b);
167 iter.enforce_converged(
true);
171 #ifdef GMM_USES_MUMPS
172 template <
typename MAT,
typename VECT>
173 struct linear_solver_mumps :
public abstract_linear_solver<MAT, VECT> {
174 void operator ()(
const MAT &M, VECT &x,
const VECT &b,
176 bool ok = gmm::MUMPS_solve(M, x, b,
false);
177 iter.enforce_converged(ok);
180 template <
typename MAT,
typename VECT>
181 struct linear_solver_mumps_sym :
public abstract_linear_solver<MAT, VECT> {
182 void operator ()(
const MAT &M, VECT &x,
const VECT &b,
184 bool ok = gmm::MUMPS_solve(M, x, b,
true);
185 iter.enforce_converged(ok);
190 #if GETFEM_PARA_LEVEL > 1 && GETFEM_PARA_SOLVER == MUMPS_PARA_SOLVER
191 template <
typename MAT,
typename VECT>
192 struct linear_solver_distributed_mumps
193 :
public abstract_linear_solver<MAT, VECT> {
194 void operator ()(
const MAT &M, VECT &x,
const VECT &b,
196 double tt_ref=MPI_Wtime();
197 bool ok = MUMPS_distributed_matrix_solve(M, x, b,
false);
198 iter.enforce_converged(ok);
199 if (MPI_IS_MASTER()) cout<<
"UNSYMMETRIC MUMPS time "<< MPI_Wtime() - tt_ref<<endl;
203 template <
typename MAT,
typename VECT>
204 struct linear_solver_distributed_mumps_sym
205 :
public abstract_linear_solver<MAT, VECT> {
206 void operator ()(
const MAT &M, VECT &x,
const VECT &b,
208 double tt_ref=MPI_Wtime();
209 bool ok = MUMPS_distributed_matrix_solve(M, x, b,
true);
210 iter.enforce_converged(ok);
211 if (MPI_IS_MASTER()) cout<<
"SYMMETRIC MUMPS time "<< MPI_Wtime() - tt_ref<<endl;
221 struct abstract_newton_line_search {
222 double conv_alpha, conv_r;
223 size_t it, itmax, glob_it;
225 virtual void init_search(
double r,
size_t git,
double R0 = 0.0) = 0;
226 virtual double next_try(
void) = 0;
227 virtual bool is_converged(
double,
double R1 = 0.0) = 0;
228 virtual double converged_value(
void) {
232 virtual double converged_residual(
void) {
return conv_r; };
233 virtual ~abstract_newton_line_search() { }
237 struct newton_search_with_step_control :
public abstract_newton_line_search {
239 virtual void init_search(
double ,
size_t ,
double = 0.0)
240 { GMM_ASSERT1(
false,
"Not to be used"); }
242 virtual double next_try(
void)
243 { GMM_ASSERT1(
false,
"Not to be used"); }
245 virtual bool is_converged(
double ,
double = 0.0)
246 { GMM_ASSERT1(
false,
"Not to be used"); }
248 newton_search_with_step_control() {}
252 struct quadratic_newton_line_search :
public abstract_newton_line_search {
255 double alpha, alpha_mult, first_res, alpha_max_ratio, alpha_min;
256 virtual void init_search(
double r,
size_t git,
double R0 = 0.0) {
257 GMM_ASSERT1(R0 != 0.0,
"You have to specify R0");
259 conv_alpha =
alpha = double(1); conv_r = first_res = r; it = 0;
262 virtual double next_try(
void) {
264 if (it == 1)
return double(1);
265 GMM_ASSERT1(R1_ != 0.0,
"You have to specify R1");
266 double a = R0_ / R1_;
267 return (a < 0) ? (a*0.5 + sqrt(a*a*0.25-a)) : a*0.5;
269 virtual bool is_converged(
double r,
double R1 = 0.0) {
271 R1_ = R1;
return ((gmm::abs(R1_) < gmm::abs(R0_*0.5)) || it >= itmax);
273 quadratic_newton_line_search(
size_t imax =
size_t(-1)) { itmax = imax; }
277 struct simplest_newton_line_search :
public abstract_newton_line_search {
278 double alpha, alpha_mult, first_res, alpha_max_ratio, alpha_min,
280 virtual void init_search(
double r,
size_t git,
double = 0.0) {
282 conv_alpha =
alpha = double(1); conv_r = first_res = r; it = 0;
284 virtual double next_try(
void)
285 { conv_alpha =
alpha;
alpha *= alpha_mult; ++it;
return conv_alpha; }
286 virtual bool is_converged(
double r,
double = 0.0) {
288 return ((it <= 1 && r < first_res)
289 || (r <= first_res * alpha_max_ratio && r <= alpha_threshold_res)
290 || (conv_alpha <= alpha_min && r < first_res * 1e5)
293 simplest_newton_line_search
294 (
size_t imax =
size_t(-1),
double a_max_ratio = 6.0/5.0,
295 double a_min = 1.0/1000.0,
double a_mult = 3.0/5.0,
296 double a_threshold_res = 1e50)
297 : alpha_mult(a_mult), alpha_max_ratio(a_max_ratio), alpha_min(a_min),
298 alpha_threshold_res(a_threshold_res)
302 struct default_newton_line_search :
public abstract_newton_line_search {
320 double alpha, alpha_old, alpha_mult, first_res, alpha_max_ratio;
321 double alpha_min_ratio, alpha_min;
323 bool max_ratio_reached;
324 double alpha_max_ratio_reached, r_max_ratio_reached;
327 virtual void init_search(
double r,
size_t git,
double = 0.0);
328 virtual double next_try(
void);
329 virtual bool is_converged(
double r,
double = 0.0);
330 default_newton_line_search(
void) { count_pat = 0; }
334 struct basic_newton_line_search :
public abstract_newton_line_search {
335 double alpha, alpha_mult, first_res, alpha_max_ratio;
336 double alpha_min, prev_res, alpha_max_augment;
337 virtual void init_search(
double r,
size_t git,
double = 0.0) {
339 conv_alpha =
alpha = double(1);
340 prev_res = conv_r = first_res = r; it = 0;
342 virtual double next_try(
void)
343 { conv_alpha =
alpha;
alpha *= alpha_mult; ++it;
return conv_alpha; }
344 virtual bool is_converged(
double r,
double = 0.0) {
345 if (glob_it == 0 || (r < first_res /
double(2))
346 || (conv_alpha <= alpha_min && r < first_res * alpha_max_augment)
348 { conv_r = r;
return true; }
349 if (it > 1 && r > prev_res && prev_res < alpha_max_ratio * first_res)
351 prev_res = conv_r = r;
354 basic_newton_line_search
355 (
size_t imax =
size_t(-1),
356 double a_max_ratio = 5.0/3.0,
357 double a_min = 1.0/1000.0,
double a_mult = 3.0/5.0,
double a_augm = 2.0)
358 : alpha_mult(a_mult), alpha_max_ratio(a_max_ratio),
359 alpha_min(a_min), alpha_max_augment(a_augm) { itmax = imax; }
363 struct systematic_newton_line_search :
public abstract_newton_line_search {
364 double alpha, alpha_mult, first_res;
365 double alpha_min, prev_res;
367 virtual void init_search(
double r,
size_t git,
double = 0.0) {
369 conv_alpha =
alpha = double(1);
370 prev_res = conv_r = first_res = r; it = 0; first =
true;
372 virtual double next_try(
void)
373 {
double a =
alpha;
alpha *= alpha_mult; ++it;
return a; }
374 virtual bool is_converged(
double r,
double = 0.0) {
376 if (r < conv_r || first)
377 { conv_r = r; conv_alpha =
alpha / alpha_mult; first =
false; }
378 if ((alpha <= alpha_min*alpha_mult) || it >= itmax)
return true;
381 systematic_newton_line_search
382 (
size_t imax =
size_t(-1),
383 double a_min = 1.0/10000.0,
double a_mult = 3.0/5.0)
384 : alpha_mult(a_mult), alpha_min(a_min) { itmax = imax; }
412 template <
typename PB>
415 typedef typename gmm::linalg_traits<typename PB::VECTOR>::value_type T;
416 typedef typename gmm::number_traits<T>::magnitude_type R;
418 iter_linsolv0.reduce_noisy();
419 iter_linsolv0.set_resmax(iter.get_resmax()/20.0);
420 iter_linsolv0.set_maxiter(10000);
422 pb.compute_residual();
423 R approx_eln = pb.approx_external_load_norm();
424 if (approx_eln == R(0)) approx_eln = R(1);
426 typename PB::VECTOR b0(gmm::vect_size(pb.residual()));
427 gmm::copy(pb.residual(), b0);
428 typename PB::VECTOR Xi(gmm::vect_size(pb.residual()));
429 gmm::copy(pb.state_vector(), Xi);
431 typename PB::VECTOR dr(gmm::vect_size(pb.residual()));
433 R alpha0(0),
alpha(1), res0(gmm::vect_norm1(b0)), minres(res0);
440 scalar_type crit = pb.residual_norm() / approx_eln;
441 if (iter.finished(crit))
return;
446 if (!iter.converged(crit)) {
450 while (is_singular) {
452 pb.add_to_residual(b0, alpha-R(1));
454 if (iter.get_noisy() > 1)
455 cout <<
"starting tangent matrix computation" << endl;
456 pb.compute_tangent_matrix();
457 if (iter.get_noisy() > 1)
458 cout <<
"starting linear solver" << endl;
459 pb.linear_solve(dr, iter_linsolv);
460 if (!iter_linsolv.converged()) {
462 if (is_singular <= 4) {
463 if (iter.get_noisy())
464 cout <<
"Singular tangent matrix:"
465 " perturbation of the state vector." << endl;
467 pb.compute_residual();
469 if (iter.get_noisy())
470 cout <<
"Singular tangent matrix: perturbation failed, "
471 <<
"aborting." << endl;
475 else is_singular = 0;
477 if (iter.get_noisy() > 1) cout <<
"linear solver done" << endl;
479 gmm::add(dr, pb.state_vector());
480 pb.compute_residual();
482 R dec = R(1)/R(2), coeff2 = coeff * R(1.5);
484 while (dec > R(1E-5) && res >= res0 * coeff) {
485 gmm::add(gmm::scaled(dr, -dec), pb.state_vector());
486 pb.compute_residual();
488 if (res2 < res*R(0.95) || res2 >= res0 * coeff2) {
489 dec /= R(2); res = res2; coeff2 *= R(1.5);
491 gmm::add(gmm::scaled(dr, dec), pb.state_vector());
498 coeff = std::max(R(1.05), coeff*R(0.93));
499 bool near_end = (iter.get_iteration() > iter.get_maxiter()/2);
500 bool cut = (
alpha < R(1)) && near_end;
501 if ((res > minres && nit > 4) || cut) {
504 if ((stagn > 10 && alpha > alpha0 + R(5E-2)) || cut) {
506 if (near_end)
alpha = R(1);
507 gmm::copy(Xi, pb.state_vector());
508 pb.compute_residual();
511 stagn = 0; coeff = R(2);
514 if (res < minres || (alpha == R(1) && nit < 10)) stagn = 0;
516 if (nit < 5) minres = res0;
else minres = std::min(minres, res0);
518 if (iter.get_noisy())
519 cout <<
"step control [" << std::setw(8) << alpha0 <<
","
520 << std::setw(8) <<
alpha <<
"," << std::setw(10) << dec <<
"]";
524 crit = res / approx_eln;
527 if (iter.finished(crit)) {
528 if (iter.converged() && alpha < R(1)) {
530 alpha = std::min(R(1), alpha*R(3) - alpha0*R(2));
532 gmm::copy(pb.state_vector(), Xi);
533 nit = 0; stagn = 0; coeff = R(2);
545 template <
typename PB>
548 typedef typename gmm::linalg_traits<typename PB::VECTOR>::value_type T;
549 typedef typename gmm::number_traits<T>::magnitude_type R;
551 iter_linsolv0.reduce_noisy();
552 iter_linsolv0.set_resmax(iter.get_resmax()/20.0);
553 iter_linsolv0.set_maxiter(10000);
555 pb.compute_residual();
556 R approx_eln = pb.approx_external_load_norm();
557 if (approx_eln == R(0)) approx_eln = R(1);
559 typename PB::VECTOR dr(gmm::vect_size(pb.residual()));
561 scalar_type crit = pb.residual_norm() / approx_eln;
562 while (!iter.finished(crit)) {
566 while (is_singular) {
569 if (iter.get_noisy() > 1)
570 cout <<
"starting computing tangent matrix" << endl;
571 pb.compute_tangent_matrix();
572 if (iter.get_noisy() > 1)
573 cout <<
"starting linear solver" << endl;
574 pb.linear_solve(dr, iter_linsolv);
575 if (!iter_linsolv.converged()) {
577 if (is_singular <= 4) {
578 if (iter.get_noisy())
579 cout <<
"Singular tangent matrix:"
580 " perturbation of the state vector." << endl;
582 pb.compute_residual();
584 if (iter.get_noisy())
585 cout <<
"Singular tangent matrix: perturbation failed, aborting."
590 else is_singular = 0;
593 if (iter.get_noisy() > 1) cout <<
"linear solver done" << endl;
594 R
alpha = pb.line_search(dr, iter);
596 if (iter.get_noisy()) cout <<
"alpha = " << std::setw(6) <<
alpha <<
" ";
598 crit = std::min(pb.residual_norm() / approx_eln,
599 gmm::vect_norm1(dr) / std::max(1E-25, pb.state_norm()));
609 typedef std::shared_ptr<abstract_linear_solver<model_real_sparse_matrix,
610 model_real_plain_vector> >
611 rmodel_plsolver_type;
612 typedef std::shared_ptr<abstract_linear_solver<model_complex_sparse_matrix,
613 model_complex_plain_vector> >
614 cmodel_plsolver_type;
616 template<
typename MATRIX,
typename VECTOR>
617 std::shared_ptr<abstract_linear_solver<MATRIX, VECTOR>>
618 default_linear_solver(
const model &md) {
620 #if GETFEM_PARA_LEVEL == 1 && GETFEM_PARA_SOLVER == MUMPS_PARA_SOLVER
621 if (md.is_symmetric())
622 return std::make_shared<linear_solver_mumps_sym<MATRIX, VECTOR>>();
624 return std::make_shared<linear_solver_mumps<MATRIX, VECTOR>>();
625 #elif GETFEM_PARA_LEVEL > 1 && GETFEM_PARA_SOLVER == MUMPS_PARA_SOLVER
626 if (md.is_symmetric())
627 return std::make_shared
628 <linear_solver_distributed_mumps_sym<MATRIX, VECTOR>>();
630 return std::make_shared
631 <linear_solver_distributed_mumps<MATRIX, VECTOR>>();
633 size_type ndof = md.nb_dof(), max3d = 15000, dim = md.leading_dimension();
634 # ifdef GMM_USES_MUMPS
637 if ((ndof<300000 && dim<=2) || (ndof<max3d && dim<=3) || (ndof<1000)) {
638 # ifdef GMM_USES_MUMPS
639 if (md.is_symmetric())
640 return std::make_shared<linear_solver_mumps_sym<MATRIX, VECTOR>>();
642 return std::make_shared<linear_solver_mumps<MATRIX, VECTOR>>();
644 return std::make_shared<linear_solver_superlu<MATRIX, VECTOR>>();
648 if (md.is_coercive())
649 return std::make_shared
650 <linear_solver_cg_preconditioned_ildlt<MATRIX, VECTOR>>();
653 return std::make_shared
654 <linear_solver_gmres_preconditioned_ilut<MATRIX,VECTOR>>();
656 return std::make_shared
657 <linear_solver_gmres_preconditioned_ilu<MATRIX,VECTOR>>();
661 return std::shared_ptr<abstract_linear_solver<MATRIX, VECTOR>>();
664 template <
typename MATRIX,
typename VECTOR>
665 std::shared_ptr<abstract_linear_solver<MATRIX, VECTOR>>
666 select_linear_solver(
const model &md,
const std::string &name) {
667 std::shared_ptr<abstract_linear_solver<MATRIX, VECTOR>> p;
668 if (bgeot::casecmp(name,
"superlu") == 0)
669 return std::make_shared<linear_solver_superlu<MATRIX, VECTOR>>();
670 else if (bgeot::casecmp(name,
"dense_lu") == 0)
671 return std::make_shared<linear_solver_dense_lu<MATRIX, VECTOR>>();
672 else if (bgeot::casecmp(name,
"mumps") == 0) {
673 #ifdef GMM_USES_MUMPS
674 # if GETFEM_PARA_LEVEL <= 1
675 return std::make_shared<linear_solver_mumps<MATRIX, VECTOR>>();
677 return std::make_shared
678 <linear_solver_distributed_mumps<MATRIX, VECTOR>>();
681 GMM_ASSERT1(
false,
"Mumps is not interfaced");
684 else if (bgeot::casecmp(name,
"cg/ildlt") == 0)
685 return std::make_shared
686 <linear_solver_cg_preconditioned_ildlt<MATRIX, VECTOR>>();
687 else if (bgeot::casecmp(name,
"gmres/ilu") == 0)
688 return std::make_shared
689 <linear_solver_gmres_preconditioned_ilu<MATRIX, VECTOR>>();
690 else if (bgeot::casecmp(name,
"gmres/ilut") == 0)
691 return std::make_shared
692 <linear_solver_gmres_preconditioned_ilut<MATRIX, VECTOR>>();
693 else if (bgeot::casecmp(name,
"gmres/ilutp") == 0)
694 return std::make_shared
695 <linear_solver_gmres_preconditioned_ilutp<MATRIX, VECTOR>>();
696 else if (bgeot::casecmp(name,
"auto") == 0)
697 return default_linear_solver<MATRIX, VECTOR>(md);
699 GMM_ASSERT1(
false,
"Unknown linear solver");
700 return std::shared_ptr<abstract_linear_solver<MATRIX, VECTOR>>();
703 inline rmodel_plsolver_type rselect_linear_solver(
const model &md,
704 const std::string &name) {
705 return select_linear_solver<model_real_sparse_matrix,
706 model_real_plain_vector>(md, name);
709 inline cmodel_plsolver_type cselect_linear_solver(
const model &md,
710 const std::string &name) {
711 return select_linear_solver<model_complex_sparse_matrix,
712 model_complex_plain_vector>(md, name);
743 rmodel_plsolver_type lsolver,
744 abstract_newton_line_search &ls);
747 cmodel_plsolver_type lsolver,
748 abstract_newton_line_search &ls);
751 rmodel_plsolver_type lsolver);
754 cmodel_plsolver_type lsolver);