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