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