xref: /petsc/src/tao/unconstrained/tutorials/minsurf2.c (revision 7dcfde4d07706b324bec70f4acb301e88d0e45fe)
1 /* Program usage: mpiexec -n <proc> minsurf2 [-help] [all TAO options] */
2 
3 /*
4   Include "petsctao.h" so we can use TAO solvers.
5   petscdmda.h for distributed array
6 */
7 #include <petsctao.h>
8 #include <petscdmda.h>
9 
10 static char help[] = "This example demonstrates use of the TAO package to \n\
11 solve an unconstrained minimization problem. This example is based on a \n\
12 problem from the MINPACK-2 test suite. Given a rectangular 2-D domain and \n\
13 boundary values along the edges of the domain, the objective is to find the\n\
14 surface with the minimal area that satisfies the boundary conditions.\n\
15 The command line options are:\n\
16   -da_grid_x <xg>, where <xg> = number of grid points in the 1st coordinate direction\n\
17   -da_grid_y <yg>, where <yg> = number of grid points in the 2nd coordinate direction\n\
18   -start <st>, where <st> =0 for zero vector, <st> >0 for random start, and <st> <0 \n\
19                for an average of the boundary conditions\n\n";
20 
21 /*
22    User-defined application context - contains data needed by the
23    application-provided call-back routines, FormFunction(),
24    FormFunctionGradient(), and FormHessian().
25 */
26 typedef struct {
27   PetscInt   mx, my;                      /* discretization in x, y directions */
28   PetscReal *bottom, *top, *left, *right; /* boundary values */
29   DM         dm;                          /* distributed array data structure */
30   Mat        H;                           /* Hessian */
31 } AppCtx;
32 
33 /* -------- User-defined Routines --------- */
34 
35 static PetscErrorCode MSA_BoundaryConditions(AppCtx *);
36 static PetscErrorCode MSA_InitialPoint(AppCtx *, Vec);
37 static PetscErrorCode QuadraticH(AppCtx *, Vec, Mat);
38 static PetscErrorCode FormFunction(Tao, Vec, PetscReal *, void *);
39 static PetscErrorCode FormFunctionGradient(Tao, Vec, PetscReal *, Vec, void *);
40 static PetscErrorCode FormGradient(Tao, Vec, Vec, void *);
41 static PetscErrorCode FormHessian(Tao, Vec, Mat, Mat, void *);
42 static PetscErrorCode My_Monitor(Tao, void *);
43 
44 int main(int argc, char **argv)
45 {
46   Vec           x;                     /* solution, gradient vectors */
47   PetscBool     viewmat;               /* flags */
48   PetscBool     fddefault, fdcoloring; /* flags */
49   Tao           tao;                   /* TAO solver context */
50   AppCtx        user;                  /* user-defined work context */
51   ISColoring    iscoloring;
52   MatFDColoring matfdcoloring;
53 
54   /* Initialize TAO */
55   PetscFunctionBeginUser;
56   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
57 
58   /* Create distributed array (DM) to manage parallel grid and vectors  */
59   PetscCall(DMDACreate2d(PETSC_COMM_WORLD, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DMDA_STENCIL_BOX, 10, 10, PETSC_DECIDE, PETSC_DECIDE, 1, 1, NULL, NULL, &user.dm));
60   PetscCall(DMSetFromOptions(user.dm));
61   PetscCall(DMSetUp(user.dm));
62   PetscCall(DMDASetUniformCoordinates(user.dm, -0.5, 0.5, -0.5, 0.5, PETSC_DECIDE, PETSC_DECIDE));
63   PetscCall(DMDAGetInfo(user.dm, PETSC_IGNORE, &user.mx, &user.my, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE));
64 
65   PetscCall(PetscPrintf(MPI_COMM_WORLD, "\n---- Minimum Surface Area Problem -----\n"));
66   PetscCall(PetscPrintf(MPI_COMM_WORLD, "mx: %" PetscInt_FMT "     my: %" PetscInt_FMT "   \n\n", user.mx, user.my));
67 
68   /* Create TAO solver and set desired solution method.*/
69   PetscCall(TaoCreate(PETSC_COMM_WORLD, &tao));
70   PetscCall(TaoSetType(tao, TAOCG));
71 
72   /*
73      Extract global vector from DA for the vector of variables --  PETSC routine
74      Compute the initial solution                              --  application specific, see below
75      Set this vector for use by TAO                            --  TAO routine
76   */
77   PetscCall(DMCreateGlobalVector(user.dm, &x));
78   PetscCall(MSA_BoundaryConditions(&user));
79   PetscCall(MSA_InitialPoint(&user, x));
80   PetscCall(TaoSetSolution(tao, x));
81 
82   /*
83      Initialize the Application context for use in function evaluations  --  application specific, see below.
84      Set routines for function and gradient evaluation
85   */
86   PetscCall(TaoSetObjective(tao, FormFunction, (void *)&user));
87   PetscCall(TaoSetObjectiveAndGradient(tao, NULL, FormFunctionGradient, (void *)&user));
88 
89   /*
90      Given the command line arguments, calculate the hessian with either the user-
91      provided function FormHessian, or the default finite-difference driven Hessian
92      functions
93   */
94   PetscCall(PetscOptionsHasName(NULL, NULL, "-tao_fddefault", &fddefault));
95   PetscCall(PetscOptionsHasName(NULL, NULL, "-tao_fdcoloring", &fdcoloring));
96 
97   /*
98      Create a matrix data structure to store the Hessian and set
99      the Hessian evaluation routine.
100      Set the matrix structure to be used for Hessian evaluations
101   */
102   PetscCall(DMCreateMatrix(user.dm, &user.H));
103   PetscCall(MatSetOption(user.H, MAT_SYMMETRIC, PETSC_TRUE));
104 
105   if (fdcoloring) {
106     PetscCall(DMCreateColoring(user.dm, IS_COLORING_GLOBAL, &iscoloring));
107     PetscCall(MatFDColoringCreate(user.H, iscoloring, &matfdcoloring));
108     PetscCall(ISColoringDestroy(&iscoloring));
109     PetscCall(MatFDColoringSetFunction(matfdcoloring, (PetscErrorCode (*)(void))FormGradient, (void *)&user));
110     PetscCall(MatFDColoringSetFromOptions(matfdcoloring));
111     PetscCall(TaoSetHessian(tao, user.H, user.H, TaoDefaultComputeHessianColor, (void *)matfdcoloring));
112   } else if (fddefault) {
113     PetscCall(TaoSetHessian(tao, user.H, user.H, TaoDefaultComputeHessian, (void *)NULL));
114   } else {
115     PetscCall(TaoSetHessian(tao, user.H, user.H, FormHessian, (void *)&user));
116   }
117 
118   /*
119      If my_monitor option is in command line, then use the user-provided
120      monitoring function
121   */
122   PetscCall(PetscOptionsHasName(NULL, NULL, "-my_monitor", &viewmat));
123   if (viewmat) PetscCall(TaoMonitorSet(tao, My_Monitor, NULL, NULL));
124 
125   /* Check for any tao command line options */
126   PetscCall(TaoSetFromOptions(tao));
127 
128   /* SOLVE THE APPLICATION */
129   PetscCall(TaoSolve(tao));
130 
131   PetscCall(TaoView(tao, PETSC_VIEWER_STDOUT_WORLD));
132 
133   /* Free TAO data structures */
134   PetscCall(TaoDestroy(&tao));
135 
136   /* Free PETSc data structures */
137   PetscCall(VecDestroy(&x));
138   PetscCall(MatDestroy(&user.H));
139   if (fdcoloring) PetscCall(MatFDColoringDestroy(&matfdcoloring));
140   PetscCall(PetscFree(user.bottom));
141   PetscCall(PetscFree(user.top));
142   PetscCall(PetscFree(user.left));
143   PetscCall(PetscFree(user.right));
144   PetscCall(DMDestroy(&user.dm));
145   PetscCall(PetscFinalize());
146   return 0;
147 }
148 
149 /* -------------------------------------------------------------------- */
150 /*
151     FormFunction - Evaluates the objective function.
152 
153     Input Parameters:
154 .   tao     - the Tao context
155 .   X       - input vector
156 .   userCtx - optional user-defined context, as set by TaoSetObjective()
157 
158     Output Parameters:
159 .   fcn     - the newly evaluated function
160 */
161 PetscErrorCode FormFunction(Tao tao, Vec X, PetscReal *fcn, void *userCtx)
162 {
163   AppCtx     *user = (AppCtx *)userCtx;
164   PetscInt    i, j;
165   PetscInt    mx = user->mx, my = user->my;
166   PetscInt    xs, xm, gxs, gxm, ys, ym, gys, gym;
167   PetscReal   ft = 0.0;
168   PetscReal   hx = 1.0 / (mx + 1), hy = 1.0 / (my + 1), area = 0.5 * hx * hy;
169   PetscReal   rhx = mx + 1, rhy = my + 1;
170   PetscReal   f2, f4, d1, d2, d3, d4, xc, xl, xr, xt, xb;
171   PetscReal **x;
172   Vec         localX;
173 
174   PetscFunctionBegin;
175   /* Get local mesh boundaries */
176   PetscCall(DMGetLocalVector(user->dm, &localX));
177   PetscCall(DMDAGetCorners(user->dm, &xs, &ys, NULL, &xm, &ym, NULL));
178   PetscCall(DMDAGetGhostCorners(user->dm, &gxs, &gys, NULL, &gxm, &gym, NULL));
179 
180   /* Scatter ghost points to local vector */
181   PetscCall(DMGlobalToLocalBegin(user->dm, X, INSERT_VALUES, localX));
182   PetscCall(DMGlobalToLocalEnd(user->dm, X, INSERT_VALUES, localX));
183 
184   /* Get pointers to vector data */
185   PetscCall(DMDAVecGetArray(user->dm, localX, (void **)&x));
186 
187   /* Compute function and gradient over the locally owned part of the mesh */
188   for (j = ys; j < ys + ym; j++) {
189     for (i = xs; i < xs + xm; i++) {
190       xc = x[j][i];
191 
192       if (i == 0) { /* left side */
193         xl = user->left[j - ys + 1];
194       } else {
195         xl = x[j][i - 1];
196       }
197 
198       if (j == 0) { /* bottom side */
199         xb = user->bottom[i - xs + 1];
200       } else {
201         xb = x[j - 1][i];
202       }
203 
204       if (i + 1 == gxs + gxm) { /* right side */
205         xr = user->right[j - ys + 1];
206       } else {
207         xr = x[j][i + 1];
208       }
209 
210       if (j + 1 == gys + gym) { /* top side */
211         xt = user->top[i - xs + 1];
212       } else {
213         xt = x[j + 1][i];
214       }
215 
216       d1 = (xc - xl);
217       d2 = (xc - xr);
218       d3 = (xc - xt);
219       d4 = (xc - xb);
220 
221       d1 *= rhx;
222       d2 *= rhx;
223       d3 *= rhy;
224       d4 *= rhy;
225 
226       f2 = PetscSqrtReal(1.0 + d1 * d1 + d4 * d4);
227       f4 = PetscSqrtReal(1.0 + d3 * d3 + d2 * d2);
228 
229       ft = ft + (f2 + f4);
230     }
231   }
232 
233   /* Compute triangular areas along the border of the domain. */
234   if (xs == 0) { /* left side */
235     for (j = ys; j < ys + ym; j++) {
236       d3 = (user->left[j - ys + 1] - user->left[j - ys + 2]) * rhy;
237       d2 = (user->left[j - ys + 1] - x[j][0]) * rhx;
238       ft = ft + PetscSqrtReal(1.0 + d3 * d3 + d2 * d2);
239     }
240   }
241   if (ys == 0) { /* bottom side */
242     for (i = xs; i < xs + xm; i++) {
243       d2 = (user->bottom[i + 1 - xs] - user->bottom[i - xs + 2]) * rhx;
244       d3 = (user->bottom[i - xs + 1] - x[0][i]) * rhy;
245       ft = ft + PetscSqrtReal(1.0 + d3 * d3 + d2 * d2);
246     }
247   }
248   if (xs + xm == mx) { /* right side */
249     for (j = ys; j < ys + ym; j++) {
250       d1 = (x[j][mx - 1] - user->right[j - ys + 1]) * rhx;
251       d4 = (user->right[j - ys] - user->right[j - ys + 1]) * rhy;
252       ft = ft + PetscSqrtReal(1.0 + d1 * d1 + d4 * d4);
253     }
254   }
255   if (ys + ym == my) { /* top side */
256     for (i = xs; i < xs + xm; i++) {
257       d1 = (x[my - 1][i] - user->top[i - xs + 1]) * rhy;
258       d4 = (user->top[i - xs + 1] - user->top[i - xs]) * rhx;
259       ft = ft + PetscSqrtReal(1.0 + d1 * d1 + d4 * d4);
260     }
261   }
262   if (ys == 0 && xs == 0) {
263     d1 = (user->left[0] - user->left[1]) / hy;
264     d2 = (user->bottom[0] - user->bottom[1]) * rhx;
265     ft += PetscSqrtReal(1.0 + d1 * d1 + d2 * d2);
266   }
267   if (ys + ym == my && xs + xm == mx) {
268     d1 = (user->right[ym + 1] - user->right[ym]) * rhy;
269     d2 = (user->top[xm + 1] - user->top[xm]) * rhx;
270     ft += PetscSqrtReal(1.0 + d1 * d1 + d2 * d2);
271   }
272 
273   ft = ft * area;
274   PetscCallMPI(MPIU_Allreduce(&ft, fcn, 1, MPIU_REAL, MPIU_SUM, MPI_COMM_WORLD));
275 
276   /* Restore vectors */
277   PetscCall(DMDAVecRestoreArray(user->dm, localX, (void **)&x));
278   PetscCall(DMRestoreLocalVector(user->dm, &localX));
279   PetscFunctionReturn(PETSC_SUCCESS);
280 }
281 
282 /* -------------------------------------------------------------------- */
283 /*
284     FormFunctionGradient - Evaluates the function and corresponding gradient.
285 
286     Input Parameters:
287 .   tao     - the Tao context
288 .   X      - input vector
289 .   userCtx - optional user-defined context, as set by TaoSetObjectiveAndGradient()
290 
291     Output Parameters:
292 .   fcn     - the newly evaluated function
293 .   G       - vector containing the newly evaluated gradient
294 */
295 PetscErrorCode FormFunctionGradient(Tao tao, Vec X, PetscReal *fcn, Vec G, void *userCtx)
296 {
297   AppCtx     *user = (AppCtx *)userCtx;
298   PetscInt    i, j;
299   PetscInt    mx = user->mx, my = user->my;
300   PetscInt    xs, xm, gxs, gxm, ys, ym, gys, gym;
301   PetscReal   ft = 0.0;
302   PetscReal   hx = 1.0 / (mx + 1), hy = 1.0 / (my + 1), hydhx = hy / hx, hxdhy = hx / hy, area = 0.5 * hx * hy;
303   PetscReal   rhx = mx + 1, rhy = my + 1;
304   PetscReal   f1, f2, f3, f4, f5, f6, d1, d2, d3, d4, d5, d6, d7, d8, xc, xl, xr, xt, xb, xlt, xrb;
305   PetscReal   df1dxc, df2dxc, df3dxc, df4dxc, df5dxc, df6dxc;
306   PetscReal **g, **x;
307   Vec         localX;
308 
309   PetscFunctionBegin;
310   /* Get local mesh boundaries */
311   PetscCall(DMGetLocalVector(user->dm, &localX));
312   PetscCall(DMDAGetCorners(user->dm, &xs, &ys, NULL, &xm, &ym, NULL));
313   PetscCall(DMDAGetGhostCorners(user->dm, &gxs, &gys, NULL, &gxm, &gym, NULL));
314 
315   /* Scatter ghost points to local vector */
316   PetscCall(DMGlobalToLocalBegin(user->dm, X, INSERT_VALUES, localX));
317   PetscCall(DMGlobalToLocalEnd(user->dm, X, INSERT_VALUES, localX));
318 
319   /* Get pointers to vector data */
320   PetscCall(DMDAVecGetArray(user->dm, localX, (void **)&x));
321   PetscCall(DMDAVecGetArray(user->dm, G, (void **)&g));
322 
323   /* Compute function and gradient over the locally owned part of the mesh */
324   for (j = ys; j < ys + ym; j++) {
325     for (i = xs; i < xs + xm; i++) {
326       xc  = x[j][i];
327       xlt = xrb = xl = xr = xb = xt = xc;
328 
329       if (i == 0) { /* left side */
330         xl  = user->left[j - ys + 1];
331         xlt = user->left[j - ys + 2];
332       } else {
333         xl = x[j][i - 1];
334       }
335 
336       if (j == 0) { /* bottom side */
337         xb  = user->bottom[i - xs + 1];
338         xrb = user->bottom[i - xs + 2];
339       } else {
340         xb = x[j - 1][i];
341       }
342 
343       if (i + 1 == gxs + gxm) { /* right side */
344         xr  = user->right[j - ys + 1];
345         xrb = user->right[j - ys];
346       } else {
347         xr = x[j][i + 1];
348       }
349 
350       if (j + 1 == gys + gym) { /* top side */
351         xt  = user->top[i - xs + 1];
352         xlt = user->top[i - xs];
353       } else {
354         xt = x[j + 1][i];
355       }
356 
357       if (i > gxs && j + 1 < gys + gym) xlt = x[j + 1][i - 1];
358       if (j > gys && i + 1 < gxs + gxm) xrb = x[j - 1][i + 1];
359 
360       d1 = (xc - xl);
361       d2 = (xc - xr);
362       d3 = (xc - xt);
363       d4 = (xc - xb);
364       d5 = (xr - xrb);
365       d6 = (xrb - xb);
366       d7 = (xlt - xl);
367       d8 = (xt - xlt);
368 
369       df1dxc = d1 * hydhx;
370       df2dxc = (d1 * hydhx + d4 * hxdhy);
371       df3dxc = d3 * hxdhy;
372       df4dxc = (d2 * hydhx + d3 * hxdhy);
373       df5dxc = d2 * hydhx;
374       df6dxc = d4 * hxdhy;
375 
376       d1 *= rhx;
377       d2 *= rhx;
378       d3 *= rhy;
379       d4 *= rhy;
380       d5 *= rhy;
381       d6 *= rhx;
382       d7 *= rhy;
383       d8 *= rhx;
384 
385       f1 = PetscSqrtReal(1.0 + d1 * d1 + d7 * d7);
386       f2 = PetscSqrtReal(1.0 + d1 * d1 + d4 * d4);
387       f3 = PetscSqrtReal(1.0 + d3 * d3 + d8 * d8);
388       f4 = PetscSqrtReal(1.0 + d3 * d3 + d2 * d2);
389       f5 = PetscSqrtReal(1.0 + d2 * d2 + d5 * d5);
390       f6 = PetscSqrtReal(1.0 + d4 * d4 + d6 * d6);
391 
392       ft = ft + (f2 + f4);
393 
394       df1dxc /= f1;
395       df2dxc /= f2;
396       df3dxc /= f3;
397       df4dxc /= f4;
398       df5dxc /= f5;
399       df6dxc /= f6;
400 
401       g[j][i] = (df1dxc + df2dxc + df3dxc + df4dxc + df5dxc + df6dxc) * 0.5;
402     }
403   }
404 
405   /* Compute triangular areas along the border of the domain. */
406   if (xs == 0) { /* left side */
407     for (j = ys; j < ys + ym; j++) {
408       d3 = (user->left[j - ys + 1] - user->left[j - ys + 2]) * rhy;
409       d2 = (user->left[j - ys + 1] - x[j][0]) * rhx;
410       ft = ft + PetscSqrtReal(1.0 + d3 * d3 + d2 * d2);
411     }
412   }
413   if (ys == 0) { /* bottom side */
414     for (i = xs; i < xs + xm; i++) {
415       d2 = (user->bottom[i + 1 - xs] - user->bottom[i - xs + 2]) * rhx;
416       d3 = (user->bottom[i - xs + 1] - x[0][i]) * rhy;
417       ft = ft + PetscSqrtReal(1.0 + d3 * d3 + d2 * d2);
418     }
419   }
420 
421   if (xs + xm == mx) { /* right side */
422     for (j = ys; j < ys + ym; j++) {
423       d1 = (x[j][mx - 1] - user->right[j - ys + 1]) * rhx;
424       d4 = (user->right[j - ys] - user->right[j - ys + 1]) * rhy;
425       ft = ft + PetscSqrtReal(1.0 + d1 * d1 + d4 * d4);
426     }
427   }
428   if (ys + ym == my) { /* top side */
429     for (i = xs; i < xs + xm; i++) {
430       d1 = (x[my - 1][i] - user->top[i - xs + 1]) * rhy;
431       d4 = (user->top[i - xs + 1] - user->top[i - xs]) * rhx;
432       ft = ft + PetscSqrtReal(1.0 + d1 * d1 + d4 * d4);
433     }
434   }
435 
436   if (ys == 0 && xs == 0) {
437     d1 = (user->left[0] - user->left[1]) / hy;
438     d2 = (user->bottom[0] - user->bottom[1]) * rhx;
439     ft += PetscSqrtReal(1.0 + d1 * d1 + d2 * d2);
440   }
441   if (ys + ym == my && xs + xm == mx) {
442     d1 = (user->right[ym + 1] - user->right[ym]) * rhy;
443     d2 = (user->top[xm + 1] - user->top[xm]) * rhx;
444     ft += PetscSqrtReal(1.0 + d1 * d1 + d2 * d2);
445   }
446 
447   ft = ft * area;
448   PetscCallMPI(MPIU_Allreduce(&ft, fcn, 1, MPIU_REAL, MPIU_SUM, MPI_COMM_WORLD));
449 
450   /* Restore vectors */
451   PetscCall(DMDAVecRestoreArray(user->dm, localX, (void **)&x));
452   PetscCall(DMDAVecRestoreArray(user->dm, G, (void **)&g));
453   PetscCall(DMRestoreLocalVector(user->dm, &localX));
454   PetscCall(PetscLogFlops(67.0 * xm * ym));
455   PetscFunctionReturn(PETSC_SUCCESS);
456 }
457 
458 PetscErrorCode FormGradient(Tao tao, Vec X, Vec G, void *userCtx)
459 {
460   PetscReal fcn;
461 
462   PetscFunctionBegin;
463   PetscCall(FormFunctionGradient(tao, X, &fcn, G, userCtx));
464   PetscFunctionReturn(PETSC_SUCCESS);
465 }
466 
467 /* ------------------------------------------------------------------- */
468 /*
469    FormHessian - Evaluates Hessian matrix.
470 
471    Input Parameters:
472 .  tao  - the Tao context
473 .  x    - input vector
474 .  ptr  - optional user-defined context, as set by TaoSetHessian()
475 
476    Output Parameters:
477 .  H    - Hessian matrix
478 .  Hpre - optionally different preconditioning matrix
479 .  flg  - flag indicating matrix structure
480 
481 */
482 PetscErrorCode FormHessian(Tao tao, Vec X, Mat H, Mat Hpre, void *ptr)
483 {
484   AppCtx *user = (AppCtx *)ptr;
485 
486   PetscFunctionBegin;
487   /* Evaluate the Hessian entries*/
488   PetscCall(QuadraticH(user, X, H));
489   PetscFunctionReturn(PETSC_SUCCESS);
490 }
491 
492 /* ------------------------------------------------------------------- */
493 /*
494    QuadraticH - Evaluates Hessian matrix.
495 
496    Input Parameters:
497 .  user - user-defined context, as set by TaoSetHessian()
498 .  X    - input vector
499 
500    Output Parameter:
501 .  H    - Hessian matrix
502 */
503 PetscErrorCode QuadraticH(AppCtx *user, Vec X, Mat Hessian)
504 {
505   PetscInt    i, j, k;
506   PetscInt    mx = user->mx, my = user->my;
507   PetscInt    xs, xm, gxs, gxm, ys, ym, gys, gym;
508   PetscReal   hx = 1.0 / (mx + 1), hy = 1.0 / (my + 1), hydhx = hy / hx, hxdhy = hx / hy;
509   PetscReal   f1, f2, f3, f4, f5, f6, d1, d2, d3, d4, d5, d6, d7, d8, xc, xl, xr, xt, xb, xlt, xrb;
510   PetscReal   hl, hr, ht, hb, hc, htl, hbr;
511   PetscReal **x, v[7];
512   MatStencil  col[7], row;
513   Vec         localX;
514   PetscBool   assembled;
515 
516   PetscFunctionBegin;
517   /* Get local mesh boundaries */
518   PetscCall(DMGetLocalVector(user->dm, &localX));
519 
520   PetscCall(DMDAGetCorners(user->dm, &xs, &ys, NULL, &xm, &ym, NULL));
521   PetscCall(DMDAGetGhostCorners(user->dm, &gxs, &gys, NULL, &gxm, &gym, NULL));
522 
523   /* Scatter ghost points to local vector */
524   PetscCall(DMGlobalToLocalBegin(user->dm, X, INSERT_VALUES, localX));
525   PetscCall(DMGlobalToLocalEnd(user->dm, X, INSERT_VALUES, localX));
526 
527   /* Get pointers to vector data */
528   PetscCall(DMDAVecGetArray(user->dm, localX, (void **)&x));
529 
530   /* Initialize matrix entries to zero */
531   PetscCall(MatAssembled(Hessian, &assembled));
532   if (assembled) PetscCall(MatZeroEntries(Hessian));
533 
534   /* Set various matrix options */
535   PetscCall(MatSetOption(Hessian, MAT_IGNORE_OFF_PROC_ENTRIES, PETSC_TRUE));
536 
537   /* Compute Hessian over the locally owned part of the mesh */
538   for (j = ys; j < ys + ym; j++) {
539     for (i = xs; i < xs + xm; i++) {
540       xc  = x[j][i];
541       xlt = xrb = xl = xr = xb = xt = xc;
542 
543       /* Left side */
544       if (i == 0) {
545         xl  = user->left[j - ys + 1];
546         xlt = user->left[j - ys + 2];
547       } else {
548         xl = x[j][i - 1];
549       }
550 
551       if (j == 0) {
552         xb  = user->bottom[i - xs + 1];
553         xrb = user->bottom[i - xs + 2];
554       } else {
555         xb = x[j - 1][i];
556       }
557 
558       if (i + 1 == mx) {
559         xr  = user->right[j - ys + 1];
560         xrb = user->right[j - ys];
561       } else {
562         xr = x[j][i + 1];
563       }
564 
565       if (j + 1 == my) {
566         xt  = user->top[i - xs + 1];
567         xlt = user->top[i - xs];
568       } else {
569         xt = x[j + 1][i];
570       }
571 
572       if (i > 0 && j + 1 < my) xlt = x[j + 1][i - 1];
573       if (j > 0 && i + 1 < mx) xrb = x[j - 1][i + 1];
574 
575       d1 = (xc - xl) / hx;
576       d2 = (xc - xr) / hx;
577       d3 = (xc - xt) / hy;
578       d4 = (xc - xb) / hy;
579       d5 = (xrb - xr) / hy;
580       d6 = (xrb - xb) / hx;
581       d7 = (xlt - xl) / hy;
582       d8 = (xlt - xt) / hx;
583 
584       f1 = PetscSqrtReal(1.0 + d1 * d1 + d7 * d7);
585       f2 = PetscSqrtReal(1.0 + d1 * d1 + d4 * d4);
586       f3 = PetscSqrtReal(1.0 + d3 * d3 + d8 * d8);
587       f4 = PetscSqrtReal(1.0 + d3 * d3 + d2 * d2);
588       f5 = PetscSqrtReal(1.0 + d2 * d2 + d5 * d5);
589       f6 = PetscSqrtReal(1.0 + d4 * d4 + d6 * d6);
590 
591       hl = (-hydhx * (1.0 + d7 * d7) + d1 * d7) / (f1 * f1 * f1) + (-hydhx * (1.0 + d4 * d4) + d1 * d4) / (f2 * f2 * f2);
592       hr = (-hydhx * (1.0 + d5 * d5) + d2 * d5) / (f5 * f5 * f5) + (-hydhx * (1.0 + d3 * d3) + d2 * d3) / (f4 * f4 * f4);
593       ht = (-hxdhy * (1.0 + d8 * d8) + d3 * d8) / (f3 * f3 * f3) + (-hxdhy * (1.0 + d2 * d2) + d2 * d3) / (f4 * f4 * f4);
594       hb = (-hxdhy * (1.0 + d6 * d6) + d4 * d6) / (f6 * f6 * f6) + (-hxdhy * (1.0 + d1 * d1) + d1 * d4) / (f2 * f2 * f2);
595 
596       hbr = -d2 * d5 / (f5 * f5 * f5) - d4 * d6 / (f6 * f6 * f6);
597       htl = -d1 * d7 / (f1 * f1 * f1) - d3 * d8 / (f3 * f3 * f3);
598 
599       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);
600 
601       hl /= 2.0;
602       hr /= 2.0;
603       ht /= 2.0;
604       hb /= 2.0;
605       hbr /= 2.0;
606       htl /= 2.0;
607       hc /= 2.0;
608 
609       row.j = j;
610       row.i = i;
611       k     = 0;
612       if (j > 0) {
613         v[k]     = hb;
614         col[k].j = j - 1;
615         col[k].i = i;
616         k++;
617       }
618 
619       if (j > 0 && i < mx - 1) {
620         v[k]     = hbr;
621         col[k].j = j - 1;
622         col[k].i = i + 1;
623         k++;
624       }
625 
626       if (i > 0) {
627         v[k]     = hl;
628         col[k].j = j;
629         col[k].i = i - 1;
630         k++;
631       }
632 
633       v[k]     = hc;
634       col[k].j = j;
635       col[k].i = i;
636       k++;
637 
638       if (i < mx - 1) {
639         v[k]     = hr;
640         col[k].j = j;
641         col[k].i = i + 1;
642         k++;
643       }
644 
645       if (i > 0 && j < my - 1) {
646         v[k]     = htl;
647         col[k].j = j + 1;
648         col[k].i = i - 1;
649         k++;
650       }
651 
652       if (j < my - 1) {
653         v[k]     = ht;
654         col[k].j = j + 1;
655         col[k].i = i;
656         k++;
657       }
658 
659       /*
660          Set matrix values using local numbering, which was defined
661          earlier, in the main routine.
662       */
663       PetscCall(MatSetValuesStencil(Hessian, 1, &row, k, col, v, INSERT_VALUES));
664     }
665   }
666 
667   PetscCall(DMDAVecRestoreArray(user->dm, localX, (void **)&x));
668   PetscCall(DMRestoreLocalVector(user->dm, &localX));
669 
670   PetscCall(MatAssemblyBegin(Hessian, MAT_FINAL_ASSEMBLY));
671   PetscCall(MatAssemblyEnd(Hessian, MAT_FINAL_ASSEMBLY));
672 
673   PetscCall(PetscLogFlops(199.0 * xm * ym));
674   PetscFunctionReturn(PETSC_SUCCESS);
675 }
676 
677 /* ------------------------------------------------------------------- */
678 /*
679    MSA_BoundaryConditions -  Calculates the boundary conditions for
680    the region.
681 
682    Input Parameter:
683 .  user - user-defined application context
684 
685    Output Parameter:
686 .  user - user-defined application context
687 */
688 static PetscErrorCode MSA_BoundaryConditions(AppCtx *user)
689 {
690   PetscInt   i, j, k, limit = 0, maxits = 5;
691   PetscInt   xs, ys, xm, ym, gxs, gys, gxm, gym;
692   PetscInt   mx = user->mx, my = user->my;
693   PetscInt   bsize = 0, lsize = 0, tsize = 0, rsize = 0;
694   PetscReal  one = 1.0, two = 2.0, three = 3.0, tol = 1e-10;
695   PetscReal  fnorm, det, hx, hy, xt = 0, yt = 0;
696   PetscReal  u1, u2, nf1, nf2, njac11, njac12, njac21, njac22;
697   PetscReal  b = -0.5, t = 0.5, l = -0.5, r = 0.5;
698   PetscReal *boundary;
699   PetscBool  flg;
700 
701   PetscFunctionBegin;
702   /* Get local mesh boundaries */
703   PetscCall(DMDAGetCorners(user->dm, &xs, &ys, NULL, &xm, &ym, NULL));
704   PetscCall(DMDAGetGhostCorners(user->dm, &gxs, &gys, NULL, &gxm, &gym, NULL));
705 
706   bsize = xm + 2;
707   lsize = ym + 2;
708   rsize = ym + 2;
709   tsize = xm + 2;
710 
711   PetscCall(PetscMalloc1(bsize, &user->bottom));
712   PetscCall(PetscMalloc1(tsize, &user->top));
713   PetscCall(PetscMalloc1(lsize, &user->left));
714   PetscCall(PetscMalloc1(rsize, &user->right));
715 
716   hx = (r - l) / (mx + 1);
717   hy = (t - b) / (my + 1);
718 
719   for (j = 0; j < 4; j++) {
720     if (j == 0) {
721       yt       = b;
722       xt       = l + hx * xs;
723       limit    = bsize;
724       boundary = user->bottom;
725     } else if (j == 1) {
726       yt       = t;
727       xt       = l + hx * xs;
728       limit    = tsize;
729       boundary = user->top;
730     } else if (j == 2) {
731       yt       = b + hy * ys;
732       xt       = l;
733       limit    = lsize;
734       boundary = user->left;
735     } else { /* if (j==3) */
736       yt       = b + hy * ys;
737       xt       = r;
738       limit    = rsize;
739       boundary = user->right;
740     }
741 
742     for (i = 0; i < limit; i++) {
743       u1 = xt;
744       u2 = -yt;
745       for (k = 0; k < maxits; k++) {
746         nf1   = u1 + u1 * u2 * u2 - u1 * u1 * u1 / three - xt;
747         nf2   = -u2 - u1 * u1 * u2 + u2 * u2 * u2 / three - yt;
748         fnorm = PetscSqrtReal(nf1 * nf1 + nf2 * nf2);
749         if (fnorm <= tol) break;
750         njac11 = one + u2 * u2 - u1 * u1;
751         njac12 = two * u1 * u2;
752         njac21 = -two * u1 * u2;
753         njac22 = -one - u1 * u1 + u2 * u2;
754         det    = njac11 * njac22 - njac21 * njac12;
755         u1     = u1 - (njac22 * nf1 - njac12 * nf2) / det;
756         u2     = u2 - (njac11 * nf2 - njac21 * nf1) / det;
757       }
758 
759       boundary[i] = u1 * u1 - u2 * u2;
760       if (j == 0 || j == 1) {
761         xt = xt + hx;
762       } else { /*  if (j==2 || j==3) */
763         yt = yt + hy;
764       }
765     }
766   }
767 
768   /* Scale the boundary if desired */
769   if (1 == 1) {
770     PetscReal scl = 1.0;
771 
772     PetscCall(PetscOptionsGetReal(NULL, NULL, "-bottom", &scl, &flg));
773     if (flg) {
774       for (i = 0; i < bsize; i++) user->bottom[i] *= scl;
775     }
776 
777     PetscCall(PetscOptionsGetReal(NULL, NULL, "-top", &scl, &flg));
778     if (flg) {
779       for (i = 0; i < tsize; i++) user->top[i] *= scl;
780     }
781 
782     PetscCall(PetscOptionsGetReal(NULL, NULL, "-right", &scl, &flg));
783     if (flg) {
784       for (i = 0; i < rsize; i++) user->right[i] *= scl;
785     }
786 
787     PetscCall(PetscOptionsGetReal(NULL, NULL, "-left", &scl, &flg));
788     if (flg) {
789       for (i = 0; i < lsize; i++) user->left[i] *= scl;
790     }
791   }
792   PetscFunctionReturn(PETSC_SUCCESS);
793 }
794 
795 /* ------------------------------------------------------------------- */
796 /*
797    MSA_InitialPoint - Calculates the initial guess in one of three ways.
798 
799    Input Parameters:
800 .  user - user-defined application context
801 .  X - vector for initial guess
802 
803    Output Parameters:
804 .  X - newly computed initial guess
805 */
806 static PetscErrorCode MSA_InitialPoint(AppCtx *user, Vec X)
807 {
808   PetscInt  start2 = -1, i, j;
809   PetscReal start1 = 0;
810   PetscBool flg1, flg2;
811 
812   PetscFunctionBegin;
813   PetscCall(PetscOptionsGetReal(NULL, NULL, "-start", &start1, &flg1));
814   PetscCall(PetscOptionsGetInt(NULL, NULL, "-random", &start2, &flg2));
815 
816   if (flg1) { /* The zero vector is reasonable */
817     PetscCall(VecSet(X, start1));
818   } else if (flg2 && start2 > 0) { /* Try a random start between -0.5 and 0.5 */
819     PetscRandom rctx;
820     PetscReal   np5 = -0.5;
821 
822     PetscCall(PetscRandomCreate(PETSC_COMM_WORLD, &rctx));
823     PetscCall(VecSetRandom(X, rctx));
824     PetscCall(PetscRandomDestroy(&rctx));
825     PetscCall(VecShift(X, np5));
826   } else { /* Take an average of the boundary conditions */
827     PetscInt    xs, xm, ys, ym;
828     PetscInt    mx = user->mx, my = user->my;
829     PetscReal **x;
830 
831     /* Get local mesh boundaries */
832     PetscCall(DMDAGetCorners(user->dm, &xs, &ys, NULL, &xm, &ym, NULL));
833 
834     /* Get pointers to vector data */
835     PetscCall(DMDAVecGetArray(user->dm, X, (void **)&x));
836 
837     /* Perform local computations */
838     for (j = ys; j < ys + ym; j++) {
839       for (i = xs; i < xs + xm; i++) x[j][i] = (((j + 1) * user->bottom[i - xs + 1] + (my - j + 1) * user->top[i - xs + 1]) / (my + 2) + ((i + 1) * user->left[j - ys + 1] + (mx - i + 1) * user->right[j - ys + 1]) / (mx + 2)) / 2.0;
840     }
841     PetscCall(DMDAVecRestoreArray(user->dm, X, (void **)&x));
842     PetscCall(PetscLogFlops(9.0 * xm * ym));
843   }
844   PetscFunctionReturn(PETSC_SUCCESS);
845 }
846 
847 /*-----------------------------------------------------------------------*/
848 PetscErrorCode My_Monitor(Tao tao, void *ctx)
849 {
850   Vec X;
851 
852   PetscFunctionBegin;
853   PetscCall(TaoGetSolution(tao, &X));
854   PetscCall(VecView(X, PETSC_VIEWER_STDOUT_WORLD));
855   PetscFunctionReturn(PETSC_SUCCESS);
856 }
857 
858 /*TEST
859 
860    build:
861       requires: !complex
862 
863    test:
864       args: -tao_monitor_short -tao_type lmvm -da_grid_x 10 -da_grid_y 8 -tao_gatol 1.e-3
865       requires: !single
866 
867    test:
868       suffix: 2
869       nsize: 2
870       args: -tao_monitor_short -tao_type nls -tao_nls_ksp_max_it 15 -tao_gatol 1.e-4
871       filter: grep -v "nls ksp"
872       requires: !single
873 
874    test:
875       suffix: 2_snes
876       nsize: 2
877       args: -tao_monitor_short -tao_type snes -ksp_converged_maxits -ksp_max_it 15 -snes_atol 1.e-4
878       filter: grep -v "nls ksp"
879       requires: !single
880 
881    test:
882       suffix: 3
883       nsize: 3
884       args: -tao_monitor_short -tao_type cg -tao_cg_type fr -da_grid_x 10 -da_grid_y 10 -tao_gatol 1.e-3
885       requires: !single
886 
887    test:
888       suffix: 3_snes
889       nsize: 3
890       args: -tao_monitor_short -tao_type snes -snes_type ncg -snes_ncg_type fr -da_grid_x 10 -da_grid_y 10 -snes_atol 1.e-4
891       requires: !single
892 
893    test:
894       suffix: 4_snes_ngmres
895       args: -tao_type snes -snes_type ngmres -npc_snes_type ncg -da_grid_x 10 -da_grid_y 10 -snes_atol 1.e-4 -npc_snes_ncg_type fr -snes_converged_reason -snes_monitor ::ascii_info_detail -snes_ngmres_monitor -snes_ngmres_select_type {{linesearch difference}separate output}
896       requires: !single
897 
898    test:
899       suffix: 5
900       nsize: 2
901       args: -tao_monitor_short -tao_type bmrm -da_grid_x 10 -da_grid_y 8 -tao_gatol 1.e-3
902       requires: !single
903 
904 TEST*/
905