Actual source code: tsdiscgrad.c

  1: /*
  2:   Code for timestepping with discrete gradient integrators
  3: */
  4: #include <petsc/private/tsimpl.h>
  5: #include <petscdm.h>

  7: PetscBool DGCite = PETSC_FALSE;
  8: const char DGCitation[] = "@article{Gonzalez1996,\n"
  9:                           "  title   = {Time integration and discrete Hamiltonian systems},\n"
 10:                           "  author  = {Oscar Gonzalez},\n"
 11:                           "  journal = {Journal of Nonlinear Science},\n"
 12:                           "  volume  = {6},\n"
 13:                           "  pages   = {449--467},\n"
 14:                           "  doi     = {10.1007/978-1-4612-1246-1_10},\n"
 15:                           "  year    = {1996}\n}\n";

 17: typedef struct {
 18:   PetscReal stage_time;
 19:   Vec       X0, X, Xdot;
 20:   PetscErrorCode (*Sfunc)(TS, PetscReal, Vec, Mat, void *);
 21:   PetscErrorCode (*Ffunc)(TS, PetscReal, Vec, PetscScalar *, void *);
 22:   PetscErrorCode (*Gfunc)(TS, PetscReal, Vec, Vec, void *);
 23: } TS_DiscGrad;

 25: static PetscErrorCode TSDiscGradGetX0AndXdot(TS ts, DM dm, Vec *X0, Vec *Xdot)
 26: {
 27:   TS_DiscGrad   *dg = (TS_DiscGrad *) ts->data;

 31:   if (X0) {
 32:     if (dm && dm != ts->dm) {DMGetNamedGlobalVector(dm, "TSDiscGrad_X0", X0);}
 33:     else                    {*X0  = ts->vec_sol;}
 34:   }
 35:   if (Xdot) {
 36:     if (dm && dm != ts->dm) {DMGetNamedGlobalVector(dm, "TSDiscGrad_Xdot", Xdot);}
 37:     else                    {*Xdot = dg->Xdot;}
 38:   }
 39:   return(0);
 40: }

 42: static PetscErrorCode TSDiscGradRestoreX0AndXdot(TS ts, DM dm, Vec *X0, Vec *Xdot)
 43: {

 47:   if (X0) {
 48:     if (dm && dm != ts->dm) {DMRestoreNamedGlobalVector(dm, "TSDiscGrad_X0", X0);}
 49:   }
 50:   if (Xdot) {
 51:     if (dm && dm != ts->dm) {DMRestoreNamedGlobalVector(dm, "TSDiscGrad_Xdot", Xdot);}
 52:   }
 53:   return(0);
 54: }

 56: static PetscErrorCode DMCoarsenHook_TSDiscGrad(DM fine, DM coarse, void *ctx)
 57: {
 59:   return(0);
 60: }

 62: static PetscErrorCode DMRestrictHook_TSDiscGrad(DM fine, Mat restrct, Vec rscale, Mat inject, DM coarse, void *ctx)
 63: {
 64:   TS             ts = (TS) ctx;
 65:   Vec            X0, Xdot, X0_c, Xdot_c;

 69:   TSDiscGradGetX0AndXdot(ts, fine, &X0, &Xdot);
 70:   TSDiscGradGetX0AndXdot(ts, coarse, &X0_c, &Xdot_c);
 71:   MatRestrict(restrct, X0, X0_c);
 72:   MatRestrict(restrct, Xdot, Xdot_c);
 73:   VecPointwiseMult(X0_c, rscale, X0_c);
 74:   VecPointwiseMult(Xdot_c, rscale, Xdot_c);
 75:   TSDiscGradRestoreX0AndXdot(ts, fine, &X0, &Xdot);
 76:   TSDiscGradRestoreX0AndXdot(ts, coarse, &X0_c, &Xdot_c);
 77:   return(0);
 78: }

 80: static PetscErrorCode DMSubDomainHook_TSDiscGrad(DM dm, DM subdm, void *ctx)
 81: {
 83:   return(0);
 84: }

 86: static PetscErrorCode DMSubDomainRestrictHook_TSDiscGrad(DM dm, VecScatter gscat, VecScatter lscat, DM subdm, void *ctx)
 87: {
 88:   TS             ts = (TS) ctx;
 89:   Vec            X0, Xdot, X0_sub, Xdot_sub;

 93:   TSDiscGradGetX0AndXdot(ts, dm, &X0, &Xdot);
 94:   TSDiscGradGetX0AndXdot(ts, subdm, &X0_sub, &Xdot_sub);

 96:   VecScatterBegin(gscat, X0, X0_sub, INSERT_VALUES, SCATTER_FORWARD);
 97:   VecScatterEnd(gscat, X0, X0_sub, INSERT_VALUES, SCATTER_FORWARD);

 99:   VecScatterBegin(gscat, Xdot, Xdot_sub, INSERT_VALUES, SCATTER_FORWARD);
100:   VecScatterEnd(gscat, Xdot, Xdot_sub, INSERT_VALUES, SCATTER_FORWARD);

102:   TSDiscGradRestoreX0AndXdot(ts, dm, &X0, &Xdot);
103:   TSDiscGradRestoreX0AndXdot(ts, subdm, &X0_sub, &Xdot_sub);
104:   return(0);
105: }

107: static PetscErrorCode TSSetUp_DiscGrad(TS ts)
108: {
109:   TS_DiscGrad   *dg = (TS_DiscGrad *) ts->data;
110:   DM             dm;

114:   if (!dg->X)    {VecDuplicate(ts->vec_sol, &dg->X);}
115:   if (!dg->X0)   {VecDuplicate(ts->vec_sol, &dg->X0);}
116:   if (!dg->Xdot) {VecDuplicate(ts->vec_sol, &dg->Xdot);}

118:   TSGetDM(ts, &dm);
119:   DMCoarsenHookAdd(dm, DMCoarsenHook_TSDiscGrad, DMRestrictHook_TSDiscGrad, ts);
120:   DMSubDomainHookAdd(dm, DMSubDomainHook_TSDiscGrad, DMSubDomainRestrictHook_TSDiscGrad, ts);
121:   return(0);
122: }

124: static PetscErrorCode TSSetFromOptions_DiscGrad(PetscOptionItems *PetscOptionsObject, TS ts)
125: {

129:   PetscOptionsHead(PetscOptionsObject, "Discrete Gradients ODE solver options");
130:   {
131:     //PetscOptionsReal("-ts_theta_theta","Location of stage (0<Theta<=1)","TSDiscGradSetTheta",th->Theta,&th->Theta,NULL);
132:   }
133:   PetscOptionsTail();
134:   return(0);
135: }

137: static PetscErrorCode TSView_DiscGrad(TS ts,PetscViewer viewer)
138: {
139:   PetscBool      iascii;

143:   PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII, &iascii);
144:   if (iascii) {
145:     PetscViewerASCIIPrintf(viewer,"  Discrete Gradients\n");
146:   }
147:   return(0);
148: }

150: static PetscErrorCode TSReset_DiscGrad(TS ts)
151: {
152:   TS_DiscGrad   *dg = (TS_DiscGrad *) ts->data;

156:   VecDestroy(&dg->X);
157:   VecDestroy(&dg->X0);
158:   VecDestroy(&dg->Xdot);
159:   return(0);
160: }

162: static PetscErrorCode TSDestroy_DiscGrad(TS ts)
163: {
164:   DM             dm;

168:   TSReset_DiscGrad(ts);
169:   TSGetDM(ts, &dm);
170:   if (dm) {
171:     DMCoarsenHookRemove(dm, DMCoarsenHook_TSDiscGrad, DMRestrictHook_TSDiscGrad, ts);
172:     DMSubDomainHookRemove(dm, DMSubDomainHook_TSDiscGrad, DMSubDomainRestrictHook_TSDiscGrad, ts);
173:   }
174:   PetscFree(ts->data);
175:   PetscObjectComposeFunction((PetscObject)ts,"TSDiscGradGetFormulation_C",NULL);
176:   PetscObjectComposeFunction((PetscObject)ts,"TSDiscGradSetFormulation_C",NULL);
177:   return(0);
178: }

180: static PetscErrorCode TSInterpolate_DiscGrad(TS ts, PetscReal t, Vec X)
181: {
182:   TS_DiscGrad   *dg = (TS_DiscGrad*)ts->data;
183:   PetscReal      dt = t - ts->ptime;

187:   VecCopy(ts->vec_sol, dg->X);
188:   VecWAXPY(X, dt, dg->Xdot, dg->X);
189:   return(0);
190: }

192: static PetscErrorCode TSDiscGrad_SNESSolve(TS ts, Vec b, Vec x)
193: {
194:   SNES           snes;
195:   PetscInt       nits, lits;

199:   TSGetSNES(ts, &snes);
200:   SNESSolve(snes, b, x);
201:   SNESGetIterationNumber(snes, &nits);
202:   SNESGetLinearSolveIterations(snes, &lits);
203:   ts->snes_its += nits;
204:   ts->ksp_its  += lits;
205:   return(0);
206: }

208: static PetscErrorCode TSStep_DiscGrad(TS ts)
209: {
210:   TS_DiscGrad   *dg = (TS_DiscGrad *) ts->data;
211:   TSAdapt        adapt;
212:   TSStepStatus   status          = TS_STEP_INCOMPLETE;
213:   PetscInt       rejections      = 0;
214:   PetscBool      stageok, accept = PETSC_TRUE;
215:   PetscReal      next_time_step  = ts->time_step;

219:   TSGetAdapt(ts, &adapt);
220:   if (!ts->steprollback) {VecCopy(ts->vec_sol, dg->X0);}

222:   while (!ts->reason && status != TS_STEP_COMPLETE) {
223:     PetscReal shift = 1/(0.5*ts->time_step);

225:     dg->stage_time = ts->ptime + 0.5*ts->time_step;

227:     VecCopy(dg->X0, dg->X);
228:     TSPreStage(ts, dg->stage_time);
229:     TSDiscGrad_SNESSolve(ts, NULL, dg->X);
230:     TSPostStage(ts, dg->stage_time, 0, &dg->X);
231:     TSAdaptCheckStage(adapt, ts, dg->stage_time, dg->X, &stageok);
232:     if (!stageok) goto reject_step;

234:     status = TS_STEP_PENDING;
235:     VecAXPBYPCZ(dg->Xdot, -shift, shift, 0, dg->X0, dg->X);
236:     VecAXPY(ts->vec_sol, ts->time_step, dg->Xdot);
237:     TSAdaptChoose(adapt, ts, ts->time_step, NULL, &next_time_step, &accept);
238:     status = accept ? TS_STEP_COMPLETE : TS_STEP_INCOMPLETE;
239:     if (!accept) {
240:       VecCopy(dg->X0, ts->vec_sol);
241:       ts->time_step = next_time_step;
242:       goto reject_step;
243:     }
244:     ts->ptime    += ts->time_step;
245:     ts->time_step = next_time_step;
246:     break;

248:   reject_step:
249:     ts->reject++; accept = PETSC_FALSE;
250:     if (!ts->reason && ts->max_reject >= 0 && ++rejections > ts->max_reject) {
251:       ts->reason = TS_DIVERGED_STEP_REJECTED;
252:       PetscInfo2(ts, "Step=%D, step rejections %D greater than current TS allowed, stopping solve\n", ts->steps, rejections);
253:     }
254:   }
255:   return(0);
256: }

258: static PetscErrorCode TSGetStages_DiscGrad(TS ts, PetscInt *ns, Vec **Y)
259: {
260:   TS_DiscGrad *dg = (TS_DiscGrad *) ts->data;

263:   if (ns) *ns = 1;
264:   if (Y)  *Y  = &(dg->X);
265:   return(0);
266: }

268: /*
269:   This defines the nonlinear equation that is to be solved with SNES
270:     G(U) = F[t0 + 0.5*dt, U, (U-U0)/dt] = 0
271: */
272: static PetscErrorCode SNESTSFormFunction_DiscGrad(SNES snes, Vec x, Vec y, TS ts)
273: {
274:   TS_DiscGrad   *dg    = (TS_DiscGrad *) ts->data;
275:   PetscReal      shift = 1/(0.5*ts->time_step);
276:   Vec            X0, Xdot;
277:   DM             dm, dmsave;

281:   SNESGetDM(snes, &dm);
282:   TSDiscGradGetX0AndXdot(ts, dm, &X0, &Xdot);
283:   VecAXPBYPCZ(Xdot, -shift, shift, 0, X0, x);

285:   /* DM monkey-business allows user code to call TSGetDM() inside of functions evaluated on levels of FAS */
286:   dmsave = ts->dm;
287:   ts->dm = dm;
288:   TSComputeIFunction(ts, dg->stage_time, x, Xdot, y, PETSC_FALSE);
289:   ts->dm = dmsave;
290:   TSDiscGradRestoreX0AndXdot(ts, dm, &X0, &Xdot);
291:   return(0);
292: }

294: static PetscErrorCode SNESTSFormJacobian_DiscGrad(SNES snes, Vec x, Mat A, Mat B, TS ts)
295: {
296:   TS_DiscGrad   *dg    = (TS_DiscGrad *) ts->data;
297:   PetscReal      shift = 1/(0.5*ts->time_step);
298:   Vec            Xdot;
299:   DM             dm,dmsave;

303:   SNESGetDM(snes, &dm);
304:   /* Xdot has already been computed in SNESTSFormFunction_DiscGrad (SNES guarantees this) */
305:   TSDiscGradGetX0AndXdot(ts, dm, NULL, &Xdot);

307:   dmsave = ts->dm;
308:   ts->dm = dm;
309:   TSComputeIJacobian(ts, dg->stage_time, x, Xdot, shift, A, B, PETSC_FALSE);
310:   ts->dm = dmsave;
311:   TSDiscGradRestoreX0AndXdot(ts, dm, NULL, &Xdot);
312:   return(0);
313: }

315: static PetscErrorCode TSDiscGradGetFormulation_DiscGrad(TS ts, PetscErrorCode (**Sfunc)(TS, PetscReal, Vec, Mat, void *), PetscErrorCode (**Ffunc)(TS, PetscReal, Vec, PetscScalar *, void *), PetscErrorCode (**Gfunc)(TS, PetscReal, Vec, Vec, void *))
316: {
317:   TS_DiscGrad *dg = (TS_DiscGrad *) ts->data;

320:   *Sfunc = dg->Sfunc;
321:   *Ffunc = dg->Ffunc;
322:   *Gfunc = dg->Gfunc;
323:   return(0);
324: }

326: static PetscErrorCode TSDiscGradSetFormulation_DiscGrad(TS ts, PetscErrorCode (*Sfunc)(TS, PetscReal, Vec, Mat, void *), PetscErrorCode (*Ffunc)(TS, PetscReal, Vec, PetscScalar *, void *), PetscErrorCode (*Gfunc)(TS, PetscReal, Vec, Vec, void *))
327: {
328:   TS_DiscGrad *dg = (TS_DiscGrad *) ts->data;

331:   dg->Sfunc = Sfunc;
332:   dg->Ffunc = Ffunc;
333:   dg->Gfunc = Gfunc;
334:   return(0);
335: }

337: /*MC
338:   TSDISCGRAD - ODE solver using the discrete gradients version of the implicit midpoint method

340:   Level: beginner

342:   Notes: This is the implicit midpoint rule, with an optional term that guarantees the discrete gradient property. This
343:   timestepper applies to systems of the form
344: $ u_t = S(u) grad F(u)
345:   where S(u) is a linear operator, and F is a functional of u.

347: .seealso: TSCreate(), TSSetType(), TS, TSTHETA, TSDiscGradSetFormulation()
348: M*/
349: PETSC_EXTERN PetscErrorCode TSCreate_DiscGrad(TS ts)
350: {
351:   TS_DiscGrad       *th;

355:   PetscCitationsRegister(DGCitation, &DGCite);
356:   ts->ops->reset           = TSReset_DiscGrad;
357:   ts->ops->destroy         = TSDestroy_DiscGrad;
358:   ts->ops->view            = TSView_DiscGrad;
359:   ts->ops->setfromoptions  = TSSetFromOptions_DiscGrad;
360:   ts->ops->setup           = TSSetUp_DiscGrad;
361:   ts->ops->step            = TSStep_DiscGrad;
362:   ts->ops->interpolate     = TSInterpolate_DiscGrad;
363:   ts->ops->getstages       = TSGetStages_DiscGrad;
364:   ts->ops->snesfunction    = SNESTSFormFunction_DiscGrad;
365:   ts->ops->snesjacobian    = SNESTSFormJacobian_DiscGrad;
366:   ts->default_adapt_type   = TSADAPTNONE;

368:   ts->usessnes = PETSC_TRUE;

370:   PetscNewLog(ts,&th);
371:   ts->data = (void*)th;
372:   PetscObjectComposeFunction((PetscObject)ts,"TSDiscGradGetFormulation_C",TSDiscGradGetFormulation_DiscGrad);
373:   PetscObjectComposeFunction((PetscObject)ts,"TSDiscGradSetFormulation_C",TSDiscGradSetFormulation_DiscGrad);
374:   return(0);
375: }

377: /*@C
378:   TSDiscGradGetFormulation - Get the construction method for S, F, and grad F from the formulation u_t = S grad F

380:   Not Collective

382:   Input Parameter:
383: . ts - timestepping context

385:   Output Parameters:
386: + Sfunc - constructor for the S matrix from the formulation
387: . Ffunc - functional F from the formulation
388: - Gfunc - constructor for the gradient of F from the formulation


391:   Calling sequence of Sfunc:
392: $ PetscErrorCode func(TS ts, PetscReal time, Vec u, Mat S, void *)

394:   Calling sequence of Ffunc:
395: $ PetscErrorCode func(TS ts, PetscReal time, Vec u, PetscScalar *F, void *)

397:   Calling sequence of Gfunc:
398: $ PetscErrorCode func(TS ts, PetscReal time, Vec u, Vec G, void *)

400:   Level: intermediate

402: .seealso: TSDiscGradSetFormulation()
403: @*/
404: PetscErrorCode TSDiscGradGetFormulation(TS ts, PetscErrorCode (**Sfunc)(TS, PetscReal, Vec, Mat, void *), PetscErrorCode (**Ffunc)(TS, PetscReal, Vec, PetscScalar *, void *), PetscErrorCode (**Gfunc)(TS, PetscReal, Vec, Vec, void *))
405: {

413:   PetscUseMethod(ts,"TSDiscGradGetFormulation_C",(TS,PetscErrorCode(**Sfunc)(TS,PetscReal,Vec,Mat,void*),PetscErrorCode(**Ffunc)(TS,PetscReal,Vec,PetscScalar*,void*),PetscErrorCode(**Gfunc)(TS,PetscReal,Vec,Vec,void*)),(ts,Sfunc,Ffunc,Gfunc));
414:   return(0);
415: }

417: /*@C
418:   TSDiscGradSetFormulation - Set the construction method for S, F, and grad F from the formulation u_t = S(u) grad F(u)

420:   Not Collective

422:   Input Parameters:
423: + ts    - timestepping context
424: . Sfunc - constructor for the S matrix from the formulation
425: . Ffunc - functional F from the formulation
426: - Gfunc - constructor for the gradient of F from the formulation

428:   Calling sequence of Sfunc:
429: $ PetscErrorCode func(TS ts, PetscReal time, Vec u, Mat S, void *)

431:   Calling sequence of Ffunc:
432: $ PetscErrorCode func(TS ts, PetscReal time, Vec u, PetscScalar *F, void *)

434:   Calling sequence of Gfunc:
435: $ PetscErrorCode func(TS ts, PetscReal time, Vec u, Vec G, void *)

437:   Level: Intermediate

439: .seealso: TSDiscGradGetFormulation()
440: @*/
441: PetscErrorCode TSDiscGradSetFormulation(TS ts, PetscErrorCode (*Sfunc)(TS, PetscReal, Vec, Mat, void *), PetscErrorCode (*Ffunc)(TS, PetscReal, Vec , PetscScalar *, void *), PetscErrorCode (*Gfunc)(TS, PetscReal, Vec, Vec, void *), void *ctx)
442: {

450:   PetscTryMethod(ts,"TSDiscGradSetFormulation_C",(TS,PetscErrorCode(*Sfunc)(TS,PetscReal,Vec,Mat,void*),PetscErrorCode(*Ffunc)(TS,PetscReal,Vec,PetscScalar*,void*),PetscErrorCode(*Gfunc)(TS,PetscReal,Vec,Vec,void*)),(ts,Sfunc,Ffunc,Gfunc));
451:   return(0);
452: }