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

Actual source code: snesncg.c

      1: #include <../src/snes/impls/ncg/snesncgimpl.h>
      2: const char *const SNESNCGTypes[] = {"FR","PRP","HS","DY","CD","SNESNCGType","SNES_NCG_",NULL};

      4: static PetscErrorCode SNESReset_NCG(SNES snes)
      5: {
      7:   return(0);
      8: }

     10: /*
     11:   SNESDestroy_NCG - Destroys the private SNES_NCG context that was created with SNESCreate_NCG().

     13:   Input Parameter:
     14: . snes - the SNES context

     16:   Application Interface Routine: SNESDestroy()
     17: */
     18: static PetscErrorCode SNESDestroy_NCG(SNES snes)
     19: {

     23:   PetscFree(snes->data);
     24:   return(0);
     25: }

     27: /*
     28:    SNESSetUp_NCG - Sets up the internal data structures for the later use
     29:    of the SNESNCG nonlinear solver.

     31:    Input Parameters:
     32: +  snes - the SNES context
     33: -  x - the solution vector

     35:    Application Interface Routine: SNESSetUp()
     36:  */

     38: static PetscErrorCode SNESSetUp_NCG(SNES snes)
     39: {

     43:   SNESSetWorkVecs(snes,2);
     44:   if (snes->npcside== PC_RIGHT) SETERRQ(PetscObjectComm((PetscObject)snes),PETSC_ERR_ARG_WRONGSTATE, "SNESNCG only supports left preconditioning");
     45:   if (snes->functype == SNES_FUNCTION_DEFAULT) snes->functype = SNES_FUNCTION_UNPRECONDITIONED;
     46:   return(0);
     47: }

     49: static PetscErrorCode SNESLineSearchApply_NCGLinear(SNESLineSearch linesearch)
     50: {
     51:   PetscScalar    alpha, ptAp;
     52:   Vec            X, Y, F, W;
     53:   SNES           snes;
     55:   PetscReal      *fnorm, *xnorm, *ynorm;

     58:   SNESLineSearchGetSNES(linesearch, &snes);
     59:   X     = linesearch->vec_sol;
     60:   W     = linesearch->vec_sol_new;
     61:   F     = linesearch->vec_func;
     62:   Y     = linesearch->vec_update;
     63:   fnorm = &linesearch->fnorm;
     64:   xnorm = &linesearch->xnorm;
     65:   ynorm = &linesearch->ynorm;

     67:   if (!snes->jacobian) {
     68:     SNESSetUpMatrices(snes);
     69:   }

     71:   /*

     73:    The exact step size for unpreconditioned linear CG is just:
     74:    alpha = (r, r) / (p, Ap) = (f, f) / (y, Jy)
     75:    */
     76:   SNESComputeJacobian(snes, X, snes->jacobian, snes->jacobian_pre);
     77:   VecDot(F, F, &alpha);
     78:   MatMult(snes->jacobian, Y, W);
     79:   VecDot(Y, W, &ptAp);
     80:   alpha = alpha / ptAp;
     81:   VecAXPY(X,-alpha,Y);
     82:   SNESComputeFunction(snes,X,F);

     84:   VecNorm(F,NORM_2,fnorm);
     85:   VecNorm(X,NORM_2,xnorm);
     86:   VecNorm(Y,NORM_2,ynorm);
     87:   return(0);
     88: }

     90: /*MC
     91:    SNESLINESEARCHNCGLINEAR - Special line search only for SNESNCG

     93:    This line search uses the length "as if" the problem is linear (that is what is computed by the linear CG method) using the Jacobian of the function.
     94:    alpha = (r, r) / (p, Ap) = (f, f) / (y, Jy) where r (f) is the current residual (function value), p (y) is the current search direction.

     96:    Notes: This requires a Jacobian-vector product but does not require the solution of a linear system with the Jacobian

     98:    This is a "odd-ball" line search, we don't know if it is in the literature or used in practice by anyone.

    100:    Level: advanced

    102: .seealso: SNESLineSearchCreate(), SNESLineSearchSetType()
    103: M*/

    105: PETSC_EXTERN PetscErrorCode SNESLineSearchCreate_NCGLinear(SNESLineSearch linesearch)
    106: {
    108:   linesearch->ops->apply          = SNESLineSearchApply_NCGLinear;
    109:   linesearch->ops->destroy        = NULL;
    110:   linesearch->ops->setfromoptions = NULL;
    111:   linesearch->ops->reset          = NULL;
    112:   linesearch->ops->view           = NULL;
    113:   linesearch->ops->setup          = NULL;
    114:   return(0);
    115: }

    117: /*
    118:   SNESSetFromOptions_NCG - Sets various parameters for the SNESNCG method.

    120:   Input Parameter:
    121: . snes - the SNES context

    123:   Application Interface Routine: SNESSetFromOptions()
    124: */
    125: static PetscErrorCode SNESSetFromOptions_NCG(PetscOptionItems *PetscOptionsObject,SNES snes)
    126: {
    127:   SNES_NCG       *ncg = (SNES_NCG*)snes->data;
    129:   PetscBool      debug = PETSC_FALSE;
    130:   SNESNCGType    ncgtype=ncg->type;
    131:   SNESLineSearch linesearch;

    134:   PetscOptionsHead(PetscOptionsObject,"SNES NCG options");
    135:   PetscOptionsBool("-snes_ncg_monitor","Monitor the beta values used in the NCG iterations","SNES",ncg->monitor ? PETSC_TRUE : PETSC_FALSE, &debug, NULL);
    136:   if (debug) {
    137:     ncg->monitor = PETSC_VIEWER_STDOUT_(PetscObjectComm((PetscObject)snes));
    138:   }
    139:   PetscOptionsEnum("-snes_ncg_type","NCG Beta type used","SNESNCGSetType",SNESNCGTypes,(PetscEnum)ncg->type,(PetscEnum*)&ncgtype,NULL);
    140:   SNESNCGSetType(snes, ncgtype);
    141:   PetscOptionsTail();
    142:   if (!snes->linesearch) {
    143:     SNESGetLineSearch(snes, &linesearch);
    144:     if (!((PetscObject)linesearch)->type_name) {
    145:       if (!snes->npc) {
    146:         SNESLineSearchSetType(linesearch, SNESLINESEARCHCP);
    147:       } else {
    148:         SNESLineSearchSetType(linesearch, SNESLINESEARCHL2);
    149:       }
    150:     }
    151:   }
    152:   return(0);
    153: }

    155: /*
    156:   SNESView_NCG - Prints info from the SNESNCG data structure.

    158:   Input Parameters:
    159: + SNES - the SNES context
    160: - viewer - visualization context

    162:   Application Interface Routine: SNESView()
    163: */
    164: static PetscErrorCode SNESView_NCG(SNES snes, PetscViewer viewer)
    165: {
    166:   SNES_NCG      *ncg = (SNES_NCG *) snes->data;
    167:   PetscBool      iascii;

    171:   PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII, &iascii);
    172:   if (iascii) {
    173:     PetscViewerASCIIPrintf(viewer, "  type: %s\n", SNESNCGTypes[ncg->type]);
    174:   }
    175:   return(0);
    176: }

    178: /*

    180:  Assuming F = SNESComputeFunction(X) compute Y^tJ^tF using a simple secant approximation of the jacobian.

    182:  This routine is not currently used. I don't know what its intended purpose is.

    184:  Note it has a hardwired differencing parameter of 1e-5

    186:  */
    187: PetscErrorCode SNESNCGComputeYtJtF_Private(SNES snes, Vec X, Vec F, Vec Y, Vec W, Vec G, PetscScalar * ytJtf)
    188: {
    190:   PetscScalar    ftf, ftg, fty, h;

    193:   VecDot(F, F, &ftf);
    194:   VecDot(F, Y, &fty);
    195:   h      = 1e-5*fty / fty;
    196:   VecCopy(X, W);
    197:   VecAXPY(W, -h, Y);          /* this is arbitrary */
    198:   SNESComputeFunction(snes, W, G);
    199:   VecDot(G, F, &ftg);
    200:   *ytJtf = (ftg - ftf) / h;
    201:   return(0);
    202: }

    204: /*@
    205:     SNESNCGSetType - Sets the conjugate update type for SNESNCG.

    207:     Logically Collective on SNES

    209:     Input Parameters:
    210: +   snes - the iterative context
    211: -   btype - update type

    213:     Options Database:
    214: .   -snes_ncg_type <prp,fr,hs,dy,cd> -strategy for selecting algorithm for computing beta

    216:     Level: intermediate

    218:     SNESNCGSelectTypes:
    219: +   SNES_NCG_FR - Fletcher-Reeves update
    220: .   SNES_NCG_PRP - Polak-Ribiere-Polyak update
    221: .   SNES_NCG_HS - Hestenes-Steifel update
    222: .   SNES_NCG_DY - Dai-Yuan update
    223: -   SNES_NCG_CD - Conjugate Descent update

    225:    Notes:
    226:    SNES_NCG_PRP is the default, and the only one that tolerates generalized search directions.

    228:    It is not clear what "generalized search directions" means, does it mean use with a nonlinear preconditioner,
    229:    that is using -npc_snes_type <type>, SNESSetNPC(), or SNESGetNPC()?

    231: @*/
    232: PetscErrorCode SNESNCGSetType(SNES snes, SNESNCGType btype)
    233: {

    238:   PetscTryMethod(snes,"SNESNCGSetType_C",(SNES,SNESNCGType),(snes,btype));
    239:   return(0);
    240: }

    242: static PetscErrorCode SNESNCGSetType_NCG(SNES snes, SNESNCGType btype)
    243: {
    244:   SNES_NCG *ncg = (SNES_NCG*)snes->data;

    247:   ncg->type = btype;
    248:   return(0);
    249: }

    251: /*
    252:   SNESSolve_NCG - Solves a nonlinear system with the Nonlinear Conjugate Gradient method.

    254:   Input Parameters:
    255: . snes - the SNES context

    257:   Output Parameter:
    258: . outits - number of iterations until termination

    260:   Application Interface Routine: SNESSolve()
    261: */
    262: static PetscErrorCode SNESSolve_NCG(SNES snes)
    263: {
    264:   SNES_NCG             *ncg = (SNES_NCG*)snes->data;
    265:   Vec                  X,dX,lX,F,dXold;
    266:   PetscReal            fnorm, ynorm, xnorm, beta = 0.0;
    267:   PetscScalar          dXdotdX, dXolddotdXold, dXdotdXold, lXdotdX, lXdotdXold;
    268:   PetscInt             maxits, i;
    269:   PetscErrorCode       ierr;
    270:   SNESLineSearchReason lsresult = SNES_LINESEARCH_SUCCEEDED;
    271:   SNESLineSearch       linesearch;
    272:   SNESConvergedReason  reason;

    275:   if (snes->xl || snes->xu || snes->ops->computevariablebounds) SETERRQ1(PetscObjectComm((PetscObject)snes),PETSC_ERR_ARG_WRONGSTATE, "SNES solver %s does not support bounds", ((PetscObject)snes)->type_name);

    277:   PetscCitationsRegister(SNESCitation,&SNEScite);
    278:   snes->reason = SNES_CONVERGED_ITERATING;

    280:   maxits = snes->max_its;            /* maximum number of iterations */
    281:   X      = snes->vec_sol;            /* X^n */
    282:   dXold  = snes->work[0];            /* The previous iterate of X */
    283:   dX     = snes->work[1];            /* the preconditioned direction */
    284:   lX     = snes->vec_sol_update;     /* the conjugate direction */
    285:   F      = snes->vec_func;           /* residual vector */

    287:   SNESGetLineSearch(snes, &linesearch);

    289:   PetscObjectSAWsTakeAccess((PetscObject)snes);
    290:   snes->iter = 0;
    291:   snes->norm = 0.;
    292:   PetscObjectSAWsGrantAccess((PetscObject)snes);

    294:   /* compute the initial function and preconditioned update dX */

    296:   if (snes->npc && snes->functype == SNES_FUNCTION_PRECONDITIONED) {
    297:     SNESApplyNPC(snes,X,NULL,dX);
    298:     SNESGetConvergedReason(snes->npc,&reason);
    299:     if (reason < 0  && reason != SNES_DIVERGED_MAX_IT) {
    300:       snes->reason = SNES_DIVERGED_INNER;
    301:       return(0);
    302:     }
    303:     VecCopy(dX,F);
    304:     VecNorm(F,NORM_2,&fnorm);
    305:   } else {
    306:     if (!snes->vec_func_init_set) {
    307:       SNESComputeFunction(snes,X,F);
    308:     } else snes->vec_func_init_set = PETSC_FALSE;

    310:     /* convergence test */
    311:     VecNorm(F,NORM_2,&fnorm);
    312:     SNESCheckFunctionNorm(snes,fnorm);
    313:     VecCopy(F,dX);
    314:   }
    315:   if (snes->npc) {
    316:     if (snes->functype == SNES_FUNCTION_UNPRECONDITIONED) {
    317:       SNESApplyNPC(snes,X,F,dX);
    318:       SNESGetConvergedReason(snes->npc,&reason);
    319:       if (reason < 0  && reason != SNES_DIVERGED_MAX_IT) {
    320:         snes->reason = SNES_DIVERGED_INNER;
    321:         return(0);
    322:       }
    323:     }
    324:   }
    325:   VecCopy(dX,lX);
    326:   VecDot(dX, dX, &dXdotdX);

    328:   PetscObjectSAWsTakeAccess((PetscObject)snes);
    329:   snes->norm = fnorm;
    330:   PetscObjectSAWsGrantAccess((PetscObject)snes);
    331:   SNESLogConvergenceHistory(snes,fnorm,0);
    332:   SNESMonitor(snes,0,fnorm);

    334:   /* test convergence */
    335:   (*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP);
    336:   if (snes->reason) return(0);

    338:   /* Call general purpose update function */
    339:   if (snes->ops->update) {
    340:     (*snes->ops->update)(snes, snes->iter);
    341:   }

    343:   /* first update -- just use the (preconditioned) residual direction for the initial conjugate direction */

    345:   for (i = 1; i < maxits + 1; i++) {
    346:     /* some update types require the old update direction or conjugate direction */
    347:     if (ncg->type != SNES_NCG_FR) {
    348:       VecCopy(dX, dXold);
    349:     }
    350:     SNESLineSearchApply(linesearch,X,F,&fnorm,lX);
    351:     SNESLineSearchGetReason(linesearch, &lsresult);
    352:     SNESLineSearchGetNorms(linesearch, &xnorm, &fnorm, &ynorm);
    353:     if (lsresult) {
    354:       if (++snes->numFailures >= snes->maxFailures) {
    355:         snes->reason = SNES_DIVERGED_LINE_SEARCH;
    356:         return(0);
    357:       }
    358:     }
    359:     if (snes->nfuncs >= snes->max_funcs && snes->max_funcs >= 0) {
    360:       snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
    361:       return(0);
    362:     }
    363:     /* Monitor convergence */
    364:     PetscObjectSAWsTakeAccess((PetscObject)snes);
    365:     snes->iter = i;
    366:     snes->norm = fnorm;
    367:     snes->xnorm = xnorm;
    368:     snes->ynorm = ynorm;
    369:     PetscObjectSAWsGrantAccess((PetscObject)snes);
    370:     SNESLogConvergenceHistory(snes,snes->norm,0);
    371:     SNESMonitor(snes,snes->iter,snes->norm);

    373:     /* Test for convergence */
    374:     (*snes->ops->converged)(snes,snes->iter,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP);
    375:     if (snes->reason) return(0);

    377:     /* Call general purpose update function */
    378:     if (snes->ops->update) {
    379:       (*snes->ops->update)(snes, snes->iter);
    380:     }
    381:     if (snes->npc) {
    382:       if (snes->functype == SNES_FUNCTION_PRECONDITIONED) {
    383:         SNESApplyNPC(snes,X,NULL,dX);
    384:         SNESGetConvergedReason(snes->npc,&reason);
    385:         if (reason < 0  && reason != SNES_DIVERGED_MAX_IT) {
    386:           snes->reason = SNES_DIVERGED_INNER;
    387:           return(0);
    388:         }
    389:         VecCopy(dX,F);
    390:       } else {
    391:         SNESApplyNPC(snes,X,F,dX);
    392:         SNESGetConvergedReason(snes->npc,&reason);
    393:         if (reason < 0  && reason != SNES_DIVERGED_MAX_IT) {
    394:           snes->reason = SNES_DIVERGED_INNER;
    395:           return(0);
    396:         }
    397:       }
    398:     } else {
    399:       VecCopy(F,dX);
    400:     }

    402:     /* compute the conjugate direction lX = dX + beta*lX with beta = ((dX, dX) / (dX_old, dX_old) (Fletcher-Reeves update)*/
    403:     switch (ncg->type) {
    404:     case SNES_NCG_FR: /* Fletcher-Reeves */
    405:       dXolddotdXold= dXdotdX;
    406:       VecDot(dX, dX, &dXdotdX);
    407:       beta         = PetscRealPart(dXdotdX / dXolddotdXold);
    408:       break;
    409:     case SNES_NCG_PRP: /* Polak-Ribiere-Poylak */
    410:       dXolddotdXold= dXdotdX;
    411:       VecDotBegin(dX, dX, &dXdotdXold);
    412:       VecDotBegin(dXold, dX, &dXdotdXold);
    413:       VecDotEnd(dX, dX, &dXdotdX);
    414:       VecDotEnd(dXold, dX, &dXdotdXold);
    415:       beta         = PetscRealPart(((dXdotdX - dXdotdXold) / dXolddotdXold));
    416:       if (beta < 0.0) beta = 0.0; /* restart */
    417:       break;
    418:     case SNES_NCG_HS: /* Hestenes-Stiefel */
    419:       VecDotBegin(dX, dX, &dXdotdX);
    420:       VecDotBegin(dX, dXold, &dXdotdXold);
    421:       VecDotBegin(lX, dX, &lXdotdX);
    422:       VecDotBegin(lX, dXold, &lXdotdXold);
    423:       VecDotEnd(dX, dX, &dXdotdX);
    424:       VecDotEnd(dX, dXold, &dXdotdXold);
    425:       VecDotEnd(lX, dX, &lXdotdX);
    426:       VecDotEnd(lX, dXold, &lXdotdXold);
    427:       beta = PetscRealPart((dXdotdX - dXdotdXold) / (lXdotdX - lXdotdXold));
    428:       break;
    429:     case SNES_NCG_DY: /* Dai-Yuan */
    430:       VecDotBegin(dX, dX, &dXdotdX);
    431:       VecDotBegin(lX, dX, &lXdotdX);
    432:       VecDotBegin(lX, dXold, &lXdotdXold);
    433:       VecDotEnd(dX, dX, &dXdotdX);
    434:       VecDotEnd(lX, dX, &lXdotdX);
    435:       VecDotEnd(lX, dXold, &lXdotdXold);
    436:       beta = PetscRealPart(dXdotdX / (lXdotdXold - lXdotdX));
    437:       break;
    438:     case SNES_NCG_CD: /* Conjugate Descent */
    439:       VecDotBegin(dX, dX, &dXdotdX);
    440:       VecDotBegin(lX, dXold, &lXdotdXold);
    441:       VecDotEnd(dX, dX, &dXdotdX);
    442:       VecDotEnd(lX, dXold, &lXdotdXold);
    443:       beta = PetscRealPart(dXdotdX / lXdotdXold);
    444:       break;
    445:     }
    446:     if (ncg->monitor) {
    447:       PetscViewerASCIIPrintf(ncg->monitor, "beta = %e\n", (double)beta);
    448:     }
    449:     VecAYPX(lX, beta, dX);
    450:   }
    451:   PetscInfo1(snes, "Maximum number of iterations has been reached: %D\n", maxits);
    452:   if (!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
    453:   return(0);
    454: }

    456: /*MC
    457:   SNESNCG - Nonlinear Conjugate-Gradient method for the solution of general nonlinear systems.

    459:   Level: beginner

    461:   Options Database:
    462: +   -snes_ncg_type <fr, prp, dy, hs, cd> - Choice of conjugate-gradient update parameter, default is prp.
    463: .   -snes_linesearch_type <cp,l2,basic> - Line search type.
    464: -   -snes_ncg_monitor - Print the beta values used in the ncg iteration, .

    466:    Notes:
    467:     This solves the nonlinear system of equations F(x) = 0 using the nonlinear generalization of the conjugate
    468:           gradient method.  This may be used with a nonlinear preconditioner used to pick the new search directions, but otherwise
    469:           chooses the initial search direction as F(x) for the initial guess x.

    471:           Only supports left non-linear preconditioning.

    473:     Default line search is SNESLINESEARCHCP, unless a nonlinear preconditioner is used with -npc_snes_type <type>, SNESSetNPC(), or SNESGetNPC() then
    474:     SNESLINESEARCHL2 is used. Also supports the special purpose line search SNESLINESEARCHNCGLINEAR

    476:    References:
    477: .  1. -  Peter R. Brune, Matthew G. Knepley, Barry F. Smith, and Xuemin Tu,"Composing Scalable Nonlinear Algebraic Solvers",
    478:    SIAM Review, 57(4), 2015

    480: .seealso:  SNESCreate(), SNES, SNESSetType(), SNESNEWTONLS, SNESNEWTONTR, SNESNGMRES, SNESQN, SNESLINESEARCHNCGLINEAR, SNESNCGSetType(), SNESNCGGetType(), SNESLineSearchSetType()
    481: M*/
    482: PETSC_EXTERN PetscErrorCode SNESCreate_NCG(SNES snes)
    483: {
    485:   SNES_NCG       * neP;

    488:   snes->ops->destroy        = SNESDestroy_NCG;
    489:   snes->ops->setup          = SNESSetUp_NCG;
    490:   snes->ops->setfromoptions = SNESSetFromOptions_NCG;
    491:   snes->ops->view           = SNESView_NCG;
    492:   snes->ops->solve          = SNESSolve_NCG;
    493:   snes->ops->reset          = SNESReset_NCG;

    495:   snes->usesksp = PETSC_FALSE;
    496:   snes->usesnpc = PETSC_TRUE;
    497:   snes->npcside = PC_LEFT;

    499:   snes->alwayscomputesfinalresidual = PETSC_TRUE;

    501:   if (!snes->tolerancesset) {
    502:     snes->max_funcs = 30000;
    503:     snes->max_its   = 10000;
    504:     snes->stol      = 1e-20;
    505:   }

    507:   PetscNewLog(snes,&neP);
    508:   snes->data   = (void*) neP;
    509:   neP->monitor = NULL;
    510:   neP->type    = SNES_NCG_PRP;
    511:   PetscObjectComposeFunction((PetscObject)snes,"SNESNCGSetType_C", SNESNCGSetType_NCG);
    512:   return(0);
    513: }