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