1 /* -----------------------------------------------------------------------------
2  * Programmer(s): David J. Gardner @ LLNL
3  * -----------------------------------------------------------------------------
4  * SUNDIALS Copyright Start
5  * Copyright (c) 2002-2019, 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 is the implementation file for the SUNNonlinearSolver module
15  * implementation of Newton's method.
16  * ---------------------------------------------------------------------------*/
17 
18 #include <stdio.h>
19 #include <string.h>
20 #include <stdlib.h>
21 
22 #include <sunnonlinsol/sunnonlinsol_newton.h>
23 #include <sundials/sundials_math.h>
24 #include <sundials/sundials_nvector_senswrapper.h>
25 
26 /* Content structure accessibility macros  */
27 #define NEWTON_CONTENT(S) ( (SUNNonlinearSolverContent_Newton)(S->content) )
28 
29 /* Constant macros */
30 #define ONE  RCONST(1.0) /* real 1.0 */
31 
32 /*==============================================================================
33   Constructor to create a new Newton solver
34   ============================================================================*/
35 
SUNNonlinSol_Newton(N_Vector y)36 SUNNonlinearSolver SUNNonlinSol_Newton(N_Vector y)
37 {
38   SUNNonlinearSolver NLS;
39   SUNNonlinearSolver_Ops ops;
40   SUNNonlinearSolverContent_Newton content;
41 
42   /* Check that the supplied N_Vector is non-NULL */
43   if (y == NULL) return(NULL);
44 
45   /* Check that the supplied N_Vector supports all required operations */
46   if ( (y->ops->nvclone     == NULL) ||
47        (y->ops->nvdestroy   == NULL) ||
48        (y->ops->nvscale     == NULL) ||
49        (y->ops->nvlinearsum == NULL) )
50     return(NULL);
51 
52   /* Create nonlinear linear solver */
53   NLS = NULL;
54   NLS = (SUNNonlinearSolver) malloc(sizeof *NLS);
55   if (NLS == NULL) return(NULL);
56 
57   /* Create linear solver operation structure */
58   ops = NULL;
59   ops = (SUNNonlinearSolver_Ops) malloc(sizeof *ops);
60   if (ops == NULL) { free(NLS); return(NULL); }
61 
62   /* Attach operations */
63   ops->gettype         = SUNNonlinSolGetType_Newton;
64   ops->initialize      = SUNNonlinSolInitialize_Newton;
65   ops->setup           = NULL; /* no setup needed */
66   ops->solve           = SUNNonlinSolSolve_Newton;
67   ops->free            = SUNNonlinSolFree_Newton;
68   ops->setsysfn        = SUNNonlinSolSetSysFn_Newton;
69   ops->setlsetupfn     = SUNNonlinSolSetLSetupFn_Newton;
70   ops->setlsolvefn     = SUNNonlinSolSetLSolveFn_Newton;
71   ops->setctestfn      = SUNNonlinSolSetConvTestFn_Newton;
72   ops->setmaxiters     = SUNNonlinSolSetMaxIters_Newton;
73   ops->getnumiters     = SUNNonlinSolGetNumIters_Newton;
74   ops->getcuriter      = SUNNonlinSolGetCurIter_Newton;
75   ops->getnumconvfails = SUNNonlinSolGetNumConvFails_Newton;
76 
77   /* Create content */
78   content = NULL;
79   content = (SUNNonlinearSolverContent_Newton) malloc(sizeof *content);
80   if (content == NULL) { free(ops); free(NLS); return(NULL); }
81 
82   /* Initialize all components of content to 0/NULL */
83   memset(content, 0, sizeof(struct _SUNNonlinearSolverContent_Newton));
84 
85   /* Fill content */
86   content->Sys        = NULL;
87   content->LSetup     = NULL;
88   content->LSolve     = NULL;
89   content->CTest      = NULL;
90   content->delta      = N_VClone(y);
91   content->jcur       = SUNFALSE;
92   content->curiter    = 0;
93   content->maxiters   = 3;
94   content->niters     = 0;
95   content->nconvfails = 0;
96 
97   /* check if clone was successful */
98   if (content->delta == NULL) { free(ops); free(NLS); return(NULL); }
99 
100   /* Attach content and ops */
101   NLS->content = content;
102   NLS->ops     = ops;
103 
104   return(NLS);
105 }
106 
107 
108 /*==============================================================================
109   Constructor wrapper to create a new Newton solver for sensitivity solvers
110   ============================================================================*/
111 
SUNNonlinSol_NewtonSens(int count,N_Vector y)112 SUNNonlinearSolver SUNNonlinSol_NewtonSens(int count, N_Vector y)
113 {
114   SUNNonlinearSolver NLS;
115   N_Vector w;
116 
117   /* create sensitivity vector wrapper */
118   w = N_VNew_SensWrapper(count, y);
119 
120   /* create nonlinear solver using sensitivity vector wrapper */
121   NLS = SUNNonlinSol_Newton(w);
122 
123   /* free sensitivity vector wrapper */
124   N_VDestroy(w);
125 
126   /* return NLS object */
127   return(NLS);
128 }
129 
130 
131 /*==============================================================================
132   GetType, Initialize, Setup, Solve, and Free operations
133   ============================================================================*/
134 
SUNNonlinSolGetType_Newton(SUNNonlinearSolver NLS)135 SUNNonlinearSolver_Type SUNNonlinSolGetType_Newton(SUNNonlinearSolver NLS)
136 {
137   return(SUNNONLINEARSOLVER_ROOTFIND);
138 }
139 
140 
SUNNonlinSolInitialize_Newton(SUNNonlinearSolver NLS)141 int SUNNonlinSolInitialize_Newton(SUNNonlinearSolver NLS)
142 {
143   /* check that the nonlinear solver is non-null */
144   if (NLS == NULL) return(SUN_NLS_MEM_NULL);
145 
146   /* check that all required function pointers have been set */
147   if ( (NEWTON_CONTENT(NLS)->Sys    == NULL) ||
148        (NEWTON_CONTENT(NLS)->LSolve == NULL) ||
149        (NEWTON_CONTENT(NLS)->CTest  == NULL) ) {
150     return(SUN_NLS_MEM_NULL);
151   }
152 
153   /* reset the total number of iterations and convergence failures */
154   NEWTON_CONTENT(NLS)->niters     = 0;
155   NEWTON_CONTENT(NLS)->nconvfails = 0;
156 
157   /* reset the Jacobian status */
158   NEWTON_CONTENT(NLS)->jcur = SUNFALSE;
159 
160   return(SUN_NLS_SUCCESS);
161 }
162 
163 
164 /*------------------------------------------------------------------------------
165   SUNNonlinSolSolve_Newton: Performs the nonlinear solve F(y) = 0
166 
167   Successful solve return code:
168     SUN_NLS_SUCCESS = 0
169 
170   Recoverable failure return codes (positive):
171     SUN_NLS_CONV_RECVR
172     *_RHSFUNC_RECVR (ODEs) or *_RES_RECVR (DAEs)
173     *_LSETUP_RECVR
174     *_LSOLVE_RECVR
175 
176   Unrecoverable failure return codes (negative):
177     *_MEM_NULL
178     *_RHSFUNC_FAIL (ODEs) or *_RES_FAIL (DAEs)
179     *_LSETUP_FAIL
180     *_LSOLVE_FAIL
181 
182   Note return values beginning with * are package specific values returned by
183   the Sys, LSetup, and LSolve functions provided to the nonlinear solver.
184   ----------------------------------------------------------------------------*/
SUNNonlinSolSolve_Newton(SUNNonlinearSolver NLS,N_Vector y0,N_Vector y,N_Vector w,realtype tol,booleantype callLSetup,void * mem)185 int SUNNonlinSolSolve_Newton(SUNNonlinearSolver NLS,
186                              N_Vector y0, N_Vector y,
187                              N_Vector w, realtype tol,
188                              booleantype callLSetup, void* mem)
189 {
190   /* local variables */
191   int retval;
192   booleantype jbad;
193   N_Vector delta;
194 
195   /* check that the inputs are non-null */
196   if ( (NLS == NULL) ||
197        (y0  == NULL) ||
198        (y   == NULL) ||
199        (w   == NULL) ||
200        (mem == NULL) )
201     return(SUN_NLS_MEM_NULL);
202 
203   /* set local shortcut variables */
204   delta = NEWTON_CONTENT(NLS)->delta;
205 
206   /* assume the Jacobian is good */
207   jbad = SUNFALSE;
208 
209   /* looping point for attempts at solution of the nonlinear system:
210        Evaluate the nonlinear residual function (store in delta)
211        Setup the linear solver if necessary
212        Preform Newton iteraion */
213   for(;;) {
214 
215     /* compute the nonlinear residual, store in delta */
216     retval = NEWTON_CONTENT(NLS)->Sys(y0, delta, mem);
217     if (retval != SUN_NLS_SUCCESS) break;
218 
219     /* if indicated, setup the linear system */
220     if (callLSetup) {
221       retval = NEWTON_CONTENT(NLS)->LSetup(y0, delta, jbad,
222                                            &(NEWTON_CONTENT(NLS)->jcur),
223                                            mem);
224       if (retval != SUN_NLS_SUCCESS) break;
225     }
226 
227     /* initialize counter curiter */
228     NEWTON_CONTENT(NLS)->curiter = 0;
229 
230     /* load prediction into y */
231     N_VScale(ONE, y0, y);
232 
233     /* looping point for Newton iteration. Break out on any error. */
234     for(;;) {
235 
236       /* increment nonlinear solver iteration counter */
237       NEWTON_CONTENT(NLS)->niters++;
238 
239       /* compute the negative of the residual for the linear system rhs */
240       N_VScale(-ONE, delta, delta);
241 
242       /* solve the linear system to get Newton update delta */
243       retval = NEWTON_CONTENT(NLS)->LSolve(y, delta, mem);
244       if (retval != SUN_NLS_SUCCESS) break;
245 
246       /* update the Newton iterate */
247       N_VLinearSum(ONE, y, ONE, delta, y);
248 
249       /* test for convergence */
250       retval = NEWTON_CONTENT(NLS)->CTest(NLS, y, delta, tol, w, mem);
251 
252       /* if successful update Jacobian status and return */
253       if (retval == SUN_NLS_SUCCESS) {
254         NEWTON_CONTENT(NLS)->jcur = SUNFALSE;
255         return(SUN_NLS_SUCCESS);
256       }
257 
258       /* check if the iteration should continue; otherwise exit Newton loop */
259       if (retval != SUN_NLS_CONTINUE) break;
260 
261       /* not yet converged. Increment curiter and test for max allowed. */
262       NEWTON_CONTENT(NLS)->curiter++;
263       if (NEWTON_CONTENT(NLS)->curiter >= NEWTON_CONTENT(NLS)->maxiters) {
264         retval = SUN_NLS_CONV_RECVR;
265         break;
266       }
267 
268       /* compute the nonlinear residual, store in delta */
269       retval = NEWTON_CONTENT(NLS)->Sys(y, delta, mem);
270       if (retval != SUN_NLS_SUCCESS) break;
271 
272     } /* end of Newton iteration loop */
273 
274     /* all errors go here */
275 
276     /* If there is a recoverable convergence failure and the Jacobian-related
277        data appears not to be current, increment the convergence failure count
278        and loop again with a call to lsetup in which jbad is TRUE. Otherwise
279        break out and return. */
280     if ((retval > 0) && !(NEWTON_CONTENT(NLS)->jcur) && (NEWTON_CONTENT(NLS)->LSetup)) {
281       NEWTON_CONTENT(NLS)->nconvfails++;
282       callLSetup = SUNTRUE;
283       jbad = SUNTRUE;
284       continue;
285     } else {
286       break;
287     }
288 
289   } /* end of setup loop */
290 
291   /* increment number of convergence failures */
292   NEWTON_CONTENT(NLS)->nconvfails++;
293 
294   /* all error returns exit here */
295   return(retval);
296 }
297 
298 
SUNNonlinSolFree_Newton(SUNNonlinearSolver NLS)299 int SUNNonlinSolFree_Newton(SUNNonlinearSolver NLS)
300 {
301   /* return if NLS is already free */
302   if (NLS == NULL)
303     return(SUN_NLS_SUCCESS);
304 
305   /* free items from contents, then the generic structure */
306   if (NLS->content) {
307 
308     if (NEWTON_CONTENT(NLS)->delta)
309       N_VDestroy(NEWTON_CONTENT(NLS)->delta);
310     NEWTON_CONTENT(NLS)->delta = NULL;
311 
312     free(NLS->content);
313     NLS->content = NULL;
314   }
315 
316   /* free the ops structure */
317   if (NLS->ops) {
318     free(NLS->ops);
319     NLS->ops = NULL;
320   }
321 
322   /* free the nonlinear solver */
323   free(NLS);
324 
325   return(SUN_NLS_SUCCESS);
326 }
327 
328 
329 /*==============================================================================
330   Set functions
331   ============================================================================*/
332 
SUNNonlinSolSetSysFn_Newton(SUNNonlinearSolver NLS,SUNNonlinSolSysFn SysFn)333 int SUNNonlinSolSetSysFn_Newton(SUNNonlinearSolver NLS, SUNNonlinSolSysFn SysFn)
334 {
335   /* check that the nonlinear solver is non-null */
336   if (NLS == NULL)
337     return(SUN_NLS_MEM_NULL);
338 
339   /* check that the nonlinear system function is non-null */
340   if (SysFn == NULL)
341     return(SUN_NLS_ILL_INPUT);
342 
343   NEWTON_CONTENT(NLS)->Sys = SysFn;
344   return(SUN_NLS_SUCCESS);
345 }
346 
347 
SUNNonlinSolSetLSetupFn_Newton(SUNNonlinearSolver NLS,SUNNonlinSolLSetupFn LSetupFn)348 int SUNNonlinSolSetLSetupFn_Newton(SUNNonlinearSolver NLS, SUNNonlinSolLSetupFn LSetupFn)
349 {
350   /* check that the nonlinear solver is non-null */
351   if (NLS == NULL)
352     return(SUN_NLS_MEM_NULL);
353 
354   NEWTON_CONTENT(NLS)->LSetup = LSetupFn;
355   return(SUN_NLS_SUCCESS);
356 }
357 
358 
SUNNonlinSolSetLSolveFn_Newton(SUNNonlinearSolver NLS,SUNNonlinSolLSolveFn LSolveFn)359 int SUNNonlinSolSetLSolveFn_Newton(SUNNonlinearSolver NLS, SUNNonlinSolLSolveFn LSolveFn)
360 {
361   /* check that the nonlinear solver is non-null */
362   if (NLS == NULL)
363     return(SUN_NLS_MEM_NULL);
364 
365   /* check that the linear solver solve function is non-null */
366   if (LSolveFn == NULL)
367     return(SUN_NLS_ILL_INPUT);
368 
369   NEWTON_CONTENT(NLS)->LSolve = LSolveFn;
370   return(SUN_NLS_SUCCESS);
371 }
372 
373 
SUNNonlinSolSetConvTestFn_Newton(SUNNonlinearSolver NLS,SUNNonlinSolConvTestFn CTestFn)374 int SUNNonlinSolSetConvTestFn_Newton(SUNNonlinearSolver NLS, SUNNonlinSolConvTestFn CTestFn)
375 {
376   /* check that the nonlinear solver is non-null */
377   if (NLS == NULL)
378     return(SUN_NLS_MEM_NULL);
379 
380   /* check that the convergence test function is non-null */
381   if (CTestFn == NULL)
382     return(SUN_NLS_ILL_INPUT);
383 
384   NEWTON_CONTENT(NLS)->CTest = CTestFn;
385   return(SUN_NLS_SUCCESS);
386 }
387 
388 
SUNNonlinSolSetMaxIters_Newton(SUNNonlinearSolver NLS,int maxiters)389 int SUNNonlinSolSetMaxIters_Newton(SUNNonlinearSolver NLS, int maxiters)
390 {
391   /* check that the nonlinear solver is non-null */
392   if (NLS == NULL)
393     return(SUN_NLS_MEM_NULL);
394 
395   /* check that maxiters is a vaild */
396   if (maxiters < 1)
397     return(SUN_NLS_ILL_INPUT);
398 
399   NEWTON_CONTENT(NLS)->maxiters = maxiters;
400   return(SUN_NLS_SUCCESS);
401 }
402 
403 
404 /*==============================================================================
405   Get functions
406   ============================================================================*/
407 
SUNNonlinSolGetNumIters_Newton(SUNNonlinearSolver NLS,long int * niters)408 int SUNNonlinSolGetNumIters_Newton(SUNNonlinearSolver NLS, long int *niters)
409 {
410   /* check that the nonlinear solver is non-null */
411   if (NLS == NULL)
412     return(SUN_NLS_MEM_NULL);
413 
414   /* return the total number of nonlinear iterations */
415   *niters = NEWTON_CONTENT(NLS)->niters;
416   return(SUN_NLS_SUCCESS);
417 }
418 
419 
SUNNonlinSolGetCurIter_Newton(SUNNonlinearSolver NLS,int * iter)420 int SUNNonlinSolGetCurIter_Newton(SUNNonlinearSolver NLS, int *iter)
421 {
422   /* check that the nonlinear solver is non-null */
423   if (NLS == NULL)
424     return(SUN_NLS_MEM_NULL);
425 
426   /* return the current nonlinear solver iteration count */
427   *iter = NEWTON_CONTENT(NLS)->curiter;
428   return(SUN_NLS_SUCCESS);
429 }
430 
431 
SUNNonlinSolGetNumConvFails_Newton(SUNNonlinearSolver NLS,long int * nconvfails)432 int SUNNonlinSolGetNumConvFails_Newton(SUNNonlinearSolver NLS, long int *nconvfails)
433 {
434   /* check that the nonlinear solver is non-null */
435   if (NLS == NULL)
436     return(SUN_NLS_MEM_NULL);
437 
438   /* return the total number of nonlinear convergence failures */
439   *nconvfails = NEWTON_CONTENT(NLS)->nconvfails;
440   return(SUN_NLS_SUCCESS);
441 }
442 
443 
SUNNonlinSolGetSysFn_Newton(SUNNonlinearSolver NLS,SUNNonlinSolSysFn * SysFn)444 int SUNNonlinSolGetSysFn_Newton(SUNNonlinearSolver NLS, SUNNonlinSolSysFn *SysFn)
445 {
446   /* check that the nonlinear solver is non-null */
447   if (NLS == NULL)
448     return(SUN_NLS_MEM_NULL);
449 
450   /* return the nonlinear system defining function */
451   *SysFn = NEWTON_CONTENT(NLS)->Sys;
452   return(SUN_NLS_SUCCESS);
453 }
454