xref: /petsc/src/tao/unconstrained/tutorials/eptorsion2f.F90 (revision badd099fb2ece77d080fc02aefe95d4a02e75697)
1!  Program usage: mpiexec -n <proc> eptorsion2f [all TAO options]
2!
3!  Description:  This example demonstrates use of the TAO package to solve
4!  unconstrained minimization problems in parallel.  This example is based
5!  on the Elastic-Plastic Torsion (dept) problem from the MINPACK-2 test suite.
6!  The command line options are:
7!    -mx <xg>, where <xg> = number of grid points in the 1st coordinate direction
8!    -my <yg>, where <yg> = number of grid points in the 2nd coordinate direction
9!    -par <param>, where <param> = angle of twist per unit length
10!
11!
12! ----------------------------------------------------------------------
13!
14!  Elastic-plastic torsion problem.
15!
16!  The elastic plastic torsion problem arises from the deconverged
17!  of the stress field on an infinitely long cylindrical bar, which is
18!  equivalent to the solution of the following problem:
19!     min{ .5 * integral(||gradient(v(x))||^2 dx) - C * integral(v(x) dx)}
20!  where C is the torsion angle per unit length.
21!
22!  The C version of this code is eptorsion2.c
23!
24! ----------------------------------------------------------------------
25
26module eptorsion2fmodule
27#include "petsc/finclude/petscdmda.h"
28#include "petsc/finclude/petsctao.h"
29  use petscdmda
30  use petsctao
31  implicit none
32
33  type(tVec) localX
34  type(tDM) dm
35  PetscReal param
36  PetscInt mx, my
37end module
38
39use eptorsion2fmodule
40implicit none
41! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
42!                   Variable declarations
43! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
44!
45!  See additional variable declarations in the file eptorsion2f.h
46!
47PetscErrorCode ierr           ! used to check for functions returning nonzeros
48type(tVec) x              ! solution vector
49type(tMat) H              ! hessian matrix
50PetscInt Nx, Ny         ! number of processes in x- and y- directions
51type(tTao) ta            ! Tao solver context
52PetscBool flg
53PetscInt i1
54PetscInt dummy
55
56!  Note: Any user-defined Fortran routines (such as FormGradient)
57!  MUST be declared as external.
58
59external FormInitialGuess, FormFunctionGradient, ComputeHessian
60external Monitor, ConvergenceTest
61
62i1 = 1
63
64!     Initialize TAO, PETSc  contexts
65PetscCallA(PetscInitialize(ierr))
66
67!     Specify default parameters
68param = 5.0
69mx = 10
70my = 10
71Nx = PETSC_DECIDE
72Ny = PETSC_DECIDE
73
74!     Check for any command line arguments that might override defaults
75PetscCallA(PetscOptionsGetInt(PETSC_NULL_OPTIONS, PETSC_NULL_CHARACTER, '-mx', mx, flg, ierr))
76PetscCallA(PetscOptionsGetInt(PETSC_NULL_OPTIONS, PETSC_NULL_CHARACTER, '-my', my, flg, ierr))
77PetscCallA(PetscOptionsGetReal(PETSC_NULL_OPTIONS, PETSC_NULL_CHARACTER, '-par', param, flg, ierr))
78
79!     Set up distributed array and vectors
80PetscCallA(DMDACreate2d(PETSC_COMM_WORLD, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DMDA_STENCIL_BOX, mx, my, Nx, Ny, i1, i1, PETSC_NULL_INTEGER_ARRAY, PETSC_NULL_INTEGER_ARRAY, dm, ierr))
81PetscCallA(DMSetFromOptions(dm, ierr))
82PetscCallA(DMSetUp(dm, ierr))
83
84!     Create vectors
85PetscCallA(DMCreateGlobalVector(dm, x, ierr))
86PetscCallA(DMCreateLocalVector(dm, localX, ierr))
87
88!     Create Hessian
89PetscCallA(DMCreateMatrix(dm, H, ierr))
90PetscCallA(MatSetOption(H, MAT_SYMMETRIC, PETSC_TRUE, ierr))
91
92!     The TAO code begins here
93
94!     Create TAO solver
95PetscCallA(TaoCreate(PETSC_COMM_WORLD, ta, ierr))
96PetscCallA(TaoSetType(ta, TAOCG, ierr))
97
98!     Set routines for function and gradient evaluation
99
100PetscCallA(TaoSetObjectiveAndGradient(ta, PETSC_NULL_VEC, FormFunctionGradient, 0, ierr))
101PetscCallA(TaoSetHessian(ta, H, H, ComputeHessian, 0, ierr))
102
103!     Set initial guess
104PetscCallA(FormInitialGuess(x, ierr))
105PetscCallA(TaoSetSolution(ta, x, ierr))
106
107PetscCallA(PetscOptionsHasName(PETSC_NULL_OPTIONS, PETSC_NULL_CHARACTER, '-testmonitor', flg, ierr))
108if (flg) then
109  PetscCallA(TaoMonitorSet(ta, Monitor, dummy, PETSC_NULL_FUNCTION, ierr))
110end if
111
112PetscCallA(PetscOptionsHasName(PETSC_NULL_OPTIONS, PETSC_NULL_CHARACTER, '-testconvergence', flg, ierr))
113if (flg) then
114  PetscCallA(TaoSetConvergenceTest(ta, ConvergenceTest, dummy, ierr))
115end if
116
117!     Check for any TAO command line options
118PetscCallA(TaoSetFromOptions(ta, ierr))
119
120!     SOLVE THE APPLICATION
121PetscCallA(TaoSolve(ta, ierr))
122
123!     Free TAO data structures
124PetscCallA(TaoDestroy(ta, ierr))
125
126!     Free PETSc data structures
127PetscCallA(VecDestroy(x, ierr))
128PetscCallA(VecDestroy(localX, ierr))
129PetscCallA(MatDestroy(H, ierr))
130PetscCallA(DMDestroy(dm, ierr))
131
132!     Finalize TAO and PETSc
133PetscCallA(PetscFinalize(ierr))
134end
135
136! ---------------------------------------------------------------------
137!
138!   FormInitialGuess - Computes an initial approximation to the solution.
139!
140!   Input Parameters:
141!   X    - vector
142!
143!   Output Parameters:
144!   X    - vector
145!   ierr - error code
146!
147subroutine FormInitialGuess(X, ierr)
148  use eptorsion2fmodule
149  implicit none
150
151!  Input/output variables:
152  Vec X
153  PetscErrorCode ierr
154
155!  Local variables:
156  PetscInt i, j, k, xe, ye
157  PetscReal temp, val, hx, hy
158  PetscInt xs, ys, xm, ym
159  PetscInt gxm, gym, gxs, gys
160  PetscInt i1
161
162  i1 = 1
163  hx = 1.0/real(mx + 1)
164  hy = 1.0/real(my + 1)
165
166!  Get corner information
167  PetscCall(DMDAGetCorners(dm, xs, ys, PETSC_NULL_INTEGER, xm, ym, PETSC_NULL_INTEGER, ierr))
168  PetscCall(DMDAGetGhostCorners(dm, gxs, gys, PETSC_NULL_INTEGER, gxm, gym, PETSC_NULL_INTEGER, ierr))
169
170!  Compute initial guess over locally owned part of mesh
171  xe = xs + xm
172  ye = ys + ym
173  do j = ys, ye - 1
174    temp = min(j + 1, my - j)*hy
175    do i = xs, xe - 1
176      k = (j - gys)*gxm + i - gxs
177      val = min((min(i + 1, mx - i))*hx, temp)
178      PetscCall(VecSetValuesLocal(X, i1, [k], [val], ADD_VALUES, ierr))
179    end do
180  end do
181  PetscCall(VecAssemblyBegin(X, ierr))
182  PetscCall(VecAssemblyEnd(X, ierr))
183end
184
185! ---------------------------------------------------------------------
186!
187!  FormFunctionGradient - Evaluates gradient G(X).
188!
189!  Input Parameters:
190!  tao   - the Tao context
191!  X     - input vector
192!  dummy - optional user-defined context (not used here)
193!
194!  Output Parameters:
195!  f     - the function value at X
196!  G     - vector containing the newly evaluated gradient
197!  ierr  - error code
198!
199!  Notes:
200!  This routine serves as a wrapper for the lower-level routine
201!  "ApplicationGradient", where the actual computations are
202!  done using the standard Fortran style of treating the local
203!  input vector data as an array over the local mesh.
204!
205subroutine FormFunctionGradient(ta, X, f, G, dummy, ierr)
206  use eptorsion2fmodule
207  implicit none
208
209!  Input/output variables:
210  type(tTao) ta
211  type(tVec) X, G
212  PetscReal f
213  PetscErrorCode ierr
214  PetscInt dummy
215
216!  Declarations for use with local array:
217
218  PetscReal, pointer :: lx_v(:)
219
220!  Local variables:
221  PetscReal zero, p5, area, cdiv3
222  PetscReal val, flin, fquad, floc
223  PetscReal v, vb, vl, vr, vt, dvdx
224  PetscReal dvdy, hx, hy
225  PetscInt xe, ye, xsm, ysm
226  PetscInt xep, yep, i, j, k, ind
227  PetscInt xs, ys, xm, ym
228  PetscInt gxs, gys, gxm, gym
229  PetscInt i1
230
231  i1 = 1
232  ierr = 0
233  cdiv3 = param/3.0
234  zero = 0.0
235  p5 = 0.5
236  hx = 1.0/real(mx + 1)
237  hy = 1.0/real(my + 1)
238  fquad = zero
239  flin = zero
240
241!  Initialize gradient to zero
242  PetscCall(VecSet(G, zero, ierr))
243
244!  Scatter ghost points to local vector
245  PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX, ierr))
246  PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX, ierr))
247
248!  Get corner information
249  PetscCall(DMDAGetCorners(dm, xs, ys, PETSC_NULL_INTEGER, xm, ym, PETSC_NULL_INTEGER, ierr))
250  PetscCall(DMDAGetGhostCorners(dm, gxs, gys, PETSC_NULL_INTEGER, gxm, gym, PETSC_NULL_INTEGER, ierr))
251
252!  Get pointer to vector data.
253  PetscCall(VecGetArrayRead(localX, lx_v, ierr))
254
255!  Set local loop dimensions
256  xe = xs + xm
257  ye = ys + ym
258  if (xs == 0) then
259    xsm = xs - 1
260  else
261    xsm = xs
262  end if
263  if (ys == 0) then
264    ysm = ys - 1
265  else
266    ysm = ys
267  end if
268  if (xe == mx) then
269    xep = xe + 1
270  else
271    xep = xe
272  end if
273  if (ye == my) then
274    yep = ye + 1
275  else
276    yep = ye
277  end if
278
279!     Compute local gradient contributions over the lower triangular elements
280
281  do j = ysm, ye - 1
282    do i = xsm, xe - 1
283      k = (j - gys)*gxm + i - gxs
284      v = zero
285      vr = zero
286      vt = zero
287      if (i >= 0 .and. j >= 0) v = lx_v(k + 1)
288      if (i < mx - 1 .and. j > -1) vr = lx_v(k + 2)
289      if (i > -1 .and. j < my - 1) vt = lx_v(k + 1 + gxm)
290      dvdx = (vr - v)/hx
291      dvdy = (vt - v)/hy
292      if (i /= -1 .and. j /= -1) then
293        ind = k
294        val = -dvdx/hx - dvdy/hy - cdiv3
295        PetscCall(VecSetValuesLocal(G, i1, [k], [val], ADD_VALUES, ierr))
296      end if
297      if (i /= mx - 1 .and. j /= -1) then
298        ind = k + 1
299        val = dvdx/hx - cdiv3
300        PetscCall(VecSetValuesLocal(G, i1, [ind], [val], ADD_VALUES, ierr))
301      end if
302      if (i /= -1 .and. j /= my - 1) then
303        ind = k + gxm
304        val = dvdy/hy - cdiv3
305        PetscCall(VecSetValuesLocal(G, i1, [ind], [val], ADD_VALUES, ierr))
306      end if
307      fquad = fquad + dvdx*dvdx + dvdy*dvdy
308      flin = flin - cdiv3*(v + vr + vt)
309    end do
310  end do
311
312!     Compute local gradient contributions over the upper triangular elements
313
314  do j = ys, yep - 1
315    do i = xs, xep - 1
316      k = (j - gys)*gxm + i - gxs
317      vb = zero
318      vl = zero
319      v = zero
320      if (i < mx .and. j > 0) vb = lx_v(k + 1 - gxm)
321      if (i > 0 .and. j < my) vl = lx_v(k)
322      if (i < mx .and. j < my) v = lx_v(1 + k)
323      dvdx = (v - vl)/hx
324      dvdy = (v - vb)/hy
325      if (i /= mx .and. j /= 0) then
326        ind = k - gxm
327        val = -dvdy/hy - cdiv3
328        PetscCall(VecSetValuesLocal(G, i1, [ind], [val], ADD_VALUES, ierr))
329      end if
330      if (i /= 0 .and. j /= my) then
331        ind = k - 1
332        val = -dvdx/hx - cdiv3
333        PetscCall(VecSetValuesLocal(G, i1, [ind], [val], ADD_VALUES, ierr))
334      end if
335      if (i /= mx .and. j /= my) then
336        ind = k
337        val = dvdx/hx + dvdy/hy - cdiv3
338        PetscCall(VecSetValuesLocal(G, i1, [ind], [val], ADD_VALUES, ierr))
339      end if
340      fquad = fquad + dvdx*dvdx + dvdy*dvdy
341      flin = flin - cdiv3*(vb + vl + v)
342    end do
343  end do
344
345!  Restore vector
346  PetscCall(VecRestoreArrayRead(localX, lx_v, ierr))
347
348!  Assemble gradient vector
349  PetscCall(VecAssemblyBegin(G, ierr))
350  PetscCall(VecAssemblyEnd(G, ierr))
351
352! Scale the gradient
353  area = p5*hx*hy
354  floc = area*(p5*fquad + flin)
355  PetscCall(VecScale(G, area, ierr))
356
357!  Sum function contributions from all processes
358  PetscCallMPI(MPI_Allreduce(floc, f, 1, MPIU_SCALAR, MPIU_SUM, PETSC_COMM_WORLD, ierr))
359  PetscCall(PetscLogFlops(20.0d0*(ye - ysm)*(xe - xsm) + 16.0d0*(xep - xs)*(yep - ys), ierr))
360end
361
362subroutine ComputeHessian(ta, X, H, Hpre, dummy, ierr)
363  use eptorsion2fmodule
364  implicit none
365
366  type(tTao) ta
367  type(tVec) X
368  type(tMat) H, Hpre
369  PetscErrorCode ierr
370  PetscInt dummy
371
372  PetscInt i, j, k
373  PetscInt col(0:4), row
374  PetscInt xs, xm, gxs, gxm
375  PetscInt ys, ym, gys, gym
376  PetscReal v(0:4)
377  PetscInt i1
378
379  i1 = 1
380
381!     Get local grid boundaries
382  PetscCall(DMDAGetCorners(dm, xs, ys, PETSC_NULL_INTEGER, xm, ym, PETSC_NULL_INTEGER, ierr))
383  PetscCall(DMDAGetGhostCorners(dm, gxs, gys, PETSC_NULL_INTEGER, gxm, gym, PETSC_NULL_INTEGER, ierr))
384
385  do j = ys, ys + ym - 1
386    do i = xs, xs + xm - 1
387      row = (j - gys)*gxm + (i - gxs)
388
389      k = 0
390      if (j > gys) then
391        v(k) = -1.0
392        col(k) = row - gxm
393        k = k + 1
394      end if
395
396      if (i > gxs) then
397        v(k) = -1.0
398        col(k) = row - 1
399        k = k + 1
400      end if
401
402      v(k) = 4.0
403      col(k) = row
404      k = k + 1
405
406      if (i + 1 < gxs + gxm) then
407        v(k) = -1.0
408        col(k) = row + 1
409        k = k + 1
410      end if
411
412      if (j + 1 < gys + gym) then
413        v(k) = -1.0
414        col(k) = row + gxm
415        k = k + 1
416      end if
417
418      PetscCall(MatSetValuesLocal(H, i1, [row], k, col, v, INSERT_VALUES, ierr))
419    end do
420  end do
421
422!     Assemble matrix
423  PetscCall(MatAssemblyBegin(H, MAT_FINAL_ASSEMBLY, ierr))
424  PetscCall(MatAssemblyEnd(H, MAT_FINAL_ASSEMBLY, ierr))
425
426!     Tell the matrix we will never add a new nonzero location to the
427!     matrix.  If we do it will generate an error.
428
429  PetscCall(MatSetOption(H, MAT_NEW_NONZERO_LOCATION_ERR, PETSC_TRUE, ierr))
430  PetscCall(MatSetOption(H, MAT_SYMMETRIC, PETSC_TRUE, ierr))
431
432  PetscCall(PetscLogFlops(9.0d0*xm*ym + 49.0d0*xm, ierr))
433
434  ierr = 0
435end
436
437subroutine Monitor(ta, dummy, ierr)
438  use eptorsion2fmodule
439  implicit none
440
441  type(tTao) ta
442  PetscInt dummy
443  PetscErrorCode ierr
444
445  PetscInt its
446  PetscReal f, gnorm, cnorm, xdiff
447  TaoConvergedReason reason
448
449  PetscCall(TaoGetSolutionStatus(ta, its, f, gnorm, cnorm, xdiff, reason, ierr))
450  if (mod(its, 5) /= 0) then
451    PetscCall(PetscPrintf(PETSC_COMM_WORLD, 'iteration multiple of 5\n', ierr))
452  end if
453
454  ierr = 0
455
456end
457
458subroutine ConvergenceTest(ta, dummy, ierr)
459  use eptorsion2fmodule
460  implicit none
461
462  type(tTao) ta
463  PetscInt dummy
464  PetscErrorCode ierr
465
466  PetscInt its
467  PetscReal f, gnorm, cnorm, xdiff
468  TaoConvergedReason reason
469
470  PetscCall(TaoGetSolutionStatus(ta, its, f, gnorm, cnorm, xdiff, reason, ierr))
471  if (its == 7) then
472    PetscCall(TaoSetConvergedReason(ta, TAO_DIVERGED_MAXITS, ierr))
473  end if
474
475  ierr = 0
476
477end
478
479!/*TEST
480!
481!   build:
482!      requires: !complex
483!
484!   test:
485!      args: -tao_monitor_short -tao_type nls -tao_gttol 1.e-2
486!
487!   test:
488!      suffix: 2
489!      nsize: 2
490!      args: -tao_monitor_short -tao_type lmvm -tao_gttol 1.e-2
491!
492!   test:
493!      suffix: 3
494!      args: -testmonitor -tao_type lmvm -tao_gttol 1.e-2
495!TEST*/
496