xref: /petsc/src/ts/tutorials/multirate/ex5.c (revision a336c15037c72f93cd561f5a5e11e93175f2efd9)
1 /*
2   Note:
3   To performance a convergence study, a reference solution can be generated with a small stepsize and stored into a binary file, e.g. -ts_type ssp -ts_time_step 1e-5 -ts_max_steps 30000 -ts_view_solution binary:reference.bin
4   Errors can be computed in the following runs with -simulation -f reference.bin
5 
6   Multirate RK options:
7   -ts_rk_dtratio is the ratio between larger time step size and small time step size
8   -ts_rk_multirate_type has three choices: none (for single step RK)
9                                            combined (for multirate method and user just need to provide the same RHS with the single step RK)
10                                            partitioned (for multiraet method and user need to provide two RHS, one is for fast components and the orther is for slow components
11 */
12 
13 static const char help[] = "1D periodic Finite Volume solver in slope-limiter form with semidiscrete time stepping.\n"
14                            " advection   - Variable coefficient scalar advection\n"
15                            "                u_t       + (a*u)_x               = 0\n"
16                            " a is a piecewise function with two values say a0 and a1 (both a0 and a1 are constant).\n"
17                            " in this toy problem we assume a=a0 when x<0 and a=a1 when x>0 with a0<a1 which has the same discontinuous point with initial condition.\n"
18                            " we don't provide an exact solution, so you need to generate reference solution to do convergnece staudy,\n"
19                            " more precisely, you need firstly generate a reference to a binary file say file.bin, then on command line,\n"
20                            " you should type -simulation -f file.bin.\n"
21                            " you can choose the number of grids by -da_grid_x.\n"
22                            " you can choose the value of a by -physics_advect_a1 and -physics_advect_a2.\n";
23 
24 #include <petscts.h>
25 #include <petscdm.h>
26 #include <petscdmda.h>
27 #include <petscdraw.h>
28 #include <petsc/private/tsimpl.h>
29 
30 #include <petsc/private/kernels/blockinvert.h> /* For the Kernel_*_gets_* stuff for BAIJ */
31 
32 #include "finitevolume1d.h"
33 
34 static inline PetscReal RangeMod(PetscReal a, PetscReal xmin, PetscReal xmax)
35 {
36   PetscReal range = xmax - xmin;
37   return xmin + PetscFmodReal(range + PetscFmodReal(a, range), range);
38 }
39 
40 /* --------------------------------- Advection ----------------------------------- */
41 
42 typedef struct {
43   PetscReal a[2]; /* advective velocity */
44 } AdvectCtx;
45 
46 static PetscErrorCode PhysicsRiemann_Advect(void *vctx, PetscInt m, const PetscScalar *uL, const PetscScalar *uR, PetscScalar *flux, PetscReal *maxspeed, PetscReal x, PetscReal xmin, PetscReal xmax)
47 {
48   AdvectCtx *ctx = (AdvectCtx *)vctx;
49   PetscReal *speed;
50 
51   PetscFunctionBeginUser;
52   speed = ctx->a;
53   if (x == 0 || x == xmin || x == xmax)
54     flux[0] = PetscMax(0, (speed[0] + speed[1]) / 2.0) * uL[0] + PetscMin(0, (speed[0] + speed[1]) / 2.0) * uR[0]; /* if condition need to be changed base on different problem, '0' is the discontinuous point of a */
55   else if (x < 0) flux[0] = PetscMax(0, speed[0]) * uL[0] + PetscMin(0, speed[0]) * uR[0];                         /* else if condition need to be changed based on different problem, 'x = 0' is discontinuous point of a */
56   else flux[0] = PetscMax(0, speed[1]) * uL[0] + PetscMin(0, speed[1]) * uR[0];
57   *maxspeed = *speed;
58   PetscFunctionReturn(PETSC_SUCCESS);
59 }
60 
61 static PetscErrorCode PhysicsCharacteristic_Advect(void *vctx, PetscInt m, const PetscScalar *u, PetscScalar *X, PetscScalar *Xi, PetscReal *speeds, PetscReal x)
62 {
63   AdvectCtx *ctx = (AdvectCtx *)vctx;
64 
65   PetscFunctionBeginUser;
66   X[0]  = 1.;
67   Xi[0] = 1.;
68   if (x < 0) speeds[0] = ctx->a[0]; /* x is discontinuous point of a */
69   else speeds[0] = ctx->a[1];
70   PetscFunctionReturn(PETSC_SUCCESS);
71 }
72 
73 static PetscErrorCode PhysicsSample_Advect(void *vctx, PetscInt initial, FVBCType bctype, PetscReal xmin, PetscReal xmax, PetscReal t, PetscReal x, PetscReal *u)
74 {
75   AdvectCtx *ctx = (AdvectCtx *)vctx;
76   PetscReal *a   = ctx->a, x0;
77 
78   PetscFunctionBeginUser;
79   if (x < 0) { /* x is cell center */
80     switch (bctype) {
81     case FVBC_OUTFLOW:
82       x0 = x - a[0] * t;
83       break;
84     case FVBC_PERIODIC:
85       x0 = RangeMod(x - a[0] * t, xmin, xmax);
86       break;
87     default:
88       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "unknown BCType");
89     }
90     switch (initial) {
91     case 0:
92       u[0] = (x0 < 0) ? 1 : -1;
93       break;
94     case 1:
95       u[0] = (x0 < 0) ? -1 : 1;
96       break;
97     case 2:
98       u[0] = (0 < x0 && x0 < 1) ? 1 : 0;
99       break;
100     case 3:
101       u[0] = PetscSinReal(2 * PETSC_PI * x0);
102       break;
103     case 4:
104       u[0] = PetscAbs(x0);
105       break;
106     case 5:
107       u[0] = (x0 < 0 || x0 > 0.5) ? 0 : PetscSqr(PetscSinReal(2 * PETSC_PI * x0));
108       break;
109     case 6:
110       u[0] = (x0 < 0) ? 0 : ((x0 < 1) ? x0 : ((x0 < 2) ? 2 - x0 : 0));
111       break;
112     case 7:
113       u[0] = PetscPowReal(PetscSinReal(PETSC_PI * x0), 10.0);
114       break;
115     default:
116       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "unknown initial condition");
117     }
118   } else {
119     switch (bctype) {
120     case FVBC_OUTFLOW:
121       x0 = x - a[1] * t;
122       break;
123     case FVBC_PERIODIC:
124       x0 = RangeMod(x - a[1] * t, xmin, xmax);
125       break;
126     default:
127       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "unknown BCType");
128     }
129     switch (initial) {
130     case 0:
131       u[0] = (x0 < 0) ? 1 : -1;
132       break;
133     case 1:
134       u[0] = (x0 < 0) ? -1 : 1;
135       break;
136     case 2:
137       u[0] = (0 < x0 && x0 < 1) ? 1 : 0;
138       break;
139     case 3:
140       u[0] = PetscSinReal(2 * PETSC_PI * x0);
141       break;
142     case 4:
143       u[0] = PetscAbs(x0);
144       break;
145     case 5:
146       u[0] = (x0 < 0 || x0 > 0.5) ? 0 : PetscSqr(PetscSinReal(2 * PETSC_PI * x0));
147       break;
148     case 6:
149       u[0] = (x0 < 0) ? 0 : ((x0 < 1) ? x0 : ((x0 < 2) ? 2 - x0 : 0));
150       break;
151     case 7:
152       u[0] = PetscPowReal(PetscSinReal(PETSC_PI * x0), 10.0);
153       break;
154     default:
155       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "unknown initial condition");
156     }
157   }
158   PetscFunctionReturn(PETSC_SUCCESS);
159 }
160 
161 static PetscErrorCode PhysicsCreate_Advect(FVCtx *ctx)
162 {
163   AdvectCtx *user;
164 
165   PetscFunctionBeginUser;
166   PetscCall(PetscNew(&user));
167   ctx->physics.sample         = PhysicsSample_Advect;
168   ctx->physics.riemann        = PhysicsRiemann_Advect;
169   ctx->physics.characteristic = PhysicsCharacteristic_Advect;
170   ctx->physics.destroy        = PhysicsDestroy_SimpleFree;
171   ctx->physics.user           = user;
172   ctx->physics.dof            = 1;
173   PetscCall(PetscStrallocpy("u", &ctx->physics.fieldname[0]));
174   user->a[0] = 1;
175   user->a[1] = 1;
176   PetscOptionsBegin(ctx->comm, ctx->prefix, "Options for advection", "");
177   {
178     PetscCall(PetscOptionsReal("-physics_advect_a1", "Speed1", "", user->a[0], &user->a[0], NULL));
179     PetscCall(PetscOptionsReal("-physics_advect_a2", "Speed2", "", user->a[1], &user->a[1], NULL));
180   }
181   PetscOptionsEnd();
182   PetscFunctionReturn(PETSC_SUCCESS);
183 }
184 
185 /* --------------------------------- Finite Volume Solver for slow parts ----------------------------------- */
186 
187 PetscErrorCode FVRHSFunctionslow(TS ts, PetscReal time, Vec X, Vec F, void *vctx)
188 {
189   FVCtx       *ctx = (FVCtx *)vctx;
190   PetscInt     i, j, k, Mx, dof, xs, xm, len_slow;
191   PetscReal    hx, cfl_idt = 0;
192   PetscScalar *x, *f, *slope;
193   Vec          Xloc;
194   DM           da;
195 
196   PetscFunctionBeginUser;
197   PetscCall(TSGetDM(ts, &da));
198   PetscCall(DMGetLocalVector(da, &Xloc));
199   PetscCall(DMDAGetInfo(da, 0, &Mx, 0, 0, 0, 0, 0, &dof, 0, 0, 0, 0, 0));
200   hx = (ctx->xmax - ctx->xmin) / Mx;
201   PetscCall(DMGlobalToLocalBegin(da, X, INSERT_VALUES, Xloc));
202   PetscCall(DMGlobalToLocalEnd(da, X, INSERT_VALUES, Xloc));
203   PetscCall(VecZeroEntries(F));
204   PetscCall(DMDAVecGetArray(da, Xloc, &x));
205   PetscCall(VecGetArray(F, &f));
206   PetscCall(DMDAGetArray(da, PETSC_TRUE, &slope));
207   PetscCall(DMDAGetCorners(da, &xs, 0, 0, &xm, 0, 0));
208   PetscCall(ISGetSize(ctx->iss, &len_slow));
209 
210   if (ctx->bctype == FVBC_OUTFLOW) {
211     for (i = xs - 2; i < 0; i++) {
212       for (j = 0; j < dof; j++) x[i * dof + j] = x[j];
213     }
214     for (i = Mx; i < xs + xm + 2; i++) {
215       for (j = 0; j < dof; j++) x[i * dof + j] = x[(xs + xm - 1) * dof + j];
216     }
217   }
218   for (i = xs - 1; i < xs + xm + 1; i++) {
219     struct _LimitInfo info;
220     PetscScalar      *cjmpL, *cjmpR;
221     if (i < len_slow + 1) {
222       /* Determine the right eigenvectors R, where A = R \Lambda R^{-1} */
223       PetscCall((*ctx->physics.characteristic)(ctx->physics.user, dof, &x[i * dof], ctx->R, ctx->Rinv, ctx->speeds, ctx->xmin + hx * i));
224       /* Evaluate jumps across interfaces (i-1, i) and (i, i+1), put in characteristic basis */
225       PetscCall(PetscArrayzero(ctx->cjmpLR, 2 * dof));
226       cjmpL = &ctx->cjmpLR[0];
227       cjmpR = &ctx->cjmpLR[dof];
228       for (j = 0; j < dof; j++) {
229         PetscScalar jmpL, jmpR;
230         jmpL = x[(i + 0) * dof + j] - x[(i - 1) * dof + j];
231         jmpR = x[(i + 1) * dof + j] - x[(i + 0) * dof + j];
232         for (k = 0; k < dof; k++) {
233           cjmpL[k] += ctx->Rinv[k + j * dof] * jmpL;
234           cjmpR[k] += ctx->Rinv[k + j * dof] * jmpR;
235         }
236       }
237       /* Apply limiter to the left and right characteristic jumps */
238       info.m  = dof;
239       info.hx = hx;
240       (*ctx->limit)(&info, cjmpL, cjmpR, ctx->cslope);
241       for (j = 0; j < dof; j++) ctx->cslope[j] /= hx; /* rescale to a slope */
242       for (j = 0; j < dof; j++) {
243         PetscScalar tmp = 0;
244         for (k = 0; k < dof; k++) tmp += ctx->R[j + k * dof] * ctx->cslope[k];
245         slope[i * dof + j] = tmp;
246       }
247     }
248   }
249 
250   for (i = xs; i < xs + xm + 1; i++) {
251     PetscReal    maxspeed;
252     PetscScalar *uL, *uR;
253     if (i < len_slow) { /* slow parts can be changed based on a */
254       uL = &ctx->uLR[0];
255       uR = &ctx->uLR[dof];
256       for (j = 0; j < dof; j++) {
257         uL[j] = x[(i - 1) * dof + j] + slope[(i - 1) * dof + j] * hx / 2;
258         uR[j] = x[(i - 0) * dof + j] - slope[(i - 0) * dof + j] * hx / 2;
259       }
260       PetscCall((*ctx->physics.riemann)(ctx->physics.user, dof, uL, uR, ctx->flux, &maxspeed, ctx->xmin + hx * i, ctx->xmin, ctx->xmax));
261       cfl_idt = PetscMax(cfl_idt, PetscAbsScalar(maxspeed / hx)); /* Max allowable value of 1/Delta t */
262       if (i > xs) {
263         for (j = 0; j < dof; j++) f[(i - 1) * dof + j] -= ctx->flux[j] / hx;
264       }
265       if (i < xs + xm) {
266         for (j = 0; j < dof; j++) f[i * dof + j] += ctx->flux[j] / hx;
267       }
268     }
269     if (i == len_slow) { /* slow parts can be changed based on a */
270       uL = &ctx->uLR[0];
271       uR = &ctx->uLR[dof];
272       for (j = 0; j < dof; j++) {
273         uL[j] = x[(i - 1) * dof + j] + slope[(i - 1) * dof + j] * hx / 2;
274         uR[j] = x[(i - 0) * dof + j] - slope[(i - 0) * dof + j] * hx / 2;
275       }
276       PetscCall((*ctx->physics.riemann)(ctx->physics.user, dof, uL, uR, ctx->flux, &maxspeed, ctx->xmin + hx * i, ctx->xmin, ctx->xmax));
277       cfl_idt = PetscMax(cfl_idt, PetscAbsScalar(maxspeed / hx)); /* Max allowable value of 1/Delta t */
278       if (i > xs) {
279         for (j = 0; j < dof; j++) f[(i - 1) * dof + j] -= ctx->flux[j] / hx;
280       }
281     }
282   }
283   PetscCall(DMDAVecRestoreArray(da, Xloc, &x));
284   PetscCall(VecRestoreArray(F, &f));
285   PetscCall(DMDARestoreArray(da, PETSC_TRUE, &slope));
286   PetscCall(DMRestoreLocalVector(da, &Xloc));
287   PetscFunctionReturn(PETSC_SUCCESS);
288 }
289 
290 /* --------------------------------- Finite Volume Solver for fast  parts ----------------------------------- */
291 
292 PetscErrorCode FVRHSFunctionfast(TS ts, PetscReal time, Vec X, Vec F, void *vctx)
293 {
294   FVCtx       *ctx = (FVCtx *)vctx;
295   PetscInt     i, j, k, Mx, dof, xs, xm, len_slow;
296   PetscReal    hx, cfl_idt = 0;
297   PetscScalar *x, *f, *slope;
298   Vec          Xloc;
299   DM           da;
300 
301   PetscFunctionBeginUser;
302   PetscCall(TSGetDM(ts, &da));
303   PetscCall(DMGetLocalVector(da, &Xloc));
304   PetscCall(DMDAGetInfo(da, 0, &Mx, 0, 0, 0, 0, 0, &dof, 0, 0, 0, 0, 0));
305   hx = (ctx->xmax - ctx->xmin) / Mx;
306   PetscCall(DMGlobalToLocalBegin(da, X, INSERT_VALUES, Xloc));
307   PetscCall(DMGlobalToLocalEnd(da, X, INSERT_VALUES, Xloc));
308   PetscCall(VecZeroEntries(F));
309   PetscCall(DMDAVecGetArray(da, Xloc, &x));
310   PetscCall(VecGetArray(F, &f));
311   PetscCall(DMDAGetArray(da, PETSC_TRUE, &slope));
312   PetscCall(DMDAGetCorners(da, &xs, 0, 0, &xm, 0, 0));
313   PetscCall(ISGetSize(ctx->iss, &len_slow));
314 
315   if (ctx->bctype == FVBC_OUTFLOW) {
316     for (i = xs - 2; i < 0; i++) {
317       for (j = 0; j < dof; j++) x[i * dof + j] = x[j];
318     }
319     for (i = Mx; i < xs + xm + 2; i++) {
320       for (j = 0; j < dof; j++) x[i * dof + j] = x[(xs + xm - 1) * dof + j];
321     }
322   }
323   for (i = xs - 1; i < xs + xm + 1; i++) {
324     struct _LimitInfo info;
325     PetscScalar      *cjmpL, *cjmpR;
326     if (i > len_slow - 2) {
327       PetscCall((*ctx->physics.characteristic)(ctx->physics.user, dof, &x[i * dof], ctx->R, ctx->Rinv, ctx->speeds, ctx->xmin + hx * i));
328       PetscCall(PetscArrayzero(ctx->cjmpLR, 2 * dof));
329       cjmpL = &ctx->cjmpLR[0];
330       cjmpR = &ctx->cjmpLR[dof];
331       for (j = 0; j < dof; j++) {
332         PetscScalar jmpL, jmpR;
333         jmpL = x[(i + 0) * dof + j] - x[(i - 1) * dof + j];
334         jmpR = x[(i + 1) * dof + j] - x[(i + 0) * dof + j];
335         for (k = 0; k < dof; k++) {
336           cjmpL[k] += ctx->Rinv[k + j * dof] * jmpL;
337           cjmpR[k] += ctx->Rinv[k + j * dof] * jmpR;
338         }
339       }
340       /* Apply limiter to the left and right characteristic jumps */
341       info.m  = dof;
342       info.hx = hx;
343       (*ctx->limit)(&info, cjmpL, cjmpR, ctx->cslope);
344       for (j = 0; j < dof; j++) ctx->cslope[j] /= hx; /* rescale to a slope */
345       for (j = 0; j < dof; j++) {
346         PetscScalar tmp = 0;
347         for (k = 0; k < dof; k++) tmp += ctx->R[j + k * dof] * ctx->cslope[k];
348         slope[i * dof + j] = tmp;
349       }
350     }
351   }
352 
353   for (i = xs; i < xs + xm + 1; i++) {
354     PetscReal    maxspeed;
355     PetscScalar *uL, *uR;
356     if (i > len_slow) {
357       uL = &ctx->uLR[0];
358       uR = &ctx->uLR[dof];
359       for (j = 0; j < dof; j++) {
360         uL[j] = x[(i - 1) * dof + j] + slope[(i - 1) * dof + j] * hx / 2;
361         uR[j] = x[(i - 0) * dof + j] - slope[(i - 0) * dof + j] * hx / 2;
362       }
363       PetscCall((*ctx->physics.riemann)(ctx->physics.user, dof, uL, uR, ctx->flux, &maxspeed, ctx->xmin + hx * i, ctx->xmin, ctx->xmax));
364       cfl_idt = PetscMax(cfl_idt, PetscAbsScalar(maxspeed / hx)); /* Max allowable value of 1/Delta t */
365       if (i > xs) {
366         for (j = 0; j < dof; j++) f[(i - len_slow - 1) * dof + j] -= ctx->flux[j] / hx;
367       }
368       if (i < xs + xm) {
369         for (j = 0; j < dof; j++) f[(i - len_slow) * dof + j] += ctx->flux[j] / hx;
370       }
371     }
372     if (i == len_slow) {
373       uL = &ctx->uLR[0];
374       uR = &ctx->uLR[dof];
375       for (j = 0; j < dof; j++) {
376         uL[j] = x[(i - 1) * dof + j] + slope[(i - 1) * dof + j] * hx / 2;
377         uR[j] = x[(i - 0) * dof + j] - slope[(i - 0) * dof + j] * hx / 2;
378       }
379       PetscCall((*ctx->physics.riemann)(ctx->physics.user, dof, uL, uR, ctx->flux, &maxspeed, ctx->xmin + hx * i, ctx->xmin, ctx->xmax));
380       cfl_idt = PetscMax(cfl_idt, PetscAbsScalar(maxspeed / hx)); /* Max allowable value of 1/Delta t */
381       if (i < xs + xm) {
382         for (j = 0; j < dof; j++) f[(i - len_slow) * dof + j] += ctx->flux[j] / hx;
383       }
384     }
385   }
386   PetscCall(DMDAVecRestoreArray(da, Xloc, &x));
387   PetscCall(VecRestoreArray(F, &f));
388   PetscCall(DMDARestoreArray(da, PETSC_TRUE, &slope));
389   PetscCall(DMRestoreLocalVector(da, &Xloc));
390   PetscFunctionReturn(PETSC_SUCCESS);
391 }
392 
393 PetscErrorCode FVRHSFunctionslow2(TS ts, PetscReal time, Vec X, Vec F, void *vctx)
394 {
395   FVCtx       *ctx = (FVCtx *)vctx;
396   PetscInt     i, j, k, Mx, dof, xs, xm, len_slow1, len_slow2;
397   PetscReal    hx, cfl_idt = 0;
398   PetscScalar *x, *f, *slope;
399   Vec          Xloc;
400   DM           da;
401 
402   PetscFunctionBeginUser;
403   PetscCall(TSGetDM(ts, &da));
404   PetscCall(DMGetLocalVector(da, &Xloc));
405   PetscCall(DMDAGetInfo(da, 0, &Mx, 0, 0, 0, 0, 0, &dof, 0, 0, 0, 0, 0));
406   hx = (ctx->xmax - ctx->xmin) / Mx;
407   PetscCall(DMGlobalToLocalBegin(da, X, INSERT_VALUES, Xloc));
408   PetscCall(DMGlobalToLocalEnd(da, X, INSERT_VALUES, Xloc));
409   PetscCall(VecZeroEntries(F));
410   PetscCall(DMDAVecGetArray(da, Xloc, &x));
411   PetscCall(VecGetArray(F, &f));
412   PetscCall(DMDAGetArray(da, PETSC_TRUE, &slope));
413   PetscCall(DMDAGetCorners(da, &xs, 0, 0, &xm, 0, 0));
414   PetscCall(ISGetSize(ctx->iss, &len_slow1));
415   PetscCall(ISGetSize(ctx->iss2, &len_slow2));
416   if (ctx->bctype == FVBC_OUTFLOW) {
417     for (i = xs - 2; i < 0; i++) {
418       for (j = 0; j < dof; j++) x[i * dof + j] = x[j];
419     }
420     for (i = Mx; i < xs + xm + 2; i++) {
421       for (j = 0; j < dof; j++) x[i * dof + j] = x[(xs + xm - 1) * dof + j];
422     }
423   }
424   for (i = xs - 1; i < xs + xm + 1; i++) {
425     struct _LimitInfo info;
426     PetscScalar      *cjmpL, *cjmpR;
427     if (i < len_slow1 + len_slow2 + 1 && i > len_slow1 - 2) {
428       PetscCall((*ctx->physics.characteristic)(ctx->physics.user, dof, &x[i * dof], ctx->R, ctx->Rinv, ctx->speeds, ctx->xmin + hx * i));
429       PetscCall(PetscArrayzero(ctx->cjmpLR, 2 * dof));
430       cjmpL = &ctx->cjmpLR[0];
431       cjmpR = &ctx->cjmpLR[dof];
432       for (j = 0; j < dof; j++) {
433         PetscScalar jmpL, jmpR;
434         jmpL = x[(i + 0) * dof + j] - x[(i - 1) * dof + j];
435         jmpR = x[(i + 1) * dof + j] - x[(i + 0) * dof + j];
436         for (k = 0; k < dof; k++) {
437           cjmpL[k] += ctx->Rinv[k + j * dof] * jmpL;
438           cjmpR[k] += ctx->Rinv[k + j * dof] * jmpR;
439         }
440       }
441       /* Apply limiter to the left and right characteristic jumps */
442       info.m  = dof;
443       info.hx = hx;
444       (*ctx->limit)(&info, cjmpL, cjmpR, ctx->cslope);
445       for (j = 0; j < dof; j++) ctx->cslope[j] /= hx; /* rescale to a slope */
446       for (j = 0; j < dof; j++) {
447         PetscScalar tmp = 0;
448         for (k = 0; k < dof; k++) tmp += ctx->R[j + k * dof] * ctx->cslope[k];
449         slope[i * dof + j] = tmp;
450       }
451     }
452   }
453 
454   for (i = xs; i < xs + xm + 1; i++) {
455     PetscReal    maxspeed;
456     PetscScalar *uL, *uR;
457     if (i < len_slow1 + len_slow2 && i > len_slow1) { /* slow parts can be changed based on a */
458       uL = &ctx->uLR[0];
459       uR = &ctx->uLR[dof];
460       for (j = 0; j < dof; j++) {
461         uL[j] = x[(i - 1) * dof + j] + slope[(i - 1) * dof + j] * hx / 2;
462         uR[j] = x[(i - 0) * dof + j] - slope[(i - 0) * dof + j] * hx / 2;
463       }
464       PetscCall((*ctx->physics.riemann)(ctx->physics.user, dof, uL, uR, ctx->flux, &maxspeed, ctx->xmin + hx * i, ctx->xmin, ctx->xmax));
465       cfl_idt = PetscMax(cfl_idt, PetscAbsScalar(maxspeed / hx)); /* Max allowable value of 1/Delta t */
466       if (i > xs) {
467         for (j = 0; j < dof; j++) f[(i - len_slow1 - 1) * dof + j] -= ctx->flux[j] / hx;
468       }
469       if (i < xs + xm) {
470         for (j = 0; j < dof; j++) f[(i - len_slow1) * dof + j] += ctx->flux[j] / hx;
471       }
472     }
473     if (i == len_slow1 + len_slow2) { /* slow parts can be changed based on a */
474       uL = &ctx->uLR[0];
475       uR = &ctx->uLR[dof];
476       for (j = 0; j < dof; j++) {
477         uL[j] = x[(i - 1) * dof + j] + slope[(i - 1) * dof + j] * hx / 2;
478         uR[j] = x[(i - 0) * dof + j] - slope[(i - 0) * dof + j] * hx / 2;
479       }
480       PetscCall((*ctx->physics.riemann)(ctx->physics.user, dof, uL, uR, ctx->flux, &maxspeed, ctx->xmin + hx * i, ctx->xmin, ctx->xmax));
481       cfl_idt = PetscMax(cfl_idt, PetscAbsScalar(maxspeed / hx)); /* Max allowable value of 1/Delta t */
482       if (i > xs) {
483         for (j = 0; j < dof; j++) f[(i - len_slow1 - 1) * dof + j] -= ctx->flux[j] / hx;
484       }
485     }
486     if (i == len_slow1) { /* slow parts can be changed based on a */
487       uL = &ctx->uLR[0];
488       uR = &ctx->uLR[dof];
489       for (j = 0; j < dof; j++) {
490         uL[j] = x[(i - 1) * dof + j] + slope[(i - 1) * dof + j] * hx / 2;
491         uR[j] = x[(i - 0) * dof + j] - slope[(i - 0) * dof + j] * hx / 2;
492       }
493       PetscCall((*ctx->physics.riemann)(ctx->physics.user, dof, uL, uR, ctx->flux, &maxspeed, ctx->xmin + hx * i, ctx->xmin, ctx->xmax));
494       cfl_idt = PetscMax(cfl_idt, PetscAbsScalar(maxspeed / hx)); /* Max allowable value of 1/Delta t */
495       if (i < xs + xm) {
496         for (j = 0; j < dof; j++) f[(i - len_slow1) * dof + j] += ctx->flux[j] / hx;
497       }
498     }
499   }
500 
501   PetscCall(DMDAVecRestoreArray(da, Xloc, &x));
502   PetscCall(VecRestoreArray(F, &f));
503   PetscCall(DMDARestoreArray(da, PETSC_TRUE, &slope));
504   PetscCall(DMRestoreLocalVector(da, &Xloc));
505   PetscFunctionReturn(PETSC_SUCCESS);
506 }
507 
508 PetscErrorCode FVRHSFunctionfast2(TS ts, PetscReal time, Vec X, Vec F, void *vctx)
509 {
510   FVCtx       *ctx = (FVCtx *)vctx;
511   PetscInt     i, j, k, Mx, dof, xs, xm, len_slow1, len_slow2;
512   PetscReal    hx, cfl_idt = 0;
513   PetscScalar *x, *f, *slope;
514   Vec          Xloc;
515   DM           da;
516 
517   PetscFunctionBeginUser;
518   PetscCall(TSGetDM(ts, &da));
519   PetscCall(DMGetLocalVector(da, &Xloc));
520   PetscCall(DMDAGetInfo(da, 0, &Mx, 0, 0, 0, 0, 0, &dof, 0, 0, 0, 0, 0));
521   hx = (ctx->xmax - ctx->xmin) / Mx;
522   PetscCall(DMGlobalToLocalBegin(da, X, INSERT_VALUES, Xloc));
523   PetscCall(DMGlobalToLocalEnd(da, X, INSERT_VALUES, Xloc));
524   PetscCall(VecZeroEntries(F));
525   PetscCall(DMDAVecGetArray(da, Xloc, &x));
526   PetscCall(VecGetArray(F, &f));
527   PetscCall(DMDAGetArray(da, PETSC_TRUE, &slope));
528   PetscCall(DMDAGetCorners(da, &xs, 0, 0, &xm, 0, 0));
529   PetscCall(ISGetSize(ctx->iss, &len_slow1));
530   PetscCall(ISGetSize(ctx->iss2, &len_slow2));
531 
532   if (ctx->bctype == FVBC_OUTFLOW) {
533     for (i = xs - 2; i < 0; i++) {
534       for (j = 0; j < dof; j++) x[i * dof + j] = x[j];
535     }
536     for (i = Mx; i < xs + xm + 2; i++) {
537       for (j = 0; j < dof; j++) x[i * dof + j] = x[(xs + xm - 1) * dof + j];
538     }
539   }
540   for (i = xs - 1; i < xs + xm + 1; i++) {
541     struct _LimitInfo info;
542     PetscScalar      *cjmpL, *cjmpR;
543     if (i > len_slow1 + len_slow2 - 2) {
544       PetscCall((*ctx->physics.characteristic)(ctx->physics.user, dof, &x[i * dof], ctx->R, ctx->Rinv, ctx->speeds, ctx->xmin + hx * i));
545       PetscCall(PetscArrayzero(ctx->cjmpLR, 2 * dof));
546       cjmpL = &ctx->cjmpLR[0];
547       cjmpR = &ctx->cjmpLR[dof];
548       for (j = 0; j < dof; j++) {
549         PetscScalar jmpL, jmpR;
550         jmpL = x[(i + 0) * dof + j] - x[(i - 1) * dof + j];
551         jmpR = x[(i + 1) * dof + j] - x[(i + 0) * dof + j];
552         for (k = 0; k < dof; k++) {
553           cjmpL[k] += ctx->Rinv[k + j * dof] * jmpL;
554           cjmpR[k] += ctx->Rinv[k + j * dof] * jmpR;
555         }
556       }
557       /* Apply limiter to the left and right characteristic jumps */
558       info.m  = dof;
559       info.hx = hx;
560       (*ctx->limit)(&info, cjmpL, cjmpR, ctx->cslope);
561       for (j = 0; j < dof; j++) ctx->cslope[j] /= hx; /* rescale to a slope */
562       for (j = 0; j < dof; j++) {
563         PetscScalar tmp = 0;
564         for (k = 0; k < dof; k++) tmp += ctx->R[j + k * dof] * ctx->cslope[k];
565         slope[i * dof + j] = tmp;
566       }
567     }
568   }
569 
570   for (i = xs; i < xs + xm + 1; i++) {
571     PetscReal    maxspeed;
572     PetscScalar *uL, *uR;
573     if (i > len_slow1 + len_slow2) {
574       uL = &ctx->uLR[0];
575       uR = &ctx->uLR[dof];
576       for (j = 0; j < dof; j++) {
577         uL[j] = x[(i - 1) * dof + j] + slope[(i - 1) * dof + j] * hx / 2;
578         uR[j] = x[(i - 0) * dof + j] - slope[(i - 0) * dof + j] * hx / 2;
579       }
580       PetscCall((*ctx->physics.riemann)(ctx->physics.user, dof, uL, uR, ctx->flux, &maxspeed, ctx->xmin + hx * i, ctx->xmin, ctx->xmax));
581       cfl_idt = PetscMax(cfl_idt, PetscAbsScalar(maxspeed / hx)); /* Max allowable value of 1/Delta t */
582       if (i > xs) {
583         for (j = 0; j < dof; j++) f[(i - len_slow1 - len_slow2 - 1) * dof + j] -= ctx->flux[j] / hx;
584       }
585       if (i < xs + xm) {
586         for (j = 0; j < dof; j++) f[(i - len_slow1 - len_slow2) * dof + j] += ctx->flux[j] / hx;
587       }
588     }
589     if (i == len_slow1 + len_slow2) {
590       uL = &ctx->uLR[0];
591       uR = &ctx->uLR[dof];
592       for (j = 0; j < dof; j++) {
593         uL[j] = x[(i - 1) * dof + j] + slope[(i - 1) * dof + j] * hx / 2;
594         uR[j] = x[(i - 0) * dof + j] - slope[(i - 0) * dof + j] * hx / 2;
595       }
596       PetscCall((*ctx->physics.riemann)(ctx->physics.user, dof, uL, uR, ctx->flux, &maxspeed, ctx->xmin + hx * i, ctx->xmin, ctx->xmax));
597       cfl_idt = PetscMax(cfl_idt, PetscAbsScalar(maxspeed / hx)); /* Max allowable value of 1/Delta t */
598       if (i < xs + xm) {
599         for (j = 0; j < dof; j++) f[(i - len_slow1 - len_slow2) * dof + j] += ctx->flux[j] / hx;
600       }
601     }
602   }
603   PetscCall(DMDAVecRestoreArray(da, Xloc, &x));
604   PetscCall(VecRestoreArray(F, &f));
605   PetscCall(DMDARestoreArray(da, PETSC_TRUE, &slope));
606   PetscCall(DMRestoreLocalVector(da, &Xloc));
607   PetscFunctionReturn(PETSC_SUCCESS);
608 }
609 
610 int main(int argc, char *argv[])
611 {
612   char              lname[256] = "mc", physname[256] = "advect", final_fname[256] = "solution.m";
613   PetscFunctionList limiters = 0, physics = 0;
614   MPI_Comm          comm;
615   TS                ts;
616   DM                da;
617   Vec               X, X0, R;
618   FVCtx             ctx;
619   PetscInt          i, k, dof, xs, xm, Mx, draw = 0, *index_slow, *index_fast, islow = 0, ifast = 0;
620   PetscBool         view_final = PETSC_FALSE;
621   PetscReal         ptime;
622 
623   PetscFunctionBeginUser;
624   PetscCall(PetscInitialize(&argc, &argv, 0, help));
625   comm = PETSC_COMM_WORLD;
626   PetscCall(PetscMemzero(&ctx, sizeof(ctx)));
627 
628   /* Register limiters to be available on the command line */
629   PetscCall(PetscFunctionListAdd(&limiters, "upwind", Limit_Upwind));
630   PetscCall(PetscFunctionListAdd(&limiters, "lax-wendroff", Limit_LaxWendroff));
631   PetscCall(PetscFunctionListAdd(&limiters, "beam-warming", Limit_BeamWarming));
632   PetscCall(PetscFunctionListAdd(&limiters, "fromm", Limit_Fromm));
633   PetscCall(PetscFunctionListAdd(&limiters, "minmod", Limit_Minmod));
634   PetscCall(PetscFunctionListAdd(&limiters, "superbee", Limit_Superbee));
635   PetscCall(PetscFunctionListAdd(&limiters, "mc", Limit_MC));
636   PetscCall(PetscFunctionListAdd(&limiters, "vanleer", Limit_VanLeer));
637   PetscCall(PetscFunctionListAdd(&limiters, "vanalbada", Limit_VanAlbada));
638   PetscCall(PetscFunctionListAdd(&limiters, "vanalbadatvd", Limit_VanAlbadaTVD));
639   PetscCall(PetscFunctionListAdd(&limiters, "koren", Limit_Koren));
640   PetscCall(PetscFunctionListAdd(&limiters, "korensym", Limit_KorenSym));
641   PetscCall(PetscFunctionListAdd(&limiters, "koren3", Limit_Koren3));
642   PetscCall(PetscFunctionListAdd(&limiters, "cada-torrilhon2", Limit_CadaTorrilhon2));
643   PetscCall(PetscFunctionListAdd(&limiters, "cada-torrilhon3-r0p1", Limit_CadaTorrilhon3R0p1));
644   PetscCall(PetscFunctionListAdd(&limiters, "cada-torrilhon3-r1", Limit_CadaTorrilhon3R1));
645   PetscCall(PetscFunctionListAdd(&limiters, "cada-torrilhon3-r10", Limit_CadaTorrilhon3R10));
646   PetscCall(PetscFunctionListAdd(&limiters, "cada-torrilhon3-r100", Limit_CadaTorrilhon3R100));
647 
648   /* Register physical models to be available on the command line */
649   PetscCall(PetscFunctionListAdd(&physics, "advect", PhysicsCreate_Advect));
650 
651   ctx.comm   = comm;
652   ctx.cfl    = 0.9;
653   ctx.bctype = FVBC_PERIODIC;
654   ctx.xmin   = -1.0;
655   ctx.xmax   = 1.0;
656   PetscOptionsBegin(comm, NULL, "Finite Volume solver options", "");
657   PetscCall(PetscOptionsReal("-xmin", "X min", "", ctx.xmin, &ctx.xmin, NULL));
658   PetscCall(PetscOptionsReal("-xmax", "X max", "", ctx.xmax, &ctx.xmax, NULL));
659   PetscCall(PetscOptionsFList("-limit", "Name of flux limiter to use", "", limiters, lname, lname, sizeof(lname), NULL));
660   PetscCall(PetscOptionsInt("-draw", "Draw solution vector, bitwise OR of (1=initial,2=final,4=final error)", "", draw, &draw, NULL));
661   PetscCall(PetscOptionsString("-view_final", "Write final solution in ASCII MATLAB format to given file name", "", final_fname, final_fname, sizeof(final_fname), &view_final));
662   PetscCall(PetscOptionsInt("-initial", "Initial condition (depends on the physics)", "", ctx.initial, &ctx.initial, NULL));
663   PetscCall(PetscOptionsBool("-simulation", "Compare errors with reference solution", "", ctx.simulation, &ctx.simulation, NULL));
664   PetscCall(PetscOptionsReal("-cfl", "CFL number to time step at", "", ctx.cfl, &ctx.cfl, NULL));
665   PetscCall(PetscOptionsEnum("-bc_type", "Boundary condition", "", FVBCTypes, (PetscEnum)ctx.bctype, (PetscEnum *)&ctx.bctype, NULL));
666   PetscCall(PetscOptionsBool("-recursive_split", "Split the domain recursively", "", ctx.recursive, &ctx.recursive, NULL));
667   PetscOptionsEnd();
668 
669   /* Choose the limiter from the list of registered limiters */
670   PetscCall(PetscFunctionListFind(limiters, lname, &ctx.limit));
671   PetscCheck(ctx.limit, PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Limiter '%s' not found", lname);
672 
673   /* Choose the physics from the list of registered models */
674   {
675     PetscErrorCode (*r)(FVCtx *);
676     PetscCall(PetscFunctionListFind(physics, physname, &r));
677     PetscCheck(r, PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Physics '%s' not found", physname);
678     /* Create the physics, will set the number of fields and their names */
679     PetscCall((*r)(&ctx));
680   }
681 
682   /* Create a DMDA to manage the parallel grid */
683   PetscCall(DMDACreate1d(comm, DM_BOUNDARY_PERIODIC, 50, ctx.physics.dof, 2, NULL, &da));
684   PetscCall(DMSetFromOptions(da));
685   PetscCall(DMSetUp(da));
686   /* Inform the DMDA of the field names provided by the physics. */
687   /* The names will be shown in the title bars when run with -ts_monitor_draw_solution */
688   for (i = 0; i < ctx.physics.dof; i++) PetscCall(DMDASetFieldName(da, i, ctx.physics.fieldname[i]));
689   PetscCall(DMDAGetInfo(da, 0, &Mx, 0, 0, 0, 0, 0, &dof, 0, 0, 0, 0, 0));
690   PetscCall(DMDAGetCorners(da, &xs, 0, 0, &xm, 0, 0));
691 
692   /* Set coordinates of cell centers */
693   PetscCall(DMDASetUniformCoordinates(da, ctx.xmin + 0.5 * (ctx.xmax - ctx.xmin) / Mx, ctx.xmax + 0.5 * (ctx.xmax - ctx.xmin) / Mx, 0, 0, 0, 0));
694 
695   /* Allocate work space for the Finite Volume solver (so it doesn't have to be reallocated on each function evaluation) */
696   PetscCall(PetscMalloc4(dof * dof, &ctx.R, dof * dof, &ctx.Rinv, 2 * dof, &ctx.cjmpLR, 1 * dof, &ctx.cslope));
697   PetscCall(PetscMalloc3(2 * dof, &ctx.uLR, dof, &ctx.flux, dof, &ctx.speeds));
698 
699   /* Create a vector to store the solution and to save the initial state */
700   PetscCall(DMCreateGlobalVector(da, &X));
701   PetscCall(VecDuplicate(X, &X0));
702   PetscCall(VecDuplicate(X, &R));
703 
704   /*-------------------------------- create index for slow parts and fast parts ----------------------------------------*/
705   PetscCall(PetscMalloc1(xm * dof, &index_slow));
706   PetscCall(PetscMalloc1(xm * dof, &index_fast));
707   for (i = xs; i < xs + xm; i++) {
708     if (ctx.xmin + i * (ctx.xmax - ctx.xmin) / (PetscReal)Mx + 0.5 * (ctx.xmax - ctx.xmin) / (PetscReal)Mx < 0)
709       for (k = 0; k < dof; k++) index_slow[islow++] = i * dof + k;
710     else
711       for (k = 0; k < dof; k++) index_fast[ifast++] = i * dof + k;
712   } /* this step need to be changed based on discontinuous point of a */
713   PetscCall(ISCreateGeneral(PETSC_COMM_WORLD, islow, index_slow, PETSC_COPY_VALUES, &ctx.iss));
714   PetscCall(ISCreateGeneral(PETSC_COMM_WORLD, ifast, index_fast, PETSC_COPY_VALUES, &ctx.isf));
715 
716   /* Create a time-stepping object */
717   PetscCall(TSCreate(comm, &ts));
718   PetscCall(TSSetDM(ts, da));
719   PetscCall(TSSetRHSFunction(ts, R, FVRHSFunction, &ctx));
720   PetscCall(TSRHSSplitSetIS(ts, "slow", ctx.iss));
721   PetscCall(TSRHSSplitSetIS(ts, "fast", ctx.isf));
722   PetscCall(TSRHSSplitSetRHSFunction(ts, "slow", NULL, FVRHSFunctionslow, &ctx));
723   PetscCall(TSRHSSplitSetRHSFunction(ts, "fast", NULL, FVRHSFunctionfast, &ctx));
724 
725   if (ctx.recursive) {
726     TS subts;
727     islow = 0;
728     ifast = 0;
729     for (i = xs; i < xs + xm; i++) {
730       PetscReal coord = ctx.xmin + i * (ctx.xmax - ctx.xmin) / (PetscReal)Mx + 0.5 * (ctx.xmax - ctx.xmin) / (PetscReal)Mx;
731       if (coord >= 0 && coord < ctx.xmin + (ctx.xmax - ctx.xmin) * 3 / 4.)
732         for (k = 0; k < dof; k++) index_slow[islow++] = i * dof + k;
733       if (coord >= ctx.xmin + (ctx.xmax - ctx.xmin) * 3 / 4.)
734         for (k = 0; k < dof; k++) index_fast[ifast++] = i * dof + k;
735     } /* this step need to be changed based on discontinuous point of a */
736     PetscCall(ISCreateGeneral(PETSC_COMM_WORLD, islow, index_slow, PETSC_COPY_VALUES, &ctx.iss2));
737     PetscCall(ISCreateGeneral(PETSC_COMM_WORLD, ifast, index_fast, PETSC_COPY_VALUES, &ctx.isf2));
738 
739     PetscCall(TSRHSSplitGetSubTS(ts, "fast", &subts));
740     PetscCall(TSRHSSplitSetIS(subts, "slow", ctx.iss2));
741     PetscCall(TSRHSSplitSetIS(subts, "fast", ctx.isf2));
742     PetscCall(TSRHSSplitSetRHSFunction(subts, "slow", NULL, FVRHSFunctionslow2, &ctx));
743     PetscCall(TSRHSSplitSetRHSFunction(subts, "fast", NULL, FVRHSFunctionfast2, &ctx));
744   }
745 
746   /*PetscCall(TSSetType(ts,TSSSP));*/
747   PetscCall(TSSetType(ts, TSMPRK));
748   PetscCall(TSSetMaxTime(ts, 10));
749   PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_STEPOVER));
750 
751   /* Compute initial conditions and starting time step */
752   PetscCall(FVSample(&ctx, da, 0, X0));
753   PetscCall(FVRHSFunction(ts, 0, X0, X, (void *)&ctx)); /* Initial function evaluation, only used to determine max speed */
754   PetscCall(VecCopy(X0, X));                            /* The function value was not used so we set X=X0 again */
755   PetscCall(TSSetTimeStep(ts, ctx.cfl / ctx.cfl_idt));
756   PetscCall(TSSetFromOptions(ts)); /* Take runtime options */
757   PetscCall(SolutionStatsView(da, X, PETSC_VIEWER_STDOUT_WORLD));
758   {
759     PetscInt    steps;
760     PetscScalar mass_initial, mass_final, mass_difference;
761 
762     PetscCall(TSSolve(ts, X));
763     PetscCall(TSGetSolveTime(ts, &ptime));
764     PetscCall(TSGetStepNumber(ts, &steps));
765     PetscCall(PetscPrintf(comm, "Final time %g, steps %" PetscInt_FMT "\n", (double)ptime, steps));
766     /* calculate the total mass at initial time and final time */
767     mass_initial = 0.0;
768     mass_final   = 0.0;
769     PetscCall(VecSum(X0, &mass_initial));
770     PetscCall(VecSum(X, &mass_final));
771     mass_difference = (ctx.xmax - ctx.xmin) / (PetscScalar)Mx * (mass_final - mass_initial);
772     PetscCall(PetscPrintf(comm, "Mass difference %g\n", (double)mass_difference));
773     if (ctx.simulation) {
774       PetscViewer fd;
775       char        filename[PETSC_MAX_PATH_LEN] = "binaryoutput";
776       Vec         XR;
777       PetscReal   nrm1, nrmsup;
778       PetscBool   flg;
779 
780       PetscCall(PetscOptionsGetString(NULL, NULL, "-f", filename, sizeof(filename), &flg));
781       PetscCheck(flg, PETSC_COMM_WORLD, PETSC_ERR_USER, "Must indicate binary file with the -f option");
782       PetscCall(PetscViewerBinaryOpen(PETSC_COMM_WORLD, filename, FILE_MODE_READ, &fd));
783       PetscCall(VecDuplicate(X0, &XR));
784       PetscCall(VecLoad(XR, fd));
785       PetscCall(PetscViewerDestroy(&fd));
786       PetscCall(VecAYPX(XR, -1, X));
787       PetscCall(VecNorm(XR, NORM_1, &nrm1));
788       PetscCall(VecNorm(XR, NORM_INFINITY, &nrmsup));
789       nrm1 /= Mx;
790       PetscCall(PetscPrintf(comm, "Error ||x-x_e||_1 %g  ||x-x_e||_sup %g\n", (double)nrm1, (double)nrmsup));
791       PetscCall(VecDestroy(&XR));
792     }
793   }
794 
795   PetscCall(SolutionStatsView(da, X, PETSC_VIEWER_STDOUT_WORLD));
796   if (draw & 0x1) PetscCall(VecView(X0, PETSC_VIEWER_DRAW_WORLD));
797   if (draw & 0x2) PetscCall(VecView(X, PETSC_VIEWER_DRAW_WORLD));
798   if (draw & 0x4) {
799     Vec Y;
800     PetscCall(VecDuplicate(X, &Y));
801     PetscCall(FVSample(&ctx, da, ptime, Y));
802     PetscCall(VecAYPX(Y, -1, X));
803     PetscCall(VecView(Y, PETSC_VIEWER_DRAW_WORLD));
804     PetscCall(VecDestroy(&Y));
805   }
806 
807   if (view_final) {
808     PetscViewer viewer;
809     PetscCall(PetscViewerASCIIOpen(PETSC_COMM_WORLD, final_fname, &viewer));
810     PetscCall(PetscViewerPushFormat(viewer, PETSC_VIEWER_ASCII_MATLAB));
811     PetscCall(VecView(X, viewer));
812     PetscCall(PetscViewerPopFormat(viewer));
813     PetscCall(PetscViewerDestroy(&viewer));
814   }
815 
816   /* Clean up */
817   PetscCall((*ctx.physics.destroy)(ctx.physics.user));
818   for (i = 0; i < ctx.physics.dof; i++) PetscCall(PetscFree(ctx.physics.fieldname[i]));
819   PetscCall(PetscFree4(ctx.R, ctx.Rinv, ctx.cjmpLR, ctx.cslope));
820   PetscCall(PetscFree3(ctx.uLR, ctx.flux, ctx.speeds));
821   PetscCall(ISDestroy(&ctx.iss));
822   PetscCall(ISDestroy(&ctx.isf));
823   PetscCall(ISDestroy(&ctx.iss2));
824   PetscCall(ISDestroy(&ctx.isf2));
825   PetscCall(VecDestroy(&X));
826   PetscCall(VecDestroy(&X0));
827   PetscCall(VecDestroy(&R));
828   PetscCall(DMDestroy(&da));
829   PetscCall(TSDestroy(&ts));
830   PetscCall(PetscFree(index_slow));
831   PetscCall(PetscFree(index_fast));
832   PetscCall(PetscFunctionListDestroy(&limiters));
833   PetscCall(PetscFunctionListDestroy(&physics));
834   PetscCall(PetscFinalize());
835   return 0;
836 }
837 
838 /*TEST
839     build:
840       requires: !complex
841       depends: finitevolume1d.c
842 
843     test:
844       args: -da_grid_x 60 -initial 1 -xmin -1 -xmax 1 -limit mc -physics_advect_a1 1 -physics_advect_a2 2 -ts_time_step 0.025 -ts_max_steps 24 -ts_type rk -ts_rk_type 2a -ts_rk_dtratio 2 -ts_rk_multirate -ts_use_splitrhsfunction 0
845 
846     test:
847       suffix: 2
848       args: -da_grid_x 60 -initial 1 -xmin -1 -xmax 1 -limit mc -physics_advect_a1 1 -physics_advect_a2 2 -ts_time_step 0.025 -ts_max_steps 24 -ts_type rk -ts_rk_type 2a -ts_rk_dtratio 2 -ts_rk_multirate -ts_use_splitrhsfunction 1
849       output_file: output/ex5_1.out
850 
851     test:
852       suffix: 3
853       args: -da_grid_x 60 -initial 1 -xmin -1 -xmax 1 -limit mc -physics_advect_a1 1 -physics_advect_a2 2 -ts_time_step 0.025 -ts_max_steps 24 -ts_type mprk -ts_mprk_type 2a22 -ts_use_splitrhsfunction 0
854 
855     test:
856       suffix: 4
857       args: -da_grid_x 60 -initial 1 -xmin -1 -xmax 1 -limit mc -physics_advect_a1 1 -physics_advect_a2 2 -ts_time_step 0.025 -ts_max_steps 24 -ts_type mprk -ts_mprk_type 2a22 -ts_use_splitrhsfunction 1
858       output_file: output/ex5_3.out
859 
860     test:
861       suffix: 5
862       args: -da_grid_x 60 -initial 1 -xmin -1 -xmax 1 -limit mc -physics_advect_a1 1 -physics_advect_a2 2 -ts_time_step 0.025 -ts_max_steps 24 -ts_type rk -ts_rk_type 2a -ts_rk_dtratio 2 -ts_rk_multirate -ts_use_splitrhsfunction 0 -recursive_split
863 
864     test:
865       suffix: 6
866       args: -da_grid_x 60 -initial 1 -xmin -1 -xmax 1 -limit mc -physics_advect_a1 1 -physics_advect_a2 2 -ts_time_step 0.025 -ts_max_steps 24 -ts_type rk -ts_rk_type 2a -ts_rk_dtratio 2 -ts_rk_multirate -ts_use_splitrhsfunction 1 -recursive_split
867 TEST*/
868