1 /*
2  * -----------------------------------------------------------------
3  * Programmer(s): Daniel R. Reynolds @ SMU
4  *    Michael Wittman, Alan C. Hindmarsh, Radu Serban, and
5  *    Aaron Collier @ LLNL
6  * -----------------------------------------------------------------
7  * SUNDIALS Copyright Start
8  * Copyright (c) 2002-2021, Lawrence Livermore National Security
9  * and Southern Methodist University.
10  * All rights reserved.
11  *
12  * See the top-level LICENSE and NOTICE files for details.
13  *
14  * SPDX-License-Identifier: BSD-3-Clause
15  * SUNDIALS Copyright End
16  * -----------------------------------------------------------------
17  * This file contains implementations of routines for a
18  * band-block-diagonal preconditioner, i.e. a block-diagonal
19  * matrix with banded blocks, for use with CVODE, the CVLS linear
20  * solver interface, and the MPI-parallel implementation of NVECTOR.
21  * -----------------------------------------------------------------
22  */
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 
27 #include "cvode_impl.h"
28 #include "cvode_bbdpre_impl.h"
29 #include "cvode_ls_impl.h"
30 #include <sundials/sundials_math.h>
31 #include <nvector/nvector_serial.h>
32 
33 #define MIN_INC_MULT RCONST(1000.0)
34 #define ZERO         RCONST(0.0)
35 #define ONE          RCONST(1.0)
36 #define TWO          RCONST(2.0)
37 
38 /* Prototypes of functions CVBBDPrecSetup and CVBBDPrecSolve */
39 static int CVBBDPrecSetup(realtype t, N_Vector y, N_Vector fy,
40                           booleantype jok, booleantype *jcurPtr,
41                           realtype gamma, void *bbd_data);
42 static int CVBBDPrecSolve(realtype t, N_Vector y, N_Vector fy,
43                           N_Vector r, N_Vector z,
44                           realtype gamma, realtype delta,
45                           int lr, void *bbd_data);
46 
47 /* Prototype for CVBBDPrecFree */
48 static int CVBBDPrecFree(CVodeMem cv_mem);
49 
50 /* Prototype for difference quotient Jacobian calculation routine */
51 static int CVBBDDQJac(CVBBDPrecData pdata, realtype t,
52                       N_Vector y, N_Vector gy,
53                       N_Vector ytemp, N_Vector gtemp);
54 
55 /*-----------------------------------------------------------------
56   User-Callable Functions: initialization, reinit and free
57   -----------------------------------------------------------------*/
CVBBDPrecInit(void * cvode_mem,sunindextype Nlocal,sunindextype mudq,sunindextype mldq,sunindextype mukeep,sunindextype mlkeep,realtype dqrely,CVLocalFn gloc,CVCommFn cfn)58 int CVBBDPrecInit(void *cvode_mem, sunindextype Nlocal,
59                   sunindextype mudq, sunindextype mldq,
60                   sunindextype mukeep, sunindextype mlkeep,
61                   realtype dqrely, CVLocalFn gloc, CVCommFn cfn)
62 {
63   CVodeMem cv_mem;
64   CVLsMem cvls_mem;
65   CVBBDPrecData pdata;
66   sunindextype muk, mlk, storage_mu, lrw1, liw1;
67   long int lrw, liw;
68   int flag;
69 
70   if (cvode_mem == NULL) {
71     cvProcessError(NULL, CVLS_MEM_NULL, "CVBBDPRE",
72                    "CVBBDPrecInit", MSGBBD_MEM_NULL);
73     return(CVLS_MEM_NULL);
74   }
75   cv_mem = (CVodeMem) cvode_mem;
76 
77   /* Test if the CVLS linear solver interface has been created */
78   if (cv_mem->cv_lmem == NULL) {
79     cvProcessError(cv_mem, CVLS_LMEM_NULL, "CVBBDPRE",
80                    "CVBBDPrecInit", MSGBBD_LMEM_NULL);
81     return(CVLS_LMEM_NULL);
82   }
83   cvls_mem = (CVLsMem) cv_mem->cv_lmem;
84 
85   /* Test compatibility of NVECTOR package with the BBD preconditioner */
86   if(cv_mem->cv_tempv->ops->nvgetarraypointer == NULL) {
87     cvProcessError(cv_mem, CVLS_ILL_INPUT, "CVBBDPRE",
88                    "CVBBDPrecInit", MSGBBD_BAD_NVECTOR);
89     return(CVLS_ILL_INPUT);
90   }
91 
92   /* Allocate data memory */
93   pdata = NULL;
94   pdata = (CVBBDPrecData) malloc(sizeof *pdata);
95   if (pdata == NULL) {
96     cvProcessError(cv_mem, CVLS_MEM_FAIL, "CVBBDPRE",
97                    "CVBBDPrecInit", MSGBBD_MEM_FAIL);
98     return(CVLS_MEM_FAIL);
99   }
100 
101   /* Set pointers to gloc and cfn; load half-bandwidths */
102   pdata->cvode_mem = cvode_mem;
103   pdata->gloc = gloc;
104   pdata->cfn = cfn;
105   pdata->mudq = SUNMIN(Nlocal-1, SUNMAX(0,mudq));
106   pdata->mldq = SUNMIN(Nlocal-1, SUNMAX(0,mldq));
107   muk = SUNMIN(Nlocal-1, SUNMAX(0,mukeep));
108   mlk = SUNMIN(Nlocal-1, SUNMAX(0,mlkeep));
109   pdata->mukeep = muk;
110   pdata->mlkeep = mlk;
111 
112   /* Allocate memory for saved Jacobian */
113   pdata->savedJ = SUNBandMatrixStorage(Nlocal, muk, mlk, muk);
114   if (pdata->savedJ == NULL) {
115     free(pdata); pdata = NULL;
116     cvProcessError(cv_mem, CVLS_MEM_FAIL, "CVBBDPRE",
117                    "CVBBDPrecInit", MSGBBD_MEM_FAIL);
118     return(CVLS_MEM_FAIL);
119   }
120 
121   /* Allocate memory for preconditioner matrix */
122   storage_mu = SUNMIN(Nlocal-1, muk + mlk);
123   pdata->savedP = NULL;
124   pdata->savedP = SUNBandMatrixStorage(Nlocal, muk, mlk, storage_mu);
125   if (pdata->savedP == NULL) {
126     SUNMatDestroy(pdata->savedJ);
127     free(pdata); pdata = NULL;
128     cvProcessError(cv_mem, CVLS_MEM_FAIL, "CVBBDPRE",
129                    "CVBBDPrecInit", MSGBBD_MEM_FAIL);
130     return(CVLS_MEM_FAIL);
131   }
132 
133   /* Allocate memory for temporary N_Vectors */
134   pdata->zlocal = NULL;
135   pdata->zlocal = N_VNewEmpty_Serial(Nlocal);
136   if (pdata->zlocal == NULL) {
137     SUNMatDestroy(pdata->savedP);
138     SUNMatDestroy(pdata->savedJ);
139     free(pdata); pdata = NULL;
140     cvProcessError(cv_mem, CVLS_MEM_FAIL, "CVBBDPRE",
141                    "CVBBDPrecInit", MSGBBD_MEM_FAIL);
142     return(CVLS_MEM_FAIL);
143   }
144   pdata->rlocal = NULL;
145   pdata->rlocal = N_VNewEmpty_Serial(Nlocal);
146   if (pdata->rlocal == NULL) {
147     N_VDestroy(pdata->zlocal);
148     SUNMatDestroy(pdata->savedP);
149     SUNMatDestroy(pdata->savedJ);
150     free(pdata); pdata = NULL;
151     cvProcessError(cv_mem, CVLS_MEM_FAIL, "CVBBDPRE",
152                    "CVBBDPrecInit", MSGBBD_MEM_FAIL);
153     return(CVLS_MEM_FAIL);
154   }
155   pdata->tmp1 = NULL;
156   pdata->tmp1 = N_VClone(cv_mem->cv_tempv);
157   if (pdata->tmp1 == NULL) {
158     N_VDestroy(pdata->zlocal);
159     N_VDestroy(pdata->rlocal);
160     SUNMatDestroy(pdata->savedP);
161     SUNMatDestroy(pdata->savedJ);
162     free(pdata); pdata = NULL;
163     cvProcessError(cv_mem, CVLS_MEM_FAIL, "CVBBDPRE",
164                    "CVBBDPrecInit", MSGBBD_MEM_FAIL);
165     return(CVLS_MEM_FAIL);
166   }
167   pdata->tmp2 = NULL;
168   pdata->tmp2 = N_VClone(cv_mem->cv_tempv);
169   if (pdata->tmp2 == NULL) {
170     N_VDestroy(pdata->tmp1);
171     N_VDestroy(pdata->zlocal);
172     N_VDestroy(pdata->rlocal);
173     SUNMatDestroy(pdata->savedP);
174     SUNMatDestroy(pdata->savedJ);
175     free(pdata); pdata = NULL;
176     cvProcessError(cv_mem, CVLS_MEM_FAIL, "CVBBDPRE",
177                    "CVBBDPrecInit", MSGBBD_MEM_FAIL);
178     return(CVLS_MEM_FAIL);
179   }
180   pdata->tmp3 = NULL;
181   pdata->tmp3 = N_VClone(cv_mem->cv_tempv);
182   if (pdata->tmp3 == NULL) {
183     N_VDestroy(pdata->tmp1);
184     N_VDestroy(pdata->tmp2);
185     N_VDestroy(pdata->zlocal);
186     N_VDestroy(pdata->rlocal);
187     SUNMatDestroy(pdata->savedP);
188     SUNMatDestroy(pdata->savedJ);
189     free(pdata); pdata = NULL;
190     cvProcessError(cv_mem, CVLS_MEM_FAIL, "CVBBDPRE",
191                    "CVBBDPrecInit", MSGBBD_MEM_FAIL);
192     return(CVLS_MEM_FAIL);
193   }
194 
195   /* Allocate memory for banded linear solver */
196   pdata->LS = NULL;
197   pdata->LS = SUNLinSol_Band(pdata->rlocal, pdata->savedP);
198   if (pdata->LS == NULL) {
199     N_VDestroy(pdata->tmp1);
200     N_VDestroy(pdata->tmp2);
201     N_VDestroy(pdata->tmp3);
202     N_VDestroy(pdata->zlocal);
203     N_VDestroy(pdata->rlocal);
204     SUNMatDestroy(pdata->savedP);
205     SUNMatDestroy(pdata->savedJ);
206     free(pdata); pdata = NULL;
207     cvProcessError(cv_mem, CVLS_MEM_FAIL, "CVBBDPRE",
208                    "CVBBDPrecInit", MSGBBD_MEM_FAIL);
209     return(CVLS_MEM_FAIL);
210   }
211 
212   /* initialize band linear solver object */
213   flag = SUNLinSolInitialize(pdata->LS);
214   if (flag != SUNLS_SUCCESS) {
215     N_VDestroy(pdata->tmp1);
216     N_VDestroy(pdata->tmp2);
217     N_VDestroy(pdata->tmp3);
218     N_VDestroy(pdata->zlocal);
219     N_VDestroy(pdata->rlocal);
220     SUNMatDestroy(pdata->savedP);
221     SUNMatDestroy(pdata->savedJ);
222     SUNLinSolFree(pdata->LS);
223     free(pdata); pdata = NULL;
224     cvProcessError(cv_mem, CVLS_SUNLS_FAIL, "CVBBDPRE",
225                    "CVBBDPrecInit", MSGBBD_SUNLS_FAIL);
226     return(CVLS_SUNLS_FAIL);
227   }
228 
229   /* Set pdata->dqrely based on input dqrely (0 implies default). */
230   pdata->dqrely = (dqrely > ZERO) ?
231     dqrely : SUNRsqrt(cv_mem->cv_uround);
232 
233   /* Store Nlocal to be used in CVBBDPrecSetup */
234   pdata->n_local = Nlocal;
235 
236   /* Set work space sizes and initialize nge */
237   pdata->rpwsize = 0;
238   pdata->ipwsize = 0;
239   if (cv_mem->cv_tempv->ops->nvspace) {
240     N_VSpace(cv_mem->cv_tempv, &lrw1, &liw1);
241     pdata->rpwsize += 3*lrw1;
242     pdata->ipwsize += 3*liw1;
243   }
244   if (pdata->rlocal->ops->nvspace) {
245     N_VSpace(pdata->rlocal, &lrw1, &liw1);
246     pdata->rpwsize += 2*lrw1;
247     pdata->ipwsize += 2*liw1;
248   }
249   if (pdata->savedJ->ops->space) {
250     flag = SUNMatSpace(pdata->savedJ, &lrw, &liw);
251     pdata->rpwsize += lrw;
252     pdata->ipwsize += liw;
253   }
254   if (pdata->savedP->ops->space) {
255     flag = SUNMatSpace(pdata->savedP, &lrw, &liw);
256     pdata->rpwsize += lrw;
257     pdata->ipwsize += liw;
258   }
259   if (pdata->LS->ops->space) {
260     flag = SUNLinSolSpace(pdata->LS, &lrw, &liw);
261     pdata->rpwsize += lrw;
262     pdata->ipwsize += liw;
263   }
264   pdata->nge = 0;
265 
266   /* make sure P_data is free from any previous allocations */
267   if (cvls_mem->pfree)
268     cvls_mem->pfree(cv_mem);
269 
270   /* Point to the new P_data field in the LS memory */
271   cvls_mem->P_data = pdata;
272 
273   /* Attach the pfree function */
274   cvls_mem->pfree = CVBBDPrecFree;
275 
276   /* Attach preconditioner solve and setup functions */
277   flag = CVodeSetPreconditioner(cvode_mem,
278                                 CVBBDPrecSetup,
279                                 CVBBDPrecSolve);
280   return(flag);
281 }
282 
283 
CVBBDPrecReInit(void * cvode_mem,sunindextype mudq,sunindextype mldq,realtype dqrely)284 int CVBBDPrecReInit(void *cvode_mem, sunindextype mudq,
285                     sunindextype mldq, realtype dqrely)
286 {
287   CVodeMem cv_mem;
288   CVLsMem cvls_mem;
289   CVBBDPrecData pdata;
290   sunindextype Nlocal;
291 
292   if (cvode_mem == NULL) {
293     cvProcessError(NULL, CVLS_MEM_NULL, "CVBBDPRE",
294                    "CVBBDPrecReInit", MSGBBD_MEM_NULL);
295     return(CVLS_MEM_NULL);
296   }
297   cv_mem = (CVodeMem) cvode_mem;
298 
299   /* Test if the LS linear solver interface has been created */
300   if (cv_mem->cv_lmem == NULL) {
301     cvProcessError(cv_mem, CVLS_LMEM_NULL, "CVBBDPRE",
302                    "CVBBDPrecReInit", MSGBBD_LMEM_NULL);
303     return(CVLS_LMEM_NULL);
304   }
305   cvls_mem = (CVLsMem) cv_mem->cv_lmem;
306 
307   /* Test if the preconditioner data is non-NULL */
308   if (cvls_mem->P_data == NULL) {
309     cvProcessError(cv_mem, CVLS_PMEM_NULL, "CVBBDPRE",
310                    "CVBBDPrecReInit", MSGBBD_PMEM_NULL);
311     return(CVLS_PMEM_NULL);
312   }
313   pdata = (CVBBDPrecData) cvls_mem->P_data;
314 
315   /* Load half-bandwidths */
316   Nlocal = pdata->n_local;
317   pdata->mudq = SUNMIN(Nlocal-1, SUNMAX(0,mudq));
318   pdata->mldq = SUNMIN(Nlocal-1, SUNMAX(0,mldq));
319 
320   /* Set pdata->dqrely based on input dqrely (0 implies default). */
321   pdata->dqrely = (dqrely > ZERO) ?
322     dqrely : SUNRsqrt(cv_mem->cv_uround);
323 
324   /* Re-initialize nge */
325   pdata->nge = 0;
326 
327   return(CVLS_SUCCESS);
328 }
329 
330 
CVBBDPrecGetWorkSpace(void * cvode_mem,long int * lenrwBBDP,long int * leniwBBDP)331 int CVBBDPrecGetWorkSpace(void *cvode_mem,
332                           long int *lenrwBBDP,
333                           long int *leniwBBDP)
334 {
335   CVodeMem cv_mem;
336   CVLsMem cvls_mem;
337   CVBBDPrecData pdata;
338 
339   if (cvode_mem == NULL) {
340     cvProcessError(NULL, CVLS_MEM_NULL, "CVBBDPRE",
341                    "CVBBDPrecGetWorkSpace", MSGBBD_MEM_NULL);
342     return(CVLS_MEM_NULL);
343   }
344   cv_mem = (CVodeMem) cvode_mem;
345 
346   if (cv_mem->cv_lmem == NULL) {
347     cvProcessError(cv_mem, CVLS_LMEM_NULL, "CVBBDPRE",
348                    "CVBBDPrecGetWorkSpace", MSGBBD_LMEM_NULL);
349     return(CVLS_LMEM_NULL);
350   }
351   cvls_mem = (CVLsMem) cv_mem->cv_lmem;
352 
353   if (cvls_mem->P_data == NULL) {
354     cvProcessError(cv_mem, CVLS_PMEM_NULL, "CVBBDPRE",
355                    "CVBBDPrecGetWorkSpace", MSGBBD_PMEM_NULL);
356     return(CVLS_PMEM_NULL);
357   }
358   pdata = (CVBBDPrecData) cvls_mem->P_data;
359 
360   *lenrwBBDP = pdata->rpwsize;
361   *leniwBBDP = pdata->ipwsize;
362 
363   return(CVLS_SUCCESS);
364 }
365 
366 
CVBBDPrecGetNumGfnEvals(void * cvode_mem,long int * ngevalsBBDP)367 int CVBBDPrecGetNumGfnEvals(void *cvode_mem,
368                             long int *ngevalsBBDP)
369 {
370   CVodeMem cv_mem;
371   CVLsMem cvls_mem;
372   CVBBDPrecData pdata;
373 
374   if (cvode_mem == NULL) {
375     cvProcessError(NULL, CVLS_MEM_NULL, "CVBBDPRE",
376                    "CVBBDPrecGetNumGfnEvals", MSGBBD_MEM_NULL);
377     return(CVLS_MEM_NULL);
378   }
379   cv_mem = (CVodeMem) cvode_mem;
380 
381   if (cv_mem->cv_lmem == NULL) {
382     cvProcessError(cv_mem, CVLS_LMEM_NULL, "CVBBDPRE",
383                    "CVBBDPrecGetNumGfnEvals", MSGBBD_LMEM_NULL);
384     return(CVLS_LMEM_NULL);
385   }
386   cvls_mem = (CVLsMem) cv_mem->cv_lmem;
387 
388   if (cvls_mem->P_data == NULL) {
389     cvProcessError(cv_mem, CVLS_PMEM_NULL, "CVBBDPRE",
390                    "CVBBDPrecGetNumGfnEvals", MSGBBD_PMEM_NULL);
391     return(CVLS_PMEM_NULL);
392   }
393   pdata = (CVBBDPrecData) cvls_mem->P_data;
394 
395   *ngevalsBBDP = pdata->nge;
396 
397   return(CVLS_SUCCESS);
398 }
399 
400 
401 /*-----------------------------------------------------------------
402   Function : CVBBDPrecSetup
403   -----------------------------------------------------------------
404   CVBBDPrecSetup generates and factors a banded block of the
405   preconditioner matrix on each processor, via calls to the
406   user-supplied gloc and cfn functions. It uses difference
407   quotient approximations to the Jacobian elements.
408 
409   CVBBDPrecSetup calculates a new J,if necessary, then calculates
410   P = I - gamma*J, and does an LU factorization of P.
411 
412   The parameters of CVBBDPrecSetup used here are as follows:
413 
414   t       is the current value of the independent variable.
415 
416   y       is the current value of the dependent variable vector,
417           namely the predicted value of y(t).
418 
419   fy      is the vector f(t,y).
420 
421   jok     is an input flag indicating whether Jacobian-related
422           data needs to be recomputed, as follows:
423             jok == SUNFALSE means recompute Jacobian-related data
424                    from scratch.
425             jok == SUNTRUE  means that Jacobian data from the
426                    previous cvBBDPrecSetup call can be reused
427                    (with the current value of gamma).
428           A cvBBDPrecSetup call with jok == SUNTRUE should only occur
429           after a call with jok == SUNFALSE.
430 
431   jcurPtr is a pointer to an output integer flag which is
432           set by cvBBDPrecSetup as follows:
433             *jcurPtr = SUNTRUE if Jacobian data was recomputed.
434             *jcurPtr = SUNFALSE if Jacobian data was not recomputed,
435                        but saved data was reused.
436 
437   gamma   is the scalar appearing in the Newton matrix.
438 
439   bbd_data is a pointer to the preconditioner data set by
440            CVBBDPrecInit
441 
442   Return value:
443   The value returned by this CVBBDPrecSetup function is the int
444     0  if successful,
445     1  for a recoverable error (step will be retried).
446   -----------------------------------------------------------------*/
CVBBDPrecSetup(realtype t,N_Vector y,N_Vector fy,booleantype jok,booleantype * jcurPtr,realtype gamma,void * bbd_data)447 static int CVBBDPrecSetup(realtype t, N_Vector y, N_Vector fy,
448                           booleantype jok, booleantype *jcurPtr,
449                           realtype gamma, void *bbd_data)
450 {
451   CVBBDPrecData pdata;
452   CVodeMem cv_mem;
453   int retval;
454 
455   pdata = (CVBBDPrecData) bbd_data;
456   cv_mem = (CVodeMem) pdata->cvode_mem;
457 
458   /* If jok = SUNTRUE, use saved copy of J */
459   if (jok) {
460     *jcurPtr = SUNFALSE;
461     retval = SUNMatCopy(pdata->savedJ, pdata->savedP);
462     if (retval < 0) {
463       cvProcessError(cv_mem, -1, "CVBBDPRE",
464                      "CVBBDPrecSetup", MSGBBD_SUNMAT_FAIL);
465       return(-1);
466     }
467     if (retval > 0) {
468       return(1);
469     }
470 
471   /* Otherwise call CVBBDDQJac for new J value */
472   } else {
473 
474     *jcurPtr = SUNTRUE;
475     retval = SUNMatZero(pdata->savedJ);
476     if (retval < 0) {
477       cvProcessError(cv_mem, -1, "CVBBDPRE",
478                      "CVBBDPrecSetup", MSGBBD_SUNMAT_FAIL);
479       return(-1);
480     }
481     if (retval > 0) {
482       return(1);
483     }
484 
485     retval = CVBBDDQJac(pdata, t, y, pdata->tmp1,
486                         pdata->tmp2, pdata->tmp3);
487     if (retval < 0) {
488       cvProcessError(cv_mem, -1, "CVBBDPRE", "CVBBDPrecSetup",
489                      MSGBBD_FUNC_FAILED);
490       return(-1);
491     }
492     if (retval > 0) {
493       return(1);
494     }
495 
496     retval = SUNMatCopy(pdata->savedJ, pdata->savedP);
497     if (retval < 0) {
498       cvProcessError(cv_mem, -1, "CVBBDPRE",
499                      "CVBBDPrecSetup", MSGBBD_SUNMAT_FAIL);
500       return(-1);
501     }
502     if (retval > 0) {
503       return(1);
504     }
505 
506   }
507 
508   /* Scale and add I to get P = I - gamma*J */
509   retval = SUNMatScaleAddI(-gamma, pdata->savedP);
510   if (retval) {
511     cvProcessError(cv_mem, -1, "CVBBDPRE",
512                    "CVBBDPrecSetup", MSGBBD_SUNMAT_FAIL);
513     return(-1);
514   }
515 
516   /* Do LU factorization of matrix and return error flag */
517   retval = SUNLinSolSetup_Band(pdata->LS, pdata->savedP);
518   return(retval);
519 }
520 
521 
522 /*-----------------------------------------------------------------
523   Function : CVBBDPrecSolve
524   -----------------------------------------------------------------
525   CVBBDPrecSolve solves a linear system P z = r, with the
526   band-block-diagonal preconditioner matrix P generated and
527   factored by CVBBDPrecSetup.
528 
529   The parameters of CVBBDPrecSolve used here are as follows:
530 
531   r is the right-hand side vector of the linear system.
532 
533   bbd_data is a pointer to the preconditioner data set by
534     CVBBDPrecInit.
535 
536   z is the output vector computed by CVBBDPrecSolve.
537 
538   The value returned by the CVBBDPrecSolve function is always 0,
539   indicating success.
540   -----------------------------------------------------------------*/
CVBBDPrecSolve(realtype t,N_Vector y,N_Vector fy,N_Vector r,N_Vector z,realtype gamma,realtype delta,int lr,void * bbd_data)541 static int CVBBDPrecSolve(realtype t, N_Vector y, N_Vector fy,
542                           N_Vector r, N_Vector z,
543                           realtype gamma, realtype delta,
544                           int lr, void *bbd_data)
545 {
546   int retval;
547   CVBBDPrecData pdata;
548 
549   pdata = (CVBBDPrecData) bbd_data;
550 
551   /* Attach local data arrays for r and z to rlocal and zlocal */
552   N_VSetArrayPointer(N_VGetArrayPointer(r), pdata->rlocal);
553   N_VSetArrayPointer(N_VGetArrayPointer(z), pdata->zlocal);
554 
555   /* Call banded solver object to do the work */
556   retval = SUNLinSolSolve(pdata->LS, pdata->savedP, pdata->zlocal,
557                           pdata->rlocal, ZERO);
558 
559   /* Detach local data arrays from rlocal and zlocal */
560   N_VSetArrayPointer(NULL, pdata->rlocal);
561   N_VSetArrayPointer(NULL, pdata->zlocal);
562 
563   return(retval);
564 }
565 
566 
CVBBDPrecFree(CVodeMem cv_mem)567 static int CVBBDPrecFree(CVodeMem cv_mem)
568 {
569   CVLsMem cvls_mem;
570   CVBBDPrecData pdata;
571 
572   if (cv_mem->cv_lmem == NULL) return(0);
573   cvls_mem = (CVLsMem) cv_mem->cv_lmem;
574 
575   if (cvls_mem->P_data == NULL) return(0);
576   pdata = (CVBBDPrecData) cvls_mem->P_data;
577 
578   SUNLinSolFree(pdata->LS);
579   N_VDestroy(pdata->tmp1);
580   N_VDestroy(pdata->tmp2);
581   N_VDestroy(pdata->tmp3);
582   N_VDestroy(pdata->zlocal);
583   N_VDestroy(pdata->rlocal);
584   SUNMatDestroy(pdata->savedP);
585   SUNMatDestroy(pdata->savedJ);
586 
587   free(pdata);
588   pdata = NULL;
589 
590   return(0);
591 }
592 
593 
594 /*-----------------------------------------------------------------
595   Function : CVBBDDQJac
596   -----------------------------------------------------------------
597   This routine generates a banded difference quotient approximation
598   to the local block of the Jacobian of g(t,y). It assumes that a
599   band SUNMatrix is stored columnwise, and that elements within each
600   column are contiguous. All matrix elements are generated as
601   difference quotients, by way of calls to the user routine gloc.
602   By virtue of the band structure, the number of these calls is
603   bandwidth + 1, where bandwidth = mldq + mudq + 1.
604   But the band matrix kept has bandwidth = mlkeep + mukeep + 1.
605   This routine also assumes that the local elements of a vector are
606   stored contiguously.
607   -----------------------------------------------------------------*/
CVBBDDQJac(CVBBDPrecData pdata,realtype t,N_Vector y,N_Vector gy,N_Vector ytemp,N_Vector gtemp)608 static int CVBBDDQJac(CVBBDPrecData pdata, realtype t, N_Vector y,
609                       N_Vector gy, N_Vector ytemp, N_Vector gtemp)
610 {
611   CVodeMem cv_mem;
612   realtype gnorm, minInc, inc, inc_inv, yj, conj;
613   sunindextype group, i, j, width, ngroups, i1, i2;
614   realtype *y_data, *ewt_data, *gy_data, *gtemp_data;
615   realtype *ytemp_data, *col_j, *cns_data;
616   int retval;
617 
618   /* initialize cns_data to avoid compiler warning */
619   cns_data = NULL;
620 
621   cv_mem = (CVodeMem) pdata->cvode_mem;
622 
623   /* Load ytemp with y = predicted solution vector */
624   N_VScale(ONE, y, ytemp);
625 
626   /* Call cfn and gloc to get base value of g(t,y) */
627   if (pdata->cfn != NULL) {
628     retval = pdata->cfn(pdata->n_local, t, y, cv_mem->cv_user_data);
629     if (retval != 0) return(retval);
630   }
631 
632   retval = pdata->gloc(pdata->n_local, t, ytemp, gy,
633                        cv_mem->cv_user_data);
634   pdata->nge++;
635   if (retval != 0) return(retval);
636 
637   /* Obtain pointers to the data for various vectors */
638   y_data     =  N_VGetArrayPointer(y);
639   gy_data    =  N_VGetArrayPointer(gy);
640   ewt_data   =  N_VGetArrayPointer(cv_mem->cv_ewt);
641   ytemp_data =  N_VGetArrayPointer(ytemp);
642   gtemp_data =  N_VGetArrayPointer(gtemp);
643   if (cv_mem->cv_constraintsSet)
644     cns_data =  N_VGetArrayPointer(cv_mem->cv_constraints);
645 
646   /* Set minimum increment based on uround and norm of g */
647   gnorm = N_VWrmsNorm(gy, cv_mem->cv_ewt);
648   minInc = (gnorm != ZERO) ?
649     (MIN_INC_MULT * SUNRabs(cv_mem->cv_h) *
650      cv_mem->cv_uround * pdata->n_local * gnorm) : ONE;
651 
652   /* Set bandwidth and number of column groups for band differencing */
653   width = pdata->mldq + pdata->mudq + 1;
654   ngroups = SUNMIN(width, pdata->n_local);
655 
656   /* Loop over groups */
657   for (group=1; group <= ngroups; group++) {
658 
659     /* Increment all y_j in group */
660     for(j=group-1; j < pdata->n_local; j+=width) {
661       inc = SUNMAX(pdata->dqrely * SUNRabs(y_data[j]), minInc/ewt_data[j]);
662       yj = y_data[j];
663 
664       /* Adjust sign(inc) again if yj has an inequality constraint. */
665       if (cv_mem->cv_constraintsSet) {
666         conj = cns_data[j];
667         if (SUNRabs(conj) == ONE)      {if ((yj+inc)*conj < ZERO)  inc = -inc;}
668         else if (SUNRabs(conj) == TWO) {if ((yj+inc)*conj <= ZERO) inc = -inc;}
669       }
670 
671       ytemp_data[j] += inc;
672     }
673 
674     /* Evaluate g with incremented y */
675     retval = pdata->gloc(pdata->n_local, t, ytemp, gtemp,
676                          cv_mem->cv_user_data);
677     pdata->nge++;
678     if (retval != 0) return(retval);
679 
680     /* Restore ytemp, then form and load difference quotients */
681     for (j=group-1; j < pdata->n_local; j+=width) {
682       yj = y_data[j];
683       ytemp_data[j] = y_data[j];
684       col_j = SUNBandMatrix_Column(pdata->savedJ,j);
685       inc = SUNMAX(pdata->dqrely * SUNRabs(y_data[j]), minInc/ewt_data[j]);
686 
687       /* Adjust sign(inc) as before. */
688       if (cv_mem->cv_constraintsSet) {
689         conj = cns_data[j];
690         if (SUNRabs(conj) == ONE)      {if ((yj+inc)*conj < ZERO)  inc = -inc;}
691         else if (SUNRabs(conj) == TWO) {if ((yj+inc)*conj <= ZERO) inc = -inc;}
692       }
693 
694       inc_inv = ONE/inc;
695       i1 = SUNMAX(0, j-pdata->mukeep);
696       i2 = SUNMIN(j + pdata->mlkeep, pdata->n_local-1);
697       for (i=i1; i <= i2; i++)
698         SM_COLUMN_ELEMENT_B(col_j,i,j) =
699           inc_inv * (gtemp_data[i] - gy_data[i]);
700     }
701   }
702 
703   return(0);
704 }
705