1 /*
2  * -----------------------------------------------------------------
3  * $Revision$
4  * $Date$
5  * -----------------------------------------------------------------
6  * Programmers: Radu Serban @ LLNL
7  * -----------------------------------------------------------------
8  * SUNDIALS Copyright Start
9  * Copyright (c) 2002-2019, Lawrence Livermore National Security
10  * and Southern Methodist University.
11  * All rights reserved.
12  *
13  * See the top-level LICENSE and NOTICE files for details.
14  *
15  * SPDX-License-Identifier: BSD-3-Clause
16  * SUNDIALS Copyright End
17  * -----------------------------------------------------------------
18  * This is the implementation file for the IC calculation for IDAS.
19  * It is independent of the linear solver in use.
20  * -----------------------------------------------------------------
21  */
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 
26 #include "idas_impl.h"
27 #include <sundials/sundials_math.h>
28 
29 /*
30  * =================================================================
31  * IDA Constants
32  * =================================================================
33  */
34 
35 /* Private Constants */
36 
37 #define ZERO       RCONST(0.0)    /* real 0.0    */
38 #define HALF       RCONST(0.5)    /* real 0.5    */
39 #define ONE        RCONST(1.0)    /* real 1.0    */
40 #define TWO        RCONST(2.0)    /* real 2.0    */
41 #define PT99       RCONST(0.99)   /* real 0.99   */
42 #define PT1        RCONST(0.1)    /* real 0.1    */
43 #define PT001      RCONST(0.001)  /* real 0.001  */
44 
45 /* IDACalcIC control constants */
46 
47 #define ICRATEMAX  RCONST(0.9)    /* max. Newton conv. rate */
48 #define ALPHALS    RCONST(0.0001) /* alpha in linesearch conv. test */
49 
50 /* Return values for lower level routines used by IDACalcIC */
51 
52 #define IC_FAIL_RECOV       1
53 #define IC_CONSTR_FAILED    2
54 #define IC_LINESRCH_FAILED  3
55 #define IC_CONV_FAIL        4
56 #define IC_SLOW_CONVRG      5
57 
58 /*
59  * =================================================================
60  * Private Helper Functions Prototypes
61  * =================================================================
62  */
63 
64 extern int IDAInitialSetup(IDAMem IDA_mem);
65 extern realtype IDAWrmsNorm(IDAMem IDA_mem, N_Vector x,
66                             N_Vector w, booleantype mask);
67 extern realtype IDASensWrmsNorm(IDAMem IDA_mem, N_Vector *xS,
68                                 N_Vector *wS, booleantype mask);
69 extern realtype IDASensWrmsNormUpdate(IDAMem IDA_mem, realtype old_nrm,
70                                       N_Vector *xS, N_Vector *wS,
71                                       booleantype mask);
72 
73 extern int IDASensEwtSet(IDAMem IDA_mem, N_Vector *yScur, N_Vector *weightS);
74 
75 static int IDANlsIC(IDAMem IDA_mem);
76 
77 static int IDANewtonIC(IDAMem IDA_mem);
78 static int IDALineSrch(IDAMem IDA_mem, realtype *delnorm, realtype *fnorm);
79 static int IDAfnorm(IDAMem IDA_mem, realtype *fnorm);
80 static int IDANewyyp(IDAMem IDA_mem, realtype lambda);
81 static int IDANewy(IDAMem IDA_mem);
82 
83 static int IDASensNewtonIC(IDAMem IDA_mem);
84 static int IDASensLineSrch(IDAMem IDA_mem, realtype *delnorm, realtype *fnorm);
85 static int IDASensNewyyp(IDAMem IDA_mem, realtype lambda);
86 static int IDASensfnorm(IDAMem IDA_mem, realtype *fnorm);
87 static int IDASensNlsIC(IDAMem IDA_mem);
88 
89 static int IDAICFailFlag(IDAMem IDA_mem, int retval);
90 
91 
92 /*
93  * =================================================================
94  * EXPORTED FUNCTIONS IMPLEMENTATION
95  * =================================================================
96  */
97 
98 /*
99  * -----------------------------------------------------------------
100  * IDACalcIC
101  * -----------------------------------------------------------------
102  * IDACalcIC computes consistent initial conditions, given the
103  * user's initial guess for unknown components of yy0 and/or yp0.
104  *
105  * The return value is IDA_SUCCESS = 0 if no error occurred.
106  *
107  * The error return values (fully described in ida.h) are:
108  *   IDA_MEM_NULL        ida_mem is NULL
109  *   IDA_NO_MALLOC       ida_mem was not allocated
110  *   IDA_ILL_INPUT       bad value for icopt, tout1, or id
111  *   IDA_LINIT_FAIL      the linear solver linit routine failed
112  *   IDA_BAD_EWT         zero value of some component of ewt
113  *   IDA_RES_FAIL        res had a non-recoverable error
114  *   IDA_FIRST_RES_FAIL  res failed recoverably on the first call
115  *   IDA_LSETUP_FAIL     lsetup had a non-recoverable error
116  *   IDA_LSOLVE_FAIL     lsolve had a non-recoverable error
117  *   IDA_NO_RECOVERY     res, lsetup, or lsolve had a recoverable
118  *                       error, but IDACalcIC could not recover
119  *   IDA_CONSTR_FAIL     the inequality constraints could not be met
120  *   IDA_LINESEARCH_FAIL if the linesearch failed (either on steptol test
121  *                       or on the maxbacks test)
122  *   IDA_CONV_FAIL       the Newton iterations failed to converge
123  * -----------------------------------------------------------------
124  */
125 
IDACalcIC(void * ida_mem,int icopt,realtype tout1)126 int IDACalcIC(void *ida_mem, int icopt, realtype tout1)
127 {
128   int ewtsetOK;
129   int ier, nwt, nh, mxnh, icret, retval=0;
130   int is;
131   realtype tdist, troundoff, minid, hic, ypnorm;
132   IDAMem IDA_mem;
133   booleantype sensi_stg, sensi_sim;
134 
135   /* Check if IDA memory exists */
136 
137   if(ida_mem == NULL) {
138     IDAProcessError(NULL, IDA_MEM_NULL, "IDAS", "IDACalcIC", MSG_NO_MEM);
139     return(IDA_MEM_NULL);
140   }
141   IDA_mem = (IDAMem) ida_mem;
142 
143   /* Check if problem was malloc'ed */
144 
145   if(IDA_mem->ida_MallocDone == SUNFALSE) {
146     IDAProcessError(IDA_mem, IDA_NO_MALLOC, "IDAS", "IDACalcIC", MSG_NO_MALLOC);
147     return(IDA_NO_MALLOC);
148   }
149 
150   /* Check inputs to IDA for correctness and consistency */
151 
152   ier = IDAInitialSetup(IDA_mem);
153   if(ier != IDA_SUCCESS) return(IDA_ILL_INPUT);
154   IDA_mem->ida_SetupDone = SUNTRUE;
155 
156   /* Check legality of input arguments, and set IDA memory copies. */
157 
158   if(icopt != IDA_YA_YDP_INIT && icopt != IDA_Y_INIT) {
159     IDAProcessError(IDA_mem, IDA_ILL_INPUT, "IDAS", "IDACalcIC", MSG_IC_BAD_ICOPT);
160     return(IDA_ILL_INPUT);
161   }
162   IDA_mem->ida_icopt = icopt;
163 
164   if(icopt == IDA_YA_YDP_INIT && (IDA_mem->ida_id == NULL)) {
165     IDAProcessError(IDA_mem, IDA_ILL_INPUT, "IDAS", "IDACalcIC", MSG_IC_MISSING_ID);
166     return(IDA_ILL_INPUT);
167   }
168 
169   tdist = SUNRabs(tout1 - IDA_mem->ida_tn);
170   troundoff = TWO * IDA_mem->ida_uround * (SUNRabs(IDA_mem->ida_tn) + SUNRabs(tout1));
171   if(tdist < troundoff) {
172     IDAProcessError(IDA_mem, IDA_ILL_INPUT, "IDAS", "IDACalcIC", MSG_IC_TOO_CLOSE);
173     return(IDA_ILL_INPUT);
174   }
175 
176   /* Are we computing sensitivities? */
177   sensi_stg  = (IDA_mem->ida_sensi && (IDA_mem->ida_ism==IDA_STAGGERED));
178   sensi_sim  = (IDA_mem->ida_sensi && (IDA_mem->ida_ism==IDA_SIMULTANEOUS));
179 
180   /* Allocate space and initialize temporary vectors */
181 
182   IDA_mem->ida_yy0 = N_VClone(IDA_mem->ida_ee);
183   IDA_mem->ida_yp0 = N_VClone(IDA_mem->ida_ee);
184   IDA_mem->ida_t0  = IDA_mem->ida_tn;
185   N_VScale(ONE, IDA_mem->ida_phi[0], IDA_mem->ida_yy0);
186   N_VScale(ONE, IDA_mem->ida_phi[1], IDA_mem->ida_yp0);
187 
188   if (IDA_mem->ida_sensi) {
189 
190     /* Allocate temporary space required for sensitivity IC: yyS0 and ypS0. */
191     IDA_mem->ida_yyS0 = N_VCloneVectorArray(IDA_mem->ida_Ns, IDA_mem->ida_ee);
192     IDA_mem->ida_ypS0 = N_VCloneVectorArray(IDA_mem->ida_Ns, IDA_mem->ida_ee);
193 
194     /* Initialize sensitivity vector. */
195     for (is=0; is<IDA_mem->ida_Ns; is++) {
196       N_VScale(ONE, IDA_mem->ida_phiS[0][is], IDA_mem->ida_yyS0[is]);
197       N_VScale(ONE, IDA_mem->ida_phiS[1][is], IDA_mem->ida_ypS0[is]);
198     }
199 
200     /* Initialize work space vectors needed for sensitivities. */
201     IDA_mem->ida_savresS = IDA_mem->ida_phiS[2];
202     IDA_mem->ida_delnewS = IDA_mem->ida_phiS[3];
203     IDA_mem->ida_yyS0new = IDA_mem->ida_phiS[4];
204     IDA_mem->ida_ypS0new = IDA_mem->ida_eeS;
205   }
206 
207   /* For use in the IDA_YA_YP_INIT case, set sysindex and tscale. */
208 
209   IDA_mem->ida_sysindex = 1;
210   IDA_mem->ida_tscale   = tdist;
211   if(icopt == IDA_YA_YDP_INIT) {
212     minid = N_VMin(IDA_mem->ida_id);
213     if(minid < ZERO) {
214       IDAProcessError(IDA_mem, IDA_ILL_INPUT, "IDAS", "IDACalcIC", MSG_IC_BAD_ID);
215       return(IDA_ILL_INPUT);
216     }
217     if(minid > HALF) IDA_mem->ida_sysindex = 0;
218   }
219 
220   /* Set the test constant in the Newton convergence test */
221 
222   IDA_mem->ida_epsNewt = IDA_mem->ida_epiccon;
223 
224   /* Initializations:
225      cjratio = 1 (for use in direct linear solvers);
226      set nbacktr = 0; */
227 
228   IDA_mem->ida_cjratio = ONE;
229   IDA_mem->ida_nbacktr = 0;
230 
231   /* Set hic, hh, cj, and mxnh. */
232 
233   hic = PT001*tdist;
234   ypnorm = IDAWrmsNorm(IDA_mem, IDA_mem->ida_yp0, IDA_mem->ida_ewt, IDA_mem->ida_suppressalg);
235 
236   if (sensi_sim)
237     ypnorm = IDASensWrmsNormUpdate(IDA_mem, ypnorm, IDA_mem->ida_ypS0, IDA_mem->ida_ewtS, SUNFALSE);
238 
239   if(ypnorm > HALF/hic) hic = HALF/ypnorm;
240   if(tout1 < IDA_mem->ida_tn) hic = -hic;
241   IDA_mem->ida_hh = hic;
242   if(icopt == IDA_YA_YDP_INIT) {
243     IDA_mem->ida_cj = ONE/hic;
244     mxnh = IDA_mem->ida_maxnh;
245   }
246   else {
247     IDA_mem->ida_cj = ZERO;
248     mxnh = 1;
249   }
250 
251   /* Loop over nwt = number of evaluations of ewt vector. */
252 
253   for(nwt = 1; nwt <= 2; nwt++) {
254 
255     /* Loop over nh = number of h values. */
256     for(nh = 1; nh <= mxnh; nh++) {
257 
258       /* Call the IC nonlinear solver function. */
259       retval = IDANlsIC(IDA_mem);
260 
261       /* Cut h and loop on recoverable IDA_YA_YDP_INIT failure; else break. */
262       if(retval == IDA_SUCCESS) break;
263       IDA_mem->ida_ncfn++;
264       if(retval < 0) break;
265       if(nh == mxnh) break;
266 
267       /* If looping to try again, reset yy0 and yp0 if not converging. */
268       if(retval != IC_SLOW_CONVRG) {
269         N_VScale(ONE, IDA_mem->ida_phi[0], IDA_mem->ida_yy0);
270         N_VScale(ONE, IDA_mem->ida_phi[1], IDA_mem->ida_yp0);
271         if (sensi_sim) {
272 
273           /* Reset yyS0 and ypS0. */
274           /* Copy phiS[0] and phiS[1] into yyS0 and ypS0. */
275           for (is=0; is<IDA_mem->ida_Ns; is++) {
276             N_VScale(ONE, IDA_mem->ida_phiS[0][is], IDA_mem->ida_yyS0[is]);
277             N_VScale(ONE, IDA_mem->ida_phiS[1][is], IDA_mem->ida_ypS0[is]);
278           }
279         }
280       }
281       hic *= PT1;
282       IDA_mem->ida_cj = ONE/hic;
283       IDA_mem->ida_hh = hic;
284     }   /* End of nh loop */
285 
286     /* Break on failure */
287     if(retval != IDA_SUCCESS) break;
288 
289     /* Reset ewt, save yy0, yp0 in phi, and loop. */
290     ewtsetOK = IDA_mem->ida_efun(IDA_mem->ida_yy0, IDA_mem->ida_ewt, IDA_mem->ida_edata);
291     if(ewtsetOK != 0) {
292       retval = IDA_BAD_EWT;
293       break;
294     }
295     N_VScale(ONE, IDA_mem->ida_yy0, IDA_mem->ida_phi[0]);
296     N_VScale(ONE, IDA_mem->ida_yp0, IDA_mem->ida_phi[1]);
297 
298     if (sensi_sim) {
299 
300       /* Reevaluate ewtS. */
301       ewtsetOK = IDASensEwtSet(IDA_mem, IDA_mem->ida_yyS0, IDA_mem->ida_ewtS);
302       if(ewtsetOK != 0) {
303         retval = IDA_BAD_EWT;
304         break;
305       }
306 
307       /* Save yyS0 and ypS0. */
308       for (is=0; is<IDA_mem->ida_Ns; is++) {
309             N_VScale(ONE, IDA_mem->ida_yyS0[is], IDA_mem->ida_phiS[0][is]);
310             N_VScale(ONE, IDA_mem->ida_ypS0[is], IDA_mem->ida_phiS[1][is]);
311       }
312     }
313 
314   }   /* End of nwt loop */
315 
316   /* Load the optional outputs. */
317 
318   if(icopt == IDA_YA_YDP_INIT)   IDA_mem->ida_hused = hic;
319 
320   /* On any failure, free memory, print error message and return */
321 
322   if(retval != IDA_SUCCESS) {
323     N_VDestroy(IDA_mem->ida_yy0);
324     N_VDestroy(IDA_mem->ida_yp0);
325 
326     if(IDA_mem->ida_sensi) {
327       N_VDestroyVectorArray(IDA_mem->ida_yyS0, IDA_mem->ida_Ns);
328       N_VDestroyVectorArray(IDA_mem->ida_ypS0, IDA_mem->ida_Ns);
329     }
330 
331     icret = IDAICFailFlag(IDA_mem, retval);
332     return(icret);
333   }
334 
335   /* Unless using the STAGGERED approach for sensitivities, return now */
336 
337   if (!sensi_stg) {
338 
339     N_VDestroy(IDA_mem->ida_yy0);
340     N_VDestroy(IDA_mem->ida_yp0);
341 
342     if(IDA_mem->ida_sensi) {
343       N_VDestroyVectorArray(IDA_mem->ida_yyS0, IDA_mem->ida_Ns);
344       N_VDestroyVectorArray(IDA_mem->ida_ypS0, IDA_mem->ida_Ns);
345     }
346 
347     return(IDA_SUCCESS);
348   }
349 
350   /* Find consistent I.C. for sensitivities using a staggered approach */
351 
352 
353   /* Evaluate res at converged y, needed for future evaluations of sens. RHS
354      If res() fails recoverably, treat it as a convergence failure and
355      attempt the step again */
356 
357   retval = IDA_mem->ida_res(IDA_mem->ida_t0, IDA_mem->ida_yy0,
358                             IDA_mem->ida_yp0, IDA_mem->ida_delta,
359                             IDA_mem->ida_user_data);
360   IDA_mem->ida_nre++;
361   if(retval < 0)
362     /* res function failed unrecoverably. */
363     return(IDA_RES_FAIL);
364 
365   if(retval > 0)
366     /* res function failed recoverably but no recovery possible. */
367     return(IDA_FIRST_RES_FAIL);
368 
369   /* Loop over nwt = number of evaluations of ewt vector. */
370   for(nwt = 1; nwt <= 2; nwt++) {
371 
372     /* Loop over nh = number of h values. */
373     for(nh = 1; nh <= mxnh; nh++) {
374 
375       retval = IDASensNlsIC(IDA_mem);
376       if(retval == IDA_SUCCESS) break;
377 
378       /* Increment the number of the sensitivity related corrector convergence failures. */
379       IDA_mem->ida_ncfnS++;
380 
381       if(retval < 0) break;
382       if(nh == mxnh) break;
383 
384       /* If looping to try again, reset yyS0 and ypS0 if not converging. */
385       if(retval != IC_SLOW_CONVRG) {
386         for (is=0; is<IDA_mem->ida_Ns; is++) {
387           N_VScale(ONE, IDA_mem->ida_phiS[0][is], IDA_mem->ida_yyS0[is]);
388           N_VScale(ONE, IDA_mem->ida_phiS[1][is], IDA_mem->ida_ypS0[is]);
389         }
390       }
391       hic *= PT1;
392       IDA_mem->ida_cj = ONE/hic;
393       IDA_mem->ida_hh = hic;
394 
395     }   /* End of nh loop */
396 
397     /* Break on failure */
398     if(retval != IDA_SUCCESS) break;
399 
400     /* Since it was successful, reevaluate ewtS with the new values of yyS0, save
401        yyS0 and ypS0 in phiS[0] and phiS[1] and loop one more time to check and
402        maybe correct the  new sensitivities IC with respect to the new weights. */
403 
404     /* Reevaluate ewtS. */
405     ewtsetOK = IDASensEwtSet(IDA_mem, IDA_mem->ida_yyS0, IDA_mem->ida_ewtS);
406     if(ewtsetOK != 0) {
407       retval = IDA_BAD_EWT;
408       break;
409     }
410 
411     /* Save yyS0 and ypS0. */
412     for (is=0; is<IDA_mem->ida_Ns; is++) {
413       N_VScale(ONE, IDA_mem->ida_yyS0[is], IDA_mem->ida_phiS[0][is]);
414       N_VScale(ONE, IDA_mem->ida_ypS0[is], IDA_mem->ida_phiS[1][is]);
415     }
416 
417   }   /* End of nwt loop */
418 
419 
420   /* Load the optional outputs. */
421   if(icopt == IDA_YA_YDP_INIT)   IDA_mem->ida_hused = hic;
422 
423   /* Free temporary space */
424   N_VDestroy(IDA_mem->ida_yy0);
425   N_VDestroy(IDA_mem->ida_yp0);
426 
427   /* Here sensi is SUNTRUE, so deallocate sensitivity temporary vectors. */
428   N_VDestroyVectorArray(IDA_mem->ida_yyS0, IDA_mem->ida_Ns);
429   N_VDestroyVectorArray(IDA_mem->ida_ypS0, IDA_mem->ida_Ns);
430 
431 
432   /* On any failure, print message and return proper flag. */
433   if(retval != IDA_SUCCESS) {
434     icret = IDAICFailFlag(IDA_mem, retval);
435     return(icret);
436   }
437 
438   /* Otherwise return success flag. */
439 
440   return(IDA_SUCCESS);
441 
442 }
443 
444 /*
445  * =================================================================
446  * PRIVATE FUNCTIONS IMPLEMENTATION
447  * =================================================================
448  */
449 
450 /*
451  * -----------------------------------------------------------------
452  * IDANlsIC
453  * -----------------------------------------------------------------
454  * IDANlsIC solves a nonlinear system for consistent initial
455  * conditions.  It calls IDANewtonIC to do most of the work.
456  *
457  * The return value is IDA_SUCCESS = 0 if no error occurred.
458  * The error return values (positive) considered recoverable are:
459  *  IC_FAIL_RECOV      if res, lsetup, or lsolve failed recoverably
460  *  IC_CONSTR_FAILED   if the constraints could not be met
461  *  IC_LINESRCH_FAILED if the linesearch failed (either on steptol test
462  *                     or on maxbacks test)
463  *  IC_CONV_FAIL       if the Newton iterations failed to converge
464  *  IC_SLOW_CONVRG     if the iterations are converging slowly
465  *                     (failed the convergence test, but showed
466  *                     norm reduction or convergence rate < 1)
467  * The error return values (negative) considered non-recoverable are:
468  *  IDA_RES_FAIL       if res had a non-recoverable error
469  *  IDA_FIRST_RES_FAIL if res failed recoverably on the first call
470  *  IDA_LSETUP_FAIL    if lsetup had a non-recoverable error
471  *  IDA_LSOLVE_FAIL    if lsolve had a non-recoverable error
472  * -----------------------------------------------------------------
473  */
474 
IDANlsIC(IDAMem IDA_mem)475 static int IDANlsIC(IDAMem IDA_mem)
476 {
477   int retval, nj, is;
478   N_Vector tv1, tv2, tv3;
479   booleantype sensi_sim;
480 
481   /* Are we computing sensitivities with the IDA_SIMULTANEOUS approach? */
482   sensi_sim = (IDA_mem->ida_sensi && (IDA_mem->ida_ism==IDA_SIMULTANEOUS));
483 
484   tv1 = IDA_mem->ida_ee;
485   tv2 = IDA_mem->ida_tempv2;
486   tv3 = IDA_mem->ida_phi[2];
487 
488   /* Evaluate RHS. */
489   retval = IDA_mem->ida_res(IDA_mem->ida_t0, IDA_mem->ida_yy0, IDA_mem->ida_yp0,
490                             IDA_mem->ida_delta, IDA_mem->ida_user_data);
491   IDA_mem->ida_nre++;
492   if(retval < 0) return(IDA_RES_FAIL);
493   if(retval > 0) return(IDA_FIRST_RES_FAIL);
494 
495   /* Save the residual. */
496   N_VScale(ONE, IDA_mem->ida_delta, IDA_mem->ida_savres);
497 
498   if(sensi_sim) {
499 
500     /*Evaluate sensitivity RHS and save it in savresS. */
501     retval = IDA_mem->ida_resS(IDA_mem->ida_Ns, IDA_mem->ida_t0,
502                                IDA_mem->ida_yy0, IDA_mem->ida_yp0,
503                                IDA_mem->ida_delta,
504                                IDA_mem->ida_yyS0, IDA_mem->ida_ypS0,
505                                IDA_mem->ida_deltaS,
506                                IDA_mem->ida_user_dataS,
507                                IDA_mem->ida_tmpS1, IDA_mem->ida_tmpS2,
508                                IDA_mem->ida_tmpS3);
509     IDA_mem->ida_nrSe++;
510     if(retval < 0) return(IDA_RES_FAIL);
511     if(retval > 0) return(IDA_FIRST_RES_FAIL);
512 
513     for(is=0; is<IDA_mem->ida_Ns; is++)
514       N_VScale(ONE, IDA_mem->ida_deltaS[is], IDA_mem->ida_savresS[is]);
515   }
516 
517   /* Loop over nj = number of linear solve Jacobian setups. */
518   for(nj = 1; nj <= IDA_mem->ida_maxnj; nj++) {
519 
520     /* If there is a setup routine, call it. */
521     if(IDA_mem->ida_lsetup) {
522       IDA_mem->ida_nsetups++;
523       retval = IDA_mem->ida_lsetup(IDA_mem, IDA_mem->ida_yy0,
524                                    IDA_mem->ida_yp0, IDA_mem->ida_delta,
525                                    tv1, tv2, tv3);
526       if(retval < 0) return(IDA_LSETUP_FAIL);
527       if(retval > 0) return(IC_FAIL_RECOV);
528     }
529 
530     /* Call the Newton iteration routine, and return if successful.  */
531     retval = IDANewtonIC(IDA_mem);
532     if(retval == IDA_SUCCESS) return(IDA_SUCCESS);
533 
534     /* If converging slowly and lsetup is nontrivial, retry. */
535     if(retval == IC_SLOW_CONVRG && IDA_mem->ida_lsetup) {
536       N_VScale(ONE, IDA_mem->ida_savres, IDA_mem->ida_delta);
537 
538       if(sensi_sim)
539         for(is=0; is<IDA_mem->ida_Ns; is++)
540           N_VScale(ONE, IDA_mem->ida_savresS[is], IDA_mem->ida_deltaS[is]);
541 
542       continue;
543     } else {
544       return(retval);
545     }
546 
547   }   /* End of nj loop */
548 
549   /* No convergence after maxnj tries; return with retval=IC_SLOW_CONVRG */
550   return(retval);
551 
552 }
553 
554 /*
555  * -----------------------------------------------------------------
556  * IDANewtonIC
557  * -----------------------------------------------------------------
558  * IDANewtonIC performs the Newton iteration to solve for consistent
559  * initial conditions.  It calls IDALineSrch within each iteration.
560  * On return, savres contains the current residual vector.
561  *
562  * The return value is IDA_SUCCESS = 0 if no error occurred.
563  * The error return values (positive) considered recoverable are:
564  *  IC_FAIL_RECOV      if res or lsolve failed recoverably
565  *  IC_CONSTR_FAILED   if the constraints could not be met
566  *  IC_LINESRCH_FAILED if the linesearch failed (either on steptol test
567  *                     or on maxbacks test)
568  *  IC_CONV_FAIL       if the Newton iterations failed to converge
569  *  IC_SLOW_CONVRG     if the iterations appear to be converging slowly.
570  *                     They failed the convergence test, but showed
571  *                     an overall norm reduction (by a factor of < 0.1)
572  *                     or a convergence rate <= ICRATEMAX).
573  * The error return values (negative) considered non-recoverable are:
574  *  IDA_RES_FAIL   if res had a non-recoverable error
575  *  IDA_LSOLVE_FAIL      if lsolve had a non-recoverable error
576  * -----------------------------------------------------------------
577  */
578 
IDANewtonIC(IDAMem IDA_mem)579 static int IDANewtonIC(IDAMem IDA_mem)
580 {
581   int retval, mnewt, is;
582   realtype delnorm, fnorm, fnorm0, oldfnrm, rate;
583   booleantype sensi_sim;
584 
585   /* Are we computing sensitivities with the IDA_SIMULTANEOUS approach? */
586   sensi_sim = (IDA_mem->ida_sensi && (IDA_mem->ida_ism==IDA_SIMULTANEOUS));
587 
588   /* Set pointer for vector delnew */
589   IDA_mem->ida_delnew = IDA_mem->ida_phi[2];
590 
591   /* Call the linear solve function to get the Newton step, delta. */
592   retval = IDA_mem->ida_lsolve(IDA_mem, IDA_mem->ida_delta,
593                                IDA_mem->ida_ewt, IDA_mem->ida_yy0,
594                                IDA_mem->ida_yp0, IDA_mem->ida_savres);
595   if(retval < 0) return(IDA_LSOLVE_FAIL);
596   if(retval > 0) return(IC_FAIL_RECOV);
597 
598   /* Compute the norm of the step. */
599   fnorm = IDAWrmsNorm(IDA_mem, IDA_mem->ida_delta, IDA_mem->ida_ewt, SUNFALSE);
600 
601   /* Call the lsolve function to get correction vectors deltaS. */
602   if (sensi_sim) {
603     for(is=0;is<IDA_mem->ida_Ns;is++) {
604       retval = IDA_mem->ida_lsolve(IDA_mem, IDA_mem->ida_deltaS[is],
605                                    IDA_mem->ida_ewtS[is], IDA_mem->ida_yy0,
606                                    IDA_mem->ida_yp0, IDA_mem->ida_savres);
607       if(retval < 0) return(IDA_LSOLVE_FAIL);
608       if(retval > 0) return(IC_FAIL_RECOV);
609     }
610     /* Update the norm of delta. */
611     fnorm = IDASensWrmsNormUpdate(IDA_mem, fnorm, IDA_mem->ida_deltaS,
612                                   IDA_mem->ida_ewtS, SUNFALSE);
613   }
614 
615   /* Test for convergence. Return now if the norm is small. */
616   if(IDA_mem->ida_sysindex == 0)
617     fnorm *= IDA_mem->ida_tscale * SUNRabs(IDA_mem->ida_cj);
618   if(fnorm <= IDA_mem->ida_epsNewt) return(IDA_SUCCESS);
619   fnorm0 = fnorm;
620 
621   /* Initialize rate to avoid compiler warning message */
622   rate = ZERO;
623 
624   /* Newton iteration loop */
625 
626   for(mnewt = 0; mnewt < IDA_mem->ida_maxnit; mnewt++) {
627 
628     IDA_mem->ida_nni++;
629     delnorm = fnorm;
630     oldfnrm = fnorm;
631 
632     /* Call the Linesearch function and return if it failed. */
633     retval = IDALineSrch(IDA_mem, &delnorm, &fnorm);
634     if(retval != IDA_SUCCESS) return(retval);
635 
636     /* Set the observed convergence rate and test for convergence. */
637     rate = fnorm/oldfnrm;
638     if(fnorm <= IDA_mem->ida_epsNewt) return(IDA_SUCCESS);
639 
640     /* If not converged, copy new step vector, and loop. */
641     N_VScale(ONE, IDA_mem->ida_delnew, IDA_mem->ida_delta);
642 
643     if(sensi_sim) {
644       /* Update the iteration's step for sensitivities. */
645       for(is=0; is<IDA_mem->ida_Ns; is++)
646         N_VScale(ONE, IDA_mem->ida_delnewS[is], IDA_mem->ida_deltaS[is]);
647     }
648 
649   }   /* End of Newton iteration loop */
650 
651   /* Return either IC_SLOW_CONVRG or recoverable fail flag. */
652   if(rate <= ICRATEMAX || fnorm < PT1*fnorm0) return(IC_SLOW_CONVRG);
653   return(IC_CONV_FAIL);
654 }
655 
656 /*
657  * -----------------------------------------------------------------
658  * IDALineSrch
659  * -----------------------------------------------------------------
660  * IDALineSrch performs the Linesearch algorithm with the
661  * calculation of consistent initial conditions.
662  *
663  * On entry, yy0 and yp0 are the current values of y and y', the
664  * Newton step is delta, the current residual vector F is savres,
665  * delnorm is WRMS-norm(delta), and fnorm is the norm of the vector
666  * J-inverse F.
667  *
668  * On a successful return, yy0, yp0, and savres have been updated,
669  * delnew contains the current value of J-inverse F, and fnorm is
670  * WRMS-norm(delnew).
671  *
672  * The return value is IDA_SUCCESS = 0 if no error occurred.
673  * The error return values (positive) considered recoverable are:
674  *  IC_FAIL_RECOV      if res or lsolve failed recoverably
675  *  IC_CONSTR_FAILED   if the constraints could not be met
676  *  IC_LINESRCH_FAILED if the linesearch failed (either on steptol test
677  *                     or on maxbacks test)
678  * The error return values (negative) considered non-recoverable are:
679  *  IDA_RES_FAIL   if res had a non-recoverable error
680  *  IDA_LSOLVE_FAIL      if lsolve had a non-recoverable error
681  * -----------------------------------------------------------------
682  */
683 
IDALineSrch(IDAMem IDA_mem,realtype * delnorm,realtype * fnorm)684 static int IDALineSrch(IDAMem IDA_mem, realtype *delnorm, realtype *fnorm)
685 {
686   booleantype conOK;
687   int retval, is, nbacks;
688   realtype f1norm, fnormp, f1normp, ratio, lambda, minlam, slpi;
689   N_Vector mc;
690   booleantype sensi_sim;
691 
692   /* Initialize work space pointers, f1norm, ratio.
693      (Use of mc in constraint check does not conflict with ypnew.) */
694   mc = IDA_mem->ida_ee;
695   IDA_mem->ida_dtemp = IDA_mem->ida_phi[3];
696   IDA_mem->ida_ynew = IDA_mem->ida_tempv2;
697   IDA_mem->ida_ypnew = IDA_mem->ida_ee;
698   f1norm = (*fnorm)*(*fnorm)*HALF;
699   ratio = ONE;
700 
701   /* If there are constraints, check and reduce step if necessary. */
702   if(IDA_mem->ida_constraintsSet) {
703 
704     /* Update y and check constraints. */
705     IDANewy(IDA_mem);
706     conOK = N_VConstrMask(IDA_mem->ida_constraints,
707                           IDA_mem->ida_ynew, mc);
708 
709     if(!conOK) {
710       /* Not satisfied.  Compute scaled step to satisfy constraints. */
711       N_VProd(mc, IDA_mem->ida_delta, IDA_mem->ida_dtemp);
712       ratio = PT99*N_VMinQuotient(IDA_mem->ida_yy0, IDA_mem->ida_dtemp);
713       (*delnorm) *= ratio;
714       if((*delnorm) <= IDA_mem->ida_steptol)
715         return(IC_CONSTR_FAILED);
716       N_VScale(ratio, IDA_mem->ida_delta, IDA_mem->ida_delta);
717     }
718 
719   } /* End of constraints check */
720 
721   slpi = -TWO*f1norm*ratio;
722   minlam = IDA_mem->ida_steptol / (*delnorm);
723   lambda = ONE;
724   nbacks = 0;
725 
726   /* Are we computing sensitivities with the IDA_SIMULTANEOUS approach? */
727   sensi_sim = (IDA_mem->ida_sensi && (IDA_mem->ida_ism==IDA_SIMULTANEOUS));
728 
729   /* In IDA_Y_INIT case, set ypnew = yp0 (fixed) for linesearch. */
730   if(IDA_mem->ida_icopt == IDA_Y_INIT) {
731     N_VScale(ONE, IDA_mem->ida_yp0, IDA_mem->ida_ypnew);
732 
733     /* do the same for sensitivities. */
734     if(sensi_sim) {
735       for(is=0; is<IDA_mem->ida_Ns; is++)
736         N_VScale(ONE, IDA_mem->ida_ypS0[is], IDA_mem->ida_ypS0new[is]);
737     }
738   }
739 
740   /* Loop on linesearch variable lambda. */
741 
742   for(;;) {
743 
744     if (nbacks == IDA_mem->ida_maxbacks)
745       return(IC_LINESRCH_FAILED);
746     /* Get new (y,y') = (ynew,ypnew) and norm of new function value. */
747     IDANewyyp(IDA_mem, lambda);
748     retval = IDAfnorm(IDA_mem, &fnormp);
749     if(retval != IDA_SUCCESS) return(retval);
750 
751     /* If lsoff option is on, break out. */
752     if(IDA_mem->ida_lsoff) break;
753 
754     /* Do alpha-condition test. */
755     f1normp = fnormp*fnormp*HALF;
756     if(f1normp <= f1norm + ALPHALS*slpi*lambda) break;
757     if(lambda < minlam) return(IC_LINESRCH_FAILED);
758     lambda /= TWO;
759     IDA_mem->ida_nbacktr++; nbacks++;
760 
761   }  /* End of breakout linesearch loop */
762 
763   /* Update yy0, yp0. */
764   N_VScale(ONE, IDA_mem->ida_ynew, IDA_mem->ida_yy0);
765 
766   if(sensi_sim) {
767     /* Update yyS0 and ypS0. */
768     for(is=0; is<IDA_mem->ida_Ns; is++)
769       N_VScale(ONE, IDA_mem->ida_yyS0new[is], IDA_mem->ida_yyS0[is]);
770   }
771 
772   if(IDA_mem->ida_icopt == IDA_YA_YDP_INIT) {
773     N_VScale(ONE, IDA_mem->ida_ypnew, IDA_mem->ida_yp0);
774 
775     if(sensi_sim)
776       for(is=0; is<IDA_mem->ida_Ns; is++)
777         N_VScale(ONE, IDA_mem->ida_ypS0new[is], IDA_mem->ida_ypS0[is]);
778 
779   }
780   /* Update fnorm, then return. */
781   *fnorm = fnormp;
782   return(IDA_SUCCESS);
783 
784 }
785 
786 /*
787  * -----------------------------------------------------------------
788  * IDAfnorm
789  * -----------------------------------------------------------------
790  * IDAfnorm computes the norm of the current function value, by
791  * evaluating the DAE residual function, calling the linear
792  * system solver, and computing a WRMS-norm.
793  *
794  * On return, savres contains the current residual vector F, and
795  * delnew contains J-inverse F.
796  *
797  * The return value is IDA_SUCCESS = 0 if no error occurred, or
798  *  IC_FAIL_RECOV    if res or lsolve failed recoverably, or
799  *  IDA_RES_FAIL     if res had a non-recoverable error, or
800  *  IDA_LSOLVE_FAIL  if lsolve had a non-recoverable error.
801  * -----------------------------------------------------------------
802  */
803 
IDAfnorm(IDAMem IDA_mem,realtype * fnorm)804 static int IDAfnorm(IDAMem IDA_mem, realtype *fnorm)
805 {
806   int retval, is;
807 
808   /* Get residual vector F, return if failed, and save F in savres. */
809   retval = IDA_mem->ida_res(IDA_mem->ida_t0, IDA_mem->ida_ynew,
810                             IDA_mem->ida_ypnew, IDA_mem->ida_delnew,
811                             IDA_mem->ida_user_data);
812   IDA_mem->ida_nre++;
813   if(retval < 0) return(IDA_RES_FAIL);
814   if(retval > 0) return(IC_FAIL_RECOV);
815 
816   N_VScale(ONE, IDA_mem->ida_delnew, IDA_mem->ida_savres);
817 
818   /* Call the linear solve function to get J-inverse F; return if failed. */
819   retval = IDA_mem->ida_lsolve(IDA_mem, IDA_mem->ida_delnew, IDA_mem->ida_ewt,
820                                IDA_mem->ida_ynew, IDA_mem->ida_ypnew,
821                                IDA_mem->ida_savres);
822   if(retval < 0) return(IDA_LSOLVE_FAIL);
823   if(retval > 0) return(IC_FAIL_RECOV);
824 
825   /* Compute the WRMS-norm. */
826   *fnorm = IDAWrmsNorm(IDA_mem, IDA_mem->ida_delnew, IDA_mem->ida_ewt, SUNFALSE);
827 
828 
829   /* Are we computing SENSITIVITIES with the IDA_SIMULTANEOUS approach? */
830 
831   if(IDA_mem->ida_sensi && (IDA_mem->ida_ism==IDA_SIMULTANEOUS)) {
832 
833     /* Evaluate the residual for sensitivities. */
834     retval = IDA_mem->ida_resS(IDA_mem->ida_Ns, IDA_mem->ida_t0,
835                                IDA_mem->ida_ynew, IDA_mem->ida_ypnew,
836                                IDA_mem->ida_savres,
837                                IDA_mem->ida_yyS0new,
838                                IDA_mem->ida_ypS0new,
839                                IDA_mem->ida_delnewS,
840                                IDA_mem->ida_user_dataS,
841                                IDA_mem->ida_tmpS1, IDA_mem->ida_tmpS2,
842                                IDA_mem->ida_tmpS3);
843     IDA_mem->ida_nrSe++;
844     if(retval < 0) return(IDA_RES_FAIL);
845     if(retval > 0) return(IC_FAIL_RECOV);
846 
847     /* Save delnewS in savresS. */
848     for(is=0; is<IDA_mem->ida_Ns; is++)
849       N_VScale(ONE, IDA_mem->ida_delnewS[is], IDA_mem->ida_savresS[is]);
850 
851     /* Call the linear solve function to get J-inverse deltaS. */
852     for(is=0; is<IDA_mem->ida_Ns; is++) {
853 
854       retval = IDA_mem->ida_lsolve(IDA_mem, IDA_mem->ida_delnewS[is],
855                                    IDA_mem->ida_ewtS[is],
856                                    IDA_mem->ida_ynew,
857                                    IDA_mem->ida_ypnew,
858                                    IDA_mem->ida_savres);
859       if(retval < 0) return(IDA_LSOLVE_FAIL);
860       if(retval > 0) return(IC_FAIL_RECOV);
861     }
862 
863     /* Include sensitivities in norm. */
864     *fnorm = IDASensWrmsNormUpdate(IDA_mem, *fnorm, IDA_mem->ida_delnewS,
865                                    IDA_mem->ida_ewtS, SUNFALSE);
866   }
867 
868   /* Rescale norm if index = 0. */
869   if(IDA_mem->ida_sysindex == 0)
870     (*fnorm) *= IDA_mem->ida_tscale * SUNRabs(IDA_mem->ida_cj);
871 
872   return(IDA_SUCCESS);
873 
874 }
875 
876 /*
877  * -----------------------------------------------------------------
878  * IDANewyyp
879  * -----------------------------------------------------------------
880  * IDANewyyp updates the vectors ynew and ypnew from yy0 and yp0,
881  * using the current step vector lambda*delta, in a manner
882  * depending on icopt and the input id vector.
883  *
884  * The return value is always IDA_SUCCESS = 0.
885  * -----------------------------------------------------------------
886  */
887 
IDANewyyp(IDAMem IDA_mem,realtype lambda)888 static int IDANewyyp(IDAMem IDA_mem, realtype lambda)
889 {
890   int retval;
891 
892   retval = IDA_SUCCESS;
893 
894   /* IDA_YA_YDP_INIT case: ynew  = yy0 - lambda*delta    where id_i = 0
895                            ypnew = yp0 - cj*lambda*delta where id_i = 1. */
896   if(IDA_mem->ida_icopt == IDA_YA_YDP_INIT) {
897 
898     N_VProd(IDA_mem->ida_id, IDA_mem->ida_delta, IDA_mem->ida_dtemp);
899     N_VLinearSum(ONE, IDA_mem->ida_yp0, -IDA_mem->ida_cj*lambda,
900                  IDA_mem->ida_dtemp, IDA_mem->ida_ypnew);
901     N_VLinearSum(ONE, IDA_mem->ida_delta, -ONE,
902                  IDA_mem->ida_dtemp, IDA_mem->ida_dtemp);
903     N_VLinearSum(ONE, IDA_mem->ida_yy0, -lambda,
904                  IDA_mem->ida_dtemp, IDA_mem->ida_ynew);
905 
906   }else if(IDA_mem->ida_icopt == IDA_Y_INIT) {
907 
908     /* IDA_Y_INIT case: ynew = yy0 - lambda*delta. (ypnew = yp0 preset.) */
909     N_VLinearSum(ONE, IDA_mem->ida_yy0, -lambda, IDA_mem->ida_delta,
910                  IDA_mem->ida_ynew);
911   }
912 
913   if(IDA_mem->ida_sensi && (IDA_mem->ida_ism==IDA_SIMULTANEOUS))
914     retval = IDASensNewyyp(IDA_mem, lambda);
915 
916   return(retval);
917 
918 }
919 
920 /*
921  * -----------------------------------------------------------------
922  * IDANewy
923  * -----------------------------------------------------------------
924  * IDANewy updates the vector ynew from yy0,
925  * using the current step vector delta, in a manner
926  * depending on icopt and the input id vector.
927  *
928  * The return value is always IDA_SUCCESS = 0.
929  * -----------------------------------------------------------------
930  */
931 
IDANewy(IDAMem IDA_mem)932 static int IDANewy(IDAMem IDA_mem)
933 {
934 
935   /* IDA_YA_YDP_INIT case: ynew = yy0 - delta    where id_i = 0. */
936   if(IDA_mem->ida_icopt == IDA_YA_YDP_INIT) {
937     N_VProd(IDA_mem->ida_id, IDA_mem->ida_delta, IDA_mem->ida_dtemp);
938     N_VLinearSum(ONE, IDA_mem->ida_delta, -ONE,
939                  IDA_mem->ida_dtemp, IDA_mem->ida_dtemp);
940     N_VLinearSum(ONE, IDA_mem->ida_yy0, -ONE,
941                  IDA_mem->ida_dtemp, IDA_mem->ida_ynew);
942     return(IDA_SUCCESS);
943   }
944 
945   /* IDA_Y_INIT case: ynew = yy0 - delta. */
946   N_VLinearSum(ONE, IDA_mem->ida_yy0, -ONE, IDA_mem->ida_delta,
947                IDA_mem->ida_ynew);
948   return(IDA_SUCCESS);
949 
950 }
951 /*
952  * -----------------------------------------------------------------
953  * Sensitivity I.C. functions
954  * -----------------------------------------------------------------
955  */
956 
957 /*
958  * -----------------------------------------------------------------
959  * IDASensNlsIC
960  * -----------------------------------------------------------------
961  * IDASensNlsIC solves nonlinear systems for sensitivities consistent
962  * initial conditions.  It mainly relies on IDASensNewtonIC.
963  *
964  * The return value is IDA_SUCCESS = 0 if no error occurred.
965  * The error return values (positive) considered recoverable are:
966  *  IC_FAIL_RECOV      if res, lsetup, or lsolve failed recoverably
967  *  IC_CONSTR_FAILED   if the constraints could not be met
968  *  IC_LINESRCH_FAILED if the linesearch failed (either on steptol test
969  *                     or on maxbacks test)
970  *  IC_CONV_FAIL       if the Newton iterations failed to converge
971  *  IC_SLOW_CONVRG     if the iterations are converging slowly
972  *                     (failed the convergence test, but showed
973  *                     norm reduction or convergence rate < 1)
974  * The error return values (negative) considered non-recoverable are:
975  *  IDA_RES_FAIL       if res had a non-recoverable error
976  *  IDA_FIRST_RES_FAIL if res failed recoverably on the first call
977  *  IDA_LSETUP_FAIL    if lsetup had a non-recoverable error
978  *  IDA_LSOLVE_FAIL    if lsolve had a non-recoverable error
979  * -----------------------------------------------------------------
980  */
IDASensNlsIC(IDAMem IDA_mem)981 static int IDASensNlsIC(IDAMem IDA_mem)
982 {
983   int retval;
984   int is, nj;
985 
986   retval = IDA_mem->ida_resS(IDA_mem->ida_Ns, IDA_mem->ida_t0,
987                              IDA_mem->ida_yy0, IDA_mem->ida_yp0,
988                              IDA_mem->ida_delta, IDA_mem->ida_yyS0,
989                              IDA_mem->ida_ypS0,
990                              IDA_mem->ida_deltaS,
991                              IDA_mem->ida_user_dataS,
992                              IDA_mem->ida_tmpS1, IDA_mem->ida_tmpS2,
993                              IDA_mem->ida_tmpS3);
994   IDA_mem->ida_nrSe++;
995   if(retval < 0) return(IDA_RES_FAIL);
996   if(retval > 0) return(IDA_FIRST_RES_FAIL);
997 
998   /* Save deltaS */
999   for(is=0; is<IDA_mem->ida_Ns; is++)
1000     N_VScale(ONE, IDA_mem->ida_deltaS[is], IDA_mem->ida_savresS[is]);
1001 
1002   /* Loop over nj = number of linear solve Jacobian setups. */
1003 
1004   for(nj = 1; nj <= 2; nj++) {
1005 
1006     /* Call the Newton iteration routine */
1007     retval = IDASensNewtonIC(IDA_mem);
1008     if(retval == IDA_SUCCESS) return(IDA_SUCCESS);
1009 
1010     /* If converging slowly and lsetup is nontrivial and this is the first pass,
1011        update Jacobian and retry. */
1012     if(retval == IC_SLOW_CONVRG && IDA_mem->ida_lsetup && nj==1) {
1013 
1014       /* Restore deltaS. */
1015       for(is=0; is<IDA_mem->ida_Ns; is++)
1016         N_VScale(ONE, IDA_mem->ida_savresS[is], IDA_mem->ida_deltaS[is]);
1017 
1018       IDA_mem->ida_nsetupsS++;
1019       retval = IDA_mem->ida_lsetup(IDA_mem, IDA_mem->ida_yy0, IDA_mem->ida_yp0,
1020                                    IDA_mem->ida_delta, IDA_mem->ida_tmpS1,
1021                                    IDA_mem->ida_tmpS2, IDA_mem->ida_tmpS3);
1022       if(retval < 0) return(IDA_LSETUP_FAIL);
1023       if(retval > 0) return(IC_FAIL_RECOV);
1024 
1025       continue;
1026     } else {
1027       return(retval);
1028     }
1029   }
1030 
1031   return(IDA_SUCCESS);
1032 }
1033 
1034 /*
1035  * -----------------------------------------------------------------
1036  * IDASensNewtonIC
1037  * -----------------------------------------------------------------
1038  * IDANewtonIC performs the Newton iteration to solve for
1039  * sensitivities consistent initial conditions.  It calls
1040  * IDASensLineSrch within each iteration.
1041  * On return, savresS contains the current residual vectors.
1042  *
1043  * The return value is IDA_SUCCESS = 0 if no error occurred.
1044  * The error return values (positive) considered recoverable are:
1045  *  IC_FAIL_RECOV      if res or lsolve failed recoverably
1046  *  IC_CONSTR_FAILED   if the constraints could not be met
1047  *  IC_LINESRCH_FAILED if the linesearch failed (either on steptol test
1048  *                     or on maxbacks test)
1049  *  IC_CONV_FAIL       if the Newton iterations failed to converge
1050  *  IC_SLOW_CONVRG     if the iterations appear to be converging slowly.
1051  *                     They failed the convergence test, but showed
1052  *                     an overall norm reduction (by a factor of < 0.1)
1053  *                     or a convergence rate <= ICRATEMAX).
1054  * The error return values (negative) considered non-recoverable are:
1055  *  IDA_RES_FAIL   if res had a non-recoverable error
1056  *  IDA_LSOLVE_FAIL      if lsolve had a non-recoverable error
1057  * -----------------------------------------------------------------
1058  */
IDASensNewtonIC(IDAMem IDA_mem)1059 static int IDASensNewtonIC(IDAMem IDA_mem)
1060 {
1061   int retval, is, mnewt;
1062   realtype delnorm, fnorm, fnorm0, oldfnrm, rate;
1063 
1064   for(is=0;is<IDA_mem->ida_Ns;is++) {
1065 
1066     /* Call the linear solve function to get the Newton step, delta. */
1067     retval = IDA_mem->ida_lsolve(IDA_mem, IDA_mem->ida_deltaS[is],
1068                                  IDA_mem->ida_ewtS[is], IDA_mem->ida_yy0,
1069                                  IDA_mem->ida_yp0, IDA_mem->ida_delta);
1070     if(retval < 0) return(IDA_LSOLVE_FAIL);
1071     if(retval > 0) return(IC_FAIL_RECOV);
1072 
1073   }
1074     /* Compute the norm of the step and return if it is small enough */
1075   fnorm = IDASensWrmsNorm(IDA_mem, IDA_mem->ida_deltaS,
1076                           IDA_mem->ida_ewtS, SUNFALSE);
1077   if(IDA_mem->ida_sysindex == 0)
1078     fnorm *= IDA_mem->ida_tscale * SUNRabs(IDA_mem->ida_cj);
1079   if(fnorm <= IDA_mem->ida_epsNewt) return(IDA_SUCCESS);
1080   fnorm0 = fnorm;
1081 
1082   rate = ZERO;
1083 
1084   /* Newton iteration loop */
1085   for(mnewt = 0; mnewt < IDA_mem->ida_maxnit; mnewt++) {
1086 
1087     IDA_mem->ida_nniS++;
1088     delnorm = fnorm;
1089     oldfnrm = fnorm;
1090 
1091     /* Call the Linesearch function and return if it failed. */
1092     retval = IDASensLineSrch(IDA_mem, &delnorm, &fnorm);
1093     if(retval != IDA_SUCCESS) return(retval);
1094 
1095     /* Set the observed convergence rate and test for convergence. */
1096     rate = fnorm/oldfnrm;
1097     if(fnorm <= IDA_mem->ida_epsNewt) return(IDA_SUCCESS);
1098 
1099     /* If not converged, copy new step vectors, and loop. */
1100     for(is=0; is<IDA_mem->ida_Ns; is++)
1101       N_VScale(ONE, IDA_mem->ida_delnewS[is], IDA_mem->ida_deltaS[is]);
1102 
1103   }   /* End of Newton iteration loop */
1104 
1105   /* Return either IC_SLOW_CONVRG or recoverable fail flag. */
1106   if(rate <= ICRATEMAX || fnorm < PT1*fnorm0) return(IC_SLOW_CONVRG);
1107   return(IC_CONV_FAIL);
1108 }
1109 
1110 /*
1111  * -----------------------------------------------------------------
1112  * IDASensLineSrch
1113  * -----------------------------------------------------------------
1114  * IDASensLineSrch performs the Linesearch algorithm with the
1115  * calculation of consistent initial conditions for sensitivities
1116  * systems.
1117  *
1118  * On entry, yyS0 and ypS0 contain the current values, the Newton
1119  * steps are contained in deltaS, the current residual vectors FS are
1120  * savresS, delnorm is sens-WRMS-norm(deltaS), and fnorm is
1121  * max { WRMS-norm( J-inverse FS[is] ) : is=1,2,...,Ns }
1122  *
1123  * On a successful return, yy0, yp0, and savres have been updated,
1124  * delnew contains the current values of J-inverse FS, and fnorm is
1125  * max { WRMS-norm(delnewS[is]) : is = 1,2,...Ns }
1126  *
1127  * The return value is IDA_SUCCESS = 0 if no error occurred.
1128  * The error return values (positive) considered recoverable are:
1129  *  IC_FAIL_RECOV      if res or lsolve failed recoverably
1130  *  IC_CONSTR_FAILED   if the constraints could not be met
1131  *  IC_LINESRCH_FAILED if the linesearch failed (either on steptol test
1132  *                     or on maxbacks test)
1133  * The error return values (negative) considered non-recoverable are:
1134  *  IDA_RES_FAIL   if res had a non-recoverable error
1135  *  IDA_LSOLVE_FAIL      if lsolve had a non-recoverable error
1136  * -----------------------------------------------------------------
1137  */
1138 
IDASensLineSrch(IDAMem IDA_mem,realtype * delnorm,realtype * fnorm)1139 static int IDASensLineSrch(IDAMem IDA_mem, realtype *delnorm, realtype *fnorm)
1140 {
1141   int is, retval, nbacks;
1142   realtype f1norm, fnormp, f1normp, slpi, minlam;
1143   realtype lambda, ratio;
1144 
1145   /* Set work space pointer. */
1146   IDA_mem->ida_dtemp = IDA_mem->ida_phi[3];
1147 
1148   f1norm = (*fnorm)*(*fnorm)*HALF;
1149 
1150   /* Initialize local variables. */
1151   ratio = ONE;
1152   slpi = -TWO*f1norm*ratio;
1153   minlam = IDA_mem->ida_steptol / (*delnorm);
1154   lambda = ONE;
1155   nbacks = 0;
1156 
1157   for(;;) {
1158 
1159     if (nbacks == IDA_mem->ida_maxbacks)
1160       return(IC_LINESRCH_FAILED);
1161     /* Get new iteration in (ySnew, ypSnew). */
1162     IDASensNewyyp(IDA_mem, lambda);
1163 
1164     /* Get the norm of new function value. */
1165     retval = IDASensfnorm(IDA_mem, &fnormp);
1166     if (retval!=IDA_SUCCESS) return retval;
1167 
1168     /* If lsoff option is on, break out. */
1169     if(IDA_mem->ida_lsoff) break;
1170 
1171     /* Do alpha-condition test. */
1172     f1normp = fnormp*fnormp*HALF;
1173     if(f1normp <= f1norm + ALPHALS*slpi*lambda) break;
1174     if(lambda < minlam) return(IC_LINESRCH_FAILED);
1175     lambda /= TWO;
1176     IDA_mem->ida_nbacktr++; nbacks++;
1177   }
1178 
1179   /* Update yyS0, ypS0 and fnorm and return. */
1180   for(is=0; is<IDA_mem->ida_Ns; is++) {
1181     N_VScale(ONE, IDA_mem->ida_yyS0new[is], IDA_mem->ida_yyS0[is]);
1182   }
1183 
1184   if (IDA_mem->ida_icopt == IDA_YA_YDP_INIT)
1185     for(is=0; is<IDA_mem->ida_Ns; is++)
1186       N_VScale(ONE, IDA_mem->ida_ypS0new[is], IDA_mem->ida_ypS0[is]);
1187 
1188   *fnorm = fnormp;
1189   return(IDA_SUCCESS);
1190 }
1191 
1192 /*
1193  * -----------------------------------------------------------------
1194  * IDASensfnorm
1195  * -----------------------------------------------------------------
1196  * IDASensfnorm computes the norm of the current function value, by
1197  * evaluating the sensitivity residual function, calling the linear
1198  * system solver, and computing a WRMS-norm.
1199  *
1200  * On return, savresS contains the current residual vectors FS, and
1201  * delnewS contains J-inverse FS.
1202  *
1203  * The return value is IDA_SUCCESS = 0 if no error occurred, or
1204  *  IC_FAIL_RECOV    if res or lsolve failed recoverably, or
1205  *  IDA_RES_FAIL     if res had a non-recoverable error, or
1206  *  IDA_LSOLVE_FAIL  if lsolve had a non-recoverable error.
1207  * -----------------------------------------------------------------
1208  */
1209 
IDASensfnorm(IDAMem IDA_mem,realtype * fnorm)1210 static int IDASensfnorm(IDAMem IDA_mem, realtype *fnorm)
1211 {
1212   int is, retval;
1213 
1214   /* Get sensitivity residual */
1215   retval = IDA_mem->ida_resS(IDA_mem->ida_Ns, IDA_mem->ida_t0,
1216                              IDA_mem->ida_yy0, IDA_mem->ida_yp0,
1217                              IDA_mem->ida_delta,
1218                              IDA_mem->ida_yyS0new,
1219                              IDA_mem->ida_ypS0new,
1220                              IDA_mem->ida_delnewS,
1221                              IDA_mem->ida_user_dataS,
1222                              IDA_mem->ida_tmpS1, IDA_mem->ida_tmpS2,
1223                              IDA_mem->ida_tmpS3);
1224   IDA_mem->ida_nrSe++;
1225   if(retval < 0) return(IDA_RES_FAIL);
1226   if(retval > 0) return(IC_FAIL_RECOV);
1227 
1228   for(is=0; is<IDA_mem->ida_Ns; is++)
1229     N_VScale(ONE, IDA_mem->ida_delnewS[is], IDA_mem->ida_savresS[is]);
1230 
1231   /* Call linear solve function */
1232   for(is=0; is<IDA_mem->ida_Ns; is++) {
1233 
1234     retval = IDA_mem->ida_lsolve(IDA_mem, IDA_mem->ida_delnewS[is],
1235                                  IDA_mem->ida_ewtS[is],
1236                                  IDA_mem->ida_yy0,
1237                                  IDA_mem->ida_yp0,
1238                                  IDA_mem->ida_delta);
1239     if(retval < 0) return(IDA_LSOLVE_FAIL);
1240     if(retval > 0) return(IC_FAIL_RECOV);
1241   }
1242 
1243   /* Compute the WRMS-norm; rescale if index = 0. */
1244   *fnorm = IDASensWrmsNorm(IDA_mem, IDA_mem->ida_delnewS, IDA_mem->ida_ewtS, SUNFALSE);
1245   if(IDA_mem->ida_sysindex == 0)
1246     (*fnorm) *= IDA_mem->ida_tscale * SUNRabs(IDA_mem->ida_cj);
1247 
1248   return(IDA_SUCCESS);
1249 }
1250 
1251 /*
1252  * -----------------------------------------------------------------
1253  * IDASensNewyyp
1254  * -----------------------------------------------------------------
1255  * IDASensNewyyp computes the Newton updates for each of the
1256  * sensitivities systems using the current step vector lambda*delta,
1257  * in a manner depending on icopt and the input id vector.
1258  *
1259  * The return value is always IDA_SUCCESS = 0.
1260  * -----------------------------------------------------------------
1261  */
1262 
IDASensNewyyp(IDAMem IDA_mem,realtype lambda)1263 static int IDASensNewyyp(IDAMem IDA_mem, realtype lambda)
1264 {
1265   int is;
1266 
1267   if(IDA_mem->ida_icopt == IDA_YA_YDP_INIT) {
1268 
1269   /* IDA_YA_YDP_INIT case:
1270      - ySnew  = yS0  - lambda*deltaS    where id_i = 0
1271      - ypSnew = ypS0 - cj*lambda*delta  where id_i = 1. */
1272 
1273     for(is=0; is<IDA_mem->ida_Ns; is++) {
1274 
1275       /* It is ok to use dtemp as temporary vector here. */
1276       N_VProd(IDA_mem->ida_id, IDA_mem->ida_deltaS[is], IDA_mem->ida_dtemp);
1277       N_VLinearSum(ONE, IDA_mem->ida_ypS0[is], -IDA_mem->ida_cj*lambda,
1278                    IDA_mem->ida_dtemp, IDA_mem->ida_ypS0new[is]);
1279       N_VLinearSum(ONE, IDA_mem->ida_deltaS[is], -ONE,
1280                    IDA_mem->ida_dtemp, IDA_mem->ida_dtemp);
1281       N_VLinearSum(ONE, IDA_mem->ida_yyS0[is], -lambda,
1282                    IDA_mem->ida_dtemp, IDA_mem->ida_yyS0new[is]);
1283     } /* end loop is */
1284   }else {
1285 
1286     /* IDA_Y_INIT case:
1287        - ySnew = yS0 - lambda*deltaS. (ypnew = yp0 preset.) */
1288 
1289     for(is=0; is<IDA_mem->ida_Ns; is++)
1290       N_VLinearSum(ONE, IDA_mem->ida_yyS0[is], -lambda,
1291                    IDA_mem->ida_deltaS[is], IDA_mem->ida_yyS0new[is]);
1292   } /* end loop is */
1293   return(IDA_SUCCESS);
1294 }
1295 
1296 /*
1297  * -----------------------------------------------------------------
1298  * IDAICFailFlag
1299  * -----------------------------------------------------------------
1300  * IDAICFailFlag prints a message and sets the IDACalcIC return
1301  * value appropriate to the flag retval returned by IDANlsIC.
1302  * -----------------------------------------------------------------
1303  */
1304 
IDAICFailFlag(IDAMem IDA_mem,int retval)1305 static int IDAICFailFlag(IDAMem IDA_mem, int retval)
1306 {
1307 
1308   /* Depending on retval, print error message and return error flag. */
1309   switch(retval) {
1310 
1311     case IDA_RES_FAIL:
1312       IDAProcessError(IDA_mem, IDA_RES_FAIL, "IDAS", "IDACalcIC", MSG_IC_RES_NONREC);
1313       return(IDA_RES_FAIL);
1314 
1315     case IDA_FIRST_RES_FAIL:
1316       IDAProcessError(IDA_mem, IDA_FIRST_RES_FAIL, "IDAS", "IDACalcIC", MSG_IC_RES_FAIL);
1317       return(IDA_FIRST_RES_FAIL);
1318 
1319     case IDA_LSETUP_FAIL:
1320       IDAProcessError(IDA_mem, IDA_LSETUP_FAIL, "IDAS", "IDACalcIC", MSG_IC_SETUP_FAIL);
1321       return(IDA_LSETUP_FAIL);
1322 
1323     case IDA_LSOLVE_FAIL:
1324       IDAProcessError(IDA_mem, IDA_LSOLVE_FAIL, "IDAS", "IDACalcIC", MSG_IC_SOLVE_FAIL);
1325       return(IDA_LSOLVE_FAIL);
1326 
1327     case IC_FAIL_RECOV:
1328       IDAProcessError(IDA_mem, IDA_NO_RECOVERY, "IDAS", "IDACalcIC", MSG_IC_NO_RECOVERY);
1329       return(IDA_NO_RECOVERY);
1330 
1331     case IC_CONSTR_FAILED:
1332       IDAProcessError(IDA_mem, IDA_CONSTR_FAIL, "IDAS", "IDACalcIC", MSG_IC_FAIL_CONSTR);
1333       return(IDA_CONSTR_FAIL);
1334 
1335     case IC_LINESRCH_FAILED:
1336       IDAProcessError(IDA_mem, IDA_LINESEARCH_FAIL, "IDAS", "IDACalcIC", MSG_IC_FAILED_LINS);
1337       return(IDA_LINESEARCH_FAIL);
1338 
1339     case IC_CONV_FAIL:
1340       IDAProcessError(IDA_mem, IDA_CONV_FAIL, "IDAS", "IDACalcIC", MSG_IC_CONV_FAILED);
1341       return(IDA_CONV_FAIL);
1342 
1343     case IC_SLOW_CONVRG:
1344       IDAProcessError(IDA_mem, IDA_CONV_FAIL, "IDAS", "IDACalcIC", MSG_IC_CONV_FAILED);
1345       return(IDA_CONV_FAIL);
1346 
1347     case IDA_BAD_EWT:
1348       IDAProcessError(IDA_mem, IDA_BAD_EWT, "IDAS", "IDACalcIC", MSG_IC_BAD_EWT);
1349       return(IDA_BAD_EWT);
1350 
1351   }
1352   return -99;
1353 }
1354