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