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