Actual source code: eisen.c

petsc-3.7.7 2017-09-25
Report Typos and Errors
  2: /*
  3:    Defines a  Eisenstat trick SSOR  preconditioner. This uses about
  4:  %50 of the usual amount of floating point ops used for SSOR + Krylov
  5:  method. But it requires actually solving the preconditioned problem
  6:  with both left and right preconditioning.
  7: */
  8: #include <petsc/private/pcimpl.h>           /*I "petscpc.h" I*/

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


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

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

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

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

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

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

 69:   if (!eis->b[pc->presolvedone-1]) {
 70:     VecDuplicate(b,&eis->b[pc->presolvedone-1]);
 71:     PetscLogObjectParent((PetscObject)pc,(PetscObject)eis->b[pc->presolvedone-1]);
 72:   }

 74:   /* if nonzero initial guess, modify x */
 75:   KSPGetInitialGuessNonzero(ksp,&nonzero);
 76:   if (nonzero) {
 77:     VecCopy(x,eis->b[pc->presolvedone-1]);
 78:     MatSOR(eis->A,eis->b[pc->presolvedone-1],eis->omega,SOR_APPLY_UPPER,0.0,1,1,x);
 79:   }

 81:   /* save true b, other option is to swap pointers */
 82:   VecCopy(b,eis->b[pc->presolvedone-1]);

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

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

 97:   /* get back true b */
 98:   VecCopy(eis->b[pc->presolvedone],b);

100:   /* modify x by (U + D/omega)^{-1} */
101:   VecCopy(x,eis->b[pc->presolvedone]);
102:   MatSOR(eis->A,eis->b[pc->presolvedone],eis->omega,(MatSORType)(SOR_ZERO_INITIAL_GUESS | SOR_LOCAL_BACKWARD_SWEEP),0.0,1,1,x);
103:   if (!pc->presolvedone) pc->mat = eis->A;
104:   return(0);
105: }

109: static PetscErrorCode PCReset_Eisenstat(PC pc)
110: {
111:   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;

115:   VecDestroy(&eis->b[0]);
116:   VecDestroy(&eis->b[1]);
117:   MatDestroy(&eis->shell);
118:   VecDestroy(&eis->diag);
119:   return(0);
120: }

124: static PetscErrorCode PCDestroy_Eisenstat(PC pc)
125: {

129:   PCReset_Eisenstat(pc);
130:   PetscFree(pc->data);
131:   return(0);
132: }

136: static PetscErrorCode PCSetFromOptions_Eisenstat(PetscOptionItems *PetscOptionsObject,PC pc)
137: {
138:   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
140:   PetscBool      set,flg;

143:   PetscOptionsHead(PetscOptionsObject,"Eisenstat SSOR options");
144:   PetscOptionsReal("-pc_eisenstat_omega","Relaxation factor 0 < omega < 2","PCEisenstatSetOmega",eis->omega,&eis->omega,NULL);
145:   PetscOptionsBool("-pc_eisenstat_no_diagonal_scaling","Do not use standard diagonal scaling","PCEisenstatSetNoDiagonalScaling",eis->usediag ? PETSC_FALSE : PETSC_TRUE,&flg,&set);
146:   if (set) {
147:     PCEisenstatSetNoDiagonalScaling(pc,flg);
148:   }
149:   PetscOptionsTail();
150:   return(0);
151: }

155: static PetscErrorCode PCView_Eisenstat(PC pc,PetscViewer viewer)
156: {
157:   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
159:   PetscBool      iascii;

162:   PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);
163:   if (iascii) {
164:     PetscViewerASCIIPrintf(viewer,"Eisenstat: omega = %g\n",(double)eis->omega);
165:     if (eis->usediag) {
166:       PetscViewerASCIIPrintf(viewer,"Eisenstat: Using diagonal scaling (default)\n");
167:     } else {
168:       PetscViewerASCIIPrintf(viewer,"Eisenstat: Not using diagonal scaling\n");
169:     }
170:   }
171:   return(0);
172: }

176: static PetscErrorCode PCSetUp_Eisenstat(PC pc)
177: {
179:   PetscInt       M,N,m,n;
180:   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;

183:   if (!pc->setupcalled) {
184:     MatGetSize(pc->mat,&M,&N);
185:     MatGetLocalSize(pc->mat,&m,&n);
186:     MatCreate(PetscObjectComm((PetscObject)pc),&eis->shell);
187:     MatSetSizes(eis->shell,m,n,M,N);
188:     MatSetType(eis->shell,MATSHELL);
189:     MatSetUp(eis->shell);
190:     MatShellSetContext(eis->shell,(void*)pc);
191:     PetscLogObjectParent((PetscObject)pc,(PetscObject)eis->shell);
192:     MatShellSetOperation(eis->shell,MATOP_MULT,(void (*)(void))PCMult_Eisenstat);
193:   }
194:   if (!eis->usediag) return(0);
195:   if (!pc->setupcalled) {
196:     MatCreateVecs(pc->pmat,&eis->diag,0);
197:     PetscLogObjectParent((PetscObject)pc,(PetscObject)eis->diag);
198:   }
199:   MatGetDiagonal(pc->pmat,eis->diag);
200:   return(0);
201: }

203: /* --------------------------------------------------------------------*/

207: static PetscErrorCode  PCEisenstatSetOmega_Eisenstat(PC pc,PetscReal omega)
208: {
209:   PC_Eisenstat *eis = (PC_Eisenstat*)pc->data;

212:   if (omega >= 2.0 || omega <= 0.0) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_OUTOFRANGE,"Relaxation out of range");
213:   eis->omega = omega;
214:   return(0);
215: }

219: static PetscErrorCode  PCEisenstatSetNoDiagonalScaling_Eisenstat(PC pc,PetscBool flg)
220: {
221:   PC_Eisenstat *eis = (PC_Eisenstat*)pc->data;

224:   eis->usediag = flg;
225:   return(0);
226: }

230: static PetscErrorCode  PCEisenstatGetOmega_Eisenstat(PC pc,PetscReal *omega)
231: {
232:   PC_Eisenstat *eis = (PC_Eisenstat*)pc->data;

235:   *omega = eis->omega;
236:   return(0);
237: }

241: static PetscErrorCode  PCEisenstatGetNoDiagonalScaling_Eisenstat(PC pc,PetscBool *flg)
242: {
243:   PC_Eisenstat *eis = (PC_Eisenstat*)pc->data;

246:   *flg = eis->usediag;
247:   return(0);
248: }

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

256:    Logically Collective on PC

258:    Input Parameters:
259: +  pc - the preconditioner context
260: -  omega - relaxation coefficient (0 < omega < 2)

262:    Options Database Key:
263: .  -pc_eisenstat_omega <omega> - Sets omega

265:    Notes:
266:    The Eisenstat trick implementation of SSOR requires about 50% of the
267:    usual amount of floating point operations used for SSOR + Krylov method;
268:    however, the preconditioned problem must be solved with both left
269:    and right preconditioning.

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

275:    Level: intermediate

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

279: .seealso: PCSORSetOmega()
280: @*/
281: PetscErrorCode  PCEisenstatSetOmega(PC pc,PetscReal omega)
282: {

288:   PetscTryMethod(pc,"PCEisenstatSetOmega_C",(PC,PetscReal),(pc,omega));
289:   return(0);
290: }

294: /*@
295:    PCEisenstatSetNoDiagonalScaling - Causes the Eisenstat preconditioner
296:    not to do additional diagonal preconditioning. For matrices with a constant
297:    along the diagonal, this may save a small amount of work.

299:    Logically Collective on PC

301:    Input Parameters:
302: +  pc - the preconditioner context
303: -  flg - PETSC_TRUE turns off diagonal scaling inside the algorithm  

305:    Options Database Key:
306: .  -pc_eisenstat_no_diagonal_scaling - Activates PCEisenstatSetNoDiagonalScaling()

308:    Level: intermediate

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

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

316: .seealso: PCEisenstatSetOmega()
317: @*/
318: PetscErrorCode  PCEisenstatSetNoDiagonalScaling(PC pc,PetscBool flg)
319: {

324:   PetscTryMethod(pc,"PCEisenstatSetNoDiagonalScaling_C",(PC,PetscBool),(pc,flg));
325:   return(0);
326: }

330: /*@
331:    PCEisenstatGetOmega - Gets the SSOR relaxation coefficient, omega,
332:    to use with Eisenstat's trick (where omega = 1.0 by default).

334:    Logically Collective on PC

336:    Input Parameter:
337: .  pc - the preconditioner context

339:    Output Parameter:
340: .  omega - relaxation coefficient (0 < omega < 2)

342:    Options Database Key:
343: .  -pc_eisenstat_omega <omega> - Sets omega

345:    Notes:
346:    The Eisenstat trick implementation of SSOR requires about 50% of the
347:    usual amount of floating point operations used for SSOR + Krylov method;
348:    however, the preconditioned problem must be solved with both left
349:    and right preconditioning.

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

355:    Level: intermediate

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

359: .seealso: PCSORGetOmega(), PCEisenstatSetOmega()
360: @*/
361: PetscErrorCode  PCEisenstatGetOmega(PC pc,PetscReal *omega)
362: {

367:   PetscUseMethod(pc,"PCEisenstatGetOmega_C",(PC,PetscReal*),(pc,omega));
368:   return(0);
369: }

373: /*@
374:    PCEisenstatGetNoDiagonalScaling - Tells if the Eisenstat preconditioner
375:    not to do additional diagonal preconditioning. For matrices with a constant
376:    along the diagonal, this may save a small amount of work.

378:    Logically Collective on PC

380:    Input Parameter:
381: .  pc - the preconditioner context

383:    Output Parameter:
384: .  flg - PETSC_TRUE means there is no diagonal scaling applied

386:    Options Database Key:
387: .  -pc_eisenstat_no_diagonal_scaling - Activates PCEisenstatSetNoDiagonalScaling()

389:    Level: intermediate

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

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

397: .seealso: PCEisenstatGetOmega()
398: @*/
399: PetscErrorCode  PCEisenstatGetNoDiagonalScaling(PC pc,PetscBool *flg)
400: {

405:   PetscUseMethod(pc,"PCEisenstatGetNoDiagonalScaling_C",(PC,PetscBool*),(pc,flg));
406:   return(0);
407: }

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

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

415:    Options Database Keys:
416: +  -pc_eisenstat_omega <omega> - Sets omega
417: -  -pc_eisenstat_no_diagonal_scaling - Activates PCEisenstatSetNoDiagonalScaling()

419:    Level: beginner

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

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

427: .seealso:  PCCreate(), PCSetType(), PCType (for list of available types), PC,
428:            PCEisenstatSetNoDiagonalScaling(), PCEisenstatSetOmega(), PCSOR
429: M*/

433: PETSC_EXTERN PetscErrorCode PCCreate_Eisenstat(PC pc)
434: {
436:   PC_Eisenstat   *eis;

439:   PetscNewLog(pc,&eis);

441:   pc->ops->apply           = PCApply_Eisenstat;
442:   pc->ops->presolve        = PCPreSolve_Eisenstat;
443:   pc->ops->postsolve       = PCPostSolve_Eisenstat;
444:   pc->ops->applyrichardson = 0;
445:   pc->ops->setfromoptions  = PCSetFromOptions_Eisenstat;
446:   pc->ops->destroy         = PCDestroy_Eisenstat;
447:   pc->ops->reset           = PCReset_Eisenstat;
448:   pc->ops->view            = PCView_Eisenstat;
449:   pc->ops->setup           = PCSetUp_Eisenstat;

451:   pc->data     = (void*)eis;
452:   eis->omega   = 1.0;
453:   eis->b[0]    = 0;
454:   eis->b[1]    = 0;
455:   eis->diag    = 0;
456:   eis->usediag = PETSC_TRUE;

458:   PetscObjectComposeFunction((PetscObject)pc,"PCEisenstatSetOmega_C",PCEisenstatSetOmega_Eisenstat);
459:   PetscObjectComposeFunction((PetscObject)pc,"PCEisenstatSetNoDiagonalScaling_C",PCEisenstatSetNoDiagonalScaling_Eisenstat);
460:   PetscObjectComposeFunction((PetscObject)pc,"PCEisenstatGetOmega_C",PCEisenstatGetOmega_Eisenstat);
461:   PetscObjectComposeFunction((PetscObject)pc,"PCEisenstatGetNoDiagonalScaling_C",PCEisenstatGetNoDiagonalScaling_Eisenstat);
462:   return(0);
463: }