Actual source code: eisen.c

petsc-3.11.4 2019-09-28
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>

 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;


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

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

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

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

 49: static PetscErrorCode PCPreSolve_Eisenstat(PC pc,KSP ksp,Vec b,Vec x)
 50: {
 51:   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
 52:   PetscBool      nonzero;

 56:   if (pc->presolvedone < 2) {
 57:     if (pc->mat != pc->pmat) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_SUP,"Cannot have different mat and pmat");
 58:     /* swap shell matrix and true matrix */
 59:     eis->A  = pc->mat;
 60:     pc->mat = eis->shell;
 61:   }

 63:   if (!eis->b[pc->presolvedone-1]) {
 64:     VecDuplicate(b,&eis->b[pc->presolvedone-1]);
 65:     PetscLogObjectParent((PetscObject)pc,(PetscObject)eis->b[pc->presolvedone-1]);
 66:   }

 68:   /* if nonzero initial guess, modify x */
 69:   KSPGetInitialGuessNonzero(ksp,&nonzero);
 70:   if (nonzero) {
 71:     VecCopy(x,eis->b[pc->presolvedone-1]);
 72:     MatSOR(eis->A,eis->b[pc->presolvedone-1],eis->omega,SOR_APPLY_UPPER,0.0,1,1,x);
 73:   }

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

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

 83: static PetscErrorCode PCPostSolve_Eisenstat(PC pc,KSP ksp,Vec b,Vec x)
 84: {
 85:   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;

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

 92:   /* modify x by (U + D/omega)^{-1} */
 93:   VecCopy(x,eis->b[pc->presolvedone]);
 94:   MatSOR(eis->A,eis->b[pc->presolvedone],eis->omega,(MatSORType)(SOR_ZERO_INITIAL_GUESS | SOR_LOCAL_BACKWARD_SWEEP),0.0,1,1,x);
 95:   if (!pc->presolvedone) pc->mat = eis->A;
 96:   return(0);
 97: }

 99: static PetscErrorCode PCReset_Eisenstat(PC pc)
100: {
101:   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;

105:   VecDestroy(&eis->b[0]);
106:   VecDestroy(&eis->b[1]);
107:   MatDestroy(&eis->shell);
108:   VecDestroy(&eis->diag);
109:   return(0);
110: }

112: static PetscErrorCode PCDestroy_Eisenstat(PC pc)
113: {

117:   PCReset_Eisenstat(pc);
118:   PetscFree(pc->data);
119:   return(0);
120: }

122: static PetscErrorCode PCSetFromOptions_Eisenstat(PetscOptionItems *PetscOptionsObject,PC pc)
123: {
124:   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
126:   PetscBool      set,flg;

129:   PetscOptionsHead(PetscOptionsObject,"Eisenstat SSOR options");
130:   PetscOptionsReal("-pc_eisenstat_omega","Relaxation factor 0 < omega < 2","PCEisenstatSetOmega",eis->omega,&eis->omega,NULL);
131:   PetscOptionsBool("-pc_eisenstat_no_diagonal_scaling","Do not use standard diagonal scaling","PCEisenstatSetNoDiagonalScaling",eis->usediag ? PETSC_FALSE : PETSC_TRUE,&flg,&set);
132:   if (set) {
133:     PCEisenstatSetNoDiagonalScaling(pc,flg);
134:   }
135:   PetscOptionsTail();
136:   return(0);
137: }

139: static PetscErrorCode PCView_Eisenstat(PC pc,PetscViewer viewer)
140: {
141:   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
143:   PetscBool      iascii;

146:   PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);
147:   if (iascii) {
148:     PetscViewerASCIIPrintf(viewer,"  omega = %g\n",(double)eis->omega);
149:     if (eis->usediag) {
150:       PetscViewerASCIIPrintf(viewer,"  Using diagonal scaling (default)\n");
151:     } else {
152:       PetscViewerASCIIPrintf(viewer,"  Not using diagonal scaling\n");
153:     }
154:   }
155:   return(0);
156: }

158: static PetscErrorCode PCSetUp_Eisenstat(PC pc)
159: {
161:   PetscInt       M,N,m,n;
162:   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;

165:   if (!pc->setupcalled) {
166:     MatGetSize(pc->mat,&M,&N);
167:     MatGetLocalSize(pc->mat,&m,&n);
168:     MatCreate(PetscObjectComm((PetscObject)pc),&eis->shell);
169:     MatSetSizes(eis->shell,m,n,M,N);
170:     MatSetType(eis->shell,MATSHELL);
171:     MatSetUp(eis->shell);
172:     MatShellSetContext(eis->shell,(void*)pc);
173:     PetscLogObjectParent((PetscObject)pc,(PetscObject)eis->shell);
174:     MatShellSetOperation(eis->shell,MATOP_MULT,(void (*)(void))PCMult_Eisenstat);
175:   }
176:   if (!eis->usediag) return(0);
177:   if (!pc->setupcalled) {
178:     MatCreateVecs(pc->pmat,&eis->diag,0);
179:     PetscLogObjectParent((PetscObject)pc,(PetscObject)eis->diag);
180:   }
181:   MatGetDiagonal(pc->pmat,eis->diag);
182:   return(0);
183: }

185: /* --------------------------------------------------------------------*/

187: static PetscErrorCode  PCEisenstatSetOmega_Eisenstat(PC pc,PetscReal omega)
188: {
189:   PC_Eisenstat *eis = (PC_Eisenstat*)pc->data;

192:   if (omega >= 2.0 || omega <= 0.0) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_OUTOFRANGE,"Relaxation out of range");
193:   eis->omega = omega;
194:   return(0);
195: }

197: static PetscErrorCode  PCEisenstatSetNoDiagonalScaling_Eisenstat(PC pc,PetscBool flg)
198: {
199:   PC_Eisenstat *eis = (PC_Eisenstat*)pc->data;

202:   eis->usediag = flg;
203:   return(0);
204: }

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

211:   *omega = eis->omega;
212:   return(0);
213: }

215: static PetscErrorCode  PCEisenstatGetNoDiagonalScaling_Eisenstat(PC pc,PetscBool *flg)
216: {
217:   PC_Eisenstat *eis = (PC_Eisenstat*)pc->data;

220:   *flg = eis->usediag;
221:   return(0);
222: }

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

228:    Logically Collective on PC

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

234:    Options Database Key:
235: .  -pc_eisenstat_omega <omega> - Sets omega

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

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

247:    Level: intermediate

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

251: .seealso: PCSORSetOmega()
252: @*/
253: PetscErrorCode  PCEisenstatSetOmega(PC pc,PetscReal omega)
254: {

260:   PetscTryMethod(pc,"PCEisenstatSetOmega_C",(PC,PetscReal),(pc,omega));
261:   return(0);
262: }

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

269:    Logically Collective on PC

271:    Input Parameters:
272: +  pc - the preconditioner context
273: -  flg - PETSC_TRUE turns off diagonal scaling inside the algorithm  

275:    Options Database Key:
276: .  -pc_eisenstat_no_diagonal_scaling - Activates PCEisenstatSetNoDiagonalScaling()

278:    Level: intermediate

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

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

286: .seealso: PCEisenstatSetOmega()
287: @*/
288: PetscErrorCode  PCEisenstatSetNoDiagonalScaling(PC pc,PetscBool flg)
289: {

294:   PetscTryMethod(pc,"PCEisenstatSetNoDiagonalScaling_C",(PC,PetscBool),(pc,flg));
295:   return(0);
296: }

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

302:    Logically Collective on PC

304:    Input Parameter:
305: .  pc - the preconditioner context

307:    Output Parameter:
308: .  omega - relaxation coefficient (0 < omega < 2)

310:    Options Database Key:
311: .  -pc_eisenstat_omega <omega> - Sets omega

313:    Notes:
314:    The Eisenstat trick implementation of SSOR requires about 50% of the
315:    usual amount of floating point operations used for SSOR + Krylov method;
316:    however, the preconditioned problem must be solved with both left
317:    and right preconditioning.

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

323:    Level: intermediate

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

327: .seealso: PCSORGetOmega(), PCEisenstatSetOmega()
328: @*/
329: PetscErrorCode  PCEisenstatGetOmega(PC pc,PetscReal *omega)
330: {

335:   PetscUseMethod(pc,"PCEisenstatGetOmega_C",(PC,PetscReal*),(pc,omega));
336:   return(0);
337: }

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

344:    Logically Collective on PC

346:    Input Parameter:
347: .  pc - the preconditioner context

349:    Output Parameter:
350: .  flg - PETSC_TRUE means there is no diagonal scaling applied

352:    Options Database Key:
353: .  -pc_eisenstat_no_diagonal_scaling - Activates PCEisenstatSetNoDiagonalScaling()

355:    Level: intermediate

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

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

363: .seealso: PCEisenstatGetOmega()
364: @*/
365: PetscErrorCode  PCEisenstatGetNoDiagonalScaling(PC pc,PetscBool *flg)
366: {

371:   PetscUseMethod(pc,"PCEisenstatGetNoDiagonalScaling_C",(PC,PetscBool*),(pc,flg));
372:   return(0);
373: }

375: static PetscErrorCode PCPreSolveChangeRHS_Eisenstat(PC pc, PetscBool* change)
376: {
378:   *change = PETSC_TRUE;
379:   return(0);
380: }

382: /* --------------------------------------------------------------------*/

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

388:    Options Database Keys:
389: +  -pc_eisenstat_omega <omega> - Sets omega
390: -  -pc_eisenstat_no_diagonal_scaling - Activates PCEisenstatSetNoDiagonalScaling()

392:    Level: beginner

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

396:    Notes:
397:     Only implemented for the SeqAIJ matrix format.
398:           Not a true parallel SOR, in parallel this implementation corresponds to block
399:           Jacobi with SOR on each block.

401: .seealso:  PCCreate(), PCSetType(), PCType (for list of available types), PC,
402:            PCEisenstatSetNoDiagonalScaling(), PCEisenstatSetOmega(), PCSOR
403: M*/

405: PETSC_EXTERN PetscErrorCode PCCreate_Eisenstat(PC pc)
406: {
408:   PC_Eisenstat   *eis;

411:   PetscNewLog(pc,&eis);

413:   pc->ops->apply           = PCApply_Eisenstat;
414:   pc->ops->presolve        = PCPreSolve_Eisenstat;
415:   pc->ops->postsolve       = PCPostSolve_Eisenstat;
416:   pc->ops->applyrichardson = 0;
417:   pc->ops->setfromoptions  = PCSetFromOptions_Eisenstat;
418:   pc->ops->destroy         = PCDestroy_Eisenstat;
419:   pc->ops->reset           = PCReset_Eisenstat;
420:   pc->ops->view            = PCView_Eisenstat;
421:   pc->ops->setup           = PCSetUp_Eisenstat;

423:   pc->data     = (void*)eis;
424:   eis->omega   = 1.0;
425:   eis->b[0]    = 0;
426:   eis->b[1]    = 0;
427:   eis->diag    = 0;
428:   eis->usediag = PETSC_TRUE;

430:   PetscObjectComposeFunction((PetscObject)pc,"PCEisenstatSetOmega_C",PCEisenstatSetOmega_Eisenstat);
431:   PetscObjectComposeFunction((PetscObject)pc,"PCEisenstatSetNoDiagonalScaling_C",PCEisenstatSetNoDiagonalScaling_Eisenstat);
432:   PetscObjectComposeFunction((PetscObject)pc,"PCEisenstatGetOmega_C",PCEisenstatGetOmega_Eisenstat);
433:   PetscObjectComposeFunction((PetscObject)pc,"PCEisenstatGetNoDiagonalScaling_C",PCEisenstatGetNoDiagonalScaling_Eisenstat);
434:   PetscObjectComposeFunction((PetscObject)pc,"PCPreSolveChangeRHS_C",PCPreSolveChangeRHS_Eisenstat);
435:   return(0);
436: }