xref: /petsc/src/ts/tutorials/ex50.c (revision 8847d985fe1fdba95409f30cbb25abc5b8faf780)
1 static char help[] = "Solves one dimensional Burger's equation compares with exact solution\n\n";
2 
3 /*
4 
5     Not yet tested in parallel
6 
7 */
8 
9 /* ------------------------------------------------------------------------
10 
11    This program uses the one-dimensional Burger's equation
12        u_t = mu*u_xx - u u_x,
13    on the domain 0 <= x <= 1, with periodic boundary conditions
14 
15    The operators are discretized with the spectral element method
16 
17    See the paper PDE-CONSTRAINED OPTIMIZATION WITH SPECTRAL ELEMENTS USING PETSC AND TAO
18    by OANA MARIN, EMIL CONSTANTINESCU, AND BARRY SMITH for details on the exact solution
19    used
20 
21    See src/tao/unconstrained/tutorials/burgers_spectral.c
22 
23   ------------------------------------------------------------------------- */
24 
25 #include <petscts.h>
26 #include <petscdt.h>
27 #include <petscdraw.h>
28 #include <petscdmda.h>
29 
30 /*
31    User-defined application context - contains data needed by the
32    application-provided call-back routines.
33 */
34 
35 typedef struct {
36   PetscInt   n;       /* number of nodes */
37   PetscReal *nodes;   /* GLL nodes */
38   PetscReal *weights; /* GLL weights */
39 } PetscGLL;
40 
41 typedef struct {
42   PetscInt  N;               /* grid points per elements*/
43   PetscInt  E;               /* number of elements */
44   PetscReal tol_L2, tol_max; /* error norms */
45   PetscInt  steps;           /* number of timesteps */
46   PetscReal Tend;            /* endtime */
47   PetscReal mu;              /* viscosity */
48   PetscReal L;               /* total length of domain */
49   PetscReal Le;
50   PetscReal Tadj;
51 } PetscParam;
52 
53 typedef struct {
54   Vec grid; /* total grid */
55   Vec curr_sol;
56 } PetscData;
57 
58 typedef struct {
59   Vec      grid;  /* total grid */
60   Vec      mass;  /* mass matrix for total integration */
61   Mat      stiff; /* stifness matrix */
62   Mat      keptstiff;
63   Mat      grad;
64   PetscGLL gll;
65 } PetscSEMOperators;
66 
67 typedef struct {
68   DM                da; /* distributed array data structure */
69   PetscSEMOperators SEMop;
70   PetscParam        param;
71   PetscData         dat;
72   TS                ts;
73   PetscReal         initial_dt;
74 } AppCtx;
75 
76 /*
77    User-defined routines
78 */
79 extern PetscErrorCode RHSMatrixLaplaciangllDM(TS, PetscReal, Vec, Mat, Mat, void *);
80 extern PetscErrorCode RHSMatrixAdvectiongllDM(TS, PetscReal, Vec, Mat, Mat, void *);
81 extern PetscErrorCode TrueSolution(TS, PetscReal, Vec, AppCtx *);
82 extern PetscErrorCode RHSFunction(TS, PetscReal, Vec, Vec, void *);
83 extern PetscErrorCode RHSJacobian(TS, PetscReal, Vec, Mat, Mat, void *);
84 
85 int main(int argc, char **argv)
86 {
87   AppCtx       appctx; /* user-defined application context */
88   PetscInt     i, xs, xm, ind, j, lenglob;
89   PetscReal    x, *wrk_ptr1, *wrk_ptr2;
90   MatNullSpace nsp;
91   PetscMPIInt  size;
92 
93   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
94      Initialize program and set problem parameters
95      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
96   PetscFunctionBeginUser;
97 
98   PetscFunctionBeginUser;
99   PetscCall(PetscInitialize(&argc, &argv, (char *)0, help));
100 
101   /*initialize parameters */
102   appctx.param.N     = 10;   /* order of the spectral element */
103   appctx.param.E     = 10;   /* number of elements */
104   appctx.param.L     = 4.0;  /* length of the domain */
105   appctx.param.mu    = 0.01; /* diffusion coefficient */
106   appctx.initial_dt  = 5e-3;
107   appctx.param.steps = PETSC_MAX_INT;
108   appctx.param.Tend  = 4;
109 
110   PetscCall(PetscOptionsGetInt(NULL, NULL, "-N", &appctx.param.N, NULL));
111   PetscCall(PetscOptionsGetInt(NULL, NULL, "-E", &appctx.param.E, NULL));
112   PetscCall(PetscOptionsGetReal(NULL, NULL, "-Tend", &appctx.param.Tend, NULL));
113   PetscCall(PetscOptionsGetReal(NULL, NULL, "-mu", &appctx.param.mu, NULL));
114   appctx.param.Le = appctx.param.L / appctx.param.E;
115 
116   PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
117   PetscCheck((appctx.param.E % size) == 0, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "Number of elements must be divisible by number of processes");
118 
119   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
120      Create GLL data structures
121      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
122   PetscCall(PetscMalloc2(appctx.param.N, &appctx.SEMop.gll.nodes, appctx.param.N, &appctx.SEMop.gll.weights));
123   PetscCall(PetscDTGaussLobattoLegendreQuadrature(appctx.param.N, PETSCGAUSSLOBATTOLEGENDRE_VIA_LINEAR_ALGEBRA, appctx.SEMop.gll.nodes, appctx.SEMop.gll.weights));
124   appctx.SEMop.gll.n = appctx.param.N;
125   lenglob            = appctx.param.E * (appctx.param.N - 1);
126 
127   /*
128      Create distributed array (DMDA) to manage parallel grid and vectors
129      and to set up the ghost point communication pattern.  There are E*(Nl-1)+1
130      total grid values spread equally among all the processors, except first and last
131   */
132 
133   PetscCall(DMDACreate1d(PETSC_COMM_WORLD, DM_BOUNDARY_PERIODIC, lenglob, 1, 1, NULL, &appctx.da));
134   PetscCall(DMSetFromOptions(appctx.da));
135   PetscCall(DMSetUp(appctx.da));
136 
137   /*
138      Extract global and local vectors from DMDA; we use these to store the
139      approximate solution.  Then duplicate these for remaining vectors that
140      have the same types.
141   */
142 
143   PetscCall(DMCreateGlobalVector(appctx.da, &appctx.dat.curr_sol));
144   PetscCall(VecDuplicate(appctx.dat.curr_sol, &appctx.SEMop.grid));
145   PetscCall(VecDuplicate(appctx.dat.curr_sol, &appctx.SEMop.mass));
146 
147   PetscCall(DMDAGetCorners(appctx.da, &xs, NULL, NULL, &xm, NULL, NULL));
148   PetscCall(DMDAVecGetArray(appctx.da, appctx.SEMop.grid, &wrk_ptr1));
149   PetscCall(DMDAVecGetArray(appctx.da, appctx.SEMop.mass, &wrk_ptr2));
150 
151   /* Compute function over the locally owned part of the grid */
152 
153   xs = xs / (appctx.param.N - 1);
154   xm = xm / (appctx.param.N - 1);
155 
156   /*
157      Build total grid and mass over entire mesh (multi-elemental)
158   */
159 
160   for (i = xs; i < xs + xm; i++) {
161     for (j = 0; j < appctx.param.N - 1; j++) {
162       x             = (appctx.param.Le / 2.0) * (appctx.SEMop.gll.nodes[j] + 1.0) + appctx.param.Le * i;
163       ind           = i * (appctx.param.N - 1) + j;
164       wrk_ptr1[ind] = x;
165       wrk_ptr2[ind] = .5 * appctx.param.Le * appctx.SEMop.gll.weights[j];
166       if (j == 0) wrk_ptr2[ind] += .5 * appctx.param.Le * appctx.SEMop.gll.weights[j];
167     }
168   }
169   PetscCall(DMDAVecRestoreArray(appctx.da, appctx.SEMop.grid, &wrk_ptr1));
170   PetscCall(DMDAVecRestoreArray(appctx.da, appctx.SEMop.mass, &wrk_ptr2));
171 
172   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
173    Create matrix data structure; set matrix evaluation routine.
174    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
175   PetscCall(DMSetMatrixPreallocateOnly(appctx.da, PETSC_TRUE));
176   PetscCall(DMCreateMatrix(appctx.da, &appctx.SEMop.stiff));
177   PetscCall(DMCreateMatrix(appctx.da, &appctx.SEMop.grad));
178   /*
179    For linear problems with a time-dependent f(u,t) in the equation
180    u_t = f(u,t), the user provides the discretized right-hand-side
181    as a time-dependent matrix.
182    */
183   PetscCall(RHSMatrixLaplaciangllDM(appctx.ts, 0.0, appctx.dat.curr_sol, appctx.SEMop.stiff, appctx.SEMop.stiff, &appctx));
184   PetscCall(RHSMatrixAdvectiongllDM(appctx.ts, 0.0, appctx.dat.curr_sol, appctx.SEMop.grad, appctx.SEMop.grad, &appctx));
185   /*
186        For linear problems with a time-dependent f(u,t) in the equation
187        u_t = f(u,t), the user provides the discretized right-hand-side
188        as a time-dependent matrix.
189     */
190 
191   PetscCall(MatDuplicate(appctx.SEMop.stiff, MAT_COPY_VALUES, &appctx.SEMop.keptstiff));
192 
193   /* attach the null space to the matrix, this probably is not needed but does no harm */
194   PetscCall(MatNullSpaceCreate(PETSC_COMM_WORLD, PETSC_TRUE, 0, NULL, &nsp));
195   PetscCall(MatSetNullSpace(appctx.SEMop.stiff, nsp));
196   PetscCall(MatSetNullSpace(appctx.SEMop.keptstiff, nsp));
197   PetscCall(MatNullSpaceTest(nsp, appctx.SEMop.stiff, NULL));
198   PetscCall(MatNullSpaceDestroy(&nsp));
199   /* attach the null space to the matrix, this probably is not needed but does no harm */
200   PetscCall(MatNullSpaceCreate(PETSC_COMM_WORLD, PETSC_TRUE, 0, NULL, &nsp));
201   PetscCall(MatSetNullSpace(appctx.SEMop.grad, nsp));
202   PetscCall(MatNullSpaceTest(nsp, appctx.SEMop.grad, NULL));
203   PetscCall(MatNullSpaceDestroy(&nsp));
204 
205   /* Create the TS solver that solves the ODE and its adjoint; set its options */
206   PetscCall(TSCreate(PETSC_COMM_WORLD, &appctx.ts));
207   PetscCall(TSSetProblemType(appctx.ts, TS_NONLINEAR));
208   PetscCall(TSSetType(appctx.ts, TSRK));
209   PetscCall(TSSetDM(appctx.ts, appctx.da));
210   PetscCall(TSSetTime(appctx.ts, 0.0));
211   PetscCall(TSSetTimeStep(appctx.ts, appctx.initial_dt));
212   PetscCall(TSSetMaxSteps(appctx.ts, appctx.param.steps));
213   PetscCall(TSSetMaxTime(appctx.ts, appctx.param.Tend));
214   PetscCall(TSSetExactFinalTime(appctx.ts, TS_EXACTFINALTIME_MATCHSTEP));
215   PetscCall(TSSetTolerances(appctx.ts, 1e-7, NULL, 1e-7, NULL));
216   PetscCall(TSSetSaveTrajectory(appctx.ts));
217   PetscCall(TSSetFromOptions(appctx.ts));
218   PetscCall(TSSetRHSFunction(appctx.ts, NULL, RHSFunction, &appctx));
219   PetscCall(TSSetRHSJacobian(appctx.ts, appctx.SEMop.stiff, appctx.SEMop.stiff, RHSJacobian, &appctx));
220 
221   /* Set Initial conditions for the problem  */
222   PetscCall(TrueSolution(appctx.ts, 0, appctx.dat.curr_sol, &appctx));
223 
224   PetscCall(TSSetSolutionFunction(appctx.ts, (PetscErrorCode(*)(TS, PetscReal, Vec, void *))TrueSolution, &appctx));
225   PetscCall(TSSetTime(appctx.ts, 0.0));
226   PetscCall(TSSetStepNumber(appctx.ts, 0));
227 
228   PetscCall(TSSolve(appctx.ts, appctx.dat.curr_sol));
229 
230   PetscCall(MatDestroy(&appctx.SEMop.stiff));
231   PetscCall(MatDestroy(&appctx.SEMop.keptstiff));
232   PetscCall(MatDestroy(&appctx.SEMop.grad));
233   PetscCall(VecDestroy(&appctx.SEMop.grid));
234   PetscCall(VecDestroy(&appctx.SEMop.mass));
235   PetscCall(VecDestroy(&appctx.dat.curr_sol));
236   PetscCall(PetscFree2(appctx.SEMop.gll.nodes, appctx.SEMop.gll.weights));
237   PetscCall(DMDestroy(&appctx.da));
238   PetscCall(TSDestroy(&appctx.ts));
239 
240   /*
241      Always call PetscFinalize() before exiting a program.  This routine
242        - finalizes the PETSc libraries as well as MPI
243        - provides summary and diagnostic information if certain runtime
244          options are chosen (e.g., -log_view).
245   */
246   PetscCall(PetscFinalize());
247   return 0;
248 }
249 
250 /*
251    TrueSolution() computes the true solution for the PDE
252 
253    Input Parameter:
254    u - uninitialized solution vector (global)
255    appctx - user-defined application context
256 
257    Output Parameter:
258    u - vector with solution at initial time (global)
259 */
260 PetscErrorCode TrueSolution(TS ts, PetscReal t, Vec u, AppCtx *appctx)
261 {
262   PetscScalar       *s;
263   const PetscScalar *xg;
264   PetscInt           i, xs, xn;
265 
266   PetscFunctionBeginUser;
267   PetscCall(DMDAVecGetArray(appctx->da, u, &s));
268   PetscCall(DMDAVecGetArrayRead(appctx->da, appctx->SEMop.grid, (void *)&xg));
269   PetscCall(DMDAGetCorners(appctx->da, &xs, NULL, NULL, &xn, NULL, NULL));
270   for (i = xs; i < xs + xn; i++) {
271     s[i] = 2.0 * appctx->param.mu * PETSC_PI * PetscSinScalar(PETSC_PI * xg[i]) * PetscExpReal(-appctx->param.mu * PETSC_PI * PETSC_PI * t) / (2.0 + PetscCosScalar(PETSC_PI * xg[i]) * PetscExpReal(-appctx->param.mu * PETSC_PI * PETSC_PI * t));
272   }
273   PetscCall(DMDAVecRestoreArray(appctx->da, u, &s));
274   PetscCall(DMDAVecRestoreArrayRead(appctx->da, appctx->SEMop.grid, (void *)&xg));
275   PetscFunctionReturn(PETSC_SUCCESS);
276 }
277 
278 PetscErrorCode RHSFunction(TS ts, PetscReal t, Vec globalin, Vec globalout, void *ctx)
279 {
280   AppCtx *appctx = (AppCtx *)ctx;
281 
282   PetscFunctionBeginUser;
283   PetscCall(MatMult(appctx->SEMop.grad, globalin, globalout)); /* grad u */
284   PetscCall(VecPointwiseMult(globalout, globalin, globalout)); /* u grad u */
285   PetscCall(VecScale(globalout, -1.0));
286   PetscCall(MatMultAdd(appctx->SEMop.keptstiff, globalin, globalout, globalout));
287   PetscFunctionReturn(PETSC_SUCCESS);
288 }
289 
290 /*
291 
292       K is the discretiziation of the Laplacian
293       G is the discretization of the gradient
294 
295       Computes Jacobian of      K u + diag(u) G u   which is given by
296               K   + diag(u)G + diag(Gu)
297 */
298 PetscErrorCode RHSJacobian(TS ts, PetscReal t, Vec globalin, Mat A, Mat B, void *ctx)
299 {
300   AppCtx *appctx = (AppCtx *)ctx;
301   Vec     Gglobalin;
302 
303   PetscFunctionBeginUser;
304   /*    A = diag(u) G */
305 
306   PetscCall(MatCopy(appctx->SEMop.grad, A, SAME_NONZERO_PATTERN));
307   PetscCall(MatDiagonalScale(A, globalin, NULL));
308 
309   /*    A  = A + diag(Gu) */
310   PetscCall(VecDuplicate(globalin, &Gglobalin));
311   PetscCall(MatMult(appctx->SEMop.grad, globalin, Gglobalin));
312   PetscCall(MatDiagonalSet(A, Gglobalin, ADD_VALUES));
313   PetscCall(VecDestroy(&Gglobalin));
314 
315   /*   A  = K - A    */
316   PetscCall(MatScale(A, -1.0));
317   PetscCall(MatAXPY(A, 0.0, appctx->SEMop.keptstiff, SAME_NONZERO_PATTERN));
318   PetscFunctionReturn(PETSC_SUCCESS);
319 }
320 
321 /* --------------------------------------------------------------------- */
322 
323 #include "petscblaslapack.h"
324 /*
325      Matrix free operation of 1d Laplacian and Grad for GLL spectral elements
326 */
327 PetscErrorCode MatMult_Laplacian(Mat A, Vec x, Vec y)
328 {
329   AppCtx            *appctx;
330   PetscReal        **temp, vv;
331   PetscInt           i, j, xs, xn;
332   Vec                xlocal, ylocal;
333   const PetscScalar *xl;
334   PetscScalar       *yl;
335   PetscBLASInt       _One  = 1, n;
336   PetscScalar        _DOne = 1;
337 
338   PetscFunctionBeginUser;
339   PetscCall(MatShellGetContext(A, &appctx));
340   PetscCall(DMGetLocalVector(appctx->da, &xlocal));
341   PetscCall(DMGlobalToLocalBegin(appctx->da, x, INSERT_VALUES, xlocal));
342   PetscCall(DMGlobalToLocalEnd(appctx->da, x, INSERT_VALUES, xlocal));
343   PetscCall(DMGetLocalVector(appctx->da, &ylocal));
344   PetscCall(VecSet(ylocal, 0.0));
345   PetscCall(PetscGaussLobattoLegendreElementLaplacianCreate(appctx->SEMop.gll.n, appctx->SEMop.gll.nodes, appctx->SEMop.gll.weights, &temp));
346   for (i = 0; i < appctx->param.N; i++) {
347     vv = -appctx->param.mu * 2.0 / appctx->param.Le;
348     for (j = 0; j < appctx->param.N; j++) temp[i][j] = temp[i][j] * vv;
349   }
350   PetscCall(DMDAVecGetArrayRead(appctx->da, xlocal, (void *)&xl));
351   PetscCall(DMDAVecGetArray(appctx->da, ylocal, &yl));
352   PetscCall(DMDAGetCorners(appctx->da, &xs, NULL, NULL, &xn, NULL, NULL));
353   PetscCall(PetscBLASIntCast(appctx->param.N, &n));
354   for (j = xs; j < xs + xn; j += appctx->param.N - 1) PetscCallBLAS("BLASgemv", BLASgemv_("N", &n, &n, &_DOne, &temp[0][0], &n, &xl[j], &_One, &_DOne, &yl[j], &_One));
355   PetscCall(DMDAVecRestoreArrayRead(appctx->da, xlocal, (void *)&xl));
356   PetscCall(DMDAVecRestoreArray(appctx->da, ylocal, &yl));
357   PetscCall(PetscGaussLobattoLegendreElementLaplacianDestroy(appctx->SEMop.gll.n, appctx->SEMop.gll.nodes, appctx->SEMop.gll.weights, &temp));
358   PetscCall(VecSet(y, 0.0));
359   PetscCall(DMLocalToGlobalBegin(appctx->da, ylocal, ADD_VALUES, y));
360   PetscCall(DMLocalToGlobalEnd(appctx->da, ylocal, ADD_VALUES, y));
361   PetscCall(DMRestoreLocalVector(appctx->da, &xlocal));
362   PetscCall(DMRestoreLocalVector(appctx->da, &ylocal));
363   PetscCall(VecPointwiseDivide(y, y, appctx->SEMop.mass));
364   PetscFunctionReturn(PETSC_SUCCESS);
365 }
366 
367 PetscErrorCode MatMult_Advection(Mat A, Vec x, Vec y)
368 {
369   AppCtx            *appctx;
370   PetscReal        **temp;
371   PetscInt           j, xs, xn;
372   Vec                xlocal, ylocal;
373   const PetscScalar *xl;
374   PetscScalar       *yl;
375   PetscBLASInt       _One  = 1, n;
376   PetscScalar        _DOne = 1;
377 
378   PetscFunctionBeginUser;
379   PetscCall(MatShellGetContext(A, &appctx));
380   PetscCall(DMGetLocalVector(appctx->da, &xlocal));
381   PetscCall(DMGlobalToLocalBegin(appctx->da, x, INSERT_VALUES, xlocal));
382   PetscCall(DMGlobalToLocalEnd(appctx->da, x, INSERT_VALUES, xlocal));
383   PetscCall(DMGetLocalVector(appctx->da, &ylocal));
384   PetscCall(VecSet(ylocal, 0.0));
385   PetscCall(PetscGaussLobattoLegendreElementAdvectionCreate(appctx->SEMop.gll.n, appctx->SEMop.gll.nodes, appctx->SEMop.gll.weights, &temp));
386   PetscCall(DMDAVecGetArrayRead(appctx->da, xlocal, (void *)&xl));
387   PetscCall(DMDAVecGetArray(appctx->da, ylocal, &yl));
388   PetscCall(DMDAGetCorners(appctx->da, &xs, NULL, NULL, &xn, NULL, NULL));
389   PetscCall(PetscBLASIntCast(appctx->param.N, &n));
390   for (j = xs; j < xs + xn; j += appctx->param.N - 1) PetscCallBLAS("BLASgemv", BLASgemv_("N", &n, &n, &_DOne, &temp[0][0], &n, &xl[j], &_One, &_DOne, &yl[j], &_One));
391   PetscCall(DMDAVecRestoreArrayRead(appctx->da, xlocal, (void *)&xl));
392   PetscCall(DMDAVecRestoreArray(appctx->da, ylocal, &yl));
393   PetscCall(PetscGaussLobattoLegendreElementAdvectionDestroy(appctx->SEMop.gll.n, appctx->SEMop.gll.nodes, appctx->SEMop.gll.weights, &temp));
394   PetscCall(VecSet(y, 0.0));
395   PetscCall(DMLocalToGlobalBegin(appctx->da, ylocal, ADD_VALUES, y));
396   PetscCall(DMLocalToGlobalEnd(appctx->da, ylocal, ADD_VALUES, y));
397   PetscCall(DMRestoreLocalVector(appctx->da, &xlocal));
398   PetscCall(DMRestoreLocalVector(appctx->da, &ylocal));
399   PetscCall(VecPointwiseDivide(y, y, appctx->SEMop.mass));
400   PetscCall(VecScale(y, -1.0));
401   PetscFunctionReturn(PETSC_SUCCESS);
402 }
403 
404 /*
405    RHSMatrixLaplacian - User-provided routine to compute the right-hand-side
406    matrix for the Laplacian operator
407 
408    Input Parameters:
409    ts - the TS context
410    t - current time  (ignored)
411    X - current solution (ignored)
412    dummy - optional user-defined context, as set by TSetRHSJacobian()
413 
414    Output Parameters:
415    AA - Jacobian matrix
416    BB - optionally different matrix from which the preconditioner is built
417    str - flag indicating matrix structure
418 
419 */
420 PetscErrorCode RHSMatrixLaplaciangllDM(TS ts, PetscReal t, Vec X, Mat A, Mat BB, void *ctx)
421 {
422   PetscReal **temp;
423   PetscReal   vv;
424   AppCtx     *appctx = (AppCtx *)ctx; /* user-defined application context */
425   PetscInt    i, xs, xn, l, j;
426   PetscInt   *rowsDM;
427   PetscBool   flg = PETSC_FALSE;
428 
429   PetscFunctionBeginUser;
430   PetscCall(PetscOptionsGetBool(NULL, NULL, "-gll_mf", &flg, NULL));
431 
432   if (!flg) {
433     /*
434      Creates the element stiffness matrix for the given gll
435      */
436     PetscCall(PetscGaussLobattoLegendreElementLaplacianCreate(appctx->SEMop.gll.n, appctx->SEMop.gll.nodes, appctx->SEMop.gll.weights, &temp));
437     /* workaround for clang analyzer warning: Division by zero */
438     PetscCheck(appctx->param.N > 1, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "Spectral element order should be > 1");
439 
440     /* scale by the size of the element */
441     for (i = 0; i < appctx->param.N; i++) {
442       vv = -appctx->param.mu * 2.0 / appctx->param.Le;
443       for (j = 0; j < appctx->param.N; j++) temp[i][j] = temp[i][j] * vv;
444     }
445 
446     PetscCall(MatSetOption(A, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE));
447     PetscCall(DMDAGetCorners(appctx->da, &xs, NULL, NULL, &xn, NULL, NULL));
448 
449     xs = xs / (appctx->param.N - 1);
450     xn = xn / (appctx->param.N - 1);
451 
452     PetscCall(PetscMalloc1(appctx->param.N, &rowsDM));
453     /*
454      loop over local elements
455      */
456     for (j = xs; j < xs + xn; j++) {
457       for (l = 0; l < appctx->param.N; l++) rowsDM[l] = 1 + (j - xs) * (appctx->param.N - 1) + l;
458       PetscCall(MatSetValuesLocal(A, appctx->param.N, rowsDM, appctx->param.N, rowsDM, &temp[0][0], ADD_VALUES));
459     }
460     PetscCall(PetscFree(rowsDM));
461     PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
462     PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
463     PetscCall(VecReciprocal(appctx->SEMop.mass));
464     PetscCall(MatDiagonalScale(A, appctx->SEMop.mass, 0));
465     PetscCall(VecReciprocal(appctx->SEMop.mass));
466 
467     PetscCall(PetscGaussLobattoLegendreElementLaplacianDestroy(appctx->SEMop.gll.n, appctx->SEMop.gll.nodes, appctx->SEMop.gll.weights, &temp));
468   } else {
469     PetscCall(MatSetType(A, MATSHELL));
470     PetscCall(MatSetUp(A));
471     PetscCall(MatShellSetContext(A, appctx));
472     PetscCall(MatShellSetOperation(A, MATOP_MULT, (void (*)(void))MatMult_Laplacian));
473   }
474   PetscFunctionReturn(PETSC_SUCCESS);
475 }
476 
477 /*
478    RHSMatrixAdvection - User-provided routine to compute the right-hand-side
479    matrix for the Advection (gradient) operator.
480 
481    Input Parameters:
482    ts - the TS context
483    t - current time
484    global_in - global input vector
485    dummy - optional user-defined context, as set by TSetRHSJacobian()
486 
487    Output Parameters:
488    AA - Jacobian matrix
489    BB - optionally different preconditioning matrix
490    str - flag indicating matrix structure
491 
492 */
493 PetscErrorCode RHSMatrixAdvectiongllDM(TS ts, PetscReal t, Vec X, Mat A, Mat BB, void *ctx)
494 {
495   PetscReal **temp;
496   AppCtx     *appctx = (AppCtx *)ctx; /* user-defined application context */
497   PetscInt    xs, xn, l, j;
498   PetscInt   *rowsDM;
499   PetscBool   flg = PETSC_FALSE;
500 
501   PetscFunctionBeginUser;
502   PetscCall(PetscOptionsGetBool(NULL, NULL, "-gll_mf", &flg, NULL));
503 
504   if (!flg) {
505     /*
506      Creates the advection matrix for the given gll
507      */
508     PetscCall(PetscGaussLobattoLegendreElementAdvectionCreate(appctx->SEMop.gll.n, appctx->SEMop.gll.nodes, appctx->SEMop.gll.weights, &temp));
509     PetscCall(MatSetOption(A, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE));
510     PetscCall(DMDAGetCorners(appctx->da, &xs, NULL, NULL, &xn, NULL, NULL));
511     xs = xs / (appctx->param.N - 1);
512     xn = xn / (appctx->param.N - 1);
513 
514     PetscCall(PetscMalloc1(appctx->param.N, &rowsDM));
515     for (j = xs; j < xs + xn; j++) {
516       for (l = 0; l < appctx->param.N; l++) rowsDM[l] = 1 + (j - xs) * (appctx->param.N - 1) + l;
517       PetscCall(MatSetValuesLocal(A, appctx->param.N, rowsDM, appctx->param.N, rowsDM, &temp[0][0], ADD_VALUES));
518     }
519     PetscCall(PetscFree(rowsDM));
520     PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
521     PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
522 
523     PetscCall(VecReciprocal(appctx->SEMop.mass));
524     PetscCall(MatDiagonalScale(A, appctx->SEMop.mass, 0));
525     PetscCall(VecReciprocal(appctx->SEMop.mass));
526 
527     PetscCall(PetscGaussLobattoLegendreElementAdvectionDestroy(appctx->SEMop.gll.n, appctx->SEMop.gll.nodes, appctx->SEMop.gll.weights, &temp));
528   } else {
529     PetscCall(MatSetType(A, MATSHELL));
530     PetscCall(MatSetUp(A));
531     PetscCall(MatShellSetContext(A, appctx));
532     PetscCall(MatShellSetOperation(A, MATOP_MULT, (void (*)(void))MatMult_Advection));
533   }
534   PetscFunctionReturn(PETSC_SUCCESS);
535 }
536 
537 /*TEST
538 
539     build:
540       requires: !complex
541 
542     test:
543       suffix: 1
544       requires: !single
545 
546     test:
547       suffix: 2
548       nsize: 5
549       requires: !single
550 
551     test:
552       suffix: 3
553       requires: !single
554       args: -ts_view -ts_type beuler -gll_mf -pc_type none -ts_max_steps 5 -ts_monitor_error
555 
556     test:
557       suffix: 4
558       requires: !single
559       args: -ts_view -ts_type beuler -pc_type none -ts_max_steps 5 -ts_monitor_error
560 
561 TEST*/
562