xref: /petsc/src/ksp/pc/impls/eisens/eisen.c (revision a3fa217bfefef3d54995bb82b2b5ea8a3c3e9019)
1 
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*/
9 
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;
16 
17 
18 #undef __FUNCT__
19 #define __FUNCT__ "PCMult_Eisenstat"
20 static PetscErrorCode PCMult_Eisenstat(Mat mat,Vec b,Vec x)
21 {
22   PetscErrorCode ierr;
23   PC             pc;
24   PC_Eisenstat   *eis;
25 
26   PetscFunctionBegin;
27   ierr = MatShellGetContext(mat,(void**)&pc);CHKERRQ(ierr);
28   eis  = (PC_Eisenstat*)pc->data;
29   ierr = MatSOR(eis->A,b,eis->omega,SOR_EISENSTAT,0.0,1,1,x);CHKERRQ(ierr);
30   PetscFunctionReturn(0);
31 }
32 
33 #undef __FUNCT__
34 #define __FUNCT__ "PCApply_Eisenstat"
35 static PetscErrorCode PCApply_Eisenstat(PC pc,Vec x,Vec y)
36 {
37   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
38   PetscErrorCode ierr;
39   PetscBool      hasop;
40 
41   PetscFunctionBegin;
42   if (eis->usediag) {
43     ierr = MatHasOperation(pc->pmat,MATOP_MULT_DIAGONAL_BLOCK,&hasop);CHKERRQ(ierr);
44     if (hasop) {
45       ierr = MatMultDiagonalBlock(pc->pmat,x,y);CHKERRQ(ierr);
46     } else {
47       ierr = VecPointwiseMult(y,x,eis->diag);CHKERRQ(ierr);
48     }
49   } else {ierr = VecCopy(x,y);CHKERRQ(ierr);}
50   PetscFunctionReturn(0);
51 }
52 
53 #undef __FUNCT__
54 #define __FUNCT__ "PCPreSolve_Eisenstat"
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;
59   PetscErrorCode ierr;
60 
61   PetscFunctionBegin;
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   }
68 
69   if (!eis->b[pc->presolvedone-1]) {
70     ierr = VecDuplicate(b,&eis->b[pc->presolvedone-1]);CHKERRQ(ierr);
71     ierr = PetscLogObjectParent((PetscObject)pc,(PetscObject)eis->b[pc->presolvedone-1]);CHKERRQ(ierr);
72   }
73 
74   /* if nonzero initial guess, modify x */
75   ierr = KSPGetInitialGuessNonzero(ksp,&nonzero);CHKERRQ(ierr);
76   if (nonzero) {
77     ierr = VecCopy(x,eis->b[pc->presolvedone-1]);CHKERRQ(ierr);
78     ierr = MatSOR(eis->A,eis->b[pc->presolvedone-1],eis->omega,SOR_APPLY_UPPER,0.0,1,1,x);CHKERRQ(ierr);
79   }
80 
81   /* save true b, other option is to swap pointers */
82   ierr = VecCopy(b,eis->b[pc->presolvedone-1]);CHKERRQ(ierr);
83 
84   /* modify b by (L + D/omega)^{-1} */
85   ierr =   MatSOR(eis->A,eis->b[pc->presolvedone-1],eis->omega,(MatSORType)(SOR_ZERO_INITIAL_GUESS | SOR_LOCAL_FORWARD_SWEEP),0.0,1,1,b);CHKERRQ(ierr);
86   PetscFunctionReturn(0);
87 }
88 
89 #undef __FUNCT__
90 #define __FUNCT__ "PCPostSolve_Eisenstat"
91 static PetscErrorCode PCPostSolve_Eisenstat(PC pc,KSP ksp,Vec b,Vec x)
92 {
93   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
94   PetscErrorCode ierr;
95 
96   PetscFunctionBegin;
97   /* get back true b */
98   ierr = VecCopy(eis->b[pc->presolvedone],b);CHKERRQ(ierr);
99 
100   /* modify x by (U + D/omega)^{-1} */
101   ierr = VecCopy(x,eis->b[pc->presolvedone]);CHKERRQ(ierr);
102   ierr = MatSOR(eis->A,eis->b[pc->presolvedone],eis->omega,(MatSORType)(SOR_ZERO_INITIAL_GUESS | SOR_LOCAL_BACKWARD_SWEEP),0.0,1,1,x);CHKERRQ(ierr);
103   if (!pc->presolvedone) pc->mat = eis->A;
104   PetscFunctionReturn(0);
105 }
106 
107 #undef __FUNCT__
108 #define __FUNCT__ "PCReset_Eisenstat"
109 static PetscErrorCode PCReset_Eisenstat(PC pc)
110 {
111   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
112   PetscErrorCode ierr;
113 
114   PetscFunctionBegin;
115   ierr = VecDestroy(&eis->b[0]);CHKERRQ(ierr);
116   ierr = VecDestroy(&eis->b[1]);CHKERRQ(ierr);
117   ierr = MatDestroy(&eis->shell);CHKERRQ(ierr);
118   ierr = VecDestroy(&eis->diag);CHKERRQ(ierr);
119   PetscFunctionReturn(0);
120 }
121 
122 #undef __FUNCT__
123 #define __FUNCT__ "PCDestroy_Eisenstat"
124 static PetscErrorCode PCDestroy_Eisenstat(PC pc)
125 {
126   PetscErrorCode ierr;
127 
128   PetscFunctionBegin;
129   ierr = PCReset_Eisenstat(pc);CHKERRQ(ierr);
130   ierr = PetscFree(pc->data);CHKERRQ(ierr);
131   PetscFunctionReturn(0);
132 }
133 
134 #undef __FUNCT__
135 #define __FUNCT__ "PCSetFromOptions_Eisenstat"
136 static PetscErrorCode PCSetFromOptions_Eisenstat(PC pc)
137 {
138   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
139   PetscErrorCode ierr;
140   PetscBool      flg = PETSC_FALSE;
141 
142   PetscFunctionBegin;
143   ierr = PetscOptionsHead("Eisenstat SSOR options");CHKERRQ(ierr);
144   ierr = PetscOptionsReal("-pc_eisenstat_omega","Relaxation factor 0 < omega < 2","PCEisenstatSetOmega",eis->omega,&eis->omega,NULL);CHKERRQ(ierr);
145   ierr = PetscOptionsBool("-pc_eisenstat_no_diagonal_scaling","Do not use standard diagonal scaling","PCEisenstatNoDiagonalScaling",flg,&flg,NULL);CHKERRQ(ierr);
146   if (flg) {
147     ierr = PCEisenstatNoDiagonalScaling(pc);CHKERRQ(ierr);
148   }
149   ierr = PetscOptionsTail();CHKERRQ(ierr);
150   PetscFunctionReturn(0);
151 }
152 
153 #undef __FUNCT__
154 #define __FUNCT__ "PCView_Eisenstat"
155 static PetscErrorCode PCView_Eisenstat(PC pc,PetscViewer viewer)
156 {
157   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
158   PetscErrorCode ierr;
159   PetscBool      iascii;
160 
161   PetscFunctionBegin;
162   ierr = PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);CHKERRQ(ierr);
163   if (iascii) {
164     ierr = PetscViewerASCIIPrintf(viewer,"Eisenstat: omega = %g\n",(double)eis->omega);CHKERRQ(ierr);
165     if (eis->usediag) {
166       ierr = PetscViewerASCIIPrintf(viewer,"Eisenstat: Using diagonal scaling (default)\n");CHKERRQ(ierr);
167     } else {
168       ierr = PetscViewerASCIIPrintf(viewer,"Eisenstat: Not using diagonal scaling\n");CHKERRQ(ierr);
169     }
170   }
171   PetscFunctionReturn(0);
172 }
173 
174 #undef __FUNCT__
175 #define __FUNCT__ "PCSetUp_Eisenstat"
176 static PetscErrorCode PCSetUp_Eisenstat(PC pc)
177 {
178   PetscErrorCode ierr;
179   PetscInt       M,N,m,n;
180   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
181 
182   PetscFunctionBegin;
183   if (!pc->setupcalled) {
184     ierr = MatGetSize(pc->mat,&M,&N);CHKERRQ(ierr);
185     ierr = MatGetLocalSize(pc->mat,&m,&n);CHKERRQ(ierr);
186     ierr = MatCreate(PetscObjectComm((PetscObject)pc),&eis->shell);CHKERRQ(ierr);
187     ierr = MatSetSizes(eis->shell,m,n,M,N);CHKERRQ(ierr);
188     ierr = MatSetType(eis->shell,MATSHELL);CHKERRQ(ierr);
189     ierr = MatSetUp(eis->shell);CHKERRQ(ierr);
190     ierr = MatShellSetContext(eis->shell,(void*)pc);CHKERRQ(ierr);
191     ierr = PetscLogObjectParent((PetscObject)pc,(PetscObject)eis->shell);CHKERRQ(ierr);
192     ierr = MatShellSetOperation(eis->shell,MATOP_MULT,(void (*)(void))PCMult_Eisenstat);CHKERRQ(ierr);
193   }
194   if (!eis->usediag) PetscFunctionReturn(0);
195   if (!pc->setupcalled) {
196     ierr = MatGetVecs(pc->pmat,&eis->diag,0);CHKERRQ(ierr);
197     ierr = PetscLogObjectParent((PetscObject)pc,(PetscObject)eis->diag);CHKERRQ(ierr);
198   }
199   ierr = MatGetDiagonal(pc->pmat,eis->diag);CHKERRQ(ierr);
200   PetscFunctionReturn(0);
201 }
202 
203 /* --------------------------------------------------------------------*/
204 
205 #undef __FUNCT__
206 #define __FUNCT__ "PCEisenstatSetOmega_Eisenstat"
207 static PetscErrorCode  PCEisenstatSetOmega_Eisenstat(PC pc,PetscReal omega)
208 {
209   PC_Eisenstat *eis;
210 
211   PetscFunctionBegin;
212   if (omega >= 2.0 || omega <= 0.0) SETERRQ(PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_OUTOFRANGE,"Relaxation out of range");
213   eis        = (PC_Eisenstat*)pc->data;
214   eis->omega = omega;
215   PetscFunctionReturn(0);
216 }
217 
218 #undef __FUNCT__
219 #define __FUNCT__ "PCEisenstatNoDiagonalScaling_Eisenstat"
220 static PetscErrorCode  PCEisenstatNoDiagonalScaling_Eisenstat(PC pc)
221 {
222   PC_Eisenstat *eis;
223 
224   PetscFunctionBegin;
225   eis          = (PC_Eisenstat*)pc->data;
226   eis->usediag = PETSC_FALSE;
227   PetscFunctionReturn(0);
228 }
229 
230 #undef __FUNCT__
231 #define __FUNCT__ "PCEisenstatSetOmega"
232 /*@
233    PCEisenstatSetOmega - Sets the SSOR relaxation coefficient, omega,
234    to use with Eisenstat's trick (where omega = 1.0 by default).
235 
236    Logically Collective on PC
237 
238    Input Parameters:
239 +  pc - the preconditioner context
240 -  omega - relaxation coefficient (0 < omega < 2)
241 
242    Options Database Key:
243 .  -pc_eisenstat_omega <omega> - Sets omega
244 
245    Notes:
246    The Eisenstat trick implementation of SSOR requires about 50% of the
247    usual amount of floating point operations used for SSOR + Krylov method;
248    however, the preconditioned problem must be solved with both left
249    and right preconditioning.
250 
251    To use SSOR without the Eisenstat trick, employ the PCSOR preconditioner,
252    which can be chosen with the database options
253 $    -pc_type  sor  -pc_sor_symmetric
254 
255    Level: intermediate
256 
257 .keywords: PC, Eisenstat, set, SOR, SSOR, relaxation, omega
258 
259 .seealso: PCSORSetOmega()
260 @*/
261 PetscErrorCode  PCEisenstatSetOmega(PC pc,PetscReal omega)
262 {
263   PetscErrorCode ierr;
264 
265   PetscFunctionBegin;
266   PetscValidHeaderSpecific(pc,PC_CLASSID,1);
267   PetscValidLogicalCollectiveReal(pc,omega,2);
268   ierr = PetscTryMethod(pc,"PCEisenstatSetOmega_C",(PC,PetscReal),(pc,omega));CHKERRQ(ierr);
269   PetscFunctionReturn(0);
270 }
271 
272 #undef __FUNCT__
273 #define __FUNCT__ "PCEisenstatNoDiagonalScaling"
274 /*@
275    PCEisenstatNoDiagonalScaling - Causes the Eisenstat preconditioner
276    not to do additional diagonal preconditioning. For matrices with a constant
277    along the diagonal, this may save a small amount of work.
278 
279    Logically Collective on PC
280 
281    Input Parameter:
282 .  pc - the preconditioner context
283 
284    Options Database Key:
285 .  -pc_eisenstat_no_diagonal_scaling - Activates PCEisenstatNoDiagonalScaling()
286 
287    Level: intermediate
288 
289    Note:
290      If you use the KPSSetDiagonalScaling() or -ksp_diagonal_scale option then you will
291    likley want to use this routine since it will save you some unneeded flops.
292 
293 .keywords: PC, Eisenstat, use, diagonal, scaling, SSOR
294 
295 .seealso: PCEisenstatSetOmega()
296 @*/
297 PetscErrorCode  PCEisenstatNoDiagonalScaling(PC pc)
298 {
299   PetscErrorCode ierr;
300 
301   PetscFunctionBegin;
302   PetscValidHeaderSpecific(pc,PC_CLASSID,1);
303   ierr = PetscTryMethod(pc,"PCEisenstatNoDiagonalScaling_C",(PC),(pc));CHKERRQ(ierr);
304   PetscFunctionReturn(0);
305 }
306 
307 /* --------------------------------------------------------------------*/
308 
309 /*MC
310      PCEISENSTAT - An implementation of SSOR (symmetric successive over relaxation, symmetric Gauss-Seidel)
311            preconditioning that incorporates Eisenstat's trick to reduce the amount of computation needed.
312 
313    Options Database Keys:
314 +  -pc_eisenstat_omega <omega> - Sets omega
315 -  -pc_eisenstat_no_diagonal_scaling - Activates PCEisenstatNoDiagonalScaling()
316 
317    Level: beginner
318 
319   Concepts: SOR, preconditioners, Gauss-Seidel, Eisenstat's trick
320 
321    Notes: Only implemented for the SeqAIJ matrix format.
322           Not a true parallel SOR, in parallel this implementation corresponds to block
323           Jacobi with SOR on each block.
324 
325 .seealso:  PCCreate(), PCSetType(), PCType (for list of available types), PC,
326            PCEisenstatNoDiagonalScaling(), PCEisenstatSetOmega(), PCSOR
327 M*/
328 
329 #undef __FUNCT__
330 #define __FUNCT__ "PCCreate_Eisenstat"
331 PETSC_EXTERN PetscErrorCode PCCreate_Eisenstat(PC pc)
332 {
333   PetscErrorCode ierr;
334   PC_Eisenstat   *eis;
335 
336   PetscFunctionBegin;
337   ierr = PetscNewLog(pc,&eis);CHKERRQ(ierr);
338 
339   pc->ops->apply           = PCApply_Eisenstat;
340   pc->ops->presolve        = PCPreSolve_Eisenstat;
341   pc->ops->postsolve       = PCPostSolve_Eisenstat;
342   pc->ops->applyrichardson = 0;
343   pc->ops->setfromoptions  = PCSetFromOptions_Eisenstat;
344   pc->ops->destroy         = PCDestroy_Eisenstat;
345   pc->ops->reset           = PCReset_Eisenstat;
346   pc->ops->view            = PCView_Eisenstat;
347   pc->ops->setup           = PCSetUp_Eisenstat;
348 
349   pc->data     = (void*)eis;
350   eis->omega   = 1.0;
351   eis->b[0]    = 0;
352   eis->b[1]    = 0;
353   eis->diag    = 0;
354   eis->usediag = PETSC_TRUE;
355 
356   ierr = PetscObjectComposeFunction((PetscObject)pc,"PCEisenstatSetOmega_C",PCEisenstatSetOmega_Eisenstat);CHKERRQ(ierr);
357   ierr = PetscObjectComposeFunction((PetscObject)pc,"PCEisenstatNoDiagonalScaling_C",PCEisenstatNoDiagonalScaling_Eisenstat);CHKERRQ(ierr);
358   PetscFunctionReturn(0);
359 }
360