1 /* -----------------------------------------------------------------------------
2 * Programmer(s): David J. Gardner @ LLNL
3 * -----------------------------------------------------------------------------
4 * SUNDIALS Copyright Start
5 * Copyright (c) 2002-2020, Lawrence Livermore National Security
6 * and Southern Methodist University.
7 * All rights reserved.
8 *
9 * See the top-level LICENSE and NOTICE files for details.
10 *
11 * SPDX-License-Identifier: BSD-3-Clause
12 * SUNDIALS Copyright End
13 * -----------------------------------------------------------------------------
14 * This the implementation file for the CVODES nonlinear solver interface.
15 * ---------------------------------------------------------------------------*/
16
17 /*
18 * When sensitivities are computed using the CV_SIMULTANEOUS approach and the
19 * Newton solver is selected the iteraiton is a quasi-Newton method on the
20 * combined system (by approximating the Jacobian matrix by its block diagonal)
21 * and thus only solve linear systems with multiple right hand sides (all
22 * sharing the same coefficient matrix - whatever iteration matrix we decide on)
23 * we set-up the linear solver to handle N equations at a time.
24 */
25
26 #include "cvodes_impl.h"
27 #include "sundials/sundials_math.h"
28 #include "sundials/sundials_nvector_senswrapper.h"
29
30 /* constant macros */
31 #define ONE RCONST(1.0)
32
33 /* private functions */
34 static int cvNlsResidualSensSim(N_Vector ycorSim, N_Vector resSim,
35 void* cvode_mem);
36 static int cvNlsFPFunctionSensSim(N_Vector ycorSim, N_Vector resSim,
37 void* cvode_mem);
38
39 static int cvNlsLSetupSensSim(booleantype jbad, booleantype* jcur,
40 void* cvode_mem);
41 static int cvNlsLSolveSensSim(N_Vector deltaSim, void* cvode_mem);
42 static int cvNlsConvTestSensSim(SUNNonlinearSolver NLS,
43 N_Vector ycorSim, N_Vector delSim,
44 realtype tol, N_Vector ewtSim, void* cvode_mem);
45
46 /* -----------------------------------------------------------------------------
47 * Exported functions
48 * ---------------------------------------------------------------------------*/
49
CVodeSetNonlinearSolverSensSim(void * cvode_mem,SUNNonlinearSolver NLS)50 int CVodeSetNonlinearSolverSensSim(void *cvode_mem, SUNNonlinearSolver NLS)
51 {
52 CVodeMem cv_mem;
53 int retval, is;
54
55 /* Return immediately if CVode memory is NULL */
56 if (cvode_mem == NULL) {
57 cvProcessError(NULL, CV_MEM_NULL, "CVODES",
58 "CVodeSetNonlinearSolverSensSim", MSGCV_NO_MEM);
59 return(CV_MEM_NULL);
60 }
61 cv_mem = (CVodeMem) cvode_mem;
62
63 /* Return immediately if NLS memory is NULL */
64 if (NLS == NULL) {
65 cvProcessError(NULL, CV_ILL_INPUT, "CVODES",
66 "CVodeSetNonlinearSolverSensSim",
67 "NLS must be non-NULL");
68 return (CV_ILL_INPUT);
69 }
70
71 /* check for required nonlinear solver functions */
72 if ( NLS->ops->gettype == NULL ||
73 NLS->ops->solve == NULL ||
74 NLS->ops->setsysfn == NULL ) {
75 cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES",
76 "CVodeSetNonlinearSolverSensSim",
77 "NLS does not support required operations");
78 return(CV_ILL_INPUT);
79 }
80
81 /* check that sensitivities were initialized */
82 if (!(cv_mem->cv_sensi)) {
83 cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES",
84 "CVodeSetNonlinearSolverSensSim",
85 MSGCV_NO_SENSI);
86 return(CV_ILL_INPUT);
87 }
88
89 /* check that simultaneous corrector was selected */
90 if (cv_mem->cv_ism != CV_SIMULTANEOUS) {
91 cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES",
92 "CVodeSetNonlinearSolverSensStg",
93 "Sensitivity solution method is not CV_SIMULTANEOUS");
94 return(CV_ILL_INPUT);
95 }
96
97 /* free any existing nonlinear solver */
98 if ((cv_mem->NLSsim != NULL) && (cv_mem->ownNLSsim))
99 retval = SUNNonlinSolFree(cv_mem->NLSsim);
100
101 /* set SUNNonlinearSolver pointer */
102 cv_mem->NLSsim = NLS;
103
104 /* Set NLS ownership flag. If this function was called to attach the default
105 NLS, CVODE will set the flag to SUNTRUE after this function returns. */
106 cv_mem->ownNLSsim = SUNFALSE;
107
108 /* set the nonlinear system function */
109 if (SUNNonlinSolGetType(NLS) == SUNNONLINEARSOLVER_ROOTFIND) {
110 retval = SUNNonlinSolSetSysFn(cv_mem->NLSsim, cvNlsResidualSensSim);
111 } else if (SUNNonlinSolGetType(NLS) == SUNNONLINEARSOLVER_FIXEDPOINT) {
112 retval = SUNNonlinSolSetSysFn(cv_mem->NLSsim, cvNlsFPFunctionSensSim);
113 } else {
114 cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES",
115 "CVodeSetNonlinearSolverSensSim",
116 "Invalid nonlinear solver type");
117 return(CV_ILL_INPUT);
118 }
119
120 if (retval != CV_SUCCESS) {
121 cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES",
122 "CVodeSetNonlinearSolverSensSim",
123 "Setting nonlinear system function failed");
124 return(CV_ILL_INPUT);
125 }
126
127 /* set convergence test function */
128 retval = SUNNonlinSolSetConvTestFn(cv_mem->NLSsim, cvNlsConvTestSensSim,
129 cvode_mem);
130 if (retval != CV_SUCCESS) {
131 cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES",
132 "CVodeSetNonlinearSolverSensSim",
133 "Setting convergence test function failed");
134 return(CV_ILL_INPUT);
135 }
136
137 /* set max allowed nonlinear iterations */
138 retval = SUNNonlinSolSetMaxIters(cv_mem->NLSsim, NLS_MAXCOR);
139 if (retval != CV_SUCCESS) {
140 cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES",
141 "CVodeSetNonlinearSolverSensSim",
142 "Setting maximum number of nonlinear iterations failed");
143 return(CV_ILL_INPUT);
144 }
145
146 /* create vector wrappers if necessary */
147 if (cv_mem->simMallocDone == SUNFALSE) {
148
149 cv_mem->zn0Sim = N_VNewEmpty_SensWrapper(cv_mem->cv_Ns+1);
150 if (cv_mem->zn0Sim == NULL) {
151 cvProcessError(cv_mem, CV_MEM_FAIL, "CVODES",
152 "CVodeSetNonlinearSolverSensSim", MSGCV_MEM_FAIL);
153 return(CV_MEM_FAIL);
154 }
155
156 cv_mem->ycorSim = N_VNewEmpty_SensWrapper(cv_mem->cv_Ns+1);
157 if (cv_mem->ycorSim == NULL) {
158 N_VDestroy(cv_mem->zn0Sim);
159 cvProcessError(cv_mem, CV_MEM_FAIL, "CVODES",
160 "CVodeSetNonlinearSolverSensSim", MSGCV_MEM_FAIL);
161 return(CV_MEM_FAIL);
162 }
163
164 cv_mem->ewtSim = N_VNewEmpty_SensWrapper(cv_mem->cv_Ns+1);
165 if (cv_mem->ewtSim == NULL) {
166 N_VDestroy(cv_mem->zn0Sim);
167 N_VDestroy(cv_mem->ycorSim);
168 cvProcessError(cv_mem, CV_MEM_FAIL, "CVODES",
169 "CVodeSetNonlinearSolverSensSim", MSGCV_MEM_FAIL);
170 return(CV_MEM_FAIL);
171 }
172
173 cv_mem->simMallocDone = SUNTRUE;
174 }
175
176 /* attach vectors to vector wrappers */
177 NV_VEC_SW(cv_mem->zn0Sim, 0) = cv_mem->cv_zn[0];
178 NV_VEC_SW(cv_mem->ycorSim, 0) = cv_mem->cv_acor;
179 NV_VEC_SW(cv_mem->ewtSim, 0) = cv_mem->cv_ewt;
180
181 for (is=0; is < cv_mem->cv_Ns; is++) {
182 NV_VEC_SW(cv_mem->zn0Sim, is+1) = cv_mem->cv_znS[0][is];
183 NV_VEC_SW(cv_mem->ycorSim, is+1) = cv_mem->cv_acorS[is];
184 NV_VEC_SW(cv_mem->ewtSim, is+1) = cv_mem->cv_ewtS[is];
185 }
186
187 /* Reset the acnrmcur flag to SUNFALSE */
188 cv_mem->cv_acnrmcur = SUNFALSE;
189
190 return(CV_SUCCESS);
191 }
192
193
194 /* -----------------------------------------------------------------------------
195 * Private functions
196 * ---------------------------------------------------------------------------*/
197
198
cvNlsInitSensSim(CVodeMem cvode_mem)199 int cvNlsInitSensSim(CVodeMem cvode_mem)
200 {
201 int retval;
202
203 /* set the linear solver setup wrapper function */
204 if (cvode_mem->cv_lsetup)
205 retval = SUNNonlinSolSetLSetupFn(cvode_mem->NLSsim, cvNlsLSetupSensSim);
206 else
207 retval = SUNNonlinSolSetLSetupFn(cvode_mem->NLSsim, NULL);
208
209 if (retval != CV_SUCCESS) {
210 cvProcessError(cvode_mem, CV_ILL_INPUT, "CVODES", "cvNlsInitSensSim",
211 "Setting the linear solver setup function failed");
212 return(CV_NLS_INIT_FAIL);
213 }
214
215 /* set the linear solver solve wrapper function */
216 if (cvode_mem->cv_lsolve)
217 retval = SUNNonlinSolSetLSolveFn(cvode_mem->NLSsim, cvNlsLSolveSensSim);
218 else
219 retval = SUNNonlinSolSetLSolveFn(cvode_mem->NLSsim, NULL);
220
221 if (retval != CV_SUCCESS) {
222 cvProcessError(cvode_mem, CV_ILL_INPUT, "CVODES", "cvNlsInitSensSim",
223 "Setting linear solver solve function failed");
224 return(CV_NLS_INIT_FAIL);
225 }
226
227 /* initialize nonlinear solver */
228 retval = SUNNonlinSolInitialize(cvode_mem->NLSsim);
229
230 if (retval != CV_SUCCESS) {
231 cvProcessError(cvode_mem, CV_ILL_INPUT, "CVODES", "cvNlsInitSensSim",
232 MSGCV_NLS_INIT_FAIL);
233 return(CV_NLS_INIT_FAIL);
234 }
235
236 return(CV_SUCCESS);
237 }
238
239
cvNlsLSetupSensSim(booleantype jbad,booleantype * jcur,void * cvode_mem)240 static int cvNlsLSetupSensSim(booleantype jbad, booleantype* jcur,
241 void* cvode_mem)
242 {
243 CVodeMem cv_mem;
244 int retval;
245
246 if (cvode_mem == NULL) {
247 cvProcessError(NULL, CV_MEM_NULL, "CVODES",
248 "cvNlsLSetupSensSim", MSGCV_NO_MEM);
249 return(CV_MEM_NULL);
250 }
251 cv_mem = (CVodeMem) cvode_mem;
252
253 /* if the nonlinear solver marked the Jacobian as bad update convfail */
254 if (jbad)
255 cv_mem->convfail = CV_FAIL_BAD_J;
256
257 /* setup the linear solver */
258 retval = cv_mem->cv_lsetup(cv_mem, cv_mem->convfail, cv_mem->cv_y,
259 cv_mem->cv_ftemp, &(cv_mem->cv_jcur),
260 cv_mem->cv_vtemp1, cv_mem->cv_vtemp2,
261 cv_mem->cv_vtemp3);
262 cv_mem->cv_nsetups++;
263
264 /* update Jacobian status */
265 *jcur = cv_mem->cv_jcur;
266
267 cv_mem->cv_forceSetup = SUNFALSE;
268 cv_mem->cv_gamrat = ONE;
269 cv_mem->cv_gammap = cv_mem->cv_gamma;
270 cv_mem->cv_crate = ONE;
271 cv_mem->cv_crateS = ONE;
272 cv_mem->cv_nstlp = cv_mem->cv_nst;
273
274 if (retval < 0) return(CV_LSETUP_FAIL);
275 if (retval > 0) return(SUN_NLS_CONV_RECVR);
276
277 return(CV_SUCCESS);
278 }
279
280
cvNlsLSolveSensSim(N_Vector deltaSim,void * cvode_mem)281 static int cvNlsLSolveSensSim(N_Vector deltaSim, void* cvode_mem)
282 {
283 CVodeMem cv_mem;
284 int retval, is;
285 N_Vector delta;
286 N_Vector *deltaS;
287
288 if (cvode_mem == NULL) {
289 cvProcessError(NULL, CV_MEM_NULL, "CVODES",
290 "cvNlsLSolveSensSim", MSGCV_NO_MEM);
291 return(CV_MEM_NULL);
292 }
293 cv_mem = (CVodeMem) cvode_mem;
294
295 /* extract state delta from the vector wrapper */
296 delta = NV_VEC_SW(deltaSim,0);
297
298 /* solve the state linear system */
299 retval = cv_mem->cv_lsolve(cv_mem, delta, cv_mem->cv_ewt, cv_mem->cv_y,
300 cv_mem->cv_ftemp);
301
302 if (retval < 0) return(CV_LSOLVE_FAIL);
303 if (retval > 0) return(SUN_NLS_CONV_RECVR);
304
305 /* extract sensitivity deltas from the vector wrapper */
306 deltaS = NV_VECS_SW(deltaSim)+1;
307
308 /* solve the sensitivity linear systems */
309 for (is=0; is<cv_mem->cv_Ns; is++) {
310 retval = cv_mem->cv_lsolve(cv_mem, deltaS[is], cv_mem->cv_ewtS[is],
311 cv_mem->cv_y, cv_mem->cv_ftemp);
312
313 if (retval < 0) return(CV_LSOLVE_FAIL);
314 if (retval > 0) return(SUN_NLS_CONV_RECVR);
315 }
316
317 return(CV_SUCCESS);
318 }
319
320
cvNlsConvTestSensSim(SUNNonlinearSolver NLS,N_Vector ycorSim,N_Vector deltaSim,realtype tol,N_Vector ewtSim,void * cvode_mem)321 static int cvNlsConvTestSensSim(SUNNonlinearSolver NLS,
322 N_Vector ycorSim, N_Vector deltaSim,
323 realtype tol, N_Vector ewtSim, void* cvode_mem)
324 {
325 CVodeMem cv_mem;
326 int m, retval;
327 realtype del, delS, Del;
328 realtype dcon;
329 N_Vector ycor, delta, ewt;
330 N_Vector *deltaS, *ewtS;
331
332 if (cvode_mem == NULL) {
333 cvProcessError(NULL, CV_MEM_NULL, "CVODES",
334 "cvNlsConvTestSensSim", MSGCV_NO_MEM);
335 return(CV_MEM_NULL);
336 }
337 cv_mem = (CVodeMem) cvode_mem;
338
339 /* extract the current state and sensitivity corrections */
340 ycor = NV_VEC_SW(ycorSim,0);
341
342 /* extract state and sensitivity deltas */
343 delta = NV_VEC_SW(deltaSim,0);
344 deltaS = NV_VECS_SW(deltaSim)+1;
345
346 /* extract state and sensitivity error weights */
347 ewt = NV_VEC_SW(ewtSim,0);
348 ewtS = NV_VECS_SW(ewtSim)+1;
349
350 /* compute the norm of the state and sensitivity corrections */
351 del = N_VWrmsNorm(delta, ewt);
352 delS = cvSensUpdateNorm(cv_mem, del, deltaS, ewtS);
353
354 /* norm used in error test */
355 Del = delS;
356
357 /* get the current nonlinear solver iteration count */
358 retval = SUNNonlinSolGetCurIter(NLS, &m);
359 if (retval != CV_SUCCESS) return(CV_MEM_NULL);
360
361 /* Test for convergence. If m > 0, an estimate of the convergence
362 rate constant is stored in crate, and used in the test.
363
364 Recall that, even when errconS=SUNFALSE, all variables are used in the
365 convergence test. Hence, we use Del (and not del). However, acnrm is used
366 in the error test and thus it has different forms depending on errconS
367 (and this explains why we have to carry around del and delS).
368 */
369 if (m > 0) {
370 cv_mem->cv_crate = SUNMAX(CRDOWN * cv_mem->cv_crate, Del/cv_mem->cv_delp);
371 }
372 dcon = Del * SUNMIN(ONE, cv_mem->cv_crate) / tol;
373
374 /* check if nonlinear system was solved successfully */
375 if (dcon <= ONE) {
376 if (m == 0) {
377 cv_mem->cv_acnrm = (cv_mem->cv_errconS) ? delS : del;
378 } else {
379 cv_mem->cv_acnrm = (cv_mem->cv_errconS) ?
380 N_VWrmsNorm(ycorSim, ewtSim) : N_VWrmsNorm(ycor, ewt);
381 }
382 cv_mem->cv_acnrmcur = SUNTRUE;
383 return(CV_SUCCESS);
384 }
385
386 /* check if the iteration seems to be diverging */
387 if ((m >= 1) && (Del > RDIV*cv_mem->cv_delp)) return(SUN_NLS_CONV_RECVR);
388
389 /* Save norm of correction and loop again */
390 cv_mem->cv_delp = Del;
391
392 /* Not yet converged */
393 return(SUN_NLS_CONTINUE);
394 }
395
396
cvNlsResidualSensSim(N_Vector ycorSim,N_Vector resSim,void * cvode_mem)397 static int cvNlsResidualSensSim(N_Vector ycorSim, N_Vector resSim, void* cvode_mem)
398 {
399 CVodeMem cv_mem;
400 int retval;
401 N_Vector ycor, res;
402 N_Vector *ycorS, *resS;
403 realtype cvals[3];
404 N_Vector* XXvecs[3];
405
406 if (cvode_mem == NULL) {
407 cvProcessError(NULL, CV_MEM_NULL, "CVODES",
408 "cvNlsResidualSensSim", MSGCV_NO_MEM);
409 return(CV_MEM_NULL);
410 }
411 cv_mem = (CVodeMem) cvode_mem;
412
413 /* extract state and residual vectors from the vector wrapper */
414 ycor = NV_VEC_SW(ycorSim,0);
415 res = NV_VEC_SW(resSim,0);
416
417 /* update the state based on the current correction */
418 N_VLinearSum(ONE, cv_mem->cv_zn[0], ONE, ycor, cv_mem->cv_y);
419
420 /* evaluate the rhs function */
421 retval = cv_mem->cv_f(cv_mem->cv_tn, cv_mem->cv_y, cv_mem->cv_ftemp,
422 cv_mem->cv_user_data);
423 cv_mem->cv_nfe++;
424 if (retval < 0) return(CV_RHSFUNC_FAIL);
425 if (retval > 0) return(RHSFUNC_RECVR);
426
427 /* compute the resiudal */
428 N_VLinearSum(cv_mem->cv_rl1, cv_mem->cv_zn[1], ONE, ycor, res);
429 N_VLinearSum(-cv_mem->cv_gamma, cv_mem->cv_ftemp, ONE, res, res);
430
431 /* extract sensitivity and residual vectors from the vector wrapper */
432 ycorS = NV_VECS_SW(ycorSim)+1;
433 resS = NV_VECS_SW(resSim)+1;
434
435 /* update sensitivities based on the current correction */
436 retval = N_VLinearSumVectorArray(cv_mem->cv_Ns,
437 ONE, cv_mem->cv_znS[0],
438 ONE, ycorS, cv_mem->cv_yS);
439 if (retval != CV_SUCCESS) return(CV_VECTOROP_ERR);
440
441 /* evaluate the sensitivity rhs function */
442 retval = cvSensRhsWrapper(cv_mem, cv_mem->cv_tn,
443 cv_mem->cv_y, cv_mem->cv_ftemp,
444 cv_mem->cv_yS, cv_mem->cv_ftempS,
445 cv_mem->cv_vtemp1, cv_mem->cv_vtemp2);
446
447 if (retval < 0) return(CV_SRHSFUNC_FAIL);
448 if (retval > 0) return(SRHSFUNC_RECVR);
449
450 /* compute the sensitivity resiudal */
451 cvals[0] = cv_mem->cv_rl1; XXvecs[0] = cv_mem->cv_znS[1];
452 cvals[1] = ONE; XXvecs[1] = ycorS;
453 cvals[2] = -cv_mem->cv_gamma; XXvecs[2] = cv_mem->cv_ftempS;
454
455 retval = N_VLinearCombinationVectorArray(cv_mem->cv_Ns,
456 3, cvals, XXvecs, resS);
457 if (retval != CV_SUCCESS) return(CV_VECTOROP_ERR);
458
459 return(CV_SUCCESS);
460 }
461
462
cvNlsFPFunctionSensSim(N_Vector ycorSim,N_Vector resSim,void * cvode_mem)463 static int cvNlsFPFunctionSensSim(N_Vector ycorSim, N_Vector resSim, void* cvode_mem)
464 {
465 CVodeMem cv_mem;
466 int retval, is;
467 N_Vector ycor, res;
468 N_Vector *ycorS, *resS;
469
470 if (cvode_mem == NULL) {
471 cvProcessError(NULL, CV_MEM_NULL, "CVODES",
472 "cvNlsFPFunctionSensSim", MSGCV_NO_MEM);
473 return(CV_MEM_NULL);
474 }
475 cv_mem = (CVodeMem) cvode_mem;
476
477 /* extract state and residual vectors from the vector wrapper */
478 ycor = NV_VEC_SW(ycorSim,0);
479 res = NV_VEC_SW(resSim,0);
480
481 /* update the state based on the current correction */
482 N_VLinearSum(ONE, cv_mem->cv_zn[0], ONE, ycor, cv_mem->cv_y);
483
484 /* evaluate the rhs function */
485 retval = cv_mem->cv_f(cv_mem->cv_tn, cv_mem->cv_y, res,
486 cv_mem->cv_user_data);
487 cv_mem->cv_nfe++;
488 if (retval < 0) return(CV_RHSFUNC_FAIL);
489 if (retval > 0) return(RHSFUNC_RECVR);
490
491 /* evaluate fixed point function */
492 N_VLinearSum(cv_mem->cv_h, res, -ONE, cv_mem->cv_zn[1], res);
493 N_VScale(cv_mem->cv_rl1, res, res);
494
495 /* extract sensitivity and residual vectors from the vector wrapper */
496 ycorS = NV_VECS_SW(ycorSim)+1;
497 resS = NV_VECS_SW(resSim)+1;
498
499 /* update the sensitivities based on the current correction */
500 N_VLinearSumVectorArray(cv_mem->cv_Ns,
501 ONE, cv_mem->cv_znS[0],
502 ONE, ycorS, cv_mem->cv_yS);
503
504 /* evaluate the sensitivity rhs function */
505 retval = cvSensRhsWrapper(cv_mem, cv_mem->cv_tn,
506 cv_mem->cv_y, res,
507 cv_mem->cv_yS, resS,
508 cv_mem->cv_vtemp1, cv_mem->cv_vtemp2);
509
510 if (retval < 0) return(CV_SRHSFUNC_FAIL);
511 if (retval > 0) return(SRHSFUNC_RECVR);
512
513 /* evaluate sensitivity fixed point function */
514 for (is=0; is<cv_mem->cv_Ns; is++) {
515 N_VLinearSum(cv_mem->cv_h, resS[is], -ONE, cv_mem->cv_znS[1][is], resS[is]);
516 N_VScale(cv_mem->cv_rl1, resS[is], resS[is]);
517 }
518
519 return(CV_SUCCESS);
520 }
521