Actual source code: fas.c
1: /* Defines the basic SNES object */
2: #include <../src/snes/impls/fas/fasimpls.h>
4: const char *const SNESFASTypes[] = {"MULTIPLICATIVE", "ADDITIVE", "FULL", "KASKADE", "SNESFASType", "SNES_FAS", NULL};
6: static PetscErrorCode SNESReset_FAS(SNES snes)
7: {
8: SNES_FAS *fas = (SNES_FAS *)snes->data;
10: PetscFunctionBegin;
11: PetscCall(SNESDestroy(&fas->smoothu));
12: PetscCall(SNESDestroy(&fas->smoothd));
13: PetscCall(MatDestroy(&fas->inject));
14: PetscCall(MatDestroy(&fas->interpolate));
15: PetscCall(MatDestroy(&fas->restrct));
16: PetscCall(VecDestroy(&fas->rscale));
17: PetscCall(VecDestroy(&fas->Xg));
18: PetscCall(VecDestroy(&fas->Fg));
19: if (fas->next) PetscCall(SNESReset(fas->next));
20: PetscFunctionReturn(PETSC_SUCCESS);
21: }
23: static PetscErrorCode SNESDestroy_FAS(SNES snes)
24: {
25: SNES_FAS *fas = (SNES_FAS *)snes->data;
27: PetscFunctionBegin;
28: /* recursively resets and then destroys */
29: PetscCall(SNESReset_FAS(snes));
30: PetscCall(SNESDestroy(&fas->next));
31: PetscCall(PetscFree(fas));
32: PetscFunctionReturn(PETSC_SUCCESS);
33: }
35: static PetscErrorCode SNESFASSetUpLineSearch_Private(SNES snes, SNES smooth)
36: {
37: SNESLineSearch linesearch;
38: SNESLineSearch slinesearch;
39: void *lsprectx, *lspostctx;
40: PetscErrorCode (*precheck)(SNESLineSearch, Vec, Vec, PetscBool *, void *);
41: PetscErrorCode (*postcheck)(SNESLineSearch, Vec, Vec, Vec, PetscBool *, PetscBool *, void *);
43: PetscFunctionBegin;
44: if (!snes->linesearch) PetscFunctionReturn(PETSC_SUCCESS);
45: PetscCall(SNESGetLineSearch(snes, &linesearch));
46: PetscCall(SNESGetLineSearch(smooth, &slinesearch));
47: PetscCall(SNESLineSearchGetPreCheck(linesearch, &precheck, &lsprectx));
48: PetscCall(SNESLineSearchGetPostCheck(linesearch, &postcheck, &lspostctx));
49: PetscCall(SNESLineSearchSetPreCheck(slinesearch, precheck, lsprectx));
50: PetscCall(SNESLineSearchSetPostCheck(slinesearch, postcheck, lspostctx));
51: PetscCall(PetscObjectCopyFortranFunctionPointers((PetscObject)linesearch, (PetscObject)slinesearch));
52: PetscFunctionReturn(PETSC_SUCCESS);
53: }
55: static PetscErrorCode SNESFASCycleSetUpSmoother_Private(SNES snes, SNES smooth)
56: {
57: SNES_FAS *fas = (SNES_FAS *)snes->data;
59: PetscFunctionBegin;
60: PetscCall(PetscObjectCopyFortranFunctionPointers((PetscObject)snes, (PetscObject)smooth));
61: PetscCall(SNESSetFromOptions(smooth));
62: PetscCall(SNESFASSetUpLineSearch_Private(snes, smooth));
64: PetscCall(PetscObjectReference((PetscObject)snes->vec_sol));
65: PetscCall(PetscObjectReference((PetscObject)snes->vec_sol_update));
66: PetscCall(PetscObjectReference((PetscObject)snes->vec_func));
67: smooth->vec_sol = snes->vec_sol;
68: smooth->vec_sol_update = snes->vec_sol_update;
69: smooth->vec_func = snes->vec_func;
71: if (fas->eventsmoothsetup) PetscCall(PetscLogEventBegin(fas->eventsmoothsetup, smooth, 0, 0, 0));
72: PetscCall(SNESSetUp(smooth));
73: if (fas->eventsmoothsetup) PetscCall(PetscLogEventEnd(fas->eventsmoothsetup, smooth, 0, 0, 0));
74: PetscFunctionReturn(PETSC_SUCCESS);
75: }
77: static PetscErrorCode SNESSetUp_FAS(SNES snes)
78: {
79: SNES_FAS *fas = (SNES_FAS *)snes->data;
80: PetscInt dm_levels;
81: SNES next;
82: PetscBool isFine, hasCreateRestriction, hasCreateInjection;
84: PetscFunctionBegin;
85: PetscCall(SNESFASCycleIsFine(snes, &isFine));
86: if (fas->usedmfornumberoflevels && isFine) {
87: PetscCall(DMGetRefineLevel(snes->dm, &dm_levels));
88: dm_levels++;
89: if (dm_levels > fas->levels) {
90: /* reset the number of levels */
91: PetscCall(SNESFASSetLevels(snes, dm_levels, NULL));
92: PetscCall(SNESSetFromOptions(snes));
93: }
94: }
95: PetscCall(SNESFASCycleGetCorrection(snes, &next));
96: if (!isFine) snes->gridsequence = 0; /* no grid sequencing inside the multigrid hierarchy! */
98: PetscCall(SNESSetWorkVecs(snes, 2)); /* work vectors used for intergrid transfers */
100: /* set up the smoothers if they haven't already been set up */
101: if (!fas->smoothd) PetscCall(SNESFASCycleCreateSmoother_Private(snes, &fas->smoothd));
103: if (snes->dm) {
104: /* set the smoother DMs properly */
105: if (fas->smoothu) PetscCall(SNESSetDM(fas->smoothu, snes->dm));
106: PetscCall(SNESSetDM(fas->smoothd, snes->dm));
107: /* construct EVERYTHING from the DM -- including the progressive set of smoothers */
108: if (next) {
109: /* for now -- assume the DM and the evaluation functions have been set externally */
110: if (!next->dm) {
111: PetscCall(DMCoarsen(snes->dm, PetscObjectComm((PetscObject)next), &next->dm));
112: PetscCall(SNESSetDM(next, next->dm));
113: }
114: /* set the interpolation and restriction from the DM */
115: if (!fas->interpolate) {
116: PetscCall(DMCreateInterpolation(next->dm, snes->dm, &fas->interpolate, &fas->rscale));
117: if (!fas->restrct) {
118: PetscCall(DMHasCreateRestriction(next->dm, &hasCreateRestriction));
119: /* DM can create restrictions, use that */
120: if (hasCreateRestriction) {
121: PetscCall(DMCreateRestriction(next->dm, snes->dm, &fas->restrct));
122: } else {
123: PetscCall(PetscObjectReference((PetscObject)fas->interpolate));
124: fas->restrct = fas->interpolate;
125: }
126: }
127: }
128: /* set the injection from the DM */
129: if (!fas->inject) {
130: PetscCall(DMHasCreateInjection(next->dm, &hasCreateInjection));
131: if (hasCreateInjection) PetscCall(DMCreateInjection(next->dm, snes->dm, &fas->inject));
132: }
133: }
134: }
136: /*pass the smoother, function, and jacobian up to the next level if it's not user set already */
137: if (fas->galerkin) {
138: if (next) PetscCall(SNESSetFunction(next, NULL, SNESFASGalerkinFunctionDefault, next));
139: if (fas->smoothd && fas->level != fas->levels - 1) PetscCall(SNESSetFunction(fas->smoothd, NULL, SNESFASGalerkinFunctionDefault, snes));
140: if (fas->smoothu && fas->level != fas->levels - 1) PetscCall(SNESSetFunction(fas->smoothu, NULL, SNESFASGalerkinFunctionDefault, snes));
141: }
143: /* sets the down (pre) smoother's default norm and sets it from options */
144: if (fas->smoothd) {
145: if (fas->level == 0 && fas->levels != 1) {
146: PetscCall(SNESSetNormSchedule(fas->smoothd, SNES_NORM_NONE));
147: } else {
148: PetscCall(SNESSetNormSchedule(fas->smoothd, SNES_NORM_FINAL_ONLY));
149: }
150: PetscCall(SNESFASCycleSetUpSmoother_Private(snes, fas->smoothd));
151: }
153: /* sets the up (post) smoother's default norm and sets it from options */
154: if (fas->smoothu) {
155: if (fas->level != fas->levels - 1) {
156: PetscCall(SNESSetNormSchedule(fas->smoothu, SNES_NORM_NONE));
157: } else {
158: PetscCall(SNESSetNormSchedule(fas->smoothu, SNES_NORM_FINAL_ONLY));
159: }
160: PetscCall(SNESFASCycleSetUpSmoother_Private(snes, fas->smoothu));
161: }
163: if (next) {
164: /* gotta set up the solution vector for this to work */
165: if (!next->vec_sol) PetscCall(SNESFASCreateCoarseVec(snes, &next->vec_sol));
166: if (!next->vec_rhs) PetscCall(SNESFASCreateCoarseVec(snes, &next->vec_rhs));
167: PetscCall(PetscObjectCopyFortranFunctionPointers((PetscObject)snes, (PetscObject)next));
168: PetscCall(SNESFASSetUpLineSearch_Private(snes, next));
169: PetscCall(SNESSetUp(next));
170: }
172: /* setup FAS work vectors */
173: if (fas->galerkin) {
174: PetscCall(VecDuplicate(snes->vec_sol, &fas->Xg));
175: PetscCall(VecDuplicate(snes->vec_sol, &fas->Fg));
176: }
177: PetscFunctionReturn(PETSC_SUCCESS);
178: }
180: static PetscErrorCode SNESSetFromOptions_FAS(SNES snes, PetscOptionItems *PetscOptionsObject)
181: {
182: SNES_FAS *fas = (SNES_FAS *)snes->data;
183: PetscInt levels = 1;
184: PetscBool flg = PETSC_FALSE, upflg = PETSC_FALSE, downflg = PETSC_FALSE, monflg = PETSC_FALSE, galerkinflg = PETSC_FALSE, continuationflg = PETSC_FALSE;
185: SNESFASType fastype;
186: const char *optionsprefix;
187: SNESLineSearch linesearch;
188: PetscInt m, n_up, n_down;
189: SNES next;
190: PetscBool isFine;
192: PetscFunctionBegin;
193: PetscCall(SNESFASCycleIsFine(snes, &isFine));
194: PetscOptionsHeadBegin(PetscOptionsObject, "SNESFAS Options-----------------------------------");
196: /* number of levels -- only process most options on the finest level */
197: if (isFine) {
198: PetscCall(PetscOptionsInt("-snes_fas_levels", "Number of Levels", "SNESFASSetLevels", levels, &levels, &flg));
199: if (!flg && snes->dm) {
200: PetscCall(DMGetRefineLevel(snes->dm, &levels));
201: levels++;
202: fas->usedmfornumberoflevels = PETSC_TRUE;
203: }
204: PetscCall(SNESFASSetLevels(snes, levels, NULL));
205: fastype = fas->fastype;
206: PetscCall(PetscOptionsEnum("-snes_fas_type", "FAS correction type", "SNESFASSetType", SNESFASTypes, (PetscEnum)fastype, (PetscEnum *)&fastype, &flg));
207: if (flg) PetscCall(SNESFASSetType(snes, fastype));
209: PetscCall(SNESGetOptionsPrefix(snes, &optionsprefix));
210: PetscCall(PetscOptionsInt("-snes_fas_cycles", "Number of cycles", "SNESFASSetCycles", fas->n_cycles, &m, &flg));
211: if (flg) PetscCall(SNESFASSetCycles(snes, m));
212: PetscCall(PetscOptionsBool("-snes_fas_continuation", "Corrected grid-sequence continuation", "SNESFASSetContinuation", fas->continuation, &continuationflg, &flg));
213: if (flg) PetscCall(SNESFASSetContinuation(snes, continuationflg));
215: PetscCall(PetscOptionsBool("-snes_fas_galerkin", "Form coarse problems with Galerkin", "SNESFASSetGalerkin", fas->galerkin, &galerkinflg, &flg));
216: if (flg) PetscCall(SNESFASSetGalerkin(snes, galerkinflg));
218: if (fas->fastype == SNES_FAS_FULL) {
219: PetscCall(PetscOptionsBool("-snes_fas_full_downsweep", "Smooth on the initial down sweep for full FAS cycles", "SNESFASFullSetDownSweep", fas->full_downsweep, &fas->full_downsweep, &flg));
220: if (flg) PetscCall(SNESFASFullSetDownSweep(snes, fas->full_downsweep));
221: PetscCall(PetscOptionsBool("-snes_fas_full_total", "Use total restriction and interpolaton on the indial down and up sweeps for the full FAS cycle", "SNESFASFullSetUseTotal", fas->full_total, &fas->full_total, &flg));
222: if (flg) PetscCall(SNESFASFullSetTotal(snes, fas->full_total));
223: }
225: PetscCall(PetscOptionsInt("-snes_fas_smoothup", "Number of post-smoothing steps", "SNESFASSetNumberSmoothUp", fas->max_up_it, &n_up, &upflg));
227: PetscCall(PetscOptionsInt("-snes_fas_smoothdown", "Number of pre-smoothing steps", "SNESFASSetNumberSmoothDown", fas->max_down_it, &n_down, &downflg));
229: {
230: PetscViewer viewer;
231: PetscViewerFormat format;
232: PetscCall(PetscOptionsGetViewer(PetscObjectComm((PetscObject)snes), ((PetscObject)snes)->options, ((PetscObject)snes)->prefix, "-snes_fas_monitor", &viewer, &format, &monflg));
233: if (monflg) {
234: PetscViewerAndFormat *vf;
235: PetscCall(PetscViewerAndFormatCreate(viewer, format, &vf));
236: PetscCall(PetscObjectDereference((PetscObject)viewer));
237: PetscCall(SNESFASSetMonitor(snes, vf, PETSC_TRUE));
238: }
239: }
240: flg = PETSC_FALSE;
241: monflg = PETSC_TRUE;
242: PetscCall(PetscOptionsBool("-snes_fas_log", "Log times for each FAS level", "SNESFASSetLog", monflg, &monflg, &flg));
243: if (flg) PetscCall(SNESFASSetLog(snes, monflg));
244: }
246: PetscOptionsHeadEnd();
248: /* setup from the determined types if there is no pointwise procedure or smoother defined */
249: if (upflg) PetscCall(SNESFASSetNumberSmoothUp(snes, n_up));
250: if (downflg) PetscCall(SNESFASSetNumberSmoothDown(snes, n_down));
252: /* set up the default line search for coarse grid corrections */
253: if (fas->fastype == SNES_FAS_ADDITIVE) {
254: if (!snes->linesearch) {
255: PetscCall(SNESGetLineSearch(snes, &linesearch));
256: PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHL2));
257: }
258: }
260: /* recursive option setting for the smoothers */
261: PetscCall(SNESFASCycleGetCorrection(snes, &next));
262: if (next) PetscCall(SNESSetFromOptions(next));
263: PetscFunctionReturn(PETSC_SUCCESS);
264: }
266: #include <petscdraw.h>
267: static PetscErrorCode SNESView_FAS(SNES snes, PetscViewer viewer)
268: {
269: SNES_FAS *fas = (SNES_FAS *)snes->data;
270: PetscBool isFine, iascii, isdraw;
271: PetscInt i;
272: SNES smoothu, smoothd, levelsnes;
274: PetscFunctionBegin;
275: PetscCall(SNESFASCycleIsFine(snes, &isFine));
276: if (isFine) {
277: PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
278: PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
279: if (iascii) {
280: PetscCall(PetscViewerASCIIPrintf(viewer, " type is %s, levels=%" PetscInt_FMT ", cycles=%" PetscInt_FMT "\n", SNESFASTypes[fas->fastype], fas->levels, fas->n_cycles));
281: if (fas->galerkin) {
282: PetscCall(PetscViewerASCIIPrintf(viewer, " Using Galerkin computed coarse grid function evaluation\n"));
283: } else {
284: PetscCall(PetscViewerASCIIPrintf(viewer, " Not using Galerkin computed coarse grid function evaluation\n"));
285: }
286: for (i = 0; i < fas->levels; i++) {
287: PetscCall(SNESFASGetCycleSNES(snes, i, &levelsnes));
288: PetscCall(SNESFASCycleGetSmootherUp(levelsnes, &smoothu));
289: PetscCall(SNESFASCycleGetSmootherDown(levelsnes, &smoothd));
290: if (!i) {
291: PetscCall(PetscViewerASCIIPrintf(viewer, " Coarse grid solver -- level %" PetscInt_FMT " -------------------------------\n", i));
292: } else {
293: PetscCall(PetscViewerASCIIPrintf(viewer, " Down solver (pre-smoother) on level %" PetscInt_FMT " -------------------------------\n", i));
294: }
295: PetscCall(PetscViewerASCIIPushTab(viewer));
296: if (smoothd) {
297: PetscCall(SNESView(smoothd, viewer));
298: } else {
299: PetscCall(PetscViewerASCIIPrintf(viewer, "Not yet available\n"));
300: }
301: PetscCall(PetscViewerASCIIPopTab(viewer));
302: if (i && (smoothd == smoothu)) {
303: PetscCall(PetscViewerASCIIPrintf(viewer, " Up solver (post-smoother) same as down solver (pre-smoother)\n"));
304: } else if (i) {
305: PetscCall(PetscViewerASCIIPrintf(viewer, " Up solver (post-smoother) on level %" PetscInt_FMT " -------------------------------\n", i));
306: PetscCall(PetscViewerASCIIPushTab(viewer));
307: if (smoothu) {
308: PetscCall(SNESView(smoothu, viewer));
309: } else {
310: PetscCall(PetscViewerASCIIPrintf(viewer, "Not yet available\n"));
311: }
312: PetscCall(PetscViewerASCIIPopTab(viewer));
313: }
314: }
315: } else if (isdraw) {
316: PetscDraw draw;
317: PetscReal x, w, y, bottom, th, wth;
318: SNES_FAS *curfas = fas;
319: PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
320: PetscCall(PetscDrawGetCurrentPoint(draw, &x, &y));
321: PetscCall(PetscDrawStringGetSize(draw, &wth, &th));
322: bottom = y - th;
323: while (curfas) {
324: if (!curfas->smoothu) {
325: PetscCall(PetscDrawPushCurrentPoint(draw, x, bottom));
326: if (curfas->smoothd) PetscCall(SNESView(curfas->smoothd, viewer));
327: PetscCall(PetscDrawPopCurrentPoint(draw));
328: } else {
329: w = 0.5 * PetscMin(1.0 - x, x);
330: PetscCall(PetscDrawPushCurrentPoint(draw, x - w, bottom));
331: if (curfas->smoothd) PetscCall(SNESView(curfas->smoothd, viewer));
332: PetscCall(PetscDrawPopCurrentPoint(draw));
333: PetscCall(PetscDrawPushCurrentPoint(draw, x + w, bottom));
334: if (curfas->smoothu) PetscCall(SNESView(curfas->smoothu, viewer));
335: PetscCall(PetscDrawPopCurrentPoint(draw));
336: }
337: /* this is totally bogus but we have no way of knowing how low the previous one was draw to */
338: bottom -= 5 * th;
339: if (curfas->next) curfas = (SNES_FAS *)curfas->next->data;
340: else curfas = NULL;
341: }
342: }
343: }
344: PetscFunctionReturn(PETSC_SUCCESS);
345: }
347: /*
348: Defines the action of the downsmoother
349: */
350: static PetscErrorCode SNESFASDownSmooth_Private(SNES snes, Vec B, Vec X, Vec F, PetscReal *fnorm)
351: {
352: SNESConvergedReason reason;
353: Vec FPC;
354: SNES smoothd;
355: PetscBool flg;
356: SNES_FAS *fas = (SNES_FAS *)snes->data;
358: PetscFunctionBegin;
359: PetscCall(SNESFASCycleGetSmootherDown(snes, &smoothd));
360: PetscCall(SNESSetInitialFunction(smoothd, F));
361: if (fas->eventsmoothsolve) PetscCall(PetscLogEventBegin(fas->eventsmoothsolve, smoothd, B, X, 0));
362: PetscCall(SNESSolve(smoothd, B, X));
363: if (fas->eventsmoothsolve) PetscCall(PetscLogEventEnd(fas->eventsmoothsolve, smoothd, B, X, 0));
364: /* check convergence reason for the smoother */
365: PetscCall(SNESGetConvergedReason(smoothd, &reason));
366: if (reason < 0 && !(reason == SNES_DIVERGED_MAX_IT || reason == SNES_DIVERGED_LOCAL_MIN || reason == SNES_DIVERGED_LINE_SEARCH)) {
367: snes->reason = SNES_DIVERGED_INNER;
368: PetscFunctionReturn(PETSC_SUCCESS);
369: }
371: PetscCall(SNESGetFunction(smoothd, &FPC, NULL, NULL));
372: PetscCall(SNESGetAlwaysComputesFinalResidual(smoothd, &flg));
373: if (!flg) PetscCall(SNESComputeFunction(smoothd, X, FPC));
374: PetscCall(VecCopy(FPC, F));
375: if (fnorm) PetscCall(VecNorm(F, NORM_2, fnorm));
376: PetscFunctionReturn(PETSC_SUCCESS);
377: }
379: /*
380: Defines the action of the upsmoother
381: */
382: static PetscErrorCode SNESFASUpSmooth_Private(SNES snes, Vec B, Vec X, Vec F, PetscReal *fnorm)
383: {
384: SNESConvergedReason reason;
385: Vec FPC;
386: SNES smoothu;
387: PetscBool flg;
388: SNES_FAS *fas = (SNES_FAS *)snes->data;
390: PetscFunctionBegin;
391: PetscCall(SNESFASCycleGetSmootherUp(snes, &smoothu));
392: if (fas->eventsmoothsolve) PetscCall(PetscLogEventBegin(fas->eventsmoothsolve, smoothu, 0, 0, 0));
393: PetscCall(SNESSolve(smoothu, B, X));
394: if (fas->eventsmoothsolve) PetscCall(PetscLogEventEnd(fas->eventsmoothsolve, smoothu, 0, 0, 0));
395: /* check convergence reason for the smoother */
396: PetscCall(SNESGetConvergedReason(smoothu, &reason));
397: if (reason < 0 && !(reason == SNES_DIVERGED_MAX_IT || reason == SNES_DIVERGED_LOCAL_MIN || reason == SNES_DIVERGED_LINE_SEARCH)) {
398: snes->reason = SNES_DIVERGED_INNER;
399: PetscFunctionReturn(PETSC_SUCCESS);
400: }
401: PetscCall(SNESGetFunction(smoothu, &FPC, NULL, NULL));
402: PetscCall(SNESGetAlwaysComputesFinalResidual(smoothu, &flg));
403: if (!flg) PetscCall(SNESComputeFunction(smoothu, X, FPC));
404: PetscCall(VecCopy(FPC, F));
405: if (fnorm) PetscCall(VecNorm(F, NORM_2, fnorm));
406: PetscFunctionReturn(PETSC_SUCCESS);
407: }
409: /*@
410: SNESFASCreateCoarseVec - create `Vec` corresponding to a state vector on one level coarser than current level
412: Collective
414: Input Parameter:
415: . snes - `SNESFAS` object
417: Output Parameter:
418: . Xcoarse - vector on level one coarser than snes
420: Level: developer
422: .seealso: `SNESFASSetRestriction()`, `SNESFASRestrict()`
423: @*/
424: PetscErrorCode SNESFASCreateCoarseVec(SNES snes, Vec *Xcoarse)
425: {
426: SNES_FAS *fas;
428: PetscFunctionBegin;
431: fas = (SNES_FAS *)snes->data;
432: if (fas->rscale) {
433: PetscCall(VecDuplicate(fas->rscale, Xcoarse));
434: } else if (fas->interpolate) {
435: PetscCall(MatCreateVecs(fas->interpolate, Xcoarse, NULL));
436: } else SETERRQ(PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_WRONGSTATE, "Must set rscale or interpolation");
437: PetscFunctionReturn(PETSC_SUCCESS);
438: }
440: /*@
441: SNESFASRestrict - restrict a `Vec` to the next coarser level
443: Collective
445: Input Parameters:
446: + fine - `SNES` from which to restrict
447: - Xfine - vector to restrict
449: Output Parameter:
450: . Xcoarse - result of restriction
452: Level: developer
454: .seealso: `SNES`, `SNESFAS`, `SNESFASSetRestriction()`, `SNESFASSetInjection()`
455: @*/
456: PetscErrorCode SNESFASRestrict(SNES fine, Vec Xfine, Vec Xcoarse)
457: {
458: SNES_FAS *fas;
460: PetscFunctionBegin;
464: fas = (SNES_FAS *)fine->data;
465: if (fas->inject) {
466: PetscCall(MatRestrict(fas->inject, Xfine, Xcoarse));
467: } else {
468: PetscCall(MatRestrict(fas->restrct, Xfine, Xcoarse));
469: PetscCall(VecPointwiseMult(Xcoarse, fas->rscale, Xcoarse));
470: }
471: PetscFunctionReturn(PETSC_SUCCESS);
472: }
474: /*
476: Performs a variant of FAS using the interpolated total coarse solution
478: fine problem: F(x) = b
479: coarse problem: F^c(x^c) = Rb, Initial guess Rx
480: interpolated solution: x^f = I x^c (total solution interpolation
482: */
483: static PetscErrorCode SNESFASInterpolatedCoarseSolution(SNES snes, Vec X, Vec X_new)
484: {
485: Vec X_c, B_c;
486: SNESConvergedReason reason;
487: SNES next;
488: Mat restrct, interpolate;
489: SNES_FAS *fasc;
491: PetscFunctionBegin;
492: PetscCall(SNESFASCycleGetCorrection(snes, &next));
493: if (next) {
494: fasc = (SNES_FAS *)next->data;
496: PetscCall(SNESFASCycleGetRestriction(snes, &restrct));
497: PetscCall(SNESFASCycleGetInterpolation(snes, &interpolate));
499: X_c = next->vec_sol;
501: if (fasc->eventinterprestrict) PetscCall(PetscLogEventBegin(fasc->eventinterprestrict, snes, 0, 0, 0));
502: /* restrict the total solution: Rb */
503: PetscCall(SNESFASRestrict(snes, X, X_c));
504: B_c = next->vec_rhs;
505: if (snes->vec_rhs) {
506: /* restrict the total rhs defect: Rb */
507: PetscCall(MatRestrict(restrct, snes->vec_rhs, B_c));
508: } else {
509: PetscCall(VecSet(B_c, 0.));
510: }
511: if (fasc->eventinterprestrict) PetscCall(PetscLogEventEnd(fasc->eventinterprestrict, snes, 0, 0, 0));
513: PetscCall(SNESSolve(next, B_c, X_c));
514: PetscCall(SNESGetConvergedReason(next, &reason));
515: if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
516: snes->reason = SNES_DIVERGED_INNER;
517: PetscFunctionReturn(PETSC_SUCCESS);
518: }
519: /* x^f <- Ix^c*/
520: DM dmc, dmf;
522: PetscCall(SNESGetDM(next, &dmc));
523: PetscCall(SNESGetDM(snes, &dmf));
524: if (fasc->eventinterprestrict) PetscCall(PetscLogEventBegin(fasc->eventinterprestrict, snes, 0, 0, 0));
525: PetscCall(DMInterpolateSolution(dmc, dmf, interpolate, X_c, X_new));
526: if (fasc->eventinterprestrict) PetscCall(PetscLogEventEnd(fasc->eventinterprestrict, snes, 0, 0, 0));
527: PetscCall(PetscObjectSetName((PetscObject)X_c, "Coarse solution"));
528: PetscCall(VecViewFromOptions(X_c, NULL, "-fas_coarse_solution_view"));
529: PetscCall(PetscObjectSetName((PetscObject)X_new, "Updated Fine solution"));
530: PetscCall(VecViewFromOptions(X_new, NULL, "-fas_levels_1_solution_view"));
531: }
532: PetscFunctionReturn(PETSC_SUCCESS);
533: }
535: /*
537: Performs the FAS coarse correction as:
539: fine problem: F(x) = b
540: coarse problem: F^c(x^c) = b^c
542: b^c = F^c(Rx) - R(F(x) - b)
544: */
545: PetscErrorCode SNESFASCoarseCorrection(SNES snes, Vec X, Vec F, Vec X_new)
546: {
547: Vec X_c, Xo_c, F_c, B_c;
548: SNESConvergedReason reason;
549: SNES next;
550: Mat restrct, interpolate;
551: SNES_FAS *fasc;
553: PetscFunctionBegin;
554: PetscCall(SNESFASCycleGetCorrection(snes, &next));
555: if (next) {
556: fasc = (SNES_FAS *)next->data;
558: PetscCall(SNESFASCycleGetRestriction(snes, &restrct));
559: PetscCall(SNESFASCycleGetInterpolation(snes, &interpolate));
561: X_c = next->vec_sol;
562: Xo_c = next->work[0];
563: F_c = next->vec_func;
564: B_c = next->vec_rhs;
566: if (fasc->eventinterprestrict) PetscCall(PetscLogEventBegin(fasc->eventinterprestrict, snes, 0, 0, 0));
567: PetscCall(SNESFASRestrict(snes, X, Xo_c));
568: /* restrict the defect: R(F(x) - b) */
569: PetscCall(MatRestrict(restrct, F, B_c));
570: if (fasc->eventinterprestrict) PetscCall(PetscLogEventEnd(fasc->eventinterprestrict, snes, 0, 0, 0));
572: if (fasc->eventresidual) PetscCall(PetscLogEventBegin(fasc->eventresidual, next, 0, 0, 0));
573: /* F_c = F^c(Rx) - R(F(x) - b) since the second term was sitting in next->vec_rhs */
574: PetscCall(SNESComputeFunction(next, Xo_c, F_c));
575: if (fasc->eventresidual) PetscCall(PetscLogEventEnd(fasc->eventresidual, next, 0, 0, 0));
577: /* solve the coarse problem corresponding to F^c(x^c) = b^c = F^c(Rx) - R(F(x) - b) */
578: PetscCall(VecCopy(B_c, X_c));
579: PetscCall(VecCopy(F_c, B_c));
580: PetscCall(VecCopy(X_c, F_c));
581: /* set initial guess of the coarse problem to the projected fine solution */
582: PetscCall(VecCopy(Xo_c, X_c));
584: /* recurse to the next level */
585: PetscCall(SNESSetInitialFunction(next, F_c));
586: PetscCall(SNESSolve(next, B_c, X_c));
587: PetscCall(SNESGetConvergedReason(next, &reason));
588: if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
589: snes->reason = SNES_DIVERGED_INNER;
590: PetscFunctionReturn(PETSC_SUCCESS);
591: }
592: /* correct as x <- x + I(x^c - Rx)*/
593: PetscCall(VecAXPY(X_c, -1.0, Xo_c));
595: if (fasc->eventinterprestrict) PetscCall(PetscLogEventBegin(fasc->eventinterprestrict, snes, 0, 0, 0));
596: PetscCall(MatInterpolateAdd(interpolate, X_c, X, X_new));
597: if (fasc->eventinterprestrict) PetscCall(PetscLogEventEnd(fasc->eventinterprestrict, snes, 0, 0, 0));
598: PetscCall(PetscObjectSetName((PetscObject)X_c, "Coarse correction"));
599: PetscCall(VecViewFromOptions(X_c, NULL, "-fas_coarse_solution_view"));
600: PetscCall(PetscObjectSetName((PetscObject)X_new, "Updated Fine solution"));
601: PetscCall(VecViewFromOptions(X_new, NULL, "-fas_levels_1_solution_view"));
602: }
603: PetscFunctionReturn(PETSC_SUCCESS);
604: }
606: /*
608: The additive cycle looks like:
610: xhat = x
611: xhat = dS(x, b)
612: x = coarsecorrection(xhat, b_d)
613: x = x + nu*(xhat - x);
614: (optional) x = uS(x, b)
616: With the coarse RHS (defect correction) as below.
618: */
619: static PetscErrorCode SNESFASCycle_Additive(SNES snes, Vec X)
620: {
621: Vec F, B, Xhat;
622: Vec X_c, Xo_c, F_c, B_c;
623: SNESConvergedReason reason;
624: PetscReal xnorm, fnorm, ynorm;
625: SNESLineSearchReason lsresult;
626: SNES next;
627: Mat restrct, interpolate;
628: SNES_FAS *fas = (SNES_FAS *)snes->data, *fasc;
630: PetscFunctionBegin;
631: PetscCall(SNESFASCycleGetCorrection(snes, &next));
632: F = snes->vec_func;
633: B = snes->vec_rhs;
634: Xhat = snes->work[1];
635: PetscCall(VecCopy(X, Xhat));
636: /* recurse first */
637: if (next) {
638: fasc = (SNES_FAS *)next->data;
639: PetscCall(SNESFASCycleGetRestriction(snes, &restrct));
640: PetscCall(SNESFASCycleGetInterpolation(snes, &interpolate));
641: if (fas->eventresidual) PetscCall(PetscLogEventBegin(fas->eventresidual, snes, 0, 0, 0));
642: PetscCall(SNESComputeFunction(snes, Xhat, F));
643: if (fas->eventresidual) PetscCall(PetscLogEventEnd(fas->eventresidual, snes, 0, 0, 0));
644: PetscCall(VecNorm(F, NORM_2, &fnorm));
645: X_c = next->vec_sol;
646: Xo_c = next->work[0];
647: F_c = next->vec_func;
648: B_c = next->vec_rhs;
650: PetscCall(SNESFASRestrict(snes, Xhat, Xo_c));
651: /* restrict the defect */
652: PetscCall(MatRestrict(restrct, F, B_c));
654: /* solve the coarse problem corresponding to F^c(x^c) = b^c = Rb + F^c(Rx) - RF(x) */
655: if (fasc->eventresidual) PetscCall(PetscLogEventBegin(fasc->eventresidual, next, 0, 0, 0));
656: PetscCall(SNESComputeFunction(next, Xo_c, F_c));
657: if (fasc->eventresidual) PetscCall(PetscLogEventEnd(fasc->eventresidual, next, 0, 0, 0));
658: PetscCall(VecCopy(B_c, X_c));
659: PetscCall(VecCopy(F_c, B_c));
660: PetscCall(VecCopy(X_c, F_c));
661: /* set initial guess of the coarse problem to the projected fine solution */
662: PetscCall(VecCopy(Xo_c, X_c));
664: /* recurse */
665: PetscCall(SNESSetInitialFunction(next, F_c));
666: PetscCall(SNESSolve(next, B_c, X_c));
668: /* smooth on this level */
669: PetscCall(SNESFASDownSmooth_Private(snes, B, X, F, &fnorm));
671: PetscCall(SNESGetConvergedReason(next, &reason));
672: if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
673: snes->reason = SNES_DIVERGED_INNER;
674: PetscFunctionReturn(PETSC_SUCCESS);
675: }
677: /* correct as x <- x + I(x^c - Rx)*/
678: PetscCall(VecAYPX(X_c, -1.0, Xo_c));
679: PetscCall(MatInterpolate(interpolate, X_c, Xhat));
681: /* additive correction of the coarse direction*/
682: PetscCall(SNESLineSearchApply(snes->linesearch, X, F, &fnorm, Xhat));
683: PetscCall(SNESLineSearchGetReason(snes->linesearch, &lsresult));
684: PetscCall(SNESLineSearchGetNorms(snes->linesearch, &xnorm, &snes->norm, &ynorm));
685: if (lsresult) {
686: if (++snes->numFailures >= snes->maxFailures) {
687: snes->reason = SNES_DIVERGED_LINE_SEARCH;
688: PetscFunctionReturn(PETSC_SUCCESS);
689: }
690: }
691: } else {
692: PetscCall(SNESFASDownSmooth_Private(snes, B, X, F, &snes->norm));
693: }
694: PetscFunctionReturn(PETSC_SUCCESS);
695: }
697: /*
699: Defines the FAS cycle as:
701: fine problem: F(x) = b
702: coarse problem: F^c(x) = b^c
704: b^c = F^c(Rx) - R(F(x) - b)
706: correction:
708: x = x + I(x^c - Rx)
710: */
711: static PetscErrorCode SNESFASCycle_Multiplicative(SNES snes, Vec X)
712: {
713: Vec F, B;
714: SNES next;
716: PetscFunctionBegin;
717: F = snes->vec_func;
718: B = snes->vec_rhs;
719: /* pre-smooth -- just update using the pre-smoother */
720: PetscCall(SNESFASCycleGetCorrection(snes, &next));
721: PetscCall(SNESFASDownSmooth_Private(snes, B, X, F, &snes->norm));
722: if (next) {
723: PetscCall(SNESFASCoarseCorrection(snes, X, F, X));
724: PetscCall(SNESFASUpSmooth_Private(snes, B, X, F, &snes->norm));
725: }
726: PetscFunctionReturn(PETSC_SUCCESS);
727: }
729: static PetscErrorCode SNESFASCycleSetupPhase_Full(SNES snes)
730: {
731: SNES next;
732: SNES_FAS *fas = (SNES_FAS *)snes->data;
733: PetscBool isFine;
735: PetscFunctionBegin;
736: /* pre-smooth -- just update using the pre-smoother */
737: PetscCall(SNESFASCycleIsFine(snes, &isFine));
738: PetscCall(SNESFASCycleGetCorrection(snes, &next));
739: fas->full_stage = 0;
740: if (next) PetscCall(SNESFASCycleSetupPhase_Full(next));
741: PetscFunctionReturn(PETSC_SUCCESS);
742: }
744: static PetscErrorCode SNESFASCycle_Full(SNES snes, Vec X)
745: {
746: Vec F, B;
747: SNES_FAS *fas = (SNES_FAS *)snes->data;
748: PetscBool isFine;
749: SNES next;
751: PetscFunctionBegin;
752: F = snes->vec_func;
753: B = snes->vec_rhs;
754: PetscCall(SNESFASCycleIsFine(snes, &isFine));
755: PetscCall(SNESFASCycleGetCorrection(snes, &next));
757: if (isFine) PetscCall(SNESFASCycleSetupPhase_Full(snes));
759: if (fas->full_stage == 0) {
760: /* downsweep */
761: if (next) {
762: if (fas->level != 1) next->max_its += 1;
763: if (fas->full_downsweep) PetscCall(SNESFASDownSmooth_Private(snes, B, X, F, &snes->norm));
764: fas->full_downsweep = PETSC_TRUE;
765: if (fas->full_total) PetscCall(SNESFASInterpolatedCoarseSolution(snes, X, X));
766: else PetscCall(SNESFASCoarseCorrection(snes, X, F, X));
767: fas->full_total = PETSC_FALSE;
768: PetscCall(SNESFASUpSmooth_Private(snes, B, X, F, &snes->norm));
769: if (fas->level != 1) next->max_its -= 1;
770: } else {
771: /* The smoother on the coarse level is the coarse solver */
772: PetscCall(SNESFASDownSmooth_Private(snes, B, X, F, &snes->norm));
773: }
774: fas->full_stage = 1;
775: } else if (fas->full_stage == 1) {
776: if (snes->iter == 0) PetscCall(SNESFASDownSmooth_Private(snes, B, X, F, &snes->norm));
777: if (next) {
778: PetscCall(SNESFASCoarseCorrection(snes, X, F, X));
779: PetscCall(SNESFASUpSmooth_Private(snes, B, X, F, &snes->norm));
780: }
781: }
782: /* final v-cycle */
783: if (isFine) {
784: if (next) {
785: PetscCall(SNESFASCoarseCorrection(snes, X, F, X));
786: PetscCall(SNESFASUpSmooth_Private(snes, B, X, F, &snes->norm));
787: }
788: }
789: PetscFunctionReturn(PETSC_SUCCESS);
790: }
792: static PetscErrorCode SNESFASCycle_Kaskade(SNES snes, Vec X)
793: {
794: Vec F, B;
795: SNES next;
797: PetscFunctionBegin;
798: F = snes->vec_func;
799: B = snes->vec_rhs;
800: PetscCall(SNESFASCycleGetCorrection(snes, &next));
801: if (next) {
802: PetscCall(SNESFASCoarseCorrection(snes, X, F, X));
803: PetscCall(SNESFASUpSmooth_Private(snes, B, X, F, &snes->norm));
804: } else {
805: PetscCall(SNESFASDownSmooth_Private(snes, B, X, F, &snes->norm));
806: }
807: PetscFunctionReturn(PETSC_SUCCESS);
808: }
810: PetscBool SNEScite = PETSC_FALSE;
811: const char SNESCitation[] = "@techreport{pbmkbsxt2012,\n"
812: " title = {Composing Scalable Nonlinear Algebraic Solvers},\n"
813: " author = {Peter Brune and Mathew Knepley and Barry Smith and Xuemin Tu},\n"
814: " year = 2013,\n"
815: " type = Preprint,\n"
816: " number = {ANL/MCS-P2010-0112},\n"
817: " institution = {Argonne National Laboratory}\n}\n";
819: static PetscErrorCode SNESSolve_FAS(SNES snes)
820: {
821: PetscInt i;
822: Vec X, F;
823: PetscReal fnorm;
824: SNES_FAS *fas = (SNES_FAS *)snes->data, *ffas;
825: DM dm;
826: PetscBool isFine;
828: PetscFunctionBegin;
829: 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);
831: PetscCall(PetscCitationsRegister(SNESCitation, &SNEScite));
832: snes->reason = SNES_CONVERGED_ITERATING;
833: X = snes->vec_sol;
834: F = snes->vec_func;
836: PetscCall(SNESFASCycleIsFine(snes, &isFine));
837: /* norm setup */
838: PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
839: snes->iter = 0;
840: snes->norm = 0;
841: PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
842: if (!snes->vec_func_init_set) {
843: if (fas->eventresidual) PetscCall(PetscLogEventBegin(fas->eventresidual, snes, 0, 0, 0));
844: PetscCall(SNESComputeFunction(snes, X, F));
845: if (fas->eventresidual) PetscCall(PetscLogEventEnd(fas->eventresidual, snes, 0, 0, 0));
846: } else snes->vec_func_init_set = PETSC_FALSE;
848: PetscCall(VecNorm(F, NORM_2, &fnorm)); /* fnorm <- ||F|| */
849: SNESCheckFunctionNorm(snes, fnorm);
850: PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
851: snes->norm = fnorm;
852: PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
853: PetscCall(SNESLogConvergenceHistory(snes, fnorm, 0));
854: PetscCall(SNESMonitor(snes, snes->iter, fnorm));
856: /* test convergence */
857: PetscUseTypeMethod(snes, converged, 0, 0.0, 0.0, fnorm, &snes->reason, snes->cnvP);
858: if (snes->reason) PetscFunctionReturn(PETSC_SUCCESS);
860: if (isFine) {
861: /* propagate scale-dependent data up the hierarchy */
862: PetscCall(SNESGetDM(snes, &dm));
863: for (ffas = fas; ffas->next; ffas = (SNES_FAS *)ffas->next->data) {
864: DM dmcoarse;
865: PetscCall(SNESGetDM(ffas->next, &dmcoarse));
866: PetscCall(DMRestrict(dm, ffas->restrct, ffas->rscale, ffas->inject, dmcoarse));
867: dm = dmcoarse;
868: }
869: }
871: for (i = 0; i < snes->max_its; i++) {
872: /* Call general purpose update function */
873: PetscTryTypeMethod(snes, update, snes->iter);
875: if (fas->fastype == SNES_FAS_MULTIPLICATIVE) {
876: PetscCall(SNESFASCycle_Multiplicative(snes, X));
877: } else if (fas->fastype == SNES_FAS_ADDITIVE) {
878: PetscCall(SNESFASCycle_Additive(snes, X));
879: } else if (fas->fastype == SNES_FAS_FULL) {
880: PetscCall(SNESFASCycle_Full(snes, X));
881: } else if (fas->fastype == SNES_FAS_KASKADE) {
882: PetscCall(SNESFASCycle_Kaskade(snes, X));
883: } else SETERRQ(PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_WRONGSTATE, "Unsupported FAS type");
885: /* check for FAS cycle divergence */
886: if (snes->reason != SNES_CONVERGED_ITERATING) PetscFunctionReturn(PETSC_SUCCESS);
888: /* Monitor convergence */
889: PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
890: snes->iter = i + 1;
891: PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
892: PetscCall(SNESLogConvergenceHistory(snes, snes->norm, 0));
893: PetscCall(SNESMonitor(snes, snes->iter, snes->norm));
894: /* Test for convergence */
895: if (isFine) {
896: PetscUseTypeMethod(snes, converged, snes->iter, 0.0, 0.0, snes->norm, &snes->reason, snes->cnvP);
897: if (snes->reason) break;
898: }
899: }
900: if (i == snes->max_its) {
901: PetscCall(PetscInfo(snes, "Maximum number of iterations has been reached: %" PetscInt_FMT "\n", i));
902: if (!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
903: }
904: PetscFunctionReturn(PETSC_SUCCESS);
905: }
907: /*MC
909: SNESFAS - Full Approximation Scheme nonlinear multigrid solver.
911: The nonlinear problem is solved by correction using coarse versions
912: of the nonlinear problem. This problem is perturbed so that a projected
913: solution of the fine problem elicits no correction from the coarse problem.
915: Options Database Keys and Prefixes:
916: + -snes_fas_levels - The number of levels
917: . -snes_fas_cycles<1> - The number of cycles -- 1 for V, 2 for W
918: . -snes_fas_type<additive,multiplicative,full,kaskade> - Additive or multiplicative cycle
919: . -snes_fas_galerkin<`PETSC_FALSE`> - Form coarse problems by projection back upon the fine problem
920: . -snes_fas_smoothup<1> - The number of iterations of the post-smoother
921: . -snes_fas_smoothdown<1> - The number of iterations of the pre-smoother
922: . -snes_fas_monitor - Monitor progress of all of the levels
923: . -snes_fas_full_downsweep<`PETSC_FALSE`> - call the downsmooth on the initial downsweep of full FAS
924: . -fas_levels_snes_ - `SNES` options for all smoothers
925: . -fas_levels_cycle_snes_ - `SNES` options for all cycles
926: . -fas_levels_i_snes_ - `SNES` options for the smoothers on level i
927: . -fas_levels_i_cycle_snes_ - `SNES` options for the cycle on level i
928: - -fas_coarse_snes_ - `SNES` options for the coarsest smoother
930: Note:
931: The organization of the FAS solver is slightly different from the organization of `PCMG`
932: As each level has smoother `SNES` instances(down and potentially up) and a cycle `SNES` instance.
933: The cycle `SNES` instance may be used for monitoring convergence on a particular level.
935: Level: beginner
937: References:
938: . * - Peter R. Brune, Matthew G. Knepley, Barry F. Smith, and Xuemin Tu, "Composing Scalable Nonlinear Algebraic Solvers",
939: SIAM Review, 57(4), 2015
941: .seealso: `PCMG`, `SNESCreate()`, `SNES`, `SNESSetType()`, `SNESType`, `SNESFASSetRestriction()`, `SNESFASSetInjection()`,
942: `SNESFASFullGetTotal()`
943: M*/
945: PETSC_EXTERN PetscErrorCode SNESCreate_FAS(SNES snes)
946: {
947: SNES_FAS *fas;
949: PetscFunctionBegin;
950: snes->ops->destroy = SNESDestroy_FAS;
951: snes->ops->setup = SNESSetUp_FAS;
952: snes->ops->setfromoptions = SNESSetFromOptions_FAS;
953: snes->ops->view = SNESView_FAS;
954: snes->ops->solve = SNESSolve_FAS;
955: snes->ops->reset = SNESReset_FAS;
957: snes->usesksp = PETSC_FALSE;
958: snes->usesnpc = PETSC_FALSE;
960: if (!snes->tolerancesset) {
961: snes->max_funcs = 30000;
962: snes->max_its = 10000;
963: }
965: snes->alwayscomputesfinalresidual = PETSC_TRUE;
967: PetscCall(PetscNew(&fas));
969: snes->data = (void *)fas;
970: fas->level = 0;
971: fas->levels = 1;
972: fas->n_cycles = 1;
973: fas->max_up_it = 1;
974: fas->max_down_it = 1;
975: fas->smoothu = NULL;
976: fas->smoothd = NULL;
977: fas->next = NULL;
978: fas->previous = NULL;
979: fas->fine = snes;
980: fas->interpolate = NULL;
981: fas->restrct = NULL;
982: fas->inject = NULL;
983: fas->usedmfornumberoflevels = PETSC_FALSE;
984: fas->fastype = SNES_FAS_MULTIPLICATIVE;
985: fas->full_downsweep = PETSC_FALSE;
986: fas->full_total = PETSC_FALSE;
988: fas->eventsmoothsetup = 0;
989: fas->eventsmoothsolve = 0;
990: fas->eventresidual = 0;
991: fas->eventinterprestrict = 0;
992: PetscFunctionReturn(PETSC_SUCCESS);
993: }