xref: /petsc/src/tao/unconstrained/tutorials/burgers_spectral.c (revision a5b0eddbbbd6a0c73e34f654efd1185d330c478a)
1 
2 static char help[] = "Solves a simple data assimilation problem with one dimensional Burger's equation using TSAdjoint\n\n";
3 
4 /*
5 
6     Not yet tested in parallel
7 
8 */
9 
10 /* ------------------------------------------------------------------------
11 
12    This program uses the one-dimensional Burger's equation
13        u_t = mu*u_xx - u u_x,
14    on the domain 0 <= x <= 1, with periodic boundary conditions
15 
16    to demonstrate solving a data assimilation problem of finding the initial conditions
17    to produce a given solution at a fixed time.
18 
19    The operators are discretized with the spectral element method
20 
21    See the paper PDE-CONSTRAINED OPTIMIZATION WITH SPECTRAL ELEMENTS USING PETSC AND TAO
22    by OANA MARIN, EMIL CONSTANTINESCU, AND BARRY SMITH for details on the exact solution
23    used
24 
25   ------------------------------------------------------------------------- */
26 
27 #include <petsctao.h>
28 #include <petscts.h>
29 #include <petscdt.h>
30 #include <petscdraw.h>
31 #include <petscdmda.h>
32 
33 /*
34    User-defined application context - contains data needed by the
35    application-provided call-back routines.
36 */
37 
38 typedef struct {
39   PetscInt   n;       /* number of nodes */
40   PetscReal *nodes;   /* GLL nodes */
41   PetscReal *weights; /* GLL weights */
42 } PetscGLL;
43 
44 typedef struct {
45   PetscInt  N;               /* grid points per elements*/
46   PetscInt  E;               /* number of elements */
47   PetscReal tol_L2, tol_max; /* error norms */
48   PetscInt  steps;           /* number of timesteps */
49   PetscReal Tend;            /* endtime */
50   PetscReal mu;              /* viscosity */
51   PetscReal L;               /* total length of domain */
52   PetscReal Le;
53   PetscReal Tadj;
54 } PetscParam;
55 
56 typedef struct {
57   Vec obj;  /* desired end state */
58   Vec grid; /* total grid */
59   Vec grad;
60   Vec ic;
61   Vec curr_sol;
62   Vec true_solution; /* actual initial conditions for the final solution */
63 } PetscData;
64 
65 typedef struct {
66   Vec      grid;  /* total grid */
67   Vec      mass;  /* mass matrix for total integration */
68   Mat      stiff; /* stifness matrix */
69   Mat      keptstiff;
70   Mat      grad;
71   PetscGLL gll;
72 } PetscSEMOperators;
73 
74 typedef struct {
75   DM                da; /* distributed array data structure */
76   PetscSEMOperators SEMop;
77   PetscParam        param;
78   PetscData         dat;
79   TS                ts;
80   PetscReal         initial_dt;
81 } AppCtx;
82 
83 /*
84    User-defined routines
85 */
86 extern PetscErrorCode FormFunctionGradient(Tao, Vec, PetscReal *, Vec, void *);
87 extern PetscErrorCode RHSMatrixLaplaciangllDM(TS, PetscReal, Vec, Mat, Mat, void *);
88 extern PetscErrorCode RHSMatrixAdvectiongllDM(TS, PetscReal, Vec, Mat, Mat, void *);
89 extern PetscErrorCode InitialConditions(Vec, AppCtx *);
90 extern PetscErrorCode TrueSolution(Vec, AppCtx *);
91 extern PetscErrorCode ComputeObjective(PetscReal, Vec, AppCtx *);
92 extern PetscErrorCode MonitorError(Tao, void *);
93 extern PetscErrorCode RHSFunction(TS, PetscReal, Vec, Vec, void *);
94 extern PetscErrorCode RHSJacobian(TS, PetscReal, Vec, Mat, Mat, void *);
95 
96 int main(int argc, char **argv)
97 {
98   AppCtx       appctx; /* user-defined application context */
99   Tao          tao;
100   Vec          u; /* approximate solution vector */
101   PetscInt     i, xs, xm, ind, j, lenglob;
102   PetscReal    x, *wrk_ptr1, *wrk_ptr2;
103   MatNullSpace nsp;
104   PetscMPIInt  size;
105 
106   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
107      Initialize program and set problem parameters
108      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
109   PetscFunctionBegin;
110 
111   PetscFunctionBeginUser;
112   PetscCall(PetscInitialize(&argc, &argv, (char *)0, help));
113 
114   /*initialize parameters */
115   appctx.param.N     = 10;   /* order of the spectral element */
116   appctx.param.E     = 10;   /* number of elements */
117   appctx.param.L     = 4.0;  /* length of the domain */
118   appctx.param.mu    = 0.01; /* diffusion coefficient */
119   appctx.initial_dt  = 5e-3;
120   appctx.param.steps = PETSC_MAX_INT;
121   appctx.param.Tend  = 4;
122 
123   PetscCall(PetscOptionsGetInt(NULL, NULL, "-N", &appctx.param.N, NULL));
124   PetscCall(PetscOptionsGetInt(NULL, NULL, "-E", &appctx.param.E, NULL));
125   PetscCall(PetscOptionsGetReal(NULL, NULL, "-Tend", &appctx.param.Tend, NULL));
126   PetscCall(PetscOptionsGetReal(NULL, NULL, "-mu", &appctx.param.mu, NULL));
127   appctx.param.Le = appctx.param.L / appctx.param.E;
128 
129   PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
130   PetscCheck((appctx.param.E % size) == 0, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "Number of elements must be divisible by number of processes");
131 
132   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
133      Create GLL data structures
134      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
135   PetscCall(PetscMalloc2(appctx.param.N, &appctx.SEMop.gll.nodes, appctx.param.N, &appctx.SEMop.gll.weights));
136   PetscCall(PetscDTGaussLobattoLegendreQuadrature(appctx.param.N, PETSCGAUSSLOBATTOLEGENDRE_VIA_LINEAR_ALGEBRA, appctx.SEMop.gll.nodes, appctx.SEMop.gll.weights));
137   appctx.SEMop.gll.n = appctx.param.N;
138   lenglob            = appctx.param.E * (appctx.param.N - 1);
139 
140   /*
141      Create distributed array (DMDA) to manage parallel grid and vectors
142      and to set up the ghost point communication pattern.  There are E*(Nl-1)+1
143      total grid values spread equally among all the processors, except first and last
144   */
145 
146   PetscCall(DMDACreate1d(PETSC_COMM_WORLD, DM_BOUNDARY_PERIODIC, lenglob, 1, 1, NULL, &appctx.da));
147   PetscCall(DMSetFromOptions(appctx.da));
148   PetscCall(DMSetUp(appctx.da));
149 
150   /*
151      Extract global and local vectors from DMDA; we use these to store the
152      approximate solution.  Then duplicate these for remaining vectors that
153      have the same types.
154   */
155 
156   PetscCall(DMCreateGlobalVector(appctx.da, &u));
157   PetscCall(VecDuplicate(u, &appctx.dat.ic));
158   PetscCall(VecDuplicate(u, &appctx.dat.true_solution));
159   PetscCall(VecDuplicate(u, &appctx.dat.obj));
160   PetscCall(VecDuplicate(u, &appctx.SEMop.grid));
161   PetscCall(VecDuplicate(u, &appctx.SEMop.mass));
162   PetscCall(VecDuplicate(u, &appctx.dat.curr_sol));
163 
164   PetscCall(DMDAGetCorners(appctx.da, &xs, NULL, NULL, &xm, NULL, NULL));
165   PetscCall(DMDAVecGetArray(appctx.da, appctx.SEMop.grid, &wrk_ptr1));
166   PetscCall(DMDAVecGetArray(appctx.da, appctx.SEMop.mass, &wrk_ptr2));
167 
168   /* Compute function over the locally owned part of the grid */
169 
170   xs = xs / (appctx.param.N - 1);
171   xm = xm / (appctx.param.N - 1);
172 
173   /*
174      Build total grid and mass over entire mesh (multi-elemental)
175   */
176 
177   for (i = xs; i < xs + xm; i++) {
178     for (j = 0; j < appctx.param.N - 1; j++) {
179       x             = (appctx.param.Le / 2.0) * (appctx.SEMop.gll.nodes[j] + 1.0) + appctx.param.Le * i;
180       ind           = i * (appctx.param.N - 1) + j;
181       wrk_ptr1[ind] = x;
182       wrk_ptr2[ind] = .5 * appctx.param.Le * appctx.SEMop.gll.weights[j];
183       if (j == 0) wrk_ptr2[ind] += .5 * appctx.param.Le * appctx.SEMop.gll.weights[j];
184     }
185   }
186   PetscCall(DMDAVecRestoreArray(appctx.da, appctx.SEMop.grid, &wrk_ptr1));
187   PetscCall(DMDAVecRestoreArray(appctx.da, appctx.SEMop.mass, &wrk_ptr2));
188 
189   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
190    Create matrix data structure; set matrix evaluation routine.
191    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
192   PetscCall(DMSetMatrixPreallocateOnly(appctx.da, PETSC_TRUE));
193   PetscCall(DMCreateMatrix(appctx.da, &appctx.SEMop.stiff));
194   PetscCall(DMCreateMatrix(appctx.da, &appctx.SEMop.grad));
195   /*
196    For linear problems with a time-dependent f(u,t) in the equation
197    u_t = f(u,t), the user provides the discretized right-hand-side
198    as a time-dependent matrix.
199    */
200   PetscCall(RHSMatrixLaplaciangllDM(appctx.ts, 0.0, u, appctx.SEMop.stiff, appctx.SEMop.stiff, &appctx));
201   PetscCall(RHSMatrixAdvectiongllDM(appctx.ts, 0.0, u, appctx.SEMop.grad, appctx.SEMop.grad, &appctx));
202   /*
203        For linear problems with a time-dependent f(u,t) in the equation
204        u_t = f(u,t), the user provides the discretized right-hand-side
205        as a time-dependent matrix.
206     */
207 
208   PetscCall(MatDuplicate(appctx.SEMop.stiff, MAT_COPY_VALUES, &appctx.SEMop.keptstiff));
209 
210   /* attach the null space to the matrix, this probably is not needed but does no harm */
211   PetscCall(MatNullSpaceCreate(PETSC_COMM_WORLD, PETSC_TRUE, 0, NULL, &nsp));
212   PetscCall(MatSetNullSpace(appctx.SEMop.stiff, nsp));
213   PetscCall(MatSetNullSpace(appctx.SEMop.keptstiff, nsp));
214   PetscCall(MatNullSpaceTest(nsp, appctx.SEMop.stiff, NULL));
215   PetscCall(MatNullSpaceDestroy(&nsp));
216   /* attach the null space to the matrix, this probably is not needed but does no harm */
217   PetscCall(MatNullSpaceCreate(PETSC_COMM_WORLD, PETSC_TRUE, 0, NULL, &nsp));
218   PetscCall(MatSetNullSpace(appctx.SEMop.grad, nsp));
219   PetscCall(MatNullSpaceTest(nsp, appctx.SEMop.grad, NULL));
220   PetscCall(MatNullSpaceDestroy(&nsp));
221 
222   /* Create the TS solver that solves the ODE and its adjoint; set its options */
223   PetscCall(TSCreate(PETSC_COMM_WORLD, &appctx.ts));
224   PetscCall(TSSetProblemType(appctx.ts, TS_NONLINEAR));
225   PetscCall(TSSetType(appctx.ts, TSRK));
226   PetscCall(TSSetDM(appctx.ts, appctx.da));
227   PetscCall(TSSetTime(appctx.ts, 0.0));
228   PetscCall(TSSetTimeStep(appctx.ts, appctx.initial_dt));
229   PetscCall(TSSetMaxSteps(appctx.ts, appctx.param.steps));
230   PetscCall(TSSetMaxTime(appctx.ts, appctx.param.Tend));
231   PetscCall(TSSetExactFinalTime(appctx.ts, TS_EXACTFINALTIME_MATCHSTEP));
232   PetscCall(TSSetTolerances(appctx.ts, 1e-7, NULL, 1e-7, NULL));
233   PetscCall(TSSetFromOptions(appctx.ts));
234   /* Need to save initial timestep user may have set with -ts_dt so it can be reset for each new TSSolve() */
235   PetscCall(TSGetTimeStep(appctx.ts, &appctx.initial_dt));
236   PetscCall(TSSetRHSFunction(appctx.ts, NULL, RHSFunction, &appctx));
237   PetscCall(TSSetRHSJacobian(appctx.ts, appctx.SEMop.stiff, appctx.SEMop.stiff, RHSJacobian, &appctx));
238 
239   /* Set Objective and Initial conditions for the problem and compute Objective function (evolution of true_solution to final time */
240   PetscCall(InitialConditions(appctx.dat.ic, &appctx));
241   PetscCall(TrueSolution(appctx.dat.true_solution, &appctx));
242   PetscCall(ComputeObjective(appctx.param.Tend, appctx.dat.obj, &appctx));
243 
244   PetscCall(TSSetSaveTrajectory(appctx.ts));
245   PetscCall(TSSetFromOptions(appctx.ts));
246 
247   /* Create TAO solver and set desired solution method  */
248   PetscCall(TaoCreate(PETSC_COMM_WORLD, &tao));
249   PetscCall(TaoSetMonitor(tao, MonitorError, &appctx, NULL));
250   PetscCall(TaoSetType(tao, TAOBQNLS));
251   PetscCall(TaoSetSolution(tao, appctx.dat.ic));
252   /* Set routine for function and gradient evaluation  */
253   PetscCall(TaoSetObjectiveAndGradient(tao, NULL, FormFunctionGradient, (void *)&appctx));
254   /* Check for any TAO command line options  */
255   PetscCall(TaoSetTolerances(tao, 1e-8, PETSC_DEFAULT, PETSC_DEFAULT));
256   PetscCall(TaoSetFromOptions(tao));
257   PetscCall(TaoSolve(tao));
258 
259   PetscCall(TaoDestroy(&tao));
260   PetscCall(MatDestroy(&appctx.SEMop.stiff));
261   PetscCall(MatDestroy(&appctx.SEMop.keptstiff));
262   PetscCall(MatDestroy(&appctx.SEMop.grad));
263   PetscCall(VecDestroy(&u));
264   PetscCall(VecDestroy(&appctx.dat.ic));
265   PetscCall(VecDestroy(&appctx.dat.true_solution));
266   PetscCall(VecDestroy(&appctx.dat.obj));
267   PetscCall(VecDestroy(&appctx.SEMop.grid));
268   PetscCall(VecDestroy(&appctx.SEMop.mass));
269   PetscCall(VecDestroy(&appctx.dat.curr_sol));
270   PetscCall(PetscFree2(appctx.SEMop.gll.nodes, appctx.SEMop.gll.weights));
271   PetscCall(DMDestroy(&appctx.da));
272   PetscCall(TSDestroy(&appctx.ts));
273 
274   /*
275      Always call PetscFinalize() before exiting a program.  This routine
276        - finalizes the PETSc libraries as well as MPI
277        - provides summary and diagnostic information if certain runtime
278          options are chosen (e.g., -log_view).
279   */
280   PetscCall(PetscFinalize());
281   return 0;
282 }
283 
284 /* --------------------------------------------------------------------- */
285 /*
286    InitialConditions - Computes the initial conditions for the Tao optimization solve (these are also initial conditions for the first TSSolve()
287 
288                        The routine TrueSolution() computes the true solution for the Tao optimization solve which means they are the initial conditions for the objective function
289 
290    Input Parameter:
291    u - uninitialized solution vector (global)
292    appctx - user-defined application context
293 
294    Output Parameter:
295    u - vector with solution at initial time (global)
296 */
297 PetscErrorCode InitialConditions(Vec u, AppCtx *appctx)
298 {
299   PetscScalar       *s;
300   const PetscScalar *xg;
301   PetscInt           i, xs, xn;
302 
303   PetscFunctionBegin;
304   PetscCall(DMDAVecGetArray(appctx->da, u, &s));
305   PetscCall(DMDAVecGetArrayRead(appctx->da, appctx->SEMop.grid, (void *)&xg));
306   PetscCall(DMDAGetCorners(appctx->da, &xs, NULL, NULL, &xn, NULL, NULL));
307   for (i = xs; i < xs + xn; i++) s[i] = 2.0 * appctx->param.mu * PETSC_PI * PetscSinScalar(PETSC_PI * xg[i]) / (2.0 + PetscCosScalar(PETSC_PI * xg[i])) + 0.25 * PetscExpReal(-4.0 * PetscPowReal(xg[i] - 2.0, 2.0));
308   PetscCall(DMDAVecRestoreArray(appctx->da, u, &s));
309   PetscCall(DMDAVecRestoreArrayRead(appctx->da, appctx->SEMop.grid, (void *)&xg));
310   PetscFunctionReturn(PETSC_SUCCESS);
311 }
312 
313 /*
314    TrueSolution() computes the true solution for the Tao optimization solve which means they are the initial conditions for the objective function.
315 
316              InitialConditions() computes the initial conditions for the beginning of the Tao iterations
317 
318    Input Parameter:
319    u - uninitialized solution vector (global)
320    appctx - user-defined application context
321 
322    Output Parameter:
323    u - vector with solution at initial time (global)
324 */
325 PetscErrorCode TrueSolution(Vec u, AppCtx *appctx)
326 {
327   PetscScalar       *s;
328   const PetscScalar *xg;
329   PetscInt           i, xs, xn;
330 
331   PetscFunctionBegin;
332   PetscCall(DMDAVecGetArray(appctx->da, u, &s));
333   PetscCall(DMDAVecGetArrayRead(appctx->da, appctx->SEMop.grid, (void *)&xg));
334   PetscCall(DMDAGetCorners(appctx->da, &xs, NULL, NULL, &xn, NULL, NULL));
335   for (i = xs; i < xs + xn; i++) s[i] = 2.0 * appctx->param.mu * PETSC_PI * PetscSinScalar(PETSC_PI * xg[i]) / (2.0 + PetscCosScalar(PETSC_PI * xg[i]));
336   PetscCall(DMDAVecRestoreArray(appctx->da, u, &s));
337   PetscCall(DMDAVecRestoreArrayRead(appctx->da, appctx->SEMop.grid, (void *)&xg));
338   PetscFunctionReturn(PETSC_SUCCESS);
339 }
340 /* --------------------------------------------------------------------- */
341 /*
342    Sets the desired profile for the final end time
343 
344    Input Parameters:
345    t - final time
346    obj - vector storing the desired profile
347    appctx - user-defined application context
348 
349 */
350 PetscErrorCode ComputeObjective(PetscReal t, Vec obj, AppCtx *appctx)
351 {
352   PetscScalar       *s;
353   const PetscScalar *xg;
354   PetscInt           i, xs, xn;
355 
356   PetscFunctionBegin;
357   PetscCall(DMDAVecGetArray(appctx->da, obj, &s));
358   PetscCall(DMDAVecGetArrayRead(appctx->da, appctx->SEMop.grid, (void *)&xg));
359   PetscCall(DMDAGetCorners(appctx->da, &xs, NULL, NULL, &xn, NULL, NULL));
360   for (i = xs; i < xs + xn; i++) {
361     s[i] = 2.0 * appctx->param.mu * PETSC_PI * PetscSinScalar(PETSC_PI * xg[i]) * PetscExpScalar(-PETSC_PI * PETSC_PI * t * appctx->param.mu) / (2.0 + PetscExpScalar(-PETSC_PI * PETSC_PI * t * appctx->param.mu) * PetscCosScalar(PETSC_PI * xg[i]));
362   }
363   PetscCall(DMDAVecRestoreArray(appctx->da, obj, &s));
364   PetscCall(DMDAVecRestoreArrayRead(appctx->da, appctx->SEMop.grid, (void *)&xg));
365   PetscFunctionReturn(PETSC_SUCCESS);
366 }
367 
368 PetscErrorCode RHSFunction(TS ts, PetscReal t, Vec globalin, Vec globalout, void *ctx)
369 {
370   AppCtx *appctx = (AppCtx *)ctx;
371 
372   PetscFunctionBegin;
373   PetscCall(MatMult(appctx->SEMop.grad, globalin, globalout)); /* grad u */
374   PetscCall(VecPointwiseMult(globalout, globalin, globalout)); /* u grad u */
375   PetscCall(VecScale(globalout, -1.0));
376   PetscCall(MatMultAdd(appctx->SEMop.keptstiff, globalin, globalout, globalout));
377   PetscFunctionReturn(PETSC_SUCCESS);
378 }
379 
380 /*
381 
382       K is the discretiziation of the Laplacian
383       G is the discretization of the gradient
384 
385       Computes Jacobian of      K u + diag(u) G u   which is given by
386               K   + diag(u)G + diag(Gu)
387 */
388 PetscErrorCode RHSJacobian(TS ts, PetscReal t, Vec globalin, Mat A, Mat B, void *ctx)
389 {
390   AppCtx *appctx = (AppCtx *)ctx;
391   Vec     Gglobalin;
392 
393   PetscFunctionBegin;
394   /*    A = diag(u) G */
395 
396   PetscCall(MatCopy(appctx->SEMop.grad, A, SAME_NONZERO_PATTERN));
397   PetscCall(MatDiagonalScale(A, globalin, NULL));
398 
399   /*    A  = A + diag(Gu) */
400   PetscCall(VecDuplicate(globalin, &Gglobalin));
401   PetscCall(MatMult(appctx->SEMop.grad, globalin, Gglobalin));
402   PetscCall(MatDiagonalSet(A, Gglobalin, ADD_VALUES));
403   PetscCall(VecDestroy(&Gglobalin));
404 
405   /*   A  = K - A    */
406   PetscCall(MatScale(A, -1.0));
407   PetscCall(MatAXPY(A, 1.0, appctx->SEMop.keptstiff, SAME_NONZERO_PATTERN));
408   PetscFunctionReturn(PETSC_SUCCESS);
409 }
410 
411 /* --------------------------------------------------------------------- */
412 
413 /*
414    RHSMatrixLaplacian - User-provided routine to compute the right-hand-side
415    matrix for the heat equation.
416 
417    Input Parameters:
418    ts - the TS context
419    t - current time  (ignored)
420    X - current solution (ignored)
421    dummy - optional user-defined context, as set by TSetRHSJacobian()
422 
423    Output Parameters:
424    AA - Jacobian matrix
425    BB - optionally different matrix from which the preconditioner is built
426    str - flag indicating matrix structure
427 
428 */
429 PetscErrorCode RHSMatrixLaplaciangllDM(TS ts, PetscReal t, Vec X, Mat A, Mat BB, void *ctx)
430 {
431   PetscReal **temp;
432   PetscReal   vv;
433   AppCtx     *appctx = (AppCtx *)ctx; /* user-defined application context */
434   PetscInt    i, xs, xn, l, j;
435   PetscInt   *rowsDM;
436 
437   PetscFunctionBegin;
438   /*
439    Creates the element stiffness matrix for the given gll
440    */
441   PetscCall(PetscGaussLobattoLegendreElementLaplacianCreate(appctx->SEMop.gll.n, appctx->SEMop.gll.nodes, appctx->SEMop.gll.weights, &temp));
442   /* workaround for clang analyzer warning: Division by zero */
443   PetscCheck(appctx->param.N > 1, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "Spectral element order should be > 1");
444 
445   /* scale by the size of the element */
446   for (i = 0; i < appctx->param.N; i++) {
447     vv = -appctx->param.mu * 2.0 / appctx->param.Le;
448     for (j = 0; j < appctx->param.N; j++) temp[i][j] = temp[i][j] * vv;
449   }
450 
451   PetscCall(MatSetOption(A, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE));
452   PetscCall(DMDAGetCorners(appctx->da, &xs, NULL, NULL, &xn, NULL, NULL));
453 
454   xs = xs / (appctx->param.N - 1);
455   xn = xn / (appctx->param.N - 1);
456 
457   PetscCall(PetscMalloc1(appctx->param.N, &rowsDM));
458   /*
459    loop over local elements
460    */
461   for (j = xs; j < xs + xn; j++) {
462     for (l = 0; l < appctx->param.N; l++) rowsDM[l] = 1 + (j - xs) * (appctx->param.N - 1) + l;
463     PetscCall(MatSetValuesLocal(A, appctx->param.N, rowsDM, appctx->param.N, rowsDM, &temp[0][0], ADD_VALUES));
464   }
465   PetscCall(PetscFree(rowsDM));
466   PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
467   PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
468   PetscCall(VecReciprocal(appctx->SEMop.mass));
469   PetscCall(MatDiagonalScale(A, appctx->SEMop.mass, 0));
470   PetscCall(VecReciprocal(appctx->SEMop.mass));
471 
472   PetscCall(PetscGaussLobattoLegendreElementLaplacianDestroy(appctx->SEMop.gll.n, appctx->SEMop.gll.nodes, appctx->SEMop.gll.weights, &temp));
473   PetscFunctionReturn(PETSC_SUCCESS);
474 }
475 
476 /*
477    RHSMatrixAdvection - User-provided routine to compute the right-hand-side
478    matrix for the Advection equation.
479 
480    Input Parameters:
481    ts - the TS context
482    t - current time
483    global_in - global input vector
484    dummy - optional user-defined context, as set by TSetRHSJacobian()
485 
486    Output Parameters:
487    AA - Jacobian matrix
488    BB - optionally different preconditioning matrix
489    str - flag indicating matrix structure
490 
491 */
492 PetscErrorCode RHSMatrixAdvectiongllDM(TS ts, PetscReal t, Vec X, Mat A, Mat BB, void *ctx)
493 {
494   PetscReal **temp;
495   AppCtx     *appctx = (AppCtx *)ctx; /* user-defined application context */
496   PetscInt    xs, xn, l, j;
497   PetscInt   *rowsDM;
498 
499   PetscFunctionBegin;
500   /*
501    Creates the advection matrix for the given gll
502    */
503   PetscCall(PetscGaussLobattoLegendreElementAdvectionCreate(appctx->SEMop.gll.n, appctx->SEMop.gll.nodes, appctx->SEMop.gll.weights, &temp));
504   PetscCall(MatSetOption(A, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE));
505 
506   PetscCall(DMDAGetCorners(appctx->da, &xs, NULL, NULL, &xn, NULL, NULL));
507 
508   xs = xs / (appctx->param.N - 1);
509   xn = xn / (appctx->param.N - 1);
510 
511   PetscCall(PetscMalloc1(appctx->param.N, &rowsDM));
512   for (j = xs; j < xs + xn; j++) {
513     for (l = 0; l < appctx->param.N; l++) rowsDM[l] = 1 + (j - xs) * (appctx->param.N - 1) + l;
514     PetscCall(MatSetValuesLocal(A, appctx->param.N, rowsDM, appctx->param.N, rowsDM, &temp[0][0], ADD_VALUES));
515   }
516   PetscCall(PetscFree(rowsDM));
517   PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
518   PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
519 
520   PetscCall(VecReciprocal(appctx->SEMop.mass));
521   PetscCall(MatDiagonalScale(A, appctx->SEMop.mass, 0));
522   PetscCall(VecReciprocal(appctx->SEMop.mass));
523   PetscCall(PetscGaussLobattoLegendreElementAdvectionDestroy(appctx->SEMop.gll.n, appctx->SEMop.gll.nodes, appctx->SEMop.gll.weights, &temp));
524   PetscFunctionReturn(PETSC_SUCCESS);
525 }
526 /* ------------------------------------------------------------------ */
527 /*
528    FormFunctionGradient - Evaluates the function and corresponding gradient.
529 
530    Input Parameters:
531    tao - the Tao context
532    IC   - the input vector
533    ctx - optional user-defined context, as set when calling TaoSetObjectiveAndGradient()
534 
535    Output Parameters:
536    f   - the newly evaluated function
537    G   - the newly evaluated gradient
538 
539    Notes:
540 
541           The forward equation is
542               M u_t = F(U)
543           which is converted to
544                 u_t = M^{-1} F(u)
545           in the user code since TS has no direct way of providing a mass matrix. The Jacobian of this is
546                  M^{-1} J
547           where J is the Jacobian of F. Now the adjoint equation is
548                 M v_t = J^T v
549           but TSAdjoint does not solve this since it can only solve the transposed system for the
550           Jacobian the user provided. Hence TSAdjoint solves
551                  w_t = J^T M^{-1} w  (where w = M v)
552           since there is no way to indicate the mass matrix as a separate entity to TS. Thus one
553           must be careful in initializing the "adjoint equation" and using the result. This is
554           why
555               G = -2 M(u(T) - u_d)
556           below (instead of -2(u(T) - u_d) and why the result is
557               G = G/appctx->SEMop.mass (that is G = M^{-1}w)
558           below (instead of just the result of the "adjoint solve").
559 
560 */
561 PetscErrorCode FormFunctionGradient(Tao tao, Vec IC, PetscReal *f, Vec G, void *ctx)
562 {
563   AppCtx            *appctx = (AppCtx *)ctx; /* user-defined application context */
564   Vec                temp;
565   PetscInt           its;
566   PetscReal          ff, gnorm, cnorm, xdiff, errex;
567   TaoConvergedReason reason;
568 
569   PetscFunctionBegin;
570   PetscCall(TSSetTime(appctx->ts, 0.0));
571   PetscCall(TSSetStepNumber(appctx->ts, 0));
572   PetscCall(TSSetTimeStep(appctx->ts, appctx->initial_dt));
573   PetscCall(VecCopy(IC, appctx->dat.curr_sol));
574 
575   PetscCall(TSSolve(appctx->ts, appctx->dat.curr_sol));
576 
577   PetscCall(VecWAXPY(G, -1.0, appctx->dat.curr_sol, appctx->dat.obj));
578 
579   /*
580      Compute the L2-norm of the objective function, cost function is f
581   */
582   PetscCall(VecDuplicate(G, &temp));
583   PetscCall(VecPointwiseMult(temp, G, G));
584   PetscCall(VecDot(temp, appctx->SEMop.mass, f));
585 
586   /* local error evaluation   */
587   PetscCall(VecWAXPY(temp, -1.0, appctx->dat.ic, appctx->dat.true_solution));
588   PetscCall(VecPointwiseMult(temp, temp, temp));
589   /* for error evaluation */
590   PetscCall(VecDot(temp, appctx->SEMop.mass, &errex));
591   PetscCall(VecDestroy(&temp));
592   errex = PetscSqrtReal(errex);
593 
594   /*
595      Compute initial conditions for the adjoint integration. See Notes above
596   */
597 
598   PetscCall(VecScale(G, -2.0));
599   PetscCall(VecPointwiseMult(G, G, appctx->SEMop.mass));
600   PetscCall(TSSetCostGradients(appctx->ts, 1, &G, NULL));
601   PetscCall(TSAdjointSolve(appctx->ts));
602   PetscCall(VecPointwiseDivide(G, G, appctx->SEMop.mass));
603 
604   PetscCall(TaoGetSolutionStatus(tao, &its, &ff, &gnorm, &cnorm, &xdiff, &reason));
605   PetscFunctionReturn(PETSC_SUCCESS);
606 }
607 
608 PetscErrorCode MonitorError(Tao tao, void *ctx)
609 {
610   AppCtx   *appctx = (AppCtx *)ctx;
611   Vec       temp;
612   PetscReal nrm;
613 
614   PetscFunctionBegin;
615   PetscCall(VecDuplicate(appctx->dat.ic, &temp));
616   PetscCall(VecWAXPY(temp, -1.0, appctx->dat.ic, appctx->dat.true_solution));
617   PetscCall(VecPointwiseMult(temp, temp, temp));
618   PetscCall(VecDot(temp, appctx->SEMop.mass, &nrm));
619   PetscCall(VecDestroy(&temp));
620   nrm = PetscSqrtReal(nrm);
621   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Error for initial conditions %g\n", (double)nrm));
622   PetscFunctionReturn(PETSC_SUCCESS);
623 }
624 
625 /*TEST
626 
627     build:
628       requires: !complex
629 
630     test:
631       args: -tao_max_it 5 -tao_gatol 1.e-4
632       requires: !single
633 
634     test:
635       suffix: 2
636       nsize: 2
637       args: -tao_max_it 5 -tao_gatol 1.e-4
638       requires: !single
639 
640 TEST*/
641