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