deal.II version GIT relicensing-6842-g793a97d2aa 2026-10-02 14:00:01+00:00
\(\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\}}\)
Loading...
Searching...
No Matches
kinsol.cc
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2017 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
13
14
15#include <deal.II/base/config.h>
16
18
19#ifdef DEAL_II_WITH_SUNDIALS
20
23
27# ifdef DEAL_II_TRILINOS_WITH_EPETRA
30# endif
31# ifdef DEAL_II_TRILINOS_WITH_TPETRA
34# endif
35# ifdef DEAL_II_WITH_PETSC
38# endif
39
42
43// Make sure we #include the SUNDIALS config file...
44# include <sundials/sundials_config.h>
45// ...before the rest of the SUNDIALS files:
46# if DEAL_II_SUNDIALS_VERSION_GTE(6, 0, 0)
47# include <kinsol/kinsol_ls.h>
48# else
49# include <kinsol/kinsol_direct.h>
50# endif
51# include <kinsol/kinsol.h>
52# include <sunlinsol/sunlinsol_dense.h>
53# include <sunmatrix/sunmatrix_dense.h>
54
55# include <iomanip>
56# include <iostream>
57
58
59#endif
60
62
63#ifdef DEAL_II_WITH_SUNDIALS
64
65namespace SUNDIALS
66{
67 template <typename VectorType>
69 const SolutionStrategy &strategy,
70 const unsigned int maximum_non_linear_iterations,
71 const double function_tolerance,
72 const double step_tolerance,
73 const bool no_init_setup,
74 const unsigned int maximum_setup_calls,
75 const double maximum_newton_step,
76 const double dq_relative_error,
77 const unsigned int maximum_beta_failures,
78 const unsigned int anderson_subspace_size,
79 const OrthogonalizationStrategy anderson_qr_orthogonalization)
80 : strategy(strategy)
81 , maximum_non_linear_iterations(maximum_non_linear_iterations)
82 , function_tolerance(function_tolerance)
83 , step_tolerance(step_tolerance)
84 , no_init_setup(no_init_setup)
85 , maximum_setup_calls(maximum_setup_calls)
86 , maximum_newton_step(maximum_newton_step)
87 , dq_relative_error(dq_relative_error)
88 , maximum_beta_failures(maximum_beta_failures)
89 , anderson_subspace_size(anderson_subspace_size)
90 , anderson_qr_orthogonalization(anderson_qr_orthogonalization)
91 {}
92
93
94
95 template <typename VectorType>
96 void
98 {
99 static std::string strategy_str("newton");
100 prm.add_parameter("Solution strategy",
101 strategy_str,
102 "Choose among newton|linesearch|fixed_point|picard",
104 "newton|linesearch|fixed_point|picard"));
105 prm.add_action("Solution strategy", [&](const std::string &value) {
106 if (value == "newton")
107 strategy = newton;
108 else if (value == "linesearch")
109 strategy = linesearch;
110 else if (value == "fixed_point")
111 strategy = fixed_point;
112 else if (value == "picard")
113 strategy = picard;
114 else
116 });
117 prm.add_parameter("Maximum number of nonlinear iterations",
118 maximum_non_linear_iterations);
119 prm.add_parameter("Function norm stopping tolerance", function_tolerance);
120 prm.add_parameter("Scaled step stopping tolerance", step_tolerance);
121
122 prm.enter_subsection("Newton parameters");
123 prm.add_parameter("No initial matrix setup", no_init_setup);
124 prm.add_parameter("Maximum iterations without matrix setup",
125 maximum_setup_calls);
126 prm.add_parameter("Maximum allowable scaled length of the Newton step",
127 maximum_newton_step);
128 prm.add_parameter("Relative error for different quotient computation",
129 dq_relative_error);
130 prm.leave_subsection();
131
132 prm.enter_subsection("Linesearch parameters");
133 prm.add_parameter("Maximum number of beta-condition failures",
134 maximum_beta_failures);
135 prm.leave_subsection();
136
137
138 prm.enter_subsection("Fixed point and Picard parameters");
139 prm.add_parameter("Anderson acceleration subspace size",
140 anderson_subspace_size);
141
142 static std::string orthogonalization_str("modified_gram_schmidt");
143 prm.add_parameter(
144 "Anderson QR orthogonalization",
145 orthogonalization_str,
146 "Choose among modified_gram_schmidt|inverse_compact|"
147 "classical_gram_schmidt|delayed_classical_gram_schmidt",
149 "modified_gram_schmidt|inverse_compact|classical_gram_schmidt|"
150 "delayed_classical_gram_schmidt"));
151 prm.add_action("Anderson QR orthogonalization",
152 [&](const std::string &value) {
153 if (value == "modified_gram_schmidt")
154 anderson_qr_orthogonalization = modified_gram_schmidt;
155 else if (value == "inverse_compact")
156 anderson_qr_orthogonalization = inverse_compact;
157 else if (value == "classical_gram_schmidt")
158 anderson_qr_orthogonalization = classical_gram_schmidt;
159 else if (value == "delayed_classical_gram_schmidt")
160 anderson_qr_orthogonalization =
161 delayed_classical_gram_schmidt;
162 else
164 });
165 prm.leave_subsection();
166 }
167
168
169
170 template <typename VectorType>
172 : KINSOL(data, MPI_COMM_SELF)
173 {}
174
175
176
177 template <typename VectorType>
179 const MPI_Comm mpi_comm)
180 : data(data)
181 , mpi_communicator(mpi_comm)
182 , kinsol_mem(nullptr)
184 , kinsol_ctx(nullptr)
185# endif
186 , pending_exception(nullptr)
187 {
189
190 // SUNDIALS will always duplicate communicators if we provide them. This
191 // can cause problems if SUNDIALS is configured with MPI and we pass along
192 // MPI_COMM_SELF in a serial application as MPI won't be
193 // initialized. Hence, work around that by just not providing a
194 // communicator in that case.
195# if DEAL_II_SUNDIALS_VERSION_GTE(7, 0, 0)
196 const int status =
197 SUNContext_Create(mpi_communicator == MPI_COMM_SELF ? SUN_COMM_NULL :
199 &kinsol_ctx);
200 (void)status;
201 AssertKINSOL(status);
202# elif DEAL_II_SUNDIALS_VERSION_GTE(6, 0, 0)
203 const int status =
204 SUNContext_Create(mpi_communicator == MPI_COMM_SELF ? nullptr :
206 &kinsol_ctx);
207 (void)status;
208 AssertKINSOL(status);
209# endif
210 }
211
212
213
214 template <typename VectorType>
216 {
217 KINFree(&kinsol_mem);
218# if DEAL_II_SUNDIALS_VERSION_GTE(6, 0, 0)
219 const int status = SUNContext_Free(&kinsol_ctx);
220 (void)status;
221 AssertKINSOL(status);
222# endif
223
224 Assert(pending_exception == nullptr, ExcInternalError());
225 }
226
227
228
229 template <typename VectorType>
230 unsigned int
231 KINSOL<VectorType>::solve(VectorType &initial_guess_and_solution)
232 {
233 // Make sure we have what we need
234 if (data.strategy == AdditionalData::fixed_point)
235 {
236 Assert(iteration_function,
237 ExcFunctionNotProvided("iteration_function"));
238 }
239 else
240 {
241 Assert(residual, ExcFunctionNotProvided("residual"));
242 Assert(solve_with_jacobian,
243 ExcFunctionNotProvided("solve_with_jacobian"));
244 }
245
246 // Create a new solver object:
247 int status = 0;
248 (void)status;
249
250 KINFree(&kinsol_mem);
251# if DEAL_II_SUNDIALS_VERSION_GTE(6, 0, 0)
252 status = SUNContext_Free(&kinsol_ctx);
253 AssertKINSOL(status);
254# endif
255
256
257# if DEAL_II_SUNDIALS_VERSION_GTE(7, 0, 0)
258 // Same comment applies as in class constructor:
259 status =
260 SUNContext_Create(mpi_communicator == MPI_COMM_SELF ? SUN_COMM_NULL :
261 mpi_communicator,
262 &kinsol_ctx);
263 AssertKINSOL(status);
264
265 kinsol_mem = KINCreate(kinsol_ctx);
266# elif DEAL_II_SUNDIALS_VERSION_GTE(6, 0, 0)
267 // Same comment applies as in class constructor:
268 status =
269 SUNContext_Create(mpi_communicator == MPI_COMM_SELF ? nullptr :
270 &mpi_communicator,
271 &kinsol_ctx);
272 AssertKINSOL(status);
273
274 kinsol_mem = KINCreate(kinsol_ctx);
275# else
276 kinsol_mem = KINCreate();
277# endif
278
279 status = KINSetUserData(kinsol_mem, static_cast<void *>(this));
280 AssertKINSOL(status);
281
282 // helper function to create N_Vectors compatible with different versions
283 // of SUNDIALS
284 const auto make_compatible_nvector_view =
285# if DEAL_II_SUNDIALS_VERSION_LT(6, 0, 0)
286 [](auto &v) { return internal::make_nvector_view(v); };
287# else
288 [this](auto &v) { return internal::make_nvector_view(v, kinsol_ctx); };
289# endif
290
291
292 VectorType ones;
293 // Prepare constant vector for scaling f or u only if we need it
294 if (!get_function_scaling || !get_solution_scaling)
295 {
296 reinit_vector(ones);
297 ones = 1.0;
298 }
299
300 auto u_scale = make_compatible_nvector_view(
301 get_solution_scaling ? get_solution_scaling() : ones);
302 auto f_scale = make_compatible_nvector_view(
303 get_function_scaling ? get_function_scaling() : ones);
304
305 auto solution = make_compatible_nvector_view(initial_guess_and_solution);
306
307 // This must be called before KINSetMAA
308 status = KINSetNumMaxIters(kinsol_mem, data.maximum_non_linear_iterations);
309 AssertKINSOL(status);
310
311 // From the manual: this must be called BEFORE KINInit
312 status = KINSetMAA(kinsol_mem, data.anderson_subspace_size);
313 AssertKINSOL(status);
314
315# if DEAL_II_SUNDIALS_VERSION_GTE(6, 0, 0)
316 // From the manual: this must be called BEFORE KINInit
317 status = KINSetOrthAA(kinsol_mem, data.anderson_qr_orthogonalization);
318 AssertKINSOL(status);
319# else
321 data.anderson_qr_orthogonalization ==
322 AdditionalData::modified_gram_schmidt,
324 "You specified an orthogonalization strategy for QR factorization "
325 "different from the default (modified Gram-Schmidt) but the installed "
326 "SUNDIALS version does not support this feature. Either choose the "
327 "default or install a SUNDIALS version >= 6.0.0."));
328# endif
329
330 if (data.strategy == AdditionalData::fixed_point)
331 status = KINInit(
332 kinsol_mem,
333 /* wrap up the iteration_function() callback: */
334 [](N_Vector yy, N_Vector FF, void *user_data) -> int {
335 KINSOL<VectorType> &solver =
336 *static_cast<KINSOL<VectorType> *>(user_data);
337
338 auto src_yy = internal::unwrap_nvector_const<VectorType>(yy);
339 auto dst_FF = internal::unwrap_nvector<VectorType>(FF);
340
342
344 solver.iteration_function,
345 solver.pending_exception,
346 *src_yy,
347 *dst_FF);
348
349 return err;
350 },
351 solution);
352 else
353 status = KINInit(
354 kinsol_mem,
355 /* wrap up the residual() callback: */
356 [](N_Vector yy, N_Vector FF, void *user_data) -> int {
357 KINSOL<VectorType> &solver =
358 *static_cast<KINSOL<VectorType> *>(user_data);
359
360 auto src_yy = internal::unwrap_nvector_const<VectorType>(yy);
361 auto dst_FF = internal::unwrap_nvector<VectorType>(FF);
362
364
366 solver.residual, solver.pending_exception, *src_yy, *dst_FF);
367
368 return err;
369 },
370 solution);
371 AssertKINSOL(status);
372
373 status = KINSetFuncNormTol(kinsol_mem, data.function_tolerance);
374 AssertKINSOL(status);
375
376 status = KINSetScaledStepTol(kinsol_mem, data.step_tolerance);
377 AssertKINSOL(status);
378
379 status = KINSetMaxSetupCalls(kinsol_mem, data.maximum_setup_calls);
380 AssertKINSOL(status);
381
382 status = KINSetNoInitSetup(kinsol_mem, data.no_init_setup);
383 AssertKINSOL(status);
384
385 status = KINSetMaxNewtonStep(kinsol_mem, data.maximum_newton_step);
386 AssertKINSOL(status);
387
388 status = KINSetMaxBetaFails(kinsol_mem, data.maximum_beta_failures);
389 AssertKINSOL(status);
390
391 status = KINSetRelErrFunc(kinsol_mem, data.dq_relative_error);
392 AssertKINSOL(status);
393
394 SUNMatrix J = nullptr;
395 SUNLinearSolver LS = nullptr;
396
397 // user assigned a function object to the solver slot
398 if (solve_with_jacobian)
399 {
400 // Set the operations we care for in the sun_linear_solver object
401 // and attach it to the KINSOL object. The functions that will get
402 // called do not actually receive the KINSOL object, just the LS
403 // object, so we have to store a pointer to the current
404 // object in the LS object
405# if DEAL_II_SUNDIALS_VERSION_LT(6, 0, 0)
406 LS = SUNLinSolNewEmpty();
407# else
408 LS = SUNLinSolNewEmpty(kinsol_ctx);
409# endif
410 LS->content = this;
411
412 LS->ops->gettype =
413 [](SUNLinearSolver /*ignored*/) -> SUNLinearSolver_Type {
414 return SUNLINEARSOLVER_MATRIX_ITERATIVE;
415 };
416
417 LS->ops->free = [](SUNLinearSolver LS) -> int {
418 if (LS->content)
419 {
420 LS->content = nullptr;
421 }
422 if (LS->ops)
423 {
424 std::free(LS->ops);
425 LS->ops = nullptr;
426 }
427 std::free(LS);
428 LS = nullptr;
429 return 0;
430 };
431
432 LS->ops->solve = [](SUNLinearSolver LS,
433 SUNMatrix /*ignored*/,
434 N_Vector x,
435 N_Vector b,
436 SUNDIALS::realtype tol) -> int {
437 // Receive the object that describes the linear solver and
438 // unpack the pointer to the KINSOL object from which we can then
439 // get the 'reinit' and 'solve' functions.
440 const KINSOL<VectorType> &solver =
441 *static_cast<const KINSOL<VectorType> *>(LS->content);
442
444
445 auto src_b = internal::unwrap_nvector_const<VectorType>(b);
446 auto dst_x = internal::unwrap_nvector<VectorType>(x);
447
450 solver.pending_exception,
451 *src_b,
452 *dst_x,
453 tol);
455 return err;
456 };
457
458 // Even though we don't use it, KINSOL still wants us to set some
459 // kind of matrix object for the nonlinear solver. This is because
460 // if we don't set it, it won't call the functions that set up
461 // the matrix object (i.e., the argument to the 'KINSetJacFn'
462 // function below).
463# if DEAL_II_SUNDIALS_VERSION_LT(6, 0, 0)
464 J = SUNMatNewEmpty();
465# else
466 J = SUNMatNewEmpty(kinsol_ctx);
467# endif
468 J->content = this;
469
470 J->ops->getid = [](SUNMatrix /*ignored*/) -> SUNMatrix_ID {
471 return SUNMATRIX_CUSTOM;
472 };
473
474 J->ops->destroy = [](SUNMatrix A) {
475 if (A->content)
476 {
477 A->content = nullptr;
478 }
479 if (A->ops)
480 {
481 std::free(A->ops);
482 A->ops = nullptr;
483 }
484 std::free(A);
485 A = nullptr;
486 };
487
488 // Now set the linear system and Jacobian objects in the solver:
489 status = KINSetLinearSolver(kinsol_mem, LS, J);
490 AssertKINSOL(status);
491
492 // Finally, if we were given a set-up function, tell KINSOL about
493 // it as well. The manual says that this must happen *after*
494 // calling KINSetLinearSolver
495 if (!setup_jacobian)
496 setup_jacobian = [](const VectorType &, const VectorType &) {
497 return 0;
498 };
499 status = KINSetJacFn(
500 kinsol_mem,
501 [](N_Vector u,
502 N_Vector f,
503 SUNMatrix /* ignored */,
504 void *user_data,
505 N_Vector /* tmp1 */,
506 N_Vector /* tmp2 */) {
507 // Receive the object that describes the linear solver and
508 // unpack the pointer to the KINSOL object from which we can then
509 // get the 'setup' function.
510 const KINSOL<VectorType> &solver =
511 *static_cast<const KINSOL<VectorType> *>(user_data);
512
513 auto ycur = internal::unwrap_nvector_const<VectorType>(u);
514 auto fcur = internal::unwrap_nvector<VectorType>(f);
515
516 // Call the user-provided setup function with these arguments:
518 solver.setup_jacobian, solver.pending_exception, *ycur, *fcur);
519 });
520 AssertKINSOL(status);
521 }
522
523 // Right before calling the main KINSol() call, allow expert users to
524 // perform additional setup operations on the KINSOL object.
525 if (custom_setup)
526 custom_setup(kinsol_mem);
527
528
529 // Having set up all of the ancillary things, finally call the main KINSol
530 // function. Once we return, check what happened:
531 // - If we have a pending recoverable exception, ignore it if SUNDIAL's
532 // return code was zero -- in that case, SUNDIALS managed to indeed
533 // recover and we no longer need the exception
534 // - If we have any other exception, rethrow it
535 // - If no exception, test that SUNDIALS really did successfully return
536 //
537 // This all creates difficult exit paths from this function. We have to
538 // do some manual clean ups to get rid of the explicitly created
539 // temporary objects of this class. To avoid having to repeat the clean-up
540 // code on each exit path, we package it up and put the code into a
541 // ScopeExit object that is executed automatically on each such path
542 // out of this function.
543 Assert(pending_exception == nullptr, ExcInternalError());
544 status = KINSol(kinsol_mem, solution, data.strategy, u_scale, f_scale);
545
546 ScopeExit upon_exit([this, &J, &LS]() mutable {
547 if (J != nullptr)
548 SUNMatDestroy(J);
549 if (LS != nullptr)
550 SUNLinSolFree(LS);
551 KINFree(&kinsol_mem);
552 });
553
554 if (pending_exception)
555 {
556 try
557 {
558 std::rethrow_exception(pending_exception);
559 }
560 catch (const RecoverableUserCallbackError &exc)
561 {
562 pending_exception = nullptr;
563 if (status == 0)
564 /* just eat the exception */;
565 else
566 throw;
567 }
568 catch (...)
569 {
570 pending_exception = nullptr;
571 throw;
572 }
573 }
574 // It is of course also possible that KINSOL experienced
575 // convergence issues even if the user-side callbacks
576 // succeeded. In that case, we also want to throw an exception
577 // that can be caught by the user -- whether that's actually
578 // useful to determine a different course of action (i.e., whether
579 // the user side can do something to recover the ability to
580 // converge) is a separate matter that we need not decide
581 // here. (One could imagine this happening in a time or load
582 // stepping procedure where re-starting with a smaller time step
583 // or load step could help.)
584 AssertThrow(status >= 0, ExcKINSOLError(status));
585
586 long nniters;
587 status = KINGetNumNonlinSolvIters(kinsol_mem, &nniters);
588 AssertKINSOL(status);
589
590 return static_cast<unsigned int>(nniters);
591 }
592
593
594
595 template <typename VectorType>
596 void
598 {
599 reinit_vector = [](VectorType &) {
600 AssertThrow(false, ExcFunctionNotProvided("reinit_vector"));
601 };
602 }
603
604 template class KINSOL<Vector<double>>;
605 template class KINSOL<BlockVector<double>>;
606
609
610# ifdef DEAL_II_WITH_MPI
611
612# ifdef DEAL_II_TRILINOS_WITH_EPETRA
615# endif
616
617# ifdef DEAL_II_TRILINOS_WITH_TPETRA
618 template class KINSOL<
620 template class KINSOL<
622
623 template class KINSOL<
625 template class KINSOL<
627# endif
628
629
630# ifdef DEAL_II_WITH_PETSC
631# ifndef PETSC_USE_COMPLEX
634# endif
635# endif
636
637# endif
638
639} // namespace SUNDIALS
640
641
642#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 enter_subsection(const std::string &subsection, const bool create_path_if_needed=true)
void add_action(const std::string &entry, const std::function< void(const std::string &value)> &action, const bool execute_action=true)
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, const OrthogonalizationStrategy anderson_qr_orthogonalization=modified_gram_schmidt)
Definition kinsol.cc:68
void add_parameters(ParameterHandler &prm)
Definition kinsol.cc:97
void set_functions_to_trigger_an_assert()
Definition kinsol.cc:597
MPI_Comm mpi_communicator
Definition kinsol.h:730
unsigned int solve(VectorType &initial_guess_and_solution)
Definition kinsol.cc:231
SUNContext kinsol_ctx
Definition kinsol.h:741
std::function< void(const VectorType &src, VectorType &dst)> residual
Definition kinsol.h:493
std::function< void(const VectorType &src, VectorType &dst)> iteration_function
Definition kinsol.h:509
std::exception_ptr pending_exception
Definition kinsol.h:755
std::function< void(const VectorType &rhs, VectorType &dst, const double tolerance)> solve_with_jacobian
Definition kinsol.h:601
AdditionalData data
Definition kinsol.h:725
KINSOL(const AdditionalData &data=AdditionalData())
Definition kinsol.cc:171
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_SUNDIALS_VERSION_GTE(major, minor, patch)
Definition config.h:532
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_ASSERT_UNREACHABLE()
#define AssertKINSOL(code)
static ::ExceptionBase & ExcFunctionNotProvided(std::string arg1)
#define Assert(cond, exc)
static ::ExceptionBase & ExcKINSOLError(int arg1)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & RecoverableUserCallbackError()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
std::vector< index_type > data
Definition mpi.cc:734
constexpr char A
Tpetra::Vector< Number, LO, GO, NodeType< MemorySpace > > VectorType
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
int call_and_possibly_capture_exception(const F &f, std::exception_ptr &eptr, Args &&...args)
Definition utilities.h:48
::sunrealtype realtype