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