xref: /petsc/src/snes/impls/tr/tr.c (revision 1d27aa22b2f6148b2c4e3f06a75e0638d6493e09)
1c6db04a5SJed Brown #include <../src/snes/impls/tr/trimpl.h> /*I   "petscsnes.h"   I*/
24800dd8cSBarry Smith 
3971273eeSBarry Smith typedef struct {
4971273eeSBarry Smith   SNES snes;
5df8705c3SBarry Smith   PetscErrorCode (*convtest)(KSP, PetscInt, PetscReal, KSPConvergedReason *, void *);
6df8705c3SBarry Smith   PetscErrorCode (*convdestroy)(void *);
7df8705c3SBarry Smith   void *convctx;
8971273eeSBarry Smith } SNES_TR_KSPConverged_Ctx;
9971273eeSBarry Smith 
104a221d59SStefano Zampini const char *const SNESNewtonTRFallbackTypes[] = {"NEWTON", "CAUCHY", "DOGLEG", "SNESNewtonTRFallbackType", "SNES_TR_FALLBACK_", NULL};
114a221d59SStefano Zampini 
12d71ae5a4SJacob Faibussowitsch static PetscErrorCode SNESTR_KSPConverged_Private(KSP ksp, PetscInt n, PetscReal rnorm, KSPConvergedReason *reason, void *cctx)
13d71ae5a4SJacob Faibussowitsch {
14971273eeSBarry Smith   SNES_TR_KSPConverged_Ctx *ctx  = (SNES_TR_KSPConverged_Ctx *)cctx;
15971273eeSBarry Smith   SNES                      snes = ctx->snes;
1604d7464bSBarry Smith   SNES_NEWTONTR            *neP  = (SNES_NEWTONTR *)snes->data;
17df60cc22SBarry Smith   Vec                       x;
18064f8208SBarry Smith   PetscReal                 nrm;
19df60cc22SBarry Smith 
203a40ed3dSBarry Smith   PetscFunctionBegin;
219566063dSJacob Faibussowitsch   PetscCall((*ctx->convtest)(ksp, n, rnorm, reason, ctx->convctx));
2248a46eb9SPierre Jolivet   if (*reason) PetscCall(PetscInfo(snes, "Default or user provided convergence test KSP iterations=%" PetscInt_FMT ", rnorm=%g\n", n, (double)rnorm));
23a935fc98SLois Curfman McInnes   /* Determine norm of solution */
249566063dSJacob Faibussowitsch   PetscCall(KSPBuildSolution(ksp, NULL, &x));
259566063dSJacob Faibussowitsch   PetscCall(VecNorm(x, NORM_2, &nrm));
26064f8208SBarry Smith   if (nrm >= neP->delta) {
279566063dSJacob Faibussowitsch     PetscCall(PetscInfo(snes, "Ending linear iteration early, delta=%g, length=%g\n", (double)neP->delta, (double)nrm));
28329f5518SBarry Smith     *reason = KSP_CONVERGED_STEP_LENGTH;
29df60cc22SBarry Smith   }
303ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
31df60cc22SBarry Smith }
3282bf6240SBarry Smith 
33d71ae5a4SJacob Faibussowitsch static PetscErrorCode SNESTR_KSPConverged_Destroy(void *cctx)
34d71ae5a4SJacob Faibussowitsch {
35971273eeSBarry Smith   SNES_TR_KSPConverged_Ctx *ctx = (SNES_TR_KSPConverged_Ctx *)cctx;
36971273eeSBarry Smith 
37971273eeSBarry Smith   PetscFunctionBegin;
389566063dSJacob Faibussowitsch   PetscCall((*ctx->convdestroy)(ctx->convctx));
399566063dSJacob Faibussowitsch   PetscCall(PetscFree(ctx));
403ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
41971273eeSBarry Smith }
42971273eeSBarry Smith 
43d71ae5a4SJacob Faibussowitsch static PetscErrorCode SNESTR_Converged_Private(SNES snes, PetscInt it, PetscReal xnorm, PetscReal pnorm, PetscReal fnorm, SNESConvergedReason *reason, void *dummy)
44d71ae5a4SJacob Faibussowitsch {
4504d7464bSBarry Smith   SNES_NEWTONTR *neP = (SNES_NEWTONTR *)snes->data;
4685385478SLisandro Dalcin 
4785385478SLisandro Dalcin   PetscFunctionBegin;
4885385478SLisandro Dalcin   *reason = SNES_CONVERGED_ITERATING;
494a221d59SStefano Zampini   if (neP->delta < snes->deltatol) {
504a221d59SStefano Zampini     PetscCall(PetscInfo(snes, "Diverged due to too small a trust region %g<%g\n", (double)neP->delta, (double)snes->deltatol));
511c6b2ff8SBarry Smith     *reason = SNES_DIVERGED_TR_DELTA;
52e71169deSBarry Smith   } else if (snes->nfuncs >= snes->max_funcs && snes->max_funcs >= 0) {
5363a3b9bcSJacob Faibussowitsch     PetscCall(PetscInfo(snes, "Exceeded maximum number of function evaluations: %" PetscInt_FMT "\n", snes->max_funcs));
5485385478SLisandro Dalcin     *reason = SNES_DIVERGED_FUNCTION_COUNT;
5585385478SLisandro Dalcin   }
563ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
5785385478SLisandro Dalcin }
5885385478SLisandro Dalcin 
594a221d59SStefano Zampini /*@
60420bcc1bSBarry Smith   SNESNewtonTRSetFallbackType - Set the type of fallback to use if the solution of the trust region subproblem is outside the radius
614a221d59SStefano Zampini 
624a221d59SStefano Zampini   Input Parameters:
634a221d59SStefano Zampini + snes  - the nonlinear solver object
644a221d59SStefano Zampini - ftype - the fallback type, see `SNESNewtonTRFallbackType`
654a221d59SStefano Zampini 
664a221d59SStefano Zampini   Level: intermediate
674a221d59SStefano Zampini 
68420bcc1bSBarry Smith .seealso: [](ch_snes), `SNESNEWTONTR`, `SNESNewtonTRPreCheck()`, `SNESNewtonTRGetPreCheck()`, `SNESNewtonTRSetPreCheck()`,
694a221d59SStefano Zampini           `SNESNewtonTRSetPostCheck()`, `SNESNewtonTRGetPostCheck()`
704a221d59SStefano Zampini @*/
714a221d59SStefano Zampini PetscErrorCode SNESNewtonTRSetFallbackType(SNES snes, SNESNewtonTRFallbackType ftype)
724a221d59SStefano Zampini {
734a221d59SStefano Zampini   SNES_NEWTONTR *tr = (SNES_NEWTONTR *)snes->data;
744a221d59SStefano Zampini   PetscBool      flg;
754a221d59SStefano Zampini 
764a221d59SStefano Zampini   PetscFunctionBegin;
774a221d59SStefano Zampini   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
784a221d59SStefano Zampini   PetscValidLogicalCollectiveEnum(snes, ftype, 2);
794a221d59SStefano Zampini   PetscCall(PetscObjectTypeCompare((PetscObject)snes, SNESNEWTONTR, &flg));
804a221d59SStefano Zampini   if (flg) tr->fallback = ftype;
814a221d59SStefano Zampini   PetscFunctionReturn(PETSC_SUCCESS);
824a221d59SStefano Zampini }
834a221d59SStefano Zampini 
847cb011f5SBarry Smith /*@C
85c9368356SGlenn Hammond   SNESNewtonTRSetPreCheck - Sets a user function that is called before the search step has been determined.
864a221d59SStefano Zampini   Allows the user a chance to change or override the trust region decision.
87f6dfbefdSBarry Smith 
88c3339decSBarry Smith   Logically Collective
89c9368356SGlenn Hammond 
90c9368356SGlenn Hammond   Input Parameters:
91c9368356SGlenn Hammond + snes - the nonlinear solver object
9220f4b53cSBarry Smith . func - [optional] function evaluation routine, for the calling sequence see `SNESNewtonTRPreCheck()`
9320f4b53cSBarry Smith - ctx  - [optional] user-defined context for private data for the function evaluation routine (may be `NULL`)
94c9368356SGlenn Hammond 
9520f4b53cSBarry Smith   Level: deprecated (since 3.19)
96c9368356SGlenn Hammond 
97f6dfbefdSBarry Smith   Note:
984a221d59SStefano Zampini   This function is called BEFORE the function evaluation within the solver.
99c9368356SGlenn Hammond 
100420bcc1bSBarry Smith .seealso: [](ch_snes), `SNESNEWTONTR`, `SNESNewtonTRPreCheck()`, `SNESNewtonTRGetPreCheck()`, `SNESNewtonTRSetPostCheck()`, `SNESNewtonTRGetPostCheck()`,
101c9368356SGlenn Hammond @*/
102d71ae5a4SJacob Faibussowitsch PetscErrorCode SNESNewtonTRSetPreCheck(SNES snes, PetscErrorCode (*func)(SNES, Vec, Vec, PetscBool *, void *), void *ctx)
103d71ae5a4SJacob Faibussowitsch {
104c9368356SGlenn Hammond   SNES_NEWTONTR *tr = (SNES_NEWTONTR *)snes->data;
1054a221d59SStefano Zampini   PetscBool      flg;
106c9368356SGlenn Hammond 
107c9368356SGlenn Hammond   PetscFunctionBegin;
108c9368356SGlenn Hammond   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
1094a221d59SStefano Zampini   PetscCall(PetscObjectTypeCompare((PetscObject)snes, SNESNEWTONTR, &flg));
1104a221d59SStefano Zampini   if (flg) {
111c9368356SGlenn Hammond     if (func) tr->precheck = func;
112c9368356SGlenn Hammond     if (ctx) tr->precheckctx = ctx;
1134a221d59SStefano Zampini   }
1143ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
115c9368356SGlenn Hammond }
116c9368356SGlenn Hammond 
117c9368356SGlenn Hammond /*@C
118c9368356SGlenn Hammond   SNESNewtonTRGetPreCheck - Gets the pre-check function
119c9368356SGlenn Hammond 
12020f4b53cSBarry Smith   Deprecated use `SNESNEWTONDCTRDC`
12120f4b53cSBarry Smith 
12220f4b53cSBarry Smith   Not Collective
123c9368356SGlenn Hammond 
124c9368356SGlenn Hammond   Input Parameter:
125c9368356SGlenn Hammond . snes - the nonlinear solver context
126c9368356SGlenn Hammond 
127c9368356SGlenn Hammond   Output Parameters:
12820f4b53cSBarry Smith + func - [optional] function evaluation routine, for the calling sequence see `SNESNewtonTRPreCheck()`
12920f4b53cSBarry Smith - ctx  - [optional] user-defined context for private data for the function evaluation routine (may be `NULL`)
130c9368356SGlenn Hammond 
13120f4b53cSBarry Smith   Level: deprecated (since 3.19)
132c9368356SGlenn Hammond 
133420bcc1bSBarry Smith .seealso: [](ch_snes), `SNESNEWTONTR`, `SNESNewtonTRSetPreCheck()`, `SNESNewtonTRPreCheck()`
134c9368356SGlenn Hammond @*/
135d71ae5a4SJacob Faibussowitsch PetscErrorCode SNESNewtonTRGetPreCheck(SNES snes, PetscErrorCode (**func)(SNES, Vec, Vec, PetscBool *, void *), void **ctx)
136d71ae5a4SJacob Faibussowitsch {
137c9368356SGlenn Hammond   SNES_NEWTONTR *tr = (SNES_NEWTONTR *)snes->data;
1384a221d59SStefano Zampini   PetscBool      flg;
139c9368356SGlenn Hammond 
140c9368356SGlenn Hammond   PetscFunctionBegin;
141c9368356SGlenn Hammond   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
1424a221d59SStefano Zampini   PetscCall(PetscObjectTypeCompare((PetscObject)snes, SNESNEWTONTR, &flg));
1434a221d59SStefano Zampini   PetscAssert(flg, PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_WRONG, "Not for type %s", ((PetscObject)snes)->type_name);
144c9368356SGlenn Hammond   if (func) *func = tr->precheck;
145c9368356SGlenn Hammond   if (ctx) *ctx = tr->precheckctx;
1463ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
147c9368356SGlenn Hammond }
148c9368356SGlenn Hammond 
149c9368356SGlenn Hammond /*@C
1507cb011f5SBarry Smith   SNESNewtonTRSetPostCheck - Sets a user function that is called after the search step has been determined but before the next
1514a221d59SStefano Zampini   function evaluation. Allows the user a chance to change or override the internal decision of the solver
152f6dfbefdSBarry Smith 
153c3339decSBarry Smith   Logically Collective
1547cb011f5SBarry Smith 
1557cb011f5SBarry Smith   Input Parameters:
1567cb011f5SBarry Smith + snes - the nonlinear solver object
15720f4b53cSBarry Smith . func - [optional] function evaluation routine, for the calling sequence see `SNESNewtonTRPostCheck()`
15820f4b53cSBarry Smith - ctx  - [optional] user-defined context for private data for the function evaluation routine (may be `NULL`)
1597cb011f5SBarry Smith 
16020f4b53cSBarry Smith   Level: deprecated (since 3.19)
1617cb011f5SBarry Smith 
162f6dfbefdSBarry Smith   Note:
1634a221d59SStefano Zampini   This function is called BEFORE the function evaluation within the solver while the function set in
164f6dfbefdSBarry Smith   `SNESLineSearchSetPostCheck()` is called AFTER the function evaluation.
1657cb011f5SBarry Smith 
166420bcc1bSBarry Smith .seealso: [](ch_snes), `SNESNEWTONTR`, `SNESNewtonTRPostCheck()`, `SNESNewtonTRGetPostCheck()`, `SNESNewtonTRSetPreCheck()`, `SNESNewtonTRGetPreCheck()`
1677cb011f5SBarry Smith @*/
168d71ae5a4SJacob Faibussowitsch PetscErrorCode SNESNewtonTRSetPostCheck(SNES snes, PetscErrorCode (*func)(SNES, Vec, Vec, Vec, PetscBool *, PetscBool *, void *), void *ctx)
169d71ae5a4SJacob Faibussowitsch {
1707cb011f5SBarry Smith   SNES_NEWTONTR *tr = (SNES_NEWTONTR *)snes->data;
1714a221d59SStefano Zampini   PetscBool      flg;
1727cb011f5SBarry Smith 
1737cb011f5SBarry Smith   PetscFunctionBegin;
1747cb011f5SBarry Smith   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
1754a221d59SStefano Zampini   PetscCall(PetscObjectTypeCompare((PetscObject)snes, SNESNEWTONTR, &flg));
1764a221d59SStefano Zampini   if (flg) {
1777cb011f5SBarry Smith     if (func) tr->postcheck = func;
1787cb011f5SBarry Smith     if (ctx) tr->postcheckctx = ctx;
1794a221d59SStefano Zampini   }
1803ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1817cb011f5SBarry Smith }
1827cb011f5SBarry Smith 
1837cb011f5SBarry Smith /*@C
1847cb011f5SBarry Smith   SNESNewtonTRGetPostCheck - Gets the post-check function
1857cb011f5SBarry Smith 
18620f4b53cSBarry Smith   Not Collective
1877cb011f5SBarry Smith 
1887cb011f5SBarry Smith   Input Parameter:
1897cb011f5SBarry Smith . snes - the nonlinear solver context
1907cb011f5SBarry Smith 
1917cb011f5SBarry Smith   Output Parameters:
19220f4b53cSBarry Smith + func - [optional] function evaluation routine, for the calling sequence see `SNESNewtonTRPostCheck()`
19320f4b53cSBarry Smith - ctx  - [optional] user-defined context for private data for the function evaluation routine (may be `NULL`)
1947cb011f5SBarry Smith 
1957cb011f5SBarry Smith   Level: intermediate
1967cb011f5SBarry Smith 
197420bcc1bSBarry Smith .seealso: [](ch_snes), `SNESNEWTONTR`, `SNESNewtonTRSetPostCheck()`, `SNESNewtonTRPostCheck()`
1987cb011f5SBarry Smith @*/
199d71ae5a4SJacob Faibussowitsch PetscErrorCode SNESNewtonTRGetPostCheck(SNES snes, PetscErrorCode (**func)(SNES, Vec, Vec, Vec, PetscBool *, PetscBool *, void *), void **ctx)
200d71ae5a4SJacob Faibussowitsch {
2017cb011f5SBarry Smith   SNES_NEWTONTR *tr = (SNES_NEWTONTR *)snes->data;
2024a221d59SStefano Zampini   PetscBool      flg;
2037cb011f5SBarry Smith 
2047cb011f5SBarry Smith   PetscFunctionBegin;
2057cb011f5SBarry Smith   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
2064a221d59SStefano Zampini   PetscCall(PetscObjectTypeCompare((PetscObject)snes, SNESNEWTONTR, &flg));
2074a221d59SStefano Zampini   PetscAssert(flg, PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_WRONG, "Not for type %s", ((PetscObject)snes)->type_name);
2087cb011f5SBarry Smith   if (func) *func = tr->postcheck;
2097cb011f5SBarry Smith   if (ctx) *ctx = tr->postcheckctx;
2103ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
2117cb011f5SBarry Smith }
2127cb011f5SBarry Smith 
2137cb011f5SBarry Smith /*@C
2144a221d59SStefano Zampini   SNESNewtonTRPreCheck - Runs the precheck routine
215c9368356SGlenn Hammond 
216c3339decSBarry Smith   Logically Collective
217c9368356SGlenn Hammond 
218c9368356SGlenn Hammond   Input Parameters:
219c9368356SGlenn Hammond + snes - the solver
220c9368356SGlenn Hammond . X    - The last solution
221c9368356SGlenn Hammond - Y    - The step direction
222c9368356SGlenn Hammond 
2232fe279fdSBarry Smith   Output Parameter:
2242fe279fdSBarry Smith . changed_Y - Indicator that the step direction `Y` has been changed.
225c9368356SGlenn Hammond 
2264a221d59SStefano Zampini   Level: intermediate
227c9368356SGlenn Hammond 
228420bcc1bSBarry Smith .seealso: [](ch_snes), `SNESNEWTONTR`, `SNESNewtonTRSetPreCheck()`, `SNESNewtonTRGetPreCheck()`, `SNESNewtonTRPostCheck()`
229c9368356SGlenn Hammond @*/
2304a221d59SStefano Zampini PetscErrorCode SNESNewtonTRPreCheck(SNES snes, Vec X, Vec Y, PetscBool *changed_Y)
231d71ae5a4SJacob Faibussowitsch {
232c9368356SGlenn Hammond   SNES_NEWTONTR *tr = (SNES_NEWTONTR *)snes->data;
2334a221d59SStefano Zampini   PetscBool      flg;
234c9368356SGlenn Hammond 
235c9368356SGlenn Hammond   PetscFunctionBegin;
2364a221d59SStefano Zampini   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
2374a221d59SStefano Zampini   PetscCall(PetscObjectTypeCompare((PetscObject)snes, SNESNEWTONTR, &flg));
2384a221d59SStefano Zampini   PetscAssert(flg, PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_WRONG, "Not for type %s", ((PetscObject)snes)->type_name);
239c9368356SGlenn Hammond   *changed_Y = PETSC_FALSE;
240c9368356SGlenn Hammond   if (tr->precheck) {
2419566063dSJacob Faibussowitsch     PetscCall((*tr->precheck)(snes, X, Y, changed_Y, tr->precheckctx));
242c9368356SGlenn Hammond     PetscValidLogicalCollectiveBool(snes, *changed_Y, 4);
243c9368356SGlenn Hammond   }
2443ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
245c9368356SGlenn Hammond }
246c9368356SGlenn Hammond 
247c9368356SGlenn Hammond /*@C
2484a221d59SStefano Zampini   SNESNewtonTRPostCheck - Runs the postcheck routine
2497cb011f5SBarry Smith 
250c3339decSBarry Smith   Logically Collective
2517cb011f5SBarry Smith 
2527cb011f5SBarry Smith   Input Parameters:
2536b867d5aSJose E. Roman + snes - the solver
2546b867d5aSJose E. Roman . X    - The last solution
2557cb011f5SBarry Smith . Y    - The full step direction
2563312a946SBarry Smith - W    - The updated solution, W = X - Y
2577cb011f5SBarry Smith 
2587cb011f5SBarry Smith   Output Parameters:
2593312a946SBarry Smith + changed_Y - indicator if step has been changed
2603312a946SBarry Smith - changed_W - Indicator if the new candidate solution W has been changed.
2617cb011f5SBarry Smith 
262f6dfbefdSBarry Smith   Note:
2633312a946SBarry Smith   If Y is changed then W is recomputed as X - Y
2647cb011f5SBarry Smith 
2654a221d59SStefano Zampini   Level: intermediate
2667cb011f5SBarry Smith 
267420bcc1bSBarry Smith .seealso: [](ch_snes), `SNESNEWTONTR`, `SNESNewtonTRSetPostCheck()`, `SNESNewtonTRGetPostCheck()`, `SNESNewtonTRPreCheck()`
2687cb011f5SBarry Smith @*/
2694a221d59SStefano Zampini PetscErrorCode SNESNewtonTRPostCheck(SNES snes, Vec X, Vec Y, Vec W, PetscBool *changed_Y, PetscBool *changed_W)
270d71ae5a4SJacob Faibussowitsch {
2717cb011f5SBarry Smith   SNES_NEWTONTR *tr = (SNES_NEWTONTR *)snes->data;
2724a221d59SStefano Zampini   PetscBool      flg;
2737cb011f5SBarry Smith 
2747cb011f5SBarry Smith   PetscFunctionBegin;
2754a221d59SStefano Zampini   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
2764a221d59SStefano Zampini   PetscCall(PetscObjectTypeCompare((PetscObject)snes, SNESNEWTONTR, &flg));
2774a221d59SStefano Zampini   PetscAssert(flg, PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_WRONG, "Not for type %s", ((PetscObject)snes)->type_name);
278c9368356SGlenn Hammond   *changed_Y = PETSC_FALSE;
2797cb011f5SBarry Smith   *changed_W = PETSC_FALSE;
2807cb011f5SBarry Smith   if (tr->postcheck) {
2819566063dSJacob Faibussowitsch     PetscCall((*tr->postcheck)(snes, X, Y, W, changed_Y, changed_W, tr->postcheckctx));
282c9368356SGlenn Hammond     PetscValidLogicalCollectiveBool(snes, *changed_Y, 5);
2837cb011f5SBarry Smith     PetscValidLogicalCollectiveBool(snes, *changed_W, 6);
2847cb011f5SBarry Smith   }
2853ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
2867cb011f5SBarry Smith }
28785385478SLisandro Dalcin 
2884a221d59SStefano Zampini static inline void PetscQuadraticRoots(PetscReal a, PetscReal b, PetscReal c, PetscReal *xm, PetscReal *xp)
2894a221d59SStefano Zampini {
2904a221d59SStefano Zampini   PetscReal temp = -0.5 * (b + PetscCopysignReal(1.0, b) * PetscSqrtReal(b * b - 4 * a * c));
2914a221d59SStefano Zampini   PetscReal x1   = temp / a;
2924a221d59SStefano Zampini   PetscReal x2   = c / temp;
2934a221d59SStefano Zampini   *xm            = PetscMin(x1, x2);
2944a221d59SStefano Zampini   *xp            = PetscMax(x1, x2);
2954a221d59SStefano Zampini }
2964a221d59SStefano Zampini 
2977aa289d8SStefano Zampini /* Computes the quadratic model difference */
2987aa289d8SStefano Zampini static PetscErrorCode SNESNewtonTRQuadraticDelta(SNES snes, PetscBool has_objective, Vec Y, Vec GradF, Vec W, PetscReal *yTHy, PetscReal *gTy, PetscReal *deltaqm)
2997aa289d8SStefano Zampini {
3007aa289d8SStefano Zampini   PetscFunctionBegin;
3017aa289d8SStefano Zampini   PetscCall(MatMult(snes->jacobian, Y, W));
3027aa289d8SStefano Zampini   if (has_objective) PetscCall(VecDotRealPart(Y, W, yTHy));
3037aa289d8SStefano Zampini   else PetscCall(VecDotRealPart(W, W, yTHy)); /* Gauss-Newton approximation J^t * J */
3047aa289d8SStefano Zampini   PetscCall(VecDotRealPart(GradF, Y, gTy));
3057aa289d8SStefano Zampini   *deltaqm = -(-(*gTy) + 0.5 * (*yTHy)); /* difference in quadratic model, -gTy because SNES solves it this way */
3067aa289d8SStefano Zampini   PetscFunctionReturn(PETSC_SUCCESS);
3077aa289d8SStefano Zampini }
3087aa289d8SStefano Zampini 
309df60cc22SBarry Smith /*
3104a221d59SStefano Zampini    SNESSolve_NEWTONTR - Implements Newton's Method with trust-region subproblem and adds dogleg Cauchy
3114a221d59SStefano Zampini    (Steepest Descent direction) step and direction if the trust region is not satisfied for solving system of
3124a221d59SStefano Zampini    nonlinear equations
3134800dd8cSBarry Smith 
3144800dd8cSBarry Smith */
315d71ae5a4SJacob Faibussowitsch static PetscErrorCode SNESSolve_NEWTONTR(SNES snes)
316d71ae5a4SJacob Faibussowitsch {
31704d7464bSBarry Smith   SNES_NEWTONTR            *neP = (SNES_NEWTONTR *)snes->data;
3184a221d59SStefano Zampini   Vec                       X, F, Y, G, W, GradF, YU;
3194a221d59SStefano Zampini   PetscInt                  maxits, lits;
3204b0a5c37SStefano Zampini   PetscReal                 rho, fnorm, gnorm = 0.0, xnorm = 0.0, delta, ynorm;
3214a221d59SStefano Zampini   PetscReal                 deltaM, fk, fkp1, deltaqm, gTy, yTHy;
322fb01098fSStefano Zampini   PetscReal                 auk, gfnorm, ycnorm, gTBg, objmin = 0.0;
323df60cc22SBarry Smith   KSP                       ksp;
3244a221d59SStefano Zampini   PetscBool                 already_done = PETSC_FALSE;
3257aa289d8SStefano Zampini   PetscBool                 clear_converged_test, rho_satisfied, has_objective;
326df8705c3SBarry Smith   SNES_TR_KSPConverged_Ctx *ctx;
3275e28bcb6Sprj-   void                     *convctx;
3284a221d59SStefano Zampini   PetscErrorCode (*convtest)(KSP, PetscInt, PetscReal, KSPConvergedReason *, void *), (*convdestroy)(void *);
3294a221d59SStefano Zampini   PetscErrorCode (*objective)(SNES, Vec, PetscReal *, void *);
3304800dd8cSBarry Smith 
3313a40ed3dSBarry Smith   PetscFunctionBegin;
3324a221d59SStefano Zampini   PetscCall(SNESGetObjective(snes, &objective, NULL));
3337aa289d8SStefano Zampini   has_objective = objective ? PETSC_TRUE : PETSC_FALSE;
334c579b300SPatrick Farrell 
335fbe28522SBarry Smith   maxits = snes->max_its;                                   /* maximum number of iterations */
336fbe28522SBarry Smith   X      = snes->vec_sol;                                   /* solution vector */
33739e2f89bSBarry Smith   F      = snes->vec_func;                                  /* residual vector */
3384a221d59SStefano Zampini   Y      = snes->vec_sol_update;                            /* update vector */
3394a221d59SStefano Zampini   G      = snes->work[0];                                   /* updated residual */
3404a221d59SStefano Zampini   W      = snes->work[1];                                   /* temporary vector */
3417aa289d8SStefano Zampini   GradF  = !has_objective ? snes->work[2] : snes->vec_func; /* grad f = J^T F */
3424a221d59SStefano Zampini   YU     = snes->work[3];                                   /* work vector for dogleg method */
3434a221d59SStefano Zampini 
3444a221d59SStefano Zampini   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);
3454800dd8cSBarry Smith 
3469566063dSJacob Faibussowitsch   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
3474c49b128SBarry Smith   snes->iter = 0;
3489566063dSJacob Faibussowitsch   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
3494800dd8cSBarry Smith 
3504a221d59SStefano Zampini   /* Set the linear stopping criteria to use the More' trick if needed */
3514a221d59SStefano Zampini   clear_converged_test = PETSC_FALSE;
3529566063dSJacob Faibussowitsch   PetscCall(SNESGetKSP(snes, &ksp));
3539566063dSJacob Faibussowitsch   PetscCall(KSPGetConvergenceTest(ksp, &convtest, &convctx, &convdestroy));
354fcc61681SStefano Zampini   if (convtest != SNESTR_KSPConverged_Private) {
3554a221d59SStefano Zampini     clear_converged_test = PETSC_TRUE;
3569566063dSJacob Faibussowitsch     PetscCall(PetscNew(&ctx));
357df8705c3SBarry Smith     ctx->snes = snes;
3589566063dSJacob Faibussowitsch     PetscCall(KSPGetAndClearConvergenceTest(ksp, &ctx->convtest, &ctx->convctx, &ctx->convdestroy));
3599566063dSJacob Faibussowitsch     PetscCall(KSPSetConvergenceTest(ksp, SNESTR_KSPConverged_Private, ctx, SNESTR_KSPConverged_Destroy));
3609566063dSJacob Faibussowitsch     PetscCall(PetscInfo(snes, "Using Krylov convergence test SNESTR_KSPConverged_Private\n"));
361df8705c3SBarry Smith   }
362df8705c3SBarry Smith 
363e4ed7901SPeter Brune   if (!snes->vec_func_init_set) {
3649566063dSJacob Faibussowitsch     PetscCall(SNESComputeFunction(snes, X, F)); /* F(X) */
3651aa26658SKarl Rupp   } else snes->vec_func_init_set = PETSC_FALSE;
366e4ed7901SPeter Brune 
3679566063dSJacob Faibussowitsch   PetscCall(VecNorm(F, NORM_2, &fnorm)); /* fnorm <- || F || */
368422a814eSBarry Smith   SNESCheckFunctionNorm(snes, fnorm);
3699566063dSJacob Faibussowitsch   PetscCall(VecNorm(X, NORM_2, &xnorm)); /* xnorm <- || X || */
3704a221d59SStefano Zampini 
3719566063dSJacob Faibussowitsch   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
372fbe28522SBarry Smith   snes->norm = fnorm;
3739566063dSJacob Faibussowitsch   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
3744a221d59SStefano Zampini   delta      = neP->delta0;
3754a221d59SStefano Zampini   deltaM     = neP->deltaM;
3764800dd8cSBarry Smith   neP->delta = delta;
3779566063dSJacob Faibussowitsch   PetscCall(SNESLogConvergenceHistory(snes, fnorm, 0));
378b37302c6SLois Curfman McInnes 
37985385478SLisandro Dalcin   /* test convergence */
3804a221d59SStefano Zampini   rho_satisfied = PETSC_FALSE;
3812d157150SStefano Zampini   PetscCall(SNESConverged(snes, 0, 0.0, 0.0, fnorm));
3822d157150SStefano Zampini   PetscCall(SNESMonitor(snes, 0, fnorm));
3833ba16761SJacob Faibussowitsch   if (snes->reason) PetscFunctionReturn(PETSC_SUCCESS);
3843f149594SLisandro Dalcin 
3857aa289d8SStefano Zampini   if (has_objective) PetscCall(SNESComputeObjective(snes, X, &fk));
3864a221d59SStefano Zampini   else fk = 0.5 * PetscSqr(fnorm); /* obj(x) = 0.5 * ||F(x)||^2 */
38799a96b7cSMatthew Knepley 
3884a221d59SStefano Zampini   while (snes->iter < maxits) {
389c9368356SGlenn Hammond     PetscBool changed_y;
3907cb011f5SBarry Smith     PetscBool changed_w;
3916b5873e3SBarry Smith 
39212d0050eSStefano Zampini     /* calculating Jacobian and GradF of minimization function only once */
3934a221d59SStefano Zampini     if (!already_done) {
39412d0050eSStefano Zampini       /* Call general purpose update function */
39512d0050eSStefano Zampini       PetscTryTypeMethod(snes, update, snes->iter);
39612d0050eSStefano Zampini 
3974b0a5c37SStefano Zampini       /* apply the nonlinear preconditioner */
3984b0a5c37SStefano Zampini       if (snes->npc && snes->npcside == PC_RIGHT) {
3994b0a5c37SStefano Zampini         SNESConvergedReason reason;
4004b0a5c37SStefano Zampini 
4014b0a5c37SStefano Zampini         PetscCall(SNESSetInitialFunction(snes->npc, F));
4024b0a5c37SStefano Zampini         PetscCall(PetscLogEventBegin(SNES_NPCSolve, snes->npc, X, snes->vec_rhs, 0));
4034b0a5c37SStefano Zampini         PetscCall(SNESSolve(snes->npc, snes->vec_rhs, X));
4044b0a5c37SStefano Zampini         PetscCall(PetscLogEventEnd(SNES_NPCSolve, snes->npc, X, snes->vec_rhs, 0));
4054b0a5c37SStefano Zampini         PetscCall(SNESGetConvergedReason(snes->npc, &reason));
4064b0a5c37SStefano Zampini         if (reason < 0 && reason != SNES_DIVERGED_MAX_IT && reason != SNES_DIVERGED_TR_DELTA) {
4074b0a5c37SStefano Zampini           snes->reason = SNES_DIVERGED_INNER;
4084b0a5c37SStefano Zampini           PetscFunctionReturn(PETSC_SUCCESS);
4094b0a5c37SStefano Zampini         }
4104b0a5c37SStefano Zampini         // XXX
4114b0a5c37SStefano Zampini         PetscCall(SNESGetNPCFunction(snes, F, &fnorm));
4124b0a5c37SStefano Zampini         if (has_objective) PetscCall(SNESComputeObjective(snes, X, &fk));
4134b0a5c37SStefano Zampini         else fk = 0.5 * PetscSqr(fnorm); /* obj(x) = 0.5 * ||F(x)||^2 */
4144b0a5c37SStefano Zampini         // XXX
4154b0a5c37SStefano Zampini       } else if (snes->ops->update) { /* if update is present, recompute objective function and function norm */
41612d0050eSStefano Zampini         PetscCall(SNESComputeFunction(snes, X, F));
41712d0050eSStefano Zampini         PetscCall(VecNorm(F, NORM_2, &fnorm));
41812d0050eSStefano Zampini         if (has_objective) PetscCall(SNESComputeObjective(snes, X, &fk));
41912d0050eSStefano Zampini         else fk = 0.5 * PetscSqr(fnorm); /* obj(x) = 0.5 * ||F(x)||^2 */
42012d0050eSStefano Zampini       }
42112d0050eSStefano Zampini 
42212d0050eSStefano Zampini       /* Jacobian */
4234a221d59SStefano Zampini       PetscCall(SNESComputeJacobian(snes, X, snes->jacobian, snes->jacobian_pre));
4244a221d59SStefano Zampini       SNESCheckJacobianDomainerror(snes);
42512d0050eSStefano Zampini 
42612d0050eSStefano Zampini       /* GradF */
4277aa289d8SStefano Zampini       if (has_objective) gfnorm = fnorm;
4287aa289d8SStefano Zampini       else {
4297aa289d8SStefano Zampini         PetscCall(MatMultTranspose(snes->jacobian, F, GradF)); /* grad f = J^T F */
4307aa289d8SStefano Zampini         PetscCall(VecNorm(GradF, NORM_2, &gfnorm));
431fbe28522SBarry Smith       }
4327aa289d8SStefano Zampini     }
4337aa289d8SStefano Zampini     already_done = PETSC_TRUE;
4347aa289d8SStefano Zampini 
4354b0a5c37SStefano Zampini     /* solve trust-region subproblem */
4364b0a5c37SStefano Zampini 
4374b0a5c37SStefano Zampini     /* sufficient decrease (see 6.3.27 in Conn, Gould, Toint "Trust Region Methods")
4384b0a5c37SStefano Zampini        This is actually more relaxed, since they use include gnorm/beta_k, with
4394b0a5c37SStefano Zampini        beta_k the largest eigenvalue of the Hessian */
4404b0a5c37SStefano Zampini     objmin = -neP->kmdc * gnorm * PetscMin(gnorm, delta);
441fb01098fSStefano Zampini     PetscCall(KSPCGSetObjectiveTarget(snes->ksp, objmin));
4424b0a5c37SStefano Zampini 
4434b0a5c37SStefano Zampini     /* don't specify radius if not looking for Newton step only */
4444b0a5c37SStefano Zampini     PetscCall(KSPCGSetRadius(snes->ksp, neP->fallback == SNES_TR_FALLBACK_NEWTON ? delta : 0.0));
4454b0a5c37SStefano Zampini 
4464a221d59SStefano Zampini     PetscCall(KSPSetOperators(snes->ksp, snes->jacobian, snes->jacobian_pre));
4474a221d59SStefano Zampini     PetscCall(KSPSolve(snes->ksp, F, Y));
4484a221d59SStefano Zampini     SNESCheckKSPSolve(snes);
4494a221d59SStefano Zampini     PetscCall(KSPGetIterationNumber(snes->ksp, &lits));
4504b0a5c37SStefano Zampini     PetscCall(PetscInfo(snes, "iter=%" PetscInt_FMT ", linear solve iterations=%" PetscInt_FMT "\n", snes->iter, lits));
4514800dd8cSBarry Smith 
4524a221d59SStefano Zampini     /* decide what to do when the update is outside of trust region */
4534a221d59SStefano Zampini     PetscCall(VecNorm(Y, NORM_2, &ynorm));
4545ec2728bSStefano Zampini     if (ynorm > delta || ynorm == 0.0) {
4555ec2728bSStefano Zampini       SNESNewtonTRFallbackType fallback = ynorm > 0.0 ? neP->fallback : SNES_TR_FALLBACK_CAUCHY;
4565ec2728bSStefano Zampini 
4575ec2728bSStefano Zampini       switch (fallback) {
4584a221d59SStefano Zampini       case SNES_TR_FALLBACK_NEWTON:
4594a221d59SStefano Zampini         auk = delta / ynorm;
4604a221d59SStefano Zampini         PetscCall(VecScale(Y, auk));
4614b0a5c37SStefano Zampini         PetscCall(PetscInfo(snes, "SN evaluated. delta: %g, ynorm: %g\n", (double)delta, (double)ynorm));
4624a221d59SStefano Zampini         break;
4634a221d59SStefano Zampini       case SNES_TR_FALLBACK_CAUCHY:
4644a221d59SStefano Zampini       case SNES_TR_FALLBACK_DOGLEG:
4654a221d59SStefano Zampini         PetscCall(MatMult(snes->jacobian, GradF, W));
4667aa289d8SStefano Zampini         if (has_objective) PetscCall(VecDotRealPart(GradF, W, &gTBg));
4674a221d59SStefano Zampini         else PetscCall(VecDotRealPart(W, W, &gTBg)); /* B = J^t * J */
4684a221d59SStefano Zampini         /* Eqs 4.7 and 4.8 in Nocedal and Wright */
4694a221d59SStefano Zampini         auk = delta / gfnorm;
4704a221d59SStefano Zampini         if (gTBg > 0.0) auk *= PetscMin(gfnorm * gfnorm * gfnorm / (delta * gTBg), 1);
4714a221d59SStefano Zampini         ycnorm = auk * gfnorm;
4725ec2728bSStefano Zampini         if (fallback == SNES_TR_FALLBACK_CAUCHY || gTBg <= 0.0) {
4734a221d59SStefano Zampini           /* Cauchy solution */
4744a221d59SStefano Zampini           PetscCall(VecAXPBY(Y, auk, 0.0, GradF));
4754a221d59SStefano Zampini           PetscCall(PetscInfo(snes, "CP evaluated. delta: %g, ynorm: %g, ycnorm: %g, gTBg: %g\n", (double)delta, (double)ynorm, (double)ycnorm, (double)gTBg));
4764a221d59SStefano Zampini         } else { /* take linear combination of Cauchy and Newton direction and step */
4774a221d59SStefano Zampini           PetscReal c0, c1, c2, tau = 0.0, tpos, tneg;
4784a221d59SStefano Zampini           PetscBool noroots;
479284fb49fSHeeho Park 
4804a221d59SStefano Zampini           auk = gfnorm * gfnorm / gTBg;
4814a221d59SStefano Zampini           PetscCall(VecAXPBY(YU, auk, 0.0, GradF));
4824a221d59SStefano Zampini           PetscCall(VecAXPY(Y, -1.0, YU));
4834a221d59SStefano Zampini           PetscCall(VecNorm(Y, NORM_2, &c0));
4844a221d59SStefano Zampini           PetscCall(VecDotRealPart(YU, Y, &c1));
4854a221d59SStefano Zampini           c0 = PetscSqr(c0);
4864a221d59SStefano Zampini           c2 = PetscSqr(ycnorm) - PetscSqr(delta);
4874a221d59SStefano Zampini           PetscQuadraticRoots(c0, c1, c2, &tneg, &tpos);
4884a221d59SStefano Zampini 
4894a221d59SStefano Zampini           noroots = PetscIsInfOrNanReal(tneg);
4904a221d59SStefano Zampini           if (noroots) { /*  No roots, select Cauchy point */
4914a221d59SStefano Zampini             auk = delta / gfnorm;
4924a221d59SStefano Zampini             auk *= PetscMin(gfnorm * gfnorm * gfnorm / (delta * gTBg), 1);
4934a221d59SStefano Zampini             PetscCall(VecAXPBY(Y, auk, 0.0, GradF));
4944a221d59SStefano Zampini           } else { /* Here roots corresponds to tau-1 in Nocedal and Wright */
4954a221d59SStefano Zampini             tpos += 1.0;
4964a221d59SStefano Zampini             tneg += 1.0;
4974a221d59SStefano Zampini             tau = PetscClipInterval(tpos, 0.0, 2.0); /* clip to tau [0,2] */
4984a221d59SStefano Zampini             if (tau < 1.0) {
4994a221d59SStefano Zampini               PetscCall(VecAXPBY(Y, tau, 0.0, YU));
5004a221d59SStefano Zampini             } else {
5014a221d59SStefano Zampini               PetscCall(VecAXPBY(Y, 1.0, tau - 1, YU));
5024a221d59SStefano Zampini             }
5034a221d59SStefano Zampini           }
5044a221d59SStefano Zampini           PetscCall(VecNorm(Y, NORM_2, &c0)); /* this norm will be cached and reused later */
5054a221d59SStefano Zampini           PetscCall(PetscInfo(snes, "%s evaluated. roots: (%g, %g), tau %g, ynorm: %g, ycnorm: %g, ydlnorm %g, gTBg: %g\n", noroots ? "CP" : "DL", (double)tneg, (double)tpos, (double)tau, (double)ynorm, (double)ycnorm, (double)c0, (double)gTBg));
5064a221d59SStefano Zampini         }
5074a221d59SStefano Zampini         break;
5084a221d59SStefano Zampini       default:
5094a221d59SStefano Zampini         SETERRQ(PetscObjectComm((PetscObject)snes), PETSC_ERR_SUP, "Unknown fallback mode");
510454a90a3SBarry Smith         break;
51152392280SLois Curfman McInnes       }
5124800dd8cSBarry Smith     }
5134a221d59SStefano Zampini 
5144a221d59SStefano Zampini     /* Evaluate the solution to meet the improvement ratio criteria */
5154a221d59SStefano Zampini 
5164a221d59SStefano Zampini     /* compute the final ynorm */
5174a221d59SStefano Zampini     PetscCall(SNESNewtonTRPreCheck(snes, X, Y, &changed_y));
5184a221d59SStefano Zampini     PetscCall(VecNorm(Y, NORM_2, &ynorm));
5194a221d59SStefano Zampini 
5207aa289d8SStefano Zampini     /* compute the quadratic model difference */
5217aa289d8SStefano Zampini     PetscCall(SNESNewtonTRQuadraticDelta(snes, has_objective, Y, GradF, W, &yTHy, &gTy, &deltaqm));
5224a221d59SStefano Zampini 
5234a221d59SStefano Zampini     /* update */
5244a221d59SStefano Zampini     PetscCall(VecWAXPY(W, -1.0, Y, X)); /* Xkp1 */
5254a221d59SStefano Zampini     PetscCall(SNESNewtonTRPostCheck(snes, X, Y, W, &changed_y, &changed_w));
5264a221d59SStefano Zampini     if (changed_y) {
5274a221d59SStefano Zampini       /* Need to recompute the quadratic model difference */
5287aa289d8SStefano Zampini       PetscCall(SNESNewtonTRQuadraticDelta(snes, has_objective, Y, GradF, YU, &yTHy, &gTy, &deltaqm));
5294a221d59SStefano Zampini       /* User changed Y but not W */
5304a221d59SStefano Zampini       if (!changed_w) PetscCall(VecWAXPY(W, -1.0, Y, X));
5314a221d59SStefano Zampini     }
5324a221d59SStefano Zampini 
5334a221d59SStefano Zampini     /* Compute new objective function */
5344a221d59SStefano Zampini     PetscCall(SNESComputeFunction(snes, W, G)); /*  F(Xkp1) = G */
5354a221d59SStefano Zampini     PetscCall(VecNorm(G, NORM_2, &gnorm));
5367aa289d8SStefano Zampini     if (has_objective) PetscCall(SNESComputeObjective(snes, W, &fkp1));
5374a221d59SStefano Zampini     else fkp1 = 0.5 * PetscSqr(gnorm);
5384a221d59SStefano Zampini     SNESCheckFunctionNorm(snes, fkp1);
5394a221d59SStefano Zampini 
5404a221d59SStefano Zampini     if (deltaqm > 0.0) rho = (fk - fkp1) / deltaqm; /* actual improvement over predicted improvement */
5414a221d59SStefano Zampini     else rho = -1.0;                                /*  no reduction in quadratic model, step must be rejected */
5424a221d59SStefano Zampini     PetscCall(PetscInfo(snes, "rho=%g, delta=%g, fk=%g, fkp1=%g, deltaqm=%g, gTy=%g, yTHy=%g\n", (double)rho, (double)delta, (double)fk, (double)fkp1, (double)deltaqm, (double)gTy, (double)yTHy));
5434a221d59SStefano Zampini 
5444a221d59SStefano Zampini     if (rho < neP->eta2) delta *= neP->t1;      /* shrink the region */
5454a221d59SStefano Zampini     else if (rho > neP->eta3) delta *= neP->t2; /* expand the region */
5464a221d59SStefano Zampini     delta = PetscMin(delta, deltaM);            /* but not greater than deltaM */
5474a221d59SStefano Zampini 
5484a221d59SStefano Zampini     neP->delta = delta;
5494a221d59SStefano Zampini     if (rho >= neP->eta1) {
5504a221d59SStefano Zampini       rho_satisfied = PETSC_TRUE;
5514a221d59SStefano Zampini     } else {
5524a221d59SStefano Zampini       rho_satisfied = PETSC_FALSE;
5534a221d59SStefano Zampini       PetscCall(PetscInfo(snes, "Trying again in smaller region\n"));
5544a221d59SStefano Zampini       /* check to see if progress is hopeless */
5554a221d59SStefano Zampini       PetscCall(SNESTR_Converged_Private(snes, snes->iter, xnorm, ynorm, fnorm, &snes->reason, snes->cnvP));
5562d157150SStefano Zampini       if (!snes->reason) PetscCall(SNESConverged(snes, snes->iter, xnorm, ynorm, fnorm));
5574b0a5c37SStefano Zampini       if (snes->reason == SNES_CONVERGED_SNORM_RELATIVE) snes->reason = SNES_DIVERGED_TR_DELTA;
5584a221d59SStefano Zampini       snes->numFailures++;
5594a221d59SStefano Zampini       /* We're not progressing, so return with the current iterate */
5604a221d59SStefano Zampini       if (snes->reason) break;
5614a221d59SStefano Zampini     }
5624a221d59SStefano Zampini     if (rho_satisfied) {
5634a221d59SStefano Zampini       /* Update function values */
5644a221d59SStefano Zampini       already_done = PETSC_FALSE;
5654800dd8cSBarry Smith       fnorm        = gnorm;
5664a221d59SStefano Zampini       fk           = fkp1;
5674a221d59SStefano Zampini 
5684a221d59SStefano Zampini       /* New residual and linearization point */
5699566063dSJacob Faibussowitsch       PetscCall(VecCopy(G, F));
5709566063dSJacob Faibussowitsch       PetscCall(VecCopy(W, X));
5714a221d59SStefano Zampini 
57285385478SLisandro Dalcin       /* Monitor convergence */
5739566063dSJacob Faibussowitsch       PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
5744a221d59SStefano Zampini       snes->iter++;
575fbe28522SBarry Smith       snes->norm  = fnorm;
576c1e67a49SFande Kong       snes->xnorm = xnorm;
577c1e67a49SFande Kong       snes->ynorm = ynorm;
5789566063dSJacob Faibussowitsch       PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
5799566063dSJacob Faibussowitsch       PetscCall(SNESLogConvergenceHistory(snes, snes->norm, lits));
5804a221d59SStefano Zampini 
58185385478SLisandro Dalcin       /* Test for convergence, xnorm = || X || */
5824a221d59SStefano Zampini       PetscCall(VecNorm(X, NORM_2, &xnorm));
5832d157150SStefano Zampini       PetscCall(SNESConverged(snes, snes->iter, xnorm, ynorm, fnorm));
5842d157150SStefano Zampini       PetscCall(SNESMonitor(snes, snes->iter, snes->norm));
5854a221d59SStefano Zampini       if (snes->reason) break;
5864a221d59SStefano Zampini     }
58738442cffSBarry Smith   }
588284fb49fSHeeho Park 
5894a221d59SStefano Zampini   if (clear_converged_test) {
5909566063dSJacob Faibussowitsch     PetscCall(KSPGetAndClearConvergenceTest(ksp, &ctx->convtest, &ctx->convctx, &ctx->convdestroy));
5919566063dSJacob Faibussowitsch     PetscCall(PetscFree(ctx));
5929566063dSJacob Faibussowitsch     PetscCall(KSPSetConvergenceTest(ksp, convtest, convctx, convdestroy));
5935e28bcb6Sprj-   }
5943ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
5954800dd8cSBarry Smith }
596284fb49fSHeeho Park 
597d71ae5a4SJacob Faibussowitsch static PetscErrorCode SNESSetUp_NEWTONTR(SNES snes)
598d71ae5a4SJacob Faibussowitsch {
5993a40ed3dSBarry Smith   PetscFunctionBegin;
6009566063dSJacob Faibussowitsch   PetscCall(SNESSetWorkVecs(snes, 4));
6019566063dSJacob Faibussowitsch   PetscCall(SNESSetUpMatrices(snes));
6023ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
6034800dd8cSBarry Smith }
6046b8b9a38SLisandro Dalcin 
605d71ae5a4SJacob Faibussowitsch static PetscErrorCode SNESDestroy_NEWTONTR(SNES snes)
606d71ae5a4SJacob Faibussowitsch {
6073a40ed3dSBarry Smith   PetscFunctionBegin;
6089566063dSJacob Faibussowitsch   PetscCall(PetscFree(snes->data));
6093ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
6104800dd8cSBarry Smith }
6114800dd8cSBarry Smith 
612d71ae5a4SJacob Faibussowitsch static PetscErrorCode SNESSetFromOptions_NEWTONTR(SNES snes, PetscOptionItems *PetscOptionsObject)
613d71ae5a4SJacob Faibussowitsch {
61404d7464bSBarry Smith   SNES_NEWTONTR *ctx = (SNES_NEWTONTR *)snes->data;
6154800dd8cSBarry Smith 
6163a40ed3dSBarry Smith   PetscFunctionBegin;
617d0609cedSBarry Smith   PetscOptionsHeadBegin(PetscOptionsObject, "SNES trust region options for nonlinear equations");
6184a221d59SStefano Zampini   PetscCall(PetscOptionsReal("-snes_tr_tol", "Trust region tolerance", "SNESSetTrustRegionTolerance", snes->deltatol, &snes->deltatol, NULL));
6194a221d59SStefano Zampini   PetscCall(PetscOptionsReal("-snes_tr_eta1", "eta1", "None", ctx->eta1, &ctx->eta1, NULL));
6204a221d59SStefano Zampini   PetscCall(PetscOptionsReal("-snes_tr_eta2", "eta2", "None", ctx->eta2, &ctx->eta2, NULL));
6214a221d59SStefano Zampini   PetscCall(PetscOptionsReal("-snes_tr_eta3", "eta3", "None", ctx->eta3, &ctx->eta3, NULL));
6224a221d59SStefano Zampini   PetscCall(PetscOptionsReal("-snes_tr_t1", "t1", "None", ctx->t1, &ctx->t1, NULL));
6234a221d59SStefano Zampini   PetscCall(PetscOptionsReal("-snes_tr_t2", "t2", "None", ctx->t2, &ctx->t2, NULL));
6244a221d59SStefano Zampini   PetscCall(PetscOptionsReal("-snes_tr_deltaM", "deltaM", "None", ctx->deltaM, &ctx->deltaM, NULL));
6259566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-snes_tr_delta0", "delta0", "None", ctx->delta0, &ctx->delta0, NULL));
6264b0a5c37SStefano Zampini   PetscCall(PetscOptionsReal("-snes_tr_kmdc", "sufficient decrease parameter", "None", ctx->kmdc, &ctx->kmdc, NULL));
6274a221d59SStefano Zampini   PetscCall(PetscOptionsEnum("-snes_tr_fallback_type", "Type of fallback if subproblem solution is outside of the trust region", "SNESNewtonTRSetFallbackType", SNESNewtonTRFallbackTypes, (PetscEnum)ctx->fallback, (PetscEnum *)&ctx->fallback, NULL));
628d0609cedSBarry Smith   PetscOptionsHeadEnd();
6293ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
6304800dd8cSBarry Smith }
6314800dd8cSBarry Smith 
632d71ae5a4SJacob Faibussowitsch static PetscErrorCode SNESView_NEWTONTR(SNES snes, PetscViewer viewer)
633d71ae5a4SJacob Faibussowitsch {
63404d7464bSBarry Smith   SNES_NEWTONTR *tr = (SNES_NEWTONTR *)snes->data;
635ace3abfcSBarry Smith   PetscBool      iascii;
636a935fc98SLois Curfman McInnes 
6373a40ed3dSBarry Smith   PetscFunctionBegin;
6389566063dSJacob Faibussowitsch   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
63932077d6dSBarry Smith   if (iascii) {
6404a221d59SStefano Zampini     PetscCall(PetscViewerASCIIPrintf(viewer, "  Trust region tolerance %g\n", (double)snes->deltatol));
6414a221d59SStefano Zampini     PetscCall(PetscViewerASCIIPrintf(viewer, "  eta1=%g, eta2=%g, eta3=%g\n", (double)tr->eta1, (double)tr->eta2, (double)tr->eta3));
6424a221d59SStefano Zampini     PetscCall(PetscViewerASCIIPrintf(viewer, "  delta0=%g, t1=%g, t2=%g, deltaM=%g\n", (double)tr->delta0, (double)tr->t1, (double)tr->t2, (double)tr->deltaM));
6434b0a5c37SStefano Zampini     PetscCall(PetscViewerASCIIPrintf(viewer, "  kmdc=%g\n", (double)tr->kmdc));
6444a221d59SStefano Zampini     PetscCall(PetscViewerASCIIPrintf(viewer, "  fallback=%s\n", SNESNewtonTRFallbackTypes[tr->fallback]));
64519bcc07fSBarry Smith   }
6463ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
647a935fc98SLois Curfman McInnes }
648f6dfbefdSBarry Smith 
649ebe3b25bSBarry Smith /*MC
650*1d27aa22SBarry Smith    SNESNEWTONTR - Newton based nonlinear solver that uses trust-region dogleg method with Cauchy direction {cite}`nocedal2006numerical`
651f6dfbefdSBarry Smith 
652f6dfbefdSBarry Smith    Options Database Keys:
6534a221d59SStefano Zampini +  -snes_tr_tol <tol>                            - trust region tolerance
6544a221d59SStefano Zampini .  -snes_tr_eta1 <eta1>                          - trust region parameter 0.0 <= eta1 <= eta2, rho >= eta1 breaks out of the inner iteration (default: eta1=0.001)
6554a221d59SStefano Zampini .  -snes_tr_eta2 <eta2>                          - trust region parameter 0.0 <= eta1 <= eta2, rho <= eta2 shrinks the trust region (default: eta2=0.25)
6564a221d59SStefano Zampini .  -snes_tr_eta3 <eta3>                          - trust region parameter eta3 > eta2, rho >= eta3 expands the trust region (default: eta3=0.75)
6574a221d59SStefano Zampini .  -snes_tr_t1 <t1>                              - trust region parameter, shrinking factor of trust region (default: 0.25)
6584a221d59SStefano Zampini .  -snes_tr_t2 <t2>                              - trust region parameter, expanding factor of trust region (default: 2.0)
6594a221d59SStefano Zampini .  -snes_tr_deltaM <deltaM>                      - trust region parameter, max size of trust region (default: MAX_REAL)
6604a221d59SStefano Zampini .  -snes_tr_delta0 <delta0>                      - trust region parameter, initial size of trust region (default: 0.2)
6614a221d59SStefano Zampini -  -snes_tr_fallback_type <newton,cauchy,dogleg> - Solution strategy to test reduction when step is outside of trust region. Can use scaled Newton direction, Cauchy point (Steepest Descent direction) or dogleg method.
662ebe3b25bSBarry Smith 
66320f4b53cSBarry Smith    Level: deprecated (since 3.19)
664ee3001cbSBarry Smith 
665420bcc1bSBarry Smith .seealso: [](ch_snes), `SNESCreate()`, `SNES`, `SNESSetType()`, `SNESNEWTONLS`, `SNESSetTrustRegionTolerance()`,
6664a221d59SStefano Zampini           `SNESNewtonTRPreCheck()`, `SNESNewtonTRGetPreCheck()`, `SNESNewtonTRSetPostCheck()`, `SNESNewtonTRGetPostCheck()`,
6674a221d59SStefano Zampini           `SNESNewtonTRSetPreCheck()`, `SNESNewtonTRSetFallbackType()`
668ebe3b25bSBarry Smith M*/
669d71ae5a4SJacob Faibussowitsch PETSC_EXTERN PetscErrorCode SNESCreate_NEWTONTR(SNES snes)
670d71ae5a4SJacob Faibussowitsch {
67104d7464bSBarry Smith   SNES_NEWTONTR *neP;
6724800dd8cSBarry Smith 
6733a40ed3dSBarry Smith   PetscFunctionBegin;
67404d7464bSBarry Smith   snes->ops->setup          = SNESSetUp_NEWTONTR;
67504d7464bSBarry Smith   snes->ops->solve          = SNESSolve_NEWTONTR;
67604d7464bSBarry Smith   snes->ops->destroy        = SNESDestroy_NEWTONTR;
67704d7464bSBarry Smith   snes->ops->setfromoptions = SNESSetFromOptions_NEWTONTR;
67804d7464bSBarry Smith   snes->ops->view           = SNESView_NEWTONTR;
679fbe28522SBarry Smith 
6801a58d6d3SStefano Zampini   snes->stol    = 0.0;
68142f4f86dSBarry Smith   snes->usesksp = PETSC_TRUE;
6824b0a5c37SStefano Zampini   snes->npcside = PC_RIGHT;
6834b0a5c37SStefano Zampini   snes->usesnpc = PETSC_TRUE;
68442f4f86dSBarry Smith 
6854fc747eaSLawrence Mitchell   snes->alwayscomputesfinalresidual = PETSC_TRUE;
6864fc747eaSLawrence Mitchell 
6874dfa11a4SJacob Faibussowitsch   PetscCall(PetscNew(&neP));
688fbe28522SBarry Smith   snes->data    = (void *)neP;
689fbe28522SBarry Smith   neP->delta    = 0.0;
690fbe28522SBarry Smith   neP->delta0   = 0.2;
6914a221d59SStefano Zampini   neP->eta1     = 0.001;
6924a221d59SStefano Zampini   neP->eta2     = 0.25;
6934a221d59SStefano Zampini   neP->eta3     = 0.75;
6944a221d59SStefano Zampini   neP->t1       = 0.25;
6954a221d59SStefano Zampini   neP->t2       = 2.0;
6964a221d59SStefano Zampini   neP->deltaM   = 1.e10;
6974a221d59SStefano Zampini   neP->fallback = SNES_TR_FALLBACK_NEWTON;
6984b0a5c37SStefano Zampini   neP->kmdc     = 0.0; /* by default do not use sufficient decrease */
6993ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
7004800dd8cSBarry Smith }
701