xref: /petsc/src/tao/bound/tutorials/jbearing2.c (revision 503c0ea9b45bcfbcebbb1ea5341243bbc69f0bea)
1 /*
2   Include "petsctao.h" so we can use TAO solvers
3   Include "petscdmda.h" so that we can use distributed arrays (DMs) for managing
4   Include "petscksp.h" so we can set KSP type
5   the parallel mesh.
6 */
7 
8 #include <petsctao.h>
9 #include <petscdmda.h>
10 
11 static  char help[]=
12 "This example demonstrates use of the TAO package to \n\
13 solve a bound constrained minimization problem.  This example is based on \n\
14 the problem DPJB from the MINPACK-2 test suite.  This pressure journal \n\
15 bearing problem is an example of elliptic variational problem defined over \n\
16 a two dimensional rectangle.  By discretizing the domain into triangular \n\
17 elements, the pressure surrounding the journal bearing is defined as the \n\
18 minimum of a quadratic function whose variables are bounded below by zero.\n\
19 The command line options are:\n\
20   -mx <xg>, where <xg> = number of grid points in the 1st coordinate direction\n\
21   -my <yg>, where <yg> = number of grid points in the 2nd coordinate direction\n\
22  \n";
23 
24 /*T
25    Concepts: TAO^Solving a bound constrained minimization problem
26    Routines: TaoCreate();
27    Routines: TaoSetType(); TaoSetObjectiveAndGradient();
28    Routines: TaoSetHessian();
29    Routines: TaoSetVariableBounds();
30    Routines: TaoSetMonitor(); TaoSetConvergenceTest();
31    Routines: TaoSetSolution();
32    Routines: TaoSetFromOptions();
33    Routines: TaoSolve();
34    Routines: TaoDestroy();
35    Processors: n
36 T*/
37 
38 /*
39    User-defined application context - contains data needed by the
40    application-provided call-back routines, FormFunctionGradient(),
41    FormHessian().
42 */
43 typedef struct {
44   /* problem parameters */
45   PetscReal      ecc;          /* test problem parameter */
46   PetscReal      b;            /* A dimension of journal bearing */
47   PetscInt       nx,ny;        /* discretization in x, y directions */
48 
49   /* Working space */
50   DM          dm;           /* distributed array data structure */
51   Mat         A;            /* Quadratic Objective term */
52   Vec         B;            /* Linear Objective term */
53 } AppCtx;
54 
55 /* User-defined routines */
56 static PetscReal p(PetscReal xi, PetscReal ecc);
57 static PetscErrorCode FormFunctionGradient(Tao, Vec, PetscReal *,Vec,void *);
58 static PetscErrorCode FormHessian(Tao,Vec,Mat, Mat, void *);
59 static PetscErrorCode ComputeB(AppCtx*);
60 static PetscErrorCode Monitor(Tao, void*);
61 static PetscErrorCode ConvergenceTest(Tao, void*);
62 
63 int main(int argc, char **argv)
64 {
65   PetscInt           Nx, Ny;          /* number of processors in x- and y- directions */
66   PetscInt           m;               /* number of local elements in vectors */
67   Vec                x;               /* variables vector */
68   Vec                xl,xu;           /* bounds vectors */
69   PetscReal          d1000 = 1000;
70   PetscBool          flg,testgetdiag; /* A return variable when checking for user options */
71   Tao                tao;             /* Tao solver context */
72   KSP                ksp;
73   AppCtx             user;            /* user-defined work context */
74   PetscReal          zero = 0.0;      /* lower bound on all variables */
75 
76   /* Initialize PETSC and TAO */
77   PetscCall(PetscInitialize(&argc, &argv,(char *)0,help));
78 
79   /* Set the default values for the problem parameters */
80   user.nx = 50; user.ny = 50; user.ecc = 0.1; user.b = 10.0;
81   testgetdiag = PETSC_FALSE;
82 
83   /* Check for any command line arguments that override defaults */
84   PetscCall(PetscOptionsGetInt(NULL,NULL,"-mx",&user.nx,&flg));
85   PetscCall(PetscOptionsGetInt(NULL,NULL,"-my",&user.ny,&flg));
86   PetscCall(PetscOptionsGetReal(NULL,NULL,"-ecc",&user.ecc,&flg));
87   PetscCall(PetscOptionsGetReal(NULL,NULL,"-b",&user.b,&flg));
88   PetscCall(PetscOptionsGetBool(NULL,NULL,"-test_getdiagonal",&testgetdiag,NULL));
89 
90   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"\n---- Journal Bearing Problem SHB-----\n"));
91   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"mx: %D,  my: %D,  ecc: %g \n\n",user.nx,user.ny,(double)user.ecc));
92 
93   /* Let Petsc determine the grid division */
94   Nx = PETSC_DECIDE; Ny = PETSC_DECIDE;
95 
96   /*
97      A two dimensional distributed array will help define this problem,
98      which derives from an elliptic PDE on two dimensional domain.  From
99      the distributed array, Create the vectors.
100   */
101   PetscCall(DMDACreate2d(PETSC_COMM_WORLD,DM_BOUNDARY_NONE,DM_BOUNDARY_NONE,DMDA_STENCIL_STAR,user.nx,user.ny,Nx,Ny,1,1,NULL,NULL,&user.dm));
102   PetscCall(DMSetFromOptions(user.dm));
103   PetscCall(DMSetUp(user.dm));
104 
105   /*
106      Extract global and local vectors from DM; the vector user.B is
107      used solely as work space for the evaluation of the function,
108      gradient, and Hessian.  Duplicate for remaining vectors that are
109      the same types.
110   */
111   PetscCall(DMCreateGlobalVector(user.dm,&x)); /* Solution */
112   PetscCall(VecDuplicate(x,&user.B)); /* Linear objective */
113 
114   /*  Create matrix user.A to store quadratic, Create a local ordering scheme. */
115   PetscCall(VecGetLocalSize(x,&m));
116   PetscCall(DMCreateMatrix(user.dm,&user.A));
117 
118   if (testgetdiag) {
119     PetscCall(MatSetOperation(user.A,MATOP_GET_DIAGONAL,NULL));
120   }
121 
122   /* User defined function -- compute linear term of quadratic */
123   PetscCall(ComputeB(&user));
124 
125   /* The TAO code begins here */
126 
127   /*
128      Create the optimization solver
129      Suitable methods: TAOGPCG, TAOBQPIP, TAOTRON, TAOBLMVM
130   */
131   PetscCall(TaoCreate(PETSC_COMM_WORLD,&tao));
132   PetscCall(TaoSetType(tao,TAOBLMVM));
133 
134   /* Set the initial vector */
135   PetscCall(VecSet(x, zero));
136   PetscCall(TaoSetSolution(tao,x));
137 
138   /* Set the user function, gradient, hessian evaluation routines and data structures */
139   PetscCall(TaoSetObjectiveAndGradient(tao,NULL,FormFunctionGradient,(void*) &user));
140 
141   PetscCall(TaoSetHessian(tao,user.A,user.A,FormHessian,(void*)&user));
142 
143   /* Set a routine that defines the bounds */
144   PetscCall(VecDuplicate(x,&xl));
145   PetscCall(VecDuplicate(x,&xu));
146   PetscCall(VecSet(xl, zero));
147   PetscCall(VecSet(xu, d1000));
148   PetscCall(TaoSetVariableBounds(tao,xl,xu));
149 
150   PetscCall(TaoGetKSP(tao,&ksp));
151   if (ksp) {
152     PetscCall(KSPSetType(ksp,KSPCG));
153   }
154 
155   PetscCall(PetscOptionsHasName(NULL,NULL,"-testmonitor",&flg));
156   if (flg) {
157     PetscCall(TaoSetMonitor(tao,Monitor,&user,NULL));
158   }
159   PetscCall(PetscOptionsHasName(NULL,NULL,"-testconvergence",&flg));
160   if (flg) {
161     PetscCall(TaoSetConvergenceTest(tao,ConvergenceTest,&user));
162   }
163 
164   /* Check for any tao command line options */
165   PetscCall(TaoSetFromOptions(tao));
166 
167   /* Solve the bound constrained problem */
168   PetscCall(TaoSolve(tao));
169 
170   /* Free PETSc data structures */
171   PetscCall(VecDestroy(&x));
172   PetscCall(VecDestroy(&xl));
173   PetscCall(VecDestroy(&xu));
174   PetscCall(MatDestroy(&user.A));
175   PetscCall(VecDestroy(&user.B));
176 
177   /* Free TAO data structures */
178   PetscCall(TaoDestroy(&tao));
179   PetscCall(DMDestroy(&user.dm));
180   PetscCall(PetscFinalize());
181   return 0;
182 }
183 
184 static PetscReal p(PetscReal xi, PetscReal ecc)
185 {
186   PetscReal t=1.0+ecc*PetscCosScalar(xi);
187   return (t*t*t);
188 }
189 
190 PetscErrorCode ComputeB(AppCtx* user)
191 {
192   PetscInt       i,j,k;
193   PetscInt       nx,ny,xs,xm,gxs,gxm,ys,ym,gys,gym;
194   PetscReal      two=2.0, pi=4.0*atan(1.0);
195   PetscReal      hx,hy,ehxhy;
196   PetscReal      temp,*b;
197   PetscReal      ecc=user->ecc;
198 
199   PetscFunctionBegin;
200   nx=user->nx;
201   ny=user->ny;
202   hx=two*pi/(nx+1.0);
203   hy=two*user->b/(ny+1.0);
204   ehxhy = ecc*hx*hy;
205 
206   /*
207      Get local grid boundaries
208   */
209   PetscCall(DMDAGetCorners(user->dm,&xs,&ys,NULL,&xm,&ym,NULL));
210   PetscCall(DMDAGetGhostCorners(user->dm,&gxs,&gys,NULL,&gxm,&gym,NULL));
211 
212   /* Compute the linear term in the objective function */
213   PetscCall(VecGetArray(user->B,&b));
214   for (i=xs; i<xs+xm; i++) {
215     temp=PetscSinScalar((i+1)*hx);
216     for (j=ys; j<ys+ym; j++) {
217       k=xm*(j-ys)+(i-xs);
218       b[k]=  - ehxhy*temp;
219     }
220   }
221   PetscCall(VecRestoreArray(user->B,&b));
222   PetscCall(PetscLogFlops(5.0*xm*ym+3.0*xm));
223   PetscFunctionReturn(0);
224 }
225 
226 PetscErrorCode FormFunctionGradient(Tao tao, Vec X, PetscReal *fcn,Vec G,void *ptr)
227 {
228   AppCtx*        user=(AppCtx*)ptr;
229   PetscInt       i,j,k,kk;
230   PetscInt       col[5],row,nx,ny,xs,xm,gxs,gxm,ys,ym,gys,gym;
231   PetscReal      one=1.0, two=2.0, six=6.0,pi=4.0*atan(1.0);
232   PetscReal      hx,hy,hxhy,hxhx,hyhy;
233   PetscReal      xi,v[5];
234   PetscReal      ecc=user->ecc, trule1,trule2,trule3,trule4,trule5,trule6;
235   PetscReal      vmiddle, vup, vdown, vleft, vright;
236   PetscReal      tt,f1,f2;
237   PetscReal      *x,*g,zero=0.0;
238   Vec            localX;
239 
240   PetscFunctionBegin;
241   nx=user->nx;
242   ny=user->ny;
243   hx=two*pi/(nx+1.0);
244   hy=two*user->b/(ny+1.0);
245   hxhy=hx*hy;
246   hxhx=one/(hx*hx);
247   hyhy=one/(hy*hy);
248 
249   PetscCall(DMGetLocalVector(user->dm,&localX));
250 
251   PetscCall(DMGlobalToLocalBegin(user->dm,X,INSERT_VALUES,localX));
252   PetscCall(DMGlobalToLocalEnd(user->dm,X,INSERT_VALUES,localX));
253 
254   PetscCall(VecSet(G, zero));
255   /*
256     Get local grid boundaries
257   */
258   PetscCall(DMDAGetCorners(user->dm,&xs,&ys,NULL,&xm,&ym,NULL));
259   PetscCall(DMDAGetGhostCorners(user->dm,&gxs,&gys,NULL,&gxm,&gym,NULL));
260 
261   PetscCall(VecGetArray(localX,&x));
262   PetscCall(VecGetArray(G,&g));
263 
264   for (i=xs; i< xs+xm; i++) {
265     xi=(i+1)*hx;
266     trule1=hxhy*(p(xi,ecc) + p(xi+hx,ecc) + p(xi,ecc)) / six; /* L(i,j) */
267     trule2=hxhy*(p(xi,ecc) + p(xi-hx,ecc) + p(xi,ecc)) / six; /* U(i,j) */
268     trule3=hxhy*(p(xi,ecc) + p(xi+hx,ecc) + p(xi+hx,ecc)) / six; /* U(i+1,j) */
269     trule4=hxhy*(p(xi,ecc) + p(xi-hx,ecc) + p(xi-hx,ecc)) / six; /* L(i-1,j) */
270     trule5=trule1; /* L(i,j-1) */
271     trule6=trule2; /* U(i,j+1) */
272 
273     vdown=-(trule5+trule2)*hyhy;
274     vleft=-hxhx*(trule2+trule4);
275     vright= -hxhx*(trule1+trule3);
276     vup=-hyhy*(trule1+trule6);
277     vmiddle=(hxhx)*(trule1+trule2+trule3+trule4)+hyhy*(trule1+trule2+trule5+trule6);
278 
279     for (j=ys; j<ys+ym; j++) {
280 
281       row=(j-gys)*gxm + (i-gxs);
282        v[0]=0; v[1]=0; v[2]=0; v[3]=0; v[4]=0;
283 
284        k=0;
285        if (j>gys) {
286          v[k]=vdown; col[k]=row - gxm; k++;
287        }
288 
289        if (i>gxs) {
290          v[k]= vleft; col[k]=row - 1; k++;
291        }
292 
293        v[k]= vmiddle; col[k]=row; k++;
294 
295        if (i+1 < gxs+gxm) {
296          v[k]= vright; col[k]=row+1; k++;
297        }
298 
299        if (j+1 <gys+gym) {
300          v[k]= vup; col[k] = row+gxm; k++;
301        }
302        tt=0;
303        for (kk=0;kk<k;kk++) {
304          tt+=v[kk]*x[col[kk]];
305        }
306        row=(j-ys)*xm + (i-xs);
307        g[row]=tt;
308 
309      }
310 
311   }
312 
313   PetscCall(VecRestoreArray(localX,&x));
314   PetscCall(VecRestoreArray(G,&g));
315 
316   PetscCall(DMRestoreLocalVector(user->dm,&localX));
317 
318   PetscCall(VecDot(X,G,&f1));
319   PetscCall(VecDot(user->B,X,&f2));
320   PetscCall(VecAXPY(G, one, user->B));
321   *fcn = f1/2.0 + f2;
322 
323   PetscCall(PetscLogFlops((91 + 10.0*ym) * xm));
324   PetscFunctionReturn(0);
325 
326 }
327 
328 /*
329    FormHessian computes the quadratic term in the quadratic objective function
330    Notice that the objective function in this problem is quadratic (therefore a constant
331    hessian).  If using a nonquadratic solver, then you might want to reconsider this function
332 */
333 PetscErrorCode FormHessian(Tao tao,Vec X,Mat hes, Mat Hpre, void *ptr)
334 {
335   AppCtx*        user=(AppCtx*)ptr;
336   PetscInt       i,j,k;
337   PetscInt       col[5],row,nx,ny,xs,xm,gxs,gxm,ys,ym,gys,gym;
338   PetscReal      one=1.0, two=2.0, six=6.0,pi=4.0*atan(1.0);
339   PetscReal      hx,hy,hxhy,hxhx,hyhy;
340   PetscReal      xi,v[5];
341   PetscReal      ecc=user->ecc, trule1,trule2,trule3,trule4,trule5,trule6;
342   PetscReal      vmiddle, vup, vdown, vleft, vright;
343   PetscBool      assembled;
344 
345   PetscFunctionBegin;
346   nx=user->nx;
347   ny=user->ny;
348   hx=two*pi/(nx+1.0);
349   hy=two*user->b/(ny+1.0);
350   hxhy=hx*hy;
351   hxhx=one/(hx*hx);
352   hyhy=one/(hy*hy);
353 
354   /*
355     Get local grid boundaries
356   */
357   PetscCall(DMDAGetCorners(user->dm,&xs,&ys,NULL,&xm,&ym,NULL));
358   PetscCall(DMDAGetGhostCorners(user->dm,&gxs,&gys,NULL,&gxm,&gym,NULL));
359   PetscCall(MatAssembled(hes,&assembled));
360   if (assembled) PetscCall(MatZeroEntries(hes));
361 
362   for (i=xs; i< xs+xm; i++) {
363     xi=(i+1)*hx;
364     trule1=hxhy*(p(xi,ecc) + p(xi+hx,ecc) + p(xi,ecc)) / six; /* L(i,j) */
365     trule2=hxhy*(p(xi,ecc) + p(xi-hx,ecc) + p(xi,ecc)) / six; /* U(i,j) */
366     trule3=hxhy*(p(xi,ecc) + p(xi+hx,ecc) + p(xi+hx,ecc)) / six; /* U(i+1,j) */
367     trule4=hxhy*(p(xi,ecc) + p(xi-hx,ecc) + p(xi-hx,ecc)) / six; /* L(i-1,j) */
368     trule5=trule1; /* L(i,j-1) */
369     trule6=trule2; /* U(i,j+1) */
370 
371     vdown=-(trule5+trule2)*hyhy;
372     vleft=-hxhx*(trule2+trule4);
373     vright= -hxhx*(trule1+trule3);
374     vup=-hyhy*(trule1+trule6);
375     vmiddle=(hxhx)*(trule1+trule2+trule3+trule4)+hyhy*(trule1+trule2+trule5+trule6);
376     v[0]=0; v[1]=0; v[2]=0; v[3]=0; v[4]=0;
377 
378     for (j=ys; j<ys+ym; j++) {
379       row=(j-gys)*gxm + (i-gxs);
380 
381       k=0;
382       if (j>gys) {
383         v[k]=vdown; col[k]=row - gxm; k++;
384       }
385 
386       if (i>gxs) {
387         v[k]= vleft; col[k]=row - 1; k++;
388       }
389 
390       v[k]= vmiddle; col[k]=row; k++;
391 
392       if (i+1 < gxs+gxm) {
393         v[k]= vright; col[k]=row+1; k++;
394       }
395 
396       if (j+1 <gys+gym) {
397         v[k]= vup; col[k] = row+gxm; k++;
398       }
399       PetscCall(MatSetValuesLocal(hes,1,&row,k,col,v,INSERT_VALUES));
400 
401     }
402 
403   }
404 
405   /*
406      Assemble matrix, using the 2-step process:
407      MatAssemblyBegin(), MatAssemblyEnd().
408      By placing code between these two statements, computations can be
409      done while messages are in transition.
410   */
411   PetscCall(MatAssemblyBegin(hes,MAT_FINAL_ASSEMBLY));
412   PetscCall(MatAssemblyEnd(hes,MAT_FINAL_ASSEMBLY));
413 
414   /*
415     Tell the matrix we will never add a new nonzero location to the
416     matrix. If we do it will generate an error.
417   */
418   PetscCall(MatSetOption(hes,MAT_NEW_NONZERO_LOCATION_ERR,PETSC_TRUE));
419   PetscCall(MatSetOption(hes,MAT_SYMMETRIC,PETSC_TRUE));
420 
421   PetscCall(PetscLogFlops(9.0*xm*ym+49.0*xm));
422   PetscFunctionReturn(0);
423 }
424 
425 PetscErrorCode Monitor(Tao tao, void *ctx)
426 {
427   PetscInt           its;
428   PetscReal          f,gnorm,cnorm,xdiff;
429   TaoConvergedReason reason;
430 
431   PetscFunctionBegin;
432   PetscCall(TaoGetSolutionStatus(tao, &its, &f, &gnorm, &cnorm, &xdiff, &reason));
433   if (!(its%5)) {
434     PetscCall(PetscPrintf(PETSC_COMM_WORLD,"iteration=%D\tf=%g\n",its,(double)f));
435   }
436   PetscFunctionReturn(0);
437 }
438 
439 PetscErrorCode ConvergenceTest(Tao tao, void *ctx)
440 {
441   PetscInt           its;
442   PetscReal          f,gnorm,cnorm,xdiff;
443   TaoConvergedReason reason;
444 
445   PetscFunctionBegin;
446   PetscCall(TaoGetSolutionStatus(tao, &its, &f, &gnorm, &cnorm, &xdiff, &reason));
447   if (its == 100) {
448     PetscCall(TaoSetConvergedReason(tao,TAO_DIVERGED_MAXITS));
449   }
450   PetscFunctionReturn(0);
451 
452 }
453 
454 /*TEST
455 
456    build:
457       requires: !complex
458 
459    test:
460       args: -tao_smonitor -mx 8 -my 12 -tao_type tron -tao_gatol 1.e-5
461       requires: !single
462 
463    test:
464       suffix: 2
465       nsize: 2
466       args: -tao_smonitor -mx 50 -my 50 -ecc 0.99 -tao_type gpcg -tao_gatol 1.e-5
467       requires: !single
468 
469    test:
470       suffix: 3
471       nsize: 2
472       args: -tao_smonitor -mx 10 -my 16 -ecc 0.9 -tao_type bqpip -tao_gatol 1.e-4
473       requires: !single
474 
475    test:
476       suffix: 4
477       nsize: 2
478       args: -tao_smonitor -mx 10 -my 16 -ecc 0.9 -tao_type bqpip -tao_gatol 1.e-4 -test_getdiagonal
479       output_file: output/jbearing2_3.out
480       requires: !single
481 
482    test:
483       suffix: 5
484       args: -tao_smonitor -mx 8 -my 12 -tao_type bncg -tao_bncg_type gd -tao_gatol 1e-4
485       requires: !single
486 
487    test:
488       suffix: 6
489       args: -tao_smonitor -mx 8 -my 12 -tao_type bncg -tao_gatol 1e-4
490       requires: !single
491 
492    test:
493       suffix: 7
494       args: -tao_smonitor -mx 8 -my 12 -tao_type bnls -tao_gatol 1e-5
495       requires: !single
496 
497    test:
498       suffix: 8
499       args: -tao_smonitor -mx 8 -my 12 -tao_type bntr -tao_gatol 1e-5
500       requires: !single
501 
502    test:
503       suffix: 9
504       args: -tao_smonitor -mx 8 -my 12 -tao_type bntl -tao_gatol 1e-5
505       requires: !single
506 
507    test:
508       suffix: 10
509       args: -tao_smonitor -mx 8 -my 12 -tao_type bnls -tao_gatol 1e-5 -tao_bnk_max_cg_its 3
510       requires: !single
511 
512    test:
513       suffix: 11
514       args: -tao_smonitor -mx 8 -my 12 -tao_type bntr -tao_gatol 1e-5 -tao_bnk_max_cg_its 3
515       requires: !single
516 
517    test:
518       suffix: 12
519       args: -tao_smonitor -mx 8 -my 12 -tao_type bntl -tao_gatol 1e-5 -tao_bnk_max_cg_its 3
520       requires: !single
521 
522    test:
523      suffix: 13
524      args: -tao_smonitor -mx 8 -my 12 -tao_gatol 1e-4 -tao_type bqnls
525      requires: !single
526 
527    test:
528      suffix: 14
529      args: -tao_smonitor -mx 8 -my 12 -tao_gatol 1e-4 -tao_type blmvm
530      requires: !single
531 
532    test:
533      suffix: 15
534      args: -tao_smonitor -mx 8 -my 12 -tao_gatol 1e-4 -tao_type bqnkls -tao_bqnk_mat_type lmvmbfgs
535      requires: !single
536 
537    test:
538      suffix: 16
539      args: -tao_smonitor -mx 8 -my 12 -tao_gatol 1e-4 -tao_type bqnktr -tao_bqnk_mat_type lmvmsr1
540      requires: !single
541 
542    test:
543      suffix: 17
544      args: -tao_smonitor -mx 8 -my 12 -tao_gatol 1e-4 -tao_type bqnls -tao_bqnls_mat_lmvm_scale_type scalar -tao_view
545      requires: !single
546 
547    test:
548      suffix: 18
549      args: -tao_smonitor -mx 8 -my 12 -tao_gatol 1e-4 -tao_type bqnls -tao_bqnls_mat_lmvm_scale_type none -tao_view
550      requires: !single
551 
552    test:
553      suffix: 19
554      args: -tao_smonitor -mx 8 -my 12 -tao_type bnls -tao_gatol 1e-5 -tao_mf_hessian
555      requires: !single
556 
557    test:
558       suffix: 20
559       args: -tao_smonitor -mx 8 -my 12 -tao_type bntr -tao_gatol 1e-5 -tao_mf_hessian
560       requires: !single
561 
562    test:
563       suffix: 21
564       args: -tao_smonitor -mx 8 -my 12 -tao_type bntl -tao_gatol 1e-5 -tao_mf_hessian
565       requires: !single
566 TEST*/
567