Actual source code: minsurf2.c

  1: /* Program usage: mpiexec -n <proc> minsurf2 [-help] [all TAO options] */

  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>

 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";

 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;

 33: /* -------- User-defined Routines --------- */

 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 *);

 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;

 54:   /* Initialize TAO */
 55:   PetscFunctionBeginUser;
 56:   PetscCall(PetscInitialize(&argc, &argv, NULL, help));

 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));

 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));

 68:   /* Create TAO solver and set desired solution method.*/
 69:   PetscCall(TaoCreate(PETSC_COMM_WORLD, &tao));
 70:   PetscCall(TaoSetType(tao, TAOCG));

 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));

 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));

 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));

 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));

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:   }

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));

125:   /* Check for any tao command line options */
126:   PetscCall(TaoSetFromOptions(tao));

128:   /* SOLVE THE APPLICATION */
129:   PetscCall(TaoSolve(tao));

131:   PetscCall(TaoView(tao, PETSC_VIEWER_STDOUT_WORLD));

133:   /* Free TAO data structures */
134:   PetscCall(TaoDestroy(&tao));

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: }

149: /* -------------------------------------------------------------------- */
150: /*
151:     FormFunction - Evaluates the objective function.

153:     Input Parameters:
154: .   tao     - the Tao context
155: .   X       - input vector
156: .   userCtx - optional user-defined context, as set by TaoSetObjective()

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;

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));

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));

184:   /* Get pointers to vector data */
185:   PetscCall(DMDAVecGetArray(user->dm, localX, (void **)&x));

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];

192:       if (i == 0) { /* left side */
193:         xl = user->left[j - ys + 1];
194:       } else {
195:         xl = x[j][i - 1];
196:       }

198:       if (j == 0) { /* bottom side */
199:         xb = user->bottom[i - xs + 1];
200:       } else {
201:         xb = x[j - 1][i];
202:       }

204:       if (i + 1 == gxs + gxm) { /* right side */
205:         xr = user->right[j - ys + 1];
206:       } else {
207:         xr = x[j][i + 1];
208:       }

210:       if (j + 1 == gys + gym) { /* top side */
211:         xt = user->top[i - xs + 1];
212:       } else {
213:         xt = x[j + 1][i];
214:       }

216:       d1 = (xc - xl);
217:       d2 = (xc - xr);
218:       d3 = (xc - xt);
219:       d4 = (xc - xb);

221:       d1 *= rhx;
222:       d2 *= rhx;
223:       d3 *= rhy;
224:       d4 *= rhy;

226:       f2 = PetscSqrtReal(1.0 + d1 * d1 + d4 * d4);
227:       f4 = PetscSqrtReal(1.0 + d3 * d3 + d2 * d2);

229:       ft = ft + (f2 + f4);
230:     }
231:   }

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:   }

273:   ft = ft * area;
274:   PetscCallMPI(MPIU_Allreduce(&ft, fcn, 1, MPIU_REAL, MPIU_SUM, MPI_COMM_WORLD));

276:   /* Restore vectors */
277:   PetscCall(DMDAVecRestoreArray(user->dm, localX, (void **)&x));
278:   PetscCall(DMRestoreLocalVector(user->dm, &localX));
279:   PetscFunctionReturn(PETSC_SUCCESS);
280: }

282: /* -------------------------------------------------------------------- */
283: /*
284:     FormFunctionGradient - Evaluates the function and corresponding gradient.

286:     Input Parameters:
287: .   tao     - the Tao context
288: .   X      - input vector
289: .   userCtx - optional user-defined context, as set by TaoSetObjectiveAndGradient()

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;

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));

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));

319:   /* Get pointers to vector data */
320:   PetscCall(DMDAVecGetArray(user->dm, localX, (void **)&x));
321:   PetscCall(DMDAVecGetArray(user->dm, G, (void **)&g));

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;

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:       }

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:       }

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:       }

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:       }

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];

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);

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;

376:       d1 *= rhx;
377:       d2 *= rhx;
378:       d3 *= rhy;
379:       d4 *= rhy;
380:       d5 *= rhy;
381:       d6 *= rhx;
382:       d7 *= rhy;
383:       d8 *= rhx;

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);

392:       ft = ft + (f2 + f4);

394:       df1dxc /= f1;
395:       df2dxc /= f2;
396:       df3dxc /= f3;
397:       df4dxc /= f4;
398:       df5dxc /= f5;
399:       df6dxc /= f6;

401:       g[j][i] = (df1dxc + df2dxc + df3dxc + df4dxc + df5dxc + df6dxc) * 0.5;
402:     }
403:   }

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:   }

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:   }

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:   }

447:   ft = ft * area;
448:   PetscCallMPI(MPIU_Allreduce(&ft, fcn, 1, MPIU_REAL, MPIU_SUM, MPI_COMM_WORLD));

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: }

458: PetscErrorCode FormGradient(Tao tao, Vec X, Vec G, void *userCtx)
459: {
460:   PetscReal fcn;

462:   PetscFunctionBegin;
463:   PetscCall(FormFunctionGradient(tao, X, &fcn, G, userCtx));
464:   PetscFunctionReturn(PETSC_SUCCESS);
465: }

467: /* ------------------------------------------------------------------- */
468: /*
469:    FormHessian - Evaluates Hessian matrix.

471:    Input Parameters:
472: .  tao  - the Tao context
473: .  x    - input vector
474: .  ptr  - optional user-defined context, as set by TaoSetHessian()

476:    Output Parameters:
477: .  H    - Hessian matrix
478: .  Hpre - optionally different preconditioning matrix
479: .  flg  - flag indicating matrix structure

481: */
482: PetscErrorCode FormHessian(Tao tao, Vec X, Mat H, Mat Hpre, void *ptr)
483: {
484:   AppCtx *user = (AppCtx *)ptr;

486:   PetscFunctionBegin;
487:   /* Evaluate the Hessian entries*/
488:   PetscCall(QuadraticH(user, X, H));
489:   PetscFunctionReturn(PETSC_SUCCESS);
490: }

492: /* ------------------------------------------------------------------- */
493: /*
494:    QuadraticH - Evaluates Hessian matrix.

496:    Input Parameters:
497: .  user - user-defined context, as set by TaoSetHessian()
498: .  X    - input vector

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;

516:   PetscFunctionBegin;
517:   /* Get local mesh boundaries */
518:   PetscCall(DMGetLocalVector(user->dm, &localX));

520:   PetscCall(DMDAGetCorners(user->dm, &xs, &ys, NULL, &xm, &ym, NULL));
521:   PetscCall(DMDAGetGhostCorners(user->dm, &gxs, &gys, NULL, &gxm, &gym, NULL));

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));

527:   /* Get pointers to vector data */
528:   PetscCall(DMDAVecGetArray(user->dm, localX, (void **)&x));

530:   /* Initialize matrix entries to zero */
531:   PetscCall(MatAssembled(Hessian, &assembled));
532:   if (assembled) PetscCall(MatZeroEntries(Hessian));

534:   /* Set various matrix options */
535:   PetscCall(MatSetOption(Hessian, MAT_IGNORE_OFF_PROC_ENTRIES, PETSC_TRUE));

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;

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:       }

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:       }

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:       }

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:       }

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];

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;

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);

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);

596:       hbr = -d2 * d5 / (f5 * f5 * f5) - d4 * d6 / (f6 * f6 * f6);
597:       htl = -d1 * d7 / (f1 * f1 * f1) - d3 * d8 / (f3 * f3 * f3);

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);

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;

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:       }

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:       }

626:       if (i > 0) {
627:         v[k]     = hl;
628:         col[k].j = j;
629:         col[k].i = i - 1;
630:         k++;
631:       }

633:       v[k]     = hc;
634:       col[k].j = j;
635:       col[k].i = i;
636:       k++;

638:       if (i < mx - 1) {
639:         v[k]     = hr;
640:         col[k].j = j;
641:         col[k].i = i + 1;
642:         k++;
643:       }

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:       }

652:       if (j < my - 1) {
653:         v[k]     = ht;
654:         col[k].j = j + 1;
655:         col[k].i = i;
656:         k++;
657:       }

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:   }

667:   PetscCall(DMDAVecRestoreArray(user->dm, localX, (void **)&x));
668:   PetscCall(DMRestoreLocalVector(user->dm, &localX));

670:   PetscCall(MatAssemblyBegin(Hessian, MAT_FINAL_ASSEMBLY));
671:   PetscCall(MatAssemblyEnd(Hessian, MAT_FINAL_ASSEMBLY));

673:   PetscCall(PetscLogFlops(199.0 * xm * ym));
674:   PetscFunctionReturn(PETSC_SUCCESS);
675: }

677: /* ------------------------------------------------------------------- */
678: /*
679:    MSA_BoundaryConditions -  Calculates the boundary conditions for
680:    the region.

682:    Input Parameter:
683: .  user - user-defined application context

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;

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));

706:   bsize = xm + 2;
707:   lsize = ym + 2;
708:   rsize = ym + 2;
709:   tsize = xm + 2;

711:   PetscCall(PetscMalloc1(bsize, &user->bottom));
712:   PetscCall(PetscMalloc1(tsize, &user->top));
713:   PetscCall(PetscMalloc1(lsize, &user->left));
714:   PetscCall(PetscMalloc1(rsize, &user->right));

716:   hx = (r - l) / (mx + 1);
717:   hy = (t - b) / (my + 1);

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:     }

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:       }

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:   }

768:   /* Scale the boundary if desired */
769:   if (1 == 1) {
770:     PetscReal scl = 1.0;

772:     PetscCall(PetscOptionsGetReal(NULL, NULL, "-bottom", &scl, &flg));
773:     if (flg) {
774:       for (i = 0; i < bsize; i++) user->bottom[i] *= scl;
775:     }

777:     PetscCall(PetscOptionsGetReal(NULL, NULL, "-top", &scl, &flg));
778:     if (flg) {
779:       for (i = 0; i < tsize; i++) user->top[i] *= scl;
780:     }

782:     PetscCall(PetscOptionsGetReal(NULL, NULL, "-right", &scl, &flg));
783:     if (flg) {
784:       for (i = 0; i < rsize; i++) user->right[i] *= scl;
785:     }

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: }

795: /* ------------------------------------------------------------------- */
796: /*
797:    MSA_InitialPoint - Calculates the initial guess in one of three ways.

799:    Input Parameters:
800: .  user - user-defined application context
801: .  X - vector for initial guess

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;

812:   PetscFunctionBegin;
813:   PetscCall(PetscOptionsGetReal(NULL, NULL, "-start", &start1, &flg1));
814:   PetscCall(PetscOptionsGetInt(NULL, NULL, "-random", &start2, &flg2));

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;

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;

831:     /* Get local mesh boundaries */
832:     PetscCall(DMDAGetCorners(user->dm, &xs, &ys, NULL, &xm, &ym, NULL));

834:     /* Get pointers to vector data */
835:     PetscCall(DMDAVecGetArray(user->dm, X, (void **)&x));

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: }

847: /*-----------------------------------------------------------------------*/
848: PetscErrorCode My_Monitor(Tao tao, void *ctx)
849: {
850:   Vec X;

852:   PetscFunctionBegin;
853:   PetscCall(TaoGetSolution(tao, &X));
854:   PetscCall(VecView(X, PETSC_VIEWER_STDOUT_WORLD));
855:   PetscFunctionReturn(PETSC_SUCCESS);
856: }

858: /*TEST

860:    build:
861:       requires: !complex

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

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

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

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

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

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

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

904: TEST*/