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