Actual source code: posindep.c
1: /*
2: Code for Timestepping with implicit backwards Euler.
3: */
4: #include <private/tsimpl.h> /*I "petscts.h" I*/
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 */
11: /* information used for Pseudo-timestepping */
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;
18: PetscReal fnorm_initial,fnorm; /* original and current norm of F(u) */
19: PetscReal fnorm_previous;
21: PetscReal dt_initial; /* initial time-step */
22: PetscReal dt_increment; /* scaling that dt is incremented each time-step */
23: PetscBool increment_dt_from_initial_dt;
24: } TS_Pseudo;
26: /* ------------------------------------------------------------------------------*/
30: /*@
31: TSPseudoComputeTimeStep - Computes the next timestep for a currently running
32: pseudo-timestepping process.
34: Collective on TS
36: Input Parameter:
37: . ts - timestep context
39: Output Parameter:
40: . dt - newly computed timestep
42: Level: advanced
44: Notes:
45: The routine to be called here to compute the timestep should be
46: set by calling TSPseudoSetTimeStep().
48: .keywords: timestep, pseudo, compute
50: .seealso: TSPseudoDefaultTimeStep(), TSPseudoSetTimeStep()
51: @*/
52: PetscErrorCode TSPseudoComputeTimeStep(TS ts,PetscReal *dt)
53: {
54: TS_Pseudo *pseudo = (TS_Pseudo*)ts->data;
58: PetscLogEventBegin(TS_PseudoComputeTimeStep,ts,0,0,0);
59: (*pseudo->dt)(ts,dt,pseudo->dtctx);
60: PetscLogEventEnd(TS_PseudoComputeTimeStep,ts,0,0,0);
61: return(0);
62: }
65: /* ------------------------------------------------------------------------------*/
68: /*@C
69: TSPseudoDefaultVerifyTimeStep - Default code to verify the quality of the last timestep.
71: Collective on TS
73: Input Parameters:
74: + ts - the timestep context
75: . dtctx - unused timestep context
76: - update - latest solution vector
78: Output Parameters:
79: + newdt - the timestep to use for the next step
80: - flag - flag indicating whether the last time step was acceptable
82: Level: advanced
84: Note:
85: This routine always returns a flag of 1, indicating an acceptable
86: timestep.
88: .keywords: timestep, pseudo, default, verify
90: .seealso: TSPseudoSetVerifyTimeStep(), TSPseudoVerifyTimeStep()
91: @*/
92: PetscErrorCode TSPseudoDefaultVerifyTimeStep(TS ts,Vec update,void *dtctx,PetscReal *newdt,PetscBool *flag)
93: {
95: *flag = PETSC_TRUE;
96: return(0);
97: }
102: /*@
103: TSPseudoVerifyTimeStep - Verifies whether the last timestep was acceptable.
105: Collective on TS
107: Input Parameters:
108: + ts - timestep context
109: - update - latest solution vector
111: Output Parameters:
112: + dt - newly computed timestep (if it had to shrink)
113: - flag - indicates if current timestep was ok
115: Level: advanced
117: Notes:
118: The routine to be called here to compute the timestep should be
119: set by calling TSPseudoSetVerifyTimeStep().
121: .keywords: timestep, pseudo, verify
123: .seealso: TSPseudoSetVerifyTimeStep(), TSPseudoDefaultVerifyTimeStep()
124: @*/
125: PetscErrorCode TSPseudoVerifyTimeStep(TS ts,Vec update,PetscReal *dt,PetscBool *flag)
126: {
127: TS_Pseudo *pseudo = (TS_Pseudo*)ts->data;
131: if (!pseudo->verify) {*flag = PETSC_TRUE; return(0);}
133: (*pseudo->verify)(ts,update,pseudo->verifyctx,dt,flag);
135: return(0);
136: }
138: /* --------------------------------------------------------------------------------*/
142: static PetscErrorCode TSStep_Pseudo(TS ts)
143: {
144: TS_Pseudo *pseudo = (TS_Pseudo*)ts->data;
145: PetscInt its,lits,reject;
146: PetscBool stepok;
147: PetscReal next_time_step;
148: SNESConvergedReason snesreason = SNES_CONVERGED_ITERATING;
152: if (ts->steps == 0) {
153: pseudo->dt_initial = ts->time_step;
154: }
155: VecCopy(ts->vec_sol,pseudo->update);
156: next_time_step = ts->time_step;
157: TSPseudoComputeTimeStep(ts,&next_time_step);
158: for (reject=0; reject<ts->max_reject; reject++,ts->reject++) {
159: ts->time_step = next_time_step;
160: SNESSolve(ts->snes,PETSC_NULL,pseudo->update);
161: SNESGetConvergedReason(ts->snes,&snesreason);
162: SNESGetLinearSolveIterations(ts->snes,&lits);
163: SNESGetIterationNumber(ts->snes,&its);
164: ts->nonlinear_its += its; ts->linear_its += lits;
165: PetscInfo3(ts,"step=%D, nonlinear solve iterations=%D, linear solve iterations=%D\n",ts->steps,its,lits);
166: pseudo->fnorm = -1; /* The current norm is no longer valid, monitor must recompute it. */
167: TSPseudoVerifyTimeStep(ts,pseudo->update,&next_time_step,&stepok);
168: if (stepok) break;
169: }
170: if (snesreason < 0 && ++ts->num_snes_failures >= ts->max_snes_failures) {
171: ts->reason = TS_DIVERGED_NONLINEAR_SOLVE;
172: PetscInfo2(ts,"step=%D, nonlinear solve solve failures %D greater than current TS allowed, stopping solve\n",ts->steps,ts->num_snes_failures);
173: return(0);
174: }
175: if (reject >= ts->max_reject) {
176: ts->reason = TS_DIVERGED_STEP_REJECTED;
177: PetscInfo2(ts,"step=%D, step rejections %D greater than current TS allowed, stopping solve\n",ts->steps,reject);
178: return(0);
179: }
180: VecCopy(pseudo->update,ts->vec_sol);
181: ts->ptime += ts->time_step;
182: ts->time_step = next_time_step;
183: ts->steps++;
184: return(0);
185: }
187: /*------------------------------------------------------------*/
190: static PetscErrorCode TSReset_Pseudo(TS ts)
191: {
192: TS_Pseudo *pseudo = (TS_Pseudo*)ts->data;
196: VecDestroy(&pseudo->update);
197: VecDestroy(&pseudo->func);
198: VecDestroy(&pseudo->xdot);
199: return(0);
200: }
204: static PetscErrorCode TSDestroy_Pseudo(TS ts)
205: {
209: TSReset_Pseudo(ts);
210: PetscFree(ts->data);
211: PetscObjectComposeFunctionDynamic((PetscObject)ts,"TSPseudoSetVerifyTimeStep_C","",PETSC_NULL);
212: PetscObjectComposeFunctionDynamic((PetscObject)ts,"TSPseudoSetTimeStepIncrement_C","",PETSC_NULL);
213: PetscObjectComposeFunctionDynamic((PetscObject)ts,"TSPseudoIncrementDtFromInitialDt_C","",PETSC_NULL);
214: PetscObjectComposeFunctionDynamic((PetscObject)ts,"TSPseudoSetTimeStep_C","",PETSC_NULL);
215: return(0);
216: }
218: /*------------------------------------------------------------*/
222: /*
223: Compute Xdot = (X^{n+1}-X^n)/dt) = 0
224: */
225: static PetscErrorCode TSPseudoGetXdot(TS ts,Vec X,Vec *Xdot)
226: {
227: TS_Pseudo *pseudo = (TS_Pseudo*)ts->data;
228: const PetscScalar mdt = 1.0/ts->time_step,*xnp1,*xn;
229: PetscScalar *xdot;
231: PetscInt i,n;
234: VecGetArrayRead(ts->vec_sol,&xn);
235: VecGetArrayRead(X,&xnp1);
236: VecGetArray(pseudo->xdot,&xdot);
237: VecGetLocalSize(X,&n);
238: for (i=0; i<n; i++) {
239: xdot[i] = mdt*(xnp1[i] - xn[i]);
240: }
241: VecRestoreArrayRead(ts->vec_sol,&xn);
242: VecRestoreArrayRead(X,&xnp1);
243: VecRestoreArray(pseudo->xdot,&xdot);
244: *Xdot = pseudo->xdot;
245: return(0);
246: }
250: /*
251: The transient residual is
253: F(U^{n+1},(U^{n+1}-U^n)/dt) = 0
255: or for ODE,
257: (U^{n+1} - U^{n})/dt - F(U^{n+1}) = 0
259: This is the function that must be evaluated for transient simulation and for
260: finite difference Jacobians. On the first Newton step, this algorithm uses
261: a guess of U^{n+1} = U^n in which case the transient term vanishes and the
262: residual is actually the steady state residual. Pseudotransient
263: continuation as described in the literature is a linearly implicit
264: algorithm, it only takes this one Newton step with the steady state
265: residual, and then advances to the next time step.
266: */
267: static PetscErrorCode SNESTSFormFunction_Pseudo(SNES snes,Vec X,Vec Y,TS ts)
268: {
269: Vec Xdot;
273: TSPseudoGetXdot(ts,X,&Xdot);
274: TSComputeIFunction(ts,ts->ptime+ts->time_step,X,Xdot,Y,PETSC_FALSE);
275: return(0);
276: }
280: /*
281: This constructs the Jacobian needed for SNES. For DAE, this is
283: dF(X,Xdot)/dX + shift*dF(X,Xdot)/dXdot
285: and for ODE:
287: J = I/dt - J_{Frhs} where J_{Frhs} is the given Jacobian of Frhs.
288: */
289: static PetscErrorCode SNESTSFormJacobian_Pseudo(SNES snes,Vec X,Mat *AA,Mat *BB,MatStructure *str,TS ts)
290: {
291: Vec Xdot;
295: TSPseudoGetXdot(ts,X,&Xdot);
296: TSComputeIJacobian(ts,ts->ptime+ts->time_step,X,Xdot,1./ts->time_step,AA,BB,str,PETSC_FALSE);
297: return(0);
298: }
303: static PetscErrorCode TSSetUp_Pseudo(TS ts)
304: {
305: TS_Pseudo *pseudo = (TS_Pseudo*)ts->data;
309: VecDuplicate(ts->vec_sol,&pseudo->update);
310: VecDuplicate(ts->vec_sol,&pseudo->func);
311: VecDuplicate(ts->vec_sol,&pseudo->xdot);
312: return(0);
313: }
314: /*------------------------------------------------------------*/
318: PetscErrorCode TSPseudoMonitorDefault(TS ts,PetscInt step,PetscReal ptime,Vec v,void *dummy)
319: {
320: TS_Pseudo *pseudo = (TS_Pseudo*)ts->data;
321: PetscErrorCode ierr;
322: PetscViewer viewer = dummy ? (PetscViewer) dummy : PETSC_VIEWER_STDOUT_(((PetscObject)ts)->comm);
325: if (pseudo->fnorm < 0) { /* The last computed norm is stale, recompute */
326: VecZeroEntries(pseudo->xdot);
327: TSComputeIFunction(ts,ts->ptime,ts->vec_sol,pseudo->xdot,pseudo->func,PETSC_FALSE);
328: VecNorm(pseudo->func,NORM_2,&pseudo->fnorm);
329: }
330: PetscViewerASCIIAddTab(viewer,((PetscObject)ts)->tablevel);
331: PetscViewerASCIIPrintf(viewer,"TS %D dt %G time %G fnorm %G\n",step,ts->time_step,ptime,pseudo->fnorm);
332: PetscViewerASCIISubtractTab(viewer,((PetscObject)ts)->tablevel);
333: return(0);
334: }
338: static PetscErrorCode TSSetFromOptions_Pseudo(TS ts)
339: {
340: TS_Pseudo *pseudo = (TS_Pseudo*)ts->data;
341: PetscErrorCode ierr;
342: PetscBool flg = PETSC_FALSE;
343: PetscViewer viewer;
346: PetscOptionsHead("Pseudo-timestepping options");
347: PetscOptionsBool("-ts_monitor_pseudo","Monitor convergence","TSPseudoMonitorDefault",flg,&flg,PETSC_NULL);
348: if (flg) {
349: PetscViewerASCIIOpen(((PetscObject)ts)->comm,"stdout",&viewer);
350: TSMonitorSet(ts,TSPseudoMonitorDefault,viewer,(PetscErrorCode (*)(void**))PetscViewerDestroy);
351: }
352: flg = PETSC_FALSE;
353: PetscOptionsBool("-ts_pseudo_increment_dt_from_initial_dt","Increase dt as a ratio from original dt","TSPseudoIncrementDtFromInitialDt",flg,&flg,PETSC_NULL);
354: if (flg) {
355: TSPseudoIncrementDtFromInitialDt(ts);
356: }
357: PetscOptionsReal("-ts_pseudo_increment","Ratio to increase dt","TSPseudoSetTimeStepIncrement",pseudo->dt_increment,&pseudo->dt_increment,0);
359: SNESSetFromOptions(ts->snes);
360: PetscOptionsTail();
361: return(0);
362: }
366: static PetscErrorCode TSView_Pseudo(TS ts,PetscViewer viewer)
367: {
371: SNESView(ts->snes,viewer);
372: return(0);
373: }
375: /* ----------------------------------------------------------------------------- */
378: /*@C
379: TSPseudoSetVerifyTimeStep - Sets a user-defined routine to verify the quality of the
380: last timestep.
382: Logically Collective on TS
384: Input Parameters:
385: + ts - timestep context
386: . dt - user-defined function to verify timestep
387: - ctx - [optional] user-defined context for private data
388: for the timestep verification routine (may be PETSC_NULL)
390: Level: advanced
392: Calling sequence of func:
393: . func (TS ts,Vec update,void *ctx,PetscReal *newdt,PetscBool *flag);
395: . update - latest solution vector
396: . ctx - [optional] timestep context
397: . newdt - the timestep to use for the next step
398: . flag - flag indicating whether the last time step was acceptable
400: Notes:
401: The routine set here will be called by TSPseudoVerifyTimeStep()
402: during the timestepping process.
404: .keywords: timestep, pseudo, set, verify
406: .seealso: TSPseudoDefaultVerifyTimeStep(), TSPseudoVerifyTimeStep()
407: @*/
408: PetscErrorCode TSPseudoSetVerifyTimeStep(TS ts,PetscErrorCode (*dt)(TS,Vec,void*,PetscReal*,PetscBool *),void* ctx)
409: {
414: PetscTryMethod(ts,"TSPseudoSetVerifyTimeStep_C",(TS,PetscErrorCode (*)(TS,Vec,void*,PetscReal *,PetscBool *),void *),(ts,dt,ctx));
415: return(0);
416: }
420: /*@
421: TSPseudoSetTimeStepIncrement - Sets the scaling increment applied to
422: dt when using the TSPseudoDefaultTimeStep() routine.
424: Logically Collective on TS
426: Input Parameters:
427: + ts - the timestep context
428: - inc - the scaling factor >= 1.0
430: Options Database Key:
431: $ -ts_pseudo_increment <increment>
433: Level: advanced
435: .keywords: timestep, pseudo, set, increment
437: .seealso: TSPseudoSetTimeStep(), TSPseudoDefaultTimeStep()
438: @*/
439: PetscErrorCode TSPseudoSetTimeStepIncrement(TS ts,PetscReal inc)
440: {
446: PetscTryMethod(ts,"TSPseudoSetTimeStepIncrement_C",(TS,PetscReal),(ts,inc));
447: return(0);
448: }
452: /*@
453: TSPseudoIncrementDtFromInitialDt - Indicates that a new timestep
454: is computed via the formula
455: $ dt = initial_dt*initial_fnorm/current_fnorm
456: rather than the default update,
457: $ dt = current_dt*previous_fnorm/current_fnorm.
459: Logically Collective on TS
461: Input Parameter:
462: . ts - the timestep context
464: Options Database Key:
465: $ -ts_pseudo_increment_dt_from_initial_dt
467: Level: advanced
469: .keywords: timestep, pseudo, set, increment
471: .seealso: TSPseudoSetTimeStep(), TSPseudoDefaultTimeStep()
472: @*/
473: PetscErrorCode TSPseudoIncrementDtFromInitialDt(TS ts)
474: {
479: PetscTryMethod(ts,"TSPseudoIncrementDtFromInitialDt_C",(TS),(ts));
480: return(0);
481: }
486: /*@C
487: TSPseudoSetTimeStep - Sets the user-defined routine to be
488: called at each pseudo-timestep to update the timestep.
490: Logically Collective on TS
492: Input Parameters:
493: + ts - timestep context
494: . dt - function to compute timestep
495: - ctx - [optional] user-defined context for private data
496: required by the function (may be PETSC_NULL)
498: Level: intermediate
500: Calling sequence of func:
501: . func (TS ts,PetscReal *newdt,void *ctx);
503: . newdt - the newly computed timestep
504: . ctx - [optional] timestep context
506: Notes:
507: The routine set here will be called by TSPseudoComputeTimeStep()
508: during the timestepping process.
510: .keywords: timestep, pseudo, set
512: .seealso: TSPseudoDefaultTimeStep(), TSPseudoComputeTimeStep()
513: @*/
514: PetscErrorCode TSPseudoSetTimeStep(TS ts,PetscErrorCode (*dt)(TS,PetscReal*,void*),void* ctx)
515: {
520: PetscTryMethod(ts,"TSPseudoSetTimeStep_C",(TS,PetscErrorCode (*)(TS,PetscReal *,void *),void *),(ts,dt,ctx));
521: return(0);
522: }
524: /* ----------------------------------------------------------------------------- */
530: PetscErrorCode TSPseudoSetVerifyTimeStep_Pseudo(TS ts,FCN1 dt,void* ctx)
531: {
532: TS_Pseudo *pseudo;
535: pseudo = (TS_Pseudo*)ts->data;
536: pseudo->verify = dt;
537: pseudo->verifyctx = ctx;
538: return(0);
539: }
545: PetscErrorCode TSPseudoSetTimeStepIncrement_Pseudo(TS ts,PetscReal inc)
546: {
547: TS_Pseudo *pseudo = (TS_Pseudo*)ts->data;
550: pseudo->dt_increment = inc;
551: return(0);
552: }
558: PetscErrorCode TSPseudoIncrementDtFromInitialDt_Pseudo(TS ts)
559: {
560: TS_Pseudo *pseudo = (TS_Pseudo*)ts->data;
563: pseudo->increment_dt_from_initial_dt = PETSC_TRUE;
564: return(0);
565: }
572: PetscErrorCode TSPseudoSetTimeStep_Pseudo(TS ts,FCN2 dt,void* ctx)
573: {
574: TS_Pseudo *pseudo = (TS_Pseudo*)ts->data;
577: pseudo->dt = dt;
578: pseudo->dtctx = ctx;
579: return(0);
580: }
583: /* ----------------------------------------------------------------------------- */
584: /*MC
585: TSPSEUDO - Solve steady state ODE and DAE problems with pseudo time stepping
587: This method solves equations of the form
589: $ F(X,Xdot) = 0
591: for steady state using the iteration
593: $ [G'] S = -F(X,0)
594: $ X += S
596: where
598: $ G(Y) = F(Y,(Y-X)/dt)
600: This is linearly-implicit Euler with the residual always evaluated "at steady
601: state". See note below.
603: Options database keys:
604: + -ts_pseudo_increment <real> - ratio of increase dt
605: - -ts_pseudo_increment_dt_from_initial_dt <truth> - Increase dt as a ratio from original dt
607: Level: beginner
609: References:
610: Todd S. Coffey and C. T. Kelley and David E. Keyes, Pseudotransient Continuation and Differential-Algebraic Equations, 2003.
611: C. T. Kelley and David E. Keyes, Convergence analysis of Pseudotransient Continuation, 1998.
613: Notes:
614: The residual computed by this method includes the transient term (Xdot is computed instead of
615: always being zero), but since the prediction from the last step is always the solution from the
616: last step, on the first Newton iteration we have
618: $ Xdot = (Xpredicted - Xold)/dt = (Xold-Xold)/dt = 0
620: Therefore, the linear system solved by the first Newton iteration is equivalent to the one
621: described above and in the papers. If the user chooses to perform multiple Newton iterations, the
622: algorithm is no longer the one described in the referenced papers.
624: .seealso: TSCreate(), TS, TSSetType()
626: M*/
630: PetscErrorCode TSCreate_Pseudo(TS ts)
631: {
632: TS_Pseudo *pseudo;
634: SNES snes;
635: const SNESType stype;
638: ts->ops->reset = TSReset_Pseudo;
639: ts->ops->destroy = TSDestroy_Pseudo;
640: ts->ops->view = TSView_Pseudo;
642: ts->ops->setup = TSSetUp_Pseudo;
643: ts->ops->step = TSStep_Pseudo;
644: ts->ops->setfromoptions = TSSetFromOptions_Pseudo;
645: ts->ops->snesfunction = SNESTSFormFunction_Pseudo;
646: ts->ops->snesjacobian = SNESTSFormJacobian_Pseudo;
648: TSGetSNES(ts,&snes);
649: SNESGetType(snes,&stype);
650: if (!stype) {SNESSetType(snes,SNESKSPONLY);}
652: PetscNewLog(ts,TS_Pseudo,&pseudo);
653: ts->data = (void*)pseudo;
655: pseudo->dt_increment = 1.1;
656: pseudo->increment_dt_from_initial_dt = PETSC_FALSE;
657: pseudo->dt = TSPseudoDefaultTimeStep;
658: pseudo->fnorm = -1;
660: PetscObjectComposeFunctionDynamic((PetscObject)ts,"TSPseudoSetVerifyTimeStep_C",
661: "TSPseudoSetVerifyTimeStep_Pseudo",
662: TSPseudoSetVerifyTimeStep_Pseudo);
663: PetscObjectComposeFunctionDynamic((PetscObject)ts,"TSPseudoSetTimeStepIncrement_C",
664: "TSPseudoSetTimeStepIncrement_Pseudo",
665: TSPseudoSetTimeStepIncrement_Pseudo);
666: PetscObjectComposeFunctionDynamic((PetscObject)ts,"TSPseudoIncrementDtFromInitialDt_C",
667: "TSPseudoIncrementDtFromInitialDt_Pseudo",
668: TSPseudoIncrementDtFromInitialDt_Pseudo);
669: PetscObjectComposeFunctionDynamic((PetscObject)ts,"TSPseudoSetTimeStep_C",
670: "TSPseudoSetTimeStep_Pseudo",
671: TSPseudoSetTimeStep_Pseudo);
672: return(0);
673: }
678: /*@C
679: TSPseudoDefaultTimeStep - Default code to compute pseudo-timestepping.
680: Use with TSPseudoSetTimeStep().
682: Collective on TS
684: Input Parameters:
685: . ts - the timestep context
686: . dtctx - unused timestep context
688: Output Parameter:
689: . newdt - the timestep to use for the next step
691: Level: advanced
693: .keywords: timestep, pseudo, default
695: .seealso: TSPseudoSetTimeStep(), TSPseudoComputeTimeStep()
696: @*/
697: PetscErrorCode TSPseudoDefaultTimeStep(TS ts,PetscReal* newdt,void* dtctx)
698: {
699: TS_Pseudo *pseudo = (TS_Pseudo*)ts->data;
700: PetscReal inc = pseudo->dt_increment,fnorm_previous = pseudo->fnorm_previous;
704: VecZeroEntries(pseudo->xdot);
705: TSComputeIFunction(ts,ts->ptime,ts->vec_sol,pseudo->xdot,pseudo->func,PETSC_FALSE);
706: VecNorm(pseudo->func,NORM_2,&pseudo->fnorm);
707: if (pseudo->fnorm_initial == 0.0) {
708: /* first time through so compute initial function norm */
709: pseudo->fnorm_initial = pseudo->fnorm;
710: fnorm_previous = pseudo->fnorm;
711: }
712: if (pseudo->fnorm == 0.0) {
713: *newdt = 1.e12*inc*ts->time_step;
714: } else if (pseudo->increment_dt_from_initial_dt) {
715: *newdt = inc*pseudo->dt_initial*pseudo->fnorm_initial/pseudo->fnorm;
716: } else {
717: *newdt = inc*ts->time_step*fnorm_previous/pseudo->fnorm;
718: }
719: pseudo->fnorm_previous = pseudo->fnorm;
720: return(0);
721: }