Actual source code: mg.c

  1: /*$Id: mg.c,v 1.128 2001/10/03 22:12:40 balay Exp $*/
  2: /*
  3:     Defines the multigrid preconditioner interface.
  4: */
 5:  #include src/sles/pc/impls/mg/mgimpl.h


  8: /*
  9:        MGMCycle_Private - Given an MG structure created with MGCreate() runs 
 10:                   one multiplicative cycle down through the levels and
 11:                   back up.

 13:     Input Parameter:
 14: .   mg - structure created with  MGCreate().
 15: */
 16: int MGMCycle_Private(MG *mglevels,PetscTruth *converged)
 17: {
 18:   MG          mg = *mglevels,mgc;
 19:   int         cycles = mg->cycles,ierr,its;
 20:   PetscScalar zero = 0.0;

 23:   if (converged) *converged = PETSC_FALSE;

 25:   if (!mg->level) {  /* coarse grid */
 26:     SLESSolve(mg->smoothd,mg->b,mg->x,&its);
 27:   } else {
 28:     SLESSolve(mg->smoothd,mg->b,mg->x,&its);
 29:     (*mg->residual)(mg->A,mg->b,mg->x,mg->r);

 31:     /* if on finest level and have convergence criteria set */
 32:     if (mg->level == mg->levels-1 && mg->ttol) {
 33:       PetscReal rnorm;
 34:       VecNorm(mg->r,NORM_2,&rnorm);
 35:       if (rnorm <= mg->ttol) {
 36:         *converged = PETSC_TRUE;
 37:         if (rnorm < mg->atol) {
 38:           PetscLogInfo(0,"Linear solver has converged. Residual norm %g is less than absolute tolerance %gn",rnorm,mg->atol);
 39:         } else {
 40:           PetscLogInfo(0,"Linear solver has converged. Residual norm %g is less than relative tolerance times initial residual norm %gn",rnorm,mg->ttol);
 41:         }
 42:         return(0);
 43:       }
 44:     }

 46:     mgc = *(mglevels - 1);
 47:     MatRestrict(mg->restrct,mg->r,mgc->b);
 48:     VecSet(&zero,mgc->x);
 49:     while (cycles--) {
 50:       MGMCycle_Private(mglevels-1,converged);
 51:     }
 52:     MatInterpolateAdd(mg->interpolate,mgc->x,mg->x,mg->x);
 53:     SLESSolve(mg->smoothu,mg->b,mg->x,&its);
 54:   }
 55:   return(0);
 56: }

 58: /*
 59:        MGCreate_Private - Creates a MG structure for use with the
 60:                multigrid code. Level 0 is the coarsest. (But the 
 61:                finest level is stored first in the array).

 63: */
 64: static int MGCreate_Private(MPI_Comm comm,int levels,PC pc,MPI_Comm *comms,MG **result)
 65: {
 66:   MG   *mg;
 67:   int  i,ierr,size;
 68:   char *prefix;
 69:   KSP  ksp;
 70:   PC   ipc;

 73:   PetscMalloc(levels*sizeof(MG),&mg);
 74:   PetscLogObjectMemory(pc,levels*(sizeof(MG)+sizeof(struct _MG)));

 76:   PCGetOptionsPrefix(pc,&prefix);

 78:   for (i=0; i<levels; i++) {
 79:     PetscNew(struct _MG,&mg[i]);
 80:     PetscMemzero(mg[i],sizeof(struct _MG));
 81:     mg[i]->level  = i;
 82:     mg[i]->levels = levels;
 83:     mg[i]->cycles = 1;

 85:     if (comms) comm = comms[i];
 86:     SLESCreate(comm,&mg[i]->smoothd);
 87:     SLESGetKSP(mg[i]->smoothd,&ksp);
 88:     KSPSetTolerances(ksp,PETSC_DEFAULT,PETSC_DEFAULT,PETSC_DEFAULT,1);
 89:     SLESSetOptionsPrefix(mg[i]->smoothd,prefix);

 91:     /* do special stuff for coarse grid */
 92:     if (!i && levels > 1) {
 93:       SLESAppendOptionsPrefix(mg[0]->smoothd,"mg_coarse_");

 95:       /* coarse solve is (redundant) LU by default */
 96:       SLESGetKSP(mg[0]->smoothd,&ksp);
 97:       KSPSetType(ksp,KSPPREONLY);
 98:       SLESGetPC(mg[0]->smoothd,&ipc);
 99:       MPI_Comm_size(comm,&size);
100:       if (size > 1) {
101:         PCSetType(ipc,PCREDUNDANT);
102:         PCRedundantGetPC(ipc,&ipc);
103:       }
104:       PCSetType(ipc,PCLU);

106:     } else {
107:       SLESAppendOptionsPrefix(mg[i]->smoothd,"mg_levels_");
108:     }
109:     PetscLogObjectParent(pc,mg[i]->smoothd);
110:     mg[i]->smoothu         = mg[i]->smoothd;
111:     mg[i]->default_smoothu = 10000;
112:     mg[i]->default_smoothd = 10000;
113:     mg[i]->rtol = 0.0;
114:     mg[i]->atol = 0.0;
115:     mg[i]->dtol = 0.0;
116:     mg[i]->ttol = 0.0;
117:   }
118:   *result = mg;
119:   return(0);
120: }

122: static int PCDestroy_MG(PC pc)
123: {
124:   MG  *mg = (MG*)pc->data;
125:   int i,n = mg[0]->levels,ierr;

128:   for (i=0; i<n; i++) {
129:     if (mg[i]->smoothd != mg[i]->smoothu) {
130:       SLESDestroy(mg[i]->smoothd);
131:     }
132:     SLESDestroy(mg[i]->smoothu);
133:     PetscFree(mg[i]);
134:   }
135:   PetscFree(mg);
136:   return(0);
137: }



141: EXTERN int MGACycle_Private(MG*);
142: EXTERN int MGFCycle_Private(MG*);
143: EXTERN int MGKCycle_Private(MG*);

145: /*
146:    PCApply_MG - Runs either an additive, multiplicative, Kaskadic
147:              or full cycle of multigrid. 

149:   Note: 
150:   A simple wrapper which calls MGMCycle(),MGACycle(), or MGFCycle(). 
151: */
152: static int PCApply_MG(PC pc,Vec b,Vec x)
153: {
154:   MG     *mg = (MG*)pc->data;
155:   PetscScalar zero = 0.0;
156:   int    levels = mg[0]->levels,ierr;

159:   mg[levels-1]->b = b;
160:   mg[levels-1]->x = x;
161:   if (mg[0]->am == MGMULTIPLICATIVE) {
162:     VecSet(&zero,x);
163:     MGMCycle_Private(mg+levels-1,PETSC_NULL);
164:   }
165:   else if (mg[0]->am == MGADDITIVE) {
166:     MGACycle_Private(mg);
167:   }
168:   else if (mg[0]->am == MGKASKADE) {
169:     MGKCycle_Private(mg);
170:   }
171:   else {
172:     MGFCycle_Private(mg);
173:   }
174:   return(0);
175: }

177: static int PCApplyRichardson_MG(PC pc,Vec b,Vec x,Vec w,PetscReal rtol,PetscReal atol, PetscReal dtol,int its)
178: {
179:   MG         *mg = (MG*)pc->data;
180:   int        ierr,levels = mg[0]->levels;
181:   PetscTruth converged = PETSC_FALSE;

184:   mg[levels-1]->b    = b;
185:   mg[levels-1]->x    = x;

187:   mg[levels-1]->rtol = rtol;
188:   mg[levels-1]->atol = atol;
189:   mg[levels-1]->dtol = dtol;
190:   if (rtol) {
191:     /* compute initial residual norm for relative convergence test */
192:     PetscReal rnorm;
193:     ierr               = (*mg[levels-1]->residual)(mg[levels-1]->A,b,x,w);
194:     ierr               = VecNorm(w,NORM_2,&rnorm);
195:     mg[levels-1]->ttol = PetscMax(rtol*rnorm,atol);
196:   } else if (atol) {
197:     mg[levels-1]->ttol = atol;
198:   } else {
199:     mg[levels-1]->ttol = 0.0;
200:   }

202:   while (its-- && !converged) {
203:     MGMCycle_Private(mg+levels-1,&converged);
204:   }
205:   return(0);
206: }

208: static int PCSetFromOptions_MG(PC pc)
209: {
210:   int        ierr,m,levels = 1;
211:   PetscTruth flg;
212:   char       buff[16],*type[] = {"additive","multiplicative","full","cascade"};


216:   PetscOptionsHead("Multigrid options");
217:     if (!pc->data) {
218:       PetscOptionsInt("-pc_mg_levels","Number of Levels","MGSetLevels",levels,&levels,&flg);
219:       MGSetLevels(pc,levels,PETSC_NULL);
220:     }
221:     PetscOptionsInt("-pc_mg_cycles","1 for V cycle, 2 for W-cycle","MGSetCycles",1,&m,&flg);
222:     if (flg) {
223:       MGSetCycles(pc,m);
224:     }
225:     PetscOptionsInt("-pc_mg_smoothup","Number of post-smoothing steps","MGSetNumberSmoothUp",1,&m,&flg);
226:     if (flg) {
227:       MGSetNumberSmoothUp(pc,m);
228:     }
229:     PetscOptionsInt("-pc_mg_smoothdown","Number of pre-smoothing steps","MGSetNumberSmoothDown",1,&m,&flg);
230:     if (flg) {
231:       MGSetNumberSmoothDown(pc,m);
232:     }
233:     PetscOptionsEList("-pc_mg_type","Multigrid type","MGSetType",type,4,"multiplicative",buff,15,&flg);
234:     if (flg) {
235:       MGType     mg;
236:       PetscTruth isadd,ismult,isfull,iskask,iscasc;

238:       PetscStrcmp(buff,type[0],&isadd);
239:       PetscStrcmp(buff,type[1],&ismult);
240:       PetscStrcmp(buff,type[2],&isfull);
241:       PetscStrcmp(buff,type[3],&iscasc);
242:       PetscStrcmp(buff,"kaskade",&iskask);

244:       if      (isadd)  mg = MGADDITIVE;
245:       else if (ismult) mg = MGMULTIPLICATIVE;
246:       else if (isfull) mg = MGFULL;
247:       else if (iskask) mg = MGKASKADE;
248:       else if (iscasc) mg = MGKASKADE;
249:       else SETERRQ1(PETSC_ERR_ARG_OUTOFRANGE,"Unknown type: %s",buff);
250:       MGSetType(pc,mg);
251:     }
252:   PetscOptionsTail();
253:   return(0);
254: }

256: static int PCView_MG(PC pc,PetscViewer viewer)
257: {
258:   MG         *mg = (MG*)pc->data;
259:   KSP        kspu,kspd;
260:   int        itu,itd,ierr,levels = mg[0]->levels,i;
261:   PetscReal  dtol,atol,rtol;
262:   char       *cstring;
263:   PetscTruth isascii;

266:   PetscTypeCompare((PetscObject)viewer,PETSC_VIEWER_ASCII,&isascii);
267:   if (isascii) {
268:     SLESGetKSP(mg[0]->smoothu,&kspu);
269:     SLESGetKSP(mg[0]->smoothd,&kspd);
270:     KSPGetTolerances(kspu,&dtol,&atol,&rtol,&itu);
271:     KSPGetTolerances(kspd,&dtol,&atol,&rtol,&itd);
272:     if (mg[0]->am == MGMULTIPLICATIVE) cstring = "multiplicative";
273:     else if (mg[0]->am == MGADDITIVE)  cstring = "additive";
274:     else if (mg[0]->am == MGFULL)      cstring = "full";
275:     else if (mg[0]->am == MGKASKADE)   cstring = "Kaskade";
276:     else cstring = "unknown";
277:     PetscViewerASCIIPrintf(viewer,"  MG: type is %s, levels=%d cycles=%d, pre-smooths=%d, post-smooths=%dn",
278:                       cstring,levels,mg[0]->cycles,mg[0]->default_smoothu,mg[0]->default_smoothd);
279:     for (i=0; i<levels; i++) {
280:       PetscViewerASCIIPrintf(viewer,"Down solver (pre-smoother) on level %d -------------------------------n",i);
281:       PetscViewerASCIIPushTab(viewer);
282:       SLESView(mg[i]->smoothd,viewer);
283:       PetscViewerASCIIPopTab(viewer);
284:       if (mg[i]->smoothd == mg[i]->smoothu) {
285:         PetscViewerASCIIPrintf(viewer,"Up solver same as down solvern");
286:       } else {
287:         PetscViewerASCIIPrintf(viewer,"Up solver (post-smoother) on level %d -------------------------------n",i);
288:         PetscViewerASCIIPushTab(viewer);
289:         SLESView(mg[i]->smoothu,viewer);
290:         PetscViewerASCIIPopTab(viewer);
291:       }
292:     }
293:   } else {
294:     SETERRQ1(1,"Viewer type %s not supported for PCMG",((PetscObject)viewer)->type_name);
295:   }
296:   return(0);
297: }

299: /*
300:     Calls setup for the SLES on each level
301: */
302: static int PCSetUp_MG(PC pc)
303: {
304:   MG          *mg = (MG*)pc->data;
305:   int         ierr,i,n = mg[0]->levels;
306:   KSP         ksp;
307:   PC          cpc;
308:   PetscTruth  preonly,lu,redundant,monitor = PETSC_FALSE,dump;
309:   PetscViewer ascii;
310:   MPI_Comm    comm;

313:   /*
314:      temporarily stick pc->vec into mg[0]->b and x so that 
315:    SLESSetUp is happy. Since currently those slots are empty.
316:   */
317:   mg[n-1]->x = pc->vec;
318:   mg[n-1]->b = pc->vec;

320:   if (pc->setupcalled == 0) {
321:     PetscOptionsHasName(0,"-pc_mg_monitor",&monitor);
322: 
323:     for (i=1; i<n; i++) {
324:       if (mg[i]->smoothd) {
325:         if (monitor) {
326:           SLESGetKSP(mg[i]->smoothd,&ksp);
327:           PetscObjectGetComm((PetscObject)ksp,&comm);
328:           PetscViewerASCIIOpen(comm,"stdout",&ascii);
329:           PetscViewerASCIISetTab(ascii,n-i);
330:           KSPSetMonitor(ksp,KSPDefaultMonitor,ascii,(int(*)(void*))PetscViewerDestroy);
331:         }
332:         SLESSetFromOptions(mg[i]->smoothd);
333:       }
334:     }
335:     for (i=0; i<n; i++) {
336:       if (mg[i]->smoothu && mg[i]->smoothu != mg[i]->smoothd) {
337:         if (monitor) {
338:           SLESGetKSP(mg[i]->smoothu,&ksp);
339:           PetscObjectGetComm((PetscObject)ksp,&comm);
340:           PetscViewerASCIIOpen(comm,"stdout",&ascii);
341:           PetscViewerASCIISetTab(ascii,n-i);
342:           KSPSetMonitor(ksp,KSPDefaultMonitor,ascii,(int(*)(void*))PetscViewerDestroy);
343:         }
344:         SLESSetFromOptions(mg[i]->smoothu);
345:       }
346:     }
347:   }

349:   for (i=1; i<n; i++) {
350:     if (mg[i]->smoothd) {
351:       SLESGetKSP(mg[i]->smoothd,&ksp);
352:       KSPSetInitialGuessNonzero(ksp,PETSC_TRUE);
353:       SLESSetUp(mg[i]->smoothd,mg[i]->b,mg[i]->x);
354:     }
355:   }
356:   for (i=0; i<n; i++) {
357:     if (mg[i]->smoothu && mg[i]->smoothu != mg[i]->smoothd) {
358:       SLESGetKSP(mg[i]->smoothu,&ksp);
359:       KSPSetInitialGuessNonzero(ksp,PETSC_TRUE);
360:       SLESSetUp(mg[i]->smoothu,mg[i]->b,mg[i]->x);
361:     }
362:   }

364:   /*
365:       If coarse solver is not direct method then DO NOT USE preonly 
366:   */
367:   SLESGetKSP(mg[0]->smoothd,&ksp);
368:   PetscTypeCompare((PetscObject)ksp,KSPPREONLY,&preonly);
369:   if (preonly) {
370:     SLESGetPC(mg[0]->smoothd,&cpc);
371:     PetscTypeCompare((PetscObject)cpc,PCLU,&lu);
372:     PetscTypeCompare((PetscObject)cpc,PCREDUNDANT,&redundant);
373:     if (!lu && !redundant) {
374:       KSPSetType(ksp,KSPGMRES);
375:     }
376:   }

378:   if (pc->setupcalled == 0) {
379:     if (monitor) {
380:       SLESGetKSP(mg[0]->smoothd,&ksp);
381:       PetscObjectGetComm((PetscObject)ksp,&comm);
382:       PetscViewerASCIIOpen(comm,"stdout",&ascii);
383:       PetscViewerASCIISetTab(ascii,n);
384:       KSPSetMonitor(ksp,KSPDefaultMonitor,ascii,(int(*)(void*))PetscViewerDestroy);
385:     }
386:     SLESSetFromOptions(mg[0]->smoothd);
387:   }

389:   SLESSetUp(mg[0]->smoothd,mg[0]->b,mg[0]->x);

391:   /*
392:      Dump the interpolation/restriction matrices to matlab plus the 
393:    Jacobian/stiffness on each level. This allows Matlab users to 
394:    easily check if the Galerkin condition A_c = R A_f R^T is satisfied */
395:   PetscOptionsHasName(pc->prefix,"-pc_mg_dump_matlab",&dump);
396:   if (dump) {
397:     for (i=1; i<n; i++) {
398:       MatView(mg[i]->restrct,PETSC_VIEWER_SOCKET_(pc->comm));
399:     }
400:     for (i=0; i<n; i++) {
401:       SLESGetPC(mg[i]->smoothd,&pc);
402:       MatView(pc->mat,PETSC_VIEWER_SOCKET_(pc->comm));
403:     }
404:   }

406:   return(0);
407: }

409: /* -------------------------------------------------------------------------------------*/

411: /*@C
412:    MGSetLevels - Sets the number of levels to use with MG.
413:    Must be called before any other MG routine.

415:    Collective on PC

417:    Input Parameters:
418: +  pc - the preconditioner context
419: .  levels - the number of levels
420: -  comms - optional communicators for each level; this is to allow solving the coarser problems
421:            on smaller sets of processors. Use PETSC_NULL_OBJECT for default in Fortran

423:    Level: intermediate

425:    Notes:
426:      If the number of levels is one then the multigrid uses the -mg_levels prefix
427:   for setting the level options rather than the -mg_coarse prefix.

429: .keywords: MG, set, levels, multigrid

431: .seealso: MGSetType(), MGGetLevels()
432: @*/
433: int MGSetLevels(PC pc,int levels,MPI_Comm *comms)
434: {
436:   MG  *mg;


441:   if (pc->data) {
442:     SETERRQ(1,"Number levels already set for MGn
443:     make sure that you call MGSetLevels() before SLESSetFromOptions()");
444:   }
445:   ierr                     = MGCreate_Private(pc->comm,levels,pc,comms,&mg);
446:   mg[0]->am                = MGMULTIPLICATIVE;
447:   pc->data                 = (void*)mg;
448:   pc->ops->applyrichardson = PCApplyRichardson_MG;
449:   return(0);
450: }

452: /*@
453:    MGGetLevels - Gets the number of levels to use with MG.

455:    Not Collective

457:    Input Parameter:
458: .  pc - the preconditioner context

460:    Output parameter:
461: .  levels - the number of levels

463:    Level: advanced

465: .keywords: MG, get, levels, multigrid

467: .seealso: MGSetLevels()
468: @*/
469: int MGGetLevels(PC pc,int *levels)
470: {
471:   MG  *mg;


476:   mg      = (MG*)pc->data;
477:   *levels = mg[0]->levels;
478:   return(0);
479: }

481: /*@
482:    MGSetType - Determines the form of multigrid to use:
483:    multiplicative, additive, full, or the Kaskade algorithm.

485:    Collective on PC

487:    Input Parameters:
488: +  pc - the preconditioner context
489: -  form - multigrid form, one of MGMULTIPLICATIVE, MGADDITIVE,
490:    MGFULL, MGKASKADE

492:    Options Database Key:
493: .  -pc_mg_type <form> - Sets <form>, one of multiplicative,
494:    additive, full, kaskade   

496:    Level: advanced

498: .keywords: MG, set, method, multiplicative, additive, full, Kaskade, multigrid

500: .seealso: MGSetLevels()
501: @*/
502: int MGSetType(PC pc,MGType form)
503: {
504:   MG *mg;

508:   mg = (MG*)pc->data;

510:   if (!mg) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Must set MG levels before calling");
511:   mg[0]->am = form;
512:   if (form == MGMULTIPLICATIVE) pc->ops->applyrichardson = PCApplyRichardson_MG;
513:   else pc->ops->applyrichardson = 0;
514:   return(0);
515: }

517: /*@
518:    MGSetCycles - Sets the type cycles to use.  Use MGSetCyclesOnLevel() for more 
519:    complicated cycling.

521:    Collective on PC

523:    Input Parameters:
524: +  mg - the multigrid context 
525: -  n - the number of cycles

527:    Options Database Key:
528: $  -pc_mg_cycles n - 1 denotes a V-cycle; 2 denotes a W-cycle.

530:    Level: advanced

532: .keywords: MG, set, cycles, V-cycle, W-cycle, multigrid

534: .seealso: MGSetCyclesOnLevel()
535: @*/
536: int MGSetCycles(PC pc,int n)
537: {
538:   MG  *mg;
539:   int i,levels;

543:   mg     = (MG*)pc->data;
544:   if (!mg) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Must set MG levels before calling");
545:   levels = mg[0]->levels;

547:   for (i=0; i<levels; i++) {
548:     mg[i]->cycles  = n;
549:   }
550:   return(0);
551: }

553: /*@
554:    MGCheck - Checks that all components of the MG structure have 
555:    been set.

557:    Collective on PC

559:    Input Parameters:
560: .  mg - the MG structure

562:    Level: advanced

564: .keywords: MG, check, set, multigrid
565: @*/
566: int MGCheck(PC pc)
567: {
568:   MG  *mg;
569:   int i,n,count = 0;

573:   mg = (MG*)pc->data;

575:   if (!mg) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Must set MG levels before calling");

577:   n = mg[0]->levels;

579:   for (i=1; i<n; i++) {
580:     if (!mg[i]->restrct) {
581:       (*PetscErrorPrintf)("No restrict set level %d n",n-i); count++;
582:     }
583:     if (!mg[i]->interpolate) {
584:       (*PetscErrorPrintf)("No interpolate set level %d n",n-i); count++;
585:     }
586:     if (!mg[i]->residual) {
587:       (*PetscErrorPrintf)("No residual set level %d n",n-i); count++;
588:     }
589:     if (!mg[i]->smoothu) {
590:       (*PetscErrorPrintf)("No smoothup set level %d n",n-i); count++;
591:     }
592:     if (!mg[i]->smoothd) {
593:       (*PetscErrorPrintf)("No smoothdown set level %d n",n-i); count++;
594:     }
595:     if (!mg[i]->r) {
596:       (*PetscErrorPrintf)("No r set level %d n",n-i); count++;
597:     }
598:     if (!mg[i-1]->x) {
599:       (*PetscErrorPrintf)("No x set level %d n",n-i); count++;
600:     }
601:     if (!mg[i-1]->b) {
602:       (*PetscErrorPrintf)("No b set level %d n",n-i); count++;
603:     }
604:   }
605:   PetscFunctionReturn(count);
606: }


609: /*@
610:    MGSetNumberSmoothDown - Sets the number of pre-smoothing steps to
611:    use on all levels. Use MGGetSmootherDown() to set different 
612:    pre-smoothing steps on different levels.

614:    Collective on PC

616:    Input Parameters:
617: +  mg - the multigrid context 
618: -  n - the number of smoothing steps

620:    Options Database Key:
621: .  -pc_mg_smoothdown <n> - Sets number of pre-smoothing steps

623:    Level: advanced

625: .keywords: MG, smooth, down, pre-smoothing, steps, multigrid

627: .seealso: MGSetNumberSmoothUp()
628: @*/
629: int MGSetNumberSmoothDown(PC pc,int n)
630: {
631:   MG  *mg;
632:   int i,levels,ierr;
633:   KSP ksp;

637:   mg     = (MG*)pc->data;
638:   if (!mg) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Must set MG levels before calling");
639:   levels = mg[0]->levels;

641:   for (i=0; i<levels; i++) {
642:     SLESGetKSP(mg[i]->smoothd,&ksp);
643:     KSPSetTolerances(ksp,PETSC_DEFAULT,PETSC_DEFAULT,PETSC_DEFAULT,n);
644:     mg[i]->default_smoothd = n;
645:   }
646:   return(0);
647: }

649: /*@
650:    MGSetNumberSmoothUp - Sets the number of post-smoothing steps to use 
651:    on all levels. Use MGGetSmootherUp() to set different numbers of 
652:    post-smoothing steps on different levels.

654:    Collective on PC

656:    Input Parameters:
657: +  mg - the multigrid context 
658: -  n - the number of smoothing steps

660:    Options Database Key:
661: .  -pc_mg_smoothup <n> - Sets number of post-smoothing steps

663:    Level: advanced

665: .keywords: MG, smooth, up, post-smoothing, steps, multigrid

667: .seealso: MGSetNumberSmoothDown()
668: @*/
669: int  MGSetNumberSmoothUp(PC pc,int n)
670: {
671:   MG  *mg;
672:   int i,levels,ierr;
673:   KSP ksp;

677:   mg     = (MG*)pc->data;
678:   if (!mg) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Must set MG levels before calling");
679:   levels = mg[0]->levels;

681:   for (i=0; i<levels; i++) {
682:     SLESGetKSP(mg[i]->smoothu,&ksp);
683:     KSPSetTolerances(ksp,PETSC_DEFAULT,PETSC_DEFAULT,PETSC_DEFAULT,n);
684:     mg[i]->default_smoothu = n;
685:   }
686:   return(0);
687: }

689: /* ----------------------------------------------------------------------------------------*/

691: EXTERN_C_BEGIN
692: int PCCreate_MG(PC pc)
693: {
695:   pc->ops->apply          = PCApply_MG;
696:   pc->ops->setup          = PCSetUp_MG;
697:   pc->ops->destroy        = PCDestroy_MG;
698:   pc->ops->setfromoptions = PCSetFromOptions_MG;
699:   pc->ops->view           = PCView_MG;

701:   pc->data                = (void*)0;
702:   return(0);
703: }
704: EXTERN_C_END