Block-Structured AMR Software Framework
Loading...
Searching...
No Matches
AMReX_SundialsIntegrator.H
Go to the documentation of this file.
1#ifndef AMREX_SUNDIALS_INTEGRATOR_H
2#define AMREX_SUNDIALS_INTEGRATOR_H
3
4#include <functional>
5#include <utility>
6
7#include <AMReX_Config.H>
8#include <AMReX_REAL.H>
9#include <AMReX_Vector.H>
10#include <AMReX_ParmParse.H>
13#include <AMReX_Sundials.H>
14
15#include <nvector/nvector_manyvector.h>
16#include <sunnonlinsol/sunnonlinsol_fixedpoint.h>
17#include <sunlinsol/sunlinsol_spgmr.h>
18#include <arkode/arkode_arkstep.h>
19#include <arkode/arkode_mristep.h>
20
28namespace amrex {
29
37 std::function<int(amrex::Real, N_Vector, N_Vector, void*)> f;
38 std::function<int(amrex::Real, N_Vector, N_Vector, void*)> fi;
39 std::function<int(amrex::Real, N_Vector, N_Vector, void*)> fe;
40 std::function<int(amrex::Real, N_Vector, N_Vector, void*)> ff;
41 std::function<int(amrex::Real, N_Vector, void*)> post_stage;
42 std::function<int(amrex::Real, N_Vector, void*)> post_step;
43 std::function<int(amrex::Real, N_Vector, void*)> post_fast_stage;
44 std::function<int(amrex::Real, N_Vector, void*)> post_fast_step;
45};
46
47namespace SundialsUserFun {
48 static int f (amrex::Real t, N_Vector y_data, N_Vector y_rhs, void *user_data) {
49 SundialsUserData* udata = static_cast<SundialsUserData*>(user_data);
50 return udata->f(t, y_data, y_rhs, user_data);
51 }
52
53 static int fi (amrex::Real t, N_Vector y_data, N_Vector y_rhs, void *user_data) {
54 SundialsUserData* udata = static_cast<SundialsUserData*>(user_data);
55 return udata->fi(t, y_data, y_rhs, user_data);
56 }
57
58 static int fe (amrex::Real t, N_Vector y_data, N_Vector y_rhs, void *user_data) {
59 SundialsUserData* udata = static_cast<SundialsUserData*>(user_data);
60 return udata->fe(t, y_data, y_rhs, user_data);
61 }
62
63 static int ff (amrex::Real t, N_Vector y_data, N_Vector y_rhs, void *user_data) {
64 SundialsUserData* udata = static_cast<SundialsUserData*>(user_data);
65 return udata->ff(t, y_data, y_rhs, user_data);
66 }
67
68 static int post_stage (amrex::Real t, N_Vector y_data, void *user_data) {
69 SundialsUserData* udata = static_cast<SundialsUserData*>(user_data);
70 return udata->post_stage(t, y_data, user_data);
71 }
72
73 static int post_step (amrex::Real t, N_Vector y_data, void *user_data) {
74 SundialsUserData* udata = static_cast<SundialsUserData*>(user_data);
75 return udata->post_step(t, y_data, user_data);
76 }
77
78 static int post_fast_stage (amrex::Real t, N_Vector y_data, void *user_data) {
79 SundialsUserData* udata = static_cast<SundialsUserData*>(user_data);
80 return udata->post_fast_stage(t, y_data, user_data);
81 }
82
83 static int post_fast_step (amrex::Real t, N_Vector y_data, void *user_data) {
84 SundialsUserData* udata = static_cast<SundialsUserData*>(user_data);
85 return udata->post_fast_step(t, y_data, user_data);
86 }
87}
88
94template<class T>
96{
97private:
99
100 // Method type: ERK, DIRK, IMEX-RK, EX-MRI, IM-MRI, IMEX-MRI
101 std::string type = "ERK";
102
103 // Use SUNDIALS default methods
104 std::string method = "DEFAULT"; // ERK, DIRK, or slow method with MRI
105 std::string method_e = "DEFAULT"; // ERK in IMEX-RK
106 std::string method_i = "DEFAULT"; // DIRK in IMEX-RK
107
108 // Fast method type (ERK or DIRK) and method
109 std::string fast_type = "ERK";
110 std::string fast_method = "DEFAULT";
111
112 // Nonlinear solver
113 std::string nonlinear_solver = "Newton";
114 int max_nonlinear_iters = 0;
115
116 std::string fast_nonlinear_solver = "Newton";
117 int fast_max_nonlinear_iters = 0;
118
119 // Linear solver
120 std::string linear_solver = "GMRES";
121 int max_linear_iters = 0;
122
123 std::string fast_linear_solver = "GMRES";
124 int fast_max_linear_iters = 0;
125
126 // SUNDIALS package flags, set based on type
127 bool use_ark = false;
128 bool use_mri = false;
129
130 // structure for interfacing with user-supplied functions
131 SundialsUserData udata;
132
133 // SUNDIALS context
134 //
135 // We should probably use context created by amrex:sundials::Initialize but
136 // that context is not MPI-aware
137 ::sundials::Context sunctx;
138
139 // Single rate or slow time scale
140 void *arkode_mem = nullptr;
141 SUNLinearSolver LS = nullptr;
142 SUNNonlinearSolver NLS = nullptr;
143
144 // Fast time scale
145 void *arkode_fast_mem = nullptr;
146 MRIStepInnerStepper fast_stepper = nullptr;
147 SUNLinearSolver fast_LS = nullptr;
148 SUNNonlinearSolver fast_NLS = nullptr;
149
150 // Integrator stop time
151 bool set_stop_time = false;
152 amrex::Real stop_time = 0.0;
153
154 // Max steps between returns
155 amrex::Long max_num_steps = 0;
156
158 void free_memory ()
159 {
160 if (use_mri) {
161 MRIStepInnerStepper_Free(&fast_stepper);
162 ARKStepFree(&arkode_fast_mem);
163 MRIStepFree(&arkode_mem);
164 } else if (use_ark) {
165 ARKStepFree(&arkode_mem);
166 }
167 SUNLinSolFree(LS);
168 LS = nullptr;
169 SUNLinSolFree(fast_LS);
170 fast_LS = nullptr;
171 SUNNonlinSolFree(NLS);
172 NLS = nullptr;
173 SUNNonlinSolFree(fast_NLS);
174 fast_NLS = nullptr;
175 use_ark = false;
176 use_mri = false;
177 }
178
179 void initialize_parameters ()
180 {
181 amrex::ParmParse pp("integration.sundials");
182
183 pp.query("type", type);
184 pp.query("method", method);
185 pp.query("method_e", method_e);
186 pp.query("method_i", method_i);
187
188 pp.query("fast_type", fast_type);
189 pp.query("fast_method", fast_method);
190
191 if (type == "ERK" || type == "DIRK" || type == "IMEX-RK") {
192 use_ark = true;
193 }
194 else if (type == "EX-MRI" || type == "IM-MRI" || type == "IMEX-MRI") {
195 use_mri = true;
196 }
197 else {
198 std::string msg("Unknown method type: ");
199 msg += type;
200 amrex::Error(msg.c_str());
201 }
202
203 pp.query("nonlinear_solver", nonlinear_solver);
204 pp.query("max_nonlinear_iters", max_nonlinear_iters);
205
206 pp.query("fast_nonlinear_solver", fast_nonlinear_solver);
207 pp.query("fast_max_nonlinear_iters", fast_max_nonlinear_iters);
208
209 pp.query("linear_solver", linear_solver);
210 pp.query("max_linear_iters", max_linear_iters);
211
212 pp.query("fast_linear_solver", fast_linear_solver);
213 pp.query("fast_max_linear_iters", fast_max_linear_iters);
214
215 set_stop_time = pp.query("stop_time", stop_time);
216
217 pp.query("max_num_steps", max_num_steps);
218 }
219
220 void SetupRK (amrex::Real time, N_Vector y_data)
221 {
222 if (amrex::Verbose()) { amrex::Print() << "Using SUNDIALS time integrator\n"; }
223 int flag = 0;
224
225 // Create integrator and select method
226 if (type == "ERK") {
227 if (amrex::Verbose()) { amrex::Print() << "ERK method: " << method << "\n"; }
228 arkode_mem = ARKStepCreate(SundialsUserFun::f, nullptr, time, y_data, sunctx);
229 AMREX_ALWAYS_ASSERT(arkode_mem != nullptr);
230 if (method != "DEFAULT") {
231 flag = ARKStepSetTableName(arkode_mem, "ARKODE_DIRK_NONE", method.c_str());
232 AMREX_ALWAYS_ASSERT(flag == 0);
233 }
234 }
235 else if (type == "DIRK") {
236 if (amrex::Verbose()) { amrex::Print() << "DIRK method: " << method << "\n"; }
237 arkode_mem = ARKStepCreate(nullptr, SundialsUserFun::f, time, y_data, sunctx);
238 AMREX_ALWAYS_ASSERT(arkode_mem != nullptr);
239 if (method != "DEFAULT") {
240 flag = ARKStepSetTableName(arkode_mem, method.c_str(), "ARKODE_ERK_NONE");
241 AMREX_ALWAYS_ASSERT(flag == 0);
242 }
243 }
244 else if (type == "IMEX-RK") {
245 if (amrex::Verbose()) { amrex::Print() << "IMEX-RK method: " << method_i << " and "
246 << method_e << "\n"; }
247 arkode_mem = ARKStepCreate(SundialsUserFun::fe, SundialsUserFun::fi, time, y_data, sunctx);
248 AMREX_ALWAYS_ASSERT(arkode_mem != 0);
249 if (method_e != "DEFAULT" && method_i != "DEFAULT")
250 {
251 flag = ARKStepSetTableName(arkode_mem, method_i.c_str(), method_e.c_str());
252 AMREX_ALWAYS_ASSERT(flag == 0);
253 }
254 }
255
256 // Attach structure with user-supplied function wrappers
257 flag = ARKStepSetUserData(arkode_mem, &udata);
258 AMREX_ALWAYS_ASSERT(flag == 0);
259
260 // Set integrator tolerances
261 if (BaseT::use_adaptive_time_step || type == "DIRK" || type == "IMEX-RK") {
262 if (amrex::Verbose()) {
263 amrex::Print() << "Relative tolerance: " << BaseT::rel_tol << "\n";
264 amrex::Print() << "Absolute tolerance: " << BaseT::abs_tol << "\n";
265 }
266 flag = ARKStepSStolerances(arkode_mem, BaseT::rel_tol, BaseT::abs_tol);
267 AMREX_ALWAYS_ASSERT(flag == 0);
268 }
269
270 // Create and attach linear solver for implicit methods
271 if (type == "DIRK" || type == "IMEX-RK") {
272 if (amrex::Verbose()) {
273 amrex::Print() << "Nonlinear solver: " << nonlinear_solver << "\n";
274 amrex::Print() << "Max nonlinear iters: " << max_nonlinear_iters << "\n";
275 }
276 if (nonlinear_solver == "fixed-point") {
277 NLS = SUNNonlinSol_FixedPoint(y_data, 0, sunctx);
278 AMREX_ALWAYS_ASSERT(NLS != nullptr);
279 flag = ARKStepSetNonlinearSolver(arkode_mem, NLS);
280 AMREX_ALWAYS_ASSERT(flag == 0);
281 }
282 flag = ARKStepSetMaxNonlinIters(arkode_mem, max_nonlinear_iters);
283 AMREX_ALWAYS_ASSERT(flag == 0);
284
285 if (nonlinear_solver == "Newton") {
286 if (amrex::Verbose()) {
287 amrex::Print() << "Linear solver: " << linear_solver << "\n";
288 amrex::Print() << "Max linear iters: " << max_linear_iters << "\n";
289 }
290 LS = SUNLinSol_SPGMR(y_data, SUN_PREC_NONE, max_linear_iters, sunctx);
291 AMREX_ALWAYS_ASSERT(LS != nullptr);
292 flag = ARKStepSetLinearSolver(arkode_mem, LS, nullptr);
293 AMREX_ALWAYS_ASSERT(flag == 0);
294 }
295 }
296
297 // Set post stage and step function
298 flag = ARKStepSetPostprocessStageFn(arkode_mem, SundialsUserFun::post_stage);
299 AMREX_ALWAYS_ASSERT(flag == 0);
300 flag = ARKStepSetPostprocessStepFn(arkode_mem, SundialsUserFun::post_step);
301 AMREX_ALWAYS_ASSERT(flag == 0);
302
303 // Set a stop time
304 if (set_stop_time) {
305 if (amrex::Verbose()) { amrex::Print() << "Stop time: " << stop_time << "\n"; }
306 flag = ARKStepSetStopTime(arkode_mem, stop_time);
307 AMREX_ALWAYS_ASSERT(flag == 0);
308 }
309
310 // Set max number of steps between returns
311 flag = ARKStepSetMaxNumSteps(arkode_mem, max_num_steps);
312 AMREX_ALWAYS_ASSERT(flag == 0);
313 }
314
315 void SetupMRI (amrex::Real time, N_Vector y_data)
316 {
317 if (amrex::Verbose()) { amrex::Print() << "Using SUNDIALS multirate time integrator\n"; }
318 int flag = 0;
319
320 // Create the fast integrator and select method
321 if (fast_type == "ERK") {
322 if (amrex::Verbose()) { amrex::Print() << "Fast ERK method: " << fast_method << "\n"; }
323 arkode_fast_mem = ARKStepCreate(SundialsUserFun::ff, nullptr, time, y_data, sunctx);
324 AMREX_ALWAYS_ASSERT(arkode_fast_mem != nullptr);
325 if (fast_method != "DEFAULT") {
326 flag = ARKStepSetTableName(arkode_fast_mem, "ARKODE_DIRK_NONE", fast_method.c_str());
327 AMREX_ALWAYS_ASSERT(flag == 0);
328 }
329 }
330 else if (fast_type == "DIRK") {
331 if (amrex::Verbose()) { amrex::Print() << "Fast DIRK method: " << fast_method << "\n"; }
332 arkode_fast_mem = ARKStepCreate(nullptr, SundialsUserFun::ff, time, y_data, sunctx);
333 AMREX_ALWAYS_ASSERT(arkode_fast_mem != nullptr);
334 if (fast_method != "DEFAULT") {
335 flag = ARKStepSetTableName(arkode_fast_mem, fast_method.c_str(), "ARKODE_ERK_NONE");
336 AMREX_ALWAYS_ASSERT(flag == 0);
337 }
338
339 if (amrex::Verbose()) {
340 amrex::Print() << "Fast nonlinear solver: " << fast_nonlinear_solver << "\n";
341 amrex::Print() << "Fast max nonlinear iters: " << fast_max_nonlinear_iters << "\n";
342 }
343 if (fast_nonlinear_solver == "fixed-point") {
344 fast_NLS = SUNNonlinSol_FixedPoint(y_data, 0, sunctx);
345 AMREX_ALWAYS_ASSERT(fast_NLS != nullptr);
346 flag = ARKStepSetNonlinearSolver(arkode_fast_mem, fast_NLS);
347 AMREX_ALWAYS_ASSERT(flag == 0);
348 }
349 flag = ARKStepSetMaxNonlinIters(arkode_fast_mem, fast_max_nonlinear_iters);
350 AMREX_ALWAYS_ASSERT(flag == 0);
351
352 if (fast_nonlinear_solver == "Newton") {
353 if (amrex::Verbose()) {
354 amrex::Print() << "Linear solver: " << fast_linear_solver << "\n";
355 amrex::Print() << "Max linear iters: " << fast_max_linear_iters << "\n";
356 }
357 fast_LS = SUNLinSol_SPGMR(y_data, SUN_PREC_NONE, fast_max_linear_iters, sunctx);
358 AMREX_ALWAYS_ASSERT(fast_LS != nullptr);
359 flag = ARKStepSetLinearSolver(arkode_fast_mem, fast_LS, nullptr);
360 AMREX_ALWAYS_ASSERT(flag == 0);
361 }
362 }
363
364 // Attach structure with user-supplied function wrappers
365 flag = ARKStepSetUserData(arkode_fast_mem, &udata);
366 AMREX_ALWAYS_ASSERT(flag == 0);
367
368 // Set integrator tolerances
369 if (BaseT::use_adaptive_fast_time_step || fast_type == "DIRK" || fast_type == "IMEX-RK") {
370 if (amrex::Verbose()) {
371 amrex::Print() << "Fast relative tolerance: " << BaseT::fast_rel_tol << "\n";
372 amrex::Print() << "Fast absolute tolerance: " << BaseT::fast_abs_tol << "\n";
373 }
374 flag = ARKStepSStolerances(arkode_fast_mem, BaseT::fast_rel_tol, BaseT::fast_abs_tol);
375 AMREX_ALWAYS_ASSERT(flag == 0);
376 }
377
378 // Set post stage and step function
379 flag = ARKStepSetPostprocessStageFn(arkode_fast_mem, SundialsUserFun::post_fast_stage);
380 AMREX_ALWAYS_ASSERT(flag == 0);
381 flag = ARKStepSetPostprocessStepFn(arkode_fast_mem, SundialsUserFun::post_fast_step);
382 AMREX_ALWAYS_ASSERT(flag == 0);
383
384 // Set max number of steps between returns
385 flag = ARKStepSetMaxNumSteps(arkode_fast_mem, max_num_steps);
386 AMREX_ALWAYS_ASSERT(flag == 0);
387
388 // Wrap fast integrator as an inner stepper
389 flag = ARKStepCreateMRIStepInnerStepper(arkode_fast_mem, &fast_stepper);
390 AMREX_ALWAYS_ASSERT(flag == 0);
391
392 // Create slow integrator
393 if (type == "EX-MRI") {
394 if (amrex::Verbose()) { amrex::Print() << "EX-MRI method: " << method << "\n"; }
395 arkode_mem = MRIStepCreate(SundialsUserFun::f, nullptr, time, y_data,
396 fast_stepper, sunctx);
397 AMREX_ALWAYS_ASSERT(arkode_mem != nullptr);
398 }
399 else if (type == "IM-MRI") {
400 if (amrex::Verbose()) { amrex::Print() << "IM-MRI method: " << method << "\n"; }
401 arkode_mem = MRIStepCreate(nullptr, SundialsUserFun::f, time, y_data,
402 fast_stepper, sunctx);
403 AMREX_ALWAYS_ASSERT(arkode_mem != nullptr);
404 }
405 else if (type == "IMEX-MRI") {
406 if (amrex::Verbose()) { amrex::Print() << "IMEX-MRI method: " << method << "\n"; }
407 arkode_mem = MRIStepCreate(SundialsUserFun::fe, SundialsUserFun::fi,
408 time, y_data, fast_stepper, sunctx);
409 AMREX_ALWAYS_ASSERT(arkode_mem != nullptr);
410 }
411
412 // Set method
413 if (method != "DEFAULT") {
414 MRIStepCoupling MRIC = MRIStepCoupling_LoadTableByName(method.c_str());
415 AMREX_ALWAYS_ASSERT(MRIC != nullptr);
416 flag = MRIStepSetCoupling(arkode_mem, MRIC);
417 AMREX_ALWAYS_ASSERT(flag == 0);
418 MRIStepCoupling_Free(MRIC);
419 }
420
421 // Attach structure with user-supplied function wrappers
422 flag = MRIStepSetUserData(arkode_mem, &udata);
423 AMREX_ALWAYS_ASSERT(flag == 0);
424
425 // Set integrator tolerances
426 if (BaseT::use_adaptive_time_step || type == "IM-MRI" || type == "IMEX-MRI") {
427 if (amrex::Verbose()) {
428 amrex::Print() << "Relative tolerance: " << BaseT::rel_tol << "\n";
429 amrex::Print() << "Absolute tolerance: " << BaseT::abs_tol << "\n";
430 }
431 flag = MRIStepSStolerances(arkode_mem, BaseT::rel_tol, BaseT::abs_tol);
432 AMREX_ALWAYS_ASSERT(flag == 0);
433 }
434
435 // Create and attach linear solver
436 if (type == "IM-MRI" || type == "IMEX-MRI") {
437 if (amrex::Verbose()) {
438 amrex::Print() << "Nonlinear solver: " << nonlinear_solver << "\n";
439 amrex::Print() << "Max nonlinear iters: " << max_nonlinear_iters << "\n";
440 }
441 if (nonlinear_solver == "fixed-point") {
442 NLS = SUNNonlinSol_FixedPoint(y_data, 0, sunctx);
443 AMREX_ALWAYS_ASSERT(NLS != nullptr);
444 flag = MRIStepSetNonlinearSolver(arkode_mem, NLS);
445 AMREX_ALWAYS_ASSERT(flag == 0);
446 }
447 flag = MRIStepSetMaxNonlinIters(arkode_mem, max_nonlinear_iters);
448 AMREX_ALWAYS_ASSERT(flag == 0);
449
450 if (nonlinear_solver == "Newton") {
451 if (amrex::Verbose()) {
452 amrex::Print() << "Linear solver: " << linear_solver << "\n";
453 amrex::Print() << "Max linear iters: " << max_linear_iters << "\n";
454 }
455 LS = SUNLinSol_SPGMR(y_data, SUN_PREC_NONE, max_linear_iters, sunctx);
456 AMREX_ALWAYS_ASSERT(LS != nullptr);
457 flag = MRIStepSetLinearSolver(arkode_mem, LS, nullptr);
458 AMREX_ALWAYS_ASSERT(flag == 0);
459 }
460 }
461
462 // Set post stage and step function
463 flag = MRIStepSetPostprocessStageFn(arkode_mem, SundialsUserFun::post_stage);
464 AMREX_ALWAYS_ASSERT(flag == 0);
465 flag = MRIStepSetPostprocessStepFn(arkode_mem, SundialsUserFun::post_step);
466 AMREX_ALWAYS_ASSERT(flag == 0);
467
468 // Set a stop time
469 if (set_stop_time) {
470 if (amrex::Verbose()) { amrex::Print() << "Stop time: " << stop_time << "\n"; }
471 flag = MRIStepSetStopTime(arkode_mem, stop_time);
472 AMREX_ALWAYS_ASSERT(flag == 0);
473 }
474
475 // Set max number of steps between returns
476 flag = MRIStepSetMaxNumSteps(arkode_mem, max_num_steps);
477 AMREX_ALWAYS_ASSERT(flag == 0);
478 }
479
480 // -------------------------------------
481 // Vector<MultiFab> / N_Vector Utilities
482 // -------------------------------------
483
484 // Utility to unpack a SUNDIALS ManyVector into a vector of MultiFabs
485 void unpack_vector (N_Vector y_data, amrex::Vector<amrex::MultiFab>& S_data)
486 {
487 const int num_vecs = N_VGetNumSubvectors_ManyVector(y_data);
488 S_data.resize(num_vecs);
489
490 for(int i = 0; i < num_vecs; i++)
491 {
492 S_data.at(i) = amrex::MultiFab(*amrex::sundials::getMFptr(N_VGetSubvector_ManyVector(y_data, i)),
494 0,
495 amrex::sundials::getMFptr(N_VGetSubvector_ManyVector(y_data, i))->nComp());
496 }
497 };
498
499 // Utility to wrap vector of MultiFabs as a SUNDIALS ManyVector
500 N_Vector wrap_data (amrex::Vector<amrex::MultiFab>& S_data)
501 {
502 auto get_length = [&](int index) -> sunindextype {
503 auto* p_mf = &S_data[index];
504 return p_mf->nComp() * (p_mf->boxArray()).numPts();
505 };
506
507 sunindextype NV_len = S_data.size();
508 N_Vector* NV_array = new N_Vector[NV_len];
509
510 for (int i = 0; i < NV_len; ++i) {
511 NV_array[i] = amrex::sundials::N_VMake_MultiFab(get_length(i),
512 &S_data[i], &sunctx);
513 }
514
515 N_Vector y_data = N_VNew_ManyVector(NV_len, NV_array, sunctx);
516
517 delete[] NV_array;
518
519 return y_data;
520 };
521
522 // Utility to wrap vector of MultiFabs as a SUNDIALS ManyVector
523 N_Vector copy_data (const amrex::Vector<amrex::MultiFab>& S_data)
524 {
525 auto get_length = [&](int index) -> sunindextype {
526 auto* p_mf = &S_data[index];
527 return p_mf->nComp() * (p_mf->boxArray()).numPts();
528 };
529
530 sunindextype NV_len = S_data.size();
531 N_Vector* NV_array = new N_Vector[NV_len];
532
533 for (int i = 0; i < NV_len; ++i) {
534 NV_array[i] = amrex::sundials::N_VNew_MultiFab(get_length(i),
535 S_data[i].boxArray(),
536 S_data[i].DistributionMap(),
537 S_data[i].nComp(),
538 S_data[i].nGrow(),
539 &sunctx);
540
542 S_data[i],
543 0,
544 0,
545 S_data[i].nComp(),
546 S_data[i].nGrow());
547 }
548
549 N_Vector y_data = N_VNew_ManyVector(NV_len, NV_array, sunctx);
550
551 delete[] NV_array;
552
553 return y_data;
554 };
555
556 // -----------------------------
557 // MultiFab / N_Vector Utilities
558 // -----------------------------
559
560 // Utility to unpack a SUNDIALS Vector into a MultiFab
561 void unpack_vector (N_Vector y_data, amrex::MultiFab& S_data)
562 {
565 0,
567 };
568
569 // Utility to wrap a MultiFab as a SUNDIALS Vector
570 N_Vector wrap_data (amrex::MultiFab& S_data)
571 {
572 return amrex::sundials::N_VMake_MultiFab(S_data.nComp() * S_data.boxArray().numPts(),
573 &S_data, &sunctx);
574 };
575
576 // Utility to wrap a MultiFab as a SUNDIALS Vector
577 N_Vector copy_data (const amrex::MultiFab& S_data)
578 {
579 N_Vector y_data = amrex::sundials::N_VNew_MultiFab(S_data.nComp() * S_data.boxArray().numPts(),
580 S_data.boxArray(),
581 S_data.DistributionMap(),
582 S_data.nComp(),
583 S_data.nGrow(),
584 &sunctx);
585
587 S_data,
588 0,
589 0,
590 S_data.nComp(),
591 S_data.nGrow());
592
593 return y_data;
594 };
595
596public:
601
608 SundialsIntegrator (const T& S_data, const amrex::Real time = 0.0)
609 {
610 initialize(S_data, time);
611 }
612
619 void initialize (const T& S_data, const amrex::Real time = 0.0)
620 {
621 free_memory(); // no-op on the first call
622 {
623 // Destroy the current context. Move assignment below does not free
624 // it in SUNDIALS older than 7.6.
625 ::sundials::Context old_sunctx = std::move(sunctx);
626 }
627
628 initialize_parameters();
630#if defined(SUNDIALS_VERSION_MAJOR) && (SUNDIALS_VERSION_MAJOR < 7)
631# ifdef AMREX_USE_MPI
632 sunctx = ::sundials::Context(&mpi_comm);
633# else
634 sunctx = ::sundials::Context(nullptr);
635# endif
636#else
637# ifdef AMREX_USE_MPI
638 sunctx = ::sundials::Context(mpi_comm);
639# else
640 sunctx = ::sundials::Context(SUN_COMM_NULL);
641# endif
642#endif
643
644 // Right-hand side function wrappers
645 udata.f = [&](amrex::Real rhs_time, N_Vector y_data, N_Vector y_rhs,
646 void * /* user_data */) -> int {
647
648 T S_data;
649 unpack_vector(y_data, S_data);
650
651 T S_rhs;
652 unpack_vector(y_rhs, S_rhs);
653
654 BaseT::Rhs(S_rhs, S_data, rhs_time);
655
656 return 0;
657 };
658
659 udata.fi = [&](amrex::Real rhs_time, N_Vector y_data, N_Vector y_rhs,
660 void * /* user_data */) -> int {
661
662 T S_data;
663 unpack_vector(y_data, S_data);
664
665 T S_rhs;
666 unpack_vector(y_rhs, S_rhs);
667
668 BaseT::RhsIm(S_rhs, S_data, rhs_time);
669
670 return 0;
671 };
672
673 udata.fe = [&](amrex::Real rhs_time, N_Vector y_data, N_Vector y_rhs,
674 void * /* user_data */) -> int {
675
676 T S_data;
677 unpack_vector(y_data, S_data);
678
679 T S_rhs;
680 unpack_vector(y_rhs, S_rhs);
681
682 BaseT::RhsEx(S_rhs, S_data, rhs_time);
683
684 return 0;
685 };
686
687 udata.ff = [&](amrex::Real rhs_time, N_Vector y_data, N_Vector y_rhs,
688 void * /* user_data */) -> int {
689
690 T S_data;
691 unpack_vector(y_data, S_data);
692
693 T S_rhs;
694 unpack_vector(y_rhs, S_rhs);
695
696 BaseT::RhsFast(S_rhs, S_data, rhs_time);
697
698 return 0;
699 };
700
701 udata.post_stage = [&](amrex::Real time, N_Vector y_data,
702 void * /* user_data */) -> int {
703
704 T S_data;
705 unpack_vector(y_data, S_data);
706
707 BaseT::post_stage_action(S_data, time);
708
709 return 0;
710 };
711
712 udata.post_step = [&](amrex::Real time, N_Vector y_data,
713 void * /* user_data */) -> int {
714
715 T S_data;
716 unpack_vector(y_data, S_data);
717
718 BaseT::post_step_action(S_data, time);
719
720 return 0;
721 };
722
723 udata.post_fast_stage = [&](amrex::Real time, N_Vector y_data,
724 void * /* user_data */) -> int {
725
726 T S_data;
727 unpack_vector(y_data, S_data);
728
729 BaseT::post_fast_stage_action(S_data, time);
730
731 return 0;
732 };
733
734 udata.post_fast_step = [&](amrex::Real time, N_Vector y_data,
735 void * /* user_data */) -> int {
736
737 T S_data;
738 unpack_vector(y_data, S_data);
739
740 BaseT::post_fast_step_action(S_data, time);
741
742 return 0;
743 };
744
745 N_Vector y_data = copy_data(S_data); // ideally just wrap and ignore const
746
747 if (use_ark) {
748 SetupRK(time, y_data);
749 }
750 else if (use_mri)
751 {
752 SetupMRI(time, y_data);
753 }
754
755 N_VDestroy(y_data);
756 }
757
762 // Print integrator statistics
763 if (amrex::Verbose()) {
764 if (type == "EX-MRI" || type == "IM-MRI" || type == "IMEX-MRI") {
765 amrex::Print() << "Slow Time Integrator Stats\n";
767 MRIStepPrintAllStats(arkode_mem, stdout, SUN_OUTPUTFORMAT_TABLE);
768 }
769 amrex::Print() << "Fast Time Integrator Stats\n";
771 ARKStepPrintAllStats(arkode_fast_mem, stdout, SUN_OUTPUTFORMAT_TABLE);
772 }
773 } else {
774 amrex::Print() << "Time Integrator Stats\n";
776 ARKStepPrintAllStats(arkode_mem, stdout, SUN_OUTPUTFORMAT_TABLE);
777 }
778 }
779 }
780
781 // Clean up allocated memory
782 free_memory();
783 }
784
794 amrex::Real advance (T& S_old, T& S_new, amrex::Real time, const amrex::Real dt) override
795 {
796 amrex::Real tout = time + dt;
797 amrex::Real tret;
798
799 N_Vector y_old = wrap_data(S_old);
800 N_Vector y_new = wrap_data(S_new);
801
802 if (use_ark) {
803 ARKStepReset(arkode_mem, time, y_old); // should probably resize
804 ARKStepSetFixedStep(arkode_mem, dt);
805 int flag = ARKStepEvolve(arkode_mem, tout, y_new, &tret, ARK_ONE_STEP);
806 AMREX_ALWAYS_ASSERT(flag >= 0);
807 }
808 else if (use_mri) {
809 MRIStepReset(arkode_mem, time, y_old); // should probably resize -- need to resize inner stepper
810 MRIStepSetFixedStep(arkode_mem, dt);
811 int flag = MRIStepEvolve(arkode_mem, tout, y_new, &tret, ARK_ONE_STEP);
812 AMREX_ALWAYS_ASSERT(flag >= 0);
813 } else {
814 Error("SUNDIALS integrator type not specified.");
815 }
816
817 N_VDestroy(y_old);
818 N_VDestroy(y_new);
819
820 return dt;
821 }
822
829 void evolve (T& S_out, const amrex::Real time_out) override
830 {
831 int flag = 0; // SUNDIALS return status
832 amrex::Real time_ret; // SUNDIALS return time
833
834 N_Vector y_out = wrap_data(S_out);
835
836 if (use_ark) {
838 ARKStepSetFixedStep(arkode_mem, BaseT::time_step);
839 }
840 flag = ARKStepEvolve(arkode_mem, time_out, y_out, &time_ret, ARK_NORMAL);
841 AMREX_ALWAYS_ASSERT(flag >= 0);
842 }
843 else if (use_mri) {
845 MRIStepSetFixedStep(arkode_mem, BaseT::time_step);
846 }
848 ARKStepSetFixedStep(arkode_fast_mem, BaseT::fast_time_step);
849 }
850 flag = MRIStepEvolve(arkode_mem, time_out, y_out, &time_ret, ARK_NORMAL);
851 AMREX_ALWAYS_ASSERT(flag >= 0);
852 } else {
853 Error("SUNDIALS integrator type not specified.");
854 }
855
856 N_VDestroy(y_out);
857 }
858
862 void time_interpolate (const T& /* S_new */, const T& /* S_old */, amrex::Real /* timestep_fraction */, T& /* data */) override {}
863
867 void map_data (std::function<void(T&)> /* Map */) override {}
868};
869
870}
871
872#endif
#define AMREX_ALWAYS_ASSERT(EX)
Definition AMReX_BLassert.H:50
amrex::ParmParse pp
Input file parser instance for the given namespace.
Definition AMReX_HypreIJIface.cpp:15
Long numPts() const noexcept
Returns the total number of cells contained in all boxes in the BoxArray.
Definition AMReX_BoxArray.cpp:394
int nGrow(int direction=0) const noexcept
Return the grow factor that defines the region of definition.
Definition AMReX_FabArrayBase.H:83
const DistributionMapping & DistributionMap() const noexcept
Return constant reference to associated DistributionMapping.
Definition AMReX_FabArrayBase.H:135
int nComp() const noexcept
Return number of variables (aka components) associated with each point.
Definition AMReX_FabArrayBase.H:88
const BoxArray & boxArray() const noexcept
Return a constant reference to the BoxArray that defines the valid region associated with this FabArr...
Definition AMReX_FabArrayBase.H:100
Definition AMReX_IntegratorBase.H:167
bool use_adaptive_fast_time_step
Flag to enable/disable adaptive time stepping at the fast time scale in multirate methods (bool)
Definition AMReX_IntegratorBase.H:249
amrex::Real fast_rel_tol
Relative tolerance for adaptive time stepping at the fast time scale (Real)
Definition AMReX_IntegratorBase.H:281
amrex::Real rel_tol
Relative tolerance for adaptive time stepping (Real)
Definition AMReX_IntegratorBase.H:270
std::function< void(T &, amrex::Real)> post_fast_stage_action
The post_stage_action function is called by the integrator on the computed stage just after it is com...
Definition AMReX_IntegratorBase.H:221
amrex::Real fast_abs_tol
Absolute tolerance for adaptive time stepping at the fast time scale (Real)
Definition AMReX_IntegratorBase.H:287
amrex::Real fast_time_step
Current integrator fast time scale time step size with multirate methods (Real)
Definition AMReX_IntegratorBase.H:255
std::function< void(T &rhs, T &state, const amrex::Real time)> RhsEx
RhsEx is the explicit right-hand-side function an ImEx integrator will use.
Definition AMReX_IntegratorBase.H:197
std::function< void(T &, amrex::Real)> post_step_action
The post_step_action function is called by the integrator on the computed state just after it is comp...
Definition AMReX_IntegratorBase.H:215
std::function< void(T &, amrex::Real)> post_fast_step_action
The post_step_action function is called by the integrator on the computed state just after it is comp...
Definition AMReX_IntegratorBase.H:227
std::function< void(T &rhs, T &state, const amrex::Real time)> RhsIm
RhsIm is the implicit right-hand-side function an ImEx integrator will use.
Definition AMReX_IntegratorBase.H:191
std::function< void(T &rhs, T &state, const amrex::Real time)> Rhs
Rhs is the right-hand-side function the integrator will use.
Definition AMReX_IntegratorBase.H:185
bool use_adaptive_time_step
Flag to enable/disable adaptive time stepping in single rate methods or at the slow time scale in mul...
Definition AMReX_IntegratorBase.H:233
std::function< void(T &, amrex::Real)> post_stage_action
The post_stage_action function is called by the integrator on the computed stage just after it is com...
Definition AMReX_IntegratorBase.H:209
amrex::Real time_step
Current integrator time step size (Real)
Definition AMReX_IntegratorBase.H:238
std::function< void(T &rhs, T &state, const amrex::Real time)> RhsFast
RhsFast is the fast timescale right-hand-side function a multirate integrator will use.
Definition AMReX_IntegratorBase.H:203
amrex::Real abs_tol
Absolute tolerance for adaptive time stepping (Real)
Definition AMReX_IntegratorBase.H:275
A collection (stored as an array) of FArrayBox objects.
Definition AMReX_MultiFab.H:40
static void Copy(MultiFab &dst, const MultiFab &src, int srccomp, int dstcomp, int numcomp, int nghost)
Copy from src to dst including nghost ghost cells. The two MultiFabs MUST have the same underlying Bo...
Definition AMReX_MultiFab.cpp:193
Parse Parameters From Command Line and Input Files.
Definition AMReX_ParmParse.H:353
int query(std::string_view name, bool &ref, int ival=FIRST) const
Same as querykth() but searches for the last occurrence of name.
Definition AMReX_ParmParse.cpp:1976
This class provides the user with a few print options.
Definition AMReX_Print.H:35
IntegratorBase implementation powered by SUNDIALS ARKStep/MRIStep.
Definition AMReX_SundialsIntegrator.H:96
void time_interpolate(const T &, const T &, amrex::Real, T &) override
Interpolate between SUNDIALS stages (not yet implemented for this integrator).
Definition AMReX_SundialsIntegrator.H:862
SundialsIntegrator()
Construct an uninitialized integrator; call initialize() before use.
Definition AMReX_SundialsIntegrator.H:600
void initialize(const T &S_data, const amrex::Real time=0.0)
Configure (or reconfigure) the SUNDIALS integrator for the provided state.
Definition AMReX_SundialsIntegrator.H:619
amrex::Real advance(T &S_old, T &S_new, amrex::Real time, const amrex::Real dt) override
Take a single time step of size dt starting from S_old.
Definition AMReX_SundialsIntegrator.H:794
void evolve(T &S_out, const amrex::Real time_out) override
Evolve the solution in S_out up to time_out using ARKStep/MRIStep.
Definition AMReX_SundialsIntegrator.H:829
virtual ~SundialsIntegrator()
Destroy the integrator, printing summary statistics when verbose.
Definition AMReX_SundialsIntegrator.H:761
SundialsIntegrator(const T &S_data, const amrex::Real time=0.0)
Construct and immediately configure the integrator with S_data at time time.
Definition AMReX_SundialsIntegrator.H:608
void map_data(std::function< void(T &)>) override
Apply a user-supplied mapping to every MultiFab in the integrator (unused placeholder).
Definition AMReX_SundialsIntegrator.H:867
This class is a thin wrapper around std::vector. Unlike vector, Vector::operator[] provides bound che...
Definition AMReX_Vector.H:29
Long size() const noexcept
Definition AMReX_Vector.H:54
amrex_real Real
Floating Point Type for Fields.
Definition AMReX_REAL.H:80
amrex_long Long
Definition AMReX_INT.H:30
bool IOProcessor() noexcept
Is this CPU the I/O Processor? To get the rank number, call IOProcessorNumber()
Definition AMReX_ParallelDescriptor.H:289
MPI_Comm CommunicatorSub() noexcept
sub-communicator for current frame
Definition AMReX_ParallelContext.H:70
static int fi(amrex::Real t, N_Vector y_data, N_Vector y_rhs, void *user_data)
Definition AMReX_SundialsIntegrator.H:53
static int fe(amrex::Real t, N_Vector y_data, N_Vector y_rhs, void *user_data)
Definition AMReX_SundialsIntegrator.H:58
static int post_fast_step(amrex::Real t, N_Vector y_data, void *user_data)
Definition AMReX_SundialsIntegrator.H:83
static int f(amrex::Real t, N_Vector y_data, N_Vector y_rhs, void *user_data)
Definition AMReX_SundialsIntegrator.H:48
static int post_step(amrex::Real t, N_Vector y_data, void *user_data)
Definition AMReX_SundialsIntegrator.H:73
static int ff(amrex::Real t, N_Vector y_data, N_Vector y_rhs, void *user_data)
Definition AMReX_SundialsIntegrator.H:63
static int post_stage(amrex::Real t, N_Vector y_data, void *user_data)
Definition AMReX_SundialsIntegrator.H:68
static int post_fast_stage(amrex::Real t, N_Vector y_data, void *user_data)
Definition AMReX_SundialsIntegrator.H:78
int MPI_Comm
Definition AMReX_ccse-mpi.H:51
N_Vector N_VMake_MultiFab(sunindextype length, amrex::MultiFab *v_mf, ::sundials::Context *sunctx)
Wrap an existing MultiFab mf as an N_Vector without copying.
Definition AMReX_NVector_MultiFab.cpp:105
amrex::MultiFab *& getMFptr(N_Vector v)
Access the MultiFab pointer stored inside v (non-const).
Definition AMReX_NVector_MultiFab.cpp:233
N_Vector N_VNew_MultiFab(sunindextype length, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm, sunindextype nComp, sunindextype nGhost, ::sundials::Context *sunctx)
Allocate a MultiFab-backed N_Vector of length vec_length.
Definition AMReX_NVector_MultiFab.cpp:80
Definition AMReX_Amr.cpp:50
@ make_alias
Definition AMReX_MakeType.H:7
BoxArray const & boxArray(FabArrayBase const &fa)
Convenience wrapper that forwards to fa.boxArray().
Definition AMReX_FabArrayBase.cpp:2870
DistributionMapping const & DistributionMap(FabArrayBase const &fa)
Convenience wrapper that forwards to fa.DistributionMap().
Definition AMReX_FabArrayBase.cpp:2875
int nComp(FabArrayBase const &fa)
Convenience wrapper that forwards to fa.nComp().
Definition AMReX_FabArrayBase.cpp:2860
void Error(const std::string &msg)
Print a message to stderr and abort the program.
Definition AMReX.cpp:236
int Verbose() noexcept
Return the verbosity level configured via ParmParse or SetVerbose().
Definition AMReX.cpp:182
const int[]
Definition AMReX_BLProfiler.cpp:1665
User-supplied callbacks consumed by the AMReX/SUNDIALS bridge.
Definition AMReX_SundialsIntegrator.H:36
std::function< int(amrex::Real, N_Vector, N_Vector, void *)> fi
Implicit RHS for ImEx schemes.
Definition AMReX_SundialsIntegrator.H:38
std::function< int(amrex::Real, N_Vector, void *)> post_fast_stage
Hook for MRI fast stages.
Definition AMReX_SundialsIntegrator.H:43
std::function< int(amrex::Real, N_Vector, void *)> post_step
Hook invoked after each time step.
Definition AMReX_SundialsIntegrator.H:42
std::function< int(amrex::Real, N_Vector, N_Vector, void *)> f
ERK/DIRK RHS or MRI slow RHS.
Definition AMReX_SundialsIntegrator.H:37
std::function< int(amrex::Real, N_Vector, void *)> post_stage
Hook invoked after each stage.
Definition AMReX_SundialsIntegrator.H:41
std::function< int(amrex::Real, N_Vector, N_Vector, void *)> ff
MRI fast-scale RHS.
Definition AMReX_SundialsIntegrator.H:40
std::function< int(amrex::Real, N_Vector, void *)> post_fast_step
Hook for MRI fast steps.
Definition AMReX_SundialsIntegrator.H:44
std::function< int(amrex::Real, N_Vector, N_Vector, void *)> fe
Explicit RHS for ImEx schemes.
Definition AMReX_SundialsIntegrator.H:39