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