Actual source code: eptorsion1.c

  1: /* Program usage: mpiexec -n 1 eptorsion1 [-help] [all TAO options] */

  3: /* ----------------------------------------------------------------------

  5:   Elastic-plastic torsion problem.

  7:   The elastic plastic torsion problem arises from the determination
  8:   of the stress field on an infinitely long cylindrical bar, which is
  9:   equivalent to the solution of the following problem:

 11:   min{ .5 * integral(||gradient(v(x))||^2 dx) - C * integral(v(x) dx)}

 13:   where C is the torsion angle per unit length.

 15:   The multiprocessor version of this code is eptorsion2.c.

 17: ---------------------------------------------------------------------- */

 19: /*
 20:   Include "petsctao.h" so that we can use TAO solvers.  Note that this
 21:   file automatically includes files for lower-level support, such as those
 22:   provided by the PETSc library:
 23:      petscsys.h - system routines
 24:      petscis.h  - index sets
 25:      petscvec.h - vectors
 26:      petscmat.h - matrices
 27:      petscpc.h  - preconditioners
 28:      petscksp.h - Krylov subspace methods
 29: */

 31: #include <petsctao.h>

 33: static char help[] = "Demonstrates use of the TAO package to solve \n\
 34: unconstrained minimization problems on a single processor.  This example \n\
 35: is based on the Elastic-Plastic Torsion (dept) problem from the MINPACK-2 \n\
 36: test suite.\n\
 37: The command line options are:\n\
 38:   -mx <xg>, where <xg> = number of grid points in the 1st coordinate direction\n\
 39:   -my <yg>, where <yg> = number of grid points in the 2nd coordinate direction\n\
 40:   -par <param>, where <param> = angle of twist per unit length\n\n";

 42: /*
 43:    User-defined application context - contains data needed by the
 44:    application-provided call-back routines, FormFunction(),
 45:    FormGradient(), and FormHessian().
 46: */

 48: typedef struct {
 49:   PetscReal param;      /* nonlinearity parameter */
 50:   PetscInt  mx, my;     /* discretization in x- and y-directions */
 51:   PetscInt  ndim;       /* problem dimension */
 52:   Vec       s, y, xvec; /* work space for computing Hessian */
 53:   PetscReal hx, hy;     /* mesh spacing in x- and y-directions */
 54: } AppCtx;

 56: /* -------- User-defined Routines --------- */

 58: PetscErrorCode FormInitialGuess(AppCtx *, Vec);
 59: PetscErrorCode FormFunction(Tao, Vec, PetscReal *, void *);
 60: PetscErrorCode FormGradient(Tao, Vec, Vec, void *);
 61: PetscErrorCode FormHessian(Tao, Vec, Mat, Mat, void *);
 62: PetscErrorCode HessianProductMat(Mat, Vec, Vec);
 63: PetscErrorCode HessianProduct(void *, Vec, Vec);
 64: PetscErrorCode MatrixFreeHessian(Tao, Vec, Mat, Mat, void *);
 65: PetscErrorCode FormFunctionGradient(Tao, Vec, PetscReal *, Vec, void *);

 67: int main(int argc, char **argv)
 68: {
 69:   PetscInt    mx = 10; /* discretization in x-direction */
 70:   PetscInt    my = 10; /* discretization in y-direction */
 71:   Vec         x;       /* solution, gradient vectors */
 72:   PetscBool   flg;     /* A return value when checking for use options */
 73:   Tao         tao;     /* Tao solver context */
 74:   Mat         H;       /* Hessian matrix */
 75:   AppCtx      user;    /* application context */
 76:   PetscMPIInt size;    /* number of processes */
 77:   PetscReal   one = 1.0;

 79:   PetscBool test_lmvm = PETSC_FALSE;
 80:   KSP       ksp;
 81:   PC        pc;
 82:   Mat       M;
 83:   Vec       in, out, out2;
 84:   PetscReal mult_solve_dist;

 86:   /* Initialize TAO,PETSc */
 87:   PetscFunctionBeginUser;
 88:   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
 89:   PetscCallMPI(MPI_Comm_size(MPI_COMM_WORLD, &size));
 90:   PetscCheck(size == 1, PETSC_COMM_WORLD, PETSC_ERR_WRONG_MPI_SIZE, "Incorrect number of processors");

 92:   /* Specify default parameters for the problem, check for command-line overrides */
 93:   user.param = 5.0;
 94:   PetscCall(PetscOptionsGetInt(NULL, NULL, "-my", &my, &flg));
 95:   PetscCall(PetscOptionsGetInt(NULL, NULL, "-mx", &mx, &flg));
 96:   PetscCall(PetscOptionsGetReal(NULL, NULL, "-par", &user.param, &flg));
 97:   PetscCall(PetscOptionsGetBool(NULL, NULL, "-test_lmvm", &test_lmvm, &flg));

 99:   PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n---- Elastic-Plastic Torsion Problem -----\n"));
100:   PetscCall(PetscPrintf(PETSC_COMM_SELF, "mx: %" PetscInt_FMT "     my: %" PetscInt_FMT "   \n\n", mx, my));
101:   user.ndim = mx * my;
102:   user.mx   = mx;
103:   user.my   = my;
104:   user.hx   = one / (mx + 1);
105:   user.hy   = one / (my + 1);

107:   /* Allocate vectors */
108:   PetscCall(VecCreateSeq(PETSC_COMM_SELF, user.ndim, &user.y));
109:   PetscCall(VecDuplicate(user.y, &user.s));
110:   PetscCall(VecDuplicate(user.y, &x));

112:   /* Create TAO solver and set desired solution method */
113:   PetscCall(TaoCreate(PETSC_COMM_SELF, &tao));
114:   PetscCall(TaoSetType(tao, TAOLMVM));

116:   /* Set solution vector with an initial guess */
117:   PetscCall(FormInitialGuess(&user, x));
118:   PetscCall(TaoSetSolution(tao, x));

120:   /* Set routine for function and gradient evaluation */
121:   PetscCall(TaoSetObjectiveAndGradient(tao, NULL, FormFunctionGradient, (void *)&user));

123:   /* From command line options, determine if using matrix-free hessian */
124:   PetscCall(PetscOptionsHasName(NULL, NULL, "-my_tao_mf", &flg));
125:   if (flg) {
126:     PetscCall(MatCreateShell(PETSC_COMM_SELF, user.ndim, user.ndim, user.ndim, user.ndim, (void *)&user, &H));
127:     PetscCall(MatShellSetOperation(H, MATOP_MULT, (PetscErrorCodeFn *)HessianProductMat));
128:     PetscCall(MatSetOption(H, MAT_SYMMETRIC, PETSC_TRUE));

130:     PetscCall(TaoSetHessian(tao, H, H, MatrixFreeHessian, (void *)&user));
131:   } else {
132:     PetscCall(MatCreateSeqAIJ(PETSC_COMM_SELF, user.ndim, user.ndim, 5, NULL, &H));
133:     PetscCall(MatSetOption(H, MAT_SYMMETRIC, PETSC_TRUE));
134:     PetscCall(TaoSetHessian(tao, H, H, FormHessian, (void *)&user));
135:   }

137:   /* Test the LMVM matrix */
138:   if (test_lmvm) {
139:     PetscCall(PetscOptionsSetValue(NULL, "-tao_type", "bntr"));
140:     PetscCall(PetscOptionsSetValue(NULL, "-tao_bnk_pc_type", "lmvm"));
141:   }

143:   /* Check for any TAO command line options */
144:   PetscCall(TaoSetFromOptions(tao));

146:   /* SOLVE THE APPLICATION */
147:   PetscCall(TaoSolve(tao));

149:   /* Test the LMVM matrix */
150:   if (test_lmvm) {
151:     PetscCall(TaoGetKSP(tao, &ksp));
152:     PetscCall(KSPGetPC(ksp, &pc));
153:     PetscCall(PCLMVMGetMatLMVM(pc, &M));
154:     PetscCall(VecDuplicate(x, &in));
155:     PetscCall(VecDuplicate(x, &out));
156:     PetscCall(VecDuplicate(x, &out2));
157:     PetscCall(VecSet(in, 5.0));
158:     PetscCall(MatMult(M, in, out));
159:     PetscCall(MatSolve(M, out, out2));
160:     PetscCall(VecAXPY(out2, -1.0, in));
161:     PetscCall(VecNorm(out2, NORM_2, &mult_solve_dist));
162:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)tao), "error between MatMult and MatSolve: %e\n", (double)mult_solve_dist));
163:     PetscCall(VecDestroy(&in));
164:     PetscCall(VecDestroy(&out));
165:     PetscCall(VecDestroy(&out2));
166:   }

168:   PetscCall(TaoDestroy(&tao));
169:   PetscCall(VecDestroy(&user.s));
170:   PetscCall(VecDestroy(&user.y));
171:   PetscCall(VecDestroy(&x));
172:   PetscCall(MatDestroy(&H));

174:   PetscCall(PetscFinalize());
175:   return 0;
176: }

178: /* ------------------------------------------------------------------- */
179: /*
180:     FormInitialGuess - Computes an initial approximation to the solution.

182:     Input Parameters:
183: .   user - user-defined application context
184: .   X    - vector

186:     Output Parameters:
187: .   X    - vector
188: */
189: PetscErrorCode FormInitialGuess(AppCtx *user, Vec X)
190: {
191:   PetscReal hx = user->hx, hy = user->hy, temp;
192:   PetscReal val;
193:   PetscInt  i, j, k, nx = user->mx, ny = user->my;

195:   /* Compute initial guess */
196:   PetscFunctionBeginUser;
197:   for (j = 0; j < ny; j++) {
198:     temp = PetscMin(j + 1, ny - j) * hy;
199:     for (i = 0; i < nx; i++) {
200:       k   = nx * j + i;
201:       val = PetscMin((PetscMin(i + 1, nx - i)) * hx, temp);
202:       PetscCall(VecSetValues(X, 1, &k, &val, ADD_VALUES));
203:     }
204:   }
205:   PetscCall(VecAssemblyBegin(X));
206:   PetscCall(VecAssemblyEnd(X));
207:   PetscFunctionReturn(PETSC_SUCCESS);
208: }

210: /* ------------------------------------------------------------------- */
211: /*
212:    FormFunctionGradient - Evaluates the function and corresponding gradient.

214:    Input Parameters:
215:    tao - the Tao context
216:    X   - the input vector
217:    ptr - optional user-defined context, as set by TaoSetFunction()

219:    Output Parameters:
220:    f   - the newly evaluated function
221:    G   - the newly evaluated gradient
222: */
223: PetscErrorCode FormFunctionGradient(Tao tao, Vec X, PetscReal *f, Vec G, void *ptr)
224: {
225:   PetscFunctionBeginUser;
226:   PetscCall(FormFunction(tao, X, f, ptr));
227:   PetscCall(FormGradient(tao, X, G, ptr));
228:   PetscFunctionReturn(PETSC_SUCCESS);
229: }

231: /* ------------------------------------------------------------------- */
232: /*
233:    FormFunction - Evaluates the function, f(X).

235:    Input Parameters:
236: .  tao - the Tao context
237: .  X   - the input vector
238: .  ptr - optional user-defined context, as set by TaoSetFunction()

240:    Output Parameters:
241: .  f    - the newly evaluated function
242: */
243: PetscErrorCode FormFunction(Tao tao, Vec X, PetscReal *f, void *ptr)
244: {
245:   AppCtx            *user = (AppCtx *)ptr;
246:   PetscReal          hx = user->hx, hy = user->hy, area, three = 3.0, p5 = 0.5;
247:   PetscReal          zero = 0.0, vb, vl, vr, vt, dvdx, dvdy, flin = 0.0, fquad = 0.0;
248:   PetscReal          v, cdiv3 = user->param / three;
249:   const PetscScalar *x;
250:   PetscInt           nx = user->mx, ny = user->my, i, j, k;

252:   PetscFunctionBeginUser;
253:   /* Get pointer to vector data */
254:   PetscCall(VecGetArrayRead(X, &x));

256:   /* Compute function contributions over the lower triangular elements */
257:   for (j = -1; j < ny; j++) {
258:     for (i = -1; i < nx; i++) {
259:       k  = nx * j + i;
260:       v  = zero;
261:       vr = zero;
262:       vt = zero;
263:       if (i >= 0 && j >= 0) v = x[k];
264:       if (i < nx - 1 && j > -1) vr = x[k + 1];
265:       if (i > -1 && j < ny - 1) vt = x[k + nx];
266:       dvdx = (vr - v) / hx;
267:       dvdy = (vt - v) / hy;
268:       fquad += dvdx * dvdx + dvdy * dvdy;
269:       flin -= cdiv3 * (v + vr + vt);
270:     }
271:   }

273:   /* Compute function contributions over the upper triangular elements */
274:   for (j = 0; j <= ny; j++) {
275:     for (i = 0; i <= nx; i++) {
276:       k  = nx * j + i;
277:       vb = zero;
278:       vl = zero;
279:       v  = zero;
280:       if (i < nx && j > 0) vb = x[k - nx];
281:       if (i > 0 && j < ny) vl = x[k - 1];
282:       if (i < nx && j < ny) v = x[k];
283:       dvdx  = (v - vl) / hx;
284:       dvdy  = (v - vb) / hy;
285:       fquad = fquad + dvdx * dvdx + dvdy * dvdy;
286:       flin  = flin - cdiv3 * (vb + vl + v);
287:     }
288:   }

290:   /* Restore vector */
291:   PetscCall(VecRestoreArrayRead(X, &x));

293:   /* Scale the function */
294:   area = p5 * hx * hy;
295:   *f   = area * (p5 * fquad + flin);

297:   PetscCall(PetscLogFlops(24.0 * nx * ny));
298:   PetscFunctionReturn(PETSC_SUCCESS);
299: }

301: /* ------------------------------------------------------------------- */
302: /*
303:     FormGradient - Evaluates the gradient, G(X).

305:     Input Parameters:
306: .   tao  - the Tao context
307: .   X    - input vector
308: .   ptr  - optional user-defined context

310:     Output Parameters:
311: .   G - vector containing the newly evaluated gradient
312: */
313: PetscErrorCode FormGradient(Tao tao, Vec X, Vec G, void *ptr)
314: {
315:   AppCtx            *user = (AppCtx *)ptr;
316:   PetscReal          zero = 0.0, p5 = 0.5, three = 3.0, area, val;
317:   PetscInt           nx = user->mx, ny = user->my, ind, i, j, k;
318:   PetscReal          hx = user->hx, hy = user->hy;
319:   PetscReal          vb, vl, vr, vt, dvdx, dvdy;
320:   PetscReal          v, cdiv3 = user->param / three;
321:   const PetscScalar *x;

323:   PetscFunctionBeginUser;
324:   /* Initialize gradient to zero */
325:   PetscCall(VecSet(G, zero));

327:   /* Get pointer to vector data */
328:   PetscCall(VecGetArrayRead(X, &x));

330:   /* Compute gradient contributions over the lower triangular elements */
331:   for (j = -1; j < ny; j++) {
332:     for (i = -1; i < nx; i++) {
333:       k  = nx * j + i;
334:       v  = zero;
335:       vr = zero;
336:       vt = zero;
337:       if (i >= 0 && j >= 0) v = x[k];
338:       if (i < nx - 1 && j > -1) vr = x[k + 1];
339:       if (i > -1 && j < ny - 1) vt = x[k + nx];
340:       dvdx = (vr - v) / hx;
341:       dvdy = (vt - v) / hy;
342:       if (i != -1 && j != -1) {
343:         ind = k;
344:         val = -dvdx / hx - dvdy / hy - cdiv3;
345:         PetscCall(VecSetValues(G, 1, &ind, &val, ADD_VALUES));
346:       }
347:       if (i != nx - 1 && j != -1) {
348:         ind = k + 1;
349:         val = dvdx / hx - cdiv3;
350:         PetscCall(VecSetValues(G, 1, &ind, &val, ADD_VALUES));
351:       }
352:       if (i != -1 && j != ny - 1) {
353:         ind = k + nx;
354:         val = dvdy / hy - cdiv3;
355:         PetscCall(VecSetValues(G, 1, &ind, &val, ADD_VALUES));
356:       }
357:     }
358:   }

360:   /* Compute gradient contributions over the upper triangular elements */
361:   for (j = 0; j <= ny; j++) {
362:     for (i = 0; i <= nx; i++) {
363:       k  = nx * j + i;
364:       vb = zero;
365:       vl = zero;
366:       v  = zero;
367:       if (i < nx && j > 0) vb = x[k - nx];
368:       if (i > 0 && j < ny) vl = x[k - 1];
369:       if (i < nx && j < ny) v = x[k];
370:       dvdx = (v - vl) / hx;
371:       dvdy = (v - vb) / hy;
372:       if (i != nx && j != 0) {
373:         ind = k - nx;
374:         val = -dvdy / hy - cdiv3;
375:         PetscCall(VecSetValues(G, 1, &ind, &val, ADD_VALUES));
376:       }
377:       if (i != 0 && j != ny) {
378:         ind = k - 1;
379:         val = -dvdx / hx - cdiv3;
380:         PetscCall(VecSetValues(G, 1, &ind, &val, ADD_VALUES));
381:       }
382:       if (i != nx && j != ny) {
383:         ind = k;
384:         val = dvdx / hx + dvdy / hy - cdiv3;
385:         PetscCall(VecSetValues(G, 1, &ind, &val, ADD_VALUES));
386:       }
387:     }
388:   }
389:   PetscCall(VecRestoreArrayRead(X, &x));

391:   /* Assemble gradient vector */
392:   PetscCall(VecAssemblyBegin(G));
393:   PetscCall(VecAssemblyEnd(G));

395:   /* Scale the gradient */
396:   area = p5 * hx * hy;
397:   PetscCall(VecScale(G, area));
398:   PetscCall(PetscLogFlops(24.0 * nx * ny));
399:   PetscFunctionReturn(PETSC_SUCCESS);
400: }

402: /* ------------------------------------------------------------------- */
403: /*
404:    FormHessian - Forms the Hessian matrix.

406:    Input Parameters:
407: .  tao - the Tao context
408: .  X    - the input vector
409: .  ptr  - optional user-defined context, as set by TaoSetHessian()

411:    Output Parameters:
412: .  H     - Hessian matrix
413: .  PrecH - optionally different preconditioning Hessian

415:    Notes:
416:    This routine is intended simply as an example of the interface
417:    to a Hessian evaluation routine.  Since this example compute the
418:    Hessian a column at a time, it is not particularly efficient and
419:    is not recommended.
420: */
421: PetscErrorCode FormHessian(Tao tao, Vec X, Mat H, Mat Hpre, void *ptr)
422: {
423:   AppCtx    *user = (AppCtx *)ptr;
424:   PetscInt   i, j, ndim = user->ndim;
425:   PetscReal *y, zero = 0.0, one = 1.0;
426:   PetscBool  assembled;

428:   PetscFunctionBeginUser;
429:   user->xvec = X;

431:   /* Initialize Hessian entries and work vector to zero */
432:   PetscCall(MatAssembled(H, &assembled));
433:   if (assembled) PetscCall(MatZeroEntries(H));

435:   PetscCall(VecSet(user->s, zero));

437:   /* Loop over matrix columns to compute entries of the Hessian */
438:   for (j = 0; j < ndim; j++) {
439:     PetscCall(VecSetValues(user->s, 1, &j, &one, INSERT_VALUES));
440:     PetscCall(VecAssemblyBegin(user->s));
441:     PetscCall(VecAssemblyEnd(user->s));

443:     PetscCall(HessianProduct(ptr, user->s, user->y));

445:     PetscCall(VecSetValues(user->s, 1, &j, &zero, INSERT_VALUES));
446:     PetscCall(VecAssemblyBegin(user->s));
447:     PetscCall(VecAssemblyEnd(user->s));

449:     PetscCall(VecGetArray(user->y, &y));
450:     for (i = 0; i < ndim; i++) {
451:       if (y[i] != zero) PetscCall(MatSetValues(H, 1, &i, 1, &j, &y[i], ADD_VALUES));
452:     }
453:     PetscCall(VecRestoreArray(user->y, &y));
454:   }
455:   PetscCall(MatAssemblyBegin(H, MAT_FINAL_ASSEMBLY));
456:   PetscCall(MatAssemblyEnd(H, MAT_FINAL_ASSEMBLY));
457:   PetscFunctionReturn(PETSC_SUCCESS);
458: }

460: /* ------------------------------------------------------------------- */
461: /*
462:    MatrixFreeHessian - Sets a pointer for use in computing Hessian-vector
463:    products.

465:    Input Parameters:
466: .  tao - the Tao context
467: .  X    - the input vector
468: .  ptr  - optional user-defined context, as set by TaoSetHessian()

470:    Output Parameters:
471: .  H     - Hessian matrix
472: .  PrecH - optionally different preconditioning Hessian
473: */
474: PetscErrorCode MatrixFreeHessian(Tao tao, Vec X, Mat H, Mat PrecH, void *ptr)
475: {
476:   AppCtx *user = (AppCtx *)ptr;

478:   /* Sets location of vector for use in computing matrix-vector products  of the form H(X)*y  */
479:   PetscFunctionBeginUser;
480:   user->xvec = X;
481:   PetscFunctionReturn(PETSC_SUCCESS);
482: }

484: /* ------------------------------------------------------------------- */
485: /*
486:    HessianProductMat - Computes the matrix-vector product
487:    y = mat*svec.

489:    Input Parameters:
490: .  mat  - input matrix
491: .  svec - input vector

493:    Output Parameters:
494: .  y    - solution vector
495: */
496: PetscErrorCode HessianProductMat(Mat mat, Vec svec, Vec y)
497: {
498:   void *ptr;

500:   PetscFunctionBeginUser;
501:   PetscCall(MatShellGetContext(mat, &ptr));
502:   PetscCall(HessianProduct(ptr, svec, y));
503:   PetscFunctionReturn(PETSC_SUCCESS);
504: }

506: /* ------------------------------------------------------------------- */
507: /*
508:    Hessian Product - Computes the matrix-vector product:
509:    y = f''(x)*svec.

511:    Input Parameters:
512: .  ptr  - pointer to the user-defined context
513: .  svec - input vector

515:    Output Parameters:
516: .  y    - product vector
517: */
518: PetscErrorCode HessianProduct(void *ptr, Vec svec, Vec y)
519: {
520:   AppCtx            *user = (AppCtx *)ptr;
521:   PetscReal          p5 = 0.5, zero = 0.0, one = 1.0, hx, hy, val, area;
522:   const PetscScalar *x, *s;
523:   PetscReal          v, vb, vl, vr, vt, hxhx, hyhy;
524:   PetscInt           nx, ny, i, j, k, ind;

526:   PetscFunctionBeginUser;
527:   nx   = user->mx;
528:   ny   = user->my;
529:   hx   = user->hx;
530:   hy   = user->hy;
531:   hxhx = one / (hx * hx);
532:   hyhy = one / (hy * hy);

534:   /* Get pointers to vector data */
535:   PetscCall(VecGetArrayRead(user->xvec, &x));
536:   PetscCall(VecGetArrayRead(svec, &s));

538:   /* Initialize product vector to zero */
539:   PetscCall(VecSet(y, zero));

541:   /* Compute f''(x)*s over the lower triangular elements */
542:   for (j = -1; j < ny; j++) {
543:     for (i = -1; i < nx; i++) {
544:       k  = nx * j + i;
545:       v  = zero;
546:       vr = zero;
547:       vt = zero;
548:       if (i != -1 && j != -1) v = s[k];
549:       if (i != nx - 1 && j != -1) {
550:         vr  = s[k + 1];
551:         ind = k + 1;
552:         val = hxhx * (vr - v);
553:         PetscCall(VecSetValues(y, 1, &ind, &val, ADD_VALUES));
554:       }
555:       if (i != -1 && j != ny - 1) {
556:         vt  = s[k + nx];
557:         ind = k + nx;
558:         val = hyhy * (vt - v);
559:         PetscCall(VecSetValues(y, 1, &ind, &val, ADD_VALUES));
560:       }
561:       if (i != -1 && j != -1) {
562:         ind = k;
563:         val = hxhx * (v - vr) + hyhy * (v - vt);
564:         PetscCall(VecSetValues(y, 1, &ind, &val, ADD_VALUES));
565:       }
566:     }
567:   }

569:   /* Compute f''(x)*s over the upper triangular elements */
570:   for (j = 0; j <= ny; j++) {
571:     for (i = 0; i <= nx; i++) {
572:       k  = nx * j + i;
573:       v  = zero;
574:       vl = zero;
575:       vb = zero;
576:       if (i != nx && j != ny) v = s[k];
577:       if (i != nx && j != 0) {
578:         vb  = s[k - nx];
579:         ind = k - nx;
580:         val = hyhy * (vb - v);
581:         PetscCall(VecSetValues(y, 1, &ind, &val, ADD_VALUES));
582:       }
583:       if (i != 0 && j != ny) {
584:         vl  = s[k - 1];
585:         ind = k - 1;
586:         val = hxhx * (vl - v);
587:         PetscCall(VecSetValues(y, 1, &ind, &val, ADD_VALUES));
588:       }
589:       if (i != nx && j != ny) {
590:         ind = k;
591:         val = hxhx * (v - vl) + hyhy * (v - vb);
592:         PetscCall(VecSetValues(y, 1, &ind, &val, ADD_VALUES));
593:       }
594:     }
595:   }
596:   /* Restore vector data */
597:   PetscCall(VecRestoreArrayRead(svec, &s));
598:   PetscCall(VecRestoreArrayRead(user->xvec, &x));

600:   /* Assemble vector */
601:   PetscCall(VecAssemblyBegin(y));
602:   PetscCall(VecAssemblyEnd(y));

604:   /* Scale resulting vector by area */
605:   area = p5 * hx * hy;
606:   PetscCall(VecScale(y, area));
607:   PetscCall(PetscLogFlops(18.0 * nx * ny));
608:   PetscFunctionReturn(PETSC_SUCCESS);
609: }

611: /*TEST

613:    build:
614:       requires: !complex

616:    test:
617:       suffix: 1
618:       args: -tao_monitor -tao_type ntl -tao_gatol 1.e-4

620:    test:
621:       suffix: 2
622:       args: -tao_monitor -tao_type ntr -tao_gatol 1.e-4

624:    test:
625:       suffix: 3
626:       args: -tao_monitor -tao_type bntr -tao_gatol 1.e-4 -my_tao_mf -tao_test_hessian

628:    test:
629:      suffix: 4
630:      args: -tao_monitor -tao_gatol 1e-3 -tao_type bqnls

632:    test:
633:      suffix: 5
634:      args: -tao_monitor -tao_gatol 1e-3 -tao_type blmvm

636:    test:
637:      suffix: 6
638:      args: -tao_monitor -tao_gatol 1e-3 -tao_type bqnktr -tao_bqnk_mat_type lmvmsr1

640:    test:
641:      suffix: snes
642:      args: -snes_monitor ::ascii_info_detail -tao_type snes -snes_type newtontr -ksp_type cg -snes_atol 1.e-4 -tao_mf_hessian {{0 1}} -pc_type none

644:    test:
645:      suffix: snes_2
646:      args: -snes_monitor ::ascii_info_detail -tao_type snes -snes_type newtontr -snes_atol 5.e-4 -tao_mf_hessian -pc_type none -snes_tr_fallback_type cauchy

648:    test:
649:      suffix: snes_3
650:      args: -snes_monitor ::ascii_info_detail -tao_type snes -snes_type newtontr -snes_atol 5.e-4 -tao_mf_hessian -pc_type lmvm -snes_tr_fallback_type cauchy

652: TEST*/