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