Actual source code: ex4.c

      1: #include <petscsnes.h>
      2: #include <petscdmda.h>

      4: static const char help[] = "Minimum surface area problem in 2D using DMDA.\n\
      5: It solves an unconstrained minimization problem. This example is based on a \n\
      6: problem from the MINPACK-2 test suite. Given a rectangular 2-D domain and \n\
      7: boundary values along the edges of the domain, the objective is to find the\n\
      8: surface with the minimal area that satisfies the boundary conditions.\n\
      9: \n\
     10: The command line options are:\n\
     11:   -da_grid_x <nx>, where <nx> = number of grid points in the 1st coordinate direction\n\
     12:   -da_grid_y <ny>, where <ny> = number of grid points in the 2nd coordinate direction\n\
     13:   \n";

     15: /*
     16:    User-defined application context - contains data needed by the
     17:    application-provided call-back routines, FormJacobianLocal() and
     18:    FormFunctionLocal().
     19: */

     21: typedef enum {
     22:   PROBLEM_ENNEPER,
     23:   PROBLEM_SINS,
     24: } ProblemType;
     25: static const char *const ProblemTypes[] = {"ENNEPER", "SINS", "ProblemType", "PROBLEM_", 0};

     27: typedef struct {
     28:   PetscScalar *bottom, *top, *left, *right;
     29: } AppCtx;

     31: /* -------- User-defined Routines --------- */

     33: static PetscErrorCode FormBoundaryConditions_Enneper(SNES, AppCtx **);
     34: static PetscErrorCode FormBoundaryConditions_Sins(SNES, AppCtx **);
     35: static PetscErrorCode DestroyBoundaryConditions(AppCtx **);
     36: static PetscErrorCode FormObjectiveLocal(DMDALocalInfo *, PetscScalar **, PetscReal *, void *);
     37: static PetscErrorCode FormFunctionLocal(DMDALocalInfo *, PetscScalar **, PetscScalar **, void *);
     38: static PetscErrorCode FormJacobianLocal(DMDALocalInfo *, PetscScalar **, Mat, Mat, void *);

     40: int main(int argc, char **argv)
     41: {
     42:   Vec         x;
     43:   SNES        snes;
     44:   DM          da;
     45:   ProblemType ptype   = PROBLEM_ENNEPER;
     46:   PetscBool   use_obj = PETSC_TRUE;
     47:   PetscReal   bbox[4] = {0.};
     48:   AppCtx     *user;
     49:   PetscErrorCode (*form_bc)(SNES, AppCtx **) = NULL;

     51:   PetscFunctionBeginUser;
     52:   PetscCall(PetscInitialize(&argc, &argv, NULL, help));

     54:   PetscOptionsBegin(PETSC_COMM_WORLD, NULL, "Minimal surface options", __FILE__);
     55:   PetscCall(PetscOptionsEnum("-problem_type", "Problem type", NULL, ProblemTypes, (PetscEnum)ptype, (PetscEnum *)&ptype, NULL));
     56:   PetscCall(PetscOptionsBool("-use_objective", "Use objective function", NULL, use_obj, &use_obj, NULL));
     57:   PetscOptionsEnd();
     58:   switch (ptype) {
     59:   case PROBLEM_ENNEPER:
     60:     bbox[0] = -0.5;
     61:     bbox[1] = 0.5;
     62:     bbox[2] = -0.5;
     63:     bbox[3] = 0.5;
     64:     form_bc = FormBoundaryConditions_Enneper;
     65:     break;
     66:   case PROBLEM_SINS:
     67:     bbox[0] = 0.0;
     68:     bbox[1] = 1.0;
     69:     bbox[2] = 0.0;
     70:     bbox[3] = 1.0;
     71:     form_bc = FormBoundaryConditions_Sins;
     72:     break;
     73:   }

     75:   /* Create distributed array to manage the 2d grid */
     76:   PetscCall(DMDACreate2d(PETSC_COMM_WORLD, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DMDA_STENCIL_BOX, 4, 4, PETSC_DECIDE, PETSC_DECIDE, 1, 1, NULL, NULL, &da));
     77:   PetscCall(DMSetFromOptions(da));
     78:   PetscCall(DMSetUp(da));
     79:   PetscCall(DMDASetUniformCoordinates(da, bbox[0], bbox[1], bbox[2], bbox[3], PETSC_DECIDE, PETSC_DECIDE));

     81:   /* Extract global vectors from DMDA; */
     82:   PetscCall(DMCreateGlobalVector(da, &x));

     84:   /* Create nonlinear solver context */
     85:   PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
     86:   PetscCall(SNESSetDM(snes, da));
     87:   PetscCall((*form_bc)(snes, &user));
     88:   PetscCall(SNESSetApplicationContext(snes, user));

     90:   /*  Set local callbacks */
     91:   if (use_obj) PetscCall(DMDASNESSetObjectiveLocal(da, (DMDASNESObjectiveFn *)FormObjectiveLocal, NULL));
     92:   PetscCall(DMDASNESSetFunctionLocal(da, INSERT_VALUES, (DMDASNESFunctionFn *)FormFunctionLocal, NULL));
     93:   PetscCall(DMDASNESSetJacobianLocal(da, (DMDASNESJacobianFn *)FormJacobianLocal, NULL));

     95:   /* Customize from command line */
     96:   PetscCall(SNESSetFromOptions(snes));

     98:   /* Solve the application */
     99:   PetscCall(SNESSolve(snes, NULL, x));

    101:   /* Free user-created data structures */
    102:   PetscCall(VecDestroy(&x));
    103:   PetscCall(SNESDestroy(&snes));
    104:   PetscCall(DMDestroy(&da));
    105:   PetscCall(DestroyBoundaryConditions(&user));

    107:   PetscCall(PetscFinalize());
    108:   return 0;
    109: }

    111: /* Compute objective function over the locally owned part of the mesh */
    112: PetscErrorCode FormObjectiveLocal(DMDALocalInfo *info, PetscScalar **x, PetscReal *v, void *ptr)
    113: {
    114:   AppCtx     *user = (AppCtx *)ptr;
    115:   PetscInt    mx = info->mx, my = info->my;
    116:   PetscInt    xs = info->xs, xm = info->xm, ys = info->ys, ym = info->ym;
    117:   PetscInt    i, j;
    118:   PetscScalar hx, hy;
    119:   PetscScalar f2, f4, d1, d2, d3, d4, xc, xl, xr, xt, xb;
    120:   PetscReal   ft = 0, area;

    122:   PetscFunctionBeginUser;
    123:   PetscCheck(user, PetscObjectComm((PetscObject)info->da), PETSC_ERR_PLIB, "Missing application context");
    124:   hx   = 1.0 / (mx + 1);
    125:   hy   = 1.0 / (my + 1);
    126:   area = 0.5 * hx * hy;
    127:   for (j = ys; j < ys + ym; j++) {
    128:     for (i = xs; i < xs + xm; i++) {
    129:       xc = x[j][i];
    130:       xl = xr = xb = xt = xc;

    132:       if (i == 0) { /* left side */
    133:         xl = user->left[j + 1];
    134:       } else xl = x[j][i - 1];

    136:       if (j == 0) { /* bottom side */
    137:         xb = user->bottom[i + 1];
    138:       } else xb = x[j - 1][i];

    140:       if (i + 1 == mx) { /* right side */
    141:         xr = user->right[j + 1];
    142:       } else xr = x[j][i + 1];

    144:       if (j + 1 == 0 + my) { /* top side */
    145:         xt = user->top[i + 1];
    146:       } else xt = x[j + 1][i];

    148:       d1 = (xc - xl);
    149:       d2 = (xc - xr);
    150:       d3 = (xc - xt);
    151:       d4 = (xc - xb);

    153:       d1 /= hx;
    154:       d2 /= hx;
    155:       d3 /= hy;
    156:       d4 /= hy;

    158:       f2 = PetscSqrtScalar(1.0 + d1 * d1 + d4 * d4);
    159:       f4 = PetscSqrtScalar(1.0 + d3 * d3 + d2 * d2);

    161:       ft += PetscRealPart(f2 + f4);
    162:     }
    163:   }

    165:   /* Compute triangular areas along the border of the domain. */
    166:   if (xs == 0) { /* left side */
    167:     for (j = ys; j < ys + ym; j++) {
    168:       d3 = (user->left[j + 1] - user->left[j + 2]) / hy;
    169:       d2 = (user->left[j + 1] - x[j][0]) / hx;
    170:       ft += PetscSqrtReal(1.0 + d3 * d3 + d2 * d2);
    171:     }
    172:   }
    173:   if (ys == 0) { /* bottom side */
    174:     for (i = xs; i < xs + xm; i++) {
    175:       d2 = (user->bottom[i + 1] - user->bottom[i + 2]) / hx;
    176:       d3 = (user->bottom[i + 1] - x[0][i]) / hy;
    177:       ft += PetscSqrtReal(1.0 + d3 * d3 + d2 * d2);
    178:     }
    179:   }
    180:   if (xs + xm == mx) { /* right side */
    181:     for (j = ys; j < ys + ym; j++) {
    182:       d1 = (x[j][mx - 1] - user->right[j + 1]) / hx;
    183:       d4 = (user->right[j] - user->right[j + 1]) / hy;
    184:       ft += PetscSqrtReal(1.0 + d1 * d1 + d4 * d4);
    185:     }
    186:   }
    187:   if (ys + ym == my) { /* top side */
    188:     for (i = xs; i < xs + xm; i++) {
    189:       d1 = (x[my - 1][i] - user->top[i + 1]) / hy;
    190:       d4 = (user->top[i + 1] - user->top[i]) / hx;
    191:       ft += PetscSqrtReal(1.0 + d1 * d1 + d4 * d4);
    192:     }
    193:   }
    194:   if (ys == 0 && xs == 0) {
    195:     d1 = (user->left[0] - user->left[1]) / hy;
    196:     d2 = (user->bottom[0] - user->bottom[1]) / hx;
    197:     ft += PetscSqrtReal(1.0 + d1 * d1 + d2 * d2);
    198:   }
    199:   if (ys + ym == my && xs + xm == mx) {
    200:     d1 = (user->right[ym + 1] - user->right[ym]) / hy;
    201:     d2 = (user->top[xm + 1] - user->top[xm]) / hx;
    202:     ft += PetscSqrtReal(1.0 + d1 * d1 + d2 * d2);
    203:   }
    204:   ft *= area;
    205:   *v = ft;
    206:   PetscFunctionReturn(PETSC_SUCCESS);
    207: }

    209: /* Compute gradient over the locally owned part of the mesh */
    210: PetscErrorCode FormFunctionLocal(DMDALocalInfo *info, PetscScalar **x, PetscScalar **g, void *ptr)
    211: {
    212:   AppCtx     *user = (AppCtx *)ptr;
    213:   PetscInt    mx = info->mx, my = info->my;
    214:   PetscInt    xs = info->xs, xm = info->xm, ys = info->ys, ym = info->ym;
    215:   PetscInt    i, j;
    216:   PetscScalar hx, hy, hydhx, hxdhy;
    217:   PetscScalar f1, f2, f3, f4, f5, f6, d1, d2, d3, d4, d5, d6, d7, d8, xc, xl, xr, xt, xb, xlt, xrb;
    218:   PetscScalar df1dxc, df2dxc, df3dxc, df4dxc, df5dxc, df6dxc;

    220:   PetscFunctionBeginUser;
    221:   PetscCheck(user, PetscObjectComm((PetscObject)info->da), PETSC_ERR_PLIB, "Missing application context");
    222:   hx    = 1.0 / (mx + 1);
    223:   hy    = 1.0 / (my + 1);
    224:   hydhx = hy / hx;
    225:   hxdhy = hx / hy;

    227:   for (j = ys; j < ys + ym; j++) {
    228:     for (i = xs; i < xs + xm; i++) {
    229:       xc  = x[j][i];
    230:       xlt = xrb = xl = xr = xb = xt = xc;

    232:       if (i == 0) { /* left side */
    233:         xl  = user->left[j + 1];
    234:         xlt = user->left[j + 2];
    235:       } else xl = x[j][i - 1];

    237:       if (j == 0) { /* bottom side */
    238:         xb  = user->bottom[i + 1];
    239:         xrb = user->bottom[i + 2];
    240:       } else xb = x[j - 1][i];

    242:       if (i + 1 == mx) { /* right side */
    243:         xr  = user->right[j + 1];
    244:         xrb = user->right[j];
    245:       } else xr = x[j][i + 1];

    247:       if (j + 1 == 0 + my) { /* top side */
    248:         xt  = user->top[i + 1];
    249:         xlt = user->top[i];
    250:       } else xt = x[j + 1][i];

    252:       if (i > 0 && j + 1 < my) xlt = x[j + 1][i - 1]; /* left top side */
    253:       if (j > 0 && i + 1 < mx) xrb = x[j - 1][i + 1]; /* right bottom */

    255:       d1 = (xc - xl);
    256:       d2 = (xc - xr);
    257:       d3 = (xc - xt);
    258:       d4 = (xc - xb);
    259:       d5 = (xr - xrb);
    260:       d6 = (xrb - xb);
    261:       d7 = (xlt - xl);
    262:       d8 = (xt - xlt);

    264:       df1dxc = d1 * hydhx;
    265:       df2dxc = (d1 * hydhx + d4 * hxdhy);
    266:       df3dxc = d3 * hxdhy;
    267:       df4dxc = (d2 * hydhx + d3 * hxdhy);
    268:       df5dxc = d2 * hydhx;
    269:       df6dxc = d4 * hxdhy;

    271:       d1 /= hx;
    272:       d2 /= hx;
    273:       d3 /= hy;
    274:       d4 /= hy;
    275:       d5 /= hy;
    276:       d6 /= hx;
    277:       d7 /= hy;
    278:       d8 /= hx;

    280:       f1 = PetscSqrtScalar(1.0 + d1 * d1 + d7 * d7);
    281:       f2 = PetscSqrtScalar(1.0 + d1 * d1 + d4 * d4);
    282:       f3 = PetscSqrtScalar(1.0 + d3 * d3 + d8 * d8);
    283:       f4 = PetscSqrtScalar(1.0 + d3 * d3 + d2 * d2);
    284:       f5 = PetscSqrtScalar(1.0 + d2 * d2 + d5 * d5);
    285:       f6 = PetscSqrtScalar(1.0 + d4 * d4 + d6 * d6);

    287:       df1dxc /= f1;
    288:       df2dxc /= f2;
    289:       df3dxc /= f3;
    290:       df4dxc /= f4;
    291:       df5dxc /= f5;
    292:       df6dxc /= f6;

    294:       g[j][i] = (df1dxc + df2dxc + df3dxc + df4dxc + df5dxc + df6dxc) / 2.0;
    295:     }
    296:   }
    297:   PetscFunctionReturn(PETSC_SUCCESS);
    298: }

    300: /* Compute Hessian over the locally owned part of the mesh */
    301: PetscErrorCode FormJacobianLocal(DMDALocalInfo *info, PetscScalar **x, Mat H, Mat Hp, void *ptr)
    302: {
    303:   AppCtx     *user = (AppCtx *)ptr;
    304:   PetscInt    mx = info->mx, my = info->my;
    305:   PetscInt    xs = info->xs, xm = info->xm, ys = info->ys, ym = info->ym;
    306:   PetscInt    i, j, k;
    307:   MatStencil  row, col[7];
    308:   PetscScalar hx, hy, hydhx, hxdhy;
    309:   PetscScalar f1, f2, f3, f4, f5, f6, d1, d2, d3, d4, d5, d6, d7, d8, xc, xl, xr, xt, xb, xlt, xrb;
    310:   PetscScalar hl, hr, ht, hb, hc, htl, hbr;
    311:   PetscScalar v[7];

    313:   PetscFunctionBeginUser;
    314:   PetscCheck(user, PetscObjectComm((PetscObject)info->da), PETSC_ERR_PLIB, "Missing application context");
    315:   hx    = 1.0 / (mx + 1);
    316:   hy    = 1.0 / (my + 1);
    317:   hydhx = hy / hx;
    318:   hxdhy = hx / hy;

    320:   for (j = ys; j < ys + ym; j++) {
    321:     for (i = xs; i < xs + xm; i++) {
    322:       xc  = x[j][i];
    323:       xlt = xrb = xl = xr = xb = xt = xc;

    325:       /* Left */
    326:       if (i == 0) {
    327:         xl  = user->left[j + 1];
    328:         xlt = user->left[j + 2];
    329:       } else xl = x[j][i - 1];

    331:       /* Bottom */
    332:       if (j == 0) {
    333:         xb  = user->bottom[i + 1];
    334:         xrb = user->bottom[i + 2];
    335:       } else xb = x[j - 1][i];

    337:       /* Right */
    338:       if (i + 1 == mx) {
    339:         xr  = user->right[j + 1];
    340:         xrb = user->right[j];
    341:       } else xr = x[j][i + 1];

    343:       /* Top */
    344:       if (j + 1 == my) {
    345:         xt  = user->top[i + 1];
    346:         xlt = user->top[i];
    347:       } else xt = x[j + 1][i];

    349:       /* Top left */
    350:       if (i > 0 && j + 1 < my) xlt = x[j + 1][i - 1];

    352:       /* Bottom right */
    353:       if (j > 0 && i + 1 < mx) xrb = x[j - 1][i + 1];

    355:       d1 = (xc - xl) / hx;
    356:       d2 = (xc - xr) / hx;
    357:       d3 = (xc - xt) / hy;
    358:       d4 = (xc - xb) / hy;
    359:       d5 = (xrb - xr) / hy;
    360:       d6 = (xrb - xb) / hx;
    361:       d7 = (xlt - xl) / hy;
    362:       d8 = (xlt - xt) / hx;

    364:       f1 = PetscSqrtScalar(1.0 + d1 * d1 + d7 * d7);
    365:       f2 = PetscSqrtScalar(1.0 + d1 * d1 + d4 * d4);
    366:       f3 = PetscSqrtScalar(1.0 + d3 * d3 + d8 * d8);
    367:       f4 = PetscSqrtScalar(1.0 + d3 * d3 + d2 * d2);
    368:       f5 = PetscSqrtScalar(1.0 + d2 * d2 + d5 * d5);
    369:       f6 = PetscSqrtScalar(1.0 + d4 * d4 + d6 * d6);

    371:       hl = (-hydhx * (1.0 + d7 * d7) + d1 * d7) / (f1 * f1 * f1) + (-hydhx * (1.0 + d4 * d4) + d1 * d4) / (f2 * f2 * f2);
    372:       hr = (-hydhx * (1.0 + d5 * d5) + d2 * d5) / (f5 * f5 * f5) + (-hydhx * (1.0 + d3 * d3) + d2 * d3) / (f4 * f4 * f4);
    373:       ht = (-hxdhy * (1.0 + d8 * d8) + d3 * d8) / (f3 * f3 * f3) + (-hxdhy * (1.0 + d2 * d2) + d2 * d3) / (f4 * f4 * f4);
    374:       hb = (-hxdhy * (1.0 + d6 * d6) + d4 * d6) / (f6 * f6 * f6) + (-hxdhy * (1.0 + d1 * d1) + d1 * d4) / (f2 * f2 * f2);

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

    379:       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.0 * d1 * d4) / (f2 * f2 * f2) + (hxdhy * (1.0 + d2 * d2) + hydhx * (1.0 + d3 * d3) - 2.0 * d2 * d3) / (f4 * f4 * f4);

    381:       hl /= 2.0;
    382:       hr /= 2.0;
    383:       ht /= 2.0;
    384:       hb /= 2.0;
    385:       hbr /= 2.0;
    386:       htl /= 2.0;
    387:       hc /= 2.0;

    389:       k     = 0;
    390:       row.i = i;
    391:       row.j = j;
    392:       /* Bottom */
    393:       if (j > 0) {
    394:         v[k]     = hb;
    395:         col[k].i = i;
    396:         col[k].j = j - 1;
    397:         k++;
    398:       }

    400:       /* Bottom right */
    401:       if (j > 0 && i < mx - 1) {
    402:         v[k]     = hbr;
    403:         col[k].i = i + 1;
    404:         col[k].j = j - 1;
    405:         k++;
    406:       }

    408:       /* left */
    409:       if (i > 0) {
    410:         v[k]     = hl;
    411:         col[k].i = i - 1;
    412:         col[k].j = j;
    413:         k++;
    414:       }

    416:       /* Centre */
    417:       v[k]     = hc;
    418:       col[k].i = row.i;
    419:       col[k].j = row.j;
    420:       k++;

    422:       /* Right */
    423:       if (i < mx - 1) {
    424:         v[k]     = hr;
    425:         col[k].i = i + 1;
    426:         col[k].j = j;
    427:         k++;
    428:       }

    430:       /* Top left */
    431:       if (i > 0 && j < my - 1) {
    432:         v[k]     = htl;
    433:         col[k].i = i - 1;
    434:         col[k].j = j + 1;
    435:         k++;
    436:       }

    438:       /* Top */
    439:       if (j < my - 1) {
    440:         v[k]     = ht;
    441:         col[k].i = i;
    442:         col[k].j = j + 1;
    443:         k++;
    444:       }

    446:       PetscCall(MatSetValuesStencil(Hp, 1, &row, k, col, v, INSERT_VALUES));
    447:     }
    448:   }

    450:   /* Assemble the matrix */
    451:   PetscCall(MatAssemblyBegin(Hp, MAT_FINAL_ASSEMBLY));
    452:   PetscCall(MatAssemblyEnd(Hp, MAT_FINAL_ASSEMBLY));
    453:   PetscFunctionReturn(PETSC_SUCCESS);
    454: }

    456: PetscErrorCode FormBoundaryConditions_Enneper(SNES snes, AppCtx **ouser)
    457: {
    458:   PetscInt     i, j, k, limit = 0, maxits = 5;
    459:   PetscInt     mx, my;
    460:   PetscInt     bsize = 0, lsize = 0, tsize = 0, rsize = 0;
    461:   PetscScalar  one = 1.0, two = 2.0, three = 3.0;
    462:   PetscScalar  det, hx, hy, xt = 0, yt = 0;
    463:   PetscReal    fnorm, tol = 1e-10;
    464:   PetscScalar  u1, u2, nf1, nf2, njac11, njac12, njac21, njac22;
    465:   PetscScalar  b = -0.5, t = 0.5, l = -0.5, r = 0.5;
    466:   PetscScalar *boundary;
    467:   AppCtx      *user;
    468:   DM           da;

    470:   PetscFunctionBeginUser;
    471:   PetscCall(SNESGetDM(snes, &da));
    472:   PetscCall(PetscNew(&user));
    473:   *ouser = user;
    474:   PetscCall(DMDAGetInfo(da, PETSC_IGNORE, &mx, &my, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE));
    475:   bsize = mx + 2;
    476:   lsize = my + 2;
    477:   rsize = my + 2;
    478:   tsize = mx + 2;

    480:   PetscCall(PetscMalloc1(bsize, &user->bottom));
    481:   PetscCall(PetscMalloc1(tsize, &user->top));
    482:   PetscCall(PetscMalloc1(lsize, &user->left));
    483:   PetscCall(PetscMalloc1(rsize, &user->right));

    485:   hx = 1.0 / (mx + 1.0);
    486:   hy = 1.0 / (my + 1.0);

    488:   for (j = 0; j < 4; j++) {
    489:     if (j == 0) {
    490:       yt       = b;
    491:       xt       = l;
    492:       limit    = bsize;
    493:       boundary = user->bottom;
    494:     } else if (j == 1) {
    495:       yt       = t;
    496:       xt       = l;
    497:       limit    = tsize;
    498:       boundary = user->top;
    499:     } else if (j == 2) {
    500:       yt       = b;
    501:       xt       = l;
    502:       limit    = lsize;
    503:       boundary = user->left;
    504:     } else { /* if  (j==3) */
    505:       yt       = b;
    506:       xt       = r;
    507:       limit    = rsize;
    508:       boundary = user->right;
    509:     }

    511:     for (i = 0; i < limit; i++) {
    512:       u1 = xt;
    513:       u2 = -yt;
    514:       for (k = 0; k < maxits; k++) {
    515:         nf1   = u1 + u1 * u2 * u2 - u1 * u1 * u1 / three - xt;
    516:         nf2   = -u2 - u1 * u1 * u2 + u2 * u2 * u2 / three - yt;
    517:         fnorm = PetscRealPart(PetscSqrtScalar(nf1 * nf1 + nf2 * nf2));
    518:         if (fnorm <= tol) break;
    519:         njac11 = one + u2 * u2 - u1 * u1;
    520:         njac12 = two * u1 * u2;
    521:         njac21 = -two * u1 * u2;
    522:         njac22 = -one - u1 * u1 + u2 * u2;
    523:         det    = njac11 * njac22 - njac21 * njac12;
    524:         u1     = u1 - (njac22 * nf1 - njac12 * nf2) / det;
    525:         u2     = u2 - (njac11 * nf2 - njac21 * nf1) / det;
    526:       }

    528:       boundary[i] = u1 * u1 - u2 * u2;
    529:       if (j == 0 || j == 1) xt = xt + hx;
    530:       else yt = yt + hy; /* if (j==2 || j==3) */
    531:     }
    532:   }
    533:   PetscFunctionReturn(PETSC_SUCCESS);
    534: }

    536: PetscErrorCode DestroyBoundaryConditions(AppCtx **ouser)
    537: {
    538:   AppCtx *user = *ouser;

    540:   PetscFunctionBeginUser;
    541:   PetscCall(PetscFree(user->bottom));
    542:   PetscCall(PetscFree(user->top));
    543:   PetscCall(PetscFree(user->left));
    544:   PetscCall(PetscFree(user->right));
    545:   PetscCall(PetscFree(*ouser));
    546:   PetscFunctionReturn(PETSC_SUCCESS);
    547: }

    549: PetscErrorCode FormBoundaryConditions_Sins(SNES snes, AppCtx **ouser)
    550: {
    551:   PetscInt     i, j;
    552:   PetscInt     mx, my;
    553:   PetscInt     limit, bsize = 0, lsize = 0, tsize = 0, rsize = 0;
    554:   PetscScalar  hx, hy, xt = 0, yt = 0;
    555:   PetscScalar  b = 0.0, t = 1.0, l = 0.0, r = 1.0;
    556:   PetscScalar *boundary;
    557:   AppCtx      *user;
    558:   DM           da;
    559:   PetscReal    pi2 = 2 * PETSC_PI;

    561:   PetscFunctionBeginUser;
    562:   PetscCall(SNESGetDM(snes, &da));
    563:   PetscCall(PetscNew(&user));
    564:   *ouser = user;
    565:   PetscCall(DMDAGetInfo(da, PETSC_IGNORE, &mx, &my, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE));
    566:   bsize = mx + 2;
    567:   lsize = my + 2;
    568:   rsize = my + 2;
    569:   tsize = mx + 2;

    571:   PetscCall(PetscMalloc1(bsize, &user->bottom));
    572:   PetscCall(PetscMalloc1(tsize, &user->top));
    573:   PetscCall(PetscMalloc1(lsize, &user->left));
    574:   PetscCall(PetscMalloc1(rsize, &user->right));

    576:   hx = 1.0 / (mx + 1.0);
    577:   hy = 1.0 / (my + 1.0);

    579:   for (j = 0; j < 4; j++) {
    580:     if (j == 0) {
    581:       yt       = b;
    582:       xt       = l;
    583:       limit    = bsize;
    584:       boundary = user->bottom;
    585:     } else if (j == 1) {
    586:       yt       = t;
    587:       xt       = l;
    588:       limit    = tsize;
    589:       boundary = user->top;
    590:     } else if (j == 2) {
    591:       yt       = b;
    592:       xt       = l;
    593:       limit    = lsize;
    594:       boundary = user->left;
    595:     } else { /* if  (j==3) */
    596:       yt       = b;
    597:       xt       = r;
    598:       limit    = rsize;
    599:       boundary = user->right;
    600:     }

    602:     for (i = 0; i < limit; i++) {
    603:       if (j == 0) { /* bottom */
    604:         boundary[i] = -0.5 * PetscSinReal(pi2 * xt);
    605:       } else if (j == 1) { /* top */
    606:         boundary[i] = 0.5 * PetscSinReal(pi2 * xt);
    607:       } else if (j == 2) { /* left */
    608:         boundary[i] = -0.5 * PetscSinReal(pi2 * yt);
    609:       } else { /* right */
    610:         boundary[i] = 0.5 * PetscSinReal(pi2 * yt);
    611:       }
    612:       if (j == 0 || j == 1) xt = xt + hx;
    613:       else yt = yt + hy;
    614:     }
    615:   }
    616:   PetscFunctionReturn(PETSC_SUCCESS);
    617: }

    619: /*TEST

    621:   build:
    622:     requires: !complex

    624:   test:
    625:     requires: !single
    626:     filter: sed -e "s/CONVERGED_FNORM_ABS/CONVERGED_FNORM_RELATIVE/g"
    627:     suffix: qn_nasm
    628:     args: -snes_type qn -snes_npc_side {{left right}separate output} -npc_snes_type nasm -snes_converged_reason -da_local_subdomains 4 -use_objective {{0 1}separate output}

    630: TEST*/