xref: /petsc/src/ts/impls/pseudo/posindep.c (revision 2ff79c18c26c94ed8cb599682f680f231dca6444)
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