xref: /petsc/src/ksp/pc/impls/eisens/eisen.c (revision 5e71baeff2f3138f93cd4f5927dfd596eb8325cc)
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 static PetscErrorCode PCMult_Eisenstat(Mat mat,Vec b,Vec x)
19 {
20   PetscErrorCode ierr;
21   PC             pc;
22   PC_Eisenstat   *eis;
23 
24   PetscFunctionBegin;
25   ierr = MatShellGetContext(mat,(void**)&pc);CHKERRQ(ierr);
26   eis  = (PC_Eisenstat*)pc->data;
27   ierr = MatSOR(eis->A,b,eis->omega,SOR_EISENSTAT,0.0,1,1,x);CHKERRQ(ierr);
28   PetscFunctionReturn(0);
29 }
30 
31 static PetscErrorCode PCApply_Eisenstat(PC pc,Vec x,Vec y)
32 {
33   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
34   PetscErrorCode ierr;
35   PetscBool      hasop;
36 
37   PetscFunctionBegin;
38   if (eis->usediag) {
39     ierr = MatHasOperation(pc->pmat,MATOP_MULT_DIAGONAL_BLOCK,&hasop);CHKERRQ(ierr);
40     if (hasop) {
41       ierr = MatMultDiagonalBlock(pc->pmat,x,y);CHKERRQ(ierr);
42     } else {
43       ierr = VecPointwiseMult(y,x,eis->diag);CHKERRQ(ierr);
44     }
45   } else {ierr = VecCopy(x,y);CHKERRQ(ierr);}
46   PetscFunctionReturn(0);
47 }
48 
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;
53   PetscErrorCode ierr;
54 
55   PetscFunctionBegin;
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   }
62 
63   if (!eis->b[pc->presolvedone-1]) {
64     ierr = VecDuplicate(b,&eis->b[pc->presolvedone-1]);CHKERRQ(ierr);
65     ierr = PetscLogObjectParent((PetscObject)pc,(PetscObject)eis->b[pc->presolvedone-1]);CHKERRQ(ierr);
66   }
67 
68   /* if nonzero initial guess, modify x */
69   ierr = KSPGetInitialGuessNonzero(ksp,&nonzero);CHKERRQ(ierr);
70   if (nonzero) {
71     ierr = VecCopy(x,eis->b[pc->presolvedone-1]);CHKERRQ(ierr);
72     ierr = MatSOR(eis->A,eis->b[pc->presolvedone-1],eis->omega,SOR_APPLY_UPPER,0.0,1,1,x);CHKERRQ(ierr);
73   }
74 
75   /* save true b, other option is to swap pointers */
76   ierr = VecCopy(b,eis->b[pc->presolvedone-1]);CHKERRQ(ierr);
77 
78   /* modify b by (L + D/omega)^{-1} */
79   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);
80   PetscFunctionReturn(0);
81 }
82 
83 static PetscErrorCode PCPostSolve_Eisenstat(PC pc,KSP ksp,Vec b,Vec x)
84 {
85   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
86   PetscErrorCode ierr;
87 
88   PetscFunctionBegin;
89   /* get back true b */
90   ierr = VecCopy(eis->b[pc->presolvedone],b);CHKERRQ(ierr);
91 
92   /* modify x by (U + D/omega)^{-1} */
93   ierr = VecCopy(x,eis->b[pc->presolvedone]);CHKERRQ(ierr);
94   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);
95   if (!pc->presolvedone) pc->mat = eis->A;
96   PetscFunctionReturn(0);
97 }
98 
99 static PetscErrorCode PCReset_Eisenstat(PC pc)
100 {
101   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
102   PetscErrorCode ierr;
103 
104   PetscFunctionBegin;
105   ierr = VecDestroy(&eis->b[0]);CHKERRQ(ierr);
106   ierr = VecDestroy(&eis->b[1]);CHKERRQ(ierr);
107   ierr = MatDestroy(&eis->shell);CHKERRQ(ierr);
108   ierr = VecDestroy(&eis->diag);CHKERRQ(ierr);
109   PetscFunctionReturn(0);
110 }
111 
112 static PetscErrorCode PCDestroy_Eisenstat(PC pc)
113 {
114   PetscErrorCode ierr;
115 
116   PetscFunctionBegin;
117   ierr = PCReset_Eisenstat(pc);CHKERRQ(ierr);
118   ierr = PetscFree(pc->data);CHKERRQ(ierr);
119   PetscFunctionReturn(0);
120 }
121 
122 static PetscErrorCode PCSetFromOptions_Eisenstat(PetscOptionItems *PetscOptionsObject,PC pc)
123 {
124   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
125   PetscErrorCode ierr;
126   PetscBool      set,flg;
127 
128   PetscFunctionBegin;
129   ierr = PetscOptionsHead(PetscOptionsObject,"Eisenstat SSOR options");CHKERRQ(ierr);
130   ierr = PetscOptionsReal("-pc_eisenstat_omega","Relaxation factor 0 < omega < 2","PCEisenstatSetOmega",eis->omega,&eis->omega,NULL);CHKERRQ(ierr);
131   ierr = PetscOptionsBool("-pc_eisenstat_no_diagonal_scaling","Do not use standard diagonal scaling","PCEisenstatSetNoDiagonalScaling",eis->usediag ? PETSC_FALSE : PETSC_TRUE,&flg,&set);CHKERRQ(ierr);
132   if (set) {
133     ierr = PCEisenstatSetNoDiagonalScaling(pc,flg);CHKERRQ(ierr);
134   }
135   ierr = PetscOptionsTail();CHKERRQ(ierr);
136   PetscFunctionReturn(0);
137 }
138 
139 static PetscErrorCode PCView_Eisenstat(PC pc,PetscViewer viewer)
140 {
141   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
142   PetscErrorCode ierr;
143   PetscBool      iascii;
144 
145   PetscFunctionBegin;
146   ierr = PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);CHKERRQ(ierr);
147   if (iascii) {
148     ierr = PetscViewerASCIIPrintf(viewer,"  omega = %g\n",(double)eis->omega);CHKERRQ(ierr);
149     if (eis->usediag) {
150       ierr = PetscViewerASCIIPrintf(viewer,"  Using diagonal scaling (default)\n");CHKERRQ(ierr);
151     } else {
152       ierr = PetscViewerASCIIPrintf(viewer,"  Not using diagonal scaling\n");CHKERRQ(ierr);
153     }
154   }
155   PetscFunctionReturn(0);
156 }
157 
158 static PetscErrorCode PCSetUp_Eisenstat(PC pc)
159 {
160   PetscErrorCode ierr;
161   PetscInt       M,N,m,n;
162   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
163 
164   PetscFunctionBegin;
165   if (!pc->setupcalled) {
166     ierr = MatGetSize(pc->mat,&M,&N);CHKERRQ(ierr);
167     ierr = MatGetLocalSize(pc->mat,&m,&n);CHKERRQ(ierr);
168     ierr = MatCreate(PetscObjectComm((PetscObject)pc),&eis->shell);CHKERRQ(ierr);
169     ierr = MatSetSizes(eis->shell,m,n,M,N);CHKERRQ(ierr);
170     ierr = MatSetType(eis->shell,MATSHELL);CHKERRQ(ierr);
171     ierr = MatSetUp(eis->shell);CHKERRQ(ierr);
172     ierr = MatShellSetContext(eis->shell,(void*)pc);CHKERRQ(ierr);
173     ierr = PetscLogObjectParent((PetscObject)pc,(PetscObject)eis->shell);CHKERRQ(ierr);
174     ierr = MatShellSetOperation(eis->shell,MATOP_MULT,(void (*)(void))PCMult_Eisenstat);CHKERRQ(ierr);
175   }
176   if (!eis->usediag) PetscFunctionReturn(0);
177   if (!pc->setupcalled) {
178     ierr = MatCreateVecs(pc->pmat,&eis->diag,0);CHKERRQ(ierr);
179     ierr = PetscLogObjectParent((PetscObject)pc,(PetscObject)eis->diag);CHKERRQ(ierr);
180   }
181   ierr = MatGetDiagonal(pc->pmat,eis->diag);CHKERRQ(ierr);
182   PetscFunctionReturn(0);
183 }
184 
185 /* --------------------------------------------------------------------*/
186 
187 static PetscErrorCode  PCEisenstatSetOmega_Eisenstat(PC pc,PetscReal omega)
188 {
189   PC_Eisenstat *eis = (PC_Eisenstat*)pc->data;
190 
191   PetscFunctionBegin;
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   PetscFunctionReturn(0);
195 }
196 
197 static PetscErrorCode  PCEisenstatSetNoDiagonalScaling_Eisenstat(PC pc,PetscBool flg)
198 {
199   PC_Eisenstat *eis = (PC_Eisenstat*)pc->data;
200 
201   PetscFunctionBegin;
202   eis->usediag = flg;
203   PetscFunctionReturn(0);
204 }
205 
206 static PetscErrorCode  PCEisenstatGetOmega_Eisenstat(PC pc,PetscReal *omega)
207 {
208   PC_Eisenstat *eis = (PC_Eisenstat*)pc->data;
209 
210   PetscFunctionBegin;
211   *omega = eis->omega;
212   PetscFunctionReturn(0);
213 }
214 
215 static PetscErrorCode  PCEisenstatGetNoDiagonalScaling_Eisenstat(PC pc,PetscBool *flg)
216 {
217   PC_Eisenstat *eis = (PC_Eisenstat*)pc->data;
218 
219   PetscFunctionBegin;
220   *flg = eis->usediag;
221   PetscFunctionReturn(0);
222 }
223 
224 /*@
225    PCEisenstatSetOmega - Sets the SSOR relaxation coefficient, omega,
226    to use with Eisenstat's trick (where omega = 1.0 by default).
227 
228    Logically Collective on PC
229 
230    Input Parameters:
231 +  pc - the preconditioner context
232 -  omega - relaxation coefficient (0 < omega < 2)
233 
234    Options Database Key:
235 .  -pc_eisenstat_omega <omega> - Sets omega
236 
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.
242 
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
246 
247    Level: intermediate
248 
249 .keywords: PC, Eisenstat, set, SOR, SSOR, relaxation, omega
250 
251 .seealso: PCSORSetOmega()
252 @*/
253 PetscErrorCode  PCEisenstatSetOmega(PC pc,PetscReal omega)
254 {
255   PetscErrorCode ierr;
256 
257   PetscFunctionBegin;
258   PetscValidHeaderSpecific(pc,PC_CLASSID,1);
259   PetscValidLogicalCollectiveReal(pc,omega,2);
260   ierr = PetscTryMethod(pc,"PCEisenstatSetOmega_C",(PC,PetscReal),(pc,omega));CHKERRQ(ierr);
261   PetscFunctionReturn(0);
262 }
263 
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.
268 
269    Logically Collective on PC
270 
271    Input Parameters:
272 +  pc - the preconditioner context
273 -  flg - PETSC_TRUE turns off diagonal scaling inside the algorithm
274 
275    Options Database Key:
276 .  -pc_eisenstat_no_diagonal_scaling - Activates PCEisenstatSetNoDiagonalScaling()
277 
278    Level: intermediate
279 
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.
283 
284 .keywords: PC, Eisenstat, use, diagonal, scaling, SSOR
285 
286 .seealso: PCEisenstatSetOmega()
287 @*/
288 PetscErrorCode  PCEisenstatSetNoDiagonalScaling(PC pc,PetscBool flg)
289 {
290   PetscErrorCode ierr;
291 
292   PetscFunctionBegin;
293   PetscValidHeaderSpecific(pc,PC_CLASSID,1);
294   ierr = PetscTryMethod(pc,"PCEisenstatSetNoDiagonalScaling_C",(PC,PetscBool),(pc,flg));CHKERRQ(ierr);
295   PetscFunctionReturn(0);
296 }
297 
298 /*@
299    PCEisenstatGetOmega - Gets the SSOR relaxation coefficient, omega,
300    to use with Eisenstat's trick (where omega = 1.0 by default).
301 
302    Logically Collective on PC
303 
304    Input Parameter:
305 .  pc - the preconditioner context
306 
307    Output Parameter:
308 .  omega - relaxation coefficient (0 < omega < 2)
309 
310    Options Database Key:
311 .  -pc_eisenstat_omega <omega> - Sets omega
312 
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.
318 
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
322 
323    Level: intermediate
324 
325 .keywords: PC, Eisenstat, set, SOR, SSOR, relaxation, omega
326 
327 .seealso: PCSORGetOmega(), PCEisenstatSetOmega()
328 @*/
329 PetscErrorCode  PCEisenstatGetOmega(PC pc,PetscReal *omega)
330 {
331   PetscErrorCode ierr;
332 
333   PetscFunctionBegin;
334   PetscValidHeaderSpecific(pc,PC_CLASSID,1);
335   ierr = PetscUseMethod(pc,"PCEisenstatGetOmega_C",(PC,PetscReal*),(pc,omega));CHKERRQ(ierr);
336   PetscFunctionReturn(0);
337 }
338 
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.
343 
344    Logically Collective on PC
345 
346    Input Parameter:
347 .  pc - the preconditioner context
348 
349    Output Parameter:
350 .  flg - PETSC_TRUE means there is no diagonal scaling applied
351 
352    Options Database Key:
353 .  -pc_eisenstat_no_diagonal_scaling - Activates PCEisenstatSetNoDiagonalScaling()
354 
355    Level: intermediate
356 
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.
360 
361 .keywords: PC, Eisenstat, use, diagonal, scaling, SSOR
362 
363 .seealso: PCEisenstatGetOmega()
364 @*/
365 PetscErrorCode  PCEisenstatGetNoDiagonalScaling(PC pc,PetscBool *flg)
366 {
367   PetscErrorCode ierr;
368 
369   PetscFunctionBegin;
370   PetscValidHeaderSpecific(pc,PC_CLASSID,1);
371   ierr = PetscUseMethod(pc,"PCEisenstatGetNoDiagonalScaling_C",(PC,PetscBool*),(pc,flg));CHKERRQ(ierr);
372   PetscFunctionReturn(0);
373 }
374 
375 static PetscErrorCode PCPreSolveChangeRHS_Eisenstat(PC pc, PetscBool* change)
376 {
377   PetscFunctionBegin;
378   *change = PETSC_TRUE;
379   PetscFunctionReturn(0);
380 }
381 
382 /* --------------------------------------------------------------------*/
383 
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.
387 
388    Options Database Keys:
389 +  -pc_eisenstat_omega <omega> - Sets omega
390 -  -pc_eisenstat_no_diagonal_scaling - Activates PCEisenstatSetNoDiagonalScaling()
391 
392    Level: beginner
393 
394   Concepts: SOR, preconditioners, Gauss-Seidel, Eisenstat's trick
395 
396    Notes: Only implemented for the SeqAIJ matrix format.
397           Not a true parallel SOR, in parallel this implementation corresponds to block
398           Jacobi with SOR on each block.
399 
400 .seealso:  PCCreate(), PCSetType(), PCType (for list of available types), PC,
401            PCEisenstatSetNoDiagonalScaling(), PCEisenstatSetOmega(), PCSOR
402 M*/
403 
404 PETSC_EXTERN PetscErrorCode PCCreate_Eisenstat(PC pc)
405 {
406   PetscErrorCode ierr;
407   PC_Eisenstat   *eis;
408 
409   PetscFunctionBegin;
410   ierr = PetscNewLog(pc,&eis);CHKERRQ(ierr);
411 
412   pc->ops->apply           = PCApply_Eisenstat;
413   pc->ops->presolve        = PCPreSolve_Eisenstat;
414   pc->ops->postsolve       = PCPostSolve_Eisenstat;
415   pc->ops->applyrichardson = 0;
416   pc->ops->setfromoptions  = PCSetFromOptions_Eisenstat;
417   pc->ops->destroy         = PCDestroy_Eisenstat;
418   pc->ops->reset           = PCReset_Eisenstat;
419   pc->ops->view            = PCView_Eisenstat;
420   pc->ops->setup           = PCSetUp_Eisenstat;
421 
422   pc->data     = (void*)eis;
423   eis->omega   = 1.0;
424   eis->b[0]    = 0;
425   eis->b[1]    = 0;
426   eis->diag    = 0;
427   eis->usediag = PETSC_TRUE;
428 
429   ierr = PetscObjectComposeFunction((PetscObject)pc,"PCEisenstatSetOmega_C",PCEisenstatSetOmega_Eisenstat);CHKERRQ(ierr);
430   ierr = PetscObjectComposeFunction((PetscObject)pc,"PCEisenstatSetNoDiagonalScaling_C",PCEisenstatSetNoDiagonalScaling_Eisenstat);CHKERRQ(ierr);
431   ierr = PetscObjectComposeFunction((PetscObject)pc,"PCEisenstatGetOmega_C",PCEisenstatGetOmega_Eisenstat);CHKERRQ(ierr);
432   ierr = PetscObjectComposeFunction((PetscObject)pc,"PCEisenstatGetNoDiagonalScaling_C",PCEisenstatGetNoDiagonalScaling_Eisenstat);CHKERRQ(ierr);
433   ierr = PetscObjectComposeFunction((PetscObject)pc,"PCPreSolveChangeRHS_C",PCPreSolveChangeRHS_Eisenstat);CHKERRQ(ierr);
434   PetscFunctionReturn(0);
435 }
436