xref: /petsc/src/snes/impls/ncg/snesncg.c (revision ac530a7e429a3ef5a9263376acf6071236a5db98)
10a844d1aSPeter Brune #include <../src/snes/impls/ncg/snesncgimpl.h> /*I "petscsnes.h" I*/
29e5d0892SLisandro Dalcin const char *const SNESNCGTypes[] = {"FR", "PRP", "HS", "DY", "CD", "SNESNCGType", "SNES_NCG_", NULL};
3fef7b6d8SPeter Brune 
4d71ae5a4SJacob Faibussowitsch static PetscErrorCode SNESDestroy_NCG(SNES snes)
5d71ae5a4SJacob Faibussowitsch {
6fef7b6d8SPeter Brune   PetscFunctionBegin;
72e956fe4SStefano Zampini   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNCGSetType_C", NULL));
89566063dSJacob Faibussowitsch   PetscCall(PetscFree(snes->data));
93ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
10fef7b6d8SPeter Brune }
11fef7b6d8SPeter Brune 
12d71ae5a4SJacob Faibussowitsch static PetscErrorCode SNESSetUp_NCG(SNES snes)
13d71ae5a4SJacob Faibussowitsch {
14fef7b6d8SPeter Brune   PetscFunctionBegin;
159566063dSJacob Faibussowitsch   PetscCall(SNESSetWorkVecs(snes, 2));
1608401ef6SPierre Jolivet   PetscCheck(snes->npcside != PC_RIGHT, PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_WRONGSTATE, "SNESNCG only supports left preconditioning");
176c67d002SPeter Brune   if (snes->functype == SNES_FUNCTION_DEFAULT) snes->functype = SNES_FUNCTION_UNPRECONDITIONED;
183ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
19fef7b6d8SPeter Brune }
20fef7b6d8SPeter Brune 
21d71ae5a4SJacob Faibussowitsch static PetscErrorCode SNESLineSearchApply_NCGLinear(SNESLineSearch linesearch)
22d71ae5a4SJacob Faibussowitsch {
23ea630c6eSPeter Brune   PetscScalar alpha, ptAp;
24bbd5d0b3SPeter Brune   Vec         X, Y, F, W;
25bbd5d0b3SPeter Brune   SNES        snes;
26bbd5d0b3SPeter Brune   PetscReal  *fnorm, *xnorm, *ynorm;
27ea630c6eSPeter Brune 
28ea630c6eSPeter Brune   PetscFunctionBegin;
299566063dSJacob Faibussowitsch   PetscCall(SNESLineSearchGetSNES(linesearch, &snes));
30bbd5d0b3SPeter Brune   X     = linesearch->vec_sol;
31bbd5d0b3SPeter Brune   W     = linesearch->vec_sol_new;
32bbd5d0b3SPeter Brune   F     = linesearch->vec_func;
33bbd5d0b3SPeter Brune   Y     = linesearch->vec_update;
34bbd5d0b3SPeter Brune   fnorm = &linesearch->fnorm;
35bbd5d0b3SPeter Brune   xnorm = &linesearch->xnorm;
36bbd5d0b3SPeter Brune   ynorm = &linesearch->ynorm;
37bbd5d0b3SPeter Brune 
3848a46eb9SPierre Jolivet   if (!snes->jacobian) PetscCall(SNESSetUpMatrices(snes));
399105210eSPeter Brune 
40ea630c6eSPeter Brune   /*
41d2140ca3SPeter Brune    The exact step size for unpreconditioned linear CG is just:
42ea630c6eSPeter Brune    alpha = (r, r) / (p, Ap) = (f, f) / (y, Jy)
43ea630c6eSPeter Brune    */
449566063dSJacob Faibussowitsch   PetscCall(SNESComputeJacobian(snes, X, snes->jacobian, snes->jacobian_pre));
459566063dSJacob Faibussowitsch   PetscCall(VecDot(F, F, &alpha));
469566063dSJacob Faibussowitsch   PetscCall(MatMult(snes->jacobian, Y, W));
479566063dSJacob Faibussowitsch   PetscCall(VecDot(Y, W, &ptAp));
48ea630c6eSPeter Brune   alpha = alpha / ptAp;
499566063dSJacob Faibussowitsch   PetscCall(VecAXPY(X, -alpha, Y));
509566063dSJacob Faibussowitsch   PetscCall(SNESComputeFunction(snes, X, F));
51bbd5d0b3SPeter Brune 
529566063dSJacob Faibussowitsch   PetscCall(VecNorm(F, NORM_2, fnorm));
539566063dSJacob Faibussowitsch   PetscCall(VecNorm(X, NORM_2, xnorm));
549566063dSJacob Faibussowitsch   PetscCall(VecNorm(Y, NORM_2, ynorm));
553ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
56ea630c6eSPeter Brune }
57ea630c6eSPeter Brune 
58b5badacbSBarry Smith /*MC
59f6dfbefdSBarry Smith    SNESLINESEARCHNCGLINEAR - Special line search only for the nonlinear CG solver `SNESNCG`
60b5badacbSBarry Smith 
61b5badacbSBarry Smith    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.
62b5badacbSBarry Smith    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.
63b5badacbSBarry Smith 
64420bcc1bSBarry Smith    Level: advanced
65420bcc1bSBarry Smith 
66f6dfbefdSBarry Smith    Notes:
67f6dfbefdSBarry Smith    This requires a Jacobian-vector product but does not require the solution of a linear system with the Jacobian
68b5badacbSBarry Smith 
69b5badacbSBarry Smith    This is a "odd-ball" line search, we don't know if it is in the literature or used in practice by anyone.
70b5badacbSBarry Smith 
710241b274SPierre Jolivet .seealso: [](ch_snes), `SNES`, `SNESNCG`, `SNESLineSearchCreate()`, `SNESLineSearchSetType()`
72b5badacbSBarry Smith M*/
73b5badacbSBarry Smith 
74d71ae5a4SJacob Faibussowitsch PETSC_EXTERN PetscErrorCode SNESLineSearchCreate_NCGLinear(SNESLineSearch linesearch)
75d71ae5a4SJacob Faibussowitsch {
76bbd5d0b3SPeter Brune   PetscFunctionBegin;
77f1c6b773SPeter Brune   linesearch->ops->apply          = SNESLineSearchApply_NCGLinear;
780298fd71SBarry Smith   linesearch->ops->destroy        = NULL;
790298fd71SBarry Smith   linesearch->ops->setfromoptions = NULL;
800298fd71SBarry Smith   linesearch->ops->reset          = NULL;
810298fd71SBarry Smith   linesearch->ops->view           = NULL;
820298fd71SBarry Smith   linesearch->ops->setup          = NULL;
833ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
84bbd5d0b3SPeter Brune }
85bbd5d0b3SPeter Brune 
86ce78bad3SBarry Smith static PetscErrorCode SNESSetFromOptions_NCG(SNES snes, PetscOptionItems PetscOptionsObject)
87d71ae5a4SJacob Faibussowitsch {
88b5badacbSBarry Smith   SNES_NCG      *ncg     = (SNES_NCG *)snes->data;
89b5badacbSBarry Smith   PetscBool      debug   = PETSC_FALSE;
90b5badacbSBarry Smith   SNESNCGType    ncgtype = ncg->type;
91b5badacbSBarry Smith   SNESLineSearch linesearch;
92b5badacbSBarry Smith 
93b5badacbSBarry Smith   PetscFunctionBegin;
94d0609cedSBarry Smith   PetscOptionsHeadBegin(PetscOptionsObject, "SNES NCG options");
959566063dSJacob Faibussowitsch   PetscCall(PetscOptionsBool("-snes_ncg_monitor", "Monitor the beta values used in the NCG iterations", "SNES", ncg->monitor ? PETSC_TRUE : PETSC_FALSE, &debug, NULL));
96ad540459SPierre Jolivet   if (debug) ncg->monitor = PETSC_VIEWER_STDOUT_(PetscObjectComm((PetscObject)snes));
979566063dSJacob Faibussowitsch   PetscCall(PetscOptionsEnum("-snes_ncg_type", "NCG Beta type used", "SNESNCGSetType", SNESNCGTypes, (PetscEnum)ncg->type, (PetscEnum *)&ncgtype, NULL));
989566063dSJacob Faibussowitsch   PetscCall(SNESNCGSetType(snes, ncgtype));
99d0609cedSBarry Smith   PetscOptionsHeadEnd();
100b5badacbSBarry Smith   if (!snes->linesearch) {
1019566063dSJacob Faibussowitsch     PetscCall(SNESGetLineSearch(snes, &linesearch));
102ec786807SJed Brown     if (!((PetscObject)linesearch)->type_name) {
103b5badacbSBarry Smith       if (!snes->npc) {
1049566063dSJacob Faibussowitsch         PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHCP));
105b5badacbSBarry Smith       } else {
106a99ef635SJonas Heinzmann         PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHSECANT));
107b5badacbSBarry Smith       }
108b5badacbSBarry Smith     }
109ec786807SJed Brown   }
1103ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
111b5badacbSBarry Smith }
112b5badacbSBarry Smith 
113d71ae5a4SJacob Faibussowitsch static PetscErrorCode SNESView_NCG(SNES snes, PetscViewer viewer)
114d71ae5a4SJacob Faibussowitsch {
115b5badacbSBarry Smith   SNES_NCG *ncg = (SNES_NCG *)snes->data;
1169f196a02SMartin Diehl   PetscBool isascii;
117b5badacbSBarry Smith 
118b5badacbSBarry Smith   PetscFunctionBegin;
1199f196a02SMartin Diehl   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
1209f196a02SMartin Diehl   if (isascii) PetscCall(PetscViewerASCIIPrintf(viewer, "  type: %s\n", SNESNCGTypes[ncg->type]));
1213ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
122b5badacbSBarry Smith }
123b5badacbSBarry Smith 
1240a844d1aSPeter Brune /*@
125f6dfbefdSBarry Smith   SNESNCGSetType - Sets the conjugate update type for nonlinear CG `SNESNCG`.
1260a844d1aSPeter Brune 
127c3339decSBarry Smith   Logically Collective
1280a844d1aSPeter Brune 
1290a844d1aSPeter Brune   Input Parameters:
1300a844d1aSPeter Brune + snes  - the iterative context
131ceaaa498SBarry Smith - btype - update type, see `SNESNCGType`
1320a844d1aSPeter Brune 
1333c7db156SBarry Smith   Options Database Key:
134b5badacbSBarry Smith . -snes_ncg_type <prp,fr,hs,dy,cd> - strategy for selecting algorithm for computing beta
1350a844d1aSPeter Brune 
1360a844d1aSPeter Brune   Level: intermediate
1370a844d1aSPeter Brune 
1380a844d1aSPeter Brune   Notes:
139f6dfbefdSBarry Smith   `SNES_NCG_PRP` is the default, and the only one that tolerates generalized search directions.
140b5badacbSBarry Smith 
141b5badacbSBarry Smith   It is not clear what "generalized search directions" means, does it mean use with a nonlinear preconditioner,
142f6dfbefdSBarry Smith   that is using -npc_snes_type <type>, `SNESSetNPC()`, or `SNESGetNPC()`?
1430a844d1aSPeter Brune 
144420bcc1bSBarry Smith .seealso: [](ch_snes), `SNES`, `SNESNCG`, `SNESNCGType`, `SNES_NCG_FR`, `SNES_NCG_PRP`, `SNES_NCG_HS`, `SNES_NCG_DY`, `SNES_NCG_CD`
1450a844d1aSPeter Brune @*/
146d71ae5a4SJacob Faibussowitsch PetscErrorCode SNESNCGSetType(SNES snes, SNESNCGType btype)
147d71ae5a4SJacob Faibussowitsch {
1480a844d1aSPeter Brune   PetscFunctionBegin;
1490a844d1aSPeter Brune   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
150cac4c232SBarry Smith   PetscTryMethod(snes, "SNESNCGSetType_C", (SNES, SNESNCGType), (snes, btype));
1513ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1520a844d1aSPeter Brune }
1530a844d1aSPeter Brune 
154d71ae5a4SJacob Faibussowitsch static PetscErrorCode SNESNCGSetType_NCG(SNES snes, SNESNCGType btype)
155d71ae5a4SJacob Faibussowitsch {
1560a844d1aSPeter Brune   SNES_NCG *ncg = (SNES_NCG *)snes->data;
1576e111a19SKarl Rupp 
1580a844d1aSPeter Brune   PetscFunctionBegin;
1590a844d1aSPeter Brune   ncg->type = btype;
1603ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1610a844d1aSPeter Brune }
1620a844d1aSPeter Brune 
163fef7b6d8SPeter Brune /*
164fef7b6d8SPeter Brune   SNESSolve_NCG - Solves a nonlinear system with the Nonlinear Conjugate Gradient method.
165fef7b6d8SPeter Brune 
1662fe279fdSBarry Smith   Input Parameter:
1672fe279fdSBarry Smith . snes - the `SNES` context
168fef7b6d8SPeter Brune 
169fef7b6d8SPeter Brune   Output Parameter:
170fef7b6d8SPeter Brune . outits - number of iterations until termination
171fef7b6d8SPeter Brune 
172fef7b6d8SPeter Brune   Application Interface Routine: SNESSolve()
173fef7b6d8SPeter Brune */
174d71ae5a4SJacob Faibussowitsch static PetscErrorCode SNESSolve_NCG(SNES snes)
175d71ae5a4SJacob Faibussowitsch {
176dfb256c7SPeter Brune   SNES_NCG            *ncg = (SNES_NCG *)snes->data;
17732cc618eSPeter Brune   Vec                  X, dX, lX, F, dXold;
178bbd5d0b3SPeter Brune   PetscReal            fnorm, ynorm, xnorm, beta = 0.0;
17932cc618eSPeter Brune   PetscScalar          dXdotdX, dXolddotdXold, dXdotdXold, lXdotdX, lXdotdXold;
180fef7b6d8SPeter Brune   PetscInt             maxits, i;
181422a814eSBarry Smith   SNESLineSearchReason lsresult = SNES_LINESEARCH_SUCCEEDED;
182f1c6b773SPeter Brune   SNESLineSearch       linesearch;
183b7281c8aSPeter Brune   SNESConvergedReason  reason;
18488d374b2SPeter Brune 
185fef7b6d8SPeter Brune   PetscFunctionBegin;
1860b121fc5SBarry Smith   PetscCheck(!snes->xl && !snes->xu && !snes->ops->computevariablebounds, PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_WRONGSTATE, "SNES solver %s does not support bounds", ((PetscObject)snes)->type_name);
187c579b300SPatrick Farrell 
1889566063dSJacob Faibussowitsch   PetscCall(PetscCitationsRegister(SNESCitation, &SNEScite));
189fef7b6d8SPeter Brune   snes->reason = SNES_CONVERGED_ITERATING;
190fef7b6d8SPeter Brune 
191fef7b6d8SPeter Brune   maxits = snes->max_its;        /* maximum number of iterations */
192fef7b6d8SPeter Brune   X      = snes->vec_sol;        /* X^n */
19332cc618eSPeter Brune   dXold  = snes->work[0];        /* The previous iterate of X */
19488d374b2SPeter Brune   dX     = snes->work[1];        /* the preconditioned direction */
195169e2be8SPeter Brune   lX     = snes->vec_sol_update; /* the conjugate direction */
196fef7b6d8SPeter Brune   F      = snes->vec_func;       /* residual vector */
197fef7b6d8SPeter Brune 
1989566063dSJacob Faibussowitsch   PetscCall(SNESGetLineSearch(snes, &linesearch));
1999e764e56SPeter Brune 
2009566063dSJacob Faibussowitsch   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
201fef7b6d8SPeter Brune   snes->iter = 0;
202fef7b6d8SPeter Brune   snes->norm = 0.;
2039566063dSJacob Faibussowitsch   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
204fef7b6d8SPeter Brune 
205bbd5d0b3SPeter Brune   /* compute the initial function and preconditioned update dX */
206a71552e2SPeter Brune 
207efd4aadfSBarry Smith   if (snes->npc && snes->functype == SNES_FUNCTION_PRECONDITIONED) {
2089566063dSJacob Faibussowitsch     PetscCall(SNESApplyNPC(snes, X, NULL, dX));
2099566063dSJacob Faibussowitsch     PetscCall(SNESGetConvergedReason(snes->npc, &reason));
210b7281c8aSPeter Brune     if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
211b7281c8aSPeter Brune       snes->reason = SNES_DIVERGED_INNER;
2123ba16761SJacob Faibussowitsch       PetscFunctionReturn(PETSC_SUCCESS);
213b7281c8aSPeter Brune     }
2149566063dSJacob Faibussowitsch     PetscCall(VecCopy(dX, F));
2159566063dSJacob Faibussowitsch     PetscCall(VecNorm(F, NORM_2, &fnorm));
216a71552e2SPeter Brune   } else {
217*ac530a7eSPierre Jolivet     if (!snes->vec_func_init_set) PetscCall(SNESComputeFunction(snes, X, F));
218*ac530a7eSPierre Jolivet     else snes->vec_func_init_set = PETSC_FALSE;
219c1c75074SPeter Brune 
220fef7b6d8SPeter Brune     /* convergence test */
2219566063dSJacob Faibussowitsch     PetscCall(VecNorm(F, NORM_2, &fnorm));
222422a814eSBarry Smith     SNESCheckFunctionNorm(snes, fnorm);
2239566063dSJacob Faibussowitsch     PetscCall(VecCopy(F, dX));
224a71552e2SPeter Brune   }
225efd4aadfSBarry Smith   if (snes->npc) {
226a71552e2SPeter Brune     if (snes->functype == SNES_FUNCTION_UNPRECONDITIONED) {
2279566063dSJacob Faibussowitsch       PetscCall(SNESApplyNPC(snes, X, F, dX));
2289566063dSJacob Faibussowitsch       PetscCall(SNESGetConvergedReason(snes->npc, &reason));
229b7281c8aSPeter Brune       if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
230b7281c8aSPeter Brune         snes->reason = SNES_DIVERGED_INNER;
2313ba16761SJacob Faibussowitsch         PetscFunctionReturn(PETSC_SUCCESS);
232b7281c8aSPeter Brune       }
233a71552e2SPeter Brune     }
234a71552e2SPeter Brune   }
2359566063dSJacob Faibussowitsch   PetscCall(VecCopy(dX, lX));
2369566063dSJacob Faibussowitsch   PetscCall(VecDot(dX, dX, &dXdotdX));
237a71552e2SPeter Brune 
2389566063dSJacob Faibussowitsch   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
239fef7b6d8SPeter Brune   snes->norm = fnorm;
2409566063dSJacob Faibussowitsch   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
2419566063dSJacob Faibussowitsch   PetscCall(SNESLogConvergenceHistory(snes, fnorm, 0));
242fef7b6d8SPeter Brune 
243fef7b6d8SPeter Brune   /* test convergence */
2442d157150SStefano Zampini   PetscCall(SNESConverged(snes, 0, 0.0, 0.0, fnorm));
2452d157150SStefano Zampini   PetscCall(SNESMonitor(snes, 0, fnorm));
2463ba16761SJacob Faibussowitsch   if (snes->reason) PetscFunctionReturn(PETSC_SUCCESS);
247fef7b6d8SPeter Brune 
248fef7b6d8SPeter Brune   /* Call general purpose update function */
249dbbe0bcdSBarry Smith   PetscTryTypeMethod(snes, update, snes->iter);
250fef7b6d8SPeter Brune 
251fef7b6d8SPeter Brune   /* first update -- just use the (preconditioned) residual direction for the initial conjugate direction */
252bbd5d0b3SPeter Brune 
25309c08436SPeter Brune   for (i = 1; i < maxits + 1; i++) {
25429c94e34SPeter Brune     /* some update types require the old update direction or conjugate direction */
25548a46eb9SPierre Jolivet     if (ncg->type != SNES_NCG_FR) PetscCall(VecCopy(dX, dXold));
2569566063dSJacob Faibussowitsch     PetscCall(SNESLineSearchApply(linesearch, X, F, &fnorm, lX));
2579566063dSJacob Faibussowitsch     PetscCall(SNESLineSearchGetReason(linesearch, &lsresult));
2589566063dSJacob Faibussowitsch     PetscCall(SNESLineSearchGetNorms(linesearch, &xnorm, &fnorm, &ynorm));
259422a814eSBarry Smith     if (lsresult) {
260fef7b6d8SPeter Brune       if (++snes->numFailures >= snes->maxFailures) {
261fef7b6d8SPeter Brune         snes->reason = SNES_DIVERGED_LINE_SEARCH;
2623ba16761SJacob Faibussowitsch         PetscFunctionReturn(PETSC_SUCCESS);
263fef7b6d8SPeter Brune       }
264fef7b6d8SPeter Brune     }
265e71169deSBarry Smith     if (snes->nfuncs >= snes->max_funcs && snes->max_funcs >= 0) {
266fef7b6d8SPeter Brune       snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
2673ba16761SJacob Faibussowitsch       PetscFunctionReturn(PETSC_SUCCESS);
268fef7b6d8SPeter Brune     }
269fef7b6d8SPeter Brune     /* Monitor convergence */
2709566063dSJacob Faibussowitsch     PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
271169e2be8SPeter Brune     snes->iter  = i;
272fef7b6d8SPeter Brune     snes->norm  = fnorm;
273c1e67a49SFande Kong     snes->xnorm = xnorm;
274c1e67a49SFande Kong     snes->ynorm = ynorm;
2759566063dSJacob Faibussowitsch     PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
2769566063dSJacob Faibussowitsch     PetscCall(SNESLogConvergenceHistory(snes, snes->norm, 0));
277302dbbaeSPeter Brune 
278fef7b6d8SPeter Brune     /* Test for convergence */
2792d157150SStefano Zampini     PetscCall(SNESConverged(snes, snes->iter, xnorm, ynorm, fnorm));
2802d157150SStefano Zampini     PetscCall(SNESMonitor(snes, snes->iter, snes->norm));
2813ba16761SJacob Faibussowitsch     if (snes->reason) PetscFunctionReturn(PETSC_SUCCESS);
282302dbbaeSPeter Brune 
283302dbbaeSPeter Brune     /* Call general purpose update function */
284dbbe0bcdSBarry Smith     PetscTryTypeMethod(snes, update, snes->iter);
285efd4aadfSBarry Smith     if (snes->npc) {
286a71552e2SPeter Brune       if (snes->functype == SNES_FUNCTION_PRECONDITIONED) {
2879566063dSJacob Faibussowitsch         PetscCall(SNESApplyNPC(snes, X, NULL, dX));
2889566063dSJacob Faibussowitsch         PetscCall(SNESGetConvergedReason(snes->npc, &reason));
289b7281c8aSPeter Brune         if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
290b7281c8aSPeter Brune           snes->reason = SNES_DIVERGED_INNER;
2913ba16761SJacob Faibussowitsch           PetscFunctionReturn(PETSC_SUCCESS);
292b7281c8aSPeter Brune         }
2939566063dSJacob Faibussowitsch         PetscCall(VecCopy(dX, F));
294a71552e2SPeter Brune       } else {
2959566063dSJacob Faibussowitsch         PetscCall(SNESApplyNPC(snes, X, F, dX));
2969566063dSJacob Faibussowitsch         PetscCall(SNESGetConvergedReason(snes->npc, &reason));
297b7281c8aSPeter Brune         if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
298b7281c8aSPeter Brune           snes->reason = SNES_DIVERGED_INNER;
2993ba16761SJacob Faibussowitsch           PetscFunctionReturn(PETSC_SUCCESS);
300b7281c8aSPeter Brune         }
301302dbbaeSPeter Brune       }
302a3685310SPeter Brune     } else {
3039566063dSJacob Faibussowitsch       PetscCall(VecCopy(F, dX));
304302dbbaeSPeter Brune     }
305302dbbaeSPeter Brune 
30629c94e34SPeter Brune     /* compute the conjugate direction lX = dX + beta*lX with beta = ((dX, dX) / (dX_old, dX_old) (Fletcher-Reeves update)*/
3070a844d1aSPeter Brune     switch (ncg->type) {
3080a844d1aSPeter Brune     case SNES_NCG_FR: /* Fletcher-Reeves */
30932cc618eSPeter Brune       dXolddotdXold = dXdotdX;
3109566063dSJacob Faibussowitsch       PetscCall(VecDot(dX, dX, &dXdotdX));
31132cc618eSPeter Brune       beta = PetscRealPart(dXdotdX / dXolddotdXold);
312dfb256c7SPeter Brune       break;
3130a844d1aSPeter Brune     case SNES_NCG_PRP: /* Polak-Ribiere-Poylak */
31432cc618eSPeter Brune       dXolddotdXold = dXdotdX;
315d3981886SPierre Jolivet       PetscCall(VecDotBegin(dX, dX, &dXdotdX));
3169566063dSJacob Faibussowitsch       PetscCall(VecDotBegin(dXold, dX, &dXdotdXold));
3179566063dSJacob Faibussowitsch       PetscCall(VecDotEnd(dX, dX, &dXdotdX));
3189566063dSJacob Faibussowitsch       PetscCall(VecDotEnd(dXold, dX, &dXdotdXold));
319f4f49eeaSPierre Jolivet       beta = PetscRealPart((dXdotdX - dXdotdXold) / dXolddotdXold);
320dfb256c7SPeter Brune       if (beta < 0.0) beta = 0.0; /* restart */
321dfb256c7SPeter Brune       break;
3220a844d1aSPeter Brune     case SNES_NCG_HS: /* Hestenes-Stiefel */
3239566063dSJacob Faibussowitsch       PetscCall(VecDotBegin(dX, dX, &dXdotdX));
3249566063dSJacob Faibussowitsch       PetscCall(VecDotBegin(dX, dXold, &dXdotdXold));
3259566063dSJacob Faibussowitsch       PetscCall(VecDotBegin(lX, dX, &lXdotdX));
3269566063dSJacob Faibussowitsch       PetscCall(VecDotBegin(lX, dXold, &lXdotdXold));
3279566063dSJacob Faibussowitsch       PetscCall(VecDotEnd(dX, dX, &dXdotdX));
3289566063dSJacob Faibussowitsch       PetscCall(VecDotEnd(dX, dXold, &dXdotdXold));
3299566063dSJacob Faibussowitsch       PetscCall(VecDotEnd(lX, dX, &lXdotdX));
3309566063dSJacob Faibussowitsch       PetscCall(VecDotEnd(lX, dXold, &lXdotdXold));
33132cc618eSPeter Brune       beta = PetscRealPart((dXdotdX - dXdotdXold) / (lXdotdX - lXdotdXold));
332dfb256c7SPeter Brune       break;
3330a844d1aSPeter Brune     case SNES_NCG_DY: /* Dai-Yuan */
3349566063dSJacob Faibussowitsch       PetscCall(VecDotBegin(dX, dX, &dXdotdX));
3359566063dSJacob Faibussowitsch       PetscCall(VecDotBegin(lX, dX, &lXdotdX));
3369566063dSJacob Faibussowitsch       PetscCall(VecDotBegin(lX, dXold, &lXdotdXold));
3379566063dSJacob Faibussowitsch       PetscCall(VecDotEnd(dX, dX, &dXdotdX));
3389566063dSJacob Faibussowitsch       PetscCall(VecDotEnd(lX, dX, &lXdotdX));
3399566063dSJacob Faibussowitsch       PetscCall(VecDotEnd(lX, dXold, &lXdotdXold));
3402f613bf5SBarry Smith       beta = PetscRealPart(dXdotdX / (lXdotdXold - lXdotdX));
341dfb256c7SPeter Brune       break;
3420a844d1aSPeter Brune     case SNES_NCG_CD: /* Conjugate Descent */
3439566063dSJacob Faibussowitsch       PetscCall(VecDotBegin(dX, dX, &dXdotdX));
3449566063dSJacob Faibussowitsch       PetscCall(VecDotBegin(lX, dXold, &lXdotdXold));
3459566063dSJacob Faibussowitsch       PetscCall(VecDotEnd(dX, dX, &dXdotdX));
3469566063dSJacob Faibussowitsch       PetscCall(VecDotEnd(lX, dXold, &lXdotdXold));
3472f613bf5SBarry Smith       beta = PetscRealPart(dXdotdX / lXdotdXold);
348dfb256c7SPeter Brune       break;
349dfb256c7SPeter Brune     }
35048a46eb9SPierre Jolivet     if (ncg->monitor) PetscCall(PetscViewerASCIIPrintf(ncg->monitor, "beta = %e\n", (double)beta));
3519566063dSJacob Faibussowitsch     PetscCall(VecAYPX(lX, beta, dX));
352fef7b6d8SPeter Brune   }
3533ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
354fef7b6d8SPeter Brune }
355fef7b6d8SPeter Brune 
356fef7b6d8SPeter Brune /*MC
3571d27aa22SBarry Smith   SNESNCG - Nonlinear Conjugate-Gradient method for the solution of nonlinear systems {cite}`bruneknepleysmithtu15`.
358fef7b6d8SPeter Brune 
359fef7b6d8SPeter Brune   Level: beginner
360fef7b6d8SPeter Brune 
361f6dfbefdSBarry Smith   Options Database Keys:
36262842358SBarry Smith +   -snes_ncg_type <fr, prp, dy, hs, cd> - Choice of conjugate-gradient update parameter, default is `prp`.
36341a8cb50SPeter Brune .   -snes_linesearch_type <cp,l2,basic>  - Line search type.
3641d27aa22SBarry Smith -   -snes_ncg_monitor                    - Print the beta values nonlinear Conjugate-Gradient used in the  iteration, .
365fef7b6d8SPeter Brune 
36695452b02SPatrick Sanan    Notes:
3671d27aa22SBarry Smith    This solves the nonlinear system of equations $ F(x) = 0 $ using the nonlinear generalization of the conjugate
368fef7b6d8SPeter Brune    gradient method.  This may be used with a nonlinear preconditioner used to pick the new search directions, but otherwise
3691d27aa22SBarry Smith    chooses the initial search direction as $ F(x) $ for the initial guess $x$.
370fef7b6d8SPeter Brune 
3712d547940SBarry Smith    Only supports left non-linear preconditioning.
3722d547940SBarry Smith 
37362842358SBarry Smith    Default line search is `SNESLINESEARCHCP`, unless a nonlinear preconditioner is used with `-npc_snes_type` <type>, `SNESSetNPC()`, or `SNESGetNPC()` then
374a99ef635SJonas Heinzmann    `SNESLINESEARCHSECANT` is used. Also supports the special-purpose line search `SNESLINESEARCHNCGLINEAR`
375b5badacbSBarry Smith 
376420bcc1bSBarry Smith .seealso: [](ch_snes), `SNES`, `SNESNCG`, `SNESCreate()`, `SNES`, `SNESSetType()`, `SNESNEWTONLS`, `SNESNEWTONTR`, `SNESNGMRES`, `SNESQN`, `SNESLINESEARCHNCGLINEAR`, `SNESNCGSetType()`, `SNESLineSearchSetType()`
377fef7b6d8SPeter Brune M*/
378d71ae5a4SJacob Faibussowitsch PETSC_EXTERN PetscErrorCode SNESCreate_NCG(SNES snes)
379d71ae5a4SJacob Faibussowitsch {
380ea630c6eSPeter Brune   SNES_NCG *neP;
381fef7b6d8SPeter Brune 
382fef7b6d8SPeter Brune   PetscFunctionBegin;
383fef7b6d8SPeter Brune   snes->ops->destroy        = SNESDestroy_NCG;
384fef7b6d8SPeter Brune   snes->ops->setup          = SNESSetUp_NCG;
385fef7b6d8SPeter Brune   snes->ops->setfromoptions = SNESSetFromOptions_NCG;
386fef7b6d8SPeter Brune   snes->ops->view           = SNESView_NCG;
387fef7b6d8SPeter Brune   snes->ops->solve          = SNESSolve_NCG;
388fef7b6d8SPeter Brune 
389fef7b6d8SPeter Brune   snes->usesksp = PETSC_FALSE;
390efd4aadfSBarry Smith   snes->usesnpc = PETSC_TRUE;
391efd4aadfSBarry Smith   snes->npcside = PC_LEFT;
392fef7b6d8SPeter Brune 
3934fc747eaSLawrence Mitchell   snes->alwayscomputesfinalresidual = PETSC_TRUE;
3944fc747eaSLawrence Mitchell 
39577e5a1f9SBarry Smith   PetscCall(SNESParametersInitialize(snes));
39677e5a1f9SBarry Smith   PetscObjectParameterSetDefault(snes, max_funcs, 30000);
39777e5a1f9SBarry Smith   PetscObjectParameterSetDefault(snes, max_its, 10000);
39877e5a1f9SBarry Smith   PetscObjectParameterSetDefault(snes, stol, 1e-20);
3990e444f03SPeter Brune 
4004dfa11a4SJacob Faibussowitsch   PetscCall(PetscNew(&neP));
401fef7b6d8SPeter Brune   snes->data   = (void *)neP;
4020298fd71SBarry Smith   neP->monitor = NULL;
4030a844d1aSPeter Brune   neP->type    = SNES_NCG_PRP;
4049566063dSJacob Faibussowitsch   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNCGSetType_C", SNESNCGSetType_NCG));
4053ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
406fef7b6d8SPeter Brune }
407