Date: 2025-03-28T21:11:17+00:00

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:   void     *funcCtx;
     21:   PetscBool gonzalez;
     22:   PetscErrorCode (*Sfunc)(TS, PetscReal, Vec, Mat, void *);
     23:   PetscErrorCode (*Ffunc)(TS, PetscReal, Vec, PetscScalar *, void *);
     24:   PetscErrorCode (*Gfunc)(TS, PetscReal, Vec, Vec, void *);
     25: } TS_DiscGrad;

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

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

     43: static PetscErrorCode TSDiscGradRestoreX0AndXdot(TS ts, DM dm, Vec *X0, Vec *Xdot)
     44: {
     45:   PetscFunctionBegin;
     46:   if (X0) {
     47:     if (dm && dm != ts->dm) PetscCall(DMRestoreNamedGlobalVector(dm, "TSDiscGrad_X0", X0));
     48:   }
     49:   if (Xdot) {
     50:     if (dm && dm != ts->dm) PetscCall(DMRestoreNamedGlobalVector(dm, "TSDiscGrad_Xdot", Xdot));
     51:   }
     52:   PetscFunctionReturn(PETSC_SUCCESS);
     53: }

     55: static PetscErrorCode DMCoarsenHook_TSDiscGrad(DM fine, DM coarse, void *ctx)
     56: {
     57:   PetscFunctionBegin;
     58:   PetscFunctionReturn(PETSC_SUCCESS);
     59: }

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

     66:   PetscFunctionBegin;
     67:   PetscCall(TSDiscGradGetX0AndXdot(ts, fine, &X0, &Xdot));
     68:   PetscCall(TSDiscGradGetX0AndXdot(ts, coarse, &X0_c, &Xdot_c));
     69:   PetscCall(MatRestrict(restrct, X0, X0_c));
     70:   PetscCall(MatRestrict(restrct, Xdot, Xdot_c));
     71:   PetscCall(VecPointwiseMult(X0_c, rscale, X0_c));
     72:   PetscCall(VecPointwiseMult(Xdot_c, rscale, Xdot_c));
     73:   PetscCall(TSDiscGradRestoreX0AndXdot(ts, fine, &X0, &Xdot));
     74:   PetscCall(TSDiscGradRestoreX0AndXdot(ts, coarse, &X0_c, &Xdot_c));
     75:   PetscFunctionReturn(PETSC_SUCCESS);
     76: }

     78: static PetscErrorCode DMSubDomainHook_TSDiscGrad(DM dm, DM subdm, void *ctx)
     79: {
     80:   PetscFunctionBegin;
     81:   PetscFunctionReturn(PETSC_SUCCESS);
     82: }

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

     89:   PetscFunctionBegin;
     90:   PetscCall(TSDiscGradGetX0AndXdot(ts, dm, &X0, &Xdot));
     91:   PetscCall(TSDiscGradGetX0AndXdot(ts, subdm, &X0_sub, &Xdot_sub));

     93:   PetscCall(VecScatterBegin(gscat, X0, X0_sub, INSERT_VALUES, SCATTER_FORWARD));
     94:   PetscCall(VecScatterEnd(gscat, X0, X0_sub, INSERT_VALUES, SCATTER_FORWARD));

     96:   PetscCall(VecScatterBegin(gscat, Xdot, Xdot_sub, INSERT_VALUES, SCATTER_FORWARD));
     97:   PetscCall(VecScatterEnd(gscat, Xdot, Xdot_sub, INSERT_VALUES, SCATTER_FORWARD));

     99:   PetscCall(TSDiscGradRestoreX0AndXdot(ts, dm, &X0, &Xdot));
    100:   PetscCall(TSDiscGradRestoreX0AndXdot(ts, subdm, &X0_sub, &Xdot_sub));
    101:   PetscFunctionReturn(PETSC_SUCCESS);
    102: }

    104: static PetscErrorCode TSSetUp_DiscGrad(TS ts)
    105: {
    106:   TS_DiscGrad *dg = (TS_DiscGrad *)ts->data;
    107:   DM           dm;

    109:   PetscFunctionBegin;
    110:   if (!dg->X) PetscCall(VecDuplicate(ts->vec_sol, &dg->X));
    111:   if (!dg->X0) PetscCall(VecDuplicate(ts->vec_sol, &dg->X0));
    112:   if (!dg->Xdot) PetscCall(VecDuplicate(ts->vec_sol, &dg->Xdot));

    114:   PetscCall(TSGetDM(ts, &dm));
    115:   PetscCall(DMCoarsenHookAdd(dm, DMCoarsenHook_TSDiscGrad, DMRestrictHook_TSDiscGrad, ts));
    116:   PetscCall(DMSubDomainHookAdd(dm, DMSubDomainHook_TSDiscGrad, DMSubDomainRestrictHook_TSDiscGrad, ts));
    117:   PetscFunctionReturn(PETSC_SUCCESS);
    118: }

    120: static PetscErrorCode TSSetFromOptions_DiscGrad(TS ts, PetscOptionItems *PetscOptionsObject)
    121: {
    122:   TS_DiscGrad *dg = (TS_DiscGrad *)ts->data;

    124:   PetscFunctionBegin;
    125:   PetscOptionsHeadBegin(PetscOptionsObject, "Discrete Gradients ODE solver options");
    126:   {
    127:     PetscCall(PetscOptionsBool("-ts_discgrad_gonzalez", "Use Gonzalez term in discrete gradients formulation", "TSDiscGradUseGonzalez", dg->gonzalez, &dg->gonzalez, NULL));
    128:   }
    129:   PetscOptionsHeadEnd();
    130:   PetscFunctionReturn(PETSC_SUCCESS);
    131: }

    133: static PetscErrorCode TSView_DiscGrad(TS ts, PetscViewer viewer)
    134: {
    135:   PetscBool iascii;

    137:   PetscFunctionBegin;
    138:   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
    139:   if (iascii) PetscCall(PetscViewerASCIIPrintf(viewer, "  Discrete Gradients\n"));
    140:   PetscFunctionReturn(PETSC_SUCCESS);
    141: }

    143: static PetscErrorCode TSDiscGradIsGonzalez_DiscGrad(TS ts, PetscBool *gonzalez)
    144: {
    145:   TS_DiscGrad *dg = (TS_DiscGrad *)ts->data;

    147:   PetscFunctionBegin;
    148:   *gonzalez = dg->gonzalez;
    149:   PetscFunctionReturn(PETSC_SUCCESS);
    150: }

    152: static PetscErrorCode TSDiscGradUseGonzalez_DiscGrad(TS ts, PetscBool flg)
    153: {
    154:   TS_DiscGrad *dg = (TS_DiscGrad *)ts->data;

    156:   PetscFunctionBegin;
    157:   dg->gonzalez = flg;
    158:   PetscFunctionReturn(PETSC_SUCCESS);
    159: }

    161: static PetscErrorCode TSReset_DiscGrad(TS ts)
    162: {
    163:   TS_DiscGrad *dg = (TS_DiscGrad *)ts->data;

    165:   PetscFunctionBegin;
    166:   PetscCall(VecDestroy(&dg->X));
    167:   PetscCall(VecDestroy(&dg->X0));
    168:   PetscCall(VecDestroy(&dg->Xdot));
    169:   PetscFunctionReturn(PETSC_SUCCESS);
    170: }

    172: static PetscErrorCode TSDestroy_DiscGrad(TS ts)
    173: {
    174:   DM dm;

    176:   PetscFunctionBegin;
    177:   PetscCall(TSReset_DiscGrad(ts));
    178:   PetscCall(TSGetDM(ts, &dm));
    179:   if (dm) {
    180:     PetscCall(DMCoarsenHookRemove(dm, DMCoarsenHook_TSDiscGrad, DMRestrictHook_TSDiscGrad, ts));
    181:     PetscCall(DMSubDomainHookRemove(dm, DMSubDomainHook_TSDiscGrad, DMSubDomainRestrictHook_TSDiscGrad, ts));
    182:   }
    183:   PetscCall(PetscFree(ts->data));
    184:   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSDiscGradGetFormulation_C", NULL));
    185:   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSDiscGradSetFormulation_C", NULL));
    186:   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSDiscGradIsGonzalez_C", NULL));
    187:   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSDiscGradUseGonzalez_C", NULL));
    188:   PetscFunctionReturn(PETSC_SUCCESS);
    189: }

    191: static PetscErrorCode TSInterpolate_DiscGrad(TS ts, PetscReal t, Vec X)
    192: {
    193:   TS_DiscGrad *dg = (TS_DiscGrad *)ts->data;
    194:   PetscReal    dt = t - ts->ptime;

    196:   PetscFunctionBegin;
    197:   PetscCall(VecCopy(ts->vec_sol, dg->X));
    198:   PetscCall(VecWAXPY(X, dt, dg->Xdot, dg->X));
    199:   PetscFunctionReturn(PETSC_SUCCESS);
    200: }

    202: static PetscErrorCode TSDiscGrad_SNESSolve(TS ts, Vec b, Vec x)
    203: {
    204:   SNES     snes;
    205:   PetscInt nits, lits;

    207:   PetscFunctionBegin;
    208:   PetscCall(TSGetSNES(ts, &snes));
    209:   PetscCall(SNESSolve(snes, b, x));
    210:   PetscCall(SNESGetIterationNumber(snes, &nits));
    211:   PetscCall(SNESGetLinearSolveIterations(snes, &lits));
    212:   ts->snes_its += nits;
    213:   ts->ksp_its += lits;
    214:   PetscFunctionReturn(PETSC_SUCCESS);
    215: }

    217: static PetscErrorCode TSStep_DiscGrad(TS ts)
    218: {
    219:   TS_DiscGrad *dg = (TS_DiscGrad *)ts->data;
    220:   TSAdapt      adapt;
    221:   TSStepStatus status     = TS_STEP_INCOMPLETE;
    222:   PetscInt     rejections = 0;
    223:   PetscBool    stageok, accept = PETSC_TRUE;
    224:   PetscReal    next_time_step = ts->time_step;

    226:   PetscFunctionBegin;
    227:   PetscCall(TSGetAdapt(ts, &adapt));
    228:   if (!ts->steprollback) PetscCall(VecCopy(ts->vec_sol, dg->X0));

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

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

    235:     PetscCall(VecCopy(dg->X0, dg->X));
    236:     PetscCall(TSPreStage(ts, dg->stage_time));
    237:     PetscCall(TSDiscGrad_SNESSolve(ts, NULL, dg->X));
    238:     PetscCall(TSPostStage(ts, dg->stage_time, 0, &dg->X));
    239:     PetscCall(TSAdaptCheckStage(adapt, ts, dg->stage_time, dg->X, &stageok));
    240:     if (!stageok) goto reject_step;

    242:     status = TS_STEP_PENDING;
    243:     PetscCall(VecAXPBYPCZ(dg->Xdot, -shift, shift, 0, dg->X0, dg->X));
    244:     PetscCall(VecAXPY(ts->vec_sol, ts->time_step, dg->Xdot));
    245:     PetscCall(TSAdaptChoose(adapt, ts, ts->time_step, NULL, &next_time_step, &accept));
    246:     status = accept ? TS_STEP_COMPLETE : TS_STEP_INCOMPLETE;
    247:     if (!accept) {
    248:       PetscCall(VecCopy(dg->X0, ts->vec_sol));
    249:       ts->time_step = next_time_step;
    250:       goto reject_step;
    251:     }
    252:     ts->ptime += ts->time_step;
    253:     ts->time_step = next_time_step;
    254:     break;

    256:   reject_step:
    257:     ts->reject++;
    258:     accept = PETSC_FALSE;
    259:     if (!ts->reason && ts->max_reject >= 0 && ++rejections > ts->max_reject) {
    260:       ts->reason = TS_DIVERGED_STEP_REJECTED;
    261:       PetscCall(PetscInfo(ts, "Step=%" PetscInt_FMT ", step rejections %" PetscInt_FMT " greater than current TS allowed, stopping solve\n", ts->steps, rejections));
    262:     }
    263:   }
    264:   PetscFunctionReturn(PETSC_SUCCESS);
    265: }

    267: static PetscErrorCode TSGetStages_DiscGrad(TS ts, PetscInt *ns, Vec **Y)
    268: {
    269:   TS_DiscGrad *dg = (TS_DiscGrad *)ts->data;

    271:   PetscFunctionBegin;
    272:   if (ns) *ns = 1;
    273:   if (Y) *Y = &dg->X;
    274:   PetscFunctionReturn(PETSC_SUCCESS);
    275: }

    277: /*
    278:   This defines the nonlinear equation that is to be solved with SNES
    279:     G(U) = F[t0 + 0.5*dt, U, (U-U0)/dt] = 0
    280: */

    282: /* x = (x+x')/2 */
    283: /* NEED TO CALCULATE x_{n+1} from x and x_{n}*/
    284: static PetscErrorCode SNESTSFormFunction_DiscGrad(SNES snes, Vec x, Vec y, TS ts)
    285: {
    286:   TS_DiscGrad *dg = (TS_DiscGrad *)ts->data;
    287:   PetscReal    norm, shift = 1 / (0.5 * ts->time_step);
    288:   PetscInt     n;
    289:   Vec          X0, Xdot, Xp, Xdiff;
    290:   Mat          S;
    291:   PetscScalar  F = 0, F0 = 0, Gp;
    292:   Vec          G, SgF;
    293:   DM           dm, dmsave;

    295:   PetscFunctionBegin;
    296:   PetscCall(SNESGetDM(snes, &dm));

    298:   PetscCall(VecDuplicate(y, &Xp));
    299:   PetscCall(VecDuplicate(y, &Xdiff));
    300:   PetscCall(VecDuplicate(y, &SgF));
    301:   PetscCall(VecDuplicate(y, &G));

    303:   PetscCall(VecGetLocalSize(y, &n));
    304:   PetscCall(MatCreate(PETSC_COMM_WORLD, &S));
    305:   PetscCall(MatSetSizes(S, PETSC_DECIDE, PETSC_DECIDE, n, n));
    306:   PetscCall(MatSetFromOptions(S));
    307:   PetscCall(MatSetUp(S));
    308:   PetscCall(MatAssemblyBegin(S, MAT_FINAL_ASSEMBLY));
    309:   PetscCall(MatAssemblyEnd(S, MAT_FINAL_ASSEMBLY));

    311:   PetscCall(TSDiscGradGetX0AndXdot(ts, dm, &X0, &Xdot));
    312:   PetscCall(VecAXPBYPCZ(Xdot, -shift, shift, 0, X0, x)); /* Xdot = shift (x - X0) */

    314:   PetscCall(VecAXPBYPCZ(Xp, -1, 2, 0, X0, x));     /* Xp = 2*x - X0 + (0)*Xmid */
    315:   PetscCall(VecAXPBYPCZ(Xdiff, -1, 1, 0, X0, Xp)); /* Xdiff = xp - X0 + (0)*Xdiff */

    317:   if (dg->gonzalez) {
    318:     PetscCall((*dg->Sfunc)(ts, dg->stage_time, x, S, dg->funcCtx));
    319:     PetscCall((*dg->Ffunc)(ts, dg->stage_time, Xp, &F, dg->funcCtx));
    320:     PetscCall((*dg->Ffunc)(ts, dg->stage_time, X0, &F0, dg->funcCtx));
    321:     PetscCall((*dg->Gfunc)(ts, dg->stage_time, x, G, dg->funcCtx));

    323:     /* Adding Extra Gonzalez Term */
    324:     PetscCall(VecDot(Xdiff, G, &Gp));
    325:     PetscCall(VecNorm(Xdiff, NORM_2, &norm));
    326:     if (norm < PETSC_SQRT_MACHINE_EPSILON) {
    327:       Gp = 0;
    328:     } else {
    329:       /* Gp = (1/|xn+1 - xn|^2) * (F(xn+1) - F(xn) - Gp) */
    330:       Gp = (F - F0 - Gp) / PetscSqr(norm);
    331:     }
    332:     PetscCall(VecAXPY(G, Gp, Xdiff));
    333:     PetscCall(MatMult(S, G, SgF)); /* S*gradF */

    335:   } else {
    336:     PetscCall((*dg->Sfunc)(ts, dg->stage_time, x, S, dg->funcCtx));
    337:     PetscCall((*dg->Gfunc)(ts, dg->stage_time, x, G, dg->funcCtx));

    339:     PetscCall(MatMult(S, G, SgF)); /* Xdot = S*gradF */
    340:   }
    341:   /* DM monkey-business allows user code to call TSGetDM() inside of functions evaluated on levels of FAS */
    342:   dmsave = ts->dm;
    343:   ts->dm = dm;
    344:   PetscCall(VecAXPBYPCZ(y, 1, -1, 0, Xdot, SgF));
    345:   ts->dm = dmsave;
    346:   PetscCall(TSDiscGradRestoreX0AndXdot(ts, dm, &X0, &Xdot));

    348:   PetscCall(VecDestroy(&Xp));
    349:   PetscCall(VecDestroy(&Xdiff));
    350:   PetscCall(VecDestroy(&SgF));
    351:   PetscCall(VecDestroy(&G));
    352:   PetscCall(MatDestroy(&S));
    353:   PetscFunctionReturn(PETSC_SUCCESS);
    354: }

    356: static PetscErrorCode SNESTSFormJacobian_DiscGrad(SNES snes, Vec x, Mat A, Mat B, TS ts)
    357: {
    358:   TS_DiscGrad *dg    = (TS_DiscGrad *)ts->data;
    359:   PetscReal    shift = 1 / (0.5 * ts->time_step);
    360:   Vec          Xdot;
    361:   DM           dm, dmsave;

    363:   PetscFunctionBegin;
    364:   PetscCall(SNESGetDM(snes, &dm));
    365:   /* Xdot has already been computed in SNESTSFormFunction_DiscGrad (SNES guarantees this) */
    366:   PetscCall(TSDiscGradGetX0AndXdot(ts, dm, NULL, &Xdot));

    368:   dmsave = ts->dm;
    369:   ts->dm = dm;
    370:   PetscCall(TSComputeIJacobian(ts, dg->stage_time, x, Xdot, shift, A, B, PETSC_FALSE));
    371:   ts->dm = dmsave;
    372:   PetscCall(TSDiscGradRestoreX0AndXdot(ts, dm, NULL, &Xdot));
    373:   PetscFunctionReturn(PETSC_SUCCESS);
    374: }

    376: 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 *), void *ctx)
    377: {
    378:   TS_DiscGrad *dg = (TS_DiscGrad *)ts->data;

    380:   PetscFunctionBegin;
    381:   *Sfunc = dg->Sfunc;
    382:   *Ffunc = dg->Ffunc;
    383:   *Gfunc = dg->Gfunc;
    384:   PetscFunctionReturn(PETSC_SUCCESS);
    385: }

    387: 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 *), void *ctx)
    388: {
    389:   TS_DiscGrad *dg = (TS_DiscGrad *)ts->data;

    391:   PetscFunctionBegin;
    392:   dg->Sfunc   = Sfunc;
    393:   dg->Ffunc   = Ffunc;
    394:   dg->Gfunc   = Gfunc;
    395:   dg->funcCtx = ctx;
    396:   PetscFunctionReturn(PETSC_SUCCESS);
    397: }

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

    402:   Level: intermediate

    404:   Notes:
    405:   This is the implicit midpoint rule, with an optional term that guarantees the discrete
    406:   gradient property. This timestepper applies to systems of the form $u_t = S(u) \nabla F(u)$
    407:   where $S(u)$ is a linear operator, and $F$ is a functional of $u$.

    409: .seealso: [](ch_ts), `TSCreate()`, `TSSetType()`, `TS`, `TSDISCGRAD`, `TSDiscGradSetFormulation()`
    410: M*/
    411: PETSC_EXTERN PetscErrorCode TSCreate_DiscGrad(TS ts)
    412: {
    413:   TS_DiscGrad *th;

    415:   PetscFunctionBegin;
    416:   PetscCall(PetscCitationsRegister(DGCitation, &DGCite));
    417:   ts->ops->reset          = TSReset_DiscGrad;
    418:   ts->ops->destroy        = TSDestroy_DiscGrad;
    419:   ts->ops->view           = TSView_DiscGrad;
    420:   ts->ops->setfromoptions = TSSetFromOptions_DiscGrad;
    421:   ts->ops->setup          = TSSetUp_DiscGrad;
    422:   ts->ops->step           = TSStep_DiscGrad;
    423:   ts->ops->interpolate    = TSInterpolate_DiscGrad;
    424:   ts->ops->getstages      = TSGetStages_DiscGrad;
    425:   ts->ops->snesfunction   = SNESTSFormFunction_DiscGrad;
    426:   ts->ops->snesjacobian   = SNESTSFormJacobian_DiscGrad;
    427:   ts->default_adapt_type  = TSADAPTNONE;

    429:   ts->usessnes = PETSC_TRUE;

    431:   PetscCall(PetscNew(&th));
    432:   ts->data = (void *)th;

    434:   th->gonzalez = PETSC_FALSE;

    436:   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSDiscGradGetFormulation_C", TSDiscGradGetFormulation_DiscGrad));
    437:   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSDiscGradSetFormulation_C", TSDiscGradSetFormulation_DiscGrad));
    438:   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSDiscGradIsGonzalez_C", TSDiscGradIsGonzalez_DiscGrad));
    439:   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSDiscGradUseGonzalez_C", TSDiscGradUseGonzalez_DiscGrad));
    440:   PetscFunctionReturn(PETSC_SUCCESS);
    441: }

    443: /*@C
    444:   TSDiscGradGetFormulation - Get the construction method for S, F, and grad F from the
    445:   formulation $u_t = S \nabla F$ for `TSDISCGRAD`

    447:   Not Collective

    449:   Input Parameter:
    450: . ts - timestepping context

    452:   Output Parameters:
    453: + Sfunc - constructor for the S matrix from the formulation
    454: . Ffunc - functional F from the formulation
    455: . Gfunc - constructor for the gradient of F from the formulation
    456: - ctx   - the user context

    458:   Calling sequence of `Sfunc`:
    459: + ts   - the integrator
    460: . time - the current time
    461: . u    - the solution
    462: . S    - the S-matrix from the formulation
    463: - ctx  - the user context

    465:   Calling sequence of `Ffunc`:
    466: + ts   - the integrator
    467: . time - the current time
    468: . u    - the solution
    469: . F    - the computed function from the formulation
    470: - ctx  - the user context

    472:   Calling sequence of `Gfunc`:
    473: + ts   - the integrator
    474: . time - the current time
    475: . u    - the solution
    476: . G    - the gradient of the computed function from the formulation
    477: - ctx  - the user context

    479:   Level: intermediate

    481: .seealso: [](ch_ts), `TS`, `TSDISCGRAD`, `TSDiscGradSetFormulation()`
    482: @*/
    483: PetscErrorCode TSDiscGradGetFormulation(TS ts, PetscErrorCode (**Sfunc)(TS ts, PetscReal time, Vec u, Mat S, void *ctx), PetscErrorCode (**Ffunc)(TS ts, PetscReal time, Vec u, PetscScalar *F, void *ctx), PetscErrorCode (**Gfunc)(TS ts, PetscReal time, Vec u, Vec G, void *ctx), void *ctx)
    484: {
    485:   PetscFunctionBegin;
    487:   PetscAssertPointer(Sfunc, 2);
    488:   PetscAssertPointer(Ffunc, 3);
    489:   PetscAssertPointer(Gfunc, 4);
    490:   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 *), void *), (ts, Sfunc, Ffunc, Gfunc, ctx));
    491:   PetscFunctionReturn(PETSC_SUCCESS);
    492: }

    494: /*@C
    495:   TSDiscGradSetFormulation - Set the construction method for S, F, and grad F from the
    496:   formulation $u_t = S(u) \nabla F(u)$ for `TSDISCGRAD`

    498:   Not Collective

    500:   Input Parameters:
    501: + ts    - timestepping context
    502: . Sfunc - constructor for the S matrix from the formulation
    503: . Ffunc - functional F from the formulation
    504: . Gfunc - constructor for the gradient of F from the formulation
    505: - ctx   - optional context for the functions

    507:   Calling sequence of `Sfunc`:
    508: + ts   - the integrator
    509: . time - the current time
    510: . u    - the solution
    511: . S    - the S-matrix from the formulation
    512: - ctx  - the user context

    514:   Calling sequence of `Ffunc`:
    515: + ts   - the integrator
    516: . time - the current time
    517: . u    - the solution
    518: . F    - the computed function from the formulation
    519: - ctx  - the user context

    521:   Calling sequence of `Gfunc`:
    522: + ts   - the integrator
    523: . time - the current time
    524: . u    - the solution
    525: . G    - the gradient of the computed function from the formulation
    526: - ctx  - the user context

    528:   Level: intermediate

    530: .seealso: [](ch_ts), `TSDISCGRAD`, `TSDiscGradGetFormulation()`
    531: @*/
    532: PetscErrorCode TSDiscGradSetFormulation(TS ts, PetscErrorCode (*Sfunc)(TS ts, PetscReal time, Vec u, Mat S, void *ctx), PetscErrorCode (*Ffunc)(TS ts, PetscReal time, Vec u, PetscScalar *F, void *ctx), PetscErrorCode (*Gfunc)(TS ts, PetscReal time, Vec u, Vec G, void *ctx), void *ctx)
    533: {
    534:   PetscFunctionBegin;
    539:   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 *), void *), (ts, Sfunc, Ffunc, Gfunc, ctx));
    540:   PetscFunctionReturn(PETSC_SUCCESS);
    541: }

    543: /*@
    544:   TSDiscGradIsGonzalez - Checks flag for whether to use additional conservative terms in
    545:   discrete gradient formulation for `TSDISCGRAD`

    547:   Not Collective

    549:   Input Parameter:
    550: . ts - timestepping context

    552:   Output Parameter:
    553: . gonzalez - `PETSC_TRUE` when using the Gonzalez term

    555:   Level: advanced

    557: .seealso: [](ch_ts), `TSDISCGRAD`, `TSDiscGradUseGonzalez()`
    558: @*/
    559: PetscErrorCode TSDiscGradIsGonzalez(TS ts, PetscBool *gonzalez)
    560: {
    561:   PetscFunctionBegin;
    563:   PetscAssertPointer(gonzalez, 2);
    564:   PetscUseMethod(ts, "TSDiscGradIsGonzalez_C", (TS, PetscBool *), (ts, gonzalez));
    565:   PetscFunctionReturn(PETSC_SUCCESS);
    566: }

    568: /*@
    569:   TSDiscGradUseGonzalez - Sets discrete gradient formulation with or without additional
    570:   conservative terms.

    572:   Not Collective

    574:   Input Parameters:
    575: + ts  - timestepping context
    576: - flg - `PETSC_TRUE` to use the Gonzalez term

    578:   Options Database Key:
    579: . -ts_discgrad_gonzalez <flg> - use the Gonzalez term for the discrete gradient formulation

    581:   Level: intermediate

    583:   Notes:
    584:   Without `flg`, the discrete gradients timestepper is just backwards Euler.

    586: .seealso: [](ch_ts), `TSDISCGRAD`
    587: @*/
    588: PetscErrorCode TSDiscGradUseGonzalez(TS ts, PetscBool flg)
    589: {
    590:   PetscFunctionBegin;
    592:   PetscTryMethod(ts, "TSDiscGradUseGonzalez_C", (TS, PetscBool), (ts, flg));
    593:   PetscFunctionReturn(PETSC_SUCCESS);
    594: }