xref: /petsc/src/tao/unconstrained/tutorials/eptorsion3.c (revision 98d129c30f3ee9fdddc40fdbc5a989b7be64f888)
1 /* Program usage: mpiexec -n 1 eptorsion1 [-help] [all TAO options] */
2 
3 /* ----------------------------------------------------------------------
4 
5   Elastic-plastic torsion problem.
6 
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:
10 
11   min{ .5 * integral(||gradient(v(x))||^2 dx) - C * integral(v(x) dx)}
12 
13   where C is the torsion angle per unit length.
14 
15   The multiprocessor version of this code is eptorsion2.c.
16 
17 ---------------------------------------------------------------------- */
18 
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      petsc.h       - base PETSc routines   petscvec.h - vectors
24      petscsys.h    - system routines        petscmat.h - matrices
25      petscis.h     - index sets            petscksp.h - Krylov subspace methods
26      petscviewer.h - viewers               petscpc.h  - preconditioners
27 */
28 
29 #include <petsctao.h>
30 
31 static char help[] = "Demonstrates use of the TAO package to solve \n\
32 unconstrained minimization problems on a single processor.  This example \n\
33 is based on the Elastic-Plastic Torsion (dept) problem from the MINPACK-2 \n\
34 test suite.\n\
35 The command line options are:\n\
36   -mx <xg>, where <xg> = number of grid points in the 1st coordinate direction\n\
37   -my <yg>, where <yg> = number of grid points in the 2nd coordinate direction\n\
38   -par <param>, where <param> = angle of twist per unit length\n\n";
39 
40 /*
41    User-defined application context - contains data needed by the
42    application-provided call-back routines, FormFunction(),
43    FormGradient(), and FormHessian().
44 */
45 
46 typedef struct {
47   PetscReal param;      /* nonlinearity parameter */
48   PetscInt  mx, my;     /* discretization in x- and y-directions */
49   PetscInt  ndim;       /* problem dimension */
50   Vec       s, y, xvec; /* work space for computing Hessian */
51   PetscReal hx, hy;     /* mesh spacing in x- and y-directions */
52 } AppCtx;
53 
54 /* -------- User-defined Routines --------- */
55 
56 PetscErrorCode FormInitialGuess(AppCtx *, Vec);
57 PetscErrorCode FormFunction(Tao, Vec, PetscReal *, void *);
58 PetscErrorCode FormGradient(Tao, Vec, Vec, void *);
59 PetscErrorCode FormHessian(Tao, Vec, Mat, Mat, void *);
60 PetscErrorCode HessianProductMat(Mat, Vec, Vec);
61 PetscErrorCode HessianProduct(void *, Vec, Vec);
62 PetscErrorCode MatrixFreeHessian(Tao, Vec, Mat, Mat, void *);
63 PetscErrorCode FormFunctionGradient(Tao, Vec, PetscReal *, Vec, void *);
64 
65 int main(int argc, char **argv)
66 {
67   PetscInt    mx = 10; /* discretization in x-direction */
68   PetscInt    my = 10; /* discretization in y-direction */
69   Vec         x;       /* solution, gradient vectors */
70   PetscBool   flg;     /* A return value when checking for use options */
71   Tao         tao;     /* Tao solver context */
72   Mat         H;       /* Hessian matrix */
73   AppCtx      user;    /* application context */
74   PetscMPIInt size;    /* number of processes */
75   PetscReal   one = 1.0;
76 
77   PetscBool test_lmvm = PETSC_FALSE;
78   KSP       ksp;
79   PC        pc;
80   Mat       M;
81   Vec       in, out, out2;
82   PetscReal mult_solve_dist;
83   Vec       ub, lb;
84   PetscInt  i = 3;
85 
86   /* Initialize TAO,PETSc */
87   PetscFunctionBeginUser;
88   PetscCall(PetscInitialize(&argc, &argv, (char *)0, 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");
91 
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));
98 
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);
106 
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));
111   PetscCall(VecDuplicate(user.y, &lb));
112   PetscCall(VecDuplicate(user.y, &ub));
113 
114   PetscCall(VecSet(ub, 0.1));
115   PetscCall(VecSet(lb, -0.1));
116   PetscCall(VecSetValue(ub, i, 0., INSERT_VALUES));
117   PetscCall(VecSetValue(lb, i, 0., INSERT_VALUES));
118   PetscCall(VecAssemblyBegin(ub));
119   PetscCall(VecAssemblyBegin(lb));
120   PetscCall(VecAssemblyEnd(ub));
121   PetscCall(VecAssemblyEnd(lb));
122 
123   /* Create TAO solver and set desired solution method */
124   PetscCall(TaoCreate(PETSC_COMM_SELF, &tao));
125   PetscCall(TaoSetType(tao, TAOLMVM));
126 
127   /* Set solution vector with an initial guess */
128   PetscCall(FormInitialGuess(&user, x));
129   PetscCall(TaoSetSolution(tao, x));
130   PetscCall(TaoSetVariableBounds(tao, lb, ub));
131 
132   /* Set routine for function and gradient evaluation */
133   PetscCall(TaoSetObjectiveAndGradient(tao, NULL, FormFunctionGradient, (void *)&user));
134 
135   /* From command line options, determine if using matrix-free hessian */
136   PetscCall(PetscOptionsHasName(NULL, NULL, "-my_tao_mf", &flg));
137   if (flg) {
138     PetscCall(MatCreateShell(PETSC_COMM_SELF, user.ndim, user.ndim, user.ndim, user.ndim, (void *)&user, &H));
139     PetscCall(MatShellSetOperation(H, MATOP_MULT, (void (*)(void))HessianProductMat));
140     PetscCall(MatSetOption(H, MAT_SYMMETRIC, PETSC_TRUE));
141 
142     PetscCall(TaoSetHessian(tao, H, H, MatrixFreeHessian, (void *)&user));
143   } else {
144     PetscCall(MatCreateSeqAIJ(PETSC_COMM_SELF, user.ndim, user.ndim, 5, NULL, &H));
145     PetscCall(MatSetOption(H, MAT_SYMMETRIC, PETSC_TRUE));
146     PetscCall(TaoSetHessian(tao, H, H, FormHessian, (void *)&user));
147   }
148 
149   /* Test the LMVM matrix */
150   if (test_lmvm) {
151     PetscCall(PetscOptionsSetValue(NULL, "-tao_type", "bntr"));
152     PetscCall(PetscOptionsSetValue(NULL, "-tao_bnk_pc_type", "lmvm"));
153   }
154 
155   /* Check for any TAO command line options */
156   PetscCall(TaoSetFromOptions(tao));
157 
158   /* SOLVE THE APPLICATION */
159   PetscCall(TaoSolve(tao));
160 
161   /* Test the LMVM matrix */
162   if (test_lmvm) {
163     PetscCall(TaoGetKSP(tao, &ksp));
164     PetscCall(KSPGetPC(ksp, &pc));
165     PetscCall(PCLMVMGetMatLMVM(pc, &M));
166     PetscCall(VecDuplicate(x, &in));
167     PetscCall(VecDuplicate(x, &out));
168     PetscCall(VecDuplicate(x, &out2));
169     PetscCall(VecSet(in, 5.0));
170     PetscCall(MatMult(M, in, out));
171     PetscCall(MatSolve(M, out, out2));
172     PetscCall(VecAXPY(out2, -1.0, in));
173     PetscCall(VecNorm(out2, NORM_2, &mult_solve_dist));
174     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)tao), "error between MatMult and MatSolve: %e\n", (double)mult_solve_dist));
175     PetscCall(VecDestroy(&in));
176     PetscCall(VecDestroy(&out));
177     PetscCall(VecDestroy(&out2));
178   }
179 
180   PetscCall(TaoDestroy(&tao));
181   PetscCall(VecDestroy(&user.s));
182   PetscCall(VecDestroy(&user.y));
183   PetscCall(VecDestroy(&x));
184   PetscCall(MatDestroy(&H));
185   PetscCall(VecDestroy(&lb));
186   PetscCall(VecDestroy(&ub));
187 
188   PetscCall(PetscFinalize());
189   return 0;
190 }
191 
192 /* ------------------------------------------------------------------- */
193 /*
194     FormInitialGuess - Computes an initial approximation to the solution.
195 
196     Input Parameters:
197 .   user - user-defined application context
198 .   X    - vector
199 
200     Output Parameters:
201 .   X    - vector
202 */
203 PetscErrorCode FormInitialGuess(AppCtx *user, Vec X)
204 {
205   PetscReal hx = user->hx, hy = user->hy, temp;
206   PetscReal val;
207   PetscInt  i, j, k, nx = user->mx, ny = user->my;
208 
209   /* Compute initial guess */
210   PetscFunctionBeginUser;
211   for (j = 0; j < ny; j++) {
212     temp = PetscMin(j + 1, ny - j) * hy;
213     for (i = 0; i < nx; i++) {
214       k   = nx * j + i;
215       val = PetscMin((PetscMin(i + 1, nx - i)) * hx, temp);
216       PetscCall(VecSetValues(X, 1, &k, &val, ADD_VALUES));
217     }
218   }
219   PetscCall(VecAssemblyBegin(X));
220   PetscCall(VecAssemblyEnd(X));
221   PetscFunctionReturn(PETSC_SUCCESS);
222 }
223 
224 /* ------------------------------------------------------------------- */
225 /*
226    FormFunctionGradient - Evaluates the function and corresponding gradient.
227 
228    Input Parameters:
229    tao - the Tao context
230    X   - the input vector
231    ptr - optional user-defined context, as set by TaoSetFunction()
232 
233    Output Parameters:
234    f   - the newly evaluated function
235    G   - the newly evaluated gradient
236 */
237 PetscErrorCode FormFunctionGradient(Tao tao, Vec X, PetscReal *f, Vec G, void *ptr)
238 {
239   PetscFunctionBeginUser;
240   PetscCall(FormFunction(tao, X, f, ptr));
241   PetscCall(FormGradient(tao, X, G, ptr));
242   PetscFunctionReturn(PETSC_SUCCESS);
243 }
244 
245 /* ------------------------------------------------------------------- */
246 /*
247    FormFunction - Evaluates the function, f(X).
248 
249    Input Parameters:
250 .  tao - the Tao context
251 .  X   - the input vector
252 .  ptr - optional user-defined context, as set by TaoSetFunction()
253 
254    Output Parameters:
255 .  f    - the newly evaluated function
256 */
257 PetscErrorCode FormFunction(Tao tao, Vec X, PetscReal *f, void *ptr)
258 {
259   AppCtx            *user = (AppCtx *)ptr;
260   PetscReal          hx = user->hx, hy = user->hy, area, three = 3.0, p5 = 0.5;
261   PetscReal          zero = 0.0, vb, vl, vr, vt, dvdx, dvdy, flin = 0.0, fquad = 0.0;
262   PetscReal          v, cdiv3 = user->param / three;
263   const PetscScalar *x;
264   PetscInt           nx = user->mx, ny = user->my, i, j, k;
265 
266   PetscFunctionBeginUser;
267   /* Get pointer to vector data */
268   PetscCall(VecGetArrayRead(X, &x));
269 
270   /* Compute function contributions over the lower triangular elements */
271   for (j = -1; j < ny; j++) {
272     for (i = -1; i < nx; i++) {
273       k  = nx * j + i;
274       v  = zero;
275       vr = zero;
276       vt = zero;
277       if (i >= 0 && j >= 0) v = x[k];
278       if (i < nx - 1 && j > -1) vr = x[k + 1];
279       if (i > -1 && j < ny - 1) vt = x[k + nx];
280       dvdx = (vr - v) / hx;
281       dvdy = (vt - v) / hy;
282       fquad += dvdx * dvdx + dvdy * dvdy;
283       flin -= cdiv3 * (v + vr + vt);
284     }
285   }
286 
287   /* Compute function contributions over the upper triangular elements */
288   for (j = 0; j <= ny; j++) {
289     for (i = 0; i <= nx; i++) {
290       k  = nx * j + i;
291       vb = zero;
292       vl = zero;
293       v  = zero;
294       if (i < nx && j > 0) vb = x[k - nx];
295       if (i > 0 && j < ny) vl = x[k - 1];
296       if (i < nx && j < ny) v = x[k];
297       dvdx  = (v - vl) / hx;
298       dvdy  = (v - vb) / hy;
299       fquad = fquad + dvdx * dvdx + dvdy * dvdy;
300       flin  = flin - cdiv3 * (vb + vl + v);
301     }
302   }
303 
304   /* Restore vector */
305   PetscCall(VecRestoreArrayRead(X, &x));
306 
307   /* Scale the function */
308   area = p5 * hx * hy;
309   *f   = area * (p5 * fquad + flin);
310 
311   PetscCall(PetscLogFlops(24.0 * nx * ny));
312   PetscFunctionReturn(PETSC_SUCCESS);
313 }
314 
315 /* ------------------------------------------------------------------- */
316 /*
317     FormGradient - Evaluates the gradient, G(X).
318 
319     Input Parameters:
320 .   tao  - the Tao context
321 .   X    - input vector
322 .   ptr  - optional user-defined context
323 
324     Output Parameters:
325 .   G - vector containing the newly evaluated gradient
326 */
327 PetscErrorCode FormGradient(Tao tao, Vec X, Vec G, void *ptr)
328 {
329   AppCtx            *user = (AppCtx *)ptr;
330   PetscReal          zero = 0.0, p5 = 0.5, three = 3.0, area, val;
331   PetscInt           nx = user->mx, ny = user->my, ind, i, j, k;
332   PetscReal          hx = user->hx, hy = user->hy;
333   PetscReal          vb, vl, vr, vt, dvdx, dvdy;
334   PetscReal          v, cdiv3 = user->param / three;
335   const PetscScalar *x;
336 
337   PetscFunctionBeginUser;
338   /* Initialize gradient to zero */
339   PetscCall(VecSet(G, zero));
340 
341   /* Get pointer to vector data */
342   PetscCall(VecGetArrayRead(X, &x));
343 
344   /* Compute gradient contributions over the lower triangular elements */
345   for (j = -1; j < ny; j++) {
346     for (i = -1; i < nx; i++) {
347       k  = nx * j + i;
348       v  = zero;
349       vr = zero;
350       vt = zero;
351       if (i >= 0 && j >= 0) v = x[k];
352       if (i < nx - 1 && j > -1) vr = x[k + 1];
353       if (i > -1 && j < ny - 1) vt = x[k + nx];
354       dvdx = (vr - v) / hx;
355       dvdy = (vt - v) / hy;
356       if (i != -1 && j != -1) {
357         ind = k;
358         val = -dvdx / hx - dvdy / hy - cdiv3;
359         PetscCall(VecSetValues(G, 1, &ind, &val, ADD_VALUES));
360       }
361       if (i != nx - 1 && j != -1) {
362         ind = k + 1;
363         val = dvdx / hx - cdiv3;
364         PetscCall(VecSetValues(G, 1, &ind, &val, ADD_VALUES));
365       }
366       if (i != -1 && j != ny - 1) {
367         ind = k + nx;
368         val = dvdy / hy - cdiv3;
369         PetscCall(VecSetValues(G, 1, &ind, &val, ADD_VALUES));
370       }
371     }
372   }
373 
374   /* Compute gradient contributions over the upper triangular elements */
375   for (j = 0; j <= ny; j++) {
376     for (i = 0; i <= nx; i++) {
377       k  = nx * j + i;
378       vb = zero;
379       vl = zero;
380       v  = zero;
381       if (i < nx && j > 0) vb = x[k - nx];
382       if (i > 0 && j < ny) vl = x[k - 1];
383       if (i < nx && j < ny) v = x[k];
384       dvdx = (v - vl) / hx;
385       dvdy = (v - vb) / hy;
386       if (i != nx && j != 0) {
387         ind = k - nx;
388         val = -dvdy / hy - cdiv3;
389         PetscCall(VecSetValues(G, 1, &ind, &val, ADD_VALUES));
390       }
391       if (i != 0 && j != ny) {
392         ind = k - 1;
393         val = -dvdx / hx - cdiv3;
394         PetscCall(VecSetValues(G, 1, &ind, &val, ADD_VALUES));
395       }
396       if (i != nx && j != ny) {
397         ind = k;
398         val = dvdx / hx + dvdy / hy - cdiv3;
399         PetscCall(VecSetValues(G, 1, &ind, &val, ADD_VALUES));
400       }
401     }
402   }
403   PetscCall(VecRestoreArrayRead(X, &x));
404 
405   /* Assemble gradient vector */
406   PetscCall(VecAssemblyBegin(G));
407   PetscCall(VecAssemblyEnd(G));
408 
409   /* Scale the gradient */
410   area = p5 * hx * hy;
411   PetscCall(VecScale(G, area));
412   PetscCall(PetscLogFlops(24.0 * nx * ny));
413   PetscFunctionReturn(PETSC_SUCCESS);
414 }
415 
416 /* ------------------------------------------------------------------- */
417 /*
418    FormHessian - Forms the Hessian matrix.
419 
420    Input Parameters:
421 .  tao - the Tao context
422 .  X    - the input vector
423 .  ptr  - optional user-defined context, as set by TaoSetHessian()
424 
425    Output Parameters:
426 .  H     - Hessian matrix
427 .  PrecH - optionally different preconditioning Hessian
428 .  flag  - flag indicating matrix structure
429 
430    Notes:
431    This routine is intended simply as an example of the interface
432    to a Hessian evaluation routine.  Since this example compute the
433    Hessian a column at a time, it is not particularly efficient and
434    is not recommended.
435 */
436 PetscErrorCode FormHessian(Tao tao, Vec X, Mat H, Mat Hpre, void *ptr)
437 {
438   AppCtx    *user = (AppCtx *)ptr;
439   PetscInt   i, j, ndim = user->ndim;
440   PetscReal *y, zero = 0.0, one = 1.0;
441   PetscBool  assembled;
442 
443   PetscFunctionBeginUser;
444   user->xvec = X;
445 
446   /* Initialize Hessian entries and work vector to zero */
447   PetscCall(MatAssembled(H, &assembled));
448   if (assembled) PetscCall(MatZeroEntries(H));
449 
450   PetscCall(VecSet(user->s, zero));
451 
452   /* Loop over matrix columns to compute entries of the Hessian */
453   for (j = 0; j < ndim; j++) {
454     PetscCall(VecSetValues(user->s, 1, &j, &one, INSERT_VALUES));
455     PetscCall(VecAssemblyBegin(user->s));
456     PetscCall(VecAssemblyEnd(user->s));
457 
458     PetscCall(HessianProduct(ptr, user->s, user->y));
459 
460     PetscCall(VecSetValues(user->s, 1, &j, &zero, INSERT_VALUES));
461     PetscCall(VecAssemblyBegin(user->s));
462     PetscCall(VecAssemblyEnd(user->s));
463 
464     PetscCall(VecGetArray(user->y, &y));
465     for (i = 0; i < ndim; i++) {
466       if (y[i] != zero) PetscCall(MatSetValues(H, 1, &i, 1, &j, &y[i], ADD_VALUES));
467     }
468     PetscCall(VecRestoreArray(user->y, &y));
469   }
470   PetscCall(MatAssemblyBegin(H, MAT_FINAL_ASSEMBLY));
471   PetscCall(MatAssemblyEnd(H, MAT_FINAL_ASSEMBLY));
472   PetscFunctionReturn(PETSC_SUCCESS);
473 }
474 
475 /* ------------------------------------------------------------------- */
476 /*
477    MatrixFreeHessian - Sets a pointer for use in computing Hessian-vector
478    products.
479 
480    Input Parameters:
481 .  tao - the Tao context
482 .  X    - the input vector
483 .  ptr  - optional user-defined context, as set by TaoSetHessian()
484 
485    Output Parameters:
486 .  H     - Hessian matrix
487 .  PrecH - optionally different preconditioning Hessian
488 .  flag  - flag indicating matrix structure
489 */
490 PetscErrorCode MatrixFreeHessian(Tao tao, Vec X, Mat H, Mat PrecH, void *ptr)
491 {
492   AppCtx *user = (AppCtx *)ptr;
493 
494   /* Sets location of vector for use in computing matrix-vector products  of the form H(X)*y  */
495   PetscFunctionBeginUser;
496   user->xvec = X;
497   PetscFunctionReturn(PETSC_SUCCESS);
498 }
499 
500 /* ------------------------------------------------------------------- */
501 /*
502    HessianProductMat - Computes the matrix-vector product
503    y = mat*svec.
504 
505    Input Parameters:
506 .  mat  - input matrix
507 .  svec - input vector
508 
509    Output Parameters:
510 .  y    - solution vector
511 */
512 PetscErrorCode HessianProductMat(Mat mat, Vec svec, Vec y)
513 {
514   void *ptr;
515 
516   PetscFunctionBeginUser;
517   PetscCall(MatShellGetContext(mat, &ptr));
518   PetscCall(HessianProduct(ptr, svec, y));
519   PetscFunctionReturn(PETSC_SUCCESS);
520 }
521 
522 /* ------------------------------------------------------------------- */
523 /*
524    Hessian Product - Computes the matrix-vector product:
525    y = f''(x)*svec.
526 
527    Input Parameters:
528 .  ptr  - pointer to the user-defined context
529 .  svec - input vector
530 
531    Output Parameters:
532 .  y    - product vector
533 */
534 PetscErrorCode HessianProduct(void *ptr, Vec svec, Vec y)
535 {
536   AppCtx            *user = (AppCtx *)ptr;
537   PetscReal          p5 = 0.5, zero = 0.0, one = 1.0, hx, hy, val, area;
538   const PetscScalar *x, *s;
539   PetscReal          v, vb, vl, vr, vt, hxhx, hyhy;
540   PetscInt           nx, ny, i, j, k, ind;
541 
542   PetscFunctionBeginUser;
543   nx   = user->mx;
544   ny   = user->my;
545   hx   = user->hx;
546   hy   = user->hy;
547   hxhx = one / (hx * hx);
548   hyhy = one / (hy * hy);
549 
550   /* Get pointers to vector data */
551   PetscCall(VecGetArrayRead(user->xvec, &x));
552   PetscCall(VecGetArrayRead(svec, &s));
553 
554   /* Initialize product vector to zero */
555   PetscCall(VecSet(y, zero));
556 
557   /* Compute f''(x)*s over the lower triangular elements */
558   for (j = -1; j < ny; j++) {
559     for (i = -1; i < nx; i++) {
560       k  = nx * j + i;
561       v  = zero;
562       vr = zero;
563       vt = zero;
564       if (i != -1 && j != -1) v = s[k];
565       if (i != nx - 1 && j != -1) {
566         vr  = s[k + 1];
567         ind = k + 1;
568         val = hxhx * (vr - v);
569         PetscCall(VecSetValues(y, 1, &ind, &val, ADD_VALUES));
570       }
571       if (i != -1 && j != ny - 1) {
572         vt  = s[k + nx];
573         ind = k + nx;
574         val = hyhy * (vt - v);
575         PetscCall(VecSetValues(y, 1, &ind, &val, ADD_VALUES));
576       }
577       if (i != -1 && j != -1) {
578         ind = k;
579         val = hxhx * (v - vr) + hyhy * (v - vt);
580         PetscCall(VecSetValues(y, 1, &ind, &val, ADD_VALUES));
581       }
582     }
583   }
584 
585   /* Compute f''(x)*s over the upper triangular elements */
586   for (j = 0; j <= ny; j++) {
587     for (i = 0; i <= nx; i++) {
588       k  = nx * j + i;
589       v  = zero;
590       vl = zero;
591       vb = zero;
592       if (i != nx && j != ny) v = s[k];
593       if (i != nx && j != 0) {
594         vb  = s[k - nx];
595         ind = k - nx;
596         val = hyhy * (vb - v);
597         PetscCall(VecSetValues(y, 1, &ind, &val, ADD_VALUES));
598       }
599       if (i != 0 && j != ny) {
600         vl  = s[k - 1];
601         ind = k - 1;
602         val = hxhx * (vl - v);
603         PetscCall(VecSetValues(y, 1, &ind, &val, ADD_VALUES));
604       }
605       if (i != nx && j != ny) {
606         ind = k;
607         val = hxhx * (v - vl) + hyhy * (v - vb);
608         PetscCall(VecSetValues(y, 1, &ind, &val, ADD_VALUES));
609       }
610     }
611   }
612   /* Restore vector data */
613   PetscCall(VecRestoreArrayRead(svec, &s));
614   PetscCall(VecRestoreArrayRead(user->xvec, &x));
615 
616   /* Assemble vector */
617   PetscCall(VecAssemblyBegin(y));
618   PetscCall(VecAssemblyEnd(y));
619 
620   /* Scale resulting vector by area */
621   area = p5 * hx * hy;
622   PetscCall(VecScale(y, area));
623   PetscCall(PetscLogFlops(18.0 * nx * ny));
624   PetscFunctionReturn(PETSC_SUCCESS);
625 }
626 
627 /*TEST
628 
629    build:
630       requires: !complex
631 
632    test:
633       args: -tao_monitor -tao_type bntr -tao_view -tao_bnk_ksp_monitor_short
634 
635 TEST*/
636