Hermes2D  2.0
runge_kutta.h
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 #ifndef __H2D_RUNGE_KUTTA_H
17 #define __H2D_RUNGE_KUTTA_H
18 
19 #include "global.h"
20 #include "weakform/weakform.h"
21 #include "function/filter.h"
22 #include "exceptions.h"
23 #include "mixins2d.h"
24 namespace Hermes
25 {
26  namespace Hermes2D
27  {
28  // TODO LIST:
29  //
30  // (1) With explicit and diagonally implicit methods, the matrix is treated
31  // in the same way as with fully implicit ones. To make this more
32  // efficient, with explicit and diagonally implicit methods one should
33  // first only solve for the upper left block, then eliminate all blocks
34  // under it, then solve for block at position 22, eliminate all blocks
35  // under it, etc. Currently this is not done and everything is left to
36  // the matrix solver.
37  //
38  // (2) In example 03-timedep-adapt-space-and-time with implicit Euler
39  // method, Newton's method takes much longer than in 01-timedep-adapt-space-only
40  // (that also uses implicit Euler method). This means that the initial guess for
41  // the K_vector should be improved (currently it is zero).
42  //
43  // (3) At the end of rk_time_step_newton(), the previous time level solution is
44  // projected onto the space of the new time-level solution so that
45  // it can be added to the stages. This projection is slow so we should
46  // find a way to do this differently. In any case, the projection
47  // is not necessary when no adaptivity in space takes place and the
48  // two spaces are the same (but it is done anyway).
49  //
50  // (4) We do not take advantage of the fact that all blocks in the
51  // Jacobian matrix have the same structure. Thus it is enough to
52  // assemble the matrix M (one block) and copy the sparsity structure
53  // into all remaining nonzero blocks (and diagonal blocks). Right
54  // now, the sparsity structure is created expensively in each block
55  // again.
56  //
57  // (5) If space does not change, the sparsity does not change. Right now
58  // we discard everything at the end of every time step, we should not
59  // do it.
60  //
61  // (6) If the problem does not depend explicitly on time, then all the blocks
62  // in the Jacobian matrix of the stationary residual are the same up
63  // to a multiplicative constant. Thus they do not have to be aassembled
64  // from scratch.
65  //
66  //
67  // (7) In practice, Butcher's tables are being transformed to the
68  // Jordan canonical form (I think) for better performance. This
69  // can be found, I think, in newer Butcher's papers or presentation
70  // (he has them online), and possibly in his book.
73  template<typename Scalar>
74  class HERMES_API RungeKutta : public Hermes::Mixins::Loggable, public Hermes::Mixins::TimeMeasurable, public Hermes::Mixins::IntegrableWithGlobalOrder, public Hermes::Mixins::SettableComputationTime, public Hermes::Hermes2D::Mixins::SettableSpaces<Scalar>, public Hermes::Hermes2D::Mixins::MatrixRhsOutput<Scalar>
75  {
76  public:
80  RungeKutta(const WeakForm<Scalar>* wf, Hermes::vector<const Space<Scalar> *> spaces, ButcherTable* bt);
81 
83  RungeKutta(const WeakForm<Scalar>* wf, const Space<Scalar>* space, ButcherTable* bt);
84 
86  void use_local_projections();
87 
88  void set_start_from_zero_K_vector();
89  void set_residual_as_solutions();
90  void set_block_diagonal_jacobian();
91 
93  ~RungeKutta();
94 
95  // Perform one explicit or implicit time step using the Runge-Kutta method
96  // corresponding to a given Butcher's table. If err_vec != NULL then it will be
97  // filled with an error vector calculated using the second B-row of the Butcher's
98  // table (the second B-row B2 must be nonzero in that case). The negative default
99  // values for newton_tol and newton_max_iter are for linear problems.
100  // Many improvements are needed, a todo list is presented at the beginning of
101  // the corresponding .cpp file.
102  // freeze_jacobian... if true then the Jacobian is not recalculated in each
103  // iteration of the Newton's method.
104  // block_diagonal_jacobian... if true then the tensor product block Jacobian is
105  // reduced to just the diagonal blocks.
106  void rk_time_step_newton(Hermes::vector<Solution<Scalar>*> slns_time_prev, Hermes::vector<Solution<Scalar>*> slns_time_new, Hermes::vector<Solution<Scalar>*> error_fns);
107  void rk_time_step_newton(Solution<Scalar>* slns_time_prev, Solution<Scalar>* slns_time_new, Solution<Scalar>* error_fn);
108 
109  // This is a wrapper for the previous function if error_fn is not provided
110  // (adaptive time stepping is not wanted).
111  void rk_time_step_newton(Hermes::vector<Solution<Scalar>*> slns_time_prev, Hermes::vector<Solution<Scalar>*> slns_time_new);
112  void rk_time_step_newton(Solution<Scalar>* sln_time_prev, Solution<Scalar>* sln_time_new);
113 
114  void set_freeze_jacobian();
115  void set_newton_tol(double newton_tol);
116  void set_newton_max_iter(int newton_max_iter);
117  void set_newton_damping_coeff(double newton_damping_coeff);
118  void set_newton_max_allowed_residual_norm(double newton_max_allowed_residual_norm);
119 
120  virtual void set_spaces(Hermes::vector<const Space<Scalar>*> spaces);
121  virtual void set_space(const Space<Scalar>* space);
122  virtual Hermes::vector<const Space<Scalar>*> get_spaces() const;
123 
135  void set_filters_to_reinit(Hermes::vector<Filter<Scalar>*> filters_to_reinit);
136 
137  protected:
139  void init();
140 
147  void multiply_as_diagonal_block_matrix(SparseMatrix<Scalar>* matrix_left, int num_stages,
148  Scalar* stage_coeff_vec, Scalar* vector_left);
149 
155  void create_stage_wf(unsigned int size, bool block_diagonal_jacobian);
156 
158  void update_stage_wf(Hermes::vector<Solution<Scalar>*> slns_time_prev);
159 
160  // Prepare u_ext_vec.
161  void prepare_u_ext_vec();
162 
164  SparseMatrix<Scalar>* matrix_left;
165 
167  SparseMatrix<Scalar>* matrix_right;
168  Vector<Scalar>* vector_right;
169 
171  LinearMatrixSolver<Scalar>* solver;
172 
174  const WeakForm<Scalar>* wf;
175 
177  Hermes::vector<const Space<Scalar>*> spaces;
178  Hermes::vector<Space<Scalar>*> spaces_mutable;
179  Hermes::vector<unsigned int> spaces_seqs;
180 
182  ButcherTable* bt;
183 
185  unsigned int num_stages;
186 
188  // For the main part equation (written on the right),
190  WeakForm<Scalar> stage_wf_right;
191  DiscreteProblem<Scalar>* stage_dp_right;
192 
194  WeakForm<Scalar> stage_wf_left;
195  DiscreteProblem<Scalar>* stage_dp_left;
196 
197  bool start_from_zero_K_vector;
198  bool block_diagonal_jacobian;
199  bool residual_as_vector;
200 
202  unsigned int iteration;
203 
204  bool do_global_projections;
205  bool freeze_jacobian;
206  double newton_tol;
207  int newton_max_iter;
208  double newton_damping_coeff;
209  double newton_max_allowed_residual_norm;
210 
211  Hermes::vector<Solution<Scalar>*> residuals_vector;
212 
215  Scalar* K_vector;
216 
218  Scalar* u_ext_vec;
219 
221  Scalar* vector_left;
222 
224  Hermes::vector<Filter<Scalar>*> filters_to_reinit;
225  };
226  }
227 }
228 #endif