Date: 2022-03-04T17:39:06+00:00

Actual source code: gcr.c

      2: #include <petsc/private/kspimpl.h>

      4: typedef struct {
      5:   PetscInt    restart;
      6:   PetscInt    n_restarts;
      7:   PetscScalar *val;
      8:   Vec         *VV, *SS;
      9:   Vec         R;

     11:   PetscErrorCode (*modifypc)(KSP,PetscInt,PetscReal,void*);  /* function to modify the preconditioner*/
     12:   PetscErrorCode (*modifypc_destroy)(void*);                 /* function to destroy the user context for the modifypc function */

     14:   void *modifypc_ctx;                                        /* user defined data for the modifypc function */
     15: } KSP_GCR;

     17: static PetscErrorCode KSPSolve_GCR_cycle(KSP ksp)
     18: {
     19:   KSP_GCR        *ctx = (KSP_GCR*)ksp->data;
     21:   PetscScalar    r_dot_v;
     22:   Mat            A, B;
     23:   PC             pc;
     24:   Vec            s,v,r;
     25:   /*
     26:      The residual norm will not be computed when ksp->its > ksp->chknorm hence need to initialize norm_r with some dummy value
     27:   */
     28:   PetscReal      norm_r = 0.0,nrm;
     29:   PetscInt       k, i, restart;
     30:   Vec            x;

     33:   restart = ctx->restart;
     34:   KSPGetPC(ksp, &pc);
     35:   KSPGetOperators(ksp, &A, &B);

     37:   x = ksp->vec_sol;
     38:   r = ctx->R;

     40:   for (k=0; k<restart; k++) {
     41:     v = ctx->VV[k];
     42:     s = ctx->SS[k];
     43:     if (ctx->modifypc) {
     44:       (*ctx->modifypc)(ksp,ksp->its,ksp->rnorm,ctx->modifypc_ctx);
     45:     }

     47:     KSP_PCApply(ksp, r, s); /* s = B^{-1} r */
     48:     KSP_MatMult(ksp,A, s, v);  /* v = A s */

     50:     VecMDot(v,k, ctx->VV, ctx->val);
     51:     for (i=0; i<k; i++) ctx->val[i] = -ctx->val[i];
     52:     VecMAXPY(v,k,ctx->val,ctx->VV); /* v = v - sum_{i=0}^{k-1} alpha_i v_i */
     53:     VecMAXPY(s,k,ctx->val,ctx->SS); /* s = s - sum_{i=0}^{k-1} alpha_i s_i */

     55:     VecDotNorm2(r,v,&r_dot_v,&nrm);
     56:     nrm     = PetscSqrtReal(nrm);
     57:     r_dot_v = r_dot_v/nrm;
     58:     VecScale(v, 1.0/nrm);
     59:     VecScale(s, 1.0/nrm);
     60:     VecAXPY(x,  r_dot_v, s);
     61:     VecAXPY(r, -r_dot_v, v);
     62:     if (ksp->its > ksp->chknorm && ksp->normtype != KSP_NORM_NONE) {
     63:       VecNorm(r, NORM_2, &norm_r);
     64:       KSPCheckNorm(ksp,norm_r);
     65:     }
     66:     /* update the local counter and the global counter */
     67:     ksp->its++;
     68:     ksp->rnorm = norm_r;

     70:     KSPLogResidualHistory(ksp,norm_r);
     71:     KSPMonitor(ksp,ksp->its,norm_r);

     73:     if (ksp->its-1 > ksp->chknorm) {
     74:       (*ksp->converged)(ksp,ksp->its,norm_r,&ksp->reason,ksp->cnvP);
     75:       if (ksp->reason) break;
     76:     }

     78:     if (ksp->its >= ksp->max_it) {
     79:       ksp->reason = KSP_CONVERGED_ITS;
     80:       break;
     81:     }
     82:   }
     83:   ctx->n_restarts++;
     84:   return(0);
     85: }

     87: static PetscErrorCode KSPSolve_GCR(KSP ksp)
     88: {
     89:   KSP_GCR        *ctx = (KSP_GCR*)ksp->data;
     91:   Mat            A, B;
     92:   Vec            r,b,x;
     93:   PetscReal      norm_r = 0.0;

     96:   KSPGetOperators(ksp, &A, &B);
     97:   x    = ksp->vec_sol;
     98:   b    = ksp->vec_rhs;
     99:   r    = ctx->R;

    101:   /* compute initial residual */
    102:   KSP_MatMult(ksp,A, x, r);
    103:   VecAYPX(r, -1.0, b); /* r = b - A x  */
    104:   if (ksp->normtype != KSP_NORM_NONE) {
    105:     VecNorm(r, NORM_2, &norm_r);
    106:     KSPCheckNorm(ksp,norm_r);
    107:   }
    108:   ksp->its    = 0;
    109:   ksp->rnorm0 = norm_r;

    111:   KSPLogResidualHistory(ksp,ksp->rnorm0);
    112:   KSPMonitor(ksp,ksp->its,ksp->rnorm0);
    113:   (*ksp->converged)(ksp,ksp->its,ksp->rnorm0,&ksp->reason,ksp->cnvP);
    114:   if (ksp->reason) return(0);

    116:   do {
    117:     KSPSolve_GCR_cycle(ksp);
    118:     if (ksp->reason) return(0); /* catch case when convergence occurs inside the cycle */
    119:   } while (ksp->its < ksp->max_it);

    121:   if (ksp->its >= ksp->max_it) ksp->reason = KSP_DIVERGED_ITS;
    122:   return(0);
    123: }

    125: static PetscErrorCode KSPView_GCR(KSP ksp, PetscViewer viewer)
    126: {
    127:   KSP_GCR        *ctx = (KSP_GCR*)ksp->data;
    129:   PetscBool      iascii;

    132:   PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);
    133:   if (iascii) {
    134:     PetscViewerASCIIPrintf(viewer,"  restart = %D \n", ctx->restart);
    135:     PetscViewerASCIIPrintf(viewer,"  restarts performed = %D \n", ctx->n_restarts);
    136:   }
    137:   return(0);
    138: }

    140: static PetscErrorCode KSPSetUp_GCR(KSP ksp)
    141: {
    142:   KSP_GCR        *ctx = (KSP_GCR*)ksp->data;
    144:   Mat            A;
    145:   PetscBool      diagonalscale;

    148:   PCGetDiagonalScale(ksp->pc,&diagonalscale);
    149:   if (diagonalscale) SETERRQ1(PetscObjectComm((PetscObject)ksp),PETSC_ERR_SUP,"Krylov method %s does not support diagonal scaling",((PetscObject)ksp)->type_name);

    151:   KSPGetOperators(ksp, &A, NULL);
    152:   MatCreateVecs(A, &ctx->R, NULL);
    153:   VecDuplicateVecs(ctx->R, ctx->restart, &ctx->VV);
    154:   VecDuplicateVecs(ctx->R, ctx->restart, &ctx->SS);

    156:   PetscMalloc1(ctx->restart, &ctx->val);
    157:   return(0);
    158: }

    160: static PetscErrorCode KSPReset_GCR(KSP ksp)
    161: {
    163:   KSP_GCR        *ctx = (KSP_GCR*)ksp->data;

    166:   VecDestroy(&ctx->R);
    167:   VecDestroyVecs(ctx->restart,&ctx->VV);
    168:   VecDestroyVecs(ctx->restart,&ctx->SS);
    169:   if (ctx->modifypc_destroy) {
    170:     (*ctx->modifypc_destroy)(ctx->modifypc_ctx);
    171:   }
    172:   PetscFree(ctx->val);
    173:   return(0);
    174: }

    176: static PetscErrorCode KSPDestroy_GCR(KSP ksp)
    177: {

    181:   KSPReset_GCR(ksp);
    182:   KSPDestroyDefault(ksp);
    183:   PetscObjectComposeFunction((PetscObject)ksp,"KSPGCRSetRestart_C",NULL);
    184:   PetscObjectComposeFunction((PetscObject)ksp,"KSPGCRGetRestart_C",NULL);
    185:   PetscObjectComposeFunction((PetscObject)ksp,"KSPGCRSetModifyPC_C",NULL);
    186:   return(0);
    187: }

    189: static PetscErrorCode KSPSetFromOptions_GCR(PetscOptionItems *PetscOptionsObject,KSP ksp)
    190: {
    192:   KSP_GCR        *ctx = (KSP_GCR*)ksp->data;
    193:   PetscInt       restart;
    194:   PetscBool      flg;

    197:   PetscOptionsHead(PetscOptionsObject,"KSP GCR options");
    198:   PetscOptionsInt("-ksp_gcr_restart","Number of Krylov search directions","KSPGCRSetRestart",ctx->restart,&restart,&flg);
    199:   if (flg) { KSPGCRSetRestart(ksp,restart); }
    200:   PetscOptionsTail();
    201:   return(0);
    202: }

    205: typedef PetscErrorCode (*KSPGCRModifyPCFunction)(KSP,PetscInt,PetscReal,void*);
    206: typedef PetscErrorCode (*KSPGCRDestroyFunction)(void*);

    208: static PetscErrorCode  KSPGCRSetModifyPC_GCR(KSP ksp,KSPGCRModifyPCFunction function,void *data,KSPGCRDestroyFunction destroy)
    209: {
    210:   KSP_GCR *ctx = (KSP_GCR*)ksp->data;

    214:   ctx->modifypc         = function;
    215:   ctx->modifypc_destroy = destroy;
    216:   ctx->modifypc_ctx     = data;
    217:   return(0);
    218: }

    220: /*@C
    221:  KSPGCRSetModifyPC - Sets the routine used by GCR to modify the preconditioner.

    223:  Logically Collective on ksp

    225:  Input Parameters:
    226:  +  ksp      - iterative context obtained from KSPCreate()
    227:  .  function - user defined function to modify the preconditioner
    228:  .  ctx      - user provided context for the modify preconditioner function
    229:  -  destroy  - the function to use to destroy the user provided application context.

    231:  Calling Sequence of function:
    232:   PetscErrorCode function (KSP ksp, PetscInt n, PetscReal rnorm, void *ctx)

    234:  ksp   - iterative context
    235:  n     - the total number of GCR iterations that have occurred
    236:  rnorm - 2-norm residual value
    237:  ctx   - the user provided application context

    239:  Level: intermediate

    241:  Notes:
    242:  The default modifypc routine is KSPGCRModifyPCNoChange()

    244:  .seealso: KSPGCRModifyPCNoChange()

    246:  @*/
    247: PetscErrorCode  KSPGCRSetModifyPC(KSP ksp,PetscErrorCode (*function)(KSP,PetscInt,PetscReal,void*),void *data,PetscErrorCode (*destroy)(void*))
    248: {

    252:   PetscUseMethod(ksp,"KSPGCRSetModifyPC_C",(KSP,PetscErrorCode (*)(KSP,PetscInt,PetscReal,void*),void *data,PetscErrorCode (*)(void*)),(ksp,function,data,destroy));
    253:   return(0);
    254: }

    256: static PetscErrorCode KSPGCRSetRestart_GCR(KSP ksp,PetscInt restart)
    257: {
    258:   KSP_GCR *ctx;

    261:   ctx          = (KSP_GCR*)ksp->data;
    262:   ctx->restart = restart;
    263:   return(0);
    264: }

    266: static PetscErrorCode KSPGCRGetRestart_GCR(KSP ksp,PetscInt *restart)
    267: {
    268:   KSP_GCR *ctx;

    271:   ctx      = (KSP_GCR*)ksp->data;
    272:   *restart = ctx->restart;
    273:   return(0);
    274: }

    276: /*@
    277:    KSPGCRSetRestart - Sets number of iterations at which GCR restarts.

    279:    Not Collective

    281:    Input Parameters:
    282: +  ksp - the Krylov space context
    283: -  restart - integer restart value

    285:    Note: The default value is 30.

    287:    Level: intermediate

    289: .seealso: KSPSetTolerances(), KSPGCRGetRestart(), KSPGMRESSetRestart()
    290: @*/
    291: PetscErrorCode KSPGCRSetRestart(KSP ksp, PetscInt restart)
    292: {

    296:   PetscTryMethod(ksp,"KSPGCRSetRestart_C",(KSP,PetscInt),(ksp,restart));
    297:   return(0);
    298: }

    300: /*@
    301:    KSPGCRGetRestart - Gets number of iterations at which GCR restarts.

    303:    Not Collective

    305:    Input Parameter:
    306: .  ksp - the Krylov space context

    308:    Output Parameter:
    309: .   restart - integer restart value

    311:    Note: The default value is 30.

    313:    Level: intermediate

    315: .seealso: KSPSetTolerances(), KSPGCRSetRestart(), KSPGMRESGetRestart()
    316: @*/
    317: PetscErrorCode KSPGCRGetRestart(KSP ksp, PetscInt *restart)
    318: {

    322:   PetscTryMethod(ksp,"KSPGCRGetRestart_C",(KSP,PetscInt*),(ksp,restart));
    323:   return(0);
    324: }

    326: static PetscErrorCode  KSPBuildSolution_GCR(KSP ksp, Vec v, Vec *V)
    327: {
    329:   Vec            x;

    332:   x = ksp->vec_sol;
    333:   if (v) {
    334:     VecCopy(x, v);
    335:     if (V) *V = v;
    336:   } else if (V) {
    337:     *V = ksp->vec_sol;
    338:   }
    339:   return(0);
    340: }

    342: static PetscErrorCode  KSPBuildResidual_GCR(KSP ksp, Vec t, Vec v, Vec *V)
    343: {
    345:   KSP_GCR        *ctx;

    348:   ctx = (KSP_GCR*)ksp->data;
    349:   if (v) {
    350:     VecCopy(ctx->R, v);
    351:     if (V) *V = v;
    352:   } else if (V) {
    353:     *V = ctx->R;
    354:   }
    355:   return(0);
    356: }

    358: /*MC
    359:      KSPGCR - Implements the preconditioned Generalized Conjugate Residual method.

    361:    Options Database Keys:
    362: .   -ksp_gcr_restart <restart> - the number of stored vectors to orthogonalize against

    364:    Level: beginner

    366:     Notes:
    367:     The GCR Krylov method supports non-symmetric matrices and permits the use of a preconditioner
    368:            which may vary from one iteration to the next. Users can can define a method to vary the
    369:            preconditioner between iterates via KSPGCRSetModifyPC().

    371:            Restarts are solves with x0 not equal to zero. When a restart occurs, the initial starting
    372:            solution is given by the current estimate for x which was obtained by the last restart
    373:            iterations of the GCR algorithm.

    375:            Unlike GMRES and FGMRES, when using GCR, the solution and residual vector can be directly accessed at any iterate,
    376:            with zero computational cost, via a call to KSPBuildSolution() and KSPBuildResidual() respectively.

    378:            This implementation of GCR will only apply the stopping condition test whenever ksp->its > ksp->chknorm,
    379:            where ksp->chknorm is specified via the command line argument -ksp_check_norm_iteration or via
    380:            the function KSPSetCheckNormIteration(). Hence the residual norm reported by the monitor and stored
    381:            in the residual history will be listed as 0.0 before this iteration. It is actually not 0.0; just not calculated.

    383:            The method implemented requires the storage of 2 x restart + 1 vectors, twice as much as GMRES.
    384:            Support only for right preconditioning.

    386:     Contributed by Dave May

    388:     References:
    389: .          1. - S. C. Eisenstat, H. C. Elman, and H. C. Schultz. Variational iterative methods for
    390:            nonsymmetric systems of linear equations. SIAM J. Numer. Anal., 20, 1983

    392: .seealso:  KSPCreate(), KSPSetType(), KSPType (for list of available types), KSP,
    393:            KSPGCRSetRestart(), KSPGCRSetModifyPC(), KSPGMRES, KSPFGMRES

    395: M*/
    396: PETSC_EXTERN PetscErrorCode KSPCreate_GCR(KSP ksp)
    397: {
    399:   KSP_GCR        *ctx;

    402:   PetscNewLog(ksp,&ctx);

    404:   ctx->restart    = 30;
    405:   ctx->n_restarts = 0;
    406:   ksp->data       = (void*)ctx;

    408:   KSPSetSupportedNorm(ksp,KSP_NORM_NONE,PC_RIGHT,1);
    409:   KSPSetSupportedNorm(ksp,KSP_NORM_UNPRECONDITIONED,PC_RIGHT,3);

    411:   ksp->ops->setup          = KSPSetUp_GCR;
    412:   ksp->ops->solve          = KSPSolve_GCR;
    413:   ksp->ops->reset          = KSPReset_GCR;
    414:   ksp->ops->destroy        = KSPDestroy_GCR;
    415:   ksp->ops->view           = KSPView_GCR;
    416:   ksp->ops->setfromoptions = KSPSetFromOptions_GCR;
    417:   ksp->ops->buildsolution  = KSPBuildSolution_GCR;
    418:   ksp->ops->buildresidual  = KSPBuildResidual_GCR;

    420:   PetscObjectComposeFunction((PetscObject)ksp,"KSPGCRSetRestart_C",KSPGCRSetRestart_GCR);
    421:   PetscObjectComposeFunction((PetscObject)ksp,"KSPGCRGetRestart_C",KSPGCRGetRestart_GCR);
    422:   PetscObjectComposeFunction((PetscObject)ksp,"KSPGCRSetModifyPC_C",KSPGCRSetModifyPC_GCR);
    423:   return(0);
    424: }