1 #include <../src/snes/impls/al/alimpl.h> /*I "petscsnes.h" I*/ 2 3 /* 4 This file implements a truncated Newton method with arc length continuation, 5 for solving a system of nonlinear equations, using the KSP, Vec, 6 and Mat interfaces for linear solvers, vectors, and matrices, 7 respectively. 8 */ 9 10 const char NewtonALExactCitation[] = "@article{Ritto-CorreaCamotim2008,\n" 11 " title={On the arc-length and other quadratic control methods: Established, less known and new implementation procedures},\n" 12 " volume={86},\n" 13 " ISSN={0045-7949},\n" 14 " DOI={10.1016/j.compstruc.2007.08.003},\n" 15 " number={11},\n" 16 " journal={Computers & Structures},\n" 17 " author={Ritto-Corr{\\^{e}}a, Manuel and Camotim, Dinar},\n" 18 " year={2008},\n" 19 " month=jun,\n" 20 " pages={1353-1368},\n" 21 "}\n"; 22 PetscBool NewtonALExactCitationSet = PETSC_FALSE; 23 const char NewtonALNormalCitation[] = "@article{LeonPaulinoPereiraMenezesLages_2011,\n" 24 " title={A Unified Library of Nonlinear Solution Schemes},\n" 25 " volume={64},\n" 26 " ISSN={0003-6900, 2379-0407},\n" 27 " DOI={10.1115/1.4006992},\n" 28 " number={4},\n" 29 " journal={Applied Mechanics Reviews},\n" 30 " author={Leon, Sofie E. and Paulino, Glaucio H. and Pereira, Anderson and Menezes, Ivan F. M. and Lages, Eduardo N.},\n" 31 " year={2011},\n" 32 " month=jul,\n" 33 " pages={040803},\n" 34 " language={en}\n" 35 "}\n"; 36 PetscBool NewtonALNormalCitationSet = PETSC_FALSE; 37 38 const char *const SNESNewtonALCorrectionTypes[] = {"EXACT", "NORMAL", "SNESNewtonALCorrectionType", "SNES_NEWTONAL_CORRECTION_", NULL}; 39 40 static PetscErrorCode SNESNewtonALCheckArcLength(SNES snes, Vec XStep, PetscReal lambdaStep, PetscReal stepSize) 41 { 42 PetscReal arcLength, arcLengthError; 43 SNES_NEWTONAL *al = (SNES_NEWTONAL *)snes->data; 44 45 PetscFunctionBegin; 46 PetscCall(VecNorm(XStep, NORM_2, &arcLength)); 47 arcLength = PetscSqrtReal(PetscSqr(arcLength) + al->psisq * lambdaStep * lambdaStep); 48 arcLengthError = PetscAbsReal(arcLength - stepSize); 49 50 if (arcLengthError > PETSC_SQRT_MACHINE_EPSILON) PetscCall(PetscInfo(snes, "Arc length differs from specified step size: computed=%18.16e, expected=%18.16e, error=%18.16e \n", (double)arcLength, (double)stepSize, (double)arcLengthError)); 51 PetscFunctionReturn(PETSC_SUCCESS); 52 } 53 54 /* stable implementation of roots of a*x^2 + b*x + c = 0 */ 55 static inline void PetscQuadraticRoots(PetscReal a, PetscReal b, PetscReal c, PetscReal *xm, PetscReal *xp) 56 { 57 PetscReal temp = -0.5 * (b + PetscCopysignReal(1.0, b) * PetscSqrtReal(b * b - 4 * a * c)); 58 PetscReal x1 = temp / a; 59 PetscReal x2 = c / temp; 60 *xm = PetscMin(x1, x2); 61 *xp = PetscMax(x1, x2); 62 } 63 64 static PetscErrorCode SNESNewtonALSetCorrectionType_NEWTONAL(SNES snes, SNESNewtonALCorrectionType ctype) 65 { 66 SNES_NEWTONAL *al = (SNES_NEWTONAL *)snes->data; 67 68 PetscFunctionBegin; 69 al->correction_type = ctype; 70 PetscFunctionReturn(PETSC_SUCCESS); 71 } 72 73 /*@ 74 SNESNewtonALSetCorrectionType - Set the type of correction to use in the arc-length continuation method. 75 76 Logically Collective 77 78 Input Parameters: 79 + snes - the nonlinear solver object 80 - ctype - the type of correction to use 81 82 Options Database Key: 83 . -snes_newtonal_correction_type <type> - Set the type of correction to use; use -help for a list of available types 84 85 Level: intermediate 86 87 .seealso: [](ch_snes), `SNES`, `SNESNEWTONAL`, `SNESNewtonALCorrectionType` 88 @*/ 89 PetscErrorCode SNESNewtonALSetCorrectionType(SNES snes, SNESNewtonALCorrectionType ctype) 90 { 91 PetscFunctionBegin; 92 PetscValidHeaderSpecific(snes, SNES_CLASSID, 1); 93 PetscValidLogicalCollectiveEnum(snes, ctype, 2); 94 PetscTryMethod(snes, "SNESNewtonALSetCorrectionType_C", (SNES, SNESNewtonALCorrectionType), (snes, ctype)); 95 PetscFunctionReturn(PETSC_SUCCESS); 96 } 97 98 static PetscErrorCode SNESNewtonALSetFunction_NEWTONAL(SNES snes, SNESFunctionFn *func, void *ctx) 99 { 100 SNES_NEWTONAL *al = (SNES_NEWTONAL *)snes->data; 101 102 PetscFunctionBegin; 103 al->computealfunction = func; 104 al->alctx = ctx; 105 PetscFunctionReturn(PETSC_SUCCESS); 106 } 107 108 /*@C 109 SNESNewtonALSetFunction - Sets a user function that is called at each function evaluation to 110 compute the tangent load vector for the arc-length continuation method. 111 112 Logically Collective 113 114 Input Parameters: 115 + snes - the nonlinear solver object 116 . func - [optional] tangent load function evaluation routine, see `SNESFunctionFn` for the calling sequence. `U` is the current solution vector, `Q` is the output tangent load vector 117 - ctx - [optional] user-defined context for private data for the function evaluation routine (may be `NULL`) 118 119 Level: intermediate 120 121 Notes: 122 If the current value of the load parameter is needed in `func`, it can be obtained with `SNESNewtonALGetLoadParameter()`. 123 124 The tangent load vector is the partial derivative of external load with respect to the load parameter. 125 In the case of proportional loading, the tangent load vector is the full external load vector at the end of the load step. 126 127 .seealso: [](ch_snes), `SNES`, `SNESNEWTONAL`, `SNESNewtonALGetFunction()`, `SNESNewtonALGetLoadParameter()` 128 @*/ 129 PetscErrorCode SNESNewtonALSetFunction(SNES snes, SNESFunctionFn *func, void *ctx) 130 { 131 PetscFunctionBegin; 132 PetscValidHeaderSpecific(snes, SNES_CLASSID, 1); 133 PetscTryMethod(snes, "SNESNewtonALSetFunction_C", (SNES, SNESFunctionFn *, void *), (snes, func, ctx)); 134 PetscFunctionReturn(PETSC_SUCCESS); 135 } 136 137 static PetscErrorCode SNESNewtonALGetFunction_NEWTONAL(SNES snes, SNESFunctionFn **func, void **ctx) 138 { 139 SNES_NEWTONAL *al = (SNES_NEWTONAL *)snes->data; 140 141 PetscFunctionBegin; 142 if (func) *func = al->computealfunction; 143 if (ctx) *ctx = al->alctx; 144 PetscFunctionReturn(PETSC_SUCCESS); 145 } 146 147 /*@C 148 SNESNewtonALGetFunction - Get the user function and context set with `SNESNewtonALSetFunction` 149 150 Logically Collective 151 152 Input Parameters: 153 + snes - the nonlinear solver object 154 . func - [optional] tangent load function evaluation routine, see `SNESNewtonALSetFunction()` for the call sequence 155 - ctx - [optional] user-defined context for private data for the function evaluation routine (may be `NULL`) 156 157 Level: intermediate 158 159 .seealso: [](ch_snes), `SNES`, `SNESNEWTONAL`, `SNESNewtonALSetFunction()` 160 @*/ 161 PetscErrorCode SNESNewtonALGetFunction(SNES snes, SNESFunctionFn **func, void **ctx) 162 { 163 PetscFunctionBegin; 164 PetscValidHeaderSpecific(snes, SNES_CLASSID, 1); 165 PetscUseMethod(snes, "SNESNewtonALGetFunction_C", (SNES, SNESFunctionFn **, void **), (snes, func, ctx)); 166 PetscFunctionReturn(PETSC_SUCCESS); 167 } 168 169 static PetscErrorCode SNESNewtonALGetLoadParameter_NEWTONAL(SNES snes, PetscReal *lambda) 170 { 171 SNES_NEWTONAL *al; 172 173 PetscFunctionBeginHot; 174 al = (SNES_NEWTONAL *)snes->data; 175 *lambda = al->lambda; 176 PetscFunctionReturn(PETSC_SUCCESS); 177 } 178 179 /*@C 180 SNESNewtonALGetLoadParameter - Get the value of the load parameter `lambda` for the arc-length continuation method. 181 182 Logically Collective 183 184 Input Parameter: 185 . snes - the nonlinear solver object 186 187 Output Parameter: 188 . lambda - the arc-length parameter 189 190 Level: intermediate 191 192 Notes: 193 This function should be used in the functions provided to `SNESSetFunction()` and `SNESNewtonALSetFunction()` 194 to compute the residual and tangent load vectors for a given value of `lambda` (0 <= lambda <= 1). 195 196 Usually, `lambda` is used to scale the external force vector in the residual function, i.e. proportional loading, 197 in which case the tangent load vector is the full external force vector. 198 199 .seealso: [](ch_snes), `SNES`, `SNESNEWTONAL`, `SNESNewtonALSetFunction()` 200 @*/ 201 PetscErrorCode SNESNewtonALGetLoadParameter(SNES snes, PetscReal *lambda) 202 { 203 PetscFunctionBeginHot; 204 PetscValidHeaderSpecific(snes, SNES_CLASSID, 1); 205 PetscAssertPointer(lambda, 2); 206 PetscUseMethod(snes, "SNESNewtonALGetLoadParameter_C", (SNES, PetscReal *), (snes, lambda)); 207 PetscFunctionReturn(PETSC_SUCCESS); 208 } 209 210 static PetscErrorCode SNESNewtonALComputeFunction_NEWTONAL(SNES snes, Vec X, Vec Q) 211 { 212 void *ctx = NULL; 213 SNESFunctionFn *computealfunction = NULL; 214 SNES_NEWTONAL *al; 215 216 PetscFunctionBegin; 217 al = (SNES_NEWTONAL *)snes->data; 218 PetscCall(SNESNewtonALGetFunction(snes, &computealfunction, &ctx)); 219 220 PetscCall(VecZeroEntries(Q)); 221 PetscCheck(computealfunction || (snes->vec_rhs && al->scale_rhs), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "No tangent load function or rhs vector has been set"); 222 if (computealfunction) { 223 PetscCall(VecLockReadPush(X)); 224 PetscCallBack("SNES callback NewtonAL tangent load function", (*computealfunction)(snes, X, Q, ctx)); 225 PetscCall(VecLockReadPop(X)); 226 } 227 if (al->scale_rhs && snes->vec_rhs) { 228 /* Save original RHS vector values, then scale `snes->vec_rhs` by load parameter */ 229 if (!al->vec_rhs_orig) { PetscCall(VecDuplicate(snes->vec_rhs, &al->vec_rhs_orig)); } 230 if (!al->copied_rhs) { 231 PetscCall(VecCopy(snes->vec_rhs, al->vec_rhs_orig)); 232 al->copied_rhs = PETSC_TRUE; 233 } 234 PetscCall(VecAXPBY(snes->vec_rhs, al->lambda, 0.0, al->vec_rhs_orig)); 235 PetscCall(VecAXPY(Q, 1, al->vec_rhs_orig)); 236 } 237 PetscFunctionReturn(PETSC_SUCCESS); 238 } 239 240 /*@C 241 SNESNewtonALComputeFunction - Calls the function that has been set with `SNESNewtonALSetFunction()`. 242 243 Collective 244 245 Input Parameters: 246 + snes - the `SNES` context 247 - X - input vector 248 249 Output Parameter: 250 . Q - tangent load vector, as set by `SNESNewtonALSetFunction()` 251 252 Level: developer 253 254 Note: 255 `SNESNewtonALComputeFunction()` is typically used within nonlinear solvers 256 implementations, so users would not generally call this routine themselves. 257 258 .seealso: [](ch_snes), `SNES`, `SNESNewtonALSetFunction()`, `SNESNewtonALGetFunction()` 259 @*/ 260 PetscErrorCode SNESNewtonALComputeFunction(SNES snes, Vec X, Vec Q) 261 { 262 PetscFunctionBegin; 263 PetscValidHeaderSpecific(snes, SNES_CLASSID, 1); 264 PetscValidHeaderSpecific(X, VEC_CLASSID, 2); 265 PetscValidHeaderSpecific(Q, VEC_CLASSID, 3); 266 PetscCheckSameComm(snes, 1, X, 2); 267 PetscCheckSameComm(snes, 1, Q, 3); 268 PetscCall(VecValidValues_Internal(X, 2, PETSC_TRUE)); 269 PetscCall(PetscLogEventBegin(SNES_NewtonALEval, snes, X, Q, 0)); 270 PetscTryMethod(snes, "SNESNewtonALComputeFunction_C", (SNES, Vec, Vec), (snes, X, Q)); 271 PetscCall(PetscLogEventEnd(SNES_NewtonALEval, snes, X, Q, 0)); 272 PetscFunctionReturn(PETSC_SUCCESS); 273 } 274 275 /* 276 SNESSolve_NEWTONAL - Solves a nonlinear system with Newton's method with arc length continuation. 277 278 Input Parameter: 279 . snes - the `SNES` context 280 281 Application Interface Routine: SNESSolve() 282 */ 283 static PetscErrorCode SNESSolve_NEWTONAL(SNES snes) 284 { 285 SNES_NEWTONAL *data = (SNES_NEWTONAL *)snes->data; 286 PetscInt maxits, maxincs, lits; 287 PetscReal fnorm, xnorm, ynorm, stepSize; 288 Vec DeltaX, deltaX, X, R, Q, deltaX_Q, deltaX_R; 289 290 PetscFunctionBegin; 291 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); 292 293 /* Register citations */ 294 PetscCall(PetscCitationsRegister(SNESCitation, &SNEScite)); 295 if (data->correction_type == SNES_NEWTONAL_CORRECTION_EXACT) { 296 PetscCall(PetscCitationsRegister(NewtonALExactCitation, &NewtonALExactCitationSet)); 297 } else if (data->correction_type == SNES_NEWTONAL_CORRECTION_NORMAL) { 298 PetscCall(PetscCitationsRegister(NewtonALNormalCitation, &NewtonALNormalCitationSet)); 299 } 300 301 data->copied_rhs = PETSC_FALSE; 302 data->lambda_update = 0.0; 303 data->lambda = 0.0; 304 snes->numFailures = 0; 305 snes->numLinearSolveFailures = 0; 306 snes->reason = SNES_CONVERGED_ITERATING; 307 308 maxits = snes->max_its; /* maximum number of iterations */ 309 maxincs = data->max_continuation_steps; /* maximum number of increments */ 310 X = snes->vec_sol; /* solution vector */ 311 R = snes->vec_func; /* residual vector */ 312 Q = snes->work[0]; /* tangent load vector */ 313 deltaX_Q = snes->work[1]; /* variation of X with respect to lambda */ 314 deltaX_R = snes->work[2]; /* linearized error correction */ 315 DeltaX = snes->work[3]; /* step from equilibrium */ 316 deltaX = snes->vec_sol_update; /* full newton step */ 317 stepSize = data->step_size; /* initial step size */ 318 319 PetscCall(VecZeroEntries(DeltaX)); 320 321 /* set snes->max_its for convergence test */ 322 snes->max_its = maxits * maxincs; 323 324 /* main incremental-iterative loop */ 325 for (PetscInt i = 0; i < maxincs || maxincs < 0; i++) { 326 PetscReal deltaLambda; 327 328 PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes)); 329 snes->norm = 0.0; 330 PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes)); 331 PetscCall(SNESNewtonALComputeFunction(snes, X, Q)); 332 PetscCall(SNESComputeFunction(snes, X, R)); 333 PetscCall(VecAXPY(R, 1, Q)); /* R <- R + Q */ 334 PetscCall(VecNorm(R, NORM_2, &fnorm)); /* fnorm <- ||R|| */ 335 SNESCheckFunctionNorm(snes, fnorm); 336 337 /* Monitor convergence */ 338 PetscCall(SNESConverged(snes, snes->iter, 0.0, 0.0, fnorm)); 339 PetscCall(SNESMonitor(snes, snes->iter, fnorm)); 340 if (i == 0 && snes->reason) break; 341 for (PetscInt j = 0; j < maxits; j++) { 342 PetscReal normsqX_Q, deltaS = 1; 343 344 /* Call general purpose update function */ 345 PetscTryTypeMethod(snes, update, snes->iter); 346 347 PetscCall(SNESComputeJacobian(snes, X, snes->jacobian, snes->jacobian_pre)); 348 SNESCheckJacobianDomainerror(snes); 349 PetscCall(KSPSetOperators(snes->ksp, snes->jacobian, snes->jacobian_pre)); 350 /* Solve J deltaX_Q = Q, where J is Jacobian matrix */ 351 PetscCall(KSPSolve(snes->ksp, Q, deltaX_Q)); 352 SNESCheckKSPSolve(snes); 353 PetscCall(KSPGetIterationNumber(snes->ksp, &lits)); 354 PetscCall(PetscInfo(snes, "iter=%" PetscInt_FMT ", tangent load linear solve iterations=%" PetscInt_FMT "\n", snes->iter, lits)); 355 /* Compute load parameter variation */ 356 PetscCall(VecNorm(deltaX_Q, NORM_2, &normsqX_Q)); 357 normsqX_Q *= normsqX_Q; 358 /* On first iter, use predictor. This is the same regardless of corrector scheme. */ 359 if (j == 0) { 360 PetscReal sign = 1.0; 361 if (i > 0) { 362 PetscCall(VecDotRealPart(DeltaX, deltaX_Q, &sign)); 363 sign += data->psisq * data->lambda_update; 364 sign = sign >= 0 ? 1.0 : -1.0; 365 } 366 data->lambda_update = 0.0; 367 PetscCall(VecZeroEntries(DeltaX)); 368 deltaLambda = sign * stepSize / PetscSqrtReal(normsqX_Q + data->psisq); 369 } else { 370 /* Solve J deltaX_R = -R */ 371 PetscCall(KSPSolve(snes->ksp, R, deltaX_R)); 372 SNESCheckKSPSolve(snes); 373 PetscCall(KSPGetIterationNumber(snes->ksp, &lits)); 374 PetscCall(PetscInfo(snes, "iter=%" PetscInt_FMT ", residual linear solve iterations=%" PetscInt_FMT "\n", snes->iter, lits)); 375 PetscCall(VecScale(deltaX_R, -1)); 376 377 if (data->correction_type == SNES_NEWTONAL_CORRECTION_NORMAL) { 378 /* 379 Take a step orthogonal to the current incremental update DeltaX. 380 Note, this approach is cheaper than the exact correction, but may exhibit convergence 381 issues due to the iterative trial points not being on the quadratic constraint surface. 382 On the bright side, we always have a real and unique solution for deltaLambda. 383 */ 384 PetscScalar coefs[2]; 385 Vec rhs[] = {deltaX_R, deltaX_Q}; 386 387 PetscCall(VecMDot(DeltaX, 2, rhs, coefs)); 388 deltaLambda = -PetscRealPart(coefs[0]) / (PetscRealPart(coefs[1]) + data->psisq * data->lambda_update); 389 } else { 390 /* 391 Solve 392 a*deltaLambda^2 + b*deltaLambda + c = 0 (*) 393 where 394 a = a0 395 b = b0 + b1*deltaS 396 c = c0 + c1*deltaS + c2*deltaS^2 397 and deltaS is either 1, or the largest value in (0, 1) that satisfies 398 b^2 - 4*a*c = as*deltaS^2 + bs*deltaS + cs >= 0 399 where 400 as = b1^2 - 4*a0*c2 401 bs = 2*b1*b0 - 4*a0*c1 402 cs = b0^2 - 4*a0*c0 403 These "partial corrections" prevent (*) from having complex roots. 404 */ 405 PetscReal psisqLambdaUpdate, discriminant; 406 PetscReal as, bs, cs; 407 PetscReal a0, b0, b1, c0, c1, c2; 408 PetscScalar coefs1[3]; /* coefs[0] = deltaX_Q*DeltaX, coefs[1] = deltaX_R*DeltaX, coefs[2] = DeltaX*DeltaX */ 409 PetscScalar coefs2[2]; /* coefs[0] = deltaX_Q*deltaX_R, coefs[1] = deltaX_R*deltaX_R */ 410 const Vec rhs1[3] = {deltaX_Q, deltaX_R, DeltaX}; 411 const Vec rhs2[2] = {deltaX_Q, deltaX_R}; 412 413 psisqLambdaUpdate = data->psisq * data->lambda_update; 414 PetscCall(VecMDotBegin(DeltaX, 3, rhs1, coefs1)); 415 PetscCall(VecMDotBegin(deltaX_R, 2, rhs2, coefs2)); 416 PetscCall(VecMDotEnd(DeltaX, 3, rhs1, coefs1)); 417 PetscCall(VecMDotEnd(deltaX_R, 2, rhs2, coefs2)); 418 419 a0 = normsqX_Q + data->psisq; 420 b0 = 2 * (PetscRealPart(coefs1[0]) + psisqLambdaUpdate); 421 b1 = 2 * PetscRealPart(coefs2[0]); 422 c0 = PetscRealPart(coefs1[2]) + psisqLambdaUpdate * data->lambda_update - stepSize * stepSize; 423 c1 = 2 * PetscRealPart(coefs1[1]); 424 c2 = PetscRealPart(coefs2[1]); 425 426 as = b1 * b1 - 4 * a0 * c2; 427 bs = 2 * (b1 * b0 - 2 * a0 * c1); 428 cs = b0 * b0 - 4 * a0 * c0; 429 430 discriminant = cs + bs * deltaS + as * deltaS * deltaS; 431 432 if (discriminant < 0) { 433 /* Take deltaS < 1 with the unique root -b/(2*a) */ 434 PetscReal x1; 435 436 /* Compute deltaS to be the largest root of (as * x^2 + bs * x + cs = 0) */ 437 PetscQuadraticRoots(as, bs, cs, &x1, &deltaS); 438 PetscCall(PetscInfo(snes, "iter=%" PetscInt_FMT ", discriminant=%18.16e < 0, shrinking residual update size to deltaS = %18.16e\n", snes->iter, (double)discriminant, (double)deltaS)); 439 deltaLambda = -0.5 * (b0 + b1 * deltaS) / a0; 440 } else { 441 /* Use deltaS = 1, pick root that is closest to the last point to prevent doubling back */ 442 PetscReal dlambda1, dlambda2; 443 444 PetscQuadraticRoots(a0, b0 + b1, c0 + c1 + c2, &dlambda1, &dlambda2); 445 deltaLambda = (b0 * dlambda1) > (b0 * dlambda2) ? dlambda1 : dlambda2; 446 } 447 } 448 } 449 PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes)); 450 data->lambda = data->lambda + deltaLambda; 451 if (data->lambda > data->lambda_max) { 452 /* Ensure that lambda = lambda_max exactly at the end of incremental process. This ensures the final solution matches the problem we want to solve. */ 453 deltaLambda = deltaLambda - (data->lambda - data->lambda_max); 454 data->lambda = data->lambda_max; 455 } 456 if (data->lambda < data->lambda_min) { 457 // LCOV_EXCL_START 458 /* Ensure that lambda >= lambda_min. This prevents some potential oscillatory behavior. */ 459 deltaLambda = deltaLambda - (data->lambda - data->lambda_min); 460 data->lambda = data->lambda_min; 461 // LCOV_EXCL_STOP 462 } 463 data->lambda_update = data->lambda_update + deltaLambda; 464 PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes)); 465 PetscCall(PetscInfo(snes, "iter=%" PetscInt_FMT ", lambda=%18.16e, lambda_update=%18.16e\n", snes->iter, (double)data->lambda, (double)data->lambda_update)); 466 if (j == 0) { 467 /* deltaX = deltaLambda*deltaX_Q */ 468 PetscCall(VecCopy(deltaX_Q, deltaX)); 469 PetscCall(VecScale(deltaX, deltaLambda)); 470 } else { 471 /* deltaX = deltaS*deltaX_R + deltaLambda*deltaX_Q */ 472 PetscCall(VecAXPBYPCZ(deltaX, deltaS, deltaLambda, 0, deltaX_R, deltaX_Q)); 473 } 474 PetscCall(VecAXPY(DeltaX, 1, deltaX)); 475 PetscCall(VecAXPY(X, 1, deltaX)); 476 /* Q = -dF/dlambda(X, lambda)*/ 477 PetscCall(SNESNewtonALComputeFunction(snes, X, Q)); 478 /* R = F(X, lambda) */ 479 PetscCall(SNESComputeFunction(snes, X, R)); 480 PetscCall(VecNormBegin(R, NORM_2, &fnorm)); 481 PetscCall(VecNormBegin(X, NORM_2, &xnorm)); 482 PetscCall(VecNormBegin(deltaX, NORM_2, &ynorm)); 483 PetscCall(VecNormEnd(R, NORM_2, &fnorm)); 484 PetscCall(VecNormEnd(X, NORM_2, &xnorm)); 485 PetscCall(VecNormEnd(deltaX, NORM_2, &ynorm)); 486 487 if (PetscLogPrintInfo) PetscCall(SNESNewtonALCheckArcLength(snes, DeltaX, data->lambda_update, stepSize)); 488 489 /* Monitor convergence */ 490 PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes)); 491 snes->iter++; 492 snes->norm = fnorm; 493 snes->ynorm = ynorm; 494 snes->xnorm = xnorm; 495 PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes)); 496 PetscCall(SNESLogConvergenceHistory(snes, snes->norm, lits)); 497 PetscCall(SNESConverged(snes, snes->iter, xnorm, ynorm, fnorm)); 498 PetscCall(SNESMonitor(snes, snes->iter, snes->norm)); 499 if (snes->reason) break; 500 if (j == maxits - 1) { 501 snes->reason = SNES_DIVERGED_MAX_IT; 502 break; 503 } 504 } 505 if (snes->reason < 0) break; 506 if (data->lambda >= data->lambda_max) { 507 break; 508 } else if (maxincs > 0 && i == maxincs - 1) { 509 snes->reason = SNES_DIVERGED_MAX_IT; 510 break; 511 } else { 512 snes->reason = SNES_CONVERGED_ITERATING; 513 } 514 } 515 /* Reset RHS vector, if changed */ 516 if (data->copied_rhs) { 517 PetscCall(VecCopy(data->vec_rhs_orig, snes->vec_rhs)); 518 data->copied_rhs = PETSC_FALSE; 519 } 520 snes->max_its = maxits; /* reset snes->max_its */ 521 PetscFunctionReturn(PETSC_SUCCESS); 522 } 523 524 /* 525 SNESSetUp_NEWTONAL - Sets up the internal data structures for the later use 526 of the SNESNEWTONAL nonlinear solver. 527 528 Input Parameter: 529 . snes - the SNES context 530 . x - the solution vector 531 532 Application Interface Routine: SNESSetUp() 533 */ 534 static PetscErrorCode SNESSetUp_NEWTONAL(SNES snes) 535 { 536 PetscFunctionBegin; 537 PetscCall(SNESSetWorkVecs(snes, 4)); 538 PetscCall(SNESSetUpMatrices(snes)); 539 PetscFunctionReturn(PETSC_SUCCESS); 540 } 541 542 /* 543 SNESSetFromOptions_NEWTONAL - Sets various parameters for the SNESNEWTONAL method. 544 545 Input Parameter: 546 . snes - the SNES context 547 548 Application Interface Routine: SNESSetFromOptions() 549 */ 550 static PetscErrorCode SNESSetFromOptions_NEWTONAL(SNES snes, PetscOptionItems PetscOptionsObject) 551 { 552 SNES_NEWTONAL *data = (SNES_NEWTONAL *)snes->data; 553 SNESNewtonALCorrectionType correction_type = data->correction_type; 554 555 PetscFunctionBegin; 556 PetscOptionsHeadBegin(PetscOptionsObject, "SNES Newton Arc Length options"); 557 PetscCall(PetscOptionsReal("-snes_newtonal_step_size", "Initial arc length increment step size", "SNESNewtonAL", data->step_size, &data->step_size, NULL)); 558 PetscCall(PetscOptionsInt("-snes_newtonal_max_continuation_steps", "Maximum number of increment steps", "SNESNewtonAL", data->max_continuation_steps, &data->max_continuation_steps, NULL)); 559 PetscCall(PetscOptionsReal("-snes_newtonal_psisq", "Regularization parameter for arc length continuation, 0 for cylindrical", "SNESNewtonAL", data->psisq, &data->psisq, NULL)); 560 PetscCall(PetscOptionsReal("-snes_newtonal_lambda_min", "Minimum value of the load parameter lambda", "SNESNewtonAL", data->lambda_min, &data->lambda_min, NULL)); 561 PetscCall(PetscOptionsReal("-snes_newtonal_lambda_max", "Maximum value of the load parameter lambda", "SNESNewtonAL", data->lambda_max, &data->lambda_max, NULL)); 562 PetscCall(PetscOptionsBool("-snes_newtonal_scale_rhs", "Scale the constant vector passed to `SNESSolve` by the load parameter lambda", "SNESNewtonAL", data->scale_rhs, &data->scale_rhs, NULL)); 563 PetscCall(PetscOptionsEnum("-snes_newtonal_correction_type", "Type of correction to use in the arc-length continuation method", "SNESNewtonALCorrectionType", SNESNewtonALCorrectionTypes, (PetscEnum)correction_type, (PetscEnum *)&correction_type, NULL)); 564 PetscCall(SNESNewtonALSetCorrectionType(snes, correction_type)); 565 PetscOptionsHeadEnd(); 566 PetscFunctionReturn(PETSC_SUCCESS); 567 } 568 569 static PetscErrorCode SNESReset_NEWTONAL(SNES snes) 570 { 571 SNES_NEWTONAL *al = (SNES_NEWTONAL *)snes->data; 572 573 PetscFunctionBegin; 574 PetscCall(VecDestroy(&al->vec_rhs_orig)); 575 PetscFunctionReturn(PETSC_SUCCESS); 576 } 577 578 /* 579 SNESDestroy_NEWTONAL - Destroys the private SNES_NEWTONAL context that was created 580 with SNESCreate_NEWTONAL(). 581 582 Input Parameter: 583 . snes - the SNES context 584 585 Application Interface Routine: SNESDestroy() 586 */ 587 static PetscErrorCode SNESDestroy_NEWTONAL(SNES snes) 588 { 589 PetscFunctionBegin; 590 PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNewtonALSetCorrectionType_C", NULL)); 591 PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNewtonALGetLoadParameter_C", NULL)); 592 PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNewtonALSetFunction_C", NULL)); 593 PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNewtonALGetFunction_C", NULL)); 594 PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNewtonALComputeFunction_C", NULL)); 595 PetscCall(PetscFree(snes->data)); 596 PetscFunctionReturn(PETSC_SUCCESS); 597 } 598 599 /*MC 600 SNESNEWTONAL - Newton based nonlinear solver that uses a arc-length continuation method to solve the nonlinear system. 601 602 Options Database Keys: 603 + -snes_newtonal_step_size <1.0> - Initial arc length increment step size 604 . -snes_newtonal_max_continuation_steps <100> - Maximum number of continuation steps, or negative for no limit (not recommended) 605 . -snes_newtonal_psisq <1.0> - Regularization parameter for arc length continuation, 0 for cylindrical. Larger values generally lead to more steps. 606 . -snes_newtonal_lambda_min <0.0> - Minimum value of the load parameter lambda 607 . -snes_newtonal_lambda_max <1.0> - Maximum value of the load parameter lambda 608 . -snes_newtonal_scale_rhs <true> - Scale the constant vector passed to `SNESSolve` by the load parameter lambda 609 - -snes_newtonal_correction_type <exact> - Type of correction to use in the arc-length continuation method, `exact` or `normal` 610 611 Level: intermediate 612 613 Note: 614 The exact correction scheme with partial updates is detailed in {cite}`Ritto-CorreaCamotim2008` and the implementation of the 615 normal correction scheme is based on {cite}`LeonPaulinoPereiraMenezesLages_2011`. 616 617 .seealso: [](ch_snes), `SNESCreate()`, `SNES`, `SNESSetType()`, `SNESNEWTONAL`, `SNESNewtonALSetFunction()`, `SNESNewtonALGetFunction()`, `SNESNewtonALGetLoadParameter()` 618 M*/ 619 PETSC_EXTERN PetscErrorCode SNESCreate_NEWTONAL(SNES snes) 620 { 621 SNES_NEWTONAL *arclengthParameters; 622 623 PetscFunctionBegin; 624 snes->ops->setup = SNESSetUp_NEWTONAL; 625 snes->ops->solve = SNESSolve_NEWTONAL; 626 snes->ops->destroy = SNESDestroy_NEWTONAL; 627 snes->ops->setfromoptions = SNESSetFromOptions_NEWTONAL; 628 snes->ops->reset = SNESReset_NEWTONAL; 629 630 snes->usesksp = PETSC_TRUE; 631 snes->usesnpc = PETSC_FALSE; 632 633 PetscCall(SNESParametersInitialize(snes)); 634 PetscObjectParameterSetDefault(snes, max_funcs, 30000); 635 PetscObjectParameterSetDefault(snes, max_its, 50); 636 637 snes->alwayscomputesfinalresidual = PETSC_TRUE; 638 639 PetscCall(PetscNew(&arclengthParameters)); 640 arclengthParameters->lambda = 0.0; 641 arclengthParameters->lambda_update = 0.0; 642 arclengthParameters->step_size = 1.0; 643 arclengthParameters->max_continuation_steps = 100; 644 arclengthParameters->psisq = 1.0; 645 arclengthParameters->lambda_min = 0.0; 646 arclengthParameters->lambda_max = 1.0; 647 arclengthParameters->scale_rhs = PETSC_TRUE; 648 arclengthParameters->correction_type = SNES_NEWTONAL_CORRECTION_EXACT; 649 snes->data = (void *)arclengthParameters; 650 651 PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNewtonALSetCorrectionType_C", SNESNewtonALSetCorrectionType_NEWTONAL)); 652 PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNewtonALGetLoadParameter_C", SNESNewtonALGetLoadParameter_NEWTONAL)); 653 PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNewtonALSetFunction_C", SNESNewtonALSetFunction_NEWTONAL)); 654 PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNewtonALGetFunction_C", SNESNewtonALGetFunction_NEWTONAL)); 655 PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNewtonALComputeFunction_C", SNESNewtonALComputeFunction_NEWTONAL)); 656 PetscFunctionReturn(PETSC_SUCCESS); 657 } 658