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