Actual source code: eptorsion2.c

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

  3: /* ----------------------------------------------------------------------

  5:   Elastic-plastic torsion problem.

  7:   The elastic plastic torsion problem arises from the determination
  8:   of the stress field on an infinitely long cylindrical bar, which is
  9:   equivalent to the solution of the following problem:

 11:   min{ .5 * integral(||gradient(v(x))||^2 dx) - C * integral(v(x) dx)}

 13:   where C is the torsion angle per unit length.

 15:   The uniprocessor version of this code is eptorsion1.c; the Fortran
 16:   version of this code is eptorsion2f.F.

 18:   This application solves the problem without calculating hessians
 19: ---------------------------------------------------------------------- */

 21: /*
 22:   Include "petsctao.h" so that we can use TAO solvers.  Note that this
 23:   file automatically includes files for lower-level support, such as those
 24:   provided by the PETSc library:
 25:      petsc.h       - base PETSc routines   petscvec.h - vectors
 26:      petscsys.h    - system routines        petscmat.h - matrices
 27:      petscis.h     - index sets            petscksp.h - Krylov subspace methods
 28:      petscviewer.h - viewers               petscpc.h  - preconditioners
 29:   Include "petscdmda.h" so that we can use distributed arrays (DMs) for managing
 30:   the parallel mesh.
 31: */

 33: #include <petsctao.h>
 34: #include <petscdmda.h>

 36: static char help[] = "Demonstrates use of the TAO package to solve \n\
 37: unconstrained minimization problems in parallel.  This example is based on \n\
 38: the Elastic-Plastic Torsion (dept) problem from the MINPACK-2 test suite.\n\
 39: The command line options are:\n\
 40:   -mx <xg>, where <xg> = number of grid points in the 1st coordinate direction\n\
 41:   -my <yg>, where <yg> = number of grid points in the 2nd coordinate direction\n\
 42:   -par <param>, where <param> = angle of twist per unit length\n\n";

 44: /*
 45:    User-defined application context - contains data needed by the
 46:    application-provided call-back routines, FormFunction() and
 47:    FormGradient().
 48: */
 49: typedef struct {
 50:   /* parameters */
 51:   PetscInt  mx, my; /* global discretization in x- and y-directions */
 52:   PetscReal param;  /* nonlinearity parameter */

 54:   /* work space */
 55:   Vec localX; /* local vectors */
 56:   DM  dm;     /* distributed array data structure */
 57: } AppCtx;

 59: PetscErrorCode FormInitialGuess(AppCtx *, Vec);
 60: PetscErrorCode FormFunctionGradient(Tao, Vec, PetscReal *, Vec, void *);
 61: PetscErrorCode FormHessian(Tao, Vec, Mat, Mat, void *);

 63: int main(int argc, char **argv)
 64: {
 65:   Vec       x;
 66:   Mat       H;
 67:   PetscInt  Nx, Ny;
 68:   Tao       tao;
 69:   PetscBool flg;
 70:   KSP       ksp;
 71:   PC        pc;
 72:   AppCtx    user;

 74:   PetscCall(PetscInitialize(&argc, &argv, (char *)0, help));

 76:   /* Specify default dimension of the problem */
 77:   user.param = 5.0;
 78:   user.mx    = 10;
 79:   user.my    = 10;
 80:   Nx = Ny = PETSC_DECIDE;

 82:   /* Check for any command line arguments that override defaults */
 83:   PetscCall(PetscOptionsGetReal(NULL, NULL, "-par", &user.param, &flg));
 84:   PetscCall(PetscOptionsGetInt(NULL, NULL, "-my", &user.my, &flg));
 85:   PetscCall(PetscOptionsGetInt(NULL, NULL, "-mx", &user.mx, &flg));

 87:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "\n---- Elastic-Plastic Torsion Problem -----\n"));
 88:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "mx: %" PetscInt_FMT "     my: %" PetscInt_FMT "   \n\n", user.mx, user.my));

 90:   /* Set up distributed array */
 91:   PetscCall(DMDACreate2d(PETSC_COMM_WORLD, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DMDA_STENCIL_STAR, user.mx, user.my, Nx, Ny, 1, 1, NULL, NULL, &user.dm));
 92:   PetscCall(DMSetFromOptions(user.dm));
 93:   PetscCall(DMSetUp(user.dm));

 95:   /* Create vectors */
 96:   PetscCall(DMCreateGlobalVector(user.dm, &x));

 98:   PetscCall(DMCreateLocalVector(user.dm, &user.localX));

100:   /* Create Hessian */
101:   PetscCall(DMCreateMatrix(user.dm, &H));
102:   PetscCall(MatSetOption(H, MAT_SYMMETRIC, PETSC_TRUE));

104:   /* The TAO code begins here */

106:   /* Create TAO solver and set desired solution method */
107:   PetscCall(TaoCreate(PETSC_COMM_WORLD, &tao));
108:   PetscCall(TaoSetType(tao, TAOCG));

110:   /* Set initial solution guess */
111:   PetscCall(FormInitialGuess(&user, x));
112:   PetscCall(TaoSetSolution(tao, x));

114:   /* Set routine for function and gradient evaluation */
115:   PetscCall(TaoSetObjectiveAndGradient(tao, NULL, FormFunctionGradient, (void *)&user));

117:   PetscCall(TaoSetHessian(tao, H, H, FormHessian, (void *)&user));

119:   /* Check for any TAO command line options */
120:   PetscCall(TaoSetFromOptions(tao));

122:   PetscCall(TaoGetKSP(tao, &ksp));
123:   if (ksp) {
124:     PetscCall(KSPGetPC(ksp, &pc));
125:     PetscCall(PCSetType(pc, PCNONE));
126:   }

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

131:   /* Free TAO data structures */
132:   PetscCall(TaoDestroy(&tao));

134:   /* Free PETSc data structures */
135:   PetscCall(VecDestroy(&x));
136:   PetscCall(MatDestroy(&H));

138:   PetscCall(VecDestroy(&user.localX));
139:   PetscCall(DMDestroy(&user.dm));

141:   PetscCall(PetscFinalize());
142:   return 0;
143: }

145: /* ------------------------------------------------------------------- */
146: /*
147:     FormInitialGuess - Computes an initial approximation to the solution.

149:     Input Parameters:
150: .   user - user-defined application context
151: .   X    - vector

153:     Output Parameters:
154:     X    - vector
155: */
156: PetscErrorCode FormInitialGuess(AppCtx *user, Vec X)
157: {
158:   PetscInt  i, j, k, mx = user->mx, my = user->my;
159:   PetscInt  xs, ys, xm, ym, gxm, gym, gxs, gys, xe, ye;
160:   PetscReal hx = 1.0 / (mx + 1), hy = 1.0 / (my + 1), temp, val;

162:   PetscFunctionBegin;
163:   /* Get local mesh boundaries */
164:   PetscCall(DMDAGetCorners(user->dm, &xs, &ys, NULL, &xm, &ym, NULL));
165:   PetscCall(DMDAGetGhostCorners(user->dm, &gxs, &gys, NULL, &gxm, &gym, NULL));

167:   /* Compute initial guess over locally owned part of mesh */
168:   xe = xs + xm;
169:   ye = ys + ym;
170:   for (j = ys; j < ye; j++) { /*  for (j=0; j<my; j++) */
171:     temp = PetscMin(j + 1, my - j) * hy;
172:     for (i = xs; i < xe; i++) { /*  for (i=0; i<mx; i++) */
173:       k   = (j - gys) * gxm + i - gxs;
174:       val = PetscMin((PetscMin(i + 1, mx - i)) * hx, temp);
175:       PetscCall(VecSetValuesLocal(X, 1, &k, &val, ADD_VALUES));
176:     }
177:   }
178:   PetscCall(VecAssemblyBegin(X));
179:   PetscCall(VecAssemblyEnd(X));
180:   PetscFunctionReturn(PETSC_SUCCESS);
181: }

183: /* ------------------------------------------------------------------ */
184: /*
185:    FormFunctionGradient - Evaluates the function and corresponding gradient.

187:    Input Parameters:
188:    tao - the Tao context
189:    X   - the input vector
190:    ptr - optional user-defined context, as set by TaoSetObjectiveAndGradient()

192:    Output Parameters:
193:    f   - the newly evaluated function
194:    G   - the newly evaluated gradient
195: */
196: PetscErrorCode FormFunctionGradient(Tao tao, Vec X, PetscReal *f, Vec G, void *ptr)
197: {
198:   AppCtx   *user = (AppCtx *)ptr;
199:   PetscInt  i, j, k, ind;
200:   PetscInt  xe, ye, xsm, ysm, xep, yep;
201:   PetscInt  xs, ys, xm, ym, gxm, gym, gxs, gys;
202:   PetscInt  mx = user->mx, my = user->my;
203:   PetscReal three = 3.0, zero = 0.0, *x, floc, cdiv3 = user->param / three;
204:   PetscReal p5 = 0.5, area, val, flin, fquad;
205:   PetscReal v, vb, vl, vr, vt, dvdx, dvdy;
206:   PetscReal hx     = 1.0 / (user->mx + 1);
207:   PetscReal hy     = 1.0 / (user->my + 1);
208:   Vec       localX = user->localX;

210:   PetscFunctionBegin;
211:   /* Initialize */
212:   flin = fquad = zero;

214:   PetscCall(VecSet(G, zero));
215:   /*
216:      Scatter ghost points to local vector,using the 2-step process
217:         DMGlobalToLocalBegin(),DMGlobalToLocalEnd().
218:      By placing code between these two statements, computations can be
219:      done while messages are in transition.
220:   */
221:   PetscCall(DMGlobalToLocalBegin(user->dm, X, INSERT_VALUES, localX));
222:   PetscCall(DMGlobalToLocalEnd(user->dm, X, INSERT_VALUES, localX));

224:   /* Get pointer to vector data */
225:   PetscCall(VecGetArray(localX, &x));

227:   /* Get local mesh boundaries */
228:   PetscCall(DMDAGetCorners(user->dm, &xs, &ys, NULL, &xm, &ym, NULL));
229:   PetscCall(DMDAGetGhostCorners(user->dm, &gxs, &gys, NULL, &gxm, &gym, NULL));

231:   /* Set local loop dimensions */
232:   xe = xs + xm;
233:   ye = ys + ym;
234:   if (xs == 0) xsm = xs - 1;
235:   else xsm = xs;
236:   if (ys == 0) ysm = ys - 1;
237:   else ysm = ys;
238:   if (xe == mx) xep = xe + 1;
239:   else xep = xe;
240:   if (ye == my) yep = ye + 1;
241:   else yep = ye;

243:   /* Compute local gradient contributions over the lower triangular elements */
244:   for (j = ysm; j < ye; j++) {   /*  for (j=-1; j<my; j++) */
245:     for (i = xsm; i < xe; i++) { /*   for (i=-1; i<mx; i++) */
246:       k  = (j - gys) * gxm + i - gxs;
247:       v  = zero;
248:       vr = zero;
249:       vt = zero;
250:       if (i >= 0 && j >= 0) v = x[k];
251:       if (i < mx - 1 && j > -1) vr = x[k + 1];
252:       if (i > -1 && j < my - 1) vt = x[k + gxm];
253:       dvdx = (vr - v) / hx;
254:       dvdy = (vt - v) / hy;
255:       if (i != -1 && j != -1) {
256:         ind = k;
257:         val = -dvdx / hx - dvdy / hy - cdiv3;
258:         PetscCall(VecSetValuesLocal(G, 1, &k, &val, ADD_VALUES));
259:       }
260:       if (i != mx - 1 && j != -1) {
261:         ind = k + 1;
262:         val = dvdx / hx - cdiv3;
263:         PetscCall(VecSetValuesLocal(G, 1, &ind, &val, ADD_VALUES));
264:       }
265:       if (i != -1 && j != my - 1) {
266:         ind = k + gxm;
267:         val = dvdy / hy - cdiv3;
268:         PetscCall(VecSetValuesLocal(G, 1, &ind, &val, ADD_VALUES));
269:       }
270:       fquad += dvdx * dvdx + dvdy * dvdy;
271:       flin -= cdiv3 * (v + vr + vt);
272:     }
273:   }

275:   /* Compute local gradient contributions over the upper triangular elements */
276:   for (j = ys; j < yep; j++) {   /*  for (j=0; j<=my; j++) */
277:     for (i = xs; i < xep; i++) { /*   for (i=0; i<=mx; i++) */
278:       k  = (j - gys) * gxm + i - gxs;
279:       vb = zero;
280:       vl = zero;
281:       v  = zero;
282:       if (i < mx && j > 0) vb = x[k - gxm];
283:       if (i > 0 && j < my) vl = x[k - 1];
284:       if (i < mx && j < my) v = x[k];
285:       dvdx = (v - vl) / hx;
286:       dvdy = (v - vb) / hy;
287:       if (i != mx && j != 0) {
288:         ind = k - gxm;
289:         val = -dvdy / hy - cdiv3;
290:         PetscCall(VecSetValuesLocal(G, 1, &ind, &val, ADD_VALUES));
291:       }
292:       if (i != 0 && j != my) {
293:         ind = k - 1;
294:         val = -dvdx / hx - cdiv3;
295:         PetscCall(VecSetValuesLocal(G, 1, &ind, &val, ADD_VALUES));
296:       }
297:       if (i != mx && j != my) {
298:         ind = k;
299:         val = dvdx / hx + dvdy / hy - cdiv3;
300:         PetscCall(VecSetValuesLocal(G, 1, &ind, &val, ADD_VALUES));
301:       }
302:       fquad += dvdx * dvdx + dvdy * dvdy;
303:       flin -= cdiv3 * (vb + vl + v);
304:     }
305:   }

307:   /* Restore vector */
308:   PetscCall(VecRestoreArray(localX, &x));

310:   /* Assemble gradient vector */
311:   PetscCall(VecAssemblyBegin(G));
312:   PetscCall(VecAssemblyEnd(G));

314:   /* Scale the gradient */
315:   area = p5 * hx * hy;
316:   floc = area * (p5 * fquad + flin);
317:   PetscCall(VecScale(G, area));

319:   /* Sum function contributions from all processes */ /* TODO: Change to PetscCallMPI() */
320:   PetscCall((PetscErrorCode)MPI_Allreduce((void *)&floc, (void *)f, 1, MPIU_REAL, MPIU_SUM, MPI_COMM_WORLD));

322:   PetscCall(PetscLogFlops((ye - ysm) * (xe - xsm) * 20 + (xep - xs) * (yep - ys) * 16));
323:   PetscFunctionReturn(PETSC_SUCCESS);
324: }

326: PetscErrorCode FormHessian(Tao tao, Vec X, Mat A, Mat Hpre, void *ctx)
327: {
328:   AppCtx   *user = (AppCtx *)ctx;
329:   PetscInt  i, j, k;
330:   PetscInt  col[5], row;
331:   PetscInt  xs, xm, gxs, gxm, ys, ym, gys, gym;
332:   PetscReal v[5];
333:   PetscReal hx = 1.0 / (user->mx + 1), hy = 1.0 / (user->my + 1), hxhx = 1.0 / (hx * hx), hyhy = 1.0 / (hy * hy), area = 0.5 * hx * hy;

335:   /* Compute the quadratic term in the objective function */

337:   /*
338:      Get local grid boundaries
339:   */

341:   PetscFunctionBegin;
342:   PetscCall(DMDAGetCorners(user->dm, &xs, &ys, NULL, &xm, &ym, NULL));
343:   PetscCall(DMDAGetGhostCorners(user->dm, &gxs, &gys, NULL, &gxm, &gym, NULL));

345:   for (j = ys; j < ys + ym; j++) {
346:     for (i = xs; i < xs + xm; i++) {
347:       row = (j - gys) * gxm + (i - gxs);

349:       k = 0;
350:       if (j > gys) {
351:         v[k]   = -2 * hyhy;
352:         col[k] = row - gxm;
353:         k++;
354:       }

356:       if (i > gxs) {
357:         v[k]   = -2 * hxhx;
358:         col[k] = row - 1;
359:         k++;
360:       }

362:       v[k]   = 4.0 * (hxhx + hyhy);
363:       col[k] = row;
364:       k++;

366:       if (i + 1 < gxs + gxm) {
367:         v[k]   = -2.0 * hxhx;
368:         col[k] = row + 1;
369:         k++;
370:       }

372:       if (j + 1 < gys + gym) {
373:         v[k]   = -2 * hyhy;
374:         col[k] = row + gxm;
375:         k++;
376:       }

378:       PetscCall(MatSetValuesLocal(A, 1, &row, k, col, v, INSERT_VALUES));
379:     }
380:   }
381:   /*
382:      Assemble matrix, using the 2-step process:
383:      MatAssemblyBegin(), MatAssemblyEnd().
384:      By placing code between these two statements, computations can be
385:      done while messages are in transition.
386:   */
387:   PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
388:   PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
389:   /*
390:     Tell the matrix we will never add a new nonzero location to the
391:     matrix. If we do it will generate an error.
392:   */
393:   PetscCall(MatScale(A, area));
394:   PetscCall(MatSetOption(A, MAT_NEW_NONZERO_LOCATION_ERR, PETSC_TRUE));
395:   PetscCall(MatSetOption(A, MAT_SYMMETRIC, PETSC_TRUE));
396:   PetscCall(PetscLogFlops(9 * xm * ym + 49 * xm));
397:   PetscFunctionReturn(PETSC_SUCCESS);
398: }

400: /*TEST

402:    build:
403:       requires: !complex

405:    test:
406:       suffix: 1
407:       nsize: 2
408:       args: -tao_monitor_short -tao_type nls -mx 16 -my 16 -tao_gatol 1.e-4

410: TEST*/