xref: /petsc/src/tao/unconstrained/tutorials/minsurf1.c (revision a336c15037c72f93cd561f5a5e11e93175f2efd9)
1 /* Program usage: mpiexec -n 1 minsurf1 [-help] [all TAO options] */
2 
3 /*  Include "petsctao.h" so we can use TAO solvers.  */
4 #include <petsctao.h>
5 
6 static char help[] = "This example demonstrates use of the TAO package to \n\
7 solve an unconstrained minimization problem.  This example is based on a \n\
8 problem from the MINPACK-2 test suite.  Given a rectangular 2-D domain and \n\
9 boundary values along the edges of the domain, the objective is to find the\n\
10 surface with the minimal area that satisfies the boundary conditions.\n\
11 The command line options are:\n\
12   -mx <xg>, where <xg> = number of grid points in the 1st coordinate direction\n\
13   -my <yg>, where <yg> = number of grid points in the 2nd coordinate direction\n\
14   -start <st>, where <st> =0 for zero vector, and an average of the boundary conditions otherwise \n\n";
15 
16 /*
17    User-defined application context - contains data needed by the
18    application-provided call-back routines, FormFunctionGradient()
19    and FormHessian().
20 */
21 typedef struct {
22   PetscInt   mx, my;                      /* discretization in x, y directions */
23   PetscReal *bottom, *top, *left, *right; /* boundary values */
24   Mat        H;
25 } AppCtx;
26 
27 /* -------- User-defined Routines --------- */
28 
29 static PetscErrorCode MSA_BoundaryConditions(AppCtx *);
30 static PetscErrorCode MSA_InitialPoint(AppCtx *, Vec);
31 static PetscErrorCode QuadraticH(AppCtx *, Vec, Mat);
32 PetscErrorCode        FormFunctionGradient(Tao, Vec, PetscReal *, Vec, void *);
33 PetscErrorCode        FormHessian(Tao, Vec, Mat, Mat, void *);
34 
35 int main(int argc, char **argv)
36 {
37   PetscInt    N;    /* Size of vector */
38   PetscMPIInt size; /* Number of processors */
39   Vec         x;    /* solution, gradient vectors */
40   KSP         ksp;  /*  PETSc Krylov subspace method */
41   PetscBool   flg;  /* A return value when checking for user options */
42   Tao         tao;  /* Tao solver context */
43   AppCtx      user; /* user-defined work context */
44 
45   /* Initialize TAO,PETSc */
46   PetscFunctionBeginUser;
47   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
48 
49   PetscCallMPI(MPI_Comm_size(MPI_COMM_WORLD, &size));
50   PetscCheck(size == 1, PETSC_COMM_WORLD, PETSC_ERR_WRONG_MPI_SIZE, "Incorrect number of processors");
51 
52   /* Specify default dimension of the problem */
53   user.mx = 4;
54   user.my = 4;
55 
56   /* Check for any command line arguments that override defaults */
57   PetscCall(PetscOptionsGetInt(NULL, NULL, "-mx", &user.mx, &flg));
58   PetscCall(PetscOptionsGetInt(NULL, NULL, "-my", &user.my, &flg));
59 
60   PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n---- Minimum Surface Area Problem -----\n"));
61   PetscCall(PetscPrintf(PETSC_COMM_SELF, "mx: %" PetscInt_FMT "     my: %" PetscInt_FMT "   \n\n", user.mx, user.my));
62 
63   /* Calculate any derived values from parameters */
64   N = user.mx * user.my;
65 
66   /* Create TAO solver and set desired solution method  */
67   PetscCall(TaoCreate(PETSC_COMM_SELF, &tao));
68   PetscCall(TaoSetType(tao, TAOLMVM));
69 
70   /* Initialize minsurf application data structure for use in the function evaluations  */
71   PetscCall(MSA_BoundaryConditions(&user)); /* Application specific routine */
72 
73   /*
74      Create a vector to hold the variables.  Compute an initial solution.
75      Set this vector, which will be used by TAO.
76   */
77   PetscCall(VecCreateSeq(PETSC_COMM_SELF, N, &x));
78   PetscCall(MSA_InitialPoint(&user, x)); /* Application specific routine */
79   PetscCall(TaoSetSolution(tao, x));     /* A TAO routine                */
80 
81   /* Provide TAO routines for function, gradient, and Hessian evaluation */
82   PetscCall(TaoSetObjectiveAndGradient(tao, NULL, FormFunctionGradient, (void *)&user));
83 
84   /* Create a matrix data structure to store the Hessian.  This structure will be used by TAO */
85   PetscCall(MatCreateSeqAIJ(PETSC_COMM_SELF, N, N, 7, NULL, &user.H));
86   PetscCall(MatSetOption(user.H, MAT_SYMMETRIC, PETSC_TRUE));
87   PetscCall(TaoSetHessian(tao, user.H, user.H, FormHessian, (void *)&user));
88 
89   /* Check for any TAO command line options */
90   PetscCall(TaoSetFromOptions(tao));
91 
92   /* Limit the number of iterations in the KSP linear solver */
93   PetscCall(TaoGetKSP(tao, &ksp));
94   if (ksp) PetscCall(KSPSetTolerances(ksp, PETSC_CURRENT, PETSC_CURRENT, PETSC_CURRENT, user.mx * user.my));
95 
96   /* SOLVE THE APPLICATION */
97   PetscCall(TaoSolve(tao));
98 
99   PetscCall(TaoDestroy(&tao));
100   PetscCall(VecDestroy(&x));
101   PetscCall(MatDestroy(&user.H));
102   PetscCall(PetscFree(user.bottom));
103   PetscCall(PetscFree(user.top));
104   PetscCall(PetscFree(user.left));
105   PetscCall(PetscFree(user.right));
106 
107   PetscCall(PetscFinalize());
108   return 0;
109 }
110 
111 /* -------------------------------------------------------------------- */
112 
113 /*  FormFunctionGradient - Evaluates function and corresponding gradient.
114 
115     Input Parameters:
116 .   tao     - the Tao context
117 .   X       - input vector
118 .   userCtx - optional user-defined context, as set by TaoSetFunctionGradient()
119 
120     Output Parameters:
121 .   fcn     - the newly evaluated function
122 .   G       - vector containing the newly evaluated gradient
123 */
124 PetscErrorCode FormFunctionGradient(Tao tao, Vec X, PetscReal *fcn, Vec G, void *userCtx)
125 {
126   AppCtx            *user = (AppCtx *)userCtx;
127   PetscInt           i, j, row;
128   PetscInt           mx = user->mx, my = user->my;
129   PetscReal          rhx = mx + 1, rhy = my + 1;
130   PetscReal          hx = 1.0 / (mx + 1), hy = 1.0 / (my + 1), hydhx = hy / hx, hxdhy = hx / hy, area = 0.5 * hx * hy, ft = 0;
131   PetscReal          f1, f2, f3, f4, f5, f6, d1, d2, d3, d4, d5, d6, d7, d8, xc, xl, xr, xt, xb, xlt, xrb;
132   PetscReal          df1dxc, df2dxc, df3dxc, df4dxc, df5dxc, df6dxc;
133   PetscReal          zero = 0.0;
134   PetscScalar       *g;
135   const PetscScalar *x;
136 
137   PetscFunctionBeginUser;
138   PetscCall(VecSet(G, zero));
139 
140   PetscCall(VecGetArrayRead(X, &x));
141   PetscCall(VecGetArray(G, &g));
142 
143   /* Compute function over the locally owned part of the mesh */
144   for (j = 0; j < my; j++) {
145     for (i = 0; i < mx; i++) {
146       row = (j)*mx + (i);
147       xc  = x[row];
148       xlt = xrb = xl = xr = xb = xt = xc;
149       if (i == 0) { /* left side */
150         xl  = user->left[j + 1];
151         xlt = user->left[j + 2];
152       } else {
153         xl = x[row - 1];
154       }
155 
156       if (j == 0) { /* bottom side */
157         xb  = user->bottom[i + 1];
158         xrb = user->bottom[i + 2];
159       } else {
160         xb = x[row - mx];
161       }
162 
163       if (i + 1 == mx) { /* right side */
164         xr  = user->right[j + 1];
165         xrb = user->right[j];
166       } else {
167         xr = x[row + 1];
168       }
169 
170       if (j + 1 == 0 + my) { /* top side */
171         xt  = user->top[i + 1];
172         xlt = user->top[i];
173       } else {
174         xt = x[row + mx];
175       }
176 
177       if (i > 0 && j + 1 < my) xlt = x[row - 1 + mx];
178       if (j > 0 && i + 1 < mx) xrb = x[row + 1 - mx];
179 
180       d1 = (xc - xl);
181       d2 = (xc - xr);
182       d3 = (xc - xt);
183       d4 = (xc - xb);
184       d5 = (xr - xrb);
185       d6 = (xrb - xb);
186       d7 = (xlt - xl);
187       d8 = (xt - xlt);
188 
189       df1dxc = d1 * hydhx;
190       df2dxc = (d1 * hydhx + d4 * hxdhy);
191       df3dxc = d3 * hxdhy;
192       df4dxc = (d2 * hydhx + d3 * hxdhy);
193       df5dxc = d2 * hydhx;
194       df6dxc = d4 * hxdhy;
195 
196       d1 *= rhx;
197       d2 *= rhx;
198       d3 *= rhy;
199       d4 *= rhy;
200       d5 *= rhy;
201       d6 *= rhx;
202       d7 *= rhy;
203       d8 *= rhx;
204 
205       f1 = PetscSqrtScalar(1.0 + d1 * d1 + d7 * d7);
206       f2 = PetscSqrtScalar(1.0 + d1 * d1 + d4 * d4);
207       f3 = PetscSqrtScalar(1.0 + d3 * d3 + d8 * d8);
208       f4 = PetscSqrtScalar(1.0 + d3 * d3 + d2 * d2);
209       f5 = PetscSqrtScalar(1.0 + d2 * d2 + d5 * d5);
210       f6 = PetscSqrtScalar(1.0 + d4 * d4 + d6 * d6);
211 
212       ft = ft + (f2 + f4);
213 
214       df1dxc /= f1;
215       df2dxc /= f2;
216       df3dxc /= f3;
217       df4dxc /= f4;
218       df5dxc /= f5;
219       df6dxc /= f6;
220 
221       g[row] = (df1dxc + df2dxc + df3dxc + df4dxc + df5dxc + df6dxc) / 2.0;
222     }
223   }
224 
225   for (j = 0; j < my; j++) { /* left side */
226     d3 = (user->left[j + 1] - user->left[j + 2]) * rhy;
227     d2 = (user->left[j + 1] - x[j * mx]) * rhx;
228     ft = ft + PetscSqrtScalar(1.0 + d3 * d3 + d2 * d2);
229   }
230 
231   for (i = 0; i < mx; i++) { /* bottom */
232     d2 = (user->bottom[i + 1] - user->bottom[i + 2]) * rhx;
233     d3 = (user->bottom[i + 1] - x[i]) * rhy;
234     ft = ft + PetscSqrtScalar(1.0 + d3 * d3 + d2 * d2);
235   }
236 
237   for (j = 0; j < my; j++) { /* right side */
238     d1 = (x[(j + 1) * mx - 1] - user->right[j + 1]) * rhx;
239     d4 = (user->right[j] - user->right[j + 1]) * rhy;
240     ft = ft + PetscSqrtScalar(1.0 + d1 * d1 + d4 * d4);
241   }
242 
243   for (i = 0; i < mx; i++) { /* top side */
244     d1 = (x[(my - 1) * mx + i] - user->top[i + 1]) * rhy;
245     d4 = (user->top[i + 1] - user->top[i]) * rhx;
246     ft = ft + PetscSqrtScalar(1.0 + d1 * d1 + d4 * d4);
247   }
248 
249   /* Bottom left corner */
250   d1 = (user->left[0] - user->left[1]) * rhy;
251   d2 = (user->bottom[0] - user->bottom[1]) * rhx;
252   ft += PetscSqrtScalar(1.0 + d1 * d1 + d2 * d2);
253 
254   /* Top right corner */
255   d1 = (user->right[my + 1] - user->right[my]) * rhy;
256   d2 = (user->top[mx + 1] - user->top[mx]) * rhx;
257   ft += PetscSqrtScalar(1.0 + d1 * d1 + d2 * d2);
258 
259   (*fcn) = ft * area;
260 
261   /* Restore vectors */
262   PetscCall(VecRestoreArrayRead(X, &x));
263   PetscCall(VecRestoreArray(G, &g));
264   PetscCall(PetscLogFlops(67.0 * mx * my));
265   PetscFunctionReturn(PETSC_SUCCESS);
266 }
267 
268 /* ------------------------------------------------------------------- */
269 /*
270    FormHessian - Evaluates the Hessian matrix.
271 
272    Input Parameters:
273 .  tao  - the Tao context
274 .  x    - input vector
275 .  ptr  - optional user-defined context, as set by TaoSetHessian()
276 
277    Output Parameters:
278 .  H    - Hessian matrix
279 .  Hpre - optionally different matrix used to compute the preconditioner
280 
281 */
282 PetscErrorCode FormHessian(Tao tao, Vec X, Mat H, Mat Hpre, void *ptr)
283 {
284   AppCtx *user = (AppCtx *)ptr;
285 
286   PetscFunctionBeginUser;
287   /* Evaluate the Hessian entries*/
288   PetscCall(QuadraticH(user, X, H));
289   PetscFunctionReturn(PETSC_SUCCESS);
290 }
291 
292 /* ------------------------------------------------------------------- */
293 /*
294    QuadraticH - Evaluates the Hessian matrix.
295 
296    Input Parameters:
297 .  user - user-defined context, as set by TaoSetHessian()
298 .  X    - input vector
299 
300    Output Parameter:
301 .  H    - Hessian matrix
302 */
303 PetscErrorCode QuadraticH(AppCtx *user, Vec X, Mat Hessian)
304 {
305   PetscInt           i, j, k, row;
306   PetscInt           mx = user->mx, my = user->my;
307   PetscInt           col[7];
308   PetscReal          hx = 1.0 / (mx + 1), hy = 1.0 / (my + 1), hydhx = hy / hx, hxdhy = hx / hy;
309   PetscReal          rhx = mx + 1, rhy = my + 1;
310   PetscReal          f1, f2, f3, f4, f5, f6, d1, d2, d3, d4, d5, d6, d7, d8, xc, xl, xr, xt, xb, xlt, xrb;
311   PetscReal          hl, hr, ht, hb, hc, htl, hbr;
312   const PetscScalar *x;
313   PetscReal          v[7];
314 
315   PetscFunctionBeginUser;
316   /* Get pointers to vector data */
317   PetscCall(VecGetArrayRead(X, &x));
318 
319   /* Initialize matrix entries to zero */
320   PetscCall(MatZeroEntries(Hessian));
321 
322   /* Set various matrix options */
323   PetscCall(MatSetOption(Hessian, MAT_IGNORE_OFF_PROC_ENTRIES, PETSC_TRUE));
324 
325   /* Compute Hessian over the locally owned part of the mesh */
326   for (i = 0; i < mx; i++) {
327     for (j = 0; j < my; j++) {
328       row = (j)*mx + (i);
329 
330       xc  = x[row];
331       xlt = xrb = xl = xr = xb = xt = xc;
332 
333       /* Left side */
334       if (i == 0) {
335         xl  = user->left[j + 1];
336         xlt = user->left[j + 2];
337       } else {
338         xl = x[row - 1];
339       }
340 
341       if (j == 0) {
342         xb  = user->bottom[i + 1];
343         xrb = user->bottom[i + 2];
344       } else {
345         xb = x[row - mx];
346       }
347 
348       if (i + 1 == mx) {
349         xr  = user->right[j + 1];
350         xrb = user->right[j];
351       } else {
352         xr = x[row + 1];
353       }
354 
355       if (j + 1 == my) {
356         xt  = user->top[i + 1];
357         xlt = user->top[i];
358       } else {
359         xt = x[row + mx];
360       }
361 
362       if (i > 0 && j + 1 < my) xlt = x[row - 1 + mx];
363       if (j > 0 && i + 1 < mx) xrb = x[row + 1 - mx];
364 
365       d1 = (xc - xl) * rhx;
366       d2 = (xc - xr) * rhx;
367       d3 = (xc - xt) * rhy;
368       d4 = (xc - xb) * rhy;
369       d5 = (xrb - xr) * rhy;
370       d6 = (xrb - xb) * rhx;
371       d7 = (xlt - xl) * rhy;
372       d8 = (xlt - xt) * rhx;
373 
374       f1 = PetscSqrtScalar(1.0 + d1 * d1 + d7 * d7);
375       f2 = PetscSqrtScalar(1.0 + d1 * d1 + d4 * d4);
376       f3 = PetscSqrtScalar(1.0 + d3 * d3 + d8 * d8);
377       f4 = PetscSqrtScalar(1.0 + d3 * d3 + d2 * d2);
378       f5 = PetscSqrtScalar(1.0 + d2 * d2 + d5 * d5);
379       f6 = PetscSqrtScalar(1.0 + d4 * d4 + d6 * d6);
380 
381       hl = (-hydhx * (1.0 + d7 * d7) + d1 * d7) / (f1 * f1 * f1) + (-hydhx * (1.0 + d4 * d4) + d1 * d4) / (f2 * f2 * f2);
382       hr = (-hydhx * (1.0 + d5 * d5) + d2 * d5) / (f5 * f5 * f5) + (-hydhx * (1.0 + d3 * d3) + d2 * d3) / (f4 * f4 * f4);
383       ht = (-hxdhy * (1.0 + d8 * d8) + d3 * d8) / (f3 * f3 * f3) + (-hxdhy * (1.0 + d2 * d2) + d2 * d3) / (f4 * f4 * f4);
384       hb = (-hxdhy * (1.0 + d6 * d6) + d4 * d6) / (f6 * f6 * f6) + (-hxdhy * (1.0 + d1 * d1) + d1 * d4) / (f2 * f2 * f2);
385 
386       hbr = -d2 * d5 / (f5 * f5 * f5) - d4 * d6 / (f6 * f6 * f6);
387       htl = -d1 * d7 / (f1 * f1 * f1) - d3 * d8 / (f3 * f3 * f3);
388 
389       hc = hydhx * (1.0 + d7 * d7) / (f1 * f1 * f1) + hxdhy * (1.0 + d8 * d8) / (f3 * f3 * f3) + hydhx * (1.0 + d5 * d5) / (f5 * f5 * f5) + hxdhy * (1.0 + d6 * d6) / (f6 * f6 * f6) + (hxdhy * (1.0 + d1 * d1) + hydhx * (1.0 + d4 * d4) - 2 * d1 * d4) / (f2 * f2 * f2) + (hxdhy * (1.0 + d2 * d2) + hydhx * (1.0 + d3 * d3) - 2 * d2 * d3) / (f4 * f4 * f4);
390 
391       hl *= 0.5;
392       hr *= 0.5;
393       ht *= 0.5;
394       hb *= 0.5;
395       hbr *= 0.5;
396       htl *= 0.5;
397       hc *= 0.5;
398 
399       k = 0;
400       if (j > 0) {
401         v[k]   = hb;
402         col[k] = row - mx;
403         k++;
404       }
405 
406       if (j > 0 && i < mx - 1) {
407         v[k]   = hbr;
408         col[k] = row - mx + 1;
409         k++;
410       }
411 
412       if (i > 0) {
413         v[k]   = hl;
414         col[k] = row - 1;
415         k++;
416       }
417 
418       v[k]   = hc;
419       col[k] = row;
420       k++;
421 
422       if (i < mx - 1) {
423         v[k]   = hr;
424         col[k] = row + 1;
425         k++;
426       }
427 
428       if (i > 0 && j < my - 1) {
429         v[k]   = htl;
430         col[k] = row + mx - 1;
431         k++;
432       }
433 
434       if (j < my - 1) {
435         v[k]   = ht;
436         col[k] = row + mx;
437         k++;
438       }
439 
440       /*
441          Set matrix values using local numbering, which was defined
442          earlier, in the main routine.
443       */
444       PetscCall(MatSetValues(Hessian, 1, &row, k, col, v, INSERT_VALUES));
445     }
446   }
447 
448   /* Restore vectors */
449   PetscCall(VecRestoreArrayRead(X, &x));
450 
451   /* Assemble the matrix */
452   PetscCall(MatAssemblyBegin(Hessian, MAT_FINAL_ASSEMBLY));
453   PetscCall(MatAssemblyEnd(Hessian, MAT_FINAL_ASSEMBLY));
454 
455   PetscCall(PetscLogFlops(199.0 * mx * my));
456   PetscFunctionReturn(PETSC_SUCCESS);
457 }
458 
459 /* ------------------------------------------------------------------- */
460 /*
461    MSA_BoundaryConditions -  Calculates the boundary conditions for
462    the region.
463 
464    Input Parameter:
465 .  user - user-defined application context
466 
467    Output Parameter:
468 .  user - user-defined application context
469 */
470 static PetscErrorCode MSA_BoundaryConditions(AppCtx *user)
471 {
472   PetscInt   i, j, k, limit = 0;
473   PetscInt   maxits = 5;
474   PetscInt   mx = user->mx, my = user->my;
475   PetscInt   bsize = 0, lsize = 0, tsize = 0, rsize = 0;
476   PetscReal  one = 1.0, two = 2.0, three = 3.0, tol = 1e-10;
477   PetscReal  fnorm, det, hx, hy, xt = 0, yt = 0;
478   PetscReal  u1, u2, nf1, nf2, njac11, njac12, njac21, njac22;
479   PetscReal  b = -0.5, t = 0.5, l = -0.5, r = 0.5;
480   PetscReal *boundary;
481 
482   PetscFunctionBeginUser;
483   bsize = mx + 2;
484   lsize = my + 2;
485   rsize = my + 2;
486   tsize = mx + 2;
487 
488   PetscCall(PetscMalloc1(bsize, &user->bottom));
489   PetscCall(PetscMalloc1(tsize, &user->top));
490   PetscCall(PetscMalloc1(lsize, &user->left));
491   PetscCall(PetscMalloc1(rsize, &user->right));
492 
493   hx = (r - l) / (mx + 1);
494   hy = (t - b) / (my + 1);
495 
496   for (j = 0; j < 4; j++) {
497     if (j == 0) {
498       yt       = b;
499       xt       = l;
500       limit    = bsize;
501       boundary = user->bottom;
502     } else if (j == 1) {
503       yt       = t;
504       xt       = l;
505       limit    = tsize;
506       boundary = user->top;
507     } else if (j == 2) {
508       yt       = b;
509       xt       = l;
510       limit    = lsize;
511       boundary = user->left;
512     } else { /*  if (j==3) */
513       yt       = b;
514       xt       = r;
515       limit    = rsize;
516       boundary = user->right;
517     }
518 
519     for (i = 0; i < limit; i++) {
520       u1 = xt;
521       u2 = -yt;
522       for (k = 0; k < maxits; k++) {
523         nf1   = u1 + u1 * u2 * u2 - u1 * u1 * u1 / three - xt;
524         nf2   = -u2 - u1 * u1 * u2 + u2 * u2 * u2 / three - yt;
525         fnorm = PetscSqrtScalar(nf1 * nf1 + nf2 * nf2);
526         if (fnorm <= tol) break;
527         njac11 = one + u2 * u2 - u1 * u1;
528         njac12 = two * u1 * u2;
529         njac21 = -two * u1 * u2;
530         njac22 = -one - u1 * u1 + u2 * u2;
531         det    = njac11 * njac22 - njac21 * njac12;
532         u1     = u1 - (njac22 * nf1 - njac12 * nf2) / det;
533         u2     = u2 - (njac11 * nf2 - njac21 * nf1) / det;
534       }
535 
536       boundary[i] = u1 * u1 - u2 * u2;
537       if (j == 0 || j == 1) {
538         xt = xt + hx;
539       } else { /*  if (j==2 || j==3) */
540         yt = yt + hy;
541       }
542     }
543   }
544   PetscFunctionReturn(PETSC_SUCCESS);
545 }
546 
547 /* ------------------------------------------------------------------- */
548 /*
549    MSA_InitialPoint - Calculates the initial guess in one of three ways.
550 
551    Input Parameters:
552 .  user - user-defined application context
553 .  X - vector for initial guess
554 
555    Output Parameters:
556 .  X - newly computed initial guess
557 */
558 static PetscErrorCode MSA_InitialPoint(AppCtx *user, Vec X)
559 {
560   PetscInt  start = -1, i, j;
561   PetscReal zero  = 0.0;
562   PetscBool flg;
563 
564   PetscFunctionBeginUser;
565   PetscCall(VecSet(X, zero));
566   PetscCall(PetscOptionsGetInt(NULL, NULL, "-start", &start, &flg));
567 
568   if (flg && start == 0) { /* The zero vector is reasonable */
569     PetscCall(VecSet(X, zero));
570   } else { /* Take an average of the boundary conditions */
571     PetscInt   row;
572     PetscInt   mx = user->mx, my = user->my;
573     PetscReal *x;
574 
575     /* Get pointers to vector data */
576     PetscCall(VecGetArray(X, &x));
577     /* Perform local computations */
578     for (j = 0; j < my; j++) {
579       for (i = 0; i < mx; i++) {
580         row    = (j)*mx + (i);
581         x[row] = (((j + 1) * user->bottom[i + 1] + (my - j + 1) * user->top[i + 1]) / (my + 2) + ((i + 1) * user->left[j + 1] + (mx - i + 1) * user->right[j + 1]) / (mx + 2)) / 2.0;
582       }
583     }
584     /* Restore vectors */
585     PetscCall(VecRestoreArray(X, &x));
586   }
587   PetscFunctionReturn(PETSC_SUCCESS);
588 }
589 
590 /*TEST
591 
592    build:
593       requires: !complex
594 
595    test:
596       args: -tao_monitor_short -tao_type nls -mx 10 -my 8 -tao_gatol 1.e-4
597       requires: !single
598 
599    test:
600       suffix: 2
601       args: -tao_monitor_short -tao_type bmrm -mx 10 -my 8 -tao_gatol 1.e-3
602       requires: !single
603 
604    test:
605       suffix: 3
606       args: -tao_monitor_short -tao_type lmvm -mx 10 -my 8 -tao_gatol 1.e-3
607       requires: !single
608 
609    test:
610       suffix: 4
611       args: -tao_monitor_short -tao_type bntr -mx 10 -my 8 -tao_gatol 1.e-4
612       requires: !single
613 
614    test:
615       suffix: 4_ew
616       args: -tao_monitor_short -tao_type bntr -tao_ksp_ew -mx 10 -my 8 -tao_gatol 1.e-4
617       requires: !single
618 
619    test:
620       suffix: 5
621       args: -tao_monitor_short -tao_type bntl -mx 10 -my 8 -tao_gatol 1.e-4
622       requires: !single
623 
624    test:
625       suffix: 5_ew
626       args: -tao_monitor_short -tao_type bntl -tao_ksp_ew -mx 10 -my 8 -tao_gatol 1.e-4
627       requires: !single
628 
629    test:
630       suffix: 6
631       args: -tao_monitor_short -tao_type bnls -mx 10 -my 8 -tao_gatol 1.e-4
632       requires: !single
633 
634    test:
635       suffix: 6_ew
636       args: -tao_monitor_short -tao_type bnls -tao_ksp_ew -tao_bnk_ksp_ew_version 3 -mx 10 -my 8 -tao_gatol 1.e-4
637       requires: !single
638 
639    test:
640       suffix: 7
641       args: -tao_monitor_short -tao_type bntr -mx 10 -my 8 -tao_bnk_max_cg_its 3 -tao_gatol 1.e-4 -tao_bnk_cg_tao_monitor_short
642       requires: !single
643 
644    test:
645       suffix: 8
646       args: -tao_monitor_short -tao_type bntl -mx 10 -my 8 -tao_bnk_max_cg_its 3 -tao_gatol 1.e-4 -tao_bnk_cg_tao_monitor_short
647       requires: !single
648 
649    test:
650       suffix: 9
651       args: -tao_monitor_short -tao_type bnls -mx 10 -my 8 -tao_bnk_max_cg_its 3 -tao_gatol 1.e-4 -tao_bnk_cg_tao_monitor_short
652       requires: !single
653 
654    test:
655       suffix: 10
656       args: -tao_monitor_short -tao_type bnls -mx 10 -my 8 -tao_bnk_max_cg_its 3 -tao_gatol 1.e-4 -tao_mf_hessian -tao_bnk_cg_tao_monitor_short
657 
658    test:
659       suffix: 11
660       args: -tao_monitor_short -tao_type bntr -mx 10 -my 8 -tao_gatol 1.e-4 -tao_mf_hessian
661       requires: !single
662 
663    test:
664       suffix: 12
665       args: -tao_monitor_short -tao_type bntl -mx 10 -my 8 -tao_gatol 1.e-4 -tao_mf_hessian
666       requires: !single
667 
668 TEST*/
669