xref: /petsc/src/ksp/pc/impls/eisens/eisen.c (revision f89e2e87fbc2bf363b9a1b6d11cc8db8663cdbc0)
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   PetscTruth 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   PetscTruth     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   PetscTruth     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   PetscTruth     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 = PetscOptionsTruth("-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   PetscTruth     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 PETSCKSP_DLLEXPORT 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 PETSCKSP_DLLEXPORT 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    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 PETSCKSP_DLLEXPORT PCEisenstatSetOmega(PC pc,PetscReal omega)
256 {
257   PetscErrorCode ierr,(*f)(PC,PetscReal);
258 
259   PetscFunctionBegin;
260   PetscValidHeaderSpecific(pc,PC_CLASSID,1);
261   ierr = PetscObjectQueryFunction((PetscObject)pc,"PCEisenstatSetOmega_C",(void (**)(void))&f);CHKERRQ(ierr);
262   if (f) {
263     ierr = (*f)(pc,omega);CHKERRQ(ierr);
264   }
265   PetscFunctionReturn(0);
266 }
267 
268 #undef __FUNCT__
269 #define __FUNCT__ "PCEisenstatNoDiagonalScaling"
270 /*@
271    PCEisenstatNoDiagonalScaling - Causes the Eisenstat preconditioner
272    not to do additional diagonal preconditioning. For matrices with a constant
273    along the diagonal, this may save a small amount of work.
274 
275    Collective on PC
276 
277    Input Parameter:
278 .  pc - the preconditioner context
279 
280    Options Database Key:
281 .  -pc_eisenstat_no_diagonal_scaling - Activates PCEisenstatNoDiagonalScaling()
282 
283    Level: intermediate
284 
285    Note:
286      If you use the KPSSetDiagonalScaling() or -ksp_diagonal_scale option then you will
287    likley want to use this routine since it will save you some unneeded flops.
288 
289 .keywords: PC, Eisenstat, use, diagonal, scaling, SSOR
290 
291 .seealso: PCEisenstatSetOmega()
292 @*/
293 PetscErrorCode PETSCKSP_DLLEXPORT PCEisenstatNoDiagonalScaling(PC pc)
294 {
295   PetscErrorCode ierr,(*f)(PC);
296 
297   PetscFunctionBegin;
298   PetscValidHeaderSpecific(pc,PC_CLASSID,1);
299   ierr = PetscObjectQueryFunction((PetscObject)pc,"PCEisenstatNoDiagonalScaling_C",(void (**)(void))&f);CHKERRQ(ierr);
300   if (f) {
301     ierr = (*f)(pc);CHKERRQ(ierr);
302   }
303   PetscFunctionReturn(0);
304 }
305 
306 /* --------------------------------------------------------------------*/
307 
308 /*MC
309      PCEISENSTAT - An implementation of SSOR (symmetric successive over relaxation, symmetric Gauss-Seidel)
310            preconditioning that incorporates Eisenstat's trick to reduce the amount of computation needed.
311 
312    Options Database Keys:
313 +  -pc_eisenstat_omega <omega> - Sets omega
314 -  -pc_eisenstat_no_diagonal_scaling - Activates PCEisenstatNoDiagonalScaling()
315 
316    Level: beginner
317 
318   Concepts: SOR, preconditioners, Gauss-Seidel, Eisenstat's trick
319 
320    Notes: Only implemented for the SeqAIJ matrix format.
321           Not a true parallel SOR, in parallel this implementation corresponds to block
322           Jacobi with SOR on each block.
323 
324 .seealso:  PCCreate(), PCSetType(), PCType (for list of available types), PC,
325            PCEisenstatNoDiagonalScaling(), PCEisenstatSetOmega(), PCSOR
326 M*/
327 
328 EXTERN_C_BEGIN
329 #undef __FUNCT__
330 #define __FUNCT__ "PCCreate_Eisenstat"
331 PetscErrorCode PETSCKSP_DLLEXPORT PCCreate_Eisenstat(PC pc)
332 {
333   PetscErrorCode ierr;
334   PC_Eisenstat   *eis;
335 
336   PetscFunctionBegin;
337   ierr = PetscNewLog(pc,PC_Eisenstat,&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->view            = PCView_Eisenstat;
346   pc->ops->setup           = PCSetUp_Eisenstat;
347 
348   pc->data           = (void*)eis;
349   eis->omega         = 1.0;
350   eis->b             = 0;
351   eis->diag          = 0;
352   eis->usediag       = PETSC_TRUE;
353 
354   ierr = PetscObjectComposeFunctionDynamic((PetscObject)pc,"PCEisenstatSetOmega_C","PCEisenstatSetOmega_Eisenstat",
355                     PCEisenstatSetOmega_Eisenstat);CHKERRQ(ierr);
356   ierr = PetscObjectComposeFunctionDynamic((PetscObject)pc,"PCEisenstatNoDiagonalScaling_C",
357                     "PCEisenstatNoDiagonalScaling_Eisenstat",
358                     PCEisenstatNoDiagonalScaling_Eisenstat);CHKERRQ(ierr);
359  PetscFunctionReturn(0);
360 }
361 EXTERN_C_END
362