Actual source code: eisen.c
petsc-3.7.7 2017-09-25
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: }