Reference documentation for deal.II version 9.3.0
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
kinsol.cc
Go to the documentation of this file.
1 //-----------------------------------------------------------
2 //
3 // Copyright (C) 2017 - 2021 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II 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.md at
12 // the top level directory of deal.II.
13 //
14 //-----------------------------------------------------------
15 
16 
17 #include <deal.II/base/config.h>
18 
20 
21 #ifdef DEAL_II_WITH_SUNDIALS
22 
23 # include <deal.II/base/utilities.h>
24 
28 # ifdef DEAL_II_WITH_TRILINOS
31 # endif
32 # ifdef DEAL_II_WITH_PETSC
35 # endif
36 
38 
39 // Make sure we #include the SUNDIALS config file...
40 # include <sundials/sundials_config.h>
41 // ...before the rest of the SUNDIALS files:
42 # include <kinsol/kinsol_direct.h>
43 # include <sunlinsol/sunlinsol_dense.h>
44 # include <sunmatrix/sunmatrix_dense.h>
45 
46 # include <iomanip>
47 # include <iostream>
48 
50 
51 namespace SUNDIALS
52 {
53  using namespace internal;
54 
55 
56  template <typename VectorType>
58  const SolutionStrategy &strategy,
59  const unsigned int maximum_non_linear_iterations,
60  const double function_tolerance,
61  const double step_tolerance,
62  const bool no_init_setup,
63  const unsigned int maximum_setup_calls,
64  const double maximum_newton_step,
65  const double dq_relative_error,
66  const unsigned int maximum_beta_failures,
67  const unsigned int anderson_subspace_size)
68  : strategy(strategy)
69  , maximum_non_linear_iterations(maximum_non_linear_iterations)
70  , function_tolerance(function_tolerance)
71  , step_tolerance(step_tolerance)
72  , no_init_setup(no_init_setup)
73  , maximum_setup_calls(maximum_setup_calls)
74  , maximum_newton_step(maximum_newton_step)
75  , dq_relative_error(dq_relative_error)
76  , maximum_beta_failures(maximum_beta_failures)
77  , anderson_subspace_size(anderson_subspace_size)
78  {}
79 
80 
81 
82  template <typename VectorType>
83  void
85  {
86  static std::string strategy_str("newton");
87  prm.add_parameter("Solution strategy",
88  strategy_str,
89  "Choose among newton|linesearch|fixed_point|picard",
91  "newton|linesearch|fixed_point|picard"));
92  prm.add_action("Solution strategy", [&](const std::string &value) {
93  if (value == "newton")
94  strategy = newton;
95  else if (value == "linesearch")
96  strategy = linesearch;
97  else if (value == "fixed_point")
98  strategy = fixed_point;
99  else if (value == "picard")
100  strategy = picard;
101  else
102  Assert(false, ExcInternalError());
103  });
104  prm.add_parameter("Maximum number of nonlinear iterations",
105  maximum_non_linear_iterations);
106  prm.add_parameter("Function norm stopping tolerance", function_tolerance);
107  prm.add_parameter("Scaled step stopping tolerance", step_tolerance);
108 
109  prm.enter_subsection("Newton parameters");
110  prm.add_parameter("No initial matrix setup", no_init_setup);
111  prm.add_parameter("Maximum iterations without matrix setup",
112  maximum_setup_calls);
113  prm.add_parameter("Maximum allowable scaled length of the Newton step",
114  maximum_newton_step);
115  prm.add_parameter("Relative error for different quotient computation",
116  dq_relative_error);
117  prm.leave_subsection();
118 
119  prm.enter_subsection("Linesearch parameters");
120  prm.add_parameter("Maximum number of beta-condition failures",
121  maximum_beta_failures);
122  prm.leave_subsection();
123 
124 
125  prm.enter_subsection("Fixed point and Picard parameters");
126  prm.add_parameter("Anderson acceleration subspace size",
127  anderson_subspace_size);
128  prm.leave_subsection();
129  }
130 
131 
132  namespace
133  {
134  template <typename VectorType>
135  int
136  residual_callback(N_Vector yy, N_Vector FF, void *user_data)
137  {
138  KINSOL<VectorType> &solver =
139  *static_cast<KINSOL<VectorType> *>(user_data);
140 
141  auto *src_yy = internal::unwrap_nvector_const<VectorType>(yy);
142  auto *dst_FF = internal::unwrap_nvector<VectorType>(FF);
143 
144  int err = 0;
145  if (solver.residual)
146  err = solver.residual(*src_yy, *dst_FF);
147  else
148  Assert(false, ExcInternalError());
149 
150  return err;
151  }
152 
153 
154 
155  template <typename VectorType>
156  int
157  iteration_callback(N_Vector yy, N_Vector FF, void *user_data)
158  {
159  KINSOL<VectorType> &solver =
160  *static_cast<KINSOL<VectorType> *>(user_data);
161 
162  auto *src_yy = internal::unwrap_nvector_const<VectorType>(yy);
163  auto *dst_FF = internal::unwrap_nvector<VectorType>(FF);
164 
165  int err = 0;
166  if (solver.iteration_function)
167  err = solver.iteration_function(*src_yy, *dst_FF);
168  else
169  Assert(false, ExcInternalError());
170 
171  return err;
172  }
173 
174 
175 
176 # if DEAL_II_SUNDIALS_VERSION_LT(4, 1, 0)
177  template <typename VectorType>
178  int
179  setup_jacobian_callback(KINMem kinsol_mem)
180  {
181  KINSOL<VectorType> &solver =
182  *static_cast<KINSOL<VectorType> *>(kinsol_mem->kin_user_data);
183 
184  auto *src_ycur =
185  internal::unwrap_nvector_const<VectorType>(kinsol_mem->kin_uu);
186  auto *src_fcur =
187  internal::unwrap_nvector_const<VectorType>(kinsol_mem->kin_fval);
188 
189  int err = solver.setup_jacobian(*src_ycur, *src_fcur);
190  return err;
191  }
192 
193 
194 
195  template <typename VectorType>
196  int
197  solve_with_jacobian_callback(KINMem kinsol_mem,
198  N_Vector x,
199  N_Vector b,
200  realtype *sJpnorm,
201  realtype *sFdotJp)
202  {
203  KINSOL<VectorType> &solver =
204  *static_cast<KINSOL<VectorType> *>(kinsol_mem->kin_user_data);
205 
206  auto *src_ycur =
207  internal::unwrap_nvector_const<VectorType>(kinsol_mem->kin_uu);
208  auto *src_fcur =
209  internal::unwrap_nvector_const<VectorType>(kinsol_mem->kin_fval);
210  auto *src = internal::unwrap_nvector_const<VectorType>(b);
211  auto *dst = internal::unwrap_nvector<VectorType>(x);
212 
213  int err = solver.solve_jacobian_system(*src_ycur, *src_fcur, *src, *dst);
214 
215  *sJpnorm = N_VWL2Norm(b, kinsol_mem->kin_fscale);
216  N_VProd(b, kinsol_mem->kin_fscale, b);
217  N_VProd(b, kinsol_mem->kin_fscale, b);
218  *sFdotJp = N_VDotProd(kinsol_mem->kin_fval, b);
219 
220  return err;
221  }
222 
223 # else // SUNDIALS 5.0 or later
224 
225  template <typename VectorType>
226  int
227  setup_jacobian_callback(N_Vector u,
228  N_Vector f,
229  SUNMatrix /* ignored */,
230  void *user_data,
231  N_Vector /* tmp1 */,
232  N_Vector /* tmp2 */)
233  {
234  // Receive the object that describes the linear solver and
235  // unpack the pointer to the KINSOL object from which we can then
236  // get the 'setup' function.
237  const KINSOL<VectorType> &solver =
238  *static_cast<const KINSOL<VectorType> *>(user_data);
239 
240  auto *ycur = internal::unwrap_nvector_const<VectorType>(u);
241  auto *fcur = internal::unwrap_nvector_const<VectorType>(f);
242 
243  // Call the user-provided setup function with these arguments:
244  solver.setup_jacobian(*ycur, *fcur);
245 
246  return 0;
247  }
248 
249 
250 
251  template <typename VectorType>
252  int
253  solve_with_jacobian_callback(SUNLinearSolver LS,
254  SUNMatrix /*ignored*/,
255  N_Vector x,
256  N_Vector b,
257  realtype tol)
258  {
259  // Receive the object that describes the linear solver and
260  // unpack the pointer to the KINSOL object from which we can then
261  // get the 'reinit' and 'solve' functions.
262  const KINSOL<VectorType> &solver =
263  *static_cast<const KINSOL<VectorType> *>(LS->content);
264 
265  // This is where we have to make a decision about which of the two
266  // signals to call. Let's first check the more modern one:
267  if (solver.solve_with_jacobian)
268  {
269  auto *src_b = internal::unwrap_nvector<VectorType>(b);
270  auto *dst_x = internal::unwrap_nvector<VectorType>(x);
271 
272  const int err = solver.solve_with_jacobian(*src_b, *dst_x, tol);
273 
274  return err;
275  }
276  else
277  {
278  // User has not provided the modern callback, so the fact that we are
279  // here means that they must have given us something for the old
280  // signal. Check this.
281  Assert(solver.solve_jacobian_system, ExcInternalError());
282 
283  // Allocate temporary (deal.II-type) vectors into which to copy the
284  // N_vectors
286  typename VectorMemory<VectorType>::Pointer src_ycur(mem);
287  typename VectorMemory<VectorType>::Pointer src_fcur(mem);
288 
289  auto *src_b = internal::unwrap_nvector_const<VectorType>(b);
290  auto *dst_x = internal::unwrap_nvector<VectorType>(x);
291 
292  // Call the user-provided setup function with these arguments. Note
293  // that Sundials 4.x and later no longer provide values for
294  // src_ycur and src_fcur, and so we simply pass dummy vector in.
295  // These vectors will have zero lengths because we don't reinit them
296  // above.
297  const int err =
298  solver.solve_jacobian_system(*src_ycur, *src_fcur, *src_b, *dst_x);
299 
300  return err;
301  }
302  }
303 # endif
304  } // namespace
305 
306 
307 
308  template <typename VectorType>
310  : data(data)
311  , kinsol_mem(nullptr)
312  {
314  }
315 
316 
317 
318  template <typename VectorType>
320  {
321  if (kinsol_mem)
322  KINFree(&kinsol_mem);
323  }
324 
325 
326 
327  template <typename VectorType>
328  unsigned int
329  KINSOL<VectorType>::solve(VectorType &initial_guess_and_solution)
330  {
331  NVectorView<VectorType> u_scale, f_scale;
332 
333  VectorType u_scale_temp, f_scale_temp;
334 
335  if (get_solution_scaling)
336  u_scale = internal::make_nvector_view(get_solution_scaling());
337  else
338  {
339  reinit_vector(u_scale_temp);
340  u_scale_temp = 1.0;
341  u_scale = internal::make_nvector_view(u_scale_temp);
342  }
343 
344  if (get_function_scaling)
345  f_scale = internal::make_nvector_view(get_function_scaling());
346  else
347  {
348  reinit_vector(f_scale_temp);
349  f_scale_temp = 1.0;
350  f_scale = internal::make_nvector_view(f_scale_temp);
351  }
352 
353  // Make sure we have what we need
354  if (data.strategy == AdditionalData::fixed_point)
355  {
356  Assert(iteration_function,
357  ExcFunctionNotProvided("iteration_function"));
358  }
359  else
360  {
361  Assert(residual, ExcFunctionNotProvided("residual"));
362  Assert(solve_jacobian_system || solve_with_jacobian,
363  ExcFunctionNotProvided(
364  "solve_jacobian_system || solve_with_jacobian"));
365  }
366 
367  auto solution = internal::make_nvector_view(initial_guess_and_solution);
368 
369  if (kinsol_mem)
370  KINFree(&kinsol_mem);
371 
372  kinsol_mem = KINCreate();
373 
374  int status = 0;
375  (void)status;
376 
377  status = KINSetUserData(kinsol_mem, static_cast<void *>(this));
378  AssertKINSOL(status);
379 
380  // This must be called before KINSetMAA
381  status = KINSetNumMaxIters(kinsol_mem, data.maximum_non_linear_iterations);
382  AssertKINSOL(status);
383 
384  // From the manual: this must be called BEFORE KINInit
385  status = KINSetMAA(kinsol_mem, data.anderson_subspace_size);
386  AssertKINSOL(status);
387 
388  if (data.strategy == AdditionalData::fixed_point)
389  status = KINInit(kinsol_mem, iteration_callback<VectorType>, solution);
390  else
391  status = KINInit(kinsol_mem, residual_callback<VectorType>, solution);
392  AssertKINSOL(status);
393 
394  status = KINSetFuncNormTol(kinsol_mem, data.function_tolerance);
395  AssertKINSOL(status);
396 
397  status = KINSetScaledStepTol(kinsol_mem, data.step_tolerance);
398  AssertKINSOL(status);
399 
400  status = KINSetMaxSetupCalls(kinsol_mem, data.maximum_setup_calls);
401  AssertKINSOL(status);
402 
403  status = KINSetNoInitSetup(kinsol_mem, data.no_init_setup);
404  AssertKINSOL(status);
405 
406  status = KINSetMaxNewtonStep(kinsol_mem, data.maximum_newton_step);
407  AssertKINSOL(status);
408 
409  status = KINSetMaxBetaFails(kinsol_mem, data.maximum_beta_failures);
410  AssertKINSOL(status);
411 
412  status = KINSetRelErrFunc(kinsol_mem, data.dq_relative_error);
413  AssertKINSOL(status);
414 
415  SUNMatrix J = nullptr;
416  SUNLinearSolver LS = nullptr;
417 
418  if (solve_jacobian_system ||
419  solve_with_jacobian) // user assigned a function object to the solver
420  // slot
421  {
422 /* interface up to and including 4.0 */
423 # if DEAL_II_SUNDIALS_VERSION_LT(4, 1, 0)
424  auto KIN_mem = static_cast<KINMem>(kinsol_mem);
425  // Old version only works with solve_jacobian_system
426  Assert(solve_jacobian_system,
427  ExcFunctionNotProvided("solve_jacobian_system"))
428  KIN_mem->kin_lsolve = solve_with_jacobian_callback<VectorType>;
429  if (setup_jacobian) // user assigned a function object to the Jacobian
430  // set-up slot
431  KIN_mem->kin_lsetup = setup_jacobian_callback<VectorType>;
432 
433 /* interface up to and including 4.1 */
434 # elif DEAL_II_SUNDIALS_VERSION_LT(5, 0, 0)
435 
436  // deal.II does not currently have support for KINSOL in
437  // SUNDIALS 4.1. One could write this and update this section,
438  // but it does not seem worthwhile spending the time to
439  // interface with an old version of SUNDIAL given that the
440  // code below supports modern SUNDIAL versions just fine.
441  Assert(false, ExcNotImplemented());
442 
443 # else /* interface starting with SUNDIALS 5.0 */
444  // Set the operations we care for in the sun_linear_solver object
445  // and attach it to the KINSOL object. The functions that will get
446  // called do not actually receive the KINSOL object, just the LS
447  // object, so we have to store a pointer to the current
448  // object in the LS object
449  LS = SUNLinSolNewEmpty();
450  LS->content = this;
451 
452  LS->ops->gettype =
453  [](SUNLinearSolver /*ignored*/) -> SUNLinearSolver_Type {
454  return SUNLINEARSOLVER_MATRIX_ITERATIVE;
455  };
456 
457  LS->ops->free = [](SUNLinearSolver LS) -> int {
458  if (LS->content)
459  {
460  LS->content = nullptr;
461  }
462  if (LS->ops)
463  {
464  free(LS->ops);
465  LS->ops = nullptr;
466  }
467  free(LS);
468  LS = nullptr;
469  return 0;
470  };
471 
472  LS->ops->solve = solve_with_jacobian_callback<VectorType>;
473 
474  // Even though we don't use it, KINSOL still wants us to set some
475  // kind of matrix object for the nonlinear solver. This is because
476  // if we don't set it, it won't call the functions that set up
477  // the matrix object (i.e., the argument to the 'KINSetJacFn'
478  // function below).
479  J = SUNMatNewEmpty();
480  J->content = this;
481 
482  J->ops->getid = [](SUNMatrix /*ignored*/) -> SUNMatrix_ID {
483  return SUNMATRIX_CUSTOM;
484  };
485 
486  J->ops->destroy = [](SUNMatrix A) {
487  if (A->content)
488  {
489  A->content = nullptr;
490  }
491  if (A->ops)
492  {
493  free(A->ops);
494  A->ops = nullptr;
495  }
496  free(A);
497  A = nullptr;
498  };
499 
500  // Now set the linear system and Jacobian objects in the solver:
501  status = KINSetLinearSolver(kinsol_mem, LS, J);
502  AssertKINSOL(status);
503 
504  // Finally, if we were given a set-up function, tell KINSOL about
505  // it as well. The manual says that this must happen *after*
506  // calling KINSetLinearSolver
507  if (!setup_jacobian)
508  setup_jacobian = [](const VectorType &, const VectorType &) {
509  return 0;
510  };
511  status = KINSetJacFn(kinsol_mem, &setup_jacobian_callback<VectorType>);
512  AssertKINSOL(status);
513 # endif
514  }
515 
516  // call to KINSol
517  status = KINSol(kinsol_mem, solution, data.strategy, u_scale, f_scale);
518  AssertKINSOL(status);
519 
520  long nniters;
521  status = KINGetNumNonlinSolvIters(kinsol_mem, &nniters);
522  AssertKINSOL(status);
523 
524  if (J != nullptr)
525  SUNMatDestroy(J);
526  if (LS != nullptr)
527  SUNLinSolFree(LS);
528  KINFree(&kinsol_mem);
529 
530  return static_cast<unsigned int>(nniters);
531  }
532 
533  template <typename VectorType>
534  void
536  {
537  reinit_vector = [](VectorType &) {
538  AssertThrow(false, ExcFunctionNotProvided("reinit_vector"));
539  };
540  }
541 
542  template class KINSOL<Vector<double>>;
543  template class KINSOL<BlockVector<double>>;
544 
547 
548 # ifdef DEAL_II_WITH_MPI
549 
550 # ifdef DEAL_II_WITH_TRILINOS
553 # endif
554 
555 # ifdef DEAL_II_WITH_PETSC
556 # ifndef PETSC_USE_COMPLEX
557  template class KINSOL<PETScWrappers::MPI::Vector>;
559 # endif
560 # endif
561 
562 # endif
563 
564 } // namespace SUNDIALS
565 
567 
568 #endif
void add_parameter(const std::string &entry, ParameterType &parameter, const std::string &documentation="", const Patterns::PatternBase &pattern= *Patterns::Tools::Convert< ParameterType >::to_pattern(), const bool has_to_be_set=false)
void add_action(const std::string &entry, const std::function< void(const std::string &value)> &action)
void enter_subsection(const std::string &subsection)
AdditionalData(const SolutionStrategy &strategy=linesearch, const unsigned int maximum_non_linear_iterations=200, const double function_tolerance=0.0, const double step_tolerance=0.0, const bool no_init_setup=false, const unsigned int maximum_setup_calls=0, const double maximum_newton_step=0.0, const double dq_relative_error=0.0, const unsigned int maximum_beta_failures=0, const unsigned int anderson_subspace_size=0)
Definition: kinsol.cc:57
void add_parameters(ParameterHandler &prm)
Definition: kinsol.cc:84
void set_functions_to_trigger_an_assert()
Definition: kinsol.cc:535
KINSOL(const AdditionalData &data=AdditionalData(), const MPI_Comm &mpi_comm=MPI_COMM_WORLD)
Definition: kinsol.cc:309
unsigned int solve(VectorType &initial_guess_and_solution)
Definition: kinsol.cc:329
GrowingVectorMemory< VectorType > mem
Definition: kinsol.h:695
std::function< int(const VectorType &src, VectorType &dst)> residual
Definition: kinsol.h:424
AdditionalData data
Definition: kinsol.h:685
std::function< int(const VectorType &src, VectorType &dst)> iteration_function
Definition: kinsol.h:441
void * kinsol_mem
Definition: kinsol.h:690
#define DEAL_II_NAMESPACE_OPEN
Definition: config.h:394
#define DEAL_II_NAMESPACE_CLOSE
Definition: config.h:395
static ::ExceptionBase & ExcInternalError()
#define Assert(cond, exc)
Definition: exceptions.h:1465
static ::ExceptionBase & ExcNotImplemented()
#define AssertThrow(cond, exc)
Definition: exceptions.h:1575
#define AssertKINSOL(code)
Definition: kinsol.h:49
static const char A
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
SUNLinearSolver SUNLinSolNewEmpty()
void free(T *&pointer)
Definition: cuda.h:97