Actual source code: adr_ex5adj.cxx

  1: static char help[] = "Demonstrates adjoint sensitivity analysis for Reaction-Diffusion Equations.\n";

  3: /*
  4:    REQUIRES configuration of PETSc with option --download-adolc.

  6:    For documentation on ADOL-C, see
  7:      $PETSC_ARCH/externalpackages/ADOL-C-2.6.0/ADOL-C/doc/adolc-manual.pdf

  9:    REQUIRES configuration of PETSc with option --download-colpack

 11:    For documentation on ColPack, see
 12:      $PETSC_ARCH/externalpackages/git.colpack/README.md
 13: */
 14: /* ------------------------------------------------------------------------
 15:   See ../advection-diffusion-reaction/ex5 for a description of the problem
 16:   ------------------------------------------------------------------------- */

 18: /*
 19:   Runtime options:

 21:     Solver:
 22:       -forwardonly       - Run the forward simulation without adjoint.
 23:       -implicitform      - Provide IFunction and IJacobian to TS, if not set, RHSFunction and RHSJacobian will be used.
 24:       -aijpc             - Set the preconditioner matrix to be aij (the Jacobian matrix can be of a different type such as ELL).

 26:     Jacobian generation:
 27:       -jacobian_by_hand  - Use the hand-coded Jacobian of ex5.c, rather than generating it automatically.
 28:       -no_annotation     - Do not annotate ADOL-C active variables. (Should be used alongside -jacobian_by_hand.)
 29:       -adolc_sparse      - Calculate Jacobian in compressed format, using ADOL-C sparse functionality.
 30:       -adolc_sparse_view - Print sparsity pattern used by -adolc_sparse option.
 31:  */
 32: /*
 33:   NOTE: If -adolc_sparse option is used, at least four processors should be used, so that no processor owns boundaries which are
 34:         identified by the periodic boundary conditions. The number of grid points in both x- and y-directions should be multiples
 35:         of 5, in order for the 5-point stencil to be cleanly parallelised.
 36: */

 38: #include <petscdmda.h>
 39: #include <petscts.h>
 40: #include "adolc-utils/drivers.cxx"
 41: #include <adolc/adolc.h>

 43: /* (Passive) field for the two variables */
 44: typedef struct {
 45:   PetscScalar u, v;
 46: } Field;

 48: /* Active field for the two variables */
 49: typedef struct {
 50:   adouble u, v;
 51: } AField;

 53: /* Application context */
 54: typedef struct {
 55:   PetscReal D1, D2, gamma, kappa;
 56:   AField  **u_a, **f_a;
 57:   PetscBool aijpc;
 58:   AdolcCtx *adctx; /* Automatic differentation support */
 59: } AppCtx;

 61: extern PetscErrorCode InitialConditions(DM da, Vec U);
 62: extern PetscErrorCode InitializeLambda(DM da, Vec lambda, PetscReal x, PetscReal y);
 63: extern PetscErrorCode IFunctionLocalPassive(DMDALocalInfo *info, PetscReal t, Field **u, Field **udot, Field **f, void *ptr);
 64: extern PetscErrorCode IFunctionActive(TS ts, PetscReal ftime, Vec U, Vec Udot, Vec F, void *ptr);
 65: extern PetscErrorCode RHSFunctionActive(TS ts, PetscReal ftime, Vec U, Vec F, void *ptr);
 66: extern PetscErrorCode RHSFunctionPassive(TS ts, PetscReal ftime, Vec U, Vec F, void *ptr);
 67: extern PetscErrorCode IJacobianByHand(TS ts, PetscReal t, Vec U, Vec Udot, PetscReal a, Mat A, Mat B, void *ctx);
 68: extern PetscErrorCode IJacobianAdolc(TS ts, PetscReal t, Vec U, Vec Udot, PetscReal a, Mat A, Mat B, void *ctx);
 69: extern PetscErrorCode RHSJacobianByHand(TS ts, PetscReal t, Vec U, Mat A, Mat B, void *ctx);
 70: extern PetscErrorCode RHSJacobianAdolc(TS ts, PetscReal t, Vec U, Mat A, Mat B, void *ctx);

 72: int main(int argc, char **argv)
 73: {
 74:   TS             ts;
 75:   Vec            x, r, xdot;
 76:   DM             da;
 77:   AppCtx         appctx;
 78:   AdolcCtx      *adctx;
 79:   Vec            lambda[1];
 80:   PetscBool      forwardonly = PETSC_FALSE, implicitform = PETSC_FALSE, byhand = PETSC_FALSE;
 81:   PetscInt       gxm, gym, i, dofs = 2, ctrl[3] = {0, 0, 0};
 82:   PetscScalar  **Seed = NULL, **Rec = NULL, *u_vec;
 83:   unsigned int **JP = NULL;
 84:   ISColoring     iscoloring;

 86:   PetscFunctionBeginUser;
 87:   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
 88:   PetscCall(PetscNew(&adctx));
 89:   PetscCall(PetscOptionsGetBool(NULL, NULL, "-forwardonly", &forwardonly, NULL));
 90:   PetscCall(PetscOptionsGetBool(NULL, NULL, "-implicitform", &implicitform, NULL));
 91:   appctx.aijpc = PETSC_FALSE, adctx->no_an = PETSC_FALSE, adctx->sparse = PETSC_FALSE, adctx->sparse_view = PETSC_FALSE;
 92:   adctx->sparse_view_done = PETSC_FALSE;
 93:   PetscCall(PetscOptionsGetBool(NULL, NULL, "-aijpc", &appctx.aijpc, NULL));
 94:   PetscCall(PetscOptionsGetBool(NULL, NULL, "-no_annotation", &adctx->no_an, NULL));
 95:   PetscCall(PetscOptionsGetBool(NULL, NULL, "-jacobian_by_hand", &byhand, NULL));
 96:   PetscCall(PetscOptionsGetBool(NULL, NULL, "-adolc_sparse", &adctx->sparse, NULL));
 97:   PetscCall(PetscOptionsGetBool(NULL, NULL, "-adolc_sparse_view", &adctx->sparse_view, NULL));
 98:   appctx.D1    = 8.0e-5;
 99:   appctx.D2    = 4.0e-5;
100:   appctx.gamma = .024;
101:   appctx.kappa = .06;
102:   appctx.adctx = adctx;

104:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
105:      Create distributed array (DMDA) to manage parallel grid and vectors
106:   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
107:   PetscCall(DMDACreate2d(PETSC_COMM_WORLD, DM_BOUNDARY_PERIODIC, DM_BOUNDARY_PERIODIC, DMDA_STENCIL_STAR, 65, 65, PETSC_DECIDE, PETSC_DECIDE, 2, 1, NULL, NULL, &da));
108:   PetscCall(DMSetFromOptions(da));
109:   PetscCall(DMSetUp(da));
110:   PetscCall(DMDASetFieldName(da, 0, "u"));
111:   PetscCall(DMDASetFieldName(da, 1, "v"));

113:   /*  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
114:      Extract global vectors from DMDA; then duplicate for remaining
115:      vectors that are the same types
116:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
117:   PetscCall(DMCreateGlobalVector(da, &x));
118:   PetscCall(VecDuplicate(x, &r));
119:   PetscCall(VecDuplicate(x, &xdot));

121:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
122:      Create timestepping solver context
123:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
124:   PetscCall(TSCreate(PETSC_COMM_WORLD, &ts));
125:   PetscCall(TSSetType(ts, TSCN));
126:   PetscCall(TSSetDM(ts, da));
127:   PetscCall(TSSetProblemType(ts, TS_NONLINEAR));
128:   if (!implicitform) {
129:     PetscCall(TSSetRHSFunction(ts, NULL, RHSFunctionPassive, &appctx));
130:   } else {
131:     PetscCall(DMDATSSetIFunctionLocal(da, INSERT_VALUES, (DMDATSIFunctionLocalFn *)IFunctionLocalPassive, &appctx));
132:   }

134:   if (!adctx->no_an) {
135:     PetscCall(DMDAGetGhostCorners(da, NULL, NULL, NULL, &gxm, &gym, NULL));
136:     adctx->m = dofs * gxm * gym;
137:     adctx->n = dofs * gxm * gym; /* Number of dependent and independent variables */

139:     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
140:        Trace function(s) just once
141:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
142:     if (!implicitform) {
143:       PetscCall(RHSFunctionActive(ts, 1.0, x, r, &appctx));
144:     } else {
145:       PetscCall(IFunctionActive(ts, 1.0, x, xdot, r, &appctx));
146:     }

148:     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
149:       In the case where ADOL-C generates the Jacobian in compressed format,
150:       seed and recovery matrices are required. Since the sparsity structure
151:       of the Jacobian does not change over the course of the time
152:       integration, we can save computational effort by only generating
153:       these objects once.
154:        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
155:     if (adctx->sparse) {
156:       /*
157:          Generate sparsity pattern

159:          One way to do this is to use the Jacobian pattern driver `jac_pat`
160:          provided by ColPack. Otherwise, it is the responsibility of the user
161:          to obtain the sparsity pattern.
162:       */
163:       PetscCall(PetscMalloc1(adctx->n, &u_vec));
164:       JP = (unsigned int **)malloc(adctx->m * sizeof(unsigned int *));
165:       jac_pat(1, adctx->m, adctx->n, u_vec, JP, ctrl);
166:       if (adctx->sparse_view) PetscCall(PrintSparsity(MPI_COMM_WORLD, adctx->m, JP));

168:       /*
169:         Extract a column colouring

171:         For problems using DMDA, colourings can be extracted directly, as
172:         follows. There are also ColPack drivers available. Otherwise, it is the
173:         responsibility of the user to obtain a suitable colouring.
174:       */
175:       PetscCall(DMCreateColoring(da, IS_COLORING_LOCAL, &iscoloring));
176:       PetscCall(ISColoringGetIS(iscoloring, PETSC_USE_POINTER, &adctx->p, NULL));

178:       /* Generate seed matrix to propagate through the forward mode of AD */
179:       PetscCall(AdolcMalloc2(adctx->n, adctx->p, &Seed));
180:       PetscCall(GenerateSeedMatrix(iscoloring, Seed));
181:       PetscCall(ISColoringDestroy(&iscoloring));

183:       /*
184:         Generate recovery matrix, which is used to recover the Jacobian from
185:         compressed format */
186:       PetscCall(AdolcMalloc2(adctx->m, adctx->p, &Rec));
187:       PetscCall(GetRecoveryMatrix(Seed, JP, adctx->m, adctx->p, Rec));

189:       /* Store results and free workspace */
190:       adctx->Rec = Rec;
191:       for (i = 0; i < adctx->m; i++) free(JP[i]);
192:       free(JP);
193:       PetscCall(PetscFree(u_vec));

195:     } else {
196:       /*
197:         In 'full' Jacobian mode, propagate an identity matrix through the
198:         forward mode of AD.
199:       */
200:       adctx->p = adctx->n;
201:       PetscCall(AdolcMalloc2(adctx->n, adctx->p, &Seed));
202:       PetscCall(Identity(adctx->n, Seed));
203:     }
204:     adctx->Seed = Seed;
205:   }

207:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
208:      Set Jacobian
209:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
210:   if (!implicitform) {
211:     if (!byhand) {
212:       PetscCall(TSSetRHSJacobian(ts, NULL, NULL, RHSJacobianAdolc, &appctx));
213:     } else {
214:       PetscCall(TSSetRHSJacobian(ts, NULL, NULL, RHSJacobianByHand, &appctx));
215:     }
216:   } else {
217:     if (appctx.aijpc) {
218:       Mat A, B;

220:       PetscCall(DMSetMatType(da, MATSELL));
221:       PetscCall(DMCreateMatrix(da, &A));
222:       PetscCall(MatConvert(A, MATAIJ, MAT_INITIAL_MATRIX, &B));
223:       /* FIXME do we need to change viewer to display matrix in natural ordering as DMCreateMatrix_DA does? */
224:       if (!byhand) {
225:         PetscCall(TSSetIJacobian(ts, A, B, IJacobianAdolc, &appctx));
226:       } else {
227:         PetscCall(TSSetIJacobian(ts, A, B, IJacobianByHand, &appctx));
228:       }
229:       PetscCall(MatDestroy(&A));
230:       PetscCall(MatDestroy(&B));
231:     } else {
232:       if (!byhand) {
233:         PetscCall(TSSetIJacobian(ts, NULL, NULL, IJacobianAdolc, &appctx));
234:       } else {
235:         PetscCall(TSSetIJacobian(ts, NULL, NULL, IJacobianByHand, &appctx));
236:       }
237:     }
238:   }

240:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
241:      Set initial conditions
242:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
243:   PetscCall(InitialConditions(da, x));
244:   PetscCall(TSSetSolution(ts, x));

246:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
247:     Have the TS save its trajectory so that TSAdjointSolve() may be used
248:     and set solver options
249:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
250:   if (!forwardonly) {
251:     PetscCall(TSSetSaveTrajectory(ts));
252:     PetscCall(TSSetMaxTime(ts, 200.0));
253:     PetscCall(TSSetTimeStep(ts, 0.5));
254:   } else {
255:     PetscCall(TSSetMaxTime(ts, 2000.0));
256:     PetscCall(TSSetTimeStep(ts, 10));
257:   }
258:   PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_STEPOVER));
259:   PetscCall(TSSetFromOptions(ts));

261:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
262:      Solve ODE system
263:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
264:   PetscCall(TSSolve(ts, x));
265:   if (!forwardonly) {
266:     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
267:        Start the Adjoint model
268:        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
269:     PetscCall(VecDuplicate(x, &lambda[0]));
270:     /*   Reset initial conditions for the adjoint integration */
271:     PetscCall(InitializeLambda(da, lambda[0], 0.5, 0.5));
272:     PetscCall(TSSetCostGradients(ts, 1, lambda, NULL));
273:     PetscCall(TSAdjointSolve(ts));
274:     PetscCall(VecDestroy(&lambda[0]));
275:   }

277:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
278:      Free work space.
279:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
280:   PetscCall(VecDestroy(&xdot));
281:   PetscCall(VecDestroy(&r));
282:   PetscCall(VecDestroy(&x));
283:   PetscCall(TSDestroy(&ts));
284:   if (!adctx->no_an) {
285:     if (adctx->sparse) PetscCall(AdolcFree2(Rec));
286:     PetscCall(AdolcFree2(Seed));
287:   }
288:   PetscCall(DMDestroy(&da));
289:   PetscCall(PetscFree(adctx));
290:   PetscCall(PetscFinalize());
291:   return 0;
292: }

294: PetscErrorCode InitialConditions(DM da, Vec U)
295: {
296:   PetscInt  i, j, xs, ys, xm, ym, Mx, My;
297:   Field   **u;
298:   PetscReal hx, hy, x, y;

300:   PetscFunctionBegin;
301:   PetscCall(DMDAGetInfo(da, PETSC_IGNORE, &Mx, &My, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE));

303:   hx = 2.5 / (PetscReal)Mx;
304:   hy = 2.5 / (PetscReal)My;

306:   /*
307:      Get pointers to vector data
308:   */
309:   PetscCall(DMDAVecGetArray(da, U, &u));

311:   /*
312:      Get local grid boundaries
313:   */
314:   PetscCall(DMDAGetCorners(da, &xs, &ys, NULL, &xm, &ym, NULL));

316:   /*
317:      Compute function over the locally owned part of the grid
318:   */
319:   for (j = ys; j < ys + ym; j++) {
320:     y = j * hy;
321:     for (i = xs; i < xs + xm; i++) {
322:       x = i * hx;
323:       if (PetscApproximateGTE(x, 1.0) && PetscApproximateLTE(x, 1.5) && PetscApproximateGTE(y, 1.0) && PetscApproximateLTE(y, 1.5))
324:         u[j][i].v = PetscPowReal(PetscSinReal(4.0 * PETSC_PI * x), 2.0) * PetscPowReal(PetscSinReal(4.0 * PETSC_PI * y), 2.0) / 4.0;
325:       else u[j][i].v = 0.0;

327:       u[j][i].u = 1.0 - 2.0 * u[j][i].v;
328:     }
329:   }

331:   /*
332:      Restore vectors
333:   */
334:   PetscCall(DMDAVecRestoreArray(da, U, &u));
335:   PetscFunctionReturn(PETSC_SUCCESS);
336: }

338: PetscErrorCode InitializeLambda(DM da, Vec lambda, PetscReal x, PetscReal y)
339: {
340:   PetscInt i, j, Mx, My, xs, ys, xm, ym;
341:   Field  **l;

343:   PetscFunctionBegin;
344:   PetscCall(DMDAGetInfo(da, PETSC_IGNORE, &Mx, &My, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE));
345:   /* locate the global i index for x and j index for y */
346:   i = (PetscInt)(x * (Mx - 1));
347:   j = (PetscInt)(y * (My - 1));
348:   PetscCall(DMDAGetCorners(da, &xs, &ys, NULL, &xm, &ym, NULL));

350:   if (xs <= i && i < xs + xm && ys <= j && j < ys + ym) {
351:     /* the i,j vertex is on this process */
352:     PetscCall(DMDAVecGetArray(da, lambda, &l));
353:     l[j][i].u = 1.0;
354:     l[j][i].v = 1.0;
355:     PetscCall(DMDAVecRestoreArray(da, lambda, &l));
356:   }
357:   PetscFunctionReturn(PETSC_SUCCESS);
358: }

360: PetscErrorCode IFunctionLocalPassive(DMDALocalInfo *info, PetscReal t, Field **u, Field **udot, Field **f, void *ptr)
361: {
362:   AppCtx     *appctx = (AppCtx *)ptr;
363:   PetscInt    i, j, xs, ys, xm, ym;
364:   PetscReal   hx, hy, sx, sy;
365:   PetscScalar uc, uxx, uyy, vc, vxx, vyy;

367:   PetscFunctionBegin;
368:   hx = 2.50 / (PetscReal)info->mx;
369:   sx = 1.0 / (hx * hx);
370:   hy = 2.50 / (PetscReal)info->my;
371:   sy = 1.0 / (hy * hy);

373:   /* Get local grid boundaries */
374:   xs = info->xs;
375:   xm = info->xm;
376:   ys = info->ys;
377:   ym = info->ym;

379:   /* Compute function over the locally owned part of the grid */
380:   for (j = ys; j < ys + ym; j++) {
381:     for (i = xs; i < xs + xm; i++) {
382:       uc        = u[j][i].u;
383:       uxx       = (-2.0 * uc + u[j][i - 1].u + u[j][i + 1].u) * sx;
384:       uyy       = (-2.0 * uc + u[j - 1][i].u + u[j + 1][i].u) * sy;
385:       vc        = u[j][i].v;
386:       vxx       = (-2.0 * vc + u[j][i - 1].v + u[j][i + 1].v) * sx;
387:       vyy       = (-2.0 * vc + u[j - 1][i].v + u[j + 1][i].v) * sy;
388:       f[j][i].u = udot[j][i].u - appctx->D1 * (uxx + uyy) + uc * vc * vc - appctx->gamma * (1.0 - uc);
389:       f[j][i].v = udot[j][i].v - appctx->D2 * (vxx + vyy) - uc * vc * vc + (appctx->gamma + appctx->kappa) * vc;
390:     }
391:   }
392:   PetscCall(PetscLogFlops(16.0 * xm * ym));
393:   PetscFunctionReturn(PETSC_SUCCESS);
394: }

396: PetscErrorCode IFunctionActive(TS ts, PetscReal ftime, Vec U, Vec Udot, Vec F, void *ptr)
397: {
398:   AppCtx       *appctx = (AppCtx *)ptr;
399:   DM            da;
400:   DMDALocalInfo info;
401:   Field       **u, **f, **udot;
402:   Vec           localU;
403:   PetscInt      i, j, xs, ys, xm, ym, gxs, gys, gxm, gym;
404:   PetscReal     hx, hy, sx, sy;
405:   adouble       uc, uxx, uyy, vc, vxx, vyy;
406:   AField      **f_a, *f_c, **u_a, *u_c;
407:   PetscScalar   dummy;

409:   PetscFunctionBegin;
410:   PetscCall(TSGetDM(ts, &da));
411:   PetscCall(DMDAGetLocalInfo(da, &info));
412:   PetscCall(DMGetLocalVector(da, &localU));
413:   hx  = 2.50 / (PetscReal)info.mx;
414:   sx  = 1.0 / (hx * hx);
415:   hy  = 2.50 / (PetscReal)info.my;
416:   sy  = 1.0 / (hy * hy);
417:   xs  = info.xs;
418:   xm  = info.xm;
419:   gxs = info.gxs;
420:   gxm = info.gxm;
421:   ys  = info.ys;
422:   ym  = info.ym;
423:   gys = info.gys;
424:   gym = info.gym;

426:   /*
427:      Scatter ghost points to local vector,using the 2-step process
428:         DMGlobalToLocalBegin(),DMGlobalToLocalEnd().
429:      By placing code between these two statements, computations can be
430:      done while messages are in transition.
431:   */
432:   PetscCall(DMGlobalToLocalBegin(da, U, INSERT_VALUES, localU));
433:   PetscCall(DMGlobalToLocalEnd(da, U, INSERT_VALUES, localU));

435:   /*
436:      Get pointers to vector data
437:   */
438:   PetscCall(DMDAVecGetArrayRead(da, localU, &u));
439:   PetscCall(DMDAVecGetArray(da, F, &f));
440:   PetscCall(DMDAVecGetArrayRead(da, Udot, &udot));

442:   /*
443:     Create contiguous 1-arrays of AFields

445:     NOTE: Memory for ADOL-C active variables (such as adouble and AField)
446:           cannot be allocated using PetscMalloc, as this does not call the
447:           relevant class constructor. Instead, we use the C++ keyword `new`.
448:   */
449:   u_c = new AField[info.gxm * info.gym];
450:   f_c = new AField[info.gxm * info.gym];

452:   /* Create corresponding 2-arrays of AFields */
453:   u_a = new AField *[info.gym];
454:   f_a = new AField *[info.gym];

456:   /* Align indices between array types to endow 2d array with ghost points */
457:   PetscCall(GiveGhostPoints(da, u_c, &u_a));
458:   PetscCall(GiveGhostPoints(da, f_c, &f_a));

460:   trace_on(1); /* Start of active section on tape 1 */

462:   /*
463:     Mark independence

465:     NOTE: Ghost points are marked as independent, in place of the points they represent on
466:           other processors / on other boundaries.
467:   */
468:   for (j = gys; j < gys + gym; j++) {
469:     for (i = gxs; i < gxs + gxm; i++) {
470:       u_a[j][i].u <<= u[j][i].u;
471:       u_a[j][i].v <<= u[j][i].v;
472:     }
473:   }

475:   /* Compute function over the locally owned part of the grid */
476:   for (j = ys; j < ys + ym; j++) {
477:     for (i = xs; i < xs + xm; i++) {
478:       uc          = u_a[j][i].u;
479:       uxx         = (-2.0 * uc + u_a[j][i - 1].u + u_a[j][i + 1].u) * sx;
480:       uyy         = (-2.0 * uc + u_a[j - 1][i].u + u_a[j + 1][i].u) * sy;
481:       vc          = u_a[j][i].v;
482:       vxx         = (-2.0 * vc + u_a[j][i - 1].v + u_a[j][i + 1].v) * sx;
483:       vyy         = (-2.0 * vc + u_a[j - 1][i].v + u_a[j + 1][i].v) * sy;
484:       f_a[j][i].u = udot[j][i].u - appctx->D1 * (uxx + uyy) + uc * vc * vc - appctx->gamma * (1.0 - uc);
485:       f_a[j][i].v = udot[j][i].v - appctx->D2 * (vxx + vyy) - uc * vc * vc + (appctx->gamma + appctx->kappa) * vc;
486:     }
487:   }

489:   /*
490:     Mark dependence

492:     NOTE: Marking dependence of dummy variables makes the index notation much simpler when forming
493:           the Jacobian later.
494:   */
495:   for (j = gys; j < gys + gym; j++) {
496:     for (i = gxs; i < gxs + gxm; i++) {
497:       if ((i < xs) || (i >= xs + xm) || (j < ys) || (j >= ys + ym)) {
498:         f_a[j][i].u >>= dummy;
499:         f_a[j][i].v >>= dummy;
500:       } else {
501:         f_a[j][i].u >>= f[j][i].u;
502:         f_a[j][i].v >>= f[j][i].v;
503:       }
504:     }
505:   }
506:   trace_off(); /* End of active section */
507:   PetscCall(PetscLogFlops(16.0 * xm * ym));

509:   /* Restore vectors */
510:   PetscCall(DMDAVecRestoreArray(da, F, &f));
511:   PetscCall(DMDAVecRestoreArrayRead(da, localU, &u));
512:   PetscCall(DMDAVecRestoreArrayRead(da, Udot, &udot));

514:   PetscCall(DMRestoreLocalVector(da, &localU));

516:   /* Destroy AFields appropriately */
517:   f_a += info.gys;
518:   u_a += info.gys;
519:   delete[] f_a;
520:   delete[] u_a;
521:   delete[] f_c;
522:   delete[] u_c;
523:   PetscFunctionReturn(PETSC_SUCCESS);
524: }

526: PetscErrorCode RHSFunctionPassive(TS ts, PetscReal ftime, Vec U, Vec F, void *ptr)
527: {
528:   AppCtx     *appctx = (AppCtx *)ptr;
529:   DM          da;
530:   PetscInt    i, j, xs, ys, xm, ym, Mx, My;
531:   PetscReal   hx, hy, sx, sy;
532:   PetscScalar uc, uxx, uyy, vc, vxx, vyy;
533:   Field     **u, **f;
534:   Vec         localU, localF;

536:   PetscFunctionBegin;
537:   PetscCall(TSGetDM(ts, &da));
538:   PetscCall(DMDAGetInfo(da, PETSC_IGNORE, &Mx, &My, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE));
539:   hx = 2.50 / (PetscReal)Mx;
540:   sx = 1.0 / (hx * hx);
541:   hy = 2.50 / (PetscReal)My;
542:   sy = 1.0 / (hy * hy);
543:   PetscCall(DMGetLocalVector(da, &localU));
544:   PetscCall(DMGetLocalVector(da, &localF));

546:   /*
547:      Scatter ghost points to local vector,using the 2-step process
548:         DMGlobalToLocalBegin(),DMGlobalToLocalEnd().
549:      By placing code between these two statements, computations can be
550:      done while messages are in transition.
551:   */
552:   PetscCall(DMGlobalToLocalBegin(da, U, INSERT_VALUES, localU));
553:   PetscCall(DMGlobalToLocalEnd(da, U, INSERT_VALUES, localU));
554:   PetscCall(VecZeroEntries(F)); // NOTE (1): See (2) below
555:   PetscCall(DMGlobalToLocalBegin(da, F, INSERT_VALUES, localF));
556:   PetscCall(DMGlobalToLocalEnd(da, F, INSERT_VALUES, localF));

558:   /*
559:      Get pointers to vector data
560:   */
561:   PetscCall(DMDAVecGetArrayRead(da, localU, &u));
562:   PetscCall(DMDAVecGetArray(da, localF, &f));

564:   /*
565:      Get local grid boundaries
566:   */
567:   PetscCall(DMDAGetCorners(da, &xs, &ys, NULL, &xm, &ym, NULL));

569:   /*
570:      Compute function over the locally owned part of the grid
571:   */
572:   for (j = ys; j < ys + ym; j++) {
573:     for (i = xs; i < xs + xm; i++) {
574:       uc        = u[j][i].u;
575:       uxx       = (-2.0 * uc + u[j][i - 1].u + u[j][i + 1].u) * sx;
576:       uyy       = (-2.0 * uc + u[j - 1][i].u + u[j + 1][i].u) * sy;
577:       vc        = u[j][i].v;
578:       vxx       = (-2.0 * vc + u[j][i - 1].v + u[j][i + 1].v) * sx;
579:       vyy       = (-2.0 * vc + u[j - 1][i].v + u[j + 1][i].v) * sy;
580:       f[j][i].u = appctx->D1 * (uxx + uyy) - uc * vc * vc + appctx->gamma * (1.0 - uc);
581:       f[j][i].v = appctx->D2 * (vxx + vyy) + uc * vc * vc - (appctx->gamma + appctx->kappa) * vc;
582:     }
583:   }

585:   /*
586:      Gather global vector, using the 2-step process
587:         DMLocalToGlobalBegin(),DMLocalToGlobalEnd().

589:      NOTE (2): We need to use ADD_VALUES if boundaries are not of type DM_BOUNDARY_NONE or
590:                DM_BOUNDARY_GHOSTED, meaning we should also zero F before addition (see (1) above).
591:   */
592:   PetscCall(DMLocalToGlobalBegin(da, localF, ADD_VALUES, F));
593:   PetscCall(DMLocalToGlobalEnd(da, localF, ADD_VALUES, F));

595:   /*
596:      Restore vectors
597:   */
598:   PetscCall(DMDAVecRestoreArray(da, localF, &f));
599:   PetscCall(DMDAVecRestoreArrayRead(da, localU, &u));
600:   PetscCall(DMRestoreLocalVector(da, &localF));
601:   PetscCall(DMRestoreLocalVector(da, &localU));
602:   PetscCall(PetscLogFlops(16.0 * xm * ym));
603:   PetscFunctionReturn(PETSC_SUCCESS);
604: }

606: PetscErrorCode RHSFunctionActive(TS ts, PetscReal ftime, Vec U, Vec F, void *ptr)
607: {
608:   AppCtx   *appctx = (AppCtx *)ptr;
609:   DM        da;
610:   PetscInt  i, j, xs, ys, xm, ym, gxs, gys, gxm, gym, Mx, My;
611:   PetscReal hx, hy, sx, sy;
612:   AField  **f_a, *f_c, **u_a, *u_c;
613:   adouble   uc, uxx, uyy, vc, vxx, vyy;
614:   Field   **u, **f;
615:   Vec       localU, localF;

617:   PetscFunctionBegin;
618:   PetscCall(TSGetDM(ts, &da));
619:   PetscCall(DMDAGetInfo(da, PETSC_IGNORE, &Mx, &My, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE));
620:   hx = 2.50 / (PetscReal)Mx;
621:   sx = 1.0 / (hx * hx);
622:   hy = 2.50 / (PetscReal)My;
623:   sy = 1.0 / (hy * hy);
624:   PetscCall(DMGetLocalVector(da, &localU));
625:   PetscCall(DMGetLocalVector(da, &localF));

627:   /*
628:      Scatter ghost points to local vector,using the 2-step process
629:         DMGlobalToLocalBegin(),DMGlobalToLocalEnd().
630:      By placing code between these two statements, computations can be
631:      done while messages are in transition.
632:   */
633:   PetscCall(DMGlobalToLocalBegin(da, U, INSERT_VALUES, localU));
634:   PetscCall(DMGlobalToLocalEnd(da, U, INSERT_VALUES, localU));
635:   PetscCall(VecZeroEntries(F)); // NOTE (1): See (2) below
636:   PetscCall(DMGlobalToLocalBegin(da, F, INSERT_VALUES, localF));
637:   PetscCall(DMGlobalToLocalEnd(da, F, INSERT_VALUES, localF));

639:   /*
640:      Get pointers to vector data
641:   */
642:   PetscCall(DMDAVecGetArrayRead(da, localU, &u));
643:   PetscCall(DMDAVecGetArray(da, localF, &f));

645:   /*
646:      Get local and ghosted grid boundaries
647:   */
648:   PetscCall(DMDAGetGhostCorners(da, &gxs, &gys, NULL, &gxm, &gym, NULL));
649:   PetscCall(DMDAGetCorners(da, &xs, &ys, NULL, &xm, &ym, NULL));

651:   /*
652:     Create contiguous 1-arrays of AFields

654:     NOTE: Memory for ADOL-C active variables (such as adouble and AField)
655:           cannot be allocated using PetscMalloc, as this does not call the
656:           relevant class constructor. Instead, we use the C++ keyword `new`.
657:   */
658:   u_c = new AField[gxm * gym];
659:   f_c = new AField[gxm * gym];

661:   /* Create corresponding 2-arrays of AFields */
662:   u_a = new AField *[gym];
663:   f_a = new AField *[gym];

665:   /* Align indices between array types to endow 2d array with ghost points */
666:   PetscCall(GiveGhostPoints(da, u_c, &u_a));
667:   PetscCall(GiveGhostPoints(da, f_c, &f_a));

669:   /*
670:      Compute function over the locally owned part of the grid
671:   */
672:   trace_on(1); // ----------------------------------------------- Start of active section

674:   /*
675:     Mark independence

677:     NOTE: Ghost points are marked as independent, in place of the points they represent on
678:           other processors / on other boundaries.
679:   */
680:   for (j = gys; j < gys + gym; j++) {
681:     for (i = gxs; i < gxs + gxm; i++) {
682:       u_a[j][i].u <<= u[j][i].u;
683:       u_a[j][i].v <<= u[j][i].v;
684:     }
685:   }

687:   /*
688:     Compute function over the locally owned part of the grid
689:   */
690:   for (j = ys; j < ys + ym; j++) {
691:     for (i = xs; i < xs + xm; i++) {
692:       uc          = u_a[j][i].u;
693:       uxx         = (-2.0 * uc + u_a[j][i - 1].u + u_a[j][i + 1].u) * sx;
694:       uyy         = (-2.0 * uc + u_a[j - 1][i].u + u_a[j + 1][i].u) * sy;
695:       vc          = u_a[j][i].v;
696:       vxx         = (-2.0 * vc + u_a[j][i - 1].v + u_a[j][i + 1].v) * sx;
697:       vyy         = (-2.0 * vc + u_a[j - 1][i].v + u_a[j + 1][i].v) * sy;
698:       f_a[j][i].u = appctx->D1 * (uxx + uyy) - uc * vc * vc + appctx->gamma * (1.0 - uc);
699:       f_a[j][i].v = appctx->D2 * (vxx + vyy) + uc * vc * vc - (appctx->gamma + appctx->kappa) * vc;
700:     }
701:   }
702:   /*
703:     Mark dependence

705:     NOTE: Ghost points are marked as dependent in order to vastly simplify index notation
706:           during Jacobian assembly.
707:   */
708:   for (j = gys; j < gys + gym; j++) {
709:     for (i = gxs; i < gxs + gxm; i++) {
710:       f_a[j][i].u >>= f[j][i].u;
711:       f_a[j][i].v >>= f[j][i].v;
712:     }
713:   }
714:   trace_off(); // ----------------------------------------------- End of active section

716:   /* Test zeroth order scalar evaluation in ADOL-C gives the same result */
717:   //  if (appctx->adctx->zos) {
718:   //    PetscCall(TestZOS2d(da,f,u,appctx)); // FIXME: Why does this give nonzero?
719:   //  }

721:   /*
722:      Gather global vector, using the 2-step process
723:         DMLocalToGlobalBegin(),DMLocalToGlobalEnd().

725:      NOTE (2): We need to use ADD_VALUES if boundaries are not of type DM_BOUNDARY_NONE or
726:                DM_BOUNDARY_GHOSTED, meaning we should also zero F before addition (see (1) above).
727:   */
728:   PetscCall(DMLocalToGlobalBegin(da, localF, ADD_VALUES, F));
729:   PetscCall(DMLocalToGlobalEnd(da, localF, ADD_VALUES, F));

731:   /*
732:      Restore vectors
733:   */
734:   PetscCall(DMDAVecRestoreArray(da, localF, &f));
735:   PetscCall(DMDAVecRestoreArrayRead(da, localU, &u));
736:   PetscCall(DMRestoreLocalVector(da, &localF));
737:   PetscCall(DMRestoreLocalVector(da, &localU));
738:   PetscCall(PetscLogFlops(16.0 * xm * ym));

740:   /* Destroy AFields appropriately */
741:   f_a += gys;
742:   u_a += gys;
743:   delete[] f_a;
744:   delete[] u_a;
745:   delete[] f_c;
746:   delete[] u_c;
747:   PetscFunctionReturn(PETSC_SUCCESS);
748: }

750: PetscErrorCode IJacobianAdolc(TS ts, PetscReal t, Vec U, Vec Udot, PetscReal a, Mat A, Mat B, void *ctx)
751: {
752:   AppCtx            *appctx = (AppCtx *)ctx;
753:   DM                 da;
754:   const PetscScalar *u_vec;
755:   Vec                localU;

757:   PetscFunctionBegin;
758:   PetscCall(TSGetDM(ts, &da));
759:   PetscCall(DMGetLocalVector(da, &localU));

761:   /*
762:      Scatter ghost points to local vector,using the 2-step process
763:         DMGlobalToLocalBegin(),DMGlobalToLocalEnd().
764:      By placing code between these two statements, computations can be
765:      done while messages are in transition.
766:   */
767:   PetscCall(DMGlobalToLocalBegin(da, U, INSERT_VALUES, localU));
768:   PetscCall(DMGlobalToLocalEnd(da, U, INSERT_VALUES, localU));

770:   /* Get pointers to vector data */
771:   PetscCall(VecGetArrayRead(localU, &u_vec));

773:   /*
774:     Compute Jacobian
775:   */
776:   PetscCall(PetscAdolcComputeIJacobianLocalIDMass(1, A, u_vec, a, appctx->adctx));

778:   /*
779:      Restore vectors
780:   */
781:   PetscCall(VecRestoreArrayRead(localU, &u_vec));
782:   PetscCall(DMRestoreLocalVector(da, &localU));
783:   PetscFunctionReturn(PETSC_SUCCESS);
784: }

786: PetscErrorCode IJacobianByHand(TS ts, PetscReal t, Vec U, Vec Udot, PetscReal a, Mat A, Mat B, void *ctx)
787: {
788:   AppCtx     *appctx = (AppCtx *)ctx; /* user-defined application context */
789:   DM          da;
790:   PetscInt    i, j, Mx, My, xs, ys, xm, ym;
791:   PetscReal   hx, hy, sx, sy;
792:   PetscScalar uc, vc;
793:   Field     **u;
794:   Vec         localU;
795:   MatStencil  stencil[6], rowstencil;
796:   PetscScalar entries[6];

798:   PetscFunctionBegin;
799:   PetscCall(TSGetDM(ts, &da));
800:   PetscCall(DMGetLocalVector(da, &localU));
801:   PetscCall(DMDAGetInfo(da, PETSC_IGNORE, &Mx, &My, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE));

803:   hx = 2.50 / (PetscReal)Mx;
804:   sx = 1.0 / (hx * hx);
805:   hy = 2.50 / (PetscReal)My;
806:   sy = 1.0 / (hy * hy);

808:   /*
809:      Scatter ghost points to local vector,using the 2-step process
810:         DMGlobalToLocalBegin(),DMGlobalToLocalEnd().
811:      By placing code between these two statements, computations can be
812:      done while messages are in transition.
813:   */
814:   PetscCall(DMGlobalToLocalBegin(da, U, INSERT_VALUES, localU));
815:   PetscCall(DMGlobalToLocalEnd(da, U, INSERT_VALUES, localU));

817:   /*
818:      Get pointers to vector data
819:   */
820:   PetscCall(DMDAVecGetArrayRead(da, localU, &u));

822:   /*
823:      Get local grid boundaries
824:   */
825:   PetscCall(DMDAGetCorners(da, &xs, &ys, NULL, &xm, &ym, NULL));

827:   stencil[0].k = 0;
828:   stencil[1].k = 0;
829:   stencil[2].k = 0;
830:   stencil[3].k = 0;
831:   stencil[4].k = 0;
832:   stencil[5].k = 0;
833:   rowstencil.k = 0;
834:   /*
835:      Compute function over the locally owned part of the grid
836:   */
837:   for (j = ys; j < ys + ym; j++) {
838:     stencil[0].j = j - 1;
839:     stencil[1].j = j + 1;
840:     stencil[2].j = j;
841:     stencil[3].j = j;
842:     stencil[4].j = j;
843:     stencil[5].j = j;
844:     rowstencil.k = 0;
845:     rowstencil.j = j;
846:     for (i = xs; i < xs + xm; i++) {
847:       uc = u[j][i].u;
848:       vc = u[j][i].v;

850:       /*      uxx       = (-2.0*uc + u[j][i-1].u + u[j][i+1].u)*sx;
851:       uyy       = (-2.0*uc + u[j-1][i].u + u[j+1][i].u)*sy;

853:       vxx       = (-2.0*vc + u[j][i-1].v + u[j][i+1].v)*sx;
854:       vyy       = (-2.0*vc + u[j-1][i].v + u[j+1][i].v)*sy;
855:        f[j][i].u = appctx->D1*(uxx + uyy) - uc*vc*vc + appctx->gamma*(1.0 - uc);*/

857:       stencil[0].i = i;
858:       stencil[0].c = 0;
859:       entries[0]   = -appctx->D1 * sy;
860:       stencil[1].i = i;
861:       stencil[1].c = 0;
862:       entries[1]   = -appctx->D1 * sy;
863:       stencil[2].i = i - 1;
864:       stencil[2].c = 0;
865:       entries[2]   = -appctx->D1 * sx;
866:       stencil[3].i = i + 1;
867:       stencil[3].c = 0;
868:       entries[3]   = -appctx->D1 * sx;
869:       stencil[4].i = i;
870:       stencil[4].c = 0;
871:       entries[4]   = 2.0 * appctx->D1 * (sx + sy) + vc * vc + appctx->gamma + a;
872:       stencil[5].i = i;
873:       stencil[5].c = 1;
874:       entries[5]   = 2.0 * uc * vc;
875:       rowstencil.i = i;
876:       rowstencil.c = 0;

878:       PetscCall(MatSetValuesStencil(A, 1, &rowstencil, 6, stencil, entries, INSERT_VALUES));
879:       if (appctx->aijpc) PetscCall(MatSetValuesStencil(B, 1, &rowstencil, 6, stencil, entries, INSERT_VALUES));
880:       stencil[0].c = 1;
881:       entries[0]   = -appctx->D2 * sy;
882:       stencil[1].c = 1;
883:       entries[1]   = -appctx->D2 * sy;
884:       stencil[2].c = 1;
885:       entries[2]   = -appctx->D2 * sx;
886:       stencil[3].c = 1;
887:       entries[3]   = -appctx->D2 * sx;
888:       stencil[4].c = 1;
889:       entries[4]   = 2.0 * appctx->D2 * (sx + sy) - 2.0 * uc * vc + appctx->gamma + appctx->kappa + a;
890:       stencil[5].c = 0;
891:       entries[5]   = -vc * vc;

893:       PetscCall(MatSetValuesStencil(A, 1, &rowstencil, 6, stencil, entries, INSERT_VALUES));
894:       if (appctx->aijpc) PetscCall(MatSetValuesStencil(B, 1, &rowstencil, 6, stencil, entries, INSERT_VALUES));
895:       /* f[j][i].v = appctx->D2*(vxx + vyy) + uc*vc*vc - (appctx->gamma + appctx->kappa)*vc; */
896:     }
897:   }

899:   /*
900:      Restore vectors
901:   */
902:   PetscCall(PetscLogFlops(19.0 * xm * ym));
903:   PetscCall(DMDAVecRestoreArrayRead(da, localU, &u));
904:   PetscCall(DMRestoreLocalVector(da, &localU));
905:   PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
906:   PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
907:   PetscCall(MatSetOption(A, MAT_NEW_NONZERO_LOCATION_ERR, PETSC_TRUE));
908:   if (appctx->aijpc) {
909:     PetscCall(MatAssemblyBegin(B, MAT_FINAL_ASSEMBLY));
910:     PetscCall(MatAssemblyEnd(B, MAT_FINAL_ASSEMBLY));
911:     PetscCall(MatSetOption(B, MAT_NEW_NONZERO_LOCATION_ERR, PETSC_TRUE));
912:   }
913:   PetscFunctionReturn(PETSC_SUCCESS);
914: }

916: PetscErrorCode RHSJacobianByHand(TS ts, PetscReal t, Vec U, Mat A, Mat B, void *ctx)
917: {
918:   AppCtx     *appctx = (AppCtx *)ctx; /* user-defined application context */
919:   DM          da;
920:   PetscInt    i, j, Mx, My, xs, ys, xm, ym;
921:   PetscReal   hx, hy, sx, sy;
922:   PetscScalar uc, vc;
923:   Field     **u;
924:   Vec         localU;
925:   MatStencil  stencil[6], rowstencil;
926:   PetscScalar entries[6];

928:   PetscFunctionBegin;
929:   PetscCall(TSGetDM(ts, &da));
930:   PetscCall(DMGetLocalVector(da, &localU));
931:   PetscCall(DMDAGetInfo(da, PETSC_IGNORE, &Mx, &My, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE));

933:   hx = 2.50 / (PetscReal)Mx;
934:   sx = 1.0 / (hx * hx);
935:   hy = 2.50 / (PetscReal)My;
936:   sy = 1.0 / (hy * hy);

938:   /*
939:      Scatter ghost points to local vector,using the 2-step process
940:         DMGlobalToLocalBegin(),DMGlobalToLocalEnd().
941:      By placing code between these two statements, computations can be
942:      done while messages are in transition.
943:   */
944:   PetscCall(DMGlobalToLocalBegin(da, U, INSERT_VALUES, localU));
945:   PetscCall(DMGlobalToLocalEnd(da, U, INSERT_VALUES, localU));

947:   /*
948:      Get pointers to vector data
949:   */
950:   PetscCall(DMDAVecGetArrayRead(da, localU, &u));

952:   /*
953:      Get local grid boundaries
954:   */
955:   PetscCall(DMDAGetCorners(da, &xs, &ys, NULL, &xm, &ym, NULL));

957:   stencil[0].k = 0;
958:   stencil[1].k = 0;
959:   stencil[2].k = 0;
960:   stencil[3].k = 0;
961:   stencil[4].k = 0;
962:   stencil[5].k = 0;
963:   rowstencil.k = 0;

965:   /*
966:      Compute function over the locally owned part of the grid
967:   */
968:   for (j = ys; j < ys + ym; j++) {
969:     stencil[0].j = j - 1;
970:     stencil[1].j = j + 1;
971:     stencil[2].j = j;
972:     stencil[3].j = j;
973:     stencil[4].j = j;
974:     stencil[5].j = j;
975:     rowstencil.k = 0;
976:     rowstencil.j = j;
977:     for (i = xs; i < xs + xm; i++) {
978:       uc = u[j][i].u;
979:       vc = u[j][i].v;

981:       /*      uxx       = (-2.0*uc + u[j][i-1].u + u[j][i+1].u)*sx;
982:       uyy       = (-2.0*uc + u[j-1][i].u + u[j+1][i].u)*sy;

984:       vxx       = (-2.0*vc + u[j][i-1].v + u[j][i+1].v)*sx;
985:       vyy       = (-2.0*vc + u[j-1][i].v + u[j+1][i].v)*sy;
986:        f[j][i].u = appctx->D1*(uxx + uyy) - uc*vc*vc + appctx->gamma*(1.0 - uc);*/

988:       stencil[0].i = i;
989:       stencil[0].c = 0;
990:       entries[0]   = appctx->D1 * sy;
991:       stencil[1].i = i;
992:       stencil[1].c = 0;
993:       entries[1]   = appctx->D1 * sy;
994:       stencil[2].i = i - 1;
995:       stencil[2].c = 0;
996:       entries[2]   = appctx->D1 * sx;
997:       stencil[3].i = i + 1;
998:       stencil[3].c = 0;
999:       entries[3]   = appctx->D1 * sx;
1000:       stencil[4].i = i;
1001:       stencil[4].c = 0;
1002:       entries[4]   = -2.0 * appctx->D1 * (sx + sy) - vc * vc - appctx->gamma;
1003:       stencil[5].i = i;
1004:       stencil[5].c = 1;
1005:       entries[5]   = -2.0 * uc * vc;
1006:       rowstencil.i = i;
1007:       rowstencil.c = 0;

1009:       PetscCall(MatSetValuesStencil(A, 1, &rowstencil, 6, stencil, entries, INSERT_VALUES));

1011:       stencil[0].c = 1;
1012:       entries[0]   = appctx->D2 * sy;
1013:       stencil[1].c = 1;
1014:       entries[1]   = appctx->D2 * sy;
1015:       stencil[2].c = 1;
1016:       entries[2]   = appctx->D2 * sx;
1017:       stencil[3].c = 1;
1018:       entries[3]   = appctx->D2 * sx;
1019:       stencil[4].c = 1;
1020:       entries[4]   = -2.0 * appctx->D2 * (sx + sy) + 2.0 * uc * vc - appctx->gamma - appctx->kappa;
1021:       stencil[5].c = 0;
1022:       entries[5]   = vc * vc;
1023:       rowstencil.c = 1;

1025:       PetscCall(MatSetValuesStencil(A, 1, &rowstencil, 6, stencil, entries, INSERT_VALUES));
1026:       /* f[j][i].v = appctx->D2*(vxx + vyy) + uc*vc*vc - (appctx->gamma + appctx->kappa)*vc; */
1027:     }
1028:   }

1030:   /*
1031:      Restore vectors
1032:   */
1033:   PetscCall(PetscLogFlops(19.0 * xm * ym));
1034:   PetscCall(DMDAVecRestoreArrayRead(da, localU, &u));
1035:   PetscCall(DMRestoreLocalVector(da, &localU));
1036:   PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
1037:   PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
1038:   PetscCall(MatSetOption(A, MAT_NEW_NONZERO_LOCATION_ERR, PETSC_TRUE));
1039:   if (appctx->aijpc) {
1040:     PetscCall(MatAssemblyBegin(B, MAT_FINAL_ASSEMBLY));
1041:     PetscCall(MatAssemblyEnd(B, MAT_FINAL_ASSEMBLY));
1042:     PetscCall(MatSetOption(B, MAT_NEW_NONZERO_LOCATION_ERR, PETSC_TRUE));
1043:   }
1044:   PetscFunctionReturn(PETSC_SUCCESS);
1045: }

1047: PetscErrorCode RHSJacobianAdolc(TS ts, PetscReal t, Vec U, Mat A, Mat B, void *ctx)
1048: {
1049:   AppCtx      *appctx = (AppCtx *)ctx;
1050:   DM           da;
1051:   PetscScalar *u_vec;
1052:   Vec          localU;

1054:   PetscFunctionBegin;
1055:   PetscCall(TSGetDM(ts, &da));
1056:   PetscCall(DMGetLocalVector(da, &localU));

1058:   /*
1059:      Scatter ghost points to local vector,using the 2-step process
1060:         DMGlobalToLocalBegin(),DMGlobalToLocalEnd().
1061:      By placing code between these two statements, computations can be
1062:      done while messages are in transition.
1063:   */
1064:   PetscCall(DMGlobalToLocalBegin(da, U, INSERT_VALUES, localU));
1065:   PetscCall(DMGlobalToLocalEnd(da, U, INSERT_VALUES, localU));

1067:   /* Get pointers to vector data */
1068:   PetscCall(VecGetArray(localU, &u_vec));

1070:   /*
1071:     Compute Jacobian
1072:   */
1073:   PetscCall(PetscAdolcComputeRHSJacobianLocal(1, A, u_vec, appctx->adctx));

1075:   /*
1076:      Restore vectors
1077:   */
1078:   PetscCall(VecRestoreArray(localU, &u_vec));
1079:   PetscCall(DMRestoreLocalVector(da, &localU));
1080:   PetscFunctionReturn(PETSC_SUCCESS);
1081: }

1083: /*TEST

1085:   build:
1086:     requires: double !complex adolc colpack

1088:   test:
1089:     suffix: 1
1090:     nsize: 1
1091:     args: -ts_max_steps 1 -da_grid_x 12 -da_grid_y 12 -snes_test_jacobian
1092:     output_file: output/adr_ex5adj_1.out

1094:   test:
1095:     suffix: 2
1096:     nsize: 1
1097:     args: -ts_max_steps 1 -da_grid_x 12 -da_grid_y 12 -snes_test_jacobian -implicitform
1098:     output_file: output/adr_ex5adj_2.out

1100:   test:
1101:     suffix: 3
1102:     nsize: 4
1103:     args: -ts_max_steps 10 -da_grid_x 12 -da_grid_y 12 -ts_monitor -ts_adjoint_monitor
1104:     output_file: output/adr_ex5adj_3.out

1106:   test:
1107:     suffix: 4
1108:     nsize: 4
1109:     args: -ts_max_steps 10 -da_grid_x 12 -da_grid_y 12 -ts_monitor -ts_adjoint_monitor -implicitform
1110:     output_file: output/adr_ex5adj_4.out

1112:   testset:
1113:     suffix: 5
1114:     nsize: 4
1115:     args: -ts_max_steps 10 -da_grid_x 15 -da_grid_y 15 -ts_monitor -ts_adjoint_monitor -adolc_sparse
1116:     output_file: output/adr_ex5adj_5.out

1118:   testset:
1119:     suffix: 6
1120:     nsize: 4
1121:     args: -ts_max_steps 10 -da_grid_x 15 -da_grid_y 15 -ts_monitor -ts_adjoint_monitor -adolc_sparse -implicitform
1122:     output_file: output/adr_ex5adj_6.out

1124: TEST*/