1 #ifdef PETSC_RCS_HEADER 2 static char vcid[] = "$Id: ls.c,v 1.125 1999/03/14 17:17:00 curfman Exp curfman $"; 3 #endif 4 5 #include "src/snes/impls/ls/ls.h" 6 7 /* -------------------------------------------------------------------- 8 9 This file implements a truncated Newton method with a line search, 10 for solving a system of nonlinear equations, using the SLES, Vec, 11 and Mat interfaces for linear solvers, vectors, and matrices, 12 respectively. 13 14 The following basic routines are required for each nonlinear solver: 15 SNESCreate_XXX() - Creates a nonlinear solver context 16 SNESSetFromOptions_XXX() - Sets runtime options 17 SNESSolve_XXX() - Solves the nonlinear system 18 SNESDestroy_XXX() - Destroys the nonlinear solver context 19 The suffix "_XXX" denotes a particular implementation, in this case 20 we use _EQ_LS (e.g., SNESCreate_EQ_LS, SNESSolve_EQ_LS) for solving 21 systems of nonlinear equations (EQ) with a line search (LS) method. 22 These routines are actually called via the common user interface 23 routines SNESCreate(), SNESSetFromOptions(), SNESSolve(), and 24 SNESDestroy(), so the application code interface remains identical 25 for all nonlinear solvers. 26 27 Another key routine is: 28 SNESSetUp_XXX() - Prepares for the use of a nonlinear solver 29 by setting data structures and options. The interface routine SNESSetUp() 30 is not usually called directly by the user, but instead is called by 31 SNESSolve() if necessary. 32 33 Additional basic routines are: 34 SNESPrintHelp_XXX() - Prints nonlinear solver runtime options 35 SNESView_XXX() - Prints details of runtime options that 36 have actually been used. 37 These are called by application codes via the interface routines 38 SNESPrintHelp() and SNESView(). 39 40 The various types of solvers (preconditioners, Krylov subspace methods, 41 nonlinear solvers, timesteppers) are all organized similarly, so the 42 above description applies to these categories also. 43 44 -------------------------------------------------------------------- */ 45 /* 46 SNESSolve_EQ_LS - Solves a nonlinear system with a truncated Newton 47 method with a line search. 48 49 Input Parameters: 50 . snes - the SNES context 51 52 Output Parameter: 53 . outits - number of iterations until termination 54 55 Application Interface Routine: SNESSolve() 56 57 Notes: 58 This implements essentially a truncated Newton method with a 59 line search. By default a cubic backtracking line search 60 is employed, as described in the text "Numerical Methods for 61 Unconstrained Optimization and Nonlinear Equations" by Dennis 62 and Schnabel. 63 */ 64 #undef __FUNC__ 65 #define __FUNC__ "SNESSolve_EQ_LS" 66 int SNESSolve_EQ_LS(SNES snes,int *outits) 67 { 68 SNES_LS *neP = (SNES_LS *) snes->data; 69 int maxits, i, ierr, lits, lsfail; 70 MatStructure flg = DIFFERENT_NONZERO_PATTERN; 71 double fnorm, gnorm, xnorm, ynorm; 72 Vec Y, X, F, G, W, TMP; 73 74 PetscFunctionBegin; 75 maxits = snes->max_its; /* maximum number of iterations */ 76 X = snes->vec_sol; /* solution vector */ 77 F = snes->vec_func; /* residual vector */ 78 Y = snes->work[0]; /* work vectors */ 79 G = snes->work[1]; 80 W = snes->work[2]; 81 82 ierr = SNESComputeFunction(snes,X,F); CHKERRQ(ierr); /* F(X) */ 83 ierr = VecNorm(F,NORM_2,&fnorm); CHKERRQ(ierr); /* fnorm <- ||F|| */ 84 PetscAMSTakeAccess(snes); 85 snes->iter = 0; 86 snes->norm = fnorm; 87 PetscAMSGrantAccess(snes); 88 SNESLogConvHistory(snes,fnorm,0); 89 SNESMonitor(snes,0,fnorm); 90 91 if (fnorm < snes->atol) {*outits = 0; PetscFunctionReturn(0);} 92 93 /* set parameter for default relative tolerance convergence test */ 94 snes->ttol = fnorm*snes->rtol; 95 96 for ( i=0; i<maxits; i++ ) { 97 98 /* Solve J Y = F, where J is Jacobian matrix */ 99 ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg); CHKERRQ(ierr); 100 ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian_pre,flg); CHKERRQ(ierr); 101 ierr = SLESSolve(snes->sles,F,Y,&lits); CHKERRQ(ierr); 102 snes->linear_its += PetscAbsInt(lits); 103 PLogInfo(snes,"SNESSolve_EQ_LS: iter=%d, linear solve iterations=%d\n",snes->iter,lits); 104 105 /* Compute a (scaled) negative update in the line search routine: 106 Y <- X - lambda*Y 107 and evaluate G(Y) = function(Y)) 108 */ 109 ierr = VecCopy(Y,snes->vec_sol_update_always); CHKERRQ(ierr); 110 ierr = (*neP->LineSearch)(snes,neP->lsP,X,F,G,Y,W,fnorm,&ynorm,&gnorm,&lsfail); CHKERRQ(ierr); 111 PLogInfo(snes,"SNESSolve_EQ_LS: fnorm=%g, gnorm=%g, ynorm=%g, lsfail=%d\n",fnorm,gnorm,ynorm,lsfail); 112 if (lsfail) snes->nfailures++; 113 114 TMP = F; F = G; snes->vec_func_always = F; G = TMP; 115 TMP = X; X = Y; snes->vec_sol_always = X; Y = TMP; 116 fnorm = gnorm; 117 118 PetscAMSTakeAccess(snes); 119 snes->iter = i+1; 120 snes->norm = fnorm; 121 PetscAMSGrantAccess(snes); 122 SNESLogConvHistory(snes,fnorm,lits); 123 SNESMonitor(snes,i+1,fnorm); 124 125 /* Test for convergence */ 126 if (snes->converged) { 127 ierr = VecNorm(X,NORM_2,&xnorm); CHKERRQ(ierr); /* xnorm = || X || */ 128 if ((*snes->converged)(snes,xnorm,ynorm,fnorm,snes->cnvP)) { 129 break; 130 } 131 } 132 } 133 if (X != snes->vec_sol) { 134 ierr = VecCopy(X,snes->vec_sol); CHKERRQ(ierr); 135 snes->vec_sol_always = snes->vec_sol; 136 snes->vec_func_always = snes->vec_func; 137 } 138 if (i == maxits) { 139 PLogInfo(snes,"SNESSolve_EQ_LS: Maximum number of iterations has been reached: %d\n",maxits); 140 i--; 141 } 142 *outits = i+1; 143 PetscFunctionReturn(0); 144 } 145 /* -------------------------------------------------------------------------- */ 146 /* 147 SNESSetUp_EQ_LS - Sets up the internal data structures for the later use 148 of the SNES_EQ_LS nonlinear solver. 149 150 Input Parameter: 151 . snes - the SNES context 152 . x - the solution vector 153 154 Application Interface Routine: SNESSetUp() 155 156 Notes: 157 For basic use of the SNES solvers the user need not explicitly call 158 SNESSetUp(), since these actions will automatically occur during 159 the call to SNESSolve(). 160 */ 161 #undef __FUNC__ 162 #define __FUNC__ "SNESSetUp_EQ_LS" 163 int SNESSetUp_EQ_LS(SNES snes) 164 { 165 int ierr; 166 167 PetscFunctionBegin; 168 snes->nwork = 4; 169 ierr = VecDuplicateVecs(snes->vec_sol,snes->nwork,&snes->work);CHKERRQ(ierr); 170 PLogObjectParents(snes,snes->nwork,snes->work); 171 snes->vec_sol_update_always = snes->work[3]; 172 PetscFunctionReturn(0); 173 } 174 /* -------------------------------------------------------------------------- */ 175 /* 176 SNESDestroy_EQ_LS - Destroys the private SNES_LS context that was created 177 with SNESCreate_EQ_LS(). 178 179 Input Parameter: 180 . snes - the SNES context 181 182 Application Interface Routine: SNESDestroy() 183 */ 184 #undef __FUNC__ 185 #define __FUNC__ "SNESDestroy_EQ_LS" 186 int SNESDestroy_EQ_LS(SNES snes) 187 { 188 int ierr; 189 190 PetscFunctionBegin; 191 if (snes->nwork) { 192 ierr = VecDestroyVecs(snes->work,snes->nwork); CHKERRQ(ierr); 193 } 194 PetscFree(snes->data); 195 PetscFunctionReturn(0); 196 } 197 /* -------------------------------------------------------------------------- */ 198 #undef __FUNC__ 199 #define __FUNC__ "SNESNoLineSearch" 200 201 /*@C 202 SNESNoLineSearch - This routine is not a line search at all; 203 it simply uses the full Newton step. Thus, this routine is intended 204 to serve as a template and is not recommended for general use. 205 206 Collective on SNES and Vec 207 208 Input Parameters: 209 + snes - nonlinear context 210 . lsctx - optional context for line search (not used here) 211 . x - current iterate 212 . f - residual evaluated at x 213 . y - search direction (contains new iterate on output) 214 . w - work vector 215 - fnorm - 2-norm of f 216 217 Output Parameters: 218 + g - residual evaluated at new iterate y 219 . y - new iterate (contains search direction on input) 220 . gnorm - 2-norm of g 221 . ynorm - 2-norm of search length 222 - flag - set to 0, indicating a successful line search 223 224 Options Database Key: 225 . -snes_eq_ls basic - Activates SNESNoLineSearch() 226 227 Level: advanced 228 229 .keywords: SNES, nonlinear, line search, cubic 230 231 .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(), 232 SNESSetLineSearch(), SNESNoLineSearchNoNorms() 233 @*/ 234 int SNESNoLineSearch(SNES snes, void *lsctx, Vec x, Vec f, Vec g, Vec y, Vec w, 235 double fnorm, double *ynorm, double *gnorm,int *flag ) 236 { 237 int ierr; 238 Scalar mone = -1.0; 239 240 PetscFunctionBegin; 241 *flag = 0; 242 PLogEventBegin(SNES_LineSearch,snes,x,f,g); 243 ierr = VecNorm(y,NORM_2,ynorm); CHKERRQ(ierr); /* ynorm = || y || */ 244 ierr = VecAYPX(&mone,x,y); CHKERRQ(ierr); /* y <- y - x */ 245 ierr = SNESComputeFunction(snes,y,g); CHKERRQ(ierr); /* Compute F(y) */ 246 ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr); /* gnorm = || g || */ 247 PLogEventEnd(SNES_LineSearch,snes,x,f,g); 248 PetscFunctionReturn(0); 249 } 250 /* -------------------------------------------------------------------------- */ 251 #undef __FUNC__ 252 #define __FUNC__ "SNESNoLineSearchNoNorms" 253 254 /*@C 255 SNESNoLineSearchNoNorms - This routine is not a line search at 256 all; it simply uses the full Newton step. This version does not 257 even compute the norm of the function or search direction; this 258 is intended only when you know the full step is fine and are 259 not checking for convergence of the nonlinear iteration (for 260 example, you are running always for a fixed number of Newton steps). 261 262 Collective on SNES and Vec 263 264 Input Parameters: 265 + snes - nonlinear context 266 . lsctx - optional context for line search (not used here) 267 . x - current iterate 268 . f - residual evaluated at x 269 . y - search direction (contains new iterate on output) 270 . w - work vector 271 - fnorm - 2-norm of f 272 273 Output Parameters: 274 + g - residual evaluated at new iterate y 275 . gnorm - not changed 276 . ynorm - not changed 277 - flag - set to 0, indicating a successful line search 278 279 Options Database Key: 280 . -snes_eq_ls basicnonorms - Activates SNESNoLineSearchNoNorms() 281 282 Level: advanced 283 284 .keywords: SNES, nonlinear, line search, cubic 285 286 .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(), 287 SNESSetLineSearch(), SNESNoLineSearch() 288 @*/ 289 int SNESNoLineSearchNoNorms(SNES snes, void *lsctx, Vec x, Vec f, Vec g, Vec y, Vec w, 290 double fnorm, double *ynorm, double *gnorm,int *flag ) 291 { 292 int ierr; 293 Scalar mone = -1.0; 294 295 PetscFunctionBegin; 296 *flag = 0; 297 PLogEventBegin(SNES_LineSearch,snes,x,f,g); 298 ierr = VecAYPX(&mone,x,y); CHKERRQ(ierr); /* y <- y - x */ 299 ierr = SNESComputeFunction(snes,y,g); CHKERRQ(ierr); /* Compute F(y) */ 300 PLogEventEnd(SNES_LineSearch,snes,x,f,g); 301 PetscFunctionReturn(0); 302 } 303 /* -------------------------------------------------------------------------- */ 304 #undef __FUNC__ 305 #define __FUNC__ "SNESCubicLineSearch" 306 /*@C 307 SNESCubicLineSearch - Performs a cubic line search (default line search method). 308 309 Collective on SNES 310 311 Input Parameters: 312 + snes - nonlinear context 313 . lsctx - optional context for line search (not used here) 314 . x - current iterate 315 . f - residual evaluated at x 316 . y - search direction (contains new iterate on output) 317 . w - work vector 318 - fnorm - 2-norm of f 319 320 Output Parameters: 321 + g - residual evaluated at new iterate y 322 . y - new iterate (contains search direction on input) 323 . gnorm - 2-norm of g 324 . ynorm - 2-norm of search length 325 - flag - 0 if line search succeeds; -1 on failure. 326 327 Options Database Key: 328 $ -snes_eq_ls cubic - Activates SNESCubicLineSearch() 329 330 Notes: 331 This line search is taken from "Numerical Methods for Unconstrained 332 Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325. 333 334 Level: advanced 335 336 .keywords: SNES, nonlinear, line search, cubic 337 338 .seealso: SNESQuadraticLineSearch(), SNESNoLineSearch(), SNESSetLineSearch(), SNESNoLineSearchNoNorms() 339 @*/ 340 int SNESCubicLineSearch(SNES snes,void *lsctx,Vec x,Vec f,Vec g,Vec y,Vec w, 341 double fnorm,double *ynorm,double *gnorm,int *flag) 342 { 343 /* 344 Note that for line search purposes we work with with the related 345 minimization problem: 346 min z(x): R^n -> R, 347 where z(x) = .5 * fnorm*fnorm, and fnorm = || f ||_2. 348 */ 349 350 double steptol, initslope, lambdaprev, gnormprev, a, b, d, t1, t2; 351 double maxstep, minlambda, alpha, lambda, lambdatemp, lambdaneg; 352 #if defined(USE_PETSC_COMPLEX) 353 Scalar cinitslope, clambda; 354 #endif 355 int ierr, count; 356 SNES_LS *neP = (SNES_LS *) snes->data; 357 Scalar mone = -1.0,scale; 358 359 PetscFunctionBegin; 360 PLogEventBegin(SNES_LineSearch,snes,x,f,g); 361 *flag = 0; 362 alpha = neP->alpha; 363 maxstep = neP->maxstep; 364 steptol = neP->steptol; 365 366 ierr = VecNorm(y,NORM_2,ynorm); CHKERRQ(ierr); 367 if (*ynorm < snes->atol) { 368 PLogInfo(snes,"SNESCubicLineSearch: Search direction and size are nearly 0\n"); 369 *gnorm = fnorm; 370 ierr = VecCopy(x,y); CHKERRQ(ierr); 371 ierr = VecCopy(f,g); CHKERRQ(ierr); 372 goto theend1; 373 } 374 if (*ynorm > maxstep) { /* Step too big, so scale back */ 375 scale = maxstep/(*ynorm); 376 #if defined(USE_PETSC_COMPLEX) 377 PLogInfo(snes,"SNESCubicLineSearch: Scaling step by %g\n",PetscReal(scale)); 378 #else 379 PLogInfo(snes,"SNESCubicLineSearch: Scaling step by %g\n",scale); 380 #endif 381 ierr = VecScale(&scale,y); CHKERRQ(ierr); 382 *ynorm = maxstep; 383 } 384 minlambda = steptol/(*ynorm); 385 ierr = MatMult(snes->jacobian,y,w); CHKERRQ(ierr); 386 #if defined(USE_PETSC_COMPLEX) 387 ierr = VecDot(f,w,&cinitslope); CHKERRQ(ierr); 388 initslope = PetscReal(cinitslope); 389 #else 390 ierr = VecDot(f,w,&initslope); CHKERRQ(ierr); 391 #endif 392 if (initslope > 0.0) initslope = -initslope; 393 if (initslope == 0.0) initslope = -1.0; 394 395 ierr = VecCopy(y,w); CHKERRQ(ierr); 396 ierr = VecAYPX(&mone,x,w); CHKERRQ(ierr); 397 ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 398 ierr = VecNorm(g,NORM_2,gnorm); 399 if ((*gnorm)*(*gnorm)*0.5 <= fnorm*fnorm*0.5 + alpha*initslope) { /* Sufficient reduction */ 400 ierr = VecCopy(w,y); CHKERRQ(ierr); 401 PLogInfo(snes,"SNESCubicLineSearch: Using full step\n"); 402 goto theend1; 403 } 404 405 /* Fit points with quadratic */ 406 lambda = 1.0; count = 0; 407 lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope); 408 lambdaprev = lambda; 409 gnormprev = *gnorm; 410 if (lambdatemp <= .1*lambda) lambda = .1*lambda; 411 else lambda = lambdatemp; 412 ierr = VecCopy(x,w); CHKERRQ(ierr); 413 lambdaneg = -lambda; 414 #if defined(USE_PETSC_COMPLEX) 415 clambda = lambdaneg; ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr); 416 #else 417 ierr = VecAXPY(&lambdaneg,y,w); CHKERRQ(ierr); 418 #endif 419 ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 420 ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr); 421 if ((*gnorm)*(*gnorm)*0.5 <= fnorm*fnorm*0.5 + alpha*initslope) { /* sufficient reduction */ 422 ierr = VecCopy(w,y); CHKERRQ(ierr); 423 PLogInfo(snes,"SNESCubicLineSearch: Quadratically determined step, lambda=%g\n",lambda); 424 goto theend1; 425 } 426 427 /* Fit points with cubic */ 428 count = 1; 429 while (1) { 430 if (lambda <= minlambda) { /* bad luck; use full step */ 431 PLogInfo(snes,"SNESCubicLineSearch:Unable to find good step length! %d \n",count); 432 PLogInfo(snes,"SNESCubicLineSearch:fnorm=%g, gnorm=%g, ynorm=%g, lambda=%g, initial slope=%g\n", 433 fnorm,*gnorm,*ynorm,lambda,initslope); 434 ierr = VecCopy(w,y); CHKERRQ(ierr); 435 *flag = -1; break; 436 } 437 t1 = ((*gnorm)*(*gnorm) - fnorm*fnorm)*0.5 - lambda*initslope; 438 t2 = (gnormprev*gnormprev - fnorm*fnorm)*0.5 - lambdaprev*initslope; 439 a = (t1/(lambda*lambda) - t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 440 b = (-lambdaprev*t1/(lambda*lambda) + lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 441 d = b*b - 3*a*initslope; 442 if (d < 0.0) d = 0.0; 443 if (a == 0.0) { 444 lambdatemp = -initslope/(2.0*b); 445 } else { 446 lambdatemp = (-b + sqrt(d))/(3.0*a); 447 } 448 if (lambdatemp > .5*lambda) { 449 lambdatemp = .5*lambda; 450 } 451 lambdaprev = lambda; 452 gnormprev = *gnorm; 453 if (lambdatemp <= .1*lambda) { 454 lambda = .1*lambda; 455 } 456 else lambda = lambdatemp; 457 ierr = VecCopy( x, w ); CHKERRQ(ierr); 458 lambdaneg = -lambda; 459 #if defined(USE_PETSC_COMPLEX) 460 clambda = lambdaneg; 461 ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr); 462 #else 463 ierr = VecAXPY(&lambdaneg,y,w); CHKERRQ(ierr); 464 #endif 465 ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 466 ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr); 467 if ((*gnorm)*(*gnorm)*0.5 <= fnorm*fnorm*0.5 + alpha*initslope) { /* is reduction enough? */ 468 ierr = VecCopy(w,y); CHKERRQ(ierr); 469 PLogInfo(snes,"SNESCubicLineSearch: Cubically determined step, lambda=%g\n",lambda); 470 goto theend1; 471 } else { 472 PLogInfo(snes,"SNESCubicLineSearch: Cubic step no good, shrinking lambda, lambda=%g\n",lambda); 473 } 474 count++; 475 } 476 theend1: 477 PLogEventEnd(SNES_LineSearch,snes,x,f,g); 478 PetscFunctionReturn(0); 479 } 480 /* -------------------------------------------------------------------------- */ 481 #undef __FUNC__ 482 #define __FUNC__ "SNESQuadraticLineSearch" 483 /*@C 484 SNESQuadraticLineSearch - Performs a quadratic line search. 485 486 Collective on SNES and Vec 487 488 Input Parameters: 489 + snes - the SNES context 490 . lsctx - optional context for line search (not used here) 491 . x - current iterate 492 . f - residual evaluated at x 493 . y - search direction (contains new iterate on output) 494 . w - work vector 495 - fnorm - 2-norm of f 496 497 Output Parameters: 498 + g - residual evaluated at new iterate y 499 . y - new iterate (contains search direction on input) 500 . gnorm - 2-norm of g 501 . ynorm - 2-norm of search length 502 - flag - 0 if line search succeeds; -1 on failure. 503 504 Options Database Key: 505 . -snes_eq_ls quadratic - Activates SNESQuadraticLineSearch() 506 507 Notes: 508 Use SNESSetLineSearch() to set this routine within the SNES_EQ_LS method. 509 510 Level: advanced 511 512 .keywords: SNES, nonlinear, quadratic, line search 513 514 .seealso: SNESCubicLineSearch(), SNESNoLineSearch(), SNESSetLineSearch(), SNESNoLineSearchNoNorms() 515 @*/ 516 int SNESQuadraticLineSearch(SNES snes, void *lsctx, Vec x, Vec f, Vec g, Vec y, Vec w, 517 double fnorm, double *ynorm, double *gnorm,int *flag) 518 { 519 /* 520 Note that for line search purposes we work with with the related 521 minimization problem: 522 min z(x): R^n -> R, 523 where z(x) = .5 * fnorm*fnorm, and fnorm = || f ||_2. 524 */ 525 double steptol,initslope,maxstep,minlambda,alpha,lambda,lambdatemp; 526 #if defined(USE_PETSC_COMPLEX) 527 Scalar cinitslope,clambda; 528 #endif 529 int ierr,count; 530 SNES_LS *neP = (SNES_LS *) snes->data; 531 Scalar mone = -1.0,scale; 532 533 PetscFunctionBegin; 534 PLogEventBegin(SNES_LineSearch,snes,x,f,g); 535 *flag = 0; 536 alpha = neP->alpha; 537 maxstep = neP->maxstep; 538 steptol = neP->steptol; 539 540 VecNorm(y, NORM_2,ynorm ); 541 if (*ynorm < snes->atol) { 542 PLogInfo(snes,"SNESQuadraticLineSearch: Search direction and size is 0\n"); 543 *gnorm = fnorm; 544 ierr = VecCopy(x,y); CHKERRQ(ierr); 545 ierr = VecCopy(f,g); CHKERRQ(ierr); 546 goto theend2; 547 } 548 if (*ynorm > maxstep) { /* Step too big, so scale back */ 549 scale = maxstep/(*ynorm); 550 ierr = VecScale(&scale,y); CHKERRQ(ierr); 551 *ynorm = maxstep; 552 } 553 minlambda = steptol/(*ynorm); 554 ierr = MatMult(snes->jacobian,y,w); CHKERRQ(ierr); 555 #if defined(USE_PETSC_COMPLEX) 556 ierr = VecDot(f,w,&cinitslope); CHKERRQ(ierr); 557 initslope = PetscReal(cinitslope); 558 #else 559 ierr = VecDot(f,w,&initslope); CHKERRQ(ierr); 560 #endif 561 if (initslope > 0.0) initslope = -initslope; 562 if (initslope == 0.0) initslope = -1.0; 563 564 ierr = VecCopy(y,w); CHKERRQ(ierr); 565 ierr = VecAYPX(&mone,x,w); CHKERRQ(ierr); 566 ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 567 ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr); 568 if ((*gnorm)*(*gnorm)*0.5 <= fnorm*fnorm*0.5 + alpha*initslope) { /* Sufficient reduction */ 569 ierr = VecCopy(w,y); CHKERRQ(ierr); 570 PLogInfo(snes,"SNESQuadraticLineSearch: Using full step\n"); 571 goto theend2; 572 } 573 574 /* Fit points with quadratic */ 575 lambda = 1.0; count = 0; 576 count = 1; 577 while (1) { 578 if (lambda <= minlambda) { /* bad luck; use full step */ 579 PLogInfo(snes,"SNESQuadraticLineSearch:Unable to find good step length! %d \n",count); 580 PLogInfo(snes,"SNESQuadraticLineSearch:fnorm=%g, gnorm=%g, ynorm=%g, lambda=%g, initial slope=%g\n", 581 fnorm,*gnorm,*ynorm,lambda,initslope); 582 ierr = VecCopy(w,y); CHKERRQ(ierr); 583 *flag = -1; break; 584 } 585 lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope); 586 if (lambdatemp <= .1*lambda) { 587 lambda = .1*lambda; 588 } else lambda = lambdatemp; 589 ierr = VecCopy(x,w); CHKERRQ(ierr); 590 lambda = -lambda; 591 #if defined(USE_PETSC_COMPLEX) 592 clambda = lambda; ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr); 593 #else 594 ierr = VecAXPY(&lambda,y,w); CHKERRQ(ierr); 595 #endif 596 ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 597 ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr); 598 if ((*gnorm)*(*gnorm)*0.5 <= fnorm*fnorm*0.5 + alpha*initslope) { /* sufficient reduction */ 599 ierr = VecCopy(w,y); CHKERRQ(ierr); 600 PLogInfo(snes,"SNESQuadraticLineSearch:Quadratically determined step, lambda=%g\n",lambda); 601 break; 602 } 603 count++; 604 } 605 theend2: 606 PLogEventEnd(SNES_LineSearch,snes,x,f,g); 607 PetscFunctionReturn(0); 608 } 609 /* -------------------------------------------------------------------------- */ 610 #undef __FUNC__ 611 #define __FUNC__ "SNESSetLineSearch" 612 /*@C 613 SNESSetLineSearch - Sets the line search routine to be used 614 by the method SNES_EQ_LS. 615 616 Input Parameters: 617 + snes - nonlinear context obtained from SNESCreate() 618 . lsctx - optional user-defined context for use by line search 619 - func - pointer to int function 620 621 Collective on SNES 622 623 Available Routines: 624 + SNESCubicLineSearch() - default line search 625 . SNESQuadraticLineSearch() - quadratic line search 626 . SNESNoLineSearch() - the full Newton step (actually not a line search) 627 - SNESNoLineSearchNoNorms() - the full Newton step (calculating no norms; faster in parallel) 628 629 Options Database Keys: 630 + -snes_eq_ls [cubic,quadratic,basic,basicnonorms] - Selects line search 631 . -snes_eq_ls_alpha <alpha> - Sets alpha 632 . -snes_eq_ls_maxstep <max> - Sets maxstep 633 - -snes_eq_ls_steptol <steptol> - Sets steptol 634 635 Calling sequence of func: 636 .vb 637 func (SNES snes, void *lsctx, Vec x, Vec f, Vec g, Vec y, Vec w, 638 double fnorm, double *ynorm, double *gnorm, *flag) 639 .ve 640 641 Input parameters for func: 642 + snes - nonlinear context 643 . lsctx - optional user-defined context for line search 644 . x - current iterate 645 . f - residual evaluated at x 646 . y - search direction (contains new iterate on output) 647 . w - work vector 648 - fnorm - 2-norm of f 649 650 Output parameters for func: 651 + g - residual evaluated at new iterate y 652 . y - new iterate (contains search direction on input) 653 . gnorm - 2-norm of g 654 . ynorm - 2-norm of search length 655 - flag - set to 0 if the line search succeeds; a nonzero integer 656 on failure. 657 658 Level: advanced 659 660 .keywords: SNES, nonlinear, set, line search, routine 661 662 .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(), SNESNoLineSearch(), SNESNoLineSearchNoNorms() 663 @*/ 664 int SNESSetLineSearch(SNES snes,int (*func)(SNES,void*,Vec,Vec,Vec,Vec,Vec,double,double*,double*,int*),void *lsctx) 665 { 666 int ierr, (*f)(SNES,int (*)(SNES,void*,Vec,Vec,Vec,Vec,Vec,double,double*,double*,int*),void*); 667 668 PetscFunctionBegin; 669 ierr = PetscObjectQueryFunction((PetscObject)snes,"SNESSetLineSearch_C",(void **)&f);CHKERRQ(ierr); 670 if (f) { 671 ierr = (*f)(snes,func,lsctx);CHKERRQ(ierr); 672 } 673 PetscFunctionReturn(0); 674 } 675 /* -------------------------------------------------------------------------- */ 676 EXTERN_C_BEGIN 677 #undef __FUNC__ 678 #define __FUNC__ "SNESSetLineSearch_LS" 679 int SNESSetLineSearch_LS(SNES snes,int (*func)(SNES,void*,Vec,Vec,Vec,Vec,Vec, 680 double,double*,double*,int*),void *lsctx) 681 { 682 PetscFunctionBegin; 683 ((SNES_LS *)(snes->data))->LineSearch = func; 684 ((SNES_LS *)(snes->data))->lsP = lsctx; 685 PetscFunctionReturn(0); 686 } 687 EXTERN_C_END 688 /* -------------------------------------------------------------------------- */ 689 #undef __FUNC__ 690 #define __FUNC__ "SNESSetLineSearchCheck" 691 /*@C 692 SNESSetLineSearchCheck - Sets a routine to check the step computed by the 693 line search routine in the Newton-based method SNES_EQ_LS. 694 695 Input Parameters: 696 + snes - nonlinear context obtained from SNESCreate() 697 . func - pointer to int function 698 - checkctx - optional user-defined context for use by step checking routine 699 700 Collective on SNES 701 702 Calling sequence of func: 703 .vb 704 func (SNES snes, void *lsctx, Vec x, int *flag) 705 .ve 706 707 Input parameters for func: 708 + snes - nonlinear context 709 . checkctx - optional user-defined context for use by step checking routine 710 - x - current iterate 711 712 Output parameters for func: 713 + x - current iterate (possibly modified) 714 - flag - flag indicating whether x has been modified (either 715 PETSC_TRUE of PETSC_FALSE) 716 717 Level: advanced 718 719 .keywords: SNES, nonlinear, set, line search check, step check, routine 720 721 .seealso: SNESSetLineSearch() 722 @*/ 723 int SNESSetLineSearchCheck(SNES snes,int (*func)(SNES,void*,Vec,int*),void *checkctx) 724 { 725 int ierr, (*f)(SNES,int (*)(SNES,void*,Vec,int*),void*); 726 727 PetscFunctionBegin; 728 ierr = PetscObjectQueryFunction((PetscObject)snes,"SNESSetLineSearchCheck_C",(void **)&f);CHKERRQ(ierr); 729 if (f) { 730 ierr = (*f)(snes,func,checkctx);CHKERRQ(ierr); 731 } 732 PetscFunctionReturn(0); 733 } 734 /* -------------------------------------------------------------------------- */ 735 EXTERN_C_BEGIN 736 #undef __FUNC__ 737 #define __FUNC__ "SNESSetLineSearchCheck_LS" 738 int SNESSetLineSearchCheck_LS(SNES snes,int (*func)(SNES,void*,Vec,int*),void *checkctx) 739 { 740 PetscFunctionBegin; 741 ((SNES_LS *)(snes->data))->CheckStep = func; 742 ((SNES_LS *)(snes->data))->checkP = checkctx; 743 PetscFunctionReturn(0); 744 } 745 EXTERN_C_END 746 /* -------------------------------------------------------------------------- */ 747 /* 748 SNESPrintHelp_EQ_LS - Prints all options for the SNES_EQ_LS method. 749 750 Input Parameter: 751 . snes - the SNES context 752 753 Application Interface Routine: SNESPrintHelp() 754 */ 755 #undef __FUNC__ 756 #define __FUNC__ "SNESPrintHelp_EQ_LS" 757 static int SNESPrintHelp_EQ_LS(SNES snes,char *p) 758 { 759 SNES_LS *ls = (SNES_LS *)snes->data; 760 761 PetscFunctionBegin; 762 (*PetscHelpPrintf)(snes->comm," method SNES_EQ_LS (ls) for systems of nonlinear equations:\n"); 763 (*PetscHelpPrintf)(snes->comm," %ssnes_eq_ls [cubic,quadratic,basic,basicnonorms,...]\n",p); 764 (*PetscHelpPrintf)(snes->comm," %ssnes_eq_ls_alpha <alpha> (default %g)\n",p,ls->alpha); 765 (*PetscHelpPrintf)(snes->comm," %ssnes_eq_ls_maxstep <max> (default %g)\n",p,ls->maxstep); 766 (*PetscHelpPrintf)(snes->comm," %ssnes_eq_ls_steptol <tol> (default %g)\n",p,ls->steptol); 767 PetscFunctionReturn(0); 768 } 769 /* -------------------------------------------------------------------------- */ 770 /* 771 SNESView_EQ_LS - Prints info from the SNES_EQ_LS data structure. 772 773 Input Parameters: 774 . SNES - the SNES context 775 . viewer - visualization context 776 777 Application Interface Routine: SNESView() 778 */ 779 #undef __FUNC__ 780 #define __FUNC__ "SNESView_EQ_LS" 781 static int SNESView_EQ_LS(SNES snes,Viewer viewer) 782 { 783 SNES_LS *ls = (SNES_LS *)snes->data; 784 char *cstr; 785 int ierr; 786 ViewerType vtype; 787 788 PetscFunctionBegin; 789 ierr = ViewerGetType(viewer,&vtype); CHKERRQ(ierr); 790 if (PetscTypeCompare(vtype,ASCII_VIEWER)) { 791 if (ls->LineSearch == SNESNoLineSearch) cstr = "SNESNoLineSearch"; 792 else if (ls->LineSearch == SNESQuadraticLineSearch) cstr = "SNESQuadraticLineSearch"; 793 else if (ls->LineSearch == SNESCubicLineSearch) cstr = "SNESCubicLineSearch"; 794 else cstr = "unknown"; 795 ViewerASCIIPrintf(viewer," line search variant: %s\n",cstr); 796 ViewerASCIIPrintf(viewer," alpha=%g, maxstep=%g, steptol=%g\n",ls->alpha,ls->maxstep,ls->steptol); 797 } else { 798 SETERRQ(1,1,"Viewer type not supported for this object"); 799 } 800 PetscFunctionReturn(0); 801 } 802 /* -------------------------------------------------------------------------- */ 803 /* 804 SNESSetFromOptions_EQ_LS - Sets various parameters for the SNES_EQ_LS method. 805 806 Input Parameter: 807 . snes - the SNES context 808 809 Application Interface Routine: SNESSetFromOptions() 810 */ 811 #undef __FUNC__ 812 #define __FUNC__ "SNESSetFromOptions_EQ_LS" 813 static int SNESSetFromOptions_EQ_LS(SNES snes) 814 { 815 SNES_LS *ls = (SNES_LS *)snes->data; 816 char ver[16]; 817 double tmp; 818 int ierr,flg; 819 820 PetscFunctionBegin; 821 ierr = OptionsGetDouble(snes->prefix,"-snes_eq_ls_alpha",&tmp, &flg);CHKERRQ(ierr); 822 if (flg) { 823 ls->alpha = tmp; 824 } 825 ierr = OptionsGetDouble(snes->prefix,"-snes_eq_ls_maxstep",&tmp, &flg);CHKERRQ(ierr); 826 if (flg) { 827 ls->maxstep = tmp; 828 } 829 ierr = OptionsGetDouble(snes->prefix,"-snes_eq_ls_steptol",&tmp, &flg);CHKERRQ(ierr); 830 if (flg) { 831 ls->steptol = tmp; 832 } 833 ierr = OptionsGetString(snes->prefix,"-snes_eq_ls",ver,16, &flg); CHKERRQ(ierr); 834 if (flg) { 835 if (!PetscStrcmp(ver,"basic")) { 836 ierr = SNESSetLineSearch(snes,SNESNoLineSearch,PETSC_NULL);CHKERRQ(ierr); 837 } else if (!PetscStrcmp(ver,"basicnonorms")) { 838 ierr = SNESSetLineSearch(snes,SNESNoLineSearchNoNorms,PETSC_NULL);CHKERRQ(ierr); 839 } else if (!PetscStrcmp(ver,"quadratic")) { 840 ierr = SNESSetLineSearch(snes,SNESQuadraticLineSearch,PETSC_NULL);CHKERRQ(ierr); 841 } else if (!PetscStrcmp(ver,"cubic")) { 842 ierr = SNESSetLineSearch(snes,SNESCubicLineSearch,PETSC_NULL);CHKERRQ(ierr); 843 } 844 else {SETERRQ(PETSC_ERR_ARG_OUTOFRANGE,0,"Unknown line search");} 845 } 846 PetscFunctionReturn(0); 847 } 848 /* -------------------------------------------------------------------------- */ 849 /* 850 SNESCreate_EQ_LS - Creates a nonlinear solver context for the SNES_EQ_LS method, 851 SNES_LS, and sets this as the private data within the generic nonlinear solver 852 context, SNES, that was created within SNESCreate(). 853 854 855 Input Parameter: 856 . snes - the SNES context 857 858 Application Interface Routine: SNESCreate() 859 */ 860 EXTERN_C_BEGIN 861 #undef __FUNC__ 862 #define __FUNC__ "SNESCreate_EQ_LS" 863 int SNESCreate_EQ_LS(SNES snes) 864 { 865 int ierr; 866 SNES_LS *neP; 867 868 PetscFunctionBegin; 869 if (snes->method_class != SNES_NONLINEAR_EQUATIONS) { 870 SETERRQ(PETSC_ERR_ARG_WRONG,0,"For SNES_NONLINEAR_EQUATIONS only"); 871 } 872 873 snes->setup = SNESSetUp_EQ_LS; 874 snes->solve = SNESSolve_EQ_LS; 875 snes->destroy = SNESDestroy_EQ_LS; 876 snes->converged = SNESConverged_EQ_LS; 877 snes->printhelp = SNESPrintHelp_EQ_LS; 878 snes->setfromoptions = SNESSetFromOptions_EQ_LS; 879 snes->view = SNESView_EQ_LS; 880 snes->nwork = 0; 881 882 neP = PetscNew(SNES_LS); CHKPTRQ(neP); 883 PLogObjectMemory(snes,sizeof(SNES_LS)); 884 snes->data = (void *) neP; 885 neP->alpha = 1.e-4; 886 neP->maxstep = 1.e8; 887 neP->steptol = 1.e-12; 888 neP->LineSearch = SNESCubicLineSearch; 889 neP->lsP = PETSC_NULL; 890 neP->CheckStep = PETSC_NULL; 891 neP->checkP = PETSC_NULL; 892 893 ierr = PetscObjectComposeFunction((PetscObject)snes,"SNESSetLineSearch_C","SNESSetLineSearch_LS", 894 (void*)SNESSetLineSearch_LS);CHKERRQ(ierr); 895 ierr = PetscObjectComposeFunction((PetscObject)snes,"SNESSetLineSearchCheck_C","SNESSetLineSearchCheck_LS", 896 (void*)SNESSetLineSearchCheck_LS);CHKERRQ(ierr); 897 898 PetscFunctionReturn(0); 899 } 900 EXTERN_C_END 901 902 903 904