xref: /petsc/src/tao/unconstrained/tutorials/eptorsion2.c (revision fbf9dbe564678ed6eff1806adbc4c4f01b9743f4)
1 /* Program usage: mpiexec -n <proc> eptorsion2 [-help] [all TAO options] */
2 
3 /* ----------------------------------------------------------------------
4 
5   Elastic-plastic torsion problem.
6 
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:
10 
11   min{ .5 * integral(||gradient(v(x))||^2 dx) - C * integral(v(x) dx)}
12 
13   where C is the torsion angle per unit length.
14 
15   The uniprocessor version of this code is eptorsion1.c; the Fortran
16   version of this code is eptorsion2f.F.
17 
18   This application solves the problem without calculating hessians
19 ---------------------------------------------------------------------- */
20 
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 */
32 
33 #include <petsctao.h>
34 #include <petscdmda.h>
35 
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";
43 
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 */
53 
54   /* work space */
55   Vec localX; /* local vectors */
56   DM  dm;     /* distributed array data structure */
57 } AppCtx;
58 
59 PetscErrorCode FormInitialGuess(AppCtx *, Vec);
60 PetscErrorCode FormFunctionGradient(Tao, Vec, PetscReal *, Vec, void *);
61 PetscErrorCode FormHessian(Tao, Vec, Mat, Mat, void *);
62 
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;
73 
74   PetscCall(PetscInitialize(&argc, &argv, (char *)0, help));
75 
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;
81 
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));
86 
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));
89 
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));
94 
95   /* Create vectors */
96   PetscCall(DMCreateGlobalVector(user.dm, &x));
97 
98   PetscCall(DMCreateLocalVector(user.dm, &user.localX));
99 
100   /* Create Hessian */
101   PetscCall(DMCreateMatrix(user.dm, &H));
102   PetscCall(MatSetOption(H, MAT_SYMMETRIC, PETSC_TRUE));
103 
104   /* The TAO code begins here */
105 
106   /* Create TAO solver and set desired solution method */
107   PetscCall(TaoCreate(PETSC_COMM_WORLD, &tao));
108   PetscCall(TaoSetType(tao, TAOCG));
109 
110   /* Set initial solution guess */
111   PetscCall(FormInitialGuess(&user, x));
112   PetscCall(TaoSetSolution(tao, x));
113 
114   /* Set routine for function and gradient evaluation */
115   PetscCall(TaoSetObjectiveAndGradient(tao, NULL, FormFunctionGradient, (void *)&user));
116 
117   PetscCall(TaoSetHessian(tao, H, H, FormHessian, (void *)&user));
118 
119   /* Check for any TAO command line options */
120   PetscCall(TaoSetFromOptions(tao));
121 
122   PetscCall(TaoGetKSP(tao, &ksp));
123   if (ksp) {
124     PetscCall(KSPGetPC(ksp, &pc));
125     PetscCall(PCSetType(pc, PCNONE));
126   }
127 
128   /* SOLVE THE APPLICATION */
129   PetscCall(TaoSolve(tao));
130 
131   /* Free TAO data structures */
132   PetscCall(TaoDestroy(&tao));
133 
134   /* Free PETSc data structures */
135   PetscCall(VecDestroy(&x));
136   PetscCall(MatDestroy(&H));
137 
138   PetscCall(VecDestroy(&user.localX));
139   PetscCall(DMDestroy(&user.dm));
140 
141   PetscCall(PetscFinalize());
142   return 0;
143 }
144 
145 /* ------------------------------------------------------------------- */
146 /*
147     FormInitialGuess - Computes an initial approximation to the solution.
148 
149     Input Parameters:
150 .   user - user-defined application context
151 .   X    - vector
152 
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;
161 
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));
166 
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 }
182 
183 /* ------------------------------------------------------------------ */
184 /*
185    FormFunctionGradient - Evaluates the function and corresponding gradient.
186 
187    Input Parameters:
188    tao - the Tao context
189    X   - the input vector
190    ptr - optional user-defined context, as set by TaoSetObjectiveAndGradient()
191 
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;
209 
210   PetscFunctionBegin;
211   /* Initialize */
212   flin = fquad = zero;
213 
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));
223 
224   /* Get pointer to vector data */
225   PetscCall(VecGetArray(localX, &x));
226 
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));
230 
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;
242 
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   }
274 
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   }
306 
307   /* Restore vector */
308   PetscCall(VecRestoreArray(localX, &x));
309 
310   /* Assemble gradient vector */
311   PetscCall(VecAssemblyBegin(G));
312   PetscCall(VecAssemblyEnd(G));
313 
314   /* Scale the gradient */
315   area = p5 * hx * hy;
316   floc = area * (p5 * fquad + flin);
317   PetscCall(VecScale(G, area));
318 
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));
321 
322   PetscCall(PetscLogFlops((ye - ysm) * (xe - xsm) * 20 + (xep - xs) * (yep - ys) * 16));
323   PetscFunctionReturn(PETSC_SUCCESS);
324 }
325 
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;
334 
335   /* Compute the quadratic term in the objective function */
336 
337   /*
338      Get local grid boundaries
339   */
340 
341   PetscFunctionBegin;
342   PetscCall(DMDAGetCorners(user->dm, &xs, &ys, NULL, &xm, &ym, NULL));
343   PetscCall(DMDAGetGhostCorners(user->dm, &gxs, &gys, NULL, &gxm, &gym, NULL));
344 
345   for (j = ys; j < ys + ym; j++) {
346     for (i = xs; i < xs + xm; i++) {
347       row = (j - gys) * gxm + (i - gxs);
348 
349       k = 0;
350       if (j > gys) {
351         v[k]   = -2 * hyhy;
352         col[k] = row - gxm;
353         k++;
354       }
355 
356       if (i > gxs) {
357         v[k]   = -2 * hxhx;
358         col[k] = row - 1;
359         k++;
360       }
361 
362       v[k]   = 4.0 * (hxhx + hyhy);
363       col[k] = row;
364       k++;
365 
366       if (i + 1 < gxs + gxm) {
367         v[k]   = -2.0 * hxhx;
368         col[k] = row + 1;
369         k++;
370       }
371 
372       if (j + 1 < gys + gym) {
373         v[k]   = -2 * hyhy;
374         col[k] = row + gxm;
375         k++;
376       }
377 
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 }
399 
400 /*TEST
401 
402    build:
403       requires: !complex
404 
405    test:
406       suffix: 1
407       nsize: 2
408       args: -tao_smonitor -tao_type nls -mx 16 -my 16 -tao_gatol 1.e-4
409 
410 TEST*/
411