1 /* 2 Defines a Eisenstat trick SSOR preconditioner. This uses about 3 %50 of the usual amount of floating point ops used for SSOR + Krylov 4 method. But it requires actually solving the preconditioned problem 5 with both left and right preconditioning. 6 */ 7 #include <petsc/private/pcimpl.h> /*I "petscpc.h" I*/ 8 9 typedef struct { 10 Mat shell, A; 11 Vec b[2], diag; /* temporary storage for true right-hand side */ 12 PetscReal omega; 13 PetscBool usediag; /* indicates preconditioner should include diagonal scaling*/ 14 } PC_Eisenstat; 15 16 static PetscErrorCode PCMult_Eisenstat(Mat mat, Vec b, Vec x) 17 { 18 PC pc; 19 PC_Eisenstat *eis; 20 21 PetscFunctionBegin; 22 PetscCall(MatShellGetContext(mat, &pc)); 23 eis = (PC_Eisenstat *)pc->data; 24 PetscCall(MatSOR(eis->A, b, eis->omega, SOR_EISENSTAT, 0.0, 1, 1, x)); 25 PetscCall(MatFactorGetError(eis->A, (MatFactorError *)&pc->failedreason)); 26 PetscFunctionReturn(PETSC_SUCCESS); 27 } 28 29 static PetscErrorCode PCNorm_Eisenstat(Mat mat, NormType type, PetscReal *nrm) 30 { 31 PC pc; 32 PC_Eisenstat *eis; 33 34 PetscFunctionBegin; 35 PetscCall(MatShellGetContext(mat, &pc)); 36 eis = (PC_Eisenstat *)pc->data; 37 PetscCall(MatNorm(eis->A, type, nrm)); 38 PetscFunctionReturn(PETSC_SUCCESS); 39 } 40 41 static PetscErrorCode PCApply_Eisenstat(PC pc, Vec x, Vec y) 42 { 43 PC_Eisenstat *eis = (PC_Eisenstat *)pc->data; 44 PetscBool hasop; 45 46 PetscFunctionBegin; 47 if (eis->usediag) { 48 PetscCall(MatHasOperation(pc->pmat, MATOP_MULT_DIAGONAL_BLOCK, &hasop)); 49 if (hasop) { 50 PetscCall(MatMultDiagonalBlock(pc->pmat, x, y)); 51 } else { 52 PetscCall(VecPointwiseMult(y, x, eis->diag)); 53 } 54 } else PetscCall(VecCopy(x, y)); 55 PetscFunctionReturn(PETSC_SUCCESS); 56 } 57 58 static PetscErrorCode PCApplyTranspose_Eisenstat(PC pc, Vec x, Vec y) 59 { 60 PC_Eisenstat *eis = (PC_Eisenstat *)pc->data; 61 PetscBool hasop, set, sym; 62 63 PetscFunctionBegin; 64 PetscCall(MatIsSymmetricKnown(eis->A, &set, &sym)); 65 PetscCheck(set && sym, PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP, "Can only apply transpose of Eisenstat if matrix is symmetric"); 66 if (eis->usediag) { 67 PetscCall(MatHasOperation(pc->pmat, MATOP_MULT_DIAGONAL_BLOCK, &hasop)); 68 if (hasop) { 69 PetscCall(MatMultDiagonalBlock(pc->pmat, x, y)); 70 } else { 71 PetscCall(VecPointwiseMult(y, x, eis->diag)); 72 } 73 } else PetscCall(VecCopy(x, y)); 74 PetscFunctionReturn(PETSC_SUCCESS); 75 } 76 77 static PetscErrorCode PCPreSolve_Eisenstat(PC pc, KSP ksp, Vec b, Vec x) 78 { 79 PC_Eisenstat *eis = (PC_Eisenstat *)pc->data; 80 PetscBool nonzero; 81 82 PetscFunctionBegin; 83 if (pc->presolvedone < 2) { 84 PetscCheck(pc->mat == pc->pmat, PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP, "Cannot have different mat and pmat"); 85 /* swap shell matrix and true matrix */ 86 eis->A = pc->mat; 87 pc->mat = eis->shell; 88 } 89 90 if (!eis->b[pc->presolvedone - 1]) PetscCall(VecDuplicate(b, &eis->b[pc->presolvedone - 1])); 91 92 /* if nonzero initial guess, modify x */ 93 PetscCall(KSPGetInitialGuessNonzero(ksp, &nonzero)); 94 if (nonzero) { 95 PetscCall(VecCopy(x, eis->b[pc->presolvedone - 1])); 96 PetscCall(MatSOR(eis->A, eis->b[pc->presolvedone - 1], eis->omega, SOR_APPLY_UPPER, 0.0, 1, 1, x)); 97 PetscCall(MatFactorGetError(eis->A, (MatFactorError *)&pc->failedreason)); 98 } 99 100 /* save true b, other option is to swap pointers */ 101 PetscCall(VecCopy(b, eis->b[pc->presolvedone - 1])); 102 103 /* modify b by (L + D/omega)^{-1} */ 104 PetscCall(MatSOR(eis->A, eis->b[pc->presolvedone - 1], eis->omega, (MatSORType)(SOR_ZERO_INITIAL_GUESS | SOR_LOCAL_FORWARD_SWEEP), 0.0, 1, 1, b)); 105 PetscCall(MatFactorGetError(eis->A, (MatFactorError *)&pc->failedreason)); 106 PetscFunctionReturn(PETSC_SUCCESS); 107 } 108 109 static PetscErrorCode PCPostSolve_Eisenstat(PC pc, KSP ksp, Vec b, Vec x) 110 { 111 PC_Eisenstat *eis = (PC_Eisenstat *)pc->data; 112 113 PetscFunctionBegin; 114 /* get back true b */ 115 PetscCall(VecCopy(eis->b[pc->presolvedone], b)); 116 117 /* modify x by (U + D/omega)^{-1} */ 118 PetscCall(VecCopy(x, eis->b[pc->presolvedone])); 119 PetscCall(MatSOR(eis->A, eis->b[pc->presolvedone], eis->omega, (MatSORType)(SOR_ZERO_INITIAL_GUESS | SOR_LOCAL_BACKWARD_SWEEP), 0.0, 1, 1, x)); 120 PetscCall(MatFactorGetError(eis->A, (MatFactorError *)&pc->failedreason)); 121 if (!pc->presolvedone) pc->mat = eis->A; 122 PetscFunctionReturn(PETSC_SUCCESS); 123 } 124 125 static PetscErrorCode PCReset_Eisenstat(PC pc) 126 { 127 PC_Eisenstat *eis = (PC_Eisenstat *)pc->data; 128 129 PetscFunctionBegin; 130 PetscCall(VecDestroy(&eis->b[0])); 131 PetscCall(VecDestroy(&eis->b[1])); 132 PetscCall(MatDestroy(&eis->shell)); 133 PetscCall(VecDestroy(&eis->diag)); 134 PetscFunctionReturn(PETSC_SUCCESS); 135 } 136 137 static PetscErrorCode PCDestroy_Eisenstat(PC pc) 138 { 139 PetscFunctionBegin; 140 PetscCall(PCReset_Eisenstat(pc)); 141 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCEisenstatSetOmega_C", NULL)); 142 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCEisenstatSetNoDiagonalScaling_C", NULL)); 143 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCEisenstatGetOmega_C", NULL)); 144 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCEisenstatGetNoDiagonalScaling_C", NULL)); 145 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCPreSolveChangeRHS_C", NULL)); 146 PetscCall(PetscFree(pc->data)); 147 PetscFunctionReturn(PETSC_SUCCESS); 148 } 149 150 static PetscErrorCode PCSetFromOptions_Eisenstat(PC pc, PetscOptionItems PetscOptionsObject) 151 { 152 PC_Eisenstat *eis = (PC_Eisenstat *)pc->data; 153 PetscBool set, flg; 154 PetscReal omega; 155 156 PetscFunctionBegin; 157 PetscOptionsHeadBegin(PetscOptionsObject, "Eisenstat SSOR options"); 158 PetscCall(PetscOptionsReal("-pc_eisenstat_omega", "Relaxation factor 0 < omega < 2", "PCEisenstatSetOmega", eis->omega, &omega, &flg)); 159 if (flg) PetscCall(PCEisenstatSetOmega(pc, omega)); 160 PetscCall(PetscOptionsBool("-pc_eisenstat_no_diagonal_scaling", "Do not use standard diagonal scaling", "PCEisenstatSetNoDiagonalScaling", eis->usediag ? PETSC_FALSE : PETSC_TRUE, &flg, &set)); 161 if (set) PetscCall(PCEisenstatSetNoDiagonalScaling(pc, flg)); 162 PetscOptionsHeadEnd(); 163 PetscFunctionReturn(PETSC_SUCCESS); 164 } 165 166 static PetscErrorCode PCView_Eisenstat(PC pc, PetscViewer viewer) 167 { 168 PC_Eisenstat *eis = (PC_Eisenstat *)pc->data; 169 PetscBool isascii; 170 171 PetscFunctionBegin; 172 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii)); 173 if (isascii) { 174 PetscCall(PetscViewerASCIIPrintf(viewer, " omega = %g\n", (double)eis->omega)); 175 if (eis->usediag) { 176 PetscCall(PetscViewerASCIIPrintf(viewer, " Using diagonal scaling (default)\n")); 177 } else { 178 PetscCall(PetscViewerASCIIPrintf(viewer, " Not using diagonal scaling\n")); 179 } 180 } 181 PetscFunctionReturn(PETSC_SUCCESS); 182 } 183 184 static PetscErrorCode PCSetUp_Eisenstat(PC pc) 185 { 186 PetscInt M, N, m, n; 187 PetscBool set, sym; 188 PC_Eisenstat *eis = (PC_Eisenstat *)pc->data; 189 190 PetscFunctionBegin; 191 if (!pc->setupcalled) { 192 PetscCall(MatGetSize(pc->mat, &M, &N)); 193 PetscCall(MatGetLocalSize(pc->mat, &m, &n)); 194 PetscCall(MatIsSymmetricKnown(pc->mat, &set, &sym)); 195 PetscCall(MatCreate(PetscObjectComm((PetscObject)pc), &eis->shell)); 196 PetscCall(MatSetSizes(eis->shell, m, n, M, N)); 197 PetscCall(MatSetType(eis->shell, MATSHELL)); 198 PetscCall(MatSetUp(eis->shell)); 199 PetscCall(MatShellSetContext(eis->shell, pc)); 200 PetscCall(MatShellSetOperation(eis->shell, MATOP_MULT, (PetscErrorCodeFn *)PCMult_Eisenstat)); 201 if (set && sym) PetscCall(MatShellSetOperation(eis->shell, MATOP_MULT_TRANSPOSE, (PetscErrorCodeFn *)PCMult_Eisenstat)); 202 PetscCall(MatShellSetOperation(eis->shell, MATOP_NORM, (PetscErrorCodeFn *)PCNorm_Eisenstat)); 203 } 204 if (!eis->usediag) PetscFunctionReturn(PETSC_SUCCESS); 205 if (!pc->setupcalled) PetscCall(MatCreateVecs(pc->pmat, &eis->diag, NULL)); 206 PetscCall(MatGetDiagonal(pc->pmat, eis->diag)); 207 PetscFunctionReturn(PETSC_SUCCESS); 208 } 209 210 /* --------------------------------------------------------------------*/ 211 212 static PetscErrorCode PCEisenstatSetOmega_Eisenstat(PC pc, PetscReal omega) 213 { 214 PC_Eisenstat *eis = (PC_Eisenstat *)pc->data; 215 216 PetscFunctionBegin; 217 PetscCheck(omega > 0.0 && omega < 2.0, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_OUTOFRANGE, "Relaxation out of range"); 218 eis->omega = omega; 219 PetscFunctionReturn(PETSC_SUCCESS); 220 } 221 222 static PetscErrorCode PCEisenstatSetNoDiagonalScaling_Eisenstat(PC pc, PetscBool flg) 223 { 224 PC_Eisenstat *eis = (PC_Eisenstat *)pc->data; 225 226 PetscFunctionBegin; 227 eis->usediag = flg; 228 PetscFunctionReturn(PETSC_SUCCESS); 229 } 230 231 static PetscErrorCode PCEisenstatGetOmega_Eisenstat(PC pc, PetscReal *omega) 232 { 233 PC_Eisenstat *eis = (PC_Eisenstat *)pc->data; 234 235 PetscFunctionBegin; 236 *omega = eis->omega; 237 PetscFunctionReturn(PETSC_SUCCESS); 238 } 239 240 static PetscErrorCode PCEisenstatGetNoDiagonalScaling_Eisenstat(PC pc, PetscBool *flg) 241 { 242 PC_Eisenstat *eis = (PC_Eisenstat *)pc->data; 243 244 PetscFunctionBegin; 245 *flg = eis->usediag; 246 PetscFunctionReturn(PETSC_SUCCESS); 247 } 248 249 /*@ 250 PCEisenstatSetOmega - Sets the SSOR relaxation coefficient, omega, 251 to use with Eisenstat's trick (where omega = 1.0 by default) 252 253 Logically Collective 254 255 Input Parameters: 256 + pc - the preconditioner context 257 - omega - relaxation coefficient (0 < omega < 2) 258 259 Options Database Key: 260 . -pc_eisenstat_omega <omega> - Sets omega 261 262 Level: intermediate 263 264 Notes: 265 The Eisenstat trick implementation of SSOR requires about 50% of the 266 usual amount of floating point operations used for SSOR + Krylov method; 267 however, the preconditioned problem must be solved with both left 268 and right preconditioning. 269 270 To use SSOR without the Eisenstat trick, employ the `PCSOR` preconditioner, 271 which can be chosen with the database options `-pc_type sor -pc_sor_symmetric` 272 273 .seealso: [](ch_ksp), `PCSORSetOmega()`, `PCEISENSTAT` 274 @*/ 275 PetscErrorCode PCEisenstatSetOmega(PC pc, PetscReal omega) 276 { 277 PetscFunctionBegin; 278 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 279 PetscValidLogicalCollectiveReal(pc, omega, 2); 280 PetscTryMethod(pc, "PCEisenstatSetOmega_C", (PC, PetscReal), (pc, omega)); 281 PetscFunctionReturn(PETSC_SUCCESS); 282 } 283 284 /*@ 285 PCEisenstatSetNoDiagonalScaling - Causes the Eisenstat preconditioner, `PCEISENSTAT` 286 not to do additional diagonal preconditioning. For matrices with a constant 287 along the diagonal, this may save a small amount of work. 288 289 Logically Collective 290 291 Input Parameters: 292 + pc - the preconditioner context 293 - flg - `PETSC_TRUE` turns off diagonal scaling inside the algorithm 294 295 Options Database Key: 296 . -pc_eisenstat_no_diagonal_scaling - Activates `PCEisenstatSetNoDiagonalScaling()` 297 298 Level: intermediate 299 300 Note: 301 If you use the `KSPSetDiagonalScaling()` or -ksp_diagonal_scale option then you will 302 likely want to use this routine since it will save you some unneeded flops. 303 304 .seealso: [](ch_ksp), `PCEisenstatSetOmega()`, `PCEISENSTAT` 305 @*/ 306 PetscErrorCode PCEisenstatSetNoDiagonalScaling(PC pc, PetscBool flg) 307 { 308 PetscFunctionBegin; 309 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 310 PetscTryMethod(pc, "PCEisenstatSetNoDiagonalScaling_C", (PC, PetscBool), (pc, flg)); 311 PetscFunctionReturn(PETSC_SUCCESS); 312 } 313 314 /*@ 315 PCEisenstatGetOmega - Gets the SSOR relaxation coefficient, omega, 316 to use with Eisenstat's trick (where omega = 1.0 by default). 317 318 Logically Collective 319 320 Input Parameter: 321 . pc - the preconditioner context 322 323 Output Parameter: 324 . omega - relaxation coefficient (0 < omega < 2) 325 326 Options Database Key: 327 . -pc_eisenstat_omega <omega> - Sets omega 328 329 Notes: 330 The Eisenstat trick implementation of SSOR requires about 50% of the 331 usual amount of floating point operations used for SSOR + Krylov method; 332 however, the preconditioned problem must be solved with both left 333 and right preconditioning. 334 335 To use SSOR without the Eisenstat trick, employ the PCSOR preconditioner, 336 which can be chosen with the database options `-pc_type sor -pc_sor_symmetric` 337 338 Level: intermediate 339 340 .seealso: [](ch_ksp), `PCEISENSTAT`, `PCSORGetOmega()`, `PCEisenstatSetOmega()` 341 @*/ 342 PetscErrorCode PCEisenstatGetOmega(PC pc, PetscReal *omega) 343 { 344 PetscFunctionBegin; 345 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 346 PetscUseMethod(pc, "PCEisenstatGetOmega_C", (PC, PetscReal *), (pc, omega)); 347 PetscFunctionReturn(PETSC_SUCCESS); 348 } 349 350 /*@ 351 PCEisenstatGetNoDiagonalScaling - Tells if the Eisenstat preconditioner 352 not to do additional diagonal preconditioning. For matrices with a constant 353 along the diagonal, this may save a small amount of work. 354 355 Logically Collective 356 357 Input Parameter: 358 . pc - the preconditioner context 359 360 Output Parameter: 361 . flg - `PETSC_TRUE` means there is no diagonal scaling applied 362 363 Options Database Key: 364 . -pc_eisenstat_no_diagonal_scaling - Activates `PCEisenstatSetNoDiagonalScaling()` 365 366 Level: intermediate 367 368 Note: 369 If you use the KSPSetDiagonalScaling() or -ksp_diagonal_scale option then you will 370 likely want to use this routine since it will save you some unneeded flops. 371 372 .seealso: , `PCEISENSTAT`, `PCEisenstatGetOmega()` 373 @*/ 374 PetscErrorCode PCEisenstatGetNoDiagonalScaling(PC pc, PetscBool *flg) 375 { 376 PetscFunctionBegin; 377 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 378 PetscUseMethod(pc, "PCEisenstatGetNoDiagonalScaling_C", (PC, PetscBool *), (pc, flg)); 379 PetscFunctionReturn(PETSC_SUCCESS); 380 } 381 382 static PetscErrorCode PCPreSolveChangeRHS_Eisenstat(PC pc, PetscBool *change) 383 { 384 PetscFunctionBegin; 385 *change = PETSC_TRUE; 386 PetscFunctionReturn(PETSC_SUCCESS); 387 } 388 389 /*MC 390 PCEISENSTAT - An implementation of SSOR (symmetric successive over relaxation, symmetric Gauss-Seidel) 391 preconditioning that incorporates Eisenstat's trick to reduce the amount of computation needed. 392 393 Options Database Keys: 394 + -pc_eisenstat_omega <omega> - Sets omega 395 - -pc_eisenstat_no_diagonal_scaling - Activates `PCEisenstatSetNoDiagonalScaling()` 396 397 Level: beginner 398 399 Notes: 400 Only implemented for the `MATAIJ` matrix format. 401 402 Not a true parallel SOR, in parallel this implementation corresponds to block Jacobi with SOR on each block. 403 404 Developer Note: 405 Since this algorithm runs the Krylov method on a transformed linear system the implementation provides `PCPreSolve()` and `PCPostSolve()` 406 routines that `KSP` uses to set up the transformed linear system. 407 408 .seealso: [](ch_ksp), `PCCreate()`, `PCSetType()`, `PCType`, `PC`, `PCEisenstatGetOmega()`, 409 `PCEisenstatSetNoDiagonalScaling()`, `PCEisenstatSetOmega()`, `PCSOR` 410 M*/ 411 412 PETSC_EXTERN PetscErrorCode PCCreate_Eisenstat(PC pc) 413 { 414 PC_Eisenstat *eis; 415 416 PetscFunctionBegin; 417 PetscCall(PetscNew(&eis)); 418 419 pc->ops->apply = PCApply_Eisenstat; 420 pc->ops->applytranspose = PCApplyTranspose_Eisenstat; 421 pc->ops->presolve = PCPreSolve_Eisenstat; 422 pc->ops->postsolve = PCPostSolve_Eisenstat; 423 pc->ops->applyrichardson = NULL; 424 pc->ops->setfromoptions = PCSetFromOptions_Eisenstat; 425 pc->ops->destroy = PCDestroy_Eisenstat; 426 pc->ops->reset = PCReset_Eisenstat; 427 pc->ops->view = PCView_Eisenstat; 428 pc->ops->setup = PCSetUp_Eisenstat; 429 430 pc->data = eis; 431 eis->omega = 1.0; 432 eis->b[0] = NULL; 433 eis->b[1] = NULL; 434 eis->diag = NULL; 435 eis->usediag = PETSC_TRUE; 436 437 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCEisenstatSetOmega_C", PCEisenstatSetOmega_Eisenstat)); 438 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCEisenstatSetNoDiagonalScaling_C", PCEisenstatSetNoDiagonalScaling_Eisenstat)); 439 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCEisenstatGetOmega_C", PCEisenstatGetOmega_Eisenstat)); 440 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCEisenstatGetNoDiagonalScaling_C", PCEisenstatGetNoDiagonalScaling_Eisenstat)); 441 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCPreSolveChangeRHS_C", PCPreSolveChangeRHS_Eisenstat)); 442 PetscFunctionReturn(PETSC_SUCCESS); 443 } 444