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