xref: /petsc/src/ksp/pc/impls/eisens/eisen.c (revision 7b6bb2c608b6fc6714ef38fda02c2dbb91c82665)
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 <private/pcimpl.h>           /*I "petscpc.h" I*/
9 
10 typedef struct {
11   Mat        shell,A;
12   Vec        b,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->mat != pc->pmat) SETERRQ(((PetscObject)pc)->comm,PETSC_ERR_SUP,"Cannot have different mat and pmat");
63 
64   /* swap shell matrix and true matrix */
65   eis->A    = pc->mat;
66   pc->mat   = eis->shell;
67 
68   if (!eis->b) {
69     ierr = VecDuplicate(b,&eis->b);CHKERRQ(ierr);
70     ierr = PetscLogObjectParent(pc,eis->b);CHKERRQ(ierr);
71   }
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);CHKERRQ(ierr);
78     ierr = MatSOR(eis->A,eis->b,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);CHKERRQ(ierr);
83 
84   /* modify b by (L + D/omega)^{-1} */
85   ierr =   MatSOR(eis->A,eis->b,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,b);CHKERRQ(ierr);
99 
100   /* modify x by (U + D/omega)^{-1} */
101   ierr = VecCopy(x,eis->b);CHKERRQ(ierr);
102   ierr = MatSOR(eis->A,eis->b,eis->omega,(MatSORType)(SOR_ZERO_INITIAL_GUESS | SOR_LOCAL_BACKWARD_SWEEP),0.0,1,1,x);CHKERRQ(ierr);
103   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);CHKERRQ(ierr);
116   ierr = MatDestroy(&eis->shell);CHKERRQ(ierr);
117   ierr = VecDestroy(&eis->diag);CHKERRQ(ierr);
118   PetscFunctionReturn(0);
119 }
120 
121 #undef __FUNCT__
122 #define __FUNCT__ "PCDestroy_Eisenstat"
123 static PetscErrorCode PCDestroy_Eisenstat(PC pc)
124 {
125   PetscErrorCode ierr;
126 
127   PetscFunctionBegin;
128   ierr = PCReset_Eisenstat(pc);CHKERRQ(ierr);
129   ierr = PetscFree(pc->data);CHKERRQ(ierr);
130   PetscFunctionReturn(0);
131 }
132 
133 #undef __FUNCT__
134 #define __FUNCT__ "PCSetFromOptions_Eisenstat"
135 static PetscErrorCode PCSetFromOptions_Eisenstat(PC pc)
136 {
137   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
138   PetscErrorCode ierr;
139   PetscBool      flg = PETSC_FALSE;
140 
141   PetscFunctionBegin;
142   ierr = PetscOptionsHead("Eisenstat SSOR options");CHKERRQ(ierr);
143     ierr = PetscOptionsReal("-pc_eisenstat_omega","Relaxation factor 0 < omega < 2","PCEisenstatSetOmega",eis->omega,&eis->omega,PETSC_NULL);CHKERRQ(ierr);
144     ierr = PetscOptionsBool("-pc_eisenstat_no_diagonal_scaling","Do not use standard diagonal scaling","PCEisenstatNoDiagonalScaling",flg,&flg,PETSC_NULL);CHKERRQ(ierr);
145     if (flg) {
146       ierr = PCEisenstatNoDiagonalScaling(pc);CHKERRQ(ierr);
147     }
148   ierr = PetscOptionsTail();CHKERRQ(ierr);
149   PetscFunctionReturn(0);
150 }
151 
152 #undef __FUNCT__
153 #define __FUNCT__ "PCView_Eisenstat"
154 static PetscErrorCode PCView_Eisenstat(PC pc,PetscViewer viewer)
155 {
156   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
157   PetscErrorCode ierr;
158   PetscBool      iascii;
159 
160   PetscFunctionBegin;
161   ierr = PetscTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);CHKERRQ(ierr);
162   if (iascii) {
163     ierr = PetscViewerASCIIPrintf(viewer,"Eisenstat: omega = %G\n",eis->omega);CHKERRQ(ierr);
164     if (eis->usediag) {
165       ierr = PetscViewerASCIIPrintf(viewer,"Eisenstat: Using diagonal scaling (default)\n");CHKERRQ(ierr);
166     } else {
167       ierr = PetscViewerASCIIPrintf(viewer,"Eisenstat: Not using diagonal scaling\n");CHKERRQ(ierr);
168     }
169   } else {
170     SETERRQ1(((PetscObject)pc)->comm,PETSC_ERR_SUP,"Viewer type %s not supported for Eisenstat PC",((PetscObject)viewer)->type_name);
171   }
172   PetscFunctionReturn(0);
173 }
174 
175 #undef __FUNCT__
176 #define __FUNCT__ "PCSetUp_Eisenstat"
177 static PetscErrorCode PCSetUp_Eisenstat(PC pc)
178 {
179   PetscErrorCode ierr;
180   PetscInt       M,N,m,n;
181   PC_Eisenstat   *eis = (PC_Eisenstat*)pc->data;
182 
183   PetscFunctionBegin;
184   if (!pc->setupcalled) {
185     ierr = MatGetSize(pc->mat,&M,&N);CHKERRQ(ierr);
186     ierr = MatGetLocalSize(pc->mat,&m,&n);CHKERRQ(ierr);
187     ierr = MatCreate(((PetscObject)pc)->comm,&eis->shell);CHKERRQ(ierr);
188     ierr = MatSetSizes(eis->shell,m,n,M,N);CHKERRQ(ierr);
189     ierr = MatSetType(eis->shell,MATSHELL);CHKERRQ(ierr);
190     ierr = MatShellSetContext(eis->shell,(void*)pc);CHKERRQ(ierr);
191     ierr = PetscLogObjectParent(pc,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(pc,eis->diag);CHKERRQ(ierr);
198   }
199   ierr = MatGetDiagonal(pc->pmat,eis->diag);CHKERRQ(ierr);
200   PetscFunctionReturn(0);
201 }
202 
203 /* --------------------------------------------------------------------*/
204 
205 EXTERN_C_BEGIN
206 #undef __FUNCT__
207 #define __FUNCT__ "PCEisenstatSetOmega_Eisenstat"
208 PetscErrorCode  PCEisenstatSetOmega_Eisenstat(PC pc,PetscReal omega)
209 {
210   PC_Eisenstat  *eis;
211 
212   PetscFunctionBegin;
213   if (omega >= 2.0 || omega <= 0.0) SETERRQ(((PetscObject)pc)->comm,PETSC_ERR_ARG_OUTOFRANGE,"Relaxation out of range");
214   eis = (PC_Eisenstat*)pc->data;
215   eis->omega = omega;
216   PetscFunctionReturn(0);
217 }
218 EXTERN_C_END
219 
220 EXTERN_C_BEGIN
221 #undef __FUNCT__
222 #define __FUNCT__ "PCEisenstatNoDiagonalScaling_Eisenstat"
223 PetscErrorCode  PCEisenstatNoDiagonalScaling_Eisenstat(PC pc)
224 {
225   PC_Eisenstat *eis;
226 
227   PetscFunctionBegin;
228   eis = (PC_Eisenstat*)pc->data;
229   eis->usediag = PETSC_FALSE;
230   PetscFunctionReturn(0);
231 }
232 EXTERN_C_END
233 
234 #undef __FUNCT__
235 #define __FUNCT__ "PCEisenstatSetOmega"
236 /*@
237    PCEisenstatSetOmega - Sets the SSOR relaxation coefficient, omega,
238    to use with Eisenstat's trick (where omega = 1.0 by default).
239 
240    Logically Collective on PC
241 
242    Input Parameters:
243 +  pc - the preconditioner context
244 -  omega - relaxation coefficient (0 < omega < 2)
245 
246    Options Database Key:
247 .  -pc_eisenstat_omega <omega> - Sets omega
248 
249    Notes:
250    The Eisenstat trick implementation of SSOR requires about 50% of the
251    usual amount of floating point operations used for SSOR + Krylov method;
252    however, the preconditioned problem must be solved with both left
253    and right preconditioning.
254 
255    To use SSOR without the Eisenstat trick, employ the PCSOR preconditioner,
256    which can be chosen with the database options
257 $    -pc_type  sor  -pc_sor_symmetric
258 
259    Level: intermediate
260 
261 .keywords: PC, Eisenstat, set, SOR, SSOR, relaxation, omega
262 
263 .seealso: PCSORSetOmega()
264 @*/
265 PetscErrorCode  PCEisenstatSetOmega(PC pc,PetscReal omega)
266 {
267   PetscErrorCode ierr;
268 
269   PetscFunctionBegin;
270   PetscValidHeaderSpecific(pc,PC_CLASSID,1);
271   PetscValidLogicalCollectiveReal(pc,omega,2);
272   ierr = PetscTryMethod(pc,"PCEisenstatSetOmega_C",(PC,PetscReal),(pc,omega));CHKERRQ(ierr);
273   PetscFunctionReturn(0);
274 }
275 
276 #undef __FUNCT__
277 #define __FUNCT__ "PCEisenstatNoDiagonalScaling"
278 /*@
279    PCEisenstatNoDiagonalScaling - Causes the Eisenstat preconditioner
280    not to do additional diagonal preconditioning. For matrices with a constant
281    along the diagonal, this may save a small amount of work.
282 
283    Logically Collective on PC
284 
285    Input Parameter:
286 .  pc - the preconditioner context
287 
288    Options Database Key:
289 .  -pc_eisenstat_no_diagonal_scaling - Activates PCEisenstatNoDiagonalScaling()
290 
291    Level: intermediate
292 
293    Note:
294      If you use the KPSSetDiagonalScaling() or -ksp_diagonal_scale option then you will
295    likley want to use this routine since it will save you some unneeded flops.
296 
297 .keywords: PC, Eisenstat, use, diagonal, scaling, SSOR
298 
299 .seealso: PCEisenstatSetOmega()
300 @*/
301 PetscErrorCode  PCEisenstatNoDiagonalScaling(PC pc)
302 {
303   PetscErrorCode ierr;
304 
305   PetscFunctionBegin;
306   PetscValidHeaderSpecific(pc,PC_CLASSID,1);
307   ierr = PetscTryMethod(pc,"PCEisenstatNoDiagonalScaling_C",(PC),(pc));CHKERRQ(ierr);
308   PetscFunctionReturn(0);
309 }
310 
311 /* --------------------------------------------------------------------*/
312 
313 /*MC
314      PCEISENSTAT - An implementation of SSOR (symmetric successive over relaxation, symmetric Gauss-Seidel)
315            preconditioning that incorporates Eisenstat's trick to reduce the amount of computation needed.
316 
317    Options Database Keys:
318 +  -pc_eisenstat_omega <omega> - Sets omega
319 -  -pc_eisenstat_no_diagonal_scaling - Activates PCEisenstatNoDiagonalScaling()
320 
321    Level: beginner
322 
323   Concepts: SOR, preconditioners, Gauss-Seidel, Eisenstat's trick
324 
325    Notes: Only implemented for the SeqAIJ matrix format.
326           Not a true parallel SOR, in parallel this implementation corresponds to block
327           Jacobi with SOR on each block.
328 
329 .seealso:  PCCreate(), PCSetType(), PCType (for list of available types), PC,
330            PCEisenstatNoDiagonalScaling(), PCEisenstatSetOmega(), PCSOR
331 M*/
332 
333 EXTERN_C_BEGIN
334 #undef __FUNCT__
335 #define __FUNCT__ "PCCreate_Eisenstat"
336 PetscErrorCode  PCCreate_Eisenstat(PC pc)
337 {
338   PetscErrorCode ierr;
339   PC_Eisenstat   *eis;
340 
341   PetscFunctionBegin;
342   ierr = PetscNewLog(pc,PC_Eisenstat,&eis);CHKERRQ(ierr);
343 
344   pc->ops->apply           = PCApply_Eisenstat;
345   pc->ops->presolve        = PCPreSolve_Eisenstat;
346   pc->ops->postsolve       = PCPostSolve_Eisenstat;
347   pc->ops->applyrichardson = 0;
348   pc->ops->setfromoptions  = PCSetFromOptions_Eisenstat;
349   pc->ops->destroy         = PCDestroy_Eisenstat;
350   pc->ops->reset           = PCReset_Eisenstat;
351   pc->ops->view            = PCView_Eisenstat;
352   pc->ops->setup           = PCSetUp_Eisenstat;
353 
354   pc->data           = (void*)eis;
355   eis->omega         = 1.0;
356   eis->b             = 0;
357   eis->diag          = 0;
358   eis->usediag       = PETSC_TRUE;
359 
360   ierr = PetscObjectComposeFunctionDynamic((PetscObject)pc,"PCEisenstatSetOmega_C","PCEisenstatSetOmega_Eisenstat",
361                     PCEisenstatSetOmega_Eisenstat);CHKERRQ(ierr);
362   ierr = PetscObjectComposeFunctionDynamic((PetscObject)pc,"PCEisenstatNoDiagonalScaling_C",
363                     "PCEisenstatNoDiagonalScaling_Eisenstat",
364                     PCEisenstatNoDiagonalScaling_Eisenstat);CHKERRQ(ierr);
365  PetscFunctionReturn(0);
366 }
367 EXTERN_C_END
368