#ifdef PETSC_RCS_HEADER static char vcid[] = "$Id: snesut.c,v 1.35 1998/01/06 20:12:19 bsmith Exp bsmith $"; #endif #include #include "src/snes/snesimpl.h" /*I "snes.h" I*/ #undef __FUNC__ #define __FUNC__ "SNESDefaultMonitor" /*@C SNESDefaultMonitor - Monitoring progress of the SNES solvers (default). Input Parameters: . snes - the SNES context . its - iteration number . fgnorm - 2-norm of residual (or gradient) . dummy - unused context Notes: For SNES_NONLINEAR_EQUATIONS methods the routine prints the residual norm at each iteration. For SNES_UNCONSTRAINED_MINIMIZATION methods the routine prints the function value and gradient norm at each iteration. .keywords: SNES, nonlinear, default, monitor, norm .seealso: SNESSetMonitor() @*/ int SNESDefaultMonitor(SNES snes,int its,double fgnorm,void *dummy) { PetscFunctionBegin; if (snes->method_class == SNES_NONLINEAR_EQUATIONS) { PetscPrintf(snes->comm, "iter = %d, SNES Function norm %g \n",its,fgnorm); } else if (snes->method_class == SNES_UNCONSTRAINED_MINIMIZATION) { PetscPrintf(snes->comm,"iter = %d, SNES Function value %g, Gradient norm %g \n",its,snes->fc,fgnorm); } else SETERRQ(PETSC_ERR_ARG_OUTOFRANGE,0,"Unknown method class"); PetscFunctionReturn(0); } /* ---------------------------------------------------------------- */ #undef __FUNC__ #define __FUNC__ "SNESDefaultSMonitor" /* Default (short) SNES Monitor, same as SNESDefaultMonitor() except it prints fewer digits of the residual as the residual gets smaller. This is because the later digits are meaningless and are often different on different machines; by using this routine different machines will usually generate the same output. */ int SNESDefaultSMonitor(SNES snes,int its, double fgnorm,void *dummy) { PetscFunctionBegin; if (snes->method_class == SNES_NONLINEAR_EQUATIONS) { if (fgnorm > 1.e-9) { PetscPrintf(snes->comm, "iter = %d, SNES Function norm %g \n",its,fgnorm); } else if (fgnorm > 1.e-11){ PetscPrintf(snes->comm, "iter = %d, SNES Function norm %5.3e \n",its,fgnorm); } else { PetscPrintf(snes->comm, "iter = %d, SNES Function norm < 1.e-11\n",its); } } else if (snes->method_class == SNES_UNCONSTRAINED_MINIMIZATION) { if (fgnorm > 1.e-9) { PetscPrintf(snes->comm, "iter = %d, SNES Function value %g, Gradient norm %g \n",its,snes->fc,fgnorm); } else if (fgnorm > 1.e-11) { PetscPrintf(snes->comm, "iter = %d, SNES Function value %g, Gradient norm %5.3e \n",its,snes->fc,fgnorm); } else { PetscPrintf(snes->comm, "iter = %d, SNES Function value %g, Gradient norm < 1.e-11\n",its,snes->fc); } } else SETERRQ(PETSC_ERR_ARG_OUTOFRANGE,0,"Unknown method class"); PetscFunctionReturn(0); } /* ---------------------------------------------------------------- */ #undef __FUNC__ #define __FUNC__ "SNESConverged_EQ_LS" /*@C SNESConverged_EQ_LS - Monitors the convergence of the solvers for systems of nonlinear equations (default). Input Parameters: . snes - the SNES context . xnorm - 2-norm of current iterate . pnorm - 2-norm of current step . fnorm - 2-norm of function . dummy - unused context Returns: $ 2 if ( fnorm < atol ), $ 3 if ( pnorm < xtol*xnorm ), $ 4 if ( fnorm < rtol*fnorm0 ), $ -2 if ( nfct > maxf ), $ 0 otherwise, where $ maxf - maximum number of function evaluations, $ set with SNESSetTolerances() $ nfct - number of function evaluations, $ atol - absolute function norm tolerance, $ set with SNESSetTolerances() $ rtol - relative function norm tolerance, $ set with SNESSetTolerances() .keywords: SNES, nonlinear, default, converged, convergence .seealso: SNESSetConvergenceTest(), SNESEisenstatWalkerConverged() @*/ int SNESConverged_EQ_LS(SNES snes,double xnorm,double pnorm,double fnorm,void *dummy) { PetscFunctionBegin; if (snes->method_class != SNES_NONLINEAR_EQUATIONS) { SETERRQ(PETSC_ERR_ARG_WRONG,0,"For SNES_NONLINEAR_EQUATIONS only"); } /* Note: Reserve return code 1, -1 for compatibility with SNESConverged_EQ_TR */ if (fnorm != fnorm) { PLogInfo(snes,"SNESConverged_EQ_LS:Failed to converged, function norm is NaN\n"); PetscFunctionReturn(-3); } if (fnorm <= snes->ttol) { PLogInfo(snes, "SNESConverged_EQ_LS:Converged due to function norm %g < %g (relative tolerance)\n",fnorm,snes->ttol); PetscFunctionReturn(4); } if (fnorm < snes->atol) { PLogInfo(snes, "SNESConverged_EQ_LS: Converged due to function norm %g < %g\n",fnorm,snes->atol); PetscFunctionReturn(2); } if (pnorm < snes->xtol*(xnorm)) { PLogInfo(snes, "SNESConverged_EQ_LS: Converged due to small update length: %g < %g * %g\n", pnorm,snes->xtol,xnorm); PetscFunctionReturn(3); } if (snes->nfuncs > snes->max_funcs) { PLogInfo(snes,"SNESConverged_EQ_LS: Exceeded maximum number of function evaluations: %d > %d\n", snes->nfuncs, snes->max_funcs ); PetscFunctionReturn(-2); } PetscFunctionReturn(0); } /* ------------------------------------------------------------ */ #undef __FUNC__ #define __FUNC__ "SNES_KSP_SetConvergenceTestEW" /*@ SNES_KSP_SetConvergenceTestEW - Sets alternative convergence test for the linear solvers within an inexact Newton method. Input Parameter: . snes - SNES context Notes: Currently, the default is to use a constant relative tolerance for the inner linear solvers. Alternatively, one can use the Eisenstat-Walker method, where the relative convergence tolerance is reset at each Newton iteration according progress of the nonlinear solver. Reference: S. C. Eisenstat and H. F. Walker, "Choosing the forcing terms in an inexact Newton method", Utah State University Math. Stat. Dept. Res. Report 6/94/75, June, 1994, to appear in SIAM J. Sci. Comput. .keywords: SNES, KSP, Eisenstat, Walker, convergence, test, inexact, Newton @*/ int SNES_KSP_SetConvergenceTestEW(SNES snes) { PetscFunctionBegin; snes->ksp_ewconv = 1; PetscFunctionReturn(0); } #undef __FUNC__ #define __FUNC__ "SNES_KSP_SetParametersEW" /*@ SNES_KSP_SetParametersEW - Sets parameters for Eisenstat-Walker convergence criteria for the linear solvers within an inexact Newton method. Input Parameters: . snes - SNES context . version - version 1 or 2 (default is 2) . rtol_0 - initial relative tolerance $ (0 <= rtol_0 < 1) . rtol_max - maximum relative tolerance $ (0 <= rtol_max < 1) . alpha - power for version 2 rtol computation $ (1 < alpha <= 2) . alpha2 - power for safeguard . gamma2 - multiplicative factor for version 2 rtol computation $ (0 <= gamma2 <= 1) . threshold - threshold for imposing safeguard $ (0 < threshold < 1) Note: Use PETSC_DEFAULT to retain the default for any of the parameters. Reference: S. C. Eisenstat and H. F. Walker, "Choosing the forcing terms in an inexact Newton method", Utah State University Math. Stat. Dept. Res. Report 6/94/75, June, 1994, to appear in SIAM J. Sci. Comput. .keywords: SNES, KSP, Eisenstat, Walker, set, parameters .seealso: SNES_KSP_SetConvergenceTestEW() @*/ int SNES_KSP_SetParametersEW(SNES snes,int version,double rtol_0, double rtol_max,double gamma2,double alpha, double alpha2,double threshold) { SNES_KSP_EW_ConvCtx *kctx = (SNES_KSP_EW_ConvCtx*)snes->kspconvctx; PetscFunctionBegin; if (!kctx) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,0,"No Eisenstat-Walker context existing"); if (version != PETSC_DEFAULT) kctx->version = version; if (rtol_0 != PETSC_DEFAULT) kctx->rtol_0 = rtol_0; if (rtol_max != PETSC_DEFAULT) kctx->rtol_max = rtol_max; if (gamma2 != PETSC_DEFAULT) kctx->gamma = gamma2; if (alpha != PETSC_DEFAULT) kctx->alpha = alpha; if (alpha2 != PETSC_DEFAULT) kctx->alpha2 = alpha2; if (threshold != PETSC_DEFAULT) kctx->threshold = threshold; if (kctx->rtol_0 < 0.0 || kctx->rtol_0 >= 1.0) { SETERRQ(PETSC_ERR_ARG_OUTOFRANGE,0,"0.0 <= rtol_0 < 1.0\n"); } if (kctx->rtol_max < 0.0 || kctx->rtol_max >= 1.0) { SETERRQ(PETSC_ERR_ARG_OUTOFRANGE,0,"0.0 <= rtol_max < 1.0\n"); } if (kctx->threshold <= 0.0 || kctx->threshold >= 1.0) { SETERRQ(PETSC_ERR_ARG_OUTOFRANGE,0,"0.0 < threshold < 1.0\n"); } if (kctx->gamma < 0.0 || kctx->gamma > 1.0) { SETERRQ(PETSC_ERR_ARG_OUTOFRANGE,0,"0.0 <= alpha <= 1.0\n"); } if (kctx->alpha <= 1.0 || kctx->alpha > 2.0) { SETERRQ(PETSC_ERR_ARG_OUTOFRANGE,0,"1.0 < alpha <= 2.0\n"); } if (kctx->version != 1 && kctx->version !=2) { SETERRQ(PETSC_ERR_ARG_OUTOFRANGE,0,"Only versions 1 and 2 are supported"); } PetscFunctionReturn(0); } #undef __FUNC__ #define __FUNC__ "SNES_KSP_EW_ComputeRelativeTolerance_Private" int SNES_KSP_EW_ComputeRelativeTolerance_Private(SNES snes,KSP ksp) { SNES_KSP_EW_ConvCtx *kctx = (SNES_KSP_EW_ConvCtx*)snes->kspconvctx; double rtol, stol; int ierr; PetscFunctionBegin; if (!kctx) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,0,"No Eisenstat-Walker context exists"); if (snes->iter == 1) { rtol = kctx->rtol_0; } else { if (kctx->version == 1) { rtol = (snes->norm - kctx->lresid_last)/kctx->norm_last; if (rtol < 0.0) rtol = -rtol; stol = pow(kctx->rtol_last,kctx->alpha2); if (stol > kctx->threshold) rtol = PetscMax(rtol,stol); } else if (kctx->version == 2) { rtol = kctx->gamma * pow(snes->norm/kctx->norm_last,kctx->alpha); stol = kctx->gamma * pow(kctx->rtol_last,kctx->alpha); if (stol > kctx->threshold) rtol = PetscMax(rtol,stol); } else SETERRQ( PETSC_ERR_ARG_OUTOFRANGE,0,"Only versions 1 or 2 are supported"); } rtol = PetscMin(rtol,kctx->rtol_max); kctx->rtol_last = rtol; PLogInfo(snes,"SNESConverged_EQ_LS: iter %d, Eisenstat-Walker (version %d) KSP rtol = %g\n", snes->iter,kctx->version,rtol); ierr = KSPSetTolerances(ksp,rtol,PETSC_DEFAULT,PETSC_DEFAULT,PETSC_DEFAULT); CHKERRQ(ierr); kctx->norm_last = snes->norm; PetscFunctionReturn(0); } #undef __FUNC__ #define __FUNC__ "SNES_KSP_EW_Converged_Private" int SNES_KSP_EW_Converged_Private(KSP ksp,int n,double rnorm,void *ctx) { SNES snes = (SNES)ctx; SNES_KSP_EW_ConvCtx *kctx = (SNES_KSP_EW_ConvCtx*)snes->kspconvctx; int convinfo; PetscFunctionBegin; if (!kctx) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,0,"No Eisenstat-Walker context set"); if (n == 0) SNES_KSP_EW_ComputeRelativeTolerance_Private(snes,ksp); convinfo = KSPDefaultConverged(ksp,n,rnorm,ctx); kctx->lresid_last = rnorm; if (convinfo) { PLogInfo(snes,"SNES_KSP_EW_Converged_Private: KSP iterations=%d, rnorm=%g\n",n,rnorm); } PetscFunctionReturn(convinfo); }