xref: /petsc/src/snes/impls/ngmres/snesngmres.c (revision 6a4a1270853b5b1995c94de98f44d28bec57470a)
1 #include <../src/snes/impls/ngmres/snesngmres.h> /*I "petscsnes.h" I*/
2 #include <petscblaslapack.h>
3 #include <petscdm.h>
4 
5 const char *const SNESNGMRESRestartTypes[] = {"NONE", "PERIODIC", "DIFFERENCE", "SNESNGMRESRestartType", "SNES_NGMRES_RESTART_", NULL};
6 const char *const SNESNGMRESSelectTypes[]  = {"NONE", "DIFFERENCE", "LINESEARCH", "SNESNGMRESSelectType", "SNES_NGMRES_SELECT_", NULL};
7 
8 PetscErrorCode SNESReset_NGMRES(SNES snes) {
9   SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data;
10 
11   PetscFunctionBegin;
12   PetscCall(VecDestroyVecs(ngmres->msize, &ngmres->Fdot));
13   PetscCall(VecDestroyVecs(ngmres->msize, &ngmres->Xdot));
14   PetscCall(SNESLineSearchDestroy(&ngmres->additive_linesearch));
15   PetscFunctionReturn(0);
16 }
17 
18 PetscErrorCode SNESDestroy_NGMRES(SNES snes) {
19   SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data;
20 
21   PetscFunctionBegin;
22   PetscCall(SNESReset_NGMRES(snes));
23   PetscCall(PetscFree4(ngmres->h, ngmres->beta, ngmres->xi, ngmres->q));
24   PetscCall(PetscFree3(ngmres->xnorms, ngmres->fnorms, ngmres->s));
25 #if defined(PETSC_USE_COMPLEX)
26   PetscCall(PetscFree(ngmres->rwork));
27 #endif
28   PetscCall(PetscFree(ngmres->work));
29   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNGMRESSetSelectType_C", NULL));
30   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNGMRESSetRestartType_C", NULL));
31   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNGMRESSetRestartFmRise_C", NULL));
32   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNGMRESGetRestartFmRise_C", NULL));
33   PetscCall(PetscFree(snes->data));
34   PetscFunctionReturn(0);
35 }
36 
37 PetscErrorCode SNESSetUp_NGMRES(SNES snes) {
38   SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data;
39   const char  *optionsprefix;
40   PetscInt     msize, hsize;
41   DM           dm;
42 
43   PetscFunctionBegin;
44   if (snes->npc && snes->npcside == PC_LEFT && snes->functype == SNES_FUNCTION_UNPRECONDITIONED) {
45     SETERRQ(PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_WRONGSTATE, "SNESNGMRES does not support left preconditioning with unpreconditioned function");
46   }
47   if (snes->npcside == PC_LEFT && snes->functype == SNES_FUNCTION_DEFAULT) snes->functype = SNES_FUNCTION_PRECONDITIONED;
48   PetscCall(SNESSetWorkVecs(snes, 5));
49 
50   if (!snes->vec_sol) {
51     PetscCall(SNESGetDM(snes, &dm));
52     PetscCall(DMCreateGlobalVector(dm, &snes->vec_sol));
53   }
54 
55   if (!ngmres->Xdot) PetscCall(VecDuplicateVecs(snes->vec_sol, ngmres->msize, &ngmres->Xdot));
56   if (!ngmres->Fdot) PetscCall(VecDuplicateVecs(snes->vec_sol, ngmres->msize, &ngmres->Fdot));
57   if (!ngmres->setup_called) {
58     msize = ngmres->msize; /* restart size */
59     hsize = msize * msize;
60 
61     /* explicit least squares minimization solve */
62     PetscCall(PetscCalloc4(hsize, &ngmres->h, msize, &ngmres->beta, msize, &ngmres->xi, hsize, &ngmres->q));
63     PetscCall(PetscMalloc3(msize, &ngmres->xnorms, msize, &ngmres->fnorms, msize, &ngmres->s));
64     ngmres->nrhs  = 1;
65     ngmres->lda   = msize;
66     ngmres->ldb   = msize;
67     ngmres->lwork = 12 * msize;
68 #if defined(PETSC_USE_COMPLEX)
69     PetscCall(PetscMalloc1(ngmres->lwork, &ngmres->rwork));
70 #endif
71     PetscCall(PetscMalloc1(ngmres->lwork, &ngmres->work));
72   }
73 
74   /* linesearch setup */
75   PetscCall(SNESGetOptionsPrefix(snes, &optionsprefix));
76 
77   if (ngmres->select_type == SNES_NGMRES_SELECT_LINESEARCH) {
78     PetscCall(SNESLineSearchCreate(PetscObjectComm((PetscObject)snes), &ngmres->additive_linesearch));
79     PetscCall(SNESLineSearchSetSNES(ngmres->additive_linesearch, snes));
80     if (!((PetscObject)ngmres->additive_linesearch)->type_name) PetscCall(SNESLineSearchSetType(ngmres->additive_linesearch, SNESLINESEARCHL2));
81     PetscCall(SNESLineSearchAppendOptionsPrefix(ngmres->additive_linesearch, "additive_"));
82     PetscCall(SNESLineSearchAppendOptionsPrefix(ngmres->additive_linesearch, optionsprefix));
83     PetscCall(SNESLineSearchSetFromOptions(ngmres->additive_linesearch));
84   }
85 
86   ngmres->setup_called = PETSC_TRUE;
87   PetscFunctionReturn(0);
88 }
89 
90 PetscErrorCode SNESSetFromOptions_NGMRES(SNES snes, PetscOptionItems *PetscOptionsObject) {
91   SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data;
92   PetscBool    debug  = PETSC_FALSE;
93 
94   PetscFunctionBegin;
95   PetscOptionsHeadBegin(PetscOptionsObject, "SNES NGMRES options");
96   PetscCall(PetscOptionsEnum("-snes_ngmres_select_type", "Select type", "SNESNGMRESSetSelectType", SNESNGMRESSelectTypes, (PetscEnum)ngmres->select_type, (PetscEnum *)&ngmres->select_type, NULL));
97   PetscCall(PetscOptionsEnum("-snes_ngmres_restart_type", "Restart type", "SNESNGMRESSetRestartType", SNESNGMRESRestartTypes, (PetscEnum)ngmres->restart_type, (PetscEnum *)&ngmres->restart_type, NULL));
98   PetscCall(PetscOptionsBool("-snes_ngmres_candidate", "Use candidate storage", "SNES", ngmres->candidate, &ngmres->candidate, NULL));
99   PetscCall(PetscOptionsBool("-snes_ngmres_approxfunc", "Linearly approximate the function", "SNES", ngmres->approxfunc, &ngmres->approxfunc, NULL));
100   PetscCall(PetscOptionsInt("-snes_ngmres_m", "Number of directions", "SNES", ngmres->msize, &ngmres->msize, NULL));
101   PetscCall(PetscOptionsInt("-snes_ngmres_restart", "Iterations before forced restart", "SNES", ngmres->restart_periodic, &ngmres->restart_periodic, NULL));
102   PetscCall(PetscOptionsInt("-snes_ngmres_restart_it", "Tolerance iterations before restart", "SNES", ngmres->restart_it, &ngmres->restart_it, NULL));
103   PetscCall(PetscOptionsBool("-snes_ngmres_monitor", "Monitor actions of NGMRES", "SNES", ngmres->monitor ? PETSC_TRUE : PETSC_FALSE, &debug, NULL));
104   if (debug) ngmres->monitor = PETSC_VIEWER_STDOUT_(PetscObjectComm((PetscObject)snes));
105   PetscCall(PetscOptionsReal("-snes_ngmres_gammaA", "Residual selection constant", "SNES", ngmres->gammaA, &ngmres->gammaA, NULL));
106   PetscCall(PetscOptionsReal("-snes_ngmres_gammaC", "Residual restart constant", "SNES", ngmres->gammaC, &ngmres->gammaC, NULL));
107   PetscCall(PetscOptionsReal("-snes_ngmres_epsilonB", "Difference selection constant", "SNES", ngmres->epsilonB, &ngmres->epsilonB, NULL));
108   PetscCall(PetscOptionsReal("-snes_ngmres_deltaB", "Difference residual selection constant", "SNES", ngmres->deltaB, &ngmres->deltaB, NULL));
109   PetscCall(PetscOptionsBool("-snes_ngmres_single_reduction", "Aggregate reductions", "SNES", ngmres->singlereduction, &ngmres->singlereduction, NULL));
110   PetscCall(PetscOptionsBool("-snes_ngmres_restart_fm_rise", "Restart on F_M residual rise", "SNESNGMRESSetRestartFmRise", ngmres->restart_fm_rise, &ngmres->restart_fm_rise, NULL));
111   PetscOptionsHeadEnd();
112   if ((ngmres->gammaA > ngmres->gammaC) && (ngmres->gammaC > 2.)) ngmres->gammaC = ngmres->gammaA;
113   PetscFunctionReturn(0);
114 }
115 
116 PetscErrorCode SNESView_NGMRES(SNES snes, PetscViewer viewer) {
117   SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data;
118   PetscBool    iascii;
119 
120   PetscFunctionBegin;
121   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
122   if (iascii) {
123     PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of stored past updates: %" PetscInt_FMT "\n", ngmres->msize));
124     PetscCall(PetscViewerASCIIPrintf(viewer, "  Residual selection: gammaA=%1.0e, gammaC=%1.0e\n", (double)ngmres->gammaA, (double)ngmres->gammaC));
125     PetscCall(PetscViewerASCIIPrintf(viewer, "  Difference restart: epsilonB=%1.0e, deltaB=%1.0e\n", (double)ngmres->epsilonB, (double)ngmres->deltaB));
126     PetscCall(PetscViewerASCIIPrintf(viewer, "  Restart on F_M residual increase: %s\n", PetscBools[ngmres->restart_fm_rise]));
127   }
128   PetscFunctionReturn(0);
129 }
130 
131 PetscErrorCode SNESSolve_NGMRES(SNES snes) {
132   SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data;
133   /* present solution, residual, and preconditioned residual */
134   Vec          X, F, B, D, Y;
135 
136   /* candidate linear combination answers */
137   Vec XA, FA, XM, FM;
138 
139   /* coefficients and RHS to the minimization problem */
140   PetscReal fnorm, fMnorm, fAnorm;
141   PetscReal xnorm, xMnorm, xAnorm;
142   PetscReal ynorm, yMnorm, yAnorm;
143   PetscInt  k, k_restart, l, ivec, restart_count = 0;
144 
145   /* solution selection data */
146   PetscBool selectRestart;
147   /*
148       These two variables are initialized to prevent compilers/analyzers from producing false warnings about these variables being passed
149       to SNESNGMRESSelect_Private() without being set when SNES_NGMRES_RESTART_DIFFERENCE, the values are not used in the subroutines in that case
150       so the code is correct as written.
151   */
152   PetscReal dnorm = 0.0, dminnorm = 0.0;
153   PetscReal fminnorm;
154 
155   SNESConvergedReason  reason;
156   SNESLineSearchReason lssucceed;
157 
158   PetscFunctionBegin;
159   PetscCheck(!snes->xl && !snes->xu && !snes->ops->computevariablebounds, PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_WRONGSTATE, "SNES solver %s does not support bounds", ((PetscObject)snes)->type_name);
160 
161   PetscCall(PetscCitationsRegister(SNESCitation, &SNEScite));
162   /* variable initialization */
163   snes->reason = SNES_CONVERGED_ITERATING;
164   X            = snes->vec_sol;
165   F            = snes->vec_func;
166   B            = snes->vec_rhs;
167   XA           = snes->vec_sol_update;
168   FA           = snes->work[0];
169   D            = snes->work[1];
170 
171   /* work for the line search */
172   Y  = snes->work[2];
173   XM = snes->work[3];
174   FM = snes->work[4];
175 
176   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
177   snes->iter = 0;
178   snes->norm = 0.;
179   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
180 
181   /* initialization */
182 
183   if (snes->npc && snes->npcside == PC_LEFT) {
184     PetscCall(SNESApplyNPC(snes, X, NULL, F));
185     PetscCall(SNESGetConvergedReason(snes->npc, &reason));
186     if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
187       snes->reason = SNES_DIVERGED_INNER;
188       PetscFunctionReturn(0);
189     }
190     PetscCall(VecNorm(F, NORM_2, &fnorm));
191   } else {
192     if (!snes->vec_func_init_set) {
193       PetscCall(SNESComputeFunction(snes, X, F));
194     } else snes->vec_func_init_set = PETSC_FALSE;
195 
196     PetscCall(VecNorm(F, NORM_2, &fnorm));
197     SNESCheckFunctionNorm(snes, fnorm);
198   }
199   fminnorm = fnorm;
200 
201   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
202   snes->norm = fnorm;
203   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
204   PetscCall(SNESLogConvergenceHistory(snes, fnorm, 0));
205   PetscCall(SNESMonitor(snes, 0, fnorm));
206   PetscUseTypeMethod(snes, converged, 0, 0.0, 0.0, fnorm, &snes->reason, snes->cnvP);
207   if (snes->reason) PetscFunctionReturn(0);
208   SNESNGMRESUpdateSubspace_Private(snes, 0, 0, F, fnorm, X);
209 
210   k_restart = 1;
211   l         = 1;
212   ivec      = 0;
213   for (k = 1; k < snes->max_its + 1; k++) {
214     /* Computation of x^M */
215     if (snes->npc && snes->npcside == PC_RIGHT) {
216       PetscCall(VecCopy(X, XM));
217       PetscCall(SNESSetInitialFunction(snes->npc, F));
218 
219       PetscCall(PetscLogEventBegin(SNES_NPCSolve, snes->npc, XM, B, 0));
220       PetscCall(SNESSolve(snes->npc, B, XM));
221       PetscCall(PetscLogEventEnd(SNES_NPCSolve, snes->npc, XM, B, 0));
222 
223       PetscCall(SNESGetConvergedReason(snes->npc, &reason));
224       if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
225         snes->reason = SNES_DIVERGED_INNER;
226         PetscFunctionReturn(0);
227       }
228       PetscCall(SNESGetNPCFunction(snes, FM, &fMnorm));
229     } else {
230       /* no preconditioner -- just take gradient descent with line search */
231       PetscCall(VecCopy(F, Y));
232       PetscCall(VecCopy(F, FM));
233       PetscCall(VecCopy(X, XM));
234 
235       fMnorm = fnorm;
236 
237       PetscCall(SNESLineSearchApply(snes->linesearch, XM, FM, &fMnorm, Y));
238       PetscCall(SNESLineSearchGetReason(snes->linesearch, &lssucceed));
239       if (lssucceed) {
240         if (++snes->numFailures >= snes->maxFailures) {
241           snes->reason = SNES_DIVERGED_LINE_SEARCH;
242           PetscFunctionReturn(0);
243         }
244       }
245     }
246 
247     PetscCall(SNESNGMRESFormCombinedSolution_Private(snes, ivec, l, XM, FM, fMnorm, X, XA, FA));
248     /* r = F(x) */
249     if (fminnorm > fMnorm) fminnorm = fMnorm; /* the minimum norm is now of F^M */
250 
251     /* differences for selection and restart */
252     if (ngmres->restart_type == SNES_NGMRES_RESTART_DIFFERENCE || ngmres->select_type == SNES_NGMRES_SELECT_DIFFERENCE) {
253       PetscCall(SNESNGMRESNorms_Private(snes, l, X, F, XM, FM, XA, FA, D, &dnorm, &dminnorm, &xMnorm, NULL, &yMnorm, &xAnorm, &fAnorm, &yAnorm));
254     } else {
255       PetscCall(SNESNGMRESNorms_Private(snes, l, X, F, XM, FM, XA, FA, D, NULL, NULL, &xMnorm, NULL, &yMnorm, &xAnorm, &fAnorm, &yAnorm));
256     }
257     SNESCheckFunctionNorm(snes, fnorm);
258 
259     /* combination (additive) or selection (multiplicative) of the N-GMRES solution */
260     PetscCall(SNESNGMRESSelect_Private(snes, k_restart, XM, FM, xMnorm, fMnorm, yMnorm, XA, FA, xAnorm, fAnorm, yAnorm, dnorm, fminnorm, dminnorm, X, F, Y, &xnorm, &fnorm, &ynorm));
261     selectRestart = PETSC_FALSE;
262 
263     if (ngmres->restart_type == SNES_NGMRES_RESTART_DIFFERENCE) {
264       PetscCall(SNESNGMRESSelectRestart_Private(snes, l, fMnorm, fAnorm, dnorm, fminnorm, dminnorm, &selectRestart));
265 
266       /* if the restart conditions persist for more than restart_it iterations, restart. */
267       if (selectRestart) restart_count++;
268       else restart_count = 0;
269     } else if (ngmres->restart_type == SNES_NGMRES_RESTART_PERIODIC) {
270       if (k_restart > ngmres->restart_periodic) {
271         if (ngmres->monitor) PetscCall(PetscViewerASCIIPrintf(ngmres->monitor, "periodic restart after %" PetscInt_FMT " iterations\n", k_restart));
272         restart_count = ngmres->restart_it;
273       }
274     }
275 
276     ivec = k_restart % ngmres->msize; /* replace the last used part of the subspace */
277 
278     /* restart after restart conditions have persisted for a fixed number of iterations */
279     if (restart_count >= ngmres->restart_it) {
280       if (ngmres->monitor) PetscCall(PetscViewerASCIIPrintf(ngmres->monitor, "Restarted at iteration %" PetscInt_FMT "\n", k_restart));
281       restart_count = 0;
282       k_restart     = 1;
283       l             = 1;
284       ivec          = 0;
285       /* q_{00} = nu */
286       PetscCall(SNESNGMRESUpdateSubspace_Private(snes, 0, 0, FM, fMnorm, XM));
287     } else {
288       /* select the current size of the subspace */
289       if (l < ngmres->msize) l++;
290       k_restart++;
291       /* place the current entry in the list of previous entries */
292       if (ngmres->candidate) {
293         if (fminnorm > fMnorm) fminnorm = fMnorm;
294         PetscCall(SNESNGMRESUpdateSubspace_Private(snes, ivec, l, FM, fMnorm, XM));
295       } else {
296         if (fminnorm > fnorm) fminnorm = fnorm;
297         PetscCall(SNESNGMRESUpdateSubspace_Private(snes, ivec, l, F, fnorm, X));
298       }
299     }
300 
301     PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
302     snes->iter = k;
303     snes->norm = fnorm;
304     PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
305     PetscCall(SNESLogConvergenceHistory(snes, snes->norm, snes->iter));
306     PetscCall(SNESMonitor(snes, snes->iter, snes->norm));
307     PetscUseTypeMethod(snes, converged, snes->iter, 0, 0, fnorm, &snes->reason, snes->cnvP);
308     if (snes->reason) PetscFunctionReturn(0);
309   }
310   snes->reason = SNES_DIVERGED_MAX_IT;
311   PetscFunctionReturn(0);
312 }
313 
314 /*@
315  SNESNGMRESSetRestartFmRise - Increase the restart count if the step x_M increases the residual F_M
316 
317    Input Parameters:
318 +  snes - the `SNES` context.
319 -  flg  - boolean value deciding whether to use the option or not, default is `PETSC_FALSE`
320 
321    Options Database Key:
322 .   -snes_ngmres_restart_fm_rise - Increase the restart count if the step x_M increases the residual F_M
323 
324    Level: intermediate
325 
326    Notes:
327    If the proposed step x_M increases the residual F_M, it might be trying to get out of a stagnation area.
328    To help the solver do that, reset the Krylov subspace whenever F_M increases.
329 
330    This option must be used with the `SNESNGMRES` `SNESNGMRESRestartType` of `SNES_NGMRES_RESTART_DIFFERENCE`
331 
332 .seealso: `SNES_NGMRES_RESTART_DIFFERENCE`, `SNESNGMRES`, `SNESNGMRESRestartType`, `SNESNGMRESSetRestartType()`
333   @*/
334 PetscErrorCode SNESNGMRESSetRestartFmRise(SNES snes, PetscBool flg) {
335   PetscErrorCode (*f)(SNES, PetscBool);
336 
337   PetscFunctionBegin;
338   PetscCall(PetscObjectQueryFunction((PetscObject)snes, "SNESNGMRESSetRestartFmRise_C", &f));
339   if (f) PetscCall((f)(snes, flg));
340   PetscFunctionReturn(0);
341 }
342 
343 PetscErrorCode SNESNGMRESSetRestartFmRise_NGMRES(SNES snes, PetscBool flg) {
344   SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data;
345 
346   PetscFunctionBegin;
347   ngmres->restart_fm_rise = flg;
348   PetscFunctionReturn(0);
349 }
350 
351 PetscErrorCode SNESNGMRESGetRestartFmRise(SNES snes, PetscBool *flg) {
352   PetscErrorCode (*f)(SNES, PetscBool *);
353 
354   PetscFunctionBegin;
355   PetscCall(PetscObjectQueryFunction((PetscObject)snes, "SNESNGMRESGetRestartFmRise_C", &f));
356   if (f) PetscCall((f)(snes, flg));
357   PetscFunctionReturn(0);
358 }
359 
360 PetscErrorCode SNESNGMRESGetRestartFmRise_NGMRES(SNES snes, PetscBool *flg) {
361   SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data;
362 
363   PetscFunctionBegin;
364   *flg = ngmres->restart_fm_rise;
365   PetscFunctionReturn(0);
366 }
367 
368 /*@
369     SNESNGMRESSetRestartType - Sets the restart type for `SNESNGMRES`.
370 
371     Logically Collective on snes
372 
373     Input Parameters:
374 +   snes - the iterative context
375 -   rtype - restart type
376 
377     Options Databas Keys:
378 +   -snes_ngmres_restart_type<difference,periodic,none> - set the restart type
379 -   -snes_ngmres_restart[30] - sets the number of iterations before restart for periodic
380 
381     `SNESNGMRESRestartType`s:
382 +   `SNES_NGMRES_RESTART_NONE` - never restart
383 .   `SNES_NGMRES_RESTART_DIFFERENCE` - restart based upon difference criteria
384 -   `SNES_NGMRES_RESTART_PERIODIC` - restart after a fixed number of iterations
385 
386     Level: intermediate
387 
388 .seealso: `SNES_NGMRES_RESTART_DIFFERENCE`, `SNESNGMRES`, `SNESNGMRESRestartType`, `SNESNGMRESSetRestartFmRise()`
389 @*/
390 PetscErrorCode SNESNGMRESSetRestartType(SNES snes, SNESNGMRESRestartType rtype) {
391   PetscFunctionBegin;
392   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
393   PetscTryMethod(snes, "SNESNGMRESSetRestartType_C", (SNES, SNESNGMRESRestartType), (snes, rtype));
394   PetscFunctionReturn(0);
395 }
396 
397 /*@
398     SNESNGMRESSetSelectType - Sets the selection type for `SNESNGMRES`.  This determines how the candidate solution and
399     combined solution are used to create the next iterate.
400 
401     Logically Collective on snes
402 
403     Input Parameters:
404 +   snes - the iterative context
405 -   stype - selection type
406 
407     Options Database Key:
408 .   -snes_ngmres_select_type<difference,none,linesearch> - select type
409 
410     Level: intermediate
411 
412     `SNESNGMRESSelectType`s:
413 +   `SNES_NGMRES_SELECT_NONE` - choose the combined solution all the time
414 .   `SNES_NGMRES_SELECT_DIFFERENCE` - choose based upon the selection criteria
415 -   `SNES_NGMRES_SELECT_LINESEARCH` - choose based upon line search combination
416 
417     Note:
418     The default line search used is the `SNESLINESEARCHL2` line search and it requires two additional function evaluations.
419 
420 .seealso: `SNESNGMRESSelectType()`, `SNES_NGMRES_SELECT_NONE`, `SNES_NGMRES_SELECT_DIFFERENCE`, `SNES_NGMRES_SELECT_LINESEARCH`
421 @*/
422 PetscErrorCode SNESNGMRESSetSelectType(SNES snes, SNESNGMRESSelectType stype) {
423   PetscFunctionBegin;
424   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
425   PetscTryMethod(snes, "SNESNGMRESSetSelectType_C", (SNES, SNESNGMRESSelectType), (snes, stype));
426   PetscFunctionReturn(0);
427 }
428 
429 PetscErrorCode SNESNGMRESSetSelectType_NGMRES(SNES snes, SNESNGMRESSelectType stype) {
430   SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data;
431 
432   PetscFunctionBegin;
433   ngmres->select_type = stype;
434   PetscFunctionReturn(0);
435 }
436 
437 PetscErrorCode SNESNGMRESSetRestartType_NGMRES(SNES snes, SNESNGMRESRestartType rtype) {
438   SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data;
439 
440   PetscFunctionBegin;
441   ngmres->restart_type = rtype;
442   PetscFunctionReturn(0);
443 }
444 
445 /*MC
446   SNESNGMRES - The Nonlinear Generalized Minimum Residual method.
447 
448    Level: beginner
449 
450    Options Database Keys:
451 +  -snes_ngmres_select_type<difference,none,linesearch> - choose the select between candidate and combined solution
452 .  -snes_ngmres_restart_type<difference,none,periodic> - choose the restart conditions
453 .  -snes_ngmres_candidate        - Use `SNESNGMRES` variant which combines candidate solutions instead of actual solutions
454 .  -snes_ngmres_m                - Number of stored previous solutions and residuals
455 .  -snes_ngmres_restart_it       - Number of iterations the restart conditions hold before restart
456 .  -snes_ngmres_gammaA           - Residual tolerance for solution select between the candidate and combination
457 .  -snes_ngmres_gammaC           - Residual tolerance for restart
458 .  -snes_ngmres_epsilonB         - Difference tolerance between subsequent solutions triggering restart
459 .  -snes_ngmres_deltaB           - Difference tolerance between residuals triggering restart
460 .  -snes_ngmres_restart_fm_rise  - Restart on residual rise from x_M step
461 .  -snes_ngmres_monitor          - Prints relevant information about the ngmres iteration
462 .  -snes_linesearch_type <basic,l2,cp> - Line search type used for the default smoother
463 -  -additive_snes_linesearch_type - linesearch type used to select between the candidate and combined solution with additive select type
464 
465    Notes:
466    The N-GMRES method combines m previous solutions into a minimum-residual solution by solving a small linearized
467    optimization problem at each iteration.
468 
469    Very similar to the `SNESANDERSON` algorithm.
470 
471    References:
472 +  * - C. W. Oosterlee and T. Washio, "Krylov Subspace Acceleration of Nonlinear Multigrid with Application to Recirculating Flows",
473    SIAM Journal on Scientific Computing, 21(5), 2000.
474 -  * - Peter R. Brune, Matthew G. Knepley, Barry F. Smith, and Xuemin Tu, "Composing Scalable Nonlinear Algebraic Solvers",
475    SIAM Review, 57(4), 2015
476 
477 .seealso: `SNESCreate()`, `SNES`, `SNESSetType()`, `SNESType`, `SNESANDERSON`, `SNESNGMRESSetSelectType()`, `SNESNGMRESSetRestartType()`,
478           `SNESNGMRESSetRestartFmRise()`
479 M*/
480 
481 PETSC_EXTERN PetscErrorCode SNESCreate_NGMRES(SNES snes) {
482   SNES_NGMRES   *ngmres;
483   SNESLineSearch linesearch;
484 
485   PetscFunctionBegin;
486   snes->ops->destroy        = SNESDestroy_NGMRES;
487   snes->ops->setup          = SNESSetUp_NGMRES;
488   snes->ops->setfromoptions = SNESSetFromOptions_NGMRES;
489   snes->ops->view           = SNESView_NGMRES;
490   snes->ops->solve          = SNESSolve_NGMRES;
491   snes->ops->reset          = SNESReset_NGMRES;
492 
493   snes->usesnpc = PETSC_TRUE;
494   snes->usesksp = PETSC_FALSE;
495   snes->npcside = PC_RIGHT;
496 
497   snes->alwayscomputesfinalresidual = PETSC_TRUE;
498 
499   PetscCall(PetscNewLog(snes, &ngmres));
500   snes->data    = (void *)ngmres;
501   ngmres->msize = 30;
502 
503   if (!snes->tolerancesset) {
504     snes->max_funcs = 30000;
505     snes->max_its   = 10000;
506   }
507 
508   ngmres->candidate = PETSC_FALSE;
509 
510   PetscCall(SNESGetLineSearch(snes, &linesearch));
511   if (!((PetscObject)linesearch)->type_name) PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHBASIC));
512 
513   ngmres->additive_linesearch = NULL;
514   ngmres->approxfunc          = PETSC_FALSE;
515   ngmres->restart_it          = 2;
516   ngmres->restart_periodic    = 30;
517   ngmres->gammaA              = 2.0;
518   ngmres->gammaC              = 2.0;
519   ngmres->deltaB              = 0.9;
520   ngmres->epsilonB            = 0.1;
521   ngmres->restart_fm_rise     = PETSC_FALSE;
522 
523   ngmres->restart_type = SNES_NGMRES_RESTART_DIFFERENCE;
524   ngmres->select_type  = SNES_NGMRES_SELECT_DIFFERENCE;
525 
526   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNGMRESSetSelectType_C", SNESNGMRESSetSelectType_NGMRES));
527   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNGMRESSetRestartType_C", SNESNGMRESSetRestartType_NGMRES));
528   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNGMRESSetRestartFmRise_C", SNESNGMRESSetRestartFmRise_NGMRES));
529   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNGMRESGetRestartFmRise_C", SNESNGMRESGetRestartFmRise_NGMRES));
530   PetscFunctionReturn(0);
531 }
532