Date: 2026-03-31T05:46:33+00:00

Actual source code: minsurf1.c

      1: #include <petsctao.h>

      3: static char help[] = "This example demonstrates use of the TAO package to\n\
      4: solve an unconstrained system of equations.  This example is based on a\n\
      5: problem from the MINPACK-2 test suite.  Given a rectangular 2-D domain and\n\
      6: boundary values along the edges of the domain, the objective is to find the\n\
      7: surface with the minimal area that satisfies the boundary conditions.\n\
      8: This application solves this problem using complimentarity -- We are actually\n\
      9: solving the system  (grad f)_i >= 0, if x_i == l_i \n\
     10:                     (grad f)_i = 0, if l_i < x_i < u_i \n\
     11:                     (grad f)_i <= 0, if x_i == u_i  \n\
     12: where f is the function to be minimized. \n\
     13: \n\
     14: The command line options are:\n\
     15:   -mx <xg>, where <xg> = number of grid points in the 1st coordinate direction\n\
     16:   -my <yg>, where <yg> = number of grid points in the 2nd coordinate direction\n\
     17:   -start <st>, where <st> =0 for zero vector, and an average of the boundary conditions otherwise \n\n";

     19: /*
     20:    User-defined application context - contains data needed by the
     21:    application-provided call-back routines, FormFunctionGradient(),
     22:    FormHessian().
     23: */
     24: typedef struct {
     25:   PetscInt   mx, my;
     26:   PetscReal *bottom, *top, *left, *right;
     27: } AppCtx;

     29: /* -------- User-defined Routines --------- */

     31: static PetscErrorCode MSA_BoundaryConditions(AppCtx *);
     32: static PetscErrorCode MSA_InitialPoint(AppCtx *, Vec);
     33: PetscErrorCode        FormConstraints(Tao, Vec, Vec, void *);
     34: PetscErrorCode        FormJacobian(Tao, Vec, Mat, Mat, void *);

     36: int main(int argc, char **argv)
     37: {
     38:   Vec         x;                    /* solution vector */
     39:   Vec         c;                    /* Constraints function vector */
     40:   Vec         xl, xu;               /* Bounds on the variables */
     41:   PetscBool   flg;                  /* A return variable when checking for user options */
     42:   Tao         tao;                  /* TAO solver context */
     43:   Mat         J;                    /* Jacobian matrix */
     44:   PetscInt    N;                    /* Number of elements in vector */
     45:   PetscScalar lb = PETSC_NINFINITY; /* lower bound constant */
     46:   PetscScalar ub = PETSC_INFINITY;  /* upper bound constant */
     47:   AppCtx      user;                 /* user-defined work context */

     49:   /* Initialize PETSc, TAO */
     50:   PetscFunctionBeginUser;
     51:   PetscCall(PetscInitialize(&argc, &argv, NULL, help));

     53:   /* Specify default dimension of the problem */
     54:   user.mx = 4;
     55:   user.my = 4;

     57:   /* Check for any command line arguments that override defaults */
     58:   PetscCall(PetscOptionsGetInt(NULL, NULL, "-mx", &user.mx, &flg));
     59:   PetscCall(PetscOptionsGetInt(NULL, NULL, "-my", &user.my, &flg));

     61:   /* Calculate any derived values from parameters */
     62:   N = user.mx * user.my;

     64:   PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n---- Minimum Surface Area Problem -----\n"));
     65:   PetscCall(PetscPrintf(PETSC_COMM_SELF, "mx:%" PetscInt_FMT ", my:%" PetscInt_FMT "\n", user.mx, user.my));

     67:   /* Create appropriate vectors and matrices */
     68:   PetscCall(VecCreateSeq(MPI_COMM_SELF, N, &x));
     69:   PetscCall(VecDuplicate(x, &c));
     70:   PetscCall(MatCreateSeqAIJ(MPI_COMM_SELF, N, N, 7, NULL, &J));

     72:   /* The TAO code begins here */

     74:   /* Create TAO solver and set desired solution method */
     75:   PetscCall(TaoCreate(PETSC_COMM_SELF, &tao));
     76:   PetscCall(TaoSetType(tao, TAOSSILS));

     78:   /* Set data structure */
     79:   PetscCall(TaoSetSolution(tao, x));

     81:   /*  Set routines for constraints function and Jacobian evaluation */
     82:   PetscCall(TaoSetConstraintsRoutine(tao, c, FormConstraints, (void *)&user));
     83:   PetscCall(TaoSetJacobianRoutine(tao, J, J, FormJacobian, (void *)&user));

     85:   /* Set the variable bounds */
     86:   PetscCall(MSA_BoundaryConditions(&user));

     88:   /* Set initial solution guess */
     89:   PetscCall(MSA_InitialPoint(&user, x));

     91:   /* Set Bounds on variables */
     92:   PetscCall(VecDuplicate(x, &xl));
     93:   PetscCall(VecDuplicate(x, &xu));
     94:   PetscCall(VecSet(xl, lb));
     95:   PetscCall(VecSet(xu, ub));
     96:   PetscCall(TaoSetVariableBounds(tao, xl, xu));

     98:   /* Check for any tao command line options */
     99:   PetscCall(TaoSetFromOptions(tao));

    101:   /* Solve the application */
    102:   PetscCall(TaoSolve(tao));

    104:   /* Free Tao data structures */
    105:   PetscCall(TaoDestroy(&tao));

    107:   /* Free PETSc data structures */
    108:   PetscCall(VecDestroy(&x));
    109:   PetscCall(VecDestroy(&xl));
    110:   PetscCall(VecDestroy(&xu));
    111:   PetscCall(VecDestroy(&c));
    112:   PetscCall(MatDestroy(&J));

    114:   /* Free user-created data structures */
    115:   PetscCall(PetscFree(user.bottom));
    116:   PetscCall(PetscFree(user.top));
    117:   PetscCall(PetscFree(user.left));
    118:   PetscCall(PetscFree(user.right));

    120:   PetscCall(PetscFinalize());
    121:   return 0;
    122: }

    124: /* -------------------------------------------------------------------- */

    126: /*  FormConstraints - Evaluates gradient of f.

    128:     Input Parameters:
    129: .   tao  - the TAO_APPLICATION context
    130: .   X    - input vector
    131: .   ptr  - optional user-defined context, as set by TaoSetConstraintsRoutine()

    133:     Output Parameters:
    134: .   G - vector containing the newly evaluated gradient
    135: */
    136: PetscErrorCode FormConstraints(Tao tao, Vec X, Vec G, void *ptr)
    137: {
    138:   AppCtx      *user = (AppCtx *)ptr;
    139:   PetscInt     i, j, row;
    140:   PetscInt     mx = user->mx, my = user->my;
    141:   PetscReal    hx = 1.0 / (mx + 1), hy = 1.0 / (my + 1), hydhx = hy / hx, hxdhy = hx / hy;
    142:   PetscReal    f1, f2, f3, f4, f5, f6, d1, d2, d3, d4, d5, d6, d7, d8, xc, xl, xr, xt, xb, xlt, xrb;
    143:   PetscReal    df1dxc, df2dxc, df3dxc, df4dxc, df5dxc, df6dxc;
    144:   PetscScalar  zero = 0.0;
    145:   PetscScalar *g, *x;

    147:   PetscFunctionBegin;
    148:   /* Initialize vector to zero */
    149:   PetscCall(VecSet(G, zero));

    151:   /* Get pointers to vector data */
    152:   PetscCall(VecGetArray(X, &x));
    153:   PetscCall(VecGetArray(G, &g));

    155:   /* Compute function over the locally owned part of the mesh */
    156:   for (j = 0; j < my; j++) {
    157:     for (i = 0; i < mx; i++) {
    158:       row = j * mx + i;

    160:       xc  = x[row];
    161:       xlt = xrb = xl = xr = xb = xt = xc;

    163:       if (i == 0) { /* left side */
    164:         xl  = user->left[j + 1];
    165:         xlt = user->left[j + 2];
    166:       } else {
    167:         xl = x[row - 1];
    168:       }

    170:       if (j == 0) { /* bottom side */
    171:         xb  = user->bottom[i + 1];
    172:         xrb = user->bottom[i + 2];
    173:       } else {
    174:         xb = x[row - mx];
    175:       }

    177:       if (i + 1 == mx) { /* right side */
    178:         xr  = user->right[j + 1];
    179:         xrb = user->right[j];
    180:       } else {
    181:         xr = x[row + 1];
    182:       }

    184:       if (j + 1 == 0 + my) { /* top side */
    185:         xt  = user->top[i + 1];
    186:         xlt = user->top[i];
    187:       } else {
    188:         xt = x[row + mx];
    189:       }

    191:       if (i > 0 && j + 1 < my) xlt = x[row - 1 + mx];
    192:       if (j > 0 && i + 1 < mx) xrb = x[row + 1 - mx];

    194:       d1 = (xc - xl);
    195:       d2 = (xc - xr);
    196:       d3 = (xc - xt);
    197:       d4 = (xc - xb);
    198:       d5 = (xr - xrb);
    199:       d6 = (xrb - xb);
    200:       d7 = (xlt - xl);
    201:       d8 = (xt - xlt);

    203:       df1dxc = d1 * hydhx;
    204:       df2dxc = (d1 * hydhx + d4 * hxdhy);
    205:       df3dxc = d3 * hxdhy;
    206:       df4dxc = (d2 * hydhx + d3 * hxdhy);
    207:       df5dxc = d2 * hydhx;
    208:       df6dxc = d4 * hxdhy;

    210:       d1 /= hx;
    211:       d2 /= hx;
    212:       d3 /= hy;
    213:       d4 /= hy;
    214:       d5 /= hy;
    215:       d6 /= hx;
    216:       d7 /= hy;
    217:       d8 /= hx;

    219:       f1 = PetscSqrtScalar(1.0 + d1 * d1 + d7 * d7);
    220:       f2 = PetscSqrtScalar(1.0 + d1 * d1 + d4 * d4);
    221:       f3 = PetscSqrtScalar(1.0 + d3 * d3 + d8 * d8);
    222:       f4 = PetscSqrtScalar(1.0 + d3 * d3 + d2 * d2);
    223:       f5 = PetscSqrtScalar(1.0 + d2 * d2 + d5 * d5);
    224:       f6 = PetscSqrtScalar(1.0 + d4 * d4 + d6 * d6);

    226:       df1dxc /= f1;
    227:       df2dxc /= f2;
    228:       df3dxc /= f3;
    229:       df4dxc /= f4;
    230:       df5dxc /= f5;
    231:       df6dxc /= f6;

    233:       g[row] = (df1dxc + df2dxc + df3dxc + df4dxc + df5dxc + df6dxc) / 2.0;
    234:     }
    235:   }

    237:   /* Restore vectors */
    238:   PetscCall(VecRestoreArray(X, &x));
    239:   PetscCall(VecRestoreArray(G, &g));
    240:   PetscCall(PetscLogFlops(67 * mx * my));
    241:   PetscFunctionReturn(PETSC_SUCCESS);
    242: }

    244: /* ------------------------------------------------------------------- */
    245: /*
    246:    FormJacobian - Evaluates Jacobian matrix.

    248:    Input Parameters:
    249: .  tao  - the TAO_APPLICATION context
    250: .  X    - input vector
    251: .  ptr  - optional user-defined context, as set by TaoSetJacobian()

    253:    Output Parameters:
    254: .  tH    - Jacobian matrix

    256: */
    257: PetscErrorCode FormJacobian(Tao tao, Vec X, Mat H, Mat tHPre, void *ptr)
    258: {
    259:   AppCtx            *user = (AppCtx *)ptr;
    260:   PetscInt           i, j, k, row;
    261:   PetscInt           mx = user->mx, my = user->my;
    262:   PetscInt           col[7];
    263:   PetscReal          hx = 1.0 / (mx + 1), hy = 1.0 / (my + 1), hydhx = hy / hx, hxdhy = hx / hy;
    264:   PetscReal          f1, f2, f3, f4, f5, f6, d1, d2, d3, d4, d5, d6, d7, d8, xc, xl, xr, xt, xb, xlt, xrb;
    265:   PetscReal          hl, hr, ht, hb, hc, htl, hbr;
    266:   const PetscScalar *x;
    267:   PetscScalar        v[7];
    268:   PetscBool          assembled;

    270:   /* Set various matrix options */
    271:   PetscFunctionBegin;
    272:   PetscCall(MatSetOption(H, MAT_IGNORE_OFF_PROC_ENTRIES, PETSC_TRUE));
    273:   PetscCall(MatAssembled(H, &assembled));
    274:   if (assembled) PetscCall(MatZeroEntries(H));

    276:   /* Get pointers to vector data */
    277:   PetscCall(VecGetArrayRead(X, &x));

    279:   /* Compute Jacobian over the locally owned part of the mesh */
    280:   for (i = 0; i < mx; i++) {
    281:     for (j = 0; j < my; j++) {
    282:       row = j * mx + i;

    284:       xc  = x[row];
    285:       xlt = xrb = xl = xr = xb = xt = xc;

    287:       /* Left side */
    288:       if (i == 0) {
    289:         xl  = user->left[j + 1];
    290:         xlt = user->left[j + 2];
    291:       } else {
    292:         xl = x[row - 1];
    293:       }

    295:       if (j == 0) {
    296:         xb  = user->bottom[i + 1];
    297:         xrb = user->bottom[i + 2];
    298:       } else {
    299:         xb = x[row - mx];
    300:       }

    302:       if (i + 1 == mx) {
    303:         xr  = user->right[j + 1];
    304:         xrb = user->right[j];
    305:       } else {
    306:         xr = x[row + 1];
    307:       }

    309:       if (j + 1 == my) {
    310:         xt  = user->top[i + 1];
    311:         xlt = user->top[i];
    312:       } else {
    313:         xt = x[row + mx];
    314:       }

    316:       if (i > 0 && j + 1 < my) xlt = x[row - 1 + mx];
    317:       if (j > 0 && i + 1 < mx) xrb = x[row + 1 - mx];

    319:       d1 = (xc - xl) / hx;
    320:       d2 = (xc - xr) / hx;
    321:       d3 = (xc - xt) / hy;
    322:       d4 = (xc - xb) / hy;
    323:       d5 = (xrb - xr) / hy;
    324:       d6 = (xrb - xb) / hx;
    325:       d7 = (xlt - xl) / hy;
    326:       d8 = (xlt - xt) / hx;

    328:       f1 = PetscSqrtScalar(1.0 + d1 * d1 + d7 * d7);
    329:       f2 = PetscSqrtScalar(1.0 + d1 * d1 + d4 * d4);
    330:       f3 = PetscSqrtScalar(1.0 + d3 * d3 + d8 * d8);
    331:       f4 = PetscSqrtScalar(1.0 + d3 * d3 + d2 * d2);
    332:       f5 = PetscSqrtScalar(1.0 + d2 * d2 + d5 * d5);
    333:       f6 = PetscSqrtScalar(1.0 + d4 * d4 + d6 * d6);

    335:       hl = (-hydhx * (1.0 + d7 * d7) + d1 * d7) / (f1 * f1 * f1) + (-hydhx * (1.0 + d4 * d4) + d1 * d4) / (f2 * f2 * f2);
    336:       hr = (-hydhx * (1.0 + d5 * d5) + d2 * d5) / (f5 * f5 * f5) + (-hydhx * (1.0 + d3 * d3) + d2 * d3) / (f4 * f4 * f4);
    337:       ht = (-hxdhy * (1.0 + d8 * d8) + d3 * d8) / (f3 * f3 * f3) + (-hxdhy * (1.0 + d2 * d2) + d2 * d3) / (f4 * f4 * f4);
    338:       hb = (-hxdhy * (1.0 + d6 * d6) + d4 * d6) / (f6 * f6 * f6) + (-hxdhy * (1.0 + d1 * d1) + d1 * d4) / (f2 * f2 * f2);

    340:       hbr = -d2 * d5 / (f5 * f5 * f5) - d4 * d6 / (f6 * f6 * f6);
    341:       htl = -d1 * d7 / (f1 * f1 * f1) - d3 * d8 / (f3 * f3 * f3);

    343:       hc = hydhx * (1.0 + d7 * d7) / (f1 * f1 * f1) + hxdhy * (1.0 + d8 * d8) / (f3 * f3 * f3) + hydhx * (1.0 + d5 * d5) / (f5 * f5 * f5) + hxdhy * (1.0 + d6 * d6) / (f6 * f6 * f6) + (hxdhy * (1.0 + d1 * d1) + hydhx * (1.0 + d4 * d4) - 2 * d1 * d4) / (f2 * f2 * f2) + (hxdhy * (1.0 + d2 * d2) + hydhx * (1.0 + d3 * d3) - 2 * d2 * d3) / (f4 * f4 * f4);

    345:       hl /= 2.0;
    346:       hr /= 2.0;
    347:       ht /= 2.0;
    348:       hb /= 2.0;
    349:       hbr /= 2.0;
    350:       htl /= 2.0;
    351:       hc /= 2.0;

    353:       k = 0;
    354:       if (j > 0) {
    355:         v[k]   = hb;
    356:         col[k] = row - mx;
    357:         k++;
    358:       }

    360:       if (j > 0 && i < mx - 1) {
    361:         v[k]   = hbr;
    362:         col[k] = row - mx + 1;
    363:         k++;
    364:       }

    366:       if (i > 0) {
    367:         v[k]   = hl;
    368:         col[k] = row - 1;
    369:         k++;
    370:       }

    372:       v[k]   = hc;
    373:       col[k] = row;
    374:       k++;

    376:       if (i < mx - 1) {
    377:         v[k]   = hr;
    378:         col[k] = row + 1;
    379:         k++;
    380:       }

    382:       if (i > 0 && j < my - 1) {
    383:         v[k]   = htl;
    384:         col[k] = row + mx - 1;
    385:         k++;
    386:       }

    388:       if (j < my - 1) {
    389:         v[k]   = ht;
    390:         col[k] = row + mx;
    391:         k++;
    392:       }

    394:       /*
    395:          Set matrix values using local numbering, which was defined
    396:          earlier, in the main routine.
    397:       */
    398:       PetscCall(MatSetValues(H, 1, &row, k, col, v, INSERT_VALUES));
    399:     }
    400:   }

    402:   /* Restore vectors */
    403:   PetscCall(VecRestoreArrayRead(X, &x));

    405:   /* Assemble the matrix */
    406:   PetscCall(MatAssemblyBegin(H, MAT_FINAL_ASSEMBLY));
    407:   PetscCall(MatAssemblyEnd(H, MAT_FINAL_ASSEMBLY));
    408:   PetscCall(PetscLogFlops(199 * mx * my));
    409:   PetscFunctionReturn(PETSC_SUCCESS);
    410: }

    412: /* ------------------------------------------------------------------- */
    413: /*
    414:    MSA_BoundaryConditions -  Calculates the boundary conditions for
    415:    the region.

    417:    Input Parameter:
    418: .  user - user-defined application context

    420:    Output Parameter:
    421: .  user - user-defined application context
    422: */
    423: static PetscErrorCode MSA_BoundaryConditions(AppCtx *user)
    424: {
    425:   PetscInt   i, j, k, limit = 0, maxits = 5;
    426:   PetscInt   mx = user->mx, my = user->my;
    427:   PetscInt   bsize = 0, lsize = 0, tsize = 0, rsize = 0;
    428:   PetscReal  one = 1.0, two = 2.0, three = 3.0, tol = 1e-10;
    429:   PetscReal  fnorm, det, hx, hy, xt = 0, yt = 0;
    430:   PetscReal  u1, u2, nf1, nf2, njac11, njac12, njac21, njac22;
    431:   PetscReal  b = -0.5, t = 0.5, l = -0.5, r = 0.5;
    432:   PetscReal *boundary;

    434:   PetscFunctionBegin;
    435:   bsize = mx + 2;
    436:   lsize = my + 2;
    437:   rsize = my + 2;
    438:   tsize = mx + 2;

    440:   PetscCall(PetscMalloc1(bsize, &user->bottom));
    441:   PetscCall(PetscMalloc1(tsize, &user->top));
    442:   PetscCall(PetscMalloc1(lsize, &user->left));
    443:   PetscCall(PetscMalloc1(rsize, &user->right));

    445:   hx = (r - l) / (mx + 1);
    446:   hy = (t - b) / (my + 1);

    448:   for (j = 0; j < 4; j++) {
    449:     if (j == 0) {
    450:       yt       = b;
    451:       xt       = l;
    452:       limit    = bsize;
    453:       boundary = user->bottom;
    454:     } else if (j == 1) {
    455:       yt       = t;
    456:       xt       = l;
    457:       limit    = tsize;
    458:       boundary = user->top;
    459:     } else if (j == 2) {
    460:       yt       = b;
    461:       xt       = l;
    462:       limit    = lsize;
    463:       boundary = user->left;
    464:     } else { /* if  (j==3) */
    465:       yt       = b;
    466:       xt       = r;
    467:       limit    = rsize;
    468:       boundary = user->right;
    469:     }

    471:     for (i = 0; i < limit; i++) {
    472:       u1 = xt;
    473:       u2 = -yt;
    474:       for (k = 0; k < maxits; k++) {
    475:         nf1   = u1 + u1 * u2 * u2 - u1 * u1 * u1 / three - xt;
    476:         nf2   = -u2 - u1 * u1 * u2 + u2 * u2 * u2 / three - yt;
    477:         fnorm = PetscSqrtScalar(nf1 * nf1 + nf2 * nf2);
    478:         if (fnorm <= tol) break;
    479:         njac11 = one + u2 * u2 - u1 * u1;
    480:         njac12 = two * u1 * u2;
    481:         njac21 = -two * u1 * u2;
    482:         njac22 = -one - u1 * u1 + u2 * u2;
    483:         det    = njac11 * njac22 - njac21 * njac12;
    484:         u1     = u1 - (njac22 * nf1 - njac12 * nf2) / det;
    485:         u2     = u2 - (njac11 * nf2 - njac21 * nf1) / det;
    486:       }

    488:       boundary[i] = u1 * u1 - u2 * u2;
    489:       if (j == 0 || j == 1) {
    490:         xt = xt + hx;
    491:       } else { /* if (j==2 || j==3) */
    492:         yt = yt + hy;
    493:       }
    494:     }
    495:   }
    496:   PetscFunctionReturn(PETSC_SUCCESS);
    497: }

    499: /* ------------------------------------------------------------------- */
    500: /*
    501:    MSA_InitialPoint - Calculates the initial guess in one of three ways.

    503:    Input Parameters:
    504: .  user - user-defined application context
    505: .  X - vector for initial guess

    507:    Output Parameters:
    508: .  X - newly computed initial guess
    509: */
    510: static PetscErrorCode MSA_InitialPoint(AppCtx *user, Vec X)
    511: {
    512:   PetscInt    start = -1, i, j;
    513:   PetscScalar zero  = 0.0;
    514:   PetscBool   flg;

    516:   PetscFunctionBegin;
    517:   PetscCall(PetscOptionsGetInt(NULL, NULL, "-start", &start, &flg));

    519:   if (flg && start == 0) { /* The zero vector is reasonable */
    520:     PetscCall(VecSet(X, zero));
    521:   } else { /* Take an average of the boundary conditions */
    522:     PetscInt     row;
    523:     PetscInt     mx = user->mx, my = user->my;
    524:     PetscScalar *x;

    526:     /* Get pointers to vector data */
    527:     PetscCall(VecGetArray(X, &x));

    529:     /* Perform local computations */
    530:     for (j = 0; j < my; j++) {
    531:       for (i = 0; i < mx; i++) {
    532:         row    = (j)*mx + (i);
    533:         x[row] = (((j + 1) * user->bottom[i + 1] + (my - j + 1) * user->top[i + 1]) / (my + 2) + ((i + 1) * user->left[j + 1] + (mx - i + 1) * user->right[j + 1]) / (mx + 2)) / 2.0;
    534:       }
    535:     }

    537:     /* Restore vectors */
    538:     PetscCall(VecRestoreArray(X, &x));
    539:   }
    540:   PetscFunctionReturn(PETSC_SUCCESS);
    541: }

    543: /*TEST

    545:    build:
    546:       requires: !complex

    548:    test:
    549:       args: -tao_monitor -tao_view -tao_type ssils -tao_gttol 1.e-5
    550:       requires: !single

    552:    test:
    553:       suffix: 2
    554:       args: -tao_monitor -tao_view -tao_type ssfls -tao_gttol 1.e-5

    556: TEST*/