Hermes2D  2.0
runge_kutta.cpp
1 // This file is part of Hermes2D.
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/>.
15 
16 #include "runge_kutta.h"
17 #include "discrete_problem.h"
18 #include "projections/ogprojection.h"
19 #include "projections/localprojection.h"
20 #include "weakform_library/weakforms_hcurl.h"
21 namespace Hermes
22 {
23  namespace Hermes2D
24  {
25  template<typename Scalar>
26  RungeKutta<Scalar>::RungeKutta(const WeakForm<Scalar>* wf, Hermes::vector<const Space<Scalar> *> spaces, ButcherTable* bt)
27  : wf(wf), bt(bt), num_stages(bt->get_size()), stage_wf_right(bt->get_size() * spaces.size()),
28  stage_wf_left(spaces.size()), start_from_zero_K_vector(false), block_diagonal_jacobian(false), residual_as_vector(true), iteration(0),
29  freeze_jacobian(false), newton_tol(1e-6), newton_max_iter(20), newton_damping_coeff(1.0), newton_max_allowed_residual_norm(1e10)
30  {
31  for(unsigned int i = 0; i < spaces.size(); i++)
32  {
33  this->spaces.push_back(spaces.at(i));
34  this->spaces_seqs.push_back(spaces.at(i)->get_seq());
35  }
36  for(unsigned int i = 0; i < spaces.size(); i++)
37  this->spaces_mutable.push_back(const_cast<Space<Scalar>*>(spaces.at(i)));
38 
39  if(bt==NULL)
40  throw Exceptions::NullException(2);
41 
42  do_global_projections = true;
43 
44  matrix_right = create_matrix<Scalar>();
45  matrix_left = create_matrix<Scalar>();
46  vector_right = create_vector<Scalar>();
47  // Create matrix solver.
48  solver = create_linear_solver(matrix_right, vector_right);
49 
50  // Vector K_vector of length num_stages * ndof. will represent
51  // the 'K_i' vectors in the usual R-K notation.
52  K_vector = new Scalar[num_stages * Space<Scalar>::get_num_dofs(this->spaces)];
53 
54  // Vector u_ext_vec will represent h \sum_{j = 1}^s a_{ij} K_i.
55  u_ext_vec = new Scalar[num_stages * Space<Scalar>::get_num_dofs(this->spaces)];
56 
57  // Vector for the left part of the residual.
58  vector_left = new Scalar[num_stages* Space<Scalar>::get_num_dofs(this->spaces)];
59 
60  this->stage_dp_left = NULL;
61  this->stage_dp_right = NULL;
62  }
63 
64  template<typename Scalar>
65  RungeKutta<Scalar>::RungeKutta(const WeakForm<Scalar>* wf, const Space<Scalar>* space, ButcherTable* bt)
66  : wf(wf), bt(bt), num_stages(bt->get_size()), stage_wf_right(bt->get_size() * 1),
67  stage_wf_left(1), start_from_zero_K_vector(false), block_diagonal_jacobian(false), residual_as_vector(true), iteration(0),
68  freeze_jacobian(false), newton_tol(1e-6), newton_max_iter(20), newton_damping_coeff(1.0), newton_max_allowed_residual_norm(1e10)
69  {
70  this->spaces.push_back(space);
71  this->spaces_seqs.push_back(space->get_seq());
72  this->spaces_mutable.push_back(const_cast<Space<Scalar>*>(space));
73 
74  if(bt==NULL) throw Exceptions::NullException(2);
75 
76  do_global_projections = true;
77 
78  matrix_right = create_matrix<Scalar>();
79  matrix_left = create_matrix<Scalar>();
80  vector_right = create_vector<Scalar>();
81  // Create matrix solver.
82  solver = create_linear_solver(matrix_right, vector_right);
83 
84  // Vector K_vector of length num_stages * ndof. will represent
85  // the 'K_i' vectors in the usual R-K notation.
86  K_vector = new Scalar[num_stages * Space<Scalar>::get_num_dofs(spaces)];
87 
88  // Vector u_ext_vec will represent h \sum_{j = 1}^s a_{ij} K_i.
89  u_ext_vec = new Scalar[num_stages * Space<Scalar>::get_num_dofs(spaces)];
90 
91  // Vector for the left part of the residual.
92  vector_left = new Scalar[num_stages* Space<Scalar>::get_num_dofs(spaces)];
93 
94  this->stage_dp_left = NULL;
95  this->stage_dp_right = NULL;
96  }
97 
98  template<typename Scalar>
99  void RungeKutta<Scalar>::set_spaces(Hermes::vector<const Space<Scalar>*> spaces)
100  {
101  bool delete_K_vector = false;
102  for(unsigned int i = 0; i < spaces.size(); i++)
103  {
104  if(spaces[i]->get_seq() != this->spaces_seqs[i])
105  delete_K_vector = true;
106  }
107 
108  this->spaces = spaces;
109  this->spaces_seqs.clear();
110  for(unsigned int i = 0; i < spaces.size(); i++)
111  this->spaces_seqs.push_back(spaces.at(i)->get_seq());
112  this->spaces_mutable.clear();
113  for(unsigned int i = 0; i < this->spaces.size(); i++)
114  this->spaces_mutable.push_back(const_cast<Space<Scalar>*>(this->spaces.at(i)));
115 
116  if(delete_K_vector)
117  {
118  delete [] K_vector;
119  K_vector = new Scalar[num_stages * Space<Scalar>::get_num_dofs(this->spaces)];
120  this->info("\tRunge-Kutta: K vectors are being set to zero, as the spaces changed during computation.");
121  memset(K_vector, 0, num_stages * Space<Scalar>::get_num_dofs(this->spaces) * sizeof(Scalar));
122  }
123  delete [] u_ext_vec;
124  u_ext_vec = new Scalar[num_stages * Space<Scalar>::get_num_dofs(this->spaces)];
125  delete [] vector_left;
126  vector_left = new Scalar[num_stages* Space<Scalar>::get_num_dofs(this->spaces)];
127 
128  if(this->stage_dp_left != NULL)
129  static_cast<DiscreteProblem<Scalar>*>(this->stage_dp_left)->set_spaces(this->spaces);
130  }
131 
132  template<typename Scalar>
133  void RungeKutta<Scalar>::set_space(const Space<Scalar>* space)
134  {
135  bool delete_K_vector = false;
136  if(space->get_seq() != this->spaces_seqs[0])
137  delete_K_vector = true;
138 
139  this->spaces.clear();
140  this->spaces.push_back(space);
141  this->spaces_seqs.clear();
142  this->spaces_seqs.push_back(space->get_seq());
143  this->spaces_mutable.clear();
144  this->spaces_mutable.push_back(const_cast<Space<Scalar>*>(space));
145 
146  if(delete_K_vector)
147  {
148  delete [] K_vector;
149  K_vector = new Scalar[num_stages * Space<Scalar>::get_num_dofs(this->spaces)];
150  this->info("\tRunge-Kutta: K vector is being set to zero, as the spaces changed during computation.");
151  memset(K_vector, 0, num_stages * Space<Scalar>::get_num_dofs(this->spaces) * sizeof(Scalar));
152  }
153  delete [] u_ext_vec;
154  u_ext_vec = new Scalar[num_stages * Space<Scalar>::get_num_dofs(this->spaces)];
155  delete [] vector_left;
156  vector_left = new Scalar[num_stages* Space<Scalar>::get_num_dofs(this->spaces)];
157 
158  if(this->stage_dp_left != NULL)
159  static_cast<DiscreteProblem<Scalar>*>(this->stage_dp_left)->set_space(space);
160  }
161 
162  template<typename Scalar>
163  Hermes::vector<const Space<Scalar>*> RungeKutta<Scalar>::get_spaces() const
164  {
165  return this->spaces;
166  }
167 
168  template<typename Scalar>
169  void RungeKutta<Scalar>::init()
170  {
171  this->create_stage_wf(spaces.size(), block_diagonal_jacobian);
172 
173  if(this->get_verbose_output())
174  {
175  this->stage_wf_left.set_verbose_output(true);
176  this->stage_wf_right.set_verbose_output(true);
177  }
178  else
179  {
180  this->stage_wf_left.set_verbose_output(false);
181  this->stage_wf_right.set_verbose_output(false);
182  }
183 
184  // The tensor discrete problem is created in two parts. First, matrix_left is the Jacobian
185  // matrix of the term coming from the left-hand side of the RK formula k_i = f(...). This is
186  // a block-diagonal mass matrix. The corresponding part of the residual is obtained by multiplying
187  // this block mass matrix with the tensor vector K. Next, matrix_right and vector_right are the Jacobian
188  // matrix and residula vector coming from the function f(...). Of course the RK equation is assumed
189  // in a form suitable for the Newton's method: k_i - f(...) = 0. At the end, matrix_left and vector_left
190  // are added to matrix_right and vector_right, respectively.
191  this->stage_dp_left = new DiscreteProblem<Scalar>(&stage_wf_left, spaces);
192 
193  // All Spaces of the problem.
194  Hermes::vector<const Space<Scalar>*> stage_spaces_vector;
195 
196  // Create spaces for stage solutions K_i. This is necessary
197  // to define a num_stages x num_stages block weak formulation.
198  for (unsigned int i = 0; i < num_stages; i++)
199  for(unsigned int space_i = 0; space_i < spaces.size(); space_i++)
200  stage_spaces_vector.push_back(spaces[space_i]);
201 
202  this->stage_dp_right = new DiscreteProblem<Scalar>(&stage_wf_right, stage_spaces_vector);
203 
204  stage_dp_right->set_RK(spaces.size());
205 
206  // Prepare residuals of stage solutions.
207  if(!residual_as_vector)
208  for (unsigned int i = 0; i < num_stages; i++)
209  for(unsigned int sln_i = 0; sln_i < spaces.size(); sln_i++)
210  residuals_vector.push_back(new Solution<Scalar>(spaces[sln_i]->get_mesh()));
211  }
212 
213  template<typename Scalar>
214  void RungeKutta<Scalar>::set_start_from_zero_K_vector()
215  {
216  this->start_from_zero_K_vector = true;
217  }
218 
219  template<typename Scalar>
220  void RungeKutta<Scalar>::set_residual_as_solutions()
221  {
222  this->residual_as_vector = false;
223  }
224 
225  template<typename Scalar>
226  void RungeKutta<Scalar>::set_block_diagonal_jacobian()
227  {
228  this->block_diagonal_jacobian = true;
229  }
230 
231  template<typename Scalar>
232  void RungeKutta<Scalar>::set_freeze_jacobian()
233  {
234  this->freeze_jacobian = true;
235  }
236  template<typename Scalar>
237  void RungeKutta<Scalar>::set_newton_tol(double newton_tol)
238  {
239  this->newton_tol = newton_tol;
240  }
241  template<typename Scalar>
242  void RungeKutta<Scalar>::set_newton_max_iter(int newton_max_iter)
243  {
244  this->newton_max_iter = newton_max_iter;
245  }
246  template<typename Scalar>
247  void RungeKutta<Scalar>::set_newton_damping_coeff(double newton_damping_coeff)
248  {
249  this->newton_damping_coeff = newton_damping_coeff;
250  }
251  template<typename Scalar>
252  void RungeKutta<Scalar>::set_newton_max_allowed_residual_norm(double newton_max_allowed_residual_norm)
253  {
254  this->newton_max_allowed_residual_norm = newton_max_allowed_residual_norm;
255  }
256 
257  template<typename Scalar>
258  RungeKutta<Scalar>::~RungeKutta()
259  {
260  if(stage_dp_left != NULL)
261  delete stage_dp_left;
262  if(stage_dp_right != NULL)
263  delete stage_dp_right;
264  delete solver;
265  delete matrix_right;
266  delete matrix_left;
267  delete vector_right;
268  delete [] K_vector;
269  delete [] u_ext_vec;
270  delete [] vector_left;
271  }
272 
273  template<typename Scalar>
274  void RungeKutta<Scalar>::use_local_projections()
275  {
276  do_global_projections = false;
277  }
278 
279  template<typename Scalar>
280  void RungeKutta<Scalar>::multiply_as_diagonal_block_matrix(SparseMatrix<Scalar>* matrix, int num_blocks,
281  Scalar* source_vec, Scalar* target_vec)
282  {
283  int size = matrix->get_size();
284  for (int i = 0; i < num_blocks; i++)
285  {
286  matrix->multiply_with_vector(source_vec + i*size, target_vec + i*size);
287  }
288  }
289 
290  template<typename Scalar>
291  void RungeKutta<Scalar>::rk_time_step_newton(Solution<Scalar>* sln_time_prev,
292  Solution<Scalar>* sln_time_new, Solution<Scalar>* error_fn)
293  {
294  Hermes::vector<Solution<Scalar>*> slns_time_prev = Hermes::vector<Solution<Scalar>*>();
295  slns_time_prev.push_back(sln_time_prev);
296  Hermes::vector<Solution<Scalar>*> slns_time_new = Hermes::vector<Solution<Scalar>*>();
297  slns_time_new.push_back(sln_time_new);
298  Hermes::vector<Solution<Scalar>*> error_fns = Hermes::vector<Solution<Scalar>*>();
299  error_fns.push_back(error_fn);
300  return rk_time_step_newton(slns_time_prev, slns_time_new,
301  error_fns);
302  }
303 
304  template<typename Scalar>
305  void RungeKutta<Scalar>::rk_time_step_newton(Hermes::vector<Solution<Scalar>*> slns_time_prev,
306  Hermes::vector<Solution<Scalar>*> slns_time_new,
307  Hermes::vector<Solution<Scalar>*> error_fns)
308  {
309  this->tick();
310 
311  int ndof = Space<Scalar>::get_num_dofs(spaces);
312 
313  if(this->stage_dp_left == NULL)
314  this->init();
315 
316  // Creates the stage weak formulation.
317  update_stage_wf(slns_time_prev);
318 
319  // Check whether the user provided a nonzero B2-row if he wants temporal error estimation.
320  if(error_fns != Hermes::vector<Solution<Scalar>*>() && bt->is_embedded() == false)
321  throw Hermes::Exceptions::Exception("rk_time_step_newton(): R-K method must be embedded if temporal error estimate is requested.");
322 
323  info("\tRunge-Kutta: time step, time: %f, time step: %f", this->time, this->time_step);
324 
325  // Set the correct time to the essential boundary conditions.
326  for (unsigned int stage_i = 0; stage_i < num_stages; stage_i++)
327  Space<Scalar>::update_essential_bc_values(spaces_mutable, this->time + bt->get_C(stage_i)*this->time_step);
328 
329  // All Spaces of the problem.
330  Hermes::vector<const Space<Scalar>*> stage_spaces_vector;
331 
332  // Create spaces for stage solutions K_i. This is necessary
333  // to define a num_stages x num_stages block weak formulation.
334  for (unsigned int i = 0; i < num_stages; i++)
335  for(unsigned int space_i = 0; space_i < spaces.size(); space_i++)
336  {
337  typename Space<Scalar>::ReferenceSpaceCreator ref_space_creator(spaces[space_i], spaces[space_i]->get_mesh(), 0);
338  stage_spaces_vector.push_back(ref_space_creator.create_ref_space());
339  }
340 
341  this->stage_dp_right->set_spaces(stage_spaces_vector);
342 
343  // Zero utility vectors.
344  if(start_from_zero_K_vector || !iteration)
345  memset(K_vector, 0, num_stages * ndof * sizeof(Scalar));
346  memset(u_ext_vec, 0, num_stages * ndof * sizeof(Scalar));
347  memset(vector_left, 0, num_stages * ndof * sizeof(Scalar));
348 
349  // Assemble the block-diagonal mass matrix M of size ndof times ndof.
350  // The corresponding part of the global residual vector is obtained
351  // just by multiplication with the stage vector K.
352  // FIXME: This should not be repeated if spaces have not changed.
353  stage_dp_left->assemble(matrix_left, NULL);
354 
355  // The Newton's loop.
356  double residual_norm;
357  int it = 1;
358  while (true)
359  {
360  // Prepare vector h\sum_{j = 1}^s a_{ij} K_j.
361  prepare_u_ext_vec();
362 
363  // Reinitialize filters.
364  if(this->filters_to_reinit.size() > 0)
365  {
366  Solution<Scalar>::vector_to_solutions(u_ext_vec, spaces, slns_time_new);
367 
368  for(unsigned int filters_i = 0; filters_i < this->filters_to_reinit.size(); filters_i++)
369  filters_to_reinit.at(filters_i)->reinit();
370  }
371 
372  // Residual corresponding to the stage derivatives k_i in the equation k_i - f(...) = 0.
373  multiply_as_diagonal_block_matrix(matrix_left, num_stages, K_vector, vector_left);
374 
375  // Assemble the block Jacobian matrix of the stationary residual F.
376  // Diagonal blocks are created even if empty, so that matrix_left can be added later.
377  bool force_diagonal_blocks = true;
378  stage_dp_right->assemble(u_ext_vec, NULL, vector_right, force_diagonal_blocks);
379 
380  // Finalizing the residual vector.
381  vector_right->add_vector(vector_left);
382 
383  // Multiply the residual vector with -1 since the matrix
384  // equation reads J(Y^n) \deltaY^{n + 1} = -F(Y^n).
385  vector_right->change_sign();
386  if(this->output_rhsOn && (this->output_rhsIterations == -1 || this->output_rhsIterations >= it))
387  {
388  char* fileName = new char[this->RhsFilename.length() + 5];
389  if(this->RhsFormat == Hermes::Algebra::DF_MATLAB_SPARSE)
390  sprintf(fileName, "%s%i.m", this->RhsFilename.c_str(), it);
391  else
392  sprintf(fileName, "%s%i", this->RhsFilename.c_str(), it);
393  FILE* rhs_file = fopen(fileName, "w+");
394  vector_right->dump(rhs_file, this->RhsVarname.c_str(), this->RhsFormat, this->rhs_number_format);
395  fclose(rhs_file);
396  }
397 
398  // Measure the residual norm.
399  if(residual_as_vector)
400  // Calculate the l2-norm of residual vector.
401  residual_norm = Global<Scalar>::get_l2_norm(vector_right);
402  else
403  {
404  // Translate residual vector into residual functions.
405  Hermes::vector<bool> add_dir_lift_vector;
406  add_dir_lift_vector.reserve(1);
407  add_dir_lift_vector.push_back(false);
408  Solution<Scalar>::vector_to_solutions(vector_right, stage_dp_right->get_spaces(),
409  residuals_vector, false);
410  residual_norm = Global<Scalar>::calc_norms(residuals_vector);
411  }
412 
413  // Info for the user.
414  if(it == 1)
415  this->info("\tRunge-Kutta: Newton initial residual norm: %g", residual_norm);
416  else
417  this->info("\tRunge-Kutta: Newton iteration %d, residual norm: %g", it-1, residual_norm);
418 
419  // If maximum allowed residual norm is exceeded, fail.
420  if(residual_norm > newton_max_allowed_residual_norm)
421  {
422  throw Exceptions::ValueException("residual norm", residual_norm, newton_max_allowed_residual_norm);
423  }
424 
425  // If residual norm is within tolerance, or the maximum number
426  // of iteration has been reached, or the problem is linear, then quit.
427  if((residual_norm < newton_tol || it > newton_max_iter) && it > 1)
428  break;
429 
430  bool rhs_only = (freeze_jacobian && it > 1);
431  if(!rhs_only)
432  {
433  // Assemble the block Jacobian matrix of the stationary residual F
434  // Diagonal blocks are created even if empty, so that matrix_left
435  // can be added later.
436  stage_dp_right->assemble(u_ext_vec, matrix_right, NULL, force_diagonal_blocks);
437 
438  // Adding the block mass matrix M to matrix_right. This completes the
439  // resulting tensor Jacobian.
440  matrix_right->add_sparse_to_diagonal_blocks(num_stages, matrix_left);
441 
442  if(this->output_matrixOn && (this->output_matrixIterations == -1 || this->output_matrixIterations >= it))
443  {
444  char* fileName = new char[this->matrixFilename.length() + 5];
445  if(this->matrixFormat == Hermes::Algebra::DF_MATLAB_SPARSE)
446  sprintf(fileName, "%s%i.m", this->matrixFilename.c_str(), it);
447  else
448  sprintf(fileName, "%s%i", this->matrixFilename.c_str(), it);
449  FILE* matrix_file = fopen(fileName, "w+");
450 
451  matrix_right->dump(matrix_file, this->matrixVarname.c_str(), this->matrixFormat, this->matrix_number_format);
452  fclose(matrix_file);
453  }
454 
455  matrix_right->finish();
456  }
457  else
458  solver->set_factorization_scheme(HERMES_REUSE_FACTORIZATION_COMPLETELY);
459 
460  // Solve the linear system.
461  if(!solver->solve())
462  throw Exceptions::LinearMatrixSolverException();
463 
464  // Add \deltaK^{n + 1} to K^n.
465  for (unsigned int i = 0; i < num_stages*ndof; i++)
466  K_vector[i] += newton_damping_coeff * solver->get_sln_vector()[i];
467 
468  // Increase iteration counter.
469  it++;
470  }
471 
472  // If max number of iterations was exceeded, fail.
473  if(it >= newton_max_iter)
474  {
475  this->tick();
476  this->info("\tRunge-Kutta: time step duration: %f s.\n", this->last());
477  throw Exceptions::ValueException("Newton iterations", it, newton_max_iter);
478  }
479 
480  // Project previous time level solution on the stage space,
481  // to be able to add them together. The result of the projection
482  // will be stored in the vector coeff_vec.
483  // FIXME - this projection is not needed when the
484  // spaces are the same (if spatial adaptivity is not used).
485  Scalar* coeff_vec = new Scalar[ndof];
486  if(do_global_projections)
487  {
488  OGProjection<Scalar> ogProjection;
489  ogProjection.project_global(spaces, slns_time_prev, coeff_vec);
490  }
491  else
492  {
493  LocalProjection<Scalar> ogProjection;
494  ogProjection.project_local(spaces, slns_time_prev, coeff_vec);
495  }
496 
497  if(do_global_projections)
498  {
499  OGProjection<Scalar> ogProjection;
500  ogProjection.project_global(spaces, slns_time_prev, coeff_vec);
501  }
502  else
503  {
504  LocalProjection<Scalar> ogProjection;
505  ogProjection.project_local(spaces, slns_time_prev, coeff_vec);
506  }
507 
508  // Calculate new time level solution in the stage space (u_{n + 1} = u_n + h \sum_{j = 1}^s b_j k_j).
509  for (int i = 0; i < ndof; i++)
510  for (unsigned int j = 0; j < num_stages; j++)
511  coeff_vec[i] += this->time_step * bt->get_B(j) * K_vector[j * ndof + i];
512 
513  Solution<Scalar>::vector_to_solutions(coeff_vec, spaces, slns_time_new);
514 
515  // If error_fn is not NULL, use the B2-row in the Butcher's
516  // table to calculate the temporal error estimate.
517  if(error_fns != Hermes::vector<Solution<Scalar>*>())
518  {
519  for (int i = 0; i < ndof; i++)
520  {
521  coeff_vec[i] = 0.;
522  for (unsigned int j = 0; j < num_stages; j++)
523  coeff_vec[i] += (bt->get_B(j) - bt->get_B2(j)) * K_vector[j * ndof + i];
524  coeff_vec[i] *= this->time_step;
525  }
526  Solution<Scalar>::vector_to_solutions_common_dir_lift(coeff_vec, spaces, error_fns);
527  }
528 
529  // Delete stage spaces.
530  for (unsigned int i = 0; i < num_stages * spaces.size(); i++)
531  delete stage_spaces_vector[i];
532 
533  // Delete all residuals.
534  if(!residual_as_vector)
535  for (unsigned int i = 0; i < num_stages; i++)
536  delete residuals_vector[i];
537 
538  // Clean up.
539  delete [] coeff_vec;
540 
541  iteration++;
542  this->tick();
543  this->info("\tRunge-Kutta: time step duration: %f s.\n", this->last());
544  }
545 
546  template<typename Scalar>
547  void RungeKutta<Scalar>::rk_time_step_newton(Hermes::vector<Solution<Scalar>*> slns_time_prev,
548  Hermes::vector<Solution<Scalar>*> slns_time_new)
549  {
550  return rk_time_step_newton(slns_time_prev, slns_time_new,
551  Hermes::vector<Solution<Scalar>*>());
552  }
553 
554  template<typename Scalar>
555  void RungeKutta<Scalar>::rk_time_step_newton(Solution<Scalar>* sln_time_prev, Solution<Scalar>* sln_time_new)
556  {
557  Hermes::vector<Solution<Scalar>*> slns_time_prev = Hermes::vector<Solution<Scalar>*>();
558  slns_time_prev.push_back(sln_time_prev);
559  Hermes::vector<Solution<Scalar>*> slns_time_new = Hermes::vector<Solution<Scalar>*>();
560  slns_time_new.push_back(sln_time_new);
561  Hermes::vector<Solution<Scalar>*> error_fns = Hermes::vector<Solution<Scalar>*>();
562  return rk_time_step_newton(slns_time_prev, slns_time_new,
563  error_fns);
564  }
565 
566  template<typename Scalar>
567  void RungeKutta<Scalar>::set_filters_to_reinit(Hermes::vector<Filter<Scalar>*> filters_to_reinit)
568  {
569  for(int i = 0; i < filters_to_reinit.size(); i++)
570  this->filters_to_reinit.push_back(filters_to_reinit.at(i));
571  }
572 
573  template<typename Scalar>
574  void RungeKutta<Scalar>::create_stage_wf(unsigned int size, bool block_diagonal_jacobian)
575  {
576  // Clear the WeakForms.
577  stage_wf_left.delete_all();
578  stage_wf_right.delete_all();
579 
580  // First let's do the mass matrix (only one block ndof times ndof).
581  for(unsigned int component_i = 0; component_i < size; component_i++)
582  {
583  if(spaces[component_i]->get_type() == HERMES_H1_SPACE
584  || spaces[component_i]->get_type() == HERMES_L2_SPACE)
585  {
586  MatrixFormVolL2<Scalar>* proj_form = new MatrixFormVolL2<Scalar>(component_i, component_i);
587  proj_form->areas.push_back(HERMES_ANY);
588  proj_form->scaling_factor = 1.0;
589  proj_form->u_ext_offset = 0;
590  stage_wf_left.add_matrix_form(proj_form);
591  }
592  if(spaces[component_i]->get_type() == HERMES_HDIV_SPACE
593  || spaces[component_i]->get_type() == HERMES_HCURL_SPACE)
594  {
595  MatrixFormVolHCurl<Scalar>* proj_form = new MatrixFormVolHCurl<Scalar>(component_i, component_i);
596  proj_form->areas.push_back(HERMES_ANY);
597  proj_form->scaling_factor = 1.0;
598  proj_form->u_ext_offset = 0;
599  stage_wf_left.add_matrix_form(proj_form);
600  }
601  }
602 
603  // In the rest we will take the stationary jacobian and residual forms
604  // (right-hand side) and use them to create a block Jacobian matrix of
605  // size (num_stages*ndof times num_stages*ndof) and a block residual
606  // vector of length num_stages*ndof.
607 
608  // Extracting volume and surface matrix and vector forms from the
609  // original weak formulation.
610  Hermes::vector<MatrixFormVol<Scalar> *> mfvol_base = wf->mfvol;
611  Hermes::vector<MatrixFormSurf<Scalar> *> mfsurf_base = wf->mfsurf;
612  Hermes::vector<VectorFormVol<Scalar> *> vfvol_base = wf->vfvol;
613  Hermes::vector<VectorFormSurf<Scalar> *> vfsurf_base = wf->vfsurf;
614 
615  // Duplicate matrix volume forms, scale them according
616  // to the Butcher's table, enhance them with additional
617  // external solutions, and anter them as blocks to the
618  // new stage Jacobian. If block_diagonal_jacobian = true
619  // then only diagonal blocks are considered.
620  for (unsigned int m = 0; m < mfvol_base.size(); m++)
621  {
622  for (unsigned int i = 0; i < num_stages; i++)
623  {
624  for (unsigned int j = 0; j < num_stages; j++)
625  {
626  if(block_diagonal_jacobian && i != j) continue;
627 
628  MatrixFormVol<Scalar>* mfv_ij = mfvol_base[m]->clone();
629 
630  mfv_ij->i = mfv_ij->i + i * spaces.size();
631  mfv_ij->j = mfv_ij->j + j * spaces.size();
632 
633  mfv_ij->u_ext_offset = i * spaces.size();
634 
635  // Add the matrix form to the corresponding block of the
636  // stage Jacobian matrix.
637  stage_wf_right.add_matrix_form(mfv_ij);
638  }
639  }
640  }
641 
642  // Duplicate matrix surface forms, enhance them with
643  // additional external solutions, and anter them as
644  // blocks of the stage Jacobian.
645  for (unsigned int m = 0; m < mfsurf_base.size(); m++)
646  {
647  for (unsigned int i = 0; i < num_stages; i++)
648  {
649  for (unsigned int j = 0; j < num_stages; j++)
650  {
651  if(block_diagonal_jacobian && i != j) continue;
652 
653  MatrixFormSurf<Scalar>* mfs_ij = mfsurf_base[m]->clone();
654 
655  mfs_ij->i = mfs_ij->i + i * spaces.size();
656  mfs_ij->j = mfs_ij->j + j * spaces.size();
657 
658  mfs_ij->u_ext_offset = i * spaces.size();
659 
660  // Add the matrix form to the corresponding block of the
661  // stage Jacobian matrix.
662  stage_wf_right.add_matrix_form_surf(mfs_ij);
663  }
664  }
665  }
666 
667  // Duplicate vector volume forms, enhance them with
668  // additional external solutions, and anter them as
669  // blocks of the stage residual.
670  for (unsigned int m = 0; m < vfvol_base.size(); m++)
671  {
672  for (unsigned int i = 0; i < num_stages; i++)
673  {
674  VectorFormVol<Scalar>* vfv_i = vfvol_base[m]->clone();
675 
676  vfv_i->i = vfv_i->i + i * spaces.size();
677 
678  vfv_i->scaling_factor = -1.0;
679  vfv_i->u_ext_offset = i * spaces.size();
680 
681  // Add the matrix form to the corresponding block of the
682  // stage Jacobian matrix.
683  stage_wf_right.add_vector_form(vfv_i);
684  }
685  }
686 
687  // Duplicate vector surface forms, enhance them with
688  // additional external solutions, and anter them as
689  // blocks of the stage residual.
690  for (unsigned int m = 0; m < vfsurf_base.size(); m++)
691  {
692  for (unsigned int i = 0; i < num_stages; i++)
693  {
694  VectorFormSurf<Scalar>* vfs_i = vfsurf_base[m]->clone();
695 
696  vfs_i->i = vfs_i->i + i * spaces.size();
697 
698  vfs_i->scaling_factor = -1.0;
699  vfs_i->u_ext_offset = i * spaces.size();
700 
701  // Add the matrix form to the corresponding block of the
702  // stage Jacobian matrix.
703  stage_wf_right.add_vector_form_surf(vfs_i);
704  }
705  }
706  }
707 
708  template<typename Scalar>
709  void RungeKutta<Scalar>::update_stage_wf(Hermes::vector<Solution<Scalar>*> slns_time_prev)
710  {
711  if(this->wf->global_integration_order_set)
712  {
713  this->stage_wf_left.set_global_integration_order(this->wf->global_integration_order);
714  this->stage_wf_right.set_global_integration_order(this->wf->global_integration_order);
715  }
716 
717  // Extracting volume and surface matrix and vector forms from the
718  // 'right' weak formulation.
719  Hermes::vector<MatrixFormVol<Scalar> *> mfvol = stage_wf_right.mfvol;
720  Hermes::vector<MatrixFormSurf<Scalar> *> mfsurf = stage_wf_right.mfsurf;
721  Hermes::vector<VectorFormVol<Scalar> *> vfvol = stage_wf_right.vfvol;
722  Hermes::vector<VectorFormSurf<Scalar> *> vfsurf = stage_wf_right.vfsurf;
723 
724  for(unsigned int slns_time_prev_i = 0; slns_time_prev_i < slns_time_prev.size(); slns_time_prev_i++)
725  stage_wf_right.ext.push_back(slns_time_prev[slns_time_prev_i]);
726 
727  // Duplicate matrix volume forms, scale them according
728  // to the Butcher's table, enhance them with additional
729  // external solutions, and anter them as blocks to the
730  // new stage Jacobian. If block_diagonal_jacobian = true
731  // then only diagonal blocks are considered.
732  for (unsigned int m = 0; m < mfvol.size(); m++)
733  {
734  MatrixFormVol<Scalar> *mfv_ij = mfvol[m];
735  mfv_ij->scaling_factor = -this->time_step * bt->get_A(mfv_ij->i / spaces.size(), mfv_ij->j / spaces.size());
736  mfv_ij->set_current_stage_time(this->time + bt->get_C(mfv_ij->i / spaces.size()) * this->time_step);
737  }
738 
739  // Duplicate matrix surface forms, enhance them with
740  // additional external solutions, and anter them as
741  // blocks of the stage Jacobian.
742  for (unsigned int m = 0; m < mfsurf.size(); m++)
743  {
744  MatrixFormSurf<Scalar> *mfs_ij = mfsurf[m];
745  mfs_ij->scaling_factor = -this->time_step * bt->get_A(mfs_ij->i / spaces.size(), mfs_ij->j / spaces.size());
746  mfs_ij->set_current_stage_time(this->time + bt->get_C(mfs_ij->i / spaces.size()) * this->time_step);
747  }
748 
749  // Duplicate vector volume forms, enhance them with
750  // additional external solutions, and anter them as
751  // blocks of the stage residual.
752  for (unsigned int m = 0; m < vfvol.size(); m++)
753  {
754  VectorFormVol<Scalar>* vfv_i = vfvol[m];
755  vfv_i->set_current_stage_time(this->time + bt->get_C(vfv_i->i / spaces.size())*this->time_step);
756  }
757 
758  // Duplicate vector surface forms, enhance them with
759  // additional external solutions, and anter them as
760  // blocks of the stage residual.
761  for (unsigned int m = 0; m < vfsurf.size(); m++)
762  {
763  VectorFormSurf<Scalar>* vfs_i = vfsurf[m];
764  vfs_i->set_current_stage_time(this->time + bt->get_C(vfs_i->i / spaces.size())*this->time_step);
765  }
766  }
767 
768  template<typename Scalar>
769  void RungeKutta<Scalar>::prepare_u_ext_vec()
770  {
771  unsigned int ndof = Space<Scalar>::get_num_dofs(spaces);
772  for (unsigned int stage_i = 0; stage_i < num_stages; stage_i++)
773  {
774  unsigned int running_space_ndofs = 0;
775  for(unsigned int space_i = 0; space_i < spaces.size(); space_i++)
776  {
777  for (int idx = 0; idx < spaces[space_i]->get_num_dofs(); idx++)
778  {
779  Scalar increment = 0;
780  for (unsigned int stage_j = 0; stage_j < num_stages; stage_j++)
781  increment += bt->get_A(stage_i, stage_j) * K_vector[stage_j * ndof + running_space_ndofs + idx];
782  u_ext_vec[stage_i * ndof + running_space_ndofs + idx] = this->time_step * increment;
783  }
784  running_space_ndofs += spaces[space_i]->get_num_dofs();
785  }
786  }
787  }
788  template class HERMES_API RungeKutta<double>;
789  template class HERMES_API RungeKutta<std::complex<double> >;
790  }
791 }