1 /* 2 Code for Timestepping with implicit backwards Euler. 3 */ 4 #include <petsc/private/tsimpl.h> /*I "petscts.h" I*/ 5 6 typedef struct { 7 Vec update; /* work vector where new solution is formed */ 8 Vec func; /* work vector where F(t[i],u[i]) is stored */ 9 Vec xdot; /* work vector for time derivative of state */ 10 11 /* information used for Pseudo-timestepping */ 12 13 PetscErrorCode (*dt)(TS, PetscReal *, void *); /* compute next timestep, and related context */ 14 void *dtctx; 15 PetscErrorCode (*verify)(TS, Vec, void *, PetscReal *, PetscBool *); /* verify previous timestep and related context */ 16 void *verifyctx; 17 18 PetscReal fnorm_initial, fnorm; /* original and current norm of F(u) */ 19 PetscReal fnorm_previous; 20 21 PetscReal dt_initial; /* initial time-step */ 22 PetscReal dt_increment; /* scaling that dt is incremented each time-step */ 23 PetscReal dt_max; /* maximum time step */ 24 PetscBool increment_dt_from_initial_dt; 25 PetscReal fatol, frtol; 26 27 PetscObjectState Xstate; /* state of input vector to TSComputeIFunction() with 0 Xdot */ 28 } TS_Pseudo; 29 30 /* ------------------------------------------------------------------------------*/ 31 32 /*@ 33 TSPseudoComputeTimeStep - Computes the next timestep for a currently running 34 pseudo-timestepping process. 35 36 Collective 37 38 Input Parameter: 39 . ts - timestep context 40 41 Output Parameter: 42 . dt - newly computed timestep 43 44 Level: developer 45 46 Note: 47 The routine to be called here to compute the timestep should be 48 set by calling `TSPseudoSetTimeStep()`. 49 50 .seealso: [](ch_ts), `TSPSEUDO`, `TSPseudoTimeStepDefault()`, `TSPseudoSetTimeStep()` 51 @*/ 52 PetscErrorCode TSPseudoComputeTimeStep(TS ts, PetscReal *dt) 53 { 54 TS_Pseudo *pseudo = (TS_Pseudo *)ts->data; 55 56 PetscFunctionBegin; 57 PetscCall(PetscLogEventBegin(TS_PseudoComputeTimeStep, ts, 0, 0, 0)); 58 PetscCall((*pseudo->dt)(ts, dt, pseudo->dtctx)); 59 PetscCall(PetscLogEventEnd(TS_PseudoComputeTimeStep, ts, 0, 0, 0)); 60 PetscFunctionReturn(PETSC_SUCCESS); 61 } 62 63 /* ------------------------------------------------------------------------------*/ 64 /*@C 65 TSPseudoVerifyTimeStepDefault - Default code to verify the quality of the last timestep. 66 67 Collective, No Fortran Support 68 69 Input Parameters: 70 + ts - the timestep context 71 . dtctx - unused timestep context 72 - update - latest solution vector 73 74 Output Parameters: 75 + newdt - the timestep to use for the next step 76 - flag - flag indicating whether the last time step was acceptable 77 78 Level: advanced 79 80 Note: 81 This routine always returns a flag of 1, indicating an acceptable 82 timestep. 83 84 .seealso: [](ch_ts), `TSPSEUDO`, `TSPseudoSetVerifyTimeStep()`, `TSPseudoVerifyTimeStep()` 85 @*/ 86 PetscErrorCode TSPseudoVerifyTimeStepDefault(TS ts, Vec update, void *dtctx, PetscReal *newdt, PetscBool *flag) 87 { 88 PetscFunctionBegin; 89 *flag = PETSC_TRUE; 90 PetscFunctionReturn(PETSC_SUCCESS); 91 } 92 93 /*@ 94 TSPseudoVerifyTimeStep - Verifies whether the last timestep was acceptable. 95 96 Collective 97 98 Input Parameters: 99 + ts - timestep context 100 - update - latest solution vector 101 102 Output Parameters: 103 + dt - newly computed timestep (if it had to shrink) 104 - flag - indicates if current timestep was ok 105 106 Level: advanced 107 108 Notes: 109 The routine to be called here to compute the timestep should be 110 set by calling `TSPseudoSetVerifyTimeStep()`. 111 112 .seealso: [](ch_ts), `TSPSEUDO`, `TSPseudoSetVerifyTimeStep()`, `TSPseudoVerifyTimeStepDefault()` 113 @*/ 114 PetscErrorCode TSPseudoVerifyTimeStep(TS ts, Vec update, PetscReal *dt, PetscBool *flag) 115 { 116 TS_Pseudo *pseudo = (TS_Pseudo *)ts->data; 117 118 PetscFunctionBegin; 119 *flag = PETSC_TRUE; 120 if (pseudo->verify) PetscCall((*pseudo->verify)(ts, update, pseudo->verifyctx, dt, flag)); 121 PetscFunctionReturn(PETSC_SUCCESS); 122 } 123 124 /* --------------------------------------------------------------------------------*/ 125 126 static PetscErrorCode TSStep_Pseudo(TS ts) 127 { 128 TS_Pseudo *pseudo = (TS_Pseudo *)ts->data; 129 PetscInt nits, lits, reject; 130 PetscBool stepok; 131 PetscReal next_time_step = ts->time_step; 132 133 PetscFunctionBegin; 134 if (ts->steps == 0) { 135 pseudo->dt_initial = ts->time_step; 136 PetscCall(VecCopy(ts->vec_sol, pseudo->update)); /* in all future updates pseudo->update already contains the current time solution */ 137 } 138 PetscCall(TSPseudoComputeTimeStep(ts, &next_time_step)); 139 for (reject = 0; reject < ts->max_reject; reject++, ts->reject++) { 140 ts->time_step = next_time_step; 141 PetscCall(TSPreStage(ts, ts->ptime + ts->time_step)); 142 PetscCall(SNESSolve(ts->snes, NULL, pseudo->update)); 143 PetscCall(SNESGetIterationNumber(ts->snes, &nits)); 144 PetscCall(SNESGetLinearSolveIterations(ts->snes, &lits)); 145 ts->snes_its += nits; 146 ts->ksp_its += lits; 147 PetscCall(TSPostStage(ts, ts->ptime + ts->time_step, 0, &pseudo->update)); 148 PetscCall(TSAdaptCheckStage(ts->adapt, ts, ts->ptime + ts->time_step, pseudo->update, &stepok)); 149 if (!stepok) { 150 next_time_step = ts->time_step; 151 continue; 152 } 153 pseudo->fnorm = -1; /* The current norm is no longer valid */ 154 PetscCall(TSPseudoVerifyTimeStep(ts, pseudo->update, &next_time_step, &stepok)); 155 if (stepok) break; 156 } 157 if (reject >= ts->max_reject) { 158 ts->reason = TS_DIVERGED_STEP_REJECTED; 159 PetscCall(PetscInfo(ts, "Step=%" PetscInt_FMT ", step rejections %" PetscInt_FMT " greater than current TS allowed, stopping solve\n", ts->steps, reject)); 160 PetscFunctionReturn(PETSC_SUCCESS); 161 } 162 163 PetscCall(VecCopy(pseudo->update, ts->vec_sol)); 164 ts->ptime += ts->time_step; 165 ts->time_step = next_time_step; 166 167 PetscCall(VecZeroEntries(pseudo->xdot)); 168 PetscCall(TSComputeIFunction(ts, ts->ptime, pseudo->update, pseudo->xdot, pseudo->func, PETSC_FALSE)); 169 PetscCall(PetscObjectStateGet((PetscObject)pseudo->update, &pseudo->Xstate)); 170 PetscCall(VecNorm(pseudo->func, NORM_2, &pseudo->fnorm)); 171 172 if (pseudo->fnorm < pseudo->fatol) { 173 ts->reason = TS_CONVERGED_PSEUDO_FATOL; 174 PetscCall(PetscInfo(ts, "Step=%" PetscInt_FMT ", converged since fnorm %g < fatol %g\n", ts->steps, (double)pseudo->fnorm, (double)pseudo->frtol)); 175 PetscFunctionReturn(PETSC_SUCCESS); 176 } 177 if (pseudo->fnorm / pseudo->fnorm_initial < pseudo->frtol) { 178 ts->reason = TS_CONVERGED_PSEUDO_FRTOL; 179 PetscCall(PetscInfo(ts, "Step=%" PetscInt_FMT ", converged since fnorm %g / fnorm_initial %g < frtol %g\n", ts->steps, (double)pseudo->fnorm, (double)pseudo->fnorm_initial, (double)pseudo->fatol)); 180 PetscFunctionReturn(PETSC_SUCCESS); 181 } 182 PetscFunctionReturn(PETSC_SUCCESS); 183 } 184 185 /*------------------------------------------------------------*/ 186 static PetscErrorCode TSReset_Pseudo(TS ts) 187 { 188 TS_Pseudo *pseudo = (TS_Pseudo *)ts->data; 189 190 PetscFunctionBegin; 191 PetscCall(VecDestroy(&pseudo->update)); 192 PetscCall(VecDestroy(&pseudo->func)); 193 PetscCall(VecDestroy(&pseudo->xdot)); 194 PetscFunctionReturn(PETSC_SUCCESS); 195 } 196 197 static PetscErrorCode TSDestroy_Pseudo(TS ts) 198 { 199 PetscFunctionBegin; 200 PetscCall(TSReset_Pseudo(ts)); 201 PetscCall(PetscFree(ts->data)); 202 PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSPseudoSetVerifyTimeStep_C", NULL)); 203 PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSPseudoSetTimeStepIncrement_C", NULL)); 204 PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSPseudoSetMaxTimeStep_C", NULL)); 205 PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSPseudoIncrementDtFromInitialDt_C", NULL)); 206 PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSPseudoSetTimeStep_C", NULL)); 207 PetscFunctionReturn(PETSC_SUCCESS); 208 } 209 210 /*------------------------------------------------------------*/ 211 212 /* 213 Compute Xdot = (X^{n+1}-X^n)/dt) = 0 214 */ 215 static PetscErrorCode TSPseudoGetXdot(TS ts, Vec X, Vec *Xdot) 216 { 217 TS_Pseudo *pseudo = (TS_Pseudo *)ts->data; 218 const PetscScalar mdt = 1.0 / ts->time_step, *xnp1, *xn; 219 PetscScalar *xdot; 220 PetscInt i, n; 221 222 PetscFunctionBegin; 223 *Xdot = NULL; 224 PetscCall(VecGetArrayRead(ts->vec_sol, &xn)); 225 PetscCall(VecGetArrayRead(X, &xnp1)); 226 PetscCall(VecGetArray(pseudo->xdot, &xdot)); 227 PetscCall(VecGetLocalSize(X, &n)); 228 for (i = 0; i < n; i++) xdot[i] = mdt * (xnp1[i] - xn[i]); 229 PetscCall(VecRestoreArrayRead(ts->vec_sol, &xn)); 230 PetscCall(VecRestoreArrayRead(X, &xnp1)); 231 PetscCall(VecRestoreArray(pseudo->xdot, &xdot)); 232 *Xdot = pseudo->xdot; 233 PetscFunctionReturn(PETSC_SUCCESS); 234 } 235 236 /* 237 The transient residual is 238 239 F(U^{n+1},(U^{n+1}-U^n)/dt) = 0 240 241 or for ODE, 242 243 (U^{n+1} - U^{n})/dt - F(U^{n+1}) = 0 244 245 This is the function that must be evaluated for transient simulation and for 246 finite difference Jacobians. On the first Newton step, this algorithm uses 247 a guess of U^{n+1} = U^n in which case the transient term vanishes and the 248 residual is actually the steady state residual. Pseudotransient 249 continuation as described in the literature is a linearly implicit 250 algorithm, it only takes this one full Newton step with the steady state 251 residual, and then advances to the next time step. 252 253 This routine avoids a repeated identical call to TSComputeRHSFunction() when that result 254 is already contained in pseudo->func due to the call to TSComputeIFunction() in TSStep_Pseudo() 255 256 */ 257 static PetscErrorCode SNESTSFormFunction_Pseudo(SNES snes, Vec X, Vec Y, TS ts) 258 { 259 Vec Xdot; 260 TSIFunctionFn *ifunction; 261 TSRHSFunctionFn *rhsfunction; 262 void *ctx; 263 DM dm; 264 TS_Pseudo *pseudo = (TS_Pseudo *)ts->data; 265 PetscBool KSPSNES; 266 PetscObjectState Xstate; 267 268 PetscFunctionBegin; 269 PetscValidHeaderSpecific(snes, SNES_CLASSID, 1); 270 PetscValidHeaderSpecific(X, VEC_CLASSID, 2); 271 PetscValidHeaderSpecific(Y, VEC_CLASSID, 3); 272 PetscValidHeaderSpecific(ts, TS_CLASSID, 4); 273 274 PetscCall(TSGetDM(ts, &dm)); 275 PetscCall(DMTSGetIFunction(dm, &ifunction, &ctx)); 276 PetscCall(DMTSGetRHSFunction(dm, &rhsfunction, NULL)); 277 PetscCheck(rhsfunction || ifunction, PetscObjectComm((PetscObject)ts), PETSC_ERR_USER, "Must call TSSetRHSFunction() and / or TSSetIFunction()"); 278 279 PetscCall(PetscObjectTypeCompare((PetscObject)snes, SNESKSPONLY, &KSPSNES)); 280 PetscCall(TSPseudoGetXdot(ts, X, &Xdot)); 281 PetscCall(PetscObjectStateGet((PetscObject)X, &Xstate)); 282 if (Xstate != pseudo->Xstate || ifunction || !KSPSNES) { 283 PetscCall(TSComputeIFunction(ts, ts->ptime + ts->time_step, X, Xdot, Y, PETSC_FALSE)); 284 } else { 285 /* reuse the TSComputeIFunction() result performed inside TSStep_Pseudo() */ 286 /* note that pseudo->func contains the negation of TSComputeRHSFunction() */ 287 PetscCall(VecWAXPY(Y, 1, pseudo->func, Xdot)); 288 } 289 PetscFunctionReturn(PETSC_SUCCESS); 290 } 291 292 /* 293 This constructs the Jacobian needed for SNES. For DAE, this is 294 295 dF(X,Xdot)/dX + shift*dF(X,Xdot)/dXdot 296 297 and for ODE: 298 299 J = I/dt - J_{Frhs} where J_{Frhs} is the given Jacobian of Frhs. 300 */ 301 static PetscErrorCode SNESTSFormJacobian_Pseudo(SNES snes, Vec X, Mat AA, Mat BB, TS ts) 302 { 303 Vec Xdot; 304 305 PetscFunctionBegin; 306 PetscCall(TSPseudoGetXdot(ts, X, &Xdot)); 307 PetscCall(TSComputeIJacobian(ts, ts->ptime + ts->time_step, X, Xdot, 1. / ts->time_step, AA, BB, PETSC_FALSE)); 308 PetscFunctionReturn(PETSC_SUCCESS); 309 } 310 311 static PetscErrorCode TSSetUp_Pseudo(TS ts) 312 { 313 TS_Pseudo *pseudo = (TS_Pseudo *)ts->data; 314 315 PetscFunctionBegin; 316 PetscCall(VecDuplicate(ts->vec_sol, &pseudo->update)); 317 PetscCall(VecDuplicate(ts->vec_sol, &pseudo->func)); 318 PetscCall(VecDuplicate(ts->vec_sol, &pseudo->xdot)); 319 PetscFunctionReturn(PETSC_SUCCESS); 320 } 321 /*------------------------------------------------------------*/ 322 323 static PetscErrorCode TSPseudoMonitorDefault(TS ts, PetscInt step, PetscReal ptime, Vec v, void *dummy) 324 { 325 TS_Pseudo *pseudo = (TS_Pseudo *)ts->data; 326 PetscViewer viewer = (PetscViewer)dummy; 327 328 PetscFunctionBegin; 329 if (pseudo->fnorm < 0) { /* The last computed norm is stale, recompute */ 330 PetscCall(VecZeroEntries(pseudo->xdot)); 331 PetscCall(TSComputeIFunction(ts, ts->ptime, ts->vec_sol, pseudo->xdot, pseudo->func, PETSC_FALSE)); 332 PetscCall(PetscObjectStateGet((PetscObject)ts->vec_sol, &pseudo->Xstate)); 333 PetscCall(VecNorm(pseudo->func, NORM_2, &pseudo->fnorm)); 334 } 335 PetscCall(PetscViewerASCIIAddTab(viewer, ((PetscObject)ts)->tablevel)); 336 PetscCall(PetscViewerASCIIPrintf(viewer, "TS %" PetscInt_FMT " dt %g time %g fnorm %g\n", step, (double)ts->time_step, (double)ptime, (double)pseudo->fnorm)); 337 PetscCall(PetscViewerASCIISubtractTab(viewer, ((PetscObject)ts)->tablevel)); 338 PetscFunctionReturn(PETSC_SUCCESS); 339 } 340 341 static PetscErrorCode TSSetFromOptions_Pseudo(TS ts, PetscOptionItems PetscOptionsObject) 342 { 343 TS_Pseudo *pseudo = (TS_Pseudo *)ts->data; 344 PetscBool flg = PETSC_FALSE; 345 PetscViewer viewer; 346 347 PetscFunctionBegin; 348 PetscOptionsHeadBegin(PetscOptionsObject, "Pseudo-timestepping options"); 349 PetscCall(PetscOptionsBool("-ts_monitor_pseudo", "Monitor convergence", "", flg, &flg, NULL)); 350 if (flg) { 351 PetscCall(PetscViewerASCIIOpen(PetscObjectComm((PetscObject)ts), "stdout", &viewer)); 352 PetscCall(TSMonitorSet(ts, TSPseudoMonitorDefault, viewer, (PetscCtxDestroyFn *)PetscViewerDestroy)); 353 } 354 flg = pseudo->increment_dt_from_initial_dt; 355 PetscCall(PetscOptionsBool("-ts_pseudo_increment_dt_from_initial_dt", "Increase dt as a ratio from original dt", "TSPseudoIncrementDtFromInitialDt", flg, &flg, NULL)); 356 pseudo->increment_dt_from_initial_dt = flg; 357 PetscCall(PetscOptionsReal("-ts_pseudo_increment", "Ratio to increase dt", "TSPseudoSetTimeStepIncrement", pseudo->dt_increment, &pseudo->dt_increment, NULL)); 358 PetscCall(PetscOptionsReal("-ts_pseudo_max_dt", "Maximum value for dt", "TSPseudoSetMaxTimeStep", pseudo->dt_max, &pseudo->dt_max, NULL)); 359 PetscCall(PetscOptionsReal("-ts_pseudo_fatol", "Tolerance for norm of function", "", pseudo->fatol, &pseudo->fatol, NULL)); 360 PetscCall(PetscOptionsReal("-ts_pseudo_frtol", "Relative tolerance for norm of function", "", pseudo->frtol, &pseudo->frtol, NULL)); 361 PetscOptionsHeadEnd(); 362 PetscFunctionReturn(PETSC_SUCCESS); 363 } 364 365 static PetscErrorCode TSView_Pseudo(TS ts, PetscViewer viewer) 366 { 367 PetscBool isascii; 368 369 PetscFunctionBegin; 370 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii)); 371 if (isascii) { 372 TS_Pseudo *pseudo = (TS_Pseudo *)ts->data; 373 PetscCall(PetscViewerASCIIPrintf(viewer, " frtol - relative tolerance in function value %g\n", (double)pseudo->frtol)); 374 PetscCall(PetscViewerASCIIPrintf(viewer, " fatol - absolute tolerance in function value %g\n", (double)pseudo->fatol)); 375 PetscCall(PetscViewerASCIIPrintf(viewer, " dt_initial - initial timestep %g\n", (double)pseudo->dt_initial)); 376 PetscCall(PetscViewerASCIIPrintf(viewer, " dt_increment - increase in timestep on successful step %g\n", (double)pseudo->dt_increment)); 377 PetscCall(PetscViewerASCIIPrintf(viewer, " dt_max - maximum time %g\n", (double)pseudo->dt_max)); 378 } 379 PetscFunctionReturn(PETSC_SUCCESS); 380 } 381 382 /*@C 383 TSPseudoSetVerifyTimeStep - Sets a user-defined routine to verify the quality of the 384 last timestep. 385 386 Logically Collective 387 388 Input Parameters: 389 + ts - timestep context 390 . dt - user-defined function to verify timestep 391 - ctx - [optional] user-defined context for private data for the timestep verification routine (may be `NULL`) 392 393 Calling sequence of `func`: 394 + ts - the time-step context 395 . update - latest solution vector 396 . ctx - [optional] user-defined timestep context 397 . newdt - the timestep to use for the next step 398 - flag - flag indicating whether the last time step was acceptable 399 400 Level: advanced 401 402 Note: 403 The routine set here will be called by `TSPseudoVerifyTimeStep()` 404 during the timestepping process. 405 406 .seealso: [](ch_ts), `TSPSEUDO`, `TSPseudoVerifyTimeStepDefault()`, `TSPseudoVerifyTimeStep()` 407 @*/ 408 PetscErrorCode TSPseudoSetVerifyTimeStep(TS ts, PetscErrorCode (*dt)(TS ts, Vec update, void *ctx, PetscReal *newdt, PetscBool *flag), void *ctx) 409 { 410 PetscFunctionBegin; 411 PetscValidHeaderSpecific(ts, TS_CLASSID, 1); 412 PetscTryMethod(ts, "TSPseudoSetVerifyTimeStep_C", (TS, PetscErrorCode (*)(TS, Vec, void *, PetscReal *, PetscBool *), void *), (ts, dt, ctx)); 413 PetscFunctionReturn(PETSC_SUCCESS); 414 } 415 416 /*@ 417 TSPseudoSetTimeStepIncrement - Sets the scaling increment applied to 418 dt when using the TSPseudoTimeStepDefault() routine. 419 420 Logically Collective 421 422 Input Parameters: 423 + ts - the timestep context 424 - inc - the scaling factor >= 1.0 425 426 Options Database Key: 427 . -ts_pseudo_increment <increment> - set pseudo increment 428 429 Level: advanced 430 431 .seealso: [](ch_ts), `TSPSEUDO`, `TSPseudoSetTimeStep()`, `TSPseudoTimeStepDefault()` 432 @*/ 433 PetscErrorCode TSPseudoSetTimeStepIncrement(TS ts, PetscReal inc) 434 { 435 PetscFunctionBegin; 436 PetscValidHeaderSpecific(ts, TS_CLASSID, 1); 437 PetscValidLogicalCollectiveReal(ts, inc, 2); 438 PetscTryMethod(ts, "TSPseudoSetTimeStepIncrement_C", (TS, PetscReal), (ts, inc)); 439 PetscFunctionReturn(PETSC_SUCCESS); 440 } 441 442 /*@ 443 TSPseudoSetMaxTimeStep - Sets the maximum time step 444 when using the TSPseudoTimeStepDefault() routine. 445 446 Logically Collective 447 448 Input Parameters: 449 + ts - the timestep context 450 - maxdt - the maximum time step, use a non-positive value to deactivate 451 452 Options Database Key: 453 . -ts_pseudo_max_dt <increment> - set pseudo max dt 454 455 Level: advanced 456 457 .seealso: [](ch_ts), `TSPSEUDO`, `TSPseudoSetTimeStep()`, `TSPseudoTimeStepDefault()` 458 @*/ 459 PetscErrorCode TSPseudoSetMaxTimeStep(TS ts, PetscReal maxdt) 460 { 461 PetscFunctionBegin; 462 PetscValidHeaderSpecific(ts, TS_CLASSID, 1); 463 PetscValidLogicalCollectiveReal(ts, maxdt, 2); 464 PetscTryMethod(ts, "TSPseudoSetMaxTimeStep_C", (TS, PetscReal), (ts, maxdt)); 465 PetscFunctionReturn(PETSC_SUCCESS); 466 } 467 468 /*@ 469 TSPseudoIncrementDtFromInitialDt - Indicates that a new timestep 470 is computed via the formula $ dt = initial\_dt*initial\_fnorm/current\_fnorm $ rather than the default update, $ dt = current\_dt*previous\_fnorm/current\_fnorm.$ 471 472 Logically Collective 473 474 Input Parameter: 475 . ts - the timestep context 476 477 Options Database Key: 478 . -ts_pseudo_increment_dt_from_initial_dt <true,false> - use the initial $dt$ to determine increment 479 480 Level: advanced 481 482 .seealso: [](ch_ts), `TSPSEUDO`, `TSPseudoSetTimeStep()`, `TSPseudoTimeStepDefault()` 483 @*/ 484 PetscErrorCode TSPseudoIncrementDtFromInitialDt(TS ts) 485 { 486 PetscFunctionBegin; 487 PetscValidHeaderSpecific(ts, TS_CLASSID, 1); 488 PetscTryMethod(ts, "TSPseudoIncrementDtFromInitialDt_C", (TS), (ts)); 489 PetscFunctionReturn(PETSC_SUCCESS); 490 } 491 492 /*@C 493 TSPseudoSetTimeStep - Sets the user-defined routine to be 494 called at each pseudo-timestep to update the timestep. 495 496 Logically Collective 497 498 Input Parameters: 499 + ts - timestep context 500 . dt - function to compute timestep 501 - ctx - [optional] user-defined context for private data required by the function (may be `NULL`) 502 503 Calling sequence of `dt`: 504 + ts - the `TS` context 505 . newdt - the newly computed timestep 506 - ctx - [optional] user-defined context 507 508 Level: intermediate 509 510 Notes: 511 The routine set here will be called by `TSPseudoComputeTimeStep()` 512 during the timestepping process. 513 514 If not set then `TSPseudoTimeStepDefault()` is automatically used 515 516 .seealso: [](ch_ts), `TSPSEUDO`, `TSPseudoTimeStepDefault()`, `TSPseudoComputeTimeStep()` 517 @*/ 518 PetscErrorCode TSPseudoSetTimeStep(TS ts, PetscErrorCode (*dt)(TS ts, PetscReal *newdt, void *ctx), void *ctx) 519 { 520 PetscFunctionBegin; 521 PetscValidHeaderSpecific(ts, TS_CLASSID, 1); 522 PetscTryMethod(ts, "TSPseudoSetTimeStep_C", (TS, PetscErrorCode (*)(TS, PetscReal *, void *), void *), (ts, dt, ctx)); 523 PetscFunctionReturn(PETSC_SUCCESS); 524 } 525 526 /* ----------------------------------------------------------------------------- */ 527 528 typedef PetscErrorCode (*FCN1)(TS, Vec, void *, PetscReal *, PetscBool *); /* force argument to next function to not be extern C*/ 529 static PetscErrorCode TSPseudoSetVerifyTimeStep_Pseudo(TS ts, FCN1 dt, void *ctx) 530 { 531 TS_Pseudo *pseudo = (TS_Pseudo *)ts->data; 532 533 PetscFunctionBegin; 534 pseudo->verify = dt; 535 pseudo->verifyctx = ctx; 536 PetscFunctionReturn(PETSC_SUCCESS); 537 } 538 539 static PetscErrorCode TSPseudoSetTimeStepIncrement_Pseudo(TS ts, PetscReal inc) 540 { 541 TS_Pseudo *pseudo = (TS_Pseudo *)ts->data; 542 543 PetscFunctionBegin; 544 pseudo->dt_increment = inc; 545 PetscFunctionReturn(PETSC_SUCCESS); 546 } 547 548 static PetscErrorCode TSPseudoSetMaxTimeStep_Pseudo(TS ts, PetscReal maxdt) 549 { 550 TS_Pseudo *pseudo = (TS_Pseudo *)ts->data; 551 552 PetscFunctionBegin; 553 pseudo->dt_max = maxdt; 554 PetscFunctionReturn(PETSC_SUCCESS); 555 } 556 557 static PetscErrorCode TSPseudoIncrementDtFromInitialDt_Pseudo(TS ts) 558 { 559 TS_Pseudo *pseudo = (TS_Pseudo *)ts->data; 560 561 PetscFunctionBegin; 562 pseudo->increment_dt_from_initial_dt = PETSC_TRUE; 563 PetscFunctionReturn(PETSC_SUCCESS); 564 } 565 566 typedef PetscErrorCode (*FCN2)(TS, PetscReal *, void *); /* force argument to next function to not be extern C*/ 567 static PetscErrorCode TSPseudoSetTimeStep_Pseudo(TS ts, FCN2 dt, void *ctx) 568 { 569 TS_Pseudo *pseudo = (TS_Pseudo *)ts->data; 570 571 PetscFunctionBegin; 572 pseudo->dt = dt; 573 pseudo->dtctx = ctx; 574 PetscFunctionReturn(PETSC_SUCCESS); 575 } 576 577 /*MC 578 TSPSEUDO - Solve steady state ODE and DAE problems with pseudo time stepping {cite}`ckk02` {cite}`kk97` 579 580 This method solves equations of the form 581 582 $$ 583 F(X,Xdot) = 0 584 $$ 585 586 for steady state using the iteration 587 588 $$ 589 [G'] S = -F(X,0) 590 X += S 591 $$ 592 593 where 594 595 $$ 596 G(Y) = F(Y,(Y-X)/dt) 597 $$ 598 599 This is linearly-implicit Euler with the residual always evaluated "at steady 600 state". See note below. 601 602 In addition to the modified solve, a dedicated adaptive timestepping scheme is implemented, mimicking the switched evolution relaxation in {cite}`ckk02`. 603 It determines the next timestep via 604 605 $$ 606 dt_{n} = r dt_{n-1} \frac{\Vert F(X_{n},0) \Vert}{\Vert F(X_{n-1},0) \Vert} 607 $$ 608 609 where $r$ is an additional growth factor (set by `-ts_pseudo_increment`). 610 An alternative formulation is also available that uses the initial timestep and function norm. 611 612 $$ 613 dt_{n} = r dt_{0} \frac{\Vert F(X_{n},0) \Vert}{\Vert F(X_{0},0) \Vert} 614 $$ 615 616 This is chosen by setting `-ts_pseudo_increment_dt_from_initial_dt`. 617 For either method, an upper limit on the timestep can be set by `-ts_pseudo_max_dt`. 618 619 Options Database Keys: 620 + -ts_pseudo_increment <real> - ratio of increase dt 621 . -ts_pseudo_increment_dt_from_initial_dt <truth> - Increase dt as a ratio from original dt 622 . -ts_pseudo_max_dt - Maximum dt for adaptive timestepping algorithm 623 . -ts_pseudo_monitor - Monitor convergence of the function norm 624 . -ts_pseudo_fatol <atol> - stop iterating when the function norm is less than `atol` 625 - -ts_pseudo_frtol <rtol> - stop iterating when the function norm divided by the initial function norm is less than `rtol` 626 627 Level: beginner 628 629 Notes: 630 The residual computed by this method includes the transient term (Xdot is computed instead of 631 always being zero), but since the prediction from the last step is always the solution from the 632 last step, on the first Newton iteration we have 633 634 $$ 635 Xdot = (Xpredicted - Xold)/dt = (Xold-Xold)/dt = 0 636 $$ 637 638 The Jacobian $ dF/dX + shift*dF/dXdot $ contains a non-zero $ dF/dXdot$ term. In the $ X' = F(X) $ case it 639 is $ dF/dX + shift*1/dt $. Hence still contains the $ 1/dt $ term so the Jacobian is not simply the 640 Jacobian of $ F $ and thus this pseudo-transient continuation is not just Newton on $F(x)=0$. 641 642 Therefore, the linear system solved by the first Newton iteration is equivalent to the one 643 described above and in the papers. If the user chooses to perform multiple Newton iterations, the 644 algorithm is no longer the one described in the referenced papers. 645 By default, the `SNESType` is set to `SNESKSPONLY` to match the algorithm from the referenced papers. 646 Setting the `SNESType` via `-snes_type` will override this default setting. 647 648 .seealso: [](ch_ts), `TSCreate()`, `TS`, `TSSetType()` 649 M*/ 650 PETSC_EXTERN PetscErrorCode TSCreate_Pseudo(TS ts) 651 { 652 TS_Pseudo *pseudo; 653 SNES snes; 654 SNESType stype; 655 656 PetscFunctionBegin; 657 ts->ops->reset = TSReset_Pseudo; 658 ts->ops->destroy = TSDestroy_Pseudo; 659 ts->ops->view = TSView_Pseudo; 660 ts->ops->setup = TSSetUp_Pseudo; 661 ts->ops->step = TSStep_Pseudo; 662 ts->ops->setfromoptions = TSSetFromOptions_Pseudo; 663 ts->ops->snesfunction = SNESTSFormFunction_Pseudo; 664 ts->ops->snesjacobian = SNESTSFormJacobian_Pseudo; 665 ts->default_adapt_type = TSADAPTNONE; 666 ts->usessnes = PETSC_TRUE; 667 668 PetscCall(TSGetSNES(ts, &snes)); 669 PetscCall(SNESGetType(snes, &stype)); 670 if (!stype) PetscCall(SNESSetType(snes, SNESKSPONLY)); 671 672 PetscCall(PetscNew(&pseudo)); 673 ts->data = (void *)pseudo; 674 675 pseudo->dt = TSPseudoTimeStepDefault; 676 pseudo->dtctx = NULL; 677 pseudo->dt_increment = 1.1; 678 pseudo->increment_dt_from_initial_dt = PETSC_FALSE; 679 pseudo->fnorm = -1; 680 pseudo->fnorm_initial = -1; 681 pseudo->fnorm_previous = -1; 682 #if defined(PETSC_USE_REAL_SINGLE) 683 pseudo->fatol = 1.e-25; 684 pseudo->frtol = 1.e-5; 685 #else 686 pseudo->fatol = 1.e-50; 687 pseudo->frtol = 1.e-12; 688 #endif 689 PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSPseudoSetVerifyTimeStep_C", TSPseudoSetVerifyTimeStep_Pseudo)); 690 PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSPseudoSetTimeStepIncrement_C", TSPseudoSetTimeStepIncrement_Pseudo)); 691 PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSPseudoSetMaxTimeStep_C", TSPseudoSetMaxTimeStep_Pseudo)); 692 PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSPseudoIncrementDtFromInitialDt_C", TSPseudoIncrementDtFromInitialDt_Pseudo)); 693 PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSPseudoSetTimeStep_C", TSPseudoSetTimeStep_Pseudo)); 694 PetscFunctionReturn(PETSC_SUCCESS); 695 } 696 697 /*@C 698 TSPseudoTimeStepDefault - Default code to compute pseudo-timestepping. Use with `TSPseudoSetTimeStep()`. 699 700 Collective, No Fortran Support 701 702 Input Parameters: 703 + ts - the timestep context 704 - dtctx - unused timestep context 705 706 Output Parameter: 707 . newdt - the timestep to use for the next step 708 709 Level: advanced 710 711 .seealso: [](ch_ts), `TSPseudoSetTimeStep()`, `TSPseudoComputeTimeStep()`, `TSPSEUDO` 712 @*/ 713 PetscErrorCode TSPseudoTimeStepDefault(TS ts, PetscReal *newdt, void *dtctx) 714 { 715 TS_Pseudo *pseudo = (TS_Pseudo *)ts->data; 716 PetscReal inc = pseudo->dt_increment; 717 718 PetscFunctionBegin; 719 if (pseudo->fnorm < 0.0) { 720 PetscCall(VecZeroEntries(pseudo->xdot)); 721 PetscCall(TSComputeIFunction(ts, ts->ptime, ts->vec_sol, pseudo->xdot, pseudo->func, PETSC_FALSE)); 722 PetscCall(PetscObjectStateGet((PetscObject)ts->vec_sol, &pseudo->Xstate)); 723 PetscCall(VecNorm(pseudo->func, NORM_2, &pseudo->fnorm)); 724 } 725 if (pseudo->fnorm_initial < 0) { 726 /* first time through so compute initial function norm */ 727 pseudo->fnorm_initial = pseudo->fnorm; 728 pseudo->fnorm_previous = pseudo->fnorm; 729 } 730 if (pseudo->fnorm == 0.0) *newdt = 1.e12 * inc * ts->time_step; 731 else if (pseudo->increment_dt_from_initial_dt) *newdt = inc * pseudo->dt_initial * pseudo->fnorm_initial / pseudo->fnorm; 732 else *newdt = inc * ts->time_step * pseudo->fnorm_previous / pseudo->fnorm; 733 if (pseudo->dt_max > 0) *newdt = PetscMin(*newdt, pseudo->dt_max); 734 pseudo->fnorm_previous = pseudo->fnorm; 735 PetscFunctionReturn(PETSC_SUCCESS); 736 } 737