xref: /petsc/src/ts/impls/eimex/eimex.c (revision a69119a591a03a9d906b29c0a4e9802e4d7c9795)
1 
2 #include <petsc/private/tsimpl.h> /*I   "petscts.h"   I*/
3 #include <petscdm.h>
4 
5 static const PetscInt TSEIMEXDefault = 3;
6 
7 typedef struct {
8   PetscInt     row_ind;    /* Return the term T[row_ind][col_ind] */
9   PetscInt     col_ind;    /* Return the term T[row_ind][col_ind] */
10   PetscInt     nstages;    /* Numbers of stages in current scheme */
11   PetscInt     max_rows;   /* Maximum number of rows */
12   PetscInt    *N;          /* Harmonic sequence N[max_rows] */
13   Vec          Y;          /* States computed during the step, used to complete the step */
14   Vec          Z;          /* For shift*(Y-Z) */
15   Vec         *T;          /* Working table, size determined by nstages */
16   Vec          YdotRHS;    /* f(x) Work vector holding YdotRHS during residual evaluation */
17   Vec          YdotI;      /* xdot-g(x) Work vector holding YdotI = G(t,x,xdot) when xdot =0 */
18   Vec          Ydot;       /* f(x)+g(x) Work vector */
19   Vec          VecSolPrev; /* Work vector holding the solution from the previous step (used for interpolation) */
20   PetscReal    shift;
21   PetscReal    ctime;
22   PetscBool    recompute_jacobian; /* Recompute the Jacobian at each stage, default is to freeze the Jacobian at the start of each step */
23   PetscBool    ord_adapt;          /* order adapativity */
24   TSStepStatus status;
25 } TS_EIMEX;
26 
27 /* This function is pure */
28 static PetscInt Map(PetscInt i, PetscInt j, PetscInt s) {
29   return ((2 * s - j + 1) * j / 2 + i - j);
30 }
31 
32 static PetscErrorCode TSEvaluateStep_EIMEX(TS ts, PetscInt order, Vec X, PetscBool *done) {
33   TS_EIMEX      *ext = (TS_EIMEX *)ts->data;
34   const PetscInt ns  = ext->nstages;
35   PetscFunctionBegin;
36   PetscCall(VecCopy(ext->T[Map(ext->row_ind, ext->col_ind, ns)], X));
37   PetscFunctionReturn(0);
38 }
39 
40 static PetscErrorCode TSStage_EIMEX(TS ts, PetscInt istage) {
41   TS_EIMEX *ext = (TS_EIMEX *)ts->data;
42   PetscReal h;
43   Vec       Y = ext->Y, Z = ext->Z;
44   SNES      snes;
45   TSAdapt   adapt;
46   PetscInt  i, its, lits;
47   PetscBool accept;
48 
49   PetscFunctionBegin;
50   PetscCall(TSGetSNES(ts, &snes));
51   h          = ts->time_step / ext->N[istage]; /* step size for the istage-th stage */
52   ext->shift = 1. / h;
53   PetscCall(SNESSetLagJacobian(snes, -2)); /* Recompute the Jacobian on this solve, but not again */
54   PetscCall(VecCopy(ext->VecSolPrev, Y));  /* Take the previous solution as initial step */
55 
56   for (i = 0; i < ext->N[istage]; i++) {
57     ext->ctime = ts->ptime + h * i;
58     PetscCall(VecCopy(Y, Z)); /* Save the solution of the previous substep */
59     PetscCall(SNESSolve(snes, NULL, Y));
60     PetscCall(SNESGetIterationNumber(snes, &its));
61     PetscCall(SNESGetLinearSolveIterations(snes, &lits));
62     ts->snes_its += its;
63     ts->ksp_its += lits;
64     PetscCall(TSGetAdapt(ts, &adapt));
65     PetscCall(TSAdaptCheckStage(adapt, ts, ext->ctime, Y, &accept));
66   }
67   PetscFunctionReturn(0);
68 }
69 
70 static PetscErrorCode TSStep_EIMEX(TS ts) {
71   TS_EIMEX      *ext = (TS_EIMEX *)ts->data;
72   const PetscInt ns  = ext->nstages;
73   Vec           *T = ext->T, Y = ext->Y;
74   SNES           snes;
75   PetscInt       i, j;
76   PetscBool      accept = PETSC_FALSE;
77   PetscReal      alpha, local_error, local_error_a, local_error_r;
78 
79   PetscFunctionBegin;
80   PetscCall(TSGetSNES(ts, &snes));
81   PetscCall(SNESSetType(snes, "ksponly"));
82   ext->status = TS_STEP_INCOMPLETE;
83 
84   PetscCall(VecCopy(ts->vec_sol, ext->VecSolPrev));
85 
86   /* Apply n_j steps of the base method to obtain solutions of T(j,1),1<=j<=s */
87   for (j = 0; j < ns; j++) {
88     PetscCall(TSStage_EIMEX(ts, j));
89     PetscCall(VecCopy(Y, T[j]));
90   }
91 
92   for (i = 1; i < ns; i++) {
93     for (j = i; j < ns; j++) {
94       alpha = -(PetscReal)ext->N[j] / ext->N[j - i];
95       PetscCall(VecAXPBYPCZ(T[Map(j, i, ns)], alpha, 1.0, 0, T[Map(j, i - 1, ns)], T[Map(j - 1, i - 1, ns)])); /* T[j][i]=alpha*T[j][i-1]+T[j-1][i-1] */
96       alpha = 1.0 / (1.0 + alpha);
97       PetscCall(VecScale(T[Map(j, i, ns)], alpha));
98     }
99   }
100 
101   PetscCall(TSEvaluateStep(ts, ns, ts->vec_sol, NULL)); /*update ts solution */
102 
103   if (ext->ord_adapt && ext->nstages < ext->max_rows) {
104     accept = PETSC_FALSE;
105     while (!accept && ext->nstages < ext->max_rows) {
106       PetscCall(TSErrorWeightedNorm(ts, ts->vec_sol, T[Map(ext->nstages - 1, ext->nstages - 2, ext->nstages)], ts->adapt->wnormtype, &local_error, &local_error_a, &local_error_r));
107       accept = (local_error < 1.0) ? PETSC_TRUE : PETSC_FALSE;
108 
109       if (!accept) { /* add one more stage*/
110         PetscCall(TSStage_EIMEX(ts, ext->nstages));
111         ext->nstages++;
112         ext->row_ind++;
113         ext->col_ind++;
114         /*T table need to be recycled*/
115         PetscCall(VecDuplicateVecs(ts->vec_sol, (1 + ext->nstages) * ext->nstages / 2, &ext->T));
116         for (i = 0; i < ext->nstages - 1; i++) {
117           for (j = 0; j <= i; j++) PetscCall(VecCopy(T[Map(i, j, ext->nstages - 1)], ext->T[Map(i, j, ext->nstages)]));
118         }
119         PetscCall(VecDestroyVecs(ext->nstages * (ext->nstages - 1) / 2, &T));
120         T = ext->T; /*reset the pointer*/
121         /*recycling finished, store the new solution*/
122         PetscCall(VecCopy(Y, T[ext->nstages - 1]));
123         /*extrapolation for the newly added stage*/
124         for (i = 1; i < ext->nstages; i++) {
125           alpha = -(PetscReal)ext->N[ext->nstages - 1] / ext->N[ext->nstages - 1 - i];
126           PetscCall(VecAXPBYPCZ(T[Map(ext->nstages - 1, i, ext->nstages)], alpha, 1.0, 0, T[Map(ext->nstages - 1, i - 1, ext->nstages)], T[Map(ext->nstages - 1 - 1, i - 1, ext->nstages)])); /*T[ext->nstages-1][i]=alpha*T[ext->nstages-1][i-1]+T[ext->nstages-1-1][i-1]*/
127           alpha = 1.0 / (1.0 + alpha);
128           PetscCall(VecScale(T[Map(ext->nstages - 1, i, ext->nstages)], alpha));
129         }
130         /*update ts solution */
131         PetscCall(TSEvaluateStep(ts, ext->nstages, ts->vec_sol, NULL));
132       } /*end if !accept*/
133     }   /*end while*/
134 
135     if (ext->nstages == ext->max_rows) PetscCall(PetscInfo(ts, "Max number of rows has been used\n"));
136   } /*end if ext->ord_adapt*/
137   ts->ptime += ts->time_step;
138   ext->status = TS_STEP_COMPLETE;
139 
140   if (ext->status != TS_STEP_COMPLETE && !ts->reason) ts->reason = TS_DIVERGED_STEP_REJECTED;
141   PetscFunctionReturn(0);
142 }
143 
144 /* cubic Hermit spline */
145 static PetscErrorCode TSInterpolate_EIMEX(TS ts, PetscReal itime, Vec X) {
146   TS_EIMEX       *ext = (TS_EIMEX *)ts->data;
147   PetscReal       t, a, b;
148   Vec             Y0 = ext->VecSolPrev, Y1 = ext->Y, Ydot = ext->Ydot, YdotI = ext->YdotI;
149   const PetscReal h = ts->ptime - ts->ptime_prev;
150   PetscFunctionBegin;
151   t = (itime - ts->ptime + h) / h;
152   /* YdotI = -f(x)-g(x) */
153 
154   PetscCall(VecZeroEntries(Ydot));
155   PetscCall(TSComputeIFunction(ts, ts->ptime - h, Y0, Ydot, YdotI, PETSC_FALSE));
156 
157   a = 2.0 * t * t * t - 3.0 * t * t + 1.0;
158   b = -(t * t * t - 2.0 * t * t + t) * h;
159   PetscCall(VecAXPBYPCZ(X, a, b, 0.0, Y0, YdotI));
160 
161   PetscCall(TSComputeIFunction(ts, ts->ptime, Y1, Ydot, YdotI, PETSC_FALSE));
162   a = -2.0 * t * t * t + 3.0 * t * t;
163   b = -(t * t * t - t * t) * h;
164   PetscCall(VecAXPBYPCZ(X, a, b, 1.0, Y1, YdotI));
165 
166   PetscFunctionReturn(0);
167 }
168 
169 static PetscErrorCode TSReset_EIMEX(TS ts) {
170   TS_EIMEX *ext = (TS_EIMEX *)ts->data;
171   PetscInt  ns;
172 
173   PetscFunctionBegin;
174   ns = ext->nstages;
175   PetscCall(VecDestroyVecs((1 + ns) * ns / 2, &ext->T));
176   PetscCall(VecDestroy(&ext->Y));
177   PetscCall(VecDestroy(&ext->Z));
178   PetscCall(VecDestroy(&ext->YdotRHS));
179   PetscCall(VecDestroy(&ext->YdotI));
180   PetscCall(VecDestroy(&ext->Ydot));
181   PetscCall(VecDestroy(&ext->VecSolPrev));
182   PetscCall(PetscFree(ext->N));
183   PetscFunctionReturn(0);
184 }
185 
186 static PetscErrorCode TSDestroy_EIMEX(TS ts) {
187   PetscFunctionBegin;
188   PetscCall(TSReset_EIMEX(ts));
189   PetscCall(PetscFree(ts->data));
190   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSEIMEXSetMaxRows_C", NULL));
191   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSEIMEXSetRowCol_C", NULL));
192   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSEIMEXSetOrdAdapt_C", NULL));
193   PetscFunctionReturn(0);
194 }
195 
196 static PetscErrorCode TSEIMEXGetVecs(TS ts, DM dm, Vec *Z, Vec *Ydot, Vec *YdotI, Vec *YdotRHS) {
197   TS_EIMEX *ext = (TS_EIMEX *)ts->data;
198 
199   PetscFunctionBegin;
200   if (Z) {
201     if (dm && dm != ts->dm) {
202       PetscCall(DMGetNamedGlobalVector(dm, "TSEIMEX_Z", Z));
203     } else *Z = ext->Z;
204   }
205   if (Ydot) {
206     if (dm && dm != ts->dm) {
207       PetscCall(DMGetNamedGlobalVector(dm, "TSEIMEX_Ydot", Ydot));
208     } else *Ydot = ext->Ydot;
209   }
210   if (YdotI) {
211     if (dm && dm != ts->dm) {
212       PetscCall(DMGetNamedGlobalVector(dm, "TSEIMEX_YdotI", YdotI));
213     } else *YdotI = ext->YdotI;
214   }
215   if (YdotRHS) {
216     if (dm && dm != ts->dm) {
217       PetscCall(DMGetNamedGlobalVector(dm, "TSEIMEX_YdotRHS", YdotRHS));
218     } else *YdotRHS = ext->YdotRHS;
219   }
220   PetscFunctionReturn(0);
221 }
222 
223 static PetscErrorCode TSEIMEXRestoreVecs(TS ts, DM dm, Vec *Z, Vec *Ydot, Vec *YdotI, Vec *YdotRHS) {
224   PetscFunctionBegin;
225   if (Z) {
226     if (dm && dm != ts->dm) PetscCall(DMRestoreNamedGlobalVector(dm, "TSEIMEX_Z", Z));
227   }
228   if (Ydot) {
229     if (dm && dm != ts->dm) PetscCall(DMRestoreNamedGlobalVector(dm, "TSEIMEX_Ydot", Ydot));
230   }
231   if (YdotI) {
232     if (dm && dm != ts->dm) PetscCall(DMRestoreNamedGlobalVector(dm, "TSEIMEX_YdotI", YdotI));
233   }
234   if (YdotRHS) {
235     if (dm && dm != ts->dm) PetscCall(DMRestoreNamedGlobalVector(dm, "TSEIMEX_YdotRHS", YdotRHS));
236   }
237   PetscFunctionReturn(0);
238 }
239 
240 /*
241   This defines the nonlinear equation that is to be solved with SNES
242   Fn[t0+Theta*dt, U, (U-U0)*shift] = 0
243   In the case of Backward Euler, Fn = (U-U0)/h-g(t1,U))
244   Since FormIFunction calculates G = ydot - g(t,y), ydot will be set to (U-U0)/h
245 */
246 static PetscErrorCode SNESTSFormFunction_EIMEX(SNES snes, Vec X, Vec G, TS ts) {
247   TS_EIMEX *ext = (TS_EIMEX *)ts->data;
248   Vec       Ydot, Z;
249   DM        dm, dmsave;
250 
251   PetscFunctionBegin;
252   PetscCall(VecZeroEntries(G));
253 
254   PetscCall(SNESGetDM(snes, &dm));
255   PetscCall(TSEIMEXGetVecs(ts, dm, &Z, &Ydot, NULL, NULL));
256   PetscCall(VecZeroEntries(Ydot));
257   dmsave = ts->dm;
258   ts->dm = dm;
259   PetscCall(TSComputeIFunction(ts, ext->ctime, X, Ydot, G, PETSC_FALSE));
260   /* PETSC_FALSE indicates non-imex, adding explicit RHS to the implicit I function.  */
261   PetscCall(VecCopy(G, Ydot));
262   ts->dm = dmsave;
263   PetscCall(TSEIMEXRestoreVecs(ts, dm, &Z, &Ydot, NULL, NULL));
264 
265   PetscFunctionReturn(0);
266 }
267 
268 /*
269  This defined the Jacobian matrix for SNES. Jn = (I/h-g'(t,y))
270  */
271 static PetscErrorCode SNESTSFormJacobian_EIMEX(SNES snes, Vec X, Mat A, Mat B, TS ts) {
272   TS_EIMEX *ext = (TS_EIMEX *)ts->data;
273   Vec       Ydot;
274   DM        dm, dmsave;
275   PetscFunctionBegin;
276   PetscCall(SNESGetDM(snes, &dm));
277   PetscCall(TSEIMEXGetVecs(ts, dm, NULL, &Ydot, NULL, NULL));
278   /*  PetscCall(VecZeroEntries(Ydot)); */
279   /* ext->Ydot have already been computed in SNESTSFormFunction_EIMEX (SNES guarantees this) */
280   dmsave = ts->dm;
281   ts->dm = dm;
282   PetscCall(TSComputeIJacobian(ts, ts->ptime, X, Ydot, ext->shift, A, B, PETSC_TRUE));
283   ts->dm = dmsave;
284   PetscCall(TSEIMEXRestoreVecs(ts, dm, NULL, &Ydot, NULL, NULL));
285   PetscFunctionReturn(0);
286 }
287 
288 static PetscErrorCode DMCoarsenHook_TSEIMEX(DM fine, DM coarse, void *ctx) {
289   PetscFunctionBegin;
290   PetscFunctionReturn(0);
291 }
292 
293 static PetscErrorCode DMRestrictHook_TSEIMEX(DM fine, Mat restrct, Vec rscale, Mat inject, DM coarse, void *ctx) {
294   TS  ts = (TS)ctx;
295   Vec Z, Z_c;
296 
297   PetscFunctionBegin;
298   PetscCall(TSEIMEXGetVecs(ts, fine, &Z, NULL, NULL, NULL));
299   PetscCall(TSEIMEXGetVecs(ts, coarse, &Z_c, NULL, NULL, NULL));
300   PetscCall(MatRestrict(restrct, Z, Z_c));
301   PetscCall(VecPointwiseMult(Z_c, rscale, Z_c));
302   PetscCall(TSEIMEXRestoreVecs(ts, fine, &Z, NULL, NULL, NULL));
303   PetscCall(TSEIMEXRestoreVecs(ts, coarse, &Z_c, NULL, NULL, NULL));
304   PetscFunctionReturn(0);
305 }
306 
307 static PetscErrorCode TSSetUp_EIMEX(TS ts) {
308   TS_EIMEX *ext = (TS_EIMEX *)ts->data;
309   DM        dm;
310 
311   PetscFunctionBegin;
312   if (!ext->N) { /* ext->max_rows not set */
313     PetscCall(TSEIMEXSetMaxRows(ts, TSEIMEXDefault));
314   }
315   if (-1 == ext->row_ind && -1 == ext->col_ind) {
316     PetscCall(TSEIMEXSetRowCol(ts, ext->max_rows, ext->max_rows));
317   } else { /* ext->row_ind and col_ind already set */
318     if (ext->ord_adapt) PetscCall(PetscInfo(ts, "Order adaptivity is enabled and TSEIMEXSetRowCol or -ts_eimex_row_col option will take no effect\n"));
319   }
320 
321   if (ext->ord_adapt) {
322     ext->nstages = 2; /* Start with the 2-stage scheme */
323     PetscCall(TSEIMEXSetRowCol(ts, ext->nstages, ext->nstages));
324   } else {
325     ext->nstages = ext->max_rows; /* by default nstages is the same as max_rows, this can be changed by setting order adaptivity */
326   }
327 
328   PetscCall(TSGetAdapt(ts, &ts->adapt));
329 
330   PetscCall(VecDuplicateVecs(ts->vec_sol, (1 + ext->nstages) * ext->nstages / 2, &ext->T)); /* full T table */
331   PetscCall(VecDuplicate(ts->vec_sol, &ext->YdotI));
332   PetscCall(VecDuplicate(ts->vec_sol, &ext->YdotRHS));
333   PetscCall(VecDuplicate(ts->vec_sol, &ext->Ydot));
334   PetscCall(VecDuplicate(ts->vec_sol, &ext->VecSolPrev));
335   PetscCall(VecDuplicate(ts->vec_sol, &ext->Y));
336   PetscCall(VecDuplicate(ts->vec_sol, &ext->Z));
337   PetscCall(TSGetDM(ts, &dm));
338   if (dm) PetscCall(DMCoarsenHookAdd(dm, DMCoarsenHook_TSEIMEX, DMRestrictHook_TSEIMEX, ts));
339   PetscFunctionReturn(0);
340 }
341 
342 static PetscErrorCode TSSetFromOptions_EIMEX(TS ts, PetscOptionItems *PetscOptionsObject) {
343   TS_EIMEX *ext = (TS_EIMEX *)ts->data;
344   PetscInt  tindex[2];
345   PetscInt  np = 2, nrows = TSEIMEXDefault;
346 
347   PetscFunctionBegin;
348   tindex[0] = TSEIMEXDefault;
349   tindex[1] = TSEIMEXDefault;
350   PetscOptionsHeadBegin(PetscOptionsObject, "EIMEX ODE solver options");
351   {
352     PetscBool flg;
353     PetscCall(PetscOptionsInt("-ts_eimex_max_rows", "Define the maximum number of rows used", "TSEIMEXSetMaxRows", nrows, &nrows, &flg)); /* default value 3 */
354     if (flg) PetscCall(TSEIMEXSetMaxRows(ts, nrows));
355     PetscCall(PetscOptionsIntArray("-ts_eimex_row_col", "Return the specific term in the T table", "TSEIMEXSetRowCol", tindex, &np, &flg));
356     if (flg) PetscCall(TSEIMEXSetRowCol(ts, tindex[0], tindex[1]));
357     PetscCall(PetscOptionsBool("-ts_eimex_order_adapt", "Solve the problem with adaptive order", "TSEIMEXSetOrdAdapt", ext->ord_adapt, &ext->ord_adapt, NULL));
358   }
359   PetscOptionsHeadEnd();
360   PetscFunctionReturn(0);
361 }
362 
363 static PetscErrorCode TSView_EIMEX(TS ts, PetscViewer viewer) {
364   PetscFunctionBegin;
365   PetscFunctionReturn(0);
366 }
367 
368 /*@C
369   TSEIMEXSetMaxRows - Set the maximum number of rows for EIMEX schemes
370 
371   Logically collective
372 
373   Input Parameters:
374 +  ts - timestepping context
375 -  nrows - maximum number of rows
376 
377   Level: intermediate
378 
379 .seealso: `TSEIMEXSetRowCol()`, `TSEIMEXSetOrdAdapt()`, `TSEIMEX`
380 @*/
381 PetscErrorCode TSEIMEXSetMaxRows(TS ts, PetscInt nrows) {
382   PetscFunctionBegin;
383   PetscValidHeaderSpecific(ts, TS_CLASSID, 1);
384   PetscTryMethod(ts, "TSEIMEXSetMaxRows_C", (TS, PetscInt), (ts, nrows));
385   PetscFunctionReturn(0);
386 }
387 
388 /*@C
389   TSEIMEXSetRowCol - Set the type index in the T table for the return value
390 
391   Logically collective
392 
393   Input Parameters:
394 +  ts - timestepping context
395 -  tindex - index in the T table
396 
397   Level: intermediate
398 
399 .seealso: `TSEIMEXSetMaxRows()`, `TSEIMEXSetOrdAdapt()`, `TSEIMEX`
400 @*/
401 PetscErrorCode TSEIMEXSetRowCol(TS ts, PetscInt row, PetscInt col) {
402   PetscFunctionBegin;
403   PetscValidHeaderSpecific(ts, TS_CLASSID, 1);
404   PetscTryMethod(ts, "TSEIMEXSetRowCol_C", (TS, PetscInt, PetscInt), (ts, row, col));
405   PetscFunctionReturn(0);
406 }
407 
408 /*@C
409   TSEIMEXSetOrdAdapt - Set the order adaptativity
410 
411   Logically collective
412 
413   Input Parameters:
414 +  ts - timestepping context
415 -  tindex - index in the T table
416 
417   Level: intermediate
418 
419 .seealso: `TSEIMEXSetRowCol()`, `TSEIMEXSetOrdAdapt()`, `TSEIMEX`
420 @*/
421 PetscErrorCode TSEIMEXSetOrdAdapt(TS ts, PetscBool flg) {
422   PetscFunctionBegin;
423   PetscValidHeaderSpecific(ts, TS_CLASSID, 1);
424   PetscTryMethod(ts, "TSEIMEXSetOrdAdapt_C", (TS, PetscBool), (ts, flg));
425   PetscFunctionReturn(0);
426 }
427 
428 static PetscErrorCode TSEIMEXSetMaxRows_EIMEX(TS ts, PetscInt nrows) {
429   TS_EIMEX *ext = (TS_EIMEX *)ts->data;
430   PetscInt  i;
431 
432   PetscFunctionBegin;
433   PetscCheck(nrows >= 0 && nrows <= 100, ((PetscObject)ts)->comm, PETSC_ERR_ARG_OUTOFRANGE, "Max number of rows (current value %" PetscInt_FMT ") should be an integer number between 1 and 100", nrows);
434   PetscCall(PetscFree(ext->N));
435   ext->max_rows = nrows;
436   PetscCall(PetscMalloc1(nrows, &ext->N));
437   for (i = 0; i < nrows; i++) ext->N[i] = i + 1;
438   PetscFunctionReturn(0);
439 }
440 
441 static PetscErrorCode TSEIMEXSetRowCol_EIMEX(TS ts, PetscInt row, PetscInt col) {
442   TS_EIMEX *ext = (TS_EIMEX *)ts->data;
443 
444   PetscFunctionBegin;
445   PetscCheck(row >= 1 && col >= 1, ((PetscObject)ts)->comm, PETSC_ERR_ARG_OUTOFRANGE, "The row or column index (current value %" PetscInt_FMT ",%" PetscInt_FMT ") should not be less than 1 ", row, col);
446   PetscCheck(row <= ext->max_rows && col <= ext->max_rows, ((PetscObject)ts)->comm, PETSC_ERR_ARG_OUTOFRANGE, "The row or column index (current value %" PetscInt_FMT ",%" PetscInt_FMT ") exceeds the maximum number of rows %" PetscInt_FMT, row, col,
447              ext->max_rows);
448   PetscCheck(col <= row, ((PetscObject)ts)->comm, PETSC_ERR_ARG_OUTOFRANGE, "The column index (%" PetscInt_FMT ") exceeds the row index (%" PetscInt_FMT ")", col, row);
449 
450   ext->row_ind = row - 1;
451   ext->col_ind = col - 1; /* Array index in C starts from 0 */
452   PetscFunctionReturn(0);
453 }
454 
455 static PetscErrorCode TSEIMEXSetOrdAdapt_EIMEX(TS ts, PetscBool flg) {
456   TS_EIMEX *ext = (TS_EIMEX *)ts->data;
457   PetscFunctionBegin;
458   ext->ord_adapt = flg;
459   PetscFunctionReturn(0);
460 }
461 
462 /*MC
463       TSEIMEX - Time stepping with Extrapolated IMEX methods.
464 
465    These methods are intended for problems with well-separated time scales, especially when a slow scale is strongly nonlinear such that it
466    is expensive to solve with a fully implicit method. The user should provide the stiff part of the equation using TSSetIFunction() and the
467    non-stiff part with TSSetRHSFunction().
468 
469    Notes:
470   The default is a 3-stage scheme, it can be changed with TSEIMEXSetMaxRows() or -ts_eimex_max_rows
471 
472   This method currently only works with ODE, for which the stiff part G(t,X,Xdot) has the form Xdot + Ghat(t,X).
473 
474   The general system is written as
475 
476   G(t,X,Xdot) = F(t,X)
477 
478   where G represents the stiff part and F represents the non-stiff part. The user should provide the stiff part
479   of the equation using TSSetIFunction() and the non-stiff part with TSSetRHSFunction().
480   This method is designed to be linearly implicit on G and can use an approximate and lagged Jacobian.
481 
482   Another common form for the system is
483 
484   y'=f(x)+g(x)
485 
486   The relationship between F,G and f,g is
487 
488   G = y'-g(x), F = f(x)
489 
490  References
491   E. Constantinescu and A. Sandu, Extrapolated implicit-explicit time stepping, SIAM Journal on Scientific
492 Computing, 31 (2010), pp. 4452-4477.
493 
494       Level: beginner
495 
496 .seealso: `TSCreate()`, `TS`, `TSSetType()`, `TSEIMEXSetMaxRows()`, `TSEIMEXSetRowCol()`, `TSEIMEXSetOrdAdapt()`
497 
498  M*/
499 PETSC_EXTERN PetscErrorCode TSCreate_EIMEX(TS ts) {
500   TS_EIMEX *ext;
501 
502   PetscFunctionBegin;
503 
504   ts->ops->reset          = TSReset_EIMEX;
505   ts->ops->destroy        = TSDestroy_EIMEX;
506   ts->ops->view           = TSView_EIMEX;
507   ts->ops->setup          = TSSetUp_EIMEX;
508   ts->ops->step           = TSStep_EIMEX;
509   ts->ops->interpolate    = TSInterpolate_EIMEX;
510   ts->ops->evaluatestep   = TSEvaluateStep_EIMEX;
511   ts->ops->setfromoptions = TSSetFromOptions_EIMEX;
512   ts->ops->snesfunction   = SNESTSFormFunction_EIMEX;
513   ts->ops->snesjacobian   = SNESTSFormJacobian_EIMEX;
514   ts->default_adapt_type  = TSADAPTNONE;
515 
516   ts->usessnes = PETSC_TRUE;
517 
518   PetscCall(PetscNewLog(ts, &ext));
519   ts->data = (void *)ext;
520 
521   ext->ord_adapt = PETSC_FALSE; /* By default, no order adapativity */
522   ext->row_ind   = -1;
523   ext->col_ind   = -1;
524   ext->max_rows  = TSEIMEXDefault;
525   ext->nstages   = TSEIMEXDefault;
526 
527   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSEIMEXSetMaxRows_C", TSEIMEXSetMaxRows_EIMEX));
528   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSEIMEXSetRowCol_C", TSEIMEXSetRowCol_EIMEX));
529   PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSEIMEXSetOrdAdapt_C", TSEIMEXSetOrdAdapt_EIMEX));
530   PetscFunctionReturn(0);
531 }
532