deal.II version GIT relicensing-6834-g5b78e6bcdf 2026-10-01 11:20: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
ida.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#include <deal.II/base/config.h>
14
16
18
19#ifdef DEAL_II_WITH_SUNDIALS
21
23
26# ifdef DEAL_II_WITH_TRILINOS
29# endif
30# ifdef DEAL_II_WITH_PETSC
33# endif
34
37
38# include <idas/idas.h>
39# if DEAL_II_SUNDIALS_VERSION_GTE(6, 0, 0)
40# include <sundials/sundials_context.h>
41# endif
42
43# include <iomanip>
44# include <iostream>
45
46
47#endif // DEAL_II_WITH_SUNDIALS
48
50
51#ifdef DEAL_II_WITH_SUNDIALS
52
53namespace SUNDIALS
54{
55 template <typename VectorType>
57 : IDA(data, MPI_COMM_SELF)
58 {}
59
60
61
62 template <typename VectorType>
64 : data(data)
65 , ida_mem(nullptr)
67 , ida_ctx(nullptr)
68# endif
69 , mpi_communicator(mpi_comm)
70 , pending_exception(nullptr)
71 {
73
74 // SUNDIALS will always duplicate communicators if we provide them. This
75 // can cause problems if SUNDIALS is configured with MPI and we pass along
76 // MPI_COMM_SELF in a serial application as MPI won't be
77 // initialized. Hence, work around that by just not providing a
78 // communicator in that case.
79# if DEAL_II_SUNDIALS_VERSION_GTE(7, 0, 0)
80 const int status =
81 SUNContext_Create(mpi_communicator == MPI_COMM_SELF ? SUN_COMM_NULL :
83 &ida_ctx);
84 (void)status;
85 AssertIDA(status);
86# elif DEAL_II_SUNDIALS_VERSION_GTE(6, 0, 0)
87 const int status =
88 SUNContext_Create(mpi_communicator == MPI_COMM_SELF ? nullptr :
90 &ida_ctx);
91 (void)status;
92 AssertIDA(status);
93# endif
94 }
95
96
97
98 template <typename VectorType>
100 {
101 IDAFree(&ida_mem);
102# if DEAL_II_SUNDIALS_VERSION_GTE(6, 0, 0)
103 const int status = SUNContext_Free(&ida_ctx);
104 (void)status;
105 AssertIDA(status);
106# endif
107
108 Assert(pending_exception == nullptr, ExcInternalError());
109 }
110
111
112
113 template <typename VectorType>
114 unsigned int
115 IDA<VectorType>::solve_dae(VectorType &solution, VectorType &solution_dot)
116 {
117 double t = data.initial_time;
118 double h = data.initial_step_size;
119 unsigned int step_number = 0;
120
121 int status;
122 (void)status;
123
124 reset(data.initial_time, data.initial_step_size, solution, solution_dot);
125
126 // The solution is stored in
127 // solution. Here we take only a
128 // view of it.
129
130 double next_time = data.initial_time;
131
132 output_step(0, solution, solution_dot, 0);
133
134 while (t < data.final_time)
135 {
136 next_time += data.output_period;
137
138 auto yy = internal::make_nvector_view(solution
139# if !DEAL_II_SUNDIALS_VERSION_LT(6, 0, 0)
140 ,
141 ida_ctx
142# endif
143 );
144 auto yp = internal::make_nvector_view(solution_dot
145# if !DEAL_II_SUNDIALS_VERSION_LT(6, 0, 0)
146 ,
147 ida_ctx
148# endif
149 );
150
151 // Execute time steps. If we ended up with a pending exception,
152 // see if it was recoverable; if it was, and if IDA recovered,
153 // just continue on. If IDA did not recover, rethrow the exception.
154 // Do the same if the exception was not recoverable.
155 status = IDASolve(ida_mem, next_time, &t, yy, yp, IDA_NORMAL);
156 if (pending_exception)
157 {
158 try
159 {
160 std::rethrow_exception(pending_exception);
161 }
162 catch (const RecoverableUserCallbackError &exc)
163 {
164 pending_exception = nullptr;
165 if (status == 0)
166 /* just eat the exception and continue */;
167 else
168 throw;
169 }
170 catch (...)
171 {
172 pending_exception = nullptr;
173 throw;
174 }
175 }
176 AssertIDA(status);
177
178 status = IDAGetLastStep(ida_mem, &h);
179 AssertIDA(status);
180
181 while (solver_should_restart(t, solution, solution_dot))
182 reset(t, h, solution, solution_dot);
183
184 ++step_number;
185
186 output_step(t, solution, solution_dot, step_number);
187 }
188 long int n_steps;
189 status = IDAGetNumSteps(ida_mem, &n_steps);
190 AssertIDA(status);
191 return n_steps;
192 }
193
194
195
196 template <typename VectorType>
197 void
198 IDA<VectorType>::reset(const double current_time,
199 const double current_time_step,
200 VectorType &solution,
201 VectorType &solution_dot)
202 {
203 bool first_step = (current_time == data.initial_time);
204
205 int status;
206 (void)status;
207
208# if DEAL_II_SUNDIALS_VERSION_GTE(7, 0, 0)
209 status = SUNContext_Free(&ida_ctx);
210 AssertIDA(status);
211
212 // Same comment applies as in class constructor:
213 status =
214 SUNContext_Create(mpi_communicator == MPI_COMM_SELF ? SUN_COMM_NULL :
215 mpi_communicator,
216 &ida_ctx);
217 AssertIDA(status);
218# elif DEAL_II_SUNDIALS_VERSION_GTE(6, 0, 0)
219 status = SUNContext_Free(&ida_ctx);
220 AssertIDA(status);
221
222 // Same comment applies as in class constructor:
223 status =
224 SUNContext_Create(mpi_communicator == MPI_COMM_SELF ? nullptr :
225 &mpi_communicator,
226 &ida_ctx);
227 AssertIDA(status);
228# endif
229
230 if (ida_mem)
231 {
232 IDAFree(&ida_mem);
233 // Initialization is version-dependent: do that in a moment
234 }
235
236# if DEAL_II_SUNDIALS_VERSION_LT(6, 0, 0)
237 ida_mem = IDACreate();
238# else
239 ida_mem = IDACreate(ida_ctx);
240# endif
241
242 auto yy = internal::make_nvector_view(solution
243# if !DEAL_II_SUNDIALS_VERSION_LT(6, 0, 0)
244 ,
245 ida_ctx
246# endif
247 );
248 auto yp = internal::make_nvector_view(solution_dot
249# if !DEAL_II_SUNDIALS_VERSION_LT(6, 0, 0)
250 ,
251 ida_ctx
252# endif
253 );
254
255 status = IDAInit(
256 ida_mem,
257 [](SUNDIALS::realtype tt,
258 N_Vector yy,
259 N_Vector yp,
260 N_Vector rr,
261 void *user_data) -> int {
262 IDA<VectorType> &solver = *static_cast<IDA<VectorType> *>(user_data);
263
264 auto *src_yy = internal::unwrap_nvector_const<VectorType>(yy);
265 auto *src_yp = internal::unwrap_nvector_const<VectorType>(yp);
266 auto *residual = internal::unwrap_nvector<VectorType>(rr);
267
269 solver.residual,
270 solver.pending_exception,
271 tt,
272 *src_yy,
273 *src_yp,
274 *residual);
275 },
276 current_time,
277 yy,
278 yp);
279 AssertIDA(status);
280 if (get_local_tolerances)
281 {
282 const auto abs_tols = internal::make_nvector_view(get_local_tolerances()
283# if !DEAL_II_SUNDIALS_VERSION_LT(6, 0, 0)
284 ,
285 ida_ctx
286# endif
287 );
288 status = IDASVtolerances(ida_mem, data.relative_tolerance, abs_tols);
289 AssertIDA(status);
290 }
291 else
292 {
293 status = IDASStolerances(ida_mem,
294 data.relative_tolerance,
295 data.absolute_tolerance);
296 AssertIDA(status);
297 }
298
299 status = IDASetInitStep(ida_mem, current_time_step);
300 AssertIDA(status);
301
302 status = IDASetUserData(ida_mem, this);
303 AssertIDA(status);
304
305 if (data.ic_type == AdditionalData::use_y_diff ||
306 data.reset_type == AdditionalData::use_y_diff ||
307 data.ignore_algebraic_terms_for_errors)
308 {
309 VectorType diff_comp_vector(solution);
310 diff_comp_vector = 0.0;
311 for (const auto &component : differential_components())
312 diff_comp_vector[component] = 1.0;
313 diff_comp_vector.compress(VectorOperation::insert);
314
315 const auto diff_id = internal::make_nvector_view(diff_comp_vector
316# if !DEAL_II_SUNDIALS_VERSION_LT(6, 0, 0)
317 ,
318 ida_ctx
319# endif
320 );
321 status = IDASetId(ida_mem, diff_id);
322 AssertIDA(status);
323 }
324
325 status = IDASetSuppressAlg(ida_mem, data.ignore_algebraic_terms_for_errors);
326 AssertIDA(status);
327
328 // status = IDASetMaxNumSteps(ida_mem, max_steps);
329 status = IDASetStopTime(ida_mem, data.final_time);
330 AssertIDA(status);
331
332 status = IDASetMaxNonlinIters(ida_mem, data.maximum_non_linear_iterations);
333 AssertIDA(status);
334
335 // Initialize solver
336 SUNMatrix J = nullptr;
337 SUNLinearSolver LS = nullptr;
338
339 // and attach it to the SUNLinSol object. The functions that will get
340 // called do not actually receive the IDAMEM object, just the LS
341 // object, so we have to store a pointer to the current
342 // object in the LS object
343# if DEAL_II_SUNDIALS_VERSION_LT(6, 0, 0)
344 LS = SUNLinSolNewEmpty();
345# else
346 LS = SUNLinSolNewEmpty(ida_ctx);
347# endif
348
349 LS->content = this;
350
351 LS->ops->gettype = [](SUNLinearSolver /*ignored*/) -> SUNLinearSolver_Type {
352 return SUNLINEARSOLVER_MATRIX_ITERATIVE;
353 };
354
355 LS->ops->free = [](SUNLinearSolver LS) -> int {
356 if (LS->content)
357 {
358 LS->content = nullptr;
359 }
360 if (LS->ops)
361 {
362 std::free(LS->ops);
363 LS->ops = nullptr;
364 }
365 std::free(LS);
366 LS = nullptr;
367 return 0;
368 };
369
370 AssertThrow(solve_with_jacobian,
371 ExcFunctionNotProvided("solve_with_jacobian"));
372 LS->ops->solve = [](SUNLinearSolver LS,
373 SUNMatrix /*ignored*/,
374 N_Vector x,
375 N_Vector b,
376 SUNDIALS::realtype tol) -> int {
377 IDA<VectorType> &solver = *static_cast<IDA<VectorType> *>(LS->content);
378
379 auto *src_b = internal::unwrap_nvector_const<VectorType>(b);
380 auto *dst_x = internal::unwrap_nvector<VectorType>(x);
382 solver.solve_with_jacobian,
383 solver.pending_exception,
384 *src_b,
385 *dst_x,
386 tol);
387 };
388
389 // When we set an iterative solver IDA requires that resid is provided. From
390 // SUNDIALS docs If an iterative method computes the preconditioned initial
391 // residual and returns with a successful solve without performing any
392 // iterations (i.e., either the initial guess or the preconditioner is
393 // sufficiently accurate), then this optional routine may be called by the
394 // SUNDIALS package. This routine should return the N_Vector containing the
395 // preconditioned initial residual vector.
396 LS->ops->resid = [](SUNLinearSolver /*ignored*/) -> N_Vector {
397 return nullptr;
398 };
399 // When we set an iterative solver IDA requires that last number of
400 // iteration is provided. Since we can't know what kind of solver the user
401 // has provided we set 1. This is clearly suboptimal.
402 LS->ops->numiters = [](SUNLinearSolver /*ignored*/) -> int { return 1; };
403 // Even though we don't use it, IDA still wants us to set some
404 // kind of matrix object for the nonlinear solver. This is because
405 // if we don't set it, it won't call the functions that set up
406 // the matrix object (i.e., the argument to the 'IDASetJacFn'
407 // function below).
408# if DEAL_II_SUNDIALS_VERSION_LT(6, 0, 0)
409 J = SUNMatNewEmpty();
410# else
411 J = SUNMatNewEmpty(ida_ctx);
412# endif
413 J->content = this;
414
415 J->ops->getid = [](SUNMatrix /*ignored*/) -> SUNMatrix_ID {
416 return SUNMATRIX_CUSTOM;
417 };
418
419 J->ops->destroy = [](SUNMatrix A) {
420 if (A->content)
421 {
422 A->content = nullptr;
423 }
424 if (A->ops)
425 {
426 std::free(A->ops);
427 A->ops = nullptr;
428 }
429 std::free(A);
430 A = nullptr;
431 };
432
433 // Now set the linear system and Jacobian objects in the solver:
434 status = IDASetLinearSolver(ida_mem, LS, J);
435 AssertIDA(status);
436
437 status = IDASetLSNormFactor(ida_mem, data.ls_norm_factor);
438 AssertIDA(status);
439 // Finally tell IDA about
440 // it as well. The manual says that this must happen *after*
441 // calling IDASetLinearSolver
442 status = IDASetJacFn(
443 ida_mem,
444 [](SUNDIALS::realtype tt,
446 N_Vector yy,
447 N_Vector yp,
448 N_Vector /* residual */,
449 SUNMatrix /* ignored */,
450 void *user_data,
451 N_Vector /* tmp1 */,
452 N_Vector /* tmp2 */,
453 N_Vector /* tmp3 */) -> int {
454 Assert(user_data != nullptr, ExcInternalError());
455 IDA<VectorType> &solver = *static_cast<IDA<VectorType> *>(user_data);
456
457 auto *src_yy = internal::unwrap_nvector_const<VectorType>(yy);
458 auto *src_yp = internal::unwrap_nvector_const<VectorType>(yp);
459
461 solver.setup_jacobian,
462 solver.pending_exception,
463 tt,
464 *src_yy,
465 *src_yp,
466 cj);
467 });
468 AssertIDA(status);
469 status = IDASetMaxOrd(ida_mem, data.maximum_order);
470 AssertIDA(status);
471
473 if (first_step)
474 type = data.ic_type;
475 else
476 type = data.reset_type;
477
478 status =
479 IDASetMaxNumItersIC(ida_mem, data.maximum_non_linear_iterations_ic);
480 AssertIDA(status);
481
482 if (type == AdditionalData::use_y_dot)
483 {
484 // (re)initialization of the vectors
485 status =
486 IDACalcIC(ida_mem, IDA_Y_INIT, current_time + current_time_step);
487 AssertIDA(status);
488
489 status = IDAGetConsistentIC(ida_mem, yy, yp);
490 AssertIDA(status);
491 }
492 else if (type == AdditionalData::use_y_diff)
493 {
494 status =
495 IDACalcIC(ida_mem, IDA_YA_YDP_INIT, current_time + current_time_step);
496 AssertIDA(status);
497
498 status = IDAGetConsistentIC(ida_mem, yy, yp);
499 AssertIDA(status);
500 }
501 }
502
503 template <typename VectorType>
504 void
506 {
507 reinit_vector = [](VectorType &) {
508 AssertThrow(false, ExcFunctionNotProvided("reinit_vector"));
509 };
510
511 residual = [](const double,
512 const VectorType &,
513 const VectorType &,
514 VectorType &) -> int {
515 int ret = 0;
516 AssertThrow(false, ExcFunctionNotProvided("residual"));
517 return ret;
518 };
519
520
521 output_step = [](const double,
522 const VectorType &,
523 const VectorType &,
524 const unsigned int) { return; };
525
526 solver_should_restart =
527 [](const double, VectorType &, VectorType &) -> bool { return false; };
528
529 differential_components = [&]() -> IndexSet {
531 typename VectorMemory<VectorType>::Pointer v(mem);
532 reinit_vector(*v);
533 return v->locally_owned_elements();
534 };
535 }
536
537 template class IDA<Vector<double>>;
538 template class IDA<BlockVector<double>>;
539
540# ifdef DEAL_II_WITH_MPI
541
542# ifdef DEAL_II_WITH_TRILINOS
545# endif // DEAL_II_WITH_TRILINOS
546
547# ifdef DEAL_II_WITH_PETSC
548# ifndef PETSC_USE_COMPLEX
549 template class IDA<PETScWrappers::MPI::Vector>;
551# endif // PETSC_USE_COMPLEX
552# endif // DEAL_II_WITH_PETSC
553
554# endif // DEAL_II_WITH_MPI
555
556} // namespace SUNDIALS
557
558
559#endif // DEAL_II_WITH_SUNDIALS
*const unsigned int n_steps
std::function< void(const VectorType &rhs, VectorType &dst, const double tolerance)> solve_with_jacobian
Definition ida.h:992
unsigned int solve_dae(VectorType &solution, VectorType &solution_dot)
Definition ida.cc:115
MPI_Comm mpi_communicator
Definition ida.h:1103
std::function< void(const double t, const VectorType &y, const VectorType &y_dot, const double alpha)> setup_jacobian
Definition ida.h:947
void set_functions_to_trigger_an_assert()
Definition ida.cc:505
void reset(const double t, const double h, VectorType &y, VectorType &yp)
Definition ida.cc:198
std::function< void(const double t, const VectorType &y, const VectorType &y_dot, VectorType &res)> residual
Definition ida.h:911
IDA(const AdditionalData &data=AdditionalData())
Definition ida.cc:56
SUNContext ida_ctx
Definition ida.h:1096
std::exception_ptr pending_exception
Definition ida.h:1115
#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_SUNDIALS_VERSION_LT(major, minor, patch)
Definition config.h:539
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define AssertIDA(code)
static ::ExceptionBase & ExcFunctionNotProvided(std::string arg1)
#define Assert(cond, exc)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & RecoverableUserCallbackError()
#define AssertThrow(cond, exc)
std::vector< index_type > data
Definition mpi.cc:734
int call_and_possibly_capture_exception(const F &f, std::exception_ptr &eptr, Args &&...args)
Definition utilities.h:48
::sunrealtype realtype