Hermes2D  2.0
picard_solver.cpp
1 // This file is part of Hermes2D
2 //
3 // Copyright (c) 2009 hp-FEM group at the University of Nevada, Reno (UNR).
4 // Email: hpfem-group@unr.edu, home page: http://hpfem.org/.
5 //
6 // Hermes2D is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published
8 // by the Free Software Foundation; either version 2 of the License,
9 // or (at your option) any later version.
10 //
11 // Hermes2D is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with Hermes2D; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 #include "picard_solver.h"
20 #include "projections/ogprojection.h"
21 #include "exact_solution.h"
22 
23 namespace Hermes
24 {
25  namespace Hermes2D
26  {
27  template<typename Scalar>
28  PicardSolver<Scalar>::PicardSolver()
29  : NonlinearSolver<Scalar>(new DiscreteProblem<Scalar>()), verbose_output_linear_solver(false), own_dp(true)
30  {
31  init();
32  }
33 
34  template<typename Scalar>
35  PicardSolver<Scalar>::PicardSolver(DiscreteProblem<Scalar>* dp)
36  : NonlinearSolver<Scalar>(dp), verbose_output_linear_solver(false), own_dp(false)
37  {
38  init();
39  }
40 
41  template<typename Scalar>
42  PicardSolver<Scalar>::PicardSolver(const WeakForm<Scalar>* wf, const Space<Scalar>* space)
43  : NonlinearSolver<Scalar>(new DiscreteProblem<Scalar>(wf, space)), verbose_output_linear_solver(false), own_dp(true)
44  {
45  init();
46  }
47 
48  template<typename Scalar>
49  PicardSolver<Scalar>::PicardSolver(const WeakForm<Scalar>* wf, Hermes::vector<const Space<Scalar>*> spaces)
50  : NonlinearSolver<Scalar>(new DiscreteProblem<Scalar>(wf, spaces)), verbose_output_linear_solver(false), own_dp(true)
51  {
52  init();
53  }
54 
55  template<typename Scalar>
57  {
58  (static_cast<DiscreteProblem<Scalar>*>(this->dp))->set_weak_formulation(wf);
59  }
60 
61  template<typename Scalar>
63  {
64  tol = 1e-4;
65  max_iter = 50;
66  num_last_vectors_used = 3;
67  anderson_beta = 1.0;
68  anderson_is_on = false;
69 
70  matrix = create_matrix<Scalar>();
71  rhs = create_vector<Scalar>();
72  linear_solver = create_linear_solver<Scalar>(matrix, rhs);
73  }
74 
75  template<typename Scalar>
77  {
78  if(static_cast<DiscreteProblem<Scalar>*>(this->dp)->get_weak_formulation() == NULL)
79  return false;
80  if(static_cast<DiscreteProblem<Scalar>*>(this->dp)->get_spaces().size() == 0)
81  return false;
82  return true;
83  }
84 
85  template<typename Scalar>
87  {
88  Hermes::vector<Space<Scalar>*> spaces;
89  for(unsigned int i = 0; i < static_cast<DiscreteProblem<Scalar>*>(this->dp)->get_spaces().size(); i++)
90  spaces.push_back(const_cast<Space<Scalar>*>(static_cast<DiscreteProblem<Scalar>*>(this->dp)->get_space(i)));
91 
93  const_cast<WeakForm<Scalar>*>(static_cast<DiscreteProblem<Scalar>*>(this->dp)->wf)->set_current_time(time);
94  }
95 
96  template<typename Scalar>
97  void PicardSolver<Scalar>::set_time_step(double time_step)
98  {
99  const_cast<WeakForm<Scalar>*>(static_cast<DiscreteProblem<Scalar>*>(this->dp)->wf)->set_current_time_step(time_step);
100  }
101 
102  template<typename Scalar>
103  PicardSolver<Scalar>::~PicardSolver()
104  {
105  delete matrix;
106  delete rhs;
107  delete linear_solver;
108  if(own_dp)
109  delete this->dp;
110  else
111  static_cast<DiscreteProblem<Scalar>*>(this->dp)->have_matrix = false;
112  }
113 
114  template<typename Scalar>
115  void PicardSolver<Scalar>::set_spaces(Hermes::vector<const Space<Scalar>*> spaces)
116  {
117  static_cast<DiscreteProblem<Scalar>*>(this->dp)->set_spaces(spaces);
118  }
119 
120  template<typename Scalar>
122  {
123  static_cast<DiscreteProblem<Scalar>*>(this->dp)->set_space(space);
124  }
125 
126  template<typename Scalar>
127  Hermes::vector<const Space<Scalar>*> PicardSolver<Scalar>::get_spaces() const
128  {
129  return static_cast<DiscreteProblem<Scalar>*>(this->dp)->get_spaces();
130  }
131 
132  template<typename Scalar>
134  {
135  this->verbose_output_linear_solver = to_set;
136  }
137 
138  template<typename Scalar>
139  void calculate_anderson_coeffs(Scalar** previous_vectors, Scalar* anderson_coeffs, int num_last_vectors_used, int ndof)
140  {
141  if(num_last_vectors_used <= 1) throw Hermes::Exceptions::Exception("Picard: Anderson acceleration makes sense only if at least two last iterations are used.");
142 
143  // If num_last_vectors_used is 2, then there is only one residual, and thus only one alpha coeff which is 1.0.
144  if(num_last_vectors_used == 2)
145  {
146  anderson_coeffs[0] = 1.0;
147  return;
148  }
149 
150  // In the following, num_last_vectors_used is at least three.
151  // Thematrix problem will have dimension num_last_vectors_used - 2.
152  int n = num_last_vectors_used - 2;
153 
154  // Allocate the matrix system for the Anderson coefficients.
155  double** mat = new_matrix<double>(n, n);
156  Scalar* rhs = new Scalar[n];
157 
158  // Set up the matrix and rhs vector.
159  for (int i = 0; i < n; i++)
160  {
161  // Calculate i-th entry of the rhs vector.
162  rhs[i] = 0;
163  for (int k = 0; k < ndof; k++)
164  {
165  Scalar residual_n_k = previous_vectors[n + 1][k] - previous_vectors[n][k];
166  Scalar residual_i_k = previous_vectors[i + 1][k] - previous_vectors[i][k];
167  rhs[i] += residual_n_k * (residual_n_k - residual_i_k);
168  }
169  for (int j = 0; j < n; j++)
170  {
171  Scalar val = 0;
172  for (int k = 0; k < ndof; k++)
173  {
174  Scalar residual_n_k = previous_vectors[n + 1][k] - previous_vectors[n][k];
175  Scalar residual_i_k = previous_vectors[i + 1][k] - previous_vectors[i][k];
176  Scalar residual_j_k = previous_vectors[j + 1][k] - previous_vectors[j][k];
177  val += (residual_n_k - residual_i_k) * (residual_n_k - residual_j_k);
178  }
179 
180  // FIXME: This is not a nice way to cast Scalar to double. Not mentioning
181  // that this will not work for Scalar = complex.
182  double* ptr = (double*)(&val);
183  mat[i][j] = *ptr;
184  }
185  }
186 
187  // Solve the matrix system.
188  double d;
189  int* perm = new int[n];
190  ludcmp(mat, n, perm, &d);
191  lubksb<Scalar>(mat, n, perm, rhs);
192 
193  // Use the result to define the Anderson coefficients. Remember that
194  // n were computed and the last one is 1.0 minus the sum of the 'n' numbers.
195  Scalar sum = 0;
196  for (int i = 0; i < n; i++)
197  {
198  anderson_coeffs[i] = rhs[i];
199  sum += rhs[i];
200  }
201  anderson_coeffs[n] = 1.0 - sum;
202 
203  // Clean up.
204  delete [] mat;
205  delete [] rhs;
206 
207  return;
208  }
209 
210  template<typename Scalar>
212  {
213  this->tol = tol;
214  }
215 
216  template<typename Scalar>
218  {
219  this->max_iter = max_iter;
220  }
221 
222  template<typename Scalar>
224  {
225  this->num_last_vectors_used = num;
226  }
227 
228  template<typename Scalar>
230  {
231  this->anderson_beta = beta;
232  }
233 
234  template<typename Scalar>
236  {
237  anderson_is_on = to_set;
238  }
239 
240  template<typename Scalar>
242  {
243  Hermes::vector<Solution<Scalar>*> vectorToPass;
244  vectorToPass.push_back(initial_guess);
245  this->solve(vectorToPass);
246  }
247 
248  template<typename Scalar>
249  void PicardSolver<Scalar>::solve(Hermes::vector<Solution<Scalar>*> initial_guess)
250  {
251  int ndof = this->dp->get_num_dofs();
252  Scalar* coeff_vec = new Scalar[ndof];
253  OGProjection<Scalar> ogProjection;
254  ogProjection.project_global(static_cast<DiscreteProblem<Scalar>*>(this->dp)->get_spaces(), initial_guess, coeff_vec);
255  this->solve(coeff_vec);
256  }
257 
258  template<typename Scalar>
259  void PicardSolver<Scalar>::solve(Scalar* coeff_vec)
260  {
261  this->check();
262  this->tick();
263 
264  // Sanity check.
265  if(num_last_vectors_used < 1)
266  throw Hermes::Exceptions::Exception("Picard: Bad number of last iterations to be used (must be at least one).");
267 
268  // Preliminaries.
269  int num_spaces = static_cast<DiscreteProblem<Scalar>*>(this->dp)->get_spaces().size();
270  int ndof = static_cast<DiscreteProblem<Scalar>*>(this->dp)->get_num_dofs();
271  Hermes::vector<const Space<Scalar>* > spaces = static_cast<DiscreteProblem<Scalar>*>(this->dp)->get_spaces();
272  Hermes::vector<bool> add_dir_lift;
273  for(unsigned int i = 0; i < spaces.size(); i++)
274  add_dir_lift.push_back(false);
275 
276  // Delete solution vector if there is any.
277  if(this->sln_vector != NULL)
278  {
279  delete [] this->sln_vector;
280  this->sln_vector = NULL;
281  }
282 
283  this->sln_vector = new Scalar[ndof];
284 
285  bool delete_coeff_vec = false;
286  if(coeff_vec == NULL)
287  {
288  coeff_vec = new Scalar[ndof];
289  memset(coeff_vec, 0, ndof*sizeof(Scalar));
290  delete_coeff_vec = true;
291  }
292 
293  memcpy(this->sln_vector, coeff_vec, ndof*sizeof(Scalar));
294 
295  // Save the coefficient vector, it will be used to calculate increment error
296  // after a new coefficient vector is calculated.
297  Scalar* last_iter_vector = new Scalar[ndof];
298  for (int i = 0; i < ndof; i++)
299  last_iter_vector[i] = this->sln_vector[i];
300 
301  // If Anderson is used, allocate memory for vectors and coefficients.
302  Scalar** previous_vectors = NULL; // To store num_last_vectors_used last coefficient vectors.
303  Scalar* anderson_coeffs = NULL; // To store num_last_vectors_used - 1 Anderson coefficients.
304  if (anderson_is_on)
305  {
306  previous_vectors = new Scalar*[num_last_vectors_used];
307  for (int i = 0; i < num_last_vectors_used; i++) previous_vectors[i] = new Scalar[ndof];
308  anderson_coeffs = new Scalar[num_last_vectors_used-1];
309  }
310 
311  // If Anderson is used, save the initial coefficient vector in the memory.
312  if (anderson_is_on)
313  for (int i = 0; i < ndof; i++) previous_vectors[0][i] = this->sln_vector[i];
314 
315  int it = 1;
316  int vec_in_memory = 1; // There is already one vector in the memory.
317 
318  this->on_initialization();
319 
320  while (true)
321  {
322  this->on_step_begin();
323 
324  (static_cast<DiscreteProblem<Scalar>*>(this->dp))->is_linear = false;
325  this->dp->assemble(last_iter_vector, matrix, rhs);
326  if(this->output_matrixOn && (this->output_matrixIterations == -1 || this->output_matrixIterations >= it))
327  {
328  char* fileName = new char[this->matrixFilename.length() + 5];
329  if(this->matrixFormat == Hermes::Algebra::DF_MATLAB_SPARSE)
330  sprintf(fileName, "%s%i.m", this->matrixFilename.c_str(), it);
331  else
332  sprintf(fileName, "%s%i", this->matrixFilename.c_str(), it);
333  FILE* matrix_file = fopen(fileName, "w+");
334 
335  matrix->dump(matrix_file, this->matrixVarname.c_str(), this->matrixFormat, this->matrix_number_format);
336  fclose(matrix_file);
337  delete [] fileName;
338  }
339  if(this->output_rhsOn && (this->output_rhsIterations == -1 || this->output_rhsIterations >= it))
340  {
341  char* fileName = new char[this->RhsFilename.length() + 5];
342  if(this->RhsFormat == Hermes::Algebra::DF_MATLAB_SPARSE)
343  sprintf(fileName, "%s%i.m", this->RhsFilename.c_str(), it);
344  else
345  sprintf(fileName, "%s%i", this->RhsFilename.c_str(), it);
346  FILE* rhs_file = fopen(fileName, "w+");
347  rhs->dump(rhs_file, this->RhsVarname.c_str(), this->RhsFormat, this->rhs_number_format);
348  fclose(rhs_file);
349  delete [] fileName;
350  }
351 
352  this->on_step_end();
353 
354  //rhs->change_sign();
355 
356  // Solve the linear system.
357  if(!linear_solver->solve())
358  throw Exceptions::LinearMatrixSolverException();
359 
360  memcpy(this->sln_vector, linear_solver->get_sln_vector(), sizeof(Scalar)*ndof);
361 
362  // If Anderson is used, store the new vector in the memory.
363  if (anderson_is_on)
364  {
365  // If memory not full, just add the vector.
366  if (vec_in_memory < num_last_vectors_used)
367  {
368  for (int i = 0; i < ndof; i++) previous_vectors[vec_in_memory][i] = this->sln_vector[i];
369  vec_in_memory++;
370  }
371  else
372  {
373  // If memory full, shift all vectors back, forgetting the oldest one.
374  // Save this->sln_vector[] as the newest one.
375  Scalar* oldest_vec = previous_vectors[0];
376  for (int i = 0; i < num_last_vectors_used-1; i++) previous_vectors[i] = previous_vectors[i + 1];
377  previous_vectors[num_last_vectors_used-1] = oldest_vec;
378  for (int j = 0; j < ndof; j++) previous_vectors[num_last_vectors_used-1][j] = this->sln_vector[j];
379  }
380  }
381 
382  // If there is enough vectors in the memory, calculate Anderson coeffs.
383  if (anderson_is_on && vec_in_memory >= num_last_vectors_used)
384  {
385  // Calculate Anderson coefficients.
386  calculate_anderson_coeffs(previous_vectors, anderson_coeffs, num_last_vectors_used, ndof);
387 
388  // Calculate new vector and store it in this->sln_vector[].
389  for (int i = 0; i < ndof; i++)
390  {
391  this->sln_vector[i] = 0;
392  for (int j = 1; j < num_last_vectors_used; j++)
393  {
394  this->sln_vector[i] += anderson_coeffs[j-1] * previous_vectors[j][i] - (1.0 - anderson_beta) * anderson_coeffs[j-1] * (previous_vectors[j][i] - previous_vectors[j-1][i]);
395  }
396  }
397  }
398 
399  // Calculate relative error between last_iter_vector[] and this->sln_vector[].
400  // FIXME: this is wrong in the complex case (complex conjugation must be used).
401  // FIXME: This will crash if norm of last_iter_vector[] is zero.
402  double last_iter_vec_norm = 0;
403  for (int i = 0; i < ndof; i++)
404  last_iter_vec_norm += std::abs(last_iter_vector[i] * last_iter_vector[i]);
405 
406  last_iter_vec_norm = sqrt(last_iter_vec_norm);
407 
408  double abs_error = 0;
409  for (int i = 0; i < ndof; i++) abs_error += std::abs((this->sln_vector[i] - last_iter_vector[i]) * (this->sln_vector[i] - last_iter_vector[i]));
410  abs_error = sqrt(abs_error);
411 
412  double rel_error = abs_error / last_iter_vec_norm;
413 
414  // Output for the user.
415  if(std::abs(last_iter_vec_norm) < 1e-12)
416  this->info("\tPicard: iteration %d, nDOFs %d, starting from zero vector.", it, ndof);
417  else
418  this->info("\tPicard: iteration %d, nDOFs %d, relative error %g%%", it, ndof, rel_error);
419 
420  // Stopping because error is sufficiently low.
421  if(rel_error < tol)
422  {
423  delete [] last_iter_vector;
424  // If Anderson acceleration was employed, release memory for the Anderson vectors and coeffs.
425  if (anderson_is_on)
426  {
427  for (int i = 0; i < num_last_vectors_used; i++) delete [] previous_vectors[i];
428  delete [] previous_vectors;
429  delete [] anderson_coeffs;
430  }
431 
432  static_cast<DiscreteProblem<Scalar>*>(this->dp)->have_matrix = false;
433 
434  this->tick();
435  this->info("\tPicard: solution duration: %f s.\n", this->last());
436  this->on_finish();
437  return;
438  }
439 
440  // Stopping because maximum number of iterations reached.
441  if(it >= max_iter)
442  {
443  delete [] last_iter_vector;
444  // If Anderson acceleration was employed, release memory for the Anderson vectors and coeffs.
445  if (anderson_is_on)
446  {
447  for (int i = 0; i < num_last_vectors_used; i++) delete [] previous_vectors[i];
448  delete [] previous_vectors;
449  delete [] anderson_coeffs;
450  }
451  static_cast<DiscreteProblem<Scalar>*>(this->dp)->have_matrix = false;
452 
453  this->tick();
454  this->info("\tPicard: solution duration: %f s.\n", this->last());
455 
456  this->on_finish();
457  throw Hermes::Exceptions::Exception("\tPicard: maximum allowed number of Picard iterations exceeded.");
458  return;
459  }
460  this->on_step_end();
461 
462  // Increase counter of iterations.
463  it++;
464 
465  // Renew the last iteration vector.
466  for (int i = 0; i < ndof; i++)
467  last_iter_vector[i] = this->sln_vector[i];
468  }
469  }
470  template class HERMES_API PicardSolver<double>;
471  template class HERMES_API PicardSolver<std::complex<double> >;
472  }
473 }