Actual source code: posindep.c

  1: #define PETSCTS_DLL

  3: /*
  4:        Code for Timestepping with implicit backwards Euler.
  5: */
 6:  #include private/tsimpl.h

  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 */

 13:   /* information used for Pseudo-timestepping */

 15:   PetscErrorCode (*dt)(TS,PetscReal*,void*);              /* compute next timestep, and related context */
 16:   void           *dtctx;
 17:   PetscErrorCode (*verify)(TS,Vec,void*,PetscReal*,PetscTruth*); /* verify previous timestep and related context */
 18:   void           *verifyctx;

 20:   PetscReal  initial_fnorm,fnorm;                  /* original and current norm of F(u) */
 21:   PetscReal  fnorm_previous;

 23:   PetscReal  dt_increment;                  /* scaling that dt is incremented each time-step */
 24:   PetscTruth increment_dt_from_initial_dt;
 25: } TS_Pseudo;

 27: /* ------------------------------------------------------------------------------*/

 31: /*@
 32:     TSPseudoComputeTimeStep - Computes the next timestep for a currently running
 33:     pseudo-timestepping process.

 35:     Collective on TS

 37:     Input Parameter:
 38: .   ts - timestep context

 40:     Output Parameter:
 41: .   dt - newly computed timestep

 43:     Level: advanced

 45:     Notes:
 46:     The routine to be called here to compute the timestep should be
 47:     set by calling TSPseudoSetTimeStep().

 49: .keywords: timestep, pseudo, compute

 51: .seealso: TSPseudoDefaultTimeStep(), TSPseudoSetTimeStep()
 52: @*/
 53: PetscErrorCode  TSPseudoComputeTimeStep(TS ts,PetscReal *dt)
 54: {
 55:   TS_Pseudo      *pseudo = (TS_Pseudo*)ts->data;

 59:   PetscLogEventBegin(TS_PseudoComputeTimeStep,ts,0,0,0);
 60:   (*pseudo->dt)(ts,dt,pseudo->dtctx);
 61:   PetscLogEventEnd(TS_PseudoComputeTimeStep,ts,0,0,0);
 62:   return(0);
 63: }


 66: /* ------------------------------------------------------------------------------*/
 69: /*@C
 70:    TSPseudoDefaultVerifyTimeStep - Default code to verify the quality of the last timestep.

 72:    Collective on TS

 74:    Input Parameters:
 75: +  ts - the timestep context
 76: .  dtctx - unused timestep context
 77: -  update - latest solution vector

 79:    Output Parameters:
 80: +  newdt - the timestep to use for the next step
 81: -  flag - flag indicating whether the last time step was acceptable

 83:    Level: advanced

 85:    Note:
 86:    This routine always returns a flag of 1, indicating an acceptable 
 87:    timestep.

 89: .keywords: timestep, pseudo, default, verify 

 91: .seealso: TSPseudoSetVerifyTimeStep(), TSPseudoVerifyTimeStep()
 92: @*/
 93: PetscErrorCode  TSPseudoDefaultVerifyTimeStep(TS ts,Vec update,void *dtctx,PetscReal *newdt,PetscTruth *flag)
 94: {
 96:   *flag = PETSC_TRUE;
 97:   return(0);
 98: }


103: /*@
104:     TSPseudoVerifyTimeStep - Verifies whether the last timestep was acceptable.

106:     Collective on TS

108:     Input Parameters:
109: +   ts - timestep context
110: -   update - latest solution vector

112:     Output Parameters:
113: +   dt - newly computed timestep (if it had to shrink)
114: -   flag - indicates if current timestep was ok

116:     Level: advanced

118:     Notes:
119:     The routine to be called here to compute the timestep should be
120:     set by calling TSPseudoSetVerifyTimeStep().

122: .keywords: timestep, pseudo, verify 

124: .seealso: TSPseudoSetVerifyTimeStep(), TSPseudoDefaultVerifyTimeStep()
125: @*/
126: PetscErrorCode  TSPseudoVerifyTimeStep(TS ts,Vec update,PetscReal *dt,PetscTruth *flag)
127: {
128:   TS_Pseudo      *pseudo = (TS_Pseudo*)ts->data;

132:   if (!pseudo->verify) {*flag = PETSC_TRUE; return(0);}

134:   (*pseudo->verify)(ts,update,pseudo->verifyctx,dt,flag);

136:   return(0);
137: }

139: /* --------------------------------------------------------------------------------*/

143: static PetscErrorCode TSStep_Pseudo(TS ts,PetscInt *steps,PetscReal *ptime)
144: {
145:   Vec            sol = ts->vec_sol;
147:   PetscInt       i,max_steps = ts->max_steps,its,lits;
148:   PetscTruth     ok;
149:   TS_Pseudo      *pseudo = (TS_Pseudo*)ts->data;
150:   PetscReal      current_time_step;
151: 
153:   *steps = -ts->steps;

155:   VecCopy(sol,pseudo->update);
156:   for (i=0; i<max_steps && ts->ptime < ts->max_time; i++) {
157:     TSPseudoComputeTimeStep(ts,&ts->time_step);
158:     TSMonitor(ts,ts->steps,ts->ptime,sol);
159:     current_time_step = ts->time_step;
160:     TSPreStep(ts);
161:     while (PETSC_TRUE) {
162:       ts->ptime  += current_time_step;
163:       SNESSolve(ts->snes,PETSC_NULL,pseudo->update);
164:       SNESGetLinearSolveIterations(ts->snes,&lits);
165:       SNESGetIterationNumber(ts->snes,&its);
166:       ts->nonlinear_its += its; ts->linear_its += lits;
167:       TSPseudoVerifyTimeStep(ts,pseudo->update,&ts->time_step,&ok);
168:       if (ok) break;
169:       ts->ptime        -= current_time_step;
170:       current_time_step = ts->time_step;
171:     }
172:     VecCopy(pseudo->update,sol);
173:     ts->steps++;
174:     TSPostStep(ts);
175:   }
176:   TSComputeRHSFunction(ts,ts->ptime,ts->vec_sol,pseudo->func);
177:   VecNorm(pseudo->func,NORM_2,&pseudo->fnorm);
178:   TSMonitor(ts,ts->steps,ts->ptime,sol);

180:   *steps += ts->steps;
181:   *ptime  = ts->ptime;
182:   return(0);
183: }

185: /*------------------------------------------------------------*/
188: static PetscErrorCode TSDestroy_Pseudo(TS ts)
189: {
190:   TS_Pseudo *pseudo = (TS_Pseudo*)ts->data;

194:   if (pseudo->update) {VecDestroy(pseudo->update);}
195:   if (pseudo->func) {VecDestroy(pseudo->func);}
196:   if (pseudo->xdot) {VecDestroy(pseudo->xdot);}
197:   PetscFree(pseudo);
198:   return(0);
199: }


202: /*------------------------------------------------------------*/

206: /*
207:     Compute Xdot = (X^{n+1}-X^n)/dt) = 0
208: */
209: static PetscErrorCode TSPseudoGetXdot(TS ts,Vec X,Vec *Xdot)
210: {
211:   TS_Pseudo      *pseudo = (TS_Pseudo*)ts->data;
212:   PetscScalar    mdt = 1.0/ts->time_step,*xnp1,*xn,*xdot;
214:   PetscInt       i,n;

217:   VecGetArray(ts->vec_sol,&xn);
218:   VecGetArray(X,&xnp1);
219:   VecGetArray(pseudo->xdot,&xdot);
220:   VecGetLocalSize(X,&n);
221:   for (i=0; i<n; i++) {
222:     xdot[i] = mdt*(xnp1[i] - xn[i]);
223:   }
224:   VecRestoreArray(ts->vec_sol,&xn);
225:   VecRestoreArray(X,&xnp1);
226:   VecRestoreArray(pseudo->xdot,&xdot);
227:   *Xdot = pseudo->xdot;
228:   return(0);
229: }

233: /*
234:     The transient residual is

236:         F(U^{n+1},(U^{n+1}-U^n)/dt) = 0

238:     or for ODE,

240:         (U^{n+1} - U^{n})/dt - F(U^{n+1}) = 0

242:     This is the function that must be evaluated for transient simulation and for
243:     finite difference Jacobians.  On the first Newton step, this algorithm uses
244:     a guess of U^{n+1} = U^n in which case the transient term vanishes and the
245:     residual is actually the steady state residual.  Pseudotransient
246:     continuation as described in the literature is a linearly implicit
247:     algorithm, it only takes this one Newton step with the steady state
248:     residual, and then advances to the next time step.
249: */
250: static PetscErrorCode TSPseudoFunction(SNES snes,Vec X,Vec Y,void *ctx)
251: {
252:   TS             ts = (TS)ctx;
253:   Vec            Xdot;

257:   TSPseudoGetXdot(ts,X,&Xdot);
258:   TSComputeIFunction(ts,ts->ptime,X,Xdot,Y);
259:   return(0);
260: }

264: /*
265:    This constructs the Jacobian needed for SNES.  For DAE, this is

267:        dF(X,Xdot)/dX + shift*dF(X,Xdot)/dXdot

269:     and for ODE:

271:        J = I/dt - J_{Frhs}   where J_{Frhs} is the given Jacobian of Frhs.
272: */
273: static PetscErrorCode TSPseudoJacobian(SNES snes,Vec X,Mat *AA,Mat *BB,MatStructure *str,void *ctx)
274: {
275:   TS             ts = (TS)ctx;
276:   Vec            Xdot;

280:   TSPseudoGetXdot(ts,X,&Xdot);
281:   TSComputeIJacobian(ts,ts->ptime,X,Xdot,1./ts->time_step,AA,BB,str);
282:   return(0);
283: }


288: static PetscErrorCode TSSetUp_Pseudo(TS ts)
289: {
290:   TS_Pseudo      *pseudo = (TS_Pseudo*)ts->data;

294:   VecDuplicate(ts->vec_sol,&pseudo->update);
295:   VecDuplicate(ts->vec_sol,&pseudo->func);
296:   VecDuplicate(ts->vec_sol,&pseudo->xdot);
297:   SNESSetFunction(ts->snes,pseudo->func,TSPseudoFunction,ts);
298:   SNESSetJacobian(ts->snes,ts->Arhs,ts->B,TSPseudoJacobian,ts);
299:   return(0);
300: }
301: /*------------------------------------------------------------*/

305: PetscErrorCode TSPseudoMonitorDefault(TS ts,PetscInt step,PetscReal ptime,Vec v,void *ctx)
306: {
307:   TS_Pseudo               *pseudo = (TS_Pseudo*)ts->data;
308:   PetscErrorCode          ierr;
309:   PetscViewerASCIIMonitor viewer = (PetscViewerASCIIMonitor)ctx;

312:   if (!ctx) {
313:     PetscViewerASCIIMonitorCreate(((PetscObject)ts)->comm,"stdout",0,&viewer);
314:   }
315:   PetscViewerASCIIMonitorPrintf(viewer,"TS %D dt %G time %G fnorm %G\n",step,ts->time_step,ptime,pseudo->fnorm);
316:   if (!ctx) {
317:     PetscViewerASCIIMonitorDestroy(viewer);
318:   }
319:   return(0);
320: }

324: static PetscErrorCode TSSetFromOptions_Pseudo(TS ts)
325: {
326:   TS_Pseudo               *pseudo = (TS_Pseudo*)ts->data;
327:   PetscErrorCode          ierr;
328:   PetscTruth              flg = PETSC_FALSE;
329:   PetscViewerASCIIMonitor viewer;

332:   PetscOptionsHead("Pseudo-timestepping options");
333:     PetscOptionsTruth("-ts_monitor","Monitor convergence","TSPseudoMonitorDefault",flg,&flg,PETSC_NULL);
334:     if (flg) {
335:       PetscViewerASCIIMonitorCreate(((PetscObject)ts)->comm,"stdout",0,&viewer);
336:       TSMonitorSet(ts,TSPseudoMonitorDefault,viewer,(PetscErrorCode (*)(void*))PetscViewerASCIIMonitorDestroy);
337:     }
338:     flg  = PETSC_FALSE;
339:     PetscOptionsTruth("-ts_pseudo_increment_dt_from_initial_dt","Increase dt as a ratio from original dt","TSPseudoIncrementDtFromInitialDt",flg,&flg,PETSC_NULL);
340:     if (flg) {
341:       TSPseudoIncrementDtFromInitialDt(ts);
342:     }
343:     PetscOptionsReal("-ts_pseudo_increment","Ratio to increase dt","TSPseudoSetTimeStepIncrement",pseudo->dt_increment,&pseudo->dt_increment,0);
344:   PetscOptionsTail();
345:   return(0);
346: }

350: static PetscErrorCode TSView_Pseudo(TS ts,PetscViewer viewer)
351: {
353:   return(0);
354: }

356: /* ----------------------------------------------------------------------------- */
359: /*@C
360:    TSPseudoSetVerifyTimeStep - Sets a user-defined routine to verify the quality of the 
361:    last timestep.

363:    Collective on TS

365:    Input Parameters:
366: +  ts - timestep context
367: .  dt - user-defined function to verify timestep
368: -  ctx - [optional] user-defined context for private data
369:          for the timestep verification routine (may be PETSC_NULL)

371:    Level: advanced

373:    Calling sequence of func:
374: .  func (TS ts,Vec update,void *ctx,PetscReal *newdt,PetscTruth *flag);

376: .  update - latest solution vector
377: .  ctx - [optional] timestep context
378: .  newdt - the timestep to use for the next step
379: .  flag - flag indicating whether the last time step was acceptable

381:    Notes:
382:    The routine set here will be called by TSPseudoVerifyTimeStep()
383:    during the timestepping process.

385: .keywords: timestep, pseudo, set, verify 

387: .seealso: TSPseudoDefaultVerifyTimeStep(), TSPseudoVerifyTimeStep()
388: @*/
389: PetscErrorCode  TSPseudoSetVerifyTimeStep(TS ts,PetscErrorCode (*dt)(TS,Vec,void*,PetscReal*,PetscTruth*),void* ctx)
390: {
391:   PetscErrorCode ierr,(*f)(TS,PetscErrorCode (*)(TS,Vec,void*,PetscReal *,PetscTruth *),void *);


396:   PetscObjectQueryFunction((PetscObject)ts,"TSPseudoSetVerifyTimeStep_C",(void (**)(void))&f);
397:   if (f) {
398:     (*f)(ts,dt,ctx);
399:   }
400:   return(0);
401: }

405: /*@
406:     TSPseudoSetTimeStepIncrement - Sets the scaling increment applied to 
407:     dt when using the TSPseudoDefaultTimeStep() routine.

409:    Collective on TS

411:     Input Parameters:
412: +   ts - the timestep context
413: -   inc - the scaling factor >= 1.0

415:     Options Database Key:
416: $    -ts_pseudo_increment <increment>

418:     Level: advanced

420: .keywords: timestep, pseudo, set, increment

422: .seealso: TSPseudoSetTimeStep(), TSPseudoDefaultTimeStep()
423: @*/
424: PetscErrorCode  TSPseudoSetTimeStepIncrement(TS ts,PetscReal inc)
425: {
426:   PetscErrorCode ierr,(*f)(TS,PetscReal);


431:   PetscObjectQueryFunction((PetscObject)ts,"TSPseudoSetTimeStepIncrement_C",(void (**)(void))&f);
432:   if (f) {
433:     (*f)(ts,inc);
434:   }
435:   return(0);
436: }

440: /*@
441:     TSPseudoIncrementDtFromInitialDt - Indicates that a new timestep
442:     is computed via the formula
443: $         dt = initial_dt*initial_fnorm/current_fnorm 
444:       rather than the default update,
445: $         dt = current_dt*previous_fnorm/current_fnorm.

447:    Collective on TS

449:     Input Parameter:
450: .   ts - the timestep context

452:     Options Database Key:
453: $    -ts_pseudo_increment_dt_from_initial_dt

455:     Level: advanced

457: .keywords: timestep, pseudo, set, increment

459: .seealso: TSPseudoSetTimeStep(), TSPseudoDefaultTimeStep()
460: @*/
461: PetscErrorCode  TSPseudoIncrementDtFromInitialDt(TS ts)
462: {
463:   PetscErrorCode ierr,(*f)(TS);


468:   PetscObjectQueryFunction((PetscObject)ts,"TSPseudoIncrementDtFromInitialDt_C",(void (**)(void))&f);
469:   if (f) {
470:     (*f)(ts);
471:   }
472:   return(0);
473: }


478: /*@C
479:    TSPseudoSetTimeStep - Sets the user-defined routine to be
480:    called at each pseudo-timestep to update the timestep.

482:    Collective on TS

484:    Input Parameters:
485: +  ts - timestep context
486: .  dt - function to compute timestep
487: -  ctx - [optional] user-defined context for private data
488:          required by the function (may be PETSC_NULL)

490:    Level: intermediate

492:    Calling sequence of func:
493: .  func (TS ts,PetscReal *newdt,void *ctx);

495: .  newdt - the newly computed timestep
496: .  ctx - [optional] timestep context

498:    Notes:
499:    The routine set here will be called by TSPseudoComputeTimeStep()
500:    during the timestepping process.

502: .keywords: timestep, pseudo, set

504: .seealso: TSPseudoDefaultTimeStep(), TSPseudoComputeTimeStep()
505: @*/
506: PetscErrorCode  TSPseudoSetTimeStep(TS ts,PetscErrorCode (*dt)(TS,PetscReal*,void*),void* ctx)
507: {
508:   PetscErrorCode ierr,(*f)(TS,PetscErrorCode (*)(TS,PetscReal *,void *),void *);


513:   PetscObjectQueryFunction((PetscObject)ts,"TSPseudoSetTimeStep_C",(void (**)(void))&f);
514:   if (f) {
515:     (*f)(ts,dt,ctx);
516:   }
517:   return(0);
518: }

520: /* ----------------------------------------------------------------------------- */

526: PetscErrorCode  TSPseudoSetVerifyTimeStep_Pseudo(TS ts,FCN1 dt,void* ctx)
527: {
528:   TS_Pseudo *pseudo;

531:   pseudo              = (TS_Pseudo*)ts->data;
532:   pseudo->verify      = dt;
533:   pseudo->verifyctx   = ctx;
534:   return(0);
535: }

541: PetscErrorCode  TSPseudoSetTimeStepIncrement_Pseudo(TS ts,PetscReal inc)
542: {
543:   TS_Pseudo *pseudo = (TS_Pseudo*)ts->data;

546:   pseudo->dt_increment = inc;
547:   return(0);
548: }

554: PetscErrorCode  TSPseudoIncrementDtFromInitialDt_Pseudo(TS ts)
555: {
556:   TS_Pseudo *pseudo = (TS_Pseudo*)ts->data;

559:   pseudo->increment_dt_from_initial_dt = PETSC_TRUE;
560:   return(0);
561: }

568: PetscErrorCode  TSPseudoSetTimeStep_Pseudo(TS ts,FCN2 dt,void* ctx)
569: {
570:   TS_Pseudo *pseudo = (TS_Pseudo*)ts->data;

573:   pseudo->dt      = dt;
574:   pseudo->dtctx   = ctx;
575:   return(0);
576: }

579: /* ----------------------------------------------------------------------------- */
580: /*MC
581:       TSPSEUDO - Solve steady state ODE and DAE problems with pseudo time stepping

583:   This method solves equations of the form

585: $    F(X,Xdot) = 0

587:   for steady state using the iteration

589: $    [G'] S = -F(X,0)
590: $    X += S

592:   where

594: $    G(Y) = F(Y,(Y-X)/dt)

596:   This is linearly-implicit Euler with the residual always evaluated "at steady
597:   state".  See note below.

599:   Options database keys:
600: +  -ts_pseudo_increment <real> - ratio of increase dt
601: -  -ts_pseudo_increment_dt_from_initial_dt <truth> - Increase dt as a ratio from original dt

603:   Level: beginner

605:   References:
606:   Todd S. Coffey and C. T. Kelley and David E. Keyes, Pseudotransient Continuation and Differential-Algebraic Equations, 2003.
607:   C. T. Kelley and David E. Keyes, Convergence analysis of Pseudotransient Continuation, 1998.

609:   Notes:
610:   The residual computed by this method includes the transient term (Xdot is computed instead of
611:   always being zero), but since the prediction from the last step is always the solution from the
612:   last step, on the first Newton iteration we have

614: $  Xdot = (Xpredicted - Xold)/dt = (Xold-Xold)/dt = 0

616:   Therefore, the linear system solved by the first Newton iteration is equivalent to the one
617:   described above and in the papers.  If the user chooses to perform multiple Newton iterations, the
618:   algorithm is no longer the one described in the referenced papers.

620: .seealso:  TSCreate(), TS, TSSetType()

622: M*/
626: PetscErrorCode  TSCreate_Pseudo(TS ts)
627: {
628:   TS_Pseudo      *pseudo;

632:   ts->ops->destroy         = TSDestroy_Pseudo;
633:   ts->ops->view            = TSView_Pseudo;

635:   if (ts->problem_type == TS_LINEAR) {
636:     SETERRQ(PETSC_ERR_ARG_WRONG,"Only for nonlinear problems");
637:   }
638:   if (!ts->Arhs) {
639:     SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Must set Jacobian");
640:   }

642:   ts->ops->setup           = TSSetUp_Pseudo;
643:   ts->ops->step            = TSStep_Pseudo;
644:   ts->ops->setfromoptions  = TSSetFromOptions_Pseudo;

646:   /* create the required nonlinear solver context */
647:   SNESCreate(((PetscObject)ts)->comm,&ts->snes);
648:   PetscObjectIncrementTabLevel((PetscObject)ts->snes,(PetscObject)ts,1);

650:   PetscNewLog(ts,TS_Pseudo,&pseudo);
651:   ts->data = (void*)pseudo;

653:   pseudo->dt_increment                 = 1.1;
654:   pseudo->increment_dt_from_initial_dt = PETSC_FALSE;
655:   pseudo->dt                           = TSPseudoDefaultTimeStep;

657:   PetscObjectComposeFunctionDynamic((PetscObject)ts,"TSPseudoSetVerifyTimeStep_C",
658:                     "TSPseudoSetVerifyTimeStep_Pseudo",
659:                      TSPseudoSetVerifyTimeStep_Pseudo);
660:   PetscObjectComposeFunctionDynamic((PetscObject)ts,"TSPseudoSetTimeStepIncrement_C",
661:                     "TSPseudoSetTimeStepIncrement_Pseudo",
662:                      TSPseudoSetTimeStepIncrement_Pseudo);
663:   PetscObjectComposeFunctionDynamic((PetscObject)ts,"TSPseudoIncrementDtFromInitialDt_C",
664:                     "TSPseudoIncrementDtFromInitialDt_Pseudo",
665:                      TSPseudoIncrementDtFromInitialDt_Pseudo);
666:   PetscObjectComposeFunctionDynamic((PetscObject)ts,"TSPseudoSetTimeStep_C",
667:                     "TSPseudoSetTimeStep_Pseudo",
668:                      TSPseudoSetTimeStep_Pseudo);
669:   return(0);
670: }

675: /*@C
676:    TSPseudoDefaultTimeStep - Default code to compute pseudo-timestepping.
677:    Use with TSPseudoSetTimeStep().

679:    Collective on TS

681:    Input Parameters:
682: .  ts - the timestep context
683: .  dtctx - unused timestep context

685:    Output Parameter:
686: .  newdt - the timestep to use for the next step

688:    Level: advanced

690: .keywords: timestep, pseudo, default

692: .seealso: TSPseudoSetTimeStep(), TSPseudoComputeTimeStep()
693: @*/
694: PetscErrorCode  TSPseudoDefaultTimeStep(TS ts,PetscReal* newdt,void* dtctx)
695: {
696:   TS_Pseudo      *pseudo = (TS_Pseudo*)ts->data;
697:   PetscReal      inc = pseudo->dt_increment,fnorm_previous = pseudo->fnorm_previous;

701:   TSComputeRHSFunction(ts,ts->ptime,ts->vec_sol,pseudo->func);
702:   VecNorm(pseudo->func,NORM_2,&pseudo->fnorm);
703:   if (pseudo->initial_fnorm == 0.0) {
704:     /* first time through so compute initial function norm */
705:     pseudo->initial_fnorm = pseudo->fnorm;
706:     fnorm_previous        = pseudo->fnorm;
707:   }
708:   if (pseudo->fnorm == 0.0) {
709:     *newdt = 1.e12*inc*ts->time_step;
710:   } else if (pseudo->increment_dt_from_initial_dt) {
711:     *newdt = inc*ts->initial_time_step*pseudo->initial_fnorm/pseudo->fnorm;
712:   } else {
713:     *newdt = inc*ts->time_step*fnorm_previous/pseudo->fnorm;
714:   }
715:   pseudo->fnorm_previous = pseudo->fnorm;
716:   return(0);
717: }