GetFEM  5.5
getfem_model_solvers.cc
1 /*===========================================================================
2 
3  Copyright (C) 2009-2026 Yves Renard
4 
5  This file is a part of GetFEM
6 
7  GetFEM is free software; you can redistribute it and/or modify it
8  under the terms of the GNU Lesser General Public License as published
9  by the Free Software Foundation; either version 3 of the License, or
10  (at your option) any later version along with the GCC Runtime Library
11  Exception either version 3.1 or (at your option) any later version.
12  This program is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15  License and GCC Runtime Library Exception for more details.
16  You should have received a copy of the GNU Lesser General Public License
17  along with this program. If not, see https://www.gnu.org/licenses/.
18 
19 ===========================================================================*/
20 
22 #include "gmm/gmm_inoutput.h"
23 #include <iomanip>
24 
25 namespace getfem {
26 
27  #define TRACE_SOL 0
28 
29  /* ***************************************************************** */
30  /* Intermediary structure for Newton algorithms with getfem::model. */
31  /* ***************************************************************** */
32 
33  template <typename PLSOLVER>
34  class pb_base {
35  public:
36  typedef typename PLSOLVER::element_type::MATRIX MATRIX;
37  typedef typename PLSOLVER::element_type::VECTOR VECTOR;
38  typedef typename gmm::linalg_traits<VECTOR>::value_type T;
39  typedef typename gmm::number_traits<T>::magnitude_type R;
40 
41  protected:
42  PLSOLVER linear_solver;
43  const MATRIX &K;
44  VECTOR &rhs;
45  VECTOR state;
46 
47  public:
48  virtual const VECTOR &residual() const { return rhs; }
49  // A norm1 seems to be better than a norm2 (at least for contact problems).
50  virtual R residual_norm() { return gmm::vect_norm1(residual()); }
51  virtual const VECTOR &state_vector() const { return state; }
52  virtual VECTOR &state_vector() { return state; }
53  virtual R state_norm() const { return gmm::vect_norm1(state_vector()); }
54 
55  virtual void perturbation() {
56  R res = gmm::vect_norm2(state), ampl = std::max(res*R(1E-20), R(1E-50));
57  std::vector<R> V(gmm::vect_size(state));
59  gmm::add(gmm::scaled(V, ampl), state);
60  }
61 
62  virtual void add_to_residual(VECTOR &extra_rhs, R mult=1.) {
63  if (mult == R(1)) gmm::add(extra_rhs, rhs);
64  else if (mult != R(0)) gmm::add(gmm::scaled(extra_rhs, mult), rhs);
65  }
66 
67  virtual void linear_solve(VECTOR &dr, gmm::iteration &iter) {
68  (*linear_solver)(K, dr, rhs, iter);
69  }
70 
71  pb_base(PLSOLVER linsolv, const MATRIX &K_, VECTOR &rhs_)
72  : linear_solver(linsolv), K(K_), rhs(rhs_), state(gmm::vect_size(rhs_)) {}
73  virtual ~pb_base() {}
74  };
75 
76  /* ***************************************************************** */
77  /* Linear model problem. */
78  /* ***************************************************************** */
79  template <typename PLSOLVER>
80  class lin_model_pb : pb_base<PLSOLVER> {
81  model &md;
82  public:
83  using typename pb_base<PLSOLVER>::VECTOR;
84  using typename pb_base<PLSOLVER>::R;
85  using pb_base<PLSOLVER>::state_vector;
86  using pb_base<PLSOLVER>::linear_solve;
87  void compute_all() { md.assembly(model::BUILD_ALL); }
88  lin_model_pb(model &, PLSOLVER) = delete;
89  };
90 
91  template <>
92  lin_model_pb<rmodel_plsolver_type>::lin_model_pb
93  (model &md_, rmodel_plsolver_type linsolv)
94  : pb_base<rmodel_plsolver_type>
95  (linsolv, md_.real_tangent_matrix(), md_.set_real_rhs()),
96  md(md_)
97  { md.from_variables(state_vector()); }
98  template <>
99  lin_model_pb<cmodel_plsolver_type>::lin_model_pb
100  (model &md_, cmodel_plsolver_type linsolv)
101  : pb_base<cmodel_plsolver_type>
102  (linsolv, md_.complex_tangent_matrix(), md_.set_complex_rhs()),
103  md(md_)
104  { md.from_variables(state_vector()); }
105 
106 
107  /* ***************************************************************** */
108  /* Non-linear model problem. */
109  /* ***************************************************************** */
110  template <typename PLSOLVER>
111  class nonlin_model_pb : protected pb_base<PLSOLVER> {
112  public:
113  using typename pb_base<PLSOLVER>::VECTOR;
114  using typename pb_base<PLSOLVER>::R;
115  protected:
116  model &md;
117  abstract_newton_line_search &ls;
118  private:
119  VECTOR stateinit; // just a temp used in line_search, could also be mutable
120  public:
121  using pb_base<PLSOLVER>::residual;
122  using pb_base<PLSOLVER>::residual_norm;
123  using pb_base<PLSOLVER>::state_vector;
124  using pb_base<PLSOLVER>::state_norm;
125  using pb_base<PLSOLVER>::add_to_residual;
126  using pb_base<PLSOLVER>::perturbation;
127  using pb_base<PLSOLVER>::linear_solve;
128 
129  virtual R approx_external_load_norm() { return md.approx_external_load(); }
130 
131  virtual void compute_tangent_matrix() {
132  md.to_variables(state_vector());
133  md.assembly(model::BUILD_MATRIX);
134  }
135 
136  virtual void compute_residual() {
137  md.to_variables(state_vector());
138  md.assembly(model::BUILD_RHS);
139  }
140 
141  virtual R line_search(VECTOR &dr, const gmm::iteration &iter) {
142  size_type nit = 0;
143  gmm::resize(stateinit, gmm::vect_size(state_vector()));
144  gmm::copy(state_vector(), stateinit);
145  R alpha(1), res, /* res_init, */ R0;
146 
147  /* res_init = */ res = residual_norm();
148  // cout << "first residual = " << residual() << endl << endl;
149  R0 = gmm::real(gmm::vect_sp(dr, residual()));
150  ls.init_search(res, iter.get_iteration(), R0);
151  do {
152  alpha = ls.next_try();
153  gmm::add(stateinit, gmm::scaled(dr, alpha), state_vector());
154 
155  compute_residual();
156  res = residual_norm();
157  // cout << "residual = " << residual() << endl << endl;
158  R0 = gmm::real(gmm::vect_sp(dr, residual()));
159  ++ nit;
160  } while (!ls.is_converged(res, R0));
161 
162  if (alpha != ls.converged_value()) {
163  alpha = ls.converged_value();
164  gmm::add(stateinit, gmm::scaled(dr, alpha), state_vector());
165  res = ls.converged_residual();
166  compute_residual();
167  }
168 
169  return alpha;
170  }
171 
172  nonlin_model_pb(model &md_, abstract_newton_line_search &ls_,
173  PLSOLVER linear_solver_) = delete;
174  };
175 
176  template <>
177  nonlin_model_pb<rmodel_plsolver_type>::nonlin_model_pb
178  (model &md_, abstract_newton_line_search &ls_, rmodel_plsolver_type linsolv)
179  : pb_base<rmodel_plsolver_type>
180  (linsolv, md_.real_tangent_matrix(), md_.set_real_rhs()),
181  md(md_), ls(ls_)
182  { md.from_variables(state_vector()); }
183  template <>
184  nonlin_model_pb<cmodel_plsolver_type>::nonlin_model_pb
185  (model &md_, abstract_newton_line_search &ls_, cmodel_plsolver_type linsolv)
186  : pb_base<cmodel_plsolver_type>
187  (linsolv, md_.complex_tangent_matrix(), md_.set_complex_rhs()),
188  md(md_), ls(ls_)
189  { md.from_variables(state_vector()); }
190 
191 
192 
193  /* ***************************************************************** */
194  /* Non-linear model problem with internal variable condensation. */
195  /* ***************************************************************** */
196  template <typename PLSOLVER>
197  class nonlin_condensed_model_pb : public nonlin_model_pb<PLSOLVER> {
198  public:
199  using typename pb_base<PLSOLVER>::MATRIX;
200  using typename pb_base<PLSOLVER>::VECTOR;
201  using typename pb_base<PLSOLVER>::R;
202 
203  private:
204  using nonlin_model_pb<PLSOLVER>::md;
205  bool condensed_vars;
206  const MATRIX &internal_K;
207  VECTOR &full_rhs;
208 
209  public:
210  virtual const VECTOR &residual() const { return full_rhs; }
211  // A norm1 seems to be better than a norm2 (at least for contact problems).
212  virtual R residual_norm() { return gmm::vect_norm1(full_rhs); }
213 
214  using pb_base<PLSOLVER>::state_vector;
215  using pb_base<PLSOLVER>::state_norm;
216 
217  virtual void add_to_residual(VECTOR &extra_rhs, R mult=1.) {
218  if (mult == R(1)) gmm::add(extra_rhs, full_rhs);
219  else if (mult != R(0)) gmm::add(gmm::scaled(extra_rhs, mult), full_rhs);
220  }
221 
222  using pb_base<PLSOLVER>::perturbation;
223 
224  virtual void linear_solve(VECTOR &dr, gmm::iteration &iter) {
225  VECTOR dr0(md.nb_primary_dof(), R(0));
226  pb_base<PLSOLVER>::linear_solve(dr0, iter);
227  gmm::sub_interval I_prim(0, md.nb_primary_dof()),
228  I_intern(md.nb_primary_dof(), md.nb_internal_dof());
229  gmm::copy(dr0, gmm::sub_vector(dr, I_prim));
230  // recover solution of condensed variables: intern_K*prim_sol + intern_sol
231  gmm::mult(internal_K,
232  gmm::scaled(gmm::sub_vector(dr, I_prim), scalar_type(-1)),
233  md.internal_solution(),
234  gmm::sub_vector(dr, I_intern));
235  }
236 
237  virtual void compute_tangent_matrix() {
238  md.to_variables(state_vector(), condensed_vars);
239  md.assembly(condensed_vars ? model::BUILD_MATRIX_CONDENSED
240  : model::BUILD_MATRIX);
241  }
242 
243  virtual void compute_residual() {
244  md.to_variables(state_vector(), condensed_vars);
245  md.assembly(condensed_vars ? model::BUILD_RHS_WITH_INTERNAL // --> full_rhs
246  : model::BUILD_RHS); // ---> rhs (= full_rhs)
247  }
248 
249  nonlin_condensed_model_pb(model &md_, abstract_newton_line_search &ls_,
250  PLSOLVER linear_solver_) = delete;
251  };
252 
253  template <>
254  nonlin_condensed_model_pb<rmodel_plsolver_type>::nonlin_condensed_model_pb
255  (model &md_, abstract_newton_line_search &ls_, rmodel_plsolver_type linsolv)
256  : nonlin_model_pb<rmodel_plsolver_type>(md_, ls_, linsolv),
257  condensed_vars(md_.has_internal_variables()),
258  internal_K(md_.real_tangent_matrix(condensed_vars)),
259  full_rhs(md_.set_real_rhs(condensed_vars))
260  {
261  gmm::resize(state_vector(), md.nb_dof(condensed_vars));
262  md.from_variables(state_vector(), condensed_vars);
263  }
264 
265  template <>
266  nonlin_condensed_model_pb<cmodel_plsolver_type>::nonlin_condensed_model_pb
267  (model &md_, abstract_newton_line_search &ls_, cmodel_plsolver_type linsolv)
268  : nonlin_model_pb<cmodel_plsolver_type>(md_, ls_, linsolv),
269  condensed_vars(false), internal_K(md_.complex_tangent_matrix()),
270  full_rhs(md_.set_complex_rhs())
271  {
272  GMM_ASSERT1(false, "No support for internal variable condensation in "
273  "complex valued models.");
274  }
275 
276 
277 
278  /* ***************************************************************** */
279  /* Linear solvers. */
280  /* ***************************************************************** */
281  static rmodel_plsolver_type rdefault_linear_solver(const model &md) {
282  return default_linear_solver<model_real_sparse_matrix,
283  model_real_plain_vector>(md);
284  }
285 
286  static cmodel_plsolver_type cdefault_linear_solver(const model &md) {
287  return default_linear_solver<model_complex_sparse_matrix,
288  model_complex_plain_vector>(md);
289  }
290 
291  void default_newton_line_search::init_search(double r, size_t git, double) {
292  alpha_min_ratio = 0.9;
293  alpha_min = 1e-10;
294  alpha_max_ratio = 10.0;
295  alpha_mult = 0.25;
296  itmax = size_type(-1);
297  glob_it = git; if (git <= 1) count_pat = 0;
298  conv_alpha = alpha = alpha_old = 1.;
299  conv_r = first_res = r; it = 0;
300  count = 0;
301  max_ratio_reached = false;
302  }
303 
304  double default_newton_line_search::next_try() {
305  alpha_old = alpha; ++it;
306  // alpha *= 0.5;
307  if (alpha >= 0.4) alpha *= 0.5; else alpha *= alpha_mult;
308  return alpha_old;
309  }
310 
311  bool default_newton_line_search::is_converged(double r, double) {
312  // cout << "r = " << r << " alpha = " << alpha_old << " count_pat = " << count_pat << endl;
313  if (!max_ratio_reached && r < first_res * alpha_max_ratio) {
314  alpha_max_ratio_reached = alpha_old; r_max_ratio_reached = r;
315  it_max_ratio_reached = it; max_ratio_reached = true;
316  }
317  if (max_ratio_reached &&
318  r < r_max_ratio_reached * 0.5 &&
319  r > first_res * 1.1 && it <= it_max_ratio_reached+1) {
320  alpha_max_ratio_reached = alpha_old; r_max_ratio_reached = r;
321  it_max_ratio_reached = it;
322  }
323  if (count == 0 || r < conv_r)
324  { conv_r = r; conv_alpha = alpha_old; count = 1; }
325  if (conv_r < first_res) ++count;
326 
327  if (r < first_res * alpha_min_ratio)
328  { count_pat = 0; return true; }
329  if (count>=5 || (alpha < alpha_min && max_ratio_reached) || alpha<1e-15) {
330  if (conv_r < first_res * 0.99) count_pat = 0;
331  if (/*gmm::random() * 50. < -log(conv_alpha)-4.0 ||*/ count_pat >= 3)
332  { conv_r=r_max_ratio_reached; conv_alpha=alpha_max_ratio_reached; }
333  if (conv_r >= first_res * 0.999) count_pat++;
334  return true;
335  }
336  return false;
337  }
338 
339 
340  /* ***************************************************************** */
341  /* Computation of initial values of velocity/acceleration for */
342  /* time integration schemes. */
343  /* ***************************************************************** */
344 
345  template <typename PLSOLVER>
346  void compute_init_values(model &md, gmm::iteration &iter,
347  PLSOLVER lsolver,
348  abstract_newton_line_search &ls) {
349 
350  typename PLSOLVER::element_type::VECTOR state(md.nb_dof());
351  md.from_variables(state);
352  md.cancel_init_step();
353  md.set_time_integration(2);
354  scalar_type dt = md.get_time_step();
355  scalar_type ddt = md.get_init_time_step();
356  scalar_type t = md.get_time();
357 
358  // Solve for ddt
359  md.set_time_step(ddt);
360  gmm::iteration iter1 = iter;
361  standard_solve(md, iter1, lsolver, ls);
362  md.copy_init_time_derivative();
363 
364  // Restore the model state
365  md.set_time_step(dt);
366  md.set_time(t);
367  md.to_variables(state);
368  md.set_time_integration(1);
369  }
370 
371  /* ***************************************************************** */
372  /* Standard solve. */
373  /* ***************************************************************** */
374 
375  template <typename PLSOLVER>
376  void standard_solve(model &md, gmm::iteration &iter,
377  PLSOLVER lsolver,
378  abstract_newton_line_search &ls) {
379 
380  int time_integration = md.is_time_integration();
381  if (time_integration) {
382  if (time_integration == 1 && md.is_init_step()) {
383  compute_init_values(md, iter, lsolver, ls);
384  return;
385  }
386  md.set_time(md.get_time() + md.get_time_step());
387  md.call_init_affine_dependent_variables(time_integration);
388  }
389 
390  if (md.is_linear()) {
391  lin_model_pb<PLSOLVER> mdpb(md, lsolver);
392  mdpb.compute_all();
393  mdpb.linear_solve(mdpb.state_vector(), iter);
394  md.to_variables(mdpb.state_vector()); // copy the state vector into the model variables
395  } else {
396  std::unique_ptr<nonlin_model_pb<PLSOLVER>> mdpb;
397  if (md.has_internal_variables())
398  mdpb = std::make_unique<nonlin_condensed_model_pb<PLSOLVER>>(md, ls, lsolver);
399  else
400  mdpb = std::make_unique<nonlin_model_pb<PLSOLVER>>(md, ls, lsolver);
401  if (dynamic_cast<newton_search_with_step_control *>(&ls))
402  Newton_with_step_control(*mdpb, iter);
403  else
404  classical_Newton(*mdpb, iter);
405  md.to_variables(mdpb->state_vector()); // copy the state vector into the model variables
406  }
407  }
408 
410  rmodel_plsolver_type lsolver,
411  abstract_newton_line_search &ls) {
412  standard_solve<rmodel_plsolver_type>(md, iter, lsolver, ls);
413  }
414 
415  void standard_solve(model &md, gmm::iteration &iter,
416  cmodel_plsolver_type lsolver,
417  abstract_newton_line_search &ls) {
418  standard_solve<cmodel_plsolver_type>(md, iter, lsolver, ls);
419  }
420 
421 
422  void standard_solve(model &md, gmm::iteration &iter,
423  rmodel_plsolver_type lsolver) {
424  default_newton_line_search ls;
425  standard_solve(md, iter, lsolver, ls);
426  }
427 
428  void standard_solve(model &md, gmm::iteration &iter,
429  cmodel_plsolver_type lsolver) {
430  newton_search_with_step_control ls;
431  // default_newton_line_search ls;
432  standard_solve(md, iter, lsolver, ls);
433  }
434 
435  void standard_solve(model &md, gmm::iteration &iter) {
436  newton_search_with_step_control ls;
437  // default_newton_line_search ls;
438  if (md.is_complex())
439  standard_solve(md, iter, cdefault_linear_solver(md), ls);
440  else
441  standard_solve(md, iter, rdefault_linear_solver(md), ls);
442  }
443 
444 
445 
446 } /* end of namespace getfem. */
447 
`‘Model’' variables store the variables, the data and the description of a model.
The Iteration object calculates whether the solution has reached the desired accuracy,...
Definition: gmm_iter.h:52
Standard solvers for model bricks.
void copy(const L1 &l1, L2 &l2)
*‍/
Definition: gmm_blas.h:976
number_traits< typename linalg_traits< V >::value_type >::magnitude_type vect_norm2(const V &v)
Euclidean norm of a vector.
Definition: gmm_blas.h:556
void fill_random(L &l)
fill a vector or matrix with random value (uniform [-1,1]).
Definition: gmm_blas.h:129
void resize(V &v, size_type n)
*‍/
Definition: gmm_blas.h:209
void mult(const L1 &l1, const L2 &l2, L3 &l3)
*‍/
Definition: gmm_blas.h:1663
number_traits< typename linalg_traits< V >::value_type >::magnitude_type vect_norm1(const V &v)
1-norm of a vector
Definition: gmm_blas.h:645
void add(const L1 &l1, L2 &l2)
*‍/
Definition: gmm_blas.h:1275
Input/output on sparse matrices.
size_t size_type
used as the common size type in the library
Definition: bgeot_poly.h:48
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
GEneric Tool for Finite Element Methods.
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.