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: }