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