1 /*-----------------------------------------------------------------
2 * Programmer(s): Daniel R. Reynolds @ SMU
3 * Alan C. Hindmarsh and Radu Serban @ LLNL
4 *-----------------------------------------------------------------
5 * SUNDIALS Copyright Start
6 * Copyright (c) 2002-2021, Lawrence Livermore National Security
7 * and Southern Methodist University.
8 * All rights reserved.
9 *
10 * See the top-level LICENSE and NOTICE files for details.
11 *
12 * SPDX-License-Identifier: BSD-3-Clause
13 * SUNDIALS Copyright End
14 *-----------------------------------------------------------------
15 * Implementation file for IDA's linear solver interface.
16 *-----------------------------------------------------------------*/
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #include "ida_impl.h"
23 #include "ida_ls_impl.h"
24 #include <sundials/sundials_math.h>
25 #include <sundials/sundials_linearsolver.h>
26 #include <sunmatrix/sunmatrix_band.h>
27 #include <sunmatrix/sunmatrix_dense.h>
28 #include <sunmatrix/sunmatrix_sparse.h>
29
30 /* constants */
31 #define MAX_ITERS 3 /* max. number of attempts to recover in DQ J*v */
32 #define ZERO RCONST(0.0)
33 #define PT25 RCONST(0.25)
34 #define PT05 RCONST(0.05)
35 #define PT9 RCONST(0.9)
36 #define ONE RCONST(1.0)
37 #define TWO RCONST(2.0)
38
39
40 /*===============================================================
41 IDALS Exported functions -- Required
42 ===============================================================*/
43
44 /*---------------------------------------------------------------
45 IDASetLinearSolver specifies the linear solver
46 ---------------------------------------------------------------*/
IDASetLinearSolver(void * ida_mem,SUNLinearSolver LS,SUNMatrix A)47 int IDASetLinearSolver(void *ida_mem, SUNLinearSolver LS, SUNMatrix A)
48 {
49 IDAMem IDA_mem;
50 IDALsMem idals_mem;
51 int retval, LSType;
52 booleantype iterative; /* is the solver iterative? */
53 booleantype matrixbased; /* is a matrix structure used? */
54
55 /* Return immediately if any input is NULL */
56 if (ida_mem == NULL) {
57 IDAProcessError(NULL, IDALS_MEM_NULL, "IDALS",
58 "IDASetLinearSolver", MSG_LS_IDAMEM_NULL);
59 return(IDALS_MEM_NULL);
60 }
61 if (LS == NULL) {
62 IDAProcessError(NULL, IDALS_ILL_INPUT, "IDALS",
63 "IDASetLinearSolver",
64 "LS must be non-NULL");
65 return(IDALS_ILL_INPUT);
66 }
67 IDA_mem = (IDAMem) ida_mem;
68
69 /* Test if solver is compatible with LS interface */
70 if ( (LS->ops->gettype == NULL) || (LS->ops->solve == NULL) ) {
71 IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDALS",
72 "IDASetLinearSolver",
73 "LS object is missing a required operation");
74 return(IDALS_ILL_INPUT);
75 }
76
77 /* Retrieve the LS type */
78 LSType = SUNLinSolGetType(LS);
79
80 /* Set flags based on LS type */
81 iterative = (LSType != SUNLINEARSOLVER_DIRECT);
82 matrixbased = (LSType != SUNLINEARSOLVER_ITERATIVE);
83
84 /* Test if vector is compatible with LS interface */
85 if (IDA_mem->ida_tempv1->ops->nvconst == NULL ||
86 IDA_mem->ida_tempv1->ops->nvwrmsnorm == NULL) {
87 IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDALS",
88 "IDASetLinearSolver", MSG_LS_BAD_NVECTOR);
89 return(IDALS_ILL_INPUT);
90 }
91
92 /* Check for compatible LS type, matrix and "atimes" support */
93 if (iterative) {
94
95 if (IDA_mem->ida_tempv1->ops->nvgetlength == NULL) {
96 IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDALS",
97 "IDASetLinearSolver", MSG_LS_BAD_NVECTOR);
98 return(IDALS_ILL_INPUT);
99 }
100
101 if (LS->ops->resid == NULL || LS->ops->numiters == NULL) {
102 IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDALS", "IDASetLinearSolver",
103 "Iterative LS object requires 'resid' and 'numiters' routines");
104 return(IDALS_ILL_INPUT);
105 }
106
107 if (!matrixbased && LS->ops->setatimes == NULL) {
108 IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDALS", "IDASetLinearSolver",
109 "Incompatible inputs: iterative LS must support ATimes routine");
110 return(IDALS_ILL_INPUT);
111 }
112
113 if (matrixbased && A == NULL) {
114 IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDALS", "IDASetLinearSolver",
115 "Incompatible inputs: matrix-iterative LS requires non-NULL matrix");
116 return(IDALS_ILL_INPUT);
117 }
118
119 } else if (A == NULL) {
120
121 IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDALS", "IDASetLinearSolver",
122 "Incompatible inputs: direct LS requires non-NULL matrix");
123 return(IDALS_ILL_INPUT);
124
125 }
126
127 /* free any existing system solver attached to IDA */
128 if (IDA_mem->ida_lfree) IDA_mem->ida_lfree(IDA_mem);
129
130 /* Set four main system linear solver function fields in IDA_mem */
131 IDA_mem->ida_linit = idaLsInitialize;
132 IDA_mem->ida_lsetup = idaLsSetup;
133 IDA_mem->ida_lsolve = idaLsSolve;
134 IDA_mem->ida_lfree = idaLsFree;
135
136 /* Set ida_lperf if using an iterative SUNLinearSolver object */
137 IDA_mem->ida_lperf = (iterative) ? idaLsPerf : NULL;
138
139 /* Allocate memory for IDALsMemRec */
140 idals_mem = NULL;
141 idals_mem = (IDALsMem) malloc(sizeof(struct IDALsMemRec));
142 if (idals_mem == NULL) {
143 IDAProcessError(IDA_mem, IDALS_MEM_FAIL, "IDALS",
144 "IDASetLinearSolver", MSG_LS_MEM_FAIL);
145 return(IDALS_MEM_FAIL);
146 }
147 memset(idals_mem, 0, sizeof(struct IDALsMemRec));
148
149 /* set SUNLinearSolver pointer */
150 idals_mem->LS = LS;
151
152 /* Linear solver type information */
153 idals_mem->iterative = iterative;
154 idals_mem->matrixbased = matrixbased;
155
156 /* Set defaults for Jacobian-related fields */
157 idals_mem->J = A;
158 if (A != NULL) {
159 idals_mem->jacDQ = SUNTRUE;
160 idals_mem->jac = idaLsDQJac;
161 idals_mem->J_data = IDA_mem;
162 } else {
163 idals_mem->jacDQ = SUNFALSE;
164 idals_mem->jac = NULL;
165 idals_mem->J_data = NULL;
166 }
167 idals_mem->jtimesDQ = SUNTRUE;
168 idals_mem->jtsetup = NULL;
169 idals_mem->jtimes = idaLsDQJtimes;
170 idals_mem->jt_res = IDA_mem->ida_res;
171 idals_mem->jt_data = IDA_mem;
172
173 /* Set defaults for preconditioner-related fields */
174 idals_mem->pset = NULL;
175 idals_mem->psolve = NULL;
176 idals_mem->pfree = NULL;
177 idals_mem->pdata = IDA_mem->ida_user_data;
178
179 /* Initialize counters */
180 idaLsInitializeCounters(idals_mem);
181
182 /* Set default values for the rest of the Ls parameters */
183 idals_mem->eplifac = PT05;
184 idals_mem->dqincfac = ONE;
185 idals_mem->last_flag = IDALS_SUCCESS;
186
187 /* If LS supports ATimes, attach IDALs routine */
188 if (LS->ops->setatimes) {
189 retval = SUNLinSolSetATimes(LS, IDA_mem, idaLsATimes);
190 if (retval != SUNLS_SUCCESS) {
191 IDAProcessError(IDA_mem, IDALS_SUNLS_FAIL, "IDALS",
192 "IDASetLinearSolver",
193 "Error in calling SUNLinSolSetATimes");
194 free(idals_mem); idals_mem = NULL;
195 return(IDALS_SUNLS_FAIL);
196 }
197 }
198
199 /* If LS supports preconditioning, initialize pset/psol to NULL */
200 if (LS->ops->setpreconditioner) {
201 retval = SUNLinSolSetPreconditioner(LS, IDA_mem, NULL, NULL);
202 if (retval != SUNLS_SUCCESS) {
203 IDAProcessError(IDA_mem, IDALS_SUNLS_FAIL, "IDALS",
204 "IDASetLinearSolver",
205 "Error in calling SUNLinSolSetPreconditioner");
206 free(idals_mem); idals_mem = NULL;
207 return(IDALS_SUNLS_FAIL);
208 }
209 }
210
211 /* Allocate memory for ytemp, yptemp and x */
212 idals_mem->ytemp = N_VClone(IDA_mem->ida_tempv1);
213 if (idals_mem->ytemp == NULL) {
214 IDAProcessError(IDA_mem, IDALS_MEM_FAIL, "IDALS",
215 "IDASetLinearSolver", MSG_LS_MEM_FAIL);
216 free(idals_mem); idals_mem = NULL;
217 return(IDALS_MEM_FAIL);
218 }
219
220 idals_mem->yptemp = N_VClone(IDA_mem->ida_tempv1);
221 if (idals_mem->yptemp == NULL) {
222 IDAProcessError(IDA_mem, IDALS_MEM_FAIL, "IDALS",
223 "IDASetLinearSolver", MSG_LS_MEM_FAIL);
224 N_VDestroy(idals_mem->ytemp);
225 free(idals_mem); idals_mem = NULL;
226 return(IDALS_MEM_FAIL);
227 }
228
229 idals_mem->x = N_VClone(IDA_mem->ida_tempv1);
230 if (idals_mem->x == NULL) {
231 IDAProcessError(IDA_mem, IDALS_MEM_FAIL, "IDALS",
232 "IDASetLinearSolver", MSG_LS_MEM_FAIL);
233 N_VDestroy(idals_mem->ytemp);
234 N_VDestroy(idals_mem->yptemp);
235 free(idals_mem); idals_mem = NULL;
236 return(IDALS_MEM_FAIL);
237 }
238
239 /* For iterative LS, compute sqrtN */
240 if (iterative)
241 idals_mem->nrmfac = SUNRsqrt( N_VGetLength(idals_mem->ytemp) );
242
243 /* For matrix-based LS, enable solution scaling */
244 if (matrixbased)
245 idals_mem->scalesol = SUNTRUE;
246 else
247 idals_mem->scalesol = SUNFALSE;
248
249 /* Attach linear solver memory to integrator memory */
250 IDA_mem->ida_lmem = idals_mem;
251
252 return(IDALS_SUCCESS);
253 }
254
255
256 /*===============================================================
257 Optional input/output routines
258 ===============================================================*/
259
260
261 /* IDASetJacFn specifies the Jacobian function */
IDASetJacFn(void * ida_mem,IDALsJacFn jac)262 int IDASetJacFn(void *ida_mem, IDALsJacFn jac)
263 {
264 IDAMem IDA_mem;
265 IDALsMem idals_mem;
266 int retval;
267
268 /* access IDALsMem structure */
269 retval = idaLs_AccessLMem(ida_mem, "IDALsSetJacFn",
270 &IDA_mem, &idals_mem);
271 if (retval != IDALS_SUCCESS) return(retval);
272
273 /* return with failure if jac cannot be used */
274 if ((jac != NULL) && (idals_mem->J == NULL)) {
275 IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDALS", "IDASetJacFn",
276 "Jacobian routine cannot be supplied for NULL SUNMatrix");
277 return(IDALS_ILL_INPUT);
278 }
279
280 /* set Jacobian routine pointer, and update relevant flags */
281 if (jac != NULL) {
282 idals_mem->jacDQ = SUNFALSE;
283 idals_mem->jac = jac;
284 idals_mem->J_data = IDA_mem->ida_user_data;
285 } else {
286 idals_mem->jacDQ = SUNTRUE;
287 idals_mem->jac = idaLsDQJac;
288 idals_mem->J_data = IDA_mem;
289 }
290
291 return(IDALS_SUCCESS);
292 }
293
294
295 /* IDASetEpsLin specifies the nonlinear -> linear tolerance scale factor */
IDASetEpsLin(void * ida_mem,realtype eplifac)296 int IDASetEpsLin(void *ida_mem, realtype eplifac)
297 {
298 IDAMem IDA_mem;
299 IDALsMem idals_mem;
300 int retval;
301
302 /* access IDALsMem structure */
303 retval = idaLs_AccessLMem(ida_mem, "IDASetEpsLin",
304 &IDA_mem, &idals_mem);
305 if (retval != IDALS_SUCCESS) return(retval);
306
307 /* Check for legal eplifac */
308 if (eplifac < ZERO) {
309 IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDALS",
310 "IDASetEpsLin", MSG_LS_NEG_EPLIFAC);
311 return(IDALS_ILL_INPUT);
312 }
313
314 idals_mem->eplifac = (eplifac == ZERO) ? PT05 : eplifac;
315
316 return(IDALS_SUCCESS);
317 }
318
319
320 /* IDASetWRMSNormFactor sets or computes the factor to use when converting from
321 the integrator tolerance to the linear solver tolerance (WRMS to L2 norm). */
IDASetLSNormFactor(void * ida_mem,realtype nrmfac)322 int IDASetLSNormFactor(void *ida_mem, realtype nrmfac)
323 {
324 IDAMem IDA_mem;
325 IDALsMem idals_mem;
326 int retval;
327
328 /* access IDALsMem structure */
329 retval = idaLs_AccessLMem(ida_mem, "IDASetLSNormFactor",
330 &IDA_mem, &idals_mem);
331 if (retval != IDALS_SUCCESS) return(retval);
332
333 if (nrmfac > ZERO) {
334 /* user-provided factor */
335 idals_mem->nrmfac = nrmfac;
336 } else if (nrmfac < ZERO) {
337 /* compute factor for WRMS norm with dot product */
338 N_VConst(ONE, idals_mem->ytemp);
339 idals_mem->nrmfac = SUNRsqrt(N_VDotProd(idals_mem->ytemp, idals_mem->ytemp));
340 } else {
341 /* compute default factor for WRMS norm from vector legnth */
342 idals_mem->nrmfac = SUNRsqrt(N_VGetLength(idals_mem->ytemp));
343 }
344
345 return(IDALS_SUCCESS);
346 }
347
348
349 /* IDASetLinearSolutionScaling enables or disables scaling the linear solver
350 solution to account for changes in cj. */
IDASetLinearSolutionScaling(void * ida_mem,booleantype onoff)351 int IDASetLinearSolutionScaling(void *ida_mem, booleantype onoff)
352 {
353 IDAMem IDA_mem;
354 IDALsMem idals_mem;
355 int retval;
356
357 /* access IDALsMem structure */
358 retval = idaLs_AccessLMem(ida_mem, "IDASetLinearSolutionScaling",
359 &IDA_mem, &idals_mem);
360 if (retval != IDALS_SUCCESS) return(retval);
361
362 /* check for valid solver type */
363 if (!(idals_mem->matrixbased)) return(IDALS_ILL_INPUT);
364
365 /* set solution scaling flag */
366 idals_mem->scalesol = onoff;
367
368 return(IDALS_SUCCESS);
369 }
370
371
372 /* IDASetIncrementFactor specifies increment factor for DQ approximations to Jv */
IDASetIncrementFactor(void * ida_mem,realtype dqincfac)373 int IDASetIncrementFactor(void *ida_mem, realtype dqincfac)
374 {
375 IDAMem IDA_mem;
376 IDALsMem idals_mem;
377 int retval;
378
379 /* access IDALsMem structure */
380 retval = idaLs_AccessLMem(ida_mem, "IDASetIncrementFactor",
381 &IDA_mem, &idals_mem);
382 if (retval != IDALS_SUCCESS) return(retval);
383
384 /* Check for legal dqincfac */
385 if (dqincfac <= ZERO) {
386 IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDALS",
387 "IDASetIncrementFactor", MSG_LS_NEG_DQINCFAC);
388 return(IDALS_ILL_INPUT);
389 }
390
391 idals_mem->dqincfac = dqincfac;
392
393 return(IDALS_SUCCESS);
394 }
395
396
397 /* IDASetPreconditioner specifies the user-supplied psetup and psolve routines */
IDASetPreconditioner(void * ida_mem,IDALsPrecSetupFn psetup,IDALsPrecSolveFn psolve)398 int IDASetPreconditioner(void *ida_mem,
399 IDALsPrecSetupFn psetup,
400 IDALsPrecSolveFn psolve)
401 {
402 IDAMem IDA_mem;
403 IDALsMem idals_mem;
404 PSetupFn idals_psetup;
405 PSolveFn idals_psolve;
406 int retval;
407
408 /* access IDALsMem structure */
409 retval = idaLs_AccessLMem(ida_mem, "IDASetPreconditioner",
410 &IDA_mem, &idals_mem);
411 if (retval != IDALS_SUCCESS) return(retval);
412
413 /* store function pointers for user-supplied routines in IDALs interface */
414 idals_mem->pset = psetup;
415 idals_mem->psolve = psolve;
416
417 /* issue error if LS object does not allow user-supplied preconditioning */
418 if (idals_mem->LS->ops->setpreconditioner == NULL) {
419 IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDALS",
420 "IDASetPreconditioner",
421 "SUNLinearSolver object does not support user-supplied preconditioning");
422 return(IDALS_ILL_INPUT);
423 }
424
425 /* notify iterative linear solver to call IDALs interface routines */
426 idals_psetup = (psetup == NULL) ? NULL : idaLsPSetup;
427 idals_psolve = (psolve == NULL) ? NULL : idaLsPSolve;
428 retval = SUNLinSolSetPreconditioner(idals_mem->LS, IDA_mem,
429 idals_psetup, idals_psolve);
430 if (retval != SUNLS_SUCCESS) {
431 IDAProcessError(IDA_mem, IDALS_SUNLS_FAIL, "IDALS",
432 "IDASetPreconditioner",
433 "Error in calling SUNLinSolSetPreconditioner");
434 return(IDALS_SUNLS_FAIL);
435 }
436
437 return(IDALS_SUCCESS);
438 }
439
440
441 /* IDASetJacTimes specifies the user-supplied Jacobian-vector product
442 setup and multiply routines */
IDASetJacTimes(void * ida_mem,IDALsJacTimesSetupFn jtsetup,IDALsJacTimesVecFn jtimes)443 int IDASetJacTimes(void *ida_mem, IDALsJacTimesSetupFn jtsetup,
444 IDALsJacTimesVecFn jtimes)
445 {
446 IDAMem IDA_mem;
447 IDALsMem idals_mem;
448 int retval;
449
450 /* access IDALsMem structure */
451 retval = idaLs_AccessLMem(ida_mem, "IDASetJacTimes",
452 &IDA_mem, &idals_mem);
453 if (retval != IDALS_SUCCESS) return(retval);
454
455 /* issue error if LS object does not allow user-supplied ATimes */
456 if (idals_mem->LS->ops->setatimes == NULL) {
457 IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDALS",
458 "IDASetJacTimes",
459 "SUNLinearSolver object does not support user-supplied ATimes routine");
460 return(IDALS_ILL_INPUT);
461 }
462
463 /* store function pointers for user-supplied routines in IDALs
464 interface (NULL jtimes implies use of DQ default) */
465 if (jtimes != NULL) {
466 idals_mem->jtimesDQ = SUNFALSE;
467 idals_mem->jtsetup = jtsetup;
468 idals_mem->jtimes = jtimes;
469 idals_mem->jt_data = IDA_mem->ida_user_data;
470 } else {
471 idals_mem->jtimesDQ = SUNTRUE;
472 idals_mem->jtsetup = NULL;
473 idals_mem->jtimes = idaLsDQJtimes;
474 idals_mem->jt_res = IDA_mem->ida_res;
475 idals_mem->jt_data = IDA_mem;
476 }
477
478 return(IDALS_SUCCESS);
479 }
480
481
482 /* IDASetJacTimesResFn specifies an alternative user-supplied DAE residual
483 function to use in the internal finite difference Jacobian-vector
484 product */
IDASetJacTimesResFn(void * ida_mem,IDAResFn jtimesResFn)485 int IDASetJacTimesResFn(void *ida_mem, IDAResFn jtimesResFn)
486 {
487 IDAMem IDA_mem;
488 IDALsMem idals_mem;
489 int retval;
490
491 /* access IDALsMem structure */
492 retval = idaLs_AccessLMem(ida_mem, "IDASetJacTimesResFn",
493 &IDA_mem, &idals_mem);
494 if (retval != IDALS_SUCCESS) return(retval);
495
496 /* check if using internal finite difference approximation */
497 if (!(idals_mem->jtimesDQ)) {
498 IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDALS", "IDASetJacTimesResFn",
499 "Internal finite-difference Jacobian-vector product is disabled.");
500 return(IDALS_ILL_INPUT);
501 }
502
503 /* store function pointers for Res function (NULL implies use DAE Res) */
504 if (jtimesResFn != NULL)
505 idals_mem->jt_res = jtimesResFn;
506 else
507 idals_mem->jt_res = IDA_mem->ida_res;
508
509 return(IDALS_SUCCESS);
510 }
511
512
513 /* IDAGetLinWorkSpace returns the length of workspace allocated
514 for the IDALS linear solver interface */
IDAGetLinWorkSpace(void * ida_mem,long int * lenrwLS,long int * leniwLS)515 int IDAGetLinWorkSpace(void *ida_mem, long int *lenrwLS,
516 long int *leniwLS)
517 {
518 IDAMem IDA_mem;
519 IDALsMem idals_mem;
520 sunindextype lrw1, liw1;
521 long int lrw, liw;
522 int retval;
523
524 /* access IDALsMem structure */
525 retval = idaLs_AccessLMem(ida_mem, "IDAGetLinWorkSpace",
526 &IDA_mem, &idals_mem);
527 if (retval != IDALS_SUCCESS) return(retval);
528
529 /* start with fixed sizes plus vector/matrix pointers */
530 *lenrwLS = 3;
531 *leniwLS = 33;
532
533 /* add N_Vector sizes */
534 if (IDA_mem->ida_tempv1->ops->nvspace) {
535 N_VSpace(IDA_mem->ida_tempv1, &lrw1, &liw1);
536 *lenrwLS += 3*lrw1;
537 *leniwLS += 3*liw1;
538 }
539
540 /* add LS sizes */
541 if (idals_mem->LS->ops->space) {
542 retval = SUNLinSolSpace(idals_mem->LS, &lrw, &liw);
543 if (retval == 0) {
544 *lenrwLS += lrw;
545 *leniwLS += liw;
546 }
547 }
548
549 return(IDALS_SUCCESS);
550 }
551
552
553 /* IDAGetNumJacEvals returns the number of Jacobian evaluations */
IDAGetNumJacEvals(void * ida_mem,long int * njevals)554 int IDAGetNumJacEvals(void *ida_mem, long int *njevals)
555 {
556 IDAMem IDA_mem;
557 IDALsMem idals_mem;
558 int retval;
559
560 /* access IDALsMem structure; store output and return */
561 retval = idaLs_AccessLMem(ida_mem, "IDAGetNumJacEvals",
562 &IDA_mem, &idals_mem);
563 if (retval != IDALS_SUCCESS) return(retval);
564 *njevals = idals_mem->nje;
565 return(IDALS_SUCCESS);
566 }
567
568
569 /* IDAGetNumPrecEvals returns the number of preconditioner evaluations */
IDAGetNumPrecEvals(void * ida_mem,long int * npevals)570 int IDAGetNumPrecEvals(void *ida_mem, long int *npevals)
571 {
572 IDAMem IDA_mem;
573 IDALsMem idals_mem;
574 int retval;
575
576 /* access IDALsMem structure; store output and return */
577 retval = idaLs_AccessLMem(ida_mem, "IDAGetNumPrecEvals",
578 &IDA_mem, &idals_mem);
579 if (retval != IDALS_SUCCESS) return(retval);
580 *npevals = idals_mem->npe;
581 return(IDALS_SUCCESS);
582 }
583
584
585 /* IDAGetNumPrecSolves returns the number of preconditioner solves */
IDAGetNumPrecSolves(void * ida_mem,long int * npsolves)586 int IDAGetNumPrecSolves(void *ida_mem, long int *npsolves)
587 {
588 IDAMem IDA_mem;
589 IDALsMem idals_mem;
590 int retval;
591
592 /* access IDALsMem structure; store output and return */
593 retval = idaLs_AccessLMem(ida_mem, "IDAGetNumPrecSolves",
594 &IDA_mem, &idals_mem);
595 if (retval != IDALS_SUCCESS) return(retval);
596 *npsolves = idals_mem->nps;
597 return(IDALS_SUCCESS);
598 }
599
600
601 /* IDAGetNumLinIters returns the number of linear iterations */
IDAGetNumLinIters(void * ida_mem,long int * nliters)602 int IDAGetNumLinIters(void *ida_mem, long int *nliters)
603 {
604 IDAMem IDA_mem;
605 IDALsMem idals_mem;
606 int retval;
607
608 /* access IDALsMem structure; store output and return */
609 retval = idaLs_AccessLMem(ida_mem, "IDAGetNumLinIters",
610 &IDA_mem, &idals_mem);
611 if (retval != IDALS_SUCCESS) return(retval);
612 *nliters = idals_mem->nli;
613 return(IDALS_SUCCESS);
614 }
615
616
617 /* IDAGetNumLinConvFails returns the number of linear convergence failures */
IDAGetNumLinConvFails(void * ida_mem,long int * nlcfails)618 int IDAGetNumLinConvFails(void *ida_mem, long int *nlcfails)
619 {
620 IDAMem IDA_mem;
621 IDALsMem idals_mem;
622 int retval;
623
624 /* access IDALsMem structure; store output and return */
625 retval = idaLs_AccessLMem(ida_mem, "IDAGetNumLinConvFails",
626 &IDA_mem, &idals_mem);
627 if (retval != IDALS_SUCCESS) return(retval);
628 *nlcfails = idals_mem->ncfl;
629 return(IDALS_SUCCESS);
630 }
631
632
633 /* IDAGetNumJTSetupEvals returns the number of calls to the
634 user-supplied Jacobian-vector product setup routine */
IDAGetNumJTSetupEvals(void * ida_mem,long int * njtsetups)635 int IDAGetNumJTSetupEvals(void *ida_mem, long int *njtsetups)
636 {
637 IDAMem IDA_mem;
638 IDALsMem idals_mem;
639 int retval;
640
641 /* access IDALsMem structure; store output and return */
642 retval = idaLs_AccessLMem(ida_mem, "IDAGetNumJTSetupEvals",
643 &IDA_mem, &idals_mem);
644 if (retval != IDALS_SUCCESS) return(retval);
645 *njtsetups = idals_mem->njtsetup;
646 return(IDALS_SUCCESS);
647 }
648
649
650 /* IDAGetNumJtimesEvals returns the number of calls to the
651 Jacobian-vector product multiply routine */
IDAGetNumJtimesEvals(void * ida_mem,long int * njvevals)652 int IDAGetNumJtimesEvals(void *ida_mem, long int *njvevals)
653 {
654 IDAMem IDA_mem;
655 IDALsMem idals_mem;
656 int retval;
657
658 /* access IDALsMem structure; store output and return */
659 retval = idaLs_AccessLMem(ida_mem, "IDAGetNumJtimesEvals",
660 &IDA_mem, &idals_mem);
661 if (retval != IDALS_SUCCESS) return(retval);
662 *njvevals = idals_mem->njtimes;
663 return(IDALS_SUCCESS);
664 }
665
666
667 /* IDAGetNumLinResEvals returns the number of calls to the DAE
668 residual needed for the DQ Jacobian approximation or J*v
669 product approximation */
IDAGetNumLinResEvals(void * ida_mem,long int * nrevalsLS)670 int IDAGetNumLinResEvals(void *ida_mem, long int *nrevalsLS)
671 {
672 IDAMem IDA_mem;
673 IDALsMem idals_mem;
674 int retval;
675
676 /* access IDALsMem structure; store output and return */
677 retval = idaLs_AccessLMem(ida_mem, "IDAGetNumLinResEvals",
678 &IDA_mem, &idals_mem);
679 if (retval != IDALS_SUCCESS) return(retval);
680 *nrevalsLS = idals_mem->nreDQ;
681 return(IDALS_SUCCESS);
682 }
683
684
685 /* IDAGetLastLinFlag returns the last flag set in a IDALS function */
IDAGetLastLinFlag(void * ida_mem,long int * flag)686 int IDAGetLastLinFlag(void *ida_mem, long int *flag)
687 {
688 IDAMem IDA_mem;
689 IDALsMem idals_mem;
690 int retval;
691
692 /* access IDALsMem structure; store output and return */
693 retval = idaLs_AccessLMem(ida_mem, "IDAGetLastLinFlag",
694 &IDA_mem, &idals_mem);
695 if (retval != IDALS_SUCCESS) return(retval);
696 *flag = idals_mem->last_flag;
697 return(IDALS_SUCCESS);
698 }
699
700
701 /* IDAGetLinReturnFlagName translates from the integer error code
702 returned by an IDALs routine to the corresponding string
703 equivalent for that flag */
IDAGetLinReturnFlagName(long int flag)704 char *IDAGetLinReturnFlagName(long int flag)
705 {
706 char *name = (char *)malloc(30*sizeof(char));
707
708 switch(flag) {
709 case IDALS_SUCCESS:
710 sprintf(name,"IDALS_SUCCESS");
711 break;
712 case IDALS_MEM_NULL:
713 sprintf(name,"IDALS_MEM_NULL");
714 break;
715 case IDALS_LMEM_NULL:
716 sprintf(name,"IDALS_LMEM_NULL");
717 break;
718 case IDALS_ILL_INPUT:
719 sprintf(name,"IDALS_ILL_INPUT");
720 break;
721 case IDALS_MEM_FAIL:
722 sprintf(name,"IDALS_MEM_FAIL");
723 break;
724 case IDALS_PMEM_NULL:
725 sprintf(name,"IDALS_PMEM_NULL");
726 break;
727 case IDALS_JACFUNC_UNRECVR:
728 sprintf(name,"IDALS_JACFUNC_UNRECVR");
729 break;
730 case IDALS_JACFUNC_RECVR:
731 sprintf(name,"IDALS_JACFUNC_RECVR");
732 break;
733 case IDALS_SUNMAT_FAIL:
734 sprintf(name,"IDALS_SUNMAT_FAIL");
735 break;
736 case IDALS_SUNLS_FAIL:
737 sprintf(name,"IDALS_SUNLS_FAIL");
738 break;
739 default:
740 sprintf(name,"NONE");
741 }
742
743 return(name);
744 }
745
746
747 /*===============================================================
748 IDALS Private functions
749 ===============================================================*/
750
751 /*---------------------------------------------------------------
752 idaLsATimes:
753
754 This routine generates the matrix-vector product z = Jv, where
755 J is the system Jacobian, by calling either the user provided
756 routine or the internal DQ routine. The return value is
757 the same as the value returned by jtimes --
758 0 if successful, nonzero otherwise.
759 ---------------------------------------------------------------*/
idaLsATimes(void * ida_mem,N_Vector v,N_Vector z)760 int idaLsATimes(void *ida_mem, N_Vector v, N_Vector z)
761 {
762 IDAMem IDA_mem;
763 IDALsMem idals_mem;
764 int retval;
765
766 /* access IDALsMem structure */
767 retval = idaLs_AccessLMem(ida_mem, "idaLsATimes",
768 &IDA_mem, &idals_mem);
769 if (retval != IDALS_SUCCESS) return(retval);
770
771 /* call Jacobian-times-vector product routine
772 (either user-supplied or internal DQ) */
773 retval = idals_mem->jtimes(IDA_mem->ida_tn, idals_mem->ycur,
774 idals_mem->ypcur, idals_mem->rcur,
775 v, z, IDA_mem->ida_cj,
776 idals_mem->jt_data, idals_mem->ytemp,
777 idals_mem->yptemp);
778 idals_mem->njtimes++;
779 return(retval);
780 }
781
782
783 /*---------------------------------------------------------------
784 idaLsPSetup:
785
786 This routine interfaces between the generic iterative linear
787 solvers and the user's psetup routine. It passes to psetup all
788 required state information from ida_mem. Its return value
789 is the same as that returned by psetup. Note that the generic
790 iterative linear solvers guarantee that idaLsPSetup will only
791 be called in the case that the user's psetup routine is non-NULL.
792 ---------------------------------------------------------------*/
idaLsPSetup(void * ida_mem)793 int idaLsPSetup(void *ida_mem)
794 {
795 IDAMem IDA_mem;
796 IDALsMem idals_mem;
797 int retval;
798
799 /* access IDALsMem structure */
800 retval = idaLs_AccessLMem(ida_mem, "idaLsPSetup",
801 &IDA_mem, &idals_mem);
802 if (retval != IDALS_SUCCESS) return(retval);
803
804 /* Call user pset routine to update preconditioner and possibly
805 reset jcur (pass !jbad as update suggestion) */
806 retval = idals_mem->pset(IDA_mem->ida_tn, idals_mem->ycur,
807 idals_mem->ypcur, idals_mem->rcur,
808 IDA_mem->ida_cj, idals_mem->pdata);
809 idals_mem->npe++;
810 return(retval);
811 }
812
813
814 /*---------------------------------------------------------------
815 idaLsPSolve:
816
817 This routine interfaces between the generic SUNLinSolSolve
818 routine and the user's psolve routine. It passes to psolve all
819 required state information from ida_mem. Its return value is
820 the same as that returned by psolve. Note that the generic
821 SUNLinSol solver guarantees that IDASilsPSolve will not be
822 called in the case in which preconditioning is not done. This
823 is the only case in which the user's psolve routine is allowed
824 to be NULL.
825 ---------------------------------------------------------------*/
idaLsPSolve(void * ida_mem,N_Vector r,N_Vector z,realtype tol,int lr)826 int idaLsPSolve(void *ida_mem, N_Vector r, N_Vector z, realtype tol, int lr)
827 {
828 IDAMem IDA_mem;
829 IDALsMem idals_mem;
830 int retval;
831
832 /* access IDALsMem structure */
833 retval = idaLs_AccessLMem(ida_mem, "idaLsPSolve",
834 &IDA_mem, &idals_mem);
835 if (retval != IDALS_SUCCESS) return(retval);
836
837 /* call the user-supplied psolve routine, and accumulate count */
838 retval = idals_mem->psolve(IDA_mem->ida_tn, idals_mem->ycur,
839 idals_mem->ypcur, idals_mem->rcur,
840 r, z, IDA_mem->ida_cj, tol,
841 idals_mem->pdata);
842 idals_mem->nps++;
843 return(retval);
844 }
845
846
847 /*---------------------------------------------------------------
848 idaLsDQJac:
849
850 This routine is a wrapper for the Dense and Band
851 implementations of the difference quotient Jacobian
852 approximation routines.
853 ---------------------------------------------------------------*/
idaLsDQJac(realtype t,realtype c_j,N_Vector y,N_Vector yp,N_Vector r,SUNMatrix Jac,void * ida_mem,N_Vector tmp1,N_Vector tmp2,N_Vector tmp3)854 int idaLsDQJac(realtype t, realtype c_j, N_Vector y, N_Vector yp,
855 N_Vector r, SUNMatrix Jac, void *ida_mem,
856 N_Vector tmp1, N_Vector tmp2, N_Vector tmp3)
857 {
858 int retval;
859 IDAMem IDA_mem;
860 IDA_mem = (IDAMem) ida_mem;
861
862 /* access IDAMem structure */
863 if (ida_mem == NULL) {
864 IDAProcessError(NULL, IDALS_MEM_NULL, "IDALS",
865 "idaLsDQJac", MSG_LS_IDAMEM_NULL);
866 return(IDALS_MEM_NULL);
867 }
868
869 /* verify that Jac is non-NULL */
870 if (Jac == NULL) {
871 IDAProcessError(IDA_mem, IDALS_LMEM_NULL, "IDALS",
872 "idaLsDQJac", MSG_LS_LMEM_NULL);
873 return(IDALS_LMEM_NULL);
874 }
875
876 /* Verify that N_Vector supports required operations */
877 if (IDA_mem->ida_tempv1->ops->nvcloneempty == NULL ||
878 IDA_mem->ida_tempv1->ops->nvlinearsum == NULL ||
879 IDA_mem->ida_tempv1->ops->nvdestroy == NULL ||
880 IDA_mem->ida_tempv1->ops->nvscale == NULL ||
881 IDA_mem->ida_tempv1->ops->nvgetarraypointer == NULL ||
882 IDA_mem->ida_tempv1->ops->nvsetarraypointer == NULL) {
883 IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDALS",
884 "idaLsDQJac", MSG_LS_BAD_NVECTOR);
885 return(IDALS_ILL_INPUT);
886 }
887
888 /* Call the matrix-structure-specific DQ approximation routine */
889 if (SUNMatGetID(Jac) == SUNMATRIX_DENSE) {
890 retval = idaLsDenseDQJac(t, c_j, y, yp, r, Jac, IDA_mem, tmp1);
891 } else if (SUNMatGetID(Jac) == SUNMATRIX_BAND) {
892 retval = idaLsBandDQJac(t, c_j, y, yp, r, Jac, IDA_mem, tmp1, tmp2, tmp3);
893 } else {
894 IDAProcessError(IDA_mem, IDA_ILL_INPUT, "IDALS",
895 "idaLsDQJac",
896 "unrecognized matrix type for idaLsDQJac");
897 retval = IDA_ILL_INPUT;
898 }
899 return(retval);
900 }
901
902
903 /*---------------------------------------------------------------
904 idaLsDenseDQJac
905
906 This routine generates a dense difference quotient approximation
907 to the Jacobian F_y + c_j*F_y'. It assumes a dense SUNmatrix
908 input (stored column-wise, and that elements within each column
909 are contiguous). The address of the jth column of J is obtained
910 via the function SUNDenseMatrix_Column() and this pointer is
911 associated with an N_Vector using the
912 N_VGetArrayPointer/N_VSetArrayPointer functions. Finally, the
913 actual computation of the jth column of the Jacobian is
914 done with a call to N_VLinearSum.
915 ---------------------------------------------------------------*/
idaLsDenseDQJac(realtype tt,realtype c_j,N_Vector yy,N_Vector yp,N_Vector rr,SUNMatrix Jac,IDAMem IDA_mem,N_Vector tmp1)916 int idaLsDenseDQJac(realtype tt, realtype c_j, N_Vector yy,
917 N_Vector yp, N_Vector rr, SUNMatrix Jac,
918 IDAMem IDA_mem, N_Vector tmp1)
919 {
920 realtype inc, inc_inv, yj, ypj, srur, conj;
921 realtype *y_data, *yp_data, *ewt_data, *cns_data = NULL;
922 N_Vector rtemp, jthCol;
923 sunindextype j, N;
924 IDALsMem idals_mem;
925 int retval = 0;
926
927 /* access LsMem interface structure */
928 idals_mem = (IDALsMem) IDA_mem->ida_lmem;
929
930 /* access matrix dimension */
931 N = SUNDenseMatrix_Columns(Jac);
932
933 /* Rename work vectors for readibility */
934 rtemp = tmp1;
935
936 /* Create an empty vector for matrix column calculations */
937 jthCol = N_VCloneEmpty(tmp1);
938
939 /* Obtain pointers to the data for ewt, yy, yp. */
940 ewt_data = N_VGetArrayPointer(IDA_mem->ida_ewt);
941 y_data = N_VGetArrayPointer(yy);
942 yp_data = N_VGetArrayPointer(yp);
943 if(IDA_mem->ida_constraintsSet)
944 cns_data = N_VGetArrayPointer(IDA_mem->ida_constraints);
945
946 srur = SUNRsqrt(IDA_mem->ida_uround);
947
948 for (j=0; j < N; j++) {
949
950 /* Generate the jth col of J(tt,yy,yp) as delta(F)/delta(y_j). */
951
952 /* Set data address of jthCol, and save y_j and yp_j values. */
953 N_VSetArrayPointer(SUNDenseMatrix_Column(Jac,j), jthCol);
954 yj = y_data[j];
955 ypj = yp_data[j];
956
957 /* Set increment inc to y_j based on sqrt(uround)*abs(y_j), with
958 adjustments using yp_j and ewt_j if this is small, and a further
959 adjustment to give it the same sign as hh*yp_j. */
960
961 inc = SUNMAX( srur * SUNMAX( SUNRabs(yj), SUNRabs(IDA_mem->ida_hh*ypj) ),
962 ONE/ewt_data[j] );
963
964 if (IDA_mem->ida_hh*ypj < ZERO) inc = -inc;
965 inc = (yj + inc) - yj;
966
967 /* Adjust sign(inc) again if y_j has an inequality constraint. */
968 if (IDA_mem->ida_constraintsSet) {
969 conj = cns_data[j];
970 if (SUNRabs(conj) == ONE) {if((yj+inc)*conj < ZERO) inc = -inc;}
971 else if (SUNRabs(conj) == TWO) {if((yj+inc)*conj <= ZERO) inc = -inc;}
972 }
973
974 /* Increment y_j and yp_j, call res, and break on error return. */
975 y_data[j] += inc;
976 yp_data[j] += c_j*inc;
977
978 retval = IDA_mem->ida_res(tt, yy, yp, rtemp, IDA_mem->ida_user_data);
979 idals_mem->nreDQ++;
980 if (retval != 0) break;
981
982 /* Construct difference quotient in jthCol */
983 inc_inv = ONE/inc;
984 N_VLinearSum(inc_inv, rtemp, -inc_inv, rr, jthCol);
985
986 /* reset y_j, yp_j */
987 y_data[j] = yj;
988 yp_data[j] = ypj;
989 }
990
991 /* Destroy jthCol vector */
992 N_VSetArrayPointer(NULL, jthCol); /* SHOULDN'T BE NEEDED */
993 N_VDestroy(jthCol);
994
995 return(retval);
996 }
997
998
999 /*---------------------------------------------------------------
1000 idaLsBandDQJac
1001
1002 This routine generates a banded difference quotient approximation
1003 JJ to the DAE system Jacobian J. It assumes a band SUNMatrix
1004 input (stored column-wise, and that elements within each column
1005 are contiguous). This makes it possible to get the address
1006 of a column of JJ via the function SUNBandMatrix_Column(). The
1007 columns of the Jacobian are constructed using mupper + mlower + 1
1008 calls to the res routine, and appropriate differencing.
1009 The return value is either IDABAND_SUCCESS = 0, or the nonzero
1010 value returned by the res routine, if any.
1011 ---------------------------------------------------------------*/
idaLsBandDQJac(realtype tt,realtype c_j,N_Vector yy,N_Vector yp,N_Vector rr,SUNMatrix Jac,IDAMem IDA_mem,N_Vector tmp1,N_Vector tmp2,N_Vector tmp3)1012 int idaLsBandDQJac(realtype tt, realtype c_j, N_Vector yy,
1013 N_Vector yp, N_Vector rr, SUNMatrix Jac,
1014 IDAMem IDA_mem, N_Vector tmp1, N_Vector tmp2,
1015 N_Vector tmp3)
1016 {
1017 realtype inc, inc_inv, yj, ypj, srur, conj, ewtj;
1018 realtype *y_data, *yp_data, *ewt_data, *cns_data = NULL;
1019 realtype *ytemp_data, *yptemp_data, *rtemp_data, *r_data, *col_j;
1020 N_Vector rtemp, ytemp, yptemp;
1021 sunindextype i, j, i1, i2, width, ngroups, group;
1022 sunindextype N, mupper, mlower;
1023 IDALsMem idals_mem;
1024 int retval = 0;
1025
1026 /* access LsMem interface structure */
1027 idals_mem = (IDALsMem) IDA_mem->ida_lmem;
1028
1029 /* access matrix dimensions */
1030 N = SUNBandMatrix_Columns(Jac);
1031 mupper = SUNBandMatrix_UpperBandwidth(Jac);
1032 mlower = SUNBandMatrix_LowerBandwidth(Jac);
1033
1034 /* Rename work vectors for use as temporary values of r, y and yp */
1035 rtemp = tmp1;
1036 ytemp = tmp2;
1037 yptemp= tmp3;
1038
1039 /* Obtain pointers to the data for all eight vectors used. */
1040 ewt_data = N_VGetArrayPointer(IDA_mem->ida_ewt);
1041 r_data = N_VGetArrayPointer(rr);
1042 y_data = N_VGetArrayPointer(yy);
1043 yp_data = N_VGetArrayPointer(yp);
1044 rtemp_data = N_VGetArrayPointer(rtemp);
1045 ytemp_data = N_VGetArrayPointer(ytemp);
1046 yptemp_data = N_VGetArrayPointer(yptemp);
1047 if (IDA_mem->ida_constraintsSet)
1048 cns_data = N_VGetArrayPointer(IDA_mem->ida_constraints);
1049
1050 /* Initialize ytemp and yptemp. */
1051 N_VScale(ONE, yy, ytemp);
1052 N_VScale(ONE, yp, yptemp);
1053
1054 /* Compute miscellaneous values for the Jacobian computation. */
1055 srur = SUNRsqrt(IDA_mem->ida_uround);
1056 width = mlower + mupper + 1;
1057 ngroups = SUNMIN(width, N);
1058
1059 /* Loop over column groups. */
1060 for (group=1; group <= ngroups; group++) {
1061
1062 /* Increment all yy[j] and yp[j] for j in this group. */
1063 for (j=group-1; j<N; j+=width) {
1064 yj = y_data[j];
1065 ypj = yp_data[j];
1066 ewtj = ewt_data[j];
1067
1068 /* Set increment inc to yj based on sqrt(uround)*abs(yj), with
1069 adjustments using ypj and ewtj if this is small, and a further
1070 adjustment to give it the same sign as hh*ypj. */
1071 inc = SUNMAX( srur * SUNMAX( SUNRabs(yj), SUNRabs(IDA_mem->ida_hh*ypj) ),
1072 ONE/ewtj );
1073 if (IDA_mem->ida_hh*ypj < ZERO) inc = -inc;
1074 inc = (yj + inc) - yj;
1075
1076 /* Adjust sign(inc) again if yj has an inequality constraint. */
1077 if (IDA_mem->ida_constraintsSet) {
1078 conj = cns_data[j];
1079 if (SUNRabs(conj) == ONE) {if((yj+inc)*conj < ZERO) inc = -inc;}
1080 else if (SUNRabs(conj) == TWO) {if((yj+inc)*conj <= ZERO) inc = -inc;}
1081 }
1082
1083 /* Increment yj and ypj. */
1084 ytemp_data[j] += inc;
1085 yptemp_data[j] += IDA_mem->ida_cj*inc;
1086 }
1087
1088 /* Call res routine with incremented arguments. */
1089 retval = IDA_mem->ida_res(tt, ytemp, yptemp, rtemp, IDA_mem->ida_user_data);
1090 idals_mem->nreDQ++;
1091 if (retval != 0) break;
1092
1093 /* Loop over the indices j in this group again. */
1094 for (j=group-1; j<N; j+=width) {
1095
1096 /* Reset ytemp and yptemp components that were perturbed. */
1097 yj = ytemp_data[j] = y_data[j];
1098 ypj = yptemp_data[j] = yp_data[j];
1099 col_j = SUNBandMatrix_Column(Jac, j);
1100 ewtj = ewt_data[j];
1101
1102 /* Set increment inc exactly as above. */
1103 inc = SUNMAX( srur * SUNMAX( SUNRabs(yj), SUNRabs(IDA_mem->ida_hh*ypj) ),
1104 ONE/ewtj );
1105 if (IDA_mem->ida_hh*ypj < ZERO) inc = -inc;
1106 inc = (yj + inc) - yj;
1107 if (IDA_mem->ida_constraintsSet) {
1108 conj = cns_data[j];
1109 if (SUNRabs(conj) == ONE) {if((yj+inc)*conj < ZERO) inc = -inc;}
1110 else if (SUNRabs(conj) == TWO) {if((yj+inc)*conj <= ZERO) inc = -inc;}
1111 }
1112
1113 /* Load the difference quotient Jacobian elements for column j */
1114 inc_inv = ONE/inc;
1115 i1 = SUNMAX(0, j-mupper);
1116 i2 = SUNMIN(j+mlower,N-1);
1117 for (i=i1; i<=i2; i++)
1118 SM_COLUMN_ELEMENT_B(col_j,i,j) = inc_inv * (rtemp_data[i]-r_data[i]);
1119 }
1120 }
1121
1122 return(retval);
1123 }
1124
1125
1126 /*---------------------------------------------------------------
1127 idaLsDQJtimes
1128
1129 This routine generates a difference quotient approximation to
1130 the matrix-vector product z = Jv, where J is the system
1131 Jacobian. The approximation is
1132 Jv = [F(t,y1,yp1) - F(t,y,yp)]/sigma,
1133 where
1134 y1 = y + sigma*v, yp1 = yp + cj*sigma*v,
1135 sigma = sqrt(Neq)*dqincfac.
1136 The return value from the call to res is saved in order to set
1137 the return flag from idaLsSolve.
1138 ---------------------------------------------------------------*/
idaLsDQJtimes(realtype tt,N_Vector yy,N_Vector yp,N_Vector rr,N_Vector v,N_Vector Jv,realtype c_j,void * ida_mem,N_Vector work1,N_Vector work2)1139 int idaLsDQJtimes(realtype tt, N_Vector yy, N_Vector yp, N_Vector rr,
1140 N_Vector v, N_Vector Jv, realtype c_j,
1141 void *ida_mem, N_Vector work1, N_Vector work2)
1142 {
1143 IDAMem IDA_mem;
1144 IDALsMem idals_mem;
1145 N_Vector y_tmp, yp_tmp;
1146 realtype sig, siginv;
1147 int iter, retval;
1148 SUNLinearSolver_ID LSID;
1149
1150 /* access IDALsMem structure */
1151 retval = idaLs_AccessLMem(ida_mem, "idaLsDQJtimes",
1152 &IDA_mem, &idals_mem);
1153 if (retval != IDALS_SUCCESS) return(retval);
1154
1155 LSID = SUNLinSolGetID(idals_mem->LS);
1156 if (LSID == SUNLINEARSOLVER_SPGMR || LSID == SUNLINEARSOLVER_SPFGMR)
1157 sig = idals_mem->nrmfac * idals_mem->dqincfac;
1158 else
1159 sig = idals_mem->dqincfac / N_VWrmsNorm(v, IDA_mem->ida_ewt);
1160
1161 /* Rename work1 and work2 for readibility */
1162 y_tmp = work1;
1163 yp_tmp = work2;
1164
1165 for (iter=0; iter<MAX_ITERS; iter++) {
1166
1167 /* Set y_tmp = yy + sig*v, yp_tmp = yp + cj*sig*v. */
1168 N_VLinearSum(sig, v, ONE, yy, y_tmp);
1169 N_VLinearSum(c_j*sig, v, ONE, yp, yp_tmp);
1170
1171 /* Call res for Jv = F(t, y_tmp, yp_tmp), and return if it failed. */
1172 retval = idals_mem->jt_res(tt, y_tmp, yp_tmp, Jv, IDA_mem->ida_user_data);
1173 idals_mem->nreDQ++;
1174 if (retval == 0) break;
1175 if (retval < 0) return(-1);
1176
1177 sig *= PT25;
1178 }
1179
1180 if (retval > 0) return(+1);
1181
1182 /* Set Jv to [Jv - rr]/sig and return. */
1183 siginv = ONE/sig;
1184 N_VLinearSum(siginv, Jv, -siginv, rr, Jv);
1185
1186 return(0);
1187 }
1188
1189
1190 /*---------------------------------------------------------------
1191 idaLsInitialize
1192
1193 This routine performs remaining initializations specific
1194 to the iterative linear solver interface (and solver itself)
1195 ---------------------------------------------------------------*/
idaLsInitialize(IDAMem IDA_mem)1196 int idaLsInitialize(IDAMem IDA_mem)
1197 {
1198 IDALsMem idals_mem;
1199 int retval;
1200
1201 /* access IDALsMem structure */
1202 if (IDA_mem->ida_lmem == NULL) {
1203 IDAProcessError(IDA_mem, IDALS_LMEM_NULL, "IDALS",
1204 "idaLsInitialize", MSG_LS_LMEM_NULL);
1205 return(IDALS_LMEM_NULL);
1206 }
1207 idals_mem = (IDALsMem) IDA_mem->ida_lmem;
1208
1209
1210 /* Test for valid combinations of matrix & Jacobian routines: */
1211 if (idals_mem->J == NULL) {
1212
1213 /* If SUNMatrix A is NULL: ensure 'jac' function pointer is NULL */
1214 idals_mem->jacDQ = SUNFALSE;
1215 idals_mem->jac = NULL;
1216 idals_mem->J_data = NULL;
1217
1218 } else if (idals_mem->jacDQ) {
1219
1220 /* If J is non-NULL, and 'jac' is not user-supplied:
1221 - if J is dense or band, ensure that our DQ approx. is used
1222 - otherwise => error */
1223 retval = 0;
1224 if (idals_mem->J->ops->getid) {
1225
1226 if ( (SUNMatGetID(idals_mem->J) == SUNMATRIX_DENSE) ||
1227 (SUNMatGetID(idals_mem->J) == SUNMATRIX_BAND) ) {
1228 idals_mem->jac = idaLsDQJac;
1229 idals_mem->J_data = IDA_mem;
1230 } else {
1231 retval++;
1232 }
1233
1234 } else {
1235 retval++;
1236 }
1237 if (retval) {
1238 IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDALS", "idaLsInitialize",
1239 "No Jacobian constructor available for SUNMatrix type");
1240 idals_mem->last_flag = IDALS_ILL_INPUT;
1241 return(IDALS_ILL_INPUT);
1242 }
1243
1244 } else {
1245
1246 /* If J is non-NULL, and 'jac' is user-supplied,
1247 reset J_data pointer (just in case) */
1248 idals_mem->J_data = IDA_mem->ida_user_data;
1249 }
1250
1251 /* reset counters */
1252 idaLsInitializeCounters(idals_mem);
1253
1254 /* Set Jacobian-related fields, based on jtimesDQ */
1255 if (idals_mem->jtimesDQ) {
1256 idals_mem->jtsetup = NULL;
1257 idals_mem->jtimes = idaLsDQJtimes;
1258 idals_mem->jt_data = IDA_mem;
1259 } else {
1260 idals_mem->jt_data = IDA_mem->ida_user_data;
1261 }
1262
1263 /* if J is NULL and psetup is not present, then idaLsSetup does
1264 not need to be called, so set the lsetup function to NULL */
1265 if ( (idals_mem->J == NULL) && (idals_mem->pset == NULL) )
1266 IDA_mem->ida_lsetup = NULL;
1267
1268 /* Call LS initialize routine */
1269 idals_mem->last_flag = SUNLinSolInitialize(idals_mem->LS);
1270 return(idals_mem->last_flag);
1271 }
1272
1273
1274 /*---------------------------------------------------------------
1275 idaLsSetup
1276
1277 This calls the Jacobian evaluation routine (if using a SUNMatrix
1278 object), updates counters, and calls the LS 'setup' routine to
1279 prepare for subsequent calls to the LS 'solve' routine.
1280 ---------------------------------------------------------------*/
idaLsSetup(IDAMem IDA_mem,N_Vector y,N_Vector yp,N_Vector r,N_Vector vt1,N_Vector vt2,N_Vector vt3)1281 int idaLsSetup(IDAMem IDA_mem, N_Vector y, N_Vector yp, N_Vector r,
1282 N_Vector vt1, N_Vector vt2, N_Vector vt3)
1283 {
1284 IDALsMem idals_mem;
1285 int retval;
1286
1287 /* access IDALsMem structure */
1288 if (IDA_mem->ida_lmem == NULL) {
1289 IDAProcessError(IDA_mem, IDALS_LMEM_NULL, "IDALS",
1290 "idaLsSetup", MSG_LS_LMEM_NULL);
1291 return(IDALS_LMEM_NULL);
1292 }
1293 idals_mem = (IDALsMem) IDA_mem->ida_lmem;
1294
1295 /* Set IDALs N_Vector pointers to inputs */
1296 idals_mem->ycur = y;
1297 idals_mem->ypcur = yp;
1298 idals_mem->rcur = r;
1299
1300 /* recompute if J if it is non-NULL */
1301 if (idals_mem->J) {
1302
1303 /* Increment nje counter. */
1304 idals_mem->nje++;
1305
1306 /* Clear the linear system matrix if necessary */
1307 if (SUNLinSolGetType(idals_mem->LS) == SUNLINEARSOLVER_DIRECT) {
1308 retval = SUNMatZero(idals_mem->J);
1309 if (retval != 0) {
1310 IDAProcessError(IDA_mem, IDALS_SUNMAT_FAIL, "IDALS",
1311 "idaLsSetup", MSG_LS_MATZERO_FAILED);
1312 idals_mem->last_flag = IDALS_SUNMAT_FAIL;
1313 return(idals_mem->last_flag);
1314 }
1315 }
1316
1317 /* Call Jacobian routine */
1318 retval = idals_mem->jac(IDA_mem->ida_tn, IDA_mem->ida_cj, y,
1319 yp, r, idals_mem->J,
1320 idals_mem->J_data, vt1, vt2, vt3);
1321 if (retval < 0) {
1322 IDAProcessError(IDA_mem, IDALS_JACFUNC_UNRECVR, "IDALS",
1323 "idaLsSetup", MSG_LS_JACFUNC_FAILED);
1324 idals_mem->last_flag = IDALS_JACFUNC_UNRECVR;
1325 return(-1);
1326 }
1327 if (retval > 0) {
1328 idals_mem->last_flag = IDALS_JACFUNC_RECVR;
1329 return(1);
1330 }
1331
1332 }
1333
1334 /* Call LS setup routine -- the LS will call idaLsPSetup if applicable */
1335 idals_mem->last_flag = SUNLinSolSetup(idals_mem->LS, idals_mem->J);
1336 return(idals_mem->last_flag);
1337 }
1338
1339
1340 /*---------------------------------------------------------------
1341 idaLsSolve
1342
1343 This routine interfaces between IDA and the generic
1344 SUNLinearSolver object LS, by setting the appropriate tolerance
1345 and scaling vectors, calling the solver, accumulating
1346 statistics from the solve for use/reporting by IDA, and scaling
1347 the result if using a non-NULL SUNMatrix and cjratio does not
1348 equal one.
1349 ---------------------------------------------------------------*/
idaLsSolve(IDAMem IDA_mem,N_Vector b,N_Vector weight,N_Vector ycur,N_Vector ypcur,N_Vector rescur)1350 int idaLsSolve(IDAMem IDA_mem, N_Vector b, N_Vector weight,
1351 N_Vector ycur, N_Vector ypcur, N_Vector rescur)
1352 {
1353 IDALsMem idals_mem;
1354 int nli_inc, retval;
1355 realtype tol, w_mean;
1356
1357 /* access IDALsMem structure */
1358 if (IDA_mem->ida_lmem == NULL) {
1359 IDAProcessError(IDA_mem, IDALS_LMEM_NULL, "IDALS",
1360 "idaLsSolve", MSG_LS_LMEM_NULL);
1361 return(IDALS_LMEM_NULL);
1362 }
1363 idals_mem = (IDALsMem) IDA_mem->ida_lmem;
1364
1365 /* If the linear solver is iterative: set convergence test constant tol,
1366 in terms of the Newton convergence test constant epsNewt and safety
1367 factors. The factor nrmlfac assures that the convergence test is
1368 applied to the WRMS norm of the residual vector, rather than the
1369 weighted L2 norm. */
1370 if (idals_mem->iterative) {
1371 tol = idals_mem->nrmfac * idals_mem->eplifac * IDA_mem->ida_epsNewt;
1372 } else {
1373 tol = ZERO;
1374 }
1375
1376 /* Set vectors ycur, ypcur and rcur for use by the Atimes and
1377 Psolve interface routines */
1378 idals_mem->ycur = ycur;
1379 idals_mem->ypcur = ypcur;
1380 idals_mem->rcur = rescur;
1381
1382 /* Set scaling vectors for LS to use (if applicable) */
1383 if (idals_mem->LS->ops->setscalingvectors) {
1384 retval = SUNLinSolSetScalingVectors(idals_mem->LS, weight, weight);
1385 if (retval != SUNLS_SUCCESS) {
1386 IDAProcessError(IDA_mem, IDALS_SUNLS_FAIL, "IDALS", "idaLsSolve",
1387 "Error in calling SUNLinSolSetScalingVectors");
1388 idals_mem->last_flag = IDALS_SUNLS_FAIL;
1389 return(idals_mem->last_flag);
1390 }
1391
1392 /* If solver is iterative and does not support scaling vectors, update the
1393 tolerance in an attempt to account for weight vector. We make the
1394 following assumptions:
1395 1. w_i = w_mean, for i=0,...,n-1 (i.e. the weights are homogeneous)
1396 2. the linear solver uses a basic 2-norm to measure convergence
1397 Hence (using the notation from sunlinsol_spgmr.h, with S = diag(w)),
1398 || bbar - Abar xbar ||_2 < tol
1399 <=> || S b - S A x ||_2 < tol
1400 <=> || S (b - A x) ||_2 < tol
1401 <=> \sum_{i=0}^{n-1} (w_i (b - A x)_i)^2 < tol^2
1402 <=> w_mean^2 \sum_{i=0}^{n-1} (b - A x_i)^2 < tol^2
1403 <=> \sum_{i=0}^{n-1} (b - A x_i)^2 < tol^2 / w_mean^2
1404 <=> || b - A x ||_2 < tol / w_mean
1405 So we compute w_mean = ||w||_RMS and scale the desired tolerance accordingly. */
1406 } else if (idals_mem->iterative) {
1407
1408 N_VConst(ONE, idals_mem->x);
1409 w_mean = N_VWrmsNorm(weight, idals_mem->x);
1410 tol /= w_mean;
1411
1412 }
1413
1414 /* Set initial guess x = 0 to LS */
1415 N_VConst(ZERO, idals_mem->x);
1416
1417 /* If a user-provided jtsetup routine is supplied, call that here */
1418 if (idals_mem->jtsetup) {
1419 idals_mem->last_flag = idals_mem->jtsetup(IDA_mem->ida_tn, ycur, ypcur, rescur,
1420 IDA_mem->ida_cj, idals_mem->jt_data);
1421 idals_mem->njtsetup++;
1422 if (idals_mem->last_flag != 0) {
1423 IDAProcessError(IDA_mem, retval, "IDALS",
1424 "idaLsSolve", MSG_LS_JTSETUP_FAILED);
1425 return(idals_mem->last_flag);
1426 }
1427 }
1428
1429 /* Call solver */
1430 retval = SUNLinSolSolve(idals_mem->LS, idals_mem->J,
1431 idals_mem->x, b, tol);
1432
1433 /* Copy appropriate result to b (depending on solver type) */
1434 if (idals_mem->iterative) {
1435
1436 /* Retrieve solver statistics */
1437 nli_inc = SUNLinSolNumIters(idals_mem->LS);
1438
1439 /* Copy x (or preconditioned residual vector if no iterations required) to b */
1440 if (nli_inc == 0) N_VScale(ONE, SUNLinSolResid(idals_mem->LS), b);
1441 else N_VScale(ONE, idals_mem->x, b);
1442
1443 /* Increment nli counter */
1444 idals_mem->nli += nli_inc;
1445
1446 } else {
1447
1448 /* Copy x to b */
1449 N_VScale(ONE, idals_mem->x, b);
1450
1451 }
1452
1453 /* If using a direct or matrix-iterative solver, scale the correction to
1454 account for change in cj */
1455 if (idals_mem->scalesol && (IDA_mem->ida_cjratio != ONE))
1456 N_VScale(TWO/(ONE + IDA_mem->ida_cjratio), b, b);
1457
1458 /* Increment ncfl counter */
1459 if (retval != SUNLS_SUCCESS) idals_mem->ncfl++;
1460
1461 /* Interpret solver return value */
1462 idals_mem->last_flag = retval;
1463
1464 switch(retval) {
1465
1466 case SUNLS_SUCCESS:
1467 return(0);
1468 break;
1469 case SUNLS_RES_REDUCED:
1470 case SUNLS_CONV_FAIL:
1471 case SUNLS_PSOLVE_FAIL_REC:
1472 case SUNLS_PACKAGE_FAIL_REC:
1473 case SUNLS_QRFACT_FAIL:
1474 case SUNLS_LUFACT_FAIL:
1475 return(1);
1476 break;
1477 case SUNLS_MEM_NULL:
1478 case SUNLS_ILL_INPUT:
1479 case SUNLS_MEM_FAIL:
1480 case SUNLS_GS_FAIL:
1481 case SUNLS_QRSOL_FAIL:
1482 return(-1);
1483 break;
1484 case SUNLS_PACKAGE_FAIL_UNREC:
1485 IDAProcessError(IDA_mem, SUNLS_PACKAGE_FAIL_UNREC, "IDALS",
1486 "idaLsSolve",
1487 "Failure in SUNLinSol external package");
1488 return(-1);
1489 break;
1490 case SUNLS_PSOLVE_FAIL_UNREC:
1491 IDAProcessError(IDA_mem, SUNLS_PSOLVE_FAIL_UNREC, "IDALS",
1492 "idaLsSolve", MSG_LS_PSOLVE_FAILED);
1493 return(-1);
1494 break;
1495 }
1496
1497 return(0);
1498 }
1499
1500
1501 /*---------------------------------------------------------------
1502 idaLsPerf: accumulates performance statistics information
1503 for IDA
1504 ---------------------------------------------------------------*/
idaLsPerf(IDAMem IDA_mem,int perftask)1505 int idaLsPerf(IDAMem IDA_mem, int perftask)
1506 {
1507 IDALsMem idals_mem;
1508 realtype rcfn, rcfl;
1509 long int nstd, nnid;
1510 booleantype lcfn, lcfl;
1511
1512 /* access IDALsMem structure */
1513 if (IDA_mem->ida_lmem == NULL) {
1514 IDAProcessError(IDA_mem, IDALS_LMEM_NULL, "IDALS",
1515 "idaLsPerf", MSG_LS_LMEM_NULL);
1516 return(IDALS_LMEM_NULL);
1517 }
1518 idals_mem = (IDALsMem) IDA_mem->ida_lmem;
1519
1520 /* when perftask == 0, store current performance statistics */
1521 if (perftask == 0) {
1522 idals_mem->nst0 = IDA_mem->ida_nst;
1523 idals_mem->nni0 = IDA_mem->ida_nni;
1524 idals_mem->ncfn0 = IDA_mem->ida_ncfn;
1525 idals_mem->ncfl0 = idals_mem->ncfl;
1526 idals_mem->nwarn = 0;
1527 return(0);
1528 }
1529
1530 /* Compute statistics since last call
1531
1532 Note: the performance monitor that checked whether the average
1533 number of linear iterations was too close to maxl has been
1534 removed, since the 'maxl' value is no longer owned by the
1535 IDALs interface.
1536 */
1537 nstd = IDA_mem->ida_nst - idals_mem->nst0;
1538 nnid = IDA_mem->ida_nni - idals_mem->nni0;
1539 if (nstd == 0 || nnid == 0) return(0);
1540
1541 rcfn = ((realtype) (IDA_mem->ida_ncfn - idals_mem->ncfn0)) / ((realtype) nstd);
1542 rcfl = ((realtype) (idals_mem->ncfl - idals_mem->ncfl0)) / ((realtype) nnid);
1543 lcfn = (rcfn > PT9);
1544 lcfl = (rcfl > PT9);
1545 if (!(lcfn || lcfl)) return(0);
1546 idals_mem->nwarn++;
1547 if (idals_mem->nwarn > 10) return(1);
1548 if (lcfn)
1549 IDAProcessError(IDA_mem, IDA_WARNING, "IDALS", "idaLsPerf",
1550 MSG_LS_CFN_WARN, IDA_mem->ida_tn, rcfn);
1551 if (lcfl)
1552 IDAProcessError(IDA_mem, IDA_WARNING, "IDALS", "idaLsPerf",
1553 MSG_LS_CFL_WARN, IDA_mem->ida_tn, rcfl);
1554 return(0);
1555 }
1556
1557
1558 /*---------------------------------------------------------------
1559 idaLsFree frees memory associates with the IDALs system
1560 solver interface.
1561 ---------------------------------------------------------------*/
idaLsFree(IDAMem IDA_mem)1562 int idaLsFree(IDAMem IDA_mem)
1563 {
1564 IDALsMem idals_mem;
1565
1566 /* Return immediately if IDA_mem or IDA_mem->ida_lmem are NULL */
1567 if (IDA_mem == NULL) return (IDALS_SUCCESS);
1568 if (IDA_mem->ida_lmem == NULL) return(IDALS_SUCCESS);
1569 idals_mem = (IDALsMem) IDA_mem->ida_lmem;
1570
1571 /* Free N_Vector memory */
1572 if (idals_mem->ytemp) {
1573 N_VDestroy(idals_mem->ytemp);
1574 idals_mem->ytemp = NULL;
1575 }
1576 if (idals_mem->yptemp) {
1577 N_VDestroy(idals_mem->yptemp);
1578 idals_mem->yptemp = NULL;
1579 }
1580 if (idals_mem->x) {
1581 N_VDestroy(idals_mem->x);
1582 idals_mem->x = NULL;
1583 }
1584
1585 /* Nullify other N_Vector pointers */
1586 idals_mem->ycur = NULL;
1587 idals_mem->ypcur = NULL;
1588 idals_mem->rcur = NULL;
1589
1590 /* Nullify SUNMatrix pointer */
1591 idals_mem->J = NULL;
1592
1593 /* Free preconditioner memory (if applicable) */
1594 if (idals_mem->pfree) idals_mem->pfree(IDA_mem);
1595
1596 /* free IDALs interface structure */
1597 free(IDA_mem->ida_lmem);
1598
1599 return(IDALS_SUCCESS);
1600 }
1601
1602
1603 /*---------------------------------------------------------------
1604 idaLsInitializeCounters resets all counters from an
1605 IDALsMem structure.
1606 ---------------------------------------------------------------*/
idaLsInitializeCounters(IDALsMem idals_mem)1607 int idaLsInitializeCounters(IDALsMem idals_mem)
1608 {
1609 idals_mem->nje = 0;
1610 idals_mem->nreDQ = 0;
1611 idals_mem->npe = 0;
1612 idals_mem->nli = 0;
1613 idals_mem->nps = 0;
1614 idals_mem->ncfl = 0;
1615 idals_mem->njtsetup = 0;
1616 idals_mem->njtimes = 0;
1617 return(0);
1618 }
1619
1620
1621 /*---------------------------------------------------------------
1622 idaLs_AccessLMem
1623
1624 This routine unpacks the IDA_mem and idals_mem structures from
1625 the void* ida_mem pointer. If either is missing it returns
1626 IDALS_MEM_NULL or IDALS_LMEM_NULL.
1627 ---------------------------------------------------------------*/
idaLs_AccessLMem(void * ida_mem,const char * fname,IDAMem * IDA_mem,IDALsMem * idals_mem)1628 int idaLs_AccessLMem(void* ida_mem, const char* fname,
1629 IDAMem* IDA_mem, IDALsMem* idals_mem)
1630 {
1631 if (ida_mem==NULL) {
1632 IDAProcessError(NULL, IDALS_MEM_NULL, "IDALS",
1633 fname, MSG_LS_IDAMEM_NULL);
1634 return(IDALS_MEM_NULL);
1635 }
1636 *IDA_mem = (IDAMem) ida_mem;
1637 if ((*IDA_mem)->ida_lmem==NULL) {
1638 IDAProcessError(*IDA_mem, IDALS_LMEM_NULL, "IDALS",
1639 fname, MSG_LS_LMEM_NULL);
1640 return(IDALS_LMEM_NULL);
1641 }
1642 *idals_mem = (IDALsMem) (*IDA_mem)->ida_lmem;
1643 return(IDALS_SUCCESS);
1644 }
1645
1646
1647 /*---------------------------------------------------------------
1648 EOF
1649 ---------------------------------------------------------------*/
1650