Actual source code: eisen.c

  1: #define PETSCKSP_DLL

  3: /*
  4:    Defines a  Eisenstat trick SSOR  preconditioner. This uses about 
  5:  %50 of the usual amount of floating point ops used for SSOR + Krylov 
  6:  method. But it requires actually solving the preconditioned problem 
  7:  with both left and right preconditioning. 
  8: */
 9:  #include private/pcimpl.h

 11: typedef struct {
 12:   Mat        shell,A;
 13:   Vec        b,diag;     /* temporary storage for true right hand side */
 14:   PetscReal  omega;
 15:   PetscTruth usediag;    /* indicates preconditioner should include diagonal scaling*/
 16: } PC_Eisenstat;


 21: static PetscErrorCode PCMult_Eisenstat(Mat mat,Vec b,Vec x)
 22: {
 24:   PC             pc;
 25:   PC_Eisenstat   *eis;

 28:   MatShellGetContext(mat,(void **)&pc);
 29:   eis = (PC_Eisenstat*)pc->data;
 30:   MatSOR(eis->A,b,eis->omega,SOR_EISENSTAT,0.0,1,1,x);
 31:   return(0);
 32: }

 36: static PetscErrorCode PCApply_Eisenstat(PC pc,Vec x,Vec y)
 37: {
 38:   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
 40:   PetscTruth     hasop;

 43:   if (eis->usediag)  {
 44:     MatHasOperation(pc->pmat,MATOP_MULT_DIAGONAL_BLOCK,&hasop);
 45:     if (hasop) {
 46:       MatMultDiagonalBlock(pc->pmat,x,y);
 47:     } else {
 48:       VecPointwiseMult(y,x,eis->diag);
 49:     }
 50:   } else {VecCopy(x,y);}
 51:   return(0);
 52: }

 56: static PetscErrorCode PCPreSolve_Eisenstat(PC pc,KSP ksp,Vec b,Vec x)
 57: {
 58:   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
 59:   PetscTruth     nonzero;

 63:   if (pc->mat != pc->pmat) SETERRQ(PETSC_ERR_SUP,"Cannot have different mat and pmat");
 64: 
 65:   /* swap shell matrix and true matrix */
 66:   eis->A    = pc->mat;
 67:   pc->mat   = eis->shell;

 69:   if (!eis->b) {
 70:     VecDuplicate(b,&eis->b);
 71:     PetscLogObjectParent(pc,eis->b);
 72:   }
 73: 
 74:   /* save true b, other option is to swap pointers */
 75:   VecCopy(b,eis->b);

 77:   /* if nonzero initial guess, modify x */
 78:   KSPGetInitialGuessNonzero(ksp,&nonzero);
 79:   if (nonzero) {
 80:     MatSOR(eis->A,x,eis->omega,SOR_APPLY_UPPER,0.0,1,1,x);
 81:   }

 83:   /* modify b by (L + D/omega)^{-1} */
 84:     MatSOR(eis->A,b,eis->omega,(MatSORType)(SOR_ZERO_INITIAL_GUESS | SOR_LOCAL_FORWARD_SWEEP),0.0,1,1,b);
 85:   return(0);
 86: }

 90: static PetscErrorCode PCPostSolve_Eisenstat(PC pc,KSP ksp,Vec b,Vec x)
 91: {
 92:   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;

 96:   /* modify x by (U + D/omega)^{-1} */
 97:     MatSOR(eis->A,x,eis->omega,(MatSORType)(SOR_ZERO_INITIAL_GUESS | SOR_LOCAL_BACKWARD_SWEEP),0.0,1,1,x);
 98:   pc->mat = eis->A;
 99:   /* get back true b */
100:   VecCopy(eis->b,b);
101:   return(0);
102: }

106: static PetscErrorCode PCDestroy_Eisenstat(PC pc)
107: {
108:   PC_Eisenstat   *eis = (PC_Eisenstat *)pc->data;

112:   if (eis->b)     {VecDestroy(eis->b);}
113:   if (eis->shell) {MatDestroy(eis->shell);}
114:   if (eis->diag)  {VecDestroy(eis->diag);}
115:   PetscFree(eis);
116:   return(0);
117: }

121: static PetscErrorCode PCSetFromOptions_Eisenstat(PC pc)
122: {
123:   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
125:   PetscTruth     flg = PETSC_FALSE;

128:   PetscOptionsHead("Eisenstat SSOR options");
129:     PetscOptionsReal("-pc_eisenstat_omega","Relaxation factor 0 < omega < 2","PCEisenstatSetOmega",eis->omega,&eis->omega,PETSC_NULL);
130:     PetscOptionsTruth("-pc_eisenstat_no_diagonal_scaling","Do not use standard diagonal scaling","PCEisenstatNoDiagonalScaling",flg,&flg,PETSC_NULL);
131:     if (flg) {
132:       PCEisenstatNoDiagonalScaling(pc);
133:     }
134:   PetscOptionsTail();
135:   return(0);
136: }

140: static PetscErrorCode PCView_Eisenstat(PC pc,PetscViewer viewer)
141: {
142:   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
144:   PetscTruth     iascii;

147:   PetscTypeCompare((PetscObject)viewer,PETSC_VIEWER_ASCII,&iascii);
148:   if (iascii) {
149:     PetscViewerASCIIPrintf(viewer,"Eisenstat: omega = %G\n",eis->omega);
150:     if (eis->usediag) {
151:       PetscViewerASCIIPrintf(viewer,"Eisenstat: Using diagonal scaling (default)\n");
152:     } else {
153:       PetscViewerASCIIPrintf(viewer,"Eisenstat: Not using diagonal scaling\n");
154:     }
155:   } else {
156:     SETERRQ1(PETSC_ERR_SUP,"Viewer type %s not supported for Eisenstat PC",((PetscObject)viewer)->type_name);
157:   }
158:   return(0);
159: }

163: static PetscErrorCode PCSetUp_Eisenstat(PC pc)
164: {
166:   PetscInt       M,N,m,n;
167:   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;

170:   if (!pc->setupcalled) {
171:     MatGetSize(pc->mat,&M,&N);
172:     MatGetLocalSize(pc->mat,&m,&n);
173:     MatCreate(((PetscObject)pc)->comm,&eis->shell);
174:     MatSetSizes(eis->shell,m,n,M,N);
175:     MatSetType(eis->shell,MATSHELL);
176:     MatShellSetContext(eis->shell,(void*)pc);
177:     PetscLogObjectParent(pc,eis->shell);
178:     MatShellSetOperation(eis->shell,MATOP_MULT,(void(*)(void))PCMult_Eisenstat);
179:   }
180:   if (!eis->usediag) return(0);
181:   if (!pc->setupcalled) {
182:     MatGetVecs(pc->pmat,&eis->diag,0);
183:     PetscLogObjectParent(pc,eis->diag);
184:   }
185:   MatGetDiagonal(pc->pmat,eis->diag);
186:   return(0);
187: }

189: /* --------------------------------------------------------------------*/

194: PetscErrorCode  PCEisenstatSetOmega_Eisenstat(PC pc,PetscReal omega)
195: {
196:   PC_Eisenstat  *eis;

199:   if (omega >= 2.0 || omega <= 0.0) SETERRQ(PETSC_ERR_ARG_OUTOFRANGE,"Relaxation out of range");
200:   eis = (PC_Eisenstat*)pc->data;
201:   eis->omega = omega;
202:   return(0);
203: }

209: PetscErrorCode  PCEisenstatNoDiagonalScaling_Eisenstat(PC pc)
210: {
211:   PC_Eisenstat *eis;

214:   eis = (PC_Eisenstat*)pc->data;
215:   eis->usediag = PETSC_FALSE;
216:   return(0);
217: }

222: /*@ 
223:    PCEisenstatSetOmega - Sets the SSOR relaxation coefficient, omega,
224:    to use with Eisenstat's trick (where omega = 1.0 by default).

226:    Collective on PC

228:    Input Parameters:
229: +  pc - the preconditioner context
230: -  omega - relaxation coefficient (0 < omega < 2)

232:    Options Database Key:
233: .  -pc_eisenstat_omega <omega> - Sets omega

235:    Notes: 
236:    The Eisenstat trick implementation of SSOR requires about 50% of the
237:    usual amount of floating point operations used for SSOR + Krylov method;
238:    however, the preconditioned problem must be solved with both left 
239:    and right preconditioning.

241:    To use SSOR without the Eisenstat trick, employ the PCSOR preconditioner, 
242:    which can be chosen with the database options
243: $    -pc_type  sor  -pc_sor_symmetric

245:    Level: intermediate

247: .keywords: PC, Eisenstat, set, SOR, SSOR, relaxation, omega

249: .seealso: PCSORSetOmega()
250: @*/
251: PetscErrorCode  PCEisenstatSetOmega(PC pc,PetscReal omega)
252: {
253:   PetscErrorCode ierr,(*f)(PC,PetscReal);

257:   PetscObjectQueryFunction((PetscObject)pc,"PCEisenstatSetOmega_C",(void (**)(void))&f);
258:   if (f) {
259:     (*f)(pc,omega);
260:   }
261:   return(0);
262: }

266: /*@
267:    PCEisenstatNoDiagonalScaling - Causes the Eisenstat preconditioner
268:    not to do additional diagonal preconditioning. For matrices with a constant 
269:    along the diagonal, this may save a small amount of work.

271:    Collective on PC

273:    Input Parameter:
274: .  pc - the preconditioner context

276:    Options Database Key:
277: .  -pc_eisenstat_no_diagonal_scaling - Activates PCEisenstatNoDiagonalScaling()

279:    Level: intermediate

281:    Note:
282:      If you use the KPSSetDiagonalScaling() or -ksp_diagonal_scale option then you will
283:    likley want to use this routine since it will save you some unneeded flops.

285: .keywords: PC, Eisenstat, use, diagonal, scaling, SSOR

287: .seealso: PCEisenstatSetOmega()
288: @*/
289: PetscErrorCode  PCEisenstatNoDiagonalScaling(PC pc)
290: {
291:   PetscErrorCode ierr,(*f)(PC);

295:   PetscObjectQueryFunction((PetscObject)pc,"PCEisenstatNoDiagonalScaling_C",(void (**)(void))&f);
296:   if (f) {
297:     (*f)(pc);
298:   }
299:   return(0);
300: }

302: /* --------------------------------------------------------------------*/

304: /*MC
305:      PCEISENSTAT - An implementation of SSOR (symmetric successive over relaxation, symmetric Gauss-Seidel)
306:            preconditioning that incorporates Eisenstat's trick to reduce the amount of computation needed.

308:    Options Database Keys:
309: +  -pc_eisenstat_omega <omega> - Sets omega
310: -  -pc_eisenstat_no_diagonal_scaling - Activates PCEisenstatNoDiagonalScaling()

312:    Level: beginner

314:   Concepts: SOR, preconditioners, Gauss-Seidel, Eisenstat's trick

316:    Notes: Only implemented for the SeqAIJ matrix format.
317:           Not a true parallel SOR, in parallel this implementation corresponds to block
318:           Jacobi with SOR on each block.

320: .seealso:  PCCreate(), PCSetType(), PCType (for list of available types), PC,
321:            PCEisenstatNoDiagonalScaling(), PCEisenstatSetOmega(), PCSOR
322: M*/

327: PetscErrorCode  PCCreate_Eisenstat(PC pc)
328: {
330:   PC_Eisenstat   *eis;

333:   PetscNewLog(pc,PC_Eisenstat,&eis);

335:   pc->ops->apply           = PCApply_Eisenstat;
336:   pc->ops->presolve        = PCPreSolve_Eisenstat;
337:   pc->ops->postsolve       = PCPostSolve_Eisenstat;
338:   pc->ops->applyrichardson = 0;
339:   pc->ops->setfromoptions  = PCSetFromOptions_Eisenstat;
340:   pc->ops->destroy         = PCDestroy_Eisenstat;
341:   pc->ops->view            = PCView_Eisenstat;
342:   pc->ops->setup           = PCSetUp_Eisenstat;

344:   pc->data           = (void*)eis;
345:   eis->omega         = 1.0;
346:   eis->b             = 0;
347:   eis->diag          = 0;
348:   eis->usediag       = PETSC_TRUE;

350:   PetscObjectComposeFunctionDynamic((PetscObject)pc,"PCEisenstatSetOmega_C","PCEisenstatSetOmega_Eisenstat",
351:                     PCEisenstatSetOmega_Eisenstat);
352:   PetscObjectComposeFunctionDynamic((PetscObject)pc,"PCEisenstatNoDiagonalScaling_C",
353:                     "PCEisenstatNoDiagonalScaling_Eisenstat",
354:                     PCEisenstatNoDiagonalScaling_Eisenstat);
355:  return(0);
356: }