deal2lkit: A ToolKit library for Deal.II
kinsol_interface.cc
Go to the documentation of this file.
1 //-----------------------------------------------------------
2 //
3 // Copyright (C) 2016 by the deal2lkit authors
4 //
5 // This file is part of the deal2lkit library.
6 //
7 // The deal2lkit library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE at
12 // the top level of the deal2lkit distribution.
13 //
14 //-----------------------------------------------------------
15 
17 #ifdef D2K_WITH_SUNDIALS
18 #include <deal.II/lac/vector.h>
21 
22 #ifdef DEAL_II_WITH_PETSC
24 #ifdef DEAL_II_WITH_MPI
26 #endif
27 #endif
28 
29 #ifdef DEAL_II_WITH_TRILINOS
31 #ifdef DEAL_II_WITH_MPI
33 #endif
34 #endif
35 
36 #include <deal2lkit/utilities.h>
37 #include <kinsol/kinsol_dense.h>
38 
39 D2K_NAMESPACE_OPEN
40 
41 // protect the helper functions
42 namespace
43 {
44 
46  template<typename VEC>
47  int kinsol_residual( N_Vector y, N_Vector res, void *user_data)
48  {
49 
50  KINSOLInterface<VEC> &solver = *static_cast<KINSOLInterface<VEC> *>(user_data);
51 
52 
53  shared_ptr<VEC> loc_y = solver.create_new_vector();
54  shared_ptr<VEC> loc_res = solver.create_new_vector();
55 
56  copy(*loc_y, y);
57  copy(*loc_res, res);
58 
59  int ret = solver.residual(*loc_y, *loc_res);
60 
61  copy(res, *loc_res);
62  return ret;
63  }
64 
66  template<typename VEC>
67  int kinsol_setup_jacobian( KINMem kin_mem )
68  {
69  KINSOLInterface<VEC> &solver = *static_cast<KINSOLInterface<VEC> *> (kin_mem->kin_user_data);
70 
71  shared_ptr<VEC> loc_y = solver.create_new_vector();
72 
73  copy( *loc_y, kin_mem->kin_uu );
74  int err = solver.setup_jacobian( *loc_y );
75  return err;
76  }
77 
79  template<typename VEC>
80  int kinsol_solve_linear_system(KINMem kin_mem,
81  N_Vector x,
82  N_Vector b,
83  double *sJpnorm,
84  double *sFdotJp )
85  {
86  KINSOLInterface<VEC> &solver = *static_cast<KINSOLInterface<VEC> *> (kin_mem->kin_user_data);
87 
88  //shared_ptr<VEC> loc_y = solver.create_new_vector();
89  shared_ptr<VEC> loc_res = solver.create_new_vector();
90  shared_ptr<VEC> dst = solver.create_new_vector();
91  shared_ptr<VEC> loc_b = solver.create_new_vector();
92 
93  //copy( *loc_y, x );
94  copy( *loc_res, kin_mem->kin_fval);
95 
96  int err = solver.solve_linear_system(*loc_res, *dst );
97 
98  copy(x, *dst);
99  err += solver.jacobian_vmult( *dst, *loc_b );
100 
101  copy(b, *loc_b);
102 
103  *sJpnorm = N_VWL2Norm(b, kin_mem->kin_fscale);
104  N_VProd(b, kin_mem->kin_fscale, b);
105  N_VProd(b, kin_mem->kin_fscale, b);
106  *sFdotJp = N_VDotProd(kin_mem->kin_fval, b);
107  return err;
108 
109  }
110 
111 }
112 
113 template <typename VEC>
115 {
116 
117  create_new_vector = []() ->shared_ptr<VEC>
118  {
119  shared_ptr<VEC> p;
121  return p;
122  };
123 
124  residual = [](const VEC &, VEC &) ->int
125  {
126  int ret=0;
128  return ret;
129  };
130 
131  setup_jacobian = [](const VEC &) ->int
132  {
133  int ret=0;
135  return ret;
136  };
137 
138  solve_linear_system = [](const VEC &, VEC &) ->int
139  {
140  int ret=0;
142  return ret;
143  };
144 
145  jacobian_vmult = [](const VEC &, VEC &) ->int
146  {
147  int ret =0;
149  return ret;
150  };
151 
152 }
153 
154 // Class constructor:
155 #ifdef DEAL_II_WITH_MPI
156 
157 
158 template <typename VEC>
159 KINSOLInterface<VEC>::KINSOLInterface(const std::string name,
160  const MPI_Comm mpi_comm):
161  ParameterAcceptor(name),
162  is_initialized(false),
163  scaling_is_set(false),
164  communicator(Utilities::MPI::duplicate_communicator(mpi_comm)),
165  pcout(std::cout, Utilities::MPI::this_mpi_process(mpi_comm)==0),
166  kin_mem(nullptr)
167 {
168  set_functions_to_trigger_an_assert();
169 }
170 
171 #else
172 
173 template <typename VEC>
175  ParameterAcceptor(name),
176  is_initialized(false),
177  pcout(std::cout),
178  kin_mem(nullptr)
179 {
181 }
182 
183 #endif
184 
185 // Class destructor:
186 template <typename VEC>
188 {
189  if (kin_mem)
190  KINFree(&kin_mem);
191 }
192 
193 // Parameters parsing and initialization of the parameters:
194 template <typename VEC>
196 {
197 
199  "Maximum number of iterations","200",
201  "The non-linear solver will stop when reaching this number of"
202  "Newton iterations no matter what.");
203 
204  add_parameter(prm, &ftol,
205  "Tolerance for residuals", "1e-9",
206  Patterns::Double(0),
207  "This define the condition (small residual) for a successful completion of KINSOL.\n"
208  " 0 means the default value for KINSOL");
209 
210  add_parameter(prm, &steptol,
211  "Step tolerance", "1e-11",
212  Patterns::Double(0),
213  "The Newton method will terminate when the maximum scaled step is below the given tolerance." );
214 
215  add_parameter(prm, &verbosity,
216  "Level of verbosity of the KINSOL solver", "0", Patterns::Integer(0,3),
217  "Allowed values [0,3]");
218 
219  add_parameter(prm, &strategy,"Strategy" ,"newton",
220  Patterns::Selection("newton|global_newton|fixed_point|picard"),
221  "newton = basic Newton iteration \n"
222  "global_newton = Newton with line search \n"
223  "fixed_point = fixed-point iteration with Anderson Acceleration \n"
224  "picard = Picard iteration with Anderson Acceleration");
225 
226  add_parameter(prm, &mbset,
227  "Maximum number of iteration before Jacobian update", "10",
229  "Maximum number of nonlinear iterations that can be done with an outdated Jacobian.\n"
230  "If set to 1 the Jacobian is updated at each nonlinear iteration");
231 
233  "Use internal KINSOL direct solver", "false",
234  Patterns::Bool(),
235  "If true the dense direct linear solver of KINSOL is used");
236 
237 }
238 
239 // Initialization of the solver:
240 template <typename VEC>
241 void KINSOLInterface<VEC>::initialize_solver( VEC &initial_guess )
242 {
243  int status;
244 
245  // check if the solver is initialized. If it is reset:
246  if (kin_mem)
247  {
248  KINFree(&kin_mem);
249  is_initialized = false;
250  scaling_is_set = false;
251  }
252  // create the KINSOL memory object:
253  kin_mem = KINCreate();
254 
255  // get the size of the initial guess as the size of the system:
256  system_size = initial_guess.size();
257  // need to feed KINSOL init with a N_VECTOR. For simplicity the initial guess is stored in the solution vector.
258 
259 #ifdef DEAL_II_WITH_MPI
260  IndexSet is = initial_guess.locally_owned_elements();
261  local_system_size = is.n_elements();
262  solution = N_VNew_Parallel(communicator, local_system_size, system_size);
263 #else
264  solution = N_VNew_Serial(system_size);
265 #endif
266 
267  // copy the initial guess on the solution:
268  copy( solution, initial_guess );
269 
270  // pass the pointer to the class to kinsol:
271  status = KINSetUserData(kin_mem, (void *) this);
272  Assert(status == KIN_SUCCESS, ExcMessage("Error initializing KINSOL. KINInit failed."));
273 
274  // 1- maximum iterations:
275  status = KINSetNumMaxIters(kin_mem, max_iterations );
276  Assert(status == KIN_SUCCESS, ExcMessage("Error initializing KINSOL. KINSetNumMaxIters failed."));
277 
278  // 2- ftol:
279  status = KINSetFuncNormTol(kin_mem, ftol);
280  Assert(status == KIN_SUCCESS, ExcMessage("Error initializing KINSOL. KINSetFuncNormTol failed."));
281 
282  // 3- steptol:
283  status = KINSetScaledStepTol(kin_mem, steptol);
284  Assert(status == KIN_SUCCESS, ExcMessage("Error initializing KINSOL. KINSetFuncNormTol failed."));
285 
286  // initialize with the helper function:
287  status = KINInit(kin_mem, kinsol_residual<VEC> , solution);
288  Assert(status == KIN_SUCCESS, ExcMessage("Error initializing KINSOL. KINInit failed."));
289 
290  // set the level of verbosity
291  status = KINSetPrintLevel(kin_mem, verbosity);
292  Assert(status == KIN_SUCCESS, ExcMessage("Error intializing KINSOL. KINSetPrintLevel failed."));
293 
294  status = KINSetMaxSetupCalls(kin_mem, mbset);
295  Assert(status == KIN_SUCCESS, ExcMessage("Error intializing KINSOL. KINSetMaxSetupCalls failed."));
296 
297 
299  status = KINDense(kin_mem, system_size );
300  else
301  {
302  KINMem KIN_mem;
303  KIN_mem = (KINMem) kin_mem;
304  KIN_mem->kin_lsetup = kinsol_setup_jacobian<VEC>;
305  KIN_mem->kin_lsolve = kinsol_solve_linear_system<VEC>;
306  KIN_mem->kin_setupNonNull = true;
307  }
308  is_initialized = true;
309 
310  (void)status;
311 }
312 
313 // Run the solver:
314 template <typename VEC>
316 {
317  int status=0;
318  // Check initialization:
319  if (!is_initialized)
320  {
321  initialize_solver( sol );
322  }
323  // Check initialization of the scaling. If scaling is not initialized we modify the
324  // call to spare allocating a vector.
325  if ( !scaling_is_set )
326  {
327 #ifdef DEAL_II_WITH_MPI
328  u_scale = N_VNew_Parallel(communicator, local_system_size, system_size);
329  N_VConst_Parallel( 1.e0, u_scale );
330  f_scale = N_VNew_Parallel(communicator, local_system_size, system_size);
331  N_VConst_Parallel( 1.e0, f_scale );
332 
333 #else
334  u_scale = N_VNew_Serial(system_size);
335  N_VConst_Serial( 1.e0, u_scale );
336  f_scale = N_VNew_Serial(system_size);
337  N_VConst_Serial( 1.e0, f_scale );
338 #endif
339  }
340 
341 
342  // call to KINSol:
343  if (strategy == "newton")
344  status = KINSol(kin_mem, this->solution, KIN_NONE, u_scale, f_scale);
345  if (strategy == "global_newton")
346  status = KINSol(kin_mem, this->solution, KIN_LINESEARCH, u_scale, f_scale);
347  if (strategy == "fixed_point")
348  status = KINSol(kin_mem, this->solution, KIN_FP, u_scale, f_scale);
349  if (strategy == "picard")
350  status = KINSol(kin_mem, this->solution, KIN_PICARD, u_scale, f_scale);
351 
352  if (status == KIN_MXNEWT_5X_EXCEEDED)
353  {
354  // we get here when the inequality
355  // norm_L2(u_scale*newton_update) > 0.99*mxnewtstep
356  // is satisfied for 5 consecutive steps.
357  // Such a failure may mean that norm_L2(f_scale*F(u))
358  // asymptotes from above to a positive value,
359  // or the real scalar mxnewtstep is too small.
360  // So we rescale the mxnewtstep to the residual norm
361  // and give to kinsol another try
362  auto res = create_new_vector();
363  KINMem KIN_mem;
364  KIN_mem = (KINMem) kin_mem;
365  copy(*res, KIN_mem->kin_fval);
366  KINSetMaxNewtonStep(kin_mem, res->l2_norm());
367 
368  pcout << "Don't worry. This might be only a problem of scaling... Let's try."
369  << std::endl;
370 
371  if (strategy == "newton")
372  status = KINSol(kin_mem, this->solution, KIN_NONE, u_scale, f_scale);
373  if (strategy == "global_newton")
374  status = KINSol(kin_mem, this->solution, KIN_LINESEARCH, u_scale, f_scale);
375 
376  }
377  AssertThrow(status >= 0 , ExcMessage("KINSOL did not converge. You might try with a different strategy."));
378 
379  copy( sol, this->solution );
380  return status;
381 
382 }
383 
384 template <typename VEC>
385 void KINSOLInterface<VEC>::set_scaling_vectors( const VEC &uscale, const VEC &fscale )
386 {
387 
388  // Check initialization:
390  ExcMessage("KINSOLInterface is not initialized when "
391  "calling set_scaling_vectors!\n"
392  "you need to call initialize_solver() first "
393  "in order to make kinsol aware of the dimension of the system."));
394  // initialize the scaling vectors:
395 #ifdef DEAL_II_WITH_MPI
396  u_scale = N_VNew_Parallel(communicator, local_system_size, system_size);
397  f_scale = N_VNew_Parallel(communicator, local_system_size, system_size);
398 #else
399  u_scale = N_VNew_Serial(system_size);
400  f_scale = N_VNew_Serial(system_size);
401 #endif
402  // copy:
403  copy(u_scale, uscale );
404  copy(f_scale, fscale );
405  // scaling is now initialized:
406  scaling_is_set = true;
407 
408 }
409 
410 template <typename VEC>
411 void KINSOLInterface<VEC>::set_constraint_vector( const VEC &constraint )
412 {
413 
414  int status;
415  // Check initialization:
417  ExcMessage("KINSOLInterface is not initialized "
418  "when calling initialize_constraint_vector!"));
419  // copy the vector to an N_Vector:
420 #ifdef DEAL_II_WITH_MPI
421  N_Vector constraint_v = N_VNew_Parallel(communicator, local_system_size, system_size);
422 #else
423  N_Vector constraint_v = N_VNew_Serial(system_size);
424 #endif
425  copy(constraint_v, constraint );
426  // call:
427  status = KINSetConstraints( kin_mem, constraint_v);
428  // check status:
429  Assert(status == KIN_SUCCESS, ExcMessage("Error initializing KINSOL. KINSetConstraints in initialize_constraint_vector failed."));
430  (void)status;
431 
432 }
433 
434 D2K_NAMESPACE_CLOSE
435 
436 template class deal2lkit::KINSOLInterface<BlockVector<double> >;
437 
438 #ifdef DEAL_II_WITH_MPI
439 
440 #ifdef DEAL_II_WITH_TRILINOS
441 template class deal2lkit::KINSOLInterface<TrilinosWrappers::MPI::Vector>;
442 template class deal2lkit::KINSOLInterface<TrilinosWrappers::MPI::BlockVector>;
443 #endif
444 
445 #ifdef DEAL_II_WITH_PETSC
446 template class deal2lkit::KINSOLInterface<PETScWrappers::MPI::Vector>;
447 template class deal2lkit::KINSOLInterface<PETScWrappers::MPI::BlockVector>;
448 #endif
449 
450 #endif
451 
452 #endif
N_Vector solution
KINSOL solution vector.
static::ExceptionBase & ExcPureFunctionCalled()
unsigned int system_size
dimension of the system
A parameter acceptor base class.
static ParameterHandler prm
Static parameter.
bool use_internal_solver
if true the internal direct solver of KINSOL is used.
double steptol
The Newton method will terminate when the scaled norm of the update is smaller than steptol...
void set_scaling_vectors(const VEC &uscale, const VEC &fscale)
Set the scaling for the solver.
void copy(BlockVector< double > &dst, const N_Vector &src)
Definition: utilities.cc:258
size_type n_elements() const
std::function< int(const VEC &v, VEC &dst)> jacobian_vmult
standard function multiplying the Jacobian to a vector
std::function< int(const VEC &y, VEC &res)> residual
standard function computing residuals
void set_functions_to_trigger_an_assert()
This function is executed at construction time to set the std::function above to trigger an assert if...
STL namespace.
#define AssertThrow(cond, exc)
unsigned int max_iterations
Maximum number of iterations.
double ftol
Input scalar tolerance for residuals.
std::function< shared_ptr< VEC >)> create_new_vector
this function has to be implemented by the user and it must return a shared pointer to a VEC vector...
static::ExceptionBase & ExcMessage(std::string arg1)
Interface to SUNDIALS KINSOL library.
unsigned int verbosity
Level of verbosity of kinsol solver 0,1,2,3.
ConditionalOStream pcout
Output stream.
void set_constraint_vector(const VEC &constraint)
Set the constraint.
#define Assert(cond, exc)
std::string strategy
Strategy used by the solver: newton = basic Newton iteration global_newton = Newton with line search ...
std::function< int(const VEC &y)> setup_jacobian
standard function computing the Jacobian
KINSOLInterface(const std::string name="")
Constructor for the KINSOLInterface class.
void initialize_solver(VEC &initial_guess)
Initializes the solver with the initial guess and the residual function.
virtual void declare_parameters(ParameterHandler &prm)
Declare parameters for this class to function properly.
N_Vector f_scale
Internal scaling vector for the Jacobian.
std::function< int(const VEC &res, VEC &dst)> solve_linear_system
standard function solving linear system
~KINSOLInterface()
House cleaning.
bool scaling_is_set
scaling flag: if the scaling have been provided by the user calling the function set_scaling_vectors(...
MPI_Comm duplicate_communicator(const MPI_Comm &mpi_communicator)
unsigned int this_mpi_process(const MPI_Comm &mpi_communicator)
bool is_initialized
Initialization flag.
N_Vector u_scale
Internal scaling vector for the solution.
int solve(VEC &solution)
solve the non-linear system
void add_parameter(ParameterHandler &prm, T *parameter, const std::string &entry, const std::string &default_value, const Patterns::PatternBase &pattern=Patterns::Anything(), const std::string &documentation=std::string())
Add a parameter the given parameter list.
double mbset
Maximum number of nonlinear iteration that can be done without a Jacobian update. ...