xref: /petsc/src/tao/bound/tutorials/plate2.c (revision d52a580b706c59ca78066c1e38754e45b6b56e2b)
1 #include <petscdmda.h>
2 #include <petsctao.h>
3 
4 static char help[] = "This example demonstrates use of the TAO package to \n\
5 solve an unconstrained minimization problem.  This example is based on a \n\
6 problem from the MINPACK-2 test suite.  Given a rectangular 2-D domain, \n\
7 boundary values along the edges of the domain, and a plate represented by \n\
8 lower boundary conditions, the objective is to find the\n\
9 surface with the minimal area that satisfies the boundary conditions.\n\
10 The command line options are:\n\
11   -mx <xg>, where <xg> = number of grid points in the 1st coordinate direction\n\
12   -my <yg>, where <yg> = number of grid points in the 2nd coordinate direction\n\
13   -bmx <bxg>, where <bxg> = number of grid points under plate in 1st direction\n\
14   -bmy <byg>, where <byg> = number of grid points under plate in 2nd direction\n\
15   -bheight <ht>, where <ht> = height of the plate\n\
16   -start <st>, where <st> =0 for zero vector, <st> >0 for random start, and <st> <0 \n\
17                for an average of the boundary conditions\n\n";
18 
19 /*
20    User-defined application context - contains data needed by the
21    application-provided call-back routines, FormFunctionGradient(),
22    FormHessian().
23 */
24 typedef struct {
25   /* problem parameters */
26   PetscReal bheight;                  /* Height of plate under the surface */
27   PetscInt  mx, my;                   /* discretization in x, y directions */
28   PetscInt  bmx, bmy;                 /* Size of plate under the surface */
29   Vec       Bottom, Top, Left, Right; /* boundary values */
30 
31   /* Working space */
32   Vec localX, localV; /* ghosted local vector */
33   DM  dm;             /* distributed array data structure */
34   Mat H;
35 } AppCtx;
36 
37 /* -------- User-defined Routines --------- */
38 
39 static PetscErrorCode MSA_BoundaryConditions(AppCtx *);
40 static PetscErrorCode MSA_InitialPoint(AppCtx *, Vec);
41 static PetscErrorCode MSA_Plate(Vec, Vec, void *);
42 PetscErrorCode        FormFunctionGradient(Tao, Vec, PetscReal *, Vec, void *);
43 PetscErrorCode        FormHessian(Tao, Vec, Mat, Mat, void *);
44 
45 /* For testing matrix-free submatrices */
46 PetscErrorCode MatrixFreeHessian(Tao, Vec, Mat, Mat, void *);
47 PetscErrorCode MyMatMult(Mat, Vec, Vec);
48 
49 int main(int argc, char **argv)
50 {
51   PetscInt               Nx, Ny;    /* number of processors in x- and y- directions */
52   PetscInt               m, N;      /* number of local and global elements in vectors */
53   Vec                    x, xl, xu; /* solution vector  and bounds*/
54   PetscBool              flg;       /* A return variable when checking for user options */
55   Tao                    tao;       /* Tao solver context */
56   ISLocalToGlobalMapping isltog;    /* local-to-global mapping object */
57   Mat                    H_shell;   /* to test matrix-free submatrices */
58   AppCtx                 user;      /* user-defined work context */
59 
60   /* Initialize PETSc, TAO */
61   PetscFunctionBeginUser;
62   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
63 
64   /* Specify default dimension of the problem */
65   user.mx      = 10;
66   user.my      = 10;
67   user.bheight = 0.1;
68 
69   /* Check for any command line arguments that override defaults */
70   PetscCall(PetscOptionsGetInt(NULL, NULL, "-mx", &user.mx, &flg));
71   PetscCall(PetscOptionsGetInt(NULL, NULL, "-my", &user.my, &flg));
72   PetscCall(PetscOptionsGetReal(NULL, NULL, "-bheight", &user.bheight, &flg));
73 
74   user.bmx = user.mx / 2;
75   user.bmy = user.my / 2;
76   PetscCall(PetscOptionsGetInt(NULL, NULL, "-bmx", &user.bmx, &flg));
77   PetscCall(PetscOptionsGetInt(NULL, NULL, "-bmy", &user.bmy, &flg));
78 
79   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "\n---- Minimum Surface Area With Plate Problem -----\n"));
80   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "mx:%" PetscInt_FMT ", my:%" PetscInt_FMT ", bmx:%" PetscInt_FMT ", bmy:%" PetscInt_FMT ", height:%g\n", user.mx, user.my, user.bmx, user.bmy, (double)user.bheight));
81 
82   /* Calculate any derived values from parameters */
83   N = user.mx * user.my;
84 
85   /* Let PETSc determine the dimensions of the local vectors */
86   Nx = PETSC_DECIDE;
87   Ny = PETSC_DECIDE;
88 
89   /*
90      A two dimensional distributed array will help define this problem,
91      which derives from an elliptic PDE on two dimensional domain.  From
92      the distributed array, Create the vectors.
93   */
94   PetscCall(DMDACreate2d(MPI_COMM_WORLD, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DMDA_STENCIL_BOX, user.mx, user.my, Nx, Ny, 1, 1, NULL, NULL, &user.dm));
95   PetscCall(DMSetFromOptions(user.dm));
96   PetscCall(DMSetUp(user.dm));
97   /*
98      Extract global and local vectors from DM; The local vectors are
99      used solely as work space for the evaluation of the function,
100      gradient, and Hessian.  Duplicate for remaining vectors that are
101      the same types.
102   */
103   PetscCall(DMCreateGlobalVector(user.dm, &x)); /* Solution */
104   PetscCall(DMCreateLocalVector(user.dm, &user.localX));
105   PetscCall(VecDuplicate(user.localX, &user.localV));
106 
107   PetscCall(VecDuplicate(x, &xl));
108   PetscCall(VecDuplicate(x, &xu));
109 
110   /* The TAO code begins here */
111 
112   /*
113      Create TAO solver and set desired solution method
114      The method must either be TAOTRON or TAOBLMVM
115      If TAOBLMVM is used, then hessian function is not called.
116   */
117   PetscCall(TaoCreate(PETSC_COMM_WORLD, &tao));
118   PetscCall(TaoSetType(tao, TAOBLMVM));
119 
120   /* Set initial solution guess; */
121   PetscCall(MSA_BoundaryConditions(&user));
122   PetscCall(MSA_InitialPoint(&user, x));
123   PetscCall(TaoSetSolution(tao, x));
124 
125   /* Set routines for function, gradient and hessian evaluation */
126   PetscCall(TaoSetObjectiveAndGradient(tao, NULL, FormFunctionGradient, (void *)&user));
127 
128   PetscCall(VecGetLocalSize(x, &m));
129   PetscCall(DMCreateMatrix(user.dm, &user.H));
130   PetscCall(MatSetOption(user.H, MAT_SYMMETRIC, PETSC_TRUE));
131 
132   PetscCall(DMGetLocalToGlobalMapping(user.dm, &isltog));
133   PetscCall(MatSetLocalToGlobalMapping(user.H, isltog, isltog));
134   PetscCall(PetscOptionsHasName(NULL, NULL, "-matrixfree", &flg));
135   if (flg) {
136     PetscCall(MatCreateShell(PETSC_COMM_WORLD, m, m, N, N, (void *)&user, &H_shell));
137     PetscCall(MatShellSetOperation(H_shell, MATOP_MULT, (PetscErrorCodeFn *)MyMatMult));
138     PetscCall(MatSetOption(H_shell, MAT_SYMMETRIC, PETSC_TRUE));
139     PetscCall(TaoSetHessian(tao, H_shell, H_shell, MatrixFreeHessian, (void *)&user));
140   } else {
141     PetscCall(TaoSetHessian(tao, user.H, user.H, FormHessian, (void *)&user));
142   }
143 
144   /* Set Variable bounds */
145   PetscCall(MSA_Plate(xl, xu, (void *)&user));
146   PetscCall(TaoSetVariableBounds(tao, xl, xu));
147 
148   /* Check for any tao command line options */
149   PetscCall(TaoSetFromOptions(tao));
150 
151   /* SOLVE THE APPLICATION */
152   PetscCall(TaoSolve(tao));
153 
154   PetscCall(TaoView(tao, PETSC_VIEWER_STDOUT_WORLD));
155 
156   /* Free TAO data structures */
157   PetscCall(TaoDestroy(&tao));
158 
159   /* Free PETSc data structures */
160   PetscCall(VecDestroy(&x));
161   PetscCall(VecDestroy(&xl));
162   PetscCall(VecDestroy(&xu));
163   PetscCall(MatDestroy(&user.H));
164   PetscCall(VecDestroy(&user.localX));
165   PetscCall(VecDestroy(&user.localV));
166   PetscCall(VecDestroy(&user.Bottom));
167   PetscCall(VecDestroy(&user.Top));
168   PetscCall(VecDestroy(&user.Left));
169   PetscCall(VecDestroy(&user.Right));
170   PetscCall(DMDestroy(&user.dm));
171   if (flg) PetscCall(MatDestroy(&H_shell));
172   PetscCall(PetscFinalize());
173   return 0;
174 }
175 
176 /*  FormFunctionGradient - Evaluates f(x) and gradient g(x).
177 
178     Input Parameters:
179 .   tao     - the Tao context
180 .   X      - input vector
181 .   userCtx - optional user-defined context, as set by TaoSetObjectiveAndGradient()
182 
183     Output Parameters:
184 .   fcn     - the function value
185 .   G      - vector containing the newly evaluated gradient
186 
187    Notes:
188    In this case, we discretize the domain and Create triangles. The
189    surface of each triangle is planar, whose surface area can be easily
190    computed.  The total surface area is found by sweeping through the grid
191    and computing the surface area of the two triangles that have their
192    right angle at the grid point.  The diagonal line segments on the
193    grid that define the triangles run from top left to lower right.
194    The numbering of points starts at the lower left and runs left to
195    right, then bottom to top.
196 */
197 PetscErrorCode FormFunctionGradient(Tao tao, Vec X, PetscReal *fcn, Vec G, void *userCtx)
198 {
199   AppCtx    *user = (AppCtx *)userCtx;
200   PetscInt   i, j, row;
201   PetscInt   mx = user->mx, my = user->my;
202   PetscInt   xs, xm, gxs, gxm, ys, ym, gys, gym;
203   PetscReal  ft   = 0;
204   PetscReal  zero = 0.0;
205   PetscReal  hx = 1.0 / (mx + 1), hy = 1.0 / (my + 1), hydhx = hy / hx, hxdhy = hx / hy, area = 0.5 * hx * hy;
206   PetscReal  rhx = mx + 1, rhy = my + 1;
207   PetscReal  f1, f2, f3, f4, f5, f6, d1, d2, d3, d4, d5, d6, d7, d8, xc, xl, xr, xt, xb, xlt, xrb;
208   PetscReal  df1dxc, df2dxc, df3dxc, df4dxc, df5dxc, df6dxc;
209   PetscReal *g, *x, *left, *right, *bottom, *top;
210   Vec        localX = user->localX, localG = user->localV;
211 
212   PetscFunctionBeginUser;
213   /* Get local mesh boundaries */
214   PetscCall(DMDAGetCorners(user->dm, &xs, &ys, NULL, &xm, &ym, NULL));
215   PetscCall(DMDAGetGhostCorners(user->dm, &gxs, &gys, NULL, &gxm, &gym, NULL));
216 
217   /* Scatter ghost points to local vector */
218   PetscCall(DMGlobalToLocalBegin(user->dm, X, INSERT_VALUES, localX));
219   PetscCall(DMGlobalToLocalEnd(user->dm, X, INSERT_VALUES, localX));
220 
221   /* Initialize vector to zero */
222   PetscCall(VecSet(localG, zero));
223 
224   /* Get pointers to vector data */
225   PetscCall(VecGetArray(localX, &x));
226   PetscCall(VecGetArray(localG, &g));
227   PetscCall(VecGetArray(user->Top, &top));
228   PetscCall(VecGetArray(user->Bottom, &bottom));
229   PetscCall(VecGetArray(user->Left, &left));
230   PetscCall(VecGetArray(user->Right, &right));
231 
232   /* Compute function over the locally owned part of the mesh */
233   for (j = ys; j < ys + ym; j++) {
234     for (i = xs; i < xs + xm; i++) {
235       row = (j - gys) * gxm + (i - gxs);
236 
237       xc  = x[row];
238       xlt = xrb = xl = xr = xb = xt = xc;
239 
240       if (i == 0) { /* left side */
241         xl  = left[j - ys + 1];
242         xlt = left[j - ys + 2];
243       } else {
244         xl = x[row - 1];
245       }
246 
247       if (j == 0) { /* bottom side */
248         xb  = bottom[i - xs + 1];
249         xrb = bottom[i - xs + 2];
250       } else {
251         xb = x[row - gxm];
252       }
253 
254       if (i + 1 == gxs + gxm) { /* right side */
255         xr  = right[j - ys + 1];
256         xrb = right[j - ys];
257       } else {
258         xr = x[row + 1];
259       }
260 
261       if (j + 1 == gys + gym) { /* top side */
262         xt  = top[i - xs + 1];
263         xlt = top[i - xs];
264       } else {
265         xt = x[row + gxm];
266       }
267 
268       if (i > gxs && j + 1 < gys + gym) xlt = x[row - 1 + gxm];
269       if (j > gys && i + 1 < gxs + gxm) xrb = x[row + 1 - gxm];
270 
271       d1 = (xc - xl);
272       d2 = (xc - xr);
273       d3 = (xc - xt);
274       d4 = (xc - xb);
275       d5 = (xr - xrb);
276       d6 = (xrb - xb);
277       d7 = (xlt - xl);
278       d8 = (xt - xlt);
279 
280       df1dxc = d1 * hydhx;
281       df2dxc = (d1 * hydhx + d4 * hxdhy);
282       df3dxc = d3 * hxdhy;
283       df4dxc = (d2 * hydhx + d3 * hxdhy);
284       df5dxc = d2 * hydhx;
285       df6dxc = d4 * hxdhy;
286 
287       d1 *= rhx;
288       d2 *= rhx;
289       d3 *= rhy;
290       d4 *= rhy;
291       d5 *= rhy;
292       d6 *= rhx;
293       d7 *= rhy;
294       d8 *= rhx;
295 
296       f1 = PetscSqrtScalar(1.0 + d1 * d1 + d7 * d7);
297       f2 = PetscSqrtScalar(1.0 + d1 * d1 + d4 * d4);
298       f3 = PetscSqrtScalar(1.0 + d3 * d3 + d8 * d8);
299       f4 = PetscSqrtScalar(1.0 + d3 * d3 + d2 * d2);
300       f5 = PetscSqrtScalar(1.0 + d2 * d2 + d5 * d5);
301       f6 = PetscSqrtScalar(1.0 + d4 * d4 + d6 * d6);
302 
303       ft = ft + (f2 + f4);
304 
305       df1dxc /= f1;
306       df2dxc /= f2;
307       df3dxc /= f3;
308       df4dxc /= f4;
309       df5dxc /= f5;
310       df6dxc /= f6;
311 
312       g[row] = (df1dxc + df2dxc + df3dxc + df4dxc + df5dxc + df6dxc) * 0.5;
313     }
314   }
315 
316   /* Compute triangular areas along the border of the domain. */
317   if (xs == 0) { /* left side */
318     for (j = ys; j < ys + ym; j++) {
319       d3 = (left[j - ys + 1] - left[j - ys + 2]) * rhy;
320       d2 = (left[j - ys + 1] - x[(j - gys) * gxm]) * rhx;
321       ft = ft + PetscSqrtScalar(1.0 + d3 * d3 + d2 * d2);
322     }
323   }
324   if (ys == 0) { /* bottom side */
325     for (i = xs; i < xs + xm; i++) {
326       d2 = (bottom[i + 1 - xs] - bottom[i - xs + 2]) * rhx;
327       d3 = (bottom[i - xs + 1] - x[i - gxs]) * rhy;
328       ft = ft + PetscSqrtScalar(1.0 + d3 * d3 + d2 * d2);
329     }
330   }
331 
332   if (xs + xm == mx) { /* right side */
333     for (j = ys; j < ys + ym; j++) {
334       d1 = (x[(j + 1 - gys) * gxm - 1] - right[j - ys + 1]) * rhx;
335       d4 = (right[j - ys] - right[j - ys + 1]) * rhy;
336       ft = ft + PetscSqrtScalar(1.0 + d1 * d1 + d4 * d4);
337     }
338   }
339   if (ys + ym == my) { /* top side */
340     for (i = xs; i < xs + xm; i++) {
341       d1 = (x[(gym - 1) * gxm + i - gxs] - top[i - xs + 1]) * rhy;
342       d4 = (top[i - xs + 1] - top[i - xs]) * rhx;
343       ft = ft + PetscSqrtScalar(1.0 + d1 * d1 + d4 * d4);
344     }
345   }
346 
347   if (ys == 0 && xs == 0) {
348     d1 = (left[0] - left[1]) * rhy;
349     d2 = (bottom[0] - bottom[1]) * rhx;
350     ft += PetscSqrtScalar(1.0 + d1 * d1 + d2 * d2);
351   }
352   if (ys + ym == my && xs + xm == mx) {
353     d1 = (right[ym + 1] - right[ym]) * rhy;
354     d2 = (top[xm + 1] - top[xm]) * rhx;
355     ft += PetscSqrtScalar(1.0 + d1 * d1 + d2 * d2);
356   }
357 
358   ft = ft * area;
359   PetscCallMPI(MPIU_Allreduce(&ft, fcn, 1, MPIU_REAL, MPIU_SUM, MPI_COMM_WORLD));
360 
361   /* Restore vectors */
362   PetscCall(VecRestoreArray(localX, &x));
363   PetscCall(VecRestoreArray(localG, &g));
364   PetscCall(VecRestoreArray(user->Left, &left));
365   PetscCall(VecRestoreArray(user->Top, &top));
366   PetscCall(VecRestoreArray(user->Bottom, &bottom));
367   PetscCall(VecRestoreArray(user->Right, &right));
368 
369   /* Scatter values to global vector */
370   PetscCall(DMLocalToGlobalBegin(user->dm, localG, INSERT_VALUES, G));
371   PetscCall(DMLocalToGlobalEnd(user->dm, localG, INSERT_VALUES, G));
372 
373   PetscCall(PetscLogFlops(70.0 * xm * ym));
374   PetscFunctionReturn(PETSC_SUCCESS);
375 }
376 
377 /* ------------------------------------------------------------------- */
378 /*
379    FormHessian - Evaluates Hessian matrix.
380 
381    Input Parameters:
382 .  tao  - the Tao context
383 .  x    - input vector
384 .  ptr  - optional user-defined context, as set by TaoSetHessian()
385 
386    Output Parameters:
387 .  A    - Hessian matrix
388 .  B    - optionally different matrix used to construct the preconditioner
389 
390    Notes:
391    Due to mesh point reordering with DMs, we must always work
392    with the local mesh points, and then transform them to the new
393    global numbering with the local-to-global mapping.  We cannot work
394    directly with the global numbers for the original uniprocessor mesh!
395 
396    Two methods are available for imposing this transformation
397    when setting matrix entries:
398      (A) MatSetValuesLocal(), using the local ordering (including
399          ghost points!)
400          - Do the following two steps once, before calling TaoSolve()
401            - Use DMGetISLocalToGlobalMapping() to extract the
402              local-to-global map from the DM
403            - Associate this map with the matrix by calling
404              MatSetLocalToGlobalMapping()
405          - Then set matrix entries using the local ordering
406            by calling MatSetValuesLocal()
407      (B) MatSetValues(), using the global ordering
408          - Use DMGetGlobalIndices() to extract the local-to-global map
409          - Then apply this map explicitly yourself
410          - Set matrix entries using the global ordering by calling
411            MatSetValues()
412    Option (A) seems cleaner/easier in many cases, and is the procedure
413    used in this example.
414 */
415 PetscErrorCode FormHessian(Tao tao, Vec X, Mat Hptr, Mat Hessian, void *ptr)
416 {
417   AppCtx    *user = (AppCtx *)ptr;
418   PetscInt   i, j, k, row;
419   PetscInt   mx = user->mx, my = user->my;
420   PetscInt   xs, xm, gxs, gxm, ys, ym, gys, gym, col[7];
421   PetscReal  hx = 1.0 / (mx + 1), hy = 1.0 / (my + 1), hydhx = hy / hx, hxdhy = hx / hy;
422   PetscReal  rhx = mx + 1, rhy = my + 1;
423   PetscReal  f1, f2, f3, f4, f5, f6, d1, d2, d3, d4, d5, d6, d7, d8, xc, xl, xr, xt, xb, xlt, xrb;
424   PetscReal  hl, hr, ht, hb, hc, htl, hbr;
425   PetscReal *x, *left, *right, *bottom, *top;
426   PetscReal  v[7];
427   Vec        localX = user->localX;
428   PetscBool  assembled;
429 
430   PetscFunctionBeginUser;
431   /* Set various matrix options */
432   PetscCall(MatSetOption(Hessian, MAT_IGNORE_OFF_PROC_ENTRIES, PETSC_TRUE));
433 
434   /* Initialize matrix entries to zero */
435   PetscCall(MatAssembled(Hessian, &assembled));
436   if (assembled) PetscCall(MatZeroEntries(Hessian));
437 
438   /* Get local mesh boundaries */
439   PetscCall(DMDAGetCorners(user->dm, &xs, &ys, NULL, &xm, &ym, NULL));
440   PetscCall(DMDAGetGhostCorners(user->dm, &gxs, &gys, NULL, &gxm, &gym, NULL));
441 
442   /* Scatter ghost points to local vector */
443   PetscCall(DMGlobalToLocalBegin(user->dm, X, INSERT_VALUES, localX));
444   PetscCall(DMGlobalToLocalEnd(user->dm, X, INSERT_VALUES, localX));
445 
446   /* Get pointers to vector data */
447   PetscCall(VecGetArray(localX, &x));
448   PetscCall(VecGetArray(user->Top, &top));
449   PetscCall(VecGetArray(user->Bottom, &bottom));
450   PetscCall(VecGetArray(user->Left, &left));
451   PetscCall(VecGetArray(user->Right, &right));
452 
453   /* Compute Hessian over the locally owned part of the mesh */
454 
455   for (i = xs; i < xs + xm; i++) {
456     for (j = ys; j < ys + ym; j++) {
457       row = (j - gys) * gxm + (i - gxs);
458 
459       xc  = x[row];
460       xlt = xrb = xl = xr = xb = xt = xc;
461 
462       /* Left side */
463       if (i == gxs) {
464         xl  = left[j - ys + 1];
465         xlt = left[j - ys + 2];
466       } else {
467         xl = x[row - 1];
468       }
469 
470       if (j == gys) {
471         xb  = bottom[i - xs + 1];
472         xrb = bottom[i - xs + 2];
473       } else {
474         xb = x[row - gxm];
475       }
476 
477       if (i + 1 == gxs + gxm) {
478         xr  = right[j - ys + 1];
479         xrb = right[j - ys];
480       } else {
481         xr = x[row + 1];
482       }
483 
484       if (j + 1 == gys + gym) {
485         xt  = top[i - xs + 1];
486         xlt = top[i - xs];
487       } else {
488         xt = x[row + gxm];
489       }
490 
491       if (i > gxs && j + 1 < gys + gym) xlt = x[row - 1 + gxm];
492       if (j > gys && i + 1 < gxs + gxm) xrb = x[row + 1 - gxm];
493 
494       d1 = (xc - xl) * rhx;
495       d2 = (xc - xr) * rhx;
496       d3 = (xc - xt) * rhy;
497       d4 = (xc - xb) * rhy;
498       d5 = (xrb - xr) * rhy;
499       d6 = (xrb - xb) * rhx;
500       d7 = (xlt - xl) * rhy;
501       d8 = (xlt - xt) * rhx;
502 
503       f1 = PetscSqrtScalar(1.0 + d1 * d1 + d7 * d7);
504       f2 = PetscSqrtScalar(1.0 + d1 * d1 + d4 * d4);
505       f3 = PetscSqrtScalar(1.0 + d3 * d3 + d8 * d8);
506       f4 = PetscSqrtScalar(1.0 + d3 * d3 + d2 * d2);
507       f5 = PetscSqrtScalar(1.0 + d2 * d2 + d5 * d5);
508       f6 = PetscSqrtScalar(1.0 + d4 * d4 + d6 * d6);
509 
510       hl = (-hydhx * (1.0 + d7 * d7) + d1 * d7) / (f1 * f1 * f1) + (-hydhx * (1.0 + d4 * d4) + d1 * d4) / (f2 * f2 * f2);
511       hr = (-hydhx * (1.0 + d5 * d5) + d2 * d5) / (f5 * f5 * f5) + (-hydhx * (1.0 + d3 * d3) + d2 * d3) / (f4 * f4 * f4);
512       ht = (-hxdhy * (1.0 + d8 * d8) + d3 * d8) / (f3 * f3 * f3) + (-hxdhy * (1.0 + d2 * d2) + d2 * d3) / (f4 * f4 * f4);
513       hb = (-hxdhy * (1.0 + d6 * d6) + d4 * d6) / (f6 * f6 * f6) + (-hxdhy * (1.0 + d1 * d1) + d1 * d4) / (f2 * f2 * f2);
514 
515       hbr = -d2 * d5 / (f5 * f5 * f5) - d4 * d6 / (f6 * f6 * f6);
516       htl = -d1 * d7 / (f1 * f1 * f1) - d3 * d8 / (f3 * f3 * f3);
517 
518       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);
519 
520       hl *= 0.5;
521       hr *= 0.5;
522       ht *= 0.5;
523       hb *= 0.5;
524       hbr *= 0.5;
525       htl *= 0.5;
526       hc *= 0.5;
527 
528       k = 0;
529       if (j > 0) {
530         v[k]   = hb;
531         col[k] = row - gxm;
532         k++;
533       }
534 
535       if (j > 0 && i < mx - 1) {
536         v[k]   = hbr;
537         col[k] = row - gxm + 1;
538         k++;
539       }
540 
541       if (i > 0) {
542         v[k]   = hl;
543         col[k] = row - 1;
544         k++;
545       }
546 
547       v[k]   = hc;
548       col[k] = row;
549       k++;
550 
551       if (i < mx - 1) {
552         v[k]   = hr;
553         col[k] = row + 1;
554         k++;
555       }
556 
557       if (i > 0 && j < my - 1) {
558         v[k]   = htl;
559         col[k] = row + gxm - 1;
560         k++;
561       }
562 
563       if (j < my - 1) {
564         v[k]   = ht;
565         col[k] = row + gxm;
566         k++;
567       }
568 
569       /*
570          Set matrix values using local numbering, which was defined
571          earlier, in the main routine.
572       */
573       PetscCall(MatSetValuesLocal(Hessian, 1, &row, k, col, v, INSERT_VALUES));
574     }
575   }
576 
577   /* Restore vectors */
578   PetscCall(VecRestoreArray(localX, &x));
579   PetscCall(VecRestoreArray(user->Left, &left));
580   PetscCall(VecRestoreArray(user->Top, &top));
581   PetscCall(VecRestoreArray(user->Bottom, &bottom));
582   PetscCall(VecRestoreArray(user->Right, &right));
583 
584   /* Assemble the matrix */
585   PetscCall(MatAssemblyBegin(Hessian, MAT_FINAL_ASSEMBLY));
586   PetscCall(MatAssemblyEnd(Hessian, MAT_FINAL_ASSEMBLY));
587 
588   PetscCall(PetscLogFlops(199.0 * xm * ym));
589   PetscFunctionReturn(PETSC_SUCCESS);
590 }
591 
592 /* ------------------------------------------------------------------- */
593 /*
594    MSA_BoundaryConditions -  Calculates the boundary conditions for
595    the region.
596 
597    Input Parameter:
598 .  user - user-defined application context
599 
600    Output Parameter:
601 .  user - user-defined application context
602 */
603 static PetscErrorCode MSA_BoundaryConditions(AppCtx *user)
604 {
605   PetscInt   i, j, k, maxits = 5, limit = 0;
606   PetscInt   xs, ys, xm, ym, gxs, gys, gxm, gym;
607   PetscInt   mx = user->mx, my = user->my;
608   PetscInt   bsize = 0, lsize = 0, tsize = 0, rsize = 0;
609   PetscReal  one = 1.0, two = 2.0, three = 3.0, scl = 1.0, tol = 1e-10;
610   PetscReal  fnorm, det, hx, hy, xt = 0, yt = 0;
611   PetscReal  u1, u2, nf1, nf2, njac11, njac12, njac21, njac22;
612   PetscReal  b = -0.5, t = 0.5, l = -0.5, r = 0.5;
613   PetscReal *boundary;
614   PetscBool  flg;
615   Vec        Bottom, Top, Right, Left;
616 
617   PetscFunctionBeginUser;
618   /* Get local mesh boundaries */
619   PetscCall(DMDAGetCorners(user->dm, &xs, &ys, NULL, &xm, &ym, NULL));
620   PetscCall(DMDAGetGhostCorners(user->dm, &gxs, &gys, NULL, &gxm, &gym, NULL));
621 
622   bsize = xm + 2;
623   lsize = ym + 2;
624   rsize = ym + 2;
625   tsize = xm + 2;
626 
627   PetscCall(VecCreateMPI(MPI_COMM_WORLD, bsize, PETSC_DECIDE, &Bottom));
628   PetscCall(VecCreateMPI(MPI_COMM_WORLD, tsize, PETSC_DECIDE, &Top));
629   PetscCall(VecCreateMPI(MPI_COMM_WORLD, lsize, PETSC_DECIDE, &Left));
630   PetscCall(VecCreateMPI(MPI_COMM_WORLD, rsize, PETSC_DECIDE, &Right));
631 
632   user->Top    = Top;
633   user->Left   = Left;
634   user->Bottom = Bottom;
635   user->Right  = Right;
636 
637   hx = (r - l) / (mx + 1);
638   hy = (t - b) / (my + 1);
639 
640   for (j = 0; j < 4; j++) {
641     if (j == 0) {
642       yt    = b;
643       xt    = l + hx * xs;
644       limit = bsize;
645       PetscCall(VecGetArray(Bottom, &boundary));
646     } else if (j == 1) {
647       yt    = t;
648       xt    = l + hx * xs;
649       limit = tsize;
650       PetscCall(VecGetArray(Top, &boundary));
651     } else if (j == 2) {
652       yt    = b + hy * ys;
653       xt    = l;
654       limit = lsize;
655       PetscCall(VecGetArray(Left, &boundary));
656     } else if (j == 3) {
657       yt    = b + hy * ys;
658       xt    = r;
659       limit = rsize;
660       PetscCall(VecGetArray(Right, &boundary));
661     }
662 
663     for (i = 0; i < limit; i++) {
664       u1 = xt;
665       u2 = -yt;
666       for (k = 0; k < maxits; k++) {
667         nf1   = u1 + u1 * u2 * u2 - u1 * u1 * u1 / three - xt;
668         nf2   = -u2 - u1 * u1 * u2 + u2 * u2 * u2 / three - yt;
669         fnorm = PetscSqrtScalar(nf1 * nf1 + nf2 * nf2);
670         if (fnorm <= tol) break;
671         njac11 = one + u2 * u2 - u1 * u1;
672         njac12 = two * u1 * u2;
673         njac21 = -two * u1 * u2;
674         njac22 = -one - u1 * u1 + u2 * u2;
675         det    = njac11 * njac22 - njac21 * njac12;
676         u1     = u1 - (njac22 * nf1 - njac12 * nf2) / det;
677         u2     = u2 - (njac11 * nf2 - njac21 * nf1) / det;
678       }
679 
680       boundary[i] = u1 * u1 - u2 * u2;
681       if (j == 0 || j == 1) {
682         xt = xt + hx;
683       } else if (j == 2 || j == 3) {
684         yt = yt + hy;
685       }
686     }
687     if (j == 0) {
688       PetscCall(VecRestoreArray(Bottom, &boundary));
689     } else if (j == 1) {
690       PetscCall(VecRestoreArray(Top, &boundary));
691     } else if (j == 2) {
692       PetscCall(VecRestoreArray(Left, &boundary));
693     } else if (j == 3) {
694       PetscCall(VecRestoreArray(Right, &boundary));
695     }
696   }
697 
698   /* Scale the boundary if desired */
699 
700   PetscCall(PetscOptionsGetReal(NULL, NULL, "-bottom", &scl, &flg));
701   if (flg) PetscCall(VecScale(Bottom, scl));
702   PetscCall(PetscOptionsGetReal(NULL, NULL, "-top", &scl, &flg));
703   if (flg) PetscCall(VecScale(Top, scl));
704   PetscCall(PetscOptionsGetReal(NULL, NULL, "-right", &scl, &flg));
705   if (flg) PetscCall(VecScale(Right, scl));
706 
707   PetscCall(PetscOptionsGetReal(NULL, NULL, "-left", &scl, &flg));
708   if (flg) PetscCall(VecScale(Left, scl));
709   PetscFunctionReturn(PETSC_SUCCESS);
710 }
711 
712 /* ------------------------------------------------------------------- */
713 /*
714    MSA_Plate -  Calculates an obstacle for surface to stretch over.
715 
716    Input Parameter:
717 .  user - user-defined application context
718 
719    Output Parameter:
720 .  user - user-defined application context
721 */
722 static PetscErrorCode MSA_Plate(Vec XL, Vec XU, PetscCtx ctx)
723 {
724   AppCtx    *user = (AppCtx *)ctx;
725   PetscInt   i, j, row;
726   PetscInt   xs, ys, xm, ym;
727   PetscInt   mx = user->mx, my = user->my, bmy, bmx;
728   PetscReal  t1, t2, t3;
729   PetscReal *xl, lb = PETSC_NINFINITY, ub = PETSC_INFINITY;
730   PetscBool  cylinder;
731 
732   PetscFunctionBeginUser;
733   user->bmy = PetscMax(0, user->bmy);
734   user->bmy = PetscMin(my, user->bmy);
735   user->bmx = PetscMax(0, user->bmx);
736   user->bmx = PetscMin(mx, user->bmx);
737   bmy       = user->bmy;
738   bmx       = user->bmx;
739 
740   PetscCall(DMDAGetCorners(user->dm, &xs, &ys, NULL, &xm, &ym, NULL));
741 
742   PetscCall(VecSet(XL, lb));
743   PetscCall(VecSet(XU, ub));
744 
745   PetscCall(VecGetArray(XL, &xl));
746 
747   PetscCall(PetscOptionsHasName(NULL, NULL, "-cylinder", &cylinder));
748   /* Compute the optional lower box */
749   if (cylinder) {
750     for (i = xs; i < xs + xm; i++) {
751       for (j = ys; j < ys + ym; j++) {
752         row = (j - ys) * xm + (i - xs);
753         t1  = (2.0 * i - mx) * bmy;
754         t2  = (2.0 * j - my) * bmx;
755         t3  = bmx * bmx * bmy * bmy;
756         if (t1 * t1 + t2 * t2 <= t3) xl[row] = user->bheight;
757       }
758     }
759   } else {
760     /* Compute the optional lower box */
761     for (i = xs; i < xs + xm; i++) {
762       for (j = ys; j < ys + ym; j++) {
763         row = (j - ys) * xm + (i - xs);
764         if (i >= (mx - bmx) / 2 && i < mx - (mx - bmx) / 2 && j >= (my - bmy) / 2 && j < my - (my - bmy) / 2) xl[row] = user->bheight;
765       }
766     }
767   }
768   PetscCall(VecRestoreArray(XL, &xl));
769   PetscFunctionReturn(PETSC_SUCCESS);
770 }
771 
772 /* ------------------------------------------------------------------- */
773 /*
774    MSA_InitialPoint - Calculates the initial guess in one of three ways.
775 
776    Input Parameters:
777 .  user - user-defined application context
778 .  X - vector for initial guess
779 
780    Output Parameters:
781 .  X - newly computed initial guess
782 */
783 static PetscErrorCode MSA_InitialPoint(AppCtx *user, Vec X)
784 {
785   PetscInt  start = -1, i, j;
786   PetscReal zero  = 0.0;
787   PetscBool flg;
788 
789   PetscFunctionBeginUser;
790   PetscCall(PetscOptionsGetInt(NULL, NULL, "-start", &start, &flg));
791   if (flg && start == 0) { /* The zero vector is reasonable */
792     PetscCall(VecSet(X, zero));
793   } else if (flg && start > 0) { /* Try a random start between -0.5 and 0.5 */
794     PetscRandom rctx;
795     PetscReal   np5 = -0.5;
796 
797     PetscCall(PetscRandomCreate(MPI_COMM_WORLD, &rctx));
798     for (i = 0; i < start; i++) PetscCall(VecSetRandom(X, rctx));
799     PetscCall(PetscRandomDestroy(&rctx));
800     PetscCall(VecShift(X, np5));
801 
802   } else { /* Take an average of the boundary conditions */
803 
804     PetscInt   row, xs, xm, gxs, gxm, ys, ym, gys, gym;
805     PetscInt   mx = user->mx, my = user->my;
806     PetscReal *x, *left, *right, *bottom, *top;
807     Vec        localX = user->localX;
808 
809     /* Get local mesh boundaries */
810     PetscCall(DMDAGetCorners(user->dm, &xs, &ys, NULL, &xm, &ym, NULL));
811     PetscCall(DMDAGetGhostCorners(user->dm, &gxs, &gys, NULL, &gxm, &gym, NULL));
812 
813     /* Get pointers to vector data */
814     PetscCall(VecGetArray(user->Top, &top));
815     PetscCall(VecGetArray(user->Bottom, &bottom));
816     PetscCall(VecGetArray(user->Left, &left));
817     PetscCall(VecGetArray(user->Right, &right));
818 
819     PetscCall(VecGetArray(localX, &x));
820     /* Perform local computations */
821     for (j = ys; j < ys + ym; j++) {
822       for (i = xs; i < xs + xm; i++) {
823         row    = (j - gys) * gxm + (i - gxs);
824         x[row] = ((j + 1) * bottom[i - xs + 1] / my + (my - j + 1) * top[i - xs + 1] / (my + 2) + (i + 1) * left[j - ys + 1] / mx + (mx - i + 1) * right[j - ys + 1] / (mx + 2)) / 2.0;
825       }
826     }
827 
828     /* Restore vectors */
829     PetscCall(VecRestoreArray(localX, &x));
830 
831     PetscCall(VecRestoreArray(user->Left, &left));
832     PetscCall(VecRestoreArray(user->Top, &top));
833     PetscCall(VecRestoreArray(user->Bottom, &bottom));
834     PetscCall(VecRestoreArray(user->Right, &right));
835 
836     /* Scatter values into global vector */
837     PetscCall(DMLocalToGlobalBegin(user->dm, localX, INSERT_VALUES, X));
838     PetscCall(DMLocalToGlobalEnd(user->dm, localX, INSERT_VALUES, X));
839   }
840   PetscFunctionReturn(PETSC_SUCCESS);
841 }
842 
843 /* For testing matrix-free submatrices */
844 PetscErrorCode MatrixFreeHessian(Tao tao, Vec x, Mat H, Mat Hpre, void *ptr)
845 {
846   AppCtx *user = (AppCtx *)ptr;
847 
848   PetscFunctionBegin;
849   PetscCall(FormHessian(tao, x, user->H, user->H, ptr));
850   PetscFunctionReturn(PETSC_SUCCESS);
851 }
852 
853 PetscErrorCode MyMatMult(Mat H_shell, Vec X, Vec Y)
854 {
855   void   *ptr;
856   AppCtx *user;
857 
858   PetscFunctionBegin;
859   PetscCall(MatShellGetContext(H_shell, &ptr));
860   user = (AppCtx *)ptr;
861   PetscCall(MatMult(user->H, X, Y));
862   PetscFunctionReturn(PETSC_SUCCESS);
863 }
864 
865 /*TEST
866 
867    build:
868       requires: !complex
869 
870    test:
871       args: -tao_monitor_short -mx 8 -my 6 -bmx 3 -bmy 3 -bheight 0.2 -tao_type tron -tao_gatol 1.e-5
872       requires: !single
873 
874    test:
875       suffix: 2
876       nsize: 2
877       args: -tao_monitor_short -mx 8 -my 8 -bmx 2 -bmy 5 -bheight 0.3 -tao_type blmvm -tao_gatol 1.e-4
878       requires: !single
879 
880    test:
881       suffix: 3
882       nsize: 3
883       args: -tao_monitor_short -mx 8 -my 12 -bmx 4 -bmy 10 -bheight 0.1 -tao_type tron -tao_gatol 1.e-5
884       requires: !single
885 
886    test:
887       suffix: 4
888       nsize: 3
889       args: -tao_monitor_short -mx 8 -my 12 -bmx 4 -bmy 10 -bheight 0.1 -tao_subset_type mask -tao_type tron -tao_gatol 1.e-5
890       requires: !single
891 
892    test:
893       suffix: 5
894       nsize: 3
895       args: -tao_monitor_short -mx 8 -my 12 -bmx 4 -bmy 10 -bheight 0.1 -tao_subset_type matrixfree -matrixfree -pc_type none -tao_type tron -tao_gatol 1.e-5
896       requires: !single
897 
898    test:
899       suffix: 6
900       nsize: 3
901       args: -tao_monitor_short -mx 8 -my 12 -bmx 4 -bmy 10 -bheight 0.1 -tao_subset_type matrixfree -matrixfree -tao_type blmvm -tao_gatol 1.e-4
902       requires: !single
903 
904    test:
905       suffix: 7
906       nsize: 3
907       args: -tao_monitor_short -mx 8 -my 12 -bmx 4 -bmy 10 -bheight 0.1 -tao_subset_type matrixfree -pc_type none -tao_type gpcg -tao_gatol 1.e-5
908       requires: !single
909 
910    test:
911       suffix: 8
912       args: -tao_monitor_short -mx 8 -my 6 -bmx 3 -bmy 3 -bheight 0.2 -tao_type bncg -tao_bncg_type gd -tao_gatol 1e-4
913       requires: !single
914 
915    test:
916       suffix: 9
917       args: -tao_monitor_short -mx 8 -my 6 -bmx 3 -bmy 3 -bheight 0.2 -tao_type bncg -tao_gatol 1e-4
918       requires: !single
919 
920    test:
921       suffix: 10
922       args: -tao_monitor_short -mx 8 -my 6 -bmx 3 -bmy 3 -bheight 0.2 -tao_type bnls -tao_gatol 1e-5
923       requires: !single
924 
925    test:
926       suffix: 11
927       args: -tao_monitor_short -mx 8 -my 6 -bmx 3 -bmy 3 -bheight 0.2 -tao_type bntr -tao_gatol 1e-5
928       requires: !single
929 
930    test:
931       suffix: 12
932       args: -tao_monitor_short -mx 8 -my 6 -bmx 3 -bmy 3 -bheight 0.2 -tao_type bntl -tao_gatol 1e-5
933       requires: !single
934 
935    test:
936       suffix: 13
937       args: -tao_monitor_short -mx 8 -my 6 -bmx 3 -bmy 3 -bheight 0.2 -tao_type bnls -tao_gatol 1e-5 -tao_bnk_max_cg_its 3
938       requires: !single
939 
940    test:
941       suffix: 14
942       args: -tao_monitor_short -mx 8 -my 6 -bmx 3 -bmy 3 -bheight 0.2 -tao_type bntr -tao_gatol 1e-5 -tao_bnk_max_cg_its 3
943       requires: !single
944 
945    test:
946       suffix: 15
947       args: -tao_monitor_short -mx 8 -my 6 -bmx 3 -bmy 3 -bheight 0.2 -tao_type bntl -tao_gatol 1e-5 -tao_bnk_max_cg_its 3
948       requires: !single
949 
950    test:
951      suffix: 16
952      args: -tao_monitor_short -mx 8 -my 8 -bmx 2 -bmy 5 -bheight 0.3 -tao_gatol 1e-4 -tao_type bqnls
953      requires: !single
954 
955    test:
956      suffix: 17
957      args: -tao_monitor_short -mx 8 -my 8 -bmx 2 -bmy 5 -bheight 0.3 -tao_gatol 1e-4 -tao_type bqnkls -tao_bqnk_mat_type lmvmbfgs
958      requires: !single
959 
960    test:
961      suffix: 18
962      args: -tao_monitor_short -mx 8 -my 6 -bmx 3 -bmy 3 -bheight 0.2 -tao_type bnls -tao_gatol 1e-5 -tao_mf_hessian
963      requires: !single
964 
965    test:
966      suffix: 19
967      args: -tao_monitor_short -mx 8 -my 6 -bmx 3 -bmy 3 -bheight 0.2 -tao_type bntr -tao_gatol 1e-5 -tao_mf_hessian
968      requires: !single
969 
970    test:
971      suffix: 20
972      args: -tao_monitor_short -mx 8 -my 6 -bmx 3 -bmy 3 -bheight 0.2 -tao_type bntl -tao_gatol 1e-5 -tao_mf_hessian
973      requires: !single
974 
975 TEST*/
976