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